Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/msgcc/msggen.c
1808 views
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 <[email protected]> *
18
* *
19
***********************************************************************/
20
#pragma prototyped
21
/*
22
* Glenn Fowler
23
* AT&T Research
24
*/
25
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.]"
105
"}"
106
107
"\n"
108
"\ncatfile [ msgfile ]\n"
109
"\n"
110
111
"[+SEE ALSO?\bgencat\b(1), \biconv\b(1), \bmsgcc\b(1), \btranslate\b(1),"
112
" \bfmtfmt\b(3)]"
113
;
114
115
#include <ast.h>
116
#include <ctype.h>
117
#include <ccode.h>
118
#include <error.h>
119
#include <mc.h>
120
121
typedef struct Xl_s
122
{
123
struct Xl_s* next;
124
char* date;
125
char name[1];
126
} Xl_t;
127
128
/*
129
* append s to the translation list
130
*/
131
132
static Xl_t*
133
translation(Xl_t* xp, register char* s)
134
{
135
register Xl_t* px;
136
register char* t;
137
char* d;
138
char* e;
139
140
do
141
{
142
for (; isspace(*s); s++);
143
for (d = e = 0, t = s; *t; t++)
144
if (*t == ',')
145
{
146
e = t;
147
*e++ = 0;
148
break;
149
}
150
else if (isspace(*t))
151
d = t;
152
if (d)
153
{
154
*d++ = 0;
155
for (px = xp; px; px = px->next)
156
if (streq(px->name, s))
157
{
158
if (strcoll(px->date, d) < 0)
159
{
160
free(px->date);
161
if (!(px->date = strdup(d)))
162
error(ERROR_SYSTEM|3, "out of space [translation]");
163
}
164
break;
165
}
166
if (!px)
167
{
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;
173
}
174
}
175
} while (s = e);
176
return xp;
177
}
178
179
/*
180
* sfprintf() with ccmaps(from,to)
181
*/
182
183
static int
184
ccsfprintf(int from, int to, Sfio_t* sp, const char* format, ...)
185
{
186
va_list ap;
187
Sfio_t* tp;
188
char* s;
189
int n;
190
191
va_start(ap, format);
192
if (from == to)
193
n = sfvprintf(sp, format, ap);
194
else if (tp = sfstropen())
195
{
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);
201
}
202
else
203
n = -1;
204
return n;
205
}
206
207
int
208
main(int argc, char** argv)
209
{
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;
226
227
Xl_t* xp = 0;
228
int format = 0;
229
int list = 0;
230
int set = 0;
231
232
NoP(argc);
233
error_info.id = "msggen";
234
for (;;)
235
{
236
switch (optget(argv, usage))
237
{
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;
253
}
254
break;
255
}
256
argv += opt_info.index;
257
if (error_info.errors || !(catfile = *argv++))
258
error(ERROR_USAGE|4, "%s", optusage(NiL));
259
260
/*
261
* set and list only need catfile
262
*/
263
264
if (set)
265
{
266
sfprintf(sfstdout, "%d\n", mcindex(catfile, NiL, NiL, NiL));
267
return error_info.errors != 0;
268
}
269
else if (list)
270
{
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)
277
{
278
for (set = 1; set <= mc->num; set++)
279
if (mc->set[set].num)
280
{
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));
285
}
286
}
287
else
288
{
289
if (*mc->translation)
290
{
291
ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "$translation ");
292
sfprintf(sfstdout, "%s", mc->translation);
293
ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "\n");
294
}
295
ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "$quote \"\n");
296
for (set = 1; set <= mc->num; set++)
297
if (mc->set[set].num)
298
{
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])
302
{
303
ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "%d \"", num);
304
while (c = *s++)
305
{
306
/*INDENT...*/
307
308
switch (c)
309
{
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;
338
}
339
340
/*...UNDENT*/
341
sfputc(sfstdout, c);
342
}
343
ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "\"\n");
344
}
345
}
346
}
347
mcclose(mc);
348
return error_info.errors != 0;
349
}
350
else if (!(msgfile = *argv++) || *argv)
351
error(3, "exactly one message file must be specified");
352
353
/*
354
* open the files and handles
355
*/
356
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);
367
368
/*
369
* read the message file
370
*/
371
372
q = 0;
373
set = 1;
374
error_info.file = msgfile;
375
while (s = sfgetr(mp, '\n', 1))
376
{
377
error_info.line++;
378
if (!*s)
379
continue;
380
if (*s == '$')
381
{
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"))
388
{
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);
395
}
396
else if (streq(t, "quote"))
397
q = *s ? *s : 0;
398
else if (streq(t, "set"))
399
{
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");
407
}
408
else if (streq(t, "translation"))
409
xp = translation(xp, s);
410
}
411
else
412
{
413
t = s + sfvalue(mp);
414
num = (int)strtol(s, &e, 0);
415
if (e != s)
416
{
417
s = e;
418
if (!*s)
419
{
420
if (mcput(mc, set, num, NiL))
421
error(2, "(%d,%d): cannot delete message", set, num);
422
}
423
else if (isspace(*s++))
424
{
425
if (t > (s + 1) && *(t -= 2) == '\\')
426
{
427
sfwrite(tp, s, t - s);
428
while (s = sfgetr(mp, '\n', 0))
429
{
430
error_info.line++;
431
t = s + sfvalue(mp);
432
if (t <= (s + 1) || *(t -= 2) != '\\')
433
break;
434
sfwrite(tp, s, t - s);
435
}
436
if (!(s = sfstruse(tp)))
437
error(ERROR_SYSTEM|3, "out of space");
438
}
439
if (q)
440
{
441
if (*s++ != q)
442
{
443
error(2, "(%d,%d): %c quote expected", set, num, q);
444
continue;
445
}
446
b = t = s;
447
while (c = *s++)
448
{
449
if (c == '\\')
450
{
451
c = chresc(s - 1, &e);
452
s = e;
453
if (c)
454
*t++ = c;
455
else
456
error(1, "nul character ignored");
457
}
458
else if (c == q)
459
break;
460
else
461
*t++ = c;
462
}
463
if (*s)
464
{
465
error(2, "(%d,%d): characters after quote not expected", set, num);
466
continue;
467
}
468
*t = 0;
469
s = b;
470
}
471
if (mcput(mc, set, num, s))
472
error(2, "(%d,%d): cannot add message", set, num);
473
}
474
else
475
error(2, "message text expected");
476
}
477
else
478
error(2, "message number expected");
479
}
480
}
481
error_info.file = 0;
482
error_info.line = 0;
483
484
/*
485
* fix up the translation record
486
*/
487
488
if (xp)
489
{
490
t = "";
491
for (;;)
492
{
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;
501
}
502
if (!(mc->translation = sfstruse(tp)))
503
error(ERROR_SYSTEM|3, "out of space");
504
}
505
506
/*
507
* dump the catalog to a local temporary
508
* rename if no errors
509
*/
510
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))
514
{
515
remove(s);
516
error(ERROR_SYSTEM|3, "%s: temporary catalog file write error", s);
517
}
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;
522
}
523
524