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