xref: /illumos-gate/usr/src/cmd/sgs/m4/common/m4.c (revision 1dd08564)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1988 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include	<signal.h>
33 #include	<unistd.h>
34 #include	<fcntl.h>
35 #include	"m4.h"
36 
37 #if defined(__lint)
38 extern int yydebug;
39 #endif
40 
41 #define	match(c, s)	(c == *s && (!s[1] || inpmatch(s+1)))
42 
43 static char	tmp_name[] = "/tmp/m4aXXXXX";
44 static wchar_t	prev_char;
45 static int mb_cur_max;
46 
47 static void getflags(int *, char ***, int *);
48 static void initalloc(void);
49 static void expand(wchar_t **, int);
50 static void lnsync(FILE *);
51 static void fpath(FILE *);
52 static void puttok(wchar_t *);
53 static void error3(void);
54 static wchar_t itochr(int);
55 /*LINTED: E_STATIC_UNUSED*/
56 static wchar_t *chkbltin(wchar_t *);
57 static wchar_t *inpmatch(wchar_t *);
58 static void chkspace(char **, int *, char ***);
59 static void catchsig(int);
60 static FILE *m4open(char ***, char *, int *);
61 static void showwrap(void);
62 static void sputchr(wchar_t, FILE *);
63 static void putchr(wchar_t);
64 static void *xcalloc(size_t, size_t);
65 static wint_t myfgetwc(FILE *, int);
66 static wint_t myfputwc(wchar_t, FILE *);
67 static int myfeof(int);
68 
69 int
70 main(int argc, char **argv)
71 {
72 	wchar_t t;
73 	int i, opt_end = 0;
74 	int sigs[] = {SIGHUP, SIGINT, SIGPIPE, 0};
75 
76 #if defined(__lint)
77 	yydebug = 0;
78 #endif
79 
80 	for (i = 0; sigs[i]; ++i) {
81 		if (signal(sigs[i], SIG_IGN) != SIG_IGN)
82 			(void) signal(sigs[i], catchsig);
83 	}
84 	tempfile = mktemp(tmp_name);
85 	(void) close(creat(tempfile, 0));
86 
87 	(void) setlocale(LC_ALL, "");
88 
89 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
90 #define	TEXT_DOMAIN "SYS_TEST"
91 #endif
92 	(void) textdomain(TEXT_DOMAIN);
93 
94 	if ((mb_cur_max = MB_CUR_MAX) > 1)
95 		wide = 1;
96 
97 	procnam = argv[0];
98 	getflags(&argc, &argv, &opt_end);
99 	initalloc();
100 
101 	setfname("-");
102 	if (argc > 1) {
103 		--argc;
104 		++argv;
105 		if (strcmp(argv[0], "-")) {
106 			ifile[ifx] = m4open(&argv, "r", &argc);
107 			setfname(argv[0]);
108 		}
109 	}
110 
111 	for (;;) {
112 		token[0] = t = getchr();
113 		token[1] = EOS;
114 
115 		if (t == WEOF) {
116 			if (ifx > 0) {
117 				(void) fclose(ifile[ifx]);
118 				ipflr = ipstk[--ifx];
119 				continue;
120 			}
121 
122 			getflags(&argc, &argv, &opt_end);
123 
124 			if (argc <= 1)
125 				/*
126 				 * If dowrap() has been called, the m4wrap
127 				 * macro has been processed, and a linked
128 				 * list of m4wrap strings has been created.
129 				 * The list starts at wrapstart.
130 				 */
131 				if (wrapstart) {
132 					/*
133 					 * Now that EOF has been processed,
134 					 * display the m4wrap strings.
135 					 */
136 					showwrap();
137 					continue;
138 				} else
139 					break;
140 			--argc;
141 			++argv;
142 
143 			if (ifile[ifx] != stdin)
144 				(void) fclose(ifile[ifx]);
145 
146 			if (strcmp(argv[0], "-"))
147 				ifile[ifx] = m4open(&argv, "r", &argc);
148 			else
149 				ifile[ifx] = stdin;
150 
151 			setfname(argv[0]);
152 			continue;
153 		}
154 
155 		if (is_alpha(t) || t == '_') {
156 			wchar_t	*tp = token+1;
157 			int tlim = toksize;
158 			struct nlist	*macadd;  /* temp variable */
159 
160 			while ((*tp = getchr()) != WEOF &&
161 			    (is_alnum(*tp) || *tp == '_')) {
162 				tp++;
163 				if (--tlim <= 0)
164 					error2(gettext(
165 					    "more than %d chars in word"),
166 					    toksize);
167 			}
168 			putbak(*tp);
169 			*tp = EOS;
170 
171 			macadd = lookup(token);
172 			*Ap = (wchar_t *)macadd;
173 			if (macadd->def) {
174 				if ((wchar_t *)(++Ap) >= astklm) {
175 					--Ap;
176 					error2(gettext(
177 					    "more than %d items on "
178 					    "argument stack"),
179 					    stksize);
180 				}
181 
182 				if (Cp++ == NULL)
183 					Cp = callst;
184 
185 				Cp->argp = Ap;
186 				*Ap++ = op;
187 				puttok(token);
188 				stkchr(EOS);
189 				t = getchr();
190 				putbak(t);
191 
192 				if (t != '(')
193 					pbstr(L"()");
194 				else	/* try to fix arg count */
195 					*Ap++ = op;
196 
197 				Cp->plev = 0;
198 			} else {
199 				puttok(token);
200 			}
201 		} else if (match(t, lquote)) {
202 			int	qlev = 1;
203 
204 			for (;;) {
205 				token[0] = t = getchr();
206 				token[1] = EOS;
207 
208 				if (match(t, rquote)) {
209 					if (--qlev > 0)
210 						puttok(token);
211 					else
212 						break;
213 				} else if (match(t, lquote)) {
214 					++qlev;
215 					puttok(token);
216 				} else {
217 					if (t == WEOF)
218 						error(gettext(
219 						"EOF in quote"));
220 					putchr(t);
221 				}
222 			}
223 		} else if (match(t, lcom) &&
224 		    ((lcom[0] != L'#' || lcom[1] != L'\0') ||
225 		    prev_char != '$')) {
226 
227 			/*
228 			 * Don't expand commented macro (between lcom and
229 			 * rcom).
230 			 * What we know so far is that we have found the
231 			 * left comment char (lcom).
232 			 * Make sure we haven't found '#' (lcom) immediately
233 			 * preceded by '$' because we want to expand "$#".
234 			 */
235 
236 			puttok(token);
237 			for (;;) {
238 				token[0] = t = getchr();
239 				token[1] = EOS;
240 				if (match(t, rcom)) {
241 					puttok(token);
242 					break;
243 				} else {
244 					if (t == WEOF)
245 						error(gettext(
246 						"EOF in comment"));
247 					putchr(t);
248 				}
249 			}
250 		} else if (Cp == NULL) {
251 			putchr(t);
252 		} else if (t == '(') {
253 			if (Cp->plev)
254 				stkchr(t);
255 			else {
256 				/* skip white before arg */
257 				while ((t = getchr()) != WEOF && is_space(t))
258 					;
259 
260 				putbak(t);
261 			}
262 
263 			++Cp->plev;
264 		} else if (t == ')') {
265 			--Cp->plev;
266 
267 			if (Cp->plev == 0) {
268 				stkchr(EOS);
269 				expand(Cp->argp, Ap-Cp->argp-1);
270 				op = *Cp->argp;
271 				Ap = Cp->argp-1;
272 
273 				if (--Cp < callst)
274 					Cp = NULL;
275 			} else
276 				stkchr(t);
277 		} else if (t == ',' && Cp->plev <= 1) {
278 			stkchr(EOS);
279 			*Ap = op;
280 
281 			if ((wchar_t *)(++Ap) >= astklm) {
282 				--Ap;
283 				error2(gettext(
284 				    "more than %d items on argument stack"),
285 				    stksize);
286 			}
287 
288 			while ((t = getchr()) != WEOF && is_space(t))
289 				;
290 
291 			putbak(t);
292 		} else {
293 			stkchr(t);
294 		}
295 	}
296 
297 	if (Cp != NULL)
298 		error(gettext(
299 		"EOF in argument list"));
300 
301 	delexit(exitstat, 1);
302 	return (0);
303 }
304 
305 static wchar_t *
306 inpmatch(wchar_t *s)
307 {
308 	wchar_t	*tp = token+1;
309 
310 	while (*s) {
311 		*tp = getchr();
312 
313 		if (*tp++ != *s++) {
314 			*tp = EOS;
315 			pbstr(token+1);
316 			return (0);
317 		}
318 	}
319 
320 	*tp = EOS;
321 	return (token);
322 }
323 
324 static void
325 getflags(int *xargc, char ***xargv, int *option_end)
326 {
327 	char	*arg;
328 	char *t;
329 	wchar_t *s[3];
330 
331 	while (*xargc > 1) {
332 		arg = (*xargv)[1]; /* point arg to current argument */
333 
334 		/*
335 		 * This argument is not an option if it equals "-" or if
336 		 * "--" has already been parsed.
337 		 */
338 		if (arg[0] != '-' || arg[1] == EOS || *option_end)
339 			break;
340 		if (arg[0] == '-' && arg[1] == '-' && arg[2] == '\0') {
341 			*option_end = 1;
342 		} else {
343 			switch (arg[1]) {
344 			case 'B':
345 				chkspace(&arg, xargc, xargv);
346 				bufsize = atoi(&arg[2]);
347 				break;
348 			case 'D':
349 				initalloc();
350 				chkspace(&arg, xargc, xargv);
351 				for (t = &arg[2]; *t; t++) {
352 					if (*t == '=') {
353 						*t++ = EOS;
354 						break;
355 					}
356 				}
357 				s[1] = str2wstr(&arg[2], 1);
358 				s[2] = str2wstr(t, 1);
359 				dodef(&s[0], 2);
360 				free(s[1]);
361 				free(s[2]);
362 				break;
363 			case 'H':
364 				chkspace(&arg, xargc, xargv);
365 				hshsize = atoi(&arg[2]);
366 				break;
367 			case 'S':
368 				chkspace(&arg, xargc, xargv);
369 				stksize = atoi(&arg[2]);
370 				break;
371 			case 'T':
372 				chkspace(&arg, xargc, xargv);
373 				toksize = atoi(&arg[2]);
374 				break;
375 			case 'U':
376 				initalloc();
377 				chkspace(&arg, xargc, xargv);
378 				s[1] = str2wstr(&arg[2], 1);
379 				doundef(&s[0], 1);
380 				free(s[1]);
381 				break;
382 			case 'e':
383 				setbuf(stdout, NULL);
384 				(void) signal(SIGINT, SIG_IGN);
385 				break;
386 			case 's':
387 				/* turn on line sync */
388 				sflag = 1;
389 				break;
390 			default:
391 				(void) fprintf(stderr,
392 				    gettext("%s: bad option: %s\n"),
393 				    procnam, arg);
394 				delexit(NOT_OK, 0);
395 			}
396 		} /* end else not "--" */
397 
398 		(*xargv)++;
399 		--(*xargc);
400 	} /* end while options to process */
401 }
402 
403 /*
404  * Function: chkspace
405  *
406  * If there is a space between the option and its argument,
407  * adjust argptr so that &arg[2] will point to beginning of the option argument.
408  * This will ensure that processing in getflags() will work, because &arg[2]
409  * will point to the beginning of the option argument whether or not we have
410  * a space between the option and its argument.  If there is a space between
411  * the option and its argument, also adjust xargv and xargc because we are
412  * processing the next argument.
413  */
414 static void
415 chkspace(char **argptr, int *xargc, char ***xargv)
416 {
417 	if ((*argptr)[2] == EOS) {
418 		/* there is a space between the option and its argument */
419 		(*xargv)++; /* look at the next argument */
420 		--(*xargc);
421 		/*
422 		 * Adjust argptr if the option is followed by an
423 		 * option argument.
424 		 */
425 		if (*xargc > 1) {
426 			*argptr = (*xargv)[1];
427 			/* point &arg[2] to beginning of option argument */
428 			*argptr -= 2;
429 		}
430 	}
431 }
432 
433 static void
434 initalloc(void)
435 {
436 	static int done = 0;
437 	int	t;
438 
439 	if (done++)
440 		return;
441 
442 	hshtab = xcalloc(hshsize, sizeof (struct nlist *));
443 	callst = xcalloc(stksize/3+1, sizeof (struct call));
444 	Ap = argstk = xcalloc(stksize+3, sizeof (wchar_t *));
445 	ipstk[0] = ipflr = ip = ibuf = xcalloc(bufsize+1, sizeof (wchar_t));
446 	op = obuf = xcalloc(bufsize+1, sizeof (wchar_t));
447 	token = xcalloc(toksize+1, sizeof (wchar_t));
448 
449 	astklm = (wchar_t *)(&argstk[stksize]);
450 	ibuflm = &ibuf[bufsize];
451 	obuflm = &obuf[bufsize];
452 	toklm = &token[toksize];
453 
454 	for (t = 0; barray[t].bname; ++t) {
455 		wchar_t	p[2] = {0, EOS};
456 
457 		p[0] = builtin(t);
458 		install(barray[t].bname, p, NOPUSH);
459 	}
460 	install(L"unix", nullstr, NOPUSH);
461 }
462 
463 void
464 install(wchar_t *nam, wchar_t *val, int mode)
465 {
466 	struct nlist *np;
467 	wchar_t	*cp;
468 	int		l;
469 
470 	if (mode == PUSH)
471 		(void) lookup(nam);	/* lookup sets hshval */
472 	else
473 		while (undef(nam))	/* undef calls lookup */
474 			;
475 
476 	np = xcalloc(1, sizeof (*np));
477 	np->name = wstrdup(nam);
478 	np->next = hshtab[hshval];
479 	hshtab[hshval] = np;
480 
481 	cp = xcalloc((l = wcslen(val))+1, sizeof (*val));
482 	np->def = cp;
483 	cp = &cp[l];
484 
485 	while (*val)
486 		*--cp = *val++;
487 }
488 
489 struct nlist *
490 lookup(wchar_t *str)
491 {
492 	wchar_t	*s1;
493 	struct nlist	*np;
494 	static struct nlist	nodef;
495 
496 	s1 = str;
497 
498 	for (hshval = 0; *s1; )
499 		hshval += *s1++;
500 
501 	hshval %= hshsize;
502 
503 	for (np = hshtab[hshval]; np != NULL; np = np->next) {
504 		if (*str == *np->name && wcscmp(str, np->name) == 0)
505 			return (np);
506 	}
507 	return (&nodef);
508 }
509 
510 static void
511 expand(wchar_t **a1, int c)
512 {
513 	wchar_t	*dp;
514 	struct nlist	*sp;
515 
516 	sp = (struct nlist *)a1[-1];
517 
518 	if (sp->tflag || trace) {
519 #if !defined(__lint)	/* lint doesn't grok "%ws" */
520 		int	i;
521 
522 		(void) fprintf(stderr,
523 		    "Trace(%d): %ws", Cp-callst, a1[0]);
524 #endif
525 
526 		if (c > 0) {
527 #if !defined(__lint)	/* lint doesn't grok "%ws" */
528 			(void) fprintf(stderr, "(%ws", chkbltin(a1[1]));
529 			for (i = 2; i <= c; ++i)
530 				(void) fprintf(stderr, ",%ws", chkbltin(a1[i]));
531 #endif
532 			(void) fprintf(stderr, ")");
533 		}
534 		(void) fprintf(stderr, "\n");
535 	}
536 
537 	dp = sp->def;
538 
539 	for (; *dp; ++dp) {
540 		if (is_builtin(*dp)) {
541 			(*barray[builtin_idx(*dp)].bfunc)(a1, c);
542 		} else if (dp[1] == '$') {
543 			if (is_digit(*dp)) {
544 				int	n;
545 				if ((n = *dp-'0') <= c)
546 					pbstr(a1[n]);
547 				++dp;
548 			} else if (*dp == '#') {
549 				pbnum((long)c);
550 				++dp;
551 			} else if (*dp == '*' || *dp == '@') {
552 				int i = c;
553 				wchar_t **a = a1;
554 
555 				if (i > 0)
556 					for (;;) {
557 						if (*dp == '@')
558 							pbstr(rquote);
559 
560 						pbstr(a[i--]);
561 
562 						if (*dp == '@')
563 							pbstr(lquote);
564 
565 						if (i <= 0)
566 						break;
567 
568 						pbstr(L",");
569 					}
570 				++dp;
571 			} else
572 				putbak(*dp);
573 		} else
574 			putbak(*dp);
575 	}
576 }
577 
578 void
579 setfname(char *s)
580 {
581 	if (fname[ifx])
582 		free(fname[ifx]);
583 	if ((fname[ifx] = strdup(s)) == NULL)
584 		error(gettext("out of storage"));
585 	fline[ifx] = 1;
586 	nflag = 1;
587 	lnsync(stdout);
588 }
589 
590 static void
591 lnsync(FILE *iop)
592 {
593 	static int cline = 0;
594 	static int cfile = 0;
595 
596 	if (!sflag || iop != stdout)
597 		return;
598 
599 	if (nflag || ifx != cfile) {
600 		nflag = 0;
601 		cfile = ifx;
602 		(void) fprintf(iop, "#line %d \"", cline = fline[ifx]);
603 		fpath(iop);
604 		(void) fprintf(iop, "\"\n");
605 	} else if (++cline != fline[ifx])
606 		(void) fprintf(iop, "#line %d\n", cline = fline[ifx]);
607 }
608 
609 static void
610 fpath(FILE *iop)
611 {
612 	int	i;
613 
614 	if (fname[0] == NULL)
615 		return;
616 
617 	(void) fprintf(iop, "%s", fname[0]);
618 
619 	for (i = 1; i <= ifx; ++i)
620 		(void) fprintf(iop, ":%s", fname[i]);
621 }
622 
623 /* ARGSUSED */
624 static void
625 catchsig(int i)
626 {
627 	(void) signal(SIGHUP, SIG_IGN);
628 	(void) signal(SIGINT, SIG_IGN);
629 	delexit(NOT_OK, 0);
630 }
631 
632 void
633 delexit(int code, int flushio)
634 {
635 	int i;
636 
637 	cf = stdout;
638 
639 /*
640  *	if (ofx != 0) {
641  *		ofx = 0;
642  *		code = NOT_OK;
643  *	}
644  */
645 	ofx = 0;	/* ensure that everything comes out */
646 	for (i = 1; i < 10; i++)
647 		undiv(i, code);
648 
649 	tempfile[7] = 'a';
650 	(void) unlink(tempfile);
651 
652 	/* flush standard I/O buffers, ie: call exit() not _exit() */
653 	if (flushio)
654 		exit(code);
655 
656 	_exit(code);
657 }
658 
659 static void
660 puttok(wchar_t *tp)
661 {
662 	if (Cp) {
663 		while (*tp)
664 			stkchr(*tp++);
665 	} else if (cf) {
666 		while (*tp) {
667 			sputchr(*tp++, cf);
668 		}
669 	}
670 }
671 
672 void
673 pbstr(wchar_t *str)
674 {
675 	wchar_t *p;
676 
677 	for (p = str + wcslen(str); --p >= str; )
678 		putbak(*p);
679 }
680 
681 void
682 undiv(int i, int code)
683 {
684 	FILE *fp;
685 	wint_t c;
686 
687 	if (i < 1 || i > 9 || i == ofx || !ofile[i])
688 		return;
689 
690 	(void) fclose(ofile[i]);
691 	tempfile[7] = 'a'+i;
692 
693 	if (code == OK && cf) {
694 		fp = xfopen(tempfile, "r");
695 
696 		if (wide) {
697 			while ((c = myfgetwc(fp, -1)) != WEOF)
698 				sputchr((wchar_t)c, cf);
699 		} else {
700 			while ((c = (wint_t)getc(fp)) != WEOF)
701 				sputchr((wchar_t)c, cf);
702 		}
703 
704 		(void) fclose(fp);
705 	}
706 
707 	(void) unlink(tempfile);
708 	ofile[i] = NULL;
709 }
710 
711 void
712 pbnum(long num)
713 {
714 	pbnbr(num, 10, 1);
715 }
716 
717 void
718 pbnbr(long nbr, int base, int len)
719 {
720 	int	neg = 0;
721 
722 	if (base <= 0)
723 		return;
724 
725 	if (nbr < 0)
726 		neg = 1;
727 	else
728 		nbr = -nbr;
729 
730 	while (nbr < 0) {
731 		int	i;
732 		if (base > 1) {
733 			i = nbr%base;
734 			nbr /= base;
735 #if (-3 % 2) != -1
736 			while (i > 0) {
737 				i -= base;
738 				++nbr;
739 			}
740 #endif
741 			i = -i;
742 		} else {
743 			i = 1;
744 			++nbr;
745 		}
746 		putbak(itochr(i));
747 		--len;
748 	}
749 
750 	while (--len >= 0)
751 		putbak('0');
752 
753 	if (neg)
754 		putbak('-');
755 }
756 
757 static wchar_t
758 itochr(int i)
759 {
760 	if (i > 9)
761 		return ((wchar_t)(i-10+'A'));
762 	else
763 		return ((wchar_t)(i+'0'));
764 }
765 
766 long
767 ctol(wchar_t *str)
768 {
769 	int sign;
770 	long num;
771 
772 	while (is_space(*str))
773 		++str;
774 	num = 0;
775 	if (*str == '-') {
776 		sign = -1;
777 		++str;
778 	} else
779 		sign = 1;
780 	while (is_digit(*str))
781 		num = num*10 + *str++ - '0';
782 	return (sign * num);
783 }
784 
785 int
786 min(int a, int b)
787 {
788 	if (a > b)
789 		return (b);
790 	return (a);
791 }
792 
793 FILE *
794 xfopen(char *name, char *mode)
795 {
796 	FILE	*fp;
797 
798 	if ((fp = fopen(name, mode)) == NULL)
799 		error(gettext("can't open file"));
800 
801 	return (fp);
802 }
803 
804 /*
805  * m4open
806  *
807  * Continue processing files when unable to open the given file argument.
808  */
809 FILE *
810 m4open(char ***argvec, char *mode, int *argcnt)
811 {
812 	FILE	*fp;
813 	char *arg;
814 
815 	while (*argcnt > 0) {
816 		arg = (*argvec)[0]; /* point arg to current file name */
817 		if (arg[0] == '-' && arg[1] == EOS)
818 			return (stdin);
819 		else {
820 			if ((fp = fopen(arg, mode)) == NULL) {
821 				(void) fprintf(stderr, gettext(
822 				"m4: cannot open %s: "), arg);
823 				perror("");
824 				if (*argcnt == 1) {
825 					/* last arg therefore exit */
826 					error3();
827 				} else {
828 					exitstat = 1;
829 					(*argvec)++; /* try next arg */
830 					(*argcnt)--;
831 				}
832 			} else
833 				break;
834 		}
835 	}
836 	return (fp);
837 }
838 
839 void *
840 xmalloc(size_t size)
841 {
842 	void *ptr;
843 
844 	if ((ptr = malloc(size)) == NULL)
845 		error(gettext("out of storage"));
846 	return (ptr);
847 }
848 
849 static void *
850 xcalloc(size_t nbr, size_t size)
851 {
852 	void	*ptr;
853 
854 	ptr = xmalloc(nbr * size);
855 	(void) memset(ptr, '\0', nbr * size);
856 	return (ptr);
857 }
858 
859 /* PRINTFLIKE1 */
860 void
861 error2(char *str, int num)
862 {
863 	char buf[500];
864 
865 	(void) snprintf(buf, sizeof (buf), str, num);
866 	error(buf);
867 }
868 
869 void
870 error(char *str)
871 {
872 	(void) fprintf(stderr, "\n%s:", procnam);
873 	fpath(stderr);
874 	(void) fprintf(stderr, ":%d %s\n", fline[ifx], str);
875 	error3();
876 }
877 
878 static void
879 error3()
880 {
881 	if (Cp) {
882 		struct call	*mptr;
883 
884 		/* fix limit */
885 		*op = EOS;
886 		(Cp+1)->argp = Ap+1;
887 
888 		for (mptr = callst; mptr <= Cp; ++mptr) {
889 			wchar_t	**aptr, **lim;
890 
891 			aptr = mptr->argp;
892 			lim = (mptr+1)->argp-1;
893 			if (mptr == callst)
894 				(void) fputws(*aptr, stderr);
895 			++aptr;
896 			(void) fputs("(", stderr);
897 			if (aptr < lim)
898 				for (;;) {
899 					(void) fputws(*aptr++, stderr);
900 					if (aptr >= lim)
901 						break;
902 					(void) fputs(",", stderr);
903 				}
904 		}
905 		while (--mptr >= callst)
906 			(void) fputs(")", stderr);
907 
908 		(void) fputs("\n", stderr);
909 	}
910 	delexit(NOT_OK, 1);
911 }
912 
913 static wchar_t *
914 chkbltin(wchar_t *s)
915 {
916 	static wchar_t buf[24];
917 
918 	if (is_builtin(*s)) {
919 		(void) swprintf(buf, sizeof (buf)/sizeof (wchar_t), L"<%ls>",
920 		    barray[builtin_idx(*s)].bname);
921 		return (buf);
922 	}
923 	return (s);
924 }
925 
926 wchar_t
927 getchr()
928 {
929 	static wchar_t C;
930 
931 	prev_char = C;
932 	if (ip > ipflr)
933 		return (*--ip);
934 	if (wide) {
935 		C = (wchar_t)(myfeof(ifx) ? WEOF : myfgetwc(NULL, ifx));
936 	} else {
937 		C = (wchar_t)(feof(ifile[ifx]) ?
938 		    WEOF : (wint_t)getc(ifile[ifx]));
939 	}
940 	if (C == '\n')
941 		fline[ifx]++;
942 	return (C);
943 }
944 
945 /*
946  * showwrap
947  *
948  * Loop through the list of m4wrap strings.  Call pbstr() so that the
949  * string will be displayed, then delete the list entry and free the memory
950  * allocated for it.
951  */
952 static void
953 showwrap()
954 {
955 	struct Wrap *prev;
956 
957 	while (wrapstart) {
958 		pbstr(wrapstart->wrapstr);
959 		free(wrapstart->wrapstr);
960 		prev = wrapstart;
961 		wrapstart = wrapstart->nxt;
962 		free(prev);
963 	}
964 }
965 
966 static void
967 sputchr(wchar_t c, FILE *f)
968 {
969 	wint_t ret;
970 
971 	if (is_builtin(c))
972 		return;
973 	if (wide)
974 		ret = myfputwc(c, f);
975 	else
976 		ret = (wint_t)putc((int)c, f);
977 	if (ret == WEOF)
978 		error(gettext("output error"));
979 	if (ret == '\n')
980 		lnsync(f);
981 }
982 
983 static void
984 putchr(wchar_t c)
985 {
986 	wint_t ret;
987 
988 	if (Cp)
989 		stkchr(c);
990 	else if (cf) {
991 		if (sflag)
992 			sputchr(c, cf);
993 		else {
994 			if (is_builtin(c))
995 				return;
996 			if (wide)
997 				ret = myfputwc(c, cf);
998 			else
999 				ret = (wint_t)putc((int)c, cf);
1000 			if (ret == WEOF) {
1001 				error(gettext("output error"));
1002 			}
1003 		}
1004 	}
1005 }
1006 
1007 wchar_t *
1008 wstrdup(wchar_t *p)
1009 {
1010 	size_t len = wcslen(p);
1011 	wchar_t *ret;
1012 
1013 	ret = xmalloc((len + 1) * sizeof (wchar_t));
1014 	(void) wcscpy(ret, p);
1015 	return (ret);
1016 }
1017 
1018 int
1019 wstoi(wchar_t *p)
1020 {
1021 	return ((int)wcstol(p, NULL, 10));
1022 }
1023 
1024 char *
1025 wstr2str(wchar_t *from, int alloc)
1026 {
1027 	static char *retbuf;
1028 	static size_t bsiz;
1029 	char *p, *ret;
1030 
1031 	if (alloc) {
1032 		ret = p = xmalloc(wcslen(from) * mb_cur_max + 1);
1033 	} else {
1034 		while (bsiz < (wcslen(from) * mb_cur_max + 1)) {
1035 			if ((p = realloc(retbuf, bsiz + 256)) == NULL)
1036 				error(gettext("out of storage"));
1037 			bsiz += 256;
1038 			retbuf = p;
1039 		}
1040 		ret = p = retbuf;
1041 	}
1042 
1043 	if (wide) {
1044 		while (*from) {
1045 			int len;
1046 
1047 			if (*from & INVALID_CHAR) {
1048 				*p = (char)(*from & ~INVALID_CHAR);
1049 				len = 1;
1050 			} else {
1051 				if ((len = wctomb(p, *from)) == -1) {
1052 					*p = (char)*from;
1053 					len = 1;
1054 				}
1055 			}
1056 			p += len;
1057 			from++;
1058 		}
1059 	} else {
1060 		while (*from)
1061 			*p++ = (char)*from++;
1062 	}
1063 	*p = '\0';
1064 
1065 	return (ret);
1066 }
1067 
1068 wchar_t *
1069 str2wstr(char *from, int alloc)
1070 {
1071 	static wchar_t *retbuf;
1072 	static size_t bsiz;
1073 	wchar_t *p, *ret;
1074 
1075 	if (alloc) {
1076 		ret = p = xmalloc((strlen(from) + 1) * sizeof (wchar_t));
1077 	} else {
1078 		while (bsiz < (strlen(from) + 1)) {
1079 			if ((p = realloc(retbuf,
1080 			    (bsiz + 256) * sizeof (wchar_t))) == NULL) {
1081 				error(gettext("out of storage"));
1082 			}
1083 			bsiz += 256;
1084 			retbuf = p;
1085 		}
1086 		ret = p = retbuf;
1087 	}
1088 
1089 	if (wide) {
1090 		while (*from) {
1091 			int len;
1092 			wchar_t wc;
1093 
1094 			if ((len = mbtowc(&wc, from, mb_cur_max)) <= 0) {
1095 				wc = *from | INVALID_CHAR;
1096 				len = 1;
1097 			}
1098 			*p++ = wc;
1099 			from += len;
1100 		}
1101 	} else {
1102 		while (*from)
1103 			*p++ = (unsigned char) *from++;
1104 	}
1105 	*p = 0;
1106 
1107 	return (ret);
1108 }
1109 
1110 static wint_t
1111 myfgetwc(FILE *fp, int idx)
1112 {
1113 	int i, c, len, nb;
1114 	wchar_t wc;
1115 	unsigned char *buf;
1116 
1117 	if (fp == NULL)
1118 		fp = ifile[idx];
1119 	else
1120 		idx = 10; /* extra slot */
1121 	buf = ibuffer[idx].buffer;
1122 	nb = ibuffer[idx].nbytes;
1123 	len = 0;
1124 	for (i = 1; i <= mb_cur_max; i++) {
1125 		if (nb < i) {
1126 			c = getc(fp);
1127 			if (c == EOF) {
1128 				if (nb == 0)
1129 					return (WEOF);
1130 				else
1131 					break;
1132 			}
1133 			buf[nb++] = (unsigned char)c;
1134 		}
1135 		if ((len = mbtowc(&wc, (char *)buf, i)) >= 0)
1136 			break;
1137 	}
1138 	if (len <= 0) {
1139 		wc = buf[0] | INVALID_CHAR;
1140 		len = 1;
1141 	}
1142 	nb -= len;
1143 	if (nb > 0) {
1144 		for (i = 0; i < nb; i++)
1145 			buf[i] = buf[i + len];
1146 	}
1147 	ibuffer[idx].nbytes = nb;
1148 	return (wc);
1149 }
1150 
1151 static wint_t
1152 myfputwc(wchar_t wc, FILE *fp)
1153 {
1154 	if (wc & INVALID_CHAR) {
1155 		wc &= ~INVALID_CHAR;
1156 		return (fputc((int)wc, fp));
1157 	}
1158 	return (fputwc(wc, fp));
1159 }
1160 
1161 static int
1162 myfeof(int idx)
1163 {
1164 	return (ibuffer[idx].nbytes == 0 && feof(ifile[idx]));
1165 }
1166