1 /* 2 ** Sendmail 3 ** Copyright (c) 1983 Eric P. Allman 4 ** Berkeley, California 5 ** 6 ** Copyright (c) 1983 Regents of the University of California. 7 ** All rights reserved. The Berkeley software License Agreement 8 ** specifies the terms and conditions for redistribution. 9 */ 10 11 #ifndef lint 12 static char SccsId[] = "@(#)readcf.c 5.10 (Berkeley) 01/11/86"; 13 #endif not lint 14 15 # include "sendmail.h" 16 17 /* 18 ** READCF -- read control file. 19 ** 20 ** This routine reads the control file and builds the internal 21 ** form. 22 ** 23 ** The file is formatted as a sequence of lines, each taken 24 ** atomically. The first character of each line describes how 25 ** the line is to be interpreted. The lines are: 26 ** Dxval Define macro x to have value val. 27 ** Cxword Put word into class x. 28 ** Fxfile [fmt] Read file for lines to put into 29 ** class x. Use scanf string 'fmt' 30 ** or "%s" if not present. Fmt should 31 ** only produce one string-valued result. 32 ** Hname: value Define header with field-name 'name' 33 ** and value as specified; this will be 34 ** macro expanded immediately before 35 ** use. 36 ** Sn Use rewriting set n. 37 ** Rlhs rhs Rewrite addresses that match lhs to 38 ** be rhs. 39 ** Mn arg=val... Define mailer. n is the internal name. 40 ** Args specify mailer parameters. 41 ** Oxvalue Set option x to value. 42 ** Pname=value Set precedence name to value. 43 ** 44 ** Parameters: 45 ** cfname -- control file name. 46 ** 47 ** Returns: 48 ** none. 49 ** 50 ** Side Effects: 51 ** Builds several internal tables. 52 */ 53 54 readcf(cfname) 55 char *cfname; 56 { 57 FILE *cf; 58 int ruleset = 0; 59 char *q; 60 char **pv; 61 struct rewrite *rwp = NULL; 62 char buf[MAXLINE]; 63 register char *p; 64 extern char **prescan(); 65 extern char **copyplist(); 66 char exbuf[MAXLINE]; 67 char pvpbuf[PSBUFSIZE]; 68 extern char *fgetfolded(); 69 extern char *munchstring(); 70 71 cf = fopen(cfname, "r"); 72 if (cf == NULL) 73 { 74 syserr("cannot open %s", cfname); 75 exit(EX_OSFILE); 76 } 77 78 FileName = cfname; 79 LineNumber = 0; 80 while (fgetfolded(buf, sizeof buf, cf) != NULL) 81 { 82 /* map $ into \001 (ASCII SOH) for macro expansion */ 83 for (p = buf; *p != '\0'; p++) 84 { 85 if (*p != '$') 86 continue; 87 88 if (p[1] == '$') 89 { 90 /* actual dollar sign.... */ 91 (void) strcpy(p, p + 1); 92 continue; 93 } 94 95 /* convert to macro expansion character */ 96 *p = '\001'; 97 } 98 99 /* interpret this line */ 100 switch (buf[0]) 101 { 102 case '\0': 103 case '#': /* comment */ 104 break; 105 106 case 'R': /* rewriting rule */ 107 for (p = &buf[1]; *p != '\0' && *p != '\t'; p++) 108 continue; 109 110 if (*p == '\0') 111 { 112 syserr("invalid rewrite line \"%s\"", buf); 113 break; 114 } 115 116 /* allocate space for the rule header */ 117 if (rwp == NULL) 118 { 119 RewriteRules[ruleset] = rwp = 120 (struct rewrite *) xalloc(sizeof *rwp); 121 } 122 else 123 { 124 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 125 rwp = rwp->r_next; 126 } 127 rwp->r_next = NULL; 128 129 /* expand and save the LHS */ 130 *p = '\0'; 131 expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv); 132 rwp->r_lhs = prescan(exbuf, '\t', pvpbuf); 133 if (rwp->r_lhs != NULL) 134 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 135 136 /* expand and save the RHS */ 137 while (*++p == '\t') 138 continue; 139 q = p; 140 while (*p != '\0' && *p != '\t') 141 p++; 142 *p = '\0'; 143 expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv); 144 rwp->r_rhs = prescan(exbuf, '\t', pvpbuf); 145 if (rwp->r_rhs != NULL) 146 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 147 break; 148 149 case 'S': /* select rewriting set */ 150 ruleset = atoi(&buf[1]); 151 if (ruleset >= MAXRWSETS || ruleset < 0) 152 { 153 syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS); 154 ruleset = 0; 155 } 156 rwp = NULL; 157 break; 158 159 case 'D': /* macro definition */ 160 define(buf[1], newstr(munchstring(&buf[2])), CurEnv); 161 break; 162 163 case 'H': /* required header line */ 164 (void) chompheader(&buf[1], TRUE); 165 break; 166 167 case 'C': /* word class */ 168 case 'F': /* word class from file */ 169 /* read list of words from argument or file */ 170 if (buf[0] == 'F') 171 { 172 /* read from file */ 173 for (p = &buf[2]; *p != '\0' && !isspace(*p); p++) 174 continue; 175 if (*p == '\0') 176 p = "%s"; 177 else 178 { 179 *p = '\0'; 180 while (isspace(*++p)) 181 continue; 182 } 183 fileclass(buf[1], &buf[2], p); 184 break; 185 } 186 187 /* scan the list of words and set class for all */ 188 for (p = &buf[2]; *p != '\0'; ) 189 { 190 register char *wd; 191 char delim; 192 193 while (*p != '\0' && isspace(*p)) 194 p++; 195 wd = p; 196 while (*p != '\0' && !isspace(*p)) 197 p++; 198 delim = *p; 199 *p = '\0'; 200 if (wd[0] != '\0') 201 setclass(buf[1], wd); 202 *p = delim; 203 } 204 break; 205 206 case 'M': /* define mailer */ 207 makemailer(&buf[1]); 208 break; 209 210 case 'O': /* set option */ 211 setoption(buf[1], &buf[2], TRUE, FALSE); 212 break; 213 214 case 'P': /* set precedence */ 215 if (NumPriorities >= MAXPRIORITIES) 216 { 217 toomany('P', MAXPRIORITIES); 218 break; 219 } 220 for (p = &buf[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) 221 continue; 222 if (*p == '\0') 223 goto badline; 224 *p = '\0'; 225 Priorities[NumPriorities].pri_name = newstr(&buf[1]); 226 Priorities[NumPriorities].pri_val = atoi(++p); 227 NumPriorities++; 228 break; 229 230 case 'T': /* trusted user(s) */ 231 p = &buf[1]; 232 while (*p != '\0') 233 { 234 while (isspace(*p)) 235 p++; 236 q = p; 237 while (*p != '\0' && !isspace(*p)) 238 p++; 239 if (*p != '\0') 240 *p++ = '\0'; 241 if (*q == '\0') 242 continue; 243 for (pv = TrustedUsers; *pv != NULL; pv++) 244 continue; 245 if (pv >= &TrustedUsers[MAXTRUST]) 246 { 247 toomany('T', MAXTRUST); 248 break; 249 } 250 *pv = newstr(q); 251 } 252 break; 253 254 default: 255 badline: 256 syserr("unknown control line \"%s\"", buf); 257 } 258 } 259 FileName = NULL; 260 } 261 /* 262 ** TOOMANY -- signal too many of some option 263 ** 264 ** Parameters: 265 ** id -- the id of the error line 266 ** maxcnt -- the maximum possible values 267 ** 268 ** Returns: 269 ** none. 270 ** 271 ** Side Effects: 272 ** gives a syserr. 273 */ 274 275 toomany(id, maxcnt) 276 char id; 277 int maxcnt; 278 { 279 syserr("too many %c lines, %d max", id, maxcnt); 280 } 281 /* 282 ** FILECLASS -- read members of a class from a file 283 ** 284 ** Parameters: 285 ** class -- class to define. 286 ** filename -- name of file to read. 287 ** fmt -- scanf string to use for match. 288 ** 289 ** Returns: 290 ** none 291 ** 292 ** Side Effects: 293 ** 294 ** puts all lines in filename that match a scanf into 295 ** the named class. 296 */ 297 298 fileclass(class, filename, fmt) 299 int class; 300 char *filename; 301 char *fmt; 302 { 303 FILE *f; 304 char buf[MAXLINE]; 305 306 f = fopen(filename, "r"); 307 if (f == NULL) 308 { 309 syserr("cannot open %s", filename); 310 return; 311 } 312 313 while (fgets(buf, sizeof buf, f) != NULL) 314 { 315 register STAB *s; 316 register char *p; 317 # ifdef SCANF 318 char wordbuf[MAXNAME+1]; 319 320 if (sscanf(buf, fmt, wordbuf) != 1) 321 continue; 322 p = wordbuf; 323 # else SCANF 324 p = buf; 325 # endif SCANF 326 327 /* 328 ** Break up the match into words. 329 */ 330 331 while (*p != '\0') 332 { 333 register char *q; 334 335 /* strip leading spaces */ 336 while (isspace(*p)) 337 p++; 338 if (*p == '\0') 339 break; 340 341 /* find the end of the word */ 342 q = p; 343 while (*p != '\0' && !isspace(*p)) 344 p++; 345 if (*p != '\0') 346 *p++ = '\0'; 347 348 /* enter the word in the symbol table */ 349 s = stab(q, ST_CLASS, ST_ENTER); 350 setbitn(class, s->s_class); 351 } 352 } 353 354 (void) fclose(f); 355 } 356 /* 357 ** MAKEMAILER -- define a new mailer. 358 ** 359 ** Parameters: 360 ** line -- description of mailer. This is in labeled 361 ** fields. The fields are: 362 ** P -- the path to the mailer 363 ** F -- the flags associated with the mailer 364 ** A -- the argv for this mailer 365 ** S -- the sender rewriting set 366 ** R -- the recipient rewriting set 367 ** E -- the eol string 368 ** The first word is the canonical name of the mailer. 369 ** 370 ** Returns: 371 ** none. 372 ** 373 ** Side Effects: 374 ** enters the mailer into the mailer table. 375 */ 376 377 makemailer(line) 378 char *line; 379 { 380 register char *p; 381 register struct mailer *m; 382 register STAB *s; 383 int i; 384 char fcode; 385 extern int NextMailer; 386 extern char **makeargv(); 387 extern char *munchstring(); 388 extern char *DelimChar; 389 extern long atol(); 390 391 /* allocate a mailer and set up defaults */ 392 m = (struct mailer *) xalloc(sizeof *m); 393 bzero((char *) m, sizeof *m); 394 m->m_mno = NextMailer; 395 m->m_eol = "\n"; 396 397 /* collect the mailer name */ 398 for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++) 399 continue; 400 if (*p != '\0') 401 *p++ = '\0'; 402 m->m_name = newstr(line); 403 404 /* now scan through and assign info from the fields */ 405 while (*p != '\0') 406 { 407 while (*p != '\0' && (*p == ',' || isspace(*p))) 408 p++; 409 410 /* p now points to field code */ 411 fcode = *p; 412 while (*p != '\0' && *p != '=' && *p != ',') 413 p++; 414 if (*p++ != '=') 415 { 416 syserr("`=' expected"); 417 return; 418 } 419 while (isspace(*p)) 420 p++; 421 422 /* p now points to the field body */ 423 p = munchstring(p); 424 425 /* install the field into the mailer struct */ 426 switch (fcode) 427 { 428 case 'P': /* pathname */ 429 m->m_mailer = newstr(p); 430 break; 431 432 case 'F': /* flags */ 433 for (; *p != '\0'; p++) 434 setbitn(*p, m->m_flags); 435 break; 436 437 case 'S': /* sender rewriting ruleset */ 438 case 'R': /* recipient rewriting ruleset */ 439 i = atoi(p); 440 if (i < 0 || i >= MAXRWSETS) 441 { 442 syserr("invalid rewrite set, %d max", MAXRWSETS); 443 return; 444 } 445 if (fcode == 'S') 446 m->m_s_rwset = i; 447 else 448 m->m_r_rwset = i; 449 break; 450 451 case 'E': /* end of line string */ 452 m->m_eol = newstr(p); 453 break; 454 455 case 'A': /* argument vector */ 456 m->m_argv = makeargv(p); 457 break; 458 459 case 'M': /* maximum message size */ 460 m->m_maxsize = atol(p); 461 break; 462 } 463 464 p = DelimChar; 465 } 466 467 /* now store the mailer away */ 468 if (NextMailer >= MAXMAILERS) 469 { 470 syserr("too many mailers defined (%d max)", MAXMAILERS); 471 return; 472 } 473 Mailer[NextMailer++] = m; 474 s = stab(m->m_name, ST_MAILER, ST_ENTER); 475 s->s_mailer = m; 476 } 477 /* 478 ** MUNCHSTRING -- translate a string into internal form. 479 ** 480 ** Parameters: 481 ** p -- the string to munch. 482 ** 483 ** Returns: 484 ** the munched string. 485 ** 486 ** Side Effects: 487 ** Sets "DelimChar" to point to the string that caused us 488 ** to stop. 489 */ 490 491 char * 492 munchstring(p) 493 register char *p; 494 { 495 register char *q; 496 bool backslash = FALSE; 497 bool quotemode = FALSE; 498 static char buf[MAXLINE]; 499 extern char *DelimChar; 500 501 for (q = buf; *p != '\0'; p++) 502 { 503 if (backslash) 504 { 505 /* everything is roughly literal */ 506 backslash = FALSE; 507 switch (*p) 508 { 509 case 'r': /* carriage return */ 510 *q++ = '\r'; 511 continue; 512 513 case 'n': /* newline */ 514 *q++ = '\n'; 515 continue; 516 517 case 'f': /* form feed */ 518 *q++ = '\f'; 519 continue; 520 521 case 'b': /* backspace */ 522 *q++ = '\b'; 523 continue; 524 } 525 *q++ = *p; 526 } 527 else 528 { 529 if (*p == '\\') 530 backslash = TRUE; 531 else if (*p == '"') 532 quotemode = !quotemode; 533 else if (quotemode || *p != ',') 534 *q++ = *p; 535 else 536 break; 537 } 538 } 539 540 DelimChar = p; 541 *q++ = '\0'; 542 return (buf); 543 } 544 /* 545 ** MAKEARGV -- break up a string into words 546 ** 547 ** Parameters: 548 ** p -- the string to break up. 549 ** 550 ** Returns: 551 ** a char **argv (dynamically allocated) 552 ** 553 ** Side Effects: 554 ** munges p. 555 */ 556 557 char ** 558 makeargv(p) 559 register char *p; 560 { 561 char *q; 562 int i; 563 char **avp; 564 char *argv[MAXPV + 1]; 565 566 /* take apart the words */ 567 i = 0; 568 while (*p != '\0' && i < MAXPV) 569 { 570 q = p; 571 while (*p != '\0' && !isspace(*p)) 572 p++; 573 while (isspace(*p)) 574 *p++ = '\0'; 575 argv[i++] = newstr(q); 576 } 577 argv[i++] = NULL; 578 579 /* now make a copy of the argv */ 580 avp = (char **) xalloc(sizeof *avp * i); 581 bcopy((char *) argv, (char *) avp, sizeof *avp * i); 582 583 return (avp); 584 } 585 /* 586 ** PRINTRULES -- print rewrite rules (for debugging) 587 ** 588 ** Parameters: 589 ** none. 590 ** 591 ** Returns: 592 ** none. 593 ** 594 ** Side Effects: 595 ** prints rewrite rules. 596 */ 597 598 # ifdef DEBUG 599 600 printrules() 601 { 602 register struct rewrite *rwp; 603 register int ruleset; 604 605 for (ruleset = 0; ruleset < 10; ruleset++) 606 { 607 if (RewriteRules[ruleset] == NULL) 608 continue; 609 printf("\n----Rule Set %d:", ruleset); 610 611 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 612 { 613 printf("\nLHS:"); 614 printav(rwp->r_lhs); 615 printf("RHS:"); 616 printav(rwp->r_rhs); 617 } 618 } 619 } 620 621 # endif DEBUG 622 /* 623 ** SETOPTION -- set global processing option 624 ** 625 ** Parameters: 626 ** opt -- option name. 627 ** val -- option value (as a text string). 628 ** safe -- set if this came from a configuration file. 629 ** Some options (if set from the command line) will 630 ** reset the user id to avoid security problems. 631 ** sticky -- if set, don't let other setoptions override 632 ** this value. 633 ** 634 ** Returns: 635 ** none. 636 ** 637 ** Side Effects: 638 ** Sets options as implied by the arguments. 639 */ 640 641 static BITMAP StickyOpt; /* set if option is stuck */ 642 extern char *NetName; /* name of home (local) network */ 643 # ifdef SMTP 644 # ifdef WIZ 645 extern char *WizWord; /* the stored wizard password */ 646 # endif WIZ 647 # endif SMTP 648 649 setoption(opt, val, safe, sticky) 650 char opt; 651 char *val; 652 bool safe; 653 bool sticky; 654 { 655 extern bool atobool(); 656 extern time_t convtime(); 657 extern int QueueLA; 658 extern int RefuseLA; 659 extern bool trusteduser(); 660 extern char *username(); 661 662 # ifdef DEBUG 663 if (tTd(37, 1)) 664 printf("setoption %c=%s", opt, val); 665 # endif DEBUG 666 667 /* 668 ** See if this option is preset for us. 669 */ 670 671 if (bitnset(opt, StickyOpt)) 672 { 673 # ifdef DEBUG 674 if (tTd(37, 1)) 675 printf(" (ignored)\n"); 676 # endif DEBUG 677 return; 678 } 679 680 /* 681 ** Check to see if this option can be specified by this user. 682 */ 683 684 if (!safe && getruid() == 0) 685 safe = TRUE; 686 if (!safe && index("deiLmorsv", opt) == NULL) 687 { 688 # ifdef DEBUG 689 if (tTd(37, 1)) 690 printf(" (unsafe)"); 691 # endif DEBUG 692 if (getruid() != geteuid()) 693 { 694 printf("(Resetting uid)\n"); 695 (void) setgid(getgid()); 696 (void) setuid(getuid()); 697 } 698 } 699 #ifdef DEBUG 700 else if (tTd(37, 1)) 701 printf("\n"); 702 #endif DEBUG 703 704 switch (opt) 705 { 706 case 'A': /* set default alias file */ 707 if (val[0] == '\0') 708 AliasFile = "aliases"; 709 else 710 AliasFile = newstr(val); 711 break; 712 713 case 'a': /* look N minutes for "@:@" in alias file */ 714 if (val[0] == '\0') 715 SafeAlias = 5; 716 else 717 SafeAlias = atoi(val); 718 break; 719 720 case 'B': /* substitution for blank character */ 721 SpaceSub = val[0]; 722 if (SpaceSub == '\0') 723 SpaceSub = ' '; 724 break; 725 726 case 'c': /* don't connect to "expensive" mailers */ 727 NoConnect = atobool(val); 728 break; 729 730 case 'C': /* checkpoint after N connections */ 731 CheckPointLimit = atoi(val); 732 break; 733 734 case 'd': /* delivery mode */ 735 switch (*val) 736 { 737 case '\0': 738 SendMode = SM_DELIVER; 739 break; 740 741 case SM_QUEUE: /* queue only */ 742 #ifndef QUEUE 743 syserr("need QUEUE to set -odqueue"); 744 #endif QUEUE 745 /* fall through..... */ 746 747 case SM_DELIVER: /* do everything */ 748 case SM_FORK: /* fork after verification */ 749 SendMode = *val; 750 break; 751 752 default: 753 syserr("Unknown delivery mode %c", *val); 754 exit(EX_USAGE); 755 } 756 break; 757 758 case 'D': /* rebuild alias database as needed */ 759 AutoRebuild = atobool(val); 760 break; 761 762 case 'e': /* set error processing mode */ 763 switch (*val) 764 { 765 case EM_QUIET: /* be silent about it */ 766 case EM_MAIL: /* mail back */ 767 case EM_BERKNET: /* do berknet error processing */ 768 case EM_WRITE: /* write back (or mail) */ 769 HoldErrs = TRUE; 770 /* fall through... */ 771 772 case EM_PRINT: /* print errors normally (default) */ 773 ErrorMode = *val; 774 break; 775 } 776 break; 777 778 case 'F': /* file mode */ 779 FileMode = atooct(val) & 0777; 780 break; 781 782 case 'f': /* save Unix-style From lines on front */ 783 SaveFrom = atobool(val); 784 break; 785 786 case 'g': /* default gid */ 787 DefGid = atoi(val); 788 break; 789 790 case 'H': /* help file */ 791 if (val[0] == '\0') 792 HelpFile = "sendmail.hf"; 793 else 794 HelpFile = newstr(val); 795 break; 796 797 case 'i': /* ignore dot lines in message */ 798 IgnrDot = atobool(val); 799 break; 800 801 case 'L': /* log level */ 802 LogLevel = atoi(val); 803 break; 804 805 case 'M': /* define macro */ 806 define(val[0], newstr(&val[1]), CurEnv); 807 sticky = FALSE; 808 break; 809 810 case 'm': /* send to me too */ 811 MeToo = atobool(val); 812 break; 813 814 case 'n': /* validate RHS in newaliases */ 815 CheckAliases = atobool(val); 816 break; 817 818 # ifdef DAEMON 819 case 'N': /* home (local?) network name */ 820 NetName = newstr(val); 821 break; 822 # endif DAEMON 823 824 case 'o': /* assume old style headers */ 825 if (atobool(val)) 826 CurEnv->e_flags |= EF_OLDSTYLE; 827 else 828 CurEnv->e_flags &= ~EF_OLDSTYLE; 829 break; 830 831 case 'P': /* postmaster copy address for returned mail */ 832 PostMasterCopy = newstr(val); 833 break; 834 835 case 'q': /* slope of queue only function */ 836 QueueFactor = atoi(val); 837 break; 838 839 case 'Q': /* queue directory */ 840 if (val[0] == '\0') 841 QueueDir = "mqueue"; 842 else 843 QueueDir = newstr(val); 844 break; 845 846 case 'r': /* read timeout */ 847 ReadTimeout = convtime(val); 848 break; 849 850 case 'S': /* status file */ 851 if (val[0] == '\0') 852 StatFile = "sendmail.st"; 853 else 854 StatFile = newstr(val); 855 break; 856 857 case 's': /* be super safe, even if expensive */ 858 SuperSafe = atobool(val); 859 break; 860 861 case 'T': /* queue timeout */ 862 TimeOut = convtime(val); 863 break; 864 865 case 't': /* time zone name */ 866 # ifdef V6 867 StdTimezone = newstr(val); 868 DstTimezone = index(StdTimeZone, ','); 869 if (DstTimezone == NULL) 870 syserr("bad time zone spec"); 871 else 872 *DstTimezone++ = '\0'; 873 # endif V6 874 break; 875 876 case 'u': /* set default uid */ 877 DefUid = atoi(val); 878 break; 879 880 case 'v': /* run in verbose mode */ 881 Verbose = atobool(val); 882 break; 883 884 # ifdef SMTP 885 # ifdef WIZ 886 case 'W': /* set the wizards password */ 887 WizWord = newstr(val); 888 break; 889 # endif WIZ 890 # endif SMTP 891 892 case 'x': /* load avg at which to auto-queue msgs */ 893 QueueLA = atoi(val); 894 break; 895 896 case 'X': /* load avg at which to auto-reject connections */ 897 RefuseLA = atoi(val); 898 break; 899 900 case 'y': /* work recipient factor */ 901 WkRecipFact = atoi(val); 902 break; 903 904 case 'Y': /* fork jobs during queue runs */ 905 ForkQueueRuns = atobool(val); 906 break; 907 908 case 'z': /* work message class factor */ 909 WkClassFact = atoi(val); 910 break; 911 912 case 'Z': /* work time factor */ 913 WkTimeFact = atoi(val); 914 break; 915 916 default: 917 break; 918 } 919 if (sticky) 920 setbitn(opt, StickyOpt); 921 return; 922 } 923 /* 924 ** SETCLASS -- set a word into a class 925 ** 926 ** Parameters: 927 ** class -- the class to put the word in. 928 ** word -- the word to enter 929 ** 930 ** Returns: 931 ** none. 932 ** 933 ** Side Effects: 934 ** puts the word into the symbol table. 935 */ 936 937 setclass(class, word) 938 int class; 939 char *word; 940 { 941 register STAB *s; 942 943 s = stab(word, ST_CLASS, ST_ENTER); 944 setbitn(class, s->s_class); 945 } 946