1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin * *
3da2e3ebdSchin * This software is part of the ast package *
4*34f9b3eeSRoland Mainz * Copyright (c) 2000-2009 AT&T Intellectual Property *
5da2e3ebdSchin * and is licensed under the *
6da2e3ebdSchin * Common Public License, Version 1.0 *
77c2fbfb3SApril Chin * by AT&T Intellectual Property *
8da2e3ebdSchin * *
9da2e3ebdSchin * A copy of the License is available at *
10da2e3ebdSchin * http://www.opensource.org/licenses/cpl1.0.txt *
11da2e3ebdSchin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12da2e3ebdSchin * *
13da2e3ebdSchin * Information and Software Systems Research *
14da2e3ebdSchin * AT&T Research *
15da2e3ebdSchin * Florham Park NJ *
16da2e3ebdSchin * *
17da2e3ebdSchin * Glenn Fowler <gsf@research.att.com> *
18da2e3ebdSchin * *
19da2e3ebdSchin ***********************************************************************/
20da2e3ebdSchin #pragma prototyped
21da2e3ebdSchin /*
22da2e3ebdSchin * Glenn Fowler
23da2e3ebdSchin * AT&T Research
24da2e3ebdSchin */
25da2e3ebdSchin
26da2e3ebdSchin static const char usage[] =
27da2e3ebdSchin "[-?\n@(#)$Id: msggen (AT&T Research) 2002-03-11 $\n]"
28da2e3ebdSchin USAGE_LICENSE
29da2e3ebdSchin "[+NAME?msggen - generate a machine independent formatted message catalog]"
30da2e3ebdSchin "[+DESCRIPTION?\bmsggen\b merges the message text source files \amsgfile\a"
31da2e3ebdSchin " into a machine independent formatted message catalog \acatfile\a."
32da2e3ebdSchin " The file \acatfile\a will be created if it does not already exist."
33da2e3ebdSchin " If \acatfile\a does exist, its messages will be included in the new"
34da2e3ebdSchin " \acatfile\a. If set and message numbers collide, the new message"
35da2e3ebdSchin " text defined in \amsgfile\a will replace the old message text"
36da2e3ebdSchin " currently contained in \acatfile\a. Non-ASCII characters must be"
37da2e3ebdSchin " UTF-8 encoded. \biconv\b(1) can be used to convert to/from UTF-8.]"
38da2e3ebdSchin "[f:format?List the \bprintf\b(3) format signature for each message in"
39da2e3ebdSchin " \acatfile\a. A format signature is one line containing one character"
40da2e3ebdSchin " per format specification:]{"
41da2e3ebdSchin " [c?char]"
42da2e3ebdSchin " [d?double]"
43da2e3ebdSchin " [D?long double]"
44da2e3ebdSchin " [f?float]"
45da2e3ebdSchin " [h?short]"
46da2e3ebdSchin " [i?int]"
47da2e3ebdSchin " [j?long long]"
48da2e3ebdSchin " [l?long]"
49da2e3ebdSchin " [p?void*]"
50da2e3ebdSchin " [s?string]"
51da2e3ebdSchin " [t?ptrdiff_t]"
52da2e3ebdSchin " [z?size_t]"
53da2e3ebdSchin " [???unknown]"
54da2e3ebdSchin "}"
55da2e3ebdSchin "[l:list?List \acatfile\a in UTF-8 \amsgfile\a form.]"
56da2e3ebdSchin "[s:set?Convert the \acatfile\a operand to a message set number and"
57da2e3ebdSchin " print the number on the standard output.]"
58da2e3ebdSchin "[+EXTENDED DESCRIPTION?Message text source files are in \bgencat\b(1)"
59da2e3ebdSchin " format, defined as follows. Note that the fields of a message text"
60da2e3ebdSchin " source line are separated by a single blank character. Any other"
61da2e3ebdSchin " blank characters are considered as being part of the subsequent"
62da2e3ebdSchin " field. The \bNL_*\b constants are defined in one or both of"
63da2e3ebdSchin " \b<limits.h>\b and \b<nl_types.h>\b.]{"
64da2e3ebdSchin " [+$ \acomment\a?A line beginning with \b$\b followed by a"
65da2e3ebdSchin " blank character is treated as a comment.]"
66da2e3ebdSchin " [+$delset \an\a \acomment\a?This line deletes message set"
67da2e3ebdSchin " \an\a from an existing message catalog. \an\a"
68da2e3ebdSchin " denotes the set number [1, \bNL_SETMAX\b]]. Any"
69da2e3ebdSchin " text following the set number is treated as a"
70da2e3ebdSchin " comment.]"
71da2e3ebdSchin " [+$quote \ac\a?This line specifies an optional quote"
72da2e3ebdSchin " character \ac\a, which can be used to surround"
73da2e3ebdSchin " \amessage-text\a so that trailing spaces or"
74da2e3ebdSchin " empty messages are visible in a message source"
75da2e3ebdSchin " line. By default, or if an empty \b$quote\b"
76da2e3ebdSchin " directive is supplied, no quoting of \amessage-text\a"
77da2e3ebdSchin " will be recognized.]"
78da2e3ebdSchin " [+$set \an\a \acomment\a?This line specifies the set"
79da2e3ebdSchin " identifier of the following messages until the next"
80da2e3ebdSchin " \b$set\b or end-of-file appears. \an\a denotes the set"
81da2e3ebdSchin " identifier, which is defined as a number in the range"
82da2e3ebdSchin " [1, \bNL_SETMAX\b]]. Set numbers need not be"
83da2e3ebdSchin " contiguous. Any text following the set identifier is"
84da2e3ebdSchin " treated as a comment. If no \b$set\b directive is"
85da2e3ebdSchin " specified in a message text source file, all messages"
86da2e3ebdSchin " will be located in message set \b1\b.]"
87da2e3ebdSchin " [+$translation \aidentification\a \aYYYY-MM-DD\a[,...]]?Append"
88da2e3ebdSchin " translation info to the message catalog header. Only"
89da2e3ebdSchin " the newest date for a given \aidentification\a"
90da2e3ebdSchin " is retained in the catalog. Multiple translation lines"
91da2e3ebdSchin " are combined into a single \b,\b separated list.]"
92da2e3ebdSchin " [+\am\a \amessage-text\a?\am\a denotes the message identifier,"
93da2e3ebdSchin " which is defined as a number in the range"
94da2e3ebdSchin " [1, \bNL_MSGMAX\b]]. The message-text is stored in the"
95da2e3ebdSchin " message catalogue with the set identifier specified by"
96da2e3ebdSchin " the last \b$set\b directive, and with message"
97da2e3ebdSchin " identifier \am\a. If the \amessage-text\a is empty,"
98da2e3ebdSchin " and a blank character field separator is present, an"
99da2e3ebdSchin " empty string is stored in the message catalogue. If a"
100da2e3ebdSchin " message source line has a message number, but neither"
101da2e3ebdSchin " a field separator nor \amessage-text\a, the existing"
102da2e3ebdSchin " message with that number (if any) is deleted from the"
103da2e3ebdSchin " catalogue. Message identifiers need not be contiguous."
104da2e3ebdSchin " There are no \amessage-text\a length restrictions.]"
105da2e3ebdSchin "}"
106da2e3ebdSchin
107da2e3ebdSchin "\n"
108da2e3ebdSchin "\ncatfile [ msgfile ]\n"
109da2e3ebdSchin "\n"
110da2e3ebdSchin
111da2e3ebdSchin "[+SEE ALSO?\bgencat\b(1), \biconv\b(1), \bmsgcc\b(1), \btranslate\b(1),"
112da2e3ebdSchin " \bfmtfmt\b(3)]"
113da2e3ebdSchin ;
114da2e3ebdSchin
115da2e3ebdSchin #include <ast.h>
116da2e3ebdSchin #include <ctype.h>
117da2e3ebdSchin #include <ccode.h>
118da2e3ebdSchin #include <error.h>
119da2e3ebdSchin #include <mc.h>
120da2e3ebdSchin
121da2e3ebdSchin typedef struct Xl_s
122da2e3ebdSchin {
123da2e3ebdSchin struct Xl_s* next;
124da2e3ebdSchin char* date;
125da2e3ebdSchin char name[1];
126da2e3ebdSchin } Xl_t;
127da2e3ebdSchin
128da2e3ebdSchin /*
129da2e3ebdSchin * append s to the translation list
130da2e3ebdSchin */
131da2e3ebdSchin
132da2e3ebdSchin static Xl_t*
translation(Xl_t * xp,register char * s)133da2e3ebdSchin translation(Xl_t* xp, register char* s)
134da2e3ebdSchin {
135da2e3ebdSchin register Xl_t* px;
136da2e3ebdSchin register char* t;
137da2e3ebdSchin char* d;
138da2e3ebdSchin char* e;
139da2e3ebdSchin
140da2e3ebdSchin do
141da2e3ebdSchin {
142da2e3ebdSchin for (; isspace(*s); s++);
143da2e3ebdSchin for (d = e = 0, t = s; *t; t++)
144da2e3ebdSchin if (*t == ',')
145da2e3ebdSchin {
146da2e3ebdSchin e = t;
147da2e3ebdSchin *e++ = 0;
148da2e3ebdSchin break;
149da2e3ebdSchin }
150da2e3ebdSchin else if (isspace(*t))
151da2e3ebdSchin d = t;
152da2e3ebdSchin if (d)
153da2e3ebdSchin {
154da2e3ebdSchin *d++ = 0;
155da2e3ebdSchin for (px = xp; px; px = px->next)
156da2e3ebdSchin if (streq(px->name, s))
157da2e3ebdSchin {
158da2e3ebdSchin if (strcoll(px->date, d) < 0)
159da2e3ebdSchin {
160da2e3ebdSchin free(px->date);
161da2e3ebdSchin if (!(px->date = strdup(d)))
162da2e3ebdSchin error(ERROR_SYSTEM|3, "out of space [translation]");
163da2e3ebdSchin }
164da2e3ebdSchin break;
165da2e3ebdSchin }
166da2e3ebdSchin if (!px)
167da2e3ebdSchin {
168da2e3ebdSchin if (!(px = newof(0, Xl_t, 1, strlen(s))) || !(px->date = strdup(d)))
169da2e3ebdSchin error(ERROR_SYSTEM|3, "out of space [translation]");
170da2e3ebdSchin strcpy(px->name, s);
171da2e3ebdSchin px->next = xp;
172da2e3ebdSchin xp = px;
173da2e3ebdSchin }
174da2e3ebdSchin }
175da2e3ebdSchin } while (s = e);
176da2e3ebdSchin return xp;
177da2e3ebdSchin }
178da2e3ebdSchin
179da2e3ebdSchin /*
180da2e3ebdSchin * sfprintf() with ccmaps(from,to)
181da2e3ebdSchin */
182da2e3ebdSchin
183da2e3ebdSchin static int
ccsfprintf(int from,int to,Sfio_t * sp,const char * format,...)184da2e3ebdSchin ccsfprintf(int from, int to, Sfio_t* sp, const char* format, ...)
185da2e3ebdSchin {
186da2e3ebdSchin va_list ap;
187da2e3ebdSchin Sfio_t* tp;
188da2e3ebdSchin char* s;
189da2e3ebdSchin int n;
190da2e3ebdSchin
191da2e3ebdSchin va_start(ap, format);
192da2e3ebdSchin if (from == to)
193da2e3ebdSchin n = sfvprintf(sp, format, ap);
194da2e3ebdSchin else if (tp = sfstropen())
195da2e3ebdSchin {
196da2e3ebdSchin n = sfvprintf(tp, format, ap);
197da2e3ebdSchin s = sfstrbase(tp);
198da2e3ebdSchin ccmaps(s, n, from, to);
199da2e3ebdSchin n = sfwrite(sp, s, n);
200da2e3ebdSchin sfstrclose(tp);
201da2e3ebdSchin }
202da2e3ebdSchin else
203da2e3ebdSchin n = -1;
204da2e3ebdSchin return n;
205da2e3ebdSchin }
206da2e3ebdSchin
207da2e3ebdSchin int
main(int argc,char ** argv)208da2e3ebdSchin main(int argc, char** argv)
209da2e3ebdSchin {
210da2e3ebdSchin register Mc_t* mc;
211da2e3ebdSchin register char* s;
212da2e3ebdSchin register char* t;
213da2e3ebdSchin register int c;
214da2e3ebdSchin register int q;
215da2e3ebdSchin register int i;
216da2e3ebdSchin int num;
217da2e3ebdSchin char* b;
218da2e3ebdSchin char* e;
219da2e3ebdSchin char* catfile;
220da2e3ebdSchin char* msgfile;
221da2e3ebdSchin Sfio_t* sp;
222da2e3ebdSchin Sfio_t* mp;
223da2e3ebdSchin Sfio_t* tp;
224da2e3ebdSchin Xl_t* px;
225da2e3ebdSchin Xl_t* bp;
226da2e3ebdSchin
227da2e3ebdSchin Xl_t* xp = 0;
228da2e3ebdSchin int format = 0;
229da2e3ebdSchin int list = 0;
230da2e3ebdSchin int set = 0;
231da2e3ebdSchin
232da2e3ebdSchin NoP(argc);
233da2e3ebdSchin error_info.id = "msggen";
234da2e3ebdSchin for (;;)
235da2e3ebdSchin {
236da2e3ebdSchin switch (optget(argv, usage))
237da2e3ebdSchin {
238da2e3ebdSchin case 'f':
239da2e3ebdSchin format = list = 1;
240da2e3ebdSchin continue;
241da2e3ebdSchin case 'l':
242da2e3ebdSchin list = 1;
243da2e3ebdSchin continue;
244da2e3ebdSchin case 's':
245da2e3ebdSchin set = 1;
246da2e3ebdSchin continue;
247da2e3ebdSchin case '?':
248da2e3ebdSchin error(ERROR_USAGE|4, "%s", opt_info.arg);
249da2e3ebdSchin continue;
250da2e3ebdSchin case ':':
251da2e3ebdSchin error(2, "%s", opt_info.arg);
252da2e3ebdSchin continue;
253da2e3ebdSchin }
254da2e3ebdSchin break;
255da2e3ebdSchin }
256da2e3ebdSchin argv += opt_info.index;
257da2e3ebdSchin if (error_info.errors || !(catfile = *argv++))
258da2e3ebdSchin error(ERROR_USAGE|4, "%s", optusage(NiL));
259da2e3ebdSchin
260da2e3ebdSchin /*
261da2e3ebdSchin * set and list only need catfile
262da2e3ebdSchin */
263da2e3ebdSchin
264da2e3ebdSchin if (set)
265da2e3ebdSchin {
266da2e3ebdSchin sfprintf(sfstdout, "%d\n", mcindex(catfile, NiL, NiL, NiL));
267da2e3ebdSchin return error_info.errors != 0;
268da2e3ebdSchin }
269da2e3ebdSchin else if (list)
270da2e3ebdSchin {
271da2e3ebdSchin if (!(sp = sfopen(NiL, catfile, "r")))
272da2e3ebdSchin error(ERROR_SYSTEM|3, "%s: cannot read catalog", catfile);
273da2e3ebdSchin if (!(mc = mcopen(sp)))
274da2e3ebdSchin error(ERROR_SYSTEM|3, "%s: catalog content error", catfile);
275da2e3ebdSchin sfclose(sp);
276da2e3ebdSchin if (format)
277da2e3ebdSchin {
278da2e3ebdSchin for (set = 1; set <= mc->num; set++)
279da2e3ebdSchin if (mc->set[set].num)
280da2e3ebdSchin {
281da2e3ebdSchin sfprintf(sfstdout, "$set %d\n", set);
282da2e3ebdSchin for (num = 1; num <= mc->set[set].num; num++)
283da2e3ebdSchin if (s = mc->set[set].msg[num])
284da2e3ebdSchin sfprintf(sfstdout, "%d \"%s\"\n", num, fmtfmt(s));
285da2e3ebdSchin }
286da2e3ebdSchin }
287da2e3ebdSchin else
288da2e3ebdSchin {
289da2e3ebdSchin if (*mc->translation)
290da2e3ebdSchin {
291da2e3ebdSchin ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "$translation ");
292da2e3ebdSchin sfprintf(sfstdout, "%s", mc->translation);
293da2e3ebdSchin ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "\n");
294da2e3ebdSchin }
295da2e3ebdSchin ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "$quote \"\n");
296da2e3ebdSchin for (set = 1; set <= mc->num; set++)
297da2e3ebdSchin if (mc->set[set].num)
298da2e3ebdSchin {
299da2e3ebdSchin ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "$set %d\n", set);
300da2e3ebdSchin for (num = 1; num <= mc->set[set].num; num++)
301da2e3ebdSchin if (s = mc->set[set].msg[num])
302da2e3ebdSchin {
303da2e3ebdSchin ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "%d \"", num);
304da2e3ebdSchin while (c = *s++)
305da2e3ebdSchin {
306da2e3ebdSchin /*INDENT...*/
307da2e3ebdSchin
308da2e3ebdSchin switch (c)
309da2e3ebdSchin {
310da2e3ebdSchin case 0x22: /* " */
311da2e3ebdSchin case 0x5C: /* \ */
312da2e3ebdSchin sfputc(sfstdout, 0x5C);
313da2e3ebdSchin break;
314da2e3ebdSchin case 0x07: /* \a */
315da2e3ebdSchin c = 0x61;
316da2e3ebdSchin sfputc(sfstdout, 0x5C);
317da2e3ebdSchin break;
318da2e3ebdSchin case 0x08: /* \b */
319da2e3ebdSchin c = 0x62;
320da2e3ebdSchin sfputc(sfstdout, 0x5C);
321da2e3ebdSchin break;
322da2e3ebdSchin case 0x0A: /* \n */
323da2e3ebdSchin c = 0x6E;
324da2e3ebdSchin sfputc(sfstdout, 0x5C);
325da2e3ebdSchin break;
326da2e3ebdSchin case 0x0B: /* \v */
327da2e3ebdSchin c = 0x76;
328da2e3ebdSchin sfputc(sfstdout, 0x5C);
329da2e3ebdSchin break;
330da2e3ebdSchin case 0x0C: /* \f */
331da2e3ebdSchin c = 0x66;
332da2e3ebdSchin sfputc(sfstdout, 0x5C);
333da2e3ebdSchin break;
334da2e3ebdSchin case 0x0D: /* \r */
335da2e3ebdSchin c = 0x72;
336da2e3ebdSchin sfputc(sfstdout, 0x5C);
337da2e3ebdSchin break;
338da2e3ebdSchin }
339da2e3ebdSchin
340da2e3ebdSchin /*...UNDENT*/
341da2e3ebdSchin sfputc(sfstdout, c);
342da2e3ebdSchin }
343da2e3ebdSchin ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "\"\n");
344da2e3ebdSchin }
345da2e3ebdSchin }
346da2e3ebdSchin }
347da2e3ebdSchin mcclose(mc);
348da2e3ebdSchin return error_info.errors != 0;
349da2e3ebdSchin }
350da2e3ebdSchin else if (!(msgfile = *argv++) || *argv)
351da2e3ebdSchin error(3, "exactly one message file must be specified");
352da2e3ebdSchin
353da2e3ebdSchin /*
354da2e3ebdSchin * open the files and handles
355da2e3ebdSchin */
356da2e3ebdSchin
357da2e3ebdSchin if (!(tp = sfstropen()))
358da2e3ebdSchin error(ERROR_SYSTEM|3, "out of space [string stream]");
359da2e3ebdSchin if (!(mp = sfopen(NiL, msgfile, "r")))
360da2e3ebdSchin error(ERROR_SYSTEM|3, "%s: cannot read message file", msgfile);
361da2e3ebdSchin sp = sfopen(NiL, catfile, "r");
362da2e3ebdSchin if (!(mc = mcopen(sp)))
363da2e3ebdSchin error(ERROR_SYSTEM|3, "%s: catalog content error", catfile);
364da2e3ebdSchin if (sp)
365da2e3ebdSchin sfclose(sp);
366da2e3ebdSchin xp = translation(xp, mc->translation);
367da2e3ebdSchin
368da2e3ebdSchin /*
369da2e3ebdSchin * read the message file
370da2e3ebdSchin */
371da2e3ebdSchin
372da2e3ebdSchin q = 0;
373da2e3ebdSchin set = 1;
374da2e3ebdSchin error_info.file = msgfile;
375da2e3ebdSchin while (s = sfgetr(mp, '\n', 1))
376da2e3ebdSchin {
377da2e3ebdSchin error_info.line++;
378da2e3ebdSchin if (!*s)
379da2e3ebdSchin continue;
380da2e3ebdSchin if (*s == '$')
381da2e3ebdSchin {
382da2e3ebdSchin if (!*++s || isspace(*s))
383da2e3ebdSchin continue;
384da2e3ebdSchin for (t = s; *s && !isspace(*s); s++);
385da2e3ebdSchin if (*s)
386da2e3ebdSchin *s++ = 0;
387da2e3ebdSchin if (streq(t, "delset"))
388da2e3ebdSchin {
389da2e3ebdSchin while (isspace(*s))
390da2e3ebdSchin s++;
391da2e3ebdSchin num = (int)strtol(s, NiL, 0);
392da2e3ebdSchin if (num < mc->num && mc->set[num].num)
393da2e3ebdSchin for (i = 1; i <= mc->set[num].num; i++)
394da2e3ebdSchin mcput(mc, num, i, NiL);
395da2e3ebdSchin }
396da2e3ebdSchin else if (streq(t, "quote"))
397da2e3ebdSchin q = *s ? *s : 0;
398da2e3ebdSchin else if (streq(t, "set"))
399da2e3ebdSchin {
400da2e3ebdSchin while (isspace(*s))
401da2e3ebdSchin s++;
402da2e3ebdSchin num = (int)strtol(s, &e, 0);
403da2e3ebdSchin if (e != s)
404da2e3ebdSchin set = num;
405da2e3ebdSchin else
406da2e3ebdSchin error(2, "set number expected");
407da2e3ebdSchin }
408da2e3ebdSchin else if (streq(t, "translation"))
409da2e3ebdSchin xp = translation(xp, s);
410da2e3ebdSchin }
411da2e3ebdSchin else
412da2e3ebdSchin {
413da2e3ebdSchin t = s + sfvalue(mp);
414da2e3ebdSchin num = (int)strtol(s, &e, 0);
415da2e3ebdSchin if (e != s)
416da2e3ebdSchin {
417da2e3ebdSchin s = e;
418da2e3ebdSchin if (!*s)
419da2e3ebdSchin {
420da2e3ebdSchin if (mcput(mc, set, num, NiL))
421da2e3ebdSchin error(2, "(%d,%d): cannot delete message", set, num);
422da2e3ebdSchin }
423da2e3ebdSchin else if (isspace(*s++))
424da2e3ebdSchin {
425da2e3ebdSchin if (t > (s + 1) && *(t -= 2) == '\\')
426da2e3ebdSchin {
427da2e3ebdSchin sfwrite(tp, s, t - s);
428da2e3ebdSchin while (s = sfgetr(mp, '\n', 0))
429da2e3ebdSchin {
430da2e3ebdSchin error_info.line++;
431da2e3ebdSchin t = s + sfvalue(mp);
432da2e3ebdSchin if (t <= (s + 1) || *(t -= 2) != '\\')
433da2e3ebdSchin break;
434da2e3ebdSchin sfwrite(tp, s, t - s);
435da2e3ebdSchin }
436da2e3ebdSchin if (!(s = sfstruse(tp)))
437da2e3ebdSchin error(ERROR_SYSTEM|3, "out of space");
438da2e3ebdSchin }
439da2e3ebdSchin if (q)
440da2e3ebdSchin {
441da2e3ebdSchin if (*s++ != q)
442da2e3ebdSchin {
443da2e3ebdSchin error(2, "(%d,%d): %c quote expected", set, num, q);
444da2e3ebdSchin continue;
445da2e3ebdSchin }
446da2e3ebdSchin b = t = s;
447da2e3ebdSchin while (c = *s++)
448da2e3ebdSchin {
449da2e3ebdSchin if (c == '\\')
450da2e3ebdSchin {
451da2e3ebdSchin c = chresc(s - 1, &e);
452da2e3ebdSchin s = e;
453da2e3ebdSchin if (c)
454da2e3ebdSchin *t++ = c;
455da2e3ebdSchin else
456da2e3ebdSchin error(1, "nul character ignored");
457da2e3ebdSchin }
458da2e3ebdSchin else if (c == q)
459da2e3ebdSchin break;
460da2e3ebdSchin else
461da2e3ebdSchin *t++ = c;
462da2e3ebdSchin }
463da2e3ebdSchin if (*s)
464da2e3ebdSchin {
465da2e3ebdSchin error(2, "(%d,%d): characters after quote not expected", set, num);
466da2e3ebdSchin continue;
467da2e3ebdSchin }
468da2e3ebdSchin *t = 0;
469da2e3ebdSchin s = b;
470da2e3ebdSchin }
471da2e3ebdSchin if (mcput(mc, set, num, s))
472da2e3ebdSchin error(2, "(%d,%d): cannot add message", set, num);
473da2e3ebdSchin }
474da2e3ebdSchin else
475da2e3ebdSchin error(2, "message text expected");
476da2e3ebdSchin }
477da2e3ebdSchin else
478da2e3ebdSchin error(2, "message number expected");
479da2e3ebdSchin }
480da2e3ebdSchin }
481da2e3ebdSchin error_info.file = 0;
482da2e3ebdSchin error_info.line = 0;
483da2e3ebdSchin
484da2e3ebdSchin /*
485da2e3ebdSchin * fix up the translation record
486da2e3ebdSchin */
487da2e3ebdSchin
488da2e3ebdSchin if (xp)
489da2e3ebdSchin {
490da2e3ebdSchin t = "";
491da2e3ebdSchin for (;;)
492da2e3ebdSchin {
493da2e3ebdSchin for (bp = 0, px = xp; px; px = px->next)
494da2e3ebdSchin if (px->date && (!bp || strcoll(bp->date, px->date) < 0))
495da2e3ebdSchin bp = px;
496da2e3ebdSchin if (!bp)
497da2e3ebdSchin break;
498da2e3ebdSchin sfprintf(tp, "%s%s %s", t, bp->name, bp->date);
499da2e3ebdSchin t = ", ";
500da2e3ebdSchin bp->date = 0;
501da2e3ebdSchin }
502da2e3ebdSchin if (!(mc->translation = sfstruse(tp)))
503da2e3ebdSchin error(ERROR_SYSTEM|3, "out of space");
504da2e3ebdSchin }
505da2e3ebdSchin
506da2e3ebdSchin /*
507da2e3ebdSchin * dump the catalog to a local temporary
508da2e3ebdSchin * rename if no errors
509da2e3ebdSchin */
510da2e3ebdSchin
511da2e3ebdSchin if (!(s = pathtemp(NiL, 0, "", error_info.id, NiL)) || !(sp = sfopen(NiL, s, "w")))
512da2e3ebdSchin error(ERROR_SYSTEM|3, "%s: cannot write catalog file", catfile);
513da2e3ebdSchin if (mcdump(mc, sp) || mcclose(mc) || sfclose(sp))
514da2e3ebdSchin {
515da2e3ebdSchin remove(s);
516da2e3ebdSchin error(ERROR_SYSTEM|3, "%s: temporary catalog file write error", s);
517da2e3ebdSchin }
518da2e3ebdSchin remove(catfile);
519da2e3ebdSchin if (rename(s, catfile))
520da2e3ebdSchin error(ERROR_SYSTEM|3, "%s: cannot rename from temporary catalog file %s", catfile, s);
521da2e3ebdSchin return error_info.errors != 0;
522da2e3ebdSchin }
523