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