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 }