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