Page Menu
Home
Software Heritage
Search
Configure Global Search
Log In
Files
F9123632
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
29 KB
Subscribers
None
View Options
diff --git a/docs/f-tracking.rst b/docs/f-tracking.rst
index d5063908..3afe836b 100644
--- a/docs/f-tracking.rst
+++ b/docs/f-tracking.rst
@@ -1,404 +1,420 @@
Tracking other projects
======================================================================
This is working note for tracking activities other projects,
expecially activity at exuberant-ctags.
I put this as the top of this hacking guide because
I consider tracking activities as the first class fruits
of this project.
exuberant-ctags
----------------------------------------------------------------------
subversion
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* status
Revisions up to <r815> are merged except:
NOTHING HERE NOW
(Mon Sep 22 12:41:32 2014 by yamato)
* howto
::
<svn>
=> <git: local exuberant repo>
=> <git: local fishman repo>
1. prepare your own fishman repo: a local git repo cloned from github.
You may know how to do it :)
::
$ git clone https://github.com/fishman/ctags.git
2. prepare exuberant SVN repo: a local git repo clone from exuberant svn tree.
The original clone is already part of fishman tree.
To initialize your git repository with the required subversion information do ::
$ git svn init https://svn.code.sf.net/p/ctags/code/trunk
$ git update-ref refs/remotes/git-svn refs/remotes/origin/sourceforge
and then ::
$ git svn fetch
$ git svn rebase
to get the latest changes and reflect it to the local copy.
3. merge
TODO
4. cherry-pick
4.1. Make a branch at local fishman repo and switch to it.
4.2. Do cherry-pick like::
$ git cherry-pick -s -x c81a8ce
You can find commit id on the another terminal
<git: local exuberant repo>::
$ git log
or ::
$ git log --oneline
If conflicts are occurred in cherry-picking, you can
abort/reset cherry-picking with::
$ git reset --hard
<git: local fishman repo>
at the branch for picking.
bugs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<358> Vim parser: Segmentation fault when reaing empty vim file
* directly contributed by the original author of bug report and patch::
e0f854f0100e7a3cb8b959a23d6036e43f6b6c85
* it is fixed in sf, too::
5d774f6022a1af71fa5866994699aafce0253085
<355> Error when parsing empty file (OCaml)
* fixed::
02ec2066b5be6b129eba49685bd0b17fef4acfa
<341> Introducing ctags.conf.d
* merged the improved version::
216880c5287e0421d9c49898d983144db61c83aa
+ <271> regex callback is broken; <320> [PATCH] fix regex callback match count
+ * merged patch (with updated bug number)::
+
+ a12b3a24b62d6535a968e076675f68bac9ad32ba
+
`patches <https://sourceforge.net/p/ctags/patches/%d>`_
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tracking the tickets in patch tracker is quite fruitful.
Patches are always there. So it is easy to evaluate the value:)
[(<]TICKET#[>)] TITLE
* STATUS
+ MORE STATUS
<TICKET#>
means the ticket is closed from the view of fishman tree
developers. We don't have to take time for this ticket.
(TICKET#)
means the ticket is still opened from the view of fishman
tree developers. We don't have to take time for this ticket.
----
<84> C++11 new using semantics
* solved by annother implementation::
c93e3bfa05b70d7fbc2539454c957eb2169e16b3
502355489b1ba748b1a235641bbd512ba6da315e
<83> New full non-regex PHP parser
* contributed by the original author
<82> Support for comments in .ctags files
* contributed by the original author::
cab4735e4f99ce23c52b78dc879bc06af66796fd
<81> ocaml parser segfaults on invalid files
* the bug is not reproduced
<80> Add support for falcon pl
* contributed by the original author
<67> Objective C language parser
* This is the implementation is we have in fishman tree.
<65> absoluteFilename uses strcpy on overlapping strings
* Fixed in exuberant tree, however the ticket is still open::
d2bdf505abb7569deae2b50305ea1edce6208557
<64> Fix strcpy() misuse
* Fixed in exuberant tree, however the ticket is still open::
d2bdf505abb7569deae2b50305ea1edce6208557
<51> Ada support
* Ada support is now available in fishman tree::
4b6b4a72f3d2d4ef969d7c650de1829d79f0ea7c
<38> Ada support
* Ada support is now available in fishman tree::
4b6b4a72f3d2d4ef969d7c650de1829d79f0ea7c
<33> Add basic ObjC support
* This one is written in regexp.
* we have better objc parser.
\(1\) bibtex parser
* Reject because...
+ the owner of the ticket is anonymous.
+ the name of patch author is not written explicitly at
the header of patch.
* Alternative
https://gist.github.com/ptrv/4576213
devel mailing list (ctags-devel@sourceforge)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<[Ctags] Shebang with python3 instead of python>
From: Martin Ueding <dev@ma...> - 2013-01-26 18:36:32
Added python, python2 and python3 as extensions of
python parser::
bb81485205c67617f1b34f61341e60b9e8030502
<[Ctags-devel] Lack of fnmatch(3) in Windows>
From: Frank Fesevur <ffes@us...> - 2013-08-24 20:25:47
There is no fnmatch() in the Windows C library. Therefore
a string comparison is done in fileNameMatched() in
strlist.c and patterns are not recognized::
698bf2f3db692946d2358892d228a864014abc4b
<Re: [Ctags-devel] WindRes parser>
From: Frank Fesevur <ffes@unns...> - 2013-08-30 21:23:50
A parser for Windows Resource files.
http://en.wikipedia.org/wiki/Resource_%28Windows%29
::
95b4806ba6c006e4b7e72a006700e33c720ab9e7
([Ctags-devel] Skip repeat PATH_SEPARATORs in relativeFilename())
From: Seth Dickson <whefxlr@gm...> - 2013-12-24 04:51:01
Looks interesting.
Fedora
----------------------------------------------------------------------
Some patches are maintained in ctags package of Fedora.
Inventory of patches are
http://pkgs.fedoraproject.org/cgit/ctags.git/tree/ctags.spec
<ctags-5.7-destdir.patch>
This patch was merged in fishman ctags git tree::
d4b5972427a46cbdcbfb050a944cf62b300676be
<ctags-5.7-segment-fault.patch>
This patch was merged in fishman ctags git tree::
8cc2b482f6c7257c5151893a6d02b8c79851fedd
(ctags-5.8-cssparse.patch)
Not in fishman tree.
The reproducer is attached to following page:
https://bugzilla.redhat.com/show_bug.cgi?id=852101
However, fishman-ctags doesn't reproduce with it.
I, Masatake YAMATO, read the patch. However, I don't
understand the patch.
<ctags-5.8-css.patch>
This patch was merged in fishman ctags git tree::
80c1522a36df3ba52b8b7cd7f5c79d5c30437a63
<ctags-5.8-memmove.patch>
This patch was merged in exuberant ctags svn tree.
As the result this patch is in fishman tree::
d2bdf505abb7569deae2b50305ea1edce6208557
<ctags-5.8-ocaml-crash.patch>
This patch was merged in exuberant ctags svn tree.
As the result this patch is in fishman tree::
ddb29762b37d60a875252dcc401de0b7479527b1
<ctags-5.8-format-security.patch>
This patch was merged in exuberant ctags svn tree.
As the result this patch is in fishman tree::
2f7a78ce21e4156ec3e63c821827cf1d5680ace8
Debian
----------------------------------------------------------------------
Some patches are maintained in ctags package of Debian.
Inventory of patches are
http://anonscm.debian.org/cgit/users/cjwatson/exuberant-ctags.git/tree/debian/patches/series
(python-disable-imports.patch)
Not in fishman tree.
I don't want to merge this patch. I think ctags should extract
as much as possible informatoin from input source code.
The user has responsibility to filter out the noise.
The definition of noise is upto the user.
<vim-command-loop.patch>
This patch was merged as an alternative for
7fb36a2f4690374526e9e7ef4f1e24800b6914ec
Discussed on https://github.com/fishman/ctags/issues/74
::
e59325a576e38bc63b91abb05a5a22d2cef25ab7
Other interesting ctags repositories
----------------------------------------------------------------------
There are several interesting repo's with ctags around. These are
interesting to integrate in the future.
Geany
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Repo
https://github.com/geany/geany/tree/master/tagmanager/ctags
Geany has created a library out of ctags
https://github.com/fishman/ctags/issues/63
Their language parsers have many improvements to various parsers.
Changes known by fishman devs worth backporting:
+* HTML reads <h1><h2><h3> tags
* Make has support for targets
* SQL tags are stored with scopes instead of "tablename.field"
They have a these additional language parsers:
* `Abaqus <http://en.wikipedia.org/wiki/Abaqus>`_
* `ActionScript <http://en.wikipedia.org/wiki/ActionScript>`_
* `AsciiDoc <http://en.wikipedia.org/wiki/AsciiDoc>`_
+* `D (c.c) <http://dlang.org>`_
* `DocBook <http://en.wikipedia.org/wiki/DocBook>`_
+* `Ferite (c.c) <http://en.wikipedia.org/wiki/Ferite>`_
+* `GLSL (c.c) <http://en.wikipedia.org/wiki/OpenGL_Shading_Language>`_
* `Haskell <http://en.wikipedia.org/wiki/Haskell_%28programming_language%29>`_
* `Haxe <http://en.wikipedia.org/wiki/Haxe>`_
* `NSIS <http://en.wikipedia.org/wiki/Nullsoft_Scriptable_Install_System>`_
* `R <http://en.wikipedia.org/wiki/R_%28programming_language%29>`_
+* `Rust <http://rust-lang.org>`_
* `reStructuredText (reST) <http://en.wikipedia.org/wiki/ReStructuredText>`_
* `txt2tags <http://en.wikipedia.org/wiki/Txt2tags>`_
+* `Vala (c.c) <http://en.wikipedia.org/wiki/Vala_%28programming_language%29>`_
+
+
+These changes have been merged:
+
+* Fix regex callback match count - https://github.com/fishman/ctags/pull/104
`VIM-Japan <https://github.com/vim-jp/ctags/>`_
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Anjuta is a Gnome IDE. They did not fork Exuberant ctags, but they did
natively include it in Anjuta. They have made several additions to
thier version of it including fairly extensive Vala langauge support.
tagbar
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Wiki
https://github.com/majutsushi/tagbar/wiki
This is a gold mine of xcmd and optlib.
External command(xcmd)
----------------------------------------------------------------------
Near feature fishman-ctags can invoke external command as a
specialized parser though some glue code or script may be
needed. Sometimes we may have to hack the external command to adjust
the interface between the command and fishman-ctags.
So let's track external commands maintained out fishman-ctags. If we
prepare glue code or script, mark it with <>, and if not, mark it with
().
<`CoffeeTags <https://github.com/lukaszkorecki/CoffeeTags>`_>
This is the primary target during developing xcmd
feature. CoffeeTags side hacking is done.
(`perl-tags <https://github.com/dtikhonov/perl-tags>`_)
diff --git a/lregex.c b/lregex.c
index 0ed936ef..4d3d2f19 100644
--- a/lregex.c
+++ b/lregex.c
@@ -1,772 +1,777 @@
/*
* $Id$
*
* Copyright (c) 2000-2003, Darren Hiebert
*
* This source code is released for free distribution under the terms of the
* GNU General Public License.
*
* This module contains functions for applying regular expression matching.
*
* The code for utlizing the Gnu regex package with regards to processing the
* regex option and checking for regex matches was adapted from routines in
* Gnu etags.
*/
/*
* INCLUDE FILES
*/
#include "general.h" /* must always come first */
#include <string.h>
#ifdef HAVE_REGCOMP
# include <ctype.h>
# include <stddef.h>
# ifdef HAVE_SYS_TYPES_H
# include <sys/types.h> /* declare off_t (not known to regex.h on FreeBSD) */
# endif
# include <regex.h>
#endif
#include "debug.h"
#include "entry.h"
#include "flags.h"
#include "parse.h"
#include "read.h"
#include "routines.h"
static boolean regexAvailable = FALSE;
#if defined (HAVE_REGEX) && !defined (REGCOMP_BROKEN)
/*
* MACROS
*/
/* Back-references \0 through \9 */
#define BACK_REFERENCE_COUNT 10
#define REGEX_NAME "Regex"
/*
* DATA DECLARATIONS
*/
struct sKind {
boolean enabled;
char letter;
char* name;
char* description;
};
enum pType { PTRN_TAG, PTRN_CALLBACK };
typedef struct {
regex_t *pattern;
enum pType type;
boolean exclusive;
union {
struct {
char *name_pattern;
struct sKind kind;
} tag;
struct {
regexCallback function;
} callback;
} u;
} regexPattern;
typedef struct {
regexPattern *patterns;
unsigned int count;
} patternSet;
#define COUNT(D) (sizeof(D)/sizeof(D[0]))
/*
* DATA DEFINITIONS
*/
/* Array of pattern sets, indexed by language */
static patternSet* Sets = NULL;
static int SetUpper = -1; /* upper language index in list */
/*
* FUNCTION DEFINITIONS
*/
static void clearPatternSet (const langType language)
{
if (language <= SetUpper)
{
patternSet* const set = Sets + language;
unsigned int i;
for (i = 0 ; i < set->count ; ++i)
{
regexPattern *p = &set->patterns [i];
regfree (p->pattern);
eFree (p->pattern);
p->pattern = NULL;
if (p->type == PTRN_TAG)
{
eFree (p->u.tag.name_pattern);
p->u.tag.name_pattern = NULL;
eFree (p->u.tag.kind.name);
p->u.tag.kind.name = NULL;
if (p->u.tag.kind.description != NULL)
{
eFree (p->u.tag.kind.description);
p->u.tag.kind.description = NULL;
}
}
}
if (set->patterns != NULL)
eFree (set->patterns);
set->patterns = NULL;
set->count = 0;
}
}
/*
* Regex psuedo-parser
*/
static void makeRegexTag (
const vString* const name, const struct sKind* const kind)
{
if (kind->enabled)
{
tagEntryInfo e;
Assert (name != NULL && vStringLength (name) > 0);
Assert (kind != NULL);
initTagEntry (&e, vStringValue (name));
e.kind = kind->letter;
e.kindName = kind->name;
makeTagEntry (&e);
}
}
/*
* Regex pattern definition
*/
/* Take a string like "/blah/" and turn it into "blah", making sure
* that the first and last characters are the same, and handling
* quoted separator characters. Actually, stops on the occurrence of
* an unquoted separator. Also turns "\t" into a Tab character.
* Returns pointer to terminating separator. Works in place. Null
* terminates name string.
*/
static char* scanSeparators (char* name)
{
char sep = name [0];
char *copyto = name;
boolean quoted = FALSE;
for (++name ; *name != '\0' ; ++name)
{
if (quoted)
{
if (*name == sep)
*copyto++ = sep;
else if (*name == 't')
*copyto++ = '\t';
else
{
/* Something else is quoted, so preserve the quote. */
*copyto++ = '\\';
*copyto++ = *name;
}
quoted = FALSE;
}
else if (*name == '\\')
quoted = TRUE;
else if (*name == sep)
{
break;
}
else
*copyto++ = *name;
}
*copyto = '\0';
return name;
}
/* Parse `regexp', in form "/regex/name/[k,Kind/]flags" (where the separator
* character is whatever the first character of `regexp' is), by breaking it
* up into null terminated strings, removing the separators, and expanding
* '\t' into tabs. When complete, `regexp' points to the line matching
* pattern, a pointer to the name matching pattern is written to `name', a
* pointer to the kinds is written to `kinds' (possibly NULL), and a pointer
* to the trailing flags is written to `flags'. If the pattern is not in the
* correct format, a false value is returned.
*/
static boolean parseTagRegex (
char* const regexp, char** const name,
char** const kinds, char** const flags)
{
boolean result = FALSE;
const int separator = (unsigned char) regexp [0];
*name = scanSeparators (regexp);
if (*regexp == '\0')
error (WARNING, "empty regexp");
else if (**name != separator)
error (WARNING, "%s: incomplete regexp", regexp);
else
{
char* const third = scanSeparators (*name);
if (**name == '\0')
error (WARNING, "%s: regexp missing name pattern", regexp);
if ((*name) [strlen (*name) - 1] == '\\')
error (WARNING, "error in name pattern: \"%s\"", *name);
if (*third != separator)
error (WARNING, "%s: regexp missing final separator", regexp);
else
{
char* const fourth = scanSeparators (third);
if (*fourth == separator)
{
*kinds = third;
scanSeparators (fourth);
*flags = fourth;
}
else
{
*flags = third;
*kinds = NULL;
}
result = TRUE;
}
}
return result;
}
static void ptrn_flag_exclusive_short (char c __unused__, void* data)
{
regexPattern *ptrn = data;
ptrn->exclusive = TRUE;
}
static void ptrn_flag_exclusive_long (const char* const s __unused__, const char* const unused __unused__, void* data)
{
ptrn_flag_exclusive_short ('x', data);
}
static flagDefinition ptrnFlagDef[] = {
{ 'x', "exclusive", ptrn_flag_exclusive_short, ptrn_flag_exclusive_long },
};
static regexPattern* addCompiledTagCommon (const langType language,
regex_t* const pattern)
{
patternSet* set;
regexPattern *ptrn;
if (language > SetUpper)
{
int i;
Sets = xRealloc (Sets, (language + 1), patternSet);
for (i = SetUpper + 1 ; i <= language ; ++i)
{
Sets [i].patterns = NULL;
Sets [i].count = 0;
}
SetUpper = language;
}
set = Sets + language;
set->patterns = xRealloc (set->patterns, (set->count + 1), regexPattern);
ptrn = &set->patterns [set->count];
ptrn->pattern = pattern;
ptrn->exclusive = FALSE;
set->count += 1;
useRegexMethod(language);
return ptrn;
}
static void addCompiledTagPattern (
const langType language, regex_t* const pattern,
char* const name, const char kind, char* const kindName,
char *const description, const char* flags)
{
regexPattern * ptrn;
ptrn = addCompiledTagCommon(language, pattern);
ptrn->type = PTRN_TAG;
ptrn->u.tag.name_pattern = name;
ptrn->u.tag.kind.enabled = TRUE;
ptrn->u.tag.kind.letter = kind;
ptrn->u.tag.kind.name = kindName;
ptrn->u.tag.kind.description = description;
flagsEval (flags, ptrnFlagDef, COUNT(ptrnFlagDef), ptrn);
}
static void addCompiledCallbackPattern (
const langType language, regex_t* const pattern,
const regexCallback callback, const char* flags)
{
regexPattern * ptrn;
ptrn = addCompiledTagCommon(language, pattern);
ptrn->type = PTRN_CALLBACK;
ptrn->u.callback.function = callback;
flagsEval (flags, ptrnFlagDef, COUNT(ptrnFlagDef), ptrn);
}
static void regex_flag_basic_short (char c __unused__, void* data)
{
int* cflags = data;
*cflags &= ~REG_EXTENDED;
}
static void regex_flag_basic_long (const char* const s __unused__, const char* const unused __unused__, void* data)
{
regex_flag_basic_short ('b', data);
}
static void regex_flag_extend_short (char c __unused__, void* data)
{
int* cflags = data;
*cflags |= REG_EXTENDED;
}
static void regex_flag_extend_long (const char* const c __unused__, const char* const unused __unused__, void* data)
{
regex_flag_extend_short('e', data);
}
static void regex_flag_icase_short (char c __unused__, void* data)
{
int* cflags = data;
*cflags |= REG_ICASE;
}
static void regex_flag_icase_long (const char* s __unused__, const char* const unused __unused__, void* data)
{
regex_flag_icase_short ('i', data);
}
static regex_t* compileRegex (const char* const regexp, const char* const flags)
{
int cflags = REG_EXTENDED | REG_NEWLINE;
regex_t *result = NULL;
int errcode;
flagDefinition regexFlagDefs[] = {
{ 'b', "basic", regex_flag_basic_short, regex_flag_basic_long },
{ 'e', "extend", regex_flag_extend_short, regex_flag_extend_long },
{ 'i', "icase", regex_flag_icase_short, regex_flag_icase_long },
};
flagsEval (flags,
regexFlagDefs,
COUNT(regexFlagDefs),
&cflags);
result = xMalloc (1, regex_t);
errcode = regcomp (result, regexp, cflags);
if (errcode != 0)
{
char errmsg[256];
regerror (errcode, result, errmsg, 256);
error (WARNING, "regcomp %s: %s", regexp, errmsg);
regfree (result);
eFree (result);
result = NULL;
}
return result;
}
static void parseKinds (
const char* const kinds, char* const kind, char** const kindName,
char **description)
{
*kind = '\0';
*kindName = NULL;
*description = NULL;
if (kinds == NULL || kinds [0] == '\0')
{
*kind = 'r';
*kindName = eStrdup ("regex");
}
else if (kinds [0] != '\0')
{
const char* k = kinds;
if (k [0] != ',' && (k [1] == ',' || k [1] == '\0'))
*kind = *k++;
else
*kind = 'r';
if (*k == ',')
++k;
if (k [0] == '\0')
*kindName = eStrdup ("regex");
else
{
const char *const comma = strchr (k, ',');
if (comma == NULL)
*kindName = eStrdup (k);
else
{
*kindName = (char*) eMalloc (comma - k + 1);
strncpy (*kindName, k, comma - k);
(*kindName) [comma - k] = '\0';
k = comma + 1;
if (k [0] != '\0')
*description = eStrdup (k);
}
}
}
}
static void printRegexKind (const regexPattern *pat, unsigned int i, boolean indent)
{
const struct sKind *const kind = &pat [i].u.tag.kind;
const char *const indentation = indent ? " " : "";
Assert (pat [i].type == PTRN_TAG);
printf ("%s%c %s %s\n", indentation,
kind->letter != '\0' ? kind->letter : '?',
kind->description != NULL ? kind->description : kind->name,
kind->enabled ? "" : " [off]");
}
static void processLanguageRegex (const langType language,
const char* const parameter)
{
if (parameter == NULL || parameter [0] == '\0')
clearPatternSet (language);
else if (parameter [0] != '@')
addLanguageRegex (language, parameter);
else if (! doesFileExist (parameter + 1))
error (WARNING, "cannot open regex file");
else
{
const char* regexfile = parameter + 1;
FILE* const fp = fopen (regexfile, "r");
if (fp == NULL)
error (WARNING | PERROR, "%s", regexfile);
else
{
vString* const regex = vStringNew ();
while (readLine (regex, fp))
addLanguageRegex (language, vStringValue (regex));
fclose (fp);
vStringDelete (regex);
}
}
}
/*
* Regex pattern matching
*/
static vString* substitute (
const char* const in, const char* out,
const int nmatch, const regmatch_t* const pmatch)
{
vString* result = vStringNew ();
const char* p;
for (p = out ; *p != '\0' ; p++)
{
if (*p == '\\' && isdigit ((int) *++p))
{
const int dig = *p - '0';
if (0 < dig && dig < nmatch && pmatch [dig].rm_so != -1)
{
const int diglen = pmatch [dig].rm_eo - pmatch [dig].rm_so;
vStringNCatS (result, in + pmatch [dig].rm_so, diglen);
}
}
else if (*p != '\n' && *p != '\r')
vStringPut (result, *p);
}
vStringTerminate (result);
return result;
}
static void matchTagPattern (const vString* const line,
const regexPattern* const patbuf,
const regmatch_t* const pmatch)
{
vString *const name = substitute (vStringValue (line),
patbuf->u.tag.name_pattern, BACK_REFERENCE_COUNT, pmatch);
vStringStripLeading (name);
vStringStripTrailing (name);
if (vStringLength (name) > 0)
makeRegexTag (name, &patbuf->u.tag.kind);
else
error (WARNING, "%s:%ld: null expansion of name pattern \"%s\"",
getInputFileName (), getInputLineNumber (),
patbuf->u.tag.name_pattern);
vStringDelete (name);
}
static void matchCallbackPattern (
const vString* const line, const regexPattern* const patbuf,
const regmatch_t* const pmatch)
{
regexMatch matches [BACK_REFERENCE_COUNT];
unsigned int count = 0;
int i;
- for (i = 0 ; i < BACK_REFERENCE_COUNT && pmatch [i].rm_so != -1 ; ++i)
+ for (i = 0 ; i < BACK_REFERENCE_COUNT ; ++i)
{
matches [i].start = pmatch [i].rm_so;
matches [i].length = pmatch [i].rm_eo - pmatch [i].rm_so;
- ++count;
+ /* a valid match may have both offsets == -1,
+ * e.g. (foo)*(bar) matching "bar" - see CTags bug 271.
+ * As POSIX regex doesn't seem to have a way to count matches,
+ * we return the count up to the last non-empty match. */
+ if (pmatch [i].rm_so != -1)
+ count = i + 1;
}
patbuf->u.callback.function (vStringValue (line), matches, count);
}
static boolean matchRegexPattern (const vString* const line,
const regexPattern* const patbuf)
{
boolean result = FALSE;
regmatch_t pmatch [BACK_REFERENCE_COUNT];
const int match = regexec (patbuf->pattern, vStringValue (line),
BACK_REFERENCE_COUNT, pmatch, 0);
if (match == 0)
{
result = TRUE;
if (patbuf->type == PTRN_TAG)
matchTagPattern (line, patbuf, pmatch);
else if (patbuf->type == PTRN_CALLBACK)
matchCallbackPattern (line, patbuf, pmatch);
else
{
Assert ("invalid pattern type" == NULL);
result = FALSE;
}
}
return result;
}
/* PUBLIC INTERFACE */
/* Match against all patterns for specified language. Returns true if at least
* on pattern matched.
*/
extern boolean matchRegex (const vString* const line, const langType language)
{
boolean result = FALSE;
if (language != LANG_IGNORE && language <= SetUpper &&
Sets [language].count > 0)
{
const patternSet* const set = Sets + language;
unsigned int i;
for (i = 0 ; i < set->count ; ++i)
{
regexPattern* ptrn = set->patterns + i;
if (matchRegexPattern (line, ptrn))
{
result = TRUE;
if (ptrn->exclusive)
break;
}
}
}
return result;
}
extern void findRegexTags (void)
{
/* merely read all lines of the file */
while (fileReadLine () != NULL)
;
}
#endif /* HAVE_REGEX */
extern void addTagRegex (
const langType language __unused__,
const char* const regex __unused__,
const char* const name __unused__,
const char* const kinds __unused__,
const char* const flags __unused__)
{
#ifdef HAVE_REGEX
Assert (regex != NULL);
Assert (name != NULL);
if (regexAvailable)
{
regex_t* const cp = compileRegex (regex, flags);
if (cp != NULL)
{
char kind;
char* kindName;
char* description;
parseKinds (kinds, &kind, &kindName, &description);
addCompiledTagPattern (language, cp, eStrdup (name),
kind, kindName, description, flags);
}
}
#endif
}
extern void addCallbackRegex (
const langType language __unused__,
const char* const regex __unused__,
const char* const flags __unused__,
const regexCallback callback __unused__)
{
#ifdef HAVE_REGEX
Assert (regex != NULL);
if (regexAvailable)
{
regex_t* const cp = compileRegex (regex, flags);
if (cp != NULL)
addCompiledCallbackPattern (language, cp, callback, flags);
}
#endif
}
extern void addLanguageRegex (
const langType language __unused__, const char* const regex __unused__)
{
#ifdef HAVE_REGEX
if (regexAvailable)
{
char *const regex_pat = eStrdup (regex);
char *name, *kinds, *flags;
if (parseTagRegex (regex_pat, &name, &kinds, &flags))
{
addTagRegex (language, regex_pat, name, kinds, flags);
eFree (regex_pat);
}
}
#endif
}
/*
* Regex option parsing
*/
extern boolean processRegexOption (const char *const option,
const char *const parameter __unused__)
{
const char* const dash = strchr (option, '-');
char* lang;
langType language;
if (dash == NULL)
return FALSE;
if (strncmp (option, "regex", dash - option) == 0)
lang = eStrdup (dash + 1);
else if (strcmp (dash + 1, "regex") == 0)
lang = eStrndup (option, dash - option);
else
return FALSE;
#ifdef HAVE_REGEX
language = getNamedLanguage (lang);
if (language == LANG_IGNORE)
error (WARNING, "unknown language \"%s\" in --%s option", lang, option);
else
processLanguageRegex (language, parameter);
#else
error (WARNING, "regex support not available; required for --%s option",
option);
#endif
eFree (lang);
return TRUE;
}
extern void resetRegexKinds (const langType language __unused__,
boolean mode)
{
#ifdef HAVE_REGEX
if (language <= SetUpper && Sets [language].count > 0)
{
patternSet* const set = Sets + language;
unsigned int i;
for (i = 0 ; i < set->count ; ++i)
if (set->patterns [i].type == PTRN_TAG)
set->patterns [i].u.tag.kind.enabled = mode;
}
#endif
}
extern boolean enableRegexKind (
const langType language __unused__,
const int kind __unused__, const boolean mode __unused__)
{
boolean result = FALSE;
#ifdef HAVE_REGEX
if (language <= SetUpper && Sets [language].count > 0)
{
patternSet* const set = Sets + language;
unsigned int i;
for (i = 0 ; i < set->count ; ++i)
if (set->patterns [i].type == PTRN_TAG &&
set->patterns [i].u.tag.kind.letter == kind)
{
set->patterns [i].u.tag.kind.enabled = mode;
result = TRUE;
}
}
#endif
return result;
}
extern void printRegexKinds (const langType language __unused__, boolean indent __unused__)
{
#ifdef HAVE_REGEX
if (language <= SetUpper && Sets [language].count > 0)
{
patternSet* const set = Sets + language;
unsigned int i;
for (i = 0 ; i < set->count ; ++i)
if (set->patterns [i].type == PTRN_TAG)
printRegexKind (set->patterns, i, indent);
}
#endif
}
extern void freeRegexResources (void)
{
#ifdef HAVE_REGEX
int i;
for (i = 0 ; i <= SetUpper ; ++i)
clearPatternSet (i);
if (Sets != NULL)
eFree (Sets);
Sets = NULL;
SetUpper = -1;
#endif
}
/* Return TRUE if available. */
extern boolean checkRegex (void)
{
#if ! defined (HAVE_REGEX)
regexAvailable = FALSE;
#elif defined (CHECK_REGCOMP)
{
/* Check for broken regcomp() on Cygwin */
regex_t patbuf;
int errcode;
if (regcomp (&patbuf, "/hello/", 0) != 0)
error (WARNING, "Disabling broken regex");
else
regexAvailable = TRUE;
}
#else
/* We are using bundled regex engine. */
regexAvailable = TRUE;
#endif
return regexAvailable;
}
/* vi:set tabstop=4 shiftwidth=4: */
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Jun 21, 5:50 PM (1 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3447356
Attached To
rPUC universal-ctags debian packaging
Event Timeline
Log In to Comment