1 /* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
2 
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  */
23 
24 /**
25  *@file
26  *The definition of the #CRTknzr (tokenizer)
27  *class.
28  */
29 
30 #include "string.h"
31 #include "cr-tknzr.h"
32 #include "cr-doc-handler.h"
33 
34 struct _CRTknzrPriv {
35         /**The parser input stream of bytes*/
36         CRInput *input;
37 
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;
46 
47         /**
48          *The position of the end of the previous token
49          *or char fetched.
50          */
51         CRInputPos prev_pos;
52 
53         CRDocHandler *sac_handler;
54 
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 };
62 
63 #define PRIVATE(obj) ((obj)->priv)
64 
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)
70 
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 }
89 
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) \
103 }
104 
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)
116 
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)
131 
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)
143 
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) ;
160 
161 #define BYTE(a_input, a_n, a_eof) \
162 cr_input_peek_byte2 (a_input, a_n, a_eof)
163 
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) ;
175 
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) ;
188 
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) ; \
202 }
203 
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 ;}
212 
213 static enum CRStatus  cr_tknzr_parse_nl (CRTknzr * a_this,
214                                          guchar ** a_start,
215                                          guchar ** a_end,
216                                          CRParsingLocation *a_location);
217 
218 static enum CRStatus cr_tknzr_parse_w (CRTknzr * a_this,
219                                        guchar ** a_start,
220                                        guchar ** a_end,
221                                        CRParsingLocation *a_location) ;
222 
223 static enum CRStatus cr_tknzr_parse_unicode_escape (CRTknzr * a_this,
224                                                     guint32 * a_unicode,
225                                                     CRParsingLocation *a_location) ;
226 
227 static enum CRStatus cr_tknzr_parse_escape (CRTknzr * a_this,
228                                             guint32 * a_esc_code,
229                                             CRParsingLocation *a_location);
230 
231 static enum CRStatus cr_tknzr_parse_string (CRTknzr * a_this,
232                                             CRString ** a_str);
233 
234 static enum CRStatus cr_tknzr_parse_comment (CRTknzr * a_this,
235                                              CRString ** a_comment);
236 
237 static enum CRStatus cr_tknzr_parse_nmstart (CRTknzr * a_this,
238                                              guint32 * a_char,
239                                              CRParsingLocation *a_location);
240 
241 static enum CRStatus cr_tknzr_parse_num (CRTknzr * a_this,
242                                          CRNum ** a_num);
243 
244 /**********************************
245  *PRIVATE methods
246  **********************************/
247 
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
cr_tknzr_parse_w(CRTknzr * a_this,guchar ** a_start,guchar ** a_end,CRParsingLocation * a_location)261 cr_tknzr_parse_w (CRTknzr * a_this,
262                   guchar ** a_start,
263                   guchar ** a_end,
264                   CRParsingLocation *a_location)
265 {
266         guint32 cur_char = 0;
267         CRInputPos init_pos;
268         enum CRStatus status = CR_OK;
269 
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);
274 
275         RECORD_INITIAL_POS (a_this, &init_pos);
276 
277         *a_start = NULL;
278         *a_end = NULL;
279 
280         READ_NEXT_CHAR (a_this, &cur_char);
281 
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;
292 
293         for (;;) {
294                 gboolean is_eof = FALSE;
295 
296                 cr_input_get_end_of_file (PRIVATE (a_this)->input, &is_eof);
297                 if (is_eof)
298                         break;
299 
300                 status = cr_tknzr_peek_char (a_this, &cur_char);
301                 if (status == CR_END_OF_INPUT_ERROR) {
302                         break;
303                 } else if (status != CR_OK) {
304                         goto error;
305                 }
306 
307                 if (cr_utils_is_white_space (cur_char) == TRUE) {
308                         READ_NEXT_CHAR (a_this, &cur_char);
309                         RECORD_CUR_BYTE_ADDR (a_this, a_end);
310                 } else {
311                         break;
312                 }
313         }
314 
315         return CR_OK;
316 
317       error:
318         cr_tknzr_set_cur_pos (a_this, &init_pos);
319 
320         return status;
321 }
322 
323 /**
324  *Parses a newline as defined in the css2 spec:
325  * nl   ::=    \n|\r\n|\r|\f
326  *
327  *@param a_this the "this pointer" of the current instance of #CRTknzr.
328  *@param a_start a pointer to the first character of the successfully
329  *parsed string.
330  *@param a_end a pointer to the last character of the successfully parsed
331  *string.
332  *@result CR_OK uppon successfull completion, an error code otherwise.
333  */
334 static enum CRStatus
cr_tknzr_parse_nl(CRTknzr * a_this,guchar ** a_start,guchar ** a_end,CRParsingLocation * a_location)335 cr_tknzr_parse_nl (CRTknzr * a_this,
336                    guchar ** a_start,
337                    guchar ** a_end,
338                    CRParsingLocation *a_location)
339 {
340         CRInputPos init_pos;
341         guchar next_chars[2] = { 0 };
342         enum CRStatus status = CR_PARSING_ERROR;
343 
344         g_return_val_if_fail (a_this && PRIVATE (a_this)
345                               && a_start && a_end, CR_BAD_PARAM_ERROR);
346 
347         RECORD_INITIAL_POS (a_this, &init_pos);
348 
349         PEEK_BYTE (a_this, 1, &next_chars[0]);
350         PEEK_BYTE (a_this, 2, &next_chars[1]);
351 
352         if ((next_chars[0] == '\r' && next_chars[1] == '\n')) {
353                 SKIP_BYTES (a_this, 1);
354                 if (a_location) {
355                         cr_tknzr_get_parsing_location
356                                 (a_this, a_location) ;
357                 }
358                 SKIP_CHARS (a_this, 1);
359 
360                 RECORD_CUR_BYTE_ADDR (a_this, a_end);
361 
362                 status = CR_OK;
363         } else if (next_chars[0] == '\n'
364                    || next_chars[0] == '\r' || next_chars[0] == '\f') {
365                 SKIP_CHARS (a_this, 1);
366                 if (a_location) {
367                         cr_tknzr_get_parsing_location
368                                 (a_this, a_location) ;
369                 }
370                 RECORD_CUR_BYTE_ADDR (a_this, a_start);
371                 *a_end = *a_start;
372                 status = CR_OK;
373         } else {
374                 status = CR_PARSING_ERROR;
375                 goto error;
376         }
377         return CR_OK ;
378 
379  error:
380         cr_tknzr_set_cur_pos (a_this, &init_pos) ;
381         return status;
382 }
383 
384 /**
385  *Go ahead in the parser input, skipping all the spaces.
386  *If the next char if not a white space, this function does nothing.
387  *In any cases, it stops when it encounters a non white space character.
388  *
389  *@param a_this the current instance of #CRTknzr.
390  *@return CR_OK upon successfull completion, an error code otherwise.
391  */
392 static enum CRStatus
cr_tknzr_try_to_skip_spaces(CRTknzr * a_this)393 cr_tknzr_try_to_skip_spaces (CRTknzr * a_this)
394 {
395         enum CRStatus status = CR_ERROR;
396         guint32 cur_char = 0;
397 
398         g_return_val_if_fail (a_this && PRIVATE (a_this)
399                               && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
400 
401         status = cr_input_peek_char (PRIVATE (a_this)->input, &cur_char);
402 
403         if (status != CR_OK) {
404                 if (status == CR_END_OF_INPUT_ERROR)
405                         return CR_OK;
406                 return status;
407         }
408 
409         if (cr_utils_is_white_space (cur_char) == TRUE) {
410                 gulong nb_chars = -1; /*consume all spaces */
411 
412                 status = cr_input_consume_white_spaces
413                         (PRIVATE (a_this)->input, &nb_chars);
414         }
415 
416         return status;
417 }
418 
419 /**
420  *Parses a "comment" as defined in the css spec at [4.1.1]:
421  *COMMENT ::= \/\*[^*]*\*+([^/][^*]*\*+)*\/ .
422  *This complex regexp is just to say that comments start
423  *with the two chars '/''*' and ends with the two chars '*''/'.
424  *It also means that comments cannot be nested.
425  *So based on that, I've just tried to implement the parsing function
426  *simply and in a straight forward manner.
427  */
428 static enum CRStatus
cr_tknzr_parse_comment(CRTknzr * a_this,CRString ** a_comment)429 cr_tknzr_parse_comment (CRTknzr * a_this,
430                         CRString ** a_comment)
431 {
432         enum CRStatus status = CR_OK;
433         CRInputPos init_pos;
434         guint32 cur_char = 0, next_char= 0;
435         CRString *comment = NULL;
436         CRParsingLocation loc = {0} ;
437 
438         g_return_val_if_fail (a_this && PRIVATE (a_this)
439                               && PRIVATE (a_this)->input,
440                               CR_BAD_PARAM_ERROR);
441 
442         RECORD_INITIAL_POS (a_this, &init_pos);
443         READ_NEXT_CHAR (a_this, &cur_char) ;
444         ENSURE_PARSING_COND (cur_char == '/');
445         cr_tknzr_get_parsing_location (a_this, &loc) ;
446 
447         READ_NEXT_CHAR (a_this, &cur_char);
448         ENSURE_PARSING_COND (cur_char == '*');
449         comment = cr_string_new ();
450         for (;;) { /* [^*]* */
451                 PEEK_NEXT_CHAR (a_this, &next_char);
452                 if (next_char == '*')
453                         break;
454                 READ_NEXT_CHAR (a_this, &cur_char);
455                 g_string_append_unichar (comment->stryng, cur_char);
456         }
457         /* Stop condition: next_char == '*' */
458         for (;;) { /* \*+ */
459                 READ_NEXT_CHAR(a_this, &cur_char);
460                 ENSURE_PARSING_COND (cur_char == '*');
461                 g_string_append_unichar (comment->stryng, cur_char);
462                 PEEK_NEXT_CHAR (a_this, &next_char);
463                 if (next_char != '*')
464                         break;
465         }
466         /* Stop condition: next_char != '*' */
467         for (;;) { /* ([^/][^*]*\*+)* */
468                 if (next_char == '/')
469                         break;
470                 READ_NEXT_CHAR(a_this, &cur_char);
471                 g_string_append_unichar (comment->stryng, cur_char);
472                 for (;;) { /* [^*]* */
473                         PEEK_NEXT_CHAR (a_this, &next_char);
474                         if (next_char == '*')
475                                 break;
476                         READ_NEXT_CHAR (a_this, &cur_char);
477                         g_string_append_unichar (comment->stryng, cur_char);
478                 }
479                 /* Stop condition: next_char = '*', no need to verify, because peek and read exit to error anyway */
480                 for (;;) { /* \*+ */
481                         READ_NEXT_CHAR(a_this, &cur_char);
482                         ENSURE_PARSING_COND (cur_char == '*');
483                         g_string_append_unichar (comment->stryng, cur_char);
484                         PEEK_NEXT_CHAR (a_this, &next_char);
485                         if (next_char != '*')
486                                 break;
487                 }
488                 /* Continue condition: next_char != '*' */
489         }
490         /* Stop condition: next_char == '\/' */
491         READ_NEXT_CHAR(a_this, &cur_char);
492         g_string_append_unichar (comment->stryng, cur_char);
493 
494         if (status == CR_OK) {
495                 cr_parsing_location_copy (&comment->location,
496                                           &loc) ;
497                 *a_comment = comment;
498                 return CR_OK;
499         }
500  error:
501 
502         if (comment) {
503                 cr_string_destroy (comment);
504                 comment = NULL;
505         }
506 
507         cr_tknzr_set_cur_pos (a_this, &init_pos);
508 
509         return status;
510 }
511 
512 /**
513  *Parses an 'unicode' escape sequence defined
514  *in css spec at chap 4.1.1:
515  *unicode ::= \\[0-9a-f]{1,6}[ \n\r\t\f]?
516  *@param a_this the current instance of #CRTknzr.
517  *@param a_start out parameter. A pointer to the start
518  *of the unicode escape sequence. Must *NOT* be deleted by
519  *the caller.
520  *@param a_end out parameter. A pointer to the last character
521  *of the unicode escape sequence. Must *NOT* be deleted by the caller.
522  *@return CR_OK if parsing succeded, an error code otherwise.
523  *Error code can be either CR_PARSING_ERROR if the string
524  *parsed just doesn't
525  *respect the production or another error if a
526  *lower level error occurred.
527  */
528 static enum CRStatus
cr_tknzr_parse_unicode_escape(CRTknzr * a_this,guint32 * a_unicode,CRParsingLocation * a_location)529 cr_tknzr_parse_unicode_escape (CRTknzr * a_this,
530                                guint32 * a_unicode,
531                                CRParsingLocation *a_location)
532 {
533         guint32 cur_char;
534         CRInputPos init_pos;
535         glong occur = 0;
536         guint32 unicode = 0;
537         guchar *tmp_char_ptr1 = NULL,
538                 *tmp_char_ptr2 = NULL;
539         enum CRStatus status = CR_OK;
540 
541         g_return_val_if_fail (a_this && PRIVATE (a_this)
542                               && a_unicode, CR_BAD_PARAM_ERROR);
543 
544         /*first, let's backup the current position pointer */
545         RECORD_INITIAL_POS (a_this, &init_pos);
546 
547         READ_NEXT_CHAR (a_this, &cur_char);
548 
549         if (cur_char != '\\') {
550                 status = CR_PARSING_ERROR;
551                 goto error;
552         }
553         if (a_location) {
554                 cr_tknzr_get_parsing_location
555                         (a_this, a_location) ;
556         }
557         PEEK_NEXT_CHAR (a_this, &cur_char);
558 
559         for (occur = 0, unicode = 0; ((cur_char >= '0' && cur_char <= '9')
560                                       || (cur_char >= 'a' && cur_char <= 'f')
561                                       || (cur_char >= 'A' && cur_char <= 'F'))
562              && occur < 6; occur++) {
563                 gint cur_char_val = 0;
564 
565                 READ_NEXT_CHAR (a_this, &cur_char);
566 
567                 if ((cur_char >= '0' && cur_char <= '9')) {
568                         cur_char_val = (cur_char - '0');
569                 } else if ((cur_char >= 'a' && cur_char <= 'f')) {
570                         cur_char_val = 10 + (cur_char - 'a');
571                 } else if ((cur_char >= 'A' && cur_char <= 'F')) {
572                         cur_char_val = 10 + (cur_char - 'A');
573                 }
574 
575                 unicode = unicode * 16 + cur_char_val;
576 
577                 PEEK_NEXT_CHAR (a_this, &cur_char);
578         }
579 
580         /* Eat a whitespace if possible. */
581         cr_tknzr_parse_w (a_this, &tmp_char_ptr1,
582                           &tmp_char_ptr2, NULL);
583         *a_unicode = unicode;
584         return CR_OK;
585 
586       error:
587         /*
588          *restore the initial position pointer backuped at
589          *the beginning of this function.
590          */
591         cr_tknzr_set_cur_pos (a_this, &init_pos);
592 
593         return status;
594 }
595 
596 /**
597  *parses an escape sequence as defined by the css spec:
598  *escape ::= {unicode}|\\[ -~\200-\4177777]
599  *@param a_this the current instance of #CRTknzr .
600  */
601 static enum CRStatus
cr_tknzr_parse_escape(CRTknzr * a_this,guint32 * a_esc_code,CRParsingLocation * a_location)602 cr_tknzr_parse_escape (CRTknzr * a_this, guint32 * a_esc_code,
603                        CRParsingLocation *a_location)
604 {
605         enum CRStatus status = CR_OK;
606         guint32 cur_char = 0;
607         CRInputPos init_pos;
608         guchar next_chars[2];
609 
610         g_return_val_if_fail (a_this && PRIVATE (a_this)
611                               && a_esc_code, CR_BAD_PARAM_ERROR);
612 
613         RECORD_INITIAL_POS (a_this, &init_pos);
614 
615         PEEK_BYTE (a_this, 1, &next_chars[0]);
616         PEEK_BYTE (a_this, 2, &next_chars[1]);
617 
618         if (next_chars[0] != '\\') {
619                 status = CR_PARSING_ERROR;
620                 goto error;
621         }
622 
623         if ((next_chars[1] >= '0' && next_chars[1] <= '9')
624             || (next_chars[1] >= 'a' && next_chars[1] <= 'f')
625             || (next_chars[1] >= 'A' && next_chars[1] <= 'F')) {
626                 status = cr_tknzr_parse_unicode_escape (a_this, a_esc_code,
627                                                         a_location);
628         } else {
629                 /*consume the '\' char */
630                 READ_NEXT_CHAR (a_this, &cur_char);
631                 if (a_location) {
632                         cr_tknzr_get_parsing_location (a_this,
633                                                        a_location) ;
634                 }
635                 /*then read the char after the '\' */
636                 READ_NEXT_CHAR (a_this, &cur_char);
637 
638                 if (cur_char != ' ' && (cur_char < 200 || cur_char > 4177777)) {
639                         status = CR_PARSING_ERROR;
640                         goto error;
641                 }
642                 *a_esc_code = cur_char;
643 
644         }
645         if (status == CR_OK) {
646                 return CR_OK;
647         }
648  error:
649         cr_tknzr_set_cur_pos (a_this, &init_pos);
650         return status;
651 }
652 
653 /**
654  *Parses a string type as defined in css spec [4.1.1]:
655  *
656  *string ::= {string1}|{string2}
657  *string1 ::= \"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\"
658  *string2 ::= \'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\'
659  *
660  *@param a_this the current instance of #CRTknzr.
661  *@param a_start out parameter. Upon successfull completion,
662  *points to the beginning of the string, points to an undefined value
663  *otherwise.
664  *@param a_end out parameter. Upon successfull completion, points to
665  *the beginning of the string, points to an undefined value otherwise.
666  *@return CR_OK upon successfull completion, an error code otherwise.
667  */
668 static enum CRStatus
cr_tknzr_parse_string(CRTknzr * a_this,CRString ** a_str)669 cr_tknzr_parse_string (CRTknzr * a_this, CRString ** a_str)
670 {
671         guint32 cur_char = 0,
672                 delim = 0;
673         CRInputPos init_pos;
674         enum CRStatus status = CR_OK;
675         CRString *str = NULL;
676 
677         g_return_val_if_fail (a_this && PRIVATE (a_this)
678                               && PRIVATE (a_this)->input
679                               && a_str, CR_BAD_PARAM_ERROR);
680 
681         RECORD_INITIAL_POS (a_this, &init_pos);
682         READ_NEXT_CHAR (a_this, &cur_char);
683 
684         if (cur_char == '"')
685                 delim = '"';
686         else if (cur_char == '\'')
687                 delim = '\'';
688         else {
689                 status = CR_PARSING_ERROR;
690                 goto error;
691         }
692         str = cr_string_new ();
693         if (str) {
694                 cr_tknzr_get_parsing_location
695                         (a_this, &str->location) ;
696         }
697         for (;;) {
698                 guchar next_chars[2] = { 0 };
699 
700                 PEEK_BYTE (a_this, 1, &next_chars[0]);
701                 PEEK_BYTE (a_this, 2, &next_chars[1]);
702 
703                 if (next_chars[0] == '\\') {
704                         guchar *tmp_char_ptr1 = NULL,
705                                 *tmp_char_ptr2 = NULL;
706                         guint32 esc_code = 0;
707 
708                         if (next_chars[1] == '\'' || next_chars[1] == '"') {
709                                 g_string_append_unichar (str->stryng,
710                                                          next_chars[1]);
711                                 SKIP_BYTES (a_this, 2);
712                                 status = CR_OK;
713                         } else {
714                                 status = cr_tknzr_parse_escape
715                                         (a_this, &esc_code, NULL);
716 
717                                 if (status == CR_OK) {
718                                         g_string_append_unichar
719                                                 (str->stryng,
720                                                  esc_code);
721                                 }
722                         }
723 
724                         if (status != CR_OK) {
725                                 /*
726                                  *consume the '\' char, and try to parse
727                                  *a newline.
728                                  */
729                                 READ_NEXT_CHAR (a_this, &cur_char);
730 
731                                 status = cr_tknzr_parse_nl
732                                         (a_this, &tmp_char_ptr1,
733                                          &tmp_char_ptr2, NULL);
734                         }
735 
736                         CHECK_PARSING_STATUS (status, FALSE);
737                 } else if (strchr ("\t !#$%&", next_chars[0])
738                            || (next_chars[0] >= '(' && next_chars[0] <= '~')) {
739                         READ_NEXT_CHAR (a_this, &cur_char);
740                         g_string_append_unichar (str->stryng,
741                                                  cur_char);
742                         status = CR_OK;
743                 }
744 
745                 else if (cr_utils_is_nonascii (next_chars[0])) {
746                         READ_NEXT_CHAR (a_this, &cur_char);
747                         g_string_append_unichar (str->stryng, cur_char);
748                 } else if (next_chars[0] == delim) {
749                         READ_NEXT_CHAR (a_this, &cur_char);
750                         break;
751                 } else {
752                         status = CR_PARSING_ERROR;
753                         goto error;
754                 }
755         }
756 
757         if (status == CR_OK) {
758                 if (*a_str == NULL) {
759                         *a_str = str;
760                         str = NULL;
761                 } else {
762                         (*a_str)->stryng = g_string_append_len
763                                 ((*a_str)->stryng,
764                                  str->stryng->str,
765                                  str->stryng->len);
766                         cr_string_destroy (str);
767                 }
768                 return CR_OK;
769         }
770 
771  error:
772 
773         if (str) {
774                 cr_string_destroy (str) ;
775                 str = NULL;
776         }
777         cr_tknzr_set_cur_pos (a_this, &init_pos);
778         return status;
779 }
780 
781 /**
782  *Parses the an nmstart as defined by the css2 spec [4.1.1]:
783  * nmstart [a-zA-Z]|{nonascii}|{escape}
784  *
785  *@param a_this the current instance of #CRTknzr.
786  *@param a_start out param. A pointer to the starting point of
787  *the token.
788  *@param a_end out param. A pointer to the ending point of the
789  *token.
790  *@param a_char out param. The actual parsed nmchar.
791  *@return CR_OK upon successfull completion,
792  *an error code otherwise.
793  */
794 static enum CRStatus
cr_tknzr_parse_nmstart(CRTknzr * a_this,guint32 * a_char,CRParsingLocation * a_location)795 cr_tknzr_parse_nmstart (CRTknzr * a_this,
796                         guint32 * a_char,
797                         CRParsingLocation *a_location)
798 {
799         CRInputPos init_pos;
800         enum CRStatus status = CR_OK;
801         guint32 cur_char = 0,
802                 next_char = 0;
803 
804         g_return_val_if_fail (a_this && PRIVATE (a_this)
805                               && PRIVATE (a_this)->input
806                               && a_char, CR_BAD_PARAM_ERROR);
807 
808         RECORD_INITIAL_POS (a_this, &init_pos);
809 
810         PEEK_NEXT_CHAR (a_this, &next_char);
811 
812         if (next_char == '\\') {
813                 status = cr_tknzr_parse_escape (a_this, a_char,
814                                                 a_location);
815 
816                 if (status != CR_OK)
817                         goto error;
818 
819         } else if (cr_utils_is_nonascii (next_char) == TRUE
820                    || ((next_char >= 'a') && (next_char <= 'z'))
821                    || ((next_char >= 'A') && (next_char <= 'Z'))
822                 ) {
823                 READ_NEXT_CHAR (a_this, &cur_char);
824                 if (a_location) {
825                         cr_tknzr_get_parsing_location (a_this,
826                                                        a_location) ;
827                 }
828                 *a_char = cur_char;
829                 status = CR_OK;
830         } else {
831                 status = CR_PARSING_ERROR;
832                 goto error;
833         }
834 
835         return CR_OK;
836 
837  error:
838         cr_tknzr_set_cur_pos (a_this, &init_pos);
839 
840         return status;
841 
842 }
843 
844 /**
845  *Parses an nmchar as described in the css spec at
846  *chap 4.1.1:
847  *nmchar ::= [a-z0-9-]|{nonascii}|{escape}
848  *
849  *Humm, I have added the possibility for nmchar to
850  *contain upper case letters.
851  *
852  *@param a_this the current instance of #CRTknzr.
853  *@param a_start out param. A pointer to the starting point of
854  *the token.
855  *@param a_end out param. A pointer to the ending point of the
856  *token.
857  *@param a_char out param. The actual parsed nmchar.
858  *@return CR_OK upon successfull completion,
859  *an error code otherwise.
860  */
861 static enum CRStatus
cr_tknzr_parse_nmchar(CRTknzr * a_this,guint32 * a_char,CRParsingLocation * a_location)862 cr_tknzr_parse_nmchar (CRTknzr * a_this, guint32 * a_char,
863                        CRParsingLocation *a_location)
864 {
865         guint32 cur_char = 0,
866                 next_char = 0;
867         enum CRStatus status = CR_OK;
868         CRInputPos init_pos;
869 
870         g_return_val_if_fail (a_this && PRIVATE (a_this) && a_char,
871                               CR_BAD_PARAM_ERROR);
872 
873         RECORD_INITIAL_POS (a_this, &init_pos);
874 
875         status = cr_input_peek_char (PRIVATE (a_this)->input,
876                                      &next_char) ;
877         if (status != CR_OK)
878                 goto error;
879 
880         if (next_char == '\\') {
881                 status = cr_tknzr_parse_escape (a_this, a_char,
882                                                 a_location);
883 
884                 if (status != CR_OK)
885                         goto error;
886 
887         } else if (cr_utils_is_nonascii (next_char) == TRUE
888                    || ((next_char >= 'a') && (next_char <= 'z'))
889                    || ((next_char >= 'A') && (next_char <= 'Z'))
890                    || ((next_char >= '0') && (next_char <= '9'))
891                    || (next_char == '-')
892                    || (next_char == '_') /*'_' not allowed by the spec. */
893                 ) {
894                 READ_NEXT_CHAR (a_this, &cur_char);
895                 *a_char = cur_char;
896                 status = CR_OK;
897                 if (a_location) {
898                         cr_tknzr_get_parsing_location
899                                 (a_this, a_location) ;
900                 }
901         } else {
902                 status = CR_PARSING_ERROR;
903                 goto error;
904         }
905         return CR_OK;
906 
907  error:
908         cr_tknzr_set_cur_pos (a_this, &init_pos);
909         return status;
910 }
911 
912 /**
913  *Parses an "ident" as defined in css spec [4.1.1]:
914  *ident ::= {nmstart}{nmchar}*
915  *
916  *Actually parses it using the css3 grammar:
917  *ident ::= -?{nmstart}{nmchar}*
918  *@param a_this the currens instance of #CRTknzr.
919  *
920  *@param a_str a pointer to parsed ident. If *a_str is NULL,
921  *this function allocates a new instance of CRString. If not,
922  *the function just appends the parsed string to the one passed.
923  *In both cases it is up to the caller to free *a_str.
924  *
925  *@return CR_OK upon successfull completion, an error code
926  *otherwise.
927  */
928 static enum CRStatus
cr_tknzr_parse_ident(CRTknzr * a_this,CRString ** a_str)929 cr_tknzr_parse_ident (CRTknzr * a_this, CRString ** a_str)
930 {
931         guint32 tmp_char = 0;
932         CRString *stringue = NULL ;
933         CRInputPos init_pos;
934         enum CRStatus status = CR_OK;
935         gboolean location_is_set = FALSE ;
936 
937         g_return_val_if_fail (a_this && PRIVATE (a_this)
938                               && PRIVATE (a_this)->input
939                               && a_str, CR_BAD_PARAM_ERROR);
940 
941         RECORD_INITIAL_POS (a_this, &init_pos);
942         PEEK_NEXT_CHAR (a_this, &tmp_char) ;
943         stringue = cr_string_new () ;
944         g_return_val_if_fail (stringue,
945                               CR_OUT_OF_MEMORY_ERROR) ;
946 
947         if (tmp_char == '-') {
948                 READ_NEXT_CHAR (a_this, &tmp_char) ;
949                 cr_tknzr_get_parsing_location
950                         (a_this, &stringue->location) ;
951                 location_is_set = TRUE ;
952                 g_string_append_unichar (stringue->stryng,
953                                          tmp_char) ;
954         }
955         status = cr_tknzr_parse_nmstart (a_this, &tmp_char, NULL);
956         if (status != CR_OK) {
957                 status = CR_PARSING_ERROR;
958                 goto end ;
959         }
960         if (location_is_set == FALSE) {
961                 cr_tknzr_get_parsing_location
962                         (a_this, &stringue->location) ;
963                 location_is_set = TRUE ;
964         }
965         g_string_append_unichar (stringue->stryng, tmp_char);
966         for (;;) {
967                 status = cr_tknzr_parse_nmchar (a_this,
968                                                 &tmp_char,
969                                                 NULL);
970                 if (status != CR_OK) {
971                         status = CR_OK ;
972                         break;
973                 }
974                 g_string_append_unichar (stringue->stryng, tmp_char);
975         }
976         if (status == CR_OK) {
977                 if (!*a_str) {
978                         *a_str = stringue ;
979 
980                 } else {
981                         g_string_append_len ((*a_str)->stryng,
982                                              stringue->stryng->str,
983                                              stringue->stryng->len) ;
984                         cr_string_destroy (stringue) ;
985                 }
986                 stringue = NULL ;
987         }
988 
989  error:
990  end:
991         if (stringue) {
992                 cr_string_destroy (stringue) ;
993                 stringue = NULL ;
994         }
995         if (status != CR_OK ) {
996                 cr_tknzr_set_cur_pos (a_this, &init_pos) ;
997         }
998         return status ;
999 }
1000 
1001 
1002 /**
1003  *Parses a "name" as defined by css spec [4.1.1]:
1004  *name ::= {nmchar}+
1005  *
1006  *@param a_this the current instance of #CRTknzr.
1007  *
1008  *@param a_str out parameter. A pointer to the successfully parsed
1009  *name. If *a_str is set to NULL, this function allocates a new instance
1010  *of CRString. If not, it just appends the parsed name to the passed *a_str.
1011  *In both cases, it is up to the caller to free *a_str.
1012  *
1013  *@return CR_OK upon successfull completion, an error code otherwise.
1014  */
1015 static enum CRStatus
cr_tknzr_parse_name(CRTknzr * a_this,CRString ** a_str)1016 cr_tknzr_parse_name (CRTknzr * a_this,
1017                      CRString ** a_str)
1018 {
1019         guint32 tmp_char = 0;
1020         CRInputPos init_pos;
1021         enum CRStatus status = CR_OK;
1022         gboolean str_needs_free = FALSE,
1023                 is_first_nmchar=TRUE ;
1024         glong i = 0;
1025         CRParsingLocation loc = {0} ;
1026 
1027         g_return_val_if_fail (a_this && PRIVATE (a_this)
1028                               && PRIVATE (a_this)->input
1029                               && a_str,
1030                               CR_BAD_PARAM_ERROR) ;
1031 
1032         RECORD_INITIAL_POS (a_this, &init_pos);
1033 
1034         if (*a_str == NULL) {
1035                 *a_str = cr_string_new ();
1036                 str_needs_free = TRUE;
1037         }
1038         for (i = 0;; i++) {
1039                 if (is_first_nmchar == TRUE) {
1040                         status = cr_tknzr_parse_nmchar
1041                                 (a_this, &tmp_char,
1042                                  &loc) ;
1043                         is_first_nmchar = FALSE ;
1044                 } else {
1045                         status = cr_tknzr_parse_nmchar
1046                                 (a_this, &tmp_char, NULL) ;
1047                 }
1048                 if (status != CR_OK)
1049                         break;
1050                 g_string_append_unichar ((*a_str)->stryng,
1051                                          tmp_char);
1052         }
1053         if (i > 0) {
1054                 cr_parsing_location_copy
1055                         (&(*a_str)->location, &loc) ;
1056                 return CR_OK;
1057         }
1058         if (str_needs_free == TRUE && *a_str) {
1059                 cr_string_destroy (*a_str);
1060                 *a_str = NULL;
1061         }
1062         cr_tknzr_set_cur_pos (a_this, &init_pos);
1063         return CR_PARSING_ERROR;
1064 }
1065 
1066 /**
1067  *Parses a "hash" as defined by the css spec in [4.1.1]:
1068  *HASH ::= #{name}
1069  */
1070 static enum CRStatus
cr_tknzr_parse_hash(CRTknzr * a_this,CRString ** a_str)1071 cr_tknzr_parse_hash (CRTknzr * a_this, CRString ** a_str)
1072 {
1073         guint32 cur_char = 0;
1074         CRInputPos init_pos;
1075         enum CRStatus status = CR_OK;
1076         gboolean str_needs_free = FALSE;
1077         CRParsingLocation loc = {0} ;
1078 
1079         g_return_val_if_fail (a_this && PRIVATE (a_this)
1080                               && PRIVATE (a_this)->input,
1081                               CR_BAD_PARAM_ERROR);
1082 
1083         RECORD_INITIAL_POS (a_this, &init_pos);
1084         READ_NEXT_CHAR (a_this, &cur_char);
1085         if (cur_char != '#') {
1086                 status = CR_PARSING_ERROR;
1087                 goto error;
1088         }
1089         if (*a_str == NULL) {
1090                 *a_str = cr_string_new ();
1091                 str_needs_free = TRUE;
1092         }
1093         cr_tknzr_get_parsing_location (a_this,
1094                                        &loc) ;
1095         status = cr_tknzr_parse_name (a_this, a_str);
1096         cr_parsing_location_copy (&(*a_str)->location, &loc) ;
1097         if (status != CR_OK) {
1098                 goto error;
1099         }
1100         return CR_OK;
1101 
1102  error:
1103         if (str_needs_free == TRUE && *a_str) {
1104                 cr_string_destroy (*a_str);
1105                 *a_str = NULL;
1106         }
1107 
1108         cr_tknzr_set_cur_pos (a_this, &init_pos);
1109         return status;
1110 }
1111 
1112 /**
1113  *Parses an uri as defined by the css spec [4.1.1]:
1114  * URI ::= url\({w}{string}{w}\)
1115  *         |url\({w}([!#$%&*-~]|{nonascii}|{escape})*{w}\)
1116  *
1117  *@param a_this the current instance of #CRTknzr.
1118  *@param a_str the successfully parsed url.
1119  *@return CR_OK upon successfull completion, an error code otherwise.
1120  */
1121 static enum CRStatus
cr_tknzr_parse_uri(CRTknzr * a_this,CRString ** a_str)1122 cr_tknzr_parse_uri (CRTknzr * a_this,
1123                     CRString ** a_str)
1124 {
1125         guint32 cur_char = 0;
1126         CRInputPos init_pos;
1127         enum CRStatus status = CR_PARSING_ERROR;
1128         guchar tab[4] = { 0 }, *tmp_ptr1 = NULL, *tmp_ptr2 = NULL;
1129         CRString *str = NULL;
1130         CRParsingLocation location = {0} ;
1131 
1132         g_return_val_if_fail (a_this
1133                               && PRIVATE (a_this)
1134                               && PRIVATE (a_this)->input
1135                               && a_str,
1136                               CR_BAD_PARAM_ERROR);
1137 
1138         RECORD_INITIAL_POS (a_this, &init_pos);
1139 
1140         PEEK_BYTE (a_this, 1, &tab[0]);
1141         PEEK_BYTE (a_this, 2, &tab[1]);
1142         PEEK_BYTE (a_this, 3, &tab[2]);
1143         PEEK_BYTE (a_this, 4, &tab[3]);
1144 
1145         if (tab[0] != 'u' || tab[1] != 'r' || tab[2] != 'l' || tab[3] != '(') {
1146                 status = CR_PARSING_ERROR;
1147                 goto error;
1148         }
1149         /*
1150          *Here, we want to skip 4 bytes ('u''r''l''(').
1151          *But we also need to keep track of the parsing location
1152          *of the 'u'. So, we skip 1 byte, we record the parsing
1153          *location, then we skip the 3 remaining bytes.
1154          */
1155         SKIP_CHARS (a_this, 1);
1156         cr_tknzr_get_parsing_location (a_this, &location) ;
1157         SKIP_CHARS (a_this, 3);
1158         cr_tknzr_try_to_skip_spaces (a_this);
1159         status = cr_tknzr_parse_string (a_this, a_str);
1160 
1161         if (status == CR_OK) {
1162                 guint32 next_char = 0;
1163                 status = cr_tknzr_parse_w (a_this, &tmp_ptr1,
1164                                            &tmp_ptr2, NULL);
1165                 cr_tknzr_try_to_skip_spaces (a_this);
1166                 PEEK_NEXT_CHAR (a_this, &next_char);
1167                 if (next_char == ')') {
1168                         READ_NEXT_CHAR (a_this, &cur_char);
1169                         status = CR_OK;
1170                 } else {
1171                         status = CR_PARSING_ERROR;
1172                 }
1173         }
1174         if (status != CR_OK) {
1175                 str = cr_string_new ();
1176                 for (;;) {
1177                         guint32 next_char = 0;
1178                         PEEK_NEXT_CHAR (a_this, &next_char);
1179                         if (strchr ("!#$%&", next_char)
1180                             || (next_char >= '*' && next_char <= '~')
1181                             || (cr_utils_is_nonascii (next_char) == TRUE)) {
1182                                 READ_NEXT_CHAR (a_this, &cur_char);
1183                                 g_string_append_unichar
1184                                         (str->stryng, cur_char);
1185                                 status = CR_OK;
1186                         } else {
1187                                 guint32 esc_code = 0;
1188                                 status = cr_tknzr_parse_escape
1189                                         (a_this, &esc_code, NULL);
1190                                 if (status == CR_OK) {
1191                                         g_string_append_unichar
1192                                                 (str->stryng,
1193                                                  esc_code);
1194                                 } else {
1195                                         status = CR_OK;
1196                                         break;
1197                                 }
1198                         }
1199                 }
1200                 cr_tknzr_try_to_skip_spaces (a_this);
1201                 READ_NEXT_CHAR (a_this, &cur_char);
1202                 if (cur_char == ')') {
1203                         status = CR_OK;
1204                 } else {
1205                         status = CR_PARSING_ERROR;
1206                         goto error;
1207                 }
1208                 if (str) {
1209                         if (*a_str == NULL) {
1210                                 *a_str = str;
1211                                 str = NULL;
1212                         } else {
1213                                 g_string_append_len
1214                                         ((*a_str)->stryng,
1215                                          str->stryng->str,
1216                                          str->stryng->len);
1217                                 cr_string_destroy (str);
1218                         }
1219                 }
1220         }
1221 
1222         cr_parsing_location_copy
1223                 (&(*a_str)->location,
1224                  &location) ;
1225         return CR_OK ;
1226  error:
1227         if (str) {
1228                 cr_string_destroy (str);
1229                 str = NULL;
1230         }
1231         cr_tknzr_set_cur_pos (a_this, &init_pos);
1232         return status;
1233 }
1234 
1235 /**
1236  *parses an RGB as defined in the css2 spec.
1237  *rgb: rgb '('S*{num}%?S* ',' {num}#?S*,S*{num}#?S*')'
1238  *
1239  *@param a_this the "this pointer" of the current instance of
1240  *@param a_rgb out parameter the parsed rgb.
1241  *@return CR_OK upon successfull completion, an error code otherwise.
1242  */
1243 static enum CRStatus
cr_tknzr_parse_rgb(CRTknzr * a_this,CRRgb ** a_rgb)1244 cr_tknzr_parse_rgb (CRTknzr * a_this, CRRgb ** a_rgb)
1245 {
1246         enum CRStatus status = CR_OK;
1247         CRInputPos init_pos;
1248         CRNum *num = NULL;
1249         guchar next_bytes[3] = { 0 }, cur_byte = 0;
1250         glong red = 0,
1251                 green = 0,
1252                 blue = 0,
1253                 i = 0;
1254         gboolean is_percentage = FALSE;
1255         CRParsingLocation location = {0} ;
1256 
1257         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1258 
1259         RECORD_INITIAL_POS (a_this, &init_pos);
1260 
1261         PEEK_BYTE (a_this, 1, &next_bytes[0]);
1262         PEEK_BYTE (a_this, 2, &next_bytes[1]);
1263         PEEK_BYTE (a_this, 3, &next_bytes[2]);
1264 
1265         if (((next_bytes[0] == 'r') || (next_bytes[0] == 'R'))
1266             && ((next_bytes[1] == 'g') || (next_bytes[1] == 'G'))
1267             && ((next_bytes[2] == 'b') || (next_bytes[2] == 'B'))) {
1268                 SKIP_CHARS (a_this, 1);
1269                 cr_tknzr_get_parsing_location (a_this, &location) ;
1270                 SKIP_CHARS (a_this, 2);
1271         } else {
1272                 status = CR_PARSING_ERROR;
1273                 goto error;
1274         }
1275         READ_NEXT_BYTE (a_this, &cur_byte);
1276         ENSURE_PARSING_COND (cur_byte == '(');
1277 
1278         cr_tknzr_try_to_skip_spaces (a_this);
1279         status = cr_tknzr_parse_num (a_this, &num);
1280         ENSURE_PARSING_COND ((status == CR_OK) && (num != NULL));
1281 
1282         if (num->val > G_MAXLONG) {
1283                 status = CR_PARSING_ERROR;
1284                 goto error;
1285         }
1286 
1287         red = num->val;
1288         cr_num_destroy (num);
1289         num = NULL;
1290 
1291         PEEK_BYTE (a_this, 1, &next_bytes[0]);
1292         if (next_bytes[0] == '%') {
1293                 SKIP_CHARS (a_this, 1);
1294                 is_percentage = TRUE;
1295         }
1296         cr_tknzr_try_to_skip_spaces (a_this);
1297 
1298         for (i = 0; i < 2; i++) {
1299                 READ_NEXT_BYTE (a_this, &cur_byte);
1300                 ENSURE_PARSING_COND (cur_byte == ',');
1301 
1302                 cr_tknzr_try_to_skip_spaces (a_this);
1303                 status = cr_tknzr_parse_num (a_this, &num);
1304                 ENSURE_PARSING_COND ((status == CR_OK) && (num != NULL));
1305 
1306                 if (num->val > G_MAXLONG) {
1307                         status = CR_PARSING_ERROR;
1308                         goto error;
1309                 }
1310 
1311                 PEEK_BYTE (a_this, 1, &next_bytes[0]);
1312                 if (next_bytes[0] == '%') {
1313                         SKIP_CHARS (a_this, 1);
1314                         is_percentage = 1;
1315                 }
1316 
1317                 if (i == 0) {
1318                         green = num->val;
1319                 } else if (i == 1) {
1320                         blue = num->val;
1321                 }
1322 
1323                 if (num) {
1324                         cr_num_destroy (num);
1325                         num = NULL;
1326                 }
1327                 cr_tknzr_try_to_skip_spaces (a_this);
1328         }
1329 
1330         READ_NEXT_BYTE (a_this, &cur_byte);
1331         if (*a_rgb == NULL) {
1332                 *a_rgb = cr_rgb_new_with_vals (red, green, blue,
1333                                                is_percentage);
1334 
1335                 if (*a_rgb == NULL) {
1336                         status = CR_ERROR;
1337                         goto error;
1338                 }
1339                 status = CR_OK;
1340         } else {
1341                 (*a_rgb)->red = red;
1342                 (*a_rgb)->green = green;
1343                 (*a_rgb)->blue = blue;
1344                 (*a_rgb)->is_percentage = is_percentage;
1345 
1346                 status = CR_OK;
1347         }
1348 
1349         if (status == CR_OK) {
1350                 if (a_rgb && *a_rgb) {
1351                         cr_parsing_location_copy
1352                                 (&(*a_rgb)->location,
1353                                  &location) ;
1354                 }
1355                 return CR_OK;
1356         }
1357 
1358  error:
1359         if (num) {
1360                 cr_num_destroy (num);
1361                 num = NULL;
1362         }
1363 
1364         cr_tknzr_set_cur_pos (a_this, &init_pos);
1365         return CR_OK;
1366 }
1367 
1368 /**
1369  *Parses a atkeyword as defined by the css spec in [4.1.1]:
1370  *ATKEYWORD ::= @{ident}
1371  *
1372  *@param a_this the "this pointer" of the current instance of
1373  *#CRTknzr.
1374  *
1375  *@param a_str out parameter. The parsed atkeyword. If *a_str is
1376  *set to NULL this function allocates a new instance of CRString and
1377  *sets it to the parsed atkeyword. If not, this function just appends
1378  *the parsed atkeyword to the end of *a_str. In both cases it is up to
1379  *the caller to free *a_str.
1380  *
1381  *@return CR_OK upon successfull completion, an error code otherwise.
1382  */
1383 static enum CRStatus
cr_tknzr_parse_atkeyword(CRTknzr * a_this,CRString ** a_str)1384 cr_tknzr_parse_atkeyword (CRTknzr * a_this,
1385                           CRString ** a_str)
1386 {
1387         guint32 cur_char = 0;
1388         CRInputPos init_pos;
1389         gboolean str_needs_free = FALSE;
1390         enum CRStatus status = CR_OK;
1391 
1392         g_return_val_if_fail (a_this && PRIVATE (a_this)
1393                               && PRIVATE (a_this)->input
1394                               && a_str, CR_BAD_PARAM_ERROR);
1395 
1396         RECORD_INITIAL_POS (a_this, &init_pos);
1397 
1398         READ_NEXT_CHAR (a_this, &cur_char);
1399 
1400         if (cur_char != '@') {
1401                 status = CR_PARSING_ERROR;
1402                 goto error;
1403         }
1404 
1405         if (*a_str == NULL) {
1406                 *a_str = cr_string_new ();
1407                 str_needs_free = TRUE;
1408         }
1409         status = cr_tknzr_parse_ident (a_this, a_str);
1410         if (status != CR_OK) {
1411                 goto error;
1412         }
1413         return CR_OK;
1414  error:
1415 
1416         if (str_needs_free == TRUE && *a_str) {
1417                 cr_string_destroy (*a_str);
1418                 *a_str = NULL;
1419         }
1420         cr_tknzr_set_cur_pos (a_this, &init_pos);
1421         return status;
1422 }
1423 
1424 static enum CRStatus
cr_tknzr_parse_important(CRTknzr * a_this,CRParsingLocation * a_location)1425 cr_tknzr_parse_important (CRTknzr * a_this,
1426                           CRParsingLocation *a_location)
1427 {
1428         guint32 cur_char = 0;
1429         CRInputPos init_pos;
1430         enum CRStatus status = CR_OK;
1431 
1432         g_return_val_if_fail (a_this && PRIVATE (a_this)
1433                               && PRIVATE (a_this)->input,
1434                               CR_BAD_PARAM_ERROR);
1435 
1436         RECORD_INITIAL_POS (a_this, &init_pos);
1437         READ_NEXT_CHAR (a_this, &cur_char);
1438         ENSURE_PARSING_COND (cur_char == '!');
1439         if (a_location) {
1440                 cr_tknzr_get_parsing_location (a_this,
1441                                                a_location) ;
1442         }
1443         cr_tknzr_try_to_skip_spaces (a_this);
1444 
1445         if (BYTE (PRIVATE (a_this)->input, 1, NULL) == 'i'
1446             && BYTE (PRIVATE (a_this)->input, 2, NULL) == 'm'
1447             && BYTE (PRIVATE (a_this)->input, 3, NULL) == 'p'
1448             && BYTE (PRIVATE (a_this)->input, 4, NULL) == 'o'
1449             && BYTE (PRIVATE (a_this)->input, 5, NULL) == 'r'
1450             && BYTE (PRIVATE (a_this)->input, 6, NULL) == 't'
1451             && BYTE (PRIVATE (a_this)->input, 7, NULL) == 'a'
1452             && BYTE (PRIVATE (a_this)->input, 8, NULL) == 'n'
1453             && BYTE (PRIVATE (a_this)->input, 9, NULL) == 't') {
1454                 SKIP_BYTES (a_this, 9);
1455                 if (a_location) {
1456                         cr_tknzr_get_parsing_location (a_this,
1457                                                        a_location) ;
1458                 }
1459                 return CR_OK;
1460         } else {
1461                 status = CR_PARSING_ERROR;
1462         }
1463 
1464  error:
1465         cr_tknzr_set_cur_pos (a_this, &init_pos);
1466 
1467         return status;
1468 }
1469 
1470 /**
1471  *Parses a num as defined in the css spec [4.1.1]:
1472  *[0-9]+|[0-9]*\.[0-9]+
1473  *@param a_this the current instance of #CRTknzr.
1474  *@param a_num out parameter. The parsed number.
1475  *@return CR_OK upon successfull completion,
1476  *an error code otherwise.
1477  *
1478  *The CSS specification says that numbers may be
1479  *preceeded by '+' or '-' to indicate the sign.
1480  *Technically, the "num" construction as defined
1481  *by the tokenizer doesn't allow this, but we parse
1482  *it here for simplicity.
1483  */
1484 static enum CRStatus
cr_tknzr_parse_num(CRTknzr * a_this,CRNum ** a_num)1485 cr_tknzr_parse_num (CRTknzr * a_this,
1486                     CRNum ** a_num)
1487 {
1488         enum CRStatus status = CR_PARSING_ERROR;
1489         enum CRNumType val_type = NUM_GENERIC;
1490         gboolean parsing_dec,  /* true iff seen decimal point. */
1491                 parsed; /* true iff the substring seen so far is a valid CSS
1492                            number, i.e. `[0-9]+|[0-9]*\.[0-9]+'. */
1493         guint32 cur_char = 0,
1494                 next_char = 0;
1495         gdouble numerator, denominator = 1;
1496         CRInputPos init_pos;
1497         CRParsingLocation location = {0} ;
1498         int sign = 1;
1499 
1500         g_return_val_if_fail (a_this && PRIVATE (a_this)
1501                               && PRIVATE (a_this)->input,
1502                               CR_BAD_PARAM_ERROR);
1503 
1504         RECORD_INITIAL_POS (a_this, &init_pos);
1505         READ_NEXT_CHAR (a_this, &cur_char);
1506 
1507         if (cur_char == '+' || cur_char == '-') {
1508                 if (cur_char == '-') {
1509                         sign = -1;
1510                 }
1511                 READ_NEXT_CHAR (a_this, &cur_char);
1512         }
1513 
1514         if (IS_NUM (cur_char)) {
1515                 numerator = (cur_char - '0');
1516                 parsing_dec = FALSE;
1517                 parsed = TRUE;
1518         } else if (cur_char == '.') {
1519                 numerator = 0;
1520                 parsing_dec = TRUE;
1521                 parsed = FALSE;
1522         } else {
1523                 status = CR_PARSING_ERROR;
1524                 goto error;
1525         }
1526         cr_tknzr_get_parsing_location (a_this, &location) ;
1527 
1528         for (;;) {
1529                 status = cr_tknzr_peek_char (a_this, &next_char);
1530                 if (status != CR_OK) {
1531                         if (status == CR_END_OF_INPUT_ERROR)
1532                                 status = CR_OK;
1533                         break;
1534                 }
1535                 if (next_char == '.') {
1536                         if (parsing_dec) {
1537                                 status = CR_PARSING_ERROR;
1538                                 goto error;
1539                         }
1540 
1541                         READ_NEXT_CHAR (a_this, &cur_char);
1542                         parsing_dec = TRUE;
1543                         parsed = FALSE;  /* In CSS, there must be at least
1544                                             one digit after `.'. */
1545                 } else if (IS_NUM (next_char)) {
1546                         READ_NEXT_CHAR (a_this, &cur_char);
1547                         parsed = TRUE;
1548 
1549                         numerator = numerator * 10 + (cur_char - '0');
1550                         if (parsing_dec) {
1551                                 denominator *= 10;
1552                         }
1553                 } else {
1554                         break;
1555                 }
1556         }
1557 
1558         if (!parsed) {
1559                 status = CR_PARSING_ERROR;
1560         }
1561 
1562         /*
1563          *Now, set the output param values.
1564          */
1565         if (status == CR_OK) {
1566                 gdouble val = (numerator / denominator) * sign;
1567                 if (*a_num == NULL) {
1568                         *a_num = cr_num_new_with_val (val, val_type);
1569 
1570                         if (*a_num == NULL) {
1571                                 status = CR_ERROR;
1572                                 goto error;
1573                         }
1574                 } else {
1575                         (*a_num)->val = val;
1576                         (*a_num)->type = val_type;
1577                 }
1578                 cr_parsing_location_copy (&(*a_num)->location,
1579                                           &location) ;
1580                 return CR_OK;
1581         }
1582 
1583  error:
1584 
1585         cr_tknzr_set_cur_pos (a_this, &init_pos);
1586 
1587         return status;
1588 }
1589 
1590 /*********************************************
1591  *PUBLIC methods
1592  ********************************************/
1593 
1594 CRTknzr *
cr_tknzr_new(CRInput * a_input)1595 cr_tknzr_new (CRInput * a_input)
1596 {
1597         CRTknzr *result = NULL;
1598 
1599         result = g_try_malloc (sizeof (CRTknzr));
1600 
1601         if (result == NULL) {
1602                 cr_utils_trace_info ("Out of memory");
1603                 return NULL;
1604         }
1605 
1606         memset (result, 0, sizeof (CRTknzr));
1607 
1608         result->priv = g_try_malloc (sizeof (CRTknzrPriv));
1609 
1610         if (result->priv == NULL) {
1611                 cr_utils_trace_info ("Out of memory");
1612 
1613                 if (result) {
1614                         g_free (result);
1615                         result = NULL;
1616                 }
1617 
1618                 return NULL;
1619         }
1620         memset (result->priv, 0, sizeof (CRTknzrPriv));
1621         if (a_input)
1622                 cr_tknzr_set_input (result, a_input);
1623         return result;
1624 }
1625 
1626 CRTknzr *
cr_tknzr_new_from_buf(guchar * a_buf,gulong a_len,enum CREncoding a_enc,gboolean a_free_at_destroy)1627 cr_tknzr_new_from_buf (guchar * a_buf, gulong a_len,
1628                        enum CREncoding a_enc,
1629                        gboolean a_free_at_destroy)
1630 {
1631         CRTknzr *result = NULL;
1632         CRInput *input = NULL;
1633 
1634         input = cr_input_new_from_buf (a_buf, a_len, a_enc,
1635                                        a_free_at_destroy);
1636 
1637         g_return_val_if_fail (input != NULL, NULL);
1638 
1639         result = cr_tknzr_new (input);
1640 
1641         return result;
1642 }
1643 
1644 CRTknzr *
cr_tknzr_new_from_uri(const guchar * a_file_uri,enum CREncoding a_enc)1645 cr_tknzr_new_from_uri (const guchar * a_file_uri,
1646                        enum CREncoding a_enc)
1647 {
1648         CRTknzr *result = NULL;
1649         CRInput *input = NULL;
1650 
1651         input = cr_input_new_from_uri ((const gchar *) a_file_uri, a_enc);
1652         g_return_val_if_fail (input != NULL, NULL);
1653 
1654         result = cr_tknzr_new (input);
1655 
1656         return result;
1657 }
1658 
1659 void
cr_tknzr_ref(CRTknzr * a_this)1660 cr_tknzr_ref (CRTknzr * a_this)
1661 {
1662         g_return_if_fail (a_this && PRIVATE (a_this));
1663 
1664         PRIVATE (a_this)->ref_count++;
1665 }
1666 
1667 gboolean
cr_tknzr_unref(CRTknzr * a_this)1668 cr_tknzr_unref (CRTknzr * a_this)
1669 {
1670         g_return_val_if_fail (a_this && PRIVATE (a_this), FALSE);
1671 
1672         if (PRIVATE (a_this)->ref_count > 0) {
1673                 PRIVATE (a_this)->ref_count--;
1674         }
1675 
1676         if (PRIVATE (a_this)->ref_count == 0) {
1677                 cr_tknzr_destroy (a_this);
1678                 return TRUE;
1679         }
1680 
1681         return FALSE;
1682 }
1683 
1684 enum CRStatus
cr_tknzr_set_input(CRTknzr * a_this,CRInput * a_input)1685 cr_tknzr_set_input (CRTknzr * a_this, CRInput * a_input)
1686 {
1687         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1688 
1689         if (PRIVATE (a_this)->input) {
1690                 cr_input_unref (PRIVATE (a_this)->input);
1691         }
1692 
1693         PRIVATE (a_this)->input = a_input;
1694 
1695         cr_input_ref (PRIVATE (a_this)->input);
1696 
1697         return CR_OK;
1698 }
1699 
1700 enum CRStatus
cr_tknzr_get_input(CRTknzr * a_this,CRInput ** a_input)1701 cr_tknzr_get_input (CRTknzr * a_this, CRInput ** a_input)
1702 {
1703         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1704 
1705         *a_input = PRIVATE (a_this)->input;
1706 
1707         return CR_OK;
1708 }
1709 
1710 /*********************************
1711  *Tokenizer input handling routines
1712  *********************************/
1713 
1714 /**
1715  *Reads the next byte from the parser input stream.
1716  *@param a_this the "this pointer" of the current instance of
1717  *#CRParser.
1718  *@param a_byte out parameter the place where to store the byte
1719  *read.
1720  *@return CR_OK upon successfull completion, an error
1721  *code otherwise.
1722  */
1723 enum CRStatus
cr_tknzr_read_byte(CRTknzr * a_this,guchar * a_byte)1724 cr_tknzr_read_byte (CRTknzr * a_this, guchar * a_byte)
1725 {
1726         g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
1727 
1728         return cr_input_read_byte (PRIVATE (a_this)->input, a_byte);
1729 
1730 }
1731 
1732 /**
1733  *Reads the next char from the parser input stream.
1734  *@param a_this the current instance of #CRTknzr.
1735  *@param a_char out parameter. The read char.
1736  *@return CR_OK upon successfull completion, an error code
1737  *otherwise.
1738  */
1739 enum CRStatus
cr_tknzr_read_char(CRTknzr * a_this,guint32 * a_char)1740 cr_tknzr_read_char (CRTknzr * a_this, guint32 * a_char)
1741 {
1742         g_return_val_if_fail (a_this && PRIVATE (a_this)
1743                               && PRIVATE (a_this)->input
1744                               && a_char, CR_BAD_PARAM_ERROR);
1745 
1746         if (PRIVATE (a_this)->token_cache) {
1747                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
1748                                       &PRIVATE (a_this)->prev_pos);
1749                 cr_token_destroy (PRIVATE (a_this)->token_cache);
1750                 PRIVATE (a_this)->token_cache = NULL;
1751         }
1752 
1753         return cr_input_read_char (PRIVATE (a_this)->input, a_char);
1754 }
1755 
1756 /**
1757  *Peeks a char from the parser input stream.
1758  *To "peek a char" means reads the next char without consuming it.
1759  *Subsequent calls to this function return the same char.
1760  *@param a_this the current instance of #CRTknzr.
1761  *@param a_char out parameter. The peeked char uppon successfull completion.
1762  *@return CR_OK upon successfull completion, an error code otherwise.
1763  */
1764 enum CRStatus
cr_tknzr_peek_char(CRTknzr * a_this,guint32 * a_char)1765 cr_tknzr_peek_char (CRTknzr * a_this, guint32 * a_char)
1766 {
1767         g_return_val_if_fail (a_this && PRIVATE (a_this)
1768                               && PRIVATE (a_this)->input
1769                               && a_char, CR_BAD_PARAM_ERROR);
1770 
1771         if (PRIVATE (a_this)->token_cache) {
1772                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
1773                                       &PRIVATE (a_this)->prev_pos);
1774                 cr_token_destroy (PRIVATE (a_this)->token_cache);
1775                 PRIVATE (a_this)->token_cache = NULL;
1776         }
1777 
1778         return cr_input_peek_char (PRIVATE (a_this)->input, a_char);
1779 }
1780 
1781 /**
1782  *Peeks a byte ahead at a given postion in the parser input stream.
1783  *@param a_this the current instance of #CRTknzr.
1784  *@param a_offset the offset of the peeked byte starting from the current
1785  *byte in the parser input stream.
1786  *@param a_byte out parameter. The peeked byte upon
1787  *successfull completion.
1788  *@return CR_OK upon successfull completion, an error code otherwise.
1789  */
1790 enum CRStatus
cr_tknzr_peek_byte(CRTknzr * a_this,gulong a_offset,guchar * a_byte)1791 cr_tknzr_peek_byte (CRTknzr * a_this, gulong a_offset, guchar * a_byte)
1792 {
1793         g_return_val_if_fail (a_this && PRIVATE (a_this)
1794                               && PRIVATE (a_this)->input && a_byte,
1795                               CR_BAD_PARAM_ERROR);
1796 
1797         if (PRIVATE (a_this)->token_cache) {
1798                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
1799                                       &PRIVATE (a_this)->prev_pos);
1800                 cr_token_destroy (PRIVATE (a_this)->token_cache);
1801                 PRIVATE (a_this)->token_cache = NULL;
1802         }
1803 
1804         return cr_input_peek_byte (PRIVATE (a_this)->input,
1805                                    CR_SEEK_CUR, a_offset, a_byte);
1806 }
1807 
1808 /**
1809  *Same as cr_tknzr_peek_byte() but this api returns the byte peeked.
1810  *@param a_this the current instance of #CRTknzr.
1811  *@param a_offset the offset of the peeked byte starting from the current
1812  *byte in the parser input stream.
1813  *@param a_eof out parameter. If not NULL, is set to TRUE if we reached end of
1814  *file, FALE otherwise. If the caller sets it to NULL, this parameter
1815  *is just ignored.
1816  *@return the peeked byte.
1817  */
1818 guchar
cr_tknzr_peek_byte2(CRTknzr * a_this,gulong a_offset,gboolean * a_eof)1819 cr_tknzr_peek_byte2 (CRTknzr * a_this, gulong a_offset, gboolean * a_eof)
1820 {
1821         g_return_val_if_fail (a_this && PRIVATE (a_this)
1822                               && PRIVATE (a_this)->input, 0);
1823 
1824         return cr_input_peek_byte2 (PRIVATE (a_this)->input, a_offset, a_eof);
1825 }
1826 
1827 /**
1828  *Gets the number of bytes left in the topmost input stream
1829  *associated to this parser.
1830  *@param a_this the current instance of #CRTknzr
1831  *@return the number of bytes left or -1 in case of error.
1832  */
1833 glong
cr_tknzr_get_nb_bytes_left(CRTknzr * a_this)1834 cr_tknzr_get_nb_bytes_left (CRTknzr * a_this)
1835 {
1836         g_return_val_if_fail (a_this && PRIVATE (a_this)
1837                               && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
1838 
1839         if (PRIVATE (a_this)->token_cache) {
1840                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
1841                                       &PRIVATE (a_this)->prev_pos);
1842                 cr_token_destroy (PRIVATE (a_this)->token_cache);
1843                 PRIVATE (a_this)->token_cache = NULL;
1844         }
1845 
1846         return cr_input_get_nb_bytes_left (PRIVATE (a_this)->input);
1847 }
1848 
1849 enum CRStatus
cr_tknzr_get_cur_pos(CRTknzr * a_this,CRInputPos * a_pos)1850 cr_tknzr_get_cur_pos (CRTknzr * a_this, CRInputPos * a_pos)
1851 {
1852         g_return_val_if_fail (a_this && PRIVATE (a_this)
1853                               && PRIVATE (a_this)->input
1854                               && a_pos, CR_BAD_PARAM_ERROR);
1855 
1856         if (PRIVATE (a_this)->token_cache) {
1857                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
1858                                       &PRIVATE (a_this)->prev_pos);
1859                 cr_token_destroy (PRIVATE (a_this)->token_cache);
1860                 PRIVATE (a_this)->token_cache = NULL;
1861         }
1862 
1863         return cr_input_get_cur_pos (PRIVATE (a_this)->input, a_pos);
1864 }
1865 
1866 enum CRStatus
cr_tknzr_get_parsing_location(CRTknzr * a_this,CRParsingLocation * a_loc)1867 cr_tknzr_get_parsing_location (CRTknzr *a_this,
1868                                CRParsingLocation *a_loc)
1869 {
1870         g_return_val_if_fail (a_this
1871                               && PRIVATE (a_this)
1872                               && a_loc,
1873                               CR_BAD_PARAM_ERROR) ;
1874 
1875         return cr_input_get_parsing_location
1876                 (PRIVATE (a_this)->input, a_loc) ;
1877 }
1878 
1879 enum CRStatus
cr_tknzr_get_cur_byte_addr(CRTknzr * a_this,guchar ** a_addr)1880 cr_tknzr_get_cur_byte_addr (CRTknzr * a_this, guchar ** a_addr)
1881 {
1882         g_return_val_if_fail (a_this && PRIVATE (a_this)
1883                               && 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         }
1890 
1891         return cr_input_get_cur_byte_addr (PRIVATE (a_this)->input, a_addr);
1892 }
1893 
1894 enum CRStatus
cr_tknzr_seek_index(CRTknzr * a_this,enum CRSeekPos a_origin,gint a_pos)1895 cr_tknzr_seek_index (CRTknzr * a_this, enum CRSeekPos a_origin, gint a_pos)
1896 {
1897         g_return_val_if_fail (a_this && PRIVATE (a_this)
1898                               && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
1899 
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         }
1906 
1907         return cr_input_seek_index (PRIVATE (a_this)->input, a_origin, a_pos);
1908 }
1909 
1910 enum CRStatus
cr_tknzr_consume_chars(CRTknzr * a_this,guint32 a_char,glong * a_nb_char)1911 cr_tknzr_consume_chars (CRTknzr * a_this, guint32 a_char, glong * a_nb_char)
1912 {
1913 	gulong consumed = *(gulong *) a_nb_char;
1914 	enum CRStatus status;
1915         g_return_val_if_fail (a_this && PRIVATE (a_this)
1916                               && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
1917 
1918         if (PRIVATE (a_this)->token_cache) {
1919                 cr_input_set_cur_pos (PRIVATE (a_this)->input,
1920                                       &PRIVATE (a_this)->prev_pos);
1921                 cr_token_destroy (PRIVATE (a_this)->token_cache);
1922                 PRIVATE (a_this)->token_cache = NULL;
1923         }
1924 
1925         status = cr_input_consume_chars (PRIVATE (a_this)->input,
1926                                          a_char, &consumed);
1927 	*a_nb_char = (glong) consumed;
1928 	return status;
1929 }
1930 
1931 enum CRStatus
cr_tknzr_set_cur_pos(CRTknzr * a_this,CRInputPos * a_pos)1932 cr_tknzr_set_cur_pos (CRTknzr * a_this, CRInputPos * a_pos)
1933 {
1934         g_return_val_if_fail (a_this && PRIVATE (a_this)
1935                               && PRIVATE (a_this)->input, CR_BAD_PARAM_ERROR);
1936 
1937         if (PRIVATE (a_this)->token_cache) {
1938                 cr_token_destroy (PRIVATE (a_this)->token_cache);
1939                 PRIVATE (a_this)->token_cache = NULL;
1940         }
1941 
1942         return cr_input_set_cur_pos (PRIVATE (a_this)->input, a_pos);
1943 }
1944 
1945 enum CRStatus
cr_tknzr_unget_token(CRTknzr * a_this,CRToken * a_token)1946 cr_tknzr_unget_token (CRTknzr * a_this, CRToken * a_token)
1947 {
1948         g_return_val_if_fail (a_this && PRIVATE (a_this)
1949                               && PRIVATE (a_this)->token_cache == NULL,
1950                               CR_BAD_PARAM_ERROR);
1951 
1952         PRIVATE (a_this)->token_cache = a_token;
1953 
1954         return CR_OK;
1955 }
1956 
1957 /**
1958  *Returns the next token of the input stream.
1959  *This method is really central. Each parsing
1960  *method calls it.
1961  *@param a_this the current tokenizer.
1962  *@param a_tk out parameter. The returned token.
1963  *for the sake of mem leak avoidance, *a_tk must
1964  *be NULL.
1965  *@param CR_OK upon successfull completion, an error code
1966  *otherwise.
1967  */
1968 enum CRStatus
cr_tknzr_get_next_token(CRTknzr * a_this,CRToken ** a_tk)1969 cr_tknzr_get_next_token (CRTknzr * a_this, CRToken ** a_tk)
1970 {
1971         enum CRStatus status = CR_OK;
1972         CRToken *token = NULL;
1973         CRInputPos init_pos;
1974         guint32 next_char = 0;
1975         guchar next_bytes[4] = { 0 };
1976         gboolean reached_eof = FALSE;
1977         CRInput *input = NULL;
1978         CRString *str = NULL;
1979         CRRgb *rgb = NULL;
1980         CRParsingLocation location = {0} ;
1981 
1982         g_return_val_if_fail (a_this && PRIVATE (a_this)
1983                               && a_tk && *a_tk == NULL
1984                               && PRIVATE (a_this)->input,
1985                               CR_BAD_PARAM_ERROR);
1986 
1987         if (PRIVATE (a_this)->token_cache) {
1988                 *a_tk = PRIVATE (a_this)->token_cache;
1989                 PRIVATE (a_this)->token_cache = NULL;
1990                 return CR_OK;
1991         }
1992 
1993         RECORD_INITIAL_POS (a_this, &init_pos);
1994 
1995         status = cr_input_get_end_of_file
1996                 (PRIVATE (a_this)->input, &reached_eof);
1997         ENSURE_PARSING_COND (status == CR_OK);
1998 
1999         if (reached_eof == TRUE) {
2000                 status = CR_END_OF_INPUT_ERROR;
2001                 goto error;
2002         }
2003 
2004         input = PRIVATE (a_this)->input;
2005 
2006         PEEK_NEXT_CHAR (a_this, &next_char);
2007         token = cr_token_new ();
2008         ENSURE_PARSING_COND (token);
2009 
2010         switch (next_char) {
2011         case '@':
2012                 {
2013                         if (BYTE (input, 2, NULL) == 'f'
2014                             && BYTE (input, 3, NULL) == 'o'
2015                             && BYTE (input, 4, NULL) == 'n'
2016                             && BYTE (input, 5, NULL) == 't'
2017                             && BYTE (input, 6, NULL) == '-'
2018                             && BYTE (input, 7, NULL) == 'f'
2019                             && BYTE (input, 8, NULL) == 'a'
2020                             && BYTE (input, 9, NULL) == 'c'
2021                             && BYTE (input, 10, NULL) == 'e') {
2022                                 SKIP_CHARS (a_this, 1);
2023                                 cr_tknzr_get_parsing_location
2024                                         (a_this, &location) ;
2025                                 SKIP_CHARS (a_this, 9);
2026                                 status = cr_token_set_font_face_sym (token);
2027                                 CHECK_PARSING_STATUS (status, TRUE);
2028                                 cr_parsing_location_copy (&token->location,
2029                                                           &location) ;
2030                                 goto done;
2031                         }
2032 
2033                         if (BYTE (input, 2, NULL) == 'c'
2034                             && BYTE (input, 3, NULL) == 'h'
2035                             && BYTE (input, 4, NULL) == 'a'
2036                             && BYTE (input, 5, NULL) == 'r'
2037                             && BYTE (input, 6, NULL) == 's'
2038                             && BYTE (input, 7, NULL) == 'e'
2039                             && BYTE (input, 8, NULL) == 't') {
2040                                 SKIP_CHARS (a_this, 1);
2041                                 cr_tknzr_get_parsing_location
2042                                         (a_this, &location) ;
2043                                 SKIP_CHARS (a_this, 7);
2044                                 status = cr_token_set_charset_sym (token);
2045                                 CHECK_PARSING_STATUS (status, TRUE);
2046                                 cr_parsing_location_copy (&token->location,
2047                                                           &location) ;
2048                                 goto done;
2049                         }
2050 
2051                         if (BYTE (input, 2, NULL) == 'i'
2052                             && BYTE (input, 3, NULL) == 'm'
2053                             && BYTE (input, 4, NULL) == 'p'
2054                             && BYTE (input, 5, NULL) == 'o'
2055                             && BYTE (input, 6, NULL) == 'r'
2056                             && BYTE (input, 7, NULL) == 't') {
2057                                 SKIP_CHARS (a_this, 1);
2058                                 cr_tknzr_get_parsing_location
2059                                         (a_this, &location) ;
2060                                 SKIP_CHARS (a_this, 6);
2061                                 status = cr_token_set_import_sym (token);
2062                                 CHECK_PARSING_STATUS (status, TRUE);
2063                                 cr_parsing_location_copy (&token->location,
2064                                                           &location) ;
2065                                 goto done;
2066                         }
2067 
2068                         if (BYTE (input, 2, NULL) == 'm'
2069                             && BYTE (input, 3, NULL) == 'e'
2070                             && BYTE (input, 4, NULL) == 'd'
2071                             && BYTE (input, 5, NULL) == 'i'
2072                             && BYTE (input, 6, NULL) == 'a') {
2073                                 SKIP_CHARS (a_this, 1);
2074                                 cr_tknzr_get_parsing_location (a_this,
2075                                                                &location) ;
2076                                 SKIP_CHARS (a_this, 5);
2077                                 status = cr_token_set_media_sym (token);
2078                                 CHECK_PARSING_STATUS (status, TRUE);
2079                                 cr_parsing_location_copy (&token->location,
2080                                                           &location) ;
2081                                 goto done;
2082                         }
2083 
2084                         if (BYTE (input, 2, NULL) == 'p'
2085                             && BYTE (input, 3, NULL) == 'a'
2086                             && BYTE (input, 4, NULL) == 'g'
2087                             && BYTE (input, 5, NULL) == 'e') {
2088                                 SKIP_CHARS (a_this, 1);
2089                                 cr_tknzr_get_parsing_location (a_this,
2090                                                                &location) ;
2091                                 SKIP_CHARS (a_this, 4);
2092                                 status = cr_token_set_page_sym (token);
2093                                 CHECK_PARSING_STATUS (status, TRUE);
2094                                 cr_parsing_location_copy (&token->location,
2095                                                           &location) ;
2096                                 goto done;
2097                         }
2098                         status = cr_tknzr_parse_atkeyword (a_this, &str);
2099                         if (status == CR_OK) {
2100                                 status = cr_token_set_atkeyword (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                 }
2109                 break;
2110 
2111         case 'u':
2112 
2113                 if (BYTE (input, 2, NULL) == 'r'
2114                     && BYTE (input, 3, NULL) == 'l'
2115                     && BYTE (input, 4, NULL) == '(') {
2116                         CRString *str2 = NULL;
2117 
2118                         status = cr_tknzr_parse_uri (a_this, &str2);
2119                         if (status == CR_OK) {
2120                                 status = cr_token_set_uri (token, str2);
2121                                 CHECK_PARSING_STATUS (status, TRUE);
2122                                 if (str2) {
2123                                         cr_parsing_location_copy (&token->location,
2124                                                                   &str2->location) ;
2125                                 }
2126                                 goto done;
2127                         }
2128                 }
2129                 goto fallback;
2130                 break;
2131 
2132         case 'r':
2133                 if (BYTE (input, 2, NULL) == 'g'
2134                     && BYTE (input, 3, NULL) == 'b'
2135                     && BYTE (input, 4, NULL) == '(') {
2136                         status = cr_tknzr_parse_rgb (a_this, &rgb);
2137                         if (status == CR_OK && rgb) {
2138                                 status = cr_token_set_rgb (token, rgb);
2139                                 CHECK_PARSING_STATUS (status, TRUE);
2140                                 if (rgb) {
2141                                         cr_parsing_location_copy (&token->location,
2142                                                                   &rgb->location) ;
2143                                 }
2144                                 rgb = NULL;
2145                                 goto done;
2146                         }
2147 
2148                 }
2149                 goto fallback;
2150                 break;
2151 
2152         case '<':
2153                 if (BYTE (input, 2, NULL) == '!'
2154                     && BYTE (input, 3, NULL) == '-'
2155                     && BYTE (input, 4, NULL) == '-') {
2156                         SKIP_CHARS (a_this, 1);
2157                         cr_tknzr_get_parsing_location (a_this,
2158                                                        &location) ;
2159                         SKIP_CHARS (a_this, 3);
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;
2167 
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                         } else {
2192                                 goto parse_number;
2193                         }
2194                 }
2195                 break;
2196 
2197         case '~':
2198                 if (BYTE (input, 2, NULL) == '=') {
2199                         SKIP_CHARS (a_this, 1);
2200                         cr_tknzr_get_parsing_location (a_this,
2201                                                        &location) ;
2202                         SKIP_CHARS (a_this, 1);
2203                         status = cr_token_set_includes (token);
2204                         CHECK_PARSING_STATUS (status, TRUE);
2205                         cr_parsing_location_copy (&token->location,
2206                                                   &location) ;
2207                         goto done;
2208                 }
2209                 break;
2210 
2211         case '|':
2212                 if (BYTE (input, 2, NULL) == '=') {
2213                         SKIP_CHARS (a_this, 1);
2214                         cr_tknzr_get_parsing_location (a_this,
2215                                                        &location) ;
2216                         SKIP_CHARS (a_this, 1);
2217                         status = cr_token_set_dashmatch (token);
2218                         CHECK_PARSING_STATUS (status, TRUE);
2219                         cr_parsing_location_copy (&token->location,
2220                                                   &location) ;
2221                         goto done;
2222                 }
2223                 break;
2224 
2225         case '/':
2226                 if (BYTE (input, 2, NULL) == '*') {
2227                         status = cr_tknzr_parse_comment (a_this, &str);
2228 
2229                         if (status == CR_OK) {
2230                                 status = cr_token_set_comment (token, str);
2231                                 str = NULL;
2232                                 CHECK_PARSING_STATUS (status, TRUE);
2233                                 if (str) {
2234                                         cr_parsing_location_copy (&token->location,
2235                                                                   &str->location) ;
2236                                 }
2237                                 goto done;
2238                         }
2239                 }
2240                 break ;
2241 
2242         case ';':
2243                 SKIP_CHARS (a_this, 1);
2244                 cr_tknzr_get_parsing_location (a_this,
2245                                                &location) ;
2246                 status = cr_token_set_semicolon (token);
2247                 CHECK_PARSING_STATUS (status, TRUE);
2248                 cr_parsing_location_copy (&token->location,
2249                                           &location) ;
2250                 goto done;
2251 
2252         case '{':
2253                 SKIP_CHARS (a_this, 1);
2254                 cr_tknzr_get_parsing_location (a_this,
2255                                                &location) ;
2256                 status = cr_token_set_cbo (token);
2257                 CHECK_PARSING_STATUS (status, TRUE);
2258                 cr_tknzr_get_parsing_location (a_this,
2259                                                &location) ;
2260                 goto done;
2261 
2262         case '}':
2263                 SKIP_CHARS (a_this, 1);
2264                 cr_tknzr_get_parsing_location (a_this,
2265                                                &location) ;
2266                 status = cr_token_set_cbc (token);
2267                 CHECK_PARSING_STATUS (status, TRUE);
2268                 cr_parsing_location_copy (&token->location,
2269                                           &location) ;
2270                 goto done;
2271 
2272         case '(':
2273                 SKIP_CHARS (a_this, 1);
2274                 cr_tknzr_get_parsing_location (a_this,
2275                                                &location) ;
2276                 status = cr_token_set_po (token);
2277                 CHECK_PARSING_STATUS (status, TRUE);
2278                 cr_parsing_location_copy (&token->location,
2279                                           &location) ;
2280                 goto done;
2281 
2282         case ')':
2283                 SKIP_CHARS (a_this, 1);
2284                 cr_tknzr_get_parsing_location (a_this,
2285                                                &location) ;
2286                 status = cr_token_set_pc (token);
2287                 CHECK_PARSING_STATUS (status, TRUE);
2288                 cr_parsing_location_copy (&token->location,
2289                                           &location) ;
2290                 goto done;
2291 
2292         case '[':
2293                 SKIP_CHARS (a_this, 1);
2294                 cr_tknzr_get_parsing_location (a_this,
2295                                                &location) ;
2296                 status = cr_token_set_bo (token);
2297                 CHECK_PARSING_STATUS (status, TRUE);
2298                 cr_parsing_location_copy (&token->location,
2299                                           &location) ;
2300                 goto done;
2301 
2302         case ']':
2303                 SKIP_CHARS (a_this, 1);
2304                 cr_tknzr_get_parsing_location (a_this,
2305                                                &location) ;
2306                 status = cr_token_set_bc (token);
2307                 CHECK_PARSING_STATUS (status, TRUE);
2308                 cr_parsing_location_copy (&token->location,
2309                                           &location) ;
2310                 goto done;
2311 
2312         case ' ':
2313         case '\t':
2314         case '\n':
2315         case '\f':
2316         case '\r':
2317                 {
2318                         guchar *start = NULL,
2319                                 *end = NULL;
2320 
2321                         status = cr_tknzr_parse_w (a_this, &start,
2322                                                    &end, &location);
2323                         if (status == CR_OK) {
2324                                 status = cr_token_set_s (token);
2325                                 CHECK_PARSING_STATUS (status, TRUE);
2326                                 cr_tknzr_get_parsing_location (a_this,
2327                                                                &location) ;
2328                                 goto done;
2329                         }
2330                 }
2331                 break;
2332 
2333         case '#':
2334                 {
2335                         status = cr_tknzr_parse_hash (a_this, &str);
2336                         if (status == CR_OK && str) {
2337                                 status = cr_token_set_hash (token, str);
2338                                 CHECK_PARSING_STATUS (status, TRUE);
2339                                 if (str) {
2340                                         cr_parsing_location_copy (&token->location,
2341                                                                   &str->location) ;
2342                                 }
2343                                 str = NULL;
2344                                 goto done;
2345                         }
2346                 }
2347                 break;
2348 
2349         case '\'':
2350         case '"':
2351                 status = cr_tknzr_parse_string (a_this, &str);
2352                 if (status == CR_OK && str) {
2353                         status = cr_token_set_string (token, str);
2354                         CHECK_PARSING_STATUS (status, TRUE);
2355                         if (str) {
2356                                 cr_parsing_location_copy (&token->location,
2357                                                           &str->location) ;
2358                         }
2359                         str = NULL;
2360                         goto done;
2361                 }
2362                 break;
2363 
2364         case '!':
2365                 status = cr_tknzr_parse_important (a_this, &location);
2366                 if (status == CR_OK) {
2367                         status = cr_token_set_important_sym (token);
2368                         CHECK_PARSING_STATUS (status, TRUE);
2369                         cr_parsing_location_copy (&token->location,
2370                                                   &location) ;
2371                         goto done;
2372                 }
2373                 break;
2374 
2375         case '0':
2376         case '1':
2377         case '2':
2378         case '3':
2379         case '4':
2380         case '5':
2381         case '6':
2382         case '7':
2383         case '8':
2384         case '9':
2385         case '.':
2386         case '+':
2387         /* '-' case is handled separately above for --> comments */
2388         parse_number:
2389                 {
2390                         CRNum *num = NULL;
2391 
2392                         status = cr_tknzr_parse_num (a_this, &num);
2393                         if (status == CR_OK && num) {
2394                                 next_bytes[0] = BYTE (input, 1, NULL);
2395                                 next_bytes[1] = BYTE (input, 2, NULL);
2396                                 next_bytes[2] = BYTE (input, 3, NULL);
2397                                 next_bytes[3] = BYTE (input, 4, NULL);
2398 
2399                                 if (next_bytes[0] == 'e'
2400                                     && next_bytes[1] == 'm') {
2401                                         num->type = NUM_LENGTH_EM;
2402                                         status = cr_token_set_ems (token,
2403                                                                    num);
2404                                         num = NULL;
2405                                         SKIP_CHARS (a_this, 2);
2406                                 } else if (next_bytes[0] == 'e'
2407                                            && next_bytes[1] == 'x') {
2408                                         num->type = NUM_LENGTH_EX;
2409                                         status = cr_token_set_exs (token,
2410                                                                    num);
2411                                         num = NULL;
2412                                         SKIP_CHARS (a_this, 2);
2413                                 } else if (next_bytes[0] == 'p'
2414                                            && next_bytes[1] == 'x') {
2415                                         num->type = NUM_LENGTH_PX;
2416                                         status = cr_token_set_length
2417                                                 (token, num, LENGTH_PX_ET);
2418                                         num = NULL;
2419                                         SKIP_CHARS (a_this, 2);
2420                                 } else if (next_bytes[0] == 'c'
2421                                            && next_bytes[1] == 'm') {
2422                                         num->type = NUM_LENGTH_CM;
2423                                         status = cr_token_set_length
2424                                                 (token, num, LENGTH_CM_ET);
2425                                         num = NULL;
2426                                         SKIP_CHARS (a_this, 2);
2427                                 } else if (next_bytes[0] == 'm'
2428                                            && next_bytes[1] == 'm') {
2429                                         num->type = NUM_LENGTH_MM;
2430                                         status = cr_token_set_length
2431                                                 (token, num, LENGTH_MM_ET);
2432                                         num = NULL;
2433                                         SKIP_CHARS (a_this, 2);
2434                                 } else if (next_bytes[0] == 'i'
2435                                            && next_bytes[1] == 'n') {
2436                                         num->type = NUM_LENGTH_IN;
2437                                         status = cr_token_set_length
2438                                                 (token, num, LENGTH_IN_ET);
2439                                         num = NULL;
2440                                         SKIP_CHARS (a_this, 2);
2441                                 } else if (next_bytes[0] == 'p'
2442                                            && next_bytes[1] == 't') {
2443                                         num->type = NUM_LENGTH_PT;
2444                                         status = cr_token_set_length
2445                                                 (token, num, LENGTH_PT_ET);
2446                                         num = NULL;
2447                                         SKIP_CHARS (a_this, 2);
2448                                 } else if (next_bytes[0] == 'p'
2449                                            && next_bytes[1] == 'c') {
2450                                         num->type = NUM_LENGTH_PC;
2451                                         status = cr_token_set_length
2452                                                 (token, num, LENGTH_PC_ET);
2453                                         num = NULL;
2454                                         SKIP_CHARS (a_this, 2);
2455                                 } else if (next_bytes[0] == 'd'
2456                                            && next_bytes[1] == 'e'
2457                                            && next_bytes[2] == 'g') {
2458                                         num->type = NUM_ANGLE_DEG;
2459                                         status = cr_token_set_angle
2460                                                 (token, num, ANGLE_DEG_ET);
2461                                         num = NULL;
2462                                         SKIP_CHARS (a_this, 3);
2463                                 } else if (next_bytes[0] == 'r'
2464                                            && next_bytes[1] == 'a'
2465                                            && next_bytes[2] == 'd') {
2466                                         num->type = NUM_ANGLE_RAD;
2467                                         status = cr_token_set_angle
2468                                                 (token, num, ANGLE_RAD_ET);
2469                                         num = NULL;
2470                                         SKIP_CHARS (a_this, 3);
2471                                 } else if (next_bytes[0] == 'g'
2472                                            && next_bytes[1] == 'r'
2473                                            && next_bytes[2] == 'a'
2474                                            && next_bytes[3] == 'd') {
2475                                         num->type = NUM_ANGLE_GRAD;
2476                                         status = cr_token_set_angle
2477                                                 (token, num, ANGLE_GRAD_ET);
2478                                         num = NULL;
2479                                         SKIP_CHARS (a_this, 4);
2480                                 } else if (next_bytes[0] == 'm'
2481                                            && next_bytes[1] == 's') {
2482                                         num->type = NUM_TIME_MS;
2483                                         status = cr_token_set_time
2484                                                 (token, num, TIME_MS_ET);
2485                                         num = NULL;
2486                                         SKIP_CHARS (a_this, 2);
2487                                 } else if (next_bytes[0] == 's') {
2488                                         num->type = NUM_TIME_S;
2489                                         status = cr_token_set_time
2490                                                 (token, num, TIME_S_ET);
2491                                         num = NULL;
2492                                         SKIP_CHARS (a_this, 1);
2493                                 } else if (next_bytes[0] == 'H'
2494                                            && next_bytes[1] == 'z') {
2495                                         num->type = NUM_FREQ_HZ;
2496                                         status = cr_token_set_freq
2497                                                 (token, num, FREQ_HZ_ET);
2498                                         num = NULL;
2499                                         SKIP_CHARS (a_this, 2);
2500                                 } else if (next_bytes[0] == 'k'
2501                                            && next_bytes[1] == 'H'
2502                                            && next_bytes[2] == 'z') {
2503                                         num->type = NUM_FREQ_KHZ;
2504                                         status = cr_token_set_freq
2505                                                 (token, num, FREQ_KHZ_ET);
2506                                         num = NULL;
2507                                         SKIP_CHARS (a_this, 3);
2508                                 } else if (next_bytes[0] == '%') {
2509                                         num->type = NUM_PERCENTAGE;
2510                                         status = cr_token_set_percentage
2511                                                 (token, num);
2512                                         num = NULL;
2513                                         SKIP_CHARS (a_this, 1);
2514                                 } else {
2515                                         status = cr_tknzr_parse_ident (a_this,
2516                                                                        &str);
2517                                         if (status == CR_OK && str) {
2518                                                 num->type = NUM_UNKNOWN_TYPE;
2519                                                 status = cr_token_set_dimen
2520                                                         (token, num, str);
2521                                                 num = NULL;
2522                                                 CHECK_PARSING_STATUS (status,
2523                                                                       TRUE);
2524                                                 str = NULL;
2525                                         } else {
2526                                                 status = cr_token_set_number
2527                                                         (token, num);
2528                                                 num = NULL;
2529                                                 CHECK_PARSING_STATUS (status, CR_OK);
2530                                                 str = NULL;
2531                                         }
2532                                 }
2533                                 if (token && token->u.num) {
2534                                         cr_parsing_location_copy (&token->location,
2535                                                                   &token->u.num->location) ;
2536                                 } else {
2537                                         status = CR_ERROR ;
2538                                 }
2539                                 goto done ;
2540                         }
2541                 }
2542                 break;
2543 
2544         default:
2545         fallback:
2546                 /*process the fallback cases here */
2547 
2548                 if (next_char == '\\'
2549                     || (cr_utils_is_nonascii (next_bytes[0]) == TRUE)
2550                     || ((next_char >= 'a') && (next_char <= 'z'))
2551                     || ((next_char >= 'A') && (next_char <= 'Z'))) {
2552                         status = cr_tknzr_parse_ident (a_this, &str);
2553                         if (status == CR_OK && str) {
2554                                 guint32 next_c = 0;
2555 
2556                                 status = cr_input_peek_char
2557                                         (PRIVATE (a_this)->input, &next_c);
2558 
2559                                 if (status == CR_OK && next_c == '(') {
2560 
2561                                         SKIP_CHARS (a_this, 1);
2562                                         status = cr_token_set_function
2563                                                 (token, str);
2564                                         CHECK_PARSING_STATUS (status, TRUE);
2565                                         /*ownership is transfered
2566                                          *to token by cr_token_set_function.
2567                                          */
2568                                         if (str) {
2569                                                 cr_parsing_location_copy (&token->location,
2570                                                                           &str->location) ;
2571                                         }
2572                                         str = NULL;
2573                                 } else {
2574                                         status = cr_token_set_ident (token,
2575                                                                      str);
2576                                         CHECK_PARSING_STATUS (status, TRUE);
2577                                         if (str) {
2578                                                 cr_parsing_location_copy (&token->location,
2579                                                                           &str->location) ;
2580                                         }
2581                                         str = NULL;
2582                                 }
2583                                 goto done;
2584                         } else {
2585                                 if (str) {
2586                                         cr_string_destroy (str);
2587                                         str = NULL;
2588                                 }
2589                         }
2590                 }
2591                 break;
2592         }
2593 
2594         READ_NEXT_CHAR (a_this, &next_char);
2595         cr_tknzr_get_parsing_location (a_this,
2596                                        &location) ;
2597         status = cr_token_set_delim (token, next_char);
2598         CHECK_PARSING_STATUS (status, TRUE);
2599         cr_parsing_location_copy (&token->location,
2600                                   &location) ;
2601  done:
2602 
2603         if (status == CR_OK && token) {
2604                 *a_tk = token;
2605                 /*
2606                  *store the previous position input stream pos.
2607                  */
2608                 memmove (&PRIVATE (a_this)->prev_pos,
2609                          &init_pos, sizeof (CRInputPos));
2610                 return CR_OK;
2611         }
2612 
2613  error:
2614         if (token) {
2615                 cr_token_destroy (token);
2616                 token = NULL;
2617         }
2618 
2619         if (str) {
2620                 cr_string_destroy (str);
2621                 str = NULL;
2622         }
2623         cr_tknzr_set_cur_pos (a_this, &init_pos);
2624         return status;
2625 
2626 }
2627 
2628 enum CRStatus
cr_tknzr_parse_token(CRTknzr * a_this,enum CRTokenType a_type,enum CRTokenExtraType a_et,gpointer a_res,gpointer a_extra_res)2629 cr_tknzr_parse_token (CRTknzr * a_this, enum CRTokenType a_type,
2630                       enum CRTokenExtraType a_et, gpointer a_res,
2631                       gpointer a_extra_res)
2632 {
2633         enum CRStatus status = CR_OK;
2634         CRToken *token = NULL;
2635 
2636         g_return_val_if_fail (a_this && PRIVATE (a_this)
2637                               && PRIVATE (a_this)->input
2638                               && a_res, CR_BAD_PARAM_ERROR);
2639 
2640         status = cr_tknzr_get_next_token (a_this, &token);
2641         if (status != CR_OK)
2642                 return status;
2643         if (token == NULL)
2644                 return CR_PARSING_ERROR;
2645 
2646         if (token->type == a_type) {
2647                 switch (a_type) {
2648                 case NO_TK:
2649                 case S_TK:
2650                 case CDO_TK:
2651                 case CDC_TK:
2652                 case INCLUDES_TK:
2653                 case DASHMATCH_TK:
2654                 case IMPORT_SYM_TK:
2655                 case PAGE_SYM_TK:
2656                 case MEDIA_SYM_TK:
2657                 case FONT_FACE_SYM_TK:
2658                 case CHARSET_SYM_TK:
2659                 case IMPORTANT_SYM_TK:
2660                         status = CR_OK;
2661                         break;
2662 
2663                 case STRING_TK:
2664                 case IDENT_TK:
2665                 case HASH_TK:
2666                 case ATKEYWORD_TK:
2667                 case FUNCTION_TK:
2668                 case COMMENT_TK:
2669                 case URI_TK:
2670                         *((CRString **) a_res) = token->u.str;
2671                         token->u.str = NULL;
2672                         status = CR_OK;
2673                         break;
2674 
2675                 case EMS_TK:
2676                 case EXS_TK:
2677                 case PERCENTAGE_TK:
2678                 case NUMBER_TK:
2679                         *((CRNum **) a_res) = token->u.num;
2680                         token->u.num = NULL;
2681                         status = CR_OK;
2682                         break;
2683 
2684                 case LENGTH_TK:
2685                 case ANGLE_TK:
2686                 case TIME_TK:
2687                 case FREQ_TK:
2688                         if (token->extra_type == a_et) {
2689                                 *((CRNum **) a_res) = token->u.num;
2690                                 token->u.num = NULL;
2691                                 status = CR_OK;
2692                         }
2693                         break;
2694 
2695                 case DIMEN_TK:
2696                         *((CRNum **) a_res) = token->u.num;
2697                         if (a_extra_res == NULL) {
2698                                 status = CR_BAD_PARAM_ERROR;
2699                                 goto error;
2700                         }
2701 
2702                         *((CRString **) a_extra_res) = token->dimen;
2703                         token->u.num = NULL;
2704                         token->dimen = NULL;
2705                         status = CR_OK;
2706                         break;
2707 
2708                 case DELIM_TK:
2709                         *((guint32 *) a_res) = token->u.unichar;
2710                         status = CR_OK;
2711                         break;
2712 
2713                 case UNICODERANGE_TK:
2714                 default:
2715                         status = CR_PARSING_ERROR;
2716                         break;
2717                 }
2718 
2719                 cr_token_destroy (token);
2720                 token = NULL;
2721         } else {
2722                 cr_tknzr_unget_token (a_this, token);
2723                 token = NULL;
2724                 status = CR_PARSING_ERROR;
2725         }
2726 
2727         return status;
2728 
2729       error:
2730 
2731         if (token) {
2732                 cr_tknzr_unget_token (a_this, token);
2733                 token = NULL;
2734         }
2735 
2736         return status;
2737 }
2738 
2739 void
cr_tknzr_destroy(CRTknzr * a_this)2740 cr_tknzr_destroy (CRTknzr * a_this)
2741 {
2742         g_return_if_fail (a_this);
2743 
2744         if (PRIVATE (a_this) && PRIVATE (a_this)->input) {
2745                 if (cr_input_unref (PRIVATE (a_this)->input)
2746                     == TRUE) {
2747                         PRIVATE (a_this)->input = NULL;
2748                 }
2749         }
2750 
2751         if (PRIVATE (a_this)->token_cache) {
2752                 cr_token_destroy (PRIVATE (a_this)->token_cache);
2753                 PRIVATE (a_this)->token_cache = NULL;
2754         }
2755 
2756         if (PRIVATE (a_this)) {
2757                 g_free (PRIVATE (a_this));
2758                 PRIVATE (a_this) = NULL;
2759         }
2760 
2761         g_free (a_this);
2762 }
2763