1da2e3ebdSchin /*********************************************************************** 2da2e3ebdSchin * * 3da2e3ebdSchin * This software is part of the ast package * 4*7c2fbfb3SApril Chin * Copyright (c) 2000-2008 AT&T Intellectual Property * 5da2e3ebdSchin * and is licensed under the * 6da2e3ebdSchin * Common Public License, Version 1.0 * 7*7c2fbfb3SApril 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* 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 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 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