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