Version bump.
[geany-mirror.git] / tagmanager / css.c
blob9ae2506dbc6061ce8331e33d0b236a4ce1fd11f0
1 /***************************************************************************
2 * css.c
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 **************************************************************************/
7 #include "general.h"
9 #include <string.h>
10 #include <ctype.h>
12 #include "parse.h"
13 #include "read.h"
16 typedef enum eCssKinds {
17 K_NONE = -1, K_SELECTOR, K_ID, K_CLASS
18 } cssKind;
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 */
38 } CssParserState;
40 static void makeCssSimpleTag( vString *name, cssKind kind, boolean delete )
42 vStringTerminate (name);
43 makeSimpleTag (name, CssKinds, kind);
44 vStringClear (name);
45 if( delete )
46 vStringDelete (name);
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 */
76 if( *cp == ',' )
78 makeCssSimpleTag(name, kind, TRUE);
79 *position = cp;
80 return P_STATE_NONE;
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);
85 *position = cp;
86 return P_STATE_IN_DEFINITION;
89 vStringPut (name, (int) *cp);
90 ++cp;
93 makeCssSimpleTag(name, kind, TRUE);
94 *position = cp;
96 return P_STATE_NONE;
99 static CssParserState parseCssLine( const unsigned char *line, CssParserState state )
101 vString *aux;
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);
114 ++line;
116 vStringTerminate (stack);
118 switch( state )
120 case P_STATE_NONE:
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 */
127 ++line;
128 aux = vStringNew();
129 while( !isspace((int) *line) )
131 vStringPut (aux, (int) *line);
132 ++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;
145 vStringDelete (aux);
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) );
152 break;
153 case P_STATE_IN_COMMENT:
154 if( *line == '/' && *(line-1) == '*')
155 state = P_STATE_NONE;
156 break;
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 */
160 break;
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 */
164 break;
165 case P_STATE_IN_MEDIA:
166 /* skip to start of media body or line end */
167 while( *line != '{' )
169 if( *line == '\0' )
170 break;
171 ++line;
173 if( *line == '{' )
174 state = P_STATE_NONE;
175 break;
176 case P_STATE_IN_IMPORT:
177 case P_STATE_IN_NAMESPACE:
178 /* skip to end of declaration or line end */
179 while( *line != ';' )
181 if( *line == '\0' )
182 break;
183 ++line;
185 if( *line == ';' )
186 state = P_STATE_NONE;
187 break;
188 case P_STATE_IN_PAGE:
189 case P_STATE_IN_FONTFACE:
190 case P_STATE_IN_DEFINITION:
191 if( *line == '\0' )
192 line = fileReadLine ();
193 if( *line == '}' )
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;
199 break;
200 case P_STATE_AT_END:
201 return state;
202 break;
204 if (line == NULL) return P_STATE_AT_END;
205 line++;
207 vStringDelete (stack);
209 return state;
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;
233 return def;