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