1 /***************************************************************************
3 * Character-based parser for Css definitions
4 * Author - Iago Rubio <iagorubio(at)users.sourceforge.net>
5 * - Bronisław Białek <after89(at)gmail.com>
6 **************************************************************************/
16 typedef enum eCssKinds
{
17 K_NONE
= -1, K_SELECTOR
, K_ID
, K_CLASS
20 static kindOption CssKinds
[] = {
21 { TRUE
, 's', "struct", "selectors" },
22 { TRUE
, 'v', "variable", "identities" },
23 { TRUE
, 'c', "class", "classes" }
26 typedef enum _CssParserState
{ /* state of parsing */
27 P_STATE_NONE
, /* default state */
28 P_STATE_IN_COMMENT
, /* into a comment, only multi line in CSS */
29 P_STATE_IN_SINGLE_STRING
, /* into a single quoted string */
30 P_STATE_IN_DOUBLE_STRING
, /* into a double quoted string */
31 P_STATE_IN_DEFINITION
, /* on the body of the style definition, nothing for us */
32 P_STATE_IN_MEDIA
, /* on a @media declaration, can be multi-line */
33 P_STATE_IN_IMPORT
, /* on a @import declaration, can be multi-line */
34 P_STATE_IN_NAMESPACE
, /* on a @namespace declaration */
35 P_STATE_IN_PAGE
, /* on a @page declaration */
36 P_STATE_IN_FONTFACE
, /* on a @font-face declaration */
37 P_STATE_AT_END
/* end of parsing */
40 static void makeCssSimpleTag( vString
*name
, cssKind kind
, boolean
delete )
42 vStringTerminate (name
);
43 makeSimpleTag (name
, CssKinds
, kind
);
49 static boolean
isCssDeclarationAllowedChar( const unsigned char *cp
)
51 return isalnum ((int) *cp
) ||
52 isspace ((int) *cp
) ||
53 *cp
== '_' || /* allowed char */
54 *cp
== '-' || /* allowed char */
55 *cp
== '+' || /* allow all sibling in a single tag */
56 *cp
== '>' || /* allow all child in a single tag */
57 *cp
== '{' || /* allow the start of the declaration */
58 *cp
== '.' || /* allow classes and selectors */
59 *cp
== ',' || /* allow multiple declarations */
60 *cp
== ':' || /* allow pseudo classes */
61 *cp
== '*' || /* allow globs as P + * */
62 *cp
== '#'; /* allow ids */
65 static CssParserState
parseCssDeclaration( const unsigned char **position
, cssKind kind
, const char *aname
)
67 const unsigned char *cp
= *position
;
68 vString
*name
= vStringNew ();
69 vStringCopyS(name
, aname
);
71 /* pick to the end of line including children and sibling
72 * if declaration is multiline go for the next line */
73 while ( isCssDeclarationAllowedChar(cp
) ||
74 *cp
== '\0' ) /* track the end of line into the loop */
78 makeCssSimpleTag(name
, kind
, TRUE
);
82 else if( *cp
== '{' || *cp
== '\0' )
83 { /* assume that line end is the same as a starting definition (i.e. the { is on the next line */
84 makeCssSimpleTag(name
, kind
, TRUE
);
86 return P_STATE_IN_DEFINITION
;
89 vStringPut (name
, (int) *cp
);
93 makeCssSimpleTag(name
, kind
, TRUE
);
99 static CssParserState
parseCssLine( const unsigned char *line
, CssParserState state
)
102 vString
*stack
= vStringNew ();
104 while( *line
!= '\0' ) /* fileReadLine returns NULL terminated strings */
106 vStringClear (stack
);
107 while (state
== P_STATE_NONE
&&
108 (isspace ((int) *line
) || isalnum ((int) *line
) || ( *line
== '*' && *(line
-1) != '/' )))
110 if ((stack
->length
> 0 && isspace((int) *line
)) || isalnum ((int) *line
) || *line
== '*') {
111 vStringPut(stack
, (int) *line
);
116 vStringTerminate (stack
);
121 if( *line
== '.' ) /* a class */
122 state
= parseCssDeclaration( &line
, K_CLASS
, vStringValue(stack
) );
123 else if( *line
== '#' ) /* an id */
124 state
= parseCssDeclaration( &line
, K_ID
, vStringValue(stack
) );
125 else if( *line
== '@' ) /* at-rules, we'll ignore them */
129 while( !isspace((int) *line
) )
131 vStringPut (aux
, (int) *line
);
134 vStringTerminate (aux
);
135 if( strcmp( aux
->buffer
, "media" ) == 0 )
136 state
= P_STATE_IN_MEDIA
;
137 else if ( strcmp( aux
->buffer
, "import" ) == 0 )
138 state
= P_STATE_IN_IMPORT
;
139 else if ( strcmp( aux
->buffer
, "namespace" ) == 0 )
140 state
= P_STATE_IN_NAMESPACE
;
141 else if ( strcmp( aux
->buffer
, "page" ) == 0 )
142 state
= P_STATE_IN_PAGE
;
143 else if ( strcmp( aux
->buffer
, "font-face" ) == 0 )
144 state
= P_STATE_IN_FONTFACE
;
147 else if( *line
== '*' && *(line
-1) == '/' ) /* multi-line comment */
148 state
= P_STATE_IN_COMMENT
;
149 else if ( stack
->length
> 0 )
150 state
= parseCssDeclaration( &line
, K_SELECTOR
, vStringValue(stack
) );
153 case P_STATE_IN_COMMENT
:
154 if( *line
== '/' && *(line
-1) == '*')
155 state
= P_STATE_NONE
;
157 case P_STATE_IN_SINGLE_STRING
:
158 if( *line
== '\'' && *(line
-1) != '\\' )
159 state
= P_STATE_IN_DEFINITION
; /* PAGE, FONTFACE and DEFINITION are treated the same way */
161 case P_STATE_IN_DOUBLE_STRING
:
162 if( *line
=='"' && *(line
-1) != '\\' )
163 state
= P_STATE_IN_DEFINITION
; /* PAGE, FONTFACE and DEFINITION are treated the same way */
165 case P_STATE_IN_MEDIA
:
166 /* skip to start of media body or line end */
167 while( *line
!= '{' )
174 state
= P_STATE_NONE
;
176 case P_STATE_IN_IMPORT
:
177 case P_STATE_IN_NAMESPACE
:
178 /* skip to end of declaration or line end */
179 while( *line
!= ';' )
186 state
= P_STATE_NONE
;
188 case P_STATE_IN_PAGE
:
189 case P_STATE_IN_FONTFACE
:
190 case P_STATE_IN_DEFINITION
:
192 line
= fileReadLine ();
194 state
= P_STATE_NONE
;
195 else if( *line
== '\'' )
196 state
= P_STATE_IN_SINGLE_STRING
;
197 else if( *line
== '"' )
198 state
= P_STATE_IN_DOUBLE_STRING
;
204 if (line
== NULL
) return P_STATE_AT_END
;
207 vStringDelete (stack
);
212 static void findCssTags (void)
214 const unsigned char *line
;
215 CssParserState state
= P_STATE_NONE
;
217 while ( (line
= fileReadLine ()) != NULL
)
219 state
= parseCssLine( line
, state
);
220 if( state
==P_STATE_AT_END
) return;
224 /* parser definition */
225 extern parserDefinition
* CssParser (void)
227 static const char *const extensions
[] = { "css", NULL
};
228 parserDefinition
* def
= parserNew ("CSS");
229 def
->kinds
= CssKinds
;
230 def
->kindCount
= KIND_COUNT (CssKinds
);
231 def
->extensions
= extensions
;
232 def
->parser
= findCssTags
;