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, " — ");
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 * (>) for (>), (<) for (<), (&) for (&) 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("&", _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, "— ", 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, " — ");
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, " — ");
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