/* $TOG: mkcatdefs.c /main/4 1997/03/31 13:43:31 dbl $ */ /* * COPYRIGHT NOTICE * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc. * Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group * ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for * the full copyright text. * * This software is subject to an open license. It may only be * used on, with or for operating systems which are themselves open * source systems. You must contact The Open Group for a license * allowing distribution and sublicensing of this software on, with, * or for operating systems which are not Open Source programs. * * See http://www.opengroup.org/openmotif/license for full * details of the license agreement. Any use, reproduction, or * distribution of the program constitutes recipient's acceptance of * this agreement. * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS * PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY * WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY * OR FITNESS FOR A PARTICULAR PURPOSE * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT * NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE * EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * */ /* * HISTORY */ /* * COMPONENT_NAME: (CMDMSG) Message Catalogue Facilities * * FUNCTIONS: main, mkcatdefs, incl, chkcontin, insert, nsearch, hash * * ORIGINS: 27, 18 * * IBM CONFIDENTIAL -- (IBM Confidential Restricted when * combined with the aggregated modules for this product) * OBJECT CODE ONLY SOURCE MATERIALS * (C) COPYRIGHT International Business Machines Corp. 1988, 1989, 1991 * All Rights Reserved * * US Government Users Restricted Rights - Use, duplication or * disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ /* * @OPENGROUP_COPYRIGHT@ */ #include #include #include #include #include #ifdef aix #include #endif #ifdef hpux #ifndef _XPG2 #define _XPG2 #endif #endif #include #include #ifndef NL_TEXTMAX #define NL_TEXTMAX 8192 #endif #ifndef PATH_MAX #define PATH_MAX 1024 #endif #define MAXLINELEN NL_TEXTMAX #define KEY_START '$' #define MAXIDLEN 64 #ifdef _D_NAME_MAX #define MDIRSIZ _D_NAME_MAX #else #define MDIRSIZ 14 #endif static int errflg = 0; static int setno = 1; static int msgno = 1; static int symbflg = 0; static int inclfile = 1; static FILE *outfp; static FILE *msgfp; static FILE *descfile; static char inname [PATH_MAX]; static char outname [PATH_MAX]; static char catname [PATH_MAX]; static char *mname; static void mkcatdefs(char *); static int chkcontin(char *); static int insert(char *, int); static int nsearch(char *); static int hash(char *); /* * NAME: main * * FUNCTION: Make message catalog defines. * * EXECUTION ENVIRONMENT: * User mode. * * NOTES: Invoked by: * mkcatdefs * * Results are 1) Creates header file .h. * 2) Displays message file to stdout. The message file is * ready to be used as input to gencat. * * mkcatdefs takes a message definition file and produces * a header file containing #defines for the message catalog, * the message sets and the messages themselves. It also * produces a new message file which has the symbolic message set and * message identifiers replaced by their numeric values (in the form * required by gencat). * * DATA STRUCTURES: Effects on global data structures -- none. * * RETURNS: 1 - error condition */ int main (int argc, char *argv[]) { register int i; register char *cp; int count; char *t; setlocale (LC_ALL,""); /* usage: handle multiple files; -h option has to be at the end */ if (argc < 3) { fprintf (stderr, "mkcatdefs: Usage: %s header_file msg_file [msg_file...] [-h]\n", argv [0]); exit (0); } /* check if include file should be created; -h is the last argument */ if (argv[argc-1][0] == '-' && argv[argc-1][1] == 'h') inclfile = 0; /* open header output file */ if (inclfile) { mname = argv [1]; if ((int)strlen((t = strrchr(mname,'/')) ? t + 1 : mname) > MDIRSIZ) { fprintf (stderr, "mkcatdefs: header file name too long\n"); exit (1); } sprintf (outname, "%s", mname); if (strrchr(mname,'/')) mname = strrchr(mname,'/') + 1; if ((outfp = fopen (outname, "w")) == NULL) { fprintf (stderr, "mkcatdefs: Cannot open %s\n", outname); exit (1); } else { /* convert name to upper case */ for (cp=mname; *cp; cp+=i) { i = mblen(cp, MB_CUR_MAX); if (i < 0) { fprintf (stderr, "mkcatdefs: filename contains invalid character\n"); exit (1); } if (i == 1) { if (islower(*cp) != 0) *cp = toupper(*cp); else if (!isupper(*cp) && !isdigit(*cp)) *cp = '_'; } } } } else sprintf (outname, "msg.h"); /* open new msg output file */ msgfp = stdout; /* if message descriptor files were specified then process each one in turn */ if (inclfile == 0 ) count = argc - 1; else count = argc; for (i = 2; i < count; i++) { /* open input file */ sprintf (inname, "%s", argv[i]); if (strcmp(inname,"-") == 0) { strcpy(inname,"stdin"); descfile = stdin; /* input from stdin if no source files */ mkcatdefs(inname); } else { if ((descfile = fopen(inname,"r")) == NULL) { fprintf (stderr, "mkcatdefs: Cannot open %s\n", inname); errflg = 1; } else { mkcatdefs (inname); fclose(descfile); descfile = NULL; } } } if (inclfile) { fflush (outfp); if (ferror (outfp)) { fprintf (stderr, "mkcatdefs: There were write errors on file %s\n", outname); errflg = 1; } fclose (outfp); } if (errflg) { fprintf (stderr, "mkcatdefs: Errors found: no %s created\n", outname); if (inclfile) unlink(outname); } else { if (inclfile) { if (symbflg) fprintf (stderr, "mkcatdefs: %s created\n", outname); else { fprintf (stderr, "mkcatdefs: No symbolic identifiers; no %s created\n", outname); unlink (outname); } } else fprintf(stderr, "mkcatdefs: no %s created\n", outname); } exit (errflg); } /* * NAME: mkcatdefs * * FUNCTION: Make message catalog definitions. * * EXECUTION ENVIRONMENT: * User mode. * * RETURNS: None */ static void mkcatdefs(char *fname) /*---- fname: message descriptor file name ----*/ { char msgname [PATH_MAX]; char line [MAXLINELEN]; register char *cp; register char *cpt; register int m; register int n; int contin = 0; int len; /* # bytes in a character */ /* put out header for include file */ if (inclfile) { fprintf (outfp, "/* $%s$ */\n", "XConsortium"); fprintf (outfp, "\n\n/* The following was generated from %s. */\n\n", fname); } /* process the message file */ while (fgets(line, MAXLINELEN, descfile)) { line[MAXLINELEN-1] = '\0'; /* terminate in case length exceeded */ /* find first nonblank character */ for (cp=line; *cp; cp+=len) { len = mblen(cp, MB_CUR_MAX); if (len < 0) { fprintf (stderr, "mkcatdefs: sourcefile contains invalid character:\n\t%s", line); errflg = 1; return; } if (len == 1 && isspace(*cp) == 0) break; } if (*cp == KEY_START) { cp++; for (cpt = cp; *cp; cp += len) { len = mblen(cp, MB_CUR_MAX); if (len < 0) { fprintf (stderr, "mkcatdefs: sourcefile contains invalid character:\n\t%s", line); errflg = 1; return; } if (len == 1 && isspace(*cp) == 0) break; } if (cp != cpt) { sscanf (cp, "%s", msgname); if ((m = nsearch(msgname)) > 0) { fprintf (msgfp, "$ %d", m); cp += strlen(msgname); fprintf (msgfp, "%s", cp); } else fputs (line, msgfp); continue; /* line is a comment */ } if ((strncmp (cp, "set", 3) == 0) && ((len = mblen(&(cp[3]), MB_CUR_MAX)) == 1) && (isspace(cp[3]) != 0)) { char setname [MAXIDLEN]; sscanf (cp+3+len, "%s", setname); if (inclfile) fprintf (outfp, "\n/* definitions for set %s */\n", setname, ""); if (isdigit(setname[0])) { cpt = setname; do { if (!isdigit(*cpt)) { fprintf(stderr, "mkcatdefs: %s is an invalid identifier\n", setname); errflg = 1; return; } } while (*++cpt); n = atoi (setname); if (n >= setno) setno = n; else { if (n == 0) fprintf(stderr, "mkcatdefs: %s is an invalid identifier\n", setname); else fprintf(stderr, "mkcatdefs: set # %d already assigned or sets not in ascending sequence\n", n); errflg = 1; return; } } else { cpt = setname; do { len = mblen(cpt, MB_CUR_MAX); if (len <= 0) { fprintf (stderr, "mkcatdefs: sourcefile contains invalid character:\n\t%s", line); errflg = 1; return; } if (len == 1 && (isalnum(*cpt) == 0) && (*cpt != '_')) { fprintf(stderr, "mkcatdefs: %s is an invalid identifier\n", setname); errflg = 1; return; } } while (*(cpt += len)); if (inclfile) fprintf (outfp, "#define %s %d\n\n", setname, (char *)setno); symbflg = 1; } #ifdef aix fprintf (msgfp,"$delset"); fprintf (msgfp," %d\n", setno); #endif fprintf (msgfp,"%.4s", line); fprintf (msgfp," %d\n", setno++); msgno = 1; continue; } else { /* !!!other command */ } } else if (contin) { if (!chkcontin(line)) contin = 0; } else if (setno > 1) { /* set must have been seen first */ char msgname [MAXIDLEN]; msgname [0] = '\0'; if (sscanf (cp, "%s", msgname) && msgname[0]) { len = mblen(cp, MB_CUR_MAX); if (len < 0) { fprintf (stderr, "mkcatdefs: sourcefile contains invalid character:\n\t%s", line); errflg = 1; return; } if (len == 1 && isalpha(*cp) != 0) { cpt = msgname; do { len = mblen(cpt, MB_CUR_MAX); if (len < 0) { fprintf (stderr, "mkcatdefs: sourcefile contains invalid character:\n\t%s", line); errflg = 1; return; } if (len == 1 && (isalnum(*cpt) == 0) && (*cpt != '_')) { fprintf(stderr, "mkcatdefs: %s is an invalid identifier\n", msgname); errflg = 1; return; } } while (*(cpt += len)); cp += strlen(msgname); fprintf (msgfp,"%d%s", msgno,cp); if (inclfile) fprintf (outfp, "#define %s %d\n", msgname, (char *)msgno); symbflg = 1; if (chkcontin(line)) contin = 1; if(insert(msgname,msgno++) < 0) { fprintf(stderr, "mkcatdefs: name %s used more than once\n", msgname); errflg = 1; return; } continue; } else if (isdigit (msgname[0])){ cpt = msgname; do { if (!isdigit(*cpt)) { fprintf(stderr, "mkcatdefs: invalid syntax in %s\n", line); errflg = 1; return; } } while (*++cpt); n = atoi (msgname); if ((n >= msgno) || (n == 0 && msgno == 1)) msgno = n + 1; else { errflg = 1; if (n == 0) fprintf(stderr, "mkcatdefs: %s is an invalid identifier\n", msgno); else if (n == msgno) fprintf(stderr, "mkcatdefs: message id %s already assigned to identifier\n", msgname); else fprintf(stderr, "mkcatdefs: source messages not in ascending sequence\n"); return; } } } if (chkcontin(line)) contin = 1; } fputs (line, msgfp); } /* make sure the operations read/write operations were successful */ if (ferror(descfile)) { fprintf (stderr, "mkcatdefs: There were read errors on file %s\n", inname); errflg = 1; } return; } /* * NAME: chkcontin * * FUNCTION: Check for continuation line. * * EXECUTION ENVIRONMENT: * User mode. * * RETURNS: 0 - not a continuation line. * 1 - continuation line. */ static int chkcontin(char *line) { int len; /* # bytes in character */ wchar_t wc; /* process code of current character in line */ wchar_t wcprev; /* process code of previous character in line */ for (wc=0; *line; line+=len) { wcprev = wc; len = mbtowc(&wc, line, MB_CUR_MAX); if (len < 0) { fprintf (stderr, "mkcatdefs: sourcefile contains invalid character:\n\t%s", line); errflg = 1; return (0); } } if (wcprev == '\\' && wc == '\n') return (1); return (0); } #define HASHSIZE 256 /* must be a power of 2 */ #define HASHMAX HASHSIZE - 1 struct name { char *regname; int regnr; struct name *left; struct name *right; }; static struct name *symtab[HASHSIZE]; /* hashed pointers to binary trees */ /* * NAME: insert * * FUNCTION: Insert symbol * * EXECUTION ENVIRONMENT: * User mode. * * NOTES: These routines manipulate a symbol table for the mkcatdefs program. * The symbol table is organized as a hashed set of binary trees. If the * symbol being passed in is found then a -1 is returned, otherwise the * symbol is placed in the symbol table and a 0 is returned. The purpose * of the symbol table is to keep mkcatdefs from assigning two different * message set / message numbers to the same symbol. * Read the next line from the open message catalog descriptor file. * * RETURNS: 0 - symbol inserted. * -1 - symbol exists. */ static int insert(char *tname, int seqno) /* tname - pointer to symbol seqno - integer value of symbol */ { register struct name *ptr,*optr; int rslt = -1,i,hashval; hashval = hash(tname); ptr = symtab[hashval]; /* search the binary tree for specified symbol */ while (ptr && (rslt = strcmp(tname,ptr->regname))) { optr=ptr; if (rslt<0) ptr = ptr->left; else ptr = ptr->right; } if (rslt == 0) /* found the symbol already defined */ return (-1); else { /* symbol not defined yet so put it into symbol table */ ptr = (struct name *)calloc(sizeof(struct name), 1); ptr->regname = malloc(strlen(tname) + 1); strcpy (ptr->regname, tname); ptr->regnr = seqno; /* not first entry in tree so update branch pointer */ if (symtab[hashval]) { if (rslt < 0) optr->left = ptr; else optr->right = ptr; /* first entry in tree so set the root pointer */ } else symtab[hashval] = ptr; return (0); } } /* * NAME: nsearch * * FUNCTION: Search for symbol * * EXECUTION ENVIRONMENT: * User mode. * * NOTES: Searches for specific identifier. If found, return allocated number. * If not found, return -1. * * RETURNS: Symbol sequence number if symbol is found. * -1 if symbol is not found. */ static int nsearch (char *tname) /* tname - pointer to symbol */ { register struct name *ptr,*optr; int rslt = -1,i,hashval; hashval = hash(tname); ptr = symtab[hashval]; /* search the binary tree for specified symbol */ while (ptr && (rslt = strcmp(tname,ptr->regname))) { optr=ptr; if (rslt<0) ptr = ptr->left; else ptr = ptr->right; } if (rslt == 0) /* found the symbol already defined */ return(ptr->regnr); else return (-1); } /* * NAME: hash * * FUNCTION: Create hash value from symbol name. * * EXECUTION ENVIRONMENT: * User mode. * * NOTES: Hash the symbol name using simple addition algorithm. * Make sure that the hash value is in range when it is returned. * * RETURNS: A hash value. */ static int hash (char *name) /* pointer to symbol */ { register int hashval = 0; while (*name) hashval += *name++; return (hashval & HASHMAX); }