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