/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* $XConsortium: AccessCCDF.c /main/10 1996/11/01 10:09:50 drk $ */ /************************************<+>************************************* **************************************************************************** ** ** File: AccessCCDF.c ** ** Project: Cde 1.0 Help Library ** ** Description: This body of code handles the access routines to the ** legacy CCDF Help files. ** ** ** (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 Hewlett-Packard Company ** ** (c) Copyright 1993, 1994 Hewlett-Packard Company ** (c) Copyright 1993, 1994 International Business Machines Corp. ** (c) Copyright 1993, 1994 Sun Microsystems, Inc. ** (c) Copyright 1993, 1994 Novell, Inc. ** ** **************************************************************************** ************************************<+>*************************************/ /* * system includes */ #include #include #include #include #include #include #include #include #ifdef X_NOT_STDC_ENV extern int errno; #endif extern char *_DtHelpGetLocale(void); /* * Canvas Engine includes */ #include "CanvasP.h" #include "CanvasSegP.h" /* * private includes */ #include "CanvasError.h" #include "AccessP.h" #include "bufioI.h" #include "AccessCCDFP.h" #include "AccessCCDFI.h" #include "FormatUtilI.h" #include "StringFuncsI.h" #include "HelpXlate.h" #ifdef NLS16 #endif /******** Private Defines ********/ #define LIST_INCREMENT 10 #define BUFF_SIZE 256 /******** End Private Defines ********/ /******** Private Function Declarations ********/ static char *CreateFileName( char *path, char *string ); static int GetResourceInt ( XrmDatabase db, char *topic, char *resClass, char *resName, int *ret_value); static char *GetResourceString ( XrmDatabase db, char *topic, char *resClass, char *resName); static char **GetResourceStringArray ( XrmDatabase db, char *topic, char *resClass, char *resName); static int GetTopicMap ( _DtHelpVolume vol, char *target_id, int level, char ***ret_ids ); /******** End Private Function Declarations ********/ /******** Private Macro Declarations ********/ #define GetCcdfVolumePtr(vol) \ ((CcdfVolumePtr)((vol)->vols.ccdf_vol)) #define GetFilenameResource(vol, topic) \ GetResourceString((vol)->volDb, topic, "Filename", "filename") /******** End Private Macro Declarations ********/ /****************************************************************************** * * Private variables used within this file. * *******************************************************************************/ static const char *Period = "."; static const char *Slash = "/"; static const char *VolumeStr = "Volume."; static const char *volumeStr = "Volume."; static const struct _CcdfVolumeInfo DefaultCcdfVol = { NULL, /* XrmDatabase volDb; */ NULL, /* char **topicList; */ NULL, /* char *keywordFile; */ }; /****************************************************************************** * Private Functions ******************************************************************************/ /****************************************************************************** * Function: CreateFileName (char *path, char *string) * * Parameters: path Specifies the path to the volume. * string Specifies the file's name including the extension. * * Memory owned by caller: * ptr returned * * Returns: Non null ptr if successful, null if a failure occurs. * * errno Values: CEErrorMalloc * * Purpose: Creates a fully qualified path to a file based on the * path given. * ******************************************************************************/ static char * CreateFileName ( char *path, char *string ) { int len = 0; char *ptr; if ((MB_CUR_MAX == 1 || mblen (string, MB_CUR_MAX) == 1) && *string != '/' && path) { /* * determine the length of the path. */ _DtHelpCeStrrchr (path, Slash, MB_CUR_MAX, &ptr); if (ptr) len = ptr - path + 1; } /* * malloc the room for the path and file name. */ ptr = (char *) malloc (len + strlen(string) + 1); if (ptr) { /* * copy the name into the destination string. */ ptr[0] = '\0'; if (len && path) strncat (ptr, path, len); strcat (ptr, string); } return ptr; } /***************************************************************************** * Function: GetTopicMap (_DtHelpVolume vol, char *target_id, * int level, char ***ret_ids) * * Parameters: vol Specifies the loaded volume * target_id The target location ID of a topic. * level The levels progressed so far. It is * used to calculate how much memory * is required to hold all the ids. * ret_ids Returns a null terminated list of the * location IDs of the topics between the * target and the parent. * Memory own by caller: * ret_ids * * Returns: The number of ids put in the list so far, * -1 for failure. * * errno Values: CEErrorMalloc * * * Purpose: Recursively build a list of id strings containing all of * the target_id's ancestors. * *****************************************************************************/ static int GetTopicMap ( _DtHelpVolume vol, char *target_id, int level, char ***ret_ids ) { int result = -1; char *idParent; if (_DtHelpCeGetCcdfTopicParent (vol, target_id, &idParent) == 0) { /* * still not at the top */ if (idParent) { result = GetTopicMap (vol, idParent, level + 1, ret_ids); if (result != -1) { (*ret_ids)[result] = strdup (idParent); result++; } } else { *ret_ids = (char **) malloc (sizeof(char *) * (level + 2)); if ((*ret_ids) == NULL) return -1; (*ret_ids)[level + 1] = NULL; result = 0; } } return result; } /* End GetTopicMap */ /****************************************************************************** * Function: char *GetResourceString (XrmDatabase db, char *topic, * char *resClass, char *resName) * * Parameters: db Specifies the handle to a resource database. * topic Specifies the topic whose resource value is * desired. If 'topic' is NULL, the * desired resource is for the volume and * not a specific topic. * resClass Specifies the resource class name. * resName Specifies the resource name. * * Return Value: Returns the desired resource as string. * This string is NOT owned by the caller and * should only be read or copied. * * Returns NULL if an error occurs. * * errno Values: CEErrorMalloc * CEErrorIllegalResource If the resource is not in * the database or if the * resource NULL * * Purpose: Get a resource value for a volume or topic. * ******************************************************************************/ static char * GetResourceString ( XrmDatabase db, char *topic, char *resClass, char *resName) { int len; int topicLen = 0; char *retVal = NULL; char *fullResName; char *fullResClass; char *resType; XrmValue resValue; if (topic != NULL) topicLen = strlen(topic) + strlen(Period); len = strlen(volumeStr) + topicLen + 1; fullResName = (char *) malloc (len + strlen (resName)); fullResClass = (char *) malloc (len + strlen (resClass)); if (fullResName != NULL && fullResClass != NULL) { strcpy (fullResName, volumeStr); strcpy (fullResClass, VolumeStr); if (topic != NULL) { strcat (fullResName, topic); strcat (fullResName, Period); strcat (fullResClass, topic); strcat (fullResClass, Period); } strcat (fullResName , resName); strcat (fullResClass, resClass); } else errno = CEErrorMalloc; if (fullResClass != NULL && fullResName != NULL) { if (XrmGetResource (db, fullResClass, fullResName, &resType, &resValue) && strlen ((char *) resValue.addr)) retVal = (char *) resValue.addr; else errno = CEErrorIllegalResource; } if (fullResName) free (fullResName); if (fullResClass) free (fullResClass); return (retVal); } /****************************************************************************** * Function: char **GetResourceStringArray (XrmDatabase db, char *topic, * char *resClass, char *resName) * * Parameters: db Specifies the handle to a resource database. * topic Specifies the topic whose resource value is * desired. If 'topic' is NULL, the * desired resource is for the volume and * not a specific topic. * resClass Specifies the resource class name. * resName Specifies the resource name. * * Return Value: Returns a NULL-terminated string array containing the * value of the desired resource. The elements of the * array are the strings of non-whitespace characters in * the resource value. This array is owned by the caller * and should be freed (using _DtHelpCeFreeStringArray) when * not needed. * * Purpose: Get am array-valued resource for a volume or topic. * ******************************************************************************/ static char ** GetResourceStringArray ( XrmDatabase db, char *topic, char *resClass, char *resName) { char *val; char **valArray = NULL; char *token; char *nextC; /* Get the resource value which is a single string where the elements are separated by white space. */ val = GetResourceString (db, topic, resClass, resName); if (val != NULL) { nextC = val; while (nextC && *nextC != '\0') { nextC = _DtHelpGetNxtToken (nextC, &token); if (token == NULL) { _DtHelpCeFreeStringArray (valArray); return NULL; } /* If the token is a '\0' then we are at the end and we can quit. If the token is a '\n', then ignore it. Otherwise the token is an element of the array we are building. */ if (*token == '\0') break; if (*token != '\n') { valArray = (char **) _DtHelpCeAddPtrToArray ( (void **) valArray, (void *) token); /* * If we malloc'ed ourselves out of existence...stop processing. */ if (!valArray) break; } } } return (valArray); } /****************************************************************************** * Function: char *GetNextKeyword (char *str, char *delimiter, char **ret_token) * * Parameters: str Specifies the string which is being parsed. * delimiter Specifies the delimiter string. * ret_token Returns the string found between the current * position of the input string and the delimiter * string. * * Newline or Null strings are * not owned by the caller. * * Otherwise, the memory for the returned * token is owned by the caller. * * Return Value: Returns a pointer to the next unparsed character * in the input string. A NULL value indicates an error. * * Purpose: Load the keywords associated with a volume. * ******************************************************************************/ static char * GetNextKeyword ( char *str, char *delimiter, char **ret_token ) { int len; char *start; char *token; short done; /* Find the next token in the string. The parsing rules are: - The deliminater (except for \n) separate a keyword from its list of location IDs. - \n is a token itself. - The \0 at the end of the string is a token. */ /* Skip all of the whitespace and \n. */ (void) _DtHelpCeStrspn (str, " \n", MB_CUR_MAX, &len); str += len; /* Str is pointing at the start of the next keyword. Depending on the type of token, malloc the memory and copy the token value. */ if (*str == '\0') token = str; else { /* We have some non-whitespace characters. Find the end of */ /* them and copy them into new memory. */ start = str; done = False; do { _DtHelpCeStrchr (str, delimiter, MB_CUR_MAX, &str); if (str) { if (strncmp (str, delimiter, strlen(delimiter)) == 0) done = True; else str++; } else /* if (str == NULL) */ { str = start + strlen (start); done = -1; } } while (!done); token = (char *) malloc ((str - start + 1) * sizeof (char)); if (token) { strncpy (token, start, str - start); *(token + (str - start)) = '\0'; if (done == True) str += strlen (delimiter); } } *ret_token = token; return (str); } /****************************************************************************** * Function: int TopicFilename (_DtHelpVolume vol, char *topic, * char **retFname); * * Parameters: vol Specifies the loaded volume * topic Specifies locationID for the topic * retFname Returns the name of the file where the topic * is located. * Memory own by caller: * retFname * * Returns: 0 if successful, -1 if a failure occurs * * Purpose: Get the name of the file where a topic is stored. * ******************************************************************************/ static int TopicFilename ( _DtHelpVolume vol, char *topic, char **retFname) { CcdfVolumePtr ccdfVol = GetCcdfVolumePtr(vol); *retFname = GetFilenameResource (ccdfVol, topic); if (*retFname == NULL && errno == CEErrorIllegalResource) errno = CEErrorMissingFilenameRes; else *retFname = CreateFileName (vol->volFile, *retFname); if (*retFname == NULL) return (-1); return (0); } /****************************************************************************** * Function: int TopicFilepos (_DtHelpVolume vol, char *topic, int *retFpos); * * Parameters: vol Specifies the loaded volume * topic The locationID for the topic * retFpos Returns the byte offset of the start of the * topic within the topic file. * * Return Value: 0 if successful, -1 if a failure occurs. * * Purpose: Determine the position of the topic within the topic file. ******************************************************************************/ static int TopicFilepos ( _DtHelpVolume vol, char *topic, int *retFpos) { CcdfVolumePtr ccdfVol = GetCcdfVolumePtr(vol); if (GetResourceInt(ccdfVol->volDb, topic, "Filepos", "filepos", retFpos) == -1) return -1; return (0); } /****************************************************************************** * Function: int GetResourceInt (XrmDatabase db, char *topic, * char *resClass, char *resName, ret_value) * * Parameters: db Specifies the handle to a resource database. * topic Specifies the topic whose resource value is * desired. If 'topic' is NULL, the * desired resource is for the volume and * not a specific topic. * resClass Specifies the resource class name. * resName Specifies the resource name. * ret_value Returns an int containing the resource value. * * Return Value: 0 if successful, -1 if a failure occurs. * * Purpose: Get an integer-valued resource for a volume or topic. ******************************************************************************/ static int GetResourceInt ( XrmDatabase db, char *topic, char *resClass, char *resName, int *ret_value) { char *retValue; retValue = GetResourceString (db, topic, resClass, resName); if (retValue) { *ret_value = atoi(retValue); return 0; } return -1; } /****************************************************************************** * Function: static int LocationIDTopic (_DtHelpVolume vol, char *locId, * char **retTopic); * * Parameters: vol Specifies the loaded volume * locId Specifies locationID desired. * retTopic Returns the locationID of the topic with * contains 'locId'. This string IS owned by * the caller and must be freed when no longer * needed. * * Return Value: 0 if successful, -1 if a failure occurs * * Purpose: Find which topic contains a specified locationID. ******************************************************************************/ static int LocationIDTopic ( _DtHelpVolume vol, char *locId, char **retTopic) { char **allTopics; char **nextTopic; char **locIdList = NULL; char **nextLocId; CcdfVolumePtr ccdfVol = GetCcdfVolumePtr(vol); *retTopic = NULL; if (_DtHelpCeGetCcdfVolIdList (vol, &allTopics) != 0) return (-1); /* Check whether the locationID is a topic. */ for (nextTopic = allTopics; nextTopic && *nextTopic != NULL && *retTopic == NULL; nextTopic++) { if (_DtHelpCeStrCaseCmpLatin1 (locId, *nextTopic) == 0) *retTopic = strdup(*nextTopic); } /* For each topic in the volume, get its list of locationIDs and check them. NOTE: This code should be separated out into a public _DtTopicLocationIDs function. Then we would have a function that returns all of the locationIDs in a topic, which might prove useful someday. */ if (*retTopic == NULL) { for (nextTopic = allTopics; nextTopic && *nextTopic != NULL && *retTopic == NULL; nextTopic++) { /* * valid to get a NULL on this resource, but not good */ errno = 0; locIdList = GetResourceStringArray (ccdfVol->volDb, *nextTopic, "LocationIDs", "locationIDs"); if (locIdList == NULL && errno != CEErrorIllegalResource) break; errno = CEErrorLocIdNotFound; for (nextLocId = locIdList; nextLocId != NULL && *nextLocId != NULL && *retTopic == NULL; nextLocId++) { if (_DtHelpCeStrCaseCmpLatin1 (locId, *nextLocId) == 0) *retTopic = strdup(*nextTopic); } if (NULL != locIdList) _DtHelpCeFreeStringArray (locIdList); } } if (*retTopic == NULL) return (-1); return (0); } /****************************************************************************** * Semi-Public CCDF Access Functions ******************************************************************************/ /***************************************************************************** * Function: int _DtHelpCeGetCcdfIdPath (_DtHelpVolume vol, char *target_id, * char ***ret_ids) * * Parameters: vol Specifies the loaded volume * target_id The target location ID of a topic. * ret_ids Returns a null terminated list of the * location IDs of the topics between the * target and the parent. * Memory own by caller: * ret_ids * * Returns: The number of ids in the list, -1 for failure. If successful, * the list will always contain at least the target_id. * * Purpose: Get the list of ids between the top and the target id. * *****************************************************************************/ int _DtHelpCeGetCcdfIdPath ( _DtHelpVolume vol, char *target_id, char ***ret_ids ) { int idCount = 0; char *topicId = NULL; if (LocationIDTopic (vol, target_id, &topicId) != 0) return -1; idCount = GetTopicMap (vol, topicId, 0, ret_ids); if (idCount != -1) { /* * include this entry in the count */ (*ret_ids)[idCount] = topicId; idCount++; } else { free(topicId); } return idCount; } /* End _DtHelpCeGetCcdfIdPath */ /****************************************************************************** * Function: int _DtHelpCeGetCcdfTopicChildren (_DtHelpVolume vol, * char *topic_id, * char ***topics); * * Parameters: vol Specifies the loaded volume. * topic_id Specifies the topic for which children * are desired. * retTopics Returns a NULL-terminated string array * containing the list of children for a topic * in the volume. This array is owned by the * caller and should be freed (using * _DtHelpCeFreeStringArray) when not needed. * * Memory own by caller: * retTopics * * Return Value: > 0 if successful, -1 if a failure occurs * * Purpose: Get the list of children for a topic contained in a volume. * ******************************************************************************/ int _DtHelpCeGetCcdfTopicChildren ( _DtHelpVolume vol, char *topic_id, char ***retTopics) { int result; int count = 0; char *parent_id; char *child_id; char *topicId = NULL; char **topicList; if (LocationIDTopic (vol, topic_id, &topicId) != 0) return -1; /* * initialize the return value */ *retTopics = NULL; /* * get the list */ result = _DtHelpCeGetCcdfVolIdList (vol, &topicList); if (result == 0) { while (*topicList && result == 0) { result = _DtHelpCeGetCcdfTopicParent (vol, *topicList, &parent_id); if (result == 0) { /* * It's legal to get a NULL back - means the topic * doesn't have a parent. */ if (parent_id && _DtHelpCeStrCaseCmpLatin1 (parent_id, topicId) == 0) { child_id = strdup (*topicList); if (child_id) { *retTopics = (char **) _DtHelpCeAddPtrToArray ( (void **) (*retTopics), child_id); if (*retTopics == NULL) result = -1; count++; } else { /* * lost the previous data...stop processing. */ if (*retTopics) _DtHelpCeFreeStringArray (*retTopics); *retTopics = NULL; result = -1; break; } } } else { /* * problems processing TopicParent...stop processing */ if (*retTopics) _DtHelpCeFreeStringArray (*retTopics); *retTopics = NULL; break; } topicList++; } } /* * free the duplicate string */ if (topicId) free (topicId); if (result != 0) return -1; return count; } /****************************************************************************** * Function: int _DtHelpCeGetCcdfVolIdList (_DtHelpVolume vol, char ***topics); * * Parameters: vol Specifies the loaded volume. * topics Returns a NULL-terminated string array * containing the ordered list of topics in the * volume. This array is NOT owned by the caller * and should only be read or copied. * * Return Value: 0 if successful, -1 if a failure occurs * * Purpose: Get the list of topics contained in a volume. * ******************************************************************************/ int _DtHelpCeGetCcdfVolIdList ( _DtHelpVolume vol, char ***retTopics) { CcdfVolumePtr ccdfVol = GetCcdfVolumePtr(vol); if (ccdfVol->topicList == NULL) ccdfVol->topicList = GetResourceStringArray (ccdfVol->volDb, NULL, "TopicList", "topicList"); *retTopics = ccdfVol->topicList; if (*retTopics == NULL) { if (errno == CEErrorIllegalResource) errno = CEErrorMissingTopicList; return -1; } return 0; } /***************************************************************************** * Function: int _DtHelpCeFindCcdfId (_DtHelpVolume vol, char *target_id, * char *ret_name, int *ret_offset) * * Parameters: vol Specifies the loaded volume * target_id Specifies target location ID * ret_name Returns a null terminated string * containing a fully qualified path to * the file that contains 'target_id'. * ret_offset Returns the offset into 'ret_name' * to the topic that contains 'target_id'. * * Memory own by caller: * ret_name * * Returns: True if successful, False if a failure occurs * * Purpose: Find which topic contains a specified locationID. *****************************************************************************/ int _DtHelpCeFindCcdfId ( _DtHelpVolume vol, char *target_id, char **ret_name, int *ret_offset ) { char newTarget[65]; char *topicId = NULL; int found = False; snprintf(newTarget, sizeof(newTarget), "%s", target_id); _DtHelpCeUpperCase(newTarget); /* * find the location id for the topic that contains the * target_id (they may be the same). Then find the filename * and offset. */ if (TopicFilename (vol, newTarget, ret_name) == -1) { /* * if the reason TopicFilename failed was because we couldn't * find a resource, try looking for it in the LocationIDs. */ if (errno == CEErrorMissingFilenameRes && LocationIDTopic (vol, newTarget, &topicId) == 0 && TopicFilename (vol, topicId, ret_name) == 0 && TopicFilepos (vol, topicId, ret_offset) == 0) found = True; } else if (TopicFilepos (vol, newTarget, ret_offset) != -1) found = True; /* * free the excess strings */ if (topicId) free (topicId); return found; } /* End _DtHelpCeFindCcdfId */ /****************************************************************************** * Function: int _DtHelpCeGetCcdfTopicParent (_DtHelpVolume vol, char *topic, * char **retParent) * * Parameters: vol Specifies the loaded volume * topic Specifies locationID for the topic * retParent Returns a string with the locationID for the * topic which is the parent of the current * topic. If the current topic is at the top of * the hierarchy, a NULL string is returned. * This string is NOT owned by the caller and * should only be read or copied. * * Return Value: 0 if successful, -1 if a failure occurs * * Purpose: Find the parent for a topic. ******************************************************************************/ int _DtHelpCeGetCcdfTopicParent ( _DtHelpVolume vol, char *topic, char **retParent) { CcdfVolumePtr ccdfVol = GetCcdfVolumePtr(vol); /* Don't return an error if we are asked for the parent of NULL, or if the topic has no parent. Both cases are valid (and used by _DtTopicPath). */ *retParent = NULL; if (topic != NULL) { errno = 0; *retParent = GetResourceString(ccdfVol->volDb, topic, "Parent", "parent"); if (*retParent == NULL && errno != CEErrorIllegalResource) return -1; } return (0); } /***************************************************************************** * Function: int _DtHelpCeGetCcdfKeywordList (_DtHelpVolume vol, * * Parameters: vol Specifies the volume whose keywords need to be * loaded from disk. Once loaded, they can be * accessed through the fields of the volume structure. * * Return Value: 0 if successful, -1 if a failure occurs * * Purpose: Load the keywords associated with a volume. *****************************************************************************/ int _DtHelpCeGetCcdfKeywordList ( _DtHelpVolume vol) { XrmDatabase kDb; char *keywordString; char *nextC; char **topics; char ***topicList; char *token; char *currKeyword; CcdfVolumePtr ccdfVol = GetCcdfVolumePtr(vol); /* Generate the name of the keyword file. Because volume files use the ".hv" suffix and keyword files use ".hvk", all we have to do is append a "k". */ /* * If the keywordFile is non-null, we've already tried once. * We want to try again, because the problem may have been * fixed without restarting this process. * * But don't malloc memory again, because we'll leak memory. * Just use what is given. */ if (ccdfVol->keywordFile == NULL) { ccdfVol->keywordFile = (char *) malloc (strlen (vol->volFile) + 2); if (ccdfVol->keywordFile == NULL) return -1; strcpy (ccdfVol->keywordFile, vol->volFile); strcat (ccdfVol->keywordFile, "k"); } /* * check to see if it exists */ if (access (ccdfVol->keywordFile, R_OK) == -1) return -1; /* Load the keyword file and get the "keywords" resource. */ kDb = XrmGetFileDatabase (ccdfVol->keywordFile); if (kDb == NULL) return -1; keywordString = GetResourceString (kDb, NULL, "Keywords", "keywords"); if (keywordString == NULL) { if (errno == CEErrorIllegalResource) errno = CEErrorMissingKeywordsRes; XrmDestroyDatabase (kDb); return (-1); } /* Now parse the string into the appropriate arrays. The string has the following syntax: keyword1<\>topic topic topic ... \n keyword2<\>topic topic topic ... \n */ nextC = (char *) keywordString; while (nextC && *nextC) { /* Get the next keyword. If we find newlines while looking for the keyword, throw them away. If the next token is the end- of-file (\0), quit. */ nextC = GetNextKeyword (nextC, "<\\>", &token); if (token == NULL) { XrmDestroyDatabase (kDb); if (vol->keywords) { _DtHelpCeFreeStringArray (vol->keywords); vol->keywords = NULL; } if (vol->keywordTopics) { for (topicList = vol->keywordTopics; topicList; topicList++) _DtHelpCeFreeStringArray (*topicList); free (vol->keywordTopics); vol->keywordTopics = NULL; } return -1; } if (*token == '\0') break; /* We have the next keyword. Hang onto it and add it to the list once we get the array of topics. We don't add it yet because if there are no topics we want to throw it away. (Silently ignoring keywords which specify no topics is an undocumented feature.) */ currKeyword = token; /* Now get the list of topics. */ topics = NULL; do { nextC = _DtHelpGetNxtToken (nextC, &token); if (token == NULL) { XrmDestroyDatabase (kDb); if (vol->keywords) { _DtHelpCeFreeStringArray (vol->keywords); vol->keywords = NULL; } if (vol->keywordTopics) { for (topicList = vol->keywordTopics; topicList; topicList++) _DtHelpCeFreeStringArray (*topicList); free (vol->keywordTopics); vol->keywordTopics = NULL; } if (topics) _DtHelpCeFreeStringArray (topics); free (currKeyword); return -1; } /* If the next token is end-of-file (\0), then quit. Otherwise if the next token is a newline, then we have gotten all of the topics and we need to add them to the array of topic arrays. The final choice is that the token is a string so we add it to the current array of topics. */ if (*token == '\0') break; if (*token == '\n') { /* We have all of the topics. If the array of topics isn't empty, add everything to the data structures. */ if (topics != NULL) { vol->keywords = (char **) _DtHelpCeAddPtrToArray ( (void **) vol->keywords, (void *) currKeyword); vol->keywordTopics = (char ***) _DtHelpCeAddPtrToArray ( (void **) vol->keywordTopics, (void *) topics); /* * If we just malloc'ed ourselves out of existence... * stop here. */ if (vol->keywords == 0 || vol->keywordTopics == 0) { XrmDestroyDatabase (kDb); if (vol->keywords) { free (currKeyword); _DtHelpCeFreeStringArray (vol->keywords); _DtHelpCeFreeStringArray (topics); vol->keywords = NULL; } if (vol->keywordTopics) { for (topicList = vol->keywordTopics; topicList; topicList++) _DtHelpCeFreeStringArray (*topicList); free (vol->keywordTopics); vol->keywordTopics = NULL; } return -1; } } break; } else { topics = (char **) _DtHelpCeAddPtrToArray ((void **) topics, (void *) token); /* * If we just malloc'ed ourselves out of existence * stop here. */ if (topics == NULL) { free (currKeyword); XrmDestroyDatabase (kDb); if (vol->keywords) { _DtHelpCeFreeStringArray (vol->keywords); vol->keywords = NULL; } if (vol->keywordTopics != NULL) { for (topicList = vol->keywordTopics; topicList; topicList++) _DtHelpCeFreeStringArray (*topicList); free (vol->keywordTopics); vol->keywordTopics = NULL; } return -1; } } } while (nextC && *nextC); if (topics == NULL) free (currKeyword); } XrmDestroyDatabase (kDb); return (0); } /* End _DtHelpCeGetCcdfKeywordList */ /****************************************************************************** * Function: int _DtHelpCeGetCcdfVolumeAbstract (_DtHelpVolume vol, * char **abstract); * * Parameters: vol Specifies the loaded volume. * abstract Returns the abstract of the volume. This string * is owned by the caller and should be freed. * * Return Value: 0 if successful, -1 if a failure occurs * * Purpose: Get the abstract of a volume. ******************************************************************************/ int _DtHelpCeGetCcdfVolumeAbstract ( _DtHelpVolume vol, char **retAbs) { char *abstract; CcdfVolumePtr ccdfVol = GetCcdfVolumePtr(vol); *retAbs = NULL; abstract = GetResourceString(ccdfVol->volDb, NULL, "Abstract", "abstract"); if (abstract == NULL) { if (errno == CEErrorIllegalResource) errno = CEErrorMissingAbstractRes; } else *retAbs = strdup(abstract); if (*retAbs == NULL) return (-1); return (0); } /***************************************************************************** * Function: int _DtHelpCeMapCcdfTargetToId (_DtHelpVolume vol, * char *target_id, * char *ret_id) * * Parameters: vol Specifies the loaded volume * target_id Specifies target location ID * ret_id Returns the id containing the target_id. * This memory *IS NOT* owned by the caller. * And *MAY* point to target_id. * * Returns: 0 if successful, -1 if a failure occurs * * Purpose: Find which topic contains a specified locationID. * *****************************************************************************/ int _DtHelpCeMapCcdfTargetToId ( _DtHelpVolume vol, const char *target_id, char **ret_id) { char newTarget[128]; CcdfVolumePtr ccdfVol = GetCcdfVolumePtr(vol); snprintf(newTarget, sizeof(newTarget), "%s", target_id); _DtHelpCeUpperCase(newTarget); /* * find the location id for the topic that contains the * target_id (they may be the same). Then find the filename * and offset. */ *ret_id = (char *) target_id; if (GetFilenameResource (ccdfVol, newTarget) == NULL) { /* * if the reason TopicFilename failed was because we couldn't * find a resource, try looking for it in the LocationIDs. */ if (errno == CEErrorIllegalResource && LocationIDTopic (vol, newTarget, ret_id) == 0 && GetFilenameResource(ccdfVol, *ret_id) != NULL) return 0; return -1; } return 0; } /* End _DtHelpCeMapCcdfTargetToId */ /***************************************************************************** * Function: char * _DtHelpCeGetCcdfVolLocale (_DtHelpVolume vol) * * Parameters: vol Specifies the loaded volume * * Returns: The pointer to the locale string if successful. Otherwise * NULL. * * Purpose: Get the locale of the specified volume. * Returns the locale in a unix specific format * - locale[_ter][.charset] - This memory is owned by * the caller. * *****************************************************************************/ char * _DtHelpCeGetCcdfVolLocale ( _DtHelpVolume vol) { char *locale, *ptr, *resStr; char *mResStr = NULL; char *mLang = NULL; char *lang = "C"; char *defLocale = "C.UTF-8"; CcdfVolumePtr ccdfVol = GetCcdfVolumePtr(vol); errno = 0; /* resStr is owned by the volume Xrm database; don't free */ resStr = GetResourceString(ccdfVol->volDb, NULL, "CharSet", "charSet"); if (resStr == NULL || *resStr == '\0') { locale = strdup(defLocale); goto done; } mResStr = strdup(resStr); if (_DtHelpCeStrchr(mResStr, ".", 1, &ptr) == 0) { *ptr++ = '\0'; if (mResStr == NULL || *mResStr == '\0' || ptr == NULL || *ptr == '\0') locale = strdup(defLocale); else locale = strdup(resStr); goto done; } mLang = _DtHelpGetLocale(); if (_DtHelpCeStrrchr(mLang, ".", 1, &ptr) == 0) { *ptr = '\0'; if (mLang != NULL && *mLang != '\0') lang = mLang; } asprintf(&locale, "%s.%s", lang, resStr); done: free(mLang); free(mResStr); return locale; } /* End _DtHelpCeGetCcdfVolLocale */ /***************************************************************************** * Function: int _DtHelpCeGetCcdfDocStamp (_DtHelpVolume vol, char **ret_doc, * char **ret_time) * * Parameters: volume Specifies the loaded volume * ret_doc Returns the doc id. * ret_time Returns the time stamp. * * Memory: Caller owns the string in ret_doc and ret_time. * * Returns: 0 if successful, -2 if the volume does not contain * one or the other, -1 if any other failure. * * Purpose: Get doc id and time stamp of a volume. * *****************************************************************************/ int _DtHelpCeGetCcdfDocStamp ( _DtHelpVolume vol, char **ret_doc, char **ret_time) { int result; struct stat buf; result = -2; if (ret_doc != NULL) *ret_doc = NULL; if (ret_time != NULL) { result = -1; *ret_time = NULL; if (stat(vol->volFile, &buf) == 0) { *ret_time = (char *) malloc (sizeof(time_t) * 3 + 1); if (*ret_time != NULL) { sprintf(*ret_time, "%u", (unsigned) buf.st_mtime); return -2; } } } return result; } /* End _DtHelpCeGetCcdfDocStamp */ /****************************************************************************** * Function: static int _DtHelpCeGetCcdfTopTopic (_DtHelpVolume vol, * char **topic); * * Parameters: vol Specifies the loaded volume. * topic Returns the locationID for the top topic in * the volume hierarchy. This string is NOT * owned by the caller and should only be read or * copied. * * Return Value: 0 if successful, -1 if a failure occurs * * Purpose: Get the top topic of a volume. ******************************************************************************/ int _DtHelpCeGetCcdfTopTopic ( _DtHelpVolume vol, char **retTopic) { CcdfVolumePtr ccdfVol = GetCcdfVolumePtr(vol); *retTopic = GetResourceString(ccdfVol->volDb, NULL, "TopTopic", "topTopic"); if (*retTopic == NULL) { if (errno == CEErrorIllegalResource) errno = CEErrorMissingTopTopicRes; return (-1); } return (0); } /****************************************************************************** * Function: char *_DtHelpCeGetCcdfVolTitle (_DtHelpVolume vol); * * Parameters: vol Specifies the loaded volume. * * Return Value: The title if successful, NULL otherwise. * The caller *DOES NOT* own the memory returned * and *MUST NOT* modify the memory. * * Purpose: Get the title of a volume. * ******************************************************************************/ char * _DtHelpCeGetCcdfVolTitle ( _DtHelpVolume vol) { CcdfVolumePtr ccdfVol = GetCcdfVolumePtr(vol); return (GetResourceString (ccdfVol->volDb, NULL, "Title", "title")); } /****************************************************************************** * Function: int _DtHelpCeGetCcdfVolumeTitle (_DtHelpVolume vol); * * Parameters: vol Specifies the loaded volume. * * Return Value: The title if successful, NULL otherwise. * The caller *DOES NOT* own the memory returned * and *MUST NOT* modify the memory. * * Purpose: Get the title of a volume. * ******************************************************************************/ int _DtHelpCeGetCcdfVolumeTitle ( _DtHelpVolume vol, char **ret_title) { *ret_title = _DtHelpCeGetCcdfVolTitle(vol); if (*ret_title == NULL) { if (errno == CEErrorIllegalResource) errno = CEErrorMissingTitleRes; return (-1); } *ret_title = strdup(*ret_title); if (*ret_title == NULL) { errno = CEErrorMalloc; return (-1); } return (0); } /****************************************************************************** * Function: int _DtHelpCeOpenCcdfVolume (_DtHelpVolume vol); * * Parameters: vol Specifies the loaded volume. * * Return Value: * * Purpose: Open a CCDF help volume * ******************************************************************************/ int _DtHelpCeOpenCcdfVolume ( _DtHelpVolume vol) { struct stat buf; CcdfVolumePtr ccdfVol; ccdfVol = (struct _CcdfVolumeInfo *) malloc(sizeof(struct _CcdfVolumeInfo)); if (ccdfVol != NULL) { *ccdfVol = DefaultCcdfVol; ccdfVol->volDb = XrmGetFileDatabase(vol->volFile); if (ccdfVol->volDb != NULL) { (void) stat(vol->volFile, &buf); vol->check_time = buf.st_mtime; vol->vols.ccdf_vol = (CcdfVolumeHandle) ccdfVol; return 0; } free(ccdfVol); } return -1; } /****************************************************************************** * Function: void _DtHelpCeCloseCcdfVolume (_DtHelpVolume vol); * * Parameters: vol Specifies the loaded volume. * * Return Value: * * Purpose: Open a CCDF help volume * ******************************************************************************/ void _DtHelpCeCloseCcdfVolume ( _DtHelpVolume vol) { CcdfVolumePtr ccdfVol = GetCcdfVolumePtr(vol); if (ccdfVol->volDb != NULL) XrmDestroyDatabase (ccdfVol->volDb); if (ccdfVol->topicList != NULL) _DtHelpCeFreeStringArray (ccdfVol->topicList); if (ccdfVol->keywordFile != NULL) free (ccdfVol->keywordFile); free(ccdfVol); } /****************************************************************************** * Function: int _DtHelpCeRereadCcdfVolume (_DtHelpVolume vol); * * Parameters: vol Specifies the loaded volume. * * Return Value: * * Purpose: Reread a CCDF volume. * ******************************************************************************/ int _DtHelpCeRereadCcdfVolume ( _DtHelpVolume vol) { CcdfVolumePtr ccdfVol = GetCcdfVolumePtr(vol); if (ccdfVol->volDb != NULL) XrmDestroyDatabase (ccdfVol->volDb); if (ccdfVol->topicList != NULL) _DtHelpCeFreeStringArray (ccdfVol->topicList); if (ccdfVol->keywordFile != NULL) free (ccdfVol->keywordFile); ccdfVol->topicList = NULL; ccdfVol->keywordFile = NULL; ccdfVol->volDb = XrmGetFileDatabase(vol->volFile); if (ccdfVol->volDb != NULL) return 0; return -1; } /****************************************************************************** * Function: char *_DtHelpCeGetResourceString (XrmDatabase db, char *topic, * char *resClass, char *resName) * * Parameters: db Specifies the handle to a resource database. * topic Specifies the topic whose resource value is * desired. If 'topic' is NULL, the * desired resource is for the volume and * not a specific topic. * resClass Specifies the resource class name. * resName Specifies the resource name. * * Return Value: Returns the desired resource as string. * This string is NOT owned by the caller and * should only be read or copied. * * Returns NULL if an error occurs. * * errno Values: CEErrorMalloc * CEErrorIllegalResource If the resource is not in * the database or if the * resource NULL * * Purpose: Get a resource value for a volume or topic. * ******************************************************************************/ char * _DtHelpCeGetResourceString ( XrmDatabase db, char *topic, char *resClass, char *resName) { return (GetResourceString(db, topic, resClass, resName)); } /****************************************************************************** * Function: char **_DtHelpCeGetResourceStringArray (XrmDatabase db, * char *topic, char *resClass, char *resName) * * Parameters: db Specifies the handle to a resource database. * topic Specifies the topic whose resource value * is desired. If 'topic' is NULL, the * desired resource is for the volume and * not a specific topic. * resClass Specifies the resource class name. * resName Specifies the resource name. * * Return Value: Returns a NULL-terminated string array * containing the value of the desired resource. * The elements of the array are the strings of * non-whitespace characters in the resource value. * This array is owned by the caller and should be * freed (using _DtHelpCeFreeStringArray) when not * needed. * * Purpose: Get am array-valued resource for a volume or topic. * ******************************************************************************/ char ** _DtHelpCeGetResourceStringArray ( XrmDatabase db, char *topic, char *resClass, char *resName) { return (GetResourceStringArray(db, topic, resClass, resName)); }