Backport changes from CTags SVN to fix parse problems in the Ruby parser.

Add filetype extension "*.ruby".


git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@1563 ea778897-0a13-0410-b9d1-a72fbfd435f5
This commit is contained in:
Enrico Tröger 2007-05-23 14:26:52 +00:00
parent bd205a7374
commit 12104026d0
6 changed files with 556 additions and 286 deletions

View File

@ -4,6 +4,11 @@
* geany.glade, src/dialogs.c, src/geany.h, src/interface.c, * geany.glade, src/dialogs.c, src/geany.h, src/interface.c,
src/keyfile.c, src/main.c, src/prefs.c: src/keyfile.c, src/main.c, src/prefs.c:
Add default startup directory option (closes #1704988). 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> 2007-05-23 Nick Treleaven <nick.treleaven@btinternet.com>

View File

@ -14,7 +14,7 @@ Perl=*.pl;*.perl;*.pm;*.agi;*.pod;
PHP=*.php;*.php3;*.php4;*.php5;*.phtml; PHP=*.php;*.php3;*.php4;*.php5;*.phtml;
Javascript=*.js; Javascript=*.js;
Python=*.py;*.pyw; Python=*.py;*.pyw;
Ruby=*.rb;*.rhtml; Ruby=*.rb;*.rhtml;*.ruby;
Tcl=*.tcl;*.tk;*.wish; Tcl=*.tcl;*.tk;*.wish;
Lua=*.lua; Lua=*.lua;
Ferite=*.fe; Ferite=*.fe;

View File

@ -301,7 +301,7 @@ void filetypes_init_types()
filetypes[GEANY_FILETYPES_RUBY]->has_tags = TRUE; filetypes[GEANY_FILETYPES_RUBY]->has_tags = TRUE;
filetypes[GEANY_FILETYPES_RUBY]->title = g_strdup(_("Ruby source file")); filetypes[GEANY_FILETYPES_RUBY]->title = g_strdup(_("Ruby source file"));
filetypes[GEANY_FILETYPES_RUBY]->extension = g_strdup("rb"); 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]->style_func_ptr = styleset_ruby;
filetypes[GEANY_FILETYPES_RUBY]->comment_open = g_strdup("#"); filetypes[GEANY_FILETYPES_RUBY]->comment_open = g_strdup("#");
filetypes[GEANY_FILETYPES_RUBY]->comment_close = NULL; filetypes[GEANY_FILETYPES_RUBY]->comment_close = NULL;

View File

