diff options
Diffstat (limited to 'gencat')
-rw-r--r-- | gencat/Makefile | 18 | ||||
-rw-r--r-- | gencat/gencat.c | 265 | ||||
-rw-r--r-- | gencat/gencat.h | 107 | ||||
-rw-r--r-- | gencat/genlib.c | 892 | ||||
-rw-r--r-- | gencat/msgcat.h | 178 |
5 files changed, 1460 insertions, 0 deletions
diff --git a/gencat/Makefile b/gencat/Makefile new file mode 100644 index 0000000..8800958 --- /dev/null +++ b/gencat/Makefile @@ -0,0 +1,18 @@ +# extremely primitive makefile +# just for people that don't have gencat but need it to make man + +# note: you only need gencat if you want non-English messages + +gencat: gencat.o genlib.o + $(CC) $(LDFLAGS) -o gencat gencat.o genlib.o + +gencat.o genlib.o: gencat.h + +genlib.o: msgcat.h + +install: gencat + cp gencat$(EXEEXT) $(DESTDIR)$(PREFIX)/usr/bin + +clean: + rm -f *~ *.o gencat + diff --git a/gencat/gencat.c b/gencat/gencat.c new file mode 100644 index 0000000..d1a7c31 --- /dev/null +++ b/gencat/gencat.c @@ -0,0 +1,265 @@ + +/*********************************************************** +Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that Alfalfa's name not be used in +advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +If you make any modifications, bugfixes or other changes to this software +we'd appreciate it if you could send a copy to us so we can keep things +up-to-date. Many thanks. + Kee Hinckley + Alfalfa Software, Inc. + 267 Allston St., #3 + Cambridge, MA 02139 USA + nazgul@alfalfa.com + +******************************************************************/ + +/* Edit History + +01/18/91 3 hamilton #if not reparsed +01/12/91 2 schulert conditionally use prototypes +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 <stdlib.h> +#include <sys/types.h> +#ifdef SYSV +#include <sys/fcntl.h> +#include <string.h> +#else +#include <strings.h> +#endif +#ifdef __linux__ +#include <string.h> +#include <unistd.h> +#endif +#include <sys/file.h> +#include <sys/stat.h> +#include "gencat.h" + +#ifndef L_SET +#define L_SET SEEK_SET +#endif + +#ifndef L_INCR +#define L_INCR SEEK_CUR +#endif + +/* + * The spec says the syntax is "gencat catfile msgfile...". + * We extend it to: + * gencat [-new] [-or] [-lang C|C++|ANSIC] catfile msgfile + * [-h <header-file>]... + * Flags are order dependant, we'll take whatever lang was most recently chosen + * and use it to generate the next header file. The header files are generated + * at the point in the command line they are listed. Thus the sequence: + * 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. + */ + +static void writeIfChanged( +#if defined(__STDC__) || defined(__cplusplus) + char *fname, int lang, int orConsts +#endif +); + +void usage() { + fprintf(stderr, "Use: gencat [-new] [-or] [-lang C|C++|ANSIC] catfile msgfile [-h <header-file>]...\n"); +} + +int main( +#if defined(__STDC__) || defined(__cplusplus) + int argc, char *argv[]) +#else + argc, argv) +int argc; +char *argv[]; +#endif +{ + int ofd, ifd, i; + FILE *fptr; + char *catfile = NULL; + char *input = NULL; + int lang = MCLangC; + int new = False; + int orConsts = False; + + for (i = 1; i < argc; ++i) { + if (argv[i][0] == '-') { + 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 (strncmp(argv[i], "-h", 2) == 0) { + if (!input) { + fprintf(stderr, "gencat: Can't write to a header before reading something.\n"); + exit(1); + } + ++i; + writeIfChanged(argv[i], lang, orConsts); + } else if (strncmp(argv[i], "-new", 4) == 0) { + if (catfile) { + fprintf(stderr, "gencat: You must specify -new before the catalog file name\n"); + exit(1); + } + new = True; + } else if (strncmp(argv[i], "-or", 3) == 0) { + orConsts = ~orConsts; + } else { + usage(); + exit(1); + } + } else { + if (!catfile) { + catfile = argv[i]; + if (new) { + 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); + } + } 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); + close(ifd); + } + } + } + if (catfile) { + MCWriteCat(ofd); + exit(0); + } else { + usage(); + exit(1); + } + return 0; /* just for gcc */ +} + +static void writeIfChanged( +#if defined(__STDC__) || defined(__cplusplus) + char *fname, int lang, int orConsts) +#else + fname, lang, orConsts) +char *fname; +int lang; +int orConsts; +#endif +{ + char tmpname[32]; + char buf[BUFSIZ], tbuf[BUFSIZ], *cptr, *tptr; + int fd, tfd; + int diff = False; + int c, len, tlen; + struct stat sbuf; + + /* If it doesn't exist, just create it */ + if (stat(fname, &sbuf)) { + if ((fd = open(fname, O_WRONLY|O_CREAT, 0666)) < 0) { + fprintf(stderr, "gencat: Unable to create header file %s.\n", fname); + exit(1); + } + MCWriteConst(fd, lang, orConsts); + close(fd); + return; + } + + /* If it does exist, create a temp file for now */ + sprintf(tmpname, "/tmp/gencat.%d", (int) getpid()); + if ((tfd = open(tmpname, O_RDWR|O_CREAT, 0666)) < 0) { + fprintf(stderr, "gencat: Unable to open temporary file: %s\n", tmpname); + exit(1); + } + unlink(tmpname); + + /* Write to the temp file and rewind */ + MCWriteConst(tfd, lang, orConsts); + + /* Open the real header file */ + if ((fd = open(fname, O_RDONLY)) < 0) { + fprintf(stderr, "gencat: Unable to read header file: %s\n", fname); + exit(1); + } + + /* Backup to the start of the temp file */ + if (lseek(tfd, 0L, L_SET) < 0) { + fprintf(stderr, "gencat: Unable to seek in tempfile: %s\n", tmpname); + exit(1); + } + + /* Now compare them */ + while ((tlen = read(tfd, tbuf, BUFSIZ)) > 0) { + if ((len = read(fd, buf, BUFSIZ)) != tlen) { + diff = True; + goto done; + } + for (cptr = buf, tptr = tbuf; cptr < buf+len; ++cptr, ++tptr) { + if (*tptr != *cptr) { + diff = True; + goto done; + } + } + } +done: + if (diff) { + if (lseek(tfd, 0L, L_SET) < 0) { + fprintf(stderr, "gencat: Unable to seek in tempfile: %s\n", tmpname); + exit(1); + } + close(fd); + if ((fd = open(fname, O_WRONLY|O_TRUNC)) < 0) { + fprintf(stderr, "gencat: Unable to truncate header file: %s\n", fname); + exit(1); + } + while ((len = read(tfd, buf, BUFSIZ)) > 0) { + if (write(fd, buf, len) != len) { + fprintf(stderr, "gencat: Error writing to header file: %s\n", fname); + } + } + } + close(fd); + close(tfd); +} diff --git a/gencat/gencat.h b/gencat/gencat.h new file mode 100644 index 0000000..fcf20b7 --- /dev/null +++ b/gencat/gencat.h @@ -0,0 +1,107 @@ + +/*********************************************************** +Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that Alfalfa's name not be used in +advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +If you make any modifications, bugfixes or other changes to this software +we'd appreciate it if you could send a copy to us so we can keep things +up-to-date. Many thanks. + Kee Hinckley + Alfalfa Software, Inc. + 267 Allston St., #3 + Cambridge, MA 02139 USA + nazgul@alfalfa.com + +******************************************************************/ + +/* Edit History + +02/25/91 2 nazgul Added MCGetByteOrder +01/18/91 2 hamilton #if not reparsed +01/12/91 2 schulert conditionally use prototypes +11/03/90 1 hamilton Alphalpha->Alfalfa & OmegaMail->Poste +08/13/90 1 schulert move from ua to omu +*/ + +#ifndef gencat_h +#define gencat_h + +/* + * $set n comment + * My extension: If the comment begins with # treat the next string + * as a constant identifier. + * $delset n comment + * n goes from 1 to NL_SETMAX + * Deletes a set from the MC + * $ comment + * My extension: If comment begins with # treat the next string as + * a constant identifier for the next message. + * m message-text + * m goes from 1 to NL_MSGMAX + * If message-text is empty, and a space or tab is present, put + * empty string in catalog. + * If message-text is empty, delete the message. + * Length of text is 0 to NL_TEXTMAX + * My extension: If '#' is used instead of a number, the number + * is generated automatically. A # followed by anything is an empty message. + * $quote c + * Optional quote character which can suround message-text to + * show where spaces are. + * + * Escape Characters + * \n (newline), \t (horiz tab), \v (vert tab), \b (backspace), + * \r (carriage return), \f (formfeed), \\ (backslash), \ddd (bitpattern + * in octal). + * Also, \ at end of line is a continuation. + * + */ + +#define MCLangC 0 +#define MCLangCPlusPlus 1 +#define MCLangANSIC 2 + +#define MAXTOKEN 1024 + +#if !defined(ANSI_C) && (defined(__STDC__) || defined(_AIX)) +# define ANSI_C 1 +#endif + +#if ANSI_C || defined(__cplusplus) +# define P_(x) x +#else +# define P_(x) /**/ +#endif + +extern void MCAddSet P_((int setId, char *c)); +extern void MCDelSet P_((int setId)); +extern void MCAddMsg P_((int msgId, char *msg, char *c)); +extern void MCDelMsg P_((int msgId)); +extern void MCParse P_((int fd)); +extern void MCReadCat P_((int fd)); +extern void MCWriteConst P_((int fd, int type, int orConsts)); +extern void MCWriteCat P_((int fd)); +extern long MCGetByteOrder P_((void)); + +#ifndef True +# define True ~0 +# define False 0 +#endif + +#endif diff --git a/gencat/genlib.c b/gencat/genlib.c new file mode 100644 index 0000000..737a9cb --- /dev/null +++ b/gencat/genlib.c @@ -0,0 +1,892 @@ +/* -*-c++-*- */ + + +/*********************************************************** +Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that Alfalfa's name not be used in +advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +If you make any modifications, bugfixes or other changes to this software +we'd appreciate it if you could send a copy to us so we can keep things +up-to-date. Many thanks. + Kee Hinckley + Alfalfa Software, Inc. + 267 Allston St., #3 + Cambridge, MA 02139 USA + nazgul@alfalfa.com + +******************************************************************/ + +/* Edit History + +12/03/82 nazgul Patch from <Jan.Djarv@sa.erisoft.se>. This may fix + the problem with updating sets. +10/18/92 3 schulert actually put in the changes described in last edit. +10/18/92 7 nazgul Changes from gbooman@feds1.Prime.COM (Gordon Booman) + 1) Support backslash quoted quotes in message file text. + 2) Correctly indicate error location in messages that have tabs. + 3) Fixed a misspelling. +04/15/91 6 nazgul Check for byte order first +02/25/91 5 nazgul Added flag for MS byteorder +01/14/91 4 nazgul Off by one on number specified entries +*/ + +#include <stdio.h> +#include <stdlib.h> +#ifdef SYSV +#include <sys/types.h> +#include <unistd.h> +#endif + +#if !defined(__linux__) && !defined(__CYGWIN__) +#include <memory.h> +static int bcopy(src, dst, length) +char *src, *dst; +int length; +{ + memcpy(dst, src, length); +} +static int bzero(b, length) +char *b; +int length; +{ + memset(b, '\0', length); +} +#else +#include <string.h> +#endif + +#include <sys/file.h> +#include <ctype.h> +#include "msgcat.h" +#include "gencat.h" + +#ifndef L_SET +#define L_SET SEEK_SET +#endif + +#ifndef L_INCR +#define L_INCR SEEK_CUR +#endif + +static char *curline = NULL; +static long lineno = 0; + +static void warning(cptr, msg) +char *cptr; +char *msg; +{ + fprintf(stderr, "gencat: %s on line %d\n", msg, lineno); + fprintf(stderr, "%s\n", curline); + if (cptr) { + char *tptr; + for (tptr = curline; tptr < cptr; ++tptr) + putc(*tptr == '\t' ? '\t' : ' ', stderr); + fprintf(stderr, "^\n"); + } +} + +static void error(cptr, msg) +char *cptr; +char *msg; +{ + warning(cptr, msg); + exit(1); +} + +static void corrupt() { + error(NULL, "corrupt message catalog"); +} +static void nomem() { + error(NULL, "out of memory"); +} + +static char *my_getline(fd) +int fd; +{ + static long len = 0, curlen = BUFSIZ; + static char buf[BUFSIZ], *bptr = buf, *bend = buf; + char *cptr, *cend; + long buflen; + + if (!curline) { + curline = (char *) malloc(curlen); + if (!curline) nomem(); + } + ++lineno; + + cptr = curline; + cend = curline + curlen; + while (True) { + 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 = (char *) realloc(curline, curlen *= 2); + cend = curline + curlen; + } + } +} + + +static char *token(cptr) +char *cptr; +{ + static char tok[MAXTOKEN+1]; + char *tptr = tok; + + while (*cptr && isspace(*cptr)) ++cptr; + while (*cptr && !isspace(*cptr)) *tptr++ = *cptr++; + *tptr = '\0'; + return(tok); +} +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; + + int needq; + + if (quote && *cptr == quote) { + needq = True; + ++cptr; + } else needq = False; + + clen = strlen(cptr) + 1; + if (clen > msglen) { + if (msglen) msg = (char *) realloc(msg, clen); + else msg = (char *) malloc(clen); + msglen = clen; + } + tptr = msg; + + while (*cptr) { + if (quote && *cptr == quote) { + char *tmp; + tmp = cptr+1; + if (*tmp && (!isspace(*tmp) || *wskip(tmp))) { + warning(cptr, "quote character before end of line, ignoring"); + *tptr++ = *cptr++; + } else { + *cptr = '\0'; + } + } else if (*cptr == '\\') { + ++cptr; + switch (*cptr) { + case '\0': + cptr = my_getline(fd); + if (!cptr) error(NULL, "premature end of file"); + msglen += strlen(cptr); + i = tptr - msg; + msg = (char *) realloc(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 if (*cptr == quote) { + *tptr++ = *cptr++; + } else { + warning(cptr, "unrecognized escape sequence"); + } + } + } else { + *tptr++ = *cptr++; + } + } + *tptr = '\0'; + return(msg); +} + + + +static char *dupstr(ostr) +char *ostr; +{ + char *nstr; + + nstr = (char *) malloc(strlen(ostr) + 1); + if (!nstr) error(NULL, "unable to allocate storage"); + strcpy(nstr, ostr); + return(nstr); +} + + +/* + * The Global Stuff + */ + + +typedef struct _msgT { + long msgId; + char *str; + char *hconst; + long offset; + struct _msgT *prev, *next; +} msgT; +typedef struct _setT { + long setId; + char *hconst; + msgT *first, *last; + struct _setT *prev, *next; +} setT; +typedef struct { + setT *first, *last; +} catT; + +static setT *curSet; +static catT *cat; + +/* + * Find the current byte order. There are of course some others, but this will do + * for now. Note that all we care about is "long". + */ +long MCGetByteOrder() { + long l = 0x00010203; + char *cptr = (char *) &l; + + if (cptr[0] == 0 && cptr[1] == 1 && cptr[2] == 2 && cptr[3] == 3) + return MC68KByteOrder; + else return MCn86ByteOrder; +} + + +void MCParse( +#if PROTO + int fd) +#else + fd) +int fd; +#endif +{ + char *cptr, *str; + int setid, msgid = 0; + char hconst[MAXTOKEN+1]; + char quote = 0; + int i; + + if (!cat) { + cat = (catT *) malloc(sizeof(catT)); + if (!cat) nomem(); + bzero(cat, sizeof(catT)); + } + + hconst[0] = '\0'; + + while (cptr = my_getline(fd)) { + if (*cptr == '$') { + ++cptr; + if (strncmp(cptr, "set", 3) == 0) { + cptr += 3; + cptr = wskip(cptr); + setid = atoi(cptr); + cptr = cskip(cptr); + if (*cptr) cptr = wskip(cptr); + if (*cptr == '#') { + ++cptr; + MCAddSet(setid, token(cptr)); + } else MCAddSet(setid, NULL); + 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)) { + cptr = wskip(cptr); + if (*cptr == '#') { + ++cptr; + strcpy(hconst, token(cptr)); + } + } else { + if (*cptr) { + cptr = wskip(cptr); + if (*cptr) warning(cptr, "unrecognized line"); + } + } + } else { + if (isdigit(*cptr) || *cptr == '#') { + if (*cptr == '#') { + ++msgid; + ++cptr; + if (!*cptr) { + MCAddMsg(msgid, "", hconst); + hconst[0] = '\0'; + continue; + } + if (!isspace(*cptr)) warning(cptr, "expected a space"); + ++cptr; + if (!*cptr) { + MCAddMsg(msgid, "", hconst); + hconst[0] = '\0'; + continue; + } + } else { + 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, hconst); + hconst[0] = '\0'; + } + } + } + } +} + +void MCReadCat( +#if PROTO + int fd) +#else + fd) +int fd; +#endif +{ + MCHeaderT mcHead; + MCMsgT mcMsg; + MCSetT mcSet; + msgT *msg; + setT *set; + int i; + char *data; + + cat = (catT *) malloc(sizeof(catT)); + if (!cat) nomem(); + bzero(cat, sizeof(catT)); + + if (read(fd, &mcHead, sizeof(mcHead)) != sizeof(mcHead)) corrupt(); + if (strncmp(mcHead.magic, MCMagic, MCMagicLen) != 0) corrupt(); + if ((mcHead.flags & MCGetByteOrder()) == 0) error(NULL, "wrong byte order"); + if (mcHead.majorVer != MCMajorVer) error(NULL, "unrecognized catalog version"); + + if (lseek(fd, mcHead.firstSet, L_SET) == -1) corrupt(); + + while (True) { + if (read(fd, &mcSet, sizeof(mcSet)) != sizeof(mcSet)) corrupt(); + if (mcSet.invalid) continue; + + set = (setT *) malloc(sizeof(setT)); + if (!set) nomem(); + bzero(set, 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 = (char *) malloc(mcSet.dataLen); + if (!data) nomem(); + if (lseek(fd, mcSet.data.off, L_SET) == -1) corrupt(); + if (read(fd, data, mcSet.dataLen) != mcSet.dataLen) corrupt(); + if (lseek(fd, mcSet.u.firstMsg, L_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 = (msgT *) malloc(sizeof(msgT)); + if (!msg) nomem(); + bzero(msg, 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 = dupstr((char *) (data + mcMsg.msg.off)); + } + free(data); + } + if (!mcSet.nextSet) break; + if (lseek(fd, mcSet.nextSet, L_SET) == -1) corrupt(); + } +} + + +static void printS(fd, str) +int fd; +char *str; +{ + write(fd, str, strlen(str)); +} +static void printL(fd, l) +int fd; +long l; +{ + char buf[32]; + sprintf(buf, "%ld", l); + write(fd, buf, strlen(buf)); +} +static void printLX(fd, l) +int fd; +long l; +{ + char buf[32]; + sprintf(buf, "%lx", l); + write(fd, buf, strlen(buf)); +} + +static void genconst(fd, type, setConst, msgConst, val) +int fd; +int type; +char *setConst; +char *msgConst; +long val; +{ + switch (type) { + case MCLangC: + if (!msgConst) { + printS(fd, "\n#define "); + printS(fd, setConst); + printS(fd, "Set"); + } else { + printS(fd, "#define "); + printS(fd, setConst); + printS(fd, msgConst); + } + printS(fd, "\t0x"); + printLX(fd, val); + printS(fd, "\n"); + break; + case MCLangCPlusPlus: + case MCLangANSIC: + if (!msgConst) { + printS(fd, "\nconst long "); + printS(fd, setConst); + printS(fd, "Set"); + } else { + printS(fd, "const long "); + printS(fd, setConst); + printS(fd, msgConst); + } + printS(fd, "\t= "); + printL(fd, val); + printS(fd, ";\n"); + break; + default: + error(NULL, "not a recognized (programming) language type"); + } +} + +void MCWriteConst( +#if PROTO + int fd, int type, int orConsts) +#else + fd, type, orConsts) +int fd; +int type; +int orConsts; +#endif +{ + msgT *msg; + setT *set; + long id; + + if (orConsts && (type == MCLangC || type == MCLangCPlusPlus || type == MCLangANSIC)) { + printS(fd, "/* Use these Macros to compose and decompose setId's and msgId's */\n"); + printS(fd, "#ifndef MCMakeId\n"); + printS(fd, "# define MCuint unsigned int\n"); + printS(fd, "# define MCushort unsigned short\n"); + printS(fd, "# define MCulong unsigned long\n"); + printS(fd, "# define MCMakeId(s,m)\t(MCulong)(((MCushort)s<<(sizeof(short)*8))\\\n"); + printS(fd, " \t |(MCushort)m)\n"); + printS(fd, "# define MCSetId(id)\t(MCuint) ((MCuint)id >> (MCuint)(sizeof(short) * 8))\n"); + printS(fd, "# define MCMsgId(id)\t(MCuint) (((MCuint)id << (MCuint)(sizeof(short) * 8))\\\n"); + printS(fd, " \t >> (MCuint)(sizeof(short) * 8))\n"); + printS(fd, "#endif\n"); + } + + for (set = cat->first; set; set = set->next) { + if (set->hconst) genconst(fd, type, set->hconst, NULL, set->setId); + + for (msg = set->first; msg; msg = msg->next) { + if (msg->hconst) { + if (orConsts) id = MCMakeId(set->setId, msg->msgId); + else id = msg->msgId; + genconst(fd, type, set->hconst, msg->hconst, id); + free(msg->hconst); + msg->hconst = NULL; + } + } + if (set->hconst) { + free(set->hconst); + set->hconst = NULL; + } + } +} + +void MCWriteCat( +#if PROTO + int fd) +#else + fd) +int fd; +#endif +{ + MCHeaderT mcHead; + int cnt; + setT *set; + msgT *msg; + MCSetT mcSet; + MCMsgT mcMsg; + off_t pos; + + bcopy(MCMagic, mcHead.magic, MCMagicLen); + mcHead.majorVer = MCMajorVer; + mcHead.minorVer = MCMinorVer; + mcHead.flags = MCGetByteOrder(); + mcHead.firstSet = 0; /* We'll be back to set this in a minute */ + + for (cnt = 0, set = cat->first; set; set = set->next) ++cnt; + mcHead.numSets = cnt; + + lseek(fd, 0L, L_SET); + write(fd, &mcHead, sizeof(mcHead)); + mcHead.firstSet = lseek(fd, 0, L_INCR); + lseek(fd, 0L, L_SET); + write(fd, &mcHead, sizeof(mcHead)); + + for (set = cat->first; set; set = set->next) { + bzero(&mcSet, sizeof(mcSet)); + + mcSet.setId = set->setId; + mcSet.invalid = False; + + /* The rest we'll have to come back and change in a moment */ + pos = lseek(fd, 0, L_INCR); + write(fd, &mcSet, sizeof(mcSet)); + + /* Now write all the string data */ + mcSet.data.off = lseek(fd, 0, L_INCR); + cnt = 0; + for (msg = set->first; msg; msg = msg->next) { + msg->offset = lseek(fd, 0, L_INCR) - mcSet.data.off; + mcSet.dataLen += write(fd, msg->str, strlen(msg->str) + 1); + ++cnt; + } + mcSet.u.firstMsg = lseek(fd, 0, L_INCR); + mcSet.numMsgs = cnt; + + /* Now write the message headers */ + for (msg = set->first; msg; msg = msg->next) { + mcMsg.msgId = msg->msgId; + mcMsg.msg.off = msg->offset; + mcMsg.invalid = False; + write(fd, &mcMsg, sizeof(mcMsg)); + } + + /* Go back and fix things up */ + + if (set == cat->last) { + mcSet.nextSet = 0; + lseek(fd, pos, L_SET); + write(fd, &mcSet, sizeof(mcSet)); + } else { + mcSet.nextSet = lseek(fd, 0, L_INCR); + lseek(fd, pos, L_SET); + write(fd, &mcSet, sizeof(mcSet)); + lseek(fd, mcSet.nextSet, L_SET); + } + } +} + + +void MCAddSet( +#if PROTO + int setId, char *hconst) +#else + setId, hconst) +int setId; +char *hconst; +#endif +{ + setT *set; + + if (setId <= 0) { + error(NULL, "setId's must be greater than zero"); + return; + } + + if (hconst && !*hconst) hconst = NULL; + for (set = cat->first; set; set = set->next) { + if (set->setId == setId) { + if (set->hconst && hconst) free(set->hconst); + set->hconst = NULL; + break; + } else if (set->setId > setId) { + setT *newSet; + + newSet = (setT *) malloc(sizeof(setT)); + if (!newSet) nomem(); + bzero(newSet, sizeof(setT)); + newSet->prev = set->prev; + newSet->next = set; + if (set->prev) set->prev->next = newSet; + else cat->first = newSet; + set->prev = newSet; + set = newSet; + break; + } + } + if (!set) { + set = (setT *) malloc(sizeof(setT)); + if (!set) nomem(); + bzero(set, sizeof(setT)); + + if (cat->first) { + set->prev = cat->last; + set->next = NULL; + cat->last->next = set; + cat->last = set; + } else { + set->prev = set->next = NULL; + cat->first = cat->last = set; + } + } + set->setId = setId; + if (hconst) set->hconst = dupstr(hconst); + curSet = set; +} + +void MCAddMsg( +#if PROTO + int msgId, char *str, char *hconst) +#else + msgId, str, hconst) +int msgId; +char *str; +char *hconst; +#endif +{ + msgT *msg; + + 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"); + return; + } + + if (hconst && !*hconst) hconst = NULL; + for (msg = curSet->first; msg; msg = msg->next) { + if (msg->msgId == msgId) { + if (msg->hconst && hconst) free(msg->hconst); + if (msg->str) free(msg->str); + msg->hconst = msg->str = NULL; + break; + } else if (msg->msgId > msgId) { + msgT *newMsg; + + newMsg = (msgT *) malloc(sizeof(msgT)); + if (!newMsg) nomem(); + bzero(newMsg, sizeof(msgT)); + newMsg->prev = msg->prev; + newMsg->next = msg; + if (msg->prev) msg->prev->next = newMsg; + else curSet->first = newMsg; + msg->prev = newMsg; + msg = newMsg; + break; + } + } + if (!msg) { + msg = (msgT *) malloc(sizeof(msgT)); + if (!msg) nomem(); + bzero(msg, sizeof(msgT)); + + if (curSet->first) { + msg->prev = curSet->last; + msg->next = NULL; + curSet->last->next = msg; + curSet->last = msg; + } else { + msg->prev = msg->next = NULL; + curSet->first = curSet->last = msg; + } + } + msg->msgId = msgId; + if (hconst) msg->hconst = dupstr(hconst); + msg->str = dupstr(str); +} + +void MCDelSet( +#if PROTO + int setId) +#else + setId) +int setId; +#endif +{ + setT *set; + msgT *msg; + + for (set = cat->first; set; set = set->next) { + if (set->setId == setId) { + for (msg = set->first; msg; msg = msg->next) { + if (msg->hconst) free(msg->hconst); + if (msg->str) free(msg->str); + free(msg); + } + if (set->hconst) free(set->hconst); + + if (set->prev) set->prev->next = set->next; + else cat->first = set->next; + + if (set->next) set->next->prev = set->prev; + else cat->last = set->prev; + + free(set); + return; + } else if (set->setId > setId) break; + } + warning(NULL, "specified set doesn't exist"); +} + +void MCDelMsg( +#if PROTO + int msgId) +#else + msgId) +int msgId; +#endif +{ + msgT *msg; + + if (!curSet) error(NULL, "you can't delete a message before defining the set"); + + for (msg = curSet->first; msg; msg = msg->next) { + if (msg->msgId == msgId) { + if (msg->hconst) free(msg->hconst); + if (msg->str) free(msg->str); + + if (msg->prev) msg->prev->next = msg->next; + else curSet->first = msg->next; + + if (msg->next) msg->next->prev = msg->prev; + else curSet->last = msg->prev; + + free(msg); + return; + } else if (msg->msgId > msgId) break; + } + warning(NULL, "specified msg doesn't exist"); +} + + + + + + diff --git a/gencat/msgcat.h b/gencat/msgcat.h new file mode 100644 index 0000000..6c3a9d5 --- /dev/null +++ b/gencat/msgcat.h @@ -0,0 +1,178 @@ +/* -*-c++-*- */ + +#ifndef __msgcath + + +/*********************************************************** +Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that Alfalfa's name not be used in +advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +If you make any modifications, bugfixes or other changes to this software +we'd appreciate it if you could send a copy to us so we can keep things +up-to-date. Many thanks. + Kee Hinckley + Alfalfa Software, Inc. + 267 Allston St., #3 + Cambridge, MA 02139 USA + nazgul@alfalfa.com + +******************************************************************/ + + +#include <sys/types.h> + +/* + * On disk data structures + */ + +/* Edit History + +02/25/91 2 nazgul Byte order flags, upped the version number +11/03/90 1 hamilton Alphalpha->Alfalfa & OmegaMail->Poste +08/13/90 1 schulert move from ua to omu +*/ + +/* For or'd constants */ +#define MCMakeId(s,m) (unsigned long) ( ((unsigned short)s << (sizeof(short)*8)) \ + | (unsigned short)m ) +#define MCSetId(id) (unsigned int) ( id >> (sizeof(short) * 8) ) +#define MCMsgId(id) (unsigned int) ( (id << (sizeof(short) * 8)) \ + >> (sizeof(short) * 8) ) +#undef S +#undef UI +#undef UL + +#define MCMagicLen 8 +#define MCMagic "*nazgul*" +#define MCLastMsg 0 +#define MCLastSet 0 + +#define MCMajorVer 1 +#define MCMinorVer 0 + +/* + * Critical note here. Sets and Messages *MUST* be stored in ascending + * order. There are stored that way (by specification) in the original + * data file, however in the process of merging in new stuff you might + * mix that up. Don't! The catget stuff does a binary search and will + * totally lose it if these aren't in order (not contiguous mind you, just + * in order. If this turns out to be a major problem this could be enhanced + * by adding a 'sorted' flag to the db, and sorting msgs and sets at load + * time if things aren't sorted, but I'd like not to have to do that. + */ + +/* + * I have tried here to define data structures which can be used + * while the catalog is on disk, and at runtime. + * This is rather dangerous of course, but I think it can be done without + * overly increasing the memory usage, and it makes loading and storing + * somewhat simpler and less prone to accidents. I have also tried to + * define on disk data structures which can be updated in place, so that + * with a very large catalog (e.g. all system errors) you don't have to + * load everything in memory in order to add or update one set. With + * this in mind there are "invalid" flags which allow items to be + * invalidated and thus not loaded at runtime. Note however that although + * I pay attention to these when I load the DB, I do not currently use + * them in gencat (it just reads everything into memory), so there is + * no guarantee that this will all work. + */ + +/* These should be publicly available */ + +#define MCLoadBySet 0 /* Load entire sets as they are used */ +#define MCLoadAll 1 /* Load entire DB on catopen */ + +extern char *MCAppPath; /* Additional search path for strings (appended) */ + +/* + * MCOffsetT - Union to handle both disk and runtime pointers + */ +typedef union { + off_t off; + char *str; + void *ptr; + struct _MCMsgT *msg; + struct _MCSetT *set; +} MCOffsetT; + +/* + * MCMsgT - Message structure (disk and runtime) + */ +typedef struct _MCMsgT { + long msgId; /* Id of this message */ + MCOffsetT msg; /* Relative offset on disk or pointer in memory */ + long invalid; /* Valid on disk, loaded in memory */ +} MCMsgT; + +/* + * MCSetT - Set structure (disk and runtime) + */ +typedef struct _MCSetT { + long setId; /* Id of this set */ + off_t nextSet; /* Offset of next set on disk */ + union { + off_t firstMsg; /* Offset to first Msg (while on disk) */ + MCMsgT *msgs; /* Pointer to array of msgs (in mem, loaded) */ + } u; + MCOffsetT data; /* Offset to data, or pointer to data */ + long dataLen; /* Length of data area on disk */ + long numMsgs; /* Number of messages */ + long invalid; /* Valid on disk, loaded in memory */ +} MCSetT; + +/* + * MCCatT - Runtime catalog pointer + */ +typedef struct { + long loadType; /* How to load the messages (see MSLoadType) */ +#ifdef HAVE_MMAP + union { +#endif + int fd; /* File descriptor of catalog (if load-on-demand) */ +#ifdef HAVE_MMAP + caddr_t addr; /* Mmaped() address */ + } u; + off_t size; /* File size */ +#endif + long numSets; /* Number of sets */ + MCSetT *sets; /* Pointer to the sets */ + off_t firstSet; /* Offset of first set on disk */ +} MCCatT; + +/* + * MCHeaderT - Disk file header + */ +typedef struct { + char magic[MCMagicLen]; /* Magic cookie "*nazgul*" */ + long majorVer; /* ++ on incompatible changes */ + long minorVer; /* ++ on compatible changes */ + long flags; /* Informational flags */ + long numSets; /* Number of valid Sets */ + off_t firstSet; /* Offset of first set on disk */ +} MCHeaderT; + +/* Some flags */ +#define MC68KByteOrder 0x01 +#define MCn86ByteOrder 0x02 + + + + +#endif |