2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $XConsortium: msgcat.c /main/3 1996/06/19 17:16:22 drk $ */
25 X/Open message catalogue functions and gencat utility.
27 Written by James Clark (jjc@jclark.com).
34 /* In this implementation the message catalogue format is the same as the
35 message text source file format (see pp 42-43 of the X/Open
36 Portability Guide, Issue 3, Volume 3.) This means that you don't have
37 to use the gencat utility, but it is still useful for checking and
38 merging catalogues. */
40 /* Compile this with -DGENCAT to get the gencat utility. */
46 #define P(parms) parms
51 /* Default message set. */
55 #define PATH_FILE_SEP ':'
58 #ifndef DEFAULT_NLSPATH
59 #define DEFAULT_NLSPATH ""
63 #define DEFAULT_LANG "default"
66 #define HASH_TAB_SIZE 251
79 struct message *table[HASH_TAB_SIZE];
82 static char *read_buf = 0;
83 static unsigned read_buf_len = 0;
85 /* Errors that can be generated by read_catalog. */
88 E_ZERO, /* not an error */
99 /* These must match enum cat_err. */
100 static char *cat_errlist[] = {
102 "Invalid argument to command",
104 "Unrecognized command",
106 "Unexpected end of file",
107 "Space or tab expected after message number",
113 /* The value of NLSPATH. */
114 static char *nlspath = 0;
115 /* The value of LANG. */
116 static char *lang = 0;
117 #endif /* not GENCAT */
119 static int current_lineno = -1;
120 static enum cat_err cat_errno = E_ZERO;
123 static void load_catalog P((struct cat *));
124 static FILE *find_catalog P((char *, char **));
126 static int read_catalog P((FILE *, struct message **));
127 static void delete_set P((struct message **, unsigned));
128 static void delete_message P((struct message **, unsigned, unsigned));
129 static int hash P((unsigned setnum, unsigned msgnum));
130 static char *parse_text P((FILE *, int));
134 nl_catd catopen(name, oflag)
144 catp = (struct cat *)malloc(sizeof *catp);
147 for (i = 0; i < HASH_TAB_SIZE; i++)
149 catp->name = malloc(strlen(name) + 1);
152 strcpy(catp->name, name);
153 return (nl_catd)catp;
160 struct cat *catp = (struct cat *)catd;
165 for (i = 0; i < HASH_TAB_SIZE; i++) {
166 struct message *p, *nextp;
167 for (p = catp->table[i]; p; p = nextp) {
179 char *catgets(catd, setnum, msgnum, dflt)
187 /* setnum and msgnum are required to be >= 1. */
188 if (!catd || setnum <= 0 || msgnum <= 0)
190 catp = (struct cat *)catd;
195 for (p = catp->table[hash(setnum, msgnum)]; p; p = p->next)
196 if (p->msgnum == msgnum && p->setnum == setnum)
204 VOID load_catalog(catp)
211 fp = find_catalog(catp->name, &path);
217 if (read_catalog(fp, catp->table) < 0)
229 FILE *find_catalog(name, pathp)
238 nlspath = getenv("NLSPATH");
240 nlspath = DEFAULT_NLSPATH;
243 lang = getenv("LANG");
252 for (p = path; *p != '\0' && *p != PATH_FILE_SEP; p++) {
258 else if (p[1] == 'L') {
262 else if (p[1] == '%') {
276 s = try = malloc(len + 1);
279 for (p = path; *p != '\0' && *p != PATH_FILE_SEP; p++) {
286 else if (p[1] == 'L') {
291 else if (p[1] == '%') {
302 fp = fopen(try, "r");
316 #endif /* not GENCAT */
318 /* 0 success, -1 error */
321 int parse_message(c, fp, table, setnum, quote)
324 struct message **table;
329 struct message *msgp;
338 msgnum = msgnum*10 + (c - '0');
341 delete_message(table, setnum, msgnum);
344 if (c != ' ' && c != '\t') {
345 cat_errno = E_BADSEP;
348 text = parse_text(fp, quote);
351 hc = hash(setnum, msgnum);
352 for (msgp = table[hc]; msgp; msgp = msgp->next)
353 if (msgp->setnum == setnum && msgp->msgnum == msgnum)
358 msgp = (struct message *)malloc(sizeof *msgp);
363 msgp->next = table[hc];
365 msgp->msgnum = msgnum;
366 msgp->setnum = setnum;
373 char *parse_text(fp, quote)
389 for (;; c = getc(fp)) {
401 Can quotes be used in quoted message text if protected by \ ?
403 Is it illegal to omit the closing quote if there's an opening
406 Is it illegal to have anything after a closing quote?
410 if (quoted && c == quote) {
411 /* Skip the rest of the line. */
412 while ((c = getc(fp)) != '\n')
463 if (d >= '0' && d <= '7') {
466 if (d >= '0' && d <= '7')
477 /* Ignore the quote. */
481 if (i >= read_buf_len) {
483 read_buf = malloc(read_buf_len = 40);
485 read_buf = realloc(read_buf, read_buf_len *= 2);
498 memcpy(p, read_buf, i);
503 /* 0 success, -1 error */
506 int parse_command(fp, table, setnump, quotep)
508 struct message **table;
513 if (fgets(buf, 128, fp) == NULL) {
514 cat_errno = ferror(fp) ? E_INPUT : E_EOF;
517 if (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n')
519 else if (strncmp(buf, "set", 3) == 0) {
520 if (sscanf(buf + 3, "%u", setnump) != 1) {
521 cat_errno = E_BADARG;
526 else if (strncmp(buf, "delset", 6) == 0) {
528 if (sscanf(buf + 6, "%u", &num) != 1) {
529 cat_errno = E_BADARG;
532 delete_set(table, num);
535 else if (strncmp(buf, "quote", 5) == 0) {
537 while (*p == ' ' || *p == '\t')
539 /* XXX should \ be allowed as the quote character? */
540 if (*p == '\0' || *p == '\n')
546 cat_errno = E_NOSUCHCOMMAND;
549 if (strchr(buf, '\n') == 0) {
551 while ((c = getc(fp)) != '\n' && c != EOF)
559 VOID delete_set(table, setnum)
560 struct message **table;
565 for (i = 0; i < HASH_TAB_SIZE; i++) {
566 struct message *p, *nextp;
567 for (p = table[i], table[i] = 0; p; p = nextp) {
569 if (p->setnum == setnum)
580 VOID delete_message(table, setnum, msgnum)
581 struct message **table;
582 unsigned setnum, msgnum;
586 for (pp = &table[hash(setnum, msgnum)]; *pp; pp = &(*pp)->next)
587 if ((*pp)->setnum == setnum && (*pp)->msgnum == msgnum) {
588 struct message *p = *pp;
596 /* 0 success, -1 error. On error cat_errno is set to the error number. */
599 int read_catalog(fp, table)
601 struct message **table;
604 unsigned setnum = NL_SETD;
614 if (parse_message(c, fp, table, setnum, quote_char) < 0)
618 if (parse_command(fp, table, &setnum, "e_char) < 0)
621 else if (c != '\n') {
622 while ((c = getc(fp)) != '\n' && c != EOF)
623 if (c != ' ' && c != '\t') {
624 cat_errno = E_BADLINE;
635 int hash(setnum, msgnum)
636 unsigned setnum, msgnum;
638 return ((setnum << 8) + msgnum) % HASH_TAB_SIZE;
643 static char *program_name;
645 static int message_compare P((UNIV, UNIV));
646 static void print_text P((char *, FILE *));
647 static void usage P((void));
652 static void fatal P((char *,...));
661 struct message **list;
663 struct message *table[HASH_TAB_SIZE];
665 program_name = argv[0];
670 for (i = 0; i < HASH_TAB_SIZE; i++)
672 for (i = 1; i < argc; i++) {
674 fp = fopen(argv[i], "r");
677 fatal("can't open `%s': %s", argv[i], strerror(errno));
682 if (read_catalog(fp, table) < 0) {
683 assert(cat_errno != E_ZERO);
685 < sizeof(cat_errlist)/sizeof(cat_errlist[0]));
686 fatal("%s:%d: %s", argv[i], current_lineno,
687 cat_errlist[cat_errno]);
694 fp = fopen(argv[1], "w");
696 fatal("can't open `%s' for output: %s", argv[1], strerror(errno));
698 for (i = 0; i < HASH_TAB_SIZE; i++) {
700 for (p = table[i]; p; p = p->next)
703 list = (struct message **)malloc(nmessages*sizeof(struct message *));
705 fatal("out of memory");
707 for (i = 0; i < HASH_TAB_SIZE; i++) {
709 for (p = table[i]; p; p = p->next)
712 assert(j == nmessages);
714 qsort((UNIV)list, nmessages, sizeof(struct message *), message_compare);
717 for (i = 0; i < nmessages; i++) {
718 struct message *p = list[i];
719 if (p->setnum != setnum) {
721 fprintf(fp, "$set %u\n", setnum);
723 fprintf(fp, "%u ", p->msgnum);
724 print_text(p->text, fp);
727 if (fclose(fp) == EOF)
728 fatal("error closing `%s'", argv[1]);
735 fprintf(stderr, "usage: %s catfile msgfile...\n", program_name);
741 VOID fatal(va_alist) va_dcl
742 #else /* not VARARGS */
743 VOID fatal(char *message,...)
744 #endif /* not VARARGS */
751 message = va_arg(ap, char *);
752 #else /* not VARARGS */
753 va_start(ap, message);
754 #endif /* not VARARGS */
756 fprintf(stderr, "%s: ", program_name);
757 vfprintf(stderr, message, ap);
764 int message_compare(p1, p2)
767 struct message *m1 = *(struct message **)p1;
768 struct message *m2 = *(struct message **)p2;
770 if (m1->setnum < m2->setnum)
772 if (m1->setnum > m2->setnum)
774 if (m1->msgnum < m2->msgnum)
776 if (m1->msgnum > m2->msgnum)
782 VOID print_text(s, fp)
789 else if (ISASCII(*s) && isprint((UNCH)*s))
812 fprintf(fp, "\\%03o", (unsigned char)*s);
831 fprintf(stderr, "usage: %s catalogue\n", argv[0]);
834 catd = catopen(argv[1], 0);
835 fprintf(stderr, "Enter set number, message number pairs:\n");
837 while (scanf("%d %d", &setnum, &msgnum) == 2) {
838 char *msg = catgets(catd, setnum, msgnum, "<default>");
839 fprintf(stderr, "Returned \"%s\"\n", msg);
847 #endif /* not HAVE_CAT */
851 c-continued-statement-offset: 5