1#include "mupdf/fitz.h"
2#include "html-imp.h"
3
4#include <string.h>
5
6struct lexbuf
7{
8 fz_context *ctx;
9 fz_pool *pool;
10 const unsigned char *s;
11 const char *file;
12 int line;
13 int lookahead;
14 int c;
15 int string_len;
16 char string[1024];
17};
18
19static fz_css_value *parse_expr(struct lexbuf *buf);
20static fz_css_selector *parse_selector(struct lexbuf *buf);
21
22FZ_NORETURN static void fz_css_error(struct lexbuf *buf, const char *msg)
23{
24 fz_throw(buf->ctx, FZ_ERROR_SYNTAX, "css syntax error: %s (%s:%d)", msg, buf->file, buf->line);
25}
26
27fz_css *fz_new_css(fz_context *ctx)
28{
29 fz_pool *pool = fz_new_pool(ctx);
30 fz_css *css = NULL;
31
32 fz_try(ctx)
33 {
34 css = fz_pool_alloc(ctx, pool, sizeof *css);
35 css->pool = pool;
36 css->rule = NULL;
37 }
38 fz_catch(ctx)
39 {
40 fz_drop_pool(ctx, pool);
41 fz_rethrow(ctx);
42 }
43
44 return css;
45}
46
47void fz_drop_css(fz_context *ctx, fz_css *css)
48{
49 if (css)
50 fz_drop_pool(ctx, css->pool);
51}
52
53static fz_css_rule *fz_new_css_rule(fz_context *ctx, fz_pool *pool, fz_css_selector *selector, fz_css_property *declaration)
54{
55 fz_css_rule *rule = fz_pool_alloc(ctx, pool, sizeof *rule);
56 rule->selector = selector;
57 rule->declaration = declaration;
58 rule->next = NULL;
59 return rule;
60}
61
62static fz_css_selector *fz_new_css_selector(fz_context *ctx, fz_pool *pool, const char *name)
63{
64 fz_css_selector *sel = fz_pool_alloc(ctx, pool, sizeof *sel);
65 sel->name = name ? fz_pool_strdup(ctx, pool, name) : NULL;
66 sel->combine = 0;
67 sel->cond = NULL;
68 sel->left = NULL;
69 sel->right = NULL;
70 sel->next = NULL;
71 return sel;
72}
73
74static fz_css_condition *fz_new_css_condition(fz_context *ctx, fz_pool *pool, int type, const char *key, const char *val)
75{
76 fz_css_condition *cond = fz_pool_alloc(ctx, pool, sizeof *cond);
77 cond->type = type;
78 cond->key = key ? fz_pool_strdup(ctx, pool, key) : NULL;
79 cond->val = val ? fz_pool_strdup(ctx, pool, val) : NULL;
80 cond->next = NULL;
81 return cond;
82}
83
84static fz_css_property *fz_new_css_property(fz_context *ctx, fz_pool *pool, const char *name, fz_css_value *value, int spec)
85{
86 fz_css_property *prop = fz_pool_alloc(ctx, pool, sizeof *prop);
87 prop->name = fz_pool_strdup(ctx, pool, name);
88 prop->value = value;
89 prop->spec = spec;
90 prop->important = 0;
91 prop->next = NULL;
92 return prop;
93}
94
95static fz_css_value *fz_new_css_value_x(fz_context *ctx, fz_pool *pool, int type)
96{
97 fz_css_value *val = fz_pool_alloc(ctx, pool, sizeof *val);
98 val->type = type;
99 val->data = NULL;
100 val->args = NULL;
101 val->next = NULL;
102 return val;
103}
104
105static fz_css_value *fz_new_css_value(fz_context *ctx, fz_pool *pool, int type, const char *data)
106{
107 fz_css_value *val = fz_pool_alloc(ctx, pool, sizeof *val);
108 val->type = type;
109 val->data = fz_pool_strdup(ctx, pool, data);
110 val->args = NULL;
111 val->next = NULL;
112 return val;
113}
114
115static void css_lex_next(struct lexbuf *buf)
116{
117 buf->c = *(buf->s++);
118 if (buf->c == '\n')
119 ++buf->line;
120}
121
122static void css_lex_init(fz_context *ctx, struct lexbuf *buf, fz_pool *pool, const char *s, const char *file)
123{
124 buf->ctx = ctx;
125 buf->pool = pool;
126 buf->s = (const unsigned char *)s;
127 buf->c = 0;
128 buf->file = file;
129 buf->line = 1;
130 css_lex_next(buf);
131
132 buf->string_len = 0;
133}
134
135static inline int iswhite(int c)
136{
137 return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f';
138}
139
140static int isnmstart(int c)
141{
142 return c == '\\' || c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
143 (c >= 128 && c <= 255);
144}
145
146static int isnmchar(int c)
147{
148 return c == '\\' || c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
149 (c >= '0' && c <= '9') || c == '-' || (c >= 128 && c <= 255);
150}
151
152static void css_push_char(struct lexbuf *buf, int c)
153{
154 if (buf->string_len + 1 >= nelem(buf->string))
155 fz_css_error(buf, "token too long");
156 buf->string[buf->string_len++] = c;
157}
158
159static int css_lex_accept(struct lexbuf *buf, int t)
160{
161 if (buf->c == t)
162 {
163 css_lex_next(buf);
164 return 1;
165 }
166 return 0;
167}
168
169static void css_lex_expect(struct lexbuf *buf, int t)
170{
171 if (!css_lex_accept(buf, t))
172 fz_css_error(buf, "unexpected character");
173}
174
175static int css_lex_number(struct lexbuf *buf)
176{
177 while (buf->c >= '0' && buf->c <= '9')
178 {
179 css_push_char(buf, buf->c);
180 css_lex_next(buf);
181 }
182
183 if (css_lex_accept(buf, '.'))
184 {
185 css_push_char(buf, '.');
186 while (buf->c >= '0' && buf->c <= '9')
187 {
188 css_push_char(buf, buf->c);
189 css_lex_next(buf);
190 }
191 }
192
193 if (css_lex_accept(buf, '%'))
194 {
195 css_push_char(buf, '%');
196 css_push_char(buf, 0);
197 return CSS_PERCENT;
198 }
199
200 if (isnmstart(buf->c))
201 {
202 css_push_char(buf, buf->c);
203 css_lex_next(buf);
204 while (isnmchar(buf->c))
205 {
206 css_push_char(buf, buf->c);
207 css_lex_next(buf);
208 }
209 css_push_char(buf, 0);
210 return CSS_LENGTH;
211 }
212
213 css_push_char(buf, 0);
214 return CSS_NUMBER;
215}
216
217static int css_lex_keyword(struct lexbuf *buf)
218{
219 while (isnmchar(buf->c))
220 {
221 css_push_char(buf, buf->c);
222 css_lex_next(buf);
223 }
224 css_push_char(buf, 0);
225 return CSS_KEYWORD;
226}
227
228static int css_lex_hash(struct lexbuf *buf)
229{
230 while (isnmchar(buf->c))
231 {
232 css_push_char(buf, buf->c);
233 css_lex_next(buf);
234 }
235 css_push_char(buf, 0);
236 return CSS_HASH;
237}
238
239static int css_lex_string(struct lexbuf *buf, int q)
240{
241 while (buf->c && buf->c != q)
242 {
243 if (css_lex_accept(buf, '\\'))
244 {
245 if (css_lex_accept(buf, 'n'))
246 css_push_char(buf, '\n');
247 else if (css_lex_accept(buf, 'r'))
248 css_push_char(buf, '\r');
249 else if (css_lex_accept(buf, 'f'))
250 css_push_char(buf, '\f');
251 else if (css_lex_accept(buf, '\f'))
252 /* line continuation */ ;
253 else if (css_lex_accept(buf, '\n'))
254 /* line continuation */ ;
255 else if (css_lex_accept(buf, '\r'))
256 css_lex_accept(buf, '\n');
257 else
258 {
259 css_push_char(buf, buf->c);
260 css_lex_next(buf);
261 }
262 }
263 else
264 {
265 css_push_char(buf, buf->c);
266 css_lex_next(buf);
267 }
268 }
269 css_lex_expect(buf, q);
270 css_push_char(buf, 0);
271 return CSS_STRING;
272}
273
274static void css_lex_uri(struct lexbuf *buf)
275{
276 while (buf->c && buf->c != ')' && !iswhite(buf->c))
277 {
278 if (css_lex_accept(buf, '\\'))
279 {
280 if (css_lex_accept(buf, 'n'))
281 css_push_char(buf, '\n');
282 else if (css_lex_accept(buf, 'r'))
283 css_push_char(buf, '\r');
284 else if (css_lex_accept(buf, 'f'))
285 css_push_char(buf, '\f');
286 else
287 {
288 css_push_char(buf, buf->c);
289 css_lex_next(buf);
290 }
291 }
292 else if (buf->c == '!' || buf->c == '#' || buf->c == '$' || buf->c == '%' || buf->c == '&' ||
293 (buf->c >= '*' && buf->c <= '[') ||
294 (buf->c >= ']' && buf->c <= '~') ||
295 buf->c > 159)
296 {
297 css_push_char(buf, buf->c);
298 css_lex_next(buf);
299 }
300 else
301 fz_css_error(buf, "unexpected character in url");
302 }
303 css_push_char(buf, 0);
304}
305
306static int css_lex(struct lexbuf *buf)
307{
308 int t;
309
310 // TODO: keyword escape sequences
311
312 buf->string_len = 0;
313
314restart:
315 if (buf->c == 0)
316 return EOF;
317
318 if (iswhite(buf->c))
319 {
320 while (iswhite(buf->c))
321 css_lex_next(buf);
322 return ' ';
323 }
324
325 if (css_lex_accept(buf, '/'))
326 {
327 if (css_lex_accept(buf, '*'))
328 {
329 while (buf->c)
330 {
331 if (css_lex_accept(buf, '*'))
332 {
333 while (buf->c == '*')
334 css_lex_next(buf);
335 if (css_lex_accept(buf, '/'))
336 goto restart;
337 }
338 css_lex_next(buf);
339 }
340 fz_css_error(buf, "unterminated comment");
341 }
342 return '/';
343 }
344
345 if (css_lex_accept(buf, '<'))
346 {
347 if (css_lex_accept(buf, '!'))
348 {
349 css_lex_expect(buf, '-');
350 css_lex_expect(buf, '-');
351 goto restart; /* ignore CDO */
352 }
353 return '<';
354 }
355
356 if (css_lex_accept(buf, '-'))
357 {
358 if (css_lex_accept(buf, '-'))
359 {
360 css_lex_expect(buf, '>');
361 goto restart; /* ignore CDC */
362 }
363 if (isnmstart(buf->c))
364 {
365 css_push_char(buf, '-');
366 return css_lex_keyword(buf);
367 }
368 return '-';
369 }
370
371 if (css_lex_accept(buf, '.'))
372 {
373 if (buf->c >= '0' && buf->c <= '9')
374 {
375 css_push_char(buf, '.');
376 return css_lex_number(buf);
377 }
378 return '.';
379 }
380
381 if (css_lex_accept(buf, '#'))
382 {
383 if (isnmchar(buf->c))
384 return css_lex_hash(buf);
385 return '#';
386 }
387
388 if (css_lex_accept(buf, '"'))
389 return css_lex_string(buf, '"');
390 if (css_lex_accept(buf, '\''))
391 return css_lex_string(buf, '\'');
392
393 if (buf->c >= '0' && buf->c <= '9')
394 return css_lex_number(buf);
395
396 if (css_lex_accept(buf, 'u'))
397 {
398 if (css_lex_accept(buf, 'r'))
399 {
400 if (css_lex_accept(buf, 'l'))
401 {
402 if (css_lex_accept(buf, '('))
403 {
404 while (iswhite(buf->c))
405 css_lex_next(buf);
406 if (css_lex_accept(buf, '"'))
407 css_lex_string(buf, '"');
408 else if (css_lex_accept(buf, '\''))
409 css_lex_string(buf, '\'');
410 else
411 css_lex_uri(buf);
412 while (iswhite(buf->c))
413 css_lex_next(buf);
414 css_lex_expect(buf, ')');
415 return CSS_URI;
416 }
417 css_push_char(buf, 'u');
418 css_push_char(buf, 'r');
419 css_push_char(buf, 'l');
420 return css_lex_keyword(buf);
421 }
422 css_push_char(buf, 'u');
423 css_push_char(buf, 'r');
424 return css_lex_keyword(buf);
425 }
426 css_push_char(buf, 'u');
427 return css_lex_keyword(buf);
428 }
429
430 if (isnmstart(buf->c))
431 {
432 css_push_char(buf, buf->c);
433 css_lex_next(buf);
434 return css_lex_keyword(buf);
435 }
436
437 t = buf->c;
438 css_lex_next(buf);
439 return t;
440}
441
442static void next(struct lexbuf *buf)
443{
444 buf->lookahead = css_lex(buf);
445}
446
447static int accept(struct lexbuf *buf, int t)
448{
449 if (buf->lookahead == t)
450 {
451 next(buf);
452 return 1;
453 }
454 return 0;
455}
456
457static void expect(struct lexbuf *buf, int t)
458{
459 if (accept(buf, t))
460 return;
461 fz_css_error(buf, "unexpected token");
462}
463
464static void white(struct lexbuf *buf)
465{
466 while (buf->lookahead == ' ')
467 next(buf);
468}
469
470static int iscond(int t)
471{
472 return t == ':' || t == '.' || t == '[' || t == CSS_HASH;
473}
474
475static fz_css_value *parse_term(struct lexbuf *buf)
476{
477 fz_css_value *v;
478
479 if (buf->lookahead == '+' || buf->lookahead == '-')
480 {
481 float sign = buf->lookahead == '-' ? -1 : 1;
482 next(buf);
483 if (buf->lookahead != CSS_NUMBER && buf->lookahead != CSS_LENGTH && buf->lookahead != CSS_PERCENT)
484 fz_css_error(buf, "expected number");
485 if (sign < 0)
486 {
487 v = fz_new_css_value_x(buf->ctx, buf->pool, buf->lookahead);
488 v->data = fz_pool_alloc(buf->ctx, buf->pool, strlen(buf->string) + 2);
489 v->data[0] = '-';
490 strcpy(v->data + 1, buf->string);
491 }
492 else
493 {
494 v = fz_new_css_value(buf->ctx, buf->pool, buf->lookahead, buf->string);
495 }
496 next(buf);
497 white(buf);
498 return v;
499 }
500
501 if (buf->lookahead == CSS_KEYWORD)
502 {
503 v = fz_new_css_value(buf->ctx, buf->pool, CSS_KEYWORD, buf->string);
504 next(buf);
505 if (accept(buf, '('))
506 {
507 white(buf);
508 v->type = '(';
509 v->args = parse_expr(buf);
510 expect(buf, ')');
511 }
512 white(buf);
513 return v;
514 }
515
516 switch (buf->lookahead)
517 {
518 case CSS_HASH:
519 case CSS_STRING:
520 case CSS_URI:
521 case CSS_NUMBER:
522 case CSS_LENGTH:
523 case CSS_PERCENT:
524 v = fz_new_css_value(buf->ctx, buf->pool, buf->lookahead, buf->string);
525 next(buf);
526 white(buf);
527 return v;
528 }
529
530 fz_css_error(buf, "expected value");
531}
532
533static fz_css_value *parse_expr(struct lexbuf *buf)
534{
535 fz_css_value *head, *tail;
536
537 head = tail = parse_term(buf);
538
539 while (buf->lookahead != '}' && buf->lookahead != ';' && buf->lookahead != '!' &&
540 buf->lookahead != ')' && buf->lookahead != EOF)
541 {
542 if (accept(buf, ','))
543 {
544 white(buf);
545 tail = tail->next = fz_new_css_value(buf->ctx, buf->pool, ',', ",");
546 tail = tail->next = parse_term(buf);
547 }
548 else if (accept(buf, '/'))
549 {
550 white(buf);
551 tail = tail->next = fz_new_css_value(buf->ctx, buf->pool, '/', "/");
552 tail = tail->next = parse_term(buf);
553 }
554 else
555 {
556 tail = tail->next = parse_term(buf);
557 }
558 }
559
560 return head;
561}
562
563static fz_css_property *parse_declaration(struct lexbuf *buf)
564{
565 fz_css_property *p;
566
567 if (buf->lookahead != CSS_KEYWORD)
568 fz_css_error(buf, "expected keyword in property");
569 p = fz_new_css_property(buf->ctx, buf->pool, buf->string, NULL, 0);
570 next(buf);
571
572 white(buf);
573 expect(buf, ':');
574 white(buf);
575
576 p->value = parse_expr(buf);
577
578 /* !important */
579 if (accept(buf, '!'))
580 {
581 white(buf);
582 if (buf->lookahead != CSS_KEYWORD || strcmp(buf->string, "important"))
583 fz_css_error(buf, "expected keyword 'important' after '!'");
584 p->important = 1;
585 next(buf);
586 white(buf);
587 }
588
589 return p;
590}
591
592static fz_css_property *parse_declaration_list(struct lexbuf *buf)
593{
594 fz_css_property *head, *tail;
595
596 white(buf);
597
598 if (buf->lookahead == '}' || buf->lookahead == EOF)
599 return NULL;
600
601 head = tail = parse_declaration(buf);
602
603 while (accept(buf, ';'))
604 {
605 white(buf);
606
607 if (buf->lookahead != '}' && buf->lookahead != ';' && buf->lookahead != EOF)
608 {
609 tail = tail->next = parse_declaration(buf);
610 }
611 }
612
613 return head;
614}
615
616static char *parse_attrib_value(struct lexbuf *buf)
617{
618 char *s;
619
620 if (buf->lookahead == CSS_KEYWORD || buf->lookahead == CSS_STRING)
621 {
622 s = fz_pool_strdup(buf->ctx, buf->pool, buf->string);
623 next(buf);
624 white(buf);
625 return s;
626 }
627
628 fz_css_error(buf, "expected attribute value");
629}
630
631static fz_css_condition *parse_condition(struct lexbuf *buf)
632{
633 fz_css_condition *c;
634
635 if (accept(buf, ':'))
636 {
637 accept(buf, ':'); /* swallow css3 :: syntax and pretend it's a normal pseudo-class */
638 if (buf->lookahead != CSS_KEYWORD)
639 fz_css_error(buf, "expected keyword after ':'");
640 c = fz_new_css_condition(buf->ctx, buf->pool, ':', "pseudo", buf->string);
641 next(buf);
642 if (accept(buf, '('))
643 {
644 white(buf);
645 if (accept(buf, CSS_KEYWORD))
646 white(buf);
647 expect(buf, ')');
648 }
649 return c;
650 }
651
652 if (accept(buf, '.'))
653 {
654 if (buf->lookahead != CSS_KEYWORD)
655 fz_css_error(buf, "expected keyword after '.'");
656 c = fz_new_css_condition(buf->ctx, buf->pool, '.', "class", buf->string);
657 next(buf);
658 return c;
659 }
660
661 if (accept(buf, '['))
662 {
663 white(buf);
664
665 if (buf->lookahead != CSS_KEYWORD)
666 fz_css_error(buf, "expected keyword after '['");
667 c = fz_new_css_condition(buf->ctx, buf->pool, '[', buf->string, NULL);
668 next(buf);
669
670 white(buf);
671
672 if (accept(buf, '='))
673 {
674 c->type = '=';
675 c->val = parse_attrib_value(buf);
676 }
677 else if (accept(buf, '|'))
678 {
679 expect(buf, '=');
680 c->type = '|';
681 c->val = parse_attrib_value(buf);
682 }
683 else if (accept(buf, '~'))
684 {
685 expect(buf, '=');
686 c->type = '~';
687 c->val = parse_attrib_value(buf);
688 }
689
690 expect(buf, ']');
691
692 return c;
693 }
694
695 if (buf->lookahead == CSS_HASH)
696 {
697 c = fz_new_css_condition(buf->ctx, buf->pool, '#', "id", buf->string);
698 next(buf);
699 return c;
700 }
701
702 fz_css_error(buf, "expected condition");
703}
704
705static fz_css_condition *parse_condition_list(struct lexbuf *buf)
706{
707 fz_css_condition *head, *tail;
708
709 head = tail = parse_condition(buf);
710 while (iscond(buf->lookahead))
711 {
712 tail = tail->next = parse_condition(buf);
713 }
714 return head;
715}
716
717static fz_css_selector *parse_simple_selector(struct lexbuf *buf)
718{
719 fz_css_selector *s;
720
721 if (accept(buf, '*'))
722 {
723 s = fz_new_css_selector(buf->ctx, buf->pool, NULL);
724 if (iscond(buf->lookahead))
725 s->cond = parse_condition_list(buf);
726 return s;
727 }
728 else if (buf->lookahead == CSS_KEYWORD)
729 {
730 s = fz_new_css_selector(buf->ctx, buf->pool, buf->string);
731 next(buf);
732 if (iscond(buf->lookahead))
733 s->cond = parse_condition_list(buf);
734 return s;
735 }
736 else if (iscond(buf->lookahead))
737 {
738 s = fz_new_css_selector(buf->ctx, buf->pool, NULL);
739 s->cond = parse_condition_list(buf);
740 return s;
741 }
742
743 fz_css_error(buf, "expected selector");
744}
745
746static fz_css_selector *parse_combinator(struct lexbuf *buf, int c, fz_css_selector *a)
747{
748 fz_css_selector *sel, *b;
749 white(buf);
750 b = parse_simple_selector(buf);
751 sel = fz_new_css_selector(buf->ctx, buf->pool, NULL);
752 sel->combine = c;
753 sel->left = a;
754 sel->right = b;
755 return sel;
756}
757
758static fz_css_selector *parse_selector(struct lexbuf *buf)
759{
760 fz_css_selector *sel = parse_simple_selector(buf);
761 for (;;)
762 {
763 if (accept(buf, ' '))
764 {
765 if (accept(buf, '+'))
766 sel = parse_combinator(buf, '+', sel);
767 else if (accept(buf, '>'))
768 sel = parse_combinator(buf, '>', sel);
769 else if (buf->lookahead != ',' && buf->lookahead != '{' && buf->lookahead != EOF)
770 sel = parse_combinator(buf, ' ', sel);
771 else
772 break;
773 }
774 else if (accept(buf, '+'))
775 sel = parse_combinator(buf, '+', sel);
776 else if (accept(buf, '>'))
777 sel = parse_combinator(buf, '>', sel);
778 else
779 break;
780 }
781 return sel;
782}
783
784static fz_css_selector *parse_selector_list(struct lexbuf *buf)
785{
786 fz_css_selector *head, *tail;
787
788 head = tail = parse_selector(buf);
789 while (accept(buf, ','))
790 {
791 white(buf);
792 tail = tail->next = parse_selector(buf);
793 }
794 return head;
795}
796
797static fz_css_rule *parse_ruleset(struct lexbuf *buf)
798{
799 fz_css_selector *s = NULL;
800 fz_css_property *p = NULL;
801
802 fz_try(buf->ctx)
803 {
804 s = parse_selector_list(buf);
805 expect(buf, '{');
806 p = parse_declaration_list(buf);
807 expect(buf, '}');
808 white(buf);
809 }
810 fz_catch(buf->ctx)
811 {
812 if (fz_caught(buf->ctx) != FZ_ERROR_SYNTAX)
813 fz_rethrow(buf->ctx);
814 while (buf->lookahead != EOF)
815 {
816 if (accept(buf, '}'))
817 {
818 white(buf);
819 break;
820 }
821 next(buf);
822 }
823 return NULL;
824 }
825
826 return fz_new_css_rule(buf->ctx, buf->pool, s, p);
827}
828
829static fz_css_rule *parse_at_page(struct lexbuf *buf)
830{
831 fz_css_selector *s = NULL;
832 fz_css_property *p = NULL;
833
834 white(buf);
835 if (accept(buf, ':'))
836 {
837 expect(buf, CSS_KEYWORD);
838 white(buf);
839 }
840 expect(buf, '{');
841 p = parse_declaration_list(buf);
842 expect(buf, '}');
843 white(buf);
844
845 s = fz_new_css_selector(buf->ctx, buf->pool, "@page");
846 return fz_new_css_rule(buf->ctx, buf->pool, s, p);
847}
848
849static fz_css_rule *parse_at_font_face(struct lexbuf *buf)
850{
851 fz_css_selector *s = NULL;
852 fz_css_property *p = NULL;
853
854 white(buf);
855 expect(buf, '{');
856 p = parse_declaration_list(buf);
857 expect(buf, '}');
858 white(buf);
859
860 s = fz_new_css_selector(buf->ctx, buf->pool, "@font-face");
861 return fz_new_css_rule(buf->ctx, buf->pool, s, p);
862}
863
864static void parse_at_rule(struct lexbuf *buf)
865{
866 expect(buf, CSS_KEYWORD);
867
868 /* skip until '{' or ';' */
869 while (buf->lookahead != EOF)
870 {
871 if (accept(buf, ';'))
872 {
873 white(buf);
874 return;
875 }
876 if (accept(buf, '{'))
877 {
878 int depth = 1;
879 while (buf->lookahead != EOF && depth > 0)
880 {
881 if (accept(buf, '{'))
882 ++depth;
883 else if (accept(buf, '}'))
884 --depth;
885 else
886 next(buf);
887 }
888 white(buf);
889 return;
890 }
891 next(buf);
892 }
893}
894
895static fz_css_rule *parse_stylesheet(struct lexbuf *buf, fz_css_rule *chain)
896{
897 fz_css_rule *rule, **nextp, *tail;
898
899 tail = chain;
900 if (tail)
901 {
902 while (tail->next)
903 tail = tail->next;
904 nextp = &tail->next;
905 }
906 else
907 {
908 nextp = &tail;
909 }
910
911 white(buf);
912
913 while (buf->lookahead != EOF)
914 {
915 if (accept(buf, '@'))
916 {
917 if (buf->lookahead == CSS_KEYWORD && !strcmp(buf->string, "page"))
918 {
919 next(buf);
920 rule = *nextp = parse_at_page(buf);
921 nextp = &rule->next;
922 }
923 else if (buf->lookahead == CSS_KEYWORD && !strcmp(buf->string, "font-face"))
924 {
925 next(buf);
926 rule = *nextp = parse_at_font_face(buf);
927 nextp = &rule->next;
928 }
929 else
930 {
931 parse_at_rule(buf);
932 }
933 }
934 else
935 {
936 fz_css_rule *x = parse_ruleset(buf);
937 if (x)
938 {
939 rule = *nextp = x;
940 nextp = &rule->next;
941 }
942 }
943 white(buf);
944 }
945
946 return chain ? chain : tail;
947}
948
949fz_css_property *fz_parse_css_properties(fz_context *ctx, fz_pool *pool, const char *source)
950{
951 struct lexbuf buf;
952 css_lex_init(ctx, &buf, pool, source, "<inline>");
953 next(&buf);
954 return parse_declaration_list(&buf);
955}
956
957void fz_parse_css(fz_context *ctx, fz_css *css, const char *source, const char *file)
958{
959 struct lexbuf buf;
960 css_lex_init(ctx, &buf, css->pool, source, file);
961 next(&buf);
962 css->rule = parse_stylesheet(&buf, css->rule);
963}
964