xref: /titanic_52/usr/src/cmd/sgs/m4/common/m4.c (revision 1a7c1b724419d3cb5fa6eea75123c6b2060ba31b)
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 /*	Copyright (c) 1988 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2002-2003 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
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 main(argc, argv)
66 int	argc;
67 char 	**argv;
68 {
69 	wchar_t t;
70 	int i, opt_end = 0;
71 	int sigs[] = {SIGHUP, SIGINT, SIGPIPE, 0};
72 
73 	for (i = 0; sigs[i]; ++i) {
74 		if (signal(sigs[i], SIG_IGN) != SIG_IGN)
75 			(void) signal(sigs[i], catchsig);
76 	}
77 	tempfile = mktemp(tmp_name);
78 	(void) close(creat(tempfile, 0));
79 
80 	(void) setlocale(LC_ALL, "");
81 
82 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
83 #define	TEXT_DOMAIN "SYS_TEST"
84 #endif
85 	(void) textdomain(TEXT_DOMAIN);
86 
87 	if ((mb_cur_max = MB_CUR_MAX) > 1)
88 		wide = 1;
89 
90 	procnam = argv[0];
91 	getflags(&argc, &argv, &opt_end);
92 	initalloc();
93 
94 	setfname("-");
95 	if (argc > 1) {
96 		--argc;
97 		++argv;
98 		if (strcmp(argv[0], "-")) {
99 			ifile[ifx] = m4open(&argv, "r", &argc);
100 			setfname(argv[0]);
101 		}
102 	}
103 
104 	for (;;) {
105 		token[0] = t = getchr();
106 		token[1] = EOS;
107 
108 		if (t == WEOF) {
109 			if (ifx > 0) {
110 				(void) fclose(ifile[ifx]);
111 				ipflr = ipstk[--ifx];
112 				continue;
113 			}
114 
115 			getflags(&argc, &argv, &opt_end);
116 
117 			if (argc <= 1)
118 				/*
119 				 * If dowrap() has been called, the m4wrap
120 				 * macro has been processed, and a linked
121 				 * list of m4wrap strings has been created.
122 				 * The list starts at wrapstart.
123 				 */
124 				if (wrapstart) {
125 					/*
126 					 * Now that EOF has been processed,
127 					 * display the m4wrap strings.
128 					 */
129 					showwrap();
130 					continue;
131 				} else
132 					break;
133 			--argc;
134 			++argv;
135 
136 			if (ifile[ifx] != stdin)
137 				(void) fclose(ifile[ifx]);
138 
139 			if (strcmp(argv[0], "-"))
140 				ifile[ifx] = m4open(&argv, "r", &argc);
141 			else
142 				ifile[ifx] = stdin;
143 
144 			setfname(argv[0]);
145 			continue;
146 		}
147 
148 		if (is_alpha(t) || t == '_') {
149 			wchar_t	*tp = token+1;
150 			int tlim = toksize;
151 			struct nlist	*macadd;  /* temp variable */
152 
153 			while ((*tp = getchr()) != WEOF &&
154 				(is_alnum(*tp) || *tp == '_')) {
155 				tp++;
156 				if (--tlim <= 0)
157 					error2(gettext(
158 					"more than %d chars in word"),
159 							toksize);
160 			}
161 			putbak(*tp);
162 			*tp = EOS;
163 
164 			macadd = lookup(token);
165 			*Ap = (wchar_t *)macadd;
166 			if (macadd->def) {
167 				if ((wchar_t *)(++Ap) >= astklm) {
168 					--Ap;
169 					error2(gettext(
170 					"more than %d items on argument stack"),
171 					stksize);
172 				}
173 
174 				if (Cp++ == NULL)
175 					Cp = callst;
176 
177 				Cp->argp = Ap;
178 				*Ap++ = op;
179 				puttok(token);
180 				stkchr(EOS);
181 				t = getchr();
182 				putbak(t);
183 
184 				if (t != '(')
185 					pbstr(L"()");
186 				else	/* try to fix arg count */
187 					*Ap++ = op;
188 
189 				Cp->plev = 0;
190 			} else {
191 				puttok(token);
192 			}
193 		} else if (match(t, lquote)) {
194 			register	qlev = 1;
195 
196 			for (;;) {
197 				token[0] = t = getchr();
198 				token[1] = EOS;
199 
200 				if (match(t, rquote)) {
201 					if (--qlev > 0)
202 						puttok(token);
203 					else
204 						break;
205 				} else if (match(t, lquote)) {
206 					++qlev;
207 					puttok(token);
208 				} else {
209 					if (t == WEOF)
210 						error(gettext(
211 						"EOF in quote"));
212 					putchr(t);
213 				}
214 			}
215 		} else if (match(t, lcom) &&
216 			((lcom[0] != L'#' || lcom[1] != L'\0') ||
217 			prev_char != '$')) {
218 
219 			/*
220 			 * Don't expand commented macro (between lcom and
221 			 * rcom).
222 			 * What we know so far is that we have found the
223 			 * left comment char (lcom).
224 			 * Make sure we haven't found '#' (lcom) immediately
225 			 * preceded by '$' because we want to expand "$#".
226 			 */
227 
228 			puttok(token);
229 			for (;;) {
230 				token[0] = t = getchr();
231 				token[1] = EOS;
232 				if (match(t, rcom)) {
233 					puttok(token);
234 					break;
235 				} else {
236 					if (t == WEOF)
237 						error(gettext(
238 						"EOF in comment"));
239 					putchr(t);
240 				}
241 			}
242 		} else if (Cp == NULL) {
243 			putchr(t);
244 		} else if (t == '(') {
245 			if (Cp->plev)
246 				stkchr(t);
247 			else {
248 				/* skip white before arg */
249 				while ((t = getchr()) != WEOF && is_space(t))
250 					;
251 
252 				putbak(t);
253 			}
254 
255 			++Cp->plev;
256 		} else if (t == ')') {
257 			--Cp->plev;
258 
259 			if (Cp->plev == 0) {
260 				stkchr(EOS);
261 				expand(Cp->argp, Ap-Cp->argp-1);
262 				op = *Cp->argp;
263 				Ap = Cp->argp-1;
264 
265 				if (--Cp < callst)
266 					Cp = NULL;
267 			} else
268 				stkchr(t);
269 		} else if (t == ',' && Cp->plev <= 1) {
270 			stkchr(EOS);
271 			*Ap = op;
272 
273 			if ((wchar_t *)(++Ap) >= astklm) {
274 				--Ap;
275 				error2(gettext(
276 					"more than %d items on argument stack"),
277 					stksize);
278 			}
279 
280 			while ((t = getchr()) != WEOF && is_space(t))
281 				;
282 
283 			putbak(t);
284 		} else {
285 			stkchr(t);
286 		}
287 	}
288 
289 	if (Cp != NULL)
290 		error(gettext(
291 		"EOF in argument list"));
292 
293 	delexit(exitstat, 1);
294 	return (0);
295 }
296 
297 static wchar_t *
298 inpmatch(wchar_t *s)
299 {
300 	wchar_t	*tp = token+1;
301 
302 	while (*s) {
303 		*tp = getchr();
304 
305 		if (*tp++ != *s++) {
306 			*tp = EOS;
307 			pbstr(token+1);
308 			return (0);
309 		}
310 	}
311 
312 	*tp = EOS;
313 	return (token);
314 }
315 
316 static void
317 getflags(int *xargc, char ***xargv, int *option_end)
318 {
319 	char	*arg;
320 	char *t;
321 	wchar_t *s[3];
322 
323 	while (*xargc > 1) {
324 		arg = (*xargv)[1]; /* point arg to current argument */
325 
326 		/*
327 		 * This argument is not an option if it equals "-" or if
328 		 * "--" has already been parsed.
329 		 */
330 		if (arg[0] != '-' || arg[1] == EOS || *option_end)
331 			break;
332 		if (arg[0] == '-' && arg[1] == '-' && arg[2] == '\0') {
333 			*option_end = 1;
334 		} else {
335 			switch (arg[1]) {
336 			case 'B':
337 				chkspace(&arg, xargc, xargv);
338 				bufsize = atoi(&arg[2]);
339 				break;
340 			case 'D':
341 				initalloc();
342 				chkspace(&arg, xargc, xargv);
343 				for (t = &arg[2]; *t; t++) {
344 					if (*t == '=') {
345 						*t++ = EOS;
346 						break;
347 					}
348 				}
349 				s[1] = str2wstr(&arg[2], 1);
350 				s[2] = str2wstr(t, 1);
351 				dodef(&s[0], 2);
352 				free(s[1]);
353 				free(s[2]);
354 				break;
355 			case 'H':
356 				chkspace(&arg, xargc, xargv);
357 				hshsize = atoi(&arg[2]);
358 				break;
359 			case 'S':
360 				chkspace(&arg, xargc, xargv);
361 				stksize = atoi(&arg[2]);
362 				break;
363 			case 'T':
364 				chkspace(&arg, xargc, xargv);
365 				toksize = atoi(&arg[2]);
366 				break;
367 			case 'U':
368 				initalloc();
369 				chkspace(&arg, xargc, xargv);
370 				s[1] = str2wstr(&arg[2], 1);
371 				doundef(&s[0], 1);
372 				free(s[1]);
373 				break;
374 			case 'e':
375 				setbuf(stdout, NULL);
376 				(void) signal(SIGINT, SIG_IGN);
377 				break;
378 			case 's':
379 				/* turn on line sync */
380 				sflag = 1;
381 				break;
382 			default:
383 				(void) fprintf(stderr,
384 				    gettext("%s: bad option: %s\n"),
385 				    procnam, arg);
386 				delexit(NOT_OK, 0);
387 			}
388 		} /* end else not "--" */
389 
390 		(*xargv)++;
391 		--(*xargc);
392 	} /* end while options to process */
393 }
394 
395 /*
396  * Function: chkspace
397  *
398  * If there is a space between the option and its argument,
399  * adjust argptr so that &arg[2] will point to beginning of the option argument.
400  * This will ensure that processing in getflags() will work, because &arg[2]
401  * will point to the beginning of the option argument whether or not we have
402  * a space between the option and its argument.  If there is a space between
403  * the option and its argument, also adjust xargv and xargc because we are
404  * processing the next argument.
405  */
406 static void
407 chkspace(char **argptr, int *xargc, char ***xargv)
408 {
409 	if ((*argptr)[2] == EOS) {
410 		/* there is a space between the option and its argument */
411 		(*xargv)++; /* look at the next argument */
412 		--(*xargc);
413 		/*
414 		 * Adjust argptr if the option is followed by an
415 		 * option argument.
416 		 */
417 		if (*xargc > 1) {
418 			*argptr = (*xargv)[1];
419 			/* point &arg[2] to beginning of option argument */
420 			*argptr -= 2;
421 		}
422 	}
423 }
424 
425 static void
426 initalloc()
427 {
428 	static	done = 0;
429 	register	t;
430 
431 	if (done++)
432 		return;
433 
434 	hshtab = xcalloc(hshsize, sizeof (struct nlist *));
435 	callst = xcalloc(stksize/3+1, sizeof (struct call));
436 	Ap = argstk = xcalloc(stksize+3, sizeof (wchar_t *));
437 	ipstk[0] = ipflr = ip = ibuf = xcalloc(bufsize+1, sizeof (wchar_t));
438 	op = obuf = xcalloc(bufsize+1, sizeof (wchar_t));
439 	token = xcalloc(toksize+1, sizeof (wchar_t));
440 
441 	astklm = (wchar_t *)(&argstk[stksize]);
442 	ibuflm = &ibuf[bufsize];
443 	obuflm = &obuf[bufsize];
444 	toklm = &token[toksize];
445 
446 	for (t = 0; barray[t].bname; ++t) {
447 		wchar_t	p[2] = {0, EOS};
448 
449 		p[0] = builtin(t);
450 		install(barray[t].bname, p, NOPUSH);
451 	}
452 	install(L"unix", nullstr, NOPUSH);
453 }
454 
455 void
456 install(wchar_t *nam, wchar_t *val, int mode)
457 {
458 	register struct nlist *np;
459 	wchar_t	*cp;
460 	int		l;
461 
462 	if (mode == PUSH)
463 		(void) lookup(nam);	/* lookup sets hshval */
464 	else
465 		while (undef(nam))	/* undef calls lookup */
466 			;
467 
468 	np = xcalloc(1, sizeof (*np));
469 	np->name = wstrdup(nam);
470 	np->next = hshtab[hshval];
471 	hshtab[hshval] = np;
472 
473 	cp = xcalloc((l = wcslen(val))+1, sizeof (*val));
474 	np->def = cp;
475 	cp = &cp[l];
476 
477 	while (*val)
478 		*--cp = *val++;
479 }
480 
481 struct nlist *
482 lookup(wchar_t *str)
483 {
484 	wchar_t	*s1;
485 	register struct nlist	*np;
486 	static struct nlist	nodef;
487 
488 	s1 = str;
489 
490 	for (hshval = 0; *s1; )
491 		hshval += *s1++;
492 
493 	hshval %= hshsize;
494 
495 	for (np = hshtab[hshval]; np != NULL; np = np->next) {
496 		if (*str == *np->name && wcscmp(str, np->name) == 0)
497 			return (np);
498 	}
499 	return (&nodef);
500 }
501 
502 static void
503 expand(wchar_t **a1, int c)
504 {
505 	wchar_t	*dp;
506 	register struct nlist	*sp;
507 
508 	sp = (struct nlist *)a1[-1];
509 
510 	if (sp->tflag || trace) {
511 		int	i;
512 
513 		(void) fprintf(stderr,
514 		    "Trace(%d): %ws", Cp-callst, a1[0]);
515 
516 		if (c > 0) {
517 			(void) fprintf(stderr, "(%ws", chkbltin(a1[1]));
518 			for (i = 2; i <= c; ++i)
519 				(void) fprintf(stderr, ",%ws", chkbltin(a1[i]));
520 			(void) fprintf(stderr, ")");
521 		}
522 		(void) fprintf(stderr, "\n");
523 	}
524 
525 	dp = sp->def;
526 
527 	for (; *dp; ++dp) {
528 		if (is_builtin(*dp)) {
529 			(*barray[builtin_idx(*dp)].bfunc)(a1, c);
530 		} else if (dp[1] == '$') {
531 			if (is_digit(*dp)) {
532 				register	n;
533 				if ((n = *dp-'0') <= c)
534 					pbstr(a1[n]);
535 				++dp;
536 			} else if (*dp == '#') {
537 				pbnum((long)c);
538 				++dp;
539 			} else if (*dp == '*' || *dp == '@') {
540 				register i = c;
541 				wchar_t **a = a1;
542 
543 				if (i > 0)
544 					for (;;) {
545 						if (*dp == '@')
546 							pbstr(rquote);
547 
548 						pbstr(a[i--]);
549 
550 						if (*dp == '@')
551 							pbstr(lquote);
552 
553 						if (i <= 0)
554 						break;
555 
556 						pbstr(L",");
557 					}
558 				++dp;
559 			} else
560 				putbak(*dp);
561 		} else
562 			putbak(*dp);
563 	}
564 }
565 
566 void
567 setfname(char *s)
568 {
569 	if (fname[ifx])
570 		free(fname[ifx]);
571 	if ((fname[ifx] = strdup(s)) == NULL)
572 		error(gettext("out of storage"));
573 	fline[ifx] = 1;
574 	nflag = 1;
575 	lnsync(stdout);
576 }
577 
578 static void
579 lnsync(FILE *iop)
580 {
581 	static int cline = 0;
582 	static int cfile = 0;
583 
584 	if (!sflag || iop != stdout)
585 		return;
586 
587 	if (nflag || ifx != cfile) {
588 		nflag = 0;
589 		cfile = ifx;
590 		(void) fprintf(iop, "#line %d \"", cline = fline[ifx]);
591 		fpath(iop);
592 		(void) fprintf(iop, "\"\n");
593 	} else if (++cline != fline[ifx])
594 		(void) fprintf(iop, "#line %d\n", cline = fline[ifx]);
595 }
596 
597 static void
598 fpath(FILE *iop)
599 {
600 	register	i;
601 
602 	if (fname[0] == NULL)
603 		return;
604 
605 	(void) fprintf(iop, "%s", fname[0]);
606 
607 	for (i = 1; i <= ifx; ++i)
608 		(void) fprintf(iop, ":%s", fname[i]);
609 }
610 
611 /* ARGSUSED */
612 static void
613 catchsig(int i)
614 {
615 	(void) signal(SIGHUP, SIG_IGN);
616 	(void) signal(SIGINT, SIG_IGN);
617 	delexit(NOT_OK, 0);
618 }
619 
620 void
621 delexit(int code, int flushio)
622 {
623 	register i;
624 
625 	cf = stdout;
626 
627 /*
628  *	if (ofx != 0) {
629  *		ofx = 0;
630  *		code = NOT_OK;
631  *	}
632  */
633 	ofx = 0;	/* ensure that everything comes out */
634 	for (i = 1; i < 10; i++)
635 		undiv(i, code);
636 
637 	tempfile[7] = 'a';
638 	(void) unlink(tempfile);
639 
640 	/* flush standard I/O buffers, ie: call exit() not _exit() */
641 	if (flushio)
642 		exit(code);
643 
644 	_exit(code);
645 }
646 
647 static void
648 puttok(wchar_t *tp)
649 {
650 	if (Cp) {
651 		while (*tp)
652 			stkchr(*tp++);
653 	} else if (cf) {
654 		while (*tp) {
655 			sputchr(*tp++, cf);
656 		}
657 	}
658 }
659 
660 void
661 pbstr(wchar_t *str)
662 {
663 	wchar_t *p;
664 
665 	for (p = str + wcslen(str); --p >= str; )
666 		putbak(*p);
667 }
668 
669 void
670 undiv(int i, int code)
671 {
672 	register FILE *fp;
673 	wint_t c;
674 
675 	if (i < 1 || i > 9 || i == ofx || !ofile[i])
676 		return;
677 
678 	(void) fclose(ofile[i]);
679 	tempfile[7] = 'a'+i;
680 
681 	if (code == OK && cf) {
682 		fp = xfopen(tempfile, "r");
683 
684 		if (wide) {
685 			while ((c = myfgetwc(fp, -1)) != WEOF)
686 				sputchr((wchar_t)c, cf);
687 		} else {
688 			while ((c = (wint_t)getc(fp)) != WEOF)
689 				sputchr((wchar_t)c, cf);
690 		}
691 
692 		(void) fclose(fp);
693 	}
694 
695 	(void) unlink(tempfile);
696 	ofile[i] = NULL;
697 }
698 
699 void
700 pbnum(long num)
701 {
702 	pbnbr(num, 10, 1);
703 }
704 
705 void
706 pbnbr(long nbr, int base, int len)
707 {
708 	register	neg = 0;
709 
710 	if (base <= 0)
711 		return;
712 
713 	if (nbr < 0)
714 		neg = 1;
715 	else
716 		nbr = -nbr;
717 
718 	while (nbr < 0) {
719 		register int	i;
720 		if (base > 1) {
721 			i = nbr%base;
722 			nbr /= base;
723 #if (-3 % 2) != -1
724 			while (i > 0) {
725 				i -= base;
726 				++nbr;
727 			}
728 #endif
729 			i = -i;
730 		} else {
731 			i = 1;
732 			++nbr;
733 		}
734 		putbak(itochr(i));
735 		--len;
736 	}
737 
738 	while (--len >= 0)
739 		putbak('0');
740 
741 	if (neg)
742 		putbak('-');
743 }
744 
745 static wchar_t
746 itochr(int i)
747 {
748 	if (i > 9)
749 		return ((wchar_t)(i-10+'A'));
750 	else
751 		return ((wchar_t)(i+'0'));
752 }
753 
754 long
755 ctol(wchar_t *str)
756 {
757 	register sign;
758 	long num;
759 
760 	while (is_space(*str))
761 		++str;
762 	num = 0;
763 	if (*str == '-') {
764 		sign = -1;
765 		++str;
766 	} else
767 		sign = 1;
768 	while (is_digit(*str))
769 		num = num*10 + *str++ - '0';
770 	return (sign * num);
771 }
772 
773 int
774 min(int a, int b)
775 {
776 	if (a > b)
777 		return (b);
778 	return (a);
779 }
780 
781 FILE *
782 xfopen(char *name, char *mode)
783 {
784 	FILE	*fp;
785 
786 	if ((fp = fopen(name, mode)) == NULL)
787 		error(gettext("can't open file"));
788 
789 	return (fp);
790 }
791 
792 /*
793  * m4open
794  *
795  * Continue processing files when unable to open the given file argument.
796  */
797 FILE *
798 m4open(char ***argvec, char *mode, int *argcnt)
799 {
800 	FILE	*fp;
801 	char *arg;
802 
803 	while (*argcnt > 0) {
804 		arg = (*argvec)[0]; /* point arg to current file name */
805 		if (arg[0] == '-' && arg[1] == EOS)
806 			return (stdin);
807 		else {
808 			if ((fp = fopen(arg, mode)) == NULL) {
809 				(void) fprintf(stderr, gettext(
810 				"m4: cannot open %s: "), arg);
811 				perror("");
812 				if (*argcnt == 1) {
813 					/* last arg therefore exit */
814 					error3();
815 				} else {
816 					exitstat = 1;
817 					(*argvec)++; /* try next arg */
818 					(*argcnt)--;
819 				}
820 			} else
821 				break;
822 		}
823 	}
824 	return (fp);
825 }
826 
827 void *
828 xmalloc(size_t size)
829 {
830 	void *ptr;
831 
832 	if ((ptr = malloc(size)) == NULL)
833 		error(gettext("out of storage"));
834 	return (ptr);
835 }
836 
837 static void *
838 xcalloc(size_t nbr, size_t size)
839 {
840 	register void	*ptr;
841 
842 	ptr = xmalloc(nbr * size);
843 	(void) memset(ptr, '\0', nbr * size);
844 	return (ptr);
845 }
846 
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 		register 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