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