@ -1,17 +1,15 @@
/* /*
* $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) 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 * This source code is released for free distribution under the terms of the
* GNU General Public License. * GNU General Public License.
* *
* This module contains functions for generating tags for Ruby language * This module contains functions for generating tags for Ruby language
* files. * files.
*
* Copyright (c) 2002, Matthias Veit <matthias_veit@yahoo.de>
* Enable parsing of class (in ruby: singleton) methods and mixins.
*
*/ */
/* /*
@ -21,174 +19,375 @@
#include <string.h> #include <string.h>
#include "entry.h"
#include "parse.h" #include "parse.h"
#include "read.h" #include "read.h"
#include "vstring.h" #include "vstring.h"
/* /*
* DATA DEFINITIONS * DATA DECLARATIONS
*/ */
typedef enum { 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; } rubyKind;
/*
* DATA DEFINITIONS
*/
static kindOption RubyKinds [] = { static kindOption RubyKinds [] = {
{ TRUE, 'c', "class", "classes" }, { TRUE, 'c', "class", "classes" },
{ TRUE, 'f', "function", "methods" }, { TRUE, 'f', "method", "methods" },
{ TRUE, 'm', "member", "singleton_methods" }, { TRUE, 'm', "module", "modules" },
{ TRUE, 'd', "macro", "mixins" }, { TRUE, 'F', "singleton method", "singleton methods" }
{ TRUE, 'v', "variable", "variable" },
{ TRUE, 's', "struct", "member" }
}; };
static stringList* nesting = 0;
/* /*
* FUNCTION DEFINITIONS * FUNCTION DEFINITIONS
*/ */
static void findRubyTags (void) { /*
vString *name = vStringNew (); * Returns a string describing the scope in 'list'.
const unsigned char *line; * We record the current scope as a list of entered scopes.
boolean inMultilineString = FALSE; * Scopes corresponding to 'if' statements and the like are
* represented by empty strings. Scopes corresponding to
while ((line = fileReadLine ()) != NULL) { * modules and classes are represented by the name of the
const unsigned char *cp = line; * module or class.
boolean is_singleton = FALSE; */
static vString* stringListToScope (const stringList* list)
while (*cp != '\0')
{ {
if (*cp=='"' && unsigned int i;
strncmp ((const char*) cp, "\"\"\"", (size_t) 3) == 0) { unsigned int chunks_output = 0;
inMultilineString = (boolean) !inMultilineString; vString* result = vStringNew ();
cp += 3; 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));
} }
/* 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 */ return result;
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 == '#') * 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;
}
/*
* 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;
}
/*
* 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;
vStringTerminate (name);
scope = stringListToScope (nesting);
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);
stringListAdd (nesting, vStringNewCopy (name));
vStringClear (name);
vStringDelete (scope);
}
/* Tests whether 'ch' is a character in 'list'. */
static boolean charIsIn (char ch, const char* list)
{
return (strchr (list, ch) != 0);
}
/* Advances 'cp' over leading whitespace. */
static void skipWhitespace (const unsigned char** cp)
{
while (isspace (**cp))
{
++*cp;
}
}
/*
* 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; break;
else if (*cp == '=' && *(cp+1) != '=' && (isspace((int) *(cp-1)) || isalnum((int) *(cp-1)))) { }
}
}
return kind;
}
// try to detect a variable by the = sign - enrico static void readAndEmitTag (const unsigned char** cp, rubyKind expected_kind)
// 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--;
while (isspace ((int) *cp)) --cp;
while (isalnum ((int) *cp) || *cp == '_') cp--;
if (*cp == '@') is_member = 1; // handle @...
else if (!isspace((int) *cp))
{ {
cp = cur_pos + 1; 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; continue;
} }
// cp points to the char before the variable name, so go forward skipWhitespace (&cp);
cp++;
while (cp != cur_pos && ! isspace((int) *cp)) /* Avoid mistakenly starting a scope for modifiers such as
{ *
vStringPut (name, (int) *cp); * return if <exp>
cp++; *
} * FIXME: this is fooled by code such as
vStringTerminate (name); *
if (vStringLength (name) > 0) * result = if <exp>
{ * <a>
if (is_member) makeSimpleTag (name, RubyKinds, K_MEMBER); * else
else makeSimpleTag (name, RubyKinds, K_VARIABLE); * <b>
} * end
vStringClear (name); *
cp = cur_pos + 1; * FIXME: we're also fooled if someone does something heinous such as
*
} * puts("hello") \
else if (strncmp ((const char*) cp, "module", (size_t) 6) == 0) { * unless <exp>
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;
/* 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=='.') { if (canMatch (&cp, "case") || canMatch (&cp, "for") ||
/* classmethods are accesible only via class instance instead canMatch (&cp, "if") || canMatch (&cp, "unless") ||
* of object instance. This difference has to be outlined. canMatch (&cp, "while"))
*/ {
if (*cp == '.') { enterUnnamedScope ();
//class method
is_singleton = TRUE;
vStringTerminate (name);
vStringClear(name);
} else {
vStringPut (name, (int) *cp);
} }
/*
* "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; ++cp;
} }
vStringTerminate (name); else if (*cp == '#')
if (is_singleton) { {
makeSimpleTag (name, RubyKinds, K_SINGLETON); /* FIXME: this is wrong, but there *probably* won't be a
} else { * definition after an interpolated string (where # doesn't
makeSimpleTag (name, RubyKinds, K_METHOD); * mean 'comment').
*/
break;
} }
vStringClear (name); else if (canMatch (&cp, "begin") || canMatch (&cp, "do"))
{
enterUnnamedScope ();
} }
} else if (*cp != '\0') { else if (canMatch (&cp, "end") && stringListCount (nesting) > 0)
{
/* Leave the most recent scope. */
vStringDelete (stringListLast (nesting));
stringListRemoveLast (nesting);
}
else if (*cp != '\0')
{
do do
++cp; ++cp;
while (isalnum ((int) *cp) || *cp == '_'); while (isalnum (*cp) || *cp == '_');
} }
} }
} }
vStringDelete (name); stringListDelete (nesting);
} }
extern parserDefinition* RubyParser (void) extern parserDefinition* RubyParser (void)
{ {
static const char *const extensions [] = { "rb", "rhtml", NULL }; static const char *const extensions [] = { "rb", "ruby", "rhtml", NULL };
parserDefinition* def = parserNew ("Ruby"); parserDefinition* def = parserNew ("Ruby");
def->kinds = RubyKinds; def->kinds = RubyKinds;
def->kindCount = KIND_COUNT (RubyKinds); def->kindCount = KIND_COUNT (RubyKinds);
@ -196,3 +395,5 @@ extern parserDefinition* RubyParser (void)
def->parser = findRubyTags; def->parser = findRubyTags;
return def; return def;
} }
/* vi:set tabstop=4 shiftwidth=4: */

