Code

remove rudimental general-purpose clipart, add some Inkscape-related graphics, update...
[inkscape.git] / src / libcroco / cr-tknzr.c
1 /* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
3 /*
4  * This file is part of The Croco Library
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of version 2.1 of the GNU Lesser General Public
8  * License as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA
19  *
20  * Author: Dodji Seketeli
21  * See the COPYRIGHTS file for copyrights information.
22  */
24 /**
25  *@file
26  *The definition of the #CRTknzr (tokenizer)
27  *class.
28  */
30 #include "string.h"
31 #include "cr-tknzr.h"
32 #include "cr-doc-handler.h"
34 struct _CRTknzrPriv {
35         /**The parser input stream of bytes*/
36         CRInput *input;
38         /**
39          *A cache where tknzr_unget_token()
40          *puts back the token. tknzr_get_next_token()
41          *first look in this cache, and if and 
42          *only if it's empty, fetches the next token
43          *from the input stream.
44          */
45         CRToken *token_cache;
47         /**
48          *The position of the end of the previous token
49          *or char fetched.
50          */
51         CRInputPos prev_pos;
53         CRDocHandler *sac_handler;
55         /**
56          *The reference count of the current instance
57          *of #CRTknzr. Is manipulated by cr_tknzr_ref()
58          *and cr_tknzr_unref().
59          */
60         glong ref_count;
61 };
63 #define PRIVATE(obj) ((obj)->priv)
65 /**
66  *return TRUE if the character is a number ([0-9]), FALSE otherwise
67  *@param a_char the char to test.
68  */
69 #define IS_NUM(a_char) (((a_char) >= '0' && (a_char) <= '9')?TRUE:FALSE)
71 /**
72  *Checks if 'status' equals CR_OK. If not, goto the 'error' label.
73  *
74  *@param status the status (of type enum CRStatus) to test.
75  *@param is_exception if set to FALSE, the final status returned the
76  *current function will be CR_PARSING_ERROR. If set to TRUE, the
77  *current status will be the current value of the 'status' variable.
78  *
79  */
80 #define CHECK_PARSING_STATUS(status, is_exception) \
81 if ((status) != CR_OK) \
82 { \
83         if (is_exception == FALSE) \
84         { \
85                 status = CR_PARSING_ERROR ; \
86         } \
87         goto error ; \
88 }
90 /**
91  *Peeks the next char from the input stream of the current tokenizer.
92  *invokes CHECK_PARSING_STATUS on the status returned by
93  *cr_tknzr_input_peek_char().
94  *
95  *@param the current instance of #CRTkzr.
96  *@param to_char a pointer to the char where to store the
97  *char peeked.
98  */
99 #define PEEK_NEXT_CHAR(a_tknzr, a_to_char) \
100 {\
101 status = cr_tknzr_peek_char  (a_tknzr, a_to_char) ; \
102 CHECK_PARSING_STATUS (status, TRUE) \
105 /**
106  *Reads the next char from the input stream of the current parser.
107  *In case of error, jumps to the "error:" label located in the
108  *function where this macro is called.
109  *@param parser the curent instance of #CRTknzr
110  *@param to_char a pointer to the guint32 char where to store
111  *the character read.
112  */
113 #define READ_NEXT_CHAR(a_tknzr, to_char) \
114 status = cr_tknzr_read_char (a_tknzr, to_char) ;\
115 CHECK_PARSING_STATUS (status, TRUE)
117 /**
118  *Gets information about the current position in
119  *the input of the parser.
120  *In case of failure, this macro returns from the 
121  *calling function and
122  *returns a status code of type enum #CRStatus.
123  *@param parser the current instance of #CRTknzr.
124  *@param pos out parameter. A pointer to the position 
125  *inside the current parser input. Must
126  */
127 #define RECORD_INITIAL_POS(a_tknzr, a_pos) \
128 status = cr_input_get_cur_pos (PRIVATE  \
129 (a_tknzr)->input, a_pos) ; \
130 g_return_val_if_fail (status == CR_OK, status)
132 /**
133  *Gets the address of the current byte inside the
134  *parser input.
135  *@param parser the current instance of #CRTknzr.
136  *@param addr out parameter a pointer (guchar*)
137  *to where the address  must be put.
138  */
139 #define RECORD_CUR_BYTE_ADDR(a_tknzr, a_addr) \
140 status = cr_input_get_cur_byte_addr \
141             (PRIVATE (a_tknzr)->input, a_addr) ; \
142 CHECK_PARSING_STATUS (status, TRUE)
144 /**
145  *Peeks a byte from the topmost parser input at
146  *a given offset from the current position.
147  *If it fails, goto the "error:" label.
148  *
149  *@param a_parser the current instance of #CRTknzr.
150  *@param a_offset the offset of the byte to peek, the
151  *current byte having the offset '0'.
152  *@param a_byte_ptr out parameter a pointer (guchar*) to
153  *where the peeked char is to be stored.
154  */
155 #define PEEK_BYTE(a_tknzr, a_offset, a_byte_ptr) \
156 status = cr_tknzr_peek_byte (a_tknzr, \
157                              a_offset, \
158                              a_byte_ptr) ; \
159 CHECK_PARSING_STATUS (status, TRUE) ;
161 #define BYTE(a_input, a_n, a_eof) \
162 cr_input_peek_byte2 (a_input, a_n, a_eof)
164 /**
165  *Reads a byte from the topmost parser input
166  *steam.
167  *If it fails, goto the "error" label.
168  *@param a_parser the current instance of #CRTknzr.
169  *@param a_byte_ptr the guchar * where to put the read char.
170  */
171 #define READ_NEXT_BYTE(a_tknzr, a_byte_ptr) \
172 status = \
173 cr_input_read_byte (PRIVATE (a_tknzr)->input, a_byte_ptr) ;\
174 CHECK_PARSING_STATUS (status, TRUE) ;
176 /**
177  *Skips a given number of byte in the topmost
178  *parser input. Don't update line and column number.
179  *In case of error, jumps to the "error:" label
180  *of the surrounding function.
181  *@param a_parser the current instance of #CRTknzr.
182  *@param a_nb_bytes the number of bytes to skip.
183  */
184 #define SKIP_BYTES(a_tknzr, a_nb_bytes) \
185 status = cr_input_seek_index (PRIVATE (a_tknzr)->input, \
186                                      CR_SEEK_CUR, a_nb_bytes) ; \
187 CHECK_PARSING_STATUS (status, TRUE) ;
189 /**
190  *Skip utf8 encoded characters.
191  *Updates line and column numbers.
192  *@param a_parser the current instance of #CRTknzr.
193  *@param a_nb_chars the number of chars to skip. Must be of
194  *type glong.
195  */
196 #define SKIP_CHARS(a_tknzr, a_nb_chars) \
197 { \
198 gulong nb_chars = a_nb_chars ; \
199 status = cr_input_consume_chars \
200      (PRIVATE (a_tknzr)->input,0, &nb_chars) ; \
201 CHECK_PARSING_STATUS (status, TRUE) ; \
204 /**
205  *Tests the condition and if it is false, sets
206  *status to "CR_PARSING_ERROR" and goto the 'error'
207  *label.
208  *@param condition the condition to test.
209  */
210 #define ENSURE_PARSING_COND(condition) \
211 if (! (condition)) {status = CR_PARSING_ERROR; goto error ;}
213 static enum CRStatus  cr_tknzr_parse_nl (CRTknzr * a_this, 
214                                          guchar ** a_start, 
215                                          guchar ** a_end,
216                                          CRParsingLocation *a_location);
218 static enum CRStatus cr_tknzr_parse_w (CRTknzr * a_this, 
219                                        guchar ** a_start, 
220                                        guchar ** a_end,
221                                        CRParsingLocation *a_location) ;
223 static enum CRStatus cr_tknzr_parse_unicode_escape (CRTknzr * a_this, 
224                                                     guint32 * a_unicode,
225                                                     CRParsingLocation *a_location) ;
227 static enum CRStatus cr_tknzr_parse_escape (CRTknzr * a_this, 
228                                             guint32 * a_esc_code,
229                                             CRParsingLocation *a_location);
231 static enum CRStatus cr_tknzr_parse_string (CRTknzr * a_this, 
232                                             CRString ** a_str);
234 static enum CRStatus cr_tknzr_parse_comment (CRTknzr * a_this, 
235                                              CRString ** a_comment);
237 static enum CRStatus cr_tknzr_parse_nmstart (CRTknzr * a_this, 
238                                              guint32 * a_char, 
239                                              CRParsingLocation *a_location);
241 static enum CRStatus cr_tknzr_parse_num (CRTknzr * a_this,
242                                          CRNum ** a_num);
244 /**********************************
245  *PRIVATE methods
246  **********************************/
248 /**
249  *Parses a "w" as defined by the css spec at [4.1.1]:
250  * w ::= [ \t\r\n\f]*
251  *
252  *@param a_this the current instance of #CRTknzr.
253  *@param a_start out param. Upon successfull completion, points
254  *to the beginning of the parsed white space, points to NULL otherwise.
255  *Can also point to NULL is there is no white space actually.
256  *@param a_end out param. Upon successfull completion, points
257  *to the end of the parsed white space, points to NULL otherwise.
258  *Can also point to NULL is there is no white space actually.
259  */
260 static enum CRStatus
261 cr_tknzr_parse_w (CRTknzr * a_this, 
262                   guchar ** a_start, 
263                   guchar ** a_end, 
264                   CRParsingLocation *a_location)
266         guint32 cur_char = 0;
267         CRInputPos init_pos;
268         enum CRStatus status = CR_OK;
270         g_return_val_if_fail (a_this && PRIVATE (a_this)
271                               && PRIVATE (a_this)->input
272                               && a_start && a_end, 
273                               CR_BAD_PARAM_ERROR);
275         RECORD_INITIAL_POS (a_this, &init_pos);
277         *a_start = NULL;
278         *a_end = NULL;
280         READ_NEXT_CHAR (a_this, &cur_char);
282         if (cr_utils_is_white_space (cur_char) == FALSE) {
283                 status = CR_PARSING_ERROR;
284                 goto error;
285         }
286         if (a_location) {
287                 cr_tknzr_get_parsing_location (a_this, 
288                                                a_location) ;
289         }
290         RECORD_CUR_BYTE_ADDR (a_this, a_start);
291         *a_end = *a_start;
293         for (;;) {
294                 gboolean is_eof = FALSE;
296                 cr_input_get_end_of_file (PRIVATE (a_this)->input, &is_eof);
297                 if (is_eof)
298                         break;
300                 status = cr_tknzr_peek_char (a_this, &cur_char);
301                 if (status == CR_END_OF_INPUT_ERROR) {
302                         status = CR_OK;
303                         break;
304                 } else if (status != CR_OK) {
305                         goto error;
306                 }
308                 if (cr_utils_is_white_space (cur_char) == TRUE) {
309                         READ_NEXT_CHAR (a_this, &cur_char);
310                         RECORD_CUR_BYTE_ADDR (a_this, a_end);
311                 } else {
312                         break;
313                 }
314         }
316         return CR_OK;
318       error:
319         cr_tknzr_set_cur_pos (a_this, &init_pos);
321         return status;
324 /**
325  *Parses a newline as defined in the css2 spec:
326  * nl   ::=    \n|\r\n|\r|\f
327  *
328  *@param a_this the "this pointer" of the current instance of #CRTknzr.
329  *@param a_start a pointer to the first character of the successfully 
330  *parsed string.
331  *@param a_end a pointer to the last character of the successfully parsed
332  *string.
333  *@result CR_OK uppon successfull completion, an error code otherwise.
334  */
335 static enum CRStatus
336 cr_tknzr_parse_nl (CRTknzr * a_this, 
337                    guchar ** a_start, 
338                    guchar ** a_end, 
339                    CRParsingLocation *a_location)
341         CRInputPos init_pos;
342         guchar next_chars[2] = { 0 };
343         enum CRStatus status = CR_PARSING_ERROR;
345         g_return_val_if_fail (a_this && PRIVATE (a_this)
346                               && a_start && a_end, CR_BAD_PARAM_ERROR);
348         RECORD_INITIAL_POS (a_this, &init_pos);
350         PEEK_BYTE (a_this, 1, &next_chars[0]);
351         PEEK_BYTE (a_this, 2, &next_chars[1]);
353         if ((next_chars[0] == '\r' && next_chars[1] == '\n')) {
354                 SKIP_BYTES (a_this, 1);
355                 if (a_location) {
356                         cr_tknzr_get_parsing_location 
357                                 (a_this, a_location) ;
358                 }
359                 SKIP_CHARS (a_this, 1);
361                 RECORD_CUR_BYTE_ADDR (a_this, a_end);
363                 status = CR_OK;
364         } else if (next_chars[0] == '\n'
365                    || next_chars[0] == '\r' || next_chars[0] == '\f') {
366                 SKIP_CHARS (a_this, 1);
367                 if (a_location) {
368                         cr_tknzr_get_parsing_location 
369                                 (a_this, a_location) ;
370                 }
371                 RECORD_CUR_BYTE_ADDR (a_this, a_start);
372                 *a_end = *a_start;
373                 status = CR_OK;
374         } else {
375                 status = CR_PARSING_ERROR;
376                 goto error;
377         }
378         return CR_OK ;
380  error:
381         cr_tknzr_set_cur_pos (a_this, &init_pos) ;
382         return status;
385 /**
386  *Go ahead in the parser input, skipping all the spaces.
387  *If the next char if not a white space, this function does nothing.
388  *In any cases, it stops when it encounters a non white space character.
389  *
390  *@param a_this the current instance of #CRTknzr.
391  *@return CR_OK upon successfull completion, an error code otherwise.
392  */
393 static enum CRStatus
394 cr_tknzr_try_to_skip_spaces (CRTknzr * a_this)
396         enum CRStatus status = CR_ERROR;
397         guint32 cur_char = 0;
399         g_return_val_if_fail (a_this && PRIVATE (a_this)
400                               && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
402         status = cr_input_peek_char (PRIVATE (a_this)->input, &cur_char);
404         if (status != CR_OK) {
405                 if (status == CR_END_OF_INPUT_ERROR)
406                         return CR_OK;
407                 return status;
408         }
410         if (cr_utils_is_white_space (cur_char) == TRUE) {
411                 gulong nb_chars = -1; /*consume all spaces */
413                 status = cr_input_consume_white_spaces
414                         (PRIVATE (a_this)->input, &nb_chars);
415         }
417         return status;
420 /**
421  *Parses a "comment" as defined in the css spec at [4.1.1]:
422  *COMMENT ::= \/\*[^*]*\*+([^/][^*]*\*+)*\/ .
423  *This complex regexp is just to say that comments start
424  *with the two chars '/''*' and ends with the two chars '*''/'.
425  *It also means that comments cannot be nested.
426  *So based on that, I've just tried to implement the parsing function
427  *simply and in a straight forward manner.
428  */
429 static enum CRStatus
430 cr_tknzr_parse_comment (CRTknzr * a_this, 
431                         CRString ** a_comment)
433         enum CRStatus status = CR_OK;
434         CRInputPos init_pos;
435         guint32 cur_char = 0, next_char= 0;
436         CRString *comment = NULL;
437         CRParsingLocation loc = {0,0,0} ;
439         g_return_val_if_fail (a_this && PRIVATE (a_this)
440                               && PRIVATE (a_this)->input, 
441                               CR_BAD_PARAM_ERROR);
443         RECORD_INITIAL_POS (a_this, &init_pos);        
444         READ_NEXT_CHAR (a_this, &cur_char) ;        
445         ENSURE_PARSING_COND (cur_char == '/');
446         cr_tknzr_get_parsing_location (a_this, &loc) ;
448         READ_NEXT_CHAR (a_this, &cur_char);
449         ENSURE_PARSING_COND (cur_char == '*');
450         comment = cr_string_new ();
451         for (;;) {
452                 READ_NEXT_CHAR (a_this, &cur_char);
454                 /*make sure there are no nested comments */
455                 if (cur_char == '/') {
456                         READ_NEXT_CHAR (a_this, &cur_char);
457                         ENSURE_PARSING_COND (cur_char != '*');
458                         g_string_append_c (comment->stryng, '/');
459                         g_string_append_unichar (comment->stryng, 
460                                                  cur_char);
461                         continue;
462                 }
464                 /*Detect the end of the comments region */
465                 if (cur_char == '*') {
466                         PEEK_NEXT_CHAR (a_this, &next_char);
468                         if (next_char == '/') {
469                                 /*
470                                  *end of comments region
471                                  *Now, call the right SAC callback.
472                                  */
473                                 SKIP_CHARS (a_this, 1) ;
474                                 status = CR_OK;
475                                 break;
476                         } else {
477                                 g_string_append_c (comment->stryng, 
478                                                    '*');
479                         }
480                 }
481                 g_string_append_unichar (comment->stryng, cur_char);
482         }
484         if (status == CR_OK) {
485                 cr_parsing_location_copy (&comment->location, 
486                                           &loc) ;
487                 *a_comment = comment;                
488                 return CR_OK;
489         }
490  error:
492         if (comment) {
493                 cr_string_destroy (comment);
494                 comment = NULL;
495         }
497         cr_tknzr_set_cur_pos (a_this, &init_pos);
499         return status;
502 /**
503  *Parses an 'unicode' escape sequence defined
504  *in css spec at chap 4.1.1:
505  *unicode ::= \\[0-9a-f]{1,6}[ \n\r\t\f]?
506  *@param a_this the current instance of #CRTknzr.
507  *@param a_start out parameter. A pointer to the start
508  *of the unicode escape sequence. Must *NOT* be deleted by
509  *the caller.
510  *@param a_end out parameter. A pointer to the last character
511  *of the unicode escape sequence. Must *NOT* be deleted by the caller.
512  *@return CR_OK if parsing succeded, an error code otherwise.
513  *Error code can be either CR_PARSING_ERROR if the string 
514  *parsed just doesn't
515  *respect the production or another error if a 
516  *lower level error occured.
517  */
518 static enum CRStatus
519 cr_tknzr_parse_unicode_escape (CRTknzr * a_this, 
520                                guint32 * a_unicode,
521                                CRParsingLocation *a_location)
523         guint32 cur_char;
524         CRInputPos init_pos;
525         glong occur = 0;
526         guint32 unicode = 0;
527         guchar *tmp_char_ptr1 = NULL,
528                 *tmp_char_ptr2 = NULL;
529         enum CRStatus status = CR_OK;
531         g_return_val_if_fail (a_this && PRIVATE (a_this)
532                               && a_unicode, CR_BAD_PARAM_ERROR);
534         /*first, let's backup the current position pointer */
535         RECORD_INITIAL_POS (a_this, &init_pos);
537         READ_NEXT_CHAR (a_this, &cur_char);
539         if (cur_char != '\\') {
540                 status = CR_PARSING_ERROR;
541                 goto error;
542         }
543         if (a_location) {
544                 cr_tknzr_get_parsing_location 
545                         (a_this, a_location) ;
546         }
547         PEEK_NEXT_CHAR (a_this, &cur_char);
549         for (occur = 0, unicode = 0; ((cur_char >= '0' && cur_char <= '9')
550                                       || (cur_char >= 'a' && cur_char <= 'f')
551                                       || (cur_char >= 'A' && cur_char <= 'F'))
552              && occur < 6; occur++) {
553                 gint cur_char_val = 0;
555                 READ_NEXT_CHAR (a_this, &cur_char);
557                 if ((cur_char >= '0' && cur_char <= '9')) {
558                         cur_char_val = (cur_char - '0');
559                 } else if ((cur_char >= 'a' && cur_char <= 'f')) {
560                         cur_char_val = 10 + (cur_char - 'a');
561                 } else if ((cur_char >= 'A' && cur_char <= 'F')) {
562                         cur_char_val = 10 + (cur_char - 'A');
563                 }
565                 unicode = unicode * 10 + cur_char_val;
567                 PEEK_NEXT_CHAR (a_this, &cur_char);
568         }
570         if (occur == 5) {
571                 /*
572                  *the unicode escape is 6 digit length
573                  */
575                 /*
576                  *parse one space that may 
577                  *appear just after the unicode
578                  *escape.
579                  */
580                 cr_tknzr_parse_w (a_this, &tmp_char_ptr1, 
581                                   &tmp_char_ptr2, NULL);
582                 status = CR_OK;
583         } else {
584                 /*
585                  *The unicode escape is less than
586                  *6 digit length. The character
587                  *that comes right after the escape
588                  *must be a white space.
589                  */
590                 status = cr_tknzr_parse_w (a_this, &tmp_char_ptr1,
591                                            &tmp_char_ptr2, NULL);
592         }
594         if (status == CR_OK) {
595                 *a_unicode = unicode;
596                 return CR_OK;
597         }
599       error:
600         /*
601          *restore the initial position pointer backuped at
602          *the beginning of this function.
603          */
604         cr_tknzr_set_cur_pos (a_this, &init_pos);
606         return status;
609 /**
610  *parses an escape sequence as defined by the css spec:
611  *escape ::= {unicode}|\\[ -~\200-\4177777]
612  *@param a_this the current instance of #CRTknzr .
613  */
614 static enum CRStatus
615 cr_tknzr_parse_escape (CRTknzr * a_this, guint32 * a_esc_code,
616                        CRParsingLocation *a_location)
618         enum CRStatus status = CR_OK;
619         guint32 cur_char = 0;
620         CRInputPos init_pos;
621         guchar next_chars[2];
623         g_return_val_if_fail (a_this && PRIVATE (a_this)
624                               && a_esc_code, CR_BAD_PARAM_ERROR);
626         RECORD_INITIAL_POS (a_this, &init_pos);
628         PEEK_BYTE (a_this, 1, &next_chars[0]);
629         PEEK_BYTE (a_this, 2, &next_chars[1]);
631         if (next_chars[0] != '\\') {
632                 status = CR_PARSING_ERROR;
633                 goto error;
634         }
636         if ((next_chars[1] >= '0' && next_chars[1] <= '9')
637             || (next_chars[1] >= 'a' && next_chars[1] <= 'f')
638             || (next_chars[1] >= 'A' && next_chars[1] <= 'F')) {
639                 status = cr_tknzr_parse_unicode_escape (a_this, a_esc_code, 
640                                                         a_location);
641         } else {
642                 /*consume the '\' char */
643                 READ_NEXT_CHAR (a_this, &cur_char);
644                 if (a_location) {
645                         cr_tknzr_get_parsing_location (a_this, 
646                                                        a_location) ;
647                 }
648                 /*then read the char after the '\' */
649                 READ_NEXT_CHAR (a_this, &cur_char);
651                 if (cur_char != ' ' && (cur_char < 200 || cur_char > 4177777)) {
652                         status = CR_PARSING_ERROR;
653                         goto error;
654                 }
655                 *a_esc_code = cur_char;
657         }
658         if (status == CR_OK) {
659                 return CR_OK;
660         }
661  error:
662         cr_tknzr_set_cur_pos (a_this, &init_pos);
663         return status;
666 /**
667  *Parses a string type as defined in css spec [4.1.1]:
668  *
669  *string ::= {string1}|{string2}
670  *string1 ::= \"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\"
671  *string2 ::= \'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\'
672  *
673  *@param a_this the current instance of #CRTknzr.
674  *@param a_start out parameter. Upon successfull completion, 
675  *points to the beginning of the string, points to an undefined value
676  *otherwise.
677  *@param a_end out parameter. Upon successfull completion, points to
678  *the beginning of the string, points to an undefined value otherwise.
679  *@return CR_OK upon successfull completion, an error code otherwise.
680  */
681 static enum CRStatus
682 cr_tknzr_parse_string (CRTknzr * a_this, CRString ** a_str)
684         guint32 cur_char = 0,
685                 delim = 0;
686         CRInputPos init_pos;
687         enum CRStatus status = CR_OK;
688         CRString *str = NULL;
690         g_return_val_if_fail (a_this && PRIVATE (a_this)
691                               && PRIVATE (a_this)->input
692                               && a_str, CR_BAD_PARAM_ERROR);
694         RECORD_INITIAL_POS (a_this, &init_pos);
695         READ_NEXT_CHAR (a_this, &cur_char);
697         if (cur_char == '"')
698                 delim = '"';
699         else if (cur_char == '\'')
700                 delim = '\'';
701         else {
702                 status = CR_PARSING_ERROR;
703                 goto error;
704         }
705         str = cr_string_new ();
706         if (str) {
707                 cr_tknzr_get_parsing_location 
708                         (a_this, &str->location) ;
709         }
710         for (;;) {
711                 guchar next_chars[2] = { 0 };
713                 PEEK_BYTE (a_this, 1, &next_chars[0]);
714                 PEEK_BYTE (a_this, 2, &next_chars[1]);
716                 if (next_chars[0] == '\\') {
717                         guchar *tmp_char_ptr1 = NULL,
718                                 *tmp_char_ptr2 = NULL;
719                         guint32 esc_code = 0;
721                         if (next_chars[1] == '\'' || next_chars[1] == '"') {
722                                 g_string_append_unichar (str->stryng, 
723                                                          next_chars[1]);
724                                 SKIP_BYTES (a_this, 2);
725                                 status = CR_OK;
726                         } else {
727                                 status = cr_tknzr_parse_escape
728                                         (a_this, &esc_code, NULL);
730                                 if (status == CR_OK) {
731                                         g_string_append_unichar
732                                                 (str->stryng, 
733                                                  esc_code);
734                                 }
735                         }
737                         if (status != CR_OK) {
738                                 /*
739                                  *consume the '\' char, and try to parse
740                                  *a newline.
741                                  */
742                                 READ_NEXT_CHAR (a_this, &cur_char);
744                                 status = cr_tknzr_parse_nl
745                                         (a_this, &tmp_char_ptr1,
746                                          &tmp_char_ptr2, NULL);
747                         }
749                         CHECK_PARSING_STATUS (status, FALSE);
750                 } else if (strchr ("\t !#$%&", next_chars[0])
751                            || (next_chars[0] >= '(' && next_chars[0] <= '~')) {
752                         READ_NEXT_CHAR (a_this, &cur_char);
753                         g_string_append_unichar (str->stryng, 
754                                                  cur_char);
755                         status = CR_OK;
756                 }
758                 else if (cr_utils_is_nonascii (next_chars[0])) {
759                         READ_NEXT_CHAR (a_this, &cur_char);
760                         g_string_append_unichar (str->stryng, cur_char);
761                 } else if (next_chars[0] == delim) {
762                         READ_NEXT_CHAR (a_this, &cur_char);
763                         break;
764                 } else {
765                         status = CR_PARSING_ERROR;
766                         goto error;
767                 }
768         }
770         if (status == CR_OK) {
771                 if (*a_str == NULL) {
772                         *a_str = str;
773                         str = NULL;
774                 } else {
775                         (*a_str)->stryng = g_string_append_len
776                                 ((*a_str)->stryng,
777                                  str->stryng->str, 
778                                  str->stryng->len);
779                         cr_string_destroy (str);
780                 }
781                 return CR_OK;
782         }
784  error:
786         if (str) {
787                 cr_string_destroy (str) ;
788                 str = NULL;
789         }
790         cr_tknzr_set_cur_pos (a_this, &init_pos);
791         return status;
794 /**
795  *Parses the an nmstart as defined by the css2 spec [4.1.1]:
796  * nmstart [a-zA-Z]|{nonascii}|{escape}
797  *
798  *@param a_this the current instance of #CRTknzr.
799  *@param a_start out param. A pointer to the starting point of
800  *the token.
801  *@param a_end out param. A pointer to the ending point of the
802  *token.
803  *@param a_char out param. The actual parsed nmchar.
804  *@return CR_OK upon successfull completion, 
805  *an error code otherwise.
806  */
807 static enum CRStatus
808 cr_tknzr_parse_nmstart (CRTknzr * a_this, 
809                         guint32 * a_char,
810                         CRParsingLocation *a_location)
812         CRInputPos init_pos;
813         enum CRStatus status = CR_OK;
814         guint32 cur_char = 0,
815                 next_char = 0;
817         g_return_val_if_fail (a_this && PRIVATE (a_this)
818                               && PRIVATE (a_this)->input
819                               && a_char, CR_BAD_PARAM_ERROR);
821         RECORD_INITIAL_POS (a_this, &init_pos);
823         PEEK_NEXT_CHAR (a_this, &next_char);
825         if (next_char == '\\') {
826                 status = cr_tknzr_parse_escape (a_this, a_char,
827                                                 a_location);
829                 if (status != CR_OK)
830                         goto error;
832         } else if (cr_utils_is_nonascii (next_char) == TRUE
833                    || ((next_char >= 'a') && (next_char <= 'z'))
834                    || ((next_char >= 'A') && (next_char <= 'Z'))
835                 ) {
836                 READ_NEXT_CHAR (a_this, &cur_char);
837                 if (a_location) {
838                         cr_tknzr_get_parsing_location (a_this, 
839                                                        a_location) ;
840                 }
841                 *a_char = cur_char;
842                 status = CR_OK;
843         } else {
844                 status = CR_PARSING_ERROR;
845                 goto error;
846         }
848         return CR_OK;
850  error:        
851         cr_tknzr_set_cur_pos (a_this, &init_pos);
853         return status;
857 /**
858  *Parses an nmchar as described in the css spec at
859  *chap 4.1.1:
860  *nmchar ::= [a-z0-9-]|{nonascii}|{escape}
861  *
862  *Humm, I have added the possibility for nmchar to
863  *contain upper case letters.
864  *
865  *@param a_this the current instance of #CRTknzr.
866  *@param a_start out param. A pointer to the starting point of
867  *the token.
868  *@param a_end out param. A pointer to the ending point of the
869  *token.
870  *@param a_char out param. The actual parsed nmchar.
871  *@return CR_OK upon successfull completion, 
872  *an error code otherwise.
873  */
874 static enum CRStatus
875 cr_tknzr_parse_nmchar (CRTknzr * a_this, guint32 * a_char,
876                        CRParsingLocation *a_location)
878         guint32 cur_char = 0,
879                 next_char = 0;
880         enum CRStatus status = CR_OK;
881         CRInputPos init_pos;
883         g_return_val_if_fail (a_this && PRIVATE (a_this) && a_char,
884                               CR_BAD_PARAM_ERROR);
886         RECORD_INITIAL_POS (a_this, &init_pos);
888         status = cr_input_peek_char (PRIVATE (a_this)->input, 
889                                      &next_char) ;
890         if (status != CR_OK)
891                 goto error;
893         if (next_char == '\\') {
894                 status = cr_tknzr_parse_escape (a_this, a_char, 
895                                                 a_location);
897                 if (status != CR_OK)
898                         goto error;
900         } else if (cr_utils_is_nonascii (next_char) == TRUE
901                    || ((next_char >= 'a') && (next_char <= 'z'))
902                    || ((next_char >= 'A') && (next_char <= 'Z'))
903                    || ((next_char >= '0') && (next_char <= '9'))
904                    || (next_char == '-')
905                    || (next_char == '_') /*'_' not allowed by the spec. */
906                 ) {
907                 READ_NEXT_CHAR (a_this, &cur_char);
908                 *a_char = cur_char;
909                 status = CR_OK;
910                 if (a_location) {
911                         cr_tknzr_get_parsing_location
912                                 (a_this, a_location) ;
913                 }
914         } else {
915                 status = CR_PARSING_ERROR;
916                 goto error;
917         }
918         return CR_OK;
920  error:
921         cr_tknzr_set_cur_pos (a_this, &init_pos);
922         return status;
925 /**
926  *Parses an "ident" as defined in css spec [4.1.1]:
927  *ident ::= {nmstart}{nmchar}*
928  *
929  *Actually parses it using the css3 grammar:
930  *ident ::= -?{nmstart}{nmchar}*
931  *@param a_this the currens instance of #CRTknzr.
932  *
933  *@param a_str a pointer to parsed ident. If *a_str is NULL,
934  *this function allocates a new instance of CRString. If not, 
935  *the function just appends the parsed string to the one passed.
936  *In both cases it is up to the caller to free *a_str.
937  *
938  *@return CR_OK upon successfull completion, an error code 
939  *otherwise.
940  */
941 static enum CRStatus
942 cr_tknzr_parse_ident (CRTknzr * a_this, CRString ** a_str)
944         guint32 tmp_char = 0;
945         CRString *stringue = NULL ;
946         CRInputPos init_pos;
947         enum CRStatus status = CR_OK;
948         gboolean location_is_set = FALSE ;
950         g_return_val_if_fail (a_this && PRIVATE (a_this)
951                               && PRIVATE (a_this)->input
952                               && a_str, CR_BAD_PARAM_ERROR);
954         RECORD_INITIAL_POS (a_this, &init_pos);
955         PEEK_NEXT_CHAR (a_this, &tmp_char) ;
956         stringue = cr_string_new () ;
957         g_return_val_if_fail (stringue, 
958                               CR_OUT_OF_MEMORY_ERROR) ;
960         if (tmp_char == '-') {
961                 READ_NEXT_CHAR (a_this, &tmp_char) ;
962                 cr_tknzr_get_parsing_location
963                         (a_this, &stringue->location) ;
964                 location_is_set = TRUE ;
965                 g_string_append_unichar (stringue->stryng, 
966                                          tmp_char) ;
967         }
968         status = cr_tknzr_parse_nmstart (a_this, &tmp_char, NULL);
969         if (status != CR_OK) {
970                 status = CR_PARSING_ERROR;
971                 goto end ;
972         }
973         if (location_is_set == FALSE) {
974                 cr_tknzr_get_parsing_location 
975                         (a_this, &stringue->location) ;
976                 location_is_set = TRUE ;
977         }
978         g_string_append_unichar (stringue->stryng, tmp_char);
979         for (;;) {
980                 status = cr_tknzr_parse_nmchar (a_this, 
981                                                 &tmp_char, 
982                                                 NULL);
983                 if (status != CR_OK) {
984                         status = CR_OK ;
985                         break;
986                 }
987                 g_string_append_unichar (stringue->stryng, tmp_char);
988         }
989         if (status == CR_OK) {
990                 if (!*a_str) {
991                         *a_str = stringue ;
992                 
993                 } else {
994                         g_string_append_len ((*a_str)->stryng, 
995                                              stringue->stryng->str, 
996                                              stringue->stryng->len) ;
997                         cr_string_destroy (stringue) ;
998                 }
999                 stringue = NULL ;
1000         }
1002  error:
1003  end:
1004         if (stringue) {
1005                 cr_string_destroy (stringue) ;
1006                 stringue = NULL ;
1007         }
1008         if (status != CR_OK ) {
1009                 cr_tknzr_set_cur_pos (a_this, &init_pos) ;
1010         }
1011         return status ;
1015 /**
1016  *Parses a "name" as defined by css spec [4.1.1]:
1017  *name ::= {nmchar}+
1018  *
1019  *@param a_this the current instance of #CRTknzr.
1020  *
1021  *@param a_str out parameter. A pointer to the successfully parsed
1022  *name. If *a_str is set to NULL, this function allocates a new instance
1023  *of CRString. If not, it just appends the parsed name to the passed *a_str.
1024  *In both cases, it is up to the caller to free *a_str.
1025  *
1026  *@return CR_OK upon successfull completion, an error code otherwise.
1027  */
1028 static enum CRStatus
1029 cr_tknzr_parse_name (CRTknzr * a_this, 
1030                      CRString ** a_str)
1032         guint32 tmp_char = 0;
1033         CRInputPos init_pos;
1034         enum CRStatus status = CR_OK;
1035         gboolean str_needs_free = FALSE,
1036                 is_first_nmchar=TRUE ;
1037         glong i = 0;
1038         CRParsingLocation loc = {0,0,0} ;
1040         g_return_val_if_fail (a_this && PRIVATE (a_this)
1041                               && PRIVATE (a_this)->input
1042                               && a_str,
1043                               CR_BAD_PARAM_ERROR) ;
1045         RECORD_INITIAL_POS (a_this, &init_pos);
1047         if (*a_str == NULL) {
1048                 *a_str = cr_string_new ();
1049                 str_needs_free = TRUE;
1050         }
1051         for (i = 0;; i++) {
1052                 if (is_first_nmchar == TRUE) {
1053                         status = cr_tknzr_parse_nmchar 
1054                                 (a_this, &tmp_char,
1055                                  &loc) ;
1056                         is_first_nmchar = FALSE ;
1057                 } else {
1058                         status = cr_tknzr_parse_nmchar 
1059                                 (a_this, &tmp_char, NULL) ;
1060                 }
1061                 if (status != CR_OK)
1062                         break;                
1063                 g_string_append_unichar ((*a_str)->stryng, 
1064                                          tmp_char);
1065         }
1066         if (i > 0) {
1067                 cr_parsing_location_copy 
1068                         (&(*a_str)->location, &loc) ;
1069                 return CR_OK;
1070         }
1071         if (str_needs_free == TRUE && *a_str) {
1072                 cr_string_destroy (*a_str);
1073                 *a_str = NULL;
1074         }
1075         cr_tknzr_set_cur_pos (a_this, &init_pos);
1076         return CR_PARSING_ERROR;
1079 /**
1080  *Parses a "hash" as defined by the css spec in [4.1.1]:
1081  *HASH ::= #{name}
1082  */
1083 static enum CRStatus
1084 cr_tknzr_parse_hash (CRTknzr * a_this, CRString ** a_str)
1086         guint32 cur_char = 0;
1087         CRInputPos init_pos;
1088         enum CRStatus status = CR_OK;
1089         gboolean str_needs_free = FALSE;
1090         CRParsingLocation loc = {0,0,0} ;
1092         g_return_val_if_fail (a_this && PRIVATE (a_this)
1093                               && PRIVATE (a_this)->input,
1094                               CR_BAD_PARAM_ERROR);
1096         RECORD_INITIAL_POS (a_this, &init_pos);
1097         READ_NEXT_CHAR (a_this, &cur_char);
1098         if (cur_char != '#') {
1099                 status = CR_PARSING_ERROR;
1100                 goto error;
1101         }
1102         if (*a_str == NULL) {
1103                 *a_str = cr_string_new ();
1104                 str_needs_free = TRUE;
1105         }
1106         cr_tknzr_get_parsing_location (a_this,
1107                                        &loc) ;
1108         status = cr_tknzr_parse_name (a_this, a_str);
1109         cr_parsing_location_copy (&(*a_str)->location, &loc) ;
1110         if (status != CR_OK) {
1111                 goto error;
1112         }
1113         return CR_OK;
1115  error:
1116         if (str_needs_free == TRUE && *a_str) {
1117                 cr_string_destroy (*a_str);
1118                 *a_str = NULL;
1119         }
1121         cr_tknzr_set_cur_pos (a_this, &init_pos);
1122         return status;
1125 /**
1126  *Parses an uri as defined by the css spec [4.1.1]:
1127  * URI ::= url\({w}{string}{w}\)
1128  *         |url\({w}([!#$%&*-~]|{nonascii}|{escape})*{w}\)
1129  *
1130  *@param a_this the current instance of #CRTknzr.
1131  *@param a_str the successfully parsed url.
1132  *@return CR_OK upon successfull completion, an error code otherwise.
1133  */
1134 static enum CRStatus
1135 cr_tknzr_parse_uri (CRTknzr * a_this, 
1136                     CRString ** a_str)
1138         guint32 cur_char = 0;
1139         CRInputPos init_pos;
1140         enum CRStatus status = CR_PARSING_ERROR;
1141         guchar tab[4] = { 0 }, *tmp_ptr1 = NULL, *tmp_ptr2 = NULL;
1142         CRString *str = NULL;
1143         CRParsingLocation location = {0,0,0} ;
1145         g_return_val_if_fail (a_this 
1146                               && PRIVATE (a_this)
1147                               && PRIVATE (a_this)->input
1148                               && a_str, 
1149                               CR_BAD_PARAM_ERROR);
1151         RECORD_INITIAL_POS (a_this, &init_pos);
1153         PEEK_BYTE (a_this, 1, &tab[0]);
1154         PEEK_BYTE (a_this, 2, &tab[1]);
1155         PEEK_BYTE (a_this, 3, &tab[2]);
1156         PEEK_BYTE (a_this, 4, &tab[3]);
1158         if (tab[0] != 'u' || tab[1] != 'r' || tab[2] != 'l' || tab[3] != '(') {
1159                 status = CR_PARSING_ERROR;
1160                 goto error;
1161         }
1162         /*
1163          *Here, we want to skip 4 bytes ('u''r''l''(').
1164          *But we also need to keep track of the parsing location
1165          *of the 'u'. So, we skip 1 byte, we record the parsing
1166          *location, then we skip the 3 remaining bytes.
1167          */
1168         SKIP_CHARS (a_this, 1);
1169         cr_tknzr_get_parsing_location (a_this, &location) ;
1170         SKIP_CHARS (a_this, 3);
1171         cr_tknzr_try_to_skip_spaces (a_this);
1172         status = cr_tknzr_parse_string (a_this, a_str);
1174         if (status == CR_OK) {
1175                 guint32 next_char = 0;
1176                 status = cr_tknzr_parse_w (a_this, &tmp_ptr1, 
1177                                            &tmp_ptr2, NULL);
1178                 cr_tknzr_try_to_skip_spaces (a_this);
1179                 PEEK_NEXT_CHAR (a_this, &next_char);
1180                 if (next_char == ')') {
1181                         READ_NEXT_CHAR (a_this, &cur_char);
1182                         status = CR_OK;
1183                 } else {
1184                         status = CR_PARSING_ERROR;
1185                 }
1186         }
1187         if (status != CR_OK) {
1188                 str = cr_string_new ();
1189                 for (;;) {
1190                         guint32 next_char = 0;
1191                         PEEK_NEXT_CHAR (a_this, &next_char);
1192                         if (strchr ("!#$%&", next_char)
1193                             || (next_char >= '*' && next_char <= '~')
1194                             || (cr_utils_is_nonascii (next_char) == TRUE)) {
1195                                 READ_NEXT_CHAR (a_this, &cur_char);
1196                                 g_string_append_unichar 
1197                                         (str->stryng, cur_char);
1198                                 status = CR_OK;
1199                         } else {
1200                                 guint32 esc_code = 0;
1201                                 status = cr_tknzr_parse_escape
1202                                         (a_this, &esc_code, NULL);
1203                                 if (status == CR_OK) {
1204                                         g_string_append_unichar
1205                                                 (str->stryng, 
1206                                                  esc_code);
1207                                 } else {
1208                                         status = CR_OK;
1209                                         break;
1210                                 }
1211                         }
1212                 }
1213                 cr_tknzr_try_to_skip_spaces (a_this);
1214                 READ_NEXT_CHAR (a_this, &cur_char);
1215                 if (cur_char == ')') {
1216                         status = CR_OK;
1217                 } else {
1218                         status = CR_PARSING_ERROR;
1219                         goto error;
1220                 }
1221                 if (str) {                        
1222                         if (*a_str == NULL) {
1223                                 *a_str = str;
1224                                 str = NULL;
1225                         } else {
1226                                 g_string_append_len
1227                                         ((*a_str)->stryng,
1228                                          str->stryng->str,
1229                                          str->stryng->len);
1230                                 cr_string_destroy (str);
1231                         }                        
1232                 }
1233         }
1235         cr_parsing_location_copy
1236                 (&(*a_str)->location,
1237                  &location) ;
1238         return CR_OK ;
1239  error:
1240         if (str) {
1241                 cr_string_destroy (str);
1242                 str = NULL;
1243         }
1244         cr_tknzr_set_cur_pos (a_this, &init_pos);
1245         return status;
1248 /**
1249  *parses an RGB as defined in the css2 spec.
1250  *rgb: rgb '('S*{num}%?S* ',' {num}#?S*,S*{num}#?S*')'
1251  *
1252  *@param a_this the "this pointer" of the current instance of
1253  *@param a_rgb out parameter the parsed rgb.
1254  *@return CR_OK upon successfull completion, an error code otherwise.
1255  */
1256 static enum CRStatus
1257 cr_tknzr_parse_rgb (CRTknzr * a_this, CRRgb ** a_rgb)
1259         enum CRStatus status = CR_OK;
1260         CRInputPos init_pos;
1261         CRNum *num = NULL;
1262         guchar next_bytes[3] = { 0 }, cur_byte = 0;
1263         glong red = 0,
1264                 green = 0,
1265                 blue = 0,
1266                 i = 0;
1267         gboolean is_percentage = FALSE;
1268         CRParsingLocation location = {0,0,0} ;
1270         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1272         RECORD_INITIAL_POS (a_this, &init_pos);
1274         PEEK_BYTE (a_this, 1, &next_bytes[0]);
1275         PEEK_BYTE (a_this, 2, &next_bytes[1]);
1276         PEEK_BYTE (a_this, 3, &next_bytes[2]);
1278         if (((next_bytes[0] == 'r') || (next_bytes[0] == 'R'))
1279             && ((next_bytes[1] == 'g') || (next_bytes[1] == 'G'))
1280             && ((next_bytes[2] == 'b') || (next_bytes[2] == 'B'))) {
1281                 SKIP_CHARS (a_this, 1);
1282                 cr_tknzr_get_parsing_location (a_this, &location) ;
1283                 SKIP_CHARS (a_this, 2);
1284         } else {
1285                 status = CR_PARSING_ERROR;
1286                 goto error;
1287         }
1288         READ_NEXT_BYTE (a_this, &cur_byte);
1289         ENSURE_PARSING_COND (cur_byte == '(');
1291         cr_tknzr_try_to_skip_spaces (a_this);
1292         status = cr_tknzr_parse_num (a_this, &num);
1293         ENSURE_PARSING_COND ((status == CR_OK) && (num != NULL));
1295         red = (glong)num->val;
1296         cr_num_destroy (num);
1297         num = NULL;
1299         PEEK_BYTE (a_this, 1, &next_bytes[0]);
1300         if (next_bytes[0] == '%') {
1301                 SKIP_CHARS (a_this, 1);
1302                 is_percentage = TRUE;
1303         }
1304         cr_tknzr_try_to_skip_spaces (a_this);
1306         for (i = 0; i < 2; i++) {
1307                 READ_NEXT_BYTE (a_this, &cur_byte);
1308                 ENSURE_PARSING_COND (cur_byte == ',');
1310                 cr_tknzr_try_to_skip_spaces (a_this);
1311                 status = cr_tknzr_parse_num (a_this, &num);
1312                 ENSURE_PARSING_COND ((status == CR_OK) && (num != NULL));
1314                 PEEK_BYTE (a_this, 1, &next_bytes[0]);
1315                 if (next_bytes[0] == '%') {
1316                         SKIP_CHARS (a_this, 1);
1317                         is_percentage = 1;
1318                 }
1320                 if (i == 0) {
1321                         green = (glong)num->val;
1322                 } else if (i == 1) {
1323                         blue = (glong)num->val;
1324                 }
1326                 if (num) {
1327                         cr_num_destroy (num);
1328                         num = NULL;
1329                 }
1330                 cr_tknzr_try_to_skip_spaces (a_this);
1331         }
1333         READ_NEXT_BYTE (a_this, &cur_byte);
1334         if (*a_rgb == NULL) {
1335                 *a_rgb = cr_rgb_new_with_vals (red, green, blue,
1336                                                is_percentage);
1338                 if (*a_rgb == NULL) {
1339                         status = CR_ERROR;
1340                         goto error;
1341                 }
1342                 status = CR_OK;
1343         } else {
1344                 (*a_rgb)->red = red;
1345                 (*a_rgb)->green = green;
1346                 (*a_rgb)->blue = blue;
1347                 (*a_rgb)->is_percentage = is_percentage;
1349                 status = CR_OK;
1350         }
1352         if (status == CR_OK) {
1353                 if (a_rgb && *a_rgb) {
1354                         cr_parsing_location_copy 
1355                                 (&(*a_rgb)->location, 
1356                                  &location) ;
1357                 }
1358                 return CR_OK;
1359         }
1361  error:
1362         if (num) {
1363                 cr_num_destroy (num);
1364                 num = NULL;
1365         }
1367         cr_tknzr_set_cur_pos (a_this, &init_pos);
1368         return CR_OK;
1371 /**
1372  *Parses a atkeyword as defined by the css spec in [4.1.1]:
1373  *ATKEYWORD ::= @{ident}
1374  *
1375  *@param a_this the "this pointer" of the current instance of
1376  *#CRTknzr.
1377  *
1378  *@param a_str out parameter. The parsed atkeyword. If *a_str is
1379  *set to NULL this function allocates a new instance of CRString and
1380  *sets it to the parsed atkeyword. If not, this function just appends
1381  *the parsed atkeyword to the end of *a_str. In both cases it is up to
1382  *the caller to free *a_str.
1383  *
1384  *@return CR_OK upon successfull completion, an error code otherwise.
1385  */
1386 static enum CRStatus
1387 cr_tknzr_parse_atkeyword (CRTknzr * a_this, 
1388                           CRString ** a_str)
1390         guint32 cur_char = 0;
1391         CRInputPos init_pos;
1392         gboolean str_needs_free = FALSE;
1393         enum CRStatus status = CR_OK;
1395         g_return_val_if_fail (a_this && PRIVATE (a_this)
1396                               && PRIVATE (a_this)->input
1397                               && a_str, CR_BAD_PARAM_ERROR);
1399         RECORD_INITIAL_POS (a_this, &init_pos);
1401         READ_NEXT_CHAR (a_this, &cur_char);
1403         if (cur_char != '@') {
1404                 status = CR_PARSING_ERROR;
1405                 goto error;
1406         }
1408         if (*a_str == NULL) {
1409                 *a_str = cr_string_new ();
1410                 str_needs_free = TRUE;
1411         }
1412         status = cr_tknzr_parse_ident (a_this, a_str);
1413         if (status != CR_OK) {
1414                 goto error;
1415         }
1416         return CR_OK;
1417  error:
1419         if (str_needs_free == TRUE && *a_str) {
1420                 cr_string_destroy (*a_str);
1421                 *a_str = NULL;
1422         }
1423         cr_tknzr_set_cur_pos (a_this, &init_pos);
1424         return status;
1427 static enum CRStatus
1428 cr_tknzr_parse_important (CRTknzr * a_this,
1429                           CRParsingLocation *a_location)
1431         guint32 cur_char = 0;
1432         CRInputPos init_pos;
1433         enum CRStatus status = CR_OK;
1435         g_return_val_if_fail (a_this && PRIVATE (a_this)
1436                               && PRIVATE (a_this)->input,
1437                               CR_BAD_PARAM_ERROR);
1439         RECORD_INITIAL_POS (a_this, &init_pos);
1440         READ_NEXT_CHAR (a_this, &cur_char);
1441         ENSURE_PARSING_COND (cur_char == '!');
1442         if (a_location) {
1443                 cr_tknzr_get_parsing_location (a_this, 
1444                                                a_location) ;
1445         }
1446         cr_tknzr_try_to_skip_spaces (a_this);
1448         if (BYTE (PRIVATE (a_this)->input, 1, NULL) == 'i'
1449             && BYTE (PRIVATE (a_this)->input, 2, NULL) == 'm'
1450             && BYTE (PRIVATE (a_this)->input, 3, NULL) == 'p'
1451             && BYTE (PRIVATE (a_this)->input, 4, NULL) == 'o'
1452             && BYTE (PRIVATE (a_this)->input, 5, NULL) == 'r'
1453             && BYTE (PRIVATE (a_this)->input, 6, NULL) == 't'
1454             && BYTE (PRIVATE (a_this)->input, 7, NULL) == 'a'
1455             && BYTE (PRIVATE (a_this)->input, 8, NULL) == 'n'
1456             && BYTE (PRIVATE (a_this)->input, 9, NULL) == 't') {
1457                 SKIP_BYTES (a_this, 9);
1458                 if (a_location) {
1459                         cr_tknzr_get_parsing_location (a_this,
1460                                                        a_location) ;
1461                 }
1462                 return CR_OK;
1463         } else {
1464                 status = CR_PARSING_ERROR;
1465         }
1467  error:
1468         cr_tknzr_set_cur_pos (a_this, &init_pos);
1470         return status;
1473 /**
1474  *Parses a num as defined in the css spec [4.1.1]:
1475  *[0-9]+|[0-9]*\.[0-9]+
1476  *@param a_this the current instance of #CRTknzr.
1477  *@param a_num out parameter. The parsed number.
1478  *@return CR_OK upon successfull completion, 
1479  *an error code otherwise.
1480  */
1481 static enum CRStatus
1482 cr_tknzr_parse_num (CRTknzr * a_this, 
1483                     CRNum ** a_num)
1485         enum CRStatus status = CR_PARSING_ERROR;
1486         enum CRNumType val_type = NUM_GENERIC;
1487         gboolean parsing_dec,  /* true iff seen decimal point. */
1488                 parsed; /* true iff the substring seen so far is a valid CSS
1489                            number, i.e. `[0-9]+|[0-9]*\.[0-9]+'. */
1490         guint32 cur_char = 0,
1491                 next_char = 0;
1492         gdouble numerator, denominator = 1;
1493         CRInputPos init_pos;
1494         CRParsingLocation location = {0,0,0} ;
1496         g_return_val_if_fail (a_this && PRIVATE (a_this)
1497                               && PRIVATE (a_this)->input, 
1498                               CR_BAD_PARAM_ERROR);
1500         RECORD_INITIAL_POS (a_this, &init_pos);
1501         READ_NEXT_CHAR (a_this, &cur_char);        
1502         if (IS_NUM (cur_char)) {
1503                 numerator = (cur_char - '0');
1504                 parsing_dec = FALSE;
1505                 parsed = TRUE;
1506         } else if (cur_char == '.') {
1507                 numerator = 0;
1508                 parsing_dec = TRUE;
1509                 parsed = FALSE;
1510         } else {
1511                 status = CR_PARSING_ERROR;
1512                 goto error;
1513         }
1514         cr_tknzr_get_parsing_location (a_this, &location) ;
1516         for (;;) {
1517                 status = cr_tknzr_peek_char (a_this, &next_char);
1518                 if (status != CR_OK) {
1519                         if (status == CR_END_OF_INPUT_ERROR)
1520                                 status = CR_OK;
1521                         break;
1522                 }
1523                 if (next_char == '.') {
1524                         if (parsing_dec) {
1525                                 status = CR_PARSING_ERROR;
1526                                 goto error;
1527                         }
1529                         READ_NEXT_CHAR (a_this, &cur_char);
1530                         parsing_dec = TRUE;
1531                         parsed = FALSE;  /* In CSS, there must be at least
1532                                             one digit after `.'. */
1533                 } else if (IS_NUM (next_char)) {
1534                         READ_NEXT_CHAR (a_this, &cur_char);
1535                         parsed = TRUE;
1537                         numerator = numerator * 10 + (cur_char - '0');
1538                         if (parsing_dec) {
1539                                 denominator *= 10;
1540                         }
1541                 } else {
1542                         break;
1543                 }
1544         }
1546         if (!parsed) {
1547                 status = CR_PARSING_ERROR;
1548         }
1550         /*
1551          *Now, set the output param values.
1552          */
1553         if (status == CR_OK) {
1554                 gdouble val = numerator / denominator;
1555                 if (*a_num == NULL) {
1556                         *a_num = cr_num_new_with_val (val, val_type);
1558                         if (*a_num == NULL) {
1559                                 status = CR_ERROR;
1560                                 goto error;
1561                         }
1562                 } else {
1563                         (*a_num)->val = val;
1564                         (*a_num)->type = val_type;
1565                 }
1566                 cr_parsing_location_copy (&(*a_num)->location,
1567                                           &location) ;
1568                 return CR_OK;
1569         }
1571  error:
1573         cr_tknzr_set_cur_pos (a_this, &init_pos);
1575         return status;
1578 /*********************************************
1579  *PUBLIC methods
1580  ********************************************/
1582 CRTknzr *
1583 cr_tknzr_new (CRInput * a_input)
1585         CRTknzr *result = (CRTknzr *)g_try_malloc (sizeof (CRTknzr));
1587         if (result == NULL) {
1588                 cr_utils_trace_info ("Out of memory");
1589                 return NULL;
1590         }
1592         memset (result, 0, sizeof (CRTknzr));
1594         result->priv = (CRTknzrPriv *)g_try_malloc (sizeof (CRTknzrPriv));
1596         if (result->priv == NULL) {
1597                 cr_utils_trace_info ("Out of memory");
1599                 if (result) {
1600                         g_free (result);
1601                         result = NULL;
1602                 }
1604                 return NULL;
1605         }
1606         memset (result->priv, 0, sizeof (CRTknzrPriv));
1607         if (a_input)
1608                 cr_tknzr_set_input (result, a_input);
1609         return result;
1612 CRTknzr *
1613 cr_tknzr_new_from_buf (guchar * a_buf, gulong a_len,
1614                        enum CREncoding a_enc, 
1615                        gboolean a_free_at_destroy)
1617         CRTknzr *result = NULL;
1618         CRInput *input = NULL;
1620         input = cr_input_new_from_buf (a_buf, a_len, a_enc,
1621                                        a_free_at_destroy);
1623         g_return_val_if_fail (input != NULL, NULL);
1625         result = cr_tknzr_new (input);
1627         return result;
1630 CRTknzr *
1631 cr_tknzr_new_from_uri (const guchar * a_file_uri, 
1632                        enum CREncoding a_enc)
1634         CRTknzr *result = NULL;
1635         CRInput *input = cr_input_new_from_uri ((gchar *)a_file_uri, a_enc);
1636         g_return_val_if_fail (input != NULL, NULL);
1638         result = cr_tknzr_new (input);
1640         return result;
1643 void
1644 cr_tknzr_ref (CRTknzr * a_this)
1646         g_return_if_fail (a_this && PRIVATE (a_this));
1648         PRIVATE (a_this)->ref_count++;
1651 gboolean
1652 cr_tknzr_unref (CRTknzr * a_this)
1654         g_return_val_if_fail (a_this && PRIVATE (a_this), FALSE);
1656         if (PRIVATE (a_this)->ref_count > 0) {
1657                 PRIVATE (a_this)->ref_count--;
1658         }
1660         if (PRIVATE (a_this)->ref_count == 0) {
1661                 cr_tknzr_destroy (a_this);
1662                 return TRUE;
1663         }
1665         return FALSE;
1668 enum CRStatus
1669 cr_tknzr_set_input (CRTknzr * a_this, CRInput * a_input)
1671         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1673         if (PRIVATE (a_this)->input) {
1674                 cr_input_unref (PRIVATE (a_this)->input);
1675         }
1677         PRIVATE (a_this)->input = a_input;
1679         cr_input_ref (PRIVATE (a_this)->input);
1681         return CR_OK;
1684 enum CRStatus
1685 cr_tknzr_get_input (CRTknzr * a_this, CRInput ** a_input)
1687         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1689         *a_input = PRIVATE (a_this)->input;
1691         return CR_OK;
1694 /*********************************
1695  *Tokenizer input handling routines
1696  *********************************/
1698 /**
1699  *Reads the next byte from the parser input stream.
1700  *@param a_this the "this pointer" of the current instance of
1701  *#CRParser.
1702  *@param a_byte out parameter the place where to store the byte
1703  *read.
1704  *@return CR_OK upon successfull completion, an error 
1705  *code otherwise.
1706  */
1707 enum CRStatus
1708 cr_tknzr_read_byte (CRTknzr * a_this, guchar * a_byte)
1710         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1712         return cr_input_read_byte (PRIVATE (a_this)->input, a_byte);
1716 /**
1717  *Reads the next char from the parser input stream.
1718  *@param a_this the current instance of #CRTknzr.
1719  *@param a_char out parameter. The read char.
1720  *@return CR_OK upon successfull completion, an error code
1721  *otherwise.
1722  */
1723 enum CRStatus
1724 cr_tknzr_read_char (CRTknzr * a_this, guint32 * a_char)
1726         g_return_val_if_fail (a_this && PRIVATE (a_this)
1727                               && PRIVATE (a_this)->input
1728                               && a_char, CR_BAD_PARAM_ERROR);
1730         if (PRIVATE (a_this)->token_cache) {
1731                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
1732                                       &PRIVATE (a_this)->prev_pos);
1733                 cr_token_destroy (PRIVATE (a_this)->token_cache);
1734                 PRIVATE (a_this)->token_cache = NULL;
1735         }
1737         return cr_input_read_char (PRIVATE (a_this)->input, a_char);
1740 /**
1741  *Peeks a char from the parser input stream.
1742  *To "peek a char" means reads the next char without consuming it.
1743  *Subsequent calls to this function return the same char.
1744  *@param a_this the current instance of #CRTknzr.
1745  *@param a_char out parameter. The peeked char uppon successfull completion.
1746  *@return CR_OK upon successfull completion, an error code otherwise.
1747  */
1748 enum CRStatus
1749 cr_tknzr_peek_char (CRTknzr * a_this, guint32 * a_char)
1751         g_return_val_if_fail (a_this && PRIVATE (a_this)
1752                               && PRIVATE (a_this)->input
1753                               && a_char, CR_BAD_PARAM_ERROR);
1755         if (PRIVATE (a_this)->token_cache) {
1756                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
1757                                       &PRIVATE (a_this)->prev_pos);
1758                 cr_token_destroy (PRIVATE (a_this)->token_cache);
1759                 PRIVATE (a_this)->token_cache = NULL;
1760         }
1762         return cr_input_peek_char (PRIVATE (a_this)->input, a_char);
1765 /**
1766  *Peeks a byte ahead at a given postion in the parser input stream.
1767  *@param a_this the current instance of #CRTknzr.
1768  *@param a_offset the offset of the peeked byte starting from the current
1769  *byte in the parser input stream.
1770  *@param a_byte out parameter. The peeked byte upon 
1771  *successfull completion.
1772  *@return CR_OK upon successfull completion, an error code otherwise.
1773  */
1774 enum CRStatus
1775 cr_tknzr_peek_byte (CRTknzr * a_this, gulong a_offset, guchar * a_byte)
1777         g_return_val_if_fail (a_this && PRIVATE (a_this)
1778                               && PRIVATE (a_this)->input && a_byte,
1779                               CR_BAD_PARAM_ERROR);
1781         if (PRIVATE (a_this)->token_cache) {
1782                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
1783                                       &PRIVATE (a_this)->prev_pos);
1784                 cr_token_destroy (PRIVATE (a_this)->token_cache);
1785                 PRIVATE (a_this)->token_cache = NULL;
1786         }
1788         return cr_input_peek_byte (PRIVATE (a_this)->input,
1789                                    CR_SEEK_CUR, a_offset, a_byte);
1792 /**
1793  *Same as cr_tknzr_peek_byte() but this api returns the byte peeked.
1794  *@param a_this the current instance of #CRTknzr.
1795  *@param a_offset the offset of the peeked byte starting from the current
1796  *byte in the parser input stream.
1797  *@param a_eof out parameter. If not NULL, is set to TRUE if we reached end of
1798  *file, FALE otherwise. If the caller sets it to NULL, this parameter 
1799  *is just ignored.
1800  *@return the peeked byte.
1801  */
1802 guchar
1803 cr_tknzr_peek_byte2 (CRTknzr * a_this, gulong a_offset, gboolean * a_eof)
1805         g_return_val_if_fail (a_this && PRIVATE (a_this)
1806                               && PRIVATE (a_this)->input, 0);
1808         return cr_input_peek_byte2 (PRIVATE (a_this)->input, a_offset, a_eof);
1811 /**
1812  *Gets the number of bytes left in the topmost input stream
1813  *associated to this parser.
1814  *@param a_this the current instance of #CRTknzr
1815  *@return the number of bytes left or -1 in case of error.
1816  */
1817 glong
1818 cr_tknzr_get_nb_bytes_left (CRTknzr * a_this)
1820         g_return_val_if_fail (a_this && PRIVATE (a_this)
1821                               && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
1823         if (PRIVATE (a_this)->token_cache) {
1824                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
1825                                       &PRIVATE (a_this)->prev_pos);
1826                 cr_token_destroy (PRIVATE (a_this)->token_cache);
1827                 PRIVATE (a_this)->token_cache = NULL;
1828         }
1830         return cr_input_get_nb_bytes_left (PRIVATE (a_this)->input);
1833 enum CRStatus
1834 cr_tknzr_get_cur_pos (CRTknzr * a_this, CRInputPos * a_pos)
1836         g_return_val_if_fail (a_this && PRIVATE (a_this)
1837                               && PRIVATE (a_this)->input
1838                               && a_pos, CR_BAD_PARAM_ERROR);
1840         if (PRIVATE (a_this)->token_cache) {
1841                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
1842                                       &PRIVATE (a_this)->prev_pos);
1843                 cr_token_destroy (PRIVATE (a_this)->token_cache);
1844                 PRIVATE (a_this)->token_cache = NULL;
1845         }
1847         return cr_input_get_cur_pos (PRIVATE (a_this)->input, a_pos);
1850 enum CRStatus 
1851 cr_tknzr_get_parsing_location (CRTknzr *a_this,
1852                                CRParsingLocation *a_loc)
1854         g_return_val_if_fail (a_this 
1855                               && PRIVATE (a_this)
1856                               && a_loc,
1857                               CR_BAD_PARAM_ERROR) ;
1859         return cr_input_get_parsing_location 
1860                 (PRIVATE (a_this)->input, a_loc) ;
1863 enum CRStatus
1864 cr_tknzr_get_cur_byte_addr (CRTknzr * a_this, guchar ** a_addr)
1866         g_return_val_if_fail (a_this && PRIVATE (a_this)
1867                               && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
1868         if (PRIVATE (a_this)->token_cache) {
1869                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
1870                                       &PRIVATE (a_this)->prev_pos);
1871                 cr_token_destroy (PRIVATE (a_this)->token_cache);
1872                 PRIVATE (a_this)->token_cache = NULL;
1873         }
1875         return cr_input_get_cur_byte_addr (PRIVATE (a_this)->input, a_addr);
1878 enum CRStatus
1879 cr_tknzr_seek_index (CRTknzr * a_this, enum CRSeekPos a_origin, gint a_pos)
1881         g_return_val_if_fail (a_this && PRIVATE (a_this)
1882                               && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
1884         if (PRIVATE (a_this)->token_cache) {
1885                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
1886                                       &PRIVATE (a_this)->prev_pos);
1887                 cr_token_destroy (PRIVATE (a_this)->token_cache);
1888                 PRIVATE (a_this)->token_cache = NULL;
1889         }
1891         return cr_input_seek_index (PRIVATE (a_this)->input, a_origin, a_pos);
1894 enum CRStatus
1895 cr_tknzr_consume_chars (CRTknzr * a_this, guint32 a_char, glong * a_nb_char)
1897         g_return_val_if_fail (a_this && PRIVATE (a_this)
1898                               && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
1900         if (PRIVATE (a_this)->token_cache) {
1901                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
1902                                       &PRIVATE (a_this)->prev_pos);
1903                 cr_token_destroy (PRIVATE (a_this)->token_cache);
1904                 PRIVATE (a_this)->token_cache = NULL;
1905         }
1907         return cr_input_consume_chars (PRIVATE (a_this)->input,
1908                                        a_char, (gulong *)a_nb_char);
1911 enum CRStatus
1912 cr_tknzr_set_cur_pos (CRTknzr * a_this, CRInputPos * a_pos)
1914         g_return_val_if_fail (a_this && PRIVATE (a_this)
1915                               && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
1917         if (PRIVATE (a_this)->token_cache) {
1918                 cr_token_destroy (PRIVATE (a_this)->token_cache);
1919                 PRIVATE (a_this)->token_cache = NULL;
1920         }
1922         return cr_input_set_cur_pos (PRIVATE (a_this)->input, a_pos);
1925 enum CRStatus
1926 cr_tknzr_unget_token (CRTknzr * a_this, CRToken * a_token)
1928         g_return_val_if_fail (a_this && PRIVATE (a_this)
1929                               && PRIVATE (a_this)->token_cache == NULL,
1930                               CR_BAD_PARAM_ERROR);
1932         PRIVATE (a_this)->token_cache = a_token;
1934         return CR_OK;
1937 /**
1938  *Returns the next token of the input stream.
1939  *This method is really central. Each parsing
1940  *method calls it.
1941  *@param a_this the current tokenizer.
1942  *@param a_tk out parameter. The returned token.
1943  *for the sake of mem leak avoidance, *a_tk must
1944  *be NULL.
1945  *@param CR_OK upon successfull completion, an error code
1946  *otherwise.
1947  */
1948 enum CRStatus
1949 cr_tknzr_get_next_token (CRTknzr * a_this, CRToken ** a_tk)
1951         enum CRStatus status = CR_OK;
1952         CRToken *token = NULL;
1953         CRInputPos init_pos;
1954         guint32 next_char = 0;
1955         guchar next_bytes[4] = { 0 };
1956         gboolean reached_eof = FALSE;
1957         CRInput *input = NULL;
1958         CRString *str = NULL;
1959         CRRgb *rgb = NULL;
1960         CRParsingLocation location = {0,0,0} ;
1962         g_return_val_if_fail (a_this && PRIVATE (a_this)
1963                               && a_tk && *a_tk == NULL
1964                               && PRIVATE (a_this)->input, 
1965                               CR_BAD_PARAM_ERROR);
1967         if (PRIVATE (a_this)->token_cache) {
1968                 *a_tk = PRIVATE (a_this)->token_cache;
1969                 PRIVATE (a_this)->token_cache = NULL;
1970                 return CR_OK;
1971         }
1973         RECORD_INITIAL_POS (a_this, &init_pos);
1975         status = cr_input_get_end_of_file
1976                 (PRIVATE (a_this)->input, &reached_eof);
1977         ENSURE_PARSING_COND (status == CR_OK);
1979         if (reached_eof == TRUE) {
1980                 status = CR_END_OF_INPUT_ERROR;
1981                 goto error;
1982         }
1984         input = PRIVATE (a_this)->input;
1986         PEEK_NEXT_CHAR (a_this, &next_char);
1987         token = cr_token_new ();
1988         ENSURE_PARSING_COND (token);
1990         switch (next_char) {
1991         case '@':
1992                 {
1993                         if (BYTE (input, 2, NULL) == 'f'
1994                             && BYTE (input, 3, NULL) == 'o'
1995                             && BYTE (input, 4, NULL) == 'n'
1996                             && BYTE (input, 5, NULL) == 't'
1997                             && BYTE (input, 6, NULL) == '-'
1998                             && BYTE (input, 7, NULL) == 'f'
1999                             && BYTE (input, 8, NULL) == 'a'
2000                             && BYTE (input, 9, NULL) == 'c'
2001                             && BYTE (input, 10, NULL) == 'e') {
2002                                 SKIP_CHARS (a_this, 1);
2003                                 cr_tknzr_get_parsing_location 
2004                                         (a_this, &location) ;
2005                                 SKIP_CHARS (a_this, 9);
2006                                 status = cr_token_set_font_face_sym (token);
2007                                 CHECK_PARSING_STATUS (status, TRUE);
2008                                 cr_parsing_location_copy (&token->location,
2009                                                           &location) ;
2010                                 goto done;
2011                         }
2013                         if (BYTE (input, 2, NULL) == 'c'
2014                             && BYTE (input, 3, NULL) == 'h'
2015                             && BYTE (input, 4, NULL) == 'a'
2016                             && BYTE (input, 5, NULL) == 'r'
2017                             && BYTE (input, 6, NULL) == 's'
2018                             && BYTE (input, 7, NULL) == 'e'
2019                             && BYTE (input, 8, NULL) == 't') {
2020                                 SKIP_CHARS (a_this, 1);
2021                                 cr_tknzr_get_parsing_location
2022                                         (a_this, &location) ;
2023                                 SKIP_CHARS (a_this, 7);
2024                                 status = cr_token_set_charset_sym (token);
2025                                 CHECK_PARSING_STATUS (status, TRUE);
2026                                 cr_parsing_location_copy (&token->location,
2027                                                           &location) ;
2028                                 goto done;
2029                         }
2031                         if (BYTE (input, 2, NULL) == 'i'
2032                             && BYTE (input, 3, NULL) == 'm'
2033                             && BYTE (input, 4, NULL) == 'p'
2034                             && BYTE (input, 5, NULL) == 'o'
2035                             && BYTE (input, 6, NULL) == 'r'
2036                             && BYTE (input, 7, NULL) == 't') {
2037                                 SKIP_CHARS (a_this, 1);
2038                                 cr_tknzr_get_parsing_location 
2039                                         (a_this, &location) ;
2040                                 SKIP_CHARS (a_this, 6);
2041                                 status = cr_token_set_import_sym (token);
2042                                 CHECK_PARSING_STATUS (status, TRUE);
2043                                 cr_parsing_location_copy (&token->location,
2044                                                           &location) ;
2045                                 goto done;
2046                         }
2048                         if (BYTE (input, 2, NULL) == 'm'
2049                             && BYTE (input, 3, NULL) == 'e'
2050                             && BYTE (input, 4, NULL) == 'd'
2051                             && BYTE (input, 5, NULL) == 'i'
2052                             && BYTE (input, 6, NULL) == 'a') {
2053                                 SKIP_CHARS (a_this, 1);
2054                                 cr_tknzr_get_parsing_location (a_this, 
2055                                                                &location) ;
2056                                 SKIP_CHARS (a_this, 5);
2057                                 status = cr_token_set_media_sym (token);
2058                                 CHECK_PARSING_STATUS (status, TRUE);
2059                                 cr_parsing_location_copy (&token->location, 
2060                                                           &location) ;
2061                                 goto done;
2062                         }
2064                         if (BYTE (input, 2, NULL) == 'p'
2065                             && BYTE (input, 3, NULL) == 'a'
2066                             && BYTE (input, 4, NULL) == 'g'
2067                             && BYTE (input, 5, NULL) == 'e') {
2068                                 SKIP_CHARS (a_this, 1);
2069                                 cr_tknzr_get_parsing_location (a_this, 
2070                                                                &location) ;
2071                                 SKIP_CHARS (a_this, 4);
2072                                 status = cr_token_set_page_sym (token);
2073                                 CHECK_PARSING_STATUS (status, TRUE);
2074                                 cr_parsing_location_copy (&token->location, 
2075                                                           &location) ;
2076                                 goto done;
2077                         }
2078                         status = cr_tknzr_parse_atkeyword (a_this, &str);
2079                         if (status == CR_OK) {
2080                                 status = cr_token_set_atkeyword (token, str);
2081                                 CHECK_PARSING_STATUS (status, TRUE);
2082                                 if (str) {
2083                                         cr_parsing_location_copy (&token->location, 
2084                                                                   &str->location) ;
2085                                 }
2086                                 goto done;
2087                         }
2088                 }
2089                 break;
2091         case 'u':
2093                 if (BYTE (input, 2, NULL) == 'r'
2094                     && BYTE (input, 3, NULL) == 'l'
2095                     && BYTE (input, 4, NULL) == '(') {
2096                         CRString *str = NULL;
2098                         status = cr_tknzr_parse_uri (a_this, &str);
2099                         if (status == CR_OK) {
2100                                 status = cr_token_set_uri (token, str);
2101                                 CHECK_PARSING_STATUS (status, TRUE);
2102                                 if (str) {
2103                                         cr_parsing_location_copy (&token->location,
2104                                                                   &str->location) ;
2105                                 }
2106                                 goto done;
2107                         }
2108                 } else {
2109                         status = cr_tknzr_parse_ident (a_this, &str);
2110                         if (status == CR_OK && str) {
2111                                 status = cr_token_set_ident (token, str);
2112                                 CHECK_PARSING_STATUS (status, TRUE);
2113                                 if (str) {
2114                                         cr_parsing_location_copy (&token->location, 
2115                                                                   &str->location) ;
2116                                 }
2117                                 goto done;
2118                         }
2119                 }
2120                 break;
2122         case 'r':
2123                 if (BYTE (input, 2, NULL) == 'g'
2124                     && BYTE (input, 3, NULL) == 'b'
2125                     && BYTE (input, 4, NULL) == '(') {
2126                         status = cr_tknzr_parse_rgb (a_this, &rgb);
2127                         if (status == CR_OK && rgb) {
2128                                 status = cr_token_set_rgb (token, rgb);
2129                                 CHECK_PARSING_STATUS (status, TRUE);
2130                                 if (rgb) {
2131                                         cr_parsing_location_copy (&token->location, 
2132                                                                   &rgb->location) ;
2133                                 }
2134                                 rgb = NULL;
2135                                 goto done;
2136                         }
2138                 } else {
2139                         status = cr_tknzr_parse_ident (a_this, &str);
2140                         if (status == CR_OK) {
2141                                 status = cr_token_set_ident (token, str);
2142                                 CHECK_PARSING_STATUS (status, TRUE);
2143                                 if (str) {
2144                                         cr_parsing_location_copy (&token->location, 
2145                                                                   &str->location) ;
2146                                 }
2147                                 str = NULL;
2148                                 goto done;
2149                         }
2150                 }
2151                 break;
2153         case '<':
2154                 if (BYTE (input, 2, NULL) == '-'
2155                     && BYTE (input, 3, NULL) == '-') {
2156                         SKIP_CHARS (a_this, 1);
2157                         cr_tknzr_get_parsing_location (a_this, 
2158                                                        &location) ;
2159                         SKIP_CHARS (a_this, 2);
2160                         status = cr_token_set_cdo (token);
2161                         CHECK_PARSING_STATUS (status, TRUE);
2162                         cr_parsing_location_copy (&token->location, 
2163                                                   &location) ;
2164                         goto done;
2165                 }
2166                 break;
2168         case '-':
2169                 if (BYTE (input, 2, NULL) == '-'
2170                     && BYTE (input, 3, NULL) == '>') {
2171                         SKIP_CHARS (a_this, 1);
2172                         cr_tknzr_get_parsing_location (a_this, 
2173                                                        &location) ;
2174                         SKIP_CHARS (a_this, 2);
2175                         status = cr_token_set_cdc (token);
2176                         CHECK_PARSING_STATUS (status, TRUE);
2177                         cr_parsing_location_copy (&token->location, 
2178                                                   &location) ;
2179                         goto done;
2180                 } else {
2181                         status = cr_tknzr_parse_ident
2182                                 (a_this, &str);
2183                         if (status == CR_OK) {
2184                                 cr_token_set_ident
2185                                         (token, str);
2186                                 if (str) {
2187                                         cr_parsing_location_copy (&token->location, 
2188                                                                   &str->location) ;
2189                                 }
2190                                 goto done;
2191                         }
2192                 }
2193                 break;
2195         case '~':
2196                 if (BYTE (input, 2, NULL) == '=') {
2197                         SKIP_CHARS (a_this, 1);
2198                         cr_tknzr_get_parsing_location (a_this, 
2199                                                        &location) ;
2200                         SKIP_CHARS (a_this, 1);
2201                         status = cr_token_set_includes (token);
2202                         CHECK_PARSING_STATUS (status, TRUE);
2203                         cr_parsing_location_copy (&token->location, 
2204                                                   &location) ;
2205                         goto done;
2206                 }
2207                 break;
2209         case '|':
2210                 if (BYTE (input, 2, NULL) == '=') {
2211                         SKIP_CHARS (a_this, 1);
2212                         cr_tknzr_get_parsing_location (a_this, 
2213                                                        &location) ;
2214                         SKIP_CHARS (a_this, 1);
2215                         status = cr_token_set_dashmatch (token);
2216                         CHECK_PARSING_STATUS (status, TRUE);
2217                         cr_parsing_location_copy (&token->location,
2218                                                   &location) ;
2219                         goto done;
2220                 }
2221                 break;
2223         case '/':
2224                 if (BYTE (input, 2, NULL) == '*') {
2225                         status = cr_tknzr_parse_comment (a_this, &str);
2227                         if (status == CR_OK) {
2228                                 status = cr_token_set_comment (token, str);
2229                                 str = NULL;
2230                                 CHECK_PARSING_STATUS (status, TRUE);
2231                                 if (str) {
2232                                         cr_parsing_location_copy (&token->location, 
2233                                                                   &str->location) ;
2234                                 }
2235                                 goto done;
2236                         }
2237                 }
2238                 break ;
2240         case ';':
2241                 SKIP_CHARS (a_this, 1);
2242                 cr_tknzr_get_parsing_location (a_this, 
2243                                                &location) ;
2244                 status = cr_token_set_semicolon (token);
2245                 CHECK_PARSING_STATUS (status, TRUE);
2246                 cr_parsing_location_copy (&token->location, 
2247                                           &location) ;
2248                 goto done;
2250         case '{':
2251                 SKIP_CHARS (a_this, 1);
2252                 cr_tknzr_get_parsing_location (a_this, 
2253                                                &location) ;
2254                 status = cr_token_set_cbo (token);
2255                 CHECK_PARSING_STATUS (status, TRUE);
2256                 cr_tknzr_get_parsing_location (a_this, 
2257                                                &location) ;
2258                 goto done;
2260         case '}':
2261                 SKIP_CHARS (a_this, 1);
2262                 cr_tknzr_get_parsing_location (a_this, 
2263                                                &location) ;
2264                 status = cr_token_set_cbc (token);
2265                 CHECK_PARSING_STATUS (status, TRUE);
2266                 cr_parsing_location_copy (&token->location, 
2267                                           &location) ;
2268                 goto done;
2270         case '(':
2271                 SKIP_CHARS (a_this, 1);
2272                 cr_tknzr_get_parsing_location (a_this, 
2273                                                &location) ;
2274                 status = cr_token_set_po (token);
2275                 CHECK_PARSING_STATUS (status, TRUE);
2276                 cr_parsing_location_copy (&token->location, 
2277                                           &location) ;
2278                 goto done;
2280         case ')':
2281                 SKIP_CHARS (a_this, 1);
2282                 cr_tknzr_get_parsing_location (a_this, 
2283                                                &location) ;
2284                 status = cr_token_set_pc (token);
2285                 CHECK_PARSING_STATUS (status, TRUE);
2286                 cr_parsing_location_copy (&token->location, 
2287                                           &location) ;
2288                 goto done;
2290         case '[':
2291                 SKIP_CHARS (a_this, 1);
2292                 cr_tknzr_get_parsing_location (a_this, 
2293                                                &location) ;
2294                 status = cr_token_set_bo (token);
2295                 CHECK_PARSING_STATUS (status, TRUE);
2296                 cr_parsing_location_copy (&token->location, 
2297                                           &location) ;
2298                 goto done;
2300         case ']':
2301                 SKIP_CHARS (a_this, 1);
2302                 cr_tknzr_get_parsing_location (a_this, 
2303                                                &location) ;
2304                 status = cr_token_set_bc (token);
2305                 CHECK_PARSING_STATUS (status, TRUE);
2306                 cr_parsing_location_copy (&token->location, 
2307                                           &location) ;
2308                 goto done;
2310         case ' ':
2311         case '\t':
2312         case '\n':
2313         case '\f':
2314         case '\r':
2315                 {
2316                         guchar *start = NULL,
2317                                 *end = NULL;
2319                         status = cr_tknzr_parse_w (a_this, &start, 
2320                                                    &end, &location);
2321                         if (status == CR_OK) {
2322                                 status = cr_token_set_s (token);
2323                                 CHECK_PARSING_STATUS (status, TRUE);
2324                                 cr_tknzr_get_parsing_location (a_this, 
2325                                                                &location) ;
2326                                 goto done;
2327                         }
2328                 }
2329                 break;
2331         case '#':
2332                 {
2333                         status = cr_tknzr_parse_hash (a_this, &str);
2334                         if (status == CR_OK && str) {
2335                                 status = cr_token_set_hash (token, str);
2336                                 CHECK_PARSING_STATUS (status, TRUE);
2337                                 if (str) {
2338                                         cr_parsing_location_copy (&token->location,
2339                                                                   &str->location) ;
2340                                 }
2341                                 str = NULL;
2342                                 goto done;
2343                         }
2344                 }
2345                 break;
2347         case '\'':
2348         case '"':
2349                 status = cr_tknzr_parse_string (a_this, &str);
2350                 if (status == CR_OK && str) {
2351                         status = cr_token_set_string (token, str);
2352                         CHECK_PARSING_STATUS (status, TRUE);
2353                         if (str) {
2354                                 cr_parsing_location_copy (&token->location, 
2355                                                           &str->location) ;
2356                         }
2357                         str = NULL;
2358                         goto done;
2359                 }
2360                 break;
2362         case '!':
2363                 status = cr_tknzr_parse_important (a_this, &location);
2364                 if (status == CR_OK) {
2365                         status = cr_token_set_important_sym (token);
2366                         CHECK_PARSING_STATUS (status, TRUE);
2367                         cr_parsing_location_copy (&token->location, 
2368                                                   &location) ;
2369                         goto done;
2370                 }
2371                 break;
2373         case '0':
2374         case '1':
2375         case '2':
2376         case '3':
2377         case '4':
2378         case '5':
2379         case '6':
2380         case '7':
2381         case '8':
2382         case '9':
2383         case '.':
2384                 {
2385                         CRNum *num = NULL;
2387                         status = cr_tknzr_parse_num (a_this, &num);
2388                         if (status == CR_OK && num) {
2389                                 next_bytes[0] = BYTE (input, 1, NULL);
2390                                 next_bytes[1] = BYTE (input, 2, NULL);
2391                                 next_bytes[2] = BYTE (input, 3, NULL);
2392                                 next_bytes[3] = BYTE (input, 3, NULL);
2394                                 if (next_bytes[0] == 'e'
2395                                     && next_bytes[1] == 'm') {
2396                                         num->type = NUM_LENGTH_EM;
2397                                         status = cr_token_set_ems (token,
2398                                                                    num);
2399                                         num = NULL;
2400                                         SKIP_CHARS (a_this, 2);
2401                                 } else if (next_bytes[0] == 'e'
2402                                            && next_bytes[1] == 'x') {
2403                                         num->type = NUM_LENGTH_EX;
2404                                         status = cr_token_set_exs (token,
2405                                                                    num);
2406                                         num = NULL;
2407                                         SKIP_CHARS (a_this, 2);
2408                                 } else if (next_bytes[0] == 'p'
2409                                            && next_bytes[1] == 'x') {
2410                                         num->type = NUM_LENGTH_PX;
2411                                         status = cr_token_set_length
2412                                                 (token, num, LENGTH_PX_ET);
2413                                         num = NULL;
2414                                         SKIP_CHARS (a_this, 2);
2415                                 } else if (next_bytes[0] == 'c'
2416                                            && next_bytes[1] == 'm') {
2417                                         num->type = NUM_LENGTH_CM;
2418                                         status = cr_token_set_length
2419                                                 (token, num, LENGTH_CM_ET);
2420                                         num = NULL;
2421                                         SKIP_CHARS (a_this, 2);
2422                                 } else if (next_bytes[0] == 'm'
2423                                            && next_bytes[1] == 'm') {
2424                                         num->type = NUM_LENGTH_MM;
2425                                         status = cr_token_set_length
2426                                                 (token, num, LENGTH_MM_ET);
2427                                         num = NULL;
2428                                         SKIP_CHARS (a_this, 2);
2429                                 } else if (next_bytes[0] == 'i'
2430                                            && next_bytes[1] == 'n') {
2431                                         num->type = NUM_LENGTH_IN;
2432                                         status = cr_token_set_length
2433                                                 (token, num, LENGTH_IN_ET);
2434                                         num = NULL;
2435                                         SKIP_CHARS (a_this, 2);
2436                                 } else if (next_bytes[0] == 'p'
2437                                            && next_bytes[1] == 't') {
2438                                         num->type = NUM_LENGTH_PT;
2439                                         status = cr_token_set_length
2440                                                 (token, num, LENGTH_PT_ET);
2441                                         num = NULL;
2442                                         SKIP_CHARS (a_this, 2);
2443                                 } else if (next_bytes[0] == 'p'
2444                                            && next_bytes[1] == 'c') {
2445                                         num->type = NUM_LENGTH_PC;
2446                                         status = cr_token_set_length
2447                                                 (token, num, LENGTH_PC_ET);
2448                                         num = NULL;
2449                                         SKIP_CHARS (a_this, 2);
2450                                 } else if (next_bytes[0] == 'd'
2451                                            && next_bytes[1] == 'e'
2452                                            && next_bytes[2] == 'g') {
2453                                         num->type = NUM_ANGLE_DEG;
2454                                         status = cr_token_set_angle
2455                                                 (token, num, ANGLE_DEG_ET);
2456                                         num = NULL;
2457                                         SKIP_CHARS (a_this, 3);
2458                                 } else if (next_bytes[0] == 'r'
2459                                            && next_bytes[1] == 'a'
2460                                            && next_bytes[2] == 'd') {
2461                                         num->type = NUM_ANGLE_RAD;
2462                                         status = cr_token_set_angle
2463                                                 (token, num, ANGLE_RAD_ET);
2464                                         num = NULL;
2465                                         SKIP_CHARS (a_this, 3);
2466                                 } else if (next_bytes[0] == 'g'
2467                                            && next_bytes[1] == 'r'
2468                                            && next_bytes[2] == 'a'
2469                                            && next_bytes[3] == 'd') {
2470                                         num->type = NUM_ANGLE_GRAD;
2471                                         status = cr_token_set_angle
2472                                                 (token, num, ANGLE_GRAD_ET);
2473                                         num = NULL;
2474                                         SKIP_CHARS (a_this, 4);
2475                                 } else if (next_bytes[0] == 'm'
2476                                            && next_bytes[1] == 's') {
2477                                         num->type = NUM_TIME_MS;
2478                                         status = cr_token_set_time
2479                                                 (token, num, TIME_MS_ET);
2480                                         num = NULL;
2481                                         SKIP_CHARS (a_this, 2);
2482                                 } else if (next_bytes[0] == 's') {
2483                                         num->type = NUM_TIME_S;
2484                                         status = cr_token_set_time
2485                                                 (token, num, TIME_S_ET);
2486                                         num = NULL;
2487                                         SKIP_CHARS (a_this, 1);
2488                                 } else if (next_bytes[0] == 'H'
2489                                            && next_bytes[1] == 'z') {
2490                                         num->type = NUM_FREQ_HZ;
2491                                         status = cr_token_set_freq
2492                                                 (token, num, FREQ_HZ_ET);
2493                                         num = NULL;
2494                                         SKIP_CHARS (a_this, 2);
2495                                 } else if (next_bytes[0] == 'k'
2496                                            && next_bytes[1] == 'H'
2497                                            && next_bytes[2] == 'z') {
2498                                         num->type = NUM_FREQ_KHZ;
2499                                         status = cr_token_set_freq
2500                                                 (token, num, FREQ_KHZ_ET);
2501                                         num = NULL;
2502                                         SKIP_CHARS (a_this, 3);
2503                                 } else if (next_bytes[0] == '%') {
2504                                         num->type = NUM_PERCENTAGE;
2505                                         status = cr_token_set_percentage
2506                                                 (token, num);
2507                                         num = NULL;
2508                                         SKIP_CHARS (a_this, 1);
2509                                 } else {
2510                                         status = cr_tknzr_parse_ident (a_this,
2511                                                                        &str);
2512                                         if (status == CR_OK && str) {
2513                                                 num->type = NUM_UNKNOWN_TYPE;
2514                                                 status = cr_token_set_dimen
2515                                                         (token, num, str);
2516                                                 num = NULL;
2517                                                 CHECK_PARSING_STATUS (status,
2518                                                                       TRUE);
2519                                                 str = NULL;
2520                                         } else {
2521                                                 status = cr_token_set_number
2522                                                         (token, num);
2523                                                 num = NULL;
2524                                                 CHECK_PARSING_STATUS (status, CR_OK);
2525                                                 str = NULL;
2526                                         }
2527                                 }
2528                                 if (token && token->u.num) {
2529                                         cr_parsing_location_copy (&token->location,
2530                                                                   &token->u.num->location) ;
2531                                 } else {
2532                                         status = CR_ERROR ;
2533                                 }
2534                                 goto done ;
2535                         }
2536                 }
2537                 break;
2539         default:
2540                 /*process the fallback cases here */
2542                 if (next_char == '\\'
2543                     || (cr_utils_is_nonascii (next_bytes[0]) == TRUE)
2544                     || ((next_char >= 'a') && (next_char <= 'z'))
2545                     || ((next_char >= 'A') && (next_char <= 'Z'))) {
2546                         status = cr_tknzr_parse_ident (a_this, &str);
2547                         if (status == CR_OK && str) {
2548                                 guint32 next_c = 0;
2550                                 status = cr_input_peek_char
2551                                         (PRIVATE (a_this)->input, &next_c);
2553                                 if (status == CR_OK && next_c == '(') {
2555                                         SKIP_CHARS (a_this, 1);
2556                                         status = cr_token_set_function
2557                                                 (token, str);
2558                                         CHECK_PARSING_STATUS (status, TRUE);
2559                                         /*ownership is transfered
2560                                          *to token by cr_token_set_function.
2561                                          */
2562                                         if (str) {
2563                                                 cr_parsing_location_copy (&token->location, 
2564                                                                           &str->location) ;
2565                                         }
2566                                         str = NULL;
2567                                 } else {
2568                                         status = cr_token_set_ident (token,
2569                                                                      str);
2570                                         CHECK_PARSING_STATUS (status, TRUE);
2571                                         if (str) {
2572                                                 cr_parsing_location_copy (&token->location, 
2573                                                                           &str->location) ;
2574                                         }
2575                                         str = NULL;
2576                                 }
2577                                 goto done;
2578                         } else {
2579                                 if (str) {
2580                                         cr_string_destroy (str);
2581                                         str = NULL;
2582                                 }
2583                         }
2584                 }
2585                 break;
2586         }
2588         READ_NEXT_CHAR (a_this, &next_char);
2589         cr_tknzr_get_parsing_location (a_this, 
2590                                        &location) ;
2591         status = cr_token_set_delim (token, next_char);
2592         CHECK_PARSING_STATUS (status, TRUE);
2593         cr_parsing_location_copy (&token->location, 
2594                                   &location) ;
2595  done:
2597         if (status == CR_OK && token) {
2598                 *a_tk = token;
2599                 /*
2600                  *store the previous position input stream pos.
2601                  */
2602                 memmove (&PRIVATE (a_this)->prev_pos,
2603                          &init_pos, sizeof (CRInputPos));
2604                 return CR_OK;
2605         }
2607  error:
2608         if (token) {
2609                 cr_token_destroy (token);
2610                 token = NULL;
2611         }
2613         if (str) {
2614                 cr_string_destroy (str);
2615                 str = NULL;
2616         }
2617         cr_tknzr_set_cur_pos (a_this, &init_pos);
2618         return status;
2622 enum CRStatus
2623 cr_tknzr_parse_token (CRTknzr * a_this, enum CRTokenType a_type,
2624                       enum CRTokenExtraType a_et, gpointer a_res,
2625                       gpointer a_extra_res)
2627         enum CRStatus status = CR_OK;
2628         CRToken *token = NULL;
2630         g_return_val_if_fail (a_this && PRIVATE (a_this)
2631                               && PRIVATE (a_this)->input
2632                               && a_res, CR_BAD_PARAM_ERROR);
2634         status = cr_tknzr_get_next_token (a_this, &token);
2635         if (status != CR_OK)
2636                 return status;
2637         if (token == NULL)
2638                 return CR_PARSING_ERROR;
2640         if (token->type == a_type) {
2641                 switch (a_type) {
2642                 case NO_TK:
2643                 case S_TK:
2644                 case CDO_TK:
2645                 case CDC_TK:
2646                 case INCLUDES_TK:
2647                 case DASHMATCH_TK:
2648                 case IMPORT_SYM_TK:
2649                 case PAGE_SYM_TK:
2650                 case MEDIA_SYM_TK:
2651                 case FONT_FACE_SYM_TK:
2652                 case CHARSET_SYM_TK:
2653                 case IMPORTANT_SYM_TK:
2654                         status = CR_OK;
2655                         break;
2657                 case STRING_TK:
2658                 case IDENT_TK:
2659                 case HASH_TK:
2660                 case ATKEYWORD_TK:
2661                 case FUNCTION_TK:
2662                 case COMMENT_TK:
2663                 case URI_TK:
2664                         *((CRString **) a_res) = token->u.str;
2665                         token->u.str = NULL;
2666                         status = CR_OK;
2667                         break;
2669                 case EMS_TK:
2670                 case EXS_TK:
2671                 case PERCENTAGE_TK:
2672                 case NUMBER_TK:
2673                         *((CRNum **) a_res) = token->u.num;
2674                         token->u.num = NULL;
2675                         status = CR_OK;
2676                         break;
2678                 case LENGTH_TK:
2679                 case ANGLE_TK:
2680                 case TIME_TK:
2681                 case FREQ_TK:
2682                         if (token->extra_type == a_et) {
2683                                 *((CRNum **) a_res) = token->u.num;
2684                                 token->u.num = NULL;
2685                                 status = CR_OK;
2686                         }
2687                         break;
2689                 case DIMEN_TK:
2690                         *((CRNum **) a_res) = token->u.num;
2691                         if (a_extra_res == NULL) {
2692                                 status = CR_BAD_PARAM_ERROR;
2693                                 goto error;
2694                         }
2696                         *((CRString **) a_extra_res) = token->dimen;
2697                         token->u.num = NULL;
2698                         token->dimen = NULL;
2699                         status = CR_OK;
2700                         break;
2702                 case DELIM_TK:
2703                         *((guint32 *) a_res) = token->u.unichar;
2704                         status = CR_OK;
2705                         break;
2707                 case UNICODERANGE_TK:
2708                 default:
2709                         status = CR_PARSING_ERROR;
2710                         break;
2711                 }
2713                 cr_token_destroy (token);
2714                 token = NULL;
2715         } else {
2716                 cr_tknzr_unget_token (a_this, token);
2717                 token = NULL;
2718                 status = CR_PARSING_ERROR;
2719         }
2721         return status;
2723       error:
2725         if (token) {
2726                 cr_tknzr_unget_token (a_this, token);
2727                 token = NULL;
2728         }
2730         return status;
2733 void
2734 cr_tknzr_destroy (CRTknzr * a_this)
2736         g_return_if_fail (a_this);
2738         if (PRIVATE (a_this) && PRIVATE (a_this)->input) {
2739                 if (cr_input_unref (PRIVATE (a_this)->input)
2740                     == TRUE) {
2741                         PRIVATE (a_this)->input = NULL;
2742                 }
2743         }
2745         if (PRIVATE (a_this)->token_cache) {
2746                 cr_token_destroy (PRIVATE (a_this)->token_cache);
2747                 PRIVATE (a_this)->token_cache = NULL;
2748         }
2750         if (PRIVATE (a_this)) {
2751                 g_free (PRIVATE (a_this));
2752                 PRIVATE (a_this) = NULL;
2753         }
2755         g_free (a_this);