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