gencat.c revision 1.26 1 1.26 ginsbach /* $NetBSD: gencat.c,v 1.26 2008/11/04 03:14:46 ginsbach Exp $ */
2 1.5 veego
3 1.5 veego /*
4 1.3 jtc * Copyright (c) 1996 The NetBSD Foundation, Inc.
5 1.3 jtc * All rights reserved.
6 1.3 jtc *
7 1.3 jtc * This code is derived from software contributed to The NetBSD Foundation
8 1.3 jtc * by J.T. Conklin.
9 1.3 jtc *
10 1.3 jtc * Redistribution and use in source and binary forms, with or without
11 1.3 jtc * modification, are permitted provided that the following conditions
12 1.3 jtc * are met:
13 1.3 jtc * 1. Redistributions of source code must retain the above copyright
14 1.3 jtc * notice, this list of conditions and the following disclaimer.
15 1.3 jtc * 2. Redistributions in binary form must reproduce the above copyright
16 1.3 jtc * notice, this list of conditions and the following disclaimer in the
17 1.3 jtc * documentation and/or other materials provided with the distribution.
18 1.3 jtc *
19 1.3 jtc * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.3 jtc * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.3 jtc * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.6 jtc * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.6 jtc * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.3 jtc * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.3 jtc * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.3 jtc * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.3 jtc * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.3 jtc * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.3 jtc * POSSIBILITY OF SUCH DAMAGE.
30 1.3 jtc */
31 1.1 jtc
32 1.7 lukem #include <sys/cdefs.h>
33 1.14 tv #if defined(__RCSID) && !defined(lint)
34 1.26 ginsbach __RCSID("$NetBSD: gencat.c,v 1.26 2008/11/04 03:14:46 ginsbach Exp $");
35 1.7 lukem #endif
36 1.7 lukem
37 1.1 jtc /***********************************************************
38 1.1 jtc Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
39 1.1 jtc
40 1.1 jtc All Rights Reserved
41 1.1 jtc
42 1.1 jtc Permission to use, copy, modify, and distribute this software and its
43 1.1 jtc documentation for any purpose and without fee is hereby granted,
44 1.1 jtc provided that the above copyright notice appear in all copies and that
45 1.1 jtc both that copyright notice and this permission notice appear in
46 1.1 jtc supporting documentation, and that Alfalfa's name not be used in
47 1.1 jtc advertising or publicity pertaining to distribution of the software
48 1.1 jtc without specific, written prior permission.
49 1.1 jtc
50 1.1 jtc ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
51 1.1 jtc ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
52 1.1 jtc ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
53 1.1 jtc ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
54 1.1 jtc WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
55 1.1 jtc ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
56 1.1 jtc SOFTWARE.
57 1.1 jtc
58 1.1 jtc If you make any modifications, bugfixes or other changes to this software
59 1.1 jtc we'd appreciate it if you could send a copy to us so we can keep things
60 1.1 jtc up-to-date. Many thanks.
61 1.1 jtc Kee Hinckley
62 1.1 jtc Alfalfa Software, Inc.
63 1.1 jtc 267 Allston St., #3
64 1.1 jtc Cambridge, MA 02139 USA
65 1.1 jtc nazgul (at) alfalfa.com
66 1.3 jtc
67 1.1 jtc ******************************************************************/
68 1.1 jtc
69 1.18 lukem #if HAVE_NBTOOL_CONFIG_H
70 1.18 lukem #include "nbtool_config.h"
71 1.13 tv #endif
72 1.13 tv
73 1.3 jtc #define _NLS_PRIVATE
74 1.1 jtc
75 1.17 bjh21 #include <sys/types.h>
76 1.3 jtc #include <sys/queue.h>
77 1.15 bjh21
78 1.16 bjh21 #include <netinet/in.h> /* Needed by arpa/inet.h on NetBSD */
79 1.16 bjh21 #include <arpa/inet.h> /* Needed for htonl() on POSIX systems */
80 1.15 bjh21
81 1.3 jtc #include <ctype.h>
82 1.14 tv #include <err.h>
83 1.24 ginsbach #include <errno.h>
84 1.7 lukem #include <fcntl.h>
85 1.12 yamt #include <limits.h>
86 1.7 lukem #include <nl_types.h>
87 1.1 jtc #include <stdio.h>
88 1.3 jtc #include <stdlib.h>
89 1.3 jtc #include <string.h>
90 1.3 jtc #include <unistd.h>
91 1.13 tv
92 1.14 tv #ifndef NL_SETMAX
93 1.14 tv #define NL_SETMAX 255
94 1.14 tv #endif
95 1.14 tv #ifndef NL_MSGMAX
96 1.14 tv #define NL_MSGMAX 2048
97 1.13 tv #endif
98 1.3 jtc
99 1.3 jtc struct _msgT {
100 1.3 jtc long msgId;
101 1.3 jtc char *str;
102 1.3 jtc LIST_ENTRY(_msgT) entries;
103 1.3 jtc };
104 1.3 jtc
105 1.3 jtc struct _setT {
106 1.3 jtc long setId;
107 1.3 jtc LIST_HEAD(msghead, _msgT) msghead;
108 1.3 jtc LIST_ENTRY(_setT) entries;
109 1.3 jtc };
110 1.1 jtc
111 1.3 jtc LIST_HEAD(sethead, _setT) sethead;
112 1.3 jtc static struct _setT *curSet;
113 1.1 jtc
114 1.24 ginsbach static const char *curfile;
115 1.3 jtc static char *curline = NULL;
116 1.3 jtc static long lineno = 0;
117 1.1 jtc
118 1.23 ginsbach static char *cskip(char *);
119 1.23 ginsbach static void error(const char *);
120 1.23 ginsbach static char *getline(int);
121 1.23 ginsbach static char *getmsg(int, char *, char);
122 1.23 ginsbach static void warning(const char *, const char *);
123 1.23 ginsbach static char *wskip(char *);
124 1.23 ginsbach static char *xstrdup(const char *);
125 1.23 ginsbach static void *xmalloc(size_t);
126 1.23 ginsbach static void *xrealloc(void *, size_t);
127 1.23 ginsbach
128 1.23 ginsbach void MCParse(int fd);
129 1.23 ginsbach void MCReadCat(int fd);
130 1.23 ginsbach void MCWriteCat(int fd);
131 1.23 ginsbach void MCDelMsg(int msgId);
132 1.23 ginsbach void MCAddMsg(int msgId, const char *msg);
133 1.23 ginsbach void MCAddSet(int setId);
134 1.23 ginsbach void MCDelSet(int setId);
135 1.23 ginsbach int main(int, char **);
136 1.23 ginsbach void usage(void);
137 1.7 lukem
138 1.24 ginsbach #define CORRUPT "corrupt message catalog"
139 1.24 ginsbach #define NOMEMORY "out of memory"
140 1.7 lukem
141 1.3 jtc void
142 1.23 ginsbach usage(void)
143 1.3 jtc {
144 1.19 jmmv fprintf(stderr, "usage: %s catfile msgfile ...\n", getprogname());
145 1.3 jtc exit(1);
146 1.3 jtc }
147 1.1 jtc
148 1.3 jtc int
149 1.23 ginsbach main(int argc, char *argv[])
150 1.3 jtc {
151 1.3 jtc int ofd, ifd;
152 1.3 jtc char *catfile = NULL;
153 1.3 jtc int c;
154 1.24 ginsbach int updatecat = 0;
155 1.3 jtc
156 1.3 jtc while ((c = getopt(argc, argv, "")) != -1) {
157 1.3 jtc switch (c) {
158 1.3 jtc case '?':
159 1.3 jtc default:
160 1.3 jtc usage();
161 1.3 jtc /* NOTREACHED */
162 1.3 jtc }
163 1.3 jtc }
164 1.3 jtc argc -= optind;
165 1.3 jtc argv += optind;
166 1.1 jtc
167 1.3 jtc if (argc < 2) {
168 1.1 jtc usage();
169 1.3 jtc /* NOTREACHED */
170 1.3 jtc }
171 1.3 jtc catfile = *argv++;
172 1.3 jtc
173 1.24 ginsbach if ((catfile[0] == '-') && (catfile[1] == '\0')) {
174 1.24 ginsbach ofd = STDOUT_FILENO;
175 1.24 ginsbach } else {
176 1.24 ginsbach ofd = open(catfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
177 1.24 ginsbach if (ofd < 0) {
178 1.24 ginsbach if (errno == EEXIST) {
179 1.24 ginsbach if ((ofd = open(catfile, O_RDWR)) < 0) {
180 1.24 ginsbach err(1, "Unable to open %s", catfile);
181 1.24 ginsbach /* NOTREACHED */
182 1.24 ginsbach }
183 1.24 ginsbach } else {
184 1.24 ginsbach err(1, "Unable to create new %s", catfile);
185 1.24 ginsbach /* NOTREACHED */
186 1.24 ginsbach }
187 1.24 ginsbach curfile = catfile;
188 1.24 ginsbach updatecat = 1;
189 1.24 ginsbach MCReadCat(ofd);
190 1.24 ginsbach if (lseek(ofd, SEEK_SET, 0) < 0) {
191 1.24 ginsbach err(1, "Unable to seek on %s", catfile);
192 1.24 ginsbach /* NOTREACHED */
193 1.24 ginsbach }
194 1.24 ginsbach }
195 1.24 ginsbach }
196 1.24 ginsbach
197 1.24 ginsbach if (((*argv)[0] == '-') && ((*argv)[1] == '\0')) {
198 1.24 ginsbach if (argc != 2)
199 1.24 ginsbach usage();
200 1.24 ginsbach /* NOTREACHED */
201 1.24 ginsbach MCParse(STDIN_FILENO);
202 1.24 ginsbach } else {
203 1.24 ginsbach for (; *argv; argv++) {
204 1.24 ginsbach if ((ifd = open(*argv, O_RDONLY)) < 0)
205 1.24 ginsbach err(1, "Unable to read %s", *argv);
206 1.24 ginsbach curfile = *argv;
207 1.24 ginsbach lineno = 0;
208 1.24 ginsbach MCParse(ifd);
209 1.24 ginsbach close(ifd);
210 1.24 ginsbach }
211 1.24 ginsbach }
212 1.24 ginsbach
213 1.24 ginsbach if (updatecat) {
214 1.24 ginsbach if (ftruncate(ofd, 0) != 0) {
215 1.24 ginsbach err(1, "Unable to truncate %s", catfile);
216 1.24 ginsbach /* NOTREACHED */
217 1.24 ginsbach }
218 1.1 jtc }
219 1.3 jtc
220 1.1 jtc MCWriteCat(ofd);
221 1.1 jtc exit(0);
222 1.3 jtc }
223 1.3 jtc
224 1.3 jtc static void
225 1.23 ginsbach warning(const char *cptr, const char *msg)
226 1.3 jtc {
227 1.24 ginsbach if (lineno) {
228 1.24 ginsbach fprintf(stderr, "%s: %s on line %ld, %s\n",
229 1.24 ginsbach getprogname(), msg, lineno, curfile);
230 1.24 ginsbach fprintf(stderr, "%s\n", curline);
231 1.24 ginsbach if (cptr) {
232 1.24 ginsbach char *tptr;
233 1.24 ginsbach for (tptr = curline; tptr < cptr; ++tptr)
234 1.24 ginsbach putc(' ', stderr);
235 1.24 ginsbach fprintf(stderr, "^\n");
236 1.24 ginsbach }
237 1.24 ginsbach } else {
238 1.24 ginsbach fprintf(stderr, "%s: %s, %s\n", getprogname(), msg, curfile);
239 1.3 jtc }
240 1.3 jtc }
241 1.3 jtc
242 1.3 jtc static void
243 1.23 ginsbach error(const char *msg)
244 1.3 jtc {
245 1.22 ginsbach warning(NULL, msg);
246 1.1 jtc exit(1);
247 1.1 jtc }
248 1.1 jtc
249 1.3 jtc static void *
250 1.23 ginsbach xmalloc(size_t len)
251 1.3 jtc {
252 1.3 jtc void *p;
253 1.3 jtc
254 1.3 jtc if ((p = malloc(len)) == NULL)
255 1.24 ginsbach errx(1, NOMEMORY);
256 1.3 jtc return (p);
257 1.3 jtc }
258 1.3 jtc
259 1.3 jtc static void *
260 1.23 ginsbach xrealloc(void *ptr, size_t size)
261 1.3 jtc {
262 1.3 jtc if ((ptr = realloc(ptr, size)) == NULL)
263 1.24 ginsbach errx(1, NOMEMORY);
264 1.3 jtc return (ptr);
265 1.3 jtc }
266 1.3 jtc
267 1.3 jtc static char *
268 1.23 ginsbach xstrdup(const char *str)
269 1.3 jtc {
270 1.7 lukem char *nstr;
271 1.7 lukem
272 1.7 lukem if ((nstr = strdup(str)) == NULL)
273 1.24 ginsbach errx(1, NOMEMORY);
274 1.7 lukem return (nstr);
275 1.3 jtc }
276 1.3 jtc
277 1.3 jtc static char *
278 1.23 ginsbach getline(int fd)
279 1.3 jtc {
280 1.3 jtc static long curlen = BUFSIZ;
281 1.3 jtc static char buf[BUFSIZ], *bptr = buf, *bend = buf;
282 1.3 jtc char *cptr, *cend;
283 1.3 jtc long buflen;
284 1.3 jtc
285 1.3 jtc if (!curline) {
286 1.3 jtc curline = xmalloc(curlen);
287 1.3 jtc }
288 1.3 jtc ++lineno;
289 1.3 jtc
290 1.3 jtc cptr = curline;
291 1.3 jtc cend = curline + curlen;
292 1.3 jtc for (;;) {
293 1.3 jtc for (; bptr < bend && cptr < cend; ++cptr, ++bptr) {
294 1.3 jtc if (*bptr == '\n') {
295 1.3 jtc *cptr = '\0';
296 1.3 jtc ++bptr;
297 1.3 jtc return (curline);
298 1.3 jtc } else
299 1.3 jtc *cptr = *bptr;
300 1.3 jtc }
301 1.11 yamt if (cptr == cend) {
302 1.11 yamt cptr = curline = xrealloc(curline, curlen *= 2);
303 1.11 yamt cend = curline + curlen;
304 1.11 yamt }
305 1.3 jtc if (bptr == bend) {
306 1.3 jtc buflen = read(fd, buf, BUFSIZ);
307 1.3 jtc if (buflen <= 0) {
308 1.3 jtc if (cptr > curline) {
309 1.3 jtc *cptr = '\0';
310 1.3 jtc return (curline);
311 1.3 jtc }
312 1.3 jtc return (NULL);
313 1.3 jtc }
314 1.3 jtc bend = buf + buflen;
315 1.3 jtc bptr = buf;
316 1.3 jtc }
317 1.3 jtc }
318 1.3 jtc }
319 1.3 jtc
320 1.3 jtc static char *
321 1.23 ginsbach wskip(char *cptr)
322 1.3 jtc {
323 1.9 itohy if (!*cptr || !isspace((unsigned char) *cptr)) {
324 1.3 jtc warning(cptr, "expected a space");
325 1.3 jtc return (cptr);
326 1.3 jtc }
327 1.9 itohy while (*cptr && isspace((unsigned char) *cptr))
328 1.3 jtc ++cptr;
329 1.3 jtc return (cptr);
330 1.3 jtc }
331 1.3 jtc
332 1.3 jtc static char *
333 1.23 ginsbach cskip(char *cptr)
334 1.3 jtc {
335 1.9 itohy if (!*cptr || isspace((unsigned char) *cptr)) {
336 1.3 jtc warning(cptr, "wasn't expecting a space");
337 1.3 jtc return (cptr);
338 1.3 jtc }
339 1.9 itohy while (*cptr && !isspace((unsigned char) *cptr))
340 1.3 jtc ++cptr;
341 1.3 jtc return (cptr);
342 1.3 jtc }
343 1.3 jtc
344 1.3 jtc static char *
345 1.23 ginsbach getmsg(int fd, char *cptr, char quote)
346 1.3 jtc {
347 1.3 jtc static char *msg = NULL;
348 1.3 jtc static long msglen = 0;
349 1.3 jtc long clen, i;
350 1.3 jtc char *tptr;
351 1.3 jtc
352 1.3 jtc if (quote && *cptr == quote) {
353 1.3 jtc ++cptr;
354 1.3 jtc }
355 1.3 jtc
356 1.3 jtc clen = strlen(cptr) + 1;
357 1.3 jtc if (clen > msglen) {
358 1.3 jtc if (msglen)
359 1.3 jtc msg = xrealloc(msg, clen);
360 1.3 jtc else
361 1.3 jtc msg = xmalloc(clen);
362 1.3 jtc msglen = clen;
363 1.3 jtc }
364 1.3 jtc tptr = msg;
365 1.3 jtc
366 1.3 jtc while (*cptr) {
367 1.3 jtc if (quote && *cptr == quote) {
368 1.3 jtc char *tmp;
369 1.3 jtc tmp = cptr + 1;
370 1.9 itohy if (*tmp && (!isspace((unsigned char) *tmp) || *wskip(tmp))) {
371 1.8 mjacob warning(cptr, "unexpected quote character, ignoring");
372 1.3 jtc *tptr++ = *cptr++;
373 1.3 jtc } else {
374 1.3 jtc *cptr = '\0';
375 1.3 jtc }
376 1.26 ginsbach } else {
377 1.3 jtc if (*cptr == '\\') {
378 1.3 jtc ++cptr;
379 1.3 jtc switch (*cptr) {
380 1.3 jtc case '\0':
381 1.3 jtc cptr = getline(fd);
382 1.3 jtc if (!cptr)
383 1.22 ginsbach error("premature end of file");
384 1.3 jtc msglen += strlen(cptr);
385 1.3 jtc i = tptr - msg;
386 1.3 jtc msg = xrealloc(msg, msglen);
387 1.3 jtc tptr = msg + i;
388 1.3 jtc break;
389 1.3 jtc case 'n':
390 1.3 jtc *tptr++ = '\n';
391 1.3 jtc ++cptr;
392 1.3 jtc break;
393 1.3 jtc case 't':
394 1.3 jtc *tptr++ = '\t';
395 1.3 jtc ++cptr;
396 1.3 jtc break;
397 1.3 jtc case 'v':
398 1.3 jtc *tptr++ = '\v';
399 1.3 jtc ++cptr;
400 1.3 jtc break;
401 1.3 jtc case 'b':
402 1.3 jtc *tptr++ = '\b';
403 1.3 jtc ++cptr;
404 1.3 jtc break;
405 1.3 jtc case 'r':
406 1.3 jtc *tptr++ = '\r';
407 1.3 jtc ++cptr;
408 1.3 jtc break;
409 1.3 jtc case 'f':
410 1.3 jtc *tptr++ = '\f';
411 1.3 jtc ++cptr;
412 1.3 jtc break;
413 1.3 jtc case '\\':
414 1.3 jtc *tptr++ = '\\';
415 1.3 jtc ++cptr;
416 1.3 jtc break;
417 1.3 jtc default:
418 1.8 mjacob if (quote && *cptr == quote) {
419 1.8 mjacob *tptr++ = *cptr++;
420 1.9 itohy } else if (isdigit((unsigned char) *cptr)) {
421 1.3 jtc *tptr = 0;
422 1.3 jtc for (i = 0; i < 3; ++i) {
423 1.9 itohy if (!isdigit((unsigned char) *cptr))
424 1.3 jtc break;
425 1.3 jtc if (*cptr > '7')
426 1.3 jtc warning(cptr, "octal number greater than 7?!");
427 1.3 jtc *tptr *= 8;
428 1.3 jtc *tptr += (*cptr - '0');
429 1.3 jtc ++cptr;
430 1.3 jtc }
431 1.3 jtc } else {
432 1.3 jtc warning(cptr, "unrecognized escape sequence");
433 1.3 jtc }
434 1.8 mjacob break;
435 1.3 jtc }
436 1.3 jtc } else {
437 1.3 jtc *tptr++ = *cptr++;
438 1.3 jtc }
439 1.26 ginsbach }
440 1.3 jtc }
441 1.3 jtc *tptr = '\0';
442 1.3 jtc return (msg);
443 1.3 jtc }
444 1.3 jtc
445 1.3 jtc void
446 1.23 ginsbach MCParse(int fd)
447 1.3 jtc {
448 1.3 jtc char *cptr, *str;
449 1.21 ginsbach int msgid = 0;
450 1.21 ginsbach int setid = 0;
451 1.3 jtc char quote = 0;
452 1.3 jtc
453 1.3 jtc /* XXX: init sethead? */
454 1.3 jtc
455 1.3 jtc while ((cptr = getline(fd))) {
456 1.3 jtc if (*cptr == '$') {
457 1.3 jtc ++cptr;
458 1.3 jtc if (strncmp(cptr, "set", 3) == 0) {
459 1.3 jtc cptr += 3;
460 1.3 jtc cptr = wskip(cptr);
461 1.3 jtc setid = atoi(cptr);
462 1.3 jtc MCAddSet(setid);
463 1.3 jtc msgid = 0;
464 1.3 jtc } else if (strncmp(cptr, "delset", 6) == 0) {
465 1.3 jtc cptr += 6;
466 1.3 jtc cptr = wskip(cptr);
467 1.3 jtc setid = atoi(cptr);
468 1.3 jtc MCDelSet(setid);
469 1.3 jtc } else if (strncmp(cptr, "quote", 5) == 0) {
470 1.3 jtc cptr += 5;
471 1.3 jtc if (!*cptr)
472 1.3 jtc quote = 0;
473 1.3 jtc else {
474 1.3 jtc cptr = wskip(cptr);
475 1.3 jtc if (!*cptr)
476 1.3 jtc quote = 0;
477 1.3 jtc else
478 1.3 jtc quote = *cptr;
479 1.3 jtc }
480 1.9 itohy } else if (isspace((unsigned char) *cptr)) {
481 1.3 jtc ;
482 1.3 jtc } else {
483 1.3 jtc if (*cptr) {
484 1.3 jtc cptr = wskip(cptr);
485 1.3 jtc if (*cptr)
486 1.3 jtc warning(cptr, "unrecognized line");
487 1.3 jtc }
488 1.3 jtc }
489 1.3 jtc } else {
490 1.8 mjacob /*
491 1.8 mjacob * First check for (and eat) empty lines....
492 1.8 mjacob */
493 1.8 mjacob if (!*cptr)
494 1.8 mjacob continue;
495 1.8 mjacob /*
496 1.8 mjacob * We have a digit? Start of a message. Else,
497 1.8 mjacob * syntax error.
498 1.8 mjacob */
499 1.9 itohy if (isdigit((unsigned char) *cptr)) {
500 1.3 jtc msgid = atoi(cptr);
501 1.3 jtc cptr = cskip(cptr);
502 1.26 ginsbach if (*cptr) {
503 1.24 ginsbach cptr = wskip(cptr);
504 1.26 ginsbach if (!*cptr) {
505 1.26 ginsbach MCAddMsg(msgid, "");
506 1.26 ginsbach continue;
507 1.26 ginsbach }
508 1.26 ginsbach }
509 1.8 mjacob } else {
510 1.8 mjacob warning(cptr, "neither blank line nor start of a message id");
511 1.8 mjacob continue;
512 1.3 jtc }
513 1.8 mjacob /*
514 1.21 ginsbach * If no set directive specified, all messages
515 1.21 ginsbach * shall be in default message set NL_SETD.
516 1.21 ginsbach */
517 1.21 ginsbach if (setid == 0) {
518 1.21 ginsbach setid = NL_SETD;
519 1.21 ginsbach MCAddSet(setid);
520 1.21 ginsbach }
521 1.21 ginsbach /*
522 1.8 mjacob * If we have a message ID, but no message,
523 1.8 mjacob * then this means "delete this message id
524 1.8 mjacob * from the catalog".
525 1.8 mjacob */
526 1.8 mjacob if (!*cptr) {
527 1.3 jtc MCDelMsg(msgid);
528 1.8 mjacob } else {
529 1.3 jtc str = getmsg(fd, cptr, quote);
530 1.3 jtc MCAddMsg(msgid, str);
531 1.3 jtc }
532 1.3 jtc }
533 1.3 jtc }
534 1.3 jtc }
535 1.3 jtc
536 1.3 jtc void
537 1.23 ginsbach MCReadCat(int fd)
538 1.3 jtc {
539 1.24 ginsbach void *msgcat; /* message catalog data */
540 1.24 ginsbach struct _nls_cat_hdr cat_hdr;
541 1.24 ginsbach struct _nls_set_hdr *set_hdr;
542 1.24 ginsbach struct _nls_msg_hdr *msg_hdr;
543 1.24 ginsbach char *strings;
544 1.24 ginsbach int m, n, s;
545 1.24 ginsbach int msgno, setno;
546 1.3 jtc
547 1.3 jtc /* XXX init sethead? */
548 1.3 jtc
549 1.24 ginsbach n = read(fd, &cat_hdr, sizeof(cat_hdr));
550 1.24 ginsbach if (n < sizeof(cat_hdr)) {
551 1.24 ginsbach if (n == 0)
552 1.24 ginsbach return; /* empty file */
553 1.24 ginsbach else if (n == -1)
554 1.24 ginsbach err(1, "header read");
555 1.24 ginsbach else
556 1.24 ginsbach errx(1, CORRUPT);
557 1.24 ginsbach }
558 1.24 ginsbach if (ntohl(cat_hdr.__magic) != _NLS_MAGIC)
559 1.24 ginsbach errx(1, "%s: bad magic number (%#x)", CORRUPT, cat_hdr.__magic);
560 1.3 jtc
561 1.24 ginsbach cat_hdr.__mem = ntohl(cat_hdr.__mem);
562 1.24 ginsbach msgcat = xmalloc(cat_hdr.__mem);
563 1.3 jtc
564 1.24 ginsbach cat_hdr.__nsets = ntohl(cat_hdr.__nsets);
565 1.24 ginsbach cat_hdr.__msg_hdr_offset = ntohl(cat_hdr.__msg_hdr_offset);
566 1.24 ginsbach cat_hdr.__msg_txt_offset = ntohl(cat_hdr.__msg_txt_offset);
567 1.24 ginsbach if ((cat_hdr.__mem < 0) ||
568 1.24 ginsbach (cat_hdr.__msg_hdr_offset < 0) ||
569 1.24 ginsbach (cat_hdr.__msg_txt_offset < 0) ||
570 1.24 ginsbach (cat_hdr.__mem < (cat_hdr.__nsets * sizeof(struct _nls_set_hdr))) ||
571 1.24 ginsbach (cat_hdr.__mem < cat_hdr.__msg_hdr_offset) ||
572 1.24 ginsbach (cat_hdr.__mem < cat_hdr.__msg_txt_offset))
573 1.24 ginsbach errx(1, "%s: catalog header", CORRUPT);
574 1.24 ginsbach
575 1.24 ginsbach n = read(fd, msgcat, cat_hdr.__mem);
576 1.24 ginsbach if (n < cat_hdr.__mem) {
577 1.24 ginsbach if (n == -1)
578 1.24 ginsbach err(1, "data read");
579 1.24 ginsbach else
580 1.24 ginsbach errx(1, CORRUPT);
581 1.24 ginsbach }
582 1.3 jtc
583 1.24 ginsbach set_hdr = (struct _nls_set_hdr *)msgcat;
584 1.24 ginsbach msg_hdr = (struct _nls_msg_hdr *)((char *)msgcat +
585 1.24 ginsbach cat_hdr.__msg_hdr_offset);
586 1.24 ginsbach strings = (char *)msgcat + cat_hdr.__msg_txt_offset;
587 1.24 ginsbach
588 1.24 ginsbach setno = 0;
589 1.24 ginsbach for (s = 0; s < cat_hdr.__nsets; s++, set_hdr++) {
590 1.24 ginsbach set_hdr->__setno = ntohl(set_hdr->__setno);
591 1.24 ginsbach if (set_hdr->__setno < setno)
592 1.24 ginsbach errx(1, "%s: bad set number (%d)",
593 1.24 ginsbach CORRUPT, set_hdr->__setno);
594 1.24 ginsbach setno = set_hdr->__setno;
595 1.24 ginsbach
596 1.24 ginsbach MCAddSet(setno);
597 1.24 ginsbach
598 1.24 ginsbach set_hdr->__nmsgs = ntohl(set_hdr->__nmsgs);
599 1.24 ginsbach set_hdr->__index = ntohl(set_hdr->__index);
600 1.24 ginsbach if (set_hdr->__nmsgs < 0 || set_hdr->__index < 0)
601 1.24 ginsbach errx(1, "%s: set header", CORRUPT);
602 1.3 jtc
603 1.3 jtc /* Get the data */
604 1.24 ginsbach msgno = 0;
605 1.24 ginsbach for (m = 0; m < set_hdr->__nmsgs; m++, msg_hdr++) {
606 1.24 ginsbach msg_hdr->__msgno = ntohl(msg_hdr->__msgno);
607 1.24 ginsbach msg_hdr->__offset = ntohl(msg_hdr->__offset);
608 1.24 ginsbach if (msg_hdr->__msgno < msgno)
609 1.24 ginsbach errx(1, "%s: bad message number (%d)",
610 1.24 ginsbach CORRUPT, msg_hdr->__msgno);
611 1.24 ginsbach if ((msg_hdr->__offset < 0) ||
612 1.24 ginsbach ((strings + msg_hdr->__offset) >
613 1.24 ginsbach ((char *)msgcat + cat_hdr.__mem)))
614 1.24 ginsbach errx(1, "%s: message header", CORRUPT);
615 1.3 jtc
616 1.24 ginsbach msgno = msg_hdr->__msgno;
617 1.24 ginsbach MCAddMsg(msgno, strings + msg_hdr->__offset);
618 1.3 jtc }
619 1.3 jtc }
620 1.24 ginsbach free(msgcat);
621 1.3 jtc }
622 1.3 jtc
623 1.3 jtc /*
624 1.3 jtc * Write message catalog.
625 1.3 jtc *
626 1.3 jtc * The message catalog is first converted from its internal to its
627 1.3 jtc * external representation in a chunk of memory allocated for this
628 1.3 jtc * purpose. Then the completed catalog is written. This approach
629 1.3 jtc * avoids additional housekeeping variables and/or a lot of seeks
630 1.3 jtc * that would otherwise be required.
631 1.3 jtc */
632 1.3 jtc void
633 1.23 ginsbach MCWriteCat(int fd)
634 1.1 jtc {
635 1.3 jtc int nsets; /* number of sets */
636 1.3 jtc int nmsgs; /* number of msgs */
637 1.3 jtc int string_size; /* total size of string pool */
638 1.3 jtc int msgcat_size; /* total size of message catalog */
639 1.3 jtc void *msgcat; /* message catalog data */
640 1.3 jtc struct _nls_cat_hdr *cat_hdr;
641 1.3 jtc struct _nls_set_hdr *set_hdr;
642 1.3 jtc struct _nls_msg_hdr *msg_hdr;
643 1.3 jtc char *strings;
644 1.3 jtc struct _setT *set;
645 1.3 jtc struct _msgT *msg;
646 1.3 jtc int msg_index;
647 1.3 jtc int msg_offset;
648 1.3 jtc
649 1.3 jtc /* determine number of sets, number of messages, and size of the
650 1.3 jtc * string pool */
651 1.3 jtc nsets = 0;
652 1.3 jtc nmsgs = 0;
653 1.3 jtc string_size = 0;
654 1.3 jtc
655 1.3 jtc for (set = sethead.lh_first; set != NULL;
656 1.3 jtc set = set->entries.le_next) {
657 1.3 jtc nsets++;
658 1.3 jtc
659 1.3 jtc for (msg = set->msghead.lh_first; msg != NULL;
660 1.3 jtc msg = msg->entries.le_next) {
661 1.3 jtc nmsgs++;
662 1.3 jtc string_size += strlen(msg->str) + 1;
663 1.3 jtc }
664 1.3 jtc }
665 1.3 jtc
666 1.3 jtc #ifdef DEBUG
667 1.3 jtc printf("number of sets: %d\n", nsets);
668 1.3 jtc printf("number of msgs: %d\n", nmsgs);
669 1.3 jtc printf("string pool size: %d\n", string_size);
670 1.3 jtc #endif
671 1.3 jtc
672 1.3 jtc /* determine size and then allocate buffer for constructing external
673 1.3 jtc * message catalog representation */
674 1.3 jtc msgcat_size = sizeof(struct _nls_cat_hdr)
675 1.3 jtc + (nsets * sizeof(struct _nls_set_hdr))
676 1.3 jtc + (nmsgs * sizeof(struct _nls_msg_hdr))
677 1.3 jtc + string_size;
678 1.3 jtc
679 1.3 jtc msgcat = xmalloc(msgcat_size);
680 1.3 jtc memset(msgcat, '\0', msgcat_size);
681 1.3 jtc
682 1.3 jtc /* fill in msg catalog header */
683 1.3 jtc cat_hdr = (struct _nls_cat_hdr *) msgcat;
684 1.3 jtc cat_hdr->__magic = htonl(_NLS_MAGIC);
685 1.3 jtc cat_hdr->__nsets = htonl(nsets);
686 1.3 jtc cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr));
687 1.3 jtc cat_hdr->__msg_hdr_offset =
688 1.3 jtc htonl(nsets * sizeof(struct _nls_set_hdr));
689 1.3 jtc cat_hdr->__msg_txt_offset =
690 1.3 jtc htonl(nsets * sizeof(struct _nls_set_hdr) +
691 1.3 jtc nmsgs * sizeof(struct _nls_msg_hdr));
692 1.3 jtc
693 1.3 jtc /* compute offsets for set & msg header tables and string pool */
694 1.3 jtc set_hdr = (struct _nls_set_hdr *) ((char *) msgcat +
695 1.3 jtc sizeof(struct _nls_cat_hdr));
696 1.3 jtc msg_hdr = (struct _nls_msg_hdr *) ((char *) msgcat +
697 1.3 jtc sizeof(struct _nls_cat_hdr) +
698 1.3 jtc nsets * sizeof(struct _nls_set_hdr));
699 1.3 jtc strings = (char *) msgcat +
700 1.3 jtc sizeof(struct _nls_cat_hdr) +
701 1.3 jtc nsets * sizeof(struct _nls_set_hdr) +
702 1.3 jtc nmsgs * sizeof(struct _nls_msg_hdr);
703 1.3 jtc
704 1.3 jtc msg_index = 0;
705 1.3 jtc msg_offset = 0;
706 1.3 jtc for (set = sethead.lh_first; set != NULL;
707 1.3 jtc set = set->entries.le_next) {
708 1.3 jtc
709 1.3 jtc nmsgs = 0;
710 1.3 jtc for (msg = set->msghead.lh_first; msg != NULL;
711 1.3 jtc msg = msg->entries.le_next) {
712 1.3 jtc int msg_len = strlen(msg->str) + 1;
713 1.3 jtc
714 1.3 jtc msg_hdr->__msgno = htonl(msg->msgId);
715 1.3 jtc msg_hdr->__msglen = htonl(msg_len);
716 1.3 jtc msg_hdr->__offset = htonl(msg_offset);
717 1.3 jtc
718 1.3 jtc memcpy(strings, msg->str, msg_len);
719 1.3 jtc strings += msg_len;
720 1.3 jtc msg_offset += msg_len;
721 1.3 jtc
722 1.3 jtc nmsgs++;
723 1.3 jtc msg_hdr++;
724 1.3 jtc }
725 1.3 jtc
726 1.3 jtc set_hdr->__setno = htonl(set->setId);
727 1.3 jtc set_hdr->__nmsgs = htonl(nmsgs);
728 1.3 jtc set_hdr->__index = htonl(msg_index);
729 1.3 jtc msg_index += nmsgs;
730 1.3 jtc set_hdr++;
731 1.3 jtc }
732 1.3 jtc
733 1.3 jtc /* write out catalog. XXX: should this be done in small chunks? */
734 1.3 jtc write(fd, msgcat, msgcat_size);
735 1.3 jtc }
736 1.3 jtc
737 1.3 jtc void
738 1.23 ginsbach MCAddSet(int setId)
739 1.3 jtc {
740 1.3 jtc struct _setT *p, *q;
741 1.3 jtc
742 1.3 jtc if (setId <= 0) {
743 1.22 ginsbach error("setId's must be greater than zero");
744 1.3 jtc /* NOTREACHED */
745 1.3 jtc }
746 1.3 jtc if (setId > NL_SETMAX) {
747 1.22 ginsbach error("setId exceeds limit");
748 1.3 jtc /* NOTREACHED */
749 1.3 jtc }
750 1.3 jtc
751 1.3 jtc p = sethead.lh_first;
752 1.3 jtc q = NULL;
753 1.3 jtc for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next);
754 1.3 jtc
755 1.3 jtc if (p && p->setId == setId) {
756 1.3 jtc ;
757 1.3 jtc } else {
758 1.3 jtc p = xmalloc(sizeof(struct _setT));
759 1.3 jtc memset(p, '\0', sizeof(struct _setT));
760 1.3 jtc LIST_INIT(&p->msghead);
761 1.3 jtc
762 1.3 jtc p->setId = setId;
763 1.3 jtc
764 1.3 jtc if (q == NULL) {
765 1.3 jtc LIST_INSERT_HEAD(&sethead, p, entries);
766 1.3 jtc } else {
767 1.3 jtc LIST_INSERT_AFTER(q, p, entries);
768 1.3 jtc }
769 1.3 jtc }
770 1.3 jtc
771 1.3 jtc curSet = p;
772 1.3 jtc }
773 1.3 jtc
774 1.3 jtc void
775 1.23 ginsbach MCAddMsg(int msgId, const char *str)
776 1.3 jtc {
777 1.3 jtc struct _msgT *p, *q;
778 1.3 jtc
779 1.3 jtc if (!curSet)
780 1.22 ginsbach error("can't specify a message when no set exists");
781 1.3 jtc
782 1.3 jtc if (msgId <= 0) {
783 1.22 ginsbach error("msgId's must be greater than zero");
784 1.3 jtc /* NOTREACHED */
785 1.3 jtc }
786 1.12 yamt if (msgId > NL_MSGMAX) {
787 1.22 ginsbach error("msgID exceeds limit");
788 1.3 jtc /* NOTREACHED */
789 1.3 jtc }
790 1.3 jtc
791 1.3 jtc p = curSet->msghead.lh_first;
792 1.3 jtc q = NULL;
793 1.3 jtc for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next);
794 1.3 jtc
795 1.3 jtc if (p && p->msgId == msgId) {
796 1.3 jtc free(p->str);
797 1.3 jtc } else {
798 1.3 jtc p = xmalloc(sizeof(struct _msgT));
799 1.3 jtc memset(p, '\0', sizeof(struct _msgT));
800 1.3 jtc
801 1.3 jtc if (q == NULL) {
802 1.3 jtc LIST_INSERT_HEAD(&curSet->msghead, p, entries);
803 1.3 jtc } else {
804 1.3 jtc LIST_INSERT_AFTER(q, p, entries);
805 1.3 jtc }
806 1.3 jtc }
807 1.3 jtc
808 1.3 jtc p->msgId = msgId;
809 1.3 jtc p->str = xstrdup(str);
810 1.3 jtc }
811 1.3 jtc
812 1.3 jtc void
813 1.23 ginsbach MCDelSet(int setId)
814 1.3 jtc {
815 1.3 jtc struct _setT *set;
816 1.3 jtc struct _msgT *msg;
817 1.3 jtc
818 1.21 ginsbach if (setId <= 0) {
819 1.22 ginsbach error("setId's must be greater than zero");
820 1.21 ginsbach /* NOTREACHED */
821 1.21 ginsbach }
822 1.21 ginsbach if (setId > NL_SETMAX) {
823 1.22 ginsbach error("setId exceeds limit");
824 1.21 ginsbach /* NOTREACHED */
825 1.21 ginsbach }
826 1.21 ginsbach
827 1.3 jtc set = sethead.lh_first;
828 1.3 jtc for (; set != NULL && set->setId < setId; set = set->entries.le_next);
829 1.3 jtc
830 1.3 jtc if (set && set->setId == setId) {
831 1.20 dsl LIST_REMOVE(set, entries);
832 1.20 dsl while ((msg = set->msghead.lh_first) != NULL) {
833 1.20 dsl LIST_REMOVE(msg, entries);
834 1.3 jtc free(msg->str);
835 1.20 dsl free(msg);
836 1.3 jtc }
837 1.20 dsl free(set);
838 1.3 jtc return;
839 1.3 jtc }
840 1.3 jtc warning(NULL, "specified set doesn't exist");
841 1.3 jtc }
842 1.1 jtc
843 1.3 jtc void
844 1.23 ginsbach MCDelMsg(int msgId)
845 1.3 jtc {
846 1.3 jtc struct _msgT *msg;
847 1.1 jtc
848 1.3 jtc if (!curSet)
849 1.22 ginsbach error("you can't delete a message before defining the set");
850 1.1 jtc
851 1.3 jtc msg = curSet->msghead.lh_first;
852 1.3 jtc for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next);
853 1.1 jtc
854 1.3 jtc if (msg && msg->msgId == msgId) {
855 1.20 dsl LIST_REMOVE(msg, entries);
856 1.3 jtc free(msg->str);
857 1.20 dsl free(msg);
858 1.3 jtc return;
859 1.3 jtc }
860 1.3 jtc warning(NULL, "specified msg doesn't exist");
861 1.1 jtc }
862