1 /*
2  * Copyright (c) 2010, 2014, 2020 Tama Communications Corporation
3  *
4  * This file is part of GNU GLOBAL.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 #include <ctype.h>
24 #include <stdio.h>
25 #include "getopt.h"
26 #include "checkalloc.h"
27 #include "die.h"
28 #include "env.h"
29 #include "locatestring.h"
30 #include "strbuf.h"
31 #include "test.h"
32 #include "gpathop.h"
33 #include "args.h"
34 #if (defined(_WIN32) && !defined(__CYGWIN__)) || defined(__DJGPP__)
35 #include "path.h"
36 #endif
37 
38 #define ARGS_NOP	0
39 #define ARGS_ARGS	1
40 #define ARGS_FILELIST	2
41 #define ARGS_GFIND	3
42 #define ARGS_BOTH	4
43 
44 static int type;
45 static char *const *argslist;
46 static FILE *ip;
47 static GFIND *gp;
48 
49 /**
50  * args_open:
51  *
52  *	@param[in]	args	args array
53  */
54 void
args_open(char * const * argv)55 args_open(char *const *argv)
56 {
57 	type = ARGS_ARGS;
58 	argslist = argv;
59 }
60 /**
61  * args_open_filelist: args_open like interface for handling output of find(1).
62  *
63  *	@param[in]	filename	file including list of file names.
64  *				When "-" is specified, read from standard input.
65  */
66 void
args_open_filelist(const char * filename)67 args_open_filelist(const char *filename)
68 {
69 	type = ARGS_FILELIST;
70 	if (!strcmp(filename, "-")) {
71 		ip = stdin;
72 	} else {
73 		ip = fopen(filename, "r");
74 		if (ip == NULL)
75 			die("cannot open '%s'.", filename);
76 	}
77 }
78 /**
79  * args_open_both: args_open like interface for argument and file list.
80  *
81  *	@param[in]	args		args array
82  *	@param[in]	filename	file including list of file names.
83  *				When "-" is specified, read from standard input.
84  */
85 void
args_open_both(char * const * argv,const char * filename)86 args_open_both(char *const *argv, const char *filename)
87 {
88 	type = ARGS_BOTH;
89 	argslist = argv;
90 	if (!strcmp(filename, "-")) {
91 		ip = stdin;
92 	} else {
93 		ip = fopen(filename, "r");
94 		if (ip == NULL)
95 			die("cannot open '%s'.", filename);
96 	}
97 }
98 /**
99  * args_open_gfind: args_open like interface for handling output of gfind.
100  *
101  *	@param[in]	agp	GFIND descriptor
102  */
103 void
args_open_gfind(GFIND * agp)104 args_open_gfind(GFIND *agp)
105 {
106 	type = ARGS_GFIND;
107 	gp = agp;
108 }
109 void
args_open_nop(void)110 args_open_nop(void)
111 {
112 	type = ARGS_NOP;
113 }
114 /**
115  * args_read: read path From args.
116  *
117  *	@return		path (NULL: end of argument)
118  */
119 const char *
args_read(void)120 args_read(void)
121 {
122 	const char *p;
123 	STATIC_STRBUF(sb);
124 
125 	strbuf_clear(sb);
126 	switch (type) {
127 	case ARGS_NOP:
128 		p = NULL;
129 		break;
130 	case ARGS_ARGS:
131 		p = *argslist++;
132 		break;
133 	case ARGS_FILELIST:
134 		p = strbuf_fgets(sb, ip, STRBUF_NOCRLF);
135 		break;
136 	case ARGS_GFIND:
137 		p = gfind_read(gp);
138 		break;
139 	case ARGS_BOTH:
140 		if (*argslist != NULL)
141 			p = *argslist++;
142 		else
143 			p = strbuf_fgets(sb, ip, STRBUF_NOCRLF);
144 		break;
145 	default:
146 		die("args_read: invalid type.");
147 	}
148 	return p;
149 }
150 /**
151  * args_close: close args.
152  */
153 void
args_close(void)154 args_close(void)
155 {
156 	switch (type) {
157 	case ARGS_NOP:
158 	case ARGS_ARGS:
159 		break;
160 	case ARGS_FILELIST:
161 	case ARGS_BOTH:
162 		if (ip != NULL && ip != stdin)
163 			fclose(ip);
164 		ip = NULL;
165 		break;
166 	case ARGS_GFIND:
167 		if (gp != NULL)
168 			gfind_close(gp);
169 		gp = NULL;
170 		break;
171 	default:
172 		die("something wrong.");
173 	}
174 }
175 /**
176  * preparse_options
177  *
178  *	@param[in]	argc	main()'s argc integer
179  *	@param[in]	argv	main()'s argv string array
180  *
181  * Setup the "GTAGSCONF" and "GTAGSLABEL" environment variables
182  * according to the --gtagsconf and --gtagslabel options.
183  * Additionally changes directory acording to the --directory.
184  */
185 int
preparse_options(int argc,char * const * argv)186 preparse_options(int argc, char *const *argv)
187 {
188 	int optchar;
189 	int option_index = 0;
190 	char *confpath = NULL;
191 	char *label = NULL;
192 	char *dir = NULL;
193 	extern const char *short_options;
194 	extern struct option const long_options[];
195 
196 	/*
197 	 * restart scanning of the same argv by setting optind = 1.
198 	 */
199         optind = 1;
200 	while ((optchar = getopt_long(argc, argv, short_options, long_options, &option_index)) != EOF) {
201 		switch (optchar) {
202 		case 'C':
203 			dir = optarg;
204 			break;
205 		case OPT_GTAGSCONF:
206 			confpath = optarg;
207 			break;
208 		case OPT_GTAGSLABEL:
209 			label = optarg;
210 			break;
211 		case '?':
212 			return -1;
213 		default:
214 			break;
215 		}
216 	}
217 	/*
218 	 * Change the directory before doing all the work including parameter analysis.
219 	 */
220 	if (dir)
221 		if (chdir(dir) < 0)
222 			die("cannot change directory to '%s'.", dir);
223 	if (confpath) {
224 		char real[MAXPATHLEN];
225 
226 		if (!test("f", confpath))
227 			die("--gtagsconf file not found.");
228 		if (!realpath(confpath, real))
229 			die("cannot get absolute path of --gtagsconf file.");
230 		set_env("GTAGSCONF", real);
231 	}
232 	if (label)
233 		set_env("GTAGSLABEL", label);
234 	/*
235 	 * restart scanning of the same argv by setting optind = 1.
236 	 * This is needed for the calling of getopt() in main().
237 	 */
238         optind = 1;
239 	return 0;
240 }
241 /**
242  * prepend_options: creates a new argv main() array, by prepending (space separated)
243  *		options and arguments from the string argument options.
244  *
245  *	@param[in,out]	argc	pointer to main()'s argc integer
246  *	@param[in]	argv	main()'s argv string array
247  *	@param[in]	options	string
248  *	@return	The new argv array.
249  *
250  *	The program's name is copied back into: returned[0] (argv[0]).
251  */
252 char **
prepend_options(int * argc,char * const * argv,const char * options)253 prepend_options(int *argc, char *const *argv, const char *options)
254 {
255 	STRBUF *sb = strbuf_open(0);
256 	const char *p, *opt = check_strdup(options);
257 	int count = 1;
258 	int quote = 0;
259 	const char **newargv;
260 	int i = 0, j = 1;
261 
262 	for (p = opt; *p && isspace(*p); p++)
263 		;
264 	for (; *p; p++) {
265 		int c = *p;
266 
267 		if (quote) {
268 			if (quote == c)
269 				quote = 0;
270 			else
271 				strbuf_putc(sb, c);
272 		} else if (c == '\\') {
273 			if (*(p + 1))
274 				strbuf_putc(sb, *++p);
275 		} else if (c == '\'' || c == '"') {
276 			quote = c;
277 		} else if (isspace(c)) {
278 			strbuf_putc(sb, '\0');
279 			count++;
280 			while (*p && isspace(*p))
281 				p++;
282 			p--;
283 		} else {
284 			strbuf_putc(sb, *p);
285 		}
286 	}
287 	newargv = (const char **)check_malloc(sizeof(char *) * (*argc + count + 1));
288 	newargv[i++] = argv[0];
289 	p = strbuf_value(sb);
290 	while (count--) {
291 		newargv[i++] = p;
292 		p += strlen(p) + 1;
293 	}
294 	while (j < *argc)
295 		newargv[i++] = argv[j++];
296 	newargv[i] = NULL;
297 	*argc = i;
298 #ifdef DEBUG
299 	for (i = 0; i < *argc; i++)
300 		fprintf(stderr, "newargv[%d] = '%s'\n", i, newargv[i]);
301 #endif
302 	/* doesn't close string buffer. */
303 
304 	return (char **)newargv;
305 }
306 /**
307  * serialize_options
308  */
309 char *
serialize_options(int argc,char * const * argv)310 serialize_options(int argc, char *const *argv)
311 {
312 	STRBUF *sb = strbuf_open(0);
313 	char *string = NULL;
314 	char *p = NULL;
315 	int i;
316 	for (i = 0; i < argc; i++) {
317 		if (i > 0)
318 			strbuf_putc(sb, ' ');
319 		for (p = argv[i]; *p; p++) {
320 			/* quote spaces using url encoding */
321 			if (*p == ' ')
322 				strbuf_puts(sb, "%20");
323 			else
324 				strbuf_putc(sb, *p);
325 		}
326 	}
327 	string = check_strdup(strbuf_value(sb));
328 	strbuf_close(sb);
329 	return string;
330 }
331