Print this page
Incorporate rmustacc's review feedback.
   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;


 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;


 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 *


 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;


 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 }
   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;


  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;


 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 *


 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;


 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 }