|
|
1.1 root 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) 6/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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.