SF.net SVN: geany: [1563] trunk
eht16 at users.sourceforge.net
eht16 at xxxxx
Wed May 23 14:26:53 UTC 2007
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 at 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 at mediaone.net>
+* Copyright (c) 2002 Matthias Veit <matthias_veit at yahoo.de>
+* Copyright (c) 2004 Elliott Hughes <enh at 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 at 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.
More information about the Commits
mailing list