1 #include "cfgfile.h"
2 #include <stdio.h>
3 #include <ctype.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <pwd.h>
7 #include <unistd.h>
8 #include "defs.h"
9 #include "n_libc.h"
10 
11 static char *
next_opt_str(char ** p0)12 next_opt_str(char **p0) {
13 	char	*p = *p0;
14 	char	*start;
15 
16 	while (isspace((unsigned char)*p)) {
17 		++p;
18 	}
19 	if (*p == 0) {
20 		return NULL;
21 	}
22 	start = p;
23 	do {
24 		++p;
25 	} while (*p != 0 && !isspace((unsigned char)*p));
26 	if (*p != 0) {
27 		*p = 0;
28 		*p0 = p+1;
29 	} else {
30 		*p0 = p;
31 	}
32 	return start;
33 }
34 
35 struct cfg_option {
36 	char	*name;
37 	char	**argv;
38 	int	argc;
39 	struct opt_info	*info;
40 	struct cfg_option *next;
41 };
42 
43 static struct cfg_option *
lookup_option(struct cfg_option * list,const char * name)44 lookup_option(struct cfg_option *list, const char *name) {
45 	for (; list != NULL; list = list->next) {
46 		if (list->name && strcmp(list->name, name) == 0) {
47 			return list;
48 		}
49 	}
50 	return NULL;
51 }
52 
53 #if 0
54 static void
55 print_option_list(struct cfg_option *opt) {
56 	int	i;
57 
58 	for (; opt != NULL; opt = opt->next) {
59 		if (opt->name == NULL) {
60 			continue;
61 		}
62 		printf("option %s:\n", opt->name);
63 		for (i = 0; i < opt->argc; ++i) {
64 			printf("\t%s\n", opt->argv[i]);
65 		}
66 	}
67 }
68 #endif
69 
70 static void
free_option(struct cfg_option * opt)71 free_option(struct cfg_option *opt) {
72 	free(opt->argv);
73 	free(opt->name);
74 	free(opt);
75 }
76 
77 static void
free_option_list(struct cfg_option * list)78 free_option_list(struct cfg_option *list) {
79 	while (list != NULL) {
80 		struct cfg_option	*tmp = list->next;
81 		int			i;
82 
83 		for (i = 0; i < list->argc; ++i) {
84 			free(list->argv[i]);
85 		}
86 
87 		free(list->argv);
88 		free(list->name);
89 		list = tmp;
90 	}
91 }
92 
93 #define OPT_OPTIONS	1
94 #define OPT_ARCH	2
95 #define OPT_ABI		3
96 #define OPT_ASM		4
97 #define OPT_GNUC	5
98 #define OPT_CPP		6
99 
100 static struct opt_info {
101 	char	*name;
102 	int	code;
103 	int	minargs;
104 	int	maxargs;
105 	int	mergable;
106 	char	*env_override;
107 } known_options[] = {
108 	{ "options", OPT_OPTIONS, 1, 0, 1, NULL },
109 	{ "arch", OPT_ARCH, 1, 1, 0, "NWCC_ARCH" },
110 	{ "abi", OPT_ABI, 1, 1, 0, "NWCC_ABI" },
111 	{ "asm", OPT_ASM, 1, 1, 0, "NWCC_ASM" },
112 	{ "gnuc", OPT_GNUC, 1, 1, 0, "NWCC_GNU_VERSION" },
113 	{ "cpp", OPT_CPP, 1, 1, 0, "NWCC_CPP" },
114 	{ NULL, 0, 0, 0, 0, NULL }
115 };
116 
117 static struct cfg_option *
do_read_config_file(const char * path,struct cfg_option * other)118 do_read_config_file(const char *path, struct cfg_option *other) {
119 	FILE				*fd;
120 	char				buf[1024];
121 	char				*p;
122 	char				*p2;
123 	char				*option;
124 	int				lineno = 0;
125 	int				i;
126 	struct cfg_option		*newcfg;
127 	struct cfg_option		*tmpcfg;
128 	struct cfg_option		*ret = NULL;
129 	struct cfg_option		*ret_tail = NULL;
130 	static struct cfg_option	nullcfg;
131 
132 	if ((fd = fopen(path, "r")) == NULL) {
133 		return NULL;
134 	}
135 
136 	while (fgets(buf, sizeof buf, fd) != NULL) {
137 		if ((p = strchr(buf, '\n')) != NULL) {
138 			*p = 0;
139 		} else {
140 			int	ch;
141 
142 			(void) fprintf(stderr,
143 				"%s:%d: Line too long - truncating\n",
144 				path, lineno);
145 			do {
146 				ch = fgetc(fd);
147 			} while (ch != '\n' && ch != EOF);
148 		}
149 		++lineno;
150 		p = buf;
151 		while (isspace((unsigned char)*p)) {
152 			++p;
153 		}
154 		if (*p == '#' || *p == 0) {
155 			/* Comment or empty line */
156 			continue;
157 		}
158 		option = p;
159 		while (isalpha((unsigned char)*p)) {
160 			++p;
161 		}
162 		if (!*p) {
163 			(void) fprintf(stderr, "%s:%d: Malformed "
164 				"line `%s'\n",
165 				path, lineno, buf);
166 			continue;
167 		}
168 		if (strncmp(option, "undef", 5) == 0
169 			&& !isalpha((unsigned char)option[5])) {
170 			/* Undefining existing option */
171 			*p++ = 0;
172 			if ((p2 = next_opt_str(&p)) != NULL) {
173 				if ((newcfg = lookup_option(other, p2)) ||
174 					(newcfg = lookup_option(ret, p2))) {
175 					free(newcfg->name);
176 					newcfg->name = NULL;
177 				}
178 			}
179 		} else {
180 			char	*startp = p;
181 			int	exists = 0;
182 
183 			/* Defining new option */
184 			newcfg = n_xmalloc(sizeof *newcfg);
185 			*newcfg = nullcfg;
186 
187 			while (isspace((unsigned char)*p)) {
188 				++p;
189 			}
190 			if (*p++ != '=') {
191 				/* Incomplete or malformed line - ignore */
192 				(void) fprintf(stderr, "%s:%d: Malformed "
193 					"line `%s'\n",
194 					path, lineno, buf);
195 				continue;
196 			}
197 			*startp = 0;
198 			if (other == NULL ||
199 				((tmpcfg=lookup_option(other,option)) == NULL
200 				&& (tmpcfg = lookup_option(ret,option))
201 				==NULL)) {
202 				/*
203 				 * Doesn't already exist
204 				 */
205 				newcfg->name = n_xstrdup(option);
206 			} else {
207 				/*
208 				 * We're overriding an existing definition
209 				 * from a previous configuration file -
210 				 * Are we dealing with an option that can
211 				 * be merged, or one that only allows a
212 				 * single definition?
213 				 */
214 				newcfg->name = tmpcfg->name;
215 				tmpcfg->name = NULL;
216 				if (tmpcfg->info->mergable) {
217 					exists = 1;
218 					newcfg->argv = tmpcfg->argv;
219 					newcfg->argc = tmpcfg->argc;
220 					tmpcfg->argv = NULL;
221 				}
222 			}
223 
224 			while ((p2 = next_opt_str(&p)) != NULL) {
225 				int	save_opt = 1;
226 
227 				if (exists) {
228 					for (i = 0; i < newcfg->argc; ++i) {
229 						if (strcmp(newcfg->argv[i],
230 							p2) == 0) {
231 							/* Already in list */
232 							save_opt = 0;
233 							break;
234 						}
235 					}
236 				}
237 				if (save_opt) {
238 					++newcfg->argc;
239 					newcfg->argv = n_xrealloc(newcfg->argv,
240 						newcfg->argc *
241 						sizeof *newcfg->argv);
242 					newcfg->argv[newcfg->argc - 1] =
243 						n_xstrdup(p2);
244 				}
245 			}
246 
247 			/*
248 			 * Now establish the option's identity and save it
249 			 * in the list
250 			 */
251 			for (i = 0; known_options[i].name != NULL; ++i) {
252 				if (strcmp(newcfg->name,
253 					known_options[i].name) == 0) {
254 					break;
255 				}
256 			}
257 			if (known_options[i].name == NULL) {
258 				/* Unknown option */
259 				(void) fprintf(stderr, "%s:%d: Unknown "
260 					"option `%s'\n",
261 					path, lineno, newcfg->name);
262 				free_option(newcfg);
263 				continue;
264 			}
265 			newcfg->info = &known_options[i];
266 			if (newcfg->info->maxargs != 0
267 				&& newcfg->argc > newcfg->info->maxargs) {
268 				(void) fprintf(stderr, "%s:%d: Too many "
269 					"arguments for `%s'\n",
270 					path, lineno, newcfg->name);
271 				free_option(newcfg);
272 				continue;
273 			} else if (newcfg->info->minargs != 0
274 				&& newcfg->argc < newcfg->info->minargs) {
275 				(void) fprintf(stderr, "%s:%d: Not "
276 					"enough arguments for `%s'\n",
277 					path, lineno, newcfg->name);
278 				free_option(newcfg);
279 				continue;
280 			}
281 
282 			if (ret == NULL) {
283 				ret = ret_tail = newcfg;
284 			} else {
285 				ret_tail->next = newcfg;
286 				ret_tail = newcfg;
287 			}
288 		}
289 	}
290 	(void) fclose(fd);
291 	if (ret != NULL && other) {
292 		free_option_list(other);
293 	}
294 
295 	return ret;
296 }
297 
298 
299 char **
make_new_argv(char ** old_argv,int * old_argc,struct cfg_option * opt,struct ga_option * options,int nopts)300 make_new_argv(char **old_argv, int *old_argc, struct cfg_option *opt,
301 	struct ga_option *options, int nopts) {
302 
303 	struct cfg_option	*tmpopt;
304 	char			*buf;
305 	char			*p;
306 	char			**ret;
307 	int			i;
308 	int			cmd_line_opts;
309 	int			ch;
310 	int			idx;
311 	int			total_config_argc;
312 	int			*opt_idx;
313 
314 	/*
315 	 * First we convert all options to their command line
316 	 * equivalents, such that e.g.
317 	 *
318 	 * target = x86
319 	 *
320 	 * becomes
321 	 *
322 	 * -arch=x86
323 	 */
324 	for (tmpopt = opt; tmpopt != NULL; tmpopt = tmpopt->next) {
325 		if (tmpopt->name == NULL) {
326 			continue;
327 		}
328 
329 		/*
330 		 * 03/25/08: Allow environment variables to override
331 		 * the config file settings!
332 		 */
333 		if (tmpopt->info->env_override != NULL) {
334 			/*
335 			 * This is currently only done for options which take
336 			 * a single operand, i.e. argv[0]. It would be nice
337 			 * to introduce NWCC_OPTIONS, which is also merged
338 			 * with all options. But not now!!!!
339 			 */
340 			char	*p = getenv(tmpopt->info->env_override);
341 
342 			if (p != NULL) {
343 				free(tmpopt->argv[0]);
344 				tmpopt->argv[0] = n_xstrdup(p);
345 			}
346 		}
347 
348 		switch (tmpopt->info->code) {
349 		case OPT_OPTIONS:
350 			/*
351 			 * Interpret all arguments verbatim, unless they
352 			 * contain a dot, in which case they are assumed
353 			 * to be file arguments, such as foo.c or bar.o
354 			 */
355 			for (i = 0; i < tmpopt->argc; ++i) {
356 				if ((p = strchr(tmpopt->argv[i], '.')) != NULL) {
357 					;
358 				} else {
359 					buf = n_xmalloc(strlen(tmpopt->argv[i])
360 						+ 2);
361 					sprintf(buf, "-%s", tmpopt->argv[i]);
362 					free(tmpopt->argv[i]);
363 					tmpopt->argv[i] = buf;
364 				}
365 			}
366 			break;
367 		case OPT_ARCH:
368 			buf = n_xmalloc(strlen(tmpopt->argv[0])
369 				+ sizeof "-arch=");
370 			sprintf(buf, "-arch=%s", tmpopt->argv[0]);
371 			free(tmpopt->argv[0]);
372 			tmpopt->argv[0] = buf;
373 			break;
374 		case OPT_ABI:
375 			buf = n_xmalloc(strlen(tmpopt->argv[0])
376 				+ sizeof "-abi=");
377 			sprintf(buf, "-abi=%s", tmpopt->argv[0]);
378 			free(tmpopt->argv[0]);
379 			tmpopt->argv[0] = buf;
380 			break;
381 		case OPT_ASM:
382 			buf = n_xmalloc(strlen(tmpopt->argv[0])
383 				+ sizeof "-asm=");
384 			sprintf(buf, "-asm=%s", tmpopt->argv[0]);
385 			free(tmpopt->argv[0]);
386 			tmpopt->argv[0] = buf;
387 			break;
388 		case OPT_GNUC:
389 			buf = n_xmalloc(strlen(tmpopt->argv[0])
390 				+ sizeof "-gnuc=");
391 			sprintf(buf, "-gnuc=%s", tmpopt->argv[0]);
392 			free(tmpopt->argv[0]);
393 			tmpopt->argv[0] = buf;
394 			break;
395 		case OPT_CPP:
396 			buf = n_xmalloc(strlen(tmpopt->argv[0])
397 				+ sizeof "-cpp=");
398 			sprintf(buf, "-cpp=%s", tmpopt->argv[0]);
399 			free(tmpopt->argv[0]);
400 			tmpopt->argv[0] = buf;
401 			break;
402 		default:
403 			abort();
404 		}
405 	}
406 
407 	/*
408 	 * Now we filter out duplicated options; The policy is to let
409 	 * options passed on the command line win over those defined
410 	 * in config files. Thus we first record the option table
411 	 * indices for all command line arguments, and then remove
412 	 * any config file options with matching indices
413 	 */
414 	opt_idx = n_xmalloc(*old_argc * sizeof *opt_idx);
415 	i = 0;
416 	while ((ch = nw_get_arg(*old_argc, old_argv, options, nopts, &idx))
417 		!= -1) {
418 		switch (ch) {
419 		case '!':
420 			continue;
421 		case '?':
422 			if (!idx) { /* XXX -1 !!! */
423 				continue;
424 			}
425 		}
426 
427 		if (options[idx].ch == 'l') {
428 			/*
429 			 * 03/03/09: XXX Library options are not handled
430 			 * properly because every -l is treated the same,
431 			 * so -lm, -lpthread, etc are considered even
432 			 */
433 			continue;
434 		}
435 
436 		{
437 			int	j;
438 			/*
439 			 * 03/03/09: Filter duplicated actual command
440 			 * line options like -foo -foo. Otherwise the
441 			 * code below, which seems to assume they occur
442 			 * only once, will break
443 			 * XXX Fix all of this stuff properly
444 			 */
445 			for (j = 0; j < i; ++j) {
446 				if (opt_idx[j] == idx) {
447 					break;
448 				}
449 			}
450 			if (j != i) {
451 				continue;
452 			}
453 		}
454 		opt_idx[i++] = idx;
455 	}
456 	cmd_line_opts = i;
457 
458 	total_config_argc = 0;
459 	for (tmpopt = opt; tmpopt != NULL; tmpopt = tmpopt->next) {
460 		if (tmpopt->name == NULL) {
461 			continue;
462 		}
463 
464 		n_optind = -1;
465 
466 		while ((ch = nw_get_arg(tmpopt->argc, tmpopt->argv,
467 			options, nopts, &idx)) != -1) {
468 			switch (ch) {
469 			case '!':
470 				continue;
471 			case '?':
472 				if (!idx) { /* XXX -1 */
473 					continue;
474 				}
475 			}
476 			for (i = 0; i < cmd_line_opts; ++i) {
477 				int	remaining;
478 
479 				if (opt_idx[i] != idx) {
480 					continue;
481 				}
482 
483 				/*
484 				 * Option is duplicated - remove config
485 				 * file version
486 				 */
487 				remaining = tmpopt->argc -
488 					(n_optind + 1);
489 				free(tmpopt->argv[n_optind]);
490 				/*
491 				 * 03/03/09: remaining can become -1?!?!?!?
492 				 *
493 				 * With
494 				 *
495 				 *    ./nwcc y.c -lm
496 				 *
497 				 * ... and
498 				 *
499 				 *    options = lm
500 				 *
501 				 * we first get a double free and then
502 				 * remaining = -1. XXX What's going on?
503 				 */
504 				if (remaining > 0) {
505 					memmove(tmpopt->argv+n_optind,
506 						tmpopt->argv+n_optind+1,
507 							remaining *
508 							sizeof(char *));
509 				} else {
510 					/*
511 					 * 03/03/09: If remaining = 0,
512 					 * there can be a double
513 					 * free() here
514 					 * XXX why?
515 					 */
516 					tmpopt->argv[n_optind] = NULL;
517 				}
518 
519 				if (tmpopt->argc == 0) {
520 					(void) fprintf(stderr, "Warning: "
521 						"Config file settings "
522 						"messed up\n");
523 				} else {
524 					--tmpopt->argc;
525 				}
526 			}
527 		}
528 		total_config_argc += tmpopt->argc;
529 	}
530 	free(opt_idx);
531 
532 	/*
533 	 * Now build an argv with a total of *old_argc + total_config_argc
534 	 * strings! (plus terminating null pointer)
535 	 */
536 	ret = n_xmalloc((*old_argc + total_config_argc + 1) * sizeof(char *));
537 
538 	memcpy(ret, old_argv, *old_argc * sizeof(char *));
539 
540 	i = 0;
541 	for (tmpopt = opt; tmpopt != NULL; tmpopt = tmpopt->next) {
542 		int	j;
543 
544 		if (tmpopt->name == NULL) {
545 			continue;
546 		}
547 
548 		for (j = 0; j < tmpopt->argc; ++j) {
549 			ret[*old_argc + i++] = tmpopt->argv[j];
550 		}
551 		tmpopt->argv = NULL;
552 		tmpopt->argc = 0;
553 	}
554 	ret[*old_argc + i] = NULL;
555 	*old_argc += total_config_argc;
556 
557 	free_option_list(opt);
558 
559 #if 0
560 	printf("new argc = %d\n", *old_argc);
561 	printf("new argv:\n");
562 	for (i = 0; i < *old_argc; ++i) {
563 		printf("  %s\n", ret[i]);
564 	}
565 	printf("   argv[*old_argc] = %p\n", ret[ *old_argc ]);
566 
567 	print_option_list(opt);
568 #endif
569 
570 	n_optind = -1; /* Reset nw_get_arg() */
571 	return ret;
572 }
573 
574 struct cfg_option *
read_config_files(void)575 read_config_files(void) {
576 	char			buf[1024];
577 	struct cfg_option	*optlist;
578 	struct cfg_option	*tmp;
579 	struct passwd		*pw;
580 
581 	optlist = do_read_config_file(INSTALLDIR "/nwcc/nwcc.conf", NULL);
582 	if ((pw = getpwuid(getuid())) != NULL
583 		&& strlen(pw->pw_dir) < (sizeof buf - 32)) {
584 		sprintf(buf, "%s/.nwcc/nwcc.conf", pw->pw_dir);
585 		tmp = do_read_config_file(buf, optlist);
586 		if (tmp != NULL) {
587 			optlist = tmp;
588 		}
589 	}
590 	return optlist;
591 }
592 
593 void
merge_argv_with_cfgfile(int * argc,char *** argv,struct ga_option * options,int nopts)594 merge_argv_with_cfgfile(int *argc, char ***argv, struct ga_option *options, int nopts) {
595 	struct cfg_option	*cfg_file_options;
596 
597 	if ((cfg_file_options = read_config_files()) != NULL) {
598 		/*
599 		 * There are config files that need to be integrated into
600 		 * the command line
601 		 */
602 		char	**tmpargv;
603 		int	tmpargc = *argc;
604 
605 		tmpargv = make_new_argv(*argv, &tmpargc, cfg_file_options,
606 				options, nopts);
607 		if (tmpargv != NULL) {
608 			*argv = tmpargv;
609 			*argc = tmpargc;
610 		}
611 	}
612 }
613 
614