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