xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 22932)
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.2 (Berkeley) 06/07/85";
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 p f s r a	Define mailer.  n - internal name,
40 **				p - pathname, f - flags, s - rewriting
41 **				ruleset for sender, s - rewriting ruleset
42 **				for recipients, a - argument vector.
43 **		Oxvalue		Set option x to value.
44 **		Pname=value	Set precedence name to value.
45 **
46 **	Parameters:
47 **		cfname -- control file name.
48 **
49 **	Returns:
50 **		none.
51 **
52 **	Side Effects:
53 **		Builds several internal tables.
54 */
55 
56 readcf(cfname)
57 	char *cfname;
58 {
59 	FILE *cf;
60 	int ruleset = 0;
61 	char *q;
62 	char **pv;
63 	struct rewrite *rwp = NULL;
64 	char buf[MAXLINE];
65 	register char *p;
66 	extern char **prescan();
67 	extern char **copyplist();
68 	char exbuf[MAXLINE];
69 	char pvpbuf[PSBUFSIZE];
70 	extern char *fgetfolded();
71 	extern char *munchstring();
72 
73 	cf = fopen(cfname, "r");
74 	if (cf == NULL)
75 	{
76 		syserr("cannot open %s", cfname);
77 		exit(EX_OSFILE);
78 	}
79 
80 	FileName = cfname;
81 	LineNumber = 0;
82 	while (fgetfolded(buf, sizeof buf, cf) != NULL)
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 				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 		  default:
257 		  badline:
258 			syserr("unknown control line \"%s\"", buf);
259 		}
260 	}
261 	FileName = NULL;
262 }
263 /*
264 **  TOOMANY -- signal too many of some option
265 **
266 **	Parameters:
267 **		id -- the id of the error line
268 **		maxcnt -- the maximum possible values
269 **
270 **	Returns:
271 **		none.
272 **
273 **	Side Effects:
274 **		gives a syserr.
275 */
276 
277 toomany(id, maxcnt)
278 	char id;
279 	int maxcnt;
280 {
281 	syserr("too many %c lines, %d max", id, maxcnt);
282 }
283 /*
284 **  FILECLASS -- read members of a class from a file
285 **
286 **	Parameters:
287 **		class -- class to define.
288 **		filename -- name of file to read.
289 **		fmt -- scanf string to use for match.
290 **
291 **	Returns:
292 **		none
293 **
294 **	Side Effects:
295 **
296 **		puts all lines in filename that match a scanf into
297 **			the named class.
298 */
299 
300 fileclass(class, filename, fmt)
301 	int class;
302 	char *filename;
303 	char *fmt;
304 {
305 	register FILE *f;
306 	char buf[MAXLINE];
307 
308 	f = fopen(filename, "r");
309 	if (f == NULL)
310 	{
311 		syserr("cannot open %s", filename);
312 		return;
313 	}
314 
315 	while (fgets(buf, sizeof buf, f) != NULL)
316 	{
317 		register STAB *s;
318 		char wordbuf[MAXNAME+1];
319 
320 		if (sscanf(buf, fmt, wordbuf) != 1)
321 			continue;
322 		s = stab(wordbuf, ST_CLASS, ST_ENTER);
323 		setbitn(class, s->s_class);
324 	}
325 
326 	(void) fclose(f);
327 }
328 /*
329 **  MAKEMAILER -- define a new mailer.
330 **
331 **	Parameters:
332 **		line -- description of mailer.  This is in labeled
333 **			fields.  The fields are:
334 **			   P -- the path to the mailer
335 **			   F -- the flags associated with the mailer
336 **			   A -- the argv for this mailer
337 **			   S -- the sender rewriting set
338 **			   R -- the recipient rewriting set
339 **			   E -- the eol string
340 **			The first word is the canonical name of the mailer.
341 **
342 **	Returns:
343 **		none.
344 **
345 **	Side Effects:
346 **		enters the mailer into the mailer table.
347 */
348 
349 makemailer(line)
350 	char *line;
351 {
352 	register char *p;
353 	register struct mailer *m;
354 	register STAB *s;
355 	int i;
356 	char fcode;
357 	extern int NextMailer;
358 	extern char **makeargv();
359 	extern char *munchstring();
360 	extern char *DelimChar;
361 	extern long atol();
362 
363 	/* allocate a mailer and set up defaults */
364 	m = (struct mailer *) xalloc(sizeof *m);
365 	bzero((char *) m, sizeof *m);
366 	m->m_mno = NextMailer;
367 	m->m_eol = "\n";
368 
369 	/* collect the mailer name */
370 	for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++)
371 		continue;
372 	if (*p != '\0')
373 		*p++ = '\0';
374 	m->m_name = newstr(line);
375 
376 	/* now scan through and assign info from the fields */
377 	while (*p != '\0')
378 	{
379 		while (*p != '\0' && (*p == ',' || isspace(*p)))
380 			p++;
381 
382 		/* p now points to field code */
383 		fcode = *p;
384 		while (*p != '\0' && *p != '=' && *p != ',')
385 			p++;
386 		if (*p++ != '=')
387 		{
388 			syserr("`=' expected");
389 			return;
390 		}
391 		while (isspace(*p))
392 			p++;
393 
394 		/* p now points to the field body */
395 		p = munchstring(p);
396 
397 		/* install the field into the mailer struct */
398 		switch (fcode)
399 		{
400 		  case 'P':		/* pathname */
401 			m->m_mailer = newstr(p);
402 			break;
403 
404 		  case 'F':		/* flags */
405 			for (; *p != '\0'; p++)
406 				setbitn(*p, m->m_flags);
407 			break;
408 
409 		  case 'S':		/* sender rewriting ruleset */
410 		  case 'R':		/* recipient rewriting ruleset */
411 			i = atoi(p);
412 			if (i < 0 || i >= MAXRWSETS)
413 			{
414 				syserr("invalid rewrite set, %d max", MAXRWSETS);
415 				return;
416 			}
417 			if (fcode == 'S')
418 				m->m_s_rwset = i;
419 			else
420 				m->m_r_rwset = i;
421 			break;
422 
423 		  case 'E':		/* end of line string */
424 			m->m_eol = newstr(p);
425 			break;
426 
427 		  case 'A':		/* argument vector */
428 			m->m_argv = makeargv(p);
429 			break;
430 
431 		  case 'M':		/* maximum message size */
432 			m->m_maxsize = atol(p);
433 			break;
434 		}
435 
436 		p = DelimChar;
437 	}
438 
439 	/* now store the mailer away */
440 	if (NextMailer >= MAXMAILERS)
441 	{
442 		syserr("too many mailers defined (%d max)", MAXMAILERS);
443 		return;
444 	}
445 	Mailer[NextMailer++] = m;
446 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
447 	s->s_mailer = m;
448 }
449 /*
450 **  MUNCHSTRING -- translate a string into internal form.
451 **
452 **	Parameters:
453 **		p -- the string to munch.
454 **
455 **	Returns:
456 **		the munched string.
457 **
458 **	Side Effects:
459 **		Sets "DelimChar" to point to the string that caused us
460 **		to stop.
461 */
462 
463 char *
464 munchstring(p)
465 	register char *p;
466 {
467 	register char *q;
468 	bool backslash = FALSE;
469 	bool quotemode = FALSE;
470 	static char buf[MAXLINE];
471 	extern char *DelimChar;
472 
473 	for (q = buf; *p != '\0'; p++)
474 	{
475 		if (backslash)
476 		{
477 			/* everything is roughly literal */
478 			backslash = FALSE;
479 			switch (*p)
480 			{
481 			  case 'r':		/* carriage return */
482 				*q++ = '\r';
483 				continue;
484 
485 			  case 'n':		/* newline */
486 				*q++ = '\n';
487 				continue;
488 
489 			  case 'f':		/* form feed */
490 				*q++ = '\f';
491 				continue;
492 
493 			  case 'b':		/* backspace */
494 				*q++ = '\b';
495 				continue;
496 			}
497 			*q++ = *p;
498 		}
499 		else
500 		{
501 			if (*p == '\\')
502 				backslash = TRUE;
503 			else if (*p == '"')
504 				quotemode = !quotemode;
505 			else if (quotemode || *p != ',')
506 				*q++ = *p;
507 			else
508 				break;
509 		}
510 	}
511 
512 	DelimChar = p;
513 	*q++ = '\0';
514 	return (buf);
515 }
516 /*
517 **  MAKEARGV -- break up a string into words
518 **
519 **	Parameters:
520 **		p -- the string to break up.
521 **
522 **	Returns:
523 **		a char **argv (dynamically allocated)
524 **
525 **	Side Effects:
526 **		munges p.
527 */
528 
529 char **
530 makeargv(p)
531 	register char *p;
532 {
533 	char *q;
534 	int i;
535 	char **avp;
536 	char *argv[MAXPV + 1];
537 
538 	/* take apart the words */
539 	i = 0;
540 	while (*p != '\0' && i < MAXPV)
541 	{
542 		q = p;
543 		while (*p != '\0' && !isspace(*p))
544 			p++;
545 		while (isspace(*p))
546 			*p++ = '\0';
547 		argv[i++] = newstr(q);
548 	}
549 	argv[i++] = NULL;
550 
551 	/* now make a copy of the argv */
552 	avp = (char **) xalloc(sizeof *avp * i);
553 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
554 
555 	return (avp);
556 }
557 /*
558 **  PRINTRULES -- print rewrite rules (for debugging)
559 **
560 **	Parameters:
561 **		none.
562 **
563 **	Returns:
564 **		none.
565 **
566 **	Side Effects:
567 **		prints rewrite rules.
568 */
569 
570 # ifdef DEBUG
571 
572 printrules()
573 {
574 	register struct rewrite *rwp;
575 	register int ruleset;
576 
577 	for (ruleset = 0; ruleset < 10; ruleset++)
578 	{
579 		if (RewriteRules[ruleset] == NULL)
580 			continue;
581 		printf("\n----Rule Set %d:", ruleset);
582 
583 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
584 		{
585 			printf("\nLHS:");
586 			printav(rwp->r_lhs);
587 			printf("RHS:");
588 			printav(rwp->r_rhs);
589 		}
590 	}
591 }
592 
593 # endif DEBUG
594 /*
595 **  SETOPTION -- set global processing option
596 **
597 **	Parameters:
598 **		opt -- option name.
599 **		val -- option value (as a text string).
600 **		safe -- set if this came from a configuration file.
601 **			Some options (if set from the command line) will
602 **			reset the user id to avoid security problems.
603 **		sticky -- if set, don't let other setoptions override
604 **			this value.
605 **
606 **	Returns:
607 **		none.
608 **
609 **	Side Effects:
610 **		Sets options as implied by the arguments.
611 */
612 
613 static BITMAP	StickyOpt;		/* set if option is stuck */
614 extern char	*WizWord;		/* the stored wizard password */
615 extern char	*NetName;		/* name of home (local) network */
616 
617 setoption(opt, val, safe, sticky)
618 	char opt;
619 	char *val;
620 	bool safe;
621 	bool sticky;
622 {
623 	extern bool atobool();
624 	extern time_t convtime();
625 	extern int QueueLA;
626 	extern int RefuseLA;
627 	extern bool trusteduser();
628 	extern char *username();
629 
630 # ifdef DEBUG
631 	if (tTd(37, 1))
632 		printf("setoption %c=%s", opt, val);
633 # endif DEBUG
634 
635 	/*
636 	**  See if this option is preset for us.
637 	*/
638 
639 	if (bitnset(opt, StickyOpt))
640 	{
641 # ifdef DEBUG
642 		if (tTd(37, 1))
643 			printf(" (ignored)\n");
644 # endif DEBUG
645 		return;
646 	}
647 
648 	/*
649 	**  Check to see if this option can be specified by this user.
650 	*/
651 
652 	if (!safe && getruid())
653 		safe = TRUE;
654 	if (!safe && index("deiLmorsv", opt) == NULL)
655 	{
656 # ifdef DEBUG
657 		if (tTd(37, 1))
658 			printf(" (unsafe)");
659 # endif DEBUG
660 		if (getruid() != geteuid())
661 		{
662 			printf("(Resetting uid)\n");
663 			setgid(getgid());
664 			setuid(getuid());
665 		}
666 	}
667 #ifdef DEBUG
668 	else if (tTd(37, 1))
669 		printf("\n");
670 #endif DEBUG
671 
672 	switch (opt)
673 	{
674 	  case 'A':		/* set default alias file */
675 		if (val[0] == '\0')
676 			AliasFile = "aliases";
677 		else
678 			AliasFile = newstr(val);
679 		break;
680 
681 	  case 'a':		/* look N minutes for "@:@" in alias file */
682 		if (val[0] == '\0')
683 			SafeAlias = 5;
684 		else
685 			SafeAlias = atoi(val);
686 		break;
687 
688 	  case 'B':		/* substitution for blank character */
689 		SpaceSub = val[0];
690 		if (SpaceSub == '\0')
691 			SpaceSub = ' ';
692 		break;
693 
694 	  case 'c':		/* don't connect to "expensive" mailers */
695 		NoConnect = atobool(val);
696 		break;
697 
698 	  case 'd':		/* delivery mode */
699 		switch (*val)
700 		{
701 		  case '\0':
702 			SendMode = SM_DELIVER;
703 			break;
704 
705 		  case SM_QUEUE:	/* queue only */
706 #ifndef QUEUE
707 			syserr("need QUEUE to set -odqueue");
708 #endif QUEUE
709 			/* fall through..... */
710 
711 		  case SM_DELIVER:	/* do everything */
712 		  case SM_FORK:		/* fork after verification */
713 			SendMode = *val;
714 			break;
715 
716 		  default:
717 			syserr("Unknown delivery mode %c", *val);
718 			exit(EX_USAGE);
719 		}
720 		break;
721 
722 	  case 'D':		/* rebuild alias database as needed */
723 		AutoRebuild = atobool(val);
724 		break;
725 
726 	  case 'e':		/* set error processing mode */
727 		switch (*val)
728 		{
729 		  case EM_QUIET:	/* be silent about it */
730 		  case EM_MAIL:		/* mail back */
731 		  case EM_BERKNET:	/* do berknet error processing */
732 		  case EM_WRITE:	/* write back (or mail) */
733 			HoldErrs = TRUE;
734 			/* fall through... */
735 
736 		  case EM_PRINT:	/* print errors normally (default) */
737 			ErrorMode = *val;
738 			break;
739 		}
740 		break;
741 
742 	  case 'F':		/* file mode */
743 		FileMode = atooct(val) & 0777;
744 		break;
745 
746 	  case 'f':		/* save Unix-style From lines on front */
747 		SaveFrom = atobool(val);
748 		break;
749 
750 	  case 'g':		/* default gid */
751 		DefGid = atoi(val);
752 		break;
753 
754 	  case 'H':		/* help file */
755 		if (val[0] == '\0')
756 			HelpFile = "sendmail.hf";
757 		else
758 			HelpFile = newstr(val);
759 		break;
760 
761 	  case 'i':		/* ignore dot lines in message */
762 		IgnrDot = atobool(val);
763 		break;
764 
765 	  case 'L':		/* log level */
766 		LogLevel = atoi(val);
767 		break;
768 
769 	  case 'M':		/* define macro */
770 		define(val[0], newstr(&val[1]), CurEnv);
771 		sticky = FALSE;
772 		break;
773 
774 	  case 'm':		/* send to me too */
775 		MeToo = atobool(val);
776 		break;
777 
778 # ifdef DAEMON
779 	  case 'N':		/* home (local?) network name */
780 		NetName = newstr(val);
781 		break;
782 # endif DAEMON
783 
784 	  case 'o':		/* assume old style headers */
785 		if (atobool(val))
786 			CurEnv->e_flags |= EF_OLDSTYLE;
787 		else
788 			CurEnv->e_flags &= ~EF_OLDSTYLE;
789 		break;
790 
791 	  case 'Q':		/* queue directory */
792 		if (val[0] == '\0')
793 			QueueDir = "mqueue";
794 		else
795 			QueueDir = newstr(val);
796 		break;
797 
798 	  case 'r':		/* read timeout */
799 		ReadTimeout = convtime(val);
800 		break;
801 
802 	  case 'S':		/* status file */
803 		if (val[0] == '\0')
804 			StatFile = "sendmail.st";
805 		else
806 			StatFile = newstr(val);
807 		break;
808 
809 	  case 's':		/* be super safe, even if expensive */
810 		SuperSafe = atobool(val);
811 		break;
812 
813 	  case 'T':		/* queue timeout */
814 		TimeOut = convtime(val);
815 		break;
816 
817 	  case 't':		/* time zone name */
818 # ifdef V6
819 		StdTimezone = newstr(val);
820 		DstTimezone = index(StdTimeZone, ',');
821 		if (DstTimezone == NULL)
822 			syserr("bad time zone spec");
823 		else
824 			*DstTimezone++ = '\0';
825 # endif V6
826 		break;
827 
828 	  case 'u':		/* set default uid */
829 		DefUid = atoi(val);
830 		break;
831 
832 	  case 'v':		/* run in verbose mode */
833 		Verbose = atobool(val);
834 		break;
835 
836 # ifdef DEBUG
837 	  case 'W':		/* set the wizards password */
838 		WizWord = newstr(val);
839 		break;
840 # endif DEBUG
841 
842 	  case 'x':		/* load avg at which to auto-queue msgs */
843 		QueueLA = atoi(val);
844 		break;
845 
846 	  case 'X':		/* load avg at which to auto-reject connections */
847 		RefuseLA = atoi(val);
848 		break;
849 
850 	  default:
851 		break;
852 	}
853 	if (sticky)
854 		setbitn(opt, StickyOpt);
855 	return;
856 }
857 /*
858 **  SETCLASS -- set a word into a class
859 **
860 **	Parameters:
861 **		class -- the class to put the word in.
862 **		word -- the word to enter
863 **
864 **	Returns:
865 **		none.
866 **
867 **	Side Effects:
868 **		puts the word into the symbol table.
869 */
870 
871 setclass(class, word)
872 	int class;
873 	char *word;
874 {
875 	register STAB *s;
876 
877 	s = stab(word, ST_CLASS, ST_ENTER);
878 	setbitn(class, s->s_class);
879 }
880