1 /**
2 * Phoebe DOM Implementation.
3 *
4 * This is a C++ approximation of the W3C DOM model, which follows
5 * fairly closely the specifications in the various .idl files, copies of
6 * which are provided for reference. Most important is this one:
7 *
8 * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html
9 *
10 * Authors:
11 * Bob Jamison
12 *
13 * Copyright (C) 2005 Bob Jamison
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
24 *
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 */
30 #include "cssparser.h"
31 #include "charclass.h"
33 #include <stdarg.h>
35 namespace org
36 {
37 namespace w3c
38 {
39 namespace dom
40 {
41 namespace css
42 {
44 //#########################################################################
45 //# M E S S A G E S
46 //#########################################################################
48 /**
49 * Get the column and row number of the given character position
50 */
51 void CssParser::getColumnAndRow(int p, int &colResult, int &rowResult, int &lastNL)
52 {
53 int col = 1;
54 int row = 1;
55 int lastnl = 0;
57 for (int i=0 ; i<p ; i++)
58 {
59 XMLCh ch = parsebuf[i];
60 if (ch == '\n')
61 {
62 lastnl = i;
63 row++;
64 col=0;
65 }
66 else
67 col++;
68 }
70 colResult = col;
71 rowResult = row;
72 lastNL = lastnl;
73 }
75 /**
76 *
77 */
78 void CssParser::error(char *fmt, ...)
79 {
80 int lineNr;
81 int colNr;
82 int lastNL;
83 getColumnAndRow(lastPosition, colNr, lineNr, lastNL);
85 va_list args;
86 fprintf(stderr, "CssParser:error at %d, line %d, column %d:",
87 lastPosition, lineNr, colNr);
88 va_start(args, fmt);
89 vfprintf(stderr, fmt, args);
90 va_end(args) ;
91 fprintf(stderr, "\n");
93 /*
94 int lineLen = lastPosition - lastNL;
95 printf("lineLen:%d lastNL:%d\n", lineLen, lastNL);
96 for (int i=lastNL+1 ; i<lastPosition ; i++)
97 fprintf(stderr, "%c", parsebuf[i]);
98 fprintf(stderr, "\n");
99 for (int i=0 ; i<lineLen-1 ; i++)
100 fprintf(stderr, " ");
101 fprintf(stderr, "^\n");
102 */
103 for (int i=0 ; i<lastPosition ; i++)
104 fprintf(stderr, "%c", parsebuf[i]);
105 fprintf(stderr, "\n");
106 }
110 //#########################################################################
111 //# P A R S I N G
112 //#########################################################################
114 /**
115 * Get the character at the position and record the fact
116 */
117 XMLCh CssParser::get(int p)
118 {
119 if (p >= parselen)
120 return 0;
121 XMLCh ch = parsebuf[p];
122 //printf("%c", ch);
123 lastPosition = p;
124 return ch;
125 }
129 /**
130 * Test if the given substring exists at the given position
131 * in parsebuf. Use get() in case of out-of-bounds
132 */
133 bool CssParser::match(int pos, char *str)
134 {
135 while (*str)
136 {
137 if (get(pos++) != *str++)
138 return false;
139 }
140 return true;
141 }
143 /**
144 *
145 */
146 int CssParser::skipwhite(int p)
147 {
148 while (p < parselen)
149 {
150 //# XML COMMENT
151 if (match(p, "<!--"))
152 {
153 p+=4;
154 bool done=false;
155 while (p<parselen)
156 {
157 if (match(p, "-->"))
158 {
159 p+=3;
160 done=true;
161 break;
162 }
163 p++;
164 }
165 lastPosition = p;
166 if (!done)
167 {
168 error("unterminated <!-- .. --> comment");
169 return -1;
170 }
171 }
172 //# C comment
173 else if (match(p, "/*"))
174 {
175 p+=2;
176 bool done=false;
177 while (p<parselen)
178 {
179 if (match(p, "*/"))
180 {
181 p+=2;
182 done=true;
183 break;
184 }
185 p++;
186 }
187 lastPosition = p;
188 if (!done)
189 {
190 error("unterminated /* .. */ comment");
191 return -1;
192 }
193 }
194 else if (!isspace(get(p)))
195 break;
196 else
197 p++;
198 }
199 lastPosition = p;
200 return p;
201 }
203 /**
204 * get a word from the buffer
205 */
206 int CssParser::getWord(int p, DOMString &result)
207 {
208 XMLCh ch = get(p);
209 if (!isLetter(ch))
210 return p;
211 DOMString str;
212 str.push_back(ch);
213 p++;
215 while (p < parselen)
216 {
217 ch = get(p);
218 if (isLetterOrDigit(ch) || ch=='-' || ch=='_')
219 {
220 str.push_back(ch);
221 p++;
222 }
223 else if (ch == '\\')
224 {
225 p+=2;
226 }
227 else
228 break;
229 }
230 result = str;
231 return p;
232 }
235 /**
236 * get a word from the buffer
237 */
238 int CssParser::getNumber(int p0, double &result)
239 {
240 int p=p0;
241 DOMString str;
242 while (p < parselen)
243 {
244 XMLCh ch = get(p);
245 if (ch<'0' || ch>'9')
246 break;
247 str.push_back(ch);
248 p++;
249 }
250 if (get(p) == '.' && get(p+1)>='0' && get(p+1)<='9')
251 {
252 p++;
253 str.push_back('.');
254 while (p < parselen)
255 {
256 XMLCh ch = get(p);
257 if (ch<'0' || ch>'9')
258 break;
259 str.push_back(ch);
260 p++;
261 }
262 }
263 if (p>p0)
264 {
265 char *start = (char *)str.c_str();
266 char *end = NULL;
267 double val = strtod(start, &end);
268 if (end > start)
269 {
270 result = val;
271 return p;
272 }
273 }
275 //not a number
276 return p0;
277 }
281 /**
282 * Assume that we are starting on a quote. Ends on the char
283 * after the final '"'
284 */
285 int CssParser::getQuoted(int p0, DOMString &result)
286 {
288 int p = p0;
290 XMLCh quoteChar = get(p);
291 if (quoteChar != '"' && quoteChar != '\'')
292 return p0;
294 p++;
296 DOMString buf;
298 bool done = false;
299 while (p<parselen )
300 {
301 XMLCh ch = get(p);
302 if (ch == quoteChar)
303 {
304 done = true;
305 p++;
306 break;
307 }
308 else
309 {
310 buf.push_back(ch);
311 }
312 p++;
313 }
315 if (!done)
316 {
317 error("unterminated quoted string");
318 return -1;
319 }
321 result.append(buf);
323 return p;
324 }
326 /**
327 * Not in api. replaces URI return by lexer
328 */
329 int CssParser::getUri(int p0, DOMString &str)
330 {
331 int p = p0;
332 if (!match(p, "url("))
333 return p0;
334 p+=4;
335 p = skipwhite(p);
336 DOMString buf;
337 XMLCh ch;
338 while (p < parselen)
339 {
340 ch = get(p);
341 if (isspace(ch) || ch==')')
342 break;
343 buf.push_back(ch);
344 p++;
345 }
346 p = skipwhite(p);
347 ch = get(p);
348 if (ch != ')')
349 {
350 error("no closing ')' on url spec");
351 return -1;
352 }
353 p++;
354 str = buf;
355 return p;
356 }
358 /**
359 * Skip to the end of the block
360 */
361 int CssParser::skipBlock(int p0)
362 {
363 int p = p0;
364 while (p < parselen)
365 {
366 XMLCh ch = get(p);
367 if (ch == '}')
368 {
369 p++;
370 break;
371 }
372 else
373 {
374 p++;
375 }
376 }
377 return p;
378 }
380 //#########################################################################
381 //# P R O D U C T I O N S
382 //#########################################################################
384 /**
385 * stylesheet
386 * : [ CHARSET_SYM S* STRING S* ';' ]?
387 * [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
388 * [ [ ruleset | media | page ] [S|CDO|CDC]* ]*
389 * ;
390 */
391 int CssParser::getStyleSheet(int p0)
392 {
393 int p = p0;
394 int p2 = p;
395 XMLCh ch;
397 //# CHARSET 0 or 1
398 if (match(p, "@charset"))
399 {
400 p+=8;
401 p = skipwhite(p);
402 DOMString str;
403 p2 = getQuoted(p, str);
404 if (p2<=p)
405 {
406 error("quoted string required after @charset");
407 return -1;
408 }
409 p = skipwhite(p2);
410 ch = get(p);
411 if (ch !=';')
412 {
413 error("';' required after @charset declaration");
414 return -1;
415 }
416 p++;
417 p = skipwhite(p);
418 }
420 //# IMPORT 0 to many
421 while (true)
422 {
423 p2 = getImport(p);
424 if (p2<0)
425 {
426 return -1;
427 }
428 if (p2<=p)
429 break;
430 p = p2;
431 }
433 //# RULESET | MEDIA | PAGE 0 to many
434 while (true)
435 {
436 //Ruleset
437 p2 = getRuleSet(p);
438 if (p2<0)
439 {
440 return -1;
441 }
442 if (p2>p)
443 {
444 p = p2;
445 continue;
446 }
448 //Media
449 p2 = getMedia(p);
450 if (p2<0)
451 {
452 return -1;
453 }
454 if (p2>p)
455 {
456 p = p2;
457 continue;
458 }
460 //Page
461 p2 = getPage(p);
462 if (p2<0)
463 {
464 return -1;
465 }
466 if (p2>p)
467 {
468 p = p2;
469 continue;
470 }
472 //none of the above
473 break;
474 }
476 return p;
477 }
479 /**
480 * import
481 * : IMPORT_SYM S*
482 * [STRING|URI] S* [ medium [ COMMA S* medium]* ]? ';' S*
483 * ;
484 */
485 int CssParser::getImport(int p0)
486 {
487 int p = p0;
488 if (!match(p, "@import"))
489 return p0;
490 p+=7;
491 p = skipwhite(p);
493 //# STRING | URI
494 DOMString str;
495 int p2 = getQuoted(p, str);
496 if (p2<0)
497 {
498 return -1;
499 }
500 if (p2<=p)
501 {
502 p2 = getUri(p, str);
503 if (p2<0)
504 {
505 return -1;
506 }
507 if (p2<=p)
508 {
509 error("quoted string or URI required after @import");
510 return -1;
511 }
512 }
513 p = p2;
514 p2 = getMedium(p);
515 if (p2<0)
516 return -1;
518 p = p2;
519 p = skipwhite(p);
520 XMLCh ch = get(p);
521 if (ch != ';')
522 {
523 error("@import must be terminated with ';'");
524 return -1;
525 }
526 p++;
527 return p;
528 }
530 /**
531 * media
532 * : MEDIA_SYM S* medium [ COMMA S* medium ]* LBRACE S* ruleset* '}' S*
533 * ;
534 */
535 int CssParser::getMedia(int p0)
536 {
537 int p = p0;
538 XMLCh ch;
539 if (!match(p, "@media"))
540 return p0;
541 p+=6;
542 p = skipwhite(p);
544 //# MEDIUM LIST
545 int p2 = getMedium(p);
546 if (p2<0)
547 return -1;
548 if (p2<=p)
549 {
550 error("@media must be followed by medium");
551 return -1;
552 }
553 p = p2;
554 while (true)
555 {
556 ch = get(p);
557 if (ch != ',')
558 break;
559 p2 = getMedium(p);
560 if (p2<0)
561 return -1;
562 if (p2<=p)
563 {
564 error("',' in medium list must be followed by medium");
565 return -1;
566 }
567 p = p2;
568 }
570 p = skipwhite(p);
571 ch = get(p);
572 if (ch!='{')
573 {
574 error("@media requires '{' for ruleset");
575 return -1;
576 }
577 p++;
578 p2 = getRuleSet(p);
579 if (p2<0)
580 return -1;
581 if (p2<=p)
582 {
583 error("@media requires ruleset after '{'");
584 return -1;
585 }
586 p = p2;
587 ch = get(p);
588 if (ch != '}')
589 {
590 error("@media requires '}' after ruleset");
591 return -1;
592 }
593 p++;
594 return p0;
595 }
597 /**
598 * medium
599 * : IDENT S*
600 * ;
601 */
602 int CssParser::getMedium(int p0)
603 {
604 int p = p0;
605 p = skipwhite(p);
607 DOMString ident;
608 int p2 = getWord(p, ident);
609 if (p2<0)
610 return -1;
611 if (p2<=p)
612 return p0;
613 p = p2;
615 return p;
616 }
618 /**
619 * page
620 * : PAGE_SYM S* pseudo_page? S*
621 * LBRACE S* declaration [ ';' S* declaration ]* '}' S*
622 * ;
623 */
624 int CssParser::getPage(int p0)
625 {
626 int p = p0;
628 //# @PAGE
629 p = skipwhite(p);
630 if (!match(p, "@page"))
631 return p0;
632 p+= 5;
634 //#PSEUDO PAGE 0 or 1
635 p = skipwhite(p);
636 int p2 = getPseudoPage(p);
637 if (p2<0)
638 return -1;
639 if (p2>p)
640 {
641 p = p2;
642 }
644 //# {
645 p=skipwhite(p);
646 XMLCh ch = get(p);
647 if (p != '{')
648 {
649 error("@page requires '{' before declarations");
650 }
651 p++;
653 //# DECLARATION LIST
654 p = skipwhite(p);
655 CSSStyleDeclaration declarationList;
656 p2 = getDeclaration(p, declarationList);
657 if (p2<0)
658 return -1;
659 if (p2<=p)
660 {
661 error("@page requires declaration(s) after '{'");
662 return -1;
663 }
664 while (true)
665 {
666 p = skipwhite(p2);
667 ch = get(p);
668 if (ch != ';')
669 break;
670 p++;
671 p = skipwhite(p);
672 p2 = getDeclaration(p, declarationList);
673 if (p2<0)
674 return -1;
675 if (p2<= p)
676 {
677 error("@page requires declaration after ';'");
678 return -1;
679 }
680 }
682 //# }
683 p=skipwhite(p);
684 ch = get(p);
685 if (p != '}')
686 {
687 error("@page requires '}' after declarations");
688 }
689 p++;
691 return p;
692 }
694 /**
695 * pseudo_page
696 * : ':' IDENT
697 * ;
698 */
699 int CssParser::getPseudoPage(int p0)
700 {
701 int p = p0;
702 if (!match(p, ":"))
703 return p0;
704 p++;
705 DOMString str;
706 int p2 = getWord(p, str);
707 if (p2<0)
708 return -1;
709 if (p2<=p)
710 {
711 error("pseudo-page requires identifier after ':'");
712 return -1;
713 }
714 p = p2;
715 return p;
716 }
718 /**
719 * ruleset
720 * : selector [ COMMA S* selector ]*
721 * LBRACE S* declaration [ ';' S* declaration ]* '}' S*
722 * ;
723 */
724 int CssParser::getRuleSet(int p0)
725 {
726 int p = p0;
727 XMLCh ch;
729 //## SELECTOR
730 p = skipwhite(p);
731 int p2 = getSelector(p);
732 if (p2<0)
733 return -1;
734 if (p2<=p) //no selector
735 {
736 if (get(p) != '{')//check for selector-less rule
737 return p0;//not me
738 }
739 p = p2;
740 while (true)
741 {
742 p = skipwhite(p);
743 ch = get(p);
744 if (ch != ',')
745 break;
746 p++;
747 p = skipwhite(p);
748 int p2 = getSelector(p);
749 if (p2<0)
750 return -1;
751 if (p2<=p)
752 {
753 error("selector required after ',' in list");
754 return -1;
755 }
756 p = p2;
757 }
759 //## {
760 ch = get(p);
761 if (ch != '{')
762 {
763 error("'{' required before declarations of ruleset");
764 return -1;
765 }
766 p++;
768 //## DECLARATIONS ( 0 to many )
769 CSSStyleDeclaration declarationList;
771 p = skipwhite(p);
772 p2 = getDeclaration(p, declarationList);
773 if (p2<0)
774 return -1;
775 if (p2>p)
776 {
777 p = p2;
778 while (true)
779 {
780 p = skipwhite(p);
781 ch = get(p);
782 if (ch != ';')
783 break;
784 p++;
785 p = skipwhite(p);
786 p2 = getDeclaration(p, declarationList);
787 if (p2<0)
788 return -1;
789 if (p2<=p)
790 {
791 //apparently this is ok
792 //error("declaration required after ';' in ruleset");
793 //return -1;
794 break;
795 }
796 p = p2;
797 }
798 }
799 //## }
800 ch = get(p);
801 if (ch != '}')
802 {
803 error("ruleset requires closing '}'");
804 return -1;
805 }
806 p++;
807 p = skipwhite(p);
809 return p;
810 }
812 /**
813 * selector
814 * : simple_selector [ combinator simple_selector ]*
815 * ;
816 */
817 int CssParser::getSelector(int p0)
818 {
819 int p = p0;
821 //## SIMPLE SELECTOR
822 p = skipwhite(p);
823 int p2 = getSimpleSelector(p);
824 if (p2<0)
825 return -1;
826 if (p2<=p)
827 return p0; //not me
828 p = p2;
830 //## COMBINATORS + MORE SELECTORS
831 while (true)
832 {
833 XMLCh ch = get(p);
834 bool wasSpace = isspace(ch);
835 p = skipwhite(p);
836 ch = get(p);
837 //# Combinators
838 //easier to do here than have a getCombinator()
839 int visibleCombinator = false;
840 if (ch == '+')
841 {
842 visibleCombinator = true;
843 p++;
844 }
845 else if (ch == '>')
846 {
847 visibleCombinator = true;
848 p++;
849 }
850 else if (wasSpace)
851 {
852 }
853 else
854 {
855 break;
856 }
857 p = skipwhite(p);
858 p2 = getSimpleSelector(p);
859 if (p2<0)
860 return -1;
861 if (p2<=p)
862 {
863 if (visibleCombinator)
864 {
865 error("need simple selector after combinator");
866 return -1;
867 }
868 else
869 {
870 break;
871 }
872 }
873 p = p2;
874 }
875 return p;
876 }
878 /**
879 * simple_selector
880 * : element_name [ HASH | class | attrib | pseudo ]*
881 * | [ HASH | class | attrib | pseudo ]+
882 * ;
883 */
884 int CssParser::getSimpleSelector(int p0)
885 {
886 int p = p0;
887 int p2;
889 DOMString str;
891 p = skipwhite(p);
893 int selectorItems = 0;
895 XMLCh ch = get(p);
897 //######################
898 //# Note: do NOT skipwhite between items. Only within the
899 //# pseudo function and attrib below
900 //######################
902 //#Element name 0 or 1
903 if (isLetter(ch))
904 {
905 p2 = getWord(p, str);
906 if (p2<0)
907 return -1;
908 if (p2<=p)
909 {
910 error("null element name");
911 return -1;
912 }
913 selectorItems++;
914 p = p2;
915 }
916 else if (ch == '*')
917 {
918 str = "*";
919 p++;
920 selectorItems++;
921 }
925 //## HASH, CLASS, ATTRIB, PSEUDO (0 to many with elem name, 1 to many without)
926 while (true)
927 {
928 XMLCh ch = get(p);
930 //# HASH
931 if (ch == '#')
932 {
933 p++;
934 p2 = getWord(p, str);
935 if (p2<0)
936 return -1;
937 if (p2<=p)
938 {
939 error("no name for hash");
940 return -1;
941 }
942 p = p2;
943 selectorItems++;
944 }
946 //# CLASS
947 else if (ch == '.')
948 {
949 p++;
950 p2 = getWord(p, str);
951 if (p2<0)
952 return -1;
953 if (p2<=p)
954 {
955 error("no name for class");
956 return -1;
957 }
958 p = p2;
959 selectorItems++;
960 }
962 //# ATTRIB
963 else if (ch == '[')
964 {
965 p++;
966 p = skipwhite(p);
967 p2 = getWord(p, str);
968 if (p2<0)
969 return -1;
970 if (p2<=p)
971 {
972 error("no name for class");
973 return -1;
974 }
975 p = skipwhite(p2);
976 bool getRHS=false;
977 if (match(p, "="))
978 {
979 p++;
980 getRHS=true;
981 }
982 else if (match(p, "~="))
983 {
984 p+=2;
985 getRHS=true;
986 }
987 else if (match(p, "|="))
988 {
989 p+=2;
990 getRHS=true;
991 }
992 if (getRHS)
993 {
994 p = skipwhite(p);
995 ch = get(p);
996 if (isLetter(ch))
997 {
998 p2 = getWord(p, str);
999 if (p2<0)
1000 return -1;
1001 if (p2<=p)
1002 {
1003 error("null ident on rhs of attrib");
1004 return -1;
1005 }
1006 p = p2;
1007 }
1008 else if (ch == '\'' || ch =='"')
1009 {
1010 p2 = getQuoted(p, str);
1011 if (p2<0)
1012 return -1;
1013 if (p2<=p)
1014 {
1015 error("null literal string on rhs of attrib");
1016 return -1;
1017 }
1018 p = p2;
1019 }
1020 }//getRHS
1021 p = skipwhite(p);
1022 ch = get(p);
1023 if (ch != ']')
1024 {
1025 error("attrib needs closing ']'");
1026 //return -1;
1027 p = skipBlock(p);
1028 return p;
1029 }
1030 p++;
1031 selectorItems++;
1032 }
1034 //# PSEUDO
1035 else if (ch == ':')
1036 {
1037 p++;
1038 p2 = getWord(p, str);
1039 if (p2<0)
1040 return -1;
1041 if (p2<=p)
1042 {
1043 error("no name for pseudo");
1044 return -1;
1045 }
1046 p = p2;
1047 selectorItems++;
1048 ch = get(p);
1049 if (ch == '(')
1050 {
1051 p++;
1052 p = skipwhite(p);
1053 ch = get(p);
1054 if (isLetter(ch))
1055 {
1056 p2 = getWord(p, str);
1057 if (p2<0)
1058 return -1;
1059 if (p2<=p)
1060 {
1061 error("null function parameter in pseudo");
1062 return -1;
1063 }
1064 p = skipwhite(p2);
1065 ch = get(p);
1066 }
1067 if (ch != ')')
1068 {
1069 error("function in pseudo needs ')'");
1070 return -1;
1071 }
1072 p++;
1073 }// ch==( -function-
1074 }//pseudo
1076 //# none of the above
1077 else
1078 {
1079 break;
1080 }
1082 }//while
1085 if (selectorItems > 0)
1086 return p;
1087 return p0;
1088 }
1090 /**
1091 * declaration
1092 * : property ':' S* expr prio?
1093 * | {empty}
1094 * ;
1095 */
1096 int CssParser::getDeclaration(int p0, CSSStyleDeclaration &declarationList)
1097 {
1098 int p = p0;
1100 //## PROPERTY
1101 p = skipwhite(p);
1102 XMLCh ch = get(p);
1103 if (!isLetter(ch))
1104 return p0; //not me
1105 DOMString propName;
1106 int p2 = getWord(p, propName);
1107 if (p2<0)
1108 return -1;
1110 //## ':'
1111 p = skipwhite(p2);
1112 ch = get(p);
1113 if (ch != ':')
1114 {
1115 error("declaration requires ':' between name and value");
1116 return -1;
1117 }
1118 p++;
1120 //## EXPR
1121 p = skipwhite(p);
1122 p2 = getExpr(p);
1123 if (p2<0)
1124 return -1;
1125 if (p2<=p)
1126 {
1127 error("declaration requires value after ':'");
1128 return -1;
1129 }
1130 DOMString propVal;
1131 for (int i=p ; i<p2 ; i++) //get our substring
1132 propVal.push_back(get(i));
1133 printf("propVal:%s\n", propVal.c_str());
1134 p = p2;
1136 //## PRIO (optional)
1137 p = skipwhite(p);
1138 DOMString prio;
1139 p2 = getPrio(p, prio);
1140 if (p2<0)
1141 return -1;
1142 if (p2>p)
1143 {
1144 //do something
1145 p = p2;
1146 }
1148 return p;
1149 }
1151 /**
1152 * prio
1153 * : IMPORTANT_SYM S*
1154 * ;
1155 */
1156 int CssParser::getPrio(int p0, DOMString &val)
1157 {
1158 int p = p0;
1160 //## '!"
1161 p = skipwhite(p);
1162 XMLCh ch = get(p);
1163 if (ch != '!')
1164 return p0;
1165 p++;
1167 //## "important"
1168 p = skipwhite(p);
1169 if (!match(p, "important"))
1170 {
1171 error("priority symbol is 'important'");
1172 return -1;
1173 }
1174 p += 9;
1175 val = "important";
1176 return p;
1177 }
1179 /**
1180 * expr
1181 * : term [ operator term ]*
1182 * ;
1183 */
1184 int CssParser::getExpr(int p0)
1185 {
1186 int p = p0;
1188 //## TERM
1189 p = skipwhite(p);
1190 int p2 = getTerm(p);
1191 if (p2<0)
1192 return -1;
1193 if (p2<=p)
1194 return p0; //not me
1195 p = p2;
1196 while (p < parselen)
1197 {
1198 p = skipwhite(p);
1199 //#Operator. do this instead of getOperator()
1200 XMLCh ch = get(p);
1201 int visibleTerm = false;
1202 if (ch == '/')
1203 {
1204 visibleTerm = true;
1205 p++;
1206 }
1207 else if (ch == ',')
1208 {
1209 visibleTerm = true;
1210 p++;
1211 }
1212 else
1213 {
1214 //just space. this is allowable between terms,
1215 // so we still need to check for another term
1216 }
1217 p = skipwhite(p);
1218 p2 = getTerm(p);
1219 if (p2<0)
1220 return -1;
1221 if (p2<=p)
1222 {
1223 if (visibleTerm)
1224 {
1225 error("expression requires term after operator");
1226 return -1;
1227 }
1228 else
1229 {
1230 break;
1231 }
1232 }
1233 p = p2;
1234 }
1236 return p;
1237 }
1239 /**
1240 * term
1241 * : unary_operator?
1242 * [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* |
1243 * TIME S* | FREQ S* | function ]
1244 * | STRING S* | IDENT S* | URI S* | hexcolor
1245 * ;
1246 */
1247 int CssParser::getTerm(int p0)
1248 {
1249 int p = p0;
1250 p = skipwhite(p);
1251 int unitType = CSSPrimitiveValue::CSS_UNKNOWN;
1252 //# Unary operator
1253 XMLCh ch = get(p);
1254 bool hasUnary = false;
1255 if (ch == '-')
1256 {
1257 p++;
1258 hasUnary = true;
1259 }
1260 else if (ch == '+')
1261 {
1262 p++;
1263 hasUnary = true;
1264 }
1265 //# NUMERIC
1266 double numVal;
1267 int p2 = getNumber(p, numVal);
1268 if (p2<0)
1269 return -1;
1270 if (p2>p)
1271 {
1272 p = p2;
1273 if (match(p, "%"))
1274 {
1275 unitType = CSSPrimitiveValue::CSS_PERCENTAGE;
1276 p++;
1277 }
1278 else if (match(p, "em"))
1279 {
1280 unitType = CSSPrimitiveValue::CSS_EMS;
1281 p+=2;
1282 }
1283 else if (match(p, "ex"))
1284 {
1285 unitType = CSSPrimitiveValue::CSS_EXS;
1286 p+=2;
1287 }
1288 else if (match(p, "px"))
1289 {
1290 unitType = CSSPrimitiveValue::CSS_PX;
1291 p+=2;
1292 }
1293 else if (match(p, "cm"))
1294 {
1295 unitType = CSSPrimitiveValue::CSS_CM;
1296 p+=2;
1297 }
1298 else if (match(p, "mm"))
1299 {
1300 unitType = CSSPrimitiveValue::CSS_MM;
1301 p+=2;
1302 }
1303 else if (match(p, "in"))
1304 {
1305 unitType = CSSPrimitiveValue::CSS_IN;
1306 p+=2;
1307 }
1308 else if (match(p, "pt"))
1309 {
1310 unitType = CSSPrimitiveValue::CSS_PT;
1311 p+=2;
1312 }
1313 else if (match(p, "pc"))
1314 {
1315 unitType = CSSPrimitiveValue::CSS_PC;
1316 p+=2;
1317 }
1318 else if (match(p, "deg"))
1319 {
1320 unitType = CSSPrimitiveValue::CSS_DEG;
1321 p+=3;
1322 }
1323 else if (match(p, "rad"))
1324 {
1325 unitType = CSSPrimitiveValue::CSS_RAD;
1326 p+=3;
1327 }
1328 else if (match(p, "grad"))
1329 {
1330 unitType = CSSPrimitiveValue::CSS_GRAD;
1331 p+=4;
1332 }
1333 else if (match(p, "ms"))
1334 {
1335 unitType = CSSPrimitiveValue::CSS_MS;
1336 p+=2;
1337 }
1338 else if (match(p, "s"))
1339 {
1340 unitType = CSSPrimitiveValue::CSS_S;
1341 p+=1;
1342 }
1343 else if (match(p, "Hz"))
1344 {
1345 unitType = CSSPrimitiveValue::CSS_HZ;
1346 p+=2;
1347 }
1348 else if (match(p, "kHz"))
1349 {
1350 unitType = CSSPrimitiveValue::CSS_KHZ;
1351 p+=2;
1352 }
1353 else if (isLetter(get(p)))//some other string
1354 {
1355 DOMString suffix;
1356 p2 = getWord(p, suffix);
1357 if (p2<0)
1358 return -1;
1359 unitType = CSSPrimitiveValue::CSS_DIMENSION;
1360 p = p2;
1361 }
1362 else //plain number
1363 {
1364 unitType = CSSPrimitiveValue::CSS_NUMBER;
1365 }
1366 return p;
1367 }
1369 DOMString str;
1371 //## URI --do before function, as syntax is similar
1372 p2 = getUri(p, str);
1373 if (p2<0)
1374 return -1;
1375 if (p2>p)
1376 {
1377 if (hasUnary)
1378 {
1379 error("+ or - not allowed on URI");
1380 return -1;
1381 }
1382 p = p2;
1383 unitType = CSSPrimitiveValue::CSS_URI;
1384 return p;
1385 }
1387 //## FUNCTION
1388 p2 = getFunction(p);
1389 if (p2<0)
1390 return -1;
1391 if (p2>p)
1392 {
1393 p = p2;
1394 return p;
1395 }
1397 //## STRING
1398 ch = get(p);
1399 if (ch == '"' || ch == '\'')
1400 {
1401 p2 = getQuoted(p, str);
1402 if (p2<0)
1403 return -1;
1404 if (p2>p)
1405 {
1406 if (hasUnary)
1407 {
1408 error("+ or - not allowed on a string");
1409 return -1;
1410 }
1411 p = p2;
1412 unitType = CSSPrimitiveValue::CSS_STRING;
1413 return p;
1414 }
1415 }
1417 //## IDENT
1418 ch = get(p);
1419 if (isLetter(ch))
1420 {
1421 p2 = getWord(p, str);
1422 if (p2<0)
1423 return -1;
1424 if (p2>p)
1425 {
1426 if (hasUnary)
1427 {
1428 error("+ or - not allowed on an identifier");
1429 return -1;
1430 }
1431 p = p2;
1432 unitType = CSSPrimitiveValue::CSS_IDENT;
1433 return p;
1434 }
1435 }
1438 //## HEXCOLOR
1439 p2 = getHexColor(p);
1440 if (p2<0)
1441 return -1;
1442 if (p2>p)
1443 {
1444 if (hasUnary)
1445 {
1446 error("+ or - not allowed on hex color");
1447 return -1;
1448 }
1449 p = p2;
1450 unitType = CSSPrimitiveValue::CSS_RGBCOLOR;
1451 return p;
1452 }
1455 return p0;
1456 }
1458 /**
1459 * function
1460 * : FUNCTION S* expr ')' S*
1461 * ;
1462 */
1463 int CssParser::getFunction(int p0)
1464 {
1465 int p = p0;
1467 //## IDENT + ( (both)
1468 DOMString name;
1469 p = skipwhite(p);
1470 int p2 = getWord(p, name);
1471 if (p2<0)
1472 return -1;
1473 if (p2<=p)
1474 return p0; //not me
1475 if (name == "uri" || name=="url")
1476 return p0; //not me
1477 p = skipwhite(p2);
1478 XMLCh ch = get(p);
1479 if (ch != '(')
1480 return p0; //still not me
1481 p++;
1483 //## EXPR
1484 p = skipwhite(p);
1485 p2 = getExpr(p);
1486 if (p2<0)
1487 return -1;
1488 if (p2<=p)
1489 {
1490 error("function requires expression");
1491 return -1;
1492 }
1493 p = p2;
1495 //## ')'
1496 p = skipwhite(p);
1497 ch = get(p);
1498 if (ch != ')')
1499 {
1500 error("function requires closing ')'");
1501 return -1;
1502 }
1503 p++;
1504 p = skipwhite(p);
1506 return p;
1507 }
1509 /**
1510 * There is a constraint on the color that it must
1511 * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
1512 * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
1513 * hexcolor
1514 * : HASH S*
1515 * ;
1516 */
1517 int CssParser::getHexColor(int p0)
1518 {
1519 int p = p0;
1521 //## '#'
1522 p = skipwhite(p);
1523 if (!match(p, "#"))
1524 return p0;
1525 p++;
1527 //## HEX
1528 DOMString hex;
1529 long hexVal = 0;
1530 while (p < parselen)
1531 {
1532 XMLCh b = get(p);
1533 if (b>='0' && b<='9')
1534 {
1535 hexVal = (hexVal << 4) + (b - '0');
1536 hex.push_back(b);
1537 p++;
1538 }
1539 else if (b>='a' && b<='f')
1540 {
1541 hexVal = (hexVal << 4) + (b - 'a' + 10);
1542 hex.push_back(b);
1543 p++;
1544 }
1545 else if (b>='A' && b<='F')
1546 {
1547 hexVal = (hexVal << 4) + (b - 'A' + 10);
1548 hex.push_back(b);
1549 p++;
1550 }
1551 else
1552 {
1553 break;
1554 }
1555 }
1557 if (hex.size() != 3 && hex.size() != 6)
1558 {
1559 error("exactly 3 or 6 hex digits are required after '#'");
1560 return -1;
1561 }
1563 return p;
1564 }
1568 /**
1569 *
1570 */
1571 bool CssParser::parse(const DOMString &str)
1572 {
1573 /*
1574 int len = str.size();
1575 for (int i=0 ; i<len ; i++)
1576 {
1577 XMLCh ch = str[i];
1578 if (ch == '\\' && i<(len-1)) //escape!
1579 {
1580 i++;
1581 }
1582 else
1583 parsebuf.push_back(ch);
1584 }
1585 */
1586 parsebuf = str;
1588 parselen = parsebuf.size();
1589 //printf("==============================\n%s\n========================\n", str.c_str());
1591 lastPosition = 0;
1593 int p = getStyleSheet(0);
1594 if (p < parselen)
1595 {
1596 error("Not everything parsed");
1597 return false;
1598 }
1600 return true;
1601 }
1604 /**
1605 *
1606 */
1607 bool CssParser::parseFile(const DOMString &fileName)
1608 {
1609 DOMString tmp = fileName;
1610 char *fname = (char *)tmp.c_str();
1611 FILE *f = fopen(fname, "r");
1612 if (!f)
1613 {
1614 printf("Could not open %s for reading\n", fname);
1615 return false;
1616 }
1618 DOMString str;
1619 while (!feof(f))
1620 {
1621 int ch = fgetc(f);
1622 if (ch<0)
1623 break;
1624 str.push_back(ch);
1625 }
1626 fclose(f);
1628 bool ret = parse(str);
1630 return ret;
1631 }
1639 } // namespace css
1640 } // namespace dom
1641 } // namespace w3c
1642 } // namespace org
1645 #ifdef TEST
1647 int main(int argc, char **argv)
1648 {
1649 org::w3c::dom::css::CssParser parser;
1650 char *fileName;
1651 fileName = "001.css";
1652 //fileName = "acid.css";
1653 //fileName = "base.css";
1654 //fileName = "inkscape.css";
1655 //fileName = "meyerweb.css";
1656 if (!parser.parseFile(fileName))
1657 {
1658 printf("Test failed\n");
1659 return 1;
1660 }
1661 return 0;
1662 }
1664 #endif /* TEST */
1666 //#########################################################################
1667 //# E N D O F F I L E
1668 //#########################################################################