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