8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / ast / msgcc / msggen.c
blobd6999c8b2caaf416d309343a428980c5516216b3
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
22 * Glenn Fowler
23 * AT&T Research
26 static const char usage[] =
27 "[-?\n@(#)$Id: msggen (AT&T Research) 2002-03-11 $\n]"
28 USAGE_LICENSE
29 "[+NAME?msggen - generate a machine independent formatted message catalog]"
30 "[+DESCRIPTION?\bmsggen\b merges the message text source files \amsgfile\a"
31 " into a machine independent formatted message catalog \acatfile\a."
32 " The file \acatfile\a will be created if it does not already exist."
33 " If \acatfile\a does exist, its messages will be included in the new"
34 " \acatfile\a. If set and message numbers collide, the new message"
35 " text defined in \amsgfile\a will replace the old message text"
36 " currently contained in \acatfile\a. Non-ASCII characters must be"
37 " UTF-8 encoded. \biconv\b(1) can be used to convert to/from UTF-8.]"
38 "[f:format?List the \bprintf\b(3) format signature for each message in"
39 " \acatfile\a. A format signature is one line containing one character"
40 " per format specification:]{"
41 " [c?char]"
42 " [d?double]"
43 " [D?long double]"
44 " [f?float]"
45 " [h?short]"
46 " [i?int]"
47 " [j?long long]"
48 " [l?long]"
49 " [p?void*]"
50 " [s?string]"
51 " [t?ptrdiff_t]"
52 " [z?size_t]"
53 " [???unknown]"
54 "}"
55 "[l:list?List \acatfile\a in UTF-8 \amsgfile\a form.]"
56 "[s:set?Convert the \acatfile\a operand to a message set number and"
57 " print the number on the standard output.]"
58 "[+EXTENDED DESCRIPTION?Message text source files are in \bgencat\b(1)"
59 " format, defined as follows. Note that the fields of a message text"
60 " source line are separated by a single blank character. Any other"
61 " blank characters are considered as being part of the subsequent"
62 " field. The \bNL_*\b constants are defined in one or both of"
63 " \b<limits.h>\b and \b<nl_types.h>\b.]{"
64 " [+$ \acomment\a?A line beginning with \b$\b followed by a"
65 " blank character is treated as a comment.]"
66 " [+$delset \an\a \acomment\a?This line deletes message set"
67 " \an\a from an existing message catalog. \an\a"
68 " denotes the set number [1, \bNL_SETMAX\b]]. Any"
69 " text following the set number is treated as a"
70 " comment.]"
71 " [+$quote \ac\a?This line specifies an optional quote"
72 " character \ac\a, which can be used to surround"
73 " \amessage-text\a so that trailing spaces or"
74 " empty messages are visible in a message source"
75 " line. By default, or if an empty \b$quote\b"
76 " directive is supplied, no quoting of \amessage-text\a"
77 " will be recognized.]"
78 " [+$set \an\a \acomment\a?This line specifies the set"
79 " identifier of the following messages until the next"
80 " \b$set\b or end-of-file appears. \an\a denotes the set"
81 " identifier, which is defined as a number in the range"
82 " [1, \bNL_SETMAX\b]]. Set numbers need not be"
83 " contiguous. Any text following the set identifier is"
84 " treated as a comment. If no \b$set\b directive is"
85 " specified in a message text source file, all messages"
86 " will be located in message set \b1\b.]"
87 " [+$translation \aidentification\a \aYYYY-MM-DD\a[,...]]?Append"
88 " translation info to the message catalog header. Only"
89 " the newest date for a given \aidentification\a"
90 " is retained in the catalog. Multiple translation lines"
91 " are combined into a single \b,\b separated list.]"
92 " [+\am\a \amessage-text\a?\am\a denotes the message identifier,"
93 " which is defined as a number in the range"
94 " [1, \bNL_MSGMAX\b]]. The message-text is stored in the"
95 " message catalogue with the set identifier specified by"
96 " the last \b$set\b directive, and with message"
97 " identifier \am\a. If the \amessage-text\a is empty,"
98 " and a blank character field separator is present, an"
99 " empty string is stored in the message catalogue. If a"
100 " message source line has a message number, but neither"
101 " a field separator nor \amessage-text\a, the existing"
102 " message with that number (if any) is deleted from the"
103 " catalogue. Message identifiers need not be contiguous."
104 " There are no \amessage-text\a length restrictions.]"
107 "\n"
108 "\ncatfile [ msgfile ]\n"
109 "\n"
111 "[+SEE ALSO?\bgencat\b(1), \biconv\b(1), \bmsgcc\b(1), \btranslate\b(1),"
112 " \bfmtfmt\b(3)]"
115 #include <ast.h>
116 #include <ctype.h>
117 #include <ccode.h>
118 #include <error.h>
119 #include <mc.h>
121 typedef struct Xl_s
123 struct Xl_s* next;
124 char* date;
125 char name[1];
126 } Xl_t;
129 * append s to the translation list
132 static Xl_t*
133 translation(Xl_t* xp, register char* s)
135 register Xl_t* px;
136 register char* t;
137 char* d;
138 char* e;
142 for (; isspace(*s); s++);
143 for (d = e = 0, t = s; *t; t++)
144 if (*t == ',')
146 e = t;
147 *e++ = 0;
148 break;
150 else if (isspace(*t))
151 d = t;
152 if (d)
154 *d++ = 0;
155 for (px = xp; px; px = px->next)
156 if (streq(px->name, s))
158 if (strcoll(px->date, d) < 0)
160 free(px->date);
161 if (!(px->date = strdup(d)))
162 error(ERROR_SYSTEM|3, "out of space [translation]");
164 break;
166 if (!px)
168 if (!(px = newof(0, Xl_t, 1, strlen(s))) || !(px->date = strdup(d)))
169 error(ERROR_SYSTEM|3, "out of space [translation]");
170 strcpy(px->name, s);
171 px->next = xp;
172 xp = px;
175 } while (s = e);
176 return xp;
180 * sfprintf() with ccmaps(from,to)
183 static int
184 ccsfprintf(int from, int to, Sfio_t* sp, const char* format, ...)
186 va_list ap;
187 Sfio_t* tp;
188 char* s;
189 int n;
191 va_start(ap, format);
192 if (from == to)
193 n = sfvprintf(sp, format, ap);
194 else if (tp = sfstropen())
196 n = sfvprintf(tp, format, ap);
197 s = sfstrbase(tp);
198 ccmaps(s, n, from, to);
199 n = sfwrite(sp, s, n);
200 sfstrclose(tp);
202 else
203 n = -1;
204 return n;
208 main(int argc, char** argv)
210 register Mc_t* mc;
211 register char* s;
212 register char* t;
213 register int c;
214 register int q;
215 register int i;
216 int num;
217 char* b;
218 char* e;
219 char* catfile;
220 char* msgfile;
221 Sfio_t* sp;
222 Sfio_t* mp;
223 Sfio_t* tp;
224 Xl_t* px;
225 Xl_t* bp;
227 Xl_t* xp = 0;
228 int format = 0;
229 int list = 0;
230 int set = 0;
232 NoP(argc);
233 error_info.id = "msggen";
234 for (;;)
236 switch (optget(argv, usage))
238 case 'f':
239 format = list = 1;
240 continue;
241 case 'l':
242 list = 1;
243 continue;
244 case 's':
245 set = 1;
246 continue;
247 case '?':
248 error(ERROR_USAGE|4, "%s", opt_info.arg);
249 continue;
250 case ':':
251 error(2, "%s", opt_info.arg);
252 continue;
254 break;
256 argv += opt_info.index;
257 if (error_info.errors || !(catfile = *argv++))
258 error(ERROR_USAGE|4, "%s", optusage(NiL));
261 * set and list only need catfile
264 if (set)
266 sfprintf(sfstdout, "%d\n", mcindex(catfile, NiL, NiL, NiL));
267 return error_info.errors != 0;
269 else if (list)
271 if (!(sp = sfopen(NiL, catfile, "r")))
272 error(ERROR_SYSTEM|3, "%s: cannot read catalog", catfile);
273 if (!(mc = mcopen(sp)))
274 error(ERROR_SYSTEM|3, "%s: catalog content error", catfile);
275 sfclose(sp);
276 if (format)
278 for (set = 1; set <= mc->num; set++)
279 if (mc->set[set].num)
281 sfprintf(sfstdout, "$set %d\n", set);
282 for (num = 1; num <= mc->set[set].num; num++)
283 if (s = mc->set[set].msg[num])
284 sfprintf(sfstdout, "%d \"%s\"\n", num, fmtfmt(s));
287 else
289 if (*mc->translation)
291 ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "$translation ");
292 sfprintf(sfstdout, "%s", mc->translation);
293 ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "\n");
295 ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "$quote \"\n");
296 for (set = 1; set <= mc->num; set++)
297 if (mc->set[set].num)
299 ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "$set %d\n", set);
300 for (num = 1; num <= mc->set[set].num; num++)
301 if (s = mc->set[set].msg[num])
303 ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "%d \"", num);
304 while (c = *s++)
306 /*INDENT...*/
308 switch (c)
310 case 0x22: /* " */
311 case 0x5C: /* \ */
312 sfputc(sfstdout, 0x5C);
313 break;
314 case 0x07: /* \a */
315 c = 0x61;
316 sfputc(sfstdout, 0x5C);
317 break;
318 case 0x08: /* \b */
319 c = 0x62;
320 sfputc(sfstdout, 0x5C);
321 break;
322 case 0x0A: /* \n */
323 c = 0x6E;
324 sfputc(sfstdout, 0x5C);
325 break;
326 case 0x0B: /* \v */
327 c = 0x76;
328 sfputc(sfstdout, 0x5C);
329 break;
330 case 0x0C: /* \f */
331 c = 0x66;
332 sfputc(sfstdout, 0x5C);
333 break;
334 case 0x0D: /* \r */
335 c = 0x72;
336 sfputc(sfstdout, 0x5C);
337 break;
340 /*...UNDENT*/
341 sfputc(sfstdout, c);
343 ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "\"\n");
347 mcclose(mc);
348 return error_info.errors != 0;
350 else if (!(msgfile = *argv++) || *argv)
351 error(3, "exactly one message file must be specified");
354 * open the files and handles
357 if (!(tp = sfstropen()))
358 error(ERROR_SYSTEM|3, "out of space [string stream]");
359 if (!(mp = sfopen(NiL, msgfile, "r")))
360 error(ERROR_SYSTEM|3, "%s: cannot read message file", msgfile);
361 sp = sfopen(NiL, catfile, "r");
362 if (!(mc = mcopen(sp)))
363 error(ERROR_SYSTEM|3, "%s: catalog content error", catfile);
364 if (sp)
365 sfclose(sp);
366 xp = translation(xp, mc->translation);
369 * read the message file
372 q = 0;
373 set = 1;
374 error_info.file = msgfile;
375 while (s = sfgetr(mp, '\n', 1))
377 error_info.line++;
378 if (!*s)
379 continue;
380 if (*s == '$')
382 if (!*++s || isspace(*s))
383 continue;
384 for (t = s; *s && !isspace(*s); s++);
385 if (*s)
386 *s++ = 0;
387 if (streq(t, "delset"))
389 while (isspace(*s))
390 s++;
391 num = (int)strtol(s, NiL, 0);
392 if (num < mc->num && mc->set[num].num)
393 for (i = 1; i <= mc->set[num].num; i++)
394 mcput(mc, num, i, NiL);
396 else if (streq(t, "quote"))
397 q = *s ? *s : 0;
398 else if (streq(t, "set"))
400 while (isspace(*s))
401 s++;
402 num = (int)strtol(s, &e, 0);
403 if (e != s)
404 set = num;
405 else
406 error(2, "set number expected");
408 else if (streq(t, "translation"))
409 xp = translation(xp, s);
411 else
413 t = s + sfvalue(mp);
414 num = (int)strtol(s, &e, 0);
415 if (e != s)
417 s = e;
418 if (!*s)
420 if (mcput(mc, set, num, NiL))
421 error(2, "(%d,%d): cannot delete message", set, num);
423 else if (isspace(*s++))
425 if (t > (s + 1) && *(t -= 2) == '\\')
427 sfwrite(tp, s, t - s);
428 while (s = sfgetr(mp, '\n', 0))
430 error_info.line++;
431 t = s + sfvalue(mp);
432 if (t <= (s + 1) || *(t -= 2) != '\\')
433 break;
434 sfwrite(tp, s, t - s);
436 if (!(s = sfstruse(tp)))
437 error(ERROR_SYSTEM|3, "out of space");
439 if (q)
441 if (*s++ != q)
443 error(2, "(%d,%d): %c quote expected", set, num, q);
444 continue;
446 b = t = s;
447 while (c = *s++)
449 if (c == '\\')
451 c = chresc(s - 1, &e);
452 s = e;
453 if (c)
454 *t++ = c;
455 else
456 error(1, "nul character ignored");
458 else if (c == q)
459 break;
460 else
461 *t++ = c;
463 if (*s)
465 error(2, "(%d,%d): characters after quote not expected", set, num);
466 continue;
468 *t = 0;
469 s = b;
471 if (mcput(mc, set, num, s))
472 error(2, "(%d,%d): cannot add message", set, num);
474 else
475 error(2, "message text expected");
477 else
478 error(2, "message number expected");
481 error_info.file = 0;
482 error_info.line = 0;
485 * fix up the translation record
488 if (xp)
490 t = "";
491 for (;;)
493 for (bp = 0, px = xp; px; px = px->next)
494 if (px->date && (!bp || strcoll(bp->date, px->date) < 0))
495 bp = px;
496 if (!bp)
497 break;
498 sfprintf(tp, "%s%s %s", t, bp->name, bp->date);
499 t = ", ";
500 bp->date = 0;
502 if (!(mc->translation = sfstruse(tp)))
503 error(ERROR_SYSTEM|3, "out of space");
507 * dump the catalog to a local temporary
508 * rename if no errors
511 if (!(s = pathtemp(NiL, 0, "", error_info.id, NiL)) || !(sp = sfopen(NiL, s, "w")))
512 error(ERROR_SYSTEM|3, "%s: cannot write catalog file", catfile);
513 if (mcdump(mc, sp) || mcclose(mc) || sfclose(sp))
515 remove(s);
516 error(ERROR_SYSTEM|3, "%s: temporary catalog file write error", s);
518 remove(catfile);
519 if (rename(s, catfile))
520 error(ERROR_SYSTEM|3, "%s: cannot rename from temporary catalog file %s", catfile, s);
521 return error_info.errors != 0;