1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2015 Garrett D'Amore <garrett@damore.org> 14 */ 15 16 /* 17 * This program tests symbol visibility using the /usr/bin/c89 and 18 * /usr/bin/c99 programs. 19 */ 20 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <errno.h> 25 #include <err.h> 26 #include <unistd.h> 27 #include <sys/types.h> 28 #include <sys/stat.h> 29 #include <note.h> 30 #include <sys/wait.h> 31 #include "test_common.h" 32 33 char *dname; 34 char *cfile; 35 char *ofile; 36 char *lfile; 37 char *efile; 38 39 const char *sym = NULL; 40 41 static int good_count = 0; 42 static int fail_count = 0; 43 static int full_count = 0; 44 static int extra_debug = 0; 45 static char *compilation = "compilation.cfg"; 46 47 #if defined(_LP64) 48 #define MFLAG "-m64" 49 #elif defined(_ILP32) 50 #define MFLAG "-m32" 51 #endif 52 53 const char *compilers[] = { 54 "cc", 55 "gcc", 56 "/opt/SUNWspro/bin/cc", 57 "/opt/gcc/4.4.4/bin/gcc", 58 "/opt/sunstudio12.1/bin/cc", 59 "/opt/sfw/bin/gcc", 60 "/usr/local/bin/gcc", 61 NULL 62 }; 63 64 char *compiler = NULL; 65 const char *c89flags = NULL; 66 const char *c99flags = NULL; 67 68 #define MAXENV 64 /* maximum number of environments (bitmask width) */ 69 #define MAXHDR 10 /* maximum # headers to require to access symbol */ 70 #define MAXARG 20 /* maximum # of arguments */ 71 72 #define WS " \t" 73 74 static int next_env = 0; 75 76 struct compile_env { 77 char *name; 78 char *lang; 79 char *defs; 80 int index; 81 }; 82 83 static struct compile_env compile_env[MAXENV]; 84 85 struct env_group { 86 char *name; 87 uint64_t mask; 88 struct env_group *next; 89 }; 90 91 typedef enum { SYM_TYPE, SYM_VALUE, SYM_FUNC } sym_type_t; 92 93 struct sym_test { 94 char *name; 95 sym_type_t type; 96 char *hdrs[MAXHDR]; 97 char *rtype; 98 char *atypes[MAXARG]; 99 uint64_t test_mask; 100 uint64_t need_mask; 101 char *prog; 102 struct sym_test *next; 103 }; 104 105 struct env_group *env_groups = NULL; 106 107 struct sym_test *sym_tests = NULL; 108 struct sym_test **sym_insert = &sym_tests; 109 110 static char * 111 mystrdup(const char *s) 112 { 113 char *r; 114 if ((r = strdup(s)) == NULL) { 115 perror("strdup"); 116 exit(1); 117 } 118 return (r); 119 } 120 121 static void * 122 myzalloc(size_t sz) 123 { 124 void *buf; 125 if ((buf = calloc(1, sz)) == NULL) { 126 perror("calloc"); 127 exit(1); 128 } 129 return (buf); 130 } 131 132 static void 133 myasprintf(char **buf, const char *fmt, ...) 134 { 135 int rv; 136 va_list va; 137 va_start(va, fmt); 138 rv = vasprintf(buf, fmt, va); 139 va_end(va); 140 if (rv < 0) { 141 perror("vasprintf"); 142 exit(1); 143 } 144 } 145 146 static void 147 append_sym_test(struct sym_test *st) 148 { 149 *sym_insert = st; 150 sym_insert = &st->next; 151 } 152 153 static int 154 find_env_mask(const char *name, uint64_t *mask) 155 { 156 for (int i = 0; i < 64; i++) { 157 if (compile_env[i].name != NULL && 158 strcmp(compile_env[i].name, name) == 0) { 159 *mask |= (1ULL << i); 160 return (0); 161 } 162 } 163 164 for (struct env_group *eg = env_groups; eg != NULL; eg = eg->next) { 165 if (strcmp(name, eg->name) == 0) { 166 *mask |= eg->mask; 167 return (0); 168 } 169 } 170 return (-1); 171 } 172 173 174 static int 175 expand_env(char *list, uint64_t *mask, char **erritem) 176 { 177 char *item; 178 for (item = strtok(list, WS); item != NULL; item = strtok(NULL, WS)) { 179 if (find_env_mask(item, mask) < 0) { 180 if (erritem != NULL) { 181 *erritem = item; 182 } 183 return (-1); 184 } 185 } 186 return (0); 187 } 188 189 static int 190 expand_env_list(char *list, uint64_t *test, uint64_t *need, char **erritem) 191 { 192 uint64_t mask = 0; 193 int act; 194 char *item; 195 for (item = strtok(list, WS); item != NULL; item = strtok(NULL, WS)) { 196 switch (item[0]) { 197 case '+': 198 act = 1; 199 item++; 200 break; 201 case '-': 202 act = 0; 203 item++; 204 break; 205 default: 206 act = 1; 207 break; 208 } 209 210 mask = 0; 211 if (find_env_mask(item, &mask) < 0) { 212 if (erritem != NULL) { 213 *erritem = item; 214 } 215 return (-1); 216 } 217 *test |= mask; 218 if (act) { 219 *need |= mask; 220 } else { 221 *need &= ~(mask); 222 } 223 } 224 return (0); 225 } 226 227 static int 228 do_env(char **fields, int nfields, char **err) 229 { 230 char *name; 231 char *lang; 232 char *defs; 233 234 if (nfields != 3) { 235 myasprintf(err, "number of fields (%d) != 3", nfields); 236 return (-1); 237 } 238 239 if (next_env >= MAXENV) { 240 myasprintf(err, "too many environments"); 241 return (-1); 242 } 243 244 name = fields[0]; 245 lang = fields[1]; 246 defs = fields[2]; 247 248 compile_env[next_env].name = mystrdup(name); 249 compile_env[next_env].lang = mystrdup(lang); 250 compile_env[next_env].defs = mystrdup(defs); 251 compile_env[next_env].index = next_env; 252 next_env++; 253 return (0); 254 } 255 256 static int 257 do_env_group(char **fields, int nfields, char **err) 258 { 259 char *name; 260 char *list; 261 struct env_group *eg; 262 uint64_t mask; 263 char *item; 264 265 if (nfields != 2) { 266 myasprintf(err, "number of fields (%d) != 2", nfields); 267 return (-1); 268 } 269 270 name = fields[0]; 271 list = fields[1]; 272 mask = 0; 273 274 if (expand_env(list, &mask, &item) < 0) { 275 myasprintf(err, "reference to undefined env %s", item); 276 return (-1); 277 } 278 279 eg = myzalloc(sizeof (*eg)); 280 eg->name = mystrdup(name); 281 eg->mask = mask; 282 eg->next = env_groups; 283 env_groups = eg; 284 return (0); 285 } 286 287 static char *progbuf = NULL; 288 size_t proglen = 0; 289 size_t progsiz = 0; 290 291 static void 292 addprogch(char c) 293 { 294 while (progsiz <= (proglen + 1)) { 295 progbuf = realloc(progbuf, progsiz + 4096); 296 if (progbuf == NULL) { 297 perror("realloc"); 298 exit(1); 299 } 300 progsiz += 1024; 301 } 302 progbuf[proglen++] = c; 303 progbuf[proglen] = 0; 304 } 305 306 static void 307 addprogstr(char *s) 308 { 309 while (*s != NULL) { 310 addprogch(*s); 311 s++; 312 } 313 } 314 315 static void 316 addprogfmt(const char *fmt, ...) 317 { 318 va_list va; 319 char *buf = NULL; 320 va_start(va, fmt); 321 if (vasprintf(&buf, fmt, va) < 0) { 322 perror("vasprintf"); 323 exit(1); 324 } 325 va_end(va); 326 addprogstr(buf); 327 free(buf); 328 } 329 330 static void 331 mkprog(struct sym_test *st) 332 { 333 char *s; 334 335 proglen = 0; 336 337 for (int i = 0; i < MAXHDR && st->hdrs[i] != NULL; i++) { 338 addprogfmt("#include <%s>\n", st->hdrs[i]); 339 } 340 341 for (s = st->rtype; *s; s++) { 342 addprogch(*s); 343 if (*s == '(') { 344 s++; 345 addprogch(*s); 346 s++; 347 break; 348 } 349 } 350 addprogch(' '); 351 352 /* for function pointers, s is closing suffix, otherwise empty */ 353 354 switch (st->type) { 355 case SYM_TYPE: 356 addprogstr("test_type;"); 357 break; 358 359 case SYM_VALUE: 360 addprogfmt("test_value%s;\n", s); /* s usually empty */ 361 addprogstr("void\ntest_func(void)\n{\n"); 362 addprogfmt("\ttest_value = %s;\n}", st->name); 363 break; 364 365 case SYM_FUNC: 366 addprogstr("\ntest_func("); 367 for (int i = 0; st->atypes[i] != NULL && i < MAXARG; i++) { 368 int didname = 0; 369 if (i > 0) { 370 addprogstr(", "); 371 } 372 if (strcmp(st->atypes[i], "void") == 0) { 373 didname = 1; 374 } 375 if (strcmp(st->atypes[i], "") == 0) { 376 didname = 1; 377 addprogstr("void"); 378 } 379 380 /* print the argument list */ 381 for (char *a = st->atypes[i]; *a; a++) { 382 if (*a == '(' && a[1] == '*' && !didname) { 383 addprogfmt("(*a%d", i); 384 didname = 1; 385 a++; 386 } else if (*a == '[' && !didname) { 387 addprogfmt("a%d[", i); 388 didname = 1; 389 } else { 390 addprogch(*a); 391 } 392 } 393 if (!didname) { 394 addprogfmt(" a%d", i); 395 } 396 } 397 398 if (st->atypes[0] == NULL) { 399 addprogstr("void"); 400 } 401 402 /* close argument list, and closing ")" for func ptrs */ 403 addprogfmt(")%s\n{\n\t", s); /* NB: s is normally empty */ 404 405 if (strcmp(st->rtype, "") != 0 && 406 strcmp(st->rtype, "void") != 0) { 407 addprogstr("return "); 408 } 409 410 /* add the function call */ 411 addprogfmt("%s(", st->name); 412 for (int i = 0; st->atypes[i] != NULL && i < MAXARG; i++) { 413 if (strcmp(st->atypes[i], "") != 0 && 414 strcmp(st->atypes[i], "void") != 0) { 415 addprogfmt("%sa%d", i > 0 ? ", " : "", i); 416 } 417 } 418 419 addprogstr(");\n}"); 420 break; 421 } 422 423 addprogch('\n'); 424 425 st->prog = progbuf; 426 } 427 428 static int 429 add_envs(struct sym_test *st, char *envs, char **err) 430 { 431 char *item; 432 if (expand_env_list(envs, &st->test_mask, &st->need_mask, &item) < 0) { 433 myasprintf(err, "bad env action %s", item); 434 return (-1); 435 } 436 return (0); 437 } 438 439 static int 440 add_headers(struct sym_test *st, char *hdrs, char **err) 441 { 442 int i = 0; 443 444 for (char *h = strsep(&hdrs, ";"); h != NULL; h = strsep(&hdrs, ";")) { 445 if (i >= MAXHDR) { 446 myasprintf(err, "too many headers"); 447 return (-1); 448 } 449 test_trim(&h); 450 st->hdrs[i++] = mystrdup(h); 451 } 452 453 return (0); 454 } 455 456 static int 457 add_arg_types(struct sym_test *st, char *atype, char **err) 458 { 459 int i = 0; 460 char *a; 461 for (a = strsep(&atype, ";"); a != NULL; a = strsep(&atype, ";")) { 462 if (i >= MAXARG) { 463 myasprintf(err, "too many arguments"); 464 return (-1); 465 } 466 test_trim(&a); 467 st->atypes[i++] = mystrdup(a); 468 } 469 470 return (0); 471 } 472 473 static int 474 do_type(char **fields, int nfields, char **err) 475 { 476 char *decl; 477 char *hdrs; 478 char *envs; 479 struct sym_test *st; 480 481 if (nfields != 3) { 482 myasprintf(err, "number of fields (%d) != 3", nfields); 483 return (-1); 484 } 485 decl = fields[0]; 486 hdrs = fields[1]; 487 envs = fields[2]; 488 489 st = myzalloc(sizeof (*st)); 490 st->type = SYM_TYPE; 491 st->name = mystrdup(decl); 492 st->rtype = mystrdup(decl); 493 494 if ((add_envs(st, envs, err) < 0) || 495 (add_headers(st, hdrs, err) < 0)) { 496 return (-1); 497 } 498 append_sym_test(st); 499 500 return (0); 501 } 502 503 static int 504 do_value(char **fields, int nfields, char **err) 505 { 506 char *name; 507 char *type; 508 char *hdrs; 509 char *envs; 510 struct sym_test *st; 511 512 if (nfields != 4) { 513 myasprintf(err, "number of fields (%d) != 4", nfields); 514 return (-1); 515 } 516 name = fields[0]; 517 type = fields[1]; 518 hdrs = fields[2]; 519 envs = fields[3]; 520 521 st = myzalloc(sizeof (*st)); 522 st->type = SYM_VALUE; 523 st->name = mystrdup(name); 524 st->rtype = mystrdup(type); 525 526 if ((add_envs(st, envs, err) < 0) || 527 (add_headers(st, hdrs, err) < 0)) { 528 return (-1); 529 } 530 append_sym_test(st); 531 532 return (0); 533 } 534 535 static int 536 do_func(char **fields, int nfields, char **err) 537 { 538 char *name; 539 char *rtype; 540 char *atype; 541 char *hdrs; 542 char *envs; 543 struct sym_test *st; 544 545 if (nfields != 5) { 546 myasprintf(err, "number of fields (%d) != 5", nfields); 547 return (-1); 548 } 549 name = fields[0]; 550 rtype = fields[1]; 551 atype = fields[2]; 552 hdrs = fields[3]; 553 envs = fields[4]; 554 555 st = myzalloc(sizeof (*st)); 556 st->type = SYM_FUNC; 557 st->name = mystrdup(name); 558 st->rtype = mystrdup(rtype); 559 560 if ((add_envs(st, envs, err) < 0) || 561 (add_headers(st, hdrs, err) < 0) || 562 (add_arg_types(st, atype, err) < 0)) { 563 return (-1); 564 } 565 append_sym_test(st); 566 567 return (0); 568 } 569 570 struct sym_test * 571 next_sym_test(struct sym_test *st) 572 { 573 return (st == NULL ? sym_tests : st->next); 574 } 575 576 const char * 577 sym_test_prog(struct sym_test *st) 578 { 579 if (st->prog == NULL) { 580 mkprog(st); 581 } 582 return (st->prog); 583 } 584 585 const char * 586 sym_test_name(struct sym_test *st) 587 { 588 return (st->name); 589 } 590 591 /* 592 * Iterate through tests. Pass in NULL for cenv to begin the iteration. For 593 * subsequent iterations, use the return value from the previous iteration. 594 * Returns NULL when there are no more environments. 595 */ 596 struct compile_env * 597 sym_test_env(struct sym_test *st, struct compile_env *cenv, int *need) 598 { 599 int i = cenv ? cenv->index + 1: 0; 600 uint64_t b = 1ULL << i; 601 602 while ((i < MAXENV) && (b != 0)) { 603 cenv = &compile_env[i]; 604 if (b & st->test_mask) { 605 *need = (st->need_mask & b) ? 1 : 0; 606 return (cenv); 607 } 608 b <<= 1; 609 i++; 610 } 611 return (NULL); 612 } 613 614 const char * 615 env_name(struct compile_env *cenv) 616 { 617 return (cenv->name); 618 } 619 620 const char * 621 env_lang(struct compile_env *cenv) 622 { 623 return (cenv->lang); 624 } 625 626 const char * 627 env_defs(struct compile_env *cenv) 628 { 629 return (cenv->defs); 630 } 631 632 static void 633 show_file(test_t t, const char *path) 634 { 635 FILE *f; 636 char *buf = NULL; 637 size_t cap = 0; 638 int line = 1; 639 640 f = fopen(path, "r"); 641 if (f == NULL) { 642 test_debugf(t, "fopen(%s): %s", path, strerror(errno)); 643 return; 644 } 645 646 test_debugf(t, "----->> begin (%s) <<------", path); 647 while (getline(&buf, &cap, f) >= 0) { 648 (void) strtok(buf, "\r\n"); 649 test_debugf(t, "%d: %s", line, buf); 650 line++; 651 } 652 test_debugf(t, "----->> end (%s) <<------", path); 653 (void) fclose(f); 654 } 655 656 static void 657 cleanup(void) 658 { 659 if (ofile != NULL) { 660 (void) unlink(ofile); 661 free(ofile); 662 ofile = NULL; 663 } 664 if (lfile != NULL) { 665 (void) unlink(lfile); 666 free(lfile); 667 lfile = NULL; 668 } 669 if (cfile != NULL) { 670 (void) unlink(cfile); 671 free(cfile); 672 cfile = NULL; 673 } 674 if (efile != NULL) { 675 (void) unlink(efile); 676 free(efile); 677 efile = NULL; 678 } 679 if (dname) { 680 (void) rmdir(dname); 681 free(dname); 682 dname = NULL; 683 } 684 } 685 686 static int 687 mkworkdir(void) 688 { 689 char b[32]; 690 char *d; 691 692 cleanup(); 693 694 (void) strlcpy(b, "/tmp/symbols_testXXXXXX", sizeof (b)); 695 if ((d = mkdtemp(b)) == NULL) { 696 perror("mkdtemp"); 697 return (-1); 698 } 699 dname = mystrdup(d); 700 myasprintf(&cfile, "%s/compile_test.c", d); 701 myasprintf(&ofile, "%s/compile_test.o", d); 702 myasprintf(&lfile, "%s/compile_test.log", d); 703 myasprintf(&efile, "%s/compile_test.exe", d); 704 return (0); 705 } 706 707 void 708 find_compiler(void) 709 { 710 test_t t; 711 int i; 712 FILE *cf; 713 714 t = test_start("finding compiler"); 715 716 if ((cf = fopen(cfile, "w+")) == NULL) { 717 test_failed(t, "Unable to open %s for write: %s", cfile, 718 strerror(errno)); 719 return; 720 } 721 (void) fprintf(cf, "#include <stdio.h>\n"); 722 (void) fprintf(cf, "int main(int argc, char **argv) {\n"); 723 (void) fprintf(cf, "#if defined(__SUNPRO_C)\n"); 724 (void) fprintf(cf, "exit(51);\n"); 725 (void) fprintf(cf, "#elif defined(__GNUC__)\n"); 726 (void) fprintf(cf, "exit(52);\n"); 727 (void) fprintf(cf, "#else\n"); 728 (void) fprintf(cf, "exit(99)\n"); 729 (void) fprintf(cf, "#endif\n}\n"); 730 (void) fclose(cf); 731 732 for (i = 0; compilers[i] != NULL; i++) { 733 char cmd[256]; 734 int rv; 735 736 (void) snprintf(cmd, sizeof (cmd), 737 "%s %s %s -o %s >/dev/null 2>&1", 738 compilers[i], MFLAG, cfile, efile); 739 test_debugf(t, "trying %s", cmd); 740 rv = system(cmd); 741 742 test_debugf(t, "result: %d", rv); 743 744 if ((rv < 0) || !WIFEXITED(rv) || WEXITSTATUS(rv) != 0) 745 continue; 746 747 rv = system(efile); 748 if (rv >= 0 && WIFEXITED(rv)) { 749 rv = WEXITSTATUS(rv); 750 } else { 751 rv = -1; 752 } 753 754 switch (rv) { 755 case 51: /* STUDIO */ 756 test_debugf(t, "Found Studio C"); 757 c89flags = "-Xc -errwarn=%all -v -xc99=%none " MFLAG; 758 c99flags = "-Xc -errwarn=%all -v -xc99=%all " MFLAG; 759 if (extra_debug) { 760 test_debugf(t, "c89flags: %s", c89flags); 761 test_debugf(t, "c99flags: %s", c99flags); 762 } 763 test_passed(t); 764 break; 765 case 52: /* GCC */ 766 test_debugf(t, "Found GNU C"); 767 c89flags = "-Wall -Werror -std=c89 " MFLAG; 768 c99flags = "-Wall -Werror -std=c99 " MFLAG; 769 if (extra_debug) { 770 test_debugf(t, "c89flags: %s", c89flags); 771 test_debugf(t, "c99flags: %s", c99flags); 772 } 773 test_passed(t); 774 break; 775 default: 776 continue; 777 } 778 myasprintf(&compiler, "%s", compilers[i]); 779 test_debugf(t, "compiler: %s", compiler); 780 return; 781 } 782 test_failed(t, "No compiler found."); 783 } 784 785 int 786 do_compile(test_t t, struct sym_test *st, struct compile_env *cenv, int need) 787 { 788 char *cmd; 789 FILE *logf; 790 FILE *dotc; 791 const char *prog; 792 793 full_count++; 794 795 if ((dotc = fopen(cfile, "w+")) == NULL) { 796 test_failed(t, "fopen(%s): %s", cfile, strerror(errno)); 797 return (-1); 798 } 799 prog = sym_test_prog(st); 800 if (fwrite(prog, 1, strlen(prog), dotc) < strlen(prog)) { 801 test_failed(t, "fwrite: %s", strerror(errno)); 802 (void) fclose(dotc); 803 return (-1); 804 } 805 if (fclose(dotc) < 0) { 806 test_failed(t, "fclose: %s", strerror(errno)); 807 return (-1); 808 } 809 810 (void) unlink(ofile); 811 812 myasprintf(&cmd, "%s %s %s -c %s -o %s >>%s 2>&1", 813 compiler, strcmp(env_lang(cenv), "c99") == 0 ? c99flags : c89flags, 814 env_defs(cenv), cfile, ofile, lfile); 815 816 if (extra_debug) { 817 test_debugf(t, "command: %s", cmd); 818 } 819 820 if ((logf = fopen(lfile, "w+")) == NULL) { 821 test_failed(t, "fopen: %s", strerror(errno)); 822 return (-1); 823 } 824 (void) fprintf(logf, "===================\n"); 825 (void) fprintf(logf, "PROGRAM:\n%s\n", sym_test_prog(st)); 826 (void) fprintf(logf, "COMMAND: %s\n", cmd); 827 (void) fprintf(logf, "EXPECT: %s\n", need ? "OK" : "FAIL"); 828 (void) fclose(logf); 829 830 switch (system(cmd)) { 831 case -1: 832 test_failed(t, "error compiling in %s: %s", env_name(cenv), 833 strerror(errno)); 834 return (-1); 835 case 0: 836 if (!need) { 837 fail_count++; 838 show_file(t, lfile); 839 test_failed(t, "symbol visible in %s", env_name(cenv)); 840 return (-1); 841 } 842 break; 843 default: 844 if (need) { 845 fail_count++; 846 show_file(t, lfile); 847 test_failed(t, "error compiling in %s", env_name(cenv)); 848 return (-1); 849 } 850 break; 851 } 852 good_count++; 853 return (0); 854 } 855 856 void 857 test_compile(void) 858 { 859 struct sym_test *st; 860 struct compile_env *cenv; 861 test_t t; 862 int need; 863 864 for (st = next_sym_test(NULL); st; st = next_sym_test(st)) { 865 if ((sym != NULL) && strcmp(sym, sym_test_name(st))) { 866 continue; 867 } 868 /* XXX: we really want a sym_test_desc() */ 869 for (cenv = sym_test_env(st, NULL, &need); 870 cenv != NULL; 871 cenv = sym_test_env(st, cenv, &need)) { 872 t = test_start("%s : %c%s", st->name, 873 need ? '+' : '-', env_name(cenv)); 874 if (do_compile(t, st, cenv, need) == 0) { 875 test_passed(t); 876 } 877 } 878 } 879 880 if (full_count > 0) { 881 test_summary(); 882 } 883 } 884 885 int 886 main(int argc, char **argv) 887 { 888 int optc; 889 int optC = 0; 890 891 while ((optc = getopt(argc, argv, "DdfCs:c:")) != EOF) { 892 switch (optc) { 893 case 'd': 894 test_set_debug(); 895 break; 896 case 'f': 897 test_set_force(); 898 break; 899 case 'D': 900 test_set_debug(); 901 extra_debug++; 902 break; 903 case 'c': 904 compilation = optarg; 905 break; 906 case 'C': 907 optC++; 908 break; 909 case 's': 910 sym = optarg; 911 break; 912 default: 913 (void) fprintf(stderr, "Usage: %s [-df]\n", argv[0]); 914 exit(1); 915 } 916 } 917 918 if (test_load_config(NULL, compilation, 919 "env", do_env, "env_group", do_env_group, NULL) < 0) { 920 exit(1); 921 } 922 923 while (optind < argc) { 924 if (test_load_config(NULL, argv[optind++], 925 "type", do_type, 926 "value", do_value, 927 "func", do_func, 928 NULL) < 0) { 929 exit(1); 930 } 931 } 932 933 if (atexit(cleanup) != 0) { 934 perror("atexit"); 935 exit(1); 936 } 937 938 if (mkworkdir() < 0) { 939 perror("mkdir"); 940 exit(1); 941 } 942 943 find_compiler(); 944 if (!optC) 945 test_compile(); 946 947 exit(0); 948 }