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            *ce_name;
  78         char            *ce_lang;
  79         char            *ce_defs;
  80         int             ce_index;
  81 };
  82 
  83 static struct compile_env compile_env[MAXENV];
  84 
  85 struct env_group {
  86         char                    *eg_name;
  87         uint64_t                eg_mask;
  88         struct env_group        *eg_next;
  89 };
  90 
  91 typedef enum { SYM_TYPE, SYM_VALUE, SYM_FUNC } sym_type_t;
  92 
  93 struct sym_test {
  94         char                    *st_name;
  95         sym_type_t              st_type;
  96         char                    *st_hdrs[MAXHDR];
  97         char                    *st_rtype;
  98         char                    *st_atypes[MAXARG];
  99         uint64_t                st_test_mask;
 100         uint64_t                st_need_mask;
 101         char                    *st_prog;
 102         struct sym_test         *st_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->st_next;
 151 }
 152 
 153 static int
 154 find_env_mask(const char *name, uint64_t *mask)
 155 {
 156         for (int i = 0; i < MAXENV; i++) {
 157                 if (compile_env[i].ce_name != NULL &&
 158                     strcmp(compile_env[i].ce_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->eg_next) {
 165                 if (strcmp(name, eg->eg_name) == 0) {
 166                         *mask |= eg->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].ce_name = mystrdup(name);
 249         compile_env[next_env].ce_lang = mystrdup(lang);
 250         compile_env[next_env].ce_defs = mystrdup(defs);
 251         compile_env[next_env].ce_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->eg_name = mystrdup(name);
 281         eg->eg_mask = mask;
 282         eg->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->st_hdrs[i] != NULL; i++) {
 338                 addprogfmt("#include <%s>\n", st->st_hdrs[i]);
 339         }
 340 
 341         for (s = st->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->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->st_name);
 363                 break;
 364 
 365         case SYM_FUNC:
 366                 addprogstr("\ntest_func(");
 367                 for (int i = 0; st->st_atypes[i] != NULL && i < MAXARG; i++) {
 368                         int didname = 0;
 369                         if (i > 0) {
 370                                 addprogstr(", ");
 371                         }
 372                         if (strcmp(st->st_atypes[i], "void") == 0) {
 373                                 didname = 1;
 374                         }
 375                         if (strcmp(st->st_atypes[i], "") == 0) {
 376                                 didname = 1;
 377                                 addprogstr("void");
 378                         }
 379 
 380                         /* print the argument list */
 381                         for (char *a = st->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->st_atypes[0] == NULL) {
 399                         addprogstr("void");
 400                 }
 401 
 402                 /*
 403                  * Close argument list, and closing ")" for func ptrs.
 404                  * Note that for non-function pointers, s will be empty
 405                  * below, otherwise it points to the trailing argument
 406                  * list.
 407                  */
 408                 addprogfmt(")%s\n{\n\t", s);
 409 
 410                 if (strcmp(st->st_rtype, "") != 0 &&
 411                     strcmp(st->st_rtype, "void") != 0) {
 412                         addprogstr("return ");
 413                 }
 414 
 415                 /* add the function call */
 416                 addprogfmt("%s(", st->st_name);
 417                 for (int i = 0; st->st_atypes[i] != NULL && i < MAXARG; i++) {
 418                         if (strcmp(st->st_atypes[i], "") != 0 &&
 419                             strcmp(st->st_atypes[i], "void") != 0) {
 420                                 addprogfmt("%sa%d", i > 0 ? ", " : "", i);
 421                         }
 422                 }
 423 
 424                 addprogstr(");\n}");
 425                 break;
 426         }
 427 
 428         addprogch('\n');
 429 
 430         st->st_prog = progbuf;
 431 }
 432 
 433 static int
 434 add_envs(struct sym_test *st, char *envs, char **err)
 435 {
 436         char *item;
 437         if (expand_env_list(envs, &st->st_test_mask, &st->st_need_mask,
 438             &item) < 0) {
 439                 myasprintf(err, "bad env action %s", item);
 440                 return (-1);
 441         }
 442         return (0);
 443 }
 444 
 445 static int
 446 add_headers(struct sym_test *st, char *hdrs, char **err)
 447 {
 448         int i = 0;
 449 
 450         for (char *h = strsep(&hdrs, ";"); h != NULL; h = strsep(&hdrs, ";")) {
 451                 if (i >= MAXHDR) {
 452                         myasprintf(err, "too many headers");
 453                         return (-1);
 454                 }
 455                 test_trim(&h);
 456                 st->st_hdrs[i++] = mystrdup(h);
 457         }
 458 
 459         return (0);
 460 }
 461 
 462 static int
 463 add_arg_types(struct sym_test *st, char *atype, char **err)
 464 {
 465         int i = 0;
 466         char *a;
 467         for (a = strsep(&atype, ";"); a != NULL; a = strsep(&atype, ";")) {
 468                 if (i >= MAXARG) {
 469                         myasprintf(err, "too many arguments");
 470                         return (-1);
 471                 }
 472                 test_trim(&a);
 473                 st->st_atypes[i++] = mystrdup(a);
 474         }
 475 
 476         return (0);
 477 }
 478 
 479 static int
 480 do_type(char **fields, int nfields, char **err)
 481 {
 482         char *decl;
 483         char *hdrs;
 484         char *envs;
 485         struct sym_test *st;
 486 
 487         if (nfields != 3) {
 488                 myasprintf(err, "number of fields (%d) != 3", nfields);
 489                 return (-1);
 490         }
 491         decl = fields[0];
 492         hdrs = fields[1];
 493         envs = fields[2];
 494 
 495         st = myzalloc(sizeof (*st));
 496         st->st_type = SYM_TYPE;
 497         st->st_name = mystrdup(decl);
 498         st->st_rtype = mystrdup(decl);
 499 
 500         if ((add_envs(st, envs, err) < 0) ||
 501             (add_headers(st, hdrs, err) < 0)) {
 502                 return (-1);
 503         }
 504         append_sym_test(st);
 505 
 506         return (0);
 507 }
 508 
 509 static int
 510 do_value(char **fields, int nfields, char **err)
 511 {
 512         char *name;
 513         char *type;
 514         char *hdrs;
 515         char *envs;
 516         struct sym_test *st;
 517 
 518         if (nfields != 4) {
 519                 myasprintf(err, "number of fields (%d) != 4", nfields);
 520                 return (-1);
 521         }
 522         name = fields[0];
 523         type = fields[1];
 524         hdrs = fields[2];
 525         envs = fields[3];
 526 
 527         st = myzalloc(sizeof (*st));
 528         st->st_type = SYM_VALUE;
 529         st->st_name = mystrdup(name);
 530         st->st_rtype = mystrdup(type);
 531 
 532         if ((add_envs(st, envs, err) < 0) ||
 533             (add_headers(st, hdrs, err) < 0)) {
 534                 return (-1);
 535         }
 536         append_sym_test(st);
 537 
 538         return (0);
 539 }
 540 
 541 static int
 542 do_func(char **fields, int nfields, char **err)
 543 {
 544         char *name;
 545         char *rtype;
 546         char *atype;
 547         char *hdrs;
 548         char *envs;
 549         struct sym_test *st;
 550 
 551         if (nfields != 5) {
 552                 myasprintf(err, "number of fields (%d) != 5", nfields);
 553                 return (-1);
 554         }
 555         name = fields[0];
 556         rtype = fields[1];
 557         atype = fields[2];
 558         hdrs = fields[3];
 559         envs = fields[4];
 560 
 561         st = myzalloc(sizeof (*st));
 562         st->st_type = SYM_FUNC;
 563         st->st_name = mystrdup(name);
 564         st->st_rtype = mystrdup(rtype);
 565 
 566         if ((add_envs(st, envs, err) < 0) ||
 567             (add_headers(st, hdrs, err) < 0) ||
 568             (add_arg_types(st, atype, err) < 0)) {
 569                 return (-1);
 570         }
 571         append_sym_test(st);
 572 
 573         return (0);
 574 }
 575 
 576 struct sym_test *
 577 next_sym_test(struct sym_test *st)
 578 {
 579         return (st == NULL ? sym_tests : st->st_next);
 580 }
 581 
 582 const char *
 583 sym_test_prog(struct sym_test *st)
 584 {
 585         if (st->st_prog == NULL) {
 586                 mkprog(st);
 587         }
 588         return (st->st_prog);
 589 }
 590 
 591 const char *
 592 sym_test_name(struct sym_test *st)
 593 {
 594         return (st->st_name);
 595 }
 596 
 597 /*
 598  * Iterate through tests.  Pass in NULL for cenv to begin the iteration. For
 599  * subsequent iterations, use the return value from the previous iteration.
 600  * Returns NULL when there are no more environments.
 601  */
 602 struct compile_env *
 603 sym_test_env(struct sym_test *st, struct compile_env *cenv, int *need)
 604 {
 605         int i = cenv ? cenv->ce_index + 1: 0;
 606         uint64_t b = 1ULL << i;
 607 
 608         while ((i < MAXENV) && (b != 0)) {
 609                 cenv = &compile_env[i];
 610                 if (b & st->st_test_mask) {
 611                         *need = (st->st_need_mask & b) ? 1 : 0;
 612                         return (cenv);
 613                 }
 614                 b <<= 1;
 615                 i++;
 616         }
 617         return (NULL);
 618 }
 619 
 620 const char *
 621 env_name(struct compile_env *cenv)
 622 {
 623         return (cenv->ce_name);
 624 }
 625 
 626 const char *
 627 env_lang(struct compile_env *cenv)
 628 {
 629         return (cenv->ce_lang);
 630 }
 631 
 632 const char *
 633 env_defs(struct compile_env *cenv)
 634 {
 635         return (cenv->ce_defs);
 636 }
 637 
 638 static void
 639 show_file(test_t t, const char *path)
 640 {
 641         FILE *f;
 642         char *buf = NULL;
 643         size_t cap = 0;
 644         int line = 1;
 645 
 646         f = fopen(path, "r");
 647         if (f == NULL) {
 648                 test_debugf(t, "fopen(%s): %s", path, strerror(errno));
 649                 return;
 650         }
 651 
 652         test_debugf(t, "----->> begin (%s) <<------", path);
 653         while (getline(&buf, &cap, f) >= 0) {
 654                 (void) strtok(buf, "\r\n");
 655                 test_debugf(t, "%d: %s", line, buf);
 656                 line++;
 657         }
 658         test_debugf(t, "----->> end (%s) <<------", path);
 659         (void) fclose(f);
 660 }
 661 
 662 static void
 663 cleanup(void)
 664 {
 665         if (ofile != NULL) {
 666                 (void) unlink(ofile);
 667                 free(ofile);
 668                 ofile = NULL;
 669         }
 670         if (lfile != NULL) {
 671                 (void) unlink(lfile);
 672                 free(lfile);
 673                 lfile = NULL;
 674         }
 675         if (cfile != NULL) {
 676                 (void) unlink(cfile);
 677                 free(cfile);
 678                 cfile = NULL;
 679         }
 680         if (efile != NULL) {
 681                 (void) unlink(efile);
 682                 free(efile);
 683                 efile = NULL;
 684         }
 685         if (dname) {
 686                 (void) rmdir(dname);
 687                 free(dname);
 688                 dname = NULL;
 689         }
 690 }
 691 
 692 static int
 693 mkworkdir(void)
 694 {
 695         char b[32];
 696         char *d;
 697 
 698         cleanup();
 699 
 700         (void) strlcpy(b, "/tmp/symbols_testXXXXXX", sizeof (b));
 701         if ((d = mkdtemp(b)) == NULL) {
 702                 perror("mkdtemp");
 703                 return (-1);
 704         }
 705         dname = mystrdup(d);
 706         myasprintf(&cfile, "%s/compile_test.c", d);
 707         myasprintf(&ofile, "%s/compile_test.o", d);
 708         myasprintf(&lfile, "%s/compile_test.log", d);
 709         myasprintf(&efile, "%s/compile_test.exe", d);
 710         return (0);
 711 }
 712 
 713 void
 714 find_compiler(void)
 715 {
 716         test_t t;
 717         int i;
 718         FILE *cf;
 719 
 720         t = test_start("finding compiler");
 721 
 722         if ((cf = fopen(cfile, "w+")) == NULL) {
 723                 test_failed(t, "Unable to open %s for write: %s", cfile,
 724                     strerror(errno));
 725                 return;
 726         }
 727         (void) fprintf(cf, "#include <stdio.h>\n");
 728         (void) fprintf(cf, "int main(int argc, char **argv) {\n");
 729         (void) fprintf(cf, "#if defined(__SUNPRO_C)\n");
 730         (void) fprintf(cf, "exit(51);\n");
 731         (void) fprintf(cf, "#elif defined(__GNUC__)\n");
 732         (void) fprintf(cf, "exit(52);\n");
 733         (void) fprintf(cf, "#else\n");
 734         (void) fprintf(cf, "exit(99)\n");
 735         (void) fprintf(cf, "#endif\n}\n");
 736         (void) fclose(cf);
 737 
 738         for (i = 0; compilers[i] != NULL; i++) {
 739                 char cmd[256];
 740                 int rv;
 741 
 742                 (void) snprintf(cmd, sizeof (cmd),
 743                     "%s %s %s -o %s >/dev/null 2>&1",
 744                     compilers[i], MFLAG, cfile, efile);
 745                 test_debugf(t, "trying %s", cmd);
 746                 rv = system(cmd);
 747 
 748                 test_debugf(t, "result: %d", rv);
 749 
 750                 if ((rv < 0) || !WIFEXITED(rv) || WEXITSTATUS(rv) != 0)
 751                         continue;
 752 
 753                 rv = system(efile);
 754                 if (rv >= 0 && WIFEXITED(rv)) {
 755                         rv = WEXITSTATUS(rv);
 756                 } else {
 757                         rv = -1;
 758                 }
 759 
 760                 switch (rv) {
 761                 case 51:        /* STUDIO */
 762                         test_debugf(t, "Found Studio C");
 763                         c89flags = "-Xc -errwarn=%all -v -xc99=%none " MFLAG;
 764                         c99flags = "-Xc -errwarn=%all -v -xc99=%all " MFLAG;
 765                         if (extra_debug) {
 766                                 test_debugf(t, "c89flags: %s", c89flags);
 767                                 test_debugf(t, "c99flags: %s", c99flags);
 768                         }
 769                         test_passed(t);
 770                         break;
 771                 case 52:        /* GCC */
 772                         test_debugf(t, "Found GNU C");
 773                         c89flags = "-Wall -Werror -std=c89 " MFLAG;
 774                         c99flags = "-Wall -Werror -std=c99 " MFLAG;
 775                         if (extra_debug) {
 776                                 test_debugf(t, "c89flags: %s", c89flags);
 777                                 test_debugf(t, "c99flags: %s", c99flags);
 778                         }
 779                         test_passed(t);
 780                         break;
 781                 case 99:
 782                         test_debugf(t, "Found unknown (unsupported) compiler");
 783                         continue;
 784                 default:
 785                         continue;
 786                 }
 787                 myasprintf(&compiler, "%s", compilers[i]);
 788                 test_debugf(t, "compiler: %s", compiler);
 789                 return;
 790         }
 791         test_failed(t, "No compiler found.");
 792 }
 793 
 794 int
 795 do_compile(test_t t, struct sym_test *st, struct compile_env *cenv, int need)
 796 {
 797         char *cmd;
 798         FILE *logf;
 799         FILE *dotc;
 800         const char *prog;
 801 
 802         full_count++;
 803 
 804         if ((dotc = fopen(cfile, "w+")) == NULL) {
 805                 test_failed(t, "fopen(%s): %s", cfile, strerror(errno));
 806                 return (-1);
 807         }
 808         prog = sym_test_prog(st);
 809         if (fwrite(prog, 1, strlen(prog), dotc) < strlen(prog)) {
 810                 test_failed(t, "fwrite: %s", strerror(errno));
 811                 (void) fclose(dotc);
 812                 return (-1);
 813         }
 814         if (fclose(dotc) < 0) {
 815                 test_failed(t, "fclose: %s", strerror(errno));
 816                 return (-1);
 817         }
 818 
 819         (void) unlink(ofile);
 820 
 821         myasprintf(&cmd, "%s %s %s -c %s -o %s >>%s 2>&1",
 822             compiler, strcmp(env_lang(cenv), "c99") == 0 ? c99flags : c89flags,
 823             env_defs(cenv), cfile, ofile, lfile);
 824 
 825         if (extra_debug) {
 826                 test_debugf(t, "command: %s", cmd);
 827         }
 828 
 829         if ((logf = fopen(lfile, "w+")) == NULL) {
 830                 test_failed(t, "fopen: %s", strerror(errno));
 831                 return (-1);
 832         }
 833         (void) fprintf(logf, "===================\n");
 834         (void) fprintf(logf, "PROGRAM:\n%s\n", sym_test_prog(st));
 835         (void) fprintf(logf, "COMMAND: %s\n", cmd);
 836         (void) fprintf(logf, "EXPECT: %s\n", need ? "OK" : "FAIL");
 837         (void) fclose(logf);
 838 
 839         switch (system(cmd)) {
 840         case -1:
 841                 test_failed(t, "error compiling in %s: %s", env_name(cenv),
 842                     strerror(errno));
 843                 return (-1);
 844         case 0:
 845                 if (!need) {
 846                         fail_count++;
 847                         show_file(t, lfile);
 848                         test_failed(t, "symbol visible in %s", env_name(cenv));
 849                         return (-1);
 850                 }
 851                 break;
 852         default:
 853                 if (need) {
 854                         fail_count++;
 855                         show_file(t, lfile);
 856                         test_failed(t, "error compiling in %s", env_name(cenv));
 857                         return (-1);
 858                 }
 859                 break;
 860         }
 861         good_count++;
 862         return (0);
 863 }
 864 
 865 void
 866 test_compile(void)
 867 {
 868         struct sym_test *st;
 869         struct compile_env *cenv;
 870         test_t t;
 871         int need;
 872 
 873         for (st = next_sym_test(NULL); st; st = next_sym_test(st)) {
 874                 if ((sym != NULL) && strcmp(sym, sym_test_name(st))) {
 875                         continue;
 876                 }
 877                 /* XXX: we really want a sym_test_desc() */
 878                 for (cenv = sym_test_env(st, NULL, &need);
 879                     cenv != NULL;
 880                     cenv = sym_test_env(st, cenv, &need)) {
 881                         t = test_start("%s : %c%s", sym_test_name(st),
 882                             need ? '+' : '-', env_name(cenv));
 883                         if (do_compile(t, st, cenv, need) == 0) {
 884                                 test_passed(t);
 885                         }
 886                 }
 887         }
 888 
 889         if (full_count > 0) {
 890                 test_summary();
 891         }
 892 }
 893 
 894 int
 895 main(int argc, char **argv)
 896 {
 897         int optc;
 898         int optC = 0;
 899 
 900         while ((optc = getopt(argc, argv, "DdfCs:c:")) != EOF) {
 901                 switch (optc) {
 902                 case 'd':
 903                         test_set_debug();
 904                         break;
 905                 case 'f':
 906                         test_set_force();
 907                         break;
 908                 case 'D':
 909                         test_set_debug();
 910                         extra_debug++;
 911                         break;
 912                 case 'c':
 913                         compilation = optarg;
 914                         break;
 915                 case 'C':
 916                         optC++;
 917                         break;
 918                 case 's':
 919                         sym = optarg;
 920                         break;
 921                 default:
 922                         (void) fprintf(stderr, "Usage: %s [-df]\n", argv[0]);
 923                         exit(1);
 924                 }
 925         }
 926 
 927         if (test_load_config(NULL, compilation,
 928             "env", do_env, "env_group", do_env_group, NULL) < 0) {
 929                 exit(1);
 930         }
 931 
 932         while (optind < argc) {
 933                 if (test_load_config(NULL, argv[optind++],
 934                     "type", do_type,
 935                     "value", do_value,
 936                     "func", do_func,
 937                     NULL) < 0) {
 938                         exit(1);
 939                 }
 940         }
 941 
 942         if (atexit(cleanup) != 0) {
 943                 perror("atexit");
 944                 exit(1);
 945         }
 946 
 947         if (mkworkdir() < 0) {
 948                 perror("mkdir");
 949                 exit(1);
 950         }
 951 
 952         find_compiler();
 953         if (!optC)
 954                 test_compile();
 955 
 956         exit(0);
 957 }