Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dthelp / parser.ccdf / volumegen / Volumegen.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 librararies 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 /* $XConsortium: Volumegen.c /main/4 1995/11/08 11:48:17 rswiston $ */
24 /************************************<+>*************************************
25  ****************************************************************************
26  *
27  * File:         Volumegen.c
28  *
29  * Project:      Cache Creek (Rivers) Project
30  * Description:  Source for the 'volumegen' portion of HelpTAG.
31  * Author:       Brian Cripe
32  *               Modifications by Mike Wilson
33  *               Modifications to support non-topic IDs added by Dex Smith
34  * Language:     C
35  *
36  * (C) Copyright 1992, Hewlett-Packard, all rights reserved.
37  *
38  ****************************************************************************
39  ************************************<+>*************************************/
40
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <fcntl.h>
47 #include <string.h>
48 #include <locale.h>
49
50 /* Local Includes */
51 #include "PstackI.h"
52
53 #define TRUE 1
54 #define FALSE 0
55
56 typedef int Boolean;
57
58 /* extern int errno; */
59 /* extern char *sys_errlist[]; */
60 /* extern int sys_nerr; */
61
62 static void GenTopicList (
63     FILE *infile,
64     FILE *outfile);
65
66 static void GenTopicHeir(
67     FILE *infile,
68     FILE *outfile);
69
70 static void GenTopicLoc(
71     FILE *infile,
72     FILE *outfile);
73
74 static void GenHomeLocation(
75     FILE *outfile);
76
77 static void GenKeyWordList(
78     FILE *infile, 
79     FILE *outfile);
80
81 static void MergeMetaInfo(
82     FILE *infile, 
83     FILE *outfile);
84
85 static char *GetNextToken(
86     FILE *file,
87     Boolean delimIsSpace);
88
89
90
91 /* Volumegen Input file suffixes */
92
93 #define HEIR_FILE_SUFFIX ".tpc"  /* The input filename suffix used for topic */
94                                  /* heirarchy files. */
95 #define TLOC_FILE_SUFFIX ".idt"  /* The input filename suffix used for topic */
96                                  /* location files. */
97
98 #define IKEY_FILE_SUFFIX ".idx"  /* The input filename suffix used for sorted*/
99                                  /* keyword file. */
100 #define IMETA_FILE_SUFFIX ".hmi" /* The input filename suffix used for meta */
101                                  /* information file. */
102
103
104
105 /* Volumegen Output file suffixes */
106
107
108 #define VOL_FILE_SUFFIX ".hv"   /* The filename suffix used for volume */
109                                 /* file generated by volumegen. */
110
111 #define KEY_FILE_SUFFIX ".hvk"  /* The filename suffix used for keyword */
112                                 /* file generated by volumegen. */
113
114
115 \f
116 /*****************************************************************************
117  * Function:        void main();
118  *
119  *
120  * Purpose:   Program Main line body        
121  *
122  *****************************************************************************/
123 void main (
124     int argc,
125     char **argv)
126 {
127     char *fName;
128     FILE *tlocFile, *heirFile, *keyFile, *metaFile;
129     FILE *volFile, *keyWordFile;
130
131     setlocale(LC_ALL, "");
132
133     /* Make sure that an argument naming the volume was supplied. */
134     if (argc < 2) {
135         fprintf (stderr, "usage: %s <volume-name>\n", *argv);
136         exit (1);
137     }
138
139     /* Construct the name of the heirarchy file and open it. */
140     fName = malloc (strlen (*(argv+1)) + strlen (HEIR_FILE_SUFFIX) + 1);
141     strcpy (fName, *(argv+1));
142     strcat (fName, HEIR_FILE_SUFFIX);
143     if ((heirFile = fopen (fName, "r")) == NULL) {
144         fprintf (stderr, 
145                  "%s: error opening heirarchy file %s for reading\n", 
146                  *argv, fName);
147         perror (NULL);
148         exit (1);
149     }
150
151     /* Construct the name of the topic location file and open it. */
152     fName = malloc (strlen (*(argv+1)) + strlen (TLOC_FILE_SUFFIX) + 1);
153     strcpy (fName, *(argv+1));
154     strcat (fName, TLOC_FILE_SUFFIX);
155     if ((tlocFile = fopen (fName, "r")) == NULL) {
156         fprintf (stderr, 
157                  "%s: error opening topic location file %s for reading\n", 
158                  *argv, fName);
159         perror (NULL);
160         exit (1);
161     }
162
163     /* Construct the name of the keyword file and open it. */
164     fName = malloc (strlen (*(argv+1)) + strlen (IKEY_FILE_SUFFIX) + 1);
165     strcpy (fName, *(argv+1));
166     strcat (fName, IKEY_FILE_SUFFIX);
167     if ((keyFile = fopen (fName, "r")) == NULL) {
168         fprintf (stderr, 
169                  "%s: error opening keyword file %s for reading\n", 
170                  *argv, fName);
171         perror (NULL);
172         exit (1);
173     }
174
175   
176     /* Construct the name of the meta information file and open it. */
177     fName = malloc (strlen (*(argv+1)) + strlen (IMETA_FILE_SUFFIX) + 1);
178     strcpy (fName, *(argv+1));
179     strcat (fName, IMETA_FILE_SUFFIX);
180     if ((metaFile = fopen(fName, "r")) == NULL) {
181         fprintf (stderr, 
182                  "%s: error opening meta file %s for reading\n", 
183                  *argv, fName);
184         perror (NULL);
185         exit (1);
186     }
187  
188
189    /* Construct the name of the volume file (the one we are creating) 
190       and open it. */
191     fName = malloc (strlen (*(argv+1)) + strlen (VOL_FILE_SUFFIX) + 1);
192     strcpy (fName, *(argv+1));
193     strcat (fName, VOL_FILE_SUFFIX);
194     if ((volFile = fopen (fName, "w")) == NULL) {
195         fprintf (stderr, "%s: error opening Volume file %s for writing\n", 
196                  *argv, fName);
197         perror (NULL);
198         exit (1);
199     }
200
201  /* Construct the name of the keyword file (the one we are creating) 
202       and open it. */
203     fName = malloc (strlen (*(argv+1)) + strlen (KEY_FILE_SUFFIX) + 1);
204     strcpy (fName, *(argv+1));
205     strcat (fName, KEY_FILE_SUFFIX);
206     if ((keyWordFile = fopen (fName, "w")) == NULL) {
207         fprintf (stderr, "%s: error opening Keyword file %s for writing\n", 
208                  *argv, fName);
209         perror (NULL);
210         exit (1);
211     }
212
213
214     /* Generate topic list and heirarchy */
215     GenTopicList (heirFile, volFile);
216     GenTopicHeir (heirFile, volFile);
217
218     /* Generate topic location table. */
219     GenTopicLoc(tlocFile, volFile);
220
221
222     /* Generate the keyword information */
223     GenKeyWordList(keyFile, keyWordFile);
224
225
226     /* Merge the Meta information */
227     MergeMetaInfo(metaFile, volFile);
228
229     /* Generate the Home Topic entry */
230     GenHomeLocation(volFile);
231
232     exit(0);
233 }
234
235 \f
236 /*****************************************************************************
237  * Function:        static void GenTopicList(
238  *                              FILE *infile,
239  *                              FILE *outfile)
240  *
241  * Parameters:      infile
242  *                  outfile
243  *
244  * Return Value:    Void.
245  *
246  * Purpose:         Generates the topic list in the <class>.hv file.        
247  *
248  *****************************************************************************/
249 static void GenTopicList (
250     FILE *infile,
251     FILE *outfile)
252 {
253     char *token;
254
255     fseek (infile, 0, SEEK_SET);
256
257     fprintf (outfile, "# Topic List\n");
258
259     /* The output formatting is kind of tricky as we want to end every
260        line with a "\", except for the last line.  Therefore each time
261        through the loop we print the "\\\n" for the previous line and 
262        the body of the current line. */
263     fprintf (outfile, "*.topicList: ");
264     while ((token = GetNextToken (infile, TRUE)) != NULL) {
265
266         if ((strcmp (token, "{") != 0) && (strcmp (token, "}") != 0))
267             fprintf (outfile, "\\\n\t%s ", token);
268
269         free (token);
270     }
271
272     fprintf (outfile, "\n\n");
273 }
274
275
276 \f
277 /*****************************************************************************
278  * Function:        static void GenTopicHeir(
279  *                              FILE *infile,
280  *                              FILE *outfile)
281  *
282  * Parameters:      infile
283  *                  outfile
284  *
285  * Return Value:    Void.
286  *
287  * Purpose:         Generates the topic hierarchy in the <class>.hv file.  
288  *
289  *****************************************************************************/
290 static void GenTopicHeir(
291     FILE *infile,
292     FILE *outfile)
293 {
294     char *token, *prevToken, *tempS;
295
296     PStack topicStack;
297
298     topicStack = PStackCreate();
299     fseek (infile, 0, SEEK_SET);
300
301     fprintf (outfile, "# Topic Heirarchy\n");
302     prevToken = NULL;
303     while ((token = GetNextToken (infile, TRUE)) != NULL) {
304
305         if (strcmp (token, "{") == 0)
306             PStackPush (topicStack, prevToken);
307
308         else if (strcmp (token, "}") == 0) {
309             if (prevToken != NULL)
310                 free (prevToken);
311             
312             free (PStackPop (topicStack));
313         }
314
315         else {
316             tempS = (char *) PStackPeek (topicStack);
317             if (tempS == NULL) {
318                 tempS = "";
319             }
320             fprintf (outfile, "*.%s.parent:\t%s\n", token, tempS);
321
322             if (prevToken != NULL)
323                 free (prevToken);
324         }
325
326         prevToken = token;
327     }
328     PStackDestroy (topicStack);
329     fprintf (outfile, "\n");
330 }
331
332
333 \f
334 /*****************************************************************************
335  * Function:        static void GenTopicLoc(
336  *                              FILE *infile,
337  *                              FILE *outfile)
338  *
339  * Parameters:      infile
340  *                  outfile
341  *
342  * Return Value:    Void.
343  *
344  * Purpose:         Generates the topic Location section in the 
345  *                  <class>.hv file.  
346  *
347  *****************************************************************************/
348 static void GenTopicLoc(
349     FILE *infile,
350     FILE *outfile)
351 {
352     char *topic, *topicFile, *topicPos;
353
354     char *trueTopicID = NULL;
355     char *locationIdNew = NULL;
356     char *idList = NULL;
357
358     fseek (infile, 0, SEEK_SET);
359
360     fprintf (outfile, 
361        "# Topic Locations (file names, file positions, and non-topic IDs)\n");
362
363     /* Topic location files have a strict syntax where each line contains
364        either three tokens (the topic ID, the file containing the topic,
365        and the byte offset to the start of the topic), or two tokens (the
366        topic ID and a non-topic ID that occurs within the topic).  The
367        first style (three tokens) is identified by the trailing ":" on the
368        topic ID.  Similarly, the second style (two tokens) is identified by
369        the trailing ">" on the topic ID.  If the file deviates from these
370        two variations of the syntax, the file is considered corrupt.  */
371
372     while (1) {
373         if ((topic = GetNextToken (infile, TRUE)) == NULL)
374             break;
375
376         /* If the topic ID ends with ":", then we've found a primary entry. */
377         if (*(topic + strlen (topic) - 1) == ':')
378           {
379             /* Strip the ":" at the end of the topic ID */
380             *(topic + strlen (topic) - 1) = '\0';
381
382             /* if there was a previous trueTopicID, print it out */
383             if (trueTopicID != (char *)NULL  && idList != (char *)NULL)
384               {
385                 fprintf(outfile, "*.%s.locationIDs:\t%s\n", 
386                         trueTopicID, idList);
387                 free (trueTopicID);
388                 free (idList);
389                 idList = (char *)NULL;
390               }
391
392             /* Save the new true topic. */
393             trueTopicID = malloc(strlen(topic) + 1);
394             strcpy(trueTopicID, topic);
395
396             if ((topicFile = GetNextToken (infile, TRUE)) == NULL)
397               break;
398
399             if ((topicPos = GetNextToken (infile, TRUE)) == NULL)
400               break;
401
402             fprintf (outfile, "*.%s.filename:\t%s\n", topic, topicFile);
403             fprintf (outfile, "*.%s.filepos:\t%s\n", topic, topicPos);
404         
405             free (topicFile);
406             free (topicPos);
407           }
408         /* Else, if the id ends with ">", we've found a non-topic entry. */
409         else if (*(topic + strlen (topic) - 1) == '>')
410           {
411             /* Get the next token, its the non-topic ID. */
412             if ((locationIdNew = GetNextToken (infile, TRUE)) == NULL)
413               break;
414
415             /* If the ID list hasn't been started for this topic, start it. */
416             if (idList == (char *)NULL)
417               {
418               idList = malloc(strlen(locationIdNew) + 1);
419               strcpy(idList, locationIdNew);
420               }
421             /* Else, add the ID we've just found to the list. */
422             else
423               {
424                 /* append the non-topic location ID to the list */
425                 idList = 
426                   realloc(idList, strlen(idList) + strlen(locationIdNew) +2);
427             
428                 strcat(idList, " ");
429                 strcat(idList, locationIdNew);
430               }
431           }
432         else
433           {
434             /* If the token does not end with ":" or ">", we have a problem. */
435             printf("The <volume>%s file is corrupt!\n", TLOC_FILE_SUFFIX);
436           }
437         free (topic);
438     }
439   
440     if (trueTopicID != (char *)NULL  && idList != (char *)NULL)
441               {
442                 fprintf(outfile, "*.%s.locationIDs:\t%s\n", 
443                         trueTopicID, idList);
444                 free (trueTopicID);
445                 free (idList);
446               }
447
448     fprintf (outfile, "\n\n");
449 }
450
451
452
453
454
455 \f
456 /*****************************************************************************
457  * Function:        static void GenKeyWordList(
458  *                              FILE *infile,
459  *                              FILE *outfile)
460  *
461  * Parameters:      infile
462  *                  outfile
463  *
464  * Return Value:    Void.
465  *
466  * Purpose:         Generates the keyword list using the <class>.idx file and
467  *                  writes the results to <class>.hv file.  
468  *
469  *****************************************************************************/
470 static void GenKeyWordList(
471     FILE *infile, 
472     FILE *outfile)
473
474 {
475    char *sortKey=NULL;
476    char *keyWordNew=NULL;
477    char *locationIdNew=NULL;
478    char *keyWordLast=NULL;
479    char *locationIdLast=NULL;
480    char *idList=NULL;
481    char *newList=NULL;
482    static char empty[] = "";
483
484    fseek (infile, 0, SEEK_SET);
485
486    fprintf (outfile, "# Keyword Index\n");
487
488     /* idx files (e.g. keyword) have a strict syntax where each line contains
489      * three tokens (the sort key, the keyword, and the locationID of the 
490      * topic in which the keyword resides). Therefore it is an error if the 
491      * number of tokens in the file is not an integral multiple of 3. 
492      */
493    
494
495     fprintf (outfile, "*Keywords:\t\\\n");
496
497     while (1) {
498
499         /* We get the sortKey, but we don't use it */
500         if ((sortKey = GetNextToken (infile, FALSE)) == NULL)
501             break;
502
503         if ((keyWordNew = GetNextToken (infile, FALSE)) == NULL)
504             break;
505
506         if ((locationIdNew = GetNextToken (infile, FALSE)) == NULL)
507             break;
508
509
510
511         /* Check to see if we have the same keyword as before? */
512         if (keyWordLast && (strcmp (keyWordNew, keyWordLast) == 0))
513           {
514          
515             /* We have a duplicate so add it to our previous keyword list */
516             idList = 
517               realloc(idList, strlen(idList) + strlen(locationIdNew) + 2);
518             
519             /* Add the new locationId to our current list */
520             strcat(idList, " ");
521             strcat(idList, locationIdNew);
522
523           }
524         else
525           if (keyWordLast != NULL)
526             {
527               /* Write out the lastList keyword list */              
528               fprintf (outfile, "%s<\\\\>%s\\n\\\n", keyWordLast, idList);
529
530               /* Clean up our old values */
531               free(keyWordLast);
532               free(idList);
533
534               /* We have a new keyword, so create a new keyword list */
535               idList = malloc(strlen(locationIdNew) + 1);
536               strcpy(idList, locationIdNew);
537               keyWordLast = malloc(strlen(keyWordNew) + 1);
538               strcpy(keyWordLast, keyWordNew);
539                
540             }
541           else
542             {
543               /* This is the first time in so we special case it */
544               /* Create our initial keyword list */
545               idList = malloc(strlen(locationIdNew) + 1);
546               strcpy(idList, locationIdNew);
547               keyWordLast = malloc(strlen(keyWordNew) + 1);
548               strcpy(keyWordLast, keyWordNew);
549
550             }
551
552     
553         free (sortKey);
554         free (keyWordNew);
555         free (locationIdNew);
556
557     }
558
559    /* Write out the last keyword and idList */
560    if (!keyWordLast)
561        keyWordLast = empty;
562    if (!idList)
563        idList = empty;
564    fprintf (outfile, "%s<\\\\>%s\\n\n", keyWordLast, idList);
565
566    /* Clean up our old values */
567    if (keyWordLast != empty)
568        free(keyWordLast);
569     if (idList != empty)
570        free(idList);
571
572    fprintf (outfile, "\n\n");
573
574 }
575
576
577
578
579 \f
580 /*****************************************************************************
581  * Function:        static char *GetAChar(FILE *file)
582  *
583  * Parameters:      file from which to read a (possibly multibyte) character
584  *
585  *
586  * Return Value:    pointer to (possibly multibyte) character
587  *
588  * Purpose:         Read a (possibly multibyte) character from the file.
589  *                  Put the character byte(s) into local static storage,
590  *                  terminating the character with a null byte.
591  *
592  *****************************************************************************/
593 static char *GetAChar(FILE *ifile)
594 {
595 int  c;
596 static char mbyte[32]; /* bigger than any possible multibyte char */
597 int  length;
598
599 length = 0;
600 if (((c = getc(ifile)) == EOF) || (c == '\0'))
601     {
602     *mbyte = '\0';
603     return mbyte;
604     }
605
606 while (1)
607     {
608     mbyte[length++] = c;
609     mbyte[length]   = '\0';
610     if (mblen(mbyte,length) != -1) return mbyte; /* hurray! */
611     if (length == MB_CUR_MAX)
612         { /* reached max without a hit */
613         printf("An invalid multi-byte character was found in the input");
614         mbyte[0] = ' ';
615         mbyte[1] = '\0';
616         return mbyte;
617         }
618     if ((c = getc(ifile)) == EOF)
619         { /* huh? */
620         printf("End-of-file found within a multi-byte character");
621         mbyte[0] = '\0';
622         return mbyte;
623         }
624     }
625 }
626
627
628
629
630 \f
631 /*****************************************************************************
632  * Function:        static void MergeMetaInfo(
633  *                              FILE *infile,
634  *                              FILE *outfile)
635  *
636  * Parameters:      infile
637  *                  outfile
638  *
639  * Return Value:    Void.
640  *
641  * Purpose:         Merges the <class>.hmi meta information into the 
642  *                  runtime volume file <class>.hv file.  
643  *
644  *****************************************************************************/
645 static void MergeMetaInfo(FILE *infile, FILE *outfile)
646 {
647
648 char  buff[BUFSIZ];
649 char *nextC;
650 char *writeC, *token;
651
652 fprintf (outfile, "# Meta Information\n");
653 fseek (infile, 0, SEEK_SET);
654
655 /* Clear out any initial white space that may be present */
656 while ((*(nextC = GetAChar(infile)) != '\0') &&
657        (mblen(nextC, MB_CUR_MAX) == 1) &&
658        isspace(*nextC))
659     ;
660
661 /* place that initial nextC value */
662 fputs(nextC, outfile);
663
664 while (*(nextC = GetAChar(infile)))
665     fputs(nextC, outfile);
666
667 fputs("\n\n", outfile);
668 }
669
670
671 \f
672 /*****************************************************************************
673  * Function:        static void GenHomeLocation(
674  *                              FILE *infile,
675  *                              FILE *outfile)
676  *
677  * Parameters:      infile
678  *                  outfile
679  *
680  * Return Value:    Void.
681  *
682  * Purpose:         Generates the topic Location section in the 
683  *                  <class>.hv file.  
684  *
685  *****************************************************************************/
686 static void GenHomeLocation(
687     FILE *outfile)
688 {
689     char *topic, *topicFile, *topicPos;
690
691     fprintf (outfile, "# Topic Home Locations\n");
692     fprintf (outfile, "*topTopic: _HOMETOPIC\n");
693     fprintf (outfile, "\n\n");
694 }
695
696
697
698
699
700
701
702
703 \f
704 /*****************************************************************************
705  * Function:        static char *GetNextToken(FILE *file)
706  *
707  * Parameters:      WidgetClass      
708  *
709  *
710  * Return Value:    Void.
711  *
712  * Purpose:         This function will return a token from the input streem.
713  *                  A boolean flag is passed in which instructs the function to
714  *                  use one of two token delimiters: " " or '\0'.
715  *                  (e.g. space or null char).
716  *****************************************************************************/
717 static char *GetNextToken(FILE *file, Boolean delimIsSpace)
718 {
719 char  buff[BUFSIZ];
720 char *nextC;
721 char *writeC, *token;
722 int   length;
723
724 while ((*(nextC = GetAChar(file)) != '\0') &&
725        ((length = mblen(nextC, MB_CUR_MAX)) == 1) &&
726        isspace(*nextC))
727     ;
728
729 if (*nextC == '\0')
730     return (NULL);
731
732 writeC = buff;
733
734 if (delimIsSpace)
735     {
736     do  {
737         if (writeC < (buff + BUFSIZ - length - 1))
738             {
739             strcpy(writeC, nextC);
740             writeC += length;
741             }
742         nextC = GetAChar(file);
743         length = mblen(nextC, MB_CUR_MAX);
744         }
745     while ((length > 1) || ((*nextC != '\0') && !isspace(*nextC)));
746     }
747 else
748     {
749     do  {
750         if (writeC < (buff + BUFSIZ - length - 1))
751             {
752             strcpy(writeC, nextC);
753             writeC += length;
754             }
755         nextC = GetAChar(file);
756         length = mblen(nextC, MB_CUR_MAX);
757         if ((length == 1) && (*nextC == '\036'))
758             *nextC = '\0';
759         }
760     while ((length > 1) || (*nextC != '\0'));
761     }
762
763 token = strdup(buff);
764 return token;
765 }