View File

@ -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 * This source code is released for free distribution under the terms of the
* GNU General Public License. * GNU General Public License.
@ -18,7 +19,6 @@
# include <fnmatch.h> # include <fnmatch.h>
#endif #endif
#include "main.h" #include "main.h"
#include "read.h" #include "read.h"
#include "strlist.h" #include "strlist.h"
@ -55,8 +55,17 @@ extern void stringListAdd (stringList *const current, vString *string)
current->list [current->count++] = string; 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' */ /* 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; unsigned int i;
Assert (current != NULL); Assert (current != NULL);
@ -83,9 +92,7 @@ extern stringList* stringListNewFromFile (const char* const fileName)
{ {
stringList* result = NULL; stringList* result = NULL;
FILE* const fp = fopen (fileName, "r"); FILE* const fp = fopen (fileName, "r");
if (fp == NULL) if (fp != NULL)
error (FATAL | PERROR, "cannot open \"%s\"", fileName);
else
{ {
result = stringListNew (); result = stringListNew ();
while (! feof (fp)) while (! feof (fp))
@ -108,13 +115,20 @@ extern unsigned int stringListCount (const stringList *const current)
return current->count; return current->count;
} }
extern vString* stringListItem (const stringList *const current, extern vString* stringListItem (
const unsigned int indx) const stringList *const current, const unsigned int indx)
{ {
Assert (current != NULL); Assert (current != NULL);
return current->list [indx]; 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) extern void stringListClear (stringList *const current)
{ {
unsigned int i; unsigned int i;
@ -143,69 +157,115 @@ extern void stringListDelete (stringList *const current)
} }
} }
extern boolean stringListHas (const stringList *const current, static boolean compareString (
const char *const str) const char *const string, vString *const itm)
{
return (boolean) (strcmp (string, vStringValue (itm)) == 0);
}
static boolean compareStringInsensitive (
const char *const string, vString *const itm)
{
return (boolean) (strcasecmp (string, vStringValue (itm)) == 0);
}
static int stringListIndex (
const stringList *const current,
const char *const string,
boolean (*test)(const char *s, vString *const vs))
{
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 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; boolean result = FALSE;
unsigned int i; unsigned int i;
Assert (current != NULL); Assert (current != NULL);
for (i = 0 ; ! result && i < current->count ; ++i) for (i = 0 ; ! result && i < current->count ; ++i)
result = (boolean) (strcmp (str, vStringValue (current->list [i]))==0); result = (*test)(vStringValue (current->list [i]));
return result; return result;
} }
extern boolean stringListHasInsensitive (const stringList *const current, extern boolean stringListRemoveExtension (
const char *const str) stringList* const current, const char* const extension)
{ {
boolean result = FALSE; boolean result = FALSE;
unsigned int i; int where;
Assert (current != NULL); #ifdef CASE_INSENSITIVE_FILENAMES
for (i = 0 ; ! result && i < current->count ; ++i) where = stringListIndex (current, extension, compareStringInsensitive);
result = (boolean) (stricmp (str, vStringValue (current->list [i]))==0); #else
return result; where = stringListIndex (current, extension, compareString);
} #endif
if (where != -1)
extern boolean stringListHasFile (const stringList *const current,
const char *const file)
{ {
boolean result = FALSE; memmove (current->list + where, current->list + where + 1,
unsigned int i; (current->count - where) * sizeof (*current->list));
Assert (current != NULL); current->list [current->count - 1] = NULL;
for (i = 0 ; ! result && i < current->count ; ++i) --current->count;
result = (boolean) (isSameFile (file, vStringValue (current->list [i]))); result = TRUE;
}
return result; return result;
} }
extern boolean stringListExtensionMatched (const stringList* const list, extern boolean stringListExtensionMatched (
const char* const extension) const stringList* const current, const char* const extension)
{ {
#ifdef CASE_INSENSITIVE_FILENAMES #ifdef CASE_INSENSITIVE_FILENAMES
return stringListHasInsensitive (list, extension); return stringListHasInsensitive (current, extension);
#else #else
return stringListHas (list, extension); return stringListHas (current, extension);
#endif #endif
} }
static boolean fileNameMatched (const vString* const vpattern, static boolean fileNameMatched (
const char* const fileName) const vString* const vpattern, const char* const fileName)
{ {
const char* const pattern = vStringValue (vpattern); const char* const pattern = vStringValue (vpattern);
#if defined (HAVE_FNMATCH) #if defined (HAVE_FNMATCH)
return (boolean) (fnmatch (pattern, fileName, 0) == 0); return (boolean) (fnmatch (pattern, fileName, 0) == 0);
#elif defined (CASE_INSENSITIVE_FILENAMES) #elif defined (CASE_INSENSITIVE_FILENAMES)
return (boolean) (stricmp (pattern, fileName) == 0); return (boolean) (strcasecmp (pattern, fileName) == 0);
#else #else
return (boolean) (strcmp (pattern, fileName) == 0); return (boolean) (strcmp (pattern, fileName) == 0);
#endif #endif
} }
extern boolean stringListFileMatched (const stringList* const list, extern boolean stringListFileMatched (
const char* const fileName) const stringList* const current, const char* const fileName)
{ {
boolean result = FALSE; boolean result = FALSE;
unsigned int i; unsigned int i;
for (i = 0 ; ! result && i < stringListCount (list) ; ++i) for (i = 0 ; ! result && i < stringListCount (current) ; ++i)
result = fileNameMatched (stringListItem (list, i), fileName); result = fileNameMatched (stringListItem (current, i), fileName);
return result; return result;
} }
@ -217,4 +277,4 @@ extern void stringListPrint (const stringList *const current)
printf ("%s%s", (i > 0) ? ", " : "", vStringValue (current->list [i])); printf ("%s%s", (i > 0) ? ", " : "", vStringValue (current->list [i]));
} }
/* vi:set tabstop=8 shiftwidth=4: */ /* vi:set tabstop=4 shiftwidth=4: */

