/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * Makedoc's chm output routines. * * By Elias Pschernig, based on makehtml. * * Grzegorz Adam Hankiewicz made sorted TOCs. * * See readme.txt for copyright information. * * See allegro/docs/src/makedoc/format.txt for a brief description of * the source of _tx files. */ #include #include #include #include #include #include #include "makechm.h" #include "makehtml.h" #include "makemisc.h" #include "makedoc.h" #define ALT_TEXT(toc) ((toc->alt) ? toc->alt : toc->text) #define PREV_ROOT 1 #define PREV_SUB 2 /* Buffered tocs. Only the 'local' pointer should be freed */ typedef struct BTOC { const char *name; char *local; } BTOC; extern const char *html_extension; static char *_get_section_name(char *buf, const char *path, int section_number); static int _write_toc(const char *filename, int is_index); static int _write_hhp(const char *filename); static void _replace_extension(char *dest, char *path, char *ext, int size); static void _write_object(FILE *file, const char *name, const char *local); static void _output_btoc(FILE *file, BTOC *btoc, int *btoc_prev, int *num_btoc); static int _qsort_btoc_helper(const void *e1, const void *e2); /* _get_section_name: */ static char *_get_section_name(char *buf, const char *path, int section_number) { int len; char *filename; filename = get_filename((char *)path); strncpy(buf, filename, 5); len = strlen(filename); if (len > 5) len = 5; sprintf(buf+len, "%03d.%s", section_number, html_extension); return buf; } /* _write_toc: * Outputs a .hhc or .hhk file which are needed by the HTML Help compiler * (along with a .hhp and the standard HTML docs) to generate the CHM docs. * is_index is a boolean, and tells if the output file should be a chm * index file. The main difference is that the internal structure is * slightly different, and it doesn't need toc sorting. */ static int _write_toc(const char *filename, int is_index) { BTOC btoc[TOC_SIZE]; FILE *file = fopen(filename, "wt"); TOC *toc; int btoc_prev[TOC_SIZE]; int section_number = -1, prev = 0, num_btoc = 0, buffering = 0; if (!file) return 1; fprintf(file, "\n"); fprintf(file, "\n"); fprintf(file, "\n"); if (is_index) fprintf(file, "Index\n"); else fprintf(file, "Contents\n"); fprintf(file, "\n"); fprintf(file, "\n"); fprintf(file, "\n"); fprintf(file, "
    \n"); toc = tochead; if (toc) toc = toc->next; for (; toc; toc = toc->next) { char name[256]; if (toc->htmlable) { if (toc->root) { if (is_index) { fprintf(file, "\n"); } else { _output_btoc(file, btoc, btoc_prev, &num_btoc); if (prev == PREV_SUB) fprintf(file, "
\n"); if (prev == PREV_ROOT) fprintf(file, "\n"); } if (toc->otherfile) { sprintf(name, "%s.%s", toc->text, html_extension); } else { section_number++; _get_section_name(name, filename, section_number); } prev = PREV_ROOT; if(!is_index) buffering = 1; _write_object(file, ALT_TEXT(toc), mystrlwr(name)); } else { if (is_index) { fprintf(file, "\n"); } else { btoc_prev[num_btoc] = prev; } _get_section_name(name, filename, section_number); strcat(name, "#"); strcat(name, toc->text); prev = PREV_SUB; if(!is_index) { /* Buffer toc for posterior output */ btoc[num_btoc].local = m_strdup(mystrlwr(name)); btoc[num_btoc++].name = ALT_TEXT(toc); } else _write_object(file, ALT_TEXT(toc), name); } } } _output_btoc(file, btoc, btoc_prev, &num_btoc); if (prev == PREV_SUB) fprintf(file, "\n"); if (prev == PREV_ROOT) fprintf(file, "\n"); fprintf(file, "\n"); fprintf(file, "\n"); fprintf(file, "\n"); fclose(file); return 0; } /* _output_btoc: * Sorts all the buffered tocs read so far and frees their memory after * writting them to the file. Can be safely called at any moment, as long * as the pointer to num_btoc is meaningfull and contains the number of * buffered tocs in the btoc table. Note that btoc_prev is NOT sorted. */ static void _output_btoc(FILE *file, BTOC *btoc, int *btoc_prev, int *num_btoc) { int num; assert(btoc); assert(num_btoc); assert(btoc_prev); qsort(btoc, *num_btoc, sizeof(BTOC), _qsort_btoc_helper); for(num = 0; num < *num_btoc; num++) { if (btoc_prev[num] == PREV_SUB) fprintf(file, "\n"); if (btoc_prev[num] == PREV_ROOT) fprintf(file, "
    \n"); _write_object(file, btoc[num].name, btoc[num].local); free(btoc[num].local); } *num_btoc = 0; } /* _qsort_btoc_helper: * qsort helper. BTOC elements are sorted through the name value. */ static int _qsort_btoc_helper(const void *e1, const void *e2) { BTOC *s1 = (BTOC *)e1; BTOC *s2 = (BTOC *)e2; return mystricmp(s1->name, s2->name); } /* _write_object: * Writes to the file a record for a new object. Note that the object * record is not closed, so other's can be nested. This forces the caller * to close the object before writting another. */ static void _write_object(FILE *file, const char *name, const char *local) { assert(file); assert(name); assert(local); fprintf(file, "
  • \n"); fprintf(file, "\n", name); fprintf(file, "\n", local); fprintf(file, "\n"); } /* _write_hhp: */ static int _write_hhp(const char *filename) { FILE *file = fopen(filename, "wt"); TOC *toc; int section_number = -1; char *stripped_filename, *p; if (!file) return 1; stripped_filename = m_strdup(get_filename(filename)); if ((p = extension(stripped_filename))) *(p-1) = 0; fprintf(file, "[OPTIONS]\n"); fprintf(file, "Compiled file=%s.chm\n", stripped_filename); fprintf(file, "Contents file=%s.hhc\n", stripped_filename); fprintf(file, "Default topic=%s.%s\n", stripped_filename, html_extension); fprintf(file, "Full-text search=Yes\n"); fprintf(file, "Index file=%s.hhk\n", stripped_filename); fprintf(file, "Language=0x409 English (USA)\n"); if((html_flags & HTML_DOCUMENT_TITLE_FLAG) && !(html_flags & HTML_OLD_H_TAG_FLAG)) fprintf(file, "Title=%s\n", document_title); else { /* Write the filename without extension and capitalized */ fputc(toupper(*stripped_filename), file); fprintf(file, "%s", stripped_filename+1); } fprintf(file, "\n"); fprintf(file, "[FILES]\n"); free(stripped_filename); toc = tochead; if (toc) toc = toc->next; for (; toc; toc = toc->next) { if (toc->htmlable && toc->root) { char name[256]; if (toc->otherfile) { sprintf(name, "%s.%s", toc->text, html_extension); } else { section_number++; _get_section_name(name, filename, section_number); } mystrlwr(name); fprintf(file, "%s\n", name); } } return 0; } /* _replace_extension: * Replaces extension in path with different one. */ static void _replace_extension(char *dest, char *path, char *ext, int size) { char *ext_p; int pos; ext_p = strrchr(path, '.'); if (ext_p == NULL) ext_p = path; else ext_p++; pos = ext_p - path; if (pos+strlen(ext)+1 > size) { fprintf(stderr, "Buffer overfull"); exit(1); } memcpy(dest, path, pos); strcpy(dest+pos, ext); } /* write_chm: */ int write_chm(char *filename) { FILE *file; int found_signature = 0; char buf[1024]; if (!strcmp(extension(filename), "htm")) html_extension = "html"; if (!(flags & MULTIFILE_FLAG)) { printf("The chm version of the documentation requires that you generate \n"); printf("multifile output in the HTML version. Please delete the generated\n"); printf("HTML files and put @multiplefiles in allegro._tx.\n\n"); return 1; } system("hhc.exe > tempfile"); file = fopen("tempfile", "r"); if (file) { char buf[256]; int n = fread(buf, 1, 255, file); buf[n] = 0; fclose(file); if (strstr(buf, "HTML Help")) found_signature++; } remove("tempfile"); if (!found_signature) { printf("\nCannot find the HTML Help Compiler necessary to generate .chm output.\n"); printf("You can try to obtain it from Microsoft:\n"); printf("http://msdn.microsoft.com/library/en-us/htmlhelp/html/htmlhelp.exe\n\n"); return 1; } _replace_extension(buf, filename, "hhp", sizeof(buf)); printf("writing '%s'\n", buf); if (_write_hhp(buf)) return 1; _replace_extension(buf, filename, "hhc", sizeof(buf)); printf("writing '%s'\n", buf); _write_toc(buf, 0); _replace_extension(buf, filename, "hhk", sizeof(buf)); printf("writing '%s'\n", buf); _write_toc(buf, 1); return 0; }