xref: /original-bsd/usr.bin/m4/m4.c (revision f0fd5f8a)
1 /* @(#)m4.c	1.2 (Berkeley) 12/16/82 */
2 #include <stdio.h>
3 #include <signal.h>
4 
5 #define ERROR NULL
6 #define	READ	"r"
7 #define	WRITE	"w"
8 
9 #define	EOS	0
10 int	lpar	= '(';
11 #define	LPAR	lpar
12 #define	RPAR	')'
13 #define	COMMA	','
14 #define	GRAVE	'`'
15 #define	ACUTE	'\''
16 #define LBRAK	'['
17 #define RBRAK	']'
18 #ifdef  M4
19 char	lquote	LBRAK;
20 char	rquote	RBRAK;
21 #endif
22 #ifndef M4
23 char	lquote	= GRAVE;
24 char	rquote	= ACUTE;
25 #endif
26 #define	COMMENT	'#'
27 #define	ALPH	1
28 #define	DIG	2
29 
30 #define	HSHSIZ	199	/* prime */
31 #define	STACKS	50
32 #define	SAVS	4096
33 #define	TOKS	128
34 
35 #define	putbak(c)	*ip++ = c;
36 #define	getchr()	(ip>cur_ip?*--ip: getc(infile[infptr]))
37 #define	putchr(c)	if (cp==NULL) {if (curfile)putc(c,curfile);} else *op++ = c
38 char	type[] = {
39 	0,	0,	0,	0,	0,	0,	0,	0,
40 	0,	0,	0,	0,	0,	0,	0,	0,
41 	0,	0,	0,	0,	0,	0,	0,	0,
42 	0,	0,	0,	0,	0,	0,	0,	0,
43 	0,	0,	0,	0,	0,	0,	0,	0,
44 	0,	0,	0,	0,	0,	0,	0,	0,
45 	DIG,	DIG,	DIG,	DIG,	DIG,	DIG,	DIG,	DIG,
46 	DIG,	DIG,	0,	0,	0,	0,	0,	0,
47 	0,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,
48 	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,
49 	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,
50 	ALPH,	ALPH,	ALPH,	0,	0,	0,	0,	ALPH,
51 	0,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,
52 	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,
53 	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,	ALPH,
54 	ALPH,	ALPH,	ALPH,	0,	0,	0,	0,	0,
55 };
56 
57 char	token[TOKS];
58 char	eoa[]	= "\0";
59 
60 #define	RESERVED	01	/* This is a reserved word with side action */
61 struct	nlist {
62 	char	*name;
63 	char	*def;
64 	char	flag;
65 	struct	nlist *next;
66 };
67 
68 struct	nlist	*hshtab[HSHSIZ];
69 char	ibuf[SAVS+TOKS];
70 char	obuf[SAVS+TOKS];
71 char	*op	= obuf;
72 char	*ip	= ibuf;
73 char *ip_stk[10] = {ibuf};
74 char *cur_ip = ibuf;
75 struct call {
76 	char	**argp;
77 	int	plev;
78 };
79 struct	call	*cp = NULL;
80 
81 char	*makeloc;
82 char	*ifdefloc;
83 char	*lenloc;
84 char	*undefloc;
85 char	*shiftloc;
86 char	*cqloc;
87 char	*defloc;
88 char	*evaloc;
89 char	*incrloc;
90 char	*substrloc;
91 char	*indexloc;
92 char	*transloc;
93 char	*ifloc;
94 char	*divloc;
95 char	*divnumloc;
96 char	*undivloc;
97 char	*dnlloc;
98 char	*inclloc;
99 char	*sinclloc;
100 char	*syscmdloc;
101 char	*dumploc;
102 char	*errploc;
103 
104 char	*tempname;
105 struct nlist	*lookup();
106 char	*install();
107 char	*malloc();
108 char	*mktemp();
109 char	*copy();
110 long	ctol();
111 int	hshval;
112 FILE	*olist[11] = { stdout };
113 int	okret;
114 int	curout	= 0;
115 FILE	*curfile = { stdout };
116 FILE	*infile[10] = { stdin };
117 int	infptr	= 0;
118 
119 main(argc, argv)
120 char **argv;
121 {
122 	char *argstk[STACKS+10];
123 	struct call callst[STACKS];
124 	register char *tp, **ap;
125 	int delexit(), catchsig();
126 	register t;
127 	int i;
128 
129 #ifdef gcos
130 #ifdef M4
131 	install("GCOS", eoa, 0);
132 #endif
133 #ifndef M4
134 	install("gcos", eoa, 0);
135 #endif
136 #endif
137 #ifdef unix
138 #ifdef M4
139 	install("UNIX", eoa, 0);
140 #endif
141 #ifndef M4
142 	install("unix", eoa, 0);
143 #endif
144 #endif
145 
146 #ifdef M4
147 	makeloc = install("MAKETEMP", eoa, RESERVED);
148 	ifdefloc = install("IFDEF", eoa, RESERVED);
149 	lenloc = install("LEN", eoa, RESERVED);
150 	undefloc = install("UNDEFINE", eoa, RESERVED);
151 	shiftloc = install("SHIFT", eoa, RESERVED);
152 	cqloc = install("CHANGEQUOTE", eoa, RESERVED);
153 	defloc = install("DEFINE", eoa, RESERVED);
154 	evaloc = install("EVAL", eoa, RESERVED);
155 	inclloc = install("INCLUDE", eoa, RESERVED);
156 	sinclloc = install("SINCLUDE", eoa, RESERVED);
157 	syscmdloc = install("SYSCMD", eoa, RESERVED);
158 	dumploc = install("DUMPDEF", eoa, RESERVED);
159 	errploc = install("ERRPRINT", eoa, RESERVED);
160 	incrloc = install("INCR", eoa, RESERVED);
161 	substrloc = install("SUBSTR", eoa, RESERVED);
162 	indexloc = install("INDEX", eoa, RESERVED);
163 	transloc = install("TRANSLIT", eoa, RESERVED);
164 	ifloc = install("IFELSE", eoa, RESERVED);
165 	divloc = install("DIVERT", eoa, RESERVED);
166 	divnumloc = install("DIVNUM", eoa, RESERVED);
167 	undivloc = install("UNDIVERT", eoa, RESERVED);
168 	dnlloc = install("DNL", eoa, RESERVED);
169 #endif
170 
171 #ifndef M4
172 	makeloc = install("maketemp", eoa, RESERVED);
173 	ifdefloc = install("ifdef", eoa, RESERVED);
174 	lenloc = install("len", eoa, RESERVED);
175 	undefloc = install("undefine", eoa, RESERVED);
176 	shiftloc = install("shift", eoa, RESERVED);
177 	cqloc = install("changequote", eoa, RESERVED);
178 	defloc = install("define", eoa, RESERVED);
179 	evaloc = install("eval", eoa, RESERVED);
180 	inclloc = install("include", eoa, RESERVED);
181 	sinclloc = install("sinclude", eoa, RESERVED);
182 	syscmdloc = install("syscmd", eoa, RESERVED);
183 	dumploc = install("dumpdef", eoa, RESERVED);
184 	errploc = install("errprint", eoa, RESERVED);
185 	incrloc = install("incr", eoa, RESERVED);
186 	substrloc = install("substr", eoa, RESERVED);
187 	indexloc = install("index", eoa, RESERVED);
188 	transloc = install("translit", eoa, RESERVED);
189 	ifloc = install("ifelse", eoa, RESERVED);
190 	divloc = install("divert", eoa, RESERVED);
191 	divnumloc = install("divnum", eoa, RESERVED);
192 	undivloc = install("undivert", eoa, RESERVED);
193 	dnlloc = install("dnl", eoa, RESERVED);
194 #endif
195 	ap = argstk;
196 #ifndef gcos
197 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
198 		signal(SIGHUP, catchsig);
199 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
200 		signal(SIGINT, catchsig);
201 	tempname = mktemp("/tmp/m4aXXXXX");
202 	close(creat(tempname, 0));
203 #endif
204 #ifdef gcos
205 	tempname = "m4.tempa";
206 #endif
207 	if (argc>1)
208 		putbak(0);
209 	for (;;) {
210 		tp = token;
211 		*tp++ = t = getchr();
212 		*tp = EOS;
213 		if (t<=0) {
214 			if (infptr > 0) {
215 				fclose(infile[infptr]);
216 				infptr--;
217 				cur_ip = ip_stk[infptr];
218 				continue;
219 			}
220 			if (argc<=1)
221 				break;
222 			argc--;
223 			argv++;
224 			if (infile[infptr]!=stdin)
225 				fclose(infile[infptr]);
226 			if (**argv=='-')
227 				infile[infptr] = stdin;
228 			else if ((infile[infptr]=fopen(argv[0], READ))==ERROR) {
229 				fprintf(stderr, "m4: file not found: %s\n", argv[0]);
230 				delexit();
231 			}
232 			continue;
233 		}
234 		if (type[t]==ALPH) {
235 			while ((t=type[*tp++=getchr()])==ALPH||t==DIG);
236 			putbak(*--tp);
237 			*tp = EOS;
238 			if (*ap = lookup(token)->def) {
239 				if (++ap >= &argstk[STACKS]) {
240 					fprintf(stderr, "m4: arg stack overflow\n");
241 					delexit();
242 				}
243 				if (cp==NULL)
244 					cp = callst;
245 				else if (++cp > &callst[STACKS]) {
246 					fprintf(stderr, "m4: call stack overflow\n");
247 					delexit();
248 				}
249 				cp->argp = ap;
250 				*ap++ = op;
251 				puttok();
252 				*op++ = '\0';
253 				t = getchr();
254 				putbak(t);
255 				if (t!=LPAR) {
256 					/* if (t!=' ' && t!='\t') */
257 						putbak(')');
258 					putbak('(');
259 				}
260 				else	/* try to fix arg count */
261 					*ap++ = op;
262 				cp->plev = 0;
263 			} else
264 				puttok();
265 		} else if (t==lquote) {
266 			i = 1;
267 			for (;;) {
268 				t = getchr();
269 				if (t==rquote) {
270 					i--;
271 					if (i==0)
272 						break;
273 				} else if (t==lquote)
274 					i++;
275 				else if (t<0) {
276 					fprintf(stderr, "m4: EOF in string\n");
277 					delexit();
278 				}
279 				putchr(t);
280 			}
281 		} else if (t==COMMENT) {
282 			putbak(t);
283 			while ((t = getchr())!='\n'&& t>=0)
284 				if (cp==NULL)
285 					putchr(t);
286 			putbak(t);
287 		} else if (cp==NULL) {
288 			puttok();
289 		} else if (t==LPAR) {
290 			if (cp->plev)
291 				*op++ = t;
292 			cp->plev++;
293 			while ( (t=getchr())==' ' || t=='\t' || t=='\n')
294 				;	/* skip leading white space during arg collection */
295 			putbak(t);
296 /*
297 		} else if (t==' ' || t=='\t' || t=='\n') {
298 			continue;
299 */
300 		} else if (t==RPAR) {
301 			cp->plev--;
302 			if (cp->plev==0) {
303 				*op++ = '\0';
304 				expand(cp->argp, ap-cp->argp-1);
305 				op = *cp->argp;
306 				ap = cp->argp-1;
307 				cp--;
308 				if (cp < callst)
309 					cp = NULL;
310 			} else
311 				*op++ = t;
312 		} else if (t==COMMA && cp->plev<=1) {
313 			*op++ = '\0';
314 			*ap++ = op;
315 			while ((t=getchr())==' ' || t=='\t' || t=='\n')
316 				;	/* skip leading white space during arg collection */
317 			putbak(t);
318 		} else
319 			*op++ = t;
320 	}
321 	if (cp!=NULL) {
322 		fprintf(stderr, "m4: unexpected EOF\n");
323 		delexit();
324 	}
325 	okret = 1;
326 	delexit();
327 }
328 
329 catchsig()
330 {
331 	okret = 0;
332 	delexit();
333 }
334 
335 delexit()
336 {
337 	register FILE *fp;
338 	register i, c;
339 
340 	if (!okret) {
341 		signal(SIGHUP, SIG_IGN);
342 		signal(SIGINT, SIG_IGN);
343 	}
344 	for (i=1; i<10; i++) {
345 		if (olist[i]==NULL)
346 			continue;
347 		fclose(olist[i]);
348 		tempname[7] = 'a'+i;
349 		if (okret) {
350 			fp = fopen(tempname, READ);
351 			while ((c = getc(fp)) > 0)
352 				putchar(c);
353 			fclose(fp);
354 		}
355 		unlink(tempname);
356 	}
357 	tempname[7] = 'a';
358 	unlink(tempname);
359 	exit(1-okret);
360 }
361 
362 puttok()
363 {
364 	register char *tp;
365 
366 	tp = token;
367 	if (cp) {
368 		if (op >= &obuf[SAVS]) {
369 			fprintf(stderr, "m4: argument overflow\n");
370 			delexit();
371 		}
372 		while (*tp)
373 			*op++ = *tp++;
374 	} else if (curfile)
375 		while (*tp)
376 			putc(*tp++, curfile);
377 }
378 
379 pbstr(str)
380 register char *str;
381 {
382 	register char *p;
383 
384 	p = str;
385 	while (*p++);
386 	--p;
387 	if (ip >= &ibuf[SAVS]) {
388 		fprintf(stderr, "m4: pushback overflow\n");
389 		delexit();
390 	}
391 	while (p > str)
392 		putbak(*--p);
393 }
394 
395 expand(a1, c)
396 register char **a1;
397 {
398 	register char *dp;
399 	register n;
400 
401 	dp = a1[-1];
402 	if (dp==defloc)
403 		dodef(a1, c);
404 	else if (dp==evaloc)
405 		doeval(a1, c);
406 	else if (dp==inclloc)
407 		doincl(a1, c, 1);
408 	else if (dp==sinclloc)
409 		doincl(a1, c, 0);
410 	else if (dp==makeloc)
411 		domake(a1, c);
412 	else if (dp==syscmdloc)
413 		dosyscmd(a1, c);
414 	else if (dp==incrloc)
415 		doincr(a1, c);
416 	else if (dp==substrloc)
417 		dosubstr(a1, c);
418 	else if (dp==indexloc)
419 		doindex(a1, c);
420 	else if (dp==transloc)
421 		dotransl(a1, c);
422 	else if (dp==ifloc)
423 		doif(a1, c);
424 	else if (dp==divloc)
425 		dodiv(a1, c);
426 	else if (dp==divnumloc)
427 		dodivnum(a1, c);
428 	else if (dp==undivloc)
429 		doundiv(a1, c);
430 	else if (dp==dnlloc)
431 		dodnl(a1, c);
432 	else if (dp==dumploc)
433 		dodump(a1, c);
434 	else if (dp==errploc)
435 		doerrp(a1, c);
436 	else if (dp==lenloc)
437 		dolen(a1, c);
438 	else if (dp==ifdefloc)
439 		doifdef(a1, c);
440 	else if (dp==undefloc)
441 		doundef(a1, c);
442 	else if (dp==shiftloc)
443 		doshift(a1, c);
444 	else if (dp==cqloc)
445 		docq(a1, c);
446 	else {
447 		while (*dp++);
448 		for (dp--; dp>a1[-1]; ) {
449 			if (--dp>a1[-1] && dp[-1]=='$') {
450 				n = *dp-'0';
451 				if (n>=0 && n<=9) {
452 					if (n <= c)
453 						pbstr(a1[n]);
454 					dp--;
455 				} else
456 					putbak(*dp);
457 			} else
458 				putbak(*dp);
459 		}
460 	}
461 }
462 
463 struct nlist *lookup(str)
464 char *str;
465 {
466 	register char *s1, *s2;
467 	register struct nlist *np;
468 	static struct nlist nodef;
469 
470 	s1 = str;
471 	for (hshval = 0; *s1; )
472 		hshval += *s1++;
473 	hshval %= HSHSIZ;
474 	for (np = hshtab[hshval]; np!=NULL; np = np->next) {
475 		s1 = str;
476 		s2 = np->name;
477 		while (*s1++ == *s2)
478 			if (*s2++ == EOS)
479 				return(np);
480 	}
481 	return(&nodef);
482 }
483 
484 char *install(nam, val, flag)
485 char *nam, *val;
486 char flag;
487 {
488 	register struct nlist *np;
489 
490 	if ((np = lookup(nam))->name == NULL) {
491 		np = (struct nlist *)malloc(sizeof(*np));
492 		if (np == NULL) {
493 			fprintf(stderr, "m4: no space for alloc\n");
494 			exit(1);
495 		}
496 		np->name = copy(nam);
497 		np->def = copy(val);
498 		np->next = hshtab[hshval];
499 		np->flag = flag;
500 		hshtab[hshval] = np;
501 		return(np->def);
502 	}
503 	free(np->def);
504 	np->flag = flag;
505 	np->def = copy(val);
506 	return(np->def);
507 }
508 
509 doundef(ap, c)
510 char **ap;
511 {
512 	register struct nlist *np, *tnp;
513 
514 	if (c < 1 || (np = lookup(ap[1]))->name == NULL)
515 		return;
516 	tnp = hshtab[hshval];	/* lookup sets hshval */
517 	if (tnp == np)	/* it's in first place */
518 		hshtab[hshval] = np->next;
519 	else {
520 		for ( ; tnp->next != np; tnp = tnp->next)
521 			;
522 		tnp->next = np->next;
523 	}
524 	/*
525 	 * If this is a reserved word, it has been removed from the
526 	 * hastable.  We do not want to actually free the space because
527 	 * of the code in expand.  Expand wants to to pointer compairs
528 	 * to tell if this is a reserved word (e.g a special action
529 	 * needs to take place).  Thus if we do not free the space,
530 	 * expand will still work, but the name will never be found
531 	 * because it out of the symbol table!
532 	 */
533 	if (np->flag&RESERVED == 0) { /* If not reserved free it */
534 		free(np->name);
535 		free(np->def);
536 		free((char *)np);
537 	}
538 }
539 
540 char *copy(s)
541 register char *s;
542 {
543 	register char *p, *s1;
544 
545 	p = s1 = malloc((unsigned)strlen(s)+1);
546 	if (p == NULL) {
547 		fprintf(stderr, "m4: no space for alloc\n");
548 		exit(1);
549 	}
550 	while (*s1++ = *s++);
551 	return(p);
552 }
553 
554 dodef(ap, c)
555 char **ap;
556 {
557 	if (c >= 2) {
558 		if (strcmp(ap[1], ap[2]) == 0) {
559 			fprintf(stderr, "m4: %s defined as itself\n", ap[1]);
560 			delexit();
561 		}
562 		install(ap[1], ap[2], 0);
563 	}
564 	else if (c == 1)
565 		install(ap[1], "", 0);
566 }
567 
568 doifdef(ap, c)
569 char **ap;
570 {
571 	register struct nlist *np;
572 
573 	if (c < 2)
574 		return;
575 	if (lookup(ap[1])->name != NULL)
576 		pbstr(ap[2]);
577 	else if (c >= 3)
578 		pbstr(ap[3]);
579 }
580 
581 dolen(ap, c)
582 char **ap;
583 {
584 	putnum((long) strlen(ap[1]));
585 }
586 
587 docq(ap, c)
588 char **ap;
589 {
590 	if (c > 1) {
591 		lquote = *ap[1];
592 		rquote = *ap[2];
593 	} else if (c == 1) {
594 		lquote = rquote = *ap[1];
595 	} else {
596 #ifndef M4
597 		lquote = GRAVE;
598 		rquote = ACUTE;
599 #endif
600 #ifdef M4
601 		lquote = LBRAK;
602 		rquote = RBRAK;
603 #endif
604 	}
605 }
606 
607 doshift(ap, c)
608 char **ap;
609 {
610 	fprintf(stderr, "m4: shift not yet implemented\n");
611 }
612 
613 dodump(ap, c)
614 char **ap;
615 {
616 	int i;
617 	register struct nlist *np;
618 
619 	if (c > 0)
620 		while (c--) {
621 			if ((np = lookup(*++ap))->name != NULL)
622 				fprintf(stderr, "`%s'	`%s'\n", np->name, np->def);
623 		}
624 	else
625 		for (i=0; i<HSHSIZ; i++)
626 			for (np=hshtab[i]; np!=NULL; np=np->next)
627 				fprintf(stderr, "`%s'	`%s'\n", np->name, np->def);
628 }
629 
630 doerrp(ap, c)
631 char **ap;
632 {
633 	if (c > 0) {
634 		fprintf(stderr, ap[1], ap[2], ap[3], ap[4], ap[5], ap[6]);
635 		fprintf(stderr, "\n");
636 	}
637 }
638 
639 
640 long	evalval;	/* return value from yacc stuff */
641 char	*pe;	/* used by grammar */
642 
643 doeval(ap, c)
644 char **ap;
645 {
646 
647 	if (c > 0) {
648 		pe = ap[1];
649 		if (yyparse() == 0)
650 			putnum(evalval);
651 		else
652 			fprintf(stderr, "m4: invalid expression in eval: %s\n", ap[1]);
653 	}
654 }
655 
656 doincl(ap, c, noisy)
657 char **ap;
658 {
659 	if (c > 0 && strlen(ap[1]) > 0) {
660 		infptr++;
661 		ip_stk[infptr] = cur_ip = ip;
662 		if ((infile[infptr] = fopen(ap[1], READ))==ERROR) {
663 			if (noisy) {
664 				fprintf(stderr, "m4: file not found: %s\n", ap[1]);
665 				delexit();
666 			}
667 			else
668 				infptr--;
669 		}
670 	}
671 }
672 
673 dosyscmd(ap, c)
674 char **ap;
675 {
676 	if (c > 0)
677 		system(ap[1]);
678 }
679 
680 domake(ap, c)
681 char **ap;
682 {
683 	if (c > 0)
684 		pbstr(mktemp(ap[1]));
685 }
686 
687 doincr(ap, c)
688 char **ap;
689 {
690 	if (c >= 1)
691 		putnum(ctol(ap[1])+1);
692 }
693 
694 putnum(num)
695 long num;
696 {
697 	register sign;
698 
699 	sign = (num < 0) ? '-' : '\0';
700 	if (num < 0)
701 		num = -num;
702 	do {
703 		putbak(num%10+'0');
704 		num = num/10;
705 	} while (num!=0);
706 	if (sign == '-')
707 		putbak('-');
708 }
709 
710 dosubstr(ap, c)
711 char **ap;
712 {
713 	int nc;
714 	register char *sp, *fc;
715 
716 	if (c<2)
717 		return;
718 	if (c<3)
719 		nc = TOKS;
720 	else
721 		nc = ctoi(ap[3]);
722 	fc = ap[1] + max(0, min(ctoi(ap[2]), strlen(ap[1])));
723 	sp = fc + min(nc, strlen(fc));
724 	while (sp > fc)
725 		putbak(*--sp);
726 }
727 
728 doindex(ap, c)
729 char **ap;
730 {
731 	if (c >= 2)
732 		putnum((long) strindex(ap[1], ap[2]));
733 }
734 
735 strindex(p1, p2)
736 char *p1, *p2;
737 {
738 	register m;
739 	register char *s, *t, *p;
740 
741 	for (p=p1; *p; p++) {
742 		s = p;
743 		m = 1;
744 		for (t=p2; *t; )
745 			if (*t++ != *s++)
746 				m = 0;
747 		if (m == 1)
748 			return(p-p1);
749 	}
750 	return(-1);
751 }
752 
753 dotransl(ap, c)
754 char **ap;
755 {
756 	register char *s, *fr, *to;
757 
758 	if (c <= 1) return;
759 
760 	if (c == 2) {
761 		register int i;
762 		to = ap[1];
763 		for (s = ap[1]; *s; s++) {
764 			i = 0;
765 			for (fr = ap[2]; *fr; fr++)
766 				if (*s == *fr) {
767 					i++;
768 					break;
769 				}
770 			if (i == 0)
771 				*to++ = *s;
772 		}
773 		*to = '\0';
774 	}
775 
776 	if (c >= 3) {
777 		for (s = ap[1]; *s; s++)
778 			for (fr = ap[2], to = ap[3]; *fr && *to; fr++, to++)
779 				if (*s == *fr)
780 					*s = *to;
781 	}
782 
783 	pbstr(ap[1]);
784 }
785 
786 doif(ap, c)
787 register char **ap;
788 {
789 	if (c < 3)
790 		return;
791 	while (c >= 3) {
792 		if (strcmp(ap[1], ap[2]) == 0) {
793 			pbstr(ap[3]);
794 			return;
795 		}
796 		c -= 3;
797 		ap += 3;
798 	}
799 	if (c > 0)
800 		pbstr(ap[1]);
801 }
802 
803 dodiv(ap, c)
804 register char **ap;
805 {
806 	register int f;
807 
808 	if (c<1)
809 		f = 0;
810 	else
811 		f = ctoi(ap[1]);
812 	if (f>=10 || f<0) {
813 		curfile = NULL;
814 		return;
815 	}
816 	tempname[7] = 'a' + f;
817 	if (olist[f] || (olist[f]=fopen(tempname, WRITE))) {
818 		curout = f;
819 		curfile = olist[f];
820 	}
821 }
822 
823 doundiv(ap, c)
824 char **ap;
825 {
826 	register FILE *fp;
827 	register int i, ch;
828 	int j;
829 
830 	if (c == 0) {
831 		for (i=1; i<10; i++) {
832 			if (i==curout || olist[i]==NULL)
833 				continue;
834 			fclose(olist[i]);
835 			tempname[7] = 'a'+i;
836 			fp = fopen(tempname, READ);
837 			if (curfile != NULL)
838 				while ((ch = getc(fp)) > 0)
839 					putc(ch, curfile);
840 			fclose(fp);
841 			unlink(tempname);
842 			olist[i] = NULL;
843 		}
844 
845 	}
846 	else {
847 		for (j = 1; j <= c; j++) {
848 			i = ctoi(*++ap);
849 			if (i<1 || i>9 || i==curout || olist[i]==NULL)
850 				continue;
851 			fclose(olist[i]);
852 			tempname[7] = 'a'+i;
853 			fp = fopen(tempname, READ);
854 			if (curfile != NULL)
855 				while ((ch = getc(fp)) > 0)
856 					putc(ch, curfile);
857 			fclose(fp);
858 			unlink(tempname);
859 			olist[i] = NULL;
860 		}
861 	}
862 }
863 
864 dodivnum(ap, c)
865 char **ap;
866 {
867 	putnum((long) curout);
868 }
869 
870 dodnl(ap, c)
871 char **ap;
872 {
873 	register t;
874 
875 	while ((t=getchr())!='\n' && t>=0)
876 		;
877 }
878 
879 long ctol(str)
880 register char *str;
881 {
882 	register sign;
883 	long num;
884 
885 	while (*str==' ' || *str=='\t' || *str=='\n')
886 		str++;
887 	num = 0;
888 	if (*str == '-') {
889 		sign = -1;
890 		str++;
891 	}
892 	else
893 		sign = 1;
894 	while (*str>='0' && *str<='9')
895 		num = num*10 + *str++ - '0';
896 	return(sign * num);
897 }
898 
899 ctoi(s)
900 char *s;
901 {
902 	return(ctol(s));
903 }
904 
905 min(a, b)
906 {
907 	if (a>b)
908 		return(b);
909 	return(a);
910 }
911 
912 max(a, b)
913 {
914 	if (a>b)
915 		return(a);
916 	return(b);
917 }
918