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