From 92382dcdbcccb7f531569dc291c33e275a2a9e44 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 18 Nov 2020 12:10:32 -0800 Subject: [PATCH] Update to latest ctags main See commit 2aa034a30dc54f5db18db2c9140821f64426283e upstream. --- configure.ac | 3 +- ctags/Makefile.am | 81 +- ctags/main/args.c | 6 +- ctags/main/{args.h => args_p.h} | 6 +- ctags/main/cmd.c | 22 + ctags/main/colprint.c | 295 ++ ctags/main/colprint_p.h | 37 + ctags/main/ctags-api.c | 144 - ctags/main/ctags-api.h | 57 - ctags/main/ctags.h | 10 +- ctags/main/debug.c | 103 +- ctags/main/debug.h | 32 +- ctags/main/dependency.c | 457 ++- ctags/main/dependency.h | 26 +- ctags/main/dependency_p.h | 58 + ctags/main/e_msoft.h | 23 +- ctags/main/entry.c | 1190 ++++++-- ctags/main/entry.h | 207 +- ctags/main/entry_p.h | 77 + ctags/main/entry_private.c | 38 + ctags/main/error.c | 43 +- ctags/main/{error.h => error_p.h} | 0 ctags/main/field.c | 929 ++++-- ctags/main/field.h | 82 +- ctags/main/{field.h => field_p.h} | 95 +- ctags/main/flags.c | 107 +- ctags/main/{flags.h => flags_p.h} | 16 +- ctags/main/fmt.c | 76 +- ctags/main/{fmt.h => fmt_p.h} | 0 ctags/main/gcc-attr.h | 8 +- ctags/main/general.h | 24 +- ctags/main/gvars.h | 29 + ctags/main/htable.c | 61 +- ctags/main/htable.h | 46 +- ctags/main/interactive_p.h | 29 + ctags/main/keyword.c | 36 +- ctags/main/keyword.h | 20 +- ctags/main/keyword_p.h | 26 + ctags/main/kind.c | 854 ++++-- ctags/main/kind.h | 89 +- ctags/main/kind_p.h | 82 + ctags/main/lcpp.c | 1020 ------- ctags/main/lcpp.h | 80 - ctags/main/lregex.c | 2767 ++++++++++++++---- ctags/main/lregex.h | 47 + ctags/main/lregex_p.h | 77 + ctags/main/lxcmd.c | 1227 -------- ctags/main/lxpath.c | 87 +- ctags/main/lxpath.h | 110 + ctags/main/lxpath_p.h | 28 + ctags/main/main.c | 321 ++- ctags/main/main.h | 26 - ctags/main/main_p.h | 22 + ctags/main/mbcs.c | 113 + ctags/main/mbcs.h | 29 +- ctags/main/mbcs_p.h | 32 + ctags/main/mini-geany.c | 346 +++ ctags/main/mio.c | 58 +- ctags/main/mio.h | 4 +- ctags/main/nestlevel.c | 38 +- ctags/main/nestlevel.h | 8 +- ctags/main/numarray.c | 17 +- ctags/main/numarray.h | 14 +- ctags/main/objpool.c | 8 +- ctags/main/options.c | 2330 ++++++++------- ctags/main/options.h | 242 +- ctags/main/{options.h => options_p.h} | 130 +- ctags/main/output-ctags.c | 59 - ctags/main/output.h | 50 - ctags/main/param.c | 58 + ctags/main/param.h | 38 + ctags/main/param_p.h | 36 + ctags/main/parse.c | 4695 ++++++++++++++++++++++++------- ctags/main/parse.h | 502 ++-- ctags/main/parse_p.h | 177 ++ ctags/main/parsers.h | 71 - ctags/main/parsers_p.h | 155 + ctags/main/pcoproc.c | 296 -- ctags/main/pcoproc.h | 29 - ctags/main/portable-dirent_p.h | 944 +++++++ ctags/main/portable-scandir.c | 240 ++ ctags/main/promise.c | 224 +- ctags/main/promise.h | 12 +- ctags/main/{promise.h => promise_p.h} | 17 +- ctags/main/ptag.c | 204 +- ctags/main/{ptag.h => ptag_p.h} | 39 +- ctags/main/ptrarray.c | 9 +- ctags/main/ptrarray.h | 7 +- ctags/main/rbtree.c | 468 +++ ctags/main/rbtree.h | 230 ++ ctags/main/read.c | 691 ++--- ctags/main/read.h | 52 +- ctags/main/read_p.h | 81 + ctags/main/repoinfo.h | 2 +- ctags/main/routines.c | 192 +- ctags/main/routines.h | 76 +- ctags/main/{routines.h => routines_p.h} | 68 +- ctags/main/seccomp.c | 80 + ctags/main/selectors.c | 228 +- ctags/main/selectors.h | 16 +- ctags/main/sort.c | 104 +- ctags/main/{sort.h => sort_p.h} | 0 ctags/main/stats.c | 85 + ctags/main/stats_p.h | 28 + ctags/main/strlist.c | 46 +- ctags/main/strlist.h | 1 + ctags/main/subparser.h | 68 + ctags/main/subparser_p.h | 48 + ctags/main/tokeninfo.c | 207 ++ ctags/main/tokeninfo.h | 97 + ctags/main/trace.c | 120 + ctags/main/trace.h | 101 +- ctags/main/trashbox.h | 47 +- ctags/main/trashbox_p.h | 30 + ctags/main/types.h | 28 +- ctags/main/unwindi.c | 358 +++ ctags/main/unwindi.h | 84 + ctags/main/vstring.c | 14 + ctags/main/vstring.h | 23 +- ctags/main/writer-ctags.c | 434 +++ ctags/main/writer-etags.c | 202 ++ ctags/main/writer-json.c | 294 ++ ctags/main/writer-xref.c | 106 + ctags/main/writer.c | 189 ++ ctags/main/writer_p.h | 106 + ctags/main/xtag.c | 546 ++-- ctags/main/xtag.h | 33 +- ctags/main/xtag_p.h | 52 + 128 files changed, 19537 insertions(+), 8665 deletions(-) rename ctags/main/{args.h => args_p.h} (92%) create mode 100644 ctags/main/cmd.c create mode 100644 ctags/main/colprint.c create mode 100644 ctags/main/colprint_p.h delete mode 100644 ctags/main/ctags-api.c delete mode 100644 ctags/main/ctags-api.h rewrite ctags/main/dependency.c (72%) create mode 100644 ctags/main/dependency_p.h create mode 100644 ctags/main/entry_p.h create mode 100644 ctags/main/entry_private.c rename ctags/main/{error.h => error_p.h} (100%) copy ctags/main/{field.h => field_p.h} (51%) 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/interactive_p.h create mode 100644 ctags/main/keyword_p.h rewrite ctags/main/kind.c (73%) create mode 100644 ctags/main/kind_p.h delete mode 100644 ctags/main/lcpp.c delete mode 100644 ctags/main/lcpp.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/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 rewrite ctags/main/mbcs.h (100%) create mode 100644 ctags/main/mbcs_p.h create mode 100644 ctags/main/mini-geany.c rewrite ctags/main/options.h (88%) copy ctags/main/{options.h => options_p.h} (68%) 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/param_p.h rewrite ctags/main/parse.h (68%) create mode 100644 ctags/main/parse_p.h delete mode 100644 ctags/main/parsers.h create mode 100644 ctags/main/parsers_p.h delete mode 100644 ctags/main/pcoproc.c delete mode 100644 ctags/main/pcoproc.h create mode 100644 ctags/main/portable-dirent_p.h create mode 100644 ctags/main/portable-scandir.c copy ctags/main/{promise.h => promise_p.h} (57%) rename ctags/main/{ptag.h => ptag_p.h} (56%) create mode 100644 ctags/main/rbtree.c create mode 100644 ctags/main/rbtree.h create mode 100644 ctags/main/read_p.h copy ctags/main/{routines.h => routines_p.h} (52%) 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.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 rewrite ctags/main/trace.h (100%) 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/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 create mode 100644 ctags/main/writer.c create mode 100644 ctags/main/writer_p.h rewrite ctags/main/xtag.c (66%) create mode 100644 ctags/main/xtag_p.h diff --git a/configure.ac b/configure.ac index d835b86a1..baa0e38e9 100644 --- a/configure.ac +++ b/configure.ac @@ -43,8 +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([CTAGS_LIB], [1], [compile ctags as a library.]) +AC_DEFINE([HAVE_STDBOOL_H], [1], [whether or not to use .]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_OFF_T diff --git a/ctags/Makefile.am b/ctags/Makefile.am index c941aa1e1..ddd805af4 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) \ @@ -30,6 +31,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 \ @@ -53,44 +56,56 @@ 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.h \ + main/args_p.h \ + 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 \ + main/debug.h \ main/dependency.c \ + main/dependency.h \ + main/dependency_p.h \ main/e_msoft.h \ main/entry.c \ main/entry.h \ + main/entry_p.h \ + main/entry_private.c \ 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.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/interactive_p.h \ main/keyword.c \ main/keyword.h \ + main/keyword_p.h \ main/kind.c \ main/kind.h \ - main/lcpp.c \ - main/lcpp.h \ + main/kind_p.h \ main/lregex.c \ - main/lxcmd.c \ + main/lregex.h \ + 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 \ @@ -101,37 +116,63 @@ 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/param_p.h \ main/parse.c \ main/parse.h \ - main/parsers.h \ - main/pcoproc.c \ - main/pcoproc.h \ + main/parse_p.h \ + main/parsers_p.h \ + main/portable-dirent_p.h \ + main/portable-scandir.c \ 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/rbtree.c \ + main/rbtree.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 \ - 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/xtag.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 \ $(parsers) diff --git a/ctags/main/args.c b/ctags/main/args.c index 4ab49134f..60ba28871 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 0b334ceef..a8e4afb5e 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/cmd.c b/ctags/main/cmd.c new file mode 100644 index 000000000..32699dc58 --- /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 new file mode 100644 index 000000000..3af644847 --- /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, unsigned 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, unsigned 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, unsigned 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 000000000..862187d32 --- /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 deleted file mode 100644 index 20b6898ec..000000000 --- a/ctags/main/ctags-api.c +++ /dev/null @@ -1,144 +0,0 @@ -/* -* Copyright (c) 2016, 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. -* -* Defines ctags API when compiled as a library. -*/ - -#include "general.h" /* must always come first */ - -#ifdef CTAGS_LIB - -#include "ctags-api.h" -#include "types.h" -#include "routines.h" -#include "error.h" -#include "output.h" -#include "parse.h" -#include "options.h" -#include "trashbox.h" - -#include -#include -#include - -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; -} - -extern void ctagsInit(void) -{ - setErrorPrinter (nofatalErrorPrinter, NULL); - setTagWriter (&ctagsWriter); - - checkRegex (); - initFieldDescs (); - - initializeParsing (); - initOptions (); - - initDefaultTrashBox (); - - /* make sure all parsers are initialized */ - initializeParser (LANG_AUTO); -} - - - -extern void ctagsParse(unsigned char *buffer, size_t bufferSize, - const char *fileName, const langType language, - tagEntryFunction tagCallback, passStartCallback passCallback, - void *userData) -{ - if (buffer == NULL && fileName == NULL) - { - error(FATAL, "Neither buffer nor file provided to ctagsParse()"); - return; - } - - createTagsWithFallback(buffer, bufferSize, fileName, language, - tagCallback, passCallback, userData); -} - - -extern const char *ctagsGetLangName(int lang) -{ - return getLanguageName(lang); -} - - -extern int ctagsGetNamedLang(const char *name) -{ - return getNamedLanguage(name, 0); -} - - -extern const char *ctagsGetLangKinds(int lang) -{ - const parserDefinition *def = getParserDefinition(lang); - unsigned int i; - static char kinds[257]; - - for (i = 0; i < def->kindCount; i++) - kinds[i] = def->kindTable[i].letter; - kinds[i] = '\0'; - - return kinds; -} - - -extern const char *ctagsGetKindName(char kind, int lang) -{ - const parserDefinition *def = getParserDefinition(lang); - unsigned int i; - - for (i = 0; i < def->kindCount; i++) - { - if (def->kindTable[i].letter == kind) - return def->kindTable[i].name; - } - return "unknown"; -} - - -extern char ctagsGetKindFromName(const char *name, int lang) -{ - const parserDefinition *def = getParserDefinition(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 '-'; -} - - -extern bool ctagsIsUsingRegexParser(int lang) -{ - return getParserDefinition(lang)->method & METHOD_REGEX; -} - - -extern unsigned int ctagsGetLangCount(void) -{ - return countParsers(); -} - -#endif /* CTAGS_LIB */ diff --git a/ctags/main/ctags-api.h b/ctags/main/ctags-api.h deleted file mode 100644 index f973ef07d..000000000 --- a/ctags/main/ctags-api.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -* Copyright (c) 2016, 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. -* -* Defines ctags API when compiled as a library. -*/ -#ifndef CTAGS_API_H -#define CTAGS_API_H - -#include "general.h" /* must always come first */ - -#ifdef CTAGS_LIB - -#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 at the beginning of every parsing pass. The return value is - * currently unused */ -typedef bool (*passStartCallback) (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, - void *userData); -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 bool ctagsIsUsingRegexParser(int lang); -extern unsigned int ctagsGetLangCount(void); - -#endif /* CTAGS_LIB */ - -#endif /* CTAGS_API_H */ diff --git a/ctags/main/ctags.h b/ctags/main/ctags.h index 64faea5bb..4fd18ba0e 100644 --- a/ctags/main/ctags.h +++ b/ctags/main/ctags.h @@ -17,7 +17,7 @@ #if defined (HAVE_CONFIG_H) # define PROGRAM_VERSION PACKAGE_VERSION #else -# define PROGRAM_VERSION "0.0.0" +# define PROGRAM_VERSION "5.9.0" #endif #define PROGRAM_NAME "Universal Ctags" #define PROGRAM_URL "https://ctags.io/" @@ -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 0dab409a8..d68aaeece 100644 --- a/ctags/main/debug.c +++ b/ctags/main/debug.c @@ -16,16 +16,22 @@ #include #include #include +#include #include "debug.h" +#include "entry_p.h" #include "options.h" +#include "parse_p.h" #include "read.h" +#include "read_p.h" /* * FUNCTION DEFINITIONS */ #ifdef DEBUG +#include "htable.h" + extern void lineBreak (void) {} /* provides a line-specified break point */ @@ -74,11 +80,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 +136,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 4d9185df7..b9f224286 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) @@ -52,6 +51,10 @@ # endif #endif +#ifdef DEBUG +/* This makes valgrind report an error earlier. */ +#define DISABLE_OBJPOOL +#endif /* * Data declarations */ @@ -79,4 +82,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 dissimilarity index 72% index a78b55357..13f27aac7 100644 --- a/ctags/main/dependency.c +++ b/ctags/main/dependency.c @@ -1,102 +1,355 @@ -/* - * - * 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" /* must always come first */ - -#include "dependency.h" -#include "parse.h" - -#include - - -static void linkKinds (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; -} - -static void linkKindDependency (parserDefinition *const masterParser, - parserDefinition *const slaveParser) -{ - unsigned int k_slave, k_master; - kindDefinition *kind_slave, *kind_master; - - for (k_slave = 0; k_slave < slaveParser->kindCount; k_slave++) - { - if (slaveParser->kindTable [k_slave].syncWith == LANG_AUTO) - { - kind_slave = slaveParser->kindTable + k_slave; - for (k_master = 0; k_master < masterParser->kindCount; k_master++) - { - 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; - } - } - } - } -} - -extern void linkDependencyAtInitializeParsing (depType dtype, - parserDefinition *const masterParser, - parserDefinition *const slaveParser) -{ - if (dtype == DEPTYPE_KIND_OWNER) - linkKindDependency (masterParser, slaveParser); - else if (dtype == DEPTYPE_SUBPARSER) - { - subparser *s = xMalloc (1, subparser); - - s->id = slaveParser->id; - s->next = masterParser->subparsers; - masterParser->subparsers = s; - } -} - -extern void initializeSubparsers (const parserDefinition *parser) -{ - subparser *sp; - - for (sp = parser->subparsers; sp; sp = sp->next) - initializeParser (sp->id); -} - -extern void finalizeSubparsers (parserDefinition *parser) -{ - subparser *sp; - subparser *tmp; - - for (sp = parser->subparsers; sp;) - { - tmp = sp; - sp = sp->next; - tmp->next = NULL; - eFree (tmp); - } - parser->subparsers = NULL; -} +/* + * + * 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" /* must always come first */ + +#include "debug.h" +#include "dependency.h" +#include "options.h" +#include "parse_p.h" +#include "read.h" +#include "read_p.h" +#include "routines.h" +#include "subparser.h" +#include "subparser_p.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; +}; + +extern void linkDependencyAtInitializeParsing (depType dtype, + parserDefinition *const master, + struct slaveControlBlock *masterSCB, + struct kindControlBlock *masterKCB, + parserDefinition *const slave, + struct kindControlBlock *slaveKCB, + void *data) +{ + if (dtype == DEPTYPE_KIND_OWNER) + linkKindDependency (masterKCB, slaveKCB); + else if (dtype == DEPTYPE_SUBPARSER) + { + slaveParser *s = xMalloc (1, slaveParser); + + s->type = dtype; + s->id = slave->id; + s->data = data; + + s->next = masterSCB->slaveParsers; + masterSCB->slaveParsers = s; + } +} + +static void attachSubparser (struct slaveControlBlock *base_sb, subparser *subparser) +{ + subparser->next = base_sb->subparsersDefault; + base_sb->subparsersDefault = subparser; +} + + +extern struct slaveControlBlock *allocSlaveControlBlock (parserDefinition *parser) +{ + struct slaveControlBlock *cb; + + 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 (sp->type == DEPTYPE_SUBPARSER) + { + 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_SUBPARSER)) + { + 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 finalizeDependencies (parserDefinition *parser, + struct slaveControlBlock *cb) +{ + while (cb->slaveParsers) + { + slaveParser *sp = cb->slaveParsers; + cb->slaveParsers = sp->next; + sp->next = NULL; + eFree (sp); + } +} + +extern void notifyInputStart (void) +{ + subparser *s; + + foreachSubparser(s, false) + { + langType lang = getSubparserLanguage (s); + notifyLanguageRegexInputStart (lang); + + if (s->inputStart) + { + enterSubparser(s); + s->inputStart (s); + leaveSubparser(); + } + } +} + +extern void notifyInputEnd (void) +{ + subparser *s; + + foreachSubparser(s, false) + { + if (s->inputEnd) + { + enterSubparser(s); + s->inputEnd (s); + leaveSubparser(); + } + + langType lang = getSubparserLanguage (s); + notifyLanguageRegexInputEnd (lang); + } +} + +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 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 *tmp; + subparser *s = NULL; + + tmp = controlBlock->subparsersInUse; + controlBlock->subparsersInUse = NULL; + + if (tmp && tmp->schedulingBaseparserExplicitly) + { + tmp->schedulingBaseparserExplicitly = false; + s = tmp; + } + + 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) +{ + if (getLanguageForBaseParser () == getInputLanguage()) + return false; + 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 551e81a1c..258aee4a1 100644 --- a/ctags/main/dependency.h +++ b/ctags/main/dependency.h @@ -12,34 +12,34 @@ #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" +/* +* DATA DECLARATIONS +*/ typedef enum eDepType { DEPTYPE_KIND_OWNER, DEPTYPE_SUBPARSER, 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); - #endif /* CTAGS_MAIN_DEPENDENCY_H */ diff --git a/ctags/main/dependency_p.h b/ctags/main/dependency_p.h new file mode 100644 index 000000000..d59e9b3d6 --- /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/e_msoft.h b/ctags/main/e_msoft.h index e09519752..90c6d07b3 100644 --- a/ctags/main/e_msoft.h +++ b/ctags/main/e_msoft.h @@ -14,34 +14,30 @@ #define MSDOS_STYLE_PATH 1 #define HAVE_FCNTL_H 1 #define HAVE_IO_H 1 -#define HAVE_LIMITS_H 1 -#define HAVE_STDLIB_H 1 #define HAVE_SYS_STAT_H 1 #define HAVE_SYS_TYPES_H 1 -#define HAVE_TIME_H 1 -#define HAVE_CLOCK 1 #define HAVE_CHSIZE 1 -#define HAVE_FGETPOS 1 +#define HAVE_DIRECT_H 1 #define HAVE_STRICMP 1 #define HAVE_STRNICMP 1 #define HAVE_STRSTR 1 #define HAVE_STRERROR 1 +#define HAVE__FINDFIRST 1 #define HAVE_FINDNEXT 1 -#define HAVE_TEMPNAM 1 +#define findfirst_t intptr_t +#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 -# define HAVE_DIRECT_H 1 # if _MSC_VER < 1900 # define snprintf _snprintf # endif -# define findfirst_t intptr_t #if (_MSC_VER >= 1800) // Visual Studio 2013 or newer #define HAVE_STDBOOL_H 1 @@ -58,14 +54,15 @@ 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 # define ffblk _finddata_t # 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 b2ea525ec..d07de24ef 100644 --- a/ctags/main/entry.c +++ b/ctags/main/entry.c @@ -36,20 +36,30 @@ # include #endif +#include +#include /* to define INT_MAX */ + #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 "rbtree.h" #include "read.h" +#include "read_p.h" #include "routines.h" -#include "sort.h" +#include "routines_p.h" +#include "parse_p.h" +#include "ptrarray.h" +#include "sort_p.h" #include "strlist.h" -#include "xtag.h" +#include "subparser_p.h" +#include "trashbox.h" +#include "writer_p.h" +#include "xtag_p.h" /* * MACROS @@ -82,21 +92,25 @@ typedef struct eTagFile { struct sMax { size_t line, tag; } max; vString *vLine; - unsigned int cork; - struct sCorkQueue { - struct sTagEntryInfo* queue; - unsigned int length; - unsigned int count; - } corkQueue; + int cork; + unsigned int corkFlags; + ptrArray *corkQueue; bool patternCacheValid; } tagFile; +typedef struct sTagEntryInfoX { + tagEntryInfo slot; + int corkIndex; + struct rb_root symtab; + struct rb_node symnode; +} tagEntryInfoX; + /* * DATA DEFINITIONS */ -tagFile TagFile = { +static tagFile TagFile = { NULL, /* tag file name */ NULL, /* tag file directory (absolute) */ NULL, /* file pointer */ @@ -104,21 +118,12 @@ tagFile TagFile = { { 0, 0 }, /* max */ NULL, /* vLine */ .cork = false, - .corkQueue = { - .queue = NULL, - .length = 0, - .count = 0 - }, + .corkQueue = NULL, .patternCacheValid = false, }; static bool TagsToStdout = false; -#ifdef CTAGS_LIB -static tagEntryFunction TagEntryFunction = NULL; -static void *TagEntryUserData = NULL; -#endif - /* * FUNCTION PROTOTYPES */ @@ -152,10 +157,8 @@ extern const char *tagFileName (void) extern void abort_if_ferror(MIO *const mio) { -#ifndef CTAGS_LIB - if (mio_error (mio)) + if (mio != NULL && mio_error (mio)) error (FATAL | PERROR, "cannot write tag file"); -#endif } static void rememberMaxLengths (const size_t nameLength, const size_t lineLength) @@ -169,52 +172,38 @@ 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); + makePtagIfEnabled (i, LANG_IGNORE, &Option); } } 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 ( @@ -382,7 +371,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; } @@ -399,9 +388,13 @@ 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.interactive == INTERACTIVE_SANDBOX) + { + TagFile.mio = mio_new_memory (NULL, 0, eRealloc, eFreeNoNullCheck); + TagFile.name = NULL; + } + else + TagFile.mio = tempFile ("w+", &TagFile.name); if (isXtagEnabled (XTAG_PSEUDO_TAGS)) addCommonPseudoTags (); } @@ -431,7 +424,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+"); } } @@ -445,10 +438,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 @@ -485,9 +482,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); } } @@ -495,9 +492,10 @@ 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); + mio_unref (mio); copyFile (name, tempName, size); copyFile (tempName, name, WHOLE_FILE); remove (tempName); @@ -532,7 +530,7 @@ static void internalSortTagFile (void) TagFile.numTags.added + TagFile.numTags.prev); if (! TagsToStdout) - mio_free (mio); + mio_unref (mio); } #endif @@ -558,6 +556,12 @@ static void resizeTagFile (const long newSize) { int result; + if (!TagFile.name) + { + mio_try_resize (TagFile.mio, newSize); + return; + } + #ifdef USE_REPLACEMENT_TRUNCATE result = replacementTruncate (TagFile.name, newSize); #else @@ -605,30 +609,35 @@ extern void closeTagFile (const bool resize) if (Option.etags) writeEtagsIncludes (TagFile.mio); mio_flush (TagFile.mio); + abort_if_ferror (TagFile.mio); desiredSize = mio_tell (TagFile.mio); mio_seek (TagFile.mio, 0L, SEEK_END); 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) { DebugStatement ( debugPrintf (DEBUG_STATUS, "shrinking %s from %ld to %ld bytes\n", - TagFile.name, size, desiredSize); ) + TagFile.name? TagFile.name: "", size, desiredSize); ) resizeTagFile (desiredSize); } 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 */ + if (TagFile.name) + remove (TagFile.name); /* remove temporary file */ } - eFree (TagFile.name); + + TagFile.mio = NULL; + if (TagFile.name) + eFree (TagFile.name); TagFile.name = NULL; } @@ -641,10 +650,13 @@ extern void closeTagFile (const bool resize) * are doubled and a leading '^' or trailing '$' is also quoted. End of line * characters (line feed or carriage return) are dropped. */ -static size_t appendInputLine (int putc_func (char , void *), const char *const line, void * data, bool *omitted) +static size_t appendInputLine (int putc_func (char , void *), const char *const line, + unsigned int patternLengthLimit, + void * data, bool *omitted) { size_t length = 0; const char *p; + int extraLength = 0; /* Write everything up to, but not including, a line end character. */ @@ -657,7 +669,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 (patternLengthLimit != 0 && length >= 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; @@ -687,11 +703,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); } +#ifdef DEBUG static bool isPosSet(MIOPos pos) { char * p = (char *)&pos; @@ -702,26 +719,23 @@ 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. + * Returns the length of the truncated line (or 0 if it doesn't truncate). */ -extern void truncateTagLine ( +extern size_t truncateTagLineAfterTag ( char *const line, const char *const token, const bool discardNewline) { + size_t len = 0; char *p = strstr (line, token); if (p != NULL) @@ -730,7 +744,10 @@ extern void truncateTagLine ( if (*p != '\0' && ! (*p == '\n' && discardNewline)) ++p; /* skip past character terminating character */ *p = '\0'; + len = p - line; } + + return len; } static char* getFullQualifiedScopeNameFromCorkQueue (const tagEntryInfo * inner_scope) @@ -739,6 +756,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; @@ -760,11 +778,16 @@ static char* getFullQualifiedScopeNameFromCorkQueue (const tagEntryInfo * inner_ stringListAdd (queue, v); kindIndex = scope->kindIndex; lang = scope->langType; + root_scope = scope; } scope = getEntryInCorkQueue (scope->extensionFields.scopeIndex); } n = vStringNew (); + sep = root_scope? scopeSeparatorFor (root_scope->langType, root_scope->kindIndex, KIND_GHOST_INDEX): NULL; + if (sep) + vStringCatS(n, sep); + while ((c = stringListCount (queue)) > 0) { v = stringListLast (queue); @@ -785,16 +808,13 @@ extern void getTagScopeInformation (tagEntryInfo *const tag, if (name) *name = NULL; + const tagEntryInfo * scope = getEntryInCorkQueue (tag->extensionFields.scopeIndex); if (tag->extensionFields.scopeKindIndex == KIND_GHOST_INDEX && tag->extensionFields.scopeName == NULL - && tag->extensionFields.scopeIndex != CORK_NIL - && TagFile.corkQueue.count > 0) + && scope + && ptrArrayCount (TagFile.corkQueue) > 0) { - const tagEntryInfo * scope = NULL; - char *full_qualified_scope_name = NULL; - - scope = getEntryInCorkQueue (tag->extensionFields.scopeIndex); - full_qualified_scope_name = getFullQualifiedScopeNameFromCorkQueue(scope); + char *full_qualified_scope_name = getFullQualifiedScopeNameFromCorkQueue(scope); Assert (full_qualified_scope_name); /* Make the information reusable to generate full qualified entry, and xformat output*/ @@ -821,9 +841,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 +865,33 @@ 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 = ""; + line_len = 0; + } + else + line_len = vStringLength (TagFile.vLine); + if (tag->truncateLineAfterTag) - truncateTagLine (line, tag->name, false); + { + size_t truncted_len; + + truncted_len = truncateTagLineAfterTag (line, tag->name, false); + if (truncted_len > 0) + line_len = truncted_len; + } - 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; @@ -870,7 +903,8 @@ extern int makePatternStringCommon (const tagEntryInfo *const tag, length += putc_func(searchChar, output); if ((tag->boundaryInfo & BOUNDARY_START) == 0) length += putc_func('^', output); - length += appendInputLine (putc_func, line, output, &omitted); + length += appendInputLine (putc_func, line, Option.patternLengthLimit, + output, &omitted); length += puts_func (omitted? "": terminator, output); length += putc_func (searchChar, output); @@ -891,30 +925,97 @@ 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; } -extern void attachParserFieldToCorkEntry (int index, - fieldType type, - const char *value) +static void tagFieldDelete (tagField * f) { - tagEntryInfo * tag; - const char * v; + if (f->valueOwner) + eFree((void *)f->value); + eFree (f); +} - if (index == CORK_NIL) - return; +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++; + } +} - tag = getEntryInCorkQueue(index); +extern void attachParserField (tagEntryInfo *const tag, bool inCorkQueue, fieldType ftype, const char * value) +{ Assert (tag != NULL); - v = eStrdup (value); - attachParserField (tag, type, v); + if (inCorkQueue) + { + const char * v; + 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); + } + else + attachParserFieldGeneric (tag, ftype, value, false); +} + +extern void attachParserFieldToCorkEntry (int index, + fieldType ftype, + const char *value) +{ + tagEntryInfo * tag = getEntryInCorkQueue (index); + if (tag) + attachParserField (tag, true, ftype, value); +} + +extern const tagField* getParserFieldForIndex (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); + } +} + +extern const char* getParserFieldValueForType (tagEntryInfo *const tag, fieldType ftype) +{ + for (int i = 0; i < tag->usedParserFields; i++) + { + const tagField *f = getParserFieldForIndex (tag, i); + if (f && f->ftype == ftype) + return f->value; + } + return NULL; } static void copyParserFields (const tagEntryInfo *const tag, tagEntryInfo* slot) @@ -924,24 +1025,42 @@ static void copyParserFields (const tagEntryInfo *const tag, tagEntryInfo* slot) for (i = 0; i < tag->usedParserFields; i++) { - value = tag->parserFields [i].value; + const tagField *f = getParserFieldForIndex (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) +static tagEntryInfo *newNilTagEntry (unsigned int corkFlags) { + tagEntryInfoX *x = xCalloc (1, tagEntryInfoX); + x->corkIndex = CORK_NIL; + x->symtab = RB_ROOT; + x->slot.kindIndex = KIND_FILE_INDEX; + return &(x->slot); +} + +static tagEntryInfoX *copyTagEntry (const tagEntryInfo *const tag, + unsigned int corkFlags) +{ + tagEntryInfoX *x = xMalloc (1, tagEntryInfoX); + x->symtab = RB_ROOT; + x->corkIndex = CORK_NIL; + tagEntryInfo *slot = (tagEntryInfo *)x; + *slot = *tag; 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); @@ -961,39 +1080,63 @@ 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 */ - if (slot->extensionFields.varType) - slot->extensionFields.varType = eStrdup (slot->extensionFields.varType); -/* GEANY DIFF END */ #ifdef HAVE_LIBXML if (slot->extensionFields.xpath) 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); + + return x; } 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) +static void deleteTagEnry (void *data) { + tagEntryInfo *slot = data; + + if (slot->kindIndex == KIND_FILE_INDEX) + goto out; + if (slot->pattern) eFree ((char *)slot->pattern); eFree ((char *)slot->inputFileName); @@ -1015,139 +1158,409 @@ static void clearTagEntryInQueue (tagEntryInfo* slot) eFree ((char *)slot->extensionFields.typeRef[0]); if (slot->extensionFields.typeRef[1]) eFree ((char *)slot->extensionFields.typeRef[1]); -/* GEANY DIFF */ - if (slot->extensionFields.varType) - eFree ((char *)slot->extensionFields.varType); -/* GEANY DIFF END */ #ifdef HAVE_LIBXML if (slot->extensionFields.xpath) eFree ((char *)slot->extensionFields.xpath); #endif + if (slot->extraDynamic) + eFree (slot->extraDynamic); + if (slot->sourceFileName) eFree ((char *)slot->sourceFileName); clearParserFields (slot); + + out: + eFree (slot); } -static unsigned int queueTagEntry(const tagEntryInfo *const tag) +static void corkSymtabPut (tagEntryInfoX *scope, const char* name, tagEntryInfoX *item) { - unsigned int i; - void *tmp; - tagEntryInfo * slot; + struct rb_root *root = &scope->symtab; + + struct rb_node **new = &(root->rb_node), *parent = NULL; - if (! (TagFile.corkQueue.count < TagFile.corkQueue.length)) + while (*new) { - if (!TagFile.corkQueue.length) - TagFile.corkQueue.length = 1; + tagEntryInfoX *this = container_of(*new, tagEntryInfoX, symnode); + int result = strcmp(item->slot.name, this->slot.name); + + parent = *new; - tmp = eRealloc (TagFile.corkQueue.queue, - sizeof (*TagFile.corkQueue.queue) * TagFile.corkQueue.length * 2); + if (result < 0) + new = &((*new)->rb_left); + else if (result > 0) + new = &((*new)->rb_right); + else + { + unsigned long lthis = this->slot.lineNumber; + unsigned long litem = item->slot.lineNumber; + + /* Comparing lineNumber */ + if (litem < lthis) + new = &((*new)->rb_left); + else if (litem > lthis) + new = &((*new)->rb_right); + else + { + /* Comparing memory address */ + if (item < this) + new = &((*new)->rb_left); + else if (item > this) + new = &((*new)->rb_right); + else + { + AssertNotReached(); /* registering the same object twice. */ + return; + } + } + } + } - TagFile.corkQueue.length *= 2; - TagFile.corkQueue.queue = tmp; + verbose ("symtbl[:=] %s<-%s/%p (line: %lu)\n", + *new? container_of(*new, tagEntryInfoX, symnode)->slot.name: "*root*", + item->slot.name, &item->slot, item->slot.lineNumber); + /* Add new node and rebalance tree. */ + rb_link_node(&item->symnode, parent, new); + rb_insert_color(&item->symnode, root); +} + +extern bool foreachEntriesInScope (int corkIndex, + const char *name, + entryForeachFunc func, + void *data) +{ + tagEntryInfoX *x = ptrArrayItem (TagFile.corkQueue, corkIndex); + + struct rb_root *root = &x->symtab; + tagEntryInfoX *rep = NULL; + + /* More than one tag can have a same name. + * Visit them from the last. + * + * 1. find one of them as the representative, + * 2. find the last one of them from the representative with rb_next, + * 3. call FUNC iteratively from the last to the first. + */ + if (name) + { + struct rb_node *node = root->rb_node; + while (node) + { + tagEntryInfoX *entry = container_of(node, tagEntryInfoX, symnode); + int result; + + result = strcmp(name, entry->slot.name); + + if (result < 0) + node = node->rb_left; + else if (result > 0) + node = node->rb_right; + else + { + rep = entry; + break; + } + } + if (rep == NULL) + return true; + + verbose("symtbl[<>] %s->%p\n", name, &rep->slot); + } + + struct rb_node *last; + + if (name) + { + struct rb_node *tmp = &rep->symnode; + last = tmp; + + while ((tmp = rb_next (tmp))) + { + tagEntryInfoX *entry = container_of(tmp, tagEntryInfoX, symnode); + if (strcmp(name, entry->slot.name) == 0) + { + verbose ("symtbl[ >] %s->%p\n", name, &container_of(tmp, tagEntryInfoX, symnode)->slot); + last = tmp; + } + else + break; + } + } + else + { + last = rb_last(root); + verbose ("last for %d<%p>: %p\n", corkIndex, root, last); } - i = TagFile.corkQueue.count; - TagFile.corkQueue.count++; + if (!last) + { + verbose ("symtbl[>V] %s->%p\n", name? name: "(null)", NULL); + return true; /* Nothing here in this node. */ + } + verbose ("symtbl[>|] %s->%p\n", name, &container_of(last, tagEntryInfoX, symnode)->slot); - slot = TagFile.corkQueue.queue + i; - recordTagEntryInQueue (tag, slot); + struct rb_node *cursor = last; + bool revisited_rep = false; + do + { + tagEntryInfoX *entry = container_of(cursor, tagEntryInfoX, symnode); + if (!revisited_rep || !name || strcmp(name, entry->slot.name)) + { + verbose ("symtbl[< ] %s->%p\n", name, &entry->slot); + if (!func (entry->corkIndex, &entry->slot, data)) + return false; + if (cursor == &rep->symnode) + revisited_rep = true; + } + else if (name) + break; + } + while ((cursor = rb_prev(cursor))); - return i; + return true; } +static bool findName (int corkIndex, tagEntryInfo *entry, void *data) +{ + int *index = data; -static void *writerData; -static tagWriter *writer; + *index = corkIndex; + return false; +} -extern void setTagWriter (tagWriter *t) +int anyEntryInScope (int corkIndex, const char *name) { - writer = t; + int index = CORK_NIL; + + if (foreachEntriesInScope (corkIndex, name, findName, &index) == false) + return index; + + return CORK_NIL; } -extern bool outpuFormatUsedStdoutByDefault (void) +struct anyKindsEntryInScopeData { + int index; + const int *kinds; + int count; +}; + +static bool findNameOfKinds (int corkIndex, tagEntryInfo *entry, void *data) { - return writer->useStdoutByDefault; + struct anyKindsEntryInScopeData * kdata = data; + + for (int i = 0; i < kdata->count; i++) + { + int k = kdata->kinds [i]; + if (entry->kindIndex == k) + { + kdata->index = corkIndex; + return false; + } + } + return true; } -extern void setupWriter (void) +int anyKindEntryInScope (int corkIndex, + const char *name, int kind) { - if (writer->preWriteEntry) - writerData = writer->preWriteEntry (TagFile.mio); - else - writerData = NULL; + return anyKindsEntryInScope (corkIndex, name, &kind, 1); } -extern void teardownWriter (const char *filename) +int anyKindsEntryInScope (int corkIndex, + const char *name, + const int *kinds, int count) { - if (writer->postWriteEntry) - writer->postWriteEntry (TagFile.mio, filename, writerData); + struct anyKindsEntryInScopeData data = { + .index = CORK_NIL, + .kinds = kinds, + .count = count, + }; + + if (foreachEntriesInScope (corkIndex, name, findNameOfKinds, &data) == false) + return data.index; + + return CORK_NIL; } -static void buildFqTagCache (const tagEntryInfo *const tag) +int anyKindsEntryInScopeRecursive (int corkIndex, + const char *name, + const int *kinds, int count) { - renderFieldEscaped (FIELD_SCOPE_KIND_LONG, tag, NO_PARSER_FIELD); - renderFieldEscaped (FIELD_SCOPE, tag, NO_PARSER_FIELD); + struct anyKindsEntryInScopeData data = { + .index = CORK_NIL, + .kinds = kinds, + .count = count, + }; + + tagEntryInfo *e; + do + { + if (foreachEntriesInScope (corkIndex, name, findNameOfKinds, &data) == false) + return data.index; + + if (corkIndex == CORK_NIL) + break; + + e = getEntryInCorkQueue (corkIndex); + if (!e) + break; + corkIndex = e->extensionFields.scopeIndex; + } + while (1); + + return CORK_NIL; } -#ifdef CTAGS_LIB -static void initCtagsTag(ctagsTag *tag, const tagEntryInfo *info) +extern void registerEntry (int corkIndex) { - tag->name = info->name; - tag->signature = info->extensionFields.signature; - tag->scopeName = info->extensionFields.scopeName; - tag->inheritance = info->extensionFields.inheritance; - tag->varType = info->extensionFields.varType; - 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; + Assert (TagFile.corkFlags & CORK_SYMTAB); + Assert (corkIndex != CORK_NIL); + + tagEntryInfoX *e = ptrArrayItem (TagFile.corkQueue, corkIndex); + { + tagEntryInfoX *scope = ptrArrayItem (TagFile.corkQueue, e->slot.extensionFields.scopeIndex); + corkSymtabPut (scope, e->slot.name, e); + } +} + +static int queueTagEntry(const tagEntryInfo *const tag) +{ + static bool warned; + + int corkIndex; + tagEntryInfoX * entry = copyTagEntry (tag, + TagFile.corkFlags); + + if (ptrArrayCount (TagFile.corkQueue) == (size_t)INT_MAX) + { + if (!warned) + { + warned = true; + error (WARNING, + "The tag entry queue overflows; drop the tag entry at %lu in %s", + tag->lineNumber, + tag->inputFileName); + } + return CORK_NIL; + } + warned = false; + + corkIndex = (int)ptrArrayAdd (TagFile.corkQueue, entry); + entry->corkIndex = corkIndex; + + return corkIndex; +} + +extern void setupWriter (void *writerClientData) +{ + writerSetup (TagFile.mio, writerClientData); +} + +extern bool teardownWriter (const char *filename) +{ + return writerTeardown (TagFile.mio, filename); +} + +static bool isTagWritable(const tagEntryInfo *const tag) +{ + if (tag->placeholder) + return false; + + if (! isLanguageKindEnabled(tag->langType, tag->kindIndex)) + return false; + + 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 representing 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; +} + +static void buildFqTagCache (tagEntryInfo *const tag) +{ + getTagScopeInformation (tag, NULL, NULL); } -#endif static void writeTagEntry (const tagEntryInfo *const tag) { 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)) - return; + Assert (tag->kindIndex != KIND_GHOST_INDEX); DebugStatement ( debugEntry (tag); ) - Assert (writer); + +#ifdef WIN32 + if (getFilenameSeparator(Option.useSlashAsFilenameSeparator) == FILENAME_SEP_USE_SLASH) + { + Assert (((const tagEntryInfo *)tag)->inputFileName); + char *c = (char *)(((tagEntryInfo *const)tag)->inputFileName); + while (*c) + { + if (*c == PATH_SEPARATOR) + *c = OUTPUT_PATH_SEPARATOR; + c++; + } + } +#endif if (includeExtensionFlags () && isXtagEnabled (XTAG_QUALIFIED_TAGS) - && doesInputLanguageRequestAutomaticFQTag ()) - buildFqTagCache (tag); + && doesInputLanguageRequestAutomaticFQTag () + && !isTagExtraBitMarked (tag, XTAG_QUALIFIED_TAGS) + && !tag->skipAutoFQEmission) + { + /* const is discarded to update the cache field of TAG. */ + buildFqTagCache ( (tagEntryInfo *const)tag); + } -#ifdef CTAGS_LIB - getTagScopeInformation((tagEntryInfo *)tag, NULL, NULL); + length = writerWriteTag (TagFile.mio, tag); - if (TagEntryFunction != NULL) + if (length > 0) { - ctagsTag t; - - initCtagsTag(&t, tag); - length = TagEntryFunction(&t, TagEntryUserData); + ++TagFile.numTags.added; + rememberMaxLengths (strlen (tag->name), (size_t) length); } -#else - length = writer->writeEntry (TagFile.mio, tag, writerData); -#endif - - ++TagFile.numTags.added; - rememberMaxLengths (strlen (tag->name), (size_t) length); - DebugStatement ( mio_flush (TagFile.mio); ) + DebugStatement ( if (TagFile.mio) mio_flush (TagFile.mio); ) abort_if_ferror (TagFile.mio); } @@ -1159,11 +1572,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; @@ -1172,15 +1585,15 @@ extern bool writePseudoTag (const ptagDesc *desc, return true; } -extern void corkTagFile(void) +extern void corkTagFile(unsigned int corkFlags) { TagFile.cork++; if (TagFile.cork == 1) { - TagFile.corkQueue.length = 1; - TagFile.corkQueue.count = 1; - TagFile.corkQueue.queue = eMalloc (sizeof (*TagFile.corkQueue.queue)); - memset (TagFile.corkQueue.queue, 0, sizeof (*TagFile.corkQueue.queue)); + TagFile.corkFlags = corkFlags; + TagFile.corkQueue = ptrArrayNew (deleteTagEnry); + tagEntryInfo *nil = newNilTagEntry (corkFlags); + ptrArrayAdd (TagFile.corkQueue, nil); } } @@ -1193,32 +1606,36 @@ extern void uncorkTagFile(void) if (TagFile.cork > 0) return ; - for (i = 1; i < TagFile.corkQueue.count; i++) + for (i = 1; i < ptrArrayCount (TagFile.corkQueue); i++) { - tagEntryInfo *tag = TagFile.corkQueue.queue + i; + tagEntryInfo *tag = ptrArrayItem (TagFile.corkQueue, i); + + if (!isTagWritable(tag)) + continue; + writeTagEntry (tag); + 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++) - clearTagEntryInQueue (TagFile.corkQueue.queue + i); - memset (TagFile.corkQueue.queue, 0, - sizeof (*TagFile.corkQueue.queue) * TagFile.corkQueue.count); - TagFile.corkQueue.count = 0; - eFree (TagFile.corkQueue.queue); - TagFile.corkQueue.queue = NULL; - TagFile.corkQueue.length = 0; + ptrArrayDelete (TagFile.corkQueue); + TagFile.corkQueue = NULL; } -extern tagEntryInfo *getEntryInCorkQueue (unsigned int n) +extern tagEntryInfo *getEntryInCorkQueue (int n) { - if ((CORK_NIL < n) && (n < TagFile.corkQueue.count)) - return TagFile.corkQueue.queue + n; + if ((CORK_NIL < n) && (((size_t)n) < ptrArrayCount (TagFile.corkQueue))) + return ptrArrayItem (TagFile.corkQueue, n); else return NULL; } @@ -1232,25 +1649,38 @@ extern tagEntryInfo *getEntryOfNestingLevel (const NestingLevel *nl) extern size_t countEntryInCorkQueue (void) { - return TagFile.corkQueue.count; + return ptrArrayCount (TagFile.corkQueue); +} + +extern int makePlaceholder (const char *const name) +{ + tagEntryInfo e; + + initTagEntry (&e, name, KIND_GHOST_INDEX); + e.placeholder = 1; + + /* + * makePlaceholder may be called even before reading any bytes + * from the input stream. In such case, initTagEntry fills + * the lineNumber field of the placeholder tag with 0. + * This breaks an assertion in makeTagEntry. Following adjustment + * is for avoiding it. + */ + if (e.lineNumber == 0) + e.lineNumber = 1; + + return makeTagEntry (&e); } extern int makeTagEntry (const tagEntryInfo *const tag) { int r = CORK_NIL; Assert (tag->name != NULL); + Assert(tag->lineNumber > 0); -#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; - } -#endif + if (!TagFile.cork) + if (!isTagWritable (tag)) + goto out; if (tag->name [0] == '\0' && (!tag->placeholder)) { @@ -1264,6 +1694,10 @@ extern int makeTagEntry (const tagEntryInfo *const tag) r = queueTagEntry (tag); else writeTagEntry (tag); + + if (r != CORK_NIL) + notifyMakeTagEntry (tag, r); + out: return r; } @@ -1272,7 +1706,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 +1715,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 +1732,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 +1744,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_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 +1796,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_GUEST); + if (doesSubparserRun ()) + markTagExtraBit (e, XTAG_SUBPARSER); + e->sourceLangType = sourceLangType; e->sourceFileName = sourceFileName; e->sourceLineNumberDifference = sourceLineNumberDifference; @@ -1394,30 +1815,163 @@ 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) +{ + initForeignRefTagEntry (e, name, getInputLanguage (), kindIndex, roleIndex); +} + +extern void initForeignRefTagEntry (tagEntryInfo *const e, const char *const name, + langType langType, + int kindIndex, int roleIndex) +{ + initTagEntryFull(e, name, + getInputLineNumber (), + langType, + 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)); +} + +extern bool isTagExtra (const tagEntryInfo *const tag) +{ + for (unsigned int i = 0; i < XTAG_COUNT; i++) + if (isTagExtraBitMarked (tag, i)) + return true; + return false; +} + +static void assignRoleFull(tagEntryInfo *const e, int roleIndex, bool assign) +{ + if (roleIndex == ROLE_DEFINITION_INDEX) + { + if (assign) + { + e->extensionFields.roleBits = 0; + markTagExtraBitFull (e, XTAG_REFERENCE_TAGS, false); + } + } + else if (roleIndex > ROLE_DEFINITION_INDEX) + { + 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_DEFINITION_INDEX) + return (!e->extensionFields.roleBits); + else + return (e->extensionFields.roleBits & makeRoleBit(roleIndex)); } extern unsigned long numTagsAdded(void) @@ -1447,12 +2001,24 @@ extern void invalidatePatternCache(void) extern void tagFilePosition (MIOPos *p) { - mio_getpos (TagFile.mio, p); + /* mini-geany doesn't set TagFile.mio. */ + if (TagFile.mio == NULL) + return; + + if (mio_getpos (TagFile.mio, p) == -1) + error (FATAL|PERROR, + "failed to get file position of the tag file\n"); } extern void setTagFilePosition (MIOPos *p) { - mio_setpos (TagFile.mio, p); + /* mini-geany doesn't set TagFile.mio. */ + if (TagFile.mio == NULL) + return; + + if (mio_setpos (TagFile.mio, p) == -1) + error (FATAL|PERROR, + "failed to set file position of the tag file\n"); } extern const char* getTagFileDirectory (void) @@ -1460,10 +2026,14 @@ extern const char* getTagFileDirectory (void) return TagFile.directory; } -#ifdef CTAGS_LIB -extern void setTagEntryFunction(tagEntryFunction entry_function, void *user_data) +static bool markAsPlaceholder (int index, tagEntryInfo *e, void *data CTAGS_ATTR_UNUSED) { - TagEntryFunction = entry_function; - TagEntryUserData = user_data; + e->placeholder = 1; + markAllEntriesInScopeAsPlaceholder (index); + return true; +} + +extern void markAllEntriesInScopeAsPlaceholder (int index) +{ + foreachEntriesInScope (index, NULL, markAsPlaceholder, NULL); } -#endif diff --git a/ctags/main/entry.h b/ctags/main/entry.h index c938c43f9..335ec01e7 100644 --- a/ctags/main/entry.h +++ b/ctags/main/entry.h @@ -16,21 +16,16 @@ #include "types.h" #include -#include #include "field.h" -#include "kind.h" -#include "vstring.h" #include "xtag.h" #include "mio.h" +#include "ptrarray.h" #include "nestlevel.h" -#include "ctags-api.h" /* * MACROS */ -#define WHOLE_FILE -1L -#define includeExtensionFlags() (Option.tagFileFormat > 1) /* * DATA DECLARATIONS @@ -38,8 +33,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 { @@ -50,6 +48,10 @@ 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 int isPseudoTag:1; /* Used only in xref output. + If a tag is a pseudo, set this. */ unsigned long lineNumber; /* line number of tag */ const char* pattern; /* pattern for locating input line @@ -60,7 +62,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,12 +87,10 @@ struct sTagEntryInfo { /* type (union/struct/etc.) and name for a variable or typedef. */ const char* typeRef [2]; /* e.g., "struct" and struct name */ -/* GEANY DIFF */ - const char *varType; -/* GEANY DIFF END */ - -#define ROLE_INDEX_DEFINITION -1 - int roleIndex; /* for role of reference tag */ +#define ROLE_DEFINITION_INDEX -1 +#define ROLE_DEFINITION_NAME "def" +#define ROLE_MAX_COUNT (sizeof(roleBitsType) * 8) + roleBitsType roleBits; /* for role of reference tag */ #ifdef HAVE_LIBXML const char* xpath; @@ -97,10 +98,16 @@ struct sTagEntryInfo { 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. */ @@ -109,6 +116,9 @@ struct sTagEntryInfo { unsigned long sourceLineNumberDifference; }; +typedef bool (* entryForeachFunc) (int corkIndex, + tagEntryInfo * entry, + void * data); /* * GLOBAL VARIABLES @@ -118,71 +128,146 @@ 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 void initForeignRefTagEntry (tagEntryInfo *const e, const char *const name, + langType type, + int kindIndex, int roleIndex); +extern void assignRole(tagEntryInfo *const e, int roleIndex); +extern bool isRoleAssigned(const tagEntryInfo *const e, int roleIndex); + 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); +#define CORK_NIL 0 +tagEntryInfo *getEntryInCorkQueue (int n); +tagEntryInfo *getEntryOfNestingLevel (const NestingLevel *nl); +size_t countEntryInCorkQueue (void); + +/* If a parser sets (CORK_QUEUE and )CORK_SYMTAB to useCork, + * the parsesr can use symbol lookup tables for the current input. + * Each scope has a symbol lookup table. + * To register an tag to the table, use registerEntry(). + * registerEntry registers CORKINDEX to a symbol table of a parent tag + * specified in the scopeIndex field of the tag specified with CORKINDEX. + */ +void registerEntry (int corkIndex); -/* Generating pattern associated tag, caller must do eFree for the returned value. */ -extern char* makePatternString (const tagEntryInfo *const tag); +/* foreachEntriesInScope is for traversing the symbol table for a table + * specified with CORKINDEX. If CORK_NIL is given, this function traverses + * top-level entries. If name is NULL, this function traverses all entries + * under the scope. + * + * If FUNC returns false, this function returns false. + * If FUNC never returns false, this func returns true. + * If FUNC is not called because no node for NAME in the symbol table. + */ +bool foreachEntriesInScope (int corkIndex, + const char *name, /* or NULL */ + entryForeachFunc func, + void *data); +/* Return the cork index for NAME in the scope specified with CORKINDEX. + * Even if more than one entries for NAME are in the scope, this function + * just returns one of them. Returning CORK_NIL means there is no entry + * for NAME. + */ +int anyEntryInScope (int corkIndex, + const char *name); -/* language is optional: can be NULL. */ -extern bool writePseudoTag (const ptagDesc *pdesc, - const char *const fileName, - const char *const pattern, - const char *const parserName); +int anyKindEntryInScope (int corkIndex, + const char *name, int kind); -#define CORK_NIL 0 -void corkTagFile(void); -void uncorkTagFile(void); -tagEntryInfo *getEntryInCorkQueue (unsigned int n); -tagEntryInfo *getEntryOfNestingLevel (const NestingLevel *nl); -size_t countEntryInCorkQueue (void); +int anyKindsEntryInScope (int corkIndex, + const char *name, + const int * kinds, int count); -extern void makeFileTag (const char *const fileName); +int anyKindsEntryInScopeRecursive (int corkIndex, + const char *name, + const int * kinds, int count); 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); +/* If any extra bit is on, return true. */ +extern bool isTagExtra (const tagEntryInfo *const tag); + +/* Functions for attaching parser specific fields + * + * Which function you should use? + * ------------------------------ + * Case A: + * + * If your parser uses the Cork API, and your parser called + * makeTagEntry () already, you can use both + * attachParserFieldToCorkEntry () and attachParserField (). Your + * parser has the cork index returned from makeTagEntry (). With the + * cork index, your parser can call attachParserFieldToCorkEntry (). + * If your parser already call getEntryInCorkQueue () to get the tag + * entry for the cork index, your parser can call attachParserField () + * with passing true for `inCorkQueue' parameter. attachParserField () + * is a bit faster than attachParserFieldToCorkEntry (). + * + * attachParserField () and attachParserFieldToCorkEntry () duplicates + * the memory object specified with `value' and stores the duplicated + * object to the entry on the cork queue. So the parser must/can free + * the original one passed to the functions after calling. The cork + * queue manages the life of the duplicated object. It is not the + * parser's concern. + * + * + * Case B: + * + * If your parser called one of initTagEntry () family but didn't call + * makeTagEntry () for a tagEntry yet, use attachParserField () with + * false for `inCorkQueue' whether your parser uses the Cork API or + * not. + * + * The parser (== caller) keeps the memory object specified with `value' + * till calling makeTagEntry (). The parser must free the memory object + * after calling makeTagEntry () if it is allocated dynamically in the + * parser side. + * + * Interpretation of VALUE + * ----------------------- + * For FIELDTYPE_STRING: + * Both json writer and xref writer prints it as-is. + * + * For FIELDTYPE_STRING|FIELDTYPE_BOOL: + * If VALUE points "" (empty C string), the json writer prints it as + * false, and the xref writer prints it as -. + * If VALUE points a non-empty C string, Both json writer and xref + * writer print it as-is. + * + * For FIELDTYPE_BOOL + * The json writer always prints true. + * The xref writer always prints the name of field. + * Set "" explicitly though the value pointed by VALUE is not referred, + * + * + * The other data type and the combination of types are not implemented yet. + * + */ +extern void attachParserField (tagEntryInfo *const tag, bool inCorkQueue, fieldType ftype, const char* value); extern void attachParserFieldToCorkEntry (int index, fieldType ftype, const char* value); +extern const char* getParserFieldValueForType (tagEntryInfo *const tag, fieldType ftype); -#ifdef CTAGS_LIB -extern void setTagEntryFunction(tagEntryFunction entry_function, void *user_data); -#endif +extern int makePlaceholder (const char *const name); + +/* Marking all tag entries entries under the scope specified + * with index recursively. + * + * The parser calling this function enables CORK_SYMTAB. + * Entries to be marked must be registered to the scope + * specified with index or its descendant scopes with + * registerEntry (). + * + * Call makePlaceholder () at the start of your parser for + * making the root scope where the entries are registered. + */ +extern void markAllEntriesInScopeAsPlaceholder (int index); #endif /* CTAGS_MAIN_ENTRY_H */ diff --git a/ctags/main/entry_p.h b/ctags/main/entry_p.h new file mode 100644 index 000000000..6172eaf52 --- /dev/null +++ b/ctags/main/entry_p.h @@ -0,0 +1,77 @@ +/* +* 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 *writerClientData); +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(unsigned int corkFlags); +void uncorkTagFile(void); + +extern void makeFileTag (const char *const fileName); + +extern const tagField* getParserFieldForIndex (const tagEntryInfo * tag, int index); + + +CTAGS_INLINE roleBitsType makeRoleBit(int roleIndex) +{ + if (roleIndex == ROLE_DEFINITION_INDEX) + return 0; + else + return ((roleBitsType)1) << roleIndex; +} + +#endif /* CTAGS_PRIVATE_ENTRY_H */ diff --git a/ctags/main/entry_private.c b/ctags/main/entry_private.c new file mode 100644 index 000000000..6f74898a8 --- /dev/null +++ b/ctags/main/entry_private.c @@ -0,0 +1,38 @@ +/* +* 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 "general.h" +#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_DEFINITION_INDEX) + return NULL; + return getLanguageRole(tag->langType, tag->kindIndex, roleIndex); +} diff --git a/ctags/main/error.c b/ctags/main/error.c index d08bd12ef..bc5493dd3 100644 --- a/ctags/main/error.c +++ b/ctags/main/error.c @@ -11,8 +11,13 @@ #include #include -#include "error.h" -#include "options.h" +#include "error_p.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/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 bb26ad65d..22a97d01a 100644 --- a/ctags/main/field.c +++ b/ctags/main/field.c @@ -19,27 +19,38 @@ #include "ctags.h" #include "debug.h" #include "entry.h" +#include "entry_p.h" #include "field.h" +#include "field_p.h" #include "kind.h" -#include "options.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" +#define FIELD_NULL_LETTER_CHAR '-' +#define FIELD_NULL_LETTER_STRING "-" -struct sFieldDesc { - fieldDefinition *spec; - unsigned int fixed: 1; /* fields which cannot be disabled. */ +typedef struct sFieldObject { + fieldDefinition *def; vString *buffer; const char* nameWithPrefix; langType language; fieldType sibling; -}; +} fieldObject; 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); @@ -50,13 +61,18 @@ static const char *renderFieldKindLetter (const tagEntryInfo *const tag, const c 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 *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 *renderFieldExtra (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 doesContainAnyCharInName (const tagEntryInfo *const tag, const char *value, const char *chars); +static bool doesContainAnyCharInInput (const tagEntryInfo *const tag, const char*value, const char *chars); +static bool doesContainAnyCharInFieldScope (const tagEntryInfo *const tag, const char *value, const char *chars); +static bool doesContainAnyCharInSignature (const tagEntryInfo *const tag, const char *value, const char *chars); + static bool isLanguageFieldAvailable (const tagEntryInfo *const tag); static bool isTyperefFieldAvailable (const tagEntryInfo *const tag); static bool isFileFieldAvailable (const tagEntryInfo *const tag); @@ -64,195 +80,238 @@ 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, 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, DCAC) \ { \ .letter = L, \ .name = N, \ .description = H, \ .enabled = V, \ - .renderEscaped = F, \ + .render = RE, \ + .renderNoEscaping= RN, \ + .doesContainAnyChar = DCAC, \ .isValueAvailable = A, \ + .dataType = DT, \ } -#define WITH_DEFUALT_VALUE(str) ((str)?(str):"-") +#define WITH_DEFUALT_VALUE(str) ((str)?(str):FIELD_NULL_LETTER_STRING) 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)", + DEFINE_FIELD_FULL ('N', "name", true, + "tag name", + NULL, + FIELDTYPE_STRING, + renderFieldName, renderFieldNameNoEscape, + doesContainAnyCharInName), + DEFINE_FIELD_FULL ('F', "input", true, + "input file", + NULL, + FIELDTYPE_STRING, + renderFieldInput, renderFieldInputNoEscape, + doesContainAnyCharInInput), + DEFINE_FIELD ('P', "pattern", true, + "pattern", + FIELDTYPE_STRING|FIELDTYPE_BOOL, renderFieldPattern), }; static fieldDefinition fieldDefinitionsExuberant [] = { - DEFINE_FIELD_SPEC ('C', "compact", false, - "compact input line (fixed field, only used in -x option)", + DEFINE_FIELD ('C', "compact", false, + "compact input line (used only in xref output)", + FIELDTYPE_STRING, 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, + renderFieldAccess, NULL, NULL), + DEFINE_FIELD_FULL ('f', "file", true, "File-restricted scoping", - renderFieldFile, isFileFieldAvailable), - DEFINE_FIELD_SPEC_FULL ('i', "inherits", false, + isFileFieldAvailable, + FIELDTYPE_BOOL, + renderFieldFile, NULL, NULL), + DEFINE_FIELD_FULL ('i', "inherits", false, "Inheritance information", - renderFieldInherits, isInheritsFieldAvailable), - DEFINE_FIELD_SPEC ('K', NULL, false, - "Kind of tag as full name", + isInheritsFieldAvailable, + FIELDTYPE_STRING|FIELDTYPE_BOOL, + renderFieldInherits, NULL, NULL), + DEFINE_FIELD ('K', NULL, false, + "Kind of tag in long-name form", + FIELDTYPE_STRING, renderFieldKindName), - DEFINE_FIELD_SPEC ('k', NULL, true, - "Kind of tag as a single letter", + DEFINE_FIELD ('k', NULL, true, + "Kind of tag in one-letter form", + FIELDTYPE_STRING, renderFieldKindLetter), - DEFINE_FIELD_SPEC_FULL ('l', "language", false, + DEFINE_FIELD_FULL ('l', "language", false, "Language of input file containing tag", - renderFieldLanguage, isLanguageFieldAvailable), - DEFINE_FIELD_SPEC_FULL ('m', "implementation", false, + isLanguageFieldAvailable, + FIELDTYPE_STRING, + renderFieldLanguage, NULL, NULL), + DEFINE_FIELD_FULL ('m', "implementation", false, "Implementation information", - renderFieldImplementation, isImplementationFieldAvailable), - DEFINE_FIELD_SPEC ('n', "line", false, + isImplementationFieldAvailable, + FIELDTYPE_STRING, + renderFieldImplementation, NULL, NULL), + DEFINE_FIELD ('n', "line", false, "Line number of tag definition", + FIELDTYPE_INTEGER, renderFieldLineNumber), - DEFINE_FIELD_SPEC_FULL ('S', "signature", false, + DEFINE_FIELD_FULL ('S', "signature", false, "Signature of routine (e.g. prototype or parameter list)", - renderFieldSignature, isSignatureFieldAvailable), - DEFINE_FIELD_SPEC ('s', NULL, true, - "Scope of tag definition (`p' can be used for printing its kind)", - renderFieldScope), - DEFINE_FIELD_SPEC_FULL ('t', "typeref", true, + isSignatureFieldAvailable, + FIELDTYPE_STRING, + renderFieldSignature, renderFieldSignatureNoEscape, + doesContainAnyCharInSignature), + DEFINE_FIELD_FULL ('s', NULL, true, + "[tags output] scope (kind:name) of tag definition, [xref and json output] name of scope", + NULL, + FIELDTYPE_STRING, + renderFieldScope, renderFieldScopeNoEscape, + doesContainAnyCharInFieldScope), + DEFINE_FIELD_FULL ('t', "typeref", true, "Type and name of a variable or typedef", - renderFieldTyperef, isTyperefFieldAvailable), - DEFINE_FIELD_SPEC ('z', "kind", false, - "Include the \"kind:\" key in kind field (use k or K) in tags output, kind full name in xref output", + isTyperefFieldAvailable, + FIELDTYPE_STRING, + renderFieldTyperef, NULL, NULL), + DEFINE_FIELD ('z', "kind", false, + "[tags output] prepend \"kind:\" to k/ (or K/) field output, [xref and json output] kind in long-name form", + FIELDTYPE_STRING, /* Following renderer is for handling --_xformat=%{kind}; and is not for tags output. */ 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, + renderFieldRoles), + DEFINE_FIELD ('R', NULL, false, "Marker (R or D) representing whether tag is definition or reference", + FIELDTYPE_STRING, renderFieldRefMarker), - DEFINE_FIELD_SPEC ('Z', "scope", false, - "Include the \"scope:\" key in scope field (use s) in tags output, scope name in xref output", + DEFINE_FIELD_FULL ('Z', "scope", false, + "[tags output] prepend \"scope:\" key to s/scope field output, [xref and json output] the same as s/ field", + NULL, + FIELDTYPE_STRING, /* Following renderer is for handling --_xformat=%{scope}; and is not for tags output. */ - renderFieldScope), - DEFINE_FIELD_SPEC_FULL ('E', "extra", false, + renderFieldScope, renderFieldScopeNoEscape, + doesContainAnyCharInFieldScope), + DEFINE_FIELD_FULL ('E', "extras", false, "Extra tag type information", - renderFieldExtra, isExtraFieldAvailable), - DEFINE_FIELD_SPEC_FULL ('x', "xpath", false, + isExtrasFieldAvailable, + FIELDTYPE_STRING, + renderFieldExtras, NULL, NULL), + DEFINE_FIELD_FULL ('x', "xpath", false, "xpath for the tag", - renderFieldXpath, isXpathFieldAvailable), - DEFINE_FIELD_SPEC ('p', "scopeKind", false, - "Kind of scope as full name", + isXpathFieldAvailable, + FIELDTYPE_STRING, + renderFieldXpath, NULL, NULL), + DEFINE_FIELD ('p', "scopeKind", false, + "[tags output] no effect, [xref and json output] kind of scope in long-name form", + FIELDTYPE_STRING, renderFieldScopeKindName), - DEFINE_FIELD_SPEC_FULL ('e', "end", false, + DEFINE_FIELD_FULL ('e', "end", false, "end lines of various items", - renderFieldEnd, isEndFieldAvailable), + isEndFieldAvailable, + FIELDTYPE_INTEGER, + renderFieldEnd, NULL, NULL), }; -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->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->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->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,102 +338,52 @@ 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; } return FIELD_UNKNOWN; } -extern const char* getFieldName(fieldType type) +extern const char* getFieldDescription (fieldType type) { - fieldDesc* fdesc; + fieldObject* fobj; - fdesc = getFieldDesc (type); - if (Option.putFieldPrefix) - return fdesc->nameWithPrefix; - else - return fdesc->spec->name; + fobj = getFieldObject (type); + return fobj->def->description; } -extern bool doesFieldHaveValue (fieldType type, const tagEntryInfo *tag) +extern const char* getFieldName(fieldType type) { - if (getFieldDesc(type)->spec->isValueAvailable) - return getFieldDesc(type)->spec->isValueAvailable(tag); - else - return true; -} + fieldObject* fobj; -#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"; + fobj = getFieldObject (type); + if (Option.putFieldPrefix) + return fobj->nameWithPrefix; 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"); + return fobj->def->name; } -extern void printFields (int language) +extern unsigned char getFieldLetter (fieldType type) { - unsigned int i; + fieldObject* fobj = getFieldObject (type); - 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"); + return fobj->def->letter == '\0' + ? FIELD_NULL_LETTER_CHAR + : fobj->def->letter; +} - for (i = 0; i < fieldDescUsed; i++) - { - if (language == LANG_AUTO || getFieldOwner (i) == language) - printField (i); - } +extern bool doesFieldHaveValue (fieldType type, const tagEntryInfo *tag) +{ + if (getFieldObject(type)->def->isValueAvailable) + return getFieldObject(type)->def->isValueAvailable(tag); + else + return true; } static const char *renderAsIs (vString* b CTAGS_ATTR_UNUSED, const char *s) @@ -390,41 +399,61 @@ 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 && (!tag->isPseudoTag) && (*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) { - return renderEscapedName (tag->name, tag, b); + return renderEscapedName (true, tag->name, tag, b); +} + +static const char *renderFieldNameNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) +{ + return renderAsIs (b, tag->name); +} + +static bool doesContainAnyCharInName (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, const char *chars) +{ + return strpbrk (tag->name, chars)? true: false; } static const char *renderFieldInput (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) @@ -436,20 +465,69 @@ 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) +{ + const char *f = tag->inputFileName; + + if (Option.lineDirectives && tag->sourceFileName) + f = tag->sourceFileName; + + return renderAsIs (b, f); +} + +static bool doesContainAnyCharInInput (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, const char *chars) +{ + const char *f = tag->inputFileName; + + if (Option.lineDirectives && tag->sourceFileName) + f = tag->sourceFileName; + + return strpbrk (f, chars)? 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 *renderFieldSignatureNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) +{ + return renderAsIs (b, WITH_DEFUALT_VALUE (tag->extensionFields.signature)); +} + +static bool doesContainAnyCharInSignature (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, const char *chars) +{ + return (tag->extensionFields.signature && strpbrk(tag->extensionFields.signature, chars)) + ? true + : false; +} + static const char *renderFieldScope (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { const char* scope; getTagScopeInformation ((tagEntryInfo *const)tag, NULL, &scope); - return scope? renderEscapedName (scope, tag, b): NULL; + return scope? renderEscapedName (false, scope, tag, b): NULL; +} + +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); + return scope? renderAsIs (b, scope): NULL; +} + +static bool doesContainAnyCharInFieldScope (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, const char *chars) +{ + const char* scope; + + getTagScopeInformation ((tagEntryInfo *const)tag, NULL, &scope); + return (scope && strpbrk (scope, chars)); } + static const char *renderFieldInherits (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { return renderEscapedString (WITH_DEFUALT_VALUE (tag->extensionFields.inheritance), @@ -458,31 +536,90 @@ static const char *renderFieldInherits (const tagEntryInfo *const tag, const cha static const char *renderFieldTyperef (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { - 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, FIELD_NULL_LETTER_STRING); + + vStringCatS (b, WITH_DEFUALT_VALUE (tag->extensionFields.typeRef [0])); + vStringPut (b, ':'); + return renderEscapedName (false, WITH_DEFUALT_VALUE (tag->extensionFields.typeRef [1]), tag, b); +} + + +static const char* renderFieldCommon (fieldType type, + const tagEntryInfo *tag, + int index, + bool noEscaping) +{ + fieldObject *fobj = fieldObjects + type; + const char *value; + fieldRenderer rfn; + + Assert (tag); + Assert (index < 0 || ((unsigned int)index) < tag->usedParserFields); + + if (index >= 0) + { + const tagField *f = getParserFieldForIndex (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 defaultDoesContainAnyChar (const tagEntryInfo *const tag CTAGS_ATTR_UNUSED, const char* value, const char* chars) +{ + return strpbrk (value, chars)? true: false; +} -extern const char* renderFieldEscaped (fieldType type, - const tagEntryInfo *tag, - int index) +extern bool doesFieldHaveTabOrNewlineChar (fieldType type, const tagEntryInfo *tag, int index) { - fieldDesc *fdesc = fieldDescs + type; + fieldObject *fobj = fieldObjects + type; const char *value; + bool (* doesContainAnyChar) (const tagEntryInfo *const, const char*, const char*) = fobj->def->doesContainAnyChar; Assert (tag); - Assert (fdesc->spec->renderEscaped); + Assert (index == NO_PARSER_FIELD || ((unsigned int)index) < tag->usedParserFields); - fdesc->buffer = vStringNewOrClear (fdesc->buffer); + if (doesContainAnyChar == NULL) + { + if (index == NO_PARSER_FIELD) + return false; + else + doesContainAnyChar = defaultDoesContainAnyChar; + } if (index >= 0) { - Assert ( tag->usedParserFields > index ); - value = tag->parserFields[ index ].value; + const tagField *f = getParserFieldForIndex (tag, index); + + value = f->value; } else value = NULL; - return fdesc->spec->renderEscaped (tag, value, fdesc->buffer); + return (* doesContainAnyChar) (tag, value, "\t\n"); } /* Writes "line", stripping leading and duplicate white space. @@ -519,8 +656,8 @@ static const char* renderCompactInputLine (vString *b, const char *const line) static const char *renderFieldKindName (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { - 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, @@ -530,9 +667,15 @@ static const char *renderFieldCompactInputLine (const tagEntryInfo *const tag, const char *line; static vString *tmp; - tmp = vStringNewOrClear (tmp); + if (tag->isPseudoTag) + { + Assert (tag->pattern); + return tag->pattern; + } + + tmp = vStringNewOrClearWithAutoRelease (tmp); - line = readLineFromBypassAnyway (tmp, tag, NULL); + line = readLineFromBypassForTag (tmp, tag, NULL); if (line) renderCompactInputLine (b, line); else @@ -561,19 +704,33 @@ 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) { - 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_DEFINITION_NAME); return vStringValue (b); } @@ -592,7 +749,7 @@ static const char *renderFieldLanguage (const tagEntryInfo *const tag, } static const char *renderFieldAccess (const tagEntryInfo *const tag, - const char *value, + const char *value CTAGS_ATTR_UNUSED, vString* b) { return renderAsIs (b, WITH_DEFUALT_VALUE (tag->extensionFields.access)); @@ -602,7 +759,11 @@ static const char *renderFieldKindLetter (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { - return "TODO"; + static char c[2] = { [1] = '\0' }; + + c [0] = getTagKindLetter(tag); + + return renderAsIs (b, c); } static const char *renderFieldImplementation (const tagEntryInfo *const tag, @@ -616,16 +777,25 @@ static const char *renderFieldFile (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { - return renderAsIs (b, tag->isFileScope? "file": "-"); + return renderAsIs (b, tag->isFileScope? "file": FIELD_NULL_LETTER_STRING); } static const char *renderFieldPattern (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { - 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); } @@ -635,19 +805,20 @@ static const char *renderFieldRefMarker (const tagEntryInfo *const tag, { 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) { 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,7 +842,7 @@ static const char *renderFieldExtra (const tagEntryInfo *const tag, } static const char *renderFieldXpath (const tagEntryInfo *const tag, - const char *value, + const char *value CTAGS_ATTR_UNUSED, vString* b) { #ifdef HAVE_LIBXML @@ -683,7 +854,7 @@ static const char *renderFieldXpath (const tagEntryInfo *const tag, } static const char *renderFieldScopeKindName(const tagEntryInfo *const tag, - const char *value, + const char *value CTAGS_ATTR_UNUSED, vString* b) { const char* kind; @@ -693,14 +864,14 @@ static const char *renderFieldScopeKindName(const tagEntryInfo *const tag, } static const char *renderFieldEnd (const tagEntryInfo *const tag, - const char *value, + const char *value CTAGS_ATTR_UNUSED, vString* b) { - static char buf[16]; + static char buf[21]; if (tag->extensionFields.endLine != 0) { - sprintf (buf, "%ld", tag->extensionFields.endLine); + sprintf (buf, "%lu", tag->extensionFields.endLine); return renderAsIs (b, buf); } else @@ -743,18 +914,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 +944,43 @@ static bool isEndFieldAvailable (const tagEntryInfo *const tag) extern bool isFieldEnabled (fieldType type) { - return getFieldDesc(type)->spec->enabled; -} - -static bool isFieldFixed (fieldType type) -{ - return getFieldDesc(type)->fixed? true: false; + return getFieldObject(type)->def->enabled; } extern bool enableField (fieldType type, bool state, bool warnIfFixedField) { - fieldDefinition *spec = getFieldDesc(type)->spec; - bool old = spec->enabled? true: false; - if (isFieldFixed (type)) + fieldDefinition *def = getFieldObject(type)->def; + bool old = def->enabled; + if (writerDoesTreatFieldAsFixed (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 +992,246 @@ 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) +extern bool doesFieldHaveRenderer (fieldType type, bool noEscaping) { - return getFieldDesc(type)->spec->renderEscaped? true: false; + if (noEscaping) + return getFieldObject(type)->def->renderNoEscaping? true: false; + else + return getFieldObject(type)->def->render? 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) { - return value; + return renderEscapedString (value, tag, buffer); } -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 ( isalpha (def->name [i]) ); + } + def->letter = NUL_FIELD_LETTER; + + if (fieldObjectUsed == fieldObjectAllocated) { - Assert ( isalnum (spec->name [i]) ); + fieldObjectAllocated *= 2; + fieldObjects = xRealloc (fieldObjects, fieldObjectAllocated, fieldObject); } - spec->letter = NUL_FIELD_LETTER; + fobj = fieldObjects + (fieldObjectUsed); + def->ftype = fieldObjectUsed++; - if (fieldDescUsed == fieldDescAllocated) + if (def->render == NULL) { - fieldDescAllocated *= 2; - fieldDescs = xRealloc (fieldDescs, fieldDescAllocated, fieldDesc); + def->render = defaultRenderer; + def->renderNoEscaping = NULL; + def->doesContainAnyChar = NULL; } - fdesc = fieldDescs + (fieldDescUsed); - spec->ftype = fieldDescUsed++; - if (spec->renderEscaped == NULL) - spec->renderEscaped = defaultRenderer; + if (! def->dataType) + def->dataType = FIELDTYPE_STRING; - fdesc->spec = spec; + fobj->def = def; - fdesc->fixed = 0; - fdesc->buffer = NULL; + fobj->buffer = NULL; - nameWithPrefix = eMalloc (sizeof CTAGS_FIELD_PREFIX + strlen (spec->name) + 1); + 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) + ? FIELD_NULL_LETTER_CHAR + : 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, writerDoesTreatFieldAsFixed (i)); + colprintLineAppendColumnCString (line, fdef->description); +} + +extern void fieldColprintAddCommonLines (struct colprintTable *table) +{ + for (int i = 0; i <= FIELD_BUILTIN_LAST; i++) + fieldColprintAddLine(table, i); +} - fdesc->language = language; - fdesc->sibling = FIELD_UNKNOWN; +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); - updateSiblingField (spec->ftype, spec->name); - return spec->ftype; + 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; + + const char *a_name = colprintLineGetColumn (a, FIELD_COL_NAME); + const char *b_name = colprintLineGetColumn (b, FIELD_COL_NAME); + + 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 741f25880..4213ab623 100644 --- a/ctags/main/field.h +++ b/ctags/main/field.h @@ -12,11 +12,19 @@ #ifndef CTAGS_MAIN_FIELD_H #define CTAGS_MAIN_FIELD_H +/* +* INCLUDE FILES +*/ + #include "general.h" #include "types.h" #include "vstring.h" +/* +* DATA DECLARATIONS +*/ + typedef enum eFieldType { /* extension field content control */ FIELD_UNKNOWN = -1, @@ -42,70 +50,56 @@ 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); -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; + +typedef const char* (*fieldRenderer)(const tagEntryInfo *const, + const char *, + vString *); #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; - isValueAvailable isValueAvailable; - unsigned int ftype; /* Given from the main part */ -} fieldDefinition; + fieldRenderer render; + fieldRenderer renderNoEscaping; + bool (* doesContainAnyChar) (const tagEntryInfo *const, const char*, const char *); + bool (* isValueAvailable) (const tagEntryInfo *const); -extern fieldType getFieldTypeForOption (char letter); + fieldDataType dataType; /* used in json output */ -/* - `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 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 void printFields (int language); - -extern bool isFieldRenderable (fieldType type); + unsigned int ftype; /* Given from the main part */ +}; -extern bool doesFieldHaveValue (fieldType type, const tagEntryInfo *tag); -extern const char* renderFieldEscaped (fieldType type, const tagEntryInfo *tag, int index); -extern void initFieldDescs (void); -extern int countFields (void); +/* +* FUNCTION PROTOTYPES +*/ -/* 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); +extern bool isFieldEnabled (fieldType type); #endif /* CTAGS_MAIN_FIELD_H */ diff --git a/ctags/main/field.h b/ctags/main/field_p.h similarity index 51% copy from ctags/main/field.h copy to ctags/main/field_p.h index 741f25880..204068370 100644 --- a/ctags/main/field.h +++ b/ctags/main/field_p.h @@ -9,69 +9,24 @@ * GNU General Public License version 2 or (at your option) any later version. * */ -#ifndef CTAGS_MAIN_FIELD_H -#define CTAGS_MAIN_FIELD_H +#ifndef CTAGS_MAIN_FIELD_PRIVATE_H +#define CTAGS_MAIN_FIELD_PRIVATE_H +/* +* INCLUDE FILES +*/ #include "general.h" -#include "types.h" - -#include "vstring.h" - -typedef enum eFieldType { /* extension field content control */ - FIELD_UNKNOWN = -1, - - /* BASIC FIELDS */ - FIELD_NAME, - FIELD_INPUT_FILE, - FIELD_PATTERN, - FIELD_COMPACT_INPUT_LINE, - - /* EXTENSION FIELDS */ - FIELD_EXTENSION_START, - FIELD_ACCESS = FIELD_EXTENSION_START, - FIELD_FILE_SCOPE, - FIELD_INHERITANCE, - FIELD_KIND_LONG, - FIELD_KIND, - FIELD_LANGUAGE, - FIELD_IMPLEMENTATION, - FIELD_LINE_NUMBER, - FIELD_SIGNATURE, - FIELD_SCOPE, - FIELD_TYPE_REF, - FIELD_KIND_KEY, - - /* EXTENSION FIELDS NEWLY INTRODUCED IN UCTAGS */ - FIELD_ROLE, - FIELD_REF_MARK, - FIELD_SCOPE_KEY, - FIELD_EXTRA, - FIELD_XPATH, - FIELD_SCOPE_KIND_LONG, - FIELD_END, - FIELD_BUILTIN_LAST = FIELD_END, -} fieldType ; +#include "colprint_p.h" +#include "field.h" -typedef const char* (* renderEscaped) (const tagEntryInfo *const tag, - const char *value, - vString * buffer); -typedef bool (* isValueAvailable) (const struct sTagEntryInfo *const tag); - -#define FIELD_LETTER_NO_USE '\0' -typedef struct sFieldDefinition { - /* lettern, 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; - isValueAvailable isValueAvailable; +/* +* DATA DECLARATIONS +*/ - unsigned int ftype; /* Given from the main part */ -} fieldDefinition; +/* +* FUNCTION PROTOTYPES +*/ extern fieldType getFieldTypeForOption (char letter); @@ -88,19 +43,26 @@ extern fieldType getFieldTypeForOption (char letter); 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 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* getFieldDescription (fieldType type); extern const char* getFieldName (fieldType type); +extern unsigned char getFieldLetter (fieldType type); +extern unsigned int getFieldDataType (fieldType type); extern void printFields (int language); -extern bool isFieldRenderable (fieldType type); +/* 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* renderFieldEscaped (fieldType type, const tagEntryInfo *tag, int index); -extern void initFieldDescs (void); +extern const char* renderField (fieldType type, const tagEntryInfo *tag, int index); +extern const char* renderFieldNoEscaping (fieldType type, const tagEntryInfo *tag, int index); +extern bool doesFieldHaveTabOrNewlineChar (fieldType type, const tagEntryInfo *tag, int index); + +extern void initFieldObjects (void); extern int countFields (void); /* language should be typed to langType. @@ -108,4 +70,11 @@ extern int countFields (void); extern int defineField (fieldDefinition *spec, langType language); extern fieldType nextSiblingField (fieldType type); -#endif /* CTAGS_MAIN_FIELD_H */ +/* --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/flags.c b/ctags/main/flags.c index ae9f83095..b5931eaa7 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 af4363682..786bf9a61 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 d15993ba1..d7c99289d 100644 --- a/ctags/main/fmt.c +++ b/ctags/main/fmt.c @@ -12,9 +12,10 @@ #include "general.h" #include "debug.h" -#include "fmt.h" +#include "entry_p.h" +#include "fmt_p.h" #include "field.h" -#include "options.h" +#include "field_p.h" #include "parse.h" #include "routines.h" #include @@ -25,6 +26,7 @@ typedef union uFmtSpec { struct { fieldType ftype; int width; + char *raw_fmtstr; } field; } fmtSpec; @@ -59,31 +61,49 @@ static int printTagField (fmtSpec* fspec, MIO* fp, const tagEntryInfo * tag) ftype = fspec->field.ftype; if (isCommonField (ftype)) - str = renderFieldEscaped (ftype, tag, NO_PARSER_FIELD); + str = renderField (ftype, tag, NO_PARSER_FIELD); else { unsigned int findex; + const tagField *f; for (findex = 0; findex < tag->usedParserFields; findex++) { - if (isParserFieldCompatibleWithFtype (tag->parserFields + findex, ftype)) + f = getParserFieldForIndex(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)) + { + unsigned int dt = getFieldDataType (f->ftype); + if (dt & FIELDTYPE_STRING) + { + str = renderField (f->ftype, tag, findex); + if ((dt & FIELDTYPE_BOOL) && str[0] == '\0') + { + /* TODO: FIELD_NULL_LETTER_STRING */ + str = "-"; + } + } + else if (dt & FIELDTYPE_BOOL) + str = getFieldName (f->ftype); + else + { + /* Not implemented */ + AssertNotReached (); + str = "CTAGS INTERNAL BUG!"; + } + } } 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 +179,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; @@ -189,7 +209,7 @@ static fmtElement** queueTagField (fmtElement **last, long width, char field_let 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); @@ -200,6 +220,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 +271,7 @@ extern fmtElement *fmtNew (const char* fmtString) else { int justification_right = 1; + bool truncation = false; vString *width = NULL; if (literal) { @@ -257,6 +288,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 +315,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 +329,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/gcc-attr.h b/ctags/main/gcc-attr.h index 4c6caf92d..45b1de585 100644 --- a/ctags/main/gcc-attr.h +++ b/ctags/main/gcc-attr.h @@ -14,16 +14,14 @@ /* Prevent warnings about unused variables in GCC. */ #if defined (__GNUC__) && !defined (__GNUG__) -# ifdef __MINGW32__ -# define CTAGS_ATTR_UNUSED -# else -# define CTAGS_ATTR_UNUSED __attribute__((unused)) -# endif +# define CTAGS_ATTR_UNUSED __attribute__((unused)) # define CTAGS_ATTR_PRINTF(s,f) __attribute__((format (printf, s, f))) +# define CTAGA_ATTR_ALIGNED(X) __attribute__((aligned(X))) # define attr__noreturn __attribute__((__noreturn__)) #else # define CTAGS_ATTR_UNUSED # define CTAGS_ATTR_PRINTF(s,f) +# define CTAGA_ATTR_ALIGNED(X) # define attr__noreturn #endif diff --git a/ctags/main/general.h b/ctags/main/general.h index c938b02bf..1b2acf747 100644 --- a/ctags/main/general.h +++ b/ctags/main/general.h @@ -14,13 +14,13 @@ */ #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 +/* To provide timings features. + */ +#include /* * MACROS @@ -50,24 +50,18 @@ * DATA DECLARATIONS */ -#ifdef USE_STDBOOL_H +#ifdef HAVE_STDBOOL_H # include #endif /* -* FUNCTION PROTOTYPES +* HACK for #1610. */ -#if defined (NEED_PROTO_REMOVE) && defined (HAVE_REMOVE) -extern int remove (const char *); -#endif - -#if defined (NEED_PROTO_UNLINK) && ! defined (HAVE_REMOVE) -extern void *unlink (const char *); -#endif - -#ifdef NEED_PROTO_GETENV -extern char *getenv (const char *); +#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 000000000..5d1ee9175 --- /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/htable.c b/ctags/main/htable.c index 2556110f8..c13daa982 100644 --- a/ctags/main/htable.c +++ b/ctags/main/htable.c @@ -47,6 +47,12 @@ struct sHashTable { hashTableFreeFunc valfreefn; }; +struct chainTracker { + const void *const target_key; + hashTableForeachFunc user_proc; + void *user_data; + hashTableEqualFunc equalfn; +}; static hentry* entry_new (void *key, void *value, hentry* next) { @@ -96,7 +102,7 @@ static void *entry_find (hentry* entry, const void* const key, hashTableEqualFun return NULL; } -static bool entry_delete (hentry **entry, void *key, hashTableEqualFunc equalfn, +static bool entry_delete (hentry **entry, const void *key, hashTableEqualFunc equalfn, hashTableFreeFunc keyfreefn, hashTableFreeFunc valfreefn) { while (*entry) @@ -111,13 +117,15 @@ static bool entry_delete (hentry **entry, void *key, hashTableEqualFunc equalfn return false; } -static void entry_foreach (hentry *entry, hashTableForeachFunc proc, void *user_data) +static bool entry_foreach (hentry *entry, hashTableForeachFunc proc, void *user_data) { while (entry) { - proc (entry->key, entry->value, user_data); + if (!proc (entry->key, entry->value, user_data)) + return false; entry = entry->next; } + return true; } extern hashTable *hashTableNew (unsigned int size, @@ -140,6 +148,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) @@ -183,7 +199,7 @@ extern void* hashTableGetItem (hashTable *htable, const void * key) return entry_find(htable->table[i], key, htable->equalfn); } -extern bool hashTableDeleteItem (hashTable *htable, void *key) +extern bool hashTableDeleteItem (hashTable *htable, const void *key) { unsigned int i; @@ -197,18 +213,49 @@ extern bool hashTableHasItem (hashTable *htable, const void *key) return hashTableGetItem (htable, key)? true: false; } -extern void hashTableForeachItem (hashTable *htable, hashTableForeachFunc proc, void *user_data) +extern bool hashTableForeachItem (hashTable *htable, hashTableForeachFunc proc, void *user_data) { unsigned int i; for (i = 0; i < htable->size; i++) - entry_foreach(htable->table[i], proc, user_data); + if (!entry_foreach(htable->table[i], proc, user_data)) + return false; + return true; +} + +static bool track_chain (const void *const key, void *value, void *chain_data) +{ + struct chainTracker *chain_tracker = chain_data; + + if (chain_tracker->equalfn (chain_tracker->target_key, key)) + { + if (! chain_tracker->user_proc (key, value, chain_tracker->user_data)) + return false; + } + return true; +} + +extern bool hashTableForeachItemOnChain (hashTable *htable, const void *key, hashTableForeachFunc proc, void *user_data) +{ + unsigned int i; + struct chainTracker chain_tracker = { + .target_key = key, + .user_proc = proc, + .user_data = user_data, + .equalfn = htable->equalfn, + }; + + i = htable->hashfn (key) % htable->size; + if (!entry_foreach(htable->table[i], track_chain, &chain_tracker)) + return false; + return true; } -static void count (void *key CTAGS_ATTR_UNUSED, void *value CTAGS_ATTR_UNUSED, void *data) +static bool count (const void *const key CTAGS_ATTR_UNUSED, void *value CTAGS_ATTR_UNUSED, void *data) { int *c = data; ++*c; + return true; } extern int hashTableCountItem (hashTable *htable) diff --git a/ctags/main/htable.h b/ctags/main/htable.h index 03cff3618..19cfd7cf0 100644 --- a/ctags/main/htable.h +++ b/ctags/main/htable.h @@ -12,12 +12,29 @@ #define CTAGS_MAIN_HTABLE_H #include "general.h" +#include +/* This hashtable allows adding multiple items for the same key. + * + * hashTablePutItem() adds a key/item pair to the htable even if the + * htable has an item for the key already. + * + * hashTableGetItem() returns the first occurrence item for the given + * key. + * + * hashTableDeleteItem() deletes the first occurrence item for the given + * key. + * + * Use hashTableForeachItemOnChain () to process all items for the same key. + */ typedef struct sHashTable hashTable; typedef unsigned int (* hashTableHashFunc) (const void * const key); typedef bool (* hashTableEqualFunc) (const void* a, const void* b); typedef void (* hashTableFreeFunc) (void * ptr); -typedef void (* hashTableForeachFunc) (void *key, void *value, void* user_data); + +/* To continue interation, return true. + * To break interation, return false. */ +typedef bool (* hashTableForeachFunc) (const void *key, void *value, void *user_data); unsigned int hashPtrhash (const void * x); bool hashPtreq (const void * a, const void * constb); @@ -36,13 +53,36 @@ 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); extern void* hashTableGetItem (hashTable *htable, const void * key); extern bool hashTableHasItem (hashTable * htable, const void * key); -extern bool hashTableDeleteItem (hashTable *htable, void *key); -extern void hashTableForeachItem (hashTable *htable, hashTableForeachFunc proc, void *user_data); +extern bool hashTableDeleteItem (hashTable *htable, const void *key); + +/* Return true if proc never returns false; proc returns true for all + * the items, or htable holds no item. + * + * Return false if htable holds at least one item and proc returns false + * for one of the items. */ +extern bool hashTableForeachItem (hashTable *htable, hashTableForeachFunc proc, void *user_data); + +/* This function is useful for htable having multiple items for a key. + * By giving a key, you can traverse all the items associated with the + * key via proc. * "Chain" means group of items associated with a + * key. */ +extern bool hashTableForeachItemOnChain (hashTable *htable, const void *key, 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)) +#define HT_PTR_TO_UINT(P) ((unsigned int)(uintptr_t)(P)) +#define HT_UINT_TO_PTR(P) ((void*)(uintptr_t)(P)) + #endif /* CTAGS_MAIN_HTABLE_H */ diff --git a/ctags/main/interactive_p.h b/ctags/main/interactive_p.h new file mode 100644 index 000000000..6dbd5be24 --- /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/keyword.c b/ctags/main/keyword.c index 374c311e5..3f55ec28b 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,36 @@ 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; + } + } +} + +extern void addKeywordGroup (const struct keywordGroup *const groupdef, + langType language) +{ + for (int i = 0; groupdef->keywords[i]; i++) + { + if (groupdef->addingUnlessExisting) + { + if (lookupKeyword (groupdef->keywords[i], + language) != KEYWORD_NONE) + continue; /* already added */ + } + else + Assert (lookupKeyword (groupdef->keywords[i], + language) == KEYWORD_NONE); + addKeyword (groupdef->keywords[i], language, groupdef->value); + } +} diff --git a/ctags/main/keyword.h b/ctags/main/keyword.h index 9be3e5b4d..31587af21 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,16 @@ 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 +/* +* KEYWORD GROUP API: Adding keywords for value in batch +*/ +struct keywordGroup { + int value; + bool addingUnlessExisting; + const char *keywords []; /* NULL terminated */ +}; + +extern void addKeywordGroup (const struct keywordGroup *const groupdef, + langType language); #endif /* CTAGS_MAIN_KEYWORD_H */ diff --git a/ctags/main/keyword_p.h b/ctags/main/keyword_p.h new file mode 100644 index 000000000..32b846d76 --- /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 dissimilarity index 73% index a751aa422..c34c19c72 100644 --- a/ctags/main/kind.c +++ b/ctags/main/kind.c @@ -1,157 +1,697 @@ -/* - * - * 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. - * - */ - -#include "general.h" - -#include -#include "debug.h" -#include "kind.h" -#include "parse.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"); -} - -extern const char *renderRole (const roleDefinition* const role, vString* b) -{ - vStringCatS (b, role->name); - 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, - 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) -{ - 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 < 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; - - if (kind->master) - enableKind (kind->master, enable); - else - { - kind->enabled = enable; - for (slave = kind->slave; slave; slave = slave->slave) - slave->enabled = enable; - } -} +/* + * + * 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. + * + */ + +#include "general.h" + +#include +#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" + +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; + 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) +{ + vStringCatS (b, role->name); + return vStringValue (b); +} + +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]"); +} + +extern void enableKind (kindDefinition *kind, bool enable) +{ + kindDefinition *slave; + + if (kind->master) + enableKind (kind->master, enable); + else + { + kind->enabled = enable; + for (slave = kind->slave; slave; slave = slave->slave) + slave->enabled = enable; + } +} + +extern void enableRole (roleDefinition *role, bool enable) +{ + role->enabled = enable; +} + +static void initRoleObject (roleObject *robj, roleDefinition *rdef, freeRoleDefFunc freefunc, int roleId) +{ +#ifdef DEBUG + size_t len = strlen (rdef->name); + for (int i = 0; i < len; i++) + Assert (isalnum (rdef->name [i])); +#endif + robj->def = rdef; + robj->free = freefunc; + robj->def->id = roleId; +} + +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++) + initRoleObject (rcb->role + j, kind->def->roles + j, NULL, 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; + + 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; + kind->def = parser->kindTable + i; + kind->free = NULL; + kind->def->id = i; + kind->rcb = allocRoleControlBlock (kind); + kind->dynamicSeparators = NULL; + } + + 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); + } + eFreeNoNullCheck (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); + 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); + + if (kcb->kind) + 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); + kcb->kind [def->id].dynamicSeparators = NULL; + + 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++; + + 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); + initRoleObject (rcb->role + roleIndex, def, freeRoleDef, 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, char 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 int getKindIndexForLetter (struct kindControlBlock* kcb, char letter) +{ + unsigned int i; + kindDefinition * kdef; + + for (i = 0; i < countKinds (kcb); ++i) + { + kdef = getKind (kcb, i); + if (kdef->letter == letter) + return (unsigned int)i; + } + return KIND_GHOST_INDEX; +} + +extern int getKindIndexForName (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 (int)i; + } + return KIND_GHOST_INDEX; +} + +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; + } + } + } + } +} + +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 suitable 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) +{ + 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_LETTER)) + { + 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_LETTER)) + 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 0bd56599c..5a9b243a6 100644 --- a/ctags/main/kind.h +++ b/ctags/main/kind.h @@ -8,45 +8,60 @@ #ifndef CTAGS_MAIN_KIND_H #define CTAGS_MAIN_KIND_H +/* +* INCLUDE FILES +*/ + #include "general.h" #include "types.h" -#include "routines.h" /* for STRINGIFY */ -#include "vstring.h" +#include "routines.h" + +/* +* DATA DECLARATIONS +*/ -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; +}; /* * Predefined kinds */ -#define KIND_REGEX_DEFAULT 'r' -#define KIND_REGEX_DEFAULT_LONG "regex" -/* We treat ' ' as a ghost kind. - It will never be listed up in --list-kinds. */ - -#define KIND_NULL '\0' - +#define KIND_REGEX_DEFAULT_LETTER 'r' +#define KIND_REGEX_DEFAULT_NAME "regex" + +#define KIND_NULL_LETTER '\0' + +/* GHOST kind can be used for a value for + * initializing a variable holding a kind index, + * or filling a struct member holding a kind index. + * + * Typical case is filling a scope related struct + * member with GHOST to represent root name scope. + * + * input.c: + * + * int main (void) { return 0; } + * + * Consider that tagging "main" in above input. + * You may wonder what kind of value + * should be used to fill tag.extensionFields.scopeKindIndex. + * KIND_GHOST_INDEX can be used for the purpose. + */ #define KIND_GHOST_INDEX -1 -#define KIND_GHOST ' ' -#define KIND_GHOST_LONG "ghost" +#define KIND_GHOST_LETTER ' ' +#define KIND_GHOST_NAME "ghost" #define KIND_FILE_INDEX -2 -#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_FILE_DEFAULT_LETTER 'F' +#define KIND_FILE_DEFAULT_NAME "file" #define KIND_WILDCARD_INDEX -3 -#define KIND_WILDCARD '*' +#define KIND_WILDCARD_LETTER '*' typedef struct sScopeSeparator { int parentKindIndex; @@ -56,18 +71,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 +99,10 @@ 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); -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) +/* +* FUNCTION PROTOTYPES +*/ -#define PR_KIND_WIDTH_LANG 15 +extern const char *scopeSeparatorFor (langType lang, int kindIndex, int parentKindIndex); #endif /* CTAGS_MAIN_KIND_H */ diff --git a/ctags/main/kind_p.h b/ctags/main/kind_p.h new file mode 100644 index 000000000..cbaace74e --- /dev/null +++ b/ctags/main/kind_p.h @@ -0,0 +1,82 @@ +/* +* 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 void enableRole (roleDefinition *role, 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, char letter); +extern kindDefinition *getKindForName (struct kindControlBlock* kcb, const char* name); +extern int getKindIndexForLetter (struct kindControlBlock* kcb, char letter); +extern int getKindIndexForName (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/lcpp.c b/ctags/main/lcpp.c deleted file mode 100644 index 956003e97..000000000 --- a/ctags/main/lcpp.c +++ /dev/null @@ -1,1020 +0,0 @@ -/* -* Copyright (c) 1996-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. -* -* This module contains the high level input read functions (preprocessor -* directives are handled within this level). -*/ - -/* -* INCLUDE FILES -*/ -#include "general.h" /* must always come first */ - -#include - -#include "debug.h" -#include "entry.h" -#include "lcpp.h" -#include "kind.h" -#include "options.h" -#include "read.h" -#include "vstring.h" -#include "parse.h" -#include "xtag.h" - -/* -* MACROS -*/ -#define stringMatch(s1,s2) (strcmp (s1,s2) == 0) -#define isspacetab(c) ((c) == SPACE || (c) == TAB) - -/* -* DATA DECLARATIONS -*/ -typedef enum { COMMENT_NONE, COMMENT_C, COMMENT_CPLUS, COMMENT_D } Comment; - -enum eCppLimits { - MaxCppNestingLevel = 20, - MaxDirectiveName = 10 -}; - -/* Defines the one nesting level of a preprocessor conditional. - */ -typedef struct sConditionalInfo { - bool ignoreAllBranches; /* ignoring parent conditional branch */ - bool singleBranch; /* choose only one branch */ - bool branchChosen; /* branch already selected */ - bool ignoring; /* current ignore state */ -} conditionalInfo; - -enum eState { - DRCTV_NONE, /* no known directive - ignore to end of line */ - DRCTV_DEFINE, /* "#define" encountered */ - DRCTV_HASH, /* initial '#' read; determine directive */ - DRCTV_IF, /* "#if" or "#ifdef" encountered */ - DRCTV_PRAGMA, /* #pragma encountered */ - DRCTV_UNDEF /* "#undef" encountered */ -}; - -/* Defines the current state of the pre-processor. - */ -typedef struct sCppState { - int ungetch, ungetch2; /* ungotten characters, if any */ - bool resolveRequired; /* must resolve if/else/elif/endif branch */ - bool hasAtLiteralStrings; /* supports @"c:\" strings */ - bool hasCxxRawLiteralStrings; /* supports R"xxx(...)xxx" strings */ - int defineMacroKindIndex; - struct sDirective { - enum eState state; /* current directive being processed */ - bool accept; /* is a directive syntactically permitted? */ - vString * name; /* macro name */ - unsigned int nestLevel; /* level 0 is not used */ - conditionalInfo ifdef [MaxCppNestingLevel]; - } directive; -} cppState; - -/* -* DATA DEFINITIONS -*/ - -static vString *signature = NULL; -static bool collectingSignature = false; - -/* Use brace formatting to detect end of block. - */ -static bool BraceFormat = false; - -static cppState Cpp = { - '\0', '\0', /* ungetch characters */ - false, /* resolveRequired */ - false, /* hasAtLiteralStrings */ - false, /* hasCxxRawLiteralStrings */ - -1, /* defineMacroKindIndex */ - { - DRCTV_NONE, /* state */ - false, /* accept */ - NULL, /* tag name */ - 0, /* nestLevel */ - { {false,false,false,false} } /* ifdef array */ - } /* directive */ -}; - -/* -* FUNCTION DEFINITIONS -*/ - -extern bool cppIsBraceFormat (void) -{ - return BraceFormat; -} - -extern unsigned int cppGetDirectiveNestLevel (void) -{ - return Cpp.directive.nestLevel; -} - -extern void cppInit (const bool state, const bool hasAtLiteralStrings, - const bool hasCxxRawLiteralStrings, - int defineMacroKindIndex) -{ - BraceFormat = state; - - Cpp.ungetch = '\0'; - Cpp.ungetch2 = '\0'; - Cpp.resolveRequired = false; - Cpp.hasAtLiteralStrings = hasAtLiteralStrings; - Cpp.hasCxxRawLiteralStrings = hasCxxRawLiteralStrings; - Cpp.defineMacroKindIndex = defineMacroKindIndex; - - Cpp.directive.state = DRCTV_NONE; - Cpp.directive.accept = true; - Cpp.directive.nestLevel = 0; - - Cpp.directive.ifdef [0].ignoreAllBranches = false; - Cpp.directive.ifdef [0].singleBranch = false; - Cpp.directive.ifdef [0].branchChosen = false; - Cpp.directive.ifdef [0].ignoring = false; - - Cpp.directive.name = vStringNewOrClear (Cpp.directive.name); -} - -extern void cppTerminate (void) -{ - if (Cpp.directive.name != NULL) - { - vStringDelete (Cpp.directive.name); - Cpp.directive.name = NULL; - } -} - -extern void cppBeginStatement (void) -{ - Cpp.resolveRequired = true; -} - -extern void cppEndStatement (void) -{ - Cpp.resolveRequired = false; -} - -/* -* Scanning functions -* -* This section handles preprocessor directives. It strips out all -* directives and may emit a tag for #define directives. -*/ - -/* This puts a character back into the input queue for the input File. - * Up to two characters may be ungotten. - */ -extern void cppUngetc (const int c) -{ - Assert (Cpp.ungetch2 == '\0'); - Cpp.ungetch2 = Cpp.ungetch; - Cpp.ungetch = c; - if (collectingSignature) - vStringChop (signature); -} - -static inline int getcAndCollect (void) -{ - int c = getcFromInputFile (); - if (collectingSignature && c != EOF) - vStringPut (signature, c); - return c; -} - -static inline void ungetcAndCollect (int c) -{ - ungetcToInputFile (c); - if (collectingSignature) - vStringChop (signature); -} - -/* Reads a directive, whose first character is given by "c", into "name". - */ -static bool readDirective (int c, char *const name, unsigned int maxLength) -{ - unsigned int i; - - for (i = 0 ; i < maxLength - 1 ; ++i) - { - if (i > 0) - { - c = getcAndCollect (); - if (c == EOF || ! isalpha (c)) - { - ungetcAndCollect (c); - break; - } - } - name [i] = c; - } - name [i] = '\0'; /* null terminate */ - - return (bool) isspacetab (c); -} - -/* Reads an identifier, whose first character is given by "c", into "tag", - * together with the file location and corresponding line number. - */ -static void readIdentifier (int c, vString *const name) -{ - vStringClear (name); - do - { - vStringPut (name, c); - c = getcAndCollect (); - } while (c != EOF && cppIsident (c)); - ungetcAndCollect (c); -} - -static conditionalInfo *currentConditional (void) -{ - return &Cpp.directive.ifdef [Cpp.directive.nestLevel]; -} - -static bool isIgnore (void) -{ - return Cpp.directive.ifdef [Cpp.directive.nestLevel].ignoring; -} - -static bool setIgnore (const bool ignore) -{ - return Cpp.directive.ifdef [Cpp.directive.nestLevel].ignoring = ignore; -} - -static bool isIgnoreBranch (void) -{ - conditionalInfo *const ifdef = currentConditional (); - - /* Force a single branch if an incomplete statement is discovered - * en route. This may have allowed earlier branches containing complete - * statements to be followed, but we must follow no further branches. - */ - if (Cpp.resolveRequired && ! BraceFormat) - ifdef->singleBranch = true; - - /* We will ignore this branch in the following cases: - * - * 1. We are ignoring all branches (conditional was within an ignored - * branch of the parent conditional) - * 2. A branch has already been chosen and either of: - * a. A statement was incomplete upon entering the conditional - * b. A statement is incomplete upon encountering a branch - */ - return (bool) (ifdef->ignoreAllBranches || - (ifdef->branchChosen && ifdef->singleBranch)); -} - -static void chooseBranch (void) -{ - if (! BraceFormat) - { - conditionalInfo *const ifdef = currentConditional (); - - ifdef->branchChosen = (bool) (ifdef->singleBranch || - Cpp.resolveRequired); - } -} - -/* Pushes one nesting level for an #if directive, indicating whether or not - * the branch should be ignored and whether a branch has already been chosen. - */ -static bool pushConditional (const bool firstBranchChosen) -{ - const bool ignoreAllBranches = isIgnore (); /* current ignore */ - bool ignoreBranch = false; - - if (Cpp.directive.nestLevel < (unsigned int) MaxCppNestingLevel - 1) - { - conditionalInfo *ifdef; - - ++Cpp.directive.nestLevel; - ifdef = currentConditional (); - - /* We take a snapshot of whether there is an incomplete statement in - * progress upon encountering the preprocessor conditional. If so, - * then we will flag that only a single branch of the conditional - * should be followed. - */ - ifdef->ignoreAllBranches = ignoreAllBranches; - ifdef->singleBranch = Cpp.resolveRequired; - ifdef->branchChosen = firstBranchChosen; - ifdef->ignoring = (bool) (ignoreAllBranches || ( - ! firstBranchChosen && ! BraceFormat && - (ifdef->singleBranch || !Option.if0))); - ignoreBranch = ifdef->ignoring; - } - return ignoreBranch; -} - -/* Pops one nesting level for an #endif directive. - */ -static bool popConditional (void) -{ - if (Cpp.directive.nestLevel > 0) - --Cpp.directive.nestLevel; - - return isIgnore (); -} - -static int makeDefineTag (const char *const name, bool parameterized, bool undef) -{ - const bool isFileScope = (bool) (! isInputHeaderFile ()); - - if (Cpp.defineMacroKindIndex == -1) - return CORK_NIL; - if (isFileScope && !isXtagEnabled(XTAG_FILE_SCOPE)) - return CORK_NIL; - - if ( /* condition for definition tag */ - ((!undef) && isLanguageKindEnabled (getInputLanguage(), Cpp.defineMacroKindIndex)) - || /* condition for reference tag */ - (undef && isXtagEnabled(XTAG_REFERENCE_TAGS))) - { - tagEntryInfo e; - - initTagEntry (&e, name, Cpp.defineMacroKindIndex); - e.lineNumberEntry = (bool) (Option.locate == EX_LINENUM); - e.isFileScope = isFileScope; - e.truncateLineAfterTag = true; - if (parameterized) - e.extensionFields.signature = cppGetSignature (); - makeTagEntry (&e); - if (parameterized) - eFree((char *) e.extensionFields.signature); - } - return CORK_NIL; -} - -static int directiveDefine (const int c, bool undef) -{ - int r = CORK_NIL; - - if (cppIsident1 (c)) - { - bool parameterized; - int nc; - - readIdentifier (c, Cpp.directive.name); - nc = getcAndCollect (); - parameterized = (nc == '('); - if (parameterized) - { - cppStartCollectingSignature (); - while (nc != EOF) - { - int lastC = nc; - nc = getcAndCollect (); - if (nc == '\n' && lastC != '\\') - break; - } - cppStopCollectingSignature (); - } - ungetcAndCollect (nc); - if (! isIgnore ()) - makeDefineTag (vStringValue (Cpp.directive.name), parameterized, undef); - } - Cpp.directive.state = DRCTV_NONE; - return r; -} - -static void directiveUndef (const int c) -{ - if (isXtagEnabled (XTAG_REFERENCE_TAGS)) - { - directiveDefine (c, true); - } - else - { - Cpp.directive.state = DRCTV_NONE; - } -} - -static void directivePragma (int c) -{ - if (cppIsident1 (c)) - { - readIdentifier (c, Cpp.directive.name); - if (stringMatch (vStringValue (Cpp.directive.name), "weak")) - { - /* generate macro tag for weak name */ - do - { - c = getcAndCollect (); - } while (c == SPACE); - if (cppIsident1 (c)) - { - readIdentifier (c, Cpp.directive.name); - makeDefineTag (vStringValue (Cpp.directive.name), NULL, false); - } - } - } - Cpp.directive.state = DRCTV_NONE; -} - -static bool directiveIf (const int c) -{ - const bool ignore = pushConditional ((bool) (c != '0')); - - Cpp.directive.state = DRCTV_NONE; - - return ignore; -} - -static bool directiveHash (const int c) -{ - bool ignore = false; - char directive [MaxDirectiveName]; - DebugStatement ( const bool ignore0 = isIgnore (); ) - - readDirective (c, directive, MaxDirectiveName); - if (stringMatch (directive, "define")) - Cpp.directive.state = DRCTV_DEFINE; - else if (stringMatch (directive, "undef")) - Cpp.directive.state = DRCTV_UNDEF; - else if (strncmp (directive, "if", (size_t) 2) == 0) - Cpp.directive.state = DRCTV_IF; - else if (stringMatch (directive, "elif") || - stringMatch (directive, "else")) - { - ignore = setIgnore (isIgnoreBranch ()); - if (! ignore && stringMatch (directive, "else")) - chooseBranch (); - Cpp.directive.state = DRCTV_NONE; - DebugStatement ( if (ignore != ignore0) debugCppIgnore (ignore); ) - } - else if (stringMatch (directive, "endif")) - { - DebugStatement ( debugCppNest (false, Cpp.directive.nestLevel); ) - ignore = popConditional (); - Cpp.directive.state = DRCTV_NONE; - DebugStatement ( if (ignore != ignore0) debugCppIgnore (ignore); ) - } - else if (stringMatch (directive, "pragma")) - Cpp.directive.state = DRCTV_PRAGMA; - else - Cpp.directive.state = DRCTV_NONE; - - return ignore; -} - -/* Handles a pre-processor directive whose first character is given by "c". - */ -static bool handleDirective (const int c, int *macroCorkIndex) -{ - bool ignore = isIgnore (); - - switch (Cpp.directive.state) - { - case DRCTV_NONE: ignore = isIgnore (); break; - case DRCTV_DEFINE: - *macroCorkIndex = directiveDefine (c, false); - break; - case DRCTV_HASH: ignore = directiveHash (c); break; - case DRCTV_IF: ignore = directiveIf (c); break; - case DRCTV_PRAGMA: directivePragma (c); break; - case DRCTV_UNDEF: directiveUndef (c); break; - } - return ignore; -} - -/* Called upon reading of a slash ('/') characters, determines whether a - * comment is encountered, and its type. - */ -static Comment isComment (void) -{ - Comment comment; - const int next = getcAndCollect (); - - if (next == '*') - comment = COMMENT_C; - else if (next == '/') - comment = COMMENT_CPLUS; - else if (next == '+') - comment = COMMENT_D; - else - { - ungetcAndCollect (next); - comment = COMMENT_NONE; - } - return comment; -} - -/* Skips over a C style comment. According to ANSI specification a comment - * is treated as white space, so we perform this substitution. - */ -int cppSkipOverCComment (void) -{ - int c = getcAndCollect (); - - while (c != EOF) - { - if (c != '*') - c = getcAndCollect (); - else - { - const int next = getcAndCollect (); - - if (next != '/') - c = next; - else - { - c = SPACE; /* replace comment with space */ - break; - } - } - } - return c; -} - -/* Skips over a C++ style comment. - */ -static int skipOverCplusComment (void) -{ - int c; - - while ((c = getcAndCollect ()) != EOF) - { - if (c == BACKSLASH) - getcAndCollect (); /* throw away next character, too */ - else if (c == NEWLINE) - break; - } - return c; -} - -/* Skips over a D style comment. - * Really we should match nested /+ comments. At least they're less common. - */ -static int skipOverDComment (void) -{ - int c = getcAndCollect (); - - while (c != EOF) - { - if (c != '+') - c = getcAndCollect (); - else - { - const int next = getcAndCollect (); - - if (next != '/') - c = next; - else - { - c = SPACE; /* replace comment with space */ - break; - } - } - } - return c; -} - -/* Skips to the end of a string, returning a special character to - * symbolically represent a generic string. - */ -static int skipToEndOfString (bool ignoreBackslash) -{ - int c; - - while ((c = getcAndCollect ()) != EOF) - { - if (c == BACKSLASH && ! ignoreBackslash) - getcAndCollect (); /* throw away next character, too */ - else if (c == DOUBLE_QUOTE) - break; - } - return STRING_SYMBOL; /* symbolic representation of string */ -} - -static int isCxxRawLiteralDelimiterChar (int c) -{ - return (c != ' ' && c != '\f' && c != '\n' && c != '\r' && c != '\t' && c != '\v' && - c != '(' && c != ')' && c != '\\'); -} - -static int skipToEndOfCxxRawLiteralString (void) -{ - int c = getcAndCollect (); - - if (c != '(' && ! isCxxRawLiteralDelimiterChar (c)) - { - ungetcAndCollect (c); - c = skipToEndOfString (false); - } - else - { - char delim[16]; - unsigned int delimLen = 0; - bool collectDelim = true; - - do - { - if (collectDelim) - { - if (isCxxRawLiteralDelimiterChar (c) && - delimLen < (sizeof delim / sizeof *delim)) - delim[delimLen++] = c; - else - collectDelim = false; - } - else if (c == ')') - { - unsigned int i = 0; - - while ((c = getcAndCollect ()) != EOF && i < delimLen && delim[i] == c) - i++; - if (i == delimLen && c == DOUBLE_QUOTE) - break; - else - ungetcAndCollect (c); - } - } - while ((c = getcAndCollect ()) != EOF); - c = STRING_SYMBOL; - } - return c; -} - -/* Skips to the end of the three (possibly four) 'c' sequence, returning a - * special character to symbolically represent a generic character. - */ -static int skipToEndOfChar (void) -{ - int c; - int count = 0; - - while ((c = getcAndCollect ()) != EOF) - { - ++count; - if (c == BACKSLASH) - getcAndCollect (); /* throw away next character, too */ - else if (c == SINGLE_QUOTE) - break; - else if (c == NEWLINE) - { - ungetcAndCollect (c); - break; - } - } - return CHAR_SYMBOL; /* symbolic representation of character */ -} - -/* This function returns the next character, stripping out comments, - * C pre-processor directives, and the contents of single and double - * quoted strings. In short, strip anything which places a burden upon - * the tokenizer. - */ -extern int cppGetc (void) -{ - bool directive = false; - bool ignore = false; - int c; - int macroCorkIndex = CORK_NIL; - - if (Cpp.ungetch != '\0') - { - c = Cpp.ungetch; - Cpp.ungetch = Cpp.ungetch2; - Cpp.ungetch2 = '\0'; - if (collectingSignature) - vStringPut (signature, c); - return c; /* return here to avoid re-calling debugPutc () */ - } - else do - { -start_loop: - c = getcAndCollect (); -process: - switch (c) - { - case EOF: - ignore = false; - directive = false; - macroCorkIndex = CORK_NIL; - break; - - case TAB: - case SPACE: - break; /* ignore most white space */ - - case NEWLINE: - if (directive && ! ignore) - { - macroCorkIndex = CORK_NIL; - directive = false; - } - Cpp.directive.accept = true; - break; - - case DOUBLE_QUOTE: - Cpp.directive.accept = false; - c = skipToEndOfString (false); - break; - - case '#': - if (Cpp.directive.accept) - { - directive = true; - Cpp.directive.state = DRCTV_HASH; - Cpp.directive.accept = false; - } - break; - - case SINGLE_QUOTE: - Cpp.directive.accept = false; - c = skipToEndOfChar (); - break; - - case '/': - { - const Comment comment = isComment (); - - if (comment == COMMENT_C) - c = cppSkipOverCComment (); - else if (comment == COMMENT_CPLUS) - { - c = skipOverCplusComment (); - if (c == NEWLINE) - ungetcAndCollect (c); - } - else if (comment == COMMENT_D) - c = skipOverDComment (); - else - Cpp.directive.accept = false; - break; - } - - case BACKSLASH: - { - int next = getcAndCollect (); - - if (next == NEWLINE) - goto start_loop; - else - ungetcAndCollect (next); - break; - } - - case '?': - { - int next = getcAndCollect (); - if (next != '?') - ungetcAndCollect (next); - else - { - next = getcAndCollect (); - switch (next) - { - case '(': c = '['; break; - case ')': c = ']'; break; - case '<': c = '{'; break; - case '>': c = '}'; break; - case '/': c = BACKSLASH; goto process; - case '!': c = '|'; break; - case SINGLE_QUOTE: c = '^'; break; - case '-': c = '~'; break; - case '=': c = '#'; goto process; - default: - ungetcAndCollect ('?'); - ungetcAndCollect (next); - break; - } - } - } break; - - /* digraphs: - * input: <: :> <% %> %: %:%: - * output: [ ] { } # ## - */ - case '<': - { - int next = getcAndCollect (); - switch (next) - { - case ':': c = '['; break; - case '%': c = '{'; break; - default: ungetcAndCollect (next); - } - goto enter; - } - case ':': - { - int next = getcAndCollect (); - if (next == '>') - c = ']'; - else - ungetcAndCollect (next); - goto enter; - } - case '%': - { - int next = getcAndCollect (); - switch (next) - { - case '>': c = '}'; break; - case ':': c = '#'; goto process; - default: ungetcAndCollect (next); - } - goto enter; - } - - default: - if (c == '@' && Cpp.hasAtLiteralStrings) - { - int next = getcAndCollect (); - if (next == DOUBLE_QUOTE) - { - Cpp.directive.accept = false; - c = skipToEndOfString (true); - break; - } - else - ungetcAndCollect (next); - } - else if (c == 'R' && Cpp.hasCxxRawLiteralStrings) - { - /* OMG!11 HACK!!11 Get the previous character. - * - * We need to know whether the previous character was an identifier or not, - * because "R" has to be on its own, not part of an identifier. This allows - * for constructs like: - * - * #define FOUR "4" - * const char *p = FOUR"5"; - * - * which is not a raw literal, but a preprocessor concatenation. - * - * FIXME: handle - * - * const char *p = R\ - * "xxx(raw)xxx"; - * - * which is perfectly valid (yet probably very unlikely). */ - int prev = getNthPrevCFromInputFile (1, '\0'); - int prev2 = getNthPrevCFromInputFile (2, '\0'); - int prev3 = getNthPrevCFromInputFile (3, '\0'); - - if (! cppIsident (prev) || - (! cppIsident (prev2) && (prev == 'L' || prev == 'u' || prev == 'U')) || - (! cppIsident (prev3) && (prev2 == 'u' && prev == '8'))) - { - int next = getcAndCollect (); - if (next != DOUBLE_QUOTE) - ungetcAndCollect (next); - else - { - Cpp.directive.accept = false; - c = skipToEndOfCxxRawLiteralString (); - break; - } - } - } - enter: - Cpp.directive.accept = false; - if (directive) - ignore = handleDirective (c, ¯oCorkIndex); - break; - } - } while (directive || ignore); - - DebugStatement ( debugPutc (DEBUG_CPP, c); ) - DebugStatement ( if (c == NEWLINE) - debugPrintf (DEBUG_CPP, "%6ld: ", getInputLineNumber () + 1); ) - - return c; -} - -typedef enum -{ - st_none_t, - st_escape_t, - st_c_comment_t, - st_cpp_comment_t, - st_double_quote_t, - st_single_quote_t -} ParseState; - -static void stripCodeBuffer(char *buf) -{ - int i = 0, pos = 0; - ParseState state = st_none_t, prev_state = st_none_t; - - while (buf[i] != '\0') - { - switch(buf[i]) - { - case '/': - if (st_none_t == state) - { - /* Check if this is the start of a comment */ - if (buf[i+1] == '*') /* C comment */ - state = st_c_comment_t; - else if (buf[i+1] == '/') /* C++ comment */ - state = st_cpp_comment_t; - else /* Normal character */ - buf[pos++] = '/'; - } - else if (st_c_comment_t == state) - { - /* Check if this is the end of a C comment */ - if (buf[i-1] == '*') - { - if ((pos > 0) && (buf[pos-1] != ' ')) - buf[pos++] = ' '; - state = st_none_t; - } - } - break; - case '"': - if (st_none_t == state) - state = st_double_quote_t; - else if (st_double_quote_t == state) - state = st_none_t; - break; - case '\'': - if (st_none_t == state) - state = st_single_quote_t; - else if (st_single_quote_t == state) - state = st_none_t; - break; - default: - if ((buf[i] == '\\') && (st_escape_t != state)) - { - prev_state = state; - state = st_escape_t; - } - else if (st_escape_t == state) - { - state = prev_state; - prev_state = st_none_t; - } - else if ((buf[i] == '\n') && (st_cpp_comment_t == state)) - { - if ((pos > 0) && (buf[pos-1] != ' ')) - buf[pos++] = ' '; - state = st_none_t; - } - else if (st_none_t == state) - { - if (isspace(buf[i])) - { - if ((pos > 0) && (buf[pos-1] != ' ')) - buf[pos++] = ' '; - } - else - buf[pos++] = buf[i]; - } - break; - } - ++i; - } - buf[pos] = '\0'; - return; -} - -extern char *cppGetSignature(void) -{ - char *start, *end; - int level; - - if (NULL == signature || vStringLength (signature) < 2) - return NULL; - - start = eStrdup (vStringValue (signature)); - stripCodeBuffer(start); - for (level = 1, end = start + 1; level > 0; ++end) - { - if ('\0' == *end) - break; - else if ('(' == *end) - ++ level; - else if (')' == *end) - -- level; - } - *end = '\0'; - return start; -} - -extern void cppStartCollectingSignature (void) -{ - signature = vStringNewOrClear (signature); - vStringPut (signature, '('); - collectingSignature = true; -} - -extern void cppStopCollectingSignature (void) -{ - collectingSignature = false; -} - -extern void cppClearSignature (void) -{ - signature = vStringNewOrClear (signature); - collectingSignature = false; -} diff --git a/ctags/main/lcpp.h b/ctags/main/lcpp.h deleted file mode 100644 index 86760a916..000000000 --- a/ctags/main/lcpp.h +++ /dev/null @@ -1,80 +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 get.c -*/ -#ifndef CTAGS_MAIN_GET_H -#define CTAGS_MAIN_GET_H - -/* -* INCLUDE FILES -*/ -#include "general.h" /* must always come first */ -#include "types.h" - -/* -* MACROS -*/ -/* Is the character valid as a character of a C identifier? - * VMS allows '$' in identifiers. - */ -#define cppIsident(c) (isalnum(c) || (c) == '_' || (c) == '$') - -/* Is the character valid as the first character of a C identifier? - * C++ allows '~' in destructors. - * VMS allows '$' in identifiers. - * Vala allows '@' in identifiers. - */ -#define cppIsident1(c) ( ((c >= 0) && (c < 0x80) && isalpha(c)) \ - || (c) == '_' || (c) == '~' || (c) == '$' || (c) == '@') -/* NOTE about isident1 profitability - - Doing the same as isascii before passing value to isalpha - ---------------------------------------------------------- - cppGetc() can return the value out of range of char. - cppGetc calls skipToEndOfString and skipToEndOfString can - return STRING_SYMBOL(== 338). - - Depending on the platform, isalpha(338) returns different value . - As far as Fedora22, it returns 0. On Windows 2010, it returns 1. - - man page on Fedora 22 says: - - These functions check whether c, which must have the value of an - unsigned char or EOF, falls into a certain character class - according to the specified locale. - - isascii is for suitable to verify the range of input. However, it - is not portable enough. */ - -#define RoleTemplateUndef { true, "undef", "undefined" } - -#define RoleTemplateSystem { true, "system", "system header" } -#define RoleTemplateLocal { true, "local", "local header" } - -/* -* FUNCTION PROTOTYPES -*/ -extern bool cppIsBraceFormat (void); -extern unsigned int cppGetDirectiveNestLevel (void); - -extern void cppInit (const bool state, - const bool hasAtLiteralStrings, - const bool hasCxxRawLiteralStrings, - int defineMacroKindIndex); -extern void cppTerminate (void); -extern void cppBeginStatement (void); -extern void cppEndStatement (void); -extern void cppUngetc (const int c); -extern int cppGetc (void); -extern int cppSkipOverCComment (void); - -extern char *cppGetSignature (void); -extern void cppStartCollectingSignature (void); -extern void cppStopCollectingSignature (void); -extern void cppClearSignature (void); - -#endif /* CTAGS_MAIN_GET_H */ diff --git a/ctags/main/lregex.c b/ctags/main/lregex.c index 6686beec1..56175fd8d 100644 --- a/ctags/main/lregex.c +++ b/ctags/main/lregex.c @@ -23,20 +23,28 @@ #ifdef HAVE_SYS_TYPES_H # include /* declare off_t (not known to regex.h on FreeBSD) */ #endif -#include +#include + +#include #include "debug.h" -#include "entry.h" -#include "flags.h" +#include "colprint_p.h" +#include "entry_p.h" +#include "field_p.h" +#include "flags_p.h" #include "htable.h" #include "kind.h" #include "options.h" -#include "parse.h" +#include "parse_p.h" +#include "promise.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; -static unsigned long currentScope = CORK_NIL; /* * MACROS @@ -45,18 +53,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 +79,73 @@ 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 boundarySpec { + int patternGroup; + bool fromStartOfGroup; + bool placeholder; +}; + +struct guestLangSpec { + enum guestLangSpecType { + GUEST_LANG_UNKNOWN, + GUEST_LANG_PLACEHOLDER, /* _ */ + GUEST_LANG_STATIC_LANGNAME, /* C, Python,... */ + GUEST_LANG_PTN_GROUP_FOR_LANGNAME, /* \1, \2, ..., \9 */ + GUEST_LANG_PTN_GROUP_FOR_FILEMAP, /* *1, *2, ... *9 */ + } type; + union { + langType lang; + int patternGroup; + } spec; +}; + +struct guestSpec { + struct guestLangSpec lang; +#define BOUNDARY_START 0 +#define BOUNDARY_END 1 + struct boundarySpec boundary[2]; +}; + +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 +154,202 @@ typedef struct { } u; unsigned int scopeActions; bool *disabled; + + enum regexParserType regptype; + struct mGroupSpec mgroup; + struct guestSpec guest; + struct mTableActionSpec taction; + + int xtagType; + ptrArray *fieldPatterns; + + char *pattern_string; + + char *anonymous_tag_prefix; + + 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 boundaryInRequest { + bool offset_set; + off_t offset; +}; + +struct guestRequest { + bool lang_set; + langType lang; + + struct boundaryInRequest boundary[2]; +}; + +struct lregexControlBlock { + int currentScope; + ptrArray *entries [2]; + + ptrArray *tables; + ptrArray *tstack; + + struct guestRequest *guest_req; + + 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 int makePromiseForAreaSpecifiedWithOffsets (const char *parser, + off_t startOffset, + off_t endOffset); + +static struct guestRequest *guestRequestNew (void); +static void guestRequestDelete (struct guestRequest *); +static bool guestRequestIsFilled(struct guestRequest *); +static void guestRequestClear (struct guestRequest *); +static void guestRequestSubmit (struct guestRequest *); + +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); + + if (p->anonymous_tag_prefix) + eFree (p->anonymous_tag_prefix); + + 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->guest_req = guestRequestNew (); + 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; + + guestRequestDelete (lcb->guest_req); + lcb->guest_req = 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 +360,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 +378,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 +413,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 @@ -275,7 +478,7 @@ static flagDefinition prePtrnFlagDef[] = { static void scope_ptrn_flag_eval (const char* const f CTAGS_ATTR_UNUSED, const char* const v, void* data) { - unsigned long *bfields = data; + unsigned int *bfields = data; if (strcmp (v, "ref") == 0) *bfields |= SCOPE_REF; @@ -294,7 +497,7 @@ static void scope_ptrn_flag_eval (const char* const f CTAGS_ATTR_UNUSED, static void placeholder_ptrn_flag_eval (const char* const f CTAGS_ATTR_UNUSED, const char* const v CTAGS_ATTR_UNUSED, void* data) { - unsigned long *bfields = data; + unsigned int *bfields = data; *bfields |= SCOPE_PLACEHOLDER; } @@ -305,296 +508,915 @@ 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); + 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 initGuestSpec (struct guestSpec *guest) +{ + guest->lang.type = GUEST_LANG_UNKNOWN; +} - if (kind_letter) - { - union cptr c = { .p = NULL }; +static void initTaction(struct mTableActionSpec *taction) +{ + taction->action = TACTION_NOP; + taction->table = NULL; +} - 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); +static regexPattern * refPattern (regexPattern * ptrn) +{ + ptrn->refcount++; 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 regexPattern * newPattern (regex_t* const pattern, + enum regexParserType regptype) { - regexPattern * ptrn; - bool exclusive = false; - unsigned long scopeActions = 0UL; + regexPattern *ptrn = xCalloc(1, regexPattern); - flagsEval (flags, prePtrnFlagDef, ARRAY_SIZE(prePtrnFlagDef), &exclusive); - flagsEval (flags, scopePtrnFlagDef, ARRAY_SIZE(scopePtrnFlagDef), &scopeActions); - if (*name == '\0' && exclusive && kind == KIND_REGEX_DEFAULT) - { - kind = KIND_GHOST; - kindName = KIND_GHOST_LONG; - } - 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') - { - /* 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; - } - else if (ptrn->u.tag.kind->name && kindName && strcmp(ptrn->u.tag.kind->name, kindName)) - { - /* 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); - } + ptrn->pattern = pattern; + ptrn->exclusive = false; + ptrn->accept_empty_name = false; + 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); + initGuestSpec (&ptrn->guest); + ptrn->u.tag.roleBits = 0; + ptrn->refcount = 1; return ptrn; } -static void addCompiledCallbackPattern (const langType language, GRegex* const pattern, - const regexCallback callback, const char* flags, - bool *disabled, - void *userData) +static regexTableEntry * newRefPatternEntry (regexTableEntry * other) { - 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; -} + regexTableEntry *entry = xCalloc (1, regexTableEntry); + Assert (other && other->pattern); -static void regex_flag_basic_short (char c CTAGS_ATTR_UNUSED, void* data) -{ - error(WARNING, "CTags 'b' flag not supported by Geany!"); + entry->pattern = refPattern(other->pattern); + return entry; } -static void regex_flag_basic_long (const char* const s CTAGS_ATTR_UNUSED, const char* const unused CTAGS_ATTR_UNUSED, void* data) +static regexTableEntry * newEntry (regex_t* const pattern, + enum regexParserType regptype) { - regex_flag_basic_short ('b', data); + regexTableEntry *entry = xCalloc (1, regexTableEntry); + entry->pattern = newPattern (pattern, regptype); + return entry; } -static void regex_flag_extend_short (char c CTAGS_ATTR_UNUSED, void* data) +static regexPattern* addCompiledTagCommon (struct lregexControlBlock *lcb, + int table_index, + regex_t* const pattern, + enum regexParserType regptype) { -} + regexTableEntry *entry = newEntry (pattern, regptype); -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); + if (regptype == REG_PARSER_MULTI_TABLE) + { + struct regexTable *table = ptrArrayItem (lcb->tables, table_index); + Assert(table); + + ptrArrayAdd (table->entries, entry); + } + else + ptrArrayAdd (lcb->entries[regptype], entry); + + useRegexMethod(lcb->owner); + + return entry->pattern; } -static void regex_flag_icase_short (char c CTAGS_ATTR_UNUSED, void* data) +static void pre_ptrn_flag_mgroup_long (const char* const s, const char* const v, void* data) { - int* cflags = data; - *cflags |= G_REGEX_CASELESS; + struct mGroupSpec *mgroup = data; + if (!v) + { + 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 (mgroup->forLineNumberDetermination < 0 + || mgroup->forLineNumberDetermination >= BACK_REFERENCE_COUNT) + { + error (WARNING, "out of range(0 ~ %d) %s specification: %s", + (BACK_REFERENCE_COUNT - 1), + s, v); + mgroup->forLineNumberDetermination = NO_MULTILINE; + } } -static void regex_flag_icase_long (const char* s CTAGS_ATTR_UNUSED, const char* const unused CTAGS_ATTR_UNUSED, void* data) +static void pre_ptrn_flag_advanceTo_long (const char* const s, const char* const v, void* data) { - regex_flag_icase_short ('i', data); -} + struct mGroupSpec *mgroup = data; + char *vdup; + char *tmp; -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"}, -}; + if (!v) + { + error (WARNING, "no value is given for: %s", s); + return; + } -static GRegex* compileRegex (const char* const regexp, const char* const flags) -{ - int cflags = G_REGEX_MULTILINE; - GRegex *result = NULL; - GError *err = NULL; + vdup = eStrdup (v); - flagsEval (flags, - regexFlagDefs, - ARRAY_SIZE(regexFlagDefs), - &cflags); + mgroup->nextFromStart = false; + if ((tmp = strstr(vdup, "start"))) + { + mgroup->nextFromStart = true; + *tmp = '\0'; + } + else if ((tmp = strstr(vdup, "end"))) + *tmp = '\0'; - result = g_regex_new(regexp, cflags, 0, &err); - if (err) + if (!strToInt (vdup, 10, &(mgroup->forNextScanning))) { - error (WARNING, "regcomp %s: %s", regexp, err->message); - g_error_free(err); + error (WARNING, "wrong %s specification: %s", s, vdup); + mgroup->nextFromStart = false; } - return result; + 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); } +struct guestPtrnFlagData { + enum regexParserType type; + struct guestSpec *guest; +}; -static void parseKinds ( - const char* const kinds, char* const kind, char** const kindName, - char **description) +static void pre_ptrn_flag_guest_long (const char* const s, const char* const v, void* data) { - *kind = '\0'; - *kindName = NULL; - *description = NULL; - if (kinds == NULL || kinds [0] == '\0') + struct guestPtrnFlagData *flagData = data; + enum regexParserType type = flagData->type; + struct guestSpec *guest = flagData->guest; + struct boundarySpec *current; + + if (!v) + { + error (WARNING, "no value is given for: %s", s); + return; + } + + char *tmp = strchr (v, ','); + if (tmp == NULL) { - *kind = KIND_REGEX_DEFAULT; - *kindName = eStrdup (KIND_REGEX_DEFAULT_LONG); + error (WARNING, "no terminator found for parser name: %s", s); + return; } - else if (kinds [0] != '\0') + + if ((tmp - v) == 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 + if (type == REG_PARSER_MULTI_LINE) { - 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); - } + error (WARNING, + "using placeholder for guest name field is not allowed in multiline regex spec: %s", v); + goto err; } + + guest->lang.type = GUEST_LANG_PLACEHOLDER; } -} + else if (*v == '\\' || *v == '*') + { + const char *n_tmp = v + 1; + const char *n = n_tmp; + for (; isdigit (*n_tmp); n_tmp++); + char c = *n_tmp; + *(char *)n_tmp = '\0'; + if (!strToInt (n, 10, &(guest->lang.spec.patternGroup))) + { + error (WARNING, "wrong guest name specification: %s", v); + goto err; + } + else if (guest->lang.spec.patternGroup >= BACK_REFERENCE_COUNT) + { + error (WARNING, "wrong guest name specification (back reference count is too large): %d", + guest->lang.spec.patternGroup); + goto err; + } -static void processLanguageRegex (const langType language, - const char* const parameter) -{ - if (parameter == NULL || parameter [0] == '\0') - clearPatternSet (language); - else if (parameter [0] != '@') - addLanguageRegex (language, parameter); - else if (! doesFileExist (parameter + 1)) - error (WARNING, "cannot open regex file"); + *(char *)n_tmp = c; + if (*n_tmp != ',') + { + error (WARNING, "wrong guest specification (garbage at the end of end guest spec): %s", v); + goto err; + } + + guest->lang.type = (*v == '\\') + ? GUEST_LANG_PTN_GROUP_FOR_LANGNAME + : GUEST_LANG_PTN_GROUP_FOR_FILEMAP; + } else { - const char* regexfile = parameter + 1; - MIO* const mio = mio_new_file (regexfile, "r"); - if (mio == NULL) - error (WARNING | PERROR, "%s", regexfile); - else + guest->lang.spec.lang = getNamedLanguage (v, (tmp - v)); + if (guest->lang.spec.lang == LANG_IGNORE) { - vString* const regex = vStringNew (); - while (readLineRaw (regex, mio)) - addLanguageRegex (language, vStringValue (regex)); - mio_free (mio); - vStringDelete (regex); + error (WARNING, "no parser found for the guest spec: %s", v); + goto err; } + guest->lang.type = GUEST_LANG_STATIC_LANGNAME; } -} - -/* -* Regex pattern matching -*/ + tmp++; + if (*tmp == '\0') + { + error (WARNING, "no area spec found in the guest spec: %s", v); + goto err; + } -static vString* substitute ( - const char* const in, const char* out, - const int nmatch, const GMatchInfo* const minfo) -{ - vString* result = vStringNew (); - const char* p; - for (p = out ; *p != '\0' ; p++) + for (int i = 0; i < 2; i++) { - if (*p == '\\' && isdigit ((int) *++p)) + current = guest->boundary + i; + const char *current_field_str = (i == BOUNDARY_START? "start": "end"); + + if (tmp [0] == ((i == BOUNDARY_START)? ',': '\0')) { - 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 (type == REG_PARSER_MULTI_LINE) + error (WARNING, + "using placeholder for %s field is not allowed in multiline regex spec: %s", + current_field_str, v); + + current->placeholder = true; + } + else + { + char *n = tmp; + + for (; isdigit (*tmp); tmp++); + char c = *tmp; + *tmp = '\0'; + if (!strToInt (n, 10, &(current->patternGroup))) { - const int diglen = eo - so; - vStringNCatS (result, in + so, diglen); + error (WARNING, "wrong guest area specification (patternGroup of %s, number expected): %s:%s", + current_field_str, v, n); + goto err; + } + *tmp = c; + if (*tmp == '\0') + { + error (WARNING, "wrong guest area specification (patternGroup of %s, nether start nor end given): %s", + current_field_str, v); + goto err; + } + else if (strncmp (tmp, "start", 5) == 0) + { + current->fromStartOfGroup = true; + tmp += 5; + } + else if (strncmp (tmp, "end", 3) == 0) + { + current->fromStartOfGroup = false; + tmp += 3; + } + else + { + error (WARNING, "wrong guest area specification (%s): %s", + current_field_str, v); + goto err; + } + } + + if (i == 0) + { + if (*tmp != ',') + { + error (WARNING, + "wrong guest area specification (separator between start and end boundaries): %s", v); + goto err; + } + tmp++; + } + else if (i == 1 && (*tmp != '\0')) + { + error (WARNING, "wrong guest area specification (garbage at the end of end boundary spec): %s", v); + goto err; + } + } + return; + err: + guest->lang.type = GUEST_LANG_UNKNOWN; +} + +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 flagDefinition guestPtrnFlagDef[] = { +#define EXPERIMENTAL "_" + { '\0', EXPERIMENTAL "guest", NULL, pre_ptrn_flag_guest_long, + "PARSERSPEC,N0[start|end],N1[start|end]", "run guest parser on the area"}, +}; + +static bool hasMessage(const regexPattern *const ptrn) +{ + return (ptrn->message.selection > 0 && ptrn->message.message_string); +} + +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 common_flag_extra_long (const char* const s, const char* const v, void* 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 struct fieldPattern * fieldPatternNew (fieldType ftype, const char *template) +{ + struct fieldPattern *fp; + + fp = xMalloc(1, struct fieldPattern); + fp->ftype = ftype; + fp->template = eStrdup(template); + + return fp; +} + +static void fieldPatternDelete (struct fieldPattern *fp) +{ + eFree ((void *)fp->template); + eFree (fp); +} + +static void common_flag_field_long (const char* const s, const char* const v, void* data) +{ + struct commonFlagData * cdata = data; + regexPattern *ptrn = cdata->ptrn; + + Assert (ptrn); + + struct fieldPattern *fp; + fieldType ftype; + char *fname; + const char* template; + char *tmp; + + if (!v) + { + error (WARNING, "no value is given for: %s", s); + return; + } + + tmp = strchr (v, ':'); + if (tmp == NULL || tmp == v) + { + error (WARNING, "no field name is given for: %s", s); + return; + } + + fname = eStrndup (v, tmp - v); + ftype = getFieldTypeForNameAndLanguage (fname, cdata->owner); + if (ftype == FIELD_UNKNOWN) + { + error (WARNING, "no such field \"%s\" in %s", fname, getLanguageName(cdata->owner)); + eFree (fname); + return; + } + + if (ptrn->fieldPatterns) + { + for (unsigned int i = 0; i < ptrArrayCount(ptrn->fieldPatterns); i++) + { + fp = ptrArrayItem(ptrn->fieldPatterns, i); + if (fp->ftype == ftype) + { + 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 common_flag_role_long (const char* const s, const char* const v, void* data) +{ + 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 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; + } + + if (ptrn->u.tag.kindIndex == KIND_GHOST_INDEX) + { + error (WARNING, "use \"%s\" regex flag only with an explicitly defined kind", s); + return; + } + + ptrn->anonymous_tag_prefix = eStrdup (v); +} + +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"}, + { '\0', EXPERIMENTAL "anonymous", NULL, common_flag_anonymous_long, + "PREFIX", "make an anonymous tag with PREFIX"}, +}; + + +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 + { + 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, + bool kind_explicitly_defined) +{ + Assert (ptrn); + Assert (ptrn->u.tag.name_pattern); + Assert (kindName); + kindDefinition *kdef = getLanguageKindForLetter (owner, kindLetter); + + if (kdef) + { + if (strcmp (kdef->name, kindName) && (strcmp(kindName, KIND_REGEX_DEFAULT_NAME))) + /* 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); + ptrn->u.tag.kindIndex = kdef->id; + } + else if (*ptrn->u.tag.name_pattern == '\0' && + kindLetter == KIND_REGEX_DEFAULT_LETTER && + (strcmp(kindName, KIND_REGEX_DEFAULT_NAME) == 0) && + (!kind_explicitly_defined)) + ptrn->u.tag.kindIndex = KIND_GHOST_INDEX; + else + { + kdef = kindNew (kindLetter, kindName, description); + defineLanguageKind (owner, kdef, kindFree); + ptrn->u.tag.kindIndex = kdef->id; + } +} + +static void patternEvalFlags (struct lregexControlBlock *lcb, + regexPattern * ptrn, + enum regexParserType regptype, + const char* flags) +{ + struct commonFlagData commonFlagData = { + .owner = lcb->owner, + .lcb = lcb, + .ptrn = ptrn + }; + + if (regptype == REG_PARSER_SINGLE_LINE) + flagsEval (flags, prePtrnFlagDef, ARRAY_SIZE(prePtrnFlagDef), &ptrn->exclusive); + + 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) + { + ptrn->mgroup.forNextScanning = 0; + /* ptrn->mgroup.nextFromStart is initialized in initMgroup() already. */ + flagsEval (flags, multilinePtrnFlagDef, ARRAY_SIZE(multilinePtrnFlagDef), &ptrn->mgroup); + } + + struct guestPtrnFlagData guestPtrnFlagData = { + .type = regptype, + .guest = &ptrn->guest, + }; + flagsEval (flags, guestPtrnFlagDef, ARRAY_SIZE(guestPtrnFlagDef), &guestPtrnFlagData); + + if (regptype == REG_PARSER_MULTI_TABLE) + flagsEval (flags, multitablePtrnFlagDef, ARRAY_SIZE(multitablePtrnFlagDef), &commonFlagData); +} + +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 kind_explicitly_defined, + bool *disabled) +{ + regexPattern * ptrn = addCompiledTagCommon(lcb, table_index, pattern, regptype); + + ptrn->type = PTRN_TAG; + ptrn->u.tag.name_pattern = eStrdup (name); + ptrn->disabled = disabled; + + setKind(ptrn, lcb->owner, kindLetter, kindName, description, kind_explicitly_defined); + patternEvalFlags (lcb, ptrn, regptype, flags); + + 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; +} + + +/* If a letter and/or a name are defined in kindSpec, return true. */ +static bool parseKinds ( + const char* const kindSpec, char* const kindLetter, char** const kindName, + char **description) +{ + *description = NULL; + + if (kindSpec == NULL || kindSpec [0] == '\0') + { + *kindLetter = KIND_REGEX_DEFAULT_LETTER; + *kindName = eStrdup (KIND_REGEX_DEFAULT_NAME); + return false; + } + else + { + bool explicitly_defined = false; + const char* k = kindSpec; + + if (k [0] != ',' && (k [1] == ',' || k [1] == '\0')) + { + *kindLetter = *k++; + explicitly_defined = true; + } + else + *kindLetter = KIND_REGEX_DEFAULT_LETTER; + + if (*k == ',') + ++k; + + if (k [0] == '\0') + *kindName = eStrdup (KIND_REGEX_DEFAULT_NAME); + else + { + const char *const comma = strchr (k, ','); + + if (comma == NULL) + { + if (strlen (k) == 0) + *kindName = eStrdup (KIND_REGEX_DEFAULT_NAME); + else + { + *kindName = eStrdup (k); + explicitly_defined = true; + } + } + else + { + if (comma - k == 0) + *kindName = eStrdup (KIND_REGEX_DEFAULT_NAME); + else + { + *kindName = eStrndup (k, comma - k ); + explicitly_defined = true; + } + k = comma + 1; + if (k [0] != '\0') + *description = eStrdup (k); + } + } + return explicitly_defined; + } +} + +/* +* Regex pattern matching +*/ + + +static vString* substitute ( + const char* const in, const char* out, + const int nmatch, const regmatch_t* const pmatch) +{ + vString* result = vStringNew (); + const char* p; + for (p = out ; *p != '\0' ; p++) + { + if (*p == '\\' && isdigit ((int) *++p)) + { + const int dig = *p - '0'; + if (0 < dig && dig < nmatch && pmatch [dig].rm_so != -1) + { + const int diglen = pmatch [dig].rm_eo - pmatch [dig].rm_so; + vStringNCatS (result, in + pmatch [dig].rm_so, diglen); } } else if (*p != '\n' && *p != '\r') @@ -603,14 +1425,42 @@ 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; + 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 = + (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 scope = CORK_NIL; int n; vStringStripLeading (name); @@ -620,119 +1470,398 @@ 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_DEFINITION_INDEX, 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, false, 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); + } + } + + if (patbuf->anonymous_tag_prefix) + markTagExtraBit (&e, XTAG_ANONYMOUS); + + 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 isGuestRequestConsistent (struct guestRequest *guest_req) +{ + return (guest_req->lang != LANG_IGNORE) + && (guest_req->boundary[BOUNDARY_START].offset < guest_req->boundary[BOUNDARY_END].offset); +} + +static bool fillGuestRequest (const char *start, + const char *current, + regmatch_t pmatch [BACK_REFERENCE_COUNT], + struct guestSpec *guest_spec, + struct guestRequest *guest_req) +{ + if (guest_spec->lang.type == GUEST_LANG_UNKNOWN) + return false; + else if (guest_spec->lang.type == GUEST_LANG_PLACEHOLDER) + ; + else if (guest_spec->lang.type == GUEST_LANG_STATIC_LANGNAME) + { + guest_req->lang = guest_spec->lang.spec.lang; + guest_req->lang_set = true; + } + else if (guest_spec->lang.type == GUEST_LANG_PTN_GROUP_FOR_LANGNAME) + { + const char * name = current + pmatch [guest_spec->lang.spec.patternGroup].rm_so; + int size = pmatch [guest_spec->lang.spec.patternGroup].rm_eo + - pmatch [guest_spec->lang.spec.patternGroup].rm_so; + if (size > 0) + { + guest_req->lang = getNamedLanguage (name, size); + guest_req->lang_set = true; + } + } + else if (guest_spec->lang.type == GUEST_LANG_PTN_GROUP_FOR_FILEMAP) + { + const char * name = current + pmatch [guest_spec->lang.spec.patternGroup].rm_so; + int size = pmatch [guest_spec->lang.spec.patternGroup].rm_eo + - pmatch [guest_spec->lang.spec.patternGroup].rm_so; + char *fname = (size > 0)? eStrndup (name, size): NULL; + + if (fname) + { + guest_req->lang = getLanguageForFilename (fname, LANG_AUTO); + guest_req->lang_set = true; + eFree (fname); + } + } + + for (int i = 0; i < 2; i++) + { + struct boundarySpec *boundary_spec = guest_spec->boundary + i; + struct boundaryInRequest *boundary = guest_req->boundary + i; + if (!boundary_spec->placeholder) + { + boundary->offset = current - start + (boundary_spec->fromStartOfGroup + ? pmatch [boundary_spec->patternGroup].rm_so + : pmatch [boundary_spec->patternGroup].rm_eo); + boundary->offset_set = true; + } + } + return guestRequestIsFilled (guest_req); +} + +static bool matchRegexPattern (struct lregexControlBlock *lcb, + const vString* const line, + regexTableEntry *entry) +{ + bool result = false; + regmatch_t pmatch [BACK_REFERENCE_COUNT]; + int match; + regexPattern* patbuf = entry->pattern; + struct guestSpec *guest = &patbuf->guest; + + if (patbuf->disabled && *(patbuf->disabled)) + return false; + + 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 (lcb, vStringValue (line), patbuf, pmatch, 0); + + if (guest->lang.type != GUEST_LANG_UNKNOWN) + { + unsigned long ln = getInputLineNumber (); + long current = getInputFileOffsetForLine (ln); + if (fillGuestRequest (vStringValue (line) - current, + vStringValue (line), pmatch, guest, lcb->guest_req)) + { + Assert (lcb->guest_req->lang != LANG_AUTO); + if (isGuestRequestConsistent(lcb->guest_req)) + guestRequestSubmit (lcb->guest_req); + guestRequestClear (lcb->guest_req); + } + } + } + else if (patbuf->type == PTRN_CALLBACK) + result = matchCallbackPattern (line, patbuf, pmatch); + else + { + Assert ("invalid pattern type" == NULL); + result = false; + } + } + 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; + struct mGroupSpec *mgroup = &patbuf->mgroup; + struct guestSpec *guest = &patbuf->guest; + bool result = false; - GMatchInfo *minfo; + regmatch_t pmatch [BACK_REFERENCE_COUNT]; + int match = 0; + unsigned int delta = 1; + + Assert (patbuf); if (patbuf->disabled && *(patbuf->disabled)) return false; - if (g_regex_match(patbuf->pattern, vStringValue (line), 0, &minfo)) + current = start = vStringValue (allLines); + do { - result = true; + 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 [mgroup->forLineNumberDetermination].rm_so) + - start; + + entry->statistics.match++; if (patbuf->type == PTRN_TAG) - matchTagPattern (line, patbuf, minfo); + { + matchTagPattern (lcb, current, patbuf, pmatch, offset); + result = true; + } else if (patbuf->type == PTRN_CALLBACK) - matchCallbackPattern (line, patbuf, minfo); + ; /* Not implemented yet */ else { Assert ("invalid pattern type" == NULL); result = false; + break; } - } - g_match_info_free(minfo); + + if (fillGuestRequest (start, current, pmatch, guest, lcb->guest_req)) + { + Assert (lcb->guest_req->lang != LANG_AUTO); + if (isGuestRequestConsistent(lcb->guest_req)) + guestRequestSubmit (lcb->guest_req); + guestRequestClear (lcb->guest_req); + } + + delta = (mgroup->nextFromStart + ? pmatch [mgroup->forNextScanning].rm_so + : pmatch [mgroup->forNextScanning].rm_eo); + if (delta == 0) + { + 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(), pos); + 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); + guestRequestClear (lcb->guest_req); +} + +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,59 +1877,157 @@ 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 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 (const langType language, +static regexPattern *addTagRegexInternal (struct lregexControlBlock *lcb, + int table_index, + enum regexParserType regptype, const char* const regex, const char* const name, const char* const kinds, const char* const flags, bool *disabled) { - 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) + return NULL; + + char kindLetter; + char* kindName; + char* description; + kindDefinition* fileKind; + + bool explictly_defined = 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 (!isalpha ((unsigned char)kindLetter)) + error (FATAL, + "Kind letter must be an alphabetical character: \"%c\"", + kindLetter); + + if (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)); + + const char *option_bsae = (regptype == REG_PARSER_SINGLE_LINE? "regex" : + regptype == REG_PARSER_MULTI_LINE ? "mline-regex" : + regptype == REG_PARSER_MULTI_TABLE? "_mtable-regex": + NULL); + Assert (option_bsae); + + for (const char * p = kindName; *p; p++) { - GRegex* const cp = compileRegex (regex, flags); - if (cp != NULL) + if (p == kindName) { - char kind; - char* kindName; - char* description; - - parseKinds (kinds, &kind, &kindName, &description); - if (kind == getLanguageFileKind (language)->letter) + if (!isalpha(*p)) 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); + "A kind name doesn't start with an alphabetical character: " + "'%s' in \"--%s-%s\" option", + kindName, + option_bsae, + getLanguageName (lcb->owner)); + } + else + { + /* + * People may object to this error. + * Searching github repositories, I found not a few .ctags files + * in which Exuberant-ctags users define kind names with whitespaces. + * "FATAL" error breaks the compatibility. + */ + if (!isalnum(*p)) + error (/* regptype == REG_PARSER_SINGLE_LINE? WARNING: */ FATAL, + "Non-alphanumeric char is used in kind name: " + "'%s' in \"--%s-%s\" option", + kindName, + option_bsae, + getLanguageName (lcb->owner)); + } } + regexPattern *rptr = addCompiledTagPattern (lcb, table_index, + regptype, cp, name, + kindLetter, kindName, description, flags, + explictly_defined, + disabled); + rptr->pattern_string = escapeRegexPattern(regex); + + eFree (kindName); + if (description) + eFree (description); + if (*name == '\0') { - if (rptr->exclusive || rptr->scopeActions & SCOPE_PLACEHOLDER) + if (rptr->exclusive || rptr->scopeActions & SCOPE_PLACEHOLDER + || rptr->anonymous_tag_prefix + || regptype == REG_PARSER_MULTI_TABLE + || rptr->guest.lang.type != GUEST_LANG_UNKNOWN + ) rptr->accept_empty_name = true; else error (WARNING, "%s: regexp missing name pattern", regex); @@ -809,45 +2036,144 @@ static regexPattern *addTagRegexInternal (const langType language, 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 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 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 (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) + { + regexPattern *rptr = addCompiledCallbackPattern (lcb, cp, callback, flags, + disabled, userData); + rptr->pattern_string = escapeRegexPattern(regex); + } +} + +static void addTagRegexOption (struct lregexControlBlock *lcb, + enum regexParserType regptype, + const char* const pattern) +{ + if (!regexAvailable) + return; + + int table_index = TABLE_INDEX_UNUSED; + char * regex_pat = NULL; + char *name, *kinds, *flags; + + + if (regptype == REG_PARSER_MULTI_TABLE) { - GRegex* const cp = compileRegex (regex, flags); - if (cp != NULL) - addCompiledCallbackPattern (language, cp, callback, flags, - disabled, userData); + const char *c; + for (c = pattern; *c; c++) + { + 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 addLanguageRegex ( - const langType language CTAGS_ATTR_UNUSED, const char* const regex CTAGS_ATTR_UNUSED) +extern void processTagRegexOption (struct lregexControlBlock *lcb, + enum regexParserType regptype, + const char* const parameter) { - if (regexAvailable) + 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 { - char *const regex_pat = eStrdup (regex); - char *name, *kinds, *flags; - if (parseTagRegex (regex_pat, &name, &kinds, &flags)) + 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 { - addTagRegexInternal (language, regex_pat, name, kinds, flags, - NULL); - eFree (regex_pat); + vString* const regex = vStringNew (); + while (readLineRaw (regex, mio)) + { + if (vStringLength (regex) > 1 && vStringValue (regex)[0] != '\n') + addTagRegexOption (lcb, regptype, vStringValue (regex)); + } + mio_unref (mio); + vStringDelete (regex); } } } @@ -856,233 +2182,592 @@ 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, guestPtrnFlagDef, ARRAY_SIZE (guestPtrnFlagDef)); + 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; -}; +extern void printMultilineRegexFlags (bool withListHeader, bool machinable, FILE *fp) +{ + struct colprintTable * table; + + table = flagsColprintTableNew (); -static void kindCbHelper (void *key, void *value, void* user_data) + flagsColprintAddDefinitions (table, regexFlagDefs, ARRAY_SIZE (regexFlagDefs)); + flagsColprintAddDefinitions (table, multilinePtrnFlagDef, ARRAY_SIZE (multilinePtrnFlagDef)); + flagsColprintAddDefinitions (table, guestPtrnFlagDef, ARRAY_SIZE (guestPtrnFlagDef)); + flagsColprintAddDefinitions (table, commonSpecFlagDef, ARRAY_SIZE (commonSpecFlagDef)); + + flagsColprintTablePrint (table, withListHeader, machinable, fp); + colprintTableDelete(table); +} + +extern void printMultitableRegexFlags (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 (); + + flagsColprintAddDefinitions (table, regexFlagDefs, ARRAY_SIZE (regexFlagDefs)); + flagsColprintAddDefinitions (table, multilinePtrnFlagDef, ARRAY_SIZE (multilinePtrnFlagDef)); + flagsColprintAddDefinitions (table, multitablePtrnFlagDef, ARRAY_SIZE (multitablePtrnFlagDef)); + flagsColprintAddDefinitions (table, guestPtrnFlagDef, ARRAY_SIZE (guestPtrnFlagDef)); + flagsColprintAddDefinitions (table, scopePtrnFlagDef, ARRAY_SIZE (scopePtrnFlagDef)); + flagsColprintAddDefinitions (table, commonSpecFlagDef, ARRAY_SIZE (commonSpecFlagDef)); + + flagsColprintTablePrint (table, withListHeader, machinable, fp); + colprintTableDelete(table); +} + +extern void freeRegexResources (void) +{ + /* TODO: SHOULD BE REMOVED */ +} - helper_data->result = helper_data->func (kind, helper_data->func_data); +extern bool regexNeedsMultilineBuffer (struct lregexControlBlock *lcb) +{ + if (ptrArrayCount(lcb->entries [REG_PARSER_MULTI_LINE]) > 0) + return true; + else if (ptrArrayCount(lcb->tables) > 0) + return true; + else + return false; } -extern void foreachRegexKinds (const langType language, - bool (*func) (kindDefinition *, void *), - void *data) +extern bool matchMultilineRegex (struct lregexControlBlock *lcb, const vString* const allLines) { - initializeParser (language); - if (language <= SetUpper && Sets [language].count > 0) + bool result = false; + + unsigned int i; + + for (i = 0; i < ptrArrayCount(lcb->entries [REG_PARSER_MULTI_LINE]); ++i) { - 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); + 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; } + return result; } - -static bool kind_reset_cb (kindDefinition *kind, void *data) +static int getTableIndexForName (const struct lregexControlBlock *const lcb, const char *name) { - kind->enabled = *(bool *)data; - return false; /* continue */ + unsigned int i; + + for (i = 0; i < ptrArrayCount(lcb->tables); i++) + { + struct regexTable *table = ptrArrayItem(lcb->tables, i); + if (strcmp (table->name, name) == 0) + return (int)i; + } + + return TABLE_INDEX_UNUSED; } -extern void resetRegexKinds (const langType language, bool mode) +extern void addRegexTable (struct lregexControlBlock *lcb, const char *name) { - foreachRegexKinds (language, kind_reset_cb, &mode); + 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; + } + + struct regexTable *table = xCalloc(1, struct regexTable); + table->name = eStrdup (name); + table->entries = ptrArrayNew(deleteTableEntry); + + ptrArrayAdd (lcb->tables, table); } -struct kind_and_mode_and_result +static void dumpSstack(FILE* fp, int scope) { - int kind; - const char *kindLong; - bool mode; - bool result; -}; + tagEntryInfo *entry; + fprintf (fp, "scope : "); + while ((entry = getEntryInCorkQueue (scope))) + { + fprintf(fp, "%s", entry->name); + + scope = entry->extensionFields.scopeIndex; + if (scope != CORK_NIL) + fprintf(fp, "%c", '/'); + } + fprintf (fp, "\n"); +} -static bool enable_kind_cb (kindDefinition *kind, void *data) +static void dumpTstack(FILE* fp, ptrArray *tstack) { - 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))) + for (unsigned int i = ptrArrayCount(tstack); i > 0; i--) { - kind->enabled = kmr->mode; - kmr->result = 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); } - /* continue: - There can be more than one patterns which represents this kind. */ - return false; + fprintf(fp, "\n"); } -extern bool enableRegexKind (const langType language, const int kind, const bool mode) +static void printInputLine(FILE* vfp, const char *c, const off_t offset) { - struct kind_and_mode_and_result kmr; + vString *v = vStringNew (); - kmr.kind = kind; - kmr.kindLong = NULL; - kmr.mode = mode; - kmr.result = false; + for (; *c && (*c != '\n'); c++) + vStringPut(v, *c); - foreachRegexKinds (language, enable_kind_cb, &kmr); - return kmr.result; + if (vStringLength (v) == 0 && *c == '\n') + vStringCatS (v, "\\n"); + + fprintf (vfp, "\ninput : \"%s\" L%lu\n", + vStringValue (v), + getInputLineNumberForFileOffset(offset)); + vStringDelete(v); } -extern bool enableRegexKindLong (const langType language, const char *kindLong, const bool mode) +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_mode_and_result kmr; + vString *msg; + + Assert (ptrn); + Assert (ptrn->message.selection > 0); + Assert (ptrn->message.message_string); - kmr.kind = KIND_NULL; - kmr.kindLong = kindLong; - kmr.mode = mode; - kmr.result = false; + msg = substitute (current, ptrn->message.message_string, BACK_REFERENCE_COUNT, pmatch); - foreachRegexKinds (language, enable_kind_cb, &kmr); - return kmr.result; + 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)); + + vStringDelete (msg); } -struct kind_and_result +static struct regexTable * matchMultitableRegexTable (struct lregexControlBlock *lcb, + struct regexTable *table, const vString *const start, unsigned int *offset) { - int kind; - bool result; -}; + struct regexTable *next = NULL; + const char *current; + regmatch_t pmatch [BACK_REFERENCE_COUNT]; + const char *cstart = vStringValue(start); + unsigned int delta; -static bool is_kind_enabled_cb (kindDefinition *kind, void *data) -{ - bool r = false; - struct kind_and_result *kr = data; - if (kind->letter == kr->kind) + restart: + current = cstart + *offset; + + /* Accept the case *offset == vStringLength(start) + because we want an empty regex // still matches empty input. */ + if (*offset > vStringLength(start)) { - kr->result = kind->enabled; - r = true; + *offset = vStringLength(start); + goto out; } - return r; + BEGIN_VERBOSE(vfp); + { + printInputLine(vfp, current, *offset); + } + END_VERBOSE(); + + for (unsigned int i = 0; i < ptrArrayCount(table->entries); i++) + { + regexTableEntry *entry = ptrArrayItem(table->entries, i); + regexPattern *ptrn = entry->pattern; + struct guestSpec *guest = &ptrn->guest; + + 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); + + if (fillGuestRequest (cstart, current, pmatch, guest, lcb->guest_req)) + { + Assert (lcb->guest_req->lang != LANG_AUTO); + if (isGuestRequestConsistent(lcb->guest_req)) + guestRequestSubmit (lcb->guest_req); + guestRequestClear (lcb->guest_req); + } + + 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++; + } + 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; } -static bool does_kind_exist_cb (kindDefinition *kind, void *data) +extern void extendRegexTable (struct lregexControlBlock *lcb, const char *src, const char *dist) { - bool r = false; - struct kind_and_result *kr = data; - if (kind->letter == kr->kind) + 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++) { - kr->result = true; - r = true; + regexTableEntry *entry = ptrArrayItem (src_table->entries, i); + ptrArrayAdd(dist_table->entries, newRefPatternEntry(entry)); } +} - return r; +extern void printMultitableStatistics (struct lregexControlBlock *lcb) +{ + if (ptrArrayCount(lcb->tables) == 0) + return; + + fprintf(stderr, "\nMTABLE REGEX STATISTICS of %s\n", getLanguageName (lcb->owner)); + fputs("==============================================\n", stderr); + for (unsigned int i = 0; i < ptrArrayCount(lcb->tables); i++) + { + struct regexTable *table = ptrArrayItem (lcb->tables, i); + 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(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', stderr); + } } -extern bool isRegexKindEnabled (const langType language, const int kind) +extern bool matchMultitableRegex (struct lregexControlBlock *lcb, const vString* const allLines) { - struct kind_and_result d; + 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); - d.kind = kind; - d.result = false; + 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: use dumpTstack */ + for (i = ptrArrayCount(lcb->tstack); 0 < i; --i) + { + t = ptrArrayItem (lcb->tstack, i - 1); + error (WARNING, "%3u %s", i - 1, t->name); + } - foreachRegexKinds (language, is_kind_enabled_cb, &d); + break; + } + } - return d.result; + return true; } -extern bool hasRegexKind (const langType language, const int kind) +static int makePromiseForAreaSpecifiedWithOffsets (const char *parser, + off_t startOffset, + off_t endOffset) { - struct kind_and_result d; + unsigned long startLine = getInputLineNumberForFileOffset(startOffset); + unsigned long endLine = getInputLineNumberForFileOffset(endOffset); + unsigned long startLineOffset = getInputFileOffsetForLine (startLine); + unsigned long endLineOffset = getInputFileOffsetForLine (endLine); + + return makePromise (parser, + startLine, startOffset - startLineOffset, + endLine, endOffset - endLineOffset, + startOffset - startLineOffset); +} - d.kind = kind; - d.result = false; +static struct guestRequest *guestRequestNew (void) +{ + struct guestRequest *r = xMalloc (1, struct guestRequest); - foreachRegexKinds (language, does_kind_exist_cb, &d); - return d.result; + guestRequestClear (r); + return r; } -struct printRegexKindCBData{ - const char* const langName; - bool allKindFields; - bool indent; - bool tabSeparated; -}; - -static bool printRegexKind (kindDefinition *kind, void *user_data) +static void guestRequestDelete (struct guestRequest *r) { - struct printRegexKindCBData *data = user_data; - if (kind->letter != KIND_GHOST) - { - 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; + eFree (r); } -extern void printRegexKinds (const langType language, - bool allKindFields, - bool indent, - bool tabSeparated) +static bool guestRequestIsFilled(struct guestRequest *r) { - const char* const langName = getLanguageName (language); - struct printRegexKindCBData data = { - .langName = langName, - .allKindFields = allKindFields, - .indent = indent, - .tabSeparated = tabSeparated, - }; - foreachRegexKinds (language, printRegexKind, &data); + return (r->lang_set && (r->boundary + 0)->offset_set && (r->boundary + 1)->offset_set); } -extern void printRegexFlags (void) +static void guestRequestClear (struct guestRequest *r) { - flagPrintHelp (regexFlagDefs, ARRAY_SIZE (regexFlagDefs)); - flagPrintHelp (prePtrnFlagDef, ARRAY_SIZE (prePtrnFlagDef)); - flagPrintHelp (scopePtrnFlagDef, ARRAY_SIZE (scopePtrnFlagDef)); + r->lang_set = false; + r->boundary[BOUNDARY_START].offset_set = false; + r->boundary[BOUNDARY_END].offset_set = false; } -extern void freeRegexResources (void) +static void guestRequestSubmit (struct guestRequest *r) { - int i; - for (i = 0 ; i <= SetUpper ; ++i) - clearPatternSet (i); - if (Sets != NULL) - eFree (Sets); - Sets = NULL; - SetUpper = -1; + const char *langName = getLanguageName (r->lang); + verbose ("guestRequestSubmit: %s; " + "range: %"PRId64" - %"PRId64"\n", + langName, + (int64_t)r->boundary[BOUNDARY_START].offset, + (int64_t)r->boundary[BOUNDARY_END].offset); + makePromiseForAreaSpecifiedWithOffsets (langName, + r->boundary[BOUNDARY_START].offset, + r->boundary[BOUNDARY_END].offset); } /* 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 000000000..19b2d3569 --- /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 000000000..cd017c7bb --- /dev/null +++ b/ctags/main/lregex_p.h @@ -0,0 +1,77 @@ +/* +* 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 "kind_p.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); + +#endif /* CTAGS_MAIN_LREGEX_PRIVATEH */ diff --git a/ctags/main/lxcmd.c b/ctags/main/lxcmd.c deleted file mode 100644 index 6811b3cf4..000000000 --- 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 c66a310a8..7518fa03e 100644 --- a/ctags/main/lxpath.c +++ b/ctags/main/lxpath.c @@ -12,8 +12,9 @@ #include "debug.h" #include "entry.h" #include "options.h" -#include "parse.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->role == ROLE_INDEX_DEFINITION) - initTagEntry (&tag, (char *)str, spec->kind); + 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_DEFINITION_INDEX) + 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; @@ -95,10 +112,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 @@ -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); @@ -132,7 +149,7 @@ static xmlDocPtr makeXMLDoc (void) { const unsigned char* data; size_t size; - xmlDocPtr doc = NULL; + xmlDocPtr doc; doc = getInputFileUserData (); if (doc) @@ -144,6 +161,7 @@ static xmlDocPtr makeXMLDoc (void) data = getInputFileData (&size); if (data) { + xmlSetGenericErrorFunc (NULL, suppressWarning); xmlLineNumbersDefault (1); doc = xmlParseMemory((const char*)data, size); } @@ -151,21 +169,24 @@ static xmlDocPtr makeXMLDoc (void) return doc; } -extern void findXMLTags (xmlXPathContext *ctx, xmlNode *root, - const tagXpathTableTable *xpathTableTable, - const kindDefinition* const kinds,void *userData) +extern void findXMLTagsFull (xmlXPathContext *ctx, xmlNode *root, + int tableTableIndex, + void (* runAfter) (xmlXPathContext *, xmlNode *, void *), + void *userData) { bool usedAsEntryPoint = false; xmlDocPtr doc = NULL; + const langType lang = getInputLanguage(); + const tagXpathTableTable *xpathTableTable + = getXpathTableTable (lang, tableTableIndex); + if (ctx == NULL) { usedAsEntryPoint = true; findRegexTags (); - xmlSetGenericErrorFunc (NULL, suppressWarning); - doc = makeXMLDoc (); if (doc == NULL) @@ -186,7 +207,9 @@ extern void findXMLTags (xmlXPathContext *ctx, xmlNode *root, } } - findXMLTagsCore (ctx, root, xpathTableTable, kinds, userData); + findXMLTagsCore (ctx, root, xpathTableTable, userData); + if (runAfter) + (* runAfter) (ctx, root, userData); out: if (usedAsEntryPoint) @@ -205,10 +228,22 @@ extern void addTagXpath (const langType language, tagXpathTable *xpathTable) xpathTable->xpathCompiled = NULL; } -extern void findXMLTags (xmlXPathContext *ctx, xmlNode *root, - const tagXpathTableTable *xpathTableTable, - const kindDefinition* const kinds, void *userData) +extern void removeTagXpath (const langType language CTAGS_ATTR_UNUSED, tagXpathTable *xpathTable CTAGS_ATTR_UNUSED) +{ +} + +extern void findXMLTagsFull (xmlXPathContext *ctx, xmlNode *root, + int tableTableIndex, + void (* runAfter) (xmlXPathContext *, xmlNode *, void *), + void *userData) { } #endif + +extern void findXMLTags (xmlXPathContext *ctx, xmlNode *root, + int tableTableIndex, + void *userData) +{ + findXMLTagsFull (ctx, root, tableTableIndex, NULL, userData); +} diff --git a/ctags/main/lxpath.h b/ctags/main/lxpath.h new file mode 100644 index 000000000..223be19f7 --- /dev/null +++ b/ctags/main/lxpath.h @@ -0,0 +1,110 @@ +/* +* 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 FILES +*/ + +#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 + + +/* +* 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); + + 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; + + +/* +* FUNCTION PROTOTYPES +*/ + +/* Xpath interface */ +extern void findXMLTagsFull (xmlXPathContext *ctx, xmlNode *root, + int tableTableIndex, + void (* runAfter) (xmlXPathContext *, xmlNode *, void *), + void *userData); + +extern void findXMLTags (xmlXPathContext *ctx, xmlNode *root, + 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 000000000..9bb73dafc --- /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 0458952cc..a9f66f2b1 100644 --- a/ctags/main/main.c +++ b/ctags/main/main.c @@ -28,21 +28,9 @@ #include #endif +#include #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 +#include /* To provide directory searching for recursion feature. */ @@ -56,74 +44,50 @@ #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 "error.h" -#include "field.h" -#include "keyword.h" -#include "main.h" -#include "options.h" -#include "output.h" -#include "read.h" -#include "routines.h" - -/* -* MACROS -*/ -#define plural(value) (((unsigned long)(value) == 1L) ? "" : "s") +#include "entry_p.h" +#include "error_p.h" +#include "field_p.h" +#include "keyword_p.h" +#include "main_p.h" +#include "options_p.h" +#include "parse_p.h" +#include "read_p.h" +#include "routines_p.h" +#include "stats_p.h" +#include "trace.h" +#include "trashbox_p.h" +#include "writer_p.h" +#include "xtag_p.h" + +#ifdef HAVE_JANSSON +#include "interactive_p.h" +#include +#include +#endif /* * DATA DEFINITIONS */ -static struct { long files, lines, bytes; } Totals = { 0, 0, 0 }; -#ifndef CTAGS_LIB static mainLoopFunc mainLoop; static void *mainData; -#endif /* * FUNCTION PROTOTYPES */ -#ifndef CTAGS_LIB static bool createTagsForEntry (const char *const entryName); -#endif /* * 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 (outpuFormatUsedStdoutByDefault() || Option.filter || - (Option.tagFileName != NULL && (strcmp (Option.tagFileName, "-") == 0 - || strcmp (Option.tagFileName, "/dev/stdout") == 0 - ))) - toStdout = true; - return toStdout; -} - -#ifndef CTAGS_LIB - -#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; @@ -143,10 +107,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 +121,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 +144,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) @@ -225,9 +180,9 @@ 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) || defined (HAVE__FINDFIRST) +#elif defined (HAVE__FINDFIRST) { vString *const pattern = vStringNew (); vStringCopyS (pattern, dirName); @@ -250,8 +205,8 @@ static bool createTagsForEntry (const char *const entryName) fileStatus *status = eStat (entryName); Assert (entryName != NULL); - if (isExcludedFile (entryName)) - verbose ("excluding \"%s\"\n", entryName); + if (isExcludedFile (entryName, true)) + verbose ("excluding \"%s\" (the early stage)\n", entryName); else if (status->isSymbolicLink && ! Option.followLinks) verbose ("ignoring \"%s\" (symbolic link)\n", entryName); else if (! status->exists) @@ -260,6 +215,8 @@ static bool createTagsForEntry (const char *const entryName) resize = recurseIntoDirectory (entryName); else if (! status->isNormalFile) verbose ("ignoring \"%s\" (special file)\n", entryName); + else if (isExcludedFile (entryName, false)) + verbose ("excluding \"%s\"\n", entryName); else resize = parseFile (entryName); @@ -275,9 +232,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)) @@ -359,69 +316,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); @@ -486,10 +380,113 @@ static void batchMakeTags (cookedArgs *args, void *user CTAGS_ATTR_UNUSED) timeStamp (2); if (Option.printTotals) - printTotals (timeStamps); + { + printTotals (timeStamps, Option.append, Option.sorted); + if (Option.printTotals > 1) + for (unsigned int i = 0; i < countParsers(); i++) + printParserStatisticsIfUsed (i); + } + #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"); + closeTagFile (false); + 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, eFreeNoNullCheck); + parseFileWithMio (filename, mio, NULL); + mio_unref (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 +505,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) @@ -547,24 +548,34 @@ static void sanitizeEnviron (void) * Start up code */ -extern int main (int argc CTAGS_ATTR_UNUSED, char **argv) +extern int ctags_cli_main (int argc CTAGS_ATTR_UNUSED, char **argv) { cookedArgs *args; +#if defined(WIN32) && defined(HAVE_MKSTEMP) + /* MinGW-w64's mkstemp() uses rand() for generating temporary files. */ + srand ((unsigned int) clock ()); +#endif + + initDefaultTrashBox (); + + DEBUG_INIT(); + setErrorPrinter (stderrDefaultErrorPrinter, NULL); setMainLoop (batchMakeTags, NULL); - setTagWriter (&ctagsWriter); + setTagWriter (WRITER_U_CTAGS, NULL); setCurrentDirectory (); setExecutableName (*argv++); sanitizeEnviron (); checkRegex (); - initFieldDescs (); + initFieldObjects (); + initXtagObjects (); args = cArgNewFromArgv (argv); previewFirstOption (args); - testEtagsInvocation (); initializeParsing (); + testEtagsInvocation (); initOptions (); readOptionConfiguration (); verbose ("Reading initial options from command line\n"); @@ -583,15 +594,15 @@ 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; exit (0); return 0; } -#endif diff --git a/ctags/main/main.h b/ctags/main/main.h deleted file mode 100644 index da29e089a..000000000 --- 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 000000000..3698da623 --- /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 000000000..902821a4e --- /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 dissimilarity index 100% index cb8848cc1..63b86500a 100644 --- a/ctags/main/mbcs.h +++ b/ctags/main/mbcs.h @@ -1 +1,28 @@ -/* 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. +*/ +#ifndef CTAGS_MAIN_MBCS_H +#define CTAGS_MAIN_MBCS_H + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ + +#ifdef HAVE_ICONV + +/* +* FUNCTION PROTOTYPES +*/ +extern bool isConverting (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 000000000..c31b1cf0c --- /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 000000000..8dc324e30 --- /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 1e924cdb4..0268e6875 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; @@ -95,10 +81,11 @@ static void eFree (void *const ptr) { free (ptr); } +#define eFreeNoNullCheck eFree # 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 +105,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 +176,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 +227,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 +252,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 +309,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 +357,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() * */ @@ -404,7 +391,7 @@ MIO *mio_new_mio (MIO *base, long start, long size) if (r != size) goto cleanup; - submio = mio_new_memory (data, size, eRealloc, eFree); + submio = mio_new_memory (data, size, eRealloc, eFreeNoNullCheck); if (! submio) goto cleanup; @@ -436,7 +423,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 +448,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 +469,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 +477,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; @@ -662,6 +649,11 @@ static int mem_try_resize (MIO *mio, size_t new_size) return success; } +int mio_try_resize (MIO *mio, size_t new_size) +{ + return mem_try_resize (mio, new_size); +} + /* * mem_try_ensure_space: * @mio: A #MIO object @@ -822,7 +814,7 @@ int mio_vprintf (MIO *mio, const char *format, va_list ap) old_size = mio->impl.mem.size; va_copy (ap_copy, ap); /* compute the size we will need into the buffer */ - n = vsnprintf (&dummy, 1, format, ap_copy); + n = vsnprintf (&dummy, 1, format, ap_copy) + 1; va_end (ap_copy); if (mem_try_ensure_space (mio, n)) { diff --git a/ctags/main/mio.h b/ctags/main/mio.h index b9186f788..de375cd28 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, @@ -159,4 +159,6 @@ int mio_flush (MIO *mio); void mio_attach_user_data (MIO *mio, void *user_data, MIODestroyNotify user_data_free_func); void *mio_get_user_data (MIO *mio); +int mio_try_resize (MIO *mio, size_t new_size); + #endif /* MIO_H */ diff --git a/ctags/main/nestlevel.c b/ctags/main/nestlevel.c index 1f777a28b..d3403f78f 100644 --- a/ctags/main/nestlevel.c +++ b/ctags/main/nestlevel.c @@ -13,11 +13,13 @@ */ #include "general.h" /* must always come first */ -#include "main.h" #include "debug.h" +#include "entry.h" #include "routines.h" #include "nestlevel.h" +#include + /* TODO: Alignment */ #define NL_SIZE(nls) (sizeof(NestingLevel) + (nls)->userDataSize) #define NL_NTH(nls,n) (NestingLevel *)(((char *)((nls)->levels)) + ((n) * NL_SIZE (nls))) @@ -26,21 +28,30 @@ * FUNCTION DEFINITIONS */ -extern NestingLevels *nestingLevelsNew(size_t userDataSize) +extern NestingLevels *nestingLevelsNewFull(size_t userDataSize, + void (* deleteUserData)(NestingLevel *)) { NestingLevels *nls = xCalloc (1, NestingLevels); nls->userDataSize = userDataSize; + nls->deleteUserData = deleteUserData; return nls; } +extern NestingLevels *nestingLevelsNew(size_t userDataSize) +{ + return nestingLevelsNewFull (userDataSize, NULL); +} + extern void nestingLevelsFree(NestingLevels *nls) { int i; NestingLevel *nl; - for (i = 0; i < nls->allocated; i++) + for (i = 0; i < nls->n; i++) { nl = NL_NTH(nls, i); + if (nls->deleteUserData) + nls->deleteUserData (nl); nl->corkIndex = CORK_NIL; } if (nls->levels) eFree(nls->levels); @@ -61,6 +72,9 @@ extern NestingLevel * nestingLevelsPush(NestingLevels *nls, int corkIndex) nls->n++; nl->corkIndex = corkIndex; + if (nls->userDataSize > 0) + memset (nl->userData, 0, nls->userDataSize); + return nl; } @@ -80,27 +94,25 @@ extern void nestingLevelsPop(NestingLevels *nls) NestingLevel *nl = nestingLevelsGetCurrent(nls); Assert (nl != NULL); + if (nls->deleteUserData) + nls->deleteUserData (nl); nl->corkIndex = CORK_NIL; nls->n--; } -extern NestingLevel *nestingLevelsGetCurrent(const NestingLevels *nls) +extern NestingLevel *nestingLevelsGetNthFromRoot (const NestingLevels *nls, int n) { Assert (nls != NULL); - - if (nls->n < 1) + if (nls->n > n && n >= 0) + return NL_NTH(nls, n); + else return NULL; - - return NL_NTH(nls, (nls->n - 1)); } -extern NestingLevel *nestingLevelsGetNth(const NestingLevels *nls, int n) +extern NestingLevel *nestingLevelsGetNthParent (const NestingLevels *nls, int n) { Assert (nls != NULL); - if (nls->n > n && n >= 0) - return NL_NTH(nls, n); - else - return NULL; + return nestingLevelsGetNthFromRoot (nls, nls->n - 1 - n); } extern void *nestingLevelGetUserData (const NestingLevel *nl) diff --git a/ctags/main/nestlevel.h b/ctags/main/nestlevel.h index a209ae706..18ac9927e 100644 --- a/ctags/main/nestlevel.h +++ b/ctags/main/nestlevel.h @@ -35,18 +35,22 @@ struct NestingLevels int n; /* number of levels in use */ int allocated; size_t userDataSize; + void (* deleteUserData) (NestingLevel *); }; /* * FUNCTION PROTOTYPES */ extern NestingLevels *nestingLevelsNew(size_t userDataSize); +extern NestingLevels *nestingLevelsNewFull(size_t userDataSize, + void (* deleteUserData)(NestingLevel *)); extern void nestingLevelsFree(NestingLevels *nls); extern NestingLevel *nestingLevelsPush(NestingLevels *nls, int corkIndex); extern NestingLevel * nestingLevelsTruncate(NestingLevels *nls, int depth, int corkIndex); extern void nestingLevelsPop(NestingLevels *nls); -extern NestingLevel *nestingLevelsGetCurrent(const NestingLevels *nls); -extern NestingLevel *nestingLevelsGetNth(const NestingLevels *nls, int n); +#define nestingLevelsGetCurrent(NLS) nestingLevelsGetNthParent((NLS), 0) +extern NestingLevel *nestingLevelsGetNthFromRoot(const NestingLevels *nls, int n); +extern NestingLevel *nestingLevelsGetNthParent(const NestingLevels *nls, int n); extern void *nestingLevelGetUserData (const NestingLevel *nl); diff --git a/ctags/main/numarray.c b/ctags/main/numarray.c index b6c8619cc..6b33a6dc1 100644 --- a/ctags/main/numarray.c +++ b/ctags/main/numarray.c @@ -36,7 +36,7 @@ return result; \ } \ \ - extern void prefix##ArrayAdd (prefix##Array *const current, type num) \ + extern unsigned int prefix##ArrayAdd (prefix##Array *const current, type num) \ { \ Assert (current != NULL); \ if (current->count == current->max) \ @@ -44,7 +44,8 @@ current->max *= 2; \ current->array = xRealloc (current->array, current->max, type); \ } \ - current->array [current->count++] = num; \ + current->array [current->count] = num; \ + return current->count++; \ } \ \ extern void prefix##ArrayRemoveLast (prefix##Array *const current) \ @@ -167,9 +168,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 318931aec..b50e9d8d4 100644 --- a/ctags/main/numarray.h +++ b/ctags/main/numarray.h @@ -21,7 +21,7 @@ typedef struct s##Prefix##Array prefix##Array; \ \ extern prefix##Array *prefix##ArrayNew (void); \ - extern void prefix##ArrayAdd (prefix##Array *const current, type num); \ + extern unsigned int prefix##ArrayAdd (prefix##Array *const current, type num); \ extern void prefix##ArrayRemoveLast (prefix##Array *const current); \ extern void prefix##ArrayCombine (prefix##Array *const current, prefix##Array *const from); \ extern void prefix##ArrayClear (prefix##Array *const current); \ @@ -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/objpool.c b/ctags/main/objpool.c index dbe2b7504..72c9d442b 100644 --- a/ctags/main/objpool.c +++ b/ctags/main/objpool.c @@ -13,6 +13,7 @@ */ #include "general.h" /* must always come first */ +#include "debug.h" #include "routines.h" #include "objpool.h" @@ -75,7 +76,12 @@ extern void objPoolPut (objPool *pool, void *obj) if (obj == NULL) return; - if (ptrArrayCount (pool->array) < pool->size) + if ( +#ifdef DISABLE_OBJPOOL + 0 && +#endif + ptrArrayCount (pool->array) < pool->size + ) ptrArrayAdd (pool->array, obj); else pool->deleteFunc (obj); diff --git a/ctags/main/options.c b/ctags/main/options.c index 73cb3b2f8..94732a361 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,34 @@ #include #include /* to declare isspace () */ -#if defined(HAVE_SCANDIR) -#include -#endif - #include "ctags.h" #include "debug.h" -#include "field.h" -#include "main.h" -#define OPTION_WRITE -#include "options.h" -#include "output.h" -#include "parse.h" -#include "ptag.h" -#include "routines.h" -#include "xtag.h" -#include "routines.h" +#include "entry_p.h" +#include "field_p.h" +#include "gvars.h" +#include "keyword_p.h" +#include "parse_p.h" +#include "ptag_p.h" +#include "routines_p.h" +#include "xtag_p.h" +#include "param_p.h" +#include "error_p.h" +#include "interactive_p.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 +66,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,101 +110,94 @@ 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 stringList *Excluded, *ExcludedException; static bool FilesRequired = true; 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 */ -#endif + EX_MIX #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 = TREL_NO, + .printTotals = 0, + .lineDirectives = false, + .printLanguage =false, + .guessLanguageEagerly = false, + .quiet = false, + .fatalWarnings = false, .patternLengthLimit = 96, .putFieldPrefix = false, .maxRecursionDepth = 0xffffffff, - .machinable = false, - .withListHeader = true, + .interactive = false, +#ifdef WIN32 + .useSlashAsFilenameSeparator = FILENAME_SEP_UNSET, +#endif #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," -? Print this option summary."}, {1," -a Append the tags to an existing tag file."}, #ifdef DEBUG {1," -b "}, @@ -211,9 +205,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"}, @@ -241,34 +237,34 @@ static optionDescription LongOptionDescription [] = { {1," -V Equivalent to --verbose."}, {1," -x Print a tabular cross reference file to standard output."}, {1," --alias-=[+|-]aliasPattern"}, - {1," Add a pattern detecting a name, can be used as an alternative name"}, - {1," for LANG."}, + {1," Add a pattern detecting a name, can be used as an alternative name"}, + {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," 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"}, + {1," Exclude files and directories matching 'pattern'."}, + {1," See also --exclude-exception option."}, + {1," --exclude-exception=pattern"}, + {1," Don't exclude files and directories matching 'pattern' even if"}, + {1," they match the pattern specified with --exclude option."}, + {0," --excmd=number|pattern|mix|combine"}, #ifdef MACROS_USE_PATTERNS {0," Uses the specified type of EX command to locate tags [pattern]."}, #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," --fields-=[+|-]flags"}, - {1," Include selected own extension fields"}, - {1," (flags: --list-fields=)."}, - {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]?"}, + {1," Include selected extension fields (flags: \"aCeEfFikKlmnNpPrRsStxzZ\") [fks]."}, + {1," --fields-=[+|-]flags"}, + {1," Include selected own extension fields"}, + {1," (flags: see the output of --list-fields= option)."}, {1," --filter=[yes|no]"}, {1," Behave as a filter, reading file names from standard input and"}, {1," writing tags to standard output [no]."}, @@ -289,16 +285,19 @@ 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 {1," --input-encoding=encoding"}, - {1," Specify encoding of all input files."}, + {1," Specify encoding of all input files."}, {1," --input-encoding-=encoding"}, - {1," Specify encoding of the LANG input files."}, + {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 ."}, {1," --langdef=name"}, {1," Define a new language to be parsed with regular expressions."}, @@ -310,8 +309,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 +317,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."}, + {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"}, @@ -396,40 +412,87 @@ static optionDescription LongOptionDescription [] = { #endif {1," --regex-=/line_pattern/name_pattern/[flags]"}, {1," Define regular expression for locating tags in specific language."}, + {1," --roles-.kind=[+|-]role, or"}, + {1," Enable/disable tag roles for kinds of 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]?"}, - {1," --totals=[yes|no]"}, + {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|extra]"}, {1," Print statistics about input and tag files [no]."}, +#ifdef WIN32 + {1," --use-slash-as-filename-separator=[yes|no]"}, + {1," Use slash as filename separator [yes] for u-ctags output format."}, +#endif {1," --verbose=[yes|no]"}, {1," Enable verbose messages describing actions on each input file."}, {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 . \"--extras-=+{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," --_pretend-="}, + {1," Make NEWLANG parser pretend OLDLANG parser in lang: field."}, + {1," --_roledef-.kind=role_name,role_desc"}, + {1," Define new role for the kind 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 + {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 +519,59 @@ 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", +#if defined (WIN32) + {"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_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_PACKCC + /* The test harnesses use this as hints for skipping test cases */ + {"packcc", "has peg based parser(s)"}, +#endif + {NULL,} }; static const char *const StageDescription [] = { @@ -501,7 +580,8 @@ static const char *const StageDescription [] = { [OptionLoadingStageDosCnf] = "DOS .cnf file", [OptionLoadingStageEtc] = "file under /etc (e.g. ctags.conf)", [OptionLoadingStageLocalEtc] = "file under /usr/local/etc (e.g. ctags.conf)", - [OptionLoadingStageHomeRecursive] = "file(s) under HOME", + [OptionLoadingStageXdg] = "file(s) under $XDG_CONFIG_HOME and $HOME/.config", + [OptionLoadingStageHomeRecursive] = "file(s) under $HOME", [OptionLoadingStageCurrentRecursive] = "file(s) under the current directory", [OptionLoadingStagePreload] = "optlib preload files", [OptionLoadingStageEnvVar] = "environment variable", @@ -514,40 +594,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 +654,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 +693,6 @@ static void freeString (char **const pString) *pString = NULL; } } -#endif extern void freeList (stringList** const pList) { @@ -651,15 +703,20 @@ 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) @@ -694,47 +751,67 @@ 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); } + writerCheckOptions (); } -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 len; + size_t prefix_len; langType language; const char *lang; + char *sep = 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); + /* Extract from + * --param-:=..., and + * --_roledef-.=... */ + sep = strpbrk (lang, ":."); + if (sep) + lang_len = sep - lang; + language = getNamedLanguageFull (lang, lang_len, noPretending); if (language == LANG_IGNORE) - error (FATAL, "Unknown language \"%s\" in \"%s\" option", lang, option); + { + const char *langName = (lang_len == 0)? lang: eStrndup (lang, lang_len); + error (FATAL, "Unknown language \"%s\" in \"%s\" option", langName, 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 = true; - setTagWriter (&etagsWriter); + Option.tagRelative = TREL_YES; + enableLanguage (LANG_FALLBACK, true); + setTagWriter (WRITER_ETAGS, NULL); } extern void testEtagsInvocation (void) @@ -757,14 +834,16 @@ extern void testEtagsInvocation (void) static void setXrefMode (void) { Option.xref = true; - setTagWriter (&xrefWriter); + setTagWriter (WRITER_XREF, NULL); } #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, NULL); } #endif @@ -997,7 +1076,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 +1086,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 +1124,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) { @@ -1048,43 +1139,77 @@ static void processEtagsInclude ( } } -static void processExcludeOption ( - const char *const option CTAGS_ATTR_UNUSED, const char *const parameter) +static void processExcludeOptionCommon ( + stringList** list, const char *const optname, const char *const parameter) { const char *const fileName = parameter + 1; if (parameter [0] == '\0') - freeList (&Excluded); + freeList (list); else if (parameter [0] == '@') { stringList* const sl = stringListNewFromFile (fileName); if (sl == NULL) error (FATAL | PERROR, "cannot open \"%s\"", fileName); - if (Excluded == NULL) - Excluded = sl; + if (*list == NULL) + *list = sl; else - stringListCombine (Excluded, sl); - verbose (" adding exclude patterns from %s\n", fileName); + stringListCombine (*list, sl); + verbose (" adding %s patterns from %s\n", optname, fileName); } else { vString *const item = vStringNewInit (parameter); - if (Excluded == NULL) - Excluded = stringListNew (); - stringListAdd (Excluded, item); - verbose (" adding exclude pattern: %s\n", parameter); +#if defined (WIN32) + vStringTranslate(item, PATH_SEPARATOR, OUTPUT_PATH_SEPARATOR); +#endif + if (*list == NULL) + *list = stringListNew (); + stringListAdd (*list, item); + verbose (" adding %s pattern: %s\n", optname, parameter); } } -extern bool isExcludedFile (const char* const name) +static void processExcludeOption ( + const char *const option, const char *const parameter) +{ + processExcludeOptionCommon (&Excluded, option, parameter); +} + +static void processExcludeExceptionOption ( + const char *const option, const char *const parameter) +{ + processExcludeOptionCommon (&ExcludedException, option, parameter); +} + +extern bool isExcludedFile (const char* const name, + bool falseIfExceptionsAreDefeind) { const char* base = baseFilename (name); bool result = false; + + if (falseIfExceptionsAreDefeind + && ExcludedException != NULL + && stringListCount (ExcludedException) > 0) + return false; + if (Excluded != NULL) { result = stringListFileMatched (Excluded, base); if (! result && name != base) result = stringListFileMatched (Excluded, name); } + + if (result && ExcludedException != NULL) + { + bool result_exception; + + result_exception = stringListFileMatched (ExcludedException, base); + if (! result_exception && name != base) + result_exception = stringListFileMatched (ExcludedException, name); + + if (result_exception) + result = false; + } return result; } @@ -1097,16 +1222,20 @@ 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: %s", option, parameter); break; } } -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 +1249,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 +1291,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 +1339,7 @@ static void processFieldsOption ( static vString * longName; bool inLongName = false; - longName = vStringNewOrClear (longName); + longName = vStringNewOrClearWithAutoRelease (longName); if (*p == '*') { @@ -1250,7 +1382,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 +1426,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 +1439,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 +1463,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 +1514,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 +1550,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 +1598,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, NULL); + 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 +1828,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 +1869,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 +1914,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 +1991,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 +2029,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 +2168,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 +2176,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 +2216,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,37 +2236,61 @@ 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 + /* Handle the case if no language is specified. + * This case is not documented. */ || strncmp (parameter, ".", 1) == 0) lang = LANG_AUTO; else { lang = getNamedLanguage (parameter, sep - parameter); if (lang == LANG_IGNORE) - error (FATAL, "Unknown language \"%s\" in \"%s\"", parameter, option); + { + const char *langName = eStrndup (parameter, sep - parameter); + error (FATAL, "Unknown language \"%s\" in \"%s\"", langName, option); + } } - printLanguageRoles (lang, kindletters); + printLanguageRoles (lang, kindspecs, + localOption.withListHeader, + localOption.machinable, + stdout); exit (0); } -static void freeSearchPathList (searchPathList** pathList) + +static void processListSubparsersOptions (const char *const option CTAGS_ATTR_UNUSED, + const char *const parameter) { - stringListClear (*pathList); - stringListDelete (*pathList); - *pathList = NULL; + 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 verboseSearchPathList (const searchPathList* pathList, const char *const varname) +static void freeSearchPathList (searchPathList** pathList) { - unsigned int i; - - verbose ("Install %s:\n", varname); - for (i = 0; i < stringListCount (pathList); ++i) - verbose (" %s\n", vStringValue (stringListItem (pathList, i))); + stringListClear (*pathList); + stringListDelete (*pathList); + *pathList = NULL; } static vString* expandOnSearchPathList (searchPathList *pathList, const char* leaf, @@ -1914,9 +2298,9 @@ static vString* expandOnSearchPathList (searchPathList *pathList, const char* le { 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,61 +2314,16 @@ static vString* expandOnSearchPathList (searchPathList *pathList, const char* le return NULL; } -static bool isDirectory (const char *const dirName) -{ - fileStatus *status = eStat (dirName); - return status->exists && status->isDirectory; -} - static vString* expandOnOptlibPathList (const char* leaf) { - vString* r; - vString* leaf_with_suffix; - - leaf_with_suffix = vStringNewInit (leaf); - vStringCatS (leaf_with_suffix, ".d"); - - 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); + return expandOnSearchPathList (OptlibPathList, leaf, + doesFileExist); } -static void processOptionFile ( - const char *const option, const char *const parameter) +static void processOptionFileCommon ( + const char *const option, const char *const parameter, + bool allowNonExistingFile) { const char* path; vString* vpath = NULL; @@ -2004,7 +2343,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 +2362,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, NULL); else if (strcmp (parameter, "etags") == 0) setEtagsMode (); else if (strcmp (parameter, "xref") == 0) @@ -2046,43 +2400,70 @@ static void processPseudoTags (const char *const option CTAGS_ATTR_UNUSED, const char *const parameter) { const char *p = parameter; - bool s; + bool s = true; ptagType t; + vString *str = vStringNew(); - if (*p == '*') + if (*p == '\0' || !strchr ("*+-", *p)) { - int i; - for (i = 0; i < PTAG_COUNT; i++) - enablePtag (i, true); - return; + for (unsigned int i = 0; i < PTAG_COUNT; i++) + enablePtag (i, false); } - if (*p == '-') - { - s= false; - p++; - } - else if (*p == '+') - { - s = true; - p++; - } - else + while (1) { - unsigned int i; - - s = true; - for (i = 0; i < PTAG_COUNT; i++) - enablePtag (i, false); if (*p == '\0') - return; - } + break; + + if (*p == '*') + { + int i; + for (i = 0; i < PTAG_COUNT; i++) + enablePtag (i, true); + p++; + continue; + } + else if (*p == '-') + { + s= false; + p++; + continue; + } + else if (*p == '+') + { + s = true; + p++; + continue; + } + else if (*p == '{') + { + const char *origin = p; - t = getPtagTypeForName (p); - if (t == PTAG_UNKNOWN) - error (FATAL, "Unknown pseudo tag name: %s", p); + p++; + while (*p != '\0' && *p != '}') + { + vStringPut (str, *p); + p++; + } + if (*p != '}') + error (FATAL, "curly bracket specifying a pseudo tags is unbalanced: %s", + origin); + p++; + } + else + { + vStringCopyS (str, p); + p += vStringLength (str); + } - enablePtag (t, s); + char *name = vStringValue (str); + t = getPtagTypeForName (name); + if (t == PTAG_UNKNOWN) + error (FATAL, "Unknown pseudo tag name: %s", name); + enablePtag (t, s); + vStringClear (str); + } + vStringDelete (str); } static void processSortOption ( @@ -2100,6 +2481,34 @@ 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) || *parameter == '\0') + 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 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); @@ -2141,164 +2550,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 +2566,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 +2578,19 @@ static void addIgnoreListFromFile (const char *const fileName) for(i=0;ipatternLengthLimit) >= 0) + return writePseudoTag (pdesc, buf, "0 for no limit", NULL); + return false; +} + +static void setBooleanToXtagWithWarning(booleanOption *const option, bool value) { /* WARNING/TODO: This function breaks capsulization. */ - xtagType t = (xtagType)option->pValue; - bool default_value = isXtagEnabled (t); - enableXtag (t, default_value); + char x = 0; + + if (strcmp (option->name, "file-tags") == 0) + x = 'f'; + else if (strcmp (option->name, "file-scope") == 0) + x = 'F'; - return &(getXtagDesc (t)->enabled); + if (x) + error (WARNING, "\"--%s\" option is obsolete; use \"--extras=%c%c\" instead", + option->name, value? '+': '-', x); + + xtagType t = (xtagType)option->pValue; + 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 }, + { "exclude-exception", processExcludeExceptionOption, 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,55 +2823,73 @@ 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 }, + { "totals", processTotals, 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, setBooleanToXtagWithWarning }, + { "file-tags", ((bool *)XTAG_FILE_NAMES), false, STAGE_ANY, setBooleanToXtagWithWarning }, { "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 }, +#ifdef WIN32 + { "use-slash-as-filename-separator", (bool *)&Option.useSlashAsFilenameSeparator, false, STAGE_ANY }, +#endif + { "with-list-header", &localOption.withListHeader, true, STAGE_ANY }, { "_fatal-warnings",&Option.fatalWarnings, false, STAGE_ANY }, }; @@ -2641,18 +2933,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 +2946,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,38 +2957,157 @@ 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 + *entry->pValue = value; + } + } + return found; +} + +static void enableLanguageField (langType language, const char *field, bool mode) +{ + + fieldType t; + + t = getFieldTypeForNameAndLanguage (field, language); + if (t == FIELD_UNKNOWN) + error(FATAL, "no such field: \'%s\'", field); + enableField (t, mode, (language != LANG_AUTO)); + if (language == LANG_AUTO) + { + fieldType ftype_next = t; + + while ((ftype_next = nextSiblingField (ftype_next)) != FIELD_UNKNOWN) + enableField (ftype_next, mode, (language != LANG_AUTO)); + } +} + +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) +{ +#define PREFIX "fields-" +#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 *f; + bool inLongName = false; + + 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 == '*') + { + resetFieldsOption (language, true); + 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 = 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 field specification: \'%c\'", + c); + inLongName = true; + break; + case '}': + if (!inLongName) + error (FATAL, + "unexpected character in field specification: \'%c\'", + c); + + f = vStringValue (longName); + enableLanguageField (language, f, mode); + inLongName = false; + vStringClear (longName); + break; + default: + if (inLongName) + vStringPut (longName, c); else - slot = entry->pValue; - *slot = getBooleanOption (option, parameter); + error (FATAL, + "only long name can be used in per language field spec: \'%c\'", + c); + break; } } - return found; -} - -static void enableLanguageField (langType language, const char *field, bool mode) -{ - - fieldType t; - - t = getFieldTypeForNameAndLanguage (field, language); - if (t == FIELD_UNKNOWN) - error(FATAL, "no such field: \'%s\'", field); - enableField (t, mode, (language != LANG_AUTO)); - if (language == LANG_AUTO) - { - fieldType ftype_next = t; - - while ((ftype_next = nextSiblingField (ftype_next)) != FIELD_UNKNOWN) - enableField (ftype_next, mode, (language != LANG_AUTO)); - } +#undef PREFIX_LEN +#undef PREFIX + return true; } -static bool processLangSpecificFieldsOption (const char *const option, +static bool processLangSpecificExtraOption (const char *const option, const char *const parameter) { -#define PREFIX "fields-" +#define PREFIX "extras-" #define PREFIX_LEN strlen(PREFIX) const char* lang; size_t len; @@ -2717,7 +3116,7 @@ static bool processLangSpecificFieldsOption (const char *const option, int c; static vString * longName; bool mode = true; - const char *f; + const char *x; bool inLongName = false; if ( strncmp (option, PREFIX, PREFIX_LEN) != 0 ) @@ -2725,9 +3124,10 @@ static bool processLangSpecificFieldsOption (const char *const option, lang = option + PREFIX_LEN; 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 +3135,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; } @@ -2743,15 +3143,19 @@ static bool processLangSpecificFieldsOption (const char *const option, if (*p == '*') { - resetFieldsOption (language, true); + resetXtags (language, true); p++; } - else if (*p == '{') - resetFieldsOption (language, false); + else if (*p == '{' || *p == '\0') + { + resetXtags (language, false); + if (*p == '\0') + return true; + } else if (*p != '+' && *p != '-') - error (WARNING, "Wrong per language field specification: %s", p); + error (WARNING, "Wrong per language extra specification: %s", p); - longName = vStringNewOrClear (longName); + longName = vStringNewOrClearWithAutoRelease (longName); while ((c = *p++) != '\0') { switch (c) @@ -2771,18 +3175,18 @@ static bool processLangSpecificFieldsOption (const char *const option, case '{': if (inLongName) error (FATAL, - "unexpected character in field specification: \'%c\'", + "unexpected character in extra specification: \'%c\'", c); inLongName = true; break; case '}': if (!inLongName) error (FATAL, - "unexpected character in field specification: \'%c\'", + "unexpected character in extra specification: \'%c\'", c); - f = vStringValue (longName); - enableLanguageField (language, f, mode); + x = vStringValue (longName); + enableLanguageXtag (language, x, mode); inLongName = false; vStringClear (longName); break; @@ -2791,7 +3195,7 @@ static bool processLangSpecificFieldsOption (const char *const option, vStringPut (longName, c); else error (FATAL, - "only long name can be used in per language field spec: \'%c\'", + "only long name can be used in per language extra spec: \'%c\'", c); break; } @@ -2799,6 +3203,62 @@ static bool processLangSpecificFieldsOption (const char *const option, 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) { @@ -2814,18 +3274,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 +3310,14 @@ 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 if (processScopesepOption (option, parameter)) + ; + else if (processPretendOption (option, parameter)) + ; + else if (processRolesOption (option, parameter)) + ; else error (FATAL, "Unknown option: --%s", option); } @@ -2864,17 +3348,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 +3390,7 @@ static void processShortOption ( processHeaderListOption (*option, parameter); break; case 'I': - processIgnoreOption (parameter); + processIgnoreOption (parameter, *option); break; case 'L': if (Option.fileList != NULL) @@ -2933,7 +3420,7 @@ static void processShortOption ( Option.sorted = SO_UNSORTED; break; case 'V': - Option.verbose = true; + ctags_verbose = true; break; case 'w': /* silently ignored */ @@ -3023,11 +3510,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,6 +3524,11 @@ extern void previewFirstOption (cookedArgs* const args) } } +#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); @@ -3045,27 +3536,6 @@ static void parseConfigurationFileOptionsInDirectoryWithLeafname (const char* di 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) static int ignore_dot_file(const struct dirent* dent) { /* Ignore a file which name is started from dot. */ @@ -3075,30 +3545,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 +3560,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 +3589,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 +3607,158 @@ 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 && strlen (envval)) + full_path = combinePathAndFile(envval, path); + + return full_path; +} + +#ifndef WIN32 +static char *getConfigForXDG (const char *path CTAGS_ATTR_UNUSED, + const char* extra CTAGS_ATTR_UNUSED) +{ + char *r = prependEnvvar ("ctags", "XDG_CONFIG_HOME"); + if (r) + return r; + + return prependEnvvar (".config/ctags", "HOME"); +} +#endif + +#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 = vStringIsEmpty (windowsHome) + ? NULL + : 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 || pathList[i].makePath != 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); + { + .path = CUSTOM_CONFIGURATION_FILE, + .isDirectory = false, + .makePath = NULL, + .stage = OptionLoadingStageCustom, + }, #endif - filename_body = (Option.configFilename)?Option.configFilename:"ctags"; -#ifdef MSDOS_STYLE_PATH - - if (asprintf (&filename,"/%s.cnf", filename_body) == -1) +#ifndef WIN32 { - error (FATAL, "error in asprintf"); - } - ENTER(DosCnf); - parseFileOptions (filename); - free (filename); + .path = NULL, + .isDirectory = true, + .makePath = getConfigForXDG, + .stage = OptionLoadingStageXdg, + }, #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, + .makePath = NULL, + }, +}; - ENTER(Preload); - preload (PreloadPathList); +static void parseConfigurationFileOptions (void) +{ + preload (preload_path_list); } static void parseEnvironmentOptions (void) @@ -3255,100 +3797,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 +3804,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 +3826,8 @@ extern void initOptions (void) processExcludeOption (NULL, ".cvsignore"); processExcludeOption (NULL, "_darcs"); processExcludeOption (NULL, ".deps"); + processExcludeOption (NULL, ".dvi"); + processExcludeOption (NULL, ".DS_Store"); processExcludeOption (NULL, "EIFGEN"); processExcludeOption (NULL, ".git"); processExcludeOption (NULL, ".gitignore"); @@ -3439,53 +3885,47 @@ extern void freeOptionResources (void) freeString (&Option.filterTerminator); freeList (&Excluded); - if(Option.ignore) - { - hashTableDelete(Option.ignore); - Option.ignore = NULL; - } + freeList (&ExcludedException); 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) +extern bool isDestinationStdout (void) { - return false; -} + bool toStdout = false; -extern void verbose (const char *const format, ...) -{ -} -/* GEANY DIFF END */ + 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; -extern bool canUseLineNumberAsLocator (void) -{ - return (Option.locate != EX_PATTERN); + return toStdout; } diff --git a/ctags/main/options.h b/ctags/main/options.h dissimilarity index 88% index 6fb7e3963..850f7c1d4 100644 --- a/ctags/main/options.h +++ b/ctags/main/options.h @@ -1,198 +1,44 @@ -/* -* 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 external interface to option processing. -*/ -#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 - -#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) { \ - 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); - -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); - -/* This is for emitting a tag for a commnn block of Fortran parser*/ -extern bool canUseLineNumberAsLocator (void); - -#endif /* CTAGS_MAIN_OPTIONS_H */ +/* +* 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 external interface to option processing. +*/ +#ifndef CTAGS_MAIN_OPTIONS_H +#define CTAGS_MAIN_OPTIONS_H + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ +#include "gvars.h" + +#include + + +/* +* DATA DECLARATIONS +*/ + +/* +* 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 (ctags_verbose) { \ + FILE* VFP = stderr +#define END_VERBOSE() } } while (0) + +#define BEGIN_VERBOSE_IF(COND,VFP) do { if (ctags_verbose || (COND)) { \ + FILE* VFP = stderr + + +extern bool inSandbox (void); + +/* This is for emitting a tag for a common block of Fortran parser*/ +extern bool canUseLineNumberAsLocator (void); + +#endif /* CTAGS_MAIN_OPTIONS_H */ diff --git a/ctags/main/options.h b/ctags/main/options_p.h similarity index 68% copy from ctags/main/options.h copy to ctags/main/options_p.h index 6fb7e3963..bbb5b8def 100644 --- a/ctags/main/options.h +++ b/ctags/main/options_p.h @@ -4,10 +4,10 @@ * 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 external interface to option processing. +* Defines internal interface to option processing. */ -#ifndef CTAGS_MAIN_OPTIONS_H -#define CTAGS_MAIN_OPTIONS_H +#ifndef CTAGS_MAIN_OPTIONS_PRIVATE_H +#define CTAGS_MAIN_OPTIONS_PRIVATE_H #if defined(OPTION_WRITE) # define CONST_OPTION @@ -20,20 +20,22 @@ */ #include "general.h" /* must always come first */ -#include - -#include "args.h" +#include "args_p.h" #include "field.h" -#include "fmt.h" +#include "fmt_p.h" +#include "options.h" #include "parse.h" #include "strlist.h" -#include "htable.h" #include "vstring.h" /* -* DATA DECLARATIONS +* MACROS */ +#define includeExtensionFlags() (Option.tagFileFormat > 1) +/* +* DATA DECLARATIONS +*/ typedef enum { OPTION_NONE, OPTION_SHORT, OPTION_LONG } optionType; typedef struct sCookedArgs { @@ -51,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 { @@ -60,85 +63,87 @@ typedef enum sortType { SO_FOLDSORTED } sortType; -typedef struct sIgnoredTokenInfo { - bool ignoreFollowingParenthesis; /* -I SOMETHING+ */ - char * replacement; /* -I SOMETHING=REPLACEMENT */ -} ignoredTokenInfo; +typedef enum eTagRelative { + TREL_NO, + TREL_YES, + TREL_ALWAYS, + TREL_NEVER, +} tagRelative; + +typedef enum eOptionLoadingStage { + OptionLoadingStageNone, + OptionLoadingStageCustom, + OptionLoadingStageDosCnf, + OptionLoadingStageEtc, + OptionLoadingStageLocalEtc, + OptionLoadingStageXdg, + OptionLoadingStageHomeRecursive, + OptionLoadingStageCurrentRecursive, + OptionLoadingStagePreload, + OptionLoadingStageEnvVar, + OptionLoadingStageCmdline, +} OptionLoadingStage; /* 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 */ + tagRelative tagRelative; /* --tag-relative file paths relative to tag file */ + int 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 */ + enum interactiveMode { INTERACTIVE_NONE = 0, + INTERACTIVE_DEFAULT, + INTERACTIVE_SANDBOX, } interactive; /* --interactive */ +#ifdef WIN32 + enum filenameSepOp { FILENAME_SEP_NO_REPLACE = false, + FILENAME_SEP_USE_SLASH = true, + FILENAME_SEP_UNSET, + } useSlashAsFilenameSeparator; /* --use-slash-as-filename-separator */ +#endif #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; +typedef void (* mainLoopFunc) (cookedArgs *args, void *data); /* * 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) { \ - FILE* VFP = stderr -#define END_VERBOSE() } } while (0) - extern void freeList (stringList** const pString); extern void setDefaultTagFileName (void); extern void checkOptions (void); @@ -154,45 +159,40 @@ 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 isExcludedFile (const char* const name, + bool falseIfExceptionsAreDefeind); 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); +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); -extern bool processKindDefinition (const char *const option, const char *const parameter); -extern bool processCorpusOption (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 processRegexOption (const char *const option, const char *const parameter); -extern bool processXcmdOption (const char *const option, const char *const parameter, OptionLoadingStage stage); +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 processRolesOption (const char *const option, const char *const parameter); -typedef void (* mainLoopFunc) (cookedArgs *args, void *data); -extern void setMainLoop (mainLoopFunc func, void *data); +extern bool isDestinationStdout (void); -/* This is for emitting a tag for a commnn block of Fortran parser*/ -extern bool canUseLineNumberAsLocator (void); +extern void setMainLoop (mainLoopFunc func, void *data); -#endif /* CTAGS_MAIN_OPTIONS_H */ +extern bool ptagMakePatternLengthLimit (ptagDesc *pdesc, langType langType, const 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 f365f5934..000000000 --- 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 32d9badcb..000000000 --- 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 000000000..cea6fe1dd --- /dev/null +++ b/ctags/main/param.c @@ -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. + * + */ + +#include "general.h" +#include "param.h" +#include "param_p.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 000000000..bbba6be3f --- /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 FILES +*/ +#include "general.h" + +#include "types.h" + + +/* +* DATA DECLARATIONS +*/ +struct sParameterHandlerTable { + const char *name; + const char *desc; + void (* handleParameter) (langType lang, const char *name, const char *arg); +}; + +/* +* FUNCTION PROTOTYPES +*/ +extern bool paramParserBool (const char *value, bool fallback, + const char *errWhat, const char *errCategory); + +#endif /* CTAGS_MAIN_PARAM_H */ diff --git a/ctags/main/param_p.h b/ctags/main/param_p.h new file mode 100644 index 000000000..0205a7d16 --- /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 449b8bea6..087a91dee 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -13,26 +13,44 @@ */ #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 "field_p.h" +#include "flags_p.h" +#include "htable.h" #include "keyword.h" -#include "main.h" -#define OPTION_WRITE -#include "options.h" -#include "parsers.h" +#include "lxpath_p.h" +#include "param.h" +#include "param_p.h" +#include "parse_p.h" +#include "parsers_p.h" #include "promise.h" -#include "ptag.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 @@ -54,33 +72,68 @@ 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. + 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 */ + + 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; /* * FUNCTION PROTOTYPES */ -#ifndef CTAGS_LIB +static void lazyInitialize (langType language); 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); +static void uninstallTagXpathTable (const langType language); /* * DATA DEFINITIONS */ -#ifndef CTAGS_LIB +static parserDefinition *FallbackParser (void); +static parserDefinition *CTagsParser (void); static parserDefinition *CTagsSelfTestParser (void); -#endif static parserDefinitionFunc* BuiltInParsers[] = { -#ifndef CTAGS_LIB + CTagsParser, /* This must be first entry. */ + FallbackParser, /* LANG_FALLBACK */ CTagsSelfTestParser, -#endif +#ifdef EXTERNAL_PARSER_LIST + EXTERNAL_PARSER_LIST +#else /* ! EXTERNAL_PARSER_LIST */ + PARSER_LIST, -#ifndef CTAGS_LIB XML_PARSER_LIST #ifdef HAVE_LIBXML , @@ -89,21 +142,38 @@ static parserDefinitionFunc* BuiltInParsers[] = { #ifdef HAVE_LIBYAML , #endif + PEG_PARSER_LIST +#ifdef HAVE_PACKCC + , #endif +#endif /* EXTERNAL_PARSER_LIST */ }; -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, - .name = KIND_FILE_DEFAULT_LONG, - .description = KIND_FILE_DEFAULT_LONG, + .letter = KIND_FILE_DEFAULT_LETTER, + .name = KIND_FILE_DEFAULT_NAME, + .description = KIND_FILE_DEFAULT_NAME, }; /* * 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 +182,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_DEFINITION_INDEX); } extern int makeSimpleRefTag (const vString* const name, const int kindIndex, @@ -129,10 +190,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) @@ -145,54 +203,42 @@ extern int makeSimpleRefTag (const vString* const name, const int kindIndex, return r; } +extern int makeSimplePlaceholder(const vString* const name) +{ + return makePlaceholder (vStringValue (name)); +} + 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,78 +246,168 @@ 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) +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]->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 kindDefinition* getLanguageFileKind (const langType language) +extern const char *getLanguageName (const langType language) { - kindDefinition* kind; + return getLanguageNameFull (language, false); +} - Assert (0 <= language && language < (int) LanguageCount); +extern const char *getLanguageKindName (const langType language, const int kindIndex) +{ + kindDefinition* kdef = getLanguageKind (language, kindIndex); + return kdef->name; +} + +static kindDefinition kindGhost = { + .letter = KIND_GHOST_LETTER, + .name = KIND_GHOST_NAME, + .description = KIND_GHOST_NAME, +}; - 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); - return (LanguageTable [language]->kindTable) + kindIndex; + 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_LETTER) + return &kindGhost; + else + return getKindForLetter (LanguageTable [language].kindControlBlock, kindLetter); } -extern langType getNamedLanguage (const char *const name, size_t len) +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_NAME) == 0) + return &kindGhost; + else + return getKindForName (LanguageTable [language].kindControlBlock, kindName); +} + +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 getNamedLanguageFull (const char *const name, size_t len, bool noPretending) { langType result = LANG_IGNORE; 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); } - } + + 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) { @@ -286,20 +422,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 +448,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 +473,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 +494,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 +517,162 @@ found: 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); +} + +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_LETTER) + parentKindex = KIND_WILDCARD_INDEX; + else if (parentKletter == KIND_FILE_DEFAULT_LETTER) + error (FATAL, + "the kind letter `%c' in \"--%s\" option is reserved for \"%s\" kind and no separator can be assigned to", + KIND_FILE_DEFAULT_LETTER, option, KIND_FILE_DEFAULT_NAME); + 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_LETTER) + { + 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_LETTER) + error (FATAL, + "the kind letter `%c' in \"--%s\" option is reserved for \"%s\" kind and no separator can be assigned to", + KIND_FILE_DEFAULT_LETTER, option, KIND_FILE_DEFAULT_NAME); + 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; @@ -492,13 +797,13 @@ 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:"); 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 +814,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 +865,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 +990,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 +1023,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, "input; \ (_glc_)->input = mio_new_mio (tmp_, 0, -1); \ - mio_free (tmp_); \ + mio_unref (tmp_); \ if (!(_glc_)->input) { \ (_glc_)->err = true; \ goto _label_; \ @@ -748,7 +1076,7 @@ struct getLangCtx { #define GLC_FCLOSE(_glc_) do { \ if ((_glc_)->input) { \ - mio_free((_glc_)->input); \ + mio_unref((_glc_)->input); \ (_glc_)->input = NULL; \ } \ } while (0) @@ -777,6 +1105,10 @@ static const struct taster { .taste = extractVimFileType, .msg = "vim modeline", }, + { + .taste = extractPHPMark, + .msg = "PHP marker", + } }; static langType tasteLanguage (struct getLangCtx *glc, const struct taster *const tasters, int n_tasters, langType *fallback); @@ -789,7 +1121,7 @@ hasTheSameSelector (langType lang, selectLanguage candidate_selector) { selectLanguage *selector; - selector = LanguageTable[ lang ]->selectLanguage; + selector = LanguageTable[ lang ].def->selectLanguage; if (selector == NULL) return false; @@ -809,7 +1141,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 +1162,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 +1189,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 +1204,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 +1244,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 +1293,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 +1364,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 +1390,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 +1408,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 +1441,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 +1465,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); @@ -1132,22 +1482,27 @@ getFileLanguageInternal (const char *const fileName, MIO **mio) verbose (" fallback[hint = %d]: %s\n", i, getLanguageName (language)); } + /* We cannot use isLanguageEnabled() here. */ + if (language == LANG_IGNORE + && LanguageTable[LANG_FALLBACK].def->enabled) + { + language = LANG_FALLBACK; + verbose (" last resort: using \"%s\" parser\n", + getLanguageName (LANG_FALLBACK)); + } 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 +1511,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 +1529,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 +1548,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 +1559,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 +1599,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 +1636,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 +1681,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 +1725,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 +1765,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) +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 +1780,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 +1864,66 @@ 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)); + for (i = 0; i < builtInCount; ++i) + { + LanguageTable [i].pretendingAsLanguage = LANG_IGNORE; + LanguageTable [i].pretendedAsLanguage = LANG_IGNORE; + } + + LanguageHTable = hashTableNew (127, + hashCstrcasehash, + hashCstrcaseeq, + NULL, + NULL); + DEFAULT_TRASH_BOX(LanguageHTable, hashTableDelete); verbose ("Installing parsers: "); for (i = 0 ; i < builtInCount ; ++i) @@ -1508,32 +1931,24 @@ extern void initializeParsing (void) parserDefinition* const def = (*BuiltInParsers [i]) (); if (def != NULL) { - bool accepted = false; - if (def->name == NULL || def->name[0] == '\0') - error (FATAL, "parser definition must contain name\n"); - else if (def->method & METHOD_NOT_CRAFTED) - { + Assert (def->name); + Assert (def->name[0] != '\0'); + Assert (strcmp (def->name, RSV_LANG_ALL)); + Assert (strpbrk (def->name, "!\"$%&'()*,-./:;<=>?@[\\]^`|~") == NULL); + + if (def->method & METHOD_NOT_CRAFTED) def->parser = findRegexTags; - accepted = true; - } - else if ((!def->invisible) && (((!!def->parser) + (!!def->parser2)) != 1)) - error (FATAL, - "%s parser definition must define one and only one parsing routine\n", - def->name); else - accepted = true; - if (accepted) - { - verbose ("%s%s", i > 0 ? ", " : "", def->name); - def->id = LanguageCount++; - LanguageTable [def->id] = def; - } + /* parser definition must define one and only one parsing routine */ + Assert ((!!def->parser) + (!!def->parser2) == 1); + + initializeParsingCommon (def, true); } } verbose ("\n"); for (i = 0; i < builtInCount ; ++i) - linkDependenciesAtInitializeParsing (LanguageTable [i]); + linkDependenciesAtInitializeParsing (LanguageTable [i].def); } extern void freeParserResources (void) @@ -1541,26 +1956,29 @@ 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); + uninstallTagXpathTable (i); - if (lang->fileKind != &defaultFileKind) - { - eFree (lang->fileKind); - lang->fileKind = NULL; - } + freeLregexControlBlock (parser->lregexControlBlock); + freeKindControlBlock (parser->kindControlBlock); + parser->kindControlBlock = 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,106 +1990,234 @@ 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; + } - if (def->fileKind != &defaultFileKind) - eFree (def->fileKind); + base = getNamedLanguage (param, 0); + if (base == LANG_IGNORE) + { + error (WARNING, "Unknown language(%s) is specified for \"%s\" flag of --langdef option", + param, optflag); + return; - def->fileKind = fileKindNew (param[0]); -} + } -static flagDefinition LangDefFlagDef [] = { - { '\0', "fileKind", NULL, lang_def_flag_file_kind_long }, -}; + flag_data->base = eStrdup(param); +} -extern void processLanguageDefineOption ( - const char *const option, const char *const parameter CTAGS_ATTR_UNUSED) +#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) { - 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); + 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 - { - char *name; - char *flags; - unsigned int i; - parserDefinition* def; + AssertNotReached (); +} - flags = strchr (parameter, LONG_FLAGS_OPEN); - if (flags) - name = eStrndup (parameter, flags - parameter); - else - name = eStrdup (parameter); +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; +} - 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; +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"}, +}; - flagsEval (flags, LangDefFlagDef, ARRAY_SIZE (LangDefFlagDef), def); +static void optlibFreeDep (langType lang, bool initialized CTAGS_ATTR_UNUSED) +{ + parserDefinition * pdef = LanguageTable [lang].def; - eFree (name); + if (pdef->dependencyCount == 1) + { + 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 kindDefinition *langKindDefinition (const langType language, const int flag) +static parserDefinition* OptlibParser(const char *name, const char *base, + subparserRunDirection direction) { - 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; + 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; } -static kindDefinition *langKindLongOption (const langType language, const char *kindLong) +extern void processLanguageDefineOption ( + const char *const option, const char *const parameter) { - 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; + char *name; + char *flags; + parserDefinition* def; + + flags = strchr (parameter, LONG_FLAGS_OPEN); + if (flags) + name = eStrndup (parameter, flags - parameter); + else + name = eStrdup (parameter); + + /* Veirfy that the name of new language is acceptable or not. */ + char *unacceptable; + if (name [0] == '\0') + { + eFree (name); + error (FATAL, "No language specified for \"%s\" option", option); + } + else if (getNamedLanguage (name, 0) != LANG_IGNORE) + { + /* name cannot be freed because it is used in the FATAL message. */ + error (FATAL, "Language \"%s\" already defined", name); + } + else if (strcmp(name, RSV_LANG_ALL) == 0) + { + eFree (name); + error (FATAL, "\"all\" is reserved; don't use it as the name for defining a new language"); + } + else if ((unacceptable = strpbrk (name, "!\"$%&'()*,-./:;<=>?@[\\]^`|~"))) + { + char c = *unacceptable; + + /* name cannot be freed because it is used in the FATAL message. */ + /* We accept '_'. + * We accept # and + because they are already used in C# parser and C++ parser. + * {... is already trimmed at the beginning of this function. */ + if ((c == '`') || (c == '\'')) + error (FATAL, "don't use \"%c\" in a language name (%s)", c, name); + else + error (FATAL, "don't use `%c' in a language name (%s)", c, name); + } + + 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"); + + 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 (); + LanguageTable [def->id].pretendingAsLanguage = LANG_IGNORE; + LanguageTable [def->id].pretendedAsLanguage = LANG_IGNORE; + + eFree (name); } extern bool isLanguageKindEnabled (const langType language, int kindIndex) @@ -1680,49 +2226,60 @@ extern bool isLanguageKindEnabled (const langType language, int kindIndex) return kdef->enabled; } +extern bool isLanguageRoleEnabled (const langType language, int kindIndex, int roleIndex) +{ + return isRoleEnabled(LanguageTable [language].kindControlBlock, + kindIndex, roleIndex); +} + +extern bool isLanguageKindRefOnly (const langType language, int kindIndex) +{ + 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 +2306,7 @@ static void processLangKindDefinition ( else if (*p != '+' && *p != '-') resetLanguageKinds (language, false); - longName = vStringNewOrClear (longName); + longName = vStringNewOrClearWithAutoRelease (longName); while ((c = *p++) != '\0') { @@ -1780,7 +2337,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 +2350,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,1011 +2360,2720 @@ static void processLangKindDefinition ( } } -struct langKindDefinitionStruct { - const char *const option; - const char *const parameter; -}; -static void processLangKindDefinitionEach( - langType lang, void* user_data) +static void freeKdef (kindDefinition *kdef) { - struct langKindDefinitionStruct *arg = user_data; - processLangKindDefinition (lang, arg->option, arg->parameter); + eFree (kdef->name); + eFree (kdef->description); + eFree (kdef); } -extern bool processKindDefinition ( - const char *const option, const char *const parameter) +static char *extractDescriptionAndFlags(const char *input, const char **flags) { -#define PREFIX "kinds-" -#define PREFIX_LEN strlen(PREFIX) + vString *vdesc = vStringNew(); + bool escaped = false; - bool handled = false; - struct langKindDefinitionStruct arg = { - .option = option, - .parameter = parameter, - }; - langType language; + if (flags) + *flags = NULL; - const char* const dash = strchr (option, '-'); - if (dash != NULL && - (strcmp (dash + 1, "kinds") == 0 || strcmp (dash + 1, "types") == 0)) + while (*input != '\0') { - size_t len = dash - option; - - if ((len == 1) && (*option == '*')) - foreachLanguage(processLangKindDefinitionEach, &arg); - else + if (escaped) { - vString* langName = vStringNew (); - vStringNCopyS (langName, option, len); - language = getNamedLanguage (vStringValue (langName), 0); - if (language == LANG_IGNORE) - error (WARNING, "Unknown language \"%s\" in \"%s\" option", vStringValue (langName), option); - else - processLangKindDefinition (language, option, parameter); - vStringDelete (langName); - } - handled = true; - } - else if ( strncmp (option, PREFIX, PREFIX_LEN) == 0 ) - { - const char* lang; - size_t len; + vStringPut (vdesc, *input); + escaped = false; - lang = option + PREFIX_LEN; - len = strlen (lang); - if (len == 0) - error (WARNING, "No language given in \"%s\" option", option); - else if (len == 1 && lang[0] == '*') - { - foreachLanguage(processLangKindDefinitionEach, &arg); - handled = true; } - else + else if (*input == '\\') + escaped = true; + else if (*input == LONG_FLAGS_OPEN) { - 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; - } + if (flags) + *flags = input; + break; } - + else + vStringPut (vdesc, *input); + input++; } - return handled; -#undef PREFIX -#undef PREFIX_LEN + return vStringDeleteUnwrap(vdesc); } -static void printRoles (const langType language, const char* letters, bool allowMissingKind) +static void pre_kind_def_flag_refonly_long (const char* const optflag, + const char* const param, void* data) { - const parserDefinition* const lang = LanguageTable [language]; - const char *c; + kindDefinition *kdef = data; + kdef->referenceOnly = true; +} - if (lang->invisible) - return; +static flagDefinition PreKindDefFlagDef [] = { + { '\0', "_refonly", NULL, pre_kind_def_flag_refonly_long, + NULL, "use this kind reference tags only"}, +}; - for (c = letters; *c != '\0'; c++) +static bool processLangDefineKind(const langType language, + const char *const option, + const char *const parameter) +{ + parserObject *parser; + + kindDefinition *kdef; + char letter; + const char * p = parameter; + char *name; + char *description; + const char *name_start; + const char *marker_end; + size_t name_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 (/* See #1697. isalnum expects 0~255 as the range of characters. */ + !isalpha ((unsigned char)letter) + ) + error (FATAL, "the kind letter given in \"--%s\" option is not an alphabet", option); + else if (letter == KIND_FILE_DEFAULT_LETTER) + error (FATAL, "the kind letter `%c' in \"--%s\" option is reserved for \"%s\" kind", + KIND_FILE_DEFAULT_LETTER, option, KIND_FILE_DEFAULT_NAME); + else if (getKindForLetter (parser->kindControlBlock, letter)) { - unsigned int i; - const kindDefinition *k; + error (WARNING, "the kind for letter `%c' specified in \"--%s\" option is already defined.", + letter, option); + return true; + } - for (i = 0; i < lang->kindCount; ++i) + 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); + marker_end = strchr (p, ','); + if (!marker_end) + error (FATAL, "no kind description specified in \"--%s\" option", option); + + name_start = p; + while (p != marker_end) + { + if (p == name_start) { - k = lang->kindTable + i; - if (*c == KIND_WILDCARD || k->letter == *c) + if (!isalpha(*p)) { - int j; - const roleDefinition *r; - - 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; + char *name_in_msg = eStrndup (name_start, marker_end - name_start); + error (FATAL, + "a kind name doesn't start with an alphabetical character: " + "'%s' in \"--%s\" option", + name_in_msg, option); + } + } + else + { + if (!isalnum (*p)) + { + char *name_in_msg = eStrndup (name_start, marker_end - name_start); + error (FATAL, + "non-alphanumeric char is used as part of kind name: " + "'%s' in \"--%s\" option", + name_in_msg, option); } } - if ((i == lang->kindCount) && (*c != KIND_WILDCARD) && (!allowMissingKind)) - error (FATAL, "No such letter kind in %s: %c\n", lang->name, *c); + p++; } -} -extern void printLanguageRoles (const langType language, const char* letters) -{ - if (language == LANG_AUTO) + if (marker_end == name_start) + error (FATAL, "the kind name in \"--%s\" option is empty", option); + + name_len = marker_end - name_start; + if (strncmp (name_start, KIND_FILE_DEFAULT_NAME, name_len) == 0) + error (FATAL, + "the kind name " KIND_FILE_DEFAULT_NAME " in \"--%s\" option is reserved", + option); + + name = eStrndup (name_start, name_len); + if (getKindForName (parser->kindControlBlock, name)) { - unsigned int i; - for (i = 0 ; i < LanguageCount ; ++i) - printRoles (i, letters, true); + error (WARNING, "the kind for name `%s' specified in \"--%s\" option is already defined.", + name, option); + eFree (name); + return true; } - else - printRoles (language, letters, false); + 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; } -extern void printLanguageFileKind (const langType language) +static void freeRdef (roleDefinition *rdef) { - if (language == LANG_AUTO) - { - unsigned int i; - for (i = 0 ; i < LanguageCount ; ++i) - { - const parserDefinition* const lang = LanguageTable [i]; - printf ("%s %c\n", lang->name, lang->fileKind->letter); - } - } - else - printf ("%c\n", LanguageTable [language]->fileKind->letter); + eFree (rdef->name); + eFree (rdef->description); + eFree (rdef); } -static void printKinds (langType language, bool allKindFields, bool indent) +static bool processLangDefineRole(const langType language, + const char *const kindSpec, + const char *const option, + const char *const parameter) { - const parserDefinition* lang; + parserObject *parser; + + kindDefinition *kdef; + roleDefinition *rdef; + char *name; + char *description; + Assert (0 <= language && language < (int) LanguageCount); + Assert (parameter); - initializeParser (language); - lang = LanguageTable [language]; - if (lang->kindTable != NULL) + parser = LanguageTable + language; + + if (*kindSpec == '{') { - unsigned int i; - for (i = 0 ; i < lang->kindCount ; ++i) + const char *end = strchr (kindSpec, '}'); + if (end == NULL) + error (FATAL, "no '}' representing the end of kind name in --%s option: %s", + option, kindSpec); + if (*(end + 1) != '\0') + error (FATAL, "garbage after the kind specification %s in --%s option", + kindSpec, option); + char *kindName = eStrndup (kindSpec + 1, end - (kindSpec + 1)); + if (strcmp (kindName, KIND_FILE_DEFAULT_NAME) == 0) + error (FATAL, "don't define a role for %c/%s kind; it has no role: --%s", + KIND_FILE_DEFAULT_LETTER, KIND_FILE_DEFAULT_NAME, + option); + kdef = getKindForName (parser->kindControlBlock, kindName); + if (kdef == NULL) + error (FATAL, "the kind for name `%s' specified in \"--%s\" option is not defined.", + kindName, option); + eFree (kindName); + } + else + { + char kletter = *kindSpec; + if (!isalnum ((unsigned char)kletter)) + error (FATAL, "the kind letter given in \"--%s\" option is not an alphabet or a number", option); + else if (kletter == KIND_FILE_DEFAULT_LETTER) + error (FATAL, "the kind letter `%c' in \"--%s\" option is reserved for \"%s\" kind, and no role can be attached to it", + KIND_FILE_DEFAULT_LETTER, option, KIND_FILE_DEFAULT_NAME); + else if (*(kindSpec + 1) != '\0') + error (FATAL, "more than one letters are specified as a kind spec in \"--%s\" option: use `{' and `}' for specifying a kind name", + option); + + kdef = getKindForLetter (parser->kindControlBlock, kletter); + if (kdef == NULL) { - if (allKindFields && indent) - printf (Option.machinable? "%s": PR_KIND_FMT (LANG,s), lang->name); - printKind (lang->kindTable + i, allKindFields, indent, Option.machinable); + error (FATAL, "the kind for letter `%c' specified in \"--%s\" option is not defined.", + *kindSpec, option); + return true; } } - printRegexKinds (language, allKindFields, indent, Option.machinable); - printXcmdKinds (language, allKindFields, indent, Option.machinable); -} -extern void printLanguageKinds (const langType language, bool allKindFields) -{ - if (language == LANG_AUTO) - { - unsigned int i; + const char * p = parameter; + const char *tmp_end = strchr (p, ','); + if (!tmp_end) + error (FATAL, "no role description specified in \"--%s\" option", option); - if (Option.withListHeader && allKindFields) - printKindListHeader (true, Option.machinable); + const char * tmp_start = p; + while (p != tmp_end) + { + if (!isalnum (*p)) + error (FATAL, "unacceptable char as part of role name in \"--%s\" option: %c", + option, *p); + p++; + } - for (i = 0 ; i < LanguageCount ; ++i) - { - const parserDefinition* const lang = LanguageTable [i]; + 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); + + const char *flags; + 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) +{ +#define PREFIX "_roledef-" +#define PREFIX_LEN strlen(PREFIX) + + langType language = getLanguageComponentInOption (option, PREFIX); + if (language == LANG_IGNORE) + return false; + + Assert (0 <= language && language < (int) LanguageCount); + const char* kindSpec = option + PREFIX_LEN + strlen (getLanguageName (language)); + if (*kindSpec == '\0') + error (FATAL, "no kind is specifined in \"--%s=%s\"", option, parameter); + if (*kindSpec != '.') + error (FATAL, "no delimiter (.) where a kindspec starts is found in \"--%s\": %c", + option, *kindSpec); + kindSpec++; + + return processLangDefineRole (language, kindSpec, option, parameter); +#undef PREFIX +#undef PREFIX_LEN +} + +struct langKindDefinitionStruct { + const char *const option; + const char *const parameter; +}; +static void processLangKindDefinitionEach( + langType lang, void* user_data) +{ + struct langKindDefinitionStruct *arg = user_data; + processLangKindDefinition (lang, arg->option, arg->parameter); +} + +static bool parameterEnablingAllOrFileKind (const char *const option, + const char *const parameter, + bool following_plus_or_minus_op) +{ + size_t file_long_flag_len = strlen(KIND_FILE_DEFAULT_NAME); + + switch (parameter[0]) + { + /* Though only '*' is documented as an acceptable kind spec for + * --kinds-all option in our man page, we accept '\0' here because + * it will be useful for testing purpose. */ + case '\0': + if (following_plus_or_minus_op) + error(FATAL, "no kind specification after + (or -) in --%s option", + option); + else + return true; + case '+': + case '-': + if (following_plus_or_minus_op) + error(FATAL, "don't repeat + (nor -) in --%s option", + option); + else + return parameterEnablingAllOrFileKind (option, parameter + 1, true); + case KIND_WILDCARD_LETTER: + if (following_plus_or_minus_op) + error(FATAL, "don't use '*' after + (nor -) in --%s option", + option); + else + return parameterEnablingAllOrFileKind (option, parameter + 1, false); + case KIND_FILE_DEFAULT_LETTER: + return parameterEnablingAllOrFileKind (option, parameter + 1, false); + case '{': + if (strncmp (parameter + 1, KIND_FILE_DEFAULT_NAME, file_long_flag_len) == 0 + && parameter [1 + file_long_flag_len] == '}') + return parameterEnablingAllOrFileKind (option, + parameter + 1 + file_long_flag_len + 1, + false); + break; + } + return false; +} + +extern bool processKindsOption ( + const char *const option, const char *const parameter) +{ +#define PREFIX "kinds-" +#define PREFIX_LEN strlen(PREFIX) + + bool handled = false; + struct langKindDefinitionStruct arg = { + .option = option, + .parameter = parameter, + }; + langType language; + + const char* const dash = strchr (option, '-'); + if (dash != NULL && + (strcmp (dash + 1, "kinds") == 0 || strcmp (dash + 1, "types") == 0)) + { + size_t len = dash - option; + char *langName = eStrndup (option, len); + + if ((len == 3) && (strcmp (langName, RSV_LANG_ALL) == 0)) + { + error (WARNING, + "\"--%s\" option is obsolete; use \"--kinds-%s\" instead", + option, langName); + if (!parameterEnablingAllOrFileKind (option, parameter, false)) + error (FATAL, "only '*', 'F', \"{file}\" or their combination is acceptable as kind letter for --%s", option); + foreachLanguage(processLangKindDefinitionEach, &arg); + } + else + { + language = getNamedLanguage (langName, 0); + if (language == LANG_IGNORE) + error (WARNING, "Unknown language \"%s\" in \"%s\" option", langName, option); + else + processLangKindDefinition (language, option, parameter); + } + eFree (langName); + handled = true; + } + else if ( strncmp (option, PREFIX, PREFIX_LEN) == 0 ) + { + const char* lang; + + lang = option + PREFIX_LEN; + if (lang[0] == '\0') + error (WARNING, "No language given in \"%s\" option", option); + else if (strcmp (lang, RSV_LANG_ALL) == 0) + { + if (!parameterEnablingAllOrFileKind (option, parameter, false)) + error (FATAL, "only '*', 'F', \"{file}\" or their combination is acceptable as kind letter for --%s", option); + foreachLanguage(processLangKindDefinitionEach, &arg); + } + 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; + } + return handled; +#undef PREFIX +#undef PREFIX_LEN +} + +/* + * The argument specification for --roles-:= option + * ===================================================================== + * + * --roles-all.*= + * --roles-all= + * => Disable all roles of all kinds in all languages. + * + * --roles-all.*='*' + * --roles-all='*' + * => Enable all roles of all kinds in all languages. + * + * --roles-.*= + * --roles-= + * => Disable all roles of all kinds. + * + * --roles-.*=* + * --roles-=* + * => Enable all roles of all kinds. + * + * --roles-.{kind}= + * --roles-.k= + * => Disable all roles of the kind specified with a letter. + * + * --roles-.{kind}=* + * --roles-.k=* + * => Enable all roles of the kind specified with a letter. + * + * --roles-.{kind}=[+|-]{role} + * --roles-.k=[+|-]{role} + * => Enable/disable the role of the kind specified with a letter. + * + * + * Examples of combination + * --------------------------------------------------------------------- + * + * --roles-.k0=+{role0}-{role1}{role2} + * --roles-.{kind1}=+{role0}-{role1}{role2} + * + * + * How --roledef should be change to align --roles- notation + * --------------------------------------------------------------------- + * + * --_roledef-.k=role,description + * --_roledef-.{kind}=role,description + * + * The notation was + * --_roledef-=k.role,description + * + * + * How --param should be change to align --roles- notation + * --------------------------------------------------------------------- + * + * --_param-.name=argument + * + * * The notation was + * --_param-:name=argument + * + * + * How --paramdef should be to align --roles- notation + * --------------------------------------------------------------------- + * + * --_paramdef-.name=[ default (desription) ] + * + * + * Discussion: which shoule we use . or : ? + * --------------------------------------------------------------------- + * + * `.' is better because `:' implies fields. + * + */ +struct langKindRoleDefinitionStruct { + int kindIndex; + const char *const option; + const char *const parameter; +}; + +typedef void (*kindCallback) (langType language, int kindIndex, void* user_data); +static void foreachKind(langType language, kindCallback callback, void *user_data) +{ + unsigned int c = countLanguageKinds (language); + for (unsigned int i = 0; i < c; i++) + callback (language, (int)i, user_data); +} + +static void resetKindRoles (const langType language, int kindIndex, const bool mode) +{ + Assert (0 <= language && language < (int) LanguageCount); + unsigned int c = countLanguageRoles (language, kindIndex); + + for (unsigned int i = 0; i < c; i++) + { + roleDefinition* rdef = getLanguageRole (language, kindIndex, (int)i); + enableRole (rdef, mode); + } +} + +static void resetKindRolesAsCallback (const langType language, int kindIndex, void *user_data) +{ + bool mode = (bool)user_data; + resetKindRoles (language, kindIndex, mode); +} + +static void processLangKindRoleDefinition ( + const langType language, const int kindIndex, const char *const option, + const char *const parameter) +{ + Assert (0 <= language && language < (int) LanguageCount); + Assert (kindIndex != KIND_GHOST_INDEX); + initializeParser (language); + + const char *p = parameter; + bool mode = true; + + if (*p == '\0') + { + resetKindRoles (language, kindIndex, false); + return; + } + else if (*p != '+' && *p != '-' ) + resetKindRoles (language, kindIndex, false); + + while (*p != '\0') + { + if (*p == '+') + { + mode = true; + p++; + } + else if (*p == '-') + { + mode = false; + p++; + } + else if (*p == '{') + { + p++; + char *q = strchr (p, '}'); + if (!q) + + error (FATAL, "no '}' representing the end of role name in --%s option: %s", + option, p); + if (p == q) + error (FATAL, "empty role for the kind letter: %c", + getLanguageKind (language, kindIndex)->letter); + + char *rname = eStrndup (p, q - p); + roleDefinition *rdef = getLanguageRoleForName (language, kindIndex, rname); + if (!rdef) + error (WARNING, "no such role: %s of %c kind in language %s", + rname, getLanguageKind (language, kindIndex)->letter, + getLanguageName (language)); + else + enableRole (rdef, mode); + eFree (rname); + p = q + 1; + } + else if (*p == '*') + { + resetKindRoles (language, kindIndex, true); + p++; + } + else + error (FATAL, "unexpected character %c in --%s=%s option", + *p, option, parameter); + } +} + +static void processLangKindRoleDefinitionEach (langType language, void* user_data) +{ + struct langKindRoleDefinitionStruct *arg = user_data; + + if (arg->kindIndex == KIND_GHOST_INDEX) + { + initializeParser (language); + foreachKind (language, resetKindRolesAsCallback, + ((*(arg->parameter) == '*')? (void *)true: (void *)false)); + } + else + processLangKindRoleDefinition (language, arg->kindIndex, + arg->option, arg->parameter); +} + +extern bool processRolesOption (const char *const option, const char *const parameter) +{ +#define PREFIX "roles-" +#define PREFIX_LEN strlen(PREFIX) + + if ( strncmp (option, PREFIX, PREFIX_LEN) != 0 ) + return false; + + const char* lang = option + PREFIX_LEN; + if (lang[0] == '\0') + { + error (WARNING, "no language given in \"%s\" option", option); + return true; + } + + /* + * --roles-all.*= + * --roles-all= + * => Disable all roles of all kinds in all languages. + * + * --roles-all.*='*' + * --roles-all='*' + * => Enable all roles of all kinds in all languages. + */ + if (strncmp (lang, RSV_LANG_ALL, strlen(RSV_LANG_ALL)) == 0) + { + if (lang [strlen (RSV_LANG_ALL)] == '\0' + || (strcmp (lang + strlen (RSV_LANG_ALL), ".*") == 0)) + { + if (*parameter == '\0' + || strcmp(parameter, "*") == 0) + { + struct langKindRoleDefinitionStruct arg = { + .kindIndex = KIND_GHOST_INDEX, + .option = option, + .parameter = parameter, + }; + foreachLanguage (processLangKindRoleDefinitionEach, &arg); + return true; + } + else + error (FATAL, "only '*' or '' (empty string) is acceptable as an argument for --%s: %s", + option, + parameter); + } + else if (lang [strlen(RSV_LANG_ALL)] == '.') + error (FATAL, "only '*' or '' (empty string) is acceptable as a kind spec for --%sall: --%s", + PREFIX, + option); + } + + /* Decide the language. */ + langType language; + const char *dot = strchr (lang, '.'); + if (dot) + language = getNamedLanguage (lang, dot - lang); + else + language = getNamedLanguage (lang, 0); + + if (language == LANG_IGNORE) + { + char *lang0 = dot? eStrndup (lang, dot - lang): NULL; + error (WARNING, "unknown language \"%s\" in --%s option", + (lang0? lang0: lang), option); + if (lang0) + eFree (lang0); + return true; + } + + /* + * --roles-.*= + * --roles-= + * => Disable all roles of all kinds. + * + * --roles-.*=* + * --roles-=* + * => Enable all roles of all kinds. + */ + if (dot == NULL || (strcmp (dot, ".*") == 0)) + { + if (*parameter == '\0' + || strcmp(parameter, "*") == 0) + { + foreachKind (language, resetKindRolesAsCallback, + ((*parameter == '*')? (void*)true: (void*)false)); + return true; + } + else + error (FATAL, "only '*' or '' (empty string) is acceptable as an argument for --%s: %s", + option, + parameter); + } + + /* Decide the kind of the language. */ + parserObject *parser = LanguageTable + language; + int kindIndex = KIND_GHOST_INDEX; + const char *kind = dot + 1; + if (*kind == '{') + { + const char *name_end = strchr (kind, '}'); + if (name_end == NULL) + error (FATAL, "no '}' representing the end of kind name in --%s option: %s", + option, kind); + char *kindName = eStrndup (kind + 1, name_end - (kind + 1)); + if (strcmp (kindName, KIND_FILE_DEFAULT_NAME) == 0) + { + error (WARNING, "don't enable/disable a role in %c/%s kind; it has no role: --%s", + KIND_FILE_DEFAULT_LETTER, KIND_FILE_DEFAULT_NAME, + option); + return true; + } + kindIndex = getKindIndexForName (parser->kindControlBlock, kindName); + if (kindIndex == KIND_GHOST_INDEX) + { + eFree (kindName); + error (WARNING, "no such kind name as specified in --%s option", option); + return true; + } + if (*(name_end + 1) != '\0') + error (FATAL, "garbage after the kind specification {%s} in --%s option", + kindName, option); + eFree (kindName); + } + else if (isalpha ((unsigned char)*kind)) + { + if (*kind == KIND_FILE_DEFAULT_LETTER) + { + error (WARNING, "don't enable/disable a role in %c/%s kind; it has no role: --%s", + KIND_FILE_DEFAULT_LETTER, KIND_FILE_DEFAULT_NAME, + option); + return true; + } + kindIndex = getKindIndexForLetter (parser->kindControlBlock, *kind); + if (kindIndex == KIND_GHOST_INDEX) + { + error (WARNING, "no such kind letter as specified in --%s option", option); + return true; + } + if (*(kind + 1) != '\0') + error (FATAL, "garbage after the kind specification '%c' in --%s option", + *kind, option); + } + else + error (FATAL, "'%c', unexpected character in --%s", *kind, option); + + + /* + * --roles-.{kind}= + * --roles-.k= + * => Disable all roles of the kind specified with a letter. + * + * --roles-.{kind}=* + * --roles-.k=* + * => Enable all roles of the kind specified with a letter. + * + * --roles-.{kind}=[+|-|]{role} + * --roles-.k=[+|-|]{role} + * => Enable/disable the role of the kind specified with a letter. + */ + processLangKindRoleDefinition (language, kindIndex, option, parameter); + + return true; +#undef PREFIX +#undef PREFIX_LEN +} + +extern void printLanguageRoles (const langType language, const char* kindspecs, + bool withListHeader, bool machinable, FILE *fp) +{ + struct colprintTable *table = roleColprintTableNew(); + parserObject *parser; + + initializeParser (language); + + if (language == LANG_AUTO) + { + for (unsigned int i = 0 ; i < LanguageCount ; ++i) + { + if (!isLanguageVisible (i)) + continue; + + parser = LanguageTable + i; + roleColprintAddRoles (table, parser->kindControlBlock, kindspecs); + } + } + else + { + parser = LanguageTable + language; + roleColprintAddRoles (table, parser->kindControlBlock, kindspecs); + } + + roleColprintTablePrint (table, (language != LANG_AUTO), + withListHeader, machinable, fp); + colprintTableDelete (table); +} + +static void printKinds (langType language, bool indent, + struct colprintTable * table) +{ + 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 + { + for (unsigned int i = 0 ; i < countKinds(kcb) ; ++i) + printKind (getKind(kcb, i), indent); + } +} + +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) + { + for (unsigned int i = 0 ; i < LanguageCount ; ++i) + { + const parserDefinition* const lang = LanguageTable [i].def; if (lang->invisible) continue; - if (!allKindFields) + if (!table) printf ("%s%s\n", lang->name, isLanguageEnabled (i) ? "" : " [disabled]"); - printKinds (i, allKindFields, true); + printKinds (i, true, table); } } else + printKinds (language, false, table); + + if (allKindFields) + { + kindColprintTablePrint(table, (language == LANG_AUTO)? 0: 1, + withListHeader, machinable, fp); + colprintTableDelete (table); + } +} + +static void printParameters (struct colprintTable *table, langType language) +{ + const parserDefinition* lang; + Assert (0 <= language && language < (int) LanguageCount); + + initializeParser (language); + lang = LanguageTable [language].def; + if (lang->parameterHandlerTable != NULL) + { + for (unsigned int i = 0; i < lang->parameterHandlerCount; ++i) + paramColprintAddParameter(table, language, lang->parameterHandlerTable + i); + } + +} + +extern void printLanguageParameters (const langType language, + bool withListHeader, bool machinable, FILE *fp) +{ + struct colprintTable *table = paramColprintTableNew(); + + if (language == LANG_AUTO) + { + for (unsigned int i = 0; i < LanguageCount ; ++i) + { + const parserDefinition* const lang = LanguageTable [i].def; + + if (lang->invisible) + continue; + + printParameters (table, i); + } + } + else + printParameters (table, language); + + paramColprintTablePrint (table, (language != LANG_AUTO), + withListHeader, machinable, fp); + colprintTableDelete (table); +} + +static void processLangAliasOption (const langType language, + const char *const parameter) +{ + const char* alias; + const parserObject * parser; + + Assert (0 <= language && language < (int) LanguageCount); + parser = LanguageTable + language; + + 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 an alias %s to %s\n", alias, parser->def->name); + } + else if (parameter[0] == '-') + { + if (parser->currentAliases) + { + alias = parameter + 1; + if (stringListDeleteItemExtension (parser->currentAliases, alias)) + { + verbose ("remove an alias %s from %s\n", alias, parser->def->name); + } + } + } + else + { + alias = parameter; + clearLanguageAliases (language); + addLanguageAlias(language, alias); + verbose ("set alias %s to %s\n", alias, parser->def->name); + } + +} + +extern bool processAliasOption ( + const char *const option, const char *const parameter) +{ + 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; +} + +static void printMaps (const langType language, langmapType type) +{ + const parserObject* parser; + unsigned int 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 (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 struct colprintTable *mapColprintTableNew (langmapType type) +{ + 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 + { + AssertNotReached (); + return NULL; + } +} + +static void mapColprintAddLanguage (struct colprintTable * table, + langmapType type, + const parserObject* parser) +{ + struct colprintLine * line; + unsigned int count; + unsigned int i; + + if ((type & LMAP_PATTERN) && (0 < (count = stringListCount (parser->currentPatterns)))) + { + 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); + + 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; + Assert (0 <= language && language < (int) LanguageCount); + lang = ltable [language]; + + if (lang->invisible) + return; + + if (lang->kindTable != NULL || (lang->method & METHOD_REGEX)) + printf ("%s%s\n", lang->name, isLanguageEnabled (lang->id) ? "" : " [disabled]"); +} + +extern void printLanguageList (void) +{ + unsigned int i; + parserDefinition **ltable; + + ltable = xMalloc (LanguageCount, parserDefinition*); + for (i = 0 ; i < LanguageCount ; ++i) + ltable[i] = LanguageTable[i].def; + qsort (ltable, LanguageCount, sizeof (parserDefinition*), compareParsersByName); + + for (i = 0 ; i < LanguageCount ; ++i) + printLanguage (i, ltable); + + 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 +*/ + +static rescanReason createTagsForFile (const langType language, + const unsigned int passCount) +{ + 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 unsigned int parserCorkFlags (parserDefinition *parser) +{ + subparser *tmp; + unsigned int r = 0; + + r |= parser->useCork; + + if (hasLanguageScopeActionInRegex (parser->id) + || parser->requestAutomaticFQTag) + r |= CORK_QUEUE; + + pushLanguage (parser->id); + foreachSubparser(tmp, true) + { + langType t = getSubparserLanguage (tmp); + r |= parserCorkFlags (LanguageTable[t].def); + } + 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); +} + +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) +{ + bool tagFileResized = false; + unsigned long numTags = numTagsAdded (); + MIOPos tagfpos; + int lastPromise = getLastPromise (); + unsigned int passCount = 0; + rescanReason whyRescan; + parserObject *parser; + unsigned int corkFlags; + bool useCork = false; + + initializeParser (language); + parser = &(LanguageTable [language]); + + setupLanguageSubparsersInUse (language); + + corkFlags = parserCorkFlags (parser->def); + useCork = corkFlags & CORK_QUEUE; + if (useCork) + corkTagFile(corkFlags); + + addParserPseudoTags (language); + initializeParserStats (parser); + tagFilePosition (&tagfpos); + + anonResetMaybe (parser); + + while ( ( whyRescan = + createTagsForFile (language, ++passCount) ) + != RESCAN_NONE) + { + if (useCork) + { + uncorkTagFile(); + corkTagFile(corkFlags); + } + + + if (whyRescan == RESCAN_FAILED) + { + /* Restore prior state of tag file. + */ + setTagFilePosition (&tagfpos); + setNumTagsAdded (numTags); + writerRescanFailed (numTags); + tagFileResized = true; + breakPromisesAfter(lastPromise); + } + else if (whyRescan == RESCAN_APPEND) + { + tagFilePosition (&tagfpos); + numTags = numTagsAdded (); + lastPromise = getLastPromise (); + } + } + + /* 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; +} + +extern bool runParserInNarrowedInputStream (const langType language, + unsigned long startLine, long startCharOffset, + unsigned long endLine, long endCharOffset, + unsigned long sourceLineOffset, + int promise) +{ + 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, + promise); + tagFileResized = createTagsWithFallback1 (language, NULL); + popNarrowedInputStream (); + return tagFileResized; + +} + +static bool createTagsWithFallback ( + const char *const fileName, const langType language, + MIO *mio, bool *failureInOpenning) +{ + langType exclusive_subparser = LANG_IGNORE; + bool tagFileResized = false; + + Assert (0 <= language && language < (int) LanguageCount); + + if (!openInputFile (fileName, language, mio)) + { + *failureInOpenning = true; + return false; + } + *failureInOpenning = false; + + tagFileResized = createTagsWithFallback1 (language, + &exclusive_subparser); + tagFileResized = forcePromises()? true: tagFileResized; + + pushLanguage ((exclusive_subparser == LANG_IGNORE) + ? language + : exclusive_subparser); + makeFileTag (fileName); + popLanguage (); + closeInputFile (); + + return tagFileResized; +} + +static void printGuessedParser (const char* const fileName, langType language) +{ + const char *parserName; + + if (language == LANG_IGNORE) + { + Option.printLanguage = ((int)true) + 1; + parserName = RSV_NONE; + } + else + { + parserName = getLanguageName (language); + } + + printf("%s: %s\n", fileName, parserName); +} + +#ifdef HAVE_ICONV +static char **EncodingMap; +static unsigned int EncodingMapMax; + +static void addLanguageEncoding (const langType language, + const char *const encoding) +{ + if (language > EncodingMapMax || EncodingMapMax == 0) + { + int i; + int istart = (EncodingMapMax == 0)? 0: EncodingMapMax + 1; + EncodingMap = xRealloc (EncodingMap, (language + 1), char*); + for (i = istart; i <= language ; ++i) + { + EncodingMap [i] = NULL; + } + EncodingMapMax = language; + } + if (EncodingMap [language]) + eFree (EncodingMap [language]); + EncodingMap [language] = eStrdup(encoding); + if (!Option.outputEncoding) + Option.outputEncoding = eStrdup("UTF-8"); +} + +extern bool processLanguageEncodingOption (const char *const option, const char *const parameter) +{ + langType language; + + language = getLanguageComponentInOption (option, "input-encoding-"); + if (language == LANG_IGNORE) + return false; + + addLanguageEncoding (language, parameter); + return true; +} + +extern void freeEncodingResources (void) +{ + if (EncodingMap) + { + unsigned int i; + for (i = 0 ; i <= EncodingMapMax ; ++i) + { + if (EncodingMap [i]) + eFree (EncodingMap [i]); + } + 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 + +static void addParserPseudoTags (langType language) +{ + parserObject *parser = LanguageTable + language; + if (!parser->pseudoTagPrinted) + { + for (int i = 0; i < PTAG_COUNT; i++) + { + if (isPtagParserSpecific (i)) + makePtagIfEnabled (i, language, parser); + } + parser->pseudoTagPrinted = 1; + } +} + +extern bool doesParserRequireMemoryStream (const langType language) +{ + Assert (0 <= language && language < (int) LanguageCount); + parserDefinition *const lang = LanguageTable [language].def; + unsigned int i; + + if (lang->tagXpathTableCount > 0 + || lang->useMemoryStreamInput) + { + verbose ("%s requires a memory stream for input\n", lang->name); + 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, NULL); + TRACE_LEAVE(); + return bRet; +} + +static bool parseMio (const char *const fileName, langType language, MIO* mio, bool useSourceFileTagPath, + void *clientData) +{ + bool tagFileResized = false; + bool failureInOpenning = false; + + setupWriter (clientData); + + setupAnon (); + + initParserTrashBox (); + + tagFileResized = createTagsWithFallback (fileName, language, mio, &failureInOpenning); + + finiParserTrashBox (); + + teardownAnon (); + + if (useSourceFileTagPath && (!failureInOpenning)) + 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; + struct GetLanguageRequest req = { + .type = mio? GLR_REUSE: GLR_OPEN, + .fileName = fileName, + .mio = mio, + }; + + language = getFileLanguageForRequest (&req); + Assert (language != LANG_AUTO); + + if (Option.printLanguage) + { + printGuessedParser (fileName, language); + return tagFileResized; + } + + if (language == LANG_IGNORE) + verbose ("ignoring %s (unknown language/language disabled)\n", + fileName); + else { - if (Option.withListHeader && allKindFields) - printKindListHeader (false, Option.machinable); + Assert(isLanguageEnabled (language)); + + if (Option.filter && ! Option.interactive) + openTagFile (); + +#ifdef HAVE_ICONV + /* TODO: checkUTF8BOM can be used to update the encodings. */ + openConverter (getLanguageEncoding (language), Option.outputEncoding); +#endif + tagFileResized = parseMio (fileName, language, req.mio, true, clientData); + if (Option.filter && ! Option.interactive) + closeTagFile (tagFileResized); + addTotals (1, 0L, 0L); - printKinds (language, allKindFields, false); +#ifdef HAVE_ICONV + closeConverter (); +#endif } + + if (req.type == GLR_OPEN && req.mio) + mio_unref (req.mio); + + return tagFileResized; } -static void processLangAliasOption (const langType language, - const char *const parameter) +extern bool parseRawBuffer(const char *fileName, unsigned char *buffer, + size_t bufferSize, const langType language, void *clientData) { - const char* alias; - const parserDefinition * lang; + MIO *mio = NULL; + bool r; - Assert (0 <= language && language < (int) LanguageCount); - Assert (parameter); - Assert (parameter[0]); - lang = LanguageTable [language]; + if (buffer) + mio = mio_new_memory (buffer, bufferSize, NULL, NULL); - if (parameter[0] == '+') - { - alias = parameter + 1; - addLanguageAlias(language, alias); - verbose ("add alias %s to %s\n", alias, lang->name); - } - else if (parameter[0] == '-') - { - if (lang->currentAliases) - { - alias = parameter + 1; - if (stringListDeleteItemExtension (lang->currentAliases, alias)) - { - verbose ("remove alias %s from %s\n", alias, lang->name); - } - } - } - else - { - alias = parameter; - clearLanguageAliases (language); - addLanguageAlias(language, alias); - verbose ("set alias %s to %s\n", alias, lang->name); - } + r = parseMio (fileName, language, mio, false, clientData); + + if (buffer) + mio_unref (mio); + return r; } -extern bool processAliasOption ( - const char *const option, const char *const parameter) +static void matchLanguageMultilineRegexCommon (const langType language, + bool (* func) (struct lregexControlBlock *, const vString* const), + const vString* const allLines) { - langType language; + subparser *tmp; - language = getLanguageComponentInOption (option, "alias-"); - if (language == LANG_IGNORE) - return false; + func ((LanguageTable + language)->lregexControlBlock, allLines); + foreachSubparser(tmp, true) + { + langType t = getSubparserLanguage (tmp); + enterSubparser (tmp); + matchLanguageMultilineRegexCommon (t, func, allLines); + leaveSubparser (); + } +} - processLangAliasOption (language, parameter); - return true; +extern void matchLanguageMultilineRegex (const langType language, + const vString* const allLines) +{ + matchLanguageMultilineRegexCommon(language, matchMultilineRegex, allLines); } -static void printMaps (const langType language, langmapType type) +extern void matchLanguageMultitableRegex (const langType language, + const vString* const allLines) { - const parserDefinition* lang; - 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) - printf (" %s", vStringValue ( - stringListItem (lang->currentPatterns, i))); - putchar ('\n'); + matchLanguageMultilineRegexCommon(language, matchMultitableRegex, allLines); } -static void printAliases (const langType language, FILE *fp) +extern void processLanguageMultitableExtendingOption (langType language, const char *const parameter) { - const parserDefinition* lang; - unsigned int i; - Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; + 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); - if (lang->currentAliases != NULL) - for (i = 0 ; i < stringListCount (lang->currentAliases) ; ++i) - fprintf (fp, " %s", vStringValue ( - stringListItem (lang->currentAliases, i))); + 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); } -extern void printLanguageMaps (const langType language, langmapType type) +static bool lregexQueryParserAndSubparsers (const langType language, bool (* predicate) (struct lregexControlBlock *)) { - if (language == LANG_AUTO) + bool r; + subparser *tmp; + + r = predicate ((LanguageTable + language)->lregexControlBlock); + if (!r) { - unsigned int i; - for (i = 0 ; i < LanguageCount ; ++i) - printMaps (i, type); + foreachSubparser(tmp, true) + { + langType t = getSubparserLanguage (tmp); + enterSubparser (tmp); + r = lregexQueryParserAndSubparsers (t, predicate); + leaveSubparser (); + + if (r) + break; + } } - else - printMaps (language, type); + + return r; } -extern void printLanguageAliases (const langType language) +extern bool hasLanguageMultilineRegexPatterns (const langType language) { - if (language == LANG_AUTO) - { - unsigned int i; - for (i = 0 ; i < LanguageCount ; ++i) - printLanguageAliases (i); - } - else - { - const parserDefinition* lang; - - Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - printf ("%-8s", lang->name); - printAliases (language, stdout); - putchar ('\n'); - } + return lregexQueryParserAndSubparsers (language, regexNeedsMultilineBuffer); } -static void printLanguage (const langType language, parserDefinition** ltable) + +extern void addLanguageCallbackRegex (const langType language, const char *const regex, const char *const flags, + const regexCallback callback, bool *disabled, void *userData) { - const parserDefinition* lang; - Assert (0 <= language && language < (int) LanguageCount); - lang = ltable [language]; + addCallbackRegex ((LanguageTable +language)->lregexControlBlock, regex, flags, callback, disabled, userData); +} - if (lang->invisible) - return; +extern bool hasLanguageScopeActionInRegex (const langType language) +{ + bool hasScopeAction; - if (lang->method & METHOD_XCMD) - initializeParser (lang->id); + pushLanguage (language); + hasScopeAction = lregexQueryParserAndSubparsers (language, hasScopeActionInRegex); + popLanguage (); - if (lang->kindTable != NULL || (lang->method & METHOD_REGEX) || (lang->method & METHOD_XCMD)) - printf ("%s%s\n", lang->name, isLanguageEnabled (lang->id) ? "" : " [disabled]"); + return hasScopeAction; } -extern void printLanguageList (void) +extern void matchLanguageRegex (const langType language, const vString* const line) { - unsigned int i; - parserDefinition **ltable; + subparser *tmp; - ltable = xMalloc (LanguageCount, parserDefinition*); - memcpy (ltable, LanguageTable, sizeof (parserDefinition*) * LanguageCount); - qsort (ltable, LanguageCount, sizeof (parserDefinition*), compareParsersByName); + matchRegex ((LanguageTable + language)->lregexControlBlock, line); + foreachSubparser(tmp, true) + { + langType t = getSubparserLanguage (tmp); + enterSubparser (tmp); + matchLanguageRegex (t, line); + leaveSubparser (); + } +} - for (i = 0 ; i < LanguageCount ; ++i) - printLanguage (i, ltable); +extern bool processLanguageRegexOption (langType language, + enum regexParserType regptype, + const char *const parameter) +{ + processTagRegexOption ((LanguageTable +language)->lregexControlBlock, + regptype, parameter); - eFree (ltable); + return true; } -/* -* File parsing -*/ - -static rescanReason createTagsForFile (const langType language, - const unsigned int passCount) +extern bool processTabledefOption (const char *const option, const char *const parameter) { - parserDefinition *const lang = LanguageTable [language]; - rescanReason rescan = RESCAN_NONE; - - resetInputFile (language); + langType language; - Assert (lang->parser || lang->parser2); + language = getLanguageComponentInOption (option, "_tabledef-"); + if (language == LANG_IGNORE) + return false; - if (lang->parser != NULL) - lang->parser (); - else if (lang->parser2 != NULL) - rescan = lang->parser2 (passCount); + if (parameter == NULL || parameter[0] == '\0') + error (FATAL, "A parameter is needed after \"%s\" option", option); - return rescan; + addRegexTable((LanguageTable +language)->lregexControlBlock, parameter); + return true; } -#ifndef CTAGS_LIB -static bool createTagsWithFallback1 (const langType language) +extern void useRegexMethod (const langType language) { - bool tagFileResized = false; - unsigned long numTags = numTagsAdded (); - MIOPos tagfpos; - int lastPromise = getLastPromise (); - unsigned int passCount = 0; - rescanReason whyRescan; + parserDefinition* lang; - initializeParser (language); - if (LanguageTable [language]->useCork) - corkTagFile(); + Assert (0 <= language && language < (int) LanguageCount); + lang = LanguageTable [language].def; + lang->method |= METHOD_REGEX; +} - addParserPseudoTags (language); - tagFilePosition (&tagfpos); +static void useXpathMethod (const langType language) +{ + parserDefinition* lang; - anonResetMaybe (LanguageTable [language]); + Assert (0 <= language && language < (int) LanguageCount); + lang = LanguageTable [language].def; + lang->method |= METHOD_XPATH; +} - while ( ( whyRescan = - createTagsForFile (language, ++passCount) ) - != RESCAN_NONE) - { - if (LanguageTable [language]->useCork) - { - uncorkTagFile(); - corkTagFile(); - } +static void installTagRegexTable (const langType language) +{ + parserObject* parser; + parserDefinition* lang; + unsigned int i; + Assert (0 <= language && language < (int) LanguageCount); + parser = LanguageTable + language; + lang = parser->def; - if (whyRescan == RESCAN_FAILED) - { - /* Restore prior state of tag file. - */ - setTagFilePosition (&tagfpos); - setNumTagsAdded (numTags); - tagFileResized = true; - breakPromisesAfter(lastPromise); - } - else if (whyRescan == RESCAN_APPEND) + + if (lang->tagRegexTable != NULL) + { + for (i = 0; i < lang->tagRegexCount; ++i) { - tagFilePosition (&tagfpos); - numTags = numTagsAdded (); - lastPromise = getLastPromise (); + 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)); } } +} - if (LanguageTable [language]->useCork) - uncorkTagFile(); +static void installKeywordTable (const langType language) +{ + parserDefinition* lang; + unsigned int i; - return tagFileResized; -} + Assert (0 <= language && language < (int) LanguageCount); + lang = LanguageTable [language].def; -#else + if (lang->keywordTable != NULL) + { + for (i = 0; i < lang->keywordCount; ++i) + addKeyword (lang->keywordTable [i].name, + language, + lang->keywordTable [i].id); + } +} -static bool createTagsWithFallback1 (const langType language, - passStartCallback passCallback, void *userData) +static void installTagXpathTable (const langType language) { - int lastPromise = getLastPromise (); - unsigned int passCount = 0; - rescanReason whyRescan; - - if (LanguageTable [language]->useCork) - corkTagFile(); + parserDefinition* lang; + unsigned int i, j; - anonResetMaybe (LanguageTable [language]); + Assert (0 <= language && language < (int) LanguageCount); + lang = LanguageTable [language].def; - passCallback(userData); - while ( ( whyRescan = - createTagsForFile (language, ++passCount) ) - != RESCAN_NONE) + if (lang->tagXpathTableTable != NULL) { - if (LanguageTable [language]->useCork) - { - uncorkTagFile(); - corkTagFile(); - } + for (i = 0; i < lang->tagXpathTableCount; ++i) + for (j = 0; j < lang->tagXpathTableTable[i].count; ++j) + addTagXpath (language, lang->tagXpathTableTable[i].table + j); + useXpathMethod (language); + } +} - if (whyRescan == RESCAN_FAILED) - breakPromisesAfter(lastPromise); - else if (whyRescan == RESCAN_APPEND) - lastPromise = getLastPromise (); +static void uninstallTagXpathTable (const langType language) +{ + parserDefinition* lang; + unsigned int i, j; - if (passCount < 3) - passCallback(userData); - else - break; + 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); } +} - if (LanguageTable [language]->useCork) - uncorkTagFile(); +const tagXpathTableTable *getXpathTableTable (const langType language, unsigned int nth) +{ + parserDefinition* lang; - return false; + Assert (0 <= language && language < (int) LanguageCount); + lang = LanguageTable [language].def; + + Assert (nth < lang->tagXpathTableCount); + return lang->tagXpathTableTable + nth; } -#endif -extern bool runParserInNarrowedInputStream (const langType language, - unsigned long startLine, int startCharOffset, - unsigned long endLine, int endCharOffset, - unsigned long sourceLineOffset) +extern unsigned int getXpathFileSpecCount (const langType language) { - bool tagFileResized = false; - pushNarrowedInputStream (language, - startLine, startCharOffset, - endLine, endCharOffset, - sourceLineOffset); -#ifndef CTAGS_LIB - tagFileResized = createTagsWithFallback1 (language); -#else - /* Simple parsing without rescans - not used by any sub-parsers anyway */ - if (LanguageTable [language]->useCork) - corkTagFile(); - createTagsForFile (language, 1); - if (LanguageTable [language]->useCork) - uncorkTagFile(); -#endif - popNarrowedInputStream (); - return tagFileResized; + parserDefinition* lang; + + Assert (0 <= language && language < (int) LanguageCount); + lang = LanguageTable [language].def; + return lang->xpathFileSpecCount; } -#ifndef CTAGS_LIB -static bool createTagsWithFallback ( - const char *const fileName, const langType language, - MIO *mio) +extern xpathFileSpec* getXpathFileSpec (const langType language, unsigned int nth) { - bool tagFileResized = false; + parserDefinition* lang; Assert (0 <= language && language < (int) LanguageCount); + lang = LanguageTable [language].def; - if (!openInputFile (fileName, language, mio)) - return false; + Assert (nth < lang->xpathFileSpecCount); + return lang->xpathFileSpecs + nth; +} - tagFileResized = createTagsWithFallback1 (language); - tagFileResized = forcePromises()? true: tagFileResized; +extern bool makeKindSeparatorsPseudoTags (const langType language, + const ptagDesc *pdesc) +{ + parserObject* parser; + parserDefinition* lang; + struct kindControlBlock *kcb; + kindDefinition *kind; + unsigned int kindCount; + unsigned int i, j; - makeFileTag (fileName); - closeInputFile (); + bool r = false; - return tagFileResized; -} + Assert (0 <= language && language < (int) LanguageCount); + parser = LanguageTable + language; + lang = parser->def; + kcb = parser->kindControlBlock; + kindCount = countKinds(kcb); -#else + if (kindCount == 0) + return r; -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))) + vString *sepval = vStringNew(); + for (i = 0; i < kindCount; ++i) { - initParserTrashBox (); - clearParsersUsedInCurrentInput (); - setTagEntryFunction(tagCallback, userData); - createTagsWithFallback1 (language, passCallback, userData); - forcePromises (); - closeInputFile (); - finiParserTrashBox (); + kind = getKind (kcb, i); + for (j = 0; j < kind->separatorCount; ++j) + { + char name[3] = {[1] = '\0', [2] = '\0'}; + const kindDefinition *upperKind; + const scopeSeparator *sep; + + sep = kind->separators + j; + + if (sep->parentKindIndex == KIND_WILDCARD_INDEX) + { + name[0] = KIND_WILDCARD_LETTER; + name[1] = kind->letter; + } + else if (sep->parentKindIndex == KIND_GHOST_INDEX) + { + /* This is root separator: no upper item is here. */ + name[0] = kind->letter; + } + else + { + upperKind = getLanguageKind (language, + sep->parentKindIndex); + if (!upperKind) + continue; + + name[0] = upperKind->letter; + name[1] = kind->letter; + } + + + vStringClear (sepval); + vStringCatSWithEscaping (sepval, sep->separator); + + r = writePseudoTag (pdesc, vStringValue (sepval), + name, lang->name) || r; + } } - else - error (WARNING, "Unable to open %s", fileName); -} + vStringDelete (sepval); -extern const parserDefinition *getParserDefinition (langType language) -{ - Assert (0 <= language && language < (int) LanguageCount); - return LanguageTable[language]; + return r; } -#endif +struct makeKindDescriptionPseudoTagData { + const char* langName; + const ptagDesc *pdesc; + bool written; +}; -#ifdef HAVE_COPROC -static bool createTagsWithXcmd ( - const char *const fileName, const langType language, - MIO *mio) +static bool makeKindDescriptionPseudoTag (kindDefinition *kind, + void *user_data) { - bool tagFileResized = false; + struct makeKindDescriptionPseudoTagData *data = user_data; + vString *letter_and_name; + vString *description; + const char *d; - if (openInputFile (fileName, language, mio)) - { - tagFileResized = invokeXcmd (fileName, language); + letter_and_name = vStringNew (); + description = vStringNew (); - /* TODO: File.lineNumber must be adjusted for the case - * Option.printTotals is non-zero. */ - closeInputFile (); - } + vStringPut (letter_and_name, kind -> letter); + vStringPut (letter_and_name, ','); + vStringCatS (letter_and_name, kind -> name); - return tagFileResized; + d = kind->description? kind->description: kind->name; + vStringCatSWithEscapingAsPattern (description, d); + data->written |= writePseudoTag (data->pdesc, vStringValue (letter_and_name), + vStringValue (description), + data->langName); + + vStringDelete (description); + vStringDelete (letter_and_name); + + return false; } -#endif -static void printGuessedParser (const char* const fileName, langType language) +extern bool makeKindDescriptionsPseudoTags (const langType language, + const ptagDesc *pdesc) { - const char *parserName; + parserObject *parser; + struct kindControlBlock *kcb; + parserDefinition* lang; + kindDefinition *kind; + unsigned int kindCount, i; + struct makeKindDescriptionPseudoTagData data; - if (language == LANG_IGNORE) - { - Option.printLanguage = ((int)true) + 1; - parserName = "NONE"; - } - else - parserName = LanguageTable [language]->name; + Assert (0 <= language && language < (int) LanguageCount); + parser = LanguageTable + language; + kcb = parser->kindControlBlock; + lang = parser->def; - printf("%s: %s\n", fileName, parserName); -} + kindCount = countKinds(kcb); -#ifdef HAVE_ICONV -static char **EncodingMap; -static unsigned int EncodingMapMax; + data.langName = lang->name; + data.pdesc = pdesc; + data.written = false; -static void addLanguageEncoding (const langType language, - const char *const encoding) -{ - if (language > EncodingMapMax || EncodingMapMax == 0) + for (i = 0; i < kindCount; ++i) { - int i; - int istart = (EncodingMapMax == 0)? 0: EncodingMapMax + 1; - EncodingMap = xRealloc (EncodingMap, (language + 1), char*); - for (i = istart; i <= language ; ++i) - { - EncodingMap [i] = NULL; - } - EncodingMapMax = language; + if (!isLanguageKindEnabled (language, i)) + continue; + + kind = getKind (kcb, i); + makeKindDescriptionPseudoTag (kind, &data); } - if (EncodingMap [language]) - eFree (EncodingMap [language]); - EncodingMap [language] = eStrdup(encoding); - if (!Option.outputEncoding) - Option.outputEncoding = eStrdup("UTF-8"); + + return data.written; } -extern bool processLanguageEncodingOption (const char *const option, const char *const parameter) +static bool makeFieldDescriptionPseudoTag (const langType language, + fieldType f, + const ptagDesc *pdesc) { - langType language; + vString *description; + const char *name = getFieldName (f); - language = getLanguageComponentInOption (option, "input-encoding-"); - if (language == LANG_IGNORE) + if (name == NULL || name [0] == '\0') return false; - addLanguageEncoding (language, parameter); - return true; + description = vStringNew (); + vStringCatSWithEscapingAsPattern (description, + getFieldDescription (f)); + + bool r = writePseudoTag (pdesc, name, + vStringValue (description), + language == LANG_IGNORE? NULL: getLanguageName (language)); + + vStringDelete (description); + return r; } -extern void freeEncodingResources (void) +extern bool makeFieldDescriptionsPseudoTags (const langType language, + const ptagDesc *pdesc) { - if (EncodingMap) + bool written = false; + for (int i = 0; i < countFields (); i++) { - int i; - for (i = 0 ; i <= EncodingMapMax ; ++i) + if (getFieldOwner (i) == language + && isFieldEnabled (i)) { - if (EncodingMap [i]) - eFree (EncodingMap [i]); + if (makeFieldDescriptionPseudoTag (language, i, pdesc)) + written = true; } - free(EncodingMap); } - if (Option.inputEncoding) - eFree (Option.inputEncoding); - if (Option.outputEncoding) - eFree (Option.outputEncoding); + return written; } -#endif -#ifndef CTAGS_LIB -static void addParserPseudoTags (langType language) +static bool makeExtraDescriptionPseudoTag (const langType language, + xtagType x, + const ptagDesc *pdesc) { - if (!LanguageTable[language]->pseudoTagPrinted) - { - makePtagIfEnabled (PTAG_KIND_DESCRIPTION, &language); - makePtagIfEnabled (PTAG_KIND_SEPARATOR, &language); - - LanguageTable[language]->pseudoTagPrinted = 1; - } -} -#endif + vString *description; + const char *name = getXtagName (x); -extern bool doesParserRequireMemoryStream (const langType language) -{ - Assert (0 <= language && language < (int) LanguageCount); - parserDefinition *const lang = LanguageTable [language]; + if (name == NULL || name [0] == '\0') + return false; - if (lang->tagXpathTableCount > 0) - return true; + description = vStringNew (); + vStringCatSWithEscapingAsPattern (description, + getXtagDescription (x)); - if (lang->method & METHOD_YAML) - return true; + bool r = writePseudoTag (pdesc, name, + vStringValue (description), + language == LANG_IGNORE? NULL: getLanguageName (language)); - return false; + vStringDelete (description); + return r; } -extern bool parseFile (const char *const fileName) +extern bool makeExtraDescriptionsPseudoTags (const langType language, + const ptagDesc *pdesc) { - bool tagFileResized = false; - langType language; - MIO *mio; - - language = getFileLanguageAndKeepMIO (fileName, &mio); - Assert (language != LANG_AUTO); - - if (Option.printLanguage) - { - printGuessedParser (fileName, language); - return tagFileResized; - } - - if (language == LANG_IGNORE) - verbose ("ignoring %s (unknown language/language disabled)\n", - fileName); - else if (! isLanguageEnabled (language)) + bool written = false; + for (int i = 0; i < countXtags (); i++) { - /* 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); + if (getXtagOwner (i) == language + && isXtagEnabled (i)) + { + if (makeExtraDescriptionPseudoTag (language, i, pdesc)) + written = true; + } } - else - { - if (Option.filter) - openTagFile (); - -#ifdef HAVE_ICONV - openConverter (EncodingMap && language <= EncodingMapMax && - EncodingMap [language] ? - EncodingMap[language] : Option.inputEncoding, Option.outputEncoding); -#endif - - setupWriter (); - - clearParsersUsedInCurrentInput (); - -#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); - - if (Option.filter) - closeTagFile (tagFileResized); - addTotals (1, 0L, 0L); + return written; +} -#ifdef HAVE_ICONV - closeConverter (); -#endif - if (mio) - mio_free (mio); - return tagFileResized; - } +/* +* 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. +* +* Anonymous name generator +*/ +static ptrArray *parsersUsedInCurrentInput; - if (mio) - mio_free (mio); - return tagFileResized; +static void setupAnon (void) +{ + parsersUsedInCurrentInput = ptrArrayNew (NULL); } -extern void useRegexMethod (const langType language) +static void teardownAnon (void) { - parserDefinition* lang; - - Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - lang->method |= METHOD_REGEX; + ptrArrayDelete (parsersUsedInCurrentInput); } -extern void useXcmdMethod (const langType language) +static void anonResetMaybe (parserObject *parser) { - parserDefinition* lang; + if (ptrArrayHas (parsersUsedInCurrentInput, parser)) + return; - Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - lang->method |= METHOD_XCMD; + parser -> anonymousIdentiferId = 0; + ptrArrayAdd (parsersUsedInCurrentInput, parser); } -static void useXpathMethod (const langType language) +static unsigned int anonHash(const unsigned char *str) { - parserDefinition* lang; + unsigned int hash = 5381; + int c; - Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - lang->method |= METHOD_XPATH; + while((c = *str++)) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + + return hash ; } -extern void notifyAvailabilityXcmdMethod (const langType language) +extern void anonHashString (const char *filename, char buf[9]) { - parserDefinition* lang; - - Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - lang->method |= METHOD_XCMD_AVAILABLE; + sprintf(buf, "%08x", anonHash((const unsigned char *)filename)); } -static void installTagRegexTable (const langType language) + +extern void anonGenerate (vString *buffer, const char *prefix, int kind) { - parserDefinition* lang; - unsigned int i; + parserObject* parser = LanguageTable + getInputLanguage (); + parser -> anonymousIdentiferId ++; - Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; + char szNum[32]; +#if 0 + char buf [9]; + vStringCopyS(buffer, prefix); - 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)); - } + 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); } -static void installKeywordTable (const langType language) +extern vString *anonGenerateNew (const char *prefix, int kind) { - parserDefinition* lang; - unsigned int i; - - Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; + vString *buffer = vStringNew (); - if (lang->keywordTable != NULL) - { - for (i = 0; i < lang->keywordCount; ++i) - addKeyword (lang->keywordTable [i].name, - language, - lang->keywordTable [i].id); - } + anonGenerate (buffer, prefix, kind); + return buffer; } -static void installTagXpathTable (const langType language) + +extern void applyParameter (const langType language, const char *name, const char *args) { - parserDefinition* lang; - unsigned int i, j; + parserDefinition* parser; + Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - if (lang->tagXpathTableTable != NULL) + initializeParserOne (language); + parser = LanguageTable [language].def; + + if (parser->parameterHandlerTable) { - for (i = 0; i < lang->tagXpathTableCount; ++i) - for (j = 0; j < lang->tagXpathTableTable[i].count; ++j) - addTagXpath (language, lang->tagXpathTableTable[i].table + j); - useXpathMethod (language); + 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 bool makeKindSeparatorsPseudoTags (const langType language, - const ptagDesc *pdesc) +extern subparser *getNextSubparser(subparser *last, + bool includingNoneCraftedParser) { - parserDefinition* lang; - kindDefinition *kinds; - unsigned int kindCount; - unsigned int i, j; + langType lang = getInputLanguage (); + parserObject *parser = LanguageTable + lang; + subparser *r; + langType t; - bool r = false; + if (last == NULL) + r = getFirstSubparser(parser->slaveControlBlock); + else + r = last->next; - Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - kinds = lang->kindTable; - kindCount = lang->kindCount; + if (r == NULL) + return r; - if (kinds == NULL) + t = getSubparserLanguage(r); + if (isLanguageEnabled (t) && + (includingNoneCraftedParser + || ((((LanguageTable + t)->def->method) & METHOD_NOT_CRAFTED) == 0))) return r; + else + return getNextSubparser (r, includingNoneCraftedParser); +} - for (i = 0; i < kindCount; ++i) - { - static vString *sepval; +extern slaveParser *getNextSlaveParser(slaveParser *last) +{ + langType lang = getInputLanguage (); + parserObject *parser = LanguageTable + lang; + slaveParser *r; - if (!sepval) - sepval = vStringNew (); + if (last == NULL) + r = getFirstSlaveParser(parser->slaveControlBlock); + else + r = last->next; - for (j = 0; j < kinds[i].separatorCount; ++j) - { - char name[5] = {[0] = '/', [3] = '/', [4] = '\0'}; - const kindDefinition *upperKind; - const scopeSeparator *sep; + return r; +} - sep = kinds[i].separators + j; +extern void scheduleRunningBaseparser (int dependencyIndex) +{ + langType current = getInputLanguage (); + parserDefinition *current_parser = LanguageTable [current].def; + parserDependency *dep = NULL; - if (sep->parentKindIndex == KIND_WILDCARD_INDEX) - { - name[1] = KIND_WILDCARD; - name[2] = kinds[i].letter; - } - else if (sep->parentKindIndex == KIND_GHOST_INDEX) + if (dependencyIndex == RUN_DEFAULT_SUBPARSERS) + { + for (unsigned int i = 0; i < current_parser->dependencyCount; ++i) + if (current_parser->dependencies[i].type == DEPTYPE_SUBPARSER) { - /* This is root separator: no upper item is here. */ - name[1] = kinds[i].letter; - name[2] = name[3]; - name[3] = '\0'; + dep = current_parser->dependencies + i; + break; } - else - { - upperKind = langKindDefinition (language, - sep->parentKindIndex); - if (!upperKind) - continue; + } + else + dep = current_parser->dependencies + dependencyIndex; - name[1] = upperKind->letter; - name[2] = kinds[i].letter; - } + if (dep == NULL) + return; + const char *base_name = dep->upperParser; + langType base = getNamedLanguage (base_name, 0); + parserObject *base_parser = LanguageTable + base; - vStringClear (sepval); - vStringCatSWithEscaping (sepval, sep->separator); + if (dependencyIndex == RUN_DEFAULT_SUBPARSERS) + useDefaultSubparsers(base_parser->slaveControlBlock); + else + useSpecifiedSubparser (base_parser->slaveControlBlock, + dep->data); - r = writePseudoTag (pdesc, vStringValue (sepval), - name, lang->name) || r; + 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"); } - return r; + + makePromise(base_name, THIN_STREAM_SPEC); } -struct makeKindDescriptionPseudoTagData { - const char* langName; - const ptagDesc *pdesc; - bool written; -}; +extern bool isParserMarkedNoEmission (void) +{ + langType lang = getInputLanguage(); + parserObject *parser = LanguageTable + lang; -static bool makeKindDescriptionPseudoTag (kindDefinition *kind, - void *user_data) + return parser->dontEmit; +} + + +extern subparser* getSubparserRunningBaseparser (void) { - struct makeKindDescriptionPseudoTagData *data = user_data; - vString *letter_and_name; - vString *description; - const char *d; + langType current = getInputLanguage (); + parserObject *current_parser = LanguageTable + current; + subparser *s = getFirstSubparser (current_parser->slaveControlBlock); - letter_and_name = vStringNew (); - description = vStringNew (); + if (s && s->schedulingBaseparserExplicitly) + return s; + else + return NULL; +} - vStringPut (letter_and_name, kind -> letter); - vStringPut (letter_and_name, ','); - vStringCatS (letter_and_name, kind -> name); +extern void printLanguageSubparsers (const langType language, + bool withListHeader, bool machinable, FILE *fp) +{ + for (int i = 0; i < (int) LanguageCount; i++) + initializeParserOne (i); - d = kind->description? kind->description: kind->name; - vStringPut (description, '/'); - vStringCatSWithEscapingAsPattern (description, d); - vStringPut (description, '/'); - data->written |= writePseudoTag (data->pdesc, vStringValue (letter_and_name), - vStringValue (description), - data->langName); + struct colprintTable * table = subparserColprintTableNew(); + parserObject *parser; - vStringDelete (description); - vStringDelete (letter_and_name); + if (language == LANG_AUTO) + { + for (int i = 0; i < (int) LanguageCount; i++) + { + parser = LanguageTable + i; + if (parser->def->invisible) + continue; - return false; + subparserColprintAddSubparsers (table, + parser->slaveControlBlock); + } + } + else + { + parser = (LanguageTable + language); + subparserColprintAddSubparsers (table, + parser->slaveControlBlock); + } + + subparserColprintTablePrint (table, + withListHeader, machinable, + fp); + colprintTableDelete (table); } -extern bool makeKindDescriptionsPseudoTags (const langType language, - const ptagDesc *pdesc) +extern void printLangdefFlags (bool withListHeader, bool machinable, FILE *fp) { + struct colprintTable * table; - parserDefinition* lang; - kindDefinition *kinds; - unsigned int kindCount, i; - struct makeKindDescriptionPseudoTagData data; + table = flagsColprintTableNew (); - Assert (0 <= language && language < (int) LanguageCount); - lang = LanguageTable [language]; - kinds = lang->kindTable; - kindCount = lang->kindCount; + flagsColprintAddDefinitions (table, PreLangDefFlagDef, ARRAY_SIZE (PreLangDefFlagDef)); - data.langName = lang->name; - data.pdesc = pdesc; - data.written = false; + flagsColprintTablePrint (table, withListHeader, machinable, fp); + colprintTableDelete(table); +} - for (i = 0; i < kindCount; ++i) - makeKindDescriptionPseudoTag (kinds + i, &data); +extern void printKinddefFlags (bool withListHeader, bool machinable, FILE *fp) +{ + struct colprintTable * table; - foreachRegexKinds (language, makeKindDescriptionPseudoTag, &data); - foreachXcmdKinds (language, makeKindDescriptionPseudoTag, &data); + table = flagsColprintTableNew (); - return data.written; -} + flagsColprintAddDefinitions (table, PreKindDefFlagDef, ARRAY_SIZE (PreKindDefFlagDef)); -/* -* 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. -* -* Anonymous name generator -*/ + flagsColprintTablePrint (table, withListHeader, machinable, fp); + colprintTableDelete(table); +} -static void clearParsersUsedInCurrentInput (void) +extern void printLanguageMultitableStatistics (langType language) { - if (parsersUsedInCurrentInput) - ptrArrayClear (parsersUsedInCurrentInput); - else - parsersUsedInCurrentInput = ptrArrayNew (NULL); + parserObject* const parser = LanguageTable + language; + printMultitableStatistics (parser->lregexControlBlock); } -static void anonResetMaybe (parserDefinition *lang) +extern void addLanguageRegexTable (const langType language, const char *name) { - if (ptrArrayHas (parsersUsedInCurrentInput, lang)) - return; + parserObject* const parser = LanguageTable + language; + addRegexTable (parser->lregexControlBlock, name); +} - lang -> anonumousIdentiferId = 0; - ptrArrayAdd (parsersUsedInCurrentInput, lang); +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); } -/* GEANY DIFF */ -#if 0 -static unsigned int anonHash(const unsigned char *str) +extern bool processPretendOption (const char *const option, const char *const parameter) { - unsigned int hash = 5381; - int c; + langType new_language, old_language; - while((c = *str++)) - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ +#define pretendOptionPrefix "_pretend-" + new_language = getLanguageComponentInOptionFull (option, pretendOptionPrefix, true); + if (new_language == LANG_IGNORE) + return false; - return hash ; + 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 disabling %s\n", + getLanguageNameFull (old_language, true)); + enableLanguage (old_language, false); + + return true; } -#endif -/* GEANY DIFF END */ -extern void anonGenerate (vString *buffer, const char *prefix, int kind) +/* + * The universal fallback parser. + * If any parser doesn't handle the input, this parser is + * used for the input when --languages=+Unknown is given. + * writer-etags enables this parser implicitly. + */ +static parserDefinition *FallbackParser (void) { - parserDefinition* lang = LanguageTable [getInputLanguage ()]; - lang -> anonumousIdentiferId ++; - - char szNum[32]; + parserDefinition *const def = parserNew ("Unknown"); + def->extensions = NULL; + def->kindTable = NULL; + def->kindCount = 0; + + /* A user can extend this parser with --regex-Unknown=... + * or --langdef=MyParser{base=Unknown}. + * + * TODO: if following conditions are met, dontFindTags() + * defined below can be used. + * - any regex pattern is not defined, + * - any sub parser is not defined, and + * - end: field is not enabled. + */ + def->parser = findRegexTags; + def->enabled = 0; + def->method = METHOD_REGEX; + return def; +} - vStringCopyS(buffer, prefix); +/* + * A dummy parser for printing pseudo tags in xref output + */ +static void dontFindTags (void) +{ +} -/* GEANY DIFF */ -/* unsigned int uHash = anonHash((const unsigned char *)getInputFileName()); - sprintf(szNum,"%08x%02x%02x",uHash,lang -> anonumousIdentiferId, kind); */ - sprintf(szNum,"%u", lang -> anonumousIdentiferId); -/* GEANY DIFF END */ +static kindDefinition CtagsKinds[] = { + {true, 'p', "ptag", "pseudo tags"}, +}; - vStringCatS(buffer,szNum); +static parserDefinition *CTagsParser (void) +{ + parserDefinition *const def = parserNew ("UniversalCtags"); + def->extensions = NULL; + def->kindTable = CtagsKinds; + def->kindCount = ARRAY_SIZE(CtagsKinds); + def->parser = dontFindTags; + def->invisible = true; + return def; } -#ifndef CTAGS_LIB /* * A parser for CTagsSelfTest (CTST) */ +#define SELF_TEST_PARSER "CTagsSelfTest" +#if defined(DEBUG) && defined(HAVE_SECCOMP) +extern void getppid(void); +#endif + +static bool CTST_GatherStats; +static int CTST_num_handled_char; + 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, + K_FIELD_TESTING, KIND_COUNT } CTST_Kind; @@ -2819,9 +5085,93 @@ 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", "disabled 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", "disabled 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_LETTER, "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)}, + {true, 'f', "fieldMaker", "tag for testing field:" }, +}; + +typedef enum { + F_BOOLEAN_FIELD, + F_BOOLEAN_AND_STRING_FIELD, + COUNT_FIELD +} CTSTField; + +static fieldDefinition CTSTFields[COUNT_FIELD] = { + { .name = "bField", + .description = "field for testing boolean type", + .dataType = FIELDTYPE_BOOL, + .enabled = true, + }, + { .name = "sbField", + .description = "field for testing string|boolean type", + .dataType = FIELDTYPE_STRING|FIELDTYPE_BOOL, + .enabled = true, + }, }; static void createCTSTTags (void) @@ -2830,36 +5180,193 @@ 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)) { + if (CTST_GatherStats) + CTST_num_handled_char++; + 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); + initTagEntry (&e, "only\nnewline", i); + makeTagEntry (&e); + initTagEntry (&e, "only\ttab", i); + makeTagEntry (&e); + initTagEntry (&e, "newline-in-scope", i); + e.extensionFields.scopeKindIndex = K_BROKEN; + e.extensionFields.scopeName = "parent\nscope"; + makeTagEntry (&e); + initTagEntry (&e, "tab-in-scope", i); + e.extensionFields.scopeKindIndex = K_BROKEN; + e.extensionFields.scopeName = "parent\tscope"; + 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_DEFINITION_INDEX; + 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); + if (qe) + 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; + } + case K_FIELD_TESTING: + { + char c = 'a'; + char name []= {'\0', 't', 'a', 'g', '\0' }; + + name [0] = c++; + initTagEntry (&e, name, i); + attachParserField (&e, false, + CTSTFields[F_BOOLEAN_FIELD].ftype, ""); + makeTagEntry (&e); + + name [0] = c++; + initTagEntry (&e, name, i); + makeTagEntry (&e); + + name [0] = c++; + initTagEntry (&e, name, i); + attachParserField (&e, false, + CTSTFields[F_BOOLEAN_AND_STRING_FIELD].ftype, "val"); + makeTagEntry (&e); + + name [0] = c++; + initTagEntry (&e, name, i); + attachParserField (&e, false, + CTSTFields[F_BOOLEAN_AND_STRING_FIELD].ftype, ""); + makeTagEntry (&e); + + break; + } } } } + 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 }; - 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 = CORK_QUEUE; + def->initStats = initStatsCTST; + def->printStats = printStatsCTST; + def->fieldTable = CTSTFields; + def->fieldCount = ARRAY_SIZE (CTSTFields); + return def; } -#endif /* CTAGS_LIB */ diff --git a/ctags/main/parse.h b/ctags/main/parse.h dissimilarity index 68% index efd38c350..93a15c5ef 100644 --- a/ctags/main/parse.h +++ b/ctags/main/parse.h @@ -1,326 +1,176 @@ -/* -* 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_H -#define CTAGS_MAIN_PARSE_H - -/* -* INCLUDE FILES -*/ -#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 "ctags-api.h" - -#ifdef HAVE_LIBXML -#include -#include -#else -#define xmlNode void -#define xmlXPathCompExpr void -#define xmlXPathContext void -#endif - -/* -* MACROS -*/ -#define LANG_AUTO (-1) -#define LANG_IGNORE (-2) - -/* -* DATA DECLARATIONS -*/ -typedef enum { - RESCAN_NONE, /* No rescan needed */ - RESCAN_FAILED, /* Scan failed, clear out tags added, rescan */ - RESCAN_APPEND /* Scan succeeded, rescan */ -} rescanReason; - -typedef void (*createRegexTag) (const vString* const name); -typedef void (*simpleParser) (void); -typedef rescanReason (*rescanParser) (const unsigned int passCount); -typedef void (*parserInitialize) (langType language); - -/* Per language finalizer is called anytime when ctags exits. - (Exceptions are a kind of options are given when invoked. Here - options are: --version, --help, --list-*, and so on.) - - 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 - 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, -} 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; -} keywordTable; - -struct sParserDefinition { - /* defined by parser */ - char* name; /* name of language */ - 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) */ - parserInitialize initialize; /* initialization routine, if needed */ - parserFinalize finalize; /* finalize routine, if needed */ - simpleParser parser; /* simple parser (common case) */ - rescanParser parser2; /* rescanning parser (unusual case) */ - selectLanguage* selectLanguage; /* may be used to resolve conflicts */ - unsigned int method; /* See METHOD_ definitions above */ - bool useCork; - bool allowNullTag; - bool requestAutomaticFQTag; - tagRegexTable *tagRegexTable; - unsigned int tagRegexCount; - const keywordTable *keywordTable; - unsigned int keywordCount; - tagXpathTableTable *tagXpathTableTable; - unsigned int tagXpathTableCount; - bool invisible; - fieldDefinition *fieldTable; - unsigned int fieldCount; - - parserDependency * dependencies; - unsigned int dependencyCount; - /* 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 */ -}; - -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, - 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 langType getNamedLanguage (const char *const name, size_t len); -extern langType getFileLanguage (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 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 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, - tagEntryFunction tagCallback, passStartCallback passCallback, - void *userData); -extern const parserDefinition *getParserDefinition (langType language); -#endif - -#ifdef HAVE_ICONV -extern void freeEncodingResources (void); -#endif - - -/* Regex interface */ -extern void findRegexTags (void); -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 bool makeKindSeparatorsPseudoTags (const langType language, - const ptagDesc *pdesc); -extern bool makeKindDescriptionsPseudoTags (const langType language, - const ptagDesc *pdesc); - -extern void anonGenerate (vString *buffer, const char *prefix, int kind); - -#endif /* CTAGS_MAIN_PARSE_H */ +/* +* 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_H +#define CTAGS_MAIN_PARSE_H + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ +#include "types.h" + +#include "kind.h" +#include "lregex.h" +#include "lxpath.h" +#include "vstring.h" + +/* +* MACROS +*/ +#define LANG_AUTO (-1) +#define LANG_IGNORE (-2) + +/* +* DATA DECLARATIONS +*/ +typedef enum { + RESCAN_NONE, /* No rescan needed */ + RESCAN_FAILED, /* Scan failed, clear out tags added, rescan */ + RESCAN_APPEND /* Scan succeeded, rescan */ +} rescanReason; + +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 + options are: --version, --help, --list-*, and so on.) + + The finalizer is called even when the initializer of the + same parser is called or not. However, the finalizer can know + 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 enum { + METHOD_NOT_CRAFTED = 1 << 0, + METHOD_REGEX = 1 << 1, + METHOD_XPATH = 1 << 2, +} parsingMethod; + +typedef struct { + const char *name; + const int id; +} keywordTable; + +typedef enum { + CORK_NO_USE, + CORK_QUEUE = (1 << 0), + CORK_SYMTAB = (1 << 1), +} corkUsage; + +struct sParserDefinition { + /* defined by parser */ + char* name; /* name of language */ + kindDefinition* kindTable; /* tag kinds handled by parser */ + unsigned int kindCount; /* size of `kinds' list */ + 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) */ + parserInitialize initialize; /* initialization routine, if needed */ + parserFinalize finalize; /* finalize routine, if needed */ + simpleParser parser; /* simple parser (common case) */ + rescanParser parser2; /* rescanning parser (unusual case) */ + selectLanguage* selectLanguage; /* may be used to resolve conflicts */ + unsigned int method; /* See METHOD_ definitions above */ + unsigned int useCork; /* bit or of corkUsage */ + bool useMemoryStreamInput; + bool allowNullTag; + bool requestAutomaticFQTag; + tagRegexTable *tagRegexTable; + unsigned int tagRegexCount; + const keywordTable *keywordTable; + unsigned int keywordCount; + tagXpathTableTable *tagXpathTableTable; + unsigned int tagXpathTableCount; + 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; + + /* Following two fields are used in a parser using cork. */ + const char *defaultScopeSeparator; + const char *defaultRootScopeSeparator; + + initStatistics initStats; + printStatistics printStats; + + /* used internally */ + langType id; /* id assigned to language */ + unsigned int enabled:1; /* currently enabled? */ + unsigned int traced:1; +}; + +typedef parserDefinition* (parserDefinitionFunc) (void); + +/* +* FUNCTION PROTOTYPES +*/ + +/* Language processing and parsing */ +extern int makeSimpleTag (const vString* const name, const int kindIndex); +extern int makeSimpleRefTag (const vString* const name, const int kindIndex, + int roleIndex); +extern int makeSimplePlaceholder(const vString* const name); +extern parserDefinition* parserNew (const char* name); + +extern const char *getLanguageName (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 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 kindDefinition* getLanguageKindForLetter (const langType language, char kindLetter); + +extern void initializeParser (langType language); + +#ifdef HAVE_ICONV +extern const char *getLanguageEncoding (const langType language); +#endif + + +/* Regex interface */ +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 void findRegexTags (void); + +/* 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 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 new file mode 100644 index 000000000..43eb3819a --- /dev/null +++ b/ctags/main/parse_p.h @@ -0,0 +1,177 @@ +/* +* 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_p.h" /* contains list of parsers */ +#include "strlist.h" +#ifdef EXTERNAL_PARSER_LIST_FILE +#include EXTERNAL_PARSER_LIST_FILE +#endif + +/* +* MACROS +*/ +#define LANG_FALLBACK (1) + +/* +* 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. + */ +#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; +#endif +#ifdef HAVE_LIBYAML +extern parserDefinitionFunc YAML_PARSER_LIST; +#endif +#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); + +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); +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, 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, + 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); + +const tagXpathTableTable *getXpathTableTable (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 bool makeFieldDescriptionsPseudoTags (const langType language, + const ptagDesc *pdesc); +extern bool makeExtraDescriptionsPseudoTags (const langType language, + const ptagDesc *pdesc); + +extern void printLanguageMultitableStatistics (langType language); +extern void printParserStatisticsIfUsed (langType lang); + +#endif /* CTAGS_MAIN_PARSE_PRIVATE_H */ diff --git a/ctags/main/parsers.h b/ctags/main/parsers.h deleted file mode 100644 index f728bbbb3..000000000 --- a/ctags/main/parsers.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -* Copyright (c) 2000-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. -* -* External interface to all language parsing modules. -* -* To add a new language parser, you need only modify this single input -* file to add the name of the parser definition function. -*/ -#ifndef CTAGS_MAIN_PARSERS_H -#define CTAGS_MAIN_PARSERS_H - -/* Add the name of any new parser definition function here */ -/* keep src/tagmanager/tm_parser.h in sync */ -#define 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, \ - FlexParser, \ - NsisParser, \ - MarkdownParser, \ - Txt2tagsParser, \ - AbcParser, \ - VerilogParser, \ - RParser, \ - CobolParser, \ - ObjcParser, \ - AsciidocParser, \ - AbaqusParser, \ - RustParser, \ - GoParser, \ - JsonParser, \ - ZephirParser, \ - PowerShellParser, \ - BibtexParser - -#endif /* CTAGS_MAIN_PARSERS_H */ diff --git a/ctags/main/parsers_p.h b/ctags/main/parsers_p.h new file mode 100644 index 000000000..7392fdbe3 --- /dev/null +++ b/ctags/main/parsers_p.h @@ -0,0 +1,155 @@ +/* +* Copyright (c) 2000-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. +* +* External interface to all language parsing modules. +* +* To add a new language parser, you need only modify this single input +* file to add the name of the parser definition function. +*/ +#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 */ +#define PARSER_LIST \ + AdaParser, \ + AntParser, \ + AsciidocParser, \ + AsmParser, \ + AspParser, \ + AutoconfParser, \ + AutoItParser, \ + AutomakeParser, \ + AwkParser, \ + BasicParser, \ + BetaParser, \ + BibtexParser, \ + ClojureParser, \ + CMakeParser, \ + CParser, \ + CppParser, \ + CPreProParser, \ + CssParser, \ + CsharpParser, \ + CtagsParser, \ + CobolParser, \ + CUDAParser, \ + DParser, \ + DiffParser, \ + DtdParser, \ + DTSParser, \ + DosBatchParser, \ + EiffelParser, \ + ElixirParser, \ + ElmParser, \ + EmacsLispParser, \ + ErlangParser, \ + FalconParser, \ + FlexParser, \ + FortranParser, \ + FyppParser, \ + GdbinitParser, \ + GoParser, \ + HtmlParser, \ + IniconfParser, \ + InkoParser, \ + ITclParser, \ + JavaParser, \ + JavaPropertiesParser, \ + JavaScriptParser, \ + JsonParser, \ + KconfigParser, \ + LdScriptParser, \ + LispParser, \ + LuaParser, \ + M4Parser, \ + ManParser, \ + MakefileParser, \ + MarkdownParser, \ + MatLabParser, \ + MooseParser, \ + MyrddinParser, \ + NsisParser, \ + ObjcParser, \ + OldCppParser, \ + OldCParser, \ + OcamlParser, \ + PasswdParser, \ + PascalParser, \ + PerlParser, \ + Perl6Parser, \ + PhpParser, \ + PodParser, \ + PowerShellParser, \ + ProtobufParser, \ + PuppetManifestParser, \ + PythonParser, \ + PythonLoggingConfigParser, \ + QemuHXParser, \ + QtMocParser, \ + RParser, \ + R6ClassParser, \ + RSpecParser, \ + RexxParser, \ + RobotParser, \ + RpmSpecParser, \ + RstParser, \ + RubyParser, \ + RustParser, \ + S4ClassParser, \ + SchemeParser, \ + SCSSParser, \ + ShParser, \ + SlangParser, \ + SmlParser, \ + SqlParser, \ + SystemdUnitParser, \ + SystemTapParser, \ + TclParser, \ + TclOOParser, \ + TexParser, \ + TexBeamerParser, \ + TTCNParser, \ + TypeScriptParser, \ + VeraParser, \ + VerilogParser, \ + SystemVerilogParser, \ + VhdlParser, \ + VimParser, \ + WindResParser, \ + YaccParser, \ + YumRepoParser, \ + ZephirParser + +#endif /* CTAGS_MAIN_PARSERS_H */ diff --git a/ctags/main/pcoproc.c b/ctags/main/pcoproc.c deleted file mode 100644 index a18015d49..000000000 --- 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 ab1ece132..000000000 --- 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 */ diff --git a/ctags/main/portable-dirent_p.h b/ctags/main/portable-dirent_p.h new file mode 100644 index 000000000..a2f215ed8 --- /dev/null +++ b/ctags/main/portable-dirent_p.h @@ -0,0 +1,944 @@ +#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 erroneous 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 resulting 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*/ +#endif /* HAVE_DIRENT_H */ diff --git a/ctags/main/portable-scandir.c b/ctags/main/portable-scandir.c new file mode 100644 index 000000000..5e7c40061 --- /dev/null +++ b/ctags/main/portable-scandir.c @@ -0,0 +1,240 @@ +#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" + +#if defined (HAVE_SCANDIR) && defined (HAVE_DIRENT_H) +#include +#elif defined (HAVE_DIRENT_H) || defined (_MSC_VER) +# ifdef HAVE_DIRENT_H +# include +# endif +#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 + +#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/promise.c b/ctags/main/promise.c index 82d6be668..509a0341a 100644 --- a/ctags/main/promise.c +++ b/ctags/main/promise.c @@ -11,39 +11,72 @@ */ #include "general.h" +#include "parse_p.h" #include "promise.h" +#include "promise_p.h" +#include "ptrarray.h" #include "debug.h" +#include "read_p.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; int r; langType lang; -/* GEANY DIFF */ -/* - if (!isXtagEnabled (XTAG_TAGS_GENERATED_BY_SUB_PARSERS)) + if ((!isThinStreamSpec(startLine, + startCharOffset, + endLine, + endCharOffset, + sourceLineOffset)) + && ( !isXtagEnabled (XTAG_GUEST))) return -1; -*/ -/* GEANY DIFF END */ lang = getNamedLanguage (parser, 0); if (lang == LANG_IGNORE) @@ -52,28 +85,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 +162,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 +186,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 dde6f78ef..65b273473 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.h b/ctags/main/promise_p.h similarity index 57% copy from ctags/main/promise.h copy to ctags/main/promise_p.h index dde6f78ef..9af398bdf 100644 --- a/ctags/main/promise.h +++ b/ctags/main/promise_p.h @@ -9,19 +9,18 @@ * GNU General Public License version 2 or (at your option) any later version. * */ -#ifndef CTAGS_MAIN_PROMISE_H -#define CTAGS_MAIN_PROMISE_H +#ifndef CTAGS_MAIN_PROMISE_PRIVATE_H +#define CTAGS_MAIN_PROMISE_PRIVATE_H #include "general.h" -#include "mio.h" -#include "parse.h" -int makePromise (const char *parser, - unsigned long startLine, int startCharOffset, - unsigned long endLine, int endCharOffset, - unsigned long sourceLineOffset); 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_H */ +#endif /* CTAGS_MAIN_PROMISE_PRIVATE_H */ diff --git a/ctags/main/ptag.c b/ctags/main/ptag.c index 643e50eb3..e8c844cf2 100644 --- a/ctags/main/ptag.c +++ b/ctags/main/ptag.c @@ -13,105 +13,113 @@ #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 "routines_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) +static bool ptagMakeFormat (ptagDesc *desc, langType language CTAGS_ATTR_UNUSED, + const void *data CTAGS_ATTR_UNUSED) { char format [11]; const char *formatComment = "unknown format"; + const optionValues *opt = data; - sprintf (format, "%u", Option.tagFileFormat); - if (Option.tagFileFormat == 1) + sprintf (format, "%u", opt->tagFileFormat); + if (opt->tagFileFormat == 1) formatComment = "original ctags format"; - else if (Option.tagFileFormat == 2) + else if (opt->tagFileFormat == 2) formatComment = "extended format; --format=1 will not append ;\" to lines"; return writePseudoTag (desc, format, formatComment, NULL); } -static bool ptagMakeHowSorted (ptagDesc *desc, void *data CTAGS_ATTR_UNUSED) +static bool ptagMakeHowSorted (ptagDesc *desc, langType language CTAGS_ATTR_UNUSED, + const void *data CTAGS_ATTR_UNUSED) { + const optionValues *opt = data; return writePseudoTag (desc, - Option.sorted == SO_FOLDSORTED ? "2" : - (Option.sorted == SO_SORTED ? "1" : "0"), + opt->sorted == SO_FOLDSORTED ? "2" : + (opt->sorted == SO_SORTED ? "1" : "0"), "0=unsorted, 1=sorted, 2=foldcase", NULL); } -static bool ptagMakeAuthor (ptagDesc *desc, void *data) +static bool ptagMakeAuthor (ptagDesc *desc, langType language CTAGS_ATTR_UNUSED, + const 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, langType language CTAGS_ATTR_UNUSED, + const 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, langType language CTAGS_ATTR_UNUSED, + const 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) +static bool ptagMakeProgVersion (ptagDesc *desc, langType language CTAGS_ATTR_UNUSED, + const void *data CTAGS_ATTR_UNUSED) { const char* repoinfo = ctags_repoinfo? ctags_repoinfo: ""; return writePseudoTag (desc, PROGRAM_VERSION, repoinfo, NULL); } #ifdef HAVE_ICONV -static bool ptagMakeFileEncoding (ptagDesc *desc, void *data CTAGS_ATTR_UNUSED) +static bool ptagMakeFileEncoding (ptagDesc *desc, langType language CTAGS_ATTR_UNUSED, + const void *data) { - if (! Option.outputEncoding) + const optionValues *opt = data; + if (! opt->outputEncoding) return false; - return writePseudoTag (desc, Option.outputEncoding, "", NULL); + return writePseudoTag (desc, opt->outputEncoding, "", NULL); } #endif -static bool ptagMakeKindSeparators (ptagDesc *desc, void *data) +static bool ptagMakeKindSeparators (ptagDesc *desc, langType language, + const void *data CTAGS_ATTR_UNUSED) { - langType *language = data; + return makeKindSeparatorsPseudoTags (language, desc); +} - return makeKindSeparatorsPseudoTags (*language, desc); +static bool ptagMakeKindDescriptions (ptagDesc *desc, langType language, + const void *data CTAGS_ATTR_UNUSED) +{ + return makeKindDescriptionsPseudoTags (language, desc); } -static bool ptagMakeKindDescriptions (ptagDesc *desc, void *data) +static bool ptagMakeFieldDescriptions (ptagDesc *desc, langType language, + const void *data CTAGS_ATTR_UNUSED) { - langType *language = data; - return makeKindDescriptionsPseudoTags (*language, desc); + return makeFieldDescriptionsPseudoTags (language, desc); +} + +static bool ptagMakeExtraDescriptions (ptagDesc *desc, langType language, + const void *data CTAGS_ATTR_UNUSED) +{ + return makeExtraDescriptionsPseudoTags (language, desc); +} + +static bool ptagMakeProcCwd (ptagDesc *desc, langType language, + const void *data CTAGS_ATTR_UNUSED) +{ + return writePseudoTag (desc, CurrentDirectory, "", NULL); } static ptagDesc ptagDescs [] = { @@ -121,48 +129,76 @@ static ptagDesc ptagDescs [] = { false, "JSON_OUTPUT_VERSION", "the version of json output stream format", ptagMakeJsonOutputVersion, - true }, + PTAGF_COMMON }, { true, "TAG_FILE_FORMAT", "the version of tags file format", ptagMakeFormat, - true }, + PTAGF_COMMON }, { true, "TAG_FILE_SORTED", "how tags are sorted", ptagMakeHowSorted, - true }, + PTAGF_COMMON }, { true, "TAG_PROGRAM_AUTHOR", "the author of this ctags implementation", ptagMakeAuthor, - true }, + PTAGF_COMMON }, { true, "TAG_PROGRAM_NAME", "the name of this ctags implementation", ptagMakeProgName, - true }, + PTAGF_COMMON }, { true, "TAG_PROGRAM_URL", "the official site URL of this ctags implementation", ptagMakeProgURL, - true }, + PTAGF_COMMON }, { true, "TAG_PROGRAM_VERSION", "the version of this ctags implementation", ptagMakeProgVersion, - true }, + PTAGF_COMMON }, #ifdef HAVE_ICONV { true, "TAG_FILE_ENCODING", "the encoding used in output tags file", ptagMakeFileEncoding, - true }, + PTAGF_COMMON }, #endif { false, "TAG_KIND_SEPARATOR", "the separators used in kinds", ptagMakeKindSeparators, - false }, + PTAGF_PARSER }, { false, "TAG_KIND_DESCRIPTION", - "the letters, names and descriptions of kinds in a parser", + "the letters, names and descriptions of enabled kinds in the language", ptagMakeKindDescriptions, - false }, + PTAGF_PARSER }, + { false, "TAG_FIELD_DESCRIPTION", + "the names and descriptions of enabled fields", + ptagMakeFieldDescriptions, + PTAGF_COMMON|PTAGF_PARSER }, + { false, "TAG_EXTRA_DESCRIPTION", + "the names and descriptions of enabled extras", + ptagMakeExtraDescriptions, + PTAGF_COMMON|PTAGF_PARSER }, + { true, "TAG_OUTPUT_MODE", + "the output mode: u-ctags or e-ctags", + ptagMakeCtagsOutputMode, + PTAGF_COMMON }, + { true, "TAG_OUTPUT_FILESEP", + "the separator used in file name (slash or backslash)", + ptagMakeCtagsOutputFilesep, + PTAGF_COMMON }, + { true, "TAG_PATTERN_LENGTH_LIMIT", + "the limit of pattern length", + ptagMakePatternLengthLimit, + PTAGF_COMMON }, + { true, "TAG_PROC_CWD", + "the current working directory of the tags generator", + ptagMakeProcCwd, + PTAGF_COMMON }, + { true, "TAG_OUTPUT_EXCMD", + "the excmd: number, pattern, mixed, or combine", + ptagMakeCtagsOutputExcmd, + PTAGF_COMMON }, }; -extern bool makePtagIfEnabled (ptagType type, void *data) +extern bool makePtagIfEnabled (ptagType type, langType language, const void *data) { ptagDesc *desc; @@ -170,7 +206,7 @@ extern bool makePtagIfEnabled (ptagType type, void *data) desc = ptagDescs + type; if (desc->enabled) - return desc->makeTag (desc, data); + return desc->makeTag (desc, language, data); else return false; } @@ -222,13 +258,39 @@ extern ptagType getPtagTypeForName (const char *name) extern bool isPtagCommonInParsers (ptagType type) { ptagDesc* pdesc = getPtagDesc (type); - return pdesc->commonInParsers; + return pdesc->flags & PTAGF_COMMON; +} + +extern bool isPtagParserSpecific (ptagType type) +{ + ptagDesc* pdesc = getPtagDesc (type); + return pdesc->flags & PTAGF_PARSER; } -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 56% rename from ctags/main/ptag.h rename to ctags/main/ptag_p.h index c6f68fbfd..5409b974a 100644 --- a/ctags/main/ptag.h +++ b/ctags/main/ptag_p.h @@ -35,29 +35,48 @@ typedef enum ePtagType { /* pseudo tag content control */ #endif PTAG_KIND_SEPARATOR, PTAG_KIND_DESCRIPTION, + PTAG_FIELD_DESCRIPTION, + PTAG_EXTRA_DESCRIPTION, + PTAG_OUTPUT_MODE, + PTAG_OUTPUT_FILESEP, + PTAG_PATTERN_TRUNCATION, + PTAG_PROC_CWD, + PTAG_OUTPUT_EXCMD, PTAG_COUNT } ptagType; +typedef enum ePtagFlag { + /* use isPtagCommonInParsers() for testing. */ + PTAGF_COMMON = 1 << 0, + /* use isPtagParserSpecific for testing. + * PSEUDO_TAG_SEPARATOR is used for printing. */ + PTAGF_PARSER = 1 << 1, +} ptagFlag; + struct sPtagDesc { bool enabled; const char* name; const char* description; /* displayed in --list-pseudo-tags output */ - bool (* makeTag) (ptagDesc *, void *); - bool commonInParsers; -}; -struct ptagXcmdData { - const char *fileName; - const char *pattern; - const char *language; + /* For making ptags for common in parsers, LANG_IGNOR is for the second + * argument and a pointer for optionValues type value for the third argument + * are passed. + * + * For parser specific ptags, the pointer for parserObject + * of the parser is passed as the thrid argument. + */ + bool (* makeTag) (ptagDesc *, langType, const void *); + + ptagFlag flags; }; -extern bool makePtagIfEnabled (ptagType type, void *data); +extern bool makePtagIfEnabled (ptagType type, langType language, const 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 isPtagCommonInParsers (ptagType type); +extern bool isPtagParserSpecific (ptagType type); extern bool enablePtag (ptagType type, bool state); #endif /* CTAGS_MAIN_FIELD_H */ diff --git a/ctags/main/ptrarray.c b/ctags/main/ptrarray.c index d45a6621d..c0b9f62eb 100644 --- a/ctags/main/ptrarray.c +++ b/ctags/main/ptrarray.c @@ -44,7 +44,7 @@ extern ptrArray *ptrArrayNew (ptrArrayDeleteFunc deleteFunc) return result; } -extern void ptrArrayAdd (ptrArray *const current, void *ptr) +extern unsigned int ptrArrayAdd (ptrArray *const current, void *ptr) { Assert (current != NULL); if (current->count == current->max) @@ -52,14 +52,17 @@ extern void ptrArrayAdd (ptrArray *const current, void *ptr) current->max *= 2; current->array = xRealloc (current->array, current->max, void*); } - current->array [current->count++] = ptr; + current->array [current->count] = ptr; + return current->count++; } -extern void ptrArrayRemoveLast (ptrArray *const current) +extern void *ptrArrayRemoveLast (ptrArray *const current) { Assert (current != NULL); Assert (current->count > 0); + void *r = ptrArrayLast (current); --current->count; + return r; } /* Combine array `from' into `current', deleting `from' */ diff --git a/ctags/main/ptrarray.h b/ctags/main/ptrarray.h index 2c0fd7794..9fc7fc388 100644 --- a/ctags/main/ptrarray.h +++ b/ctags/main/ptrarray.h @@ -18,18 +18,19 @@ /* * DATA DECLARATIONS */ +struct sPtrArray; +typedef struct sPtrArray ptrArray; typedef void (*ptrArrayDeleteFunc) (void *data); -struct sPtrArray; /* * FUNCTION PROTOTYPES */ extern ptrArray *ptrArrayNew (ptrArrayDeleteFunc deleteFunc); -extern void ptrArrayAdd (ptrArray *const current, void *ptr); -extern void ptrArrayRemoveLast (ptrArray *const current); +extern unsigned int ptrArrayAdd (ptrArray *const current, void *ptr); +extern void *ptrArrayRemoveLast (ptrArray *const current); extern void ptrArrayCombine (ptrArray *const current, ptrArray *const from); extern void ptrArrayClear (ptrArray *const current); extern unsigned int ptrArrayCount (const ptrArray *const current); diff --git a/ctags/main/rbtree.c b/ctags/main/rbtree.c new file mode 100644 index 000000000..8a402c8fb --- /dev/null +++ b/ctags/main/rbtree.c @@ -0,0 +1,468 @@ +/* + * ============================================================================= + * + * Filename: rbtree.c + * + * Description: rbtree(Red-Black tree) implementation adapted from linux + * kernel thus can be used in userspace c program. + * + * Created: 09/02/2012 11:38:12 PM + * + * Author: Fu Haiping (forhappy), haipingf@gmail.com + * Company: ICT ( Institute Of Computing Technology, CAS ) + * + * ============================================================================= + */ + +/* + Red Black Trees + (C) 1999 Andrea Arcangeli + (C) 2002 David Woodhouse + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + linux/lib/rbtree.c +*/ + +#include "general.h" +#include "rbtree.h" + +static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *right = node->rb_right; + struct rb_node *parent = rb_parent(node); + + if ((node->rb_right = right->rb_left)) + rb_set_parent(right->rb_left, node); + right->rb_left = node; + + rb_set_parent(right, parent); + + if (parent) + { + if (node == parent->rb_left) + parent->rb_left = right; + else + parent->rb_right = right; + } + else + root->rb_node = right; + rb_set_parent(node, right); +} + +static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *left = node->rb_left; + struct rb_node *parent = rb_parent(node); + + if ((node->rb_left = left->rb_right)) + rb_set_parent(left->rb_right, node); + left->rb_right = node; + + rb_set_parent(left, parent); + + if (parent) + { + if (node == parent->rb_right) + parent->rb_right = left; + else + parent->rb_left = left; + } + else + root->rb_node = left; + rb_set_parent(node, left); +} + +void rb_insert_color(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *parent, *gparent; + + while ((parent = rb_parent(node)) && rb_is_red(parent)) + { + gparent = rb_parent(parent); + + if (parent == gparent->rb_left) + { + { + register struct rb_node *uncle = gparent->rb_right; + if (uncle && rb_is_red(uncle)) + { + rb_set_black(uncle); + rb_set_black(parent); + rb_set_red(gparent); + node = gparent; + continue; + } + } + + if (parent->rb_right == node) + { + register struct rb_node *tmp; + __rb_rotate_left(parent, root); + tmp = parent; + parent = node; + node = tmp; + } + + rb_set_black(parent); + rb_set_red(gparent); + __rb_rotate_right(gparent, root); + } else { + { + register struct rb_node *uncle = gparent->rb_left; + if (uncle && rb_is_red(uncle)) + { + rb_set_black(uncle); + rb_set_black(parent); + rb_set_red(gparent); + node = gparent; + continue; + } + } + + if (parent->rb_left == node) + { + register struct rb_node *tmp; + __rb_rotate_right(parent, root); + tmp = parent; + parent = node; + node = tmp; + } + + rb_set_black(parent); + rb_set_red(gparent); + __rb_rotate_left(gparent, root); + } + } + + rb_set_black(root->rb_node); +} + +static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, + struct rb_root *root) +{ + struct rb_node *other; + + while ((!node || rb_is_black(node)) && node != root->rb_node) + { + if (parent->rb_left == node) + { + other = parent->rb_right; + if (rb_is_red(other)) + { + rb_set_black(other); + rb_set_red(parent); + __rb_rotate_left(parent, root); + other = parent->rb_right; + } + if ((!other->rb_left || rb_is_black(other->rb_left)) && + (!other->rb_right || rb_is_black(other->rb_right))) + { + rb_set_red(other); + node = parent; + parent = rb_parent(node); + } + else + { + if (!other->rb_right || rb_is_black(other->rb_right)) + { + rb_set_black(other->rb_left); + rb_set_red(other); + __rb_rotate_right(other, root); + other = parent->rb_right; + } + rb_set_color(other, rb_color(parent)); + rb_set_black(parent); + rb_set_black(other->rb_right); + __rb_rotate_left(parent, root); + node = root->rb_node; + break; + } + } + else + { + other = parent->rb_left; + if (rb_is_red(other)) + { + rb_set_black(other); + rb_set_red(parent); + __rb_rotate_right(parent, root); + other = parent->rb_left; + } + if ((!other->rb_left || rb_is_black(other->rb_left)) && + (!other->rb_right || rb_is_black(other->rb_right))) + { + rb_set_red(other); + node = parent; + parent = rb_parent(node); + } + else + { + if (!other->rb_left || rb_is_black(other->rb_left)) + { + rb_set_black(other->rb_right); + rb_set_red(other); + __rb_rotate_left(other, root); + other = parent->rb_left; + } + rb_set_color(other, rb_color(parent)); + rb_set_black(parent); + rb_set_black(other->rb_left); + __rb_rotate_right(parent, root); + node = root->rb_node; + break; + } + } + } + if (node) + rb_set_black(node); +} + +void rb_erase(struct rb_node *node, struct rb_root *root) +{ + struct rb_node *child, *parent; + int color; + + if (!node->rb_left) + child = node->rb_right; + else if (!node->rb_right) + child = node->rb_left; + else + { + struct rb_node *old = node, *left; + + node = node->rb_right; + while ((left = node->rb_left) != NULL) + node = left; + + if (rb_parent(old)) { + if (rb_parent(old)->rb_left == old) + rb_parent(old)->rb_left = node; + else + rb_parent(old)->rb_right = node; + } else + root->rb_node = node; + + child = node->rb_right; + parent = rb_parent(node); + color = rb_color(node); + + if (parent == old) { + parent = node; + } else { + if (child) + rb_set_parent(child, parent); + parent->rb_left = child; + + node->rb_right = old->rb_right; + rb_set_parent(old->rb_right, node); + } + + node->rb_parent_color = old->rb_parent_color; + node->rb_left = old->rb_left; + rb_set_parent(old->rb_left, node); + + goto color; + } + + parent = rb_parent(node); + color = rb_color(node); + + if (child) + rb_set_parent(child, parent); + if (parent) + { + if (parent->rb_left == node) + parent->rb_left = child; + else + parent->rb_right = child; + } + else + root->rb_node = child; + + color: + if (color == RB_BLACK) + __rb_erase_color(child, parent, root); +} + +static void rb_augment_path(struct rb_node *node, rb_augment_f func, void *data) +{ + struct rb_node *parent; + +up: + func(node, data); + parent = rb_parent(node); + if (!parent) + return; + + if (node == parent->rb_left && parent->rb_right) + func(parent->rb_right, data); + else if (parent->rb_left) + func(parent->rb_left, data); + + node = parent; + goto up; +} + +/* + * after inserting @node into the tree, update the tree to account for + * both the new entry and any damage done by rebalance + */ +void rb_augment_insert(struct rb_node *node, rb_augment_f func, void *data) +{ + if (node->rb_left) + node = node->rb_left; + else if (node->rb_right) + node = node->rb_right; + + rb_augment_path(node, func, data); +} + +/* + * before removing the node, find the deepest node on the rebalance path + * that will still be there after @node gets removed + */ +struct rb_node *rb_augment_erase_begin(struct rb_node *node) +{ + struct rb_node *deepest; + + if (!node->rb_right && !node->rb_left) + deepest = rb_parent(node); + else if (!node->rb_right) + deepest = node->rb_left; + else if (!node->rb_left) + deepest = node->rb_right; + else { + deepest = rb_next(node); + if (deepest->rb_right) + deepest = deepest->rb_right; + else if (rb_parent(deepest) != node) + deepest = rb_parent(deepest); + } + + return deepest; +} + +/* + * after removal, update the tree to account for the removed entry + * and any rebalance damage. + */ +void rb_augment_erase_end(struct rb_node *node, rb_augment_f func, void *data) +{ + if (node) + rb_augment_path(node, func, data); +} + +/* + * This function returns the first node (in sort order) of the tree. + */ +struct rb_node *rb_first(const struct rb_root *root) +{ + struct rb_node *n; + + n = root->rb_node; + if (!n) + return NULL; + while (n->rb_left) + n = n->rb_left; + return n; +} + +struct rb_node *rb_last(const struct rb_root *root) +{ + struct rb_node *n; + + n = root->rb_node; + if (!n) + return NULL; + while (n->rb_right) + n = n->rb_right; + return n; +} + +struct rb_node *rb_next(const struct rb_node *node) +{ + struct rb_node *parent; + + if (rb_parent(node) == node) + return NULL; + + /* If we have a right-hand child, go down and then left as far + as we can. */ + if (node->rb_right) { + node = node->rb_right; + while (node->rb_left) + node=node->rb_left; + return (struct rb_node *)node; + } + + /* No right-hand children. Everything down and left is + smaller than us, so any 'next' node must be in the general + direction of our parent. Go up the tree; any time the + ancestor is a right-hand child of its parent, keep going + up. First time it's a left-hand child of its parent, said + parent is our 'next' node. */ + while ((parent = rb_parent(node)) && node == parent->rb_right) + node = parent; + + return parent; +} + +struct rb_node *rb_prev(const struct rb_node *node) +{ + struct rb_node *parent; + + if (rb_parent(node) == node) + return NULL; + + /* If we have a left-hand child, go down and then right as far + as we can. */ + if (node->rb_left) { + node = node->rb_left; + while (node->rb_right) + node=node->rb_right; + return (struct rb_node *)node; + } + + /* No left-hand children. Go up till we find an ancestor which + is a right-hand child of its parent */ + while ((parent = rb_parent(node)) && node == parent->rb_left) + node = parent; + + return parent; +} + +void rb_replace_node(struct rb_node *victim, struct rb_node *new, + struct rb_root *root) +{ + struct rb_node *parent = rb_parent(victim); + + /* Set the surrounding nodes to point to the replacement */ + if (parent) { + if (victim == parent->rb_left) + parent->rb_left = new; + else + parent->rb_right = new; + } else { + root->rb_node = new; + } + if (victim->rb_left) + rb_set_parent(victim->rb_left, new); + if (victim->rb_right) + rb_set_parent(victim->rb_right, new); + + /* Copy the pointers/colour from the victim to the replacement */ + *new = *victim; +} diff --git a/ctags/main/rbtree.h b/ctags/main/rbtree.h new file mode 100644 index 000000000..e07f25949 --- /dev/null +++ b/ctags/main/rbtree.h @@ -0,0 +1,230 @@ +/* + * ============================================================================= + * + * Filename: rbtree.h + * + * Description: rbtree(Red-Black tree) implementation adapted from linux + * kernel thus can be used in userspace c program. + * + * Created: 09/02/2012 11:36:11 PM + * + * Author: Fu Haiping (forhappy), haipingf@gmail.com + * Company: ICT ( Institute Of Computing Technology, CAS ) + * + * ============================================================================= + */ + +/* + Red Black Trees + (C) 1999 Andrea Arcangeli + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + linux/include/linux/rbtree.h + + To use rbtrees you'll have to implement your own insert and search cores. + This will avoid us to use callbacks and to drop drammatically performances. + I know it's not the cleaner way, but in C (not in C++) to get + performances and genericity... + + Some example of insert and search follows here. The search is a plain + normal search over an ordered tree. The insert instead must be implemented + in two steps: First, the code must insert the element in order as a red leaf + in the tree, and then the support library function rb_insert_color() must + be called. Such function will do the not trivial work to rebalance the + rbtree, if necessary. + +----------------------------------------------------------------------- +static inline struct page * rb_search_page_cache(struct inode * inode, + unsigned long offset) +{ + struct rb_node * n = inode->i_rb_page_cache.rb_node; + struct page * page; + + while (n) + { + page = rb_entry(n, struct page, rb_page_cache); + + if (offset < page->offset) + n = n->rb_left; + else if (offset > page->offset) + n = n->rb_right; + else + return page; + } + return NULL; +} + +static inline struct page * __rb_insert_page_cache(struct inode * inode, + unsigned long offset, + struct rb_node * node) +{ + struct rb_node ** p = &inode->i_rb_page_cache.rb_node; + struct rb_node * parent = NULL; + struct page * page; + + while (*p) + { + parent = *p; + page = rb_entry(parent, struct page, rb_page_cache); + + if (offset < page->offset) + p = &(*p)->rb_left; + else if (offset > page->offset) + p = &(*p)->rb_right; + else + return page; + } + + rb_link_node(node, parent, p); + + return NULL; +} + +static inline struct page * rb_insert_page_cache(struct inode * inode, + unsigned long offset, + struct rb_node * node) +{ + struct page * ret; + if ((ret = __rb_insert_page_cache(inode, offset, node))) + goto out; + rb_insert_color(node, &inode->i_rb_page_cache); + out: + return ret; +} +----------------------------------------------------------------------- +*/ + +#ifndef CTAGS_MAIN_RBTREE_H +#define CTAGS_MAIN_RBTREE_H + +#include "general.h" +#include "inline.h" +#include "gcc-attr.h" +#include + +#if defined(container_of) + #undef container_of +#if defined(HAVE_TYPEOF) && defined(HAVE_STATEMENT_EXPRESSION_EXT) + #define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#else + #define container_of(ptr, type, member) \ + ((type *)( (char *)ptr - offsetof(type,member))) +#endif /* defined(HAVE_TYPEOF) && defined(HAVE_STATEMENT_EXPRESSION_EXT) */ +#else +#if defined(HAVE_TYPEOF) && defined(HAVE_STATEMENT_EXPRESSION_EXT) + #define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#else + #define container_of(ptr, type, member) \ + ((type *)( (char *)ptr - offsetof(type,member))) +#endif /* defined(HAVE_TYPEOF) && defined(HAVE_STATEMENT_EXPRESSION_EXT) */ +#endif /* defined(container_of) */ + +#if defined(offsetof) + #undef offsetof + #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#else + #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#undef NULL +#if defined(__cplusplus) + #define NULL 0 +#else + #define NULL ((void *)0) +#endif + +struct rb_node +{ + uintptr_t rb_parent_color; +#define RB_RED 0 +#define RB_BLACK 1 + struct rb_node *rb_right; + struct rb_node *rb_left; +} CTAGA_ATTR_ALIGNED(sizeof(long)); + /* The alignment might seem pointless, but allegedly CRIS needs it */ + +struct rb_root +{ + struct rb_node *rb_node; +}; + + +#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) +#define rb_color(r) ((r)->rb_parent_color & 1) +#define rb_is_red(r) (!rb_color(r)) +#define rb_is_black(r) rb_color(r) +#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) +#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) + +CTAGS_INLINE void rb_set_parent(struct rb_node *rb, struct rb_node *p) +{ + rb->rb_parent_color = (rb->rb_parent_color & 3) | (uintptr_t)p; +} +CTAGS_INLINE void rb_set_color(struct rb_node *rb, int color) +{ + rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; +} + +#define RB_ROOT (struct rb_root) { NULL, } +#define rb_entry(ptr, type, member) container_of(ptr, type, member) + +#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) +#define RB_EMPTY_NODE(node) (rb_parent(node) == node) +#define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) + +CTAGS_INLINE void rb_init_node(struct rb_node *rb) +{ + rb->rb_parent_color = 0; + rb->rb_right = NULL; + rb->rb_left = NULL; + RB_CLEAR_NODE(rb); +} + +extern void rb_insert_color(struct rb_node *, struct rb_root *); +extern void rb_erase(struct rb_node *, struct rb_root *); + +typedef void (*rb_augment_f)(struct rb_node *node, void *data); + +extern void rb_augment_insert(struct rb_node *node, + rb_augment_f func, void *data); +extern struct rb_node *rb_augment_erase_begin(struct rb_node *node); +extern void rb_augment_erase_end(struct rb_node *node, + rb_augment_f func, void *data); + +/* Find logical next and previous nodes in a tree */ +extern struct rb_node *rb_next(const struct rb_node *); +extern struct rb_node *rb_prev(const struct rb_node *); +extern struct rb_node *rb_first(const struct rb_root *); +extern struct rb_node *rb_last(const struct rb_root *); + +/* Fast replacement of a single node without remove/rebalance/add/rebalance */ +extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, + struct rb_root *root); + +CTAGS_INLINE void rb_link_node(struct rb_node * node, struct rb_node * parent, + struct rb_node ** rb_link) +{ + node->rb_parent_color = (uintptr_t)parent; + node->rb_left = node->rb_right = NULL; + + *rb_link = node; +} + +#endif /* CTAGS_MAIN_RBTREE_H */ diff --git a/ctags/main/read.c b/ctags/main/read.c index 6a1750b51..e19f15697 100644 --- a/ctags/main/read.c +++ b/ctags/main/read.c @@ -15,27 +15,26 @@ #include #include +#include #define FILE_WRITE #include "read.h" +#include "read_p.h" #include "debug.h" -#include "entry.h" -#include "main.h" +#include "entry_p.h" #include "routines.h" -#include "options.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 -#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 +61,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 +88,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 +107,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; /* @@ -115,6 +122,7 @@ static langType sourceLang; */ static void langStackInit (langStack *langStack); static langType langStackTop (langStack *langStack); +static langType langStackBotom(langStack *langStack); static void langStackPush (langStack *langStack, langType type); static langType langStackPop (langStack *langStack); static void langStackClear(langStack *langStack); @@ -125,7 +133,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 +153,49 @@ 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; +} + +static compoundPos* getInputFileCompoundPosForLine (unsigned int line) +{ + 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; +} + +extern MIOPos getInputFilePositionForLine (unsigned int line) +{ + compoundPos *cpos = getInputFileCompoundPosForLine (line); + return cpos->pos; } -extern MIOPos getInputFilePositionForLine (int line) +extern long getInputFileOffsetForLine (unsigned int line) { - return File.lineFposMap.pos[(((File.lineFposMap.count > (line - 1)) \ - && (line > 0))? (line - 1): 0)]; + compoundPos *cpos = getInputFileCompoundPosForLine (line); + return cpos->offset; } extern langType getInputLanguage (void) { - return langStackTop (&File.input.langInfo.stack); + return langStackTop (&inputLang.stack); } extern const char *getInputLanguageName (void) @@ -189,14 +223,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 kindDefinition *getInputLanguageFileKind (void) +extern unsigned int countInputLanguageKinds (void) { - return getLanguageFileKind (getInputLanguage ()); + return countLanguageKinds (getInputLanguage ()); +} + +extern unsigned int countInputLanguageRoles (int kindIndex) +{ + return countLanguageRoles (getInputLanguage (), kindIndex); +} + +extern bool doesInputLanguageAllowNullTag (void) +{ + return doesLanguageAllowNullTag (getInputLanguage ()); } extern bool doesInputLanguageRequestAutomaticFQTag (void) @@ -255,7 +300,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 +310,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 +392,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 +405,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) @@ -339,6 +432,12 @@ static void resetLangOnStack (inputLangInfo *langInfo, langType lang) langStackPush (& (langInfo->stack), lang); } +extern langType baseLangOnStack (inputLangInfo *langInfo) +{ + Assert (langInfo->stack.count > 0); + return langStackBotom (& (langInfo->stack)); +} + static void pushLangOnStack (inputLangInfo *langInfo, langType lang) { langStackPush (& langInfo->stack, lang); @@ -351,32 +450,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 +598,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) @@ -538,7 +634,37 @@ extern MIO *getMio (const char *const fileName, const char *const openMode, return mio_new_file (fileName, openMode); } fclose (src); - return mio_new_memory (data, size, eRealloc, eFree); + return mio_new_memory (data, size, eRealloc, eFreeNoNullCheck); +} + +/* 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 @@ -555,7 +681,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; } @@ -565,7 +691,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 +716,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,46 +736,36 @@ 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; } -#ifdef 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 - 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 +773,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. @@ -660,7 +783,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); } @@ -673,12 +796,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 +820,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 && + vStringChar (vLine, vStringLength (vLine) - 2) == '\r') { - vStringItem (File.line, vStringLength (File.line) - 2) = '\n'; - vStringChop (File.line); + vStringChar (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; +} + +static vString *iFileGetLine (void) +{ + eolType eol; + langType lang = getInputLanguage(); + + if (File.line == NULL) + File.line = vStringNew (); + + eol = readLine (File.line, File.mio); - if (haveLine) + 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. @@ -814,6 +979,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 @@ -837,65 +1015,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 +1040,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 +1051,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 +1086,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 +1112,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,19 +1140,30 @@ extern unsigned int getNestedInputBoundaryInfo (unsigned long lineNumber) } extern void popNarrowedInputStream (void) { - mio_free (File.mio); + if (File.thinDepth) + { + File.thinDepth--; + verbose ("CLEARING thin flag(%d)\n", File.thinDepth); + return; + } + mio_unref (File.mio); File = BackupFile; memset (&BackupFile, 0, sizeof (BackupFile)); } 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); +} + +extern langType getLanguageForBaseParser (void) +{ + return baseLangOnStack (& inputLang); } static void langStackInit (langStack *langStack) @@ -1146,6 +1171,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) @@ -1154,6 +1180,12 @@ static langType langStackTop (langStack *langStack) return langStack->languages [langStack->count - 1]; } +static langType langStackBotom(langStack *langStack) +{ + Assert (langStack->count > 0); + return langStack->languages [0]; +} + static void langStackClear (langStack *langStack) { while (langStack->count > 0) @@ -1174,3 +1206,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 + /* A parser 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 b0ac8e752..c38862378 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" @@ -55,68 +55,30 @@ 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 (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 doesInputLanguageAllowNullTag (void); -extern kindDefinition *getInputLanguageFileKind (void); -extern bool doesInputLanguageRequestAutomaticFQTag (void); +extern bool isInputLanguageRoleEnabled (int kindIndex, int roleIndex); -extern void freeInputFileResources (void); extern const unsigned char *getInputFileData (size_t *size); -/* Stream opend 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); -#ifdef CTAGS_LIB -extern bool bufferOpen (const char *const fileName, const langType language, - unsigned char *buffer, size_t buffer_size); -#endif -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, fp */ -extern char *readLineRaw (vString *const vLine, MIO *const mio); -extern char* readLineRawWithNoSeek (vString *const vline, FILE *const pp); - -/* 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 popNarrowedInputStream (void); +/* Raw: reading from given a parameter, mio */ +extern char *readLineRaw (vString *const vLine, MIO *const mio); extern void pushLanguage(const langType language); extern langType popLanguage (void); diff --git a/ctags/main/read_p.h b/ctags/main/read_p.h new file mode 100644 index 000000000..ac2761a00 --- /dev/null +++ b/ctags/main/read_p.h @@ -0,0 +1,81 @@ +/* +* 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 long getInputFileOffsetForLine (unsigned int line); + +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 langType getLanguageForBaseParser (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 a097dce5d..15312d43d 100644 --- a/ctags/main/repoinfo.h +++ b/ctags/main/repoinfo.h @@ -1 +1 @@ -#define CTAGS_REPOINFO "ff6491f" +#define CTAGS_REPOINFO "2fc21493" diff --git a/ctags/main/routines.c b/ctags/main/routines.c index cf8aefa28..b9e5fc66d 100644 --- a/ctags/main/routines.c +++ b/ctags/main/routines.c @@ -12,9 +12,7 @@ */ #include "general.h" /* must always come first */ -#ifdef HAVE_STDLIB_H -# include /* to declare malloc (), realloc () */ -#endif +#include /* to declare malloc (), realloc (), mbcs() */ #include #include #include /* to declare tempnam(), and SEEK_SET (hopefully) */ @@ -26,9 +24,7 @@ # include /* to declare mkstemp () */ #endif -#ifdef HAVE_LIMITS_H -# include /* to declare MB_LEN_MAX */ -#endif +#include /* to declare MB_LEN_MAX */ #ifndef MB_LEN_MAX # define MB_LEN_MAX 6 #endif @@ -53,22 +49,15 @@ #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" -#ifdef HAVE_ICONV -# include "mbcs.h" -#endif -#ifdef HAVE_ERRNO_H -# include -#endif +#include "routines_p.h" +#include -#include "options.h" +#include "vstring.h" /* * MACROS @@ -133,11 +122,12 @@ /* Hack for ridiculous practice of Microsoft Visual C++. */ #if defined (WIN32) -# if defined (_MSC_VER) -# define stat _stat +# if defined (_MSC_VER) || defined (__MINGW32__) +# ifndef stat +# define stat _stat +# endif # define getcwd _getcwd # define currentdrive() (_getdrive() + 'A' - 1) -# define PATH_MAX _MAX_PATH # else # define currentdrive() 'C' # endif @@ -177,6 +167,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 */ @@ -204,6 +200,18 @@ extern const char *getExecutablePath (void) } /* + * 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 */ @@ -211,7 +219,7 @@ extern void *eMalloc (const size_t size) { void *buffer = malloc (size); - if (buffer == NULL) + if (buffer == NULL && size != 0) error (FATAL, "out of memory"); return buffer; @@ -221,7 +229,7 @@ extern void *eCalloc (const size_t count, const size_t size) { void *buffer = calloc (count, size); - if (buffer == NULL) + if (buffer == NULL && count != 0 && size != 0) error (FATAL, "out of memory"); return buffer; @@ -235,7 +243,7 @@ extern void *eRealloc (void *const ptr, const size_t size) else { buffer = realloc (ptr, size); - if (buffer == NULL) + if (buffer == NULL && size != 0) error (FATAL, "out of memory"); } return buffer; @@ -247,6 +255,20 @@ extern void eFree (void *const ptr) free (ptr); } +extern void eFreeNoNullCheck (void *const ptr) +{ + free (ptr); +} + +extern void eFreeIndirect(void **ptr) +{ + if (ptr && *ptr) + { + eFree (*ptr); + *ptr = NULL; + } +} + /* * String manipulation functions */ @@ -312,8 +334,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 +435,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 +443,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 +511,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 +544,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 +575,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 +598,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 +637,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 +650,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 +666,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 +683,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 +692,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 @@ -666,14 +714,18 @@ extern char *combinePathAndFile ( const char *const path, const char *const file) { vString *const filePath = vStringNew (); - const int lastChar = path [strlen (path) - 1]; - bool terminated = isPathSeparator (lastChar); + size_t len = strlen (path); - vStringCopyS (filePath, path); - if (! terminated) - vStringPut (filePath, OUTPUT_PATH_SEPARATOR); - vStringCatS (filePath, file); + if (len) + { + const int lastChar = path [len - 1]; + bool terminated = isPathSeparator (lastChar); + vStringCopyS (filePath, path); + if (! terminated) + vStringPut (filePath, OUTPUT_PATH_SEPARATOR); + } + vStringCatS (filePath, file); return vStringDeleteUnwrap (filePath); } @@ -721,13 +773,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 @@ -737,27 +789,31 @@ extern char* absoluteFilename (const char *file) cp = slashp;/* the absolute name begins with "/.." */ #ifdef MSDOS_STYLE_PATH /* Under MSDOS and NT we get `d:/NAME' as absolute file name, - * so the luser could say `d:/../NAME'. We silently treat this + * so the user 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 +821,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 +834,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 +861,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 +871,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; } @@ -855,6 +914,19 @@ extern MIO *tempFile (const char *const mode, char **const pName) name = xMalloc (strlen (tmpdir) + 1 + strlen (pattern) + 1, char); sprintf (name, "%s%c%s", tmpdir, OUTPUT_PATH_SEPARATOR, pattern); fd = mkstemp (name); +# ifdef WIN32 + if (fd == -1) + { + /* mkstemp() sometimes fails with unknown reasons. + * Retry a few times. */ + int i; + for (i = 0; i < 5 && fd == -1; i++) + { + sprintf (name, "%s%c%s", tmpdir, OUTPUT_PATH_SEPARATOR, pattern); + fd = mkstemp (name); + } + } +# endif eStatFree (file); #elif defined(HAVE_TEMPNAM) const char *tmpdir = NULL; @@ -874,7 +946,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 72471cdbf..1d43f2bb9 100644 --- a/ctags/main/routines.h +++ b/ctags/main/routines.h @@ -16,7 +16,6 @@ #include -#include "mio.h" /* * MACROS @@ -32,68 +31,14 @@ #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,8 @@ 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 */ extern int struppercmp (const char *s1, const char *s2); @@ -120,27 +67,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.h b/ctags/main/routines_p.h similarity index 52% copy from ctags/main/routines.h copy to ctags/main/routines_p.h index 72471cdbf..2ff746777 100644 --- a/ctags/main/routines.h +++ b/ctags/main/routines_p.h @@ -4,32 +4,17 @@ * 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 routines.c +* Main part private interface to routines.c */ -#ifndef CTAGS_MAIN_ROUTINES_H -#define CTAGS_MAIN_ROUTINES_H +#ifndef CTAGS_MAIN_ROUTINES_PRIVATE_H +#define CTAGS_MAIN_ROUTINES_PRIVATE_H /* * INCLUDE FILES */ #include "general.h" /* must always come first */ - -#include - #include "mio.h" - -/* -* MACROS -*/ -#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)) - -#define ARRAY_SIZE(X) (sizeof (X) / sizeof (X[0])) -#define ARRAY_AND_SIZE(X) (X), ARRAY_SIZE(X) - -#define STRINGIFY(X) STRINGIFY_(X) -#define STRINGIFY_(X) #X +#include "portable-dirent_p.h" /* * Portability macros @@ -42,7 +27,7 @@ # endif #endif -#if defined (MSDOS_STYLE_PATH) && defined (UNIX_PATH_SEPARATOR) +#if defined (MSDOS_STYLE_PATH) # define OUTPUT_PATH_SEPARATOR '/' #else # define OUTPUT_PATH_SEPARATOR PATH_SEPARATOR @@ -51,12 +36,10 @@ /* * DATA DECLARATIONS */ +extern char *CurrentDirectory; #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 */ @@ -85,46 +68,17 @@ typedef struct { /* Size of file (pointed to) */ unsigned long size; -} fileStatus; +} 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 */ -#ifdef NEED_PROTO_MALLOC -extern void *malloc (size_t); -extern void *realloc (void *ptr, size_t); -#endif -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); - -/* String manipulation functions */ -extern int struppercmp (const char *s1, const char *s2); -extern int strnuppercmp (const char *s1, const char *s2, size_t n); -#ifndef HAVE_STRSTR -extern char* strstr (const char *str, const char *substr); -#endif -extern char* strrstr (const char *str, const char *substr); -extern char* eStrdup (const char* str); -extern char* eStrndup (const char* str, size_t len); -extern void toLowerString (char* str); -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 strToInt(const char *const str, int base, int *value); -extern bool strToLong(const char *string, int base, long *value); /* 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); @@ -132,8 +86,6 @@ 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); @@ -143,4 +95,4 @@ 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 */ +#endif /* CTAGS_MAIN_ROUTINES_PRIVATE_H */ diff --git a/ctags/main/seccomp.c b/ctags/main/seccomp.c new file mode 100644 index 000000000..12e609821 --- /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 e4b7cf4c4..ee0662957 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,174 @@ 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; + else + verbose (" Xml[rootElementName]== %s\n", + spec->rootElementName); + } + + if (spec->nameInDTD) { - /* - nameInDTD == '\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 + verbose (" Xml[nameInDTD]== %s\n", + spec->nameInDTD); } - else if (doc->children - && doc->children->name - && (strcmp ((const char*)doc->children->name, "project") == 0)) + + if (spec->externalID) { - 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"; + 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; else - return "Ant"; + verbose (" Xml[externalID]== %s\n", + spec->externalID); + + } + + 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; + else + verbose (" Xml[systemID]== %s\n", + spec->systemID); + } + + 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; + else + verbose (" Xml[rootNSPrefix]== %s\n", + spec->rootNSPrefix); + } + + 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; + else + verbose (" Xml[rootNSHref]== %s\n", + spec->rootNSHref); + } + return true; +} + +static const char * +selectParserForXmlDoc (xmlDocPtr doc, + langType *candidates, + unsigned int nCandidates) +{ + + unsigned int lang_index; + bool xml_parser_is_in_candidate = false;; + + 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++) + { + 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]); + } + + 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; } const char * -selectByDTD (MIO *input) +selectByXpathFileSpec (MIO *input, + langType *candidates, + unsigned int nCandidates) { xmlDocPtr doc; const char *r = NULL; @@ -369,7 +500,7 @@ selectByDTD (MIO *input) if (doc == NULL) return NULL; - r = selectParserForXmlDoc (doc); + r = selectParserForXmlDoc (doc, candidates, nCandidates); if (r == NULL) xmlFreeDoc (doc); @@ -379,4 +510,15 @@ selectByDTD (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 3d2b28c24..6b2eb7f7d 100644 --- a/ctags/main/selectors.h +++ b/ctags/main/selectors.h @@ -10,26 +10,24 @@ #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); -#endif +selectByXpathFileSpec (MIO *input, langType *candidates, unsigned int nCandidates); #endif diff --git a/ctags/main/sort.c b/ctags/main/sort.c index 7ee7ce0a2..d6b0171c0 100644 --- a/ctags/main/sort.c +++ b/ctags/main/sort.c @@ -15,9 +15,7 @@ #if defined (HAVE_IO_H) # include #endif -#if defined (HAVE_STDLIB_H) -# include /* to declare malloc () */ -#endif +#include /* to declare malloc () */ #if defined (HAVE_UNISTD_H) # include #endif @@ -25,11 +23,11 @@ #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" +#include "sort_p.h" /* * FUNCTION DEFINITIONS @@ -55,17 +53,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 +107,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 +142,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"); @@ -131,7 +167,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 @@ -155,7 +191,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,12 +212,16 @@ 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); - mio_free (mio); + mio_unref (mio); } extern void internalSortTags (const bool toStdout, MIO* mio, size_t numTags) @@ -190,11 +230,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 +263,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 +278,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/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 000000000..f9033d390 --- /dev/null +++ b/ctags/main/stats.c @@ -0,0 +1,85 @@ +/* +* 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); + + 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); + + 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)); + fprintf (stderr, " in %.02f seconds", + ((double) (timeStamps [2] - timeStamps [1])) / CLOCKS_PER_SEC); + 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 000000000..b88b23d4a --- /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 bf1ee1bc3..7decc23d8 100644 --- a/ctags/main/strlist.c +++ b/ctags/main/strlist.c @@ -13,9 +13,7 @@ #include "general.h" /* must always come first */ #include -/* GEANY DIFF #include - * GEANY DIFF END */ #include "debug.h" #include "read.h" @@ -75,7 +73,7 @@ extern stringList* stringListNewFromFile (const char* const fileName) else vStringDelete (str); } - mio_free (mio); + mio_unref (mio); } return result; } @@ -109,13 +107,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 +135,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 +160,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; @@ -217,11 +215,20 @@ extern vString* stringListExtensionFinds ( 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 */ + 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 } extern bool stringListFileMatched ( @@ -236,11 +243,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, PATH_SEPARATOR, OUTPUT_PATH_SEPARATOR); + 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) + vStringDelete (tmp); +#endif + return matched? vstr: NULL; } diff --git a/ctags/main/strlist.h b/ctags/main/strlist.h index d23a63c22..e227a5809 100644 --- a/ctags/main/strlist.h +++ b/ctags/main/strlist.h @@ -14,6 +14,7 @@ */ #include "general.h" /* must always come first */ +#include "routines_p.h" #include "vstring.h" #include "ptrarray.h" diff --git a/ctags/main/subparser.h b/ctags/main/subparser.h new file mode 100644 index 000000000..836fd7cc6 --- /dev/null +++ b/ctags/main/subparser.h @@ -0,0 +1,68 @@ +/* + * + * 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 "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, + 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); +}; + +/* +* FUNCTION PROTOTYPES +*/ + +/* Interface for Baseparser */ +extern subparser *getNextSubparser(subparser *last, bool includingNoneCraftedParser); +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); + +#endif /* CTAGS_MAIN_SUBPARSER_H */ diff --git a/ctags/main/subparser_p.h b/ctags/main/subparser_p.h new file mode 100644 index 000000000..801e821c9 --- /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 000000000..5dea1b884 --- /dev/null +++ b/ctags/main/tokeninfo.c @@ -0,0 +1,207 @@ +/* +* 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" +#include "tokeninfo.h" + +#include "entry.h" +#include "read.h" +#include "routines.h" + +static void* createToken (void *createArg) +{ + struct tokenInfoClass *klass = createArg; + tokenInfo *token; + + token = eCalloc (1, 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->typeForUndefined; + token->keyword = token->klass->keywordNone; + vStringClear (token->string); + token->lineNumber = getInputLineNumber (); + token->filePosition = getInputFilePosition (); +} + +static void deleteToken (void *data) +{ + tokenInfo *token = data; + + if (token->klass->delete) + token->klass->delete (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, + deleteToken, + 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 tokenDelete (tokenInfo *token) +{ + objPoolPut (token->klass->pool, token); +} + + +void tokenReadFull (tokenInfo *token, void *data) +{ + if (!token->klass->backlog) + token->klass->backlog = ptrArrayNew ((ptrArrayDeleteFunc)tokenDelete); + + if (ptrArrayCount (token->klass->backlog) > 0) + { + tokenInfo *backlog = ptrArrayLast (token->klass->backlog); + tokenCopyFull (token, backlog, data); + ptrArrayRemoveLast (token->klass->backlog); + tokenDelete (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)tokenDelete); + + 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 000000000..4f68a9218 --- /dev/null +++ b/ctags/main/tokeninfo.h @@ -0,0 +1,97 @@ +/* +* 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. +* +*/ + +#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) + +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 (*delete) (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 tokenDelete (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 tokenIsTypeVal(TKN,TV) ((TKN)->type == (TV)) +#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)) +#define tokenCat(TKN,VS) (vStringCat ((TKN)->string, VS)) +#define tokenCatS(TKN,S) (vStringCatS ((TKN)->string, S)) +#define tokenLast(TKN) (vStringIsEmpty((TKN)->string)? '\0': vStringLast((TKN)->string)) + +/* 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 000000000..45d4aa16f --- /dev/null +++ b/ctags/main/trace.c @@ -0,0 +1,120 @@ +/* +* 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 "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/trace.h b/ctags/main/trace.h dissimilarity index 100% index cb8848cc1..f5f055c16 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/trashbox.h b/ctags/main/trashbox.h index fef93e5be..72b168c06 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,29 @@ 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); + +/* A parser trash box is prepared when `parser' method of a parser is called. + * The parser can register a pair of a memory object and destructor for the object to + * the trash box with parserTrashBoxPut (). + * + * The registered memory objects are destructed after `parser' method is finished in + * the main side. You can delay the destruction with parserTrashBoxPut (). + * + * You can unregister the pair in the `parser' method with parserTrashBoxTakeBack (). + * In that case, specify the memory object of the pair as the argument. + */ 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 000000000..9959e0c53 --- /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 7a4af2a1c..b36bf7711 100644 --- a/ctags/main/types.h +++ b/ctags/main/types.h @@ -15,19 +15,37 @@ 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 sPtrArray; -typedef struct sPtrArray ptrArray; +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; #endif /* CTAGS_MAIN_TYPES_H */ diff --git a/ctags/main/unwindi.c b/ctags/main/unwindi.c new file mode 100644 index 000000000..6a87849e6 --- /dev/null +++ b/ctags/main/unwindi.c @@ -0,0 +1,358 @@ +/* + * + * 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" + +#include + +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 struct sUwiStats uwiStats; + +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 *uwiBuffer; +static unsigned int *uwiMarkerStack; +static unsigned int uwiMarkerStackLength; +static unsigned int *uwiCurrentMarker; + +extern void uwiActivate (unsigned int stackLength) +{ + Assert (stackLength > 0); + + uugcActivate (); + uwiBuffer = ptrArrayNew ((ptrArrayDeleteFunc)uugcDeleteC); + uwiMarkerStackLength = stackLength; + uwiMarkerStack = xMalloc (stackLength, unsigned int); + uwiCurrentMarker = NULL; + + uwiStatsInit (&uwiStats); +} + +extern void uwiDeactivate (struct sUwiStats *statsToBeUpdated) +{ + Assert (uwiBuffer); + Assert (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(); +} + +extern int uwiGetC () +{ + int c; + uugcChar *chr = uugciGetC (); + + c = chr->c; + + if (uwiCurrentMarker) + { + *uwiCurrentMarker += 1; + ptrArrayAdd (uwiBuffer, 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) +{ + + 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 = 0; +} + +extern void uwiPopMarker (const int upto, const bool revertChars) +{ + 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()); + + uwiCurrentMarker = NULL; + uwiStats.underflow = true; + return; + } + + uwiClearMarker (upto, revertChars); + + 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) + { + charHandler (ptrArrayLast (uwiBuffer)); + ptrArrayRemoveLast (uwiBuffer); + *uwiCurrentMarker -= 1; + } +} + +extern void uwiDropMaker () +{ + uwiPopMarker (0, false); +} + +extern void uwiStatsInit (struct sUwiStats *stats) +{ + memset (stats, 0, sizeof (*stats)); +} + +extern void uwiStatsPrint (struct sUwiStats *stats) +{ + 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 new file mode 100644 index 000000000..b4750f9df --- /dev/null +++ b/ctags/main/unwindi.h @@ -0,0 +1,84 @@ +/* + * + * 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" + +/* +* DATA DECLARATIONS +*/ +struct sUwiStats { + int maxLength; + bool overflow; + bool underflow; +}; + +/* +* FUNCTION PROTOTYPES +*/ +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); +extern unsigned long uwiGetLineNumber (void); +extern MIOPos uwiGetFilePosition (void); + +extern void uwiPushMarker (void); +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/ctags/main/vstring.c b/ctags/main/vstring.c index 2e33505b9..17c9bd92c 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 5ac421d43..6e3840e46 100644 --- a/ctags/main/vstring.h +++ b/ctags/main/vstring.h @@ -14,9 +14,7 @@ */ #include "general.h" /* must always come first */ -#if defined(HAVE_STDLIB_H) -# include /* to define size_t */ -#endif +#include /* to define size_t */ #include @@ -28,12 +26,11 @@ */ #define vStringValue(vs) ((vs)->buffer) -#define vStringItem(vs,i) ((vs)->buffer[i]) +#define vStringChar(vs,i) ((vs)->buffer[i]) #define vStringLast(vs) ((vs)->buffer[(vs)->length - 1]) #define vStringLength(vs) ((vs)->length) #define vStringIsEmpty(vs) ((vs)->length == 0) #define vStringSize(vs) ((vs)->size) -#define vStringChar(vs,i) ((vs)->buffer[i]) #define vStringLower(vs) toLowerString((vs)->buffer) #define vStringUpper(vs) toUpperString((vs)->buffer) #define vStringClear(string) \ @@ -66,7 +63,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 +89,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 +108,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 new file mode 100644 index 000000000..276ef0472 --- /dev/null +++ b/ctags/main/writer-ctags.c @@ -0,0 +1,434 @@ +/* +* 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 "field.h" +#include "field_p.h" +#include "mio.h" +#include "options_p.h" +#include "parse_p.h" +#include "ptag_p.h" +#include "read.h" +#include "writer_p.h" +#include "xtag.h" +#include "xtag_p.h" + + +#define CTAGS_FILE "tags" + + +static int writeCtagsEntry (tagWriter *writer CTAGS_ATTR_UNUSED, + 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, + void *clientData); +static bool treatFieldAsFixed (int fieldType); +static void checkCtagsOptions (tagWriter *writer); + +#ifdef WIN32 +static enum filenameSepOp overrideFilenameSeparator (enum filenameSepOp currentSetting); +#endif /* WIN32 */ + +struct rejection { + bool rejectionInThisInput; +}; + +tagWriter uCtagsWriter = { + .writeEntry = writeCtagsEntry, + .writePtagEntry = writeCtagsPtagEntry, + .printPtagByDefault = true, + .preWriteEntry = NULL, + .postWriteEntry = NULL, + .rescanFailedEntry = NULL, + .treatFieldAsFixed = treatFieldAsFixed, + .checkOptions = checkCtagsOptions, +#ifdef WIN32 + .overrideFilenameSeparator = overrideFilenameSeparator, +#endif + .defaultFileName = CTAGS_FILE, +}; + +static void *beginECtagsFile (tagWriter *writer CTAGS_ATTR_UNUSED, MIO * mio CTAGS_ATTR_UNUSED, + void *clientData CTAGS_ATTR_UNUSED) +{ + static struct rejection rej; + + rej.rejectionInThisInput = false; + + return &rej; +} + +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->rejectionInThisInput; +} + +#ifdef WIN32 +static enum filenameSepOp overrideFilenameSeparator (enum filenameSepOp currentSetting) +{ + if (currentSetting == FILENAME_SEP_UNSET) + return FILENAME_SEP_USE_SLASH; + return currentSetting; +} +#endif + +tagWriter eCtagsWriter = { + .writeEntry = writeCtagsEntry, + .writePtagEntry = writeCtagsPtagEntry, + .printPtagByDefault = true, + .preWriteEntry = beginECtagsFile, + .postWriteEntry = endECTagsFile, + .rescanFailedEntry = NULL, + .treatFieldAsFixed = treatFieldAsFixed, + .defaultFileName = CTAGS_FILE, + .checkOptions = checkCtagsOptions, +}; + +static bool hasTagEntryTabOrNewlineChar (const tagEntryInfo * const tag) +{ + + if (doesFieldHaveTabOrNewlineChar (FIELD_NAME, tag, NO_PARSER_FIELD) + || doesFieldHaveTabOrNewlineChar (FIELD_INPUT_FILE, tag, NO_PARSER_FIELD)) + return true; + + if (tag->lineNumberEntry) + { + if (Option.lineDirectives) + { + if (doesFieldHaveTabOrNewlineChar (FIELD_LINE_NUMBER, tag, NO_PARSER_FIELD)) + return true; + } + } + else if (doesFieldHaveTabOrNewlineChar (FIELD_PATTERN, tag, NO_PARSER_FIELD)) + { + /* Pattern may have a tab char. However, doesFieldHaveTabOrNewlineChar returns + * false because NO_PARSER_FIELD may not have doesContainAnyChar handler. + */ + return true; + } + + if (includeExtensionFlags ()) + { + if (isFieldEnabled (FIELD_SCOPE) && doesFieldHaveValue (FIELD_SCOPE, tag) + && (doesFieldHaveTabOrNewlineChar (FIELD_SCOPE_KIND_LONG, tag, NO_PARSER_FIELD) + || doesFieldHaveTabOrNewlineChar (FIELD_SCOPE, tag, NO_PARSER_FIELD))) + return true; + if (isFieldEnabled (FIELD_TYPE_REF) && doesFieldHaveValue (FIELD_TYPE_REF, tag) + && doesFieldHaveTabOrNewlineChar (FIELD_TYPE_REF, tag, NO_PARSER_FIELD)) + return true; + if (isFieldEnabled (FIELD_FILE_SCOPE) && doesFieldHaveValue (FIELD_FILE_SCOPE, tag) + && doesFieldHaveTabOrNewlineChar (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) + && doesFieldHaveTabOrNewlineChar (f[i], tag, NO_PARSER_FIELD)) + return true; + } + } + + for (unsigned int i = 0; i < tag->usedParserFields; i++) + { + const tagField *f = getParserFieldForIndex(tag, i); + fieldType ftype = f->ftype; + if (isFieldEnabled (ftype)) + { + if (doesFieldHaveTabOrNewlineChar (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) +{ + 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; + + for (i = 0; i < tag->usedParserFields; i++) + { + const tagField *f = getParserFieldForIndex(tag, i); + fieldType ftype = f->ftype; + if (! isFieldEnabled (ftype)) + continue; + + length += mio_printf(mio, "\t%s:%s", + getFieldName (ftype), + escapeFieldValueFull (writer, tag, ftype, i)); + } + 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_LETTER))) + { + /* Use kind long name */ + str = kdef->name; + } + else if (kdef->letter != KIND_NULL_LETTER && (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, + void *clientData CTAGS_ATTR_UNUSED) +{ + if (writer->private) + { + struct rejection *rej = writer->private; + if (hasTagEntryTabOrNewlineChar (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 ()) + { + length += addExtensionFields (writer, mio, tag); + length += addParserFields (writer, mio, tag); + } + + length += mio_printf (mio, "\n"); + + 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, + void *clientData CTAGS_ATTR_UNUSED) +{ + + bool extras = includeExtensionFlags () && isFieldEnabled (FIELD_EXTRAS); + const char *xsep = extras? ";\" ": ""; + const char *fsep = extras? ":": ""; + const char *fieldx = extras? getFieldName (FIELD_EXTRAS): ""; + const char *xptag = extras? getXtagName (XTAG_PSEUDO_TAGS): ""; + + return parserName + +#define OPT(X) ((X)?(X):"") + ? mio_printf (mio, "%s%s%s%s\t%s\t/%s/%s%s%s%s\n", + PSEUDO_TAG_PREFIX, desc->name, PSEUDO_TAG_SEPARATOR, parserName, + OPT(fileName), OPT(pattern), + xsep, fieldx, fsep, xptag) + : mio_printf (mio, "%s%s\t%s\t/%s/%s%s%s%s\n", + PSEUDO_TAG_PREFIX, desc->name, + OPT(fileName), OPT(pattern), + xsep, fieldx, fsep, xptag); +#undef OPT +} + +static bool treatFieldAsFixed (int fieldType) +{ + switch (fieldType) + { + case FIELD_NAME: + case FIELD_INPUT_FILE: + case FIELD_PATTERN: + return true; + default: + return false; + } +} + +static void checkCtagsOptions (tagWriter *writer CTAGS_ATTR_UNUSED) +{ + if (isFieldEnabled (FIELD_KIND_KEY) + && (!(isFieldEnabled (FIELD_KIND_LONG) || + isFieldEnabled (FIELD_KIND)))) + { + error (WARNING, "though %c/%s field is enabled, neither %c nor %c field is not enabled", + getFieldLetter (FIELD_KIND_KEY), + getFieldName (FIELD_KIND_KEY), + getFieldLetter (FIELD_KIND), + getFieldLetter (FIELD_KIND_LONG)); + error (WARNING, "enable the %c field to make the %c/%s field printable", + getFieldLetter (FIELD_KIND_LONG), + getFieldLetter (FIELD_KIND_KEY), + getFieldName (FIELD_KIND_KEY)); + enableField (FIELD_KIND_LONG, true, true); + } + if (isFieldEnabled (FIELD_SCOPE_KEY) + && !isFieldEnabled (FIELD_SCOPE)) + { + error (WARNING, "though %c/%s field is enabled, %c field is not enabled", + getFieldLetter (FIELD_SCOPE_KEY), + getFieldName (FIELD_SCOPE_KEY), + getFieldLetter (FIELD_SCOPE)); + error (WARNING, "enable the %c field to make the %c/%s field printable", + getFieldLetter (FIELD_SCOPE), + getFieldLetter (FIELD_SCOPE_KEY), + getFieldName (FIELD_SCOPE_KEY)); + enableField (FIELD_SCOPE, true, true); + } +} diff --git a/ctags/main/writer-etags.c b/ctags/main/writer-etags.c new file mode 100644 index 000000000..9887a7ac1 --- /dev/null +++ b/ctags/main/writer-etags.c @@ -0,0 +1,202 @@ +/* +* 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 "parse.h" +#include "parse_p.h" +#include "read.h" +#include "routines.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, + 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, + .rescanFailedEntry = NULL, + .treatFieldAsFixed = NULL, + .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, + void *clientData 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, + void *clientData CTAGS_ATTR_UNUSED) +{ + const char *line; + struct sEtags *etags = writer->private; + + mio_printf (mainfp, "\f\n%s,%ld\n", filename, (long) etags->byteCount); + setNumTagsAdded (numTagsAdded () + 1); + 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_unref (etags->mio); + remove (etags->name); + eFree (etags->name); + etags->vLine = NULL; + etags->mio = NULL; + etags->name = NULL; + } + return false; +} + +static const char* ada_suffix (const tagEntryInfo *const tag, const char *const line) +{ + kindDefinition *kdef = getLanguageKind(tag->langType, tag->kindIndex); + + Assert (kdef); + + /* Mapping from ctags' kind letter to etags's suffix string. + * See https://www.gnu.org/software/emacs/manual/html_node/emacs/Tag-Syntax.html */ + switch (kdef->letter) + { + case 'p': + case 'k': + return "/b"; + case 'K': + return "/k"; + case 'P': + return "/s"; + case 't': + return "/t"; + case 'R': + case 'r': + { + /* Unlike etags, ctags uses the procedure kind for both + * procedures and functions. So in the level, emitting a tag, + * we cannot distinguish whether a tag is for a procedureor a + * function. + * + * If the typeref field of the tag is filled, we can say the tag + * is for a function. However, Ada parser doesn't implement the + * typeref field yet, and implementing it is not so easy. + * + * So we have to take an unclean way here: scanning the input + * line again. + * FIXME: remove the scanning code and implement the typeref field + * in Ada. + */ + const char *r = strstr (line, "return"); + const char *f = strstr (line, "function"); + const char *p = strstr (line, "procedure"); + if (r && f) + return "/f"; + else if (p && !r) + return "/p"; + return ""; /* Unknown */ + } + default: + return ""; + } +} + +static int writeEtagsEntry (tagWriter *writer, + MIO * mio, const tagEntryInfo *const tag, + void *clientData CTAGS_ATTR_UNUSED) +{ + langType adaLangType = getNamedLanguage ("Ada", 0); + Assert (adaLangType != LANG_IGNORE); + + 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%s\001%lu,%ld\n", line, + tag->name, + (tag->langType == adaLangType) + ? ada_suffix (tag, line) + : "", + 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 000000000..4cb4194d0 --- /dev/null +++ b/ctags/main/writer-json.c @@ -0,0 +1,294 @@ +/* +* 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 "field_p.h" +#include "mio.h" +#include "options_p.h" +#include "read.h" +#include "routines.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, + 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, + void *clientData); + +tagWriter jsonWriter = { + .writeEntry = writeJsonEntry, + .writePtagEntry = writeJsonPtagEntry, + .printPtagByDefault = true, + .preWriteEntry = NULL, + .postWriteEntry = NULL, + .rescanFailedEntry = NULL, + .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 = escapeFieldValueRaw (tag, ftype, NO_PARSER_FIELD); + + 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 && doesFieldHaveRenderer (xftype, false) && 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; + + for (i = 0; i < tag->usedParserFields; i++) + { + const tagField *f = getParserFieldForIndex(tag, i); + fieldType ftype = f->ftype; + if (! isFieldEnabled (ftype)) + continue; + + unsigned int dt = getFieldDataType (ftype); + json_t *o; + if (dt & FIELDTYPE_STRING) + { + const char *str = escapeFieldValueRaw (tag, ftype, i); + if (dt & FIELDTYPE_BOOL && str[0] == '\0') + o = json_false (); + else + o = json_string (str); + } + else if (dt & FIELDTYPE_INTEGER) + { + /* NOT IMPLEMENTED YET */ + AssertNotReached (); + o = json_null (); + } + else if (dt & FIELDTYPE_BOOL) + o = json_true (); + else + { + AssertNotReached (); + o = json_null (); + } + + json_object_set_new (response, getFieldName (ftype), o); + } +} + +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, + void *clientData CTAGS_ATTR_UNUSED) +{ + 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 ()) + { + addExtensionFields (response, tag); + addParserFields (response, tag); + } + + /* 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); + out: + json_decref (response); + + return length; +} + +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, + void *clientData CTAGS_ATTR_UNUSED) +{ +#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, langType language CTAGS_ATTR_UNUSED, + const 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, langType language CTAGS_ATTR_UNUSED, + const 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 000000000..7885e22f8 --- /dev/null +++ b/ctags/main/writer-xref.c @@ -0,0 +1,106 @@ +/* +* 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 "field_p.h" +#include "fmt_p.h" +#include "mio.h" +#include "options_p.h" +#include "ptag_p.h" +#include "writer_p.h" + +#include + + +static int writeXrefEntry (tagWriter *writer CTAGS_ATTR_UNUSED, + MIO * mio, const tagEntryInfo *const tag, + void *clientData CTAGS_ATTR_UNUSED); +static int writeXrefPtagEntry (tagWriter *writer, MIO * mio, const ptagDesc *desc, + const char *const fileName, + const char *const pattern, + const char *const parserName, + void *clientData); + +tagWriter xrefWriter = { + .writeEntry = writeXrefEntry, + .writePtagEntry = writeXrefPtagEntry, + .printPtagByDefault = false, + .preWriteEntry = NULL, + .postWriteEntry = NULL, + .rescanFailedEntry = NULL, + .treatFieldAsFixed = NULL, + .defaultFileName = NULL, +}; + +static int writeXrefPtagEntry (tagWriter *writer, MIO * mio, const ptagDesc *desc, + const char *const fileName, + const char *const pattern, + const char *const parserName, + void *clientData) +{ + tagEntryInfo e; + vString *name = vStringNewInit(PSEUDO_TAG_PREFIX); + + memset (&e, 0, sizeof(e)); + + e.isPseudoTag = 1; + + vStringCatS (name, desc->name); + if (parserName) + { + vStringCatS (name, PSEUDO_TAG_SEPARATOR); + vStringCatS (name, parserName); + } + e.name = vStringValue (name); + e.inputFileName = fileName; + e.pattern = pattern; + + int length = writeXrefEntry (writer, mio, &e, clientData); + + vStringDelete (name); + + return length; +} + +static int writeXrefEntry (tagWriter *writer CTAGS_ATTR_UNUSED, + MIO * mio, const tagEntryInfo *const tag, + void *clientData CTAGS_ATTR_UNUSED) +{ + 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; +} diff --git a/ctags/main/writer.c b/ctags/main/writer.c new file mode 100644 index 000000000..56d404c0d --- /dev/null +++ b/ctags/main/writer.c @@ -0,0 +1,189 @@ +/* +* 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 "debug.h" +#include "entry_p.h" +#include "options_p.h" +#include "writer_p.h" + +extern tagWriter uCtagsWriter; +extern tagWriter eCtagsWriter; +extern tagWriter etagsWriter; +extern tagWriter xrefWriter; +extern tagWriter jsonWriter; + +static tagWriter *writerTable [WRITER_COUNT] = { + [WRITER_U_CTAGS] = &uCtagsWriter, + [WRITER_E_CTAGS] = &eCtagsWriter, + [WRITER_ETAGS] = &etagsWriter, + [WRITER_XREF] = &xrefWriter, + [WRITER_JSON] = &jsonWriter, + [WRITER_CUSTOM] = NULL, +}; + +static tagWriter *writer; + +extern void setTagWriter (writerType wtype, tagWriter *customWriter) +{ + if (wtype != WRITER_CUSTOM) + writer = writerTable [wtype]; + else + writer = customWriter; + writer->type = wtype; +} + +extern void writerSetup (MIO *mio, void *clientData) +{ + writer->clientData = clientData; + + if (writer->preWriteEntry) + writer->private = writer->preWriteEntry (writer, mio, + writer->clientData); + else + writer->private = NULL; +} + +extern bool writerTeardown (MIO *mio, const char *filename) +{ + if (writer->postWriteEntry) + { + bool r; + r = writer->postWriteEntry (writer, mio, filename, + writer->clientData); + writer->private = NULL; + return r; + } + return false; +} + +extern int writerWriteTag (MIO * mio, const tagEntryInfo *const tag) +{ + return writer->writeEntry (writer, mio, tag, + writer->clientData); +} + +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, + writer->clientData); + +} + +extern void writerRescanFailed (unsigned long validTagNum) +{ + if (writer->rescanFailedEntry) + writer->rescanFailedEntry(writer, validTagNum, writer->clientData); +} + +extern bool ptagMakeCtagsOutputMode (ptagDesc *desc, langType langType CTAGS_ATTR_UNUSED, + const 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; +} + +extern bool writerDoesTreatFieldAsFixed (int fieldType) +{ + if (writer->treatFieldAsFixed) + return writer->treatFieldAsFixed (fieldType); + return false; +} + +#ifdef WIN32 +extern enum filenameSepOp getFilenameSeparator (enum filenameSepOp currentSetting) +{ + if (writer->overrideFilenameSeparator) + return writer->overrideFilenameSeparator (currentSetting); + return currentSetting; +} +#endif + +extern bool ptagMakeCtagsOutputFilesep (ptagDesc *desc, + langType language CTAGS_ATTR_UNUSED, + const void *data) +{ + const char *sep = "slash"; +#ifdef WIN32 + const optionValues *opt = data; + if (getFilenameSeparator (opt->useSlashAsFilenameSeparator) + != FILENAME_SEP_USE_SLASH) + sep = "backslash"; +#endif + return writePseudoTag (desc, sep, "slash or backslash", NULL); +} + +extern bool ptagMakeCtagsOutputExcmd (ptagDesc *desc, + langType language CTAGS_ATTR_UNUSED, + const void *data) +{ + const char *excmd; + const optionValues *opt = data; + switch (opt->locate) + { + case EX_MIX: + excmd = "mixed"; + break; + case EX_LINENUM: + excmd = "number"; + break; + case EX_PATTERN: + excmd = "pattern"; + break; + case EX_COMBINE: + excmd = "combine"; + break; + default: + AssertNotReached (); + excmd = "bug!"; + break; + } + return writePseudoTag (desc, excmd, + "number, pattern, mixed, or combine", + NULL); +} + +extern void writerCheckOptions (void) +{ + if (writer->checkOptions) + writer->checkOptions (writer); +} + +extern bool writerPrintPtagByDefault (void) +{ + return writer->printPtagByDefault; +} diff --git a/ctags/main/writer_p.h b/ctags/main/writer_p.h new file mode 100644 index 000000000..16edc5f04 --- /dev/null +++ b/ctags/main/writer_p.h @@ -0,0 +1,106 @@ +/* +* 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 "options_p.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_CUSTOM, + WRITER_COUNT, +} writerType; + +struct sTagWriter; +typedef struct sTagWriter tagWriter; +struct sTagWriter { + 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 *clientData); + bool printPtagByDefault; + 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, + void *clientData); + void (* rescanFailedEntry) (tagWriter *writer, unsigned long validTagNum, + void *clientData); + bool (* treatFieldAsFixed) (int fieldType); + + void (* checkOptions) (tagWriter *writer); + +#ifdef WIN32 + enum filenameSepOp (* overrideFilenameSeparator) (enum filenameSepOp currentSetting); +#endif /* WIN32 */ + + 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; +}; + +/* 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); +int writerWritePtag (MIO * mio, + const ptagDesc *desc, + const char *const fileName, + const char *const pattern, + const char *const parserName); + +void writerRescanFailed (unsigned long validTagNum); + +extern const char *outputDefaultFileName (void); + +extern size_t truncateTagLineAfterTag (char *const line, const char *const token, + const bool discardNewline); +extern void abort_if_ferror(MIO *const fp); + +extern bool ptagMakeJsonOutputVersion (ptagDesc *desc, langType language CTAGS_ATTR_UNUSED, const void *data CTAGS_ATTR_UNUSED); +extern bool ptagMakeCtagsOutputMode (ptagDesc *desc, langType language CTAGS_ATTR_UNUSED, const void *data CTAGS_ATTR_UNUSED); +extern bool ptagMakeCtagsOutputFilesep (ptagDesc *desc, langType language CTAGS_ATTR_UNUSED, const void *data); +extern bool ptagMakeCtagsOutputExcmd (ptagDesc *desc, langType language CTAGS_ATTR_UNUSED, const void *data); + +extern bool writerCanPrintPtag (void); +extern bool writerDoesTreatFieldAsFixed (int fieldType); + +extern void writerCheckOptions (void); +extern bool writerPrintPtagByDefault (void); + +#ifdef WIN32 +extern enum filenameSepOp getFilenameSeparator (enum filenameSepOp currentSetting); +#endif /* WIN32 */ +#endif /* CTAGS_MAIN_WRITER_PRIVATE_H */ diff --git a/ctags/main/xtag.c b/ctags/main/xtag.c dissimilarity index 66% index f6d5a4602..61fb97a4c 100644 --- a/ctags/main/xtag.c +++ b/ctags/main/xtag.c @@ -1,157 +1,389 @@ -/* - * - * 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. - * - */ - -#include "general.h" /* must always come first */ -#include "debug.h" -#include "main.h" -#include "options.h" -#include "routines.h" -#include "xtag.h" - -#include - - -static bool isPseudoTagsEnabled (xtagDesc *pdesc CTAGS_ATTR_UNUSED) -{ - return ! isDestinationStdout (); -} - -static xtagDesc xtagDescs [] = { - { true, 'F', "fileScope", - "Include tags of file scope", NULL}, - { false, 'f', "inputFile", - "Include an entry for the base file name of every input file", NULL}, - { false, 'p', "pseudo", - "Include pseudo tags", - isPseudoTagsEnabled}, - { false, 'q', "qualified", - "Include an extra class-qualified tag entry for each tag", NULL}, - { false, 'r', "reference", - "Include reference tags", NULL}, - { false, 's', "subparser", - "Include tags generated by sub parsers", NULL}, - { true, '\0', "anonymous", - "Include tags for non-named objects like lambda"}, -}; - -extern xtagDesc* getXtagDesc (xtagType type) -{ - Assert ((0 <= type) && (type < XTAG_COUNT)); - return xtagDescs + type; -} - -typedef bool (* xtagPredicate) (xtagDesc *pdesc, const void *user_data); -static xtagType getXtagTypeGeneric (xtagPredicate predicate, const void *user_data) -{ - int i; - - for (i = 0; i < XTAG_COUNT; i++) - { - if (predicate (xtagDescs + i, user_data)) - return i; - } - return XTAG_UNKNOWN; -} - -static bool xtagEqualByLetter (xtagDesc *pdesc, const void *user_data) -{ - return (pdesc->letter == *((char *)user_data))? true: false; -} - -extern xtagType getXtagTypeForLetter (char letter) -{ - return getXtagTypeGeneric (xtagEqualByLetter, &letter); -} - -static bool xtagEqualByName (xtagDesc *pdesc, const void *user_data) -{ - return (strcmp (pdesc->name, user_data) == 0)? true: false; -} - -extern xtagType getXtagTypeForName (const char *name) -{ - return getXtagTypeGeneric (xtagEqualByName, name); -} - - -#define PR_XTAG_WIDTH_LETTER 7 -#define PR_XTAG_WIDTH_NAME 22 -#define PR_XTAG_WIDTH_ENABLED 7 -#define PR_XTAG_WIDTH_DESC 30 - -#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" - -static void printXtag (xtagType i) -{ - 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"); -} - -extern void printXtags (void) -{ - unsigned int i; - - if (Option.withListHeader) - printf ((Option.machinable? "%s\t%s\t%s\t%s\n": MAKE_XTAG_FMT(s)), - "#LETTER", "NAME", "ENABLED", "DESCRIPTION"); - - for (i = 0; i < XTAG_COUNT; i++) - printXtag (i); -} - -extern bool isXtagEnabled (xtagType type) -{ - xtagDesc* desc = getXtagDesc (type); - - Assert (desc); - - if (desc->isEnabled) - return desc->isEnabled (desc); - else - return desc->enabled; -} - -extern bool enableXtag (xtagType type, bool state) -{ - bool old; - xtagDesc* desc = getXtagDesc (type); - - Assert (desc); - - old = isXtagEnabled (type); - desc->enabled = state; - desc->isEnabled = NULL;; - - return old; -} - -const char* getXtagName (xtagType type) -{ - xtagDesc* desc = getXtagDesc (type); - if (desc) - return desc->name; - else - return NULL; -} +/* + * + * 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. + * + */ + +#include "general.h" /* must always come first */ +#include "ctags.h" +#include "debug.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 + +typedef struct sXtagObject { + xtagDefinition *def; + langType language; + xtagType sibling; +} xtagObject; + +static bool isPseudoTagsEnabled (xtagDefinition *pdef CTAGS_ATTR_UNUSED) +{ + if (!writerCanPrintPtag()) + return false; + if (!writerPrintPtagByDefault()) + return false; + + return ! isDestinationStdout (); +} + +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" }, + { false, 'f', "inputFile", + "Include an entry for the base file name of every input file", + NULL, + NULL, + enableFileKind}, + { false, 'p', "pseudo", + "Include pseudo tags", + isPseudoTagsEnabled, + isPseudoTagsFixed}, + { false, 'q', "qualified", + "Include an extra class-qualified tag entry for each tag"}, + { false, 'r', "reference", + "Include reference tags"}, + { false, 'g', "guest", + "Include tags generated by guest parsers"}, + { true, 's', "subparser", + "Include tags generated by subparsers"}, + { true, '\0', "anonymous", + "Include tags for non-named objects like lambda"}, +}; + +static unsigned int xtagObjectUsed; +static unsigned int xtagObjectAllocated; +static xtagObject* xtagObjects; + +static xtagObject* getXtagObject (xtagType type) +{ + Assert ((0 <= type) && ((unsigned int)type < xtagObjectUsed)); + return (xtagObjects + type); +} + +extern xtagDefinition* getXtagDefinition (xtagType type) +{ + Assert ((0 <= type) && ((unsigned int)type < xtagObjectUsed)); + + return getXtagObject (type)->def; +} + +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 (xtagObjects + i, language, user_data)) + return i; + } + return XTAG_UNKNOWN; +} + +static bool xtagEqualByLetter (xtagObject *pobj, langType language CTAGS_ATTR_UNUSED, + const void *user_data) +{ + return (pobj->def->letter == *((char *)user_data))? true: false; +} + +extern xtagType getXtagTypeForLetter (char letter) +{ + return getXtagTypeGeneric (xtagEqualByLetter, LANG_IGNORE, &letter); +} + +static bool xtagEqualByNameAndLanguage (xtagObject *pobj, langType language, const void *user_data) +{ + 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 getXtagTypeForNameAndLanguage (const char *name, langType language) +{ + 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); +} + +static void xtagColprintAddLine (struct colprintTable *table, int xtype) +{ + xtagObject* xobj = getXtagObject (xtype); + xtagDefinition *xdef = xobj->def; + + struct colprintLine *line = colprintTableGetNewLine(table); + + 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) +{ + for (int i = 0; i < XTAG_COUNT; i++) + xtagColprintAddLine (table, i); +} + +extern void xtagColprintAddLanguageLines (struct colprintTable *table, langType language) +{ + 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; + + 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); + + 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) +{ + xtagDefinition* def = getXtagDefinition (type); + + Assert (def); + + if (def->isEnabled) + return def->isEnabled (def); + else + 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; + xtagDefinition* def = getXtagDefinition (type); + + Assert (def); + + old = isXtagEnabled (type); + + 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) +{ + xtagDefinition* def = getXtagDefinition (type); + if (def) + return def->name; + else + return NULL; +} + +const char* getXtagDescription (xtagType type) +{ + xtagDefinition* def = getXtagDefinition (type); + if (def) + return def->description; + 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 17b3c9eb0..735c2857e 100644 --- a/ctags/main/xtag.h +++ b/ctags/main/xtag.h @@ -12,8 +12,16 @@ #ifndef CTAGS_MAIN_XTAG_H #define CTAGS_MAIN_XTAG_H +/* +* INCLUDE FILES +*/ + #include "general.h" +/* +* DATA DECLARATIONS +*/ + typedef enum eXtagType { /* extra tag content control */ XTAG_UNKNOWN = -1, @@ -22,14 +30,19 @@ typedef enum eXtagType { /* extra tag content control */ XTAG_PSEUDO_TAGS, XTAG_QUALIFIED_TAGS, XTAG_REFERENCE_TAGS, - XTAG_TAGS_GENERATED_BY_SUB_PARSERS, + XTAG_GUEST, + XTAG_TAGS_GENERATED_BY_GUEST_PARSERS = XTAG_GUEST, /* Geany uses the old name */ + XTAG_SUBPARSER, 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 +52,16 @@ 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 xtagType getXtagTypeForLetter (char letter); -extern xtagType getXtagTypeForName (const char *name); extern bool isXtagEnabled (xtagType type); -extern bool enableXtag (xtagType type, bool state); -const char* getXtagName (xtagType type); -extern void printXtags (void); #endif /* CTAGS_MAIN_FIELD_H */ diff --git a/ctags/main/xtag_p.h b/ctags/main/xtag_p.h new file mode 100644 index 000000000..7326e9800 --- /dev/null +++ b/ctags/main/xtag_p.h @@ -0,0 +1,52 @@ +/* + * + * 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); +const char* getXtagDescription (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 */ -- 2.11.4.GIT