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 ...

Reply via email to