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