View File

@ -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 * This source code is released for free distribution under the terms of the
* GNU General Public License. * GNU General Public License.
@ -31,20 +32,23 @@ typedef struct sStringList {
*/ */
extern stringList *stringListNew (void); extern stringList *stringListNew (void);
extern void stringListAdd (stringList *const current, vString *string); extern void stringListAdd (stringList *const current, vString *string);
extern void stringListRemoveLast (stringList *const current);
extern void stringListCombine (stringList *const current, stringList *const from); extern void stringListCombine (stringList *const current, stringList *const from);
extern stringList* stringListNewFromArgv (const char* const* const list); extern stringList* stringListNewFromArgv (const char* const* const list);
extern stringList* stringListNewFromFile (const char* const fileName); extern stringList* stringListNewFromFile (const char* const fileName);
extern void stringListClear (stringList *const current); extern void stringListClear (stringList *const current);
extern unsigned int stringListCount (const stringList *const current); extern unsigned int stringListCount (const stringList *const current);
extern vString* stringListItem (const stringList *const current, const unsigned int indx); 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 void stringListDelete (stringList *const current);
extern boolean stringListHasInsensitive (const stringList *const current, const char *const string); extern boolean stringListHasInsensitive (const stringList *const current, const char *const string);
extern boolean stringListHas (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 stringListExtensionMatched (const stringList* const list, const char* const extension);
extern boolean stringListFileMatched (const stringList* const list, const char* const str); extern boolean stringListFileMatched (const stringList* const list, const char* const str);
extern void stringListPrint (const stringList *const current); 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: */