home *** CD-ROM | disk | FTP | other *** search
- /* msgcat.c -
- X/Open message catalogue functions and gencat utility.
-
- Written by James Clark (jjc@jclark.com).
- */
-
- #include "config.h"
-
- #ifndef HAVE_CAT
-
- /* In this implementation the message catalogue format is the same as the
- message text source file format (see pp 42-43 of the X/Open
- Portability Guide, Issue 3, Volume 3.) This means that you don't have
- to use the gencat utility, but it is still useful for checking and
- merging catalogues. */
-
- /* Compile this with -DGENCAT to get the gencat utility. */
-
- #include "std.h"
- #include "msgcat.h"
-
- #ifdef USE_PROTOTYPES
- #define P(parms) parms
- #else
- #define P(parms) ()
- #endif
-
- /* Default message set. */
- #define NL_SETD 1
-
- #ifndef PATH_FILE_SEP
- #define PATH_FILE_SEP ':'
- #endif
-
- #ifndef DEFAULT_NLSPATH
- #define DEFAULT_NLSPATH ""
- #endif
-
- #ifndef DEFAULT_LANG
- #define DEFAULT_LANG "default"
- #endif
-
- #define HASH_TAB_SIZE 251
-
- struct message {
- struct message *next;
- unsigned msgnum;
- unsigned setnum;
- char *text;
- };
-
- struct cat {
- char *name;
- int loaded;
- int bad;
- struct message *table[HASH_TAB_SIZE];
- };
-
- static char *read_buf = 0;
- static unsigned read_buf_len = 0;
-
- /* Errors that can be generated by read_catalog. */
-
- enum cat_err {
- E_ZERO, /* not an error */
- E_BADARG,
- E_NOMEM,
- E_NOSUCHCOMMAND,
- E_INPUT,
- E_EOF,
- E_BADSEP,
- E_BADLINE
- };
-
- #ifdef GENCAT
- /* These must match enum cat_err. */
- static char *cat_errlist[] = {
- "Error 0",
- "Invalid argument to command",
- "Out of memory",
- "Unrecognized command",
- "Input error",
- "Unexpected end of file",
- "Space or tab expected after message number",
- "Invalid line",
- };
- #endif /* GENCAT */
-
- #ifndef GENCAT
- /* The value of NLSPATH. */
- static char *nlspath = 0;
- /* The value of LANG. */
- static char *lang = 0;
- #endif /* not GENCAT */
-
- static int current_lineno = -1;
- static enum cat_err cat_errno = E_ZERO;
-
- #ifndef GENCAT
- static void load_catalog P((struct cat *));
- static FILE *find_catalog P((char *, char **));
- #endif
- static int read_catalog P((FILE *, struct message **));
- /* static void delete_set P((struct message **, unsigned));*/
- /* static void delete_message P((struct message **, unsigned, unsigned)); */
- static int hash P((unsigned setnum, unsigned msgnum));
- static char *parse_text P((FILE *, int));
-
- #ifndef GENCAT
-
- nl_catd catopen(name, oflag)
- char *name;
- int oflag;
- {
- struct cat *catp;
- int i;
-
- if (!name)
- return 0;
-
- catp = (struct cat *)malloc(sizeof *catp);
- if (!catp)
- return 0;
- for (i = 0; i < HASH_TAB_SIZE; i++)
- catp->table[i] = 0;
- catp->name = malloc(strlen(name) + 1);
- catp->loaded = 0;
- catp->bad = 0;
- strcpy(catp->name, name);
- return (nl_catd)catp;
- }
-
- int catclose(catd)
- nl_catd catd;
- {
- int i;
- struct cat *catp = (struct cat *)catd;
-
- if (!catp)
- return 0;
-
- for (i = 0; i < HASH_TAB_SIZE; i++) {
- struct message *p, *nextp;
- for (p = catp->table[i]; p; p = nextp) {
- nextp = p->next;
- free(p->text);
- free((char *)p);
- }
- }
- if (catp->name)
- free(catp->name);
- free((char *)catp);
- return 0;
- }
-
- char *catgets(catd, setnum, msgnum, dflt)
- nl_catd catd;
- int setnum, msgnum;
- char *dflt;
- {
- struct message *p;
- struct cat *catp;
-
- /* setnum and msgnum are required to be >= 1. */
- if (!catd || setnum <= 0 || msgnum <= 0)
- return dflt;
- catp = (struct cat *)catd;
- if (!catp->loaded)
- load_catalog(catp);
- if (catp->bad)
- return dflt;
- for (p = catp->table[hash(setnum, msgnum)]; p; p = p->next)
- if (p->msgnum == msgnum && p->setnum == setnum)
- break;
- if (!p)
- return dflt;
- return p->text;
- }
-
- static
- VOID load_catalog(catp)
- struct cat *catp;
- {
- FILE *fp;
- char *path;
-
- catp->loaded = 1;
- fp = find_catalog(catp->name, &path);
- if (!fp) {
- catp->bad = 1;
- return;
- }
- current_lineno = 0;
- if (read_catalog(fp, catp->table) < 0)
- catp->bad = 1;
- fclose(fp);
- if (read_buf) {
- free(read_buf);
- read_buf = 0;
- }
- read_buf_len = 0;
- free(path);
- }
-
- static
- FILE *find_catalog(name, pathp)
- char *name;
- char **pathp;
- {
- char *path;
-
- if (!name)
- return 0;
- if (!nlspath) {
- nlspath = getenv("NLSPATH");
- if (!nlspath)
- nlspath = DEFAULT_NLSPATH;
- }
- if (!lang) {
- lang = getenv("LANG");
- if (!lang)
- lang = DEFAULT_LANG;
- }
- path = nlspath;
- for (;;) {
- char *p;
- unsigned len = 0;
-
- for (p = path; *p != '\0' && *p != PATH_FILE_SEP; p++) {
- if (*p == '%') {
- if (p[1] == 'N') {
- p++;
- len += strlen(name);
- }
- else if (p[1] == 'L') {
- p++;
- len += strlen(lang);
- }
- else if (p[1] == '%') {
- p++;
- len++;
- }
- else
- len++;
-
- }
- else
- len++;
- }
- if (len > 0) {
- char *s, *try;
- FILE *fp;
- s = try = malloc(len + 1);
- if (!s)
- return 0;
- for (p = path; *p != '\0' && *p != PATH_FILE_SEP; p++) {
- if (*p == '%') {
- if (p[1] == 'N') {
- p++;
- strcpy(s, name);
- s += strlen(name);
- }
- else if (p[1] == 'L') {
- p++;
- strcpy(s, lang);
- s += strlen(lang);
- }
- else if (p[1] == '%') {
- p++;
- *s++ = '%';
- }
- else
- *s++ = *p;
- }
- else
- *s++ = *p;
- }
- *s++ = '\0';
- fp = fopen(try, "r");
- if (fp) {
- *pathp = try;
- return fp;
- }
- free(try);
- }
- if (*p == '\0')
- break;
- path = ++p;
- }
- return 0;
- }
-
- #endif /* not GENCAT */
-
- /* 0 success, -1 error */
-
- static
- int parse_message(c, fp, table, setnum, quote)
- int c;
- FILE *fp;
- struct message **table;
- unsigned setnum;
- int quote;
- {
- unsigned msgnum;
- struct message *msgp;
- char *text;
- int hc;
-
- msgnum = c - '0';
- for (;;) {
- c = getc(fp);
- if (!isdigit(c))
- break;
- msgnum = msgnum*10 + (c - '0');
- }
- if (c == '\n') {
- delete_message(table, setnum, msgnum);
- return 0;
- }
- if (c != ' ' && c != '\t') {
- cat_errno = E_BADSEP;
- return -1;
- }
- text = parse_text(fp, quote);
- if (!text)
- return -1;
- hc = hash(setnum, msgnum);
- for (msgp = table[hc]; msgp; msgp = msgp->next)
- if (msgp->setnum == setnum && msgp->msgnum == msgnum)
- break;
- if (msgp)
- free(msgp->text);
- else {
- msgp = (struct message *)malloc(sizeof *msgp);
- if (!msgp) {
- cat_errno = E_NOMEM;
- return -1;
- }
- msgp->next = table[hc];
- table[hc] = msgp;
- msgp->msgnum = msgnum;
- msgp->setnum = setnum;
- }
- msgp->text = text;
- return 0;
- }
-
- static
- char *parse_text(fp, quote)
- FILE *fp;
- int quote;
- {
- unsigned i = 0;
- char *p;
- int c;
- int quoted;
-
- c = getc(fp);
- if (c == quote) {
- quoted = 1;
- c = getc(fp);
- }
- else
- quoted = 0;
- for (;; c = getc(fp)) {
- if (c == EOF) {
- if (ferror(fp)) {
- cat_errno = E_INPUT;
- return 0;
- }
- break;
- }
- if (c == '\n')
- break;
- /* XXX
-
- Can quotes be used in quoted message text if protected by \ ?
-
- Is it illegal to omit the closing quote if there's an opening
- quote?
-
- Is it illegal to have anything after a closing quote?
-
- */
-
- if (quoted && c == quote) {
- /* Skip the rest of the line. */
- while ((c = getc(fp)) != '\n')
- if (c == EOF) {
- if (ferror(fp)) {
- cat_errno = E_INPUT;
- return 0;
- }
- break;
- }
- break;
- }
- if (c == '\\') {
- int d;
-
- c = getc(fp);
- if (c == EOF)
- break;
- switch (c) {
- case '\n':
- current_lineno++;
- continue;
- case 'n':
- c = '\n';
- break;
- case 'b':
- c = '\b';
- break;
- case 'f':
- c = '\f';
- break;
- case 't':
- c = '\t';
- break;
- case 'v':
- c = '\v';
- break;
- case 'r':
- c = '\r';
- break;
- case '\\':
- c = '\\';
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- c -= '0';
- d = getc(fp);
- if (d >= '0' && d <= '7') {
- c = c*8 + d - '0';
- d = getc(fp);
- if (d >= '0' && d <= '7')
- c = c*8 + d - '0';
- else if (d != EOF)
- ungetc(d,fp);
- }
- else if (d != EOF)
- ungetc(d, fp);
- if (c == '\0')
- continue; /* XXX */
- break;
- default:
- /* Ignore the quote. */
- break;
- }
- }
- if (i >= read_buf_len) {
- if (!read_buf)
- read_buf = malloc(read_buf_len = 40);
- else
- read_buf = realloc(read_buf, read_buf_len *= 2);
- if (!read_buf) {
- cat_errno = E_NOMEM;
- return 0;
- }
- }
- read_buf[i++] = c;
- }
- p = malloc(i + 1);
- if (!p) {
- cat_errno = E_NOMEM;
- return 0;
- }
- memcpy(p, read_buf, i);
- p[i] = '\0';
- return p;
- }
-
- /* 0 success, -1 error */
-
- static
- int parse_command(fp, table, setnump, quotep)
- FILE *fp;
- struct message **table;
- unsigned *setnump;
- int *quotep;
- {
- char buf[128];
- if (fgets(buf, 128, fp) == NULL) {
- cat_errno = ferror(fp) ? E_INPUT : E_EOF;
- return -1;
- }
- if (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n')
- /* a comment */;
- else if (strncmp(buf, "set", 3) == 0) {
- if (sscanf(buf + 3, "%u", setnump) != 1) {
- cat_errno = E_BADARG;
- return -1;
- }
-
- }
- else if (strncmp(buf, "delset", 6) == 0) {
- unsigned num;
- if (sscanf(buf + 6, "%u", &num) != 1) {
- cat_errno = E_BADARG;
- return -1;
- }
- delete_set(table, num);
- *setnump = NL_SETD;
- }
- else if (strncmp(buf, "quote", 5) == 0) {
- char *p = buf + 5;
- while (*p == ' ' || *p == '\t')
- p++;
- /* XXX should \ be allowed as the quote character? */
- if (*p == '\0' || *p == '\n')
- *quotep = -1;
- else
- *quotep = *p;
- }
- else {
- cat_errno = E_NOSUCHCOMMAND;
- return -1;
- }
- if (strchr(buf, '\n') == 0) {
- int c;
- while ((c = getc(fp)) != '\n' && c != EOF)
- ;
- }
- return 0;
- }
-
-
- static
- VOID delete_set(table, setnum)
- struct message **table;
- unsigned setnum;
- {
- int i;
-
- for (i = 0; i < HASH_TAB_SIZE; i++) {
- struct message *p, *nextp;
- for (p = table[i], table[i] = 0; p; p = nextp) {
- nextp = p->next;
- if (p->setnum == setnum)
- free((char *)p);
- else {
- p->next = table[i];
- table[i] = p;
- }
- }
- }
- }
-
-
- static
- VOID delete_message(table, setnum, msgnum)
- struct message **table;
- unsigned setnum, msgnum;
- {
- struct message **pp;
-
- for (pp = &table[hash(setnum, msgnum)]; *pp; pp = &(*pp)->next)
- if ((*pp)->setnum == setnum && (*pp)->msgnum == msgnum) {
- struct message *p = *pp;
- *pp = p->next;
- free(p->text);
- free((char *)p);
- break;
- }
- }
-
- /* 0 success, -1 error. On error cat_errno is set to the error number. */
-
- static
- int read_catalog(fp, table)
- FILE *fp;
- struct message **table;
- {
- int c;
- unsigned setnum = NL_SETD;
- int quote_char = -1;
-
- for (;;) {
- /* start of line */
- c = getc(fp);
- if (c == EOF)
- break;
- ++current_lineno;
- if (isdigit(c)) {
- if (parse_message(c, fp, table, setnum, quote_char) < 0)
- return -1;
- }
- else if (c == '$') {
- if (parse_command(fp, table, &setnum, "e_char) < 0)
- return -1;
- }
- else if (c != '\n') {
- while ((c = getc(fp)) != '\n' && c != EOF)
- if (c != ' ' && c != '\t') {
- cat_errno = E_BADLINE;
- return -1;
- }
- if (c == EOF)
- break;
- }
- }
- return 0;
- }
-
- static
- int hash(setnum, msgnum)
- unsigned setnum, msgnum;
- {
- return ((setnum << 8) + msgnum) % HASH_TAB_SIZE;
- }
-
- #ifdef GENCAT
-
- static char *program_name;
-
- static int message_compare P((UNIV, UNIV));
- /* static void print_text P((char *, FILE *));*/
- /* static void usage P((void)); */
-
- #ifdef VARARGS
- /* static void fatal(); */
- #else
- /* static void fatal P((char *,...)); */
- #endif
-
- int main(argc, argv)
- int argc;
- char **argv;
- {
- FILE *fp;
- int i, j, nmessages;
- struct message **list;
- unsigned setnum;
- struct message *table[HASH_TAB_SIZE];
-
- program_name = argv[0];
-
- if (argc < 3)
- usage();
-
- for (i = 0; i < HASH_TAB_SIZE; i++)
- table[i] = 0;
- for (i = 1; i < argc; i++) {
- errno = 0;
- fp = fopen(argv[i], "r");
- if (!fp) {
- if (i > 1)
- fatal("can't open `%s': %s", argv[i], strerror(errno));
- }
- else {
- current_lineno = 0;
- cat_errno = E_ZERO;
- if (read_catalog(fp, table) < 0) {
- assert(cat_errno != E_ZERO);
- assert(cat_errno
- < sizeof(cat_errlist)/sizeof(cat_errlist[0]));
- fatal("%s:%d: %s", argv[i], current_lineno,
- cat_errlist[cat_errno]);
- }
- fclose(fp);
- }
- }
-
- errno = 0;
- fp = fopen(argv[1], "w");
- if (!fp)
- fatal("can't open `%s' for output: %s", argv[1], strerror(errno));
- nmessages = 0;
- for (i = 0; i < HASH_TAB_SIZE; i++) {
- struct message *p;
- for (p = table[i]; p; p = p->next)
- nmessages++;
- }
- list = (struct message **)malloc(nmessages*sizeof(struct message *));
- if (!list)
- fatal("out of memory");
- j = 0;
- for (i = 0; i < HASH_TAB_SIZE; i++) {
- struct message *p;
- for (p = table[i]; p; p = p->next)
- list[j++] = p;
- }
- assert(j == nmessages);
-
- qsort((UNIV)list, nmessages, sizeof(struct message *), message_compare);
-
- setnum = NL_SETD;
- for (i = 0; i < nmessages; i++) {
- struct message *p = list[i];
- if (p->setnum != setnum) {
- setnum = p->setnum;
- fprintf(fp, "$set %u\n", setnum);
- }
- fprintf(fp, "%u ", p->msgnum);
- print_text(p->text, fp);
- putc('\n', fp);
- }
- if (fclose(fp) == EOF)
- fatal("error closing `%s'", argv[1]);
- return 0;
- }
-
- static
- VOID usage()
- {
- fprintf(stderr, "usage: %s catfile msgfile...\n", program_name);
- exit(1);
- }
-
- static
- #ifdef VARARGS
- VOID fatal(va_alist) va_dcl
- #else /* not VARARGS */
- VOID fatal(char *message,...)
- #endif /* not VARARGS */
- {
- va_list ap;
-
- #ifdef VARARGS
- char *message;
- va_start(ap);
- message = va_arg(ap, char *);
- #else /* not VARARGS */
- va_start(ap, message);
- #endif /* not VARARGS */
-
- fprintf(stderr, "%s: ", program_name);
- vfprintf(stderr, message, ap);
- putc('\n', stderr);
- va_end(ap);
- exit(1);
- }
-
- static
- int message_compare(p1, p2)
- UNIV p1, p2;
- /* UNIV p1, UNIV p2; */
- {
- struct message *m1 = *(struct message **)p1;
- struct message *m2 = *(struct message **)p2;
-
- if (m1->setnum < m2->setnum)
- return -1;
- if (m1->setnum > m2->setnum)
- return 1;
- if (m1->msgnum < m2->msgnum)
- return -1;
- if (m1->msgnum > m2->msgnum)
- return 1;
- return 0;
- }
-
- static
- VOID print_text(s, fp)
- char *s;
- FILE *fp;
- {
- for (; *s; s++) {
- if (*s == '\\')
- fputs("\\\\", fp);
- /* else if (ISASCII(*s) && isprint((UNCH)*s)) */
- else if (ISASCII(*s) && isprint((char)*s))
- putc(*s, fp);
- else {
- switch (*s) {
- case '\n':
- fputs("\\n", fp);
- break;
- case '\b':
- fputs("\\b", fp);
- break;
- case '\f':
- fputs("\\f", fp);
- break;
- case '\t':
- fputs("\\t", fp);
- break;
- case '\v':
- fputs("\\v", fp);
- break;
- case '\r':
- fputs("\\r", fp);
- break;
- default:
- fprintf(fp, "\\%03o", (unsigned char)*s);
- break;
- }
- }
- }
- }
-
- #endif /* GENCAT */
-
- #ifdef TEST
-
- int main(argc, argv)
- int argc;
- char **argv;
- {
- nl_catd catd;
- int msgnum, setnum;
-
- if (argc != 2) {
- fprintf(stderr, "usage: %s catalogue\n", argv[0]);
- exit(1);
- }
- catd = catopen(argv[1], 0);
- fprintf(stderr, "Enter set number, message number pairs:\n");
- fflush(stderr);
- while (scanf("%d %d", &setnum, &msgnum) == 2) {
- char *msg = catgets(catd, setnum, msgnum, "<default>");
- fprintf(stderr, "Returned \"%s\"\n", msg);
- fflush(stderr);
- }
- return 0;
- }
-
- #endif /* TEST */
-
- #endif /* not HAVE_CAT */
- /*
- Local Variables:
- c-indent-level: 5
- c-continued-statement-offset: 5
- c-brace-offset: -5
- c-argdecl-indent: 0
- c-label-offset: -5
- End:
- */
-