1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 2000-2011 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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 
28 static const char usage[] =
29 "[-?\n@(#)$Id: msgcpp (AT&T Research) 2002-03-11 $\n]"
30 USAGE_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 
97 static 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 
108 static int
msgppargs(char ** argv,int last)109 msgppargs(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 
146 int
main(int argc,char ** argv)147 main(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