1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 2000-2009 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                 Glenn Fowler <gsf@research.att.com>                  *
18*                                                                      *
19***********************************************************************/
20#pragma prototyped
21/*
22 * Glenn Fowler
23 * AT&T Research
24 *
25 * C message catalog preprocessor
26 */
27
28static const char usage[] =
29"[-?\n@(#)$Id: msgcpp (AT&T Research) 2002-03-11 $\n]"
30USAGE_LICENSE
31"[+NAME?msgcpp - C language message catalog preprocessor]"
32"[+DESCRIPTION?\bmsgcpp\b is a C language message catalog preprocessor."
33"	It accepts \bcpp\b(1) style options and arguments. \bmsgcpp\b"
34"	preprocesses an input C source file and emits keyed lines to the"
35"	output, usually for further processing by \bmsgcc\b(1). \bmsgcc\b"
36"	output is in the \bgencat\b(1) syntax. Candidate message text is"
37"	determined by arguments to the \bast\b \b<error.h>\b and"
38"	\b<option.h>\b functions. The \bmsgcpp\b keyed output lines are:]{"
39"	[+cmd \acommand\a?\acommand\a is a candidate for \b--??keys\b"
40"		option string generation. Triggered by"
41"		\bb_\b\acommand\a\b(int argc,\b in the input.]"
42"	[+def \aname\a \astring\a?\aname\a is a candidate variable with"
43"		string value \astring\a.]"
44"	[+str \astring\a?\astring\a should be entered into the catalog.]"
45"	[+var \aname\a?If \bdef\b \aname\a occurs then its \astring\a value"
46"		should be entered into the catalog.]"
47"	}"
48"[+?The input source file is preprocessed with the \bpp:allpossible\b"
49"	option on. This enables non-C semantics; all source should first"
50"	be compiled error-free with a real compiler before running \bmsgcpp\b."
51"	The following changes are enabled for the top level files (i.e.,"
52"	included file behavior is not affected):]{"
53"		[+(1)?All \b#if\b, \b#ifdef\b and \b#ifndef\b branches"
54"			are enabled.]"
55"		[+(2)?The first definition for a macro is retained, even when"
56"			subsequent \b#define\b statements would normally"
57"			redefine the macro. \b#undef\b must be used to"
58"			redefine a macro.]"
59"		[+(3)?Macro calls with an improper number of arguments are"
60"			silently ignored.]"
61"		[+(4)?\b#include\b on non-existent headers are silently"
62"			ignored.]"
63"		[+(5)?Invalid C source characters are silently ignored.]"
64"	}"
65"[+?\b\"msgcat.h\"\b is included if it exists. This file may contain macro"
66"	definitions for functions that translate string arguments. If \afoo\a"
67"	is a function that translates its string arguments then include the"
68"	line \b#define \b\afoo\a\b _TRANSLATE_\b in \bmsgcat.h\b or specify"
69"	the option \b-D\b\afoo\a\b=_TRANSLATE_\b. If \abar\a is a function"
70"	that translates string arguments if the first argument is \bstderr\b"
71"	then use either \b#define \b\abar\a\b _STDIO_\b or"
72"	\b-D\b\abar\a\b=_STDIO_\b.]"
73"[+?The macro \b_BLD_msgcat\b is defined to be \b1\b. As an alternative to"
74"	\bmsgcat.h\b, \b_TRANSLATE_\b definitions could be placed inside"
75"	\b#ifdef _BLD_msgcat\b ... \b#endif\b.]"
76
77"\n"
78"\n[ input [ output ] ]\n"
79"\n"
80
81"[+SEE ALSO?\bcc\b(1), \bcpp\b(1), \bgencat\b(1), \bmsggen\b(1),"
82"	\bmsgcc\b(1), \bmsgcvt\b(1)]"
83;
84
85#include <ast.h>
86#include <error.h>
87
88#include "pp.h"
89#include "ppkey.h"
90
91#define T_STDERR	(T_KEYWORD+1)
92#define T_STDIO		(T_KEYWORD+2)
93#define T_TRANSLATE	(T_KEYWORD+3)
94
95#define OMIT		"*@(\\[[-+]*\\?*\\]|\\@\\(#\\)|Copyright \\(c\\)|\\\\000|\\\\00[!0-9]|\\\\0[!0-9])*"
96
97static struct ppkeyword	keys[] =
98{
99	"char",		T_CHAR,
100	"int",		T_INT,
101	"sfstderr",	T_STDERR,
102	"stderr",	T_STDERR,
103	"_STDIO_",	T_STDIO,
104	"_TRANSLATE_",	T_TRANSLATE,
105	0,		0
106};
107
108static int
109msgppargs(char** argv, int last)
110{
111	for (;;)
112	{
113		switch (optget(argv, usage))
114		{
115		case 0:
116			break;
117		case '?':
118			if (!last)
119			{
120				opt_info.again = 1;
121				return 1;
122			}
123			error(ERROR_USAGE|4, "%s", opt_info.arg);
124			break;
125		case ':':
126			if (!last)
127			{
128				opt_info.again = 1;
129				return 1;
130			}
131			error(2, "%s", opt_info.arg);
132			continue;
133		default:
134			if (!last)
135			{
136				opt_info.again = 1;
137				return 1;
138			}
139			continue;
140		}
141		break;
142	}
143	return argv[opt_info.index] != 0;
144}
145
146int
147main(int argc, char** argv)
148{
149	register char*	s;
150	register int	x;
151	register int	c;
152	Sfio_t*		tmp;
153
154	NoP(argc);
155	if (s = strrchr(*argv, '/'))
156		s++;
157	else
158		s = *argv;
159	error_info.id = s;
160	ppop(PP_DEFAULT, PPDEFAULT);
161	optjoin(argv, msgppargs, ppargs, NiL);
162	if (strlen(s) >= 5 && *(s + 3) != 'c')
163	{
164		ppop(PP_PLUSPLUS, 1);
165		ppop(PP_NOHASH, 1);
166		ppop(PP_PROBE, "CC");
167	}
168	ppop(PP_SPACEOUT, 0);
169	ppop(PP_COMPILE, keys);
170	ppop(PP_OPTION, "allpossible");
171	ppop(PP_OPTION, "catliteral");
172	ppop(PP_OPTION, "modern");
173	ppop(PP_OPTION, "readonly");
174	ppop(PP_DEFINE, "_BLD_msgcat=1");
175	ppop(PP_DEFINE, "const=");
176	ppop(PP_DEFINE, "errorf=_TRANSLATE_");
177	ppop(PP_DEFINE, "register=");
178	ppop(PP_DEFINE, "sfstderr=sfstderr");
179	ppop(PP_DEFINE, "stderr=stderr");
180	ppop(PP_DEFINE, "_(m)=_TRANSLATE_(m)");
181	ppop(PP_DEFINE, "__(m)=_TRANSLATE_(m)");
182	ppop(PP_DEFINE, "gettxt(i,m)=_TRANSLATE_(m)");
183	ppop(PP_DEFINE, "gettext(m)=_TRANSLATE_(m)");
184	ppop(PP_DEFINE, "dgettext(d,m)=_TRANSLATE_(m)");
185	ppop(PP_DEFINE, "dcgettext(d,m,c)=_TRANSLATE_(m)");
186	ppop(PP_DEFINE, "ERROR_catalog(m)=_TRANSLATE_(m)");
187	ppop(PP_DEFINE, "ERROR_dictionary(m)=_TRANSLATE_(m)");
188	ppop(PP_DEFINE, "ERROR_translate(l,i,c,m)=_TRANSLATE_(m)");
189	ppop(PP_DEFINE, "error(l,f,...)=_TRANSLATE_(f)");
190	ppop(PP_DEFINE, "errormsg(t,l,f,...)=_TRANSLATE_(f)");
191	ppop(PP_DIRECTIVE, "include \"msgcat.h\"");
192	ppop(PP_OPTION, "noreadonly");
193	ppop(PP_INIT);
194	if (!(tmp = sfstropen()))
195		error(ERROR_SYSTEM|3, "out of space");
196	x = 0;
197	for (;;)
198	{
199		c = pplex();
200	again:
201		switch (c)
202		{
203		case 0:
204			break;
205		case T_TRANSLATE:
206			switch (c = pplex())
207			{
208			case '(':
209				x = 1;
210				break;
211			case ')':
212				if ((c = pplex()) != '(')
213				{
214					x = 0;
215					goto again;
216				}
217				x = 1;
218				break;
219			default:
220				x = 0;
221				goto again;
222			}
223			continue;
224		case '(':
225			if (x > 0)
226				x++;
227			continue;
228		case ')':
229			if (x > 0)
230				x--;
231			continue;
232		case T_STDIO:
233			if ((c = pplex()) != '(' || (c = pplex()) != T_STDERR || (c = pplex()) != ',')
234			{
235				x = 0;
236				goto again;
237			}
238			x = 1;
239			continue;
240		case T_STRING:
241			if (x > 0 && !strmatch(pp.token, OMIT))
242				sfprintf(sfstdout, "str \"%s\"\n", pp.token);
243			continue;
244		case T_ID:
245			s = pp.symbol->name;
246			if (x > 0)
247			{
248				if ((c = pplex()) == '+' && ppisinteger(c = pplex()))
249					sfprintf(sfstdout, "var %s %s\n", pp.token, s);
250				else
251					sfprintf(sfstdout, "var %s\n", s);
252			}
253			else if (s[0] == 'b' && s[1] == '_' && s[2])
254			{
255				if ((c = pplex()) == '(' && (c = pplex()) == T_INT && (c = pplex()) == T_ID && (c = pplex()) == ',' && (c = pplex()) == T_CHAR && (c = pplex()) == '*')
256					sfprintf(sfstdout, "cmd %s\n", s + 2);
257				else
258					goto again;
259			}
260			else
261			{
262				if ((c = pplex()) == '[')
263				{
264					if (ppisinteger(c = pplex()))
265						c = pplex();
266					if (c != ']')
267						goto again;
268					c = pplex();
269				}
270				if (c == '=' && (c = pplex()) == T_STRING && !strmatch(pp.token, OMIT))
271				{
272					sfprintf(sfstdout, "def %s \"%s\"\n", s, pp.token);
273					sfprintf(tmp, "#define %s \"%s\"\n", s, pp.token);
274					if (!(s = sfstruse(tmp)))
275						error(ERROR_SYSTEM|3, "out of space");
276					ppinput(s, "string", 0);
277				}
278				else
279					goto again;
280			}
281			continue;
282		default:
283			continue;
284		}
285		break;
286	}
287	ppop(PP_DONE);
288	return error_info.errors != 0;
289}
290