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