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