Revision: 1563 http://svn.sourceforge.net/geany/?rev=1563&view=rev Author: eht16 Date: 2007-05-23 07:26:52 -0700 (Wed, 23 May 2007)
Log Message: ----------- Backport changes from CTags SVN to fix parse problems in the Ruby parser. Add filetype extension "*.ruby".
Modified Paths: -------------- trunk/ChangeLog trunk/data/filetype_extensions.conf trunk/src/filetypes.c trunk/tagmanager/ruby.c trunk/tagmanager/strlist.c trunk/tagmanager/strlist.h
Modified: trunk/ChangeLog =================================================================== --- trunk/ChangeLog 2007-05-23 14:04:20 UTC (rev 1562) +++ trunk/ChangeLog 2007-05-23 14:26:52 UTC (rev 1563) @@ -4,6 +4,11 @@ * geany.glade, src/dialogs.c, src/geany.h, src/interface.c, src/keyfile.c, src/main.c, src/prefs.c: Add default startup directory option (closes #1704988). + * tagmanager/ruby.c, tagmanager/strlist.c, tagmanager/strlist.h: + Backport changes from CTags SVN to fix parse problems in the Ruby + parser. + * data/filetype_extensions.conf, src/filetypes.c: + Add filetype extension "*.ruby".
2007-05-23 Nick Treleaven nick.treleaven@btinternet.com
Modified: trunk/data/filetype_extensions.conf =================================================================== --- trunk/data/filetype_extensions.conf 2007-05-23 14:04:20 UTC (rev 1562) +++ trunk/data/filetype_extensions.conf 2007-05-23 14:26:52 UTC (rev 1563) @@ -14,7 +14,7 @@ PHP=*.php;*.php3;*.php4;*.php5;*.phtml; Javascript=*.js; Python=*.py;*.pyw; -Ruby=*.rb;*.rhtml; +Ruby=*.rb;*.rhtml;*.ruby; Tcl=*.tcl;*.tk;*.wish; Lua=*.lua; Ferite=*.fe;
Modified: trunk/src/filetypes.c =================================================================== --- trunk/src/filetypes.c 2007-05-23 14:04:20 UTC (rev 1562) +++ trunk/src/filetypes.c 2007-05-23 14:26:52 UTC (rev 1563) @@ -301,7 +301,7 @@ filetypes[GEANY_FILETYPES_RUBY]->has_tags = TRUE; filetypes[GEANY_FILETYPES_RUBY]->title = g_strdup(_("Ruby source file")); filetypes[GEANY_FILETYPES_RUBY]->extension = g_strdup("rb"); - filetypes[GEANY_FILETYPES_RUBY]->pattern = utils_strv_new("*.rb", "*.rhtml", NULL); + filetypes[GEANY_FILETYPES_RUBY]->pattern = utils_strv_new("*.rb", "*.rhtml", "*.ruby", NULL); filetypes[GEANY_FILETYPES_RUBY]->style_func_ptr = styleset_ruby; filetypes[GEANY_FILETYPES_RUBY]->comment_open = g_strdup("#"); filetypes[GEANY_FILETYPES_RUBY]->comment_close = NULL;
Modified: trunk/tagmanager/ruby.c =================================================================== --- trunk/tagmanager/ruby.c 2007-05-23 14:04:20 UTC (rev 1562) +++ trunk/tagmanager/ruby.c 2007-05-23 14:26:52 UTC (rev 1563) @@ -1,198 +1,399 @@ /* -* $Id: ruby.c,v 1.2 2001/12/18 04:30:18 darren Exp $ +* $Id$ * * Copyright (c) 2000-2001, Thaddeus Covert sahuagin@mediaone.net +* Copyright (c) 2002 Matthias Veit matthias_veit@yahoo.de +* Copyright (c) 2004 Elliott Hughes enh@acm.org * * This source code is released for free distribution under the terms of the * GNU General Public License. * * This module contains functions for generating tags for Ruby language * files. -* -* Copyright (c) 2002, Matthias Veit matthias_veit@yahoo.de -* Enable parsing of class (in ruby: singleton) methods and mixins. -* */
/* * INCLUDE FILES */ -#include "general.h" /* must always come first */ +#include "general.h" /* must always come first */
#include <string.h>
+#include "entry.h" #include "parse.h" #include "read.h" #include "vstring.h"
/* -* DATA DEFINITIONS +* DATA DECLARATIONS */ typedef enum { - K_CLASS, K_METHOD, K_SINGLETON, K_MIXIN, K_VARIABLE, K_MEMBER + K_UNDEFINED = -1, K_CLASS, K_METHOD, K_MODULE, K_SINGLETON } rubyKind;
+/* +* DATA DEFINITIONS +*/ static kindOption RubyKinds [] = { - { TRUE, 'c', "class", "classes" }, - { TRUE, 'f', "function", "methods" }, - { TRUE, 'm', "member", "singleton_methods" }, - { TRUE, 'd', "macro", "mixins" }, - { TRUE, 'v', "variable", "variable" }, - { TRUE, 's', "struct", "member" } - }; + { TRUE, 'c', "class", "classes" }, + { TRUE, 'f', "method", "methods" }, + { TRUE, 'm', "module", "modules" }, + { TRUE, 'F', "singleton method", "singleton methods" } +};
+static stringList* nesting = 0;
/* * FUNCTION DEFINITIONS */
-static void findRubyTags (void) { - vString *name = vStringNew (); - const unsigned char *line; - boolean inMultilineString = FALSE; +/* +* Returns a string describing the scope in 'list'. +* We record the current scope as a list of entered scopes. +* Scopes corresponding to 'if' statements and the like are +* represented by empty strings. Scopes corresponding to +* modules and classes are represented by the name of the +* module or class. +*/ +static vString* stringListToScope (const stringList* list) +{ + unsigned int i; + unsigned int chunks_output = 0; + vString* result = vStringNew (); + const unsigned int max = stringListCount (list); + for (i = 0; i < max; ++i) + { + vString* chunk = stringListItem (list, i); + if (vStringLength (chunk) > 0) + { + vStringCatS (result, (chunks_output++ > 0) ? "." : ""); + vStringCatS (result, vStringValue (chunk)); + } + } + return result; +}
- while ((line = fileReadLine ()) != NULL) { - const unsigned char *cp = line; - boolean is_singleton = FALSE; +/* +* Attempts to advance 's' past 'literal'. +* Returns TRUE if it did, FALSE (and leaves 's' where +* it was) otherwise. +*/ +static boolean canMatch (const unsigned char** s, const char* literal) +{ + const int literal_length = strlen (literal); + const unsigned char next_char = *(*s + literal_length); + if (strncmp ((const char*) *s, literal, literal_length) != 0) + { + return FALSE; + } + /* Additionally check that we're at the end of a token. */ + if ( ! (next_char == 0 || isspace (next_char) || next_char == '(')) + { + return FALSE; + } + *s += literal_length; + return TRUE; +}
- while (*cp != '\0') - { - if (*cp=='"' && - strncmp ((const char*) cp, """"", (size_t) 3) == 0) { - inMultilineString = (boolean) !inMultilineString; - cp += 3; - } - /* make sure you include the comments */ - if(*cp=='=' && strncmp((const char*)cp, "==begin", (size_t)7) == 0) { - inMultilineString = (boolean)!inMultilineString; - cp +=3; - } - /* mark the end of a comment */ - if( *cp=='=' && strncmp((const char*)cp, "==end", (size_t)5) == 0) { - inMultilineString = (boolean)0; - cp+=5; - } - if (inMultilineString || isspace ((int) *cp)) - ++cp; - else if (*cp == '#') - break; - else if (*cp == '=' && *(cp+1) != '=' && (isspace((int) *(cp-1)) || isalnum((int) *(cp-1)))) { +/* +* Attempts to advance 'cp' past a Ruby operator method name. Returns +* TRUE if successful (and copies the name into 'name'), FALSE otherwise. +*/ +static boolean parseRubyOperator (vString* name, const unsigned char** cp) +{ + static const char* RUBY_OPERATORS[] = { + "[]", "[]=", + "**", + "!", "~", "+@", "-@", + "*", "/", "%", + "+", "-", + ">>", "<<", + "&", + "^", "|", + "<=", "<", ">", ">=", + "<=>", "==", "===", "!=", "=~", "!~", + "`", + 0 + }; + int i; + for (i = 0; RUBY_OPERATORS[i] != 0; ++i) + { + if (canMatch (cp, RUBY_OPERATORS[i])) + { + vStringCatS (name, RUBY_OPERATORS[i]); + return TRUE; + } + } + return FALSE; +}
- // try to detect a variable by the = sign - enrico - // exlude all what not look like ' = ' or 'l=b' - const unsigned char *cur_pos = cp; // store the current position because - // we are going backwards with cp - // (think about what happens if we don't do this ;-)) - int is_member = 0; - cp--; +/* +* Emits a tag for the given 'name' of kind 'kind' at the current nesting. +*/ +static void emitRubyTag (vString* name, rubyKind kind) +{ + tagEntryInfo tag; + vString* scope;
- while (isspace ((int) *cp)) --cp; + vStringTerminate (name); + scope = stringListToScope (nesting);
- while (isalnum ((int) *cp) || *cp == '_') cp--; + initTagEntry (&tag, vStringValue (name)); + if (vStringLength (scope) > 0) { + tag.extensionFields.scope [0] = "class"; + tag.extensionFields.scope [1] = vStringValue (scope); + } + tag.kindName = RubyKinds [kind].name; + tag.kind = RubyKinds [kind].letter; + makeTagEntry (&tag);
- if (*cp == '@') is_member = 1; // handle @... - else if (!isspace((int) *cp)) - { - cp = cur_pos + 1; - continue; - } + stringListAdd (nesting, vStringNewCopy (name));
- // cp points to the char before the variable name, so go forward - cp++; + vStringClear (name); + vStringDelete (scope); +}
- while (cp != cur_pos && ! isspace((int) *cp)) - { - vStringPut (name, (int) *cp); - cp++; - } - vStringTerminate (name); - if (vStringLength (name) > 0) - { - if (is_member) makeSimpleTag (name, RubyKinds, K_MEMBER); - else makeSimpleTag (name, RubyKinds, K_VARIABLE); - } - vStringClear (name); - cp = cur_pos + 1; +/* Tests whether 'ch' is a character in 'list'. */ +static boolean charIsIn (char ch, const char* list) +{ + return (strchr (list, ch) != 0); +}
- } - else if (strncmp ((const char*) cp, "module", (size_t) 6) == 0) { - cp += 6; - if (isspace ((int) *cp)) { - while (isspace ((int) *cp)) - ++cp; - while (isalnum ((int) *cp) || *cp == '_' || *cp == ':') { - vStringPut (name, (int) *cp); - ++cp; - } - vStringTerminate (name); - makeSimpleTag (name, RubyKinds, K_MIXIN); - vStringClear (name); - } - } else if (strncmp ((const char*) cp, "class", (size_t) 5) == 0) { - cp += 5; - if (isspace ((int) *cp)) { - while (isspace ((int) *cp)) - ++cp; - while (isalnum ((int) *cp) || *cp == '_' || *cp == ':') { - vStringPut (name, (int) *cp); - ++cp; - } - vStringTerminate (name); - makeSimpleTag (name, RubyKinds, K_CLASS); - vStringClear (name); - } - } else if (strncmp ((const char*) cp, "def", (size_t) 3) == 0) { - cp += 3; - if (isspace ((int) *cp)) { - while (isspace ((int) *cp)) - ++cp; +/* Advances 'cp' over leading whitespace. */ +static void skipWhitespace (const unsigned char** cp) +{ + while (isspace (**cp)) + { + ++*cp; + } +}
- /* Put the valid characters allowed in a variable name - * in here. In ruby a variable name ENDING in ! means - * it changes the underlying data structure in place. - * A variable name ENDING in ? means that the function - * returns a bool. Functions should not start with these - * characters. - */ - while (isalnum ((int) *cp) || *cp == '_' || *cp == '!' || *cp =='?' || *cp=='.') { - /* classmethods are accesible only via class instance instead - * of object instance. This difference has to be outlined. - */ - if (*cp == '.') { - //class method - is_singleton = TRUE; - vStringTerminate (name); - vStringClear(name); - } else { - vStringPut (name, (int) *cp); - } - ++cp; - } - vStringTerminate (name); - if (is_singleton) { - makeSimpleTag (name, RubyKinds, K_SINGLETON); - } else { - makeSimpleTag (name, RubyKinds, K_METHOD); - } - vStringClear (name); - } - } else if (*cp != '\0') { - do - ++cp; - while (isalnum ((int) *cp) || *cp == '_'); - } - } - } - vStringDelete (name); +/* +* Copies the characters forming an identifier from *cp into +* name, leaving *cp pointing to the character after the identifier. +*/ +static rubyKind parseIdentifier ( + const unsigned char** cp, vString* name, rubyKind kind) +{ + /* Method names are slightly different to class and variable names. + * A method name may optionally end with a question mark, exclamation + * point or equals sign. These are all part of the name. + * A method name may also contain a period if it's a singleton method. + */ + const char* also_ok = (kind == K_METHOD) ? "_.?!=" : "_"; + + skipWhitespace (cp); + + /* Check for an anonymous (singleton) class such as "class << HTTP". */ + if (kind == K_CLASS && **cp == '<' && *(*cp + 1) == '<') + { + return K_UNDEFINED; + } + + /* Check for operators such as "def []=(key, val)". */ + if (kind == K_METHOD || kind == K_SINGLETON) + { + if (parseRubyOperator (name, cp)) + { + return kind; + } + } + + /* Copy the identifier into 'name'. */ + while (**cp != 0 && (isalnum (**cp) || charIsIn (**cp, also_ok))) + { + char last_char = **cp; + + vStringPut (name, last_char); + ++*cp; + + if (kind == K_METHOD) + { + /* Recognize singleton methods. */ + if (last_char == '.') + { + vStringTerminate (name); + vStringClear (name); + return parseIdentifier (cp, name, K_SINGLETON); + } + + /* Recognize characters which mark the end of a method name. */ + if (charIsIn (last_char, "?!=")) + { + break; + } + } + } + return kind; }
+static void readAndEmitTag (const unsigned char** cp, rubyKind expected_kind) +{ + if (isspace (**cp)) + { + vString *name = vStringNew (); + rubyKind actual_kind = parseIdentifier (cp, name, expected_kind); + + if (actual_kind == K_UNDEFINED || vStringLength (name) == 0) + { + /* + * What kind of tags should we create for code like this? + * + * %w(self.clfloor clfloor).each do |name| + * module_eval <<-"end;" + * def #{name}(x, y=1) + * q, r = x.divmod(y) + * q = q.to_i + * return q, r + * end + * end; + * end + * + * Or this? + * + * class << HTTP + * + * For now, we don't create any. + */ + } + else + { + emitRubyTag (name, actual_kind); + } + vStringDelete (name); + } +} + +static void enterUnnamedScope (void) +{ + stringListAdd (nesting, vStringNewInit ("")); +} + +static void findRubyTags (void) +{ + const unsigned char *line; + boolean inMultiLineComment = FALSE; + + nesting = stringListNew (); + + /* FIXME: this whole scheme is wrong, because Ruby isn't line-based. + * You could perfectly well write: + * + * def + * method + * puts("hello") + * end + * + * if you wished, and this function would fail to recognize anything. + */ + while ((line = fileReadLine ()) != NULL) + { + const unsigned char *cp = line; + + if (canMatch (&cp, "=begin")) + { + inMultiLineComment = TRUE; + continue; + } + if (canMatch (&cp, "=end")) + { + inMultiLineComment = FALSE; + continue; + } + + skipWhitespace (&cp); + + /* Avoid mistakenly starting a scope for modifiers such as + * + * return if <exp> + * + * FIXME: this is fooled by code such as + * + * result = if <exp> + * <a> + * else + * <b> + * end + * + * FIXME: we're also fooled if someone does something heinous such as + * + * puts("hello") \ + * unless <exp> + */ + if (canMatch (&cp, "case") || canMatch (&cp, "for") || + canMatch (&cp, "if") || canMatch (&cp, "unless") || + canMatch (&cp, "while")) + { + enterUnnamedScope (); + } + + /* + * "module M", "class C" and "def m" should only be at the beginning + * of a line. + */ + if (canMatch (&cp, "module")) + { + readAndEmitTag (&cp, K_MODULE); + } + else if (canMatch (&cp, "class")) + { + readAndEmitTag (&cp, K_CLASS); + } + else if (canMatch (&cp, "def")) + { + readAndEmitTag (&cp, K_METHOD); + } + + while (*cp != '\0') + { + /* FIXME: we don't cope with here documents, or string literals, + * or regular expression literals, or ... you get the idea. + * Hopefully, the restriction above that insists on seeing + * definitions at the starts of lines should keep us out of + * mischief. + */ + if (inMultiLineComment || isspace (*cp)) + { + ++cp; + } + else if (*cp == '#') + { + /* FIXME: this is wrong, but there *probably* won't be a + * definition after an interpolated string (where # doesn't + * mean 'comment'). + */ + break; + } + else if (canMatch (&cp, "begin") || canMatch (&cp, "do")) + { + enterUnnamedScope (); + } + else if (canMatch (&cp, "end") && stringListCount (nesting) > 0) + { + /* Leave the most recent scope. */ + vStringDelete (stringListLast (nesting)); + stringListRemoveLast (nesting); + } + else if (*cp != '\0') + { + do + ++cp; + while (isalnum (*cp) || *cp == '_'); + } + } + } + stringListDelete (nesting); +} + extern parserDefinition* RubyParser (void) { - static const char *const extensions [] = { "rb", "rhtml", NULL }; - parserDefinition* def = parserNew ("Ruby"); - def->kinds = RubyKinds; - def->kindCount = KIND_COUNT (RubyKinds); - def->extensions = extensions; - def->parser = findRubyTags; - return def; + static const char *const extensions [] = { "rb", "ruby", "rhtml", NULL }; + parserDefinition* def = parserNew ("Ruby"); + def->kinds = RubyKinds; + def->kindCount = KIND_COUNT (RubyKinds); + def->extensions = extensions; + def->parser = findRubyTags; + return def; } + +/* vi:set tabstop=4 shiftwidth=4: */
Modified: trunk/tagmanager/strlist.c =================================================================== --- trunk/tagmanager/strlist.c 2007-05-23 14:04:20 UTC (rev 1562) +++ trunk/tagmanager/strlist.c 2007-05-23 14:26:52 UTC (rev 1563) @@ -1,6 +1,7 @@ /* +* $Id$ * -* Copyright (c) 1999-2001, Darren Hiebert +* Copyright (c) 1999-2002, Darren Hiebert * * This source code is released for free distribution under the terms of the * GNU General Public License. @@ -11,14 +12,13 @@ /* * INCLUDE FILES */ -#include "general.h" /* must always come first */ +#include "general.h" /* must always come first */
#include <string.h> #ifdef HAVE_FNMATCH_H # include <fnmatch.h> #endif
- #include "main.h" #include "read.h" #include "strlist.h" @@ -29,192 +29,252 @@
extern stringList *stringListNew (void) { - stringList* const result = xMalloc (1, stringList); - result->max = 0; - result->count = 0; - result->list = NULL; - return result; + stringList* const result = xMalloc (1, stringList); + result->max = 0; + result->count = 0; + result->list = NULL; + return result; }
extern void stringListAdd (stringList *const current, vString *string) { - enum { incrementalIncrease = 10 }; - Assert (current != NULL); - if (current->list == NULL) - { - Assert (current->max == 0); - current->count = 0; - current->max = incrementalIncrease; - current->list = xMalloc (current->max, vString*); - } - else if (current->count == current->max) - { - current->max += incrementalIncrease; - current->list = xRealloc (current->list, current->max, vString*); - } - current->list [current->count++] = string; + enum { incrementalIncrease = 10 }; + Assert (current != NULL); + if (current->list == NULL) + { + Assert (current->max == 0); + current->count = 0; + current->max = incrementalIncrease; + current->list = xMalloc (current->max, vString*); + } + else if (current->count == current->max) + { + current->max += incrementalIncrease; + current->list = xRealloc (current->list, current->max, vString*); + } + current->list [current->count++] = string; }
+extern void stringListRemoveLast (stringList *const current) +{ + Assert (current != NULL); + Assert (current->count > 0); + --current->count; + current->list [current->count] = NULL; +} + /* Combine list `from' into `current', deleting `from' */ -extern void stringListCombine (stringList *const current, stringList *const from) +extern void stringListCombine ( + stringList *const current, stringList *const from) { - unsigned int i; - Assert (current != NULL); - Assert (from != NULL); - for (i = 0 ; i < from->count ; ++i) - { - stringListAdd (current, from->list [i]); - from->list [i] = NULL; - } - stringListDelete (from); + unsigned int i; + Assert (current != NULL); + Assert (from != NULL); + for (i = 0 ; i < from->count ; ++i) + { + stringListAdd (current, from->list [i]); + from->list [i] = NULL; + } + stringListDelete (from); }
extern stringList* stringListNewFromArgv (const char* const* const argv) { - stringList* const result = stringListNew (); - const char *const *p; - Assert (argv != NULL); - for (p = argv ; *p != NULL ; ++p) - stringListAdd (result, vStringNewInit (*p)); - return result; + stringList* const result = stringListNew (); + const char *const *p; + Assert (argv != NULL); + for (p = argv ; *p != NULL ; ++p) + stringListAdd (result, vStringNewInit (*p)); + return result; }
extern stringList* stringListNewFromFile (const char* const fileName) { - stringList* result = NULL; - FILE* const fp = fopen (fileName, "r"); - if (fp == NULL) - error (FATAL | PERROR, "cannot open "%s"", fileName); - else - { - result = stringListNew (); - while (! feof (fp)) + stringList* result = NULL; + FILE* const fp = fopen (fileName, "r"); + if (fp != NULL) { - vString* const str = vStringNew (); - readLine (str, fp); - vStringStripTrailing (str); - if (vStringLength (str) > 0) - stringListAdd (result, str); - else - vStringDelete (str); + result = stringListNew (); + while (! feof (fp)) + { + vString* const str = vStringNew (); + readLine (str, fp); + vStringStripTrailing (str); + if (vStringLength (str) > 0) + stringListAdd (result, str); + else + vStringDelete (str); + } } - } - return result; + return result; }
extern unsigned int stringListCount (const stringList *const current) { - Assert (current != NULL); - return current->count; + Assert (current != NULL); + return current->count; }
-extern vString* stringListItem (const stringList *const current, - const unsigned int indx) +extern vString* stringListItem ( + const stringList *const current, const unsigned int indx) { - Assert (current != NULL); - return current->list [indx]; + Assert (current != NULL); + return current->list [indx]; }
+extern vString* stringListLast (const stringList *const current) +{ + Assert (current != NULL); + Assert (current->count > 0); + return current->list [current->count - 1]; +} + extern void stringListClear (stringList *const current) { - unsigned int i; - Assert (current != NULL); - for (i = 0 ; i < current->count ; ++i) - { - vStringDelete (current->list [i]); - current->list [i] = NULL; - } - current->count = 0; + unsigned int i; + Assert (current != NULL); + for (i = 0 ; i < current->count ; ++i) + { + vStringDelete (current->list [i]); + current->list [i] = NULL; + } + current->count = 0; }
extern void stringListDelete (stringList *const current) { - if (current != NULL) - { - if (current->list != NULL) + if (current != NULL) { - stringListClear (current); - eFree (current->list); - current->list = NULL; + if (current->list != NULL) + { + stringListClear (current); + eFree (current->list); + current->list = NULL; + } + current->max = 0; + current->count = 0; + eFree (current); } - current->max = 0; - current->count = 0; - eFree (current); - } }
-extern boolean stringListHas (const stringList *const current, - const char *const str) +static boolean compareString ( + const char *const string, vString *const itm) { - boolean result = FALSE; - unsigned int i; - Assert (current != NULL); - for (i = 0 ; ! result && i < current->count ; ++i) - result = (boolean) (strcmp (str, vStringValue (current->list [i]))==0); - return result; + return (boolean) (strcmp (string, vStringValue (itm)) == 0); }
-extern boolean stringListHasInsensitive (const stringList *const current, - const char *const str) +static boolean compareStringInsensitive ( + const char *const string, vString *const itm) { - boolean result = FALSE; - unsigned int i; - Assert (current != NULL); - for (i = 0 ; ! result && i < current->count ; ++i) - result = (boolean) (stricmp (str, vStringValue (current->list [i]))==0); - return result; + return (boolean) (strcasecmp (string, vStringValue (itm)) == 0); }
-extern boolean stringListHasFile (const stringList *const current, - const char *const file) +static int stringListIndex ( + const stringList *const current, + const char *const string, + boolean (*test)(const char *s, vString *const vs)) { - boolean result = FALSE; - unsigned int i; - Assert (current != NULL); - for (i = 0 ; ! result && i < current->count ; ++i) - result = (boolean) (isSameFile (file, vStringValue (current->list [i]))); - return result; + int result = -1; + unsigned int i; + Assert (current != NULL); + Assert (string != NULL); + Assert (test != NULL); + for (i = 0 ; result == -1 && i < current->count ; ++i) + if ((*test)(string, current->list [i])) + result = i; + return result; }
-extern boolean stringListExtensionMatched (const stringList* const list, - const char* const extension) +extern boolean stringListHas ( + const stringList *const current, const char *const string) { + boolean result = FALSE; + Assert (current != NULL); + result = stringListIndex (current, string, compareString) != -1; + return result; +} + +extern boolean stringListHasInsensitive ( + const stringList *const current, const char *const string) +{ + boolean result = FALSE; + Assert (current != NULL); + Assert (string != NULL); + result = stringListIndex (current, string, compareStringInsensitive) != -1; + return result; +} + +extern boolean stringListHasTest ( + const stringList *const current, boolean (*test)(const char *s)) +{ + boolean result = FALSE; + unsigned int i; + Assert (current != NULL); + for (i = 0 ; ! result && i < current->count ; ++i) + result = (*test)(vStringValue (current->list [i])); + return result; +} + +extern boolean stringListRemoveExtension ( + stringList* const current, const char* const extension) +{ + boolean result = FALSE; + int where; #ifdef CASE_INSENSITIVE_FILENAMES - return stringListHasInsensitive (list, extension); + where = stringListIndex (current, extension, compareStringInsensitive); #else - return stringListHas (list, extension); + where = stringListIndex (current, extension, compareString); #endif + if (where != -1) + { + memmove (current->list + where, current->list + where + 1, + (current->count - where) * sizeof (*current->list)); + current->list [current->count - 1] = NULL; + --current->count; + result = TRUE; + } + return result; }
-static boolean fileNameMatched (const vString* const vpattern, - const char* const fileName) +extern boolean stringListExtensionMatched ( + const stringList* const current, const char* const extension) { - const char* const pattern = vStringValue (vpattern); +#ifdef CASE_INSENSITIVE_FILENAMES + return stringListHasInsensitive (current, extension); +#else + return stringListHas (current, extension); +#endif +} + +static boolean fileNameMatched ( + const vString* const vpattern, const char* const fileName) +{ + const char* const pattern = vStringValue (vpattern); #if defined (HAVE_FNMATCH) - return (boolean) (fnmatch (pattern, fileName, 0) == 0); + return (boolean) (fnmatch (pattern, fileName, 0) == 0); #elif defined (CASE_INSENSITIVE_FILENAMES) - return (boolean) (stricmp (pattern, fileName) == 0); + return (boolean) (strcasecmp (pattern, fileName) == 0); #else - return (boolean) (strcmp (pattern, fileName) == 0); + return (boolean) (strcmp (pattern, fileName) == 0); #endif }
-extern boolean stringListFileMatched (const stringList* const list, - const char* const fileName) +extern boolean stringListFileMatched ( + const stringList* const current, const char* const fileName) { - boolean result = FALSE; - unsigned int i; - for (i = 0 ; ! result && i < stringListCount (list) ; ++i) - result = fileNameMatched (stringListItem (list, i), fileName); - return result; + boolean result = FALSE; + unsigned int i; + for (i = 0 ; ! result && i < stringListCount (current) ; ++i) + result = fileNameMatched (stringListItem (current, i), fileName); + return result; }
extern void stringListPrint (const stringList *const current) { - unsigned int i; - Assert (current != NULL); - for (i = 0 ; i < current->count ; ++i) - printf ("%s%s", (i > 0) ? ", " : "", vStringValue (current->list [i])); + unsigned int i; + Assert (current != NULL); + for (i = 0 ; i < current->count ; ++i) + printf ("%s%s", (i > 0) ? ", " : "", vStringValue (current->list [i])); }
-/* vi:set tabstop=8 shiftwidth=4: */ +/* vi:set tabstop=4 shiftwidth=4: */
Modified: trunk/tagmanager/strlist.h =================================================================== --- trunk/tagmanager/strlist.h 2007-05-23 14:04:20 UTC (rev 1562) +++ trunk/tagmanager/strlist.h 2007-05-23 14:26:52 UTC (rev 1563) @@ -1,6 +1,7 @@ /* +* $Id$ * -* Copyright (c) 1999-2001, Darren Hiebert +* Copyright (c) 1999-2002, Darren Hiebert * * This source code is released for free distribution under the terms of the * GNU General Public License. @@ -13,7 +14,7 @@ /* * INCLUDE FILES */ -#include "general.h" /* must always come first */ +#include "general.h" /* must always come first */
#include "vstring.h"
@@ -21,9 +22,9 @@ * DATA DECLARATIONS */ typedef struct sStringList { - unsigned int max; - unsigned int count; - vString **list; + unsigned int max; + unsigned int count; + vString **list; } stringList;
/* @@ -31,20 +32,23 @@ */ extern stringList *stringListNew (void); extern void stringListAdd (stringList *const current, vString *string); +extern void stringListRemoveLast (stringList *const current); extern void stringListCombine (stringList *const current, stringList *const from); extern stringList* stringListNewFromArgv (const char* const* const list); extern stringList* stringListNewFromFile (const char* const fileName); extern void stringListClear (stringList *const current); extern unsigned int stringListCount (const stringList *const current); extern vString* stringListItem (const stringList *const current, const unsigned int indx); +extern vString* stringListLast (const stringList *const current); extern void stringListDelete (stringList *const current); extern boolean stringListHasInsensitive (const stringList *const current, const char *const string); extern boolean stringListHas (const stringList *const current, const char *const string); -extern boolean stringListHasFile (const stringList *const current, const char *const file); +extern boolean stringListHasTest (const stringList *const current, boolean (*test)(const char *s)); +extern boolean stringListRemoveExtension (stringList* const current, const char* const extension); extern boolean stringListExtensionMatched (const stringList* const list, const char* const extension); extern boolean stringListFileMatched (const stringList* const list, const char* const str); extern void stringListPrint (const stringList *const current);
-#endif /* _STRLIST_H */ +#endif /* _STRLIST_H */
-/* vi:set tabstop=8 shiftwidth=4: */ +/* vi:set tabstop=4 shiftwidth=4: */
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.