c5f5624b27e4ebbf6dc2f6b854f9f58a41ba52a1
[oweals/cde.git] / cde / lib / DtHelp / AccessCCDF.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 /* $XConsortium: AccessCCDF.c /main/10 1996/11/01 10:09:50 drk $ */
24 /************************************<+>*************************************
25  ****************************************************************************
26  **
27  **   File:        AccessCCDF.c
28  **
29  **   Project:     Cde 1.0 Help Library
30  **
31  **   Description: This body of code handles the access routines to the
32  **                legacy CCDF Help files.
33  **
34  **
35  **  (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 Hewlett-Packard Company
36  **
37  **  (c) Copyright 1993, 1994 Hewlett-Packard Company
38  **  (c) Copyright 1993, 1994 International Business Machines Corp.
39  **  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
40  **  (c) Copyright 1993, 1994 Novell, Inc.
41  **
42  **
43  ****************************************************************************
44  ************************************<+>*************************************/
45
46 /*
47  * system includes
48  */
49 #include <errno.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include <X11/Xlib.h>
55 #include <X11/Xresource.h>
56
57 #include <X11/Xos.h>
58 #ifdef X_NOT_STDC_ENV
59 extern int errno;
60 #endif
61
62 /*
63  * Canvas Engine includes
64  */
65 #include "CanvasP.h"
66 #include "CanvasSegP.h"
67
68 /*
69  * private includes
70  */
71 #include "CanvasError.h"
72 #include "AccessP.h"
73 #include "bufioI.h"
74 #include "AccessCCDFP.h"
75 #include "AccessCCDFI.h"
76 #include "FormatUtilI.h"
77 #include "StringFuncsI.h"
78 #include "HelpXlate.h"
79
80 #ifdef NLS16
81 #endif
82
83 /********    Private Defines      ********/
84 #define LIST_INCREMENT  10
85 #define BUFF_SIZE       256
86 /********    End Private Defines  ********/
87
88 /********    Private Function Declarations    ********/
89 static  char     *CreateFileName(
90                         char    *path,
91                         char    *string );
92 static  int       GetResourceInt (
93                         XrmDatabase  db, 
94                         char    *topic, 
95                         char    *resClass, 
96                         char    *resName,
97                         int     *ret_value);
98 static  char     *GetResourceString (
99                         XrmDatabase      db, 
100                         char            *topic, 
101                         char            *resClass, 
102                         char            *resName);
103 static  char    **GetResourceStringArray (
104                         XrmDatabase      db, 
105                         char            *topic, 
106                         char            *resClass, 
107                         char            *resName);
108 static  int       GetTopicMap (
109                         _DtHelpVolume    vol,
110                         char            *target_id,
111                         int              level,
112                         char            ***ret_ids );
113 /********    End Private Function Declarations    ********/
114
115 /********    Private Macro Declarations        ********/
116 #define GetCcdfVolumePtr(vol) \
117                 ((CcdfVolumePtr)((vol)->vols.ccdf_vol))
118
119 #define GetFilenameResource(vol, topic) \
120         GetResourceString((vol)->volDb, topic, "Filename", "filename")
121
122 /********    End Private Macro Declarations    ********/
123
124 /******************************************************************************
125 *
126 * Private variables used within this file.
127 *
128 *******************************************************************************/
129 static const char *Period    = ".";
130 static const char *Slash     = "/";
131 static const char *VolumeStr = "Volume.";
132 static const char *volumeStr = "Volume.";
133
134 static const struct _CcdfVolumeInfo DefaultCcdfVol =
135   {
136     NULL,               /* XrmDatabase volDb; */
137     NULL,               /* char **topicList;  */
138     NULL,               /* char *keywordFile; */
139   };
140
141 /******************************************************************************
142  *                             Private Functions
143  ******************************************************************************/
144 /******************************************************************************
145  * Function:    CreateFileName (char *path, char *string)
146  *
147  * Parameters:  path    Specifies the path to the volume.
148  *              string  Specifies the file's name including the extension.
149  *
150  * Memory owned by caller:
151  *              ptr returned
152  *
153  * Returns:             Non null ptr if successful, null if a failure occurs.
154  *
155  * errno Values:        CEErrorMalloc
156  *
157  * Purpose:     Creates a fully qualified path to a file based on the
158  *              path given.
159  *
160  ******************************************************************************/
161 static  char *
162 CreateFileName (
163         char     *path,
164         char     *string )
165 {
166     int    len = 0;
167     char  *ptr;
168
169     if ((MB_CUR_MAX == 1 || mblen (string, MB_CUR_MAX) == 1) && *string != '/'
170                                 && path)
171       {
172         /*
173          * determine the length of the path.
174          */
175         _DtHelpCeStrrchr (path, Slash, MB_CUR_MAX, &ptr);
176         if (ptr)
177             len = ptr - path + 1;
178       }
179
180     /*
181      * malloc the room for the path and file name.
182      */
183     ptr = (char *) malloc (len + strlen(string) + 1);
184
185     if (ptr)
186       {
187         /*
188          * copy the name into the destination string.
189          */
190         ptr[0] = '\0';
191         if (len && path)
192             strncat (ptr, path, len);
193         strcat (ptr, string);
194       }
195
196     return ptr;
197 }
198
199 /*****************************************************************************
200  * Function: GetTopicMap (_DtHelpVolume vol, char *target_id,
201  *                                              int level, char ***ret_ids)
202  *
203  * Parameters:  vol             Specifies the loaded volume
204  *              target_id       The target location ID of a topic.
205  *              level           The levels progressed so far. It is
206  *                              used to calculate how much memory
207  *                              is required to hold all the ids.
208  *              ret_ids         Returns a null terminated list of the
209  *                              location IDs of the topics between the
210  *                              target and the parent.
211  * Memory own by caller:
212  *              ret_ids
213  *
214  * Returns:             The number of ids put in the list so far,
215  *                      -1 for failure.
216  *
217  * errno Values:        CEErrorMalloc
218  *
219  *
220  * Purpose:     Recursively build a list of id strings containing all of
221  *              the target_id's ancestors.
222  *
223  *****************************************************************************/
224 static int
225 GetTopicMap (
226         _DtHelpVolume    vol,
227         char            *target_id,
228         int              level,
229         char            ***ret_ids )
230 {
231     int    result = -1;
232
233     char   *idParent;
234
235     if (_DtHelpCeGetCcdfTopicParent (vol, target_id, &idParent) == 0)
236       {
237         /*
238          * still not at the top
239          */
240         if (idParent)
241           {
242             result = GetTopicMap (vol, idParent, level + 1, ret_ids);
243             if (result != -1)
244               {
245                 (*ret_ids)[result] = strdup (idParent);
246                 result++;
247               }
248           }
249         else
250           {
251             *ret_ids = (char **) malloc (sizeof(char *) * (level + 2));
252             if ((*ret_ids) == NULL)
253                 return -1;
254
255             (*ret_ids)[level + 1] = NULL;
256             result = 0;
257           }
258       }
259
260    return result;
261
262 }  /* End GetTopicMap */
263
264 /******************************************************************************
265  * Function:    char *GetResourceString (XrmDatabase db, char *topic,
266  *                                         char *resClass, char *resName)
267  *
268  * Parameters:  db              Specifies the handle to a resource database.
269  *              topic           Specifies the topic whose resource value is
270  *                              desired.  If 'topic' is NULL, the
271  *                              desired resource is for the volume and
272  *                              not a specific topic.
273  *              resClass        Specifies the resource class name.
274  *              resName         Specifies the resource name.
275  *
276  * Return Value:        Returns the desired resource as string.
277  *                      This string is NOT owned by the caller and
278  *                      should only be read or copied.
279  *
280  *                      Returns NULL if an error occurs.
281  *
282  * errno Values:        CEErrorMalloc
283  *                      CEErrorIllegalResource  If the resource is not in
284  *                                              the database or if the
285  *                                              resource NULL
286  *
287  * Purpose:     Get a resource value for a volume or topic.
288  *
289  ******************************************************************************/
290 static char * 
291 GetResourceString (
292     XrmDatabase  db, 
293     char        *topic, 
294     char        *resClass, 
295     char        *resName)
296 {
297     int          len;
298     int          topicLen = 0;
299     char        *retVal   = NULL;
300     char        *fullResName;
301     char        *fullResClass;
302     char        *resType;
303     XrmValue     resValue;
304
305
306     if (topic != NULL)
307         topicLen = strlen(topic) + strlen(Period);
308
309     len          = strlen(volumeStr) + topicLen + 1;
310     fullResName  = (char *) malloc (len + strlen (resName));
311     fullResClass = (char *) malloc (len + strlen (resClass));
312     if (fullResName != NULL && fullResClass != NULL)
313       {
314         strcpy (fullResName, volumeStr);
315         strcpy (fullResClass, VolumeStr);
316
317         if (topic != NULL)
318           {
319             strcat (fullResName, topic);
320             strcat (fullResName, Period);
321
322             strcat (fullResClass, topic);
323             strcat (fullResClass, Period);
324           }
325
326         strcat (fullResName , resName);
327         strcat (fullResClass, resClass);
328       }
329     else
330         errno = CEErrorMalloc;
331
332
333     if (fullResClass != NULL && fullResName != NULL)
334       {
335         if (XrmGetResource (db, fullResClass, fullResName, &resType, &resValue)
336                                 && strlen ((char *) resValue.addr))
337             retVal = (char *) resValue.addr;
338         else
339             errno = CEErrorIllegalResource;
340       }
341
342     if (fullResName)
343         free (fullResName);
344     if (fullResClass)
345         free (fullResClass);
346
347     return (retVal);
348 }
349
350 /******************************************************************************
351  * Function:    char **GetResourceStringArray (XrmDatabase db, char *topic,
352  *                                          char *resClass, char *resName)
353  *
354  * Parameters:  db              Specifies the handle to a resource database.
355  *              topic           Specifies the topic whose resource value is
356  *                              desired.  If 'topic' is NULL, the
357  *                              desired resource is for the volume and
358  *                              not a specific topic.
359  *              resClass        Specifies the resource class name.
360  *              resName         Specifies the resource name.
361  *
362  * Return Value:        Returns a NULL-terminated string array containing the
363  *                      value of the desired resource.  The elements of the
364  *                      array are the strings of non-whitespace characters in
365  *                      the resource value.  This array is owned by the caller
366  *                      and should be freed (using _DtHelpCeFreeStringArray) when
367  *                      not needed.
368  *
369  * Purpose:     Get am array-valued resource for a volume or topic.
370  *
371  ******************************************************************************/
372 static char **
373 GetResourceStringArray (
374     XrmDatabase  db, 
375     char        *topic, 
376     char        *resClass, 
377     char        *resName)
378 {
379     char         *val;
380     char        **valArray = NULL;
381     char         *token;
382     char         *nextC;
383
384     /* Get the resource value which is a single string where the elements are 
385        separated by white space. */
386     val = GetResourceString (db, topic, resClass, resName);
387     if (val != NULL)
388       {
389         nextC = val;
390         while (nextC && *nextC != '\0')
391           {
392             nextC = _DtHelpGetNxtToken (nextC, &token);
393
394             if (token == NULL)
395               {
396                 _DtHelpCeFreeStringArray (valArray);
397                 return NULL;
398               }
399
400             /* If the token is a '\0' then we are at the end and we can quit. 
401                If the token is a '\n', then ignore it.  Otherwise the token
402                is an element of the array we are building. */
403             if (*token == '\0')
404                 break;
405
406             if (*token != '\n')
407               {
408                 valArray = (char **) _DtHelpCeAddPtrToArray (
409                                         (void **) valArray, (void *) token);
410                 /*
411                  * If we malloc'ed ourselves out of existence...stop processing.
412                  */
413                 if (!valArray)
414                     break;
415               }
416           }
417       }
418
419     return (valArray);
420 }
421
422 /******************************************************************************
423  * Function:    char *GetNextKeyword (char *str, char *delimiter,
424                                                         char **ret_token)
425  *
426  * Parameters:  str             Specifies the string which is being parsed.
427  *              delimiter       Specifies the delimiter string.
428  *              ret_token       Returns the string found between the current
429  *                              position of the input string and the delimiter
430  *                              string.
431  *
432  *                              Newline or Null strings are
433  *                              not owned by the caller.
434  *
435  *                              Otherwise, the memory for the returned
436  *                              token is owned by the caller.
437  *
438  * Return Value:        Returns a pointer to the next unparsed character
439  *                      in the input string. A NULL value indicates an error.
440  *
441  * Purpose:     Load the keywords associated with a volume.
442  *
443  ******************************************************************************/
444 static char * 
445 GetNextKeyword (
446         char     *str,
447         char     *delimiter,
448         char    **ret_token )
449 {
450     int          len;
451     char        *start;
452     char        *token;
453     short        done;
454
455     /* Find the next token in the string.  The parsing rules are:
456
457          - The deliminater (except for \n) separate a keyword from
458            its list of location IDs.
459          - \n is a token itself.
460          - The \0 at the end of the string is a token.
461      */
462
463     /* Skip all of the whitespace and \n. */
464     (void) _DtHelpCeStrspn (str, " \n", MB_CUR_MAX, &len);
465     str += len;
466
467     /* Str is pointing at the start of the next keyword.  Depending on the
468        type of token, malloc the memory and copy the token value. */
469     if (*str == '\0')
470         token = str;
471
472     else
473       {
474         /* We have some non-whitespace characters.  Find the end of */
475         /* them and copy them into new memory. */
476         start = str;
477         done  = False;
478         do
479           {
480             _DtHelpCeStrchr (str, delimiter, MB_CUR_MAX, &str);
481             if (str)
482               {
483                 if (strncmp (str, delimiter, strlen(delimiter)) == 0)
484                     done = True;
485                 else
486                     str++;
487               }
488             else /* if (str == NULL) */
489               {
490                 str = start + strlen (start);
491                 done = -1;
492               }
493           } while (!done);
494
495         token = (char *) malloc ((str - start + 1) * sizeof (char));
496         if (token)
497           {
498             strncpy (token, start, str - start);
499             *(token + (str - start)) = '\0';
500             if (done == True)
501                 str += strlen (delimiter);
502           }
503       }
504
505     *ret_token = token;
506     return (str);
507 }
508
509 /******************************************************************************
510  * Function:    int TopicFilename (_DtHelpVolume vol, char *topic,
511  *                                                      char **retFname);
512  *
513  * Parameters:  vol             Specifies the loaded volume
514  *              topic           Specifies locationID for the topic
515  *              retFname        Returns the name of the file where the topic
516  *                              is located.
517  * Memory own by caller:
518  *              retFname
519  *
520  * Returns:     0 if successful, -1 if a failure occurs
521  *
522  * Purpose:     Get the name of the file where a topic is stored.
523  *
524  ******************************************************************************/
525 static int 
526 TopicFilename (
527     _DtHelpVolume    vol, 
528     char         *topic, 
529     char        **retFname)
530 {
531     CcdfVolumePtr  ccdfVol = GetCcdfVolumePtr(vol);
532
533     *retFname = GetFilenameResource (ccdfVol, topic);
534     if (*retFname == NULL && errno == CEErrorIllegalResource)
535         errno = CEErrorMissingFilenameRes;
536     else
537         *retFname = CreateFileName (vol->volFile, *retFname);
538
539     if (*retFname == NULL)
540         return (-1);
541
542     return (0);
543 }
544
545 /******************************************************************************
546  * Function:    int TopicFilepos (_DtHelpVolume vol, char *topic, int *retFpos);
547  *
548  * Parameters:  vol             Specifies the loaded volume
549  *              topic           The locationID for the topic
550  *              retFpos         Returns the byte offset of the start of the
551  *                              topic within the topic file.
552  *
553  * Return Value:        0 if successful, -1 if a failure occurs.
554  *
555  * Purpose:     Determine the position of the topic within the topic file.
556  ******************************************************************************/
557 static int 
558 TopicFilepos (
559     _DtHelpVolume   vol, 
560     char        *topic, 
561     int         *retFpos)
562 {
563     CcdfVolumePtr  ccdfVol = GetCcdfVolumePtr(vol);
564
565     if (GetResourceInt(ccdfVol->volDb, topic, "Filepos", "filepos", retFpos) == -1)
566         return -1;
567
568     return (0);
569 }
570
571 /******************************************************************************
572  * Function:    int GetResourceInt (XrmDatabase db, char *topic,
573  *                              char *resClass, char *resName, ret_value)
574  *
575  * Parameters:  db              Specifies the handle to a resource database.
576  *              topic           Specifies the topic whose resource value is
577  *                              desired.  If 'topic' is NULL, the
578  *                              desired resource is for the volume and
579  *                              not a specific topic.
580  *              resClass        Specifies the resource class name.
581  *              resName         Specifies the resource name.
582  *              ret_value       Returns an int containing the resource value.
583  *
584  * Return Value: 0 if successful, -1 if a failure occurs.
585  *
586  * Purpose:     Get an integer-valued resource for a volume or topic.
587  ******************************************************************************/
588 static int 
589 GetResourceInt (
590     XrmDatabase  db, 
591     char        *topic, 
592     char        *resClass, 
593     char        *resName,
594     int         *ret_value)
595 {
596     char  *retValue;
597
598     retValue = GetResourceString (db, topic, resClass, resName);
599     if (retValue)
600       {
601         *ret_value = atoi(retValue);
602         return 0;
603       }
604
605     return -1;
606 }
607
608 /******************************************************************************
609  * Function:    static int LocationIDTopic (_DtHelpVolume vol, char *locId,
610  *                                       char **retTopic);
611  *
612  * Parameters:  vol             Specifies the loaded volume
613  *              locId           Specifies locationID desired.
614  *              retTopic        Returns the locationID of the topic with
615  *                              contains 'locId'.  This string IS owned by
616  *                              the caller and must be freed when no longer
617  *                              needed.
618  *
619  * Return Value:        0 if successful, -1 if a failure occurs
620  *
621  * Purpose:             Find which topic contains a specified locationID.
622  ******************************************************************************/
623 static int 
624 LocationIDTopic (
625      _DtHelpVolume   vol, 
626      char        *locId, 
627      char       **retTopic)
628 {
629     char        **allTopics;
630     char        **nextTopic;
631     char        **locIdList = NULL;
632     char        **nextLocId;
633
634     CcdfVolumePtr  ccdfVol = GetCcdfVolumePtr(vol);
635
636     *retTopic = NULL;
637
638     if (_DtHelpCeGetCcdfVolIdList (vol, &allTopics) != 0)
639         return (-1);
640
641     /* Check whether the locationID is a topic. */
642     for (nextTopic = allTopics;
643         nextTopic && *nextTopic != NULL && *retTopic == NULL; nextTopic++)
644       {
645         if (_DtHelpCeStrCaseCmpLatin1 (locId, *nextTopic) == 0)
646             *retTopic = strdup(*nextTopic);
647       }
648
649     /* For each topic in the volume, get its list of locationIDs and
650        check them. NOTE: This code should be separated out into a public 
651        _DtTopicLocationIDs function.  Then we would have a function 
652        that returns all of the locationIDs in a topic, which might
653        prove useful someday. */
654
655     if (*retTopic == NULL)
656       {
657         for (nextTopic = allTopics;
658                 nextTopic && *nextTopic != NULL && *retTopic == NULL;
659                                                                 nextTopic++)
660           {
661             /*
662              * valid to get a NULL on this resource, but not good
663              */
664             errno = 0;
665             locIdList = GetResourceStringArray  (ccdfVol->volDb, *nextTopic,
666                                                 "LocationIDs", "locationIDs");
667             if (locIdList == NULL && errno != CEErrorIllegalResource)
668                 break;
669
670             errno = CEErrorLocIdNotFound;
671             for (nextLocId = locIdList;
672                 nextLocId != NULL && *nextLocId != NULL && *retTopic == NULL;
673                                                                 nextLocId++)
674               {
675                 if (_DtHelpCeStrCaseCmpLatin1 (locId, *nextLocId) == 0)
676                     *retTopic = strdup(*nextTopic);
677               }
678
679             if (NULL != locIdList)
680                 _DtHelpCeFreeStringArray (locIdList);
681           }
682       }
683
684     if (*retTopic == NULL)
685         return (-1);
686
687     return (0);
688 }
689
690 /******************************************************************************
691  *                       Semi-Public CCDF Access Functions
692  ******************************************************************************/
693 /*****************************************************************************
694  * Function: int _DtHelpCeGetCcdfIdPath (_DtHelpVolume vol, char *target_id,
695  *                                              char ***ret_ids)
696  *
697  * Parameters:  vol             Specifies the loaded volume
698  *              target_id       The target location ID of a topic.
699  *              ret_ids         Returns a null terminated list of the
700  *                              location IDs of the topics between the
701  *                              target and the parent.
702  * Memory own by caller:
703  *              ret_ids
704  *
705  * Returns:     The number of ids in the list, -1 for failure. If successful,
706  *              the list will always contain at least the target_id.
707  *
708  * Purpose:     Get the list of ids between the top and the target id.
709  *
710  *****************************************************************************/
711 int
712 _DtHelpCeGetCcdfIdPath (
713     _DtHelpVolume    vol,
714     char              *target_id,
715     char            ***ret_ids )
716 {
717     int       idCount = 0;
718     char     *topicId = NULL;
719
720     if (LocationIDTopic (vol, target_id, &topicId) != 0)
721         return -1;
722
723     idCount = GetTopicMap (vol, topicId, 0, ret_ids);
724     if (idCount != -1)
725       {
726         /*
727          * include this entry in the count
728          */
729         (*ret_ids)[idCount] = topicId;
730         idCount++;
731       }
732
733     return idCount;
734
735 }  /* End _DtHelpCeGetCcdfIdPath */
736
737 /******************************************************************************
738  * Function:    int  _DtHelpCeGetCcdfTopicChildren (_DtHelpVolume vol,
739  *                                                      char *topic_id,
740  *                                                      char ***topics);
741  *
742  * Parameters:  vol             Specifies the loaded volume.
743  *              topic_id        Specifies the topic for which children
744  *                              are desired.
745  *              retTopics       Returns a NULL-terminated string array
746  *                              containing the list of children for a topic
747  *                              in the volume. This array is owned by the
748  *                              caller and should be freed (using
749  *                              _DtHelpCeFreeStringArray) when not needed.
750  *
751  * Memory own by caller:
752  *      retTopics
753  *
754  * Return Value:                > 0 if successful, -1 if a failure occurs
755  *
756  * Purpose:     Get the list of children for a topic contained in a volume.
757  *
758  ******************************************************************************/
759 int 
760 _DtHelpCeGetCcdfTopicChildren (
761     _DtHelpVolume   vol,
762     char          *topic_id,
763     char        ***retTopics)
764 {
765     int     result;
766     int     count = 0;
767     char   *parent_id;
768     char   *child_id;
769     char   *topicId = NULL;
770     char  **topicList;
771
772     if (LocationIDTopic (vol, topic_id, &topicId) != 0)
773         return -1;
774
775     /*
776      * initialize the return value
777      */
778     *retTopics = NULL;
779
780     /*
781      * get the list
782      */
783     result = _DtHelpCeGetCcdfVolIdList (vol, &topicList);
784     if (result == 0)
785       {
786         while (*topicList && result == 0)
787           {
788             result = _DtHelpCeGetCcdfTopicParent (vol, *topicList, &parent_id);
789             if (result == 0)
790               {
791                 /*
792                  * It's legal to get a NULL back - means the topic
793                  * doesn't have a parent.
794                  */
795                 if (parent_id &&
796                         _DtHelpCeStrCaseCmpLatin1 (parent_id, topicId) == 0)
797                   {
798                     child_id = strdup (*topicList);
799                     if (child_id)
800                       {
801                         *retTopics = (char **) _DtHelpCeAddPtrToArray (
802                                         (void **) (*retTopics), child_id);
803                         if (*retTopics == NULL)
804                             result = -1;
805                         count++;
806                       }
807                     else
808                       {
809                         /*
810                          * lost the previous data...stop processing.
811                          */
812                         if (*retTopics)
813                             _DtHelpCeFreeStringArray (*retTopics);
814                         *retTopics = NULL;
815                         result = -1;
816                         break;
817                       }
818                   }
819               }
820             else
821               {
822                 /*
823                  * problems processing TopicParent...stop processing
824                  */
825                 if (*retTopics)
826                     _DtHelpCeFreeStringArray (*retTopics);
827                 *retTopics = NULL;
828                 break;
829               }
830             topicList++;
831           }
832       }
833
834     /*
835      * free the duplicate string
836      */
837     if (topicId)
838         free (topicId);
839
840     if (result != 0)
841         return -1;
842
843     return count;
844 }
845
846 /******************************************************************************
847  * Function:    int _DtHelpCeGetCcdfVolIdList (_DtHelpVolume vol, char ***topics);
848  *
849  * Parameters:  vol     Specifies the loaded volume.
850  *              topics  Returns a NULL-terminated string array
851  *                      containing the ordered list of topics in the
852  *                      volume.  This array is NOT owned by the caller
853  *                      and should only be read or copied.
854  *
855  * Return Value:        0 if successful, -1 if a failure occurs
856  *
857  * Purpose:     Get the list of topics contained in a volume.
858  *
859  ******************************************************************************/
860 int 
861 _DtHelpCeGetCcdfVolIdList (
862     _DtHelpVolume       vol,
863      char       ***retTopics)
864 {
865     CcdfVolumePtr  ccdfVol = GetCcdfVolumePtr(vol);
866
867     if (ccdfVol->topicList == NULL)
868         ccdfVol->topicList = GetResourceStringArray (ccdfVol->volDb, NULL, 
869                                                 "TopicList", "topicList");
870     *retTopics = ccdfVol->topicList;
871     if (*retTopics == NULL)
872       {
873         if (errno == CEErrorIllegalResource)
874             errno = CEErrorMissingTopicList;
875         return -1;
876       }
877
878     return 0;
879 }
880
881 /*****************************************************************************
882  * Function: int _DtHelpCeFindCcdfId (_DtHelpVolume vol, char *target_id,
883  *                                      char *ret_name, int *ret_offset)
884  *
885  * Parameters:  vol             Specifies the loaded volume
886  *              target_id       Specifies target location ID
887  *              ret_name        Returns a null terminated string
888  *                              containing a fully qualified path to
889  *                              the file that contains 'target_id'.
890  *              ret_offset      Returns the offset into 'ret_name'
891  *                              to the topic that contains 'target_id'.
892  *
893  * Memory own by caller:
894  *              ret_name
895  *
896  * Returns:     True if successful, False if a failure occurs
897  *
898  * Purpose:     Find which topic contains a specified locationID.
899  *****************************************************************************/
900 int
901 _DtHelpCeFindCcdfId (
902     _DtHelpVolume       vol,
903     char                *target_id,
904     char                **ret_name,
905     int         *ret_offset )
906 {
907     char        newTarget[65];
908     char       *topicId = NULL;
909     int   found = False;
910
911     snprintf(newTarget, sizeof(newTarget), "%s", target_id);
912     _DtHelpCeUpperCase(newTarget);
913
914     /*
915      * find the location id for the topic that contains the
916      * target_id (they may be the same). Then find the filename
917      * and offset.
918      */
919     if (TopicFilename (vol, newTarget, ret_name) == -1)
920       {
921         /*
922          * if the reason TopicFilename failed was because we couldn't
923          * find a resource, try looking for it in the LocationIDs.
924          */
925         if (errno == CEErrorMissingFilenameRes &&
926                 LocationIDTopic (vol, newTarget, &topicId) == 0 &&
927                         TopicFilename (vol, topicId, ret_name) == 0 &&
928                                 TopicFilepos (vol, topicId, ret_offset) == 0)
929             found = True;
930       }
931     else if (TopicFilepos (vol, newTarget, ret_offset) != -1)
932         found = True;
933
934     /*
935      * free the excess strings
936      */
937     if (topicId)
938         free (topicId);
939
940     return found;
941
942 }  /* End _DtHelpCeFindCcdfId */
943
944 /******************************************************************************
945  * Function:   int _DtHelpCeGetCcdfTopicParent (_DtHelpVolume vol, char *topic,
946  *                                      char **retParent)
947  *
948  * Parameters:  vol             Specifies the loaded volume
949  *              topic           Specifies locationID for the topic
950  *              retParent       Returns a string with the locationID for the
951  *                              topic which is the parent of the current
952  *                              topic.  If the current topic is at the top of
953  *                              the hierarchy, a NULL string is returned.
954  *                              This string is NOT owned by the caller and
955  *                              should only be read or copied.
956  *
957  * Return Value:        0 if successful, -1 if a failure occurs
958  *
959  * Purpose:     Find the parent for a topic.
960  ******************************************************************************/
961 int 
962 _DtHelpCeGetCcdfTopicParent (
963      _DtHelpVolume   vol, 
964      char        *topic, 
965      char       **retParent)
966 {
967     CcdfVolumePtr  ccdfVol = GetCcdfVolumePtr(vol);
968
969     /* Don't return an error if we are asked for the parent of NULL, or if 
970        the topic has no parent.  Both cases are valid (and used by
971        _DtTopicPath). */
972
973     *retParent = NULL;
974     if (topic != NULL)
975       {
976         errno = 0;
977         *retParent = GetResourceString(ccdfVol->volDb, topic,
978                                                         "Parent", "parent");
979         if (*retParent == NULL && errno != CEErrorIllegalResource)
980             return -1;
981       }
982
983     return (0);
984 }
985
986 /*****************************************************************************
987  * Function: int _DtHelpCeGetCcdfKeywordList (_DtHelpVolume vol,
988  *
989  * Parameters:  vol     Specifies the volume whose keywords need to be
990  *                      loaded from disk.  Once loaded, they can be 
991  *                      accessed through the fields of the volume structure.
992  *
993  * Return Value:        0 if successful, -1 if a failure occurs
994  *
995  * Purpose:     Load the keywords associated with a volume.
996  *****************************************************************************/
997 int
998 _DtHelpCeGetCcdfKeywordList (
999     _DtHelpVolume       vol)
1000 {
1001     XrmDatabase   kDb;
1002     char         *keywordString;
1003     char         *nextC;
1004     char        **topics;
1005     char        ***topicList;
1006     char         *token;
1007     char         *currKeyword;
1008     CcdfVolumePtr  ccdfVol = GetCcdfVolumePtr(vol);
1009
1010     /* Generate the name of the keyword file.  Because volume files
1011        use the ".hv" suffix and keyword files use ".hvk", all we have 
1012        to do is append a "k". */
1013     /*
1014      * If the keywordFile is non-null, we've already tried once.
1015      * We want to try again, because the problem may have been
1016      * fixed without restarting this process.
1017      *
1018      * But don't malloc memory again, because we'll leak memory.
1019      * Just use what is given.
1020      */
1021     if (ccdfVol->keywordFile == NULL)
1022       {
1023         ccdfVol->keywordFile = (char *) malloc (strlen (vol->volFile) + 2);
1024         if (ccdfVol->keywordFile == NULL)
1025             return -1;
1026
1027         strcpy (ccdfVol->keywordFile, vol->volFile);
1028         strcat (ccdfVol->keywordFile, "k");
1029       }
1030
1031     /*
1032      * check to see if it exists
1033      */
1034     if (access (ccdfVol->keywordFile, R_OK) == -1)
1035         return -1;
1036
1037     /* Load the keyword file and get the "keywords" resource. */
1038     kDb = XrmGetFileDatabase (ccdfVol->keywordFile);
1039     if (kDb == NULL)
1040         return -1;
1041
1042     keywordString = GetResourceString (kDb, NULL, "Keywords", "keywords");
1043     if (keywordString == NULL)
1044       {
1045         if (errno == CEErrorIllegalResource)
1046             errno = CEErrorMissingKeywordsRes;
1047         XrmDestroyDatabase (kDb);
1048         return (-1);
1049       }
1050
1051     /* Now parse the string into the appropriate arrays.  The string has the 
1052        following syntax:
1053
1054                 keyword1<\>topic topic topic ... \n
1055                 keyword2<\>topic topic topic ... \n
1056        
1057      */
1058     nextC = (char *) keywordString;
1059
1060     while (nextC && *nextC)
1061       {
1062
1063         /* Get the next keyword.  If we find newlines while looking for
1064            the keyword, throw them away.  If the next token is the end-
1065            of-file (\0), quit.
1066          */
1067         nextC = GetNextKeyword (nextC, "<\\>", &token);
1068
1069         if (token == NULL)
1070           {
1071             XrmDestroyDatabase (kDb);
1072             if (vol->keywords)
1073               {
1074                 _DtHelpCeFreeStringArray (vol->keywords);
1075                 vol->keywords = NULL;
1076               }
1077             if (vol->keywordTopics)
1078               {
1079                 for (topicList = vol->keywordTopics;
1080                                                 topicList; topicList++)
1081                     _DtHelpCeFreeStringArray (*topicList);
1082                 free (vol->keywordTopics);
1083                 vol->keywordTopics = NULL;
1084               }
1085             return -1;
1086           }
1087
1088         if (*token == '\0')
1089             break;
1090
1091         /* We have the next keyword.  Hang onto it and add it to the list
1092            once we get the array of topics.  We don't add it yet because if
1093            there are no topics we want to throw it away.  (Silently ignoring
1094            keywords which specify no topics is an undocumented feature.) */
1095
1096         currKeyword = token;
1097
1098         /* Now get the list of topics. */
1099         topics = NULL;
1100         do
1101           {
1102             nextC = _DtHelpGetNxtToken (nextC, &token);
1103
1104             if (token == NULL)
1105               {
1106                 XrmDestroyDatabase (kDb);
1107                 if (vol->keywords)
1108                   {
1109                     _DtHelpCeFreeStringArray (vol->keywords);
1110                     vol->keywords = NULL;
1111                   }
1112                 if (vol->keywordTopics)
1113                   {
1114                     for (topicList = vol->keywordTopics;
1115                                                 topicList; topicList++)
1116                         _DtHelpCeFreeStringArray (*topicList);
1117                     free (vol->keywordTopics);
1118                     vol->keywordTopics = NULL;
1119                   }
1120                 if (topics)
1121                         _DtHelpCeFreeStringArray (topics);
1122                 free (currKeyword);
1123
1124                 return -1;
1125               }
1126
1127             /* If the next token is end-of-file (\0), then quit.  Otherwise
1128                if the next token is a newline, then we have gotten all of
1129                the topics and we need to add them to the array of topic 
1130                arrays.  The final choice is that the token is a string so
1131                we add it to the current array of topics. */
1132             if (*token == '\0')
1133                 break;
1134
1135             if (*token == '\n')
1136               {
1137                 /* We have all of the topics.  If the array of topics isn't
1138                    empty, add everything to the data structures.
1139                  */
1140                 if (topics != NULL)
1141                   {
1142                     vol->keywords = (char **) _DtHelpCeAddPtrToArray (
1143                                       (void **) vol->keywords,
1144                                       (void *) currKeyword);
1145                     vol->keywordTopics = (char ***) _DtHelpCeAddPtrToArray (
1146                                         (void **) vol->keywordTopics,
1147                                         (void *) topics);
1148                     /*
1149                      * If we just malloc'ed ourselves out of existence...
1150                      * stop here.
1151                      */
1152                     if (vol->keywords == 0 || vol->keywordTopics == 0)
1153                       {
1154                         XrmDestroyDatabase (kDb);
1155                         if (vol->keywords)
1156                           {
1157                             free (currKeyword);
1158                             _DtHelpCeFreeStringArray (vol->keywords);
1159                             _DtHelpCeFreeStringArray (topics);
1160                             vol->keywords = NULL;
1161                           }
1162                         if (vol->keywordTopics)
1163                           {
1164                             for (topicList = vol->keywordTopics;
1165                                                         topicList; topicList++)
1166                                 _DtHelpCeFreeStringArray (*topicList);
1167                             free (vol->keywordTopics);
1168                             vol->keywordTopics = NULL;
1169                           }
1170                         return -1;
1171                       }
1172                   }
1173                 break;
1174               }
1175             else
1176               {
1177                 topics = (char **) _DtHelpCeAddPtrToArray ((void **) topics, 
1178                                                 (void *) token);
1179                 /*
1180                  * If we just malloc'ed ourselves out of existence
1181                  * stop here.
1182                  */
1183                 if (topics == NULL)
1184                   {
1185                     free (currKeyword);
1186                     XrmDestroyDatabase (kDb);
1187                     if (vol->keywords)
1188                       {
1189                         _DtHelpCeFreeStringArray (vol->keywords);
1190                         vol->keywords = NULL;
1191                       }
1192                     if (vol->keywordTopics != NULL)
1193                       {
1194                         for (topicList = vol->keywordTopics;
1195                                                         topicList; topicList++)
1196                             _DtHelpCeFreeStringArray (*topicList);
1197                         free (vol->keywordTopics);
1198                         vol->keywordTopics = NULL;
1199                       }
1200
1201                     return -1;
1202                   }
1203               }
1204
1205           } while (nextC && *nextC);
1206
1207         if (topics == NULL)
1208             free (currKeyword);
1209       }
1210
1211     XrmDestroyDatabase (kDb);
1212
1213     return (0);
1214
1215 }  /* End _DtHelpCeGetCcdfKeywordList */
1216
1217 /******************************************************************************
1218  * Function:    int _DtHelpCeGetCcdfVolumeAbstract (_DtHelpVolume vol,
1219  *                              char **abstract);
1220  *
1221  * Parameters:  vol     Specifies the loaded volume.
1222  *              abstract Returns the abstract of the volume.  This string
1223  *                       is owned by the caller and should be freed.
1224  *
1225  * Return Value: 0 if successful, -1 if a failure occurs
1226  *
1227  * Purpose:     Get the abstract of a volume.
1228  ******************************************************************************/
1229 int 
1230 _DtHelpCeGetCcdfVolumeAbstract (
1231     _DtHelpVolume         vol,
1232     char                **retAbs)
1233 {
1234     char          *abstract;
1235     CcdfVolumePtr  ccdfVol = GetCcdfVolumePtr(vol);
1236
1237     *retAbs = NULL;
1238     abstract = GetResourceString(ccdfVol->volDb, NULL, "Abstract", "abstract");
1239     if (abstract == NULL)
1240       {
1241         if (errno == CEErrorIllegalResource)
1242             errno = CEErrorMissingAbstractRes;
1243       }
1244     else
1245         *retAbs = strdup(abstract);
1246
1247     if (*retAbs == NULL)
1248         return (-1);
1249
1250     return (0);
1251 }
1252
1253 /*****************************************************************************
1254  * Function: int _DtHelpCeMapCcdfTargetToId (_DtHelpVolume vol,
1255  *                                      char *target_id,
1256  *                                      char *ret_id)
1257  *
1258  * Parameters:  vol             Specifies the loaded volume
1259  *              target_id       Specifies target location ID
1260  *              ret_id          Returns the id containing the target_id.
1261  *                              This memory *IS NOT* owned by the caller.
1262  *                              And *MAY* point to target_id.
1263  *
1264  * Returns:     0 if successful, -1 if a failure occurs
1265  *
1266  * Purpose:     Find which topic contains a specified locationID.
1267  *
1268  *****************************************************************************/
1269 int
1270 _DtHelpCeMapCcdfTargetToId (
1271     _DtHelpVolume       vol,
1272     const char          *target_id,
1273     char                **ret_id)
1274 {
1275     char        newTarget[128];
1276     CcdfVolumePtr  ccdfVol = GetCcdfVolumePtr(vol);
1277
1278     snprintf(newTarget, sizeof(newTarget), "%s", target_id);
1279     _DtHelpCeUpperCase(newTarget);
1280
1281     /*
1282      * find the location id for the topic that contains the
1283      * target_id (they may be the same). Then find the filename
1284      * and offset.
1285      */
1286     *ret_id = (char *) target_id;
1287     if (GetFilenameResource (ccdfVol, newTarget) == NULL)
1288       {
1289         /*
1290          * if the reason TopicFilename failed was because we couldn't
1291          * find a resource, try looking for it in the LocationIDs.
1292          */
1293         if (errno == CEErrorIllegalResource &&
1294                 LocationIDTopic (vol, newTarget, ret_id) == 0 &&
1295                                 GetFilenameResource(ccdfVol, *ret_id) != NULL)
1296             return 0;
1297
1298         return -1;
1299       }
1300
1301     return 0;
1302
1303 }  /* End _DtHelpCeMapCcdfTargetToId */
1304
1305 /*****************************************************************************
1306  * Function: char * _DtHelpCeGetCcdfVolLocale (_DtHelpVolume vol)
1307  *
1308  * Parameters:  vol             Specifies the loaded volume
1309  *
1310  * Returns:     The pointer to the locale string if successful. Otherwise
1311  *              NULL.
1312  *
1313  * Purpose:     Get the locale of the specified volume.
1314  *              Returns the locale in a unix specific format
1315  *              - locale[_ter][.charset] - This memory is owned by
1316  *              the caller.
1317  *
1318  *****************************************************************************/
1319 char *
1320 _DtHelpCeGetCcdfVolLocale (
1321         _DtHelpVolume   vol)
1322 {
1323     char          *locale = NULL;
1324     char          *charSet;
1325     CcdfVolumePtr  ccdfVol = GetCcdfVolumePtr(vol);
1326
1327     errno  = 0;
1328     locale = GetResourceString(ccdfVol->volDb, NULL, "CharSet", "charSet");
1329     if (_DtHelpCeStrchr(locale, ".", 1, &charSet) != 0)
1330       {
1331         charSet = locale;
1332         _DtHelpCeXlateOpToStdLocale(DtLCX_OPER_CCDF,charSet,&locale,NULL,NULL); 
1333         /* charset is owned by the volume Xrm database; don't free */
1334       }
1335     else if (NULL != locale)
1336         locale = strdup(locale);
1337
1338     return locale;
1339
1340 }  /* End _DtHelpCeGetCcdfVolLocale */
1341
1342 /*****************************************************************************
1343  * Function: int _DtHelpCeGetCcdfDocStamp (_DtHelpVolume vol, char **ret_doc,
1344  *                                              char **ret_time)
1345  *
1346  * Parameters:  volume          Specifies the loaded volume
1347  *              ret_doc         Returns the doc id.
1348  *              ret_time        Returns the time stamp.
1349  *
1350  * Memory:      Caller owns the string in ret_doc and ret_time.
1351  *
1352  * Returns:     0 if successful, -2 if the volume does not contain
1353  *              one or the other, -1 if any other failure.
1354  *
1355  * Purpose:     Get doc id and time stamp of a volume.
1356  *
1357  *****************************************************************************/
1358 int
1359 _DtHelpCeGetCcdfDocStamp (
1360     _DtHelpVolume       vol,
1361     char                **ret_doc,
1362     char                **ret_time)
1363 {
1364     int  result;
1365     struct stat buf;
1366
1367     result = -2;
1368     if (ret_doc != NULL)
1369         *ret_doc = NULL;
1370
1371     if (ret_time != NULL)
1372       {
1373         result = -1;
1374         *ret_time = NULL;
1375         if (stat(vol->volFile, &buf) == 0)
1376           {
1377             *ret_time = (char *) malloc (sizeof(time_t) * 3 + 1);
1378             if (*ret_time != NULL)
1379               {
1380                 sprintf(*ret_time, "%u", (unsigned) buf.st_mtime);
1381                 return -2;
1382               }
1383           }
1384       }
1385     return result;
1386
1387 }  /* End _DtHelpCeGetCcdfDocStamp */
1388
1389 /******************************************************************************
1390  * Function:    static int _DtHelpCeGetCcdfTopTopic (_DtHelpVolume vol,
1391  *                                      char **topic);
1392  *
1393  * Parameters:  vol     Specifies the loaded volume.
1394  *              topic   Returns the locationID for the top topic in
1395  *                      the volume hierarchy.  This string is NOT
1396  *                      owned by the caller and should only be read or
1397  *                      copied.
1398  *
1399  * Return Value:        0 if successful, -1 if a failure occurs
1400  *
1401  * Purpose:     Get the top topic of a volume.
1402  ******************************************************************************/
1403 int 
1404 _DtHelpCeGetCcdfTopTopic (
1405     _DtHelpVolume   vol,
1406     char        **retTopic)
1407 {
1408     CcdfVolumePtr  ccdfVol = GetCcdfVolumePtr(vol);
1409
1410     *retTopic = GetResourceString(ccdfVol->volDb, NULL, "TopTopic", "topTopic");
1411     if (*retTopic == NULL)
1412       {
1413         if (errno == CEErrorIllegalResource)
1414             errno = CEErrorMissingTopTopicRes;
1415         return (-1);
1416       }
1417
1418     return (0);
1419 }
1420
1421 /******************************************************************************
1422  * Function:    char  *_DtHelpCeGetCcdfVolTitle (_DtHelpVolume vol);
1423  *
1424  * Parameters:  vol             Specifies the loaded volume.
1425  *
1426  * Return Value:        The title if successful, NULL otherwise.
1427  *                      The caller *DOES NOT* own the memory returned
1428  *                      and *MUST NOT* modify the memory.
1429  *
1430  * Purpose:     Get the title of a volume.
1431  *
1432  ******************************************************************************/
1433 char * 
1434 _DtHelpCeGetCcdfVolTitle (
1435     _DtHelpVolume         vol)
1436 {
1437     CcdfVolumePtr  ccdfVol = GetCcdfVolumePtr(vol);
1438
1439     return (GetResourceString (ccdfVol->volDb, NULL, "Title", "title"));
1440 }
1441
1442 /******************************************************************************
1443  * Function:    int  _DtHelpCeGetCcdfVolumeTitle (_DtHelpVolume vol);
1444  *
1445  * Parameters:  vol             Specifies the loaded volume.
1446  *
1447  * Return Value:        The title if successful, NULL otherwise.
1448  *                      The caller *DOES NOT* own the memory returned
1449  *                      and *MUST NOT* modify the memory.
1450  *
1451  * Purpose:     Get the title of a volume.
1452  *
1453  ******************************************************************************/
1454 int 
1455 _DtHelpCeGetCcdfVolumeTitle (
1456     _DtHelpVolume         vol,
1457     char                **ret_title)
1458 {
1459     *ret_title = _DtHelpCeGetCcdfVolTitle(vol);
1460
1461     if (*ret_title == NULL)
1462       {
1463         if (errno == CEErrorIllegalResource)
1464             errno = CEErrorMissingTitleRes;
1465         return (-1);
1466       }
1467
1468     *ret_title = strdup(*ret_title);
1469     if (*ret_title == NULL)
1470       {
1471         errno = CEErrorMalloc;
1472         return (-1);
1473       }
1474     return (0);
1475 }
1476
1477 /******************************************************************************
1478  * Function:    int  _DtHelpCeOpenCcdfVolume (_DtHelpVolume vol);
1479  *
1480  * Parameters:  vol             Specifies the loaded volume.
1481  *
1482  * Return Value:
1483  *
1484  * Purpose:     Open a CCDF help volume
1485  *
1486  ******************************************************************************/
1487 int 
1488 _DtHelpCeOpenCcdfVolume (
1489     _DtHelpVolume         vol)
1490 {
1491     struct stat buf;
1492     CcdfVolumePtr ccdfVol;
1493
1494     ccdfVol = (struct _CcdfVolumeInfo *) malloc(sizeof(struct _CcdfVolumeInfo));
1495     if (ccdfVol != NULL)
1496       {
1497         *ccdfVol = DefaultCcdfVol;
1498         ccdfVol->volDb = XrmGetFileDatabase(vol->volFile);
1499         if (ccdfVol->volDb != NULL)
1500           {
1501             (void) stat(vol->volFile, &buf);
1502             vol->check_time = buf.st_mtime;
1503             vol->vols.ccdf_vol = (CcdfVolumeHandle) ccdfVol;
1504             return 0;
1505           }
1506         free(ccdfVol);
1507       }
1508
1509     return -1;
1510 }
1511
1512 /******************************************************************************
1513  * Function:    void  _DtHelpCeCloseCcdfVolume (_DtHelpVolume vol);
1514  *
1515  * Parameters:  vol             Specifies the loaded volume.
1516  *
1517  * Return Value:
1518  *
1519  * Purpose:     Open a CCDF help volume
1520  *
1521  ******************************************************************************/
1522 void 
1523 _DtHelpCeCloseCcdfVolume (
1524     _DtHelpVolume         vol)
1525 {
1526     CcdfVolumePtr  ccdfVol = GetCcdfVolumePtr(vol);
1527
1528     if (ccdfVol->volDb != NULL)
1529         XrmDestroyDatabase (ccdfVol->volDb);
1530
1531     if (ccdfVol->topicList != NULL)
1532         _DtHelpCeFreeStringArray (ccdfVol->topicList);
1533
1534     if (ccdfVol->keywordFile != NULL)
1535         free (ccdfVol->keywordFile);
1536
1537     free(ccdfVol);
1538 }
1539
1540 /******************************************************************************
1541  * Function:    int  _DtHelpCeRereadCcdfVolume (_DtHelpVolume vol);
1542  *
1543  * Parameters:  vol             Specifies the loaded volume.
1544  *
1545  * Return Value:
1546  *
1547  * Purpose:     Reread a CCDF volume.
1548  *
1549  ******************************************************************************/
1550 int 
1551 _DtHelpCeRereadCcdfVolume (
1552     _DtHelpVolume         vol)
1553 {
1554     CcdfVolumePtr  ccdfVol = GetCcdfVolumePtr(vol);
1555
1556     if (ccdfVol->volDb != NULL)
1557         XrmDestroyDatabase (ccdfVol->volDb);
1558
1559     if (ccdfVol->topicList != NULL)
1560         _DtHelpCeFreeStringArray (ccdfVol->topicList);
1561
1562     if (ccdfVol->keywordFile != NULL)
1563         free (ccdfVol->keywordFile);
1564
1565     ccdfVol->topicList   = NULL;
1566     ccdfVol->keywordFile = NULL;
1567     ccdfVol->volDb       = XrmGetFileDatabase(vol->volFile);
1568
1569     if (ccdfVol->volDb != NULL)
1570         return 0;
1571
1572     return -1;
1573 }
1574
1575 /******************************************************************************
1576  * Function:    char *_DtHelpCeGetResourceString (XrmDatabase db, char *topic,
1577  *                                         char *resClass, char *resName)
1578  *
1579  * Parameters:  db              Specifies the handle to a resource database.
1580  *              topic           Specifies the topic whose resource value is
1581  *                              desired.  If 'topic' is NULL, the
1582  *                              desired resource is for the volume and
1583  *                              not a specific topic.
1584  *              resClass        Specifies the resource class name.
1585  *              resName         Specifies the resource name.
1586  *
1587  * Return Value:        Returns the desired resource as string.
1588  *                      This string is NOT owned by the caller and
1589  *                      should only be read or copied.
1590  *
1591  *                      Returns NULL if an error occurs.
1592  *
1593  * errno Values:        CEErrorMalloc
1594  *                      CEErrorIllegalResource  If the resource is not in
1595  *                                              the database or if the
1596  *                                              resource NULL
1597  *
1598  * Purpose:     Get a resource value for a volume or topic.
1599  *
1600  ******************************************************************************/
1601 char * 
1602 _DtHelpCeGetResourceString (
1603     XrmDatabase  db, 
1604     char        *topic, 
1605     char        *resClass, 
1606     char        *resName)
1607 {
1608     return (GetResourceString(db, topic, resClass, resName));
1609 }
1610
1611 /******************************************************************************
1612  * Function:    char **_DtHelpCeGetResourceStringArray (XrmDatabase db,
1613  *                              char *topic, char *resClass, char *resName)
1614  *
1615  * Parameters:  db              Specifies the handle to a resource database.
1616  *              topic           Specifies the topic whose resource value
1617  *                              is desired.  If 'topic' is NULL, the
1618  *                              desired resource is for the volume and
1619  *                              not a specific topic.
1620  *              resClass        Specifies the resource class name.
1621  *              resName         Specifies the resource name.
1622  *
1623  * Return Value:        Returns a NULL-terminated string array
1624  *                      containing the value of the desired resource.
1625  *                      The elements of the array are the strings of
1626  *                      non-whitespace characters in the resource value.
1627  *                      This array is owned by the caller and should be
1628  *                      freed (using _DtHelpCeFreeStringArray) when not
1629  *                      needed.
1630  *
1631  * Purpose:     Get am array-valued resource for a volume or topic.
1632  *
1633  ******************************************************************************/
1634 char **
1635 _DtHelpCeGetResourceStringArray (
1636     XrmDatabase  db, 
1637     char        *topic, 
1638     char        *resClass, 
1639     char        *resName)
1640 {
1641     return (GetResourceStringArray(db, topic, resClass, resName));
1642 }