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