Code

moving trunk for module inkscape
[inkscape.git] / src / dom / cssparser.cpp
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");
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)
119     if (p >= parselen)
120         return 0;
121     XMLCh ch = parsebuf[p];
122     //printf("%c", ch);
123     lastPosition = p;
124     return ch;
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)
135     while (*str)
136        {
137        if (get(pos++) != *str++)
138            return false;
139        }
140    return true;
143 /**
144  *
145  */
146 int CssParser::skipwhite(int p)
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;
203 /**
204  * get a word from the buffer
205  */
206 int CssParser::getWord(int p, DOMString &result)
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;
235 /**
236  * get a word from the buffer
237  */
238 int CssParser::getNumber(int p0, double &result)
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;
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)
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;
326 /**
327  * Not in api.  replaces URI return by lexer
328  */
329 int CssParser::getUri(int p0, DOMString &str)
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;
358 /**
359  * Skip to the end of the block
360  */
361 int CssParser::skipBlock(int p0)
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;
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)
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;
479 /**
480  * import
481  *   : IMPORT_SYM S*
482  *     [STRING|URI] S* [ medium [ COMMA S* medium]* ]? ';' S*
483  *   ;
484  */
485 int CssParser::getImport(int p0)
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;
530 /**
531  * media
532  *   : MEDIA_SYM S* medium [ COMMA S* medium ]* LBRACE S* ruleset* '}' S*
533  *   ;
534  */
535 int CssParser::getMedia(int p0)
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;
597 /**
598  * medium
599  *   : IDENT S*
600  *   ;
601  */
602 int CssParser::getMedium(int p0)
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;
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)
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;
694 /**
695  * pseudo_page
696  *   : ':' IDENT
697  *   ;
698  */
699 int CssParser::getPseudoPage(int p0)
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;
718 /**
719  * ruleset
720  *   : selector [ COMMA S* selector ]*
721  *     LBRACE S* declaration [ ';' S* declaration ]* '}' S*
722  *   ;
723  */
724 int CssParser::getRuleSet(int p0)
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;
812 /**
813  * selector
814  *   : simple_selector [ combinator simple_selector ]*
815  *   ;
816  */
817 int CssParser::getSelector(int p0)
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;
878 /**
879  * simple_selector
880  *   : element_name [ HASH | class | attrib | pseudo ]*
881  *   | [ HASH | class | attrib | pseudo ]+
882  *   ;
883  */
884 int CssParser::getSimpleSelector(int p0)
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;
1090 /**
1091  * declaration
1092  *   : property ':' S* expr prio?
1093  *   | {empty}
1094  *   ;
1095  */
1096 int CssParser::getDeclaration(int p0, CSSStyleDeclaration &declarationList)
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;
1151 /**
1152  * prio
1153  *   : IMPORTANT_SYM S*
1154  *   ;
1155  */
1156 int CssParser::getPrio(int p0, DOMString &val)
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;
1179 /**
1180  * expr
1181  *   : term [ operator term ]*
1182  *   ;
1183  */
1184 int CssParser::getExpr(int p0)
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;
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)
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;
1458 /**
1459  * function
1460  *   : FUNCTION S* expr ')' S*
1461  *   ;
1462  */
1463 int CssParser::getFunction(int p0)
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;
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)
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;
1568 /**
1569  *
1570  */
1571 bool CssParser::parse(const DOMString &str)
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;
1604 /**
1605  *
1606  */
1607 bool CssParser::parseFile(const DOMString &fileName)
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;
1639 } // namespace css
1640 } // namespace dom
1641 } // namespace w3c
1642 } // namespace org
1645 #ifdef TEST
1647 int main(int argc, char **argv)
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;
1664 #endif /* TEST */
1666 //#########################################################################
1667 //# E N D    O F    F I L E
1668 //#########################################################################