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