Hello,
Please find attached a new patch that solves the problem of empty tag files
(and no references/definitions returned by 'global') when using Universal Ctags
with particular configuration files. The problem is triggered by some Universal
Ctags options that alter the output format, making 'gtags' fail to parse its
output.
The solution is to use '--options=NONE' at start of options passed to Universal
Ctags (and '--quiet' before to avoid a warning about skipping configuration
files).
But it is sometimes useful to specify options to Universal Ctags that alter the
list of returned tags (but not the format, e.g., '--kinds-C='). To this end, I
also added the 'ctagsoptfile' configuration option.
Not tested (even compiled) on Windows, although I modified the code for this
variant as well with care.
Regards.
--
Olivier Certner
--- gtags.conf.in.~1.55.~ 2017-12-12 15:05:24.000000000 +0100
+++ gtags.conf.in 2023-04-07 12:00:11.702854000 +0200
@@ -181,6 +181,7 @@
universal-ctags|setting to use Universal Ctags plug-in parser:\
:tc=common:\
:ctagscom=@UNIVERSAL_CTAGS@:\
+ :ctagsoptfile=:\
:ctagslib=$libdir/gtags/universal-ctags.la:\
:langmap=Ada\:.adb.ads.Ada:\
:langmap=Ant\:(build.xml)(*.build.xml).ant.xml:\
--- plugin-factory/Makefile.am.~1.13.~ 2023-04-06 21:28:23.170662000 +0200
+++ plugin-factory/Makefile.am 2023-04-07 12:02:21.191300000 +0200
@@ -22,7 +22,7 @@
exuberant_ctags_la_LDFLAGS = -module -avoid-version -no-undefined
# Univercal Ctags parser
universal_ctags_la_SOURCES = exuberant-ctags.c
-universal_ctags_la_CFLAGS = -DUSE_EXTRA_FIELDS
+universal_ctags_la_CFLAGS = -DUNIVERSAL_CTAGS_FLAVOR
universal_ctags_la_LDFLAGS = -module -avoid-version -no-undefined
# Pygments parser
--- plugin-factory/exuberant-ctags.c.~1.15.~ 2021-10-26 01:00:31.000000000 +0200
+++ plugin-factory/exuberant-ctags.c 2023-04-07 11:52:33.783619000 +0200
@@ -49,6 +49,9 @@
#define TERMINATOR "###terminator###"
#define LANGMAP_OPTION "--langmap="
+#if defined(UNIVERSAL_CTAGS_FLAVOR)
+#define OPTIONS_OPTION "--options="
+#endif
#define INITIAL_BUFSIZE 1024
static FILE *ip, *op;
@@ -60,13 +63,14 @@
* In execution phase, plug-in parser get the value from the configuration file
* (gtags.conf, $HOME/.globalrc) if it is defined.
*/
-#if defined(USE_EXTRA_FIELDS)
+#if defined(UNIVERSAL_CTAGS_FLAVOR)
static char *ctagscom = UNIVERSAL_CTAGS;
-static const char *ctagsnotfound = "Universal Ctags not found. Please see ./configure --help.";
+static const char *ctagsnotfound = "No Universal Ctags executable specified."
#else
static char *ctagscom = EXUBERANT_CTAGS;
-static const char *ctagsnotfound = "Exuberant Ctags not found. Please see ./configure --help.";
+static const char *ctagsnotfound = "No Exuberant Ctags executable specified."
#endif
+" Specify through ./configure or the 'ctagscom' configuration variable.";
#ifdef __GNUC__
static void terminate_ctags(void) __attribute__((destructor));
@@ -98,7 +102,8 @@
#include <fcntl.h>
static HANDLE pid;
static char argv[] = "\" "
-#if defined(USE_EXTRA_FIELDS)
+#if defined(UNIVERSAL_CTAGS_FLAVOR)
+ "--quiet --options=NONE "
"\"--_xformat=%R %-16N %4n %-16F %C\" "
"--extras=+r "
"--fields=+r "
@@ -116,21 +121,58 @@
SECURITY_ATTRIBUTES sa;
STARTUPINFO si;
PROCESS_INFORMATION pi;
- char* arg;
+ char* org_arg, arg;
char *path = param->getconf("ctagscom");
+ size_t ctagscom_len;
+ size_t const langmap_len = strlen(param->langmap);
+#ifdef UNIVERSAL_CTAGS_FLAVOR
+ char const * options_file = param->getconf("ctagsoptfile");
+ size_t full_options_file_len;
- if (path && strlen(path) > 0 && strcmp(path, "no") != 0)
+ if (options_file != NULL &&
+ (!options_file[0] || !strcmp(options_file, "no"))) {
+ free(options_file);
+ options_file = NULL;
+ }
+
+ full_options_file_len = options_file == NULL ? 0 :
+ strlen(options_file) + sizeof(OPTIONS_OPTION) - 1 + 1 /*SPACE*/;
+#endif
+
+ if (path && path[0] && strcmp(path, "no") != 0)
ctagscom = path;
- if (!ctagscom || !strlen(ctagscom) || !strcmp(ctagscom, "no"))
+ if (!ctagscom || !(ctagscom_len = strlen(ctagscom)) ||
+ !strcmp(ctagscom, "no"))
param->die(ctagsnotfound);
- arg = malloc(1 + strlen(ctagscom) + sizeof(argv) + strlen(param->langmap));
- if (arg == NULL)
+ org_arg = arg =
+ malloc(1 /*NUL*/ + 1 /*First double-quote*/ +
+ ctagscom_len + sizeof(argv) - 1 + langmap_len
+#ifdef UNIVERSAL_CTAGS_FLAVOR
+ + full_options_file_len
+#endif
+ );
+ if (org_arg == NULL)
param->die("short of memory.");
- *arg = '"';
- strcpy(arg+1, ctagscom);
- strcat(arg, argv);
- copy_langmap_converting_cpp(arg + strlen(arg), param->langmap);
+ *arg++ = '"';
+ memcpy(arg, ctagscom, ctagscom_len);
+ arg += ctagscom_len;
+ free(path);
+ ctagscom = NULL; // Prevent inadvertent use-after-free
+ memcpy(arg, argv, sizeof(argv) - 1);
+ arg += sizeof(argv) - 1;
+ copy_langmap_converting_cpp(arg, param->langmap);
+ // Previous call preserves length (and inserts NUL)
+ arg += langmap_len;
+#ifdef UNIVERSAL_CTAGS_FLAVOR
+ if (options_file != NULL) {
+ *arg++ = ' ';
+ memcpy(arg, OPTIONS_OPTION, sizeof(OPTIONS_OPTION) - 1);
+ arg += sizeof(OPTIONS_OPTION) - 1;
+ strcpy(arg, options_file);
+ free(options_file);
+ }
+#endif
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
@@ -146,8 +188,11 @@
si.hStdOutput = ipipe[1];
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
si.dwFlags = STARTF_USESTDHANDLES;
- if (!CreateProcess(NULL, arg, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
- param->die(ctagsnotfound);
+ if (!CreateProcess(NULL, org_arg, NULL, NULL,
+ TRUE, 0, NULL, NULL, &si, &pi))
+ param->die("cannot execute '%s'. (CreateProcess() failed)",
+ org_arg);
+ free(org_arg);
CloseHandle(opipe[0]);
CloseHandle(ipipe[1]);
CloseHandle(pi.hThread);
@@ -176,9 +221,10 @@
#include <sys/wait.h>
static pid_t pid;
static char *argv[] = {
- "ctags",
- NULL,
-#if defined(USE_EXTRA_FIELDS)
+ NULL, // Slot for program name
+#ifdef UNIVERSAL_CTAGS_FLAVOR
+ "--quiet",
+ "--options=NONE",
"--_xformat=%R %-16N %4n %-16F %C",
"--extras=+r",
"--fields=+r",
@@ -188,45 +234,96 @@
"-xu",
"--filter",
"--filter-terminator=" TERMINATOR "\n",
- NULL
+ NULL, // Slot for LANGMAP_OPTION
+#ifdef UNIVERSAL_CTAGS_FLAVOR
+ NULL, // Slot for OPTIONS_FILE
+#endif
+ NULL // Sentinel
};
static void
start_ctags(const struct parser_param *param)
{
int opipe[2], ipipe[2];
- char *path = param->getconf("ctagscom");
- if (path && strlen(path) > 0 && strcmp(path, "no") != 0)
- ctagscom = path;
- if (!ctagscom || !strlen(ctagscom) || !strcmp(ctagscom, "no"))
- param->die(ctagsnotfound);
- argv[0] = ctagscom;
- argv[1] = malloc(sizeof(LANGMAP_OPTION) + strlen(param->langmap));
- if (argv[1] == NULL)
- param->die("short of memory.");
- memcpy(argv[1], LANGMAP_OPTION, sizeof(LANGMAP_OPTION) - 1);
- copy_langmap_converting_cpp(argv[1] + sizeof(LANGMAP_OPTION) - 1, param->langmap);
-
if (pipe(opipe) < 0 || pipe(ipipe) < 0)
param->die("cannot create pipe.");
pid = fork();
+
if (pid == 0) {
+ char *path = param->getconf("ctagscom");
+ char ** const argv_last = argv +
+ (sizeof(argv) / sizeof(*argv) - 2);
+#ifdef UNIVERSAL_CTAGS_FLAVOR
+ char const * options_file = param->getconf("ctagsoptfile");
+ char ** const langmap_slot = argv_last - 1;
+ char ** const options_file_slot = argv_last;
+#else
+ char ** const langmap_slot = argv_last;
+#endif
+
/* child process */
close(opipe[1]);
close(ipipe[0]);
if (dup2(opipe[0], STDIN_FILENO) < 0
- || dup2(ipipe[1], STDOUT_FILENO) < 0)
+ || dup2(ipipe[1], STDOUT_FILENO) < 0)
param->die("dup2 failed.");
close(opipe[0]);
close(ipipe[1]);
+
+ if (path && path[0] && strcmp(path, "no") != 0)
+ ctagscom = path;
+ if (!ctagscom || !strlen(ctagscom) || !strcmp(ctagscom, "no"))
+ param->die(ctagsnotfound);
+ argv[0] = ctagscom;
+ *langmap_slot = malloc(1 + sizeof(LANGMAP_OPTION) - 1 +
+ strlen(param->langmap));
+ if (*langmap_slot == NULL)
+ param->die("short of memory.");
+ memcpy(*langmap_slot, LANGMAP_OPTION,
+ sizeof(LANGMAP_OPTION) - 1);
+ copy_langmap_converting_cpp
+ (*langmap_slot + sizeof(LANGMAP_OPTION) - 1,
+ param->langmap);
+
+#ifdef UNIVERSAL_CTAGS_FLAVOR
+ if (options_file != NULL &&
+ (!options_file[0] || !strcmp(options_file, "no"))) {
+ free(options_file);
+ options_file = NULL;
+ *options_file_slot = NULL;
+ }
+
+ if (options_file != NULL) {
+ size_t options_file_len = strlen(options_file);
+
+ *options_file_slot = malloc
+ (1 + sizeof(OPTIONS_OPTION) - 1 + options_file_len);
+ if (*options_file_slot == NULL)
+ param->die("short of memory.");
+ memcpy(*options_file_slot,
+ OPTIONS_OPTION, sizeof(OPTIONS_OPTION) - 1);
+ memcpy(*options_file_slot + sizeof(OPTIONS_OPTION) - 1,
+ options_file, options_file_len);
+ (*options_file_slot)
+ [sizeof(OPTIONS_OPTION) - 1 + options_file_len] = 0;
+
+ free(options_file);
+ }
+#endif
+
execvp(ctagscom, argv);
- param->die("cannot execute '%s'. (execvp failed)", ctagscom);
+
+ free(path);
+ free(*langmap_slot);
+#ifdef UNIVERSAL_CTAGS_FLAVOR
+ free(*options_file_slot);
+#endif
+
+ param->die("cannot execute '%s'. (execvp() failed)", ctagscom);
}
/* parent process */
if (pid < 0)
param->die("fork failed.");
- free(path);
- free(argv[1]);
close(opipe[0]);
close(ipipe[1]);
ip = fdopen(ipipe[0], "r");
@@ -286,7 +383,7 @@
int type = PARSER_DEF;
char *p, *tagname, *filename;
-#if defined(USE_EXTRA_FIELDS)
+#if defined(UNIVERSAL_CTAGS_FLAVOR)
/*
* Output of ctags:
* ctags -x ...