diff -up ctags-5.8/css.c.me ctags-5.8/css.c --- ctags-5.8/css.c.me 2012-02-08 13:59:35.000000000 +0100 +++ ctags-5.8/css.c 2012-02-08 13:55:16.000000000 +0100 @@ -0,0 +1,226 @@ +/*************************************************************************** + * css.c + * Character-based parser for Css definitions + * Author - Iago Rubio + **************************************************************************/ +#include "general.h" + +#include +#include + +#include "parse.h" +#include "read.h" + + +typedef enum eCssKinds { + K_NONE = -1, K_CLASS, K_SELECTOR, K_ID +} cssKind; + +static kindOption CssKinds [] = { + { TRUE, 'c', "class", "classes" }, + { TRUE, 's', "selector", "selectors" }, + { TRUE, 'i', "id", "identities" } +}; + +typedef enum _CssParserState { // state of parsing + P_STATE_NONE, // default state + P_STATE_IN_COMMENT, // into a comment, only multi line in CSS + P_STATE_IN_SINGLE_STRING, // into a single quoted string + P_STATE_IN_DOUBLE_STRING, // into a double quoted string + P_STATE_IN_DEFINITION, // on the body of the style definition, nothing for us + P_STATE_IN_MEDIA, // on a @media declaration, can be multi-line + P_STATE_IN_IMPORT, // on a @import declaration, can be multi-line + P_STATE_IN_NAMESPACE, // on a @namespace declaration + P_STATE_IN_PAGE, // on a @page declaration + P_STATE_IN_FONTFACE, // on a @font-face declaration + P_STATE_AT_END // end of parsing +} CssParserState; + +static void makeCssSimpleTag( vString *name, cssKind kind, boolean delete ) +{ + vStringTerminate (name); + makeSimpleTag (name, CssKinds, kind); + vStringClear (name); + if( delete ) + vStringDelete (name); +} + +static boolean isCssDeclarationAllowedChar( const unsigned char *cp ) +{ + return isalnum ((int) *cp) || + isspace ((int) *cp) || + *cp == '_' || // allowed char + *cp == '-' || // allowed char + *cp == '+' || // allow all sibling in a single tag + *cp == '>' || // allow all child in a single tag + *cp == '{' || // allow the start of the declaration + *cp == '.' || // allow classes and selectors + *cp == ',' || // allow multiple declarations + *cp == ':' || // allow pseudo classes + *cp == '*' || // allow globs as P + * + *cp == '#'; // allow ids +} + +static CssParserState parseCssDeclaration( const unsigned char **position, cssKind kind ) +{ + vString *name = vStringNew (); + const unsigned char *cp = *position; + + // pick to the end of line including children and sibling + // if declaration is multiline go for the next line + while ( isCssDeclarationAllowedChar(cp) || + *cp == '\0' ) // track the end of line into the loop + { + if( (int) *cp == '\0' ) + { + cp = fileReadLine (); + if( cp == NULL ){ + makeCssSimpleTag(name, kind, TRUE); + *position = cp; + return P_STATE_AT_END; + } + } + else if( *cp == ',' ) + { + makeCssSimpleTag(name, kind, TRUE); + *position = ++cp; + return P_STATE_NONE; + } + else if( *cp == '{' ) + { + makeCssSimpleTag(name, kind, TRUE); + *position = ++cp; + return P_STATE_IN_DEFINITION; + } + + vStringPut (name, (int) *cp); + ++cp; + } + + makeCssSimpleTag(name, kind, TRUE); + *position = cp; + + return P_STATE_NONE; +} + +static CssParserState parseCssLine( const unsigned char *line, CssParserState state ) +{ + vString *aux; + + while( *line != '\0' ) // fileReadLine returns NULL terminated strings + { + while (isspace ((int) *line)) + ++line; + switch( state ) + { + case P_STATE_NONE: + // pick first char if alphanumeric is a selector + if( isalnum ((int) *line) ) + state = parseCssDeclaration( &line, K_SELECTOR ); + else if( *line == '.' ) // a class + state = parseCssDeclaration( &line, K_CLASS ); + else if( *line == '#' ) // an id + state = parseCssDeclaration( &line, K_ID ); + else if( *line == '@' ) // at-rules, we'll ignore them + { + ++line; + aux = vStringNew(); + while( !isspace((int) *line) ) + { + vStringPut (aux, (int) *line); + ++line; + } + vStringTerminate (aux); + if( strcmp( aux->buffer, "media" ) == 0 ) + state = P_STATE_IN_MEDIA; + else if ( strcmp( aux->buffer, "import" ) == 0 ) + state = P_STATE_IN_IMPORT; + else if ( strcmp( aux->buffer, "namespace" ) == 0 ) + state = P_STATE_IN_NAMESPACE; + else if ( strcmp( aux->buffer, "page" ) == 0 ) + state = P_STATE_IN_PAGE; + else if ( strcmp( aux->buffer, "font-face" ) == 0 ) + state = P_STATE_IN_FONTFACE; + vStringDelete (aux); + } + else if( *line == '*' && *(line-1) == '/' ) // multi-line comment + state = P_STATE_IN_COMMENT; + break; + case P_STATE_IN_COMMENT: + if( *line == '/' && *(line-1) == '*') + state = P_STATE_NONE; + break; + case P_STATE_IN_SINGLE_STRING: + if( *line == '\'' && *(line-1) != '\\' ) + state = P_STATE_IN_DEFINITION; // PAGE, FONTFACE and DEFINITION are treated the same way + break; + case P_STATE_IN_DOUBLE_STRING: + if( *line=='"' && *(line-1) != '\\' ) + state = P_STATE_IN_DEFINITION; // PAGE, FONTFACE and DEFINITION are treated the same way + break; + case P_STATE_IN_MEDIA: + // skip to start of media body or line end + while( *line != '{' ) + { + if( *line == '\0' ) + break; + ++line; + } + if( *line == '{' ) + state = P_STATE_NONE; + break; + case P_STATE_IN_IMPORT: + case P_STATE_IN_NAMESPACE: + // skip to end of declaration or line end + while( *line != ';' ) + { + if( *line == '\0' ) + break; + ++line; + } + if( *line == ';' ) + state = P_STATE_NONE; + break; + case P_STATE_IN_PAGE: + case P_STATE_IN_FONTFACE: + case P_STATE_IN_DEFINITION: + if( *line == '}' ) + state = P_STATE_NONE; + else if( *line == '\'' ) + state = P_STATE_IN_SINGLE_STRING; + else if( *line == '"' ) + state = P_STATE_IN_DOUBLE_STRING; + break; + case P_STATE_AT_END: + return state; + break; + } + line++; + } + return state; +} + +static void findCssTags (void) +{ + const unsigned char *line; + CssParserState state = P_STATE_NONE; + + while ( (line = fileReadLine ()) != NULL ) + { + state = parseCssLine( line, state ); + if( state==P_STATE_AT_END ) return; + } +} + +/* parser definition */ +extern parserDefinition* CssParser (void) +{ + static const char *const extensions [] = { "css", NULL }; + parserDefinition* def = parserNew ("CSS"); + def->kinds = CssKinds; + def->kindCount = KIND_COUNT (CssKinds); + def->extensions = extensions; + def->parser = findCssTags; + return def; +} + diff -up ctags-5.8/parsers.h.me ctags-5.8/parsers.h --- ctags-5.8/parsers.h.me 2012-02-08 13:56:46.000000000 +0100 +++ ctags-5.8/parsers.h 2012-02-08 13:57:25.000000000 +0100 @@ -26,6 +26,7 @@ CppParser, \ CsharpParser, \ CobolParser, \ + CssParser, \ DosBatchParser, \ EiffelParser, \ ErlangParser, \ diff -up ctags-5.8/source.mak.me ctags-5.8/source.mak --- ctags-5.8/source.mak.me 2012-02-08 13:58:02.000000000 +0100 +++ ctags-5.8/source.mak 2012-02-08 13:58:42.000000000 +0100 @@ -17,6 +17,7 @@ SOURCES = \ beta.c \ c.c \ cobol.c \ + css.c \ dosbatch.c \ eiffel.c \ entry.c \ @@ -79,6 +80,7 @@ OBJECTS = \ beta.$(OBJEXT) \ c.$(OBJEXT) \ cobol.$(OBJEXT) \ + css.$(OBJEXT) \ dosbatch.$(OBJEXT) \ eiffel.$(OBJEXT) \ entry.$(OBJEXT) \