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
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