Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / lib / DtHelp / Access.c
1 /* $XConsortium: Access.c /main/11 1996/11/01 10:09:29 drk $ */
2 /************************************<+>*************************************
3  ****************************************************************************
4  **
5  **   File:        Access.c
6  **
7  **   Project:     Run Time Project File Access
8  **
9  **   Description: This body of code handles the access routines for the
10  **                Display Area.
11  **
12  **
13  **  (c) Copyright 1987-1994, 1996 Hewlett-Packard Company
14  **  (c) Copyright 1993, 1994, 1996 International Business Machines Corp.
15  **  (c) Copyright 1993, 1994, 1996 Sun Microsystems, Inc.
16  **  (c) Copyright 1993, 1994, 1996 Novell, Inc.
17  **  (c) Copyright 1996 Digital Equipment Corporation.  
18  **  (c) Copyright 1996 FUJITSU LIMITED.        
19  **  (c) Copyright 1996 Hitachi.        
20  **
21  **
22  ****************************************************************************
23  ************************************<+>*************************************/
24
25 /*
26  * system includes
27  */
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <limits.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <sys/param.h>
36 #include <sys/stat.h>
37
38 #include <X11/Xlib.h>
39 #include <X11/Xresource.h>
40
41 #ifdef X_NOT_STDC_ENV
42 extern int errno;
43 #endif
44
45 /*
46  * Canvas Engine includes
47  */
48 #include "CanvasP.h"
49 #include "CanvasSegP.h"
50
51 /*
52  * private includes
53  */
54 #include "CanvasError.h"
55 #include "Access.h"
56 #include "bufioI.h"
57 #include "FontAttrI.h"
58 #include "AccessP.h"
59 #include "AccessI.h"
60 #include "AccessSDLP.h"
61 #include "AccessSDLI.h"
62 #include "AccessCCDFI.h"
63 #include "FormatUtilI.h"
64
65 #include "SDLI.h"
66 #include "FormatSDLI.h"
67
68 #include "CCDFUtilI.h"
69 #include "StringFuncsI.h"
70
71 #include "Lock.h"              /* Process and App Lock macros */
72
73 #ifdef NLS16
74 #endif
75
76 /********    Private Defines      ********/
77 #define LIST_INCREMENT  10
78 #define BUFF_SIZE       256
79 /********    End Private Defines  ********/
80
81 /********    Private Function Declarations    ********/
82 static  int       GetVolumeKeywords (
83                         _DtHelpVolume     vol, 
84                         char    ***retKeywords);
85 static  int       VolumeLoad (
86                         char        *volFile, 
87                         _DtHelpVolume  *retVol);
88 static  int       VolumeUnload (
89                         _DtHelpVolume vol);
90 /********    End Private Function Declarations    ********/
91
92 /********    Private Macro Declarations        ********/
93 /********    End Private Macro Declarations    ********/
94
95 /******************************************************************************
96 *
97 * Private variables used within this file.
98 *
99 *******************************************************************************/
100 static _DtHelpVolume volChain = NULL;   /* Pointer to the head of the chain */
101                                         /* of all the open volumes. */
102 static const char *Slash  = "/";
103 static const char *Period = ".";
104
105 /******************************************************************************
106  *                             Private Functions
107  ******************************************************************************/
108 /******************************************************************************
109  * Function:    CheckVolList (_DtHelpVolume vol, _DtHelpVolume *ret_prev)
110  *
111  * Parameters:  vol             Specifies the volume to search for.
112  *              ret_prev        Returns the volume whose nextVol element
113  *                              points to 'vol' if non NULL.
114  *
115  * Returns:     0 if successful, -1 if failure.
116  *
117  * errno Values:
118  *
119  * Purpose:     To check for the existance of a volume.
120  *
121  ******************************************************************************/
122 static  int
123 CheckVolList (
124     _DtHelpVolume        vol,
125     _DtHelpVolume       *ret_prev )
126 {
127     _DtHelpVolume       myVol;
128     _DtHelpVolume       prevVol = NULL;
129
130     _DtHelpProcessLock();
131     myVol = volChain;
132
133     while (myVol != NULL && myVol != vol)
134       {
135         prevVol = myVol;
136         myVol = myVol->nextVol;
137       }
138
139     if (ret_prev)
140         *ret_prev = prevVol;
141
142     if (myVol != vol)
143       {
144         _DtHelpProcessUnlock();
145         return -1;
146       }
147
148     _DtHelpProcessUnlock();
149     return 0;
150 }
151
152 /******************************************************************************
153  * Function:    int VolumeLoad (char *volFile, _DtHelpVolume *retVol);
154  *
155  * Parameters:  volFile         Specifies the name of the Help Volume file
156  *                              to load.
157  *
158  *              retVol          Returns the handle to the loaded volume.
159  *
160  * Return Value:        Returns 0 if successful,
161  *                      -1 if an error occurred.
162  *
163  * errno Values:        CEErrorMalloc
164  *                      CEErrorIllegalDatabaseFile
165  *
166  * Purpose:     This function must be called to load a Help Volume file
167  *              before any of the information in the volume can be
168  *              accessed. 
169  *
170  ******************************************************************************/
171 static int 
172 VolumeLoad (
173     char        *volFile, 
174     _DtHelpVolume  *retVol)
175 {
176     /* Allocate the volume structure and initialize it. */
177     *retVol = (_DtHelpVolume) malloc (sizeof (struct _DtHelpVolumeRec));
178     if (*retVol)
179       {
180         (*retVol)->sdl_flag      = False;
181         (*retVol)->volFile       = volFile;
182         (*retVol)->keywords      = NULL;
183         (*retVol)->keywordTopics = NULL;
184         (*retVol)->openCount     = 1;
185         (*retVol)->nextVol       = NULL;
186         (*retVol)->vols.ccdf_vol = NULL;
187
188         if (_DtHelpCeOpenSdlVolume ((*retVol)) == 0)
189             return 0;
190         else if (_DtHelpCeOpenCcdfVolume(*retVol) == 0)
191             return 0;
192
193         /*
194          * Set the global error
195          */
196         errno = CEErrorIllegalDatabaseFile;
197
198         /*
199          * error on loading the database.
200          */
201         free ((char *) ((*retVol)->volFile));
202         free ((char *) (*retVol));
203         *retVol = NULL;
204       }
205     else
206         errno = CEErrorMalloc;
207
208     return -1;
209
210 } /* End VolumeLoad */
211
212 /*******************************************************************************
213  * Function:    int VolumeUnload (_DtHelpVolume vol);
214  *
215  * Parameters:  vol     Specifies the loaded volume.
216  *
217  * Return Value:        0 if successful, -1 if a failure occurs
218  *
219  * errno Values:        None
220  *
221  * Purpose:     When the volume is no longer needed, it should be unloaded
222  *              with this call.  Unloading it frees the memory (which means
223  *              any handles on the volume become invalid.)
224  *
225  ******************************************************************************/
226 static int 
227 VolumeUnload (
228      _DtHelpVolume vol)
229 {
230     char        ***topicList;
231     
232     if (vol != NULL)
233       {
234
235         if (vol->sdl_flag == True)
236             _DtHelpCeCloseSdlVolume((_DtHelpVolumeHdl) vol);
237         else
238             _DtHelpCeCloseCcdfVolume(vol);
239
240         if (vol->volFile != NULL)
241             free (vol->volFile);
242
243         if (vol->keywords != NULL)
244             _DtHelpCeFreeStringArray (vol->keywords);
245
246         if (vol->keywordTopics != NULL)
247           {
248             for (topicList = vol->keywordTopics;
249                                         *topicList != NULL; topicList++)
250                 _DtHelpCeFreeStringArray (*topicList);
251
252             free (vol->keywordTopics);
253           }
254
255         free (vol);
256       }
257
258     return (0);
259 }
260
261 /*******************************************************************************
262  * Function:    int RereadVolume (_DtHelpVolume vol);
263  *
264  * Parameters:  vol     Specifies the loaded volume.
265  *
266  * Return Value:        0 if successful, -1 if a failure occurs
267  *
268  * errno Values:        None
269  *
270  * Purpose:     When the volume is no longer needed, it should be unloaded
271  *              with this call.  Unloading it frees the memory (which means
272  *              any handles on the volume become invalid.)
273  *
274  ******************************************************************************/
275 static int 
276 RereadVolume (
277      _DtHelpVolume vol)
278 {
279     int            result;
280     char        ***topicList;
281     
282     if (vol->keywords != NULL)
283       {
284         _DtHelpCeFreeStringArray (vol->keywords);
285         vol->keywords = NULL;
286       }
287
288     if (vol->keywordTopics != NULL)
289       {
290         for (topicList = vol->keywordTopics; *topicList != NULL; topicList++)
291             _DtHelpCeFreeStringArray (*topicList);
292
293         free (vol->keywordTopics);
294         vol->keywordTopics = NULL;
295       }
296
297     if (vol->sdl_flag == False)
298         result = _DtHelpCeRereadCcdfVolume(vol);
299     else
300         result = _DtHelpCeRereadSdlVolume(vol);
301
302     return (result);
303 }
304
305 /******************************************************************************
306  * Function:    static int GetKeywordTopics (_DtHelpVolume vol, char *keyword,
307  *                                     char ***topics);
308  *
309  * Parameters:  vol             Specifies the loaded volume
310  *              keyword         Specifies the keyword whose location is desired.
311  *              topics          Returns a NULL-terminated string array of the
312  *                              list of topics which contain the keyword.
313  *                              This array is NOT owned by the caller and
314  *                              should only be read or copied.
315  *
316  * Return Value:        0 if successful, -1 if a failure occurs
317  *
318  * errno Values:        CEErrorNoKeywordList
319  *                                      Specifies that the volume does not
320  *                                      have a keyword list.
321  *                      CEErrorIllegalKeyword
322  *                                      Specifies that 'keyword' was not
323  *                                      found.
324  *                      CEErrorMalloc
325  *                      CEErrorIllegalDatabaseFile
326  *                                      Specifies that the keyword file is
327  *                                      invalid or corrupt.
328  *                      CEErrorMissingKeywordsRes
329  *                                      Specifies that the keyword file does
330  *                                      not contain the 'Keywords/keywords'
331  *                                      resource or the resource is NULL
332  *
333  *
334  * Purpose:     Find which topic contains a specified locationId.
335  *
336  ******************************************************************************/
337 static int 
338 GetKeywordTopics (
339    _DtHelpVolume     vol, 
340    char           *keyword, 
341    char         ***retTopics)
342 {
343     char        **keywords;
344     char        **nextKey;
345     int index;
346
347     _DtHelpProcessLock();
348     *retTopics = NULL;
349
350     /* Get the list of keywords. */
351     if (GetVolumeKeywords (vol, &keywords) != 0)
352       {
353         _DtHelpProcessUnlock();
354         return -1;
355       }
356
357     if (keywords == NULL || vol->keywordTopics == NULL)
358       {
359         errno = CEErrorNoKeywordList;
360         _DtHelpProcessUnlock();
361         return -1;
362       }
363
364     /* Search the list of keywords for the current one. */
365     nextKey = keywords;
366     while (*nextKey != NULL && strcmp (*nextKey, keyword))
367         nextKey++;
368
369     if (*nextKey == NULL)
370       {
371         errno = CEErrorIllegalKeyword;
372         _DtHelpProcessUnlock();
373         return -1;
374       }
375
376     index = nextKey - keywords;
377     *retTopics = *(vol->keywordTopics + index);
378
379     _DtHelpProcessUnlock();
380     return (0);
381 }
382
383 /******************************************************************************
384  * Function:    static int GetVolumeKeywords(_DtHelpVolume vol,char ***keywords);
385  *
386  * Parameters:  vol             Specifies the volume.
387  *              keywords        Returns a NULL-terminated string array
388  *                              containing the sorted list of keywords in the
389  *                              volume.  This array is NOT owned by the caller
390  *                              and should only be read or copied.
391  *
392  * Return Value:        0 if successful, -1 if a failure occurs
393  *
394  * errno Values:        CEErrorMalloc
395  *                      CEErrorIllegalDatabaseFile
396  *                                      Specifies that the keyword file is
397  *                                      invalid or corrupt.
398  *                      CEErrorMissingKeywordsRes
399  *                                      Specifies that the keyword file does
400  *                                      not contain the 'Keywords/keywords'
401  *                                      resource or the resource is NULL
402  *
403  * Purpose:     Get the list of keywords defined in a volume.
404  *
405  ******************************************************************************/
406 static int 
407 GetVolumeKeywords (
408     _DtHelpVolume     vol, 
409     char        ***retKeywords)
410 {
411     int   result;
412     _DtHelpCeLockInfo lockInfo;
413
414     _DtHelpProcessLock();
415
416     /* Keywords aren't loaded until they are needed, so see if they have
417        been loaded yet. */
418     if (vol->keywords == NULL)
419       {
420         /*
421          * What type of volume is it?
422          */
423         if (_DtHelpCeLockVolume(vol, &lockInfo) != 0)
424           {
425             _DtHelpProcessUnlock();
426             return -1;
427           }
428
429         if (vol->sdl_flag == False)
430             result = _DtHelpCeGetCcdfKeywordList(vol);
431         else
432             result = _DtHelpCeGetSdlKeywordList(vol);
433
434         _DtHelpCeUnlockVolume(lockInfo);
435
436         if (result != 0)
437           {
438             _DtHelpProcessUnlock();
439             return -1;
440           }
441       }
442
443     /* All of the keyword processing is done when they are loaded. */
444      *retKeywords = vol->keywords;
445
446     if (*retKeywords == NULL)
447       {
448         _DtHelpProcessUnlock();
449         return (-1);
450       }
451
452     _DtHelpProcessUnlock();
453     return (0);
454 }
455
456 /*****************************************************************************
457  * Function: GetTopicTitleAndAbbrev (
458  *
459  * Parameters:
460  *
461  * Memory own by caller:
462  *              ret_name
463  *              ret_abrrev
464  *
465  * Returns:     0 if successful, -2 if didn't find the id,
466  *              -3 if couldn't format the topic,
467  *              otherwise -1.
468  *
469  * Purpose:     Find the title and abbreviated title of a topic.
470  *
471  *****************************************************************************/
472 static int
473 GetTopicTitleAndAbbrev (
474         _DtHelpVolume    volume,
475         char             *id,
476         char            **ret_name,
477         char            **ret_abbrev )
478 {
479     int         result = 0;
480     int         offset;
481     char        buffer[BUFF_SIZE];
482     char       *bufPtr;
483     char       *filename = NULL;
484     BufFilePtr  file;
485     _DtHelpCeLockInfo lockInfo;
486
487     if (_DtHelpCeLockVolume(volume, &lockInfo) != 0)
488         return -1;
489
490     if (_DtHelpCeFindId(volume, id, lockInfo.fd, &filename, &offset) != True)
491         result = -2;
492
493     /*
494      * What type of volume is it?
495      */
496     if (result == 0)
497       {
498         if (0 == _DtHelpCeGetVolumeFlag(volume))
499           {
500             result = _DtHelpCeFileOpenAndSeek(filename,offset,-1,&file,NULL);
501             if (result == 0)
502               {
503                 result = -1;
504                 if (_DtHelpCeReadBuf (file, buffer, BUFF_SIZE) != -1)
505                   {
506                     result = 0;
507                     bufPtr = buffer;
508                     if (_DtHelpCeGetCcdfTopicAbbrev (NULL, file,
509                                 buffer, &bufPtr, BUFF_SIZE, MB_CUR_MAX,
510                                         ret_name, NULL, ret_abbrev) != 0)
511                         result = -3;
512                   }
513                 _DtHelpCeBufFileClose(file, True);
514               }
515           }
516         else
517           {
518             _DtHelpProcessLock();
519             if (_DtHelpCeFrmtSDLTitleToAscii(volume, offset,
520                                                 ret_name, ret_abbrev) != 0)
521               result = -3;
522             _DtHelpProcessUnlock();
523           }
524       }
525
526     if (filename != NULL)
527         free(filename);
528
529     _DtHelpCeUnlockVolume(lockInfo);
530
531     return result;
532 }
533
534 /*****************************************************************************
535  * Function: static int FileOpenRtnFd (char *name, int *ret_fd)
536  *
537  * Parameters:  name            Specifies the file to open.
538  *              ret_fd          Returns the fd of the opened file.
539  *
540  * Returns:      0 if required uncompress.
541  *                 file descriptor to remove the file from the system.
542  *               1 if no uncompression required.
543  *              -1 if a failure occurs
544  *
545  * errno Values:        EINVAL          Specifies an invalid parameter was
546  *                                      used.
547  *                      CEErrorFileSeek
548  *                                      Specifies the seek offset was invalid.
549  *
550  * Purpose:     Find out if a file is compressed and uncompress it if it is.
551  *
552  *****************************************************************************/
553 static int
554 FileOpenRtnFd (
555     char        *name,
556     int         *ret_fd )
557 {
558     char *inFile = NULL;
559     char  tmpName[MAXPATHLEN + 1];
560     int   result = 1;
561
562     /*
563      * check to see if the file exists in uncompressed form
564      */
565     *ret_fd = open(name, O_RDONLY);
566     if (*ret_fd == -1)
567       {
568         /*
569          * get a temporary name
570          */
571         (void) tmpnam (tmpName);
572
573         /*
574          * malloc memory for the dot Z file name.
575          */
576         inFile = (char *) malloc (strlen (name) + 3);
577         if (inFile != NULL)
578           {
579             /*
580              * make the dot Z file
581              */
582             strcpy (inFile, name);
583             strcat (inFile, ".Z");
584
585             /*
586              * do the uncompress
587              */
588             result = _DtHelpCeUncompressFile (inFile, tmpName);
589             free (inFile);
590
591             if (result != 0)
592               {
593                 errno = ENOENT;
594                 return -1;
595               }
596
597             /*
598              * now open the uncompressed file
599              */
600             *ret_fd = open(tmpName, O_RDONLY);
601             if (*ret_fd == -1)
602                 result = -1;
603             else
604                 unlink(tmpName);
605           }
606         else
607           {
608             errno = CEErrorMalloc;
609             return -1;
610           }
611       }
612
613     return result;
614
615 } /* End FileOpenRtnFd */
616
617 /******************************************************************************
618  *                          Semi-Public Functions
619  ******************************************************************************/
620 /*****************************************************************************
621  * Function: char *_DtHelpCeExpandPathname (char *spec, char *filename, char *type,
622  *              char *suffix, char *lang, _DtSubstitutionRec *subs, int num)
623  *
624  * Parameters:
625  *              spec            Specifies a string with substitution
626  *                                      characters.
627  *                              containing the character set if found.
628  *              filename        Specifies the string to substitute for %N.
629  *              type            Specifies the string to substitute for %T.
630  *              suffix          Specifies the string to substitute for %S.
631  *              lang            Specifies the string to substitute for %L.
632  *              subs            Specifies the application own specific
633  *                                      substitutions.
634  *              num             Specifies the number of substitution pairs
635  *                                      in 'subs'.
636  *
637  * Memory own by caller:
638  *              returned pointer
639  *
640  * Returns:     The expanded filename if successful. NULL if errors.
641  *
642  * Purpose:     Expand a string with %<char> substitution values.
643  *              Default substitutions are:
644  *                      %N      replaced with 'filename'.
645  *                      %T      replaced with 'type'
646  *                      %S      replaced with 'suffix'
647  *                      %L      replaced with 'lang'
648  *
649  *                      %l      replaced with the language sub part of 'lang'.
650  *                      %t      replaced with the territory sub part of 'lang'.
651  *                      %c      replaced with the code set sub part of 'lang'.
652  *              Other substitutions can be done via the 'subs' parameter.
653  *
654  *****************************************************************************/
655 char *
656 _DtHelpCeExpandPathname (
657     char  *spec,
658     char  *filename,
659     char  *type,
660     char  *suffix,
661     char  *lang,
662     _DtSubstitutionRec *subs,
663     int    num )
664 {
665     int    i;
666     int    len = 1;
667     char  *ptr;
668     char  *subString;
669     char  *partLang;
670     char  *partTer;
671     char  *partCodeSet;
672     char   pathName [MAXPATHLEN + 5];
673     Boolean   previousSlash = False;
674 #define MY_NUM 7
675     _DtSubstitutionRec mySubs [MY_NUM];
676
677     if (spec == NULL || *spec == NULL)
678       {
679         errno = EINVAL;
680         return NULL;
681       }
682
683     /*
684      * fill in the language sub parts
685      */
686     if (_DtHelpCeGetLangSubParts (lang, &partLang, &partTer, &partCodeSet))
687         return NULL;
688
689     mySubs[0].match = 'N';
690     mySubs[0].substitution = filename;
691     mySubs[1].match = 'T';
692     mySubs[1].substitution = type;
693     mySubs[2].match = 'S';
694     mySubs[2].substitution = suffix;
695     mySubs[3].match = 'L';
696     mySubs[3].substitution = lang;
697
698     mySubs[4].match = 'l';
699     mySubs[4].substitution = partLang;
700     mySubs[5].match = 't';
701     mySubs[5].substitution = partTer;
702     mySubs[6].match = 's';
703     mySubs[6].substitution = partCodeSet;
704
705     ptr = pathName;
706     while (*spec)
707       {
708         len = 1;
709         if (MB_CUR_MAX != 1)
710             len = mblen (spec, MB_CUR_MAX);
711
712         if (len == 1 && *spec == '/')
713           {
714             if (previousSlash)
715                 spec++;
716             else
717               {
718                 previousSlash = True;
719                 *ptr++ = *spec++;
720               }
721           }
722         else if (len == 1 && *spec == '%')
723           {
724             spec++;
725             switch (*spec)
726               {
727                 case '\0':
728                         *ptr++ = '%';
729                         break;
730
731                 case '/':
732                         if (!previousSlash)
733                           {
734                              previousSlash = True;
735                              *ptr++ = *spec;
736                           }
737                         spec++;
738                         break;
739
740                 default:
741                         i = 0;
742                         while (i < MY_NUM && mySubs && mySubs[i].match != *spec)
743                             i++;
744
745                         if (i < MY_NUM)
746                           {
747                             if (mySubs[i].substitution != NULL)
748                               {
749                                 subString = mySubs[i].substitution;
750                                 if (((int)(ptr - pathName + strlen(subString)))
751                                                 > MAXPATHLEN)
752                                   {
753                                     errno = CEErrorExceedMaxSize;
754                                     return NULL;
755                                   }
756                                 while (subString && *subString)
757                                     *ptr++ = *subString++;
758                               }
759                           }
760                         else
761                           {
762                             i = 0;
763                             while (i < num && subs && subs[i].match != *spec)
764                                 i++;
765                             /*
766                              * If the substitution character is not found
767                              * include the character onto the final string.
768                              */
769                             if (i >= num)
770                                 *ptr++ = *spec;
771                             else if (subs[i].substitution != NULL)
772                               {
773                                 subString = subs[i].substitution;
774                                 if (((int)(ptr - pathName + strlen(subString)))
775                                                 > MAXPATHLEN)
776                                   {
777                                     errno = CEErrorExceedMaxSize;
778                                     return NULL;
779                                   }
780                                 while (subString && *subString)
781                                     *ptr++ = *subString++;
782                               }
783                           }
784                         spec++;
785                         previousSlash = False;
786                         break;
787               }
788           }
789         else
790           {
791             previousSlash = False;
792             do 
793               {
794                 *ptr++ = *spec++;
795                 len--;
796               } while (len > 0);
797           }
798
799         if (ptr - pathName > MAXPATHLEN)
800           {
801             errno = CEErrorExceedMaxSize;
802             return NULL;
803           }
804       }
805
806     if (partLang)
807         free (partLang);
808     if (partTer)
809         free (partTer);
810     if (partCodeSet)
811         free (partCodeSet);
812
813     *ptr = '\0';
814     ptr = strdup (pathName);
815     if (ptr == NULL)
816         errno = CEErrorMalloc;
817
818     return ptr;
819 }
820
821 /*****************************************************************************
822  * Function: char *_DtHelpCeGetLangSubParts (char *lang, char **subLang,
823  *                                      char **subTer, char **subCodeSet)
824  *
825  * Parameters:
826  *              lang            Specifies the language string.
827  *              subLang         Returns the language sub part of 'lang'
828  *                                      or NULL.
829  *              subTer          Returns the territory sub part of 'lang'
830  *                                      or NULL.
831  *              subCodeSet      Returns the code set sub part of 'lang'
832  *                                      or NULL.
833  *
834  * Memory own by caller:
835  *              subLang
836  *              subTer
837  *              subCodeSet
838  *
839  * errno Values:
840  *              EINVAL
841  *              CEErrorMalloc
842  *
843  * Returns:     0 if successful, -1 if errors.
844  *
845  * Purpose:     Break a %l_%t.%c language specification into its sub parts.
846  *
847  *****************************************************************************/
848 int
849 _DtHelpCeGetLangSubParts (
850     char   *lang,
851     char  **subLang,
852     char  **subTer,
853     char  **subCodeSet )
854 {
855     int   len;
856     char *ptr;
857     char *sLang = NULL;
858     char *sTer  = NULL;
859     char *sCode = NULL;
860
861     if (subLang == NULL || subTer == NULL || subCodeSet == NULL)
862       {
863         errno = EINVAL;
864         return -1;
865       }
866
867     if (lang != NULL && *lang != '\0')
868       {
869         /*
870          * look for lang_ter
871          */
872         _DtHelpCeStrchr (lang, "_", MB_CUR_MAX, &ptr);
873         if (ptr)
874           {
875             /*
876              * do we want this string?
877              */
878             if (subLang != NULL)
879               {
880                 len = ptr - lang;
881                 sLang = (char *) malloc (len + 1);
882                 if (sLang != NULL)
883                   {
884                     strncpy (sLang, lang, len);
885                     sLang[len] = '\0';
886                   }
887                 else
888                   {
889                     errno = CEErrorMalloc;
890                     return -1;
891                   }
892               }
893             /*
894              * just mark that the lang part was found
895              */
896             else
897                 sLang = lang;
898     
899             lang = ptr + 1;
900           }
901     
902         /*
903          * look for lang.codeset
904          */
905         _DtHelpCeStrchr (lang, Period, MB_CUR_MAX, &ptr);
906         if (ptr)
907           {
908             len = ptr - lang;
909     
910             /*
911              * if it was in the form lang_ter.codeset, sLang will non-null
912              */
913             if (sLang != NULL)
914               {
915                 /*
916                  * do we want to save the territory?
917                  */
918                 if (subTer != NULL)
919                   {
920                     sTer = (char *) malloc (len + 1);
921                     if (sTer != NULL)
922                       {
923                         strncpy (sTer, lang, len);
924                         sTer[len] = '\0';
925                       }
926                     else
927                       {
928                         errno = CEErrorMalloc;
929                         return -1;
930                       }
931                   }
932                 /*
933                  * don't wan to save, but make sure we mark the territory
934                  * as being found (non-null).
935                  */
936                 else
937                     sTer = lang;
938               }
939             /*
940              * the lang was in the form lang.codeset.
941              * now check to see if want to save the lang portion.
942              */
943             else if (subLang != NULL)
944               {
945                 sLang = (char *) malloc (len + 1);
946                 if (sLang != NULL)
947                   {
948                     strncpy (sLang, lang, len);
949                     sLang[len] = '\0';
950                   }
951                 else
952                   {
953                     errno = CEErrorMalloc;
954                     return -1;
955                   }
956               }
957             /*
958              * didn't want to save the lang portion, but mark as found.
959              */
960             else
961                 sLang = lang;
962           }
963     
964        /*
965         * currently pointing at the dot?
966         */
967        if (ptr && *ptr == '.')
968          {
969             /*
970              * yes save the code set
971              */
972             ptr++;
973             if (subCodeSet != NULL)
974               {
975                 sCode = strdup (ptr);
976                 if (sCode == NULL)
977                   {
978                     errno = CEErrorMalloc;
979                     return -1;
980                   }
981               }
982             /*
983              * don't save the code set, but make sure we mark as found
984              */
985             else
986                 sCode = ptr;
987          }
988        /*
989         * didn't find a code set, so save the current info.
990         * If we haven't already processed a lang portion, save as the
991         * lang.
992         */
993        else if (sLang == NULL)
994          {
995             if (subLang != NULL)
996               {
997                 sLang = strdup (lang);
998                 if (sLang == NULL)
999                   {
1000                     errno = CEErrorMalloc;
1001                     return -1;
1002                   }
1003               }
1004             else
1005                 sLang = lang;
1006          }
1007        /*
1008         * otherwise this is the territory of the language. Save if desired
1009         */
1010        else if (subTer != NULL)
1011          {
1012             sTer = strdup (lang);
1013             if (sTer == NULL)
1014               {
1015                 errno = CEErrorMalloc;
1016                 return -1;
1017               }
1018           }
1019       }
1020
1021     if (subLang)
1022         *subLang = sLang;
1023     if (subTer)
1024         *subTer = sTer;
1025     if (subCodeSet)
1026         *subCodeSet = sCode;
1027
1028     return 0;
1029 }
1030
1031 /*****************************************************************************
1032  * Function: int _DtHelpCeGetUncompressedFileName (char *name, char **ret_name)
1033  *
1034  * Parameters:  name            Specifies the file to open.
1035  *              ret_name        Returns the name of the uncompressed file.
1036  *                              This memory must be freed by the caller.
1037  *
1038  * Returns:      0 if required uncompress. ret_name will contain the
1039  *                 name of the uncompressed file. The caller is required
1040  *                 to free the memory.
1041  *               1 if no uncompression required. ret_name points to name.
1042  *              -1 if a failure occurs
1043  *
1044  * errno Values:        EINVAL          Specifies an invalid parameter was
1045  *                                      used.
1046  *                      CEErrorFileSeek
1047  *                                      Specifies the seek offset was invalid.
1048  *
1049  * Purpose:     Find out if a file is compressed and uncompress it if it is.
1050  *
1051  *****************************************************************************/
1052 int
1053 _DtHelpCeGetUncompressedFileName (
1054         char             *name,
1055         char            **ret_name )
1056 {
1057     char *inFile = NULL;
1058     char  tmpName[MAXPATHLEN + 1];
1059     int   result = 1;
1060
1061     /*
1062      * check to see if the file exists in uncompressed form
1063      */
1064     *ret_name = name;
1065     if (access (name, F_OK) == -1)
1066       {
1067         /*
1068          * get a temporary name
1069          */
1070         (void) tmpnam (tmpName);
1071
1072         /*
1073          * malloc memory for the dot Z file name.
1074          */
1075         inFile = (char *) malloc (strlen (name) + 3);
1076         if (inFile != NULL)
1077           {
1078             /*
1079              * make the dot Z file
1080              */
1081             strcpy (inFile, name);
1082             strcat (inFile, ".Z");
1083
1084             /*
1085              * do the uncompress
1086              */
1087             result = _DtHelpCeUncompressFile (inFile, tmpName);
1088             free (inFile);
1089
1090             if (result != 0)
1091               {
1092                 errno = ENOENT;
1093                 return -1;
1094               }
1095
1096             *ret_name = strdup (tmpName);
1097             if (*ret_name == NULL)
1098               {
1099                 errno = CEErrorMalloc;
1100                 return -1;
1101               }
1102           }
1103         else
1104           {
1105             errno = CEErrorMalloc;
1106             return -1;
1107           }
1108       }
1109
1110     return result;
1111 }
1112
1113 /******************************************************************************
1114  * Function:    int _DtHelpCeCompressPathname (char *basePath)
1115  *
1116  * Parameters:  basePath        Specifies the path for the file possibily
1117  *                              containing /./, //, and /../.
1118  *
1119  * Return Value:        0 for success, -1 for failure.
1120  *                      The number of bytes in basePath will be less than or
1121  *                      equal to the number of bytes in basePath when passed
1122  *                      in.
1123  *
1124  * errno Values:        EINVAL
1125  *
1126  * Purpose:     This function compresses directory changes found
1127  *              in a file name path.
1128  *
1129  ******************************************************************************/
1130 int
1131 _DtHelpCeCompressPathname ( char        *basePath )
1132 {
1133     int    len;
1134     int    result;
1135     short  char1;
1136     short  char2;
1137     short  char3;
1138     char  *ptr     = basePath;
1139     char  *prevPtr = NULL;
1140
1141     if (basePath == NULL || *basePath != '/')
1142       {
1143         errno = EINVAL;
1144         return -1;
1145       }
1146
1147     do
1148       {
1149         /*
1150          * for multi-byte environments, check how far single bytes extend.
1151          */
1152         char1 = False;
1153         char2 = False;
1154         char3 = False;
1155         if (MB_CUR_MAX == 1 || mblen (&ptr[1], MB_CUR_MAX) == 1)
1156           {
1157             char1 = True;
1158             if (MB_CUR_MAX == 1 || mblen (&ptr[2], MB_CUR_MAX) == 1)
1159               {
1160                 char2 = True;
1161                 if (MB_CUR_MAX == 1 || mblen (&ptr[3], MB_CUR_MAX) == 1)
1162                     char3 = True;
1163               }
1164           }
1165
1166         /*
1167          * check for //
1168          */
1169         if (char1 == True && ptr[1] == '/')
1170             strcpy (ptr, (ptr + 1));
1171
1172         /*
1173          * check for /./
1174          */
1175         else if (char2 == True && ptr[1] == '.' && ptr[2] == '/')
1176             strcpy (ptr, (ptr + 2));
1177
1178         /*
1179          * check for /../
1180          */
1181         else if (char3 == True && strncmp (&ptr[1], "../", 3) == 0)
1182           {
1183             /*
1184              * if at the top of the path, just ignore the extra
1185              * directory change.
1186              */
1187             if (prevPtr == NULL)
1188                 strcpy (ptr, (ptr + 3));
1189             else
1190               {
1191                 /*
1192                  * compress the /../
1193                  */
1194                 strcpy (prevPtr, (ptr + 3));
1195
1196                 /*
1197                  * reset the current pointer
1198                  */
1199                 ptr    = prevPtr;
1200
1201                 /*
1202                  * find the previous slash
1203                  */
1204                 *ptr   = '\0';
1205                 result = _DtHelpCeStrrchr(basePath,Slash,MB_CUR_MAX,&prevPtr);
1206                 if (result == -1)
1207                     return -1;
1208
1209                 /*
1210                  * if there is no previous slash, set the pointer to
1211                  * indicate that we're at the top of the path (NULL).
1212                  */
1213                 if (result != 0)
1214                     prevPtr = NULL;
1215
1216                 /*
1217                  * restore the slash (or null byte)
1218                  */
1219                 *ptr = '/';
1220               }
1221           }
1222         else
1223           {
1224             /*
1225              * remember this slash for /../ directory changes
1226              */
1227             prevPtr = ptr;
1228
1229             /*
1230              * skip this slash, and find the next one.
1231              */
1232             ptr++;
1233             result = _DtHelpCeStrcspn (ptr, "/", MB_CUR_MAX, &len);
1234
1235             /*
1236              * if we run into invalid data, error
1237              */
1238             if (result == -1)
1239                 return -1;
1240
1241             ptr += len;
1242           }
1243
1244       } while (*ptr != '\0');
1245
1246     return (0);
1247 }
1248
1249 /******************************************************************************
1250  * Function:    char *_DtHelpCeTracePathName (char *path)
1251  *
1252  * Parameters:
1253  *               path   Specifies the a path to trace and compress
1254  *
1255  * Return Value:        The new string if successful, NULL otherwise.
1256  *                      The new string is owned by the caller and contains
1257  *                      an absolute pathname.
1258  *
1259  * errno Values:        EINVAL                  Illegal parameter specified.
1260  *                      getcwd(2)               errno set via a getcwd call.
1261  *                      readlink(2)             errno set via a readlink call.
1262  *                      DtErrorMalloc
1263  *                      DtErrorExceedMaxSize   The new path will exceed
1264  *                                              max_size.
1265  *                      DtErrorIllegalPath     The compression will required
1266  *                                              the path to change to a parent
1267  *                                              directory beyond the beginning
1268  *                                              of basePath.
1269  *
1270  * Purpose:     This function is called to trace the path of a file.
1271  *              It can contain symbolic links, //, /./, and /../.
1272  *
1273  ******************************************************************************/
1274 char *
1275 _DtHelpCeTracePathName (char *path)
1276 {
1277     int     result;
1278     int     len;
1279     char    c;
1280     char   *ptr;
1281     char   *prev;
1282     char    newPath  [2 * MAXPATHLEN + 2];
1283     char    linkPath [MAXPATHLEN + 2];
1284     char    tempPath [MAXPATHLEN + 2];
1285
1286     if (path == NULL || *path == '\0')
1287       {
1288         errno = EINVAL;
1289         return NULL;
1290       }
1291
1292     /*
1293      * initialize the new path
1294      */
1295     newPath[0] = '\0';
1296
1297     /*
1298      * if the path passed in does not start with a slash,
1299      * get the current working directory and append the path to it.
1300      */
1301     if ((MB_CUR_MAX == 1 || mblen(path, MB_CUR_MAX) == 1) && *path != '/')
1302       {
1303         if (getcwd (newPath, MAXPATHLEN) == NULL)
1304             return NULL;
1305
1306         strcat (newPath, "/");
1307       }
1308
1309     /*
1310      * put the path in the working path buffer (or append it to
1311      * the current working directory).
1312      */
1313     strcat (newPath, path);
1314
1315     /*
1316      * Compress out the slashes and directory changes.
1317      */
1318     if (_DtHelpCeCompressPathname (newPath) != 0)
1319         return NULL;
1320
1321     ptr = newPath;
1322     do
1323       {
1324         /*
1325          * point to the first character of the next directory
1326          */
1327         prev = ptr + 1;
1328
1329         /*
1330          * get the next slash after that
1331          */
1332         result = _DtHelpCeStrcspn (prev, "/", MB_CUR_MAX, &len);
1333         if (result == -1)
1334             return NULL;
1335
1336         /*
1337          * Found either a slash or a null byte.
1338          * place the string terminator at this point
1339          */
1340         ptr  = prev + len;
1341         c    = *ptr;
1342         *ptr = '\0';
1343
1344         /*
1345          * find out if this path is a symbolic link
1346          */
1347         result = readlink (newPath, linkPath, MAXPATHLEN);
1348
1349         /*
1350          * replace the slash (or null byte).
1351          */
1352         *ptr = c;
1353
1354         /*
1355          * check for the result of the readlink call
1356          */
1357         if (result == -1)
1358           {
1359             /*
1360              * if this was NOT a symbolic link, errno should be EINVAL
1361              */
1362             if (errno != EINVAL)
1363                 return NULL;
1364           }
1365         else
1366           {
1367             /*
1368              * put the null byte on the end of the symbolic link string.
1369              */
1370             linkPath [result] = '\0';
1371
1372             /*
1373              * Save the rest of the string that we haven't processed
1374              * for tacking on after the new link path has been
1375              * dropped into the path.
1376              */
1377             strcpy (tempPath, ptr);
1378
1379             /*
1380              * is it an absolute path? Simply replace the path
1381              * being search with the new link path.
1382              */
1383             if (*linkPath == '/')
1384                 strcpy (newPath, linkPath);
1385             else
1386               {
1387                 /*
1388                  * this is a relative link.
1389                  * prev is looking at the first character of this directory.
1390                  * replace with the link.
1391                  */
1392                 strcpy (prev, linkPath);
1393               }
1394
1395             /*
1396              * now tack on the rest of the name
1397              */
1398             strcat (newPath, tempPath);
1399
1400             /*
1401              * compress out the directory changes.
1402              */
1403             if (_DtHelpCeCompressPathname (newPath) != 0)
1404                 return NULL;
1405
1406             /*
1407              * start again from the top, until we have a clean path
1408              */
1409             ptr = newPath;
1410           }
1411
1412       } while (*ptr != '\0');
1413
1414     return (strdup (newPath));
1415 }
1416
1417 /******************************************************************************
1418  * Function:    char *_DtHelpCeTraceFilenamePath (char *file_path)
1419  *
1420  * Parameters:
1421  *               file_path      Specifies the a path to trace and compress
1422  *
1423  * Return Value:        The new string if successful, NULL otherwise.
1424  *                      The new string is owned by the caller and
1425  *                      contains an absolute filename path.
1426  *
1427  * errno Values:        EINVAL                  Illegal parameter specified.
1428  *                      getcwd(2)               errno set via a getcwd call.
1429  *                      readlink(2)             errno set via a readlink call.
1430  *                      DtErrorMalloc
1431  *                      DtErrorExceedMaxSize   The new path will exceed
1432  *                                              max_size.
1433  *                      DtErrorIllegalPath     The compression will required
1434  *                                              the path to change to a parent
1435  *                                              directory beyond the beginning
1436  *                                              of basePath.
1437  *
1438  * Purpose:     This function is called to trace a filename path.
1439  *              It can contain symbolic links, //, /./, and /../.
1440  *
1441  ******************************************************************************/
1442 char *
1443 _DtHelpCeTraceFilenamePath (char *file_path)
1444 {
1445     int     result;
1446     int     done = False;
1447     char   *newPath;
1448     char   *oldName;
1449     char   *namePlace;
1450     char    workName [MAXPATHLEN + 2];
1451     char    newName  [MAXPATHLEN + 2];
1452     char    linkName [MAXPATHLEN + 2];
1453
1454     if (file_path == NULL || *file_path == '\0')
1455       {
1456         errno = EINVAL;
1457         return NULL;
1458       }
1459
1460     workName[0] = '\0';
1461     if ((MB_CUR_MAX == 1 || mblen(file_path, MB_CUR_MAX) == 1)
1462                                                         && *file_path != '/')
1463       {
1464         if (getcwd(workName, MAXPATHLEN) == NULL)
1465            return NULL;
1466
1467         strcat(workName, "/");
1468       }
1469
1470     strcat (workName, file_path);
1471
1472     do
1473       {
1474         /*
1475          * find and save the old filename
1476          */
1477         result = _DtHelpCeStrrchr(workName, Slash, MB_CUR_MAX, &oldName);
1478         if (result == -1)
1479             return NULL;
1480
1481         /*
1482          * terminate the path
1483          */
1484         *oldName = '\0';
1485
1486         /*
1487          * trace the path, resolving the symbolic links
1488          * and directory changes. If /filename given,
1489          * skip the path tracing.
1490          */
1491         newName[0] = '\0';
1492         if (workName[0] != '\0')
1493           {
1494             newPath = _DtHelpCeTracePathName(workName);
1495             if (newPath == NULL)
1496                 return NULL;
1497
1498             /*
1499              * copy the new path and free the allocated copy
1500              */
1501             strcpy (newName, newPath);
1502             free (newPath);
1503           }
1504
1505         /*
1506          * replace the slash
1507          */
1508         *oldName = '/';
1509
1510         /*
1511          * now append the slash and filename (pointed to by oldName)
1512          * onto the end of the new path.
1513          */
1514         namePlace = newName + strlen (newName);
1515         strcpy (namePlace, oldName);
1516
1517         /*
1518          * See if the absolute path/filename is a symbolic link.
1519          */
1520         result = readlink (newName, linkName, MAXPATHLEN);
1521         if (result == -1)
1522           {
1523             if (errno != EINVAL)
1524                 return NULL;
1525
1526             done = True;
1527           }
1528         else
1529           {
1530             /*
1531              * put the null byte on the end of the symbolic
1532              * link string.
1533              */
1534             linkName [result] = '\0';
1535             if (*linkName == '/')
1536                 strcpy (newName, linkName);
1537             else
1538               {
1539                 /*
1540                  * overwrite the filename with the link
1541                  * but don't overwrite the slash.
1542                  */
1543                 strcpy ((namePlace + 1), linkName);
1544               }
1545
1546             /*
1547              * make a copy of the new name to work on
1548              */
1549             strcpy (workName, newName);
1550           }
1551       } while (!done);
1552
1553     return (strdup (newName));
1554 }
1555
1556
1557 /******************************************************************************
1558  *                    Core Engine Semi-Public Functions
1559  ******************************************************************************/
1560 /*****************************************************************************
1561  * Function: char *_DtHelpCeGetVolumeName (_DtHelpVolumeHdl volume)
1562  *
1563  * Parameters:
1564  *
1565  * Returns:     ptr to the name of the volume, NULL otherwise.
1566  *
1567  * Purpose:     Get the fully qualified volume name.
1568  *
1569  *****************************************************************************/
1570 char *
1571 _DtHelpCeGetVolumeName (
1572     _DtHelpVolumeHdl  volume_handle)
1573 {
1574     char *volFile;
1575
1576     _DtHelpProcessLock();
1577     volFile = ((_DtHelpVolume)volume_handle)->volFile;
1578     _DtHelpProcessUnlock();
1579     return volFile;
1580
1581 } /* End __DtHelpCeGetVolumeName */
1582
1583 /*****************************************************************************
1584  * Function: int _DtHelpCeFileOpenAndSeek (char *name, int offset, int fildes,
1585  *                                                      BufFilePtr *ret_file)
1586  *
1587  * Parameters:  name            Specifies the file to open.
1588  *              offset          Specifies location within the file to seek to.
1589  *
1590  * Returns:     0 if successful, -1 if a failure occurs
1591  *
1592  * errno Values:        EINVAL          Specifies an invalid parameter was
1593  *                                      used.
1594  *                      CEErrorFileSeek
1595  *                                      Specifies the seek offset was invalid.
1596  *
1597  * Purpose:     Open a file and seek to a specific place.
1598  *
1599  *****************************************************************************/
1600 int
1601 _DtHelpCeFileOpenAndSeek (
1602         char            *name,
1603         int              offset,
1604         int              fd,
1605         BufFilePtr      *ret_file,
1606         time_t          *ret_time)
1607 {
1608     unsigned char fileMagic[4];
1609     int           bytesRead;
1610     int           result = 0;
1611     int           tmpFd;
1612     struct stat   buf;
1613
1614     /*
1615      * Get the file descriptor of the uncompressed file
1616      */
1617     tmpFd = fd;
1618     if (fd == -1)
1619       {
1620         result = FileOpenRtnFd (name, &tmpFd);
1621         if (result == -1)
1622             return -1;
1623       }
1624
1625     if (ret_time != NULL)
1626       {
1627         (void) fstat(tmpFd, &buf);
1628         *ret_time = buf.st_mtime;
1629       }
1630
1631     /*
1632      * make sure we don't go past the end of the file
1633      */
1634     result = lseek (tmpFd, 0, SEEK_END);
1635     if (result != -1)
1636       {
1637         if (result > offset)
1638             result = lseek (tmpFd, offset, SEEK_SET);
1639         else
1640           {
1641             result = -1;
1642             errno = CEErrorFileSeek;
1643           }
1644       }
1645
1646     if (result == -1)
1647       {
1648         if (fd == -1)
1649             close (tmpFd);
1650         return -1;
1651       }
1652
1653     bytesRead = read(tmpFd, fileMagic, 4);
1654     if (bytesRead != 4)
1655       { /* something's wrong in reading the file */
1656         if (fd == -1)
1657             close (tmpFd);
1658         return -1;
1659       }
1660
1661     if (!*fileMagic)
1662       { /* started with compressed file magic number */
1663
1664         CECompressInfoPtr myInfo;
1665         BufFilePtr        inputRaw;
1666
1667         /*
1668          * allocate the private information
1669          */
1670         myInfo = (CECompressInfoPtr) malloc(sizeof(CECompressInfo));
1671         if (myInfo == NULL)
1672           {
1673             if (fd == -1)
1674                 close (tmpFd);
1675             errno = CEErrorMalloc;
1676             return -1;
1677           }
1678
1679         /*
1680          * set the information
1681          * set the size to the maximum number of bytes we
1682          * want to read.
1683          */
1684         myInfo->fd   = tmpFd;
1685         myInfo->size =
1686           (((fileMagic[1] * 256) + fileMagic[2]) * 256) + fileMagic[3];
1687
1688         /*
1689          * start with raw functionality
1690          */
1691         inputRaw = _DtHelpCeBufFileRdRawZ(myInfo);
1692         if (inputRaw == NULL)
1693           {
1694             if (fd == -1)
1695                 close (tmpFd);
1696             return -1;
1697           }
1698
1699         *ret_file = _DtHelpCeBufFilePushZ(inputRaw);
1700         if (*ret_file == NULL)
1701           {
1702             _DtHelpCeBufFileClose(inputRaw, (fd == -1 ? True : False));
1703             return -1;
1704           }
1705       }
1706     else
1707       {
1708         /*
1709          * not a compressed file, back up the four bytes we read
1710          */
1711         result = lseek (tmpFd, offset, SEEK_SET);
1712         if (result == -1)
1713           {
1714             if (fd == -1)
1715                 close (tmpFd);
1716             return -1;
1717           }
1718
1719         /*
1720          * read with raw functionality
1721          */
1722         *ret_file = _DtHelpCeBufFileRdWithFd(tmpFd);
1723         if (*ret_file == NULL)
1724           {
1725             if (fd == -1)
1726                 close (tmpFd);
1727             return -1;
1728           }
1729       }
1730
1731     return 0;
1732
1733 } /* End of _DtHelpCeFileOpenAndSeek */
1734 /******************************************************************************
1735  *                     Core Engine Public Functions
1736  ******************************************************************************/
1737 /******************************************************************************
1738  * Function:    int _DtHelpOpenVolume (char *volFile, _DtHelpVolume *retVol);
1739  *
1740  * Parameters:  volFile         Specifies the name of the Help Volume file
1741  *                              to load.
1742  *
1743  *              retVol          Returns the handle to the loaded volume.
1744  *                              If a volume is opened several times, the
1745  *                              same handle will be returned each time.
1746  *
1747  * Return Value:                0 if successful, -1 if a failure occurred.
1748  *
1749  * errno Values:        EINVAL                  Illegal parameter specified.
1750  *                      getcwd(2)               errno set via a getcwd call.
1751  *                      readlink(2)             errno set via a readlink call.
1752  *                      CEErrorMalloc
1753  *                      CEErrorExceedMaxSize    The new path will exceed
1754  *                                              max_size.
1755  *                      CEErrorIllegalPath      The compression will required
1756  *                                              the path to change to a parent
1757  *                                              directory beyond the beginning
1758  *                                              of basePath.
1759  *                      CEErrorIllegalDatabaseFile
1760  *                                              Specifies that 'volFile' is
1761  *                                              an illegal database file.
1762  *
1763  *
1764  * Purpose:     This function must be called to open a Help Volume file
1765  *              before any of the information in the volume can be
1766  *              accessed. 
1767  *
1768  ******************************************************************************/
1769 int 
1770 _DtHelpOpenVolume (
1771     char        *volFile, 
1772     _DtHelpVolumeHdl  *retVol)
1773 {
1774     int     result  = 0;
1775     _DtHelpVolume vol, prevVol;
1776
1777     _DtHelpProcessLock();
1778
1779     if (volFile == NULL || retVol == NULL)
1780       {
1781         errno = EINVAL;
1782         _DtHelpProcessUnlock();
1783         return -1;
1784       }
1785
1786     /*
1787      * follow all the symbolic links and get the absolute path and filename.
1788      */
1789     volFile = _DtHelpCeTraceFilenamePath(volFile);
1790     if (volFile == NULL)
1791       {
1792         _DtHelpProcessUnlock();
1793         return -1;
1794       }
1795
1796     /* Search the volume chain to see if it is already open. */
1797     prevVol = NULL;
1798     vol = volChain; 
1799     while (vol != NULL && strcmp (vol->volFile, volFile))
1800       {
1801         prevVol = vol;
1802         vol     = vol->nextVol;
1803       }
1804
1805     if (vol)
1806       {
1807         vol->openCount++;
1808         free(volFile);
1809       }
1810     else /* if (vol == NULL) */
1811       {
1812         /* If it isn't open, open it and insert it in the chain. */
1813         result = VolumeLoad (volFile, &vol);
1814         if (result == 0)
1815           {
1816             if (prevVol == NULL)
1817                 volChain = vol;
1818             else
1819                 prevVol->nextVol = vol;
1820           }
1821       }
1822
1823     /* Return the volume handle and a status indicating success/failure. */
1824     *retVol = (_DtHelpVolumeHdl) vol;
1825     _DtHelpProcessUnlock();
1826     return result;
1827 }
1828
1829 /******************************************************************************
1830  * Function:    int _DtHelpCeUpVolumeOpenCnt (_DtHelpVolumeHdl vol);
1831  *
1832  * Parameters:  vol     Specifies the loaded volume.
1833  *
1834  * Return Value: 0 if successful, -1 if a failure occurs
1835  *
1836  * errno Values:        EINVAL          'vol' was NULL, no volumes open or
1837  *                                      'vol' does not exist.
1838  *
1839  * Purpose:     When the volume is no longer needed, it should be
1840  *              closed with this call.  If the volume has been opened
1841  *              several times, closing it will just decrement the 
1842  *              reference count.  When it has been closed as many times
1843  *              as it was opened, the memory it is using will be freed
1844  *              and any handles to the volume will be invalid.
1845  *
1846  ******************************************************************************/
1847 int 
1848 _DtHelpCeUpVolumeOpenCnt (
1849      _DtHelpVolumeHdl   volume)
1850 {
1851     _DtHelpVolume prevVol;
1852     _DtHelpVolume vol = (_DtHelpVolume)volume;
1853
1854     _DtHelpProcessLock();
1855
1856     if (vol == NULL || volChain == NULL)
1857       {
1858         errno = EINVAL;
1859         _DtHelpProcessUnlock();
1860         return (-1);
1861      }
1862
1863     /*
1864      * check to see if this volume is in our chain
1865      */
1866     if (vol != volChain)
1867       {
1868         if (CheckVolList (vol, &prevVol) == -1)
1869           {
1870             errno = EINVAL;
1871             _DtHelpProcessUnlock();
1872             return (-1);
1873           }
1874       }
1875
1876     /*
1877      * increment it's usage count.
1878      */
1879     vol->openCount++;
1880     _DtHelpProcessUnlock();
1881     return (0);
1882 }
1883
1884 /******************************************************************************
1885  * Function:    int _DtHelpCloseVolume (_DtHelpVolumeHdl vol);
1886  *
1887  * Parameters:  vol     Specifies the loaded volume.
1888  *
1889  * Return Value: 0 if successful, -1 if a failure occurs
1890  *
1891  * errno Values:        EINVAL          'vol' was NULL, no volumes open or
1892  *                                      'vol' does not exist.
1893  *
1894  * Purpose:     When the volume is no longer needed, it should be
1895  *              closed with this call.  If the volume has been opened
1896  *              several times, closing it will just decrement the 
1897  *              reference count.  When it has been closed as many times
1898  *              as it was opened, the memory it is using will be freed
1899  *              and any handles to the volume will be invalid.
1900  *
1901  ******************************************************************************/
1902 int 
1903 _DtHelpCloseVolume (
1904      _DtHelpVolumeHdl   volume)
1905 {
1906     _DtHelpVolume prevVol;
1907     _DtHelpVolume vol = (_DtHelpVolume)volume;
1908
1909     _DtHelpProcessLock();
1910
1911     if (vol == NULL || volChain == NULL)
1912       {
1913         errno = EINVAL;
1914         _DtHelpProcessUnlock();
1915         return (-1);
1916       }
1917
1918     /*
1919      * check to see if this volume is in our chain
1920      */
1921     if (vol != volChain)
1922       {
1923         if (CheckVolList (vol, &prevVol) == -1)
1924           {
1925             errno = EINVAL;
1926             _DtHelpProcessUnlock();
1927             return (-1);
1928           }
1929       }
1930
1931     /*
1932      * decrement it's usage count.
1933      */
1934     vol->openCount--;
1935     if (vol->openCount == 0)
1936       {
1937         /* The volume is no longer needed.  Unlink it from the chain
1938            and free it. */
1939
1940         if (vol == volChain) 
1941             volChain = volChain->nextVol;
1942
1943         else
1944             prevVol->nextVol = vol->nextVol;
1945
1946         VolumeUnload (vol);
1947       }
1948
1949     _DtHelpProcessUnlock();
1950     return (0);
1951 }
1952
1953 /*****************************************************************************
1954  * Function: int _DtHelpCeGetTopTopicId (_DtHelpVolume vol,
1955  *                                      char **ret_idString)
1956  *
1957  * Parameters:  vol             Specifies the loaded volume
1958  *              ret_idString    Returns the location ID of the
1959  *                              the top level topic.
1960  *
1961  * Memory own by caller:
1962  *              ret_idString
1963  *
1964  * Returns:     True for success, False if a failure occurs.
1965  *
1966  * errno Values:        EINVAL          Specifies an invalid parameter was
1967  *                                      used.
1968  *                      CEErrorMissingTopTopicRes
1969  *                                      Specifies that the 'TopTopic/topTopic'
1970  *                                      resource is missing from the database.
1971  *                      CEErrorMalloc
1972  *
1973  * Purpose:     Get the information to access the top level topic.
1974  *
1975  *****************************************************************************/
1976 int
1977 _DtHelpCeGetTopTopicId (
1978         _DtHelpVolumeHdl        volume,
1979         char            **ret_idString )
1980 {
1981     int   found = False;
1982     _DtHelpVolume vol = (_DtHelpVolume)volume;
1983
1984     _DtHelpProcessLock();
1985
1986     if (vol == NULL || ret_idString == NULL || CheckVolList(vol, NULL) == -1)
1987         errno = EINVAL;
1988     else
1989       {
1990         /*
1991          * What type of volume is it?
1992          */
1993         if (vol->sdl_flag == False)
1994             (void) _DtHelpCeGetCcdfTopTopic(vol, ret_idString);
1995         else
1996             *ret_idString = _DtHelpCeGetSdlHomeTopicId((_DtHelpVolumeHdl) vol);
1997
1998         if (*ret_idString != NULL)
1999             *ret_idString = strdup(*ret_idString);
2000
2001         if (*ret_idString != NULL)
2002             found = True;
2003       }
2004
2005     _DtHelpProcessUnlock();
2006     return found;
2007
2008 }  /* End _DtHelpCeGetTopTopicId */
2009
2010 /*****************************************************************************
2011  * Function: int _DtHelpCeFindId (_DtHelpVolume vol, char *target_id,
2012  *                                      int fd,
2013  *                                      char *ret_name, int *ret_offset)
2014  *
2015  * Parameters:  vol             Specifies the loaded volume
2016  *              target_id       Specifies target location ID
2017  *              fd              Specifies the locked file descriptor.
2018  *              ret_name        Returns a null terminated string
2019  *                              containing a fully qualified path to
2020  *                              the file that contains 'target_id'.
2021  *              ret_offset      Returns the offset into 'ret_name'
2022  *                              to the topic that contains 'target_id'.
2023  *
2024  * Memory own by caller:
2025  *              ret_name
2026  *
2027  * Returns:     True if successful, False if a failure occurs
2028  *
2029  * errno Values:        EINVAL          Specifies an invalid parameter was
2030  *                                      used.
2031  *                      CEErrorMalloc
2032  *                      CEErrorMissingFilenameRes
2033  *                                      Specifies that the 'Filename/filename'
2034  *                                      resource for 'topic' does not exist.
2035  *                      CEErrorMissingFileposRes
2036  *                                      If the resource is not in the
2037  *                                      database or if the resource NULL.
2038  *                      CEErrorLocIdNotFound
2039  *                                      Specifies that 'locId' was not
2040  *                                      found.
2041  *
2042  * Purpose:     Find which topic contains a specified locationID.
2043  *
2044  *****************************************************************************/
2045 int
2046 _DtHelpCeFindId (
2047         _DtHelpVolumeHdl        volume,
2048         char            *target_id,
2049         int              fd,
2050         char            **ret_name,
2051         int             *ret_offset )
2052 {
2053     _DtHelpVolume vol = (_DtHelpVolume)volume;
2054     int result;
2055
2056     _DtHelpProcessLock();
2057
2058     /*
2059      * check the parameters
2060      */
2061     if (vol == NULL || target_id == NULL || ret_name == NULL ||
2062                         ret_offset == NULL || CheckVolList (vol, NULL) == -1)
2063       {
2064         errno = EINVAL;
2065         _DtHelpProcessUnlock();
2066         return False;
2067       }
2068
2069     /*
2070      * What type of volume is it?
2071      */
2072     if (vol->sdl_flag == False)
2073       {
2074         result = _DtHelpCeFindCcdfId(vol, target_id, ret_name, ret_offset);
2075       }
2076     else
2077       {
2078         result = _DtHelpCeFindSdlId(vol, target_id, fd, ret_name, ret_offset);
2079       }
2080
2081     _DtHelpProcessUnlock();
2082     return result;
2083
2084 }  /* End _DtHelpCeFindId */
2085
2086 /*****************************************************************************
2087  * Function: int _DtHelpCeGetKeywordList (_DtHelpVolume vol, char ***ret_keywords)
2088  *
2089  * Parameters:  vol             Specifies the loaded volume
2090  *              ret_keywords    Returns a NULL-terminated string array
2091  *                              containing the sorted list of keywords in the
2092  *                              volume.  This array is NOT owned by the caller
2093  *                              and should only be read or copied.
2094  *
2095  * Returns:     The count of keywords associated with the volume if successful.
2096  *              -1 if a failure occurs;
2097  *
2098  * errno Values:        EINVAL          Specifies an invalid parameter was
2099  *                                      used.
2100  *                      CEErrorMalloc
2101  *                      CEErrorIllegalDatabaseFile
2102  *                                      Specifies that the keyword file is
2103  *                                      invalid or corrupt.
2104  *                      CEErrorMissingKeywordsRes
2105  *                                      Specifies that the keyword file does
2106  *                                      not contain the 'Keywords/keywords'
2107  *                                      resource or the resource is NULL
2108  *
2109  * Purpose:     Get the list of keywords contained in a volume.
2110  *
2111  *****************************************************************************/
2112 int
2113 _DtHelpCeGetKeywordList (
2114         _DtHelpVolumeHdl        volume,
2115         char            ***ret_keywords )
2116 {
2117     int       nameCount = -1;
2118     _DtHelpVolume vol = (_DtHelpVolume)volume;
2119
2120     if (vol == NULL || ret_keywords == NULL || CheckVolList (vol, NULL) == -1)
2121         errno = EINVAL;
2122     else if (GetVolumeKeywords (vol, ret_keywords) == 0)
2123       {
2124         nameCount = 0;
2125         while (*ret_keywords && (*ret_keywords)[nameCount])
2126             nameCount++;
2127       }
2128
2129     return nameCount;
2130
2131 }  /* End _DtHelpCeGetKeywordList */
2132
2133 /*****************************************************************************
2134  * Function: int _DtHelpCeFindKeyword (_DtHelpVolume vol, char *target, char ***ret_ids)
2135  *
2136  * Parameters:  vol             Specifies the loaded volume
2137  *              target          The target keyword.
2138  *              ret_ids         Returns a null terminated list of location
2139  *                              ids associated with the target keyword.
2140  *
2141  * Returns:     The count of ids associated with the keyword if successful.
2142  *              -1 if a failure occurs;
2143  *
2144  * errno Values:        EINVAL          Specifies an invalid parameter was
2145  *                                      used.
2146  *                      CEErrorNoKeywordList
2147  *                                      Specifies that the volume does not
2148  *                                      have a keyword list.
2149  *                      CEErrorIllegalKeyword
2150  *                                      Specifies that 'target' was not
2151  *                                      found.
2152  *                      CEErrorMalloc
2153  *                      CEErrorIllegalDatabaseFile
2154  *                                      Specifies that the keyword file is
2155  *                                      invalid or corrupt.
2156  *                      CEErrorMissingKeywordsRes
2157  *                                      Specifies that the keyword file does
2158  *                                      not contain the 'Keywords/keywords'
2159  *                                      resource or the resource is NULL
2160  *
2161  * Purpose:     Get the list of location ids associated with a keyword
2162  *
2163  *****************************************************************************/
2164 int
2165 _DtHelpCeFindKeyword (
2166         _DtHelpVolumeHdl        volume,
2167         char            *target,
2168         char            ***ret_ids )
2169 {
2170     int       nameCount = -1;
2171     _DtHelpVolume vol = (_DtHelpVolume)volume;
2172
2173     if (vol == NULL || target == NULL || ret_ids == NULL ||
2174                                         CheckVolList (vol, NULL) == -1)
2175         errno = EINVAL;
2176     else if (GetKeywordTopics (vol, target, ret_ids) == 0)
2177       {
2178         nameCount = 0;
2179         while (*ret_ids && (*ret_ids)[nameCount])
2180             nameCount++;
2181       }
2182
2183     return nameCount;
2184
2185 }  /* End _DtHelpCeFindKeyword */
2186
2187 /*****************************************************************************
2188  * Function: int _DtHelpGetTopicTitle (
2189  *                                      _DtHelpVolumeHdl volume,
2190  *                                      char *id, char **ret_title)
2191  *
2192  * Parameters:  volume          Specifies the volume containing the id.
2193  *              id              Specifies the id for the topic desired.
2194  *              ret_title       Returns a null terminated string containing
2195  *                              the title.
2196  *
2197  * Memory own by caller:
2198  *              ret_title
2199  *
2200  * Returns:     0 if successful, -2 if didn't find the id,
2201  *              otherwise -1.
2202  *
2203  * errno Values:        EINVAL          Specifies an invalid parameter was
2204  *                                      used.
2205  *
2206  * Purpose:     Get the title of a topic.
2207  *
2208  *****************************************************************************/
2209 int
2210 _DtHelpGetTopicTitle (
2211     _DtHelpVolumeHdl          volume,
2212     char                 *id,
2213     char                **ret_title)
2214 {
2215     int            result;
2216     char          *abbrevTitle;
2217     _DtHelpVolume  vol = (_DtHelpVolume)volume;
2218
2219     if (volume == NULL || id == NULL ||
2220                         CheckVolList (vol, NULL) == -1 || ret_title == NULL)
2221       {
2222         errno = EINVAL;
2223         return -1;
2224       }
2225
2226     /*
2227      * Try to get the title via the <TOPIC> and <ABBREV> tags.
2228      */
2229     result = GetTopicTitleAndAbbrev(vol, id, ret_title, &abbrevTitle);
2230     if (result == 0)
2231       {
2232         /*
2233          * If we have a abbreviated title, return it instead.
2234          */
2235         if (abbrevTitle)
2236           {
2237             if (*ret_title)
2238                 free ((char *) *ret_title);
2239             *ret_title = abbrevTitle;
2240           }
2241       }
2242
2243     return result;
2244
2245 }  /* End _DtHelpGetTopicTitle */
2246
2247 /*****************************************************************************
2248  * Function: int _DtHelpCeMapTargetToId (_DtHelpVolume vol,
2249  *                                      char *target_id,
2250  *                                      char *ret_id)
2251  *
2252  * Parameters:  vol             Specifies the loaded volume
2253  *              target_id       Specifies target location ID
2254  *              ret_id          Returns the id containing the target_id.
2255  *                              This memory *IS NOT* owned by the caller.
2256  *
2257  * Returns:     0 if successful, -1 if a failure occurs
2258  *
2259  * Purpose:     Find which topic contains a specified locationID.
2260  *
2261  *****************************************************************************/
2262 int
2263 _DtHelpCeMapTargetToId (
2264         _DtHelpVolumeHdl        volume,
2265         char            *target_id,
2266         char            **ret_id)
2267 {
2268     _DtHelpVolume vol = (_DtHelpVolume)volume;
2269     int result;
2270
2271     _DtHelpProcessLock();
2272
2273     /*
2274      * check the parameters
2275      */
2276     if (vol == NULL || target_id == NULL || ret_id == NULL ||
2277                                         CheckVolList (vol, NULL) == -1)
2278       {
2279         errno = EINVAL;
2280         _DtHelpProcessUnlock();
2281         return -1;
2282       }
2283
2284     /*
2285      * What type of volume is it?
2286      */
2287     if (vol->sdl_flag == False)
2288       {
2289         result = _DtHelpCeMapCcdfTargetToId(vol, target_id, ret_id);
2290       }
2291     else
2292       {
2293         result = _DtHelpCeMapIdToSdlTopicId(vol, target_id, ret_id);
2294       }
2295
2296     _DtHelpProcessUnlock();
2297     return result;
2298
2299 }  /* End _DtHelpCeMapTargetToId */
2300
2301 /*****************************************************************************
2302  * Function: char * _DtHelpGetVolumeLocale (_DtHelpVolume vol)
2303  *
2304  * Parameters:  vol             Specifies the loaded volume
2305  *
2306  * Returns:     The pointer to the locale string if successful. Otherwise
2307  *              NULL.
2308  *
2309  * Purpose:     Get the locale of the specified volume.
2310  *              Returns the locale in a unix specific format
2311  *              - locale[_ter][.charset] - This memory is owned by
2312  *              the caller.
2313  *
2314  *****************************************************************************/
2315 char *
2316 _DtHelpGetVolumeLocale (
2317         _DtHelpVolumeHdl        volume)
2318 {
2319     _DtHelpVolume  vol = (_DtHelpVolume)volume;
2320     char *result;
2321
2322     _DtHelpProcessLock();
2323
2324     /*
2325      * check the parameters
2326      */
2327     if (vol == NULL || CheckVolList (vol, NULL) == -1)
2328       {
2329         errno = EINVAL;
2330         _DtHelpProcessUnlock();
2331         return NULL;
2332       }
2333
2334     /*
2335      * What type of volume is it?
2336      */
2337     if (vol->sdl_flag == False)
2338       {
2339         result = _DtHelpCeGetCcdfVolLocale(vol);
2340       }
2341     else
2342       {
2343         result = _DtHelpCeGetSdlVolumeLocale(vol);
2344       }
2345
2346     _DtHelpProcessUnlock();
2347     return result;
2348
2349 }  /* End _DtHelpGetVolumeLocale */
2350
2351 /*****************************************************************************
2352  * Function: int _DtHelpCeGetDocStamp (_DtHelpVolumeHdl volume, char **ret_doc,
2353  *                                              char **ret_time)
2354  *
2355  * Parameters:  volume          Specifies the loaded volume
2356  *              ret_doc         Returns the doc id.
2357  *              ret_time        Returns the time stamp.
2358  *
2359  * Memory:      Caller owns the string in ret_doc and ret_time.
2360  *
2361  * Returns:     0 if successful, -2 if the volume does not contain
2362  *              one or the other, -1 if any other failure.
2363  *
2364  * Purpose:     Get doc id and time stamp of a volume.
2365  *
2366  *****************************************************************************/
2367 int
2368 _DtHelpCeGetDocStamp (
2369     _DtHelpVolumeHdl    volume,
2370     char                **ret_doc,
2371     char                **ret_time)
2372 {
2373     _DtHelpVolume  vol = (_DtHelpVolume)volume;
2374     int result;
2375
2376     _DtHelpProcessLock();
2377
2378     /*
2379      * check the parameters
2380      */
2381     if (vol == NULL || CheckVolList (vol, NULL) == -1)
2382       {
2383         errno = EINVAL;
2384         _DtHelpProcessUnlock();
2385         return NULL;
2386       }
2387
2388     /*
2389      * What type of volume is it?
2390      */
2391     if (vol->sdl_flag == False)
2392       {
2393         result = _DtHelpCeGetCcdfDocStamp (vol, ret_doc, ret_time);
2394       }
2395     else
2396       {
2397         result = _DtHelpCeGetSdlDocStamp(vol, ret_doc, ret_time);
2398       }
2399
2400     _DtHelpProcessUnlock();
2401     return result;
2402
2403 }  /* End _DtHelpCeGetDocStamp */
2404
2405 /*****************************************************************************
2406  * Function: char * _DtHelpCeGetTopicChilren (_DtHelpVolumeHdl vol)
2407  *
2408  * Parameters:  vol             Specifies the loaded volume
2409  *              topic_id        Speicifes the topic id of which the
2410  *                              children are to be found.
2411  *              retTopics       Returns the null terminated array of
2412  *                              ids. This memory is owned by the caller
2413  *                              and must be freed.
2414  *
2415  * Returns:     > 0 if successful, -1 if failures.
2416  *
2417  * Purpose:     Get the children of a topic.
2418  *
2419  *****************************************************************************/
2420 int 
2421 _DtHelpCeGetTopicChildren (
2422     _DtHelpVolumeHdl   volume,
2423     char          *topic_id,
2424     char        ***retTopics)
2425 {
2426     _DtHelpVolume  vol = (_DtHelpVolume)volume;
2427     int result;
2428
2429     _DtHelpProcessLock();
2430
2431     /*
2432      * check the parameters
2433      */
2434     if (vol == NULL || CheckVolList (vol, NULL) == -1)
2435       {
2436         errno = EINVAL;
2437         _DtHelpProcessUnlock();
2438         return -1;
2439       }
2440
2441     /*
2442      * What type of volume is it?
2443      */
2444     if (vol->sdl_flag == False)
2445       {
2446         result = _DtHelpCeGetCcdfTopicChildren(volume, topic_id, retTopics);
2447       }
2448     else
2449       {
2450         result = _DtHelpCeGetSdlTopicChildren(volume, topic_id, retTopics);
2451       }
2452
2453     _DtHelpProcessUnlock();
2454     return result;
2455
2456 }  /* End _DtHelpCeGetTopicChildren */
2457
2458 /*****************************************************************************
2459  * Function: int _DtHelpCeGetVolumeFlag (_DtHelpVolumeHdl vol)
2460  *
2461  * Parameters:  vol             Specifies the loaded volume
2462  *
2463  * Returns:     0 if CCDF volume, 1 if SDL, -1 if failures.
2464  *
2465  * Purpose:     Determine the type of volume.
2466  *
2467  *****************************************************************************/
2468 int 
2469 _DtHelpCeGetVolumeFlag (
2470     _DtHelpVolumeHdl   volume)
2471 {
2472     _DtHelpVolume  vol = (_DtHelpVolume)volume;
2473
2474     /*
2475      * check the parameters
2476      */
2477     if (vol == NULL || CheckVolList (vol, NULL) == -1)
2478       {
2479         errno = EINVAL;
2480         return -1;
2481       }
2482
2483     /*
2484      * What type of volume is it?
2485      */
2486     return((int) vol->sdl_flag);
2487
2488 }  /* End _DtHelpCeGetVolumeFlag */
2489
2490 /*****************************************************************************
2491  * Function: int _DtHelpCeLockVolume (_DtHelpVolumeHdl vol)
2492  *
2493  * Parameters:  vol             Specifies the loaded volume
2494  *
2495  * Returns:     > 0 if successful, -1 if failures.
2496  *
2497  * Purpose:     Lock the volume so that it can't get change out from
2498  *              under the caller.
2499  *
2500  *****************************************************************************/
2501 int 
2502 _DtHelpCeLockVolume (
2503     _DtHelpVolumeHdl     volume,
2504     _DtHelpCeLockInfo   *lock_info)
2505 {
2506     struct stat    buf;
2507     _DtHelpVolume  vol = (_DtHelpVolume)volume;
2508
2509     _DtHelpProcessLock();
2510
2511     /*
2512      * check the parameters
2513      */
2514     if (vol == NULL || CheckVolList (vol, NULL) == -1)
2515       {
2516         _DtHelpProcessUnlock();
2517         return -1;
2518       }
2519
2520     /*
2521      * lock it by opening it.
2522      */
2523     lock_info->fd = open(vol->volFile, O_RDONLY);
2524     if (lock_info->fd == -1)
2525       {
2526         _DtHelpProcessUnlock();
2527         return -1;
2528       }
2529
2530     (void) fstat(lock_info->fd, &buf);
2531     if (buf.st_mtime != vol->check_time)
2532       {
2533         if (RereadVolume(vol) != 0)
2534           {
2535             close(lock_info->fd);
2536             _DtHelpProcessUnlock();
2537             return -1;
2538           }
2539
2540         vol->check_time = buf.st_mtime;
2541       }
2542
2543     /*
2544      * Synthetic open
2545      */
2546     vol->openCount++;
2547     lock_info->volume = volume;
2548     _DtHelpProcessUnlock();
2549     return 0;
2550
2551 }  /* End _DtHelpCeLockVolume */
2552
2553 /*****************************************************************************
2554  * Function: int _DtHelpCeUnlockVolume (_DtHelpVolumeHdl vol)
2555  *
2556  * Parameters:  vol             Specifies the loaded volume
2557  *
2558  * Returns:     > 0 if successful, -1 if failures.
2559  *
2560  * Purpose:     Unlock the volume.
2561  *
2562  *****************************************************************************/
2563 int 
2564 _DtHelpCeUnlockVolume (
2565     _DtHelpCeLockInfo   lock_info)
2566 {
2567     _DtHelpVolume  vol = (_DtHelpVolume)(lock_info.volume);
2568
2569     _DtHelpProcessLock();
2570
2571     /*
2572      * check the parameters
2573      */
2574     if (vol == NULL || CheckVolList (vol, NULL) == -1)
2575       {
2576         _DtHelpProcessUnlock();
2577         return -1;
2578       }
2579
2580     /*
2581      * check to see if it needs to be unlocked.
2582      */
2583     if (lock_info.fd != -1)
2584         close(lock_info.fd);
2585
2586     /*
2587      * Synthetic close
2588      */
2589     vol->openCount--;
2590     _DtHelpProcessUnlock();
2591     return 0;
2592
2593 }  /* End _DtHelpCeUnlockVolume */
2594
2595 /*****************************************************************************
2596  * Function: int _DtHelpCeIsTopTopic (_DtHelpVolumeHdl volume, const char *id)
2597  *
2598  * Parameters:  vol             Specifies the loaded volume
2599  *              id              Specifies a location id.
2600  *
2601  * Returns:     = 0 if successful, != 0 if failures.
2602  *
2603  * Purpose:     Tests to see if the location id is in the top topic of
2604  *              the volume.
2605  *
2606  *****************************************************************************/
2607 int 
2608 _DtHelpCeIsTopTopic (
2609     _DtHelpVolumeHdl     volume,
2610     const char          *id)
2611 {
2612     int            result  = -1;
2613     char          *topicId = NULL;
2614     char          *topId   = NULL;
2615     _DtHelpVolume  vol = (_DtHelpVolume) volume;
2616
2617     _DtHelpProcessLock();
2618     /*
2619      * check the parameters
2620      */
2621     if (vol == NULL || CheckVolList (vol, NULL) == -1)
2622       {
2623         _DtHelpProcessUnlock();
2624         return -1;
2625       }
2626
2627     /*
2628      * What type of volume is it?
2629      */
2630     if (vol->sdl_flag == False)
2631       {
2632         if (_DtHelpCeMapCcdfTargetToId(vol, id, &topicId) == 0 &&
2633                                 _DtHelpCeGetCcdfTopTopic(vol, &topId) == 0)
2634             result = _DtHelpCeStrCaseCmpLatin1(topId, topicId);
2635       }
2636     else if (_DtHelpCeMapIdToSdlTopicId(vol, id, &topicId) == 0)
2637       {
2638         topId = _DtHelpCeGetSdlHomeTopicId(volume);
2639         if (topId != NULL)
2640             result = _DtHelpCeStrCaseCmpLatin1(topId, topicId);
2641       }
2642
2643     _DtHelpProcessUnlock();
2644     return result;
2645
2646 }  /* End _DtHelpCeIsTopTopic */