1 /**********************************************************************
2 
3   markdown_output.c - functions for printing Elements parsed by
4                       markdown_peg.
5   (c) 2008 John MacFarlane (jgm at berkeley dot edu).
6 
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License or the MIT
9   license.  See LICENSE for details.
10 
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15 
16  ***********************************************************************/
17 
18 #include <stdbool.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <assert.h>
23 #include <glib.h>
24 #include "markdown_peg.h"
25 #include "odf.h"
26 
27 static int extensions;
28 static int odf_type = 0;
29 
30 static void print_html_string(GString *out, char *str, bool obfuscate);
31 static void print_html_element_list(GString *out, element *list, bool obfuscate);
32 static void print_html_element(GString *out, element *elt, bool obfuscate);
33 static void print_latex_string(GString *out, char *str);
34 static void print_latex_element_list(GString *out, element *list);
35 static void print_latex_element(GString *out, element *elt);
36 static void print_groff_string(GString *out, char *str);
37 static void print_groff_mm_element_list(GString *out, element *list);
38 static void print_groff_mm_element(GString *out, element *elt, int count);
39 static void print_odf_code_string(GString *out, char *str);
40 static void print_odf_string(GString *out, char *str);
41 static void print_odf_element_list(GString *out, element *list);
42 static void print_odf_element(GString *out, element *elt);
43 static bool list_contains_key(element *list, int key);
44 
45 /**********************************************************************
46 
47   Utility functions for printing
48 
49  ***********************************************************************/
50 
51 static int padded = 2;      /* Number of newlines after last output.
52                                Starts at 2 so no newlines are needed at start.
53                                */
54 
55 static GSList *endnotes = NULL; /* List of endnotes to print after main content. */
56 static int notenumber = 0;  /* Number of footnote. */
57 
58 /* pad - add newlines if needed */
pad(GString * out,int num)59 static void pad(GString *out, int num) {
60     while (num-- > padded)
61         g_string_append_printf(out, "\n");;
62     padded = num;
63 }
64 
65 /* determine whether a certain element is contained within a given list */
list_contains_key(element * list,int key)66 static bool list_contains_key(element *list, int key) {
67     element *step = NULL;
68 
69     step = list;
70     while ( step != NULL ) {
71         if (step->key == key) {
72             return TRUE;
73         }
74         if (step->children != NULL) {
75             if (list_contains_key(step->children, key)) {
76                 return TRUE;
77             }
78         }
79        step = step->next;
80     }
81     return FALSE;
82 }
83 
84 /**********************************************************************
85 
86   Functions for printing Elements as HTML
87 
88  ***********************************************************************/
89 
90 /* print_html_string - print string, escaping for HTML
91  * If obfuscate selected, convert characters to hex or decimal entities at random */
print_html_string(GString * out,char * str,bool obfuscate)92 static void print_html_string(GString *out, char *str, bool obfuscate) {
93     while (*str != '\0') {
94         switch (*str) {
95         case '&':
96             g_string_append_printf(out, "&amp;");
97             break;
98         case '<':
99             g_string_append_printf(out, "&lt;");
100             break;
101         case '>':
102             g_string_append_printf(out, "&gt;");
103             break;
104         case '"':
105             g_string_append_printf(out, "&quot;");
106             break;
107         default:
108 	  if (obfuscate && ((int) *str < 128) && ((int) *str >= 0)){
109                 if (rand() % 2 == 0)
110                     g_string_append_printf(out, "&#%d;", (int) *str);
111                 else
112                     g_string_append_printf(out, "&#x%x;", (unsigned int) *str);
113             }
114             else
115                 g_string_append_c(out, *str);
116         }
117     str++;
118     }
119 }
120 
121 /* print_html_element_list - print a list of elements as HTML */
print_html_element_list(GString * out,element * list,bool obfuscate)122 static void print_html_element_list(GString *out, element *list, bool obfuscate) {
123     while (list != NULL) {
124         print_html_element(out, list, obfuscate);
125         list = list->next;
126     }
127 }
128 
129 /* add_endnote - add an endnote to global endnotes list. */
add_endnote(element * elt)130 static void add_endnote(element *elt) {
131     endnotes = g_slist_prepend(endnotes, elt);
132 }
133 
134 /* print_html_element - print an element as HTML */
print_html_element(GString * out,element * elt,bool obfuscate)135 static void print_html_element(GString *out, element *elt, bool obfuscate) {
136     int lev;
137     switch (elt->key) {
138     case SPACE:
139         g_string_append_printf(out, "%s", elt->contents.str);
140         break;
141     case LINEBREAK:
142         g_string_append_printf(out, "<br/>\n");
143         break;
144     case STR:
145         print_html_string(out, elt->contents.str, obfuscate);
146         break;
147     case ELLIPSIS:
148         g_string_append_printf(out, "&hellip;");
149         break;
150     case EMDASH:
151         g_string_append_printf(out, "&mdash;");
152         break;
153     case ENDASH:
154         g_string_append_printf(out, "&ndash;");
155         break;
156     case APOSTROPHE:
157         g_string_append_printf(out, "&rsquo;");
158         break;
159     case SINGLEQUOTED:
160         g_string_append_printf(out, "&lsquo;");
161         print_html_element_list(out, elt->children, obfuscate);
162         g_string_append_printf(out, "&rsquo;");
163         break;
164     case DOUBLEQUOTED:
165         g_string_append_printf(out, "&ldquo;");
166         print_html_element_list(out, elt->children, obfuscate);
167         g_string_append_printf(out, "&rdquo;");
168         break;
169     case CODE:
170         g_string_append_printf(out, "<code>");
171         print_html_string(out, elt->contents.str, obfuscate);
172         g_string_append_printf(out, "</code>");
173         break;
174     case HTML:
175         g_string_append_printf(out, "%s", elt->contents.str);
176         break;
177     case LINK:
178         if (strstr(elt->contents.link->url, "mailto:") == elt->contents.link->url)
179             obfuscate = true;  /* obfuscate mailto: links */
180         g_string_append_printf(out, "<a href=\"");
181         print_html_string(out, elt->contents.link->url, obfuscate);
182         g_string_append_printf(out, "\"");
183         if (strlen(elt->contents.link->title) > 0) {
184             g_string_append_printf(out, " title=\"");
185             print_html_string(out, elt->contents.link->title, obfuscate);
186             g_string_append_printf(out, "\"");
187         }
188         g_string_append_printf(out, ">");
189         print_html_element_list(out, elt->contents.link->label, obfuscate);
190         g_string_append_printf(out, "</a>");
191         break;
192     case IMAGE:
193         g_string_append_printf(out, "<img src=\"");
194         print_html_string(out, elt->contents.link->url, obfuscate);
195         g_string_append_printf(out, "\" alt=\"");
196         print_html_element_list(out, elt->contents.link->label, obfuscate);
197         g_string_append_printf(out, "\"");
198         if (strlen(elt->contents.link->title) > 0) {
199             g_string_append_printf(out, " title=\"");
200             print_html_string(out, elt->contents.link->title, obfuscate);
201             g_string_append_printf(out, "\"");
202         }
203         g_string_append_printf(out, " />");
204         break;
205     case EMPH:
206         g_string_append_printf(out, "<em>");
207         print_html_element_list(out, elt->children, obfuscate);
208         g_string_append_printf(out, "</em>");
209         break;
210     case STRONG:
211         g_string_append_printf(out, "<strong>");
212         print_html_element_list(out, elt->children, obfuscate);
213         g_string_append_printf(out, "</strong>");
214         break;
215     case LIST:
216         print_html_element_list(out, elt->children, obfuscate);
217         break;
218     case RAW:
219         /* Shouldn't occur - these are handled by process_raw_blocks() */
220         assert(elt->key != RAW);
221         break;
222     case H1: case H2: case H3: case H4: case H5: case H6:
223         lev = elt->key - H1 + 1;  /* assumes H1 ... H6 are in order */
224         pad(out, 2);
225         g_string_append_printf(out, "<h%1d>", lev);
226         print_html_element_list(out, elt->children, obfuscate);
227         g_string_append_printf(out, "</h%1d>", lev);
228         padded = 0;
229         break;
230     case PLAIN:
231         pad(out, 1);
232         print_html_element_list(out, elt->children, obfuscate);
233         padded = 0;
234         break;
235     case PARA:
236         pad(out, 2);
237         g_string_append_printf(out, "<p>");
238         print_html_element_list(out, elt->children, obfuscate);
239         g_string_append_printf(out, "</p>");
240         padded = 0;
241         break;
242     case HRULE:
243         pad(out, 2);
244         g_string_append_printf(out, "<hr />");
245         padded = 0;
246         break;
247     case HTMLBLOCK:
248         pad(out, 2);
249         g_string_append_printf(out, "%s", elt->contents.str);
250         padded = 0;
251         break;
252     case VERBATIM:
253         pad(out, 2);
254         g_string_append_printf(out, "%s", "<pre><code>");
255         print_html_string(out, elt->contents.str, obfuscate);
256         g_string_append_printf(out, "%s", "</code></pre>");
257         padded = 0;
258         break;
259     case BULLETLIST:
260         pad(out, 2);
261         g_string_append_printf(out, "%s", "<ul>");
262         padded = 0;
263         print_html_element_list(out, elt->children, obfuscate);
264         pad(out, 1);
265         g_string_append_printf(out, "%s", "</ul>");
266         padded = 0;
267         break;
268     case ORDEREDLIST:
269         pad(out, 2);
270         g_string_append_printf(out, "%s", "<ol>");
271         padded = 0;
272         print_html_element_list(out, elt->children, obfuscate);
273         pad(out, 1);
274         g_string_append_printf(out, "</ol>");
275         padded = 0;
276         break;
277     case LISTITEM:
278         pad(out, 1);
279         g_string_append_printf(out, "<li>");
280         padded = 2;
281         print_html_element_list(out, elt->children, obfuscate);
282         g_string_append_printf(out, "</li>");
283         padded = 0;
284         break;
285     case BLOCKQUOTE:
286         pad(out, 2);
287         g_string_append_printf(out, "<blockquote>\n");
288         padded = 2;
289         print_html_element_list(out, elt->children, obfuscate);
290         pad(out, 1);
291         g_string_append_printf(out, "</blockquote>");
292         padded = 0;
293         break;
294     case REFERENCE:
295         /* Nonprinting */
296         break;
297     case NOTE:
298         /* if contents.str == 0, then print note; else ignore, since this
299          * is a note block that has been incorporated into the notes list */
300         if (elt->contents.str == 0) {
301             add_endnote(elt);
302             ++notenumber;
303             g_string_append_printf(out, "<a class=\"noteref\" id=\"fnref%d\" href=\"#fn%d\" title=\"Jump to note %d\">[%d]</a>",
304                 notenumber, notenumber, notenumber, notenumber);
305         }
306         break;
307     default:
308         fprintf(stderr, "print_html_element encountered unknown element key = %d\n", elt->key);
309         exit(EXIT_FAILURE);
310     }
311 }
312 
print_html_endnotes(GString * out)313 static void print_html_endnotes(GString *out) {
314     int counter = 0;
315     GSList *note;
316     element *note_elt;
317     if (endnotes == NULL)
318         return;
319     note = g_slist_reverse(endnotes);
320     g_string_append_printf(out, "<hr/>\n<ol id=\"notes\">");
321     while (note != NULL) {
322         note_elt = note->data;
323         counter++;
324         pad(out, 1);
325         g_string_append_printf(out, "<li id=\"fn%d\">\n", counter);
326         padded = 2;
327         print_html_element_list(out, note_elt->children, false);
328         g_string_append_printf(out, " <a href=\"#fnref%d\" title=\"Jump back to reference\">[back]</a>", counter);
329         pad(out, 1);
330         g_string_append_printf(out, "</li>");
331         note = note->next;
332     }
333     pad(out, 1);
334     g_string_append_printf(out, "</ol>");
335     g_slist_free(endnotes);
336 }
337 
338 /**********************************************************************
339 
340   Functions for printing Elements as LaTeX
341 
342  ***********************************************************************/
343 
344 /* print_latex_string - print string, escaping for LaTeX */
print_latex_string(GString * out,char * str)345 static void print_latex_string(GString *out, char *str) {
346     while (*str != '\0') {
347         switch (*str) {
348           case '{': case '}': case '$': case '%':
349           case '&': case '_': case '#':
350             g_string_append_printf(out, "\\%c", *str);
351             break;
352         case '^':
353             g_string_append_printf(out, "\\^{}");
354             break;
355         case '\\':
356             g_string_append_printf(out, "\\textbackslash{}");
357             break;
358         case '~':
359             g_string_append_printf(out, "\\ensuremath{\\sim}");
360             break;
361         case '|':
362             g_string_append_printf(out, "\\textbar{}");
363             break;
364         case '<':
365             g_string_append_printf(out, "\\textless{}");
366             break;
367         case '>':
368             g_string_append_printf(out, "\\textgreater{}");
369             break;
370         default:
371             g_string_append_c(out, *str);
372         }
373     str++;
374     }
375 }
376 
377 /* print_latex_element_list - print a list of elements as LaTeX */
print_latex_element_list(GString * out,element * list)378 static void print_latex_element_list(GString *out, element *list) {
379     while (list != NULL) {
380         print_latex_element(out, list);
381         list = list->next;
382     }
383 }
384 
385 /* print_latex_element - print an element as LaTeX */
print_latex_element(GString * out,element * elt)386 static void print_latex_element(GString *out, element *elt) {
387     int lev;
388     int i;
389     switch (elt->key) {
390     case SPACE:
391         g_string_append_printf(out, "%s", elt->contents.str);
392         break;
393     case LINEBREAK:
394         g_string_append_printf(out, "\\\\\n");
395         break;
396     case STR:
397         print_latex_string(out, elt->contents.str);
398         break;
399     case ELLIPSIS:
400         g_string_append_printf(out, "\\ldots{}");
401         break;
402     case EMDASH:
403         g_string_append_printf(out, "---");
404         break;
405     case ENDASH:
406         g_string_append_printf(out, "--");
407         break;
408     case APOSTROPHE:
409         g_string_append_printf(out, "'");
410         break;
411     case SINGLEQUOTED:
412         g_string_append_printf(out, "`");
413         print_latex_element_list(out, elt->children);
414         g_string_append_printf(out, "'");
415         break;
416     case DOUBLEQUOTED:
417         g_string_append_printf(out, "``");
418         print_latex_element_list(out, elt->children);
419         g_string_append_printf(out, "''");
420         break;
421     case CODE:
422         g_string_append_printf(out, "\\texttt{");
423         print_latex_string(out, elt->contents.str);
424         g_string_append_printf(out, "}");
425         break;
426     case HTML:
427         /* don't print HTML */
428         break;
429     case LINK:
430         g_string_append_printf(out, "\\href{%s}{", elt->contents.link->url);
431         print_latex_element_list(out, elt->contents.link->label);
432         g_string_append_printf(out, "}");
433         break;
434     case IMAGE:
435         g_string_append_printf(out, "\\includegraphics{%s}", elt->contents.link->url);
436         break;
437     case EMPH:
438         g_string_append_printf(out, "\\emph{");
439         print_latex_element_list(out, elt->children);
440         g_string_append_printf(out, "}");
441         break;
442     case STRONG:
443         g_string_append_printf(out, "\\textbf{");
444         print_latex_element_list(out, elt->children);
445         g_string_append_printf(out, "}");
446         break;
447     case LIST:
448         print_latex_element_list(out, elt->children);
449         break;
450     case RAW:
451         /* Shouldn't occur - these are handled by process_raw_blocks() */
452         assert(elt->key != RAW);
453         break;
454     case H1: case H2: case H3:
455         pad(out, 2);
456         lev = elt->key - H1 + 1;  /* assumes H1 ... H6 are in order */
457         g_string_append_printf(out, "\\");
458         for (i = elt->key; i > H1; i--)
459             g_string_append_printf(out, "sub");
460         g_string_append_printf(out, "section{");
461         print_latex_element_list(out, elt->children);
462         g_string_append_printf(out, "}");
463         padded = 0;
464         break;
465     case H4: case H5: case H6:
466         pad(out, 2);
467         g_string_append_printf(out, "\\noindent\\textbf{");
468         print_latex_element_list(out, elt->children);
469         g_string_append_printf(out, "}");
470         padded = 0;
471         break;
472     case PLAIN:
473         pad(out, 1);
474         print_latex_element_list(out, elt->children);
475         padded = 0;
476         break;
477     case PARA:
478         pad(out, 2);
479         print_latex_element_list(out, elt->children);
480         padded = 0;
481         break;
482     case HRULE:
483         pad(out, 2);
484         g_string_append_printf(out, "\\begin{center}\\rule{3in}{0.4pt}\\end{center}\n");
485         padded = 0;
486         break;
487     case HTMLBLOCK:
488         /* don't print HTML block */
489         break;
490     case VERBATIM:
491         pad(out, 1);
492         g_string_append_printf(out, "\\begin{verbatim}\n");
493         print_latex_string(out, elt->contents.str);
494         g_string_append_printf(out, "\n\\end{verbatim}");
495         padded = 0;
496         break;
497     case BULLETLIST:
498         pad(out, 1);
499         g_string_append_printf(out, "\\begin{itemize}");
500         padded = 0;
501         print_latex_element_list(out, elt->children);
502         pad(out, 1);
503         g_string_append_printf(out, "\\end{itemize}");
504         padded = 0;
505         break;
506     case ORDEREDLIST:
507         pad(out, 1);
508         g_string_append_printf(out, "\\begin{enumerate}");
509         padded = 0;
510         print_latex_element_list(out, elt->children);
511         pad(out, 1);
512         g_string_append_printf(out, "\\end{enumerate}");
513         padded = 0;
514         break;
515     case LISTITEM:
516         pad(out, 1);
517         g_string_append_printf(out, "\\item ");
518         padded = 2;
519         print_latex_element_list(out, elt->children);
520         g_string_append_printf(out, "\n");
521         break;
522     case BLOCKQUOTE:
523         pad(out, 1);
524         g_string_append_printf(out, "\\begin{quote}");
525         padded = 0;
526         print_latex_element_list(out, elt->children);
527         pad(out, 1);
528         g_string_append_printf(out, "\\end{quote}");
529         padded = 0;
530         break;
531     case NOTE:
532         /* if contents.str == 0, then print note; else ignore, since this
533          * is a note block that has been incorporated into the notes list */
534         if (elt->contents.str == 0) {
535             g_string_append_printf(out, "\\footnote{");
536             padded = 2;
537             print_latex_element_list(out, elt->children);
538             g_string_append_printf(out, "}");
539             padded = 0;
540         }
541         break;
542     case REFERENCE:
543         /* Nonprinting */
544         break;
545     default:
546         fprintf(stderr, "print_latex_element encountered unknown element key = %d\n", elt->key);
547         exit(EXIT_FAILURE);
548     }
549 }
550 
551 /**********************************************************************
552 
553   Functions for printing Elements as groff (mm macros)
554 
555  ***********************************************************************/
556 
557 static bool in_list_item = false; /* True if we're parsing contents of a list item. */
558 
559 /* print_groff_string - print string, escaping for groff */
print_groff_string(GString * out,char * str)560 static void print_groff_string(GString *out, char *str) {
561     while (*str != '\0') {
562         switch (*str) {
563         case '\\':
564             g_string_append_printf(out, "\\e");
565             break;
566         default:
567             g_string_append_c(out, *str);
568         }
569     str++;
570     }
571 }
572 
573 /* print_groff_mm_element_list - print a list of elements as groff ms */
print_groff_mm_element_list(GString * out,element * list)574 static void print_groff_mm_element_list(GString *out, element *list) {
575     int count = 1;
576     while (list != NULL) {
577         print_groff_mm_element(out, list, count);
578         list = list->next;
579         count++;
580     }
581 }
582 
583 /* print_groff_mm_element - print an element as groff ms */
print_groff_mm_element(GString * out,element * elt,int count)584 static void print_groff_mm_element(GString *out, element *elt, int count) {
585     int lev;
586     switch (elt->key) {
587     case SPACE:
588         g_string_append_printf(out, "%s", elt->contents.str);
589         padded = 0;
590         break;
591     case LINEBREAK:
592         pad(out, 1);
593         g_string_append_printf(out, ".br\n");
594         padded = 0;
595         break;
596     case STR:
597         print_groff_string(out, elt->contents.str);
598         padded = 0;
599         break;
600     case ELLIPSIS:
601         g_string_append_printf(out, "...");
602         break;
603     case EMDASH:
604         g_string_append_printf(out, "\\[em]");
605         break;
606     case ENDASH:
607         g_string_append_printf(out, "\\[en]");
608         break;
609     case APOSTROPHE:
610         g_string_append_printf(out, "'");
611         break;
612     case SINGLEQUOTED:
613         g_string_append_printf(out, "`");
614         print_groff_mm_element_list(out, elt->children);
615         g_string_append_printf(out, "'");
616         break;
617     case DOUBLEQUOTED:
618         g_string_append_printf(out, "\\[lq]");
619         print_groff_mm_element_list(out, elt->children);
620         g_string_append_printf(out, "\\[rq]");
621         break;
622     case CODE:
623         g_string_append_printf(out, "\\fC");
624         print_groff_string(out, elt->contents.str);
625         g_string_append_printf(out, "\\fR");
626         padded = 0;
627         break;
628     case HTML:
629         /* don't print HTML */
630         break;
631     case LINK:
632         print_groff_mm_element_list(out, elt->contents.link->label);
633         g_string_append_printf(out, " (%s)", elt->contents.link->url);
634         padded = 0;
635         break;
636     case IMAGE:
637         g_string_append_printf(out, "[IMAGE: ");
638         print_groff_mm_element_list(out, elt->contents.link->label);
639         g_string_append_printf(out, "]");
640         padded = 0;
641         /* not supported */
642         break;
643     case EMPH:
644         g_string_append_printf(out, "\\fI");
645         print_groff_mm_element_list(out, elt->children);
646         g_string_append_printf(out, "\\fR");
647         padded = 0;
648         break;
649     case STRONG:
650         g_string_append_printf(out, "\\fB");
651         print_groff_mm_element_list(out, elt->children);
652         g_string_append_printf(out, "\\fR");
653         padded = 0;
654         break;
655     case LIST:
656         print_groff_mm_element_list(out, elt->children);
657         padded = 0;
658         break;
659     case RAW:
660         /* Shouldn't occur - these are handled by process_raw_blocks() */
661         assert(elt->key != RAW);
662         break;
663     case H1: case H2: case H3: case H4: case H5: case H6:
664         lev = elt->key - H1 + 1;
665         pad(out, 1);
666         g_string_append_printf(out, ".H %d \"", lev);
667         print_groff_mm_element_list(out, elt->children);
668         g_string_append_printf(out, "\"");
669         padded = 0;
670         break;
671     case PLAIN:
672         pad(out, 1);
673         print_groff_mm_element_list(out, elt->children);
674         padded = 0;
675         break;
676     case PARA:
677         pad(out, 1);
678         if (!in_list_item || count != 1)
679             g_string_append_printf(out, ".P\n");
680         print_groff_mm_element_list(out, elt->children);
681         padded = 0;
682         break;
683     case HRULE:
684         pad(out, 1);
685         g_string_append_printf(out, "\\l'\\n(.lu*8u/10u'");
686         padded = 0;
687         break;
688     case HTMLBLOCK:
689         /* don't print HTML block */
690         break;
691     case VERBATIM:
692         pad(out, 1);
693         g_string_append_printf(out, ".VERBON 2\n");
694         print_groff_string(out, elt->contents.str);
695         g_string_append_printf(out, ".VERBOFF");
696         padded = 0;
697         break;
698     case BULLETLIST:
699         pad(out, 1);
700         g_string_append_printf(out, ".BL");
701         padded = 0;
702         print_groff_mm_element_list(out, elt->children);
703         pad(out, 1);
704         g_string_append_printf(out, ".LE 1");
705         padded = 0;
706         break;
707     case ORDEREDLIST:
708         pad(out, 1);
709         g_string_append_printf(out, ".AL");
710         padded = 0;
711         print_groff_mm_element_list(out, elt->children);
712         pad(out, 1);
713         g_string_append_printf(out, ".LE 1");
714         padded = 0;
715         break;
716     case LISTITEM:
717         pad(out, 1);
718         g_string_append_printf(out, ".LI\n");
719         in_list_item = true;
720         padded = 2;
721         print_groff_mm_element_list(out, elt->children);
722         in_list_item = false;
723         break;
724     case BLOCKQUOTE:
725         pad(out, 1);
726         g_string_append_printf(out, ".DS I\n");
727         padded = 2;
728         print_groff_mm_element_list(out, elt->children);
729         pad(out, 1);
730         g_string_append_printf(out, ".DE");
731         padded = 0;
732         break;
733     case NOTE:
734         /* if contents.str == 0, then print note; else ignore, since this
735          * is a note block that has been incorporated into the notes list */
736         if (elt->contents.str == 0) {
737             g_string_append_printf(out, "\\*F\n");
738             g_string_append_printf(out, ".FS\n");
739             padded = 2;
740             print_groff_mm_element_list(out, elt->children);
741             pad(out, 1);
742             g_string_append_printf(out, ".FE\n");
743             padded = 1;
744         }
745         break;
746     case REFERENCE:
747         /* Nonprinting */
748         break;
749     default:
750         fprintf(stderr, "print_groff_mm_element encountered unknown element key = %d\n", elt->key);
751         exit(EXIT_FAILURE);
752     }
753 }
754 
755 /**********************************************************************
756 
757   Functions for printing Elements as ODF
758 
759  ***********************************************************************/
760 
761 /* print_odf_code_string - print string, escaping for HTML and saving newlines
762 */
print_odf_code_string(GString * out,char * str)763 static void print_odf_code_string(GString *out, char *str) {
764     char *tmp;
765     while (*str != '\0') {
766         switch (*str) {
767         case '&':
768             g_string_append_printf(out, "&amp;");
769             break;
770         case '<':
771             g_string_append_printf(out, "&lt;");
772             break;
773         case '>':
774             g_string_append_printf(out, "&gt;");
775             break;
776         case '"':
777             g_string_append_printf(out, "&quot;");
778             break;
779         case '\n':
780             g_string_append_printf(out, "<text:line-break/>");
781             break;
782         case ' ':
783             tmp = str;
784             tmp++;
785             if (*tmp == ' ') {
786                 tmp++;
787                 if (*tmp == ' ') {
788                     tmp++;
789                     if (*tmp == ' ') {
790                         g_string_append_printf(out, "<text:tab/>");
791                         str = tmp;
792                     } else {
793                         g_string_append_printf(out, " ");
794                     }
795                 } else {
796                     g_string_append_printf(out, " ");
797                 }
798             } else {
799                 g_string_append_printf(out, " ");
800             }
801             break;
802         default:
803                g_string_append_c(out, *str);
804         }
805     str++;
806     }
807 }
808 
809 /* print_odf_string - print string, escaping for HTML and saving newlines */
print_odf_string(GString * out,char * str)810 static void print_odf_string(GString *out, char *str) {
811     char *tmp;
812     while (*str != '\0') {
813         switch (*str) {
814         case '&':
815             g_string_append_printf(out, "&amp;");
816             break;
817         case '<':
818             g_string_append_printf(out, "&lt;");
819             break;
820         case '>':
821             g_string_append_printf(out, "&gt;");
822             break;
823         case '"':
824             g_string_append_printf(out, "&quot;");
825             break;
826         case '\n':
827             tmp = str;
828             tmp--;
829             if (*tmp == ' ') {
830                 tmp--;
831                 if (*tmp == ' ') {
832                     g_string_append_printf(out, "<text:line-break/>");
833                 } else {
834                     g_string_append_printf(out, "\n");
835                 }
836             } else {
837                 g_string_append_printf(out, "\n");
838             }
839             break;
840         case ' ':
841             tmp = str;
842             tmp++;
843             if (*tmp == ' ') {
844                 tmp++;
845                 if (*tmp == ' ') {
846                     tmp++;
847                     if (*tmp == ' ') {
848                         g_string_append_printf(out, "<text:tab/>");
849                         str = tmp;
850                     } else {
851                         g_string_append_printf(out, " ");
852                     }
853                 } else {
854                     g_string_append_printf(out, " ");
855                 }
856             } else {
857                 g_string_append_printf(out, " ");
858             }
859             break;
860         default:
861                g_string_append_c(out, *str);
862         }
863     str++;
864     }
865 }
866 
867 /* print_odf_element_list - print an element list as ODF */
print_odf_element_list(GString * out,element * list)868 static void print_odf_element_list(GString *out, element *list) {
869     while (list != NULL) {
870         print_odf_element(out, list);
871         list = list->next;
872     }
873 }
874 
875 /* print_odf_element - print an element as ODF */
print_odf_element(GString * out,element * elt)876 static void print_odf_element(GString *out, element *elt) {
877     int lev;
878     int old_type = 0;
879     switch (elt->key) {
880     case SPACE:
881         g_string_append_printf(out, "%s", elt->contents.str);
882         break;
883     case LINEBREAK:
884         g_string_append_printf(out, "<text:line-break/>");
885         break;
886     case STR:
887         print_html_string(out, elt->contents.str, 0);
888         break;
889     case ELLIPSIS:
890         g_string_append_printf(out, "&hellip;");
891         break;
892     case EMDASH:
893         g_string_append_printf(out, "&mdash;");
894         break;
895     case ENDASH:
896         g_string_append_printf(out, "&ndash;");
897         break;
898     case APOSTROPHE:
899         g_string_append_printf(out, "&rsquo;");
900         break;
901     case SINGLEQUOTED:
902         g_string_append_printf(out, "&lsquo;");
903         print_odf_element_list(out, elt->children);
904         g_string_append_printf(out, "&rsquo;");
905         break;
906     case DOUBLEQUOTED:
907         g_string_append_printf(out, "&ldquo;");
908         print_odf_element_list(out, elt->children);
909         g_string_append_printf(out, "&rdquo;");
910         break;
911     case CODE:
912         g_string_append_printf(out, "<text:span text:style-name=\"Source_20_Text\">");
913         print_html_string(out, elt->contents.str, 0);
914         g_string_append_printf(out, "</text:span>");
915         break;
916     case HTML:
917         break;
918     case LINK:
919         g_string_append_printf(out, "<text:a xlink:type=\"simple\" xlink:href=\"");
920         print_html_string(out, elt->contents.link->url, 0);
921         g_string_append_printf(out, "\"");
922         if (strlen(elt->contents.link->title) > 0) {
923             g_string_append_printf(out, " office:name=\"");
924             print_html_string(out, elt->contents.link->title, 0);
925             g_string_append_printf(out, "\"");
926         }
927         g_string_append_printf(out, ">");
928         print_odf_element_list(out, elt->contents.link->label);
929         g_string_append_printf(out, "</text:a>");
930         break;
931     case IMAGE:
932         g_string_append_printf(out, "<draw:frame text:anchor-type=\"as-char\"\ndraw:z-index=\"0\" draw:style-name=\"fr1\" svg:width=\"95%%\"");
933         g_string_append_printf(out, ">\n<draw:text-box><text:p><draw:frame text:anchor-type=\"as-char\" draw:z-index=\"1\" ");
934         g_string_append_printf(out, "><draw:image xlink:href=\"");
935         print_odf_string(out, elt->contents.link->url);
936         g_string_append_printf(out,"\" xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\" draw:filter-name=\"&lt;All formats&gt;\"/>\n</draw:frame></text:p>");
937         g_string_append_printf(out, "</draw:text-box></draw:frame>\n");
938         break;
939     case EMPH:
940         g_string_append_printf(out,
941             "<text:span text:style-name=\"MMD-Italic\">");
942         print_odf_element_list(out, elt->children);
943         g_string_append_printf(out, "</text:span>");
944         break;
945     case STRONG:
946         g_string_append_printf(out,
947             "<text:span text:style-name=\"MMD-Bold\">");
948         print_odf_element_list(out, elt->children);
949         g_string_append_printf(out, "</text:span>");
950         break;
951     case LIST:
952         print_odf_element_list(out, elt->children);
953         break;
954     case RAW:
955         /* Shouldn't occur - these are handled by process_raw_blocks() */
956         assert(elt->key != RAW);
957         break;
958     case H1: case H2: case H3: case H4: case H5: case H6:
959         lev = elt->key - H1 + 1;  /* assumes H1 ... H6 are in order */
960         g_string_append_printf(out, "<text:h text:outline-level=\"%d\">", lev);
961         print_odf_element_list(out, elt->children);
962         g_string_append_printf(out, "</text:h>\n");
963         padded = 0;
964         break;
965     case PLAIN:
966         print_odf_element_list(out, elt->children);
967         padded = 0;
968         break;
969     case PARA:
970         g_string_append_printf(out, "<text:p");
971         switch (odf_type) {
972             case BLOCKQUOTE:
973                 g_string_append_printf(out," text:style-name=\"Quotations\"");
974                 break;
975             case CODE:
976                 g_string_append_printf(out," text:style-name=\"Preformatted Text\"");
977                 break;
978             case VERBATIM:
979                 g_string_append_printf(out," text:style-name=\"Preformatted Text\"");
980                 break;
981             case ORDEREDLIST:
982             case BULLETLIST:
983                 g_string_append_printf(out," text:style-name=\"P2\"");
984                 break;
985             case NOTE:
986                 g_string_append_printf(out," text:style-name=\"Footnote\"");
987                 break;
988             default:
989                 g_string_append_printf(out," text:style-name=\"Standard\"");
990                 break;
991         }
992         g_string_append_printf(out, ">");
993         print_odf_element_list(out, elt->children);
994         g_string_append_printf(out, "</text:p>\n");
995         break;
996     case HRULE:
997         g_string_append_printf(out,"<text:p text:style-name=\"Horizontal_20_Line\"/>\n");
998         break;
999     case HTMLBLOCK:
1000         /* don't print HTML block */
1001         /* but do print HTML comments for raw ODF */
1002         if (strncmp(elt->contents.str,"<!--",4) == 0) {
1003             /* trim "-->" from end */
1004             elt->contents.str[strlen(elt->contents.str)-3] = '\0';
1005             g_string_append_printf(out, "%s", &elt->contents.str[4]);
1006         }
1007         break;
1008     case VERBATIM:
1009         old_type = odf_type;
1010         odf_type = VERBATIM;
1011         g_string_append_printf(out, "<text:p text:style-name=\"Preformatted Text\">");
1012         print_odf_code_string(out, elt->contents.str);
1013         g_string_append_printf(out, "</text:p>\n");
1014         odf_type = old_type;
1015         break;
1016     case BULLETLIST:
1017         if ((odf_type == BULLETLIST) ||
1018             (odf_type == ORDEREDLIST)) {
1019             /* I think this was made unnecessary by another change.
1020             Same for ORDEREDLIST below */
1021             /*  g_string_append_printf(out, "</text:p>"); */
1022         }
1023         old_type = odf_type;
1024         odf_type = BULLETLIST;
1025         g_string_append_printf(out, "%s", "<text:list>");
1026         print_odf_element_list(out, elt->children);
1027         g_string_append_printf(out, "%s", "</text:list>");
1028         odf_type = old_type;
1029         break;
1030     case ORDEREDLIST:
1031         if ((odf_type == BULLETLIST) ||
1032             (odf_type == ORDEREDLIST)) {
1033             /* g_string_append_printf(out, "</text:p>"); */
1034         }
1035         old_type = odf_type;
1036         odf_type = ORDEREDLIST;
1037         g_string_append_printf(out, "%s", "<text:list>\n");
1038         print_odf_element_list(out, elt->children);
1039         g_string_append_printf(out, "%s", "</text:list>\n");
1040         odf_type = old_type;
1041         break;
1042     case LISTITEM:
1043         g_string_append_printf(out, "<text:list-item>\n");
1044         if (elt->children->children->key != PARA) {
1045             g_string_append_printf(out, "<text:p text:style-name=\"P2\">");
1046         }
1047         print_odf_element_list(out, elt->children);
1048 
1049         if ((list_contains_key(elt->children,BULLETLIST) ||
1050             (list_contains_key(elt->children,ORDEREDLIST)))) {
1051             } else {
1052                 if (elt->children->children->key != PARA) {
1053                     g_string_append_printf(out, "</text:p>");
1054                 }
1055             }
1056         g_string_append_printf(out, "</text:list-item>\n");
1057         break;
1058     case BLOCKQUOTE:
1059         old_type = odf_type;
1060         odf_type = BLOCKQUOTE;
1061         print_odf_element_list(out, elt->children);
1062         odf_type = old_type;
1063         break;
1064     case REFERENCE:
1065         break;
1066     case NOTE:
1067         old_type = odf_type;
1068         odf_type = NOTE;
1069         /* if contents.str == 0 then print; else ignore - like above */
1070         if (elt->contents.str == 0) {
1071             g_string_append_printf(out, "<text:note text:id=\"\" text:note-class=\"footnote\"><text:note-body>\n");
1072             print_odf_element_list(out, elt->children);
1073             g_string_append_printf(out, "</text:note-body>\n</text:note>\n");
1074        }
1075         elt->children = NULL;
1076         odf_type = old_type;
1077         break;
1078         break;  default:
1079         fprintf(stderr, "print_odf_element encountered unknown element key = %d\n", elt->key);
1080         exit(EXIT_FAILURE);
1081     }
1082 }
1083 
1084 /**********************************************************************
1085 
1086   Parameterized function for printing an Element.
1087 
1088  ***********************************************************************/
1089 
print_element_list(GString * out,element * elt,int format,int exts)1090 void print_element_list(GString *out, element *elt, int format, int exts) {
1091     /* Initialize globals */
1092     endnotes = NULL;
1093     notenumber = 0;
1094 
1095     extensions = exts;
1096     padded = 2;  /* set padding to 2, so no extra blank lines at beginning */
1097     switch (format) {
1098     case HTML_FORMAT:
1099         print_html_element_list(out, elt, false);
1100         if (endnotes != NULL) {
1101             pad(out, 2);
1102             print_html_endnotes(out);
1103         }
1104         break;
1105     case LATEX_FORMAT:
1106         print_latex_element_list(out, elt);
1107         break;
1108     case GROFF_MM_FORMAT:
1109         print_groff_mm_element_list(out, elt);
1110         break;
1111     case ODF_FORMAT:
1112         print_odf_header(out);
1113         g_string_append_printf(out, "<office:body>\n<office:text>\n");
1114         if (elt != NULL) print_odf_element_list(out,elt);
1115         print_odf_footer(out);
1116         break;
1117     default:
1118         fprintf(stderr, "print_element - unknown format = %d\n", format);
1119         exit(EXIT_FAILURE);
1120     }
1121 }
1122