/* * Help Viewer * * Copyright 1996 Ulrich Schmid */ #include #include "windows.h" #include "winhelp.h" static void Report(LPCSTR str) { #if 0 fprintf(stderr, "%s\n", str); #endif } #define GET_USHORT(buffer, i)\ (((BYTE)((buffer)[(i)]) + 0x100 * (BYTE)((buffer)[(i)+1]))) #define GET_SHORT(buffer, i)\ (((BYTE)((buffer)[(i)]) + 0x100 * (signed char)((buffer)[(i)+1]))) #define GET_UINT(buffer, i)\ GET_USHORT(buffer, i) + 0x10000 * GET_USHORT(buffer, i+2) static BOOL HLPFILE_DoReadHlpFile(HLPFILE*, LPCSTR); static BOOL HLPFILE_ReadFileToBuffer(HFILE); static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE**, BYTE**); static VOID HLPFILE_SystemCommands(HLPFILE*); static BOOL HLPFILE_Uncompress1_Phrases(); static BOOL HLPFILE_Uncompress1_Topic(); static BOOL HLPFILE_GetContext(HLPFILE*); static BOOL HLPFILE_AddPage(HLPFILE*, BYTE*, BYTE*); static BOOL HLPFILE_AddParagraph(HLPFILE*, BYTE *, BYTE*); static UINT HLPFILE_Uncompressed2_Size(BYTE*, BYTE*); static VOID HLPFILE_Uncompress2(BYTE**, BYTE*, BYTE*); static HLPFILE *first_hlpfile = 0; static HGLOBAL hFileBuffer; static BYTE *file_buffer; static struct { UINT num; BYTE *buf; HGLOBAL hBuffer; } phrases; static struct { BYTE **map; BYTE *end; UINT wMapLen; HGLOBAL hMap; HGLOBAL hBuffer; } topic; static struct { UINT bDebug; UINT wFont; UINT wIndent; UINT wHSpace; UINT wVSpace; UINT wVBackSpace; HLPFILE_LINK link; } attributes; /*********************************************************************** * * HLPFILE_Contents */ HLPFILE_PAGE *HLPFILE_Contents(LPCSTR lpszPath) { HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath); if (!hlpfile) return(0); return(hlpfile->first_page); } /*********************************************************************** * * HLPFILE_PageByNumber */ HLPFILE_PAGE *HLPFILE_PageByNumber(LPCSTR lpszPath, UINT wNum) { HLPFILE_PAGE *page; HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath); if (!hlpfile) return(0); for (page = hlpfile->first_page; page && wNum; page = page->next) wNum--; return page; } /*********************************************************************** * * HLPFILE_HlpFilePageByHash */ HLPFILE_PAGE *HLPFILE_PageByHash(LPCSTR lpszPath, LONG lHash) { INT i; UINT wNum; HLPFILE_PAGE *page; HLPFILE *hlpfile = HLPFILE_ReadHlpFile(lpszPath); if (!hlpfile) return(0); for (i = 0; i < hlpfile->wContextLen; i++) if (hlpfile->Context[i].lHash == lHash) break; if (i >= hlpfile->wContextLen) { HLPFILE_FreeHlpFile(hlpfile); return(0); } wNum = hlpfile->Context[i].wPage; for (page = hlpfile->first_page; page && wNum; page = page->next) wNum--; return page; } /*********************************************************************** * * HLPFILE_Hash */ LONG HLPFILE_Hash(LPCSTR lpszContext) { LONG lHash = 0; CHAR c; while((c = *lpszContext++)) { CHAR x = 0; if (c >= 'A' && c <= 'Z') x = c - 'A' + 17; if (c >= 'a' && c <= 'z') x = c - 'a' + 17; if (c >= '1' && c <= '9') x = c - '0'; if (c == '0') x = 10; if (c == '.') x = 12; if (c == '_') x = 13; if (x) lHash = lHash * 43 + x; } return lHash; } /*********************************************************************** * * HLPFILE_ReadHlpFile */ HLPFILE *HLPFILE_ReadHlpFile(LPCSTR lpszPath) { HGLOBAL hHlpFile; HLPFILE *hlpfile; for (hlpfile = first_hlpfile; hlpfile; hlpfile = hlpfile->next) if (!lstrcmp(hlpfile->lpszPath, lpszPath)) { hlpfile->wRefCount++; return(hlpfile); } hHlpFile = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE) + lstrlen(lpszPath) + 1); if (!hHlpFile) return(0); hlpfile = GlobalLock(hHlpFile); hlpfile->hSelf = hHlpFile; hlpfile->wRefCount = 1; hlpfile->hTitle = 0; hlpfile->hContext = 0; hlpfile->wContextLen = 0; hlpfile->first_page = 0; hlpfile->first_macro = 0; hlpfile->prev = 0; hlpfile->next = first_hlpfile; first_hlpfile = hlpfile; if (hlpfile->next) hlpfile->next->prev = hlpfile; hlpfile->lpszPath = GlobalLock(hHlpFile); hlpfile->lpszPath += sizeof(HLPFILE); strcpy(hlpfile->lpszPath, lpszPath); phrases.hBuffer = topic.hBuffer = hFileBuffer = 0; if (!HLPFILE_DoReadHlpFile(hlpfile, lpszPath)) { HLPFILE_FreeHlpFile(hlpfile); hlpfile = 0; } if (phrases.hBuffer) GlobalFree(phrases.hBuffer); if (topic.hBuffer) GlobalFree(topic.hBuffer); if (topic.hMap) GlobalFree(topic.hMap); if (hFileBuffer) GlobalFree(hFileBuffer); return(hlpfile); } /*********************************************************************** * * HLPFILE_DoReadHlpFile */ static BOOL HLPFILE_DoReadHlpFile(HLPFILE *hlpfile, LPCSTR lpszPath) { BOOL ret; HFILE hFile; OFSTRUCT ofs; BYTE *buf; hFile=OpenFile(lpszPath, &ofs, OF_READ | OF_SEARCH); if (hFile == HFILE_ERROR) return FALSE; ret = HLPFILE_ReadFileToBuffer(hFile); _lclose(hFile); if (!ret) return FALSE; HLPFILE_SystemCommands(hlpfile); if (!HLPFILE_Uncompress1_Phrases()) return FALSE; if (!HLPFILE_Uncompress1_Topic()) return FALSE; buf = topic.map[0] + 0xc; while(buf + 0xc < topic.end) { BYTE *end = MIN(buf + GET_UINT(buf, 0), topic.end); UINT next, index, offset; switch (buf[0x14]) { case 0x02: if (!HLPFILE_AddPage(hlpfile, buf, end)) return(FALSE); break; case 0x20: if (!HLPFILE_AddParagraph(hlpfile, buf, end)) return(FALSE); break; case 0x23: if (!HLPFILE_AddParagraph(hlpfile, buf, end)) return(FALSE); break; default: fprintf(stderr, "buf[0x14] = %x\n", buf[0x14]); } next = GET_UINT(buf, 0xc); if (next == 0xffffffff) break; index = next >> 14; offset = next & 0x3fff; if (index > topic.wMapLen) {Report("maplen"); break;} buf = topic.map[index] + offset; } return(HLPFILE_GetContext(hlpfile)); } /*********************************************************************** * * HLPFILE_AddPage */ static BOOL HLPFILE_AddPage(HLPFILE *hlpfile, BYTE *buf, BYTE *end) { HGLOBAL hPage; HLPFILE_PAGE *page, **pageptr; BYTE *title; UINT titlesize; for (pageptr = &hlpfile->first_page; *pageptr; pageptr = &(*pageptr)->next) /* Nothing */; if (buf + 0x31 > end) {Report("page1"); return(FALSE);}; title = buf + GET_UINT(buf, 0x10); if (title > end) {Report("page2"); return(FALSE);}; titlesize = HLPFILE_Uncompressed2_Size(title, end); hPage = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_PAGE) + titlesize); if (!hPage) return FALSE; page = *pageptr = GlobalLock(hPage); pageptr = &page->next; page->hSelf = hPage; page->file = hlpfile; page->next = 0; page->first_paragraph = 0; page->lpszTitle = GlobalLock(hPage); page->lpszTitle += sizeof(HLPFILE_PAGE); HLPFILE_Uncompress2(&title, end, page->lpszTitle); page->wNumber = GET_UINT(buf, 0x21); attributes.bDebug = 0; attributes.wFont = 0; attributes.wVSpace = 0; attributes.wVBackSpace = 0; attributes.wHSpace = 0; attributes.wIndent = 0; attributes.link.lpszPath = 0; return TRUE; } /*********************************************************************** * * HLPFILE_AddParagraph */ static BOOL HLPFILE_AddParagraph(HLPFILE *hlpfile, BYTE *buf, BYTE *end) { HGLOBAL hParagraph; HLPFILE_PAGE *page; HLPFILE_PARAGRAPH *paragraph, **paragraphptr; UINT textsize; BYTE *format, *text; BOOL format_header = TRUE; BOOL format_end = FALSE; UINT mask, i; if (!hlpfile->first_page) {Report("paragraph1"); return(FALSE);}; for (page = hlpfile->first_page; page->next; page = page->next) /* Nothing */; for (paragraphptr = &page->first_paragraph; *paragraphptr; paragraphptr = &(*paragraphptr)->next) /* Nothing */; if (buf + 0x19 > end) {Report("paragraph2"); return(FALSE);}; if (buf[0x14] == 0x02) return TRUE; text = buf + GET_UINT(buf, 0x10); switch (buf[0x14]) { case 0x20: format = buf + 0x18; while (*format) format++; format += 4; break; case 0x23: format = buf + 0x2b; if (buf[0x17] & 1) format++; break; default: Report("paragraph3"); return FALSE; } while (text < end) { if (format_header) { format_header = FALSE; mask = GET_USHORT(format, 0); mask &= 0x3ff; format += 2; for (i = 0; i < 10; i++, mask = mask >> 1) { if (mask & 1) { BOOL twoargs = FALSE; CHAR prefix0 = ' '; CHAR prefix1 = '*'; if (i == 9 && !twoargs) { switch (*format++) { default: prefix0 = prefix1 = '?'; break; case 0x82: prefix0 = prefix1 = 'x'; break; case 0x84: prefix0 = prefix1 = 'X'; twoargs = TRUE; } } if (*format & 1) switch(*format) { default: format += 2; break; } else switch(*format) { default: format++; break; case 0x08: format += 3; break; } if (twoargs) format += (*format & 1) ? 2 : 1; } } } for (; !format_header && text < end && format < end && !*text; text++) { switch(*format) { case 0x80: attributes.wFont = GET_USHORT(format, 1); format += 3; break; case 0x81: attributes.wVSpace++; format += 1; break; case 0x82: attributes.wVSpace += 2 - attributes.wVBackSpace; attributes.wVBackSpace = 0; attributes.wIndent = 0; format += 1; break; case 0x83: attributes.wIndent++; format += 1; break; case 0x84: format += 3; break; case 0x86: case 0x87: case 0x88: format += 9; break; case 0x89: attributes.wVBackSpace++; format += 1; break; case 0xa9: format += 2; break; case 0xe2: case 0xe3: attributes.link.lpszPath = hlpfile->lpszPath; attributes.link.lHash = GET_UINT(format, 1); attributes.link.bPopup = !(*format & 1); format += 5; break; case 0xea: attributes.link.lpszPath = format + 8; attributes.link.lHash = GET_UINT(format, 4); attributes.link.bPopup = !(*format & 1); format += 3 + GET_USHORT(format, 1); break; case 0xff: if (buf[0x14] != 0x23 || GET_USHORT(format, 1) == 0xffff) { if (format_end) Report("format_end"); format_end = TRUE; break; } else { format_header = TRUE; format += 10; break; } default: Report("format"); format++; } } if (text > end || format > end) {Report("paragraph_end"); return(FALSE);}; if (text == end && !format_end) Report("text_end"); if (text == end) break; textsize = HLPFILE_Uncompressed2_Size(text, end); hParagraph = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_PARAGRAPH) + textsize); if (!hParagraph) return FALSE; paragraph = *paragraphptr = GlobalLock(hParagraph); paragraphptr = ¶graph->next; paragraph->hSelf = hParagraph; paragraph->next = 0; paragraph->link = 0; paragraph->lpszText = GlobalLock(hParagraph); paragraph->lpszText += sizeof(HLPFILE_PARAGRAPH); HLPFILE_Uncompress2(&text, end, paragraph->lpszText); paragraph->bDebug = attributes.bDebug; paragraph->wFont = attributes.wFont; paragraph->wVSpace = attributes.wVSpace; paragraph->wHSpace = attributes.wHSpace; paragraph->wIndent = attributes.wIndent; if (attributes.link.lpszPath) { LPSTR ptr; HGLOBAL handle = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_LINK) + strlen(attributes.link.lpszPath) + 1); if (!handle) return FALSE; paragraph->link = GlobalLock(handle); paragraph->link->hSelf = handle; ptr = GlobalLock(handle); ptr += sizeof(HLPFILE_LINK); lstrcpy(ptr, (LPSTR) attributes.link.lpszPath); paragraph->link->lpszPath = ptr; paragraph->link->lHash = attributes.link.lHash; paragraph->link->bPopup = attributes.link.bPopup; } attributes.bDebug = 0; attributes.wVSpace = 0; attributes.wHSpace = 0; attributes.link.lpszPath = 0; } return TRUE; } /*********************************************************************** * * HLPFILE_ReadFileToBuffer */ static BOOL HLPFILE_ReadFileToBuffer(HFILE hFile) { BYTE header[16], dummy[1]; UINT size; if (_hread(hFile, header, 16) != 16) {Report("header"); return(FALSE);}; size = GET_UINT(header, 12); hFileBuffer = GlobalAlloc(GMEM_FIXED, size + 1); if (!hFileBuffer) return FALSE; file_buffer = GlobalLock(hFileBuffer); memcpy(file_buffer, header, 16); if (_hread(hFile, file_buffer + 16, size - 16) != size - 16) {Report("filesize1"); return(FALSE);}; if (_hread(hFile, dummy, 1) != 0) Report("filesize2"); file_buffer[size] = '0'; return TRUE; } /*********************************************************************** * * HLPFILE_FindSubFile */ static BOOL HLPFILE_FindSubFile(LPCSTR name, BYTE **subbuf, BYTE **subend) { BYTE *root = file_buffer + GET_UINT(file_buffer, 4); BYTE *end = file_buffer + GET_UINT(file_buffer, 12); BYTE *ptr = root + 0x37; while (ptr < end && ptr[0] == 0x7c) { BYTE *fname = ptr + 1; ptr += strlen(ptr) + 1; if (!lstrcmpi(fname, name)) { *subbuf = file_buffer + GET_UINT(ptr, 0); *subend = *subbuf + GET_UINT(*subbuf, 0); if (file_buffer > *subbuf || *subbuf > *subend || *subend >= end) { Report("subfile"); return FALSE; } return TRUE; } else ptr += 4; } return FALSE; } /*********************************************************************** * * HLPFILE_SystemCommands */ static VOID HLPFILE_SystemCommands(HLPFILE* hlpfile) { BYTE *buf, *ptr, *end; HGLOBAL handle; HLPFILE_MACRO *macro, **m; LPSTR p; hlpfile->lpszTitle = ""; if (!HLPFILE_FindSubFile("SYSTEM", &buf, &end)) return; for (ptr = buf + 0x15; ptr + 4 <= end; ptr += GET_USHORT(ptr, 2) + 4) { switch (GET_USHORT(ptr, 0)) { case 1: if (hlpfile->hTitle) {Report("title"); break;} hlpfile->hTitle = GlobalAlloc(GMEM_FIXED, strlen(ptr + 4) + 1); if (!hlpfile->hTitle) return; hlpfile->lpszTitle = GlobalLock(hlpfile->hTitle); lstrcpy(hlpfile->lpszTitle, ptr + 4); break; case 2: if (GET_USHORT(ptr, 2) != 1 || ptr[4] != 0) Report("system2"); break; case 3: if (GET_USHORT(ptr, 2) != 4 || GET_UINT(ptr, 4) != 0) Report("system3"); break; case 4: handle = GlobalAlloc(GMEM_FIXED, sizeof(HLPFILE_MACRO) + lstrlen(ptr + 4) + 1); if (!handle) break; macro = GlobalLock(handle); macro->hSelf = handle; p = GlobalLock(handle); p += sizeof(HLPFILE_MACRO); lstrcpy(p, (LPSTR) ptr + 4); macro->lpszMacro = p; macro->next = 0; for (m = &hlpfile->first_macro; *m; m = &(*m)->next); *m = macro; break; default: Report("system"); } } } /*********************************************************************** * * HLPFILE_Uncompressed1_Size */ static INT HLPFILE_Uncompressed1_Size(BYTE *ptr, BYTE *end) { INT i, newsize = 0; while (ptr < end) { INT mask=*ptr++; for (i = 0; i < 8 && ptr < end; i++, mask = mask >> 1) { if (mask & 1) { INT code = GET_USHORT(ptr, 0); INT len = 3 + (code >> 12); newsize += len; ptr += 2; } else newsize++, ptr++; } } return(newsize); } /*********************************************************************** * * HLPFILE_Uncompress1 */ static BYTE *HLPFILE_Uncompress1(BYTE *ptr, BYTE *end, BYTE *newptr) { INT i; while (ptr < end) { INT mask=*ptr++; for (i = 0; i < 8 && ptr < end; i++, mask = mask >> 1) { if (mask & 1) { INT code = GET_USHORT(ptr, 0); INT len = 3 + (code >> 12); INT offset = code & 0xfff; hmemcpy(newptr, newptr - offset - 1, len); newptr += len; ptr += 2; } else *newptr++ = *ptr++; } } return(newptr); } /*********************************************************************** * * HLPFILE_Uncompress1_Phrases */ static BOOL HLPFILE_Uncompress1_Phrases() { UINT i, num, newsize; BYTE *buf, *end, *newbuf; if (!HLPFILE_FindSubFile("Phrases", &buf, &end)) {Report("phrases0"); return FALSE;} num = phrases.num = GET_USHORT(buf, 9); if (buf + 2 * num + 0x13 >= end) {Report("uncompress1a"); return(FALSE);}; newsize = 2 * num + 2; newsize += HLPFILE_Uncompressed1_Size(buf + 0x13 + 2 * num, end); phrases.hBuffer = GlobalAlloc(GMEM_FIXED, newsize); if (!phrases.hBuffer) return FALSE; newbuf = phrases.buf = GlobalLock(phrases.hBuffer); hmemcpy(newbuf, buf + 0x11, 2 * num + 2); HLPFILE_Uncompress1(buf + 0x13 + 2 * num, end, newbuf + 2 * num + 2); for (i = 0; i < num; i++) { INT i0 = GET_USHORT(newbuf, 2 * i); INT i1 = GET_USHORT(newbuf, 2 * i + 2); if (i1 < i0 || i1 > newsize) {Report("uncompress1b"); return(FALSE);}; } return TRUE; } /*********************************************************************** * * HLPFILE_Uncompress1_Topic */ static BOOL HLPFILE_Uncompress1_Topic() { BYTE *buf, *ptr, *end, *newptr; INT i, newsize = 0; if (!HLPFILE_FindSubFile("TOPIC", &buf, &end)) {Report("topic0"); return FALSE;} buf += 9; topic.wMapLen = (end - buf - 1) / 0x1000 + 1; for (i = 0; i < topic.wMapLen; i++) { ptr = buf + i * 0x1000; /* I don't know why, it's necessary for printman.hlp */ if (ptr + 0x44 > end) ptr = end - 0x44; newsize += HLPFILE_Uncompressed1_Size(ptr + 0xc, MIN(end, ptr + 0x1000)); } topic.hMap = GlobalAlloc(GMEM_FIXED, topic.wMapLen * sizeof(topic.map[0])); topic.hBuffer = GlobalAlloc(GMEM_FIXED, newsize); if (!topic.hMap || !topic.hBuffer) return FALSE; topic.map = GlobalLock(topic.hMap); newptr = GlobalLock(topic.hBuffer); topic.end = newptr + newsize; for (i = 0; i < topic.wMapLen; i++) { ptr = buf + i * 0x1000; if (ptr + 0x44 > end) ptr = end - 0x44; topic.map[i] = newptr - 0xc; newptr = HLPFILE_Uncompress1(ptr + 0xc, MIN(end, ptr + 0x1000), newptr); } return TRUE; } /*********************************************************************** * * HLPFILE_Uncompressed2_Size */ static UINT HLPFILE_Uncompressed2_Size(BYTE *ptr, BYTE *end) { UINT wSize = 0; while (ptr < end && *ptr) { if (*ptr >= 0x20) wSize++, ptr++; else { BYTE *phptr, *phend; UINT code = 0x100 * ptr[0] + ptr[1]; UINT index = (code - 0x100) / 2; BOOL space = code & 1; if (index < phrases.num) { phptr = phrases.buf + GET_USHORT(phrases.buf, 2 * index); phend = phrases.buf + GET_USHORT(phrases.buf, 2 * index + 2); if (phend < phptr) Report("uncompress2a"); wSize += phend - phptr; if (space) wSize++; } else Report("uncompress2b"); ptr += 2; } } return(wSize + 1); } /*********************************************************************** * * HLPFILE_Uncompress2 */ static VOID HLPFILE_Uncompress2(BYTE **pptr, BYTE *end, BYTE *newptr) { BYTE *ptr = *pptr; while (ptr < end && *ptr) { if (*ptr >= 0x20) *newptr++ = *ptr++; else { BYTE *phptr, *phend; UINT code = 0x100 * ptr[0] + ptr[1]; UINT index = (code - 0x100) / 2; BOOL space = code & 1; phptr = phrases.buf + GET_USHORT(phrases.buf, 2 * index); phend = phrases.buf + GET_USHORT(phrases.buf, 2 * index + 2); hmemcpy(newptr, phptr, phend - phptr); newptr += phend - phptr; if (space) *newptr++ = ' '; ptr += 2; } } *newptr = '\0'; *pptr = ptr; } /*********************************************************************** * * HLPFILE_GetContext */ static BOOL HLPFILE_GetContext(HLPFILE *hlpfile) { UINT i, j, clen, tlen; BYTE *cbuf, *cptr, *cend, *tbuf, *tptr, *tend; if (!HLPFILE_FindSubFile("CONTEXT", &cbuf, &cend)) {Report("context0"); return FALSE;} if (cbuf + 0x37 > cend) {Report("context1"); return(FALSE);}; clen = GET_UINT(cbuf, 0x2b); if (cbuf + 0x37 + 8 * hlpfile->wContextLen > cend) {Report("context2"); return(FALSE);}; if (!HLPFILE_FindSubFile("TTLBTREE", &tbuf, &tend)) {Report("ttlb0"); return FALSE;} if (tbuf + 0x37 > tend) {Report("ttlb1"); return(FALSE);}; tlen = GET_UINT(tbuf, 0x2b); hlpfile->hContext = GlobalAlloc(GMEM_FIXED, clen * sizeof(HLPFILE_CONTEXT)); if (!hlpfile->hContext) return FALSE; hlpfile->Context = GlobalLock(hlpfile->hContext); hlpfile->wContextLen = clen; cptr = cbuf + 0x37; for (i = 0; i < clen; i++, cptr += 8) { tptr = tbuf + 0x37; for (j = 0; j < tlen; j++, tptr += 5 + strlen(tptr + 4)) { if (tptr + 4 >= tend) {Report("ttlb2"); return(FALSE);}; if (GET_UINT(tptr, 0) == GET_UINT(cptr, 4)) break; } if (j >= tlen) { Report("ttlb3"); j = 0; } hlpfile->Context[i].lHash = GET_UINT(cptr, 0); hlpfile->Context[i].wPage = j; } return TRUE; } /*********************************************************************** * * HLPFILE_DeleteParagraph */ static VOID HLPFILE_DeleteParagraph(HLPFILE_PARAGRAPH* paragraph) { if (!paragraph) return; if (paragraph->link) GlobalFree(paragraph->link->hSelf); HLPFILE_DeleteParagraph(paragraph->next); GlobalFree(paragraph->hSelf); } /*********************************************************************** * * DeletePage */ static VOID HLPFILE_DeletePage(HLPFILE_PAGE* page) { if (!page) return; HLPFILE_DeletePage(page->next); HLPFILE_DeleteParagraph(page->first_paragraph); GlobalFree(page->hSelf); } /*********************************************************************** * * DeleteMacro */ static VOID HLPFILE_DeleteMacro(HLPFILE_MACRO* macro) { if (!macro) return; HLPFILE_DeleteMacro(macro->next); GlobalFree(macro->hSelf); } /*********************************************************************** * * HLPFILE_FreeHlpFile */ VOID HLPFILE_FreeHlpFile(HLPFILE* hlpfile) { if (!hlpfile) return; if (--hlpfile->wRefCount) return; if (hlpfile->next) hlpfile->next->prev = hlpfile->prev; if (hlpfile->prev) hlpfile->prev->next = hlpfile->next; else first_hlpfile = 0; HLPFILE_DeletePage(hlpfile->first_page); HLPFILE_DeleteMacro(hlpfile->first_macro); if (hlpfile->hContext) GlobalFree(hlpfile->hContext); if (hlpfile->hTitle) GlobalFree(hlpfile->hTitle); GlobalFree(hlpfile->hSelf); } /*********************************************************************** * * FreeHlpFilePage */ VOID HLPFILE_FreeHlpFilePage(HLPFILE_PAGE* page) { if (!page) return; HLPFILE_FreeHlpFile(page->file); } /* Local Variables: */ /* c-file-style: "GNU" */ /* End: */