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