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