1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *      Makedoc's html output routines.
12  *
13  *      By Shawn Hargreaves.
14  *
15  *      Grzegorz Adam Hankiewicz made the output valid HTML 4.0, made sorted
16  *      TOCs, added support for cross references and optional CSS to color
17  *      output, added support for auto types' cross references generation,
18  *      and a few smaller details more.
19  *
20  *      Robert J. Ohannessian did some cosmetic improvements to the HTML
21  *      output and separated the CSS file.
22  *
23  *      See readme.txt for copyright information.
24  *
25  *      See allegro/docs/src/makedoc/format.txt for a brief description of
26  *      the source of _tx files.
27  */
28 
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <assert.h>
34 
35 #include "makehtml.h"
36 #include "makemisc.h"
37 #include "makedoc.h"
38 #include "maketexi.h"
39 
40 #define TOKEN_NORMAL               'n'
41 #define TOKEN_TEXT                 't'
42 
43 typedef struct t_post POST;
44 
45 struct t_post
46 {
47    char filename[1024];
48    char **token;
49    int num;
50 };
51 
52 int html_flags;
53 char charset[256]="iso-8859-1";
54 const char *html_extension = "html";
55 char *html_document_title;
56 char *html_footer;
57 char *html_see_also_text;
58 char *html_examples_using_this_text;
59 char *html_css_filename;
60 char *html_return_value_text;
61 char *html_text_substitution[256];
62 
63 static POST **_post;
64 static FILE *_file;
65 static char _filename[1024];
66 static char *_xref[256], *_eref[256];
67 static char *_full_eref_list;
68 static int _xrefs, _erefs;
69 static int _empty_count;
70 static char *_word_substitution[256];
71 static const char *_codeblock_start = "<blockquote class=\"code\"><pre>";
72 static const char *_codeblock_end = "</pre></blockquote>";
73 static const int _codeblock_start_len = 30;
74 static const int _codeblock_end_len = 19;
75 
76 /* MSVC doesn't like long strings. */
77 static const char *_css_data1 = "\
78 A.xref:link {\n\
79 \tcolor:           blue;\n\
80 \ttext-decoration: none;\n\
81 \tbackground:      transparent;\n\
82 }\n\
83 A.xref:visited {\n\
84 \tcolor:           blue;\n\
85 \ttext-decoration: none;\n\
86 \tbackground:      transparent;\n\
87 }\n\
88 A.xref:hover {\n\
89 \tcolor:           blue;\n\
90 \ttext-decoration: underline;\n\
91 \tbackground:      rgb(220, 220, 220);\n\
92 }\n\
93 A.xref:active {\n\
94 \tcolor:           red;\n\
95 \ttext-decoration: none;\n\
96 \tbackground:      rgb(255, 204, 50);\n\
97 }\n\
98 A.eref:link {\n\
99 \tcolor:           blue;\n\
100 \ttext-decoration: none;\n\
101 \tbackground:      transparent;\n\
102 }\n\
103 A.eref:visited {\n\
104 \tcolor:           blue;\n\
105 \ttext-decoration: none;\n\
106 \tbackground:      transparent;\n\
107 }\n\
108 A.eref:hover {\n\
109 \tcolor:           blue;\n\
110 \ttext-decoration: underline;\n\
111 \tbackground:      rgb(255, 165, 214);\n\
112 }\n\
113 A.eref:active {\n\
114 \tcolor:           red;\n\
115 \ttext-decoration: none;\n\
116 \tbackground:      rgb(255, 204, 50);\n\
117 }\n\
118 A.autotype:link {\n\
119 \tcolor:           rgb(64, 64, 255);\n\
120 \ttext-decoration: none;\n\
121 \tbackground:      transparent;\n\
122 }\n\
123 A.autotype:visited {\n\
124 \tcolor:           rgb(64, 64, 255);\n\
125 \ttext-decoration: none;\n\
126 \tbackground:      transparent;\n\
127 }\n\
128 A.autotype:hover {\n\
129 \tcolor:           rgb(64, 64, 255);\n\
130 \ttext-decoration: underline;\n\
131 \tbackground:      transparent;\n\
132 }\n\
133 ";
134 
135 static const char *_css_data2 = "\
136 A.autotype:active {\n\
137 \tcolor:           red;\n\
138 \ttext-decoration: none;\n\
139 \tbackground:      transparent;\n\
140 }\n\
141 blockquote.xref {\n\
142 \tfont-family:     helvetica, verdana, serif;\n\
143 \tfont-size:       smaller;\n\
144 \tborder:          thin solid rgb(220, 220, 220);\n\
145 \tcolor:           black;\n\
146 \tbackground:      rgb(240, 240, 240);\n\
147 }\n\
148 blockquote.eref {\n\
149 \tfont-family:     helvetica, verdana, serif;\n\
150 \tfont-size:       smaller;\n\
151 \tborder:          thin solid rgb(255, 165, 214);\n\
152 \tcolor:           black;\n\
153 \tbackground:      rgb(255, 201, 230);\n\
154 }\n\
155 blockquote.code {\n\
156 \tborder:          thin solid rgb(255, 234, 190);\n\
157 \tcolor:           black;\n\
158 \tbackground:      rgb(255, 255, 205);\n\
159 }\n\
160 blockquote.text {\n\
161 \tborder:          thin solid rgb(190, 234, 255);\n\
162 \tcolor:           black;\n\
163 \tbackground:      rgb(225, 255, 255);\n\
164 }\n\
165 div.al-api {\n\
166 \tpadding-left:    0.5em;\n\
167 \tcolor:           black;\n\
168 \tbackground:      rgb(255, 234, 100);\n\
169 \tfont-weight:     bold;\n\
170 \tborder-bottom:   medium solid rgb(255, 204, 51);\n\
171 \tborder-left:     medium solid rgb(255, 204, 51);\n\
172 \tmargin-top:      2em;\n\
173 }\n\
174 div.al-api-cont {\n\
175 \tpadding-left:    0.5em;\n\
176 \tcolor:           black;\n\
177 \tbackground:      rgb(255, 234, 100);\n\
178 \tfont-weight:     bold;\n\
179 \tborder-bottom:   medium solid rgb(255, 204, 51);\n\
180 \tborder-left:     medium solid rgb(255, 204, 51);\n\
181 \tmargin-top:      -1em;\n\
182 }\n\
183 div.faq-shift-to-right {\n\
184 \tmargin-left: 2em;\n\
185 }\n\
186 div.al-back-to-contents {\n\
187 \ttext-align:      center;\n\
188 \tfont-family:     sans-serif;\n\
189 \tmargin-top:      1em;\n\
190 \tfont-weight:     bold;\n\
191 \t/* Removing the  comments of the next line create longer pages */\n\
192 \t/* margin-bottom:   100em; */\n\
193 }\n\
194 ";
195 
196 /* Internal functions */
197 
198 static void _write_html_ref_list(char **ref, int *refs, const char *type);
199 static void _write_html_ref(char *ref, const char *type);
200 static void _output_html_footer(char *main_filename);
201 static void _output_toc(char *filename, int root, int body, int part);
202 static void _hfprintf(char *format, ...);
203 static void _hfputs(char *string);
204 static int _toc_scmp(const void *e1, const void *e2);
205 static int _str_cmp(const void *s1, const void *s2);
206 static int _output_section_heading(LINE *line, char *filename, int section_number);
207 static void _output_custom_markers(LINE *line);
208 static void _output_buffered_text(void);
209 static void _output_html_header(char *section);
210 static void _add_post_process_ref(const LINE *token, char type);
211 static void _post_process_pending_refs(void);
212 static POST *_search_post_section(const char *filename);
213 static POST *_search_post_section_with_token(const char *token);
214 static POST *_create_post_section(const char *filename);
215 static void _destroy_post_page(POST *p);
216 static void _post_process_filename(char *filename);
217 static int _verify_correct_input(void);
218 static void _close_html_file(FILE *file);
219 static void _output_sorted_nested_toc(TOC **list, unsigned int num_items);
220 static int _detect_non_paragraph_sections(const char *text);
221 static char *_mark_up_auto_types(char *line, char **auto_types);
222 static void _write_css_file(char *html_path, char *css_filename);
223 static void _prepare_html_text_substitution(void);
224 static void _free_html_text_substitution_memory(void);
225 static char *_do_text_substitution(char *input);
226 static void _output_symbol_index(void);
227 static const char *_find_short_description(const LINE *line);
228 static const char *_look_up_short_description(const char *token, int len);
229 static char *_scan_line_for_code_tokens(char *line, int *code_scanning);
230 static char *_replace_code_tokens(char *line, int column, int *end);
231 static char *_find_potential_token(char *line, int column, int end, int *found_size);
232 
233 
234 
235 /* write_html:
236  * Entry point to the function which translates makedoc's format
237  * to correct html output.
238  */
write_html(char * filename)239 int write_html(char *filename)
240 {
241    int block_empty_lines = 0, section_number = 0, ret;
242    int last_line_was_a_definition = 0;
243    LINE *line = head;
244    char **auto_types;
245 
246    if (_verify_correct_input())
247       return 1;
248 
249    /* English default text initialization */
250    if (!html_see_also_text)
251       html_see_also_text = m_strdup("See also:");
252    if (!html_examples_using_this_text)
253       html_examples_using_this_text = m_strdup("Examples using this:");
254    if (!html_return_value_text)
255       html_return_value_text = m_strdup("Return value:");
256 
257    /*printf("writing %s\n", filename);*/
258 
259    _file = fopen(filename, "w");
260    if (!_file)
261       return 1;
262 
263    /* build up a table with Allegro's structures' names (spelling?) */
264    auto_types = build_types_lookup_table(0);
265 
266    strcpy(_filename, filename);
267    if(html_document_title) _output_html_header(0);
268    /* write out css if specified and allowed */
269    if(html_css_filename && !(html_flags & HTML_IGNORE_CSS))
270       _write_css_file(filename, html_css_filename);
271 
272    while (line) {
273       if (line->flags & HTML_FLAG) {
274 	 if (line->flags & (HEADING_FLAG | NODE_FLAG | DEFINITION_FLAG)) {
275 	    _write_html_ref_list(_xref, &_xrefs, "xref");
276 	    _write_html_ref_list(_eref, &_erefs, "eref");
277 	 }
278 
279 	 if (line->flags & HEADING_FLAG) {
280 	    _output_buffered_text();
281 	    if (_output_section_heading(line, filename, section_number))
282 	       return 1;
283 	    _add_post_process_ref(line, TOKEN_TEXT);
284 	    section_number++;
285 	 }
286 	 else if (line->flags & RETURN_VALUE_FLAG) {
287 	    assert(html_return_value_text);
288 	    fputs("<p><b>", _file);
289 	    fputs(html_return_value_text, _file);
290 	    fputs("</b>\n", _file);
291 	 }
292 	 else if (line->flags & NODE_FLAG) {
293 	    _output_buffered_text();
294 	    /* output a node marker, which will be followed by a chunk of text inside a chapter */
295 	    fprintf(_file, "<br><center><h2><a name=\"%s\">%s</a></h2></center><p>\n",
296 	       line->text, line->text);
297 	    _add_post_process_ref(line, TOKEN_TEXT);
298 	 }
299 	 else if (line->flags & DEFINITION_FLAG) {
300 	    static int prev_continued = 0;
301 	    char *temp = m_strdup(line->text);
302 	    if (_empty_count && !block_empty_lines) _empty_count++;
303 	    _output_buffered_text();
304 	    /* output a function definition */
305 	    _add_post_process_ref(line, TOKEN_NORMAL);
306 	    temp = _mark_up_auto_types(temp, auto_types);
307 
308 	    if (!prev_continued) {
309 	       if (!(html_flags & HTML_IGNORE_CSS)) {
310 	          if (!last_line_was_a_definition) {
311 		     fputs("<div class=\"al-api\"><b>", _file);
312 		  }
313 		  else {
314 		     fputs("<div class=\"al-api-cont\"><b>", _file);
315 		  }
316 	       }
317 	       else {
318 	          if (!last_line_was_a_definition) {
319 		     fputs("<hr><font size=\"+1\"><b>", _file);
320 		  }
321 		  else {
322 		     fputs("<font size=\"+1\"><b>", _file);
323 		  }
324 	       }
325 	    }
326 	    _hfprintf("%s", temp);
327 	    if (!(line->flags & CONTINUE_FLAG)) {
328 	       if (!(html_flags & HTML_IGNORE_CSS)) {
329 		  fputs("</b></div><br>", _file);
330 	       }
331 	       else {
332 		  fputs("</b></font><br>", _file);
333 	       }
334 	       prev_continued = 0;
335 	    }
336 	    else {
337 	       prev_continued = 1;
338 	    }
339 	    fputs("\n", _file);
340 	    free(temp);
341 	 }
342 	 else if ((!(line->flags & NO_EOL_FLAG)) &&
343 		  (is_empty(strip_html(line->text))) &&
344 		  (!strstr(line->text, "<li>"))) {
345 	    _hfprintf("%s\n", line->text);
346 	    if (!block_empty_lines)
347 	       _empty_count++;
348 	 }
349 	 else if (strstr(line->text, "<email>") || strstr(line->text, "<link>")) {
350 	    _output_buffered_text();
351 	    _output_custom_markers(line);
352 	 }
353 	 else {
354 	    /* output a normal line */
355 	    _output_buffered_text();
356 	    if ((html_flags & HTML_SPACED_LI) && strstr(line->text, "<li>"))
357 	       fputs("<br><br>", _file);
358 	    _hfputs(line->text);
359 	    fputs("\n", _file);
360 	 }
361 
362 	 /* mutex to control extra line output in preformated sections */
363 	 ret = _detect_non_paragraph_sections(line->text);
364 	 if (ret) {
365 	    if(!block_empty_lines)
366 	       _empty_count = 0;
367 	    block_empty_lines += ret;
368 	 }
369       }
370       else if (line->flags & TOC_FLAG) {
371 	 _write_html_ref_list(_xref, &_xrefs, "xref");
372 	 _write_html_ref_list(_eref, &_erefs, "eref");
373 	 if (flags & MULTIFILE_FLAG) {
374 	    _output_buffered_text();
375 	    _output_toc(filename, 1, 0, -1);
376 	 }
377 	 else if (line->flags & SHORT_TOC_FLAG) {
378 	    _output_buffered_text();
379 	    _output_toc(filename, 0, 1, -1);
380 	 }
381 	 else {
382 	    _output_buffered_text();
383 	    _output_toc(filename, 1, 1, -1);
384 	 }
385       }
386       else if (line->flags & XREF_FLAG) {
387 	 if (line->flags & EREF_FLAG)
388 	    _eref[_erefs++] = line->text; /* buffer example reference */
389 	 else
390 	    _xref[_xrefs++] = line->text; /* buffer cross reference */
391       }
392       else if (line->flags & INDEX_FLAG) {
393 	 _output_buffered_text();
394 	 _output_symbol_index();
395       }
396 
397       /* Keep track of continuous definitions */
398       last_line_was_a_definition = (line->flags & DEFINITION_FLAG);
399 
400       line = line->next;
401    }
402 
403    if ((flags & MULTIFILE_FLAG) && (section_number > 1))
404       _output_html_footer(filename);
405 
406    _close_html_file(_file);
407 
408    _post_process_pending_refs();
409    free(html_see_also_text);
410    free(html_examples_using_this_text);
411    free(html_return_value_text);
412    free(_full_eref_list);
413    destroy_types_lookup_table(auto_types, 0);
414 
415    return 0;
416 }
417 
418 
419 
420 /* _output_custom_markers:
421  * Converts the <email> and <link> custom markers to proper html code.
422  */
_output_custom_markers(LINE * line)423 static void _output_custom_markers(LINE *line)
424 {
425    char *s2, *s3;
426    /* special munging for links */
427    char *s = line->text;
428 
429    do {
430       s2 = strstr(s, "<email>");
431       s3 = strstr(s, "<link>");
432 
433       if ((s2) || (s3)) {
434 	 int i;
435 	 char buf[256];
436 
437 	 if ((s3) && ((!s2) || (s3 < s2)))
438 	    s2 = s3;
439 
440 	 i = 0;
441 	 while (s < s2)
442 	    buf[i++] = *(s++);
443 
444 	 buf[i] = 0;
445 	 _hfputs(buf);
446 
447 	 while (*s != '>')
448 	    s++;
449 
450 	 s++;
451 	 i = 0;
452 
453 	 while ((*s) && (*s != '<'))
454 	    buf[i++] = *(s++);
455 
456 	 buf[i] = 0;
457 
458 	 if (s2[1] == 'e')
459 	    _hfprintf("<a href=\"mailto:%s\">%s", buf, buf);
460 	 else
461 	    _hfprintf("<a href=\"%s\">%s", buf, buf);
462       }
463    } while (s2);
464 
465    _hfputs(s);
466    fputs("\n", _file);
467 }
468 
469 
470 
471 /* _write_html_ref_list:
472  * Recieves an array of pointers and a pointer to the variable holding
473  * the number of buffered text lines. Returns if there are none waiting
474  * to be printed, otherwise sorts the list and writes a block of cross
475  * references. Type should be the string "xref" or "eref".
476  */
_write_html_ref_list(char ** ref,int * refs,const char * type)477 static void _write_html_ref_list(char **ref, int *refs, const char *type)
478 {
479    int i;
480    assert(html_see_also_text);
481    assert(html_examples_using_this_text);
482    assert(type);
483    assert(*type);
484 
485    if (!(*refs))
486       return;
487 
488    fputs("\n<blockquote", _file);
489    if (!(html_flags & HTML_IGNORE_CSS))
490       fprintf(_file, " class=\"%s\"><em><b>", type);
491    else
492       fputs("><font size=\"-1\" face=\"helvetica,verdana\"><em><b>", _file);
493 
494    /* select different wording depending on ref type */
495    if (*type == 'e')
496       fputs(html_examples_using_this_text, _file);
497    else
498       fputs(html_see_also_text, _file);
499    fputs("</b></em>\n", _file);
500 
501    for (i=0; i<(*refs); i++) {
502       if (i) _hfprintf(",\n");
503       _write_html_ref(ref[i], type);
504    }
505    *refs = 0;
506    _hfprintf(".%s</blockquote>\n",
507       (html_flags & HTML_IGNORE_CSS) ? "</font>" : "");
508    _empty_count = 0;
509 }
510 
511 
512 
513 /* _write_html_ref:
514  * Writes a single cross reference splitting ref if needed.
515  * Type should be the string "xref" or "eref".
516  */
_write_html_ref(char * ref,const char * type)517 static void _write_html_ref(char *ref, const char *type)
518 {
519    char *tok, first = 1;
520 
521    tok = strtok(ref, ",;");
522 
523    while (tok) {
524       while ((*tok) && (myisspace(*tok)))
525 	 tok++;
526       if (!first) _hfprintf(",\n");
527       first = 0;
528       _hfprintf("<a ");
529       if (!(html_flags & HTML_IGNORE_CSS))
530 	 _hfprintf("class=\"%s\" ", type);
531 
532       /* If they are erefs, they aren't on the list, and don't have spaces
533 	 in them, add them to the full list of already seen erefs */
534       if (*type == 'e' && !strchr(tok, ' ') && (!_full_eref_list ||
535 	  !strstr(_full_eref_list, tok))) {
536 	 _full_eref_list = m_strcat(_full_eref_list, tok);
537 	 _full_eref_list = m_strcat(_full_eref_list, " ");
538       }
539 
540       if (flags & MULTIFILE_FLAG)
541 	 _hfprintf("href=\"post_process#%s\" title=\"@SHORTDESC %s@\">%s</a>",
542 	    tok, tok, tok);
543       else
544 	 _hfprintf("href=\"#%s\" title=\"@SHORTDESC %s@\">%s</a>",
545 	    tok, tok, tok);
546 
547       tok = strtok(NULL, ",;");
548    }
549 }
550 
551 /* _write_css_file:
552  * Writes a separate CSS file. Requires two path components: the first
553  * one is the normal html path, the second is a relative path indicating
554  * where to put the css file. The final path will be a composition of
555  * both.
556  */
_write_css_file(char * html_path,char * css_filename)557 static void _write_css_file(char *html_path, char *css_filename)
558 {
559    FILE *file;
560    char *full_path;
561 
562    assert(html_path);
563    assert(css_filename);
564 
565    full_path = m_replace_filename(html_path, css_filename);
566    file = fopen(full_path, "wt");
567 
568    if (!file) {
569       free(full_path);
570       return;
571    }
572 
573    fputs(_css_data1, file);
574    fputs(_css_data2, file);
575    fclose(file);
576    free(full_path);
577 
578    return;
579 }
580 
581 
582 
583 /* _output_html_footer:
584  * Writes that "Back to contets" message at the end of a file with a link.
585  */
_output_html_footer(char * main_filename)586 static void _output_html_footer(char *main_filename)
587 {
588    assert(html_footer);
589    if (html_flags & HTML_OLD_F_TAG_FLAG)
590       fprintf(_file, html_footer, get_filename(main_filename));
591    else
592       fprintf(_file, "<hr><div class=\"al-back-to-contents\"><a href"
593 	 "=\"%s\">%s</a></div>\n", get_filename(main_filename), html_footer);
594 }
595 
596 
597 
598 /* _output_toc:
599  */
_output_toc(char * filename,int root,int body,int part)600 static void _output_toc(char *filename, int root, int body, int part)
601 {
602    char name[256];
603    char *s;
604    TOC *toc;
605    int nested = 0;
606 
607    #define ALT_TEXT(toc)   ((toc->alt) ? toc->alt : toc->text)
608 
609    if (root) {
610       int section_number = 0;
611       toc = tochead;
612       if (toc)
613 	 toc = toc->next;
614 
615       fprintf(_file, "<ul>\n");
616 
617       while (toc) {
618 	 if (toc->root) {
619             if (toc->htmlable) {
620                if (toc->otherfile) {
621                   sprintf(name, "%s.%s", toc->text, html_extension);
622                   mystrlwr(name);
623                   _hfprintf("<li><a href=\"%s\">%s</a>\n", name, ALT_TEXT(toc));
624                }
625                else if (body) {
626                   _hfprintf("<li><a href=\"#%s\">%s</a>\n", toc->text, ALT_TEXT(toc));
627                   section_number++;
628                }
629                else {
630                   strcpy(name, filename);
631                   s = get_extension(name)-1;
632                   if (s - get_filename(name) > 5)
633                      s = get_filename(name)+5;
634                   sprintf(s, "%03d.%s", section_number, html_extension);
635                   _hfprintf("<li><a href=\"%s\">%s</a>\n", get_filename(name), ALT_TEXT(toc));
636                   section_number++;
637                }
638             }
639             else if (toc->root == 2) {
640                _hfprintf("</ul><p>%s</p><ul>\n", ALT_TEXT(toc));
641             }
642             else if (toc->root == 3) {
643                _hfprintf("</ul><ul>\n");
644             }
645 	 }
646 	 toc = toc->next;
647       }
648 
649       fprintf(_file, "</ul>\n");
650    }
651 
652    if (body) {
653       toc = tochead;
654       if (toc)
655 	 toc = toc->next;
656 
657       if (part <= 0) {
658 	 TOC *ptr[TOC_SIZE];
659 	 int i = 0;
660 	 if (root)
661 	    fprintf(_file, "<ul>\n");
662 	 else
663 	    fprintf(_file, "<ul>\n");
664 
665 	 while (toc) {
666 	    if ((toc->htmlable) && (!toc->otherfile)) {
667 	       if (!toc->root) {
668 		  if (!nested) {
669 		     fprintf(_file, "<ul>\n");
670 		     nested = 1;
671 		  }
672 	       }
673 	       else {
674 		  if (nested) {
675 		     _output_sorted_nested_toc(ptr, i);
676 		     nested = i = 0;
677 		  }
678 	       }
679 
680 	       if (nested) {
681 		  if (i < TOC_SIZE)
682 		     ptr[i++] = toc;
683 	       }
684 	       else
685 		  _hfprintf("<li><a href=\"#%s\">%s</a>\n", toc->text, ALT_TEXT(toc));
686 	    }
687 
688 	    toc = toc->next;
689 	 }
690 
691 	 if (nested)
692 	    _output_sorted_nested_toc(ptr, i);
693 
694 	 if (root)
695 	    fprintf(_file, "</ul>\n");
696 	 else
697 	    fprintf(_file, "</ul>\n");
698       }
699       else if (!(html_flags & HTML_OPTIMIZE_FOR_CHM) &&
700                !(html_flags & HTML_OPTIMIZE_FOR_DEVHELP)) {
701 	 /* Outputs the function TOC for the current section. */
702 	 TOC *ptr[TOC_SIZE];
703 	 int j, i = 0;
704 	 int section_number = 0;
705 	 fprintf(_file, "\n<ul>\n");
706 
707 	 while ((toc) && (section_number < part)) {
708 	    if ((toc->htmlable) && (toc->root) && (!toc->otherfile))
709 	       section_number++;
710 	    toc = toc->next;
711 	 }
712 
713 	 while ((toc) && (!toc->root) && (i < TOC_SIZE)) {
714 	    if (toc->htmlable)
715 	       ptr[i++] = toc;
716 	    toc = toc->next;
717 	 }
718 
719 	 if (i > 1)
720 	    qsort(ptr, i, sizeof(TOC *), _toc_scmp);
721 
722 	 for (j = 0; j < i; j++) {
723 	    _hfprintf("<li><a href=\"#%s\">%s</a>", ptr[j]->text, ALT_TEXT(ptr[j]));
724 	    fprintf(_file, " &mdash; ");
725 	    _hfprintf("@SHORTDESC %s@\n", ALT_TEXT(ptr[j]));
726 	 }
727 
728 	 fprintf(_file, "</ul>\n");
729       }
730    }
731 }
732 
733 
734 
735 /* _hfprintf:
736  */
_hfprintf(char * format,...)737 static void _hfprintf(char *format, ...)
738 {
739    char buf[1024];
740 
741    va_list ap;
742    va_start(ap, format);
743    vsprintf(buf, format, ap);
744    va_end(ap);
745 
746    _hfputs(buf);
747 }
748 
749 
750 
751 /* _hfputs:
752  * Outputs string to _file. At the same time some things are substituted:
753  * (&gt) for (&gt;), (&lt) for (&lt;), (&) for (&amp;) and (`word') for
754  * (<tt>`word'</tt>).
755  */
_hfputs(char * string)756 static void _hfputs(char *string)
757 {
758    char *end = 0;
759    while (*string) {
760       /* Found simple text comma? */
761       if ((end == 0) && (string[0] == '`')) {
762 	 end = strchr(string + 1, 39);
763 	 /* Is there a closing comma? */
764 	 if (end) {
765 	    char *p = string + 1;
766 	    /* Check for a specific character range */
767 	    while ((p < end) && ((*p >= '.') && (*p <= 'z')))
768 	       p++;
769 
770 	    if ((p == end) && (p - string > 1)) {
771 	       fputs("<tt>`", _file);
772 	       string++;
773 	       continue;
774 	    } else {
775 	       end = 0;
776 	    }
777 	 }
778       }
779       /* Is closing comma search active? */
780       if ((string[0] == 39) && end == string) {
781 	 fputs("'</tt>", _file);
782 	 string++;
783 	 end = 0;
784 	 continue;
785       }
786       if ((string[0] == '&') &&
787 	  ((string[1] == 'l') || (string[1] == 'g')) &&
788 	  (string[2] == 't')) {
789 
790 	 fputc('&', _file);
791 	 fputc(string[1], _file);
792 	 fputc('t', _file);
793 	 fputc(';', _file);
794 
795 	 string += 3;
796       }
797       else if ((string[0] == '&') && (string[1] != 'l') &&
798 	  (string[1] != 'g')) {
799 
800 	 fputs("&amp;", _file);
801 	 string++;
802       }
803       else {
804 	 fputc(*string, _file);
805 	 string++;
806       }
807    }
808 }
809 
810 
811 
812 /* _toc_scmp:
813  * qsort helper which compares two TOC nodes alphabeticaly.
814  */
_toc_scmp(const void * e1,const void * e2)815 static int _toc_scmp(const void *e1, const void *e2)
816 {
817    TOC *t1 = *((TOC **)e1);
818    TOC *t2 = *((TOC **)e2);
819 
820    return mystricmp(t1->text, t2->text);
821 }
822 
823 
824 
825 /* _str_cmp:
826  * qsort helper which compares two tokens alphabetically.
827  */
_str_cmp(const void * s1,const void * s2)828 static int _str_cmp(const void *s1, const void *s2)
829 {
830    const char *t1 = *((const char **)s1);
831    const char *t2 = *((const char **)s2);
832    return strcmp(t1, t2);
833 }
834 
835 
836 
837 /* _output_section_heading:
838  * Used to mark a new section of the document. In multifile output closes
839  * the actual file and writes a new one.
840  */
_output_section_heading(LINE * line,char * filename,int section_number)841 static int _output_section_heading(LINE *line, char *filename, int section_number)
842 {
843    char buf[256], *s;
844 
845    /* output a section heading, switching file as required */
846    if ((flags & MULTIFILE_FLAG) && (section_number > 0)) {
847       if (section_number > 1)
848 	 _output_html_footer(filename);
849 
850       _close_html_file(_file);
851       strcpy(buf, filename);
852       s = get_extension(buf)-1;
853       if (s - get_filename(buf) > 5)
854 	 s = get_filename(buf)+5;
855       sprintf(s, "%03d.%s", section_number-1, html_extension);
856       /*printf("writing %s\n", buf);*/
857       _file = fopen(buf, "w");
858       if (!_file)
859 	 return 1;
860       strcpy(_filename, buf);
861       _output_html_header(line->text);
862    }
863    _hfprintf("<h1>%s</h1>\n", line->text);
864    if ((flags & MULTIFILE_FLAG) &&
865        (!(line->flags & NOCONTENT_FLAG)) &&
866        (section_number > 0)) {
867       _output_toc(filename, 0, 1, section_number);
868    }
869    return 0;
870 }
871 
872 
873 
874 /* _output_html_header:
875  * The header of an HTML document is a little bit complex, so it deserves
876  * it's own function. In the header you have the title of the document,
877  * the character set definition and the style sheet specification. This
878  * function just outputs to _file the correct tags, it presumes _file
879  * has just been opened. The title is specified in the source document
880  * with a @document_title= line, and section can be another text which will
881  * be appended to the title.
882  */
_output_html_header(char * section)883 static void _output_html_header(char *section)
884 {
885    assert(html_document_title);
886 
887    if (html_flags & HTML_OLD_H_TAG_FLAG) {
888       if (section && strlen(strip_html(section))) {
889 	 int i;
890 	 for (i=0; html_document_title[i]; i++) {
891 	    if (html_document_title[i] == '#')
892 	       fputs(strip_html(section), _file);
893 	    else
894 	       fputc(html_document_title[i], _file);
895 	 }
896       }
897    }
898    else {
899       fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n", _file);
900       fputs("<html><head><title>\n", _file);
901       fputs(html_document_title, _file);
902       if (section && strlen(strip_html(section))) {
903 	 fputs(": ", _file);
904 	 fputs(strip_html(section), _file);
905       }
906       fputs("\n</title>\n", _file);
907       fputs("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=", _file);
908       fputs(charset, _file);
909       fputs("\">\n", _file);
910 
911       /* optional style sheet output */
912       if (!(html_flags & HTML_IGNORE_CSS)) {
913 	 fputs("<meta http-equiv=\"Content-Style-Type\" content=\"text/css\">\n", _file);
914 	 if (html_css_filename) {
915 	    fprintf(_file, "<link rel=\"stylesheet\" title=\"Default\" type=\"text/css\" href=\"%s\">", html_css_filename);
916 	 }
917 	 else {
918 	    fputs("<style type=\"text/css\" title=\"Default\"><!--\n", _file);
919 	    fputs(_css_data1, _file);
920 	    fputs(_css_data2, _file);
921 	    fputs("\n\n--></style>\n", _file);
922 	 }
923       }
924 
925       /* header end and body start */
926       fputs("</head><body bgcolor=white text=black link=\"#0000ee\" alink=\"#ff0000\" vlink=\"#551a8b\">\n", _file);
927    }
928 }
929 
930 
931 
932 /* _output_buffered_text:
933  * Outputs buffered text, which might be a set of cross references
934  * followed by a number of empty lines, which will be transformed
935  * to apropiate HTML tags.
936  */
_output_buffered_text(void)937 static void _output_buffered_text(void)
938 {
939    if (_empty_count) {
940       if (_empty_count > 1)
941 	 fputs("<p><br>\n", _file);
942       else
943 	 fputs("<p>\n", _file);
944 
945       _empty_count = 0;
946    }
947 }
948 
949 
950 
951 /* _post_process_pending_refs:
952  * Reopens all the created files and scans for post process tags, which
953  * will be replaced with correct filenames according to the post process
954  * info in memory. Additionally, it will also look for text substitutions
955  * placed in the html_text_substitution global array.
956  */
_post_process_pending_refs(void)957 static void _post_process_pending_refs(void)
958 {
959    int f;
960    if (!_post) return ;
961 
962    _prepare_html_text_substitution();
963    for(f = 0; _post[f]; f++)
964       _post_process_filename(_post[f]->filename);
965 
966    for(f = 0; _post[f]; f++)
967       _destroy_post_page(_post[f]);
968 
969    _free_html_text_substitution_memory();
970    free(_post);
971    _post = 0;
972 }
973 
974 
975 
976 /* _add_post_process_ref:
977  * Adds a token to a buffer with the current filename for a post process.
978  * The type of the reference will be stored as the first character of
979  * the clean token, and a comma will separate its short description if
980  * there is any available.
981  */
_add_post_process_ref(const LINE * line,char type)982 static void _add_post_process_ref(const LINE *line, char type)
983 {
984    const char *token, *short_desc;
985    char *clean_token;
986    POST *p;
987    assert(line);
988 
989    token = line->text;
990    clean_token = get_clean_ref_token(token);
991    /* ignore empty tokens, or those with space at the beginning */
992    if (!clean_token[0] || myisspace(clean_token[0])) {
993       free(clean_token);
994       return ;
995    }
996 
997    p = _search_post_section(_filename);
998    if (!p)
999       p = _create_post_section(_filename);
1000 
1001    short_desc = _find_short_description(line);
1002 
1003    /* Reserve memory for the list, and the token with type. */
1004    p->token = m_xrealloc(p->token, sizeof(char*) * (1 + p->num));
1005    p->token[p->num] = m_strcat(m_strdup(" "), clean_token);
1006    p->token[p->num][0] = type;
1007    /* If we found a short description, add it with a comma. */
1008    if (short_desc) {
1009       p->token[p->num] = m_strcat(p->token[p->num], ",");
1010       p->token[p->num] = m_strcat(p->token[p->num], short_desc);
1011    }
1012    p->num++;
1013 }
1014 
1015 
1016 
1017 /* _search_post_section:
1018  * Searches the globals for an existant POST page which matches filename.
1019  * Returns the POST page or NULL if none was found.
1020  */
_search_post_section(const char * filename)1021 static POST *_search_post_section(const char *filename)
1022 {
1023    int f;
1024 
1025    if (!_post)
1026       return 0;
1027 
1028    for(f = 0; _post[f]; f++)
1029       if (!strcmp(_post[f]->filename, filename))
1030 	 return _post[f];
1031 
1032    return 0;
1033 }
1034 
1035 
1036 
1037 /* _search_post_section_with_token:
1038  * Similar to _search_post_section, but instead of matching filenames,
1039  * returns the page containing the specified token, NULL if none was found.
1040  */
_search_post_section_with_token(const char * token)1041 static POST *_search_post_section_with_token(const char *token)
1042 {
1043    int f, g, len;
1044    if (!_post)
1045       return 0;
1046 
1047    len = strlen(token);
1048 
1049    for(f = 0; _post[f]; f++) {
1050       for(g = 0; g < _post[f]->num; g++) {
1051 	 if (!strncmp(_post[f]->token[g] + 1, token, len)) {
1052 	    /* Still, we have to verify the tokens have correct size. */
1053 	    const char *desc = strchr(_post[f]->token[g], ',');
1054 	    if (desc && (desc - _post[f]->token[g] - 1) == len)
1055 	       return _post[f];
1056 	    else if (!desc && strlen(_post[f]->token[g] + 1) == (size_t)len)
1057 	       return _post[f];
1058 	 }
1059       }
1060    }
1061    return 0;
1062 }
1063 
1064 
1065 
1066 /* _look_up_short_description:
1067  * Retrieves from the cached short description the one which matches
1068  * len characters from the string token. Returns NULL if none is found.
1069  */
_look_up_short_description(const char * token,int len)1070 static const char *_look_up_short_description(const char *token, int len)
1071 {
1072    int f, g;
1073    if (!_post)
1074       return 0;
1075 
1076    for(f = 0; _post[f]; f++) {
1077       for(g = 0; g < _post[f]->num; g++) {
1078 	 /* First see if this entry has a short description. */
1079 	 const char *desc = strchr(_post[f]->token[g], ',');
1080 	 if (!desc)
1081 	    continue;
1082 	 /* Does the found token have the lenght we are looking for? */
1083 	 if ((desc - _post[f]->token[g] - 1) != len)
1084 	    continue;
1085 	 /* Verify that the token is the same and has correct length. */
1086 	 if (!strncmp(_post[f]->token[g] + 1, token, len))
1087 	    return desc + 1; /* Avoid the comma. */
1088       }
1089    }
1090    return 0;
1091 }
1092 
1093 
1094 
1095 /* _create_post_section:
1096  * Adds one POST section matching filename to the global array of pages
1097  * after creating a new POST page, which will be returned.
1098  */
_create_post_section(const char * filename)1099 static POST *_create_post_section(const char *filename)
1100 {
1101    int num;
1102    POST *p;
1103 
1104    p = m_xmalloc(sizeof(POST));
1105    p->token = m_xmalloc(sizeof(char*));
1106    p->num = 0;
1107    strcpy(p->filename, filename);
1108 
1109    if (!_post) {
1110       _post = m_xmalloc(sizeof(POST*));
1111       _post[0] = 0;
1112    }
1113 
1114    for(num = 0; _post[num]; num++)
1115       ;
1116 
1117    _post = m_xrealloc(_post, sizeof(POST*) * (2 + num));
1118    _post[num++] = p;
1119    _post[num] = 0;
1120 
1121    return p;
1122 }
1123 
1124 
1125 
1126 /* _destroy_post_page:
1127  * Frees the memory used by a post page.
1128  */
_destroy_post_page(POST * p)1129 static void _destroy_post_page(POST *p)
1130 {
1131    int f;
1132    for(f = 0; f < p->num; f++)
1133       free(p->token[f]);
1134 
1135    free(p->token);
1136    free(p);
1137 }
1138 
1139 
1140 
1141 /* _post_process_filename:
1142  * Opens the filename and replaces the post process tags.
1143  */
_post_process_filename(char * filename)1144 static void _post_process_filename(char *filename)
1145 {
1146    int code_scanning = 0;
1147    char *new_filename, *p;
1148    char *line;
1149    FILE *f1 = 0, *f2 = 0;
1150 
1151    new_filename = m_strdup(filename);
1152    if (!new_filename || strlen(new_filename) < 2) goto cancel;
1153    new_filename[strlen(filename)-1] = 'x';
1154 
1155    f1 = fopen(filename, "rt");
1156    f2 = fopen(new_filename, "wt");
1157    if (!f1 || !f2) goto cancel;
1158 
1159    /*printf("post processing %s\n", filename);*/
1160 
1161    code_scanning = 0;
1162 
1163    while ((line = m_fgets(f1))) {
1164       /* Transform read lines into hyperlinks with post process marks. */
1165       line = _scan_line_for_code_tokens(line, &code_scanning);
1166 
1167       /* Loop over pending filename post process marks. */
1168       while ((p = strstr(line, "\"post_process#"))) {
1169 	 char *clean_token = get_clean_ref_token(p + 2);
1170 	 POST *page = _search_post_section_with_token(clean_token);
1171 	 if (!page) {
1172 	    printf("Didn't find ref for %s (clean: %s)\n", line, clean_token);
1173 	    memmove(p, p + 14, strlen(p + 14) + 1);
1174 	 }
1175 	 else {
1176 	    char *temp = m_xmalloc(1 + strlen(line) + 13 +
1177 			 strlen(get_filename(page->filename)));
1178 	    strncpy(temp, line, p - line + 1);
1179 
1180 	    if (!(html_flags & HTML_OPTIMIZE_FOR_DEVHELP) && !strcmp(filename, page->filename))
1181 	       strcpy(&temp[p - line + 1], p + 13);
1182 	    else {
1183 	       strcpy(&temp[p - line + 1], get_filename(page->filename));
1184 	       strcat(temp, p + 13);
1185 	    }
1186 	    free(line);
1187 	    line = temp;
1188 	 }
1189 	 free(clean_token);
1190       }
1191 
1192       /* Loop over pending short description marks. */
1193       while ((p = strstr(line, "@SHORTDESC "))) {
1194 	 const char *desc;
1195 	 char *end = strchr(p + 11, '@');
1196 	 if (!end) {
1197 	    printf("Didn't find closing of @SHORTDESC!\n");
1198 	    abort();
1199 	 }
1200 	 desc = _look_up_short_description(p + 11, end - p - 11);
1201 	 if (desc) {
1202 	    /* Substitute with found test. */
1203 	    char *temp = m_xmalloc(1 + strlen(line) + strlen(desc));
1204 	    strncpy(temp, line, p - line);
1205 	    strcpy(temp + (p - line), desc);
1206 	    strcat(temp, end + 1); /* Don't copy the @ from end. */
1207 	    free(line);
1208 	    line = temp;
1209 	 }
1210 	 else {
1211 	    /* Remove the description then. */
1212 	    if (strncmp(p - 8, "&mdash; ", 8) == 0) {
1213 	       /* Remove the previous dash too. */
1214 	       p -= 8;
1215 	    }
1216 	    memmove(p, end + 1, strlen(end));
1217 	 }
1218       }
1219       line = _do_text_substitution(line);
1220       fputs(line, f2);
1221       free(line);
1222    }
1223 
1224    fclose(f1);
1225    fclose(f2);
1226    f1 = f2 = 0;
1227 
1228    if (remove(filename)) goto cancel;
1229    rename(new_filename, filename);
1230 
1231    cancel:
1232    if (f1) fclose(f1);
1233    if (f2) fclose(f2);
1234    if (new_filename) {
1235       remove(new_filename);
1236       free(new_filename);
1237    }
1238 }
1239 
1240 
1241 
1242 /* _verify_correct_input:
1243  * Goes through some bits marked during _tx read and verifies that the
1244  * input file is ok, returning non-zero with bad files.
1245  */
_verify_correct_input(void)1246 static int _verify_correct_input(void)
1247 {
1248    int ret = 0;
1249 
1250    if (html_flags & HTML_OLD_H_TAG_FLAG) {
1251       printf("The tag '@h=<html><head><title>#</title></head><body>' and its variant ");
1252       printf("is obsolete.\nPlease use '@document_title blah blah blah' instead.\n");
1253       printf("Otherwise the output won't be valid HTML code and won't use CSS.\n");
1254       printf("And make sure you aren't using any <head>,<title> or <body> tags!.\n");
1255    }
1256 
1257    if (!(html_flags & HTML_DOCUMENT_TITLE_FLAG) && (flags & MULTIFILE_FLAG)) {
1258       printf("Missing tag for document title. You should use one of these:\n");
1259       printf("@document_title=Allegro manual. (this is recommended)\n ...or...\n");
1260       printf("@h=<html><head><title>#</title></head><body>.\n...and later...\n");
1261       printf("@<html>\n@<head>\n@<title>Allegro - a game programming library</title></head>\n@<body>\n");
1262       ret++;
1263    }
1264 
1265    if (html_flags & HTML_OLD_F_TAG_FLAG) {
1266       printf("The tags '@f=blah', '@f1=blah' and '@f2=blah' are obsolete.\n");
1267       printf("Please use '@html_footer=Back to contents' or something similar instead.\n");
1268       printf("And make sure you aren't using any <head>,<title> or <body> tags!.\n");
1269    }
1270 
1271    if (!(html_flags & HTML_FOOTER_FLAG) && (flags & MULTIFILE_FLAG)) {
1272       printf("For multifile documents please use '@html_footer=Back to contents'.\n");
1273    }
1274 
1275    return ret;
1276 }
1277 
1278 
1279 
1280 /* _close_html_file:
1281  * Closes the stream after writting the apropiate html close tags.
1282  */
_close_html_file(FILE * file)1283 static void _close_html_file(FILE *file)
1284 {
1285    fputs("\n</body>\n</html>\n", file);
1286    fclose(file);
1287 }
1288 
1289 
1290 
1291 /* _output_sorted_nested_toc:
1292  * Outputs the buffered TOC list, according to num_items. After the
1293  * list a </ul> is appended to the text.
1294  */
_output_sorted_nested_toc(TOC ** list,unsigned int num_items)1295 static void _output_sorted_nested_toc(TOC **list, unsigned int num_items)
1296 {
1297    unsigned int f;
1298 
1299    if (num_items > 1)
1300       qsort(list, num_items, sizeof(TOC *), _toc_scmp);
1301 
1302    for (f = 0; f < num_items; f++) {
1303       _hfprintf("<li><a href=\"#%s\">%s</a>", list[f]->text, ALT_TEXT(list[f]));
1304       fprintf(_file, " &mdash; ");
1305       _hfprintf("@SHORTDESC %s@\n", ALT_TEXT(list[f]));
1306    }
1307 
1308    fprintf(_file, "</ul>\n");
1309 }
1310 
1311 
1312 
1313 /* _detect_non_paragraph_sections:
1314  * Used on a line of text, detects if the line contains tags after which the
1315  * paragraph tag <p> can't exist because it would break html validity.
1316  * The return value is a bit special: it
1317  * will be zero if no tags where found. For each opening tag the function
1318  * will add one to the return value. Equally, each closing tag will substract
1319  * one from the return value. Examples:
1320  *     <pre>..</pre>...<pre>      will return 1
1321  *     <pre>..<pre>...<pre>       will return 3 (note that HTML isn't valid)
1322  *     ..</pre>..                 will return -1
1323  */
_detect_non_paragraph_sections(const char * text)1324 static int _detect_non_paragraph_sections(const char *text)
1325 {
1326    static const char *tags[] = {"pre>", "ul>", "h1>", "h2>", "h3>", "h4>", "h5>", "h6>",
1327       "i>", "strong>", "em>", "b>", 0};
1328    const char *p = text;
1329    int f, ret = 0;
1330    assert(text);
1331 
1332    while ((p = strchr(p, '<'))) {
1333       p++;
1334       if (*p == '/')
1335 	 p++;
1336 
1337       for (f = 0; tags[f]; f++) {
1338 	 if (strncmp(p, tags[f], strlen(tags[f])) == 0) {
1339 	    if (*(p-1) == '/')
1340 	       ret--;
1341 	    else
1342 	       ret++;
1343 	 }
1344       }
1345    }
1346    return ret;
1347 }
1348 
1349 
1350 /* _mark_up_auto_types:
1351  * Instead of flooding the xref section with detected autotypes, and in
1352  * order to obtain a higher coolness factor, autotypes are detected at
1353  * definition lines and turned into hyperlinks. For this to happen you
1354  * need to pass the text line you would output for a normal definition
1355  * line, but it HAS to be dynamic memory (ie: m_strdup'ed). Pass a
1356  * previously generated auto_types table.
1357  * line has to be dynamic because it will be expanded whenever auto types
1358  * are found. Since this is done reallocating, this function will return
1359  * the new line pointer you should be using after calling this.
1360  */
_mark_up_auto_types(char * line,char ** auto_types)1361 static char *_mark_up_auto_types(char *line, char **auto_types)
1362 {
1363    int length;
1364    char *p;
1365    assert(line);
1366    assert(auto_types);
1367 
1368    /* Did we find some auto type? */
1369    while ((p = strstruct(line, auto_types, &length, 0))) {
1370       char *temp = m_strdup(p);
1371       *p = 0;
1372       if (flags & MULTIFILE_FLAG) {
1373 	 int offset = 22;
1374 	 char *mark = m_xmalloc(3 * length + 47 + 21);
1375 	 if (html_flags & HTML_IGNORE_CSS)
1376 	    strcpy(mark, "<a href=\"post_process#");
1377 	 else {
1378 	    strcpy(mark, "<a class=\"autotype\" href=\"post_process#");
1379 	    offset += 17;
1380 	 }
1381 	 strncpy(mark + offset, temp, length);
1382 	 offset += length;
1383 	 strcpy(mark + offset, "\" title=\"@SHORTDESC ");
1384 	 offset += 20;
1385 	 strncpy(mark + offset, temp, length);
1386 	 offset += length;
1387 	 strcpy(mark + offset, "@\">");
1388 	 offset += 3;
1389 	 strncpy(mark + offset, temp, length);
1390 	 offset += length;
1391 	 strcpy(mark + offset, "</a>");
1392 	 line = m_strcat(line, mark);
1393 	 free(mark);
1394 	 line = m_strcat(line, temp + length);
1395       }
1396       else {
1397 	 int offset = 10;
1398 	 char *mark = m_xmalloc(3 * length + 35 + 21);
1399 	 if (html_flags & HTML_IGNORE_CSS)
1400 	    strcpy(mark, "<a href=\"#");
1401 	 else {
1402 	    strcpy(mark, "<a class=\"autotype\" href=\"#");
1403 	    offset += 17;
1404 	 }
1405 	 strncpy(mark + offset, temp, length);
1406 	 offset += length;
1407 	 strcpy(mark + offset, "\" title=\"@SHORTDESC ");
1408 	 offset += 20;
1409 	 strncpy(mark + offset, temp, length);
1410 	 offset += length;
1411 	 strcpy(mark + offset, "@\">");
1412 	 offset += 3;
1413 	 strncpy(mark + offset, temp, length);
1414 	 offset += length;
1415 	 strcpy(mark + offset, "</a>");
1416 	 line = m_strcat(line, mark);
1417 	 free(mark);
1418 	 line = m_strcat(line, temp + length);
1419       }
1420       free(temp);
1421    }
1422    return line;
1423 }
1424 
1425 
1426 
1427 /* _prepare_html_text_substitution():
1428  * The html_text_substitution array has pointers to text lines in the form
1429  * word|text. This function fills a global array with duplicates of word,
1430  * and at the same time makes html_text_substitution point directly at
1431  * text. With two arrays it is easier to search for words and replace with
1432  * the text when they are found.
1433  */
_prepare_html_text_substitution(void)1434 static void _prepare_html_text_substitution(void)
1435 {
1436    int f;
1437    char *p;
1438 
1439    for (f = 0; html_text_substitution[f]; f++) {
1440       assert(f < 256);
1441       p = strchr(html_text_substitution[f] + 1, '|');
1442       assert(p);
1443       *p = 0;
1444       _word_substitution[f] = m_strdup(html_text_substitution[f]);
1445       p = m_strdup(p + 1);
1446       free(html_text_substitution[f]);
1447       html_text_substitution[f] = p;
1448    }
1449 }
1450 
1451 
1452 
1453 /* _free_html_text_substitution_memory:
1454  * Frees memory stored in the _word_substitution global array.
1455  */
_free_html_text_substitution_memory(void)1456 static void _free_html_text_substitution_memory(void)
1457 {
1458    int f;
1459 
1460    for (f = 0; _word_substitution[f]; f++) {
1461       free(html_text_substitution[f]);
1462       free(_word_substitution[f]);
1463    }
1464 }
1465 
1466 
1467 
1468 /* _do_text_substitution:
1469  * Given a malloc'ed string, searches all _word_substitution in it and
1470  * replaces them with html_text_substition if found. Returns a new
1471  * string or the one you passed if no changes were made.
1472  */
_do_text_substitution(char * input)1473 static char *_do_text_substitution(char *input)
1474 {
1475    int start, end, middle, f;
1476    char *temp, *found, *reader;
1477 
1478    for (f = 0; _word_substitution[f]; f++) {
1479       reader = input;
1480       while ((found = strstr(reader, _word_substitution[f]))) {
1481 	 /* Find lengths */
1482 	 start = found - input;
1483 	 middle = strlen(html_text_substitution[f]);
1484 	 end = strlen(found + strlen(_word_substitution[f]));
1485 	 /* Create new string and free old one */
1486 	 temp = m_xmalloc(start + middle + end + 1);
1487 	 strncpy(temp, input, start);
1488 	 strcpy(temp + start, html_text_substitution[f]);
1489 	 strcat(temp, found + strlen(_word_substitution[f]));
1490 	 free(input);
1491 	 /* Prepare for next loop */
1492 	 input = temp;
1493 	 reader = temp + start + middle;
1494       }
1495    }
1496 
1497    return input;
1498 }
1499 
1500 
1501 
1502 /* _output_symbol_index:
1503  * Called at the end of the first html pass, generates a list of links
1504  * to the so far found tokens in the text, which will be hyperlinked
1505  * correctly during the second pass.
1506  */
_output_symbol_index(void)1507 static void _output_symbol_index(void)
1508 {
1509    int f, g, num = 0;
1510    char last_letter;
1511    char **list = NULL;
1512 
1513    for(f = 0; _post[f]; f++) {
1514       for(g = 0; g < _post[f]->num; g++) {
1515 	 char *tok = _post[f]->token[g];
1516 	 /* filter text tokens, they are not interesting */
1517 	 if (tok[0] == TOKEN_TEXT)
1518 	    continue;
1519 	 tok++;
1520 	 /* filter normal tokens from the example list */
1521 	 if (_full_eref_list && strstr(_full_eref_list, tok))
1522 	    continue;
1523 	 list = m_xrealloc(list, sizeof(char *) * (num + 1));
1524 	 list[num++] = tok;
1525       }
1526    }
1527 
1528    if (num < 1)
1529       return ;
1530 
1531    qsort(list, num, sizeof(char *), _str_cmp);
1532 
1533    /* Create minitoc scanning first letters of sorted list. */
1534    fprintf(_file, "<div class='mini_toc' id='top'>\n\t");
1535    for (f = 0, last_letter = -1; f < num; f++) {
1536       /* Valid range? */
1537       if (list[f][0] >= 'A' && list[f][0] <= 'z') {
1538 	 /* Different letter? */
1539 	 if (last_letter != list[f][0]) {
1540 	    last_letter = list[f][0];
1541 	    fprintf(_file, "<a href='#mini_toc_%c%d'>%c</a>\n\t",
1542 	       last_letter, last_letter > '_', last_letter);
1543 	 }
1544       }
1545    }
1546    fprintf(_file, "\n</div>\n\n");
1547 
1548    for (f = 0, last_letter = -1; f < num; f++) {
1549       char *p, *temp;
1550 
1551       /* Did we change letter section? */
1552       if (list[f][0] >= 'A' && list[f][0] <= 'z' && list[f][0] != last_letter) {
1553 	 /* Is this the first section or another one? */
1554 	 if (last_letter != -1)
1555 	    fprintf(_file, "\n</ul>\n");
1556 
1557 	 last_letter = list[f][0];
1558 	 fprintf(_file, "<h1 class='mini_toc' "
1559 	    "id='mini_toc_%c%d'><a href='#top'>%c</a></h1>\n<ul>\n",
1560 	    last_letter, last_letter > '_', last_letter);
1561       }
1562 
1563       /* Before writing the string, remove possible short descriptions. */
1564       temp = m_strdup(list[f]);
1565       p = strchr(temp, ',');
1566       if (p)
1567 	 *p = 0;
1568 
1569       _hfprintf("<li><a href=\"post_process#%s\">%s</a>", temp, temp);
1570       fprintf(_file, " &mdash; ");
1571       _hfprintf("@SHORTDESC %s@\n", temp);
1572    }
1573    fprintf(_file, "</ul>");
1574 }
1575 
1576 
1577 
1578 /* _find_short_description:
1579  * Given a line, looks forward enough to find a @shortdesc definition.
1580  * Returns a const pointer to the text of the line, or NULL if no
1581  * short definition was found.
1582  */
_find_short_description(const LINE * line)1583 static const char *_find_short_description(const LINE *line)
1584 {
1585    assert(line);
1586    while (1) {
1587       line = line->next;
1588 
1589       if (!line)
1590 	 return 0;
1591 
1592       /* So, did we find a short description? */
1593       if (line->flags & SHORT_DESC_FLAG)
1594 	 return line->text;
1595 
1596       /* Continue parsing through continuations or definitions. */
1597       if (line->flags & (CONTINUE_FLAG | DEFINITION_FLAG))
1598 	 continue;
1599 
1600       /* Abort on beginning of new chapters, nodes, and paragraphs. */
1601       if (line->flags & (HEADING_FLAG | NODE_FLAG | TEXT_FLAG))
1602 	 return 0;
1603 
1604       /* Safe abort on (nearl) empty lines. */
1605       if (strlen(line->text) < 2)
1606 	 return 0;
1607    }
1608    return 0;
1609 }
1610 
1611 
1612 
1613 /* _scan_line_for_code_tokens:
1614  * Transforms line looking for code tokens. line has to be a dynamic
1615  * reallocable string. The code_scanning variable should point to a
1616  * state variable indicating whether the line we are processing should be
1617  * looked for code tokens or not. When looking for code tokens, anything
1618  * in the global token cache will be hyperlinked.
1619  */
_scan_line_for_code_tokens(char * line,int * code_scanning)1620 static char *_scan_line_for_code_tokens(char *line, int *code_scanning)
1621 {
1622    int column = 0;
1623 
1624    assert(line);
1625    assert(code_scanning);
1626 
1627    while (1) {
1628       if (*code_scanning) {
1629 	 int end;
1630 	 /* Substituting text and looking for closing tags. */
1631 	 char *p = strstr(line + column, _codeblock_end);
1632 	 if (!p) {
1633 	    end = strlen(line);
1634 	    return _replace_code_tokens(line, column, &end);
1635 	 }
1636 	 /* Looks like we have to change state. */
1637 	 end = p - line;
1638 	 line = _replace_code_tokens(line, column, &end);
1639 	 column = end;
1640 	 *code_scanning = 0;
1641       }
1642       else {
1643 	 /* Looking for code chunks. */
1644 	 char *p = strstr(line + column, _codeblock_start);
1645 	 if (!p)
1646 	    return line;
1647 	 /* Looks like we did find something. Change state. */
1648 	 column = (p - line) + _codeblock_start_len;
1649 	 *code_scanning = 1;
1650       }
1651    }
1652 }
1653 
1654 
1655 
1656 /* _replace_code_tokens:
1657  * Given a dynamic line, a beginning and an end, this function replaces
1658  * all known tokens with hyperlinks to be post processed. When the new
1659  * line is returned, end will have been updated to point at the new
1660  * end character in the returned string (because it will grow).
1661  */
_replace_code_tokens(char * line,int column,int * end)1662 static char *_replace_code_tokens(char *line, int column, int *end)
1663 {
1664    int found_size;
1665    POST *page;
1666    char buf[256];
1667    char *p, *new_line;
1668    int expanded;
1669 
1670    while ((p = _find_potential_token(line, column, *end, &found_size))) {
1671       assert(found_size < 255);
1672       /* Extract potential token into separate buffer. */
1673       strncpy(buf, p, found_size);
1674       buf[found_size] = 0;
1675 
1676       page = _search_post_section_with_token(buf);
1677       if (!page) {
1678 	 column = p - line + found_size;
1679 	 continue;
1680       }
1681 
1682       column = p - line;
1683       /* Found a token. Resize line and replace token with hyperlink. */
1684       new_line = m_xmalloc(strlen(line) + found_size * 3 + 69);
1685       strncpy(new_line, line, column);
1686       new_line[column] = 0;
1687       strcat(new_line, "<a href=\"post_process#");
1688       strcat(new_line, buf);
1689       strcat(new_line, "\" class=\"autotype\" title=\"@SHORTDESC ");
1690       strcat(new_line, buf);
1691       strcat(new_line, "@\">");
1692       strcat(new_line, buf);
1693       strcat(new_line, "</a>");
1694       expanded = strlen(new_line) - column - found_size;
1695       strcat(new_line, line + column + found_size);
1696 
1697       free(line);
1698       line = new_line;
1699       column += expanded + found_size;
1700       *end = *end + expanded;
1701    }
1702 
1703    return line;
1704 }
1705 
1706 
1707 
1708 /* _find_potential_token:
1709  * In aline, starting at column and endign at end, tries to find potential
1710  * tokens, which is words composed of alphanumeric characters and the
1711  * underscore. If found, a pointer to the token inside the line is
1712  * returned and the length stored in found_size.
1713  */
_find_potential_token(char * line,int column,int end,int * found_size)1714 static char *_find_potential_token(char *line, int column, int end, int *found_size)
1715 {
1716    char *p = line + column;
1717    *found_size = 0;
1718 
1719    #define VALID_CHARACTER()            \
1720       (*p == '_' ||                     \
1721       (*p >= 'A' && *p <= 'Z') ||       \
1722       (*p >= 'a' && *p <= 'z') ||       \
1723       (*p >= '0' && *p <= '9'))
1724 
1725    /* Discard bad characters. */
1726    while (!VALID_CHARACTER()) {
1727       p++;
1728       column++;
1729       if (p - line >= end)
1730          return 0;
1731    }
1732 
1733    /* Now accept good ones. */
1734    while (VALID_CHARACTER()) {
1735       p++;
1736       *found_size = *found_size + 1;
1737       if (column + *found_size == end)
1738          break;
1739    }
1740 
1741    #undef VALID_CHARACTER
1742 
1743    if (*found_size > 0)
1744       return line + column;
1745    else
1746       return 0;
1747 }
1748