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 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*	Copyright (c) 1988 AT&T	*/
28/*	  All Rights Reserved  	*/
29
30#pragma ident	"%Z%%M%	%I%	%E% SMI"
31
32#include	<limits.h>
33#include	<unistd.h>
34#include	<sys/types.h>
35#include	"m4.h"
36
37#define	arg(n)	(c < (n) ? nullstr: ap[n])
38static void mkpid(char *);
39static void def(wchar_t **, int, int);
40static void dump(wchar_t *, wchar_t *);
41static void incl(wchar_t **, int, int);
42static int leftmatch(wchar_t *, wchar_t *);
43
44static void
45dochcom(wchar_t **ap, int c)
46{
47	wchar_t	*l = arg(1);
48	wchar_t	*r = arg(2);
49
50	if (wcslen(l) > MAXSYM || wcslen(r) > MAXSYM)
51		error2(gettext(
52		"comment marker longer than %d chars"), MAXSYM);
53	(void) wcscpy(lcom, l);
54	(void) wcscpy(rcom, *r ? r : L"\n");
55}
56
57static void
58docq(wchar_t **ap, int c)
59{
60	wchar_t	*l = arg(1);
61	wchar_t	*r = arg(2);
62
63	if (wcslen(l) > MAXSYM || wcslen(r) > MAXSYM)
64		error2(gettext(
65		"quote marker longer than %d chars"), MAXSYM);
66
67	if (c <= 1 && !*l) {
68		l = L"`";
69		r = L"'";
70	} else if (c == 1) {
71		r = l;
72	}
73
74	(void) wcscpy(lquote, l);
75	(void) wcscpy(rquote, r);
76}
77
78static void
79dodecr(wchar_t **ap, int c)
80{
81	pbnum(ctol(arg(1))-1);
82}
83
84void
85dodef(wchar_t **ap, int c)
86{
87	def(ap, c, NOPUSH);
88}
89
90static void
91def(wchar_t **ap, int c, int mode)
92{
93	wchar_t	*s;
94
95	if (c < 1)
96		return;
97
98	s = ap[1];
99	if (is_alpha(*s) || *s == '_') {
100		s++;
101		while (is_alnum(*s) || *s == '_')
102			s++;
103	}
104	if (*s || s == ap[1])
105		error(gettext("bad macro name"));
106
107	if ((ap[2] != NULL) && (wcscmp(ap[1], ap[2]) == 0))
108		error(gettext("macro defined as itself"));
109
110	install(ap[1], arg(2), mode);
111}
112
113static void
114dodefn(wchar_t **ap, int c)
115{
116	wchar_t *d;
117
118	while (c > 0)
119		if ((d = lookup(ap[c--])->def) != NULL) {
120			putbak(*rquote);
121			while (*d)
122				putbak(*d++);
123			putbak(*lquote);
124		}
125}
126
127static void
128dodiv(wchar_t **ap, int c)
129{
130	int f;
131
132	f = wstoi(arg(1));
133	if (f >= 10 || f < 0) {
134		cf = NULL;
135		ofx = f;
136		return;
137	}
138	tempfile[7] = 'a'+f;
139	if (ofile[f] || (ofile[f] = xfopen(tempfile, "w"))) {
140		ofx = f;
141		cf = ofile[f];
142	}
143}
144
145/* ARGSUSED */
146static void
147dodivnum(wchar_t **ap, int c)
148{
149	pbnum((long)ofx);
150}
151
152/* ARGSUSED */
153static void
154dodnl(wchar_t **ap, int c)
155{
156	wchar_t t;
157
158	while ((t = getchr()) != '\n' && t != WEOF)
159		;
160}
161
162static void
163dodump(wchar_t **ap, int c)
164{
165	struct nlist *np;
166	int	i;
167
168	if (c > 0)
169		while (c--) {
170			if ((np = lookup(*++ap))->name != NULL)
171				dump(np->name, np->def);
172		}
173	else
174		for (i = 0; i < hshsize; i++)
175			for (np = hshtab[i]; np != NULL; np = np->next)
176				dump(np->name, np->def);
177}
178
179/*ARGSUSED*/
180static void
181dump(wchar_t *name, wchar_t *defnn)
182{
183	wchar_t	*s = defnn;
184
185#if !defined(__lint)	/* lint doesn't grok "%ws" */
186	(void) fprintf(stderr, "%ws:\t", name);
187#endif
188
189	while (*s++)
190		;
191	--s;
192
193	while (s > defnn) {
194		--s;
195		if (is_builtin(*s)) {
196#if !defined(__lint)	/* lint doesn't grok "%ws" */
197			(void) fprintf(stderr, "<%ws>",
198			    barray[builtin_idx(*s)].bname);
199		} else {
200#endif
201			(void) fputwc(*s, stderr);
202		}
203	}
204	(void) fputc('\n', stderr);
205}
206
207/*ARGSUSED*/
208static void
209doerrp(wchar_t **ap, int c)
210{
211#if !defined(__lint)	/* lint doesn't grok "%ws" */
212	if (c > 0)
213		(void) fprintf(stderr, "%ws", ap[1]);
214#endif
215}
216
217long	evalval;	/* return value from yacc stuff */
218wchar_t	*pe;	/* used by grammar */
219
220static void
221doeval(wchar_t **ap, int c)
222{
223	int base = wstoi(arg(2));
224	int pad = wstoi(arg(3));
225	extern	int yyparse(void);
226
227	evalval = 0;
228	if (c > 0) {
229		pe = ap[1];
230		if (yyparse() != 0)
231			error(gettext(
232			"invalid expression"));
233	}
234	pbnbr(evalval, base > 0 ? base:10, pad > 0 ? pad : 1);
235}
236
237/*
238 * doexit
239 *
240 * Process m4exit macro.
241 */
242static void
243doexit(wchar_t **ap, int c)
244{
245	delexit(wstoi(arg(1)), 1);
246}
247
248static void
249doif(wchar_t **ap, int c)
250{
251	if (c < 3)
252		return;
253	while (c >= 3) {
254		if (wcscmp(ap[1], ap[2]) == 0) {
255			pbstr(ap[3]);
256			return;
257		}
258		c -= 3;
259		ap += 3;
260	}
261	if (c > 0)
262		pbstr(ap[1]);
263}
264
265static void
266doifdef(wchar_t **ap, int c)
267{
268	if (c < 2)
269		return;
270
271	while (c >= 2) {
272		if (lookup(ap[1])->name != NULL) {
273			pbstr(ap[2]);
274			return;
275		}
276		c -= 2;
277		ap += 2;
278	}
279
280	if (c > 0)
281		pbstr(ap[1]);
282}
283
284static void
285doincl(wchar_t **ap, int c)
286{
287	incl(ap, c, 1);
288}
289
290static void
291incl(wchar_t **ap, int c, int noisy)
292{
293	if (c > 0 && wcslen(ap[1]) > 0) {
294		if (ifx >= 9)
295			error(gettext(
296			"input file nesting too deep (9)"));
297		if ((ifile[++ifx] = fopen(wstr2str(ap[1], 0), "r")) == NULL) {
298			--ifx;
299			if (noisy)
300				error(gettext(
301				"can't open file"));
302		} else {
303			ipstk[ifx] = ipflr = ip;
304			setfname(wstr2str(ap[1], 0));
305		}
306	}
307}
308
309static void
310doincr(wchar_t **ap, int c)
311{
312	pbnum(ctol(arg(1))+1);
313}
314
315static void
316doindex(wchar_t **ap, int c)
317{
318	wchar_t	*subj = arg(1);
319	wchar_t	*obj  = arg(2);
320	int	i;
321
322	for (i = 0; *subj; ++i)
323		if (leftmatch(subj++, obj)) {
324			pbnum((long)i);
325			return;
326		}
327
328	pbnum((long)-1);
329}
330
331static int
332leftmatch(wchar_t *str, wchar_t *substr)
333{
334	while (*substr)
335		if (*str++ != *substr++)
336			return (0);
337
338	return (1);
339}
340
341static void
342dolen(wchar_t **ap, int c)
343{
344	pbnum((long)wcslen(arg(1)));
345}
346
347static void
348domake(wchar_t **ap, int c)
349{
350	char *path;
351
352	if (c > 0) {
353		path = wstr2str(ap[1], 1);
354		mkpid(path);
355		pbstr(str2wstr(path, 0));
356		free(path);
357	}
358}
359
360static void
361dopopdef(wchar_t **ap, int c)
362{
363	int	i;
364
365	for (i = 1; i <= c; ++i)
366		(void) undef(ap[i]);
367}
368
369static void
370dopushdef(wchar_t **ap, int c)
371{
372	def(ap, c, PUSH);
373}
374
375static void
376doshift(wchar_t **ap, int c)
377{
378	if (c <= 1)
379		return;
380
381	for (;;) {
382		pbstr(rquote);
383		pbstr(ap[c--]);
384		pbstr(lquote);
385
386		if (c <= 1)
387			break;
388
389		pbstr(L",");
390	}
391}
392
393static void
394dosincl(wchar_t **ap, int c)
395{
396	incl(ap, c, 0);
397}
398
399static void
400dosubstr(wchar_t **ap, int c)
401{
402	wchar_t	*str;
403	int	inlen, outlen;
404	int	offset, ix;
405
406	inlen = wcslen(str = arg(1));
407	offset = wstoi(arg(2));
408
409	if (offset < 0 || offset >= inlen)
410		return;
411
412	outlen = c >= 3 ? wstoi(ap[3]) : inlen;
413	ix = min(offset+outlen, inlen);
414
415	while (ix > offset)
416		putbak(str[--ix]);
417}
418
419static void
420dosyscmd(wchar_t **ap, int c)
421{
422	sysrval = 0;
423	if (c > 0) {
424		(void) fflush(stdout);
425		sysrval = system(wstr2str(ap[1], 0));
426	}
427}
428
429/* ARGSUSED */
430static void
431dosysval(wchar_t **ap, int c)
432{
433	pbnum((long)(sysrval < 0 ? sysrval :
434	    (sysrval >> 8) & ((1 << 8) - 1)) |
435	    ((sysrval & ((1 << 8) - 1)) << 8));
436}
437
438static void
439dotransl(wchar_t **ap, int c)
440{
441	wchar_t	*sink, *fr, *sto;
442	wchar_t	*source, *to;
443
444	if (c < 1)
445		return;
446
447	sink = ap[1];
448	fr = arg(2);
449	sto = arg(3);
450
451	for (source = ap[1]; *source; source++) {
452		wchar_t	*i;
453		to = sto;
454		for (i = fr; *i; ++i) {
455			if (*source == *i)
456				break;
457			if (*to)
458				++to;
459		}
460		if (*i) {
461			if (*to)
462				*sink++ = *to;
463		} else
464			*sink++ = *source;
465	}
466	*sink = EOS;
467	pbstr(ap[1]);
468}
469
470static void
471dotroff(wchar_t **ap, int c)
472{
473	struct nlist	*np;
474
475	trace = 0;
476
477	while (c > 0)
478		if ((np = lookup(ap[c--]))->name)
479			np->tflag = 0;
480}
481
482static void
483dotron(wchar_t **ap, int c)
484{
485	struct nlist	*np;
486
487	trace = !*arg(1);
488
489	while (c > 0)
490		if ((np = lookup(ap[c--]))->name)
491			np->tflag = 1;
492}
493
494void
495doundef(wchar_t **ap, int c)
496{
497	int	i;
498
499	for (i = 1; i <= c; ++i)
500		while (undef(ap[i]))
501			;
502}
503
504int
505undef(wchar_t *nam)
506{
507	struct	nlist *np, *tnp;
508
509	if ((np = lookup(nam))->name == NULL)
510		return (0);
511	tnp = hshtab[hshval];	/* lookup sets hshval */
512	if (tnp == np)	/* it's in first place */
513		hshtab[hshval] = tnp->next;
514	else {
515		while (tnp->next != np)
516			tnp = tnp->next;
517
518		tnp->next = np->next;
519	}
520	free(np->name);
521	free(np->def);
522	free(np);
523	return (1);
524}
525
526static void
527doundiv(wchar_t **ap, int c)
528{
529	int i;
530
531	if (c <= 0)
532		for (i = 1; i < 10; i++)
533			undiv(i, OK);
534	else
535		while (--c >= 0)
536			undiv(wstoi(*++ap), OK);
537}
538
539/*
540 * dowrap
541 *
542 * Process m4wrap macro.
543 */
544static void
545dowrap(wchar_t **ap, int c)
546{
547	wchar_t	*a = arg(1);
548	struct Wrap *wrapentry;		/* entry for list of "m4wrap" strings */
549
550	wrapentry = xmalloc(sizeof (struct Wrap));
551	/* store m4wrap string */
552	wrapentry->wrapstr = wstrdup(a);
553	/* add this entry to the front of the list of Wrap entries */
554	wrapentry->nxt = wrapstart;
555	wrapstart = wrapentry;
556}
557
558static void
559mkpid(char *as)
560{
561	char *s = as;
562	char *l;
563	char *first_X;
564	unsigned xcnt = 0;
565	char my_pid[32];
566	int pid_len;
567	int i = 0;
568
569	/*
570	 * Count number of X.
571	 */
572	l = &s[strlen(s)-1];
573	while (l != as) {
574		if (*l == 'X') {
575			first_X = l;
576			l--;
577			xcnt++;
578		} else if (xcnt == 0)
579			l--;
580		else {
581			break;
582		}
583	}
584
585	/*
586	 *	1) If there is no X in the passed string,
587	 *		then it just return the passed string.
588	 *	2) If the length of the continuous right most X's of
589	 *	   the string is shorter than the length of pid,
590	 *		then right most X's will be substitued with
591	 *		upper digits of pid.
592	 *	3) If the length of the continuous right most X's of
593	 *	   the string is equat to the length of pid,
594	 *		then X's will be replaced with pid.
595	 *	4) If the lenght of the continuous right most X's of
596	 *	   the string is longer than the length of pid,
597	 *		then X's will have leading 0 followed by
598	 *		pid.
599	 */
600
601	/*
602	 * If there were no X, don't do anything.
603	 */
604	if (xcnt == 0)
605		return;
606
607	/*
608	 * Get pid
609	 */
610	(void) snprintf(my_pid, sizeof (my_pid), "%d", (int)getpid());
611	pid_len = strlen(my_pid);
612
613	if (pid_len > xcnt)
614		my_pid[xcnt] = 0;
615	else if (pid_len < xcnt) {
616		while (xcnt != pid_len) {
617			*first_X++ = '0';
618			xcnt--;
619		}
620	}
621
622	/*
623	 * Copy pid
624	 */
625	while (i != xcnt)
626		*first_X++ = my_pid[i++];
627}
628
629struct bs	barray[] = {
630	dochcom,	L"changecom",
631	docq,		L"changequote",
632	dodecr,		L"decr",
633	dodef,		L"define",
634	dodefn,		L"defn",
635	dodiv,		L"divert",
636	dodivnum,	L"divnum",
637	dodnl,		L"dnl",
638	dodump,		L"dumpdef",
639	doerrp,		L"errprint",
640	doeval,		L"eval",
641	doexit,		L"m4exit",
642	doif,		L"ifelse",
643	doifdef,	L"ifdef",
644	doincl,		L"include",
645	doincr,		L"incr",
646	doindex,	L"index",
647	dolen,		L"len",
648	domake,		L"maketemp",
649	dopopdef,	L"popdef",
650	dopushdef,	L"pushdef",
651	doshift,	L"shift",
652	dosincl,	L"sinclude",
653	dosubstr,	L"substr",
654	dosyscmd,	L"syscmd",
655	dosysval,	L"sysval",
656	dotransl,	L"translit",
657	dotroff,	L"traceoff",
658	dotron,		L"traceon",
659	doundef,	L"undefine",
660	doundiv,	L"undivert",
661	dowrap,		L"m4wrap",
662	0,		0
663};
664