Merge branch 'cde-fixups-1' of ssh://git.code.sf.net/p/cdesktopenv/code into cde...
[oweals/cde.git] / cde / programs / localized / util / mkcatdefs.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  * HISTORY
25  */
26 /*
27  * COMPONENT_NAME: (CMDMSG) Message Catalogue Facilities
28  *
29  * FUNCTIONS: main, mkcatdefs, incl, chkcontin, insert, nsearch, hash
30  *
31  * ORIGINS: 27, 18
32  *
33  * IBM CONFIDENTIAL -- (IBM Confidential Restricted when
34  * combined with the aggregated modules for this product)
35  * OBJECT CODE ONLY SOURCE MATERIALS
36  * (C) COPYRIGHT International Business Machines Corp. 1988, 1989, 1991
37  * All Rights Reserved
38  *
39  * US Government Users Restricted Rights - Use, duplication or
40  * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
41  */
42 /*
43  * @OSF_COPYRIGHT@
44  */
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <locale.h>
49 #include <ctype.h>
50 #include <ctype.h>
51
52 #ifdef aix
53 #include <sys/dir.h>
54 #endif
55
56 #ifdef hpux
57 #ifndef _XPG2
58 #define _XPG2
59 #endif
60 #endif
61
62 #include <limits.h>
63 #include <string.h>
64
65 #ifndef NL_TEXTMAX
66 #define NL_TEXTMAX 8192
67 #endif
68
69 #ifndef PATH_MAX
70 #define PATH_MAX 1024
71 #endif
72
73 #define MAXLINELEN NL_TEXTMAX
74 #define KEY_START '$'
75 #define MAXIDLEN 64
76 #ifdef _D_NAME_MAX
77 #define MDIRSIZ _D_NAME_MAX
78 #else
79 #define MDIRSIZ 14
80 #endif
81
82 static int errflg = 0;
83 static int setno = 1;
84 static int msgno = 1;
85 static int symbflg = 0;
86 static int inclfile = 1;
87 static FILE *outfp;
88 static FILE *msgfp;
89 static FILE *descfile;
90 static char inname [PATH_MAX];
91 static char outname [PATH_MAX];
92 static char catname [PATH_MAX];
93 static char *mname;
94 static void mkcatdefs(char *);
95 static int chkcontin(char *);
96 static int insert(char *, int);
97 static int nsearch(char *);
98 static int hash(char *);
99
100
101 /*
102  * NAME: main
103  *
104  * FUNCTION: Make message catalog defines.
105  *
106  * EXECUTION ENVIRONMENT:
107  *      User mode.
108  *
109  * NOTES:  Invoked by:
110  *         mkcatdefs <name> <msg_file>
111  *
112  *      Results are 1) Creates header file <name>.h.
113  *                  2) Displays message file to stdout. The message file is 
114  *                     ready to be used as input to gencat.
115  *
116  *      mkcatdefs takes a message definition file and produces
117  *      a header file containing #defines for the message catalog,
118  *      the message sets and the messages themselves.  It also
119  *      produces a new message file which has the symbolic message set and
120  *      message identifiers replaced by their numeric values (in the form
121  *      required by gencat).
122  *
123  * DATA STRUCTURES: Effects on global data structures -- none.
124  *
125  * RETURNS: 1 - error condition
126  */
127
128 int
129 main (int argc, 
130       char *argv[]) 
131 {
132   register int i;
133   register char *cp;
134   int count;
135   char *t;
136   
137   setlocale (LC_ALL,"");
138   
139   /* usage: handle multiple files; -h option has to be at the end */
140   if (argc < 3) {
141     fprintf (stderr, 
142              "mkcatdefs: Usage: %s header_file msg_file [msg_file...] [-h]\n", 
143              argv [0]); 
144     exit (0);
145   }
146   
147   /* check if  include file should be created; -h is the last argument */
148   if (argv[argc-1][0] == '-' && argv[argc-1][1] == 'h') 
149     inclfile = 0;
150   
151   /* open header output file */
152   if (inclfile) {
153     mname = argv [1];
154     if ((int)strlen((t = strrchr(mname,'/')) ? t + 1 : mname) > MDIRSIZ) {
155       fprintf (stderr, "mkcatdefs: header file name too long\n");
156       exit (1);
157     }
158     sprintf (outname, "%s", mname);
159     if (strrchr(mname,'/'))
160       mname = strrchr(mname,'/') + 1;
161     if ((outfp = fopen (outname, "w")) == NULL) {
162       fprintf (stderr, "mkcatdefs: Cannot open %s\n", outname);
163       exit (1);
164     } else  {
165       /* convert name to upper case */
166       for (cp=mname; *cp; cp+=i) {
167         i = mblen(cp, MB_CUR_MAX);
168         if (i < 0) {
169           fprintf (stderr, "mkcatdefs: filename contains invalid character\n");
170           exit (1);
171         }
172         if (i == 1) {
173           if (islower(*cp) != 0)
174             *cp = toupper(*cp);
175           else if (!isupper(*cp) && !isdigit(*cp))
176             *cp = '_';
177         }
178       }
179     }
180   } else sprintf (outname, "msg.h");
181   
182   
183   /* open new msg output file */
184   msgfp = stdout;
185   
186   /* if message descriptor files were specified then process each one in turn 
187    */
188   
189   if (inclfile == 0 )
190     count = argc - 1;
191   else
192     count = argc;
193   for (i = 2; i < count; i++) {
194     /* open input file */
195     sprintf (inname, "%s", argv[i]);
196     if (strcmp(inname,"-") == 0) {
197       strcpy(inname,"stdin");
198       descfile = stdin;       /* input from stdin if no source files */
199       mkcatdefs(inname);
200     } else      {
201       if ((descfile = fopen(inname,"r")) == NULL) {
202         fprintf (stderr, "mkcatdefs: Cannot open %s\n", inname);
203         errflg = 1;
204       } else  {
205         mkcatdefs (inname);
206         fclose(descfile);
207         descfile = NULL;
208       }
209     }
210   }
211   
212   if (inclfile) {
213     fflush (outfp);
214     if (ferror (outfp)) {
215       fprintf (stderr, "mkcatdefs: There were write errors on file %s\n", 
216                outname);
217       errflg = 1;
218     }
219     fclose (outfp);
220   }
221   
222   if (errflg) {
223     fprintf (stderr, "mkcatdefs: Errors found: no %s created\n", outname);
224     if (inclfile)  unlink(outname);
225   } else {
226     if (inclfile) {
227       if (symbflg)
228         fprintf (stderr, "mkcatdefs: %s created\n", outname);
229       else {
230         fprintf (stderr, "mkcatdefs: No symbolic identifiers; no %s created\n",
231                  outname);
232         unlink (outname);
233       }
234     } 
235     else 
236       fprintf(stderr, "mkcatdefs: no %s created\n", outname);
237   }
238   exit (errflg);
239 }
240
241 /*
242  * NAME: mkcatdefs
243  *
244  * FUNCTION: Make message catalog definitions.
245  *
246  * EXECUTION ENVIRONMENT:
247  *      User mode.
248  *
249  * RETURNS: None
250  */
251 static void
252 mkcatdefs(char *fname)
253      /*---- fname: message descriptor file name ----*/
254 {
255   char msgname [PATH_MAX];
256   char line [MAXLINELEN];
257   register char *cp;
258   register char *cpt;
259   register int m;
260   register int n;
261   int contin = 0;
262   int len;              /* # bytes in a character */
263   
264   
265   /* put out header for include file */
266   if (inclfile)
267     {
268       fprintf (outfp, "/* $%s$ */\n", "XConsortium");
269       fprintf (outfp, "\n\n/* The following was generated from %s. */\n\n", 
270                fname);
271     }
272   
273   /* process the message file */
274   while (fgets(line, MAXLINELEN, descfile)) {
275     line[MAXLINELEN-1] = '\0'; /* terminate in case length exceeded */
276     /* find first nonblank character */
277     for (cp=line; *cp; cp+=len) {
278       len = mblen(cp, MB_CUR_MAX);
279       if (len < 0) {
280         fprintf (stderr, 
281                  "mkcatdefs: sourcefile contains invalid character:\n\t%s", 
282                  line);
283         errflg = 1;
284         return;
285       }
286       if (len == 1 && isspace(*cp) == 0)
287         break;
288     }
289     if (*cp == KEY_START) {
290       cp++;
291       for (cpt = cp; *cp; cp += len) {
292         len = mblen(cp, MB_CUR_MAX);
293         if (len < 0) {
294           fprintf (stderr, 
295                    "mkcatdefs: sourcefile contains invalid character:\n\t%s",
296                    line);
297           errflg = 1;
298           return;
299         }
300         if (len == 1 && isspace(*cp) == 0)
301           break;
302       }
303       if (cp != cpt) {
304         sscanf (cp, "%s", msgname);
305         if ((m = nsearch(msgname)) > 0) {
306           fprintf (msgfp, "$ %d", m);
307           cp += strlen(msgname);
308           fprintf (msgfp, "%s", cp);
309         } else
310           fputs (line, msgfp);
311         continue; /* line is a comment */
312       }
313       if ((strncmp (cp, "set", 3) == 0) && 
314           ((len = mblen(&(cp[3]), MB_CUR_MAX)) == 1) && 
315           (isspace(cp[3]) != 0)) {
316         char setname [MAXIDLEN];
317         
318         sscanf (cp+3+len, "%s", setname);
319         if (inclfile) 
320           fprintf (outfp, "\n/* definitions for set %s */\n", setname, "");
321         if (isdigit(setname[0])) {
322           cpt = setname;
323           do  {
324             if (!isdigit(*cpt)) {
325               fprintf(stderr, "mkcatdefs: %s is an invalid identifier\n", 
326                       setname);
327               errflg = 1;
328               return;
329             }
330           }   while (*++cpt);
331           n = atoi (setname);
332           if (n >= setno)
333             setno = n;
334           else {
335             if (n == 0)
336               fprintf(stderr, "mkcatdefs: %s is an invalid identifier\n", 
337                       setname); 
338             else
339               fprintf(stderr, 
340                       "mkcatdefs: set # %d already assigned or sets not in ascending sequence\n", 
341                       n);
342             errflg = 1;
343             return;
344           }
345         } else  {
346           cpt = setname;
347           do  {
348             len = mblen(cpt, MB_CUR_MAX);
349             if (len <= 0) {
350               fprintf (stderr, 
351                        "mkcatdefs: sourcefile contains invalid character:\n\t%s",
352                        line);
353               errflg = 1;
354               return;
355             }
356             if (len == 1 && (isalnum(*cpt) == 0) && (*cpt != '_')) {
357               fprintf(stderr, 
358                       "mkcatdefs: %s is an invalid identifier\n", 
359                       setname);
360               errflg = 1;
361               return;
362             }
363           } while (*(cpt += len));
364           if (inclfile)
365             fprintf (outfp, "#define %s %d\n\n", setname, setno);
366           symbflg = 1;
367         }
368 #ifdef aix
369         fprintf (msgfp,"$delset");
370         fprintf (msgfp," %d\n", setno);
371 #endif
372         fprintf (msgfp,"%.4s", line);
373         fprintf (msgfp," %d\n", setno++);
374         msgno = 1;
375         continue;
376       } else {
377         /* !!!other command */
378       }
379     } else
380       if (contin) {
381         if (!chkcontin(line))
382           contin = 0;
383       } else if (setno > 1) { /* set must have been seen first */
384         char msgname [MAXIDLEN];
385         
386         msgname [0] = '\0';
387         if (sscanf (cp, "%s", msgname) && msgname[0]) {
388           len = mblen(cp, MB_CUR_MAX);
389           if (len < 0) {
390             fprintf (stderr, 
391                      "mkcatdefs: sourcefile contains invalid character:\n\t%s",
392                      line);
393             errflg = 1;
394             return;
395           }
396           if (len == 1 && isalpha(*cp) != 0) {
397             cpt = msgname;
398             do  {
399               len = mblen(cpt, MB_CUR_MAX);
400               if (len < 0) {
401                 fprintf (stderr, 
402                          "mkcatdefs: sourcefile contains invalid character:\n\t%s",
403                          line);
404                 errflg = 1;
405                 return;
406               }
407               if (len == 1 && (isalnum(*cpt) == 0) && (*cpt != '_')) {
408                 fprintf(stderr, "mkcatdefs: %s is an invalid identifier\n", 
409                         msgname);       
410                 errflg = 1;
411                 return;
412               }
413             }   while (*(cpt += len));
414             cp += strlen(msgname);
415             fprintf (msgfp,"%d%s", msgno,cp);
416             if (inclfile)
417               fprintf (outfp, "#define %s %d\n", msgname, msgno);
418             symbflg = 1;
419             if (chkcontin(line))
420               contin = 1;
421             if(insert(msgname,msgno++) < 0) {
422               fprintf(stderr, "mkcatdefs: name %s used more than once\n", 
423                       msgname); 
424               errflg = 1;
425               return;
426             }
427             continue;
428           } else if (isdigit (msgname[0])){
429             cpt = msgname;
430             do  {
431               if (!isdigit(*cpt)) {
432                 fprintf(stderr, "mkcatdefs: invalid syntax in %s\n", line);
433                 errflg = 1;
434                 return;
435               }
436             }   while (*++cpt);
437             n = atoi (msgname);
438             if ((n >= msgno) || (n == 0 && msgno == 1))
439               msgno = n + 1;
440             else {
441               errflg = 1;
442               if (n == 0)
443                 fprintf(stderr, "mkcatdefs: %d is an invalid identifier\n", 
444                         msgno);
445               else if (n == msgno) 
446                 fprintf(stderr,
447                         "mkcatdefs: message id %s already assigned to identifier\n", 
448                         msgname);
449               else
450                 fprintf(stderr,
451                         "mkcatdefs: source messages not in ascending sequence\n");
452               return;
453             }
454           }
455         }
456         if (chkcontin(line))
457           contin = 1;
458       }
459     fputs (line, msgfp);
460   }
461   
462   /* make sure the operations read/write operations were successful */
463   if (ferror(descfile)) {
464     fprintf (stderr, "mkcatdefs: There were read errors on file %s\n", inname);
465     errflg = 1;
466   }
467   return;
468 }
469
470 /*
471  * NAME: chkcontin
472  *
473  * FUNCTION: Check for continuation line.
474  *
475  * EXECUTION ENVIRONMENT:
476  *      User mode.
477  *
478  * RETURNS: 0 - not a continuation line.
479  *          1 - continuation line.
480  */
481 static int
482 chkcontin(char *line) 
483 {
484   int   len;            /* # bytes in character */
485   wchar_t       wc;             /* process code of current character in line */
486   wchar_t       wcprev;         /* process code of previous character in line */
487   
488   for (wc=0; *line; line+=len) {
489     wcprev = wc;
490     len = mbtowc(&wc, line, MB_CUR_MAX);
491     if (len < 0) {
492       fprintf (stderr, 
493                "mkcatdefs: sourcefile contains invalid character:\n\t%s",
494                line);
495       errflg = 1;
496       return (0);
497     }
498   }
499   if (wcprev == '\\' && wc == '\n')
500     return (1);
501   return (0);
502 }
503
504 #define HASHSIZE 256                    /* must be a power of 2 */
505 #define HASHMAX HASHSIZE - 1
506
507 struct name {
508   char *regname;
509   int   regnr;
510   struct name *left;
511   struct name *right;
512 };
513
514 static struct name *symtab[HASHSIZE];   /* hashed pointers to binary trees */
515
516 /*
517  * NAME: insert
518  *
519  * FUNCTION: Insert symbol
520  *
521  * EXECUTION ENVIRONMENT: 
522  *      User mode.
523  * 
524  * NOTES: These routines manipulate a symbol table for the mkcatdefs program.
525  *        The symbol table is organized as a hashed set of binary trees. If the
526  *        symbol being passed in is found then a -1 is returned, otherwise the
527  *        symbol is placed in the symbol table and a 0 is returned. The purpose
528  *        of the symbol table is to keep mkcatdefs from assigning two different
529  *        message set / message numbers to the same symbol.
530  *        Read the next line from the open message catalog descriptor file.
531  *
532  * RETURNS: 0 - symbol inserted.
533  *         -1 - symbol exists.
534  */
535
536 static int 
537 insert(char *tname,
538        int seqno)
539      /*
540        tname - pointer to symbol
541        seqno - integer value of symbol
542        */
543      
544 {
545   register struct name *ptr,*optr;
546   int rslt = -1,i,hashval;
547   
548   hashval = hash(tname);
549   ptr = symtab[hashval];
550   
551   /* search the binary tree for specified symbol */
552   while (ptr && (rslt = strcmp(tname,ptr->regname))) {
553     optr=ptr;  
554     if (rslt<0)
555       ptr = ptr->left;
556     else
557       ptr = ptr->right;
558   }
559   
560   if (rslt == 0)  /* found the symbol already defined */
561     return (-1);
562   else {          /* symbol not defined yet so put it into symbol table */    
563     ptr = (struct name *)calloc(sizeof(struct name), 1);
564     ptr->regname = malloc(strlen(tname) + 1);
565     strcpy (ptr->regname, tname);
566     ptr->regnr = seqno;
567     
568     /* not first entry in tree so update branch pointer */
569     if (symtab[hashval]) {
570       if (rslt < 0)
571         optr->left = ptr;
572       else
573         optr->right = ptr;
574       
575       /* first entry in tree so set the root pointer */
576     } else
577       symtab[hashval] = ptr;
578     
579     return (0);
580   }
581 }
582
583 /*
584  * NAME: nsearch
585  *
586  * FUNCTION: Search for symbol
587  *
588  * EXECUTION ENVIRONMENT: 
589  *      User mode.
590  * 
591  * NOTES: Searches for specific identifier. If found, return allocated number.
592  *        If not found, return -1.
593  *
594  * RETURNS: Symbol sequence number if symbol is found.
595  *          -1 if symbol is not found.
596  */
597 static int 
598 nsearch (char *tname)
599      /*
600        tname - pointer to symbol
601        */
602      
603 {
604   register struct name *ptr,*optr;
605   int rslt = -1,i,hashval;
606   
607   hashval = hash(tname);
608   ptr = symtab[hashval];
609   
610   /* search the binary tree for specified symbol */
611   while (ptr && (rslt = strcmp(tname,ptr->regname))) {
612     optr=ptr;  
613     if (rslt<0)
614       ptr = ptr->left;
615     else
616       ptr = ptr->right;
617   }
618   
619   if (rslt == 0)                /* found the symbol already defined */
620     return(ptr->regnr);
621   else
622     return (-1);
623 }
624
625
626 /*
627  * NAME: hash
628  *
629  * FUNCTION: Create hash value from symbol name.
630  *
631  * EXECUTION ENVIRONMENT: 
632  *      User mode.
633  * 
634  * NOTES: Hash the symbol name using simple addition algorithm.
635  *        Make sure that the hash value is in range when it is returned.
636  *
637  * RETURNS: A hash value.
638  */
639
640 static int 
641 hash (char *name) /* pointer to symbol */
642 {
643   register int hashval = 0;
644   
645   while (*name)
646     hashval += *name++;
647   
648   return (hashval & HASHMAX);
649 }