xref: /titanic_51/usr/src/cmd/ast/msgcc/msggen.c (revision 34f9b3eef6fdadbda0a846aa4d68691ac40eace5)
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