/* * Help Access Library * A Library to access the contents of Windows Help files. * * Copyright (C) 1995-2004 Bernd Herd, http://www.herdsoft.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /** * This module loads Contents file data found in a separate file * outside of the IFS. * * The main purpose is to find human-readable context ID strings * instead of the ones created from hashvalues. * * Help Access Library Project. * */ #include #include #include #include #include #include #include #include "hlpacces.h" #include "top.h" #define MAX_BOOKLEVEL 20 /** * Informations maintained while parsing the contents file. */ typedef struct { /// Directory name containing the help files. Required to build directory names for includes. char *HelpFileDirectory; /// Recursion level int recursiondepth; /// Last input line processed char line[1000]; /// Parent element to add new entries to. CONTENTSENTRY *parent[MAX_BOOKLEVEL]; /// Last book level parsed int ibooklevel; } STATE; static void addsubtopic(CONTENTSENTRY *parent, CONTENTSENTRY *new); static void _ParseFile(CONTENTSENTRY *dest, STATE *state, FILE *fin); /** * Search for a .cnt file associated with a help file and * Read it's contents. * * @param ifs HIFS-Handle returned from IFSOpen * * @return Pointer to a CONTENTSENTRY Structure with the contents * Data retrieved. */ CONTENTSENTRY * iReadContentsFile(NPIFS ifs) { int len; char *cntfilename; FILE *fcnt; CONTENTSENTRY *result = NULL; STATE state; len = strlen(ifs->HelpFileName); cntfilename = (char *) Hlpmalloc(len + 5); if (cntfilename) { char *slash; state.HelpFileDirectory = Hlpstrdup(ifs->HelpFileName); state.recursiondepth = 0; if (NULL!=(slash=strrchr(state.HelpFileDirectory, '/'))) { *slash=0; } else { strcpy(state.HelpFileDirectory, "."); } memcpy(cntfilename, ifs->HelpFileName, len+1); /* Usually a Help filename ends with ".hlp" or ".HLP", * Replace the extension with ".cnt" */ if (len>4 && !strcasecmp(cntfilename+len-4, ".hlp")) { len -= 4; } strcpy(cntfilename + len, ".cnt"); if (NULL!=(fcnt=fopen(cntfilename, "rb"))) { result = (CONTENTSENTRY *) Hlpcalloc(1, sizeof(CONTENTSENTRY)); state.ibooklevel = result->ibooklevel = 0; state.parent[state.ibooklevel] = result; _ParseFile(result, &state, fcnt); } Hlpfree(cntfilename); Hlpfree(state.HelpFileDirectory); } return result; } /** * Parse the opened File, possibly recursive * * @param dest Destination Structure to receive all informations read * @param state Recursion-Independent state informations * @param fin File stream to read from */ static void _ParseFile(CONTENTSENTRY *dest, STATE *state, FILE *fin) { state->recursiondepth++; while (fgets(state->line, sizeof(state->line), fin)) { const char *src = state->line; int len = strlen(src); while (len && (state->line[len-1]=='\r' || state->line[len-1]=='\n')) { state->line[--len]=0; } while (isspace(*src)) src++; if (*src==':') { /* Lines startmg with a double-colon contain special command */ if (!strncasecmp(src, ":include", 8) && state->recursiondepth<10) { char *fullname; FILE *finclude; src += 8; while (isspace(*src)) src++; fullname = (char *) Hlpmalloc(strlen(state->HelpFileDirectory)+strlen(src)+2); sprintf(fullname, "%s/%s", state->HelpFileDirectory, src); if (NULL!=(finclude=fopen(fullname, "rb"))) { _ParseFile(dest, state, finclude); fclose(finclude); } Hlpfree(fullname); } } else if (isdigit(*src)) { int ibooklevel; char booklevel[30]; char title[200]; char contextid[300]; char *dst; for (dst=booklevel; isdigit(*src) && (dst-booklevel)=0 && ibooklevelibooklevel = ibooklevel; new->title = Hlpstrdup(title); new->contextstring = Hlpstrdup(contextid); new->dwHashValue = HlpGenerateHashValue(contextid); /* * If the new topic is in a lower book level, then we need to close * all previously opened book levels */ if (ibooklevel < state->ibooklevel) { state->ibooklevel = ibooklevel; } /* If the new topic is in a higher book level, we need to open * new book level entries for it. */ while (ibooklevel > state->ibooklevel) { CONTENTSENTRY *container = (CONTENTSENTRY *) Hlpcalloc(1, sizeof(CONTENTSENTRY)); container->ibooklevel = state->ibooklevel + 1; addsubtopic(state->parent[state->ibooklevel], container); state->parent[ ++state->ibooklevel ] = container; } /* If the new entry is in the same booklevel as the previous one, * Then just add it to its parent, * */ addsubtopic(state->parent[state->ibooklevel], new); } } } } state->recursiondepth--; } /** * Add a subentry to a container * * @param parent Parent container * @param new New subentry */ static void addsubtopic(CONTENTSENTRY *parent, CONTENTSENTRY *new) { parent->subtopics = (CONTENTSENTRY **) Hlprealloc(parent->subtopics, (parent->subtopiccount+1) * sizeof(CONTENTSENTRY *)); parent->subtopics[parent->subtopiccount++] = new; } /** * Free a CONTENTSENTRY record with all it's sub-contents * * @param contents Contents-File record to free */ void iFreeContentsFile(CONTENTSENTRY *contents) { int i; if (contents->title) Hlpfree(contents->title); if (contents->contextstring) Hlpfree(contents->contextstring); if (contents->subtopics) { for (i=0; isubtopiccount; i++) iFreeContentsFile(contents->subtopics[i]); Hlpfree(contents->subtopics); } Hlpfree(contents); } /** * Search the data read from the .cnt for a context string that * has the requested hash value * * @param contents Contents-File record to free * @param dwHashValue Hash-Value searched for */ char * iContentsFromHash(struct contentsentry *contents, DWORD dwHashValue) { int i; char *result = NULL; // printf("0x%08x %2d %s=%s\n", (int) contents, contents->ibooklevel, contents->title, contents->contextstring); if (dwHashValue == contents->dwHashValue) { result = Hlpstrdup(contents->contextstring); } else { for (i=0; isubtopiccount && !result; i++) { result = iContentsFromHash(contents->subtopics[i], dwHashValue); } } return result; }