[BACK]Return to gencat.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / gencat

Diff for /src/usr.bin/gencat/gencat.c between version 1.2 and 1.3

version 1.2, 1996/04/21 23:43:00 version 1.3, 1996/05/22 11:38:53
Line 1 
Line 1 
   /*-
    * Copyright (c) 1996 The NetBSD Foundation, Inc.
    * All rights reserved.
    *
    * This code is derived from software contributed to The NetBSD Foundation
    * by J.T. Conklin.
    *
    * Redistribution and use in source and binary forms, with or without
    * modification, are permitted provided that the following conditions
    * are met:
    * 1. Redistributions of source code must retain the above copyright
    *    notice, this list of conditions and the following disclaimer.
    * 2. Redistributions in binary form must reproduce the above copyright
    *    notice, this list of conditions and the following disclaimer in the
    *    documentation and/or other materials provided with the distribution.
    * 3. All advertising materials mentioning features or use of this software
    *    must display the following acknowledgement:
    *        This product includes software developed by the NetBSD
    *        Foundation, Inc. and its contributors.
    * 4. Neither the name of The NetBSD Foundation nor the names of its
    *    contributors may be used to endorse or promote products derived
    *    from this software without specific prior written permission.
    *
    * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
    * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
    * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
    * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    * POSSIBILITY OF SUCH DAMAGE.
    */
   
 /***********************************************************  /***********************************************************
 Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.  Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
Line 28 
Line 63 
                                 267 Allston St., #3                                  267 Allston St., #3
                                 Cambridge, MA 02139  USA                                  Cambridge, MA 02139  USA
                                 nazgul@alfalfa.com                                  nazgul@alfalfa.com
   
 ******************************************************************/  ******************************************************************/
   
 /* Edit History  #define _NLS_PRIVATE
   
 01/18/91   3 hamilton   #if not reparsed  #include <sys/queue.h>
 01/12/91   2 schulert   conditionally use prototypes  #include <ctype.h>
 12/23/90   2 hamilton   Fix fd == NULL to fd < 0  
 11/03/90   1 hamilton   Alphalpha->Alfalfa & OmegaMail->Poste  
 08/13/90   1 schulert   move from ua to omu  
 */  
   
 #include <stdio.h>  #include <stdio.h>
 #include <sys/types.h>  #include <stdlib.h>
 #ifdef SYSV  #include <string.h>
 #include <sys/fcntl.h>  #include <unistd.h>
 #define L_SET SEEK_SET  #include <fcntl.h>
 #define L_INCR SEEK_CUR  #include <nl_types.h>
 #endif  
 #include <sys/file.h>  
 #include <sys/stat.h>  
 #include "gencat.h"  
   
 /*  extern void MCAddSet __P((int setId));
  * The spec says the syntax is "gencat catfile msgfile...".  extern void MCDelSet __P((int setId));
  * We extend it to:  extern void MCAddMsg __P((int msgId, const char *msg));
  *      gencat [-lang C|C++|ANSIC] catfile msgfile [-h <header-file>]...  extern void MCDelMsg __P((int msgId));
  * Flags are order dependant, we'll take whatever lang was most recently chosen  extern void MCParse __P((int fd));
  * and use it to generate the next header file.  The header files are generated  extern void MCReadCat __P((int fd));
  * at the point in the command line they are listed.  Thus the sequence:  extern void MCWriteCat __P((int fd));
  *      gencat -lang C foo.cat foo.mcs -h foo.h -lang C++ bar.mcs -h bar.H  
  * will put constants from foo.mcs into foo.h and constants from bar.mcs into  
  * bar.h.  Constants are not saved in the catalog file, so nothing will come  
  * from that, even if things have been defined before.  The constants in foo.h  
  * will be in C syntax, in bar.H in C++ syntax.  
  */  
   
 #if ANSI_C || defined(__cplusplus)  struct _msgT {
 # define P_(x) x          long    msgId;
 #else          char   *str;
 # define P_(x) /**/          LIST_ENTRY(_msgT) entries;
 #endif  };
   
 static void writeIfChanged P_((char *fname, int lang, int orConsts));  struct _setT {
           long    setId;
           LIST_HEAD(msghead, _msgT) msghead;
           LIST_ENTRY(_setT) entries;
   };
   
 #undef P_  LIST_HEAD(sethead, _setT) sethead;
   static struct _setT *curSet;
   
 void usage() {  static char *curline = NULL;
     fprintf(stderr, "Use: gencat [-new] [-or] [-lang C|C++|ANSIC]\n");  static long lineno = 0;
     fprintf(stderr, "            catfile msgfile [-h <header-file>]...\n");  
   void
   usage()
   {
           fprintf(stderr, "Use: gencat catfile msgfile ...\n");
           exit(1);
 }  }
   
 int main(  int
 #if ANSI_C || defined(__cplusplus)  main(argc, argv)
                 int argc, char *argv[])          int     argc;
 #else          char   *argv[];
                 argc, argv)  
 int argc;  
 char *argv[];  
 #endif  
 {  {
     int         ofd, ifd, i;          int     ofd, ifd;
     FILE        *fptr;          char   *catfile = NULL;
     char        *catfile = NULL;          int     c;
     char        *input = NULL;  
     int         lang = MCLangC;          while ((c = getopt(argc, argv, "")) != -1) {
     int         new = False;                  switch (c) {
     int         orConsts = False;                  case '?':
                   default:
     for (i = 1; i < argc; ++i) {                          usage();
         if (argv[i][0] == '-') {                          /* NOTREACHED */
             if (strcmp(argv[i], "-lang") == 0) {  
                 ++i;  
                 if (strcmp(argv[i], "C") == 0) lang = MCLangC;  
                 else if (strcmp(argv[i], "C++") == 0) lang = MCLangCPlusPlus;  
                 else if (strcmp(argv[i], "ANSIC") == 0) lang = MCLangANSIC;  
                 else {  
                     fprintf(stderr, "gencat: Unrecognized language: %s\n", argv[i]);  
                     exit(1);  
                 }  
             } else if (strcmp(argv[i], "-h") == 0) {  
                 if (!input) {  
                     fprintf(stderr, "gencat: Can't write to a header before reading something.\n");  
                     exit(1);  
                 }                  }
                 ++i;          }
                 writeIfChanged(argv[i], lang, orConsts);          argc -= optind;
             } else if (strcmp(argv[i], "-new") == 0) {          argv += optind;
                 if (catfile) {  
                     fprintf(stderr, "gencat: You must specify -new before the catalog file name\n");          if (argc < 2) {
                     exit(1);  
                 }  
                 new = True;  
             } else if (strcmp(argv[i], "-or") == 0) {  
                 orConsts = ~orConsts;  
             } else {  
                 usage();                  usage();
                 exit(1);                  /* NOTREACHED */
             }          }
         } else {          catfile = *argv++;
             if (!catfile) {  
                 catfile = argv[i];          for (; *argv; argv++) {
                 if (new) {                  if ((ifd = open(*argv, O_RDONLY)) < 0) {
                     if ((ofd = open(catfile, O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0) {                          fprintf(stderr, "gencat: Unable to read %s\n", *argv);
                         fprintf(stderr, "gencat: Unable to create a new %s.\n", catfile);  
                         exit(1);                          exit(1);
                     }  
                 } else if ((ofd = open(catfile, O_RDONLY)) < 0) {  
                     if ((ofd = open(catfile, O_WRONLY|O_CREAT, 0666)) < 0) {  
                         fprintf(stderr, "gencat: Unable to create %s.\n", catfile);  
                         exit(1);  
                     }  
                 } else {  
                     MCReadCat(ofd);  
                     close(ofd);  
                     if ((ofd = open(catfile, O_WRONLY|O_TRUNC)) < 0) {  
                         fprintf(stderr, "gencat: Unable to truncate %s.\n", catfile);  
                         exit(1);  
                     }  
                 }                  }
             } else {  
                 input = argv[i];  
                 if ((ifd = open(input, O_RDONLY)) < 0) {  
                     fprintf(stderr, "gencat: Unable to read %s\n", input);  
                     exit(1);  
                 }  
                 MCParse(ifd);                  MCParse(ifd);
                 close(ifd);                  close(ifd);
             }  
         }          }
     }  
     if (catfile) {          if ((ofd = open(catfile, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0) {
                   fprintf(stderr, "gencat: Unable to create a new %s.\n",
                       catfile);
                   exit(1);
           }
         MCWriteCat(ofd);          MCWriteCat(ofd);
         exit(0);          exit(0);
     } else {  }
         usage();  
   static void
   warning(cptr, msg)
           char   *cptr;
           char   *msg;
   {
           fprintf(stderr, "gencat: %s on line %ld\n", msg, lineno);
           fprintf(stderr, "%s\n", curline);
           if (cptr) {
                   char   *tptr;
                   for (tptr = curline; tptr < cptr; ++tptr)
                           putc(' ', stderr);
                   fprintf(stderr, "^\n");
           }
   }
   
   static void
   error(cptr, msg)
           char   *cptr;
           char   *msg;
   {
           warning(cptr, msg);
         exit(1);          exit(1);
     }  
 }  }
   
 static void writeIfChanged(  static void
 #if ANSI_C || defined(__cplusplus)  corrupt()
                 char *fname, int lang, int orConsts)  {
 #else          error(NULL, "corrupt message catalog");
                 fname, lang, orConsts)  }
 char *fname;  
 int lang;  static void
 int orConsts;  nomem()
   {
           error(NULL, "out of memory");
   }
   
   static void *
   xmalloc(len)
           size_t  len;
   {
           void   *p;
   
           if ((p = malloc(len)) == NULL)
                   nomem();
           return (p);
   }
   
   static void *
   xrealloc(ptr, size)
           void   *ptr;
           size_t  size;
   {
           if ((ptr = realloc(ptr, size)) == NULL)
                   nomem();
           return (ptr);
   }
   
   static char *
   xstrdup(str)
           char   *str;
   {
           if ((str = strdup(str)) == NULL)
                   nomem();
           return (str);
   }
   
   static char *
   getline(fd)
           int     fd;
   {
           static long curlen = BUFSIZ;
           static char buf[BUFSIZ], *bptr = buf, *bend = buf;
           char   *cptr, *cend;
           long    buflen;
   
           if (!curline) {
                   curline = xmalloc(curlen);
           }
           ++lineno;
   
           cptr = curline;
           cend = curline + curlen;
           for (;;) {
                   for (; bptr < bend && cptr < cend; ++cptr, ++bptr) {
                           if (*bptr == '\n') {
                                   *cptr = '\0';
                                   ++bptr;
                                   return (curline);
                           } else
                                   *cptr = *bptr;
                   }
                   if (bptr == bend) {
                           buflen = read(fd, buf, BUFSIZ);
                           if (buflen <= 0) {
                                   if (cptr > curline) {
                                           *cptr = '\0';
                                           return (curline);
                                   }
                                   return (NULL);
                           }
                           bend = buf + buflen;
                           bptr = buf;
                   }
                   if (cptr == cend) {
                           cptr = curline = xrealloc(curline, curlen *= 2);
                           cend = curline + curlen;
                   }
           }
   }
   
   static char *
   wskip(cptr)
           char   *cptr;
   {
           if (!*cptr || !isspace(*cptr)) {
                   warning(cptr, "expected a space");
                   return (cptr);
           }
           while (*cptr && isspace(*cptr))
                   ++cptr;
           return (cptr);
   }
   
   static char *
   cskip(cptr)
           char   *cptr;
   {
           if (!*cptr || isspace(*cptr)) {
                   warning(cptr, "wasn't expecting a space");
                   return (cptr);
           }
           while (*cptr && !isspace(*cptr))
                   ++cptr;
           return (cptr);
   }
   
   static char *
   getmsg(fd, cptr, quote)
           int     fd;
           char   *cptr;
           char    quote;
   {
           static char *msg = NULL;
           static long msglen = 0;
           long    clen, i;
           char   *tptr;
   
           if (quote && *cptr == quote) {
                   ++cptr;
           }
   
           clen = strlen(cptr) + 1;
           if (clen > msglen) {
                   if (msglen)
                           msg = xrealloc(msg, clen);
                   else
                           msg = xmalloc(clen);
                   msglen = clen;
           }
           tptr = msg;
   
           while (*cptr) {
                   if (quote && *cptr == quote) {
                           char   *tmp;
                           tmp = cptr + 1;
                           if (*tmp && (!isspace(*tmp) || *wskip(tmp))) {
                                   warning(cptr, "unexpected quote character, ignoreing");
                                   *tptr++ = *cptr++;
                           } else {
                                   *cptr = '\0';
                           }
                   } else
                           if (*cptr == '\\') {
                                   ++cptr;
                                   switch (*cptr) {
                                   case '\0':
                                           cptr = getline(fd);
                                           if (!cptr)
                                                   error(NULL, "premature end of file");
                                           msglen += strlen(cptr);
                                           i = tptr - msg;
                                           msg = xrealloc(msg, msglen);
                                           tptr = msg + i;
                                           break;
                                   case 'n':
                                           *tptr++ = '\n';
                                           ++cptr;
                                           break;
                                   case 't':
                                           *tptr++ = '\t';
                                           ++cptr;
                                           break;
                                   case 'v':
                                           *tptr++ = '\v';
                                           ++cptr;
                                           break;
                                   case 'b':
                                           *tptr++ = '\b';
                                           ++cptr;
                                           break;
                                   case 'r':
                                           *tptr++ = '\r';
                                           ++cptr;
                                           break;
                                   case 'f':
                                           *tptr++ = '\f';
                                           ++cptr;
                                           break;
                                   case '\\':
                                           *tptr++ = '\\';
                                           ++cptr;
                                           break;
                                   default:
                                           if (isdigit(*cptr)) {
                                                   *tptr = 0;
                                                   for (i = 0; i < 3; ++i) {
                                                           if (!isdigit(*cptr))
                                                                   break;
                                                           if (*cptr > '7')
                                                                   warning(cptr, "octal number greater than 7?!");
                                                           *tptr *= 8;
                                                           *tptr += (*cptr - '0');
                                                           ++cptr;
                                                   }
                                           } else {
                                                   warning(cptr, "unrecognized escape sequence");
                                           }
                                   }
                           } else {
                                   *tptr++ = *cptr++;
                           }
           }
           *tptr = '\0';
           return (msg);
   }
   
   void
   MCParse(fd)
           int     fd;
   {
           char   *cptr, *str;
           int     setid, msgid = 0;
           char    quote = 0;
   
           /* XXX: init sethead? */
   
           while ((cptr = getline(fd))) {
                   if (*cptr == '$') {
                           ++cptr;
                           if (strncmp(cptr, "set", 3) == 0) {
                                   cptr += 3;
                                   cptr = wskip(cptr);
                                   setid = atoi(cptr);
                                   MCAddSet(setid);
                                   msgid = 0;
                           } else if (strncmp(cptr, "delset", 6) == 0) {
                                   cptr += 6;
                                   cptr = wskip(cptr);
                                   setid = atoi(cptr);
                                   MCDelSet(setid);
                           } else if (strncmp(cptr, "quote", 5) == 0) {
                                   cptr += 5;
                                   if (!*cptr)
                                           quote = 0;
                                   else {
                                           cptr = wskip(cptr);
                                           if (!*cptr)
                                                   quote = 0;
                                           else
                                                   quote = *cptr;
                                   }
                           } else if (isspace(*cptr)) {
                                   ;
                           } else {
                                   if (*cptr) {
                                           cptr = wskip(cptr);
                                           if (*cptr)
                                                   warning(cptr, "unrecognized line");
                                   }
                           }
                   } else {
                           if (isdigit(*cptr)) {
                                   msgid = atoi(cptr);
                                   cptr = cskip(cptr);
                                   cptr = wskip(cptr);
                                   /* if (*cptr) ++cptr; */
                           }
                           if (!*cptr)
                                   MCDelMsg(msgid);
                           else {
                                   str = getmsg(fd, cptr, quote);
                                   MCAddMsg(msgid, str);
                           }
                   }
           }
   }
   
   void
   MCReadCat(fd)
           int     fd;
   {
   #if 0
           MCHeaderT mcHead;
           MCMsgT  mcMsg;
           MCSetT  mcSet;
           msgT   *msg;
           setT   *set;
           int     i;
           char   *data;
   
           /* XXX init sethead? */
   
           if (read(fd, &mcHead, sizeof(mcHead)) != sizeof(mcHead))
                   corrupt();
           if (strncmp(mcHead.magic, MCMagic, MCMagicLen) != 0)
                   corrupt();
           if (mcHead.majorVer != MCMajorVer)
                   error(NULL, "unrecognized catalog version");
           if ((mcHead.flags & MCGetByteOrder()) == 0)
                   error(NULL, "wrong byte order");
   
           if (lseek(fd, mcHead.firstSet, SEEK_SET) == -1)
                   corrupt();
   
           for (;;) {
                   if (read(fd, &mcSet, sizeof(mcSet)) != sizeof(mcSet))
                           corrupt();
                   if (mcSet.invalid)
                           continue;
   
                   set = xmalloc(sizeof(setT));
                   memset(set, '\0', sizeof(*set));
                   if (cat->first) {
                           cat->last->next = set;
                           set->prev = cat->last;
                           cat->last = set;
                   } else
                           cat->first = cat->last = set;
   
                   set->setId = mcSet.setId;
   
                   /* Get the data */
                   if (mcSet.dataLen) {
                           data = xmalloc(mcSet.dataLen);
                           if (lseek(fd, mcSet.data.off, SEEK_SET) == -1)
                                   corrupt();
                           if (read(fd, data, mcSet.dataLen) != mcSet.dataLen)
                                   corrupt();
                           if (lseek(fd, mcSet.u.firstMsg, SEEK_SET) == -1)
                                   corrupt();
   
                           for (i = 0; i < mcSet.numMsgs; ++i) {
                                   if (read(fd, &mcMsg, sizeof(mcMsg)) != sizeof(mcMsg))
                                           corrupt();
                                   if (mcMsg.invalid) {
                                           --i;
                                           continue;
                                   }
                                   msg = xmalloc(sizeof(msgT));
                                   memset(msg, '\0', sizeof(*msg));
                                   if (set->first) {
                                           set->last->next = msg;
                                           msg->prev = set->last;
                                           set->last = msg;
                                   } else
                                           set->first = set->last = msg;
   
                                   msg->msgId = mcMsg.msgId;
                                   msg->str = xstrdup((char *) (data + mcMsg.msg.off));
                           }
                           free(data);
                   }
                   if (!mcSet.nextSet)
                           break;
                   if (lseek(fd, mcSet.nextSet, SEEK_SET) == -1)
                           corrupt();
           }
 #endif  #endif
   }
   
   /*
    * Write message catalog.
    *
    * The message catalog is first converted from its internal to its
    * external representation in a chunk of memory allocated for this
    * purpose.  Then the completed catalog is written.  This approach
    * avoids additional housekeeping variables and/or a lot of seeks
    * that would otherwise be required.
    */
   void
   MCWriteCat(fd)
           int     fd;
 {  {
     char        tmpname[32];          int     nsets;          /* number of sets */
     char        buf[BUFSIZ], tbuf[BUFSIZ], *cptr, *tptr;          int     nmsgs;          /* number of msgs */
     int         fd, tfd;          int     string_size;    /* total size of string pool */
     int         diff = False;          int     msgcat_size;    /* total size of message catalog */
     int         c, len, tlen;          void   *msgcat;         /* message catalog data */
     struct stat sbuf;          struct _nls_cat_hdr *cat_hdr;
           struct _nls_set_hdr *set_hdr;
           struct _nls_msg_hdr *msg_hdr;
           char   *strings;
           struct _setT *set;
           struct _msgT *msg;
           int     msg_index;
           int     msg_offset;
   
     /* If it doesn't exist, just create it */          /* determine number of sets, number of messages, and size of the
     if (stat(fname, &sbuf)) {           * string pool */
         if ((fd = open(fname, O_WRONLY|O_CREAT, 0666)) < 0) {          nsets = 0;
             fprintf(stderr, "gencat: Unable to create header file %s.\n", fname);          nmsgs = 0;
             exit(1);          string_size = 0;
   
           for (set = sethead.lh_first; set != NULL;
               set = set->entries.le_next) {
                   nsets++;
   
                   for (msg = set->msghead.lh_first; msg != NULL;
                       msg = msg->entries.le_next) {
                           nmsgs++;
                           string_size += strlen(msg->str) + 1;
                   }
         }          }
         MCWriteConst(fd, lang, orConsts);  
         close(fd);  
         return;  
     }  
   
     /* If it does exist, create a temp file for now */  #ifdef DEBUG
     sprintf(tmpname, "/tmp/gencat.%d", (int) getpid());          printf("number of sets: %d\n", nsets);
     if ((tfd = open(tmpname, O_RDWR|O_CREAT, 0666)) < 0) {          printf("number of msgs: %d\n", nmsgs);
         fprintf(stderr, "gencat: Unable to open temporary file: %s\n", tmpname);          printf("string pool size: %d\n", string_size);
         exit(1);  #endif
     }  
     unlink(tmpname);  
   
     /* Write to the temp file and rewind */          /* determine size and then allocate buffer for constructing external
     MCWriteConst(tfd, lang, orConsts);           * message catalog representation */
           msgcat_size = sizeof(struct _nls_cat_hdr)
               + (nsets * sizeof(struct _nls_set_hdr))
               + (nmsgs * sizeof(struct _nls_msg_hdr))
               + string_size;
   
     /* Open the real header file */          msgcat = xmalloc(msgcat_size);
     if ((fd = open(fname, O_RDONLY)) < 0) {          memset(msgcat, '\0', msgcat_size);
         fprintf(stderr, "gencat: Unable to read header file: %s\n", fname);  
         exit(1);  
     }  
   
     /* Backup to the start of the temp file */          /* fill in msg catalog header */
     if (lseek(tfd, 0L, L_SET) < 0) {          cat_hdr = (struct _nls_cat_hdr *) msgcat;
         fprintf(stderr, "gencat: Unable to seek in tempfile: %s\n", tmpname);          cat_hdr->__magic = htonl(_NLS_MAGIC);
         exit(1);          cat_hdr->__nsets = htonl(nsets);
     }          cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr));
           cat_hdr->__msg_hdr_offset =
               htonl(nsets * sizeof(struct _nls_set_hdr));
           cat_hdr->__msg_txt_offset =
               htonl(nsets * sizeof(struct _nls_set_hdr) +
               nmsgs * sizeof(struct _nls_msg_hdr));
   
     /* Now compare them */          /* compute offsets for set & msg header tables and string pool */
     while ((tlen = read(tfd, tbuf, BUFSIZ)) > 0) {          set_hdr = (struct _nls_set_hdr *) ((char *) msgcat +
         if ((len = read(fd, buf, BUFSIZ)) != tlen) {              sizeof(struct _nls_cat_hdr));
             diff = True;          msg_hdr = (struct _nls_msg_hdr *) ((char *) msgcat +
             goto done;              sizeof(struct _nls_cat_hdr) +
               nsets * sizeof(struct _nls_set_hdr));
           strings = (char *) msgcat +
               sizeof(struct _nls_cat_hdr) +
               nsets * sizeof(struct _nls_set_hdr) +
               nmsgs * sizeof(struct _nls_msg_hdr);
   
           msg_index = 0;
           msg_offset = 0;
           for (set = sethead.lh_first; set != NULL;
               set = set->entries.le_next) {
   
                   nmsgs = 0;
                   for (msg = set->msghead.lh_first; msg != NULL;
                       msg = msg->entries.le_next) {
                           int     msg_len = strlen(msg->str) + 1;
   
                           msg_hdr->__msgno = htonl(msg->msgId);
                           msg_hdr->__msglen = htonl(msg_len);
                           msg_hdr->__offset = htonl(msg_offset);
   
                           memcpy(strings, msg->str, msg_len);
                           strings += msg_len;
                           msg_offset += msg_len;
   
                           nmsgs++;
                           msg_hdr++;
                   }
   
                   set_hdr->__setno = htonl(set->setId);
                   set_hdr->__nmsgs = htonl(nmsgs);
                   set_hdr->__index = htonl(msg_index);
                   msg_index += nmsgs;
                   set_hdr++;
         }          }
         for (cptr = buf, tptr = tbuf; cptr < buf+len; ++cptr, ++tptr) {  
             if (*tptr != *cptr) {          /* write out catalog.  XXX: should this be done in small chunks? */
                 diff = True;          write(fd, msgcat, msgcat_size);
                 goto done;  }
             }  
   void
   MCAddSet(setId)
           int     setId;
   {
           struct _setT *p, *q;
   
           if (setId <= 0) {
                   error(NULL, "setId's must be greater than zero");
                   /* NOTREACHED */
         }          }
     }  #if 0
 done:          /* XXX */
     if (diff) {          if (setId > NL_SETMAX) {
         if (lseek(tfd, 0L, L_SET) < 0) {                  error(NULL, "setId %d exceeds limit (%d)");
             fprintf(stderr, "gencat: Unable to seek in tempfile: %s\n", tmpname);                  /* NOTREACHED */
             exit(1);  
         }          }
         close(fd);  #endif
         if ((fd = open(fname, O_WRONLY|O_TRUNC)) < 0) {  
             fprintf(stderr, "gencat: Unable to truncate header file: %s\n", fname);          p = sethead.lh_first;
             exit(1);          q = NULL;
           for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next);
   
           if (p && p->setId == setId) {
                   ;
           } else {
                   p = xmalloc(sizeof(struct _setT));
                   memset(p, '\0', sizeof(struct _setT));
                   LIST_INIT(&p->msghead);
   
                   p->setId = setId;
   
                   if (q == NULL) {
                           LIST_INSERT_HEAD(&sethead, p, entries);
                   } else {
                           LIST_INSERT_AFTER(q, p, entries);
                   }
         }          }
         while ((len = read(tfd, buf, BUFSIZ)) > 0) {  
             if (write(fd, buf, len) != len) {          curSet = p;
                 fprintf(stderr, "gencat: Error writing to header file: %s\n", fname);  }
             }  
   void
   MCAddMsg(msgId, str)
           int     msgId;
           const char *str;
   {
           struct _msgT *p, *q;
   
           if (!curSet)
                   error(NULL, "can't specify a message when no set exists");
   
           if (msgId <= 0) {
                   error(NULL, "msgId's must be greater than zero");
                   /* NOTREACHED */
         }          }
     }  #if 0
     close(fd);          /* XXX */
     close(tfd);          if (msgId > NL_SETMAX) {
                   error(NULL, "msgID %d exceeds limit (%d)");
                   /* NOTREACHED */
           }
   #endif
   
           p = curSet->msghead.lh_first;
           q = NULL;
           for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next);
   
           if (p && p->msgId == msgId) {
                   free(p->str);
           } else {
                   p = xmalloc(sizeof(struct _msgT));
                   memset(p, '\0', sizeof(struct _msgT));
   
                   if (q == NULL) {
                           LIST_INSERT_HEAD(&curSet->msghead, p, entries);
                   } else {
                           LIST_INSERT_AFTER(q, p, entries);
                   }
           }
   
           p->msgId = msgId;
           p->str = xstrdup(str);
   }
   
   void
   MCDelSet(setId)
           int     setId;
   {
           struct _setT *set;
           struct _msgT *msg;
   
           set = sethead.lh_first;
           for (; set != NULL && set->setId < setId; set = set->entries.le_next);
   
           if (set && set->setId == setId) {
   
                   msg = set->msghead.lh_first;
                   while (msg) {
                           free(msg->str);
                           LIST_REMOVE(msg, entries)
                   }
   
                   LIST_REMOVE(set, entries);
                   return;
           }
           warning(NULL, "specified set doesn't exist");
   }
   
   void
   MCDelMsg(msgId)
           int     msgId;
   {
           struct _msgT *msg;
   
           if (!curSet)
                   error(NULL, "you can't delete a message before defining the set");
   
           msg = curSet->msghead.lh_first;
           for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next);
   
           if (msg && msg->msgId == msgId) {
                   free(msg->str);
                   LIST_REMOVE(msg, entries);
                   return;
           }
           warning(NULL, "specified msg doesn't exist");
 }  }

Legend:
Removed from v.1.2  
changed lines
  Added in v.1.3