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: #if !defined(lint) && !defined(NOSCCS) 18: static char sccsid[] = "@(#)readcf.c 5.12 (Berkeley) 4/1/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: /*FALLTHROUGH*/ 870: 871: case 't': /* time zone name */ 872: break; 873: 874: case 'u': /* set default uid */ 875: DefUid = atoi(val); 876: break; 877: 878: case 'v': /* run in verbose mode */ 879: Verbose = atobool(val); 880: break; 881: 882: # ifdef SMTP 883: # ifdef WIZ 884: case 'W': /* set the wizards password */ 885: WizWord = newstr(val); 886: break; 887: # endif WIZ 888: # endif SMTP 889: 890: case 'x': /* load avg at which to auto-queue msgs */ 891: QueueLA = atoi(val); 892: break; 893: 894: case 'X': /* load avg at which to auto-reject connections */ 895: RefuseLA = atoi(val); 896: break; 897: 898: case 'y': /* work recipient factor */ 899: WkRecipFact = atoi(val); 900: break; 901: 902: case 'Y': /* fork jobs during queue runs */ 903: ForkQueueRuns = atobool(val); 904: break; 905: 906: case 'z': /* work message class factor */ 907: WkClassFact = atoi(val); 908: break; 909: 910: case 'Z': /* work time factor */ 911: WkTimeFact = atoi(val); 912: break; 913: 914: default: 915: break; 916: } 917: if (sticky) 918: setbitn(opt, StickyOpt); 919: return; 920: } 921: /* 922: ** SETCLASS -- set a word into a class 923: ** 924: ** Parameters: 925: ** class -- the class to put the word in. 926: ** word -- the word to enter 927: ** 928: ** Returns: 929: ** none. 930: ** 931: ** Side Effects: 932: ** puts the word into the symbol table. 933: */ 934: 935: setclass(class, word) 936: int class; 937: char *word; 938: { 939: register STAB *s; 940: 941: s = stab(word, ST_CLASS, ST_ENTER); 942: setbitn(class, s->s_class); 943: }