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