From 76a84f9c906d0296479378355c32287f1788e7c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Tue, 23 Apr 2019 20:37:20 +0200 Subject: [PATCH 01/46] Sync with upstream ctags This sync is done against commit 7dd02539554a81aa39b49ce8df9d4511f92d9f4f of the upstream parser. All Geany changes are marked in the code either by or GEANY DIFF comment. --- ctags/Makefile.am | 29 +- ctags/main/args.c | 6 +- ctags/main/{args.h => args_p.h} | 6 +- ctags/main/colprint.c | 295 +++ ctags/main/colprint_p.h | 37 + ctags/main/ctags-api.c | 18 +- ctags/main/ctags.h | 8 +- ctags/main/debug.c | 102 +- ctags/main/debug.h | 28 +- ctags/main/dependency.c | 349 +++- ctags/main/dependency.h | 42 +- ctags/main/e_msoft.h | 10 +- ctags/main/entry.c | 620 +++++-- ctags/main/entry.h | 82 +- ctags/main/entry_p.h | 68 + ctags/main/entry_private.c | 37 + ctags/main/error.c | 41 +- ctags/main/field.c | 952 ++++++---- ctags/main/field.h | 47 +- ctags/main/flags.c | 107 +- ctags/main/{flags.h => flags_p.h} | 16 +- ctags/main/fmt.c | 57 +- ctags/main/{fmt.h => fmt_p.h} | 0 ctags/main/general.h | 13 +- ctags/main/gvars.h | 29 + ctags/main/keyword.c | 18 +- ctags/main/keyword.h | 9 +- ctags/main/keyword_p.h | 26 + ctags/main/kind.c | 536 +++++- ctags/main/kind.h | 78 +- ctags/main/lcpp.c | 5 +- ctags/main/lregex.c | 2230 +++++++++++++++++------ ctags/main/lregex.h | 47 + ctags/main/lregex_p.h | 76 + ctags/main/lxcmd.c | 1227 ------------- ctags/main/lxpath.c | 12 +- ctags/main/lxpath.h | 81 + ctags/main/main.c | 183 +- ctags/main/nestlevel.c | 1 + ctags/main/nestlevel.h | 1 - ctags/main/options.c | 2043 ++++++++++++--------- ctags/main/options.h | 173 +- ctags/main/options_p.h | 183 ++ ctags/main/output-ctags.c | 59 - ctags/main/output.h | 50 - ctags/main/param.c | 57 + ctags/main/param.h | 38 + ctags/main/parse.c | 2823 ++++++++++++++++++++++------- ctags/main/parse.h | 233 +-- ctags/main/parse_p.h | 154 ++ ctags/main/promise.c | 219 ++- ctags/main/promise.h | 12 +- ctags/main/promise_p.h | 24 + ctags/main/ptag.c | 88 +- ctags/main/{ptag.h => ptag_p.h} | 13 +- ctags/main/read.c | 604 +++--- ctags/main/read.h | 38 +- ctags/main/repoinfo.h | 2 +- ctags/main/routines.c | 129 +- ctags/main/routines.h | 75 +- ctags/main/routines_p.h | 107 ++ ctags/main/selectors.c | 190 +- ctags/main/selectors.h | 14 +- ctags/main/sort.c | 94 +- ctags/main/strlist.c | 86 +- ctags/main/strlist.h | 2 + ctags/main/subparser.h | 81 + ctags/main/trace.h | 101 +- ctags/main/types.h | 30 +- ctags/main/writer.c | 159 ++ ctags/main/writer_p.h | 78 + ctags/main/xtag.c | 350 +++- ctags/main/xtag.h | 43 +- ctags/parsers/erlang.c | 1 + ctags/parsers/go.c | 1 + ctags/parsers/make.c | 1 + ctags/parsers/objc.c | 3 +- ctags/parsers/perl.c | 1 + ctags/parsers/python.c | 1 + ctags/parsers/r.c | 1 + ctags/parsers/ruby.c | 2 +- ctags/parsers/rust.c | 2 +- ctags/parsers/sql.c | 2 +- 83 files changed, 10675 insertions(+), 5221 deletions(-) rename ctags/main/{args.h => args_p.h} (92%) create mode 100644 ctags/main/colprint.c create mode 100644 ctags/main/colprint_p.h create mode 100644 ctags/main/entry_p.h create mode 100644 ctags/main/entry_private.c rename ctags/main/{flags.h => flags_p.h} (60%) rename ctags/main/{fmt.h => fmt_p.h} (100%) create mode 100644 ctags/main/gvars.h create mode 100644 ctags/main/keyword_p.h create mode 100644 ctags/main/lregex.h create mode 100644 ctags/main/lregex_p.h delete mode 100644 ctags/main/lxcmd.c create mode 100644 ctags/main/lxpath.h create mode 100644 ctags/main/options_p.h delete mode 100644 ctags/main/output-ctags.c delete mode 100644 ctags/main/output.h create mode 100644 ctags/main/param.c create mode 100644 ctags/main/param.h create mode 100644 ctags/main/parse_p.h create mode 100644 ctags/main/promise_p.h rename ctags/main/{ptag.h => ptag_p.h} (91%) create mode 100644 ctags/main/routines_p.h create mode 100644 ctags/main/subparser.h create mode 100644 ctags/main/writer.c create mode 100644 ctags/main/writer_p.h diff --git a/ctags/Makefile.am b/ctags/Makefile.am index 806bff0054..8758e47105 100644 --- a/ctags/Makefile.am +++ b/ctags/Makefile.am @@ -54,7 +54,9 @@ parsers = \ libctags_la_SOURCES = \ main/args.c \ - main/args.h \ + main/args_p.h \ + main/colprint.c \ + main/colprint_p.h \ main/ctags.h \ main/ctags-api.c \ main/ctags-api.h \ @@ -64,29 +66,35 @@ libctags_la_SOURCES = \ main/dependency.c \ main/e_msoft.h \ main/entry.c \ + main/entry_private.c \ main/entry.h \ + main/entry_p.h \ main/error.c \ main/error.h \ main/field.c \ main/field.h \ main/flags.c \ - main/flags.h \ + main/flags_p.h \ main/fmt.c \ - main/fmt.h \ + main/fmt_p.h \ main/gcc-attr.h \ main/general.h \ + main/gvars.h \ main/htable.c \ main/htable.h \ main/inline.h \ main/keyword.c \ main/keyword.h \ + main/keyword_p.h \ main/kind.c \ main/kind.h \ main/lcpp.c \ main/lcpp.h \ main/lregex.c \ - main/lxcmd.c \ + main/lregex.h \ + main/lregex_p.h \ main/lxpath.c \ + main/lxpath.h \ main/main.c \ main/main.h \ main/mbcs.h \ @@ -100,17 +108,20 @@ libctags_la_SOURCES = \ main/objpool.h \ main/options.c \ main/options.h \ - main/output-ctags.c \ - main/output.h \ + main/options_p.h \ + main/param.c \ + main/param.h \ main/parse.c \ main/parse.h \ + main/parse_p.h \ main/parsers.h \ main/pcoproc.c \ main/pcoproc.h \ main/promise.c \ main/promise.h \ + main/promise_p.h \ main/ptag.c \ - main/ptag.h \ + main/ptag_p.h \ main/ptrarray.c \ main/ptrarray.h \ main/read.c \ @@ -119,18 +130,22 @@ libctags_la_SOURCES = \ main/repoinfo.h \ main/routines.c \ main/routines.h \ + main/routines_p.h \ main/selectors.c \ main/selectors.h \ main/sort.c \ main/sort.h \ main/strlist.c \ main/strlist.h \ + main/subparser.h \ main/trace.h \ main/trashbox.c \ main/trashbox.h \ main/types.h \ main/vstring.c \ main/vstring.h \ + main/writer.c \ + main/writer_p.h \ main/xtag.h \ main/xtag.c \ $(parsers) diff --git a/ctags/main/args.c b/ctags/main/args.c index 4ab49134fb..60ba288716 100644 --- a/ctags/main/args.c +++ b/ctags/main/args.c @@ -16,9 +16,10 @@ #include #include -#include "args.h" +#include "args_p.h" #include "debug.h" #include "routines.h" +#include "vstring.h" /* * FUNCTION DEFINITIONS @@ -281,7 +282,8 @@ extern void argForth (Arguments* const current) extern void argDelete (Arguments* const current) { Assert (current != NULL); - if (current->type == ARG_STRING && current->item != NULL) + if ((current->type == ARG_STRING + || current->type == ARG_FILE) && current->item != NULL) eFree (current->item); memset (current, 0, sizeof (Arguments)); eFree (current); diff --git a/ctags/main/args.h b/ctags/main/args_p.h similarity index 92% rename from ctags/main/args.h rename to ctags/main/args_p.h index 0b334ceefc..a8e4afb5ef 100644 --- a/ctags/main/args.h +++ b/ctags/main/args_p.h @@ -6,8 +6,8 @@ * * Defines external interface to command line argument reading. */ -#ifndef CTAGS_MAIN_ARGS_H -#define CTAGS_MAIN_ARGS_H +#ifndef CTAGS_MAIN_ARGS_PRIVATE_H +#define CTAGS_MAIN_ARGS_PRIVATE_H /* * INCLUDE FILES @@ -54,4 +54,4 @@ extern void argSetLineMode (Arguments* const current); extern void argForth (Arguments* const current); extern void argDelete (Arguments* const current); -#endif /* CTAGS_MAIN_ARGS_H */ +#endif /* CTAGS_MAIN_ARGS_PRIVATE_H */ diff --git a/ctags/main/colprint.c b/ctags/main/colprint.c new file mode 100644 index 0000000000..aacdf81464 --- /dev/null +++ b/ctags/main/colprint.c @@ -0,0 +1,295 @@ +/* +* Copyright (c) 2017 Masatake YAMATO +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +*/ +#include "general.h" /* must always come first */ + +#include "colprint_p.h" +#include "ptrarray.h" +#include "routines.h" +#include "strlist.h" +#include "vstring.h" + +#include +#include +#include + + +enum colprintJustification { + COLPRINT_LEFT, /* L:... */ + COLPRINT_RIGHT, /* R:... */ + COLPRINT_LAST, +}; + +struct colprintHeaderColumn { + vString *value; + enum colprintJustification justification; + unsigned int maxWidth; + bool needPrefix; +}; + +struct colprintTable { + ptrArray *header; + ptrArray *lines; +}; + +static void fillWithWhitespaces (int i, FILE *fp) +{ + while (i-- > 0) + { + fputc(' ', fp); + } +} + +static struct colprintHeaderColumn * colprintHeaderColumnNew (const char* spec) +{ + int offset = 2; + struct colprintHeaderColumn *headerCol = xCalloc (1, struct colprintHeaderColumn); + + if (strstr(spec, "L:") == spec) + headerCol->justification = COLPRINT_LEFT; + else if (strstr(spec, "R:") == spec) + headerCol->justification = COLPRINT_RIGHT; + else + { + headerCol->justification = COLPRINT_LEFT; + offset = 0; + } + + headerCol->value = vStringNewInit(spec + offset); + headerCol->maxWidth = vStringLength(headerCol->value); + return headerCol; +} + +static void colprintHeaderColumnDelete (struct colprintHeaderColumn * headerCol) +{ + vStringDelete (headerCol->value); + eFree (headerCol); +} + +struct colprintTable *colprintTableNew (const char* columnHeader, ... /* NULL TERMINATED */) +{ + char *tmp; + va_list ap; + struct colprintTable *table; + struct colprintHeaderColumn *headerCol; + + + table = xCalloc (1, struct colprintTable); + table->header = ptrArrayNew ((ptrArrayDeleteFunc)colprintHeaderColumnDelete); + table->lines = ptrArrayNew ((ptrArrayDeleteFunc)stringListDelete); + + headerCol = colprintHeaderColumnNew(columnHeader); + ptrArrayAdd (table->header, headerCol); + + va_start(ap, columnHeader); + while (1) + { + tmp = va_arg(ap, char*); + if (tmp) + { + headerCol = colprintHeaderColumnNew(tmp); + ptrArrayAdd (table->header, headerCol); + } + else + break; + } + va_end(ap); + + struct colprintHeaderColumn *last_col = ptrArrayLast (table->header); + if (last_col) + last_col->justification = COLPRINT_LAST; + + return table; +} + +void colprintTableDelete (struct colprintTable *table) +{ + ptrArrayDelete(table->header); + table->header = NULL; + + ptrArrayDelete(table->lines); + table->header = NULL; + + eFree (table); +} + +static void colprintColumnPrintGeneric (vString *column, struct colprintHeaderColumn *spec, bool machinable, FILE *fp) +{ + int maxWidth = spec->maxWidth + (spec->needPrefix? 1: 0); + + if ((column == spec->value) && (spec->needPrefix)) + { + fputc('#', fp); + maxWidth--; + } + + if (machinable) + { + fputs (vStringValue (column), fp); + if (spec->justification != COLPRINT_LAST) + fputc ('\t', fp); + } + else + { + int padLen = maxWidth - vStringLength (column); + if (spec->justification == COLPRINT_LEFT + || spec->justification == COLPRINT_LAST) + { + fputs (vStringValue (column), fp); + if (spec->justification != COLPRINT_LAST) + { + fillWithWhitespaces (padLen, fp); + fputc (' ', fp); + } + } + else + { + fillWithWhitespaces (padLen, fp); + fputs (vStringValue (column), fp); + fputc (' ', fp); + } + } +} + +static void colprintHeaderColumnPrint (struct colprintHeaderColumn *headerCol, bool machinable, FILE* fp) +{ + colprintColumnPrintGeneric (headerCol->value, headerCol, machinable, fp); +} + +static void colprintHeaderPrint (ptrArray *header, int startFrom, bool withHeader, bool machinable, FILE *fp) +{ + unsigned int i; + + if (!withHeader) + return; + + for (i = startFrom; i < ptrArrayCount(header); i++) + { + struct colprintHeaderColumn *headerCol = ptrArrayItem (header, i); + colprintHeaderColumnPrint (headerCol, machinable, fp); + } + fputc('\n', fp); +} + +static void colprintLinePrint (stringList *line, int startFrom, ptrArray *header, bool machinable, FILE *fp) +{ + unsigned int i; + + for (i = startFrom; i < stringListCount (line); i++) + { + vString *value = stringListItem(line, i); + struct colprintHeaderColumn *spec = ptrArrayItem (header, i); + colprintColumnPrintGeneric(value, spec, machinable, fp); + } +} +static void colprintLinesPrint (ptrArray *lines, int startFrom, ptrArray *header, bool machinable, FILE *fp) +{ + unsigned int i; + + for (i = 0; i < ptrArrayCount (lines); i++) + { + stringList *line = ptrArrayItem (lines, i); + colprintLinePrint (line, startFrom, header, machinable, fp); + fputc('\n', fp); + } +} + +static void colprintUpdateMaxWidths (ptrArray *header, ptrArray *lines, unsigned int startFrom) +{ + for (unsigned int c = 0; c < ptrArrayCount(header); c++) + { + struct colprintHeaderColumn *spec = ptrArrayItem (header, c); + + if (c == startFrom) + spec->needPrefix = true; + else + spec->needPrefix = false; + } + + for (unsigned int c = 0; c < ptrArrayCount(header); c++) + { + struct colprintHeaderColumn *spec = ptrArrayItem (header, c); + + for (unsigned int l = 0; l < ptrArrayCount(lines); l++) + { + struct colprintLine *line = ptrArrayItem(lines, l); + vString *column = ptrArrayItem((ptrArray *)line, c); + if (spec->maxWidth < vStringLength(column)) + spec->maxWidth = vStringLength(column); + } + } +} + +void colprintTablePrint (struct colprintTable *table, unsigned int startFrom, bool withHeader, bool machinable, FILE *fp) +{ + colprintUpdateMaxWidths (table->header, table->lines, startFrom); + + colprintHeaderPrint (table->header, startFrom, withHeader, machinable, fp); + colprintLinesPrint (table->lines, startFrom, table->header, machinable, fp); +} + +void colprintTableSort (struct colprintTable *table, int (* compareFn) (struct colprintLine *, struct colprintLine *)) +{ + ptrArraySort (table->lines, (int (*) (const void *, const void *))compareFn); +} + +struct colprintLine *colprintTableGetNewLine (struct colprintTable *table) +{ + stringList *line = stringListNew (); + + ptrArrayAdd (table->lines, line); + return (struct colprintLine *)line; +} + +static void colprintLineAppendColumn (struct colprintLine *line, vString *column) +{ + stringList *slist = (stringList *)line; + stringListAdd (slist, column); +} + +void colprintLineAppendColumnCString (struct colprintLine *line, const char *column) +{ + vString* vcol = vStringNewInit (column? column: ""); + colprintLineAppendColumn (line, vcol); +} + +void colprintLineAppendColumnVString (struct colprintLine *line, vString* column) +{ + colprintLineAppendColumnCString(line, vStringValue (column)); +} + +void colprintLineAppendColumnChar (struct colprintLine *line, char column) +{ + vString* vcol = vStringNew (); + vStringPut (vcol, column); + colprintLineAppendColumn (line, vcol); +} + +void colprintLineAppendColumnInt (struct colprintLine *line, unsigned int column) +{ + char buf[12]; + + snprintf(buf, 12, "%u", column); + colprintLineAppendColumnCString (line, buf); +} + +void colprintLineAppendColumnBool (struct colprintLine *line, bool column) +{ + colprintLineAppendColumnCString (line, column? "yes": "no"); +} + +const char *colprintLineGetColumn (struct colprintLine *line, unsigned int column) +{ + stringList *slist = (stringList *)line; + if (column <= stringListCount(slist)) + { + vString *vstr = stringListItem (slist, column); + return vStringValue (vstr); + } + else + return NULL; +} diff --git a/ctags/main/colprint_p.h b/ctags/main/colprint_p.h new file mode 100644 index 0000000000..862187d32e --- /dev/null +++ b/ctags/main/colprint_p.h @@ -0,0 +1,37 @@ +/* +* Copyright (c) 2017 Masatake YAMATO +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +*/ +#ifndef CTAGS_MAIN_COLPRINT_PRIVATE_H +#define CTAGS_MAIN_COLPRINT_PRIVATE_H + +#include "general.h" + +#include "vstring.h" +#include + +struct colprintTable; +struct colprintLine; + +/* Each column must have a prefix for specifying justification: "L:" or "R:". */ +struct colprintTable *colprintTableNew (const char* columnHeader, ... /* NULL TERMINATED */); +void colprintTableDelete (struct colprintTable *table); +void colprintTablePrint (struct colprintTable *table, unsigned int startFrom, bool withHeader, bool machinable, FILE *fp); +void colprintTableSort (struct colprintTable *table, int (* compareFn) (struct colprintLine *, struct colprintLine *)); + +struct colprintLine *colprintTableGetNewLine (struct colprintTable *table); + +void colprintLineAppendColumnCString (struct colprintLine *line, const char* column); +void colprintLineAppendColumnVString (struct colprintLine *line, vString* column); +void colprintLineAppendColumnChar (struct colprintLine *line, char column); +void colprintLineAppendColumnInt (struct colprintLine *line, unsigned int column); + +/* Appends "yes" or "no". */ +void colprintLineAppendColumnBool (struct colprintLine *line, bool column); + +const char *colprintLineGetColumn (struct colprintLine *line, unsigned int column); + +#endif /* CTAGS_MAIN_COLPRINT_PRIVATE_H */ diff --git a/ctags/main/ctags-api.c b/ctags/main/ctags-api.c index 20b6898ec5..0864a3864f 100644 --- a/ctags/main/ctags-api.c +++ b/ctags/main/ctags-api.c @@ -15,10 +15,13 @@ #include "types.h" #include "routines.h" #include "error.h" -#include "output.h" -#include "parse.h" -#include "options.h" +#include "mio.h" +#include "writer_p.h" +#include "parse_p.h" +#include "options_p.h" #include "trashbox.h" +#include "field.h" +#include "xtag.h" #include #include @@ -43,17 +46,18 @@ static bool nofatalErrorPrinter (const errorSelection selection, extern void ctagsInit(void) { + initDefaultTrashBox (); + setErrorPrinter (nofatalErrorPrinter, NULL); - setTagWriter (&ctagsWriter); + setTagWriter (WRITER_U_CTAGS); checkRegex (); - initFieldDescs (); + initFieldObjects (); + initXtagObjects (); initializeParsing (); initOptions (); - initDefaultTrashBox (); - /* make sure all parsers are initialized */ initializeParser (LANG_AUTO); } diff --git a/ctags/main/ctags.h b/ctags/main/ctags.h index 64faea5bba..696e7d8ad8 100644 --- a/ctags/main/ctags.h +++ b/ctags/main/ctags.h @@ -30,5 +30,11 @@ extern const char* ctags_repoinfo; #define CTAGS_FIELD_PREFIX "UCTAGS" - +/* + * Reserved words + */ +#define RSV_LANGMAP_DEFAULT "default" +#define RSV_LANG_ALL "all" +#define RSV_LANG_AUTO "auto" +#define RSV_NONE "NONE" #endif /* CTAGS_MAIN_CTAGS_H */ diff --git a/ctags/main/debug.c b/ctags/main/debug.c index 0dab409a88..95e94bb938 100644 --- a/ctags/main/debug.c +++ b/ctags/main/debug.c @@ -16,9 +16,12 @@ #include #include #include +#include #include "debug.h" +#include "entry_p.h" #include "options.h" +#include "parse_p.h" #include "read.h" /* @@ -26,6 +29,8 @@ */ #ifdef DEBUG +#include "htable.h" + extern void lineBreak (void) {} /* provides a line-specified break point */ @@ -74,11 +79,16 @@ extern void debugEntry (const tagEntryInfo *const tag) if (debug (DEBUG_PARSE)) { - printf ("<#%s%s:%s", scope, tag->kind->name, tag->name); - - if (tag->extensionFields.scopeKind != NULL && + langType lang = (tag->extensionFields.scopeLangType == LANG_AUTO) + ? tag->langType + : tag->extensionFields.scopeLangType; + kindDefinition *scopeKindDef = getLanguageKind(lang, + tag->extensionFields.scopeKindIndex); + printf ("<#%s%s:%s", scope, getTagKindName(tag), tag->name); + + if (tag->extensionFields.scopeKindIndex != KIND_GHOST_INDEX && tag->extensionFields.scopeName != NULL) - printf (" [%s:%s]", tag->extensionFields.scopeKind->name, + printf (" [%s:%s]", scopeKindDef->name, tag->extensionFields.scopeName); if (isFieldEnabled (FIELD_INHERITANCE) && @@ -125,4 +135,88 @@ extern void debugAssert (const char *assertion, const char *file, unsigned int l abort(); } +static int debugScopeDepth; +#define DEBUG_INDENT_UNIT 4 + +static char debugPrefix[DEBUG_INDENT_UNIT + 1]; + +extern void debugInit (void) +{ + memset(debugPrefix, ' ', DEBUG_INDENT_UNIT); + debugPrefix[DEBUG_INDENT_UNIT] = '\0'; +} + +extern void debugIndent(void) +{ + for(int i=0;i< debugScopeDepth;i++) + fputs(debugPrefix, stderr); +} + +extern void debugInc(void) +{ + debugScopeDepth++; +} + +extern void debugDec(void) +{ + debugScopeDepth--; + if(debugScopeDepth < 0) + debugScopeDepth = 0; +} + + + +struct circularRefChecker { + hashTable *visitTable; + int counter; +}; + +extern void circularRefCheckerDestroy (struct circularRefChecker * checker) +{ + hashTableDelete (checker->visitTable); + checker->visitTable = NULL; + eFree (checker); +} + +extern struct circularRefChecker * circularRefCheckerNew (void) +{ + Assert (sizeof(void *) >= sizeof(int)); + + struct circularRefChecker *c = xMalloc (1, struct circularRefChecker); + + c->visitTable = hashTableNew (17, hashPtrhash, hashPtreq, NULL, NULL); + c->counter = 0; + + return c; +} + +extern int circularRefCheckerCheck (struct circularRefChecker *c, void *ptr) +{ + union conv { + int i; + void *ptr; + } v; + + v.ptr = hashTableGetItem(c->visitTable, ptr); + if (v.ptr) + return v.i; + else + { + v.i = ++c->counter; + hashTablePutItem (c->visitTable, ptr, v.ptr); + return 0; + } +} + +extern int circularRefCheckerGetCurrent (struct circularRefChecker *c) +{ + return c->counter; +} + +extern void circularRefCheckClear (struct circularRefChecker *c) +{ + hashTableClear (c->visitTable); + c->counter = 0; +} + #endif diff --git a/ctags/main/debug.h b/ctags/main/debug.h index 4d9185df71..1ffd9638a1 100644 --- a/ctags/main/debug.h +++ b/ctags/main/debug.h @@ -14,27 +14,26 @@ */ #include "general.h" /* must always come first */ +#include "gvars.h" +#include "types.h" #ifdef DEBUG # include #endif -#include "entry.h" /* * Macros */ #ifdef DEBUG -# define debug(level) ((Option.debugLevel & (long)(level)) != 0) +# define debug(level) ((ctags_debugLevel & (long)(level)) != 0) # define DebugStatement(x) x # define PrintStatus(x) if (debug(DEBUG_STATUS)) printf x; # ifdef NDEBUG # define Assert(c) do {} while(0) # define AssertNotReached() do {} while(0) # else - /* based on glibc's assert.h __ASSERT_FUNCTION */ -# if defined (__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 4)) -# define ASSERT_FUNCTION __PRETTY_FUNCTION__ -# elif defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L + /* We expect cc supports c99 standard. */ +# if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L # define ASSERT_FUNCTION __func__ # else # define ASSERT_FUNCTION ((const char*)0) @@ -79,4 +78,21 @@ extern void debugCppIgnore (const bool ignore); extern void debugEntry (const tagEntryInfo *const tag); extern void debugAssert (const char *assertion, const char *file, unsigned int line, const char *function) attr__noreturn; +#ifdef DEBUG +#define DEBUG_INIT() debugInit() +extern void debugInit (void); +extern void debugIndent(void); +extern void debugInc(void); +extern void debugDec(void); + +struct circularRefChecker; +extern struct circularRefChecker * circularRefCheckerNew (void); +extern void circularRefCheckerDestroy (struct circularRefChecker * checker); +extern int circularRefCheckerCheck (struct circularRefChecker *c, void *ptr); +extern int circularRefCheckerGetCurrent (struct circularRefChecker *c); +extern void circularRefCheckClear (struct circularRefChecker *c); + +#else +#define DEBUG_INIT() do { } while(0) +#endif /* DEBUG */ #endif /* CTAGS_MAIN_DEBUG_H */ diff --git a/ctags/main/dependency.c b/ctags/main/dependency.c index a78b55357c..037192e0f0 100644 --- a/ctags/main/dependency.c +++ b/ctags/main/dependency.c @@ -12,91 +12,340 @@ #include "general.h" /* must always come first */ +#include "debug.h" #include "dependency.h" -#include "parse.h" +#include "options.h" +#include "parse_p.h" +#include "read.h" +#include "routines.h" +#include "subparser.h" +#include "xtag.h" #include +struct slaveControlBlock { + slaveParser *slaveParsers; /* The parsers on this list must be initialized when + this parser is initialized. */ + subparser *subparsersDefault; + subparser *subparsersInUse; + langType owner; +}; -static void linkKinds (kindDefinition *masterKind, kindDefinition *slaveKind) +extern void linkDependencyAtInitializeParsing (depType dtype, + parserDefinition *const master, + struct slaveControlBlock *masterSCB, + struct kindControlBlock *masterKCB, + parserDefinition *const slave, + struct kindControlBlock *slaveKCB, + void *data) { - kindDefinition *tail; + if (dtype == DEPTYPE_KIND_OWNER) + linkKindDependency (masterKCB, slaveKCB); + else if (dtype == DEPTYPE_SUBPARSER) + { + slaveParser *s = xMalloc (1, slaveParser); - slaveKind->master = masterKind; + s->type = dtype; + s->id = slave->id; + s->data = data; - tail = slaveKind; - while (tail->slave) - { - tail->enabled = masterKind->enabled; - tail = tail->slave; + s->next = masterSCB->slaveParsers; + masterSCB->slaveParsers = s; } +} - tail->slave = masterKind->slave; - masterKind->slave = slaveKind; +static void attachSubparser (struct slaveControlBlock *base_sb, subparser *subparser) +{ + subparser->next = base_sb->subparsersDefault; + base_sb->subparsersDefault = subparser; } -static void linkKindDependency (parserDefinition *const masterParser, - parserDefinition *const slaveParser) + +extern struct slaveControlBlock *allocSlaveControlBlock (parserDefinition *parser) { - unsigned int k_slave, k_master; - kindDefinition *kind_slave, *kind_master; + struct slaveControlBlock *cb; - for (k_slave = 0; k_slave < slaveParser->kindCount; k_slave++) + cb = xMalloc (1, struct slaveControlBlock); + cb->slaveParsers = NULL; + cb->subparsersDefault = NULL; + cb->subparsersInUse = NULL; + cb->owner = parser->id; + + return cb; +} + +extern void freeSlaveControlBlock (struct slaveControlBlock *cb) +{ + eFree (cb); +} + +extern void initializeDependencies (parserDefinition *parser, + struct slaveControlBlock *cb) +{ + unsigned int i; + slaveParser *sp; + + /* Initialize slaves */ + sp = cb->slaveParsers; + while (sp != NULL) { - if (slaveParser->kindTable [k_slave].syncWith == LANG_AUTO) + if (sp->type == DEPTYPE_SUBPARSER) { - kind_slave = slaveParser->kindTable + k_slave; - for (k_master = 0; k_master < masterParser->kindCount; k_master++) + subparser *sub; + + sub = (subparser *)sp->data; + sub->slaveParser = sp; + } + + if (sp->type == DEPTYPE_KIND_OWNER + || (sp->type == DEPTYPE_SUBPARSER && + (((subparser *)sp->data)->direction & SUBPARSER_BASE_RUNS_SUB))) + { + initializeParser (sp->id); + if (sp->type == DEPTYPE_SUBPARSER + && isXtagEnabled (XTAG_TAGS_GENERATED_BY_SUBPARSER)) { - kind_master = masterParser->kindTable + k_master; - if ((kind_slave->letter == kind_master->letter) - && (strcmp (kind_slave->name, kind_master->name) == 0)) - { - linkKinds (kind_master, kind_slave); - kind_slave->syncWith = masterParser->id; - kind_master->syncWith = masterParser->id; - break; - } + subparser *subparser = sp->data; + attachSubparser (cb, subparser); } } + sp = sp->next; + } + + /* Initialize masters that act as base parsers. */ + for (i = 0; i < parser->dependencyCount; i++) + { + parserDependency *d = parser->dependencies + i; + if (d->type == DEPTYPE_SUBPARSER && + ((subparser *)(d->data))->direction & SUBPARSER_SUB_RUNS_BASE) + { + langType baseParser; + baseParser = getNamedLanguage (d->upperParser, 0); + Assert (baseParser != LANG_IGNORE); + initializeParser (baseParser); + } } } -extern void linkDependencyAtInitializeParsing (depType dtype, - parserDefinition *const masterParser, - parserDefinition *const slaveParser) +extern void finalizeDependencies (parserDefinition *parser, + struct slaveControlBlock *cb) { - if (dtype == DEPTYPE_KIND_OWNER) - linkKindDependency (masterParser, slaveParser); - else if (dtype == DEPTYPE_SUBPARSER) + while (cb->slaveParsers) { - subparser *s = xMalloc (1, subparser); + slaveParser *sp = cb->slaveParsers; + cb->slaveParsers = sp->next; + sp->next = NULL; + eFree (sp); + } +} + +extern void notifyInputStart (void) +{ + subparser *s; - s->id = slaveParser->id; - s->next = masterParser->subparsers; - masterParser->subparsers = s; + foreachSubparser(s, false) + { + langType lang = getSubparserLanguage (s); + notifyLanguageRegexInputStart (lang); + + if (s->inputStart) + { + enterSubparser(s); + s->inputStart (s); + leaveSubparser(); + } } } -extern void initializeSubparsers (const parserDefinition *parser) +extern void notifyInputEnd (void) { - subparser *sp; + subparser *s; + + foreachSubparser(s, false) + { + if (s->inputEnd) + { + enterSubparser(s); + s->inputEnd (s); + leaveSubparser(); + } + + langType lang = getSubparserLanguage (s); + notifyLanguageRegexInputEnd (lang); + } +} - for (sp = parser->subparsers; sp; sp = sp->next) - initializeParser (sp->id); +extern void notifyMakeTagEntry (const tagEntryInfo *tag, int corkIndex) +{ + subparser *s; + + foreachSubparser(s, false) + { + if (s->makeTagEntryNotify) + { + enterSubparser(s); + s->makeTagEntryNotify (s, tag, corkIndex); + leaveSubparser(); + } + } +} + +extern langType getSubparserLanguage (subparser *s) +{ + return s->slaveParser->id; +} + +extern void chooseExclusiveSubparser (subparser *s, void *data) +{ + if (s->exclusiveSubparserChosenNotify) + { + s->chosenAsExclusiveSubparser = true; + enterSubparser(s); + s->exclusiveSubparserChosenNotify (s, data); + verbose ("%s is chosen as exclusive subparser\n", + getLanguageName (getSubparserLanguage (s))); + leaveSubparser(); + } } -extern void finalizeSubparsers (parserDefinition *parser) +extern subparser *getFirstSubparser(struct slaveControlBlock *controlBlock) +{ + if (controlBlock) + return controlBlock->subparsersInUse; + return NULL; +} + +extern void useDefaultSubparsers (struct slaveControlBlock *controlBlock) +{ + controlBlock->subparsersInUse = controlBlock->subparsersDefault; +} + +extern void useSpecifiedSubparser (struct slaveControlBlock *controlBlock, subparser *s) +{ + s->schedulingBaseparserExplicitly = true; + controlBlock->subparsersInUse = s; +} + +extern void setupSubparsersInUse (struct slaveControlBlock *controlBlock) +{ + if (!controlBlock->subparsersInUse) + useDefaultSubparsers(controlBlock); +} + +extern subparser* teardownSubparsersInUse (struct slaveControlBlock *controlBlock) { - subparser *sp; subparser *tmp; + subparser *s = NULL; - for (sp = parser->subparsers; sp;) + tmp = controlBlock->subparsersInUse; + controlBlock->subparsersInUse = NULL; + + if (tmp && tmp->schedulingBaseparserExplicitly) { - tmp = sp; - sp = sp->next; - tmp->next = NULL; - eFree (tmp); + tmp->schedulingBaseparserExplicitly = false; + s = tmp; } - parser->subparsers = NULL; + + if (s) + return s; + + while (tmp) + { + if (tmp->chosenAsExclusiveSubparser) + { + s = tmp; + } + tmp = tmp->next; + } + + return s; +} + + +static int subparserDepth; + +extern void enterSubparser(subparser *subparser) +{ + subparserDepth++; + pushLanguage (getSubparserLanguage (subparser)); +} + +extern void leaveSubparser(void) +{ + popLanguage (); + subparserDepth--; +} + +extern bool doesSubparserRun (void) +{ + return subparserDepth; +} + +extern slaveParser *getFirstSlaveParser (struct slaveControlBlock *scb) +{ + if (scb) + return scb->slaveParsers; + return NULL; +} + +extern struct colprintTable * subparserColprintTableNew (void) +{ + return colprintTableNew ("L:NAME", "L:BASEPARSER", "L:DIRECTIONS", NULL); +} + +extern void subparserColprintAddSubparsers (struct colprintTable *table, + struct slaveControlBlock *scb) +{ + slaveParser *tmp; + + pushLanguage (scb->owner); + foreachSlaveParser(tmp) + { + struct colprintLine *line = colprintTableGetNewLine(table); + + colprintLineAppendColumnCString (line, getLanguageName (tmp->id)); + colprintLineAppendColumnCString (line, getLanguageName (scb->owner)); + + const char *direction; + switch (((subparser *)tmp->data)->direction) + { + case SUBPARSER_BASE_RUNS_SUB: + direction = "base => sub {shared}"; + break; + case SUBPARSER_SUB_RUNS_BASE: + direction = "base <= sub {dedicated}"; + break; + case SUBPARSER_BI_DIRECTION: + direction = "base <> sub {bidirectional}"; + break; + default: + direction = "UNKNOWN(INTERNAL BUG)"; + break; + } + colprintLineAppendColumnCString (line, direction); + } + popLanguage (); +} + +static int subparserColprintCompareLines (struct colprintLine *a , struct colprintLine *b) +{ + const char *a_name = colprintLineGetColumn (a, 0); + const char *b_name = colprintLineGetColumn (b, 0); + + int r; + r = strcmp (a_name, b_name); + if (r != 0) + return r; + + const char *a_baseparser = colprintLineGetColumn (a, 1); + const char *b_baseparser = colprintLineGetColumn (b, 1); + + return strcmp(a_baseparser, b_baseparser); +} + +extern void subparserColprintTablePrint (struct colprintTable *table, + bool withListHeader, bool machinable, FILE *fp) +{ + colprintTableSort (table, subparserColprintCompareLines); + colprintTablePrint (table, 0, withListHeader, machinable, fp); } diff --git a/ctags/main/dependency.h b/ctags/main/dependency.h index 551e81a1c1..e873040a78 100644 --- a/ctags/main/dependency.h +++ b/ctags/main/dependency.h @@ -15,7 +15,7 @@ #include "general.h" #include "types.h" - +#include "kind.h" typedef enum eDepType { DEPTYPE_KIND_OWNER, @@ -23,23 +23,41 @@ typedef enum eDepType { COUNT_DEPTYPES, } depType; -typedef struct sParserDependency { +struct sParserDependency { depType type; const char *upperParser; void *data; -} parserDependency; - -extern void linkDependencyAtInitializeParsing (depType dtype, - parserDefinition *const masterParser, - parserDefinition *const slaveParser); +}; -typedef struct sSubparser subparser; -struct sSubparser { +struct sSlaveParser { + depType type; langType id; - subparser *next; + void *data; + slaveParser *next; }; -extern void initializeSubparsers (const parserDefinition *parser); -extern void finalizeSubparsers (parserDefinition *parser); +struct slaveControlBlock; /* Opaque data type for parse.c */ + + +extern void linkDependencyAtInitializeParsing (depType dtype, + parserDefinition *const master, + struct slaveControlBlock *masterSCB, + struct kindControlBlock *masterKCB, + parserDefinition *const slave, + struct kindControlBlock *slaveKCB, + void *data); + +extern struct slaveControlBlock *allocSlaveControlBlock (parserDefinition *parser); +extern void freeSlaveControlBlock (struct slaveControlBlock *cb); +extern void initializeDependencies (parserDefinition *parser, + struct slaveControlBlock *cb); +extern void finalizeDependencies (parserDefinition *parser, + struct slaveControlBlock *cb); + +extern slaveParser *getFirstSlaveParser(struct slaveControlBlock *controlBlock); +extern slaveParser *getNextSlaveParser(slaveParser *last); +#define foreachSlaveParser(VAR) \ + VAR = NULL; \ + while ((VAR = getNextSlaveParser (VAR)) != NULL) #endif /* CTAGS_MAIN_DEPENDENCY_H */ diff --git a/ctags/main/e_msoft.h b/ctags/main/e_msoft.h index e095197521..95242c8148 100644 --- a/ctags/main/e_msoft.h +++ b/ctags/main/e_msoft.h @@ -27,13 +27,14 @@ #define HAVE_STRSTR 1 #define HAVE_STRERROR 1 #define HAVE_FINDNEXT 1 -#define HAVE_TEMPNAM 1 +#define HAVE_MKSTEMP 1 #define HAVE_FNMATCH 1 #define HAVE_FNMATCH_H 1 #define HAVE_PUTENV 1 -#define tempnam(dir,pfx) _tempnam(dir,pfx) #define TMPDIR "\\" +int mkstemp (char *template_name); + #ifdef _MSC_VER # define HAVE__FINDFIRST 1 @@ -58,7 +59,6 @@ typedef enum { false, true } bool; # include <_mingw.h> # define HAVE_STDBOOL_H 1 -# define HAVE_DIR_H 1 # define HAVE_DIRENT_H 1 # define HAVE__FINDFIRST 1 # define findfirst_t long @@ -66,6 +66,10 @@ typedef enum { false, true } bool; # define FA_DIREC _A_SUBDIR # define ff_name name +# if defined(__USE_MINGW_ANSI_STDIO) && defined(__MINGW64_VERSION_MAJOR) +# define HAVE_ASPRINTF 1 +# endif + #endif #endif diff --git a/ctags/main/entry.c b/ctags/main/entry.c index b2ea525eca..d1182b35ab 100644 --- a/ctags/main/entry.c +++ b/ctags/main/entry.c @@ -36,19 +36,27 @@ # include #endif +#include + #include "debug.h" -#include "entry.h" +#include "entry_p.h" #include "field.h" -#include "fmt.h" +#include "fmt_p.h" #include "kind.h" #include "main.h" -#include "options.h" -#include "output.h" -#include "ptag.h" +#include "nestlevel.h" +#include "options_p.h" +#include "ptag_p.h" #include "read.h" #include "routines.h" +#include "routines_p.h" +#include "parse_p.h" +#include "ptrarray.h" #include "sort.h" #include "strlist.h" +#include "subparser.h" +#include "trashbox.h" +#include "writer_p.h" #include "xtag.h" /* @@ -96,7 +104,7 @@ typedef struct eTagFile { * DATA DEFINITIONS */ -tagFile TagFile = { +static tagFile TagFile = { NULL, /* tag file name */ NULL, /* tag file directory (absolute) */ NULL, /* file pointer */ @@ -169,9 +177,7 @@ static void rememberMaxLengths (const size_t nameLength, const size_t lineLength static void addCommonPseudoTags (void) { - int i; - - for (i = 0; i < PTAG_COUNT; i++) + for (int i = 0; i < PTAG_COUNT; i++) { if (isPtagCommonInParsers (i)) makePtagIfEnabled (i, NULL); @@ -180,41 +186,29 @@ static void addCommonPseudoTags (void) extern void makeFileTag (const char *const fileName) { - xtagType xtag = XTAG_UNKNOWN; - - if (isXtagEnabled(XTAG_FILE_NAMES)) - xtag = XTAG_FILE_NAMES; - - if (xtag != XTAG_UNKNOWN) - { - tagEntryInfo tag; - kindDefinition *kind; - - kind = getInputLanguageFileKind(); - Assert (kind); - kind->enabled = isXtagEnabled(XTAG_FILE_NAMES); - - /* TODO: you can return here if enabled == false. */ + tagEntryInfo tag; - initTagEntry (&tag, baseFilename (fileName), KIND_FILE_INDEX); + if (!isXtagEnabled(XTAG_FILE_NAMES)) + return; - tag.isFileEntry = true; - tag.lineNumberEntry = true; - markTagExtraBit (&tag, xtag); + initTagEntry (&tag, baseFilename (fileName), KIND_FILE_INDEX); - tag.lineNumber = 1; - if (isFieldEnabled (FIELD_END)) - { - /* isFieldEnabled is called again in the rendering - stage. However, it is called here for avoiding - unnecessary read line loop. */ - while (readLineFromInputFile () != NULL) - ; /* Do nothing */ - tag.extensionFields.endLine = getInputLineNumber (); - } + tag.isFileEntry = true; + tag.lineNumberEntry = true; + markTagExtraBit (&tag, XTAG_FILE_NAMES); - makeTagEntry (&tag); + tag.lineNumber = 1; + if (isFieldEnabled (FIELD_END_LINE)) + { + /* isFieldEnabled is called again in the rendering + stage. However, it is called here for avoiding + unnecessary read line loop. */ + while (readLineFromInputFile () != NULL) + ; /* Do nothing */ + tag.extensionFields.endLine = getInputLineNumber (); } + + makeTagEntry (&tag); } static void updateSortedFlag ( @@ -399,9 +393,20 @@ extern void openTagFile (void) */ if (TagsToStdout) { - /* Open a tempfile with read and write mode. Read mode is used when - * write the result to stdout. */ - TagFile.mio = tempFile ("w+", &TagFile.name); + if (Option.sorted == SO_UNSORTED) + { + /* Passing NULL for keeping stdout open. + stdout can be used for debugging purpose.*/ + TagFile.mio = mio_new_fp(stdout, NULL); + TagFile.name = eStrdup ("/dev/stdout"); + } + else + { + /* Open a tempfile with read and write mode. Read mode is used when + * write the result to stdout. */ + TagFile.mio = tempFile ("w+", &TagFile.name); + } + if (isXtagEnabled (XTAG_PSEUDO_TAGS)) addCommonPseudoTags (); } @@ -445,10 +450,14 @@ extern void openTagFile (void) if (TagFile.mio == NULL) error (FATAL | PERROR, "cannot open tag file"); } - if (TagsToStdout) - TagFile.directory = eStrdup (CurrentDirectory); - else - TagFile.directory = absoluteDirname (TagFile.name); + + if (TagFile.directory == NULL) + { + if (TagsToStdout) + TagFile.directory = eStrdup (CurrentDirectory); + else + TagFile.directory = absoluteDirname (TagFile.name); + } } #ifdef USE_REPLACEMENT_TRUNCATE @@ -495,6 +504,7 @@ static void copyFile (const char *const from, const char *const to, const long s */ static int replacementTruncate (const char *const name, const long size) { +#define WHOLE_FILE -1L char *tempName = NULL; MIO *mio = tempFile ("w", &tempName); mio_free (mio); @@ -605,6 +615,14 @@ extern void closeTagFile (const bool resize) if (Option.etags) writeEtagsIncludes (TagFile.mio); mio_flush (TagFile.mio); + + if ((TagsToStdout && (Option.sorted == SO_UNSORTED))) + { + if (mio_free (TagFile.mio) != 0) + error (FATAL | PERROR, "cannot close tag file"); + goto out; + } + abort_if_ferror (TagFile.mio); desiredSize = mio_tell (TagFile.mio); mio_seek (TagFile.mio, 0L, SEEK_END); @@ -628,6 +646,8 @@ extern void closeTagFile (const bool resize) error (FATAL | PERROR, "cannot close tag file"); remove (tagFileName ()); /* remove temporary file */ } + + out: eFree (TagFile.name); TagFile.name = NULL; } @@ -645,6 +665,7 @@ static size_t appendInputLine (int putc_func (char , void *), const char *const { size_t length = 0; const char *p; + int extraLength = 0; /* Write everything up to, but not including, a line end character. */ @@ -657,7 +678,11 @@ static size_t appendInputLine (int putc_func (char , void *), const char *const if (c == CRETURN || c == NEWLINE) break; - if (Option.patternLengthLimit != 0 && length >= Option.patternLengthLimit) + if (Option.patternLengthLimit != 0 && length >= Option.patternLengthLimit && + /* Do not cut inside a multi-byte UTF-8 character, but safe-guard it not to + * allow more than one extra valid UTF-8 character in case it's not actually + * UTF-8. To do that, limit to an extra 3 UTF-8 sub-bytes (0b10xxxxxx). */ + ((((unsigned char) c) & 0xc0) != 0x80 || ++extraLength > 3)) { *omitted = true; break; @@ -692,6 +717,7 @@ static int vstring_puts (const char* s, void *data) return vStringLength (str) - len; } +#if DEBUG static bool isPosSet(MIOPos pos) { char * p = (char *)&pos; @@ -702,24 +728,19 @@ static bool isPosSet(MIOPos pos) r |= p[i]; return r; } +#endif -extern char *readLineFromBypassAnyway (vString *const vLine, const tagEntryInfo *const tag, +extern char *readLineFromBypassForTag (vString *const vLine, const tagEntryInfo *const tag, long *const pSeekValue) { - char * line; - - if (isPosSet (tag->filePosition) || (tag->pattern == NULL)) - line = readLineFromBypass (vLine, tag->filePosition, pSeekValue); - else - line = readLineFromBypassSlow (vLine, tag->lineNumber, tag->pattern, pSeekValue); - - return line; + Assert (isPosSet (tag->filePosition) || (tag->pattern == NULL)); + return readLineFromBypass (vLine, tag->filePosition, pSeekValue); } /* Truncates the text line containing the tag at the character following the * tag, providing a character which designates the end of the tag. */ -extern void truncateTagLine ( +extern void truncateTagLineAfterTag ( char *const line, const char *const token, const bool discardNewline) { char *p = strstr (line, token); @@ -790,8 +811,8 @@ extern void getTagScopeInformation (tagEntryInfo *const tag, && tag->extensionFields.scopeIndex != CORK_NIL && TagFile.corkQueue.count > 0) { - const tagEntryInfo * scope = NULL; - char *full_qualified_scope_name = NULL; + const tagEntryInfo * scope; + char *full_qualified_scope_name; scope = getEntryInCorkQueue (tag->extensionFields.scopeIndex); full_qualified_scope_name = getFullQualifiedScopeNameFromCorkQueue(scope); @@ -821,9 +842,9 @@ extern void getTagScopeInformation (tagEntryInfo *const tag, } -extern int makePatternStringCommon (const tagEntryInfo *const tag, - int putc_func (char , void *), - int puts_func (const char* , void *), +static int makePatternStringCommon (const tagEntryInfo *const tag, + int (* putc_func) (char , void *), + int (* puts_func) (const char* , void *), void *output) { int length = 0; @@ -845,20 +866,25 @@ extern int makePatternStringCommon (const tagEntryInfo *const tag, && (memcmp (&tag->filePosition, &cached_location, sizeof(MIOPos)) == 0)) return puts_func (vStringValue (cached_pattern), output); - line = readLineFromBypass (TagFile.vLine, tag->filePosition, NULL); + line = readLineFromBypassForTag (TagFile.vLine, tag, NULL); if (line == NULL) - error (FATAL, "could not read tag line from %s at line %lu", getInputFileName (),tag->lineNumber); + { + /* This can be occurs if the size of input file is zero, and + an empty regex pattern (//) matches to the input. */ + line = ""; + } + if (tag->truncateLineAfterTag) - truncateTagLine (line, tag->name, false); + truncateTagLineAfterTag (line, tag->name, false); line_len = strlen (line); searchChar = Option.backward ? '?' : '/'; - terminator = (bool) (line [line_len - 1] == '\n') ? "$": ""; + terminator = (line_len > 0 && (line [line_len - 1] == '\n')) ? "$": ""; if (!tag->truncateLineAfterTag) { making_cache = true; - cached_pattern = vStringNewOrClear (cached_pattern); + cached_pattern = vStringNewOrClearWithAutoRelease (cached_pattern); puts_o_func = puts_func; o_output = output; @@ -891,17 +917,54 @@ extern char* makePatternString (const tagEntryInfo *const tag) return vStringDeleteUnwrap (pattern); } -extern void attachParserField (tagEntryInfo *const tag, fieldType ftype, const char * value) +static tagField * tagFieldNew(fieldType ftype, const char *value, bool valueOwner) { - Assert (tag->usedParserFields < PRE_ALLOCATED_PARSER_FIELDS); + tagField *f = xMalloc (1, tagField); - tag->parserFields [tag->usedParserFields].ftype = ftype; - tag->parserFields [tag->usedParserFields].value = value; - tag->usedParserFields++; + f->ftype = ftype; + f->value = value; + f->valueOwner = valueOwner; + return f; +} + +static void tagFieldDelete (tagField * f) +{ + if (f->valueOwner) + eFree((void *)f->value); + eFree (f); +} + +static void attachParserFieldGeneric (tagEntryInfo *const tag, fieldType ftype, const char * value, + bool valueOwner) +{ + if (tag->usedParserFields < PRE_ALLOCATED_PARSER_FIELDS) + { + tag->parserFields [tag->usedParserFields].ftype = ftype; + tag->parserFields [tag->usedParserFields].value = value; + tag->parserFields [tag->usedParserFields].valueOwner = valueOwner; + tag->usedParserFields++; + } + else if (tag->parserFieldsDynamic == NULL) + { + tag->parserFieldsDynamic = ptrArrayNew((ptrArrayDeleteFunc)tagFieldDelete); + PARSER_TRASH_BOX(tag->parserFieldsDynamic, ptrArrayDelete); + attachParserFieldGeneric (tag, ftype, value, valueOwner); + } + else + { + tagField *f = tagFieldNew (ftype, value, valueOwner); + ptrArrayAdd(tag->parserFieldsDynamic, f); + tag->usedParserFields++; + } +} + +extern void attachParserField (tagEntryInfo *const tag, fieldType ftype, const char * value) +{ + attachParserFieldGeneric (tag, ftype, value, false); } extern void attachParserFieldToCorkEntry (int index, - fieldType type, + fieldType ftype, const char *value) { tagEntryInfo * tag; @@ -914,7 +977,21 @@ extern void attachParserFieldToCorkEntry (int index, Assert (tag != NULL); v = eStrdup (value); - attachParserField (tag, type, v); + attachParserFieldGeneric (tag, ftype, v, true); +} + +extern const tagField* getParserField (const tagEntryInfo * tag, int index) +{ + if (index < 0 + || tag->usedParserFields <= ((unsigned int)index) ) + return NULL; + else if (index < PRE_ALLOCATED_PARSER_FIELDS) + return tag->parserFields + index; + else + { + unsigned int n = index - PRE_ALLOCATED_PARSER_FIELDS; + return ptrArrayItem(tag->parserFieldsDynamic, n); + } } static void copyParserFields (const tagEntryInfo *const tag, tagEntryInfo* slot) @@ -924,14 +1001,19 @@ static void copyParserFields (const tagEntryInfo *const tag, tagEntryInfo* slot) for (i = 0; i < tag->usedParserFields; i++) { - value = tag->parserFields [i].value; + const tagField *f = getParserField (tag, i); + Assert(f); + + value = f->value; if (value) value = eStrdup (value); - attachParserField (slot, - tag->parserFields [i].ftype, - value); + attachParserFieldGeneric (slot, + f->ftype, + value, + true); } + } static void recordTagEntryInQueue (const tagEntryInfo *const tag, tagEntryInfo* slot) @@ -940,8 +1022,6 @@ static void recordTagEntryInQueue (const tagEntryInfo *const tag, tagEntryInfo* if (slot->pattern) slot->pattern = eStrdup (slot->pattern); - else if (!slot->lineNumberEntry) - slot->pattern = makePatternString (slot); slot->inputFileName = eStrdup (slot->inputFileName); slot->name = eStrdup (slot->name); @@ -970,26 +1050,47 @@ static void recordTagEntryInQueue (const tagEntryInfo *const tag, tagEntryInfo* slot->extensionFields.xpath = eStrdup (slot->extensionFields.xpath); #endif + if (slot->extraDynamic) + { + int n = countXtags () - XTAG_COUNT; + slot->extraDynamic = xCalloc ((n / 8) + 1, uint8_t); + memcpy (slot->extraDynamic, tag->extraDynamic, (n / 8) + 1); + } + if (slot->sourceFileName) slot->sourceFileName = eStrdup (slot->sourceFileName); + slot->usedParserFields = 0; + slot->parserFieldsDynamic = NULL; copyParserFields (tag, slot); + if (slot->parserFieldsDynamic) + PARSER_TRASH_BOX_TAKE_BACK(slot->parserFieldsDynamic); } static void clearParserFields (tagEntryInfo *const tag) { - unsigned int i; + unsigned int i, n; const char* value; - for (i = 0; i < tag->usedParserFields; i++) + if ( tag->usedParserFields < PRE_ALLOCATED_PARSER_FIELDS ) + n = tag->usedParserFields; + else + n = PRE_ALLOCATED_PARSER_FIELDS; + + for (i = 0; i < n; i++) { value = tag->parserFields[i].value; - if (value) + if (value && tag->parserFields[i].valueOwner) eFree ((char *)value); tag->parserFields[i].value = NULL; tag->parserFields[i].ftype = FIELD_UNKNOWN; } + if (tag->parserFieldsDynamic) + { + ptrArrayDelete (tag->parserFieldsDynamic); + tag->parserFieldsDynamic = NULL; + } } static void clearTagEntryInQueue (tagEntryInfo* slot) @@ -1024,6 +1125,9 @@ static void clearTagEntryInQueue (tagEntryInfo* slot) eFree ((char *)slot->extensionFields.xpath); #endif + if (slot->extraDynamic) + eFree (slot->extraDynamic); + if (slot->sourceFileName) eFree ((char *)slot->sourceFileName); @@ -1059,39 +1163,69 @@ static unsigned int queueTagEntry(const tagEntryInfo *const tag) } -static void *writerData; -static tagWriter *writer; - -extern void setTagWriter (tagWriter *t) +extern void setupWriter (void) { - writer = t; + writerSetup (TagFile.mio); } -extern bool outpuFormatUsedStdoutByDefault (void) +extern bool teardownWriter (const char *filename) { - return writer->useStdoutByDefault; + return writerTeardown (TagFile.mio, filename); } -extern void setupWriter (void) +static bool isTagWritable(const tagEntryInfo *const tag) { - if (writer->preWriteEntry) - writerData = writer->preWriteEntry (TagFile.mio); - else - writerData = NULL; -} + if (tag->placeholder) + return false; -extern void teardownWriter (const char *filename) -{ - if (writer->postWriteEntry) - writer->postWriteEntry (TagFile.mio, filename, writerData); -} + if (! isLanguageKindEnabled(tag->langType, tag->kindIndex)) + return false; -static void buildFqTagCache (const tagEntryInfo *const tag) -{ - renderFieldEscaped (FIELD_SCOPE_KIND_LONG, tag, NO_PARSER_FIELD); - renderFieldEscaped (FIELD_SCOPE, tag, NO_PARSER_FIELD); + if (tag->extensionFields.roleBits) + { + size_t available_roles; + + if (!isXtagEnabled (XTAG_REFERENCE_TAGS)) + return false; + + available_roles = countLanguageRoles(tag->langType, + tag->kindIndex); + if (tag->extensionFields.roleBits >= + (makeRoleBit(available_roles))) + return false; + + /* TODO: optimization + A Bitmasks represeting all enabled roles can be calculated at the + end of initializing the parser. Calculating each time when checking + a tag entry is not needed. */ + for (unsigned int roleIndex = 0; roleIndex < available_roles; roleIndex++) + { + if (isRoleAssigned(tag, roleIndex)) + { + if (isLanguageRoleEnabled (tag->langType, tag->kindIndex, + roleIndex)) + return true; + } + + } + return false; + } + else if (isLanguageKindRefOnly(tag->langType, tag->kindIndex)) + { + error (WARNING, "definition tag for refonly kind(%s) is made: %s", + getLanguageKind(tag->langType, tag->kindIndex)->name, + tag->name); + /* This one is not so critical. */ + } + + if (!isXtagEnabled(XTAG_ANONYMOUS) + && isTagExtraBitMarked(tag, XTAG_ANONYMOUS)) + return false; + + return true; } + #ifdef CTAGS_LIB static void initCtagsTag(ctagsTag *tag, const tagEntryInfo *info) { @@ -1109,27 +1243,24 @@ static void initCtagsTag(ctagsTag *tag, const tagEntryInfo *info) } #endif -static void writeTagEntry (const tagEntryInfo *const tag) +static void writeTagEntry (const tagEntryInfo *const tag, bool checkingNeeded) { int length = 0; - if (tag->placeholder) - return; -#ifndef CTAGS_LIB - if (! tag->kind->enabled) - return; -#endif - if (tag->extensionFields.roleIndex != ROLE_INDEX_DEFINITION - && ! isXtagEnabled (XTAG_REFERENCE_TAGS)) + Assert (tag->kindIndex != KIND_GHOST_INDEX); + + if (checkingNeeded && !isTagWritable(tag)) return; DebugStatement ( debugEntry (tag); ) - Assert (writer); if (includeExtensionFlags () && isXtagEnabled (XTAG_QUALIFIED_TAGS) && doesInputLanguageRequestAutomaticFQTag ()) - buildFqTagCache (tag); + { + /* const is discarded to update the cache field of TAG. */ + writerBuildFqTagCache ( (tagEntryInfo *const)tag); + } #ifdef CTAGS_LIB getTagScopeInformation((tagEntryInfo *)tag, NULL, NULL); @@ -1142,7 +1273,7 @@ static void writeTagEntry (const tagEntryInfo *const tag) length = TagEntryFunction(&t, TagEntryUserData); } #else - length = writer->writeEntry (TagFile.mio, tag, writerData); + length = writerWriteTag (TagFile.mio, tag); #endif ++TagFile.numTags.added; @@ -1159,11 +1290,11 @@ extern bool writePseudoTag (const ptagDesc *desc, { int length; - if (writer->writePtagEntry == NULL) + length = writerWritePtag (TagFile.mio, desc, fileName, + pattern, parserName); + if (length < 0) return false; - length = writer->writePtagEntry (TagFile.mio, desc, fileName, - pattern, parserName, writerData); abort_if_ferror (TagFile.mio); ++TagFile.numTags.added; @@ -1196,7 +1327,7 @@ extern void uncorkTagFile(void) for (i = 1; i < TagFile.corkQueue.count; i++) { tagEntryInfo *tag = TagFile.corkQueue.queue + i; - writeTagEntry (tag); + writeTagEntry (tag, true); if (doesInputLanguageRequestAutomaticFQTag () && isXtagEnabled (XTAG_QUALIFIED_TAGS) && (tag->extensionFields.scopeKindIndex != KIND_GHOST_INDEX) @@ -1235,21 +1366,37 @@ extern size_t countEntryInCorkQueue (void) return TagFile.corkQueue.count; } +static void makeTagEntriesForSubwords (tagEntryInfo *const subtag) +{ + stringList *list; + + subtag->extensionFields.scopeIndex = CORK_NIL; + markTagExtraBit (subtag, XTAG_SUBWORD); + + list = stringListNewBySplittingWordIntoSubwords(subtag->name); + for (unsigned int i = 0; i < stringListCount(list); i++) + { + vString *subword = stringListItem (list, i); + + subtag->name = vStringValue(subword); + + if (TagFile.cork) + queueTagEntry (subtag); + else + writeTagEntry (subtag, false); + } + stringListDelete (list); +} + extern int makeTagEntry (const tagEntryInfo *const tag) { int r = CORK_NIL; Assert (tag->name != NULL); #ifndef CTAGS_LIB - if (getInputLanguageFileKind() != tag->kind) - { - if (! isInputLanguageKindEnabled (tag->kind->letter) && - (tag->extensionFields.roleIndex == ROLE_INDEX_DEFINITION)) - return CORK_NIL; - if ((tag->extensionFields.roleIndex != ROLE_INDEX_DEFINITION) - && (! tag->kind->roles[tag->extensionFields.roleIndex].enabled)) - return CORK_NIL; - } + if (!TagFile.cork) + if (!isTagWritable (tag)) + goto out; #endif if (tag->name [0] == '\0' && (!tag->placeholder)) @@ -1263,7 +1410,15 @@ extern int makeTagEntry (const tagEntryInfo *const tag) if (TagFile.cork) r = queueTagEntry (tag); else - writeTagEntry (tag); + writeTagEntry (tag, false); + + notifyMakeTagEntry (tag, r); + + if (isXtagEnabled (XTAG_SUBWORD)) + { + tagEntryInfo subtag = *tag; + makeTagEntriesForSubwords (&subtag); + } out: return r; } @@ -1272,7 +1427,7 @@ extern int makeQualifiedTagEntry (const tagEntryInfo *const e) { int r = CORK_NIL; tagEntryInfo x; - char xk; + int xk; const char *sep; static vString *fqn; @@ -1281,7 +1436,7 @@ extern int makeQualifiedTagEntry (const tagEntryInfo *const e) x = *e; markTagExtraBit (&x, XTAG_QUALIFIED_TAGS); - fqn = vStringNewOrClear (fqn); + fqn = vStringNewOrClearWithAutoRelease (fqn); if (e->extensionFields.scopeName) { @@ -1298,7 +1453,7 @@ extern int makeQualifiedTagEntry (const tagEntryInfo *const e) if (sep == NULL) { /* No root separator. The name of the - oritinal tag and that of full qualified tag + optional tag and that of full qualified tag are the same; recording the full qualified tag is meaningless. */ return r; @@ -1310,53 +1465,35 @@ extern int makeQualifiedTagEntry (const tagEntryInfo *const e) x.name = vStringValue (fqn); /* makeExtraTagEntry of c.c doesn't clear scope - releated fields. */ + related fields. */ #if 0 x.extensionFields.scopeKind = NULL; x.extensionFields.scopeName = NULL; + x.extensionFields.scopeIndex = CORK_NIL; #endif + + bool in_subparser + = isTagExtraBitMarked (&x, + XTAG_TAGS_GENERATED_BY_SUBPARSER); + + if (in_subparser) + pushLanguage(x.langType); + r = makeTagEntry (&x); + + if (in_subparser) + popLanguage(); } return r; } -extern void initTagEntry (tagEntryInfo *const e, const char *const name, - int kindIndex) -{ - initTagEntryFull(e, name, - getInputLineNumber (), - getInputLanguage (), - getInputFilePosition (), - getInputFileTagPath (), - kindIndex, - ROLE_INDEX_DEFINITION, - getSourceFileTagPath(), - getSourceLanguage(), - getSourceLineNumber() - getInputLineNumber ()); -} - -extern void initRefTagEntry (tagEntryInfo *const e, const char *const name, - int kindIndex, int roleIndex) -{ - initTagEntryFull(e, name, - getInputLineNumber (), - getInputLanguage (), - getInputFilePosition (), - getInputFileTagPath (), - kindIndex, - roleIndex, - getSourceFileTagPath(), - getSourceLanguage(), - getSourceLineNumber() - getInputLineNumber ()); -} - -extern void initTagEntryFull (tagEntryInfo *const e, const char *const name, +static void initTagEntryFull (tagEntryInfo *const e, const char *const name, unsigned long lineNumber, langType langType_, MIOPos filePosition, const char *inputFileName, int kindIndex, - int roleIndex, + roleBitsType roleBits, const char *sourceFileName, langType sourceLangType, long sourceLineNumberDifference) @@ -1380,12 +1517,17 @@ extern void initTagEntryFull (tagEntryInfo *const e, const char *const name, Assert (kindIndex < 0 || kindIndex < (int)countLanguageKinds(langType_)); e->kindIndex = kindIndex; - Assert (roleIndex >= ROLE_INDEX_DEFINITION); - Assert (kind == NULL || roleIndex < kind->nRoles); - e->extensionFields.roleIndex = roleIndex; - if (roleIndex > ROLE_INDEX_DEFINITION) + Assert (roleBits == 0 + || (roleBits < (makeRoleBit(countLanguageRoles(langType_, kindIndex))))); + e->extensionFields.roleBits = roleBits; + if (roleBits) markTagExtraBit (e, XTAG_REFERENCE_TAGS); + if (doesParserRunAsGuest ()) + markTagExtraBit (e, XTAG_TAGS_GENERATED_BY_GUEST_PARSERS); + if (doesSubparserRun ()) + markTagExtraBit (e, XTAG_TAGS_GENERATED_BY_SUBPARSER); + e->sourceLangType = sourceLangType; e->sourceFileName = sourceFileName; e->sourceLineNumberDifference = sourceLineNumberDifference; @@ -1394,30 +1536,148 @@ extern void initTagEntryFull (tagEntryInfo *const e, const char *const name, for ( i = 0; i < PRE_ALLOCATED_PARSER_FIELDS; i++ ) e->parserFields[i].ftype = FIELD_UNKNOWN; + + if (isParserMarkedNoEmission ()) + e->placeholder = 1; } -extern void markTagExtraBit (tagEntryInfo *const tag, xtagType extra) +extern void initTagEntry (tagEntryInfo *const e, const char *const name, + int kindIndex) +{ + initTagEntryFull(e, name, + getInputLineNumber (), + getInputLanguage (), + getInputFilePosition (), + getInputFileTagPath (), + kindIndex, + 0, + getSourceFileTagPath(), + getSourceLanguage(), + getSourceLineNumber() - getInputLineNumber ()); +} + +extern void initRefTagEntry (tagEntryInfo *const e, const char *const name, + int kindIndex, int roleIndex) +{ + initTagEntryFull(e, name, + getInputLineNumber (), + getInputLanguage (), + getInputFilePosition (), + getInputFileTagPath (), + kindIndex, + makeRoleBit(roleIndex), + getSourceFileTagPath(), + getSourceLanguage(), + getSourceLineNumber() - getInputLineNumber ()); +} + +static void markTagExtraBitFull (tagEntryInfo *const tag, xtagType extra, bool mark) { unsigned int index; unsigned int offset; + uint8_t *slot; - Assert (extra < XTAG_COUNT); Assert (extra != XTAG_UNKNOWN); - index = (extra / 8); - offset = (extra % 8); - tag->extra [ index ] |= (1 << offset); + if (extra < XTAG_COUNT) + { + index = (extra / 8); + offset = (extra % 8); + slot = tag->extra; + } + else if (tag->extraDynamic) + { + Assert (extra < countXtags ()); + + index = ((extra - XTAG_COUNT) / 8); + offset = ((extra - XTAG_COUNT) % 8); + slot = tag->extraDynamic; + } + else + { + Assert (extra < countXtags ()); + + int n = countXtags () - XTAG_COUNT; + tag->extraDynamic = xCalloc ((n / 8) + 1, uint8_t); + PARSER_TRASH_BOX(tag->extraDynamic, eFree); + markTagExtraBit (tag, extra); + return; + } + + if (mark) + slot [ index ] |= (1 << offset); + else + slot [ index ] &= ~(1 << offset); +} + +extern void markTagExtraBit (tagEntryInfo *const tag, xtagType extra) +{ + markTagExtraBitFull (tag, extra, true); } extern bool isTagExtraBitMarked (const tagEntryInfo *const tag, xtagType extra) { - unsigned int index = (extra / 8); - unsigned int offset = (extra % 8); + unsigned int index; + unsigned int offset; + const uint8_t *slot; - Assert (extra < XTAG_COUNT); Assert (extra != XTAG_UNKNOWN); - return !! ((tag->extra [ index ]) & (1 << offset)); + if (extra < XTAG_COUNT) + { + index = (extra / 8); + offset = (extra % 8); + slot = tag->extra; + + } + else if (!tag->extraDynamic) + return false; + else + { + Assert (extra < countXtags ()); + index = ((extra - XTAG_COUNT) / 8); + offset = ((extra - XTAG_COUNT) % 8); + slot = tag->extraDynamic; + + } + return !! ((slot [ index ]) & (1 << offset)); +} + +static void assignRoleFull(tagEntryInfo *const e, int roleIndex, bool assign) +{ + if (roleIndex == ROLE_INDEX_DEFINITION) + { + if (assign) + { + e->extensionFields.roleBits = 0; + markTagExtraBitFull (e, XTAG_REFERENCE_TAGS, false); + } + } + else if (roleIndex > ROLE_INDEX_DEFINITION) + { + Assert (roleIndex < (int)countLanguageRoles(e->langType, e->kindIndex)); + + if (assign) + e->extensionFields.roleBits |= (makeRoleBit(roleIndex)); + else + e->extensionFields.roleBits &= ~(makeRoleBit(roleIndex)); + markTagExtraBitFull (e, XTAG_REFERENCE_TAGS, e->extensionFields.roleBits); + } + else + AssertNotReached(); +} + +extern void assignRole(tagEntryInfo *const e, int roleIndex) +{ + assignRoleFull(e, roleIndex, true); +} + +extern bool isRoleAssigned(const tagEntryInfo *const e, int roleIndex) +{ + if (roleIndex == ROLE_INDEX_DEFINITION) + return (!e->extensionFields.roleBits); + else + return (e->extensionFields.roleBits & makeRoleBit(roleIndex)); } extern unsigned long numTagsAdded(void) diff --git a/ctags/main/entry.h b/ctags/main/entry.h index c938c43f93..5ca2edd7eb 100644 --- a/ctags/main/entry.h +++ b/ctags/main/entry.h @@ -16,21 +16,15 @@ #include "types.h" #include -#include #include "field.h" -#include "kind.h" -#include "vstring.h" #include "xtag.h" #include "mio.h" -#include "nestlevel.h" #include "ctags-api.h" /* * MACROS */ -#define WHOLE_FILE -1L -#define includeExtensionFlags() (Option.tagFileFormat > 1) /* * DATA DECLARATIONS @@ -38,8 +32,11 @@ typedef struct sTagField { fieldType ftype; const char* value; + bool valueOwner; /* used only in parserFieldsDynamic */ } tagField; +typedef uint64_t roleBitsType; + /* Information about the current tag candidate. */ struct sTagEntryInfo { @@ -60,7 +57,8 @@ struct sTagEntryInfo { const char *inputFileName; /* name of input file */ const char *name; /* name of the tag */ int kindIndex; /* kind descriptor */ - unsigned char extra[ ((XTAG_COUNT) / 8) + 1 ]; + uint8_t extra[ ((XTAG_COUNT) / 8) + 1 ]; + uint8_t *extraDynamic; /* Dynamically allocated but freed by per parser TrashBox */ struct { const char* access; @@ -84,23 +82,31 @@ struct sTagEntryInfo { /* type (union/struct/etc.) and name for a variable or typedef. */ const char* typeRef [2]; /* e.g., "struct" and struct name */ +#define ROLE_INDEX_DEFINITION -1 +#define ROLE_NAME_DEFINITION "def" +#define ROLE_MAX_COUNT (sizeof(roleBitsType) * 8) + roleBitsType roleBits; /* for role of reference tag */ + /* GEANY DIFF */ const char *varType; /* GEANY DIFF END */ -#define ROLE_INDEX_DEFINITION -1 - int roleIndex; /* for role of reference tag */ - #ifdef HAVE_LIBXML const char* xpath; #endif unsigned long endLine; } extensionFields; /* list of extension fields*/ + /* `usedParserFields' tracks how many parser own fields are + used. If it is a few (less than PRE_ALLOCATED_PARSER_FIELDS), + statically allocated parserFields is used. If more fields than + PRE_ALLOCATED_PARSER_FIELDS is defined and attached, parserFieldsDynamic + is used. */ + unsigned int usedParserFields; #define PRE_ALLOCATED_PARSER_FIELDS 5 #define NO_PARSER_FIELD -1 - unsigned int usedParserFields; tagField parserFields [PRE_ALLOCATED_PARSER_FIELDS]; + ptrArray * parserFieldsDynamic; /* Following source* fields are used only when #line is found in input and --line-directive is given in ctags command line. */ @@ -118,69 +124,37 @@ struct sTagEntryInfo { /* * FUNCTION PROTOTYPES */ -extern void freeTagFileResources (void); -extern const char *tagFileName (void); -extern void openTagFile (void); -extern void closeTagFile (const bool resize); -extern void setupWriter (void); -extern void teardownWriter (const char *inputFilename); extern int makeTagEntry (const tagEntryInfo *const tag); extern void initTagEntry (tagEntryInfo *const e, const char *const name, int kindIndex); extern void initRefTagEntry (tagEntryInfo *const e, const char *const name, int kindIndex, int roleIndex); -extern void initTagEntryFull (tagEntryInfo *const e, const char *const name, - unsigned long lineNumber, - langType langType_, - MIOPos filePosition, - const char *inputFileName, - int kindIndex, - int roleIndex, - const char *sourceFileName, - langType sourceLangType, - long sourceLineNumberDifference); -extern int makeQualifiedTagEntry (const tagEntryInfo *const e); - -extern unsigned long numTagsAdded(void); -extern void setNumTagsAdded (unsigned long nadded); -extern unsigned long numTagsTotal(void); -extern unsigned long maxTagsLine(void); -extern void invalidatePatternCache(void); -extern void tagFilePosition (MIOPos *p); -extern void setTagFilePosition (MIOPos *p); -extern const char* getTagFileDirectory (void); -extern void getTagScopeInformation (tagEntryInfo *const tag, - const char **kind, const char **name); - -/* Getting line associated with tag */ -extern char *readLineFromBypassAnyway (vString *const vLine, const tagEntryInfo *const tag, - long *const pSeekValue); -/* Generating pattern associated tag, caller must do eFree for the returned value. */ -extern char* makePatternString (const tagEntryInfo *const tag); +extern void assignRole(tagEntryInfo *const e, int roleIndex); +extern bool isRoleAssigned(const tagEntryInfo *const e, int roleIndex); +extern int makeQualifiedTagEntry (const tagEntryInfo *const e); -/* language is optional: can be NULL. */ -extern bool writePseudoTag (const ptagDesc *pdesc, - const char *const fileName, - const char *const pattern, - const char *const parserName); #define CORK_NIL 0 -void corkTagFile(void); -void uncorkTagFile(void); tagEntryInfo *getEntryInCorkQueue (unsigned int n); tagEntryInfo *getEntryOfNestingLevel (const NestingLevel *nl); size_t countEntryInCorkQueue (void); -extern void makeFileTag (const char *const fileName); - extern void markTagExtraBit (tagEntryInfo *const tag, xtagType extra); extern bool isTagExtraBitMarked (const tagEntryInfo *const tag, xtagType extra); extern void attachParserField (tagEntryInfo *const tag, fieldType ftype, const char* value); extern void attachParserFieldToCorkEntry (int index, fieldType ftype, const char* value); +CTAGS_INLINE roleBitsType makeRoleBit(int roleIndex) +{ + if (roleIndex == ROLE_INDEX_DEFINITION) + return 0; + else + return ((roleBitsType)1) << roleIndex; +} + #ifdef CTAGS_LIB extern void setTagEntryFunction(tagEntryFunction entry_function, void *user_data); #endif diff --git a/ctags/main/entry_p.h b/ctags/main/entry_p.h new file mode 100644 index 0000000000..533ea7631a --- /dev/null +++ b/ctags/main/entry_p.h @@ -0,0 +1,68 @@ +/* +* Copyright (c) 2017, Red Hat, INc. +* Copyright (c) 2017, Masatake YAMATO +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* main part private interface to entry.c +*/ +#ifndef CTAGS_PRIVATE_ENTRY_H +#define CTAGS_PRIVATE_ENTRY_H + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ +#include "entry.h" +#include "types.h" + +/* +* FUNCTION PROTOTYPES +*/ +extern const kindDefinition* getTagKind(const tagEntryInfo *const tag); +extern char getTagKindLetter(const tagEntryInfo *const tag); +extern const char* getTagKindName(const tagEntryInfo *const tag); + +extern const roleDefinition* getTagRole(const tagEntryInfo *const tag, int roleIndex); + +extern void freeTagFileResources (void); +extern const char *tagFileName (void); +extern void openTagFile (void); +extern void closeTagFile (const bool resize); +extern void setupWriter (void); +extern bool teardownWriter (const char *inputFilename); + +extern unsigned long numTagsAdded(void); +extern void setNumTagsAdded (unsigned long nadded); +extern unsigned long numTagsTotal(void); +extern unsigned long maxTagsLine(void); +extern void invalidatePatternCache(void); +extern void tagFilePosition (MIOPos *p); +extern void setTagFilePosition (MIOPos *p); +extern const char* getTagFileDirectory (void); +extern void getTagScopeInformation (tagEntryInfo *const tag, + const char **kind, const char **name); + +/* Getting line associated with tag */ +extern char *readLineFromBypassForTag (vString *const vLine, const tagEntryInfo *const tag, + long *const pSeekValue); + +/* Generating pattern associated tag, caller must do eFree for the returned value. */ +extern char* makePatternString (const tagEntryInfo *const tag); + + +/* language is optional: can be NULL. */ +extern bool writePseudoTag (const ptagDesc *pdesc, + const char *const fileName, + const char *const pattern, + const char *const parserName); + +void corkTagFile(void); +void uncorkTagFile(void); + +extern void makeFileTag (const char *const fileName); + +extern const tagField* getParserField (const tagEntryInfo * tag, int index); + +#endif /* CTAGS_PRIVATE_ENTRY_H */ diff --git a/ctags/main/entry_private.c b/ctags/main/entry_private.c new file mode 100644 index 0000000000..52c7ee6715 --- /dev/null +++ b/ctags/main/entry_private.c @@ -0,0 +1,37 @@ +/* +* Copyright (c) 2017, Red Hat, INc. +* Copyright (c) 2017, Masatake YAMATO +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* main part private interface to entry.c +*/ + +#include "entry_p.h" +#include "parse_p.h" + +extern const kindDefinition* getTagKind(const tagEntryInfo *const tag) +{ + return getLanguageKind(tag->langType, tag->kindIndex); +} + +extern char getTagKindLetter(const tagEntryInfo *const tag) +{ + kindDefinition *kdef = getLanguageKind(tag->langType, tag->kindIndex); + return kdef->letter; +} + +extern const char* getTagKindName(const tagEntryInfo *const tag) +{ + kindDefinition *kdef = getLanguageKind(tag->langType, tag->kindIndex); + return kdef->name; +} + +extern const roleDefinition* getTagRole(const tagEntryInfo *const tag, + int roleIndex) +{ + if (roleIndex == ROLE_INDEX_DEFINITION) + return NULL; + return getLanguageRole(tag->langType, tag->kindIndex, roleIndex); +} diff --git a/ctags/main/error.c b/ctags/main/error.c index d08bd12efe..2eb9d8613f 100644 --- a/ctags/main/error.c +++ b/ctags/main/error.c @@ -12,7 +12,12 @@ #include #include "error.h" -#include "options.h" +#include "options_p.h" +#include "routines_p.h" + +#ifdef HAVE_JANSSON +#include +#endif #define selected(var,feature) (((int)(var) & (int)(feature)) == (int)feature) @@ -33,11 +38,13 @@ extern bool stderrDefaultErrorPrinter (const errorSelection selection, selected (selection, WARNING) ? "Warning: " : ""); vfprintf (stderr, format, ap); if (selected (selection, PERROR)) + { #ifdef HAVE_STRERROR fprintf (stderr, " : %s", strerror (errno)); #else - perror (" "); + perror (" "); #endif + } fputs ("\n", stderr); return (selected (selection, FATAL) || Option.fatalWarnings)? true: false; @@ -57,3 +64,33 @@ extern void error (const errorSelection selection, exit (1); } +#ifdef HAVE_JANSSON +bool jsonErrorPrinter (const errorSelection selection, const char *const format, va_list ap, + void *data CTAGS_ATTR_UNUSED) +{ +#define ERR_BUFFER_SIZE 4096 + static char reason[ERR_BUFFER_SIZE]; + + vsnprintf (reason, ERR_BUFFER_SIZE, format, ap); + reason [ERR_BUFFER_SIZE - 1] = '\0'; /* Do we need this? */ + + json_t *response = json_object (); + json_object_set_new (response, "_type", json_string ("error")); + json_object_set_new (response, "message", json_string (reason)); + if (selected (selection, WARNING)) + json_object_set_new (response, "warning", json_true ()); + if (selected (selection, FATAL)) + json_object_set_new (response, "fatal", json_true ()); + if (selected (selection, PERROR)) + { + json_object_set_new (response, "errno", json_integer (errno)); + json_object_set_new (response, "perror", json_string (strerror (errno))); + } + json_dumpf (response, stdout, JSON_PRESERVE_ORDER); + fprintf (stdout, "\n"); + + json_decref (response); + + return false; +} +#endif diff --git a/ctags/main/field.c b/ctags/main/field.c index bb26ad65d5..e5db9fe000 100644 --- a/ctags/main/field.c +++ b/ctags/main/field.c @@ -19,43 +19,53 @@ #include "ctags.h" #include "debug.h" #include "entry.h" +#include "entry_p.h" #include "field.h" #include "kind.h" -#include "options.h" +#include "options_p.h" +#include "parse_p.h" #include "read.h" #include "routines.h" +#include "trashbox.h" -struct sFieldDesc { - fieldDefinition *spec; +typedef struct sFieldObject { + fieldDefinition *def; unsigned int fixed: 1; /* fields which cannot be disabled. */ vString *buffer; const char* nameWithPrefix; langType language; fieldType sibling; -}; - -static const char *renderFieldName (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldInput (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldCompactInputLine (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldSignature (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldScope (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldTyperef (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldInherits (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldKindName (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldLineNumber (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldLanguage (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldAccess (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldKindLetter (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldImplementation (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldFile (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldPattern (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldRole (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldRefMarker (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldExtra (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldXpath (const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldScopeKindName(const tagEntryInfo *const tag, const char *value, vString* b); -static const char *renderFieldEnd (const tagEntryInfo *const tag, const char *value, vString* b); +} fieldObject; + +static const char *renderFieldName (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldNameNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, + bool *rejected); +static const char *renderFieldInput (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldInputNoEscape (const tagEntryInfo *const tag, const char *value, vString* b, + bool *rejected); +static const char *renderFieldCompactInputLine (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldSignature (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldScope (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldScopeNoEscape (const tagEntryInfo *const tag, const char *value, vString* b, + bool *rejected); +static const char *renderFieldTyperef (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldInherits (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldKindName (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldLineNumber (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldLanguage (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldAccess (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldKindLetter (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldImplementation (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldFile (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldPatternCommon (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldPatternCtags (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldRoles (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldRefMarker (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldExtras (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldXpath (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldScopeKindName(const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldEnd (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); static bool isLanguageFieldAvailable (const tagEntryInfo *const tag); static bool isTyperefFieldAvailable (const tagEntryInfo *const tag); @@ -64,195 +74,243 @@ static bool isInheritsFieldAvailable (const tagEntryInfo *const tag); static bool isAccessFieldAvailable (const tagEntryInfo *const tag); static bool isImplementationFieldAvailable (const tagEntryInfo *const tag); static bool isSignatureFieldAvailable (const tagEntryInfo *const tag); -static bool isRoleFieldAvailable (const tagEntryInfo *const tag); -static bool isExtraFieldAvailable (const tagEntryInfo *const tag); +static bool isExtrasFieldAvailable (const tagEntryInfo *const tag); static bool isXpathFieldAvailable (const tagEntryInfo *const tag); static bool isEndFieldAvailable (const tagEntryInfo *const tag); -#define DEFINE_FIELD_SPEC(L, N, V, H, F) \ - DEFINE_FIELD_SPEC_FULL (L, N, V, H, F, NULL) -#define DEFINE_FIELD_SPEC_FULL(L, N, V, H, F, A)\ +#define DEFINE_FIELD(L, N, V, H, DT, ...) \ + DEFINE_FIELD_FULL (L, N, V, H, NULL, DT, __VA_ARGS__) +#define DEFINE_FIELD_FULL(L, N, V, H, A, DT, ...) \ { \ .letter = L, \ .name = N, \ .description = H, \ .enabled = V, \ - .renderEscaped = F, \ + .renderEscaped = { __VA_ARGS__ }, \ .isValueAvailable = A, \ + .dataType = DT, \ } #define WITH_DEFUALT_VALUE(str) ((str)?(str):"-") static fieldDefinition fieldDefinitionsFixed [] = { /* FIXED FIELDS */ - DEFINE_FIELD_SPEC ('N', "name", true, - "tag name (fixed field)", - renderFieldName), - DEFINE_FIELD_SPEC ('F', "input", true, - "input file (fixed field)", - renderFieldInput), - DEFINE_FIELD_SPEC ('P', "pattern", true, - "pattern (fixed field)", - renderFieldPattern), + DEFINE_FIELD ('N', "name", true, + "tag name", + FIELDTYPE_STRING, + [WRITER_U_CTAGS] = renderFieldName, + [WRITER_E_CTAGS] = renderFieldNameNoEscape, + [WRITER_JSON] = renderFieldNameNoEscape, + ), + DEFINE_FIELD ('F', "input", true, + "input file", + FIELDTYPE_STRING, + [WRITER_U_CTAGS] = renderFieldInput, + [WRITER_E_CTAGS] = renderFieldInputNoEscape, + [WRITER_JSON] = renderFieldInputNoEscape, + ), + DEFINE_FIELD ('P', "pattern", true, + "pattern", + FIELDTYPE_STRING|FIELDTYPE_BOOL, + [WRITER_U_CTAGS] = renderFieldPatternCtags, + [WRITER_XREF] = renderFieldPatternCommon, + [WRITER_JSON] = renderFieldPatternCommon, + ), }; static fieldDefinition fieldDefinitionsExuberant [] = { - DEFINE_FIELD_SPEC ('C', "compact", false, - "compact input line (fixed field, only used in -x option)", - renderFieldCompactInputLine), + DEFINE_FIELD ('C', "compact", false, + "compact input line (used only in xref output)", + FIELDTYPE_STRING, + [WRITER_U_CTAGS] = renderFieldCompactInputLine), /* EXTENSION FIELDS */ - DEFINE_FIELD_SPEC_FULL ('a', "access", false, + DEFINE_FIELD_FULL ('a', "access", false, "Access (or export) of class members", - renderFieldAccess, isAccessFieldAvailable), - DEFINE_FIELD_SPEC_FULL ('f', "file", true, + isAccessFieldAvailable, + FIELDTYPE_STRING, + [WRITER_U_CTAGS] = renderFieldAccess), + DEFINE_FIELD_FULL ('f', "file", true, "File-restricted scoping", - renderFieldFile, isFileFieldAvailable), - DEFINE_FIELD_SPEC_FULL ('i', "inherits", false, + isFileFieldAvailable, + FIELDTYPE_BOOL, + [WRITER_U_CTAGS] = renderFieldFile), + DEFINE_FIELD_FULL ('i', "inherits", false, "Inheritance information", - renderFieldInherits, isInheritsFieldAvailable), - DEFINE_FIELD_SPEC ('K', NULL, false, + isInheritsFieldAvailable, + FIELDTYPE_STRING|FIELDTYPE_BOOL, + [WRITER_U_CTAGS] = renderFieldInherits), + DEFINE_FIELD ('K', NULL, false, "Kind of tag as full name", - renderFieldKindName), - DEFINE_FIELD_SPEC ('k', NULL, true, + FIELDTYPE_STRING, + [WRITER_U_CTAGS] = renderFieldKindName), + DEFINE_FIELD ('k', NULL, true, "Kind of tag as a single letter", - renderFieldKindLetter), - DEFINE_FIELD_SPEC_FULL ('l', "language", false, + FIELDTYPE_STRING, + [WRITER_U_CTAGS] = renderFieldKindLetter), + DEFINE_FIELD_FULL ('l', "language", false, "Language of input file containing tag", - renderFieldLanguage, isLanguageFieldAvailable), - DEFINE_FIELD_SPEC_FULL ('m', "implementation", false, + isLanguageFieldAvailable, + FIELDTYPE_STRING, + [WRITER_U_CTAGS] = renderFieldLanguage), + DEFINE_FIELD_FULL ('m', "implementation", false, "Implementation information", - renderFieldImplementation, isImplementationFieldAvailable), - DEFINE_FIELD_SPEC ('n', "line", false, + isImplementationFieldAvailable, + FIELDTYPE_STRING, + [WRITER_U_CTAGS] = renderFieldImplementation), + DEFINE_FIELD ('n', "line", false, "Line number of tag definition", - renderFieldLineNumber), - DEFINE_FIELD_SPEC_FULL ('S', "signature", false, + FIELDTYPE_INTEGER, + [WRITER_U_CTAGS] = renderFieldLineNumber), + DEFINE_FIELD_FULL ('S', "signature", false, "Signature of routine (e.g. prototype or parameter list)", - renderFieldSignature, isSignatureFieldAvailable), - DEFINE_FIELD_SPEC ('s', NULL, true, + isSignatureFieldAvailable, + FIELDTYPE_STRING, + [WRITER_U_CTAGS] = renderFieldSignature), + DEFINE_FIELD ('s', NULL, true, "Scope of tag definition (`p' can be used for printing its kind)", - renderFieldScope), - DEFINE_FIELD_SPEC_FULL ('t', "typeref", true, + FIELDTYPE_STRING, + [WRITER_U_CTAGS] = renderFieldScope, + [WRITER_E_CTAGS] = renderFieldScopeNoEscape, + [WRITER_JSON] = renderFieldScopeNoEscape), + DEFINE_FIELD_FULL ('t', "typeref", true, "Type and name of a variable or typedef", - renderFieldTyperef, isTyperefFieldAvailable), - DEFINE_FIELD_SPEC ('z', "kind", false, + isTyperefFieldAvailable, + FIELDTYPE_STRING, + [WRITER_U_CTAGS] = renderFieldTyperef), + DEFINE_FIELD ('z', "kind", false, "Include the \"kind:\" key in kind field (use k or K) in tags output, kind full name in xref output", + FIELDTYPE_STRING, /* Following renderer is for handling --_xformat=%{kind}; and is not for tags output. */ - renderFieldKindName), + [WRITER_U_CTAGS] = renderFieldKindName), }; static fieldDefinition fieldDefinitionsUniversal [] = { - DEFINE_FIELD_SPEC_FULL ('r', "role", false, - "Role", - renderFieldRole, isRoleFieldAvailable), - DEFINE_FIELD_SPEC ('R', NULL, false, + DEFINE_FIELD ('r', "roles", false, + "Roles", + FIELDTYPE_STRING, + [WRITER_U_CTAGS] = renderFieldRoles), + DEFINE_FIELD ('R', NULL, false, "Marker (R or D) representing whether tag is definition or reference", - renderFieldRefMarker), - DEFINE_FIELD_SPEC ('Z', "scope", false, + FIELDTYPE_STRING, + [WRITER_U_CTAGS] = renderFieldRefMarker), + DEFINE_FIELD ('Z', "scope", false, "Include the \"scope:\" key in scope field (use s) in tags output, scope name in xref output", + FIELDTYPE_STRING, /* Following renderer is for handling --_xformat=%{scope}; and is not for tags output. */ - renderFieldScope), - DEFINE_FIELD_SPEC_FULL ('E', "extra", false, + [WRITER_U_CTAGS] = renderFieldScope, + [WRITER_E_CTAGS] = renderFieldScopeNoEscape, + [WRITER_JSON] = renderFieldScopeNoEscape), + DEFINE_FIELD_FULL ('E', "extras", false, "Extra tag type information", - renderFieldExtra, isExtraFieldAvailable), - DEFINE_FIELD_SPEC_FULL ('x', "xpath", false, + isExtrasFieldAvailable, + FIELDTYPE_STRING, + [WRITER_U_CTAGS] = renderFieldExtras), + DEFINE_FIELD_FULL ('x', "xpath", false, "xpath for the tag", - renderFieldXpath, isXpathFieldAvailable), - DEFINE_FIELD_SPEC ('p', "scopeKind", false, + isXpathFieldAvailable, + FIELDTYPE_STRING, + [WRITER_U_CTAGS] = renderFieldXpath), + DEFINE_FIELD ('p', "scopeKind", false, "Kind of scope as full name", - renderFieldScopeKindName), - DEFINE_FIELD_SPEC_FULL ('e', "end", false, + FIELDTYPE_STRING, + [WRITER_U_CTAGS] = renderFieldScopeKindName), + DEFINE_FIELD_FULL ('e', "end", false, "end lines of various items", - renderFieldEnd, isEndFieldAvailable), + isEndFieldAvailable, + FIELDTYPE_INTEGER, + [WRITER_U_CTAGS] = renderFieldEnd), }; -static unsigned int fieldDescUsed = 0; -static unsigned int fieldDescAllocated = 0; -static fieldDesc* fieldDescs = NULL; +static unsigned int fieldObjectUsed = 0; +static unsigned int fieldObjectAllocated = 0; +static fieldObject* fieldObjects = NULL; -extern void initFieldDescs (void) +extern void initFieldObjects (void) { - int i; - fieldDesc *fdesc; + unsigned int i; + fieldObject *fobj; - Assert (fieldDescs == NULL); + Assert (fieldObjects == NULL); - fieldDescAllocated + fieldObjectAllocated = ARRAY_SIZE (fieldDefinitionsFixed) + ARRAY_SIZE (fieldDefinitionsExuberant) + ARRAY_SIZE (fieldDefinitionsUniversal); - fieldDescs = xMalloc (fieldDescAllocated, fieldDesc); + fieldObjects = xMalloc (fieldObjectAllocated, fieldObject); + DEFAULT_TRASH_BOX(&fieldObjects, eFreeIndirect); - fieldDescUsed = 0; + fieldObjectUsed = 0; for (i = 0; i < ARRAY_SIZE (fieldDefinitionsFixed); i++) { - fdesc = fieldDescs + i + fieldDescUsed; - fdesc->spec = fieldDefinitionsFixed + i; - fdesc->fixed = 1; - fdesc->buffer = NULL; - fdesc->nameWithPrefix = fdesc->spec->name; - fdesc->language = LANG_IGNORE; - fdesc->sibling = FIELD_UNKNOWN; + fobj = fieldObjects + i + fieldObjectUsed; + fobj->def = fieldDefinitionsFixed + i; + fobj->fixed = 1; + fobj->buffer = NULL; + fobj->nameWithPrefix = fobj->def->name; + fobj->language = LANG_IGNORE; + fobj->sibling = FIELD_UNKNOWN; } - fieldDescUsed += ARRAY_SIZE (fieldDefinitionsFixed); + fieldObjectUsed += ARRAY_SIZE (fieldDefinitionsFixed); for (i = 0; i < ARRAY_SIZE (fieldDefinitionsExuberant); i++) { - fdesc = fieldDescs + i + fieldDescUsed; - fdesc->spec = fieldDefinitionsExuberant +i; - fdesc->fixed = 0; - fdesc->buffer = NULL; - fdesc->nameWithPrefix = fdesc->spec->name; - fdesc->language = LANG_IGNORE; - fdesc->sibling = FIELD_UNKNOWN; + fobj = fieldObjects + i + fieldObjectUsed; + fobj->def = fieldDefinitionsExuberant +i; + fobj->fixed = 0; + fobj->buffer = NULL; + fobj->nameWithPrefix = fobj->def->name; + fobj->language = LANG_IGNORE; + fobj->sibling = FIELD_UNKNOWN; } - fieldDescUsed += ARRAY_SIZE (fieldDefinitionsExuberant); + fieldObjectUsed += ARRAY_SIZE (fieldDefinitionsExuberant); for (i = 0; i < ARRAY_SIZE (fieldDefinitionsUniversal); i++) { char *nameWithPrefix; - fdesc = fieldDescs + i + fieldDescUsed; - fdesc->spec = fieldDefinitionsUniversal + i; - fdesc->fixed = 0; - fdesc->buffer = NULL; + fobj = fieldObjects + i + fieldObjectUsed; + fobj->def = fieldDefinitionsUniversal + i; + fobj->fixed = 0; + fobj->buffer = NULL; - if (fdesc->spec->name) + if (fobj->def->name) { - nameWithPrefix = eMalloc (sizeof CTAGS_FIELD_PREFIX + strlen (fdesc->spec->name) + 1); + nameWithPrefix = eMalloc (sizeof CTAGS_FIELD_PREFIX + strlen (fobj->def->name) + 1); nameWithPrefix [0] = '\0'; strcat (nameWithPrefix, CTAGS_FIELD_PREFIX); - strcat (nameWithPrefix, fdesc->spec->name); - fdesc->nameWithPrefix = nameWithPrefix; + strcat (nameWithPrefix, fobj->def->name); + fobj->nameWithPrefix = nameWithPrefix; + DEFAULT_TRASH_BOX(nameWithPrefix, eFree); } else - fdesc->nameWithPrefix = NULL; - fdesc->language = LANG_IGNORE; - fdesc->sibling = FIELD_UNKNOWN; + fobj->nameWithPrefix = NULL; + fobj->language = LANG_IGNORE; + fobj->sibling = FIELD_UNKNOWN; } - fieldDescUsed += ARRAY_SIZE (fieldDefinitionsUniversal); + fieldObjectUsed += ARRAY_SIZE (fieldDefinitionsUniversal); - Assert ( fieldDescAllocated == fieldDescUsed ); + Assert ( fieldObjectAllocated == fieldObjectUsed ); } -static fieldDesc* getFieldDesc(fieldType type) +static fieldObject* getFieldObject(fieldType type) { - Assert ((0 <= type) && (type < fieldDescUsed)); - return fieldDescs + type; + Assert ((0 <= type) && ((unsigned int)type < fieldObjectUsed)); + return fieldObjects + type; } extern fieldType getFieldTypeForOption (char letter) { unsigned int i; - for (i = 0; i < fieldDescUsed; i++) + for (i = 0; i < fieldObjectUsed; i++) { - if (fieldDescs [i].spec->letter == letter) + if (fieldObjects [i].def->letter == letter) return i; } return FIELD_UNKNOWN; @@ -279,12 +337,12 @@ extern fieldType getFieldTypeForNameAndLanguage (const char *fieldName, langType else if (language != LANG_IGNORE && (initialized == false)) initializeParser (language); - for (i = 0; i < fieldDescUsed; i++) + for (i = 0; i < fieldObjectUsed; i++) { - if (fieldDescs [i].spec->name - && strcmp (fieldDescs [i].spec->name, fieldName) == 0 + if (fieldObjects [i].def->name + && strcmp (fieldObjects [i].def->name, fieldName) == 0 && ((language == LANG_AUTO) - || (fieldDescs [i].language == language))) + || (fieldObjects [i].language == language))) return i; } @@ -293,90 +351,23 @@ extern fieldType getFieldTypeForNameAndLanguage (const char *fieldName, langType extern const char* getFieldName(fieldType type) { - fieldDesc* fdesc; + fieldObject* fobj; - fdesc = getFieldDesc (type); + fobj = getFieldObject (type); if (Option.putFieldPrefix) - return fdesc->nameWithPrefix; + return fobj->nameWithPrefix; else - return fdesc->spec->name; + return fobj->def->name; } extern bool doesFieldHaveValue (fieldType type, const tagEntryInfo *tag) { - if (getFieldDesc(type)->spec->isValueAvailable) - return getFieldDesc(type)->spec->isValueAvailable(tag); + if (getFieldObject(type)->def->isValueAvailable) + return getFieldObject(type)->def->isValueAvailable(tag); else return true; } -#define PR_FIELD_WIDTH_LETTER 7 -#define PR_FIELD_WIDTH_NAME 15 -#define PR_FIELD_WIDTH_LANGUAGE 16 -#define PR_FIELD_WIDTH_DESC 30 -#define PR_FIELD_WIDTH_XFMT 6 -#define PR_FIELD_WIDTH_ENABLED 7 - -#define PR_FIELD_STR(X) PR_FIELD_WIDTH_##X -#define PR_FIELD_FMT(X,T) "%-" STRINGIFY(PR_FIELD_STR(X)) STRINGIFY(T) - -#define MAKE_FIELD_FMT(LETTER_SPEC) \ - PR_FIELD_FMT (LETTER,LETTER_SPEC) \ - " " \ - PR_FIELD_FMT (NAME,s) \ - " " \ - PR_FIELD_FMT (ENABLED,s) \ - " " \ - PR_FIELD_FMT (LANGUAGE,s) \ - " " \ - PR_FIELD_FMT (XFMT,s) \ - " " \ - PR_FIELD_FMT (DESC,s) \ - "\n" - -static void printField (fieldType i) -{ - unsigned char letter = fieldDescs[i].spec->letter; - const char *name; - const char *language; - - if (letter == FIELD_LETTER_NO_USE) - letter = '-'; - - if (! fieldDescs[i].spec->name) - name = "NONE"; - else - name = getFieldName (i); - - if (fieldDescs[i].language == LANG_IGNORE) - language = "NONE"; - else - language = getLanguageName (fieldDescs[i].language); - - printf((Option.machinable? "%c\t%s\t%s\t%s\t%s\t%s\n": MAKE_FIELD_FMT(c)), - letter, - name, - isFieldEnabled (i)? "on": "off", - language, - getFieldDesc (i)->spec->renderEscaped? "TRUE": "FALSE", - fieldDescs[i].spec->description? fieldDescs[i].spec->description: "NONE"); -} - -extern void printFields (int language) -{ - unsigned int i; - - if (Option.withListHeader) - printf ((Option.machinable? "%s\t%s\t%s\t%s\t%s\t%s\n": MAKE_FIELD_FMT(s)), - "#LETTER", "NAME", "ENABLED", "LANGUAGE", "XFMT", "DESCRIPTION"); - - for (i = 0; i < fieldDescUsed; i++) - { - if (language == LANG_AUTO || getFieldOwner (i) == language) - printField (i); - } -} - static const char *renderAsIs (vString* b CTAGS_ATTR_UNUSED, const char *s) { return s; @@ -390,44 +381,67 @@ static const char *renderEscapedString (const char *s, return vStringValue (b); } -static const char *renderEscapedName (const char* s, +static const char *renderEscapedName (const bool isTagName, + const char* s, const tagEntryInfo *const tag, vString* b) { - const char* base = s; + int unexpected_byte = 0; - for (; *s; s++) + if (isTagName && (*s == ' ' || *s == '!')) { - int c = *s; - if ((c > 0x00 && c <= 0x1F) || c == 0x7F) + /* Don't allow a leading space or exclamation mark as it conflicts with + * pseudo-tags when sorting. Anything with a lower byte value is + * escaped by renderEscapedString() already. */ + unexpected_byte = *s; + switch (*s) { - char letter = getLanguageKind(tag->langType, tag->kindIndex)->letter; - verbose ("Unexpected character (0 < *c && *c < 0x20) included in a tagEntryInfo: %s\n", base); - verbose ("File: %s, Line: %lu, Lang: %s, Kind: %c\n", - tag->inputFileName, tag->lineNumber, getLanguageName(tag->langType), letter); - verbose ("Escape the character\n"); - break; + case ' ': vStringCatS (b, "\\x20"); s++; break; + case '!': vStringCatS (b, "\\x21"); s++; break; + default: AssertNotReached(); } - else if (c == '\\') - break; - else - continue; } + else + { + /* Find the first byte needing escaping for the warning message */ + const char *p = s; - if (*s == '\0') - return base; + while (*p > 0x1F && *p != 0x7F) + p++; + unexpected_byte = *p; + } - vStringNCatS (b, base, s - base); + if (unexpected_byte) + { + const kindDefinition *kdef = getTagKind (tag); + verbose ("Unexpected character %#04x included in a tagEntryInfo: %s\n", unexpected_byte, s); + verbose ("File: %s, Line: %lu, Lang: %s, Kind: %c\n", + tag->inputFileName, tag->lineNumber, getLanguageName(tag->langType), kdef->letter); + verbose ("Escape the character\n"); + } return renderEscapedString (s, tag, b); } -static const char *renderFieldName (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) +static const char *renderFieldName (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { - return renderEscapedName (tag->name, tag, b); + return renderEscapedName (true, tag->name, tag, b); } -static const char *renderFieldInput (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) +static const char *renderFieldNameNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, + bool *rejected) +{ + if (strpbrk (tag->name, " \t")) + { + *rejected = true; + return NULL; + } + return renderAsIs (b, tag->name); +} + +static const char *renderFieldInput (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { const char *f = tag->inputFileName; @@ -436,53 +450,108 @@ static const char *renderFieldInput (const tagEntryInfo *const tag, const char * return renderEscapedString (f, tag, b); } -static const char *renderFieldSignature (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) +static const char *renderFieldInputNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, + bool *rejected) +{ + const char *f = tag->inputFileName; + + if (Option.lineDirectives && tag->sourceFileName) + f = tag->sourceFileName; + + if (strpbrk (f, " \t")) + { + *rejected = true; + return NULL; + } + + return renderAsIs (b, f); +} + +static const char *renderFieldSignature (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { return renderEscapedString (WITH_DEFUALT_VALUE (tag->extensionFields.signature), tag, b); } -static const char *renderFieldScope (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) +static const char *renderFieldScope (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, + bool *rejected CTAGS_ATTR_UNUSED) +{ + const char* scope; + + getTagScopeInformation ((tagEntryInfo *const)tag, NULL, &scope); + return scope? renderEscapedName (false, scope, tag, b): NULL; +} + +static const char *renderFieldScopeNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, + bool *rejected) { const char* scope; getTagScopeInformation ((tagEntryInfo *const)tag, NULL, &scope); - return scope? renderEscapedName (scope, tag, b): NULL; + if (scope && strpbrk (scope, " \t")) + { + *rejected = true; + return NULL; + } + + return scope? renderAsIs (b, scope): NULL; } -static const char *renderFieldInherits (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) +static const char *renderFieldInherits (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { return renderEscapedString (WITH_DEFUALT_VALUE (tag->extensionFields.inheritance), tag, b); } -static const char *renderFieldTyperef (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) +static const char *renderFieldTyperef (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { - return renderEscapedName (WITH_DEFUALT_VALUE (tag->extensionFields.typeRef [1]), tag, b); + /* Return "-" instead of "-:-". */ + if (tag->extensionFields.typeRef [0] == NULL + && tag->extensionFields.typeRef [1] == NULL) + return renderAsIs (b, "-"); + + vStringCatS (b, WITH_DEFUALT_VALUE (tag->extensionFields.typeRef [0])); + vStringPut (b, ':'); + return renderEscapedName (false, WITH_DEFUALT_VALUE (tag->extensionFields.typeRef [1]), tag, b); } -extern const char* renderFieldEscaped (fieldType type, +extern const char* renderFieldEscaped (writerType writer, + fieldType type, const tagEntryInfo *tag, - int index) + int index, + bool *rejected) { - fieldDesc *fdesc = fieldDescs + type; + fieldObject *fobj = fieldObjects + type; const char *value; + renderEscaped rfn; + bool stub; Assert (tag); - Assert (fdesc->spec->renderEscaped); + Assert (fobj->def->renderEscaped); + Assert (index < 0 || ((unsigned int)index) < tag->usedParserFields); - fdesc->buffer = vStringNewOrClear (fdesc->buffer); + fobj->buffer = vStringNewOrClearWithAutoRelease (fobj->buffer); if (index >= 0) { - Assert ( tag->usedParserFields > index ); - value = tag->parserFields[ index ].value; + const tagField *f = getParserField (tag, index); + + value = f->value; } else value = NULL; - return fdesc->spec->renderEscaped (tag, value, fdesc->buffer); + rfn = fobj->def->renderEscaped [writer]; + if (rfn == NULL) + rfn = fobj->def->renderEscaped [WRITER_DEFAULT]; + + if (!rejected) + rejected = &stub; + return rfn (tag, value, fobj->buffer, rejected); } /* Writes "line", stripping leading and duplicate white space. @@ -517,22 +586,24 @@ static const char* renderCompactInputLine (vString *b, const char *const line) return vStringValue (b); } -static const char *renderFieldKindName (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) +static const char *renderFieldKindName (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { - kindDefinition *kdef = getLanguageKind(tag->langType, tag->kindIndex); - return renderAsIs (b, kdef->name); + const char* name = getTagKindName (tag); + return renderAsIs (b, name); } static const char *renderFieldCompactInputLine (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b) + vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { const char *line; static vString *tmp; - tmp = vStringNewOrClear (tmp); + tmp = vStringNewOrClearWithAutoRelease (tmp); - line = readLineFromBypassAnyway (tmp, tag, NULL); + line = readLineFromBypassForTag (tmp, tag, NULL); if (line) renderCompactInputLine (b, line); else @@ -549,7 +620,8 @@ static const char *renderFieldCompactInputLine (const tagEntryInfo *const tag, static const char *renderFieldLineNumber (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b) + vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { long ln = tag->lineNumber; char buf[32] = {[0] = '\0'}; @@ -561,25 +633,41 @@ static const char *renderFieldLineNumber (const tagEntryInfo *const tag, return vStringValue (b); } -static const char *renderFieldRole (const tagEntryInfo *const tag, +static const char *renderFieldRoles (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b) + vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { - int rindex = tag->extensionFields.roleIndex; - - if (rindex == ROLE_INDEX_DEFINITION) - vStringClear (b); - else + roleBitsType rbits = tag->extensionFields.roleBits; + const roleDefinition * role; + if (rbits) { - return "TODO"; - } + int roleCount = countLanguageRoles (tag->langType, tag->kindIndex); + int nRoleWritten = 0; + for (int roleIndex = 0; roleIndex < roleCount; roleIndex++) + { + if (((rbits >> roleIndex) & (roleBitsType)1) + && isLanguageRoleEnabled (tag->langType, tag->kindIndex, roleIndex)) + { + if (nRoleWritten > 0) + vStringPut(b, ','); + + role = getTagRole(tag, roleIndex); + renderRole (role, b); + nRoleWritten++; + } + } + } + else + vStringCatS (b, ROLE_NAME_DEFINITION); return vStringValue (b); } static const char *renderFieldLanguage (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b) + vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { const char *l; @@ -592,62 +680,99 @@ static const char *renderFieldLanguage (const tagEntryInfo *const tag, } static const char *renderFieldAccess (const tagEntryInfo *const tag, - const char *value, - vString* b) + const char *value CTAGS_ATTR_UNUSED, + vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { return renderAsIs (b, WITH_DEFUALT_VALUE (tag->extensionFields.access)); } static const char *renderFieldKindLetter (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b) + vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { - return "TODO"; + static char c[2] = { [1] = '\0' }; + + c [0] = getTagKindLetter(tag); + + return renderAsIs (b, c); } static const char *renderFieldImplementation (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b) + vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { return renderAsIs (b, WITH_DEFUALT_VALUE (tag->extensionFields.implementation)); } static const char *renderFieldFile (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b) + vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { return renderAsIs (b, tag->isFileScope? "file": "-"); } -static const char *renderFieldPattern (const tagEntryInfo *const tag, +static const char *renderFieldPatternCommon (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b) + vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { - char* tmp = makePatternString (tag); - vStringCatS (b, tmp); - eFree (tmp); + if (tag->isFileEntry) + return NULL; + else if (tag->pattern) + vStringCatS (b, tag->pattern); + else + { + char* tmp; + + tmp = makePatternString (tag); + vStringCatS (b, tmp); + eFree (tmp); + } return vStringValue (b); } +static const char *renderFieldPatternCtags (const tagEntryInfo *const tag, + const char *value, + vString* b, + bool *rejected) +{ + /* This is for handling 'common' of 'fortran'. See the + description of --excmd=mixed in ctags.1. In tags output, what + we call "pattern" is instructions for vi. + + However, in the other formats, pattern should be pattern as its name. */ + if (tag->lineNumberEntry) + return NULL; + + return renderFieldPatternCommon(tag, value, b, rejected); +} + static const char *renderFieldRefMarker (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b) + vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { static char c[2] = { [1] = '\0' }; - c [0] = tag->extensionFields.roleIndex == ROLE_INDEX_DEFINITION? 'D': 'R'; + c [0] = (tag->extensionFields.roleBits)? 'R': 'D'; return renderAsIs (b, c); } -static const char *renderFieldExtra (const tagEntryInfo *const tag, +static const char *renderFieldExtras (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b) + vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { int i; bool hasExtra = false; + int c = countXtags(); - for (i = 0; i < XTAG_COUNT; i++) + for (i = 0; i < c; i++) { const char *name = getXtagName (i); @@ -671,8 +796,9 @@ static const char *renderFieldExtra (const tagEntryInfo *const tag, } static const char *renderFieldXpath (const tagEntryInfo *const tag, - const char *value, - vString* b) + const char *value CTAGS_ATTR_UNUSED, + vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { #ifdef HAVE_LIBXML if (tag->extensionFields.xpath) @@ -683,8 +809,9 @@ static const char *renderFieldXpath (const tagEntryInfo *const tag, } static const char *renderFieldScopeKindName(const tagEntryInfo *const tag, - const char *value, - vString* b) + const char *value CTAGS_ATTR_UNUSED, + vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { const char* kind; @@ -693,14 +820,15 @@ static const char *renderFieldScopeKindName(const tagEntryInfo *const tag, } static const char *renderFieldEnd (const tagEntryInfo *const tag, - const char *value, - vString* b) + const char *value CTAGS_ATTR_UNUSED, + vString* b, + bool *rejected CTAGS_ATTR_UNUSED) { static char buf[16]; if (tag->extensionFields.endLine != 0) { - sprintf (buf, "%ld", tag->extensionFields.endLine); + sprintf (buf, "%lu", tag->extensionFields.endLine); return renderAsIs (b, buf); } else @@ -743,18 +871,15 @@ static bool isSignatureFieldAvailable (const tagEntryInfo *const tag) return (tag->extensionFields.signature != NULL)? true: false; } -static bool isRoleFieldAvailable (const tagEntryInfo *const tag) +static bool isExtrasFieldAvailable (const tagEntryInfo *const tag) { - return (tag->extensionFields.roleIndex != ROLE_INDEX_DEFINITION)? true: false; -} - -static bool isExtraFieldAvailable (const tagEntryInfo *const tag) -{ - int i; + unsigned int i; for (i = 0; i < sizeof (tag->extra); i++) { if (tag->extra [i]) return true; + else if (tag->extraDynamic) + return true; } return false; @@ -776,48 +901,48 @@ static bool isEndFieldAvailable (const tagEntryInfo *const tag) extern bool isFieldEnabled (fieldType type) { - return getFieldDesc(type)->spec->enabled; + return getFieldObject(type)->def->enabled; } static bool isFieldFixed (fieldType type) { - return getFieldDesc(type)->fixed? true: false; + return getFieldObject(type)->fixed? true: false; } extern bool enableField (fieldType type, bool state, bool warnIfFixedField) { - fieldDefinition *spec = getFieldDesc(type)->spec; - bool old = spec->enabled? true: false; + fieldDefinition *def = getFieldObject(type)->def; + bool old = def->enabled; if (isFieldFixed (type)) { if ((!state) && warnIfFixedField) { - if (spec->name && spec->letter != NUL_FIELD_LETTER) + if (def->name && def->letter != NUL_FIELD_LETTER) error(WARNING, "Cannot disable fixed field: '%c'{%s}", - spec->letter, spec->name); - else if (spec->name) + def->letter, def->name); + else if (def->name) error(WARNING, "Cannot disable fixed field: {%s}", - spec->name); - else if (spec->letter != NUL_FIELD_LETTER) + def->name); + else if (def->letter != NUL_FIELD_LETTER) error(WARNING, "Cannot disable fixed field: '%c'", - getFieldDesc(type)->spec->letter); + getFieldObject(type)->def->letter); else AssertNotReached(); } } else { - getFieldDesc(type)->spec->enabled = state; + getFieldObject(type)->def->enabled = state; if (isCommonField (type)) verbose ("enable field \"%s\": %s\n", - getFieldDesc(type)->spec->name, - (state? "TRUE": "FALSE")); + getFieldObject(type)->def->name, + (state? "yes": "no")); else verbose ("enable field \"%s\"<%s>: %s\n", - getFieldDesc(type)->spec->name, + getFieldObject(type)->def->name, getLanguageName (getFieldOwner(type)), - (state? "TRUE": "FALSE")); + (state? "yes": "no")); } return old; } @@ -829,90 +954,241 @@ extern bool isCommonField (fieldType type) extern int getFieldOwner (fieldType type) { - return getFieldDesc(type)->language; + return getFieldObject(type)->language; +} + +extern unsigned int getFieldDataType (fieldType type) +{ + return getFieldObject(type)->def->dataType; } extern bool isFieldRenderable (fieldType type) { - return getFieldDesc(type)->spec->renderEscaped? true: false; + return getFieldObject(type)->def->renderEscaped [WRITER_DEFAULT]? true: false; } extern int countFields (void) { - return fieldDescUsed; + return fieldObjectUsed; } extern fieldType nextSiblingField (fieldType type) { - fieldDesc *fdesc; + fieldObject *fobj; - fdesc = fieldDescs + type; - return fdesc->sibling; + fobj = fieldObjects + type; + return fobj->sibling; } static void updateSiblingField (fieldType type, const char* name) { int i; - fieldDesc *fdesc; + fieldObject *fobj; for (i = type; i > 0; i--) { - fdesc = fieldDescs + i - 1; - if (fdesc->spec->name && (strcmp (fdesc->spec->name, name) == 0)) + fobj = fieldObjects + i - 1; + if (fobj->def->name && (strcmp (fobj->def->name, name) == 0)) { - Assert (fdesc->sibling == FIELD_UNKNOWN); - fdesc->sibling = type; + Assert (fobj->sibling == FIELD_UNKNOWN); + fobj->sibling = type; break; } } } -static const char* defaultRenderer (const tagEntryInfo *const tag, +static const char* defaultRenderer (const tagEntryInfo *const tag CTAGS_ATTR_UNUSED, const char *value, - vString * buffer) + vString * buffer CTAGS_ATTR_UNUSED, + bool *rejected CTAGS_ATTR_UNUSED) { return value; } -extern int defineField (fieldDefinition *spec, langType language) +extern int defineField (fieldDefinition *def, langType language) { - fieldDesc *fdesc; + fieldObject *fobj; char *nameWithPrefix; size_t i; - Assert (spec); - Assert (spec->name); - for (i = 0; i < strlen (spec->name); i++) + Assert (def); + Assert (def->name); + for (i = 0; i < strlen (def->name); i++) { - Assert ( isalnum (spec->name [i]) ); + Assert ( isalpha (def->name [i]) ); } - spec->letter = NUL_FIELD_LETTER; + def->letter = NUL_FIELD_LETTER; - if (fieldDescUsed == fieldDescAllocated) + if (fieldObjectUsed == fieldObjectAllocated) { - fieldDescAllocated *= 2; - fieldDescs = xRealloc (fieldDescs, fieldDescAllocated, fieldDesc); + fieldObjectAllocated *= 2; + fieldObjects = xRealloc (fieldObjects, fieldObjectAllocated, fieldObject); } - fdesc = fieldDescs + (fieldDescUsed); - spec->ftype = fieldDescUsed++; + fobj = fieldObjects + (fieldObjectUsed); + def->ftype = fieldObjectUsed++; - if (spec->renderEscaped == NULL) - spec->renderEscaped = defaultRenderer; + if (def->renderEscaped [WRITER_DEFAULT] == NULL) + def->renderEscaped [WRITER_DEFAULT] = defaultRenderer; - fdesc->spec = spec; + if (! def->dataType) + def->dataType = FIELDTYPE_STRING; - fdesc->fixed = 0; - fdesc->buffer = NULL; + fobj->def = def; - nameWithPrefix = eMalloc (sizeof CTAGS_FIELD_PREFIX + strlen (spec->name) + 1); + fobj->fixed = 0; + fobj->buffer = NULL; + + nameWithPrefix = eMalloc (sizeof CTAGS_FIELD_PREFIX + strlen (def->name) + 1); nameWithPrefix [0] = '\0'; strcat (nameWithPrefix, CTAGS_FIELD_PREFIX); - strcat (nameWithPrefix, spec->name); - fdesc->nameWithPrefix = nameWithPrefix; + strcat (nameWithPrefix, def->name); + fobj->nameWithPrefix = nameWithPrefix; + DEFAULT_TRASH_BOX(nameWithPrefix, eFree); + + fobj->language = language; + fobj->sibling = FIELD_UNKNOWN; + + updateSiblingField (def->ftype, def->name); + return def->ftype; +} + +#define FIELD_COL_LETTER 0 +#define FIELD_COL_NAME 1 +#define FIELD_COL_ENABLED 2 +#define FIELD_COL_LANGUAGE 3 +#define FIELD_COL_JSTYPE 4 +#define FIELD_COL_FIXED 5 +#define FIELD_COL_DESCRIPTION 6 +extern struct colprintTable * fieldColprintTableNew (void) +{ + return colprintTableNew ("L:LETTER", "L:NAME", "L:ENABLED", + "L:LANGUAGE", "L:JSTYPE", "L:FIXED", "L:DESCRIPTION", NULL); +} + +static void fieldColprintAddLine (struct colprintTable *table, int i) +{ + fieldObject *fobj = getFieldObject(i); + fieldDefinition *fdef = fobj->def; + + struct colprintLine *line = colprintTableGetNewLine(table); + + colprintLineAppendColumnChar (line, + (fdef->letter == NUL_FIELD_LETTER) + ? '-' + : fdef->letter); + + const char *name = getFieldName (i); + colprintLineAppendColumnCString (line, name? name: RSV_NONE); + colprintLineAppendColumnBool (line, fdef->enabled); + colprintLineAppendColumnCString (line, + fobj->language == LANG_IGNORE + ? RSV_NONE + : getLanguageName (fobj->language)); + + char typefields [] = "---"; + { + unsigned int bmask, offset; + unsigned int type = getFieldDataType(i); + for (bmask = 1, offset = 0; + bmask < FIELDTYPE_END_MARKER; + bmask <<= 1, offset++) + if (type & bmask) + typefields[offset] = fieldDataTypeFalgs[offset]; + } + colprintLineAppendColumnCString (line, typefields); + colprintLineAppendColumnBool (line, fobj->fixed); + colprintLineAppendColumnCString (line, fdef->description); +} + +extern void fieldColprintAddCommonLines (struct colprintTable *table) +{ + for (int i = 0; i <= FIELD_BUILTIN_LAST; i++) + fieldColprintAddLine(table, i); +} + +extern void fieldColprintAddLanguageLines (struct colprintTable *table, langType language) +{ + for (unsigned int i = FIELD_BUILTIN_LAST + 1; i < fieldObjectUsed; i++) + { + fieldObject *fobj = getFieldObject(i); + if (fobj->language == language) + fieldColprintAddLine (table, i); + } +} + +static int fieldColprintCompareLines (struct colprintLine *a , struct colprintLine *b) +{ + const char *a_fixed = colprintLineGetColumn (a, FIELD_COL_FIXED); + const char *b_fixed = colprintLineGetColumn (b, FIELD_COL_FIXED); + const char *a_parser = colprintLineGetColumn (a, FIELD_COL_LANGUAGE); + const char *b_parser = colprintLineGetColumn (b, FIELD_COL_LANGUAGE); + + if ((strcmp (a_fixed, "yes") == 0) + && (strcmp (b_fixed, "yes") == 0)) + { + /* name, input, pattern, compact */ + const char *a_name = colprintLineGetColumn (a, FIELD_COL_NAME); + const char *b_name = colprintLineGetColumn (b, FIELD_COL_NAME); + const char *ref_name; + unsigned int a_index = ~0U; + unsigned int b_index = ~0U; + + for (unsigned int i = 0; i < ARRAY_SIZE(fieldDefinitionsFixed); i++) + { + ref_name = fieldDefinitionsFixed [i].name; + if (strcmp (a_name, ref_name) == 0) + a_index = i; + if (strcmp (b_name, ref_name) == 0) + b_index = i; + if ((a_index != ~0U) || (b_index != ~0U)) + break; + } + + if (a_index < b_index) + return -1; + else if (a_index == b_index) + return 0; /* ??? */ + else + return 1; + } + else if ((strcmp (a_fixed, "yes") == 0) + && (strcmp (b_fixed, "yes") != 0)) + return -1; + else if ((strcmp (a_fixed, "yes") != 0) + && (strcmp (b_fixed, "yes") == 0)) + return 1; + + if (strcmp (a_parser, RSV_NONE) == 0 + && strcmp (b_parser, RSV_NONE) != 0) + return -1; + else if (strcmp (a_parser, RSV_NONE) != 0 + && strcmp (b_parser, RSV_NONE) == 0) + return 1; + else if (strcmp (a_parser, RSV_NONE) != 0 + && strcmp (b_parser, RSV_NONE) != 0) + { + int r; + r = strcmp (a_parser, b_parser); + if (r != 0) + return r; - fdesc->language = language; - fdesc->sibling = FIELD_UNKNOWN; + const char *a_name = colprintLineGetColumn (a, FIELD_COL_NAME); + const char *b_name = colprintLineGetColumn (b, FIELD_COL_NAME); - updateSiblingField (spec->ftype, spec->name); - return spec->ftype; + return strcmp(a_name, b_name); + } + else + { + const char *a_letter = colprintLineGetColumn (a, FIELD_COL_LETTER); + const char *b_letter = colprintLineGetColumn (b, FIELD_COL_LETTER); + + return strcmp(a_letter, b_letter); + } +} + +extern void fieldColprintTablePrint (struct colprintTable *table, + bool withListHeader, bool machinable, FILE *fp) +{ + colprintTableSort (table, fieldColprintCompareLines); + colprintTablePrint (table, 0, withListHeader, machinable, fp); } diff --git a/ctags/main/field.h b/ctags/main/field.h index 741f258806..d16f5bc63e 100644 --- a/ctags/main/field.h +++ b/ctags/main/field.h @@ -13,6 +13,8 @@ #define CTAGS_MAIN_FIELD_H #include "general.h" +#include "colprint_p.h" +#include "writer_p.h" #include "types.h" #include "vstring.h" @@ -42,35 +44,47 @@ typedef enum eFieldType { /* extension field content control */ FIELD_KIND_KEY, /* EXTENSION FIELDS NEWLY INTRODUCED IN UCTAGS */ - FIELD_ROLE, + FIELD_ROLES, FIELD_REF_MARK, FIELD_SCOPE_KEY, - FIELD_EXTRA, + FIELD_EXTRAS, FIELD_XPATH, FIELD_SCOPE_KIND_LONG, - FIELD_END, - FIELD_BUILTIN_LAST = FIELD_END, + FIELD_END_LINE, + FIELD_BUILTIN_LAST = FIELD_END_LINE, } fieldType ; typedef const char* (* renderEscaped) (const tagEntryInfo *const tag, const char *value, - vString * buffer); + vString * buffer, + bool *rejected); typedef bool (* isValueAvailable) (const struct sTagEntryInfo *const tag); +#define fieldDataTypeFalgs "sib" /* used in --list-fields */ +typedef enum eFieldDataType { + FIELDTYPE_STRING = 1 << 0, + FIELDTYPE_INTEGER = 1 << 1, + FIELDTYPE_BOOL = 1 << 2, + + /* used in --list-fields */ + FIELDTYPE_END_MARKER = 1 << 3, +} fieldDataType; + #define FIELD_LETTER_NO_USE '\0' -typedef struct sFieldDefinition { - /* lettern, and ftype are initialized in the main part, +struct sFieldDefinition { + /* letter, and ftype are initialized in the main part, not in a parser. */ #define NUL_FIELD_LETTER '\0' unsigned char letter; const char* name; const char* description; bool enabled; - renderEscaped renderEscaped; + renderEscaped renderEscaped [WRITER_COUNT]; isValueAvailable isValueAvailable; + fieldDataType dataType; /* used in json output */ unsigned int ftype; /* Given from the main part */ -} fieldDefinition; +}; extern fieldType getFieldTypeForOption (char letter); @@ -93,14 +107,18 @@ extern bool enableField (fieldType type, bool state, bool warnIfFixedField); extern bool isCommonField (fieldType type); extern int getFieldOwner (fieldType type); extern const char* getFieldName (fieldType type); +extern unsigned int getFieldDataType (fieldType type); extern void printFields (int language); +/* Whether the field specified with TYPE has a + method for rendering in the current format. */ extern bool isFieldRenderable (fieldType type); extern bool doesFieldHaveValue (fieldType type, const tagEntryInfo *tag); -extern const char* renderFieldEscaped (fieldType type, const tagEntryInfo *tag, int index); +extern const char* renderFieldEscaped (writerType writer, fieldType type, const tagEntryInfo *tag, int index, + bool *rejected); -extern void initFieldDescs (void); +extern void initFieldObjects (void); extern int countFields (void); /* language should be typed to langType. @@ -108,4 +126,11 @@ extern int countFields (void); extern int defineField (fieldDefinition *spec, langType language); extern fieldType nextSiblingField (fieldType type); +/* --list-fields implementation. LANGUAGE must be initialized. */ +extern struct colprintTable * fieldColprintTableNew (void); +extern void fieldColprintAddCommonLines (struct colprintTable *table); +extern void fieldColprintAddLanguageLines (struct colprintTable *table, langType language); +extern void fieldColprintTablePrint (struct colprintTable *table, + bool withListHeader, bool machinable, FILE *fp); + #endif /* CTAGS_MAIN_FIELD_H */ diff --git a/ctags/main/flags.c b/ctags/main/flags.c index ae9f830956..b5931eaa7e 100644 --- a/ctags/main/flags.c +++ b/ctags/main/flags.c @@ -16,7 +16,8 @@ #include #include -#include "flags.h" +#include "ctags.h" +#include "flags_p.h" #include "vstring.h" #include "routines.h" @@ -27,8 +28,6 @@ void flagsEval (const char* flags_original, flagDefinition* defs, unsigned int n if (!flags_original) return; - if (!defs) - return; flags = eStrdup (flags_original); for (i = 0 ; flags [i] != '\0' ; ++i) @@ -38,34 +37,34 @@ void flagsEval (const char* flags_original, flagDefinition* defs, unsigned int n const char* aflag = flags + i + 1; char* needle_close_paren = strchr(aflag, LONG_FLAGS_CLOSE); const char* param; - char* needle_eqaul; + char* needle_equal; if (needle_close_paren == NULL) { - error (WARNING, "long flags specifier opened with `%c' is not closed `%c'", - LONG_FLAGS_OPEN, LONG_FLAGS_CLOSE); + error (WARNING, "long flags specifier opened with `%c' is not closed `%c': \"%s\"", + LONG_FLAGS_OPEN, LONG_FLAGS_CLOSE, flags_original); break; } *needle_close_paren = '\0'; - needle_eqaul = strchr(aflag, '='); - if ((needle_eqaul == NULL || (needle_eqaul >= needle_close_paren))) + needle_equal = strchr(aflag, '='); + if ((needle_equal == NULL || (needle_equal >= needle_close_paren))) { - needle_eqaul = NULL; + needle_equal = NULL; param = NULL; } else { - param = needle_eqaul + 1; - *needle_eqaul = '\0'; + param = needle_equal + 1; + *needle_equal = '\0'; } for ( j = 0 ; j < ndefs ; ++j ) if (defs[j].longStr && (strcmp(aflag, defs[j].longStr) == 0)) defs[j].longProc(aflag, param, data); - if (needle_eqaul) - *needle_eqaul = '='; + if (needle_equal) + *needle_equal = '='; *needle_close_paren = LONG_FLAGS_CLOSE; i = needle_close_paren - flags; @@ -77,31 +76,75 @@ void flagsEval (const char* flags_original, flagDefinition* defs, unsigned int n eFree (flags); } -void flagPrintHelp (flagDefinition* def, unsigned int ndefs) +extern struct colprintTable * flagsColprintTableNew (void) +{ + return colprintTableNew ("L:LETTER", "L:NAME", "L:DESCRIPTION", NULL); +} + +extern void flagsColprintAddDefinitions (struct colprintTable *table, flagDefinition* def, + unsigned int ndefs) { + vString *longName = vStringNew (); - unsigned int i; - const char *longStr; - const char *description; - const char *paramName; - char shortChar[3]; - for ( i = 0; i < ndefs; ++i ) + for (unsigned int i = 0; i < ndefs; i++) { - longStr = def[i].longStr? def[i].longStr: ""; - description = def[i].description? def[i].description: ""; - paramName = def[i].paramName; + struct colprintLine * line; + char shortChar; + const char *paramName; + const char *description; + - if (def[i].shortChar == '\0') - strcpy (shortChar, "\\0"); - else + line = colprintTableGetNewLine(table); + + shortChar = def[i].shortChar; + if (shortChar == '\0') + shortChar = '-'; + colprintLineAppendColumnChar (line, shortChar); + + vStringCopyS (longName, def[i].longStr? def[i].longStr: RSV_NONE); + paramName = def[i].paramName; + if (paramName) { - shortChar[0] = def[i].shortChar; - shortChar[1] = '\0'; + vStringPut (longName, '='); + vStringCatS (longName, paramName); } + colprintLineAppendColumnVString (line, longName); + vStringClear(longName); - if (paramName) - printf ("%s\t%s=%s\t%s\n", shortChar, longStr, paramName, description); - else - printf ("%s\t%s\t%s\n", shortChar, longStr, description); + description = def[i].description? def[i].description: ""; + colprintLineAppendColumnCString (line, description); } + + vStringDelete(longName); +} + +static int flagsColprintCompareLines(struct colprintLine *a , struct colprintLine *b) +{ + const char *a_letter = colprintLineGetColumn (a, 0); + const char *b_letter = colprintLineGetColumn (b, 0); + + if (a_letter[0] != '-' && b_letter[0] == '-') + return -1; + else if (a_letter[0] == '-' && b_letter[0] != '-') + return 1; + else if (a_letter[0] != '-' && b_letter[0] != '-') + return strcmp(a_letter, b_letter); + + + const char *a_name = colprintLineGetColumn (a, 1); + const char *b_name = colprintLineGetColumn (b, 1); + + if (a_name[0] != '_' && b_name[0] == '_') + return -1; + else if (a_name[0] == '_' && b_name[0] != '_') + return 1; + + return strcmp(a_name, b_name); +} + +extern void flagsColprintTablePrint (struct colprintTable *table, + bool withListHeader, bool machinable, FILE *fp) +{ + colprintTableSort (table, flagsColprintCompareLines); + colprintTablePrint (table, 0, withListHeader, machinable, fp); } diff --git a/ctags/main/flags.h b/ctags/main/flags_p.h similarity index 60% rename from ctags/main/flags.h rename to ctags/main/flags_p.h index af43636827..786bf9a61a 100644 --- a/ctags/main/flags.h +++ b/ctags/main/flags_p.h @@ -8,8 +8,12 @@ * * Defines external interface to option processing. */ -#ifndef CTAGS_MAIN_FLAGS_H -#define CTAGS_MAIN_FLAGS_H +#ifndef CTAGS_MAIN_FLAGS_PRIVATE_H +#define CTAGS_MAIN_FLAGS_PRIVATE_H + +#include "general.h" +#include "colprint_p.h" + #define LONG_FLAGS_OPEN '{' #define LONG_FLAGS_CLOSE '}' @@ -24,6 +28,8 @@ typedef struct sFlagDefinition { } flagDefinition; extern void flagsEval (const char* flags, flagDefinition* defs, unsigned int ndefs, void* data); -extern void flagPrintHelp (flagDefinition* def, unsigned int ndefs); - -#endif /* CTAGS_MAIN_FLAGS_H */ +extern struct colprintTable * flagsColprintTableNew (void); +extern void flagsColprintAddDefinitions (struct colprintTable *table, flagDefinition* def, unsigned int ndefs); +extern void flagsColprintTablePrint (struct colprintTable *table, + bool withListHeader, bool machinable, FILE *fp); +#endif /* CTAGS_MAIN_FLAGS_PRIVATE_H */ diff --git a/ctags/main/fmt.c b/ctags/main/fmt.c index d15993ba19..0be6d07aa3 100644 --- a/ctags/main/fmt.c +++ b/ctags/main/fmt.c @@ -12,9 +12,9 @@ #include "general.h" #include "debug.h" -#include "fmt.h" +#include "entry_p.h" +#include "fmt_p.h" #include "field.h" -#include "options.h" #include "parse.h" #include "routines.h" #include @@ -25,6 +25,7 @@ typedef union uFmtSpec { struct { fieldType ftype; int width; + char *raw_fmtstr; } field; } fmtSpec; @@ -59,31 +60,33 @@ static int printTagField (fmtSpec* fspec, MIO* fp, const tagEntryInfo * tag) ftype = fspec->field.ftype; if (isCommonField (ftype)) - str = renderFieldEscaped (ftype, tag, NO_PARSER_FIELD); + /* TODO: Don't use WRITER_XREF directly */ + str = renderFieldEscaped (WRITER_XREF, ftype, tag, NO_PARSER_FIELD, NULL); else { unsigned int findex; + const tagField *f; for (findex = 0; findex < tag->usedParserFields; findex++) { - if (isParserFieldCompatibleWithFtype (tag->parserFields + findex, ftype)) + f = getParserField(tag, findex); + if (isParserFieldCompatibleWithFtype (f, ftype)) break; } if (findex == tag->usedParserFields) str = ""; - else if (isFieldEnabled (tag->parserFields [findex].ftype)) - str = renderFieldEscaped (tag->parserFields [findex].ftype, - tag, findex); + else if (isFieldEnabled (f->ftype)) + /* TODO: Don't use WRITER_XREF directly */ + str = renderFieldEscaped (WRITER_XREF, f->ftype, + tag, findex, NULL); } if (str == NULL) str = ""; - if (width < 0) - i = mio_printf (fp, "%-*s", -1 * width, str); - else if (width > 0) - i = mio_printf (fp, "%*s", width, str); + if (width) + i = mio_printf (fp, fspec->field.raw_fmtstr, width, str); else { mio_puts (fp, str); @@ -159,8 +162,8 @@ static langType getLanguageComponentInFieldName (const char *fullName, return language; } -static fmtElement** queueTagField (fmtElement **last, long width, char field_letter, - const char *field_name) +static fmtElement** queueTagField (fmtElement **last, long width, bool truncation, + char field_letter, const char *field_name) { fieldType ftype; fmtElement *cur; @@ -200,6 +203,16 @@ static fmtElement** queueTagField (fmtElement **last, long width, char field_let cur->spec.field.width = width; cur->spec.field.ftype = ftype; + if (width < 0) + { + cur->spec.field.width *= -1; + cur->spec.field.raw_fmtstr = (truncation? "%-.*s": "%-*s"); + } + else if (width > 0) + cur->spec.field.raw_fmtstr = (truncation? "%.*s": "%*s"); + else + cur->spec.field.raw_fmtstr = NULL; + enableField (ftype, true, false); if (language == LANG_AUTO) { @@ -241,6 +254,7 @@ extern fmtElement *fmtNew (const char* fmtString) else { int justification_right = 1; + bool truncation = false; vString *width = NULL; if (literal) { @@ -257,6 +271,14 @@ extern fmtElement *fmtNew (const char* fmtString) error (FATAL, "unexpectedly terminated just after '-': \"%s\"", fmtString); } + if (cursor [i] == '.') + { + truncation = true; + i++; + + if (cursor [i] == '\0') + error (FATAL, "unexpectedly terminated just after '.': \"%s\"", fmtString); + } while ( '0' <= cursor[i] && cursor[i] <= '9' ) { @@ -276,7 +298,7 @@ extern fmtElement *fmtNew (const char* fmtString) if (width) { if(!strToLong (vStringValue (width), 0, &column_width)) - error (FATAL | PERROR, "coverting failed: %s", vStringValue (width)); + error (FATAL | PERROR, "converting failed: %s", vStringValue (width)); vStringDelete (width); width = NULL; column_width *= justification_right; @@ -290,13 +312,14 @@ extern fmtElement *fmtNew (const char* fmtString) for (; cursor[i] != '}'; i++) vStringPut (field_name, cursor[i]); - last = queueTagField (last, column_width, NUL_FIELD_LETTER, - vStringValue (field_name)); + last = queueTagField (last, column_width, truncation, + NUL_FIELD_LETTER, vStringValue (field_name)); vStringDelete (field_name); } else - last = queueTagField (last, column_width, cursor[i], NULL); + last = queueTagField (last, column_width, truncation, + cursor[i], NULL); } } diff --git a/ctags/main/fmt.h b/ctags/main/fmt_p.h similarity index 100% rename from ctags/main/fmt.h rename to ctags/main/fmt_p.h diff --git a/ctags/main/general.h b/ctags/main/general.h index c938b02bf9..5f68e5a2c4 100644 --- a/ctags/main/general.h +++ b/ctags/main/general.h @@ -14,9 +14,6 @@ */ #if defined (HAVE_CONFIG_H) # include -#if (defined (HAVE_FORK) && defined (HAVE_WAITPID) && defined (HAVE_EXECV) && defined (HAVE_PIPE)) -#define HAVE_COPROC -#endif #elif defined (WIN32) # include "e_msoft.h" #endif @@ -70,4 +67,14 @@ extern void *unlink (const char *); extern char *getenv (const char *); #endif +/* +* HACK for #1610. +*/ + +#ifdef ICONV_USE_LIB_PREFIX +#define iconv libiconv +#define iconv_open libiconv_open +#define iconv_close libiconv_close +#endif + #endif /* CTAGS_MAIN_GENERAL_H */ diff --git a/ctags/main/gvars.h b/ctags/main/gvars.h new file mode 100644 index 0000000000..5d1ee9175b --- /dev/null +++ b/ctags/main/gvars.h @@ -0,0 +1,29 @@ +/* +* Copyright (c) 1998-2003, Darren Hiebert +* Copyright (c) 2018, Red Hat, Inc. + * Copyright (c) 2018, Masatake YAMATO +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* Declare variables visible to parsers +*/ +#ifndef CTAGS_GVARS_H +#define CTAGS_GVARS_H + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ + +#ifdef DEBUG + +/* -d debugging output */ +extern long ctags_debugLevel; + +#endif /* DEBUG */ + +/* -V verbose */ +extern bool ctags_verbose; + +#endif /* CTAGS_GVARS_H */ diff --git a/ctags/main/keyword.c b/ctags/main/keyword.c index 374c311e51..0a5a9859f0 100644 --- a/ctags/main/keyword.c +++ b/ctags/main/keyword.c @@ -17,7 +17,8 @@ #include "debug.h" #include "keyword.h" -#include "options.h" +#include "keyword_p.h" +#include "parse.h" #include "routines.h" /* @@ -240,3 +241,18 @@ extern void printKeywordTable (void) } #endif + +extern void dumpKeywordTable (FILE *fp) +{ + unsigned int i; + for (i = 0 ; i < TableSize ; ++i) + { + hashEntry **const table = getHashTable (); + hashEntry *entry = table [i]; + while (entry != NULL) + { + fprintf(fp, "%s %s\n", entry->string, getLanguageName (entry->language)); + entry = entry->next; + } + } +} diff --git a/ctags/main/keyword.h b/ctags/main/keyword.h index 9be3e5b4d8..710de35267 100644 --- a/ctags/main/keyword.h +++ b/ctags/main/keyword.h @@ -13,10 +13,11 @@ * INCLUDE FILES */ #include "general.h" /* must always come first */ - #include "types.h" -#include "vstring.h" +/* +* MACROS +*/ #define KEYWORD_NONE -1 /* @@ -25,9 +26,5 @@ extern void addKeyword (const char *const string, langType language, int value); extern int lookupKeyword (const char *const string, langType language); extern int lookupCaseKeyword (const char *const string, langType language); -extern void freeKeywordTable (void); -#ifdef DEBUG -extern void printKeywordTable (void); -#endif #endif /* CTAGS_MAIN_KEYWORD_H */ diff --git a/ctags/main/keyword_p.h b/ctags/main/keyword_p.h new file mode 100644 index 0000000000..32b846d760 --- /dev/null +++ b/ctags/main/keyword_p.h @@ -0,0 +1,26 @@ +/* +* Copyright (c) 1998-2002, Darren Hiebert +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* main part private interface to keyword.c +*/ +#ifndef CTAGS_MAIN_KEYWORD_PRIVATE_H +#define CTAGS_MAIN_KEYWORD_PRIVATE_H + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ +#include + +extern void freeKeywordTable (void); + +extern void dumpKeywordTable (FILE *fp); + +#ifdef DEBUG +extern void printKeywordTable (void); +#endif + +#endif /* CTAGS_MAIN_KEYWORD_PRIVATE_H */ diff --git a/ctags/main/kind.c b/ctags/main/kind.c index a751aa422b..70db4d9766 100644 --- a/ctags/main/kind.c +++ b/ctags/main/kind.c @@ -13,15 +13,39 @@ #include "general.h" #include +#include + +#include "ctags.h" #include "debug.h" +#include "entry.h" #include "kind.h" -#include "parse.h" +#include "parse_p.h" +#include "options.h" +#include "routines.h" +#include "vstring.h" -extern void printRole (const roleDefinition* const role) -{ - if (role) - printf ("%s\t%s\t%s\n", role->name, role->description, role->enabled? "on": "off"); -} +typedef struct sRoleObject { + roleDefinition *def; + freeRoleDefFunc free; +} roleObject; + +struct roleControlBlock { + roleObject *role; + unsigned int count; + int owner; +}; + +typedef struct sKindObject { + kindDefinition *def; + freeKindDefFunc free; + struct roleControlBlock *rcb; +} kindObject; + +struct kindControlBlock { + kindObject *kind; + unsigned int count; + langType owner; +}; extern const char *renderRole (const roleDefinition* const role, vString* b) { @@ -29,84 +53,12 @@ extern const char *renderRole (const roleDefinition* const role, vString* b) return vStringValue (b); } -#define PR_KIND_WIDTH_LETTER 7 -#define PR_KIND_WIDTH_NAME 15 -#define PR_KIND_WIDTH_DESCRIPTION 30 -#define PR_KIND_WIDTH_ENABLED 8 -#define PR_KIND_WIDTH_REFONLY 7 -#define PR_KIND_WIDTH_NROLE 6 -#define PR_KIND_WIDTH_MASTER 10 -#define MAKE_KIND_FMT(PREFIX,LETTER_SPEC,NROLL_SPEC) \ - PREFIX \ - PR_KIND_FMT (LETTER,LETTER_SPEC) \ - " " \ - PR_KIND_FMT (NAME,s) \ - " " \ - PR_KIND_FMT (ENABLED,s) \ - " " \ - PR_KIND_FMT (REFONLY,s) \ - " " \ - PR_KIND_FMT (NROLE,NROLL_SPEC) \ - " " \ - PR_KIND_FMT (MASTER,s) \ - " " \ - PR_KIND_FMT (DESCRIPTION,s) \ - "\n" - -extern void printKindListHeader (bool indent, bool tabSeparated) -{ -#define KIND_HEADER_COMMON_FMT MAKE_KIND_FMT("%s", s, s) - - const char *fmt = tabSeparated - ? "%s%s%s\t%s\t%s\t%s\t%s\t%s\t%s\n" - : (indent - ? PR_KIND_FMT (LANG,s) KIND_HEADER_COMMON_FMT - : "%s" KIND_HEADER_COMMON_FMT) - ; - - printf (fmt, - (indent? "#PARSER": ""), - (indent? (tabSeparated? "\t": " "): ""), - (indent? "LETTER": "#LETTER"), - "NAME", - "ENABLED", - "REFONLY", - "NROLES", - "MASTER", - "DESCRIPTION"); - -#undef KIND_HEADER_COMMON_FMT -} - -extern void printKind (const kindDefinition* const kind, bool allKindFields, bool indent, - bool tabSeparated) -{ -#define KIND_FMT MAKE_KIND_FMT("", c, d) - - if (allKindFields) - { - printf ((tabSeparated - ?"%s%c\t%s\t%s\t%s\t%d\t%s\t%s\n" - :"%s" KIND_FMT), - (indent? (tabSeparated? "\t": " "): ""), - kind->letter, - kind->name != NULL ? kind->name : "", - kind->enabled ? "on" : "off", - kind->referenceOnly ? "TRUE" : "FALSE", - kind->nRoles, - (kind->master - || kind->slave ) ? getLanguageName (kind->syncWith): "", - kind->description != NULL ? kind->description : ""); - } - else if (!kind->referenceOnly) - { - printf ("%s%c %s%s\n", indent ? " " : "", kind->letter, +extern void printKind (const kindDefinition* const kind, bool indent) +{ + printf ("%s%c %s%s\n", indent ? " " : "", kind->letter, kind->description != NULL ? kind->description : (kind->name != NULL ? kind->name : ""), kind->enabled ? "" : " [off]"); - } - -#undef KIND_FMT } const char *scopeSeparatorFor (langType lang, int kindIndex, int parentKindIndex) @@ -126,7 +78,7 @@ const char *scopeSeparatorFor (langType lang, int kindIndex, int parentKindIndex return "."; } - while (table - kind->separators < kind->separatorCount) + while (table - kind->separators < (int)kind->separatorCount) { /* KIND_WILDCARD cannot be used as a key for finding a root separator.*/ @@ -155,3 +107,423 @@ extern void enableKind (kindDefinition *kind, bool enable) slave->enabled = enable; } } + +static struct roleControlBlock* allocRoleControlBlock (kindObject *kind) +{ + unsigned int j; + struct roleControlBlock* rcb; + + rcb = xMalloc(1, struct roleControlBlock); + rcb->count = kind->def->nRoles; + rcb->owner = kind->def->id; + rcb->role = xMalloc(rcb->count, roleObject); + for (j = 0; j < rcb->count; j++) + { + roleObject *role = rcb->role + j; + role->def = kind->def->roles + j; + role->free = NULL; + role->def->id = j; + } + + return rcb; +} + +extern struct kindControlBlock* allocKindControlBlock (parserDefinition *parser) +{ + unsigned int i; + struct kindControlBlock *kcb; + + kcb = xMalloc (1, struct kindControlBlock); + kcb->kind = xMalloc (parser->kindCount, kindObject); + kcb->count = parser->kindCount; + kcb->owner = parser->id; + + for (i = 0; i < parser->kindCount; ++i) + { + kindObject *kind = kcb->kind + i; + kind->def = parser->kindTable + i; + kind->free = NULL; + kind->def->id = i; + kind->rcb = allocRoleControlBlock (kind); + } + + return kcb; +} + +static void freeRoleControlBlock (struct roleControlBlock *rcb) +{ + unsigned int i; + for (i = 0; i < rcb->count; ++i) + { + if (rcb->role[i].free) + rcb->role [i].free (rcb->role [i].def); + } + eFree (rcb->role); + eFree (rcb); +} + +extern void freeKindControlBlock (struct kindControlBlock* kcb) +{ + unsigned int i; + + for (i = 0; i < kcb->count; ++i) + { + if (kcb->kind [i].free) + kcb->kind [i].free (kcb->kind [i].def); + freeRoleControlBlock (kcb->kind [i].rcb); + } + eFree (kcb->kind); + eFree (kcb); +} + +extern int defineKind (struct kindControlBlock* kcb, kindDefinition *def, + freeKindDefFunc freeKindDef) +{ + def->id = kcb->count++; + kcb->kind = xRealloc (kcb->kind, kcb->count, kindObject); + kcb->kind [def->id].def = def; + kcb->kind [def->id].free = freeKindDef; + kcb->kind [def->id].rcb = allocRoleControlBlock(kcb->kind + def->id); + + verbose ("Add kind[%d] \"%c,%s,%s\" to %s\n", def->id, + def->letter, def->name, def->description, + getLanguageName (kcb->owner)); + + return def->id; +} + +extern int defineRole (struct kindControlBlock* kcb, int kindIndex, + roleDefinition *def, freeRoleDefFunc freeRoleDef) +{ + struct roleControlBlock *rcb = kcb->kind[kindIndex].rcb; + int roleIndex = rcb->count++; + roleObject *role; + + if (roleIndex == ROLE_MAX_COUNT) + { + rcb->count--; + error (FATAL, "Too many role definition for kind \"%s\" of language \"%s\" (> %d)", + kcb->kind[kindIndex].def->name, + getLanguageName (kcb->owner), + (int)(ROLE_MAX_COUNT - 1)); + } + + rcb->role = xRealloc (rcb->role, rcb->count, roleObject); + role = rcb->role + roleIndex; + role->def = def; + role->free = freeRoleDef; + role->def->id = roleIndex; + return roleIndex; +} + +extern bool isRoleEnabled (struct kindControlBlock* kcb, int kindIndex, int roleIndex) +{ + roleDefinition *rdef = getRole (kcb, kindIndex, roleIndex); + return rdef->enabled; +} + +extern unsigned int countKinds (struct kindControlBlock* kcb) +{ + return kcb->count; +} + +extern unsigned int countRoles (struct kindControlBlock* kcb, int kindIndex) +{ + return kcb->kind [kindIndex].rcb->count; +} + +extern kindDefinition *getKind (struct kindControlBlock* kcb, int kindIndex) +{ + return kcb->kind [kindIndex].def; +} + +extern kindDefinition *getKindForLetter (struct kindControlBlock* kcb, int letter) +{ + unsigned int i; + kindDefinition * kdef; + + for (i = 0; i < countKinds (kcb); ++i) + { + kdef = getKind (kcb, i); + if (kdef->letter == letter) + return kdef; + } + return NULL; +} + +extern kindDefinition *getKindForName (struct kindControlBlock* kcb, const char* name) +{ + unsigned int i; + kindDefinition * kdef; + + for (i = 0; i < countKinds (kcb); ++i) + { + kdef = getKind (kcb, i); + Assert(kdef); + if (kdef->name && (strcmp(kdef->name, name) == 0)) + return kdef; + } + return NULL; +} + +extern roleDefinition* getRole(struct kindControlBlock* kcb, int kindIndex, int roleIndex) +{ + struct roleControlBlock *rcb = kcb->kind[kindIndex].rcb; + return rcb->role [roleIndex].def; +} + +extern roleDefinition* getRoleForName(struct kindControlBlock* kcb, + int kindIndex, const char* name) +{ + unsigned int i; + roleDefinition *rdef; + + for (i = 0; i < countRoles (kcb, kindIndex); ++i) + { + rdef = getRole(kcb, kindIndex, i); + Assert(rdef); + if (rdef->name && (strcmp(rdef->name, name) == 0)) + return rdef; + } + return NULL; +} + +static void linkKinds (langType master, kindDefinition *masterKind, kindDefinition *slaveKind) +{ + kindDefinition *tail; + + slaveKind->master = masterKind; + + tail = slaveKind; + while (tail->slave) + { + tail->enabled = masterKind->enabled; + tail = tail->slave; + } + + tail->slave = masterKind->slave; + masterKind->slave = slaveKind; + + masterKind->syncWith = master; + slaveKind->syncWith = master; +} + +extern void linkKindDependency (struct kindControlBlock *masterKCB, + struct kindControlBlock *slaveKCB) +{ + unsigned int k_slave, k_master; + kindDefinition *kind_slave, *kind_master; + + for (k_slave = 0; k_slave < countKinds (slaveKCB); k_slave++) + { + kind_slave = getKind(slaveKCB, k_slave); + if (kind_slave->syncWith == LANG_AUTO) + { + for (k_master = 0; k_master < countKinds (masterKCB); k_master++) + { + kind_master = getKind(masterKCB, k_master); + if ((kind_slave->letter == kind_master->letter) + && (strcmp (kind_slave->name, kind_master->name) == 0)) + { + linkKinds (masterKCB->owner, kind_master, kind_slave); + break; + } + } + } + } +} + +#ifdef DEBUG +extern bool doesParserUseKind (struct kindControlBlock* kcb, char letter) +{ + unsigned int k; + kindDefinition *kdef; + + for (k = 0; k < countKinds (kcb); k++) + { + kdef = getKind(kcb, k); + if (kdef->letter == letter) + return true; + } + return false; +} +#endif + +extern struct colprintTable * kindColprintTableNew (void) +{ + return colprintTableNew ("L:LANGUAGE", "L:LETTER", "L:NAME", "L:ENABLED", + "L:REFONLY", "L:NROLES", "L:MASTER", + "L:DESCRIPTION", + NULL); +} + +static void kindColprintFillLine (struct colprintLine *line, + const char *langName, + kindDefinition *kdef) +{ + langType lang = getNamedLanguage (langName, 0); + unsigned int count = countLanguageRoles(lang, kdef->id); + colprintLineAppendColumnCString (line, langName); + colprintLineAppendColumnChar (line, kdef->letter); + colprintLineAppendColumnCString (line, kdef->name + ? kdef->name + : "ThisShouldNotBePrintedKindNameMustBeGiven"); + colprintLineAppendColumnBool (line, kdef->enabled); + colprintLineAppendColumnBool (line, kdef->referenceOnly); + colprintLineAppendColumnInt (line, count); + colprintLineAppendColumnCString (line, (kdef->master + || kdef->slave ) ? + getLanguageName (kdef->syncWith): RSV_NONE); + colprintLineAppendColumnCString (line, kdef->description? kdef->description: "NO DESCRIPTION GIVEN"); +} + +extern void kindColprintAddLanguageLines (struct colprintTable *table, + struct kindControlBlock* kcb) +{ + const char *lang = getLanguageName (kcb->owner); + for (unsigned int i = 0; i < countKinds (kcb); i++) + { + kindDefinition *kdef = getKind (kcb, i); + struct colprintLine *line = colprintTableGetNewLine(table); + + kindColprintFillLine (line, lang, kdef); + } +} + +static int kindColprintCompareLines (struct colprintLine *a , struct colprintLine *b) +{ + const char *a_parser = colprintLineGetColumn (a, 0); + const char *b_parser = colprintLineGetColumn (b, 0); + const char *a_letter; + const char *b_letter; + int r; + + r = strcmp (a_parser, b_parser); + if (r != 0) + return r; + + a_letter = colprintLineGetColumn (a, 1); + b_letter = colprintLineGetColumn (b, 1); + r = strcmp (a_letter, b_letter); + if (r != 0) + return r; + + return 0; +} + +extern void kindColprintTablePrint (struct colprintTable *table, bool noparser, + bool withListHeader, bool machinable, FILE *fp) +{ + colprintTableSort (table, kindColprintCompareLines); + colprintTablePrint (table, noparser? 1: 0, withListHeader, machinable, fp); +} + + +extern struct colprintTable * roleColprintTableNew (void) +{ + return colprintTableNew ("L:LANGUAGE", "L:KIND(L/N)", "L:NAME", + "L:ENABLED", "L:DESCRIPTION", NULL); +} + +extern void roleColprintAddRoles (struct colprintTable *table, struct kindControlBlock *kcb, + const char *kindspecs) +{ + const char* lang; + vString *kind_l_and_n; + + lang = getLanguageName (kcb->owner); + kind_l_and_n = vStringNew (); + for (const char *c = kindspecs; *c != '\0'; c++) + { + const char *kname = NULL; + size_t kname_len; + + if (*c == '{') + { + const char *start = c + 1; + const char *end = strchr(c, '}'); + + if (!end) + error (FATAL, "'{' is not closed with '}' in \"%s\"", c); + if (start == end) + error (FATAL, "empty kind name is given in \"%s\"", c); + + kname = start; + kname_len = end - start; + c = end; + } + + for (unsigned int i = 0; i < countKinds (kcb); i++) + { + const kindDefinition *k = getKind (kcb, i); + + if ((kname + && strlen (k->name) == kname_len + && strncmp (k->name, kname, kname_len) == 0) + || (!kname && *c == k->letter) + || (!kname && *c == KIND_WILDCARD)) + { + unsigned int nRoles = countRoles(kcb, i); + for (unsigned int j = 0; j < nRoles; j++) + { + const roleDefinition *r = getRole (kcb, i, j); + struct colprintLine *line = colprintTableGetNewLine(table); + + colprintLineAppendColumnCString (line, lang); + + vStringPut (kind_l_and_n, k->letter); + vStringPut (kind_l_and_n, '/'); + vStringCatS (kind_l_and_n, k->name); + colprintLineAppendColumnVString (line, kind_l_and_n); + vStringClear (kind_l_and_n); + + colprintLineAppendColumnCString (line, r->name); + colprintLineAppendColumnCString (line, + r->enabled ? "on" : "off"); + colprintLineAppendColumnCString (line, r->description); + } + if (! (!kname && *c == KIND_WILDCARD)) + break; + } + } + } + vStringDelete (kind_l_and_n); +#if 0 + if ((i == countKinds (kcb)) && (*c != KIND_WILDCARD) && (!allowMissingKind)) + error (FATAL, "No such letter kind in %s: %c\n", lang->name, *c); +#endif +} + +static int roleColprintCompareLines(struct colprintLine *a, struct colprintLine *b) +{ + int r; + + const char *a_parser, *b_parser; + a_parser = colprintLineGetColumn (a, 0); + b_parser = colprintLineGetColumn (b, 0); + + r = strcmp(a_parser, b_parser); + if (r != 0) + return r; + + const char *a_kindln, *b_kindln; + a_kindln = colprintLineGetColumn (a, 1); + b_kindln = colprintLineGetColumn (b, 1); + + r = strcmp(a_kindln, b_kindln); + if (r != 0) + return r; + + const char *a_role, *b_role; + a_role = colprintLineGetColumn (a, 2); + b_role = colprintLineGetColumn (b, 2); + + return strcmp(a_role, b_role); +} + +extern void roleColprintTablePrint (struct colprintTable *table, bool noparser, + bool withListHeader, bool machinable, FILE *fp) +{ + colprintTableSort (table, roleColprintCompareLines); + colprintTablePrint (table, noparser? 1: 0, withListHeader, machinable, fp); +} diff --git a/ctags/main/kind.h b/ctags/main/kind.h index 0bd56599c8..a686824127 100644 --- a/ctags/main/kind.h +++ b/ctags/main/kind.h @@ -10,17 +10,19 @@ #include "general.h" #include "types.h" -#include "routines.h" /* for STRINGIFY */ +#include "routines.h" #include "vstring.h" -typedef struct sRoleDefinition { +struct sRoleDefinition { bool enabled; - const char* name; /* role name */ - const char* description; /* displayed in --help output */ -} roleDefinition; + char* name; /* role name */ + char* description; /* displayed in --help output */ -extern void printRole (const roleDefinition* const role); /* for --help */ -extern const char *renderRole (const roleDefinition* const role, vString* b); + int id; +}; + +typedef void (* freeRoleDefFunc) (roleDefinition *); +extern const char *renderRole (const roleDefinition* const def, vString* b); /* * Predefined kinds @@ -40,11 +42,6 @@ extern const char *renderRole (const roleDefinition* const role, vString* b); #define KIND_FILE_DEFAULT 'F' #define KIND_FILE_DEFAULT_LONG "file" -#define KIND_FILE_ALT '!' - -#define KIND_GENERIC_REFERENCE '@' -#define KIND_GENERIC_REFERENCE_DEFAULT_LONG "reference" - #define KIND_WILDCARD_INDEX -3 #define KIND_WILDCARD '*' @@ -56,18 +53,21 @@ typedef struct sScopeSeparator { struct sKindDefinition { bool enabled; /* are tags for kind enabled? */ char letter; /* kind letter */ - const char* name; /* kind name */ - const char* description; /* displayed in --help output */ + char* name; /* kind name */ + char* description; /* displayed in --help output */ bool referenceOnly; int nRoles; /* The number of role elements. */ roleDefinition *roles; scopeSeparator *separators; unsigned int separatorCount; + int id; + + /* TODO:Following fields should be moved to kindObject. */ /* Usage of `syncWith' field is a bit tricky. If `LANG_AUTO' is specified to `syncWith' field of a kind - (target kind), the main part of ctags updtes the field with + (target kind), the main part of ctags updates the field with the id of a parser (master parser) when initializing parsers. It also updates `slave' and `master' fields. @@ -81,17 +81,49 @@ struct sKindDefinition { #define ATTACH_ROLES(RS) .nRoles = ARRAY_SIZE(RS), .roles = RS #define ATTACH_SEPARATORS(S) .separators = S, .separatorCount = ARRAY_SIZE(S) -/* The value of `tabSeparated' is meaningfull only when `allKindFields' is true. */ -extern void printKind (const kindDefinition* const kind, bool allKindFields, bool indent, - bool tabSeparated); -extern void printKindListHeader (bool indent, bool tabSeparated); +/* for the obsolete --list-kinds option */ +extern void printKind (const kindDefinition* const kind, bool indent); + extern const char *scopeSeparatorFor (langType lang, int kindIndex, int parentKindIndex); extern void enableKind (kindDefinition *kind, bool enable); -#define PR_KIND_STR(X) PR_KIND_WIDTH_##X -#define PR_KIND_FMT(X,T) "%-" STRINGIFY(PR_KIND_STR(X)) STRINGIFY(T) - -#define PR_KIND_WIDTH_LANG 15 +struct kindControlBlock; +typedef void (* freeKindDefFunc) (kindDefinition *); +extern struct kindControlBlock* allocKindControlBlock (parserDefinition *parser); +extern void freeKindControlBlock (struct kindControlBlock* kcb); +extern int defineKind (struct kindControlBlock* kcb, kindDefinition *def, + freeKindDefFunc freeKindDef); +extern int defineRole (struct kindControlBlock* kcb, int kindIndex, + roleDefinition *def, freeRoleDefFunc freeRoleDef); +extern bool isRoleEnabled (struct kindControlBlock* kcb, int kindIndex, int roleIndex); + +extern unsigned int countKinds (struct kindControlBlock* kcb); +extern unsigned int countRoles (struct kindControlBlock* kcb, int kindIndex); +extern kindDefinition *getKind (struct kindControlBlock* kcb, int kindIndex); +extern kindDefinition *getKindForLetter (struct kindControlBlock* kcb, int letter); +extern kindDefinition *getKindForName (struct kindControlBlock* kcb, const char* name); +extern roleDefinition* getRole(struct kindControlBlock* kcb, int kindIndex, int roleIndex); +extern roleDefinition* getRoleForName(struct kindControlBlock* kcb, int kindIndex, const char* name); +extern void linkKindDependency (struct kindControlBlock *masterKCB, + struct kindControlBlock *slaveKCB); + +/* for --list-kinds-full option. LANGUAGE must be initialized. */ +extern struct colprintTable * kindColprintTableNew (void); +extern void kindColprintAddLanguageLines (struct colprintTable *table, + struct kindControlBlock* kcb); +extern void kindColprintTablePrint (struct colprintTable *table, bool noparser, + bool withListHeader, bool machinable, FILE *fp); + +extern struct colprintTable * roleColprintTableNew (void); +extern void roleColprintAddRoles (struct colprintTable *table, + struct kindControlBlock* kcb, + const char *kindspecs); +extern void roleColprintTablePrint (struct colprintTable *table, bool noparser, + bool withListHeader, bool machinable, FILE *fp); + +#ifdef DEBUG +extern bool doesParserUseKind (struct kindControlBlock* kcb, char letter); +#endif #endif /* CTAGS_MAIN_KIND_H */ diff --git a/ctags/main/lcpp.c b/ctags/main/lcpp.c index 956003e973..e77312cdac 100644 --- a/ctags/main/lcpp.c +++ b/ctags/main/lcpp.c @@ -19,7 +19,7 @@ #include "entry.h" #include "lcpp.h" #include "kind.h" -#include "options.h" +#include "options_p.h" #include "read.h" #include "vstring.h" #include "parse.h" @@ -305,8 +305,7 @@ static bool pushConditional (const bool firstBranchChosen) ifdef->singleBranch = Cpp.resolveRequired; ifdef->branchChosen = firstBranchChosen; ifdef->ignoring = (bool) (ignoreAllBranches || ( - ! firstBranchChosen && ! BraceFormat && - (ifdef->singleBranch || !Option.if0))); + ! firstBranchChosen && ! BraceFormat)); ignoreBranch = ifdef->ignoring; } return ignoreBranch; diff --git a/ctags/main/lregex.c b/ctags/main/lregex.c index 6686beec13..55fe25bf56 100644 --- a/ctags/main/lregex.c +++ b/ctags/main/lregex.c @@ -23,20 +23,22 @@ #ifdef HAVE_SYS_TYPES_H # include /* declare off_t (not known to regex.h on FreeBSD) */ #endif -#include +#include #include "debug.h" +#include "colprint_p.h" #include "entry.h" -#include "flags.h" +#include "flags_p.h" #include "htable.h" #include "kind.h" #include "options.h" -#include "parse.h" +#include "parse_p.h" #include "read.h" #include "routines.h" +#include "routines_p.h" +#include "trashbox.h" static bool regexAvailable = false; -static unsigned long currentScope = CORK_NIL; /* * MACROS @@ -45,18 +47,22 @@ static unsigned long currentScope = CORK_NIL; /* Back-references \0 through \9 */ #define BACK_REFERENCE_COUNT 10 +/* The max depth of taction=enter/leave stack */ +#define MTABLE_STACK_MAX_DEPTH 64 + +/* How many times ctags allows a mtable parser + stays at the same input position across table switching. + + The value is derived from MTABLE_STACK_MAX_DEPTH. + No deep meaning is in that. It just for simplifying + Tmain cases. */ +#define MTABLE_MOTIONLESS_MAX (MTABLE_STACK_MAX_DEPTH + 1) -#define REGEX_NAME "Regex" /* * DATA DECLARATIONS */ -union cptr { - char c; - void *p; -}; - enum pType { PTRN_TAG, PTRN_CALLBACK }; enum scopeAction { @@ -67,15 +73,46 @@ enum scopeAction { SCOPE_PLACEHOLDER = 1UL << 4, }; +enum tableAction { + TACTION_NOP, + TACTION_ENTER, /* {tenter=N} */ + TACTION_LEAVE, /* {tleave} */ + TACTION_JUMP, /* {tjump=N} */ + TACTION_RESET, /* {treset=N} */ + TACTION_QUIT, /* {tquit} */ +}; + +struct fieldPattern { + fieldType ftype; + const char *template; +}; + +struct mGroupSpec { +#define NO_MULTILINE -1 + int forLineNumberDetermination; + int forNextScanning; + /* true => start, false => end */ + bool nextFromStart; +}; + +struct mTableActionSpec { + enum tableAction action; + struct regexTable *table; + + /* used when action == TACTION_ENTER */ + struct regexTable *continuation_table; +}; + typedef struct { - GRegex *pattern; + regex_t *pattern; enum pType type; bool exclusive; bool accept_empty_name; union { struct { + int kindIndex; + roleBitsType roleBits; char *name_pattern; - kindDefinition *kind; } tag; struct { regexCallback function; @@ -84,81 +121,169 @@ typedef struct { } u; unsigned int scopeActions; bool *disabled; + + enum regexParserType regptype; + struct mGroupSpec mgroup; + struct mTableActionSpec taction; + + int xtagType; + ptrArray *fieldPatterns; + + char *pattern_string; + + struct { + errorSelection selection; + char *message_string; + } message; + + int refcount; } regexPattern; typedef struct { - regexPattern *patterns; - unsigned int count; - hashTable *kinds; -} patternSet; + /* the pattern can be shared among entries using a refcount */ + regexPattern *pattern; + + /* but the statistics are per-table-entry */ + struct { + unsigned int match; + unsigned int unmatch; + } statistics; +} regexTableEntry; + + +#define TABLE_INDEX_UNUSED -1 +struct regexTable { + char *name; + ptrArray *entries; +}; + +struct lregexControlBlock { + unsigned long currentScope; + ptrArray *entries [2]; + + ptrArray *tables; + ptrArray *tstack; + + langType owner; +}; /* * 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 int getTableIndexForName (const struct lregexControlBlock *const lcb, const char *name); +static void deletePattern (regexPattern *p); + +static void deleteTable (void *ptrn) +{ + struct regexTable *t = ptrn; + + ptrArrayDelete (t->entries); + eFree (t->name); + eFree (t); +} +static void deleteTableEntry (void *ptrn) +{ + regexTableEntry *e = ptrn; + Assert (e && e->pattern); + deletePattern (e->pattern); + eFree (e); +} -static void clearPatternSet (const langType language) +static void deletePattern (regexPattern *p) { - if (language <= SetUpper) + p->refcount--; + + if (p->refcount > 0) + return; + + regfree (p->pattern); + eFree (p->pattern); + p->pattern = NULL; + + if (p->type == PTRN_TAG) { - patternSet* const set = Sets + language; - unsigned int i; - for (i = 0 ; i < set->count ; ++i) - { - regexPattern *p = &set->patterns [i]; - g_regex_unref(p->pattern); - p->pattern = NULL; + eFree (p->u.tag.name_pattern); + p->u.tag.name_pattern = NULL; + } - if (p->type == PTRN_TAG) - { - eFree (p->u.tag.name_pattern); - p->u.tag.name_pattern = NULL; - p->u.tag.kind = NULL; - } - } - if (set->patterns != NULL) - eFree (set->patterns); - set->patterns = NULL; - set->count = 0; - hashTableDelete (set->kinds); - set->kinds = NULL; + if (p->fieldPatterns) + { + ptrArrayDelete (p->fieldPatterns); + p->fieldPatterns = NULL; } + + eFree (p->pattern_string); + + if (p->message.message_string) + eFree (p->message.message_string); + + eFree (p); +} + +static void clearPatternSet (struct lregexControlBlock *lcb) +{ + ptrArrayClear (lcb->entries [REG_PARSER_SINGLE_LINE]); + ptrArrayClear (lcb->entries [REG_PARSER_MULTI_LINE]); + ptrArrayClear (lcb->tables); +} + +extern struct lregexControlBlock* allocLregexControlBlock (parserDefinition *parser) +{ + struct lregexControlBlock *lcb = xCalloc (1, struct lregexControlBlock); + + lcb->entries[REG_PARSER_SINGLE_LINE] = ptrArrayNew(deleteTableEntry); + lcb->entries[REG_PARSER_MULTI_LINE] = ptrArrayNew(deleteTableEntry); + lcb->tables = ptrArrayNew(deleteTable); + lcb->tstack = ptrArrayNew(NULL); + lcb->owner = parser->id; + + return lcb; +} + +extern void freeLregexControlBlock (struct lregexControlBlock* lcb) +{ + clearPatternSet (lcb); + + ptrArrayDelete (lcb->entries [REG_PARSER_SINGLE_LINE]); + lcb->entries [REG_PARSER_SINGLE_LINE] = NULL; + ptrArrayDelete (lcb->entries [REG_PARSER_MULTI_LINE]); + lcb->entries [REG_PARSER_MULTI_LINE] = NULL; + + ptrArrayDelete (lcb->tables); + lcb->tables = NULL; + + ptrArrayDelete (lcb->tstack); + lcb->tstack = NULL; + + eFree (lcb); } /* * Regex pseudo-parser */ -static int makeRegexTag ( - const vString* const name, const kindDefinition* const kind, int scopeIndex, int placeholder) +static void initRegexTag (tagEntryInfo *e, + const vString* const name, int kindIndex, int roleIndex, int scopeIndex, int placeholder, + unsigned long line, MIOPos *pos, int xtag_type) { - Assert (kind != NULL); - /* TODO: Disable regex tag generation for now. We would need to pass kindIndex - * to initTagEntry() but currently we don't have kinds indexable because - * they are stored in hash table. Consider whether we want to support - * regex parsers at all in Geany. */ -#if 0 - if (kind->enabled) + Assert (name != NULL && ((vStringLength (name) > 0) || placeholder)); + initRefTagEntry (e, vStringValue (name), kindIndex, roleIndex); + e->extensionFields.scopeIndex = scopeIndex; + e->placeholder = !!placeholder; + if (line) { - tagEntryInfo e; - Assert (name != NULL && ((vStringLength (name) > 0) || placeholder)); - initTagEntry (&e, vStringValue (name), kind); - e.extensionFields.scopeIndex = scopeIndex; - e.placeholder = !!placeholder; - return makeTagEntry (&e); + e->lineNumber = line; + e->filePosition = *pos; } - else -#endif - return CORK_NIL; + + if (xtag_type != XTAG_UNKNOWN) + markTagExtraBit (e, xtag_type); } /* @@ -169,10 +294,11 @@ static int makeRegexTag ( * 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. + * Turns "\n" into a Newline character if MULTILINE is true. * Returns pointer to terminating separator. Works in place. Null * terminates name string. */ -static char* scanSeparators (char* name) +static char* scanSeparators (char* name, enum regexParserType regptype) { char sep = name [0]; char *copyto = name; @@ -186,6 +312,10 @@ static char* scanSeparators (char* name) *copyto++ = sep; else if (*name == 't') *copyto++ = '\t'; + else if ((regptype == REG_PARSER_MULTI_LINE + || (regptype == REG_PARSER_MULTI_TABLE)) + && *name == 'n') + *copyto++ = '\n'; else { /* Something else is quoted, so preserve the quote. */ @@ -217,31 +347,38 @@ static char* scanSeparators (char* name) * correct format, a false value is returned. */ static bool parseTagRegex ( + enum regexParserType regptype, char* const regexp, char** const name, char** const kinds, char** const flags) { bool result = false; const int separator = (unsigned char) regexp [0]; - *name = scanSeparators (regexp); + *name = scanSeparators (regexp, regptype); if (*regexp == '\0') error (WARNING, "empty regexp"); else if (**name != separator) error (WARNING, "%s: incomplete regexp", regexp); else { - char* const third = scanSeparators (*name); + char* const third = scanSeparators (*name, false); if (**name != '\0' && (*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); + /* + * first----------V third------------V + * --regex-=/regexp/replacement/[kind-spec/][flags] + * second----------------^ fourth---------------^ + */ + + char* const fourth = scanSeparators (third, false); if (*fourth == separator) { *kinds = third; - scanSeparators (fourth); + scanSeparators (fourth, false); *flags = fourth; } else @@ -305,270 +442,662 @@ static flagDefinition scopePtrnFlagDef[] = { NULL, "don't put this tag to tags file."}, }; -static kindDefinition *kindNew () +static kindDefinition *kindNew (char letter, const char *name, const char *description) { - kindDefinition *kind = xCalloc (1, kindDefinition); - kind->letter = '\0'; - kind->name = NULL; - kind->description = NULL; - kind->enabled = false; - kind->referenceOnly = false; - kind->nRoles = 0; - kind->roles = NULL; - return kind; + kindDefinition *kdef = xCalloc (1, kindDefinition); + kdef->letter = letter; + kdef->name = eStrdup (name? name: KIND_REGEX_DEFAULT_LONG); + kdef->description = eStrdup(description? description: kdef->name); + kdef->enabled = true; + return kdef; } -static void kindFree (void *data) +static void kindFree (kindDefinition *kind) { - kindDefinition *kind = data; kind->letter = '\0'; - if (kind->name) - { - eFree ((void *)kind->name); - kind->name = NULL; - } - if (kind->description) - { - eFree ((void *)kind->description); - kind->description = NULL; - } + eFree ((void *)kind->name); + kind->name = NULL; + eFree ((void *)kind->description); + kind->description = NULL; eFree (kind); } -static regexPattern* addCompiledTagCommon (const langType language, - GRegex* const pattern, - char kind_letter) +static void initMgroup(struct mGroupSpec *mgroup) { - patternSet* set; - regexPattern *ptrn; - kindDefinition *kind = NULL; + mgroup->forLineNumberDetermination = NO_MULTILINE; + mgroup->forNextScanning = NO_MULTILINE; + mgroup->nextFromStart = false; +} - 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; - Sets [i].kinds = hashTableNew (11, - hashPtrhash, - hashPtreq, - NULL, - kindFree); - } - SetUpper = language; - } - set = Sets + language; - set->patterns = xRealloc (set->patterns, (set->count + 1), regexPattern); +static void initTaction(struct mTableActionSpec *taction) +{ + taction->action = TACTION_NOP; + taction->table = NULL; +} - if (kind_letter) - { - union cptr c = { .p = NULL }; +static regexPattern * refPattern (regexPattern * ptrn) +{ + ptrn->refcount++; + return ptrn; +} + +static regexPattern * newPattern (regex_t* const pattern, + enum regexParserType regptype) +{ + regexPattern *ptrn = xCalloc(1, regexPattern); - c.c = kind_letter; - kind = hashTableGetItem (set->kinds, c.p); - if (!kind) - { - kind = kindNew (); - hashTablePutItem (set->kinds, c.p, (void *)kind); - } - } - ptrn = &set->patterns [set->count]; - memset (ptrn, 0, sizeof (*ptrn)); ptrn->pattern = pattern; ptrn->exclusive = false; ptrn->accept_empty_name = false; - if (kind_letter) - ptrn->u.tag.kind = kind; - set->count += 1; - useRegexMethod(language); + ptrn->regptype = regptype; + ptrn->xtagType = XTAG_UNKNOWN; + + if (regptype == REG_PARSER_MULTI_LINE) + initMgroup(&ptrn->mgroup); + if (regptype == REG_PARSER_MULTI_TABLE) + initTaction(&ptrn->taction); + + ptrn->u.tag.roleBits = 0; + ptrn->refcount = 1; return ptrn; } -static regexPattern *addCompiledTagPattern (const langType language, GRegex* const pattern, - const char* const name, char kind, const char* kindName, - char *const description, const char* flags, - bool *disabled) +static regexTableEntry * newRefPatternEntry (regexTableEntry * other) { - regexPattern * ptrn; - bool exclusive = false; - unsigned long scopeActions = 0UL; + regexTableEntry *entry = xCalloc (1, regexTableEntry); - flagsEval (flags, prePtrnFlagDef, ARRAY_SIZE(prePtrnFlagDef), &exclusive); - flagsEval (flags, scopePtrnFlagDef, ARRAY_SIZE(scopePtrnFlagDef), &scopeActions); - if (*name == '\0' && exclusive && kind == KIND_REGEX_DEFAULT) + Assert (other && other->pattern); + + entry->pattern = refPattern(other->pattern); + return entry; +} + +static regexTableEntry * newEntry (regex_t* const pattern, + enum regexParserType regptype) +{ + regexTableEntry *entry = xCalloc (1, regexTableEntry); + entry->pattern = newPattern (pattern, regptype); + return entry; +} + +static regexPattern* addCompiledTagCommon (struct lregexControlBlock *lcb, + int table_index, + regex_t* const pattern, + enum regexParserType regptype) +{ + regexTableEntry *entry = newEntry (pattern, regptype); + + if (regptype == REG_PARSER_MULTI_TABLE) { - kind = KIND_GHOST; - kindName = KIND_GHOST_LONG; + struct regexTable *table = ptrArrayItem (lcb->tables, table_index); + Assert(table); + + ptrArrayAdd (table->entries, entry); } - ptrn = addCompiledTagCommon(language, pattern, kind); - ptrn->type = PTRN_TAG; - ptrn->u.tag.name_pattern = eStrdup (name); - ptrn->exclusive = exclusive; - ptrn->scopeActions = scopeActions; - ptrn->disabled = disabled; - if (ptrn->u.tag.kind->letter == '\0') + else + ptrArrayAdd (lcb->entries[regptype], entry); + + useRegexMethod(lcb->owner); + + return entry->pattern; +} + +static void pre_ptrn_flag_mgroup_long (const char* const s, const char* const v, void* data) +{ + struct mGroupSpec *mgroup = data; + if (!v) { - /* This is a newly registered kind. */ - ptrn->u.tag.kind->letter = kind; - ptrn->u.tag.kind->enabled = true; - ptrn->u.tag.kind->name = kindName? eStrdup (kindName): NULL; - ptrn->u.tag.kind->description = description? eStrdup (description): NULL; + error (WARNING, "no value is given for: %s", s); + return; + } + if (!strToInt (v, 10, &mgroup->forLineNumberDetermination)) + { + error (WARNING, "wrong %s specification: %s", s, v); + mgroup->forLineNumberDetermination = NO_MULTILINE; } - else if (ptrn->u.tag.kind->name && kindName && strcmp(ptrn->u.tag.kind->name, kindName)) + else if (mgroup->forLineNumberDetermination < 0 + || mgroup->forLineNumberDetermination >= BACK_REFERENCE_COUNT) { - /* When using a same kind letter for multiple regex patterns, the name of kind - should be the same. */ - error (WARNING, "Don't reuse the kind letter `%c' in a language %s (old: \"%s\", new: \"%s\")", - ptrn->u.tag.kind->letter, getLanguageName (language), - ptrn->u.tag.kind->name, kindName); + error (WARNING, "out of range(0 ~ %d) %s specification: %s", + (BACK_REFERENCE_COUNT - 1), + s, v); + mgroup->forLineNumberDetermination = NO_MULTILINE; } - return ptrn; + if (mgroup->forLineNumberDetermination != NO_MULTILINE + && mgroup->forNextScanning == NO_MULTILINE) + { + mgroup->forNextScanning = 0; + mgroup->nextFromStart = false; + } } -static void addCompiledCallbackPattern (const langType language, GRegex* const pattern, - const regexCallback callback, const char* flags, - bool *disabled, - void *userData) +static void pre_ptrn_flag_advanceTo_long (const char* const s, const char* const v, void* data) { - regexPattern * ptrn; - bool exclusive = false; - flagsEval (flags, prePtrnFlagDef, ARRAY_SIZE(prePtrnFlagDef), &exclusive); - ptrn = addCompiledTagCommon(language, pattern, '\0'); - ptrn->type = PTRN_CALLBACK; - ptrn->u.callback.function = callback; - ptrn->u.callback.userData = userData; - ptrn->exclusive = exclusive; - ptrn->disabled = disabled; -} + struct mGroupSpec *mgroup = data; + char *vdup; + char *tmp; -static void regex_flag_basic_short (char c CTAGS_ATTR_UNUSED, void* data) -{ - error(WARNING, "CTags 'b' flag not supported by Geany!"); + if (!v) + { + error (WARNING, "no value is given for: %s", s); + return; + } + + vdup = eStrdup (v); + + mgroup->nextFromStart = false; + if ((tmp = strstr(vdup, "start"))) + { + mgroup->nextFromStart = true; + *tmp = '\0'; + } + else if ((tmp = strstr(vdup, "end"))) + *tmp = '\0'; + + if (!strToInt (vdup, 10, &(mgroup->forNextScanning))) + { + error (WARNING, "wrong %s specification: %s", s, vdup); + mgroup->nextFromStart = false; + } + else if (mgroup->forNextScanning < 0 || mgroup->forNextScanning >= BACK_REFERENCE_COUNT) + { + error (WARNING, "out of range(0 ~ %d) %s specification: %s", + (BACK_REFERENCE_COUNT - 1), s, vdup); + mgroup->nextFromStart = false; + } + + eFree (vdup); } -static void regex_flag_basic_long (const char* const s CTAGS_ATTR_UNUSED, const char* const unused CTAGS_ATTR_UNUSED, void* data) +static flagDefinition multilinePtrnFlagDef[] = { + { '\0', "mgroup", NULL, pre_ptrn_flag_mgroup_long , + "N", "a group in pattern determining the line number of tag"}, + { '\0', "_advanceTo", NULL, pre_ptrn_flag_advanceTo_long, + "N[start|end]", "a group in pattern from where the next scan starts [0end]"}, +}; + +static bool hasMessage(const regexPattern *const ptrn) { - regex_flag_basic_short ('b', data); + return (ptrn->message.selection > 0 && ptrn->message.message_string); } -static void regex_flag_extend_short (char c CTAGS_ATTR_UNUSED, void* data) +struct commonFlagData { + const langType owner; + const struct lregexControlBlock *const lcb; + regexPattern *ptrn; +}; + +static void common_flag_msg_long (const char* const s, const char* const v, void* data) { + struct commonFlagData *cdata = data; + regexPattern *ptrn = cdata->ptrn; + + Assert (ptrn); + + if (hasMessage(ptrn)) + { + error (WARNING, "only one message flag may be given per regex (already set to '%s')", + ptrn->message.message_string); + return; + } + + if (strcmp (s, "fatal") == 0) + { + ptrn->message.selection = FATAL; + } + else if (strcmp (s, "warning") == 0) + { + ptrn->message.selection = WARNING; + } + + Assert (ptrn->message.selection != 0); + + if (!v || !*v) + { + error (WARNING, "no message value is given for {%s}", s); + return; + } + + const char* begin = v; + const char* end = v + strlen (v); + --end; + + if (*begin != '"' || *end != '"' || begin == end) + { + error (WARNING, "argument for {%s} must be in double-quotes", s); + return; + } + + ++begin; + + if (begin < end) + ptrn->message.message_string = eStrndup (begin, end - begin); } -static void regex_flag_extend_long (const char* const c CTAGS_ATTR_UNUSED, const char* const unused CTAGS_ATTR_UNUSED, void* data) +static void common_flag_extra_long (const char* const s, const char* const v, void* data) { - regex_flag_extend_short('e', data); + struct commonFlagData * cdata = data; + + Assert (cdata->ptrn); + + if (!v) + { + error (WARNING, "no value is given for: %s", s); + return; + } + + cdata->ptrn->xtagType = getXtagTypeForNameAndLanguage (v, cdata->owner); + if (cdata->ptrn->xtagType == XTAG_UNKNOWN) + error (WARNING, "no such extra \"%s\" in %s", v, getLanguageName(cdata->owner)); } -static void regex_flag_icase_short (char c CTAGS_ATTR_UNUSED, void* data) + +static struct fieldPattern * fieldPatternNew (fieldType ftype, const char *template) { - int* cflags = data; - *cflags |= G_REGEX_CASELESS; + struct fieldPattern *fp; + + fp = xMalloc(1, struct fieldPattern); + fp->ftype = ftype; + fp->template = eStrdup(template); + + return fp; } -static void regex_flag_icase_long (const char* s CTAGS_ATTR_UNUSED, const char* const unused CTAGS_ATTR_UNUSED, void* data) +static void fieldPatternDelete (struct fieldPattern *fp) { - regex_flag_icase_short ('i', data); + eFree ((void *)fp->template); + eFree (fp); } - -static flagDefinition regexFlagDefs[] = { - { 'b', "basic", regex_flag_basic_short, regex_flag_basic_long, - NULL, "interpreted as a Posix basic regular expression."}, - { 'e', "extend", regex_flag_extend_short, regex_flag_extend_long, - NULL, "interpreted as a Posix extended regular expression (default)"}, - { 'i', "icase", regex_flag_icase_short, regex_flag_icase_long, - NULL, "applied in a case-insensitive manner"}, -}; - -static GRegex* compileRegex (const char* const regexp, const char* const flags) +static void common_flag_field_long (const char* const s, const char* const v, void* data) { - int cflags = G_REGEX_MULTILINE; - GRegex *result = NULL; - GError *err = NULL; + struct commonFlagData * cdata = data; + regexPattern *ptrn = cdata->ptrn; - flagsEval (flags, - regexFlagDefs, - ARRAY_SIZE(regexFlagDefs), - &cflags); + Assert (ptrn); - result = g_regex_new(regexp, cflags, 0, &err); - if (err) + struct fieldPattern *fp; + fieldType ftype; + char *fname; + const char* template; + char *tmp; + + if (!v) { - error (WARNING, "regcomp %s: %s", regexp, err->message); - g_error_free(err); + error (WARNING, "no value is given for: %s", s); + return; } - return result; -} + tmp = strchr (v, ':'); + if (tmp == NULL || tmp == v) + { + error (WARNING, "no field name is given for: %s", s); + return; + } -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') + fname = eStrndup (v, tmp - v); + ftype = getFieldTypeForNameAndLanguage (fname, cdata->owner); + if (ftype == FIELD_UNKNOWN) { - *kind = KIND_REGEX_DEFAULT; - *kindName = eStrdup (KIND_REGEX_DEFAULT_LONG); + error (WARNING, "no such field \"%s\" in %s", fname, getLanguageName(cdata->owner)); + eFree (fname); + return; } - else if (kinds [0] != '\0') + + if (ptrn->fieldPatterns) { - const char* k = kinds; - if (k [0] != ',' && (k [1] == ',' || k [1] == '\0')) - *kind = *k++; - else - *kind = KIND_REGEX_DEFAULT; - if (*k == ',') - ++k; - if (k [0] == '\0') - *kindName = eStrdup (KIND_REGEX_DEFAULT_LONG); - else + for (unsigned int i = 0; i < ptrArrayCount(ptrn->fieldPatterns); i++) { - const char *const comma = strchr (k, ','); - if (comma == NULL) - *kindName = eStrdup (k); - else + fp = ptrArrayItem(ptrn->fieldPatterns, i); + if (fp->ftype == ftype) { - *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); + error (WARNING, "duplicated field specification \"%s\" in %s", fname, getLanguageName(cdata->owner)); + eFree (fname); + return; } } } + eFree (fname); + + template = tmp + 1; + fp = fieldPatternNew (ftype, template); + + if (ptrn->fieldPatterns == NULL) + ptrn->fieldPatterns = ptrArrayNew((ptrArrayDeleteFunc)fieldPatternDelete); + ptrArrayAdd(ptrn->fieldPatterns, fp); } -static void processLanguageRegex (const langType language, - const char* const parameter) +static void common_flag_role_long (const char* const s, const char* const v, void* data) { - 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; - MIO* const mio = mio_new_file (regexfile, "r"); - if (mio == NULL) - error (WARNING | PERROR, "%s", regexfile); + struct commonFlagData * cdata = data; + regexPattern *ptrn = cdata->ptrn; + roleDefinition * role; + + Assert (ptrn); + + if (!v) + { + error (WARNING, "no value is given for: %s", s); + return; + } + + role = getLanguageRoleForName(cdata->owner, + ptrn->u.tag.kindIndex, v); + if (!role) + { + error (WARNING, "no such role: %s", v); + return; + } + + ptrn->u.tag.roleBits |= makeRoleBit(role->id); +} + +static flagDefinition commonSpecFlagDef[] = { + { '\0', "fatal", NULL, common_flag_msg_long , + "\"MESSAGE\"", "print the given MESSAGE and exit"}, + { '\0', "warning", NULL, common_flag_msg_long , + "\"MESSAGE\"", "print the given MESSAGE at WARNING level"}, +#define EXPERIMENTAL "_" + { '\0', EXPERIMENTAL "extra", NULL, common_flag_extra_long , + "EXTRA", "record the tag only when the extra is enabled"}, + { '\0', EXPERIMENTAL "field", NULL, common_flag_field_long , + "FIELD:VALUE", "record the matched string(VALUE) to parser own FIELD of the tag"}, + { '\0', EXPERIMENTAL "role", NULL, common_flag_role_long, + "ROLE", "set the given ROLE to the roles field"}, +}; + + +static void pre_ptrn_flag_mtable_long (const char* const s, const char* const v, void* data) +{ + struct commonFlagData * cdata = data; + regexPattern *ptrn = cdata->ptrn; + struct mTableActionSpec *taction; + bool taking_table = true; + + Assert (ptrn); + Assert (cdata->lcb); + + taction = &ptrn->taction; + + if (strcmp (s, "tenter") == 0) + taction->action = TACTION_ENTER; + else if (strcmp (s, "tleave") == 0) + { + taction->action = TACTION_LEAVE; + taking_table = false; + } + else if (strcmp (s, "tjump") == 0) + taction->action = TACTION_JUMP; + else if (strcmp (s, "treset") == 0) + taction->action = TACTION_RESET; + else if (strcmp (s, "tquit") == 0) + { + taction->action = TACTION_QUIT; + taking_table = false; + } + + if (taking_table) + { + int t; + char *continuation = NULL; + + + if (!v || (!*v)) + error (FATAL, "no table is given for table action: %s", s); + + if (taction->action == TACTION_ENTER + && (continuation = strchr (v, ','))) + { + char *tableEnterTo; + + tableEnterTo = eStrndup (v, continuation - v); + t = getTableIndexForName (cdata->lcb, tableEnterTo); + if (t < 0) + error (FATAL, "table is not defined: %s", tableEnterTo); + taction->table = ptrArrayItem (cdata->lcb->tables, t); + eFree (tableEnterTo); + + if (!*(continuation + 1)) + error (FATAL, "no continuation table is given for: %s", v); + + int t_cont = getTableIndexForName (cdata->lcb, continuation + 1); + if (t_cont < 0) + error (FATAL, "table for continuation is not defined: %s", continuation + 1); + taction->continuation_table = ptrArrayItem (cdata->lcb->tables, t_cont); + } else { - vString* const regex = vStringNew (); - while (readLineRaw (regex, mio)) - addLanguageRegex (language, vStringValue (regex)); - mio_free (mio); - vStringDelete (regex); + t = getTableIndexForName (cdata->lcb, v); + if (t < 0) + error (FATAL, "table is not defined: %s", v); + taction->table = ptrArrayItem (cdata->lcb->tables, t); + taction->continuation_table = NULL; + } + } +} + +static flagDefinition multitablePtrnFlagDef[] = { + { '\0', "tenter", NULL, pre_ptrn_flag_mtable_long , + "TABLE[,CONT]", "enter to given regext table (with specifying continuation)"}, + { '\0', "tleave", NULL, pre_ptrn_flag_mtable_long , + NULL, "leave from the current regext table"}, + { '\0', "tjump", NULL, pre_ptrn_flag_mtable_long , + "TABLE", "jump to another regext table(don't push the current table to state stack)"}, + { '\0', "treset", NULL, pre_ptrn_flag_mtable_long , + "TABLE", "clear the state stack and jump to given regex table"}, + { '\0', "tquit", NULL, pre_ptrn_flag_mtable_long , + NULL, "stop the parsing with this parser"}, +}; + + +static void setKind(regexPattern * ptrn, const langType owner, + const char kindLetter, const char* kindName, + const char *const description) +{ + Assert (ptrn); + Assert (ptrn->u.tag.name_pattern); + + if (*ptrn->u.tag.name_pattern == '\0' && + ptrn->exclusive && + kindLetter == KIND_REGEX_DEFAULT) + { + ptrn->u.tag.kindIndex = KIND_GHOST_INDEX; + } + else + { + kindDefinition *kdef; + + kdef = getLanguageKindForLetter (owner, kindLetter); + if (kdef) + { + if (kindName && strcmp (kdef->name, kindName) && (strcmp(kindName, KIND_REGEX_DEFAULT_LONG))) + /* When using a same kind letter for multiple regex patterns, the name of kind + should be the same. */ + error (WARNING, "Don't reuse the kind letter `%c' in a language %s (old: \"%s\", new: \"%s\")", + kdef->letter, getLanguageName (owner), + kdef->name, kindName); + } + else + { + kdef = kindNew (kindLetter, kindName, description); + defineLanguageKind (owner, kdef, kindFree); + } + + ptrn->u.tag.kindIndex = kdef->id; + } +} + + +static regexPattern *addCompiledTagPattern (struct lregexControlBlock *lcb, + int table_index, + enum regexParserType regptype, regex_t* const pattern, + const char* const name, char kindLetter, const char* kindName, + char *const description, const char* flags, + bool *disabled) +{ + regexPattern * ptrn = addCompiledTagCommon(lcb, table_index, pattern, regptype); + + struct commonFlagData commonFlagData = { + .owner = lcb->owner, + .lcb = lcb, + .ptrn = ptrn + }; + + ptrn->type = PTRN_TAG; + ptrn->u.tag.name_pattern = eStrdup (name); + ptrn->disabled = disabled; + + /* need to check for exclusive before setting the kind */ + if (regptype == REG_PARSER_SINGLE_LINE) + flagsEval (flags, prePtrnFlagDef, ARRAY_SIZE(prePtrnFlagDef), &ptrn->exclusive); + + setKind(ptrn, lcb->owner, kindLetter, kindName, description); + + flagsEval (flags, commonSpecFlagDef, ARRAY_SIZE(commonSpecFlagDef), &commonFlagData); + + if (regptype == REG_PARSER_SINGLE_LINE || regptype == REG_PARSER_MULTI_TABLE) + flagsEval (flags, scopePtrnFlagDef, ARRAY_SIZE(scopePtrnFlagDef), &ptrn->scopeActions); + + if (regptype == REG_PARSER_MULTI_LINE || regptype == REG_PARSER_MULTI_TABLE) + flagsEval (flags, multilinePtrnFlagDef, ARRAY_SIZE(multilinePtrnFlagDef), &ptrn->mgroup); + + if (regptype == REG_PARSER_MULTI_TABLE) + flagsEval (flags, multitablePtrnFlagDef, ARRAY_SIZE(multitablePtrnFlagDef), &commonFlagData); + + return ptrn; +} + +static regexPattern *addCompiledCallbackPattern (struct lregexControlBlock *lcb, regex_t* const pattern, + const regexCallback callback, const char* flags, + bool *disabled, + void *userData) +{ + regexPattern * ptrn; + bool exclusive = false; + flagsEval (flags, prePtrnFlagDef, ARRAY_SIZE(prePtrnFlagDef), &exclusive); + ptrn = addCompiledTagCommon(lcb, TABLE_INDEX_UNUSED, pattern, REG_PARSER_SINGLE_LINE); + ptrn->type = PTRN_CALLBACK; + ptrn->u.callback.function = callback; + ptrn->u.callback.userData = userData; + ptrn->exclusive = exclusive; + ptrn->disabled = disabled; + return ptrn; +} + + +static void regex_flag_basic_short (char c CTAGS_ATTR_UNUSED, void* data) +{ + int* cflags = data; + *cflags &= ~REG_EXTENDED; +} + +static void regex_flag_basic_long (const char* const s CTAGS_ATTR_UNUSED, const char* const unused CTAGS_ATTR_UNUSED, void* data) +{ + regex_flag_basic_short ('b', data); +} + +static void regex_flag_extend_short (char c CTAGS_ATTR_UNUSED, void* data) +{ + int* cflags = data; + *cflags |= REG_EXTENDED; +} + +static void regex_flag_extend_long (const char* const c CTAGS_ATTR_UNUSED, const char* const unused CTAGS_ATTR_UNUSED, void* data) +{ + regex_flag_extend_short('e', data); +} + +static void regex_flag_icase_short (char c CTAGS_ATTR_UNUSED, void* data) +{ + int* cflags = data; + *cflags |= REG_ICASE; +} + +static void regex_flag_icase_long (const char* s CTAGS_ATTR_UNUSED, const char* const unused CTAGS_ATTR_UNUSED, void* data) +{ + regex_flag_icase_short ('i', data); +} + + +static flagDefinition regexFlagDefs[] = { + { 'b', "basic", regex_flag_basic_short, regex_flag_basic_long, + NULL, "interpreted as a Posix basic regular expression."}, + { 'e', "extend", regex_flag_extend_short, regex_flag_extend_long, + NULL, "interpreted as a Posix extended regular expression (default)"}, + { 'i', "icase", regex_flag_icase_short, regex_flag_icase_long, + NULL, "applied in a case-insensitive manner"}, +}; + +static regex_t* compileRegex (enum regexParserType regptype, + const char* const regexp, const char* const flags) +{ + int cflags = REG_EXTENDED | REG_NEWLINE; + + if (regptype == REG_PARSER_MULTI_TABLE) + cflags &= ~REG_NEWLINE; + + regex_t *result; + int errcode; + + flagsEval (flags, + regexFlagDefs, + ARRAY_SIZE(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 = KIND_REGEX_DEFAULT; + *kindName = eStrdup (KIND_REGEX_DEFAULT_LONG); + } + else if (kinds [0] != '\0') + { + const char* k = kinds; + if (k [0] != ',' && (k [1] == ',' || k [1] == '\0')) + *kind = *k++; + else + *kind = KIND_REGEX_DEFAULT; + if (*k == ',') + ++k; + if (k [0] == '\0') + *kindName = eStrdup (KIND_REGEX_DEFAULT_LONG); + 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); + } } } } @@ -580,7 +1109,7 @@ static void processLanguageRegex (const langType language, static vString* substitute ( const char* const in, const char* out, - const int nmatch, const GMatchInfo* const minfo) + const int nmatch, const regmatch_t* const pmatch) { vString* result = vStringNew (); const char* p; @@ -589,12 +1118,10 @@ static vString* substitute ( if (*p == '\\' && isdigit ((int) *++p)) { const int dig = *p - '0'; - int so, eo; - if (0 < dig && dig < nmatch && - g_match_info_fetch_pos(minfo, dig, &so, &eo) && so != -1) + if (0 < dig && dig < nmatch && pmatch [dig].rm_so != -1) { - const int diglen = eo - so; - vStringNCatS (result, in + so, diglen); + 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') @@ -603,12 +1130,35 @@ static vString* substitute ( return result; } -static void matchTagPattern (const vString* const line, +static unsigned long getInputLineNumberInRegPType (enum regexParserType regptype, + off_t offset) +{ + return (regptype == REG_PARSER_MULTI_LINE || regptype == REG_PARSER_MULTI_TABLE) + ? getInputLineNumberForFileOffset (offset) + : getInputLineNumber (); +} + +static void fillEndLineFieldOfUpperScopes (struct lregexControlBlock *lcb, unsigned long endline) +{ + tagEntryInfo *entry; + unsigned int n = lcb->currentScope; + + while ((entry = getEntryInCorkQueue (n)) + && (entry->extensionFields.endLine == 0)) + { + entry->extensionFields.endLine = endline; + n = entry->extensionFields.scopeIndex; + } +} + +static void matchTagPattern (struct lregexControlBlock *lcb, + const char* line, const regexPattern* const patbuf, - const GMatchInfo* const minfo) + const regmatch_t* const pmatch, + off_t offset) { - vString *const name = substitute (vStringValue (line), - patbuf->u.tag.name_pattern, BACK_REFERENCE_COUNT, minfo); + vString *const name = substitute (line, + patbuf->u.tag.name_pattern, BACK_REFERENCE_COUNT, pmatch); bool placeholder = !!((patbuf->scopeActions & SCOPE_PLACEHOLDER) == SCOPE_PLACEHOLDER); unsigned long scope = CORK_NIL; int n; @@ -620,119 +1170,305 @@ static void matchTagPattern (const vString* const line, { tagEntryInfo *entry; - scope = currentScope; + scope = lcb->currentScope; while ((entry = getEntryInCorkQueue (scope)) && entry->placeholder) /* Look at parent */ scope = entry->extensionFields.scopeIndex; } if (patbuf->scopeActions & SCOPE_CLEAR) - currentScope = CORK_NIL; + { + unsigned long endline = getInputLineNumberInRegPType(patbuf->regptype, offset); + fillEndLineFieldOfUpperScopes (lcb, endline); + lcb->currentScope = CORK_NIL; + } if (patbuf->scopeActions & SCOPE_POP) { - tagEntryInfo *entry = getEntryInCorkQueue (currentScope); - currentScope = entry? entry->extensionFields.scopeIndex: CORK_NIL; + tagEntryInfo *entry = getEntryInCorkQueue (lcb->currentScope); + + if (entry && (entry->extensionFields.endLine == 0)) + entry->extensionFields.endLine = getInputLineNumberInRegPType(patbuf->regptype, offset); + + lcb->currentScope = entry? entry->extensionFields.scopeIndex: CORK_NIL; } if (vStringLength (name) == 0 && (placeholder == false)) { if (patbuf->accept_empty_name == false) - error (WARNING, "%s:%ld: null expansion of name pattern \"%s\"", - getInputFileName (), getInputLineNumber (), + error (WARNING, "%s:%lu: null expansion of name pattern \"%s\"", + getInputFileName (), + getInputLineNumberInRegPType(patbuf->regptype, offset), patbuf->u.tag.name_pattern); n = CORK_NIL; } else - n = makeRegexTag (name, patbuf->u.tag.kind, scope, placeholder); + { + static TrashBox* field_trashbox; + unsigned long ln = 0; + MIOPos pos; + tagEntryInfo e; + int kind; + roleBitsType roleBits; + + if ((patbuf->regptype == REG_PARSER_MULTI_LINE) + || (patbuf->regptype == REG_PARSER_MULTI_TABLE)) + { + ln = getInputLineNumberForFileOffset (offset); + pos = getInputFilePositionForLine (ln); + } + + n = CORK_NIL; + kind = patbuf->u.tag.kindIndex; + roleBits = patbuf->u.tag.roleBits; + + initRegexTag (&e, name, kind, ROLE_INDEX_DEFINITION, scope, placeholder, + ln, ln == 0? NULL: &pos, patbuf->xtagType); + + if (field_trashbox == NULL) + { + field_trashbox = trashBoxNew(); + DEFAULT_TRASH_BOX (field_trashbox, trashBoxDelete); + } + + if (patbuf->fieldPatterns) + { + for (unsigned int i = 0; i < ptrArrayCount(patbuf->fieldPatterns); i++) + { + struct fieldPattern *fp = ptrArrayItem(patbuf->fieldPatterns, i); + if (isFieldEnabled (fp->ftype)) + { + vString * const value = substitute (line, fp->template, + BACK_REFERENCE_COUNT, pmatch); + attachParserField (&e, fp->ftype, vStringValue (value)); + trashBoxPut (field_trashbox, value, + (TrashBoxDestroyItemProc)vStringDelete); + } + } + } + + if (roleBits) + { + unsigned int roleIndex; + + for (roleIndex = 0; + roleIndex < countLanguageRoles(e.langType, kind); + roleIndex++) + { + if (roleBits & makeRoleBit(roleIndex)) + assignRole (&e, roleIndex); + } + } + n = makeTagEntry (&e); + + trashBoxMakeEmpty(field_trashbox); + } if (patbuf->scopeActions & SCOPE_PUSH) - currentScope = n; + lcb->currentScope = n; vStringDelete (name); } -static void matchCallbackPattern ( +static bool matchCallbackPattern ( const vString* const line, const regexPattern* const patbuf, - const GMatchInfo* const minfo) + const regmatch_t* const pmatch) { regexMatch matches [BACK_REFERENCE_COUNT]; unsigned int count = 0; int i; for (i = 0 ; i < BACK_REFERENCE_COUNT ; ++i) { - int so = -1, eo = -1; - /* with GRegex we could get the real match count, but that might - * cause incompatibilities with CTags */ - g_match_info_fetch_pos(minfo, i, &so, &eo); - matches [i].start = so; - matches [i].length = eo - so; + matches [i].start = pmatch [i].rm_so; + matches [i].length = pmatch [i].rm_eo - pmatch [i].rm_so; /* 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 (so != -1) + if (pmatch [i].rm_so != -1) count = i + 1; } - patbuf->u.callback.function (vStringValue (line), matches, count, + return patbuf->u.callback.function (vStringValue (line), matches, count, patbuf->u.callback.userData); } -static bool matchRegexPattern (const vString* const line, - const regexPattern* const patbuf) + +static void printMessage(const langType language, + const regexPattern *const ptrn, + const off_t offset, + const char *const line, + const regmatch_t* const pmatch) +{ + vString *msg; + + Assert (ptrn); + Assert (ptrn->message.selection > 0); + Assert (ptrn->message.message_string); + + msg = substitute (line, ptrn->message.message_string, BACK_REFERENCE_COUNT, pmatch); + + error (ptrn->message.selection, "%sMessage from regex<%s>: %s (%s:%lu)", + (ptrn->message.selection == FATAL ? "Fatal: " : ""), + getLanguageName (language), + vStringValue (msg), + getInputFileName (), + getInputLineNumberInRegPType (ptrn->regptype, offset)); + + vStringDelete (msg); +} + + +static bool matchRegexPattern (struct lregexControlBlock *lcb, + const vString* const line, + regexTableEntry *entry) { bool result = false; - GMatchInfo *minfo; + regmatch_t pmatch [BACK_REFERENCE_COUNT]; + int match; + regexPattern* patbuf = entry->pattern; if (patbuf->disabled && *(patbuf->disabled)) return false; - if (g_regex_match(patbuf->pattern, vStringValue (line), 0, &minfo)) + match = regexec (patbuf->pattern, vStringValue (line), + BACK_REFERENCE_COUNT, pmatch, 0); + if (match == 0) { result = true; + entry->statistics.match++; + + if (hasMessage(patbuf)) + printMessage(lcb->owner, patbuf, 0, vStringValue (line), pmatch); + if (patbuf->type == PTRN_TAG) - matchTagPattern (line, patbuf, minfo); + matchTagPattern (lcb, vStringValue (line), patbuf, pmatch, 0); else if (patbuf->type == PTRN_CALLBACK) - matchCallbackPattern (line, patbuf, minfo); + result = matchCallbackPattern (line, patbuf, pmatch); else { Assert ("invalid pattern type" == NULL); result = false; } } - g_match_info_free(minfo); + else + entry->statistics.unmatch++; return result; } +static bool matchMultilineRegexPattern (struct lregexControlBlock *lcb, + const vString* const allLines, + regexTableEntry *entry) +{ + const char *start; + const char *current; + off_t offset = 0; + regexPattern* patbuf = entry->pattern; + + bool result = false; + regmatch_t pmatch [BACK_REFERENCE_COUNT]; + int match = 0; + unsigned int delta = 1; + + Assert (patbuf); + + if (patbuf->disabled && *(patbuf->disabled)) + return false; + + current = start = vStringValue (allLines); + do + { + match = regexec (patbuf->pattern, current, + BACK_REFERENCE_COUNT, pmatch, 0); + if (match != 0) + { + entry->statistics.unmatch++; + break; + } + + if (hasMessage(patbuf)) + printMessage(lcb->owner, patbuf, (current + pmatch[0].rm_so) - start, current, pmatch); + + offset = (current + pmatch [patbuf->mgroup.forLineNumberDetermination].rm_so) + - start; + + entry->statistics.match++; + if (patbuf->type == PTRN_TAG) + { + matchTagPattern (lcb, current, patbuf, pmatch, offset); + result = true; + } + else if (patbuf->type == PTRN_CALLBACK) + ; /* Not implemented yet */ + else + { + Assert ("invalid pattern type" == NULL); + result = false; + break; + } + + delta = (patbuf->mgroup.nextFromStart + ? pmatch [patbuf->mgroup.forNextScanning].rm_so + : pmatch [patbuf->mgroup.forNextScanning].rm_eo); + if (delta == 0) + { + unsigned int offset = current - start; + error (WARNING, + "a multi line regex pattern doesn't advance the input cursor: %s", + patbuf->pattern_string); + error (WARNING, "Language: %s, input file: %s, pos: %u", + getLanguageName (lcb->owner), getInputFileName(), offset); + break; + } + current += delta; + + } while (current < start + vStringLength (allLines)); + + return result; +} /* PUBLIC INTERFACE */ /* Match against all patterns for specified language. Returns true if at least * on pattern matched. */ -extern bool matchRegex (const vString* const line, const langType language) +extern bool matchRegex (struct lregexControlBlock *lcb, const vString* const line) { bool result = false; - if (language != LANG_IGNORE && language <= SetUpper && - Sets [language].count > 0) + unsigned int i; + for (i = 0 ; i < ptrArrayCount(lcb->entries[REG_PARSER_SINGLE_LINE]) ; ++i) { - const patternSet* const set = Sets + language; - unsigned int i; - for (i = 0 ; i < set->count ; ++i) + regexTableEntry *entry = ptrArrayItem(lcb->entries[REG_PARSER_SINGLE_LINE], i); + regexPattern *ptrn = entry->pattern; + + Assert (ptrn); + + if ((ptrn->xtagType != XTAG_UNKNOWN) + && (!isXtagEnabled (ptrn->xtagType))) + continue; + + if (matchRegexPattern (lcb, line, entry)) { - regexPattern* ptrn = set->patterns + i; - if (matchRegexPattern (line, ptrn)) - { - result = true; - if (ptrn->exclusive) - break; - } + result = true; + if (ptrn->exclusive) + break; } } return result; } +extern void notifyRegexInputStart (struct lregexControlBlock *lcb) +{ + lcb->currentScope = CORK_NIL; + + ptrArrayClear (lcb->tstack); +} + +extern void notifyRegexInputEnd (struct lregexControlBlock *lcb) +{ + unsigned long endline = getInputLineNumber (); + fillEndLineFieldOfUpperScopes (lcb, endline); +} + extern void findRegexTagsMainloop (int (* driver)(void)) { - currentScope = CORK_NIL; /* merely read all lines of the file */ while (driver () != EOF) ; @@ -748,20 +1484,61 @@ extern void findRegexTags (void) findRegexTagsMainloop (fileReadLineDriver); } -extern bool hasScopeActionInRegex (const langType language) +static bool hasScopeActionInRegex0(ptrArray *entries) { - bool r = false; - unsigned int i; + for (unsigned int i = 0; i < ptrArrayCount(entries); i++) + { + regexTableEntry *entry = ptrArrayItem(entries, i); + Assert (entry && entry->pattern); + if (entry->pattern->scopeActions) + return true; + } + return false; +} + +extern bool hasScopeActionInRegex (struct lregexControlBlock *lcb) +{ + ptrArray *entries; - if (language <= SetUpper && Sets [language].count > 0) - for (i = 0; i < Sets [language].count; i++) - if (Sets[language].patterns[i].scopeActions) - r= true; + entries = lcb->entries[REG_PARSER_SINGLE_LINE]; + if (hasScopeActionInRegex0 (entries)) + return true; - return r; + for (unsigned int i = 0; i < ptrArrayCount(lcb->tables); i++) + { + struct regexTable *table = ptrArrayItem(lcb->tables, i); + if (hasScopeActionInRegex0 (table->entries)) + return true; + } + + return false; } -static regexPattern *addTagRegexInternal (const langType language, +static char *escapeRegexPattern (const char* pattern) +{ + vString *p = vStringNew (); + + while (*pattern != '\0') + { + char c = *pattern; + if (c == '\n') + vStringCatS(p, "\\n"); + else if (c == '\t') + vStringCatS(p, "\\t"); + else if (c == '\\') + vStringCatS(p, "\\\\"); + else + vStringPut(p, c); + + pattern++; + } + + return vStringDeleteUnwrap (p); +} + +static regexPattern *addTagRegexInternal (struct lregexControlBlock *lcb, + int table_index, + enum regexParserType regptype, const char* const regex, const char* const name, const char* const kinds, @@ -771,83 +1548,195 @@ static regexPattern *addTagRegexInternal (const langType language, regexPattern *rptr = NULL; Assert (regex != NULL); Assert (name != NULL); - if (regexAvailable) + + if (!regexAvailable) + return NULL; + + regex_t* const cp = compileRegex (regptype, regex, flags); + + if (cp != NULL) { - GRegex* const cp = compileRegex (regex, flags); - if (cp != NULL) + char kindLetter; + char* kindName; + char* description; + kindDefinition* fileKind; + + parseKinds (kinds, &kindLetter, &kindName, &description); + fileKind = getLanguageKind (lcb->owner, KIND_FILE_INDEX); + if (kindLetter == fileKind->letter) + error (FATAL, + "Kind letter \'%c\' used in regex definition \"%s\" of %s language is reserved in ctags main", + kindLetter, + regex, + getLanguageName (lcb->owner)); + else if (kindName && (strcmp (kindName, fileKind->name) == 0)) + error (FATAL, + "Kind name \"%s\" used in regex definition \"%s\" of %s language is reserved in ctags main", + kindName, + regex, + getLanguageName (lcb->owner)); + + rptr = addCompiledTagPattern (lcb, table_index, + regptype, cp, name, + kindLetter, kindName, description, flags, + disabled); + rptr->pattern_string = escapeRegexPattern(regex); + if (kindName) + eFree (kindName); + if (description) + eFree (description); + + if (*name == '\0') { - char kind; - char* kindName; - char* description; - - parseKinds (kinds, &kind, &kindName, &description); - if (kind == getLanguageFileKind (language)->letter) - error (FATAL, - "Kind letter \'%c\' used in regex definition \"%s\" of %s language is reserved in ctags main", - kind, - regex, - getLanguageName (language)); - - rptr = addCompiledTagPattern (language, cp, name, - kind, kindName, description, flags, - disabled); - if (kindName) - eFree (kindName); - if (description) - eFree (description); + if (rptr->exclusive || rptr->scopeActions & SCOPE_PLACEHOLDER + || regptype == REG_PARSER_MULTI_TABLE) + rptr->accept_empty_name = true; + else + error (WARNING, "%s: regexp missing name pattern", regex); } } - if (*name == '\0') - { - if (rptr->exclusive || rptr->scopeActions & SCOPE_PLACEHOLDER) - rptr->accept_empty_name = true; - else - error (WARNING, "%s: regexp missing name pattern", regex); - } - return rptr; } -extern void addTagRegex (const langType language CTAGS_ATTR_UNUSED, - const char* const regex CTAGS_ATTR_UNUSED, - const char* const name CTAGS_ATTR_UNUSED, - const char* const kinds CTAGS_ATTR_UNUSED, - const char* const flags CTAGS_ATTR_UNUSED, +extern void addTagRegex (struct lregexControlBlock *lcb, + const char* const regex, + const char* const name, + const char* const kinds, + const char* const flags, bool *disabled) { - addTagRegexInternal (language, regex, name, kinds, flags, disabled); + addTagRegexInternal (lcb, TABLE_INDEX_UNUSED, + REG_PARSER_SINGLE_LINE, regex, name, kinds, flags, disabled); +} + +extern void addTagMultiLineRegex (struct lregexControlBlock *lcb, const char* const regex, + const char* const name, const char* const kinds, const char* const flags, + bool *disabled) +{ + addTagRegexInternal (lcb, TABLE_INDEX_UNUSED, + REG_PARSER_MULTI_LINE, regex, name, kinds, flags, disabled); +} + +extern void addTagMultiTableRegex(struct lregexControlBlock *lcb, + const char* const table_name, + const char* const regex, + const char* const name, const char* const kinds, const char* const flags, + bool *disabled) +{ + int table_index = getTableIndexForName (lcb, table_name); + + if (table_index < 0) + error (FATAL, "unknown table name: %s", table_name); + + addTagRegexInternal (lcb, table_index, REG_PARSER_MULTI_TABLE, regex, name, kinds, flags, + disabled); } -extern void addCallbackRegex (const langType language CTAGS_ATTR_UNUSED, - const char* const regex CTAGS_ATTR_UNUSED, - const char* const flags CTAGS_ATTR_UNUSED, - const regexCallback callback CTAGS_ATTR_UNUSED, +extern void addCallbackRegex (struct lregexControlBlock *lcb, + const char* const regex, + const char* const flags, + const regexCallback callback, bool *disabled, void * userData) { Assert (regex != NULL); - if (regexAvailable) + + if (!regexAvailable) + return; + + + regex_t* const cp = compileRegex (REG_PARSER_SINGLE_LINE, regex, flags); + if (cp != NULL) { - GRegex* const cp = compileRegex (regex, flags); - if (cp != NULL) - addCompiledCallbackPattern (language, cp, callback, flags, - disabled, userData); + regexPattern *rptr = addCompiledCallbackPattern (lcb, cp, callback, flags, + disabled, userData); + rptr->pattern_string = escapeRegexPattern(regex); } } -extern void addLanguageRegex ( - const langType language CTAGS_ATTR_UNUSED, const char* const regex CTAGS_ATTR_UNUSED) +static void addTagRegexOption (struct lregexControlBlock *lcb, + enum regexParserType regptype, + const char* const pattern) { - if (regexAvailable) + if (!regexAvailable) + return; + + int table_index = TABLE_INDEX_UNUSED; + char * regex_pat = NULL; + char *name, *kinds, *flags; + + + if (regptype == REG_PARSER_MULTI_TABLE) { - char *const regex_pat = eStrdup (regex); - char *name, *kinds, *flags; - if (parseTagRegex (regex_pat, &name, &kinds, &flags)) + const char *c; + for (c = pattern; *c; c++) { - addTagRegexInternal (language, regex_pat, name, kinds, flags, - NULL); - eFree (regex_pat); + if (! (isalnum(*c) || *c == '_')) + { + if (*c && (*(c + 1) != '^')) + { + vString *tmp = vStringNew (); + + /* Put '^' as prefix for the pattern */ + vStringPut(tmp, *c); + vStringPut(tmp, '^'); + vStringCatS(tmp, c + 1); + regex_pat = vStringDeleteUnwrap(tmp); + } + else + regex_pat = eStrdup (c); + break; + } + } + + if (regex_pat == NULL || *regex_pat == '\0') + error (FATAL, "wrong mtable pattern specification: %s", pattern); + + char *table_name = eStrndup(pattern, c - pattern); + table_index = getTableIndexForName (lcb, table_name); + if (table_index < 0) + error (FATAL, "unknown table name: %s (in %s)", table_name, pattern); + eFree(table_name); + } + else + regex_pat = eStrdup (pattern); + + if (parseTagRegex (regptype, regex_pat, &name, &kinds, &flags)) + addTagRegexInternal (lcb, table_index, regptype, regex_pat, name, kinds, flags, + NULL); + + eFree (regex_pat); +} + +extern void processTagRegexOption (struct lregexControlBlock *lcb, + enum regexParserType regptype, + const char* const parameter) +{ + if (parameter == NULL || parameter [0] == '\0') + clearPatternSet (lcb); + else if (parameter [0] != '@') + addTagRegexOption (lcb, regptype, parameter); + else if (! doesFileExist (parameter + 1)) + error (WARNING, "cannot open regex file"); + else + { + const char* regexfile = parameter + 1; + + verbose ("open a regex file: %s\n", regexfile); + MIO* const mio = mio_new_file (regexfile, "r"); + if (mio == NULL) + error (WARNING | PERROR, "%s", regexfile); + else + { + vString* const regex = vStringNew (); + while (readLineRaw (regex, mio)) + { + if (vStringLength (regex) > 1 && vStringValue (regex)[0] != '\n') + addTagRegexOption (lcb, regptype, vStringValue (regex)); + } + mio_free (mio); + vStringDelete (regex); } } } @@ -856,233 +1745,528 @@ extern void addLanguageRegex ( * Regex option parsing */ -extern bool processRegexOption (const char *const option, - const char *const parameter CTAGS_ATTR_UNUSED) +extern void printRegexFlags (bool withListHeader, bool machinable, FILE *fp) { - langType language; + struct colprintTable * table; - language = getLanguageComponentInOption (option, "regex-"); - if (language == LANG_IGNORE) - return false; + table = flagsColprintTableNew (); - processLanguageRegex (language, parameter); + flagsColprintAddDefinitions (table, regexFlagDefs, ARRAY_SIZE (regexFlagDefs)); + flagsColprintAddDefinitions (table, prePtrnFlagDef, ARRAY_SIZE (prePtrnFlagDef)); + flagsColprintAddDefinitions (table, scopePtrnFlagDef, ARRAY_SIZE (scopePtrnFlagDef)); + flagsColprintAddDefinitions (table, commonSpecFlagDef, ARRAY_SIZE (commonSpecFlagDef)); - return true; + flagsColprintTablePrint (table, withListHeader, machinable, fp); + colprintTableDelete(table); } -struct kindCbHelperData { - bool (*func) (kindDefinition *, void *); - void *func_data; - bool result; -}; - -static void kindCbHelper (void *key, void *value, void* user_data) +extern void printMultilineRegexFlags (bool withListHeader, bool machinable, FILE *fp) { - kindDefinition *kind = value; - struct kindCbHelperData *helper_data = user_data; + struct colprintTable * table; - if (helper_data->result) - return; + table = flagsColprintTableNew (); - helper_data->result = helper_data->func (kind, helper_data->func_data); + flagsColprintAddDefinitions (table, regexFlagDefs, ARRAY_SIZE (regexFlagDefs)); + flagsColprintAddDefinitions (table, multilinePtrnFlagDef, ARRAY_SIZE (multilinePtrnFlagDef)); + flagsColprintAddDefinitions (table, commonSpecFlagDef, ARRAY_SIZE (commonSpecFlagDef)); + + flagsColprintTablePrint (table, withListHeader, machinable, fp); + colprintTableDelete(table); } -extern void foreachRegexKinds (const langType language, - bool (*func) (kindDefinition *, void *), - void *data) +extern void printMultitableRegexFlags (bool withListHeader, bool machinable, FILE *fp) { - initializeParser (language); - if (language <= SetUpper && Sets [language].count > 0) - { - patternSet* const set = Sets + language; - hashTable *kinds = set->kinds; - struct kindCbHelperData helper_data = { - .func = func, - .func_data = data, - .result = false, - }; - hashTableForeachItem (kinds, kindCbHelper, &helper_data); - } -} + struct colprintTable * table; + table = flagsColprintTableNew (); -static bool kind_reset_cb (kindDefinition *kind, void *data) -{ - kind->enabled = *(bool *)data; - return false; /* continue */ + flagsColprintAddDefinitions (table, regexFlagDefs, ARRAY_SIZE (regexFlagDefs)); + flagsColprintAddDefinitions (table, multilinePtrnFlagDef, ARRAY_SIZE (multilinePtrnFlagDef)); + flagsColprintAddDefinitions (table, multitablePtrnFlagDef, ARRAY_SIZE (multitablePtrnFlagDef)); + flagsColprintAddDefinitions (table, scopePtrnFlagDef, ARRAY_SIZE (scopePtrnFlagDef)); + flagsColprintAddDefinitions (table, commonSpecFlagDef, ARRAY_SIZE (commonSpecFlagDef)); + + flagsColprintTablePrint (table, withListHeader, machinable, fp); + colprintTableDelete(table); } -extern void resetRegexKinds (const langType language, bool mode) +extern void freeRegexResources (void) { - foreachRegexKinds (language, kind_reset_cb, &mode); + /* TODO: SHOULD BE REMOVED */ } -struct kind_and_mode_and_result +extern bool regexNeedsMultilineBuffer (struct lregexControlBlock *lcb) { - int kind; - const char *kindLong; - bool mode; - bool result; -}; + if (ptrArrayCount(lcb->entries [REG_PARSER_MULTI_LINE]) > 0) + return true; + else if (ptrArrayCount(lcb->tables) > 0) + return true; + else + return false; +} -static bool enable_kind_cb (kindDefinition *kind, void *data) +extern bool matchMultilineRegex (struct lregexControlBlock *lcb, const vString* const allLines) { - struct kind_and_mode_and_result *kmr = data; - if ((kmr->kind != KIND_NULL - && kind->letter == kmr->kind) - || (kmr->kindLong && kind->name - && (strcmp (kmr->kindLong, kind->name) == 0))) + bool result = false; + + unsigned int i; + + for (i = 0; i < ptrArrayCount(lcb->entries [REG_PARSER_MULTI_LINE]); ++i) { - kind->enabled = kmr->mode; - kmr->result = true; + regexTableEntry *entry = ptrArrayItem(lcb->entries [REG_PARSER_MULTI_LINE], i); + Assert (entry && entry->pattern); + + if ((entry->pattern->xtagType != XTAG_UNKNOWN) + && (!isXtagEnabled (entry->pattern->xtagType))) + continue; + + result = matchMultilineRegexPattern (lcb, allLines, entry) || result; } - /* continue: - There can be more than one patterns which represents this kind. */ - return false; + return result; } -extern bool enableRegexKind (const langType language, const int kind, const bool mode) +static int getTableIndexForName (const struct lregexControlBlock *const lcb, const char *name) { - struct kind_and_mode_and_result kmr; + unsigned int i; - kmr.kind = kind; - kmr.kindLong = NULL; - kmr.mode = mode; - kmr.result = false; + for (i = 0; i < ptrArrayCount(lcb->tables); i++) + { + struct regexTable *table = ptrArrayItem(lcb->tables, i); + if (strcmp (table->name, name) == 0) + return (int)i; + } - foreachRegexKinds (language, enable_kind_cb, &kmr); - return kmr.result; + return TABLE_INDEX_UNUSED; } -extern bool enableRegexKindLong (const langType language, const char *kindLong, const bool mode) +extern void addRegexTable (struct lregexControlBlock *lcb, const char *name) { - struct kind_and_mode_and_result kmr; + const char *c; + for (c = name; *c; c++) + if (! (isalnum(*c) || *c == '_')) + error (FATAL, "`%c' in \"%s\" is not acceptable as part of table name", *c, name); + + if (getTableIndexForName(lcb, name) >= 0) + { + error (WARNING, "regex table \"%s\" is already defined", name); + return; + } - kmr.kind = KIND_NULL; - kmr.kindLong = kindLong; - kmr.mode = mode; - kmr.result = false; + struct regexTable *table = xCalloc(1, struct regexTable); + table->name = eStrdup (name); + table->entries = ptrArrayNew(deleteTableEntry); - foreachRegexKinds (language, enable_kind_cb, &kmr); - return kmr.result; + ptrArrayAdd (lcb->tables, table); } -struct kind_and_result +static void dumpSstack(FILE* fp, unsigned long scope) { - int kind; - bool result; -}; + fprintf (fp, "scope : "); + while (scope != CORK_NIL) + { + tagEntryInfo *entry = getEntryInCorkQueue (scope); + fprintf(fp, "%s", entry->name); -static bool is_kind_enabled_cb (kindDefinition *kind, void *data) -{ - bool r = false; - struct kind_and_result *kr = data; + scope = entry->extensionFields.scopeIndex; + if (scope != CORK_NIL) + fprintf(fp, "%c", '/'); + } + fprintf (fp, "\n"); +} - if (kind->letter == kr->kind) +static void dumpTstack(FILE* fp, ptrArray *tstack) +{ + for (unsigned int i = ptrArrayCount(tstack); i > 0; i--) { - kr->result = kind->enabled; - r = true; + char tmp[2]; + struct regexTable *t = ptrArrayItem(tstack, i - 1); + if (i == 1) + tmp[0] = '\0'; + else + { + tmp[0] = '/'; + tmp[1] = '\0'; + } + fprintf(fp, "%s%s", t->name, tmp); } - - return r; + fprintf(fp, "\n"); } -static bool does_kind_exist_cb (kindDefinition *kind, void *data) +static void printInputLine(FILE* vfp, const char *c, const off_t offset) { - bool r = false; - struct kind_and_result *kr = data; + vString *v = vStringNew (); - if (kind->letter == kr->kind) - { - kr->result = true; - r = true; - } + for (; *c && (*c != '\n'); c++) + vStringPut(v, *c); - return r; + if (vStringLength (v) == 0 && *c == '\n') + vStringCatS (v, "\\n"); + + fprintf (vfp, "\ninput : \"%s\" L%lu\n", + vStringValue (v), + getInputLineNumberForFileOffset(offset)); + vStringDelete(v); } -extern bool isRegexKindEnabled (const langType language, const int kind) +static void printMultitableMessage(const langType language, + const char *const tableName, + const unsigned int index, + const regexPattern *const ptrn, + const off_t offset, + const char *const current, + const regmatch_t* const pmatch) { - struct kind_and_result d; + vString *msg; + + Assert (ptrn); + Assert (ptrn->message.selection > 0); + Assert (ptrn->message.message_string); - d.kind = kind; - d.result = false; + msg = substitute (current, ptrn->message.message_string, BACK_REFERENCE_COUNT, pmatch); - foreachRegexKinds (language, is_kind_enabled_cb, &d); + error (ptrn->message.selection, "%sMessage from mtable<%s/%s[%2u]>: %s (%s:%lu)", + (ptrn->message.selection == FATAL ? "Fatal: " : ""), + getLanguageName (language), + tableName, + index, + vStringValue (msg), + getInputFileName (), + getInputLineNumberForFileOffset (offset)); - return d.result; + vStringDelete (msg); } -extern bool hasRegexKind (const langType language, const int kind) +static struct regexTable * matchMultitableRegexTable (struct lregexControlBlock *lcb, + struct regexTable *table, const vString *const start, unsigned int *offset) { - struct kind_and_result d; + struct regexTable *next = NULL; + const char *current; + regmatch_t pmatch [BACK_REFERENCE_COUNT]; + const char *cstart = vStringValue(start); + unsigned int delta; - d.kind = kind; - d.result = false; - foreachRegexKinds (language, does_kind_exist_cb, &d); + restart: + current = cstart + *offset; - return d.result; -} + /* Accept the case *offset == vStringLength(start) + because we want an empty regex // still matches empty input. */ + if (*offset > vStringLength(start)) + { + *offset = vStringLength(start); + goto out; + } -struct printRegexKindCBData{ - const char* const langName; - bool allKindFields; - bool indent; - bool tabSeparated; -}; + BEGIN_VERBOSE(vfp); + { + printInputLine(vfp, current, *offset); + } + END_VERBOSE(); -static bool printRegexKind (kindDefinition *kind, void *user_data) -{ - struct printRegexKindCBData *data = user_data; - if (kind->letter != KIND_GHOST) + for (unsigned int i = 0; i < ptrArrayCount(table->entries); i++) { - if (data->allKindFields && data->indent) - printf (Option.machinable? "%s": PR_KIND_FMT (LANG,s), data->langName); - printKind (kind, data->allKindFields, data->indent, - data->tabSeparated); + regexTableEntry *entry = ptrArrayItem(table->entries, i); + regexPattern *ptrn = entry->pattern; + + Assert (ptrn); + + BEGIN_VERBOSE(vfp); + { + char s[3]; + if (*current == '\n') + { + s [0] = '\\'; + s [1] = 'n'; + s [2] = '\0'; + } + else if (*current == '\t') + { + s [0] = '\\'; + s [1] = 't'; + s [2] = '\0'; + } + else if (*current == '\\') + { + s [0] = '\\'; + s [1] = '\\'; + s [2] = '\0'; + } + else + { + s[0] = *current; + s[1] = '\0'; + } + + if (s[1] == '\0') + fprintf (vfp, "match : '%s' %15s[%2u] /", s, table->name, i); + else if (s[0] == '\0') + fprintf (vfp, "match : '' %15s[%2u] /", table->name, i); + else + fprintf (vfp, "match :'%s' %15s[%2u] / ", s, table->name, i); + fprintf (vfp, "%s/\n", ptrn->pattern_string); + } + END_VERBOSE(); + + int match = 0; + + if (ptrn->disabled && *(ptrn->disabled)) + continue; + + match = regexec (ptrn->pattern, current, + BACK_REFERENCE_COUNT, pmatch, 0); + + if (match == 0) + { + entry->statistics.match++; + + if (ptrn->type == PTRN_TAG) + { + struct mTableActionSpec *taction = &(ptrn->taction); + + matchTagPattern (lcb, current, ptrn, pmatch, + (current + + pmatch [ptrn->mgroup.forLineNumberDetermination].rm_so) + - cstart); + BEGIN_VERBOSE(vfp); + { + fprintf(vfp, "result: matched %d bytes\n", (int)(pmatch[0].rm_eo)); + dumpSstack (vfp, lcb->currentScope); + } + END_VERBOSE(); + + if (hasMessage(ptrn)) + printMultitableMessage (lcb->owner, table->name, i, ptrn, + *offset, current, pmatch); + + delta = (ptrn->mgroup.nextFromStart + ? pmatch [ptrn->mgroup.forNextScanning].rm_so + : pmatch [ptrn->mgroup.forNextScanning].rm_eo); + *offset += delta; + + switch (taction->action) + { + case TACTION_NOP: + BEGIN_VERBOSE(vfp); + { + fprintf(vfp, "action: NOP in {%s}, stack: /", table->name); + dumpTstack(vfp, lcb->tstack); + } + END_VERBOSE(); + break; + case TACTION_ENTER: + /* TODO: Limit the depth of tstack. */ + ptrArrayAdd (lcb->tstack, + taction->continuation_table + ? taction->continuation_table + : table); + next = taction->table; + BEGIN_VERBOSE(vfp); + { + if (taction->continuation_table) + fprintf(vfp, "action: [enter] to {%s}, cont: {%s}, stack: /", + next->name, + taction->continuation_table->name); + else + fprintf(vfp, "action: [enter] to {%s}, stack: /", next->name); + dumpTstack(vfp, lcb->tstack); + } + END_VERBOSE(); + break; + case TACTION_LEAVE: + BEGIN_VERBOSE(vfp); + { + fprintf(vfp, "action: [leave] from {%s}, stack: /", table->name); + dumpTstack(vfp, lcb->tstack); + } + END_VERBOSE(); + if (ptrArrayCount (lcb->tstack) == 0) + { + error (WARNING, "leave is specified as regex table action but the table stack is empty"); + return NULL; + } + next = ptrArrayLast(lcb->tstack); + ptrArrayRemoveLast (lcb->tstack); + break; + case TACTION_JUMP: + next = taction->table; + BEGIN_VERBOSE(vfp); + { + fprintf(vfp, "action: [jump] from {%s} to {%s}, stack: /", table->name, next->name); + dumpTstack(vfp, lcb->tstack); + } + END_VERBOSE(); + + break; + case TACTION_RESET: + next = taction->table; + BEGIN_VERBOSE(vfp); + { + fprintf(vfp, "action: [reset] to {%s}, stack: /", next->name); + } + END_VERBOSE(); + + ptrArrayClear (lcb->tstack); + break; + case TACTION_QUIT: + BEGIN_VERBOSE(vfp); + { + fprintf(vfp, "action: [quit], stack: /"); + dumpTstack(vfp, lcb->tstack); + } + END_VERBOSE(); + return NULL; + } + + if (next) + break; + + if (delta == 0) + { + error (WARNING, "Forcefully advance the input pos because"); + error (WARNING, "following conditions for entering infinite loop are satisfied:"); + error (WARNING, "+ matching the pattern succeeds,"); + error (WARNING, "+ the next table is not given, and"); + error (WARNING, "+ the input file pos doesn't advance."); + error (WARNING, "Language: %s, input file: %s, pos: %u", + getLanguageName (lcb->owner), getInputFileName(), *offset); + ++*offset; + } + } + else if (ptrn->type == PTRN_CALLBACK) + ; /* Not implemented yet */ + else + { + Assert ("invalid pattern type" == NULL); + break; + } + goto restart; + } + else + entry->statistics.unmatch++; } - return false; + out: + if (next == NULL && ptrArrayCount (lcb->tstack) > 0) + { + static int apop_count = 0; + next = ptrArrayLast(lcb->tstack); + verbose("result: no match - autopop<%d> from {%s} to {%s} @ %lu\n", apop_count++, table->name, next->name, + getInputLineNumberForFileOffset(*offset)); + ptrArrayRemoveLast (lcb->tstack); + } + return next; } -extern void printRegexKinds (const langType language, - bool allKindFields, - bool indent, - bool tabSeparated) +extern void extendRegexTable (struct lregexControlBlock *lcb, const char *src, const char *dist) { - const char* const langName = getLanguageName (language); - struct printRegexKindCBData data = { - .langName = langName, - .allKindFields = allKindFields, - .indent = indent, - .tabSeparated = tabSeparated, - }; - foreachRegexKinds (language, printRegexKind, &data); + + int i; + struct regexTable * src_table; + struct regexTable * dist_table; + + verbose ("extend regex table \"%s\" with \"%s\"\n", dist, src); + + i = getTableIndexForName (lcb, src); + if (i < 0) + error (FATAL, "no such regex table in %s: %s", getLanguageName(lcb->owner), src); + src_table = ptrArrayItem(lcb->tables, i); + + i = getTableIndexForName (lcb, dist); + if (i < 0) + error (FATAL, "no such regex table in %s: %s", getLanguageName(lcb->owner), dist); + dist_table = ptrArrayItem(lcb->tables, i); + + for (i = 0; i < (int)ptrArrayCount(src_table->entries); i++) + { + regexTableEntry *entry = ptrArrayItem (src_table->entries, i); + ptrArrayAdd(dist_table->entries, newRefPatternEntry(entry)); + } } -extern void printRegexFlags (void) +extern void printMultitableStatistics (struct lregexControlBlock *lcb, FILE *vfp) { - flagPrintHelp (regexFlagDefs, ARRAY_SIZE (regexFlagDefs)); - flagPrintHelp (prePtrnFlagDef, ARRAY_SIZE (prePtrnFlagDef)); - flagPrintHelp (scopePtrnFlagDef, ARRAY_SIZE (scopePtrnFlagDef)); + struct regexTable *table = ptrArrayItem (lcb->tables, 0); + + if (ptrArrayCount(lcb->tables) == 0) + return; + + fprintf(vfp, "MTABLE REGEX STATISTICS of %s\n", getLanguageName (lcb->owner)); + fputs("==============================================\n", vfp); + for (unsigned int i = 0; i < ptrArrayCount(lcb->tables); i++) + { + table = ptrArrayItem (lcb->tables, i); + fprintf(vfp, "%s\n", table->name); + fputs("-----------------------\n", vfp); + for (unsigned int j = 0; j < ptrArrayCount(table->entries); j++) + { + regexTableEntry *entry = ptrArrayItem (table->entries, j); + Assert (entry && entry->pattern); + fprintf(vfp, "%10u/%-10u%-40s ref: %d\n", + entry->statistics.match, + entry->statistics.unmatch + entry->statistics.match, + entry->pattern->pattern_string, + entry->pattern->refcount); + } + fputc('\n', vfp); + } } -extern void freeRegexResources (void) +extern bool matchMultitableRegex (struct lregexControlBlock *lcb, const vString* const allLines) { - int i; - for (i = 0 ; i <= SetUpper ; ++i) - clearPatternSet (i); - if (Sets != NULL) - eFree (Sets); - Sets = NULL; - SetUpper = -1; + if (ptrArrayCount (lcb->tables) == 0) + return false; + + struct regexTable *table = ptrArrayItem (lcb->tables, 0); + unsigned int offset = 0; + + int motionless_counter = 0; + unsigned int last_offset; + + + while (table) + { + last_offset = offset; + table = matchMultitableRegexTable(lcb, table, allLines, &offset); + + if (last_offset == offset) + motionless_counter++; + else + motionless_counter = 0; + + if (motionless_counter > MTABLE_MOTIONLESS_MAX) + { + error (WARNING, "mtable<%s/%s>: the input cursor stays at %u in %s so long though the tables are switched", + getLanguageName (lcb->owner), + table->name, offset, getInputFileName ()); + break; + } + + if (table && (ptrArrayCount (lcb->tstack) > MTABLE_STACK_MAX_DEPTH)) + { + unsigned int i; + struct regexTable *t; + + error (WARNING, "mtable<%s/%s>: the tenter/tleave stack overflows at %u in %s", + getLanguageName (lcb->owner), + table->name, offset, getInputFileName ()); + error (WARNING, "DUMP FROM THE TOP:"); + /* TODO: ues dumpTstack */ + for (i = ptrArrayCount(lcb->tstack); 0 < i; --i) + { + t = ptrArrayItem (lcb->tstack, i - 1); + error (WARNING, "%3u %s", i - 1, t->name); + } + + break; + } + } + + return true; } /* Return true if available. */ extern bool checkRegex (void) { -/* not needed with GRegex */ -#if 0 /*defined (CHECK_REGCOMP)*/ +#if defined (CHECK_REGCOMP) { /* Check for broken regcomp() on Cygwin */ regex_t patbuf; diff --git a/ctags/main/lregex.h b/ctags/main/lregex.h new file mode 100644 index 0000000000..19b2d35692 --- /dev/null +++ b/ctags/main/lregex.h @@ -0,0 +1,47 @@ +/* +* Copyright (c) 2000-2003, Darren Hiebert +* Copyright (c) 2017, Red Hat, Inc. +* Copyright (c) 2017, Masatake YAMATO +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* This module contains functions for applying regular expression matching. +* +* The code for utilizing the Gnu regex package with regards to processing the +* regex option and checking for regex matches was adapted from routines in +* Gnu etags. +*/ + +#ifndef CTAGS_MAIN_LREGEX_H +#define CTAGS_MAIN_LREGEX_H + +/* +* INCLUDE FILES +*/ +#include "general.h" + +/* +* DATA DECLARATIONS +*/ +typedef struct sTagRegexTable { + const char *const regex; + const char* const name; + const char* const kinds; + const char *const flags; + bool *disabled; + bool mline; +} tagRegexTable; + +typedef struct { + size_t start; /* character index in line where match starts */ + size_t length; /* length of match */ +} regexMatch; + +/* Return value is referred when {exclusive} is also specified. + The input line is consumed when "{exclusive}" is specified and + the value returned from the callback function is true. */ +typedef bool (*regexCallback) (const char *line, const regexMatch *matches, unsigned int count, + void *userData); + +#endif /* CTAGS_MAIN_LREGEX_H */ diff --git a/ctags/main/lregex_p.h b/ctags/main/lregex_p.h new file mode 100644 index 0000000000..a39fa9fa61 --- /dev/null +++ b/ctags/main/lregex_p.h @@ -0,0 +1,76 @@ +/* +* Copyright (c) 2000-2003, Darren Hiebert +* Copyright (c) 2017, Red Hat, Inc. +* Copyright (c) 2017, Masatake YAMATO +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* This module contains functions for applying regular expression matching. +* +* The code for utilizing the Gnu regex package with regards to processing the +* regex option and checking for regex matches was adapted from routines in +* Gnu etags. +*/ +#ifndef CTAGS_MAIN_LREGEX_PRIVATE_H +#define CTAGS_MAIN_LREGEX_PRIVATE_H + +/* +* INCLUDE FILES +*/ +#include "general.h" +#include "lregex.h" + +/* +* DATA DECLARATIONS +*/ +enum regexParserType { + REG_PARSER_SINGLE_LINE, + REG_PARSER_MULTI_LINE, + REG_PARSER_MULTI_TABLE, +}; + +struct lregexControlBlock; + +/* +* FUNCTION PROTOTYPES +*/ +extern struct lregexControlBlock* allocLregexControlBlock (parserDefinition *parser); +extern void freeLregexControlBlock (struct lregexControlBlock* lcb); + +extern void processTagRegexOption (struct lregexControlBlock *lcb, + enum regexParserType, + const char* const parameter); +extern void addTagRegex (struct lregexControlBlock *lcb, const char* const regex, + const char* const name, const char* const kinds, const char* const flags, + bool *disabled); +extern void addTagMultiLineRegex (struct lregexControlBlock *lcb, const char* const regex, + const char* const name, const char* const kinds, const char* const flags, + bool *disabled); +extern void addTagMultiTableRegex(struct lregexControlBlock *lcb, + const char* const table_name, + const char* const regex, + const char* const name, const char* const kinds, const char* const flags, + bool *disabled); + +extern bool matchRegex (struct lregexControlBlock *lcb, const vString* const line); +extern bool hasScopeActionInRegex (struct lregexControlBlock *lcb); +extern void addCallbackRegex (struct lregexControlBlock *lcb, + const char* const regex, + const char* const flags, + const regexCallback callback, + bool *disabled, + void * userData); +extern bool regexNeedsMultilineBuffer (struct lregexControlBlock *lcb); +extern bool matchMultilineRegex (struct lregexControlBlock *lcb, const vString* const allLines); +extern bool matchMultitableRegex (struct lregexControlBlock *lcb, const vString* const allLines); + +extern void notifyRegexInputStart (struct lregexControlBlock *lcb); +extern void notifyRegexInputEnd (struct lregexControlBlock *lcb); + +extern void addRegexTable (struct lregexControlBlock *lcb, const char *name); +extern void extendRegexTable (struct lregexControlBlock *lcb, const char *src, const char *dist); + +extern void printMultitableStatistics (struct lregexControlBlock *lcb, FILE *vfp); + +#endif /* CTAGS_MAIN_LREGEX_PRIVATEH */ diff --git a/ctags/main/lxcmd.c b/ctags/main/lxcmd.c deleted file mode 100644 index 6811b3cf45..0000000000 --- a/ctags/main/lxcmd.c +++ /dev/null @@ -1,1227 +0,0 @@ -/* -* -* Copyright (c) 1996-2003, Darren Hiebert -* Copyright (c) 2014, Red Hat, Inc. -* Copyright (c) 2014, Masatake YAMATO -* -* This source code is released for free distribution under the terms of the -* GNU General Public License version 2 or (at your option) any later version. -* -* This module contains functions for invoking external command. -* Half of codes are derived from lregex.c. -* Core data structure is taken from readtags.h. -* -*/ - -/* - XCMD PROTOCOL (version 2.1) - ================================================================== - When invoking xcmd just only with --lint-kinds=LANG option, - xcmd must write lines matching one of following patterns - to stdout. - - patterns - -------- - - ^([^ \t])[ \t]+([^\t]+)([ \t]+(\[off\]))?$ - \1 => letter - \2 => name - \4 => \[off\] is optional. - - - exit code - --------- - - If xcmd itself recognizes it cannot run, it should exit with - XCMD_NOT_AVAILABLE_STATUS exit code. ctags may ignore the xcmd. -*/ - -#define XCMD_NOT_AVAILABLE_STATUS 127 - -/* -* INCLUDE FILES -*/ -#include "general.h" /* must always come first */ - -#ifndef _GNU_SOURCE -# define _GNU_SOURCE /* for WIFEXITED and WEXITSTATUS */ -#endif -#include -#include -#include /* for WIFEXITED and WEXITSTATUS */ -#include -#include -#ifdef HAVE_SYS_WAIT_H -# include /* for WIFEXITED and WEXITSTATUS */ -#endif -#ifdef HAVE_UNISTD_H -# include -#endif - - -#include "debug.h" -#include "main.h" -#include "options.h" -#include "parse.h" -#include "ptag.h" -#include "read.h" -#include "routines.h" -#include "vstring.h" - -#include "pcoproc.h" - -#include "flags.h" -#include "xtag.h" -#include "ptag.h" - -/* -* MACROS -*/ - -#define XCMD_LIST_KIND_OPTION "--list-kinds" - -/* -* DATA DECLARATIONS -*/ - -typedef struct { - - /* the key of the extension field */ - const char *key; - - /* the value of the extension field (may be an empty string) */ - const char *value; - int pull_count; - -} tagExtensionField; - -/* This structure contains information about a specific tag. */ -typedef struct { - - /* name of tag */ - const char *name; - - /* path of input file containing definition of tag */ - const char *file; - - /* address for locating tag in input file */ - struct { - /* pattern for locating input line - * (may be NULL if not present) */ - const char *pattern; - - /* line number in input file of tag definition - * (may be zero if not known) */ - unsigned long lineNumber; - } address; - - - const kindDefinition* kind; - - /* is tag of file-limited scope? */ - short fileScope; - - /* miscellaneous extension fields */ - struct { - /* number of entries in `list' */ - unsigned short count; - - /* list of key value pairs */ - tagExtensionField *list; - } fields; - -} tagEntry; - -typedef struct { - vString *path; - kindDefinition *kinds; - unsigned int n_kinds; - bool available; - unsigned int id; /* not used yet */ - int not_available_status; -} xcmdPath; - -typedef struct { - xcmdPath *paths; - unsigned int count; -} pathSet; - -#ifdef HAVE_COPROC - -static pathSet* Sets = NULL; -static int SetUpper = -1; /* upper language index in list */ - -static void clearPathSet (const langType language) -{ - if (language <= SetUpper) - { - pathSet* const set = Sets + language; - unsigned int i, k; - for (i = 0 ; i < set->count ; ++i) - { - xcmdPath *p = &set->paths [i]; - - vStringDelete (p->path); - p->path = NULL; - p->available = false; - for (k = 0; k < p->n_kinds; k++) - { - kindDefinition* kind = &(p->kinds[k]); - - eFree ((void *)kind->name); - kind->name = NULL; - eFree ((void *)kind->description); - kind->description = NULL; - } - if (p->kinds) - { - eFree (p->kinds); - p->kinds = NULL; - } - } - if (set->paths != NULL) - eFree (set->paths); - set->paths = NULL; - set->count = 0; - } -} - -static bool loadPathKind (xcmdPath *const path, char* line, char *args[]) -{ - const char* backup = line; - char* off; - vString *desc; - kindDefinition *kind; - - if (line[0] == '\0') - return false; - else if (!isblank(line[1])) - { - error (WARNING, "[%s] a space after letter is not found in kind description line: %s", args[0], backup); - return false; - } - - path->kinds = xRealloc (path->kinds, path->n_kinds + 1, kindDefinition); - kind = &path->kinds [path->n_kinds]; - memset (kind, 0, sizeof (*kind)); - kind->enabled = true; - kind->letter = line[0]; - kind->name = NULL; - kind->description = NULL; - kind->referenceOnly = false; - kind->nRoles = 0; - kind->roles = NULL; - - verbose (" kind letter: <%c>\n", kind->letter); - - for (line++; isblank(*line); line++) - ; /* do nothing */ - - if (*line == '\0') - { - error (WARNING, "[%s] unexpectedly a kind description line is terminated: %s", - args[0], backup); - return false; - } - - Assert (!isblank (*line)); - - off = strrstr(line, "[off]"); - if (off == line) - { - error (WARNING, "[%s] [off] is given but no kind description is found: %s", - args[0], backup); - return false; - } - else if (off) - { - if (!isblank (*(off - 1))) - { - error (WARNING, "[%s] a whitespace must precede [off] flag: %s", - args[0], backup); - return false; - } - kind->enabled = false; - *off = '\0'; - } - desc = vStringNewInit (line); - vStringStripTrailing (desc); - - Assert (vStringLength (desc) > 0); - - kind->description = vStringDeleteUnwrap (desc); - - /* TODO: This conversion should be part of protocol. */ - { - char *tmp = eStrdup (kind->description); - char *c; - for (c = tmp; *c != '\0'; c++) - { - if (*c == ' ' || *c == '\t') - *c = '_'; - } - kind->name = tmp; - } - - path->n_kinds += 1; - return true; -} - -static bool isSafeExecutable (const char* path) -{ - fileStatus *file; - bool r; - - Assert (path); - file = eStat (path); - - if (!file->exists) - { - /* The file doesn't exist. So I cannot say - it is unsafe. The caller should - handle this case. */ - r = true; - } - else if (file->isSetuid) - { - error (WARNING, "xcmd doesn't run a setuid executable: %s", path); - r = false; - } - else if (file->isSetgid) - { - error (WARNING, "xcmd doesn't run a setgid executable: %s", path); - r = false; - } - else - r = true; - - eStatFree (file); - return r; -} - -static bool loadPathKinds (xcmdPath *const path, const langType language) -{ - enum pcoprocError r; - FILE* pp = NULL; - char * argv[3]; - int status; - vString * opt; - char file_kind = getLanguageFileKind (language)->letter; - - opt = vStringNewInit(XCMD_LIST_KIND_OPTION); - vStringCatS (opt, "="); - vStringCatS (opt, getLanguageName(language)); - - argv[2] = NULL; - argv[1] = vStringValue (opt); - argv[0] = vStringValue (path->path); - - errno = 0; - - if (getuid() == 0 || geteuid() == 0) - { - verbose ("all xcmd feature is disabled when running ctags in root privilege\n"); - vStringDelete (opt); - return false; - } - - if (! isSafeExecutable (argv [0])) - { - vStringDelete (opt); - return false; - } - verbose ("loading path kinds of %s from [%s %s]\n", getLanguageName(language), argv[0], argv[1]); - r = pcoprocOpen (vStringValue (path->path), argv, &pp, NULL); - switch (r) { - case PCOPROC_ERROR_WPIPE: - error (WARNING | PERROR, "failed to make pipe to write to command: [%s %s]", - argv[0], argv[1]); - break; - case PCOPROC_ERROR_RPIPE: - error (WARNING | PERROR, "failed to make pipe to read from command: [%s %s]", - argv[0], argv[1]); - break; - case PCOPROC_ERROR_FORK: - error (WARNING | PERROR, "failed to do fork: [%s %s]", - argv[0], argv[1]); - break; - case PCOPROC_SUCCESSFUL: - break; - } - - if (pp) - { - vString* vline = vStringNew(); - - while (readLineRawWithNoSeek (vline, pp)) - { - char* line; - char kind_letter; - - vStringStripNewline (vline); - line = vStringValue (vline); - if (!loadPathKind (path, line, argv)) - break; - - kind_letter = path->kinds [path->n_kinds - 1].letter; - if (kind_letter == file_kind) - error (FATAL, - "Kind letter \'%c\' returned from xcmd %s of %s language is reserved in ctags main", - kind_letter, - vStringValue (path->path), - getLanguageName (language)); - } - - vStringDelete (vline); - - - status = pcoprocClose (pp); - - /* TODO: Decode status */ - verbose(" status: %d\n", status); - if (status != 0) - { - if (status > 0 - && WIFEXITED (status) - && (WEXITSTATUS (status) == path->not_available_status)) - verbose ("xcmd: the %s backend is not available\n", argv[0]); - else - error (WARNING, "xcmd exits abnormally status(%d): [%s %s]", - status, argv[0], argv[1]); - vStringDelete (opt); - return false; - } - } - else - { - error (WARNING | PERROR, "cannot make pipe to xcmd: [%s %s]", - argv[0], argv[1]); - } - - vStringDelete (opt); - return path->kinds == NULL? false: true; -} -#endif /* HAVE_COPROC */ - - -extern void foreachXcmdKinds (const langType language, - bool (*func) (kindDefinition *, void *), - void *data) -{ -#ifdef HAVE_COPROC - if (language <= SetUpper && Sets [language].count > 0) - { - pathSet* const set = Sets + language; - xcmdPath * path = set->paths; - unsigned int i; - for (i = 0 ; i < set->count ; ++i) - { - unsigned int k; - if (!path[i].available) - continue; - - for (k = 0; k < path[i].n_kinds; k++) - if (func (& (path[i].kinds[k]), data)) - break; - } - } -#endif -} - -static bool kind_reset_cb (kindDefinition *kind, void *data) -{ - kind->enabled = *(bool *)data; - return false; /* continue */ -} - -extern void resetXcmdKinds (const langType language, bool mode) -{ - foreachXcmdKinds (language, kind_reset_cb, &mode); -} - -struct kind_and_mode_and_result -{ - int kind; - const char *kindLong; - bool mode; - bool result; -}; - -static bool enable_kind_cb (kindDefinition *kind, void *data) -{ - struct kind_and_mode_and_result *kmr = data; - if ((kmr->kind != KIND_NULL - && kind->letter == kmr->kind) - || (kmr->kindLong && kind->name - && (strcmp (kmr->kindLong, kind->name) == 0))) - { - kind->enabled = kmr->mode; - kmr->result = true; - } - /* conitnue: - There can be more than one paths which deals this kind. - Consider /bin/X and /bin/Y are both parser for a language L. - ctags --langdef=L --xcmd-L=/bin/X --xcmd-L=/bin/Y ... */ - return false; - -} - -extern bool enableXcmdKind (const langType language, const int kind, - const bool mode) -{ - struct kind_and_mode_and_result kmr; - - kmr.kind = kind; - kmr.kindLong = NULL; - kmr.mode = mode; - kmr.result = false; - - foreachXcmdKinds (language, enable_kind_cb, &kmr); - return kmr.result; -} - -extern bool enableXcmdKindLong (const langType language, const char *kindLong, - const bool mode) -{ - struct kind_and_mode_and_result kmr; - - kmr.kind = KIND_NULL; - kmr.kindLong = kindLong; - kmr.mode = mode; - kmr.result = false; - - foreachXcmdKinds (language, enable_kind_cb, &kmr); - return kmr.result; -} - -struct kind_and_result -{ - int kind; - bool result; -}; - -static bool is_kind_enabled_cb (kindDefinition *kind, void *data) -{ - bool r = false; - struct kind_and_result *kr = data; - - if (kind->letter == kr->kind) - { - kr->result = kind->enabled; - r = true; - } - - return r; -} - -static bool does_kind_exist_cb (kindDefinition *kind, void *data) -{ - bool r = false; - struct kind_and_result *kr = data; - - if (kind->letter == kr->kind) - { - kr->result = true; - r = true; - } - - return r; -} - -extern bool isXcmdKindEnabled (const langType language, const int kind) -{ - struct kind_and_result d; - - d.kind = kind; - d.result = false; - - foreachXcmdKinds (language, is_kind_enabled_cb, &d); - - return d.result; -} - -extern bool hasXcmdKind (const langType language, const int kind) -{ - struct kind_and_result d; - - d.kind = kind; - d.result = false; - - foreachXcmdKinds (language, does_kind_exist_cb, &d); - - return d.result; -} - -struct printXcmdKindCBData { - const char *langName; - bool allKindFields; - bool indent; - bool tabSeparated; -}; - -#ifdef HAVE_COPROC -static bool printXcmdKind (kindDefinition *kind, void *user_data) -{ - struct printXcmdKindCBData *data = user_data; - - if (data->allKindFields && data->indent) - printf (Option.machinable? "%s": PR_KIND_FMT (LANG,s), data->langName); - - printKind (kind, data->allKindFields, data->indent, data->tabSeparated); - return false; -} -#endif - -extern void printXcmdKinds (const langType language CTAGS_ATTR_UNUSED, - bool allKindFields CTAGS_ATTR_UNUSED, - bool indent CTAGS_ATTR_UNUSED, - bool tabSeparated CTAGS_ATTR_UNUSED) -{ -#ifdef HAVE_COPROC - if (language <= SetUpper && Sets [language].count > 0) - { - const char* const langName = getLanguageName(language); - struct printXcmdKindCBData data = { - .langName = langName, - .allKindFields = allKindFields, - .indent = indent, - .tabSeparated = tabSeparated, - }; - foreachXcmdKinds (language, printXcmdKind, &data); - } -#endif -} - -extern void freeXcmdResources (void) -{ -#ifdef HAVE_COPROC - int i; - for (i = 0 ; i <= SetUpper ; ++i) - clearPathSet (i); - if (Sets != NULL) - eFree (Sets); - Sets = NULL; - SetUpper = -1; -#endif -} - -#ifdef HAVE_COPROC -static void xcmd_flag_not_avaible_status_long (const char* const s, const char* const v, void* data) -{ - xcmdPath *path = data; - - if(!strToInt(v, 0, &path->not_available_status)) - error (FATAL, "Could not parse the value for %s flag: %s", s, v); -} -#endif - -extern void addTagXcmd (const langType language, vString* pathvstr, const char* flags) -{ -#ifdef HAVE_COPROC - pathSet* set; - xcmdPath *path; - - flagDefinition xcmdFlagDefs[] = { - { '\0', "notAvailableStatus", NULL, xcmd_flag_not_avaible_status_long }, - }; - - Assert (pathvstr != NULL); - - if (language > SetUpper) - { - int i; - Sets = xRealloc (Sets, (language + 1), pathSet); - for (i = SetUpper + 1 ; i <= language ; ++i) - { - Sets [i].paths = NULL; - Sets [i].count = 0; - } - SetUpper = language; - } - set = Sets + language; - set->paths = xRealloc (set->paths, (set->count + 1), xcmdPath); - - path = &set->paths [set->count]; - path->path = pathvstr; - path->kinds = NULL; - path->n_kinds = 0; - path->id = set->count; - path->not_available_status = XCMD_NOT_AVAILABLE_STATUS; - - set->count += 1; - - flagsEval (flags, xcmdFlagDefs, ARRAY_SIZE(xcmdFlagDefs), path); - - path->available = (loadPathKinds (path, language)); - useXcmdMethod (language); - if (path->available) - notifyAvailabilityXcmdMethod (language); -#endif -} -extern void addLanguageXcmd ( - const langType language CTAGS_ATTR_UNUSED, const char* const parameter CTAGS_ATTR_UNUSED) -{ -#ifdef HAVE_COPROC - char *path; - vString* vpath; - const char* flags; - - flags = strchr (parameter, LONG_FLAGS_OPEN); - if (flags) - path = eStrndup (parameter, flags - parameter); - else - path = eStrdup (parameter); - - if (parameter [0] != '/' && parameter [0] != '.') - { - vpath = expandOnDriversPathList (path); - vpath = vpath? vpath: vStringNewInit(path); - } - else - vpath = vStringNewInit(path); - - eFree (path); - - addTagXcmd (language, vpath, flags); -#endif -} - -#ifdef HAVE_COPROC -static void processLanguageXcmd (const langType language, - const char* const parameter) -{ - if (parameter == NULL || parameter [0] == '\0') - clearPathSet (language); - else - addLanguageXcmd (language, parameter); -} -#endif - -extern bool processXcmdOption (const char *const option, const char *const parameter, - OptionLoadingStage stage) -{ - langType language; - - language = getLanguageComponentInOption (option, "xcmd-"); - if (language == LANG_IGNORE) - return false; - - if (stage == OptionLoadingStageCurrentRecursive) - { - error (WARNING, "Don't use --xcmd- option in ./.ctags nor ./.ctags/*: %s", - option); - /* Consume it here. */ - return true; - } - else if (stage == OptionLoadingStageHomeRecursive && (!Option.allowXcmdInHomeDir)) - { - error (WARNING, "Don't use --xcmd- option in ~/.ctags and/or ~/.ctags/*: %s", - option); - /* Consume it here. */ - return true; - } - -#ifdef HAVE_COPROC - processLanguageXcmd (language, parameter); -#else - error (WARNING, "coproc feature is not available; required for --%s option", - option); -#endif - - return true; -} - -#ifdef HAVE_COPROC -static const kindDefinition* lookupKindFromLetter (const xcmdPath* const path, char kind_letter) -{ - unsigned int k; - kindDefinition *kind; - - for (k = 0; k < path->n_kinds; k++) - { - kind = path->kinds + k; - if (kind->letter == kind_letter) - return kind; - } - return NULL; - -} - -static const kindDefinition* lookupKindFromName (const xcmdPath* const path, const char* const kind_name) -{ - unsigned int k; - kindDefinition *kind; - - for (k = 0; k < path->n_kinds; k++) - { - kind = path->kinds + k; - if (kind->name && (!strcmp(kind->name, kind_name))) - return kind; - - } - return NULL; - -} - -static const char* entryLookupField (tagEntry *const entry, const char *const kind, bool pulling) -{ - int i; - - for (i = 0; i < entry->fields.count; i++) - { - if (!strcmp (entry->fields.list [i].key, kind)) - { - if (pulling) - entry->fields.list [i].pull_count++; - return entry->fields.list [i].value; - } - } - return NULL; -} - -static const char* entryGetAnyUnpulledField (tagEntry *const entry, const char **const kind, bool pulling) -{ - int i; - - for (i = 0; i < entry->fields.count; i++) - { - if (entry->fields.list [i].pull_count == 0) - { - *kind = entry->fields.list [i].key; - if (pulling) - entry->fields.list [i].pull_count++; - return entry->fields.list [i].value; - } - } - return NULL; -} - -static bool isKindEnabled (xcmdPath* path, const char* value) -{ - unsigned int k; - kindDefinition *kind; - - Assert (path->kinds); - Assert (value); - Assert (*value); - - for (k = 0; k < path->n_kinds; k++) - { - kind = path->kinds + k; - if (!kind->enabled) - { - if (value[1] == '\0' && value[0] == kind->letter) - return false; - if (!strcmp(value, kind->name)) - return false; - if (!strcmp(value, kind->description)) - return false; - } - } - return true; -} - -static void entryAddField (tagEntry *const entry, const char *const key, const char *const value) -{ - entry->fields.list = xRealloc (entry->fields.list, - entry->fields.count + 1, - tagExtensionField); - entry->fields.list [entry->fields.count].key = key; - entry->fields.list [entry->fields.count].value = value; - entry->fields.list [entry->fields.count].pull_count = 0; - ++entry->fields.count; -} - -static bool parseExtensionFields (tagEntry *const entry, char *const string, xcmdPath* path) -{ - char *p = string; - - while (p != NULL && *p != '\0') - { - while (*p == TAB) - *p++ = '\0'; - if (*p != '\0') - { - char *colon; - char *field = p; - p = strchr (p, TAB); - if (p != NULL) - *p++ = '\0'; - colon = strchr (field, ':'); - if (colon == NULL) - { - if (isKindEnabled (path, field)) - { - if (entry->kind == NULL) - { - entry->kind = lookupKindFromLetter (path, field[0]); - if (entry->kind == NULL) - { - kindDefinition *fileKind = getInputLanguageFileKind (); - if (fileKind && fileKind->letter == field[0]) - /* ctags will make a tag for file. */ - goto reject; - - } - } - else { - ; /* TODO Handle warning */ - } - Assert (entry->kind); - - } - else - goto reject; - } - else - { - const char *key = field; - const char *value = colon + 1; - *colon = '\0'; - if (strcmp (key, "kind") == 0) - { - if (*value == '\0') - goto reject; - else if (isKindEnabled (path, value)) - { - if (entry->kind == NULL) - { - entry->kind = lookupKindFromName (path, value); - if (entry->kind == NULL) - { - kindDefinition *fileKind = getInputLanguageFileKind (); - if (fileKind && (strcmp(fileKind->name, value) == 0)) - /* ctags will make a tag for file. */ - goto reject; - - } - } - - else { - ; /*TODO Handle warning*/ - } - Assert (entry->kind); - } - else - goto reject; - } - else if (strcmp (key, "file") == 0) - entry->fileScope = 1; - else if (strcmp (key, "line") == 0) - entry->address.lineNumber = atol (value); - else if (strcmp (key, "language") == 0) - continue; - else - entryAddField (entry, key, value); - } - } - } - return true; - reject: - if (entry->fields.list) - { - eFree (entry->fields.list); - entry->fields.list = NULL; - } - entry->fields.count = 0; - return false; -} - -static bool hasPseudoTagPrefix (const char* const name) -{ - const size_t prefixLength = strlen (PSEUDO_TAG_PREFIX); - return !strncmp (name, PSEUDO_TAG_PREFIX, prefixLength); -} - -static bool parseXcmdPath (char* line, xcmdPath* path, tagEntry* entry) -{ - char *p = line; - char *tab = strchr (p, TAB); - bool pseudoTag = false; - - // verbose("<%s>line: %s\n", vStringValue (path->path), line); - - entry->name = p; - if (tab != NULL) - { - *tab = '\0'; - pseudoTag = hasPseudoTagPrefix (entry->name); - - p = tab + 1; - entry->file = p; - tab = strchr (p, TAB); - if (tab != NULL) - { - int fieldsPresent; - *tab = '\0'; - p = tab + 1; - if (*p == '/' || *p == '?') - { - /* parse pattern */ - int delimiter = *(unsigned char*) p; - entry->address.lineNumber = 0; - entry->address.pattern = p; - do - { - p = strchr (p + 1, delimiter); - } while (p != NULL && *(p - 1) == '\\'); - if (p == NULL) - { - *tab = '\t'; - error (WARNING, "pattern from %s is not ended with `%c': %s", - vStringValue (path->path), - (char)delimiter, - line); - return false; - } - else - ++p; - } - else if (isdigit ((int) *(unsigned char*) p)) - { - /* parse line number */ - entry->address.pattern = p; - entry->address.lineNumber = atol (p); - while (isdigit ((int) *(unsigned char*) p)) - ++p; - } - else - { - *tab = '\t'; - error (WARNING, "cannot parse as ctags output from %s: %s", - vStringValue (path->path), line); - return false; - } - fieldsPresent = (strncmp (p, ";\"", 2) == 0); - *p = '\0'; - - if (pseudoTag) - return true; - - if (fieldsPresent) - { - if (!parseExtensionFields (entry, p + 2, path)) - return false; - return true; - } - } - } - return false; -} - -static void freeTagEntry (tagEntry* entry) -{ - if (entry->fields.list) - { - eFree (entry->fields.list); - entry->fields.list = NULL; - } - entry->fields.count = 0; -} - -static bool makePseudoTagEntryFromTagEntry (tagEntry* entry) -{ - const char *tagName, *fileName, *pattern; - const size_t prefixLength = strlen (PSEUDO_TAG_PREFIX); - ptagType t = PTAG_UNKNOWN; - - tagName = entry->name + prefixLength; - fileName = entry->file; - pattern = entry->address.pattern; - - if (strcmp (tagName, "TAG_FILE_SORTED") == 0) - return false; - else if (strcmp (tagName, "TAG_FILE_FORMAT") == 0) - return false; /* ??? */ - else if (strcmp (tagName, "TAG_PROGRAM_AUTHOR") == 0) - t = PTAG_PROGRAM_AUTHOR; - else if (strcmp (tagName, "TAG_PROGRAM_NAME") == 0) - t = PTAG_PROGRAM_NAME; - else if (strcmp (tagName, "TAG_PROGRAM_URL") == 0) - t = PTAG_PROGRAM_URL; - else if (strcmp (tagName, "TAG_PROGRAM_VERSION") == 0) - t = PTAG_PROGRAM_VERSION; - - if (t == PTAG_UNKNOWN) - return false; - else - { - struct ptagXcmdData data = { - .fileName = fileName, - .pattern = pattern, - .language = entryLookupField(entry, - "language", - false), - }; - return makePtagIfEnabled (t, &data); - } -} - -static bool makeTagEntryFromTagEntry (xcmdPath* path, tagEntry* entry) -{ - tagEntryInfo tag; - MIOPos filePosition; - - if (hasPseudoTagPrefix (entry->name)) - { - if (isXtagEnabled (XTAG_PSEUDO_TAGS)) - return makePseudoTagEntryFromTagEntry (entry); - else - return false; - } - - memset(&filePosition, 0, sizeof(filePosition)); - - // pseudo if (entry->name...); - initTagEntryFull (&tag, entry->name, - entry->address.lineNumber, - entryLookupField(entry, "language", true), - filePosition, - entry->file, - entry->kind, - ROLE_INDEX_DEFINITION, - NULL, - NULL, - 0); - - tag.pattern = entry->address.pattern; - - tag.isFileScope = (bool)entry->fileScope; - tag.extensionFields.access = entryLookupField(entry, "access", true); - tag.extensionFields.implementation = entryLookupField(entry, "implementation", true); - tag.extensionFields.inheritance = entryLookupField(entry, "inherits", true); - tag.extensionFields.signature = entryLookupField(entry, "signature", true); - tag.extensionFields.typeRef[0] = entryLookupField(entry, "typeref", true); - if (tag.extensionFields.typeRef[0]) - { - char *tmp; - tmp = strchr (tag.extensionFields.typeRef[0], ':'); - if (tmp) - { - *tmp = '\0'; - tag.extensionFields.typeRef[1] = tmp + 1; - } - } - - const char *kindName = NULL; - tag.extensionFields.scopeName = entryGetAnyUnpulledField (entry, &kindName, true); - if (tag.extensionFields.scopeName && kindName) - tag.extensionFields.scopeKind = lookupKindFromName (path, kindName); - - /* TODO: role */ - - makeTagEntry (&tag); - return true; -} - -static bool invokeXcmdPath (const char* const fileName, xcmdPath* path, const langType language) -{ - enum pcoprocError r; - bool result = false; - char* argv[4]; - FILE* pp = NULL; - - if (!path->available) - return false; - - argv[2] = NULL; - argv[1] = (char * const)fileName; - argv[0] = vStringValue (path->path); - - Assert (!(getuid() == 0 || geteuid() == 0)); - if (! isSafeExecutable (argv [0])) - return false; - - verbose ("getting tags of %s language from [%s %s]\n", getLanguageName(language), argv[0], argv[1]); - r = pcoprocOpen (vStringValue (path->path), argv, &pp, NULL); - switch (r) { - case PCOPROC_ERROR_WPIPE: - error (WARNING | PERROR, "failed to make pipe to write to command: [%s %s]", - argv[0], argv[1]); - break; - case PCOPROC_ERROR_RPIPE: - error (WARNING | PERROR, "failed to make pipe to read from command: [%s %s]", - argv[0], argv[1]); - break; - case PCOPROC_ERROR_FORK: - error (WARNING | PERROR, "failed to do fork: [%s %s]", - argv[0], argv[1]); - break; - case PCOPROC_SUCCESSFUL: - break; - } - - if (pp) - { - vString* vline = vStringNew(); - int status; - - while (readLineRawWithNoSeek (vline, pp)) - { - char* line; - tagEntry entry; - - memset(&entry, 0, sizeof(entry)); - vStringStripNewline (vline); - line = vStringValue (vline); - - - if (parseXcmdPath (line, path, &entry) ) - { - entryAddField (&entry, "language", getLanguageName (language)); - - /* Throw away the input file name returned from the xcmd. - Instead we use the input file name arranged - (relative or absolute) by ctags main side. */ - entry.file = getInputFileTagPath (); - - if (makeTagEntryFromTagEntry (path, &entry)) - result = true; - freeTagEntry (&entry); - } - } - - vStringDelete (vline); - - status = pcoprocClose (pp); - verbose(" status: %d\n", status); - if (status) - { - error (WARNING | PERROR, "xcmd exits abnormally status(%d): [%s %s]", - status, argv[0], argv[1]); - return false; - } - } - else - { - error (WARNING | PERROR, "cannot make pipe to xcmd: [%s %s]", - argv[0], argv[1]); - } - - return result; -} - -#endif - -#ifdef HAVE_COPROC -extern bool invokeXcmd (const char* const fileName, const langType language) -{ - bool result = false; - - if (language != LANG_IGNORE && language <= SetUpper && - Sets [language].count > 0) - { - const pathSet* const set = Sets + language; - unsigned int i; - - for (i = 0; i < set->count ; ++i) - { - xcmdPath* path = set->paths + i; - if (invokeXcmdPath (fileName, path, language)) - result = true; - } - - } - return result; -} -#endif diff --git a/ctags/main/lxpath.c b/ctags/main/lxpath.c index c66a310a84..69c9131d81 100644 --- a/ctags/main/lxpath.c +++ b/ctags/main/lxpath.c @@ -12,7 +12,7 @@ #include "debug.h" #include "entry.h" #include "options.h" -#include "parse.h" +#include "parse_p.h" #include "read.h" #include "routines.h" #include "xtag.h" @@ -95,10 +95,10 @@ static void findXMLTagsCore (xmlXPathContext *ctx, xmlNode *root, #if 0 /* Older version of libxml2 doesn't have xmlXPathSetContextNode. */ if (xmlXPathSetContextNode (root, ctx) != 0) - { - error (WARNING, "Failed to set node to XpathContext"); - return; - } + { + error (WARNING, "Failed to set node to XpathContext"); + return; + } #else ctx->node = root; #endif @@ -132,7 +132,7 @@ static xmlDocPtr makeXMLDoc (void) { const unsigned char* data; size_t size; - xmlDocPtr doc = NULL; + xmlDocPtr doc; doc = getInputFileUserData (); if (doc) diff --git a/ctags/main/lxpath.h b/ctags/main/lxpath.h new file mode 100644 index 0000000000..23f4235f92 --- /dev/null +++ b/ctags/main/lxpath.h @@ -0,0 +1,81 @@ +/* +* Copyright (c) 2016, Masatake YAMATO +* Copyright (c) 2016, Red Hat, Inc. +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* Xpath based parer API +*/ +#ifndef CTAGS_LXPATH_PARSE_H +#define CTAGS_LXPATH_PARSE_H + +#include "general.h" /* must always come first */ +#include "types.h" + +#ifdef HAVE_LIBXML +#include +#include +#else +#define xmlNode void +#define xmlXPathCompExpr void +#define xmlXPathContext void +#endif + +typedef struct sTagXpathMakeTagSpec { + int kind; + int role; + /* If make is NULL, just makeTagEntry is used instead. */ + void (*make) (xmlNode *node, + const struct sTagXpathMakeTagSpec *spec, + tagEntryInfo *tag, + void *userData); +} tagXpathMakeTagSpec; + +typedef struct sTagXpathRecurSpec { + void (*enter) (xmlNode *node, + const struct sTagXpathRecurSpec *spec, + xmlXPathContext *ctx, + void *userData); + + int nextTable; /* A parser can use this field any purpose. + main/lxpath part doesn't touch this. */ + +} tagXpathRecurSpec; + +typedef struct sTagXpathTable +{ + const char *const xpath; + enum { LXPATH_TABLE_DO_MAKE, LXPATH_TABLE_DO_RECUR } specType; + union { + tagXpathMakeTagSpec makeTagSpec; + tagXpathRecurSpec recurSpec; + } spec; + xmlXPathCompExpr* xpathCompiled; +} tagXpathTable; + +typedef struct sTagXpathTableTable { + tagXpathTable *table; + unsigned int count; +} tagXpathTableTable; + +typedef struct sXpathFileSpec { + /* + NULL represents the associated field in DTD is not examined. + "" (an empty string) represents the associated field in DTD + (and root element) must not exist. */ + const char *rootElementName; + const char *nameInDTD; + const char *externalID; + const char *systemID; + const char *rootNSPrefix; + const char *rootNSHref; +} xpathFileSpec; + +/* Xpath interface */ +extern void findXMLTags (xmlXPathContext *ctx, xmlNode *root, + const tagXpathTableTable *xpathTableTable, + const kindDefinition* const kinds, void *userData); +extern void addTagXpath (const langType language, tagXpathTable *xpathTable); + +#endif /* CTAGS_LXPATH_PARSE_H */ diff --git a/ctags/main/main.c b/ctags/main/main.c index 0458952cc3..a78976e2bb 100644 --- a/ctags/main/main.c +++ b/ctags/main/main.c @@ -56,24 +56,31 @@ #ifdef HAVE_DIRECT_H # include /* to _getcwd() */ #endif -#ifdef HAVE_DIR_H -# include /* to declare findfirst() and findnext */ -#endif #ifdef HAVE_IO_H # include /* to declare _findfirst() */ #endif +#include "ctags.h" #include "debug.h" -#include "entry.h" +#include "entry_p.h" #include "error.h" #include "field.h" -#include "keyword.h" +#include "keyword_p.h" #include "main.h" -#include "options.h" -#include "output.h" +#include "options_p.h" +#include "parse_p.h" #include "read.h" -#include "routines.h" +#include "routines_p.h" +#include "trace.h" +#include "trashbox.h" +#include "writer_p.h" + +#ifdef HAVE_JANSSON +#include "interactive_p.h" +#include +#include +#endif /* * MACROS @@ -113,11 +120,14 @@ extern bool isDestinationStdout (void) { bool toStdout = false; - if (outpuFormatUsedStdoutByDefault() || Option.filter || + if (Option.filter || Option.interactive || (Option.tagFileName != NULL && (strcmp (Option.tagFileName, "-") == 0 || strcmp (Option.tagFileName, "/dev/stdout") == 0 ))) toStdout = true; + else if (Option.tagFileName == NULL && NULL == outputDefaultFileName ()) + toStdout = true; + return toStdout; } @@ -143,10 +153,10 @@ static bool recurseUsingOpendir (const char *const dirName) if (strcmp (dirName, ".") == 0) filePath = entry->d_name; else - { + { filePath = combinePathAndFile (dirName, entry->d_name); free_p = true; - } + } resize |= createTagsForEntry (filePath); if (free_p) eFree (filePath); @@ -157,7 +167,7 @@ static bool recurseUsingOpendir (const char *const dirName) return resize; } -#elif defined (HAVE_FINDFIRST) || defined (HAVE__FINDFIRST) +#elif defined (HAVE__FINDFIRST) static bool createTagsForWildcardEntry ( const char *const pattern, const size_t dirLength, @@ -180,16 +190,7 @@ static bool createTagsForWildcardUsingFindfirst (const char *const pattern) { bool resize = false; const size_t dirLength = baseFilename (pattern) - pattern; -#if defined (HAVE_FINDFIRST) - struct ffblk fileInfo; - int result = findfirst (pattern, &fileInfo, FA_DIREC); - while (result == 0) - { - const char *const entry = (const char *) fileInfo.ff_name; - resize |= createTagsForWildcardEntry (pattern, dirLength, entry); - result = findnext (&fileInfo); - } -#elif defined (HAVE__FINDFIRST) +#if defined (HAVE__FINDFIRST) struct _finddata_t fileInfo; findfirst_t hFile = _findfirst (pattern, &fileInfo); if (hFile != -1L) @@ -227,7 +228,7 @@ static bool recurseIntoDirectory (const char *const dirName) verbose ("RECURSING into directory \"%s\"\n", dirName); #if defined (HAVE_OPENDIR) resize = recurseUsingOpendir (dirName); -#elif defined (HAVE_FINDFIRST) || defined (HAVE__FINDFIRST) +#elif defined (HAVE__FINDFIRST) { vString *const pattern = vStringNew (); vStringCopyS (pattern, dirName); @@ -275,9 +276,9 @@ static bool createTagsForWildcardArg (const char *const arg) vString *const pattern = vStringNewInit (arg); char *patternS = vStringValue (pattern); -#if defined (HAVE_FINDFIRST) || defined (HAVE__FINDFIRST) +#if defined (HAVE__FINDFIRST) /* We must transform the "." and ".." forms into something that can - * be expanded by the findfirst/_findfirst functions. + * be expanded by the _findfirst function. */ if (Option.recurse && (strcmp (patternS, ".") == 0 || strcmp (patternS, "..") == 0)) @@ -490,6 +491,102 @@ static void batchMakeTags (cookedArgs *args, void *user CTAGS_ATTR_UNUSED) #undef timeStamp } +#ifdef HAVE_JANSSON +void interactiveLoop (cookedArgs *args CTAGS_ATTR_UNUSED, void *user) +{ + struct interactiveModeArgs *iargs = user; + + if (iargs->sandbox) { + /* As of jansson 2.6, the object hashing is seeded off + of /dev/urandom, so trigger the hash seeding + before installing the syscall filter. + */ + json_t * tmp = json_object (); + json_decref (tmp); + + if (installSyscallFilter ()) { + error (FATAL, "install_syscall_filter failed"); + /* The explicit exit call is needed because + "error (FATAL,..." just prints a message in + interactive mode. */ + exit (1); + } + } + + char buffer[1024]; + json_t *request; + + fputs ("{\"_type\": \"program\", \"name\": \"" PROGRAM_NAME "\", \"version\": \"" PROGRAM_VERSION "\"}\n", stdout); + fflush (stdout); + + while (fgets (buffer, sizeof(buffer), stdin)) + { + if (buffer[0] == '\n') + continue; + + request = json_loads (buffer, JSON_DISABLE_EOF_CHECK, NULL); + if (! request) + { + error (FATAL, "invalid json"); + goto next; + } + + json_t *command = json_object_get (request, "command"); + if (! command) + { + error (FATAL, "command name not found"); + goto next; + } + + if (!strcmp ("generate-tags", json_string_value (command))) + { + json_int_t size = -1; + const char *filename; + + if (json_unpack (request, "{ss}", "filename", &filename) == -1) + { + error (FATAL, "invalid generate-tags request"); + goto next; + } + + json_unpack (request, "{sI}", "size", &size); + + openTagFile (); + if (size == -1) + { /* read from disk */ + if (iargs->sandbox) { + error (FATAL, + "invalid request in sandbox submode: reading file contents from a file is limited"); + goto next; + } + + createTagsForEntry (filename); + } + else + { /* read nbytes from stream */ + unsigned char *data = eMalloc (size); + size = fread (data, 1, size, stdin); + MIO *mio = mio_new_memory (data, size, eRealloc, eFree); + parseFileWithMio (filename, mio); + mio_free (mio); + } + + closeTagFile (false); + fputs ("{\"_type\": \"completed\", \"command\": \"generate-tags\"}\n", stdout); + fflush(stdout); + } + else + { + error (FATAL, "unknown command name"); + goto next; + } + + next: + json_decref (request); + } +} +#endif + static bool isSafeVar (const char* var) { const char *safe_vars[] = { @@ -508,17 +605,21 @@ static bool isSafeVar (const char* var) static void sanitizeEnviron (void) { - char **e = NULL; + char **e; int i; #if HAVE_DECL___ENVIRON e = __environ; #elif HAVE_DECL__NSGETENVIRON -{ - char ***ep = _NSGetEnviron(); - if (ep) - e = *ep; -} + { + char ***ep = _NSGetEnviron(); + if (ep) + e = *ep; + else + e = NULL; + } +#else + e = NULL; #endif if (!e) @@ -551,15 +652,20 @@ extern int main (int argc CTAGS_ATTR_UNUSED, char **argv) { cookedArgs *args; + initDefaultTrashBox (); + + DEBUG_INIT(); + setErrorPrinter (stderrDefaultErrorPrinter, NULL); setMainLoop (batchMakeTags, NULL); - setTagWriter (&ctagsWriter); + setTagWriter (WRITER_U_CTAGS); setCurrentDirectory (); setExecutableName (*argv++); sanitizeEnviron (); checkRegex (); - initFieldDescs (); + initFieldObjects (); + initXtagObjects (); args = cArgNewFromArgv (argv); previewFirstOption (args); @@ -573,6 +679,14 @@ extern int main (int argc CTAGS_ATTR_UNUSED, char **argv) runMainLoop (args); + + BEGIN_VERBOSE_IF(Option.mtablePrintTotals, vfp); + { + for (unsigned int i = 0; i < countParsers(); i++) + printLanguageMultitableStatistics (i, vfp); + } + END_VERBOSE(); + /* Clean up. */ cArgDelete (args); @@ -583,11 +697,12 @@ extern int main (int argc CTAGS_ATTR_UNUSED, char **argv) freeOptionResources (); freeParserResources (); freeRegexResources (); - freeXcmdResources (); #ifdef HAVE_ICONV freeEncodingResources (); #endif + finiDefaultTrashBox(); + if (Option.printLanguage) return (Option.printLanguage == true)? 0: 1; diff --git a/ctags/main/nestlevel.c b/ctags/main/nestlevel.c index 1f777a28b1..feba045bdf 100644 --- a/ctags/main/nestlevel.c +++ b/ctags/main/nestlevel.c @@ -15,6 +15,7 @@ #include "main.h" #include "debug.h" +#include "entry.h" #include "routines.h" #include "nestlevel.h" diff --git a/ctags/main/nestlevel.h b/ctags/main/nestlevel.h index a209ae706c..a95236fb80 100644 --- a/ctags/main/nestlevel.h +++ b/ctags/main/nestlevel.h @@ -20,7 +20,6 @@ /* * DATA DECLARATIONS */ -typedef struct NestingLevel NestingLevel; typedef struct NestingLevels NestingLevels; struct NestingLevel diff --git a/ctags/main/options.c b/ctags/main/options.c index 73cb3b2f8e..e91f4e9ccb 100644 --- a/ctags/main/options.c +++ b/ctags/main/options.c @@ -12,6 +12,9 @@ */ #include "general.h" /* must always come first */ +#define OPTION_WRITE +#include "options_p.h" + #ifndef _GNU_SOURCE # define _GNU_SOURCE /* for asprintf */ #endif @@ -20,36 +23,33 @@ #include #include /* to declare isspace () */ -#if defined(HAVE_SCANDIR) -#include -#endif - #include "ctags.h" #include "debug.h" #include "field.h" +#include "gvars.h" +#include "keyword.h" #include "main.h" -#define OPTION_WRITE -#include "options.h" -#include "output.h" -#include "parse.h" -#include "ptag.h" -#include "routines.h" +#include "parse_p.h" +#include "ptag_p.h" +#include "routines_p.h" #include "xtag.h" -#include "routines.h" +#include "param.h" +#include "error.h" +#include "writer_p.h" +#include "trace.h" + +#ifdef HAVE_JANSSON +#include +#endif /* * MACROS */ #define INVOCATION "Usage: %s [options] [file(s)]\n" -#define CTAGS_DATA_PATH_ENVIRONMENT "CTAGS_DATA_PATH" -#define CTAGS_LIBEXEC_PATH_ENVIRONMENT "CTAGS_LIBEXEC_PATH" #define CTAGS_ENVIRONMENT "CTAGS" #define ETAGS_ENVIRONMENT "ETAGS" -#define CTAGS_FILE "tags" -#define ETAGS_FILE "TAGS" - #ifndef ETAGS # define ETAGS "etags" /* name which causes default use of to -e */ #endif @@ -65,19 +65,19 @@ # define DEFAULT_FILE_FORMAT 2 #endif -#if defined (HAVE_OPENDIR) || defined (HAVE_FINDFIRST) || defined (HAVE__FINDFIRST) +#if defined (HAVE_OPENDIR) || defined (HAVE__FINDFIRST) # define RECURSE_SUPPORTED #endif -#define isCompoundOption(c) (bool) (strchr ("fohiILpDb", (c)) != NULL) - -#define SUBDIR_OPTLIB "optlib" -#define SUBDIR_PRELOAD "preload" -#define SUBDIR_DRIVERS "drivers" +#define isCompoundOption(c) (bool) (strchr ("fohiILpdDb", (c)) != NULL) -#define ENTER(STAGE) do { \ - Assert (Stage <= OptionLoadingStage##STAGE); \ - Stage = OptionLoadingStage##STAGE; \ +#define ENTER(STAGE) do { \ + Assert (Stage <= OptionLoadingStage##STAGE); \ + if (Stage != OptionLoadingStage##STAGE) \ + { \ + Stage = OptionLoadingStage##STAGE; \ + verbose ("Entering configuration stage: loading %s\n", StageDescription[Stage]); \ + } \ } while (0) #define ACCEPT(STAGE) (1UL << OptionLoadingStage##STAGE) @@ -109,21 +109,18 @@ typedef const struct sBooleanOption { bool* pValue; /* pointer to option value */ bool initOnly; /* option must be specified before any files */ unsigned long acceptableStages; - bool* (* redirect) (const struct sBooleanOption *const option); + void (* set) (const struct sBooleanOption *const option, bool value); } booleanOption; /* * DATA DEFINITIONS */ -#ifndef CTAGS_LIB static bool NonOptionEncountered = false; static stringList *OptionFiles; typedef stringList searchPathList; static searchPathList *OptlibPathList; -static searchPathList *PreloadPathList; -static searchPathList *DriversPathList; static stringList* Excluded; static bool FilesRequired = true; @@ -132,77 +129,70 @@ static bool SkipConfiguration; static const char *const HeaderExtensions [] = { "h", "H", "hh", "hpp", "hxx", "h++", "inc", "def", NULL }; -#endif + +long ctags_debugLevel = 0L; +bool ctags_verbose = false; optionValues Option = { - NULL, /* -I */ - false, /* -a */ - false, /* -B */ - false, /* -e */ -#ifdef CTAGS_LIB - EX_LINENUM, -#else + .append = false, + .backward = false, + .etags = false, + .locate = #ifdef MACROS_USE_PATTERNS - EX_PATTERN, /* -n, --excmd */ + EX_PATTERN #else - EX_MIX, /* -n, --excmd */ + EX_MIX #endif -#endif - false, /* -R */ - SO_SORTED, /* -u, --sort */ - false, /* -V */ - false, /* -x */ + , + .recurse = false, + .sorted = SO_SORTED, + .xref = false, .customXfmt = NULL, - NULL, /* -L */ - NULL, /* -o */ - NULL, /* -h */ - NULL, /* --config-filename */ - NULL, /* --etags-include */ - DEFAULT_FILE_FORMAT,/* --format */ + .fileList = NULL, + .tagFileName = NULL, + .headerExt = NULL, + .etagsInclude = NULL, + .tagFileFormat = DEFAULT_FILE_FORMAT, #ifdef HAVE_ICONV - NULL, /* --input-encoding */ - NULL, /* --output-encoding */ -#endif - false, /* --if0 */ - LANG_AUTO, /* --lang */ - true, /* --links */ - false, /* --filter */ - NULL, /* --filter-terminator */ - false, /* --tag-relative */ - false, /* --totals */ - false, /* --line-directives */ - false, /* --print-language */ - false, /* --guess-language-eagerly(-G) */ - false, /* --quiet */ - false, /* --_allow-xcmd-in-homedir */ - false, /* --_fatal-warnings */ + .inputEncoding= NULL, + .outputEncoding = NULL, +#endif + .language = LANG_AUTO, + .followLinks = true, + .filter = false, + .filterTerminator = NULL, + .tagRelative = false, + .printTotals = false, + .lineDirectives = false, + .printLanguage =false, + .guessLanguageEagerly = false, + .quiet = false, + .fatalWarnings = false, .patternLengthLimit = 96, .putFieldPrefix = false, .maxRecursionDepth = 0xffffffff, - .machinable = false, - .withListHeader = true, + .interactive = false, + .mtablePrintTotals = false, #ifdef DEBUG - 0, 0 /* -D, -b */ + .breakLine = 0, #endif }; -#ifndef CTAGS_LIB +struct localOptionValues { + bool machinable; /* --machinable */ + bool withListHeader; /* --with-list-header */ +} localOption = { + .machinable = false, + .withListHeader = true, +}; + static OptionLoadingStage Stage = OptionLoadingStageNone; #define STAGE_ANY ~0UL -#endif - -/* GEANY DIFF */ -/* tags_ignore is a NULL-terminated array of strings, read from ~/.config/geany/ignore.tags. - * This file contains a space or newline separated list of symbols which should be ignored - * by the C/C++ parser, see -I command line option of ctags for details. */ -char **c_tags_ignore = NULL; -/* GEANY DIFF END */ /* - Locally used only */ -#ifndef CTAGS_LIB static optionDescription LongOptionDescription [] = { {1," -a Append the tags to an existing tag file."}, #ifdef DEBUG @@ -211,9 +201,11 @@ static optionDescription LongOptionDescription [] = { #endif {0," -B Use backward searching patterns (?...?)."}, #ifdef DEBUG - {1," -D "}, + {1," -d "}, {1," Set debug level."}, #endif + {1," -D ="}, + {1," (CPreProcessor) Give definition for macro."}, {0," -e Output tag file for use with Emacs."}, {1," -f "}, {1," Write tags to specified file. Value of \"-\" writes tags to stdout"}, @@ -245,10 +237,6 @@ static optionDescription LongOptionDescription [] = { {1," for LANG."}, {1," --append=[yes|no]"}, {1," Should tags should be appended to existing tag file [no]?"}, - {1," --config-filename=fileName"}, - {1," Use 'fileName' instead of 'ctags' in option file names."}, - {1," --data-dir=[+]DIR"}, - {1," Add or set DIR to data directory search path."}, {1," --etags-include=file"}, {1," Include reference to 'file' in Emacs-style tag file (requires -e)."}, {1," --exclude=pattern"}, @@ -259,13 +247,16 @@ static optionDescription LongOptionDescription [] = { #else {0," Uses the specified type of EX command to locate tags [mix]."}, #endif - {1," --extra=[+|-]flags"}, - {1," Include extra tag entries for selected information (flags: \"Ffq.\") [F]."}, + {1," --extras=[+|-]flags"}, + {1," Include extra tag entries for selected information (flags: \"fFgpqrs\") [F]."}, + {1," --extras-=[+|-]flags"}, + {1," Include own extra tag entries for selected information"}, + {1," (flags: see the output of --list-extras= option)."}, {1," --fields=[+|-]flags"}, - {1," Include selected extension fields (flags: \"afmikKlnsStzZ\") [fks]."}, + {1," Include selected extension fields (flags: \"aCeEfFikKlmnNpPrRsStxzZ\") [fks]."}, {1," --fields-=[+|-]flags"}, {1," Include selected own extension fields"}, - {1," (flags: --list-fields=)."}, + {1," (flags: see the output of --list-fields= option)."}, {1," --file-scope=[yes|no]"}, {1," Should tags scoped only for a single file (e.g. \"static\" tags)"}, {1," be included in the output [yes]?"}, @@ -289,6 +280,8 @@ static optionDescription LongOptionDescription [] = { {1," o vim syntax specification at the end of input file."}, {1," --help"}, {1," Print this option summary."}, + {1," --help-full"}, + {1," Print this option summary including experimental features."}, {1," --if0=[yes|no]"}, {1," Should code within #if 0 conditional branches be parsed [no]?"}, #ifdef HAVE_ICONV @@ -297,6 +290,8 @@ static optionDescription LongOptionDescription [] = { {1," --input-encoding-=encoding"}, {1," Specify encoding of the LANG input files."}, #endif + {1," --kinddef-=letter,name,desc"}, + {1," Define new kind for ."}, {1," --kinds-=[+|-]kinds, or"}, {1," ---kinds=[+|-]kinds"}, {1," Enable/disable tag kinds for language ."}, @@ -310,8 +305,6 @@ static optionDescription LongOptionDescription [] = { {1," Restrict files scanned for tags to those mapped to languages"}, {1," specified in the comma-separated 'list'. The list can contain any"}, {1," built-in or user-defined language [all]."}, - {1," --libexec-dir=[+]DIR"}, - {1," Add or set DIR to libexec directory search path."}, {1," --license"}, {1," Print details of software license."}, {0," --line-directives=[yes|no]"}, @@ -320,60 +313,79 @@ static optionDescription LongOptionDescription [] = { {1," Indicate whether symbolic links should be followed [yes]."}, {1," --list-aliases=[language|all]"}, {1," Output list of alias patterns."}, - {1," --list-extensions=[language|all]"}, - {1," Output list of language extensions in mapping."}, - {1," --list-extra"}, + {1," --list-excludes"}, + {1," Output list of exclude patterns for excluding files/directories."}, + {1," --list-extras=[language|all]"}, {1," Output list of extra tag flags."}, {1," --list-features"}, - {1," Output list of features."}, + {1," Output list of compiled features."}, {1," --list-fields=[language|all]"}, - {1," Output list of fields. This works with --machinable."}, - {1," --list-file-kind"}, - {1," List kind letter for file."}, + {1," Output list of fields."}, {1," --list-kinds=[language|all]"}, {1," Output a list of all tag kinds for specified language or all."}, {1," --list-kinds-full=[language|all]"}, {1," List the details of all tag kinds for specified language or all"}, {1," For each line, associated language name is printed when \"all\" is"}, - {1," specified as language. This works with --machinable."}, + {1," specified as language."}, {1," --list-languages"}, {1," Output list of supported languages."}, + {1," --list-map-extensions=[language|all]"}, + {1," Output list of language extensions in mapping."}, + {1," --list-map-patterns=[language|all]"}, + {1," Output list of language patterns in mapping."}, {1," --list-maps=[language|all]"}, {1," Output list of language mappings(both extensions and patterns)."}, - {1," --list-patterns=[language|all]"}, - {1," Output list of language patterns in mapping."}, + {1," --list-mline-regex-flags"}, + {1," Output list of flags which can be used in a multiline regex parser definition."}, + {1," --list-params=[language|all]"}, + {1," Output list of language parameters. This works with --machinable."}, {0," --list-pseudo-tags"}, {0," Output list of pseudo tags."}, {1," --list-regex-flags"}, {1," Output list of flags which can be used in a regex parser definition."}, + {1," --list-roles=[[language|all].[kindspecs|*]]"}, + {1," Output list of all roles of tag kind(s) specified for language(s)."}, + {1," Both letters and names can be used in kindspecs."}, + {1," e.g. --list-roles=C.{header}d"}, + {1," --list-subparsers=[baselang|all]"}, + {1," Output list of subparsers for the base language."}, {1," --machinable=[yes|no]"}, {1," Use tab separated representation in --list- option output. [no]"}, - {1," --list-extra, --list-fields, and --list-kinds-full support this option."}, + {1," --list-{aliases,extras,features,fields,kind-full,langdef-flags,params," }, + {1," pseudo-tags,regex-flags,roles,subparsers} support this option."}, {1," Suitable for scripting. Specify before --list-* option."}, - {1," --map-=[+|-]pattern|extension"}, - {1," Set or add(+) a map for ."}, - {1," Unlike --langmap, only one pattern or one extension can be specified"}, - {1," at once. Unlike, --langmap adding one affects the map of LANG; it does"}, - {1," not affect the maps of the other languages."}, + {1," --map-=[+|-]extension|pattern"}, + {1," Set, add(+) or remove(-) the map for ."}, + {1," Unlike --langmap, this doesn't take a list; only one file name pattern"}, + {1," or one file extension can be specified at once."}, + {1," Unlike --langmap the change with this option affects mapping of only."}, {1," --maxdepth=N"}, #ifdef RECURSE_SUPPORTED {1," Specify maximum recursion depth."}, #else {1," Not supported on this platform."}, #endif - {1," --options=file"}, - {1," Specify file from which command line options should be read."}, + {1," --mline-regex-=/line_pattern/name_pattern/[flags]"}, + {1," Define multiline regular expression for locating tags in specific language."}, + {1," --options=path"}, + {1," Specify file(or directory) from which command line options should be read."}, + {1," --options-maybe=path"}, + {1," Do the same as --options but this doesn't make an error for non-existing file."}, + {1," --optlib-dir=[+]DIR"}, + {1," Add or set DIR to optlib search path."}, #ifdef HAVE_ICONV {1," --output-encoding=encoding"}, {1," The encoding to write the tag file in. Defaults to UTF-8 if --input-encoding"}, {1," is specified, otherwise no conversion is performed."}, #endif - {0," --output-format=ctags|etags|xref" + {0," --output-format=u-ctags|e-ctags|etags|xref" #ifdef HAVE_JANSSON "|json" #endif }, - {0," Specify the output format. [ctags]"}, + {0," Specify the output format. [u-ctags]"}, + {1," --param-:name=argument"}, + {1," Set specific parameter. Available parameters can be listed with --list-params."}, {0," --pattern-length-limit=N"}, {0," Cutoff patterns of tag entries after N characters. Disable by setting to 0. [96]"}, {0," --print-language"}, @@ -398,8 +410,10 @@ static optionDescription LongOptionDescription [] = { {1," Define regular expression for locating tags in specific language."}, {0," --sort=[yes|no|foldcase]"}, {0," Should tags be sorted (optionally ignoring case) [yes]?"}, - {0," --tag-relative=[yes|no]"}, + {0," --tag-relative=[yes|no|always|never]"}, {0," Should paths be relative to location of tag file [no; yes when -e]?"}, + {0," always: be relative even if input files are passed in with absolute paths" }, + {0," never: be absolute even if input files are passed in with relative paths" }, {1," --totals=[yes|no]"}, {1," Print statistics about input and tag files [no]."}, {1," --verbose=[yes|no]"}, @@ -407,29 +421,65 @@ static optionDescription LongOptionDescription [] = { {1," --version"}, {1," Print version identifier to standard output."}, {1," --with-list-header=[yes|no]"}, - {1," Preprend the column descriptions in --list- output. [yes]"}, - {1," --list-extra, --list-fields, and --list-kinds-full support this option."}, + {1," Prepend the column descriptions in --list- output. [yes]"}, + {1," --list-{aliases,extras,features,fields,kind-full,langdef-flags,params," }, + {1," pseudo-tags,regex-flags,roles,subparsers} support this option."}, {1," Specify before --list-* option."}, - {1," --list-fields, and --list-kinds-full support this option."}, -#ifdef HAVE_COPROC - {1," --xcmd-=parser_command_path|parser_command_name"}, - {1," Define external parser command path or name for specific language."}, -#endif - {1," --_allow-xcmd-in-homedir"}, - {1," Allow specifying --xcmd- option in ~/.ctags and/or ~/.ctags/*."}, - {1," By default it is not allowed. This option itself can be specified only"}, - {1," in /etc or /usr/local/etc."}, + {1, NULL} +}; + +static optionDescription ExperimentalLongOptionDescription [] = { + {1," --_anonhash=fname"}, + {1," Used in u-ctags test harness"}, + {1," --_dump-keywords"}, + {1," Dump keywords of initialized parser(s)."}, + {1," --_dump-options"}, + {1," Dump options."}, {1," --_echo=msg"}, {1," Echo MSG to standard error. Useful to debug the chain"}, {1," of loading option files."}, + {1," --_extradef-=name,desc"}, + {1," Define new extra for . \"--extra-=+{name}\" enables it."}, {1," --_fatal-warnings"}, {1," Make all warnings fatal."}, + {1," --_fielddef-=name,description"}, + {1," EXPERIMENTAL, Define new field for ."}, + {1," --_force-initializing"}, + {1," Initialize all parsers in early stage"}, {1," --_force-quit=[num]"}, {1," Quit when the option is processed. Useful to debug the chain"}, {1," of loading option files."}, - {1," --_list-roles=[[language|all]:[kindletters|*]]"}, - {1," Output list of all roles of tag kind(s) specified for language(s)."}, - {1," e.g. --_list-roles=Make:I"}, +#ifdef HAVE_JANSSON + {0," --_interactive" +#ifdef HAVE_SECCOMP + "=[default|sandbox]" +#endif + }, + {0," Enter interactive mode (json over stdio)."}, +#ifdef HAVE_SECCOMP + {0," Enter file I/O limited interactive mode if sandbox is specified. [default]"}, +#endif +#endif + {1," --_list-kinddef-flags"}, + {1," Output list of flags which can be used with --kinddef option."}, + {1," --_list-langdef-flags"}, + {1," Output list of flags which can be used with --langdef option."}, + {1," --_list-mtable-regex-flags"}, + {1," Output list of flags which can be used in a multitable regex parser definition."}, + {1," --_mtable-extend-=disttable+srctable."}, + {1," Copy patterns of a regex table to another regex table."}, + {1," --_mtable-regex-=table/line_pattern/name_pattern/[flags]"}, + {1," Define multitable regular expression for locating tags in specific language."}, + {1," --_mtable-totals=[yes|no]"}, + {1," Print statistics about mtable usage [no]."}, + {1," --_roledef-=kind_letter.role_name,role_desc"}, + {1," Define new role for kind specified with in ."}, + {1," --_tabledef-=name"}, + {1," Define new regex table for ."}, +#ifdef DO_TRACING + {1," --_trace=list"}, + {1," Trace parsers for the languages."}, +#endif {1," --_xformat=field_format"}, {1," Specify custom format for tabular cross reference (-x)."}, {1," Fields can be specified with letter listed in --list-fields."}, @@ -456,43 +506,58 @@ static const char* const License2 = /* Contains a set of strings describing the set of "features" compiled into * the code. */ -static const char *const Features [] = { +static struct Feature { + const char *name; + const char *description; +} Features [] = { #ifdef WIN32 - "win32", + {"win32", "TO BE WRITTEN"}, #endif - "wildcards", /* Always available on universal ctags */ - "regex", /* Always available on universal ctags */ + /* Following two features are always available on universal ctags */ + {"wildcards", "can use glob matching"}, + {"regex", "can use regular expression based pattern matching"}, + #ifndef EXTERNAL_SORT - "internal-sort", + {"internal-sort", "uses internal sort routine instead of invoking sort command"}, #endif #ifdef CUSTOM_CONFIGURATION_FILE - "custom-conf", + {"custom-conf", "read \"" CUSTOM_CONFIGURATION_FILE "\" as config file"}, #endif #if defined (WIN32) && defined (UNIX_PATH_SEPARATOR) - "unix-path-separator", + {"unix-path-separator", "can use '/' as file name separator"}, #endif #ifdef HAVE_ICONV - "multibyte", + {"iconv", "can convert input/output encodings"}, #endif #ifdef DEBUG - "debug", -#endif -#ifdef HAVE_SCANDIR - "option-directory", + {"debug", "TO BE WRITTEN"}, #endif -#ifdef HAVE_COPROC - "coproc", +#if defined(HAVE_SCANDIR) || defined (HAVE_DIRENT_H) || defined (_MSC_VER) + {"option-directory", "TO BE WRITTEN"}, #endif #ifdef HAVE_LIBXML - "xpath", + {"xpath", "linked with library for parsing xml input"}, #endif #ifdef HAVE_JANSSON - "json", + {"json", "supports json format output"}, + {"interactive", "accepts source code from stdin"}, +#endif +#ifdef HAVE_SECCOMP + {"sandbox", "linked with code for system call level sandbox"}, #endif #ifdef HAVE_LIBYAML - "yaml", + {"yaml", "linked with library for parsing yaml input"}, +#endif +#ifdef CASE_INSENSITIVE_FILENAMES + {"case-insensitive-filenames", "TO BE WRITTEN"}, +#endif +#ifdef ENABLE_GCOV + {"gcov", "linked with code for coverage analysis"}, #endif - NULL +#ifdef HAVE_ASPELL + {"aspell", "linked with code for spell checking (internal use)"}, +#endif + {NULL,} }; static const char *const StageDescription [] = { @@ -514,40 +579,13 @@ static const char *const StageDescription [] = { static bool parseFileOptions (const char *const fileName); static bool parseAllConfigurationFilesOptionsInDirectory (const char *const fileName, stringList* const already_loaded_files); +static bool getBooleanOption (const char *const option, const char *const parameter); /* * FUNCTION DEFINITIONS */ - -static vString* getHome (void) -{ - const char* const home = getenv ("HOME"); - - if (home) - return vStringNewInit (home); - else - { -#ifdef MSDOS_STYLE_PATH - /* - * Windows users don't usually set HOME. - * The OS sets HOMEDRIVE and HOMEPATH for them. - */ - const char* homeDrive = getenv ("HOMEDRIVE"); - const char* homePath = getenv ("HOMEPATH"); - if (homeDrive != NULL && homePath != NULL) - { - vString* const windowsHome = vStringNew (); - vStringCatS (windowsHome, homeDrive); - vStringCatS (windowsHome, homePath); - return windowsHome; - } -#endif - return NULL; - } -} - -#if defined(_WIN32) && !(defined(__USE_MINGW_ANSI_STDIO) && defined(__MINGW64_VERSION_MAJOR)) +#ifndef HAVE_ASPRINTF /* Some versions of MinGW are missing _vscprintf's declaration, although they * still provide the symbol in the import library. @@ -601,7 +639,7 @@ int asprintf(char **strp, const char *fmt, ...) extern void verbose (const char *const format, ...) { - if (Option.verbose) + if (ctags_verbose) { va_list ap; va_start (ap, format); @@ -640,7 +678,6 @@ static void freeString (char **const pString) *pString = NULL; } } -#endif extern void freeList (stringList** const pList) { @@ -651,15 +688,21 @@ extern void freeList (stringList** const pList) } } -#ifndef CTAGS_LIB extern void setDefaultTagFileName (void) + { - if (Option.tagFileName != NULL) - ; /* accept given name */ - else if (Option.etags) - Option.tagFileName = stringCopy (ETAGS_FILE); - else - Option.tagFileName = stringCopy (CTAGS_FILE); + if (Option.filter || Option.interactive) + return; + + if (Option.tagFileName == NULL) + { + const char *tmp = outputDefaultFileName (); + + if (tmp == NULL) + tmp = "-"; + + Option.tagFileName = stringCopy (tmp); + } } extern bool filesRequired (void) @@ -702,26 +745,32 @@ extern void checkOptions (void) } extern langType getLanguageComponentInOption (const char *const option, - const char *const prefix) + const char *const prefix) { - size_t len; + size_t prefix_len; langType language; const char *lang; + char *colon = NULL; + size_t lang_len = 0; Assert (prefix && prefix[0]); Assert (option); - len = strlen (prefix); - if (strncmp (option, prefix, len) != 0) + prefix_len = strlen (prefix); + if (strncmp (option, prefix, prefix_len) != 0) return LANG_IGNORE; else { - lang = option + len; + lang = option + prefix_len; if (lang [0] == '\0') return LANG_IGNORE; } - language = getNamedLanguage (lang, 0); + /* --param-:=... */ + colon = strchr (lang, ':'); + if (colon) + lang_len = colon - lang; + language = getNamedLanguage (lang, lang_len); if (language == LANG_IGNORE) error (FATAL, "Unknown language \"%s\" in \"%s\" option", lang, option); @@ -733,8 +782,8 @@ static void setEtagsMode (void) Option.etags = true; Option.sorted = SO_UNSORTED; Option.lineDirectives = false; - Option.tagRelative = true; - setTagWriter (&etagsWriter); + Option.tagRelative = TREL_YES; + setTagWriter (WRITER_ETAGS); } extern void testEtagsInvocation (void) @@ -757,14 +806,16 @@ extern void testEtagsInvocation (void) static void setXrefMode (void) { Option.xref = true; - setTagWriter (&xrefWriter); + setTagWriter (WRITER_XREF); } #ifdef HAVE_JANSSON static void setJsonMode (void) { enablePtag (PTAG_JSON_OUTPUT_VERSION, true); - setTagWriter (&jsonWriter); + enablePtag (PTAG_OUTPUT_MODE, false); + enablePtag (PTAG_FILE_FORMAT, false); + setTagWriter (WRITER_JSON); } #endif @@ -997,7 +1048,8 @@ static bool isFalse (const char *parameter) strcasecmp (parameter, "0" ) == 0 || strcasecmp (parameter, "n" ) == 0 || strcasecmp (parameter, "no" ) == 0 || - strcasecmp (parameter, "off") == 0); + strcasecmp (parameter, "off") == 0 || + strcasecmp (parameter, "false") == 0 ); } static bool isTrue (const char *parameter) @@ -1006,7 +1058,25 @@ static bool isTrue (const char *parameter) strcasecmp (parameter, "1" ) == 0 || strcasecmp (parameter, "y" ) == 0 || strcasecmp (parameter, "yes") == 0 || - strcasecmp (parameter, "on" ) == 0); + strcasecmp (parameter, "on" ) == 0 || + strcasecmp (parameter, "true" ) == 0); +} + +extern bool paramParserBool (const char *value, bool fallback, + const char *errWhat, const char *errCategory) +{ + bool r = fallback; + + if (value [0] == '\0') + r = true; + else if (isFalse (value)) + r = false; + else if (isTrue (value)) + r = true; + else + error (FATAL, "Invalid value for \"%s\" %s", errWhat, errCategory); + + return r; } /* Determines whether the specified file name is considered to be a header @@ -1026,13 +1096,6 @@ extern bool isIncludeFile (const char *const fileName) * Specific option processing */ - static void processConfigFilenameOption ( - const char *const option CTAGS_ATTR_UNUSED, const char *const parameter) - { - freeString (&Option.configFilename); - Option.configFilename = stringCopy (parameter); - } - static void processEtagsInclude ( const char *const option, const char *const parameter) { @@ -1068,6 +1131,9 @@ static void processExcludeOption ( else { vString *const item = vStringNewInit (parameter); +#if defined (WIN32) + vStringTranslate(item, '\\', '/'); +#endif if (Excluded == NULL) Excluded = stringListNew (); stringListAdd (Excluded, item); @@ -1102,11 +1168,12 @@ static void processExcmdOption ( } } -static void resetXtags (bool mode) +static void resetXtags (langType lang, bool mode) { int i; - for (i = 0; i < XTAG_COUNT; i++) - enableXtag (i, mode); + for (i = 0; i < countXtags (); i++) + if ((lang == LANG_AUTO) || (lang == getXtagOwner (i))) + enableXtag (i, mode); } static void processExtraTagsOption ( @@ -1120,15 +1187,18 @@ static void processExtraTagsOption ( bool inLongName = false; const char *x; + if (strcmp (option, "extra") == 0) + error(WARNING, "--extra option is obsolete; use --extras instead"); + if (*p == '*') { - resetXtags (true); + resetXtags (LANG_IGNORE, true); p++; } else if (*p != '+' && *p != '-') - resetXtags (false); + resetXtags (LANG_IGNORE, false); - longName = vStringNewOrClear (longName); + longName = vStringNewOrClearWithAutoRelease (longName); while ((c = *p++) != '\0') { @@ -1159,7 +1229,7 @@ static void processExtraTagsOption ( "unexpected character in extra specification: \'%c\'", c); x = vStringValue (longName); - t = getXtagTypeForName (x); + t = getXtagTypeForNameAndLanguage (x, LANG_IGNORE); if (t == XTAG_UNKNOWN) error(WARNING, "Unsupported parameter '{%s}' for \"%s\" option", @@ -1207,7 +1277,7 @@ static void processFieldsOption ( static vString * longName; bool inLongName = false; - longName = vStringNewOrClear (longName); + longName = vStringNewOrClearWithAutoRelease (longName); if (*p == '*') { @@ -1250,7 +1320,7 @@ static void processFieldsOption ( } if (t == FIELD_UNKNOWN) - error(FATAL, "nosuch field: \'%s\'", vStringValue (longName)); + error(FATAL, "no such field: \'%s\'", vStringValue (longName)); enableField (t, mode, true); @@ -1294,7 +1364,7 @@ static void processFormatOption ( } #ifdef HAVE_ICONV -static void processInputEncodingOption(const char *const option, +static void processInputEncodingOption(const char *const option CTAGS_ATTR_UNUSED, const char *const parameter) { if (Option.inputEncoding) @@ -1307,7 +1377,7 @@ static void processInputEncodingOption(const char *const option, Option.inputEncoding = eStrdup(parameter); } -static void processOutputEncodingOption(const char *const option, +static void processOutputEncodingOption(const char *const option CTAGS_ATTR_UNUSED, const char *const parameter) { if (Option.outputEncoding) @@ -1331,18 +1401,49 @@ static void printOptionDescriptions (const optionDescription *const optDesc) } } + +static int excludesCompare (struct colprintLine *a, struct colprintLine *b) +{ + return strcmp (colprintLineGetColumn (a, 0), colprintLineGetColumn (b, 0)); +} + +static void processListExcludesOption(const char *const option CTAGS_ATTR_UNUSED, + const char *const parameter CTAGS_ATTR_UNUSED) +{ + int i; + struct colprintTable *table = colprintTableNew ("L:NAME", NULL); + + const int max = Excluded ? stringListCount (Excluded) : 0; + + for (i = 0; i < max; ++i) + { + struct colprintLine * line = colprintTableGetNewLine (table); + colprintLineAppendColumnVString (line, stringListItem (Excluded, i)); + } + + colprintTableSort (table, excludesCompare); + colprintTablePrint (table, 0, localOption.withListHeader, localOption.machinable, stdout); + colprintTableDelete (table); + + if (i == 0) + putchar ('\n'); + + exit (0); +} + + static void printFeatureList (void) { int i; - for (i = 0 ; Features [i] != NULL ; ++i) + for (i = 0 ; Features [i].name != NULL ; ++i) { if (i == 0) printf (" Optional compiled features: "); - if (strcmp (Features [i], "regex") != 0 || checkRegex ()) - printf ("%s+%s", (i>0 ? ", " : ""), Features [i]); + if (strcmp (Features [i].name, "regex") != 0 || checkRegex ()) + printf ("%s+%s", (i>0 ? ", " : ""), Features [i].name); #ifdef CUSTOM_CONFIGURATION_FILE - if (strcmp (Features [i], "custom-conf") == 0) + if (strcmp (Features [i].name, "custom-conf") == 0) printf ("=%s", CUSTOM_CONFIGURATION_FILE); #endif } @@ -1351,14 +1452,34 @@ static void printFeatureList (void) } +static int featureCompare (struct colprintLine *a, struct colprintLine *b) +{ + return strcmp (colprintLineGetColumn (a, 0), + colprintLineGetColumn (b, 0)); +} + static void processListFeaturesOption(const char *const option CTAGS_ATTR_UNUSED, const char *const parameter CTAGS_ATTR_UNUSED) { int i; - for (i = 0 ; Features [i] != NULL ; ++i) - if (strcmp (Features [i], "regex") != 0 || checkRegex ()) - printf ("%s\n", Features [i]); + struct colprintTable *table = colprintTableNew ("L:NAME", "L:DESCRIPTION", NULL); + + for (i = 0 ; Features [i].name != NULL ; ++i) + { + struct colprintLine * line = colprintTableGetNewLine (table); + if (strcmp (Features [i].name, "regex") != 0 || checkRegex ()) + { + colprintLineAppendColumnCString (line, Features [i].name); + colprintLineAppendColumnCString (line, Features [i].description); + } + + } + + colprintTableSort (table, featureCompare); + colprintTablePrint (table, 0, localOption.withListHeader, localOption.machinable, stdout); + colprintTableDelete (table); + if (i == 0) putchar ('\n'); exit (0); @@ -1367,23 +1488,31 @@ static void processListFeaturesOption(const char *const option CTAGS_ATTR_UNUSED static void processListFieldsOption(const char *const option CTAGS_ATTR_UNUSED, const char *const parameter) { - if (parameter [0] == '\0' || strcasecmp (parameter, "all") == 0) + struct colprintTable * table = fieldColprintTableNew (); + + if (parameter [0] == '\0' || strcasecmp (parameter, RSV_LANG_ALL) == 0) { + fieldColprintAddCommonLines (table); + initializeParser (LANG_AUTO); - printFields (LANG_AUTO); + for (unsigned int i = 0; i < countParsers (); i++) + { + if (isLanguageVisible(i)) + fieldColprintAddLanguageLines (table, i); + } } else { langType language = getNamedLanguage (parameter, 0); if (language == LANG_IGNORE) error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option); - else - { - initializeParser (language); - printFields (language); - } + initializeParser (language); + fieldColprintAddLanguageLines (table, language); } + + fieldColprintTablePrint (table, localOption.withListHeader, localOption.machinable, stdout); + colprintTableDelete (table); exit (0); } @@ -1407,23 +1536,98 @@ static void printProgramIdentification (void) printFeatureList (); } -static void processHelpOption ( +static void processHelpOptionCommon ( const char *const option CTAGS_ATTR_UNUSED, - const char *const parameter CTAGS_ATTR_UNUSED) + const char *const parameter CTAGS_ATTR_UNUSED, + bool includingExperimentalOptions) { printProgramIdentification (); putchar ('\n'); printInvocationDescription (); putchar ('\n'); printOptionDescriptions (LongOptionDescription); + if (includingExperimentalOptions) + printOptionDescriptions (ExperimentalLongOptionDescription); +} + +static void processHelpOption ( + const char *const option, + const char *const parameter) +{ + processHelpOptionCommon (option, parameter, false); + exit (0); +} + +static void processHelpFullOption ( + const char *const option, + const char *const parameter) +{ + processHelpOptionCommon (option, parameter, true); exit (0); } +#ifdef HAVE_JANSSON +static void processInteractiveOption ( + const char *const option CTAGS_ATTR_UNUSED, + const char *const parameter) +{ + static struct interactiveModeArgs args; + + + if (parameter && (strcmp (parameter, "sandbox") == 0)) + { + Option.interactive = INTERACTIVE_SANDBOX; + args.sandbox = true; + } + else if (parameter && (strcmp (parameter, "default") == 0)) + { + Option.interactive = INTERACTIVE_DEFAULT; + args.sandbox = false; + } + else if ((!parameter) || *parameter == '\0') + { + Option.interactive = INTERACTIVE_DEFAULT; + args.sandbox = false; + } + else + error (FATAL, "Unknown option argument \"%s\" for --%s option", + parameter, option); + +#ifndef HAVE_SECCOMP + if (args.sandbox) + error (FATAL, "sandbox submode is not supported on this platform"); +#endif + +#ifdef ENABLE_GCOV + if (args.sandbox) + error (FATAL, "sandbox submode does not work if gcov is instrumented"); +#endif + + Option.sorted = SO_UNSORTED; + setMainLoop (interactiveLoop, &args); + setErrorPrinter (jsonErrorPrinter, NULL); + setTagWriter (WRITER_JSON); + enablePtag (PTAG_JSON_OUTPUT_VERSION, true); + + json_set_alloc_funcs (eMalloc, eFree); +} +#endif + +static void processIf0Option (const char *const option, + const char *const parameter) +{ + bool if0 = getBooleanOption (option, parameter); + langType lang = getNamedLanguage ("CPreProcessor", 0); + const char *arg = if0? "true": "false"; + + applyParameter (lang, "if0", arg); +} + static void processLanguageForceOption ( const char *const option, const char *const parameter) { langType language; - if (strcasecmp (parameter, "auto") == 0) + if (strcasecmp (parameter, RSV_LANG_AUTO) == 0) language = LANG_AUTO; else language = getNamedLanguage (parameter, 0); @@ -1562,7 +1766,7 @@ static char* processLanguageMap (char* map) language = getNamedLanguage (map, 0); if (language != LANG_IGNORE) { - const char *const deflt = "default"; + const char *const deflt = RSV_LANGMAP_DEFAULT; char* p; if (*list == '+') ++list; @@ -1603,7 +1807,7 @@ static void processLanguageMapOption ( char *const maps = eStrdup (parameter); char *map = maps; - if (strcmp (parameter, "default") == 0) + if (strcmp (parameter, RSV_LANGMAP_DEFAULT) == 0) { verbose (" Restoring default language maps:\n"); installLanguageMapDefaults (); @@ -1648,7 +1852,7 @@ static void processLanguagesOption ( *end = '\0'; if (lang [0] != '\0') { - if (strcmp (lang, "all") == 0) + if (strcmp (lang, RSV_LANG_ALL) == 0) enableLanguages ((bool) (mode != Remove)); else { @@ -1725,6 +1929,30 @@ extern bool processMapOption ( return true; } +extern bool processParamOption ( + const char *const option, const char *const value) +{ + langType language; + const char* name; + const char* sep; + + language = getLanguageComponentInOption (option, "param-"); + if (language == LANG_IGNORE) + return false; + + sep = option + strlen ("param-") + strlen (getLanguageName (language)); + if (*sep != ':') + error (FATAL, "no separator(:) is given for %s=%s", option, value); + name = sep + 1; + + if (value == NULL || value [0] == '\0') + error (FATAL, "no parameter is given for %s", option); + + applyParameter (language, name, value); + + return true; +} + static void processLicenseOption ( const char *const option CTAGS_ATTR_UNUSED, const char *const parameter CTAGS_ATTR_UNUSED) @@ -1739,87 +1967,124 @@ static void processLicenseOption ( static void processListAliasesOption ( const char *const option, const char *const parameter) { - if (parameter [0] == '\0' || strcasecmp (parameter, "all") == 0) - printLanguageAliases (LANG_AUTO); + if (parameter [0] == '\0' || strcasecmp (parameter, RSV_LANG_ALL) == 0) + printLanguageAliases (LANG_AUTO, + localOption.withListHeader, localOption.machinable, stdout); else { langType language = getNamedLanguage (parameter, 0); if (language == LANG_IGNORE) error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option); else - printLanguageAliases (language); + printLanguageAliases (language, + localOption.withListHeader, localOption.machinable, stdout); } exit (0); } -static void processListExtraOption ( +static void processListExtrasOption ( const char *const option CTAGS_ATTR_UNUSED, const char *const parameter CTAGS_ATTR_UNUSED) { - printXtags (); + struct colprintTable * table = xtagColprintTableNew (); + + if (parameter [0] == '\0' || strcasecmp (parameter, RSV_LANG_ALL) == 0) + { + xtagColprintAddCommonLines (table); + + initializeParser (LANG_AUTO); + for (unsigned int i = 0; i < countParsers (); i++) + { + if (isLanguageVisible(i)) + xtagColprintAddLanguageLines (table, i); + } + } + else + { + langType language = getNamedLanguage (parameter, 0); + if (language == LANG_IGNORE) + error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option); + + initializeParser (language); + xtagColprintAddLanguageLines (table, language); + } + + xtagColprintTablePrint (table, localOption.withListHeader, localOption.machinable, stdout); + colprintTableDelete (table); exit (0); } -static void processListFileKindDefinition ( +static void processListKindsOption ( const char *const option, const char *const parameter) { - if (parameter [0] == '\0' || strcasecmp (parameter, "all") == 0) - printLanguageFileKind (LANG_AUTO); + bool print_all = (strcmp (option, "list-kinds-full") == 0)? true: false; + + if (parameter [0] == '\0' || strcasecmp (parameter, RSV_LANG_ALL) == 0) + printLanguageKinds (LANG_AUTO, print_all, + localOption.withListHeader, localOption.machinable, stdout); else { langType language = getNamedLanguage (parameter, 0); if (language == LANG_IGNORE) error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option); else - printLanguageFileKind (language); + printLanguageKinds (language, print_all, + localOption.withListHeader, localOption.machinable, stdout); } exit (0); } -static void processListKindsOption ( - const char *const option, const char *const parameter) +static void processListParametersOption (const char *const option, + const char *const parameter) { - bool print_all = (strcmp (option, "list-kinds-full") == 0)? true: false; - - if (parameter [0] == '\0' || strcasecmp (parameter, "all") == 0) - printLanguageKinds (LANG_AUTO, print_all); + if (parameter [0] == '\0' || strcasecmp (parameter, RSV_LANG_ALL) == 0) + printLanguageParameters (LANG_AUTO, + localOption.withListHeader, localOption.machinable, + stdout); else { langType language = getNamedLanguage (parameter, 0); if (language == LANG_IGNORE) error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option); else - printLanguageKinds (language, print_all); + printLanguageParameters (language, + localOption.withListHeader, localOption.machinable, + stdout); } exit (0); } + static void processListMapsOptionForType (const char *const option CTAGS_ATTR_UNUSED, const char *const parameter, langmapType type) { - if (parameter [0] == '\0' || strcasecmp (parameter, "all") == 0) - printLanguageMaps (LANG_AUTO, type); + if (parameter [0] == '\0' || strcasecmp (parameter, RSV_LANG_ALL) == 0) + printLanguageMaps (LANG_AUTO, type, + localOption.withListHeader, localOption.machinable, + stdout); else { langType language = getNamedLanguage (parameter, 0); if (language == LANG_IGNORE) error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option); else - printLanguageMaps (language, type); + printLanguageMaps (language, type, + localOption.withListHeader, localOption.machinable, + stdout); } exit (0); } -static void processListExtensionsOption (const char *const option, +static void processListMapExtensionsOption (const char *const option, const char *const parameter) { - processListMapsOptionForType (option, parameter, LMAP_EXTENSION); + processListMapsOptionForType (option, parameter, LMAP_EXTENSION|LMAP_TABLE_OUTPUT); } -static void processListPatternsOption (const char *const option, +static void processListMapPatternsOption (const char *const option, const char *const parameter) { - processListMapsOptionForType (option, parameter, LMAP_PATTERN); + processListMapsOptionForType (option, parameter, LMAP_PATTERN|LMAP_TABLE_OUTPUT); } static void processListMapsOption ( @@ -1841,9 +2106,7 @@ static void processListPseudoTagsOptions ( const char *const option CTAGS_ATTR_UNUSED, const char *const parameter CTAGS_ATTR_UNUSED) { - int i; - for (i = 0; i < PTAG_COUNT; i++) - printPtag (i); + printPtags (localOption.withListHeader, localOption.machinable, stdout); exit (0); } @@ -1851,7 +2114,39 @@ static void processListRegexFlagsOptions ( const char *const option CTAGS_ATTR_UNUSED, const char *const parameter CTAGS_ATTR_UNUSED) { - printRegexFlags (); + printRegexFlags (localOption.withListHeader, localOption.machinable, stdout); + exit (0); +} + +static void processListMultilineRegexFlagsOptions ( + const char *const option CTAGS_ATTR_UNUSED, + const char *const parameter CTAGS_ATTR_UNUSED) +{ + printMultilineRegexFlags (localOption.withListHeader, localOption.machinable, stdout); + exit (0); +} + +static void processListMultitableRegexFlagsOptions ( + const char *const option CTAGS_ATTR_UNUSED, + const char *const parameter CTAGS_ATTR_UNUSED) +{ + printMultitableRegexFlags (localOption.withListHeader, localOption.machinable, stdout); + exit (0); +} + +static void processListLangdefFlagsOptions ( + const char *const option CTAGS_ATTR_UNUSED, + const char *const parameter CTAGS_ATTR_UNUSED) +{ + printLangdefFlags (localOption.withListHeader, localOption.machinable, stdout); + exit (0); +} + +static void processListKinddefFlagsOptions ( + const char *const option CTAGS_ATTR_UNUSED, + const char *const parameter CTAGS_ATTR_UNUSED) +{ + printKinddefFlags (localOption.withListHeader, localOption.machinable, stdout); exit (0); } @@ -1859,13 +2154,16 @@ static void processListRolesOptions (const char *const option CTAGS_ATTR_UNUSED, const char *const parameter) { const char* sep; - const char *kindletters; + const char *kindspecs; langType lang; if (parameter == NULL || parameter[0] == '\0') { - printLanguageRoles (LANG_AUTO, "*"); + printLanguageRoles (LANG_AUTO, "*", + localOption.withListHeader, + localOption.machinable, + stdout); exit (0); } @@ -1876,10 +2174,10 @@ static void processListRolesOptions (const char *const option CTAGS_ATTR_UNUSED, vString* vstr = vStringNewInit (parameter); vStringCatS (vstr, (sep? "*": ".*")); processListRolesOptions (option, vStringValue (vstr)); - /* The control should never reache here. */ + /* The control should never reached here. */ } - kindletters = sep + 1; + kindspecs = sep + 1; if (strncmp (parameter, "all.", 4) == 0 || strncmp (parameter, "*.", 1) == 0 || strncmp (parameter, ".", 1) == 0) @@ -1890,9 +2188,38 @@ static void processListRolesOptions (const char *const option CTAGS_ATTR_UNUSED, if (lang == LANG_IGNORE) error (FATAL, "Unknown language \"%s\" in \"%s\"", parameter, option); } - printLanguageRoles (lang, kindletters); + printLanguageRoles (lang, kindspecs, + localOption.withListHeader, + localOption.machinable, + stdout); + exit (0); +} + +static void processListSubparsersOptions (const char *const option CTAGS_ATTR_UNUSED, + const char *const parameter) +{ + langType lang; + + + if (parameter == NULL || parameter[0] == '\0' + || (strcmp(parameter, RSV_LANG_ALL) == 0)) + { + printLanguageSubparsers(LANG_AUTO, + localOption.withListHeader, localOption.machinable, + stdout); + exit (0); + } + + lang = getNamedLanguage (parameter, 0); + if (lang == LANG_IGNORE) + error (FATAL, "Unknown language \"%s\" in \"%s\"", parameter, option); + + printLanguageSubparsers(lang, + localOption.withListHeader, localOption.machinable, + stdout); exit (0); } + static void freeSearchPathList (searchPathList** pathList) { stringListClear (*pathList); @@ -1900,23 +2227,14 @@ static void freeSearchPathList (searchPathList** pathList) *pathList = NULL; } -static void verboseSearchPathList (const searchPathList* pathList, const char *const varname) -{ - unsigned int i; - - verbose ("Install %s:\n", varname); - for (i = 0; i < stringListCount (pathList); ++i) - verbose (" %s\n", vStringValue (stringListItem (pathList, i))); -} - static vString* expandOnSearchPathList (searchPathList *pathList, const char* leaf, bool (* check) (const char *const)) { unsigned int i; - for (i = 0; i < stringListCount (pathList); ++i) + for (i = stringListCount (pathList); i > 0; --i) { - const char* const body = vStringValue (stringListItem (pathList, i)); + const char* const body = vStringValue (stringListItem (pathList, i - 1)); char* tmp = combinePathAndFile (body, leaf); if ((* check) (tmp)) @@ -1930,68 +2248,23 @@ static vString* expandOnSearchPathList (searchPathList *pathList, const char* le return NULL; } -static bool isDirectory (const char *const dirName) +static vString* expandOnOptlibPathList (const char* leaf) { - fileStatus *status = eStat (dirName); - return status->exists && status->isDirectory; + + return expandOnSearchPathList (OptlibPathList, leaf, + doesFileExist); } -static vString* expandOnOptlibPathList (const char* leaf) +static void processOptionFileCommon ( + const char *const option, const char *const parameter, + bool allowNonExistingFile) { - vString* r; - vString* leaf_with_suffix; - - leaf_with_suffix = vStringNewInit (leaf); - vStringCatS (leaf_with_suffix, ".d"); + const char* path; + vString* vpath = NULL; + fileStatus *status; - r = expandOnSearchPathList (OptlibPathList, vStringValue (leaf_with_suffix), - isDirectory); - - if (!r) - { - vStringCopyS (leaf_with_suffix, leaf); - vStringCatS (leaf_with_suffix, ".conf"); - r = expandOnSearchPathList (OptlibPathList, vStringValue (leaf_with_suffix), - doesFileExist); - } - - if (!r) - { - vStringCopyS (leaf_with_suffix, leaf); - vStringCatS (leaf_with_suffix, ".ctags"); - r = expandOnSearchPathList (OptlibPathList, vStringValue (leaf_with_suffix), - doesFileExist); - } - -#ifdef MSDOS_STYLE_PATH - if (!r) - { - vStringCopyS (leaf_with_suffix, leaf); - vStringCatS (leaf_with_suffix, ".cnf"); - r = expandOnSearchPathList (OptlibPathList, vStringValue (leaf_with_suffix), - doesFileExist); - } -#endif - - vStringDelete (leaf_with_suffix); - - return r; -} - -extern vString* expandOnDriversPathList (const char* leaf) -{ - return expandOnSearchPathList (DriversPathList, leaf, doesExecutableExist); -} - -static void processOptionFile ( - const char *const option, const char *const parameter) -{ - const char* path; - vString* vpath = NULL; - fileStatus *status; - - if (parameter [0] == '\0') - error (FATAL, "no option file supplied for \"%s\"", option); + if (parameter [0] == '\0') + error (FATAL, "no option file supplied for \"%s\"", option); if (parameter [0] != '/' && parameter [0] != '.') { @@ -2004,7 +2277,8 @@ static void processOptionFile ( status = eStat (path); if (!status->exists) { - error (FATAL | PERROR, "cannot stat \"%s\"", path); + if (!allowNonExistingFile) + error (FATAL | PERROR, "cannot stat \"%s\"", path); } else if (status->isDirectory) { @@ -2022,14 +2296,28 @@ static void processOptionFile ( vStringDelete (vpath); } +static void processOptionFile ( + const char *const option, const char *const parameter) +{ + processOptionFileCommon(option, parameter, false); +} + +static void processOptionFileMaybe ( + const char *const option, const char *const parameter) +{ + processOptionFileCommon(option, parameter, true); +} + static void processOutputFormat (const char *const option CTAGS_ATTR_UNUSED, const char *const parameter) { if (parameter [0] == '\0') error (FATAL, "no output format name supplied for \"%s\"", option); - if (strcmp (parameter, "ctags") == 0) + if (strcmp (parameter, "u-ctags") == 0) ; + else if (strcmp (parameter, "e-ctags") == 0) + setTagWriter (WRITER_E_CTAGS); else if (strcmp (parameter, "etags") == 0) setEtagsMode (); else if (strcmp (parameter, "xref") == 0) @@ -2100,6 +2388,21 @@ static void processSortOption ( error (FATAL, "Invalid value for \"%s\" option", option); } +static void processTagRelative ( + const char *const option, const char *const parameter) +{ + if (isFalse (parameter)) + Option.tagRelative = TREL_NO; + else if (isTrue (parameter)) + Option.tagRelative = TREL_YES; + else if (strcasecmp (parameter, "always") == 0) + Option.tagRelative = TREL_ALWAYS; + else if (strcasecmp (parameter, "never") == 0) + Option.tagRelative = TREL_NEVER; + else + error (FATAL, "Invalid value for \"%s\" option", option); +} + static void installHeaderListDefaults (void) { Option.headerExt = stringListNewFromArgv (HeaderExtensions); @@ -2141,164 +2444,15 @@ static void processHeaderListOption (const int option, const char *parameter) /* * Token ignore processing */ - -/* Determines whether or not "name" should be ignored, per the ignore list. - */ -extern const ignoredTokenInfo * isIgnoreToken(const char * name) -{ - if(!Option.ignore) - return NULL; - - return (const ignoredTokenInfo *)hashTableGetItem(Option.ignore,(char *)name); -} -#endif - -/* GEANY DIFF */ -/* Determines whether or not "name" should be ignored, per the ignore list. - */ -extern bool isIgnoreToken (const char *const name, - bool *const pIgnoreParens, - const char **const replacement) -{ - bool result = false; - - if (c_tags_ignore != NULL) - { - const size_t nameLen = strlen (name); - unsigned int i; - unsigned int len = 0; - vString *token = vStringNew(); - - while (c_tags_ignore[len]) - len++; - - if (pIgnoreParens != NULL) - *pIgnoreParens = false; - - for (i = 0 ; i < len ; ++i) - { - size_t tokenLen; - - vStringCopyS (token, c_tags_ignore[i]); - tokenLen = vStringLength (token); - - if (tokenLen >= 2 && vStringChar (token, tokenLen - 1) == '*' && - strncmp (vStringValue (token), name, tokenLen - 1) == 0) - { - result = true; - break; - } - if (strncmp (vStringValue (token), name, nameLen) == 0) - { - if (nameLen == tokenLen) - { - result = true; - break; - } - else if (tokenLen == nameLen + 1 && - vStringChar (token, tokenLen - 1) == '+') - { - result = true; - if (pIgnoreParens != NULL) - *pIgnoreParens = true; - break; - } - else if (vStringChar (token, nameLen) == '=') - { - if (replacement != NULL) - *replacement = vStringValue (token) + nameLen + 1; - break; - } - } - } - vStringDelete (token); - } - return result; -} -/* GEANY DIFF END */ - -#ifndef CTAGS_LIB -static void freeIgnoredTokenInfo(ignoredTokenInfo * info) -{ - if(!info) - return; - if(info->replacement) - eFree(info->replacement); - eFree(info); -} - -static void saveIgnoreToken(const char * ignoreToken) -{ - if(!ignoreToken) - return; - - if(!Option.ignore) - { - Option.ignore = hashTableNew( - 1024, - hashCstrhash, - hashCstreq, - free, - (void (*)(void *))freeIgnoredTokenInfo - ); - } - - const char * c = ignoreToken; - char cc = *c; - - const char * tokenBegin = c; - const char * tokenEnd = NULL; - const char * replacement = NULL; - bool ignoreFollowingParenthesis = false; - - while(cc) - { - if(cc == '=') - { - if(!tokenEnd) - tokenEnd = c; - c++; - if(*c) - replacement = c; - break; - } - - if(cc == '+') - { - if(!tokenEnd) - tokenEnd = c; - ignoreFollowingParenthesis = true; - } - - c++; - cc = *c; - } - - if(!tokenEnd) - tokenEnd = c; - - if(tokenEnd <= tokenBegin) - return; - - - ignoredTokenInfo * info = (ignoredTokenInfo *)eMalloc(sizeof(ignoredTokenInfo)); - - info->ignoreFollowingParenthesis = ignoreFollowingParenthesis; - info->replacement = replacement ? eStrdup(replacement) : NULL; - - hashTablePutItem(Option.ignore,eStrndup(tokenBegin,tokenEnd - tokenBegin),info); - - verbose (" ignore token: %s\n", ignoreToken); -} - static void readIgnoreList (const char *const list) { + langType lang = getNamedLanguage ("CPreProcessor", 0); char* newList = stringCopy (list); const char *token = strtok (newList, IGNORE_SEPARATORS); while (token != NULL) { - saveIgnoreToken (token); + applyParameter (lang, "ignore", token); token = strtok (NULL, IGNORE_SEPARATORS); } eFree (newList); @@ -2306,6 +2460,8 @@ static void readIgnoreList (const char *const list) static void addIgnoreListFromFile (const char *const fileName) { + langType lang = getNamedLanguage ("CPreProcessor", 0); + stringList* tokens = stringListNewFromFile (fileName); if (tokens == NULL) error (FATAL | PERROR, "cannot open \"%s\"", fileName); @@ -2316,15 +2472,19 @@ static void addIgnoreListFromFile (const char *const fileName) for(i=0;ipValue; - bool default_value = isXtagEnabled (t); - - enableXtag (t, default_value); - - return &(getXtagDesc (t)->enabled); + enableXtag (t, value); } /* * Option tables */ +static void processDumpOptionsOption (const char *const option, const char *const parameter); + static parametricOption ParametricOptions [] = { - { "config-filename", processConfigFilenameOption, true, STAGE_ANY }, - { "data-dir", processDataDir, false, STAGE_ANY }, { "etags-include", processEtagsInclude, false, STAGE_ANY }, { "exclude", processExcludeOption, false, STAGE_ANY }, { "excmd", processExcmdOption, false, STAGE_ANY }, { "extra", processExtraTagsOption, false, STAGE_ANY }, + { "extras", processExtraTagsOption, false, STAGE_ANY }, { "fields", processFieldsOption, false, STAGE_ANY }, { "filter-terminator", processFilterTerminatorOption, true, STAGE_ANY }, { "format", processFormatOption, true, STAGE_ANY }, { "help", processHelpOption, true, STAGE_ANY }, + { "help-full", processHelpFullOption, true, STAGE_ANY }, + { "if0", processIf0Option, false, STAGE_ANY }, #ifdef HAVE_ICONV { "input-encoding", processInputEncodingOption, false, STAGE_ANY }, { "output-encoding", processOutputEncodingOption, false, STAGE_ANY }, @@ -2549,56 +2693,72 @@ static parametricOption ParametricOptions [] = { { "languages", processLanguagesOption, false, STAGE_ANY }, { "langdef", processLanguageDefineOption, false, STAGE_ANY }, { "langmap", processLanguageMapOption, false, STAGE_ANY }, - { "libexec-dir", processLibexecDir, false, STAGE_ANY }, { "license", processLicenseOption, true, STAGE_ANY }, { "list-aliases", processListAliasesOption, true, STAGE_ANY }, - { "list-extensions", processListExtensionsOption, true, STAGE_ANY }, - { "list-extra", processListExtraOption, true, STAGE_ANY }, + { "list-excludes", processListExcludesOption, true, STAGE_ANY }, + { "list-extras", processListExtrasOption, true, STAGE_ANY }, { "list-features", processListFeaturesOption, true, STAGE_ANY }, { "list-fields", processListFieldsOption, true, STAGE_ANY }, - { "list-file-kind", processListFileKindDefinition, true, STAGE_ANY }, { "list-kinds", processListKindsOption, true, STAGE_ANY }, { "list-kinds-full", processListKindsOption, true, STAGE_ANY }, { "list-languages", processListLanguagesOption, true, STAGE_ANY }, { "list-maps", processListMapsOption, true, STAGE_ANY }, - { "list-patterns", processListPatternsOption, true, STAGE_ANY }, + { "list-map-extensions", processListMapExtensionsOption, true, STAGE_ANY }, + { "list-map-patterns", processListMapPatternsOption, true, STAGE_ANY }, + { "list-mline-regex-flags", processListMultilineRegexFlagsOptions, true, STAGE_ANY }, + { "list-params", processListParametersOption, true, STAGE_ANY }, { "list-pseudo-tags", processListPseudoTagsOptions, true, STAGE_ANY }, { "list-regex-flags", processListRegexFlagsOptions, true, STAGE_ANY }, - { "_list-roles", processListRolesOptions, true, STAGE_ANY }, + { "list-roles", processListRolesOptions, true, STAGE_ANY }, + { "list-subparsers", processListSubparsersOptions, true, STAGE_ANY }, { "maxdepth", processMaxRecursionDepthOption, true, STAGE_ANY }, + { "optlib-dir", processOptlibDir, false, STAGE_ANY }, { "options", processOptionFile, false, STAGE_ANY }, + { "options-maybe", processOptionFileMaybe, false, STAGE_ANY }, { "output-format", processOutputFormat, true, STAGE_ANY }, { "pattern-length-limit", processPatternLengthLimit, true, STAGE_ANY }, { "pseudo-tags", processPseudoTags, false, STAGE_ANY }, { "sort", processSortOption, true, STAGE_ANY }, + { "tag-relative", processTagRelative, true, STAGE_ANY }, { "version", processVersionOption, true, STAGE_ANY }, + { "_anonhash", processAnonHashOption, false, STAGE_ANY }, + { "_dump-keywords", processDumpKeywordsOption, false, STAGE_ANY }, + { "_dump-options", processDumpOptionsOption, false, STAGE_ANY }, { "_echo", processEchoOption, false, STAGE_ANY }, + { "_force-initializing", processForceInitOption, false, STAGE_ANY }, { "_force-quit", processForceQuitOption, false, STAGE_ANY }, +#ifdef HAVE_JANSSON + { "_interactive", processInteractiveOption, true, STAGE_ANY }, +#endif + { "_list-kinddef-flags", processListKinddefFlagsOptions, true, STAGE_ANY }, + { "_list-langdef-flags", processListLangdefFlagsOptions, true, STAGE_ANY }, + { "_list-mtable-regex-flags", processListMultitableRegexFlagsOptions, true, STAGE_ANY }, +#ifdef DO_TRACING + { "_trace", processTraceOption, false, STAGE_ANY }, +#endif { "_xformat", processXformatOption, false, STAGE_ANY }, }; static booleanOption BooleanOptions [] = { { "append", &Option.append, true, STAGE_ANY }, - { "file-scope", ((bool *)XTAG_FILE_SCOPE), false, STAGE_ANY, redirectToXtag }, - { "file-tags", ((bool *)XTAG_FILE_NAMES), false, STAGE_ANY, redirectToXtag }, + { "file-scope", ((bool *)XTAG_FILE_SCOPE), false, STAGE_ANY, setBooleanToXtag }, + { "file-tags", ((bool *)XTAG_FILE_NAMES), false, STAGE_ANY, setBooleanToXtag }, { "filter", &Option.filter, true, STAGE_ANY }, { "guess-language-eagerly", &Option.guessLanguageEagerly, false, STAGE_ANY }, - { "if0", &Option.if0, false, STAGE_ANY }, { "line-directives",&Option.lineDirectives, false, STAGE_ANY }, { "links", &Option.followLinks, false, STAGE_ANY }, - { "machinable", &Option.machinable, true, STAGE_ANY }, + { "machinable", &localOption.machinable, true, STAGE_ANY }, { "put-field-prefix", &Option.putFieldPrefix, false, STAGE_ANY }, { "print-language", &Option.printLanguage, true, STAGE_ANY }, { "quiet", &Option.quiet, false, STAGE_ANY }, #ifdef RECURSE_SUPPORTED { "recurse", &Option.recurse, false, STAGE_ANY }, #endif - { "tag-relative", &Option.tagRelative, true, STAGE_ANY }, { "totals", &Option.printTotals, true, STAGE_ANY }, - { "verbose", &Option.verbose, false, STAGE_ANY }, - { "with-list-header", &Option.withListHeader, true, STAGE_ANY }, - { "_allow-xcmd-in-homedir", &Option.allowXcmdInHomeDir, true, ACCEPT(Etc)|ACCEPT(LocalEtc) }, + { "verbose", &ctags_verbose, false, STAGE_ANY }, + { "with-list-header", &localOption.withListHeader, true, STAGE_ANY }, { "_fatal-warnings",&Option.fatalWarnings, false, STAGE_ANY }, + { "_mtable-totals", &Option.mtablePrintTotals, false, STAGE_ANY }, }; /* @@ -2641,18 +2801,7 @@ static bool processParametricOption ( static bool getBooleanOption ( const char *const option, const char *const parameter) { - bool selection = true; - - if (parameter [0] == '\0') - selection = true; - else if (isFalse (parameter)) - selection = false; - else if (isTrue (parameter)) - selection = true; - else - error (FATAL, "Invalid value for \"%s\" option", option); - - return selection; + return paramParserBool (parameter, true, option, "option"); } static bool processBooleanOption ( @@ -2665,7 +2814,6 @@ static bool processBooleanOption ( for (i = 0 ; i < count && ! found ; ++i) { booleanOption* const entry = &BooleanOptions [i]; - bool *slot; if (strcmp (option, entry->name) == 0) { found = true; @@ -2677,11 +2825,12 @@ static bool processBooleanOption ( } if (entry->initOnly) checkOptionOrder (option, true); - if (entry->redirect) - slot = entry->redirect (entry); + + bool value = getBooleanOption (option, parameter); + if (entry->set) + entry->set (entry, value); else - slot = entry->pValue; - *slot = getBooleanOption (option, parameter); + *entry->pValue = value; } } return found; @@ -2705,6 +2854,24 @@ static void enableLanguageField (langType language, const char *field, bool mode } } +static void enableLanguageXtag (langType language, const char *xtag, bool mode) +{ + + xtagType x; + + x = getXtagTypeForNameAndLanguage (xtag, language); + if (x == XTAG_UNKNOWN) + error(FATAL, "no such extra: \'%s\'", xtag); + enableXtag (x, mode); + if (language == LANG_AUTO) + { + xtagType xtag_next = x; + + while ((xtag_next = nextSiblingXtag (xtag_next)) != XTAG_UNKNOWN) + enableXtag (xtag_next, mode); + } +} + static bool processLangSpecificFieldsOption (const char *const option, const char *const parameter) { @@ -2727,7 +2894,7 @@ static bool processLangSpecificFieldsOption (const char *const option, len = strlen (lang); if (len == 0) error (FATAL, "No language given in \"%s\" option", option); - else if (len == 1 && lang[0] == '*') + else if (len == strlen(RSV_LANG_ALL) && (strncmp(lang, RSV_LANG_ALL, len) == 0)) language = LANG_AUTO; else language = getNamedLanguage (lang, len); @@ -2735,7 +2902,7 @@ static bool processLangSpecificFieldsOption (const char *const option, if (language == LANG_IGNORE) { error (WARNING, "Unknown language: %s (ignoring \"--%s\")", lang, option); - /* The option is consumed in tihs function. */ + /* The option is consumed in this function. */ return true; } @@ -2746,12 +2913,16 @@ static bool processLangSpecificFieldsOption (const char *const option, resetFieldsOption (language, true); p++; } - else if (*p == '{') + else if (*p == '{' || *p == '\0') + { resetFieldsOption (language, false); + if (*p == '\0') + return true; + } else if (*p != '+' && *p != '-') error (WARNING, "Wrong per language field specification: %s", p); - longName = vStringNewOrClear (longName); + longName = vStringNewOrClearWithAutoRelease (longName); while ((c = *p++) != '\0') { switch (c) @@ -2796,16 +2967,173 @@ static bool processLangSpecificFieldsOption (const char *const option, break; } } +#undef PREFIX_LEN +#undef PREFIX return true; } -static void processLongOption ( - const char *const option, const char *const parameter) +static bool processLangSpecificExtraOption (const char *const option, + const char *const parameter) { - Assert (parameter != NULL); - Assert (option != NULL); +#define PREFIX "extras-" +#define PREFIX_LEN strlen(PREFIX) + const char* lang; + size_t len; + langType language = LANG_IGNORE; + const char *p = parameter; + int c; + static vString * longName; + bool mode = true; + const char *x; + bool inLongName = false; - if (parameter [0] == '\0') + if ( strncmp (option, PREFIX, PREFIX_LEN) != 0 ) + return false; + + lang = option + PREFIX_LEN; + len = strlen (lang); + + if (len == 0) + error (FATAL, "No language given in \"%s\" option", option); + else if (len == strlen(RSV_LANG_ALL) && (strncmp(lang, RSV_LANG_ALL, len) == 0)) + language = LANG_AUTO; + else + language = getNamedLanguage (lang, len); + + if (language == LANG_IGNORE) + { + error (WARNING, "Unknown language: %s (ignoring \"--%s\")", lang, option); + /* The option is consumed in this function. */ + return true; + } + + initializeParser (language); + + if (*p == '*') + { + resetXtags (language, true); + p++; + } + else if (*p == '{' || *p == '\0') + { + resetXtags (language, false); + if (*p == '\0') + return true; + } + else if (*p != '+' && *p != '-') + error (WARNING, "Wrong per language extra specification: %s", p); + + longName = vStringNewOrClearWithAutoRelease (longName); + while ((c = *p++) != '\0') + { + switch (c) + { + case '+': + if (inLongName) + vStringPut (longName, c); + else + mode = true; + break; + case '-': + if (inLongName) + vStringPut (longName, c); + else + mode = false; + break; + case '{': + if (inLongName) + error (FATAL, + "unexpected character in extra specification: \'%c\'", + c); + inLongName = true; + break; + case '}': + if (!inLongName) + error (FATAL, + "unexpected character in extra specification: \'%c\'", + c); + + x = vStringValue (longName); + enableLanguageXtag (language, x, mode); + inLongName = false; + vStringClear (longName); + break; + default: + if (inLongName) + vStringPut (longName, c); + else + error (FATAL, + "only long name can be used in per language extra spec: \'%c\'", + c); + break; + } + } + return true; +} + +static bool processRegexOption (const char *const option, + const char *const parameter) +{ + langType language; + + language = getLanguageComponentInOption (option, "regex-"); + if (language == LANG_IGNORE) + return false; + + processLanguageRegexOption (language, REG_PARSER_SINGLE_LINE, parameter); + + return true; +} + +static bool processMultilineRegexOption (const char *const option, + const char *const parameter) +{ + langType language; + + language = getLanguageComponentInOption (option, "mline-regex-"); + if (language == LANG_IGNORE) + return false; + + processLanguageRegexOption (language, REG_PARSER_MULTI_LINE, parameter); + + return true; +} + +static bool processMultitableRegexOption (const char *const option, + const char *const parameter) +{ + langType language; + + language = getLanguageComponentInOption (option, "_mtable-regex-"); + if (language == LANG_IGNORE) + return false; + + processLanguageRegexOption (language, REG_PARSER_MULTI_TABLE, parameter); + + return true; +} + +static bool processMultitableExtendingOption (const char *const option, + const char *const parameter) +{ + langType language; + + language = getLanguageComponentInOption (option, "_mtable-extend-"); + if (language == LANG_IGNORE) + return false; + + processLanguageMultitableExtendingOption (language, parameter); + + return true; +} + +static void processLongOption ( + const char *const option, const char *const parameter) +{ + Assert (parameter != NULL); + Assert (option != NULL); + + if (parameter [0] == '\0') verbose (" Option: --%s\n", option); else verbose (" Option: --%s=%s\n", option, parameter); @@ -2814,18 +3142,34 @@ static void processLongOption ( ; else if (processLangSpecificFieldsOption(option, parameter)) ; + else if (processExtradefOption(option, parameter)) + ; + else if (processFielddefOption(option, parameter)) + ; + else if (processLangSpecificExtraOption(option, parameter)) + ; else if (processParametricOption (option, parameter)) ; - else if (processKindDefinition (option, parameter)) + else if (processKinddefOption (option, parameter)) + ; + else if (processKindsOption (option, parameter)) ; else if (processAliasOption (option, parameter)) ; else if (processRegexOption (option, parameter)) ; - else if (processXcmdOption (option, parameter, Stage)) + else if (processMultilineRegexOption (option, parameter)) ; else if (processMapOption (option, parameter)) ; + else if (processParamOption (option, parameter)) + ; + else if (processTabledefOption (option, parameter)) + ; + else if (processMultitableRegexOption (option, parameter)) + ; + else if (processMultitableExtendingOption (option, parameter)) + ; #ifdef HAVE_ICONV else if (processLanguageEncodingOption (option, parameter)) ; @@ -2834,6 +3178,8 @@ static void processLongOption ( else if (strcmp (option, "recurse") == 0) error (WARNING, "%s option not supported on this host", option); #endif + else if (processRoledefOption (option, parameter)) + ; else error (FATAL, "Unknown option: --%s", option); } @@ -2864,17 +3210,20 @@ static void processShortOption ( error (FATAL, "-%s: Invalid line number", option); Option.breakLine = atol (parameter); break; - case 'D': - if (!strToLong(parameter, 0, &Option.debugLevel)) + case 'd': + if (!strToLong(parameter, 0, &ctags_debugLevel)) error (FATAL, "-%s: Invalid debug level", option); if (debug (DEBUG_STATUS)) - Option.verbose = true; + ctags_verbose = true; break; #endif case 'B': Option.backward = true; break; + case 'D': + processIgnoreOption (parameter, *option); + break; case 'e': checkOptionOrder (option, false); setEtagsMode (); @@ -2903,7 +3252,7 @@ static void processShortOption ( processHeaderListOption (*option, parameter); break; case 'I': - processIgnoreOption (parameter); + processIgnoreOption (parameter, *option); break; case 'L': if (Option.fileList != NULL) @@ -2933,7 +3282,7 @@ static void processShortOption ( Option.sorted = SO_UNSORTED; break; case 'V': - Option.verbose = true; + ctags_verbose = true; break; case 'w': /* silently ignored */ @@ -3023,11 +3372,10 @@ extern void previewFirstOption (cookedArgs* const args) { if (strcmp (args->item, "V") == 0 || strcmp (args->item, "verbose") == 0 - || strcmp (args->item, "config-filename") == 0 || strcmp (args->item, "quiet") == 0) parseOption (args); else if (strcmp (args->item, "options") == 0 && - strcmp (args->parameter, "NONE") == 0) + strcmp (args->parameter, RSV_NONE) == 0) { notice ("No options will be read from files or environment"); SkipConfiguration = true; @@ -3038,34 +3386,16 @@ extern void previewFirstOption (cookedArgs* const args) } } +#ifndef CTAGS_LIB static void parseConfigurationFileOptionsInDirectoryWithLeafname (const char* directory, const char* leafname) { char* pathname = combinePathAndFile (directory, leafname); parseFileOptions (pathname); eFree (pathname); } - -static void parseConfigurationFileOptionsInDirectory (const char* directory) -{ - char *leafname = NULL; - - if (asprintf (&leafname,".%s",(Option.configFilename)?Option.configFilename:"ctags") == -1) - { - error (FATAL, "error in asprintf"); - } - parseConfigurationFileOptionsInDirectoryWithLeafname (directory, leafname); - free (leafname); -#ifdef MSDOS_STYLE_PATH - if (asprintf (&leafname,"%s.cnf",(Option.configFilename)?Option.configFilename:"ctags") == -1) - { - error (FATAL, "error in asprintf"); - } - parseConfigurationFileOptionsInDirectoryWithLeafname (directory, leafname); - free (leafname); #endif -} -#if defined(HAVE_SCANDIR) +#if defined(HAVE_SCANDIR) || defined (HAVE_DIRENT_H) || defined (_MSC_VER) static int ignore_dot_file(const struct dirent* dent) { /* Ignore a file which name is started from dot. */ @@ -3075,30 +3405,13 @@ static int ignore_dot_file(const struct dirent* dent) return 1; } -static int accept_only_dot_d(const struct dirent* dent) -{ - size_t len; - - /* accept only a directory ended with ".d" */ - len = strlen(dent->d_name); - - if (len < 3) - return 0; - return !strcmp(dent->d_name + (len - 2), ".d"); -} - static int accept_only_dot_ctags(const struct dirent* dent) { size_t len; - /* accept only a file ended with ".conf" or ".ctags" */ + /* accept only a file ended with ".ctags" */ len = strlen(dent->d_name); - if (len < 6) - return 0; - if (strcmp(dent->d_name + (len - 5), ".conf") == 0) - return 1; - if (len < 7) return 0; if (strcmp(dent->d_name + (len - 6), ".ctags") == 0) @@ -3107,13 +3420,19 @@ static int accept_only_dot_ctags(const struct dirent* dent) return 0; } +static int alphaSort(const struct dirent ** a, + const struct dirent ** b) +{ + return strcmp ((*a)->d_name, (*b)->d_name); +} + static bool parseAllConfigurationFilesOptionsInDirectory (const char* const dirName, stringList* const already_loaded_files) { struct dirent **dents; int i, n; - n = scandir (dirName, &dents, ignore_dot_file, alphasort); + n = scanDirectory (dirName, &dents, ignore_dot_file, alphaSort); if (n < 0) return false; @@ -3130,12 +3449,9 @@ static bool parseAllConfigurationFilesOptionsInDirectory (const char* const dirN path = combinePathAndFile (dirName, dents[i]->d_name); s = eStat (path); - if (s->exists && s->isDirectory && accept_only_dot_d(dents[i])) - parseAllConfigurationFilesOptionsInDirectory (path, - already_loaded_files); - else if (s->exists && accept_only_dot_ctags(dents[i])) + if (s->exists && accept_only_dot_ctags(dents[i])) parseConfigurationFileOptionsInDirectoryWithLeafname (dirName, - dents[i]->d_name); + dents[i]->d_name); eStatFree (s); free (dents[i]); eFree (path); @@ -3151,72 +3467,133 @@ static bool parseAllConfigurationFilesOptionsInDirectory (const char* const dirN } #endif -static void preload (const searchPathList *const pathList) +typedef char* (* preloadMakePathFunc) (const char*, const char*); + +struct preloadPathElt { + const char *path; /* NULL means the end of list */ + bool isDirectory; + preloadMakePathFunc makePath; + const char *extra; + OptionLoadingStage stage; +}; + +static char* prependEnvvar (const char *path, const char* envvar) +{ + char *full_path = NULL; + + const char* const envval = getenv (envvar); + if (envval) + full_path = combinePathAndFile(envval, path); + + return full_path; +} + +#ifdef WIN32 +static char *getConfigAtHomeOnWindows (const char *path, + const char* extra CTAGS_ATTR_UNUSED) +{ + /* + * Windows users don't usually set HOME. + * The OS sets HOMEDRIVE and HOMEPATH for them. + */ + const char* homeDrive = getenv ("HOMEDRIVE"); + const char* homePath = getenv ("HOMEPATH"); + if (homeDrive != NULL && homePath != NULL) + { + vString* const windowsHome = vStringNew (); + vStringCatS (windowsHome, homeDrive); + vStringCatS (windowsHome, homePath); + + char *tmp = combinePathAndFile (vStringValue(windowsHome), path); + vStringDelete (windowsHome); + + return tmp; + } + return NULL; +} +#endif + +static void preload (struct preloadPathElt *pathList) { unsigned int i; stringList* loaded; loaded = stringListNew (); - for (i = 0; i < stringListCount (pathList); ++i) + for (i = 0; pathList[i].path != NULL; ++i) { - const char* const dir = vStringValue (stringListItem (pathList, i)); - parseAllConfigurationFilesOptionsInDirectory (dir, loaded); + struct preloadPathElt *elt = pathList + i; + preloadMakePathFunc maker = elt->makePath; + const char *path = elt->path; + + + if (maker) + path = maker(elt->path, elt->extra); + + if (path == NULL) + continue; + + Assert (Stage <= elt->stage); + if (Stage != elt->stage) + { + Stage = elt->stage; + verbose ("Entering configuration stage: loading %s\n", StageDescription[Stage]); + } + + if (elt->isDirectory) + parseAllConfigurationFilesOptionsInDirectory (path, loaded); + else + parseFileOptions (path); + + if (path != elt->path) + eFree ((void *)path); } stringListClear (loaded); stringListDelete (loaded); } -static void parseConfigurationFileOptions (void) -{ - vString *home; - /* We parse .ctags on all systems, and additionally ctags.cnf on DOS. */ - char *filename = NULL; - const char *filename_body; - +static struct preloadPathElt preload_path_list [] = { #ifdef CUSTOM_CONFIGURATION_FILE - ENTER(Custom); - parseFileOptions (CUSTOM_CONFIGURATION_FILE); -#endif - filename_body = (Option.configFilename)?Option.configFilename:"ctags"; -#ifdef MSDOS_STYLE_PATH - - if (asprintf (&filename,"/%s.cnf", filename_body) == -1) { - error (FATAL, "error in asprintf"); - } - ENTER(DosCnf); - parseFileOptions (filename); - free (filename); + .path = CUSTOM_CONFIGURATION_FILE, + .isDirectory = false, + .makePath = NULL, + .stage = OptionLoadingStageCustom, + }, #endif - if (asprintf (&filename,"/etc/%s.conf", filename_body) == -1) { - error (FATAL, "error in asprintf"); - } - ENTER(Etc); - parseFileOptions (filename); - free (filename); - - if (asprintf (&filename,"/usr/local/etc/%s.conf", filename_body) == -1) + .path = ".ctags.d", + .isDirectory = true, + .makePath = prependEnvvar, + .extra = "HOME", + .stage = OptionLoadingStageHomeRecursive, + }, +#ifdef WIN32 { - error (FATAL, "error in asprintf"); - } - ENTER(LocalEtc); - parseFileOptions (filename); - free (filename); - - home = getHome (); - if (home != NULL) + .path = "ctags.d", + .isDirectory = true, + .makePath = getConfigAtHomeOnWindows, + .extra = NULL, + .stage = OptionLoadingStageHomeRecursive, + }, +#endif { - ENTER(HomeRecursive); - parseConfigurationFileOptionsInDirectory (vStringValue (home)); - vStringDelete (home); - } - - ENTER(CurrentRecursive); - parseConfigurationFileOptionsInDirectory ("."); + .path = ".ctags.d", + .isDirectory = true, + .makePath = NULL, + .stage = OptionLoadingStageCurrentRecursive, + }, + { + .path = "ctags.d", + .isDirectory = true, + .makePath = NULL, + .stage = OptionLoadingStageCurrentRecursive, + }, + { .path = NULL }, +}; - ENTER(Preload); - preload (PreloadPathList); +static void parseConfigurationFileOptions (void) +{ + preload (preload_path_list); } static void parseEnvironmentOptions (void) @@ -3255,100 +3632,6 @@ extern void readOptionConfiguration (void) } } -static void installDataPathList (void) -{ - char* dataPath = getenv (CTAGS_DATA_PATH_ENVIRONMENT); - - OptlibPathList = stringListNew (); - PreloadPathList = stringListNew (); - - if (dataPath) - { - char* needle; - - while (dataPath[0]) - { - needle = strchr (dataPath, ':'); - if (needle) - *needle = '\0'; - - appendToDataPathList (dataPath, false); - - if (needle) - { - *needle = ':'; - dataPath = needle + 1; - } - else - break; - } - } - - { - vString *home = getHome (); - if (home != NULL) - { - char *ctags_d; - - ctags_d = combinePathAndFile (vStringValue (home), ".ctags.d"); - - appendToDataPathList (ctags_d, false); - eFree (ctags_d); - vStringDelete (home); - } - } -#ifdef PKGSYSCONFDIR - appendToDataPathList (PKGSYSCONFDIR, false); -#endif -} - -static void installLibexecPathList (void) -{ - char* libexecPath = getenv (CTAGS_LIBEXEC_PATH_ENVIRONMENT); - - DriversPathList = stringListNew (); - - if (libexecPath) - { - char* needle; - - while (libexecPath[0]) - { - needle = strchr (libexecPath, ':'); - if (needle) - *needle = '\0'; - - appendToDataPathList (libexecPath, false); - - if (needle) - { - *needle = ':'; - libexecPath = needle + 1; - } - else - break; - } - } - - { - vString *home = getHome (); - if (home != NULL) - { - char *ctags_d; - - ctags_d = combinePathAndFile (vStringValue (home), ".ctags.d"); - - appendToLibexecPathList (ctags_d, false); - eFree (ctags_d); - vStringDelete (home); - } - } - -#ifdef PKGLIBEXECDIR - appendToLibexecPathList (PKGLIBEXECDIR, false); -#endif -} - /* * Option initialization */ @@ -3356,11 +3639,7 @@ static void installLibexecPathList (void) extern void initOptions (void) { OptionFiles = stringListNew (); - installDataPathList (); - installLibexecPathList (); - verboseSearchPathList (OptlibPathList, "OptlibPathList"); - verboseSearchPathList (PreloadPathList, "PreloadPathList"); - verboseSearchPathList (DriversPathList, "DriversPathList"); + OptlibPathList = stringListNew (); verbose ("Setting option defaults\n"); installHeaderListDefaults (); @@ -3382,6 +3661,7 @@ extern void initOptions (void) processExcludeOption (NULL, ".cvsignore"); processExcludeOption (NULL, "_darcs"); processExcludeOption (NULL, ".deps"); + processExcludeOption (NULL, ".DS_Store"); processExcludeOption (NULL, "EIFGEN"); processExcludeOption (NULL, ".git"); processExcludeOption (NULL, ".gitignore"); @@ -3439,53 +3719,100 @@ extern void freeOptionResources (void) freeString (&Option.filterTerminator); freeList (&Excluded); - if(Option.ignore) - { - hashTableDelete(Option.ignore); - Option.ignore = NULL; - } freeList (&Option.headerExt); freeList (&Option.etagsInclude); freeSearchPathList (&OptlibPathList); - freeSearchPathList (&PreloadPathList); - freeSearchPathList (&DriversPathList); freeList (&OptionFiles); } -#endif -/* GEANY DIFF */ -/* Dummy implementations of unused functions */ -extern void setDefaultTagFileName (void) +static void processDumpOptionsOption (const char *const option CTAGS_ATTR_UNUSED, const char *const parameter CTAGS_ATTR_UNUSED) { -} + fprintf(stdout, "# %s\n", "ParametricOptions"); + for (unsigned int i = 0; i < ARRAY_SIZE(ParametricOptions); i++) + fprintf(stdout, "%s\n", ParametricOptions[i].name); -extern void initOptions (void) -{ + fprintf(stdout, "# %s\n", "BooleanOptions"); + for (unsigned int i = 0; i < ARRAY_SIZE(BooleanOptions); i++) + fprintf(stdout, "%s\n", BooleanOptions[i].name); } -extern void freeOptionResources (void) +extern bool inSandbox (void) { + return (Option.interactive == INTERACTIVE_SANDBOX); } -extern langType getLanguageComponentInOption (const char *const option, - const char *const prefix) +extern bool canUseLineNumberAsLocator (void) { - return LANG_IGNORE; + return (Option.locate != EX_PATTERN); } -extern bool isIncludeFile (const char *const fileName) -{ - return false; -} +/* GEANY DIFF */ +/* tags_ignore is a NULL-terminated array of strings, read from ~/.config/geany/ignore.tags. + * This file contains a space or newline separated list of symbols which should be ignored + * by the C/C++ parser, see -I command line option of ctags for details. */ +char **c_tags_ignore = NULL; -extern void verbose (const char *const format, ...) +/* Determines whether or not "name" should be ignored, per the ignore list. + */ +extern bool isIgnoreToken (const char *const name, + bool *const pIgnoreParens, + const char **const replacement) { -} -/* GEANY DIFF END */ + bool result = false; -extern bool canUseLineNumberAsLocator (void) -{ - return (Option.locate != EX_PATTERN); + if (c_tags_ignore != NULL) + { + const size_t nameLen = strlen (name); + unsigned int i; + unsigned int len = 0; + vString *token = vStringNew(); + + while (c_tags_ignore[len]) + len++; + + if (pIgnoreParens != NULL) + *pIgnoreParens = false; + + for (i = 0 ; i < len ; ++i) + { + size_t tokenLen; + + vStringCopyS (token, c_tags_ignore[i]); + tokenLen = vStringLength (token); + + if (tokenLen >= 2 && vStringChar (token, tokenLen - 1) == '*' && + strncmp (vStringValue (token), name, tokenLen - 1) == 0) + { + result = true; + break; + } + if (strncmp (vStringValue (token), name, nameLen) == 0) + { + if (nameLen == tokenLen) + { + result = true; + break; + } + else if (tokenLen == nameLen + 1 && + vStringChar (token, tokenLen - 1) == '+') + { + result = true; + if (pIgnoreParens != NULL) + *pIgnoreParens = true; + break; + } + else if (vStringChar (token, nameLen) == '=') + { + if (replacement != NULL) + *replacement = vStringValue (token) + nameLen + 1; + break; + } + } + } + vStringDelete (token); + } + return result; } +/* GEANY DIFF END */ diff --git a/ctags/main/options.h b/ctags/main/options.h index 6fb7e39638..75c2f89097 100644 --- a/ctags/main/options.h +++ b/ctags/main/options.h @@ -9,190 +9,43 @@ #ifndef CTAGS_MAIN_OPTIONS_H #define CTAGS_MAIN_OPTIONS_H -#if defined(OPTION_WRITE) -# define CONST_OPTION -#else -# define CONST_OPTION const -#endif - /* * INCLUDE FILES */ #include "general.h" /* must always come first */ +#include "gvars.h" +#include "keyword_p.h" #include -#include "args.h" -#include "field.h" -#include "fmt.h" -#include "parse.h" -#include "strlist.h" -#include "htable.h" -#include "vstring.h" /* * DATA DECLARATIONS */ -typedef enum { OPTION_NONE, OPTION_SHORT, OPTION_LONG } optionType; - -typedef struct sCookedArgs { - /* private */ - Arguments* args; - char *shortOptions; - char simple[2]; - bool isOption; - bool longOption; - const char* parameter; - /* public */ - char* item; -} cookedArgs; - -typedef enum eLocate { - EX_MIX, /* line numbers for defines, patterns otherwise */ - EX_LINENUM, /* -n only line numbers in tag file */ - EX_PATTERN /* -N only patterns in tag file */ -} exCmd; - -typedef enum sortType { - SO_UNSORTED, - SO_SORTED, - SO_FOLDSORTED -} sortType; - -typedef struct sIgnoredTokenInfo { - bool ignoreFollowingParenthesis; /* -I SOMETHING+ */ - char * replacement; /* -I SOMETHING=REPLACEMENT */ -} ignoredTokenInfo; - -/* This stores the command line options. - */ -typedef struct sOptionValues { - hashTable * ignore; /* -I name of file containing tokens to ignore */ - bool append; /* -a append to "tags" file */ - bool backward; /* -B regexp patterns search backwards */ - bool etags; /* -e output Emacs style tags file */ - exCmd locate; /* --excmd EX command used to locate tag */ - bool recurse; /* -R recurse into directories */ - sortType sorted; /* -u,--sort sort tags */ - bool verbose; /* -V verbose */ - bool xref; /* -x generate xref output instead */ - fmtElement *customXfmt; /* compiled code for --xformat=XFMT */ - char *fileList; /* -L name of file containing names of files */ - char *tagFileName; /* -o name of tags file */ - stringList* headerExt; /* -h header extensions */ - char* configFilename; /* --config-filename use instead of 'ctags' in option file names */ - stringList* etagsInclude;/* --etags-include list of TAGS files to include*/ - unsigned int tagFileFormat;/* --format tag file format (level) */ -#ifdef HAVE_ICONV - char *inputEncoding; /* --input-encoding convert text into --output-encoding */ - char *outputEncoding; /* --output-encoding write tags file as this encoding */ -#endif - bool if0; /* --if0 examine code within "#if 0" branch */ - langType language; /* --lang specified language override */ - bool followLinks; /* --link follow symbolic links? */ - bool filter; /* --filter behave as filter: files in, tags out */ - char* filterTerminator; /* --filter-terminator string to output */ - bool tagRelative; /* --tag-relative file paths relative to tag file */ - bool printTotals; /* --totals print cumulative statistics */ - bool lineDirectives; /* --linedirectives process #line directives */ - bool printLanguage; /* --print-language */ - bool guessLanguageEagerly; /* --guess-language-eagerly|-G */ - bool quiet; /* --quiet */ - bool allowXcmdInHomeDir; /* --_allow-xcmd-in-homedir */ - bool fatalWarnings; /* --_fatal-warnings */ - unsigned int patternLengthLimit; /* --pattern-length-limit=N */ - bool putFieldPrefix; /* --put-field-prefix */ - unsigned int maxRecursionDepth; /* --maxdepth= */ - bool machinable; /* --machinable */ - bool withListHeader; /* --with-list-header */ -#ifdef DEBUG - long debugLevel; /* -D debugging output */ - unsigned long breakLine;/* -b input line at which to call lineBreak() */ -#endif -} optionValues; - -typedef enum eOptionLoadingStage { - OptionLoadingStageNone, - OptionLoadingStageCustom, - OptionLoadingStageDosCnf, - OptionLoadingStageEtc, - OptionLoadingStageLocalEtc, - OptionLoadingStageHomeRecursive, - OptionLoadingStageCurrentRecursive, - OptionLoadingStagePreload, - OptionLoadingStageEnvVar, - OptionLoadingStageCmdline, -} OptionLoadingStage; - -/* -* GLOBAL VARIABLES -*/ -extern CONST_OPTION optionValues Option; - /* * FUNCTION PROTOTYPES */ extern void notice (const char *const format, ...) CTAGS_ATTR_PRINTF (1, 2); extern void verbose (const char *const format, ...) CTAGS_ATTR_PRINTF (1, 2); -#define BEGIN_VERBOSE(VFP) do { if (Option.verbose) { \ + +#define BEGIN_VERBOSE(VFP) do { if (ctags_verbose) { \ FILE* VFP = stderr #define END_VERBOSE() } } while (0) -extern void freeList (stringList** const pString); -extern void setDefaultTagFileName (void); -extern void checkOptions (void); -extern bool filesRequired (void); -extern void testEtagsInvocation (void); - -extern cookedArgs* cArgNewFromString (const char* string); -extern cookedArgs* cArgNewFromArgv (char* const* const argv); -extern cookedArgs* cArgNewFromFile (FILE* const fp); -extern cookedArgs* cArgNewFromLineFile (FILE* const fp); -extern void cArgDelete (cookedArgs* const current); -extern bool cArgOff (cookedArgs* const current); -extern bool cArgIsOption (cookedArgs* const current); -extern const char* cArgItem (cookedArgs* const current); -extern void cArgForth (cookedArgs* const current); - -extern bool isExcludedFile (const char* const name); -extern bool isIncludeFile (const char *const fileName); -/* GEANY DIFF */ -/* extern const ignoredTokenInfo * isIgnoreToken (const char *const name); */ -extern bool isIgnoreToken (const char *const name, bool *const pIgnoreParens, const char **const replacement); -/* GEANY DIFF END */ -extern void parseCmdlineOptions (cookedArgs* const cargs); -extern void previewFirstOption (cookedArgs* const cargs); -extern void readOptionConfiguration (void); -extern void initOptions (void); -extern void freeOptionResources (void); -#ifdef HAVE_ICONV -extern void freeEncodingResources (void); -#endif - -/* Return vString must be freed by caller side. */ -extern vString* expandOnCorpusPathList (const char* leaf); -extern vString* expandOnDriversPathList (const char* leaf); - - -extern langType getLanguageComponentInOption (const char *const option, - const char *const prefix); +#define BEGIN_VERBOSE_IF(COND,VFP) do { if (ctags_verbose || (COND)) { \ + FILE* VFP = stderr -extern void processLanguageDefineOption (const char *const option, const char *const parameter); -extern bool processMapOption (const char *const option, const char *const parameter); -extern bool processKindDefinition (const char *const option, const char *const parameter); -extern bool processCorpusOption (const char *const option, const char *const parameter); -extern bool processAliasOption (const char *const option, const char *const parameter); -#ifdef HAVE_ICONV -extern bool processLanguageEncodingOption (const char *const option, const char *const parameter); -#endif -extern bool processRegexOption (const char *const option, const char *const parameter); -extern bool processXcmdOption (const char *const option, const char *const parameter, OptionLoadingStage stage); -typedef void (* mainLoopFunc) (cookedArgs *args, void *data); -extern void setMainLoop (mainLoopFunc func, void *data); +extern bool inSandbox (void); /* This is for emitting a tag for a commnn block of Fortran parser*/ extern bool canUseLineNumberAsLocator (void); +/* GEANY DIFF */ +extern bool isIgnoreToken (const char *const name, + bool *const pIgnoreParens, + const char **const replacement); +/* GEANY DIFF END */ + #endif /* CTAGS_MAIN_OPTIONS_H */ diff --git a/ctags/main/options_p.h b/ctags/main/options_p.h new file mode 100644 index 0000000000..f6a8b044fe --- /dev/null +++ b/ctags/main/options_p.h @@ -0,0 +1,183 @@ +/* +* Copyright (c) 1998-2003, Darren Hiebert +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* Defines internal interface to option processing. +*/ +#ifndef CTAGS_MAIN_OPTIONS_PRIVATE_H +#define CTAGS_MAIN_OPTIONS_PRIVATE_H + +#if defined(OPTION_WRITE) +# define CONST_OPTION +#else +# define CONST_OPTION const +#endif + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ + +#include "args_p.h" +#include "field.h" +#include "fmt_p.h" +#include "options.h" +#include "parse.h" +#include "strlist.h" +#include "vstring.h" + +/* +* MACROS +*/ +#define includeExtensionFlags() (Option.tagFileFormat > 1) + +/* +* DATA DECLARATIONS +*/ +typedef enum { OPTION_NONE, OPTION_SHORT, OPTION_LONG } optionType; + +typedef struct sCookedArgs { + /* private */ + Arguments* args; + char *shortOptions; + char simple[2]; + bool isOption; + bool longOption; + const char* parameter; + /* public */ + char* item; +} cookedArgs; + +typedef enum eLocate { + EX_MIX, /* line numbers for defines, patterns otherwise */ + EX_LINENUM, /* -n only line numbers in tag file */ + EX_PATTERN /* -N only patterns in tag file */ +} exCmd; + +typedef enum sortType { + SO_UNSORTED, + SO_SORTED, + SO_FOLDSORTED +} sortType; + +typedef enum eTagRelative { + TREL_NO, + TREL_YES, + TREL_ALWAYS, + TREL_NEVER, +} tagRelative; + +typedef enum eOptionLoadingStage { + OptionLoadingStageNone, + OptionLoadingStageCustom, + OptionLoadingStageDosCnf, + OptionLoadingStageEtc, + OptionLoadingStageLocalEtc, + OptionLoadingStageHomeRecursive, + OptionLoadingStageCurrentRecursive, + OptionLoadingStagePreload, + OptionLoadingStageEnvVar, + OptionLoadingStageCmdline, +} OptionLoadingStage; + +/* This stores the command line options. + */ +typedef struct sOptionValues { + bool append; /* -a append to "tags" file */ + bool backward; /* -B regexp patterns search backwards */ + bool etags; /* -e output Emacs style tags file */ + exCmd locate; /* --excmd EX command used to locate tag */ + bool recurse; /* -R recurse into directories */ + sortType sorted; /* -u,--sort sort tags */ + bool xref; /* -x generate xref output instead */ + fmtElement *customXfmt; /* compiled code for --xformat=XFMT */ + char *fileList; /* -L name of file containing names of files */ + char *tagFileName; /* -o name of tags file */ + stringList* headerExt; /* -h header extensions */ + stringList* etagsInclude;/* --etags-include list of TAGS files to include*/ + unsigned int tagFileFormat;/* --format tag file format (level) */ +#ifdef HAVE_ICONV + char *inputEncoding; /* --input-encoding convert text into --output-encoding */ + char *outputEncoding; /* --output-encoding write tags file as this encoding */ +#endif + langType language; /* --lang specified language override */ + bool followLinks; /* --link follow symbolic links? */ + bool filter; /* --filter behave as filter: files in, tags out */ + char* filterTerminator; /* --filter-terminator string to output */ + tagRelative tagRelative; /* --tag-relative file paths relative to tag file */ + bool printTotals; /* --totals print cumulative statistics */ + bool lineDirectives; /* --linedirectives process #line directives */ + bool printLanguage; /* --print-language */ + bool guessLanguageEagerly; /* --guess-language-eagerly|-G */ + bool quiet; /* --quiet */ + bool fatalWarnings; /* --_fatal-warnings */ + unsigned int patternLengthLimit; /* --pattern-length-limit=N */ + bool putFieldPrefix; /* --put-field-prefix */ + unsigned int maxRecursionDepth; /* --maxdepth= */ + enum interactiveMode { INTERACTIVE_NONE = 0, + INTERACTIVE_DEFAULT, + INTERACTIVE_SANDBOX, } interactive; /* --interactive */ + bool mtablePrintTotals; /* display mtable statistics */ +#ifdef DEBUG + unsigned long breakLine;/* -b input line at which to call lineBreak() */ +#endif + +} optionValues; + +typedef void (* mainLoopFunc) (cookedArgs *args, void *data); + +/* +* GLOBAL VARIABLES +*/ + +extern CONST_OPTION optionValues Option; + +/* +* FUNCTION PROTOTYPES +*/ +extern void freeList (stringList** const pString); +extern void setDefaultTagFileName (void); +extern void checkOptions (void); +extern bool filesRequired (void); +extern void testEtagsInvocation (void); + +extern cookedArgs* cArgNewFromString (const char* string); +extern cookedArgs* cArgNewFromArgv (char* const* const argv); +extern cookedArgs* cArgNewFromFile (FILE* const fp); +extern cookedArgs* cArgNewFromLineFile (FILE* const fp); +extern void cArgDelete (cookedArgs* const current); +extern bool cArgOff (cookedArgs* const current); +extern bool cArgIsOption (cookedArgs* const current); +extern const char* cArgItem (cookedArgs* const current); +extern void cArgForth (cookedArgs* const current); + +extern bool isExcludedFile (const char* const name); +extern bool isIncludeFile (const char *const fileName); +extern void parseCmdlineOptions (cookedArgs* const cargs); +extern void previewFirstOption (cookedArgs* const cargs); +extern void readOptionConfiguration (void); +extern void initOptions (void); +extern void freeOptionResources (void); + +extern langType getLanguageComponentInOption (const char *const option, + const char *const prefix); + +extern void processLanguageDefineOption (const char *const option, const char *const parameter); +extern bool processMapOption (const char *const option, const char *const parameter); +extern bool processParamOption (const char *const option, const char *const value); +extern bool processKinddefOption (const char *const option, const char *const parameter); +extern bool processKindsOption (const char *const option, const char *const parameter); +extern bool processExtradefOption (const char *const option, const char *const parameter); +extern bool processFielddefOption (const char *const option, const char *const parameter); +extern bool processAliasOption (const char *const option, const char *const parameter); +extern bool processTabledefOption (const char *const option, const char *const parameter); +#ifdef HAVE_ICONV +extern bool processLanguageEncodingOption (const char *const option, const char *const parameter); +#endif +extern bool processRoledefOption (const char *const option, const char *const parameter); + +extern void setMainLoop (mainLoopFunc func, void *data); + +#endif /* CTAGS_MAIN_OPTIONS_PRIVATE_H */ diff --git a/ctags/main/output-ctags.c b/ctags/main/output-ctags.c deleted file mode 100644 index f365f59342..0000000000 --- a/ctags/main/output-ctags.c +++ /dev/null @@ -1,59 +0,0 @@ -/* -* Copyright (c) 1998-2002, Darren Hiebert -* -* This source code is released for free distribution under the terms of the -* GNU General Public License version 2 or (at your option) any later version. -* -* External interface to entry.c -*/ - -#include "general.h" /* must always come first */ - -#include "entry.h" -#include "mio.h" -#include "options.h" -#include "output.h" -#include "read.h" -#include "ptag.h" - -/* GEANY DIFF */ -/* Dummy definitions of writers as we don't need them */ - -tagWriter etagsWriter = { - .writeEntry = NULL, - .writePtagEntry = NULL, - .preWriteEntry = NULL, - .postWriteEntry = NULL, - .useStdoutByDefault = false, -}; - -tagWriter ctagsWriter = { - .writeEntry = NULL, - .writePtagEntry = NULL, - .preWriteEntry = NULL, - .postWriteEntry = NULL, - .useStdoutByDefault = false, -}; - -tagWriter xrefWriter = { - .writeEntry = NULL, - .writePtagEntry = NULL, - .preWriteEntry = NULL, - .postWriteEntry = NULL, - .useStdoutByDefault = false, -}; - -tagWriter jsonWriter = { - .writeEntry = NULL, - .writePtagEntry = NULL, - .preWriteEntry = NULL, - .postWriteEntry = NULL, - .useStdoutByDefault = false, -}; - -extern bool ptagMakeJsonOutputVersion (ptagDesc *desc, void *data CTAGS_ATTR_UNUSED) -{ - return false; -} - -/* GEANY DIFF END */ diff --git a/ctags/main/output.h b/ctags/main/output.h deleted file mode 100644 index 32d9badcb0..0000000000 --- a/ctags/main/output.h +++ /dev/null @@ -1,50 +0,0 @@ -/* -* Copyright (c) 2016, Red Hat, Inc. -* Copyright (c) 2016, Masatake YAMATO -* -* This source code is released for free distribution under the terms of the -* GNU General Public License version 2 or (at your option) any later version. -* -*/ -#ifndef CTAGS_MAIN_OUTPUT_H -#define CTAGS_MAIN_OUTPUT_H - -#include "general.h" /* must always come first */ - -/* Other than writeEntry can be NULL. - The value returned from preWriteEntry is passed to writeEntry, - and postWriteEntry. If a resource is allocated in - preWriteEntry, postWriteEntry should free it. */ - -struct sTagWriter; -typedef struct sTagWriter tagWriter; -struct sTagWriter { - int (* writeEntry) (MIO * mio, const tagEntryInfo *const tag, void *data); - int (* writePtagEntry) (MIO * mio, const ptagDesc *desc, - const char *const fileName, - const char *const pattern, - const char *const parserName, void *data); - void * (* preWriteEntry) (MIO * mio); - void (* postWriteEntry) (MIO * mio, const char* filename, void *data); - bool useStdoutByDefault; -}; - -extern void setTagWriter (tagWriter *tagWriter); -extern tagWriter etagsWriter; -extern tagWriter ctagsWriter; -extern tagWriter xrefWriter; -extern tagWriter jsonWriter; - -extern bool outpuFormatUsedStdoutByDefault (void); - -extern int makePatternStringCommon (const tagEntryInfo *const tag, - int putc_func (char , void *), - int puts_func (const char* , void *), - void *output); -extern void truncateTagLine (char *const line, const char *const token, - const bool discardNewline); -extern void abort_if_ferror(MIO *const fp); - -extern bool ptagMakeJsonOutputVersion (ptagDesc *desc, void *data CTAGS_ATTR_UNUSED); - -#endif diff --git a/ctags/main/param.c b/ctags/main/param.c new file mode 100644 index 0000000000..2a24b61c83 --- /dev/null +++ b/ctags/main/param.c @@ -0,0 +1,57 @@ +/* + * + * Copyright (c) 2016, Red Hat, Inc. + * Copyright (c) 2016, Masatake YAMATO + * + * Author: Masatake YAMATO + * + * This source code is released for free distribution under the terms of the + * GNU General Public License version 2 or (at your option) any later version. + * + */ + +#include "general.h" +#include "param.h" +#include "parse.h" + +#include + + +extern struct colprintTable * paramColprintTableNew (void) +{ + return colprintTableNew ("L:LANGUAGE", "L:NAME","L:DESCRIPTION", NULL); +} + +extern void paramColprintAddParameter (struct colprintTable *table, + langType language, + const parameterHandlerTable *const paramHandler) +{ + struct colprintLine *line = colprintTableGetNewLine(table); + + colprintLineAppendColumnCString (line, getLanguageName (language)); + colprintLineAppendColumnCString (line, paramHandler->name); + colprintLineAppendColumnCString (line, paramHandler->desc); +} + +static int paramColprintCompareLines (struct colprintLine *a , struct colprintLine *b) +{ + const char *a_parser = colprintLineGetColumn (a, 0); + const char *b_parser = colprintLineGetColumn (b, 0); + + int r; + r = strcmp (a_parser, b_parser); + if (r != 0) + return r; + + const char *a_name = colprintLineGetColumn (a, 1); + const char *b_name = colprintLineGetColumn (b, 1); + + return strcmp(a_name, b_name); +} + +extern void paramColprintTablePrint (struct colprintTable *table, bool noparser, + bool withListHeader, bool machinable, FILE *fp) +{ + colprintTableSort (table, paramColprintCompareLines); + colprintTablePrint (table, noparser? 1: 0, withListHeader, machinable, fp); +} diff --git a/ctags/main/param.h b/ctags/main/param.h new file mode 100644 index 0000000000..244d485078 --- /dev/null +++ b/ctags/main/param.h @@ -0,0 +1,38 @@ +/* + * + * Copyright (c) 2016, Red Hat, Inc. + * Copyright (c) 2016, Masatake YAMATO + * + * Author: Masatake YAMATO + * + * This source code is released for free distribution under the terms of the + * GNU General Public License version 2 or (at your option) any later version. + * + */ +#ifndef CTAGS_MAIN_PARAM_H +#define CTAGS_MAIN_PARAM_H + +#include "general.h" + +#include "colprint_p.h" +#include "types.h" + +struct sParameterHandlerTable { + const char *name; + const char *desc; + void (* handleParameter) (langType lang, const char *name, const char *arg); +}; + +extern void applyParameter (const langType language, const char *name, const char *args); + +extern struct colprintTable * paramColprintTableNew (void); +extern void paramColprintAddParameter (struct colprintTable *table, + langType language, + const parameterHandlerTable *const paramHandler); +extern void paramColprintTablePrint (struct colprintTable *table, bool noparser, + bool withListHeader, bool machinable, FILE *fp); + +extern bool paramParserBool (const char *value, bool fallback, + const char *errWhat, const char *errCategory); + +#endif /* CTAGS_MAIN_PARAM_H */ diff --git a/ctags/main/parse.c b/ctags/main/parse.c index 449b8bea62..88bccde1ad 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -13,20 +13,31 @@ */ #include "general.h" /* must always come first */ +/* TODO: This definition should be removed. */ +#define OPTION_WRITE +#include "options_p.h" + #include +#include "ctags.h" #include "debug.h" -#include "entry.h" -#include "flags.h" +#include "entry_p.h" +#include "flags_p.h" +#include "htable.h" #include "keyword.h" #include "main.h" -#define OPTION_WRITE -#include "options.h" +#include "param.h" +#include "parse_p.h" #include "parsers.h" #include "promise.h" -#include "ptag.h" +#include "promise_p.h" +#include "ptag_p.h" +#include "ptrarray.h" #include "read.h" #include "routines.h" +#include "routines_p.h" +#include "subparser.h" +#include "trace.h" #include "trashbox.h" #include "vstring.h" #ifdef HAVE_ICONV @@ -54,20 +65,42 @@ typedef struct { enum specType specType; } parserCandidate; -static ptrArray *parsersUsedInCurrentInput; +typedef struct sParserObject { + parserDefinition *def; + + kindDefinition* fileKind; + + stringList* currentPatterns; /* current list of file name patterns */ + stringList* currentExtensions; /* current list of extensions */ + stringList* currentAliases; /* current list of aliases */ + + unsigned int initialized:1; /* initialize() is called or not */ + unsigned int dontEmit:1; /* run but don't emit tags + (a subparser requests run this parser.) */ + unsigned int pseudoTagPrinted:1; /* pseudo tags about this parser + is emitted or not. */ + + unsigned int anonymousIdentiferId; /* managed by anon* functions */ + + struct slaveControlBlock *slaveControlBlock; + struct kindControlBlock *kindControlBlock; + struct lregexControlBlock *lregexControlBlock; +} parserObject; /* * FUNCTION PROTOTYPES */ +static void lazyInitialize (langType language); #ifndef CTAGS_LIB static void addParserPseudoTags (langType language); #endif static void installKeywordTable (const langType language); static void installTagRegexTable (const langType language); static void installTagXpathTable (const langType language); -static void anonResetMaybe (parserDefinition *lang); -static void clearParsersUsedInCurrentInput (void); +static void anonResetMaybe (parserObject *parser); +static void setupAnon (void); +static void teardownAnon (void); /* * DATA DEFINITIONS @@ -91,8 +124,9 @@ static parserDefinitionFunc* BuiltInParsers[] = { #endif #endif }; -static parserDefinition** LanguageTable = NULL; +static parserObject* LanguageTable = NULL; static unsigned int LanguageCount = 0; +static hashTable* LanguageHTable = NULL; static kindDefinition defaultFileKind = { .enabled = false, .letter = KIND_FILE_DEFAULT, @@ -104,6 +138,18 @@ static kindDefinition defaultFileKind = { * FUNCTION DEFINITIONS */ +static bool isLanguageNameChar(int c) +{ + if (isgraph(c)) + { + if (c == '\'' || c == '"' || c == ';') + return false; + return true; + } + else + return false; +} + extern unsigned int countParsers (void) { return LanguageCount; @@ -112,16 +158,7 @@ extern unsigned int countParsers (void) extern int makeSimpleTag ( const vString* const name, const int kindIndex) { - int r = CORK_NIL; - - if (isInputLanguageKindEnabled(kindIndex) && name != NULL && vStringLength (name) > 0) - { - tagEntryInfo e; - initTagEntry (&e, vStringValue (name), kindIndex); - - r = makeTagEntry (&e); - } - return r; + return makeSimpleRefTag (name, kindIndex, ROLE_INDEX_DEFINITION); } extern int makeSimpleRefTag (const vString* const name, const int kindIndex, @@ -129,10 +166,7 @@ extern int makeSimpleRefTag (const vString* const name, const int kindIndex, { int r = CORK_NIL; - if (! isXtagEnabled (XTAG_REFERENCE_TAGS)) - return r; - - Assert (roleIndex < countInputLanguageRoles(kindIndex)); + Assert (roleIndex < (int)countInputLanguageRoles(kindIndex)); /* do not check for kind being disabled - that happens later in makeTagEntry() */ if (name != NULL && vStringLength (name) > 0) @@ -147,52 +181,35 @@ extern int makeSimpleRefTag (const vString* const name, const int kindIndex, extern bool isLanguageEnabled (const langType language) { - const parserDefinition* const lang = LanguageTable [language]; + const parserDefinition* const lang = LanguageTable [language].def; if (!lang->enabled) return false; - if (lang->method & METHOD_XCMD) - initializeParser (language); - - if ((lang->method & METHOD_XCMD) && - (!(lang->method & METHOD_XCMD_AVAILABLE)) && - (lang->kindTable == NULL) && - (!(lang->method & METHOD_REGEX)) && - (!(lang->method & METHOD_XPATH))) + if ((lang->kindTable == NULL) && + (!(lang->method & METHOD_REGEX)) && + (!(lang->method & METHOD_XPATH))) return false; else return true; } +extern bool isLanguageVisible (const langType language) +{ + const parserDefinition* const lang = LanguageTable [language].def; + + return !lang->invisible; +} + /* * parserDescription mapping management */ extern parserDefinition* parserNew (const char* name) -{ - return parserNewFull (name, 0); -} - -static kindDefinition* fileKindNew (char letter) -{ - kindDefinition *fileKind; - - fileKind = xMalloc (1, kindDefinition); - *(fileKind) = defaultFileKind; - fileKind->letter = letter; - return fileKind; -} - -extern parserDefinition* parserNewFull (const char* name, char fileKind) { parserDefinition* result = xCalloc (1, parserDefinition); result->name = eStrdup (name); - if (fileKind) - result->fileKind = fileKindNew(fileKind); - else - result->fileKind = &defaultFileKind; result->enabled = true; return result; } @@ -200,13 +217,13 @@ extern parserDefinition* parserNewFull (const char* name, char fileKind) extern bool doesLanguageAllowNullTag (const langType language) { Assert (0 <= language && language < (int) LanguageCount); - return LanguageTable [language]->allowNullTag; + return LanguageTable [language].def->allowNullTag; } extern bool doesLanguageRequestAutomaticFQTag (const langType language) { Assert (0 <= language && language < (int) LanguageCount); - return LanguageTable [language]->requestAutomaticFQTag; + return LanguageTable [language].def->requestAutomaticFQTag; } extern const char *getLanguageName (const langType language) @@ -217,29 +234,93 @@ extern const char *getLanguageName (const langType language) else { Assert (0 <= language && language < (int) LanguageCount); - result = LanguageTable [language]->name; + result = LanguageTable [language].def->name; } return result; } -extern kindDefinition* getLanguageFileKind (const langType language) +extern const char *getLanguageKindName (const langType language, const int kindIndex) { - kindDefinition* kind; + kindDefinition* kdef = getLanguageKind (language, kindIndex); + return kdef->name; +} - Assert (0 <= language && language < (int) LanguageCount); +static kindDefinition kindGhost = { + .letter = KIND_GHOST, + .name = KIND_GHOST_LONG, + .description = KIND_GHOST_LONG, +}; - kind = LanguageTable [language]->fileKind; +extern int defineLanguageKind (const langType language, kindDefinition *def, + freeKindDefFunc freeKindDef) +{ + return defineKind (LanguageTable [language].kindControlBlock, def, freeKindDef); +} - Assert (kind != NULL); +extern unsigned int countLanguageKinds (const langType language) +{ + return countKinds (LanguageTable [language].kindControlBlock); +} - return kind; +extern unsigned int countLanguageRoles (const langType language, int kindIndex) +{ + return countRoles (LanguageTable [language].kindControlBlock, kindIndex); } extern kindDefinition* getLanguageKind (const langType language, int kindIndex) +{ + kindDefinition* kdef; + + Assert (0 <= language && language < (int) LanguageCount); + + switch (kindIndex) + { + case KIND_FILE_INDEX: + kdef = LanguageTable [language].fileKind; + break; + case KIND_GHOST_INDEX: + kdef = &kindGhost; + break; + default: + Assert (kindIndex >= 0); + kdef = getKind (LanguageTable [language].kindControlBlock, kindIndex); + } + return kdef; +} + +extern kindDefinition* getLanguageKindForLetter (const langType language, char kindLetter) { Assert (0 <= language && language < (int) LanguageCount); + if (kindLetter == LanguageTable [language].fileKind->letter) + return LanguageTable [language].fileKind; + else if (kindLetter == KIND_GHOST) + return &kindGhost; + else + return getKindForLetter (LanguageTable [language].kindControlBlock, kindLetter); +} + +extern kindDefinition* getLanguageKindForName (const langType language, const char *kindName) +{ + Assert (0 <= language && language < (int) LanguageCount); + Assert (kindName); + + if (strcmp(kindName, LanguageTable [language].fileKind->name) == 0) + return LanguageTable [language].fileKind; + else if (strcmp(kindName, KIND_GHOST_LONG) == 0) + return &kindGhost; + else + return getKindForName (LanguageTable [language].kindControlBlock, kindName); +} - return (LanguageTable [language]->kindTable) + kindIndex; +extern roleDefinition* getLanguageRole(const langType language, int kindIndex, int roleIndex) +{ + return getRole (LanguageTable [language].kindControlBlock, kindIndex, roleIndex); +} + +extern roleDefinition* getLanguageRoleForName (const langType language, int kindIndex, + const char *roleName) +{ + return getRoleForName (LanguageTable [language].kindControlBlock, kindIndex, roleName); } extern langType getNamedLanguage (const char *const name, size_t len) @@ -248,27 +329,24 @@ extern langType getNamedLanguage (const char *const name, size_t len) unsigned int i; Assert (name != NULL); - for (i = 0 ; i < LanguageCount && result == LANG_IGNORE ; ++i) + if (len == 0) { - const parserDefinition* const lang = LanguageTable [i]; - if (lang->name != NULL) + parserDefinition *def = (parserDefinition *)hashTableGetItem (LanguageHTable, name); + if (def) + result = def->id; + } + else + for (i = 0 ; i < LanguageCount && result == LANG_IGNORE ; ++i) { - if (len == 0) - { - if (strcasecmp (name, lang->name) == 0) - result = i; - } - else - { - vString* vstr = vStringNewInit (name); - vStringTruncate (vstr, len); - - if (strcasecmp (vStringValue (vstr), lang->name) == 0) - result = i; - vStringDelete (vstr); - } + const parserDefinition* const lang = LanguageTable [i].def; + Assert (lang->name); + vString* vstr = vStringNewInit (name); + vStringTruncate (vstr, len); + + if (strcasecmp (vStringValue (vstr), lang->name) == 0) + result = i; + vStringDelete (vstr); } - } return result; } @@ -286,20 +364,20 @@ static langType getNameOrAliasesLanguageAndSpec (const char *const key, langType for (i = start_index ; i < LanguageCount && result == LANG_IGNORE ; ++i) { - const parserDefinition* const lang = LanguageTable [i]; - stringList* const aliases = lang->currentAliases; + const parserObject* const parser = LanguageTable + i; + stringList* const aliases = parser->currentAliases; vString* tmp; /* isLanguageEnabled is not used here. It calls initializeParser which takes cost. */ - if (! lang->enabled) + if (! parser->def->enabled) continue; - if (lang->name != NULL && strcasecmp (key, lang->name) == 0) + if (parser->def->name != NULL && strcasecmp (key, parser->def->name) == 0) { result = i; - *spec = lang->name; + *spec = parser->def->name; *specType = SPEC_NAME; } else if (aliases != NULL && (tmp = stringListFileFinds (aliases, key))) @@ -312,6 +390,17 @@ static langType getNameOrAliasesLanguageAndSpec (const char *const key, langType return result; } +extern langType getLanguageForCommand (const char *const command, langType startFrom) +{ + const char *const tmp_command = baseFilename (command); + char *tmp_spec; + enum specType tmp_specType; + + return getNameOrAliasesLanguageAndSpec (tmp_command, startFrom, + (const char **const)&tmp_spec, + &tmp_specType); +} + static langType getPatternLanguageAndSpec (const char *const baseName, langType start_index, const char **const spec, enum specType *specType) { @@ -326,13 +415,14 @@ static langType getPatternLanguageAndSpec (const char *const baseName, langType *spec = NULL; for (i = start_index ; i < LanguageCount && result == LANG_IGNORE ; ++i) { - stringList* const ptrns = LanguageTable [i]->currentPatterns; + parserObject *parser = LanguageTable + i; + stringList* const ptrns = parser->currentPatterns; vString* tmp; /* isLanguageEnabled is not used here. It calls initializeParser which takes cost. */ - if (! LanguageTable [i]->enabled) + if (! parser->def->enabled) continue; if (ptrns != NULL && (tmp = stringListFileFinds (ptrns, baseName))) @@ -346,13 +436,14 @@ static langType getPatternLanguageAndSpec (const char *const baseName, langType for (i = start_index ; i < LanguageCount && result == LANG_IGNORE ; ++i) { - stringList* const exts = LanguageTable [i]->currentExtensions; + parserObject *parser = LanguageTable + i; + stringList* const exts = parser->currentExtensions; vString* tmp; /* isLanguageEnabled is not used here. It calls initializeParser which takes cost. */ - if (! LanguageTable [i]->enabled) + if (! parser->def->enabled) continue; if (exts != NULL && (tmp = stringListExtensionFinds (exts, @@ -368,6 +459,17 @@ static langType getPatternLanguageAndSpec (const char *const baseName, langType return result; } +extern langType getLanguageForFilename (const char *const filename, langType startFrom) +{ + const char *const tmp_filename = baseFilename (filename); + char *tmp_spec; + enum specType tmp_specType; + + return getPatternLanguageAndSpec (tmp_filename, startFrom, + (const char **const)&tmp_spec, + &tmp_specType); +} + static parserCandidate* parserCandidateNew(unsigned int count CTAGS_ATTR_UNUSED) { parserCandidate* candidates; @@ -498,7 +600,7 @@ static vString* determineEmacsModeAtFirstLine (const char* const line) p += strlen("mode:"); for ( ; isspace ((int) *p) ; ++p) ; /* no-op */ - for ( ; *p != '\0' && (isalnum ((int) *p) || *p == '-') ; ++p) + for ( ; *p != '\0' && isLanguageNameChar ((int) *p) ; ++p) vStringPut (mode, (int) *p); } else @@ -509,7 +611,7 @@ static vString* determineEmacsModeAtFirstLine (const char* const line) if (end == NULL) goto out; - for ( ; p < end && (isalnum ((int) *p) || *p == '-') ; ++p) + for ( ; p < end && isLanguageNameChar ((int) *p) ; ++p) vStringPut (mode, (int) *p); for ( ; isspace ((int) *p) ; ++p) @@ -560,7 +662,7 @@ static vString* determineEmacsModeAtEOF (MIO* const fp) p += strlen ("mode:"); for ( ; isspace ((int) *p) ; ++p) ; /* no-op */ - for ( ; *p != '\0' && (isalnum ((int) *p) || *p == '-') ; ++p) + for ( ; *p != '\0' && isLanguageNameChar ((int) *p) ; ++p) vStringPut (mode, (int) *p); } else if (headerFound && (p = strstr(line, "End:"))) @@ -685,7 +787,23 @@ static vString* extractVimFileType(MIO* input) [text]{white}{vi:|vim:|ex:}[white]{options} */ } -static vString* determineZshAutoloadTag (const char *const modeline) +static vString* extractMarkGeneric (MIO* input, + vString * (* determiner)(const char *const, void *), + void *data) +{ + vString* const vLine = vStringNew (); + const char* const line = readLineRaw (vLine, input); + vString* mode = NULL; + + if (line) + mode = determiner (line, data); + + vStringDelete (vLine); + return mode; +} + +static vString* determineZshAutoloadTag (const char *const modeline, + void *data CTAGS_ATTR_UNUSED) { /* See "Autoloaded files" in zsh info. ------------------------------------- @@ -702,17 +820,24 @@ static vString* determineZshAutoloadTag (const char *const modeline) static vString* extractZshAutoloadTag(MIO* input) { - vString* const vLine = vStringNew (); - const char* const line = readLineRaw (vLine, input); - vString* mode = NULL; + return extractMarkGeneric (input, determineZshAutoloadTag, NULL); +} - if (line) - mode = determineZshAutoloadTag (line); +static vString* determinePHPMark(const char *const modeline, + void *data CTAGS_ATTR_UNUSED) +{ + if (strncmp (modeline, "selectLanguage; + selector = LanguageTable[ lang ].def->selectLanguage; if (selector == NULL) return false; @@ -809,7 +938,7 @@ commonSelector (const parserCandidate *candidates, int n_candidates) selectLanguage *selector; int i; - selector = LanguageTable[ candidates[0].lang ]->selectLanguage; + selector = LanguageTable[ candidates[0].lang ].def->selectLanguage; if (selector == NULL) return NULL; @@ -830,9 +959,19 @@ commonSelector (const parserCandidate *candidates, int n_candidates) * language associated with the string returned by the selector. */ static int -pickLanguageBySelection (selectLanguage selector, MIO *input) +pickLanguageBySelection (selectLanguage selector, MIO *input, + parserCandidate *candidates, + unsigned int nCandidates) { - const char *lang = selector(input); + const char *lang; + langType *cs = xMalloc(nCandidates, langType); + unsigned int i; + + for (i = 0; i < nCandidates; i++) + cs[i] = candidates[i].lang; + lang = selector(input, cs, nCandidates); + eFree (cs); + if (lang) { verbose (" selection: %s\n", lang); @@ -847,7 +986,7 @@ pickLanguageBySelection (selectLanguage selector, MIO *input) static int compareParsersByName (const void *a, const void* b) { - parserDefinition *const *la = a, *const *lb = b; + const parserDefinition *const *la = a, *const *lb = b; return strcasecmp ((*la)->name, (*lb)->name); } @@ -862,8 +1001,8 @@ static int sortParserCandidatesBySpecType (const void *a, const void *b) doesn't do "stable sort". To make the result of sorting predictable, compare the names of parsers when their specType is the same. */ - parserDefinition *la = LanguageTable [ap->lang]; - parserDefinition *lb = LanguageTable [bp->lang]; + parserDefinition *la = LanguageTable [ap->lang].def; + parserDefinition *lb = LanguageTable [bp->lang].def; return compareParsersByName (&la, &lb); } else @@ -902,7 +1041,7 @@ static void verboseReportCandidate (const char *header, for (i = 0; i < n_candidates; i++) verbose (" %u: %s (%s: \"%s\")\n", i, - LanguageTable[candidates[i].lang]->name, + LanguageTable[candidates[i].lang].def->name, specTypeName [candidates[i].specType], candidates[i].spec); } @@ -951,7 +1090,7 @@ static langType getSpecLanguageCommon (const char *const spec, struct getLangCtx GLC_FOPEN_IF_NECESSARY(glc, fopen_error, memStreamRequired); if (selector) { verbose (" selector: %p\n", selector); - language = pickLanguageBySelection(selector, glc->input); + language = pickLanguageBySelection(selector, glc->input, candidates, n_candidates); } else { verbose (" selector: NONE\n"); fopen_error: @@ -1022,9 +1161,17 @@ tasteLanguage (struct getLangCtx *glc, const struct taster *const tasters, int n return LANG_IGNORE; } + +struct GetLanguageRequest { + enum { GLR_OPEN, GLR_DISCARD, GLR_REUSE, } type; + const char *const fileName; + MIO *mio; +}; + static langType -getFileLanguageInternal (const char *const fileName, MIO **mio) +getFileLanguageForRequestInternal (struct GetLanguageRequest *req) { + const char *const fileName = req->fileName; langType language; /* ctags tries variety ways(HINTS) to choose a proper language @@ -1040,7 +1187,7 @@ getFileLanguageInternal (const char *const fileName, MIO **mio) candidates. If a hint chooses multiple candidates, and selection failure is - occured, the hint records one of the candidates as FALLBACK for + occurred, the hint records one of the candidates as FALLBACK for the hint. (The candidates are stored in an array. The first element of the array is recorded. However, there is no specification about the order of elements in the array.) @@ -1058,7 +1205,7 @@ getFileLanguageInternal (const char *const fileName, MIO **mio) int i; struct getLangCtx glc = { .fileName = fileName, - .input = NULL, + .input = (req->type == GLR_REUSE)? mio_ref (req->mio): NULL, .err = false, }; const char* const baseName = baseFilename (fileName); @@ -1091,10 +1238,10 @@ getFileLanguageInternal (const char *const fileName, MIO **mio) } } - fstatus = eStat (fileName); - if (fstatus && fstatus->exists) + /* If the input is already opened, we don't have to verify the existence. */ + if (glc.input || ((fstatus = eStat (fileName)) && fstatus->exists)) { - if (fstatus->isExecutable || Option.guessLanguageEagerly) + if ((fstatus && fstatus->isExecutable) || Option.guessLanguageEagerly) { GLC_FOPEN_IF_NECESSARY (&glc, cleanup, false); language = tasteLanguage(&glc, eager_tasters, 1, @@ -1115,8 +1262,8 @@ getFileLanguageInternal (const char *const fileName, MIO **mio) cleanup: - if (mio && glc.input) - *mio = mio_ref (glc.input); + if (req->type == GLR_OPEN && glc.input) + req->mio = mio_ref (glc.input); GLC_FCLOSE(&glc); if (fstatus) eStatFree (fstatus); @@ -1135,19 +1282,16 @@ getFileLanguageInternal (const char *const fileName, MIO **mio) return language; } -static langType getFileLanguageAndKeepMIO (const char *const fileName, MIO **mio) +static langType getFileLanguageForRequest (struct GetLanguageRequest *req) { langType l = Option.language; - if (mio) - *mio = NULL; - if (l == LANG_AUTO) - return getFileLanguageInternal(fileName, mio); + return getFileLanguageForRequestInternal(req); else if (! isLanguageEnabled (l)) { error (FATAL, - "%s parser specified with --language-force is disabled or not available(xcmd)", + "%s parser specified with --language-force is disabled", getLanguageName (l)); /* For suppressing warnings. */ return LANG_AUTO; @@ -1156,31 +1300,14 @@ static langType getFileLanguageAndKeepMIO (const char *const fileName, MIO **mio return Option.language; } -extern langType getFileLanguage (const char *const fileName) -{ - return getFileLanguageAndKeepMIO(fileName, NULL); -} - -extern langType getLanguageForCommand (const char *const command, langType startFrom) -{ - const char *const tmp_command = baseFilename (command); - char *tmp_spec; - enum specType tmp_specType; - - return getNameOrAliasesLanguageAndSpec (tmp_command, startFrom, - (const char **const)&tmp_spec, - &tmp_specType); -} - -extern langType getLanguageForFilename (const char *const filename, langType startFrom) +extern langType getLanguageForFilenameAndContents (const char *const fileName) { - const char *const tmp_filename = baseFilename (filename); - char *tmp_spec; - enum specType tmp_specType; + struct GetLanguageRequest req = { + .type = GLR_DISCARD, + .fileName = fileName, + }; - return getPatternLanguageAndSpec (tmp_filename, startFrom, - (const char **const)&tmp_spec, - &tmp_specType); + return getFileLanguageForRequest (&req); } typedef void (*languageCallback) (langType language, void* user_data); @@ -1191,17 +1318,18 @@ static void foreachLanguage(languageCallback callback, void *user_data) unsigned int i; for (i = 0 ; i < LanguageCount && result == LANG_IGNORE ; ++i) { - const parserDefinition* const lang = LanguageTable [i]; + const parserDefinition* const lang = LanguageTable [i].def; if (lang->name != NULL) callback(i, user_data); } } -extern void printLanguageMap (const langType language, FILE *fp) +static void printLanguageMap (const langType language, FILE *fp) { bool first = true; unsigned int i; - stringList* map = LanguageTable [language]->currentPatterns; + parserObject *parser = LanguageTable + language; + stringList* map = parser->currentPatterns; Assert (0 <= language && language < (int) LanguageCount); for (i = 0 ; map != NULL && i < stringListCount (map) ; ++i) { @@ -1209,7 +1337,7 @@ extern void printLanguageMap (const langType language, FILE *fp) vStringValue (stringListItem (map, i))); first = false; } - map = LanguageTable [language]->currentExtensions; + map = parser->currentExtensions; for (i = 0 ; map != NULL && i < stringListCount (map) ; ++i) { fprintf (fp, "%s.%s", (first ? "" : " "), @@ -1220,27 +1348,27 @@ extern void printLanguageMap (const langType language, FILE *fp) extern void installLanguageMapDefault (const langType language) { - parserDefinition* lang; + parserObject* parser; Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - if (lang->currentPatterns != NULL) - stringListDelete (lang->currentPatterns); - if (lang->currentExtensions != NULL) - stringListDelete (lang->currentExtensions); - - if (lang->patterns == NULL) - lang->currentPatterns = stringListNew (); + parser = LanguageTable + language; + if (parser->currentPatterns != NULL) + stringListDelete (parser->currentPatterns); + if (parser->currentExtensions != NULL) + stringListDelete (parser->currentExtensions); + + if (parser->def->patterns == NULL) + parser->currentPatterns = stringListNew (); else { - lang->currentPatterns = - stringListNewFromArgv (lang->patterns); + parser->currentPatterns = + stringListNewFromArgv (parser->def->patterns); } - if (lang->extensions == NULL) - lang->currentExtensions = stringListNew (); + if (parser->def->extensions == NULL) + parser->currentExtensions = stringListNew (); else { - lang->currentExtensions = - stringListNewFromArgv (lang->extensions); + parser->currentExtensions = + stringListNewFromArgv (parser->def->extensions); } BEGIN_VERBOSE(vfp); { @@ -1260,27 +1388,30 @@ extern void installLanguageMapDefaults (void) } } -static void printAliases (const langType language, FILE *fp); extern void installLanguageAliasesDefault (const langType language) { - parserDefinition* lang; + parserObject* parser; Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - if (lang->currentAliases != NULL) - stringListDelete (lang->currentAliases); + parser = LanguageTable + language; + if (parser->currentAliases != NULL) + stringListDelete (parser->currentAliases); - if (lang->aliases == NULL) - lang->currentAliases = stringListNew (); + if (parser->def->aliases == NULL) + parser->currentAliases = stringListNew (); else { - lang->currentAliases = - stringListNewFromArgv (lang->aliases); + parser->currentAliases = + stringListNewFromArgv (parser->def->aliases); } BEGIN_VERBOSE(vfp); - printAliases (language, vfp); + if (parser->currentAliases != NULL) + for (unsigned int i = 0 ; i < stringListCount (parser->currentAliases) ; ++i) + fprintf (vfp, " %s", vStringValue ( + stringListItem (parser->currentAliases, i))); putc ('\n', vfp); END_VERBOSE(); } + extern void installLanguageAliasesDefaults (void) { unsigned int i; @@ -1294,20 +1425,23 @@ extern void installLanguageAliasesDefaults (void) extern void clearLanguageMap (const langType language) { Assert (0 <= language && language < (int) LanguageCount); - stringListClear (LanguageTable [language]->currentPatterns); - stringListClear (LanguageTable [language]->currentExtensions); + stringListClear ((LanguageTable + language)->currentPatterns); + stringListClear ((LanguageTable + language)->currentExtensions); } extern void clearLanguageAliases (const langType language) { Assert (0 <= language && language < (int) LanguageCount); - stringListClear (LanguageTable [language]->currentAliases); + + parserObject* parser = (LanguageTable + language); + if (parser->currentAliases) + stringListClear (parser->currentAliases); } static bool removeLanguagePatternMap1(const langType language, const char *const pattern) { bool result = false; - stringList* const ptrn = LanguageTable [language]->currentPatterns; + stringList* const ptrn = (LanguageTable + language)->currentPatterns; if (ptrn != NULL && stringListDeleteItemExtension (ptrn, pattern)) { @@ -1336,18 +1470,18 @@ extern void addLanguagePatternMap (const langType language, const char* ptrn, bool exclusiveInAllLanguages) { vString* const str = vStringNewInit (ptrn); - parserDefinition* lang; + parserObject* parser; Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; + parser = LanguageTable + language; if (exclusiveInAllLanguages) removeLanguagePatternMap (LANG_AUTO, ptrn); - stringListAdd (lang->currentPatterns, str); + stringListAdd (parser->currentPatterns, str); } static bool removeLanguageExtensionMap1 (const langType language, const char *const extension) { bool result = false; - stringList* const exts = LanguageTable [language]->currentExtensions; + stringList* const exts = (LanguageTable + language)->currentExtensions; if (exts != NULL && stringListDeleteItemExtension (exts, extension)) { @@ -1380,25 +1514,38 @@ extern void addLanguageExtensionMap ( Assert (0 <= language && language < (int) LanguageCount); if (exclusiveInAllLanguages) removeLanguageExtensionMap (LANG_AUTO, extension); - stringListAdd (LanguageTable [language]->currentExtensions, str); + stringListAdd ((LanguageTable + language)->currentExtensions, str); } extern void addLanguageAlias (const langType language, const char* alias) { vString* const str = vStringNewInit (alias); - parserDefinition* lang; + parserObject* parser; Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - if (lang->currentAliases == NULL) - lang->currentAliases = stringListNew (); - stringListAdd (lang->currentAliases, str); + parser = LanguageTable + language; + if (parser->currentAliases == NULL) + parser->currentAliases = stringListNew (); + stringListAdd (parser->currentAliases, str); } extern void enableLanguage (const langType language, const bool state) { Assert (0 <= language && language < (int) LanguageCount); - LanguageTable [language]->enabled = state; + LanguageTable [language].def->enabled = state; +} + +#ifdef DO_TRACING +extern void traceLanguage (langType language) +{ + Assert (0 <= language && language < (int) LanguageCount); + LanguageTable [language].def->traced = true; +} +extern bool isLanguageTraced (langType language) +{ + Assert (0 <= language && language < (int) LanguageCount); + return LanguageTable [language].def->traced; } +#endif /* DO_TRACING */ extern void enableLanguages (const bool state) { @@ -1407,28 +1554,13 @@ extern void enableLanguages (const bool state) enableLanguage (i, state); } -#ifdef DEBUG -static bool doesParserUseKind (const parserDefinition *const parser, char letter) -{ - unsigned int k; - - for (k = 0; k < parser->kindCount; k++) - if (parser->kinds [k].letter == letter) - return true; - return false; -} -#endif - static void installFieldDefinition (const langType language) { unsigned int i; parserDefinition * parser; Assert (0 <= language && language < (int) LanguageCount); - parser = LanguageTable [language]; - if (parser->fieldCount > PRE_ALLOCATED_PARSER_FIELDS) - error (FATAL, - "INTERNAL ERROR: in a parser, fields are defined more than PRE_ALLOCATED_PARSER_FIELDS\n"); + parser = LanguageTable [language].def; if (parser->fieldTable != NULL) { @@ -1437,32 +1569,71 @@ static void installFieldDefinition (const langType language) } } +static void installXtagDefinition (const langType language) +{ + unsigned int i; + parserDefinition * parser; + + Assert (0 <= language && language < (int) LanguageCount); + parser = LanguageTable [language].def; + + if (parser->xtagTable != NULL) + { + for (i = 0; i < parser->xtagCount; i++) + defineXtag (& parser->xtagTable [i], language); + } +} + static void initializeParserOne (langType lang) { - parserDefinition *const parser = LanguageTable [lang]; + parserObject *const parser = LanguageTable + lang; if (parser->initialized) - return; + goto out; - verbose ("Initialize parser: %s\n", parser->name); + verbose ("Initialize parser: %s\n", parser->def->name); parser->initialized = true; installKeywordTable (lang); - installTagRegexTable (lang); installTagXpathTable (lang); installFieldDefinition (lang); + installXtagDefinition (lang); - if (hasScopeActionInRegex (lang) - || parser->requestAutomaticFQTag) - parser->useCork = true; + /* regex definitions refers xtag definitions. + So installing RegexTable must be after installing + xtag definitions. */ + installTagRegexTable (lang); + + if (parser->def->initialize != NULL) + parser->def->initialize (lang); - if (parser->initialize != NULL) - parser->initialize (lang); + initializeDependencies (parser->def, parser->slaveControlBlock); - initializeSubparsers (parser); + Assert (parser->fileKind != NULL); + Assert (!doesParserUseKind (parser->kindControlBlock, parser->fileKind->letter)); - Assert (parser->fileKind != KIND_NULL); - Assert (!doesParserUseKind (parser, parser->fileKind->letter)); + return; + + out: + /* lazyInitialize() installs findRegexTags() to parser->parser. + findRegexTags() should be installed to a parser if the parser is + optlib based(created by --langdef) and has some regex patterns(defined + with --regex-). findRegexTags() makes regex matching work. + + If a parser can be initialized during evaluating options, + --fields-=+{something}, for an example. + If such option is evaluated first, evaluating --regex-=... + option doesn't cause installing findRegexTags. As the result + regex matching doesn't work. lazyInitialize was called only + once when --fields-=+{something} was evaluated. In the + timing ctags had not seen --regex-=.... Even though + ctags saw --regex-=.... after initializing, there + was no chance to install findRegexTags() to parser->parser. + + Following code block gives extra chances to call lazyInitialize) + which installs findRegexTags() to parser->parser. */ + if (parser->def->initialize == lazyInitialize) + parser->def->initialize (lang); } extern void initializeParser (langType lang) @@ -1482,25 +1653,61 @@ static void linkDependenciesAtInitializeParsing (parserDefinition *const parser) unsigned int i; parserDependency *d; langType upper; - parserDefinition *upperParserDef; + parserObject *upperParser; for (i = 0; i < parser->dependencyCount; i++) { d = parser->dependencies + i; upper = getNamedLanguage (d->upperParser, 0); - upperParserDef = LanguageTable [upper]; - - linkDependencyAtInitializeParsing (d->type, upperParserDef, parser); + upperParser = LanguageTable + upper; + + linkDependencyAtInitializeParsing (d->type, upperParser->def, + upperParser->slaveControlBlock, + upperParser->kindControlBlock, + parser, + (LanguageTable + parser->id)->kindControlBlock, + d->data); } } +/* Used in both builtin and optlib parsers. */ +static void initializeParsingCommon (parserDefinition *def, bool is_builtin) +{ + parserObject *parser; + + if (is_builtin) + verbose ("%s%s", LanguageCount > 0 ? ", " : "", def->name); + else + verbose ("Add optlib parser: %s\n", def->name); + + def->id = LanguageCount++; + parser = LanguageTable + def->id; + parser->def = def; + + hashTablePutItem (LanguageHTable, def->name, def); + + parser->fileKind = &defaultFileKind; + + parser->kindControlBlock = allocKindControlBlock (def); + parser->slaveControlBlock = allocSlaveControlBlock (def); + parser->lregexControlBlock = allocLregexControlBlock (def); +} + extern void initializeParsing (void) { unsigned int builtInCount; unsigned int i; builtInCount = ARRAY_SIZE (BuiltInParsers); - LanguageTable = xMalloc (builtInCount, parserDefinition*); + LanguageTable = xMalloc (builtInCount, parserObject); + memset(LanguageTable, 0, builtInCount * sizeof (parserObject)); + + LanguageHTable = hashTableNew (127, + hashCstrcasehash, + hashCstrcaseeq, + NULL, + NULL); + DEFAULT_TRASH_BOX(LanguageHTable, hashTableDelete); verbose ("Installing parsers: "); for (i = 0 ; i < builtInCount ; ++i) @@ -1523,17 +1730,13 @@ extern void initializeParsing (void) else accepted = true; if (accepted) - { - verbose ("%s%s", i > 0 ? ", " : "", def->name); - def->id = LanguageCount++; - LanguageTable [def->id] = def; - } + initializeParsingCommon (def, true); } } verbose ("\n"); for (i = 0; i < builtInCount ; ++i) - linkDependenciesAtInitializeParsing (LanguageTable [i]); + linkDependenciesAtInitializeParsing (LanguageTable [i].def); } extern void freeParserResources (void) @@ -1541,26 +1744,27 @@ extern void freeParserResources (void) unsigned int i; for (i = 0 ; i < LanguageCount ; ++i) { - parserDefinition* const lang = LanguageTable [i]; + parserObject* const parser = LanguageTable + i; - if (lang->finalize) - (lang->finalize)((langType)i, (bool)lang->initialized); + if (parser->def->finalize) + (parser->def->finalize)((langType)i, (bool)parser->initialized); - finalizeSubparsers (lang); + freeLregexControlBlock (parser->lregexControlBlock); + freeKindControlBlock (parser->kindControlBlock); + parser->kindControlBlock = NULL; - if (lang->fileKind != &defaultFileKind) - { - eFree (lang->fileKind); - lang->fileKind = NULL; - } + finalizeDependencies (parser->def, parser->slaveControlBlock); + freeSlaveControlBlock (parser->slaveControlBlock); + parser->slaveControlBlock = NULL; - freeList (&lang->currentPatterns); - freeList (&lang->currentExtensions); - freeList (&lang->currentAliases); + freeList (&parser->currentPatterns); + freeList (&parser->currentExtensions); + freeList (&parser->currentAliases); - eFree (lang->name); - lang->name = NULL; - eFree (lang); + eFree (parser->def->name); + parser->def->name = NULL; + eFree (parser->def); + parser->def = NULL; } if (LanguageTable != NULL) eFree (LanguageTable); @@ -1572,58 +1776,170 @@ static void doNothing (void) { } +static void optlibRunBaseParser (void) +{ + scheduleRunningBaseparser (0); +} + +static bool optlibIsDedicatedSubparser (parserDefinition* def) +{ + return (def->dependencies + && (def->dependencies->type == DEPTYPE_SUBPARSER) + && ((subparser *)def->dependencies->data)->direction & SUBPARSER_SUB_RUNS_BASE); +} + static void lazyInitialize (langType language) { - parserDefinition* lang; + parserDefinition* def; Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; + def = LanguageTable [language].def; + + def->parser = doNothing; - lang->parser = doNothing; + if (def->method & METHOD_REGEX) + { + if (optlibIsDedicatedSubparser (def)) + def->parser = optlibRunBaseParser; + else + def->parser = findRegexTags; + } +} - if (lang->method & METHOD_REGEX) - lang->parser = findRegexTags; +extern void enableDefaultFileKind (bool state) +{ + defaultFileKind.enabled = state; } /* * Option parsing */ -static void lang_def_flag_file_kind_long (const char* const optflag, const char* const param, void* data) +struct preLangDefFlagData { - parserDefinition* def = data; - - Assert (def); - Assert (param); - Assert (optflag); + char *base; + subparserRunDirection direction; + bool autoFQTag; +}; +static void pre_lang_def_flag_base_long (const char* const optflag, const char* const param, void* data) +{ + struct preLangDefFlagData * flag_data = data; + langType base; if (param[0] == '\0') - error (WARNING, "No letter specified for \"%s\" flag of --langdef option", optflag); - else if (param[1] != '\0') - error (WARNING, "Specify just a letter for \"%s\" flag of --langdef option", optflag); + { + error (WARNING, "No base parser specified for \"%s\" flag of --langdef option", optflag); + return; + } + + base = getNamedLanguage (param, 0); + if (base == LANG_IGNORE) + { + error (WARNING, "Unknown language(%s) is specified for \"%s\" flag of --langdef option", + param, optflag); + return; + + } + + flag_data->base = eStrdup(param); +} - if (def->fileKind != &defaultFileKind) - eFree (def->fileKind); +#define LANGDEF_FLAG_DEDICATED "dedicated" +#define LANGDEF_FLAG_SHARED "shared" +#define LANGDEF_FLAG_BIDIR "bidirectional" +static void pre_lang_def_flag_direction_long (const char* const optflag, const char* const param CTAGS_ATTR_UNUSED, void* data) +{ + struct preLangDefFlagData * flag_data = data; + + if (strcmp(optflag, LANGDEF_FLAG_DEDICATED) == 0) + flag_data->direction = SUBPARSER_SUB_RUNS_BASE; + else if (strcmp(optflag, LANGDEF_FLAG_SHARED) == 0) + flag_data->direction = SUBPARSER_BASE_RUNS_SUB; + else if (strcmp(optflag, LANGDEF_FLAG_BIDIR) == 0) + flag_data->direction = SUBPARSER_BI_DIRECTION; + else + AssertNotReached (); +} - def->fileKind = fileKindNew (param[0]); +static void pre_lang_def_flag_autoFQTag_long (const char* const optflag, + const char* const param CTAGS_ATTR_UNUSED, + void* data) +{ + struct preLangDefFlagData * flag_data = data; + flag_data->autoFQTag = true; } -static flagDefinition LangDefFlagDef [] = { - { '\0', "fileKind", NULL, lang_def_flag_file_kind_long }, +static flagDefinition PreLangDefFlagDef [] = { + { '\0', "base", NULL, pre_lang_def_flag_base_long, + "BASEPARSER", "utilize as a base parser"}, + { '\0', LANGDEF_FLAG_DEDICATED, NULL, + pre_lang_def_flag_direction_long, + NULL, "make the base parser dedicated to this subparser"}, + { '\0', LANGDEF_FLAG_SHARED, NULL, + pre_lang_def_flag_direction_long, + NULL, "share the base parser with the other subparsers" + }, + { '\0', LANGDEF_FLAG_BIDIR, NULL, + pre_lang_def_flag_direction_long, + NULL, "utilize the base parser both 'dedicated' and 'shared' way" + }, + { '\0', "_autoFQTag", NULL, pre_lang_def_flag_autoFQTag_long, + NULL, "make full qualified tags automatically based on scope information"}, }; -extern void processLanguageDefineOption ( - const char *const option, const char *const parameter CTAGS_ATTR_UNUSED) +static void optlibFreeDep (langType lang, bool initialized CTAGS_ATTR_UNUSED) { - if (parameter [0] == '\0') - error (WARNING, "No language specified for \"%s\" option", option); - else if (getNamedLanguage (parameter, 0) != LANG_IGNORE) - error (WARNING, "Language \"%s\" already defined", parameter); - else + parserDefinition * pdef = LanguageTable [lang].def; + + if (pdef->dependencyCount == 1) { - char *name; - char *flags; - unsigned int i; + parserDependency *dep = pdef->dependencies; + + eFree ((char *)dep->upperParser); /* Dirty cast */ + dep->upperParser = NULL; + eFree (dep->data); + dep->data = NULL; + eFree (dep); + pdef->dependencies = NULL; + } +} + +static parserDefinition* OptlibParser(const char *name, const char *base, + subparserRunDirection direction) +{ + parserDefinition *def; + + def = parserNew (name); + def->initialize = lazyInitialize; + def->method = METHOD_NOT_CRAFTED; + if (base) + { + subparser *sub = xCalloc (1, subparser); + parserDependency *dep = xCalloc (1, parserDependency); + + sub->direction = direction; + dep->type = DEPTYPE_SUBPARSER; + dep->upperParser = eStrdup (base); + dep->data = sub; + def->dependencies = dep; + def->dependencyCount = 1; + def->finalize = optlibFreeDep; + } + + return def; +} + +extern void processLanguageDefineOption ( + const char *const option, const char *const parameter) +{ + if (parameter [0] == '\0') + error (WARNING, "No language specified for \"%s\" option", option); + else if (getNamedLanguage (parameter, 0) != LANG_IGNORE) + error (WARNING, "Language \"%s\" already defined", parameter); + else + { + char *name; + char *flags; parserDefinition* def; flags = strchr (parameter, LONG_FLAGS_OPEN); @@ -1632,97 +1948,98 @@ extern void processLanguageDefineOption ( else name = eStrdup (parameter); - i = LanguageCount++; - def = parserNew (name); - def->initialize = lazyInitialize; - def->currentPatterns = stringListNew (); - def->currentExtensions = stringListNew (); - def->method = METHOD_NOT_CRAFTED; - def->id = i; - LanguageTable = xRealloc (LanguageTable, i + 1, parserDefinition*); - LanguageTable [i] = def; + LanguageTable = xRealloc (LanguageTable, LanguageCount + 1, parserObject); + memset (LanguageTable + LanguageCount, 0, sizeof(parserObject)); + + struct preLangDefFlagData data = { + .base = NULL, + .direction = SUBPARSER_UNKNOWN_DIRECTION, + .autoFQTag = false, + }; + flagsEval (flags, PreLangDefFlagDef, ARRAY_SIZE (PreLangDefFlagDef), &data); + + if (data.base == NULL && data.direction != SUBPARSER_UNKNOWN_DIRECTION) + error (WARNING, "Ignore the direction of subparser because \"{base=}\" is not given"); - flagsEval (flags, LangDefFlagDef, ARRAY_SIZE (LangDefFlagDef), def); + if (data.base && data.direction == SUBPARSER_UNKNOWN_DIRECTION) + data.direction = SUBPARSER_BASE_RUNS_SUB; + + def = OptlibParser (name, data.base, data.direction); + if (data.base) + eFree (data.base); + + def->requestAutomaticFQTag = data.autoFQTag; + + initializeParsingCommon (def, false); + linkDependenciesAtInitializeParsing (def); + + LanguageTable [def->id].currentPatterns = stringListNew (); + LanguageTable [def->id].currentExtensions = stringListNew (); eFree (name); } } -static kindDefinition *langKindDefinition (const langType language, const int flag) +extern bool isLanguageKindEnabled (const langType language, int kindIndex) { - unsigned int i; - kindDefinition* result = NULL; - const parserDefinition* lang; - Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - for (i=0 ; i < lang->kindCount && result == NULL ; ++i) - if (lang->kindTable [i].letter == flag) - result = &lang->kindTable [i]; - return result; + kindDefinition * kdef = getLanguageKind (language, kindIndex); + return kdef->enabled; } -static kindDefinition *langKindLongOption (const langType language, const char *kindLong) +extern bool isLanguageRoleEnabled (const langType language, int kindIndex, int roleIndex) { - unsigned int i; - kindDefinition* result = NULL; - const parserDefinition* lang; - Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - for (i=0 ; i < lang->kindCount && result == NULL ; ++i) - if (strcmp (lang->kindTable [i].name, kindLong) == 0) - result = &lang->kindTable [i]; - return result; + return isRoleEnabled(LanguageTable [language].kindControlBlock, + kindIndex, roleIndex); } -extern bool isLanguageKindEnabled (const langType language, int kindIndex) +extern bool isLanguageKindRefOnly (const langType language, int kindIndex) { - kindDefinition * kdef = getLanguageKind (language, kindIndex); - return kdef->enabled; + kindDefinition * def = getLanguageKind(language, kindIndex); + return def->referenceOnly; } - static void resetLanguageKinds (const langType language, const bool mode) { - const parserDefinition* lang; + const parserObject* parser; + Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; + parser = LanguageTable + language; - resetRegexKinds (language, mode); - resetXcmdKinds (language, mode); { unsigned int i; - for (i = 0 ; i < lang->kindCount ; ++i) - enableKind (lang->kindTable + i, mode); + struct kindControlBlock *kcb = parser->kindControlBlock; + + for (i = 0 ; i < countKinds (kcb) ; ++i) + { + kindDefinition *kdef = getKind (kcb, i); + enableKind (kdef, mode); + } } } -static bool enableLanguageKind ( +static bool enableLanguageKindForLetter ( const langType language, const int kind, const bool mode) { bool result = false; - kindDefinition* const opt = langKindDefinition (language, kind); - if (opt != NULL) + kindDefinition* const def = getLanguageKindForLetter (language, kind); + if (def != NULL) { - enableKind (opt, mode); + enableKind (def, mode); result = true; } - result = enableRegexKind (language, kind, mode)? true: result; - result = enableXcmdKind (language, kind, mode)? true: result; return result; } -static bool enableLanguageKindLong ( - const langType language, const char * const kindLong, const bool mode) +static bool enableLanguageKindForName ( + const langType language, const char * const name, const bool mode) { bool result = false; - kindDefinition* const opt = langKindLongOption (language, kindLong); - if (opt != NULL) + kindDefinition* const def = getLanguageKindForName (language, name); + if (def != NULL) { - enableKind (opt, mode); + enableKind (def, mode); result = true; } - result = enableRegexKindLong (language, kindLong, mode)? true: result; - result = enableXcmdKindLong (language, kindLong, mode)? true: result; return result; } @@ -1749,7 +2066,7 @@ static void processLangKindDefinition ( else if (*p != '+' && *p != '-') resetLanguageKinds (language, false); - longName = vStringNewOrClear (longName); + longName = vStringNewOrClearWithAutoRelease (longName); while ((c = *p++) != '\0') { @@ -1780,7 +2097,7 @@ static void processLangKindDefinition ( "unexpected character in kind specification: \'%c\'", c); k = vStringValue (longName); - r = enableLanguageKindLong (language, k, mode); + r = enableLanguageKindForName (language, k, mode); if (! r) error (WARNING, "Unsupported kind: '%s' for --%s option", k, option); @@ -1793,7 +2110,7 @@ static void processLangKindDefinition ( vStringPut (longName, c); else { - r = enableLanguageKind (language, c, mode); + r = enableLanguageKindForLetter (language, c, mode); if (! r) error (WARNING, "Unsupported kind: '%c' for --%s option", c, option); @@ -1803,6 +2120,269 @@ static void processLangKindDefinition ( } } +static void freeKdef (kindDefinition *kdef) +{ + eFree (kdef->name); + eFree (kdef->description); + eFree (kdef); +} + +static char *extractDescriptionAndFlags(const char *input, const char **flags) +{ + vString *vdesc = vStringNew(); + bool escaped = false; + + if (flags) + *flags = NULL; + + while (*input != '\0') + { + if (escaped) + { + vStringPut (vdesc, *input); + escaped = false; + + } + else if (*input == '\\') + escaped = true; + else if (*input == LONG_FLAGS_OPEN) + { + if (flags) + *flags = input; + break; + } + else + vStringPut (vdesc, *input); + input++; + } + return vStringDeleteUnwrap(vdesc); +} + +static void pre_kind_def_flag_refonly_long (const char* const optflag, + const char* const param, void* data) +{ + kindDefinition *kdef = data; + kdef->referenceOnly = true; +} + +static flagDefinition PreKindDefFlagDef [] = { + { '\0', "_refonly", NULL, pre_kind_def_flag_refonly_long, + NULL, "use this kind reference tags only"}, +}; + +static bool processLangDefineKind(const langType language, + const char *const option, + const char *const parameter) +{ + parserObject *parser; + + kindDefinition *kdef; + int letter; + const char * p = parameter; + char *name; + char *description; + const char *tmp_start; + const char *tmp_end; + size_t tmp_len; + const char *flags; + + + Assert (0 <= language && language < (int) LanguageCount); + parser = LanguageTable + language; + + Assert (p); + + if (p[0] == '\0') + error (FATAL, "no kind definition specified in \"--%s\" option", option); + + letter = p[0]; + if (letter == ',') + error (FATAL, "no kind letter specified in \"--%s\" option", option); + if (!isalnum (letter)) + error (FATAL, "the kind letter give in \"--%s\" option is not an alphabet or a number", option); + else if (letter == KIND_FILE_DEFAULT) + error (FATAL, "the kind letter `F' in \"--%s\" option is reserved for \"file\" kind", option); + else if (getKindForLetter (parser->kindControlBlock, letter)) + { + error (WARNING, "the kind for letter `%c' specified in \"--%s\" option is already defined.", + letter, option); + return true; + } + + if (p[1] != ',') + error (FATAL, "wrong kind definition in \"--%s\" option: no comma after letter", option); + + p += 2; + if (p[0] == '\0') + error (FATAL, "no kind name specified in \"--%s\" option", option); + tmp_end = strchr (p, ','); + if (!tmp_end) + error (FATAL, "no kind description specified in \"--%s\" option", option); + + tmp_start = p; + while (p != tmp_end) + { + if (!isalnum (*p)) + error (FATAL, "unacceptable char as part of kind name in \"--%s\" option", option); + p++; + } + + if (tmp_end == tmp_start) + error (FATAL, "the kind name in \"--%s\" option is empty", option); + + tmp_len = tmp_end - tmp_start; + if (strncmp (tmp_start, KIND_FILE_DEFAULT_LONG, tmp_len) == 0) + error (FATAL, + "the kind name " KIND_FILE_DEFAULT_LONG " in \"--%s\" option is reserved", + option); + + name = eStrndup (tmp_start, tmp_len); + if (getKindForName (parser->kindControlBlock, name)) + { + error (WARNING, "the kind for name `%s' specified in \"--%s\" option is already defined.", + name, option); + eFree (name); + return true; + } + + p++; + if (p [0] == '\0' || p [0] == LONG_FLAGS_OPEN) + error (FATAL, "found an empty kind description in \"--%s\" option", option); + + description = extractDescriptionAndFlags (p, &flags); + + kdef = xCalloc (1, kindDefinition); + kdef->enabled = true; + kdef->letter = letter; + kdef->name = name; + kdef->description = description; + if (flags) + flagsEval (flags, PreKindDefFlagDef, ARRAY_SIZE (PreKindDefFlagDef), kdef); + + defineKind (parser->kindControlBlock, kdef, freeKdef); + return true; +} + +static void freeRdef (roleDefinition *rdef) +{ + eFree (rdef->name); + eFree (rdef->description); + eFree (rdef); +} + +static bool processLangDefineRole(const langType language, + const char *const option, + const char *const parameter) +{ + parserObject *parser; + + kindDefinition *kdef; + roleDefinition *rdef; + int kletter; + const char * p = parameter; + char *name; + char *description; + const char *tmp_start; + const char *tmp_end; + const char *flags; + + Assert (0 <= language && language < (int) LanguageCount); + parser = LanguageTable + language; + + Assert (p); + + if (p[0] == '\0') + error (FATAL, "no role definition specified in \"--%s\" option", option); + + kletter = p[0]; + if (kletter == '.') + error (FATAL, "no kind letter specified in \"--%s\" option", option); + if (!isalnum (kletter)) + error (FATAL, "the kind letter give in \"--%s\" option is not an alphabet or a number", option); + else if (kletter == KIND_FILE_DEFAULT) + error (FATAL, "the kind letter `F' in \"--%s\" option is reserved for \"file\" kind and no role can be attached to", option); + + kdef = getKindForLetter (parser->kindControlBlock, kletter); + + if (kdef == NULL) + { + error (WARNING, "the kind for letter `%c' specified in \"--%s\" option is not defined.", + kletter, option); + return true; + } + + if (p[1] != '.') + error (FATAL, "wrong role definition in \"--%s\" option: no period after kind letter `%c'", + option, kletter); + + p += 2; + if (p[0] == '\0') + error (FATAL, "no role name specified in \"--%s\" option", option); + tmp_end = strchr (p, ','); + if (!tmp_end) + error (FATAL, "no role description specified in \"--%s\" option", option); + + tmp_start = p; + while (p != tmp_end) + { + if (!isalnum (*p)) + error (FATAL, "unacceptable char as part of role name in \"--%s\" option", option); + p++; + } + + if (tmp_end == tmp_start) + error (FATAL, "the role name in \"--%s\" option is empty", option); + + name = eStrndup (tmp_start, tmp_end - tmp_start); + if (getRoleForName (parser->kindControlBlock, kdef->id, name)) + { + error (WARNING, "the role for name `%s' specified in \"--%s\" option is already defined.", + name, option); + eFree (name); + return true; + } + + p++; + if (p [0] == '\0' || p [0] == LONG_FLAGS_OPEN) + error (FATAL, "found an empty role description in \"--%s\" option", option); + + description = extractDescriptionAndFlags (p, &flags); + + rdef = xCalloc (1, roleDefinition); + rdef->enabled = true; + rdef->name = name; + rdef->description = description; + + if (flags) + flagsEval (flags, NULL, 0, rdef); + + defineRole (parser->kindControlBlock, kdef->id, rdef, freeRdef); + + return true; +} + +extern bool processKinddefOption (const char *const option, const char * const parameter) +{ + langType language; + + language = getLanguageComponentInOption (option, "kinddef-"); + if (language == LANG_IGNORE) + return false; + + return processLangDefineKind (language, option, parameter); +} + +extern bool processRoledefOption (const char *const option, const char * const parameter) +{ + langType language; + + language = getLanguageComponentInOption (option, "_roledef-"); + if (language == LANG_IGNORE) + return false; + + return processLangDefineRole (language, option, parameter); +} + struct langKindDefinitionStruct { const char *const option; const char *const parameter; @@ -1814,7 +2394,7 @@ static void processLangKindDefinitionEach( processLangKindDefinition (lang, arg->option, arg->parameter); } -extern bool processKindDefinition ( +extern bool processKindsOption ( const char *const option, const char *const parameter) { #define PREFIX "kinds-" @@ -1833,193 +2413,202 @@ extern bool processKindDefinition ( { size_t len = dash - option; - if ((len == 1) && (*option == '*')) + if ((len == 3) && (strncmp (option, RSV_LANG_ALL, len) == 0)) foreachLanguage(processLangKindDefinitionEach, &arg); else { - vString* langName = vStringNew (); - vStringNCopyS (langName, option, len); - language = getNamedLanguage (vStringValue (langName), 0); + language = getNamedLanguage (option, len); if (language == LANG_IGNORE) - error (WARNING, "Unknown language \"%s\" in \"%s\" option", vStringValue (langName), option); + { + char *langName = eStrndup (option, len); + error (WARNING, "Unknown language \"%s\" in \"%s\" option", langName, option); + eFree (langName); + } else processLangKindDefinition (language, option, parameter); - vStringDelete (langName); } handled = true; } else if ( strncmp (option, PREFIX, PREFIX_LEN) == 0 ) { const char* lang; - size_t len; lang = option + PREFIX_LEN; - len = strlen (lang); - if (len == 0) + if (lang[0] == '\0') error (WARNING, "No language given in \"%s\" option", option); - else if (len == 1 && lang[0] == '*') - { + else if (strcmp (lang, RSV_LANG_ALL) == 0) foreachLanguage(processLangKindDefinitionEach, &arg); - handled = true; - } else { language = getNamedLanguage (lang, 0); if (language == LANG_IGNORE) error (WARNING, "Unknown language \"%s\" in \"%s\" option", lang, option); else - { processLangKindDefinition (language, option, parameter); - handled = true; - } } - + handled = true; } return handled; #undef PREFIX #undef PREFIX_LEN } -static void printRoles (const langType language, const char* letters, bool allowMissingKind) +extern void printLanguageRoles (const langType language, const char* kindspecs, + bool withListHeader, bool machinable, FILE *fp) { - const parserDefinition* const lang = LanguageTable [language]; - const char *c; + struct colprintTable *table = roleColprintTableNew(); + parserObject *parser; - if (lang->invisible) - return; + initializeParser (language); - for (c = letters; *c != '\0'; c++) + if (language == LANG_AUTO) { - unsigned int i; - const kindDefinition *k; - - for (i = 0; i < lang->kindCount; ++i) + for (unsigned int i = 0 ; i < LanguageCount ; ++i) { - k = lang->kindTable + i; - if (*c == KIND_WILDCARD || k->letter == *c) - { - int j; - const roleDefinition *r; + if (!isLanguageVisible (i)) + continue; - for (j = 0; j < k->nRoles; j++) - { - r = k->roles + j; - printf ("%s\t%c\t", lang->name, k->letter); - printRole (r); - } - if (*c != KIND_WILDCARD) - break; - } + parser = LanguageTable + i; + roleColprintAddRoles (table, parser->kindControlBlock, kindspecs); } - if ((i == lang->kindCount) && (*c != KIND_WILDCARD) && (!allowMissingKind)) - error (FATAL, "No such letter kind in %s: %c\n", lang->name, *c); } + else + { + parser = LanguageTable + language; + roleColprintAddRoles (table, parser->kindControlBlock, kindspecs); + } + + roleColprintTablePrint (table, (language != LANG_AUTO), + withListHeader, machinable, fp); + colprintTableDelete (table); } -extern void printLanguageRoles (const langType language, const char* letters) +static void printKinds (langType language, bool indent, + struct colprintTable * table) { - if (language == LANG_AUTO) + const parserObject *parser; + struct kindControlBlock *kcb; + Assert (0 <= language && language < (int) LanguageCount); + + initializeParser (language); + parser = LanguageTable + language; + kcb = parser->kindControlBlock; + + if (table) + kindColprintAddLanguageLines (table, kcb); + else { - unsigned int i; - for (i = 0 ; i < LanguageCount ; ++i) - printRoles (i, letters, true); + for (unsigned int i = 0 ; i < countKinds(kcb) ; ++i) + printKind (getKind(kcb, i), indent); } - else - printRoles (language, letters, false); - } -extern void printLanguageFileKind (const langType language) +extern void printLanguageKinds (const langType language, bool allKindFields, + bool withListHeader, bool machinable, FILE *fp) { + struct colprintTable * table = NULL; + + if (allKindFields) + table = kindColprintTableNew (); + if (language == LANG_AUTO) { - unsigned int i; - for (i = 0 ; i < LanguageCount ; ++i) + for (unsigned int i = 0 ; i < LanguageCount ; ++i) { - const parserDefinition* const lang = LanguageTable [i]; - printf ("%s %c\n", lang->name, lang->fileKind->letter); + const parserDefinition* const lang = LanguageTable [i].def; + + if (lang->invisible) + continue; + + if (!table) + printf ("%s%s\n", lang->name, isLanguageEnabled (i) ? "" : " [disabled]"); + printKinds (i, true, table); } } else - printf ("%c\n", LanguageTable [language]->fileKind->letter); + printKinds (language, false, table); + + if (allKindFields) + { + kindColprintTablePrint(table, (language == LANG_AUTO)? 0: 1, + withListHeader, machinable, fp); + colprintTableDelete (table); + } } -static void printKinds (langType language, bool allKindFields, bool indent) +static void printParameters (struct colprintTable *table, langType language) { const parserDefinition* lang; Assert (0 <= language && language < (int) LanguageCount); initializeParser (language); - lang = LanguageTable [language]; - if (lang->kindTable != NULL) + lang = LanguageTable [language].def; + if (lang->parameterHandlerTable != NULL) { - unsigned int i; - for (i = 0 ; i < lang->kindCount ; ++i) - { - if (allKindFields && indent) - printf (Option.machinable? "%s": PR_KIND_FMT (LANG,s), lang->name); - printKind (lang->kindTable + i, allKindFields, indent, Option.machinable); - } + for (unsigned int i = 0; i < lang->parameterHandlerCount; ++i) + paramColprintAddParameter(table, language, lang->parameterHandlerTable + i); } - printRegexKinds (language, allKindFields, indent, Option.machinable); - printXcmdKinds (language, allKindFields, indent, Option.machinable); + } -extern void printLanguageKinds (const langType language, bool allKindFields) +extern void printLanguageParameters (const langType language, + bool withListHeader, bool machinable, FILE *fp) { + struct colprintTable *table = paramColprintTableNew(); + if (language == LANG_AUTO) { - unsigned int i; - - if (Option.withListHeader && allKindFields) - printKindListHeader (true, Option.machinable); - - for (i = 0 ; i < LanguageCount ; ++i) + for (unsigned int i = 0; i < LanguageCount ; ++i) { - const parserDefinition* const lang = LanguageTable [i]; + const parserDefinition* const lang = LanguageTable [i].def; if (lang->invisible) continue; - if (!allKindFields) - printf ("%s%s\n", lang->name, isLanguageEnabled (i) ? "" : " [disabled]"); - printKinds (i, allKindFields, true); + printParameters (table, i); } } else - { - if (Option.withListHeader && allKindFields) - printKindListHeader (false, Option.machinable); + printParameters (table, language); - printKinds (language, allKindFields, false); - } + paramColprintTablePrint (table, (language != LANG_AUTO), + withListHeader, machinable, fp); + colprintTableDelete (table); } static void processLangAliasOption (const langType language, const char *const parameter) { const char* alias; - const parserDefinition * lang; + const parserObject * parser; Assert (0 <= language && language < (int) LanguageCount); - Assert (parameter); - Assert (parameter[0]); - lang = LanguageTable [language]; + parser = LanguageTable + language; - if (parameter[0] == '+') + if (parameter[0] == '\0') + { + clearLanguageAliases (language); + verbose ("clear aliases for %s\n", parser->def->name); + } + else if (strcmp (parameter, RSV_LANGMAP_DEFAULT) == 0) + { + installLanguageAliasesDefault (language); + verbose ("reset aliases for %s\n", parser->def->name); + } + else if (parameter[0] == '+') { alias = parameter + 1; addLanguageAlias(language, alias); - verbose ("add alias %s to %s\n", alias, lang->name); + verbose ("add an alias %s to %s\n", alias, parser->def->name); } else if (parameter[0] == '-') { - if (lang->currentAliases) + if (parser->currentAliases) { alias = parameter + 1; - if (stringListDeleteItemExtension (lang->currentAliases, alias)) + if (stringListDeleteItemExtension (parser->currentAliases, alias)) { - verbose ("remove alias %s from %s\n", alias, lang->name); + verbose ("remove an alias %s from %s\n", alias, parser->def->name); } } } @@ -2028,7 +2617,7 @@ static void processLangAliasOption (const langType language, alias = parameter; clearLanguageAliases (language); addLanguageAlias(language, alias); - verbose ("set alias %s to %s\n", alias, lang->name); + verbose ("set alias %s to %s\n", alias, parser->def->name); } } @@ -2038,9 +2627,38 @@ extern bool processAliasOption ( { langType language; + Assert (parameter); + +#define PREFIX "alias-" + if (strcmp (option, "alias-" RSV_LANG_ALL) == 0) + { + if ((parameter[0] == '\0') + || (strcmp (parameter, RSV_LANGMAP_DEFAULT) == 0)) + { + for (unsigned int i = 0; i < LanguageCount; i++) + { + clearLanguageAliases (i); + verbose ("clear aliases for %s\n", getLanguageName(i)); + } + + if (parameter[0] != '\0') + { + verbose (" Installing default language aliases:\n"); + installLanguageAliasesDefaults (); + } + } + else + { + error (WARNING, "Use \"%s\" option for reset (\"default\") or clearing (\"\")", option); + return false; + } + return true; + } + language = getLanguageComponentInOption (option, "alias-"); if (language == LANG_IGNORE) return false; +#undef PREFIX processLangAliasOption (language, parameter); return true; @@ -2048,67 +2666,183 @@ extern bool processAliasOption ( static void printMaps (const langType language, langmapType type) { - const parserDefinition* lang; + const parserObject* parser; unsigned int i; - Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - printf ("%-8s", lang->name); - if (lang->currentExtensions != NULL && (type & LMAP_EXTENSION)) - for (i = 0 ; i < stringListCount (lang->currentExtensions) ; ++i) - printf (" *.%s", vStringValue ( - stringListItem (lang->currentExtensions, i))); - if (lang->currentPatterns != NULL && (type & LMAP_PATTERN)) - for (i = 0 ; i < stringListCount (lang->currentPatterns) ; ++i) + + parser = LanguageTable + language; + printf ("%-8s", parser->def->name); + if (parser->currentPatterns != NULL && (type & LMAP_PATTERN)) + for (i = 0 ; i < stringListCount (parser->currentPatterns) ; ++i) printf (" %s", vStringValue ( - stringListItem (lang->currentPatterns, i))); + stringListItem (parser->currentPatterns, i))); + if (parser->currentExtensions != NULL && (type & LMAP_EXTENSION)) + for (i = 0 ; i < stringListCount (parser->currentExtensions) ; ++i) + printf (" *.%s", vStringValue ( + stringListItem (parser->currentExtensions, i))); putchar ('\n'); } -static void printAliases (const langType language, FILE *fp) -{ - const parserDefinition* lang; - unsigned int i; - Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - - if (lang->currentAliases != NULL) - for (i = 0 ; i < stringListCount (lang->currentAliases) ; ++i) - fprintf (fp, " %s", vStringValue ( - stringListItem (lang->currentAliases, i))); -} - -extern void printLanguageMaps (const langType language, langmapType type) +static struct colprintTable *mapColprintTableNew (langmapType type) { - if (language == LANG_AUTO) + if ((type & LMAP_ALL) == LMAP_ALL) + return colprintTableNew ("L:LANGUAGE", "L:TYPE", "L:MAP", NULL); + else if (type & LMAP_PATTERN) + return colprintTableNew ("L:LANGUAGE", "L:PATTERN", NULL); + else if (type & LMAP_EXTENSION) + return colprintTableNew ("L:LANGUAGE", "L:EXTENSION", NULL); + else { - unsigned int i; - for (i = 0 ; i < LanguageCount ; ++i) - printMaps (i, type); + AssertNotReached (); + return NULL; } - else - printMaps (language, type); } -extern void printLanguageAliases (const langType language) +static void mapColprintAddLanguage (struct colprintTable * table, + langmapType type, + const parserObject* parser) { - if (language == LANG_AUTO) - { - unsigned int i; - for (i = 0 ; i < LanguageCount ; ++i) - printLanguageAliases (i); - } - else + struct colprintLine * line; + unsigned int count; + unsigned int i; + + if ((type & LMAP_PATTERN) && (0 < (count = stringListCount (parser->currentPatterns)))) { - const parserDefinition* lang; + for (i = 0; i < count; i++) + { + line = colprintTableGetNewLine (table); + vString *pattern = stringListItem (parser->currentPatterns, i); + colprintLineAppendColumnCString (line, parser->def->name); + if (type & LMAP_EXTENSION) + colprintLineAppendColumnCString (line, "pattern"); + colprintLineAppendColumnVString (line, pattern); + } + } + + if ((type & LMAP_EXTENSION) && (0 < (count = stringListCount (parser->currentExtensions)))) + { + for (i = 0; i < count; i++) + { + line = colprintTableGetNewLine (table); + vString *extension = stringListItem (parser->currentExtensions, i); + + colprintLineAppendColumnCString (line, parser->def->name); + if (type & LMAP_PATTERN) + colprintLineAppendColumnCString (line, "extension"); + colprintLineAppendColumnVString (line, extension); + } + } +} + +extern void printLanguageMaps (const langType language, langmapType type, + bool withListHeader, bool machinable, FILE *fp) +{ + /* DON'T SORT THE LIST + + The order of listing should be equal to the order of matching + for the parser selection. */ + + struct colprintTable * table = NULL; + if (type & LMAP_TABLE_OUTPUT) + table = mapColprintTableNew(type); + + if (language == LANG_AUTO) + { + for (unsigned int i = 0 ; i < LanguageCount ; ++i) + { + if (!isLanguageVisible (i)) + continue; + + if (type & LMAP_TABLE_OUTPUT) + { + const parserObject* parser = LanguageTable + i; + + mapColprintAddLanguage (table, type, parser); + } + else + printMaps (i, type); + } + } + else + { Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - printf ("%-8s", lang->name); - printAliases (language, stdout); - putchar ('\n'); + + if (type & LMAP_TABLE_OUTPUT) + { + const parserObject* parser = LanguageTable + language; + + mapColprintAddLanguage (table, type, parser); + } + else + printMaps (language, type); + } + + + if (type & LMAP_TABLE_OUTPUT) + { + colprintTablePrint (table, (language == LANG_AUTO)? 0: 1, + withListHeader, machinable, fp); + colprintTableDelete (table); + } +} + +static struct colprintTable *aliasColprintTableNew (void) +{ + return colprintTableNew ("L:LANGUAGE", "L:ALIAS", NULL); +} + +static void aliasColprintAddLanguage (struct colprintTable * table, + const parserObject* parser) +{ + unsigned int count; + + if (parser->currentAliases && (0 < (count = stringListCount (parser->currentAliases)))) + { + for (unsigned int i = 0; i < count; i++) + { + struct colprintLine * line = colprintTableGetNewLine (table); + vString *alias = stringListItem (parser->currentAliases, i);; + + colprintLineAppendColumnCString (line, parser->def->name); + colprintLineAppendColumnVString (line, alias); + } } } +extern void printLanguageAliases (const langType language, + bool withListHeader, bool machinable, FILE *fp) +{ + /* DON'T SORT THE LIST + + The order of listing should be equal to the order of matching + for the parser selection. */ + + struct colprintTable * table = aliasColprintTableNew(); + const parserObject* parser; + + if (language == LANG_AUTO) + { + for (unsigned int i = 0; i < LanguageCount; ++i) + { + parser = LanguageTable + i; + if (parser->def->invisible) + continue; + + aliasColprintAddLanguage (table, parser); + } + } + else + { + Assert (0 <= language && language < (int) LanguageCount); + parser = LanguageTable + language; + aliasColprintAddLanguage (table, parser); + } + + colprintTablePrint (table, (language == LANG_AUTO)? 0: 1, + withListHeader, machinable, fp); + colprintTableDelete (table); +} + static void printLanguage (const langType language, parserDefinition** ltable) { const parserDefinition* lang; @@ -2118,10 +2852,7 @@ static void printLanguage (const langType language, parserDefinition** ltable) if (lang->invisible) return; - if (lang->method & METHOD_XCMD) - initializeParser (lang->id); - - if (lang->kindTable != NULL || (lang->method & METHOD_REGEX) || (lang->method & METHOD_XCMD)) + if (lang->kindTable != NULL || (lang->method & METHOD_REGEX)) printf ("%s%s\n", lang->name, isLanguageEnabled (lang->id) ? "" : " [disabled]"); } @@ -2131,7 +2862,8 @@ extern void printLanguageList (void) parserDefinition **ltable; ltable = xMalloc (LanguageCount, parserDefinition*); - memcpy (ltable, LanguageTable, sizeof (parserDefinition*) * LanguageCount); + for (i = 0 ; i < LanguageCount ; ++i) + ltable[i] = LanguageTable[i].def; qsort (ltable, LanguageCount, sizeof (parserDefinition*), compareParsersByName); for (i = 0 ; i < LanguageCount ; ++i) @@ -2140,6 +2872,146 @@ extern void printLanguageList (void) eFree (ltable); } +static void xtagDefinitionDestroy (xtagDefinition *xdef) +{ + eFree ((void *)xdef->name); + eFree ((void *)xdef->description); + eFree (xdef); +} + +static bool processLangDefineExtra (const langType language, + const char *const option, + const char *const parameter) +{ + xtagDefinition *xdef; + const char * p = parameter; + const char *name_end; + const char *desc; + const char *flags; + + Assert (0 <= language && language < (int) LanguageCount); + Assert (p); + + if (p[0] == '\0') + error (FATAL, "no extra definition specified in \"--%s\" option", option); + + name_end = strchr (p, ','); + if (!name_end) + error (FATAL, "no extra description specified in \"--%s\" option", option); + else if (name_end == p) + error (FATAL, "the extra name in \"--%s\" option is empty", option); + + for (; p < name_end; p++) + { + if (!isalnum (*p)) + error (FATAL, "unacceptable char as part of extra name in \"--%s\" option", + option); + } + + p++; + if (p [0] == '\0' || p [0] == LONG_FLAGS_OPEN) + error (FATAL, "extra description in \"--%s\" option is empty", option); + + desc = extractDescriptionAndFlags (p, &flags); + + xdef = xCalloc (1, xtagDefinition); + xdef->enabled = false; + xdef->letter = NUL_XTAG_LETTER; + xdef->name = eStrndup (parameter, name_end - parameter); + xdef->description = desc; + xdef->isEnabled = NULL; + DEFAULT_TRASH_BOX(xdef, xtagDefinitionDestroy); + + if (flags) + flagsEval (flags, NULL, 0, xdef); + + defineXtag (xdef, language); + + return true; +} + +extern bool processExtradefOption (const char *const option, const char *const parameter) +{ + langType language; + + language = getLanguageComponentInOption (option, "_" "extradef-"); + if (language == LANG_IGNORE) + return false; + + return processLangDefineExtra (language, option, parameter); +} + +static void fieldDefinitionDestroy (fieldDefinition *fdef) +{ + eFree ((void *)fdef->description); + eFree ((void *)fdef->name); + eFree (fdef); +} + +static bool processLangDefineField (const langType language, + const char *const option, + const char *const parameter) +{ + fieldDefinition *fdef; + const char * p = parameter; + const char *name_end; + const char *desc; + const char *flags; + + Assert (0 <= language && language < (int) LanguageCount); + Assert (p); + + if (p[0] == '\0') + error (FATAL, "no field definition specified in \"--%s\" option", option); + + name_end = strchr (p, ','); + if (!name_end) + error (FATAL, "no field description specified in \"--%s\" option", option); + else if (name_end == p) + error (FATAL, "the field name in \"--%s\" option is empty", option); + + for (; p < name_end; p++) + { + if (!isalpha (*p)) + error (FATAL, "unacceptable char as part of field name in \"--%s\" option", + option); + } + + p++; + if (p [0] == '\0' || p [0] == LONG_FLAGS_OPEN) + error (FATAL, "field description in \"--%s\" option is empty", option); + + desc = extractDescriptionAndFlags (p, &flags); + + fdef = xCalloc (1, fieldDefinition); + fdef->enabled = false; + fdef->letter = NUL_FIELD_LETTER; + fdef->name = eStrndup(parameter, name_end - parameter); + fdef->description = desc; + fdef->isValueAvailable = NULL; + fdef->dataType = FIELDTYPE_STRING; /* TODO */ + fdef->ftype = FIELD_UNKNOWN; + DEFAULT_TRASH_BOX(fdef, fieldDefinitionDestroy); + + if (flags) + flagsEval (flags, NULL, 0, fdef); + + defineField (fdef, language); + + return true; +} + +extern bool processFielddefOption (const char *const option, const char *const parameter) +{ + langType language; + + language = getLanguageComponentInOption (option, "_fielddef-"); + if (language == LANG_IGNORE) + return false; + + return processLangDefineField (language, option, parameter); +} + /* * File parsing */ @@ -2147,23 +3019,97 @@ extern void printLanguageList (void) static rescanReason createTagsForFile (const langType language, const unsigned int passCount) { - parserDefinition *const lang = LanguageTable [language]; + parserDefinition *const lang = LanguageTable [language].def; rescanReason rescan = RESCAN_NONE; resetInputFile (language); Assert (lang->parser || lang->parser2); + notifyLanguageRegexInputStart (language); + notifyInputStart (); + if (lang->parser != NULL) lang->parser (); else if (lang->parser2 != NULL) rescan = lang->parser2 (passCount); + notifyInputEnd (); + notifyLanguageRegexInputEnd (language); + return rescan; } +extern void notifyLanguageRegexInputStart (langType language) +{ + notifyRegexInputStart((LanguageTable + language)->lregexControlBlock); +} + +extern void notifyLanguageRegexInputEnd (langType language) +{ + notifyRegexInputEnd((LanguageTable + language)->lregexControlBlock); +} + +static bool doesParserUseCork (parserDefinition *parser) +{ + subparser *tmp; + bool r = false; + + if (parser->useCork) + return true; + + if (hasLanguageScopeActionInRegex (parser->id) + || parser->requestAutomaticFQTag) + { + parser->useCork = true; + return true; + } + + pushLanguage (parser->id); + foreachSubparser(tmp, true) + { + langType t = getSubparserLanguage (tmp); + if (doesParserUseCork (LanguageTable[t].def)) + { + r = true; + break; + } + } + popLanguage (); + return r; +} + +static void setupLanguageSubparsersInUse (const langType language) +{ + subparser *tmp; + + setupSubparsersInUse ((LanguageTable + language)->slaveControlBlock); + foreachSubparser(tmp, true) + { + langType t = getSubparserLanguage (tmp); + enterSubparser (tmp); + setupLanguageSubparsersInUse(t); + leaveSubparser (); + } +} + +static subparser* teardownLanguageSubparsersInUse (const langType language) +{ + subparser *tmp; + + foreachSubparser(tmp, true) + { + langType t = getSubparserLanguage (tmp); + enterSubparser (tmp); + teardownLanguageSubparsersInUse(t); + leaveSubparser (); + } + return teardownSubparsersInUse ((LanguageTable + language)->slaveControlBlock); +} + #ifndef CTAGS_LIB -static bool createTagsWithFallback1 (const langType language) +static bool createTagsWithFallback1 (const langType language, + langType *exclusive_subparser) { bool tagFileResized = false; unsigned long numTags = numTagsAdded (); @@ -2171,21 +3117,28 @@ static bool createTagsWithFallback1 (const langType language) int lastPromise = getLastPromise (); unsigned int passCount = 0; rescanReason whyRescan; + parserObject *parser; + bool useCork; initializeParser (language); - if (LanguageTable [language]->useCork) + parser = &(LanguageTable [language]); + + setupLanguageSubparsersInUse (language); + + useCork = doesParserUseCork(parser->def); + if (useCork) corkTagFile(); addParserPseudoTags (language); tagFilePosition (&tagfpos); - anonResetMaybe (LanguageTable [language]); + anonResetMaybe (parser); while ( ( whyRescan = createTagsForFile (language, ++passCount) ) != RESCAN_NONE) { - if (LanguageTable [language]->useCork) + if (useCork) { uncorkTagFile(); corkTagFile(); @@ -2209,9 +3162,20 @@ static bool createTagsWithFallback1 (const langType language) } } - if (LanguageTable [language]->useCork) + /* Force filling allLines buffer and kick the multiline regex parser */ + if (hasLanguageMultilineRegexPatterns (language)) + while (readLineFromInputFile () != NULL) + ; /* Do nothing */ + + if (useCork) uncorkTagFile(); + { + subparser *s = teardownLanguageSubparsersInUse (language); + if (exclusive_subparser && s) + *exclusive_subparser = getSubparserLanguage (s); + } + return tagFileResized; } @@ -2223,18 +3187,26 @@ static bool createTagsWithFallback1 (const langType language, int lastPromise = getLastPromise (); unsigned int passCount = 0; rescanReason whyRescan; + parserObject *parser; + bool useCork; + + initializeParser (language); + parser = &(LanguageTable [language]); + + setupLanguageSubparsersInUse (language); - if (LanguageTable [language]->useCork) + useCork = doesParserUseCork(parser->def); + if (useCork) corkTagFile(); - anonResetMaybe (LanguageTable [language]); + anonResetMaybe (parser); passCallback(userData); while ( ( whyRescan = createTagsForFile (language, ++passCount) ) != RESCAN_NONE) { - if (LanguageTable [language]->useCork) + if (useCork) { uncorkTagFile(); corkTagFile(); @@ -2251,32 +3223,56 @@ static bool createTagsWithFallback1 (const langType language, break; } - if (LanguageTable [language]->useCork) + /* Force filling allLines buffer and kick the multiline regex parser */ + if (hasLanguageMultilineRegexPatterns (language)) + while (readLineFromInputFile () != NULL) + ; /* Do nothing */ + + if (useCork) uncorkTagFile(); + teardownLanguageSubparsersInUse (language); + return false; } #endif extern bool runParserInNarrowedInputStream (const langType language, - unsigned long startLine, int startCharOffset, - unsigned long endLine, int endCharOffset, - unsigned long sourceLineOffset) + unsigned long startLine, long startCharOffset, + unsigned long endLine, long endCharOffset, + unsigned long sourceLineOffset, + int promise) { - bool tagFileResized = false; - pushNarrowedInputStream (language, + bool tagFileResized; + + verbose ("runParserInNarrowedInputStream: %s; " + "file: %s, " + "start(line: %lu, offset: %ld, srcline: %lu)" + " - " + "end(line: %lu, offset: %ld)\n", + getLanguageName (language), + getInputFileName (), + startLine, startCharOffset, sourceLineOffset, + endLine, endCharOffset); + + pushNarrowedInputStream ( startLine, startCharOffset, endLine, endCharOffset, - sourceLineOffset); + sourceLineOffset, + promise); #ifndef CTAGS_LIB - tagFileResized = createTagsWithFallback1 (language); + tagFileResized = createTagsWithFallback1 (language, NULL); #else /* Simple parsing without rescans - not used by any sub-parsers anyway */ - if (LanguageTable [language]->useCork) + initializeParser (language); + parserObject *parser = &(LanguageTable [language]); + bool useCork = doesParserUseCork(parser->def); + if (useCork) corkTagFile(); createTagsForFile (language, 1); - if (LanguageTable [language]->useCork) + if (useCork) uncorkTagFile(); + tagFileResized = false; #endif popNarrowedInputStream (); return tagFileResized; @@ -2288,6 +3284,7 @@ static bool createTagsWithFallback ( const char *const fileName, const langType language, MIO *mio) { + langType exclusive_subparser = LANG_IGNORE; bool tagFileResized = false; Assert (0 <= language && language < (int) LanguageCount); @@ -2295,10 +3292,15 @@ static bool createTagsWithFallback ( if (!openInputFile (fileName, language, mio)) return false; - tagFileResized = createTagsWithFallback1 (language); + tagFileResized = createTagsWithFallback1 (language, + &exclusive_subparser); tagFileResized = forcePromises()? true: tagFileResized; + pushLanguage ((exclusive_subparser == LANG_IGNORE) + ? language + : exclusive_subparser); makeFileTag (fileName); + popLanguage (); closeInputFile (); return tagFileResized; @@ -2314,13 +3316,14 @@ extern void createTagsWithFallback(unsigned char *buffer, size_t bufferSize, if ((!buffer && openInputFile (fileName, language, NULL)) || (buffer && bufferOpen (fileName, language, buffer, bufferSize))) { + setupAnon (); initParserTrashBox (); - clearParsersUsedInCurrentInput (); setTagEntryFunction(tagCallback, userData); createTagsWithFallback1 (language, passCallback, userData); forcePromises (); closeInputFile (); finiParserTrashBox (); + teardownAnon (); } else error (WARNING, "Unable to open %s", fileName); @@ -2329,31 +3332,11 @@ extern void createTagsWithFallback(unsigned char *buffer, size_t bufferSize, extern const parserDefinition *getParserDefinition (langType language) { Assert (0 <= language && language < (int) LanguageCount); - return LanguageTable[language]; + return LanguageTable[language].def; } #endif -#ifdef HAVE_COPROC -static bool createTagsWithXcmd ( - const char *const fileName, const langType language, - MIO *mio) -{ - bool tagFileResized = false; - - if (openInputFile (fileName, language, mio)) - { - tagFileResized = invokeXcmd (fileName, language); - - /* TODO: File.lineNumber must be adjusted for the case - * Option.printTotals is non-zero. */ - closeInputFile (); - } - - return tagFileResized; -} -#endif - static void printGuessedParser (const char* const fileName, langType language) { const char *parserName; @@ -2361,10 +3344,10 @@ static void printGuessedParser (const char* const fileName, langType language) if (language == LANG_IGNORE) { Option.printLanguage = ((int)true) + 1; - parserName = "NONE"; + parserName = RSV_NONE; } else - parserName = LanguageTable [language]->name; + parserName = LanguageTable [language].def->name; printf("%s: %s\n", fileName, parserName); } @@ -2410,30 +3393,39 @@ extern void freeEncodingResources (void) { if (EncodingMap) { - int i; + unsigned int i; for (i = 0 ; i <= EncodingMapMax ; ++i) { if (EncodingMap [i]) eFree (EncodingMap [i]); } - free(EncodingMap); + eFree (EncodingMap); } if (Option.inputEncoding) eFree (Option.inputEncoding); if (Option.outputEncoding) eFree (Option.outputEncoding); } + +extern const char *getLanguageEncoding (const langType language) +{ + if (EncodingMap && language <= EncodingMapMax && EncodingMap [language]) + return EncodingMap[language]; + else + return Option.inputEncoding; +} #endif #ifndef CTAGS_LIB static void addParserPseudoTags (langType language) { - if (!LanguageTable[language]->pseudoTagPrinted) + parserObject *parser = LanguageTable + language; + if (!parser->pseudoTagPrinted) { makePtagIfEnabled (PTAG_KIND_DESCRIPTION, &language); makePtagIfEnabled (PTAG_KIND_SEPARATOR, &language); - LanguageTable[language]->pseudoTagPrinted = 1; + parser->pseudoTagPrinted = 1; } } #endif @@ -2441,24 +3433,55 @@ static void addParserPseudoTags (langType language) extern bool doesParserRequireMemoryStream (const langType language) { Assert (0 <= language && language < (int) LanguageCount); - parserDefinition *const lang = LanguageTable [language]; + parserDefinition *const lang = LanguageTable [language].def; + unsigned int i; - if (lang->tagXpathTableCount > 0) + if (lang->tagXpathTableCount > 0 + || lang->useMemoryStreamInput) + { + verbose ("%s requires a memory stream for input\n", lang->name); return true; + } - if (lang->method & METHOD_YAML) - return true; + for (i = 0; i < lang->dependencyCount; i++) + { + parserDependency *d = lang->dependencies + i; + if (d->type == DEPTYPE_SUBPARSER && + ((subparser *)(d->data))->direction & SUBPARSER_SUB_RUNS_BASE) + { + langType baseParser; + baseParser = getNamedLanguage (d->upperParser, 0); + if (doesParserRequireMemoryStream(baseParser)) + { + verbose ("%s/%s requires a memory stream for input\n", lang->name, + LanguageTable[baseParser].def->name); + return true; + } + } + } return false; } extern bool parseFile (const char *const fileName) +{ + TRACE_ENTER_TEXT("Parsing file %s",fileName); + bool bRet = parseFileWithMio (fileName, NULL); + TRACE_LEAVE(); + return bRet; +} + +extern bool parseFileWithMio (const char *const fileName, MIO *mio) { bool tagFileResized = false; langType language; - MIO *mio; + struct GetLanguageRequest req = { + .type = mio? GLR_REUSE: GLR_OPEN, + .fileName = fileName, + .mio = mio, + }; - language = getFileLanguageAndKeepMIO (fileName, &mio); + language = getFileLanguageForRequest (&req); Assert (language != LANG_AUTO); if (Option.printLanguage) @@ -2470,112 +3493,236 @@ extern bool parseFile (const char *const fileName) if (language == LANG_IGNORE) verbose ("ignoring %s (unknown language/language disabled)\n", fileName); - else if (! isLanguageEnabled (language)) - { - /* This block is needed. In the parser choosing stage, each - parser is not initialized for making ctags starting up faster. - So the chooser can choose a XCMD based parser. - However, at the stage the chooser cannot know whether - the XCMD is available or not. This isLanguageEnabled - invocation verify the availability. */ - verbose ("ignoring %s (language disabled)\n", fileName); - } else { - if (Option.filter) + Assert(isLanguageEnabled (language)); + + if (Option.filter && ! Option.interactive) openTagFile (); #ifdef HAVE_ICONV - openConverter (EncodingMap && language <= EncodingMapMax && - EncodingMap [language] ? - EncodingMap[language] : Option.inputEncoding, Option.outputEncoding); + /* TODO: checkUTF8BOM can be used to update the encodings. */ + openConverter (getLanguageEncoding (language), Option.outputEncoding); #endif setupWriter (); - clearParsersUsedInCurrentInput (); + setupAnon (); + + initParserTrashBox (); #ifndef CTAGS_LIB tagFileResized = createTagsWithFallback (fileName, language, mio); #endif -#ifdef HAVE_COPROC - if (LanguageTable [language]->method & METHOD_XCMD_AVAILABLE) - tagFileResized = createTagsWithXcmd (fileName, language, mio)? true: tagFileResized; -#endif - teardownWriter (fileName); + finiParserTrashBox (); + + teardownAnon (); - if (Option.filter) + tagFileResized = teardownWriter (getSourceFileTagPath())? true: tagFileResized; + + if (Option.filter && ! Option.interactive) closeTagFile (tagFileResized); addTotals (1, 0L, 0L); #ifdef HAVE_ICONV closeConverter (); #endif - if (mio) - mio_free (mio); + if (req.type == GLR_OPEN && req.mio) + mio_free (req.mio); return tagFileResized; } - if (mio) - mio_free (mio); + if (req.type == GLR_OPEN && req.mio) + mio_free (req.mio); + return tagFileResized; } -extern void useRegexMethod (const langType language) +static void matchLanguageMultilineRegexCommon (const langType language, + bool (* func) (struct lregexControlBlock *, const vString* const), + const vString* const allLines) { - parserDefinition* lang; + subparser *tmp; - Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - lang->method |= METHOD_REGEX; + func ((LanguageTable + language)->lregexControlBlock, allLines); + foreachSubparser(tmp, true) + { + langType t = getSubparserLanguage (tmp); + enterSubparser (tmp); + matchLanguageMultilineRegexCommon (t, func, allLines); + leaveSubparser (); + } } -extern void useXcmdMethod (const langType language) +extern void matchLanguageMultilineRegex (const langType language, + const vString* const allLines) { - parserDefinition* lang; + matchLanguageMultilineRegexCommon(language, matchMultilineRegex, allLines); +} - Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - lang->method |= METHOD_XCMD; +extern void matchLanguageMultitableRegex (const langType language, + const vString* const allLines) +{ + matchLanguageMultilineRegexCommon(language, matchMultitableRegex, allLines); } -static void useXpathMethod (const langType language) +extern void processLanguageMultitableExtendingOption (langType language, const char *const parameter) +{ + const char* src; + char* dist; + const char *tmp; + + tmp = strchr(parameter, '+'); + + if (!tmp) + error (FATAL, "no separator(+) found: %s", parameter); + + if (tmp == parameter) + error (FATAL, "the name of source table is empty in table extending: %s", parameter); + + src = tmp + 1; + if (!*src) + error (FATAL, "the name of dist table is empty in table extending: %s", parameter); + + dist = eStrndup(parameter, tmp - parameter); + extendRegexTable(((LanguageTable + language)->lregexControlBlock), src, dist); + eFree (dist); +} + +static bool lregexQueryParserAndSubparsers (const langType language, bool (* predicate) (struct lregexControlBlock *)) +{ + bool r; + subparser *tmp; + + r = predicate ((LanguageTable + language)->lregexControlBlock); + if (!r) + { + foreachSubparser(tmp, true) + { + langType t = getSubparserLanguage (tmp); + enterSubparser (tmp); + r = lregexQueryParserAndSubparsers (t, predicate); + leaveSubparser (); + + if (r) + break; + } + } + + return r; +} + +extern bool hasLanguageMultilineRegexPatterns (const langType language) +{ + return lregexQueryParserAndSubparsers (language, regexNeedsMultilineBuffer); +} + + +extern void addLanguageCallbackRegex (const langType language, const char *const regex, const char *const flags, + const regexCallback callback, bool *disabled, void *userData) +{ + addCallbackRegex ((LanguageTable +language)->lregexControlBlock, regex, flags, callback, disabled, userData); +} + +extern bool hasLanguageScopeActionInRegex (const langType language) +{ + bool hasScopeAction; + + pushLanguage (language); + hasScopeAction = lregexQueryParserAndSubparsers (language, hasScopeActionInRegex); + popLanguage (); + + return hasScopeAction; +} + +extern void matchLanguageRegex (const langType language, const vString* const line) +{ + subparser *tmp; + + matchRegex ((LanguageTable + language)->lregexControlBlock, line); + foreachSubparser(tmp, true) + { + langType t = getSubparserLanguage (tmp); + enterSubparser (tmp); + matchLanguageRegex (t, line); + leaveSubparser (); + } +} + +extern bool processLanguageRegexOption (langType language, + enum regexParserType regptype, + const char *const parameter) +{ + processTagRegexOption ((LanguageTable +language)->lregexControlBlock, + regptype, parameter); + + return true; +} + +extern bool processTabledefOption (const char *const option, const char *const parameter) +{ + langType language; + + language = getLanguageComponentInOption (option, "_tabledef-"); + if (language == LANG_IGNORE) + return false; + + if (parameter == NULL || parameter[0] == '\0') + error (FATAL, "A parameter is needed after \"%s\" option", option); + + addRegexTable((LanguageTable +language)->lregexControlBlock, parameter); + return true; +} + +extern void useRegexMethod (const langType language) { parserDefinition* lang; Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - lang->method |= METHOD_XPATH; + lang = LanguageTable [language].def; + lang->method |= METHOD_REGEX; } -extern void notifyAvailabilityXcmdMethod (const langType language) +static void useXpathMethod (const langType language) { parserDefinition* lang; Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - lang->method |= METHOD_XCMD_AVAILABLE; + lang = LanguageTable [language].def; + lang->method |= METHOD_XPATH; } static void installTagRegexTable (const langType language) { + parserObject* parser; parserDefinition* lang; unsigned int i; Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; + parser = LanguageTable + language; + lang = parser->def; if (lang->tagRegexTable != NULL) { for (i = 0; i < lang->tagRegexCount; ++i) - addTagRegex (language, - lang->tagRegexTable [i].regex, - lang->tagRegexTable [i].name, - lang->tagRegexTable [i].kinds, - lang->tagRegexTable [i].flags, - (lang->tagRegexTable [i].disabled)); + { + if (lang->tagRegexTable [i].mline) + addTagMultiLineRegex (parser->lregexControlBlock, + lang->tagRegexTable [i].regex, + lang->tagRegexTable [i].name, + lang->tagRegexTable [i].kinds, + lang->tagRegexTable [i].flags, + (lang->tagRegexTable [i].disabled)); + else + addTagRegex (parser->lregexControlBlock, + lang->tagRegexTable [i].regex, + lang->tagRegexTable [i].name, + lang->tagRegexTable [i].kinds, + lang->tagRegexTable [i].flags, + (lang->tagRegexTable [i].disabled)); + } } } @@ -2585,7 +3732,7 @@ static void installKeywordTable (const langType language) unsigned int i; Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; + lang = LanguageTable [language].def; if (lang->keywordTable != NULL) { @@ -2602,7 +3749,7 @@ static void installTagXpathTable (const langType language) unsigned int i, j; Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; + lang = LanguageTable [language].def; if (lang->tagXpathTableTable != NULL) { @@ -2613,22 +3760,46 @@ static void installTagXpathTable (const langType language) } } +extern unsigned int getXpathFileSpecCount (const langType language) +{ + parserDefinition* lang; + + Assert (0 <= language && language < (int) LanguageCount); + lang = LanguageTable [language].def; + + return lang->xpathFileSpecCount; +} + +extern xpathFileSpec* getXpathFileSpec (const langType language, unsigned int nth) +{ + parserDefinition* lang; + + Assert (0 <= language && language < (int) LanguageCount); + lang = LanguageTable [language].def; + + Assert (nth < lang->xpathFileSpecCount); + return lang->xpathFileSpecs + nth; +} + extern bool makeKindSeparatorsPseudoTags (const langType language, const ptagDesc *pdesc) { + parserObject* parser; parserDefinition* lang; - kindDefinition *kinds; + struct kindControlBlock *kcb; + kindDefinition *kind; unsigned int kindCount; unsigned int i, j; bool r = false; Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - kinds = lang->kindTable; - kindCount = lang->kindCount; + parser = LanguageTable + language; + lang = parser->def; + kcb = parser->kindControlBlock; + kindCount = countKinds(kcb); - if (kinds == NULL) + if (kindCount == 0) return r; for (i = 0; i < kindCount; ++i) @@ -2638,35 +3809,36 @@ extern bool makeKindSeparatorsPseudoTags (const langType language, if (!sepval) sepval = vStringNew (); - for (j = 0; j < kinds[i].separatorCount; ++j) + kind = getKind (kcb, i); + for (j = 0; j < kind->separatorCount; ++j) { char name[5] = {[0] = '/', [3] = '/', [4] = '\0'}; const kindDefinition *upperKind; const scopeSeparator *sep; - sep = kinds[i].separators + j; + sep = kind->separators + j; if (sep->parentKindIndex == KIND_WILDCARD_INDEX) { name[1] = KIND_WILDCARD; - name[2] = kinds[i].letter; + name[2] = kind->letter; } else if (sep->parentKindIndex == KIND_GHOST_INDEX) { /* This is root separator: no upper item is here. */ - name[1] = kinds[i].letter; + name[1] = kind->letter; name[2] = name[3]; name[3] = '\0'; } else { - upperKind = langKindDefinition (language, + upperKind = getLanguageKind (language, sep->parentKindIndex); if (!upperKind) continue; name[1] = upperKind->letter; - name[2] = kinds[i].letter; + name[2] = kind->letter; } @@ -2719,26 +3891,29 @@ static bool makeKindDescriptionPseudoTag (kindDefinition *kind, extern bool makeKindDescriptionsPseudoTags (const langType language, const ptagDesc *pdesc) { - + parserObject *parser; + struct kindControlBlock *kcb; parserDefinition* lang; - kindDefinition *kinds; + kindDefinition *kind; unsigned int kindCount, i; struct makeKindDescriptionPseudoTagData data; Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - kinds = lang->kindTable; - kindCount = lang->kindCount; + parser = LanguageTable + language; + kcb = parser->kindControlBlock; + lang = parser->def; + + kindCount = countKinds(kcb); data.langName = lang->name; data.pdesc = pdesc; data.written = false; for (i = 0; i < kindCount; ++i) - makeKindDescriptionPseudoTag (kinds + i, &data); - - foreachRegexKinds (language, makeKindDescriptionPseudoTag, &data); - foreachXcmdKinds (language, makeKindDescriptionPseudoTag, &data); + { + kind = getKind (kcb, i); + makeKindDescriptionPseudoTag (kind, &data); + } return data.written; } @@ -2751,26 +3926,27 @@ extern bool makeKindDescriptionsPseudoTags (const langType language, * * Anonymous name generator */ +static ptrArray *parsersUsedInCurrentInput; -static void clearParsersUsedInCurrentInput (void) +static void setupAnon (void) { - if (parsersUsedInCurrentInput) - ptrArrayClear (parsersUsedInCurrentInput); - else - parsersUsedInCurrentInput = ptrArrayNew (NULL); + parsersUsedInCurrentInput = ptrArrayNew (NULL); +} + +static void teardownAnon (void) +{ + ptrArrayDelete (parsersUsedInCurrentInput); } -static void anonResetMaybe (parserDefinition *lang) +static void anonResetMaybe (parserObject *parser) { - if (ptrArrayHas (parsersUsedInCurrentInput, lang)) + if (ptrArrayHas (parsersUsedInCurrentInput, parser)) return; - lang -> anonumousIdentiferId = 0; - ptrArrayAdd (parsersUsedInCurrentInput, lang); + parser -> anonymousIdentiferId = 0; + ptrArrayAdd (parsersUsedInCurrentInput, parser); } -/* GEANY DIFF */ -#if 0 static unsigned int anonHash(const unsigned char *str) { unsigned int hash = 5381; @@ -2781,33 +3957,283 @@ static unsigned int anonHash(const unsigned char *str) return hash ; } -#endif -/* GEANY DIFF END */ + +extern void anonHashString (const char *filename, char buf[9]) +{ + sprintf(buf, "%08x", anonHash((const unsigned char *)filename)); +} + extern void anonGenerate (vString *buffer, const char *prefix, int kind) { - parserDefinition* lang = LanguageTable [getInputLanguage ()]; - lang -> anonumousIdentiferId ++; + parserObject* parser = LanguageTable + getInputLanguage (); + parser -> anonymousIdentiferId ++; char szNum[32]; vStringCopyS(buffer, prefix); /* GEANY DIFF */ -/* unsigned int uHash = anonHash((const unsigned char *)getInputFileName()); - sprintf(szNum,"%08x%02x%02x",uHash,lang -> anonumousIdentiferId, kind); */ - sprintf(szNum,"%u", lang -> anonumousIdentiferId); +/* + char buf [9]; + anonHashString (getInputFileName(), buf); + sprintf(szNum,"%s%02x%02x",buf,parser -> anonymousIdentiferId, kind); +*/ + sprintf(szNum,"%u", parser -> anonymousIdentiferId); /* GEANY DIFF END */ vStringCatS(buffer,szNum); } + +extern void applyParameter (const langType language, const char *name, const char *args) +{ + parserDefinition* parser; + + + Assert (0 <= language && language < (int) LanguageCount); + + initializeParserOne (language); + parser = LanguageTable [language].def; + + if (parser->parameterHandlerTable) + { + unsigned int i; + + for (i = 0; i < parser->parameterHandlerCount; i++) + { + if (strcmp (parser->parameterHandlerTable [i].name, name) == 0) + { + parser->parameterHandlerTable [i].handleParameter (language, name, args); + return; + } + } + } + + error (FATAL, "no such parameter in %s: %s", parser->name, name); +} + +extern subparser *getNextSubparser(subparser *last, + bool includingNoneCraftedParser) +{ + langType lang = getInputLanguage (); + parserObject *parser = LanguageTable + lang; + subparser *r; + langType t; + + if (last == NULL) + r = getFirstSubparser(parser->slaveControlBlock); + else + r = last->next; + + if (r == NULL) + return r; + + t = getSubparserLanguage(r); + if (isLanguageEnabled (t) && + (includingNoneCraftedParser + || ((((LanguageTable + t)->def->method) && METHOD_NOT_CRAFTED) == 0))) + return r; + else + return getNextSubparser (r, includingNoneCraftedParser); +} + +extern slaveParser *getNextSlaveParser(slaveParser *last) +{ + langType lang = getInputLanguage (); + parserObject *parser = LanguageTable + lang; + slaveParser *r; + + if (last == NULL) + r = getFirstSlaveParser(parser->slaveControlBlock); + else + r = last->next; + + return r; +} + +extern void scheduleRunningBaseparser (int dependencyIndex) +{ + langType current = getInputLanguage (); + parserDefinition *current_parser = LanguageTable [current].def; + parserDependency *dep = NULL; + + if (dependencyIndex == RUN_DEFAULT_SUBPARSERS) + { + for (unsigned int i = 0; i < current_parser->dependencyCount; ++i) + if (current_parser->dependencies[i].type == DEPTYPE_SUBPARSER) + { + dep = current_parser->dependencies + i; + break; + } + } + else + dep = current_parser->dependencies + dependencyIndex; + + if (dep == NULL) + return; + + const char *base_name = dep->upperParser; + langType base = getNamedLanguage (base_name, 0); + parserObject *base_parser = LanguageTable + base; + + if (dependencyIndex == RUN_DEFAULT_SUBPARSERS) + useDefaultSubparsers(base_parser->slaveControlBlock); + else + useSpecifiedSubparser (base_parser->slaveControlBlock, + dep->data); + + if (!isLanguageEnabled (base)) + { + enableLanguage (base, true); + base_parser->dontEmit = true; + verbose ("force enable \"%s\" as base parser\n", base_parser->def->name); + } + + { + subparser *tmp; + + verbose ("scheduleRunningBaseparser %s with subparsers: ", base_name); + pushLanguage (base); + foreachSubparser(tmp, true) + { + langType t = getSubparserLanguage (tmp); + verbose ("%s ", getLanguageName (t)); + } + popLanguage (); + verbose ("\n"); + } + + + makePromise(base_name, THIN_STREAM_SPEC); +} + +extern bool isParserMarkedNoEmission (void) +{ + langType lang = getInputLanguage(); + parserObject *parser = LanguageTable + lang; + + return parser->dontEmit; +} + + +extern subparser* getSubparserRunningBaseparser (void) +{ + langType current = getInputLanguage (); + parserObject *current_parser = LanguageTable + current; + subparser *s = getFirstSubparser (current_parser->slaveControlBlock); + + if (s && s->schedulingBaseparserExplicitly) + return s; + else + return NULL; +} + +extern void printLanguageSubparsers (const langType language, + bool withListHeader, bool machinable, FILE *fp) +{ + for (int i = 0; i < (int) LanguageCount; i++) + initializeParserOne (i); + + struct colprintTable * table = subparserColprintTableNew(); + parserObject *parser; + + if (language == LANG_AUTO) + { + for (int i = 0; i < (int) LanguageCount; i++) + { + parser = LanguageTable + i; + if (parser->def->invisible) + continue; + + subparserColprintAddSubparsers (table, + parser->slaveControlBlock); + } + } + else + { + parser = (LanguageTable + language); + subparserColprintAddSubparsers (table, + parser->slaveControlBlock); + } + + subparserColprintTablePrint (table, + withListHeader, machinable, + fp); + colprintTableDelete (table); +} + +extern void printLangdefFlags (bool withListHeader, bool machinable, FILE *fp) +{ + struct colprintTable * table; + + table = flagsColprintTableNew (); + + flagsColprintAddDefinitions (table, PreLangDefFlagDef, ARRAY_SIZE (PreLangDefFlagDef)); + + flagsColprintTablePrint (table, withListHeader, machinable, fp); + colprintTableDelete(table); +} + +extern void printKinddefFlags (bool withListHeader, bool machinable, FILE *fp) +{ + struct colprintTable * table; + + table = flagsColprintTableNew (); + + flagsColprintAddDefinitions (table, PreKindDefFlagDef, ARRAY_SIZE (PreKindDefFlagDef)); + + flagsColprintTablePrint (table, withListHeader, machinable, fp); + colprintTableDelete(table); +} + +extern void printLanguageMultitableStatistics (langType language, FILE *vfp) +{ + parserObject* const parser = LanguageTable + language; + printMultitableStatistics (parser->lregexControlBlock, + vfp); +} + +extern void addLanguageRegexTable (const langType language, const char *name) +{ + parserObject* const parser = LanguageTable + language; + addRegexTable (parser->lregexControlBlock, name); +} + +extern void addLanguageTagMultiTableRegex(const langType language, + const char* const table_name, + const char* const regex, + const char* const name, const char* const kinds, const char* const flags, + bool *disabled) +{ + parserObject* const parser = LanguageTable + language; + addTagMultiTableRegex (parser->lregexControlBlock, table_name, regex, + name, kinds, flags, disabled); +} + #ifndef CTAGS_LIB /* * A parser for CTagsSelfTest (CTST) */ +#define SELF_TEST_PARSER "CTagsSelfTest" +#if defined(DEBUG) && defined(HAVE_SECCOMP) +extern void getppid(void); +#endif + typedef enum { K_BROKEN, + K_NO_LETTER, + K_NO_LONG_NAME, + K_NOTHING_SPECIAL, + K_GUEST_BEGINNING, + K_GUEST_END, +#if defined(DEBUG) && defined(HAVE_SECCOMP) + K_CALL_GETPPID, +#endif + K_DISABLED, + K_ENABLED, + K_ROLES, + K_ROLES_DISABLED, KIND_COUNT } CTST_Kind; @@ -2819,9 +4245,73 @@ static roleDefinition CTST_BrokenRoles [] = { {true, "broken", "broken" }, }; +typedef enum { + R_DISABLED_KIND_DISABLED_ROLE, + R_DISABLED_KIND_ENABLED_ROLE, +} CTST_DisabledKindRole; + +static roleDefinition CTST_DisabledKindRoles [] = { + { false, "disabled", "disbaled role attached to disabled kind" }, + { true, "enabled", "enabled role attached to disabled kind" }, +}; + +typedef enum { + R_ENABLED_KIND_DISABLED_ROLE, + R_ENABLED_KIND_ENABLED_ROLE, +} CTST_EnabledKindRole; + +static roleDefinition CTST_EnabledKindRoles [] = { + { false, "disabled", "disbaled role attached to enabled kind" }, + { true, "enabled", "enabled role attached to enabled kind" }, +}; + +typedef enum { + R_ROLES_KIND_A_ROLE, + R_ROLES_KIND_B_ROLE, + R_ROLES_KIND_C_ROLE, + R_ROLES_KIND_D_ROLE, +} CTST_RolesKindRole; + +static roleDefinition CTST_RolesKindRoles [] = { + { true, "a", "A role" }, + { true, "b", "B role" }, + { false, "c", "C role" }, + { true, "d", "D role" }, +}; + +typedef enum { + R_ROLES_DISABLED_KIND_A_ROLE, + R_ROLES_DISABLED_KIND_B_ROLE, +} CTST_RolesDisableKindRole; + + +static roleDefinition CTST_RolesDisabledKindRoles [] = { + { true, "A", "A role" }, + { true, "B", "B role" }, +}; + static kindDefinition CTST_Kinds[KIND_COUNT] = { + /* `a' is reserved for kinddef testing */ {true, 'b', "broken tag", "name with unwanted characters", .referenceOnly = false, ATTACH_ROLES (CTST_BrokenRoles) }, + {true, KIND_NULL, "no letter", "kind with no letter" + /* use '@' when testing. */ + }, + {true, 'L', NULL, "kind with no long name" }, + {true, 'N', "nothingSpecial", "emit a normal tag" }, + {true, 'B', NULL, "beginning of an area for a guest" }, + {true, 'E', NULL, "end of an area for a guest" }, +#if defined(DEBUG) && defined(HAVE_SECCOMP) + {true, 'P', "callGetPPid", "trigger calling getppid(2) that seccomp sandbox disallows"}, +#endif + {false,'d', "disabled", "a kind disabled by default", + .referenceOnly = false, ATTACH_ROLES (CTST_DisabledKindRoles)}, + {true, 'e', "enabled", "a kind enabled by default", + .referenceOnly = false, ATTACH_ROLES (CTST_EnabledKindRoles)}, + {true, 'r', "roles", "emit a tag with multi roles", + .referenceOnly = true, ATTACH_ROLES (CTST_RolesKindRoles)}, + {false, 'R', "rolesDisabled", "emit a tag with multi roles(disabled by default)", + .referenceOnly = true, ATTACH_ROLES (CTST_RolesDisabledKindRoles)}, }; static void createCTSTTags (void) @@ -2830,36 +4320,133 @@ static void createCTSTTags (void) const unsigned char *line; tagEntryInfo e; + unsigned long lb = 0; + unsigned long le = 0; + + int found_enabled_disabled[2] = {0, 0}; + + TRACE_ENTER_TEXT("Parsing starts"); + while ((line = readLineFromInputFile ()) != NULL) { int c = line[0]; for (i = 0; i < KIND_COUNT; i++) - if (c == CTST_Kinds[i].letter) + if ((c == CTST_Kinds[i].letter && i != K_NO_LETTER) + || (c == '@' && i == K_NO_LETTER)) { switch (i) { case K_BROKEN: initTagEntry (&e, "one\nof\rbroken\tname", i); - e.extensionFields.scopeKind = & (CTST_Kinds [K_BROKEN]); + e.extensionFields.scopeKindIndex = K_BROKEN; e.extensionFields.scopeName = "\\Broken\tContext"; makeTagEntry (&e); break; + case K_NO_LETTER: + initTagEntry (&e, "abnormal kindDefinition testing (no letter)", i); + makeTagEntry (&e); + break; + case K_NO_LONG_NAME: + initTagEntry (&e, "abnormal kindDefinition testing (no long name)", i); + makeTagEntry (&e); + break; + case K_NOTHING_SPECIAL: + if (!lb) + { + initTagEntry (&e, "NOTHING_SPECIAL", i); + makeTagEntry (&e); + } + break; + case K_GUEST_BEGINNING: + lb = getInputLineNumber (); + break; + case K_GUEST_END: + le = getInputLineNumber (); + makePromise (SELF_TEST_PARSER, lb + 1, 0, le, 0, lb + 1); + break; +#if defined(DEBUG) && defined(HAVE_SECCOMP) + case K_CALL_GETPPID: + getppid(); + break; +#endif + case K_DISABLED: + case K_ENABLED: + { + int role; + char *name; + if (found_enabled_disabled[i == K_DISABLED]++ == 0) + { + role = ROLE_INDEX_DEFINITION; + name = (i == K_DISABLED) + ? "disable-kind-no-role" + : "enabled-kind-no-role"; + } + else if (found_enabled_disabled[i == K_DISABLED]++ == 1) + { + role = (i == K_DISABLED) + ? R_DISABLED_KIND_DISABLED_ROLE + : R_ENABLED_KIND_DISABLED_ROLE; + name = (i == K_DISABLED) + ? "disable-kind-disabled-role" + : "enabled-kind-disabled-role"; + } + else + { + role = (i == K_DISABLED) + ? R_DISABLED_KIND_ENABLED_ROLE + : R_ENABLED_KIND_ENABLED_ROLE; + name = (i == K_DISABLED) + ? "disable-kind-enabled-role" + : "enabled-kind-enabled-role"; + } + initRefTagEntry (&e, name, i, role); + makeTagEntry (&e); + break; + } + case K_ROLES: + { + char *name = "multiRolesTarget"; + int qindex; + tagEntryInfo *qe; + + initTagEntry (&e, name, i); + assignRole(&e, R_ROLES_KIND_A_ROLE); + assignRole(&e, R_ROLES_KIND_C_ROLE); + assignRole(&e, R_ROLES_KIND_D_ROLE); + qindex = makeTagEntry (&e); + qe = getEntryInCorkQueue (qindex); + assignRole(qe, R_ROLES_KIND_B_ROLE); + break; + } + case K_ROLES_DISABLED: + { + char *name = "multiRolesDisabledTarget"; + + initRefTagEntry (&e, name, i, R_ROLES_DISABLED_KIND_A_ROLE); + makeTagEntry (&e); + initRefTagEntry (&e, name, i, R_ROLES_DISABLED_KIND_B_ROLE); + makeTagEntry (&e); + break; + } } } } + TRACE_LEAVE(); } static parserDefinition *CTagsSelfTestParser (void) { static const char *const extensions[] = { NULL }; - parserDefinition *const def = parserNew ("CTagsSelfTest"); + parserDefinition *const def = parserNew (SELF_TEST_PARSER); def->extensions = extensions; def->kindTable = CTST_Kinds; def->kindCount = KIND_COUNT; def->parser = createCTSTTags; def->invisible = true; + def->useMemoryStreamInput = true; + def->useCork = true; return def; } #endif /* CTAGS_LIB */ diff --git a/ctags/main/parse.h b/ctags/main/parse.h index efd38c3501..e2a0a5e2e2 100644 --- a/ctags/main/parse.h +++ b/ctags/main/parse.h @@ -15,22 +15,11 @@ #include "general.h" /* must always come first */ #include "types.h" -#include "dependency.h" -#include "field.h" #include "kind.h" -#include "parsers.h" /* contains list of parsers */ -#include "strlist.h" +#include "lregex.h" +#include "lxpath.h" #include "ctags-api.h" -#ifdef HAVE_LIBXML -#include -#include -#else -#define xmlNode void -#define xmlXPathCompExpr void -#define xmlXPathContext void -#endif - /* * MACROS */ @@ -57,68 +46,17 @@ typedef void (*parserInitialize) (langType language); The finalizer is called even when the initializer of the same parser is called or not. However, the finalizer can know - whether the assoiciated initializer is invoked or not with the + whether the associated initializer is invoked or not with the second parameter: INITIALIZED. If it is true, the initializer is called. */ typedef void (*parserFinalize) (langType language, bool initialized); -typedef const char * (*selectLanguage) (MIO *); - typedef enum { METHOD_NOT_CRAFTED = 1 << 0, METHOD_REGEX = 1 << 1, - METHOD_XCMD = 1 << 2, - METHOD_XCMD_AVAILABLE = 1 << 3, - METHOD_XPATH = 1 << 4, - METHOD_YAML = 1 << 5, + METHOD_XPATH = 1 << 2, } parsingMethod; -typedef struct { - const char *const regex; - const char* const name; - const char* const kinds; - const char *const flags; - bool *disabled; -} tagRegexTable; - -typedef struct sTagXpathMakeTagSpec { - int kind; - int role; - /* If make is NULL, just makeTagEntry is used instead. */ - void (*make) (xmlNode *node, - const struct sTagXpathMakeTagSpec *spec, - tagEntryInfo *tag, - void *userData); -} tagXpathMakeTagSpec; - -typedef struct sTagXpathRecurSpec { - void (*enter) (xmlNode *node, - const struct sTagXpathRecurSpec *spec, - xmlXPathContext *ctx, - void *userData); - - int nextTable; /* A parser can use this field any purpose. - main/lxpath part doesn't touch this. */ - -} tagXpathRecurSpec; - -typedef struct sTagXpathTable -{ - const char *const xpath; - enum { LXPATH_TABLE_DO_MAKE, LXPATH_TABLE_DO_RECUR } specType; - union { - tagXpathMakeTagSpec makeTagSpec; - tagXpathRecurSpec recurSpec; - } spec; - xmlXPathCompExpr* xpathCompiled; -} tagXpathTable; - -typedef struct sTagXpathTableTable { - tagXpathTable *table; - unsigned int count; -} tagXpathTableTable; - - typedef struct { const char *name; const int id; @@ -127,9 +65,8 @@ typedef struct { struct sParserDefinition { /* defined by parser */ char* name; /* name of language */ - kindDefinition* kindTable; /* tag kinds handled by parser */ + kindDefinition* kindTable; /* tag kinds handled by parser */ unsigned int kindCount; /* size of `kinds' list */ - kindDefinition* fileKind; /* kind for overriding the default fileKind */ const char *const *extensions; /* list of default extensions */ const char *const *patterns; /* list of default file name patterns */ const char *const *aliases; /* list of default aliases (alternative names) */ @@ -140,6 +77,7 @@ struct sParserDefinition { selectLanguage* selectLanguage; /* may be used to resolve conflicts */ unsigned int method; /* See METHOD_ definitions above */ bool useCork; + bool useMemoryStreamInput; bool allowNullTag; bool requestAutomaticFQTag; tagRegexTable *tagRegexTable; @@ -151,110 +89,51 @@ struct sParserDefinition { bool invisible; fieldDefinition *fieldTable; unsigned int fieldCount; + xtagDefinition *xtagTable; + unsigned int xtagCount; parserDependency * dependencies; unsigned int dependencyCount; + + parameterHandlerTable *parameterHandlerTable; + unsigned int parameterHandlerCount; + + xpathFileSpec *xpathFileSpecs; + unsigned int xpathFileSpecCount; + /* used internally */ langType id; /* id assigned to language */ unsigned int enabled:1; /* currently enabled? */ - unsigned int initialized:1; /* initialize() is called or not */ - unsigned int pseudoTagPrinted:1; /* pseudo tags about this parser - is emitted or not. */ - subparser *subparsers; /* The parsers on this list must be initialized when - this parser is initialized. */ - - stringList* currentPatterns; /* current list of file name patterns */ - stringList* currentExtensions; /* current list of extensions */ - stringList* currentAliases; /* current list of aliases */ - unsigned int anonumousIdentiferId; /* managed by anon* functions */ + unsigned int traced:1; }; typedef parserDefinition* (parserDefinitionFunc) (void); -typedef struct { - size_t start; /* character index in line where match starts */ - size_t length; /* length of match */ -} regexMatch; - -typedef void (*regexCallback) (const char *line, const regexMatch *matches, unsigned int count, - void *userData); - -typedef enum { - LMAP_PATTERN = 1 << 0, - LMAP_EXTENSION = 1 << 1, - LMAP_ALL = LMAP_PATTERN | LMAP_EXTENSION, -} langmapType; - - /* * FUNCTION PROTOTYPES */ -/* Each parsers' definition function is called. The routine is expected to - * return a structure allocated using parserNew(). This structure must, - * at minimum, set the `parser' field. - */ -extern parserDefinitionFunc PARSER_LIST; -#ifdef HAVE_LIBXML -extern parserDefinitionFunc XML_PARSER_LIST; -#endif -#ifdef HAVE_LIBYAML -extern parserDefinitionFunc YAML_PARSER_LIST; -#endif - - /* Language processing and parsing */ -extern int makeSimpleTag (const vString* const name, const int kind); -extern int makeSimpleRefTag (const vString* const name, const int kindIndexS, +extern int makeSimpleTag (const vString* const name, const int kindIndex); +extern int makeSimpleRefTag (const vString* const name, const int kindIndex, int roleIndex); extern parserDefinition* parserNew (const char* name); -extern parserDefinition* parserNewFull (const char* name, char fileKind); -extern kindDefinition* getLanguageKind(const langType language, int kindIndex); -extern bool doesLanguageAllowNullTag (const langType language); -extern bool doesLanguageRequestAutomaticFQTag (const langType language); + extern const char *getLanguageName (const langType language); -extern kindDefinition* getLanguageFileKind (const langType language); +extern const char *getLanguageKindName (const langType language, const int kindIndex); + extern langType getNamedLanguage (const char *const name, size_t len); -extern langType getFileLanguage (const char *const fileName); +extern langType getLanguageForFilenameAndContents (const char *const fileName); extern langType getLanguageForCommand (const char *const command, langType startFrom); extern langType getLanguageForFilename (const char *const filename, langType startFrom); extern bool isLanguageEnabled (const langType language); extern bool isLanguageKindEnabled (const langType language, int kindIndex); +extern bool isLanguageRoleEnabled (const langType language, int kindIndex, int roleIndex); -extern void installLanguageMapDefault (const langType language); -extern void installLanguageMapDefaults (void); -extern void clearLanguageMap (const langType language); -extern bool removeLanguageExtensionMap (const langType language, const char *const extension); -extern void addLanguageExtensionMap (const langType language, const char* extension, - bool exclusiveInAllLanguages); -extern bool removeLanguagePatternMap (const langType language, const char *const pattern); -extern void addLanguagePatternMap (const langType language, const char* ptrn, - bool exclusiveInAllLanguages); - -extern void installLanguageAliasesDefault (const langType language); -extern void installLanguageAliasesDefaults (void); -extern void clearLanguageAliases (const langType language); -extern void addLanguageAlias (const langType language, const char* alias); +extern kindDefinition* getLanguageKindForLetter (const langType language, char kindLetter); -extern void printLanguageMap (const langType language, FILE *fp); -extern void printLanguageMaps (const langType language, langmapType type); -extern void enableLanguages (const bool state); -extern void enableLanguage (const langType language, const bool state); -extern void initializeParsing (void); extern void initializeParser (langType language); -extern unsigned int countParsers (void); -extern void freeParserResources (void); -extern void printLanguageFileKind (const langType language); -extern void printLanguageKinds (const langType language, bool allKindFields); -extern void printLanguageRoles (const langType language, const char* letters); -extern void printLanguageAliases (const langType language); -extern void printLanguageList (void); -extern bool doesParserRequireMemoryStream (const langType language); -extern bool parseFile (const char *const fileName); -extern bool runParserInNarrowedInputStream (const langType language, - unsigned long startLine, int startCharOffset, - unsigned long endLine, int endCharOffset, - unsigned long sourceLineOffset); + #ifdef CTAGS_LIB extern void createTagsWithFallback(unsigned char *buffer, size_t bufferSize, const char *fileName, const langType language, @@ -264,63 +143,25 @@ extern const parserDefinition *getParserDefinition (langType language); #endif #ifdef HAVE_ICONV -extern void freeEncodingResources (void); +extern const char *getLanguageEncoding (const langType language); #endif /* Regex interface */ -extern void findRegexTags (void); +extern void addLanguageCallbackRegex (const langType language, const char *const regex, const char *const flags, + const regexCallback callback, bool *disabled, void *userData); extern void findRegexTagsMainloop (int (* driver)(void)); -extern bool matchRegex (const vString* const line, const langType language); -extern void addLanguageRegex (const langType language, const char* const regex); -extern void addTagRegex (const langType language, const char* const regex, - const char* const name, const char* const kinds, const char* const flags, - bool *disabled); -extern void addCallbackRegex (const langType language, const char *const regexo, const char *const flags, - const regexCallback callback, bool *disabled, void *userData); -extern void resetRegexKinds (const langType language, bool mode); -extern bool enableRegexKind (const langType language, const int kind, const bool mode); -extern bool enableRegexKindLong (const langType language, const char *kindLong, const bool mode); -extern bool isRegexKindEnabled (const langType language, const int kind); -extern bool hasRegexKind (const langType language, const int kind); -extern void printRegexKinds (const langType language, bool allKindFields, bool indent, - bool tabSeparated); -extern void foreachRegexKinds (const langType language, bool (* func) (kindDefinition*, void*), void *data); -extern void freeRegexResources (void); -extern bool checkRegex (void); -extern void useRegexMethod (const langType language); -extern void printRegexFlags (void); -extern bool hasScopeActionInRegex (const langType language); - -#ifdef HAVE_COPROC -extern bool invokeXcmd (const char* const fileName, const langType language); -#endif -extern void addLanguageXcmd (const langType language, const char* const path); -extern void addTagXcmd (const langType language, vString* pathvstr, const char* flaggs); -extern void resetXcmdKinds (const langType language, bool mode); -extern bool enableXcmdKind (const langType language, const int kind, const bool mode); -extern bool enableXcmdKindLong (const langType language, const char *kindLong, const bool mode); -extern bool isXcmdKindEnabled (const langType language, const int kind); -extern bool hasXcmdKind (const langType language, const int kind); -extern void printXcmdKinds (const langType language, bool allKindFields, bool indent, - bool tabSeparated); -extern void foreachXcmdKinds (const langType language, bool (* func) (kindDefinition*, void*), void *data); -extern void freeXcmdResources (void); -extern void useXcmdMethod (const langType language); -extern void notifyAvailabilityXcmdMethod (const langType language); - -/* Xpath interface */ -extern void findXMLTags (xmlXPathContext *ctx, xmlNode *root, - const tagXpathTableTable *xpathTableTable, - const kindDefinition* const kinds, void *userData); -extern void addTagXpath (const langType language, tagXpathTable *xpathTable); - +extern void findRegexTags (void); -extern bool makeKindSeparatorsPseudoTags (const langType language, - const ptagDesc *pdesc); -extern bool makeKindDescriptionsPseudoTags (const langType language, - const ptagDesc *pdesc); +/* Multiline Regex Interface */ +extern void addLanguageRegexTable (const langType language, const char *name); +extern void addLanguageTagMultiTableRegex(const langType language, + const char* const table_name, + const char* const regex, + const char* const name, const char* const kinds, const char* const flags, + bool *disabled); extern void anonGenerate (vString *buffer, const char *prefix, int kind); +extern void anonHashString (const char *filename, char buf[9]); #endif /* CTAGS_MAIN_PARSE_H */ diff --git a/ctags/main/parse_p.h b/ctags/main/parse_p.h new file mode 100644 index 0000000000..5039575d97 --- /dev/null +++ b/ctags/main/parse_p.h @@ -0,0 +1,154 @@ +/* +* Copyright (c) 1998-2003, Darren Hiebert +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* Private definitions for parsing support. +*/ +#ifndef CTAGS_MAIN_PARSE_PRIVATE_H +#define CTAGS_MAIN_PARSE_PRIVATE_H + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ +#include "kind.h" +#include "lregex_p.h" +#include "parse.h" +#include "parsers.h" /* contains list of parsers */ +#include "strlist.h" + +/* +* MACROS +*/ + +/* +* DATA DECLARATIONS +*/ +typedef enum { + LMAP_PATTERN = 1 << 0, + LMAP_EXTENSION = 1 << 1, + LMAP_ALL = LMAP_PATTERN | LMAP_EXTENSION, + LMAP_TABLE_OUTPUT = 1 << 2, +} langmapType; + +/* +* FUNCTION PROTOTYPES +*/ + +/* Each parsers' definition function is called. The routine is expected to + * return a structure allocated using parserNew(). This structure must, + * at minimum, set the `parser' field. + */ +extern parserDefinitionFunc PARSER_LIST; +#ifdef HAVE_LIBXML +extern parserDefinitionFunc XML_PARSER_LIST; +#endif +#ifdef HAVE_LIBYAML +extern parserDefinitionFunc YAML_PARSER_LIST; +#endif + +extern bool doesLanguageAllowNullTag (const langType language); +extern bool doesLanguageRequestAutomaticFQTag (const langType language); + +extern kindDefinition* getLanguageKind(const langType language, int kindIndex); +extern kindDefinition* getLanguageKindForName (const langType language, const char *kindName); +extern roleDefinition* getLanguageRole(const langType language, int kindIndex, int roleIndex); +extern roleDefinition* getLanguageRoleForName (const langType language, int kindIndex, + const char *roleName); + + +extern int defineLanguageKind (const langType language, kindDefinition *def, + freeKindDefFunc freeKindDef); + +extern unsigned int countLanguageKinds (const langType language); +extern unsigned int countLanguageRoles (const langType language, int kindIndex); + +extern bool isLanguageKindRefOnly (const langType language, int kindIndex); + +extern bool isLanguageVisible (const langType language); + + +extern void installLanguageMapDefault (const langType language); +extern void installLanguageMapDefaults (void); +extern void clearLanguageMap (const langType language); +extern bool removeLanguageExtensionMap (const langType language, const char *const extension); +extern void addLanguageExtensionMap (const langType language, const char* extension, + bool exclusiveInAllLanguages); +extern bool removeLanguagePatternMap (const langType language, const char *const pattern); +extern void addLanguagePatternMap (const langType language, const char* ptrn, + bool exclusiveInAllLanguages); + +extern void installLanguageAliasesDefault (const langType language); +extern void installLanguageAliasesDefaults (void); +extern void clearLanguageAliases (const langType language); +extern void addLanguageAlias (const langType language, const char* alias); + +extern void printLanguageMaps (const langType language, langmapType type, + bool withListHeader, bool machinable, FILE *fp); +extern void enableLanguages (const bool state); +extern void enableLanguage (const langType language, const bool state); +extern void initializeParsing (void); + +extern unsigned int countParsers (void); +extern void freeParserResources (void); +extern void enableDefaultFileKind (bool state); +extern void printLanguageKinds (const langType language, bool allKindFields, + bool withListHeader, bool machinable, FILE *fp); +extern void printLanguageRoles (const langType language, const char* letters, + bool withListHeader, bool machinable, FILE *fp); +extern void printLanguageAliases (const langType language, + bool withListHeader, bool machinable, FILE *fp); +extern void printLanguageList (void); +extern void printLanguageParameters (const langType language, + bool withListHeader, bool machinable, FILE *fp); +extern void printLanguageSubparsers (const langType language, + bool withListHeader, bool machinable, FILE *fp); +extern void printLangdefFlags (bool withListHeader, bool machinable, FILE *fp); +extern void printKinddefFlags (bool withListHeader, bool machinable, FILE *fp); +extern bool doesParserRequireMemoryStream (const langType language); +extern bool parseFile (const char *const fileName); +extern bool parseFileWithMio (const char *const fileName, MIO *mio); +extern bool runParserInNarrowedInputStream (const langType language, + unsigned long startLine, long startCharOffset, + unsigned long endLine, long endCharOffset, + unsigned long sourceLineOffset, + int promise); + +#ifdef HAVE_ICONV +extern void freeEncodingResources (void); +#endif + +/* Regex interface */ +extern bool processLanguageRegexOption (langType language, enum regexParserType regptype, const char *const parameter); +extern void notifyLanguageRegexInputStart (langType language); +extern void notifyLanguageRegexInputEnd (langType language); + +extern void matchLanguageRegex (const langType language, const vString* const line); +extern void freeRegexResources (void); +extern bool checkRegex (void); +extern void useRegexMethod (const langType language); +extern void printRegexFlags (bool withListHeader, bool machinable, FILE *fp); +extern void printMultilineRegexFlags (bool withListHeader, bool machinable, FILE *fp); +extern void printMultitableRegexFlags (bool withListHeader, bool machinable, FILE *fp); +extern bool hasLanguageScopeActionInRegex (const langType language); + +/* Multiline Regex Interface */ +extern bool hasLanguageMultilineRegexPatterns (const langType language); +extern void matchLanguageMultilineRegex (const langType language, const vString* const allLines); +extern void matchLanguageMultitableRegex (const langType language, const vString* const allLines); + +extern void processLanguageMultitableExtendingOption (langType language, const char *const parameter); + +extern unsigned int getXpathFileSpecCount (const langType language); +extern xpathFileSpec* getXpathFileSpec (const langType language, unsigned int nth); + +extern bool makeKindSeparatorsPseudoTags (const langType language, + const ptagDesc *pdesc); +extern bool makeKindDescriptionsPseudoTags (const langType language, + const ptagDesc *pdesc); + +extern void printLanguageMultitableStatistics (langType language, FILE *vfp); + +#endif /* CTAGS_MAIN_PARSE_PRIVATE_H */ diff --git a/ctags/main/promise.c b/ctags/main/promise.c index 82d6be668b..fbdd04acb3 100644 --- a/ctags/main/promise.c +++ b/ctags/main/promise.c @@ -11,27 +11,58 @@ */ #include "general.h" +#include "parse_p.h" #include "promise.h" +#include "ptrarray.h" #include "debug.h" +#include "read.h" +#include "trashbox.h" #include "xtag.h" +#include "numarray.h" +#include "routines.h" +#include struct promise { langType lang; unsigned long startLine; - int startCharOffset; + long startCharOffset; unsigned long endLine; - int endCharOffset; + long endCharOffset; unsigned long sourceLineOffset; + int parent_promise; + ptrArray *modifiers; +}; + +typedef void ( *promiseInputModifier) (unsigned char * input, + size_t size, + unsigned long startLine, long startCharOffset, + unsigned long endLine, long endCharOffset, + void *data); +typedef void ( *promiseDestroyAttachedData) (void *data); + +struct modifier { + promiseInputModifier modifier; + promiseDestroyAttachedData destroyData; + void *data; }; static struct promise *promises; static int promise_count; static int promise_allocated; +#define NO_PROMISE -1 +static int current_promise = NO_PROMISE; + +static void attachPromiseModifier (int promise, + promiseInputModifier modifier, + promiseDestroyAttachedData destroyData, + void *data); + + int makePromise (const char *parser, - unsigned long startLine, int startCharOffset, - unsigned long endLine, int endCharOffset, + unsigned long startLine, long startCharOffset, + unsigned long endLine, long endCharOffset, unsigned long sourceLineOffset) { struct promise *p; @@ -40,7 +71,12 @@ int makePromise (const char *parser, /* GEANY DIFF */ /* - if (!isXtagEnabled (XTAG_TAGS_GENERATED_BY_SUB_PARSERS)) + if ((!isThinStreamSpec(startLine, + startCharOffset, + endLine, + endCharOffset, + sourceLineOffset)) + && ( !isXtagEnabled (XTAG_TAGS_GENERATED_BY_GUEST_PARSERS))) return -1; */ /* GEANY DIFF END */ @@ -52,28 +88,73 @@ int makePromise (const char *parser, if ( promise_count == promise_allocated) { size_t c = promise_allocated? (promise_allocated * 2): 8; + if (promises) + DEFAULT_TRASH_BOX_TAKE_BACK(promises); promises = xRealloc (promises, c, struct promise); + DEFAULT_TRASH_BOX(promises, eFree); promise_allocated = c; } p = promises + promise_count; - + p->parent_promise = current_promise; p->lang = lang; p->startLine = startLine; p->startCharOffset = startCharOffset; p->endLine = endLine; p->endCharOffset = endCharOffset; p->sourceLineOffset = sourceLineOffset; + p->modifiers = NULL; r = promise_count; promise_count++; return r; } +static void freeModifier (void *data) +{ + struct modifier *m = data; + m->destroyData(m->data); + eFree (m); +} + +static void attachPromiseModifier (int promise, + promiseInputModifier modifier, + promiseDestroyAttachedData destroyData, + void *data) +{ + struct modifier *m = xMalloc (1, struct modifier); + + m->modifier = modifier; + m->destroyData = destroyData; + m->data = data; + + struct promise *p = promises + promise; + if (!p->modifiers) + p->modifiers = ptrArrayNew(freeModifier); + + ptrArrayAdd (p->modifiers, m); +} + +static void freeModifiers(int promise) +{ + for (int i = promise; i < promise_count; i++) + { + struct promise *p = promises + promise; + if (p->modifiers) + { + ptrArrayDelete (p->modifiers); + p->modifiers = NULL; + } + } +} + void breakPromisesAfter (int promise) { Assert (promise_count >= promise); + if (promise == NO_PROMISE) + promise = 0; + freeModifiers(promise); promise_count = promise; } @@ -84,17 +165,21 @@ bool forcePromises (void) for (i = 0; i < promise_count; ++i) { + current_promise = i; struct promise *p = promises + i; tagFileResized = runParserInNarrowedInputStream (p->lang, p->startLine, p->startCharOffset, p->endLine, p->endCharOffset, - p->sourceLineOffset) + p->sourceLineOffset, + i) ? true : tagFileResized; } + freeModifiers (0); + current_promise = NO_PROMISE; promise_count = 0; return tagFileResized; } @@ -104,3 +189,123 @@ int getLastPromise (void) { return promise_count - 1; } + +static unsigned char* fill_or_skip (unsigned char *input, unsigned char *input_end, bool filling) +{ + if ( !(input < input_end)) + return NULL; + + unsigned char *next = memchr(input, '\n', input_end - input); + if (next) + { + if (filling) + memset(input, ' ', next - input); + input = next + 1; + if (input == input_end) + return NULL; + else + return input; + } + else + { + if (filling) + memset(input, ' ', input_end - input); + return NULL; + } +} + +static void line_filler (unsigned char *input, size_t size, + unsigned long startLine, long startCharOffset, + unsigned long endLine, long endCharOffset, + void *data) +{ + ulongArray *lines = data; + unsigned int start_index, end_index; + unsigned int i; + + for (i = 0; i < ulongArrayCount (lines); i++) + { + unsigned long line = ulongArrayItem (lines, i); + if (line >= startLine) + break; + } + if (i == ulongArrayCount (lines)) + return; + if (i > endLine) + return; + start_index = i; + + for (; i < ulongArrayCount (lines); i++) + { + unsigned long line = ulongArrayItem (lines, i); + if (line > endLine) + break; + } + end_index = i; + + unsigned long input_line = startLine; + for (i = start_index; i < end_index; i++) + { + unsigned long line = ulongArrayItem (lines, i); + + while (1) + { + if (input_line == line) + { + input = fill_or_skip (input, input + size, true); + input_line++; + break; + } + else + { + input = fill_or_skip (input, input + size, false); + input_line++; + } + } + } +} + +void promiseAttachLineFiller (int promise, ulongArray *lines) +{ + attachPromiseModifier (promise, line_filler, + (promiseDestroyAttachedData)ulongArrayDelete, + lines); +} + + +static void collectModifiers(int promise, ptrArray *modifiers) +{ + while (promise != NO_PROMISE) + { + struct promise *p = promises + promise; + if (p->modifiers) + { + for (int i = ptrArrayCount(p->modifiers); i > 0; i--) + { + struct modifier *m = ptrArrayItem(p->modifiers, i - 1); + ptrArrayAdd (modifiers, m); + } + } + promise = p->parent_promise; + } +} + +void runModifiers (int promise, + unsigned long startLine, long startCharOffset, + unsigned long endLine, long endCharOffset, + unsigned char *input, + size_t size) +{ + ptrArray *modifiers = ptrArrayNew (NULL); + + collectModifiers (promise, modifiers); + for (int i = ptrArrayCount (modifiers); i > 0 ; i--) + { + struct modifier *m = ptrArrayItem (modifiers, i - 1); + m->modifier (input, size, + startLine, startCharOffset, + endLine, endCharOffset, + m->data); + } + ptrArrayDelete (modifiers); +} diff --git a/ctags/main/promise.h b/ctags/main/promise.h index dde6f78efb..65b2734739 100644 --- a/ctags/main/promise.h +++ b/ctags/main/promise.h @@ -15,13 +15,15 @@ #include "general.h" #include "mio.h" #include "parse.h" +#include "numarray.h" int makePromise (const char *parser, - unsigned long startLine, int startCharOffset, - unsigned long endLine, int endCharOffset, + unsigned long startLine, long startCharOffset, + unsigned long endLine, long endCharOffset, unsigned long sourceLineOffset); -bool forcePromises (void); -void breakPromisesAfter (int promise); -int getLastPromise (void); + +/* Fill the line with white spaces. + The callee takes the ownership of lines. */ +void promiseAttachLineFiller (int promise, ulongArray *lines); #endif /* CTAGS_MAIN_PROMISE_H */ diff --git a/ctags/main/promise_p.h b/ctags/main/promise_p.h new file mode 100644 index 0000000000..3b5cc6a0b1 --- /dev/null +++ b/ctags/main/promise_p.h @@ -0,0 +1,24 @@ +/* + * + * Copyright (c) 2016, Red Hat, Inc. + * Copyright (c) 2016, Masatake YAMATO + * + * Author: Masatake YAMATO + * + * This source code is released for free distribution under the terms of the + * GNU General Public License version 2 or (at your option) any later version. + * + */ +#ifndef CTAGS_MAIN_PROMISE_PRIVATE_H +#define CTAGS_MAIN_PROMISE_PRIVATE_H + +bool forcePromises (void); +void breakPromisesAfter (int promise); +int getLastPromise (void); +void runModifiers (int promise, + unsigned long startLine, long startCharOffset, + unsigned long endLine, long endCharOffset, + unsigned char *input, + size_t size); + +#endif /* CTAGS_MAIN_PROMISE_PRIVATE_H */ diff --git a/ctags/main/ptag.c b/ctags/main/ptag.c index 643e50eb37..69c0224951 100644 --- a/ctags/main/ptag.c +++ b/ctags/main/ptag.c @@ -13,22 +13,17 @@ #include "general.h" /* must always come first */ +#include "colprint_p.h" #include "ctags.h" #include "debug.h" -#include "options.h" -#include "parse.h" -#include "ptag.h" -#include "output.h" +#include "entry_p.h" +#include "options_p.h" +#include "parse_p.h" +#include "ptag_p.h" +#include "writer_p.h" #include -static bool writePseudoTagForXcmdData (ptagDesc *desc, - struct ptagXcmdData *pdata) -{ - return writePseudoTag (desc, - pdata->fileName, pdata->pattern, pdata->language); -} - static bool ptagMakeFormat (ptagDesc *desc, void *data CTAGS_ATTR_UNUSED) { char format [11]; @@ -52,37 +47,22 @@ static bool ptagMakeHowSorted (ptagDesc *desc, void *data CTAGS_ATTR_UNUSED) NULL); } -static bool ptagMakeAuthor (ptagDesc *desc, void *data) +static bool ptagMakeAuthor (ptagDesc *desc, void *data CTAGS_ATTR_UNUSED) { - struct ptagXcmdData *pdata = data; - - if (pdata) - return writePseudoTagForXcmdData (desc, data); - else - return writePseudoTag (desc, - AUTHOR_NAME, "", NULL); + return writePseudoTag (desc, + AUTHOR_NAME, "", NULL); } -static bool ptagMakeProgName (ptagDesc *desc, void *data) +static bool ptagMakeProgName (ptagDesc *desc, void *data CTAGS_ATTR_UNUSED) { - struct ptagXcmdData *pdata = data; - - if (pdata) - return writePseudoTagForXcmdData (desc, data); - else - return writePseudoTag (desc, - PROGRAM_NAME, "Derived from Exuberant Ctags", NULL); + return writePseudoTag (desc, + PROGRAM_NAME, "Derived from Exuberant Ctags", NULL); } -static bool ptagMakeProgURL (ptagDesc *desc, void *data) +static bool ptagMakeProgURL (ptagDesc *desc, void *data CTAGS_ATTR_UNUSED) { - struct ptagXcmdData *pdata = data; - - if (pdata) - return writePseudoTagForXcmdData (desc, data); - else - return writePseudoTag (desc, - PROGRAM_URL, "official site", NULL); + return writePseudoTag (desc, + PROGRAM_URL, "official site", NULL); } static bool ptagMakeProgVersion (ptagDesc *desc, void *data CTAGS_ATTR_UNUSED) @@ -115,6 +95,8 @@ static bool ptagMakeKindDescriptions (ptagDesc *desc, void *data) } static ptagDesc ptagDescs [] = { +/* GEANY DIFF */ +#if 0 { /* The prefix is not "TAG_". Only --output-format=json use this ptag. */ @@ -122,6 +104,8 @@ static ptagDesc ptagDescs [] = { "the version of json output stream format", ptagMakeJsonOutputVersion, true }, +#endif +/* GEANY DIFF END */ { true, "TAG_FILE_FORMAT", "the version of tags file format", ptagMakeFormat, @@ -160,6 +144,10 @@ static ptagDesc ptagDescs [] = { "the letters, names and descriptions of kinds in a parser", ptagMakeKindDescriptions, false }, + { true, "TAG_OUTPUT_MODE", + "the output mode: u-ctags or e-ctags", + ptagMakeCtagsOutputMode, + true }, }; extern bool makePtagIfEnabled (ptagType type, void *data) @@ -225,10 +213,30 @@ extern bool isPtagCommonInParsers (ptagType type) return pdesc->commonInParsers; } -extern void printPtag (ptagType type) +static int ptagCompare (struct colprintLine *a, struct colprintLine *b) { - printf("%s\t%s\t%s\n", - ptagDescs[type].name, - ptagDescs[type].description? ptagDescs[type].description: "NONE", - ptagDescs[type].enabled? "on": "off"); + const char *a_name = colprintLineGetColumn (a, 0); + const char *b_name = colprintLineGetColumn (b, 0); + return strcmp(a_name, b_name); +} + +extern void printPtags (bool withListHeader, bool machinable, FILE *fp) +{ + struct colprintTable *table = colprintTableNew ("L:NAME", + "L:ENABLED", + "L:DESCRIPTION", + NULL); + for (unsigned int i = 0; i < PTAG_COUNT; i++) + { + struct colprintLine *line = colprintTableGetNewLine (table); + colprintLineAppendColumnCString (line, ptagDescs[i].name); + colprintLineAppendColumnCString (line, ptagDescs[i].enabled + ? "on" + : "off"); + colprintLineAppendColumnCString (line, ptagDescs[i].description); + } + + colprintTableSort (table, ptagCompare); + colprintTablePrint (table, 0, withListHeader, machinable, fp); + colprintTableDelete (table); } diff --git a/ctags/main/ptag.h b/ctags/main/ptag_p.h similarity index 91% rename from ctags/main/ptag.h rename to ctags/main/ptag_p.h index c6f68fbfd5..8574103804 100644 --- a/ctags/main/ptag.h +++ b/ctags/main/ptag_p.h @@ -22,7 +22,11 @@ typedef enum ePtagType { /* pseudo tag content control */ PTAG_UNKNOWN = -1, /* Only --output-format=json use this ptag. Applications of the output may expect this comes first in the output. */ +/* GEANY DIFF */ +/* PTAG_JSON_OUTPUT_VERSION, +*/ +/* GEANY DIFF END */ PTAG_FILE_FORMAT, PTAG_FILE_SORTED, @@ -35,6 +39,7 @@ typedef enum ePtagType { /* pseudo tag content control */ #endif PTAG_KIND_SEPARATOR, PTAG_KIND_DESCRIPTION, + PTAG_OUTPUT_MODE, PTAG_COUNT } ptagType; @@ -46,16 +51,10 @@ struct sPtagDesc { bool commonInParsers; }; -struct ptagXcmdData { - const char *fileName; - const char *pattern; - const char *language; -}; - extern bool makePtagIfEnabled (ptagType type, void *data); extern ptagDesc* getPtagDesc (ptagType type); extern ptagType getPtagTypeForName (const char *name); -extern void printPtag (ptagType type); +extern void printPtags (bool withListHeader, bool machinable, FILE *fp); extern bool isPtagEnabled (ptagType type); extern bool isPtagCommonInParsers (ptagType type); extern bool enablePtag (ptagType type, bool state); diff --git a/ctags/main/read.c b/ctags/main/read.c index 6a1750b51d..9d8bfa1237 100644 --- a/ctags/main/read.c +++ b/ctags/main/read.c @@ -15,27 +15,24 @@ #include #include +#include #define FILE_WRITE #include "read.h" #include "debug.h" -#include "entry.h" +#include "entry_p.h" #include "main.h" #include "routines.h" -#include "options.h" +#include "routines_p.h" +#include "options_p.h" +#include "parse_p.h" +#include "promise_p.h" +#include "trace.h" +#include "trashbox.h" #ifdef HAVE_ICONV # include "mbcs.h" #endif -#include -#include -#ifdef HAVE_SYS_TYPES_H -# include /* declare off_t (not known to regex.h on FreeBSD) */ -#endif -/* GEANY DIFF -#include - * GEANY DIFF END */ - /* * DATA DECLARATIONS */ @@ -62,22 +59,26 @@ typedef struct sInputFileInfo { on the input stream. This is needed for nested stream. */ bool isHeader; /* is input file a header file? */ - - /* language of input file */ - inputLangInfo langInfo; } inputFileInfo; +typedef struct sComputPos { + MIOPos pos; + long offset; + bool open; + int crAdjustment; +} compoundPos; + typedef struct sInputLineFposMap { - MIOPos *pos; + compoundPos *pos; unsigned int count; unsigned int size; } inputLineFposMap; typedef struct sNestedInputStreamInfo { unsigned long startLine; - int startCharOffset; + long startCharOffset; unsigned long endLine; - int endCharOffset; + long endCharOffset; } nestedInputStreamInfo; typedef struct sInputFile { @@ -85,10 +86,11 @@ typedef struct sInputFile { vString *line; /* last line read from file */ const unsigned char* currentLine; /* current line being worked on */ MIO *mio; /* MIO stream used for reading the file */ - MIOPos filePosition; /* file position of current line */ + compoundPos filePosition; /* file position of current line */ unsigned int ungetchIdx; - int ungetchBuf[3]; /* characters that were ungotten */ + int ungetchBuf[8]; /* characters that were ungotten */ + bool bomFound; /* Contains data pertaining to the original `source' file in which the tag * was defined. This may be different from the `input' file when #line * directives are processed (i.e. the input file is preprocessor output). @@ -103,11 +105,14 @@ typedef struct sInputFile { be referred from tagsEntryInfo instances. sourceTagPathHolder is used keeping the buffer till all processing about the current input file is done. After all processing is done, the buffers - in sourceTagPathHolder are destroied. */ + in sourceTagPathHolder are destroyed. */ stringList * sourceTagPathHolder; inputLineFposMap lineFposMap; + vString *allLines; + int thinDepth; } inputFile; +static inputLangInfo inputLang; static langType sourceLang; /* @@ -125,7 +130,7 @@ static void langStackClear(langStack *langStack); */ static inputFile File; /* static read through functions */ static inputFile BackupFile; /* File is copied here when a nested parser is pushed */ -static MIOPos StartOfLine; /* holds deferred position of start of line */ +static compoundPos StartOfLine; /* holds deferred position of start of line */ /* * FUNCTION DEFINITIONS @@ -145,23 +150,25 @@ extern int getInputLineOffset (void) extern const char *getInputFileName (void) { + if (!File.input.name) + return NULL; return vStringValue (File.input.name); } extern MIOPos getInputFilePosition (void) { - return File.filePosition; + return File.filePosition.pos; } -extern MIOPos getInputFilePositionForLine (int line) +extern MIOPos getInputFilePositionForLine (unsigned int line) { return File.lineFposMap.pos[(((File.lineFposMap.count > (line - 1)) \ - && (line > 0))? (line - 1): 0)]; + && (line > 0))? (line - 1): 0)].pos; } extern langType getInputLanguage (void) { - return langStackTop (&File.input.langInfo.stack); + return langStackTop (&inputLang.stack); } extern const char *getInputLanguageName (void) @@ -189,14 +196,25 @@ extern bool isInputLanguageKindEnabled (int kindIndex) return isLanguageKindEnabled (getInputLanguage (), kindIndex); } -extern bool doesInputLanguageAllowNullTag (void) +extern bool isInputLanguageRoleEnabled (int kindIndex, int roleIndex) { - return doesLanguageAllowNullTag (getInputLanguage ()); + return isLanguageRoleEnabled (getInputLanguage (), + kindIndex, roleIndex); +} + +extern unsigned int countInputLanguageKinds (void) +{ + return countLanguageKinds (getInputLanguage ()); } -extern kindDefinition *getInputLanguageFileKind (void) +extern unsigned int countInputLanguageRoles (int kindIndex) { - return getLanguageFileKind (getInputLanguage ()); + return countLanguageRoles (getInputLanguage (), kindIndex); +} + +extern bool doesInputLanguageAllowNullTag (void) +{ + return doesLanguageAllowNullTag (getInputLanguage ()); } extern bool doesInputLanguageRequestAutomaticFQTag (void) @@ -255,7 +273,7 @@ static void freeLineFposMap (inputLineFposMap *lineFposMap) { if (lineFposMap->pos) { - free (lineFposMap->pos); + eFree (lineFposMap->pos); lineFposMap->pos = NULL; lineFposMap->count = 0; lineFposMap->size = 0; @@ -265,24 +283,65 @@ static void freeLineFposMap (inputLineFposMap *lineFposMap) static void allocLineFposMap (inputLineFposMap *lineFposMap) { #define INITIAL_lineFposMap_LEN 256 - lineFposMap->pos = xCalloc (INITIAL_lineFposMap_LEN, MIOPos); + lineFposMap->pos = xCalloc (INITIAL_lineFposMap_LEN, compoundPos); lineFposMap->size = INITIAL_lineFposMap_LEN; lineFposMap->count = 0; } -static void appendLineFposMap (inputLineFposMap *lineFposMap, MIOPos pos) +static void appendLineFposMap (inputLineFposMap *lineFposMap, compoundPos *pos, + bool crAdjustment) { + int lastCrAdjustment = 0; + if (lineFposMap->size == lineFposMap->count) { lineFposMap->size *= 2; lineFposMap->pos = xRealloc (lineFposMap->pos, lineFposMap->size, - MIOPos); + compoundPos); } - lineFposMap->pos [lineFposMap->count] = pos; + + if (lineFposMap->count != 0) + { + lineFposMap->pos [lineFposMap->count - 1].open = false; + lastCrAdjustment = lineFposMap->pos [lineFposMap->count - 1].crAdjustment; + } + + lineFposMap->pos [lineFposMap->count] = *pos; + lineFposMap->pos [lineFposMap->count].open = true; + lineFposMap->pos [lineFposMap->count].crAdjustment + = lastCrAdjustment + ((crAdjustment)? 1: 0); lineFposMap->count++; } +static int compoundPosForOffset (const void* oft, const void *p) +{ + long offset = *(long *)oft; + const compoundPos *pos = p; + const compoundPos *next = (compoundPos *)(((char *)pos) + sizeof (compoundPos)); + + if (offset < (pos->offset - pos->crAdjustment)) + return -1; + else if (((pos->offset - pos->crAdjustment) <= offset) + && (pos->open + || (offset < (next->offset - next->crAdjustment)))) + return 0; + else + return 1; +} + +extern unsigned long getInputLineNumberForFileOffset(long offset) +{ + compoundPos *p; + + p = bsearch (&offset, File.lineFposMap.pos, File.lineFposMap.count, sizeof (compoundPos), + compoundPosForOffset); + if (p == NULL) + return 1; /* TODO: 0? */ + else + return 1 + (p - File.lineFposMap.pos); +} + /* * Input file access functions */ @@ -306,7 +365,6 @@ static void setOwnerDirectoryOfInputFile (const char *const fileName) static void setInputFileParametersCommon (inputFileInfo *finfo, vString *const fileName, const langType language, - void (* setLang) (inputLangInfo *, langType), stringList *holder) { if (finfo->name != NULL) @@ -320,16 +378,24 @@ static void setInputFileParametersCommon (inputFileInfo *finfo, vString *const f else vStringDelete (finfo->tagPath); } - if (! Option.tagRelative || isAbsolutePath (vStringValue (fileName))) + + if (0) + ; + else if ( Option.tagRelative == TREL_ALWAYS ) + finfo->tagPath = + vStringNewOwn (relativeFilename (vStringValue (fileName), + getTagFileDirectory ())); + else if ( Option.tagRelative == TREL_NEVER ) + finfo->tagPath = + vStringNewOwn (absoluteFilename (vStringValue (fileName))); + else if ( Option.tagRelative == TREL_NO || isAbsolutePath (vStringValue (fileName)) ) finfo->tagPath = vStringNewCopy (fileName); else finfo->tagPath = - vStringNewOwn (relativeFilename (vStringValue (fileName), - getTagFileDirectory ())); + vStringNewOwn (relativeFilename (vStringValue (fileName), + getTagFileDirectory ())); finfo->isHeader = isIncludeFile (vStringValue (fileName)); - - setLang (& (finfo->langInfo), language); } static void resetLangOnStack (inputLangInfo *langInfo, langType lang) @@ -351,32 +417,26 @@ static langType popLangOnStack (inputLangInfo *langInfo) static void clearLangOnStack (inputLangInfo *langInfo) { - return langStackClear (& langInfo->stack); -} - -static void setLangToType (inputLangInfo *langInfo, langType lang) -{ - langInfo->type = lang; + langStackClear (& langInfo->stack); } static void setInputFileParameters (vString *const fileName, const langType language) { setInputFileParametersCommon (&File.input, fileName, - language, pushLangOnStack, - NULL); + language, NULL); + pushLangOnStack(&inputLang, language); } static void setSourceFileParameters (vString *const fileName, const langType language) { setInputFileParametersCommon (&File.source, fileName, - language, setLangToType, - File.sourceTagPathHolder); + language, File.sourceTagPathHolder); sourceLang = language; } static bool setSourceFileName (vString *const fileName) { - const langType language = getFileLanguage (vStringValue (fileName)); + const langType language = getLanguageForFilenameAndContents (vStringValue (fileName)); bool result = false; if (language != LANG_IGNORE) { @@ -505,8 +565,11 @@ static bool parseLineDirective (char *s) /* * Input file I/O operations */ - +#ifdef DEBUG +#define MAX_IN_MEMORY_FILE_SIZE 0 +#else #define MAX_IN_MEMORY_FILE_SIZE (1024*1024) +#endif extern MIO *getMio (const char *const fileName, const char *const openMode, bool memStreamRequired) @@ -541,6 +604,36 @@ extern MIO *getMio (const char *const fileName, const char *const openMode, return mio_new_memory (data, size, eRealloc, eFree); } +/* Return true if utf8 BOM is found */ +static bool checkUTF8BOM (MIO *mio, bool skipIfFound) +{ + bool r = false; + if ((0xEF == mio_getc (mio)) + && (0xBB == mio_getc (mio)) + && (0xBF == mio_getc (mio))) + r = true; + + if (! (r && skipIfFound)) + mio_rewind (mio); + return r; +} + +static void rewindInputFile (inputFile *f) +{ + mio_rewind (f->mio); + if (f->bomFound) + { + int c CTAGS_ATTR_UNUSED; + + c = mio_getc (f->mio); + Assert (c == 0xEF); + c = mio_getc (f->mio); + Assert (c == 0xBB); + c = mio_getc (f->mio); + Assert (c == 0xBF); + } +} + /* This function opens an input file, and resets the line counter. If it * fails, it will display an error message and leave the File.mio set to NULL. */ @@ -565,7 +658,10 @@ extern bool openInputFile (const char *const fileName, const langType language, invalidatePatternCache(); if (File.sourceTagPathHolder == NULL) + { File.sourceTagPathHolder = stringListNew (); + DEFAULT_TRASH_BOX(File.sourceTagPathHolder, stringListDelete); + } stringListClear (File.sourceTagPathHolder); memStreamRequired = doesParserRequireMemoryStream (language); @@ -587,9 +683,13 @@ extern bool openInputFile (const char *const fileName, const langType language, { opened = true; + + File.bomFound = checkUTF8BOM (File.mio, true); + setOwnerDirectoryOfInputFile (fileName); - mio_getpos (File.mio, &StartOfLine); - mio_getpos (File.mio, &File.filePosition); + mio_getpos (File.mio, &StartOfLine.pos); + mio_getpos (File.mio, &File.filePosition.pos); + File.filePosition.offset = StartOfLine.offset = mio_tell (File.mio); File.currentLine = NULL; if (File.line != NULL) @@ -603,9 +703,14 @@ extern bool openInputFile (const char *const fileName, const langType language, File.source.lineNumber = File.source.lineNumberOrigin; allocLineFposMap (&File.lineFposMap); - verbose ("OPENING %s as %s language %sfile\n", fileName, - getLanguageName (language), - File.input.isHeader ? "include " : ""); + File.thinDepth = 0; + verbose ("OPENING%s %s as %s language %sfile [%s%s]\n", + (File.bomFound? "(skipping utf-8 bom)": ""), + fileName, + getLanguageName (language), + File.input.isHeader ? "include " : "", + mio? "reused": "new", + memStreamRequired? ",required": ""); } return opened; } @@ -632,17 +737,20 @@ extern void resetInputFile (const langType language) { Assert (File.mio); - mio_rewind (File.mio); - mio_getpos (File.mio, &StartOfLine); - mio_getpos (File.mio, &File.filePosition); + rewindInputFile (&File); + mio_getpos (File.mio, &StartOfLine.pos); + mio_getpos (File.mio, &File.filePosition.pos); + File.filePosition.offset = StartOfLine.offset = mio_tell (File.mio); File.currentLine = NULL; if (File.line != NULL) vStringClear (File.line); + if (hasLanguageMultilineRegexPatterns (language)) + File.allLines = vStringNew (); - resetLangOnStack (& (File.input.langInfo), language); + resetLangOnStack (& inputLang, language); File.input.lineNumber = File.input.lineNumberOrigin; - setLangToType (& (File.source.langInfo), language); + sourceLang = language; File.source.lineNumber = File.source.lineNumberOrigin; } @@ -650,7 +758,7 @@ extern void closeInputFile (void) { if (File.mio != NULL) { - clearLangOnStack (& (File.input.langInfo)); + clearLangOnStack (& inputLang); /* The line count of the file is 1 too big, since it is one-based * and is incremented upon each newline. @@ -673,12 +781,13 @@ extern void *getInputFileUserData(void) /* Action to take for each encountered input newline. */ -static void fileNewline (void) +static void fileNewline (bool crAdjustment) { File.filePosition = StartOfLine; if (BackupFile.mio == NULL) - appendLineFposMap (&File.lineFposMap, File.filePosition); + appendLineFposMap (&File.lineFposMap, &File.filePosition, + crAdjustment); File.input.lineNumber++; File.source.lineNumber++; @@ -696,61 +805,102 @@ extern void ungetcToInputFile (int c) File.ungetchBuf[File.ungetchIdx++] = c; } -static vString *iFileGetLine (void) +typedef enum eEolType { + eol_eof = 0, + eol_nl, + eol_cr_nl, +} eolType; + +static eolType readLine (vString *const vLine, MIO *const mio) { char *str; size_t size; - bool haveLine; + eolType r = eol_nl; + + vStringClear (vLine); - File.line = vStringNewOrClear (File.line); - str = vStringValue (File.line); - size = vStringSize (File.line); + str = vStringValue (vLine); + size = vStringSize (vLine); for (;;) { bool newLine; bool eof; - mio_gets (File.mio, str, size); - vStringSetLength (File.line); - haveLine = vStringLength (File.line) > 0; - newLine = haveLine && vStringLast (File.line) == '\n'; - eof = mio_eof (File.mio); + if (mio_gets (mio, str, size) == NULL) + { + if (!mio_eof (mio)) + error (FATAL | PERROR, "Failure on attempt to read file"); + } + vStringSetLength (vLine); + newLine = vStringLength (vLine) > 0 && vStringLast (vLine) == '\n'; + eof = mio_eof (mio); + if (eof) + r = eol_eof; /* Turn line breaks into a canonical form. The three commonly * used forms of line breaks are: LF (UNIX/Mac OS X), CR-LF (MS-DOS) and - * CR (Mac OS 9). As CR-only EOL isn't haneled by gets() and Mac OS 9 + * CR (Mac OS 9). As CR-only EOL isn't handled by gets() and Mac OS 9 * is dead, we only handle CR-LF EOLs and convert them into LF. */ - if (newLine && vStringLength (File.line) > 1 && - vStringItem (File.line, vStringLength (File.line) - 2) == '\r') + if (newLine && vStringLength (vLine) > 1 && + vStringItem (vLine, vStringLength (vLine) - 2) == '\r') { - vStringItem (File.line, vStringLength (File.line) - 2) = '\n'; - vStringChop (File.line); + vStringItem (vLine, vStringLength (vLine) - 2) = '\n'; + vStringChop (vLine); + r = eol_cr_nl; } if (newLine || eof) break; - vStringResize (File.line, vStringLength (File.line) * 2); - str = vStringValue (File.line) + vStringLength (File.line); - size = vStringSize (File.line) - vStringLength (File.line); + vStringResize (vLine, vStringLength (vLine) * 2); + str = vStringValue (vLine) + vStringLength (vLine); + size = vStringSize (vLine) - vStringLength (vLine); } + return r; +} - if (haveLine) +static vString *iFileGetLine (void) +{ + eolType eol; + langType lang = getInputLanguage(); + + if (File.line == NULL) + File.line = vStringNew (); + + eol = readLine (File.line, File.mio); + + if (vStringLength (File.line) > 0) { /* Use StartOfLine from previous iFileGetLine() call */ - fileNewline (); + fileNewline (eol == eol_cr_nl); /* Store StartOfLine for the next iFileGetLine() call */ - mio_getpos (File.mio, &StartOfLine); + mio_getpos (File.mio, &StartOfLine.pos); + StartOfLine.offset = mio_tell (File.mio); if (Option.lineDirectives && vStringChar (File.line, 0) == '#') parseLineDirective (vStringValue (File.line) + 1); - matchRegex (File.line, getInputLanguage ()); + matchLanguageRegex (lang, File.line); + + if (File.allLines) + vStringCat (File.allLines, File.line); return File.line; } + else + { + if (File.allLines) + { + matchLanguageMultilineRegex (lang, File.allLines); + matchLanguageMultitableRegex (lang, File.allLines); - return NULL; + /* To limit the execution of multiline/multitable parser(s) only + ONCE, clear File.allLines field. */ + vStringDelete (File.allLines); + File.allLines = NULL; + } + return NULL; + } } /* Do not mix use of readLineFromInputFile () and getcFromInputFile () for the same file. @@ -837,65 +987,18 @@ extern const unsigned char *readLineFromInputFile (void) */ extern char *readLineRaw (vString *const vLine, MIO *const mio) { - char *result = NULL; - - vStringClear (vLine); if (mio == NULL) /* to free memory allocated to buffer */ error (FATAL, "NULL file pointer"); else { - bool reReadLine; - - /* If reading the line places any character other than a null or a - * newline at the last character position in the buffer (one less - * than the buffer size), then we must resize the buffer and - * reattempt to read the line. - */ - do - { - char *const pLastChar = vStringValue (vLine) + vStringSize (vLine) -2; - long startOfLine; - - startOfLine = mio_tell(mio); - reReadLine = false; - *pLastChar = '\0'; - result = mio_gets (mio, vStringValue (vLine), (int) vStringSize (vLine)); - if (result == NULL) - { - if (! mio_eof (mio)) - error (FATAL | PERROR, "Failure on attempt to read file"); - } - else if (*pLastChar != '\0' && - *pLastChar != '\n' && *pLastChar != '\r') - { - /* buffer overflow */ - vStringResize (vLine, vStringSize (vLine) * 2); - mio_seek (mio, startOfLine, SEEK_SET); - reReadLine = true; - } - else - { - char* eol; - vStringLength(vLine) = mio_tell(mio) - startOfLine; - /* canonicalize new line */ - eol = vStringValue (vLine) + vStringLength (vLine) - 1; - if (*eol == '\r') - *eol = '\n'; - else if (vStringLength (vLine) != 1 && *(eol - 1) == '\r' && *eol == '\n') - { - *(eol - 1) = '\n'; - *eol = '\0'; - --vLine->length; - } - } - } while (reReadLine); + readLine (vLine, mio); #ifdef HAVE_ICONV if (isConverting ()) convertString (vLine); #endif } - return result; + return vStringLength (vLine) > 0 ? vStringValue (vLine) : NULL; } /* Places into the line buffer the contents of the line referenced by @@ -909,6 +1012,7 @@ extern char *readLineFromBypass ( mio_getpos (File.mio, &orignalPosition); mio_setpos (File.mio, &location); + mio_clearerr (File.mio); if (pSeekValue != NULL) *pSeekValue = mio_tell (File.mio); result = readLineRaw (vLine, File.mio); @@ -919,161 +1023,27 @@ extern char *readLineFromBypass ( return result; } -/* If a xcmd parser is used, ctags cannot know the location for a tag. - * In the other hand, etags output and cross reference output require the - * line after the location. - * - * readLineFromBypassSlow retrieves the line for (lineNumber and pattern of a tag). - */ - -extern char *readLineFromBypassSlow (vString *const vLine, - unsigned long lineNumber, - const char *pattern, - long *const pSeekValue) -{ - char *result = NULL; - - -/* GEANY DIFF */ -#if 0 - MIOPos originalPosition; - char *line; - size_t len; - long pos; - - regex_t patbuf; - char lastc; - - - /* - * Compile the pattern - */ - { - char *pat; - int errcode; - char errmsg[256]; - - pat = eStrdup (pattern); - pat[strlen(pat) - 1] = '\0'; - errcode = regcomp (&patbuf, pat + 1, 0); - eFree (pat); - - if (errcode != 0) - { - regerror (errcode, &patbuf, errmsg, 256); - error (WARNING, "regcomp %s in readLineFromBypassSlow: %s", pattern, errmsg); - regfree (&patbuf); - return NULL; - } - } - - /* - * Get the line for lineNumber - */ - { - unsigned long n; - - mio_getpos (File.mio, &originalPosition); - mio_rewind (File.mio); - line = NULL; - pos = 0; - for (n = 0; n < lineNumber; n++) - { - pos = mio_tell (File.mio); - line = readLineRaw (vLine, File.mio); - if (line == NULL) - break; - } - if (line == NULL) - goto out; - else - len = strlen(line); - - if (len == 0) - goto out; - - lastc = line[len - 1]; - if (lastc == '\n') - line[len - 1] = '\0'; - } - - /* - * Match - */ - { - regmatch_t pmatch; - int after_newline = 0; - if (regexec (&patbuf, line, 1, &pmatch, 0) == 0) - { - line[len - 1] = lastc; - result = line + pmatch.rm_so; - if (pSeekValue) - { - after_newline = ((lineNumber == 1)? 0: 1); - *pSeekValue = pos + after_newline + pmatch.rm_so; - } - } - } - -out: - regfree (&patbuf); - mio_setpos (File.mio, &originalPosition); -#endif -/* GEANY DIFF END */ - return result; -} - -/* - * Similar to readLineRaw but this doesn't use fgetpos/fsetpos. - * Useful for reading from pipe. - */ -char* readLineRawWithNoSeek (vString* const vline, FILE *const pp) -{ - int c; - bool nlcr; - char *result = NULL; - - vStringClear (vline); - nlcr = false; - - while (1) - { - c = fgetc (pp); - - if (c == EOF) - { - if (! feof (pp)) - error (FATAL | PERROR, "Failure on attempt to read file"); - else - break; - } - - result = vStringValue (vline); - - if (c == '\n' || c == '\r') - nlcr = true; - else if (nlcr) - { - ungetc (c, pp); - break; - } - else - vStringPut (vline, c); - } - - return result; -} - -extern void pushNarrowedInputStream (const langType language, - unsigned long startLine, int startCharOffset, - unsigned long endLine, int endCharOffset, - unsigned long sourceLineOffset) +extern void pushNarrowedInputStream ( + unsigned long startLine, long startCharOffset, + unsigned long endLine, long endCharOffset, + unsigned long sourceLineOffset, + int promise) { long p, q; MIOPos original; MIOPos tmp; MIO *subio; + if (isThinStreamSpec (startLine, startCharOffset, + endLine, endCharOffset, + sourceLineOffset)) + { + File.thinDepth++; + verbose ("push thin stream (%d)\n", File.thinDepth); + return; + } + Assert (File.thinDepth == 0); + original = getInputFilePosition (); tmp = getInputFilePositionForLine (startLine); @@ -1088,12 +1058,23 @@ extern void pushNarrowedInputStream (const langType language, mio_setpos (File.mio, &original); - subio = mio_new_mio (File.mio, p, q - p); + invalidatePatternCache(); + + size_t size = q - p; + subio = mio_new_mio (File.mio, p, size); + if (subio == NULL) + error (FATAL, "memory for mio may be exhausted"); + runModifiers (promise, + startLine, startCharOffset, + endLine, endCharOffset, + mio_memory_get_data (subio, NULL), + size); BackupFile = File; File.mio = subio; + File.bomFound = false; File.nestedInputStreamInfo.startLine = startLine; File.nestedInputStreamInfo.startCharOffset = startCharOffset; File.nestedInputStreamInfo.endLine = endLine; @@ -1103,14 +1084,19 @@ extern void pushNarrowedInputStream (const langType language, File.source.lineNumberOrigin = ((sourceLineOffset == 0)? 0: sourceLineOffset - 1); } +extern bool doesParserRunAsGuest (void) +{ + return !(File.nestedInputStreamInfo.startLine == 0 + && File.nestedInputStreamInfo.startCharOffset == 0 + && File.nestedInputStreamInfo.endLine == 0 + && File.nestedInputStreamInfo.endCharOffset == 0); +} + extern unsigned int getNestedInputBoundaryInfo (unsigned long lineNumber) { unsigned int info; - if (File.nestedInputStreamInfo.startLine == 0 - && File.nestedInputStreamInfo.startCharOffset == 0 - && File.nestedInputStreamInfo.endLine == 0 - && File.nestedInputStreamInfo.endCharOffset == 0) + if (!doesParserRunAsGuest()) /* Not in a nested input stream */ return 0; @@ -1126,6 +1112,12 @@ extern unsigned int getNestedInputBoundaryInfo (unsigned long lineNumber) } extern void popNarrowedInputStream (void) { + if (File.thinDepth) + { + File.thinDepth--; + verbose ("CLEARING thin flag(%d)\n", File.thinDepth); + return; + } mio_free (File.mio); File = BackupFile; memset (&BackupFile, 0, sizeof (BackupFile)); @@ -1133,12 +1125,12 @@ extern void popNarrowedInputStream (void) extern void pushLanguage (const langType language) { - pushLangOnStack (&File.input.langInfo, language); + pushLangOnStack (& inputLang, language); } extern langType popLanguage (void) { - return popLangOnStack (&File.input.langInfo); + return popLangOnStack (& inputLang); } static void langStackInit (langStack *langStack) @@ -1146,6 +1138,7 @@ static void langStackInit (langStack *langStack) langStack->count = 0; langStack->size = 1; langStack->languages = xCalloc (langStack->size, langType); + DEFAULT_TRASH_BOX(&(langStack->languages), eFreeIndirect); } static langType langStackTop (langStack *langStack) @@ -1174,3 +1167,28 @@ static langType langStackPop (langStack *langStack) { return langStack->languages [ -- langStack->count ]; } + +extern bool isThinStreamSpec(unsigned long startLine, long startCharOffset, + unsigned long endLine, long endCharOffset, + unsigned long sourceLineOffset) +{ + return (startLine == 0 && + startCharOffset == 0 && + endLine == 0 && + endCharOffset == 0 && + sourceLineOffset == 0); +} + +#ifdef DO_TRACING +extern bool isTraced (void) +{ + if (File.mio == NULL) + /* A parser is not given. In that case, just check whether --_trace option + is given or not. */ + return isMainTraced (); + else + /* Ap arser is given. In that case, check whether the current parser is + specified in --_trace=,... option */ + return isLanguageTraced (getInputLanguage ()); +} +#endif /* DO_TRACING */ diff --git a/ctags/main/read.h b/ctags/main/read.h index b0ac8e7528..f453bde0c7 100644 --- a/ctags/main/read.h +++ b/ctags/main/read.h @@ -17,7 +17,7 @@ #include #include -#include "parse.h" +#include "types.h" #include "vstring.h" #include "mio.h" @@ -58,21 +58,28 @@ extern unsigned long getInputLineNumber (void); extern int getInputLineOffset (void); extern const char *getInputFileName (void); extern MIOPos getInputFilePosition (void); -extern MIOPos getInputFilePositionForLine (int line); +extern MIOPos getInputFilePositionForLine (unsigned int line); extern langType getInputLanguage (void); extern const char *getInputLanguageName (void); extern const char *getInputFileTagPath (void); extern bool isInputLanguage (langType lang); extern bool isInputHeaderFile (void); extern bool isInputLanguageKindEnabled (int kindIndex); + +extern bool isInputLanguageRoleEnabled (int kindIndex, int roleIndex); +extern unsigned int countInputLanguageKinds (void); +extern unsigned int countInputLanguageRoles (int kindIndex); + extern bool doesInputLanguageAllowNullTag (void); -extern kindDefinition *getInputLanguageFileKind (void); extern bool doesInputLanguageRequestAutomaticFQTag (void); +extern bool doesParserRunAsGuest (void); +extern bool doesSubparserRun (void); +extern bool isParserMarkedNoEmission (void); extern void freeInputFileResources (void); extern const unsigned char *getInputFileData (size_t *size); -/* Stream opend by getMio can be passed to openInputFile as the 3rd +/* Stream opened by getMio can be passed to openInputFile as the 3rd argument. If the 3rd argument is NULL, openInputFile calls getMio internally. The 3rd argument is introduced for reusing mio object created in parser guessing stage. */ @@ -103,22 +110,27 @@ extern const char *getSourceFileTagPath (void); extern langType getSourceLanguage (void); extern unsigned long getSourceLineNumber (void); -/* Raw: reading from given a parameter, fp */ -extern char *readLineRaw (vString *const vLine, MIO *const mio); -extern char* readLineRawWithNoSeek (vString *const vline, FILE *const pp); +/* Raw: reading from given a parameter, mio */ +extern char *readLineRaw (vString *const vLine, MIO *const mio); /* Bypass: reading from fp in inputFile WITHOUT updating fields in input fields */ extern char *readLineFromBypass (vString *const vLine, MIOPos location, long *const pSeekValue); -extern char *readLineFromBypassSlow (vString *const vLine, unsigned long lineNumber, - const char *pattern, long *const pSeekValue); -extern void pushNarrowedInputStream (const langType language, - unsigned long startLine, int startCharOffset, - unsigned long endLine, int endCharOffset, - unsigned long sourceLineOffset); +extern void pushNarrowedInputStream ( + unsigned long startLine, long startCharOffset, + unsigned long endLine, long endCharOffset, + unsigned long sourceLineOffset, + int promise); extern void popNarrowedInputStream (void); extern void pushLanguage(const langType language); extern langType popLanguage (void); +extern unsigned long getInputLineNumberForFileOffset(long offset); + +#define THIN_STREAM_SPEC 0, 0, 0, 0, 0 +extern bool isThinStreamSpec(unsigned long startLine, long startCharOffset, + unsigned long endLine, long endCharOffset, + unsigned long sourceLineOffset); + #endif /* CTAGS_MAIN_READ_H */ diff --git a/ctags/main/repoinfo.h b/ctags/main/repoinfo.h index a097dce5d6..c9e8a94a1f 100644 --- a/ctags/main/repoinfo.h +++ b/ctags/main/repoinfo.h @@ -1 +1 @@ -#define CTAGS_REPOINFO "ff6491f" +#define CTAGS_REPOINFO "ee887eab" diff --git a/ctags/main/routines.c b/ctags/main/routines.c index cf8aefa289..ce5e28b99a 100644 --- a/ctags/main/routines.c +++ b/ctags/main/routines.c @@ -53,14 +53,12 @@ #ifdef HAVE_DIRECT_H # include /* to _getcwd */ #endif -#ifdef HAVE_DIR_H -# include /* to declare findfirst() and findnext() */ -#endif #ifdef HAVE_IO_H # include /* to declare open() */ #endif #include "debug.h" #include "routines.h" +#include "routines_p.h" #ifdef HAVE_ICONV # include "mbcs.h" #endif @@ -68,7 +66,7 @@ # include #endif -#include "options.h" +#include "vstring.h" /* * MACROS @@ -137,7 +135,6 @@ # define stat _stat # define getcwd _getcwd # define currentdrive() (_getdrive() + 'A' - 1) -# define PATH_MAX _MAX_PATH # else # define currentdrive() 'C' # endif @@ -177,6 +174,12 @@ extern int lstat (const char *, struct stat *); # define lstat(fn,buf) stat(fn,buf) #endif +static bool isPathSeparator (const int c); +static char *strSeparator (const char *s); +static char *strRSeparator (const char *s); +static void canonicalizePath (char *const path); + + /* * FUNCTION DEFINITIONS */ @@ -203,6 +206,18 @@ extern const char *getExecutablePath (void) return ExecutableProgram; } +/* + * compare file/dirname characters with platform-correct case sensitivity + */ +static bool fnmChEq (int c1, int c2) +{ +#ifdef WIN32 + return tolower( c1 ) == tolower( c2 ); /* case-insensitive */ +#else + return c1 == c2 ; /* case- sensitive */ +#endif +} + /* * Memory allocation functions */ @@ -247,6 +262,15 @@ extern void eFree (void *const ptr) free (ptr); } +extern void eFreeIndirect(void **ptr) +{ + if (ptr && *ptr) + { + eFree (*ptr); + *ptr = NULL; + } +} + /* * String manipulation functions */ @@ -312,8 +336,8 @@ extern char* eStrdup (const char* str) extern char* eStrndup (const char* str, size_t len) { char* result = xMalloc (len + 1, char); - memset(result, 0, len + 1); strncpy (result, str, len); + result [len] = '\0'; return result; } @@ -413,7 +437,7 @@ extern bool strToInt(const char *const str, int base, int *value) * File system functions */ -extern void setCurrentDirectory (void) +extern void setCurrentDirectory (void) /* TODO */ { char* buf; if (CurrentDirectory == NULL) @@ -421,12 +445,12 @@ extern void setCurrentDirectory (void) buf = getcwd (CurrentDirectory, PATH_MAX); if (buf == NULL) perror (""); - if (CurrentDirectory [strlen (CurrentDirectory) - (size_t) 1] != - PATH_SEPARATOR) + if (! isPathSeparator (CurrentDirectory [strlen (CurrentDirectory) - (size_t) 1])) { sprintf (CurrentDirectory + strlen (CurrentDirectory), "%c", OUTPUT_PATH_SEPARATOR); } + canonicalizePath (CurrentDirectory); } /* For caching of stat() calls */ @@ -489,11 +513,11 @@ extern bool isRecursiveLink (const char* const dirName) if (status->isSymbolicLink) { char* const path = absoluteFilename (dirName); - while (path [strlen (path) - 1] == PATH_SEPARATOR) + while (isPathSeparator (path [strlen (path) - 1])) path [strlen (path) - 1] = '\0'; while (! result && strlen (path) > (size_t) 1) { - char *const separator = strrchr (path, PATH_SEPARATOR); + char *const separator = strRSeparator (path); if (separator == NULL) break; else if (separator == path) /* backed up to root directory */ @@ -522,7 +546,30 @@ static bool isPathSeparator (const int c) return result; } -#if ! defined (HAVE_STAT_ST_INO) +static char *strSeparator (const char *s) +{ +#if defined (MSDOS_STYLE_PATH) + return strpbrk (s, PathDelimiters); +#else + return strchr (s, PATH_SEPARATOR); +#endif +} + +static char *strRSeparator (const char *s) +{ +#if defined (MSDOS_STYLE_PATH) + const char *last = NULL; + + while (( s = strSeparator (s) )) + { + last = s; + s++; + } + return (char*) last; +#else + return strrchr (s, PATH_SEPARATOR); +#endif +} static void canonicalizePath (char *const path CTAGS_ATTR_UNUSED) { @@ -530,12 +577,10 @@ static void canonicalizePath (char *const path CTAGS_ATTR_UNUSED) char *p; for (p = path ; *p != '\0' ; ++p) if (isPathSeparator (*p) && *p != ':') - *p = PATH_SEPARATOR; + *p = OUTPUT_PATH_SEPARATOR; # endif } -#endif - extern bool isSameFile (const char *const name1, const char *const name2) { bool result = false; @@ -555,8 +600,8 @@ extern bool isSameFile (const char *const name1, const char *const name2) # else result = (bool) (strcmp (n1, n2) == 0); # endif - free (n1); - free (n2); + eFree (n1); + eFree (n2); } #endif return result; @@ -594,7 +639,7 @@ extern const char *baseFilename (const char *const filePath) # endif } #else - const char *tail = strrchr (filePath, PATH_SEPARATOR); + const char *tail = strRSeparator (filePath); #endif if (tail == NULL) tail = filePath; @@ -607,7 +652,7 @@ extern const char *baseFilename (const char *const filePath) extern const char *fileExtension (const char *const fileName) { const char *extension; - const char *pDelimiter = NULL; + const char *pDelimiter; const char *const base = baseFilename (fileName); pDelimiter = strrchr (base, '.'); @@ -623,7 +668,7 @@ extern const char *fileExtension (const char *const fileName) extern char* baseFilenameSansExtensionNew (const char *const fileName, const char *const templateExt) { - const char *pDelimiter = NULL; + const char *pDelimiter; const char *const base = baseFilename (fileName); char* shorten_base; @@ -640,7 +685,7 @@ extern char* baseFilenameSansExtensionNew (const char *const fileName, extern bool isAbsolutePath (const char *const path) { - bool result = false; + bool result; #if defined (MSDOS_STYLE_PATH) if (isPathSeparator (path [0])) result = true; @@ -649,13 +694,18 @@ extern bool isAbsolutePath (const char *const path) if (isPathSeparator (path [2])) result = true; else + { + result = false; /* We don't support non-absolute file names with a drive * letter, like `d:NAME' (it's too much hassle). */ error (FATAL, "%s: relative file names with drive letters not supported", path); + } } + else + result = false; #else result = isPathSeparator (path [0]); #endif @@ -721,13 +771,13 @@ extern char* absoluteFilename (const char *file) res = concat (CurrentDirectory, file, ""); /* Delete the "/dirname/.." and "/." substrings. */ - slashp = strchr (res, PATH_SEPARATOR); + slashp = strSeparator (res); while (slashp != NULL && slashp [0] != '\0') { if (slashp[1] == '.') { if (slashp [2] == '.' && - (slashp [3] == PATH_SEPARATOR || slashp [3] == '\0')) + (isPathSeparator (slashp [3]) || slashp [3] == '\0')) { cp = slashp; do @@ -740,24 +790,28 @@ extern char* absoluteFilename (const char *file) * so the luser could say `d:/../NAME'. We silently treat this * as `d:/NAME'. */ - else if (cp [0] != PATH_SEPARATOR) + else if (!isPathSeparator (cp [0])) cp = slashp; #endif memmove (cp, slashp + 3, strlen (slashp + 3) + 1); slashp = cp; continue; } - else if (slashp [2] == PATH_SEPARATOR || slashp [2] == '\0') + else if (isPathSeparator (slashp [2]) || slashp [2] == '\0') { memmove (slashp, slashp + 2, strlen (slashp + 2) + 1); continue; } } - slashp = strchr (slashp + 1, PATH_SEPARATOR); + slashp = strSeparator (slashp + 1); } if (res [0] == '\0') - return eStrdup ("/"); + { + const char root [] = {OUTPUT_PATH_SEPARATOR, '\0'}; + eFree (res); + res = eStrdup (root); + } else { #ifdef MSDOS_STYLE_PATH @@ -765,9 +819,9 @@ extern char* absoluteFilename (const char *file) if (res [1] == ':' && islower (res [0])) res [0] = toupper (res [0]); #endif - - return res; } + canonicalizePath (res); + return res; } /* Return a newly allocated string containing the absolute file name of dir @@ -778,7 +832,7 @@ extern char* absoluteDirname (char *file) { char *slashp, *res; char save; - slashp = strrchr (file, PATH_SEPARATOR); + slashp = strRSeparator (file); if (slashp == NULL) res = eStrdup (CurrentDirectory); else @@ -805,7 +859,7 @@ extern char* relativeFilename (const char *file, const char *dir) absdir = absoluteFilename (file); fp = absdir; dp = dir; - while (*fp++ == *dp++) + while (fnmChEq (*fp++, *dp++)) continue; fp--; dp--; /* back to the first differing char */ @@ -815,21 +869,24 @@ extern char* relativeFilename (const char *file, const char *dir) return absdir; /* first char differs, give up */ fp--; dp--; - } while (*fp != PATH_SEPARATOR); + } while (!isPathSeparator (*fp)); /* Build a sequence of "../" strings for the resulting relative file name. */ i = 0; - while ((dp = strchr (dp + 1, PATH_SEPARATOR)) != NULL) + while ((dp = strSeparator (dp + 1)) != NULL) i += 1; res = xMalloc (3 * i + strlen (fp + 1) + 1, char); res [0] = '\0'; while (i-- > 0) - strcat (res, "../"); + { + const char parent [] = {'.', '.', OUTPUT_PATH_SEPARATOR, '\0'}; + strcat (res, parent); + } /* Add the file name relative to the common root of file and dir. */ strcat (res, fp + 1); - free (absdir); + eFree (absdir); return res; } @@ -874,7 +931,7 @@ extern MIO *tempFile (const char *const mode, char **const pName) fd = open (name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); #endif if (fd == -1) - error (FATAL | PERROR, "cannot open temporary file"); + error (FATAL | PERROR, "cannot open temporary file: %s", name); fp = fdopen (fd, mode); if (fp == NULL) error (FATAL | PERROR, "cannot open temporary file"); diff --git a/ctags/main/routines.h b/ctags/main/routines.h index 72471cdbf9..2be9960b30 100644 --- a/ctags/main/routines.h +++ b/ctags/main/routines.h @@ -16,7 +16,6 @@ #include -#include "mio.h" /* * MACROS @@ -31,69 +30,15 @@ #define STRINGIFY(X) STRINGIFY_(X) #define STRINGIFY_(X) #X -/* - * Portability macros - */ -#ifndef PATH_SEPARATOR -# if defined (MSDOS_STYLE_PATH) -# define PATH_SEPARATOR '\\' -# else -# define PATH_SEPARATOR '/' -# endif -#endif - -#if defined (MSDOS_STYLE_PATH) && defined (UNIX_PATH_SEPARATOR) -# define OUTPUT_PATH_SEPARATOR '/' -#else -# define OUTPUT_PATH_SEPARATOR PATH_SEPARATOR -#endif - /* * DATA DECLARATIONS */ -#if defined (MSDOS_STYLE_PATH) -extern const char *const PathDelimiters; -#endif -extern char *CurrentDirectory; typedef int errorSelection; enum eErrorTypes { FATAL = 1, WARNING = 2, PERROR = 4 }; -typedef struct { - /* Name of file for which status is valid */ - char* name; - - /* Does file exist? If not, members below do not contain valid data. */ - bool exists; - - /* is file path a symbolic link to another file? */ - bool isSymbolicLink; - - /* Is file (pointed to) a directory? */ - bool isDirectory; - - /* Is file (pointed to) a normal file? */ - bool isNormalFile; - - /* Is file (pointed to) executable? */ - bool isExecutable; - - /* Is file (pointed to) setuid? */ - bool isSetuid; - - /* Is file (pointed to) setgid? */ - bool isSetgid; - - /* Size of file (pointed to) */ - unsigned long size; -} fileStatus; - /* * FUNCTION PROTOTYPES */ -extern void freeRoutineResources (void); -extern void setExecutableName (const char *const path); -extern const char *getExecutableName (void); -extern const char *getExecutablePath (void); extern void error (const errorSelection selection, const char *const format, ...) CTAGS_ATTR_PRINTF (2, 3); /* Memory allocation functions */ @@ -105,6 +50,7 @@ extern void *eMalloc (const size_t size); extern void *eCalloc (const size_t count, const size_t size); extern void *eRealloc (void *const ptr, const size_t size); extern void eFree (void *const ptr); +extern void eFreeIndirect(void **ptr); /* String manipulation functions */ extern int struppercmp (const char *s1, const char *s2); @@ -120,27 +66,12 @@ extern void toUpperString (char* str); extern char* newLowerString (const char* str); extern char* newUpperString (const char* str); extern bool strToUInt(const char *const str, int base, unsigned int *value); -extern bool strToULong(const char *string, int base, unsigned long *value); +extern bool strToULong(const char *const string, int base, unsigned long *value); extern bool strToInt(const char *const str, int base, int *value); -extern bool strToLong(const char *string, int base, long *value); +extern bool strToLong(const char *const string, int base, long *value); /* File system functions */ -extern void setCurrentDirectory (void); -extern fileStatus *eStat (const char *const fileName); -extern void eStatFree (fileStatus *status); -extern bool doesFileExist (const char *const fileName); -extern bool doesExecutableExist (const char *const fileName); -extern bool isRecursiveLink (const char* const dirName); -extern bool isSameFile (const char *const name1, const char *const name2); extern const char *baseFilename (const char *const filePath); extern const char *fileExtension (const char *const fileName); -extern bool isAbsolutePath (const char *const path); -extern char *combinePathAndFile (const char *const path, const char *const file); -extern char* absoluteFilename (const char *file); -extern char* absoluteDirname (char *file); -extern char* relativeFilename (const char *file, const char *dir); -extern MIO *tempFile (const char *const mode, char **const pName); - -extern char* baseFilenameSansExtensionNew (const char *const fileName, const char *const templateExt); #endif /* CTAGS_MAIN_ROUTINES_H */ diff --git a/ctags/main/routines_p.h b/ctags/main/routines_p.h new file mode 100644 index 0000000000..6187459382 --- /dev/null +++ b/ctags/main/routines_p.h @@ -0,0 +1,107 @@ +/* +* Copyright (c) 2002, Darren Hiebert +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* Main part private interface to routines.c +*/ +#ifndef CTAGS_MAIN_ROUTINES_PRIVATE_H +#define CTAGS_MAIN_ROUTINES_PRIVATE_H + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ +#include "mio.h" + +/* + * Portability macros + */ +#ifndef PATH_SEPARATOR +# if defined (MSDOS_STYLE_PATH) +# define PATH_SEPARATOR '\\' +# else +# define PATH_SEPARATOR '/' +# endif +#endif + +#if defined (MSDOS_STYLE_PATH) && defined (UNIX_PATH_SEPARATOR) +# define OUTPUT_PATH_SEPARATOR '/' +#else +# define OUTPUT_PATH_SEPARATOR PATH_SEPARATOR +#endif + +/* +* DATA DECLARATIONS +*/ +extern char *CurrentDirectory; +#if defined (MSDOS_STYLE_PATH) +extern const char *const PathDelimiters; +#endif + +typedef struct { + /* Name of file for which status is valid */ + char* name; + + /* Does file exist? If not, members below do not contain valid data. */ + bool exists; + + /* is file path a symbolic link to another file? */ + bool isSymbolicLink; + + /* Is file (pointed to) a directory? */ + bool isDirectory; + + /* Is file (pointed to) a normal file? */ + bool isNormalFile; + + /* Is file (pointed to) executable? */ + bool isExecutable; + + /* Is file (pointed to) setuid? */ + bool isSetuid; + + /* Is file (pointed to) setgid? */ + bool isSetgid; + + /* Size of file (pointed to) */ + unsigned long size; +} fileStatus; + +/* +* FUNCTION PROTOTYPES +*/ +extern void freeRoutineResources (void); +extern void setExecutableName (const char *const path); + +/* File system functions */ +extern const char *getExecutableName (void); +extern const char *getExecutablePath (void); +extern void setCurrentDirectory (void); +extern fileStatus *eStat (const char *const fileName); +extern void eStatFree (fileStatus *status); +extern bool doesFileExist (const char *const fileName); +extern bool doesExecutableExist (const char *const fileName); +extern bool isRecursiveLink (const char* const dirName); +extern bool isSameFile (const char *const name1, const char *const name2); +extern bool isAbsolutePath (const char *const path); +extern char *combinePathAndFile (const char *const path, const char *const file); +extern char* absoluteFilename (const char *file); +extern char* absoluteDirname (char *file); +extern char* relativeFilename (const char *file, const char *dir); +extern MIO *tempFile (const char *const mode, char **const pName); + +extern char* baseFilenameSansExtensionNew (const char *const fileName, const char *const templateExt); + +/* GEANY DIFF */ +/* +#include "portable-dirent_p.h" + +extern int scanDirectory (const char *directory_name, + struct dirent ***array_pointer, int (*select_function) (const struct dirent *), + int (*compare_function) (const struct dirent **, const struct dirent **)); +*/ +/* GEANY DIFF END */ + +#endif /* CTAGS_MAIN_ROUTINES_PRIVATE_H */ diff --git a/ctags/main/selectors.c b/ctags/main/selectors.c index e4b7cf4c4d..cc59914703 100644 --- a/ctags/main/selectors.c +++ b/ctags/main/selectors.c @@ -14,7 +14,7 @@ #include #include "debug.h" -#include "parse.h" +#include "parse_p.h" #include "options.h" #include "selectors.h" #include "vstring.h" @@ -119,7 +119,9 @@ tastePerlLine (const char *line, void *data CTAGS_ATTR_UNUSED) } const char * -selectByPickingPerlVersion (MIO *input) +selectByPickingPerlVersion (MIO *input, + langType *candidates CTAGS_ATTR_UNUSED, + unsigned int nCandidates CTAGS_ATTR_UNUSED) { /* Default to Perl 5 */ return selectByLines (input, tastePerlLine, TR_PERL5, NULL); @@ -160,7 +162,9 @@ tasteObjectiveCOrMatLabLines (const char *line, void *data CTAGS_ATTR_UNUSED) } const char * -selectByObjectiveCAndMatLabKeywords (MIO * input) +selectByObjectiveCAndMatLabKeywords (MIO * input, + langType *candidates CTAGS_ATTR_UNUSED, + unsigned int nCandidates CTAGS_ATTR_UNUSED) { return selectByLines (input, tasteObjectiveCOrMatLabLines, NULL, NULL); @@ -178,7 +182,9 @@ tasteObjectiveC (const char *line, void *data CTAGS_ATTR_UNUSED) } const char * -selectByObjectiveCKeywords (MIO * input) +selectByObjectiveCKeywords (MIO * input, + langType *candidates CTAGS_ATTR_UNUSED, + unsigned int nCandidates CTAGS_ATTR_UNUSED) { /* TODO: Ideally opening input should be delayed til enable/disable based selection is done. */ @@ -218,7 +224,9 @@ tasteR (const char *line, void *data CTAGS_ATTR_UNUSED) } const char * -selectByArrowOfR (MIO *input) +selectByArrowOfR (MIO *input, + langType *candidates CTAGS_ATTR_UNUSED, + unsigned int nCandidates CTAGS_ATTR_UNUSED) { /* TODO: Ideally opening input should be delayed till enable/disable based selection is done. */ @@ -264,7 +272,9 @@ tasteREXXOrDosBatch (const char *line, void *data) } const char * -selectByRexxCommentAndDosbatchLabelPrefix (MIO *input) +selectByRexxCommentAndDosbatchLabelPrefix (MIO *input, + langType *candidates CTAGS_ATTR_UNUSED, + unsigned int nCandidates CTAGS_ATTR_UNUSED) { /* TODO: Ideally opening input should be delayed till enable/disable based selection is done. */ @@ -314,53 +324,145 @@ xmlParseMIO (MIO *input) return xmlParseMemory((const char *)buf, len); } -static const char * -selectParserForXmlDoc (xmlDocPtr doc) +static bool +matchXpathFileSpec (xmlDocPtr doc, xpathFileSpec *spec) { - if (doc && doc->children && doc->children->name) - verbose (" Xml[root name]: %s\n", doc->children->name); - if (doc && doc->intSubset && doc->intSubset->ExternalID) - verbose (" Xml[ExternalID]: %s\n", doc->intSubset->ExternalID); - if (doc && doc->intSubset && doc->intSubset->SystemID) - verbose (" Xml[SystemID]: %s\n", doc->intSubset->SystemID); - if (doc && doc->children && doc->children->ns && doc->children->ns->href) - verbose (" Xml[NS]: %s\n", doc->children->ns->href); - - /* These conditions should be part of parsers. */ - if (doc->children - && doc->intSubset - && doc->children->name && doc->intSubset->name - && (strcmp ((const char *)doc->children->name, (const char *)doc->intSubset->name) == 0) - && doc->intSubset->ExternalID - && (strcmp ((const char *)doc->intSubset->ExternalID, - "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN") == 0) - && doc->intSubset->SystemID - && (strcmp ((const char *)doc->intSubset->SystemID, - "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd") == 0)) + if (spec->rootElementName) + { + if (*spec->rootElementName == '\0') + { + /* The statement is just for keeping code symmetric. + Meaningless examination: a root element is + always there.*/ + if (doc->children && doc->children->name) + return false; + } + else if (! (doc->children + && doc->children->name + && (strcmp (spec->rootElementName, (char *)doc->children->name) == 0))) + return false; + } + + if (spec->nameInDTD) { - /* - rootElementName == '\0') + { + if (doc->intSubset && doc->intSubset->name) + return false; + } + else if (! (doc->intSubset + && doc->intSubset->name + && (strcmp (spec->nameInDTD, (char *)doc->intSubset->name) == 0))) + return false; } - else if (doc->children - && doc->children->name - && (strcmp ((const char*)doc->children->name, "project") == 0)) + + if (spec->externalID) + { + if (*spec->externalID == '\0') + { + if (doc->intSubset && doc->intSubset->ExternalID) + return false; + } + else if (! (doc->intSubset + && doc->intSubset->ExternalID + && (strcmp (spec->externalID, (char *)doc->intSubset->ExternalID) == 0))) + return false; + } + + if (spec->systemID) + { + if (*spec->systemID == '\0') + { + if (doc->intSubset && doc->intSubset->SystemID) + return false; + } + else if (! (doc->intSubset + && doc->intSubset->SystemID + && (strcmp (spec->systemID, (char *)doc->intSubset->SystemID) == 0))) + return false; + } + + if (spec->rootNSPrefix) + { + if (*spec->rootNSPrefix == '\0') + { + if (doc->children && doc->children->ns && doc->children->ns->prefix) + return false; + } + else if (! (doc->children + && doc->children->ns + && doc->children->ns->prefix + && (strcmp (spec->rootNSPrefix, (char *)doc->children->ns->prefix)))) + return false; + } + + if (spec->rootNSHref) + { + if (*spec->rootNSHref == '\0') + { + if (doc->children && doc->children->ns && doc->children->ns->href) + return false; + } + else if (! (doc->children + && doc->children->ns + && doc->children->ns->href + && (strcmp (spec->rootNSHref, (char *)doc->children->ns->href) == 0))) + return false; + } + return true; +} + +static const char * +selectParserForXmlDoc (xmlDocPtr doc, + langType *candidates, + unsigned int nCandidates) +{ + + unsigned int lang_index; + + verbose (" Xml[rootElementName]: %s\n", + (doc->children && doc->children->name) + ? ((char *)doc->children->name): "-"); + verbose (" Xml[nameInDTD]: %s\n", + (doc->intSubset && doc->intSubset->name) + ? ((char *)doc->intSubset->name): "-"); + verbose (" Xml[externalID]: %s\n", + (doc->intSubset && doc->intSubset->ExternalID) + ? ((char *)doc->intSubset->ExternalID): "-"); + verbose (" Xml[systemID]: %s\n", + (doc->intSubset && doc->intSubset->SystemID) + ? ((char *)doc->intSubset->SystemID): "-"); + verbose (" Xml[rootNSPrefix]: %s\n", + (doc->children && doc->children->ns && doc->children->ns->prefix) + ? ((char *)doc->children->ns->prefix): "-"); + verbose (" Xml[rootNSHref]: %s\n", + (doc->children && doc->children->ns && doc->children->ns->href) + ? ((char *)doc->children->ns->href): "-"); + + for (lang_index = 0; lang_index < nCandidates; lang_index++) { - if ((doc->children->ns != NULL) - && doc->children->ns->href != NULL - && (strcmp ((const char *)doc->children->ns->href, - "http://maven.apache.org/POM/4.0.0") == 0)) - return "Maven2"; - else - return "Ant"; + unsigned int spec_index; + xpathFileSpec* spec; + unsigned int spec_count; + + verbose (" lxpath examines %s\n", getLanguageName (candidates[lang_index])); + + spec_count = getXpathFileSpecCount (candidates[lang_index]); + for (spec_index = 0; spec_index < spec_count; spec_index++) + { + spec = getXpathFileSpec (candidates[lang_index], spec_index); + if (matchXpathFileSpec (doc, spec)) + return getLanguageName (candidates[lang_index]); + } } return NULL; } const char * -selectByDTD (MIO *input) +selectByXpathFileSpec (MIO *input, + langType *candidates, + unsigned int nCandidates) { xmlDocPtr doc; const char *r = NULL; @@ -369,7 +471,7 @@ selectByDTD (MIO *input) if (doc == NULL) return NULL; - r = selectParserForXmlDoc (doc); + r = selectParserForXmlDoc (doc, candidates, nCandidates); if (r == NULL) xmlFreeDoc (doc); diff --git a/ctags/main/selectors.h b/ctags/main/selectors.h index 3d2b28c242..41132aa4c9 100644 --- a/ctags/main/selectors.h +++ b/ctags/main/selectors.h @@ -10,26 +10,26 @@ #ifndef CTAGS_MAIN_SELECTORS_H #define CTAGS_MAIN_SELECTORS_H -#include +#include "types.h" const char * -selectByPickingPerlVersion (MIO *); +selectByPickingPerlVersion (MIO *, langType *, unsigned int); const char * -selectByObjectiveCAndMatLabKeywords (MIO *); +selectByObjectiveCAndMatLabKeywords (MIO *, langType *, unsigned int); const char * -selectByObjectiveCKeywords(MIO *); +selectByObjectiveCKeywords(MIO *, langType *, unsigned int); const char * -selectByArrowOfR (MIO *); +selectByArrowOfR (MIO *, langType *, unsigned int); const char * -selectByRexxCommentAndDosbatchLabelPrefix (MIO *); +selectByRexxCommentAndDosbatchLabelPrefix (MIO *, langType *, unsigned int); #ifdef HAVE_LIBXML const char * -selectByDTD (MIO *input); +selectByXpathFileSpec (MIO *input, langType *candidates, unsigned int nCandidates); #endif #endif diff --git a/ctags/main/sort.c b/ctags/main/sort.c index 7ee7ce0a23..775dd9a04f 100644 --- a/ctags/main/sort.c +++ b/ctags/main/sort.c @@ -25,8 +25,8 @@ #include #include "debug.h" -#include "entry.h" -#include "options.h" +#include "entry_p.h" +#include "options_p.h" #include "read.h" #include "routines.h" #include "sort.h" @@ -55,17 +55,46 @@ extern void catFile (MIO *mio) # define PE_CONST const #endif +/* + Output file name should not be evaluated in system(3) function. + The name must be used as is. Quotations are required to block the + evaluation. + + Normal single-quotes are used to quote a cstring: + a => 'a' + " => '"' + + If a single-quote is included in the cstring, use double quotes for quoting it. + ' => ''"'"'' +*/ +static void appendCstringWithQuotes (vString *dest, const char* cstr) +{ +#ifdef WIN32 + vStringCatS (dest, cstr); +#else + vStringPut (dest, '\''); + for (const char *o = cstr; *o; o++) + { + if (*o == '\'') + vStringCatS (dest, "'\"'\"'"); + else + vStringPut (dest, *o); + } + vStringPut (dest, '\''); +#endif +} + extern void externalSortTags (const bool toStdout, MIO *tagFile) { const char *const sortNormalCommand = "sort -u"; const char *const sortFoldedCommand = "sort -u -f"; const char *sortCommand = Option.sorted == SO_FOLDSORTED ? sortFoldedCommand : sortNormalCommand; +# ifndef HAVE_SETENV PE_CONST char *const sortOrder1 = "LC_COLLATE=C"; PE_CONST char *const sortOrder2 = "LC_ALL=C"; - const size_t length = 4 + strlen (sortOrder1) + strlen (sortOrder2) + - strlen (sortCommand) + (2 * strlen (tagFileName ())); - char *const cmd = (char *) malloc (length + 1); +# endif + vString *cmd = vStringNew (); int ret = -1; if (cmd != NULL) @@ -80,19 +109,29 @@ extern void externalSortTags (const bool toStdout, MIO *tagFile) putenv (sortOrder1); putenv (sortOrder2); # endif - if (toStdout) - sprintf (cmd, "%s", sortCommand); - else - sprintf (cmd, "%s -o %s %s", sortCommand, - tagFileName (), tagFileName ()); + vStringCatS (cmd, sortCommand); + if (! toStdout) + { + vStringCatS (cmd, " -o "); + appendCstringWithQuotes (cmd, tagFileName ()); + vStringPut (cmd, ' '); + appendCstringWithQuotes (cmd, tagFileName ()); + } #else - if (toStdout) - sprintf (cmd, "%s %s %s", sortOrder1, sortOrder2, sortCommand); - else - sprintf (cmd, "%s %s %s -o %s %s", sortOrder1, sortOrder2, - sortCommand, tagFileName (), tagFileName ()); + vStringCatS (cmd, sortOrder1); + vStringPut (cmd, ' '); + vStringCatS (cmd, sortOrder2); + vStringPut (cmd, ' '); + vStringCatS (cmd, sortCommand); + if (! toStdout) + { + vStringCats (cmd, " -o "); + appendCstringWithQuotes (cmd, tagFileName ()); + vStringPut (cmd, ' '); + appendCstringWithQuotes (cmd, tagFileName ()); + } #endif - verbose ("system (\"%s\")\n", cmd); + verbose ("system (\"%s\")\n", vStringValue (cmd)); if (toStdout) { const int fdstdin = 0; @@ -105,15 +144,14 @@ extern void externalSortTags (const bool toStdout, MIO *tagFile) error (FATAL | PERROR, "cannot redirect stdin"); if (lseek (fdstdin, 0, SEEK_SET) != 0) error (FATAL | PERROR, "cannot rewind tag file"); - ret = system (cmd); + ret = system (vStringValue (cmd)); if (dup2 (fdsave, fdstdin) < 0) error (FATAL | PERROR, "cannot restore stdin fd"); close (fdsave); } else - ret = system (cmd); - free (cmd); - + ret = system (vStringValue (cmd)); + vStringDelete (cmd); } if (ret != 0) error (FATAL | PERROR, "cannot sort tag file"); @@ -155,7 +193,7 @@ static int compareTags (const void *const one, const void *const two) } static void writeSortedTags ( - char **const table, const size_t numTags, const bool toStdout) + char **const table, const size_t numTags, const bool toStdout, bool newlineReplaced) { MIO *mio; size_t i; @@ -176,8 +214,12 @@ static void writeSortedTags ( * pattern) if this is not an xref file. */ if (i == 0 || Option.xref || strcmp (table [i], table [i-1]) != 0) + { if (mio_puts (mio, table [i]) == EOF) failedSort (mio, NULL); + else if (newlineReplaced) + mio_putc (mio, '\n'); + } } if (toStdout) mio_flush (mio); @@ -190,11 +232,12 @@ extern void internalSortTags (const bool toStdout, MIO* mio, size_t numTags) const char *line; size_t i; int (*cmpFunc)(const void *, const void *); + bool newlineReplaced = false; /* Allocate a table of line pointers to be sorted. */ const size_t tableSize = numTags * sizeof (char *); - char **const table = (char **) malloc (tableSize); /* line pointers */ + char **table = (char **) malloc (tableSize); /* line pointers */ DebugStatement ( size_t mallocSize = tableSize; ) /* cumulative total */ @@ -222,6 +265,11 @@ extern void internalSortTags (const bool toStdout, MIO* mio, size_t numTags) failedSort (mio, "out of memory"); DebugStatement ( mallocSize += stringSize; ) strcpy (table [i], line); + if (table[i][stringSize - 2] == '\n') + { + table[i][stringSize - 2] = '\0'; + newlineReplaced = true; + } ++i; } } @@ -232,7 +280,7 @@ extern void internalSortTags (const bool toStdout, MIO* mio, size_t numTags) */ qsort (table, numTags, sizeof (*table), cmpFunc); - writeSortedTags (table, numTags, toStdout); + writeSortedTags (table, numTags, toStdout, newlineReplaced); PrintStatus (("sort memory: %ld bytes\n", (long) mallocSize)); for (i = 0 ; i < numTags ; ++i) diff --git a/ctags/main/strlist.c b/ctags/main/strlist.c index bf1ee1bc3f..4ef8f8a3ef 100644 --- a/ctags/main/strlist.c +++ b/ctags/main/strlist.c @@ -13,9 +13,11 @@ #include "general.h" /* must always come first */ #include -/* GEANY DIFF +/* GEANY DIFF */ +/* #include - * GEANY DIFF END */ +*/ +/* GEANY DIFF END */ #include "debug.h" #include "read.h" @@ -109,13 +111,13 @@ extern void stringListDelete (stringList *const current) static bool compareString ( const char *const string, vString *const itm) { - return (bool) (strcmp (string, vStringValue (itm)) == 0); + return (strcmp (string, vStringValue (itm)) == 0); } static bool compareStringInsensitive ( const char *const string, vString *const itm) { - return (bool) (strcasecmp (string, vStringValue (itm)) == 0); + return (strcasecmp (string, vStringValue (itm)) == 0); } static int stringListIndex ( @@ -137,7 +139,7 @@ static int stringListIndex ( extern bool stringListHas ( const stringList *const current, const char *const string) { - bool result = false; + bool result; Assert (current != NULL); result = stringListIndex (current, string, compareString) != -1; return result; @@ -162,7 +164,7 @@ static vString* stringListFinds ( extern bool stringListHasInsensitive ( const stringList *const current, const char *const string) { - bool result = false; + bool result; Assert (current != NULL); Assert (string != NULL); result = stringListIndex (current, string, compareStringInsensitive) != -1; @@ -218,8 +220,6 @@ static bool fileNameMatched ( const vString* const vpattern, const char* const fileName) { /* GEANY DIFF */ -/* const char* const pattern = vStringValue (vpattern); - return (bool) (fnmatch (pattern, fileName, 0) == 0); */ return false; /* GEANY DIFF END */ } @@ -236,11 +236,24 @@ extern vString* stringListFileFinds ( vString* vstr = NULL; bool matched = false; unsigned int i; + const char * normalized = fileName; + +#if defined (WIN32) + vString *tmp = vStringNewInit (fileName); + vStringTranslate (tmp, '\\', '/'); + normalized = vStringValue (tmp); +#endif + for (i = 0 ; ! matched && i < stringListCount (current) ; ++i) { vstr = stringListItem (current, i); - matched = fileNameMatched (vstr, fileName); + matched = fileNameMatched (vstr, normalized); } + +#if defined (WIN32) && defined (UNIX_PATH_SEPARATOR) + vStringDelete (tmp); +#endif + return matched? vstr: NULL; } @@ -256,3 +269,58 @@ extern void stringListReverse (const stringList *const current) { ptrArrayReverse (current); } + +extern stringList *stringListNewBySplittingWordIntoSubwords (const char* originalWord) +{ + stringList *list = stringListNew (); + + vString *subword = vStringNew(); + for (const char *cursor = originalWord; *cursor != '\0'; cursor++) + { + if (islower(*cursor)) + { + if (vStringLength (subword) > 1 && + isupper(vStringLast(subword)) && + isupper(vStringItem(subword, vStringLength (subword) - 2))) + { + /* ABC + d => AB, Cd */ + char last = vStringLast(subword); + vStringTruncate (subword, vStringLength (subword) - 1); + stringListAdd (list, subword); + subword = vStringNew(); + vStringPut(subword, last); + } + /* A + b => Ab, + a + b => ab */ + vStringPut(subword, *cursor); + } + else if (isupper(*cursor)) + { + /* a + B => a, B */ + if (vStringLength (subword) > 0 && islower(vStringLast(subword))) + { + stringListAdd (list, subword); + subword = vStringNew(); + } + /* A + B => AB */ + vStringPut(subword, *cursor); + } + else if (isdigit(*cursor)) + vStringPut(subword, *cursor); + else + { + if (vStringLength(subword) > 0) + { + stringListAdd (list, subword); + subword = vStringNew(); + } + } + } + + if (vStringLength(subword) > 0) + stringListAdd (list, subword); + else + vStringDelete(subword); + + return list; +} diff --git a/ctags/main/strlist.h b/ctags/main/strlist.h index d23a63c22f..e827a483d2 100644 --- a/ctags/main/strlist.h +++ b/ctags/main/strlist.h @@ -51,4 +51,6 @@ extern vString* stringListFileFinds (const stringList* const list, const char* c extern void stringListPrint (const stringList *const current, FILE *fp); extern void stringListReverse (const stringList *const current); +extern stringList *stringListNewBySplittingWordIntoSubwords (const char* originalWord); + #endif /* CTAGS_MAIN_STRLIST_H */ diff --git a/ctags/main/subparser.h b/ctags/main/subparser.h new file mode 100644 index 0000000000..6200f063e0 --- /dev/null +++ b/ctags/main/subparser.h @@ -0,0 +1,81 @@ +/* + * + * Copyright (c) 2017, Red Hat, Inc. + * Copyright (c) 2017, Masatake YAMATO + * + * Author: Masatake YAMATO + * + * This source code is released for free distribution under the terms of the + * GNU General Public License version 2 or (at your option) any later version. + * + */ +#ifndef CTAGS_MAIN_SUBPARSER_H +#define CTAGS_MAIN_SUBPARSER_H + +#include "general.h" + +#include "colprint_p.h" +#include "dependency.h" +#include "types.h" + + +typedef enum eSubparserRunDirection { + SUBPARSER_UNKNOWN_DIRECTION = 0, + SUBPARSER_BASE_RUNS_SUB = 1 << 0, + SUBPARSER_SUB_RUNS_BASE = 1 << 1, + SUBPARSER_BI_DIRECTION = SUBPARSER_BASE_RUNS_SUB|SUBPARSER_SUB_RUNS_BASE, +} subparserRunDirection; + +struct sSubparser { + /* private in the main part */ + slaveParser *slaveParser; + subparser *next; + bool schedulingBaseparserExplicitly; + bool chosenAsExclusiveSubparser; + + /* public to the parser */ + subparserRunDirection direction; + + void (* inputStart) (subparser *s); + void (* inputEnd) (subparser *s); + void (* exclusiveSubparserChosenNotify) (subparser *s, void *data); + void (* makeTagEntryNotify) (subparser *s, const tagEntryInfo *tag, int corkIndex); +}; + +/* A base parser doesn't have to call the following three functions. + The main part calls them internally. */ +extern void notifyInputStart (void); +extern void notifyInputEnd (void); +extern void notifyMakeTagEntry (const tagEntryInfo *info, int corkIndex); + +extern langType getSubparserLanguage (subparser *s); + +/* Interface for Baseparser */ +extern subparser *getNextSubparser(subparser *last, bool includingNoneCraftedParser); +#define foreachSubparser(VAR, INCLUDING_NONE_CRAFTED_PARSER)\ + VAR = NULL; \ + while ((VAR = getNextSubparser (VAR, INCLUDING_NONE_CRAFTED_PARSER)) != NULL) + +extern void enterSubparser(subparser *subparser); +extern void leaveSubparser(void); + +extern subparser* getSubparserRunningBaseparser (void); +extern void chooseExclusiveSubparser (subparser *s, void *data); + +/* Interface for Subparsers */ +#define RUN_DEFAULT_SUBPARSERS -1 +extern void scheduleRunningBaseparser (int dependencyIndex); + +extern subparser *getFirstSubparser(struct slaveControlBlock *controlBlock); +extern void useDefaultSubparsers (struct slaveControlBlock *controlBlock); +extern void useSpecifiedSubparser (struct slaveControlBlock *controlBlock, subparser *s); +extern void setupSubparsersInUse (struct slaveControlBlock *controlBlock); +extern subparser* teardownSubparsersInUse (struct slaveControlBlock *controlBlock); + +extern struct colprintTable * subparserColprintTableNew (void); +extern void subparserColprintAddSubparsers (struct colprintTable *table, + struct slaveControlBlock *scb); +extern void subparserColprintTablePrint (struct colprintTable *table, + bool withListHeader, bool machinable, FILE *fp); + +#endif /* CTAGS_MAIN_SUBPARSER_H */ diff --git a/ctags/main/trace.h b/ctags/main/trace.h index cb8848cc1b..f5f055c165 100644 --- a/ctags/main/trace.h +++ b/ctags/main/trace.h @@ -1 +1,100 @@ -/* Dummy header - only included by some parsers */ +#ifndef ctags_trace_h_ +#define ctags_trace_h_ +/* +* Copyright (c) 2016, Szymon Tomasz Stefanek +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* Tracing facility. +*/ + +#include "general.h" +#include "debug.h" + +// +// Master tracing switch. +// +// Uncomment this to enable extensive debugging to stderr in code. +// Use only for development as tracing reduces performance. +// +// "./configure --enable-debugging" defines DEBUG. +// When running ctags, pass --_trace=[,]* option. +// +#ifdef DEBUG +#define DO_TRACING +#endif + +#ifdef DO_TRACING + + bool isTraced (void); + void traceLanguage (langType language); + bool isLanguageTraced (langType language); + + void traceEnter(const char * szFunction,const char * szFormat,...); + void traceLeave(const char * szFunction,const char * szFormat,...); + void tracePrint(const char * szFunction,const char * szFormat,...); + + void tracePrintPrefix(const char * szFunction); + void tracePrintFmt(const char * szFormat,...); + void tracePrintNewline(void); + + #define TRACE_ENTER() traceEnter(__func__,"") + #define TRACE_LEAVE() traceLeave(__func__,"") + + #define TRACE_ENTER_TEXT(_szFormat,...) \ + traceEnter(__func__,_szFormat,## __VA_ARGS__) + + #define TRACE_LEAVE_TEXT(_szFormat,...) \ + traceLeave(__func__,_szFormat,## __VA_ARGS__) + + #define TRACE_PRINT(_szFormat,...) \ + tracePrint(__func__,_szFormat,## __VA_ARGS__) + + /* TRACE_PRINT prints line at once. + * If you want to print a trace line incrementally, + * use following macros. + * + * TRACE_PRINT_PREFIX: just print a trace prefix. No newline. + * TRACE_PRINT_FMT: print as a format. No prefix, no newline. + * TRACE_PRINT_NEWLINE: just print a newline. + */ + #define TRACE_PRINT_PREFIX() \ + tracePrintPrefix(__func__) + #define TRACE_PRINT_FMT(_szFormat,...) \ + tracePrintFmt(_szFormat,## __VA_ARGS__) + #define TRACE_PRINT_NEWLINE() \ + tracePrintNewline() + + #define TRACE_ASSERT(_condition,_szFormat,...) \ + do { \ + if(!(_condition)) \ + { \ + tracePrint(__func__,_szFormat,## __VA_ARGS__); \ + Assert(false); \ + } \ + } while(0) + + void traceMain(void); + bool isMainTraced(void); + +#else //!DO_TRACING + + #define TRACE_ENTER() do { } while(0) + #define TRACE_LEAVE() do { } while(0) + + #define TRACE_ENTER_TEXT(_szFormat,...) do { } while(0) + #define TRACE_LEAVE_TEXT(_szFormat,...) do { } while(0) + + #define TRACE_PRINT(_szFormat,...) do { } while(0) + + #define TRACE_PRINT_PREFIX() do { } while(0) + #define TRACE_PRINT_FMT(_szFormat,...) do { } while(0) + #define TRACE_PRINT_NEWLINE() do { } while(0) + + #define TRACE_ASSERT(_condition,_szFormat,...) do { } while(0) + +#endif //!DO_TRACING + + +#endif //!ctags_trace_h_ diff --git a/ctags/main/types.h b/ctags/main/types.h index 7a4af2a1c4..3b61eac5c9 100644 --- a/ctags/main/types.h +++ b/ctags/main/types.h @@ -15,18 +15,42 @@ typedef int langType; struct sTagEntryInfo; typedef struct sTagEntryInfo tagEntryInfo; -struct sFieldDesc; -typedef struct sFieldDesc fieldDesc; - struct sPtagDesc; typedef struct sPtagDesc ptagDesc; +struct sRoleDefinition; +typedef struct sRoleDefinition roleDefinition; + struct sKindDefinition; typedef struct sKindDefinition kindDefinition; struct sParserDefinition; typedef struct sParserDefinition parserDefinition; +struct _MIO; +typedef const char * (*selectLanguage) (struct _MIO *, langType *, unsigned int); + +struct sSlaveParser; +typedef struct sSlaveParser slaveParser; + +struct sSubparser; +typedef struct sSubparser subparser; + +struct sParserDependency; +typedef struct sParserDependency parserDependency; + +struct sFieldDefinition; +typedef struct sFieldDefinition fieldDefinition; + +struct sXtagDefinition; +typedef struct sXtagDefinition xtagDefinition; + +struct sParameterHandlerTable; +typedef struct sParameterHandlerTable parameterHandlerTable; + +struct NestingLevel; +typedef struct NestingLevel NestingLevel; + struct sPtrArray; typedef struct sPtrArray ptrArray; diff --git a/ctags/main/writer.c b/ctags/main/writer.c new file mode 100644 index 0000000000..45ee6b0c75 --- /dev/null +++ b/ctags/main/writer.c @@ -0,0 +1,159 @@ +/* +* Copyright (c) 2016, Red Hat, Inc. +* Copyright (c) 2016, Masatake YAMATO +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +*/ + +#include "general.h" +#include "entry_p.h" +#include "writer_p.h" + +/* GEANY DIFF */ +/* Dummy definitions of writers as we don't need them */ + +tagWriter uCtagsWriter = { + .writeEntry = NULL, + .writePtagEntry = NULL, + .preWriteEntry = NULL, + .postWriteEntry = NULL, + .buildFqTagCache = NULL, + .defaultFileName = NULL, + .private = NULL, + .type = WRITER_DEFAULT, +}; + +tagWriter eCtagsWriter = { + .writeEntry = NULL, + .writePtagEntry = NULL, + .preWriteEntry = NULL, + .postWriteEntry = NULL, + .buildFqTagCache = NULL, + .defaultFileName = NULL, + .private = NULL, + .type = WRITER_DEFAULT, +}; + +tagWriter etagsWriter = { + .writeEntry = NULL, + .writePtagEntry = NULL, + .preWriteEntry = NULL, + .postWriteEntry = NULL, + .buildFqTagCache = NULL, + .defaultFileName = NULL, + .private = NULL, + .type = WRITER_DEFAULT, +}; + +tagWriter xrefWriter = { + .writeEntry = NULL, + .writePtagEntry = NULL, + .preWriteEntry = NULL, + .postWriteEntry = NULL, + .buildFqTagCache = NULL, + .defaultFileName = NULL, + .private = NULL, + .type = WRITER_DEFAULT, +}; + +tagWriter jsonWriter = { + .writeEntry = NULL, + .writePtagEntry = NULL, + .preWriteEntry = NULL, + .postWriteEntry = NULL, + .buildFqTagCache = NULL, + .defaultFileName = NULL, + .private = NULL, + .type = WRITER_DEFAULT, +}; + +/* GEANY DIFF END */ + +static tagWriter *writerTable [WRITER_COUNT] = { + [WRITER_U_CTAGS] = &uCtagsWriter, + [WRITER_E_CTAGS] = &eCtagsWriter, + [WRITER_ETAGS] = &etagsWriter, + [WRITER_XREF] = &xrefWriter, + [WRITER_JSON] = &jsonWriter, +}; + +static tagWriter *writer; + +extern void setTagWriter (writerType wtype) +{ + writer = writerTable [wtype]; + writer->type = wtype; +} + +extern void writerSetup (MIO *mio) +{ + if (writer->preWriteEntry) + writer->private = writer->preWriteEntry (writer, mio); + else + writer->private = NULL; +} + +extern bool writerTeardown (MIO *mio, const char *filename) +{ + if (writer->postWriteEntry) + { + bool r; + r = writer->postWriteEntry (writer, mio, filename); + writer->private = NULL; + return r; + } + return false; +} + +extern int writerWriteTag (MIO * mio, const tagEntryInfo *const tag) +{ + return writer->writeEntry (writer, mio, tag); +} + +extern int writerWritePtag (MIO * mio, + const ptagDesc *desc, + const char *const fileName, + const char *const pattern, + const char *const parserName) +{ + if (writer->writePtagEntry == NULL) + return -1; + + return writer->writePtagEntry (writer, mio, desc, fileName, + pattern, parserName); + +} + +extern void writerBuildFqTagCache (tagEntryInfo *const tag) +{ + if (writer->buildFqTagCache) + writer->buildFqTagCache (writer, tag); +} + + +extern bool ptagMakeCtagsOutputMode (ptagDesc *desc, void *data CTAGS_ATTR_UNUSED) +{ + const char *mode =""; + + if (&uCtagsWriter == writer) + mode = "u-ctags"; + else if (&eCtagsWriter == writer) + mode = "e-ctags"; + + return writePseudoTag (desc, + mode, + "u-ctags or e-ctags", + NULL); +} + +extern const char *outputDefaultFileName (void) +{ + return writer->defaultFileName; +} + +extern bool writerCanPrintPtag (void) +{ + return (writer->writePtagEntry)? true: false; +} diff --git a/ctags/main/writer_p.h b/ctags/main/writer_p.h new file mode 100644 index 0000000000..17c39d09fb --- /dev/null +++ b/ctags/main/writer_p.h @@ -0,0 +1,78 @@ +/* +* Copyright (c) 2016, Red Hat, Inc. +* Copyright (c) 2016, Masatake YAMATO +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +*/ +#ifndef CTAGS_MAIN_WRITER_PRIVATE_H +#define CTAGS_MAIN_WRITER_PRIVATE_H + +#include "general.h" /* must always come first */ +#include "mio.h" +#include "types.h" + +/* Other than writeEntry can be NULL. + The value returned from preWriteEntry is passed to writeEntry, + and postWriteEntry. If a resource is allocated in + preWriteEntry, postWriteEntry should free it. */ + +typedef enum eWriterType { + WRITER_DEFAULT, + WRITER_U_CTAGS = WRITER_DEFAULT, + WRITER_E_CTAGS, + WRITER_ETAGS, + WRITER_XREF, + WRITER_JSON, + WRITER_COUNT, +} writerType; + +struct sTagWriter; +typedef struct sTagWriter tagWriter; +struct sTagWriter { + int (* writeEntry) (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag); + int (* writePtagEntry) (tagWriter *writer, MIO * mio, const ptagDesc *desc, + const char *const fileName, + const char *const pattern, + const char *const parserName); + void * (* preWriteEntry) (tagWriter *writer, MIO * mio); + + /* Returning TRUE means the output file may be shrunk. + In such case the callee may do truncate output file. */ + bool (* postWriteEntry) (tagWriter *writer, MIO * mio, const char* filename); + void (* buildFqTagCache) (tagWriter *writer, tagEntryInfo *const tag); + const char *defaultFileName; + + /* The value returned from preWriteEntry is stored `private' field. + The value must be released in postWriteEntry. */ + void *private; + writerType type; + +}; + +extern void setTagWriter (writerType otype); +extern void writerSetup (MIO *mio); +extern bool writerTeardown (MIO *mio, const char *filename); + +int writerWriteTag (MIO * mio, const tagEntryInfo *const tag); +int writerWritePtag (MIO * mio, + const ptagDesc *desc, + const char *const fileName, + const char *const pattern, + const char *const parserName); + +extern void writerBuildFqTagCache (tagEntryInfo *const tag); + +extern const char *outputDefaultFileName (void); + +extern void truncateTagLineAfterTag (char *const line, const char *const token, + const bool discardNewline); +extern void abort_if_ferror(MIO *const fp); + +extern bool ptagMakeJsonOutputVersion (ptagDesc *desc, void *data CTAGS_ATTR_UNUSED); +extern bool ptagMakeCtagsOutputMode (ptagDesc *desc, void *data CTAGS_ATTR_UNUSED); + +extern bool writerCanPrintPtag (void); + +#endif /* CTAGS_MAIN_WRITER_PRIVATE_H */ diff --git a/ctags/main/xtag.c b/ctags/main/xtag.c index f6d5a46022..6371e93f6e 100644 --- a/ctags/main/xtag.c +++ b/ctags/main/xtag.c @@ -11,147 +11,369 @@ */ #include "general.h" /* must always come first */ +#include "ctags.h" #include "debug.h" #include "main.h" #include "options.h" +#include "parse_p.h" #include "routines.h" +#include "trashbox.h" +#include "writer_p.h" #include "xtag.h" #include +#include +typedef struct sXtagObject { + xtagDefinition *def; + langType language; + xtagType sibling; +} xtagObject; -static bool isPseudoTagsEnabled (xtagDesc *pdesc CTAGS_ATTR_UNUSED) +static bool isPseudoTagsEnabled (xtagDefinition *pdef CTAGS_ATTR_UNUSED) { + if (!writerCanPrintPtag()) + return false; + return ! isDestinationStdout (); } -static xtagDesc xtagDescs [] = { +static bool isPseudoTagsFixed (xtagDefinition *pdef CTAGS_ATTR_UNUSED) +{ + if (!writerCanPrintPtag()) + return true; + else + return false; +} + +static void enableFileKind (xtagDefinition *pdef, bool state) +{ + enableDefaultFileKind(state); + pdef->enabled = state; +} + +static xtagDefinition xtagDefinitions [] = { { true, 'F', "fileScope", - "Include tags of file scope", NULL}, + "Include tags of file scope" }, { false, 'f', "inputFile", - "Include an entry for the base file name of every input file", NULL}, + "Include an entry for the base file name of every input file", + NULL, + NULL, + enableFileKind}, { false, 'p', "pseudo", "Include pseudo tags", - isPseudoTagsEnabled}, + isPseudoTagsEnabled, + isPseudoTagsFixed}, { false, 'q', "qualified", - "Include an extra class-qualified tag entry for each tag", NULL}, + "Include an extra class-qualified tag entry for each tag"}, { false, 'r', "reference", - "Include reference tags", NULL}, - { false, 's', "subparser", - "Include tags generated by sub parsers", NULL}, + "Include reference tags"}, + { false, 'g', "guest", + "Include tags generated by guest parsers"}, + { true, 's', "subparser", + "Include tags generated by subparsers"}, + { false, '\0', "subword", + "Include tags for subwords generated by splitting the original tag (only for ctags development)"}, { true, '\0', "anonymous", "Include tags for non-named objects like lambda"}, }; -extern xtagDesc* getXtagDesc (xtagType type) +static unsigned int xtagObjectUsed; +static unsigned int xtagObjectAllocated; +static xtagObject* xtagObjects; + +static xtagObject* getXtagObject (xtagType type) { - Assert ((0 <= type) && (type < XTAG_COUNT)); - return xtagDescs + type; + Assert ((0 <= type) && ((unsigned int)type < xtagObjectUsed)); + return (xtagObjects + type); } -typedef bool (* xtagPredicate) (xtagDesc *pdesc, const void *user_data); -static xtagType getXtagTypeGeneric (xtagPredicate predicate, const void *user_data) +extern xtagDefinition* getXtagDefinition (xtagType type) { - int i; + Assert ((0 <= type) && ((unsigned int)type < xtagObjectUsed)); + + return getXtagObject (type)->def; +} - for (i = 0; i < XTAG_COUNT; i++) +typedef bool (* xtagPredicate) (xtagObject *pobj, langType language, const void *user_data); +static xtagType getXtagTypeGeneric (xtagPredicate predicate, langType language, const void *user_data) +{ + static bool initialized = false; + unsigned int i; + + if (language == LANG_AUTO && (initialized == false)) + { + initialized = true; + initializeParser (LANG_AUTO); + } + else if (language != LANG_IGNORE && (initialized == false)) + initializeParser (language); + + for (i = 0; i < xtagObjectUsed; i++) { - if (predicate (xtagDescs + i, user_data)) + if (predicate (xtagObjects + i, language, user_data)) return i; } return XTAG_UNKNOWN; } -static bool xtagEqualByLetter (xtagDesc *pdesc, const void *user_data) +static bool xtagEqualByLetter (xtagObject *pobj, langType language CTAGS_ATTR_UNUSED, + const void *user_data) { - return (pdesc->letter == *((char *)user_data))? true: false; + return (pobj->def->letter == *((char *)user_data))? true: false; } extern xtagType getXtagTypeForLetter (char letter) { - return getXtagTypeGeneric (xtagEqualByLetter, &letter); + return getXtagTypeGeneric (xtagEqualByLetter, LANG_IGNORE, &letter); } -static bool xtagEqualByName (xtagDesc *pdesc, const void *user_data) +static bool xtagEqualByNameAndLanguage (xtagObject *pobj, langType language, const void *user_data) { - return (strcmp (pdesc->name, user_data) == 0)? true: false; + const char* name = user_data; + + if ((language == LANG_AUTO || pobj->language == language) + && (strcmp (pobj->def->name, name) == 0)) + return true; + else + return false; } -extern xtagType getXtagTypeForName (const char *name) +extern xtagType getXtagTypeForNameAndLanguage (const char *name, langType language) { - return getXtagTypeGeneric (xtagEqualByName, name); + return getXtagTypeGeneric (xtagEqualByNameAndLanguage, language, name); } +extern struct colprintTable * xtagColprintTableNew (void) +{ + return colprintTableNew ("L:LETTER", "L:NAME", "L:ENABLED", + "L:LANGUAGE", "L:FIXED", "L:DESCRIPTION", NULL); +} -#define PR_XTAG_WIDTH_LETTER 7 -#define PR_XTAG_WIDTH_NAME 22 -#define PR_XTAG_WIDTH_ENABLED 7 -#define PR_XTAG_WIDTH_DESC 30 +static void xtagColprintAddLine (struct colprintTable *table, int xtype) +{ + xtagObject* xobj = getXtagObject (xtype); + xtagDefinition *xdef = xobj->def; -#define PR_XTAG_STR(X) PR_XTAG_WIDTH_##X -#define PR_XTAG_FMT(X,T) "%-" STRINGIFY(PR_XTAG_STR(X)) STRINGIFY(T) -#define MAKE_XTAG_FMT(LETTER_SPEC) \ - PR_XTAG_FMT (LETTER,LETTER_SPEC) \ - " " \ - PR_XTAG_FMT (NAME,s) \ - " " \ - PR_XTAG_FMT (ENABLED,s) \ - " " \ - PR_XTAG_FMT (DESC,s) \ - "\n" + struct colprintLine *line = colprintTableGetNewLine(table); -static void printXtag (xtagType i) + colprintLineAppendColumnChar (line, + (xdef->letter == NUL_XTAG_LETTER) + ? '-' + : xdef->letter); + colprintLineAppendColumnCString (line, xdef->name); + colprintLineAppendColumnBool (line, isXtagEnabled(xdef->xtype)); + colprintLineAppendColumnCString (line, + xobj->language == LANG_IGNORE + ? RSV_NONE + : getLanguageName (xobj->language)); + colprintLineAppendColumnBool (line, isXtagFixed(xdef->xtype)); + colprintLineAppendColumnCString (line, xdef->description); +} + +extern void xtagColprintAddCommonLines (struct colprintTable *table) { - printf((Option.machinable? "%c\t%s\t%s\t%s\n": MAKE_XTAG_FMT(c)), - xtagDescs[i].letter, - xtagDescs[i].name, - getXtagDesc (i)->enabled? "TRUE": "FALSE", - xtagDescs[i].description? xtagDescs[i].description: "NONE"); + for (int i = 0; i < XTAG_COUNT; i++) + xtagColprintAddLine (table, i); } -extern void printXtags (void) +extern void xtagColprintAddLanguageLines (struct colprintTable *table, langType language) { - unsigned int i; + for (unsigned int i = XTAG_COUNT; i < xtagObjectUsed; i++) + { + xtagObject* xobj = getXtagObject (i); + + if (xobj->language == language) + xtagColprintAddLine (table, i); + } +} + +static int xtagColprintCompareLines (struct colprintLine *a , struct colprintLine *b) +{ + const char *a_parser = colprintLineGetColumn (a, 3); + const char *b_parser = colprintLineGetColumn (b, 3); + + if (strcmp (a_parser, RSV_NONE) == 0 + && strcmp (b_parser, RSV_NONE) != 0) + return -1; + else if (strcmp (a_parser, RSV_NONE) != 0 + && strcmp (b_parser, RSV_NONE) == 0) + return 1; + else if (strcmp (a_parser, RSV_NONE) != 0 + && strcmp (b_parser, RSV_NONE) != 0) + { + int r; + r = strcmp (a_parser, b_parser); + if (r != 0) + return r; + } + else + { + int r; - if (Option.withListHeader) - printf ((Option.machinable? "%s\t%s\t%s\t%s\n": MAKE_XTAG_FMT(s)), - "#LETTER", "NAME", "ENABLED", "DESCRIPTION"); + const char *a_letter = colprintLineGetColumn (a, 0); + const char *b_letter = colprintLineGetColumn (b, 0); + r = strcmp(a_letter, b_letter); + if (r != 0) + return r; + } + + const char *a_name = colprintLineGetColumn (a, 1); + const char *b_name = colprintLineGetColumn (b, 1); - for (i = 0; i < XTAG_COUNT; i++) - printXtag (i); + return strcmp(a_name, b_name); +} + +extern void xtagColprintTablePrint (struct colprintTable *table, + bool withListHeader, bool machinable, FILE *fp) +{ + colprintTableSort (table, xtagColprintCompareLines); + colprintTablePrint (table, 0, withListHeader, machinable, fp); } extern bool isXtagEnabled (xtagType type) { - xtagDesc* desc = getXtagDesc (type); + xtagDefinition* def = getXtagDefinition (type); - Assert (desc); + Assert (def); - if (desc->isEnabled) - return desc->isEnabled (desc); + if (def->isEnabled) + return def->isEnabled (def); else - return desc->enabled; + return def->enabled; +} + +extern bool isXtagFixed (xtagType type) +{ + xtagDefinition* def = getXtagDefinition (type); + + Assert (def); + + if (def->isFixed) + return def->isFixed (def); + + return false; } extern bool enableXtag (xtagType type, bool state) { bool old; - xtagDesc* desc = getXtagDesc (type); + xtagDefinition* def = getXtagDefinition (type); - Assert (desc); + Assert (def); old = isXtagEnabled (type); - desc->enabled = state; - desc->isEnabled = NULL;; + + if (isXtagFixed(type)) + def->enabled = old; + else if (def->enable) + def->enable (def, state); + else + def->enabled = state; + + def->isEnabled = NULL; return old; } +extern bool isCommonXtag (xtagType type) +{ + return (type < XTAG_COUNT)? true: false; +} + +extern int getXtagOwner (xtagType type) +{ + return getXtagObject (type)->language; +} + const char* getXtagName (xtagType type) { - xtagDesc* desc = getXtagDesc (type); - if (desc) - return desc->name; + xtagDefinition* def = getXtagDefinition (type); + if (def) + return def->name; else return NULL; } + +extern void initXtagObjects (void) +{ + xtagObject *xobj; + + xtagObjectAllocated = ARRAY_SIZE (xtagDefinitions); + xtagObjects = xMalloc (xtagObjectAllocated, xtagObject); + DEFAULT_TRASH_BOX(&xtagObjects, eFreeIndirect); + + for (unsigned int i = 0; i < ARRAY_SIZE (xtagDefinitions); i++) + { + xobj = xtagObjects + i; + xobj->def = xtagDefinitions + i; + xobj->def->xtype = i; + xobj->language = LANG_IGNORE; + xobj->sibling = XTAG_UNKNOWN; + xtagObjectUsed++; + } +} + +extern int countXtags (void) +{ + return xtagObjectUsed; +} + +static void updateSiblingXtag (xtagType type, const char* name) +{ + int i; + xtagObject *xobj; + + for (i = type; i > 0; i--) + { + xobj = xtagObjects + i - 1; + if (xobj->def->name && (strcmp (xobj->def->name, name) == 0)) + { + Assert (xobj->sibling == XTAG_UNKNOWN); + xobj->sibling = type; + break; + } + } +} + +extern int defineXtag (xtagDefinition *def, langType language) +{ + xtagObject *xobj; + size_t i; + + Assert (def); + Assert (def->name); + for (i = 0; i < strlen (def->name); i++) + { + Assert ( isalnum (def->name [i]) ); + } + def->letter = NUL_XTAG_LETTER; + + if (xtagObjectUsed == xtagObjectAllocated) + { + xtagObjectAllocated *= 2; + xtagObjects = xRealloc (xtagObjects, xtagObjectAllocated, xtagObject); + } + xobj = xtagObjects + (xtagObjectUsed); + def->xtype = xtagObjectUsed++; + xobj->def = def; + xobj->language = language; + xobj->sibling = XTAG_UNKNOWN; + + updateSiblingXtag (def->xtype, def->name); + + verbose ("Add extra[%d]: %s,%s in %s\n", + def->xtype, + def->name, def->description, + getLanguageName (language)); + + return def->xtype; +} + +extern xtagType nextSiblingXtag (xtagType type) +{ + xtagObject *xobj; + + xobj = xtagObjects + type; + return xobj->sibling; +} diff --git a/ctags/main/xtag.h b/ctags/main/xtag.h index 17b3c9eb03..4240ad9197 100644 --- a/ctags/main/xtag.h +++ b/ctags/main/xtag.h @@ -13,6 +13,8 @@ #define CTAGS_MAIN_XTAG_H #include "general.h" +#include "colprint_p.h" + typedef enum eXtagType { /* extra tag content control */ XTAG_UNKNOWN = -1, @@ -22,14 +24,19 @@ typedef enum eXtagType { /* extra tag content control */ XTAG_PSEUDO_TAGS, XTAG_QUALIFIED_TAGS, XTAG_REFERENCE_TAGS, - XTAG_TAGS_GENERATED_BY_SUB_PARSERS, + XTAG_TAGS_GENERATED_BY_GUEST_PARSERS, + XTAG_TAGS_GENERATED_BY_SUBPARSER, + XTAG_SUBWORD, XTAG_ANONYMOUS, XTAG_COUNT } xtagType; -typedef struct sXtagDesc { +struct sXtagDefinition { bool enabled; + /* letter, and ftype are initialized in the main part, + not in a parser. */ +#define NUL_XTAG_LETTER '\0' unsigned char letter; const char* name; /* used in extra: field */ const char* description; /* displayed in --list-extra output */ @@ -39,18 +46,38 @@ typedef struct sXtagDesc { "enabled" field of Pseudo extra tag depends on where the output stream is connected to. If it is connected - to standared output, the tag is disabled by default. + to standard output, the tag is disabled by default. If it is connected to a regular file, the tag is enabled by default. */ - bool (* isEnabled) (struct sXtagDesc *desc); -} xtagDesc; + bool (* isEnabled) (struct sXtagDefinition *def); + bool (* isFixed) (struct sXtagDefinition *def); + void (* enable) (struct sXtagDefinition *def, bool state); + + unsigned int xtype; /* Given from the main part */ +}; -extern xtagDesc* getXtagDesc (xtagType type); +extern xtagDefinition* getXtagDefinition (xtagType type); extern xtagType getXtagTypeForLetter (char letter); -extern xtagType getXtagTypeForName (const char *name); +extern xtagType getXtagTypeForNameAndLanguage (const char *name, langType language); extern bool isXtagEnabled (xtagType type); extern bool enableXtag (xtagType type, bool state); +extern bool isXtagFixed (xtagType type); +extern bool isCommonXtag (xtagType type); +extern int getXtagOwner (xtagType type); + const char* getXtagName (xtagType type); -extern void printXtags (void); + +extern void initXtagObjects (void); +extern int countXtags (void); + +extern int defineXtag (xtagDefinition *def, langType language); +extern xtagType nextSiblingXtag (xtagType type); + +/* --list-extras implementation. LANGUAGE must be initialized. */ +extern struct colprintTable * xtagColprintTableNew (void); +extern void xtagColprintAddCommonLines (struct colprintTable *table); +extern void xtagColprintAddLanguageLines (struct colprintTable *table, langType language); +extern void xtagColprintTablePrint (struct colprintTable *table, + bool withListHeader, bool machinable, FILE *fp); #endif /* CTAGS_MAIN_FIELD_H */ diff --git a/ctags/parsers/erlang.c b/ctags/parsers/erlang.c index d86b5cad60..d6d061f4a5 100644 --- a/ctags/parsers/erlang.c +++ b/ctags/parsers/erlang.c @@ -18,6 +18,7 @@ #include "entry.h" #include "options.h" #include "read.h" +#include "parse.h" #include "routines.h" #include "vstring.h" diff --git a/ctags/parsers/go.c b/ctags/parsers/go.c index 0571ed6766..86616456ae 100644 --- a/ctags/parsers/go.c +++ b/ctags/parsers/go.c @@ -11,6 +11,7 @@ #include "keyword.h" #include "read.h" #include "main.h" +#include "parse.h" #include "routines.h" #include "vstring.h" #include "options.h" diff --git a/ctags/parsers/make.c b/ctags/parsers/make.c index 1c72bd4a2e..d49c42108d 100644 --- a/ctags/parsers/make.c +++ b/ctags/parsers/make.c @@ -20,6 +20,7 @@ #include "parse.h" #include "read.h" #include "routines.h" +#include "strlist.h" #include "vstring.h" #include "xtag.h" diff --git a/ctags/parsers/objc.c b/ctags/parsers/objc.c index 80cffe04cd..1558229cef 100644 --- a/ctags/parsers/objc.c +++ b/ctags/parsers/objc.c @@ -17,6 +17,7 @@ #include "keyword.h" #include "entry.h" +#include "parse.h" #include "options.h" #include "read.h" #include "routines.h" @@ -1109,7 +1110,7 @@ static void objcInitialize (const langType language) extern parserDefinition *ObjcParser (void) { static const char *const extensions[] = { "m", "h", NULL }; - parserDefinition *def = parserNewFull ("ObjectiveC", KIND_FILE_ALT); + parserDefinition *def = parserNew ("ObjectiveC"); def->kindTable = ObjcKinds; def->kindCount = ARRAY_SIZE (ObjcKinds); def->extensions = extensions; diff --git a/ctags/parsers/perl.c b/ctags/parsers/perl.c index 7218f3808b..5e6c8e42aa 100644 --- a/ctags/parsers/perl.c +++ b/ctags/parsers/perl.c @@ -17,6 +17,7 @@ #include #include "entry.h" +#include "promise.h" #include "options.h" #include "read.h" #include "routines.h" diff --git a/ctags/parsers/python.c b/ctags/parsers/python.c index b84906926d..fc35ecf640 100644 --- a/ctags/parsers/python.c +++ b/ctags/parsers/python.c @@ -19,6 +19,7 @@ #include "options.h" #include "read.h" #include "main.h" +#include "parse.h" #include "vstring.h" #include "routines.h" #include "debug.h" diff --git a/ctags/parsers/r.c b/ctags/parsers/r.c index 941dc0cf77..8da35e5079 100644 --- a/ctags/parsers/r.c +++ b/ctags/parsers/r.c @@ -19,6 +19,7 @@ #include "debug.h" #include "entry.h" +#include "parse.h" #include "read.h" #include "vstring.h" #include "routines.h" diff --git a/ctags/parsers/ruby.c b/ctags/parsers/ruby.c index 2e7604e4cf..d269709da5 100644 --- a/ctags/parsers/ruby.c +++ b/ctags/parsers/ruby.c @@ -554,7 +554,7 @@ static void findRubyTags (void) extern parserDefinition* RubyParser (void) { static const char *const extensions [] = { "rb", "ruby", NULL }; - parserDefinition* def = parserNewFull ("Ruby", KIND_FILE_ALT); + parserDefinition* def = parserNew ("Ruby"); def->kindTable = RubyKinds; def->kindCount = ARRAY_SIZE (RubyKinds); def->extensions = extensions; diff --git a/ctags/parsers/rust.c b/ctags/parsers/rust.c index b473d0f446..916492b728 100644 --- a/ctags/parsers/rust.c +++ b/ctags/parsers/rust.c @@ -973,7 +973,7 @@ static void findRustTags (void) extern parserDefinition *RustParser (void) { static const char *const extensions[] = { "rs", NULL }; - parserDefinition *def = parserNewFull ("Rust", KIND_FILE_ALT); + parserDefinition *def = parserNew ("Rust"); def->kindTable = rustKinds; def->kindCount = ARRAY_SIZE (rustKinds); def->extensions = extensions; diff --git a/ctags/parsers/sql.c b/ctags/parsers/sql.c index 8d4ebaa891..eb4e3e5305 100644 --- a/ctags/parsers/sql.c +++ b/ctags/parsers/sql.c @@ -2338,7 +2338,7 @@ static void findSqlTags (void) extern parserDefinition* SqlParser (void) { static const char *const extensions [] = { "sql", NULL }; - parserDefinition* def = parserNewFull ("SQL", KIND_FILE_ALT); + parserDefinition* def = parserNew ("SQL"); def->kindTable = SqlKinds; def->kindCount = ARRAY_SIZE (SqlKinds); def->extensions = extensions; From a63ee689d085415de0c2201a832c4c6251da6c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Tue, 23 Apr 2019 22:05:08 +0200 Subject: [PATCH 02/46] Enable Cobol test --- tests/ctags/Makefile.am | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/ctags/Makefile.am b/tests/ctags/Makefile.am index a934b517cc..0525b4bfad 100644 --- a/tests/ctags/Makefile.am +++ b/tests/ctags/Makefile.am @@ -1,9 +1,6 @@ dist_check_SCRIPTS = runner.sh -# Disabled tests: -# simple.cbl - NULL = test_sources = \ 1795612.js \ @@ -275,6 +272,7 @@ test_sources = \ simple.abc \ simple.asciidoc \ simple.bas \ + simple.cbl \ simple.conf \ simple.d \ simple.diff \ From d7763b41e1acca23dc92b8f80a1bce1dd7ea2669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Tue, 23 Apr 2019 22:23:57 +0200 Subject: [PATCH 03/46] Rename Geany ctags diff markers To improve future updates to new ctags versions, rename CTAGS_LIB and GEANY_DIFF to GEANY_CTAGS_LIB and GEANY_CTAGS_DIFF, respectively. This enables us to search the code for "GEANY_CTAGS" to see all the Geany-related diffs. --- configure.ac | 2 +- ctags/main/ctags-api.c | 4 ++-- ctags/main/ctags-api.h | 4 ++-- ctags/main/entry.c | 32 +++++++++++++++--------------- ctags/main/entry.h | 8 ++++---- ctags/main/main.c | 12 +++++------ ctags/main/options.c | 8 ++++---- ctags/main/options.h | 4 ++-- ctags/main/parse.c | 44 ++++++++++++++++++++--------------------- ctags/main/parse.h | 4 ++-- ctags/main/promise.c | 4 ++-- ctags/main/ptag.c | 4 ++-- ctags/main/ptag_p.h | 4 ++-- ctags/main/read.c | 4 ++-- ctags/main/read.h | 4 ++-- ctags/main/routines_p.h | 4 ++-- ctags/main/strlist.c | 8 ++++---- ctags/main/writer.c | 4 ++-- 18 files changed, 79 insertions(+), 79 deletions(-) diff --git a/configure.ac b/configure.ac index 167622008d..3278457665 100644 --- a/configure.ac +++ b/configure.ac @@ -44,7 +44,7 @@ AC_CHECK_HEADERS([fcntl.h glob.h stdlib.h sys/time.h errno.h limits.h]) # Checks for dependencies needed by ctags AC_CHECK_HEADERS([fnmatch.h direct.h io.h sys/dir.h]) AC_DEFINE([USE_STDBOOL_H], [1], [whether or not to use .]) -AC_DEFINE([CTAGS_LIB], [1], [compile ctags as a library.]) +AC_DEFINE([GEANY_CTAGS_LIB], [1], [compile ctags as a library.]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_OFF_T diff --git a/ctags/main/ctags-api.c b/ctags/main/ctags-api.c index 0864a3864f..e3a29e6bb4 100644 --- a/ctags/main/ctags-api.c +++ b/ctags/main/ctags-api.c @@ -9,7 +9,7 @@ #include "general.h" /* must always come first */ -#ifdef CTAGS_LIB +#ifdef GEANY_CTAGS_LIB #include "ctags-api.h" #include "types.h" @@ -145,4 +145,4 @@ extern unsigned int ctagsGetLangCount(void) return countParsers(); } -#endif /* CTAGS_LIB */ +#endif /* GEANY_CTAGS_LIB */ diff --git a/ctags/main/ctags-api.h b/ctags/main/ctags-api.h index f973ef07d7..fce9a94e26 100644 --- a/ctags/main/ctags-api.h +++ b/ctags/main/ctags-api.h @@ -11,7 +11,7 @@ #include "general.h" /* must always come first */ -#ifdef CTAGS_LIB +#ifdef GEANY_CTAGS_LIB #include #include @@ -52,6 +52,6 @@ extern char ctagsGetKindFromName(const char *name, int lang); extern bool ctagsIsUsingRegexParser(int lang); extern unsigned int ctagsGetLangCount(void); -#endif /* CTAGS_LIB */ +#endif /* GEANY_CTAGS_LIB */ #endif /* CTAGS_API_H */ diff --git a/ctags/main/entry.c b/ctags/main/entry.c index d1182b35ab..003d0f0f7d 100644 --- a/ctags/main/entry.c +++ b/ctags/main/entry.c @@ -122,10 +122,10 @@ static tagFile TagFile = { static bool TagsToStdout = false; -#ifdef CTAGS_LIB +#ifdef GEANY_CTAGS_LIB static tagEntryFunction TagEntryFunction = NULL; static void *TagEntryUserData = NULL; -#endif +#endif /* GEANY_CTAGS_LIB */ /* * FUNCTION PROTOTYPES @@ -160,10 +160,10 @@ extern const char *tagFileName (void) extern void abort_if_ferror(MIO *const mio) { -#ifndef CTAGS_LIB +#ifndef GEANY_CTAGS_LIB if (mio_error (mio)) error (FATAL | PERROR, "cannot write tag file"); -#endif +#endif /* GEANY_CTAGS_LIB */ } static void rememberMaxLengths (const size_t nameLength, const size_t lineLength) @@ -1041,10 +1041,10 @@ static void recordTagEntryInQueue (const tagEntryInfo *const tag, tagEntryInfo* slot->extensionFields.typeRef[0] = eStrdup (slot->extensionFields.typeRef[0]); if (slot->extensionFields.typeRef[1]) slot->extensionFields.typeRef[1] = eStrdup (slot->extensionFields.typeRef[1]); -/* GEANY DIFF */ +/* GEANY_CTAGS_DIFF */ if (slot->extensionFields.varType) slot->extensionFields.varType = eStrdup (slot->extensionFields.varType); -/* GEANY DIFF END */ +/* GEANY_CTAGS_DIFF_END */ #ifdef HAVE_LIBXML if (slot->extensionFields.xpath) slot->extensionFields.xpath = eStrdup (slot->extensionFields.xpath); @@ -1116,10 +1116,10 @@ static void clearTagEntryInQueue (tagEntryInfo* slot) eFree ((char *)slot->extensionFields.typeRef[0]); if (slot->extensionFields.typeRef[1]) eFree ((char *)slot->extensionFields.typeRef[1]); -/* GEANY DIFF */ +/* GEANY_CTAGS_DIFF */ if (slot->extensionFields.varType) eFree ((char *)slot->extensionFields.varType); -/* GEANY DIFF END */ +/* GEANY_CTAGS_DIFF_END */ #ifdef HAVE_LIBXML if (slot->extensionFields.xpath) eFree ((char *)slot->extensionFields.xpath); @@ -1226,7 +1226,7 @@ static bool isTagWritable(const tagEntryInfo *const tag) } -#ifdef CTAGS_LIB +#ifdef GEANY_CTAGS_LIB static void initCtagsTag(ctagsTag *tag, const tagEntryInfo *info) { tag->name = info->name; @@ -1241,7 +1241,7 @@ static void initCtagsTag(ctagsTag *tag, const tagEntryInfo *info) tag->lineNumber = info->lineNumber; tag->lang = info->langType; } -#endif +#endif /* GEANY_CTAGS_LIB */ static void writeTagEntry (const tagEntryInfo *const tag, bool checkingNeeded) { @@ -1262,7 +1262,7 @@ static void writeTagEntry (const tagEntryInfo *const tag, bool checkingNeeded) writerBuildFqTagCache ( (tagEntryInfo *const)tag); } -#ifdef CTAGS_LIB +#ifdef GEANY_CTAGS_LIB getTagScopeInformation((tagEntryInfo *)tag, NULL, NULL); if (TagEntryFunction != NULL) @@ -1274,7 +1274,7 @@ static void writeTagEntry (const tagEntryInfo *const tag, bool checkingNeeded) } #else length = writerWriteTag (TagFile.mio, tag); -#endif +#endif /* GEANY_CTAGS_LIB */ ++TagFile.numTags.added; rememberMaxLengths (strlen (tag->name), (size_t) length); @@ -1393,11 +1393,11 @@ extern int makeTagEntry (const tagEntryInfo *const tag) int r = CORK_NIL; Assert (tag->name != NULL); -#ifndef CTAGS_LIB +#ifndef GEANY_CTAGS_LIB if (!TagFile.cork) if (!isTagWritable (tag)) goto out; -#endif +#endif /* GEANY_CTAGS_LIB */ if (tag->name [0] == '\0' && (!tag->placeholder)) { @@ -1720,10 +1720,10 @@ extern const char* getTagFileDirectory (void) return TagFile.directory; } -#ifdef CTAGS_LIB +#ifdef GEANY_CTAGS_LIB extern void setTagEntryFunction(tagEntryFunction entry_function, void *user_data) { TagEntryFunction = entry_function; TagEntryUserData = user_data; } -#endif +#endif /* GEANY_CTAGS_LIB */ diff --git a/ctags/main/entry.h b/ctags/main/entry.h index 5ca2edd7eb..9cff6d1d14 100644 --- a/ctags/main/entry.h +++ b/ctags/main/entry.h @@ -87,9 +87,9 @@ struct sTagEntryInfo { #define ROLE_MAX_COUNT (sizeof(roleBitsType) * 8) roleBitsType roleBits; /* for role of reference tag */ -/* GEANY DIFF */ +/* GEANY_CTAGS_DIFF */ const char *varType; -/* GEANY DIFF END */ +/* GEANY_CTAGS_DIFF_END */ #ifdef HAVE_LIBXML const char* xpath; @@ -155,8 +155,8 @@ CTAGS_INLINE roleBitsType makeRoleBit(int roleIndex) return ((roleBitsType)1) << roleIndex; } -#ifdef CTAGS_LIB +#ifdef GEANY_CTAGS_LIB extern void setTagEntryFunction(tagEntryFunction entry_function, void *user_data); -#endif +#endif /* GEANY_CTAGS_LIB */ #endif /* CTAGS_MAIN_ENTRY_H */ diff --git a/ctags/main/main.c b/ctags/main/main.c index a78976e2bb..991003a9fd 100644 --- a/ctags/main/main.c +++ b/ctags/main/main.c @@ -91,17 +91,17 @@ * DATA DEFINITIONS */ static struct { long files, lines, bytes; } Totals = { 0, 0, 0 }; -#ifndef CTAGS_LIB +#ifndef GEANY_CTAGS_LIB static mainLoopFunc mainLoop; static void *mainData; -#endif +#endif /* GEANY_CTAGS_LIB */ /* * FUNCTION PROTOTYPES */ -#ifndef CTAGS_LIB +#ifndef GEANY_CTAGS_LIB static bool createTagsForEntry (const char *const entryName); -#endif +#endif /* GEANY_CTAGS_LIB */ /* * FUNCTION DEFINITIONS @@ -131,7 +131,7 @@ extern bool isDestinationStdout (void) return toStdout; } -#ifndef CTAGS_LIB +#ifndef GEANY_CTAGS_LIB #if defined (HAVE_OPENDIR) static bool recurseUsingOpendir (const char *const dirName) @@ -709,4 +709,4 @@ extern int main (int argc CTAGS_ATTR_UNUSED, char **argv) exit (0); return 0; } -#endif +#endif /* GEANY_CTAGS_LIB */ diff --git a/ctags/main/options.c b/ctags/main/options.c index e91f4e9ccb..4d79788779 100644 --- a/ctags/main/options.c +++ b/ctags/main/options.c @@ -3386,14 +3386,14 @@ extern void previewFirstOption (cookedArgs* const args) } } -#ifndef CTAGS_LIB +#ifndef GEANY_CTAGS_LIB static void parseConfigurationFileOptionsInDirectoryWithLeafname (const char* directory, const char* leafname) { char* pathname = combinePathAndFile (directory, leafname); parseFileOptions (pathname); eFree (pathname); } -#endif +#endif /* GEANY_CTAGS_LIB */ #if defined(HAVE_SCANDIR) || defined (HAVE_DIRENT_H) || defined (_MSC_VER) static int ignore_dot_file(const struct dirent* dent) @@ -3748,7 +3748,7 @@ extern bool canUseLineNumberAsLocator (void) return (Option.locate != EX_PATTERN); } -/* GEANY DIFF */ +/* GEANY_CTAGS_DIFF */ /* tags_ignore is a NULL-terminated array of strings, read from ~/.config/geany/ignore.tags. * This file contains a space or newline separated list of symbols which should be ignored * by the C/C++ parser, see -I command line option of ctags for details. */ @@ -3815,4 +3815,4 @@ extern bool isIgnoreToken (const char *const name, } return result; } -/* GEANY DIFF END */ +/* GEANY_CTAGS_DIFF_END */ diff --git a/ctags/main/options.h b/ctags/main/options.h index 75c2f89097..5275a38af9 100644 --- a/ctags/main/options.h +++ b/ctags/main/options.h @@ -42,10 +42,10 @@ extern bool inSandbox (void); /* This is for emitting a tag for a commnn block of Fortran parser*/ extern bool canUseLineNumberAsLocator (void); -/* GEANY DIFF */ +/* GEANY_CTAGS_DIFF */ extern bool isIgnoreToken (const char *const name, bool *const pIgnoreParens, const char **const replacement); -/* GEANY DIFF END */ +/* GEANY_CTAGS_DIFF_END */ #endif /* CTAGS_MAIN_OPTIONS_H */ diff --git a/ctags/main/parse.c b/ctags/main/parse.c index 88bccde1ad..04e3d4d38d 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -92,9 +92,9 @@ typedef struct sParserObject { */ static void lazyInitialize (langType language); -#ifndef CTAGS_LIB +#ifndef GEANY_CTAGS_LIB static void addParserPseudoTags (langType language); -#endif +#endif /* GEANY_CTAGS_LIB */ static void installKeywordTable (const langType language); static void installTagRegexTable (const langType language); static void installTagXpathTable (const langType language); @@ -105,19 +105,19 @@ static void teardownAnon (void); /* * DATA DEFINITIONS */ -#ifndef CTAGS_LIB +#ifndef GEANY_CTAGS_LIB static parserDefinition *CTagsSelfTestParser (void); -#endif +#endif /* GEANY_CTAGS_LIB */ static parserDefinitionFunc* BuiltInParsers[] = { -#ifndef CTAGS_LIB +#ifndef GEANY_CTAGS_LIB CTagsSelfTestParser, -#endif +#endif /* GEANY_CTAGS_LIB */ PARSER_LIST, -#ifndef CTAGS_LIB +#ifndef GEANY_CTAGS_LIB XML_PARSER_LIST #ifdef HAVE_LIBXML , -#endif +#endif /* GEANY_CTAGS_LIB */ YAML_PARSER_LIST #ifdef HAVE_LIBYAML , @@ -3107,7 +3107,7 @@ static subparser* teardownLanguageSubparsersInUse (const langType language) return teardownSubparsersInUse ((LanguageTable + language)->slaveControlBlock); } -#ifndef CTAGS_LIB +#ifndef GEANY_CTAGS_LIB static bool createTagsWithFallback1 (const langType language, langType *exclusive_subparser) { @@ -3235,7 +3235,7 @@ static bool createTagsWithFallback1 (const langType language, return false; } -#endif +#endif /* GEANY_CTAGS_LIB */ extern bool runParserInNarrowedInputStream (const langType language, unsigned long startLine, long startCharOffset, @@ -3260,7 +3260,7 @@ extern bool runParserInNarrowedInputStream (const langType language, endLine, endCharOffset, sourceLineOffset, promise); -#ifndef CTAGS_LIB +#ifndef GEANY_CTAGS_LIB tagFileResized = createTagsWithFallback1 (language, NULL); #else /* Simple parsing without rescans - not used by any sub-parsers anyway */ @@ -3273,13 +3273,13 @@ extern bool runParserInNarrowedInputStream (const langType language, if (useCork) uncorkTagFile(); tagFileResized = false; -#endif +#endif /* GEANY_CTAGS_LIB */ popNarrowedInputStream (); return tagFileResized; } -#ifndef CTAGS_LIB +#ifndef GEANY_CTAGS_LIB static bool createTagsWithFallback ( const char *const fileName, const langType language, MIO *mio) @@ -3335,7 +3335,7 @@ extern const parserDefinition *getParserDefinition (langType language) return LanguageTable[language].def; } -#endif +#endif /* GEANY_CTAGS_LIB */ static void printGuessedParser (const char* const fileName, langType language) { @@ -3416,7 +3416,7 @@ extern const char *getLanguageEncoding (const langType language) } #endif -#ifndef CTAGS_LIB +#ifndef GEANY_CTAGS_LIB static void addParserPseudoTags (langType language) { parserObject *parser = LanguageTable + language; @@ -3428,7 +3428,7 @@ static void addParserPseudoTags (langType language) parser->pseudoTagPrinted = 1; } } -#endif +#endif /* GEANY_CTAGS_LIB */ extern bool doesParserRequireMemoryStream (const langType language) { @@ -3511,9 +3511,9 @@ extern bool parseFileWithMio (const char *const fileName, MIO *mio) initParserTrashBox (); -#ifndef CTAGS_LIB +#ifndef GEANY_CTAGS_LIB tagFileResized = createTagsWithFallback (fileName, language, mio); -#endif +#endif /* GEANY_CTAGS_LIB */ finiParserTrashBox (); @@ -3973,14 +3973,14 @@ extern void anonGenerate (vString *buffer, const char *prefix, int kind) vStringCopyS(buffer, prefix); -/* GEANY DIFF */ +/* GEANY_CTAGS_DIFF */ /* char buf [9]; anonHashString (getInputFileName(), buf); sprintf(szNum,"%s%02x%02x",buf,parser -> anonymousIdentiferId, kind); */ sprintf(szNum,"%u", parser -> anonymousIdentiferId); -/* GEANY DIFF END */ +/* GEANY_CTAGS_DIFF_END */ vStringCatS(buffer,szNum); } @@ -4211,7 +4211,7 @@ extern void addLanguageTagMultiTableRegex(const langType language, name, kinds, flags, disabled); } -#ifndef CTAGS_LIB +#ifndef GEANY_CTAGS_LIB /* * A parser for CTagsSelfTest (CTST) */ @@ -4449,4 +4449,4 @@ static parserDefinition *CTagsSelfTestParser (void) def->useCork = true; return def; } -#endif /* CTAGS_LIB */ +#endif /* GEANY_CTAGS_LIB */ diff --git a/ctags/main/parse.h b/ctags/main/parse.h index e2a0a5e2e2..52a2b1d866 100644 --- a/ctags/main/parse.h +++ b/ctags/main/parse.h @@ -134,13 +134,13 @@ extern kindDefinition* getLanguageKindForLetter (const langType language, char k extern void initializeParser (langType language); -#ifdef CTAGS_LIB +#ifdef GEANY_CTAGS_LIB extern void createTagsWithFallback(unsigned char *buffer, size_t bufferSize, const char *fileName, const langType language, tagEntryFunction tagCallback, passStartCallback passCallback, void *userData); extern const parserDefinition *getParserDefinition (langType language); -#endif +#endif /* GEANY_CTAGS_LIB */ #ifdef HAVE_ICONV extern const char *getLanguageEncoding (const langType language); diff --git a/ctags/main/promise.c b/ctags/main/promise.c index fbdd04acb3..dafe7af938 100644 --- a/ctags/main/promise.c +++ b/ctags/main/promise.c @@ -69,7 +69,7 @@ int makePromise (const char *parser, int r; langType lang; -/* GEANY DIFF */ +/* GEANY_CTAGS_DIFF */ /* if ((!isThinStreamSpec(startLine, startCharOffset, @@ -79,7 +79,7 @@ int makePromise (const char *parser, && ( !isXtagEnabled (XTAG_TAGS_GENERATED_BY_GUEST_PARSERS))) return -1; */ -/* GEANY DIFF END */ +/* GEANY_CTAGS_DIFF_END */ lang = getNamedLanguage (parser, 0); if (lang == LANG_IGNORE) diff --git a/ctags/main/ptag.c b/ctags/main/ptag.c index 69c0224951..58739cfe59 100644 --- a/ctags/main/ptag.c +++ b/ctags/main/ptag.c @@ -95,7 +95,7 @@ static bool ptagMakeKindDescriptions (ptagDesc *desc, void *data) } static ptagDesc ptagDescs [] = { -/* GEANY DIFF */ +/* GEANY_CTAGS_DIFF */ #if 0 { /* The prefix is not "TAG_". @@ -105,7 +105,7 @@ static ptagDesc ptagDescs [] = { ptagMakeJsonOutputVersion, true }, #endif -/* GEANY DIFF END */ +/* GEANY_CTAGS_DIFF_END */ { true, "TAG_FILE_FORMAT", "the version of tags file format", ptagMakeFormat, diff --git a/ctags/main/ptag_p.h b/ctags/main/ptag_p.h index 8574103804..e3d24a8be1 100644 --- a/ctags/main/ptag_p.h +++ b/ctags/main/ptag_p.h @@ -22,11 +22,11 @@ typedef enum ePtagType { /* pseudo tag content control */ PTAG_UNKNOWN = -1, /* Only --output-format=json use this ptag. Applications of the output may expect this comes first in the output. */ -/* GEANY DIFF */ +/* GEANY_CTAGS_DIFF */ /* PTAG_JSON_OUTPUT_VERSION, */ -/* GEANY DIFF END */ +/* GEANY_CTAGS_DIFF_END */ PTAG_FILE_FORMAT, PTAG_FILE_SORTED, diff --git a/ctags/main/read.c b/ctags/main/read.c index 9d8bfa1237..c2b1400e69 100644 --- a/ctags/main/read.c +++ b/ctags/main/read.c @@ -715,7 +715,7 @@ extern bool openInputFile (const char *const fileName, const langType language, return opened; } -#ifdef CTAGS_LIB +#ifdef GEANY_CTAGS_LIB /* The user should take care of allocate and free the buffer param. * This func is NOT THREAD SAFE. * The user should not tamper with the buffer while this func is executing. @@ -731,7 +731,7 @@ extern bool bufferOpen (const char *const fileName, const langType language, mio_free (mio); return opened; } -#endif +#endif /* GEANY_CTAGS_LIB */ extern void resetInputFile (const langType language) { diff --git a/ctags/main/read.h b/ctags/main/read.h index f453bde0c7..a2d0865658 100644 --- a/ctags/main/read.h +++ b/ctags/main/read.h @@ -84,10 +84,10 @@ extern const unsigned char *getInputFileData (size_t *size); internally. The 3rd argument is introduced for reusing mio object created in parser guessing stage. */ extern bool openInputFile (const char *const fileName, const langType language, MIO *mio); -#ifdef CTAGS_LIB +#ifdef GEANY_CTAGS_LIB extern bool bufferOpen (const char *const fileName, const langType language, unsigned char *buffer, size_t buffer_size); -#endif +#endif /* GEANY_CTAGS_LIB */ extern MIO *getMio (const char *const fileName, const char *const openMode, bool memStreamRequired); extern void resetInputFile (const langType language); diff --git a/ctags/main/routines_p.h b/ctags/main/routines_p.h index 6187459382..6773bde237 100644 --- a/ctags/main/routines_p.h +++ b/ctags/main/routines_p.h @@ -94,7 +94,7 @@ extern MIO *tempFile (const char *const mode, char **const pName); extern char* baseFilenameSansExtensionNew (const char *const fileName, const char *const templateExt); -/* GEANY DIFF */ +/* GEANY_CTAGS_DIFF */ /* #include "portable-dirent_p.h" @@ -102,6 +102,6 @@ extern int scanDirectory (const char *directory_name, struct dirent ***array_pointer, int (*select_function) (const struct dirent *), int (*compare_function) (const struct dirent **, const struct dirent **)); */ -/* GEANY DIFF END */ +/* GEANY_CTAGS_DIFF_END */ #endif /* CTAGS_MAIN_ROUTINES_PRIVATE_H */ diff --git a/ctags/main/strlist.c b/ctags/main/strlist.c index 4ef8f8a3ef..5ca4900146 100644 --- a/ctags/main/strlist.c +++ b/ctags/main/strlist.c @@ -13,11 +13,11 @@ #include "general.h" /* must always come first */ #include -/* GEANY DIFF */ +/* GEANY_CTAGS_DIFF */ /* #include */ -/* GEANY DIFF END */ +/* GEANY_CTAGS_DIFF_END */ #include "debug.h" #include "read.h" @@ -219,9 +219,9 @@ extern vString* stringListExtensionFinds ( static bool fileNameMatched ( const vString* const vpattern, const char* const fileName) { -/* GEANY DIFF */ +/* GEANY_CTAGS_DIFF */ return false; -/* GEANY DIFF END */ +/* GEANY_CTAGS_DIFF_END */ } extern bool stringListFileMatched ( diff --git a/ctags/main/writer.c b/ctags/main/writer.c index 45ee6b0c75..3aca1320a1 100644 --- a/ctags/main/writer.c +++ b/ctags/main/writer.c @@ -11,7 +11,7 @@ #include "entry_p.h" #include "writer_p.h" -/* GEANY DIFF */ +/* GEANY_CTAGS_DIFF */ /* Dummy definitions of writers as we don't need them */ tagWriter uCtagsWriter = { @@ -69,7 +69,7 @@ tagWriter jsonWriter = { .type = WRITER_DEFAULT, }; -/* GEANY DIFF END */ +/* GEANY_CTAGS_DIFF_END */ static tagWriter *writerTable [WRITER_COUNT] = { [WRITER_U_CTAGS] = &uCtagsWriter, From 2413f2e170bdd6b708cad715de006932bb7e8dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Tue, 23 Apr 2019 22:36:10 +0200 Subject: [PATCH 04/46] Store varType inside typeRef[1] This is what the upstream cxx parser does and thanks to this change we can eliminate several GEANY_CTAGS_DIFFs. --- ctags/main/entry.c | 10 +--------- ctags/main/entry.h | 4 ---- ctags/parsers/c.c | 2 +- ctags/parsers/go.c | 2 +- ctags/parsers/pascal.c | 2 +- ctags/parsers/rust.c | 2 +- 6 files changed, 5 insertions(+), 17 deletions(-) diff --git a/ctags/main/entry.c b/ctags/main/entry.c index 003d0f0f7d..f3ee730502 100644 --- a/ctags/main/entry.c +++ b/ctags/main/entry.c @@ -1041,10 +1041,6 @@ static void recordTagEntryInQueue (const tagEntryInfo *const tag, tagEntryInfo* slot->extensionFields.typeRef[0] = eStrdup (slot->extensionFields.typeRef[0]); if (slot->extensionFields.typeRef[1]) slot->extensionFields.typeRef[1] = eStrdup (slot->extensionFields.typeRef[1]); -/* GEANY_CTAGS_DIFF */ - if (slot->extensionFields.varType) - slot->extensionFields.varType = eStrdup (slot->extensionFields.varType); -/* GEANY_CTAGS_DIFF_END */ #ifdef HAVE_LIBXML if (slot->extensionFields.xpath) slot->extensionFields.xpath = eStrdup (slot->extensionFields.xpath); @@ -1116,10 +1112,6 @@ static void clearTagEntryInQueue (tagEntryInfo* slot) eFree ((char *)slot->extensionFields.typeRef[0]); if (slot->extensionFields.typeRef[1]) eFree ((char *)slot->extensionFields.typeRef[1]); -/* GEANY_CTAGS_DIFF */ - if (slot->extensionFields.varType) - eFree ((char *)slot->extensionFields.varType); -/* GEANY_CTAGS_DIFF_END */ #ifdef HAVE_LIBXML if (slot->extensionFields.xpath) eFree ((char *)slot->extensionFields.xpath); @@ -1233,7 +1225,7 @@ static void initCtagsTag(ctagsTag *tag, const tagEntryInfo *info) tag->signature = info->extensionFields.signature; tag->scopeName = info->extensionFields.scopeName; tag->inheritance = info->extensionFields.inheritance; - tag->varType = info->extensionFields.varType; + tag->varType = info->extensionFields.typeRef[1]; tag->access = info->extensionFields.access; tag->implementation = info->extensionFields.implementation; tag->kindLetter = getLanguageKind(info->langType, info->kindIndex)->letter; diff --git a/ctags/main/entry.h b/ctags/main/entry.h index 9cff6d1d14..2456111dd4 100644 --- a/ctags/main/entry.h +++ b/ctags/main/entry.h @@ -87,10 +87,6 @@ struct sTagEntryInfo { #define ROLE_MAX_COUNT (sizeof(roleBitsType) * 8) roleBitsType roleBits; /* for role of reference tag */ -/* GEANY_CTAGS_DIFF */ - const char *varType; -/* GEANY_CTAGS_DIFF_END */ - #ifdef HAVE_LIBXML const char* xpath; #endif diff --git a/ctags/parsers/c.c b/ctags/parsers/c.c index f898276786..244677dc22 100644 --- a/ctags/parsers/c.c +++ b/ctags/parsers/c.c @@ -1224,7 +1224,7 @@ static void addOtherFields (tagEntryInfo* const tag, const tagType type, if (((TOKEN_NAME == st->firstToken->type) || isDataTypeKeyword(st->firstToken)) && (0 != strcmp(vStringValue(st->firstToken->name), tag->name))) { - tag->extensionFields.varType = getVarType(st, nameToken); + tag->extensionFields.typeRef[1] = getVarType(st, nameToken); } } } diff --git a/ctags/parsers/go.c b/ctags/parsers/go.c index 86616456ae..d7b5981b5e 100644 --- a/ctags/parsers/go.c +++ b/ctags/parsers/go.c @@ -532,7 +532,7 @@ static void makeTag (tokenInfo *const token, const goKind kind, if (argList) e.extensionFields.signature = argList; if (varType) - e.extensionFields.varType = varType; + e.extensionFields.typeRef[1] = varType; if (parent_kind != GOTAG_UNDEFINED && parent_token != NULL) { diff --git a/ctags/parsers/pascal.c b/ctags/parsers/pascal.c index 3221dfb9a3..fd4fbdd19b 100644 --- a/ctags/parsers/pascal.c +++ b/ctags/parsers/pascal.c @@ -46,7 +46,7 @@ static void createPascalTag (tagEntryInfo* const tag, initTagEntry (tag, vStringValue (name), kind); tag->extensionFields.signature = arglist; - tag->extensionFields.varType = vartype; + tag->extensionFields.typeRef[1] = vartype; } else { diff --git a/ctags/parsers/rust.c b/ctags/parsers/rust.c index 916492b728..a9348cec15 100644 --- a/ctags/parsers/rust.c +++ b/ctags/parsers/rust.c @@ -446,7 +446,7 @@ static void addTag (vString* ident, const char* type, const char* arg_list, int tag.sourceFileName = getInputFileName(); tag.extensionFields.signature = arg_list; - tag.extensionFields.varType = type; + tag.extensionFields.typeRef[1] = type; if (parent_kind != K_NONE) { tag.extensionFields.scopeKindIndex = parent_kind; From 02d747b4405016015e0ee34343304b4c8102379f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Tue, 23 Apr 2019 22:58:55 +0200 Subject: [PATCH 05/46] Add all ctags writers to eliminate some geany diffs --- ctags/Makefile.am | 4 + ctags/main/ptag.c | 4 - ctags/main/ptag_p.h | 4 - ctags/main/writer-ctags.c | 292 ++++++++++++++++++++++++++++++++++++++ ctags/main/writer-etags.c | 133 +++++++++++++++++ ctags/main/writer-json.c | 248 ++++++++++++++++++++++++++++++++ ctags/main/writer-xref.c | 72 ++++++++++ ctags/main/writer.c | 64 +-------- 8 files changed, 754 insertions(+), 67 deletions(-) create mode 100644 ctags/main/writer-ctags.c create mode 100644 ctags/main/writer-etags.c create mode 100644 ctags/main/writer-json.c create mode 100644 ctags/main/writer-xref.c diff --git a/ctags/Makefile.am b/ctags/Makefile.am index 8758e47105..ef8ad59fdb 100644 --- a/ctags/Makefile.am +++ b/ctags/Makefile.am @@ -146,6 +146,10 @@ libctags_la_SOURCES = \ main/vstring.h \ main/writer.c \ main/writer_p.h \ + main/writer-ctags.c \ + main/writer-etags.c \ + main/writer-json.c \ + main/writer-xref.c \ main/xtag.h \ main/xtag.c \ $(parsers) diff --git a/ctags/main/ptag.c b/ctags/main/ptag.c index 58739cfe59..6324d7cff7 100644 --- a/ctags/main/ptag.c +++ b/ctags/main/ptag.c @@ -95,8 +95,6 @@ static bool ptagMakeKindDescriptions (ptagDesc *desc, void *data) } static ptagDesc ptagDescs [] = { -/* GEANY_CTAGS_DIFF */ -#if 0 { /* The prefix is not "TAG_". Only --output-format=json use this ptag. */ @@ -104,8 +102,6 @@ static ptagDesc ptagDescs [] = { "the version of json output stream format", ptagMakeJsonOutputVersion, true }, -#endif -/* GEANY_CTAGS_DIFF_END */ { true, "TAG_FILE_FORMAT", "the version of tags file format", ptagMakeFormat, diff --git a/ctags/main/ptag_p.h b/ctags/main/ptag_p.h index e3d24a8be1..7266553c44 100644 --- a/ctags/main/ptag_p.h +++ b/ctags/main/ptag_p.h @@ -22,11 +22,7 @@ typedef enum ePtagType { /* pseudo tag content control */ PTAG_UNKNOWN = -1, /* Only --output-format=json use this ptag. Applications of the output may expect this comes first in the output. */ -/* GEANY_CTAGS_DIFF */ -/* PTAG_JSON_OUTPUT_VERSION, -*/ -/* GEANY_CTAGS_DIFF_END */ PTAG_FILE_FORMAT, PTAG_FILE_SORTED, diff --git a/ctags/main/writer-ctags.c b/ctags/main/writer-ctags.c new file mode 100644 index 0000000000..9a8a14419b --- /dev/null +++ b/ctags/main/writer-ctags.c @@ -0,0 +1,292 @@ +/* +* Copyright (c) 1998-2002, Darren Hiebert +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* External interface to entry.c +*/ + +#include "general.h" /* must always come first */ + +#include "entry_p.h" +#include "mio.h" +#include "options_p.h" +#include "parse_p.h" +#include "ptag_p.h" +#include "read.h" +#include "writer_p.h" + + +#define CTAGS_FILE "tags" + + +static int writeCtagsEntry (tagWriter *writer CTAGS_ATTR_UNUSED, + MIO * mio, const tagEntryInfo *const tag); +static int writeCtagsPtagEntry (tagWriter *writer CTAGS_ATTR_UNUSED, + MIO * mio, const ptagDesc *desc, + const char *const fileName, + const char *const pattern, + const char *const parserName); +static void buildCtagsFqTagCache (tagWriter *writer CTAGS_ATTR_UNUSED, tagEntryInfo *const tag); + +struct rejection { + bool rejectedInThisRendering; + bool rejectedInThisInput; +}; + +tagWriter uCtagsWriter = { + .writeEntry = writeCtagsEntry, + .writePtagEntry = writeCtagsPtagEntry, + .preWriteEntry = NULL, + .postWriteEntry = NULL, + .buildFqTagCache = buildCtagsFqTagCache, + .defaultFileName = CTAGS_FILE, +}; + +static void *beginECtagsFile (tagWriter *writer CTAGS_ATTR_UNUSED, MIO * mio CTAGS_ATTR_UNUSED) +{ + static struct rejection rej; + + rej.rejectedInThisInput = false; + + return &rej; +} + +static bool endECTagsFile (tagWriter *writer, MIO * mio CTAGS_ATTR_UNUSED, const char* filename CTAGS_ATTR_UNUSED) +{ + struct rejection *rej = writer->private; + return rej->rejectedInThisInput; +} + +tagWriter eCtagsWriter = { + .writeEntry = writeCtagsEntry, + .writePtagEntry = writeCtagsPtagEntry, + .preWriteEntry = beginECtagsFile, + .postWriteEntry = endECTagsFile, + .buildFqTagCache = buildCtagsFqTagCache, + .defaultFileName = CTAGS_FILE, +}; + +static const char* escapeFieldValue (tagWriter *writer, const tagEntryInfo * tag, fieldType ftype) +{ + bool *reject = NULL; + + if (writer->private) + { + struct rejection * rej = writer->private; + reject = &rej->rejectedInThisRendering; + } + return renderFieldEscaped (writer->type, ftype, tag, NO_PARSER_FIELD, reject); +} + +static int renderExtensionFieldMaybe (tagWriter *writer, int xftype, const tagEntryInfo *const tag, char sep[2], MIO *mio) +{ + if (isFieldEnabled (xftype) && doesFieldHaveValue (xftype, tag)) + { + int len; + len = mio_printf (mio, "%s\t%s:%s", sep, + getFieldName (xftype), + escapeFieldValue (writer, tag, xftype)); + sep[0] = '\0'; + return len; + } + else + return 0; +} + +static int addParserFields (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag) +{ + unsigned int i; + int length = 0; + bool *reject = NULL; + + if (writer->private) + { + struct rejection *rej = writer->private; + reject = &rej->rejectedInThisRendering; + } + + for (i = 0; i < tag->usedParserFields; i++) + { + const tagField *f = getParserField(tag, i); + if (! isFieldEnabled (f->ftype)) + continue; + + length += mio_printf(mio, "\t%s:%s", + getFieldName (f->ftype), + renderFieldEscaped (writer->type, + f->ftype, tag, i, reject)); + } + return length; +} + +static int writeLineNumberEntry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag) +{ + if (Option.lineDirectives) + return mio_printf (mio, "%s", escapeFieldValue (writer, tag, FIELD_LINE_NUMBER)); + else + return mio_printf (mio, "%lu", tag->lineNumber); +} + +static int addExtensionFields (tagWriter *writer, MIO *mio, const tagEntryInfo *const tag) +{ + bool isKindKeyEnabled = isFieldEnabled (FIELD_KIND_KEY); + bool isScopeEnabled = isFieldEnabled (FIELD_SCOPE_KEY); + + const char* const kindKey = isKindKeyEnabled + ?getFieldName (FIELD_KIND_KEY) + :""; + const char* const kindFmt = isKindKeyEnabled + ?"%s\t%s:%s" + :"%s\t%s%s"; + const char* const scopeKey = isScopeEnabled + ?getFieldName (FIELD_SCOPE_KEY) + :""; + const char* const scopeFmt = isScopeEnabled + ?"%s\t%s:%s:%s" + :"%s\t%s%s:%s"; + + char sep [] = {';', '"', '\0'}; + int length = 0; + + const char *str = NULL;; + kindDefinition *kdef = getLanguageKind(tag->langType, tag->kindIndex); + const char kind_letter_str[2] = {kdef->letter, '\0'}; + + if (kdef->name != NULL && (isFieldEnabled (FIELD_KIND_LONG) || + (isFieldEnabled (FIELD_KIND) && kdef->letter == KIND_NULL))) + { + /* Use kind long name */ + str = kdef->name; + } + else if (kdef->letter != KIND_NULL && (isFieldEnabled (FIELD_KIND) || + (isFieldEnabled (FIELD_KIND_LONG) && kdef->name == NULL))) + { + /* Use kind letter */ + str = kind_letter_str; + } + + if (str) + { + length += mio_printf (mio, kindFmt, sep, kindKey, str); + sep [0] = '\0'; + } + + if (isFieldEnabled (FIELD_LINE_NUMBER) && doesFieldHaveValue (FIELD_LINE_NUMBER, tag)) + { + length += mio_printf (mio, "%s\t%s:%ld", sep, + getFieldName (FIELD_LINE_NUMBER), + tag->lineNumber); + sep [0] = '\0'; + } + + length += renderExtensionFieldMaybe (writer, FIELD_LANGUAGE, tag, sep, mio); + + if (isFieldEnabled (FIELD_SCOPE)) + { + const char* k, *v; + + k = escapeFieldValue (writer, tag, FIELD_SCOPE_KIND_LONG); + v = escapeFieldValue (writer, tag, FIELD_SCOPE); + if (k && v) + { + length += mio_printf (mio, scopeFmt, sep, scopeKey, k, v); + sep [0] = '\0'; + } + } + + if (isFieldEnabled (FIELD_TYPE_REF) && doesFieldHaveValue (FIELD_TYPE_REF, tag)) + { + length += mio_printf (mio, "%s\t%s:%s", sep, + getFieldName (FIELD_TYPE_REF), + escapeFieldValue (writer, tag, FIELD_TYPE_REF)); + sep [0] = '\0'; + } + + if (isFieldEnabled (FIELD_FILE_SCOPE) && doesFieldHaveValue (FIELD_FILE_SCOPE, tag)) + { + length += mio_printf (mio, "%s\t%s:", sep, + getFieldName (FIELD_FILE_SCOPE)); + sep [0] = '\0'; + } + + length += renderExtensionFieldMaybe (writer, FIELD_INHERITANCE, tag, sep, mio); + length += renderExtensionFieldMaybe (writer, FIELD_ACCESS, tag, sep, mio); + length += renderExtensionFieldMaybe (writer, FIELD_IMPLEMENTATION, tag, sep, mio); + length += renderExtensionFieldMaybe (writer, FIELD_SIGNATURE, tag, sep, mio); + length += renderExtensionFieldMaybe (writer, FIELD_ROLES, tag, sep, mio); + length += renderExtensionFieldMaybe (writer, FIELD_EXTRAS, tag, sep, mio); + length += renderExtensionFieldMaybe (writer, FIELD_XPATH, tag, sep, mio); + length += renderExtensionFieldMaybe (writer, FIELD_END_LINE, tag, sep, mio); + + return length; +} + +static int writeCtagsEntry (tagWriter *writer, + MIO * mio, const tagEntryInfo *const tag) +{ + long origin = 0; + + if (writer->private) + { + struct rejection *rej = writer->private; + + origin = mio_tell (mio); + rej->rejectedInThisRendering = false; + + } + + int length = mio_printf (mio, "%s\t%s\t", + escapeFieldValue (writer, tag, FIELD_NAME), + escapeFieldValue (writer, tag, FIELD_INPUT_FILE)); + + if (tag->lineNumberEntry) + length += writeLineNumberEntry (writer, mio, tag); + else + length += mio_puts(mio, escapeFieldValue(writer, tag, FIELD_PATTERN)); + + if (includeExtensionFlags ()) + { + length += addExtensionFields (writer, mio, tag); + length += addParserFields (writer, mio, tag); + } + + length += mio_printf (mio, "\n"); + + if (writer->private + && ((struct rejection *)(writer->private))->rejectedInThisRendering) + { + mio_seek (mio, origin, SEEK_SET); + + /* Truncation is needed. */ + ((struct rejection *)(writer->private))->rejectedInThisInput = true; + + length = 0; + } + return length; +} + +static int writeCtagsPtagEntry (tagWriter *writer CTAGS_ATTR_UNUSED, + MIO * mio, const ptagDesc *desc, + const char *const fileName, + const char *const pattern, + const char *const parserName) +{ + return parserName + +#define OPT(X) ((X)?(X):"") + ? mio_printf (mio, "%s%s%s%s\t%s\t%s\n", + PSEUDO_TAG_PREFIX, desc->name, PSEUDO_TAG_SEPARATOR, parserName, + OPT(fileName), OPT(pattern)) + : mio_printf (mio, "%s%s\t%s\t/%s/\n", + PSEUDO_TAG_PREFIX, desc->name, + OPT(fileName), OPT(pattern)); +#undef OPT +} + +static void buildCtagsFqTagCache (tagWriter *writer CTAGS_ATTR_UNUSED, tagEntryInfo *const tag) +{ + escapeFieldValue (writer, tag, FIELD_SCOPE_KIND_LONG); + escapeFieldValue (writer, tag, FIELD_SCOPE); +} diff --git a/ctags/main/writer-etags.c b/ctags/main/writer-etags.c new file mode 100644 index 0000000000..6af0829bd7 --- /dev/null +++ b/ctags/main/writer-etags.c @@ -0,0 +1,133 @@ +/* +* Copyright (c) 1998-2002, Darren Hiebert +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* External interface to entry.c +*/ + +#include "general.h" /* must always come first */ + +#include + +#include "debug.h" +#include "entry_p.h" +#include "mio.h" +#include "options_p.h" +#include "read.h" +#include "routines_p.h" +#include "vstring.h" +#include "writer_p.h" + + +#define ETAGS_FILE "TAGS" + + +static int writeEtagsEntry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag); +static void *beginEtagsFile (tagWriter *writer, MIO * mio); +static bool endEtagsFile (tagWriter *writer, MIO * mio, const char* filename); + +tagWriter etagsWriter = { + .writeEntry = writeEtagsEntry, + .writePtagEntry = NULL, + .preWriteEntry = beginEtagsFile, + .postWriteEntry = endEtagsFile, + .defaultFileName = ETAGS_FILE, +}; + +struct sEtags { + char *name; + MIO *mio; + size_t byteCount; + vString *vLine; +}; + + + +static void *beginEtagsFile (tagWriter *writer CTAGS_ATTR_UNUSED, MIO *mio CTAGS_ATTR_UNUSED) +{ + static struct sEtags etags = { NULL, NULL, 0, NULL }; + + etags.mio = tempFile ("w+b", &etags.name); + etags.byteCount = 0; + etags.vLine = vStringNew (); + return &etags; +} + +static bool endEtagsFile (tagWriter *writer, + MIO *mainfp, const char *filename) +{ + const char *line; + struct sEtags *etags = writer->private; + + mio_printf (mainfp, "\f\n%s,%ld\n", filename, (long) etags->byteCount); + abort_if_ferror (mainfp); + + if (etags->mio != NULL) + { + mio_rewind (etags->mio); + + while ((line = readLineRaw (etags->vLine, etags->mio)) != NULL) + mio_puts (mainfp, line); + + vStringDelete (etags->vLine); + mio_free (etags->mio); + remove (etags->name); + eFree (etags->name); + etags->vLine = NULL; + etags->mio = NULL; + etags->name = NULL; + } + return false; +} + +static int writeEtagsEntry (tagWriter *writer, + MIO * mio, const tagEntryInfo *const tag) +{ + int length; + struct sEtags *etags = writer->private; + + mio = etags->mio; + + if (tag->isFileEntry) + length = mio_printf (mio, "\177%s\001%lu,0\n", + tag->name, tag->lineNumber); + else + { + size_t len; + long seekValue; + char *const line = + readLineFromBypassForTag (etags->vLine, tag, &seekValue); + if (line == NULL || line [0] == '\0') + return 0; + + len = strlen (line); + + if (tag->truncateLineAfterTag) + truncateTagLineAfterTag (line, tag->name, true); + else if (line [len - 1] == '\n') + line [--len] = '\0'; + + if (Option.patternLengthLimit > 0 && Option.patternLengthLimit < len) + { + unsigned int truncationLength = Option.patternLengthLimit; + + /* don't cut in the middle of a UTF-8 character, but don't allow + * for more than one extra character in case it actually wasn't + * UTF-8. See also entry.c:appendInputLine() */ + while (truncationLength < len && + truncationLength < Option.patternLengthLimit + 3 && + (((unsigned char) line[truncationLength]) & 0xc0) == 0x80) + truncationLength++; + + line [truncationLength] = '\0'; + } + + length = mio_printf (mio, "%s\177%s\001%lu,%ld\n", line, + tag->name, tag->lineNumber, seekValue); + } + etags->byteCount += length; + + return length; +} diff --git a/ctags/main/writer-json.c b/ctags/main/writer-json.c new file mode 100644 index 0000000000..789eecd3db --- /dev/null +++ b/ctags/main/writer-json.c @@ -0,0 +1,248 @@ +/* +* Copyright (c) 2016, Aman Gupta +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* External interface to entry.c +*/ + +#include "general.h" /* must always come first */ + +#include "debug.h" +#include "entry_p.h" +#include "mio.h" +#include "options_p.h" +#include "read.h" +#include "ptag_p.h" +#include "writer_p.h" + + +#include + +#ifdef HAVE_JANSSON +#include + +#ifndef json_boolean /* compat with jansson < 2.4 */ +#define json_boolean(val) ((val) ? json_true() : json_false()) +#endif + + +static int writeJsonEntry (tagWriter *writer CTAGS_ATTR_UNUSED, + MIO * mio, const tagEntryInfo *const tag); + +static int writeJsonPtagEntry (tagWriter *writer CTAGS_ATTR_UNUSED, + MIO * mio, const ptagDesc *desc, + const char *const fileName, + const char *const pattern, + const char *const parserName); +static void buildJsonFqTagCache (tagWriter *writer, tagEntryInfo *const tag); + +tagWriter jsonWriter = { + .writeEntry = writeJsonEntry, + .writePtagEntry = writeJsonPtagEntry, + .preWriteEntry = NULL, + .postWriteEntry = NULL, + .buildFqTagCache = buildJsonFqTagCache, + .defaultFileName = NULL, +}; + + +static json_t* escapeFieldValue (const tagEntryInfo * tag, fieldType ftype, bool returnEmptyStringAsNoValue) +{ + const char *str = renderFieldEscaped (jsonWriter.type, ftype, tag, NO_PARSER_FIELD, NULL); + if (str) + { + unsigned int dt = getFieldDataType(ftype); + if (dt & FIELDTYPE_STRING) + { + if (dt & FIELDTYPE_BOOL && str[0] == '\0') + return json_false(); + else + return json_string (str); + } + else if (dt & FIELDTYPE_INTEGER) + { + long tmp; + + if (strToLong (str, 10, &tmp)) + return json_integer (tmp); + else + return NULL; + } + else if (dt & FIELDTYPE_BOOL) + { + /* TODO: This must be fixed when new boolean field is added. + Currently only `file:' field use this. */ + return json_boolean (strcmp ("-", str)); /* "-" -> false */ + } + AssertNotReached (); + return NULL; + } + else if (returnEmptyStringAsNoValue) + return json_false(); + else + return NULL; +} + +static void renderExtensionFieldMaybe (int xftype, const tagEntryInfo *const tag, json_t *response) +{ + const char *fname = getFieldName (xftype); + + if (fname && isFieldRenderable (xftype) && isFieldEnabled (xftype) && doesFieldHaveValue (xftype, tag)) + { + switch (xftype) + { + case FIELD_LINE_NUMBER: + json_object_set_new (response, fname, + json_integer (tag->lineNumber)); + break; + case FIELD_FILE_SCOPE: + json_object_set_new (response, fname, + json_boolean(1)); + break; + default: + json_object_set_new (response, fname, + escapeFieldValue (tag, xftype, false)); + } + } +} + +static void addParserFields (json_t *response, const tagEntryInfo *const tag) +{ + unsigned int i; + unsigned int ftype; + + for (i = 0; i < tag->usedParserFields; i++) + { + ftype = tag->parserFields [i].ftype; + if (! isFieldEnabled (ftype)) + continue; + + json_object_set_new (response, getFieldName (ftype), json_string (tag->parserFields [i].value)); + } +} + +static void addExtensionFields (json_t *response, const tagEntryInfo *const tag) +{ + int k; + + /* FIELD_KIND has no name; getFieldName (FIELD_KIND) returns NULL. + FIELD_KIND_LONG does, too. + That cannot be changed to keep the compatibility of tags file format. + Use FIELD_KIND_KEY instead */ + if (isFieldEnabled (FIELD_KIND) || isFieldEnabled (FIELD_KIND_LONG)) + enableField (FIELD_KIND_KEY, true, false); + + /* FIELD_SCOPE has no name; getFieldName (FIELD_KIND_KEY) returns NULL. + That cannot be changed to keep the compatibility of tags file format. + Use FIELD_SCOPE_KEY and FIELD_SCOPE_KIND_LONG instead. */ + if (isFieldEnabled (FIELD_SCOPE)) + { + enableField (FIELD_SCOPE_KEY, true, false); + enableField (FIELD_SCOPE_KIND_LONG, true, false); + } + + for (k = FIELD_EXTENSION_START; k <= FIELD_BUILTIN_LAST; k++) + renderExtensionFieldMaybe (k, tag, response); +} + +static int writeJsonEntry (tagWriter *writer CTAGS_ATTR_UNUSED, + MIO * mio, const tagEntryInfo *const tag) +{ + json_t *pat = escapeFieldValue(tag, FIELD_PATTERN, true); + json_t *response = json_pack ("{ss ss ss sO}", + "_type", "tag", + "name", tag->name, + "path", tag->sourceFileName, + "pattern", pat); + json_decref (pat); + + if (includeExtensionFlags ()) + { + addExtensionFields (response, tag); + addParserFields (response, tag); + } + + int length = 0; + char *buf = json_dumps (response, JSON_PRESERVE_ORDER); + if (!buf) + goto out; + + length = mio_printf (mio, "%s\n", buf); + + free (buf); + out: + json_decref (response); + + return length; +} + +static void buildJsonFqTagCache (tagWriter *writer, tagEntryInfo *const tag) +{ + renderFieldEscaped (writer->type, FIELD_SCOPE_KIND_LONG, tag, + NO_PARSER_FIELD, NULL); + renderFieldEscaped (writer->type, FIELD_SCOPE, tag, + NO_PARSER_FIELD, NULL); +} + +static int writeJsonPtagEntry (tagWriter *writer CTAGS_ATTR_UNUSED, + MIO * mio, const ptagDesc *desc, + const char *const fileName, + const char *const pattern, + const char *const parserName) +{ +#define OPT(X) ((X)?(X):"") + json_t *response; + + if (parserName) + { + response = json_pack ("{ss ss ss ss ss}", + "_type", "ptag", + "name", desc->name, + "parserName", parserName, + "path", OPT(fileName), + "pattern", OPT(pattern)); + } + else + { + response = json_pack ("{ss ss ss ss}", + "_type", "ptag", + "name", desc->name, + "path", OPT(fileName), + "pattern", OPT(pattern)); + } + + char *buf = json_dumps (response, JSON_PRESERVE_ORDER); + int length = mio_printf (mio, "%s\n", buf); + free (buf); + json_decref (response); + + return length; +#undef OPT +} + +extern bool ptagMakeJsonOutputVersion (ptagDesc *desc, void *data CTAGS_ATTR_UNUSED) +{ + return writePseudoTag (desc, + "0.0", + "in development", + NULL); +} + +#else /* HAVE_JANSSON */ + +tagWriter jsonWriter = { + .writeEntry = NULL, + .writePtagEntry = NULL, + .preWriteEntry = NULL, + .postWriteEntry = NULL, + .defaultFileName = "-", +}; + +extern bool ptagMakeJsonOutputVersion (ptagDesc *desc, void *data CTAGS_ATTR_UNUSED) +{ + return false; +} + +#endif diff --git a/ctags/main/writer-xref.c b/ctags/main/writer-xref.c new file mode 100644 index 0000000000..86b16bceb5 --- /dev/null +++ b/ctags/main/writer-xref.c @@ -0,0 +1,72 @@ +/* +* Copyright (c) 1998-2002, Darren Hiebert +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* External interface to entry.c +*/ + +#include "general.h" /* must always come first */ + +#include "entry.h" +#include "fmt_p.h" +#include "mio.h" +#include "options_p.h" +#include "writer_p.h" + + +static int writeXrefEntry (tagWriter *writer CTAGS_ATTR_UNUSED, + MIO * mio, const tagEntryInfo *const tag); +static void buildXrefFqTagCache (tagWriter *writer, tagEntryInfo *const tag); + +tagWriter xrefWriter = { + .writeEntry = writeXrefEntry, + .writePtagEntry = NULL, + .preWriteEntry = NULL, + .postWriteEntry = NULL, + .buildFqTagCache = buildXrefFqTagCache, + .defaultFileName = NULL, +}; + +static int writeXrefEntry (tagWriter *writer CTAGS_ATTR_UNUSED, + MIO * mio, const tagEntryInfo *const tag) +{ + int length; + static fmtElement *fmt1; + static fmtElement *fmt2; + + if (Option.customXfmt) + length = fmtPrint (Option.customXfmt, mio, tag); + else + { + if (tag->isFileEntry) + return 0; + + if (Option.tagFileFormat == 1) + { + if (fmt1 == NULL) + fmt1 = fmtNew ("%-16N %4n %-16F %C"); + length = fmtPrint (fmt1, mio, tag); + } + else + { + if (fmt2 == NULL) + fmt2 = fmtNew ("%-16N %-10K %4n %-16F %C"); + length = fmtPrint (fmt2, mio, tag); + } + } + + mio_putc (mio, '\n'); + length++; + + return length; +} + +static void buildXrefFqTagCache (tagWriter *writer, tagEntryInfo *const tag) +{ + renderFieldEscaped (writer->type, FIELD_SCOPE_KIND_LONG, tag, + NO_PARSER_FIELD, NULL); + renderFieldEscaped (writer->type, FIELD_SCOPE, tag, + NO_PARSER_FIELD, NULL); +} diff --git a/ctags/main/writer.c b/ctags/main/writer.c index 3aca1320a1..364c36396d 100644 --- a/ctags/main/writer.c +++ b/ctags/main/writer.c @@ -11,65 +11,11 @@ #include "entry_p.h" #include "writer_p.h" -/* GEANY_CTAGS_DIFF */ -/* Dummy definitions of writers as we don't need them */ - -tagWriter uCtagsWriter = { - .writeEntry = NULL, - .writePtagEntry = NULL, - .preWriteEntry = NULL, - .postWriteEntry = NULL, - .buildFqTagCache = NULL, - .defaultFileName = NULL, - .private = NULL, - .type = WRITER_DEFAULT, -}; - -tagWriter eCtagsWriter = { - .writeEntry = NULL, - .writePtagEntry = NULL, - .preWriteEntry = NULL, - .postWriteEntry = NULL, - .buildFqTagCache = NULL, - .defaultFileName = NULL, - .private = NULL, - .type = WRITER_DEFAULT, -}; - -tagWriter etagsWriter = { - .writeEntry = NULL, - .writePtagEntry = NULL, - .preWriteEntry = NULL, - .postWriteEntry = NULL, - .buildFqTagCache = NULL, - .defaultFileName = NULL, - .private = NULL, - .type = WRITER_DEFAULT, -}; - -tagWriter xrefWriter = { - .writeEntry = NULL, - .writePtagEntry = NULL, - .preWriteEntry = NULL, - .postWriteEntry = NULL, - .buildFqTagCache = NULL, - .defaultFileName = NULL, - .private = NULL, - .type = WRITER_DEFAULT, -}; - -tagWriter jsonWriter = { - .writeEntry = NULL, - .writePtagEntry = NULL, - .preWriteEntry = NULL, - .postWriteEntry = NULL, - .buildFqTagCache = NULL, - .defaultFileName = NULL, - .private = NULL, - .type = WRITER_DEFAULT, -}; - -/* GEANY_CTAGS_DIFF_END */ +extern tagWriter uCtagsWriter; +extern tagWriter eCtagsWriter; +extern tagWriter etagsWriter; +extern tagWriter xrefWriter; +extern tagWriter jsonWriter; static tagWriter *writerTable [WRITER_COUNT] = { [WRITER_U_CTAGS] = &uCtagsWriter, From 41078c824457cdd9b786f07685a2b165d91e0917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Tue, 23 Apr 2019 23:18:28 +0200 Subject: [PATCH 06/46] Move bufferOpen() to parse.c to eliminate diff in read.c/h --- ctags/main/parse.c | 12 ++++++++++++ ctags/main/read.c | 18 ------------------ ctags/main/read.h | 4 ---- 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/ctags/main/parse.c b/ctags/main/parse.c index 04e3d4d38d..a834237317 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -3308,6 +3308,18 @@ static bool createTagsWithFallback ( #else +static bool bufferOpen (const char *const fileName, const langType language, + unsigned char *buffer, size_t buffer_size) +{ + MIO *mio; + bool opened; + + mio = mio_new_memory (buffer, buffer_size, NULL, NULL); + opened = openInputFile (fileName, language, mio); + mio_free (mio); + return opened; +} + extern void createTagsWithFallback(unsigned char *buffer, size_t bufferSize, const char *fileName, const langType language, tagEntryFunction tagCallback, passStartCallback passCallback, diff --git a/ctags/main/read.c b/ctags/main/read.c index c2b1400e69..657f8c1050 100644 --- a/ctags/main/read.c +++ b/ctags/main/read.c @@ -715,24 +715,6 @@ extern bool openInputFile (const char *const fileName, const langType language, return opened; } -#ifdef GEANY_CTAGS_LIB -/* The user should take care of allocate and free the buffer param. - * This func is NOT THREAD SAFE. - * The user should not tamper with the buffer while this func is executing. - */ -extern bool bufferOpen (const char *const fileName, const langType language, - unsigned char *buffer, size_t buffer_size) -{ - MIO *mio; - bool opened; - - mio = mio_new_memory (buffer, buffer_size, NULL, NULL); - opened = openInputFile (fileName, language, mio); - mio_free (mio); - return opened; -} -#endif /* GEANY_CTAGS_LIB */ - extern void resetInputFile (const langType language) { Assert (File.mio); diff --git a/ctags/main/read.h b/ctags/main/read.h index a2d0865658..671606e9b0 100644 --- a/ctags/main/read.h +++ b/ctags/main/read.h @@ -84,10 +84,6 @@ extern const unsigned char *getInputFileData (size_t *size); internally. The 3rd argument is introduced for reusing mio object created in parser guessing stage. */ extern bool openInputFile (const char *const fileName, const langType language, MIO *mio); -#ifdef GEANY_CTAGS_LIB -extern bool bufferOpen (const char *const fileName, const langType language, - unsigned char *buffer, size_t buffer_size); -#endif /* GEANY_CTAGS_LIB */ extern MIO *getMio (const char *const fileName, const char *const openMode, bool memStreamRequired); extern void resetInputFile (const langType language); From dcd1dfc73e954e8546079ad1a18135a824a204ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Tue, 23 Apr 2019 23:19:31 +0200 Subject: [PATCH 07/46] Reduce diff size in fileNameMatched() --- ctags/main/strlist.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/ctags/main/strlist.c b/ctags/main/strlist.c index 5ca4900146..32d960bef7 100644 --- a/ctags/main/strlist.c +++ b/ctags/main/strlist.c @@ -14,7 +14,7 @@ #include /* GEANY_CTAGS_DIFF */ -/* +/* We don't have fnmatch in Geany #include */ /* GEANY_CTAGS_DIFF_END */ @@ -220,6 +220,22 @@ static bool fileNameMatched ( const vString* const vpattern, const char* const fileName) { /* GEANY_CTAGS_DIFF */ +/* We don't have fnmatch in Geany + const char* const pattern = vStringValue (vpattern); + +#ifdef CASE_INSENSITIVE_FILENAMES + { + char* const p = newUpperString (pattern); + char* const f = newUpperString (fileName); + bool r = (fnmatch (p, f, 0) == 0); + eFree (f); + eFree (p); + return r; + } +#else + return (fnmatch (pattern, fileName, 0) == 0); +#endif +*/ return false; /* GEANY_CTAGS_DIFF_END */ } From b1fa3c1e4efcf0fe224036bdda944c7426e98898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Tue, 23 Apr 2019 23:32:52 +0200 Subject: [PATCH 08/46] Reduce number of diffs in main.c --- ctags/main/main.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/ctags/main/main.c b/ctags/main/main.c index 991003a9fd..c3542460ef 100644 --- a/ctags/main/main.c +++ b/ctags/main/main.c @@ -91,17 +91,13 @@ * DATA DEFINITIONS */ static struct { long files, lines, bytes; } Totals = { 0, 0, 0 }; -#ifndef GEANY_CTAGS_LIB static mainLoopFunc mainLoop; static void *mainData; -#endif /* GEANY_CTAGS_LIB */ /* * FUNCTION PROTOTYPES */ -#ifndef GEANY_CTAGS_LIB static bool createTagsForEntry (const char *const entryName); -#endif /* GEANY_CTAGS_LIB */ /* * FUNCTION DEFINITIONS @@ -131,8 +127,6 @@ extern bool isDestinationStdout (void) return toStdout; } -#ifndef GEANY_CTAGS_LIB - #if defined (HAVE_OPENDIR) static bool recurseUsingOpendir (const char *const dirName) { @@ -648,7 +642,11 @@ static void sanitizeEnviron (void) * Start up code */ +#ifndef GEANY_CTAGS_LIB extern int main (int argc CTAGS_ATTR_UNUSED, char **argv) +#else +extern int ctags_main (int argc CTAGS_ATTR_UNUSED, char **argv) +#endif /* GEANY_CTAGS_LIB */ { cookedArgs *args; @@ -709,4 +707,3 @@ extern int main (int argc CTAGS_ATTR_UNUSED, char **argv) exit (0); return 0; } -#endif /* GEANY_CTAGS_LIB */ From fb9b9df7d4942cc241b72b860c7173cc608b709d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Tue, 23 Apr 2019 23:34:06 +0200 Subject: [PATCH 09/46] Use upstream mbcs.h --- ctags/main/mbcs.h | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/ctags/main/mbcs.h b/ctags/main/mbcs.h index cb8848cc1b..ceb0d0966f 100644 --- a/ctags/main/mbcs.h +++ b/ctags/main/mbcs.h @@ -1 +1,22 @@ -/* Dummy header - only included by some parsers */ +/* +* $Id$ +* +* Copyright (c) 2015, vim-jp +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* This module contains functions for checking multibyte character set. +*/ + +#include "general.h" /* must always come first */ +#include "vstring.h" + +#ifdef HAVE_ICONV + +extern bool isConverting (void); +extern bool openConverter (const char*, const char*); +extern bool convertString (vString *const); +extern void closeConverter (void); + +#endif /* HAVE_ICONV */ From 853a0eeaf0a7848df6f3daf9d5d334fd477e212e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 24 Apr 2019 00:52:37 +0200 Subject: [PATCH 10/46] Reduce number of diffs in parse.c/h 1. Define empty XML_PARSER_LIST and YAML_PARSER_LIST in parsers.h so these don't have to be handled in parse.c 2. Rename Geany version of createTagsWithFallback1() to createTagsWithFallback1Geany() so the original implementation can stay and we don't have to comment-out unused functions. 3. Move diffs together so they aren't spread across the code so much. --- ctags/main/ctags-api.c | 2 +- ctags/main/parse.c | 103 ++++++++++++++++++----------------------- ctags/main/parse.h | 4 +- ctags/main/parsers.h | 3 ++ 4 files changed, 52 insertions(+), 60 deletions(-) diff --git a/ctags/main/ctags-api.c b/ctags/main/ctags-api.c index e3a29e6bb4..e456b90a15 100644 --- a/ctags/main/ctags-api.c +++ b/ctags/main/ctags-api.c @@ -75,7 +75,7 @@ extern void ctagsParse(unsigned char *buffer, size_t bufferSize, return; } - createTagsWithFallback(buffer, bufferSize, fileName, language, + createTagsWithFallbackGeany(buffer, bufferSize, fileName, language, tagCallback, passCallback, userData); } diff --git a/ctags/main/parse.c b/ctags/main/parse.c index a834237317..43a07dd165 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -92,9 +92,7 @@ typedef struct sParserObject { */ static void lazyInitialize (langType language); -#ifndef GEANY_CTAGS_LIB static void addParserPseudoTags (langType language); -#endif /* GEANY_CTAGS_LIB */ static void installKeywordTable (const langType language); static void installTagRegexTable (const langType language); static void installTagXpathTable (const langType language); @@ -113,16 +111,14 @@ static parserDefinitionFunc* BuiltInParsers[] = { CTagsSelfTestParser, #endif /* GEANY_CTAGS_LIB */ PARSER_LIST, -#ifndef GEANY_CTAGS_LIB XML_PARSER_LIST #ifdef HAVE_LIBXML , -#endif /* GEANY_CTAGS_LIB */ +#endif YAML_PARSER_LIST #ifdef HAVE_LIBYAML , #endif -#endif }; static parserObject* LanguageTable = NULL; static unsigned int LanguageCount = 0; @@ -3107,7 +3103,6 @@ static subparser* teardownLanguageSubparsersInUse (const langType language) return teardownSubparsersInUse ((LanguageTable + language)->slaveControlBlock); } -#ifndef GEANY_CTAGS_LIB static bool createTagsWithFallback1 (const langType language, langType *exclusive_subparser) { @@ -3179,9 +3174,10 @@ static bool createTagsWithFallback1 (const langType language, return tagFileResized; } -#else +#ifdef GEANY_CTAGS_LIB -static bool createTagsWithFallback1 (const langType language, +/* keep in sync with createTagsWithFallback1() above */ +static bool createTagsWithFallback1Geany (const langType language, passStartCallback passCallback, void *userData) { int lastPromise = getLastPromise (); @@ -3235,6 +3231,47 @@ static bool createTagsWithFallback1 (const langType language, return false; } + +static bool bufferOpen (const char *const fileName, const langType language, + unsigned char *buffer, size_t buffer_size) +{ + MIO *mio; + bool opened; + + mio = mio_new_memory (buffer, buffer_size, NULL, NULL); + opened = openInputFile (fileName, language, mio); + mio_free (mio); + return opened; +} + +extern void createTagsWithFallbackGeany(unsigned char *buffer, size_t bufferSize, + const char *fileName, const langType language, + tagEntryFunction tagCallback, passStartCallback passCallback, + void *userData) +{ + if ((!buffer && openInputFile (fileName, language, NULL)) || + (buffer && bufferOpen (fileName, language, buffer, bufferSize))) + { + /* keep in sync with parseFileWithMio() and createTagsWithFallback() */ + setupAnon (); + initParserTrashBox (); + setTagEntryFunction(tagCallback, userData); + createTagsWithFallback1Geany (language, passCallback, userData); + forcePromises (); + closeInputFile (); + finiParserTrashBox (); + teardownAnon (); + } + else + error (WARNING, "Unable to open %s", fileName); +} + +extern const parserDefinition *getParserDefinition (langType language) +{ + Assert (0 <= language && language < (int) LanguageCount); + return LanguageTable[language].def; +} + #endif /* GEANY_CTAGS_LIB */ extern bool runParserInNarrowedInputStream (const langType language, @@ -3279,7 +3316,6 @@ extern bool runParserInNarrowedInputStream (const langType language, } -#ifndef GEANY_CTAGS_LIB static bool createTagsWithFallback ( const char *const fileName, const langType language, MIO *mio) @@ -3306,49 +3342,6 @@ static bool createTagsWithFallback ( return tagFileResized; } -#else - -static bool bufferOpen (const char *const fileName, const langType language, - unsigned char *buffer, size_t buffer_size) -{ - MIO *mio; - bool opened; - - mio = mio_new_memory (buffer, buffer_size, NULL, NULL); - opened = openInputFile (fileName, language, mio); - mio_free (mio); - return opened; -} - -extern void createTagsWithFallback(unsigned char *buffer, size_t bufferSize, - const char *fileName, const langType language, - tagEntryFunction tagCallback, passStartCallback passCallback, - void *userData) -{ - if ((!buffer && openInputFile (fileName, language, NULL)) || - (buffer && bufferOpen (fileName, language, buffer, bufferSize))) - { - setupAnon (); - initParserTrashBox (); - setTagEntryFunction(tagCallback, userData); - createTagsWithFallback1 (language, passCallback, userData); - forcePromises (); - closeInputFile (); - finiParserTrashBox (); - teardownAnon (); - } - else - error (WARNING, "Unable to open %s", fileName); -} - -extern const parserDefinition *getParserDefinition (langType language) -{ - Assert (0 <= language && language < (int) LanguageCount); - return LanguageTable[language].def; -} - -#endif /* GEANY_CTAGS_LIB */ - static void printGuessedParser (const char* const fileName, langType language) { const char *parserName; @@ -3428,7 +3421,6 @@ extern const char *getLanguageEncoding (const langType language) } #endif -#ifndef GEANY_CTAGS_LIB static void addParserPseudoTags (langType language) { parserObject *parser = LanguageTable + language; @@ -3440,7 +3432,6 @@ static void addParserPseudoTags (langType language) parser->pseudoTagPrinted = 1; } } -#endif /* GEANY_CTAGS_LIB */ extern bool doesParserRequireMemoryStream (const langType language) { @@ -3523,9 +3514,7 @@ extern bool parseFileWithMio (const char *const fileName, MIO *mio) initParserTrashBox (); -#ifndef GEANY_CTAGS_LIB - tagFileResized = createTagsWithFallback (fileName, language, mio); -#endif /* GEANY_CTAGS_LIB */ + tagFileResized = createTagsWithFallback (fileName, language, req.mio); finiParserTrashBox (); diff --git a/ctags/main/parse.h b/ctags/main/parse.h index 52a2b1d866..0bd47f5cf9 100644 --- a/ctags/main/parse.h +++ b/ctags/main/parse.h @@ -18,7 +18,6 @@ #include "kind.h" #include "lregex.h" #include "lxpath.h" -#include "ctags-api.h" /* * MACROS @@ -135,7 +134,8 @@ extern kindDefinition* getLanguageKindForLetter (const langType language, char k extern void initializeParser (langType language); #ifdef GEANY_CTAGS_LIB -extern void createTagsWithFallback(unsigned char *buffer, size_t bufferSize, +#include "ctags-api.h" +extern void createTagsWithFallbackGeany(unsigned char *buffer, size_t bufferSize, const char *fileName, const langType language, tagEntryFunction tagCallback, passStartCallback passCallback, void *userData); diff --git a/ctags/main/parsers.h b/ctags/main/parsers.h index 254278fef4..08acf3f597 100644 --- a/ctags/main/parsers.h +++ b/ctags/main/parsers.h @@ -67,4 +67,7 @@ ZephirParser, \ PowerShellParser +#define XML_PARSER_LIST +#define YAML_PARSER_LIST + #endif /* CTAGS_MAIN_PARSERS_H */ From 9da30f961ee13be65ecfb9dff5d48d59201aacd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 24 Apr 2019 01:05:42 +0200 Subject: [PATCH 11/46] Move diffs together in entry.c/h --- ctags/main/entry.c | 25 +++++++++++-------------- ctags/main/entry.h | 2 +- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/ctags/main/entry.c b/ctags/main/entry.c index f3ee730502..6e49c51ec5 100644 --- a/ctags/main/entry.c +++ b/ctags/main/entry.c @@ -122,11 +122,6 @@ static tagFile TagFile = { static bool TagsToStdout = false; -#ifdef GEANY_CTAGS_LIB -static tagEntryFunction TagEntryFunction = NULL; -static void *TagEntryUserData = NULL; -#endif /* GEANY_CTAGS_LIB */ - /* * FUNCTION PROTOTYPES */ @@ -1217,8 +1212,17 @@ static bool isTagWritable(const tagEntryInfo *const tag) return true; } - #ifdef GEANY_CTAGS_LIB + +static tagEntryFunction TagEntryFunction = NULL; +static void *TagEntryUserData = NULL; + +extern void setTagEntryFunction(tagEntryFunction entry_function, void *user_data) +{ + TagEntryFunction = entry_function; + TagEntryUserData = user_data; +} + static void initCtagsTag(ctagsTag *tag, const tagEntryInfo *info) { tag->name = info->name; @@ -1233,6 +1237,7 @@ static void initCtagsTag(ctagsTag *tag, const tagEntryInfo *info) tag->lineNumber = info->lineNumber; tag->lang = info->langType; } + #endif /* GEANY_CTAGS_LIB */ static void writeTagEntry (const tagEntryInfo *const tag, bool checkingNeeded) @@ -1711,11 +1716,3 @@ extern const char* getTagFileDirectory (void) { return TagFile.directory; } - -#ifdef GEANY_CTAGS_LIB -extern void setTagEntryFunction(tagEntryFunction entry_function, void *user_data) -{ - TagEntryFunction = entry_function; - TagEntryUserData = user_data; -} -#endif /* GEANY_CTAGS_LIB */ diff --git a/ctags/main/entry.h b/ctags/main/entry.h index 2456111dd4..4920058ca4 100644 --- a/ctags/main/entry.h +++ b/ctags/main/entry.h @@ -20,7 +20,6 @@ #include "field.h" #include "xtag.h" #include "mio.h" -#include "ctags-api.h" /* * MACROS @@ -152,6 +151,7 @@ CTAGS_INLINE roleBitsType makeRoleBit(int roleIndex) } #ifdef GEANY_CTAGS_LIB +#include "ctags-api.h" extern void setTagEntryFunction(tagEntryFunction entry_function, void *user_data); #endif /* GEANY_CTAGS_LIB */ From f6139d3b26e3bcfe847f94a9546636c37185fad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 24 Apr 2019 01:15:23 +0200 Subject: [PATCH 12/46] Move isIgnoreToken() to lcpp.c/h which we don't sync with uctags --- ctags/main/lcpp.c | 67 ++++++++++++++++++++++++++++++++++++++++ ctags/main/lcpp.h | 4 +++ ctags/main/options.c | 73 +------------------------------------------- ctags/main/options.h | 6 ---- ctags/parsers/c.c | 2 +- 5 files changed, 73 insertions(+), 79 deletions(-) diff --git a/ctags/main/lcpp.c b/ctags/main/lcpp.c index e77312cdac..886141a5ee 100644 --- a/ctags/main/lcpp.c +++ b/ctags/main/lcpp.c @@ -1017,3 +1017,70 @@ extern void cppClearSignature (void) signature = vStringNewOrClear (signature); collectingSignature = false; } + +/* tags_ignore is a NULL-terminated array of strings, read from ~/.config/geany/ignore.tags. + * This file contains a space or newline separated list of symbols which should be ignored + * by the C/C++ parser, see -I command line option of ctags for details. */ +char **c_tags_ignore = NULL; + +/* Determines whether or not "name" should be ignored, per the ignore list. + */ +extern bool cppIsIgnoreToken (const char *const name, + bool *const pIgnoreParens, + const char **const replacement) +{ + bool result = false; + + if (c_tags_ignore != NULL) + { + const size_t nameLen = strlen (name); + unsigned int i; + unsigned int len = 0; + vString *token = vStringNew(); + + while (c_tags_ignore[len]) + len++; + + if (pIgnoreParens != NULL) + *pIgnoreParens = false; + + for (i = 0 ; i < len ; ++i) + { + size_t tokenLen; + + vStringCopyS (token, c_tags_ignore[i]); + tokenLen = vStringLength (token); + + if (tokenLen >= 2 && vStringChar (token, tokenLen - 1) == '*' && + strncmp (vStringValue (token), name, tokenLen - 1) == 0) + { + result = true; + break; + } + if (strncmp (vStringValue (token), name, nameLen) == 0) + { + if (nameLen == tokenLen) + { + result = true; + break; + } + else if (tokenLen == nameLen + 1 && + vStringChar (token, tokenLen - 1) == '+') + { + result = true; + if (pIgnoreParens != NULL) + *pIgnoreParens = true; + break; + } + else if (vStringChar (token, nameLen) == '=') + { + if (replacement != NULL) + *replacement = vStringValue (token) + nameLen + 1; + break; + } + } + } + vStringDelete (token); + } + return result; +} diff --git a/ctags/main/lcpp.h b/ctags/main/lcpp.h index 86760a916c..73683dc3a4 100644 --- a/ctags/main/lcpp.h +++ b/ctags/main/lcpp.h @@ -77,4 +77,8 @@ extern void cppStartCollectingSignature (void); extern void cppStopCollectingSignature (void); extern void cppClearSignature (void); +extern bool cppIsIgnoreToken (const char *const name, + bool *const pIgnoreParens, + const char **const replacement); + #endif /* CTAGS_MAIN_GET_H */ diff --git a/ctags/main/options.c b/ctags/main/options.c index 4d79788779..b339fc1db8 100644 --- a/ctags/main/options.c +++ b/ctags/main/options.c @@ -3386,16 +3386,14 @@ extern void previewFirstOption (cookedArgs* const args) } } -#ifndef GEANY_CTAGS_LIB +#if defined(HAVE_SCANDIR) || defined (HAVE_DIRENT_H) || defined (_MSC_VER) static void parseConfigurationFileOptionsInDirectoryWithLeafname (const char* directory, const char* leafname) { char* pathname = combinePathAndFile (directory, leafname); parseFileOptions (pathname); eFree (pathname); } -#endif /* GEANY_CTAGS_LIB */ -#if defined(HAVE_SCANDIR) || defined (HAVE_DIRENT_H) || defined (_MSC_VER) static int ignore_dot_file(const struct dirent* dent) { /* Ignore a file which name is started from dot. */ @@ -3747,72 +3745,3 @@ extern bool canUseLineNumberAsLocator (void) { return (Option.locate != EX_PATTERN); } - -/* GEANY_CTAGS_DIFF */ -/* tags_ignore is a NULL-terminated array of strings, read from ~/.config/geany/ignore.tags. - * This file contains a space or newline separated list of symbols which should be ignored - * by the C/C++ parser, see -I command line option of ctags for details. */ -char **c_tags_ignore = NULL; - -/* Determines whether or not "name" should be ignored, per the ignore list. - */ -extern bool isIgnoreToken (const char *const name, - bool *const pIgnoreParens, - const char **const replacement) -{ - bool result = false; - - if (c_tags_ignore != NULL) - { - const size_t nameLen = strlen (name); - unsigned int i; - unsigned int len = 0; - vString *token = vStringNew(); - - while (c_tags_ignore[len]) - len++; - - if (pIgnoreParens != NULL) - *pIgnoreParens = false; - - for (i = 0 ; i < len ; ++i) - { - size_t tokenLen; - - vStringCopyS (token, c_tags_ignore[i]); - tokenLen = vStringLength (token); - - if (tokenLen >= 2 && vStringChar (token, tokenLen - 1) == '*' && - strncmp (vStringValue (token), name, tokenLen - 1) == 0) - { - result = true; - break; - } - if (strncmp (vStringValue (token), name, nameLen) == 0) - { - if (nameLen == tokenLen) - { - result = true; - break; - } - else if (tokenLen == nameLen + 1 && - vStringChar (token, tokenLen - 1) == '+') - { - result = true; - if (pIgnoreParens != NULL) - *pIgnoreParens = true; - break; - } - else if (vStringChar (token, nameLen) == '=') - { - if (replacement != NULL) - *replacement = vStringValue (token) + nameLen + 1; - break; - } - } - } - vStringDelete (token); - } - return result; -} -/* GEANY_CTAGS_DIFF_END */ diff --git a/ctags/main/options.h b/ctags/main/options.h index 5275a38af9..5bb0e87aab 100644 --- a/ctags/main/options.h +++ b/ctags/main/options.h @@ -42,10 +42,4 @@ extern bool inSandbox (void); /* This is for emitting a tag for a commnn block of Fortran parser*/ extern bool canUseLineNumberAsLocator (void); -/* GEANY_CTAGS_DIFF */ -extern bool isIgnoreToken (const char *const name, - bool *const pIgnoreParens, - const char **const replacement); -/* GEANY_CTAGS_DIFF_END */ - #endif /* CTAGS_MAIN_OPTIONS_H */ diff --git a/ctags/parsers/c.c b/ctags/parsers/c.c index 244677dc22..41a997e73b 100644 --- a/ctags/parsers/c.c +++ b/ctags/parsers/c.c @@ -1726,7 +1726,7 @@ static void analyzeIdentifier (tokenInfo *const token) bool parensToo = false; if (isInputLanguage (Lang_java) || - ! isIgnoreToken (name, &parensToo, &replacement)) + ! cppIsIgnoreToken (name, &parensToo, &replacement)) { if (replacement != NULL) token->keyword = analyzeKeyword (replacement); From bf63becee722da66ed8a4c3826a2dd3db10fc02f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 24 Apr 2019 01:24:12 +0200 Subject: [PATCH 13/46] Eliminate diff in options.c by adding interactive_p.h --- ctags/Makefile.am | 1 + ctags/main/interactive_p.h | 29 +++++++++++++++++++++++++++++ ctags/main/options.c | 1 + 3 files changed, 31 insertions(+) create mode 100644 ctags/main/interactive_p.h diff --git a/ctags/Makefile.am b/ctags/Makefile.am index ef8ad59fdb..91d6f21f43 100644 --- a/ctags/Makefile.am +++ b/ctags/Makefile.am @@ -83,6 +83,7 @@ libctags_la_SOURCES = \ main/htable.c \ main/htable.h \ main/inline.h \ + main/interactive_p.h \ main/keyword.c \ main/keyword.h \ main/keyword_p.h \ diff --git a/ctags/main/interactive_p.h b/ctags/main/interactive_p.h new file mode 100644 index 0000000000..6dbd5be24b --- /dev/null +++ b/ctags/main/interactive_p.h @@ -0,0 +1,29 @@ +/* +* Copyright (c) 2016, Aman Gupta +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* Defines interface to interactive loop. +*/ +#ifndef CTAGS_MAIN_INTERACTIVE_H +#define CTAGS_MAIN_INTERACTIVE_H + +#include "general.h" +#include "options_p.h" +#include "routines.h" + + +struct interactiveModeArgs +{ + bool sandbox; +}; + +void interactiveLoop (cookedArgs *args, void *user); +bool jsonErrorPrinter (const errorSelection selection, const char *const format, va_list ap, + void *data); +int installSyscallFilter (void); + +#endif /* CTAGS_MAIN_INTERACTIVE_H */ + +/* vi:set tabstop=4 shiftwidth=4: */ diff --git a/ctags/main/options.c b/ctags/main/options.c index b339fc1db8..e0c574a937 100644 --- a/ctags/main/options.c +++ b/ctags/main/options.c @@ -35,6 +35,7 @@ #include "xtag.h" #include "param.h" #include "error.h" +#include "interactive_p.h" #include "writer_p.h" #include "trace.h" From c503d4201946f63e84cb31a8a45fdd7b876f24c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 24 Apr 2019 12:33:53 +0200 Subject: [PATCH 14/46] Reuse createTagsWithFallback1() for Geany While this means lots of ifdefs, it makes future syncs much less error prone to forgotten stuff. --- ctags/main/parse.c | 87 ++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 57 deletions(-) diff --git a/ctags/main/parse.c b/ctags/main/parse.c index 43a07dd165..f4e0ff3208 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -92,7 +92,9 @@ typedef struct sParserObject { */ static void lazyInitialize (langType language); +#ifndef GEANY_CTAGS_LIB static void addParserPseudoTags (langType language); +#endif /* GEANY_CTAGS_LIB */ static void installKeywordTable (const langType language); static void installTagRegexTable (const langType language); static void installTagXpathTable (const langType language); @@ -3103,12 +3105,19 @@ static subparser* teardownLanguageSubparsersInUse (const langType language) return teardownSubparsersInUse ((LanguageTable + language)->slaveControlBlock); } +#ifdef GEANY_CTAGS_LIB +static passStartCallback geanyPassCallback; +static void *geanyUserData; +#endif /* GEANY_CTAGS_LIB */ + static bool createTagsWithFallback1 (const langType language, langType *exclusive_subparser) { bool tagFileResized = false; +#ifndef GEANY_CTAGS_LIB unsigned long numTags = numTagsAdded (); MIOPos tagfpos; +#endif /* GEANY_CTAGS_LIB */ int lastPromise = getLastPromise (); unsigned int passCount = 0; rescanReason whyRescan; @@ -3124,11 +3133,16 @@ static bool createTagsWithFallback1 (const langType language, if (useCork) corkTagFile(); +#ifndef GEANY_CTAGS_LIB addParserPseudoTags (language); tagFilePosition (&tagfpos); +#endif /* GEANY_CTAGS_LIB */ anonResetMaybe (parser); +#ifdef GEANY_CTAGS_LIB + geanyPassCallback(geanyUserData); +#endif /* GEANY_CTAGS_LIB */ while ( ( whyRescan = createTagsForFile (language, ++passCount) ) != RESCAN_NONE) @@ -3144,17 +3158,28 @@ static bool createTagsWithFallback1 (const langType language, { /* Restore prior state of tag file. */ +#ifndef GEANY_CTAGS_LIB setTagFilePosition (&tagfpos); setNumTagsAdded (numTags); tagFileResized = true; +#endif /* GEANY_CTAGS_LIB */ breakPromisesAfter(lastPromise); } else if (whyRescan == RESCAN_APPEND) { +#ifndef GEANY_CTAGS_LIB tagFilePosition (&tagfpos); numTags = numTagsAdded (); +#endif /* GEANY_CTAGS_LIB */ lastPromise = getLastPromise (); } + +#ifdef GEANY_CTAGS_LIB + if (passCount < 3) + geanyPassCallback(geanyUserData); + else + break; +#endif /* GEANY_CTAGS_LIB */ } /* Force filling allLines buffer and kick the multiline regex parser */ @@ -3176,62 +3201,6 @@ static bool createTagsWithFallback1 (const langType language, #ifdef GEANY_CTAGS_LIB -/* keep in sync with createTagsWithFallback1() above */ -static bool createTagsWithFallback1Geany (const langType language, - passStartCallback passCallback, void *userData) -{ - int lastPromise = getLastPromise (); - unsigned int passCount = 0; - rescanReason whyRescan; - parserObject *parser; - bool useCork; - - initializeParser (language); - parser = &(LanguageTable [language]); - - setupLanguageSubparsersInUse (language); - - useCork = doesParserUseCork(parser->def); - if (useCork) - corkTagFile(); - - anonResetMaybe (parser); - - passCallback(userData); - while ( ( whyRescan = - createTagsForFile (language, ++passCount) ) - != RESCAN_NONE) - { - if (useCork) - { - uncorkTagFile(); - corkTagFile(); - } - - if (whyRescan == RESCAN_FAILED) - breakPromisesAfter(lastPromise); - else if (whyRescan == RESCAN_APPEND) - lastPromise = getLastPromise (); - - if (passCount < 3) - passCallback(userData); - else - break; - } - - /* Force filling allLines buffer and kick the multiline regex parser */ - if (hasLanguageMultilineRegexPatterns (language)) - while (readLineFromInputFile () != NULL) - ; /* Do nothing */ - - if (useCork) - uncorkTagFile(); - - teardownLanguageSubparsersInUse (language); - - return false; -} - static bool bufferOpen (const char *const fileName, const langType language, unsigned char *buffer, size_t buffer_size) { @@ -3256,7 +3225,9 @@ extern void createTagsWithFallbackGeany(unsigned char *buffer, size_t bufferSize setupAnon (); initParserTrashBox (); setTagEntryFunction(tagCallback, userData); - createTagsWithFallback1Geany (language, passCallback, userData); + geanyPassCallback = passCallback; + geanyUserData = userData; + createTagsWithFallback1 (language, NULL); forcePromises (); closeInputFile (); finiParserTrashBox (); @@ -3421,6 +3392,7 @@ extern const char *getLanguageEncoding (const langType language) } #endif +#ifndef GEANY_CTAGS_LIB static void addParserPseudoTags (langType language) { parserObject *parser = LanguageTable + language; @@ -3432,6 +3404,7 @@ static void addParserPseudoTags (langType language) parser->pseudoTagPrinted = 1; } } +#endif /* GEANY_CTAGS_LIB */ extern bool doesParserRequireMemoryStream (const langType language) { From 6cc4c4744be041a5745ffb7be5a05b17b9d424fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 24 Apr 2019 12:50:42 +0200 Subject: [PATCH 15/46] Eliminate bufferOpen() We can use openInputFile() directly and create MIO for it when needed. The passed mio is assigned to File.mio inside openInputFile() and on the next execution of the function the mio is freed for us so we don't need to call mio_free() explicitly. --- ctags/main/parse.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/ctags/main/parse.c b/ctags/main/parse.c index f4e0ff3208..cad65aeebe 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -3201,25 +3201,17 @@ static bool createTagsWithFallback1 (const langType language, #ifdef GEANY_CTAGS_LIB -static bool bufferOpen (const char *const fileName, const langType language, - unsigned char *buffer, size_t buffer_size) -{ - MIO *mio; - bool opened; - - mio = mio_new_memory (buffer, buffer_size, NULL, NULL); - opened = openInputFile (fileName, language, mio); - mio_free (mio); - return opened; -} - extern void createTagsWithFallbackGeany(unsigned char *buffer, size_t bufferSize, const char *fileName, const langType language, tagEntryFunction tagCallback, passStartCallback passCallback, void *userData) { - if ((!buffer && openInputFile (fileName, language, NULL)) || - (buffer && bufferOpen (fileName, language, buffer, bufferSize))) + MIO *mio = NULL; + + if (buffer) + mio = mio_new_memory (buffer, bufferSize, NULL, NULL); + + if (openInputFile (fileName, language, mio)) { /* keep in sync with parseFileWithMio() and createTagsWithFallback() */ setupAnon (); From 945353f1f7e0be6c5b4e3c7438092615f11fba2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 24 Apr 2019 13:07:46 +0200 Subject: [PATCH 16/46] Make sure all functions/global vars added to ctags have the "geany" prefix This way it's much easier to see which code isn't original and makes syncing easier. --- ctags/main/ctags-api.c | 11 ++++++----- ctags/main/entry.c | 18 +++++++++--------- ctags/main/entry.h | 2 +- ctags/main/parse.c | 6 +++--- ctags/main/parse.h | 4 ++-- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/ctags/main/ctags-api.c b/ctags/main/ctags-api.c index e456b90a15..f01459ad32 100644 --- a/ctags/main/ctags-api.c +++ b/ctags/main/ctags-api.c @@ -44,6 +44,7 @@ static bool nofatalErrorPrinter (const errorSelection selection, return false; } +/* keep in sync with ctags main() - use only things interesting for us */ extern void ctagsInit(void) { initDefaultTrashBox (); @@ -75,7 +76,7 @@ extern void ctagsParse(unsigned char *buffer, size_t bufferSize, return; } - createTagsWithFallbackGeany(buffer, bufferSize, fileName, language, + geanyCreateTagsWithFallback(buffer, bufferSize, fileName, language, tagCallback, passCallback, userData); } @@ -94,7 +95,7 @@ extern int ctagsGetNamedLang(const char *name) extern const char *ctagsGetLangKinds(int lang) { - const parserDefinition *def = getParserDefinition(lang); + const parserDefinition *def = geanyGetParserDefinition(lang); unsigned int i; static char kinds[257]; @@ -108,7 +109,7 @@ extern const char *ctagsGetLangKinds(int lang) extern const char *ctagsGetKindName(char kind, int lang) { - const parserDefinition *def = getParserDefinition(lang); + const parserDefinition *def = geanyGetParserDefinition(lang); unsigned int i; for (i = 0; i < def->kindCount; i++) @@ -122,7 +123,7 @@ extern const char *ctagsGetKindName(char kind, int lang) extern char ctagsGetKindFromName(const char *name, int lang) { - const parserDefinition *def = getParserDefinition(lang); + const parserDefinition *def = geanyGetParserDefinition(lang); unsigned int i; for (i = 0; i < def->kindCount; i++) @@ -136,7 +137,7 @@ extern char ctagsGetKindFromName(const char *name, int lang) extern bool ctagsIsUsingRegexParser(int lang) { - return getParserDefinition(lang)->method & METHOD_REGEX; + return geanyGetParserDefinition(lang)->method & METHOD_REGEX; } diff --git a/ctags/main/entry.c b/ctags/main/entry.c index 6e49c51ec5..97ac8e73a4 100644 --- a/ctags/main/entry.c +++ b/ctags/main/entry.c @@ -1214,16 +1214,16 @@ static bool isTagWritable(const tagEntryInfo *const tag) #ifdef GEANY_CTAGS_LIB -static tagEntryFunction TagEntryFunction = NULL; -static void *TagEntryUserData = NULL; +static tagEntryFunction geanyTagEntryFunction = NULL; +static void *geanyTagEntryUserData = NULL; -extern void setTagEntryFunction(tagEntryFunction entry_function, void *user_data) +extern void geanySetTagEntryFunction(tagEntryFunction entry_function, void *user_data) { - TagEntryFunction = entry_function; - TagEntryUserData = user_data; + geanyTagEntryFunction = entry_function; + geanyTagEntryUserData = user_data; } -static void initCtagsTag(ctagsTag *tag, const tagEntryInfo *info) +static void geanyInitCtagsTag(ctagsTag *tag, const tagEntryInfo *info) { tag->name = info->name; tag->signature = info->extensionFields.signature; @@ -1262,12 +1262,12 @@ static void writeTagEntry (const tagEntryInfo *const tag, bool checkingNeeded) #ifdef GEANY_CTAGS_LIB getTagScopeInformation((tagEntryInfo *)tag, NULL, NULL); - if (TagEntryFunction != NULL) + if (geanyTagEntryFunction != NULL) { ctagsTag t; - initCtagsTag(&t, tag); - length = TagEntryFunction(&t, TagEntryUserData); + geanyInitCtagsTag(&t, tag); + length = geanyTagEntryFunction(&t, geanyTagEntryUserData); } #else length = writerWriteTag (TagFile.mio, tag); diff --git a/ctags/main/entry.h b/ctags/main/entry.h index 4920058ca4..3c8e8ebab6 100644 --- a/ctags/main/entry.h +++ b/ctags/main/entry.h @@ -152,7 +152,7 @@ CTAGS_INLINE roleBitsType makeRoleBit(int roleIndex) #ifdef GEANY_CTAGS_LIB #include "ctags-api.h" -extern void setTagEntryFunction(tagEntryFunction entry_function, void *user_data); +extern void geanySetTagEntryFunction(tagEntryFunction entry_function, void *user_data); #endif /* GEANY_CTAGS_LIB */ #endif /* CTAGS_MAIN_ENTRY_H */ diff --git a/ctags/main/parse.c b/ctags/main/parse.c index cad65aeebe..449536dd5e 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -3201,7 +3201,7 @@ static bool createTagsWithFallback1 (const langType language, #ifdef GEANY_CTAGS_LIB -extern void createTagsWithFallbackGeany(unsigned char *buffer, size_t bufferSize, +extern void geanyCreateTagsWithFallback(unsigned char *buffer, size_t bufferSize, const char *fileName, const langType language, tagEntryFunction tagCallback, passStartCallback passCallback, void *userData) @@ -3216,7 +3216,7 @@ extern void createTagsWithFallbackGeany(unsigned char *buffer, size_t bufferSize /* keep in sync with parseFileWithMio() and createTagsWithFallback() */ setupAnon (); initParserTrashBox (); - setTagEntryFunction(tagCallback, userData); + geanySetTagEntryFunction(tagCallback, userData); geanyPassCallback = passCallback; geanyUserData = userData; createTagsWithFallback1 (language, NULL); @@ -3229,7 +3229,7 @@ extern void createTagsWithFallbackGeany(unsigned char *buffer, size_t bufferSize error (WARNING, "Unable to open %s", fileName); } -extern const parserDefinition *getParserDefinition (langType language) +extern const parserDefinition *geanyGetParserDefinition (langType language) { Assert (0 <= language && language < (int) LanguageCount); return LanguageTable[language].def; diff --git a/ctags/main/parse.h b/ctags/main/parse.h index 0bd47f5cf9..56cd6eab6b 100644 --- a/ctags/main/parse.h +++ b/ctags/main/parse.h @@ -135,11 +135,11 @@ extern void initializeParser (langType language); #ifdef GEANY_CTAGS_LIB #include "ctags-api.h" -extern void createTagsWithFallbackGeany(unsigned char *buffer, size_t bufferSize, +extern void geanyCreateTagsWithFallback(unsigned char *buffer, size_t bufferSize, const char *fileName, const langType language, tagEntryFunction tagCallback, passStartCallback passCallback, void *userData); -extern const parserDefinition *getParserDefinition (langType language); +extern const parserDefinition *geanyGetParserDefinition (langType language); #endif /* GEANY_CTAGS_LIB */ #ifdef HAVE_ICONV From 285630e55ac2b42f55c3cab4b975c0a5d3fbf2f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 24 Apr 2019 13:16:44 +0200 Subject: [PATCH 17/46] Eliminate diff in runParserInNarrowedInputStream() Introduce a new variable geanyNarrowedContext informing us that we run a subparser and use it in createTagsWithFallback1() because we don't want to be informed about new passes for subparsers in Geany (only about master parser passes). --- ctags/main/parse.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/ctags/main/parse.c b/ctags/main/parse.c index 449536dd5e..cebd9d2e8a 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -3108,6 +3108,7 @@ static subparser* teardownLanguageSubparsersInUse (const langType language) #ifdef GEANY_CTAGS_LIB static passStartCallback geanyPassCallback; static void *geanyUserData; +static bool geanyNarrowedContext; #endif /* GEANY_CTAGS_LIB */ static bool createTagsWithFallback1 (const langType language, @@ -3141,7 +3142,8 @@ static bool createTagsWithFallback1 (const langType language, anonResetMaybe (parser); #ifdef GEANY_CTAGS_LIB - geanyPassCallback(geanyUserData); + if (!geanyNarrowedContext) + geanyPassCallback(geanyUserData); #endif /* GEANY_CTAGS_LIB */ while ( ( whyRescan = createTagsForFile (language, ++passCount) ) @@ -3175,10 +3177,13 @@ static bool createTagsWithFallback1 (const langType language, } #ifdef GEANY_CTAGS_LIB - if (passCount < 3) - geanyPassCallback(geanyUserData); - else - break; + if (!geanyNarrowedContext) + { + if (passCount < 3) + geanyPassCallback(geanyUserData); + else + break; + } #endif /* GEANY_CTAGS_LIB */ } @@ -3219,6 +3224,7 @@ extern void geanyCreateTagsWithFallback(unsigned char *buffer, size_t bufferSize geanySetTagEntryFunction(tagCallback, userData); geanyPassCallback = passCallback; geanyUserData = userData; + geanyNarrowedContext = false; createTagsWithFallback1 (language, NULL); forcePromises (); closeInputFile (); @@ -3260,20 +3266,10 @@ extern bool runParserInNarrowedInputStream (const langType language, endLine, endCharOffset, sourceLineOffset, promise); -#ifndef GEANY_CTAGS_LIB +#ifdef GEANY_CTAGS_LIB + geanyNarrowedContext = true; +#endif tagFileResized = createTagsWithFallback1 (language, NULL); -#else - /* Simple parsing without rescans - not used by any sub-parsers anyway */ - initializeParser (language); - parserObject *parser = &(LanguageTable [language]); - bool useCork = doesParserUseCork(parser->def); - if (useCork) - corkTagFile(); - createTagsForFile (language, 1); - if (useCork) - uncorkTagFile(); - tagFileResized = false; -#endif /* GEANY_CTAGS_LIB */ popNarrowedInputStream (); return tagFileResized; From e59949ae294c8fe621400b1cf468c208fda375d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 24 Apr 2019 14:12:18 +0200 Subject: [PATCH 18/46] Eliminate diff in promise.c by manually enabling Xtag during initialization --- ctags/main/ctags-api.c | 3 +++ ctags/main/promise.c | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ctags/main/ctags-api.c b/ctags/main/ctags-api.c index f01459ad32..8ae462aec2 100644 --- a/ctags/main/ctags-api.c +++ b/ctags/main/ctags-api.c @@ -61,6 +61,9 @@ extern void ctagsInit(void) /* make sure all parsers are initialized */ initializeParser (LANG_AUTO); + + /* change default value which is false */ + enableXtag(XTAG_TAGS_GENERATED_BY_GUEST_PARSERS, true); } diff --git a/ctags/main/promise.c b/ctags/main/promise.c index dafe7af938..b35753432b 100644 --- a/ctags/main/promise.c +++ b/ctags/main/promise.c @@ -69,8 +69,6 @@ int makePromise (const char *parser, int r; langType lang; -/* GEANY_CTAGS_DIFF */ -/* if ((!isThinStreamSpec(startLine, startCharOffset, endLine, @@ -78,8 +76,6 @@ int makePromise (const char *parser, sourceLineOffset)) && ( !isXtagEnabled (XTAG_TAGS_GENERATED_BY_GUEST_PARSERS))) return -1; -*/ -/* GEANY_CTAGS_DIFF_END */ lang = getNamedLanguage (parser, 0); if (lang == LANG_IGNORE) From 15246e83c61f213bbdaa61b94de151f45b28ef6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 24 Apr 2019 14:12:34 +0200 Subject: [PATCH 19/46] Add some comments --- ctags/main/entry.c | 1 + ctags/main/parse.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ctags/main/entry.c b/ctags/main/entry.c index 97ac8e73a4..e32c8b0602 100644 --- a/ctags/main/entry.c +++ b/ctags/main/entry.c @@ -156,6 +156,7 @@ extern const char *tagFileName (void) extern void abort_if_ferror(MIO *const mio) { #ifndef GEANY_CTAGS_LIB +/* to be sure some error doesn't terminate Geany */ if (mio_error (mio)) error (FATAL | PERROR, "cannot write tag file"); #endif /* GEANY_CTAGS_LIB */ diff --git a/ctags/main/parse.c b/ctags/main/parse.c index cebd9d2e8a..72d94ab3ea 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -3381,6 +3381,7 @@ extern const char *getLanguageEncoding (const langType language) #endif #ifndef GEANY_CTAGS_LIB +/* just to eliminate warning about unused function */ static void addParserPseudoTags (langType language) { parserObject *parser = LanguageTable + language; @@ -3936,7 +3937,7 @@ extern void anonGenerate (vString *buffer, const char *prefix, int kind) vStringCopyS(buffer, prefix); /* GEANY_CTAGS_DIFF */ -/* +/* we want to see numbers for anon functions in the tree view instead of the hash char buf [9]; anonHashString (getInputFileName(), buf); sprintf(szNum,"%s%02x%02x",buf,parser -> anonymousIdentiferId, kind); @@ -4174,6 +4175,7 @@ extern void addLanguageTagMultiTableRegex(const langType language, } #ifndef GEANY_CTAGS_LIB +/* just to eliminate warning about unused functions */ /* * A parser for CTagsSelfTest (CTST) */ From 1bdaa522af227e77f653b0d35151e4b5a540ea94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 24 Apr 2019 14:53:13 +0200 Subject: [PATCH 20/46] Make sure all kinds are enabled in ctags for all languages This helps to eliminate one diff in entry.c. Also, this adds some extra tags to some languages which were previously missing. --- ctags/main/ctags-api.c | 20 ++++++++++++++++++++ ctags/main/entry.c | 2 -- tests/ctags/Package.pm.tags | 3 +++ tests/ctags/bug1938565.sql.tags | 2 ++ tests/ctags/random.sql.tags | 7 +++++++ tests/ctags/refcurs.sql.tags | 2 ++ 6 files changed, 34 insertions(+), 2 deletions(-) diff --git a/ctags/main/ctags-api.c b/ctags/main/ctags-api.c index 8ae462aec2..3b924f6dde 100644 --- a/ctags/main/ctags-api.c +++ b/ctags/main/ctags-api.c @@ -44,6 +44,23 @@ static bool nofatalErrorPrinter (const errorSelection selection, return false; } +static void enableAllLangKinds() +{ + unsigned int lang; + + for (lang = 0; lang < countParsers(); lang++) + { + unsigned int kindNum = countLanguageKinds(lang); + unsigned int kind; + + for (kind = 0; kind < kindNum; kind++) + { + kindDefinition *def = getLanguageKind(lang, kind); + enableKind(def, true); + } + } +} + /* keep in sync with ctags main() - use only things interesting for us */ extern void ctagsInit(void) { @@ -64,6 +81,9 @@ extern void ctagsInit(void) /* change default value which is false */ enableXtag(XTAG_TAGS_GENERATED_BY_GUEST_PARSERS, true); + + /* some kinds we are interested in are disabled by default */ + enableAllLangKinds(); } diff --git a/ctags/main/entry.c b/ctags/main/entry.c index e32c8b0602..7f416f1135 100644 --- a/ctags/main/entry.c +++ b/ctags/main/entry.c @@ -1391,11 +1391,9 @@ extern int makeTagEntry (const tagEntryInfo *const tag) int r = CORK_NIL; Assert (tag->name != NULL); -#ifndef GEANY_CTAGS_LIB if (!TagFile.cork) if (!isTagWritable (tag)) goto out; -#endif /* GEANY_CTAGS_LIB */ if (tag->name [0] == '\0' && (!tag->placeholder)) { diff --git a/tests/ctags/Package.pm.tags b/tests/ctags/Package.pm.tags index 43b6899e44..738eaaa432 100644 --- a/tests/ctags/Package.pm.tags +++ b/tests/ctags/Package.pm.tags @@ -8,3 +8,6 @@ Test::One::Two dudeÌ16Ö0 initÌ16Ö0 quoÌ16Ö0 +quoÌ1024Ö0 +xtzÌ1024Ö0 +xuzÌ1024Ö0 diff --git a/tests/ctags/bug1938565.sql.tags b/tests/ctags/bug1938565.sql.tags index ba596240ee..2844c58b83 100644 --- a/tests/ctags/bug1938565.sql.tags +++ b/tests/ctags/bug1938565.sql.tags @@ -1,4 +1,6 @@ # format=tagmanager demo_pkgÌ512Ö0 func1Ì16Îdemo_pkgÖ0 +func1_protoÌ1024Îdemo_pkgÖ0 func2Ì16Îdemo_pkgÖ0 +func2_protoÌ1024Îdemo_pkgÖ0 diff --git a/tests/ctags/random.sql.tags b/tests/ctags/random.sql.tags index f3bff2f0d5..bfbeee05e6 100644 --- a/tests/ctags/random.sql.tags +++ b/tests/ctags/random.sql.tags @@ -1,12 +1,19 @@ # format=tagmanager SeedÌ16384ÎrandomÖ0 get_randÌ256ÎrandomÖ0 +get_randÌ1024ÎrandomÖ0 get_rand_maxÌ256ÎrandomÖ0 +get_rand_maxÌ1024ÎrandomÖ0 incrementÌ16384ÎrandomÖ0 multiplierÌ16384ÎrandomÖ0 randÌ16ÎrandomÖ0 +randÌ1024ÎrandomÖ0 rand_maxÌ16ÎrandomÖ0 +rand_maxÌ1024ÎrandomÖ0 rand_stringÌ16ÎrandomÖ0 +rand_stringÌ1024ÎrandomÖ0 randomÌ512Ö0 smallerÌ16ÎrandomÖ0 +smallerÌ1024ÎrandomÖ0 srandÌ256ÎrandomÖ0 +srandÌ1024ÎrandomÖ0 diff --git a/tests/ctags/refcurs.sql.tags b/tests/ctags/refcurs.sql.tags index 400ca2822c..e6e2d2d11b 100644 --- a/tests/ctags/refcurs.sql.tags +++ b/tests/ctags/refcurs.sql.tags @@ -1,6 +1,8 @@ # format=tagmanager get_cursor_refÌ16Îtest_ref_cursorÖ0 mainÌ256Îtest_ref_cursorÖ0 +mainÌ1024Îtest_ref_cursorÖ0 process_cursorÌ256Îtest_ref_cursorÖ0 +process_cursorÌ1024Îtest_ref_cursorÖ0 test_ref_cursorÌ512Ö0 typesÌ512Ö0 From 9a4f3fa6114ad2eacc78e0bc1adaf5d35a0d8777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 24 Apr 2019 15:50:28 +0200 Subject: [PATCH 21/46] Use the builtin ctags writer functionality to pass tags to Geany This is much cleaner and eliminates all the diffs from entry.c/h. We only need to introduce a diff to writer.c/h beause the existing setTagWriter() works with builtin writers only. --- ctags/main/ctags-api.c | 56 +++++++++++++++++++++++++++++++++++++++--- ctags/main/entry.c | 40 ------------------------------ ctags/main/entry.h | 5 ---- ctags/main/parse.c | 4 +-- ctags/main/parse.h | 3 +-- ctags/main/writer.c | 7 ++++++ ctags/main/writer_p.h | 3 +++ 7 files changed, 65 insertions(+), 53 deletions(-) diff --git a/ctags/main/ctags-api.c b/ctags/main/ctags-api.c index 3b924f6dde..4140193d97 100644 --- a/ctags/main/ctags-api.c +++ b/ctags/main/ctags-api.c @@ -22,11 +22,29 @@ #include "trashbox.h" #include "field.h" #include "xtag.h" +#include "entry_p.h" #include #include #include +static int writeEntry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag); + +tagWriter geanyWriter = { + .writeEntry = writeEntry, + .writePtagEntry = NULL, /* no pseudo-tags */ + .preWriteEntry = NULL, + .postWriteEntry = NULL, + .buildFqTagCache = NULL, + .defaultFileName = "geany_tags_file_which_should_never_appear_anywhere", + .private = NULL, + .type = WRITER_U_CTAGS /* not really but we must use some of the builtin types */ +}; + +static tagEntryFunction geanyTagEntryFunction = NULL; +static void *geanyTagEntryUserData = NULL; + + static bool nofatalErrorPrinter (const errorSelection selection, const char *const format, va_list ap, void *data CTAGS_ATTR_UNUSED) @@ -44,6 +62,7 @@ static bool nofatalErrorPrinter (const errorSelection selection, return false; } + static void enableAllLangKinds() { unsigned int lang; @@ -61,13 +80,43 @@ static void enableAllLangKinds() } } + +static void initCtagsTag(ctagsTag *tag, const tagEntryInfo *info) +{ + tag->name = info->name; + tag->signature = info->extensionFields.signature; + tag->scopeName = info->extensionFields.scopeName; + tag->inheritance = info->extensionFields.inheritance; + tag->varType = info->extensionFields.typeRef[1]; + tag->access = info->extensionFields.access; + tag->implementation = info->extensionFields.implementation; + tag->kindLetter = getLanguageKind(info->langType, info->kindIndex)->letter; + tag->isFileScope = info->isFileScope; + tag->lineNumber = info->lineNumber; + tag->lang = info->langType; +} + + +static int writeEntry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag) +{ + ctagsTag t; + + getTagScopeInformation((tagEntryInfo *)tag, NULL, NULL); + initCtagsTag(&t, tag); + geanyTagEntryFunction(&t, geanyTagEntryUserData); + + /* output length - we don't write anything to the MIO */ + return 0; +} + + /* keep in sync with ctags main() - use only things interesting for us */ extern void ctagsInit(void) { initDefaultTrashBox (); setErrorPrinter (nofatalErrorPrinter, NULL); - setTagWriter (WRITER_U_CTAGS); + geanySetTagWriter (&geanyWriter); checkRegex (); initFieldObjects (); @@ -87,7 +136,6 @@ extern void ctagsInit(void) } - extern void ctagsParse(unsigned char *buffer, size_t bufferSize, const char *fileName, const langType language, tagEntryFunction tagCallback, passStartCallback passCallback, @@ -99,8 +147,10 @@ extern void ctagsParse(unsigned char *buffer, size_t bufferSize, return; } + geanyTagEntryFunction = tagCallback; + geanyTagEntryUserData = userData; geanyCreateTagsWithFallback(buffer, bufferSize, fileName, language, - tagCallback, passCallback, userData); + passCallback, userData); } diff --git a/ctags/main/entry.c b/ctags/main/entry.c index 7f416f1135..b1a6e3b0f9 100644 --- a/ctags/main/entry.c +++ b/ctags/main/entry.c @@ -1213,34 +1213,6 @@ static bool isTagWritable(const tagEntryInfo *const tag) return true; } -#ifdef GEANY_CTAGS_LIB - -static tagEntryFunction geanyTagEntryFunction = NULL; -static void *geanyTagEntryUserData = NULL; - -extern void geanySetTagEntryFunction(tagEntryFunction entry_function, void *user_data) -{ - geanyTagEntryFunction = entry_function; - geanyTagEntryUserData = user_data; -} - -static void geanyInitCtagsTag(ctagsTag *tag, const tagEntryInfo *info) -{ - tag->name = info->name; - tag->signature = info->extensionFields.signature; - tag->scopeName = info->extensionFields.scopeName; - tag->inheritance = info->extensionFields.inheritance; - tag->varType = info->extensionFields.typeRef[1]; - tag->access = info->extensionFields.access; - tag->implementation = info->extensionFields.implementation; - tag->kindLetter = getLanguageKind(info->langType, info->kindIndex)->letter; - tag->isFileScope = info->isFileScope; - tag->lineNumber = info->lineNumber; - tag->lang = info->langType; -} - -#endif /* GEANY_CTAGS_LIB */ - static void writeTagEntry (const tagEntryInfo *const tag, bool checkingNeeded) { int length = 0; @@ -1260,19 +1232,7 @@ static void writeTagEntry (const tagEntryInfo *const tag, bool checkingNeeded) writerBuildFqTagCache ( (tagEntryInfo *const)tag); } -#ifdef GEANY_CTAGS_LIB - getTagScopeInformation((tagEntryInfo *)tag, NULL, NULL); - - if (geanyTagEntryFunction != NULL) - { - ctagsTag t; - - geanyInitCtagsTag(&t, tag); - length = geanyTagEntryFunction(&t, geanyTagEntryUserData); - } -#else length = writerWriteTag (TagFile.mio, tag); -#endif /* GEANY_CTAGS_LIB */ ++TagFile.numTags.added; rememberMaxLengths (strlen (tag->name), (size_t) length); diff --git a/ctags/main/entry.h b/ctags/main/entry.h index 3c8e8ebab6..c033bf3fba 100644 --- a/ctags/main/entry.h +++ b/ctags/main/entry.h @@ -150,9 +150,4 @@ CTAGS_INLINE roleBitsType makeRoleBit(int roleIndex) return ((roleBitsType)1) << roleIndex; } -#ifdef GEANY_CTAGS_LIB -#include "ctags-api.h" -extern void geanySetTagEntryFunction(tagEntryFunction entry_function, void *user_data); -#endif /* GEANY_CTAGS_LIB */ - #endif /* CTAGS_MAIN_ENTRY_H */ diff --git a/ctags/main/parse.c b/ctags/main/parse.c index 72d94ab3ea..56e9d4f195 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -3207,8 +3207,7 @@ static bool createTagsWithFallback1 (const langType language, #ifdef GEANY_CTAGS_LIB extern void geanyCreateTagsWithFallback(unsigned char *buffer, size_t bufferSize, - const char *fileName, const langType language, - tagEntryFunction tagCallback, passStartCallback passCallback, + const char *fileName, const langType language, passStartCallback passCallback, void *userData) { MIO *mio = NULL; @@ -3221,7 +3220,6 @@ extern void geanyCreateTagsWithFallback(unsigned char *buffer, size_t bufferSize /* keep in sync with parseFileWithMio() and createTagsWithFallback() */ setupAnon (); initParserTrashBox (); - geanySetTagEntryFunction(tagCallback, userData); geanyPassCallback = passCallback; geanyUserData = userData; geanyNarrowedContext = false; diff --git a/ctags/main/parse.h b/ctags/main/parse.h index 56cd6eab6b..977d346b61 100644 --- a/ctags/main/parse.h +++ b/ctags/main/parse.h @@ -136,8 +136,7 @@ extern void initializeParser (langType language); #ifdef GEANY_CTAGS_LIB #include "ctags-api.h" extern void geanyCreateTagsWithFallback(unsigned char *buffer, size_t bufferSize, - const char *fileName, const langType language, - tagEntryFunction tagCallback, passStartCallback passCallback, + const char *fileName, const langType language, passStartCallback passCallback, void *userData); extern const parserDefinition *geanyGetParserDefinition (langType language); #endif /* GEANY_CTAGS_LIB */ diff --git a/ctags/main/writer.c b/ctags/main/writer.c index 364c36396d..2b59706aff 100644 --- a/ctags/main/writer.c +++ b/ctags/main/writer.c @@ -27,6 +27,13 @@ static tagWriter *writerTable [WRITER_COUNT] = { static tagWriter *writer; +#ifdef GEANY_CTAGS_LIB +extern void geanySetTagWriter(tagWriter *w) +{ + writer = w; +} +#endif /* GEANY_CTAGS_LIB */ + extern void setTagWriter (writerType wtype) { writer = writerTable [wtype]; diff --git a/ctags/main/writer_p.h b/ctags/main/writer_p.h index 17c39d09fb..db920dc302 100644 --- a/ctags/main/writer_p.h +++ b/ctags/main/writer_p.h @@ -51,6 +51,9 @@ struct sTagWriter { }; +#ifdef GEANY_CTAGS_LIB +extern void geanySetTagWriter(tagWriter *w); +#endif /* GEANY_CTAGS_LIB */ extern void setTagWriter (writerType otype); extern void writerSetup (MIO *mio); extern bool writerTeardown (MIO *mio, const char *filename); From 2bbbc064a961f3626874ce84c0762ace4e24e7c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 24 Apr 2019 16:39:10 +0200 Subject: [PATCH 22/46] Remove some diffs from parse.c Instead, move the necessary checks to entry.c. --- ctags/main/entry.c | 12 +++++++++--- ctags/main/parse.c | 13 ------------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/ctags/main/entry.c b/ctags/main/entry.c index b1a6e3b0f9..44f471bca3 100644 --- a/ctags/main/entry.c +++ b/ctags/main/entry.c @@ -156,7 +156,7 @@ extern const char *tagFileName (void) extern void abort_if_ferror(MIO *const mio) { #ifndef GEANY_CTAGS_LIB -/* to be sure some error doesn't terminate Geany */ +/* TagFile.mio is NULL so this would terminate Geany */ if (mio_error (mio)) error (FATAL | PERROR, "cannot write tag file"); #endif /* GEANY_CTAGS_LIB */ @@ -1663,12 +1663,18 @@ extern void invalidatePatternCache(void) extern void tagFilePosition (MIOPos *p) { - mio_getpos (TagFile.mio, p); +#ifdef GEANY_CTAGS_LIB /* TODO: do the same upstream */ + if (TagFile.mio) +#endif /* GEANY_CTAGS_LIB */ + mio_getpos (TagFile.mio, p); } extern void setTagFilePosition (MIOPos *p) { - mio_setpos (TagFile.mio, p); +#ifdef GEANY_CTAGS_LIB /* TODO: do the same upstream */ + if (TagFile.mio) +#endif /* GEANY_CTAGS_LIB */ + mio_setpos (TagFile.mio, p); } extern const char* getTagFileDirectory (void) diff --git a/ctags/main/parse.c b/ctags/main/parse.c index 56e9d4f195..3ff388607b 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -92,9 +92,7 @@ typedef struct sParserObject { */ static void lazyInitialize (langType language); -#ifndef GEANY_CTAGS_LIB static void addParserPseudoTags (langType language); -#endif /* GEANY_CTAGS_LIB */ static void installKeywordTable (const langType language); static void installTagRegexTable (const langType language); static void installTagXpathTable (const langType language); @@ -3115,10 +3113,8 @@ static bool createTagsWithFallback1 (const langType language, langType *exclusive_subparser) { bool tagFileResized = false; -#ifndef GEANY_CTAGS_LIB unsigned long numTags = numTagsAdded (); MIOPos tagfpos; -#endif /* GEANY_CTAGS_LIB */ int lastPromise = getLastPromise (); unsigned int passCount = 0; rescanReason whyRescan; @@ -3134,10 +3130,8 @@ static bool createTagsWithFallback1 (const langType language, if (useCork) corkTagFile(); -#ifndef GEANY_CTAGS_LIB addParserPseudoTags (language); tagFilePosition (&tagfpos); -#endif /* GEANY_CTAGS_LIB */ anonResetMaybe (parser); @@ -3160,19 +3154,15 @@ static bool createTagsWithFallback1 (const langType language, { /* Restore prior state of tag file. */ -#ifndef GEANY_CTAGS_LIB setTagFilePosition (&tagfpos); setNumTagsAdded (numTags); tagFileResized = true; -#endif /* GEANY_CTAGS_LIB */ breakPromisesAfter(lastPromise); } else if (whyRescan == RESCAN_APPEND) { -#ifndef GEANY_CTAGS_LIB tagFilePosition (&tagfpos); numTags = numTagsAdded (); -#endif /* GEANY_CTAGS_LIB */ lastPromise = getLastPromise (); } @@ -3378,8 +3368,6 @@ extern const char *getLanguageEncoding (const langType language) } #endif -#ifndef GEANY_CTAGS_LIB -/* just to eliminate warning about unused function */ static void addParserPseudoTags (langType language) { parserObject *parser = LanguageTable + language; @@ -3391,7 +3379,6 @@ static void addParserPseudoTags (langType language) parser->pseudoTagPrinted = 1; } } -#endif /* GEANY_CTAGS_LIB */ extern bool doesParserRequireMemoryStream (const langType language) { From bc9932f520905c842713304b6978caa023a25343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 24 Apr 2019 17:09:50 +0200 Subject: [PATCH 23/46] Reuse the functionality of createTagsWithFallback() By calling createTagsWithFallback() we can reuse some of its functionality and simplify geanyCreateTagsWithCallback(). Also rename geanyCreateTagsWithCallback() to geanyCreateTags(). In the future we still have to keep geanyCreateTags() in sync with parseFileWithMio() but because this function does things we don't want in Geany it's more practical to do it this way. --- ctags/main/ctags-api.c | 2 +- ctags/main/parse.c | 72 ++++++++++++++++++++---------------------- ctags/main/parse.h | 2 +- 3 files changed, 37 insertions(+), 39 deletions(-) diff --git a/ctags/main/ctags-api.c b/ctags/main/ctags-api.c index 4140193d97..562fa39d60 100644 --- a/ctags/main/ctags-api.c +++ b/ctags/main/ctags-api.c @@ -149,7 +149,7 @@ extern void ctagsParse(unsigned char *buffer, size_t bufferSize, geanyTagEntryFunction = tagCallback; geanyTagEntryUserData = userData; - geanyCreateTagsWithFallback(buffer, bufferSize, fileName, language, + geanyCreateTags(buffer, bufferSize, fileName, language, passCallback, userData); } diff --git a/ctags/main/parse.c b/ctags/main/parse.c index 3ff388607b..2ca011a928 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -3194,43 +3194,6 @@ static bool createTagsWithFallback1 (const langType language, return tagFileResized; } -#ifdef GEANY_CTAGS_LIB - -extern void geanyCreateTagsWithFallback(unsigned char *buffer, size_t bufferSize, - const char *fileName, const langType language, passStartCallback passCallback, - void *userData) -{ - MIO *mio = NULL; - - if (buffer) - mio = mio_new_memory (buffer, bufferSize, NULL, NULL); - - if (openInputFile (fileName, language, mio)) - { - /* keep in sync with parseFileWithMio() and createTagsWithFallback() */ - setupAnon (); - initParserTrashBox (); - geanyPassCallback = passCallback; - geanyUserData = userData; - geanyNarrowedContext = false; - createTagsWithFallback1 (language, NULL); - forcePromises (); - closeInputFile (); - finiParserTrashBox (); - teardownAnon (); - } - else - error (WARNING, "Unable to open %s", fileName); -} - -extern const parserDefinition *geanyGetParserDefinition (langType language) -{ - Assert (0 <= language && language < (int) LanguageCount); - return LanguageTable[language].def; -} - -#endif /* GEANY_CTAGS_LIB */ - extern bool runParserInNarrowedInputStream (const langType language, unsigned long startLine, long startCharOffset, unsigned long endLine, long endCharOffset, @@ -3289,6 +3252,41 @@ static bool createTagsWithFallback ( return tagFileResized; } +#ifdef GEANY_CTAGS_LIB + +extern void geanyCreateTags(unsigned char *buffer, size_t bufferSize, + const char *fileName, const langType language, passStartCallback passCallback, + void *userData) +{ + MIO *mio = NULL; + + if (buffer) + mio = mio_new_memory (buffer, bufferSize, NULL, NULL); + + geanyPassCallback = passCallback; + geanyUserData = userData; + geanyNarrowedContext = false; + + /* keep in sync with parseFileWithMio() */ + setupWriter (); + setupAnon (); + initParserTrashBox (); + + createTagsWithFallback (fileName, language, mio); + + finiParserTrashBox (); + teardownAnon (); + teardownWriter(fileName); +} + +extern const parserDefinition *geanyGetParserDefinition (langType language) +{ + Assert (0 <= language && language < (int) LanguageCount); + return LanguageTable[language].def; +} + +#endif /* GEANY_CTAGS_LIB */ + static void printGuessedParser (const char* const fileName, langType language) { const char *parserName; diff --git a/ctags/main/parse.h b/ctags/main/parse.h index 977d346b61..9639a128b4 100644 --- a/ctags/main/parse.h +++ b/ctags/main/parse.h @@ -135,7 +135,7 @@ extern void initializeParser (langType language); #ifdef GEANY_CTAGS_LIB #include "ctags-api.h" -extern void geanyCreateTagsWithFallback(unsigned char *buffer, size_t bufferSize, +extern void geanyCreateTags(unsigned char *buffer, size_t bufferSize, const char *fileName, const langType language, passStartCallback passCallback, void *userData); extern const parserDefinition *geanyGetParserDefinition (langType language); From 7eebfbd6f2dbef38c6a8c205f803bd634b7664c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 24 Apr 2019 17:38:05 +0200 Subject: [PATCH 24/46] Simplify various ctags-api.c implementations using existing ctags functions This also helps us to eliminate geanyGetParserDefinition() and replace it with geanyIsParserRegex() which is the only missing function we need in ctags. --- ctags/main/ctags-api.c | 32 +++++++++----------------------- ctags/main/parse.c | 4 ++-- ctags/main/parse.h | 2 +- 3 files changed, 12 insertions(+), 26 deletions(-) diff --git a/ctags/main/ctags-api.c b/ctags/main/ctags-api.c index 562fa39d60..317a2481c4 100644 --- a/ctags/main/ctags-api.c +++ b/ctags/main/ctags-api.c @@ -168,12 +168,12 @@ extern int ctagsGetNamedLang(const char *name) extern const char *ctagsGetLangKinds(int lang) { - const parserDefinition *def = geanyGetParserDefinition(lang); - unsigned int i; + unsigned int kindNum = countLanguageKinds(lang); static char kinds[257]; + unsigned int i; - for (i = 0; i < def->kindCount; i++) - kinds[i] = def->kindTable[i].letter; + for (i = 0; i < kindNum; i++) + kinds[i] = getLanguageKind(lang, i)->letter; kinds[i] = '\0'; return kinds; @@ -182,35 +182,21 @@ extern const char *ctagsGetLangKinds(int lang) extern const char *ctagsGetKindName(char kind, int lang) { - const parserDefinition *def = geanyGetParserDefinition(lang); - unsigned int i; - - for (i = 0; i < def->kindCount; i++) - { - if (def->kindTable[i].letter == kind) - return def->kindTable[i].name; - } - return "unknown"; + kindDefinition *def = getLanguageKindForLetter (lang, kind); + return def ? def->name : "unknown"; } extern char ctagsGetKindFromName(const char *name, int lang) { - const parserDefinition *def = geanyGetParserDefinition(lang); - unsigned int i; - - for (i = 0; i < def->kindCount; i++) - { - if (strcmp(def->kindTable[i].name, name) == 0) - return def->kindTable[i].letter; - } - return '-'; + kindDefinition *def = getLanguageKindForName (lang, name); + return def ? def->letter : '-'; } extern bool ctagsIsUsingRegexParser(int lang) { - return geanyGetParserDefinition(lang)->method & METHOD_REGEX; + return geanyIsParserRegex(lang); } diff --git a/ctags/main/parse.c b/ctags/main/parse.c index 2ca011a928..ec222109ed 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -3279,10 +3279,10 @@ extern void geanyCreateTags(unsigned char *buffer, size_t bufferSize, teardownWriter(fileName); } -extern const parserDefinition *geanyGetParserDefinition (langType language) +extern const bool geanyIsParserRegex (langType language) { Assert (0 <= language && language < (int) LanguageCount); - return LanguageTable[language].def; + return LanguageTable[language].def->method & METHOD_REGEX; } #endif /* GEANY_CTAGS_LIB */ diff --git a/ctags/main/parse.h b/ctags/main/parse.h index 9639a128b4..c376b34948 100644 --- a/ctags/main/parse.h +++ b/ctags/main/parse.h @@ -138,7 +138,7 @@ extern void initializeParser (langType language); extern void geanyCreateTags(unsigned char *buffer, size_t bufferSize, const char *fileName, const langType language, passStartCallback passCallback, void *userData); -extern const parserDefinition *geanyGetParserDefinition (langType language); +extern const bool geanyIsParserRegex (langType language); #endif /* GEANY_CTAGS_LIB */ #ifdef HAVE_ICONV From 573b92d19bbc7557b16af7c0a593abc5a0b859a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 24 Apr 2019 17:45:09 +0200 Subject: [PATCH 25/46] Fix ctags warning because of cobol parser f/file kind which is reserved --- ctags/parsers/cobol.c | 2 +- src/tagmanager/tm_parser.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ctags/parsers/cobol.c b/ctags/parsers/cobol.c index 1ccc557ebf..db2f45559f 100644 --- a/ctags/parsers/cobol.c +++ b/ctags/parsers/cobol.c @@ -21,7 +21,7 @@ static tagRegexTable cobolTagRegexTable[] = { ")", "\\1", "d,data,data items", "i", NULL}, {"^[ \t]*[FSR]D[ \t]+([A-Z0-9][A-Z0-9-]*)\\.", "\\1", - "f,file,file descriptions (FD, SD, RD)", "i", NULL}, + "e,desc,file descriptions (FD, SD, RD)", "i", NULL}, {"^[ \t]*[0-9]+[ \t]+([A-Z0-9][A-Z0-9-]*)\\.", "\\1", "g,group,group items", "i", NULL}, {"^[ \t]*([A-Z0-9][A-Z0-9-]*)\\.", "\\1", diff --git a/src/tagmanager/tm_parser.c b/src/tagmanager/tm_parser.c index e5564ffa85..de56e095d0 100644 --- a/src/tagmanager/tm_parser.c +++ b/src/tagmanager/tm_parser.c @@ -423,7 +423,7 @@ static TMParserMapEntry map_R[] = { static TMParserMapEntry map_COBOL[] = { {'d', tm_tag_variable_t}, - {'f', tm_tag_function_t}, + {'e', tm_tag_function_t}, {'g', tm_tag_struct_t}, {'p', tm_tag_macro_t}, {'P', tm_tag_class_t}, From ca303c079b97693be57676e46127b8edc0496f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 24 Apr 2019 18:08:00 +0200 Subject: [PATCH 26/46] Add simple ActionScript test --- tests/ctags/Makefile.am | 1 + tests/ctags/simple.as | 44 ++++++++++++++++++++++++++++++++++++++ tests/ctags/simple.as.tags | 20 +++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 tests/ctags/simple.as create mode 100644 tests/ctags/simple.as.tags diff --git a/tests/ctags/Makefile.am b/tests/ctags/Makefile.am index 0525b4bfad..9082aa174b 100644 --- a/tests/ctags/Makefile.am +++ b/tests/ctags/Makefile.am @@ -270,6 +270,7 @@ test_sources = \ shebang.js \ signature.cpp \ simple.abc \ + simple.as \ simple.asciidoc \ simple.bas \ simple.cbl \ diff --git a/tests/ctags/simple.as b/tests/ctags/simple.as new file mode 100644 index 0000000000..94adc7dbae --- /dev/null +++ b/tests/ctags/simple.as @@ -0,0 +1,44 @@ +package MyPackage { + import flash.display.Sprite; + + public class AlarmClock { + public static const MODE_VISUAL = 1; + public static const MODE_AUDIO = 2; + public static const MODE_BOTH = 3; + } + + public interface IEventDispatcher + { + function addEventListener(type:String, listener:Function, + useCapture:Boolean=false, priority:int=0, + useWeakReference:Boolean = false):void; + function removeEventListener(type:String, listener:Function, + useCapture:Boolean=false):void; + function dispatchEvent(event:Event):Boolean; + function hasEventListener(type:String):Boolean; + function willTrigger(type:String):Boolean; + } + + public class FilledCircle extends Sprite { + public var player:Player; + + function FilledCircle():void { + var circle:Sprite = new Sprite(); + //TODO: fixme + circle.graphics.beginFill(0xFF794B); + //NOTE: ActionScript is dead + circle.graphics.drawCircle(50, 50, 30); + circle.graphics.endFill(); + addChild(circle); + } + + public function get myValue():Type{ + //anything here + return _desiredValue; + } + public function set myValue(value:Type):void{ + //anything here + _desiredProperty=value; + } + } +} diff --git a/tests/ctags/simple.as.tags b/tests/ctags/simple.as.tags new file mode 100644 index 0000000000..29d181e0f9 --- /dev/null +++ b/tests/ctags/simple.as.tags @@ -0,0 +1,20 @@ +# format=tagmanager +ActionScript is deadÌ524288Ö0 +AlarmClock ()Ì1Ö0 +FilledCircle ():voidÌ16Ö0 +FilledCircle (extends Sprite )Ì1Ö0 +IEventDispatcher ()Ì32Ö0 +MODE_AUDIO :Ì65536Ö0 +MODE_BOTH :Ì65536Ö0 +MODE_VISUAL :Ì65536Ö0 +MyPackageÌ512Ö0 +addEventListener (type:String, listener:Function,Ì16Ö0 +circle : SpriteÌ16384Ö0 +dispatchEvent (event:Event):Boolean;Ì16Ö0 +fixmeÌ524288Ö0 +hasEventListener (type:String):Boolean;Ì16Ö0 +myValue getÌ8Ö0 +myValue setÌ8Ö0 +player : PlayerÌ16384Ö0 +removeEventListener (type:String, listener:Function,Ì16Ö0 +willTrigger (type:String):Boolean;Ì16Ö0 From bc1dd6f361bb628efed452297c6ac0f3ad297d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Thu, 25 Apr 2019 13:27:36 +0200 Subject: [PATCH 27/46] Check also regex parser kind mappings With the past versions of ctags we didn't have access to regex parser kinds so we couldn't check if the Geany mappings are correct. This isn't the case any more so we can do the same with regex parsers like with other parsers and as a bonus we can eliminate one more diff from ctags. --- ctags/main/ctags-api.c | 6 ------ ctags/main/ctags-api.h | 1 - ctags/main/parse.c | 6 ------ ctags/main/parse.h | 1 - src/tagmanager/tm_parser.c | 5 ----- 5 files changed, 19 deletions(-) diff --git a/ctags/main/ctags-api.c b/ctags/main/ctags-api.c index 317a2481c4..14bbc76139 100644 --- a/ctags/main/ctags-api.c +++ b/ctags/main/ctags-api.c @@ -194,12 +194,6 @@ extern char ctagsGetKindFromName(const char *name, int lang) } -extern bool ctagsIsUsingRegexParser(int lang) -{ - return geanyIsParserRegex(lang); -} - - extern unsigned int ctagsGetLangCount(void) { return countParsers(); diff --git a/ctags/main/ctags-api.h b/ctags/main/ctags-api.h index fce9a94e26..e89d18ad70 100644 --- a/ctags/main/ctags-api.h +++ b/ctags/main/ctags-api.h @@ -49,7 +49,6 @@ extern int ctagsGetNamedLang(const char *name); extern const char *ctagsGetLangKinds(int lang); extern const char *ctagsGetKindName(char kind, int lang); extern char ctagsGetKindFromName(const char *name, int lang); -extern bool ctagsIsUsingRegexParser(int lang); extern unsigned int ctagsGetLangCount(void); #endif /* GEANY_CTAGS_LIB */ diff --git a/ctags/main/parse.c b/ctags/main/parse.c index ec222109ed..9f1d86104c 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -3279,12 +3279,6 @@ extern void geanyCreateTags(unsigned char *buffer, size_t bufferSize, teardownWriter(fileName); } -extern const bool geanyIsParserRegex (langType language) -{ - Assert (0 <= language && language < (int) LanguageCount); - return LanguageTable[language].def->method & METHOD_REGEX; -} - #endif /* GEANY_CTAGS_LIB */ static void printGuessedParser (const char* const fileName, langType language) diff --git a/ctags/main/parse.h b/ctags/main/parse.h index c376b34948..de82a42ecc 100644 --- a/ctags/main/parse.h +++ b/ctags/main/parse.h @@ -138,7 +138,6 @@ extern void initializeParser (langType language); extern void geanyCreateTags(unsigned char *buffer, size_t bufferSize, const char *fileName, const langType language, passStartCallback passCallback, void *userData); -extern const bool geanyIsParserRegex (langType language); #endif /* GEANY_CTAGS_LIB */ #ifdef HAVE_ICONV diff --git a/src/tagmanager/tm_parser.c b/src/tagmanager/tm_parser.c index de56e095d0..60a5a574c8 100644 --- a/src/tagmanager/tm_parser.c +++ b/src/tagmanager/tm_parser.c @@ -683,11 +683,6 @@ void tm_parser_verify_type_mappings(void) g_error("No tag types in TM for %s, is the language listed in parser_map?", ctagsGetLangName(lang)); - /* TODO: check also regex parser mappings. At the moment there's no way - * to access regex parser definitions in ctags */ - if (ctagsIsUsingRegexParser(lang)) - continue; - if (map->size != strlen(kinds)) g_error("Different number of tag types in TM (%d) and ctags (%d) for %s", map->size, (int)strlen(kinds), ctagsGetLangName(lang)); From 3de0df0872e237d955e291a27db913e650e8b8a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Thu, 25 Apr 2019 14:37:19 +0200 Subject: [PATCH 28/46] Keep the first parser in ctags CTagsSelfTestParser and remap Geany languages --- ctags/main/ctags-api.c | 23 +++++++++++++---------- ctags/main/parse.c | 7 ------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/ctags/main/ctags-api.c b/ctags/main/ctags-api.c index 14bbc76139..0150cf6832 100644 --- a/ctags/main/ctags-api.c +++ b/ctags/main/ctags-api.c @@ -28,6 +28,9 @@ #include #include +#define CTAGS_LANG(x) ((x) >= 0 ? (x) + 1 : (x)) +#define GEANY_LANG(x) ((x) >= 1 ? (x) - 1 : (x)) + static int writeEntry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag); tagWriter geanyWriter = { @@ -93,7 +96,7 @@ static void initCtagsTag(ctagsTag *tag, const tagEntryInfo *info) tag->kindLetter = getLanguageKind(info->langType, info->kindIndex)->letter; tag->isFileScope = info->isFileScope; tag->lineNumber = info->lineNumber; - tag->lang = info->langType; + tag->lang = GEANY_LANG(info->langType); } @@ -137,7 +140,7 @@ extern void ctagsInit(void) extern void ctagsParse(unsigned char *buffer, size_t bufferSize, - const char *fileName, const langType language, + const char *fileName, const int language, tagEntryFunction tagCallback, passStartCallback passCallback, void *userData) { @@ -149,31 +152,31 @@ extern void ctagsParse(unsigned char *buffer, size_t bufferSize, geanyTagEntryFunction = tagCallback; geanyTagEntryUserData = userData; - geanyCreateTags(buffer, bufferSize, fileName, language, + geanyCreateTags(buffer, bufferSize, fileName, CTAGS_LANG(language), passCallback, userData); } extern const char *ctagsGetLangName(int lang) { - return getLanguageName(lang); + return getLanguageName(CTAGS_LANG(lang)); } extern int ctagsGetNamedLang(const char *name) { - return getNamedLanguage(name, 0); + return GEANY_LANG(getNamedLanguage(name, 0)); } extern const char *ctagsGetLangKinds(int lang) { - unsigned int kindNum = countLanguageKinds(lang); + unsigned int kindNum = countLanguageKinds(CTAGS_LANG(lang)); static char kinds[257]; unsigned int i; for (i = 0; i < kindNum; i++) - kinds[i] = getLanguageKind(lang, i)->letter; + kinds[i] = getLanguageKind(CTAGS_LANG(lang), i)->letter; kinds[i] = '\0'; return kinds; @@ -182,21 +185,21 @@ extern const char *ctagsGetLangKinds(int lang) extern const char *ctagsGetKindName(char kind, int lang) { - kindDefinition *def = getLanguageKindForLetter (lang, kind); + kindDefinition *def = getLanguageKindForLetter (CTAGS_LANG(lang), kind); return def ? def->name : "unknown"; } extern char ctagsGetKindFromName(const char *name, int lang) { - kindDefinition *def = getLanguageKindForName (lang, name); + kindDefinition *def = getLanguageKindForName (CTAGS_LANG(lang), name); return def ? def->letter : '-'; } extern unsigned int ctagsGetLangCount(void) { - return countParsers(); + return GEANY_LANG(countParsers()); } #endif /* GEANY_CTAGS_LIB */ diff --git a/ctags/main/parse.c b/ctags/main/parse.c index 9f1d86104c..cf89bba969 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -103,13 +103,9 @@ static void teardownAnon (void); /* * DATA DEFINITIONS */ -#ifndef GEANY_CTAGS_LIB static parserDefinition *CTagsSelfTestParser (void); -#endif /* GEANY_CTAGS_LIB */ static parserDefinitionFunc* BuiltInParsers[] = { -#ifndef GEANY_CTAGS_LIB CTagsSelfTestParser, -#endif /* GEANY_CTAGS_LIB */ PARSER_LIST, XML_PARSER_LIST #ifdef HAVE_LIBXML @@ -4151,8 +4147,6 @@ extern void addLanguageTagMultiTableRegex(const langType language, name, kinds, flags, disabled); } -#ifndef GEANY_CTAGS_LIB -/* just to eliminate warning about unused functions */ /* * A parser for CTagsSelfTest (CTST) */ @@ -4390,4 +4384,3 @@ static parserDefinition *CTagsSelfTestParser (void) def->useCork = true; return def; } -#endif /* GEANY_CTAGS_LIB */ From b22b17e1aa22dbf2f3a11a84304a024f611961a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Thu, 25 Apr 2019 15:59:48 +0200 Subject: [PATCH 29/46] Make Geany informed about failed reparses "the right way" At the moment we pass a callback to ctags which informs us about re-parses and based on which we clear our tag list when a re-parse happens. The current solution isn't very clean because it introduces new callback function and many diffs into parse.c and in addition it doesn't do the correct thing when re-parses happen within subparsers. Instead, we can introduce a new callback function into writers, use this function and also get informed about how many tags are valid when a reparse happens and keep only that number in our array list. --- ctags/main/ctags-api.c | 22 ++++++++++++++++------ ctags/main/ctags-api.h | 7 +++---- ctags/main/parse.c | 33 ++++----------------------------- ctags/main/parse.h | 4 +--- ctags/main/writer-ctags.c | 6 ++++++ ctags/main/writer-etags.c | 3 +++ ctags/main/writer-json.c | 3 +++ ctags/main/writer-xref.c | 3 +++ ctags/main/writer.c | 8 ++++++++ ctags/main/writer_p.h | 10 +++++++--- src/tagmanager/tm_source_file.c | 16 +++++++++++----- 11 files changed, 65 insertions(+), 50 deletions(-) diff --git a/ctags/main/ctags-api.c b/ctags/main/ctags-api.c index 0150cf6832..59b9d192d0 100644 --- a/ctags/main/ctags-api.c +++ b/ctags/main/ctags-api.c @@ -32,12 +32,14 @@ #define GEANY_LANG(x) ((x) >= 1 ? (x) - 1 : (x)) static int writeEntry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag); +static void rescanFailed (tagWriter *writer, unsigned long validTagNum); tagWriter geanyWriter = { .writeEntry = writeEntry, .writePtagEntry = NULL, /* no pseudo-tags */ .preWriteEntry = NULL, .postWriteEntry = NULL, + .rescanFailedEntry = rescanFailed, .buildFqTagCache = NULL, .defaultFileName = "geany_tags_file_which_should_never_appear_anywhere", .private = NULL, @@ -45,7 +47,8 @@ tagWriter geanyWriter = { }; static tagEntryFunction geanyTagEntryFunction = NULL; -static void *geanyTagEntryUserData = NULL; +static rescanFailedFunction geanyRescanFailedFunction = NULL; +static void *geanyUserData = NULL; static bool nofatalErrorPrinter (const errorSelection selection, @@ -106,13 +109,19 @@ static int writeEntry (tagWriter *writer, MIO * mio, const tagEntryInfo *const t getTagScopeInformation((tagEntryInfo *)tag, NULL, NULL); initCtagsTag(&t, tag); - geanyTagEntryFunction(&t, geanyTagEntryUserData); + geanyTagEntryFunction(&t, geanyUserData); /* output length - we don't write anything to the MIO */ return 0; } +static void rescanFailed (tagWriter *writer, unsigned long validTagNum) +{ + geanyRescanFailedFunction (validTagNum, geanyUserData); +} + + /* keep in sync with ctags main() - use only things interesting for us */ extern void ctagsInit(void) { @@ -141,7 +150,7 @@ extern void ctagsInit(void) extern void ctagsParse(unsigned char *buffer, size_t bufferSize, const char *fileName, const int language, - tagEntryFunction tagCallback, passStartCallback passCallback, + tagEntryFunction tagCallback, rescanFailedFunction rescanCallback, void *userData) { if (buffer == NULL && fileName == NULL) @@ -151,9 +160,10 @@ extern void ctagsParse(unsigned char *buffer, size_t bufferSize, } geanyTagEntryFunction = tagCallback; - geanyTagEntryUserData = userData; - geanyCreateTags(buffer, bufferSize, fileName, CTAGS_LANG(language), - passCallback, userData); + geanyRescanFailedFunction = rescanCallback; + geanyUserData = userData; + + geanyCreateTags(buffer, bufferSize, fileName, CTAGS_LANG(language)); } diff --git a/ctags/main/ctags-api.h b/ctags/main/ctags-api.h index e89d18ad70..10f464cb67 100644 --- a/ctags/main/ctags-api.h +++ b/ctags/main/ctags-api.h @@ -34,15 +34,14 @@ typedef struct { * currently unused. */ typedef bool (*tagEntryFunction) (const ctagsTag *const tag, void *userData); -/* Callback invoked at the beginning of every parsing pass. The return value is - * currently unused */ -typedef bool (*passStartCallback) (void *userData); +/* Callback invoked when rescan fails telling us how many of the passed tags are valid */ +typedef void (*rescanFailedFunction) (unsigned long validTagNum, void *userData); extern void ctagsInit(void); extern void ctagsParse(unsigned char *buffer, size_t bufferSize, const char *fileName, const int language, - tagEntryFunction tagCallback, passStartCallback passCallback, + tagEntryFunction tagCallback, rescanFailedFunction rescanCallback, void *userData); extern const char *ctagsGetLangName(int lang); extern int ctagsGetNamedLang(const char *name); diff --git a/ctags/main/parse.c b/ctags/main/parse.c index cf89bba969..fac8393868 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -3099,12 +3099,6 @@ static subparser* teardownLanguageSubparsersInUse (const langType language) return teardownSubparsersInUse ((LanguageTable + language)->slaveControlBlock); } -#ifdef GEANY_CTAGS_LIB -static passStartCallback geanyPassCallback; -static void *geanyUserData; -static bool geanyNarrowedContext; -#endif /* GEANY_CTAGS_LIB */ - static bool createTagsWithFallback1 (const langType language, langType *exclusive_subparser) { @@ -3131,10 +3125,6 @@ static bool createTagsWithFallback1 (const langType language, anonResetMaybe (parser); -#ifdef GEANY_CTAGS_LIB - if (!geanyNarrowedContext) - geanyPassCallback(geanyUserData); -#endif /* GEANY_CTAGS_LIB */ while ( ( whyRescan = createTagsForFile (language, ++passCount) ) != RESCAN_NONE) @@ -3152,6 +3142,9 @@ static bool createTagsWithFallback1 (const langType language, */ setTagFilePosition (&tagfpos); setNumTagsAdded (numTags); +#ifdef GEANY_CTAGS_LIB + writerRescanFailed (numTags); +#endif /* GEANY_CTAGS_LIB */ tagFileResized = true; breakPromisesAfter(lastPromise); } @@ -3161,16 +3154,6 @@ static bool createTagsWithFallback1 (const langType language, numTags = numTagsAdded (); lastPromise = getLastPromise (); } - -#ifdef GEANY_CTAGS_LIB - if (!geanyNarrowedContext) - { - if (passCount < 3) - geanyPassCallback(geanyUserData); - else - break; - } -#endif /* GEANY_CTAGS_LIB */ } /* Force filling allLines buffer and kick the multiline regex parser */ @@ -3213,9 +3196,6 @@ extern bool runParserInNarrowedInputStream (const langType language, endLine, endCharOffset, sourceLineOffset, promise); -#ifdef GEANY_CTAGS_LIB - geanyNarrowedContext = true; -#endif tagFileResized = createTagsWithFallback1 (language, NULL); popNarrowedInputStream (); return tagFileResized; @@ -3251,18 +3231,13 @@ static bool createTagsWithFallback ( #ifdef GEANY_CTAGS_LIB extern void geanyCreateTags(unsigned char *buffer, size_t bufferSize, - const char *fileName, const langType language, passStartCallback passCallback, - void *userData) + const char *fileName, const langType language) { MIO *mio = NULL; if (buffer) mio = mio_new_memory (buffer, bufferSize, NULL, NULL); - geanyPassCallback = passCallback; - geanyUserData = userData; - geanyNarrowedContext = false; - /* keep in sync with parseFileWithMio() */ setupWriter (); setupAnon (); diff --git a/ctags/main/parse.h b/ctags/main/parse.h index de82a42ecc..2da54dd742 100644 --- a/ctags/main/parse.h +++ b/ctags/main/parse.h @@ -134,10 +134,8 @@ extern kindDefinition* getLanguageKindForLetter (const langType language, char k extern void initializeParser (langType language); #ifdef GEANY_CTAGS_LIB -#include "ctags-api.h" extern void geanyCreateTags(unsigned char *buffer, size_t bufferSize, - const char *fileName, const langType language, passStartCallback passCallback, - void *userData); + const char *fileName, const langType language); #endif /* GEANY_CTAGS_LIB */ #ifdef HAVE_ICONV diff --git a/ctags/main/writer-ctags.c b/ctags/main/writer-ctags.c index 9a8a14419b..c9fca36c4d 100644 --- a/ctags/main/writer-ctags.c +++ b/ctags/main/writer-ctags.c @@ -40,6 +40,9 @@ tagWriter uCtagsWriter = { .writePtagEntry = writeCtagsPtagEntry, .preWriteEntry = NULL, .postWriteEntry = NULL, +#ifdef GEANY_CTAGS_LIB + .rescanFailedEntry = NULL, +#endif /* GEANY_CTAGS_LIB */ .buildFqTagCache = buildCtagsFqTagCache, .defaultFileName = CTAGS_FILE, }; @@ -64,6 +67,9 @@ tagWriter eCtagsWriter = { .writePtagEntry = writeCtagsPtagEntry, .preWriteEntry = beginECtagsFile, .postWriteEntry = endECTagsFile, +#ifdef GEANY_CTAGS_LIB + .rescanFailedEntry = NULL, +#endif /* GEANY_CTAGS_LIB */ .buildFqTagCache = buildCtagsFqTagCache, .defaultFileName = CTAGS_FILE, }; diff --git a/ctags/main/writer-etags.c b/ctags/main/writer-etags.c index 6af0829bd7..2eb0adaa29 100644 --- a/ctags/main/writer-etags.c +++ b/ctags/main/writer-etags.c @@ -33,6 +33,9 @@ tagWriter etagsWriter = { .writePtagEntry = NULL, .preWriteEntry = beginEtagsFile, .postWriteEntry = endEtagsFile, +#ifdef GEANY_CTAGS_LIB + .rescanFailedEntry = NULL, +#endif /* GEANY_CTAGS_LIB */ .defaultFileName = ETAGS_FILE, }; diff --git a/ctags/main/writer-json.c b/ctags/main/writer-json.c index 789eecd3db..263126fd4b 100644 --- a/ctags/main/writer-json.c +++ b/ctags/main/writer-json.c @@ -43,6 +43,9 @@ tagWriter jsonWriter = { .writePtagEntry = writeJsonPtagEntry, .preWriteEntry = NULL, .postWriteEntry = NULL, +#ifdef GEANY_CTAGS_LIB + .rescanFailedEntry = NULL, +#endif /* GEANY_CTAGS_LIB */ .buildFqTagCache = buildJsonFqTagCache, .defaultFileName = NULL, }; diff --git a/ctags/main/writer-xref.c b/ctags/main/writer-xref.c index 86b16bceb5..4e410800ef 100644 --- a/ctags/main/writer-xref.c +++ b/ctags/main/writer-xref.c @@ -25,6 +25,9 @@ tagWriter xrefWriter = { .writePtagEntry = NULL, .preWriteEntry = NULL, .postWriteEntry = NULL, +#ifdef GEANY_CTAGS_LIB + .rescanFailedEntry = NULL, +#endif /* GEANY_CTAGS_LIB */ .buildFqTagCache = buildXrefFqTagCache, .defaultFileName = NULL, }; diff --git a/ctags/main/writer.c b/ctags/main/writer.c index 2b59706aff..1c01a4dac7 100644 --- a/ctags/main/writer.c +++ b/ctags/main/writer.c @@ -79,6 +79,14 @@ extern int writerWritePtag (MIO * mio, } +#ifdef GEANY_CTAGS_LIB +extern void writerRescanFailed (unsigned long validTagNum) +{ + if (writer->rescanFailedEntry) + writer->rescanFailedEntry(writer, validTagNum); +} +#endif /* GEANY_CTAGS_LIB */ + extern void writerBuildFqTagCache (tagEntryInfo *const tag) { if (writer->buildFqTagCache) diff --git a/ctags/main/writer_p.h b/ctags/main/writer_p.h index db920dc302..ca92feb0a4 100644 --- a/ctags/main/writer_p.h +++ b/ctags/main/writer_p.h @@ -41,6 +41,9 @@ struct sTagWriter { /* Returning TRUE means the output file may be shrunk. In such case the callee may do truncate output file. */ bool (* postWriteEntry) (tagWriter *writer, MIO * mio, const char* filename); +#ifdef GEANY_CTAGS_LIB + void (* rescanFailedEntry) (tagWriter *writer, unsigned long validTagNum); +#endif /* GEANY_CTAGS_LIB */ void (* buildFqTagCache) (tagWriter *writer, tagEntryInfo *const tag); const char *defaultFileName; @@ -51,9 +54,6 @@ struct sTagWriter { }; -#ifdef GEANY_CTAGS_LIB -extern void geanySetTagWriter(tagWriter *w); -#endif /* GEANY_CTAGS_LIB */ extern void setTagWriter (writerType otype); extern void writerSetup (MIO *mio); extern bool writerTeardown (MIO *mio, const char *filename); @@ -64,6 +64,10 @@ int writerWritePtag (MIO * mio, const char *const fileName, const char *const pattern, const char *const parserName); +#ifdef GEANY_CTAGS_LIB +extern void geanySetTagWriter(tagWriter *w); +void writerRescanFailed (unsigned long validTagNum); +#endif extern void writerBuildFqTagCache (tagEntryInfo *const tag); diff --git a/src/tagmanager/tm_source_file.c b/src/tagmanager/tm_source_file.c index ccbd3aef7a..4f23555f26 100644 --- a/src/tagmanager/tm_source_file.c +++ b/src/tagmanager/tm_source_file.c @@ -642,13 +642,19 @@ static void update_python_arglist(const TMTag *tag, TMSourceFile *current_source } } -/* new parsing pass ctags callback function */ -static bool ctags_pass_start(void *user_data) +/* callback function informing us about rescan failure */ +static void ctags_rescan_failed(unsigned long valid_tag_num, void *user_data) { TMSourceFile *current_source_file = user_data; + GPtrArray *tags_array = current_source_file->tags_array; - tm_tags_array_free(current_source_file->tags_array, FALSE); - return TRUE; + if (tags_array->len > valid_tag_num) + { + guint i; + for (i = valid_tag_num; i < tags_array->len; i++) + tm_tag_unref(tags_array->pdata[i]); + g_ptr_array_set_size(tags_array, valid_tag_num); + } } /* new tag ctags callback function */ @@ -827,7 +833,7 @@ gboolean tm_source_file_parse(TMSourceFile *source_file, guchar* text_buf, gsize tm_tags_array_free(source_file->tags_array, FALSE); ctagsParse(use_buffer ? text_buf : NULL, buf_size, file_name, - source_file->lang, ctags_new_tag, ctags_pass_start, source_file); + source_file->lang, ctags_new_tag, ctags_rescan_failed, source_file); return !retry; } From aeef700767660a75da5cd456c4adbee03b5da300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Thu, 25 Apr 2019 16:18:27 +0200 Subject: [PATCH 30/46] Move ctags-api.c/h into tagmanager and rename it to tm_ctags.c/h Since we now don't define any types in ctags-api.h that would be used inside ctags, we can move it to tagmanager so there's not this extra file in the ctags directory. The real "ctags API" are the internal functions ctags uses and which we use for our purposes and tm_ctags.c/h just encapsulate ctags so we don't include ctags headers directly in the rest of Geany. --- ctags/Makefile.am | 2 -- src/tagmanager/Makefile.am | 2 ++ ctags/main/ctags-api.c => src/tagmanager/tm_ctags.c | 6 +----- ctags/main/ctags-api.h => src/tagmanager/tm_ctags.h | 12 +++--------- src/tagmanager/tm_parser.c | 2 +- src/tagmanager/tm_source_file.c | 2 +- src/tagmanager/tm_tag.c | 2 +- src/tagmanager/tm_workspace.c | 2 +- 8 files changed, 10 insertions(+), 20 deletions(-) rename ctags/main/ctags-api.c => src/tagmanager/tm_ctags.c (98%) rename ctags/main/ctags-api.h => src/tagmanager/tm_ctags.h (89%) diff --git a/ctags/Makefile.am b/ctags/Makefile.am index 91d6f21f43..f9940ee7da 100644 --- a/ctags/Makefile.am +++ b/ctags/Makefile.am @@ -58,8 +58,6 @@ libctags_la_SOURCES = \ main/colprint.c \ main/colprint_p.h \ main/ctags.h \ - main/ctags-api.c \ - main/ctags-api.h \ main/debug.h \ main/debug.c \ main/dependency.h \ diff --git a/src/tagmanager/Makefile.am b/src/tagmanager/Makefile.am index e286961eca..15ddb8afe6 100644 --- a/src/tagmanager/Makefile.am +++ b/src/tagmanager/Makefile.am @@ -18,6 +18,8 @@ tagmanager_include_HEADERS = \ libtagmanager_la_SOURCES =\ + tm_ctags.h \ + tm_ctags.c \ tm_parser.h \ tm_parser.c \ tm_source_file.h \ diff --git a/ctags/main/ctags-api.c b/src/tagmanager/tm_ctags.c similarity index 98% rename from ctags/main/ctags-api.c rename to src/tagmanager/tm_ctags.c index 59b9d192d0..21cc273ab1 100644 --- a/ctags/main/ctags-api.c +++ b/src/tagmanager/tm_ctags.c @@ -9,9 +9,7 @@ #include "general.h" /* must always come first */ -#ifdef GEANY_CTAGS_LIB - -#include "ctags-api.h" +#include "tm_ctags.h" #include "types.h" #include "routines.h" #include "error.h" @@ -211,5 +209,3 @@ extern unsigned int ctagsGetLangCount(void) { return GEANY_LANG(countParsers()); } - -#endif /* GEANY_CTAGS_LIB */ diff --git a/ctags/main/ctags-api.h b/src/tagmanager/tm_ctags.h similarity index 89% rename from ctags/main/ctags-api.h rename to src/tagmanager/tm_ctags.h index 10f464cb67..ae2f1bda7b 100644 --- a/ctags/main/ctags-api.h +++ b/src/tagmanager/tm_ctags.h @@ -6,12 +6,8 @@ * * Defines ctags API when compiled as a library. */ -#ifndef CTAGS_API_H -#define CTAGS_API_H - -#include "general.h" /* must always come first */ - -#ifdef GEANY_CTAGS_LIB +#ifndef TM_CTAGS_H +#define TM_CTAGS_H #include #include @@ -50,6 +46,4 @@ extern const char *ctagsGetKindName(char kind, int lang); extern char ctagsGetKindFromName(const char *name, int lang); extern unsigned int ctagsGetLangCount(void); -#endif /* GEANY_CTAGS_LIB */ - -#endif /* CTAGS_API_H */ +#endif /* TM_CTAGS_H */ diff --git a/src/tagmanager/tm_parser.c b/src/tagmanager/tm_parser.c index 60a5a574c8..e6b156de11 100644 --- a/src/tagmanager/tm_parser.c +++ b/src/tagmanager/tm_parser.c @@ -19,7 +19,7 @@ */ #include "tm_parser.h" -#include "ctags-api.h" +#include "tm_ctags.h" #include diff --git a/src/tagmanager/tm_source_file.c b/src/tagmanager/tm_source_file.c index 4f23555f26..d7b02d81f9 100644 --- a/src/tagmanager/tm_source_file.c +++ b/src/tagmanager/tm_source_file.c @@ -31,7 +31,7 @@ #include "tm_source_file.h" #include "tm_tag.h" #include "tm_parser.h" -#include "ctags-api.h" +#include "tm_ctags.h" typedef struct { diff --git a/src/tagmanager/tm_tag.c b/src/tagmanager/tm_tag.c index 37121a1364..b16f3bcf94 100644 --- a/src/tagmanager/tm_tag.c +++ b/src/tagmanager/tm_tag.c @@ -13,7 +13,7 @@ #include #include "tm_tag.h" -#include "ctags-api.h" +#include "tm_ctags.h" #define TAG_NEW(T) ((T) = g_slice_new0(TMTag)) diff --git a/src/tagmanager/tm_workspace.c b/src/tagmanager/tm_workspace.c index 6b2ef82f3b..97090999ae 100644 --- a/src/tagmanager/tm_workspace.c +++ b/src/tagmanager/tm_workspace.c @@ -32,7 +32,7 @@ #include #include "tm_workspace.h" -#include "ctags-api.h" +#include "tm_ctags.h" #include "tm_tag.h" #include "tm_parser.h" From 266283c9a1aa3aa22bc384e2c61acb61135926fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Thu, 25 Apr 2019 17:16:04 +0200 Subject: [PATCH 31/46] Eliminate intermediate data types and callbacks Since tm_ctags is inside tag manager, we have direct access to TMTag so we can eliminate the auxiliary ctagsTag type. Similarly, we can access tag manager structures directly from ctags writer callbacks so we can eliminate tagEntryFunction and rescanFailedFunction too. The rest of the patch just moves code around so we do the things directly at the right place. --- src/tagmanager/tm_ctags.c | 137 ++++++++++++++++++++++++++------ src/tagmanager/tm_ctags.h | 26 +----- src/tagmanager/tm_source_file.c | 127 ++--------------------------- src/tagmanager/tm_source_file.h | 4 + 4 files changed, 122 insertions(+), 172 deletions(-) diff --git a/src/tagmanager/tm_ctags.c b/src/tagmanager/tm_ctags.c index 21cc273ab1..95bd51ce43 100644 --- a/src/tagmanager/tm_ctags.c +++ b/src/tagmanager/tm_ctags.c @@ -22,6 +22,8 @@ #include "xtag.h" #include "entry_p.h" +#include "tm_tag.h" + #include #include #include @@ -44,9 +46,7 @@ tagWriter geanyWriter = { .type = WRITER_U_CTAGS /* not really but we must use some of the builtin types */ }; -static tagEntryFunction geanyTagEntryFunction = NULL; -static rescanFailedFunction geanyRescanFailedFunction = NULL; -static void *geanyUserData = NULL; +static TMSourceFile *current_source_file = NULL; static bool nofatalErrorPrinter (const errorSelection selection, @@ -85,38 +85,127 @@ static void enableAllLangKinds() } -static void initCtagsTag(ctagsTag *tag, const tagEntryInfo *info) +/* + Initializes a TMTag structure with information from a ctagsTag struct + used by the ctags parsers. Note that the TMTag structure must be malloc()ed + before calling this function. + @param tag The TMTag structure to initialize + @param file Pointer to a TMSourceFile struct (it is assigned to the file member) + @param tag_entry Tag information gathered by the ctags parser + @return TRUE on success, FALSE on failure +*/ +static gboolean init_tag(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag_entry) +{ + TMTagType type; + guchar kind_letter; + TMParserType lang; + + if (!tag_entry) + return FALSE; + + lang = GEANY_LANG(tag_entry->langType); + kind_letter = getLanguageKind(tag_entry->langType, tag_entry->kindIndex)->letter; + type = tm_parser_get_tag_type(kind_letter, lang); + if (file->lang != lang) /* this is a tag from a subparser */ + { + /* check for possible re-definition of subparser type */ + type = tm_parser_get_subparser_type(file->lang, lang, type); + } + + if (!tag_entry->name || type == tm_tag_undef_t) + return FALSE; + + tag->name = g_strdup(tag_entry->name); + tag->type = type; + tag->local = tag_entry->isFileScope; + tag->pointerOrder = 0; /* backward compatibility (use var_type instead) */ + tag->line = tag_entry->lineNumber; + if (NULL != tag_entry->extensionFields.signature) + tag->arglist = g_strdup(tag_entry->extensionFields.signature); + if ((NULL != tag_entry->extensionFields.scopeName) && + (0 != tag_entry->extensionFields.scopeName[0])) + tag->scope = g_strdup(tag_entry->extensionFields.scopeName); + if (tag_entry->extensionFields.inheritance != NULL) + tag->inheritance = g_strdup(tag_entry->extensionFields.inheritance); + if (tag_entry->extensionFields.typeRef[1] != NULL) + tag->var_type = g_strdup(tag_entry->extensionFields.typeRef[1]); + if (tag_entry->extensionFields.access != NULL) + tag->access = tm_source_file_get_tag_access(tag_entry->extensionFields.access); + if (tag_entry->extensionFields.implementation != NULL) + tag->impl = tm_source_file_get_tag_impl(tag_entry->extensionFields.implementation); + if ((tm_tag_macro_t == tag->type) && (NULL != tag->arglist)) + tag->type = tm_tag_macro_with_arg_t; + tag->file = file; + /* redefine lang also for subparsers because the rest of Geany assumes that + * tags from a single file are from a single language */ + tag->lang = file->lang; + return TRUE; +} + + +/* add argument list of __init__() Python methods to the class tag */ +static void update_python_arglist(const TMTag *tag, TMSourceFile *current_source_file) { - tag->name = info->name; - tag->signature = info->extensionFields.signature; - tag->scopeName = info->extensionFields.scopeName; - tag->inheritance = info->extensionFields.inheritance; - tag->varType = info->extensionFields.typeRef[1]; - tag->access = info->extensionFields.access; - tag->implementation = info->extensionFields.implementation; - tag->kindLetter = getLanguageKind(info->langType, info->kindIndex)->letter; - tag->isFileScope = info->isFileScope; - tag->lineNumber = info->lineNumber; - tag->lang = GEANY_LANG(info->langType); + guint i; + const char *parent_tag_name; + + if (tag->type != tm_tag_method_t || tag->scope == NULL || + g_strcmp0(tag->name, "__init__") != 0) + return; + + parent_tag_name = strrchr(tag->scope, '.'); + if (parent_tag_name) + parent_tag_name++; + else + parent_tag_name = tag->scope; + + /* going in reverse order because the tag was added recently */ + for (i = current_source_file->tags_array->len; i > 0; i--) + { + TMTag *prev_tag = (TMTag *) current_source_file->tags_array->pdata[i - 1]; + if (g_strcmp0(prev_tag->name, parent_tag_name) == 0) + { + g_free(prev_tag->arglist); + prev_tag->arglist = g_strdup(tag->arglist); + break; + } + } } static int writeEntry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag) { - ctagsTag t; + TMTag *tm_tag = tm_tag_new(); getTagScopeInformation((tagEntryInfo *)tag, NULL, NULL); - initCtagsTag(&t, tag); - geanyTagEntryFunction(&t, geanyUserData); + + if (!init_tag(tm_tag, current_source_file, tag)) + { + tm_tag_unref(tm_tag); + return 0; + } + + if (tm_tag->lang == TM_PARSER_PYTHON) + update_python_arglist(tm_tag, current_source_file); + + g_ptr_array_add(current_source_file->tags_array, tm_tag); /* output length - we don't write anything to the MIO */ return 0; } -static void rescanFailed (tagWriter *writer, unsigned long validTagNum) +static void rescanFailed (tagWriter *writer, unsigned long valid_tag_num) { - geanyRescanFailedFunction (validTagNum, geanyUserData); + GPtrArray *tags_array = current_source_file->tags_array; + + if (tags_array->len > valid_tag_num) + { + guint i; + for (i = valid_tag_num; i < tags_array->len; i++) + tm_tag_unref(tags_array->pdata[i]); + g_ptr_array_set_size(tags_array, valid_tag_num); + } } @@ -147,9 +236,7 @@ extern void ctagsInit(void) extern void ctagsParse(unsigned char *buffer, size_t bufferSize, - const char *fileName, const int language, - tagEntryFunction tagCallback, rescanFailedFunction rescanCallback, - void *userData) + const char *fileName, const int language, TMSourceFile *source_file) { if (buffer == NULL && fileName == NULL) { @@ -157,9 +244,7 @@ extern void ctagsParse(unsigned char *buffer, size_t bufferSize, return; } - geanyTagEntryFunction = tagCallback; - geanyRescanFailedFunction = rescanCallback; - geanyUserData = userData; + current_source_file = source_file; geanyCreateTags(buffer, bufferSize, fileName, CTAGS_LANG(language)); } diff --git a/src/tagmanager/tm_ctags.h b/src/tagmanager/tm_ctags.h index ae2f1bda7b..8256336290 100644 --- a/src/tagmanager/tm_ctags.h +++ b/src/tagmanager/tm_ctags.h @@ -12,33 +12,11 @@ #include #include -typedef struct { - const char *name; - const char *signature; - const char *scopeName; - const char *inheritance; - const char *varType; - const char *access; - const char *implementation; - char kindLetter; - bool isFileScope; - unsigned long lineNumber; - int lang; -} ctagsTag; - -/* Callback invoked for every tag found by the parser. The return value is - * currently unused. */ -typedef bool (*tagEntryFunction) (const ctagsTag *const tag, void *userData); - -/* Callback invoked when rescan fails telling us how many of the passed tags are valid */ -typedef void (*rescanFailedFunction) (unsigned long validTagNum, void *userData); - +#include "tm_source_file.h" extern void ctagsInit(void); extern void ctagsParse(unsigned char *buffer, size_t bufferSize, - const char *fileName, const int language, - tagEntryFunction tagCallback, rescanFailedFunction rescanCallback, - void *userData); + const char *fileName, const int language, TMSourceFile *source_file); extern const char *ctagsGetLangName(int lang); extern int ctagsGetNamedLang(const char *name); extern const char *ctagsGetLangKinds(int lang); diff --git a/src/tagmanager/tm_source_file.c b/src/tagmanager/tm_source_file.c index d7b02d81f9..f6229f9bc2 100644 --- a/src/tagmanager/tm_source_file.c +++ b/src/tagmanager/tm_source_file.c @@ -135,7 +135,7 @@ gchar *tm_get_real_path(const gchar *file_name) return NULL; } -static char get_tag_impl(const char *impl) +gchar tm_source_file_get_tag_impl(const gchar *impl) { if ((0 == strcmp("virtual", impl)) || (0 == strcmp("pure virtual", impl))) @@ -147,7 +147,7 @@ static char get_tag_impl(const char *impl) return TAG_IMPL_UNKNOWN; } -static char get_tag_access(const char *access) +gchar tm_source_file_get_tag_access(const gchar *access) { if (0 == strcmp("public", access)) return TAG_ACCESS_PUBLIC; @@ -166,59 +166,6 @@ static char get_tag_access(const char *access) return TAG_ACCESS_UNKNOWN; } -/* - Initializes a TMTag structure with information from a ctagsTag struct - used by the ctags parsers. Note that the TMTag structure must be malloc()ed - before calling this function. - @param tag The TMTag structure to initialize - @param file Pointer to a TMSourceFile struct (it is assigned to the file member) - @param tag_entry Tag information gathered by the ctags parser - @return TRUE on success, FALSE on failure -*/ -static gboolean init_tag(TMTag *tag, TMSourceFile *file, const ctagsTag *tag_entry) -{ - TMTagType type; - - if (!tag_entry) - return FALSE; - - type = tm_parser_get_tag_type(tag_entry->kindLetter, tag_entry->lang); - if (file->lang != tag_entry->lang) /* this is a tag from a subparser */ - { - /* check for possible re-definition of subparser type */ - type = tm_parser_get_subparser_type(file->lang, tag_entry->lang, type); - } - - if (!tag_entry->name || type == tm_tag_undef_t) - return FALSE; - - tag->name = g_strdup(tag_entry->name); - tag->type = type; - tag->local = tag_entry->isFileScope; - tag->pointerOrder = 0; /* backward compatibility (use var_type instead) */ - tag->line = tag_entry->lineNumber; - if (NULL != tag_entry->signature) - tag->arglist = g_strdup(tag_entry->signature); - if ((NULL != tag_entry->scopeName) && - (0 != tag_entry->scopeName[0])) - tag->scope = g_strdup(tag_entry->scopeName); - if (tag_entry->inheritance != NULL) - tag->inheritance = g_strdup(tag_entry->inheritance); - if (tag_entry->varType != NULL) - tag->var_type = g_strdup(tag_entry->varType); - if (tag_entry->access != NULL) - tag->access = get_tag_access(tag_entry->access); - if (tag_entry->implementation != NULL) - tag->impl = get_tag_impl(tag_entry->implementation); - if ((tm_tag_macro_t == tag->type) && (NULL != tag->arglist)) - tag->type = tm_tag_macro_with_arg_t; - tag->file = file; - /* redefine lang also for subparsers because the rest of Geany assumes that - * tags from a single file are from a single language */ - tag->lang = file->lang; - return TRUE; -} - /* Initializes an already malloc()ed TMTag structure by reading a tag entry line from a file. The structure should be allocated beforehand. @@ -438,11 +385,11 @@ static gboolean init_tag_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *f tag->inheritance = g_strdup(value); } else if (0 == strcmp(key, "implementation")) /* implementation limit */ - tag->impl = get_tag_impl(value); + tag->impl = tm_source_file_get_tag_impl(value); else if (0 == strcmp(key, "line")) /* line */ tag->line = atol(value); else if (0 == strcmp(key, "access")) /* access */ - tag->access = get_tag_access(value); + tag->access = tm_source_file_get_tag_access(value); else if (0 == strcmp(key, "class") || 0 == strcmp(key, "enum") || 0 == strcmp(key, "function") || @@ -613,70 +560,6 @@ gboolean tm_source_file_write_tags_file(const gchar *tags_file, GPtrArray *tags_ return ret; } -/* add argument list of __init__() Python methods to the class tag */ -static void update_python_arglist(const TMTag *tag, TMSourceFile *current_source_file) -{ - guint i; - const char *parent_tag_name; - - if (tag->type != tm_tag_method_t || tag->scope == NULL || - g_strcmp0(tag->name, "__init__") != 0) - return; - - parent_tag_name = strrchr(tag->scope, '.'); - if (parent_tag_name) - parent_tag_name++; - else - parent_tag_name = tag->scope; - - /* going in reverse order because the tag was added recently */ - for (i = current_source_file->tags_array->len; i > 0; i--) - { - TMTag *prev_tag = (TMTag *) current_source_file->tags_array->pdata[i - 1]; - if (g_strcmp0(prev_tag->name, parent_tag_name) == 0) - { - g_free(prev_tag->arglist); - prev_tag->arglist = g_strdup(tag->arglist); - break; - } - } -} - -/* callback function informing us about rescan failure */ -static void ctags_rescan_failed(unsigned long valid_tag_num, void *user_data) -{ - TMSourceFile *current_source_file = user_data; - GPtrArray *tags_array = current_source_file->tags_array; - - if (tags_array->len > valid_tag_num) - { - guint i; - for (i = valid_tag_num; i < tags_array->len; i++) - tm_tag_unref(tags_array->pdata[i]); - g_ptr_array_set_size(tags_array, valid_tag_num); - } -} - -/* new tag ctags callback function */ -static bool ctags_new_tag(const ctagsTag *const tag, - void *user_data) -{ - TMSourceFile *current_source_file = user_data; - TMTag *tm_tag = tm_tag_new(); - - if (!init_tag(tm_tag, current_source_file, tag)) - { - tm_tag_unref(tm_tag); - return TRUE; - } - - if (tm_tag->lang == TM_PARSER_PYTHON) - update_python_arglist(tm_tag, current_source_file); - - g_ptr_array_add(current_source_file->tags_array, tm_tag); - - return TRUE; -} /* Initializes a TMSourceFile structure from a file name. */ static gboolean tm_source_file_init(TMSourceFile *source_file, const char *file_name, @@ -833,7 +716,7 @@ gboolean tm_source_file_parse(TMSourceFile *source_file, guchar* text_buf, gsize tm_tags_array_free(source_file->tags_array, FALSE); ctagsParse(use_buffer ? text_buf : NULL, buf_size, file_name, - source_file->lang, ctags_new_tag, ctags_rescan_failed, source_file); + source_file->lang, source_file); return !retry; } diff --git a/src/tagmanager/tm_source_file.h b/src/tagmanager/tm_source_file.h index c4cc3627d3..073048c41d 100644 --- a/src/tagmanager/tm_source_file.h +++ b/src/tagmanager/tm_source_file.h @@ -62,6 +62,10 @@ GPtrArray *tm_source_file_read_tags_file(const gchar *tags_file, TMParserType mo gboolean tm_source_file_write_tags_file(const gchar *tags_file, GPtrArray *tags_array); +gchar tm_source_file_get_tag_impl(const gchar *impl); + +gchar tm_source_file_get_tag_access(const gchar *access); + #endif /* GEANY_PRIVATE */ G_END_DECLS From 7741ef0c12bd6f9ae75ff5f2db1ab44bb327bcd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Thu, 25 Apr 2019 17:47:30 +0200 Subject: [PATCH 32/46] GLibify and Geanify tm_ctags.c/h Use GLib types and Geany naming convention. --- src/tagmanager/tm_ctags.c | 64 ++++++++++++++++----------------- src/tagmanager/tm_ctags.h | 31 +++++++++------- src/tagmanager/tm_parser.c | 14 ++++---- src/tagmanager/tm_source_file.c | 10 +++--- src/tagmanager/tm_workspace.c | 2 +- 5 files changed, 64 insertions(+), 57 deletions(-) diff --git a/src/tagmanager/tm_ctags.c b/src/tagmanager/tm_ctags.c index 95bd51ce43..b8489777f8 100644 --- a/src/tagmanager/tm_ctags.c +++ b/src/tagmanager/tm_ctags.c @@ -4,7 +4,7 @@ * This source code is released for free distribution under the terms of the * GNU General Public License version 2 or (at your option) any later version. * -* Defines ctags API when compiled as a library. +* Encapsulates ctags so it is isolated from the rest of Geany. */ #include "general.h" /* must always come first */ @@ -31,15 +31,15 @@ #define CTAGS_LANG(x) ((x) >= 0 ? (x) + 1 : (x)) #define GEANY_LANG(x) ((x) >= 1 ? (x) - 1 : (x)) -static int writeEntry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag); -static void rescanFailed (tagWriter *writer, unsigned long validTagNum); +static gint write_entry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag); +static void rescan_failed (tagWriter *writer, gulong valid_tag_num); tagWriter geanyWriter = { - .writeEntry = writeEntry, + .writeEntry = write_entry, .writePtagEntry = NULL, /* no pseudo-tags */ .preWriteEntry = NULL, .postWriteEntry = NULL, - .rescanFailedEntry = rescanFailed, + .rescanFailedEntry = rescan_failed, .buildFqTagCache = NULL, .defaultFileName = "geany_tags_file_which_should_never_appear_anywhere", .private = NULL, @@ -49,8 +49,8 @@ tagWriter geanyWriter = { static TMSourceFile *current_source_file = NULL; -static bool nofatalErrorPrinter (const errorSelection selection, - const char *const format, +static bool nonfatal_error_printer (const errorSelection selection, + const gchar *const format, va_list ap, void *data CTAGS_ATTR_UNUSED) { fprintf (stderr, "%s: ", (selection & WARNING) ? "Warning: " : "Error"); @@ -67,16 +67,16 @@ static bool nofatalErrorPrinter (const errorSelection selection, } -static void enableAllLangKinds() +static void enable_all_lang_kinds() { - unsigned int lang; + TMParserType lang; for (lang = 0; lang < countParsers(); lang++) { - unsigned int kindNum = countLanguageKinds(lang); - unsigned int kind; + guint kind_num = countLanguageKinds(lang); + guint kind; - for (kind = 0; kind < kindNum; kind++) + for (kind = 0; kind < kind_num; kind++) { kindDefinition *def = getLanguageKind(lang, kind); enableKind(def, true); @@ -147,7 +147,7 @@ static gboolean init_tag(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag static void update_python_arglist(const TMTag *tag, TMSourceFile *current_source_file) { guint i; - const char *parent_tag_name; + const gchar *parent_tag_name; if (tag->type != tm_tag_method_t || tag->scope == NULL || g_strcmp0(tag->name, "__init__") != 0) @@ -173,7 +173,7 @@ static void update_python_arglist(const TMTag *tag, TMSourceFile *current_source } -static int writeEntry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag) +static gint write_entry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag) { TMTag *tm_tag = tm_tag_new(); @@ -195,7 +195,7 @@ static int writeEntry (tagWriter *writer, MIO * mio, const tagEntryInfo *const t } -static void rescanFailed (tagWriter *writer, unsigned long valid_tag_num) +static void rescan_failed (tagWriter *writer, gulong valid_tag_num) { GPtrArray *tags_array = current_source_file->tags_array; @@ -210,11 +210,11 @@ static void rescanFailed (tagWriter *writer, unsigned long valid_tag_num) /* keep in sync with ctags main() - use only things interesting for us */ -extern void ctagsInit(void) +void tm_ctags_init(void) { initDefaultTrashBox (); - setErrorPrinter (nofatalErrorPrinter, NULL); + setErrorPrinter (nonfatal_error_printer, NULL); geanySetTagWriter (&geanyWriter); checkRegex (); @@ -231,14 +231,14 @@ extern void ctagsInit(void) enableXtag(XTAG_TAGS_GENERATED_BY_GUEST_PARSERS, true); /* some kinds we are interested in are disabled by default */ - enableAllLangKinds(); + enable_all_lang_kinds(); } -extern void ctagsParse(unsigned char *buffer, size_t bufferSize, - const char *fileName, const int language, TMSourceFile *source_file) +void tm_ctags_parse(guchar *buffer, gsize buffer_size, + const gchar *file_name, TMParserType language, TMSourceFile *source_file) { - if (buffer == NULL && fileName == NULL) + if (buffer == NULL && file_name == NULL) { error(FATAL, "Neither buffer nor file provided to ctagsParse()"); return; @@ -246,29 +246,29 @@ extern void ctagsParse(unsigned char *buffer, size_t bufferSize, current_source_file = source_file; - geanyCreateTags(buffer, bufferSize, fileName, CTAGS_LANG(language)); + geanyCreateTags(buffer, buffer_size, file_name, CTAGS_LANG(language)); } -extern const char *ctagsGetLangName(int lang) +const gchar *tm_ctags_get_lang_name(TMParserType lang) { return getLanguageName(CTAGS_LANG(lang)); } -extern int ctagsGetNamedLang(const char *name) +TMParserType tm_ctags_get_named_lang(const gchar *name) { return GEANY_LANG(getNamedLanguage(name, 0)); } -extern const char *ctagsGetLangKinds(int lang) +const gchar *tm_ctags_get_lang_kinds(TMParserType lang) { - unsigned int kindNum = countLanguageKinds(CTAGS_LANG(lang)); - static char kinds[257]; - unsigned int i; + guint kind_num = countLanguageKinds(CTAGS_LANG(lang)); + static gchar kinds[257]; + guint i; - for (i = 0; i < kindNum; i++) + for (i = 0; i < kind_num; i++) kinds[i] = getLanguageKind(CTAGS_LANG(lang), i)->letter; kinds[i] = '\0'; @@ -276,21 +276,21 @@ extern const char *ctagsGetLangKinds(int lang) } -extern const char *ctagsGetKindName(char kind, int lang) +const gchar *tm_ctags_get_kind_name(gchar kind, TMParserType lang) { kindDefinition *def = getLanguageKindForLetter (CTAGS_LANG(lang), kind); return def ? def->name : "unknown"; } -extern char ctagsGetKindFromName(const char *name, int lang) +gchar tm_ctags_get_kind_from_name(const gchar *name, TMParserType lang) { kindDefinition *def = getLanguageKindForName (CTAGS_LANG(lang), name); return def ? def->letter : '-'; } -extern unsigned int ctagsGetLangCount(void) +guint tm_ctags_get_lang_count(void) { return GEANY_LANG(countParsers()); } diff --git a/src/tagmanager/tm_ctags.h b/src/tagmanager/tm_ctags.h index 8256336290..217baf36f0 100644 --- a/src/tagmanager/tm_ctags.h +++ b/src/tagmanager/tm_ctags.h @@ -4,24 +4,31 @@ * This source code is released for free distribution under the terms of the * GNU General Public License version 2 or (at your option) any later version. * -* Defines ctags API when compiled as a library. +* Encapsulates ctags so it is isolated from the rest of Geany. */ #ifndef TM_CTAGS_H #define TM_CTAGS_H -#include -#include +#include #include "tm_source_file.h" -extern void ctagsInit(void); -extern void ctagsParse(unsigned char *buffer, size_t bufferSize, - const char *fileName, const int language, TMSourceFile *source_file); -extern const char *ctagsGetLangName(int lang); -extern int ctagsGetNamedLang(const char *name); -extern const char *ctagsGetLangKinds(int lang); -extern const char *ctagsGetKindName(char kind, int lang); -extern char ctagsGetKindFromName(const char *name, int lang); -extern unsigned int ctagsGetLangCount(void); +G_BEGIN_DECLS + +#ifdef GEANY_PRIVATE + +void tm_ctags_init(void); +void tm_ctags_parse(guchar *buffer, gsize buffer_size, + const gchar *file_name, TMParserType language, TMSourceFile *source_file); +const gchar *tm_ctags_get_lang_name(TMParserType lang); +TMParserType tm_ctags_get_named_lang(const gchar *name); +const gchar *tm_ctags_get_lang_kinds(TMParserType lang); +const gchar *tm_ctags_get_kind_name(gchar kind, TMParserType lang); +gchar tm_ctags_get_kind_from_name(const gchar *name, TMParserType lang); +guint tm_ctags_get_lang_count(void); + +#endif /* GEANY_PRIVATE */ + +G_END_DECLS #endif /* TM_CTAGS_H */ diff --git a/src/tagmanager/tm_parser.c b/src/tagmanager/tm_parser.c index e6b156de11..52468c7417 100644 --- a/src/tagmanager/tm_parser.c +++ b/src/tagmanager/tm_parser.c @@ -669,23 +669,23 @@ void tm_parser_verify_type_mappings(void) { TMParserType lang; - if (TM_PARSER_COUNT > ctagsGetLangCount()) + if (TM_PARSER_COUNT > tm_ctags_get_lang_count()) g_error("More parsers defined in Geany than in ctags"); for (lang = 0; lang < TM_PARSER_COUNT; lang++) { - const gchar *kinds = ctagsGetLangKinds(lang); + const gchar *kinds = tm_ctags_get_lang_kinds(lang); TMParserMap *map = &parser_map[lang]; gchar presence_map[256]; guint i; if (! map->entries || map->size < 1) g_error("No tag types in TM for %s, is the language listed in parser_map?", - ctagsGetLangName(lang)); + tm_ctags_get_lang_name(lang)); if (map->size != strlen(kinds)) g_error("Different number of tag types in TM (%d) and ctags (%d) for %s", - map->size, (int)strlen(kinds), ctagsGetLangName(lang)); + map->size, (int)strlen(kinds), tm_ctags_get_lang_name(lang)); memset(presence_map, 0, sizeof(presence_map)); for (i = 0; i < map->size; i++) @@ -707,10 +707,10 @@ void tm_parser_verify_type_mappings(void) } if (!ctags_found) g_error("Tag type '%c' found in TM but not in ctags for %s", - map->entries[i].kind, ctagsGetLangName(lang)); + map->entries[i].kind, tm_ctags_get_lang_name(lang)); if (!tm_found) g_error("Tag type '%c' found in ctags but not in TM for %s", - kinds[i], ctagsGetLangName(lang)); + kinds[i], tm_ctags_get_lang_name(lang)); presence_map[(unsigned char) map->entries[i].kind]++; } @@ -719,7 +719,7 @@ void tm_parser_verify_type_mappings(void) { if (presence_map[i] > 1) g_error("Duplicate tag type '%c' found for %s", - (gchar)i, ctagsGetLangName(lang)); + (gchar)i, tm_ctags_get_lang_name(lang)); } } } diff --git a/src/tagmanager/tm_source_file.c b/src/tagmanager/tm_source_file.c index f6229f9bc2..9a1f314bd0 100644 --- a/src/tagmanager/tm_source_file.c +++ b/src/tagmanager/tm_source_file.c @@ -375,7 +375,7 @@ static gboolean init_tag_from_file_ctags(TMTag *tag, TMSourceFile *file, FILE *f const gchar *kind = value ? value : key; if (kind[0] && kind[1]) - tag->type = tm_parser_get_tag_type(ctagsGetKindFromName(kind, lang), lang); + tag->type = tm_parser_get_tag_type(tm_ctags_get_kind_from_name(kind, lang), lang); else tag->type = tm_parser_get_tag_type(*kind, lang); } @@ -598,7 +598,7 @@ static gboolean tm_source_file_init(TMSourceFile *source_file, const char *file_ if (name == NULL) source_file->lang = TM_PARSER_NONE; else - source_file->lang = ctagsGetNamedLang(name); + source_file->lang = tm_ctags_get_named_lang(name); return TRUE; } @@ -715,7 +715,7 @@ gboolean tm_source_file_parse(TMSourceFile *source_file, guchar* text_buf, gsize tm_tags_array_free(source_file->tags_array, FALSE); - ctagsParse(use_buffer ? text_buf : NULL, buf_size, file_name, + tm_ctags_parse(use_buffer ? text_buf : NULL, buf_size, file_name, source_file->lang, source_file); return !retry; @@ -727,7 +727,7 @@ gboolean tm_source_file_parse(TMSourceFile *source_file, guchar* text_buf, gsize */ const gchar *tm_source_file_get_lang_name(TMParserType lang) { - return ctagsGetLangName(lang); + return tm_ctags_get_lang_name(lang); } /* Gets the language index for \a name. @@ -736,5 +736,5 @@ const gchar *tm_source_file_get_lang_name(TMParserType lang) */ TMParserType tm_source_file_get_named_lang(const gchar *name) { - return ctagsGetNamedLang(name); + return tm_ctags_get_named_lang(name); } diff --git a/src/tagmanager/tm_workspace.c b/src/tagmanager/tm_workspace.c index 97090999ae..eb2a537509 100644 --- a/src/tagmanager/tm_workspace.c +++ b/src/tagmanager/tm_workspace.c @@ -79,7 +79,7 @@ static gboolean tm_create_workspace(void) theWorkspace->typename_array = g_ptr_array_new(); theWorkspace->global_typename_array = g_ptr_array_new(); - ctagsInit(); + tm_ctags_init(); tm_parser_verify_type_mappings(); return TRUE; From 97cf266debfc7860212770b8116275d1592ca7e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Fri, 26 Apr 2019 18:49:45 +0200 Subject: [PATCH 33/46] Remove pcoproc.c/h which isn't in the upstream version any more --- ctags/Makefile.am | 2 - ctags/main/pcoproc.c | 296 ------------------------------------------- ctags/main/pcoproc.h | 29 ----- 3 files changed, 327 deletions(-) delete mode 100644 ctags/main/pcoproc.c delete mode 100644 ctags/main/pcoproc.h diff --git a/ctags/Makefile.am b/ctags/Makefile.am index f9940ee7da..0e60b0ff0c 100644 --- a/ctags/Makefile.am +++ b/ctags/Makefile.am @@ -114,8 +114,6 @@ libctags_la_SOURCES = \ main/parse.h \ main/parse_p.h \ main/parsers.h \ - main/pcoproc.c \ - main/pcoproc.h \ main/promise.c \ main/promise.h \ main/promise_p.h \ diff --git a/ctags/main/pcoproc.c b/ctags/main/pcoproc.c deleted file mode 100644 index a18015d491..0000000000 --- a/ctags/main/pcoproc.c +++ /dev/null @@ -1,296 +0,0 @@ -/* -* -* Copyright (c) 2014, Red Hat, Inc. -* Copyright (c) 2014, Masatake YAMATO -* -* This source code is released for free distribution under the terms of the -* GNU General Public License version 2 or (at your option) any later version. -* -* replaces popen/pclose -*/ -#include "general.h" -#include "pcoproc.h" - -#ifdef MAIN -/* - * PATH=/usr/bin gcc -O0 -g -Wall pcoproc.c htable.c -DMAIN - */ -#include -#ifndef xCalloc -#define xCalloc(n,Type) (Type *)calloc((size_t)(n), sizeof (Type)) -#endif -#ifndef xMalloc -#define xMalloc(n,Type) (Type *)malloc((size_t)(n) * sizeof (Type)) -#endif -#ifndef eFree -#define eFree(x) free(x) -#endif -#else -#include "routines.h" -#endif /* MAIN */ - -#if defined (HAVE_COPROC) || defined (MAIN) -#include -#include -#include -#include -#include - -#include "htable.h" - - -#define W_PARENT_EPT 1 -#define W_CHILD_EPT 0 -#define R_PARENT_EPT 0 -#define R_CHILD_EPT 1 - -static hashTable *pid_table; - -struct pid_wrapper -{ - int ref_count; - pid_t pid; -}; - -static void pid_wrapper_free (void *ptr) -{ - struct pid_wrapper *pid = ptr; - - pid->ref_count--; - if (pid->ref_count == 0) - eFree (ptr); -} - -static void pidtable_put (FILE* fp, struct pid_wrapper* pid) -{ - if (pid_table == NULL) - pid_table = hashTableNew (7, - hashPtrhash, - hashPtreq, - (void (*)(void *))fclose, - pid_wrapper_free); - - hashTablePutItem (pid_table, fp, pid); -} - -static enum pcoprocError -coproc (const char *filename, char *const argv[], pid_t *pid, int *for_reading_from_coproc, int *for_writing_to_coproc) -{ - int wpipe[2]; - int rpipe[2]; - - if (for_writing_to_coproc) - { - if (pipe(wpipe) < 0) - return PCOPROC_ERROR_WPIPE; - } - if (for_reading_from_coproc) - { - if (pipe(rpipe) < 0) - { - if (for_writing_to_coproc) - { - close (wpipe[0]); - close (wpipe[1]); - } - return PCOPROC_ERROR_RPIPE; - } - } - - - *pid = fork (); - if (*pid < 0) - { - if (for_writing_to_coproc) - { - close (wpipe[0]); - close (wpipe[1]); - } - if (for_reading_from_coproc) - { - close (rpipe[0]); - close (rpipe[1]); - } - return PCOPROC_ERROR_FORK; - } - else if (*pid == 0) - { /* child */ - - int i; - if (for_writing_to_coproc) - { - close (wpipe[W_PARENT_EPT]); - if (wpipe[W_CHILD_EPT] != STDIN_FILENO) - { - dup2 (wpipe[W_CHILD_EPT], STDIN_FILENO); - close (wpipe[W_CHILD_EPT]); - } - } - if (for_reading_from_coproc) - { - close (rpipe[R_PARENT_EPT]); - if (rpipe[R_CHILD_EPT] != STDOUT_FILENO) - { - dup2 (rpipe[R_CHILD_EPT], STDOUT_FILENO); - close (rpipe[R_CHILD_EPT]); - } - } - - for (i = 0; i < 1024; i++) - if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) - close (i); - - execv (filename, argv); - _exit(127); - return PCOPROC_ERROR_FORK; /* meaningless */ - } - else - { /* parent */ - if (for_writing_to_coproc) - { - close (wpipe[W_CHILD_EPT]); - *for_writing_to_coproc = wpipe[W_PARENT_EPT]; - } - if (for_reading_from_coproc) - { - close (rpipe[R_CHILD_EPT]); - *for_reading_from_coproc = rpipe[R_PARENT_EPT]; - } - - return 0; - } - -} - -extern enum pcoprocError pcoprocOpen (const char *filename, char *const argv[], - FILE** readfp, FILE** writefp) -{ - struct pid_wrapper *value; - - pid_t pid; - int for_reading_from_coproc, for_writing_from_coproc; - enum pcoprocError r; - - r = coproc (filename, argv, &pid, - readfp? &for_reading_from_coproc: NULL, - writefp? &for_writing_from_coproc: NULL); - - if (r != PCOPROC_SUCCESSFUL) - return r; - - value = xMalloc (1, struct pid_wrapper); - value->pid = pid; - value->ref_count = 0; - if (writefp) - { - *writefp = fdopen (for_writing_from_coproc, "w"); - if (*writefp) - { - value->ref_count++; - pidtable_put (*writefp, value); - } - else - perror("fdopen(w)"); - - } - if (readfp) - { - *readfp = fdopen (for_reading_from_coproc, "r"); - if (*readfp) - { - value->ref_count++; - pidtable_put (*readfp, value); - } - else - perror ("fdopen(r)"); - } - - if (value->ref_count == 0) - { - eFree (value); - /* TODO: all perror should be replaced with error */ - } - return r; -} - -extern int pcoprocClose (FILE* fp) -{ - struct pid_wrapper* pid_wrapper; - pid_t pid; - int stat; - int ref_count; - - if (!pid_table) - return -1; - - pid_wrapper = hashTableGetItem (pid_table, fp); - if (pid_wrapper) - { - pid = pid_wrapper->pid; - ref_count = pid_wrapper->ref_count; - - hashTableDeleteItem (pid_table, fp); - if (ref_count == 1) - { - while (waitpid(pid, &stat, 0) < 0) - { - if (errno != EINTR) - return -1; - } - return stat; - } - } - return -2; -} -#else -extern enum pcoprocError pcoprocOpen (const char *filename, char *const argv[], - FILE** readfp, FILE** writefp) -{ - *readfp = NULL; - *writefp = NULL; - return PCOPROC_SUCCESSFUL; -} -extern int pcoprocClose (FILE* fp) -{ - return 1; -} -#endif - - -#ifdef MAIN -int -main (int argc, char** argv) -{ - FILE *in, *out; - char* const a[] = { - "/bin/tr", - "a-z", - "A-Z", - NULL - }; - enum pcoprocError r; - r = pcoprocOpen ("/bin/tr", a, &in, &out); - switch (r) { - case PCOPROC_ERROR_WPIPE: - perror ("wpipe"); - return 1; - case PCOPROC_ERROR_RPIPE: - perror("rpipe"); - return 1; - case PCOPROC_ERROR_FORK: - perror("fork"); - return 1; - case PCOPROC_SUCCESSFUL: - break; - } - fprintf(out, "abc\n"); - fflush (out); - pcoprocClose (out); - - int c; - while ((c = fgetc(in)) != EOF) - putchar (c); - - return pcoprocClose (in); -} -#endif diff --git a/ctags/main/pcoproc.h b/ctags/main/pcoproc.h deleted file mode 100644 index ab1ece1322..0000000000 --- a/ctags/main/pcoproc.h +++ /dev/null @@ -1,29 +0,0 @@ -/* -* -* Copyright (c) 2014, Red Hat, Inc. -* Copyright (c) 2014, Masatake YAMATO -* -* This source code is released for free distribution under the terms of the -* GNU General Public License version 2 or (at your option) any later version. -* -* replaces popen/pclose -*/ - -#ifndef PCOPROC_H -#define PCOPROC_H - -#include - -enum pcoprocError { - PCOPROC_SUCCESSFUL = 0, - PCOPROC_ERROR_WPIPE, - PCOPROC_ERROR_RPIPE, - PCOPROC_ERROR_FORK, - /* PCOPROC_ERROR_EXECVE = -4, */ -}; - -extern enum pcoprocError pcoprocOpen (const char *filename, char *const argv[], - FILE** readfp, FILE** writefp); -extern int pcoprocClose (FILE* fp); - -#endif /* PCOPROC_H */ From 77f83326cd144ed0f615e04522113a11d27e0fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Sat, 27 Apr 2019 20:35:37 +0200 Subject: [PATCH 34/46] Enable XTAG_REFERENCE_TAGS --- src/tagmanager/tm_ctags.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tagmanager/tm_ctags.c b/src/tagmanager/tm_ctags.c index b8489777f8..0f64fbbbef 100644 --- a/src/tagmanager/tm_ctags.c +++ b/src/tagmanager/tm_ctags.c @@ -227,8 +227,9 @@ void tm_ctags_init(void) /* make sure all parsers are initialized */ initializeParser (LANG_AUTO); - /* change default value which is false */ + /* change default values which are false */ enableXtag(XTAG_TAGS_GENERATED_BY_GUEST_PARSERS, true); + enableXtag(XTAG_REFERENCE_TAGS, true); /* some kinds we are interested in are disabled by default */ enable_all_lang_kinds(); From cbb6f78c63faa7dde53b10139074a8eb7ccbe067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Sat, 27 Apr 2019 21:21:50 +0200 Subject: [PATCH 35/46] Use g_logv() for logging ctags errors --- src/tagmanager/tm_ctags.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/tagmanager/tm_ctags.c b/src/tagmanager/tm_ctags.c index 0f64fbbbef..310d45d699 100644 --- a/src/tagmanager/tm_ctags.c +++ b/src/tagmanager/tm_ctags.c @@ -53,15 +53,7 @@ static bool nonfatal_error_printer (const errorSelection selection, const gchar *const format, va_list ap, void *data CTAGS_ATTR_UNUSED) { - fprintf (stderr, "%s: ", (selection & WARNING) ? "Warning: " : "Error"); - vfprintf (stderr, format, ap); - if (selection & PERROR) -#ifdef HAVE_STRERROR - fprintf (stderr, " : %s", strerror (errno)); -#else - perror (" "); -#endif - fputs ("\n", stderr); + g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, format, ap); return false; } From a63f7ac9435186a6f38422d74ab4b3554b751023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Sun, 19 May 2019 13:30:17 -0700 Subject: [PATCH 36/46] Sync against ctags version which supports most of Geany functionality The sync is against ctags version ad3548aab5e287d2a72cb5cf35c21d88c393f36b where the Geany-related changes were introduced. There are just minor changes on the Geany side inside tm_ctags.c, mostly related to different names of functions inside ctags and also elimination of the static current_source_file variable thanks to the user_data inside writer callbacks. --- configure.ac | 2 +- ctags/Makefile.am | 25 +- ctags/main/cmd.c | 22 + ctags/main/colprint.c | 6 +- ctags/main/debug.c | 1 + ctags/main/dependency.c | 1 + ctags/main/dependency.h | 34 +- ctags/main/dependency_p.h | 58 ++ ctags/main/entry.c | 88 ++- ctags/main/entry.h | 12 +- ctags/main/entry_p.h | 11 +- ctags/main/error.c | 2 +- ctags/main/{error.h => error_p.h} | 0 ctags/main/field.c | 396 ++++++----- ctags/main/field.h | 75 +- ctags/main/field_p.h | 78 +++ ctags/main/fmt.c | 10 +- ctags/main/general.h | 37 +- ctags/main/htable.c | 8 + ctags/main/htable.h | 9 + ctags/main/kind.c | 194 +++++- ctags/main/kind.h | 57 +- ctags/main/kind_p.h | 79 +++ ctags/main/lregex.c | 57 +- ctags/main/lregex_p.h | 1 + ctags/main/lxpath.c | 55 +- ctags/main/lxpath.h | 30 +- ctags/main/lxpath_p.h | 28 + ctags/main/main.c | 133 +--- ctags/main/main.h | 26 - ctags/main/main_p.h | 22 + ctags/main/mbcs.c | 113 +++ ctags/main/mbcs.h | 14 +- ctags/main/mbcs_p.h | 32 + ctags/main/mini-geany.c | 346 ++++++++++ ctags/main/mio.c | 48 +- ctags/main/mio.h | 2 +- ctags/main/nestlevel.c | 1 - ctags/main/nestlevel.h | 1 + ctags/main/numarray.c | 12 +- ctags/main/numarray.h | 12 +- ctags/main/options.c | 74 +- ctags/main/options.h | 1 - ctags/main/options_p.h | 9 +- ctags/main/param.c | 1 + ctags/main/param.h | 20 +- ctags/main/param_p.h | 36 + ctags/main/parse.c | 418 ++++++++++-- ctags/main/parse.h | 11 +- ctags/main/parse_p.h | 14 +- ctags/main/{parsers.h => parsers_p.h} | 7 +- ctags/main/portable-dirent_p.h | 947 ++++++++++++++++++++++++++ ctags/main/portable-scandir.c | 235 +++++++ ctags/main/promise.c | 3 +- ctags/main/ptrarray.h | 3 +- ctags/main/read.c | 39 +- ctags/main/read.h | 50 +- ctags/main/read_p.h | 78 +++ ctags/main/repoinfo.h | 2 +- ctags/main/routines.c | 10 +- ctags/main/routines.h | 1 + ctags/main/routines_p.h | 6 +- ctags/main/seccomp.c | 80 +++ ctags/main/selectors.c | 42 +- ctags/main/selectors.h | 2 - ctags/main/sort.c | 6 +- ctags/main/{sort.h => sort_p.h} | 0 ctags/main/stats.c | 89 +++ ctags/main/stats_p.h | 28 + ctags/main/strlist.c | 11 +- ctags/main/subparser.h | 37 +- ctags/main/subparser_p.h | 48 ++ ctags/main/tokeninfo.c | 205 ++++++ ctags/main/tokeninfo.h | 96 +++ ctags/main/trace.c | 119 ++++ ctags/main/trashbox.h | 37 +- ctags/main/trashbox_p.h | 30 + ctags/main/types.h | 6 - ctags/main/unwindi.c | 292 ++++++++ ctags/main/unwindi.h | 71 ++ ctags/main/vstring.c | 14 + ctags/main/vstring.h | 16 + ctags/main/writer-ctags.c | 178 +++-- ctags/main/writer-etags.c | 24 +- ctags/main/writer-json.c | 79 ++- ctags/main/writer-xref.c | 20 +- ctags/main/writer.c | 49 +- ctags/main/writer_p.h | 36 +- ctags/main/xtag.c | 3 +- ctags/main/xtag.h | 30 +- ctags/main/xtag_p.h | 51 ++ ctags/parsers/go.c | 1 - ctags/parsers/haxe.c | 1 - ctags/parsers/nsis.c | 1 - ctags/parsers/powershell.c | 1 - ctags/parsers/python.c | 1 - ctags/parsers/rust.c | 1 - src/tagmanager/tm_ctags.c | 44 +- src/tagmanager/tm_workspace.c | 2 - 99 files changed, 4795 insertions(+), 1059 deletions(-) create mode 100644 ctags/main/cmd.c create mode 100644 ctags/main/dependency_p.h rename ctags/main/{error.h => error_p.h} (100%) create mode 100644 ctags/main/field_p.h create mode 100644 ctags/main/kind_p.h create mode 100644 ctags/main/lxpath_p.h delete mode 100644 ctags/main/main.h create mode 100644 ctags/main/main_p.h create mode 100644 ctags/main/mbcs.c create mode 100644 ctags/main/mbcs_p.h create mode 100644 ctags/main/mini-geany.c create mode 100644 ctags/main/param_p.h rename ctags/main/{parsers.h => parsers_p.h} (98%) create mode 100644 ctags/main/portable-dirent_p.h create mode 100644 ctags/main/portable-scandir.c create mode 100644 ctags/main/read_p.h create mode 100644 ctags/main/seccomp.c rename ctags/main/{sort.h => sort_p.h} (100%) create mode 100644 ctags/main/stats.c create mode 100644 ctags/main/stats_p.h create mode 100644 ctags/main/subparser_p.h create mode 100644 ctags/main/tokeninfo.c create mode 100644 ctags/main/tokeninfo.h create mode 100644 ctags/main/trace.c create mode 100644 ctags/main/trashbox_p.h create mode 100644 ctags/main/unwindi.c create mode 100644 ctags/main/unwindi.h create mode 100644 ctags/main/xtag_p.h diff --git a/configure.ac b/configure.ac index 3278457665..238ea395de 100644 --- a/configure.ac +++ b/configure.ac @@ -43,7 +43,7 @@ AC_CHECK_HEADERS([fcntl.h glob.h stdlib.h sys/time.h errno.h limits.h]) # Checks for dependencies needed by ctags AC_CHECK_HEADERS([fnmatch.h direct.h io.h sys/dir.h]) -AC_DEFINE([USE_STDBOOL_H], [1], [whether or not to use .]) +AC_DEFINE([HAVE_STDBOOL_H], [1], [whether or not to use .]) AC_DEFINE([GEANY_CTAGS_LIB], [1], [compile ctags as a library.]) # Checks for typedefs, structures, and compiler characteristics. diff --git a/ctags/Makefile.am b/ctags/Makefile.am index 0e60b0ff0c..b9036d5e65 100644 --- a/ctags/Makefile.am +++ b/ctags/Makefile.am @@ -61,6 +61,7 @@ libctags_la_SOURCES = \ main/debug.h \ main/debug.c \ main/dependency.h \ + main/dependency_p.h \ main/dependency.c \ main/e_msoft.h \ main/entry.c \ @@ -68,9 +69,10 @@ libctags_la_SOURCES = \ main/entry.h \ main/entry_p.h \ main/error.c \ - main/error.h \ + main/error_p.h \ main/field.c \ main/field.h \ + main/field_p.h \ main/flags.c \ main/flags_p.h \ main/fmt.c \ @@ -87,6 +89,7 @@ libctags_la_SOURCES = \ main/keyword_p.h \ main/kind.c \ main/kind.h \ + main/kind_p.h \ main/lcpp.c \ main/lcpp.h \ main/lregex.c \ @@ -94,9 +97,12 @@ libctags_la_SOURCES = \ main/lregex_p.h \ main/lxpath.c \ main/lxpath.h \ + main/lxpath_p.h \ main/main.c \ - main/main.h \ + main/main_p.h \ + main/mbcs.c \ main/mbcs.h \ + main/mbcs_p.h \ main/mio.c \ main/mio.h \ main/nestlevel.c \ @@ -110,6 +116,7 @@ libctags_la_SOURCES = \ main/options_p.h \ main/param.c \ main/param.h \ + main/param_p.h \ main/parse.c \ main/parse.h \ main/parse_p.h \ @@ -131,14 +138,23 @@ libctags_la_SOURCES = \ main/selectors.c \ main/selectors.h \ main/sort.c \ - main/sort.h \ + main/sort_p.h \ + main/stats.c \ + main/stats_p.h \ main/strlist.c \ main/strlist.h \ main/subparser.h \ + main/subparser_p.h \ + main/tokeninfo.c \ + main/tokeninfo.h \ + main/trace.c \ main/trace.h \ main/trashbox.c \ main/trashbox.h \ + main/trashbox_p.h \ main/types.h \ + main/unwindi.c \ + main/unwindi.h \ main/vstring.c \ main/vstring.h \ main/writer.c \ @@ -147,6 +163,7 @@ libctags_la_SOURCES = \ main/writer-etags.c \ main/writer-json.c \ main/writer-xref.c \ - main/xtag.h \ main/xtag.c \ + main/xtag.h \ + main/xtag_p.h \ $(parsers) diff --git a/ctags/main/cmd.c b/ctags/main/cmd.c new file mode 100644 index 0000000000..32699dc581 --- /dev/null +++ b/ctags/main/cmd.c @@ -0,0 +1,22 @@ +/* +* Copyright (c) 1998-2002, Darren Hiebert +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +*/ + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ + +#include "main_p.h" + + +/* +* FUNCTION DEFINITIONS +*/ +int main(int argc, char **argv) +{ + return ctags_cli_main (argc, argv); +} diff --git a/ctags/main/colprint.c b/ctags/main/colprint.c index aacdf81464..3af6448478 100644 --- a/ctags/main/colprint.c +++ b/ctags/main/colprint.c @@ -160,7 +160,7 @@ static void colprintHeaderColumnPrint (struct colprintHeaderColumn *headerCol, b colprintColumnPrintGeneric (headerCol->value, headerCol, machinable, fp); } -static void colprintHeaderPrint (ptrArray *header, int startFrom, bool withHeader, bool machinable, FILE *fp) +static void colprintHeaderPrint (ptrArray *header, unsigned int startFrom, bool withHeader, bool machinable, FILE *fp) { unsigned int i; @@ -175,7 +175,7 @@ static void colprintHeaderPrint (ptrArray *header, int startFrom, bool withHeade fputc('\n', fp); } -static void colprintLinePrint (stringList *line, int startFrom, ptrArray *header, bool machinable, FILE *fp) +static void colprintLinePrint (stringList *line, unsigned int startFrom, ptrArray *header, bool machinable, FILE *fp) { unsigned int i; @@ -186,7 +186,7 @@ static void colprintLinePrint (stringList *line, int startFrom, ptrArray *heade colprintColumnPrintGeneric(value, spec, machinable, fp); } } -static void colprintLinesPrint (ptrArray *lines, int startFrom, ptrArray *header, bool machinable, FILE *fp) +static void colprintLinesPrint (ptrArray *lines, unsigned int startFrom, ptrArray *header, bool machinable, FILE *fp) { unsigned int i; diff --git a/ctags/main/debug.c b/ctags/main/debug.c index 95e94bb938..d68aaeece6 100644 --- a/ctags/main/debug.c +++ b/ctags/main/debug.c @@ -23,6 +23,7 @@ #include "options.h" #include "parse_p.h" #include "read.h" +#include "read_p.h" /* * FUNCTION DEFINITIONS diff --git a/ctags/main/dependency.c b/ctags/main/dependency.c index 037192e0f0..8a4c549c2b 100644 --- a/ctags/main/dependency.c +++ b/ctags/main/dependency.c @@ -19,6 +19,7 @@ #include "read.h" #include "routines.h" #include "subparser.h" +#include "subparser_p.h" #include "xtag.h" #include diff --git a/ctags/main/dependency.h b/ctags/main/dependency.h index e873040a78..258aee4a1b 100644 --- a/ctags/main/dependency.h +++ b/ctags/main/dependency.h @@ -12,11 +12,17 @@ #ifndef CTAGS_MAIN_DEPENDENCY_H #define CTAGS_MAIN_DEPENDENCY_H -#include "general.h" +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ #include "types.h" -#include "kind.h" + +/* +* DATA DECLARATIONS +*/ typedef enum eDepType { DEPTYPE_KIND_OWNER, DEPTYPE_SUBPARSER, @@ -36,28 +42,4 @@ struct sSlaveParser { slaveParser *next; }; -struct slaveControlBlock; /* Opaque data type for parse.c */ - - -extern void linkDependencyAtInitializeParsing (depType dtype, - parserDefinition *const master, - struct slaveControlBlock *masterSCB, - struct kindControlBlock *masterKCB, - parserDefinition *const slave, - struct kindControlBlock *slaveKCB, - void *data); - -extern struct slaveControlBlock *allocSlaveControlBlock (parserDefinition *parser); -extern void freeSlaveControlBlock (struct slaveControlBlock *cb); -extern void initializeDependencies (parserDefinition *parser, - struct slaveControlBlock *cb); -extern void finalizeDependencies (parserDefinition *parser, - struct slaveControlBlock *cb); - -extern slaveParser *getFirstSlaveParser(struct slaveControlBlock *controlBlock); -extern slaveParser *getNextSlaveParser(slaveParser *last); -#define foreachSlaveParser(VAR) \ - VAR = NULL; \ - while ((VAR = getNextSlaveParser (VAR)) != NULL) - #endif /* CTAGS_MAIN_DEPENDENCY_H */ diff --git a/ctags/main/dependency_p.h b/ctags/main/dependency_p.h new file mode 100644 index 0000000000..d59e9b3d61 --- /dev/null +++ b/ctags/main/dependency_p.h @@ -0,0 +1,58 @@ +/* + * + * Copyright (c) 2016, Red Hat, Inc. + * Copyright (c) 2016, Masatake YAMATO + * + * Author: Masatake YAMATO + * + * This source code is released for free distribution under the terms of the + * GNU General Public License version 2 or (at your option) any later version. + * + */ +#ifndef CTAGS_MAIN_DEPENDENCY_PRIVATE_H +#define CTAGS_MAIN_DEPENDENCY_PRIVATE_H + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ + +#include "dependency.h" +#include "kind.h" +#include "types.h" + +/* +* MACROS +*/ +#define foreachSlaveParser(VAR) \ + VAR = NULL; \ + while ((VAR = getNextSlaveParser (VAR)) != NULL) + + +/* +* DATA DECLARATIONS +*/ +struct slaveControlBlock; /* Opaque data type for parse.c */ + +/* +* FUNCTION PROTOTYPES +*/ +extern void linkDependencyAtInitializeParsing (depType dtype, + parserDefinition *const master, + struct slaveControlBlock *masterSCB, + struct kindControlBlock *masterKCB, + parserDefinition *const slave, + struct kindControlBlock *slaveKCB, + void *data); + +extern struct slaveControlBlock *allocSlaveControlBlock (parserDefinition *parser); +extern void freeSlaveControlBlock (struct slaveControlBlock *cb); +extern void initializeDependencies (parserDefinition *parser, + struct slaveControlBlock *cb); +extern void finalizeDependencies (parserDefinition *parser, + struct slaveControlBlock *cb); + +extern slaveParser *getFirstSlaveParser(struct slaveControlBlock *controlBlock); +extern slaveParser *getNextSlaveParser(slaveParser *last); + +#endif /* CTAGS_MAIN_DEPENDENCY_PRIVATE_H */ diff --git a/ctags/main/entry.c b/ctags/main/entry.c index 44f471bca3..bf3af1b5db 100644 --- a/ctags/main/entry.c +++ b/ctags/main/entry.c @@ -43,21 +43,21 @@ #include "field.h" #include "fmt_p.h" #include "kind.h" -#include "main.h" #include "nestlevel.h" #include "options_p.h" #include "ptag_p.h" #include "read.h" +#include "read_p.h" #include "routines.h" #include "routines_p.h" #include "parse_p.h" #include "ptrarray.h" -#include "sort.h" +#include "sort_p.h" #include "strlist.h" -#include "subparser.h" +#include "subparser_p.h" #include "trashbox.h" #include "writer_p.h" -#include "xtag.h" +#include "xtag_p.h" /* * MACROS @@ -155,11 +155,8 @@ extern const char *tagFileName (void) extern void abort_if_ferror(MIO *const mio) { -#ifndef GEANY_CTAGS_LIB -/* TagFile.mio is NULL so this would terminate Geany */ - if (mio_error (mio)) + if (mio != NULL && mio_error (mio)) error (FATAL | PERROR, "cannot write tag file"); -#endif /* GEANY_CTAGS_LIB */ } static void rememberMaxLengths (const size_t nameLength, const size_t lineLength) @@ -372,7 +369,7 @@ static bool isTagFile (const char *const filename) ok = true; else ok = (bool) (isCtagsLine (line) || isEtagsLine (line)); - mio_free (mio); + mio_unref (mio); } return ok; } @@ -432,7 +429,7 @@ extern void openTagFile (void) if (TagFile.mio != NULL) { TagFile.numTags.prev = updatePseudoTags (TagFile.mio); - mio_free (TagFile.mio); + mio_unref (TagFile.mio); TagFile.mio = mio_new_file (TagFile.name, "a+"); } } @@ -490,9 +487,9 @@ static void copyFile (const char *const from, const char *const to, const long s else { copyBytes (fromMio, toMio, size); - mio_free (toMio); + mio_unref (toMio); } - mio_free (fromMio); + mio_unref (fromMio); } } @@ -503,7 +500,7 @@ static int replacementTruncate (const char *const name, const long size) #define WHOLE_FILE -1L char *tempName = NULL; MIO *mio = tempFile ("w", &tempName); - mio_free (mio); + mio_unref (mio); copyFile (name, tempName, size); copyFile (tempName, name, WHOLE_FILE); remove (tempName); @@ -538,7 +535,7 @@ static void internalSortTagFile (void) TagFile.numTags.added + TagFile.numTags.prev); if (! TagsToStdout) - mio_free (mio); + mio_unref (mio); } #endif @@ -614,7 +611,7 @@ extern void closeTagFile (const bool resize) if ((TagsToStdout && (Option.sorted == SO_UNSORTED))) { - if (mio_free (TagFile.mio) != 0) + if (mio_unref (TagFile.mio) != 0) error (FATAL | PERROR, "cannot close tag file"); goto out; } @@ -625,7 +622,7 @@ extern void closeTagFile (const bool resize) size = mio_tell (TagFile.mio); if (! TagsToStdout) /* The tag file should be closed before resizing. */ - if (mio_free (TagFile.mio) != 0) + if (mio_unref (TagFile.mio) != 0) error (FATAL | PERROR, "cannot close tag file"); if (resize && desiredSize < size) @@ -638,7 +635,7 @@ extern void closeTagFile (const bool resize) sortTagFile (); if (TagsToStdout) { - if (mio_free (TagFile.mio) != 0) + if (mio_unref (TagFile.mio) != 0) error (FATAL | PERROR, "cannot close tag file"); remove (tagFileName ()); /* remove temporary file */ } @@ -708,12 +705,12 @@ static int vstring_putc (char c, void *data) static int vstring_puts (const char* s, void *data) { vString *str = data; - int len = vStringLength (str); + size_t len = vStringLength (str); vStringCatS (str, s); - return vStringLength (str) - len; + return (int) (vStringLength (str) - len); } -#if DEBUG +#ifdef DEBUG static bool isPosSet(MIOPos pos) { char * p = (char *)&pos; @@ -756,6 +753,7 @@ static char* getFullQualifiedScopeNameFromCorkQueue (const tagEntryInfo * inner_ int kindIndex = KIND_GHOST_INDEX; langType lang; const tagEntryInfo *scope = inner_scope; + const tagEntryInfo *root_scope = NULL; stringList *queue = stringListNew (); vString *v; vString *n; @@ -778,10 +776,15 @@ static char* getFullQualifiedScopeNameFromCorkQueue (const tagEntryInfo * inner_ kindIndex = scope->kindIndex; lang = scope->langType; } + root_scope = scope; scope = getEntryInCorkQueue (scope->extensionFields.scopeIndex); } n = vStringNew (); + sep = scopeSeparatorFor (root_scope->langType, root_scope->kindIndex, KIND_GHOST_INDEX); + if (sep) + vStringCatS(n, sep); + while ((c = stringListCount (queue)) > 0) { v = stringListLast (queue); @@ -973,7 +976,11 @@ extern void attachParserFieldToCorkEntry (int index, Assert (tag != NULL); v = eStrdup (value); + + bool dynfields_allocated = tag->parserFieldsDynamic? true: false; attachParserFieldGeneric (tag, ftype, v, true); + if (!dynfields_allocated && tag->parserFieldsDynamic) + PARSER_TRASH_BOX_TAKE_BACK(tag->parserFieldsDynamic); } extern const tagField* getParserField (const tagEntryInfo * tag, int index) @@ -1151,9 +1158,9 @@ static unsigned int queueTagEntry(const tagEntryInfo *const tag) } -extern void setupWriter (void) +extern void setupWriter (void *writerClientData) { - writerSetup (TagFile.mio); + writerSetup (TagFile.mio, writerClientData); } extern bool teardownWriter (const char *filename) @@ -1213,6 +1220,11 @@ static bool isTagWritable(const tagEntryInfo *const tag) return true; } +static void buildFqTagCache (tagEntryInfo *const tag) +{ + getTagScopeInformation (tag, NULL, NULL); +} + static void writeTagEntry (const tagEntryInfo *const tag, bool checkingNeeded) { int length = 0; @@ -1226,17 +1238,22 @@ static void writeTagEntry (const tagEntryInfo *const tag, bool checkingNeeded) if (includeExtensionFlags () && isXtagEnabled (XTAG_QUALIFIED_TAGS) - && doesInputLanguageRequestAutomaticFQTag ()) + && doesInputLanguageRequestAutomaticFQTag () + && !isTagExtraBitMarked (tag, XTAG_QUALIFIED_TAGS) + && !tag->skipAutoFQEmission) { /* const is discarded to update the cache field of TAG. */ - writerBuildFqTagCache ( (tagEntryInfo *const)tag); + buildFqTagCache ( (tagEntryInfo *const)tag); } length = writerWriteTag (TagFile.mio, tag); - ++TagFile.numTags.added; - rememberMaxLengths (strlen (tag->name), (size_t) length); - DebugStatement ( mio_flush (TagFile.mio); ) + if (length > 0) + { + ++TagFile.numTags.added; + rememberMaxLengths (strlen (tag->name), (size_t) length); + } + DebugStatement ( if (TagFile.mio) mio_flush (TagFile.mio); ) abort_if_ferror (TagFile.mio); } @@ -1286,11 +1303,17 @@ extern void uncorkTagFile(void) { tagEntryInfo *tag = TagFile.corkQueue.queue + i; writeTagEntry (tag, true); + if (doesInputLanguageRequestAutomaticFQTag () && isXtagEnabled (XTAG_QUALIFIED_TAGS) - && (tag->extensionFields.scopeKindIndex != KIND_GHOST_INDEX) - && tag->extensionFields.scopeName - && tag->extensionFields.scopeIndex) + && !isTagExtraBitMarked (tag, XTAG_QUALIFIED_TAGS) + && !tag->skipAutoFQEmission + && ((tag->extensionFields.scopeKindIndex != KIND_GHOST_INDEX + && tag->extensionFields.scopeName != NULL + && tag->extensionFields.scopeIndex != CORK_NIL) + || (tag->extensionFields.scopeKindIndex == KIND_GHOST_INDEX + && tag->extensionFields.scopeName == NULL + && tag->extensionFields.scopeIndex == CORK_NIL))) makeQualifiedTagEntry (tag); } for (i = 1; i < TagFile.corkQueue.count; i++) @@ -1350,6 +1373,7 @@ extern int makeTagEntry (const tagEntryInfo *const tag) { int r = CORK_NIL; Assert (tag->name != NULL); + Assert(tag->lineNumber > 0); if (!TagFile.cork) if (!isTagWritable (tag)) @@ -1663,17 +1687,13 @@ extern void invalidatePatternCache(void) extern void tagFilePosition (MIOPos *p) { -#ifdef GEANY_CTAGS_LIB /* TODO: do the same upstream */ if (TagFile.mio) -#endif /* GEANY_CTAGS_LIB */ mio_getpos (TagFile.mio, p); } extern void setTagFilePosition (MIOPos *p) { -#ifdef GEANY_CTAGS_LIB /* TODO: do the same upstream */ if (TagFile.mio) -#endif /* GEANY_CTAGS_LIB */ mio_setpos (TagFile.mio, p); } diff --git a/ctags/main/entry.h b/ctags/main/entry.h index c033bf3fba..2d925afc85 100644 --- a/ctags/main/entry.h +++ b/ctags/main/entry.h @@ -20,6 +20,8 @@ #include "field.h" #include "xtag.h" #include "mio.h" +#include "ptrarray.h" +#include "nestlevel.h" /* * MACROS @@ -46,6 +48,8 @@ struct sTagEntryInfo { unsigned int placeholder :1; /* This is just a part of scope context. Put this entry to cork queue but don't print it to tags file. */ + unsigned int skipAutoFQEmission:1; /* If a parser makes a fq tag for the + current tag by itself, set this. */ unsigned long lineNumber; /* line number of tag */ const char* pattern; /* pattern for locating input line @@ -142,12 +146,4 @@ extern bool isTagExtraBitMarked (const tagEntryInfo *const tag, xtagType extra); extern void attachParserField (tagEntryInfo *const tag, fieldType ftype, const char* value); extern void attachParserFieldToCorkEntry (int index, fieldType ftype, const char* value); -CTAGS_INLINE roleBitsType makeRoleBit(int roleIndex) -{ - if (roleIndex == ROLE_INDEX_DEFINITION) - return 0; - else - return ((roleBitsType)1) << roleIndex; -} - #endif /* CTAGS_MAIN_ENTRY_H */ diff --git a/ctags/main/entry_p.h b/ctags/main/entry_p.h index 533ea7631a..cf09879a97 100644 --- a/ctags/main/entry_p.h +++ b/ctags/main/entry_p.h @@ -30,7 +30,7 @@ extern void freeTagFileResources (void); extern const char *tagFileName (void); extern void openTagFile (void); extern void closeTagFile (const bool resize); -extern void setupWriter (void); +extern void setupWriter (void *writerClientData); extern bool teardownWriter (const char *inputFilename); extern unsigned long numTagsAdded(void); @@ -65,4 +65,13 @@ extern void makeFileTag (const char *const fileName); extern const tagField* getParserField (const tagEntryInfo * tag, int index); + +CTAGS_INLINE roleBitsType makeRoleBit(int roleIndex) +{ + if (roleIndex == ROLE_INDEX_DEFINITION) + return 0; + else + return ((roleBitsType)1) << roleIndex; +} + #endif /* CTAGS_PRIVATE_ENTRY_H */ diff --git a/ctags/main/error.c b/ctags/main/error.c index 2eb9d8613f..bc5493dd34 100644 --- a/ctags/main/error.c +++ b/ctags/main/error.c @@ -11,7 +11,7 @@ #include #include -#include "error.h" +#include "error_p.h" #include "options_p.h" #include "routines_p.h" diff --git a/ctags/main/error.h b/ctags/main/error_p.h similarity index 100% rename from ctags/main/error.h rename to ctags/main/error_p.h diff --git a/ctags/main/field.c b/ctags/main/field.c index e5db9fe000..c96f276b1d 100644 --- a/ctags/main/field.c +++ b/ctags/main/field.c @@ -21,51 +21,55 @@ #include "entry.h" #include "entry_p.h" #include "field.h" +#include "field_p.h" #include "kind.h" #include "options_p.h" #include "parse_p.h" #include "read.h" #include "routines.h" #include "trashbox.h" +#include "writer_p.h" +#include "xtag_p.h" typedef struct sFieldObject { fieldDefinition *def; - unsigned int fixed: 1; /* fields which cannot be disabled. */ vString *buffer; const char* nameWithPrefix; langType language; fieldType sibling; } fieldObject; -static const char *renderFieldName (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldNameNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, - bool *rejected); -static const char *renderFieldInput (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldInputNoEscape (const tagEntryInfo *const tag, const char *value, vString* b, - bool *rejected); -static const char *renderFieldCompactInputLine (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldSignature (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldScope (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldScopeNoEscape (const tagEntryInfo *const tag, const char *value, vString* b, - bool *rejected); -static const char *renderFieldTyperef (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldInherits (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldKindName (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldLineNumber (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldLanguage (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldAccess (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldKindLetter (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldImplementation (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldFile (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldPatternCommon (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldPatternCtags (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldRoles (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldRefMarker (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldExtras (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldXpath (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldScopeKindName(const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); -static const char *renderFieldEnd (const tagEntryInfo *const tag, const char *value, vString* b, bool *rejected); +static const char *renderFieldName (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldNameNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b); +static const char *renderFieldInput (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldInputNoEscape (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldCompactInputLine (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldSignature (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldSignatureNoEscape (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldScope (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldScopeNoEscape (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldTyperef (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldInherits (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldKindName (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldLineNumber (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldLanguage (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldAccess (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldKindLetter (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldImplementation (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldFile (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldPattern (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldRoles (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldRefMarker (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldExtras (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldXpath (const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldScopeKindName(const tagEntryInfo *const tag, const char *value, vString* b); +static const char *renderFieldEnd (const tagEntryInfo *const tag, const char *value, vString* b); + +static bool hasTabCharInName (const tagEntryInfo *const tag, const char *value); +static bool hasTabCharInInput (const tagEntryInfo *const tag, const char*value); +static bool hasTabCharInFieldScope (const tagEntryInfo *const tag, const char *value); +static bool hasTabCharInSignature (const tagEntryInfo *const tag, const char *value); static bool isLanguageFieldAvailable (const tagEntryInfo *const tag); static bool isTyperefFieldAvailable (const tagEntryInfo *const tag); @@ -79,15 +83,17 @@ static bool isXpathFieldAvailable (const tagEntryInfo *const tag); static bool isEndFieldAvailable (const tagEntryInfo *const tag); -#define DEFINE_FIELD(L, N, V, H, DT, ...) \ - DEFINE_FIELD_FULL (L, N, V, H, NULL, DT, __VA_ARGS__) -#define DEFINE_FIELD_FULL(L, N, V, H, A, DT, ...) \ +#define DEFINE_FIELD(L, N, V, H, DT, RE) \ + DEFINE_FIELD_FULL (L, N, V, H, NULL, DT, RE, NULL, NULL) +#define DEFINE_FIELD_FULL(L, N, V, H, A, DT, RE, RN, HSC) \ { \ .letter = L, \ .name = N, \ .description = H, \ .enabled = V, \ - .renderEscaped = { __VA_ARGS__ }, \ + .render = RE, \ + .renderNoEscaping= RN, \ + .hasTabChar = HSC, \ .isValueAvailable = A, \ .dataType = DT, \ } @@ -96,133 +102,129 @@ static bool isEndFieldAvailable (const tagEntryInfo *const tag); static fieldDefinition fieldDefinitionsFixed [] = { /* FIXED FIELDS */ - DEFINE_FIELD ('N', "name", true, + DEFINE_FIELD_FULL ('N', "name", true, "tag name", + NULL, FIELDTYPE_STRING, - [WRITER_U_CTAGS] = renderFieldName, - [WRITER_E_CTAGS] = renderFieldNameNoEscape, - [WRITER_JSON] = renderFieldNameNoEscape, - ), - DEFINE_FIELD ('F', "input", true, + renderFieldName, renderFieldNameNoEscape, + hasTabCharInName), + DEFINE_FIELD_FULL ('F', "input", true, "input file", + NULL, FIELDTYPE_STRING, - [WRITER_U_CTAGS] = renderFieldInput, - [WRITER_E_CTAGS] = renderFieldInputNoEscape, - [WRITER_JSON] = renderFieldInputNoEscape, - ), + renderFieldInput, renderFieldInputNoEscape, + hasTabCharInInput), DEFINE_FIELD ('P', "pattern", true, "pattern", FIELDTYPE_STRING|FIELDTYPE_BOOL, - [WRITER_U_CTAGS] = renderFieldPatternCtags, - [WRITER_XREF] = renderFieldPatternCommon, - [WRITER_JSON] = renderFieldPatternCommon, - ), + renderFieldPattern), }; static fieldDefinition fieldDefinitionsExuberant [] = { DEFINE_FIELD ('C', "compact", false, "compact input line (used only in xref output)", FIELDTYPE_STRING, - [WRITER_U_CTAGS] = renderFieldCompactInputLine), + renderFieldCompactInputLine), /* EXTENSION FIELDS */ DEFINE_FIELD_FULL ('a', "access", false, "Access (or export) of class members", isAccessFieldAvailable, FIELDTYPE_STRING, - [WRITER_U_CTAGS] = renderFieldAccess), + renderFieldAccess, NULL, NULL), DEFINE_FIELD_FULL ('f', "file", true, "File-restricted scoping", isFileFieldAvailable, FIELDTYPE_BOOL, - [WRITER_U_CTAGS] = renderFieldFile), + renderFieldFile, NULL, NULL), DEFINE_FIELD_FULL ('i', "inherits", false, "Inheritance information", isInheritsFieldAvailable, FIELDTYPE_STRING|FIELDTYPE_BOOL, - [WRITER_U_CTAGS] = renderFieldInherits), + renderFieldInherits, NULL, NULL), DEFINE_FIELD ('K', NULL, false, "Kind of tag as full name", FIELDTYPE_STRING, - [WRITER_U_CTAGS] = renderFieldKindName), + renderFieldKindName), DEFINE_FIELD ('k', NULL, true, "Kind of tag as a single letter", FIELDTYPE_STRING, - [WRITER_U_CTAGS] = renderFieldKindLetter), + renderFieldKindLetter), DEFINE_FIELD_FULL ('l', "language", false, "Language of input file containing tag", isLanguageFieldAvailable, FIELDTYPE_STRING, - [WRITER_U_CTAGS] = renderFieldLanguage), + renderFieldLanguage, NULL, NULL), DEFINE_FIELD_FULL ('m', "implementation", false, "Implementation information", isImplementationFieldAvailable, FIELDTYPE_STRING, - [WRITER_U_CTAGS] = renderFieldImplementation), + renderFieldImplementation, NULL, NULL), DEFINE_FIELD ('n', "line", false, "Line number of tag definition", FIELDTYPE_INTEGER, - [WRITER_U_CTAGS] = renderFieldLineNumber), - DEFINE_FIELD_FULL ('S', "signature", false, + renderFieldLineNumber), + DEFINE_FIELD_FULL ('S', "signature", false, "Signature of routine (e.g. prototype or parameter list)", isSignatureFieldAvailable, FIELDTYPE_STRING, - [WRITER_U_CTAGS] = renderFieldSignature), - DEFINE_FIELD ('s', NULL, true, + renderFieldSignature, renderFieldSignatureNoEscape, + hasTabCharInSignature), + DEFINE_FIELD_FULL ('s', NULL, true, "Scope of tag definition (`p' can be used for printing its kind)", + NULL, FIELDTYPE_STRING, - [WRITER_U_CTAGS] = renderFieldScope, - [WRITER_E_CTAGS] = renderFieldScopeNoEscape, - [WRITER_JSON] = renderFieldScopeNoEscape), + renderFieldScope, renderFieldScopeNoEscape, + hasTabCharInFieldScope), DEFINE_FIELD_FULL ('t', "typeref", true, "Type and name of a variable or typedef", isTyperefFieldAvailable, FIELDTYPE_STRING, - [WRITER_U_CTAGS] = renderFieldTyperef), + renderFieldTyperef, NULL, NULL), DEFINE_FIELD ('z', "kind", false, "Include the \"kind:\" key in kind field (use k or K) in tags output, kind full name in xref output", FIELDTYPE_STRING, /* Following renderer is for handling --_xformat=%{kind}; and is not for tags output. */ - [WRITER_U_CTAGS] = renderFieldKindName), + renderFieldKindName), }; static fieldDefinition fieldDefinitionsUniversal [] = { DEFINE_FIELD ('r', "roles", false, "Roles", FIELDTYPE_STRING, - [WRITER_U_CTAGS] = renderFieldRoles), + renderFieldRoles), DEFINE_FIELD ('R', NULL, false, "Marker (R or D) representing whether tag is definition or reference", FIELDTYPE_STRING, - [WRITER_U_CTAGS] = renderFieldRefMarker), - DEFINE_FIELD ('Z', "scope", false, + renderFieldRefMarker), + DEFINE_FIELD_FULL ('Z', "scope", false, "Include the \"scope:\" key in scope field (use s) in tags output, scope name in xref output", + NULL, FIELDTYPE_STRING, /* Following renderer is for handling --_xformat=%{scope}; and is not for tags output. */ - [WRITER_U_CTAGS] = renderFieldScope, - [WRITER_E_CTAGS] = renderFieldScopeNoEscape, - [WRITER_JSON] = renderFieldScopeNoEscape), + renderFieldScope, renderFieldScopeNoEscape, + hasTabCharInFieldScope), DEFINE_FIELD_FULL ('E', "extras", false, "Extra tag type information", isExtrasFieldAvailable, FIELDTYPE_STRING, - [WRITER_U_CTAGS] = renderFieldExtras), + renderFieldExtras, NULL, NULL), DEFINE_FIELD_FULL ('x', "xpath", false, "xpath for the tag", isXpathFieldAvailable, FIELDTYPE_STRING, - [WRITER_U_CTAGS] = renderFieldXpath), + renderFieldXpath, NULL, NULL), DEFINE_FIELD ('p', "scopeKind", false, "Kind of scope as full name", FIELDTYPE_STRING, - [WRITER_U_CTAGS] = renderFieldScopeKindName), + renderFieldScopeKindName), DEFINE_FIELD_FULL ('e', "end", false, "end lines of various items", isEndFieldAvailable, FIELDTYPE_INTEGER, - [WRITER_U_CTAGS] = renderFieldEnd), + renderFieldEnd, NULL, NULL), }; @@ -250,7 +252,6 @@ extern void initFieldObjects (void) { fobj = fieldObjects + i + fieldObjectUsed; fobj->def = fieldDefinitionsFixed + i; - fobj->fixed = 1; fobj->buffer = NULL; fobj->nameWithPrefix = fobj->def->name; fobj->language = LANG_IGNORE; @@ -262,7 +263,6 @@ extern void initFieldObjects (void) { fobj = fieldObjects + i + fieldObjectUsed; fobj->def = fieldDefinitionsExuberant +i; - fobj->fixed = 0; fobj->buffer = NULL; fobj->nameWithPrefix = fobj->def->name; fobj->language = LANG_IGNORE; @@ -276,7 +276,6 @@ extern void initFieldObjects (void) fobj = fieldObjects + i + fieldObjectUsed; fobj->def = fieldDefinitionsUniversal + i; - fobj->fixed = 0; fobj->buffer = NULL; if (fobj->def->name) @@ -423,25 +422,22 @@ static const char *renderEscapedName (const bool isTagName, return renderEscapedString (s, tag, b); } -static const char *renderFieldName (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, - bool *rejected CTAGS_ATTR_UNUSED) +static const char *renderFieldName (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { return renderEscapedName (true, tag->name, tag, b); } -static const char *renderFieldNameNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, - bool *rejected) +static const char *renderFieldNameNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { - if (strpbrk (tag->name, " \t")) - { - *rejected = true; - return NULL; - } return renderAsIs (b, tag->name); } -static const char *renderFieldInput (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, - bool *rejected CTAGS_ATTR_UNUSED) +static bool hasTabCharInName (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED) +{ + return strchr (tag->name, '\t')? true: false; +} + +static const char *renderFieldInput (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { const char *f = tag->inputFileName; @@ -450,32 +446,45 @@ static const char *renderFieldInput (const tagEntryInfo *const tag, const char * return renderEscapedString (f, tag, b); } -static const char *renderFieldInputNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, - bool *rejected) +static const char *renderFieldInputNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { const char *f = tag->inputFileName; if (Option.lineDirectives && tag->sourceFileName) f = tag->sourceFileName; - if (strpbrk (f, " \t")) - { - *rejected = true; - return NULL; - } - return renderAsIs (b, f); } -static const char *renderFieldSignature (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, - bool *rejected CTAGS_ATTR_UNUSED) +static bool hasTabCharInInput (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED) +{ + const char *f = tag->inputFileName; + + if (Option.lineDirectives && tag->sourceFileName) + f = tag->sourceFileName; + + return strchr (f, '\t')? true: false; +} + +static const char *renderFieldSignature (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { return renderEscapedString (WITH_DEFUALT_VALUE (tag->extensionFields.signature), tag, b); } -static const char *renderFieldScope (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, - bool *rejected CTAGS_ATTR_UNUSED) +static const char *renderFieldSignatureNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) +{ + return renderAsIs (b, WITH_DEFUALT_VALUE (tag->extensionFields.signature)); +} + +static bool hasTabCharInSignature (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED) +{ + return (tag->extensionFields.signature && strchr(tag->extensionFields.signature, '\t')) + ? true + : false; +} + +static const char *renderFieldScope (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { const char* scope; @@ -483,30 +492,30 @@ static const char *renderFieldScope (const tagEntryInfo *const tag, const char * return scope? renderEscapedName (false, scope, tag, b): NULL; } -static const char *renderFieldScopeNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, - bool *rejected) +static const char *renderFieldScopeNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { const char* scope; getTagScopeInformation ((tagEntryInfo *const)tag, NULL, &scope); - if (scope && strpbrk (scope, " \t")) - { - *rejected = true; - return NULL; - } - return scope? renderAsIs (b, scope): NULL; } -static const char *renderFieldInherits (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, - bool *rejected CTAGS_ATTR_UNUSED) +static bool hasTabCharInFieldScope (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED) +{ + const char* scope; + + getTagScopeInformation ((tagEntryInfo *const)tag, NULL, &scope); + return (scope && strchr (scope, '\t')); +} + + +static const char *renderFieldInherits (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { return renderEscapedString (WITH_DEFUALT_VALUE (tag->extensionFields.inheritance), tag, b); } -static const char *renderFieldTyperef (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, - bool *rejected CTAGS_ATTR_UNUSED) +static const char *renderFieldTyperef (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { /* Return "-" instead of "-:-". */ if (tag->extensionFields.typeRef [0] == NULL @@ -519,22 +528,68 @@ static const char *renderFieldTyperef (const tagEntryInfo *const tag, const char } -extern const char* renderFieldEscaped (writerType writer, - fieldType type, - const tagEntryInfo *tag, - int index, - bool *rejected) +static const char* renderFieldCommon (fieldType type, + const tagEntryInfo *tag, + int index, + bool noEscaping) { fieldObject *fobj = fieldObjects + type; const char *value; - renderEscaped rfn; - bool stub; + fieldRenderer rfn; Assert (tag); - Assert (fobj->def->renderEscaped); Assert (index < 0 || ((unsigned int)index) < tag->usedParserFields); + if (index >= 0) + { + const tagField *f = getParserField (tag, index); + + value = f->value; + } + else + value = NULL; + + if (noEscaping) + rfn = fobj->def->renderNoEscaping; + else + rfn = fobj->def->render; + Assert (rfn); + fobj->buffer = vStringNewOrClearWithAutoRelease (fobj->buffer); + return rfn (tag, value, fobj->buffer); +} + +extern const char* renderField (fieldType type, const tagEntryInfo *tag, int index) +{ + return renderFieldCommon (type, tag, index, false); +} + +extern const char* renderFieldNoEscaping (fieldType type, const tagEntryInfo *tag, int index) +{ + return renderFieldCommon (type, tag, index, true); +} + +static bool defaultHasTabChar (const tagEntryInfo *const tag CTAGS_ATTR_UNUSED, const char* value) +{ + return strchr (value, '\t')? true: false; +} + +extern bool doesFieldHaveTabChar (fieldType type, const tagEntryInfo *tag, int index) +{ + fieldObject *fobj = fieldObjects + type; + const char *value; + bool (* hasTabChar) (const tagEntryInfo *const, const char*) = fobj->def->hasTabChar; + + Assert (tag); + Assert (index == NO_PARSER_FIELD || ((unsigned int)index) < tag->usedParserFields); + + if (hasTabChar == NULL) + { + if (index == NO_PARSER_FIELD) + return false; + else + hasTabChar = defaultHasTabChar; + } if (index >= 0) { @@ -545,13 +600,7 @@ extern const char* renderFieldEscaped (writerType writer, else value = NULL; - rfn = fobj->def->renderEscaped [writer]; - if (rfn == NULL) - rfn = fobj->def->renderEscaped [WRITER_DEFAULT]; - - if (!rejected) - rejected = &stub; - return rfn (tag, value, fobj->buffer, rejected); + return (* hasTabChar) (tag, value); } /* Writes "line", stripping leading and duplicate white space. @@ -586,8 +635,7 @@ static const char* renderCompactInputLine (vString *b, const char *const line) return vStringValue (b); } -static const char *renderFieldKindName (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b, - bool *rejected CTAGS_ATTR_UNUSED) +static const char *renderFieldKindName (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { const char* name = getTagKindName (tag); return renderAsIs (b, name); @@ -595,8 +643,7 @@ static const char *renderFieldKindName (const tagEntryInfo *const tag, const cha static const char *renderFieldCompactInputLine (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b, - bool *rejected CTAGS_ATTR_UNUSED) + vString* b) { const char *line; static vString *tmp; @@ -620,8 +667,7 @@ static const char *renderFieldCompactInputLine (const tagEntryInfo *const tag, static const char *renderFieldLineNumber (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b, - bool *rejected CTAGS_ATTR_UNUSED) + vString* b) { long ln = tag->lineNumber; char buf[32] = {[0] = '\0'}; @@ -635,8 +681,7 @@ static const char *renderFieldLineNumber (const tagEntryInfo *const tag, static const char *renderFieldRoles (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b, - bool *rejected CTAGS_ATTR_UNUSED) + vString* b) { roleBitsType rbits = tag->extensionFields.roleBits; const roleDefinition * role; @@ -666,8 +711,7 @@ static const char *renderFieldRoles (const tagEntryInfo *const tag, static const char *renderFieldLanguage (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b, - bool *rejected CTAGS_ATTR_UNUSED) + vString* b) { const char *l; @@ -681,16 +725,14 @@ static const char *renderFieldLanguage (const tagEntryInfo *const tag, static const char *renderFieldAccess (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b, - bool *rejected CTAGS_ATTR_UNUSED) + vString* b) { return renderAsIs (b, WITH_DEFUALT_VALUE (tag->extensionFields.access)); } static const char *renderFieldKindLetter (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b, - bool *rejected CTAGS_ATTR_UNUSED) + vString* b) { static char c[2] = { [1] = '\0' }; @@ -701,24 +743,21 @@ static const char *renderFieldKindLetter (const tagEntryInfo *const tag, static const char *renderFieldImplementation (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b, - bool *rejected CTAGS_ATTR_UNUSED) + vString* b) { return renderAsIs (b, WITH_DEFUALT_VALUE (tag->extensionFields.implementation)); } static const char *renderFieldFile (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b, - bool *rejected CTAGS_ATTR_UNUSED) + vString* b) { return renderAsIs (b, tag->isFileScope? "file": "-"); } -static const char *renderFieldPatternCommon (const tagEntryInfo *const tag, +static const char *renderFieldPattern (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b, - bool *rejected CTAGS_ATTR_UNUSED) + vString* b) { if (tag->isFileEntry) return NULL; @@ -735,26 +774,9 @@ static const char *renderFieldPatternCommon (const tagEntryInfo *const tag, return vStringValue (b); } -static const char *renderFieldPatternCtags (const tagEntryInfo *const tag, - const char *value, - vString* b, - bool *rejected) -{ - /* This is for handling 'common' of 'fortran'. See the - description of --excmd=mixed in ctags.1. In tags output, what - we call "pattern" is instructions for vi. - - However, in the other formats, pattern should be pattern as its name. */ - if (tag->lineNumberEntry) - return NULL; - - return renderFieldPatternCommon(tag, value, b, rejected); -} - static const char *renderFieldRefMarker (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b, - bool *rejected CTAGS_ATTR_UNUSED) + vString* b) { static char c[2] = { [1] = '\0' }; @@ -765,8 +787,7 @@ static const char *renderFieldRefMarker (const tagEntryInfo *const tag, static const char *renderFieldExtras (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b, - bool *rejected CTAGS_ATTR_UNUSED) + vString* b) { int i; bool hasExtra = false; @@ -797,8 +818,7 @@ static const char *renderFieldExtras (const tagEntryInfo *const tag, static const char *renderFieldXpath (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b, - bool *rejected CTAGS_ATTR_UNUSED) + vString* b) { #ifdef HAVE_LIBXML if (tag->extensionFields.xpath) @@ -810,8 +830,7 @@ static const char *renderFieldXpath (const tagEntryInfo *const tag, static const char *renderFieldScopeKindName(const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b, - bool *rejected CTAGS_ATTR_UNUSED) + vString* b) { const char* kind; @@ -821,8 +840,7 @@ static const char *renderFieldScopeKindName(const tagEntryInfo *const tag, static const char *renderFieldEnd (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, - vString* b, - bool *rejected CTAGS_ATTR_UNUSED) + vString* b) { static char buf[16]; @@ -904,16 +922,11 @@ extern bool isFieldEnabled (fieldType type) return getFieldObject(type)->def->enabled; } -static bool isFieldFixed (fieldType type) -{ - return getFieldObject(type)->fixed? true: false; -} - extern bool enableField (fieldType type, bool state, bool warnIfFixedField) { fieldDefinition *def = getFieldObject(type)->def; bool old = def->enabled; - if (isFieldFixed (type)) + if (writerDoesTreatFieldAsFixed (type)) { if ((!state) && warnIfFixedField) { @@ -962,9 +975,12 @@ extern unsigned int getFieldDataType (fieldType type) return getFieldObject(type)->def->dataType; } -extern bool isFieldRenderable (fieldType type) +extern bool doesFieldHaveRenderer (fieldType type, bool noEscaping) { - return getFieldObject(type)->def->renderEscaped [WRITER_DEFAULT]? true: false; + if (noEscaping) + return getFieldObject(type)->def->renderNoEscaping? true: false; + else + return getFieldObject(type)->def->render? true: false; } extern int countFields (void) @@ -999,10 +1015,9 @@ static void updateSiblingField (fieldType type, const char* name) static const char* defaultRenderer (const tagEntryInfo *const tag CTAGS_ATTR_UNUSED, const char *value, - vString * buffer CTAGS_ATTR_UNUSED, - bool *rejected CTAGS_ATTR_UNUSED) + vString * buffer CTAGS_ATTR_UNUSED) { - return value; + return renderEscapedString (value, tag, buffer); } extern int defineField (fieldDefinition *def, langType language) @@ -1027,15 +1042,18 @@ extern int defineField (fieldDefinition *def, langType language) fobj = fieldObjects + (fieldObjectUsed); def->ftype = fieldObjectUsed++; - if (def->renderEscaped [WRITER_DEFAULT] == NULL) - def->renderEscaped [WRITER_DEFAULT] = defaultRenderer; + if (def->render == NULL) + { + def->render = defaultRenderer; + def->renderNoEscaping = NULL; + def->hasTabChar = NULL; + } if (! def->dataType) def->dataType = FIELDTYPE_STRING; fobj->def = def; - fobj->fixed = 0; fobj->buffer = NULL; nameWithPrefix = eMalloc (sizeof CTAGS_FIELD_PREFIX + strlen (def->name) + 1); @@ -1096,7 +1114,7 @@ static void fieldColprintAddLine (struct colprintTable *table, int i) typefields[offset] = fieldDataTypeFalgs[offset]; } colprintLineAppendColumnCString (line, typefields); - colprintLineAppendColumnBool (line, fobj->fixed); + colprintLineAppendColumnBool (line, writerDoesTreatFieldAsFixed (i)); colprintLineAppendColumnCString (line, fdef->description); } diff --git a/ctags/main/field.h b/ctags/main/field.h index d16f5bc63e..d52da4ce04 100644 --- a/ctags/main/field.h +++ b/ctags/main/field.h @@ -12,13 +12,19 @@ #ifndef CTAGS_MAIN_FIELD_H #define CTAGS_MAIN_FIELD_H +/* +* INCLUDE FILES +*/ + #include "general.h" -#include "colprint_p.h" -#include "writer_p.h" #include "types.h" #include "vstring.h" +/* +* DATA DECLARATIONS +*/ + typedef enum eFieldType { /* extension field content control */ FIELD_UNKNOWN = -1, @@ -54,12 +60,6 @@ typedef enum eFieldType { /* extension field content control */ FIELD_BUILTIN_LAST = FIELD_END_LINE, } fieldType ; -typedef const char* (* renderEscaped) (const tagEntryInfo *const tag, - const char *value, - vString * buffer, - bool *rejected); -typedef bool (* isValueAvailable) (const struct sTagEntryInfo *const tag); - #define fieldDataTypeFalgs "sib" /* used in --list-fields */ typedef enum eFieldDataType { FIELDTYPE_STRING = 1 << 0, @@ -70,6 +70,10 @@ typedef enum eFieldDataType { FIELDTYPE_END_MARKER = 1 << 3, } fieldDataType; +typedef const char* (*fieldRenderer)(const tagEntryInfo *const, + const char *, + vString *); + #define FIELD_LETTER_NO_USE '\0' struct sFieldDefinition { /* letter, and ftype are initialized in the main part, @@ -79,58 +83,23 @@ struct sFieldDefinition { const char* name; const char* description; bool enabled; - renderEscaped renderEscaped [WRITER_COUNT]; - isValueAvailable isValueAvailable; + + fieldRenderer render; + fieldRenderer renderNoEscaping; + bool (*hasTabChar) (const tagEntryInfo *const, const char*); + + bool (* isValueAvailable) (const tagEntryInfo *const); + fieldDataType dataType; /* used in json output */ unsigned int ftype; /* Given from the main part */ }; -extern fieldType getFieldTypeForOption (char letter); - /* - `getFieldTypeForName' is for looking for a field not owned by any parser, - - `getFieldTypeForNameAndLanguage' can be used for getting all fields having - the same name; specify `LANG_AUTO' as `language' parameter to get the first - field having the name. With the returned fieldType, `nextSiblingField' gets - the next field having the same name. `nextSiblingField' returns `FIELD_UNKNOWN' - at the end of iteration. - - Specifying `LANG_IGNORE' has the same effects as `LANG_AUTO'. However, - internally, each parser is not initialized. `LANG_IGNORE' is a bit faster. */ -extern fieldType getFieldTypeForName (const char *name); -extern fieldType getFieldTypeForNameAndLanguage (const char *fieldName, langType language); +* FUNCTION PROTOTYPES +*/ + extern bool isFieldEnabled (fieldType type); -extern bool enableField (fieldType type, bool state, bool warnIfFixedField); -extern bool isCommonField (fieldType type); -extern int getFieldOwner (fieldType type); -extern const char* getFieldName (fieldType type); -extern unsigned int getFieldDataType (fieldType type); -extern void printFields (int language); - -/* Whether the field specified with TYPE has a - method for rendering in the current format. */ -extern bool isFieldRenderable (fieldType type); - -extern bool doesFieldHaveValue (fieldType type, const tagEntryInfo *tag); -extern const char* renderFieldEscaped (writerType writer, fieldType type, const tagEntryInfo *tag, int index, - bool *rejected); - -extern void initFieldObjects (void); -extern int countFields (void); - -/* language should be typed to langType. - Use int here to avoid circular dependency */ -extern int defineField (fieldDefinition *spec, langType language); -extern fieldType nextSiblingField (fieldType type); - -/* --list-fields implementation. LANGUAGE must be initialized. */ -extern struct colprintTable * fieldColprintTableNew (void); -extern void fieldColprintAddCommonLines (struct colprintTable *table); -extern void fieldColprintAddLanguageLines (struct colprintTable *table, langType language); -extern void fieldColprintTablePrint (struct colprintTable *table, - bool withListHeader, bool machinable, FILE *fp); #endif /* CTAGS_MAIN_FIELD_H */ diff --git a/ctags/main/field_p.h b/ctags/main/field_p.h new file mode 100644 index 0000000000..7d2b8c9fcd --- /dev/null +++ b/ctags/main/field_p.h @@ -0,0 +1,78 @@ +/* + * + * Copyright (c) 2015, Red Hat, Inc. + * Copyright (c) 2015, Masatake YAMATO + * + * Author: Masatake YAMATO + * + * This source code is released for free distribution under the terms of the + * GNU General Public License version 2 or (at your option) any later version. + * + */ +#ifndef CTAGS_MAIN_FIELD_PRIVATE_H +#define CTAGS_MAIN_FIELD_PRIVATE_H + +/* +* INCLUDE FILES +*/ +#include "general.h" +#include "colprint_p.h" +#include "field.h" + +/* +* DATA DECLARATIONS +*/ + + +/* +* FUNCTION PROTOTYPES +*/ + +extern fieldType getFieldTypeForOption (char letter); + +/* + `getFieldTypeForName' is for looking for a field not owned by any parser, + + `getFieldTypeForNameAndLanguage' can be used for getting all fields having + the same name; specify `LANG_AUTO' as `language' parameter to get the first + field having the name. With the returned fieldType, `nextSiblingField' gets + the next field having the same name. `nextSiblingField' returns `FIELD_UNKNOWN' + at the end of iteration. + + Specifying `LANG_IGNORE' has the same effects as `LANG_AUTO'. However, + internally, each parser is not initialized. `LANG_IGNORE' is a bit faster. */ +extern fieldType getFieldTypeForName (const char *name); +extern fieldType getFieldTypeForNameAndLanguage (const char *fieldName, langType language); +extern bool enableField (fieldType type, bool state, bool warnIfFixedField); +extern bool isCommonField (fieldType type); +extern int getFieldOwner (fieldType type); +extern const char* getFieldName (fieldType type); +extern unsigned int getFieldDataType (fieldType type); +extern void printFields (int language); + +/* Whether the field specified with TYPE has a + method for rendering in the current format. */ +extern bool doesFieldHaveRenderer (fieldType type, bool noEscaping); + +extern bool doesFieldHaveValue (fieldType type, const tagEntryInfo *tag); + +extern const char* renderField (fieldType type, const tagEntryInfo *tag, int index); +extern const char* renderFieldNoEscaping (fieldType type, const tagEntryInfo *tag, int index); +extern bool doesFieldHaveTabChar (fieldType type, const tagEntryInfo *tag, int index); + +extern void initFieldObjects (void); +extern int countFields (void); + +/* language should be typed to langType. + Use int here to avoid circular dependency */ +extern int defineField (fieldDefinition *spec, langType language); +extern fieldType nextSiblingField (fieldType type); + +/* --list-fields implementation. LANGUAGE must be initialized. */ +extern struct colprintTable * fieldColprintTableNew (void); +extern void fieldColprintAddCommonLines (struct colprintTable *table); +extern void fieldColprintAddLanguageLines (struct colprintTable *table, langType language); +extern void fieldColprintTablePrint (struct colprintTable *table, + bool withListHeader, bool machinable, FILE *fp); + +#endif /* CTAGS_MAIN_FIELD_PRIVATE_H */ diff --git a/ctags/main/fmt.c b/ctags/main/fmt.c index 0be6d07aa3..9163f8e970 100644 --- a/ctags/main/fmt.c +++ b/ctags/main/fmt.c @@ -15,6 +15,7 @@ #include "entry_p.h" #include "fmt_p.h" #include "field.h" +#include "field_p.h" #include "parse.h" #include "routines.h" #include @@ -60,8 +61,7 @@ static int printTagField (fmtSpec* fspec, MIO* fp, const tagEntryInfo * tag) ftype = fspec->field.ftype; if (isCommonField (ftype)) - /* TODO: Don't use WRITER_XREF directly */ - str = renderFieldEscaped (WRITER_XREF, ftype, tag, NO_PARSER_FIELD, NULL); + str = renderField (ftype, tag, NO_PARSER_FIELD); else { unsigned int findex; @@ -77,9 +77,7 @@ static int printTagField (fmtSpec* fspec, MIO* fp, const tagEntryInfo * tag) if (findex == tag->usedParserFields) str = ""; else if (isFieldEnabled (f->ftype)) - /* TODO: Don't use WRITER_XREF directly */ - str = renderFieldEscaped (WRITER_XREF, f->ftype, - tag, findex, NULL); + str = renderField (f->ftype, tag, findex); } if (str == NULL) @@ -192,7 +190,7 @@ static fmtElement** queueTagField (fmtElement **last, long width, bool truncatio error (FATAL, "No such field letter: %c", field_letter); } - if (!isFieldRenderable (ftype)) + if (!doesFieldHaveRenderer (ftype, false)) { Assert (field_letter != NUL_FIELD_LETTER); error (FATAL, "The field cannot be printed in format output: %c", field_letter); diff --git a/ctags/main/general.h b/ctags/main/general.h index 5f68e5a2c4..4a950d5963 100644 --- a/ctags/main/general.h +++ b/ctags/main/general.h @@ -18,6 +18,19 @@ # include "e_msoft.h" #endif +/* To provide timings features if available. + */ +#ifdef HAVE_CLOCK +# ifdef HAVE_TIME_H +# include +# endif +#else +# ifdef HAVE_TIMES +# ifdef HAVE_SYS_TIMES_H +# include +# endif +# endif +#endif /* * MACROS @@ -47,7 +60,7 @@ * DATA DECLARATIONS */ -#ifdef USE_STDBOOL_H +#ifdef HAVE_STDBOOL_H # include #endif @@ -77,4 +90,26 @@ extern char *getenv (const char *); #define iconv_close libiconv_close #endif +/* +* Prepare clock() and its related macros +*/ +#if defined (HAVE_CLOCK) +# define CLOCK_AVAILABLE +# ifndef CLOCKS_PER_SEC +# define CLOCKS_PER_SEC 1000000 +# endif +#elif defined (HAVE_TIMES) +# define CLOCK_AVAILABLE +# define CLOCKS_PER_SEC 60 +static clock_t clock (void) +{ + struct tms buf; + + times (&buf); + return (buf.tms_utime + buf.tms_stime); +} +#else +# define clock() (clock_t)0 +#endif + #endif /* CTAGS_MAIN_GENERAL_H */ diff --git a/ctags/main/htable.c b/ctags/main/htable.c index 2556110f8d..ee7f8ba41e 100644 --- a/ctags/main/htable.c +++ b/ctags/main/htable.c @@ -140,6 +140,14 @@ extern hashTable *hashTableNew (unsigned int size, return htable; } +extern hashTable* hashTableIntNew (unsigned int size, + hashTableHashFunc hashfn, + hashTableEqualFunc equalfn, + hashTableFreeFunc keyfreefn) +{ + return hashTableNew (size, hashfn, equalfn, keyfreefn, NULL); +} + extern void hashTableDelete (hashTable *htable) { if (!htable) diff --git a/ctags/main/htable.h b/ctags/main/htable.h index 03cff36185..5f07552276 100644 --- a/ctags/main/htable.h +++ b/ctags/main/htable.h @@ -12,6 +12,7 @@ #define CTAGS_MAIN_HTABLE_H #include "general.h" +#include typedef struct sHashTable hashTable; typedef unsigned int (* hashTableHashFunc) (const void * const key); @@ -36,6 +37,7 @@ extern hashTable* hashTableNew (unsigned int size, hashTableEqualFunc equalfn, hashTableFreeFunc keyfreefn, hashTableFreeFunc valfreefn); + extern void hashTableDelete (hashTable *htable); extern void hashTableClear (hashTable *htable); extern void hashTablePutItem (hashTable *htable, void *key, void *value); @@ -45,4 +47,11 @@ extern bool hashTableDeleteItem (hashTable *htable, void *key); extern void hashTableForeachItem (hashTable *htable, hashTableForeachFunc proc, void *user_data); extern int hashTableCountItem (hashTable *htable); +extern hashTable* hashTableIntNew (unsigned int size, + hashTableHashFunc hashfn, + hashTableEqualFunc equalfn, + hashTableFreeFunc keyfreefn); +#define HT_PTR_TO_INT(P) ((int)(intptr_t)(P)) +#define HT_INT_TO_PTR(P) ((void*)(intptr_t)(P)) + #endif /* CTAGS_MAIN_HTABLE_H */ diff --git a/ctags/main/kind.c b/ctags/main/kind.c index 70db4d9766..e44c538a1e 100644 --- a/ctags/main/kind.c +++ b/ctags/main/kind.c @@ -15,12 +15,14 @@ #include #include +#include "colprint_p.h" #include "ctags.h" #include "debug.h" #include "entry.h" #include "kind.h" #include "parse_p.h" #include "options.h" +#include "ptrarray.h" #include "routines.h" #include "vstring.h" @@ -39,12 +41,15 @@ typedef struct sKindObject { kindDefinition *def; freeKindDefFunc free; struct roleControlBlock *rcb; + ptrArray * dynamicSeparators; } kindObject; struct kindControlBlock { kindObject *kind; unsigned int count; langType owner; + scopeSeparator defaultScopeSeparator; + scopeSeparator defaultRootScopeSeparator; }; extern const char *renderRole (const roleDefinition* const role, vString* b) @@ -61,39 +66,6 @@ extern void printKind (const kindDefinition* const kind, bool indent) kind->enabled ? "" : " [off]"); } -const char *scopeSeparatorFor (langType lang, int kindIndex, int parentKindIndex) -{ - kindDefinition *kind = getLanguageKind (lang, kindIndex); - scopeSeparator *table = kind->separators; - - /* If no table is given, use the default generic separator ".". - The exception is if a root separator is looked up. In this case, - return NULL to notify there is no root separator to the caller. */ - - if (table == NULL) - { - if (parentKindIndex == KIND_GHOST_INDEX) - return NULL; - else - return "."; - } - - while (table - kind->separators < (int)kind->separatorCount) - { - /* KIND_WILDCARD cannot be used as a key for finding - a root separator.*/ - if ( (table->parentKindIndex == KIND_WILDCARD_INDEX - && parentKindIndex != KIND_GHOST_INDEX) - || table->parentKindIndex == parentKindIndex) - return table->separator; - table++; - } - if (parentKindIndex == KIND_GHOST_INDEX) - return NULL; - else - return "."; -} - extern void enableKind (kindDefinition *kind, bool enable) { kindDefinition *slave; @@ -138,6 +110,16 @@ extern struct kindControlBlock* allocKindControlBlock (parserDefinition *parser) kcb->count = parser->kindCount; kcb->owner = parser->id; + kcb->defaultScopeSeparator.parentKindIndex = KIND_WILDCARD_INDEX; + kcb->defaultScopeSeparator.separator = NULL; + if (parser->defaultScopeSeparator) + kcb->defaultScopeSeparator.separator = eStrdup (parser->defaultScopeSeparator); + + kcb->defaultRootScopeSeparator.parentKindIndex = KIND_GHOST_INDEX; + kcb->defaultRootScopeSeparator.separator = NULL; + if (parser->defaultRootScopeSeparator) + kcb->defaultRootScopeSeparator.separator = eStrdup (parser->defaultRootScopeSeparator); + for (i = 0; i < parser->kindCount; ++i) { kindObject *kind = kcb->kind + i; @@ -145,6 +127,7 @@ extern struct kindControlBlock* allocKindControlBlock (parserDefinition *parser) kind->free = NULL; kind->def->id = i; kind->rcb = allocRoleControlBlock (kind); + kind->dynamicSeparators = NULL; } return kcb; @@ -171,7 +154,15 @@ extern void freeKindControlBlock (struct kindControlBlock* kcb) if (kcb->kind [i].free) kcb->kind [i].free (kcb->kind [i].def); freeRoleControlBlock (kcb->kind [i].rcb); + if (kcb->kind [i].dynamicSeparators) + ptrArrayDelete(kcb->kind [i].dynamicSeparators); } + + if (kcb->defaultRootScopeSeparator.separator) + eFree((char *)kcb->defaultRootScopeSeparator.separator); + if (kcb->defaultScopeSeparator.separator) + eFree((char *)kcb->defaultScopeSeparator.separator); + eFree (kcb->kind); eFree (kcb); } @@ -184,6 +175,7 @@ extern int defineKind (struct kindControlBlock* kcb, kindDefinition *def, kcb->kind [def->id].def = def; kcb->kind [def->id].free = freeKindDef; kcb->kind [def->id].rcb = allocRoleControlBlock(kcb->kind + def->id); + kcb->kind [def->id].dynamicSeparators = NULL; verbose ("Add kind[%d] \"%c,%s,%s\" to %s\n", def->id, def->letter, def->name, def->description, @@ -333,6 +325,142 @@ extern void linkKindDependency (struct kindControlBlock *masterKCB, } } +static void scopeSeparatorDelete (void *data) +{ + scopeSeparator *sep = data; + eFree ((void *)sep->separator); + sep->separator = NULL; + eFree (sep); +} + +extern int defineScopeSeparator(struct kindControlBlock* kcb, + int kindIndex, + int parentKindIndex, const char *separator) +{ + if (kindIndex == KIND_WILDCARD_INDEX) + { + if (parentKindIndex == KIND_WILDCARD_INDEX) + { + if (kcb->defaultScopeSeparator.separator) + eFree ((char *)kcb->defaultScopeSeparator.separator); + verbose ("Installing default separator for %s: %s\n", + getLanguageName (kcb->owner), separator); + kcb->defaultScopeSeparator.separator = eStrdup (separator); + } + else if (parentKindIndex == KIND_GHOST_INDEX) + { + if (kcb->defaultRootScopeSeparator.separator) + eFree ((char *)kcb->defaultRootScopeSeparator.separator); + verbose ("Installing default root separator for %s: %s\n", + getLanguageName (kcb->owner), + separator); + kcb->defaultRootScopeSeparator.separator = eStrdup (separator); + } + else + error (FATAL, + "Don't specify a real kind as parent when defining a default scope separator: %d", + parentKindIndex); + return 0; + } + Assert (kcb->count > kindIndex); + kindObject *kind = kcb->kind + kindIndex; + + if (!kind->dynamicSeparators) + kind->dynamicSeparators = ptrArrayNew (scopeSeparatorDelete); + + scopeSeparator *sep = xMalloc (1, scopeSeparator); + sep->parentKindIndex = parentKindIndex; + sep->separator = eStrdup(separator); + ptrArrayAdd (kind->dynamicSeparators, sep); + + return 0; +} + +static scopeSeparator *getScopeSeparatorDynamic(kindObject *kobj, int parentKindIndex) +{ + scopeSeparator *sep; + + if (kobj->dynamicSeparators) + { + for (unsigned int i = ptrArrayCount (kobj->dynamicSeparators); 0 < i ; i--) + { + sep = ptrArrayItem (kobj->dynamicSeparators, i - 1); + if (sep->parentKindIndex == parentKindIndex) + return sep; + } + } + return NULL; +} + +static const scopeSeparator *getScopeSeparatorStatic(kindDefinition *kdef, int parentKindIndex) +{ + scopeSeparator *table = kdef->separators; + + if (table == NULL) + return NULL; + + while (table - kdef->separators < (int)kdef->separatorCount) + { + if (table->parentKindIndex == parentKindIndex) + return table; + + /* If a caller wants a root separator for kdef, + we should not return a wildcard table. */ + if (parentKindIndex != KIND_GHOST_INDEX + && table->parentKindIndex == KIND_WILDCARD_INDEX) + return table; + + table++; + } + + return NULL; +} + +extern const scopeSeparator *getScopeSeparator(struct kindControlBlock* kcb, + int kindIndex, int parentKindIndex) +{ + Assert (kindIndex != KIND_GHOST_INDEX); + Assert (kindIndex != KIND_FILE_INDEX); + Assert (kindIndex != KIND_WILDCARD_INDEX); + + Assert (parentKindIndex != KIND_WILDCARD_INDEX); + Assert (parentKindIndex != KIND_FILE_INDEX); + /* A caller specifies KIND_GHOST_INDEX for parentKindIndex when it + * wants root separator. */ + + Assert (kcb->count > kindIndex); + kindObject *kobj = kcb->kind + kindIndex; + const scopeSeparator *sep; + + sep = getScopeSeparatorDynamic (kobj, parentKindIndex); + if (sep) + return sep; + + sep = getScopeSeparatorStatic (kobj->def, parentKindIndex); + if (sep) + return sep; + + /* Cannot find a sitable sep definition. + * Use default one. */ + if (parentKindIndex == KIND_GHOST_INDEX) + { + if (kcb->defaultRootScopeSeparator.separator) + return &kcb->defaultRootScopeSeparator; + return NULL; + } + else + { + if (kcb->defaultScopeSeparator.separator) + return &kcb->defaultScopeSeparator; + + static scopeSeparator defaultSeparator = { + .separator = ".", + .parentKindIndex = KIND_WILDCARD_INDEX, + }; + return &defaultSeparator; + } +} + #ifdef DEBUG extern bool doesParserUseKind (struct kindControlBlock* kcb, char letter) { diff --git a/ctags/main/kind.h b/ctags/main/kind.h index a686824127..3a80be947b 100644 --- a/ctags/main/kind.h +++ b/ctags/main/kind.h @@ -8,10 +8,17 @@ #ifndef CTAGS_MAIN_KIND_H #define CTAGS_MAIN_KIND_H +/* +* INCLUDE FILES +*/ + #include "general.h" #include "types.h" #include "routines.h" -#include "vstring.h" + +/* +* DATA DECLARATIONS +*/ struct sRoleDefinition { bool enabled; @@ -21,9 +28,6 @@ struct sRoleDefinition { int id; }; -typedef void (* freeRoleDefFunc) (roleDefinition *); -extern const char *renderRole (const roleDefinition* const def, vString* b); - /* * Predefined kinds */ @@ -81,49 +85,10 @@ struct sKindDefinition { #define ATTACH_ROLES(RS) .nRoles = ARRAY_SIZE(RS), .roles = RS #define ATTACH_SEPARATORS(S) .separators = S, .separatorCount = ARRAY_SIZE(S) -/* for the obsolete --list-kinds option */ -extern void printKind (const kindDefinition* const kind, bool indent); +/* +* FUNCTION PROTOTYPES +*/ extern const char *scopeSeparatorFor (langType lang, int kindIndex, int parentKindIndex); -extern void enableKind (kindDefinition *kind, bool enable); - -struct kindControlBlock; -typedef void (* freeKindDefFunc) (kindDefinition *); -extern struct kindControlBlock* allocKindControlBlock (parserDefinition *parser); -extern void freeKindControlBlock (struct kindControlBlock* kcb); -extern int defineKind (struct kindControlBlock* kcb, kindDefinition *def, - freeKindDefFunc freeKindDef); -extern int defineRole (struct kindControlBlock* kcb, int kindIndex, - roleDefinition *def, freeRoleDefFunc freeRoleDef); -extern bool isRoleEnabled (struct kindControlBlock* kcb, int kindIndex, int roleIndex); - -extern unsigned int countKinds (struct kindControlBlock* kcb); -extern unsigned int countRoles (struct kindControlBlock* kcb, int kindIndex); -extern kindDefinition *getKind (struct kindControlBlock* kcb, int kindIndex); -extern kindDefinition *getKindForLetter (struct kindControlBlock* kcb, int letter); -extern kindDefinition *getKindForName (struct kindControlBlock* kcb, const char* name); -extern roleDefinition* getRole(struct kindControlBlock* kcb, int kindIndex, int roleIndex); -extern roleDefinition* getRoleForName(struct kindControlBlock* kcb, int kindIndex, const char* name); -extern void linkKindDependency (struct kindControlBlock *masterKCB, - struct kindControlBlock *slaveKCB); - -/* for --list-kinds-full option. LANGUAGE must be initialized. */ -extern struct colprintTable * kindColprintTableNew (void); -extern void kindColprintAddLanguageLines (struct colprintTable *table, - struct kindControlBlock* kcb); -extern void kindColprintTablePrint (struct colprintTable *table, bool noparser, - bool withListHeader, bool machinable, FILE *fp); - -extern struct colprintTable * roleColprintTableNew (void); -extern void roleColprintAddRoles (struct colprintTable *table, - struct kindControlBlock* kcb, - const char *kindspecs); -extern void roleColprintTablePrint (struct colprintTable *table, bool noparser, - bool withListHeader, bool machinable, FILE *fp); - -#ifdef DEBUG -extern bool doesParserUseKind (struct kindControlBlock* kcb, char letter); -#endif - #endif /* CTAGS_MAIN_KIND_H */ diff --git a/ctags/main/kind_p.h b/ctags/main/kind_p.h new file mode 100644 index 0000000000..d772ab1b9a --- /dev/null +++ b/ctags/main/kind_p.h @@ -0,0 +1,79 @@ +/* +* Copyright (c) 1998-2003, Darren Hiebert +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +*/ +#ifndef CTAGS_MAIN_KIND_PRIVATE_H +#define CTAGS_MAIN_KIND_PRIVATE_H + +/* +* INCLUDE FILES +*/ + +#include "general.h" +#include "vstring.h" + + +/* +* DATA DECLARATIONS +*/ + +struct kindControlBlock; +typedef void (* freeKindDefFunc) (kindDefinition *); +typedef void (* freeRoleDefFunc) (roleDefinition *); + + +/* +* FUNCTION PROTOTYPES +*/ +extern void enableKind (kindDefinition *kind, bool enable); +extern const char *renderRole (const roleDefinition* const def, vString* b); + +extern struct kindControlBlock* allocKindControlBlock (parserDefinition *parser); +extern void freeKindControlBlock (struct kindControlBlock* kcb); + +extern int defineKind (struct kindControlBlock* kcb, kindDefinition *def, + freeKindDefFunc freeKindDef); +extern int defineRole (struct kindControlBlock* kcb, int kindIndex, + roleDefinition *def, freeRoleDefFunc freeRoleDef); +extern bool isRoleEnabled (struct kindControlBlock* kcb, int kindIndex, int roleIndex); + +extern unsigned int countKinds (struct kindControlBlock* kcb); +extern unsigned int countRoles (struct kindControlBlock* kcb, int kindIndex); +extern kindDefinition *getKind (struct kindControlBlock* kcb, int kindIndex); +extern kindDefinition *getKindForLetter (struct kindControlBlock* kcb, int letter); +extern kindDefinition *getKindForName (struct kindControlBlock* kcb, const char* name); +extern roleDefinition* getRole(struct kindControlBlock* kcb, int kindIndex, int roleIndex); +extern roleDefinition* getRoleForName(struct kindControlBlock* kcb, int kindIndex, const char* name); +extern void linkKindDependency (struct kindControlBlock *masterKCB, + struct kindControlBlock *slaveKCB); + +extern int defineScopeSeparator(struct kindControlBlock* kcb, + int kindIndex, + int parentKindIndex, const char *separator); +extern const scopeSeparator *getScopeSeparator(struct kindControlBlock* kcb, int kindIndex, int parentKindIndex); + +/* for the obsolete --list-kinds option */ +extern void printKind (const kindDefinition* const kind, bool indent); + +/* for --list-kinds-full option. LANGUAGE must be initialized. */ +extern struct colprintTable * kindColprintTableNew (void); +extern void kindColprintAddLanguageLines (struct colprintTable *table, + struct kindControlBlock* kcb); +extern void kindColprintTablePrint (struct colprintTable *table, bool noparser, + bool withListHeader, bool machinable, FILE *fp); + +extern struct colprintTable * roleColprintTableNew (void); +extern void roleColprintAddRoles (struct colprintTable *table, + struct kindControlBlock* kcb, + const char *kindspecs); +extern void roleColprintTablePrint (struct colprintTable *table, bool noparser, + bool withListHeader, bool machinable, FILE *fp); + +#ifdef DEBUG +extern bool doesParserUseKind (struct kindControlBlock* kcb, char letter); +#endif + +#endif /* CTAGS_MAIN_KIND_PRIVATE_H */ diff --git a/ctags/main/lregex.c b/ctags/main/lregex.c index 55fe25bf56..594fc24f06 100644 --- a/ctags/main/lregex.c +++ b/ctags/main/lregex.c @@ -27,16 +27,19 @@ #include "debug.h" #include "colprint_p.h" -#include "entry.h" +#include "entry_p.h" +#include "field_p.h" #include "flags_p.h" #include "htable.h" #include "kind.h" #include "options.h" #include "parse_p.h" #include "read.h" +#include "read_p.h" #include "routines.h" #include "routines_p.h" #include "trashbox.h" +#include "xtag_p.h" static bool regexAvailable = false; @@ -131,6 +134,8 @@ typedef struct { char *pattern_string; + char *anonymous_tag_prefix; + struct { errorSelection selection; char *message_string; @@ -223,6 +228,9 @@ static void deletePattern (regexPattern *p) if (p->message.message_string) eFree (p->message.message_string); + if (p->anonymous_tag_prefix) + eFree (p->anonymous_tag_prefix); + eFree (p); } @@ -794,6 +802,31 @@ static void common_flag_role_long (const char* const s, const char* const v, voi ptrn->u.tag.roleBits |= makeRoleBit(role->id); } +static void common_flag_anonymous_long (const char* const s, const char* const v, void* data) +{ + struct commonFlagData * cdata = data; + regexPattern *ptrn = cdata->ptrn; + + Assert (ptrn); + + if (ptrn->anonymous_tag_prefix) + { + error (WARNING, "an anonymous tag prefix for this pattern (%s) is already given: %s", + ptrn->pattern_string? ptrn->pattern_string: "", + ptrn->anonymous_tag_prefix); + return; + } + + if (!v) + { + error (WARNING, "no PREFIX for anonymous regex flag is given (pattern == %s)", + ptrn->pattern_string? ptrn->pattern_string: ""); + return; + } + + ptrn->anonymous_tag_prefix = eStrdup (v); +} + static flagDefinition commonSpecFlagDef[] = { { '\0', "fatal", NULL, common_flag_msg_long , "\"MESSAGE\"", "print the given MESSAGE and exit"}, @@ -806,6 +839,8 @@ static flagDefinition commonSpecFlagDef[] = { "FIELD:VALUE", "record the matched string(VALUE) to parser own FIELD of the tag"}, { '\0', EXPERIMENTAL "role", NULL, common_flag_role_long, "ROLE", "set the given ROLE to the roles field"}, + { '\0', EXPERIMENTAL "anonymous", NULL, common_flag_anonymous_long, + "PREFIX", "make an anonymous tag with PREFIX"}, }; @@ -1157,8 +1192,13 @@ static void matchTagPattern (struct lregexControlBlock *lcb, const regmatch_t* const pmatch, off_t offset) { - vString *const name = substitute (line, - patbuf->u.tag.name_pattern, BACK_REFERENCE_COUNT, pmatch); + vString *const name = + (patbuf->u.tag.name_pattern[0] != '\0') ? substitute (line, + patbuf->u.tag.name_pattern, + BACK_REFERENCE_COUNT, pmatch): + (patbuf->anonymous_tag_prefix) ? anonGenerateNew (patbuf->anonymous_tag_prefix, + patbuf->u.tag.kindIndex): + vStringNewInit (""); bool placeholder = !!((patbuf->scopeActions & SCOPE_PLACEHOLDER) == SCOPE_PLACEHOLDER); unsigned long scope = CORK_NIL; int n; @@ -1257,6 +1297,10 @@ static void matchTagPattern (struct lregexControlBlock *lcb, assignRole (&e, roleIndex); } } + + if (patbuf->anonymous_tag_prefix) + markTagExtraBit (&e, XTAG_ANONYMOUS); + n = makeTagEntry (&e); trashBoxMakeEmpty(field_trashbox); @@ -1409,12 +1453,12 @@ static bool matchMultilineRegexPattern (struct lregexControlBlock *lcb, : pmatch [patbuf->mgroup.forNextScanning].rm_eo); if (delta == 0) { - unsigned int offset = current - start; + unsigned int pos = current - start; error (WARNING, "a multi line regex pattern doesn't advance the input cursor: %s", patbuf->pattern_string); error (WARNING, "Language: %s, input file: %s, pos: %u", - getLanguageName (lcb->owner), getInputFileName(), offset); + getLanguageName (lcb->owner), getInputFileName(), pos); break; } current += delta; @@ -1589,6 +1633,7 @@ static regexPattern *addTagRegexInternal (struct lregexControlBlock *lcb, if (*name == '\0') { if (rptr->exclusive || rptr->scopeActions & SCOPE_PLACEHOLDER + || rptr->anonymous_tag_prefix || regptype == REG_PARSER_MULTI_TABLE) rptr->accept_empty_name = true; else @@ -1735,7 +1780,7 @@ extern void processTagRegexOption (struct lregexControlBlock *lcb, if (vStringLength (regex) > 1 && vStringValue (regex)[0] != '\n') addTagRegexOption (lcb, regptype, vStringValue (regex)); } - mio_free (mio); + mio_unref (mio); vStringDelete (regex); } } diff --git a/ctags/main/lregex_p.h b/ctags/main/lregex_p.h index a39fa9fa61..40c3f2fac3 100644 --- a/ctags/main/lregex_p.h +++ b/ctags/main/lregex_p.h @@ -19,6 +19,7 @@ * INCLUDE FILES */ #include "general.h" +#include "kind_p.h" #include "lregex.h" /* diff --git a/ctags/main/lxpath.c b/ctags/main/lxpath.c index 69c9131d81..566f1566b3 100644 --- a/ctags/main/lxpath.c +++ b/ctags/main/lxpath.c @@ -14,6 +14,7 @@ #include "options.h" #include "parse_p.h" #include "read.h" +#include "read_p.h" #include "routines.h" #include "xtag.h" @@ -22,36 +23,43 @@ #include static void simpleXpathMakeTag (xmlNode *node, + const char *xpath, const tagXpathMakeTagSpec *spec, - const kindDefinition* const kinds, void *userData) { tagEntryInfo tag; xmlChar* str; char *path; + int kind; str = xmlNodeGetContent(node); if (str == NULL) return; + if (spec->kind == KIND_GHOST_INDEX && spec->decideKind) + kind = spec->decideKind (node, xpath, spec, userData); + else + kind = spec->kind; + Assert (kind != KIND_GHOST_INDEX); + if (spec->role == ROLE_INDEX_DEFINITION) - initTagEntry (&tag, (char *)str, spec->kind); + initTagEntry (&tag, (char *)str, kind); else if (isXtagEnabled(XTAG_REFERENCE_TAGS)) initRefTagEntry (&tag, (char *)str, - spec->kind, + kind, spec->role); else goto out; - tag.lineNumber = xmlGetLineNo (node); + tag.lineNumber = XML_GET_LINE (node); tag.filePosition = getInputFilePositionForLine (tag.lineNumber); path = (char *)xmlGetNodePath (node); tag.extensionFields.xpath = path; if (spec->make) - spec->make (node, spec, &tag, userData); + spec->make (node, xpath, spec, &tag, userData); else makeTagEntry (&tag); @@ -72,9 +80,18 @@ extern void addTagXpath (const langType language CTAGS_ATTR_UNUSED, tagXpathTabl error (WARNING, "Failed to compile the Xpath expression: %s", xpathTable->xpath); } +extern void removeTagXpath (const langType language CTAGS_ATTR_UNUSED, tagXpathTable *xpathTable) +{ + if (xpathTable->xpathCompiled) + { + xmlXPathFreeCompExpr (xpathTable->xpathCompiled); + xpathTable->xpathCompiled = NULL; + } +} + static void findXMLTagsCore (xmlXPathContext *ctx, xmlNode *root, const tagXpathTableTable *xpathTableTable, - const kindDefinition* const kinds,void *userData) + void *userData) { unsigned int i; int j; @@ -111,13 +128,13 @@ static void findXMLTagsCore (xmlXPathContext *ctx, xmlNode *root, if (set) { - for (j = 0; j < set->nodeNr; ++j) + for (j = 0; j < xmlXPathNodeSetGetLength (set); ++j) { - node = set->nodeTab[j]; + node = xmlXPathNodeSetItem(set, j); if (elt->specType == LXPATH_TABLE_DO_MAKE) - simpleXpathMakeTag (node, &(elt->spec.makeTagSpec), kinds, userData); + simpleXpathMakeTag (node, elt->xpath, &(elt->spec.makeTagSpec), userData); else - elt->spec.recurSpec.enter (node, &(elt->spec.recurSpec), ctx, userData); + elt->spec.recurSpec.enter (node, elt->xpath, &(elt->spec.recurSpec), ctx, userData); } } xmlXPathFreeObject (object); @@ -152,12 +169,16 @@ static xmlDocPtr makeXMLDoc (void) } extern void findXMLTags (xmlXPathContext *ctx, xmlNode *root, - const tagXpathTableTable *xpathTableTable, - const kindDefinition* const kinds,void *userData) + int tableTableIndex, + void *userData) { bool usedAsEntryPoint = false; xmlDocPtr doc = NULL; + const langType lang = getInputLanguage(); + const tagXpathTableTable *xpathTableTable + = getXpathTableTable (lang, tableTableIndex); + if (ctx == NULL) { usedAsEntryPoint = true; @@ -186,7 +207,7 @@ extern void findXMLTags (xmlXPathContext *ctx, xmlNode *root, } } - findXMLTagsCore (ctx, root, xpathTableTable, kinds, userData); + findXMLTagsCore (ctx, root, xpathTableTable, userData); out: if (usedAsEntryPoint) @@ -205,9 +226,13 @@ extern void addTagXpath (const langType language, tagXpathTable *xpathTable) xpathTable->xpathCompiled = NULL; } +extern void removeTagXpath (const langType language CTAGS_ATTR_UNUSED, tagXpathTable *xpathTable CTAGS_ATTR_UNUSED) +{ +} + extern void findXMLTags (xmlXPathContext *ctx, xmlNode *root, - const tagXpathTableTable *xpathTableTable, - const kindDefinition* const kinds, void *userData) + int tableTableIndex, + void *userData) { } diff --git a/ctags/main/lxpath.h b/ctags/main/lxpath.h index 23f4235f92..22164eb31f 100644 --- a/ctags/main/lxpath.h +++ b/ctags/main/lxpath.h @@ -10,6 +10,10 @@ #ifndef CTAGS_LXPATH_PARSE_H #define CTAGS_LXPATH_PARSE_H +/* +* INCLUDE FILES +*/ + #include "general.h" /* must always come first */ #include "types.h" @@ -22,18 +26,34 @@ #define xmlXPathContext void #endif + +/* +* DATA DECLARATIONS +*/ + typedef struct sTagXpathMakeTagSpec { + /* Kind used in making a tag. + If kind is KIND_GHOST_INDEX, a function + specified with decideKind is called to decide + the kind for the tag. */ int kind; int role; /* If make is NULL, just makeTagEntry is used instead. */ void (*make) (xmlNode *node, + const char *xpath, const struct sTagXpathMakeTagSpec *spec, tagEntryInfo *tag, void *userData); + int (*decideKind) (xmlNode *node, + const char *xpath, + const struct sTagXpathMakeTagSpec *spec, + void *userData); + /* TODO: decideRole */ } tagXpathMakeTagSpec; typedef struct sTagXpathRecurSpec { void (*enter) (xmlNode *node, + const char *xpath, const struct sTagXpathRecurSpec *spec, xmlXPathContext *ctx, void *userData); @@ -72,10 +92,14 @@ typedef struct sXpathFileSpec { const char *rootNSHref; } xpathFileSpec; + +/* +* FUNCTION PROTOTYPES +*/ + /* Xpath interface */ extern void findXMLTags (xmlXPathContext *ctx, xmlNode *root, - const tagXpathTableTable *xpathTableTable, - const kindDefinition* const kinds, void *userData); -extern void addTagXpath (const langType language, tagXpathTable *xpathTable); + int tableTableIndex, + void *userData); #endif /* CTAGS_LXPATH_PARSE_H */ diff --git a/ctags/main/lxpath_p.h b/ctags/main/lxpath_p.h new file mode 100644 index 0000000000..9bb73dafc0 --- /dev/null +++ b/ctags/main/lxpath_p.h @@ -0,0 +1,28 @@ +/* +* Copyright (c) 2016, Masatake YAMATO +* Copyright (c) 2016, Red Hat, Inc. +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* Xpath based parer API for the main part +*/ +#ifndef CTAGS_LXPATH_PARSE_PRIVATE_H +#define CTAGS_LXPATH_PARSE_PRIVATE_H + +/* +* INCLUDE FILES +*/ + +#include "general.h" /* must always come first */ +#include "types.h" + + +/* +* FUNCTION PROTOTYPES +*/ + +extern void addTagXpath (const langType language, tagXpathTable *xpathTable); +extern void removeTagXpath (const langType language, tagXpathTable *xpathTable); + +#endif /* CTAGS_LXPATH_PARSE_PRIVATE_H */ diff --git a/ctags/main/main.c b/ctags/main/main.c index c3542460ef..3808781e87 100644 --- a/ctags/main/main.c +++ b/ctags/main/main.c @@ -30,20 +30,6 @@ #include -/* To provide timings features if available. - */ -#ifdef HAVE_CLOCK -# ifdef HAVE_TIME_H -# include -# endif -#else -# ifdef HAVE_TIMES -# ifdef HAVE_SYS_TIMES_H -# include -# endif -# endif -#endif - /* To provide directory searching for recursion feature. */ @@ -64,17 +50,19 @@ #include "ctags.h" #include "debug.h" #include "entry_p.h" -#include "error.h" -#include "field.h" +#include "error_p.h" +#include "field_p.h" #include "keyword_p.h" -#include "main.h" +#include "main_p.h" #include "options_p.h" #include "parse_p.h" -#include "read.h" +#include "read_p.h" #include "routines_p.h" +#include "stats_p.h" #include "trace.h" -#include "trashbox.h" +#include "trashbox_p.h" #include "writer_p.h" +#include "xtag_p.h" #ifdef HAVE_JANSSON #include "interactive_p.h" @@ -82,15 +70,9 @@ #include #endif -/* -* MACROS -*/ -#define plural(value) (((unsigned long)(value) == 1L) ? "" : "s") - /* * DATA DEFINITIONS */ -static struct { long files, lines, bytes; } Totals = { 0, 0, 0 }; static mainLoopFunc mainLoop; static void *mainData; @@ -103,30 +85,6 @@ static bool createTagsForEntry (const char *const entryName); * FUNCTION DEFINITIONS */ -extern void addTotals ( - const unsigned int files, const long unsigned int lines, - const long unsigned int bytes) -{ - Totals.files += files; - Totals.lines += lines; - Totals.bytes += bytes; -} - -extern bool isDestinationStdout (void) -{ - bool toStdout = false; - - if (Option.filter || Option.interactive || - (Option.tagFileName != NULL && (strcmp (Option.tagFileName, "-") == 0 - || strcmp (Option.tagFileName, "/dev/stdout") == 0 - ))) - toStdout = true; - else if (Option.tagFileName == NULL && NULL == outputDefaultFileName ()) - toStdout = true; - - return toStdout; -} - #if defined (HAVE_OPENDIR) static bool recurseUsingOpendir (const char *const dirName) { @@ -354,69 +312,6 @@ static bool createTagsFromListFile (const char *const fileName) return resize; } -#if defined (HAVE_CLOCK) -# define CLOCK_AVAILABLE -# ifndef CLOCKS_PER_SEC -# define CLOCKS_PER_SEC 1000000 -# endif -#elif defined (HAVE_TIMES) -# define CLOCK_AVAILABLE -# define CLOCKS_PER_SEC 60 -static clock_t clock (void) -{ - struct tms buf; - - times (&buf); - return (buf.tms_utime + buf.tms_stime); -} -#else -# define clock() (clock_t)0 -#endif - -static void printTotals (const clock_t *const timeStamps) -{ - const unsigned long totalTags = numTagsTotal(); - const unsigned long addedTags = numTagsAdded(); - - fprintf (stderr, "%ld file%s, %ld line%s (%ld kB) scanned", - Totals.files, plural (Totals.files), - Totals.lines, plural (Totals.lines), - Totals.bytes/1024L); -#ifdef CLOCK_AVAILABLE - { - const double interval = ((double) (timeStamps [1] - timeStamps [0])) / - CLOCKS_PER_SEC; - - fprintf (stderr, " in %.01f seconds", interval); - if (interval != (double) 0.0) - fprintf (stderr, " (%lu kB/s)", - (unsigned long) (Totals.bytes / interval) / 1024L); - } -#endif - fputc ('\n', stderr); - - fprintf (stderr, "%lu tag%s added to tag file", - addedTags, plural(addedTags)); - if (Option.append) - fprintf (stderr, " (now %lu tags)", totalTags); - fputc ('\n', stderr); - - if (totalTags > 0 && Option.sorted != SO_UNSORTED) - { - fprintf (stderr, "%lu tag%s sorted", totalTags, plural (totalTags)); -#ifdef CLOCK_AVAILABLE - fprintf (stderr, " in %.02f seconds", - ((double) (timeStamps [2] - timeStamps [1])) / CLOCKS_PER_SEC); -#endif - fputc ('\n', stderr); - } - -#ifdef DEBUG - fprintf (stderr, "longest tag line = %lu\n", - (unsigned long) maxTagsLine ()); -#endif -} - static bool etagsInclude (void) { return (bool)(Option.etags && Option.etagsInclude != NULL); @@ -481,7 +376,7 @@ static void batchMakeTags (cookedArgs *args, void *user CTAGS_ATTR_UNUSED) timeStamp (2); if (Option.printTotals) - printTotals (timeStamps); + printTotals (timeStamps, Option.append, Option.sorted); #undef timeStamp } @@ -561,8 +456,8 @@ void interactiveLoop (cookedArgs *args CTAGS_ATTR_UNUSED, void *user) unsigned char *data = eMalloc (size); size = fread (data, 1, size, stdin); MIO *mio = mio_new_memory (data, size, eRealloc, eFree); - parseFileWithMio (filename, mio); - mio_free (mio); + parseFileWithMio (filename, mio, NULL); + mio_unref (mio); } closeTagFile (false); @@ -642,11 +537,7 @@ static void sanitizeEnviron (void) * Start up code */ -#ifndef GEANY_CTAGS_LIB -extern int main (int argc CTAGS_ATTR_UNUSED, char **argv) -#else -extern int ctags_main (int argc CTAGS_ATTR_UNUSED, char **argv) -#endif /* GEANY_CTAGS_LIB */ +extern int ctags_cli_main (int argc CTAGS_ATTR_UNUSED, char **argv) { cookedArgs *args; @@ -656,7 +547,7 @@ extern int ctags_main (int argc CTAGS_ATTR_UNUSED, char **argv) setErrorPrinter (stderrDefaultErrorPrinter, NULL); setMainLoop (batchMakeTags, NULL); - setTagWriter (WRITER_U_CTAGS); + setTagWriter (WRITER_U_CTAGS, NULL); setCurrentDirectory (); setExecutableName (*argv++); diff --git a/ctags/main/main.h b/ctags/main/main.h deleted file mode 100644 index da29e089a9..0000000000 --- a/ctags/main/main.h +++ /dev/null @@ -1,26 +0,0 @@ -/* -* Copyright (c) 1998-2002, Darren Hiebert -* -* This source code is released for free distribution under the terms of the -* GNU General Public License version 2 or (at your option) any later version. -* -* External interface to main.c -*/ -#ifndef CTAGS_MAIN_MAIN_H -#define CTAGS_MAIN_MAIN_H - -/* -* INCLUDE FILES -*/ -#include "general.h" /* must always come first */ - -#include - -/* -* FUNCTION PROTOTYPES -*/ -extern void addTotals (const unsigned int files, const long unsigned int lines, const long unsigned int bytes); -extern bool isDestinationStdout (void); -extern int main (int argc, char **argv); - -#endif /* CTAGS_MAIN_MAIN_H */ diff --git a/ctags/main/main_p.h b/ctags/main/main_p.h new file mode 100644 index 0000000000..3698da623c --- /dev/null +++ b/ctags/main/main_p.h @@ -0,0 +1,22 @@ +/* +* Copyright (c) 1998-2002, Darren Hiebert +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* Main part private interface to main.c +*/ +#ifndef CTAGS_MAIN_MAIN_PRIVATE_H +#define CTAGS_MAIN_MAIN_PRIVATE_H + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ + +/* +* FUNCTION PROTOTYPES +*/ +extern int ctags_cli_main (int argc, char **argv); + +#endif /* CTAGS_MAIN_MAIN_PRIVATE_H */ diff --git a/ctags/main/mbcs.c b/ctags/main/mbcs.c new file mode 100644 index 0000000000..902821a4ea --- /dev/null +++ b/ctags/main/mbcs.c @@ -0,0 +1,113 @@ +/* +* $Id$ +* +* Copyright (c) 2015, vim-jp +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* This module contains functions for checking multibyte character set. +*/ + +/* +* INCLUDE FILES +*/ +#ifndef __USE_GNU +# define __USE_GNU +#endif +#include "general.h" /* must always come first */ + +#ifdef HAVE_ICONV + +#include +#include +#include +#include +#include "options.h" +#include "mbcs.h" +#include "mbcs_p.h" +#include "routines.h" + +static iconv_t iconv_fd = (iconv_t) -1; + +extern bool openConverter (const char* inputEncoding, const char* outputEncoding) +{ + if (!inputEncoding || !outputEncoding) + { + static bool warn = false; + /* --output-encoding is specified but not --input-encoding provided */ + if (!warn && outputEncoding) + { + error (WARNING, "--input-encoding is not specified"); + warn = true; + } + return false; + } + iconv_fd = iconv_open(outputEncoding, inputEncoding); + if (iconv_fd == (iconv_t) -1) + { + error (FATAL, + "failed opening encoding from '%s' to '%s'", inputEncoding, outputEncoding); + return false; + } + return true; +} + +extern bool isConverting () +{ + return iconv_fd != (iconv_t) -1; +} + +extern bool convertString (vString *const string) +{ + size_t dest_len, src_len; + char *dest, *dest_ptr, *src; + if (iconv_fd == (iconv_t) -1) + return false; + src_len = vStringLength (string); + /* Should be longest length of bytes. so maybe utf8. */ + dest_len = src_len * 4; + dest_ptr = dest = xCalloc (dest_len, char); + if (!dest) + return false; + src = vStringValue (string); +retry: + if (iconv (iconv_fd, &src, &src_len, &dest_ptr, &dest_len) == (size_t) -1) + { + if (errno == EILSEQ) + { + *dest_ptr++ = '?'; + dest_len--; + src++; + src_len--; + verbose (" Encoding: %s\n", strerror(errno)); + goto retry; + } + eFree (dest); + return false; + } + + dest_len = dest_ptr - dest; + + vStringClear (string); + if (vStringSize (string) < dest_len + 1) + vStringResize (string, dest_len + 1); + memcpy (vStringValue (string), dest, dest_len + 1); + vStringLength (string) = dest_len; + eFree (dest); + + iconv (iconv_fd, NULL, NULL, NULL, NULL); + + return true; +} + +extern void closeConverter () +{ + if (iconv_fd != (iconv_t) -1) + { + iconv_close(iconv_fd); + iconv_fd = (iconv_t) -1; + } +} + +#endif /* HAVE_ICONV */ diff --git a/ctags/main/mbcs.h b/ctags/main/mbcs.h index ceb0d0966f..63b86500af 100644 --- a/ctags/main/mbcs.h +++ b/ctags/main/mbcs.h @@ -8,15 +8,21 @@ * * This module contains functions for checking multibyte character set. */ +#ifndef CTAGS_MAIN_MBCS_H +#define CTAGS_MAIN_MBCS_H +/* +* INCLUDE FILES +*/ #include "general.h" /* must always come first */ -#include "vstring.h" #ifdef HAVE_ICONV +/* +* FUNCTION PROTOTYPES +*/ extern bool isConverting (void); -extern bool openConverter (const char*, const char*); -extern bool convertString (vString *const); -extern void closeConverter (void); #endif /* HAVE_ICONV */ + +#endif /* CTAGS_MAIN_MBCS_H */ diff --git a/ctags/main/mbcs_p.h b/ctags/main/mbcs_p.h new file mode 100644 index 0000000000..c31b1cf0c7 --- /dev/null +++ b/ctags/main/mbcs_p.h @@ -0,0 +1,32 @@ +/* +* $Id$ +* +* Copyright (c) 2015, vim-jp +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* This module contains functions for checking multibyte character set. +*/ +#ifndef CTAGS_MAIN_MBCS_PRIVATE_H +#define CTAGS_MAIN_MBCS_PRIVATE_H + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ + +#include "vstring.h" + +#ifdef HAVE_ICONV + +/* +* FUNCTION PROTOTYPES +*/ +extern bool openConverter (const char*, const char*); +extern bool convertString (vString *const); +extern void closeConverter (void); + +#endif /* HAVE_ICONV */ + +#endif /* CTAGS_MAIN_MBCS_PRIVATE_H */ diff --git a/ctags/main/mini-geany.c b/ctags/main/mini-geany.c new file mode 100644 index 0000000000..8dc324e305 --- /dev/null +++ b/ctags/main/mini-geany.c @@ -0,0 +1,346 @@ +/* +* Copyright (c) 2019, Jiri Techet +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* Provides a simple application using ctags as a library and using the same +* set of ctags functions as the Geany editor +*/ + +#include "general.h" /* must always come first */ + +#include "types.h" +#include "routines.h" +#include "mio.h" +#include "error_p.h" +#include "writer_p.h" +#include "parse_p.h" +#include "options_p.h" +#include "trashbox_p.h" +#include "field_p.h" +#include "xtag_p.h" +#include "entry_p.h" + +#include +#include +#include + +static int writeEntry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag, void *clientData); +static void rescanFailed (tagWriter *writer, unsigned long validTagNum, void *clientData); + +/* we need to be able to provide a custom writer using which we collect the tags */ +tagWriter customWriter = { + .writeEntry = writeEntry, + .writePtagEntry = NULL, + .preWriteEntry = NULL, + .postWriteEntry = NULL, + .rescanFailedEntry = rescanFailed, + .treatFieldAsFixed = NULL, + .defaultFileName = "tags_file_which_should_never_appear_anywhere", + .private = NULL, +}; + + +/* we need to be able to provide an error printer which doesn't crash Geany on error */ +static bool nofatalErrorPrinter (const errorSelection selection, + const char *const format, + va_list ap, void *data CTAGS_ATTR_UNUSED) +{ + fprintf (stderr, "%s: ", (selection & WARNING) ? "Warning: " : "Error"); + vfprintf (stderr, format, ap); + if (selection & PERROR) +#ifdef HAVE_STRERROR + fprintf (stderr, " : %s", strerror (errno)); +#else + perror (" "); +#endif + fputs ("\n", stderr); + + return false; +} + + +/* we need to be able to enable all kinds for all languages (some are disabled by default) */ +static void enableAllLangKinds() +{ + unsigned int lang; + + for (lang = 0; lang < countParsers(); lang++) + { + unsigned int kindNum = countLanguageKinds(lang); + unsigned int kind; + + for (kind = 0; kind < kindNum; kind++) + { + kindDefinition *def = getLanguageKind(lang, kind); + enableKind(def, true); + } + } +} + + +/* we need to be able to initialize ctags like in main() but skipping some things */ +static void ctagsInit(void) +{ + initDefaultTrashBox (); + + setErrorPrinter (nofatalErrorPrinter, NULL); + setTagWriter (WRITER_CUSTOM, &customWriter); + + checkRegex (); + initFieldObjects (); + initXtagObjects (); + + initializeParsing (); + initOptions (); + + /* make sure all parsers are initialized */ + initializeParser (LANG_AUTO); + + /* change default value which is false */ + enableXtag(XTAG_TAGS_GENERATED_BY_GUEST_PARSERS, true); + enableXtag(XTAG_REFERENCE_TAGS, true); + + /* some kinds we are interested in are disabled by default */ + enableAllLangKinds(); +} + + +/* we need to be able to get a name of a given language */ +static const char *ctagsGetLangName(int lang) +{ + return getLanguageName(lang); +} + + +/* we need to be able to get an int representing a given language */ +static int ctagsGetNamedLang(const char *name) +{ + return getNamedLanguage(name, 0); +} + + +/* we need to be able to get kind letters provided by a given language */ +static const char *ctagsGetLangKinds(int lang) +{ + unsigned int kindNum = countLanguageKinds(lang); + static char kinds[257]; + unsigned int i; + + for (i = 0; i < kindNum; i++) + kinds[i] = getLanguageKind(lang, i)->letter; + kinds[i] = '\0'; + + return kinds; +} + + +/* we need to be able to get kind name from a kind letter for a given language */ +static const char *ctagsGetKindName(char kind, int lang) +{ + kindDefinition *def = getLanguageKindForLetter (lang, kind); + return def ? def->name : "unknown"; +} + + +/* we need to be able to get kind letter from a kind name for a given language */ +static char ctagsGetKindFromName(const char *name, int lang) +{ + kindDefinition *def = getLanguageKindForName (lang, name); + return def ? def->letter : '-'; +} + + +/* we need to be able to get kind letter from a kind index */ +static char ctagsGetKindFromIndex(int index, int lang) +{ + return getLanguageKind(lang, index)->letter; +} + + +/* we need to be able to get the number of parsers */ +static unsigned int ctagsGetLangCount(void) +{ + return countParsers(); +} + +/******************************************************************************* + * So let's just use what we have for our great client... + ******************************************************************************/ + +/* our internal tag representation - this is all the tag information we use in Geany */ +typedef struct { + char *name; + char *signature; + char *scopeName; + char *inheritance; + char *varType; + char *access; + char *implementation; + char kindLetter; + bool isFileScope; + unsigned long lineNumber; + int lang; +} Tag; + + +static Tag *createTag(const tagEntryInfo *info) +{ + Tag *tag = xCalloc(1, Tag); + if (info->name) + tag->name = eStrdup(info->name); + if (info->extensionFields.signature) + tag->signature = eStrdup(info->extensionFields.signature); + if (info->extensionFields.scopeName) + tag->scopeName = eStrdup(info->extensionFields.scopeName); + if (info->extensionFields.inheritance) + tag->inheritance = eStrdup(info->extensionFields.inheritance); + if (info->extensionFields.typeRef[1]) + tag->varType = eStrdup(info->extensionFields.typeRef[1]); + if (info->extensionFields.access) + tag->access = eStrdup(info->extensionFields.access); + if (info->extensionFields.implementation) + tag->implementation = eStrdup(info->extensionFields.implementation); + tag->kindLetter = ctagsGetKindFromIndex(info->kindIndex, info->langType); + tag->isFileScope = info->isFileScope; + tag->lineNumber = info->lineNumber; + tag->lang = info->langType; + return tag; +} + + +static void destroyTag(Tag *tag) +{ + if (tag->name) + eFree(tag->name); + if (tag->signature) + eFree(tag->signature); + if (tag->scopeName) + eFree(tag->scopeName); + if (tag->inheritance) + eFree(tag->inheritance); + if (tag->varType) + eFree(tag->varType); + if (tag->access) + eFree(tag->access); + if (tag->implementation) + eFree(tag->implementation); + eFree(tag); +} + + +/* callback from ctags informing us about a new tag */ +static int writeEntry (tagWriter *writer, MIO *mio, const tagEntryInfo *const tag, void *clientData) +{ + Tag *t; + + /* apparently we have to call this to get the scope info - maybe we can + * specify some option during initialization so we don't have to cal this + * ?????? */ + getTagScopeInformation((tagEntryInfo *)tag, NULL, NULL); + + /* convert tags into our internal representation and store them into an array */ + t = createTag(tag); + ptrArrayAdd((ptrArray *)clientData, t); + + /* output length - we don't write anything to the MIO */ + return 0; +} + + +/* scan has failed so we have invalid tags in our array - validTagNum should + * tell us the number of valid tags so remove all the extra tags and shrink the array */ +static void rescanFailed (tagWriter *writer, unsigned long validTagNum, void *clientData) +{ + ptrArray *tagArray = clientData; + int num = ptrArrayCount(tagArray); + + if (num > validTagNum) + { + int i; + for (i = validTagNum; i < num; i++) + { + Tag *tag = ptrArrayLast(tagArray); + destroyTag(tag); + ptrArrayRemoveLast(tagArray); + } + } +} + + +/* do whatever we want to do with the tags */ +static void processCollectedTags(ptrArray *tagArray) +{ + int i; + int num = ptrArrayCount(tagArray); + + for (i = 0; i < num; i++) + { + Tag *tag = ptrArrayItem(tagArray, i); + printf("%s\tline: %lu\tkind: %s\t lang: %s\n", + tag->name, + tag->lineNumber, + ctagsGetKindName(tag->kindLetter, tag->lang), + ctagsGetLangName(tag->lang)); + } + + /* prepare for the next parsing by clearing the tag array */ + ptrArrayClear(tagArray); +} + + +extern int main (int argc, char **argv) +{ + /* called once when Geany starts */ + ctagsInit(); + + /* create empty tag array * + * this is where we store the collected tags + * NOTE: Geany doesn't use the ptrArray type - it is used just for the purpose of this demo */ + ptrArray *tagArray = ptrArrayNew((ptrArrayDeleteFunc)destroyTag); + + printf("This parser only parses C files - provide them as arguments on the " + "command line or get a hard-coded buffer parsed when no arguments " + "are provided\n\n"); + if (argc == 1) /* parsing contents of a buffer */ + { + char *program = "int foo() {}\n\n int bar() {}\n\n int main() {}\n"; + int lang = ctagsGetNamedLang("C"); + const char *kinds = ctagsGetLangKinds(lang); + int i; + + printf("Number of all parsers is: %d\n", ctagsGetLangCount()); + printf("We are parsing %s which provides the following kinds:\n", + ctagsGetLangName(lang)); + for (i = 0; kinds[i] != '\0'; i++) + { + printf("%c: %s\n", + /* back and forth conversion - the same like just kinds[i] */ + ctagsGetKindFromName(ctagsGetKindName(kinds[i], lang), lang), + ctagsGetKindName(kinds[i], lang)); + } + + printf("\nParsing buffer:\n"); + /* we always specify the language by ourselves and don't use ctags detection */ + parseRawBuffer("whatever", (unsigned char *)program, strlen(program), lang, tagArray); + + processCollectedTags(tagArray); + } + else /* parsing contents of a file */ + { + int i; + for (i = 1; i < argc; i++) + { + printf("\nParsing %s:\n", argv[i]); + /* parseRawBuffer() is called repeatadly during Geany execution */ + parseRawBuffer(argv[i], NULL, 0, getNamedLanguage("C", 0), tagArray); + + processCollectedTags(tagArray); + } + } + + ptrArrayDelete(tagArray); + + return 0; +} diff --git a/ctags/main/mio.c b/ctags/main/mio.c index 1e924cdb4c..229d469f25 100644 --- a/ctags/main/mio.c +++ b/ctags/main/mio.c @@ -18,7 +18,7 @@ * */ -#ifndef QUALIFIER +#ifndef READTAGS_DSL #include "general.h" /* must always come first */ #include "routines.h" @@ -29,10 +29,10 @@ #include #endif -#ifdef USE_STDBOOL_H +#ifdef HAVE_STDBOOL_H #include #endif -#endif +#endif /* READTAGS_DSL */ #include "mio.h" @@ -43,9 +43,8 @@ #include #include -#ifdef QUALIFIER +#ifdef READTAGS_DSL #define xMalloc(n,Type) (Type *)eMalloc((size_t)(n) * sizeof (Type)) -#define xCalloc(n,Type) (Type *)eCalloc((size_t)(n), sizeof (Type)) #define xRealloc(p,n,Type) (Type *)eRealloc((p), (n) * sizeof (Type)) static void *eMalloc (const size_t size) @@ -61,19 +60,6 @@ static void *eMalloc (const size_t size) return buffer; } -static void *eCalloc (const size_t count, const size_t size) -{ - void *buffer = calloc (count, size); - - if (buffer == NULL) - { - fprintf(stderr, "out of memory"); - abort (); - } - - return buffer; -} - static void *eRealloc (void *const ptr, const size_t size) { void *buffer; @@ -98,7 +84,7 @@ static void eFree (void *const ptr) # define Assert(c) do {} while(0) # define AssertNotReached() do {} while(0) -#endif +#endif /* READTAGS_DSL */ /* minimal reallocation chunk size */ #define MIO_CHUNK_SIZE 4096 @@ -118,9 +104,9 @@ static void eFree (void *const ptr) * A #MIO object is created using mio_new_file(), mio_new_memory() or mio_new_mio(), * depending on whether you want file or in-memory operations. * Its life is managed by reference counting. Just after calling one of functions - * for creating, the count is 1. mio_ref() increments the counter. mio_free() + * for creating, the count is 1. mio_ref() increments the counter. mio_unref() * decrements it. When the counter becomes 0, the #MIO object will be destroyed - * in mio_free(). There is also some other convenient API to create file-based + * in mio_unref(). There is also some other convenient API to create file-based * #MIO objects for more complex cases, such as mio_new_file_full() and * mio_new_fp(). * @@ -189,10 +175,10 @@ struct _MIO { * as a close function. The former is useful e.g. if you need to wrap fopen() * for some reason (like filename encoding conversion for example), and the * latter allows you both to match your custom open function and to choose - * whether the underlying #FILE object should or not be closed when mio_free() + * whether the underlying #FILE object should or not be closed when mio_unref() * is called on the returned object. * - * Free-function: mio_free() + * Free-function: mio_unref() * * Returns: A new #MIO on success, or %NULL on failure. */ @@ -240,7 +226,7 @@ MIO *mio_new_file_full (const char *filename, * This function simply calls mio_new_file_full() with the libc's fopen() and * fclose() functions. * - * Free-function: mio_free() + * Free-function: mio_unref() * * Returns: A new #MIO on success, or %NULL on failure. */ @@ -265,7 +251,7 @@ MIO *mio_new_file (const char *filename, const char *mode) * * * - * Free-function: mio_free() + * Free-function: mio_unref() * * Returns: A new #MIO on success or %NULL on failure. */ @@ -322,7 +308,7 @@ MIO *mio_new_fp (FILE *fp, MIOFCloseFunc close_func) * * * - * Free-function: mio_free() + * Free-function: mio_unref() * * Returns: A new #MIO on success, or %NULL on failure. */ @@ -370,7 +356,7 @@ MIO *mio_new_memory (unsigned char *data, * * The function doesn't move the file position of @base. * - * Free-function: mio_free() + * Free-function: mio_unref() * */ @@ -436,7 +422,7 @@ MIO *mio_ref (MIO *mio) * Gets the underlying #FILE object associated with a #MIO file stream. * * The returned object may become invalid after a call to - * mio_free() if the stream was configured to close the file when + * mio_unref() if the stream was configured to close the file when * destroyed. * * Returns: The underlying #FILE object of the given stream, or %NULL if the @@ -461,7 +447,7 @@ FILE *mio_file_get_fp (MIO *mio) * Gets the underlying memory buffer associated with a #MIO memory stream. * * The returned pointer and size may become invalid after a - * successful write on the stream or after a call to mio_free() if the stream + * successful write on the stream or after a call to mio_unref() if the stream * was configured to free the memory when destroyed. * * Returns: The memory buffer of the given #MIO stream, or %NULL if the stream @@ -482,7 +468,7 @@ unsigned char *mio_memory_get_data (MIO *mio, size_t *size) } /** - * mio_free: + * mio_unref: * @mio: A #MIO object * * Decrements the reference counter of a #MIO and destroys the #MIO @@ -490,7 +476,7 @@ unsigned char *mio_memory_get_data (MIO *mio, size_t *size) * * Returns: Error code obtained from the registered MIOFCloseFunc or 0 on success. */ -int mio_free (MIO *mio) +int mio_unref (MIO *mio) { int rv = 0; diff --git a/ctags/main/mio.h b/ctags/main/mio.h index b9186f788b..7f0d537fc6 100644 --- a/ctags/main/mio.h +++ b/ctags/main/mio.h @@ -126,7 +126,7 @@ MIO *mio_new_memory (unsigned char *data, MIO *mio_new_mio (MIO *base, long start, long size); MIO *mio_ref (MIO *mio); -int mio_free (MIO *mio); +int mio_unref (MIO *mio); FILE *mio_file_get_fp (MIO *mio); unsigned char *mio_memory_get_data (MIO *mio, size_t *size); size_t mio_read (MIO *mio, diff --git a/ctags/main/nestlevel.c b/ctags/main/nestlevel.c index feba045bdf..d762179811 100644 --- a/ctags/main/nestlevel.c +++ b/ctags/main/nestlevel.c @@ -13,7 +13,6 @@ */ #include "general.h" /* must always come first */ -#include "main.h" #include "debug.h" #include "entry.h" #include "routines.h" diff --git a/ctags/main/nestlevel.h b/ctags/main/nestlevel.h index a95236fb80..a209ae706c 100644 --- a/ctags/main/nestlevel.h +++ b/ctags/main/nestlevel.h @@ -20,6 +20,7 @@ /* * DATA DECLARATIONS */ +typedef struct NestingLevel NestingLevel; typedef struct NestingLevels NestingLevels; struct NestingLevel diff --git a/ctags/main/numarray.c b/ctags/main/numarray.c index b6c8619cc7..31c7ba44c4 100644 --- a/ctags/main/numarray.c +++ b/ctags/main/numarray.c @@ -167,9 +167,9 @@ } /* We expect the linker we use is enough clever to delete dead code. */ -impNumArray(char, Char, char); -impNumArray(uchar, Uchar, unsigned char); -impNumArray(int, Int, int); -impNumArray(uint, Uint, unsigned int); -impNumArray(long, Long, long); -impNumArray(ulong, Ulong, unsigned long); +impNumArray(char, Char, char) +impNumArray(uchar, Uchar, unsigned char) +impNumArray(int, Int, int) +impNumArray(uint, Uint, unsigned int) +impNumArray(long, Long, long) +impNumArray(ulong, Ulong, unsigned long) diff --git a/ctags/main/numarray.h b/ctags/main/numarray.h index 318931aec9..4dbe35d8f4 100644 --- a/ctags/main/numarray.h +++ b/ctags/main/numarray.h @@ -38,11 +38,11 @@ \ extern void prefix##ArraySort (prefix##Array *const current, bool descendingOrder); -declNumArray(char, Char, char); -declNumArray(uchar, Uchar, unsigned char); -declNumArray(int, Int, int); -declNumArray(uint, Uint, unsigned int); -declNumArray(long, Long, long); -declNumArray(ulong, Ulong, unsigned long); +declNumArray(char, Char, char) +declNumArray(uchar, Uchar, unsigned char) +declNumArray(int, Int, int) +declNumArray(uint, Uint, unsigned int) +declNumArray(long, Long, long) +declNumArray(ulong, Ulong, unsigned long) #endif /* CTAGS_MAIN_NUMARRAY_H */ diff --git a/ctags/main/options.c b/ctags/main/options.c index e0c574a937..497c8648c9 100644 --- a/ctags/main/options.c +++ b/ctags/main/options.c @@ -25,16 +25,15 @@ #include "ctags.h" #include "debug.h" -#include "field.h" +#include "field_p.h" #include "gvars.h" -#include "keyword.h" -#include "main.h" +#include "keyword_p.h" #include "parse_p.h" #include "ptag_p.h" #include "routines_p.h" -#include "xtag.h" -#include "param.h" -#include "error.h" +#include "xtag_p.h" +#include "param_p.h" +#include "error_p.h" #include "interactive_p.h" #include "writer_p.h" #include "trace.h" @@ -195,6 +194,7 @@ static OptionLoadingStage Stage = OptionLoadingStageNone; */ static optionDescription LongOptionDescription [] = { + {1," -? Print this option summary."}, {1," -a Append the tags to an existing tag file."}, #ifdef DEBUG {1," -b "}, @@ -242,7 +242,7 @@ static optionDescription LongOptionDescription [] = { {1," Include reference to 'file' in Emacs-style tag file (requires -e)."}, {1," --exclude=pattern"}, {1," Exclude files and directories matching 'pattern'."}, - {0," --excmd=number|pattern|mix"}, + {0," --excmd=number|pattern|mix|combine"}, #ifdef MACROS_USE_PATTERNS {0," Uses the specified type of EX command to locate tags [pattern]."}, #else @@ -250,12 +250,12 @@ static optionDescription LongOptionDescription [] = { #endif {1," --extras=[+|-]flags"}, {1," Include extra tag entries for selected information (flags: \"fFgpqrs\") [F]."}, - {1," --extras-=[+|-]flags"}, + {1," --extras-=[+|-]flags"}, {1," Include own extra tag entries for selected information"}, {1," (flags: see the output of --list-extras= option)."}, {1," --fields=[+|-]flags"}, {1," Include selected extension fields (flags: \"aCeEfFikKlmnNpPrRsStxzZ\") [fks]."}, - {1," --fields-=[+|-]flags"}, + {1," --fields-=[+|-]flags"}, {1," Include selected own extension fields"}, {1," (flags: see the output of --list-fields= option)."}, {1," --file-scope=[yes|no]"}, @@ -473,8 +473,13 @@ static optionDescription ExperimentalLongOptionDescription [] = { {1," Define multitable regular expression for locating tags in specific language."}, {1," --_mtable-totals=[yes|no]"}, {1," Print statistics about mtable usage [no]."}, + {1," --_pretend-="}, + {1," Make NEWLANG parser pretend OLDLANG parser in lang: field."}, {1," --_roledef-=kind_letter.role_name,role_desc"}, {1," Define new role for kind specified with in ."}, + {1," --_scopesep-=[parent_kind_letter]/child_kind_letter:separator"}, + {1," Specify scope separator between and ."}, + {1," * as a kind letter matches any kind."}, {1," --_tabledef-=name"}, {1," Define new regex table for ."}, #ifdef DO_TRACING @@ -557,6 +562,10 @@ static struct Feature { #endif #ifdef HAVE_ASPELL {"aspell", "linked with code for spell checking (internal use)"}, +#endif +#ifdef HAVE_PACKCC + /* The test harnesses use this as hints for skipping test cases */ + {"packcc", "has peg based parser(s)"}, #endif {NULL,} }; @@ -745,8 +754,9 @@ extern void checkOptions (void) } } -extern langType getLanguageComponentInOption (const char *const option, - const char *const prefix) +extern langType getLanguageComponentInOptionFull (const char *const option, + const char *const prefix, + bool noPretending) { size_t prefix_len; langType language; @@ -771,20 +781,26 @@ extern langType getLanguageComponentInOption (const char *const option, colon = strchr (lang, ':'); if (colon) lang_len = colon - lang; - language = getNamedLanguage (lang, lang_len); + language = getNamedLanguageFull (lang, lang_len, noPretending); if (language == LANG_IGNORE) error (FATAL, "Unknown language \"%s\" in \"%s\" option", lang, option); return language; } +extern langType getLanguageComponentInOption (const char *const option, + const char *const prefix) +{ + return getLanguageComponentInOptionFull (option, prefix, false); +} + static void setEtagsMode (void) { Option.etags = true; Option.sorted = SO_UNSORTED; Option.lineDirectives = false; Option.tagRelative = TREL_YES; - setTagWriter (WRITER_ETAGS); + setTagWriter (WRITER_ETAGS, NULL); } extern void testEtagsInvocation (void) @@ -807,7 +823,7 @@ extern void testEtagsInvocation (void) static void setXrefMode (void) { Option.xref = true; - setTagWriter (WRITER_XREF); + setTagWriter (WRITER_XREF, NULL); } #ifdef HAVE_JANSSON @@ -816,7 +832,7 @@ static void setJsonMode (void) enablePtag (PTAG_JSON_OUTPUT_VERSION, true); enablePtag (PTAG_OUTPUT_MODE, false); enablePtag (PTAG_FILE_FORMAT, false); - setTagWriter (WRITER_JSON); + setTagWriter (WRITER_JSON, NULL); } #endif @@ -1164,7 +1180,10 @@ static void processExcmdOption ( case 'n': Option.locate = EX_LINENUM; break; case 'p': Option.locate = EX_PATTERN; break; default: - error (FATAL, "Invalid value for \"%s\" option", option); + if (strcmp(parameter, "combine") == 0) + Option.locate = EX_COMBINE; + else + error (FATAL, "Invalid value for \"%s\" option", option); break; } } @@ -1607,7 +1626,7 @@ static void processInteractiveOption ( Option.sorted = SO_UNSORTED; setMainLoop (interactiveLoop, &args); setErrorPrinter (jsonErrorPrinter, NULL); - setTagWriter (WRITER_JSON); + setTagWriter (WRITER_JSON, NULL); enablePtag (PTAG_JSON_OUTPUT_VERSION, true); json_set_alloc_funcs (eMalloc, eFree); @@ -2318,7 +2337,7 @@ static void processOutputFormat (const char *const option CTAGS_ATTR_UNUSED, if (strcmp (parameter, "u-ctags") == 0) ; else if (strcmp (parameter, "e-ctags") == 0) - setTagWriter (WRITER_E_CTAGS); + setTagWriter (WRITER_E_CTAGS, NULL); else if (strcmp (parameter, "etags") == 0) setEtagsMode (); else if (strcmp (parameter, "xref") == 0) @@ -3181,6 +3200,10 @@ static void processLongOption ( #endif else if (processRoledefOption (option, parameter)) ; + else if (processScopesepOption (option, parameter)) + ; + else if (processPretendOption (option, parameter)) + ; else error (FATAL, "Unknown option: --%s", option); } @@ -3746,3 +3769,18 @@ extern bool canUseLineNumberAsLocator (void) { return (Option.locate != EX_PATTERN); } + +extern bool isDestinationStdout (void) +{ + bool toStdout = false; + + if (Option.filter || Option.interactive || + (Option.tagFileName != NULL && (strcmp (Option.tagFileName, "-") == 0 + || strcmp (Option.tagFileName, "/dev/stdout") == 0 + ))) + toStdout = true; + else if (Option.tagFileName == NULL && NULL == outputDefaultFileName ()) + toStdout = true; + + return toStdout; +} diff --git a/ctags/main/options.h b/ctags/main/options.h index 5bb0e87aab..bd0f6fde5b 100644 --- a/ctags/main/options.h +++ b/ctags/main/options.h @@ -14,7 +14,6 @@ */ #include "general.h" /* must always come first */ #include "gvars.h" -#include "keyword_p.h" #include diff --git a/ctags/main/options_p.h b/ctags/main/options_p.h index f6a8b044fe..2e9afe5640 100644 --- a/ctags/main/options_p.h +++ b/ctags/main/options_p.h @@ -53,7 +53,8 @@ typedef struct sCookedArgs { typedef enum eLocate { EX_MIX, /* line numbers for defines, patterns otherwise */ EX_LINENUM, /* -n only line numbers in tag file */ - EX_PATTERN /* -N only patterns in tag file */ + EX_PATTERN, /* -N only patterns in tag file */ + EX_COMBINE, /* Combine linenum and pattern with `;'*/ } exCmd; typedef enum sortType { @@ -163,6 +164,8 @@ extern void freeOptionResources (void); extern langType getLanguageComponentInOption (const char *const option, const char *const prefix); +extern langType getLanguageComponentInOptionFull (const char *const option, + const char *const prefix, bool noPretending); extern void processLanguageDefineOption (const char *const option, const char *const parameter); extern bool processMapOption (const char *const option, const char *const parameter); @@ -177,6 +180,10 @@ extern bool processTabledefOption (const char *const option, const char *const p extern bool processLanguageEncodingOption (const char *const option, const char *const parameter); #endif extern bool processRoledefOption (const char *const option, const char *const parameter); +extern bool processScopesepOption (const char *const option, const char *const parameter); +extern bool processPretendOption (const char *const option, const char *const parameter); + +extern bool isDestinationStdout (void); extern void setMainLoop (mainLoopFunc func, void *data); diff --git a/ctags/main/param.c b/ctags/main/param.c index 2a24b61c83..cea6fe1dd9 100644 --- a/ctags/main/param.c +++ b/ctags/main/param.c @@ -12,6 +12,7 @@ #include "general.h" #include "param.h" +#include "param_p.h" #include "parse.h" #include diff --git a/ctags/main/param.h b/ctags/main/param.h index 244d485078..bbba6be3fe 100644 --- a/ctags/main/param.h +++ b/ctags/main/param.h @@ -12,26 +12,26 @@ #ifndef CTAGS_MAIN_PARAM_H #define CTAGS_MAIN_PARAM_H +/* +* INCLUDE FILES +*/ #include "general.h" -#include "colprint_p.h" #include "types.h" + +/* +* DATA DECLARATIONS +*/ struct sParameterHandlerTable { const char *name; const char *desc; void (* handleParameter) (langType lang, const char *name, const char *arg); }; -extern void applyParameter (const langType language, const char *name, const char *args); - -extern struct colprintTable * paramColprintTableNew (void); -extern void paramColprintAddParameter (struct colprintTable *table, - langType language, - const parameterHandlerTable *const paramHandler); -extern void paramColprintTablePrint (struct colprintTable *table, bool noparser, - bool withListHeader, bool machinable, FILE *fp); - +/* +* FUNCTION PROTOTYPES +*/ extern bool paramParserBool (const char *value, bool fallback, const char *errWhat, const char *errCategory); diff --git a/ctags/main/param_p.h b/ctags/main/param_p.h new file mode 100644 index 0000000000..0205a7d165 --- /dev/null +++ b/ctags/main/param_p.h @@ -0,0 +1,36 @@ +/* + * + * Copyright (c) 2016, Red Hat, Inc. + * Copyright (c) 2016, Masatake YAMATO + * + * Author: Masatake YAMATO + * + * This source code is released for free distribution under the terms of the + * GNU General Public License version 2 or (at your option) any later version. + * + */ +#ifndef CTAGS_MAIN_PARAM_PRIVATE_H +#define CTAGS_MAIN_PARAM_PRIVATE_H + +/* +* INCLUDE FILES +*/ +#include "general.h" + +#include "types.h" +#include "colprint_p.h" + + +/* +* FUNCTION PROTOTYPES +*/ +extern void applyParameter (const langType language, const char *name, const char *args); + +extern struct colprintTable * paramColprintTableNew (void); +extern void paramColprintAddParameter (struct colprintTable *table, + langType language, + const parameterHandlerTable *const paramHandler); +extern void paramColprintTablePrint (struct colprintTable *table, bool noparser, + bool withListHeader, bool machinable, FILE *fp); + +#endif /* CTAGS_MAIN_PARAM_PRIVATE_H */ diff --git a/ctags/main/parse.c b/ctags/main/parse.c index fac8393868..c12f86628d 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -22,28 +22,35 @@ #include "ctags.h" #include "debug.h" #include "entry_p.h" +#include "field_p.h" #include "flags_p.h" #include "htable.h" #include "keyword.h" -#include "main.h" +#include "lxpath_p.h" #include "param.h" +#include "param_p.h" #include "parse_p.h" -#include "parsers.h" +#include "parsers_p.h" #include "promise.h" #include "promise_p.h" #include "ptag_p.h" #include "ptrarray.h" #include "read.h" +#include "read_p.h" #include "routines.h" #include "routines_p.h" +#include "stats_p.h" #include "subparser.h" +#include "subparser_p.h" #include "trace.h" #include "trashbox.h" +#include "trashbox_p.h" #include "vstring.h" #ifdef HAVE_ICONV -# include "mbcs.h" +# include "mbcs_p.h" #endif -#include "xtag.h" +#include "writer_p.h" +#include "xtag_p.h" /* * DATA TYPES @@ -85,6 +92,14 @@ typedef struct sParserObject { struct slaveControlBlock *slaveControlBlock; struct kindControlBlock *kindControlBlock; struct lregexControlBlock *lregexControlBlock; + + langType pretendingAsLanguage; /* OLDLANG in --_pretend-= + is set here if this parser is NEWLANG. + LANG_IGNORE is set if no pretending. */ + langType pretendedAsLanguage; /* NEWLANG in --_pretend-= + is set here if this parser is OLDLANG. + LANG_IGNORE is set if no being pretended. */ + } parserObject; /* @@ -99,6 +114,7 @@ static void installTagXpathTable (const langType language); static void anonResetMaybe (parserObject *parser); static void setupAnon (void); static void teardownAnon (void); +static void uninstallTagXpathTable (const langType language); /* * DATA DEFINITIONS @@ -114,6 +130,10 @@ static parserDefinitionFunc* BuiltInParsers[] = { YAML_PARSER_LIST #ifdef HAVE_LIBYAML , +#endif + PEG_PARSER_LIST +#ifdef HAVE_PACKCC + , #endif }; static parserObject* LanguageTable = NULL; @@ -218,19 +238,37 @@ extern bool doesLanguageRequestAutomaticFQTag (const langType language) return LanguageTable [language].def->requestAutomaticFQTag; } -extern const char *getLanguageName (const langType language) +static const char *getLanguageNameFull (const langType language, bool noPretending) { const char* result; + if (language == LANG_IGNORE) result = "unknown"; else { Assert (0 <= language && language < (int) LanguageCount); - result = LanguageTable [language].def->name; + if (noPretending) + result = LanguageTable [language].def->name; + else + { + langType real_language = LanguageTable [language].pretendingAsLanguage; + if (real_language == LANG_IGNORE) + result = LanguageTable [language].def->name; + else + { + Assert (0 <= real_language && real_language < (int) LanguageCount); + result = LanguageTable [real_language].def->name; + } + } } return result; } +extern const char *getLanguageName (const langType language) +{ + return getLanguageNameFull (language, false); +} + extern const char *getLanguageKindName (const langType language, const int kindIndex) { kindDefinition* kdef = getLanguageKind (language, kindIndex); @@ -315,7 +353,7 @@ extern roleDefinition* getLanguageRoleForName (const langType language, int kind return getRoleForName (LanguageTable [language].kindControlBlock, kindIndex, roleName); } -extern langType getNamedLanguage (const char *const name, size_t len) +extern langType getNamedLanguageFull (const char *const name, size_t len, bool noPretending) { langType result = LANG_IGNORE; unsigned int i; @@ -339,9 +377,20 @@ extern langType getNamedLanguage (const char *const name, size_t len) result = i; vStringDelete (vstr); } + + if (result != LANG_IGNORE + && (!noPretending) + && LanguageTable [result].pretendedAsLanguage != LANG_IGNORE) + result = LanguageTable [result].pretendedAsLanguage; + return result; } +extern langType getNamedLanguage (const char *const name, size_t len) +{ + return getNamedLanguageFull (name, len, false); +} + static langType getNameOrAliasesLanguageAndSpec (const char *const key, langType start_index, const char **const spec, enum specType *specType) { @@ -462,6 +511,151 @@ extern langType getLanguageForFilename (const char *const filename, langType sta &tmp_specType); } +const char *scopeSeparatorFor (langType language, int kindIndex, int parentKindIndex) +{ + Assert (0 <= language && language < (int) LanguageCount); + + parserObject *parser = LanguageTable + language; + struct kindControlBlock *kcb = parser->kindControlBlock; + + const scopeSeparator *sep = getScopeSeparator (kcb, kindIndex, parentKindIndex); + return sep? sep->separator: NULL; +} + +static bool processLangDefineScopesep(const langType language, + const char *const option, + const char *const parameter) +{ + parserObject *parser; + const char * p = parameter; + + + char parentKletter; + int parentKindex = KIND_FILE_INDEX; + char kletter; + int kindex = KIND_FILE_INDEX; + const char *separator; + + Assert (0 <= language && language < (int) LanguageCount); + parser = LanguageTable + language; + + + /* + * Parent + */ + parentKletter = p[0]; + + if (parentKletter == '\0') + error (FATAL, "no scope separator specified in \"--%s\" option", option); + else if (parentKletter == '/') + parentKindex = KIND_GHOST_INDEX; + else if (parentKletter == KIND_WILDCARD) + parentKindex = KIND_WILDCARD_INDEX; + else if (parentKletter == KIND_FILE_DEFAULT) + error (FATAL, + "the kind letter `F' in \"--%s\" option is reserved for \"file\" kind and no separator can be assigned to", + option); + else if (isalpha (parentKletter)) + { + kindDefinition *kdef = getKindForLetter (parser->kindControlBlock, parentKletter); + if (kdef == NULL) + error (FATAL, + "the kind for letter `%c' specified in \"--%s\" option is not defined.", + parentKletter, option); + parentKindex = kdef->id; + } + else + error (FATAL, + "the kind letter `%c` given in \"--%s\" option is not an alphabet", + parentKletter, option); + + + /* + * Child + */ + if (parentKindex == KIND_GHOST_INDEX) + kletter = p[1]; + else + { + if (p[1] != '/') + error (FATAL, + "wrong separator specification in \"--%s\" option: no slash after parent kind letter `%c'", + option, parentKletter); + kletter = p[2]; + } + + if (kletter == '\0') + error (FATAL, "no child kind letter in \"--%s\" option", option); + else if (kletter == '/') + error (FATAL, + "wrong separator specification in \"--%s\" option: don't specify slash char twice: %s", + option, parameter); + else if (kletter == ':') + error (FATAL, + "no child kind letter in \"--%s\" option", option); + else if (kletter == KIND_WILDCARD) + { + if (parentKindex != KIND_WILDCARD_INDEX + && parentKindex != KIND_GHOST_INDEX) + error (FATAL, + "cannot use wild card for child kind unless parent kind is also wild card or empty"); + kindex = KIND_WILDCARD_INDEX; + } + else if (kletter == KIND_FILE_DEFAULT) + error (FATAL, + "the kind letter `F' in \"--%s\" option is reserved for \"file\" kind and no separator can be assigned to", + option); + else if (isalpha (kletter)) + { + kindDefinition *kdef = getKindForLetter (parser->kindControlBlock, kletter); + if (kdef == NULL) + error (FATAL, + "the kind for letter `%c' specified in \"--%s\" option is not defined.", + kletter, option); + kindex = kdef->id; + } + else + error (FATAL, + "the kind letter `%c` given in \"--%s\" option is not an alphabet", + kletter, option); + + /* + * Separator + */ + if (parentKindex == KIND_GHOST_INDEX) + { + if (p[2] != ':') + error (FATAL, + "wrong separator specification in \"--%s\" option: cannot find a colon after child kind: %s", + option, parameter); + separator = p + 3; + } + else + { + if (p[3] != ':') + error (FATAL, + "wrong separator specification in \"--%s\" option: cannot find a colon after child kind: %s", + option, parameter); + separator = p + 4; + } + + Assert (parentKindex != KIND_FILE_INDEX); + Assert (kindex != KIND_FILE_INDEX); + defineScopeSeparator (parser->kindControlBlock, kindex, parentKindex, separator); + return true; +} + +extern bool processScopesepOption (const char *const option, const char * const parameter) +{ + langType language; + + language = getLanguageComponentInOption (option, "_scopesep-"); + if (language == LANG_IGNORE) + return false; + + return processLangDefineScopesep (language, option, parameter); +} + static parserCandidate* parserCandidateNew(unsigned int count CTAGS_ATTR_UNUSED) { parserCandidate* candidates; @@ -586,7 +780,7 @@ static vString* determineEmacsModeAtFirstLine (const char* const line) for ( ; isspace ((int) *p) ; ++p) ; /* no-op */ - if (strncmp(p, "mode:", strlen("mode:")) == 0) + if (strncasecmp(p, "mode:", strlen("mode:")) == 0) { /* -*- mode: MODE; -*- */ p += strlen("mode:"); @@ -855,7 +1049,7 @@ struct getLangCtx { { \ MIO *tmp_ = (_glc_)->input; \ (_glc_)->input = mio_new_mio (tmp_, 0, -1); \ - mio_free (tmp_); \ + mio_unref (tmp_); \ if (!(_glc_)->input) { \ (_glc_)->err = true; \ goto _label_; \ @@ -865,7 +1059,7 @@ struct getLangCtx { #define GLC_FCLOSE(_glc_) do { \ if ((_glc_)->input) { \ - mio_free((_glc_)->input); \ + mio_unref((_glc_)->input); \ (_glc_)->input = NULL; \ } \ } while (0) @@ -1693,6 +1887,11 @@ extern void initializeParsing (void) builtInCount = ARRAY_SIZE (BuiltInParsers); LanguageTable = xMalloc (builtInCount, parserObject); memset(LanguageTable, 0, builtInCount * sizeof (parserObject)); + for (i = 0; i < builtInCount; ++i) + { + LanguageTable [i].pretendingAsLanguage = LANG_IGNORE; + LanguageTable [i].pretendedAsLanguage = LANG_IGNORE; + } LanguageHTable = hashTableNew (127, hashCstrcasehash, @@ -1741,6 +1940,8 @@ extern void freeParserResources (void) if (parser->def->finalize) (parser->def->finalize)((langType)i, (bool)parser->initialized); + uninstallTagXpathTable (i); + freeLregexControlBlock (parser->lregexControlBlock); freeKindControlBlock (parser->kindControlBlock); parser->kindControlBlock = NULL; @@ -1967,6 +2168,8 @@ extern void processLanguageDefineOption ( LanguageTable [def->id].currentPatterns = stringListNew (); LanguageTable [def->id].currentExtensions = stringListNew (); + LanguageTable [def->id].pretendingAsLanguage = LANG_IGNORE; + LanguageTable [def->id].pretendedAsLanguage = LANG_IGNORE; eFree (name); } @@ -2191,7 +2394,7 @@ static bool processLangDefineKind(const langType language, if (letter == ',') error (FATAL, "no kind letter specified in \"--%s\" option", option); if (!isalnum (letter)) - error (FATAL, "the kind letter give in \"--%s\" option is not an alphabet or a number", option); + error (FATAL, "the kind letter given in \"--%s\" option is not an alphabet or a number", option); else if (letter == KIND_FILE_DEFAULT) error (FATAL, "the kind letter `F' in \"--%s\" option is reserved for \"file\" kind", option); else if (getKindForLetter (parser->kindControlBlock, letter)) @@ -2290,7 +2493,7 @@ static bool processLangDefineRole(const langType language, if (kletter == '.') error (FATAL, "no kind letter specified in \"--%s\" option", option); if (!isalnum (kletter)) - error (FATAL, "the kind letter give in \"--%s\" option is not an alphabet or a number", option); + error (FATAL, "the kind letter given in \"--%s\" option is not an alphabet or a number", option); else if (kletter == KIND_FILE_DEFAULT) error (FATAL, "the kind letter `F' in \"--%s\" option is reserved for \"file\" kind and no role can be attached to", option); @@ -3142,9 +3345,7 @@ static bool createTagsWithFallback1 (const langType language, */ setTagFilePosition (&tagfpos); setNumTagsAdded (numTags); -#ifdef GEANY_CTAGS_LIB writerRescanFailed (numTags); -#endif /* GEANY_CTAGS_LIB */ tagFileResized = true; breakPromisesAfter(lastPromise); } @@ -3228,30 +3429,6 @@ static bool createTagsWithFallback ( return tagFileResized; } -#ifdef GEANY_CTAGS_LIB - -extern void geanyCreateTags(unsigned char *buffer, size_t bufferSize, - const char *fileName, const langType language) -{ - MIO *mio = NULL; - - if (buffer) - mio = mio_new_memory (buffer, bufferSize, NULL, NULL); - - /* keep in sync with parseFileWithMio() */ - setupWriter (); - setupAnon (); - initParserTrashBox (); - - createTagsWithFallback (fileName, language, mio); - - finiParserTrashBox (); - teardownAnon (); - teardownWriter(fileName); -} - -#endif /* GEANY_CTAGS_LIB */ - static void printGuessedParser (const char* const fileName, langType language) { const char *parserName; @@ -3262,7 +3439,9 @@ static void printGuessedParser (const char* const fileName, langType language) parserName = RSV_NONE; } else - parserName = LanguageTable [language].def->name; + { + parserName = getLanguageName (language); + } printf("%s: %s\n", fileName, parserName); } @@ -3379,12 +3558,36 @@ extern bool doesParserRequireMemoryStream (const langType language) extern bool parseFile (const char *const fileName) { TRACE_ENTER_TEXT("Parsing file %s",fileName); - bool bRet = parseFileWithMio (fileName, NULL); + bool bRet = parseFileWithMio (fileName, NULL, NULL); TRACE_LEAVE(); return bRet; } -extern bool parseFileWithMio (const char *const fileName, MIO *mio) +static bool parseMio (const char *const fileName, langType language, MIO* mio, bool useSourceFileTagPath, + void *clientData) +{ + bool tagFileResized = false; + + setupWriter (clientData); + + setupAnon (); + + initParserTrashBox (); + + tagFileResized = createTagsWithFallback (fileName, language, mio); + + finiParserTrashBox (); + + teardownAnon (); + + if (useSourceFileTagPath) + return teardownWriter (getSourceFileTagPath())? true: tagFileResized; + else + return teardownWriter(fileName); +} + +extern bool parseFileWithMio (const char *const fileName, MIO *mio, + void *clientData) { bool tagFileResized = false; langType language; @@ -3417,21 +3620,7 @@ extern bool parseFileWithMio (const char *const fileName, MIO *mio) /* TODO: checkUTF8BOM can be used to update the encodings. */ openConverter (getLanguageEncoding (language), Option.outputEncoding); #endif - - setupWriter (); - - setupAnon (); - - initParserTrashBox (); - - tagFileResized = createTagsWithFallback (fileName, language, req.mio); - - finiParserTrashBox (); - - teardownAnon (); - - tagFileResized = teardownWriter (getSourceFileTagPath())? true: tagFileResized; - + tagFileResized = parseMio (fileName, language, req.mio, true, clientData); if (Option.filter && ! Option.interactive) closeTagFile (tagFileResized); addTotals (1, 0L, 0L); @@ -3439,17 +3628,31 @@ extern bool parseFileWithMio (const char *const fileName, MIO *mio) #ifdef HAVE_ICONV closeConverter (); #endif - if (req.type == GLR_OPEN && req.mio) - mio_free (req.mio); - return tagFileResized; } if (req.type == GLR_OPEN && req.mio) - mio_free (req.mio); + mio_unref (req.mio); return tagFileResized; } +extern bool parseRawBuffer(const char *fileName, unsigned char *buffer, + size_t bufferSize, const langType language, void *clientData) +{ + MIO *mio = NULL; + bool r; + + if (buffer) + mio = mio_new_memory (buffer, bufferSize, NULL, NULL); + + r = parseMio (fileName, language, mio, false, clientData); + + if (buffer) + mio_unref (mio); + + return r; +} + static void matchLanguageMultilineRegexCommon (const langType language, bool (* func) (struct lregexControlBlock *, const vString* const), const vString* const allLines) @@ -3671,6 +3874,33 @@ static void installTagXpathTable (const langType language) } } +static void uninstallTagXpathTable (const langType language) +{ + parserDefinition* lang; + unsigned int i, j; + + Assert (0 <= language && language < (int) LanguageCount); + lang = LanguageTable [language].def; + + if (lang->tagXpathTableTable != NULL) + { + for (i = 0; i < lang->tagXpathTableCount; ++i) + for (j = 0; j < lang->tagXpathTableTable[i].count; ++j) + removeTagXpath (language, lang->tagXpathTableTable[i].table + j); + } +} + +const tagXpathTableTable *getXpathTableTable (const langType language, unsigned int nth) +{ + parserDefinition* lang; + + Assert (0 <= language && language < (int) LanguageCount); + lang = LanguageTable [language].def; + + Assert (nth < lang->tagXpathTableCount); + return lang->tagXpathTableTable + nth; +} + extern unsigned int getXpathFileSpecCount (const langType language) { parserDefinition* lang; @@ -3881,21 +4111,29 @@ extern void anonGenerate (vString *buffer, const char *prefix, int kind) parser -> anonymousIdentiferId ++; char szNum[32]; +#if 0 + char buf [9]; vStringCopyS(buffer, prefix); -/* GEANY_CTAGS_DIFF */ -/* we want to see numbers for anon functions in the tree view instead of the hash - char buf [9]; anonHashString (getInputFileName(), buf); sprintf(szNum,"%s%02x%02x",buf,parser -> anonymousIdentiferId, kind); -*/ +#else + /* we want to see numbers for anon functions in the tree view instead of the hash */ + vStringCopyS(buffer, prefix); sprintf(szNum,"%u", parser -> anonymousIdentiferId); -/* GEANY_CTAGS_DIFF_END */ - +#endif vStringCatS(buffer,szNum); } +extern vString *anonGenerateNew (const char *prefix, int kind) +{ + vString *buffer = vStringNew (); + + anonGenerate (buffer, prefix, kind); + return buffer; +} + extern void applyParameter (const langType language, const char *name, const char *args) { @@ -4122,6 +4360,54 @@ extern void addLanguageTagMultiTableRegex(const langType language, name, kinds, flags, disabled); } +extern bool processPretendOption (const char *const option, const char *const parameter) +{ + langType new_language, old_language; + +#define pretendOptionPrefix "_pretend-" + new_language = getLanguageComponentInOptionFull (option, pretendOptionPrefix, true); + if (new_language == LANG_IGNORE) + return false; + + if (parameter == NULL || parameter[0] == '\0') + error (FATAL, "A parameter is needed after \"%s\" option", option); + + old_language = getNamedLanguageFull (parameter, 0, true); + if (old_language == LANG_IGNORE) + error (FATAL, "Unknown language \"%s\" in option \"--%s=%s\"", + parameter, option, parameter); + + if (LanguageTable [new_language].pretendingAsLanguage != LANG_IGNORE) + { + error (FATAL, "%s parser pretends as %s already\n", + getLanguageNameFull (new_language, true), + getLanguageNameFull (LanguageTable [new_language].pretendingAsLanguage, true)); + } + if (LanguageTable [old_language].pretendedAsLanguage != LANG_IGNORE) + { + error (FATAL, "%s parser is pretended as %s already\n", + getLanguageNameFull (old_language, true), + getLanguageNameFull (LanguageTable [old_language].pretendedAsLanguage, true)); + } + + verbose ("%s pretends %s\n", + getLanguageNameFull (new_language, true), + getLanguageNameFull (old_language, true)); + + LanguageTable [new_language].pretendingAsLanguage = old_language; + LanguageTable [old_language].pretendedAsLanguage = new_language; + + verbose ("force enabling %s\n", + getLanguageNameFull (new_language, true)); + enableLanguage (new_language, true); + + verbose ("force disbling %s\n", + getLanguageNameFull (old_language, true)); + enableLanguage (old_language, false); + + return true; +} + /* * A parser for CTagsSelfTest (CTST) */ diff --git a/ctags/main/parse.h b/ctags/main/parse.h index 2da54dd742..c9a8c9c902 100644 --- a/ctags/main/parse.h +++ b/ctags/main/parse.h @@ -18,6 +18,7 @@ #include "kind.h" #include "lregex.h" #include "lxpath.h" +#include "vstring.h" /* * MACROS @@ -100,6 +101,10 @@ struct sParserDefinition { xpathFileSpec *xpathFileSpecs; unsigned int xpathFileSpecCount; + /* Following two fields are used in a parser using cork. */ + const char *defaultScopeSeparator; + const char *defaultRootScopeSeparator; + /* used internally */ langType id; /* id assigned to language */ unsigned int enabled:1; /* currently enabled? */ @@ -133,11 +138,6 @@ extern kindDefinition* getLanguageKindForLetter (const langType language, char k extern void initializeParser (langType language); -#ifdef GEANY_CTAGS_LIB -extern void geanyCreateTags(unsigned char *buffer, size_t bufferSize, - const char *fileName, const langType language); -#endif /* GEANY_CTAGS_LIB */ - #ifdef HAVE_ICONV extern const char *getLanguageEncoding (const langType language); #endif @@ -158,6 +158,7 @@ extern void addLanguageTagMultiTableRegex(const langType language, bool *disabled); extern void anonGenerate (vString *buffer, const char *prefix, int kind); +extern vString *anonGenerateNew (const char *prefix, int kind); extern void anonHashString (const char *filename, char buf[9]); #endif /* CTAGS_MAIN_PARSE_H */ diff --git a/ctags/main/parse_p.h b/ctags/main/parse_p.h index 5039575d97..6a3af62448 100644 --- a/ctags/main/parse_p.h +++ b/ctags/main/parse_p.h @@ -16,7 +16,7 @@ #include "kind.h" #include "lregex_p.h" #include "parse.h" -#include "parsers.h" /* contains list of parsers */ +#include "parsers_p.h" /* contains list of parsers */ #include "strlist.h" /* @@ -48,10 +48,15 @@ extern parserDefinitionFunc XML_PARSER_LIST; #ifdef HAVE_LIBYAML extern parserDefinitionFunc YAML_PARSER_LIST; #endif +#ifdef HAVE_PACKCC +extern parserDefinitionFunc PEG_PARSER_LIST; +#endif extern bool doesLanguageAllowNullTag (const langType language); extern bool doesLanguageRequestAutomaticFQTag (const langType language); +extern langType getNamedLanguageFull (const char *const name, size_t len, bool noPretending); + extern kindDefinition* getLanguageKind(const langType language, int kindIndex); extern kindDefinition* getLanguageKindForName (const langType language, const char *kindName); extern roleDefinition* getLanguageRole(const langType language, int kindIndex, int roleIndex); @@ -109,7 +114,10 @@ extern void printLangdefFlags (bool withListHeader, bool machinable, FILE *fp); extern void printKinddefFlags (bool withListHeader, bool machinable, FILE *fp); extern bool doesParserRequireMemoryStream (const langType language); extern bool parseFile (const char *const fileName); -extern bool parseFileWithMio (const char *const fileName, MIO *mio); +extern bool parseFileWithMio (const char *const fileName, MIO *mio, void *clientData); +extern bool parseRawBuffer(const char *fileName, unsigned char *buffer, + size_t bufferSize, const langType language, void *clientData); + extern bool runParserInNarrowedInputStream (const langType language, unsigned long startLine, long startCharOffset, unsigned long endLine, long endCharOffset, @@ -144,6 +152,8 @@ extern void processLanguageMultitableExtendingOption (langType language, const c extern unsigned int getXpathFileSpecCount (const langType language); extern xpathFileSpec* getXpathFileSpec (const langType language, unsigned int nth); +const tagXpathTableTable *getXpathTableTable (const langType language, unsigned int nth); + extern bool makeKindSeparatorsPseudoTags (const langType language, const ptagDesc *pdesc); extern bool makeKindDescriptionsPseudoTags (const langType language, diff --git a/ctags/main/parsers.h b/ctags/main/parsers_p.h similarity index 98% rename from ctags/main/parsers.h rename to ctags/main/parsers_p.h index 08acf3f597..309bf8208a 100644 --- a/ctags/main/parsers.h +++ b/ctags/main/parsers_p.h @@ -12,6 +12,10 @@ #ifndef CTAGS_MAIN_PARSERS_H #define CTAGS_MAIN_PARSERS_H +#define XML_PARSER_LIST +#define YAML_PARSER_LIST +#define PEG_PARSER_LIST + /* Add the name of any new parser definition function here */ /* keep src/tagmanager/tm_parser.h in sync */ #define PARSER_LIST \ @@ -67,7 +71,4 @@ ZephirParser, \ PowerShellParser -#define XML_PARSER_LIST -#define YAML_PARSER_LIST - #endif /* CTAGS_MAIN_PARSERS_H */ diff --git a/ctags/main/portable-dirent_p.h b/ctags/main/portable-dirent_p.h new file mode 100644 index 0000000000..44a7d257ab --- /dev/null +++ b/ctags/main/portable-dirent_p.h @@ -0,0 +1,947 @@ +#include "general.h" + +#if defined (HAVE_DIRENT_H) + +#include + +#elif defined (_MSC_VER) +/* + * Taken from + * https://github.com/tronkko/dirent/blob/master/include/dirent.h + * + * - s/malloc/eMalloc/ + * - s/free/eFree/ + */ + +/* + * Dirent interface for Microsoft Visual Studio + * Version 1.21 + * + * Copyright (C) 2006-2012 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of d_namlen without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return number of bytes needed to store d_namlen */ +#define _D_ALLOC_NAMLEN(p) (PATH_MAX) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +static _WDIR *_wopendir (const wchar_t *dirname); +static struct _wdirent *_wreaddir (_WDIR *dirp); +static int _wclosedir (_WDIR *dirp); +static void _wrewinddir (_WDIR* dirp); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + + +/* Multi-byte character versions */ +struct dirent { + /* Always zero */ + long d_ino; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + +static DIR *opendir (const char *dirname); +static struct dirent *readdir (DIR *dirp); +static int closedir (DIR *dirp); +static void rewinddir (DIR* dirp); + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir( + const wchar_t *dirname) +{ + _WDIR *dirp = NULL; + int error; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) eMalloc (sizeof (struct _WDIR)); + if (dirp != NULL) { + DWORD n; + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume its an absolute path. + */ +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + n = wcslen(dirname); +# else + n = GetFullPathNameW (dirname, 0, NULL, NULL); +# endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) eMalloc (sizeof (wchar_t) * n + 16); + if (dirp->patt) { + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume its an absolute path. + */ +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + wcsncpy_s(dirp->patt, n+1, dirname, n); +# else + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); +# endif + if (n > 0) { + wchar_t *p; + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + if (dirp->patt < p) { + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (dirent_first (dirp)) { + /* Directory stream opened successfully */ + error = 0; + } else { + /* Cannot retrieve first entry */ + error = 1; + dirent_set_errno (ENOENT); + } + + } else { + /* Cannot retrieve full path name */ + dirent_set_errno (ENOENT); + error = 1; + } + + } else { + /* Cannot allocate memory for search pattern */ + error = 1; + } + + } else { + /* Cannot allocate _WDIR structure */ + error = 1; + } + + /* Clean up in case of error */ + if (error && dirp) { + _wclosedir (dirp); + dirp = NULL; + } + + return dirp; +} + +/* + * Read next directory entry. The directory entry is returned in dirent + * structure in the d_name field. Individual directory entries returned by + * this function include regular files, sub-directories, pseudo-directories + * "." and ".." as well as volume labels, hidden files and system files. + */ +static struct _wdirent* +_wreaddir( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + struct _wdirent *entp; + + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* Pointer to directory entry to return */ + entp = &dirp->ent; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n + 1 < PATH_MAX && datap->cFileName[n] != 0) { + entp->d_name[n] = datap->cFileName[n]; + n++; + } + dirp->ent.d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entp->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entp->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entp->d_type = DT_DIR; + } else { + entp->d_type = DT_REG; + } + + /* Reset dummy fields */ + entp->d_ino = 0; + entp->d_reclen = sizeof (struct _wdirent); + + } else { + + /* Last directory entry read */ + entp = NULL; + + } + + return entp; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir( + _WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + } + + /* Release search pattern */ + if (dirp->patt) { + eFree (dirp->patt); + dirp->patt = NULL; + } + + /* Release directory structure */ + eFree (dirp); + ok = /*success*/0; + + } else { + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir( + _WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Open new search handle */ + dirent_first (dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to re-open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + } + return datap; +} + +/* Get next directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occured */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir( + const char *dirname) +{ + struct DIR *dirp; + int error; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) eMalloc (sizeof (struct DIR)); + if (dirp) { + wchar_t wname[PATH_MAX]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s (&n, wname, PATH_MAX, dirname, PATH_MAX); + if (!error) { + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (dirp->wdirp) { + /* Directory stream opened */ + error = 0; + } else { + /* Failed to open directory stream */ + error = 1; + } + + } else { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + error = 1; + } + + } else { + /* Cannot allocate DIR structure */ + error = 1; + } + + /* Clean up in case of error */ + if (error && dirp) { + eFree (dirp); + dirp = NULL; + } + + return dirp; +} + +/* + * Read next directory entry. + * + * When working with text consoles, please note that file names returned by + * readdir() are represented in the default ANSI code page while any output to + * console is typically formatted on another code page. Thus, non-ASCII + * characters in file names will not usually display correctly on console. The + * problem can be fixed in two ways: (1) change the character set of console + * to 1252 using chcp utility and use Lucida Console font, or (2) use + * _cprintf function when writing to console. The _cprinf() will re-encode + * ANSI strings to the console code page so many non-ASCII characters will + * display correcly. + */ +static struct dirent* +readdir( + DIR *dirp) +{ + WIN32_FIND_DATAW *datap; + struct dirent *entp; + + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, dirp->ent.d_name, PATH_MAX, datap->cFileName, PATH_MAX); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, dirp->ent.d_name, PATH_MAX, + datap->cAlternateFileName, PATH_MAX); + } + + if (!error) { + DWORD attr; + + /* Initialize directory entry for return */ + entp = &dirp->ent; + + /* Length of file name excluding zero terminator */ + entp->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entp->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entp->d_type = DT_DIR; + } else { + entp->d_type = DT_REG; + } + + /* Reset dummy fields */ + entp->d_ino = 0; + entp->d_reclen = sizeof (struct dirent); + + } else { + /* + * Cannot convert file name to multi-byte string so construct + * an errornous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entp = &dirp->ent; + entp->d_name[0] = '?'; + entp->d_name[1] = '\0'; + entp->d_namlen = 1; + entp->d_type = DT_UNKNOWN; + entp->d_ino = 0; + entp->d_reclen = 0; + } + + } else { + /* No more directory entries */ + entp = NULL; + } + + return entp; +} + +/* + * Close directory stream. + */ +static int +closedir( + DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir (dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + eFree (dirp); + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir( + DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir (dirp->wdirp); +} + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs (wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resuting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs (mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ + +#else +/*"dirent.h is not available."*/ +#endif /* HAVE_DIRENT_H */ diff --git a/ctags/main/portable-scandir.c b/ctags/main/portable-scandir.c new file mode 100644 index 0000000000..8261d1536b --- /dev/null +++ b/ctags/main/portable-scandir.c @@ -0,0 +1,235 @@ +#include "general.h" +/* + * Taken from https://github.com/ClusterLabs/pacemaker/blob/master/replace/scandir.c + */ + +/* scandir: Scan a directory, collecting all (selected) items into a an array. + * + * This code borrowed from 'libit', which can be found here: + * + * http://www.iro.umontreal.ca/~pinard/libit/dist/scandir/ + * + * The original author put this code in the public domain. + * It has been modified slightly to get rid of warnings, etc. + * + * Below is the email I received from pinard@iro.umontreal.ca (François Pinard) + * when I sent him an email asking him about the license, etc. of this + * code which I obtained from his site. + * + * I think the correct spelling of his name is Rich Salz. I think he's now + * rsalz@datapower.com... + * -- + * Rich Salz, Chief Security Architect + * DataPower Technology http://www.datapower.com + * XS40 XML Security Gateway http://www.datapower.com/products/xs40.html + * + * Copyright(C): none (public domain) + * License: none (public domain) + * Author: Rich Salz + * + * + * + * -- Alan Robertson + * alanr@unix.sh + * + ************************************************************************** + * + * Subject: Re: Scandir replacement function + * Date: 18 May 2001 12:00:48 -0400 + * From: pinard@iro.umontreal.ca (François Pinard) + * To: Alan Robertson + * References: 1 + * + * + * [Alan Robertson] + * + * > Hi, I'd like to use your scandir replacement function found here: + * > http://www.iro.umontreal.ca/~pinard/libit/dist/scandir/ But, it does + * > not indicate authorship or licensing terms in it. Could you tell me + * > who wrote this code, under what license you distribute it, and whether + * > and under what terms I may further distribute it? + * + * Hello, Alan. These are (somewhat) explained in UNSHAR.HDR found in the + * same directory. The routines have been written by Rick Saltz (I'm not + * completely sure of the spelling) a long while ago. I think that nowadays, + * Rick is better known as the main author of the nice INN package. + * + ************************************************************************** + * + * I spent a little time verifying this with Rick Salz. + * The results are below: + * + ************************************************************************** + * + * Date: Tue, 20 Sep 2005 21:52:09 -0400 (EDT) + * From: Rich Salz + * To: Alan Robertson + * Subject: Re: Verifying permissions/licenses/etc on some old code of yours - + * scandir.c + * In-Reply-To: <433071CA.8000107@unix.sh> + * Message-ID: + * Content-Type: TEXT/PLAIN; charset=US-ASCII + * + * yes, it's most definitely in the public domain. + * + * I'm glad you find it useful. I'm surprised it hasn't been replaced by, + * e.g,. something in GLibC. Ii'm impressed you tracked me down. + * + * /r$ + * + * -- + * Rich Salz Chief Security Architect + * DataPower Technology http://www.datapower.com + * XS40 XML Security Gateway http://www.datapower.com/products/xs40.html + * ----------------------------------------------------------------------> + * Subject: scandir, ftw REDUX + * Date: 1 Jan 88 00:47:01 GMT + * From: rsalz@pebbles.bbn.com + * Newsgroups: comp.sources.misc + * + * + * Forget my previous message -- I just decided for completeness's sake to + * implement the SysV ftw(3) routine, too. + * + * To repeat, these are public-domain implementations of the SystemV ftw() + * routine, the BSD scandir() and alphasort() routines, and documentation for + * same. The FTW manpage could be more readable, but so it goes. + * + * Anyhow, feel free to post these, and incorporate them into your existing + * packages. I have readdir() routiens for MSDOS and the Amiga if anyone + * wants them, and should have them for VMS by the end of January; let me + * know if you want copies. + * + * Yours in filesystems, + * /r$ + * + * Anyhow, feel free to post + * ----------------------------------------------------------------------< + * + */ + +#include "routines.h" +#include "routines_p.h" + +#ifdef HAVE_SCANDIR +#include +#else +#define USE_SCANDIR_COMPARE_STRUCT_DIRENT + +#include + +#include +#include +#include + +#ifndef NULL +# define NULL ((void *) 0) +#endif + +/* Initial guess at directory allocated. */ +#define INITIAL_ALLOCATION 20 + +int +scandir(const char *directory_name, + struct dirent ***array_pointer, int (*select_function) (const struct dirent *), +#ifdef USE_SCANDIR_COMPARE_STRUCT_DIRENT + /* This is what the linux man page says */ + int (*compare_function) (const struct dirent **, const struct dirent **) +#else + /* This is what the linux header file says ... */ + int (*compare_function) (const void *, const void *) +#endif + ) +{ + DIR *directory; + struct dirent **array; + struct dirent *entry; + struct dirent *copy; + int allocated = INITIAL_ALLOCATION; + int counter = 0; + + /* Get initial list space and open directory. */ + + if (directory = opendir(directory_name), directory == NULL) + return -1; + + if (array = (struct dirent **)eMalloc(allocated * sizeof(struct dirent *)), array == NULL) + return -1; + + /* Read entries in the directory. */ + + while (entry = readdir(directory), entry) + if (select_function == NULL || (*select_function) (entry)) { + /* User wants them all, or he wants this one. Copy the entry. */ + + /* + * On some OSes the declaration of "entry->d_name" is a minimal-length + * placeholder. Example: Solaris: + * /usr/include/sys/dirent.h: + * "char d_name[1];" + * man page "dirent(3)": + * The field d_name is the beginning of the character array + * giving the name of the directory entry. This name is + * null terminated and may have at most MAXNAMLEN chars. + * So our malloc length may need to be increased accordingly. + * sizeof(entry->d_name): space (possibly minimal) in struct. + * strlen(entry->d_name): actual length of the entry. + * + * John Kavadias + * David Lee + */ + int namelength = strlen(entry->d_name) + 1; /* length with NULL */ + int extra = 0; + + if (sizeof(entry->d_name) <= namelength) { + /* allocated space <= required space */ + extra += namelength - sizeof(entry->d_name); + } + + if (copy = (struct dirent *)eMalloc(sizeof(struct dirent) + extra), copy == NULL) { + closedir(directory); + eFree(array); + return -1; + } + copy->d_ino = entry->d_ino; + copy->d_reclen = entry->d_reclen; + strcpy(copy->d_name, entry->d_name); + + /* Save the copy. */ + + if (counter + 1 == allocated) { + allocated <<= 1; + array = (struct dirent **) + eRealloc((char *)array, allocated * sizeof(struct dirent *)); + if (array == NULL) { + closedir(directory); + eFree(array); + eFree(copy); + return -1; + } + } + array[counter++] = copy; + } + + /* Close things off. */ + + array[counter] = NULL; + *array_pointer = array; + closedir(directory); + + /* Sort? */ + + if (counter > 1 && compare_function) + qsort((char *)array, counter, sizeof(struct dirent *) + , (int (*)(const void *, const void *))(compare_function)); + + return counter; +} +#endif + +int scanDirectory (const char *directory_name, + struct dirent ***array_pointer, int (*select_function) (const struct dirent *), + int (*compare_function) (const struct dirent **, const struct dirent **)) +{ + return scandir (directory_name, array_pointer, select_function, compare_function); +} diff --git a/ctags/main/promise.c b/ctags/main/promise.c index b35753432b..ad538a2ef0 100644 --- a/ctags/main/promise.c +++ b/ctags/main/promise.c @@ -13,9 +13,10 @@ #include "general.h" #include "parse_p.h" #include "promise.h" +#include "promise_p.h" #include "ptrarray.h" #include "debug.h" -#include "read.h" +#include "read_p.h" #include "trashbox.h" #include "xtag.h" #include "numarray.h" diff --git a/ctags/main/ptrarray.h b/ctags/main/ptrarray.h index 2c0fd7794f..cf412728c9 100644 --- a/ctags/main/ptrarray.h +++ b/ctags/main/ptrarray.h @@ -18,10 +18,11 @@ /* * DATA DECLARATIONS */ +struct sPtrArray; +typedef struct sPtrArray ptrArray; typedef void (*ptrArrayDeleteFunc) (void *data); -struct sPtrArray; /* * FUNCTION PROTOTYPES diff --git a/ctags/main/read.c b/ctags/main/read.c index 657f8c1050..d0c8086d88 100644 --- a/ctags/main/read.c +++ b/ctags/main/read.c @@ -19,18 +19,20 @@ #define FILE_WRITE #include "read.h" +#include "read_p.h" #include "debug.h" #include "entry_p.h" -#include "main.h" #include "routines.h" #include "routines_p.h" #include "options_p.h" #include "parse_p.h" #include "promise_p.h" +#include "stats_p.h" #include "trace.h" #include "trashbox.h" #ifdef HAVE_ICONV # include "mbcs.h" +# include "mbcs_p.h" #endif /* @@ -162,8 +164,20 @@ extern MIOPos getInputFilePosition (void) extern MIOPos getInputFilePositionForLine (unsigned int line) { - return File.lineFposMap.pos[(((File.lineFposMap.count > (line - 1)) \ - && (line > 0))? (line - 1): 0)].pos; + int index; + if (line > 0) + { + if (File.lineFposMap.count > (line - 1)) + index = line - 1; + else if (File.lineFposMap.count != 0) + index = File.lineFposMap.count - 1; + else + index = 0; + } + else + index = 0; + + return File.lineFposMap.pos[index].pos; } extern langType getInputLanguage (void) @@ -648,7 +662,7 @@ extern bool openInputFile (const char *const fileName, const langType language, */ if (File.mio != NULL) { - mio_free (File.mio); /* close any open input file */ + mio_unref (File.mio); /* close any open input file */ File.mio = NULL; } @@ -750,7 +764,7 @@ extern void closeInputFile (void) fileStatus *status = eStat (vStringValue (File.input.name)); addTotals (0, File.input.lineNumber - 1L, status->size); } - mio_free (File.mio); + mio_unref (File.mio); File.mio = NULL; freeLineFposMap (&File.lineFposMap); } @@ -946,6 +960,19 @@ extern int skipToCharacterInInputFile (int c) return d; } +extern int skipToCharacterInInputFile2 (int c0, int c1) +{ + int d; + do + { + skipToCharacterInInputFile(c0); + do + d = getcFromInputFile (); + while (d == c0 && d != c1); + } while (d != EOF && d != c1); + return d; +} + /* An alternative interface to getcFromInputFile (). Do not mix use of readLineFromInputFile() * and getcFromInputFile() for the same file. The returned string does not contain * the terminating newline. A NULL return value means that all lines in the @@ -1100,7 +1127,7 @@ extern void popNarrowedInputStream (void) verbose ("CLEARING thin flag(%d)\n", File.thinDepth); return; } - mio_free (File.mio); + mio_unref (File.mio); File = BackupFile; memset (&BackupFile, 0, sizeof (BackupFile)); } diff --git a/ctags/main/read.h b/ctags/main/read.h index 671606e9b0..c388623783 100644 --- a/ctags/main/read.h +++ b/ctags/main/read.h @@ -55,78 +55,32 @@ enum eCharacters { /* InputFile: reading from fp in inputFile with updating fields in input fields */ extern unsigned long getInputLineNumber (void); +extern unsigned long getInputLineNumberForFileOffset(long offset); extern int getInputLineOffset (void); extern const char *getInputFileName (void); extern MIOPos getInputFilePosition (void); extern MIOPos getInputFilePositionForLine (unsigned int line); extern langType getInputLanguage (void); -extern const char *getInputLanguageName (void); -extern const char *getInputFileTagPath (void); extern bool isInputLanguage (langType lang); extern bool isInputHeaderFile (void); extern bool isInputLanguageKindEnabled (int kindIndex); - extern bool isInputLanguageRoleEnabled (int kindIndex, int roleIndex); -extern unsigned int countInputLanguageKinds (void); -extern unsigned int countInputLanguageRoles (int kindIndex); - -extern bool doesInputLanguageAllowNullTag (void); -extern bool doesInputLanguageRequestAutomaticFQTag (void); -extern bool doesParserRunAsGuest (void); -extern bool doesSubparserRun (void); -extern bool isParserMarkedNoEmission (void); -extern void freeInputFileResources (void); extern const unsigned char *getInputFileData (size_t *size); -/* Stream opened by getMio can be passed to openInputFile as the 3rd - argument. If the 3rd argument is NULL, openInputFile calls getMio - internally. The 3rd argument is introduced for reusing mio object - created in parser guessing stage. */ -extern bool openInputFile (const char *const fileName, const langType language, MIO *mio); -extern MIO *getMio (const char *const fileName, const char *const openMode, - bool memStreamRequired); -extern void resetInputFile (const langType language); - -extern void closeInputFile (void); -extern void *getInputFileUserData(void); extern int getcFromInputFile (void); extern int getNthPrevCFromInputFile (unsigned int nth, int def); extern int skipToCharacterInInputFile (int c); +extern int skipToCharacterInInputFile2 (int c0, int c1); extern void ungetcToInputFile (int c); extern const unsigned char *readLineFromInputFile (void); -enum nestedInputBoundaryFlag { - BOUNDARY_START = 1UL << 0, - BOUNDARY_END = 1UL << 1, -}; -extern unsigned int getNestedInputBoundaryInfo (unsigned long lineNumber); - -extern const char *getSourceFileTagPath (void); -extern langType getSourceLanguage (void); extern unsigned long getSourceLineNumber (void); /* Raw: reading from given a parameter, mio */ extern char *readLineRaw (vString *const vLine, MIO *const mio); -/* Bypass: reading from fp in inputFile WITHOUT updating fields in input fields */ -extern char *readLineFromBypass (vString *const vLine, MIOPos location, long *const pSeekValue); - -extern void pushNarrowedInputStream ( - unsigned long startLine, long startCharOffset, - unsigned long endLine, long endCharOffset, - unsigned long sourceLineOffset, - int promise); -extern void popNarrowedInputStream (void); - extern void pushLanguage(const langType language); extern langType popLanguage (void); -extern unsigned long getInputLineNumberForFileOffset(long offset); - -#define THIN_STREAM_SPEC 0, 0, 0, 0, 0 -extern bool isThinStreamSpec(unsigned long startLine, long startCharOffset, - unsigned long endLine, long endCharOffset, - unsigned long sourceLineOffset); - #endif /* CTAGS_MAIN_READ_H */ diff --git a/ctags/main/read_p.h b/ctags/main/read_p.h new file mode 100644 index 0000000000..e818f15a5e --- /dev/null +++ b/ctags/main/read_p.h @@ -0,0 +1,78 @@ +/* +* Copyright (c) 1998-2002, Darren Hiebert +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* Main part private interface to read.c +*/ +#ifndef CTAGS_MAIN_READ_PRIVATE_H +#define CTAGS_MAIN_READ_PRIVATE_H + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ + +#include "mio.h" +#include "types.h" +#include "vstring.h" + +/* +* DATA DECLARATIONS +*/ + +enum nestedInputBoundaryFlag { + BOUNDARY_START = 1UL << 0, + BOUNDARY_END = 1UL << 1, +}; + +/* +* FUNCTION PROTOTYPES +*/ + +extern const char *getInputLanguageName (void); +extern const char *getInputFileTagPath (void); + +extern unsigned int countInputLanguageKinds (void); +extern unsigned int countInputLanguageRoles (int kindIndex); + +extern bool doesInputLanguageAllowNullTag (void); +extern bool doesInputLanguageRequestAutomaticFQTag (void); +extern bool doesParserRunAsGuest (void); +extern bool doesSubparserRun (void); + +extern bool isParserMarkedNoEmission (void); +extern void freeInputFileResources (void); + +/* Stream opened by getMio can be passed to openInputFile as the 3rd + argument. If the 3rd argument is NULL, openInputFile calls getMio + internally. The 3rd argument is introduced for reusing mio object + created in parser guessing stage. */ +extern bool openInputFile (const char *const fileName, const langType language, MIO *mio); +extern MIO *getMio (const char *const fileName, const char *const openMode, + bool memStreamRequired); +extern void resetInputFile (const langType language); +extern void closeInputFile (void); +extern void *getInputFileUserData(void); + +extern unsigned int getNestedInputBoundaryInfo (unsigned long lineNumber); + +extern const char *getSourceFileTagPath (void); +extern langType getSourceLanguage (void); + +/* Bypass: reading from fp in inputFile WITHOUT updating fields in input fields */ +extern char *readLineFromBypass (vString *const vLine, MIOPos location, long *const pSeekValue); +extern void pushNarrowedInputStream ( + unsigned long startLine, long startCharOffset, + unsigned long endLine, long endCharOffset, + unsigned long sourceLineOffset, + int promise); +extern void popNarrowedInputStream (void); + +#define THIN_STREAM_SPEC 0, 0, 0, 0, 0 +extern bool isThinStreamSpec(unsigned long startLine, long startCharOffset, + unsigned long endLine, long endCharOffset, + unsigned long sourceLineOffset); + +#endif /* CTAGS_MAIN_READ_PRIVATE_H */ diff --git a/ctags/main/repoinfo.h b/ctags/main/repoinfo.h index c9e8a94a1f..c8d71ba40c 100644 --- a/ctags/main/repoinfo.h +++ b/ctags/main/repoinfo.h @@ -1 +1 @@ -#define CTAGS_REPOINFO "ee887eab" +#define CTAGS_REPOINFO "75c26bdf" diff --git a/ctags/main/routines.c b/ctags/main/routines.c index ce5e28b99a..27d781b05e 100644 --- a/ctags/main/routines.c +++ b/ctags/main/routines.c @@ -13,7 +13,7 @@ #include "general.h" /* must always come first */ #ifdef HAVE_STDLIB_H -# include /* to declare malloc (), realloc () */ +# include /* to declare malloc (), realloc (), mbcs() */ #endif #include #include @@ -59,9 +59,6 @@ #include "debug.h" #include "routines.h" #include "routines_p.h" -#ifdef HAVE_ICONV -# include "mbcs.h" -#endif #ifdef HAVE_ERRNO_H # include #endif @@ -262,6 +259,11 @@ extern void eFree (void *const ptr) free (ptr); } +extern void eFreeNoNullCheck (void *const ptr) +{ + free (ptr); +} + extern void eFreeIndirect(void **ptr) { if (ptr && *ptr) diff --git a/ctags/main/routines.h b/ctags/main/routines.h index 2be9960b30..1d43f2bb98 100644 --- a/ctags/main/routines.h +++ b/ctags/main/routines.h @@ -50,6 +50,7 @@ extern void *eMalloc (const size_t size); extern void *eCalloc (const size_t count, const size_t size); extern void *eRealloc (void *const ptr, const size_t size); extern void eFree (void *const ptr); +extern void eFreeNoNullCheck (void *const ptr); extern void eFreeIndirect(void **ptr); /* String manipulation functions */ diff --git a/ctags/main/routines_p.h b/ctags/main/routines_p.h index 6773bde237..d705da6bb8 100644 --- a/ctags/main/routines_p.h +++ b/ctags/main/routines_p.h @@ -94,14 +94,12 @@ extern MIO *tempFile (const char *const mode, char **const pName); extern char* baseFilenameSansExtensionNew (const char *const fileName, const char *const templateExt); -/* GEANY_CTAGS_DIFF */ -/* +#if 0 #include "portable-dirent_p.h" extern int scanDirectory (const char *directory_name, struct dirent ***array_pointer, int (*select_function) (const struct dirent *), int (*compare_function) (const struct dirent **, const struct dirent **)); -*/ -/* GEANY_CTAGS_DIFF_END */ +#endif #endif /* CTAGS_MAIN_ROUTINES_PRIVATE_H */ diff --git a/ctags/main/seccomp.c b/ctags/main/seccomp.c new file mode 100644 index 0000000000..12e6098219 --- /dev/null +++ b/ctags/main/seccomp.c @@ -0,0 +1,80 @@ +/* +* Copyright (c) 2017, Google, Inc. +* +* Author: Han-Wen Nienhuys +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +*/ + +#include "general.h" +#include "debug.h" +#include "interactive_p.h" +#include "routines.h" + +#ifdef HAVE_SECCOMP +#include + + +int installSyscallFilter (void) +{ + // Use SCMP_ACT_TRAP to get a core dump. + scmp_filter_ctx ctx = seccomp_init (SCMP_ACT_KILL); + if (ctx == NULL) + { + return 1; + } + + // Memory allocation. + seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS (mmap), 0); + seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS (munmap), 0); + seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS (mremap), 0); + seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS (brk), 0); + + // I/O + seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS (read), 0); + seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS (write), 0); + + // Clean exit + seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS (exit), 0); + seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS (exit_group), 0); + + // The bowels of stdio want to know the size of a file, even for stdout. + seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS (fstat), 0); + seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS (fstat64), 0); + + // seems unnecessary, but this comes from + // main/parse.c:2764 : tagFilePosition (&tagfpos); + seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS (lseek), 0); + seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS (_llseek), 0); + + // libxml2 uses pthread_once, which in turn uses a futex + seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS (futex), 0); + + verbose ("Entering sandbox\n"); + int err = seccomp_load (ctx); + if (err < 0) + { + error (WARNING, "Failed to install syscall filter"); + /* Error handling is done in upper layer. */ + } + + seccomp_release (ctx); + + return err; +} + +/* + TODO: on OSX, Seatbelt + (https://dev.chromium.org/developers/design-documents/sandbox/osx-sandboxing-design) + should be used for equivalent functionality. + */ + +#else +int installSyscallFilter (void) +{ + AssertNotReached (); + return -1; +} +#endif diff --git a/ctags/main/selectors.c b/ctags/main/selectors.c index cc59914703..ee0662957e 100644 --- a/ctags/main/selectors.c +++ b/ctags/main/selectors.c @@ -341,11 +341,14 @@ matchXpathFileSpec (xmlDocPtr doc, xpathFileSpec *spec) && doc->children->name && (strcmp (spec->rootElementName, (char *)doc->children->name) == 0))) return false; + else + verbose (" Xml[rootElementName]== %s\n", + spec->rootElementName); } if (spec->nameInDTD) { - if (*spec->rootElementName == '\0') + if (*spec->nameInDTD == '\0') { if (doc->intSubset && doc->intSubset->name) return false; @@ -354,6 +357,9 @@ matchXpathFileSpec (xmlDocPtr doc, xpathFileSpec *spec) && doc->intSubset->name && (strcmp (spec->nameInDTD, (char *)doc->intSubset->name) == 0))) return false; + else + verbose (" Xml[nameInDTD]== %s\n", + spec->nameInDTD); } if (spec->externalID) @@ -367,6 +373,10 @@ matchXpathFileSpec (xmlDocPtr doc, xpathFileSpec *spec) && doc->intSubset->ExternalID && (strcmp (spec->externalID, (char *)doc->intSubset->ExternalID) == 0))) return false; + else + verbose (" Xml[externalID]== %s\n", + spec->externalID); + } if (spec->systemID) @@ -380,6 +390,9 @@ matchXpathFileSpec (xmlDocPtr doc, xpathFileSpec *spec) && doc->intSubset->SystemID && (strcmp (spec->systemID, (char *)doc->intSubset->SystemID) == 0))) return false; + else + verbose (" Xml[systemID]== %s\n", + spec->systemID); } if (spec->rootNSPrefix) @@ -394,6 +407,9 @@ matchXpathFileSpec (xmlDocPtr doc, xpathFileSpec *spec) && doc->children->ns->prefix && (strcmp (spec->rootNSPrefix, (char *)doc->children->ns->prefix)))) return false; + else + verbose (" Xml[rootNSPrefix]== %s\n", + spec->rootNSPrefix); } if (spec->rootNSHref) @@ -408,6 +424,9 @@ matchXpathFileSpec (xmlDocPtr doc, xpathFileSpec *spec) && doc->children->ns->href && (strcmp (spec->rootNSHref, (char *)doc->children->ns->href) == 0))) return false; + else + verbose (" Xml[rootNSHref]== %s\n", + spec->rootNSHref); } return true; } @@ -419,6 +438,7 @@ selectParserForXmlDoc (xmlDocPtr doc, { unsigned int lang_index; + bool xml_parser_is_in_candidate = false;; verbose (" Xml[rootElementName]: %s\n", (doc->children && doc->children->name) @@ -454,6 +474,15 @@ selectParserForXmlDoc (xmlDocPtr doc, if (matchXpathFileSpec (doc, spec)) return getLanguageName (candidates[lang_index]); } + + if (strcmp (getLanguageName (candidates[lang_index]), "XML") == 0) + xml_parser_is_in_candidate = true; + } + + if (xml_parser_is_in_candidate) + { + verbose (" Use generic XML parser as fallback\n"); + return "XML"; } return NULL; @@ -481,4 +510,15 @@ selectByXpathFileSpec (MIO *input, return r; } + +#else + +const char * +selectByXpathFileSpec (MIO *input, + langType *candidates, + unsigned int nCandidates) +{ + return NULL; +} + #endif diff --git a/ctags/main/selectors.h b/ctags/main/selectors.h index 41132aa4c9..6b2eb7f7d9 100644 --- a/ctags/main/selectors.h +++ b/ctags/main/selectors.h @@ -27,9 +27,7 @@ selectByArrowOfR (MIO *, langType *, unsigned int); const char * selectByRexxCommentAndDosbatchLabelPrefix (MIO *, langType *, unsigned int); -#ifdef HAVE_LIBXML const char * selectByXpathFileSpec (MIO *input, langType *candidates, unsigned int nCandidates); -#endif #endif diff --git a/ctags/main/sort.c b/ctags/main/sort.c index 775dd9a04f..e7d78c8039 100644 --- a/ctags/main/sort.c +++ b/ctags/main/sort.c @@ -29,7 +29,7 @@ #include "options_p.h" #include "read.h" #include "routines.h" -#include "sort.h" +#include "sort_p.h" /* * FUNCTION DEFINITIONS @@ -169,7 +169,7 @@ extern void failedSort (MIO *const mio, const char* msg) { const char* const cannotSort = "cannot sort tag file"; if (mio != NULL) - mio_free (mio); + mio_unref (mio); if (msg == NULL) error (FATAL | PERROR, "%s", cannotSort); else @@ -223,7 +223,7 @@ static void writeSortedTags ( } if (toStdout) mio_flush (mio); - mio_free (mio); + mio_unref (mio); } extern void internalSortTags (const bool toStdout, MIO* mio, size_t numTags) diff --git a/ctags/main/sort.h b/ctags/main/sort_p.h similarity index 100% rename from ctags/main/sort.h rename to ctags/main/sort_p.h diff --git a/ctags/main/stats.c b/ctags/main/stats.c new file mode 100644 index 0000000000..5736c63a95 --- /dev/null +++ b/ctags/main/stats.c @@ -0,0 +1,89 @@ +/* +* Copyright (c) 1996-2003, Darren Hiebert +* +* Author: Darren Hiebert +* http://ctags.sourceforge.net +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* It is provided on an as-is basis and no responsibility is accepted for its +* failure to perform as expected. +*/ + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ + +#include + +#include "entry_p.h" +#include "options_p.h" +#include "stats_p.h" + +/* +* MACROS +*/ +#define plural(value) (((unsigned long)(value) == 1L) ? "" : "s") + +/* +* DATA DEFINITIONS +*/ +static struct { long files, lines, bytes; } Totals = { 0, 0, 0 }; + + +/* +* FUNCTION DEFINITIONS +*/ +extern void addTotals ( + const unsigned int files, const long unsigned int lines, + const long unsigned int bytes) +{ + Totals.files += files; + Totals.lines += lines; + Totals.bytes += bytes; +} + +extern void printTotals (const clock_t *const timeStamps, bool append, sortType sorted) +{ + const unsigned long totalTags = numTagsTotal(); + const unsigned long addedTags = numTagsAdded(); + + fprintf (stderr, "%ld file%s, %ld line%s (%ld kB) scanned", + Totals.files, plural (Totals.files), + Totals.lines, plural (Totals.lines), + Totals.bytes/1024L); +#ifdef CLOCK_AVAILABLE + { + const double interval = ((double) (timeStamps [1] - timeStamps [0])) / + CLOCKS_PER_SEC; + + fprintf (stderr, " in %.01f seconds", interval); + if (interval != (double) 0.0) + fprintf (stderr, " (%lu kB/s)", + (unsigned long) (Totals.bytes / interval) / 1024L); + } +#endif + fputc ('\n', stderr); + + fprintf (stderr, "%lu tag%s added to tag file", + addedTags, plural(addedTags)); + if (append) + fprintf (stderr, " (now %lu tags)", totalTags); + fputc ('\n', stderr); + + if (totalTags > 0 && sorted != SO_UNSORTED) + { + fprintf (stderr, "%lu tag%s sorted", totalTags, plural (totalTags)); +#ifdef CLOCK_AVAILABLE + fprintf (stderr, " in %.02f seconds", + ((double) (timeStamps [2] - timeStamps [1])) / CLOCKS_PER_SEC); +#endif + fputc ('\n', stderr); + } + +#ifdef DEBUG + fprintf (stderr, "longest tag line = %lu\n", + (unsigned long) maxTagsLine ()); +#endif +} diff --git a/ctags/main/stats_p.h b/ctags/main/stats_p.h new file mode 100644 index 0000000000..b88b23d4a1 --- /dev/null +++ b/ctags/main/stats_p.h @@ -0,0 +1,28 @@ +/* +* Copyright (c) 1996-2003, Darren Hiebert +* +* Author: Darren Hiebert +* http://ctags.sourceforge.net +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* It is provided on an as-is basis and no responsibility is accepted for its +* failure to perform as expected. +*/ + +#ifndef CTAGS_MAIN_STATS_PRIVATE_H +#define CTAGS_MAIN_STATS_PRIVATE_H + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ +#include "options_p.h" + +/* +* FUNCTION PROTOTYPES +*/ +extern void addTotals (const unsigned int files, const long unsigned int lines, const long unsigned int bytes); +extern void printTotals (const clock_t *const timeStamps, bool append, sortType sorted); + +#endif /* CTAGS_MAIN_STATS_PRIVATE_H */ diff --git a/ctags/main/strlist.c b/ctags/main/strlist.c index 32d960bef7..adfb522a2e 100644 --- a/ctags/main/strlist.c +++ b/ctags/main/strlist.c @@ -13,11 +13,7 @@ #include "general.h" /* must always come first */ #include -/* GEANY_CTAGS_DIFF */ -/* We don't have fnmatch in Geany #include -*/ -/* GEANY_CTAGS_DIFF_END */ #include "debug.h" #include "read.h" @@ -77,7 +73,7 @@ extern stringList* stringListNewFromFile (const char* const fileName) else vStringDelete (str); } - mio_free (mio); + mio_unref (mio); } return result; } @@ -219,8 +215,6 @@ extern vString* stringListExtensionFinds ( static bool fileNameMatched ( const vString* const vpattern, const char* const fileName) { -/* GEANY_CTAGS_DIFF */ -/* We don't have fnmatch in Geany const char* const pattern = vStringValue (vpattern); #ifdef CASE_INSENSITIVE_FILENAMES @@ -235,9 +229,6 @@ static bool fileNameMatched ( #else return (fnmatch (pattern, fileName, 0) == 0); #endif -*/ - return false; -/* GEANY_CTAGS_DIFF_END */ } extern bool stringListFileMatched ( diff --git a/ctags/main/subparser.h b/ctags/main/subparser.h index 6200f063e0..836fd7cc68 100644 --- a/ctags/main/subparser.h +++ b/ctags/main/subparser.h @@ -14,11 +14,19 @@ #include "general.h" -#include "colprint_p.h" #include "dependency.h" #include "types.h" +/* +* MACROS +*/ +#define foreachSubparser(VAR, INCLUDING_NONE_CRAFTED_PARSER)\ + VAR = NULL; \ + while ((VAR = getNextSubparser (VAR, INCLUDING_NONE_CRAFTED_PARSER)) != NULL) +/* +* DATA DECLARATIONS +*/ typedef enum eSubparserRunDirection { SUBPARSER_UNKNOWN_DIRECTION = 0, SUBPARSER_BASE_RUNS_SUB = 1 << 0, @@ -42,23 +50,14 @@ struct sSubparser { void (* makeTagEntryNotify) (subparser *s, const tagEntryInfo *tag, int corkIndex); }; -/* A base parser doesn't have to call the following three functions. - The main part calls them internally. */ -extern void notifyInputStart (void); -extern void notifyInputEnd (void); -extern void notifyMakeTagEntry (const tagEntryInfo *info, int corkIndex); - -extern langType getSubparserLanguage (subparser *s); +/* +* FUNCTION PROTOTYPES +*/ /* Interface for Baseparser */ extern subparser *getNextSubparser(subparser *last, bool includingNoneCraftedParser); -#define foreachSubparser(VAR, INCLUDING_NONE_CRAFTED_PARSER)\ - VAR = NULL; \ - while ((VAR = getNextSubparser (VAR, INCLUDING_NONE_CRAFTED_PARSER)) != NULL) - extern void enterSubparser(subparser *subparser); extern void leaveSubparser(void); - extern subparser* getSubparserRunningBaseparser (void); extern void chooseExclusiveSubparser (subparser *s, void *data); @@ -66,16 +65,4 @@ extern void chooseExclusiveSubparser (subparser *s, void *data); #define RUN_DEFAULT_SUBPARSERS -1 extern void scheduleRunningBaseparser (int dependencyIndex); -extern subparser *getFirstSubparser(struct slaveControlBlock *controlBlock); -extern void useDefaultSubparsers (struct slaveControlBlock *controlBlock); -extern void useSpecifiedSubparser (struct slaveControlBlock *controlBlock, subparser *s); -extern void setupSubparsersInUse (struct slaveControlBlock *controlBlock); -extern subparser* teardownSubparsersInUse (struct slaveControlBlock *controlBlock); - -extern struct colprintTable * subparserColprintTableNew (void); -extern void subparserColprintAddSubparsers (struct colprintTable *table, - struct slaveControlBlock *scb); -extern void subparserColprintTablePrint (struct colprintTable *table, - bool withListHeader, bool machinable, FILE *fp); - #endif /* CTAGS_MAIN_SUBPARSER_H */ diff --git a/ctags/main/subparser_p.h b/ctags/main/subparser_p.h new file mode 100644 index 0000000000..801e821c9b --- /dev/null +++ b/ctags/main/subparser_p.h @@ -0,0 +1,48 @@ +/* + * + * Copyright (c) 2017, Red Hat, Inc. + * Copyright (c) 2017, Masatake YAMATO + * + * Author: Masatake YAMATO + * + * This source code is released for free distribution under the terms of the + * GNU General Public License version 2 or (at your option) any later version. + * + */ +#ifndef CTAGS_MAIN_SUBPARSER_PRIVATE_H +#define CTAGS_MAIN_SUBPARSER_PRIVATE_H + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ + +#include "colprint_p.h" +#include "dependency_p.h" +#include "types.h" + +/* +* FUNCTION PROTOTYPES +*/ +extern subparser *getFirstSubparser(struct slaveControlBlock *controlBlock); +extern langType getSubparserLanguage (subparser *s); + +/* A base parser doesn't have to call the following three functions. + The main part calls them internally. */ +extern void notifyInputStart (void); +extern void notifyInputEnd (void); +extern void notifyMakeTagEntry (const tagEntryInfo *info, int corkIndex); + +extern void setupSubparsersInUse (struct slaveControlBlock *controlBlock); +extern subparser* teardownSubparsersInUse (struct slaveControlBlock *controlBlock); + +extern struct colprintTable * subparserColprintTableNew (void); +extern void subparserColprintAddSubparsers (struct colprintTable *table, + struct slaveControlBlock *scb); +extern void subparserColprintTablePrint (struct colprintTable *table, + bool withListHeader, bool machinable, FILE *fp); + +extern void useDefaultSubparsers (struct slaveControlBlock *controlBlock); +extern void useSpecifiedSubparser (struct slaveControlBlock *controlBlock, subparser *s); + +#endif /* CTAGS_MAIN_SUBPARSER_PRIVATE_H */ diff --git a/ctags/main/tokeninfo.c b/ctags/main/tokeninfo.c new file mode 100644 index 0000000000..a2d1166186 --- /dev/null +++ b/ctags/main/tokeninfo.c @@ -0,0 +1,205 @@ +/* +* Copyright (c) 2016, Masatake YAMATO +* Copyright (c) 2016, Red Hat, Inc. +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* This module contains functions for generating tags for Python language +* files. +*/ + +#include "tokeninfo.h" + +#include "entry.h" +#include "read.h" +#include "routines.h" + +static void* createToken (void *createArg) +{ + struct tokenInfoClass *klass = createArg; + tokenInfo *token; + + token = eMalloc (sizeof (*token) + klass->extraSpace); + token->klass = klass; + token->string = vStringNew (); + + return token; +} + +static void clearToken (void *data) +{ + tokenInfo *token = data; + + if (token->klass->clear) + token->klass->clear (token); + + token->type = token->klass->typeForKeyword; + token->keyword = token->klass->keywordNone; + vStringClear (token->string); + token->lineNumber = getInputLineNumber (); + token->filePosition = getInputFilePosition (); +} + +static void destroyToken (void *data) +{ + tokenInfo *token = data; + + if (token->klass->destroy) + token->klass->destroy (token); + + vStringDelete (token->string); + eFree (token); +} + +void *newToken (struct tokenInfoClass *klass) +{ + return newTokenFull (klass, NULL); +} + +void *newTokenFull (struct tokenInfoClass *klass, void *data) +{ + tokenInfo *token = NULL; + + if (klass->nPreAlloc == 0) + klass->nPreAlloc = 16; + + retry: + if (klass->pool) + token = objPoolGet (klass->pool); + else + { + klass->pool = objPoolNew (klass->nPreAlloc, + createToken, + destroyToken, + clearToken, + klass); + goto retry; + } + + if (klass->init) + klass->init (token, data); + return token; +} + +void flashTokenBacklog (struct tokenInfoClass *klass) +{ + if (klass->backlog) + ptrArrayClear (klass->backlog); +} + +void tokenDestroy (tokenInfo *token) +{ + objPoolPut (token->klass->pool, token); +} + + +void tokenReadFull (tokenInfo *token, void *data) +{ + if (!token->klass->backlog) + token->klass->backlog = ptrArrayNew ((ptrArrayDeleteFunc)tokenDestroy); + + if (ptrArrayCount (token->klass->backlog) > 0) + { + tokenInfo *backlog = ptrArrayLast (token->klass->backlog); + tokenCopyFull (token, backlog, data); + ptrArrayRemoveLast (token->klass->backlog); + tokenDestroy (backlog); + } + else + token->klass->read (token, data); +} + +void tokenRead (tokenInfo *token) +{ + tokenReadFull (token, NULL); +} + +void tokenCopyFull (tokenInfo *dest, tokenInfo *src, void *data) +{ + dest->lineNumber = src->lineNumber; + dest->filePosition = src->filePosition; + dest->type = src->type; + dest->keyword = src->keyword; + /* klass */ + vStringCopy(dest->string, src->string); + if (src->klass->copy) + src->klass->copy (dest, src, data); +} + +void tokenCopy (tokenInfo *dest, tokenInfo *src) +{ + tokenCopyFull (dest, src, NULL); +} + +void *newTokenByCopying (tokenInfo *src) +{ + return newTokenByCopyingFull (src, NULL); +} + +void *newTokenByCopyingFull (tokenInfo *src, void *data) +{ + void * dest = newToken (src->klass); + tokenCopyFull (dest, src, data); + return dest; +} + +bool tokenSkipToTypeFull (tokenInfo *token, tokenType t, void *data) +{ + while (! (tokenIsEOF (token) + || token->type == t)) + tokenReadFull (token, data); + + return (token->type == t)? true: false; +} + +bool tokenSkipToType (tokenInfo *token, tokenType t) +{ + return tokenSkipToTypeFull (token, t, NULL); +} + +void tokenUnreadFull (tokenInfo *token, void *data) +{ + tokenInfo *backlog; + + if (!token->klass->backlog) + token->klass->backlog = ptrArrayNew ((ptrArrayDeleteFunc)tokenDestroy); + + backlog = newToken (token->klass); + tokenCopyFull (backlog, token, data); + ptrArrayAdd (token->klass->backlog, backlog); +} + +void tokenUnread (tokenInfo *token) +{ + tokenUnreadFull (token, NULL); +} + +bool tokenSkipOverPair (tokenInfo *token) +{ + return tokenSkipOverPairFull(token, NULL); +} +bool tokenSkipOverPairFull (tokenInfo *token, void *data) +{ + int start = token->type; + int end = token->klass->typeForUndefined; + unsigned int i; + + for (i = 0; i < token->klass->pairCount; i++) + if (start == token->klass->pairs[i].start) + end = token->klass->pairs[i].end; + + if (end == token->klass->typeForUndefined) + return false; + + int depth = 1; + do { + tokenReadFull (token, data); + if (token->type == start) + depth ++; + else if (token->type == end) + depth--; + } while ((!tokenIsEOF(token)) && (depth > 0)); + + return (depth == 0)? true: false; +} diff --git a/ctags/main/tokeninfo.h b/ctags/main/tokeninfo.h new file mode 100644 index 0000000000..d196d96303 --- /dev/null +++ b/ctags/main/tokeninfo.h @@ -0,0 +1,96 @@ +/* +* Copyright (c) 2016, Masatake YAMATO +* Copyright (c) 2016, Red Hat, Inc. +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* This module contains functions for generating tags for Python language +* files. +*/ + +#include "general.h" /* must always come first */ +#include "mio.h" +#include "objpool.h" +#include "vstring.h" + +#ifndef CTAGS_MAIN_TOKEN_H +#define CTAGS_MAIN_TOKEN_H + +struct tokenClass; +struct tokenTypePair; + +typedef short tokenType; +typedef short tokenKeyword; + +typedef struct sTokenInfo { + tokenType type; + tokenKeyword keyword; + vString *string; + struct tokenInfoClass *klass; + unsigned long lineNumber; + MIOPos filePosition; +} tokenInfo; + +struct tokenTypePair { + tokenType start; + tokenType end; +}; + +#define TOKEN(X) ((tokenInfo *)X) +#define TOKENX(X,T) ((T *)(((char *)TOKEN(X)) + sizeof (tokenInfo))) + +struct tokenInfoClass { + unsigned int nPreAlloc; + tokenType typeForUndefined; + tokenKeyword keywordNone; + tokenType typeForKeyword; + tokenType typeForEOF; + size_t extraSpace; + struct tokenTypePair *pairs; + unsigned int pairCount; + void (*init) (tokenInfo *token, void *data); + void (*read) (tokenInfo *token, void *data); + void (*clear) (tokenInfo *token); + void (*destroy) (tokenInfo *token); + void (*copy) (tokenInfo *dest, tokenInfo *src, void *data); + objPool *pool; + ptrArray *backlog; +}; + +void *newToken (struct tokenInfoClass *klass); +void *newTokenFull (struct tokenInfoClass *klass, void *data); +void *newTokenByCopying (tokenInfo *src); +void *newTokenByCopyingFull (tokenInfo *src, void *data); + +void flashTokenBacklog (struct tokenInfoClass *klass); +void tokenDestroy (tokenInfo *token); + +void tokenReadFull (tokenInfo *token, void *data); +void tokenRead (tokenInfo *token); +void tokenUnreadFull (tokenInfo *token, void *data); /* DATA passed to copy method internally. */ +void tokenUnread (tokenInfo *token); + + +void tokenCopyFull (tokenInfo *dest, tokenInfo *src, void *data); +void tokenCopy (tokenInfo *dest, tokenInfo *src); + +/* Helper macro & functions */ + +#define tokenIsType(TKN,T) ((TKN)->type == TOKEN_##T) +#define tokenIsKeyword(TKN,K) ((TKN)->type == TKN->klass->typeForKeyword \ + && (TKN)->keyword == KEYWORD_##K) +#define tokenIsEOF(TKN) ((TKN)->type == (TKN)->klass->typeForEOF) + +#define tokenString(TKN) (vStringValue ((TKN)->string)) +#define tokenPutc(TKN,C) (vStringPut ((TKN)->string, C)) + +/* return true if t is found. In that case token holds an + language object type t. + return false if it reaches EOF. */ +bool tokenSkipToType (tokenInfo *token, tokenType t); +bool tokenSkipToTypeFull (tokenInfo *token, tokenType t, void *data); +bool tokenSkipOverPair (tokenInfo *token); +bool tokenSkipOverPairFull (tokenInfo *token, void *data); + +#endif diff --git a/ctags/main/trace.c b/ctags/main/trace.c new file mode 100644 index 0000000000..45fd1cdd26 --- /dev/null +++ b/ctags/main/trace.c @@ -0,0 +1,119 @@ +/* +* Copyright (c) 2016, Szymon Tomasz Stefanek +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* Tracing facility. +*/ + +#include "trace.h" + +#ifdef DO_TRACING + +#include "options.h" +#include "read.h" + +#include +#include + +void traceEnter(const char * szFunction,const char * szFormat,...) +{ + if (!isTraced()) + return; + + debugIndent (); + + fprintf(stderr,"[>> %s][at %lu] ",szFunction,getInputLineNumber()); + + va_list va; + va_start(va,szFormat); + vfprintf(stderr,szFormat,va); + va_end(va); + + fprintf(stderr,"\n"); + + debugInc(); +} + +void traceLeave(const char * szFunction,const char * szFormat,...) +{ + if (!isTraced()) + return; + + debugDec(); + debugIndent (); + + fprintf(stderr,"[<< %s][at %lu] ",szFunction,getInputLineNumber()); + + va_list va; + va_start(va,szFormat); + vfprintf(stderr,szFormat,va); + va_end(va); + + fprintf(stderr,"\n"); +} + +static void tracePrintFmtVa(const char * szFormat, va_list va) +{ + if (!isTraced()) + return; + + vfprintf(stderr,szFormat,va); +} + +void tracePrint(const char * szFunction, const char * szFormat,...) +{ + if (!isTraced()) + return; + + tracePrintPrefix(szFunction); + + va_list va; + va_start(va,szFormat); + tracePrintFmtVa (szFormat,va); + va_end(va); + + tracePrintNewline(); +} + +void tracePrintFmt(const char * szFormat,...) +{ + va_list va; + va_start(va,szFormat); + tracePrintFmtVa (szFormat,va); + va_end(va); +} + +void tracePrintPrefix(const char * szFunction) +{ + if (!isTraced()) + return; + + debugIndent(); + + fprintf(stderr,"[%s][at %lu] ",szFunction,getInputLineNumber()); +} + +void tracePrintNewline(void) +{ + if (!isTraced()) + return; + + fprintf(stderr,"\n"); +} + +static bool tracingMain; + +void traceMain(void) +{ + verbose("Tracing main part\n"); + tracingMain = true; +} + +bool isMainTraced(void) +{ + return tracingMain; +} + +#endif // DO_TRACING diff --git a/ctags/main/trashbox.h b/ctags/main/trashbox.h index fef93e5bec..0b01dd72b3 100644 --- a/ctags/main/trashbox.h +++ b/ctags/main/trashbox.h @@ -13,16 +13,23 @@ #ifndef CTAGS_MAIN_TRASH_H #define CTAGS_MAIN_TRASH_H +/* +* INCLUDE FILES +*/ + +#include "general.h" /* must always come first */ + + +/* +* DATA DECLARATIONS +*/ + typedef void (* TrashBoxDestroyItemProc) (void *); typedef struct sTrashBox TrashBox; -extern TrashBox* trashBoxNew (void); -extern TrashBox* trashBoxStack (TrashBox* trash_box); -extern void trashBoxDelete (TrashBox* trash_box); -extern void* trashBoxPut (TrashBox* trash_box, void* item, TrashBoxDestroyItemProc destroy); -extern TrashBoxDestroyItemProc trashBoxTakeBack (TrashBox* trash_box, void* item); -extern void trashBoxFree (TrashBox* trash_box, void* item); -extern void trashBoxMakeEmpty (TrashBox* trash_box); +/* +* MACROS +*/ #define DEFAULT_TRASH_BOX(PTR,PROC) trashBoxPut(NULL,PTR,(TrashBoxDestroyItemProc)PROC) #define DEFAULT_TRASH_BOX_TAKE_BACK(PTR) trashBoxTakeBack(NULL,PTR) @@ -30,11 +37,19 @@ extern void trashBoxMakeEmpty (TrashBox* trash_box); #define PARSER_TRASH_BOX(PTR,PROC) parserTrashBoxPut(PTR,(TrashBoxDestroyItemProc)PROC) #define PARSER_TRASH_BOX_TAKE_BACK(PTR) parserTrashBoxTakeBack(PTR) -extern void initDefaultTrashBox (void); -extern void finiDefaultTrashBox (void); -extern void initParserTrashBox (void); -extern void finiParserTrashBox (void); +/* +* FUNCTION PROTOTYPES +*/ + +extern TrashBox* trashBoxNew (void); +extern TrashBox* trashBoxStack (TrashBox* trash_box); +extern void trashBoxDelete (TrashBox* trash_box); +extern void* trashBoxPut (TrashBox* trash_box, void* item, TrashBoxDestroyItemProc destroy); +extern TrashBoxDestroyItemProc trashBoxTakeBack (TrashBox* trash_box, void* item); +extern void trashBoxFree (TrashBox* trash_box, void* item); +extern void trashBoxMakeEmpty (TrashBox* trash_box); + extern void* parserTrashBoxPut (void* item, TrashBoxDestroyItemProc destroy); extern TrashBoxDestroyItemProc parserTrashBoxTakeBack (void* item); diff --git a/ctags/main/trashbox_p.h b/ctags/main/trashbox_p.h new file mode 100644 index 0000000000..9959e0c53d --- /dev/null +++ b/ctags/main/trashbox_p.h @@ -0,0 +1,30 @@ +/* +* +* Copyright (c) 2017, Red Hat, Inc. +* Copyright (c) 2017, Masatake YAMATO +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +*/ + +#ifndef CTAGS_MAIN_TRASH_PRIVATE_H +#define CTAGS_MAIN_TRASH_PRIVATE_H + +/* +* INCLUDE FILES +*/ + +#include "general.h" /* must always come first */ + + +/* +* FUNCTION PROTOTYPES +*/ + +extern void initDefaultTrashBox (void); +extern void finiDefaultTrashBox (void); + +extern void initParserTrashBox (void); +extern void finiParserTrashBox (void); + +#endif /* CTAGS_MAIN_TRASH_PRIVATE_H */ diff --git a/ctags/main/types.h b/ctags/main/types.h index 3b61eac5c9..b36bf7711a 100644 --- a/ctags/main/types.h +++ b/ctags/main/types.h @@ -48,10 +48,4 @@ typedef struct sXtagDefinition xtagDefinition; struct sParameterHandlerTable; typedef struct sParameterHandlerTable parameterHandlerTable; -struct NestingLevel; -typedef struct NestingLevel NestingLevel; - -struct sPtrArray; -typedef struct sPtrArray ptrArray; - #endif /* CTAGS_MAIN_TYPES_H */ diff --git a/ctags/main/unwindi.c b/ctags/main/unwindi.c new file mode 100644 index 0000000000..89dd31d57d --- /dev/null +++ b/ctags/main/unwindi.c @@ -0,0 +1,292 @@ +/* + * + * Copyright (c) 2019, Red Hat, Inc. + * Copyright (c) 2019, Masatake YAMATO + * + * Author: Masatake YAMATO + * + * This source code is released for free distribution under the terms of the + * GNU General Public License version 2 or (at your option) any later version. + * + * Unwindable input stream / Unlimited ungetc + * + */ + +/* +* INCLUDE FILES +*/ + +#include "general.h" + +#include "debug.h" +#include "gcc-attr.h" +#include "inline.h" +#include "mio.h" +#include "objpool.h" +#include "ptrarray.h" +#include "read.h" +#include "routines.h" +#include "trashbox.h" +#include "unwindi.h" + +typedef struct sUugcChar { + int c; + /* lineNumber before reading the char (frontLineNumber). + * The lineNumber after reading the char (rearLineNumber) can be calculated: + * If the char is \n, rearLineNumber is frontLineNumber + 1. + * If the char is not, rearLineNumber is the same as frontLineNumber. */ + unsigned long lineNumber; +} uugcChar; + + +static ptrArray *uugcInputFile; +static uugcChar *uugcCurrentChar; +static objPool *uugcCharPool; + +static void deleteChar (void *c) +{ + eFree (c); +} + +static void* newChar (void *data CTAGS_ATTR_UNUSED) +{ + return xMalloc (1, uugcChar); +} + +CTAGS_INLINE void uugcDeleteC (uugcChar *c) +{ + if (c == uugcCurrentChar) + uugcCurrentChar = NULL; + + objPoolPut (uugcCharPool, c); +} + +static void uugcActivate (void) +{ + Assert (!uugcInputFile); + Assert (!uugcCurrentChar); + + if (uugcCharPool == NULL) + { + uugcCharPool = objPoolNew(256, + newChar, + deleteChar, + NULL, + NULL); + DEFAULT_TRASH_BOX(uugcCharPool, objPoolDelete); + } + + uugcInputFile = ptrArrayNew ((ptrArrayDeleteFunc)uugcDeleteC); +} + +static void uugcDeactive(void) +{ + Assert (uugcInputFile); + ptrArrayDelete (uugcInputFile); + uugcInputFile = NULL; + uugcCurrentChar = NULL; +} + +CTAGS_INLINE uugcChar *uugcNewC (int chr, unsigned long ln) +{ + Assert (uugcCharPool); + + uugcChar *c = objPoolGet (uugcCharPool); + c->c = chr; + c->lineNumber = ln; + return c; +} + +CTAGS_INLINE uugcChar *uugciGetC (void) +{ + uugcChar *c; + + Assert (uugcInputFile); + + if (ptrArrayCount (uugcInputFile) > 0) + { + c = ptrArrayLast (uugcInputFile); + ptrArrayRemoveLast (uugcInputFile); + } + else + { + unsigned long lineNumber = getInputLineNumber (); + int chr = getcFromInputFile(); + c = uugcNewC (chr, lineNumber); + } + + uugcCurrentChar = c; + return uugcCurrentChar; +} + +CTAGS_INLINE void uugcUngetC (uugcChar *c) +{ + uugcCurrentChar = NULL; + + if (c->c == EOF) + { + ptrArrayClear (uugcInputFile); + uugcDeleteC (c); + return; + } + + ptrArrayAdd (uugcInputFile, c); +} + +CTAGS_INLINE void uugcInjectC (int chr) +{ + if (chr == EOF) + return; + + uugcChar *lastc = NULL; + if (ptrArrayCount (uugcInputFile) > 0) + lastc = ptrArrayLast (uugcInputFile); + + unsigned long lineNumber; + if (lastc) + { + if (chr == '\n' && lastc->lineNumber > 0) + lineNumber = lastc->lineNumber - 1; + else + lineNumber = lastc->lineNumber; + } + else + { + lineNumber = getInputLineNumber (); + if (chr == '\n') + lineNumber--; + } + + uugcChar *c = uugcNewC(chr, lineNumber); + uugcUngetC (c); +} + +CTAGS_INLINE long uugcGetLineNumber () +{ + Assert (uugcInputFile); + + if (uugcCurrentChar) + { + unsigned long ln; + if (uugcCurrentChar->c == '\n') + ln = uugcCurrentChar->lineNumber + 1; + else + ln = uugcCurrentChar->lineNumber; + return ln; + } + else if (ptrArrayCount (uugcInputFile) > 0) + { + uugcChar *c = ptrArrayLast (uugcInputFile); + return c->lineNumber; + } + else + return getInputLineNumber (); +} + +CTAGS_INLINE MIOPos uugcGetFilePosition (void) +{ + if (uugcCurrentChar) + { + unsigned long ln; + if (uugcCurrentChar->c == '\n') + ln = uugcCurrentChar->lineNumber + 1; + else + ln = uugcCurrentChar->lineNumber; + return getInputFilePositionForLine (ln); + } + else if (ptrArrayCount (uugcInputFile) > 0) + { + uugcChar *c = ptrArrayLast (uugcInputFile); + return getInputFilePositionForLine (c->lineNumber); + } + else + return getInputFilePosition (); +} + +static ptrArray *uwiMarkerStack; +static ptrArray *uwiCurrentMarker; +extern void uwiActivate (void) +{ + Assert (!uwiMarkerStack); + + uugcActivate (); + uwiMarkerStack = ptrArrayNew ((ptrArrayDeleteFunc)ptrArrayDelete); +} + +extern void uwiDeactivate (void) +{ + Assert (uwiMarkerStack); + ptrArrayDelete (uwiMarkerStack); + uugcDeactive(); +} + +extern int uwiGetC () +{ + int c; + uugcChar *chr = uugciGetC (); + + c = chr->c; + + if (uwiCurrentMarker) + ptrArrayAdd (uwiCurrentMarker, chr); + else + { + uugcCurrentChar = NULL; + uugcDeleteC (chr); + } + + return c; +} + +extern void uwiUngetC (int c) +{ + Assert (!uwiCurrentMarker); + uugcInjectC (c); +} + +extern unsigned long uwiGetLineNumber (void) +{ + return uugcGetLineNumber (); +} + +extern MIOPos uwiGetFilePosition (void) +{ + return uugcGetFilePosition (); +} + +extern void uwiPushMarker (void) +{ + Assert (uwiMarkerStack); + + if (uwiCurrentMarker) + ptrArrayAdd (uwiMarkerStack, uwiCurrentMarker); + + uwiCurrentMarker = ptrArrayNew ((ptrArrayDeleteFunc)uugcDeleteC); +} + +extern void uwiPopMarker (int upto) +{ + Assert (uwiCurrentMarker); + + int count = (upto < 0)? ptrArrayCount (uwiCurrentMarker): upto; + while (count > 0) + { + uugcUngetC (ptrArrayLast (uwiCurrentMarker)); + ptrArrayRemoveLast (uwiCurrentMarker); + count--; + } + + ptrArrayDelete (uwiCurrentMarker); + + uwiCurrentMarker = NULL; + if (ptrArrayCount (uwiMarkerStack) > 0) + { + uwiCurrentMarker = ptrArrayLast (uwiMarkerStack); + ptrArrayRemoveLast (uwiMarkerStack); + } +} + +extern void uwiDropMaker () +{ + uwiPopMarker (0); +} diff --git a/ctags/main/unwindi.h b/ctags/main/unwindi.h new file mode 100644 index 0000000000..0e059a579a --- /dev/null +++ b/ctags/main/unwindi.h @@ -0,0 +1,71 @@ +/* + * + * Copyright (c) 2019, Red Hat, Inc. + * Copyright (c) 2019, Masatake YAMATO + * + * Author: Masatake YAMATO + * + * This source code is released for free distribution under the terms of the + * GNU General Public License version 2 or (at your option) any later version. + * + * Unwindable input stream + * + */ +#ifndef CTAGS_MAIN_UNWINDI_H +#define CTAGS_MAIN_UNWINDI_H + +/* This header file unwindable input stream. + * You cannot use this API combining with functions defined + * in read.h: + * + * - getcFromInputFile + * - ungetcToInputFile + * - readLineFromInputFile + * - getInputLineNumber + * - getInputFilePosition + * - getInputLineOffset + * + * Instead, you can use + * - uwiGetC + * - uwiUngetC + * - uwiGetLineNumber + * - uwiGetFilePosition + * + * You can mark a position in the current input stream with + * uwiPushMarker(). + * Later, call uwiPopMarker() to unwind the input stream to the + * position where you marked. + * + * uwiPopMarker() takes COUNT as a parameter. It controls unwinding + * how many bytes. If -1 is passed as COUNT, unwinding to the marked + * position. + * + * If you find that you don't have to unwind though you called + * uwiPushMarker(), call uwiDropMaker(). + * + * uwiPopMarker() and uwiDropMaker() release internally allocated resources and + * clear the marker. + * + * If no marker is set, you can use uwiUngetC(). + */ + +/* +* INCLUDE FILES +*/ +#include "general.h" + +/* +* FUNCTION PROTOTYPES +*/ +extern void uwiActivate (void); +extern void uwiDeactivate (void); + +extern int uwiGetC (void); +extern void uwiUngetC (int c); +extern unsigned long uwiGetLineNumber (void); +extern MIOPos uwiGetFilePosition (void); + +extern void uwiPushMarker (void); +extern void uwiPopMarker (int count); +extern void uwiDropMaker (void); +#endif /* CTAGS_MAIN_UNWINDI_H */ diff --git a/ctags/main/vstring.c b/ctags/main/vstring.c index 2e33505b99..17c9bd92cb 100644 --- a/ctags/main/vstring.c +++ b/ctags/main/vstring.c @@ -130,6 +130,12 @@ extern void vStringNCatS ( stringCat (string, s, len); } +extern void vStringNCatSUnsafe ( + vString *const string, const char *const s, const size_t length) +{ + stringCat (string, s, length); +} + extern void vStringCat (vString *const string, const vString *const s) { size_t len = vStringLength (s); @@ -277,6 +283,14 @@ extern char *vStringDeleteUnwrap (vString *const string) return buffer; } +extern char *vStringStrdup (const vString *const string) +{ + char *str = xMalloc (vStringLength(string) + 1, char); + str[vStringLength(string)] = '\0'; + memcpy (str, string->buffer, vStringLength(string)); + return str; +} + static char valueToXDigit (int v) { Assert (v >= 0 && v <= 0xF); diff --git a/ctags/main/vstring.h b/ctags/main/vstring.h index 5ac421d43e..655557fd56 100644 --- a/ctags/main/vstring.h +++ b/ctags/main/vstring.h @@ -66,7 +66,15 @@ extern void vStringStripTrailing (vString *const string); extern void vStringCat (vString *const string, const vString *const s); extern void vStringCatS (vString *const string, const char *const s); extern void vStringNCat (vString *const string, const vString *const s, const size_t length); + +/* vStringNCatS calls strlen(S) thought it takes LENGTH because + * the handle the case that strlen(S) is smaller than LENGTH. + * + * In the case a caller knows strlen(S) equals to or is greater than LENGTH, + * calling strlen is just overhead. vStringNCatSUnsafe doesn't call strlen. */ extern void vStringNCatS (vString *const string, const char *const s, const size_t length); +extern void vStringNCatSUnsafe (vString *const string, const char *const s, const size_t length); + extern vString *vStringNewCopy (const vString *const string); extern vString *vStringNewInit (const char *const s); extern vString *vStringNewNInit (const char *const s, const size_t length); @@ -84,6 +92,7 @@ extern vString *vStringNewOrClearWithAutoRelease (vString *const string); extern vString *vStringNewOwn (char *s); extern char *vStringDeleteUnwrap (vString *const string); +extern char *vStringStrdup (const vString *const string); extern void vStringCatSWithEscaping (vString* b, const char *s); extern void vStringCatSWithEscapingAsPattern (vString *output, const char* input); @@ -102,4 +111,11 @@ CTAGS_INLINE void vStringPut (vString *const string, const int c) string->buffer [++string->length] = '\0'; } +CTAGS_INLINE void vStringPutWithLimit (vString *const string, const int c, + unsigned int maxlen) +{ + if (vStringLength (string) < maxlen || maxlen == 0) + vStringPut (string, c); +} + #endif /* CTAGS_MAIN_VSTRING_H */ diff --git a/ctags/main/writer-ctags.c b/ctags/main/writer-ctags.c index c9fca36c4d..0221448525 100644 --- a/ctags/main/writer-ctags.c +++ b/ctags/main/writer-ctags.c @@ -10,6 +10,8 @@ #include "general.h" /* must always come first */ #include "entry_p.h" +#include "field.h" +#include "field_p.h" #include "mio.h" #include "options_p.h" #include "parse_p.h" @@ -22,17 +24,18 @@ static int writeCtagsEntry (tagWriter *writer CTAGS_ATTR_UNUSED, - MIO * mio, const tagEntryInfo *const tag); + MIO * mio, const tagEntryInfo *const tag, + void *clientData); static int writeCtagsPtagEntry (tagWriter *writer CTAGS_ATTR_UNUSED, MIO * mio, const ptagDesc *desc, const char *const fileName, const char *const pattern, - const char *const parserName); -static void buildCtagsFqTagCache (tagWriter *writer CTAGS_ATTR_UNUSED, tagEntryInfo *const tag); + const char *const parserName, + void *clientData); +static bool treatFieldAsFixed (int fieldType); struct rejection { - bool rejectedInThisRendering; - bool rejectedInThisInput; + bool rejectionInThisInput; }; tagWriter uCtagsWriter = { @@ -40,26 +43,26 @@ tagWriter uCtagsWriter = { .writePtagEntry = writeCtagsPtagEntry, .preWriteEntry = NULL, .postWriteEntry = NULL, -#ifdef GEANY_CTAGS_LIB .rescanFailedEntry = NULL, -#endif /* GEANY_CTAGS_LIB */ - .buildFqTagCache = buildCtagsFqTagCache, + .treatFieldAsFixed = treatFieldAsFixed, .defaultFileName = CTAGS_FILE, }; -static void *beginECtagsFile (tagWriter *writer CTAGS_ATTR_UNUSED, MIO * mio CTAGS_ATTR_UNUSED) +static void *beginECtagsFile (tagWriter *writer CTAGS_ATTR_UNUSED, MIO * mio CTAGS_ATTR_UNUSED, + void *clientData CTAGS_ATTR_UNUSED) { static struct rejection rej; - rej.rejectedInThisInput = false; + rej.rejectionInThisInput = false; return &rej; } -static bool endECTagsFile (tagWriter *writer, MIO * mio CTAGS_ATTR_UNUSED, const char* filename CTAGS_ATTR_UNUSED) +static bool endECTagsFile (tagWriter *writer, MIO * mio CTAGS_ATTR_UNUSED, const char* filename CTAGS_ATTR_UNUSED, + void *clientData CTAGS_ATTR_UNUSED) { struct rejection *rej = writer->private; - return rej->rejectedInThisInput; + return rej->rejectionInThisInput; } tagWriter eCtagsWriter = { @@ -67,23 +70,92 @@ tagWriter eCtagsWriter = { .writePtagEntry = writeCtagsPtagEntry, .preWriteEntry = beginECtagsFile, .postWriteEntry = endECTagsFile, -#ifdef GEANY_CTAGS_LIB .rescanFailedEntry = NULL, -#endif /* GEANY_CTAGS_LIB */ - .buildFqTagCache = buildCtagsFqTagCache, + .treatFieldAsFixed = treatFieldAsFixed, .defaultFileName = CTAGS_FILE, }; -static const char* escapeFieldValue (tagWriter *writer, const tagEntryInfo * tag, fieldType ftype) +static bool hasTagEntryTabChar (const tagEntryInfo * const tag) { - bool *reject = NULL; - if (writer->private) + if (doesFieldHaveTabChar (FIELD_NAME, tag, NO_PARSER_FIELD) + || doesFieldHaveTabChar (FIELD_INPUT_FILE, tag, NO_PARSER_FIELD)) + return true; + + if (tag->lineNumberEntry) { - struct rejection * rej = writer->private; - reject = &rej->rejectedInThisRendering; + if (Option.lineDirectives) + { + if (doesFieldHaveTabChar (FIELD_LINE_NUMBER, tag, NO_PARSER_FIELD)) + return true; + } } - return renderFieldEscaped (writer->type, ftype, tag, NO_PARSER_FIELD, reject); + else if (doesFieldHaveTabChar (FIELD_PATTERN, tag, NO_PARSER_FIELD)) + { + /* Pattern may have a tab char. However, doesFieldHaveTabChar returns + * false because NO_PARSER_FIELD may not have hasTabChar handler. + */ + return true; + } + + if (includeExtensionFlags ()) + { + if (isFieldEnabled (FIELD_SCOPE) && doesFieldHaveValue (FIELD_SCOPE, tag) + && (doesFieldHaveTabChar (FIELD_SCOPE_KIND_LONG, tag, NO_PARSER_FIELD) + || doesFieldHaveTabChar (FIELD_SCOPE, tag, NO_PARSER_FIELD))) + return true; + if (isFieldEnabled (FIELD_TYPE_REF) && doesFieldHaveValue (FIELD_TYPE_REF, tag) + && doesFieldHaveTabChar (FIELD_TYPE_REF, tag, NO_PARSER_FIELD)) + return true; + if (isFieldEnabled (FIELD_FILE_SCOPE) && doesFieldHaveValue (FIELD_FILE_SCOPE, tag) + && doesFieldHaveTabChar (FIELD_FILE_SCOPE, tag, NO_PARSER_FIELD)) + return true; + + int f[] = { FIELD_INHERITANCE, + FIELD_ACCESS, + FIELD_IMPLEMENTATION, + FIELD_SIGNATURE, + FIELD_ROLES, + FIELD_EXTRAS, + FIELD_XPATH, + FIELD_END_LINE, + -1}; + for (unsigned int i = 0; f[i] >= 0; i++) + { + if (isFieldEnabled (f[i]) && doesFieldHaveValue (f[i], tag) + && doesFieldHaveTabChar (f[i], tag, NO_PARSER_FIELD)) + return true; + } + } + + for (unsigned int i = 0; i < tag->usedParserFields; i++) + { + const tagField *f = getParserField(tag, i); + fieldType ftype = f->ftype; + if (isFieldEnabled (ftype)) + { + if (doesFieldHaveTabChar (ftype, tag, i)) + return true; + } + } + return false; +} + + +static const char* escapeFieldValueFull (tagWriter *writer, const tagEntryInfo * tag, fieldType ftype, int fieldIndex) +{ + const char *v; + if (writer->type == WRITER_E_CTAGS && doesFieldHaveRenderer(ftype, true)) + v = renderFieldNoEscaping (ftype, tag, fieldIndex); + else + v = renderField (ftype, tag, fieldIndex); + + return v; +} + +static const char* escapeFieldValue (tagWriter *writer, const tagEntryInfo * tag, fieldType ftype) +{ + return escapeFieldValueFull (writer, tag, ftype, NO_PARSER_FIELD); } static int renderExtensionFieldMaybe (tagWriter *writer, int xftype, const tagEntryInfo *const tag, char sep[2], MIO *mio) @@ -105,24 +177,17 @@ static int addParserFields (tagWriter *writer, MIO * mio, const tagEntryInfo *co { unsigned int i; int length = 0; - bool *reject = NULL; - - if (writer->private) - { - struct rejection *rej = writer->private; - reject = &rej->rejectedInThisRendering; - } for (i = 0; i < tag->usedParserFields; i++) { const tagField *f = getParserField(tag, i); - if (! isFieldEnabled (f->ftype)) + fieldType ftype = f->ftype; + if (! isFieldEnabled (ftype)) continue; length += mio_printf(mio, "\t%s:%s", - getFieldName (f->ftype), - renderFieldEscaped (writer->type, - f->ftype, tag, i, reject)); + getFieldName (ftype), + escapeFieldValueFull (writer, tag, ftype, i)); } return length; } @@ -230,27 +295,36 @@ static int addExtensionFields (tagWriter *writer, MIO *mio, const tagEntryInfo * } static int writeCtagsEntry (tagWriter *writer, - MIO * mio, const tagEntryInfo *const tag) + MIO * mio, const tagEntryInfo *const tag, + void *clientData CTAGS_ATTR_UNUSED) { - long origin = 0; - if (writer->private) { struct rejection *rej = writer->private; - - origin = mio_tell (mio); - rej->rejectedInThisRendering = false; - + if (hasTagEntryTabChar (tag)) + { + rej->rejectionInThisInput = true; + return 0; + } } int length = mio_printf (mio, "%s\t%s\t", escapeFieldValue (writer, tag, FIELD_NAME), escapeFieldValue (writer, tag, FIELD_INPUT_FILE)); + /* This is for handling 'common' of 'fortran'. See the + description of --excmd=mixed in ctags.1. In tags output, what + we call "pattern" is instructions for vi. + + However, in the other formats, pattern should be pattern as its name. */ if (tag->lineNumberEntry) length += writeLineNumberEntry (writer, mio, tag); else + { + if (Option.locate == EX_COMBINE) + length += mio_printf(mio, "%lu;", tag->lineNumber + (Option.backward? 1: -1)); length += mio_puts(mio, escapeFieldValue(writer, tag, FIELD_PATTERN)); + } if (includeExtensionFlags ()) { @@ -260,16 +334,6 @@ static int writeCtagsEntry (tagWriter *writer, length += mio_printf (mio, "\n"); - if (writer->private - && ((struct rejection *)(writer->private))->rejectedInThisRendering) - { - mio_seek (mio, origin, SEEK_SET); - - /* Truncation is needed. */ - ((struct rejection *)(writer->private))->rejectedInThisInput = true; - - length = 0; - } return length; } @@ -277,7 +341,8 @@ static int writeCtagsPtagEntry (tagWriter *writer CTAGS_ATTR_UNUSED, MIO * mio, const ptagDesc *desc, const char *const fileName, const char *const pattern, - const char *const parserName) + const char *const parserName, + void *clientData CTAGS_ATTR_UNUSED) { return parserName @@ -291,8 +356,15 @@ static int writeCtagsPtagEntry (tagWriter *writer CTAGS_ATTR_UNUSED, #undef OPT } -static void buildCtagsFqTagCache (tagWriter *writer CTAGS_ATTR_UNUSED, tagEntryInfo *const tag) +static bool treatFieldAsFixed (int fieldType) { - escapeFieldValue (writer, tag, FIELD_SCOPE_KIND_LONG); - escapeFieldValue (writer, tag, FIELD_SCOPE); + switch (fieldType) + { + case FIELD_NAME: + case FIELD_INPUT_FILE: + case FIELD_PATTERN: + return true; + default: + return false; + } } diff --git a/ctags/main/writer-etags.c b/ctags/main/writer-etags.c index 2eb0adaa29..b47aea446f 100644 --- a/ctags/main/writer-etags.c +++ b/ctags/main/writer-etags.c @@ -16,6 +16,7 @@ #include "mio.h" #include "options_p.h" #include "read.h" +#include "routines.h" #include "routines_p.h" #include "vstring.h" #include "writer_p.h" @@ -24,18 +25,20 @@ #define ETAGS_FILE "TAGS" -static int writeEtagsEntry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag); -static void *beginEtagsFile (tagWriter *writer, MIO * mio); -static bool endEtagsFile (tagWriter *writer, MIO * mio, const char* filename); +static int writeEtagsEntry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag, + void *clientData CTAGS_ATTR_UNUSED); +static void *beginEtagsFile (tagWriter *writer, MIO * mio, + void *clientData CTAGS_ATTR_UNUSED); +static bool endEtagsFile (tagWriter *writer, MIO * mio, const char* filename, + void *clientData CTAGS_ATTR_UNUSED); tagWriter etagsWriter = { .writeEntry = writeEtagsEntry, .writePtagEntry = NULL, .preWriteEntry = beginEtagsFile, .postWriteEntry = endEtagsFile, -#ifdef GEANY_CTAGS_LIB .rescanFailedEntry = NULL, -#endif /* GEANY_CTAGS_LIB */ + .treatFieldAsFixed = NULL, .defaultFileName = ETAGS_FILE, }; @@ -48,7 +51,8 @@ struct sEtags { -static void *beginEtagsFile (tagWriter *writer CTAGS_ATTR_UNUSED, MIO *mio CTAGS_ATTR_UNUSED) +static void *beginEtagsFile (tagWriter *writer CTAGS_ATTR_UNUSED, MIO *mio CTAGS_ATTR_UNUSED, + void *clientData CTAGS_ATTR_UNUSED) { static struct sEtags etags = { NULL, NULL, 0, NULL }; @@ -59,7 +63,8 @@ static void *beginEtagsFile (tagWriter *writer CTAGS_ATTR_UNUSED, MIO *mio CTAGS } static bool endEtagsFile (tagWriter *writer, - MIO *mainfp, const char *filename) + MIO *mainfp, const char *filename, + void *clientData CTAGS_ATTR_UNUSED) { const char *line; struct sEtags *etags = writer->private; @@ -75,7 +80,7 @@ static bool endEtagsFile (tagWriter *writer, mio_puts (mainfp, line); vStringDelete (etags->vLine); - mio_free (etags->mio); + mio_unref (etags->mio); remove (etags->name); eFree (etags->name); etags->vLine = NULL; @@ -86,7 +91,8 @@ static bool endEtagsFile (tagWriter *writer, } static int writeEtagsEntry (tagWriter *writer, - MIO * mio, const tagEntryInfo *const tag) + MIO * mio, const tagEntryInfo *const tag, + void *clientData CTAGS_ATTR_UNUSED) { int length; struct sEtags *etags = writer->private; diff --git a/ctags/main/writer-json.c b/ctags/main/writer-json.c index 263126fd4b..df3b2e170c 100644 --- a/ctags/main/writer-json.c +++ b/ctags/main/writer-json.c @@ -11,9 +11,11 @@ #include "debug.h" #include "entry_p.h" +#include "field_p.h" #include "mio.h" #include "options_p.h" #include "read.h" +#include "routines.h" #include "ptag_p.h" #include "writer_p.h" @@ -29,31 +31,41 @@ static int writeJsonEntry (tagWriter *writer CTAGS_ATTR_UNUSED, - MIO * mio, const tagEntryInfo *const tag); + MIO * mio, const tagEntryInfo *const tag, + void *clientData); static int writeJsonPtagEntry (tagWriter *writer CTAGS_ATTR_UNUSED, MIO * mio, const ptagDesc *desc, const char *const fileName, const char *const pattern, - const char *const parserName); -static void buildJsonFqTagCache (tagWriter *writer, tagEntryInfo *const tag); + const char *const parserName, + void *clientData); tagWriter jsonWriter = { .writeEntry = writeJsonEntry, .writePtagEntry = writeJsonPtagEntry, .preWriteEntry = NULL, .postWriteEntry = NULL, -#ifdef GEANY_CTAGS_LIB .rescanFailedEntry = NULL, -#endif /* GEANY_CTAGS_LIB */ - .buildFqTagCache = buildJsonFqTagCache, + .treatFieldAsFixed = NULL, .defaultFileName = NULL, }; +static const char* escapeFieldValueRaw (const tagEntryInfo * tag, fieldType ftype, int fieldIndex) +{ + const char *v; + if (doesFieldHaveRenderer(ftype, true)) + v = renderFieldNoEscaping (ftype, tag, fieldIndex); + else + v = renderField (ftype, tag, fieldIndex); + + return v; +} static json_t* escapeFieldValue (const tagEntryInfo * tag, fieldType ftype, bool returnEmptyStringAsNoValue) { - const char *str = renderFieldEscaped (jsonWriter.type, ftype, tag, NO_PARSER_FIELD, NULL); + const char *str = escapeFieldValueRaw (tag, ftype, NO_PARSER_FIELD); + if (str) { unsigned int dt = getFieldDataType(ftype); @@ -92,7 +104,7 @@ static void renderExtensionFieldMaybe (int xftype, const tagEntryInfo *const tag { const char *fname = getFieldName (xftype); - if (fname && isFieldRenderable (xftype) && isFieldEnabled (xftype) && doesFieldHaveValue (xftype, tag)) + if (fname && doesFieldHaveRenderer (xftype, false) && isFieldEnabled (xftype) && doesFieldHaveValue (xftype, tag)) { switch (xftype) { @@ -114,15 +126,16 @@ static void renderExtensionFieldMaybe (int xftype, const tagEntryInfo *const tag static void addParserFields (json_t *response, const tagEntryInfo *const tag) { unsigned int i; - unsigned int ftype; for (i = 0; i < tag->usedParserFields; i++) { - ftype = tag->parserFields [i].ftype; + const tagField *f = getParserField(tag, i); + fieldType ftype = f->ftype; if (! isFieldEnabled (ftype)) continue; - json_object_set_new (response, getFieldName (ftype), json_string (tag->parserFields [i].value)); + const char *str = escapeFieldValueRaw (tag, ftype, i); + json_object_set_new (response, getFieldName (ftype), json_string (str)); } } @@ -151,15 +164,26 @@ static void addExtensionFields (json_t *response, const tagEntryInfo *const tag) } static int writeJsonEntry (tagWriter *writer CTAGS_ATTR_UNUSED, - MIO * mio, const tagEntryInfo *const tag) + MIO * mio, const tagEntryInfo *const tag, + void *clientData CTAGS_ATTR_UNUSED) { - json_t *pat = escapeFieldValue(tag, FIELD_PATTERN, true); - json_t *response = json_pack ("{ss ss ss sO}", - "_type", "tag", - "name", tag->name, - "path", tag->sourceFileName, - "pattern", pat); - json_decref (pat); + int length = 0; + json_t *response = json_pack ("{ss}", "_type", "tag"); + + if (isFieldEnabled (FIELD_NAME)) + { + json_t *name = json_string (tag->name); + if (name == NULL) + goto out; + json_object_set_new (response, "name", name); + } + if (isFieldEnabled (FIELD_INPUT_FILE)) + json_object_set_new (response, "path", json_string (tag->sourceFileName)); + if (isFieldEnabled (FIELD_PATTERN)) + { + json_t *pat = escapeFieldValue(tag, FIELD_PATTERN, true); + json_object_set_new (response, "pattern", pat); + } if (includeExtensionFlags ()) { @@ -167,11 +191,11 @@ static int writeJsonEntry (tagWriter *writer CTAGS_ATTR_UNUSED, addParserFields (response, tag); } - int length = 0; - char *buf = json_dumps (response, JSON_PRESERVE_ORDER); - if (!buf) + /* Print nothing if RESPONSE has only "_type" field. */ + if (json_object_size (response) == 1) goto out; + char *buf = json_dumps (response, JSON_PRESERVE_ORDER); length = mio_printf (mio, "%s\n", buf); free (buf); @@ -181,19 +205,12 @@ static int writeJsonEntry (tagWriter *writer CTAGS_ATTR_UNUSED, return length; } -static void buildJsonFqTagCache (tagWriter *writer, tagEntryInfo *const tag) -{ - renderFieldEscaped (writer->type, FIELD_SCOPE_KIND_LONG, tag, - NO_PARSER_FIELD, NULL); - renderFieldEscaped (writer->type, FIELD_SCOPE, tag, - NO_PARSER_FIELD, NULL); -} - static int writeJsonPtagEntry (tagWriter *writer CTAGS_ATTR_UNUSED, MIO * mio, const ptagDesc *desc, const char *const fileName, const char *const pattern, - const char *const parserName) + const char *const parserName, + void *clientData CTAGS_ATTR_UNUSED) { #define OPT(X) ((X)?(X):"") json_t *response; diff --git a/ctags/main/writer-xref.c b/ctags/main/writer-xref.c index 4e410800ef..a107e7208d 100644 --- a/ctags/main/writer-xref.c +++ b/ctags/main/writer-xref.c @@ -10,6 +10,7 @@ #include "general.h" /* must always come first */ #include "entry.h" +#include "field_p.h" #include "fmt_p.h" #include "mio.h" #include "options_p.h" @@ -17,23 +18,22 @@ static int writeXrefEntry (tagWriter *writer CTAGS_ATTR_UNUSED, - MIO * mio, const tagEntryInfo *const tag); -static void buildXrefFqTagCache (tagWriter *writer, tagEntryInfo *const tag); + MIO * mio, const tagEntryInfo *const tag, + void *clientData CTAGS_ATTR_UNUSED); tagWriter xrefWriter = { .writeEntry = writeXrefEntry, .writePtagEntry = NULL, .preWriteEntry = NULL, .postWriteEntry = NULL, -#ifdef GEANY_CTAGS_LIB .rescanFailedEntry = NULL, -#endif /* GEANY_CTAGS_LIB */ - .buildFqTagCache = buildXrefFqTagCache, + .treatFieldAsFixed = NULL, .defaultFileName = NULL, }; static int writeXrefEntry (tagWriter *writer CTAGS_ATTR_UNUSED, - MIO * mio, const tagEntryInfo *const tag) + MIO * mio, const tagEntryInfo *const tag, + void *clientData CTAGS_ATTR_UNUSED) { int length; static fmtElement *fmt1; @@ -65,11 +65,3 @@ static int writeXrefEntry (tagWriter *writer CTAGS_ATTR_UNUSED, return length; } - -static void buildXrefFqTagCache (tagWriter *writer, tagEntryInfo *const tag) -{ - renderFieldEscaped (writer->type, FIELD_SCOPE_KIND_LONG, tag, - NO_PARSER_FIELD, NULL); - renderFieldEscaped (writer->type, FIELD_SCOPE, tag, - NO_PARSER_FIELD, NULL); -} diff --git a/ctags/main/writer.c b/ctags/main/writer.c index 1c01a4dac7..ad595a144f 100644 --- a/ctags/main/writer.c +++ b/ctags/main/writer.c @@ -23,27 +23,27 @@ static tagWriter *writerTable [WRITER_COUNT] = { [WRITER_ETAGS] = &etagsWriter, [WRITER_XREF] = &xrefWriter, [WRITER_JSON] = &jsonWriter, + [WRITER_CUSTOM] = NULL, }; static tagWriter *writer; -#ifdef GEANY_CTAGS_LIB -extern void geanySetTagWriter(tagWriter *w) +extern void setTagWriter (writerType wtype, tagWriter *customWriter) { - writer = w; -} -#endif /* GEANY_CTAGS_LIB */ - -extern void setTagWriter (writerType wtype) -{ - writer = writerTable [wtype]; + if (wtype != WRITER_CUSTOM) + writer = writerTable [wtype]; + else + writer = customWriter; writer->type = wtype; } -extern void writerSetup (MIO *mio) +extern void writerSetup (MIO *mio, void *clientData) { + writer->clientData = clientData; + if (writer->preWriteEntry) - writer->private = writer->preWriteEntry (writer, mio); + writer->private = writer->preWriteEntry (writer, mio, + writer->clientData); else writer->private = NULL; } @@ -53,7 +53,8 @@ extern bool writerTeardown (MIO *mio, const char *filename) if (writer->postWriteEntry) { bool r; - r = writer->postWriteEntry (writer, mio, filename); + r = writer->postWriteEntry (writer, mio, filename, + writer->clientData); writer->private = NULL; return r; } @@ -62,7 +63,8 @@ extern bool writerTeardown (MIO *mio, const char *filename) extern int writerWriteTag (MIO * mio, const tagEntryInfo *const tag) { - return writer->writeEntry (writer, mio, tag); + return writer->writeEntry (writer, mio, tag, + writer->clientData); } extern int writerWritePtag (MIO * mio, @@ -75,24 +77,16 @@ extern int writerWritePtag (MIO * mio, return -1; return writer->writePtagEntry (writer, mio, desc, fileName, - pattern, parserName); + pattern, parserName, + writer->clientData); } -#ifdef GEANY_CTAGS_LIB extern void writerRescanFailed (unsigned long validTagNum) { if (writer->rescanFailedEntry) - writer->rescanFailedEntry(writer, validTagNum); + writer->rescanFailedEntry(writer, validTagNum, writer->clientData); } -#endif /* GEANY_CTAGS_LIB */ - -extern void writerBuildFqTagCache (tagEntryInfo *const tag) -{ - if (writer->buildFqTagCache) - writer->buildFqTagCache (writer, tag); -} - extern bool ptagMakeCtagsOutputMode (ptagDesc *desc, void *data CTAGS_ATTR_UNUSED) { @@ -118,3 +112,10 @@ extern bool writerCanPrintPtag (void) { return (writer->writePtagEntry)? true: false; } + +extern bool writerDoesTreatFieldAsFixed (int fieldType) +{ + if (writer->treatFieldAsFixed) + return writer->treatFieldAsFixed (fieldType); + return false; +} diff --git a/ctags/main/writer_p.h b/ctags/main/writer_p.h index ca92feb0a4..21ecfe8f4b 100644 --- a/ctags/main/writer_p.h +++ b/ctags/main/writer_p.h @@ -25,37 +25,44 @@ typedef enum eWriterType { WRITER_ETAGS, WRITER_XREF, WRITER_JSON, + WRITER_CUSTOM, WRITER_COUNT, } writerType; struct sTagWriter; typedef struct sTagWriter tagWriter; struct sTagWriter { - int (* writeEntry) (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag); + int (* writeEntry) (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag, + void *clientData); int (* writePtagEntry) (tagWriter *writer, MIO * mio, const ptagDesc *desc, const char *const fileName, const char *const pattern, - const char *const parserName); - void * (* preWriteEntry) (tagWriter *writer, MIO * mio); + const char *const parserName, + void *clientData); + void * (* preWriteEntry) (tagWriter *writer, MIO * mio, + void *clientData); /* Returning TRUE means the output file may be shrunk. In such case the callee may do truncate output file. */ - bool (* postWriteEntry) (tagWriter *writer, MIO * mio, const char* filename); -#ifdef GEANY_CTAGS_LIB - void (* rescanFailedEntry) (tagWriter *writer, unsigned long validTagNum); -#endif /* GEANY_CTAGS_LIB */ - void (* buildFqTagCache) (tagWriter *writer, tagEntryInfo *const tag); + bool (* postWriteEntry) (tagWriter *writer, MIO * mio, const char* filename, + void *clientData); + void (* rescanFailedEntry) (tagWriter *writer, unsigned long validTagNum, + void *clientData); + bool (* treatFieldAsFixed) (int fieldType); const char *defaultFileName; /* The value returned from preWriteEntry is stored `private' field. The value must be released in postWriteEntry. */ void *private; writerType type; - + /* The value passed as the second argument for writerSetup iss + * stored here. Unlink `private' field, ctags does nothing more. */ + void *clientData; }; -extern void setTagWriter (writerType otype); -extern void writerSetup (MIO *mio); +/* customWriter is used only if otype is WRITER_CUSTOM */ +extern void setTagWriter (writerType otype, tagWriter *customWriter); +extern void writerSetup (MIO *mio, void *clientData); extern bool writerTeardown (MIO *mio, const char *filename); int writerWriteTag (MIO * mio, const tagEntryInfo *const tag); @@ -64,12 +71,8 @@ int writerWritePtag (MIO * mio, const char *const fileName, const char *const pattern, const char *const parserName); -#ifdef GEANY_CTAGS_LIB -extern void geanySetTagWriter(tagWriter *w); -void writerRescanFailed (unsigned long validTagNum); -#endif -extern void writerBuildFqTagCache (tagEntryInfo *const tag); +void writerRescanFailed (unsigned long validTagNum); extern const char *outputDefaultFileName (void); @@ -81,5 +84,6 @@ extern bool ptagMakeJsonOutputVersion (ptagDesc *desc, void *data CTAGS_ATTR_UNU extern bool ptagMakeCtagsOutputMode (ptagDesc *desc, void *data CTAGS_ATTR_UNUSED); extern bool writerCanPrintPtag (void); +extern bool writerDoesTreatFieldAsFixed (int fieldType); #endif /* CTAGS_MAIN_WRITER_PRIVATE_H */ diff --git a/ctags/main/xtag.c b/ctags/main/xtag.c index 6371e93f6e..3ece50f4f5 100644 --- a/ctags/main/xtag.c +++ b/ctags/main/xtag.c @@ -13,13 +13,14 @@ #include "general.h" /* must always come first */ #include "ctags.h" #include "debug.h" -#include "main.h" #include "options.h" +#include "options_p.h" #include "parse_p.h" #include "routines.h" #include "trashbox.h" #include "writer_p.h" #include "xtag.h" +#include "xtag_p.h" #include #include diff --git a/ctags/main/xtag.h b/ctags/main/xtag.h index 4240ad9197..0eccddd40e 100644 --- a/ctags/main/xtag.h +++ b/ctags/main/xtag.h @@ -12,9 +12,15 @@ #ifndef CTAGS_MAIN_XTAG_H #define CTAGS_MAIN_XTAG_H +/* +* INCLUDE FILES +*/ + #include "general.h" -#include "colprint_p.h" +/* +* DATA DECLARATIONS +*/ typedef enum eXtagType { /* extra tag content control */ XTAG_UNKNOWN = -1, @@ -56,28 +62,6 @@ struct sXtagDefinition { unsigned int xtype; /* Given from the main part */ }; -extern xtagDefinition* getXtagDefinition (xtagType type); -extern xtagType getXtagTypeForLetter (char letter); -extern xtagType getXtagTypeForNameAndLanguage (const char *name, langType language); extern bool isXtagEnabled (xtagType type); -extern bool enableXtag (xtagType type, bool state); -extern bool isXtagFixed (xtagType type); -extern bool isCommonXtag (xtagType type); -extern int getXtagOwner (xtagType type); - -const char* getXtagName (xtagType type); - -extern void initXtagObjects (void); -extern int countXtags (void); - -extern int defineXtag (xtagDefinition *def, langType language); -extern xtagType nextSiblingXtag (xtagType type); - -/* --list-extras implementation. LANGUAGE must be initialized. */ -extern struct colprintTable * xtagColprintTableNew (void); -extern void xtagColprintAddCommonLines (struct colprintTable *table); -extern void xtagColprintAddLanguageLines (struct colprintTable *table, langType language); -extern void xtagColprintTablePrint (struct colprintTable *table, - bool withListHeader, bool machinable, FILE *fp); #endif /* CTAGS_MAIN_FIELD_H */ diff --git a/ctags/main/xtag_p.h b/ctags/main/xtag_p.h new file mode 100644 index 0000000000..c0bc527649 --- /dev/null +++ b/ctags/main/xtag_p.h @@ -0,0 +1,51 @@ +/* + * + * Copyright (c) 2015, Red Hat, Inc. + * Copyright (c) 2015, Masatake YAMATO + * + * Author: Masatake YAMATO + * + * This source code is released for free distribution under the terms of the + * GNU General Public License version 2 or (at your option) any later version. + * + */ +#ifndef CTAGS_MAIN_XTAG_PRIVATE_H +#define CTAGS_MAIN_XTAG_PRIVATE_H + +/* +* INCLUDE FILES +*/ + +#include "general.h" + +#include "colprint_p.h" + +/* +* FUNCTION PROTOTYPES +*/ + +extern xtagDefinition* getXtagDefinition (xtagType type); +extern xtagType getXtagTypeForLetter (char letter); +extern xtagType getXtagTypeForNameAndLanguage (const char *name, langType language); + +extern bool enableXtag (xtagType type, bool state); +extern bool isXtagFixed (xtagType type); +extern bool isCommonXtag (xtagType type); +extern int getXtagOwner (xtagType type); + +const char* getXtagName (xtagType type); + +extern void initXtagObjects (void); +extern int countXtags (void); + +extern int defineXtag (xtagDefinition *def, langType language); +extern xtagType nextSiblingXtag (xtagType type); + +/* --list-extras implementation. LANGUAGE must be initialized. */ +extern struct colprintTable * xtagColprintTableNew (void); +extern void xtagColprintAddCommonLines (struct colprintTable *table); +extern void xtagColprintAddLanguageLines (struct colprintTable *table, langType language); +extern void xtagColprintTablePrint (struct colprintTable *table, + bool withListHeader, bool machinable, FILE *fp); + +#endif /* CTAGS_MAIN_FIELD_PRIVATE_H */ diff --git a/ctags/parsers/go.c b/ctags/parsers/go.c index d7b5981b5e..fc8921a768 100644 --- a/ctags/parsers/go.c +++ b/ctags/parsers/go.c @@ -10,7 +10,6 @@ #include "entry.h" #include "keyword.h" #include "read.h" -#include "main.h" #include "parse.h" #include "routines.h" #include "vstring.h" diff --git a/ctags/parsers/haxe.c b/ctags/parsers/haxe.c index 954d57107b..aa4877fb27 100644 --- a/ctags/parsers/haxe.c +++ b/ctags/parsers/haxe.c @@ -17,7 +17,6 @@ #include #endif #include -#include "main.h" #include "entry.h" #include "keyword.h" #include "parse.h" diff --git a/ctags/parsers/nsis.c b/ctags/parsers/nsis.c index 39441e8433..a06a81508b 100644 --- a/ctags/parsers/nsis.c +++ b/ctags/parsers/nsis.c @@ -17,7 +17,6 @@ #include "parse.h" #include "read.h" -#include "main.h" #include "vstring.h" #include "routines.h" diff --git a/ctags/parsers/powershell.c b/ctags/parsers/powershell.c index 9a7d0309a2..e92a1f3255 100644 --- a/ctags/parsers/powershell.c +++ b/ctags/parsers/powershell.c @@ -15,7 +15,6 @@ */ #include "general.h" /* must always come first */ #include "debug.h" -#include "main.h" #include "parse.h" #include "read.h" #include "vstring.h" diff --git a/ctags/parsers/python.c b/ctags/parsers/python.c index fc35ecf640..96fbdd6fa1 100644 --- a/ctags/parsers/python.c +++ b/ctags/parsers/python.c @@ -18,7 +18,6 @@ #include "nestlevel.h" #include "options.h" #include "read.h" -#include "main.h" #include "parse.h" #include "vstring.h" #include "routines.h" diff --git a/ctags/parsers/rust.c b/ctags/parsers/rust.c index a9348cec15..017afefccc 100644 --- a/ctags/parsers/rust.c +++ b/ctags/parsers/rust.c @@ -10,7 +10,6 @@ * INCLUDE FILES */ #include "general.h" /* must always come first */ -#include "main.h" #include diff --git a/src/tagmanager/tm_ctags.c b/src/tagmanager/tm_ctags.c index 310d45d699..b6d17256cc 100644 --- a/src/tagmanager/tm_ctags.c +++ b/src/tagmanager/tm_ctags.c @@ -7,23 +7,19 @@ * Encapsulates ctags so it is isolated from the rest of Geany. */ -#include "general.h" /* must always come first */ - #include "tm_ctags.h" -#include "types.h" -#include "routines.h" -#include "error.h" -#include "mio.h" -#include "writer_p.h" -#include "parse_p.h" -#include "options_p.h" -#include "trashbox.h" -#include "field.h" -#include "xtag.h" -#include "entry_p.h" - #include "tm_tag.h" +#include "general.h" /* must always come before the rest of ctags headers */ +#include "entry_p.h" +#include "error_p.h" +#include "field_p.h" +#include "options_p.h" +#include "parse_p.h" +#include "trashbox_p.h" +#include "writer_p.h" +#include "xtag_p.h" + #include #include #include @@ -31,8 +27,8 @@ #define CTAGS_LANG(x) ((x) >= 0 ? (x) + 1 : (x)) #define GEANY_LANG(x) ((x) >= 1 ? (x) - 1 : (x)) -static gint write_entry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag); -static void rescan_failed (tagWriter *writer, gulong valid_tag_num); +static gint write_entry(tagWriter *writer, MIO * mio, const tagEntryInfo *const tag, void *user_data); +static void rescan_failed(tagWriter *writer, gulong valid_tag_num, void *user_data); tagWriter geanyWriter = { .writeEntry = write_entry, @@ -40,14 +36,12 @@ tagWriter geanyWriter = { .preWriteEntry = NULL, .postWriteEntry = NULL, .rescanFailedEntry = rescan_failed, - .buildFqTagCache = NULL, + .treatFieldAsFixed = NULL, .defaultFileName = "geany_tags_file_which_should_never_appear_anywhere", .private = NULL, .type = WRITER_U_CTAGS /* not really but we must use some of the builtin types */ }; -static TMSourceFile *current_source_file = NULL; - static bool nonfatal_error_printer (const errorSelection selection, const gchar *const format, @@ -165,8 +159,9 @@ static void update_python_arglist(const TMTag *tag, TMSourceFile *current_source } -static gint write_entry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag) +static gint write_entry(tagWriter *writer, MIO * mio, const tagEntryInfo *const tag, void *user_data) { + TMSourceFile *current_source_file = user_data; TMTag *tm_tag = tm_tag_new(); getTagScopeInformation((tagEntryInfo *)tag, NULL, NULL); @@ -187,8 +182,9 @@ static gint write_entry (tagWriter *writer, MIO * mio, const tagEntryInfo *const } -static void rescan_failed (tagWriter *writer, gulong valid_tag_num) +static void rescan_failed(tagWriter *writer, gulong valid_tag_num, void *user_data) { + TMSourceFile *current_source_file = user_data; GPtrArray *tags_array = current_source_file->tags_array; if (tags_array->len > valid_tag_num) @@ -207,7 +203,7 @@ void tm_ctags_init(void) initDefaultTrashBox (); setErrorPrinter (nonfatal_error_printer, NULL); - geanySetTagWriter (&geanyWriter); + setTagWriter (WRITER_CUSTOM, &geanyWriter); checkRegex (); initFieldObjects (); @@ -237,9 +233,7 @@ void tm_ctags_parse(guchar *buffer, gsize buffer_size, return; } - current_source_file = source_file; - - geanyCreateTags(buffer, buffer_size, file_name, CTAGS_LANG(language)); + parseRawBuffer(file_name, buffer, buffer_size, CTAGS_LANG(language), source_file); } diff --git a/src/tagmanager/tm_workspace.c b/src/tagmanager/tm_workspace.c index eb2a537509..4d206141b5 100644 --- a/src/tagmanager/tm_workspace.c +++ b/src/tagmanager/tm_workspace.c @@ -17,8 +17,6 @@ and a set of individual source files. */ -#include "general.h" - #include #include #include From 7559ed2a9eda4a27e26297c64cd24599ac568ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Sun, 19 May 2019 13:31:20 -0700 Subject: [PATCH 37/46] Rename current_source_file to just source_file --- src/tagmanager/tm_ctags.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/tagmanager/tm_ctags.c b/src/tagmanager/tm_ctags.c index b6d17256cc..1f66c78157 100644 --- a/src/tagmanager/tm_ctags.c +++ b/src/tagmanager/tm_ctags.c @@ -130,7 +130,7 @@ static gboolean init_tag(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag /* add argument list of __init__() Python methods to the class tag */ -static void update_python_arglist(const TMTag *tag, TMSourceFile *current_source_file) +static void update_python_arglist(const TMTag *tag, TMSourceFile *source_file) { guint i; const gchar *parent_tag_name; @@ -146,9 +146,9 @@ static void update_python_arglist(const TMTag *tag, TMSourceFile *current_source parent_tag_name = tag->scope; /* going in reverse order because the tag was added recently */ - for (i = current_source_file->tags_array->len; i > 0; i--) + for (i = source_file->tags_array->len; i > 0; i--) { - TMTag *prev_tag = (TMTag *) current_source_file->tags_array->pdata[i - 1]; + TMTag *prev_tag = (TMTag *) source_file->tags_array->pdata[i - 1]; if (g_strcmp0(prev_tag->name, parent_tag_name) == 0) { g_free(prev_tag->arglist); @@ -161,21 +161,21 @@ static void update_python_arglist(const TMTag *tag, TMSourceFile *current_source static gint write_entry(tagWriter *writer, MIO * mio, const tagEntryInfo *const tag, void *user_data) { - TMSourceFile *current_source_file = user_data; + TMSourceFile *source_file = user_data; TMTag *tm_tag = tm_tag_new(); getTagScopeInformation((tagEntryInfo *)tag, NULL, NULL); - if (!init_tag(tm_tag, current_source_file, tag)) + if (!init_tag(tm_tag, source_file, tag)) { tm_tag_unref(tm_tag); return 0; } if (tm_tag->lang == TM_PARSER_PYTHON) - update_python_arglist(tm_tag, current_source_file); + update_python_arglist(tm_tag, source_file); - g_ptr_array_add(current_source_file->tags_array, tm_tag); + g_ptr_array_add(source_file->tags_array, tm_tag); /* output length - we don't write anything to the MIO */ return 0; @@ -184,8 +184,8 @@ static gint write_entry(tagWriter *writer, MIO * mio, const tagEntryInfo *const static void rescan_failed(tagWriter *writer, gulong valid_tag_num, void *user_data) { - TMSourceFile *current_source_file = user_data; - GPtrArray *tags_array = current_source_file->tags_array; + TMSourceFile *source_file = user_data; + GPtrArray *tags_array = source_file->tags_array; if (tags_array->len > valid_tag_num) { From 4a8ea44436969c1d32b557686934a8ec9dd7eae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Sun, 19 May 2019 13:33:02 -0700 Subject: [PATCH 38/46] Whitespace --- src/tagmanager/Makefile.am | 2 +- src/tagmanager/tm_ctags.c | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/tagmanager/Makefile.am b/src/tagmanager/Makefile.am index 15ddb8afe6..f1439667b0 100644 --- a/src/tagmanager/Makefile.am +++ b/src/tagmanager/Makefile.am @@ -17,7 +17,7 @@ tagmanager_include_HEADERS = \ tm_parser.h -libtagmanager_la_SOURCES =\ +libtagmanager_la_SOURCES = \ tm_ctags.h \ tm_ctags.c \ tm_parser.h \ diff --git a/src/tagmanager/tm_ctags.c b/src/tagmanager/tm_ctags.c index 1f66c78157..4d828bd6c7 100644 --- a/src/tagmanager/tm_ctags.c +++ b/src/tagmanager/tm_ctags.c @@ -43,7 +43,7 @@ tagWriter geanyWriter = { }; -static bool nonfatal_error_printer (const errorSelection selection, +static bool nonfatal_error_printer(const errorSelection selection, const gchar *const format, va_list ap, void *data CTAGS_ATTR_UNUSED) { @@ -200,20 +200,20 @@ static void rescan_failed(tagWriter *writer, gulong valid_tag_num, void *user_da /* keep in sync with ctags main() - use only things interesting for us */ void tm_ctags_init(void) { - initDefaultTrashBox (); + initDefaultTrashBox(); - setErrorPrinter (nonfatal_error_printer, NULL); - setTagWriter (WRITER_CUSTOM, &geanyWriter); + setErrorPrinter(nonfatal_error_printer, NULL); + setTagWriter(WRITER_CUSTOM, &geanyWriter); - checkRegex (); - initFieldObjects (); - initXtagObjects (); + checkRegex(); + initFieldObjects(); + initXtagObjects(); - initializeParsing (); - initOptions (); + initializeParsing(); + initOptions(); /* make sure all parsers are initialized */ - initializeParser (LANG_AUTO); + initializeParser(LANG_AUTO); /* change default values which are false */ enableXtag(XTAG_TAGS_GENERATED_BY_GUEST_PARSERS, true); @@ -265,14 +265,14 @@ const gchar *tm_ctags_get_lang_kinds(TMParserType lang) const gchar *tm_ctags_get_kind_name(gchar kind, TMParserType lang) { - kindDefinition *def = getLanguageKindForLetter (CTAGS_LANG(lang), kind); + kindDefinition *def = getLanguageKindForLetter(CTAGS_LANG(lang), kind); return def ? def->name : "unknown"; } gchar tm_ctags_get_kind_from_name(const gchar *name, TMParserType lang) { - kindDefinition *def = getLanguageKindForName (CTAGS_LANG(lang), name); + kindDefinition *def = getLanguageKindForName(CTAGS_LANG(lang), name); return def ? def->letter : '-'; } From 75c8c6c829f481d0b75c5d70eb1f51b3334234c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Sun, 19 May 2019 13:53:49 -0700 Subject: [PATCH 39/46] Move lcpp.c/h inside parsers dir This corresponds to cpreprocessor.c/h of upstream ctags which lives in the parsers directory too. Let's keep the "lcpp" name though - if we update to the latest cxx parser, we'll have to keep both implementations of the preprocessor because of the big diffs between our c.c and ctags c.c. --- ctags/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ctags/Makefile.am b/ctags/Makefile.am index b9036d5e65..438a37c2f2 100644 --- a/ctags/Makefile.am +++ b/ctags/Makefile.am @@ -29,6 +29,8 @@ parsers = \ parsers/html.c \ parsers/jscript.c \ parsers/json.c \ + parsers/lcpp.c \ + parsers/lcpp.h \ parsers/lua.c \ parsers/make.c \ parsers/markdown.c \ @@ -90,8 +92,6 @@ libctags_la_SOURCES = \ main/kind.c \ main/kind.h \ main/kind_p.h \ - main/lcpp.c \ - main/lcpp.h \ main/lregex.c \ main/lregex.h \ main/lregex_p.h \ From 1c4dcae9a6fa71eab1b5471e95c89168a0fb5b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Mon, 20 May 2019 03:59:43 -0700 Subject: [PATCH 40/46] Use g_return_if_fail() and remove unneeded includes --- src/tagmanager/tm_ctags.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/tagmanager/tm_ctags.c b/src/tagmanager/tm_ctags.c index 4d828bd6c7..5dcfdd5cca 100644 --- a/src/tagmanager/tm_ctags.c +++ b/src/tagmanager/tm_ctags.c @@ -20,10 +20,6 @@ #include "writer_p.h" #include "xtag_p.h" -#include -#include -#include - #define CTAGS_LANG(x) ((x) >= 0 ? (x) + 1 : (x)) #define GEANY_LANG(x) ((x) >= 1 ? (x) - 1 : (x)) @@ -227,11 +223,7 @@ void tm_ctags_init(void) void tm_ctags_parse(guchar *buffer, gsize buffer_size, const gchar *file_name, TMParserType language, TMSourceFile *source_file) { - if (buffer == NULL && file_name == NULL) - { - error(FATAL, "Neither buffer nor file provided to ctagsParse()"); - return; - } + g_return_if_fail(buffer != NULL || file_name != NULL); parseRawBuffer(file_name, buffer, buffer_size, CTAGS_LANG(language), source_file); } From 5c35fa79575a80498b18500c2d0c8e1904418a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Tue, 21 May 2019 04:05:59 -0700 Subject: [PATCH 41/46] Actually move the lcpp files themselves --- ctags/{main => parsers}/lcpp.c | 0 ctags/{main => parsers}/lcpp.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename ctags/{main => parsers}/lcpp.c (100%) rename ctags/{main => parsers}/lcpp.h (100%) diff --git a/ctags/main/lcpp.c b/ctags/parsers/lcpp.c similarity index 100% rename from ctags/main/lcpp.c rename to ctags/parsers/lcpp.c diff --git a/ctags/main/lcpp.h b/ctags/parsers/lcpp.h similarity index 100% rename from ctags/main/lcpp.h rename to ctags/parsers/lcpp.h From 361c058b3f727a3d4cda9e4908b588f98f151f83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Tue, 21 May 2019 04:21:06 -0700 Subject: [PATCH 42/46] Make sure we compile all sources from upstream main 1. Update Makefile.am to contain all sources from ctags main in alphabetic order 2. Remove our diff from routines_p.h thanks to checking for dirent.h in our configure.ac 3. Basically copy-over ctags/main contents, except our parsers_p.h and our small diff in parser.c related to anon tags. --- configure.ac | 2 +- ctags/Makefile.am | 17 +++++++++++------ ctags/main/portable-dirent_p.h | 2 +- ctags/main/repoinfo.h | 2 +- ctags/main/routines_p.h | 2 -- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/configure.ac b/configure.ac index 238ea395de..046391ac60 100644 --- a/configure.ac +++ b/configure.ac @@ -44,7 +44,7 @@ AC_CHECK_HEADERS([fcntl.h glob.h stdlib.h sys/time.h errno.h limits.h]) # Checks for dependencies needed by ctags AC_CHECK_HEADERS([fnmatch.h direct.h io.h sys/dir.h]) AC_DEFINE([HAVE_STDBOOL_H], [1], [whether or not to use .]) -AC_DEFINE([GEANY_CTAGS_LIB], [1], [compile ctags as a library.]) +AC_CHECK_HEADERS(dirent.h,have_dirent_h=yes) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_OFF_T diff --git a/ctags/Makefile.am b/ctags/Makefile.am index 438a37c2f2..ff7778becf 100644 --- a/ctags/Makefile.am +++ b/ctags/Makefile.am @@ -54,22 +54,23 @@ parsers = \ parsers/verilog.c \ parsers/vhdl.c +# skip cmd.c and mini-geany.c which define main() libctags_la_SOURCES = \ main/args.c \ main/args_p.h \ main/colprint.c \ main/colprint_p.h \ main/ctags.h \ - main/debug.h \ main/debug.c \ + main/debug.h \ + main/dependency.c \ main/dependency.h \ main/dependency_p.h \ - main/dependency.c \ main/e_msoft.h \ main/entry.c \ - main/entry_private.c \ main/entry.h \ main/entry_p.h \ + main/entry_private.c \ main/error.c \ main/error_p.h \ main/field.c \ @@ -120,7 +121,9 @@ libctags_la_SOURCES = \ main/parse.c \ main/parse.h \ main/parse_p.h \ - main/parsers.h \ + main/parsers_p.h \ + main/portable-dirent_p.h \ + main/portable-scandir.c \ main/promise.c \ main/promise.h \ main/promise_p.h \ @@ -130,11 +133,13 @@ libctags_la_SOURCES = \ main/ptrarray.h \ main/read.c \ main/read.h \ + main/read_p.h \ main/repoinfo.c \ main/repoinfo.h \ main/routines.c \ main/routines.h \ main/routines_p.h \ + main/seccomp.c \ main/selectors.c \ main/selectors.h \ main/sort.c \ @@ -157,12 +162,12 @@ libctags_la_SOURCES = \ main/unwindi.h \ main/vstring.c \ main/vstring.h \ - main/writer.c \ - main/writer_p.h \ main/writer-ctags.c \ main/writer-etags.c \ main/writer-json.c \ main/writer-xref.c \ + main/writer.c \ + main/writer_p.h \ main/xtag.c \ main/xtag.h \ main/xtag_p.h \ diff --git a/ctags/main/portable-dirent_p.h b/ctags/main/portable-dirent_p.h index 44a7d257ab..6f053ede3f 100644 --- a/ctags/main/portable-dirent_p.h +++ b/ctags/main/portable-dirent_p.h @@ -943,5 +943,5 @@ dirent_set_errno( #endif /*DIRENT_H*/ #else -/*"dirent.h is not available."*/ +#error "dirent.h is not available." #endif /* HAVE_DIRENT_H */ diff --git a/ctags/main/repoinfo.h b/ctags/main/repoinfo.h index c8d71ba40c..6744a80cc4 100644 --- a/ctags/main/repoinfo.h +++ b/ctags/main/repoinfo.h @@ -1 +1 @@ -#define CTAGS_REPOINFO "75c26bdf" +#define CTAGS_REPOINFO "ad3548aa" diff --git a/ctags/main/routines_p.h b/ctags/main/routines_p.h index d705da6bb8..e7d6bb1073 100644 --- a/ctags/main/routines_p.h +++ b/ctags/main/routines_p.h @@ -94,12 +94,10 @@ extern MIO *tempFile (const char *const mode, char **const pName); extern char* baseFilenameSansExtensionNew (const char *const fileName, const char *const templateExt); -#if 0 #include "portable-dirent_p.h" extern int scanDirectory (const char *directory_name, struct dirent ***array_pointer, int (*select_function) (const struct dirent *), int (*compare_function) (const struct dirent **, const struct dirent **)); -#endif #endif /* CTAGS_MAIN_ROUTINES_PRIVATE_H */ From 0182f7d1f0d254388fa9746c98dcb9941f6d488b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Tue, 21 May 2019 04:30:44 -0700 Subject: [PATCH 43/46] Add a patch file containing the only change we need against uctags --- ctags/ctags_changes.patch | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 ctags/ctags_changes.patch diff --git a/ctags/ctags_changes.patch b/ctags/ctags_changes.patch new file mode 100644 index 0000000000..7f7f882d2c --- /dev/null +++ b/ctags/ctags_changes.patch @@ -0,0 +1,24 @@ +A patch to ctags containing our changes to ctags +(changing anon tag names from anon to anon). +diff --git a/ctags/main/parse.c b/ctags/main/parse.c +index 8fbb7148..c12f8662 100644 +--- ctags/main/parse.c ++++ ctags/main/parse.c +@@ -4111,12 +4111,18 @@ extern void anonGenerate (vString *buffer, const char *prefix, int kind) + parser -> anonymousIdentiferId ++; + + char szNum[32]; ++#if 0 + char buf [9]; + + vStringCopyS(buffer, prefix); + + anonHashString (getInputFileName(), buf); + sprintf(szNum,"%s%02x%02x",buf,parser -> anonymousIdentiferId, kind); ++#else ++ /* we want to see numbers for anon functions in the tree view instead of the hash */ ++ vStringCopyS(buffer, prefix); ++ sprintf(szNum,"%u", parser -> anonymousIdentiferId); ++#endif + vStringCatS(buffer,szNum); + } From 2587ccf8afbe4fea2f42e96c444c03ea0efab4d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Tue, 21 May 2019 04:31:17 -0700 Subject: [PATCH 44/46] We can use WRITER_CUSTOM now --- src/tagmanager/tm_ctags.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tagmanager/tm_ctags.c b/src/tagmanager/tm_ctags.c index 5dcfdd5cca..fee04835b8 100644 --- a/src/tagmanager/tm_ctags.c +++ b/src/tagmanager/tm_ctags.c @@ -35,7 +35,7 @@ tagWriter geanyWriter = { .treatFieldAsFixed = NULL, .defaultFileName = "geany_tags_file_which_should_never_appear_anywhere", .private = NULL, - .type = WRITER_U_CTAGS /* not really but we must use some of the builtin types */ + .type = WRITER_CUSTOM }; From 170b388ec857983c3bba0489633a1c68e13a5227 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 22 May 2019 05:01:04 -0700 Subject: [PATCH 45/46] Inject our parser list to ctags This patch syncs to ctags version 869dd9d750d08c4bfb8a0aaa38ab84ebc33b222c which allows us to define a custom parser list. This is done using the EXTERNAL_PARSER_LIST_FILE (done inside ctags/Makefile.am) which has to point to the file with the parser list where the parsers have to be defined inside the EXTERNAL_PARSER_LIST define. This patch adds new tm_parsers.h header where our parsers are defined. This means that we don't have to care about ctags/parsers_p.h any more and it can have the same contents as the upstream ctags. Thanks to using our parser list directly, we can avoid the "invisible" test parser in ctags so we don't have to shift parser numbers by 1 any more so the CTAGS_LANG() and GEANY_LANG() macros can be dropped. Finally the patch updates various comments and the HACKING file to mention tm_parsers.h instead of ctags/parsers.h. In the HACKING file it also drops the section about using GRegex which isn't the case any more. --- HACKING | 14 +-- ctags/Makefile.am | 1 + ctags/main/lregex.c | 14 +-- ctags/main/lregex_p.h | 2 +- ctags/main/main.c | 18 ++-- ctags/main/options.c | 37 +++++--- ctags/main/options_p.h | 3 +- ctags/main/parse.c | 61 +++++++++++-- ctags/main/parse.h | 5 ++ ctags/main/parse_p.h | 10 ++- ctags/main/parsers_p.h | 155 ++++++++++++++++++++++++--------- ctags/main/portable-dirent_p.h | 3 - ctags/main/portable-scandir.c | 9 +- ctags/main/routines_p.h | 7 +- ctags/main/unwindi.c | 120 +++++++++++++++++++------ ctags/main/unwindi.h | 19 +++- src/tagmanager/Makefile.am | 1 + src/tagmanager/tm_ctags.c | 21 ++--- src/tagmanager/tm_parser.h | 2 +- src/tagmanager/tm_parsers.h | 69 +++++++++++++++ src/tagmanager/tm_workspace.c | 4 +- 21 files changed, 427 insertions(+), 148 deletions(-) create mode 100644 src/tagmanager/tm_parsers.h diff --git a/HACKING b/HACKING index 50971e64c6..6ae403340b 100644 --- a/HACKING +++ b/HACKING @@ -650,22 +650,12 @@ http://sf.net/projects/ctags - see the tracker. (You can also try the Anjuta project's anjuta-tags codebase.) -.. note:: - From Geany 1.22 GLib's GRegex engine is used instead of POSIX - regex, unlike CTags. It should be close enough to POSIX to work - OK. - We no longer support regex parsers with the "b" regex flag - option set and Geany will print debug warnings if it's used. - CTags supports it but doesn't currently (2011) include any - parsers that use it. It should be easy to convert to extended - regex syntax anyway. - Method `````` * Add foo.c to SRCS in Makefile.am. -* Add Foo to parsers.h +* Add Foo to src/tagmanager/tm_parsers.h * Add TM_PARSER_FOO to src/tagmanager/tm_parser.h. The list here must follow - exactly the order in parsers.h. + exactly the order in src/tagmanager/tm_parsers.h. In src/tagmanager/tm_parser.c: Add a map_FOO TMParserMapEntry mapping each kind's letter from foo.c's diff --git a/ctags/Makefile.am b/ctags/Makefile.am index ff7778becf..3a11e11fef 100644 --- a/ctags/Makefile.am +++ b/ctags/Makefile.am @@ -1,6 +1,7 @@ AM_CPPFLAGS = \ -I$(srcdir)/main \ -I$(srcdir)/parsers \ + -DEXTERNAL_PARSER_LIST_FILE=\"$(top_srcdir)/src/tagmanager/tm_parsers.h\" \ -DG_LOG_DOMAIN=\"CTags\" AM_CFLAGS = \ $(GTK_CFLAGS) \ diff --git a/ctags/main/lregex.c b/ctags/main/lregex.c index 594fc24f06..5988a14975 100644 --- a/ctags/main/lregex.c +++ b/ctags/main/lregex.c @@ -2227,31 +2227,31 @@ extern void extendRegexTable (struct lregexControlBlock *lcb, const char *src, c } } -extern void printMultitableStatistics (struct lregexControlBlock *lcb, FILE *vfp) +extern void printMultitableStatistics (struct lregexControlBlock *lcb) { struct regexTable *table = ptrArrayItem (lcb->tables, 0); if (ptrArrayCount(lcb->tables) == 0) return; - fprintf(vfp, "MTABLE REGEX STATISTICS of %s\n", getLanguageName (lcb->owner)); - fputs("==============================================\n", vfp); + fprintf(stderr, "\nMTABLE REGEX STATISTICS of %s\n", getLanguageName (lcb->owner)); + fputs("==============================================\n", stderr); for (unsigned int i = 0; i < ptrArrayCount(lcb->tables); i++) { table = ptrArrayItem (lcb->tables, i); - fprintf(vfp, "%s\n", table->name); - fputs("-----------------------\n", vfp); + fprintf(stderr, "%s\n", table->name); + fputs("-----------------------\n", stderr); for (unsigned int j = 0; j < ptrArrayCount(table->entries); j++) { regexTableEntry *entry = ptrArrayItem (table->entries, j); Assert (entry && entry->pattern); - fprintf(vfp, "%10u/%-10u%-40s ref: %d\n", + fprintf(stderr, "%10u/%-10u%-40s ref: %d\n", entry->statistics.match, entry->statistics.unmatch + entry->statistics.match, entry->pattern->pattern_string, entry->pattern->refcount); } - fputc('\n', vfp); + fputc('\n', stderr); } } diff --git a/ctags/main/lregex_p.h b/ctags/main/lregex_p.h index 40c3f2fac3..cd017c7bbb 100644 --- a/ctags/main/lregex_p.h +++ b/ctags/main/lregex_p.h @@ -72,6 +72,6 @@ extern void notifyRegexInputEnd (struct lregexControlBlock *lcb); extern void addRegexTable (struct lregexControlBlock *lcb, const char *name); extern void extendRegexTable (struct lregexControlBlock *lcb, const char *src, const char *dist); -extern void printMultitableStatistics (struct lregexControlBlock *lcb, FILE *vfp); +extern void printMultitableStatistics (struct lregexControlBlock *lcb); #endif /* CTAGS_MAIN_LREGEX_PRIVATEH */ diff --git a/ctags/main/main.c b/ctags/main/main.c index 3808781e87..2384cea7a6 100644 --- a/ctags/main/main.c +++ b/ctags/main/main.c @@ -85,7 +85,7 @@ static bool createTagsForEntry (const char *const entryName); * FUNCTION DEFINITIONS */ -#if defined (HAVE_OPENDIR) +#if defined (HAVE_OPENDIR) && (defined (HAVE_DIRENT_H) || defined (_MSC_VER)) static bool recurseUsingOpendir (const char *const dirName) { bool resize = false; @@ -178,7 +178,7 @@ static bool recurseIntoDirectory (const char *const dirName) else { verbose ("RECURSING into directory \"%s\"\n", dirName); -#if defined (HAVE_OPENDIR) +#if defined (HAVE_OPENDIR) && (defined (HAVE_DIRENT_H) || defined (_MSC_VER)) resize = recurseUsingOpendir (dirName); #elif defined (HAVE__FINDFIRST) { @@ -376,7 +376,13 @@ static void batchMakeTags (cookedArgs *args, void *user CTAGS_ATTR_UNUSED) timeStamp (2); if (Option.printTotals) + { printTotals (timeStamps, Option.append, Option.sorted); + if (Option.printTotals > 1) + for (unsigned int i = 0; i < countParsers(); i++) + printParserStatisticsIfUsed (i); + } + #undef timeStamp } @@ -568,14 +574,6 @@ extern int ctags_cli_main (int argc CTAGS_ATTR_UNUSED, char **argv) runMainLoop (args); - - BEGIN_VERBOSE_IF(Option.mtablePrintTotals, vfp); - { - for (unsigned int i = 0; i < countParsers(); i++) - printLanguageMultitableStatistics (i, vfp); - } - END_VERBOSE(); - /* Clean up. */ cArgDelete (args); diff --git a/ctags/main/options.c b/ctags/main/options.c index 497c8648c9..4ffd0f8c3b 100644 --- a/ctags/main/options.c +++ b/ctags/main/options.c @@ -161,8 +161,8 @@ optionValues Option = { .followLinks = true, .filter = false, .filterTerminator = NULL, - .tagRelative = false, - .printTotals = false, + .tagRelative = TREL_NO, + .printTotals = 0, .lineDirectives = false, .printLanguage =false, .guessLanguageEagerly = false, @@ -172,7 +172,6 @@ optionValues Option = { .putFieldPrefix = false, .maxRecursionDepth = 0xffffffff, .interactive = false, - .mtablePrintTotals = false, #ifdef DEBUG .breakLine = 0, #endif @@ -415,7 +414,7 @@ static optionDescription LongOptionDescription [] = { {0," Should paths be relative to location of tag file [no; yes when -e]?"}, {0," always: be relative even if input files are passed in with absolute paths" }, {0," never: be absolute even if input files are passed in with relative paths" }, - {1," --totals=[yes|no]"}, + {1," --totals=[yes|no|extra]"}, {1," Print statistics about input and tag files [no]."}, {1," --verbose=[yes|no]"}, {1," Enable verbose messages describing actions on each input file."}, @@ -471,8 +470,6 @@ static optionDescription ExperimentalLongOptionDescription [] = { {1," Copy patterns of a regex table to another regex table."}, {1," --_mtable-regex-=table/line_pattern/name_pattern/[flags]"}, {1," Define multitable regular expression for locating tags in specific language."}, - {1," --_mtable-totals=[yes|no]"}, - {1," Print statistics about mtable usage [no]."}, {1," --_pretend-="}, {1," Make NEWLANG parser pretend OLDLANG parser in lang: field."}, {1," --_roledef-=kind_letter.role_name,role_desc"}, @@ -538,7 +535,7 @@ static struct Feature { #ifdef DEBUG {"debug", "TO BE WRITTEN"}, #endif -#if defined(HAVE_SCANDIR) || defined (HAVE_DIRENT_H) || defined (_MSC_VER) +#if defined (HAVE_DIRENT_H) || defined (_MSC_VER) {"option-directory", "TO BE WRITTEN"}, #endif #ifdef HAVE_LIBXML @@ -747,7 +744,7 @@ extern void checkOptions (void) if (Option.printTotals) { error (WARNING, "%s disables totals", notice); - Option.printTotals = false; + Option.printTotals = 0; } if (Option.tagFileName != NULL) error (WARNING, "%s ignores output tag file name", notice); @@ -2413,7 +2410,7 @@ static void processTagRelative ( { if (isFalse (parameter)) Option.tagRelative = TREL_NO; - else if (isTrue (parameter)) + else if (isTrue (parameter) || *parameter == '\0') Option.tagRelative = TREL_YES; else if (strcasecmp (parameter, "always") == 0) Option.tagRelative = TREL_ALWAYS; @@ -2423,6 +2420,19 @@ static void processTagRelative ( error (FATAL, "Invalid value for \"%s\" option", option); } +static void processTotals ( + const char *const option, const char *const parameter) +{ + if (isFalse (parameter)) + Option.printTotals = 0; + else if (isTrue (parameter) || *parameter == '\0') + Option.printTotals = 1; + else if (strcasecmp (parameter, "extra") == 0) + Option.printTotals = 2; + else + error (FATAL, "Invalid value for \"%s\" option", option); +} + static void installHeaderListDefaults (void) { Option.headerExt = stringListNewFromArgv (HeaderExtensions); @@ -2740,6 +2750,7 @@ static parametricOption ParametricOptions [] = { { "pseudo-tags", processPseudoTags, false, STAGE_ANY }, { "sort", processSortOption, true, STAGE_ANY }, { "tag-relative", processTagRelative, true, STAGE_ANY }, + { "totals", processTotals, true, STAGE_ANY }, { "version", processVersionOption, true, STAGE_ANY }, { "_anonhash", processAnonHashOption, false, STAGE_ANY }, { "_dump-keywords", processDumpKeywordsOption, false, STAGE_ANY }, @@ -2774,11 +2785,9 @@ static booleanOption BooleanOptions [] = { #ifdef RECURSE_SUPPORTED { "recurse", &Option.recurse, false, STAGE_ANY }, #endif - { "totals", &Option.printTotals, true, STAGE_ANY }, { "verbose", &ctags_verbose, false, STAGE_ANY }, { "with-list-header", &localOption.withListHeader, true, STAGE_ANY }, { "_fatal-warnings",&Option.fatalWarnings, false, STAGE_ANY }, - { "_mtable-totals", &Option.mtablePrintTotals, false, STAGE_ANY }, }; /* @@ -3410,7 +3419,11 @@ extern void previewFirstOption (cookedArgs* const args) } } -#if defined(HAVE_SCANDIR) || defined (HAVE_DIRENT_H) || defined (_MSC_VER) +#if defined (HAVE_DIRENT_H) || defined (_MSC_VER) +extern int scanDirectory (const char *directory_name, + struct dirent ***array_pointer, int (*select_function) (const struct dirent *), + int (*compare_function) (const struct dirent **, const struct dirent **)); + static void parseConfigurationFileOptionsInDirectoryWithLeafname (const char* directory, const char* leafname) { char* pathname = combinePathAndFile (directory, leafname); diff --git a/ctags/main/options_p.h b/ctags/main/options_p.h index 2e9afe5640..f9960b58ca 100644 --- a/ctags/main/options_p.h +++ b/ctags/main/options_p.h @@ -108,7 +108,7 @@ typedef struct sOptionValues { bool filter; /* --filter behave as filter: files in, tags out */ char* filterTerminator; /* --filter-terminator string to output */ tagRelative tagRelative; /* --tag-relative file paths relative to tag file */ - bool printTotals; /* --totals print cumulative statistics */ + int printTotals; /* --totals print cumulative statistics */ bool lineDirectives; /* --linedirectives process #line directives */ bool printLanguage; /* --print-language */ bool guessLanguageEagerly; /* --guess-language-eagerly|-G */ @@ -120,7 +120,6 @@ typedef struct sOptionValues { enum interactiveMode { INTERACTIVE_NONE = 0, INTERACTIVE_DEFAULT, INTERACTIVE_SANDBOX, } interactive; /* --interactive */ - bool mtablePrintTotals; /* display mtable statistics */ #ifdef DEBUG unsigned long breakLine;/* -b input line at which to call lineBreak() */ #endif diff --git a/ctags/main/parse.c b/ctags/main/parse.c index c12f86628d..01c88c4e3c 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -82,10 +82,13 @@ typedef struct sParserObject { stringList* currentAliases; /* current list of aliases */ unsigned int initialized:1; /* initialize() is called or not */ - unsigned int dontEmit:1; /* run but don't emit tags - (a subparser requests run this parser.) */ + unsigned int dontEmit:1; /* run but don't emit tags. + This parser was disabled but a subparser on + this parser makes this parser run (to drive + the subparser). */ unsigned int pseudoTagPrinted:1; /* pseudo tags about this parser is emitted or not. */ + unsigned int used; /* Used for printing language specific statistics. */ unsigned int anonymousIdentiferId; /* managed by anon* functions */ @@ -121,6 +124,9 @@ static void uninstallTagXpathTable (const langType language); */ static parserDefinition *CTagsSelfTestParser (void); static parserDefinitionFunc* BuiltInParsers[] = { +#ifdef EXTERNAL_PARSER_LIST + EXTERNAL_PARSER_LIST +#else /* ! EXTERNAL_PARSER_LIST */ CTagsSelfTestParser, PARSER_LIST, XML_PARSER_LIST @@ -135,6 +141,7 @@ static parserDefinitionFunc* BuiltInParsers[] = { #ifdef HAVE_PACKCC , #endif +#endif /* EXTERNAL_PARSER_LIST */ }; static parserObject* LanguageTable = NULL; static unsigned int LanguageCount = 0; @@ -3302,6 +3309,29 @@ static subparser* teardownLanguageSubparsersInUse (const langType language) return teardownSubparsersInUse ((LanguageTable + language)->slaveControlBlock); } +static void initializeParserStats (parserObject *parser) +{ + if (Option.printTotals > 1 && parser->used == 0 && parser->def->initStats) + parser->def->initStats (parser->def->id); + parser->used = 1; +} + +extern void printParserStatisticsIfUsed (langType language) +{ + parserObject *parser = &(LanguageTable [language]); + + if (parser->used) + { + if (parser->def->printStats) + { + fprintf(stderr, "\nSTATISTICS of %s\n", getLanguageName (language)); + fputs("==============================================\n", stderr); + parser->def->printStats (language); + } + printLanguageMultitableStatistics (language); + } +} + static bool createTagsWithFallback1 (const langType language, langType *exclusive_subparser) { @@ -3324,6 +3354,7 @@ static bool createTagsWithFallback1 (const langType language, corkTagFile(); addParserPseudoTags (language); + initializeParserStats (parser); tagFilePosition (&tagfpos); anonResetMaybe (parser); @@ -4181,7 +4212,7 @@ extern subparser *getNextSubparser(subparser *last, t = getSubparserLanguage(r); if (isLanguageEnabled (t) && (includingNoneCraftedParser - || ((((LanguageTable + t)->def->method) && METHOD_NOT_CRAFTED) == 0))) + || ((((LanguageTable + t)->def->method) & METHOD_NOT_CRAFTED) == 0))) return r; else return getNextSubparser (r, includingNoneCraftedParser); @@ -4336,11 +4367,10 @@ extern void printKinddefFlags (bool withListHeader, bool machinable, FILE *fp) colprintTableDelete(table); } -extern void printLanguageMultitableStatistics (langType language, FILE *vfp) +extern void printLanguageMultitableStatistics (langType language) { parserObject* const parser = LanguageTable + language; - printMultitableStatistics (parser->lregexControlBlock, - vfp); + printMultitableStatistics (parser->lregexControlBlock); } extern void addLanguageRegexTable (const langType language, const char *name) @@ -4416,6 +4446,9 @@ extern bool processPretendOption (const char *const option, const char *const pa extern void getppid(void); #endif +static bool CTST_GatherStats; +static int CTST_num_handled_char; + typedef enum { K_BROKEN, K_NO_LETTER, @@ -4531,6 +4564,9 @@ static void createCTSTTags (void) if ((c == CTST_Kinds[i].letter && i != K_NO_LETTER) || (c == '@' && i == K_NO_LETTER)) { + if (CTST_GatherStats) + CTST_num_handled_char++; + switch (i) { case K_BROKEN: @@ -4632,6 +4668,17 @@ static void createCTSTTags (void) TRACE_LEAVE(); } +static void initStatsCTST (langType lang CTAGS_ATTR_UNUSED) +{ + CTST_GatherStats = true; +} + +static void printStatsCTST (langType lang CTAGS_ATTR_UNUSED) +{ + fprintf (stderr, "The number of handled chars: %d\n", + CTST_num_handled_char); +} + static parserDefinition *CTagsSelfTestParser (void) { static const char *const extensions[] = { NULL }; @@ -4643,5 +4690,7 @@ static parserDefinition *CTagsSelfTestParser (void) def->invisible = true; def->useMemoryStreamInput = true; def->useCork = true; + def->initStats = initStatsCTST; + def->printStats = printStatsCTST; return def; } diff --git a/ctags/main/parse.h b/ctags/main/parse.h index c9a8c9c902..d37b59164b 100644 --- a/ctags/main/parse.h +++ b/ctags/main/parse.h @@ -39,6 +39,8 @@ typedef void (*createRegexTag) (const vString* const name); typedef void (*simpleParser) (void); typedef rescanReason (*rescanParser) (const unsigned int passCount); typedef void (*parserInitialize) (langType language); +typedef void (*initStatistics) (langType language); +typedef void (*printStatistics) (langType langType); /* Per language finalizer is called anytime when ctags exits. (Exceptions are a kind of options are given when invoked. Here @@ -105,6 +107,9 @@ struct sParserDefinition { const char *defaultScopeSeparator; const char *defaultRootScopeSeparator; + initStatistics initStats; + printStatistics printStats; + /* used internally */ langType id; /* id assigned to language */ unsigned int enabled:1; /* currently enabled? */ diff --git a/ctags/main/parse_p.h b/ctags/main/parse_p.h index 6a3af62448..205835897f 100644 --- a/ctags/main/parse_p.h +++ b/ctags/main/parse_p.h @@ -18,6 +18,9 @@ #include "parse.h" #include "parsers_p.h" /* contains list of parsers */ #include "strlist.h" +#ifdef EXTERNAL_PARSER_LIST_FILE +#include EXTERNAL_PARSER_LIST_FILE +#endif /* * MACROS @@ -41,6 +44,9 @@ typedef enum { * return a structure allocated using parserNew(). This structure must, * at minimum, set the `parser' field. */ +#ifdef EXTERNAL_PARSER_LIST +extern parserDefinitionFunc EXTERNAL_PARSER_LIST; +#else /* ! EXTERNAL_PARSER_LIST */ extern parserDefinitionFunc PARSER_LIST; #ifdef HAVE_LIBXML extern parserDefinitionFunc XML_PARSER_LIST; @@ -51,6 +57,7 @@ extern parserDefinitionFunc YAML_PARSER_LIST; #ifdef HAVE_PACKCC extern parserDefinitionFunc PEG_PARSER_LIST; #endif +#endif /* EXTERNAL_PARSER_LIST */ extern bool doesLanguageAllowNullTag (const langType language); extern bool doesLanguageRequestAutomaticFQTag (const langType language); @@ -159,6 +166,7 @@ extern bool makeKindSeparatorsPseudoTags (const langType language, extern bool makeKindDescriptionsPseudoTags (const langType language, const ptagDesc *pdesc); -extern void printLanguageMultitableStatistics (langType language, FILE *vfp); +extern void printLanguageMultitableStatistics (langType language); +extern void printParserStatisticsIfUsed (langType lang); #endif /* CTAGS_MAIN_PARSE_PRIVATE_H */ diff --git a/ctags/main/parsers_p.h b/ctags/main/parsers_p.h index 309bf8208a..15aca2c1b2 100644 --- a/ctags/main/parsers_p.h +++ b/ctags/main/parsers_p.h @@ -12,63 +12,136 @@ #ifndef CTAGS_MAIN_PARSERS_H #define CTAGS_MAIN_PARSERS_H +#ifdef HAVE_LIBXML +#define XML_PARSER_LIST \ + DbusIntrospectParser, \ + GladeParser, \ + Maven2Parser, \ + PlistXMLParser, \ + RelaxNGParser, \ + SvgParser, \ + XmlParser, \ + XsltParser +#else #define XML_PARSER_LIST +#endif + +#ifdef HAVE_LIBYAML +#define YAML_PARSER_LIST \ + YamlParser, \ + AnsiblePlaybookParser +#else #define YAML_PARSER_LIST +#endif + +#ifdef HAVE_PACKCC +#define PEG_PARSER_LIST \ + VarlinkParser +#else #define PEG_PARSER_LIST +#endif /* Add the name of any new parser definition function here */ -/* keep src/tagmanager/tm_parser.h in sync */ #define PARSER_LIST \ + AdaParser, \ + AntParser, \ + AsciidocParser, \ + AsmParser, \ + AspParser, \ + AutoconfParser, \ + AutoItParser, \ + AutomakeParser, \ + AwkParser, \ + BasicParser, \ + BetaParser, \ + ClojureParser, \ + CMakeParser, \ CParser, \ CppParser, \ + CPreProParser, \ + CssParser, \ + CsharpParser, \ + CtagsParser, \ + CobolParser, \ + CUDAParser, \ + DParser, \ + DiffParser, \ + DtdParser, \ + DTSParser, \ + DosBatchParser, \ + EiffelParser, \ + ElixirParser, \ + ElmParser, \ + ErlangParser, \ + FalconParser, \ + FlexParser, \ + FortranParser, \ + FyppParser, \ + GdbinitParser, \ + GoParser, \ + HtmlParser, \ + IniconfParser, \ + InkoParser, \ + ITclParser, \ JavaParser, \ + JavaPropertiesParser, \ + JavaScriptParser, \ + JsonParser, \ + LdScriptParser, \ + LispParser, \ + LuaParser, \ + M4Parser, \ + ManParser, \ MakefileParser, \ + MarkdownParser, \ + MatLabParser, \ + MooseParser, \ + MyrddinParser, \ + ObjcParser, \ + OldCppParser, \ + OldCParser, \ + OcamlParser, \ + PasswdParser, \ PascalParser, \ PerlParser, \ + Perl6Parser, \ PhpParser, \ + PodParser, \ + ProtobufParser, \ + PuppetManifestParser, \ PythonParser, \ - TexParser, \ - AsmParser, \ - ConfParser, \ - SqlParser, \ - DocBookParser, \ - ErlangParser, \ - CssParser, \ + PythonLoggingConfigParser, \ + QemuHXParser, \ + QtMocParser, \ + RParser, \ + RSpecParser, \ + RexxParser, \ + RobotParser, \ + RpmSpecParser, \ + RstParser, \ RubyParser, \ - TclParser, \ + RustParser, \ + SchemeParser, \ ShParser, \ - DParser, \ - FortranParser, \ - FeriteParser, \ - DiffParser, \ - VhdlParser, \ - LuaParser, \ - JavaScriptParser, \ - HaskellParser, \ - CsharpParser, \ - BasicParser,\ - HaxeParser,\ - RstParser, \ - HtmlParser, \ - F77Parser, \ - GLSLParser, \ - MatLabParser, \ - ValaParser, \ - ActionScriptParser, \ - NsisParser, \ - MarkdownParser, \ - Txt2tagsParser, \ - AbcParser, \ + SlangParser, \ + SmlParser, \ + SqlParser, \ + SystemdUnitParser, \ + SystemTapParser, \ + TclParser, \ + TclOOParser, \ + TexParser, \ + TTCNParser, \ + TypeScriptParser, \ + VeraParser, \ VerilogParser, \ - RParser, \ - CobolParser, \ - ObjcParser, \ - AsciidocParser, \ - AbaqusParser, \ - RustParser, \ - GoParser, \ - JsonParser, \ - ZephirParser, \ - PowerShellParser + SystemVerilogParser, \ + UCtagsAspellParser, \ + VhdlParser, \ + VimParser, \ + WindResParser, \ + YaccParser, \ + YumRepoParser, \ + ZephirParser #endif /* CTAGS_MAIN_PARSERS_H */ diff --git a/ctags/main/portable-dirent_p.h b/ctags/main/portable-dirent_p.h index 6f053ede3f..8741627d62 100644 --- a/ctags/main/portable-dirent_p.h +++ b/ctags/main/portable-dirent_p.h @@ -941,7 +941,4 @@ dirent_set_errno( } #endif #endif /*DIRENT_H*/ - -#else -#error "dirent.h is not available." #endif /* HAVE_DIRENT_H */ diff --git a/ctags/main/portable-scandir.c b/ctags/main/portable-scandir.c index 8261d1536b..5e7c40061f 100644 --- a/ctags/main/portable-scandir.c +++ b/ctags/main/portable-scandir.c @@ -111,9 +111,12 @@ #include "routines.h" #include "routines_p.h" -#ifdef HAVE_SCANDIR +#if defined (HAVE_SCANDIR) && defined (HAVE_DIRENT_H) #include -#else +#elif defined (HAVE_DIRENT_H) || defined (_MSC_VER) +# ifdef HAVE_DIRENT_H +# include +# endif #define USE_SCANDIR_COMPARE_STRUCT_DIRENT #include @@ -227,9 +230,11 @@ scandir(const char *directory_name, } #endif +#if defined (HAVE_DIRENT_H) || defined (_MSC_VER) int scanDirectory (const char *directory_name, struct dirent ***array_pointer, int (*select_function) (const struct dirent *), int (*compare_function) (const struct dirent **, const struct dirent **)) { return scandir (directory_name, array_pointer, select_function, compare_function); } +#endif diff --git a/ctags/main/routines_p.h b/ctags/main/routines_p.h index e7d6bb1073..5aebf7b662 100644 --- a/ctags/main/routines_p.h +++ b/ctags/main/routines_p.h @@ -14,6 +14,7 @@ */ #include "general.h" /* must always come first */ #include "mio.h" +#include "portable-dirent_p.h" /* * Portability macros @@ -94,10 +95,4 @@ extern MIO *tempFile (const char *const mode, char **const pName); extern char* baseFilenameSansExtensionNew (const char *const fileName, const char *const templateExt); -#include "portable-dirent_p.h" - -extern int scanDirectory (const char *directory_name, - struct dirent ***array_pointer, int (*select_function) (const struct dirent *), - int (*compare_function) (const struct dirent **, const struct dirent **)); - #endif /* CTAGS_MAIN_ROUTINES_PRIVATE_H */ diff --git a/ctags/main/unwindi.c b/ctags/main/unwindi.c index 89dd31d57d..6a87849e6b 100644 --- a/ctags/main/unwindi.c +++ b/ctags/main/unwindi.c @@ -29,6 +29,8 @@ #include "trashbox.h" #include "unwindi.h" +#include + typedef struct sUugcChar { int c; /* lineNumber before reading the char (frontLineNumber). @@ -43,6 +45,8 @@ static ptrArray *uugcInputFile; static uugcChar *uugcCurrentChar; static objPool *uugcCharPool; +static struct sUwiStats uwiStats; + static void deleteChar (void *c) { eFree (c); @@ -203,20 +207,44 @@ CTAGS_INLINE MIOPos uugcGetFilePosition (void) return getInputFilePosition (); } -static ptrArray *uwiMarkerStack; -static ptrArray *uwiCurrentMarker; -extern void uwiActivate (void) +static ptrArray *uwiBuffer; +static unsigned int *uwiMarkerStack; +static unsigned int uwiMarkerStackLength; +static unsigned int *uwiCurrentMarker; + +extern void uwiActivate (unsigned int stackLength) { - Assert (!uwiMarkerStack); + Assert (stackLength > 0); uugcActivate (); - uwiMarkerStack = ptrArrayNew ((ptrArrayDeleteFunc)ptrArrayDelete); + uwiBuffer = ptrArrayNew ((ptrArrayDeleteFunc)uugcDeleteC); + uwiMarkerStackLength = stackLength; + uwiMarkerStack = xMalloc (stackLength, unsigned int); + uwiCurrentMarker = NULL; + + uwiStatsInit (&uwiStats); } -extern void uwiDeactivate (void) +extern void uwiDeactivate (struct sUwiStats *statsToBeUpdated) { + Assert (uwiBuffer); Assert (uwiMarkerStack); - ptrArrayDelete (uwiMarkerStack); + + if (statsToBeUpdated) + { + if (statsToBeUpdated->maxLength < uwiStats.maxLength) + statsToBeUpdated->maxLength = uwiStats.maxLength; + if (!statsToBeUpdated->overflow) + statsToBeUpdated->overflow = uwiStats.overflow; + if (!statsToBeUpdated->underflow) + statsToBeUpdated->underflow = uwiStats.underflow; + } + + ptrArrayDelete (uwiBuffer); + eFree (uwiMarkerStack); + uwiBuffer = NULL; + uwiMarkerStack = NULL; + uwiMarkerStackLength = 0; uugcDeactive(); } @@ -228,7 +256,10 @@ extern int uwiGetC () c = chr->c; if (uwiCurrentMarker) - ptrArrayAdd (uwiCurrentMarker, chr); + { + *uwiCurrentMarker += 1; + ptrArrayAdd (uwiBuffer, chr); + } else { uugcCurrentChar = NULL; @@ -256,37 +287,72 @@ extern MIOPos uwiGetFilePosition (void) extern void uwiPushMarker (void) { - Assert (uwiMarkerStack); - if (uwiCurrentMarker) - ptrArrayAdd (uwiMarkerStack, uwiCurrentMarker); + if (uwiStats.maxLength < (uwiCurrentMarker - uwiMarkerStack) + 1) + uwiStats.maxLength = (uwiCurrentMarker - uwiMarkerStack) + 1; + + if (uwiCurrentMarker - uwiMarkerStack >= ( uwiMarkerStackLength - 1 )) { + error (WARNING, + "trying to add too many markers during parsing: %s " + "(this is a bug, please consider filing an issue)", getInputFileName()); + uwiCurrentMarker = NULL; + uwiStats.overflow = true; + } + + if (uwiCurrentMarker) uwiCurrentMarker++; + else uwiCurrentMarker = uwiMarkerStack; - uwiCurrentMarker = ptrArrayNew ((ptrArrayDeleteFunc)uugcDeleteC); + *uwiCurrentMarker = 0; } -extern void uwiPopMarker (int upto) +extern void uwiPopMarker (const int upto, const bool revertChars) { - Assert (uwiCurrentMarker); + if (uwiCurrentMarker - uwiMarkerStack < 0) { + error (WARNING, + "trying to drop too many markers during parsing: %s " + "(this is a bug, please consider filing an issue)", getInputFileName()); - int count = (upto < 0)? ptrArrayCount (uwiCurrentMarker): upto; - while (count > 0) - { - uugcUngetC (ptrArrayLast (uwiCurrentMarker)); - ptrArrayRemoveLast (uwiCurrentMarker); - count--; + uwiCurrentMarker = NULL; + uwiStats.underflow = true; + return; } - ptrArrayDelete (uwiCurrentMarker); + uwiClearMarker (upto, revertChars); - uwiCurrentMarker = NULL; - if (ptrArrayCount (uwiMarkerStack) > 0) + if (uwiCurrentMarker == uwiMarkerStack) uwiCurrentMarker = NULL; + else uwiCurrentMarker--; +} + +extern void uwiClearMarker (const int upto, const bool revertChars) +{ + Assert (uwiCurrentMarker); + int count = (upto <= 0)? *uwiCurrentMarker : upto; + void (*charHandler)(uugcChar *) = revertChars ? uugcUngetC : uugcDeleteC; + + while (count-- > 0) { - uwiCurrentMarker = ptrArrayLast (uwiMarkerStack); - ptrArrayRemoveLast (uwiMarkerStack); + charHandler (ptrArrayLast (uwiBuffer)); + ptrArrayRemoveLast (uwiBuffer); + *uwiCurrentMarker -= 1; } } -extern void uwiDropMaker () +extern void uwiDropMaker () +{ + uwiPopMarker (0, false); +} + +extern void uwiStatsInit (struct sUwiStats *stats) +{ + memset (stats, 0, sizeof (*stats)); +} + +extern void uwiStatsPrint (struct sUwiStats *stats) { - uwiPopMarker (0); + fprintf(stderr, "Unwinding the longest input stream stack usage: %d\n", + stats->maxLength); + fprintf(stderr, "Unwinding input stream stack overflow incidence: %s\n", + stats->overflow? "yes": "no"); + fprintf(stderr, "Unwinding input stream stack underflow incidence: %s\n", + stats->underflow? "yes": "no"); } diff --git a/ctags/main/unwindi.h b/ctags/main/unwindi.h index 0e059a579a..b4750f9df6 100644 --- a/ctags/main/unwindi.h +++ b/ctags/main/unwindi.h @@ -54,11 +54,23 @@ */ #include "general.h" +/* +* DATA DECLARATIONS +*/ +struct sUwiStats { + int maxLength; + bool overflow; + bool underflow; +}; + /* * FUNCTION PROTOTYPES */ -extern void uwiActivate (void); -extern void uwiDeactivate (void); +extern void uwiActivate (unsigned int); +extern void uwiDeactivate (struct sUwiStats *statsToBeUpdated); + +extern void uwiStatsInit (struct sUwiStats *stats); +extern void uwiStatsPrint (struct sUwiStats *stats); extern int uwiGetC (void); extern void uwiUngetC (int c); @@ -66,6 +78,7 @@ extern unsigned long uwiGetLineNumber (void); extern MIOPos uwiGetFilePosition (void); extern void uwiPushMarker (void); -extern void uwiPopMarker (int count); +extern void uwiClearMarker (const int count, const bool revertChars); +extern void uwiPopMarker (const int count, const bool revertChars); extern void uwiDropMaker (void); #endif /* CTAGS_MAIN_UNWINDI_H */ diff --git a/src/tagmanager/Makefile.am b/src/tagmanager/Makefile.am index f1439667b0..066192b952 100644 --- a/src/tagmanager/Makefile.am +++ b/src/tagmanager/Makefile.am @@ -22,6 +22,7 @@ libtagmanager_la_SOURCES = \ tm_ctags.c \ tm_parser.h \ tm_parser.c \ + tm_parsers.h \ tm_source_file.h \ tm_source_file.c \ tm_tag.h \ diff --git a/src/tagmanager/tm_ctags.c b/src/tagmanager/tm_ctags.c index fee04835b8..3f2d86bd7d 100644 --- a/src/tagmanager/tm_ctags.c +++ b/src/tagmanager/tm_ctags.c @@ -20,9 +20,6 @@ #include "writer_p.h" #include "xtag_p.h" -#define CTAGS_LANG(x) ((x) >= 0 ? (x) + 1 : (x)) -#define GEANY_LANG(x) ((x) >= 1 ? (x) - 1 : (x)) - static gint write_entry(tagWriter *writer, MIO * mio, const tagEntryInfo *const tag, void *user_data); static void rescan_failed(tagWriter *writer, gulong valid_tag_num, void *user_data); @@ -85,7 +82,7 @@ static gboolean init_tag(TMTag *tag, TMSourceFile *file, const tagEntryInfo *tag if (!tag_entry) return FALSE; - lang = GEANY_LANG(tag_entry->langType); + lang = tag_entry->langType; kind_letter = getLanguageKind(tag_entry->langType, tag_entry->kindIndex)->letter; type = tm_parser_get_tag_type(kind_letter, lang); if (file->lang != lang) /* this is a tag from a subparser */ @@ -225,30 +222,30 @@ void tm_ctags_parse(guchar *buffer, gsize buffer_size, { g_return_if_fail(buffer != NULL || file_name != NULL); - parseRawBuffer(file_name, buffer, buffer_size, CTAGS_LANG(language), source_file); + parseRawBuffer(file_name, buffer, buffer_size, language, source_file); } const gchar *tm_ctags_get_lang_name(TMParserType lang) { - return getLanguageName(CTAGS_LANG(lang)); + return getLanguageName(lang); } TMParserType tm_ctags_get_named_lang(const gchar *name) { - return GEANY_LANG(getNamedLanguage(name, 0)); + return getNamedLanguage(name, 0); } const gchar *tm_ctags_get_lang_kinds(TMParserType lang) { - guint kind_num = countLanguageKinds(CTAGS_LANG(lang)); + guint kind_num = countLanguageKinds(lang); static gchar kinds[257]; guint i; for (i = 0; i < kind_num; i++) - kinds[i] = getLanguageKind(CTAGS_LANG(lang), i)->letter; + kinds[i] = getLanguageKind(lang, i)->letter; kinds[i] = '\0'; return kinds; @@ -257,19 +254,19 @@ const gchar *tm_ctags_get_lang_kinds(TMParserType lang) const gchar *tm_ctags_get_kind_name(gchar kind, TMParserType lang) { - kindDefinition *def = getLanguageKindForLetter(CTAGS_LANG(lang), kind); + kindDefinition *def = getLanguageKindForLetter(lang, kind); return def ? def->name : "unknown"; } gchar tm_ctags_get_kind_from_name(const gchar *name, TMParserType lang) { - kindDefinition *def = getLanguageKindForName(CTAGS_LANG(lang), name); + kindDefinition *def = getLanguageKindForName(lang, name); return def ? def->letter : '-'; } guint tm_ctags_get_lang_count(void) { - return GEANY_LANG(countParsers()); + return countParsers(); } diff --git a/src/tagmanager/tm_parser.h b/src/tagmanager/tm_parser.h index db64a61f50..a63bce1415 100644 --- a/src/tagmanager/tm_parser.h +++ b/src/tagmanager/tm_parser.h @@ -54,7 +54,7 @@ typedef gint TMParserType; #ifdef GEANY_PRIVATE -/* keep in sync with ctags/parsers.h */ +/* keep in sync with tm_parsers.h and parser_map in tm_parser.c */ enum { TM_PARSER_NONE = -2, /* keep in sync with ctags LANG_IGNORE */ diff --git a/src/tagmanager/tm_parsers.h b/src/tagmanager/tm_parsers.h new file mode 100644 index 0000000000..36112f7f00 --- /dev/null +++ b/src/tagmanager/tm_parsers.h @@ -0,0 +1,69 @@ +/* +* Copyright (c) 2019, Jiri Techet +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* Declares parsers used by ctags +*/ +#ifndef TM_PARSERS_H +#define TM_PARSERS_H + +/* This file is included by ctags by defining EXTERNAL_PARSER_LIST_FILE inside + * ctags/Makefile.am */ + +/* Keep in sync with tm_parser.h */ +#define EXTERNAL_PARSER_LIST \ + CParser, \ + CppParser, \ + JavaParser, \ + MakefileParser, \ + PascalParser, \ + PerlParser, \ + PhpParser, \ + PythonParser, \ + TexParser, \ + AsmParser, \ + ConfParser, \ + SqlParser, \ + DocBookParser, \ + ErlangParser, \ + CssParser, \ + RubyParser, \ + TclParser, \ + ShParser, \ + DParser, \ + FortranParser, \ + FeriteParser, \ + DiffParser, \ + VhdlParser, \ + LuaParser, \ + JavaScriptParser, \ + HaskellParser, \ + CsharpParser, \ + BasicParser,\ + HaxeParser,\ + RstParser, \ + HtmlParser, \ + F77Parser, \ + GLSLParser, \ + MatLabParser, \ + ValaParser, \ + ActionScriptParser, \ + NsisParser, \ + MarkdownParser, \ + Txt2tagsParser, \ + AbcParser, \ + VerilogParser, \ + RParser, \ + CobolParser, \ + ObjcParser, \ + AsciidocParser, \ + AbaqusParser, \ + RustParser, \ + GoParser, \ + JsonParser, \ + ZephirParser, \ + PowerShellParser + +#endif diff --git a/src/tagmanager/tm_workspace.c b/src/tagmanager/tm_workspace.c index 4d206141b5..40d19f39fa 100644 --- a/src/tagmanager/tm_workspace.c +++ b/src/tagmanager/tm_workspace.c @@ -662,7 +662,7 @@ static void fill_find_tags_array(GPtrArray *dst, const GPtrArray *src, @param scope The scope name of the tag to find, or NULL. @param type The tag types to return (TMTagType). Can be a bitmask. @param attrs The attributes to sort and dedup on (0 terminated integer array). - @param lang Specifies the language(see the table in parsers.h) of the tags to be found, + @param lang Specifies the language(see the table in tm_parsers.h) of the tags to be found, -1 for all @return Array of matching tags. */ @@ -710,7 +710,7 @@ static void fill_find_tags_array_prefix(GPtrArray *dst, const GPtrArray *src, /* Returns tags with the specified prefix sorted by name. If there are several tags with the same name, only one of them appears in the resulting array. @param prefix The prefix of the tag to find. - @param lang Specifies the language(see the table in parsers.h) of the tags to be found, + @param lang Specifies the language(see the table in tm_parsers.h) of the tags to be found, -1 for all. @param max_num The maximum number of tags to return. @return Array of matching tags sorted by their name. From d68731da6af74befe54a4427680426ff2542afd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 22 May 2019 05:06:26 -0700 Subject: [PATCH 46/46] Remove dirent.h check which is not needed because of upstream changes --- configure.ac | 1 - 1 file changed, 1 deletion(-) diff --git a/configure.ac b/configure.ac index 046391ac60..a875da953f 100644 --- a/configure.ac +++ b/configure.ac @@ -44,7 +44,6 @@ AC_CHECK_HEADERS([fcntl.h glob.h stdlib.h sys/time.h errno.h limits.h]) # Checks for dependencies needed by ctags AC_CHECK_HEADERS([fnmatch.h direct.h io.h sys/dir.h]) AC_DEFINE([HAVE_STDBOOL_H], [1], [whether or not to use .]) -AC_CHECK_HEADERS(dirent.h,have_dirent_h=yes) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_OFF_T