Line data Source code
1 : /* Copyright (C) 2000-2025 Free Software Foundation, Inc.
2 : *
3 : * Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cpp
4 : * but it owes a huge amount of ideas and raw code from
5 : * James Clark (jjc@jclark.com) grops/ps.cpp.
6 : */
7 :
8 : /*
9 : This file is part of groff, the GNU roff typesetting system.
10 :
11 : groff is free software; you can redistribute it and/or modify it under
12 : the terms of the GNU General Public License as published by the Free
13 : Software Foundation, either version 3 of the License, or
14 : (at your option) any later version.
15 :
16 : groff is distributed in the hope that it will be useful, but WITHOUT ANY
17 : WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 : for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
23 :
24 : #ifdef HAVE_CONFIG_H
25 : #include <config.h>
26 : #endif
27 :
28 : #include <assert.h>
29 : #include <errno.h>
30 : #include <stdio.h> // EOF, FILE, fclose(), fflush(), fopen(), freopen(),
31 : // fseek(), SEEK_SET, setbuf(), stderr, stdout
32 : #include <stdlib.h> // abs(), atoi(), EXIT_SUCCESS, exit()
33 : #include <string.h> // strcasecmp(), strcmp(), strerror(), strlen(),
34 : // strncmp()
35 : #include <time.h> // asctime(), tm
36 :
37 : #include <getopt.h> // getopt_long()
38 :
39 : #include "cset.h" // csspace()
40 : #include "curtime.h"
41 : #include "driver.h"
42 : #include "lib.h" // strsave(), xtmpfile()
43 : #include "stringclass.h"
44 : #include "unicode.h"
45 :
46 : #include "html.h"
47 : #include "html-text.h"
48 : #include "html-table.h"
49 :
50 : extern "C" const char *Version_string;
51 :
52 : #if !defined(TRUE)
53 : # define TRUE (1==1)
54 : #endif
55 : #if !defined(FALSE)
56 : # define FALSE (1==0)
57 : #endif
58 :
59 : #define MAX_LINE_LENGTH 60 /* maximum characters we want in a line */
60 : #define SIZE_INCREMENT 2 /* font size increment <big> = +2 */
61 : #define CENTER_TOLERANCE 2 /* how many pixels off center do we allow */
62 : #define ANCHOR_TEMPLATE "heading" /* if simple anchor is set we use this */
63 : #define UNICODE_DESC_START 0x80 /* all character entities above this are */
64 : /* either encoded by their glyph names or if */
65 : /* there is no name then we use &#nnn; */
66 : typedef enum {CENTERED, LEFT, RIGHT, INLINE} TAG_ALIGNMENT;
67 : typedef enum {col_tag, tab_tag, tab0_tag, none_tag} colType;
68 :
69 : #undef DEBUG_TABLES
70 : // #define DEBUG_TABLES
71 :
72 : /*
73 : * prototypes
74 : */
75 :
76 : const char *get_html_translation (font *f, const string &name);
77 : static const char *get_html_entity(unsigned int code);
78 : int char_translate_to_html (font *f, char *buf, int buflen, unsigned char ch, int b, int and_single);
79 :
80 :
81 : static int auto_links = TRUE; /* by default we enable automatic links at */
82 : /* top of the document. */
83 : static int auto_rule = TRUE; /* by default we enable an automatic rule */
84 : /* at the top and bottom of the document */
85 : static int simple_anchors = FALSE; /* default to anchors with heading text */
86 : static int manufacture_headings = FALSE; /* default is to use the Hn html headings, */
87 : /* rather than manufacture our own. */
88 : static int do_write_creator_comment = TRUE; /* write Creator HTML comment */
89 : static int do_write_date_comment = TRUE; /* write CreationDate HTML comment */
90 : /* has user requested initial bg color? */
91 : static color *default_background = 0 /* nullptr */;
92 : static string job_name; /* if set then the output is split into */
93 : /* multiple files with 'job_name'-%d.html */
94 : // TODO: boolify
95 : static int multiple_files = FALSE; /* must we the output be divided into */
96 : /* multiple html files, one for each */
97 : /* heading? */
98 : static int base_point_size = 0; /* which troff font size maps onto html */
99 : /* size 3? */
100 : static int split_level = 2; /* what heading level to split at? */
101 : static string head_info; /* user supplied information to be placed */
102 : /* into <head> </head> */
103 : static int valid_flag = FALSE; /* has user requested a valid flag at the */
104 : /* end of each page? */
105 : static int groff_sig = FALSE; /* "This document was produced using" */
106 : html_dialect dialect = html4; /* which html dialect should grohtml output */
107 : // TODO: Make this an enum.
108 : static const int CHARSET_ASCII = 0;
109 : static const int CHARSET_MIXED = 1;
110 : static const int CHARSET_UTF8 = 2;
111 : static int charset_encoding = CHARSET_MIXED;/* The character set may be plain ASCII, */
112 : /* pure UTF-8, or a mixture of character */
113 : /* entity references. */
114 : /*
115 : * TODO: CHARSET_MIXED doesn't seem to govern the output in any way.
116 : * Everywhere `charset_encoding` is tested, it is cast to a `bool`, so
117 : * all we're really choosing between is ASCII and UTF-8. Can we get rid
118 : * of CHARSET_MIXED?
119 : */
120 :
121 : /*
122 : * start with a few favorites
123 : */
124 :
125 0 : void stop () {}
126 :
127 0 : static int min (int a, int b)
128 : {
129 0 : if (a < b)
130 0 : return a;
131 : else
132 0 : return b;
133 : }
134 :
135 838 : static int max (int a, int b)
136 : {
137 838 : if (a > b)
138 4 : return a;
139 : else
140 834 : return b;
141 : }
142 :
143 : /*
144 : * is_intersection - returns TRUE if range a1..a2 intersects with
145 : * b1..b2
146 : */
147 :
148 : // TODO: boolify
149 45886 : static int is_intersection (int a1, int a2, int b1, int b2)
150 : {
151 : // easier to prove NOT outside limits
152 45886 : return ! ((a1 > b2) || (a2 < b1));
153 : }
154 :
155 : /*
156 : * is_digit - returns TRUE if character, ch, is a digit.
157 : */
158 :
159 : // TODO: boolify
160 0 : static int is_digit (char ch)
161 : {
162 0 : return (ch >= '0') && (ch <= '9');
163 : }
164 :
165 : /*
166 : * the classes and methods for maintaining a list of files.
167 : */
168 :
169 : struct file {
170 : FILE *fp;
171 : file *next;
172 : int new_output_file;
173 : int require_links;
174 : string output_file_name;
175 :
176 : file (FILE * /* f */);
177 : };
178 :
179 : /*
180 : * file - initialize all fields to null pointers
181 : */
182 :
183 75 : file::file (FILE * f)
184 : : fp(f), next(0 /* nullptr */), new_output_file(FALSE),
185 75 : require_links(FALSE), output_file_name("")
186 : {
187 75 : }
188 :
189 : // TODO: Explicit `void` parameter lists are unnecessary and
190 : // inconsistent with other groff code style.
191 : class files {
192 : public:
193 : files ();
194 : FILE *get_file (void);
195 : void start_of_list (void);
196 : void move_next (void);
197 : void add_new_file (FILE * /* f */);
198 : void set_file_name (string /* name */);
199 : void set_links_required (void);
200 : int are_links_required (void);
201 : int is_new_output_file (void);
202 : string file_name (void);
203 : string next_file_name (void);
204 : private:
205 : file *head;
206 : file *tail;
207 : file *ptr;
208 : };
209 :
210 : /*
211 : * files - create an empty list of files.
212 : */
213 :
214 17 : files::files ()
215 17 : : head(0 /* nullptr */), tail(0 /* nullptr */), ptr(0 /* nullptr */)
216 : {
217 17 : }
218 :
219 : /*
220 : * get_file - returns the FILE associated with ptr.
221 : */
222 :
223 432 : FILE *files::get_file (void)
224 : {
225 432 : if (ptr != 0 /* nullptr */)
226 415 : return ptr->fp;
227 : else
228 17 : return 0 /* nullptr */;
229 : }
230 :
231 : /*
232 : * start_of_list - reset the ptr to the start of the list.
233 : */
234 :
235 17 : void files::start_of_list (void)
236 : {
237 17 : ptr = head;
238 17 : }
239 :
240 : /*
241 : * move_next - moves the ptr to the next element on the list.
242 : */
243 :
244 75 : void files::move_next (void)
245 : {
246 75 : if (ptr != 0 /* nullptr */)
247 75 : ptr = ptr->next;
248 75 : }
249 :
250 : /*
251 : * add_new_file - adds a new file, f, to the list.
252 : */
253 :
254 75 : void files::add_new_file (FILE *f)
255 : {
256 75 : assert(f != 0 /* nullptr */);
257 75 : if (0 /* nullptr */ == head) {
258 17 : head = new file(f);
259 17 : tail = head;
260 : } else {
261 58 : tail->next = new file(f);
262 58 : tail = tail->next;
263 : }
264 75 : ptr = tail;
265 75 : }
266 :
267 : /*
268 : * set_file_name - sets the final file name to contain the html
269 : * data to name.
270 : */
271 :
272 40 : void files::set_file_name (string name)
273 : {
274 40 : if (ptr != 0 /* nullptr */) {
275 40 : ptr->output_file_name = name;
276 40 : ptr->new_output_file = TRUE;
277 : }
278 40 : }
279 :
280 : /*
281 : * set_links_required - issue links when processing this component
282 : * of the file.
283 : */
284 :
285 18 : void files::set_links_required (void)
286 : {
287 18 : if (ptr != 0 /* nullptr */)
288 18 : ptr->require_links = TRUE;
289 18 : }
290 :
291 : /*
292 : * are_links_required - returns TRUE if this section of the file
293 : * requires that links should be issued.
294 : */
295 :
296 : // TODO: boolify
297 75 : int files::are_links_required (void)
298 : {
299 75 : if (ptr != 0 /* nullptr */)
300 58 : return ptr->require_links;
301 17 : return FALSE;
302 : }
303 :
304 : /*
305 : * is_new_output_file - returns TRUE if this component of the file
306 : * is the start of a new output file.
307 : */
308 :
309 : // TODO: boolify
310 75 : int files::is_new_output_file (void)
311 : {
312 75 : if (ptr != 0 /* nullptr */)
313 58 : return ptr->new_output_file;
314 17 : return FALSE;
315 : }
316 :
317 : /*
318 : * file_name - returns the name of the file.
319 : */
320 :
321 232 : string files::file_name (void)
322 : {
323 232 : if (ptr != 0 /* nullptr */)
324 232 : return ptr->output_file_name;
325 0 : return string("");
326 : }
327 :
328 : /*
329 : * next_file_name - returns the name of the next file.
330 : */
331 :
332 57 : string files::next_file_name (void)
333 : {
334 57 : if ((ptr != 0 /* nullptr */) && (ptr->next != 0 /* nullptr */))
335 41 : return ptr->next->output_file_name;
336 16 : return string("");
337 : }
338 :
339 : /*
340 : * the class and methods for styles
341 : */
342 :
343 : struct style {
344 : font *f;
345 : int point_size;
346 : int font_no;
347 : int height;
348 : int slant;
349 : color col;
350 : style ();
351 : style (font * /* p */,
352 : int /* sz */,
353 : int /* h */,
354 : int /* sl */,
355 : int /* no */,
356 : color /* c */);
357 : int operator == (const style & /* s */) const;
358 : int operator != (const style & /* s */) const;
359 : };
360 :
361 26048 : style::style()
362 26048 : : f(0 /* nullptr */), point_size(-1)
363 : {
364 26048 : }
365 :
366 103190 : style::style(font *p, int sz, int h, int sl, int no, color c)
367 103190 : : f(p), point_size(sz), font_no(no), height(h), slant(sl), col(c)
368 : {
369 103190 : }
370 :
371 95183 : int style::operator==(const style &s) const
372 : {
373 95183 : return ((f == s.f)
374 93939 : && (point_size == s.point_size)
375 93925 : && (height == s.height)
376 93925 : && (slant == s.slant)
377 189122 : && (col == s.col));
378 : }
379 :
380 0 : int style::operator!=(const style &s) const
381 : {
382 0 : return !(*this == s);
383 : }
384 :
385 : /*
386 : * the class and methods for retaining ascii text
387 : */
388 :
389 : struct char_block {
390 : enum { SIZE = 256 };
391 : char *buffer;
392 : int used;
393 : char_block *next;
394 :
395 : char_block();
396 : char_block(int /* length */);
397 : ~char_block();
398 : };
399 :
400 0 : char_block::char_block()
401 0 : : buffer(0 /* nullptr */), used(0), next(0 /* nullptr */)
402 : {
403 0 : }
404 :
405 838 : char_block::char_block(int length)
406 838 : : used(0), next(0 /* nullptr */)
407 : {
408 838 : buffer = new char[max(length, char_block::SIZE)];
409 838 : if (0 /* nullptr */ == buffer)
410 0 : fatal("out of memory error");
411 838 : }
412 :
413 1676 : char_block::~char_block()
414 : {
415 838 : if (buffer != 0 /* nullptr */)
416 838 : delete[] buffer;
417 838 : }
418 :
419 : class char_buffer {
420 : public:
421 : char_buffer();
422 : ~char_buffer();
423 : char *add_string(const char * /* s */,
424 : unsigned int /* length */);
425 : char *add_string(const string & /* s */);
426 : private:
427 : char_block *head;
428 : char_block *tail;
429 : };
430 :
431 54 : char_buffer::char_buffer()
432 54 : : head(0 /* nullptr */), tail(0 /* nullptr */)
433 : {
434 54 : }
435 :
436 912 : char_buffer::~char_buffer()
437 : {
438 875 : while (head != 0 /* nullptr */) {
439 838 : char_block *temp = head;
440 838 : head = head->next;
441 838 : delete temp;
442 : }
443 37 : }
444 :
445 25822 : char *char_buffer::add_string (const char *s, unsigned int length)
446 : {
447 25822 : int i = 0;
448 : unsigned int old_used;
449 :
450 25822 : if ((0 /* nullptr */ == s) || (0 == length))
451 8 : return 0 /* nullptr */;
452 :
453 25814 : if (0 /* nullptr */ == tail) {
454 20 : tail = new char_block(length+1);
455 20 : head = tail;
456 : } else {
457 25794 : if (tail->used + length+1 > char_block::SIZE) {
458 818 : tail->next = new char_block(length+1);
459 818 : tail = tail->next;
460 : }
461 : }
462 :
463 25814 : old_used = tail->used;
464 159391 : do {
465 185205 : tail->buffer[tail->used] = s[i];
466 185205 : tail->used++;
467 185205 : i++;
468 185205 : length--;
469 185205 : } while (length>0);
470 :
471 : // add terminating nul character
472 :
473 25814 : tail->buffer[tail->used] = '\0';
474 25814 : tail->used++;
475 :
476 : // and return start of new string
477 :
478 25814 : return &tail->buffer[old_used];
479 : }
480 :
481 25822 : char *char_buffer::add_string (const string &s)
482 : {
483 25822 : return add_string(s.contents(), s.length());
484 : }
485 :
486 : /*
487 : * the classes and methods for maintaining glyph positions.
488 : */
489 :
490 : class text_glob {
491 : public:
492 : void text_glob_html (style * /* s */,
493 : char * /* str */,
494 : int /* length */,
495 : int /* min_vertical */,
496 : int /* min_horizontal */,
497 : int /* max_vertical */,
498 : int /* max_horizontal */);
499 : void text_glob_special (style * /* s */,
500 : char * /* str */,
501 : int /* length */,
502 : int /* min_vertical */,
503 : int /* min_horizontal */,
504 : int /* max_vertical */,
505 : int /* max_horizontal */);
506 : void text_glob_line (style * /* s */,
507 : int /* min_vertical */,
508 : int /* min_horizontal */,
509 : int /* max_vertical */,
510 : int /* max_horizontal */,
511 : int /* thickness */);
512 : void text_glob_auto_image(style * /* s */,
513 : char * /* str */,
514 : int /* length */,
515 : int /* min_vertical */,
516 : int /* min_horizontal */,
517 : int /* max_vertical */,
518 : int /* max_horizontal */);
519 : void text_glob_tag (style * /* s */,
520 : char * /* str */,
521 : int /* length */,
522 : int /* min_vertical */,
523 : int /* min_horizontal */,
524 : int /* max_vertical */,
525 : int /* max_horizontal */);
526 : text_glob (void);
527 : ~text_glob (void);
528 : int is_a_line (void);
529 : int is_a_tag (void);
530 : int is_eol (void);
531 : int is_auto_img (void);
532 : int is_br (void);
533 : int is_in (void);
534 : int is_po (void);
535 : int is_ti (void);
536 : int is_ll (void);
537 : int is_ce (void);
538 : int is_tl (void);
539 : int is_eo_tl (void);
540 : int is_eol_ce (void);
541 : int is_col (void);
542 : int is_tab (void);
543 : int is_tab0 (void);
544 : int is_ta (void);
545 : int is_tab_ts (void);
546 : int is_tab_te (void);
547 : int is_nf (void);
548 : int is_fi (void);
549 : int is_eo_h (void);
550 : int get_arg (void);
551 : int get_tab_args (char * /* align */);
552 :
553 : void remember_table (html_table * /* t */);
554 : html_table *get_table (void);
555 :
556 : style text_style;
557 : const char *text_string;
558 : unsigned int text_length;
559 : int minv, minh, maxv, maxh;
560 : int is_tag; // is this a .br, .sp, .tl etc
561 : int is_img_auto; // image created by eqn delim
562 : int is_special; // text has come via 'x X html:'
563 : int is_line; // is the command a <line>?
564 : int thickness; // the thickness of a line
565 : html_table *tab; // table description
566 :
567 : private:
568 : text_glob (style * /* s */,
569 : const char * /* str */,
570 : int /* length */,
571 : int /* min_vertical */,
572 : int /* min_horizontal */,
573 : int /* max_vertical */,
574 : int /* max_horizontal */,
575 : bool /* is_troff_command */,
576 : bool /* is_auto_image */,
577 : bool /* is_special_command */,
578 : bool /* is_a_line */,
579 : int /* thickness */);
580 : };
581 :
582 25822 : text_glob::text_glob (style *s ,
583 : const char *str,
584 : int length,
585 : int min_vertical,
586 : int min_horizontal,
587 : int max_vertical,
588 : int max_horizontal,
589 : bool is_troff_command,
590 : bool is_auto_image,
591 : bool is_special_command,
592 : bool is_a_line_flag,
593 25822 : int line_thickness)
594 25822 : : text_style(*s), text_string(str), text_length(length),
595 : minv(min_vertical), minh(min_horizontal), maxv(max_vertical),
596 : maxh(max_horizontal), is_tag(is_troff_command),
597 : is_img_auto(is_auto_image), is_special(is_special_command),
598 : is_line(is_a_line_flag), thickness(line_thickness),
599 25822 : tab(0 /* nullptr */)
600 : {
601 25822 : }
602 :
603 25822 : text_glob::text_glob ()
604 : : text_string(0 /* nullptr */), text_length(0), minv(-1), minh(-1),
605 : maxv(-1), maxh(-1), is_tag(FALSE), is_special(FALSE),
606 25822 : is_line(FALSE), thickness(0), tab(0 /* nullptr */)
607 : {
608 25822 : }
609 :
610 51650 : text_glob::~text_glob ()
611 : {
612 51650 : if (tab != 0 /* nullptr */)
613 20 : delete tab;
614 51650 : }
615 :
616 : /*
617 : * text_glob_html - used to place html text into the glob buffer.
618 : */
619 :
620 20473 : void text_glob::text_glob_html (style *s, char *str, int length,
621 : int min_vertical , int min_horizontal,
622 : int max_vertical , int max_horizontal)
623 : {
624 : text_glob *g = new text_glob(s, str, length,
625 : min_vertical, min_horizontal,
626 : max_vertical, max_horizontal,
627 20473 : FALSE, FALSE, FALSE, FALSE, 0 /* nullptr */);
628 : // TODO: penultimate argument above is not really a Boolean
629 20473 : *this = *g;
630 20473 : delete g;
631 20473 : }
632 :
633 : /*
634 : * text_glob_html - used to place html specials into the glob buffer.
635 : * This text is essentially html commands coming
636 : * through from the macro sets, with special
637 : * designated sequences of characters translated into
638 : * html. See add_and_encode.
639 : */
640 :
641 563 : void text_glob::text_glob_special (style *s, char *str, int length,
642 : int min_vertical, int min_horizontal,
643 : int max_vertical, int max_horizontal)
644 : {
645 : text_glob *g = new text_glob(s, str, length,
646 : min_vertical, min_horizontal,
647 : max_vertical, max_horizontal,
648 563 : FALSE, FALSE, TRUE, FALSE, 0 /* nullptr */);
649 : // TODO: penultimate argument above is not really a Boolean
650 563 : *this = *g;
651 563 : delete g;
652 563 : }
653 :
654 : /*
655 : * text_glob_line - record horizontal draw line commands.
656 : */
657 :
658 0 : void text_glob::text_glob_line (style *s,
659 : int min_vertical, int min_horizontal,
660 : int max_vertical, int max_horizontal,
661 : int thickness_value)
662 : {
663 : text_glob *g = new text_glob(s, "", 0,
664 : min_vertical, min_horizontal,
665 : max_vertical, max_horizontal,
666 : FALSE, FALSE, FALSE, TRUE,
667 0 : thickness_value);
668 0 : *this = *g;
669 0 : delete g;
670 0 : }
671 :
672 : /*
673 : * text_glob_auto_image - record the presence of a .auto-image tag
674 : * command. Used to mark that an image has been
675 : * created automatically by a preprocessor and
676 : * (pre-grohtml/troff) combination. Under some
677 : * circumstances images may not be created.
678 : * (consider .EQ
679 : * delim $$
680 : * .EN
681 : * .TS
682 : * tab(!), center;
683 : * l!l.
684 : * $1 over x$!recripical of x
685 : * .TE
686 : * the first auto-image marker is created via
687 : * .EQ/.EN pair and no image is created. The
688 : * second auto-image marker occurs at $1 over
689 : * x$ Currently this image will not be created
690 : * as the whole of the table is created as an
691 : * image. (Once html tables are handled by
692 : * grohtml this will change. Shortly this will
693 : * be the case).
694 : */
695 :
696 58 : void text_glob::text_glob_auto_image(style *s, char *str, int length,
697 : int min_vertical,
698 : int min_horizontal,
699 : int max_vertical,
700 : int max_horizontal)
701 : {
702 : text_glob *g = new text_glob(s, str, length,
703 : min_vertical, min_horizontal,
704 : max_vertical, max_horizontal,
705 58 : TRUE, TRUE, FALSE, FALSE, 0 /* nullptr */);
706 58 : *this = *g;
707 58 : delete g;
708 58 : }
709 :
710 : /*
711 : * text_glob_tag - records a troff tag.
712 : */
713 :
714 4728 : void text_glob::text_glob_tag (style *s, char *str, int length,
715 : int min_vertical, int min_horizontal,
716 : int max_vertical, int max_horizontal)
717 : {
718 : text_glob *g = new text_glob(s, str, length,
719 : min_vertical, min_horizontal,
720 : max_vertical, max_horizontal,
721 4728 : TRUE, FALSE, FALSE, FALSE, 0 /* nullptr */);
722 4728 : *this = *g;
723 4728 : delete g;
724 4728 : }
725 :
726 : /*
727 : * is_a_line - returns TRUE if glob should be converted into an <hr>
728 : */
729 :
730 : // TODO: boolify
731 20923 : int text_glob::is_a_line (void)
732 : {
733 20923 : return is_line;
734 : }
735 :
736 : /*
737 : * is_a_tag - returns TRUE if glob contains a troff directive.
738 : */
739 :
740 : // TODO: boolify
741 272989 : int text_glob::is_a_tag (void)
742 : {
743 272989 : return is_tag;
744 : }
745 :
746 : /*
747 : * is_eol - returns TRUE if glob contains the tag eol
748 : */
749 :
750 : // TODO: boolify
751 26287 : int text_glob::is_eol (void)
752 : {
753 26287 : return is_tag && (strcmp(text_string, "devtag:.eol") == 0);
754 : }
755 :
756 : /*
757 : * is_eol_ce - returns TRUE if glob contains the tag eol.ce
758 : */
759 :
760 : // TODO: boolify
761 3440 : int text_glob::is_eol_ce (void)
762 : {
763 3440 : return is_tag && (strcmp(text_string, "devtag:eol.ce") == 0);
764 : }
765 :
766 : /*
767 : * is_tl - returns TRUE if glob contains the tag .tl
768 : */
769 :
770 : // TODO: boolify
771 974 : int text_glob::is_tl (void)
772 : {
773 974 : return is_tag && (strcmp(text_string, "devtag:.tl") == 0);
774 : }
775 :
776 : /*
777 : * is_eo_tl - returns TRUE if glob contains the tag eo.tl
778 : */
779 :
780 : // TODO: boolify
781 24 : int text_glob::is_eo_tl (void)
782 : {
783 24 : return is_tag && (strcmp(text_string, "devtag:.eo.tl") == 0);
784 : }
785 :
786 : /*
787 : * is_nf - returns TRUE if glob contains the tag .fi 0
788 : */
789 :
790 : // TODO: boolify
791 76857 : int text_glob::is_nf (void)
792 : {
793 76857 : return is_tag
794 12654 : && (strncmp(text_string, "devtag:.fi", strlen("devtag:.fi"))
795 : == 0)
796 89511 : && (get_arg() == 0);
797 : }
798 :
799 : /*
800 : * is_fi - returns TRUE if glob contains the tag .fi 1
801 : */
802 :
803 : // TODO: boolify
804 77620 : int text_glob::is_fi (void)
805 : {
806 77620 : return (is_tag
807 13417 : && (strncmp(text_string, "devtag:.fi", strlen("devtag:.fi"))
808 : == 0)
809 91037 : && (get_arg() == 1));
810 : }
811 :
812 : /*
813 : * is_eo_h - returns TRUE if glob contains the tag .eo.h
814 : */
815 :
816 : // TODO: boolify
817 417 : int text_glob::is_eo_h (void)
818 : {
819 417 : return is_tag && (strcmp(text_string, "devtag:.eo.h") == 0);
820 : }
821 :
822 : /*
823 : * is_ce - returns TRUE if glob contains the tag .ce
824 : */
825 :
826 : // TODO: boolify
827 22986 : int text_glob::is_ce (void)
828 : {
829 22986 : return is_tag && (strncmp(text_string, "devtag:.ce",
830 22986 : strlen("devtag:.ce")) == 0);
831 : }
832 :
833 : /*
834 : * is_in - returns TRUE if glob contains the tag .in
835 : */
836 :
837 : // TODO: boolify
838 23006 : int text_glob::is_in (void)
839 : {
840 23006 : return is_tag && (strncmp(text_string, "devtag:.in ",
841 23006 : strlen("devtag:.in ")) == 0);
842 : }
843 :
844 : /*
845 : * is_po - returns TRUE if glob contains the tag .po
846 : */
847 :
848 : // TODO: boolify
849 22699 : int text_glob::is_po (void)
850 : {
851 22699 : return is_tag && (strncmp(text_string, "devtag:.po ",
852 22699 : strlen("devtag:.po ")) == 0);
853 : }
854 :
855 : /*
856 : * is_ti - returns TRUE if glob contains the tag .ti
857 : */
858 :
859 : // TODO: boolify
860 22687 : int text_glob::is_ti (void)
861 : {
862 22687 : return is_tag && (strncmp(text_string, "devtag:.ti ",
863 22687 : strlen("devtag:.ti ")) == 0);
864 : }
865 :
866 : /*
867 : * is_ll - returns TRUE if glob contains the tag .ll
868 : */
869 :
870 : // TODO: boolify
871 420 : int text_glob::is_ll (void)
872 : {
873 420 : return is_tag && (strncmp(text_string, "devtag:.ll ",
874 420 : strlen("devtag:.ll ")) == 0);
875 : }
876 :
877 : /*
878 : * is_col - returns TRUE if glob contains the tag .col
879 : */
880 :
881 : // TODO: boolify
882 67734 : int text_glob::is_col (void)
883 : {
884 67734 : return is_tag && (strncmp(text_string, "devtag:.col",
885 67734 : strlen("devtag:.col")) == 0);
886 : }
887 :
888 : /*
889 : * is_tab_ts - returns TRUE if glob contains the tag .tab_ts
890 : */
891 :
892 : // TODO: boolify
893 0 : int text_glob::is_tab_ts (void)
894 : {
895 0 : return is_tag && (strcmp(text_string, "devtag:.tab-ts") == 0);
896 : }
897 :
898 : /*
899 : * is_tab_te - returns TRUE if glob contains the tag .tab_te
900 : */
901 :
902 : // TODO: boolify
903 0 : int text_glob::is_tab_te (void)
904 : {
905 0 : return is_tag && (strcmp(text_string, "devtag:.tab-te") == 0);
906 : }
907 :
908 : /*
909 : * is_ta - returns TRUE if glob contains the tag .ta
910 : */
911 :
912 : // TODO: boolify
913 24673 : int text_glob::is_ta (void)
914 : {
915 24673 : return is_tag && (strncmp(text_string, "devtag:.ta ",
916 24673 : strlen("devtag:.ta ")) == 0);
917 : }
918 :
919 : /*
920 : * is_tab - returns TRUE if glob contains the tag tab
921 : */
922 :
923 : // TODO: boolify
924 78308 : int text_glob::is_tab (void)
925 : {
926 78308 : return is_tag && (strncmp(text_string, "devtag:tab ",
927 78308 : strlen("devtag:tab ")) == 0);
928 : }
929 :
930 : /*
931 : * is_tab0 - returns TRUE if glob contains the tag tab0
932 : */
933 :
934 : // TODO: boolify
935 45040 : int text_glob::is_tab0 (void)
936 : {
937 45040 : return is_tag && (strncmp(text_string, "devtag:tab0",
938 45040 : strlen("devtag:tab0")) == 0);
939 : }
940 :
941 : /*
942 : * is_auto_img - returns TRUE if the glob contains an automatically
943 : * generated image.
944 : */
945 :
946 : // TODO: boolify
947 441 : int text_glob::is_auto_img (void)
948 : {
949 441 : return is_img_auto;
950 : }
951 :
952 : /*
953 : * is_br - returns TRUE if the glob is a tag containing a .br
954 : * or an implied .br. Note that we do not include .nf or .fi
955 : * as grohtml will place a .br after these commands if they
956 : * should break the line.
957 : */
958 :
959 : // TODO: boolify
960 99010 : int text_glob::is_br (void)
961 : {
962 110538 : return is_a_tag() && ((strcmp ("devtag:.br", text_string) == 0)
963 11528 : || (strncmp("devtag:.sp", text_string,
964 99010 : strlen("devtag:.sp")) == 0));
965 : }
966 :
967 1407 : int text_glob::get_arg (void)
968 : {
969 1407 : if (strncmp("devtag:", text_string, strlen("devtag:")) == 0) {
970 1407 : const char *p = text_string;
971 :
972 15533 : while ((*p != '\0') && (!csspace(*p)))
973 14126 : p++;
974 2814 : while ((*p != '\0') && (csspace(*p)))
975 1407 : p++;
976 1407 : if ('\0' == *p)
977 0 : return -1;
978 1407 : return atoi(p);
979 : }
980 0 : return -1;
981 : }
982 :
983 : /*
984 : * get_tab_args - returns the tab position and alignment of the tab tag
985 : */
986 :
987 9 : int text_glob::get_tab_args (char *align)
988 : {
989 9 : assert(align != 0 /* nullptr */);
990 9 : if (strncmp("devtag:", text_string, strlen("devtag:")) == 0) {
991 9 : const char *p = text_string;
992 :
993 : // firstly the alignment C|R|L
994 99 : while ((*p != '\0') && (!csspace(*p)))
995 90 : p++;
996 18 : while ((*p != '\0') && (csspace(*p)))
997 9 : p++;
998 9 : *align = *p;
999 : // now the int value
1000 18 : while ((*p != '\0') && (!csspace(*p)))
1001 9 : p++;
1002 18 : while ((*p != '\0') && (csspace(*p)))
1003 9 : p++;
1004 9 : if ('\0' == *p)
1005 0 : return -1;
1006 9 : return atoi(p);
1007 : }
1008 0 : return -1;
1009 : }
1010 :
1011 : /*
1012 : * remember_table - saves table, t, in the text_glob.
1013 : */
1014 :
1015 20 : void text_glob::remember_table (html_table *t)
1016 : {
1017 20 : if (tab != 0 /* nullptr */)
1018 0 : delete tab;
1019 20 : tab = t;
1020 20 : }
1021 :
1022 : /*
1023 : * get_table - returns the stored table description.
1024 : */
1025 :
1026 20 : html_table *text_glob::get_table (void)
1027 : {
1028 20 : return tab;
1029 : }
1030 :
1031 : /*
1032 : * the class and methods used to construct ordered double linked
1033 : * lists. In a previous implementation we used templates via
1034 : * #include "ordered-list.h", but this does assume that all C++
1035 : * compilers can handle this feature. Pragmatically it is safer to
1036 : * assume this is not the case.
1037 : */
1038 :
1039 : struct element_list {
1040 : element_list *right;
1041 : element_list *left;
1042 : text_glob *datum;
1043 : int lineno;
1044 : int minv, minh, maxv, maxh;
1045 :
1046 : element_list (text_glob * /* d */,
1047 : int /* line_number */,
1048 : int /* min_vertical */,
1049 : int /* min_horizontal */,
1050 : int /* max_vertical */,
1051 : int /* max_horizontal */);
1052 : element_list ();
1053 : ~element_list ();
1054 : };
1055 :
1056 0 : element_list::element_list ()
1057 : : right(0 /* nullptr */), left(0 /* nullptr */),
1058 : datum(0 /* nullptr */), lineno(0),
1059 : minv(-1), minh(-1),
1060 0 : maxv(-1), maxh(-1)
1061 : {
1062 0 : }
1063 :
1064 : /*
1065 : * element_list - create a list element assigning the datum and region
1066 : * parameters.
1067 : */
1068 :
1069 25822 : element_list::element_list (text_glob *in,
1070 : int line_number,
1071 : int min_vertical, int min_horizontal,
1072 25822 : int max_vertical, int max_horizontal)
1073 : : right(0 /* nullptr */), left( /* nullptr */0), datum(in),
1074 : lineno(line_number),
1075 : minv(min_vertical), minh(min_horizontal),
1076 25822 : maxv(max_vertical), maxh(max_horizontal)
1077 : {
1078 25822 : }
1079 :
1080 51544 : element_list::~element_list ()
1081 : {
1082 25772 : if (datum != 0 /* nullptr */)
1083 25772 : delete datum;
1084 25772 : }
1085 :
1086 : class list {
1087 : public:
1088 : list ();
1089 : ~list ();
1090 : int is_less (element_list * /* a */,
1091 : element_list * /* b */);
1092 : void add (text_glob * /* in */,
1093 : int /* line_number */,
1094 : int /* min_vertical */,
1095 : int /* min_horizontal */,
1096 : int /* max_vertical */,
1097 : int /* max_horizontal */);
1098 : void sub_move_right (void);
1099 : void move_right (void);
1100 : void move_left (void);
1101 : int is_empty (void);
1102 : int is_equal_to_tail (void);
1103 : int is_equal_to_head (void);
1104 : void start_from_head (void);
1105 : void start_from_tail (void);
1106 : void insert (text_glob * /* in */);
1107 : void move_to (text_glob * /* in */);
1108 : text_glob *move_right_get_data (void);
1109 : text_glob *move_left_get_data (void);
1110 : text_glob *get_data (void);
1111 : private:
1112 : element_list *head;
1113 : element_list *tail;
1114 : element_list *ptr;
1115 : };
1116 :
1117 : /*
1118 : * list - construct an empty list.
1119 : */
1120 :
1121 71 : list::list ()
1122 71 : : head(0 /* nullptr */), tail(0 /* nullptr */), ptr(0 /* nullptr */)
1123 : {
1124 71 : }
1125 :
1126 : /*
1127 : * ~list - destroy a complete list.
1128 : */
1129 :
1130 108 : list::~list()
1131 : {
1132 54 : element_list *temp=head;
1133 :
1134 25746 : do {
1135 25800 : temp = head;
1136 25800 : if (temp != 0 /* nullptr */) {
1137 25772 : head = head->right;
1138 25772 : delete temp;
1139 : }
1140 25800 : } while ((head != 0 /* nullptr */) && (head != tail));
1141 54 : }
1142 :
1143 : /*
1144 : * is_less - returns TRUE if a is left of b if on the same line or
1145 : * if a is higher up the page than b.
1146 : */
1147 :
1148 : // TODO: boolify
1149 51480 : int list::is_less (element_list *a, element_list *b)
1150 : {
1151 51480 : assert(a != 0 /* nullptr */);
1152 51480 : assert(b != 0 /* nullptr */);
1153 : // was:
1154 : // if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
1155 51480 : if (a->lineno < b->lineno) {
1156 0 : return TRUE;
1157 51480 : } else if (a->lineno > b->lineno) {
1158 5594 : return FALSE;
1159 45886 : } else if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) {
1160 45886 : return (a->minh < b->minh);
1161 : } else {
1162 0 : return (a->maxv < b->maxv);
1163 : }
1164 : }
1165 :
1166 : /*
1167 : * add - adds a datum to the list in the order specified by the
1168 : * region position.
1169 : */
1170 :
1171 25779 : void list::add (text_glob *in, int line_number,
1172 : int min_vertical, int min_horizontal,
1173 : int max_vertical, int max_horizontal)
1174 : {
1175 : // create a new list element with datum and position fields
1176 : // initialized
1177 : element_list *t = new element_list(in, line_number,
1178 : min_vertical, min_horizontal,
1179 25779 : max_vertical, max_horizontal);
1180 : element_list *last;
1181 :
1182 : #if 0
1183 : fprintf(stderr, "[%s %d,%d,%d,%d] ",
1184 : in->text_string, min_vertical, min_horizontal,
1185 : max_vertical, max_horizontal);
1186 : fflush(stderr);
1187 : #endif
1188 :
1189 25779 : if (0 /* nullptr */ == head) {
1190 26 : head = t;
1191 26 : tail = t;
1192 26 : ptr = t;
1193 26 : t->left = t;
1194 26 : t->right = t;
1195 : } else {
1196 25753 : last = tail;
1197 :
1198 25753 : while ((last != head) && (is_less(t, last)))
1199 0 : last = last->left;
1200 :
1201 25753 : if (is_less(t, last)) {
1202 0 : t->right = last;
1203 0 : last->left->right = t;
1204 0 : t->left = last->left;
1205 0 : last->left = t;
1206 : // now check for a new head
1207 0 : if (last == head)
1208 0 : head = t;
1209 : } else {
1210 : // add t beyond last
1211 25753 : t->right = last->right;
1212 25753 : t->left = last;
1213 25753 : last->right->left = t;
1214 25753 : last->right = t;
1215 : // now check for a new tail
1216 25753 : if (last == tail)
1217 25753 : tail = t;
1218 : }
1219 : }
1220 25779 : }
1221 :
1222 : /*
1223 : * sub_move_right - removes the element which is currently pointed to
1224 : * by ptr from the list and moves ptr to the right.
1225 : */
1226 :
1227 24 : void list::sub_move_right (void)
1228 : {
1229 24 : element_list *t=ptr->right;
1230 :
1231 24 : if (head == tail) {
1232 0 : head = 0;
1233 0 : if (tail != 0 /* nullptr */)
1234 0 : delete tail;
1235 :
1236 0 : tail = 0 /* nullptr */;
1237 0 : ptr = 0 /* nullptr */;
1238 : } else {
1239 24 : if (head == ptr)
1240 0 : head = head->right;
1241 24 : if (tail == ptr)
1242 0 : tail = tail->left;
1243 24 : ptr->left->right = ptr->right;
1244 24 : ptr->right->left = ptr->left;
1245 24 : ptr = t;
1246 : }
1247 24 : }
1248 :
1249 : /*
1250 : * start_from_head - assigns ptr to the head.
1251 : */
1252 :
1253 117 : void list::start_from_head (void)
1254 : {
1255 117 : ptr = head;
1256 117 : }
1257 :
1258 : /*
1259 : * start_from_tail - assigns ptr to the tail.
1260 : */
1261 :
1262 0 : void list::start_from_tail (void)
1263 : {
1264 0 : ptr = tail;
1265 0 : }
1266 :
1267 : /*
1268 : * is_empty - returns TRUE if the list has no elements.
1269 : */
1270 :
1271 : // TODO: boolify
1272 26124 : int list::is_empty (void)
1273 : {
1274 26124 : return 0 /* nullptr */ == head;
1275 : }
1276 :
1277 : /*
1278 : * is_equal_to_tail - returns TRUE if the ptr equals the tail.
1279 : */
1280 :
1281 : // TODO: boolify
1282 1530 : int list::is_equal_to_tail (void)
1283 : {
1284 1530 : return ptr == tail;
1285 : }
1286 :
1287 : /*
1288 : * is_equal_to_head - returns TRUE if the ptr equals the head.
1289 : */
1290 :
1291 : // TODO: boolify
1292 109255 : int list::is_equal_to_head (void)
1293 : {
1294 109255 : return ptr == head;
1295 : }
1296 :
1297 : /*
1298 : * move_left - moves the ptr left.
1299 : */
1300 :
1301 11530 : void list::move_left (void)
1302 : {
1303 11530 : ptr = ptr->left;
1304 11530 : }
1305 :
1306 : /*
1307 : * move_right - moves the ptr right.
1308 : */
1309 :
1310 89073 : void list::move_right (void)
1311 : {
1312 89073 : ptr = ptr->right;
1313 89073 : }
1314 :
1315 : /*
1316 : * get_datum - returns the datum referenced via ptr.
1317 : */
1318 :
1319 103019 : text_glob* list::get_data (void)
1320 : {
1321 103019 : return ptr->datum;
1322 : }
1323 :
1324 : /*
1325 : * move_right_get_data - returns the datum referenced via ptr and moves
1326 : * ptr right.
1327 : */
1328 :
1329 25608 : text_glob* list::move_right_get_data (void)
1330 : {
1331 25608 : ptr = ptr->right;
1332 25608 : if (ptr == head)
1333 14 : return 0;
1334 : else
1335 25594 : return ptr->datum;
1336 : }
1337 :
1338 : /*
1339 : * move_left_get_data - returns the datum referenced via ptr and moves
1340 : * ptr right.
1341 : */
1342 :
1343 0 : text_glob* list::move_left_get_data (void)
1344 : {
1345 0 : ptr = ptr->left;
1346 0 : if (ptr == tail)
1347 0 : return 0;
1348 : else
1349 0 : return ptr->datum;
1350 : }
1351 :
1352 : /*
1353 : * insert - inserts data after the current position.
1354 : */
1355 :
1356 43 : void list::insert (text_glob *in)
1357 : {
1358 43 : if (is_empty())
1359 0 : fatal("list must not be empty if we are inserting data");
1360 : else {
1361 43 : if (0 /* nullptr */ == ptr)
1362 0 : ptr = head;
1363 :
1364 43 : element_list *t = new element_list(in, ptr->lineno,
1365 43 : ptr->minv, ptr->minh,
1366 43 : ptr->maxv, ptr->maxh);
1367 43 : if (ptr == tail)
1368 1 : tail = t;
1369 43 : ptr->right->left = t;
1370 43 : t->right = ptr->right;
1371 43 : ptr->right = t;
1372 43 : t->left = ptr;
1373 : }
1374 43 : }
1375 :
1376 : /*
1377 : * move_to - moves the current position to the point where data, in,
1378 : * exists. This is an expensive method and should be used
1379 : * sparingly.
1380 : */
1381 :
1382 46 : void list::move_to (text_glob *in)
1383 : {
1384 46 : ptr = head;
1385 148090 : while ((ptr != tail) && (ptr->datum != in))
1386 148044 : ptr = ptr->right;
1387 46 : }
1388 :
1389 : /*
1390 : * page class and methods
1391 : */
1392 :
1393 : class page {
1394 : public:
1395 : page (void);
1396 : void add (style * /* s */,
1397 : const string & /* str */,
1398 : int /* line_number */,
1399 : int /* min_vertical */,
1400 : int /* min_horizontal */,
1401 : int /* max_vertical */,
1402 : int /* max_horizontal */);
1403 : void add_tag (style * /* s */,
1404 : const string & /* str */,
1405 : int /* line_number */,
1406 : int /* min_vertical */,
1407 : int /* min_horizontal */,
1408 : int /* max_vertical */,
1409 : int /* max_horizontal */);
1410 : void add_and_encode (style * /* s */,
1411 : const string & /* str */,
1412 : int /* line_number */,
1413 : int /* min_vertical */,
1414 : int /* min_horizontal */,
1415 : int /* max_vertical */,
1416 : int /* max_horizontal */,
1417 : int /* is_tag */); // TODO: boolify?
1418 : void add_line (style * /* s */,
1419 : int /* line_number */,
1420 : int /* x1 */,
1421 : int /* y1 */,
1422 : int /* x2 */,
1423 : int /* y2 */,
1424 : int /* thickness */);
1425 : void insert_tag (const string & /* str */);
1426 : void dump_page (void); // debugging method
1427 :
1428 : // and the data
1429 :
1430 : list glyphs; // position of glyphs and specials on page
1431 : char_buffer buffer; // all characters for this page
1432 : };
1433 :
1434 37 : page::page()
1435 : {
1436 37 : }
1437 :
1438 : /*
1439 : * insert_tag - inserts a tag after the current position.
1440 : */
1441 :
1442 43 : void page::insert_tag (const string &str)
1443 : {
1444 43 : if (str.length() > 0) {
1445 43 : text_glob *g=new text_glob();
1446 43 : text_glob *f=glyphs.get_data();
1447 43 : g->text_glob_tag(&f->text_style, buffer.add_string(str),
1448 : str.length(), f->minv, f->minh, f->maxv, f->maxh);
1449 43 : glyphs.insert(g);
1450 : }
1451 43 : }
1452 :
1453 : /*
1454 : * add - add html text to the list of glyphs.
1455 : */
1456 :
1457 20281 : void page::add (style *s, const string &str,
1458 : int line_number,
1459 : int min_vertical, int min_horizontal,
1460 : int max_vertical, int max_horizontal)
1461 : {
1462 20281 : if (str.length() > 0) {
1463 20281 : text_glob *g=new text_glob();
1464 20281 : g->text_glob_html(s, buffer.add_string(str), str.length(),
1465 : min_vertical, min_horizontal,
1466 : max_vertical, max_horizontal);
1467 20281 : glyphs.add(g, line_number, min_vertical, min_horizontal,
1468 : max_vertical, max_horizontal);
1469 : }
1470 20281 : }
1471 :
1472 : /*
1473 : * add_tag - adds a troff tag, for example: .tl .sp .br
1474 : */
1475 :
1476 4460 : void page::add_tag (style *s, const string &str,
1477 : int line_number,
1478 : int min_vertical, int min_horizontal,
1479 : int max_vertical, int max_horizontal)
1480 : {
1481 4460 : if (str.length() > 0) {
1482 : text_glob *g;
1483 :
1484 8920 : if (strncmp((str+'\0').contents(), "devtag:.auto-image",
1485 4460 : strlen("devtag:.auto-image")) == 0) {
1486 58 : g = new text_glob();
1487 58 : g->text_glob_auto_image(s, buffer.add_string(str), str.length(),
1488 : min_vertical, min_horizontal,
1489 : max_vertical, max_horizontal);
1490 : } else {
1491 4402 : g = new text_glob();
1492 4402 : g->text_glob_tag(s, buffer.add_string(str), str.length(),
1493 : min_vertical, min_horizontal,
1494 : max_vertical, max_horizontal);
1495 : }
1496 4460 : glyphs.add(g, line_number, min_vertical, min_horizontal,
1497 : max_vertical, max_horizontal);
1498 : }
1499 4460 : }
1500 :
1501 : /*
1502 : * add_line - adds the <line> primitive providing that y1==y2
1503 : */
1504 :
1505 0 : void page::add_line (style *s,
1506 : int line_number,
1507 : int x_1, int y_1, int x_2, int y_2,
1508 : int thickness)
1509 : {
1510 0 : if (y_1 == y_2) {
1511 0 : text_glob *g = new text_glob();
1512 0 : g->text_glob_line(s,
1513 : min(y_1, y_2), min(x_1, x_2),
1514 : max(y_1, y_2), max(x_1, x_2),
1515 : thickness);
1516 0 : glyphs.add(g, line_number,
1517 : min(y_1, y_2), min(x_1, x_2),
1518 : max(y_1, y_2), max(x_1, x_2));
1519 : }
1520 0 : }
1521 :
1522 : /*
1523 : * to_numerical_char_ref - returns a numerical character reference of
1524 : * unicode character code `ch`.
1525 : */
1526 :
1527 0 : static char *to_numerical_char_ref (unsigned int ch)
1528 : {
1529 : // Make static buffer large enough for a 64-bit `int` type in
1530 : // hexadecimal (8 bytes) plus '&#x;' plus null terminator.
1531 : static char buf[8 + 4 + 1];
1532 0 : sprintf(buf, "&#x%X;", ch);
1533 0 : return buf;
1534 : }
1535 :
1536 : /*
1537 : * add_and_encode - adds a special string to the page, it translates
1538 : * the string into html glyphs. The special string
1539 : * will have come from x X html: and can contain troff
1540 : * character encodings which appear as \[char]. A
1541 : * sequence of \\ represents \.
1542 : * So for example we can write:
1543 : * "cost = \[Po]3.00 file = \\foo\\bar"
1544 : * which is translated into:
1545 : * "cost = £3.00 file = \foo\bar"
1546 : */
1547 :
1548 846 : void page::add_and_encode (style *s,
1549 : const string &str,
1550 : int line_number,
1551 : int min_vertical,
1552 : int min_horizontal,
1553 : int max_vertical,
1554 : int max_horizontal,
1555 : int is_tag)
1556 : {
1557 846 : string html_string;
1558 : const char *html_glyph;
1559 846 : int i = 0;
1560 846 : const int len = str.length();
1561 :
1562 846 : assert(s != 0 /* nullptr */);
1563 846 : if (0 /* nullptr */ == s->f)
1564 0 : return;
1565 11258 : while (i < len) {
1566 10412 : if ((i + 1 < len) && (str.substring(i, 2) == string("\\["))) {
1567 : // start of escape
1568 1 : i += 2; // move over \[
1569 1 : int a = i;
1570 3 : while ((i < len) && (str[i] != ']'))
1571 2 : i++;
1572 1 : if (i > 0) {
1573 2 : string troff_charname = str.substring(a, i - a);
1574 1 : html_glyph = get_html_translation(s->f, troff_charname);
1575 1 : if (html_glyph != 0 /* nullptr */)
1576 1 : html_string += html_glyph;
1577 : else {
1578 0 : glyph *g = name_to_glyph((troff_charname + '\0').contents());
1579 0 : if (s->f->contains(g))
1580 0 : html_string += s->f->get_code(g);
1581 : }
1582 : }
1583 : }
1584 : else
1585 10411 : html_string += str[i];
1586 10412 : i++;
1587 : }
1588 846 : if (html_string.length() > 0) {
1589 846 : text_glob *g=new text_glob();
1590 846 : if (is_tag)
1591 283 : g->text_glob_tag(s, buffer.add_string(html_string),
1592 : html_string.length(),
1593 : min_vertical, min_horizontal,
1594 : max_vertical, max_horizontal);
1595 : else
1596 563 : g->text_glob_special(s, buffer.add_string(html_string),
1597 : html_string.length(),
1598 : min_vertical, min_horizontal,
1599 : max_vertical, max_horizontal);
1600 846 : glyphs.add(g, line_number, min_vertical,
1601 : min_horizontal, max_vertical, max_horizontal);
1602 : }
1603 : }
1604 :
1605 : /*
1606 : * dump_page - dump the page contents for debugging purposes.
1607 : */
1608 :
1609 120 : void page::dump_page(void)
1610 : {
1611 : #if defined(DEBUG_TABLES)
1612 : text_glob *old_pos = glyphs.get_data();
1613 : text_glob *g;
1614 :
1615 : printf("\n<!--\n");
1616 : printf("\n\ndebugging start\n");
1617 : glyphs.start_from_head();
1618 : do {
1619 : g = glyphs.get_data();
1620 : if (g->is_tab_ts()) {
1621 : printf("\n\n");
1622 : if (g->get_table() != 0 /* nullptr */)
1623 : g->get_table()->dump_table();
1624 : }
1625 : printf("%s ", g->text_string);
1626 : if (g->is_tab_te())
1627 : printf("\n\n");
1628 : glyphs.move_right();
1629 : } while (! glyphs.is_equal_to_head());
1630 : glyphs.move_to(old_pos);
1631 : printf("\ndebugging end\n\n");
1632 : printf("\n-->\n");
1633 : fflush(stdout);
1634 : #endif
1635 120 : }
1636 :
1637 : /*
1638 : * font classes and methods
1639 : */
1640 :
1641 : class html_font : public font {
1642 : html_font(const char * /* nm */);
1643 : public:
1644 : int encoding_index;
1645 : char *encoding;
1646 : char *reencoded_name;
1647 : ~html_font();
1648 : static html_font *load_html_font(const char * /* s */);
1649 : };
1650 :
1651 18 : html_font *html_font::load_html_font(const char *s)
1652 : {
1653 18 : html_font *f = new html_font(s);
1654 18 : if (!f->load()) {
1655 0 : delete f;
1656 0 : return 0 /* nullptr */;
1657 : }
1658 18 : return f;
1659 : }
1660 :
1661 18 : html_font::html_font(const char *nm)
1662 18 : : font(nm)
1663 : {
1664 18 : }
1665 :
1666 36 : html_font::~html_font()
1667 : {
1668 36 : }
1669 :
1670 : /*
1671 : * a simple class to contain the header to this document
1672 : */
1673 :
1674 : class title_desc {
1675 : public:
1676 : title_desc ();
1677 : ~title_desc ();
1678 :
1679 : int has_been_written;
1680 : int has_been_found;
1681 : int with_h1;
1682 : string text;
1683 : };
1684 :
1685 :
1686 17 : title_desc::title_desc ()
1687 17 : : has_been_written(FALSE), has_been_found(FALSE), with_h1(FALSE)
1688 : {
1689 17 : }
1690 :
1691 17 : title_desc::~title_desc ()
1692 : {
1693 17 : }
1694 :
1695 : class header_desc {
1696 : public:
1697 : header_desc ();
1698 : ~header_desc ();
1699 :
1700 : int no_of_level_one_headings; // how many .SH or .NH 1 have we found?
1701 : int no_of_headings; // how many headings have we found?
1702 : char_buffer headings; // all the headings used in the document
1703 : list headers; // list of headers built from .NH and .SH
1704 : list header_filename; // in which file is this header?
1705 : int header_level; // current header level
1706 : int written_header; // have we written the header yet?
1707 : // TODO: boolify?
1708 : string header_buffer; // current header text
1709 :
1710 : void write_headings (FILE * /* f */,
1711 : int /* force */); // TODO: boolify
1712 : };
1713 :
1714 17 : header_desc::header_desc ()
1715 : : no_of_level_one_headings(0), no_of_headings(0), header_level(2),
1716 17 : written_header(0)
1717 : {
1718 17 : }
1719 :
1720 17 : header_desc::~header_desc ()
1721 : {
1722 17 : }
1723 :
1724 : /*
1725 : * write_headings - emits a list of links for the headings in this
1726 : * document
1727 : */
1728 :
1729 35 : void header_desc::write_headings (FILE *f, int force)
1730 : {
1731 : text_glob *g;
1732 :
1733 35 : assert(f != 0 /* nullptr */);
1734 35 : if (auto_links || force) {
1735 34 : if (! headers.is_empty()) {
1736 23 : int h=1;
1737 :
1738 23 : headers.start_from_head();
1739 23 : header_filename.start_from_head();
1740 23 : if (xhtml == dialect)
1741 0 : fputs("<p>", f);
1742 362 : do {
1743 385 : g = headers.get_data();
1744 385 : fputs("<a href=\"", f);
1745 385 : if (multiple_files && (! header_filename.is_empty())) {
1746 377 : text_glob *fn = header_filename.get_data();
1747 377 : fputs(fn->text_string, f);
1748 : }
1749 385 : fputs("#", f);
1750 385 : if (simple_anchors) {
1751 612 : string buffer(ANCHOR_TEMPLATE);
1752 :
1753 306 : buffer += as_string(h);
1754 306 : buffer += '\0';
1755 306 : fprintf(f, "%s", buffer.contents());
1756 : } else
1757 79 : fputs(g->text_string, f);
1758 385 : h++;
1759 385 : fputs("\">", f);
1760 385 : fputs(g->text_string, f);
1761 385 : fputs("</a>", f);
1762 385 : if (xhtml == dialect)
1763 0 : fputs("<br/>\n", f);
1764 : else
1765 385 : fputs("<br>\n", f);
1766 385 : headers.move_right();
1767 385 : if (multiple_files && (! header_filename.is_empty()))
1768 377 : header_filename.move_right();
1769 385 : } while (! headers.is_equal_to_head());
1770 23 : fputs("\n", f);
1771 23 : if (xhtml == dialect)
1772 0 : fputs("</p>\n", f);
1773 : }
1774 : }
1775 35 : }
1776 :
1777 : struct assert_pos {
1778 : assert_pos *next;
1779 : const char *val;
1780 : const char *id;
1781 : };
1782 :
1783 : class assert_state {
1784 : public:
1785 : assert_state ();
1786 : ~assert_state ();
1787 :
1788 : void addx (const char * /* c */,
1789 : const char * /* i */,
1790 : const char * /* v */,
1791 : const char * /* f */,
1792 : const char * /* l */);
1793 : void addy (const char * /* c */,
1794 : const char * /* i */,
1795 : const char * /* v */,
1796 : const char * /* f */,
1797 : const char * /* l */);
1798 : void build(const char * /* c */,
1799 : const char * /* v */,
1800 : const char * /* f */,
1801 : const char * /* l */);
1802 : void check_br (int /* br */);
1803 : void check_ce (int /* ce */);
1804 : void check_fi (int /* fi */);
1805 : void check_sp (int /* sp */);
1806 : void reset (void);
1807 :
1808 : private:
1809 : int check_br_flag;
1810 : int check_ce_flag;
1811 : int check_fi_flag;
1812 : int check_sp_flag;
1813 : const char *val_br;
1814 : const char *val_ce;
1815 : const char *val_fi;
1816 : const char *val_sp;
1817 : const char *file_br;
1818 : const char *file_ce;
1819 : const char *file_fi;
1820 : const char *file_sp;
1821 : const char *line_br;
1822 : const char *line_ce;
1823 : const char *line_fi;
1824 : const char *line_sp;
1825 :
1826 : assert_pos *xhead;
1827 : assert_pos *yhead;
1828 :
1829 : void add (assert_pos ** /* h */,
1830 : const char * /* c */,
1831 : const char * /* i */,
1832 : const char * /* v */,
1833 : const char * /* f */,
1834 : const char * /* l */);
1835 : void compare (assert_pos * /* t */,
1836 : const char * /* v */,
1837 : const char * /* f */,
1838 : const char * /* l */);
1839 : void close (const char * /* c */);
1840 : void set (const char * /* c */,
1841 : const char * /* v */,
1842 : const char * /* f */,
1843 : const char * /* l */);
1844 : void check_value (const char * /* s */,
1845 : int /* v */,
1846 : const char *nam /* e */,
1847 : const char * /* f */,
1848 : const char * /* l */,
1849 : int * /* flag */); // TODO: boolify
1850 : int check_value_error(int /* c */,
1851 : int /* v */,
1852 : const char * /* s */,
1853 : const char *nam /* e */,
1854 : const char * /* f */,
1855 : const char * /* l */,
1856 : int /* flag */); // TODO: boolify
1857 : };
1858 :
1859 17 : assert_state::assert_state ()
1860 : {
1861 17 : reset();
1862 17 : val_br = 0;
1863 17 : val_ce = 0;
1864 17 : val_fi = 0;
1865 17 : val_sp = 0;
1866 17 : file_br = 0;
1867 17 : file_ce = 0;
1868 17 : file_fi = 0;
1869 17 : file_sp = 0;
1870 17 : line_br = 0;
1871 17 : line_ce = 0;
1872 17 : line_fi = 0;
1873 17 : line_sp = 0;
1874 17 : xhead = 0;
1875 17 : yhead = 0;
1876 17 : }
1877 :
1878 34 : assert_state::~assert_state ()
1879 : {
1880 : assert_pos *t;
1881 :
1882 17 : while (xhead != 0 /* nullptr */) {
1883 0 : t = xhead;
1884 0 : xhead = xhead->next;
1885 0 : delete[] t->val;
1886 0 : delete[] t->id;
1887 0 : delete t;
1888 : }
1889 17 : while (yhead != 0 /* nullptr */) {
1890 0 : t = yhead;
1891 0 : yhead = yhead->next;
1892 0 : delete[] t->val;
1893 0 : delete[] t->id;
1894 0 : delete t;
1895 : }
1896 17 : }
1897 :
1898 60 : void assert_state::reset (void)
1899 : {
1900 60 : check_br_flag = 0;
1901 60 : check_ce_flag = 0;
1902 60 : check_fi_flag = 0;
1903 60 : check_sp_flag = 0;
1904 60 : }
1905 :
1906 0 : void assert_state::add (assert_pos **h,
1907 : const char *c, const char *i, const char *v,
1908 : const char *f, const char *l)
1909 : {
1910 0 : assert(h != 0 /* nullptr */);
1911 0 : assert(*h != 0 /* nullptr */);
1912 :
1913 0 : assert_pos *t = *h;
1914 :
1915 0 : while (t != 0 /* nullptr */) {
1916 0 : if (strcmp(t->id, i) == 0)
1917 0 : break;
1918 0 : t = t->next;
1919 : }
1920 0 : if ((t != 0 /* nullptr */) && (v != 0 /* nullptr */) && (v[0] != '='))
1921 0 : compare(t, v, f, l);
1922 : else {
1923 0 : if (0 /* nullptr */ == t) {
1924 0 : t = new assert_pos;
1925 0 : t->next = *h;
1926 0 : (*h) = t;
1927 : }
1928 0 : if ((0 == v) || (v[0] != '=')) {
1929 0 : if (0 /* nullptr */ == f)
1930 0 : f = strsave("stdin");
1931 0 : if (0 /* nullptr */ == l)
1932 0 : l = strsave("<none>");
1933 0 : if (0 /* nullptr */ == v)
1934 0 : v = "no value at all";
1935 0 : fprintf(stderr, "%s:%s:%s: error in assertion format of id=%s;"
1936 : " expected value prefixed with an '=', got %s\n",
1937 : program_name, f, l, i, v);
1938 : }
1939 0 : t->id = i;
1940 0 : t->val = v;
1941 0 : delete[] c;
1942 0 : delete[] f;
1943 0 : delete[] l;
1944 : }
1945 0 : }
1946 :
1947 0 : void assert_state::addx (const char *c, const char *i, const char *v,
1948 : const char *f, const char *l)
1949 : {
1950 0 : add(&xhead, c, i, v, f, l);
1951 0 : }
1952 :
1953 0 : void assert_state::addy (const char *c, const char *i, const char *v,
1954 : const char *f, const char *l)
1955 : {
1956 0 : add(&yhead, c, i, v, f, l);
1957 0 : }
1958 :
1959 0 : void assert_state::compare(assert_pos *t,
1960 : const char *v, const char *f, const char *l)
1961 : {
1962 0 : assert(t != 0 /* nullptr */);
1963 0 : assert(v != 0 /* nullptr */);
1964 0 : const char *s=t->val;
1965 :
1966 0 : while ((*v) == '=')
1967 0 : v++;
1968 0 : while ((*s) == '=')
1969 0 : s++;
1970 :
1971 0 : if (strcmp(v, s) != 0) {
1972 0 : if (0 /* nullptr */ == f)
1973 0 : f = "stdin";
1974 0 : if (0 /* nullptr */ == l)
1975 0 : l = "<none>";
1976 0 : fprintf(stderr, "%s:%s:%s: grohtml assertion failed at id%s: "
1977 : "expected %s, got %s\n", program_name, f, l, t->id,
1978 : s, v);
1979 : }
1980 0 : }
1981 :
1982 0 : void assert_state::close (const char *c)
1983 : {
1984 0 : assert(c != 0 /* nullptr */);
1985 0 : if (strcmp(c, "sp") == 0)
1986 0 : check_sp_flag = 0;
1987 0 : else if (strcmp(c, "br") == 0)
1988 0 : check_br_flag = 0;
1989 0 : else if (strcmp(c, "fi") == 0)
1990 0 : check_fi_flag = 0;
1991 0 : else if (strcmp(c, "nf") == 0)
1992 0 : check_fi_flag = 0;
1993 0 : else if (strcmp(c, "ce") == 0)
1994 0 : check_ce_flag = 0;
1995 : else
1996 0 : fprintf(stderr, "%s: ignoring unrecognized tag '%s'\n",
1997 : program_name, c);
1998 0 : }
1999 :
2000 0 : const char *replace_negate_str (const char *before, char *after)
2001 : {
2002 0 : if (before != 0 /* nullptr */)
2003 0 : delete[] before;
2004 :
2005 0 : assert(after != 0 /* nullptr */);
2006 0 : if (strlen(after) > 0) {
2007 0 : int d = atoi(after);
2008 :
2009 0 : if ((d < 0) || (d > 1)) {
2010 0 : fprintf(stderr, "%s: expected nf/fi value of 0 or 1, got '%s';"
2011 : " ignoring\n", program_name, after);
2012 0 : d = 0;
2013 : }
2014 0 : if (0 == d)
2015 0 : after[0] = '1';
2016 : else
2017 0 : after[0] = '0';
2018 0 : after[1] = '\0';
2019 : }
2020 0 : return after;
2021 : }
2022 :
2023 0 : const char *replace_str (const char *before, const char *after)
2024 : {
2025 0 : if (before != 0 /* nullptr */)
2026 0 : delete[] before;
2027 0 : return after;
2028 : }
2029 :
2030 0 : void assert_state::set (const char *c, const char *v,
2031 : const char *f, const char *l)
2032 : {
2033 0 : assert(c != 0 /* nullptr */);
2034 0 : assert(v != 0 /* nullptr */);
2035 :
2036 0 : if (0 /* nullptr */ == l)
2037 0 : l = "<none>";
2038 0 : if (0 /* nullptr */ == f)
2039 0 : f = "stdin";
2040 :
2041 : // fprintf(stderr, "%s:%s:setting %s to %s\n", f, l, c, v);
2042 0 : if (strcmp(c, "sp") == 0) {
2043 0 : check_sp_flag = 1;
2044 0 : val_sp = replace_str(val_sp, strsave(v));
2045 0 : file_sp = replace_str(file_sp, strsave(f));
2046 0 : line_sp = replace_str(line_sp, strsave(l));
2047 0 : } else if (strcmp(c, "br") == 0) {
2048 0 : check_br_flag = 1;
2049 0 : val_br = replace_str(val_br, strsave(v));
2050 0 : file_br = replace_str(file_br, strsave(f));
2051 0 : line_br = replace_str(line_br, strsave(l));
2052 0 : } else if (strcmp(c, "fi") == 0) {
2053 0 : check_fi_flag = 1;
2054 0 : val_fi = replace_str(val_fi, strsave(v));
2055 0 : file_fi = replace_str(file_fi, strsave(f));
2056 0 : line_fi = replace_str(line_fi, strsave(l));
2057 0 : } else if (strcmp(c, "nf") == 0) {
2058 0 : check_fi_flag = 1;
2059 0 : val_fi = replace_negate_str(val_fi, strsave(v));
2060 0 : file_fi = replace_str(file_fi, strsave(f));
2061 0 : line_fi = replace_str(line_fi, strsave(l));
2062 0 : } else if (strcmp(c, "ce") == 0) {
2063 0 : check_ce_flag = 1;
2064 0 : val_ce = replace_str(val_ce, strsave(v));
2065 0 : file_ce = replace_str(file_ce, strsave(f));
2066 0 : line_ce = replace_str(line_ce, strsave(l));
2067 : }
2068 0 : }
2069 :
2070 : /*
2071 : * build - builds the troff state assertion.
2072 : * see tmac/www.tmac for cmd examples.
2073 : */
2074 :
2075 0 : void assert_state::build (const char *c, const char *v,
2076 : const char *f, const char *l)
2077 : {
2078 0 : assert(c != 0 /* nullptr */);
2079 0 : if (c[0] == '{')
2080 0 : set(&c[1], v, f, l);
2081 0 : if (c[0] == '}')
2082 0 : close(&c[1]);
2083 0 : }
2084 :
2085 : // TODO: boolify
2086 0 : int assert_state::check_value_error (int c, int v, const char *s,
2087 : const char *name, const char *f,
2088 : const char *l, int flag)
2089 : {
2090 0 : if (0 /* nullptr */ == c) {
2091 0 : if (0 /* nullptr */ == f)
2092 0 : f = "stdin";
2093 0 : if (0 /* nullptr */ == l)
2094 0 : l = "<none>";
2095 0 : fprintf(stderr, "%s:%s:%s: troff state assertion failed; "
2096 : "expected %s to be %s, got %d\n", program_name, f, l, name,
2097 : s, v);
2098 0 : return 0;
2099 : }
2100 0 : return flag;
2101 : }
2102 :
2103 0 : void assert_state::check_value (const char *s, int v, const char *name,
2104 : const char *f, const char *l, int *flag)
2105 : {
2106 0 : assert(s != 0 /* nullptr */);
2107 0 : assert(flag != 0 /* nullptr */);
2108 0 : if (strncmp(s, "<=", 2) == 0)
2109 0 : *flag = check_value_error(v <= atoi(&s[2]), v, s, name, f, l, *flag);
2110 0 : else if (strncmp(s, ">=", 2) == 0)
2111 0 : *flag = check_value_error(v >= atoi(&s[2]), v, s, name, f, l, *flag);
2112 0 : else if (strncmp(s, "==", 2) == 0)
2113 0 : *flag = check_value_error(v == atoi(&s[2]), v, s, name, f, l, *flag);
2114 0 : else if (strncmp(s, "!=", 2) == 0)
2115 0 : *flag = check_value_error(v != atoi(&s[2]), v, s, name, f, l, *flag);
2116 0 : else if (strncmp(s, "<", 1) == 0)
2117 0 : *flag = check_value_error(v < atoi(&s[2]), v, s, name, f, l, *flag);
2118 0 : else if (strncmp(s, ">", 1) == 0)
2119 0 : *flag = check_value_error(v > atoi(&s[2]), v, s, name, f, l, *flag);
2120 0 : else if (strncmp(s, "=", 1) == 0)
2121 0 : *flag = check_value_error(v == atoi(&s[1]), v, s, name, f, l, *flag);
2122 : else
2123 0 : *flag = check_value_error(v == atoi(s), v, s, name, f, l, *flag);
2124 0 : }
2125 :
2126 21463 : void assert_state::check_sp (int sp)
2127 : {
2128 21463 : if (check_sp_flag)
2129 0 : check_value(val_sp, sp, "sp", file_sp, line_sp, &check_sp_flag);
2130 21463 : }
2131 :
2132 101969 : void assert_state::check_fi (int fi)
2133 : {
2134 101969 : if (check_fi_flag)
2135 0 : check_value(val_fi, fi, "fi", file_fi, line_fi, &check_fi_flag);
2136 101969 : }
2137 :
2138 21774 : void assert_state::check_br (int br)
2139 : {
2140 21774 : if (check_br_flag)
2141 0 : check_value(val_br, br, "br", file_br, line_br, &check_br_flag);
2142 21774 : }
2143 :
2144 25189 : void assert_state::check_ce (int ce)
2145 : {
2146 25189 : if (check_ce_flag)
2147 0 : check_value(val_ce, ce, "ce", file_ce, line_ce, &check_ce_flag);
2148 25189 : }
2149 :
2150 : class html_printer : public printer {
2151 : files file_list;
2152 : simple_output html;
2153 : int res;
2154 : glyph *space_glyph;
2155 : int space_width;
2156 : int no_of_printed_pages;
2157 : int paper_length;
2158 : string sbuf;
2159 : int sbuf_start_hpos;
2160 : int sbuf_vpos;
2161 : int sbuf_end_hpos;
2162 : int sbuf_prev_hpos;
2163 : int sbuf_kern;
2164 : style sbuf_style;
2165 : int last_sbuf_length;
2166 : int overstrike_detected;
2167 : style output_style;
2168 : int output_hpos;
2169 : int output_vpos;
2170 : int output_vpos_max;
2171 : int output_draw_point_size;
2172 : int line_thickness;
2173 : int output_line_thickness;
2174 : unsigned char output_space_code;
2175 : char *inside_font_style;
2176 : int page_number;
2177 : title_desc title;
2178 : header_desc header;
2179 : int header_indent;
2180 : int suppress_sub_sup;
2181 : int cutoff_heading;
2182 : page *page_contents;
2183 : html_text *current_paragraph;
2184 : html_indent *indent;
2185 : html_table *table;
2186 : int end_center;
2187 : int end_tempindent;
2188 : TAG_ALIGNMENT next_tag;
2189 : int fill_on;
2190 : int max_linelength;
2191 : int linelength;
2192 : int pageoffset;
2193 : int troff_indent;
2194 : int device_indent;
2195 : int temp_indent;
2196 : int pointsize;
2197 : int vertical_spacing;
2198 : int line_number;
2199 : color *background;
2200 : int seen_indent;
2201 : int next_indent;
2202 : int seen_pageoffset;
2203 : int next_pageoffset;
2204 : int seen_linelength;
2205 : int next_linelength;
2206 : int seen_center;
2207 : int next_center;
2208 : int seen_space;
2209 : int seen_break;
2210 : int current_column;
2211 : int row_space;
2212 : assert_state as;
2213 :
2214 : void flush_sbuf ();
2215 : void set_style (const style & /* sty */);
2216 : void set_space_code (unsigned char /* c */);
2217 : void set_line_thickness (const environment * /* env */);
2218 : void terminate_current_font(void);
2219 : void flush_font (void);
2220 : void add_to_sbuf (glyph * /* g */,
2221 : const string & /* s */);
2222 : void write_title (int /* in_head */); // TODO: boolify
2223 : int sbuf_continuation (glyph * /* g */,
2224 : const char * /* name */,
2225 : const environment * /* env */,
2226 : int /* w */);
2227 : void flush_page (void);
2228 : void troff_tag (text_glob * /* g */);
2229 : void flush_globs (void);
2230 : void emit_line (text_glob * /* g */);
2231 : void emit_raw (text_glob * /* g */);
2232 : void emit_html (text_glob * /* g */);
2233 : void determine_space (text_glob * /* g */);
2234 : void start_font (const char * /* name */);
2235 : void end_font (const char * /* name */);
2236 : int is_font_courier (font * /* f */);
2237 : int is_line_start (int /* nf */); // TODO: boolify
2238 : int is_courier_until_eol (void);
2239 : void start_size (int /* from */,
2240 : int /* to */);
2241 : void do_font (text_glob * /* g */);
2242 : void do_center (char * /* arg */);
2243 : void do_check_center (void);
2244 : void do_break (void);
2245 : void do_space (char * /* arg */);
2246 : void do_eol (void);
2247 : void do_eol_ce (void);
2248 : void do_title (void);
2249 : void do_fill (char * /* arg */);
2250 : void do_heading (char * /* arg */);
2251 : void write_header (void);
2252 : void determine_header_level(int /* level */);
2253 : void do_linelength (char * /* arg */);
2254 : void do_pageoffset (char * /* arg */);
2255 : void do_indentation (char * /* arg */);
2256 : void do_tempindent (char * /* arg */);
2257 : void do_indentedparagraph (void);
2258 : void do_verticalspacing (char * /* arg */);
2259 : void do_pointsize (char * /* arg */);
2260 : void do_centered_image (void);
2261 : void do_left_image (void);
2262 : void do_right_image (void);
2263 : void do_auto_image (text_glob * /* g */,
2264 : const char * /* filename */);
2265 : void do_links (void);
2266 : void do_flush (void);
2267 : void do_job_name (char * /* name */);
2268 : void do_head (char * /* name */);
2269 : void insert_split_file (void);
2270 : int is_in_middle (int /* left */,
2271 : int /* right */);
2272 : void do_sup_or_sub (text_glob * /* g */);
2273 : int start_subscript (text_glob * /* g */);
2274 : int end_subscript (text_glob * /* g */);
2275 : int start_superscript (text_glob * /* g */);
2276 : int end_superscript (text_glob * /* g */);
2277 : void outstanding_eol (int /* n */);
2278 : int is_bold (font * /* f */);
2279 : font *make_bold (font * /* f */);
2280 : int overstrike (glyph * /* g */,
2281 : const char * /* name */,
2282 : const environment * /* env */,
2283 : int /* w */);
2284 : void do_body (void);
2285 : int next_horiz_pos (text_glob * /* g */,
2286 : int /* nf */); // TODO: boolify
2287 : void lookahead_for_tables (void);
2288 : void insert_tab_te (void);
2289 : text_glob *insert_tab_ts (text_glob * /* where */);
2290 : void insert_tab0_foreach_tab(void);
2291 : void insert_tab_0 (text_glob * /* where */);
2292 : void do_indent (int /* in */,
2293 : int /* pageoff */,
2294 : int /* linelen */);
2295 : void shutdown_table (void);
2296 : void do_tab_ts (text_glob * /* g */);
2297 : void do_tab_te (void);
2298 : void do_col (char * /* s */);
2299 : void do_tab (char * /* s */);
2300 : void do_tab0 (void);
2301 : int calc_nf (text_glob * /* g */,
2302 : int nf); // TODO: boolify
2303 : void calc_po_in (text_glob * /* g */,
2304 : int nf); // TODO: boolify
2305 : void remove_tabs (void);
2306 : void remove_courier_tabs (void);
2307 : void update_min_max (colType /* type_of_col */,
2308 : int * /* minimum */,
2309 : int * /* maximum */,
2310 : text_glob * /* g */);
2311 : void add_table_end (const char * /* debug_string */);
2312 : void do_file_components (void);
2313 : void write_navigation (const string & /* top */,
2314 : const string & /* prev */,
2315 : const string & /* next */,
2316 : const string & /* current */);
2317 : void emit_link (const string & /* to */,
2318 : const char * /* name */);
2319 : int get_troff_indent (void);
2320 : void restore_troff_indent (void);
2321 : void handle_assertion (int /* minv */,
2322 : int /* minh */,
2323 : int /* maxv */,
2324 : int /* maxh */,
2325 : const char * /* s */);
2326 : void handle_state_assertion (text_glob * /* g */);
2327 : void do_end_para (text_glob * /* g */);
2328 : int round_width (int /* x */);
2329 : void handle_tag_within_title(text_glob * /* g */);
2330 : void writeHeadMetaStyle (void);
2331 : void handle_valid_flag (int /* needs_para */); // TODO: boolify
2332 : void do_math (text_glob * /* g */);
2333 : void write_html_anchor (text_glob * /* h */);
2334 : void write_xhtml_anchor (text_glob * /* h */);
2335 : // ADD HERE
2336 :
2337 : public:
2338 : html_printer ();
2339 : ~html_printer ();
2340 : void set_char (glyph * /*g */,
2341 : font * /*f */,
2342 : const environment * /* env */,
2343 : int /* w */,
2344 : const char * /* name */);
2345 : void set_numbered_char (int /* num */,
2346 : const environment * /* env */,
2347 : int * /* widthp */);
2348 : glyph *set_char_and_width(const char * /* nm */,
2349 : const environment * /* env */,
2350 : int * /* widthp */,
2351 : font ** /* f */);
2352 : void draw (int /* code */,
2353 : int * /* p */,
2354 : int np, // TODO: -> npoints
2355 : const environment * /* env */);
2356 : void begin_page (int /* n */);
2357 : void end_page (int); // XXX: unused argument
2358 : void special (char * /* arg */,
2359 : const environment * /* env */,
2360 : char /* type */);
2361 : void devtag (char * /* arg */,
2362 : const environment * /* env */,
2363 : char /* type */);
2364 : font *make_font (const char * /* nm */);
2365 : void end_of_line ();
2366 : };
2367 :
2368 17 : printer *make_printer()
2369 : {
2370 17 : return new html_printer;
2371 : }
2372 :
2373 : static void usage(FILE *stream);
2374 :
2375 20281 : void html_printer::set_style(const style &sty)
2376 : {
2377 20281 : const char *fontname = sty.f->get_filename();
2378 20281 : if (0 /* nullptr */ == fontname)
2379 : // XXX: Is this the only circumstance that can cause a null font
2380 : // description file name? ps.cpp uses sty.f->get_internal_name()...
2381 0 : fatal("cannot set style; font description file '%1' lacks an"
2382 0 : " 'internalname' directive", fontname);
2383 :
2384 : #if 0
2385 : change_font(fontname, (font::res / (72 * font::sizescale))
2386 : * sty.point_size);
2387 : #endif
2388 20281 : }
2389 :
2390 : /*
2391 : * is_bold - returns TRUE if font, f, is bold.
2392 : */
2393 :
2394 0 : int html_printer::is_bold (font *f)
2395 : {
2396 0 : assert(f != 0 /* nullptr */);
2397 : // XXX: This property should be inferred from font description data,
2398 : // not the file name.
2399 0 : const char *fontname = f->get_filename();
2400 0 : return (strcmp(fontname, "B") == 0) || (strcmp(fontname, "BI") == 0);
2401 : }
2402 :
2403 : /*
2404 : * make_bold - if a bold style for f exists, return it.
2405 : */
2406 :
2407 0 : font *html_printer::make_bold (font *f)
2408 : {
2409 0 : assert(f != 0 /* nullptr */);
2410 : // XXX: This logic locks us into a font description file naming
2411 : // scheme.
2412 0 : const char *fontname = f->get_filename();
2413 :
2414 0 : if (strcmp(fontname, "B") == 0)
2415 0 : return f;
2416 0 : if (strcmp(fontname, "I") == 0)
2417 0 : return font::load_font("BI");
2418 0 : if (strcmp(fontname, "BI") == 0)
2419 0 : return f;
2420 0 : return 0 /* nullptr */;
2421 : }
2422 :
2423 5452 : void html_printer::end_of_line()
2424 : {
2425 5452 : flush_sbuf();
2426 5452 : line_number++;
2427 5452 : }
2428 :
2429 : /*
2430 : * emit_line - writes out a horizontal rule.
2431 : */
2432 :
2433 0 : void html_printer::emit_line (text_glob *)
2434 : {
2435 : // --fixme-- needs to know the length in percentage
2436 0 : if (xhtml == dialect)
2437 0 : html.put_string("<hr/>");
2438 : else
2439 0 : html.put_string("<hr>");
2440 0 : }
2441 :
2442 : /*
2443 : * restore_troff_indent - is called when we have temporarily shutdown
2444 : * indentation (typically done when we have
2445 : * centered an image).
2446 : */
2447 :
2448 76 : void html_printer::restore_troff_indent (void)
2449 : {
2450 76 : troff_indent = next_indent;
2451 76 : if (troff_indent > 0) {
2452 : /*
2453 : * force device indentation
2454 : */
2455 68 : device_indent = 0;
2456 68 : do_indent(get_troff_indent(), pageoffset, linelength);
2457 : }
2458 76 : }
2459 :
2460 : /*
2461 : * emit_raw - writes the raw html information directly to the device.
2462 : */
2463 :
2464 56 : void html_printer::emit_raw (text_glob *g)
2465 : {
2466 56 : assert(g != 0 /* nullptr */);
2467 56 : do_font(g);
2468 56 : if (next_tag == INLINE) {
2469 0 : determine_space(g);
2470 0 : current_paragraph->do_emittext(g->text_string, g->text_length);
2471 : } else {
2472 : // TODO: boolify
2473 56 : int space = current_paragraph->retrieve_para_space() || seen_space;
2474 :
2475 56 : current_paragraph->done_para();
2476 56 : shutdown_table();
2477 56 : switch (next_tag) {
2478 :
2479 55 : case CENTERED:
2480 55 : if (html4 == dialect)
2481 55 : current_paragraph->do_para("align=\"center\"", space);
2482 : else
2483 0 : current_paragraph->do_para("class=\"center\"", space);
2484 55 : break;
2485 1 : case LEFT:
2486 1 : if (html4 == dialect)
2487 1 : current_paragraph->do_para(&html, "align=\"left\"",
2488 : get_troff_indent(), pageoffset,
2489 : linelength, space);
2490 : else
2491 0 : current_paragraph->do_para(&html, "class=\"left\"",
2492 : get_troff_indent(), pageoffset,
2493 : linelength, space);
2494 1 : break;
2495 0 : case RIGHT:
2496 0 : if (html4 == dialect)
2497 0 : current_paragraph->do_para(&html, "align=\"right\"",
2498 : get_troff_indent(), pageoffset,
2499 : linelength, space);
2500 : else
2501 0 : current_paragraph->do_para(&html, "class=\"right\"",
2502 : get_troff_indent(), pageoffset,
2503 : linelength, space);
2504 0 : break;
2505 0 : default:
2506 0 : fatal("unknown enumeration");
2507 : }
2508 56 : current_paragraph->do_emittext(g->text_string, g->text_length);
2509 56 : current_paragraph->done_para();
2510 56 : next_tag = INLINE;
2511 56 : suppress_sub_sup = TRUE;
2512 56 : seen_space = FALSE;
2513 56 : restore_troff_indent();
2514 : }
2515 56 : }
2516 :
2517 : /*
2518 : * handle_tag_within_title - handle a limited number of tags within
2519 : * the context of a table. Those tags which
2520 : * set values rather than generate spaces
2521 : * and paragraphs.
2522 : */
2523 :
2524 4 : void html_printer::handle_tag_within_title (text_glob *g)
2525 : {
2526 4 : assert(g != 0 /* nullptr */);
2527 4 : if (g->is_in()
2528 4 : || g->is_ti()
2529 4 : || g->is_po()
2530 4 : || g->is_ce()
2531 3 : || g->is_ll()
2532 2 : || g->is_fi()
2533 8 : || g->is_nf())
2534 3 : troff_tag(g);
2535 4 : }
2536 :
2537 : /*
2538 : * do_center - handle the .ce commands from troff.
2539 : */
2540 :
2541 125 : void html_printer::do_center (char *arg)
2542 : {
2543 125 : assert(arg != 0 /* nullptr */);
2544 125 : next_center = atoi(arg);
2545 125 : seen_center = TRUE;
2546 125 : }
2547 :
2548 : /*
2549 : * do_centered_image - set a flag such that the next devtag is
2550 : * placed inside a centered paragraph.
2551 : */
2552 :
2553 57 : void html_printer::do_centered_image (void)
2554 : {
2555 57 : next_tag = CENTERED;
2556 57 : }
2557 :
2558 : /*
2559 : * do_right_image - set a flag such that the next devtag is
2560 : * placed inside a right aligned paragraph.
2561 : */
2562 :
2563 0 : void html_printer::do_right_image (void)
2564 : {
2565 0 : next_tag = RIGHT;
2566 0 : }
2567 :
2568 : /*
2569 : * do_left_image - set a flag such that the next devtag is
2570 : * placed inside a left aligned paragraph.
2571 : */
2572 :
2573 1 : void html_printer::do_left_image (void)
2574 : {
2575 1 : next_tag = LEFT;
2576 1 : }
2577 :
2578 : /*
2579 : * exists - returns TRUE if filename exists.
2580 : */
2581 :
2582 : // TODO: boolify
2583 58 : static int exists (const char *filename)
2584 : {
2585 58 : assert(filename != 0 /* nullptr */);
2586 58 : FILE *fp = fopen(filename, "r");
2587 :
2588 58 : if (0 /* nullptr */ == fp) {
2589 2 : return FALSE;
2590 : } else {
2591 56 : fclose(fp);
2592 56 : return TRUE;
2593 : }
2594 : }
2595 :
2596 : /*
2597 : * generate_img_src - returns a html image tag for the filename
2598 : * providing that the image exists.
2599 : */
2600 :
2601 58 : static string &generate_img_src (const char *filename)
2602 : {
2603 58 : assert(filename != 0 /* nullptr */);
2604 58 : string *s = new string("");
2605 :
2606 116 : while ((filename != 0 /* nullptr */) && (' ' == filename[0]))
2607 58 : filename++;
2608 58 : if (exists(filename)) {
2609 112 : *s += string("<img src=\"") + filename + "\" "
2610 56 : + "alt=\"Image " + filename + "\">";
2611 56 : if (xhtml == dialect)
2612 0 : *s += "</img>";
2613 : }
2614 58 : return *s;
2615 : }
2616 :
2617 : /*
2618 : * do_auto_image - tests whether the image, indicated by filename,
2619 : * is present, if so then it emits an html image tag.
2620 : * An image tag may be passed through from pic, eqn
2621 : * but the corresponding image might not be created.
2622 : * Consider .EQ delim $$ .EN or an empty .PS .PE.
2623 : */
2624 :
2625 58 : void html_printer::do_auto_image (text_glob *g, const char *filename)
2626 : {
2627 58 : assert(g != 0 /* nullptr */);
2628 58 : assert(filename != 0 /* nullptr */);
2629 116 : string buffer = generate_img_src(filename);
2630 :
2631 58 : if (! buffer.empty()) {
2632 : /*
2633 : * utilize emit_raw by creating a new text_glob.
2634 : */
2635 56 : text_glob h = *g;
2636 :
2637 56 : h.text_string = buffer.contents();
2638 56 : h.text_length = buffer.length();
2639 56 : emit_raw(&h);
2640 : } else
2641 2 : next_tag = INLINE;
2642 58 : }
2643 :
2644 : /*
2645 : * outstanding_eol - call do_eol, n, times.
2646 : */
2647 :
2648 0 : void html_printer::outstanding_eol (int n)
2649 : {
2650 0 : while (n > 0) {
2651 0 : do_eol();
2652 0 : n--;
2653 : }
2654 0 : }
2655 :
2656 : /*
2657 : * do_title - handle the .tl commands from troff.
2658 : */
2659 :
2660 6 : void html_printer::do_title (void)
2661 : {
2662 : text_glob *t;
2663 : int removed_from_head;
2664 :
2665 6 : if (1 == page_number) {
2666 6 : int found_title_start = FALSE;
2667 6 : if (! page_contents->glyphs.is_empty()) {
2668 6 : page_contents->glyphs.sub_move_right(); // move onto next word
2669 18 : do {
2670 24 : t = page_contents->glyphs.get_data();
2671 24 : removed_from_head = FALSE;
2672 24 : if (t->is_auto_img()) {
2673 0 : string img = generate_img_src((t->text_string + 20));
2674 :
2675 0 : if (! img.empty()) {
2676 0 : if (found_title_start)
2677 0 : title.text += " ";
2678 0 : found_title_start = TRUE;
2679 0 : title.has_been_found = TRUE;
2680 0 : title.text += img;
2681 : }
2682 0 : page_contents->glyphs.sub_move_right(); // move onto next word
2683 0 : removed_from_head = ((!page_contents->glyphs.is_empty())
2684 0 : && (page_contents->glyphs
2685 0 : .is_equal_to_head()));
2686 24 : } else if (t->is_eo_tl()) {
2687 : // end of title found
2688 6 : title.has_been_found = TRUE;
2689 6 : return;
2690 18 : } else if (t->is_a_tag()) {
2691 4 : handle_tag_within_title(t);
2692 4 : page_contents->glyphs.sub_move_right(); // move onto next word
2693 8 : removed_from_head = ((!page_contents->glyphs.is_empty())
2694 8 : && (page_contents->glyphs
2695 4 : .is_equal_to_head()));
2696 14 : } else if (found_title_start) {
2697 8 : title.text += " " + string(t->text_string, t->text_length);
2698 8 : page_contents->glyphs.sub_move_right(); // move onto next word
2699 16 : removed_from_head = ((!page_contents->glyphs.is_empty())
2700 16 : && (page_contents->glyphs
2701 8 : .is_equal_to_head()));
2702 : } else {
2703 6 : title.text += string(t->text_string, t->text_length);
2704 6 : found_title_start = TRUE;
2705 6 : title.has_been_found = TRUE;
2706 6 : page_contents->glyphs.sub_move_right(); // move onto next word
2707 12 : removed_from_head = ((!page_contents->glyphs.is_empty())
2708 12 : && (page_contents->glyphs
2709 6 : .is_equal_to_head()));
2710 : }
2711 18 : } while ((! page_contents->glyphs.is_equal_to_head())
2712 18 : || removed_from_head);
2713 : }
2714 : }
2715 : }
2716 :
2717 : /*
2718 : * write_html_anchor - writes out an anchor. The style of the anchor
2719 : * dependent upon simple_anchor.
2720 : */
2721 :
2722 96 : void html_printer::write_html_anchor (text_glob *h)
2723 : {
2724 96 : if (html4 == dialect) {
2725 96 : if (h != 0 /* nullptr */) {
2726 96 : html.put_string("<a name=\"");
2727 96 : if (simple_anchors) {
2728 34 : string buffer(ANCHOR_TEMPLATE);
2729 :
2730 17 : buffer += as_string(header.no_of_headings);
2731 17 : buffer += '\0';
2732 17 : html.put_string(buffer.contents());
2733 : } else
2734 79 : html.put_string(header.header_buffer);
2735 96 : html.put_string("\"></a>").nl();
2736 : }
2737 : }
2738 96 : }
2739 :
2740 : /*
2741 : * write_xhtml_anchor - writes out an anchor. The style of the anchor
2742 : * dependent upon simple_anchor.
2743 : */
2744 :
2745 96 : void html_printer::write_xhtml_anchor (text_glob *h)
2746 : {
2747 96 : if (xhtml == dialect) {
2748 0 : if (h != 0 /* nullptr */) {
2749 0 : html.put_string(" id=\"");
2750 0 : if (simple_anchors) {
2751 0 : string buffer(ANCHOR_TEMPLATE);
2752 :
2753 0 : buffer += as_string(header.no_of_headings);
2754 0 : buffer += '\0';
2755 0 : html.put_string(buffer.contents());
2756 : } else
2757 0 : html.put_string(header.header_buffer);
2758 0 : html.put_string("\"");
2759 : }
2760 : }
2761 96 : }
2762 :
2763 96 : void html_printer::write_header (void)
2764 : {
2765 96 : if (! header.header_buffer.empty()) {
2766 96 : text_glob *a = 0;
2767 : // TODO: boolify
2768 96 : int space = current_paragraph->retrieve_para_space() || seen_space;
2769 :
2770 96 : if (header.header_level > 7)
2771 0 : header.header_level = 7;
2772 :
2773 : // firstly we must terminate any font and type faces
2774 96 : current_paragraph->done_para();
2775 96 : suppress_sub_sup = TRUE;
2776 :
2777 96 : if (cutoff_heading+2 > header.header_level) {
2778 : // now we save the header so we can issue a list of links
2779 96 : header.no_of_headings++;
2780 192 : style st;
2781 :
2782 96 : a = new text_glob();
2783 192 : a->text_glob_html(&st,
2784 : header.headings
2785 96 : .add_string(header.header_buffer),
2786 : header.header_buffer.length(),
2787 : header.no_of_headings, header.header_level,
2788 : header.no_of_headings, header.header_level);
2789 :
2790 : // and add this header to the header list
2791 96 : header.headers.add(a,
2792 : header.no_of_headings,
2793 : header.no_of_headings, header.no_of_headings,
2794 : header.no_of_headings, header.no_of_headings);
2795 : }
2796 :
2797 96 : html.nl().nl();
2798 :
2799 96 : if (manufacture_headings) {
2800 : // line break before a header
2801 0 : if (!current_paragraph->emitted_text())
2802 0 : current_paragraph->do_space();
2803 : // user wants manufactured headings which look better than
2804 : // <Hn></Hn>
2805 0 : if (header.header_level<4) {
2806 0 : html.put_string("<b><font size=\"+1\">");
2807 0 : html.put_string(header.header_buffer);
2808 0 : html.put_string("</font>").nl();
2809 0 : write_html_anchor(a);
2810 0 : html.put_string("</b>").nl();
2811 : }
2812 : else {
2813 0 : html.put_string("<b>");
2814 0 : html.put_string(header.header_buffer).nl();
2815 0 : write_html_anchor(a);
2816 0 : html.put_string("</b>").nl();
2817 : }
2818 : }
2819 : else {
2820 : // and now we issue the real header
2821 96 : html.put_string("<h");
2822 96 : html.put_number(header.header_level);
2823 96 : write_xhtml_anchor(a);
2824 96 : html.put_string(">");
2825 96 : html.put_string(header.header_buffer).nl();
2826 96 : write_html_anchor(a);
2827 96 : html.put_string("</h");
2828 96 : html.put_number(header.header_level);
2829 96 : html.put_string(">").nl();
2830 : }
2831 :
2832 : /* and now we save the file name in which this header will occur */
2833 :
2834 192 : style st; // fake style to enable us to use the list data structure
2835 :
2836 96 : text_glob *h=new text_glob();
2837 192 : h->text_glob_html(&st,
2838 192 : header.headings.add_string(file_list.file_name()),
2839 192 : file_list.file_name().length(),
2840 : header.no_of_headings, header.header_level,
2841 : header.no_of_headings, header.header_level);
2842 :
2843 96 : header.header_filename.add(h,
2844 : header.no_of_headings,
2845 : header.no_of_headings,
2846 : header.no_of_headings,
2847 : header.no_of_headings,
2848 : header.no_of_headings);
2849 :
2850 96 : current_paragraph->do_para(&html, "", get_troff_indent(),
2851 : pageoffset, linelength, space);
2852 : }
2853 96 : }
2854 :
2855 96 : void html_printer::determine_header_level (int level)
2856 : {
2857 96 : if (0 == level) {
2858 : int i;
2859 :
2860 0 : for (i = 0; ((i<header.header_buffer.length())
2861 0 : && (('.' == header.header_buffer[i])
2862 0 : || is_digit(header.header_buffer[i]))) ; i++) {
2863 0 : if ('.' == header.header_buffer[i])
2864 0 : level++;
2865 : }
2866 : }
2867 96 : header.header_level = level+1;
2868 96 : if ((header.header_level >= 2)
2869 96 : && (header.header_level <= split_level)) {
2870 48 : header.no_of_level_one_headings++;
2871 48 : insert_split_file();
2872 : }
2873 96 : }
2874 :
2875 : /*
2876 : * do_heading - handle the .SH and .NH and equivalent commands from
2877 : * troff.
2878 : */
2879 :
2880 96 : void html_printer::do_heading (char *arg)
2881 : {
2882 96 : assert(arg != 0 /* nullptr */);
2883 : text_glob *g;
2884 96 : int level=atoi(arg);
2885 : int horiz;
2886 :
2887 96 : header.header_buffer.clear();
2888 96 : page_contents->glyphs.move_right();
2889 96 : if (! page_contents->glyphs.is_equal_to_head()) {
2890 96 : g = page_contents->glyphs.get_data();
2891 96 : horiz = g->minh;
2892 321 : do {
2893 417 : if (g->is_auto_img()) {
2894 0 : string img=generate_img_src((g->text_string + 20));
2895 :
2896 0 : if (! img.empty()) {
2897 : // we cannot use full heading anchors with images
2898 0 : simple_anchors = TRUE;
2899 0 : if (horiz < g->minh)
2900 0 : header.header_buffer += " ";
2901 :
2902 0 : header.header_buffer += img;
2903 : }
2904 : }
2905 417 : else if (g->is_in()
2906 417 : || g->is_ti()
2907 417 : || g->is_po()
2908 417 : || g->is_ce()
2909 834 : || g->is_ll())
2910 0 : troff_tag(g);
2911 417 : else if (g->is_fi())
2912 0 : fill_on = 1;
2913 417 : else if (g->is_nf())
2914 0 : fill_on = 0;
2915 417 : else if (! (g->is_a_line() || g->is_a_tag())) {
2916 : /*
2917 : * we ignore the other tag commands when constructing a heading
2918 : */
2919 324 : if (horiz < g->minh)
2920 228 : header.header_buffer += " ";
2921 :
2922 324 : horiz = g->maxh;
2923 324 : header.header_buffer += string(g->text_string, g->text_length);
2924 : }
2925 417 : page_contents->glyphs.move_right();
2926 417 : g = page_contents->glyphs.get_data();
2927 417 : } while ((! page_contents->glyphs.is_equal_to_head())
2928 417 : && (! g->is_eo_h()));
2929 : }
2930 :
2931 96 : determine_header_level(level);
2932 96 : write_header();
2933 :
2934 : /*
2935 : * finally set the output font to uninitialized, thus forcing
2936 : * the new paragraph to start a new font block.
2937 : */
2938 :
2939 96 : output_style.f = 0;
2940 96 : g = page_contents->glyphs.get_data();
2941 96 : page_contents->glyphs.move_left(); // so that next time we use old g
2942 96 : }
2943 :
2944 : /*
2945 : * is_courier_until_eol - returns TRUE if we can see a whole line which
2946 : * is courier
2947 : */
2948 :
2949 1154 : int html_printer::is_courier_until_eol (void)
2950 : {
2951 1154 : text_glob *orig = page_contents->glyphs.get_data();
2952 1154 : int result = TRUE;
2953 : text_glob *g;
2954 :
2955 1154 : if (! page_contents->glyphs.is_equal_to_tail()) {
2956 1154 : page_contents->glyphs.move_right();
2957 45 : do {
2958 1199 : g = page_contents->glyphs.get_data();
2959 1199 : if (! g->is_a_tag() && (! is_font_courier(g->text_style.f)))
2960 778 : result = FALSE;
2961 1199 : page_contents->glyphs.move_right();
2962 : } while (result
2963 421 : && (! page_contents->glyphs.is_equal_to_head())
2964 421 : && (! g->is_fi())
2965 1620 : && (! g->is_eol()));
2966 :
2967 : /*
2968 : * now restore our previous position.
2969 : */
2970 3507 : while (page_contents->glyphs.get_data() != orig)
2971 2353 : page_contents->glyphs.move_left();
2972 : }
2973 1154 : return result;
2974 : }
2975 :
2976 : /*
2977 : * do_linelength - handle the .ll command from troff.
2978 : */
2979 :
2980 33 : void html_printer::do_linelength (char *arg)
2981 : {
2982 33 : assert(arg != 0 /* nullptr */);
2983 33 : if (-1 == max_linelength)
2984 11 : max_linelength = atoi(arg);
2985 :
2986 33 : next_linelength = atoi(arg);
2987 33 : seen_linelength = TRUE;
2988 33 : }
2989 :
2990 : /*
2991 : * do_pageoffset - handle the .po command from troff.
2992 : */
2993 :
2994 12 : void html_printer::do_pageoffset (char *arg)
2995 : {
2996 12 : assert(arg != 0 /* nullptr */);
2997 12 : next_pageoffset = atoi(arg);
2998 12 : seen_pageoffset = TRUE;
2999 12 : }
3000 :
3001 : /*
3002 : * get_troff_indent - returns the indent value.
3003 : */
3004 :
3005 3006 : int html_printer::get_troff_indent (void)
3006 : {
3007 3006 : if (end_tempindent > 0)
3008 162 : return temp_indent;
3009 : else
3010 2844 : return troff_indent;
3011 : }
3012 :
3013 : /*
3014 : * do_indentation - handle the .in command from troff.
3015 : */
3016 :
3017 307 : void html_printer::do_indentation (char *arg)
3018 : {
3019 307 : assert(arg != 0 /* nullptr */);
3020 307 : next_indent = atoi(arg);
3021 307 : seen_indent = TRUE;
3022 307 : }
3023 :
3024 : /*
3025 : * do_tempindent - handle the .ti command from troff.
3026 : */
3027 :
3028 36 : void html_printer::do_tempindent (char *arg)
3029 : {
3030 36 : assert(arg != 0 /* nullptr */);
3031 36 : if (fill_on) {
3032 : /*
3033 : * we set the end_tempindent to 2 as the first .br
3034 : * activates the .ti and the second terminates it.
3035 : */
3036 36 : end_tempindent = 2;
3037 36 : temp_indent = atoi(arg);
3038 : }
3039 36 : }
3040 :
3041 : /*
3042 : * shutdown_table - shuts down the current table.
3043 : */
3044 :
3045 56 : void html_printer::shutdown_table (void)
3046 : {
3047 56 : if (table != 0 /* nullptr */) {
3048 0 : current_paragraph->done_para();
3049 0 : table->emit_finish_table();
3050 : // don't delete this table as it will be deleted when we destroy the
3051 : // text_glob
3052 0 : table = 0 /* nullptr */;
3053 : }
3054 56 : }
3055 :
3056 : /*
3057 : * do_indent - remember the indent parameters and if
3058 : * indent is > pageoff and indent has changed
3059 : * then we start a html table to implement the indentation.
3060 : */
3061 :
3062 437 : void html_printer::do_indent (int in, int pageoff, int linelen)
3063 : {
3064 437 : if ((device_indent != -1)
3065 437 : && ((pageoffset+device_indent) != (in+pageoff))) {
3066 :
3067 : // TODO: boolify
3068 376 : int space = current_paragraph->retrieve_para_space() || seen_space;
3069 376 : current_paragraph->done_para();
3070 :
3071 376 : device_indent = in;
3072 376 : pageoffset = pageoff;
3073 376 : if (linelen <= max_linelength)
3074 78 : linelength = linelen;
3075 :
3076 376 : current_paragraph->do_para(&html, "", device_indent,
3077 : pageoffset, max_linelength, space);
3078 : }
3079 437 : }
3080 :
3081 : /*
3082 : * do_verticalspacing - handle the .vs command from troff.
3083 : */
3084 :
3085 0 : void html_printer::do_verticalspacing (char *arg)
3086 : {
3087 0 : assert(arg != 0 /* nullptr */);
3088 0 : vertical_spacing = atoi(arg);
3089 0 : }
3090 :
3091 : /*
3092 : * do_pointsize - handle the .ps command from troff.
3093 : */
3094 :
3095 0 : void html_printer::do_pointsize (char *arg)
3096 : {
3097 0 : assert(arg != 0 /* nullptr */);
3098 : /*
3099 : * firstly check to see whether this point size is really associated
3100 : * with a .tl tag
3101 : */
3102 :
3103 0 : if (! page_contents->glyphs.is_empty()) {
3104 0 : text_glob *g = page_contents->glyphs.get_data();
3105 0 : text_glob *t = page_contents->glyphs.get_data();
3106 :
3107 0 : while (t->is_a_tag() && (!page_contents->glyphs.is_equal_to_head()))
3108 : {
3109 0 : if (t->is_tl()) {
3110 : /*
3111 : * found title therefore ignore this .ps tag
3112 : */
3113 0 : while (t != g) {
3114 0 : page_contents->glyphs.move_left();
3115 0 : t = page_contents->glyphs.get_data();
3116 : }
3117 0 : return;
3118 : }
3119 0 : page_contents->glyphs.move_right();
3120 0 : t = page_contents->glyphs.get_data();
3121 : }
3122 : /*
3123 : * move back to original position
3124 : */
3125 0 : while (t != g) {
3126 0 : page_contents->glyphs.move_left();
3127 0 : t = page_contents->glyphs.get_data();
3128 : }
3129 : /*
3130 : * collect valid pointsize
3131 : */
3132 0 : pointsize = atoi(arg);
3133 : }
3134 : }
3135 :
3136 : /*
3137 : * do_fill - records whether troff has requested that text be filled.
3138 : */
3139 :
3140 217 : void html_printer::do_fill (char *arg)
3141 : {
3142 217 : assert(arg != 0 /* nullptr */);
3143 217 : int on = atoi(arg);
3144 :
3145 217 : output_hpos = get_troff_indent()+pageoffset;
3146 217 : suppress_sub_sup = TRUE;
3147 :
3148 217 : if (fill_on != on) {
3149 206 : if (on)
3150 103 : current_paragraph->do_para("", seen_space);
3151 206 : fill_on = on;
3152 : }
3153 217 : }
3154 :
3155 : /*
3156 : * do_eol - handle the end of line
3157 : */
3158 :
3159 963 : void html_printer::do_eol (void)
3160 : {
3161 963 : if (! fill_on) {
3162 710 : if (current_paragraph->ever_emitted_text()) {
3163 633 : current_paragraph->do_newline();
3164 633 : current_paragraph->do_break();
3165 : }
3166 : }
3167 963 : output_hpos = get_troff_indent()+pageoffset;
3168 963 : }
3169 :
3170 : /*
3171 : * do_check_center - checks to see whether we have seen a '.ce' tag
3172 : * during the previous line.
3173 : */
3174 :
3175 1268 : void html_printer::do_check_center(void)
3176 : {
3177 1268 : if (seen_center) {
3178 112 : seen_center = FALSE;
3179 112 : if (next_center > 0) {
3180 55 : if (0 == end_center) {
3181 : // TODO: boolify
3182 52 : int space = current_paragraph->retrieve_para_space()
3183 52 : || seen_space;
3184 52 : current_paragraph->done_para();
3185 52 : suppress_sub_sup = TRUE;
3186 52 : if (html4 == dialect)
3187 52 : current_paragraph->do_para("align=\"center\"", space);
3188 : else
3189 0 : current_paragraph->do_para("class=\"center\"", space);
3190 : } else
3191 6 : if ((strcmp("align=\"center\"",
3192 3 : current_paragraph->get_alignment()) != 0)
3193 4 : && (strcmp("class=\"center\"",
3194 1 : current_paragraph->get_alignment()) != 0)) {
3195 : /*
3196 : * different alignment, so shutdown paragraph and open
3197 : * a new one.
3198 : */
3199 : // TODO: boolify
3200 1 : int space = current_paragraph->retrieve_para_space()
3201 1 : || seen_space;
3202 1 : current_paragraph->done_para();
3203 1 : suppress_sub_sup = TRUE;
3204 1 : if (html4 == dialect)
3205 1 : current_paragraph->do_para("align=\"center\"", space);
3206 : else
3207 0 : current_paragraph->do_para("class=\"center\"", space);
3208 : } else
3209 : // same alignment; if we have emitted text, issue a break.
3210 2 : if (current_paragraph->emitted_text())
3211 0 : current_paragraph->do_break();
3212 : } else
3213 : /*
3214 : * next_center == 0
3215 : */
3216 57 : if (end_center > 0) {
3217 : // TODO: boolify
3218 104 : seen_space = seen_space
3219 52 : || current_paragraph->retrieve_para_space();
3220 52 : current_paragraph->done_para();
3221 52 : suppress_sub_sup = TRUE;
3222 52 : current_paragraph->do_para("", seen_space);
3223 : }
3224 112 : end_center = next_center;
3225 : }
3226 1268 : }
3227 :
3228 : /*
3229 : * do_eol_ce - handle end of line specifically for a .ce
3230 : */
3231 :
3232 0 : void html_printer::do_eol_ce (void)
3233 : {
3234 0 : if (end_center > 0) {
3235 0 : if (end_center > 1)
3236 0 : if (current_paragraph->emitted_text())
3237 0 : current_paragraph->do_break();
3238 :
3239 0 : end_center--;
3240 0 : if (0 == end_center) {
3241 0 : current_paragraph->done_para();
3242 0 : suppress_sub_sup = TRUE;
3243 : }
3244 : }
3245 0 : }
3246 :
3247 : /*
3248 : * do_flush - flushes all output and tags.
3249 : */
3250 :
3251 0 : void html_printer::do_flush (void)
3252 : {
3253 0 : current_paragraph->done_para();
3254 0 : }
3255 :
3256 : /*
3257 : * do_links - moves onto a new temporary file and sets auto_links to
3258 : * false.
3259 : */
3260 :
3261 18 : void html_printer::do_links (void)
3262 : {
3263 18 : html.end_line(); // flush line
3264 18 : auto_links = FALSE; // from now on only emit under user request
3265 18 : file_list.add_new_file(xtmpfile());
3266 18 : file_list.set_links_required();
3267 18 : html.set_file(file_list.get_file());
3268 18 : }
3269 :
3270 : /*
3271 : * insert_split_file -
3272 : */
3273 :
3274 48 : void html_printer::insert_split_file (void)
3275 : {
3276 48 : if (multiple_files) {
3277 40 : current_paragraph->done_para(); // flush paragraph
3278 40 : html.end_line(); // flush line
3279 40 : html.set_file(file_list.get_file()); // flush current file
3280 40 : file_list.add_new_file(xtmpfile());
3281 80 : string split_file = job_name;
3282 :
3283 40 : split_file += string("-");
3284 40 : split_file += as_string(header.no_of_level_one_headings);
3285 40 : if (xhtml == dialect)
3286 0 : split_file += string(".xhtml");
3287 : else
3288 40 : split_file += string(".html");
3289 40 : split_file += '\0';
3290 :
3291 40 : file_list.set_file_name(split_file);
3292 40 : html.set_file(file_list.get_file());
3293 : }
3294 48 : }
3295 :
3296 : /*
3297 : * do_job_name - assigns the job_name to name.
3298 : */
3299 :
3300 0 : void html_printer::do_job_name (char *name)
3301 : {
3302 0 : if (! multiple_files) {
3303 0 : multiple_files = TRUE;
3304 0 : while (name != 0 /* nullptr */ && (*name != '\0') && (' ' == *name))
3305 0 : name++;
3306 0 : job_name = name;
3307 : }
3308 0 : }
3309 :
3310 : /*
3311 : * do_head - adds a string to head_info which is to be included into
3312 : * the <head> </head> section of the html document.
3313 : */
3314 :
3315 1 : void html_printer::do_head (char *name)
3316 : {
3317 1 : assert(name != 0 /* nullptr */);
3318 1 : head_info += string(name);
3319 1 : head_info += '\n';
3320 1 : }
3321 :
3322 : /*
3323 : * do_break - handles the ".br" request and also undoes an outstanding
3324 : * ".ti" command and calls indent if the indentation related
3325 : * registers have changed.
3326 : */
3327 :
3328 1268 : void html_printer::do_break (void)
3329 : {
3330 1268 : int seen_temp_indent = FALSE;
3331 :
3332 1268 : current_paragraph->do_break();
3333 1268 : if (end_tempindent > 0) {
3334 60 : end_tempindent--;
3335 60 : if (end_tempindent > 0)
3336 36 : seen_temp_indent = TRUE;
3337 : }
3338 1268 : if (seen_indent
3339 939 : || seen_pageoffset
3340 938 : || seen_linelength
3341 934 : || seen_temp_indent) {
3342 349 : if (seen_indent && (! seen_temp_indent))
3343 308 : troff_indent = next_indent;
3344 349 : if (! seen_pageoffset)
3345 342 : next_pageoffset = pageoffset;
3346 349 : if (! seen_linelength)
3347 333 : next_linelength = linelength;
3348 349 : do_indent(get_troff_indent(), next_pageoffset, next_linelength);
3349 : }
3350 1268 : seen_indent = seen_temp_indent;
3351 1268 : seen_linelength = FALSE;
3352 1268 : seen_pageoffset = FALSE;
3353 1268 : do_check_center();
3354 1268 : output_hpos = get_troff_indent()+pageoffset;
3355 1268 : suppress_sub_sup = TRUE;
3356 1268 : }
3357 :
3358 957 : void html_printer::do_space (char *arg)
3359 : {
3360 957 : assert(arg != 0 /* nullptr */);
3361 957 : int n = atoi(arg);
3362 :
3363 957 : seen_space = atoi(arg);
3364 957 : as.check_sp(seen_space);
3365 : #if 0
3366 : if ((n>0) && table)
3367 : table->set_space(TRUE);
3368 : #endif
3369 :
3370 1914 : while (n>0) {
3371 957 : current_paragraph->do_space();
3372 957 : n--;
3373 : }
3374 957 : suppress_sub_sup = TRUE;
3375 957 : }
3376 :
3377 : /*
3378 : * do_tab_ts - start a table, which will have already been defined.
3379 : */
3380 :
3381 20 : void html_printer::do_tab_ts (text_glob *g)
3382 : {
3383 20 : assert(g != 0 /* nullptr */);
3384 20 : html_table *t = g->get_table();
3385 :
3386 20 : if (t != 0 /* nullptr */) {
3387 20 : current_column = 0;
3388 20 : current_paragraph->done_pre();
3389 20 : current_paragraph->done_para();
3390 20 : current_paragraph->remove_para_space();
3391 :
3392 : #if defined(DEBUG_TABLES)
3393 : html.simple_comment("TABS");
3394 : #endif
3395 :
3396 20 : t->set_linelength(max_linelength);
3397 20 : t->add_indent(pageoffset);
3398 : #if 0
3399 : t->emit_table_header(seen_space);
3400 : #else
3401 20 : t->emit_table_header(FALSE);
3402 : // TODO: boolify
3403 20 : row_space = current_paragraph->retrieve_para_space() || seen_space;
3404 20 : seen_space = FALSE;
3405 : #endif
3406 : }
3407 :
3408 20 : table = t;
3409 20 : }
3410 :
3411 : /*
3412 : * do_tab_te - finish a table.
3413 : */
3414 :
3415 20 : void html_printer::do_tab_te (void)
3416 : {
3417 20 : if (table != 0 /* nullptr */) {
3418 20 : current_paragraph->done_para();
3419 20 : current_paragraph->remove_para_space();
3420 20 : table->emit_finish_table();
3421 : }
3422 :
3423 20 : table = 0 /* nullptr */;
3424 20 : restore_troff_indent();
3425 20 : }
3426 :
3427 : /*
3428 : * do_tab - handle the "devtag:tab" tag
3429 : */
3430 :
3431 9 : void html_printer::do_tab (char *s)
3432 : {
3433 9 : assert(s != 0 /* nullptr */);
3434 9 : if (table != 0 /* nullptr */) {
3435 18 : while (csspace(*s))
3436 9 : s++;
3437 9 : s++;
3438 9 : int col = table->find_column(atoi(s) + pageoffset
3439 9 : + get_troff_indent());
3440 9 : if (col > 0) {
3441 9 : current_paragraph->done_para();
3442 9 : table->emit_col(col);
3443 : }
3444 : }
3445 9 : }
3446 :
3447 : /*
3448 : * do_tab0 - handle the "devtag:tab0" tag
3449 : */
3450 :
3451 2 : void html_printer::do_tab0 (void)
3452 : {
3453 2 : if (table != 0 /* nullptr */) {
3454 2 : int col = table->find_column(pageoffset+get_troff_indent());
3455 2 : if (col > 0) {
3456 2 : current_paragraph->done_para();
3457 2 : table->emit_col(col);
3458 : }
3459 : }
3460 2 : }
3461 :
3462 : /*
3463 : * do_col - start column, s.
3464 : */
3465 :
3466 56 : void html_printer::do_col (char *s)
3467 : {
3468 56 : assert(s != 0 /* nullptr */);
3469 56 : if (table != 0 /* nullptr */) {
3470 45 : if (atoi(s) < current_column)
3471 11 : row_space = seen_space;
3472 :
3473 45 : current_column = atoi(s);
3474 45 : current_paragraph->done_para();
3475 45 : table->emit_col(current_column);
3476 45 : current_paragraph->do_para("", row_space);
3477 : }
3478 56 : }
3479 :
3480 : /*
3481 : * troff_tag - processes the troff tag and manipulates the troff
3482 : * state machine.
3483 : */
3484 :
3485 4686 : void html_printer::troff_tag (text_glob *g)
3486 : {
3487 4686 : assert(g != 0 /* nullptr */);
3488 : /*
3489 : * firstly skip over devtag:
3490 : */
3491 4686 : char *t=const_cast<char *>(g->text_string)+strlen("devtag:");
3492 4686 : if (strncmp(g->text_string, "html</p>:", strlen("html</p>:")) == 0) {
3493 283 : do_end_para(g);
3494 4403 : } else if (strncmp(g->text_string, "html<?p>:", strlen("html<?p>:"))
3495 : == 0) {
3496 0 : if (current_paragraph->emitted_text())
3497 0 : html.put_string(g->text_string+9);
3498 : else
3499 0 : do_end_para(g);
3500 4403 : } else if (strncmp(g->text_string, "math<?p>:", strlen("math<?p>:"))
3501 : == 0) {
3502 0 : do_math(g);
3503 4403 : } else if (g->is_eol()) {
3504 963 : do_eol();
3505 3440 : } else if (g->is_eol_ce()) {
3506 0 : do_eol_ce();
3507 3440 : } else if (strncmp(t, ".sp", 3) == 0) {
3508 957 : char *a = t+3;
3509 957 : do_space(a);
3510 2483 : } else if (strncmp(t, ".br", 3) == 0) {
3511 1268 : seen_break = 1;
3512 1268 : as.check_br(1);
3513 1268 : do_break();
3514 1215 : } else if (strcmp(t, ".centered-image") == 0) {
3515 57 : do_centered_image();
3516 1158 : } else if (strcmp(t, ".right-image") == 0) {
3517 0 : do_right_image();
3518 1158 : } else if (strcmp(t, ".left-image") == 0) {
3519 1 : do_left_image();
3520 1157 : } else if (strncmp(t, ".auto-image", 11) == 0) {
3521 58 : char *a = t+11;
3522 58 : do_auto_image(g, a);
3523 1099 : } else if (strncmp(t, ".ce", 3) == 0) {
3524 125 : char *a = t+3;
3525 125 : suppress_sub_sup = TRUE;
3526 125 : do_center(a);
3527 974 : } else if (g->is_tl()) {
3528 5 : suppress_sub_sup = TRUE;
3529 5 : title.with_h1 = TRUE;
3530 5 : do_title();
3531 969 : } else if (strncmp(t, ".html-tl", 8) == 0) {
3532 1 : suppress_sub_sup = TRUE;
3533 1 : title.with_h1 = FALSE;
3534 1 : do_title();
3535 968 : } else if (strncmp(t, ".fi", 3) == 0) {
3536 217 : char *a = t+3;
3537 217 : do_fill(a);
3538 751 : } else if ((strncmp(t, ".SH", 3) == 0)
3539 751 : || (strncmp(t, ".NH", 3) == 0)) {
3540 96 : char *a = t+3;
3541 96 : do_heading(a);
3542 655 : } else if (strncmp(t, ".ll", 3) == 0) {
3543 33 : char *a = t+3;
3544 33 : do_linelength(a);
3545 622 : } else if (strncmp(t, ".po", 3) == 0) {
3546 12 : char *a = t+3;
3547 12 : do_pageoffset(a);
3548 610 : } else if (strncmp(t, ".in", 3) == 0) {
3549 307 : char *a = t+3;
3550 307 : do_indentation(a);
3551 303 : } else if (strncmp(t, ".ti", 3) == 0) {
3552 36 : char *a = t+3;
3553 36 : do_tempindent(a);
3554 267 : } else if (strncmp(t, ".vs", 3) == 0) {
3555 0 : char *a = t+3;
3556 0 : do_verticalspacing(a);
3557 267 : } else if (strncmp(t, ".ps", 3) == 0) {
3558 0 : char *a = t+3;
3559 0 : do_pointsize(a);
3560 267 : } else if (strcmp(t, ".links") == 0) {
3561 18 : do_links();
3562 249 : } else if (strncmp(t, ".job-name", 9) == 0) {
3563 0 : char *a = t+9;
3564 0 : do_job_name(a);
3565 249 : } else if (strncmp(t, ".head", 5) == 0) {
3566 1 : char *a = t+5;
3567 1 : do_head(a);
3568 248 : } else if (strcmp(t, ".no-auto-rule") == 0) {
3569 1 : auto_rule = FALSE;
3570 247 : } else if (strcmp(t, ".tab-ts") == 0) {
3571 20 : do_tab_ts(g);
3572 227 : } else if (strcmp(t, ".tab-te") == 0) {
3573 20 : do_tab_te();
3574 207 : } else if (strncmp(t, ".col ", 5) == 0) {
3575 56 : char *a = t+4;
3576 56 : do_col(a);
3577 151 : } else if (strncmp(t, "tab ", 4) == 0) {
3578 9 : char *a = t+3;
3579 9 : do_tab(a);
3580 142 : } else if (strncmp(t, "tab0", 4) == 0) {
3581 2 : do_tab0();
3582 : }
3583 4686 : }
3584 :
3585 : /*
3586 : * do_math - prints out the equation
3587 : */
3588 :
3589 0 : void html_printer::do_math (text_glob *g)
3590 : {
3591 0 : assert(g != 0 /* nullptr */);
3592 0 : do_font(g);
3593 0 : if (current_paragraph->emitted_text())
3594 0 : html.put_string(g->text_string+9);
3595 : else
3596 0 : do_end_para(g);
3597 0 : }
3598 :
3599 : /*
3600 : * is_in_middle - returns TRUE if the positions left..right are in the
3601 : * center of the page.
3602 : */
3603 :
3604 : // TODO: boolify
3605 0 : int html_printer::is_in_middle (int left, int right)
3606 : {
3607 0 : return( abs(abs(left-pageoffset) - abs(pageoffset+linelength-right))
3608 0 : <= CENTER_TOLERANCE );
3609 : }
3610 :
3611 : /*
3612 : * flush_globs - runs through the text glob list and emits html.
3613 : */
3614 :
3615 20 : void html_printer::flush_globs (void)
3616 : {
3617 : text_glob *g;
3618 :
3619 20 : if (! page_contents->glyphs.is_empty()) {
3620 14 : page_contents->glyphs.start_from_head();
3621 25175 : do {
3622 25189 : g = page_contents->glyphs.get_data();
3623 : #if 0
3624 : fprintf(stderr, "[%s:%d:%d:%d:%d]",
3625 : g->text_string, g->minv, g->minh, g->maxv, g->maxh) ;
3626 : fflush(stderr);
3627 : #endif
3628 :
3629 25189 : handle_state_assertion(g);
3630 :
3631 25189 : if (strcmp(g->text_string, "XXXXXXX") == 0)
3632 0 : stop();
3633 :
3634 25189 : if (g->is_a_tag())
3635 4683 : troff_tag(g);
3636 20506 : else if (g->is_a_line())
3637 0 : emit_line(g);
3638 : else {
3639 20506 : as.check_sp(seen_space);
3640 20506 : as.check_br(seen_break);
3641 20506 : seen_break = 0;
3642 20506 : seen_space = 0;
3643 20506 : emit_html(g);
3644 : }
3645 :
3646 25189 : as.check_fi(fill_on);
3647 25189 : as.check_ce(end_center);
3648 : /*
3649 : * after processing the title (and removing it) the glyph list
3650 : * might be empty
3651 : */
3652 25189 : if (! page_contents->glyphs.is_empty()) {
3653 25189 : page_contents->glyphs.move_right();
3654 : }
3655 25189 : } while (! page_contents->glyphs.is_equal_to_head());
3656 : }
3657 20 : }
3658 :
3659 : /*
3660 : * calc_nf - calculates the _no_ format flag, given the
3661 : * text glob, g.
3662 : */
3663 :
3664 : // TODO: boolify
3665 76780 : int html_printer::calc_nf (text_glob *g, int nf)
3666 : {
3667 76780 : if (g != 0 /* nullptr */) {
3668 76780 : if (g->is_fi()) {
3669 342 : as.check_fi(TRUE);
3670 342 : return FALSE;
3671 : }
3672 76438 : if (g->is_nf()) {
3673 309 : as.check_fi(FALSE);
3674 309 : return TRUE;
3675 : }
3676 : }
3677 76129 : as.check_fi(! nf);
3678 76129 : return nf;
3679 : }
3680 :
3681 : /*
3682 : * calc_po_in - calculates the, in, po, registers
3683 : */
3684 :
3685 22585 : void html_printer::calc_po_in (text_glob *g, int nf)
3686 : {
3687 22585 : assert(g != 0 /* nullptr */);
3688 22585 : if (g->is_in())
3689 307 : troff_indent = g->get_arg();
3690 22278 : else if (g->is_po())
3691 12 : pageoffset = g->get_arg();
3692 22266 : else if (g->is_ti()) {
3693 36 : temp_indent = g->get_arg();
3694 36 : end_tempindent = 2;
3695 22230 : } else if (g->is_br() || (nf && g->is_eol())) {
3696 0 : if (end_tempindent > 0)
3697 0 : end_tempindent--;
3698 : }
3699 22585 : }
3700 :
3701 : /*
3702 : * next_horiz_pos - returns the next horiz position.
3703 : * -1 is returned if it doesn't exist.
3704 : */
3705 :
3706 0 : int html_printer::next_horiz_pos (text_glob *g, int nf)
3707 : {
3708 0 : int next = -1;
3709 :
3710 0 : if ((g != 0 /* nullptr */) && (g->is_br() || (nf && g->is_eol())))
3711 0 : if (! page_contents->glyphs.is_empty()) {
3712 0 : page_contents->glyphs.move_right_get_data();
3713 0 : if (0 /* nullptr */ == g) {
3714 0 : page_contents->glyphs.start_from_head();
3715 0 : as.reset();
3716 : }
3717 : else {
3718 0 : next = g->minh;
3719 0 : page_contents->glyphs.move_left();
3720 : }
3721 : }
3722 0 : return next;
3723 : }
3724 :
3725 : /*
3726 : * insert_tab_ts - inserts a tab-ts before, where.
3727 : */
3728 :
3729 20 : text_glob *html_printer::insert_tab_ts (text_glob *where)
3730 : {
3731 : text_glob *start_of_table;
3732 20 : text_glob *old_pos = page_contents->glyphs.get_data();
3733 20 : page_contents->glyphs.move_to(where);
3734 20 : page_contents->glyphs.move_left();
3735 : // tab table start
3736 20 : page_contents->insert_tag(string("devtag:.tab-ts"));
3737 20 : page_contents->glyphs.move_right();
3738 20 : start_of_table = page_contents->glyphs.get_data();
3739 20 : page_contents->glyphs.move_to(old_pos);
3740 20 : return start_of_table;
3741 : }
3742 :
3743 : /*
3744 : * insert_tab_te - inserts a tab-te before the current position
3745 : * (it skips backward over .sp/.br)
3746 : */
3747 :
3748 20 : void html_printer::insert_tab_te (void)
3749 : {
3750 20 : text_glob *g = page_contents->glyphs.get_data();
3751 20 : page_contents->dump_page();
3752 96 : while (page_contents->glyphs.get_data()->is_a_tag())
3753 76 : page_contents->glyphs.move_left();
3754 : // tab table end
3755 20 : page_contents->insert_tag(string("devtag:.tab-te"));
3756 116 : while (g != page_contents->glyphs.get_data())
3757 96 : page_contents->glyphs.move_right();
3758 20 : page_contents->dump_page();
3759 20 : }
3760 :
3761 : /*
3762 : * insert_tab_0 - inserts a tab0 before, where.
3763 : */
3764 :
3765 2 : void html_printer::insert_tab_0 (text_glob *where)
3766 : {
3767 2 : text_glob *old_pos = page_contents->glyphs.get_data();
3768 :
3769 2 : page_contents->glyphs.move_to(where);
3770 2 : page_contents->glyphs.move_left();
3771 : // tab0 start of line
3772 2 : page_contents->insert_tag(string("devtag:tab0"));
3773 2 : page_contents->glyphs.move_right();
3774 2 : page_contents->glyphs.move_to(old_pos);
3775 2 : }
3776 :
3777 : /*
3778 : * remove_tabs - removes the tabs tags on this line.
3779 : */
3780 :
3781 376 : void html_printer::remove_tabs (void)
3782 : {
3783 376 : text_glob *orig = page_contents->glyphs.get_data();
3784 : text_glob *g;
3785 :
3786 376 : if (! page_contents->glyphs.is_equal_to_tail()) {
3787 8588 : do {
3788 8964 : g = page_contents->glyphs.get_data();
3789 8964 : if (g->is_tab()) {
3790 0 : page_contents->glyphs.sub_move_right();
3791 0 : if (g == orig)
3792 0 : orig = page_contents->glyphs.get_data();
3793 : }
3794 : else
3795 8964 : page_contents->glyphs.move_right();
3796 8964 : } while ((! page_contents->glyphs.is_equal_to_head())
3797 8964 : && (! g->is_eol()));
3798 :
3799 : /*
3800 : * now restore our previous position.
3801 : */
3802 9340 : while (page_contents->glyphs.get_data() != orig)
3803 8964 : page_contents->glyphs.move_left();
3804 : }
3805 376 : }
3806 :
3807 20 : void html_printer::remove_courier_tabs (void)
3808 : {
3809 : text_glob *g;
3810 20 : int line_start = TRUE;
3811 20 : int nf = FALSE;
3812 :
3813 20 : if (! page_contents->glyphs.is_empty()) {
3814 14 : page_contents->glyphs.start_from_head();
3815 14 : as.reset();
3816 14 : line_start = TRUE;
3817 25573 : do {
3818 25587 : g = page_contents->glyphs.get_data();
3819 25587 : handle_state_assertion(g);
3820 25587 : nf = calc_nf(g, nf);
3821 :
3822 25587 : if (line_start) {
3823 3037 : if (line_start && nf && is_courier_until_eol()) {
3824 376 : remove_tabs();
3825 376 : g = page_contents->glyphs.get_data();
3826 : }
3827 : }
3828 :
3829 : // line_start = g->is_br() || g->is_nf() || g->is_fi()
3830 : // || (nf && g->is_eol());
3831 25587 : line_start = g->is_br() || (nf && g->is_eol());
3832 25587 : page_contents->glyphs.move_right();
3833 25587 : } while (! page_contents->glyphs.is_equal_to_head());
3834 : }
3835 20 : }
3836 :
3837 20 : void html_printer::insert_tab0_foreach_tab (void)
3838 : {
3839 20 : text_glob *start_of_line = 0 /* nullptr */;
3840 20 : text_glob *g = 0 /* nullptr */;
3841 20 : int seen_tab = FALSE;
3842 20 : int seen_col = FALSE;
3843 20 : int nf = FALSE;
3844 :
3845 20 : if (! page_contents->glyphs.is_empty()) {
3846 14 : page_contents->glyphs.start_from_head();
3847 14 : as.reset();
3848 14 : start_of_line = page_contents->glyphs.get_data();
3849 22550 : do {
3850 22564 : g = page_contents->glyphs.get_data();
3851 22564 : handle_state_assertion(g);
3852 22564 : nf = calc_nf(g, nf);
3853 :
3854 22564 : if (g->is_tab())
3855 2 : seen_tab = TRUE;
3856 :
3857 22564 : if (g->is_col())
3858 16 : seen_col = TRUE;
3859 :
3860 22564 : if (g->is_br() || (nf && g->is_eol())) {
3861 915 : do {
3862 3023 : page_contents->glyphs.move_right();
3863 3023 : g = page_contents->glyphs.get_data();
3864 3023 : handle_state_assertion(g);
3865 3023 : nf = calc_nf(g, nf);
3866 3023 : if (page_contents->glyphs.is_equal_to_head()) {
3867 0 : if (seen_tab && !seen_col)
3868 0 : insert_tab_0(start_of_line);
3869 0 : return;
3870 : }
3871 3023 : } while (g->is_br() || (nf && g->is_eol()) || g->is_ta());
3872 : // printf("\nstart_of_line is: %s\n", g->text_string);
3873 2108 : if (seen_tab && !seen_col) {
3874 2 : insert_tab_0(start_of_line);
3875 2 : page_contents->glyphs.move_to(g);
3876 : }
3877 :
3878 2108 : seen_tab = FALSE;
3879 2108 : seen_col = FALSE;
3880 2108 : start_of_line = g;
3881 : }
3882 22564 : page_contents->glyphs.move_right();
3883 22564 : } while (! page_contents->glyphs.is_equal_to_head());
3884 14 : if (seen_tab && !seen_col)
3885 0 : insert_tab_0(start_of_line);
3886 :
3887 : }
3888 : }
3889 :
3890 : /*
3891 : * update_min_max - updates the extent of a column, given the left and
3892 : * right extents of a glyph, g.
3893 : */
3894 :
3895 20863 : void html_printer::update_min_max (colType type_of_col,
3896 : int *minimum, int *maximum,
3897 : text_glob *g)
3898 : {
3899 20863 : switch (type_of_col) {
3900 :
3901 40 : case tab_tag:
3902 40 : break;
3903 2 : case tab0_tag:
3904 2 : *minimum = g->minh;
3905 2 : break;
3906 327 : case col_tag:
3907 327 : *minimum = g->minh;
3908 327 : *maximum = g->maxh;
3909 327 : break;
3910 20494 : default:
3911 20494 : break;
3912 : }
3913 20863 : }
3914 :
3915 : /*
3916 : * add_table_end - moves left one glyph, adds a table end tag and adds
3917 : * a debugging string.
3918 : */
3919 :
3920 19 : void html_printer::add_table_end (const char *
3921 : #if defined(DEBUG_TABLES)
3922 : debug_string
3923 : #endif
3924 : )
3925 : {
3926 19 : page_contents->glyphs.move_left();
3927 19 : insert_tab_te();
3928 : #if defined(DEBUG_TABLES)
3929 : assert(debug_string != 0 /* nullptr */);
3930 : page_contents->insert_tag(string(debug_string));
3931 : #endif
3932 19 : }
3933 :
3934 : /*
3935 : * lookahead_for_tables - checks for .col tags and inserts table
3936 : * start/end tags
3937 : */
3938 :
3939 20 : void html_printer::lookahead_for_tables (void)
3940 : {
3941 : text_glob *g;
3942 20 : text_glob *start_of_line = 0 /* nullptr */;
3943 20 : text_glob *start_of_table = 0 /* nullptr */;
3944 20 : text_glob *last = 0 /* nullptr */;
3945 20 : colType type_of_col = none_tag;
3946 20 : int found_col = FALSE;
3947 20 : int ncol = 0;
3948 20 : int colmin = 0; // pacify compiler
3949 20 : int colmax = 0; // pacify compiler
3950 20 : html_table *tbl = new html_table(&html, -1);
3951 20 : const char *tab_defs = 0 /* nullptr */;
3952 20 : char align = 'L';
3953 20 : int nf = FALSE;
3954 20 : int old_pageoffset = pageoffset;
3955 :
3956 20 : remove_courier_tabs();
3957 20 : page_contents->dump_page();
3958 20 : insert_tab0_foreach_tab();
3959 20 : page_contents->dump_page();
3960 20 : if (! page_contents->glyphs.is_empty()) {
3961 14 : page_contents->glyphs.start_from_head();
3962 14 : as.reset();
3963 14 : g = page_contents->glyphs.get_data();
3964 14 : if (g->is_br()) {
3965 2 : g = page_contents->glyphs.move_right_get_data();
3966 2 : handle_state_assertion(g);
3967 2 : if (page_contents->glyphs.is_equal_to_head()) {
3968 0 : if (tbl != 0 /* nullptr */) {
3969 0 : delete tbl;
3970 0 : tbl = 0 /* nullptr */;
3971 : }
3972 0 : return;
3973 : }
3974 :
3975 2 : start_of_line = g;
3976 2 : ncol = 0;
3977 2 : if (found_col)
3978 0 : last = g;
3979 2 : found_col = FALSE;
3980 : }
3981 :
3982 22571 : do {
3983 : #if defined(DEBUG_TABLES)
3984 : fprintf(stderr, " [") ;
3985 : fprintf(stderr, g->text_string) ;
3986 : fprintf(stderr, "] ") ;
3987 : fflush(stderr);
3988 : if (strcmp(g->text_string, "XXXXXXX") == 0)
3989 : stop();
3990 : #endif
3991 :
3992 22585 : nf = calc_nf(g, nf);
3993 22585 : calc_po_in(g, nf);
3994 22585 : if (g->is_col()) {
3995 56 : if ((tab_tag == type_of_col)
3996 0 : && (start_of_table != 0 /* nullptr */)) {
3997 0 : page_contents->glyphs.move_left();
3998 0 : insert_tab_te();
3999 0 : start_of_table->remember_table(tbl);
4000 0 : tbl = new html_table(&html, -1);
4001 0 : page_contents->insert_tag(string("*** TAB -> COL ***"));
4002 0 : if (tab_defs != 0 /* nullptr */)
4003 0 : tbl->tab_stops->init(tab_defs);
4004 0 : start_of_table = 0 /* nullptr */;
4005 0 : last = 0 /* nullptr */;
4006 : }
4007 56 : type_of_col = col_tag;
4008 56 : found_col = TRUE;
4009 56 : ncol = g->get_arg();
4010 56 : align = 'L';
4011 56 : colmin = 0;
4012 56 : colmax = 0;
4013 22529 : } else if (g->is_tab()) {
4014 9 : type_of_col = tab_tag;
4015 9 : colmin = g->get_tab_args(&align);
4016 9 : align = 'L'; // for now as 'C' and 'R' are broken
4017 9 : ncol = tbl->find_tab_column(colmin);
4018 9 : colmin += pageoffset + get_troff_indent();
4019 9 : colmax = tbl->get_tab_pos(ncol+1);
4020 9 : if (colmax > 0)
4021 2 : colmax += pageoffset + get_troff_indent();
4022 22520 : } else if (g->is_tab0()) {
4023 2 : if ((col_tag == type_of_col)
4024 0 : && (start_of_table != 0 /* nullptr */)) {
4025 0 : page_contents->glyphs.move_left();
4026 0 : insert_tab_te();
4027 0 : start_of_table->remember_table(tbl);
4028 0 : tbl = new html_table(&html, -1);
4029 0 : page_contents->insert_tag(string("*** COL -> TAB ***"));
4030 0 : start_of_table = 0 /* nullptr */;
4031 0 : last = 0 /* nullptr */;
4032 : }
4033 2 : if (tab_defs != 0 /* nullptr */)
4034 2 : tbl->tab_stops->init(tab_defs);
4035 2 : type_of_col = tab0_tag;
4036 2 : ncol = 1;
4037 2 : colmin = 0;
4038 2 : colmax = tbl->get_tab_pos(2) + pageoffset + get_troff_indent();
4039 22518 : } else if (! g->is_a_tag())
4040 20863 : update_min_max(type_of_col, &colmin, &colmax, g);
4041 45114 : if ((g->is_col() || g->is_tab() || g->is_tab0())
4042 67 : && (start_of_line != 0 /* nullptr */)
4043 45170 : && (0 /* nullptr */ == start_of_table)) {
4044 20 : start_of_table = insert_tab_ts(start_of_line);
4045 20 : start_of_line = 0 /* nullptr */;
4046 22565 : } else if (g->is_ce()
4047 22565 : && (start_of_table != 0 /* nullptr */)) {
4048 0 : add_table_end("*** CE ***");
4049 0 : start_of_table->remember_table(tbl);
4050 0 : tbl = new html_table(&html, -1);
4051 0 : start_of_table = 0 /* nullptr */;
4052 0 : last = 0 /* nullptr */;
4053 22565 : } else if (g->is_ta()) {
4054 33 : tab_defs = g->text_string;
4055 33 : if (col_tag == type_of_col)
4056 8 : tbl->tab_stops->check_init(tab_defs);
4057 33 : if (!tbl->tab_stops->compatible(tab_defs)) {
4058 14 : if (start_of_table != 0 /* nullptr */) {
4059 0 : add_table_end("*** TABS ***");
4060 0 : start_of_table->remember_table(tbl);
4061 0 : tbl = new html_table(&html, -1);
4062 0 : start_of_table = 0 /* nullptr */;
4063 0 : type_of_col = none_tag;
4064 0 : last = 0 /* nullptr */;
4065 : }
4066 14 : tbl->tab_stops->init(tab_defs);
4067 : }
4068 : }
4069 24307 : if (((! g->is_a_tag()) || g->is_tab())
4070 24307 : && (start_of_table != 0 /* nullptr */)) {
4071 : // we are in a table and have a glyph
4072 378 : if ((0 == ncol)
4073 378 : || (! tbl->add_column(ncol, colmin, colmax, align))) {
4074 19 : if (0 == ncol)
4075 8 : add_table_end("*** NCOL == 0 ***");
4076 : else
4077 11 : add_table_end("*** CROSSED COLS ***");
4078 :
4079 19 : start_of_table->remember_table(tbl);
4080 19 : tbl = new html_table(&html, -1);
4081 19 : start_of_table = 0 /* nullptr */;
4082 19 : type_of_col = none_tag;
4083 19 : last = 0 /* nullptr */;
4084 : }
4085 : }
4086 : /*
4087 : * move onto next glob, check whether we are starting a new line
4088 : */
4089 22585 : g = page_contents->glyphs.move_right_get_data();
4090 22585 : handle_state_assertion(g);
4091 22585 : if (0 /* nullptr */ == g) {
4092 14 : if (found_col) {
4093 1 : page_contents->glyphs.start_from_head();
4094 1 : as.reset();
4095 1 : last = g;
4096 1 : found_col = FALSE;
4097 : }
4098 22571 : } else if (g->is_br() || (nf && g->is_eol())) {
4099 915 : do {
4100 3021 : g = page_contents->glyphs.move_right_get_data();
4101 3021 : handle_state_assertion(g);
4102 3021 : nf = calc_nf(g, nf);
4103 : } while ((g != 0 /* nullptr */)
4104 3021 : && (g->is_br() || (nf && g->is_eol())));
4105 2106 : start_of_line = g;
4106 2106 : ncol = 0;
4107 2106 : if (found_col)
4108 39 : last = g;
4109 2106 : found_col = FALSE;
4110 : }
4111 : } while ((g != 0 /* nullptr */)
4112 22585 : && (! page_contents->glyphs.is_equal_to_head()));
4113 :
4114 : #if defined(DEBUG_TABLES)
4115 : fprintf(stderr, "finished scanning for tables\n");
4116 : #endif
4117 :
4118 14 : page_contents->glyphs.start_from_head();
4119 14 : if (start_of_table != 0 /* nullptr */) {
4120 1 : if (last != 0 /* nullptr */)
4121 0 : while (last != page_contents->glyphs.get_data())
4122 0 : page_contents->glyphs.move_left();
4123 :
4124 1 : insert_tab_te();
4125 1 : start_of_table->remember_table(tbl);
4126 1 : tbl = 0 /* nullptr */;
4127 1 : page_contents->insert_tag(string("*** LAST ***"));
4128 : }
4129 : }
4130 20 : if (tbl != 0 /* nullptr */) {
4131 19 : delete tbl;
4132 19 : tbl = 0 /* nullptr */;
4133 : }
4134 :
4135 : // and reset the registers
4136 20 : pageoffset = old_pageoffset;
4137 20 : troff_indent = 0;
4138 20 : temp_indent = 0;
4139 20 : end_tempindent = 0;
4140 : }
4141 :
4142 20 : void html_printer::flush_page (void)
4143 : {
4144 20 : suppress_sub_sup = TRUE;
4145 20 : flush_sbuf();
4146 20 : page_contents->dump_page();
4147 20 : lookahead_for_tables();
4148 20 : page_contents->dump_page();
4149 20 : flush_globs();
4150 20 : current_paragraph->done_para();
4151 20 : current_paragraph->flush_text();
4152 : // move onto a new page
4153 20 : delete page_contents;
4154 : #if defined(DEBUG_TABLES)
4155 : fprintf(stderr, "\n\n*** flushed page ***\n\n");
4156 : html.simple_comment("new page called");
4157 : #endif
4158 20 : page_contents = new page;
4159 20 : }
4160 :
4161 : /*
4162 : * determine_space - works out whether we need to write a space.
4163 : * If last glyph is adjoining, then emit no space.
4164 : */
4165 :
4166 20506 : void html_printer::determine_space (text_glob *g)
4167 : {
4168 20506 : assert(g != 0 /* nullptr */);
4169 20506 : if (current_paragraph->is_in_pre()) {
4170 : /*
4171 : * .nf has been specified
4172 : */
4173 0 : while (output_hpos < g->minh) {
4174 0 : output_hpos += space_width;
4175 0 : current_paragraph->emit_space();
4176 : }
4177 : } else {
4178 20506 : if ((output_vpos != g->minv) || (output_hpos < g->minh)) {
4179 19727 : current_paragraph->emit_space();
4180 : }
4181 : }
4182 20506 : }
4183 :
4184 : /*
4185 : * is_line_start - returns TRUE if we are at the start of a line.
4186 : */
4187 :
4188 0 : int html_printer::is_line_start (int nf)
4189 : {
4190 0 : int line_start = FALSE; // TODO: boolify
4191 0 : int result = TRUE;
4192 0 : text_glob *orig = page_contents->glyphs.get_data();
4193 : text_glob *g;
4194 :
4195 0 : if (! page_contents->glyphs.is_equal_to_head()) {
4196 0 : do {
4197 0 : page_contents->glyphs.move_left();
4198 0 : g = page_contents->glyphs.get_data();
4199 0 : result = g->is_a_tag();
4200 0 : if (g->is_fi())
4201 0 : nf = FALSE;
4202 0 : else if (g->is_nf())
4203 0 : nf = TRUE;
4204 0 : line_start = g->is_col() || g->is_br() || (nf && g->is_eol());
4205 0 : } while ((!line_start) && (result));
4206 : /*
4207 : * now restore our previous position.
4208 : */
4209 0 : while (page_contents->glyphs.get_data() != orig)
4210 0 : page_contents->glyphs.move_right();
4211 : }
4212 0 : return result;
4213 : }
4214 :
4215 : /*
4216 : * is_font_courier - returns TRUE if the font, f, is courier.
4217 : */
4218 :
4219 : // TODO: boolify
4220 778 : int html_printer::is_font_courier (font *f)
4221 : {
4222 : // XXX: This logic locks us into a font description file naming
4223 : // scheme.
4224 778 : if (f != 0 /* nullptr */) {
4225 778 : const char *fontname = f->get_filename();
4226 778 : return((fontname != 0 /* nullptr */) && ('C' == fontname[0]));
4227 : }
4228 0 : return FALSE;
4229 : }
4230 :
4231 : /*
4232 : * end_font - shuts down the font corresponding to fontname.
4233 : */
4234 :
4235 1349 : void html_printer::end_font (const char *fontname)
4236 : {
4237 1349 : assert(fontname != 0 /* nullptr */);
4238 1349 : if (strcmp(fontname, "B") == 0) {
4239 517 : current_paragraph->done_bold();
4240 832 : } else if (strcmp(fontname, "I") == 0) {
4241 207 : current_paragraph->done_italic();
4242 625 : } else if (strcmp(fontname, "BI") == 0) {
4243 0 : current_paragraph->done_bold();
4244 0 : current_paragraph->done_italic();
4245 625 : } else if (strcmp(fontname, "CR") == 0) {
4246 0 : current_paragraph->done_tt();
4247 625 : } else if (strcmp(fontname, "CI") == 0) {
4248 0 : current_paragraph->done_italic();
4249 0 : current_paragraph->done_tt();
4250 625 : } else if (strcmp(fontname, "CB") == 0) {
4251 0 : current_paragraph->done_bold();
4252 0 : current_paragraph->done_tt();
4253 625 : } else if (strcmp(fontname, "CBI") == 0) {
4254 0 : current_paragraph->done_bold();
4255 0 : current_paragraph->done_italic();
4256 0 : current_paragraph->done_tt();
4257 : }
4258 1349 : }
4259 :
4260 : /*
4261 : * start_font - starts the font corresponding to name.
4262 : */
4263 :
4264 1447 : void html_printer::start_font (const char *fontname)
4265 : {
4266 1447 : assert(fontname != 0 /* nullptr */);
4267 1447 : if (strcmp(fontname, "R") == 0) {
4268 722 : current_paragraph->done_bold();
4269 722 : current_paragraph->done_italic();
4270 722 : current_paragraph->done_tt();
4271 725 : } else if (strcmp(fontname, "B") == 0) {
4272 517 : current_paragraph->do_bold();
4273 208 : } else if (strcmp(fontname, "I") == 0) {
4274 208 : current_paragraph->do_italic();
4275 0 : } else if (strcmp(fontname, "BI") == 0) {
4276 0 : current_paragraph->do_bold();
4277 0 : current_paragraph->do_italic();
4278 0 : } else if (strcmp(fontname, "CR") == 0) {
4279 0 : if ((! fill_on)
4280 0 : && is_courier_until_eol()
4281 0 : && is_line_start(! fill_on)) {
4282 0 : current_paragraph->do_pre();
4283 : }
4284 0 : current_paragraph->do_tt();
4285 0 : } else if (strcmp(fontname, "CI") == 0) {
4286 0 : if ((! fill_on)
4287 0 : && is_courier_until_eol()
4288 0 : && is_line_start(! fill_on)) {
4289 0 : current_paragraph->do_pre();
4290 : }
4291 0 : current_paragraph->do_tt();
4292 0 : current_paragraph->do_italic();
4293 0 : } else if (strcmp(fontname, "CB") == 0) {
4294 0 : if ((! fill_on)
4295 0 : && is_courier_until_eol()
4296 0 : && is_line_start(! fill_on)) {
4297 0 : current_paragraph->do_pre();
4298 : }
4299 0 : current_paragraph->do_tt();
4300 0 : current_paragraph->do_bold();
4301 0 : } else if (strcmp(fontname, "CBI") == 0) {
4302 0 : if ((! fill_on)
4303 0 : && is_courier_until_eol()
4304 0 : && is_line_start(! fill_on)) {
4305 0 : current_paragraph->do_pre();
4306 : }
4307 0 : current_paragraph->do_tt();
4308 0 : current_paragraph->do_italic();
4309 0 : current_paragraph->do_bold();
4310 : }
4311 1447 : }
4312 :
4313 : /*
4314 : * start_size - from is old font size, to is the new font size.
4315 : * The HTML elements <big> and <small> respectively
4316 : * increase and decrease the font size by 20%. We try and
4317 : * map these onto glyph sizes.
4318 : */
4319 :
4320 47 : void html_printer::start_size (int from, int to)
4321 : {
4322 47 : if (from < to) {
4323 48 : while (from < to) {
4324 25 : current_paragraph->do_big();
4325 25 : from += SIZE_INCREMENT;
4326 : }
4327 24 : } else if (from > to) {
4328 50 : while (from > to) {
4329 26 : current_paragraph->do_small();
4330 26 : from -= SIZE_INCREMENT;
4331 : }
4332 : }
4333 47 : }
4334 :
4335 : /*
4336 : * do_font - checks to see whether we need to alter the html font.
4337 : */
4338 :
4339 20845 : void html_printer::do_font (text_glob *g)
4340 : {
4341 20845 : assert(g != 0 /* nullptr */);
4342 : /*
4343 : * check if the output_style.point_size has not been set yet
4344 : * this allow users to place .ps at the top of their troff files
4345 : * and grohtml can then treat the .ps value as the base font size (3)
4346 : */
4347 20845 : if (-1 == output_style.point_size) {
4348 13 : output_style.point_size = pointsize;
4349 : }
4350 :
4351 20845 : if (g->text_style.f != output_style.f) {
4352 1447 : if (output_style.f != 0 /* nullptr */) {
4353 1349 : end_font(output_style.f->get_filename());
4354 : }
4355 1447 : output_style.f = g->text_style.f;
4356 1447 : if (output_style.f != 0 /* nullptr */) {
4357 1447 : start_font(output_style.f->get_filename());
4358 : }
4359 : }
4360 20845 : if (output_style.point_size != g->text_style.point_size) {
4361 60 : do_sup_or_sub(g);
4362 60 : if ((output_style.point_size > 0)
4363 47 : && (g->text_style.point_size > 0)) {
4364 47 : start_size(output_style.point_size, g->text_style.point_size);
4365 : }
4366 60 : if (g->text_style.point_size > 0) {
4367 58 : output_style.point_size = g->text_style.point_size;
4368 : }
4369 : }
4370 20845 : if (output_style.col != g->text_style.col) {
4371 6 : current_paragraph->done_color();
4372 6 : output_style.col = g->text_style.col;
4373 6 : current_paragraph->do_color(&output_style.col);
4374 : }
4375 20845 : }
4376 :
4377 : /*
4378 : * start_subscript - returns TRUE if, g, looks like a subscript start.
4379 : */
4380 :
4381 : // TODO: boolify
4382 17 : int html_printer::start_subscript (text_glob *g)
4383 : {
4384 17 : assert(g != 0 /* nullptr */);
4385 17 : int r = font::res;
4386 17 : int height = output_style.point_size*r/72;
4387 :
4388 17 : return ((output_style.point_size != 0)
4389 17 : && (output_vpos < g->minv)
4390 9 : && (output_vpos-height > g->maxv)
4391 34 : && (output_style.point_size > g->text_style.point_size));
4392 : }
4393 :
4394 : /*
4395 : * start_superscript - returns TRUE if, g, looks like a superscript
4396 : * start.
4397 : */
4398 :
4399 : // TODO: boolify
4400 17 : int html_printer::start_superscript (text_glob *g)
4401 : {
4402 17 : assert(g != 0 /* nullptr */);
4403 17 : int r = font::res;
4404 17 : int height = output_style.point_size*r/72;
4405 :
4406 17 : return ((output_style.point_size != 0)
4407 17 : && (output_vpos > g->minv)
4408 8 : && (output_vpos-height < g->maxv)
4409 34 : && (output_style.point_size > g->text_style.point_size));
4410 : }
4411 :
4412 : /*
4413 : * end_subscript - returns TRUE if, g, looks like the end of a
4414 : * subscript.
4415 : */
4416 :
4417 : // TODO: boolify
4418 17 : int html_printer::end_subscript (text_glob *g)
4419 : {
4420 17 : assert(g != 0 /* nullptr */);
4421 17 : int r = font::res;
4422 17 : int height = output_style.point_size*r/72;
4423 :
4424 17 : return ((output_style.point_size != 0)
4425 17 : && (g->minv < output_vpos)
4426 8 : && (output_vpos-height > g->maxv)
4427 34 : && (output_style.point_size < g->text_style.point_size));
4428 : }
4429 :
4430 : /*
4431 : * end_superscript - returns TRUE if, g, looks like the end of a
4432 : * superscript.
4433 : */
4434 :
4435 : // TODO: boolify
4436 17 : int html_printer::end_superscript (text_glob *g)
4437 : {
4438 17 : assert(g != 0 /* nullptr */);
4439 17 : int r = font::res;
4440 17 : int height = output_style.point_size*r/72;
4441 :
4442 17 : return ((output_style.point_size != 0)
4443 17 : && (g->minv > output_vpos)
4444 9 : && (output_vpos-height < g->maxv)
4445 34 : && (output_style.point_size < g->text_style.point_size));
4446 : }
4447 :
4448 : /*
4449 : * do_sup_or_sub - checks to see whether the next glyph is a
4450 : * subscript/superscript start/end and it calls the
4451 : * services of html-text to issue the appropriate tags.
4452 : */
4453 :
4454 60 : void html_printer::do_sup_or_sub (text_glob *g)
4455 : {
4456 60 : if (! suppress_sub_sup) {
4457 17 : if (start_subscript(g)) {
4458 0 : current_paragraph->do_sub();
4459 17 : } else if (start_superscript(g)) {
4460 0 : current_paragraph->do_sup();
4461 17 : } else if (end_subscript(g)) {
4462 0 : current_paragraph->done_sub();
4463 17 : } else if (end_superscript(g)) {
4464 0 : current_paragraph->done_sup();
4465 : }
4466 : }
4467 60 : }
4468 :
4469 : /*
4470 : * do_end_para - writes out the html text after shutting down the
4471 : * current paragraph.
4472 : */
4473 :
4474 283 : void html_printer::do_end_para (text_glob *g)
4475 : {
4476 283 : assert(g != 0 /* nullptr */);
4477 283 : do_font(g);
4478 283 : current_paragraph->done_para();
4479 283 : current_paragraph->remove_para_space();
4480 283 : html.put_string(g->text_string+9);
4481 283 : output_vpos = g->minv;
4482 283 : output_hpos = g->maxh;
4483 283 : output_vpos_max = g->maxv;
4484 283 : suppress_sub_sup = FALSE;
4485 283 : }
4486 :
4487 : /*
4488 : * emit_html - write out the html text
4489 : */
4490 :
4491 20506 : void html_printer::emit_html (text_glob *g)
4492 : {
4493 20506 : assert(g != 0 /* nullptr */);
4494 20506 : do_font(g);
4495 20506 : determine_space(g);
4496 20506 : current_paragraph->do_emittext(g->text_string, g->text_length);
4497 20506 : output_vpos = g->minv;
4498 20506 : output_hpos = g->maxh;
4499 20506 : output_vpos_max = g->maxv;
4500 20506 : suppress_sub_sup = FALSE;
4501 20506 : }
4502 :
4503 : /*
4504 : * flush_sbuf - flushes the current sbuf into the list of glyphs.
4505 : */
4506 :
4507 31080 : void html_printer::flush_sbuf()
4508 : {
4509 31080 : if (sbuf.length() > 0) {
4510 20281 : int r=font::res; // resolution of the device
4511 20281 : set_style(sbuf_style);
4512 :
4513 20281 : if (overstrike_detected && (! is_bold(sbuf_style.f))) {
4514 0 : font *bold_font = make_bold(sbuf_style.f);
4515 0 : if (bold_font != 0 /* nullptr */)
4516 0 : sbuf_style.f = bold_font;
4517 : }
4518 :
4519 20281 : page_contents->add(&sbuf_style, sbuf, line_number,
4520 20281 : (sbuf_vpos - (sbuf_style.point_size * r / 72)),
4521 : sbuf_start_hpos, sbuf_vpos, sbuf_end_hpos);
4522 20281 : output_hpos = sbuf_end_hpos;
4523 20281 : output_vpos = sbuf_vpos;
4524 20281 : last_sbuf_length = 0;
4525 20281 : sbuf_prev_hpos = sbuf_end_hpos;
4526 20281 : overstrike_detected = FALSE;
4527 20281 : sbuf.clear();
4528 : }
4529 31080 : }
4530 :
4531 0 : void html_printer::set_line_thickness(const environment *env)
4532 : {
4533 0 : assert(env != 0 /* nullptr */);
4534 0 : line_thickness = env->size;
4535 0 : }
4536 :
4537 0 : void html_printer::draw(int code, int *p, int np,
4538 : const environment *env)
4539 : {
4540 0 : assert(p != 0 /* nullptr */);
4541 0 : assert(env != 0 /* nullptr */);
4542 0 : switch (code) {
4543 :
4544 0 : case 'l':
4545 : # if 0
4546 : if (2 == np) {
4547 : page_contents->add_line(&sbuf_style,
4548 : line_number,
4549 : env->hpos, env->vpos,
4550 : (env->hpos + p[0]), (env->vpos + p[1]),
4551 : line_thickness);
4552 : } else {
4553 : error("2 arguments required for line");
4554 : }
4555 : # endif
4556 0 : break;
4557 0 : case 't':
4558 : {
4559 0 : if (0 == np) {
4560 0 : line_thickness = -1;
4561 : } else {
4562 : // troff gratuitously adds an extra 0
4563 0 : if ((np != 1) && (np != 2)) {
4564 0 : error("0 or 1 argument required for thickness");
4565 0 : break;
4566 : }
4567 0 : line_thickness = p[0];
4568 : }
4569 0 : break;
4570 : }
4571 :
4572 0 : case 'P':
4573 0 : break;
4574 0 : case 'p':
4575 0 : break;
4576 0 : case 'E':
4577 0 : break;
4578 0 : case 'e':
4579 0 : break;
4580 0 : case 'C':
4581 0 : break;
4582 0 : case 'c':
4583 0 : break;
4584 0 : case 'a':
4585 0 : break;
4586 0 : case '~':
4587 0 : break;
4588 0 : case 'f':
4589 0 : break;
4590 0 : case 'F':
4591 : // fill with color env->fill
4592 0 : if (background != 0 /* nullptr */)
4593 0 : delete background;
4594 0 : background = new color;
4595 0 : *background = *env->fill;
4596 0 : break;
4597 :
4598 0 : default:
4599 0 : error("unrecognized drawing command '%1'", char(code));
4600 0 : break;
4601 : }
4602 0 : }
4603 :
4604 17 : html_printer::html_printer()
4605 : : html(0, MAX_LINE_LENGTH),
4606 : no_of_printed_pages(0),
4607 : last_sbuf_length(0),
4608 : overstrike_detected(FALSE),
4609 : output_hpos(-1),
4610 : output_vpos(-1),
4611 : output_vpos_max(-1),
4612 : line_thickness(-1),
4613 : inside_font_style(0),
4614 : page_number(0),
4615 : header_indent(-1),
4616 : suppress_sub_sup(TRUE),
4617 : cutoff_heading(100),
4618 : indent(0),
4619 : table(0),
4620 : end_center(0),
4621 : end_tempindent(0),
4622 : next_tag(INLINE),
4623 : fill_on(TRUE),
4624 : max_linelength(-1),
4625 : linelength(0),
4626 : pageoffset(0),
4627 : troff_indent(0),
4628 : device_indent(0),
4629 : temp_indent(0),
4630 : pointsize(base_point_size),
4631 : line_number(0),
4632 : background(default_background),
4633 : seen_indent(FALSE),
4634 : next_indent(0),
4635 : seen_pageoffset(FALSE),
4636 : next_pageoffset(0),
4637 : seen_linelength(FALSE),
4638 : next_linelength(0),
4639 : seen_center(FALSE),
4640 : next_center(0),
4641 : seen_space(0),
4642 : seen_break(0),
4643 : current_column(0),
4644 17 : row_space(FALSE)
4645 : {
4646 17 : file_list.add_new_file(xtmpfile());
4647 17 : html.set_file(file_list.get_file());
4648 17 : if (font::hor != 24)
4649 0 : fatal("horizontal motion quantum must be 24");
4650 17 : if (font::vert != 40)
4651 0 : fatal("vertical motion quantum must be 40");
4652 : #if 0
4653 : // should be sorted html..
4654 : if ((font::res % (font::sizescale*72)) != 0)
4655 : fatal("res must be a multiple of 72*sizescale");
4656 : #endif
4657 17 : int r = font::res;
4658 17 : int point = 0;
4659 34 : while ((r % 10) == 0) {
4660 17 : r /= 10;
4661 17 : point++;
4662 : }
4663 17 : res = r;
4664 17 : html.set_fixed_point(point);
4665 17 : space_glyph = name_to_glyph("space");
4666 17 : space_width = font::hor;
4667 17 : paper_length = font::paperlength;
4668 17 : linelength = font::res*13/2;
4669 17 : if (0 == paper_length)
4670 17 : paper_length = 11*font::res;
4671 :
4672 17 : page_contents = new page();
4673 17 : }
4674 :
4675 : /*
4676 : * add_to_sbuf - adds character code or name to the sbuf.
4677 : */
4678 :
4679 97980 : void html_printer::add_to_sbuf (glyph *g, const string &s)
4680 : {
4681 97980 : if (0 /* nullptr */ == sbuf_style.f)
4682 0 : return;
4683 :
4684 97980 : const char *html_glyph = 0;
4685 97980 : unsigned int code = sbuf_style.f->get_code(g);
4686 :
4687 97980 : if (s.empty()) {
4688 97652 : if (sbuf_style.f->contains(g))
4689 97652 : html_glyph = get_html_entity(sbuf_style.f->get_code(g));
4690 : else
4691 0 : html_glyph = 0;
4692 :
4693 97652 : if ((0 /* nullptr */ == html_glyph) && (code >= UNICODE_DESC_START))
4694 0 : html_glyph = static_cast<bool>(charset_encoding)
4695 0 : ? to_utf8_string(code)
4696 0 : : to_numerical_char_ref(code);
4697 : } else
4698 328 : html_glyph = get_html_translation(sbuf_style.f, s);
4699 :
4700 97980 : last_sbuf_length = sbuf.length();
4701 97980 : if (0 /* nullptr */ == html_glyph)
4702 95741 : sbuf += ((char)code);
4703 : else
4704 2239 : sbuf += html_glyph;
4705 : }
4706 :
4707 : // TODO: boolify
4708 93925 : int html_printer::sbuf_continuation (glyph *g, const char *name,
4709 : const environment *env, int w)
4710 : {
4711 93925 : assert(env != 0 /* nullptr */);
4712 : /*
4713 : * lets see whether the glyph is closer to the end of sbuf
4714 : */
4715 93925 : if ((sbuf_end_hpos == env->hpos)
4716 16226 : || ((sbuf_prev_hpos < sbuf_end_hpos)
4717 16226 : && (env->hpos < sbuf_end_hpos)
4718 0 : && ((sbuf_end_hpos-env->hpos < env->hpos-sbuf_prev_hpos)))) {
4719 77699 : add_to_sbuf(g, name);
4720 77699 : sbuf_prev_hpos = sbuf_end_hpos;
4721 77699 : sbuf_end_hpos += w + sbuf_kern;
4722 77699 : return TRUE;
4723 : } else {
4724 16226 : if ((env->hpos >= sbuf_end_hpos)
4725 16226 : && ((0 == sbuf_kern)
4726 0 : || (sbuf_end_hpos - sbuf_kern != env->hpos))) {
4727 : /*
4728 : * lets see whether a space is needed or not
4729 : */
4730 :
4731 16226 : if (env->hpos-sbuf_end_hpos < space_width) {
4732 0 : add_to_sbuf(g, name);
4733 0 : sbuf_prev_hpos = sbuf_end_hpos;
4734 0 : sbuf_end_hpos = env->hpos + w;
4735 0 : return TRUE;
4736 : }
4737 : }
4738 : }
4739 16226 : return FALSE;
4740 : }
4741 :
4742 : /*
4743 : * get_html_translation - given the position of the character and its
4744 : * name return the device encoding for such
4745 : * character.
4746 : */
4747 :
4748 329 : const char *get_html_translation (font *f, const string &name)
4749 : {
4750 329 : if ((0 /* nullptr */ == f) || name.empty())
4751 0 : return 0 /* nullptr */;
4752 : else {
4753 329 : glyph *g = name_to_glyph((name + '\0').contents());
4754 329 : if (f->contains(g))
4755 329 : return get_html_entity(f->get_code(g));
4756 : else
4757 0 : return 0 /* nullptr */;
4758 : }
4759 : }
4760 :
4761 : /*
4762 : * get_html_entity - given a Unicode character's code point, return an
4763 : * HTML entity that represents the character, if the
4764 : * character cannot represent itself in all contexts.
4765 : * the return value, if not a null pointer, is
4766 : * allocated in a static buffer and is only valid
4767 : * until the next call of this function.
4768 : */
4769 97981 : static const char *get_html_entity (unsigned int code)
4770 : {
4771 97981 : if (code < UNICODE_DESC_START) {
4772 96337 : switch (code) {
4773 240 : case 0x0022: return """;
4774 10 : case 0x0026: return "&";
4775 168 : case 0x003C: return "<";
4776 178 : case 0x003E: return ">";
4777 95741 : default: return 0;
4778 : }
4779 1644 : } else if (CHARSET_UTF8 == charset_encoding) {
4780 0 : return to_utf8_string(code);
4781 : } else {
4782 1644 : switch (code) {
4783 119 : case 0x00A0: return " ";
4784 0 : case 0x00A1: return "¡";
4785 0 : case 0x00A2: return "¢";
4786 0 : case 0x00A3: return "£";
4787 0 : case 0x00A4: return "¤";
4788 0 : case 0x00A5: return "¥";
4789 0 : case 0x00A6: return "¦";
4790 0 : case 0x00A7: return "§";
4791 0 : case 0x00A8: return "¨";
4792 0 : case 0x00A9: return "©";
4793 0 : case 0x00AA: return "ª";
4794 0 : case 0x00AB: return "«";
4795 0 : case 0x00AC: return "¬";
4796 0 : case 0x00AE: return "®";
4797 0 : case 0x00AF: return "¯";
4798 0 : case 0x00B0: return "°";
4799 0 : case 0x00B1: return "±";
4800 0 : case 0x00B2: return "²";
4801 0 : case 0x00B3: return "³";
4802 0 : case 0x00B4: return "´";
4803 0 : case 0x00B5: return "µ";
4804 0 : case 0x00B6: return "¶";
4805 0 : case 0x00B7: return "·";
4806 0 : case 0x00B8: return "¸";
4807 0 : case 0x00B9: return "¹";
4808 0 : case 0x00BA: return "º";
4809 0 : case 0x00BB: return "»";
4810 0 : case 0x00BC: return "¼";
4811 0 : case 0x00BD: return "½";
4812 0 : case 0x00BE: return "¾";
4813 0 : case 0x00BF: return "¿";
4814 0 : case 0x00C0: return "À";
4815 0 : case 0x00C1: return "Á";
4816 0 : case 0x00C2: return "Â";
4817 0 : case 0x00C3: return "Ã";
4818 0 : case 0x00C4: return "Ä";
4819 0 : case 0x00C5: return "Å";
4820 0 : case 0x00C6: return "Æ";
4821 0 : case 0x00C7: return "Ç";
4822 0 : case 0x00C8: return "È";
4823 0 : case 0x00C9: return "É";
4824 0 : case 0x00CA: return "Ê";
4825 0 : case 0x00CB: return "Ë";
4826 0 : case 0x00CC: return "Ì";
4827 0 : case 0x00CD: return "Í";
4828 0 : case 0x00CE: return "Î";
4829 0 : case 0x00CF: return "Ï";
4830 0 : case 0x00D0: return "Ð";
4831 0 : case 0x00D1: return "Ñ";
4832 0 : case 0x00D2: return "Ò";
4833 0 : case 0x00D3: return "Ó";
4834 0 : case 0x00D4: return "Ô";
4835 0 : case 0x00D5: return "Õ";
4836 1 : case 0x00D6: return "Ö";
4837 0 : case 0x00D7: return "×";
4838 0 : case 0x00D8: return "Ø";
4839 0 : case 0x00D9: return "Ù";
4840 0 : case 0x00DA: return "Ú";
4841 0 : case 0x00DB: return "Û";
4842 0 : case 0x00DC: return "Ü";
4843 0 : case 0x00DD: return "Ý";
4844 0 : case 0x00DE: return "Þ";
4845 0 : case 0x00DF: return "ß";
4846 0 : case 0x00E0: return "à";
4847 3 : case 0x00E1: return "á";
4848 0 : case 0x00E2: return "â";
4849 0 : case 0x00E3: return "ã";
4850 1 : case 0x00E4: return "ä";
4851 0 : case 0x00E5: return "å";
4852 1 : case 0x00E6: return "æ";
4853 0 : case 0x00E7: return "ç";
4854 0 : case 0x00E8: return "è";
4855 1 : case 0x00E9: return "é";
4856 0 : case 0x00EA: return "ê";
4857 0 : case 0x00EB: return "ë";
4858 0 : case 0x00EC: return "ì";
4859 0 : case 0x00ED: return "í";
4860 0 : case 0x00EE: return "î";
4861 0 : case 0x00EF: return "ï";
4862 0 : case 0x00F0: return "ð";
4863 0 : case 0x00F1: return "ñ";
4864 0 : case 0x00F2: return "ò";
4865 1 : case 0x00F3: return "ó";
4866 0 : case 0x00F4: return "ô";
4867 0 : case 0x00F5: return "õ";
4868 1 : case 0x00F6: return "ö";
4869 0 : case 0x00F7: return "÷";
4870 0 : case 0x00F8: return "ø";
4871 0 : case 0x00F9: return "ù";
4872 0 : case 0x00FA: return "ú";
4873 0 : case 0x00FB: return "û";
4874 2 : case 0x00FC: return "ü";
4875 0 : case 0x00FD: return "ý";
4876 0 : case 0x00FE: return "þ";
4877 0 : case 0x00FF: return "ÿ";
4878 0 : case 0x0152: return "Œ";
4879 0 : case 0x0153: return "œ";
4880 0 : case 0x0160: return "Š";
4881 0 : case 0x0161: return "š";
4882 0 : case 0x0178: return "Ÿ";
4883 0 : case 0x0192: return "ƒ";
4884 0 : case 0x0391: return "Α";
4885 0 : case 0x0392: return "Β";
4886 0 : case 0x0393: return "Γ";
4887 0 : case 0x0394: return "Δ";
4888 0 : case 0x0395: return "Ε";
4889 0 : case 0x0396: return "Ζ";
4890 0 : case 0x0397: return "Η";
4891 0 : case 0x0398: return "Θ";
4892 0 : case 0x0399: return "Ι";
4893 0 : case 0x039A: return "Κ";
4894 0 : case 0x039B: return "Λ";
4895 0 : case 0x039C: return "Μ";
4896 0 : case 0x039D: return "Ν";
4897 0 : case 0x039E: return "Ξ";
4898 0 : case 0x039F: return "Ο";
4899 0 : case 0x03A0: return "Π";
4900 0 : case 0x03A1: return "Ρ";
4901 0 : case 0x03A3: return "Σ";
4902 0 : case 0x03A4: return "Τ";
4903 0 : case 0x03A5: return "Υ";
4904 0 : case 0x03A6: return "Φ";
4905 0 : case 0x03A7: return "Χ";
4906 0 : case 0x03A8: return "Ψ";
4907 0 : case 0x03A9: return "Ω";
4908 0 : case 0x03B1: return "α";
4909 0 : case 0x03B2: return "β";
4910 0 : case 0x03B3: return "γ";
4911 0 : case 0x03B4: return "δ";
4912 0 : case 0x03B5: return "ε";
4913 0 : case 0x03B6: return "ζ";
4914 0 : case 0x03B7: return "η";
4915 0 : case 0x03B8: return "θ";
4916 0 : case 0x03B9: return "ι";
4917 0 : case 0x03BA: return "κ";
4918 0 : case 0x03BB: return "λ";
4919 0 : case 0x03BC: return "μ";
4920 0 : case 0x03BD: return "ν";
4921 0 : case 0x03BE: return "ξ";
4922 0 : case 0x03BF: return "ο";
4923 0 : case 0x03C0: return "π";
4924 0 : case 0x03C1: return "ρ";
4925 0 : case 0x03C2: return "ς";
4926 0 : case 0x03C3: return "σ";
4927 0 : case 0x03C4: return "τ";
4928 0 : case 0x03C5: return "υ";
4929 0 : case 0x03C6: return "φ";
4930 0 : case 0x03C7: return "χ";
4931 0 : case 0x03C8: return "ψ";
4932 0 : case 0x03C9: return "ω";
4933 0 : case 0x03D1: return "ϑ";
4934 0 : case 0x03D6: return "ϖ";
4935 5 : case 0x2013: return "–";
4936 9 : case 0x2014: return "—";
4937 547 : case 0x2018: return "‘";
4938 694 : case 0x2019: return "’";
4939 0 : case 0x201A: return "‚";
4940 19 : case 0x201C: return "“";
4941 19 : case 0x201D: return "”";
4942 0 : case 0x201E: return "„";
4943 0 : case 0x2020: return "†";
4944 0 : case 0x2021: return "‡";
4945 0 : case 0x2022: return "•";
4946 0 : case 0x2030: return "‰";
4947 0 : case 0x2032: return "′";
4948 0 : case 0x2033: return "″";
4949 0 : case 0x2039: return "‹";
4950 0 : case 0x203A: return "›";
4951 0 : case 0x203E: return "‾";
4952 0 : case 0x2044: return "⁄";
4953 0 : case 0x20AC: return "€";
4954 0 : case 0x2111: return "ℑ";
4955 0 : case 0x2118: return "℘";
4956 0 : case 0x211C: return "ℜ";
4957 0 : case 0x2122: return "™";
4958 0 : case 0x2135: return "ℵ";
4959 0 : case 0x2190: return "←";
4960 0 : case 0x2191: return "↑";
4961 0 : case 0x2192: return "→";
4962 0 : case 0x2193: return "↓";
4963 0 : case 0x2194: return "↔";
4964 0 : case 0x21D0: return "⇐";
4965 0 : case 0x21D1: return "⇑";
4966 0 : case 0x21D2: return "⇒";
4967 0 : case 0x21D3: return "⇓";
4968 0 : case 0x21D4: return "⇔";
4969 0 : case 0x2200: return "∀";
4970 0 : case 0x2202: return "∂";
4971 0 : case 0x2203: return "∃";
4972 0 : case 0x2205: return "∅";
4973 0 : case 0x2207: return "∇";
4974 0 : case 0x2208: return "∈";
4975 0 : case 0x2209: return "∉";
4976 0 : case 0x220B: return "∋";
4977 0 : case 0x220F: return "∏";
4978 0 : case 0x2211: return "∑";
4979 192 : case 0x2212: return "−";
4980 0 : case 0x2217: return "∗";
4981 0 : case 0x221A: return "√";
4982 0 : case 0x221D: return "∝";
4983 0 : case 0x221E: return "∞";
4984 0 : case 0x2220: return "∠";
4985 0 : case 0x2227: return "∧";
4986 0 : case 0x2228: return "∨";
4987 0 : case 0x2229: return "∩";
4988 0 : case 0x222A: return "∪";
4989 0 : case 0x222B: return "∫";
4990 0 : case 0x2234: return "∴";
4991 0 : case 0x223C: return "∼";
4992 0 : case 0x2245: return "≅";
4993 0 : case 0x2248: return "≈";
4994 0 : case 0x2260: return "≠";
4995 0 : case 0x2261: return "≡";
4996 0 : case 0x2264: return "≤";
4997 2 : case 0x2265: return "≥";
4998 0 : case 0x2282: return "⊂";
4999 0 : case 0x2283: return "⊃";
5000 0 : case 0x2284: return "⊄";
5001 0 : case 0x2286: return "⊆";
5002 0 : case 0x2287: return "⊇";
5003 0 : case 0x2295: return "⊕";
5004 0 : case 0x2297: return "⊗";
5005 0 : case 0x22A5: return "⊥";
5006 0 : case 0x22C5: return "⋅";
5007 0 : case 0x2308: return "⌈";
5008 0 : case 0x2309: return "⌉";
5009 0 : case 0x230A: return "⌊";
5010 0 : case 0x230B: return "⌋";
5011 11 : case 0x2329: return "⟨";
5012 11 : case 0x232A: return "⟩";
5013 0 : case 0x25CA: return "◊";
5014 0 : case 0x2660: return "♠";
5015 0 : case 0x2663: return "♣";
5016 0 : case 0x2665: return "♥";
5017 0 : case 0x2666: return "♦";
5018 0 : case 0x27E8: return "⟨";
5019 0 : case 0x27E9: return "⟩";
5020 5 : default: return (static_cast<bool>(charset_encoding)
5021 5 : ? to_utf8_string(code)
5022 10 : : to_numerical_char_ref(code));
5023 : }
5024 : }
5025 : }
5026 :
5027 : /*
5028 : * overstrike - returns TRUE if the glyph (i, name) is going to
5029 : * overstrike a previous glyph in sbuf. If TRUE the font
5030 : * is changed to bold and the previous sbuf is flushed.
5031 : */
5032 :
5033 : // TODO: boolify
5034 16226 : int html_printer::overstrike(glyph *g, const char *name,
5035 : const environment *env, int w)
5036 : {
5037 16226 : assert(env != 0 /* nullptr */);
5038 16226 : if ((env->hpos < sbuf_end_hpos)
5039 16226 : || ((sbuf_kern != 0) && (sbuf_end_hpos - sbuf_kern < env->hpos)))
5040 : {
5041 : /*
5042 : * at this point we have detected an overlap
5043 : */
5044 0 : if (overstrike_detected) {
5045 : /* already detected, remove previous glyph and use this glyph */
5046 0 : sbuf.set_length(last_sbuf_length);
5047 0 : add_to_sbuf(g, name);
5048 0 : sbuf_end_hpos = env->hpos + w;
5049 0 : return TRUE;
5050 : } else {
5051 : /* first time we have detected an overstrike in the sbuf */
5052 0 : sbuf.set_length(last_sbuf_length); /* remove previous glyph */
5053 0 : if (! is_bold(sbuf_style.f))
5054 0 : flush_sbuf();
5055 0 : overstrike_detected = TRUE;
5056 0 : add_to_sbuf(g, name);
5057 0 : sbuf_end_hpos = env->hpos + w;
5058 0 : return TRUE;
5059 : }
5060 : }
5061 16226 : return FALSE;
5062 : }
5063 :
5064 : /*
5065 : * set_char - adds a character into the sbuf if it is a continuation
5066 : * with the previous word otherwise flush the current sbuf
5067 : * and add character anew.
5068 : */
5069 :
5070 97980 : void html_printer::set_char(glyph *g, font *f, const environment *env,
5071 : int w, const char *name)
5072 : {
5073 97980 : assert(env != 0 /* nullptr */);
5074 97980 : style sty(f, env->size, env->height, env->slant, env->fontno,
5075 97980 : *env->col);
5076 97980 : if (sty.slant != 0) {
5077 0 : if ((sty.slant > 80) || (sty.slant < -80)) {
5078 0 : error("slant of %1 degrees out of range", sty.slant);
5079 0 : sty.slant = 0;
5080 : }
5081 : }
5082 97980 : if (((!sbuf.empty())
5083 95183 : && (sty == sbuf_style)
5084 93925 : && (sbuf_vpos == env->vpos))
5085 209389 : && (sbuf_continuation(g, name, env, w)
5086 16226 : || overstrike(g, name, env, w)))
5087 77699 : return;
5088 :
5089 20281 : flush_sbuf();
5090 20281 : if (0 /* nullptr */ == sbuf_style.f)
5091 7 : sbuf_style = sty;
5092 20281 : add_to_sbuf(g, name);
5093 20281 : sbuf_end_hpos = env->hpos + w;
5094 20281 : sbuf_start_hpos = env->hpos;
5095 20281 : sbuf_prev_hpos = env->hpos;
5096 20281 : sbuf_vpos = env->vpos;
5097 20281 : sbuf_style = sty;
5098 20281 : sbuf_kern = 0;
5099 : }
5100 :
5101 : /*
5102 : * set_numbered_char - handle numbered characters. Negative values are
5103 : * interpreted as unbreakable spaces; the value
5104 : * (taken positive) gives the width.
5105 : */
5106 :
5107 123 : void html_printer::set_numbered_char(int num, const environment *env,
5108 : int *widthp)
5109 : {
5110 123 : assert(env != 0 /* nullptr */);
5111 123 : int nbsp_width = 0;
5112 123 : if (num < 0) {
5113 119 : nbsp_width = -num;
5114 119 : num = 160; //
5115 : }
5116 123 : glyph *g = number_to_glyph(num);
5117 123 : int fn = env->fontno;
5118 123 : if ((fn < 0) || (fn >= nfonts)) {
5119 0 : error("invalid font position '%1'", fn);
5120 0 : return;
5121 : }
5122 123 : font *f = font_table[fn];
5123 123 : if (0 /* nullptr */ == f) {
5124 0 : error("no font mounted at position %1", fn);
5125 0 : return;
5126 : }
5127 123 : if (!f->contains(g)) {
5128 0 : error("font description file '%1' has no glyph at index %2",
5129 0 : f->get_filename(), num);
5130 0 : return;
5131 : }
5132 : int w;
5133 123 : if (nbsp_width > 0)
5134 119 : w = nbsp_width;
5135 : else
5136 4 : w = f->get_width(g, env->size);
5137 123 : w = round_width(w);
5138 123 : if (widthp != 0 /* nullptr */)
5139 0 : *widthp = w;
5140 123 : set_char(g, f, env, w, 0 /* nullptr */);
5141 : }
5142 :
5143 : // XXX: Except for `w = round_width(w);`, this seems to be identical
5144 : // to the overridden `printer::set_char_and_width()`.
5145 0 : glyph *html_printer::set_char_and_width(const char *nm,
5146 : const environment *env,
5147 : int *widthp, font **f)
5148 : {
5149 0 : assert(nm != 0 /* nullptr */);
5150 0 : assert(env != 0 /* nullptr */);
5151 0 : assert(f != 0 /* nullptr */);
5152 0 : glyph *g = name_to_glyph(nm);
5153 0 : int fn = env->fontno;
5154 0 : if ((fn < 0) || (fn >= nfonts)) {
5155 0 : error("invalid font position '%1'", fn);
5156 0 : return UNDEFINED_GLYPH;
5157 : }
5158 0 : *f = font_table[fn];
5159 0 : if (0 /* nullptr */ == *f) {
5160 0 : error("no font mounted at position %1", fn);
5161 0 : return UNDEFINED_GLYPH;
5162 : }
5163 0 : if (!(*f)->contains(g)) {
5164 0 : if ((nm[0] != '\0') && ('\0' == nm[1]))
5165 0 : error("font description file '%1' lacks glyph for ordinary"
5166 0 : " character '%2'", (*f)->get_filename(), nm[0]);
5167 : else
5168 0 : error("font description file '%1' lacks glyph for special"
5169 0 : " character '%2'", (*f)->get_filename(), nm);
5170 0 : return UNDEFINED_GLYPH;
5171 : }
5172 0 : int w = (*f)->get_width(g, env->size);
5173 0 : w = round_width(w);
5174 0 : if (widthp != 0 /* nullptr */)
5175 0 : *widthp = w;
5176 0 : return g;
5177 : }
5178 :
5179 : /*
5180 : * write_title - writes the title to this document
5181 : */
5182 :
5183 34 : void html_printer::write_title (int in_head)
5184 : {
5185 34 : if (title.has_been_found) {
5186 12 : if (in_head) {
5187 6 : html.put_string("<title>");
5188 6 : html.put_string(title.text);
5189 6 : html.put_string("</title>").nl().nl();
5190 : } else {
5191 6 : title.has_been_written = TRUE;
5192 6 : if (title.with_h1) {
5193 5 : if (xhtml == dialect)
5194 0 : html.put_string("<h1>");
5195 : else
5196 5 : html.put_string("<h1 align=\"center\">");
5197 5 : html.put_string(title.text);
5198 5 : html.put_string("</h1>").nl().nl();
5199 : }
5200 : }
5201 22 : } else if (in_head) {
5202 : // place empty title tags to help conform to 'tidy'
5203 11 : html.put_string("<title></title>").nl();
5204 : }
5205 34 : }
5206 :
5207 : /*
5208 : * write_rule - emits HTML rule element if the auto_rule is TRUE.
5209 : */
5210 :
5211 192 : static void write_rule (void)
5212 : {
5213 192 : if (auto_rule) {
5214 123 : if (xhtml == dialect)
5215 0 : fputs("<hr/>\n", stdout);
5216 : else
5217 123 : fputs("<hr>\n", stdout);
5218 : }
5219 192 : }
5220 :
5221 20 : void html_printer::begin_page(int n)
5222 : {
5223 20 : page_number = n;
5224 : #if defined(DEBUGGING)
5225 : html.begin_comment("Page: ")
5226 : .put_string(i_to_a(page_number)).end_comment();;
5227 : #endif
5228 20 : no_of_printed_pages++;
5229 :
5230 20 : output_style.f = 0;
5231 20 : output_style.point_size= -1;
5232 20 : output_space_code = 32;
5233 20 : output_draw_point_size = -1;
5234 20 : output_line_thickness = -1;
5235 20 : output_hpos = -1;
5236 20 : output_vpos = -1;
5237 20 : output_vpos_max = -1;
5238 20 : current_paragraph = new html_text(&html, dialect);
5239 20 : do_indent(get_troff_indent(), pageoffset, linelength);
5240 20 : current_paragraph->do_para("", FALSE);
5241 20 : }
5242 :
5243 20 : void html_printer::end_page(int)
5244 : {
5245 20 : flush_sbuf();
5246 20 : flush_page();
5247 20 : }
5248 :
5249 18 : font *html_printer::make_font(const char *nm)
5250 : {
5251 18 : return html_font::load_html_font(nm);
5252 : }
5253 :
5254 17 : void html_printer::do_body (void)
5255 : {
5256 17 : if (0 /* nullptr */ == background)
5257 16 : fputs("<body>\n\n", stdout);
5258 : else {
5259 : char buf[(INT_HEXDIGITS * 3) + 1];
5260 : unsigned int r, g, b;
5261 :
5262 1 : background->get_rgb(&r, &g, &b);
5263 : // we have to scale 0..0xFFFF to 0..0xFF
5264 1 : sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101);
5265 :
5266 1 : fputs("<body bgcolor=\"#", stdout);
5267 1 : fputs(buf, stdout);
5268 1 : fputs("\">\n\n", stdout);
5269 : }
5270 17 : }
5271 :
5272 : /*
5273 : * emit_link - generates: <a href="to">name</a>
5274 : */
5275 :
5276 168 : void html_printer::emit_link (const string &to, const char *name)
5277 : {
5278 168 : assert(name != 0 /* nullptr */);
5279 168 : fputs("<a href=\"", stdout);
5280 168 : fputs(to.contents(), stdout);
5281 168 : fputs("\">", stdout);
5282 168 : fputs(name, stdout);
5283 168 : fputs("</a>", stdout);
5284 168 : }
5285 :
5286 : /*
5287 : * write_navigation - writes out the links which navigate between
5288 : * file fragments.
5289 : */
5290 :
5291 80 : void html_printer::write_navigation (const string &top,
5292 : const string &prev,
5293 : const string &next,
5294 : const string ¤t)
5295 : {
5296 80 : int need_bar = FALSE;
5297 :
5298 80 : if (multiple_files) {
5299 80 : current_paragraph->done_para();
5300 80 : write_rule();
5301 80 : if (groff_sig)
5302 0 : fputs("\n\n<table width=\"100%\" border=\"0\" rules=\"none\"\n"
5303 : "frame=\"void\" cellspacing=\"1\" cellpadding=\"0\">\n"
5304 : "<colgroup><col class=\"left\"></col>"
5305 : "<col class=\"right\"></col></colgroup>\n"
5306 : "<tr><td class=\"left\">", stdout);
5307 80 : handle_valid_flag(FALSE);
5308 80 : fputs("[ ", stdout);
5309 80 : if ((strcmp(prev.contents(), "") != 0)
5310 46 : && prev != top
5311 126 : && prev != current) {
5312 44 : emit_link(prev, "prev");
5313 44 : need_bar = TRUE;
5314 : }
5315 80 : if ((strcmp(next.contents(), "") != 0)
5316 44 : && next != top
5317 124 : && next != current) {
5318 44 : if (need_bar)
5319 42 : fputs(" | ", stdout);
5320 44 : emit_link(next, "next");
5321 44 : need_bar = TRUE;
5322 : }
5323 80 : if (top != "<standard input>"
5324 80 : && (strcmp(top.contents(), "") != 0)
5325 160 : && top != current) {
5326 80 : if (need_bar)
5327 46 : fputs(" | ", stdout);
5328 80 : emit_link(top, "top");
5329 : }
5330 80 : fputs(" ]\n", stdout);
5331 80 : if (groff_sig) {
5332 0 : fputs("</td><td class=\"right\"><i><small>"
5333 : "This document was produced using "
5334 : "<a href=\"http://www.gnu.org/software/groff/\">"
5335 : "groff-", stdout);
5336 0 : fputs(Version_string, stdout);
5337 0 : fputs("</a>.</small></i></td></tr></table>\n", stdout);
5338 : }
5339 80 : write_rule();
5340 : }
5341 80 : }
5342 :
5343 : /*
5344 : * do_file_components - scan the file list copying each temporary file
5345 : * in turn. This has twofold use: firstly to emit
5346 : * section heading links, between file fragments
5347 : * if required and secondly to generate jobname
5348 : * file fragments if required.
5349 : */
5350 :
5351 17 : void html_printer::do_file_components (void)
5352 : {
5353 17 : int fragment_no = 1;
5354 34 : string top;
5355 34 : string prev;
5356 34 : string next;
5357 34 : string current;
5358 :
5359 17 : file_list.start_of_list();
5360 17 : top = string(job_name);
5361 17 : if (xhtml == dialect)
5362 0 : top += string(".xhtml");
5363 : else
5364 17 : top += string(".html");
5365 17 : top += '\0';
5366 17 : next = file_list.next_file_name();
5367 17 : next += '\0';
5368 17 : current = next;
5369 92 : while (file_list.get_file() != 0 /* nullptr */) {
5370 75 : if (fseek(file_list.get_file(), 0L, SEEK_SET) < 0)
5371 0 : fatal("unable to seek within temporary file: %1",
5372 0 : strerror(errno));
5373 75 : html.copy_file(file_list.get_file());
5374 75 : fclose(file_list.get_file());
5375 75 : file_list.move_next();
5376 75 : if (file_list.is_new_output_file()) {
5377 : struct tm *t;
5378 :
5379 40 : if (fragment_no > 1)
5380 38 : write_navigation(top, prev, next, current);
5381 40 : prev = current;
5382 40 : current = next;
5383 40 : next = file_list.next_file_name();
5384 40 : next += '\0';
5385 80 : string split_file = file_list.file_name();
5386 40 : split_file += '\0';
5387 40 : fflush(stdout);
5388 40 : if (!freopen(split_file.contents(), "w", stdout)) {
5389 0 : fatal("unable to reopen standard output stream: %1",
5390 0 : strerror(errno));
5391 : }
5392 40 : fragment_no++;
5393 40 : if (xhtml == dialect)
5394 0 : writeHeadMetaStyle();
5395 :
5396 40 : if (do_write_creator_comment) {
5397 40 : html.begin_comment("Creator : ")
5398 40 : .put_string("groff ")
5399 40 : .put_string("version ")
5400 80 : .put_string(Version_string)
5401 40 : .end_comment();
5402 : }
5403 :
5404 40 : if (do_write_date_comment) {
5405 40 : t = current_time();
5406 40 : html.begin_comment("CreationDate: ")
5407 40 : .put_string(asctime(t), strlen(asctime(t))-1)
5408 40 : .end_comment();
5409 : }
5410 :
5411 40 : if (html4 == dialect)
5412 40 : writeHeadMetaStyle();
5413 :
5414 40 : html.put_string("<title>");
5415 40 : html.put_string(split_file.contents());
5416 40 : html.put_string("</title>").nl().nl();
5417 :
5418 40 : fputs(head_info.contents(), stdout);
5419 40 : fputs("</head>\n", stdout);
5420 40 : write_navigation(top, prev, next, current);
5421 : }
5422 75 : if (file_list.are_links_required())
5423 18 : header.write_headings(stdout, TRUE);
5424 : }
5425 17 : if (fragment_no > 1)
5426 2 : write_navigation(top, prev, next, current);
5427 : else {
5428 15 : assert(current_paragraph != 0 /* nullptr */);
5429 15 : current_paragraph->done_para();
5430 15 : write_rule();
5431 15 : if (valid_flag) {
5432 0 : if (groff_sig)
5433 0 : fputs("\n\n<table width=\"100%\" border=\"0\" rules=\"none\"\n"
5434 : "frame=\"void\" cellspacing=\"1\" cellpadding=\"0\">\n"
5435 : "<colgroup><col class=\"left\"></col>"
5436 : "<col class=\"right\"></col></colgroup>\n"
5437 : "<tr><td class=\"left\">", stdout);
5438 0 : handle_valid_flag(TRUE);
5439 0 : if (groff_sig) {
5440 0 : fputs("</td><td class=\"right\"><i><small>"
5441 : "This document was produced using "
5442 : "<a href=\"http://www.gnu.org/software/groff/\">"
5443 : "groff-", stdout);
5444 0 : fputs(Version_string, stdout);
5445 0 : fputs("</a>.</small></i></td></tr></table>\n", stdout);
5446 : }
5447 0 : write_rule();
5448 : }
5449 : }
5450 17 : }
5451 :
5452 : /*
5453 : * writeHeadMetaStyle - emits the <head> <meta> and <style> tags and
5454 : * related information.
5455 : */
5456 :
5457 57 : void html_printer::writeHeadMetaStyle (void)
5458 : {
5459 57 : if (html4 == dialect) {
5460 57 : fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional"
5461 : "//EN\"\n", stdout);
5462 57 : fputs("\"http://www.w3.org/TR/html4/loose.dtd\">\n", stdout);
5463 57 : fputs("<html>\n", stdout);
5464 57 : fputs("<head>\n", stdout);
5465 57 : fputs("<meta name=\"generator\" "
5466 : "content=\"groff -Thtml, see www.gnu.org\">\n", stdout);
5467 57 : fputs("<meta http-equiv=\"Content-Type\" "
5468 : "content=\"text/html; charset=", stdout);
5469 57 : fputs(static_cast<bool>(charset_encoding)
5470 : ? "UTF-8" : "US-ASCII", stdout);
5471 57 : fputs("\">\n", stdout);
5472 57 : fputs("<meta name=\"Content-Style\" content=\"text/css\">\n",
5473 : stdout);
5474 57 : fputs("<style type=\"text/css\">\n", stdout);
5475 : }
5476 : else {
5477 0 : fputs("<?xml version=\"1.0\" encoding=\"", stdout);
5478 0 : fputs(static_cast<bool>(charset_encoding)
5479 : ? "UTF-8" : "us-ascii", stdout);
5480 0 : fputs("\"?>\n", stdout);
5481 0 : fputs("<!DOCTYPE html PUBLIC \"-//W3C//"
5482 : "DTD XHTML 1.1 plus MathML 2.0//EN\"\n", stdout);
5483 0 : fputs(" \"http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd\"\n",
5484 : stdout);
5485 0 : fputs(" [<!ENTITY mathml \"http://www.w3.org/1998/Math/"
5486 : "MathML\">]>\n", stdout);
5487 :
5488 0 : fputs("<html xmlns=\"http://www.w3.org/1999/xhtml\" "
5489 : "xml:lang=\"en\">\n", stdout);
5490 0 : fputs("<head>\n", stdout);
5491 0 : fputs("<meta name=\"generator\" "
5492 : "content=\"groff -Txhtml, see www.gnu.org\"/>\n", stdout);
5493 0 : fputs("<meta http-equiv=\"Content-Type\" "
5494 : "content=\"text/html; charset=", stdout);
5495 0 : fputs(static_cast<bool>(charset_encoding)
5496 : ? "UTF-8" : "US-ASCII", stdout);
5497 0 : fputs("\"/>\n", stdout);
5498 0 : fputs("<meta name=\"Content-Style\" content=\"text/css\"/>\n",
5499 : stdout);
5500 0 : fputs("<style type=\"text/css\">\n", stdout);
5501 0 : fputs(" .center { text-align: center }\n", stdout);
5502 0 : fputs(" .right { text-align: right }\n", stdout);
5503 : }
5504 57 : fputs(" p { margin-top: 0; margin-bottom: 0; "
5505 : "vertical-align: top }\n", stdout);
5506 57 : fputs(" pre { margin-top: 0; margin-bottom: 0; "
5507 : "vertical-align: top }\n", stdout);
5508 57 : fputs(" table { margin-top: 0; margin-bottom: 0; "
5509 : "vertical-align: top }\n", stdout);
5510 57 : fputs(" h1 { text-align: center }\n", stdout);
5511 57 : fputs("</style>\n", stdout);
5512 57 : }
5513 :
5514 34 : html_printer::~html_printer()
5515 : {
5516 : struct tm *t;
5517 :
5518 17 : current_lineno = 0; // At this point, we've read all the input.
5519 :
5520 17 : if (current_paragraph)
5521 17 : current_paragraph->flush_text();
5522 17 : html.end_line();
5523 17 : html.set_file(stdout);
5524 :
5525 17 : if (xhtml == dialect)
5526 0 : writeHeadMetaStyle();
5527 :
5528 17 : if (do_write_creator_comment) {
5529 15 : html.begin_comment("Creator : ")
5530 15 : .put_string("groff ")
5531 15 : .put_string("version ")
5532 30 : .put_string(Version_string)
5533 15 : .end_comment();
5534 : }
5535 :
5536 17 : if (do_write_date_comment) {
5537 15 : t = current_time();
5538 15 : html.begin_comment("CreationDate: ")
5539 15 : .put_string(asctime(t), strlen(asctime(t))-1)
5540 15 : .end_comment();
5541 : }
5542 :
5543 17 : if (html4 == dialect)
5544 17 : writeHeadMetaStyle();
5545 :
5546 17 : write_title(TRUE);
5547 17 : head_info += '\0';
5548 17 : fputs(head_info.contents(), stdout);
5549 17 : fputs("</head>\n", stdout);
5550 17 : do_body();
5551 :
5552 17 : write_title(FALSE);
5553 17 : header.write_headings(stdout, FALSE);
5554 17 : write_rule();
5555 : #if defined(DEBUGGING)
5556 : html.begin_comment("Total number of pages: ")
5557 : .put_string(i_to_a(no_of_printed_pages)).end_comment();
5558 : #endif
5559 17 : html.end_line();
5560 17 : html.end_line();
5561 :
5562 17 : if (multiple_files) {
5563 2 : fputs("</body>\n", stdout);
5564 2 : fputs("</html>\n", stdout);
5565 2 : do_file_components();
5566 : } else {
5567 15 : do_file_components();
5568 15 : fputs("</body>\n", stdout);
5569 15 : fputs("</html>\n", stdout);
5570 : }
5571 34 : }
5572 :
5573 : /*
5574 : * get_str - returns a duplicate of string, s. The duplicate
5575 : * string is terminated at the next ',' or ']'.
5576 : */
5577 :
5578 0 : static char *get_str (const char *s, char **n)
5579 : {
5580 0 : assert(s != 0 /* nullptr */);
5581 0 : assert(n != 0 /* nullptr */);
5582 0 : int i = 0;
5583 : char *v;
5584 :
5585 0 : while ((s[i] != '\0') && (s[i] != ',') && (s[i] != ']'))
5586 0 : i++;
5587 0 : if (i>0) {
5588 0 : v = new char[i+1];
5589 0 : memcpy(v, s, i+1);
5590 0 : v[i] = '\0';
5591 0 : if (',' == s[i])
5592 0 : (*n) = const_cast<char *>(&s[i+1]);
5593 : else
5594 0 : (*n) = const_cast<char *>(&s[i]);
5595 0 : return v;
5596 : }
5597 0 : if (',' == s[i])
5598 0 : (*n) = const_cast<char *>(&s[1]);
5599 : else
5600 0 : (*n) = const_cast<char *>(s);
5601 0 : return 0;
5602 : }
5603 :
5604 : /*
5605 : * make_val - creates a string from `v` if `s` is a null pointer.
5606 : */
5607 :
5608 0 : char *make_val (char *s, int v, char *id, char *f, char *l)
5609 : {
5610 0 : assert(id != 0 /* nullptr */);
5611 0 : assert(f != 0 /* nullptr */);
5612 0 : assert(l != 0 /* nullptr */);
5613 0 : if (0 /* nullptr */ == s) {
5614 : char buf[30];
5615 :
5616 0 : sprintf(buf, "%d", v);
5617 0 : return strsave(buf);
5618 : }
5619 : else {
5620 : /*
5621 : * check that value, s, is the same as, v.
5622 : */
5623 0 : char *t = s;
5624 :
5625 0 : while ('=' == *t)
5626 0 : t++;
5627 0 : if (atoi(t) != v) {
5628 0 : if (0 /* nullptr */ == f)
5629 0 : f = const_cast<char *>("stdin");
5630 0 : if (0 /* nullptr */ == l)
5631 0 : f = const_cast<char *>("<none>");
5632 0 : fprintf(stderr, "%s:%s:%s: assertion failed at id%s;"
5633 : " expected %d, got %s\n", program_name, f, l, id, v, s);
5634 : }
5635 0 : return s;
5636 : }
5637 : }
5638 :
5639 : /*
5640 : * handle_assertion - handles the assertions created via .www:ASSERT
5641 : * in www.tmac. See www.tmac for examples. This
5642 : * method should be called as we are parsing the
5643 : * ditroff input. It checks the x, y position
5644 : * assertions. It does _not_ check the troff state
5645 : * assertions as these are unknown at this point.
5646 : */
5647 :
5648 0 : void html_printer::handle_assertion (int minv, int minh,
5649 : int maxv, int maxh, const char *s)
5650 : {
5651 : char *n;
5652 0 : char *cmd = get_str(s, &n);
5653 0 : char *id = get_str(n, &n);
5654 0 : char *val = get_str(n, &n);
5655 0 : char *file= get_str(n, &n);
5656 0 : char *line= get_str(n, &n);
5657 :
5658 0 : if (strcmp(cmd, "assertion:[x") == 0)
5659 0 : as.addx(cmd, id, make_val(val, minh, id, file, line), file, line);
5660 0 : else if (strcmp(cmd, "assertion:[y") == 0)
5661 0 : as.addy(cmd, id, make_val(val, minv, id, file, line), file, line);
5662 : else
5663 0 : if (strncmp(cmd, "assertion:[", strlen("assertion:[")) == 0)
5664 0 : page_contents->add_tag(&sbuf_style, string(s),
5665 : line_number, minv, minh, maxv, maxh);
5666 0 : }
5667 :
5668 : /*
5669 : * build_state_assertion - builds the troff state assertions.
5670 : */
5671 :
5672 101971 : void html_printer::handle_state_assertion (text_glob *g)
5673 : {
5674 101971 : if ((g != 0 /* nullptr */)
5675 101957 : && g->is_a_tag()
5676 203928 : && (strncmp(g->text_string, "assertion:[", 11) == 0)) {
5677 0 : char *n = const_cast<char *>(&g->text_string[11]);
5678 0 : char *cmd = get_str(n, &n);
5679 0 : char *val = get_str(n, &n);
5680 0 : (void)get_str(n, &n); // unused
5681 0 : char *file= get_str(n, &n);
5682 0 : char *line= get_str(n, &n);
5683 :
5684 0 : as.build(cmd, val, file, line);
5685 : }
5686 101971 : }
5687 :
5688 : /*
5689 : * special - handle all x X requests from troff. For post-html they
5690 : * allow users to pass raw HTML commands, turn auto linked
5691 : * headings off/on, and so forth.
5692 : */
5693 :
5694 847 : void html_printer::special(char *s, const environment *env, char type)
5695 : {
5696 847 : assert(env != 0 /* nullptr */);
5697 847 : if (type != 'p')
5698 0 : return;
5699 847 : if (s != 0 /* nullptr */) {
5700 847 : flush_sbuf();
5701 847 : if (env->fontno >= 0) {
5702 847 : style sty(get_font_from_index(env->fontno), env->size,
5703 847 : env->height, env->slant, env->fontno, *env->col);
5704 847 : sbuf_style = sty;
5705 : }
5706 :
5707 847 : if (strncmp(s, "html:", 5) == 0) {
5708 563 : int r=font::res; /* resolution of the device */
5709 563 : font *f=sbuf_style.f;
5710 :
5711 563 : if (0 /* nullptr */ == f)
5712 0 : f = font::load_font("TR");
5713 :
5714 : /*
5715 : * pass rest of string through to html output during flush
5716 : */
5717 563 : page_contents->add_and_encode(&sbuf_style, string(&s[5]),
5718 : line_number,
5719 563 : env->vpos-env->size*r/72, env->hpos,
5720 563 : env->vpos , env->hpos,
5721 : FALSE);
5722 :
5723 : /*
5724 : * assume that the html command has no width, if it does then
5725 : * hopefully troff will have fudged this in a macro by requesting
5726 : * that the formatting move right by the appropriate amount.
5727 : */
5728 284 : } else if ((strncmp(s, "html</p>:", 9) == 0)
5729 1 : || (strncmp(s, "html<?p>:", 9) == 0)
5730 1 : || (strncmp(s, "math<?p>:", 9) == 0)) {
5731 283 : int r=font::res; /* resolution of the device */
5732 283 : font *f=sbuf_style.f;
5733 283 : string t;
5734 :
5735 283 : if (0 /* nullptr */ == f)
5736 0 : f = font::load_font("TR");
5737 :
5738 283 : if (strncmp(s, "math<?p>:", 9) == 0) {
5739 0 : if (strncmp(&s[9], "<math>", 6) == 0) {
5740 0 : s[9] = '\0';
5741 0 : t = s;
5742 0 : t += "<math xmlns=\"http://www.w3.org/1998/Math/MathML\">";
5743 0 : t += &s[15];
5744 0 : t += '\0';
5745 0 : s = &t[0];
5746 : }
5747 : }
5748 :
5749 : /*
5750 : * need to pass all of string through to html output during flush
5751 : */
5752 283 : page_contents->add_and_encode(&sbuf_style, string(s),
5753 : line_number,
5754 283 : env->vpos-env->size*r/72, env->hpos,
5755 283 : env->vpos , env->hpos,
5756 283 : TRUE);
5757 :
5758 : /*
5759 : * assume that the html command has no width, if it does then
5760 : * hopefully troff will have fudged this in a macro by
5761 : * requesting that the formatting move right by the appropriate
5762 : * amount.
5763 : */
5764 :
5765 1 : } else if (strncmp(s, "index:", 6) == 0) {
5766 1 : cutoff_heading = atoi(&s[6]);
5767 0 : } else if (strncmp(s, "assertion:[", 11) == 0) {
5768 0 : int r=font::res; /* resolution of the device */
5769 :
5770 0 : handle_assertion(env->vpos-env->size*r/72, env->hpos,
5771 0 : env->vpos, env->hpos, s);
5772 : }
5773 : }
5774 : }
5775 :
5776 : /*
5777 : * devtag - handles device troff tags sent from the 'troff'.
5778 : * These include the troff state machine tags:
5779 : * .br, .sp, .in, .tl, .ll etc
5780 : *
5781 : * (see man 5 grohtml_tags).
5782 : */
5783 :
5784 4460 : void html_printer::devtag (char *s, const environment *env, char type)
5785 : {
5786 4460 : assert(env != 0 /* nullptr */);
5787 4460 : if (type != 'p')
5788 0 : return;
5789 :
5790 4460 : if (s != 0 /* nullptr */) {
5791 4460 : flush_sbuf();
5792 4460 : if (env->fontno >= 0) {
5793 4363 : style sty(get_font_from_index(env->fontno), env->size,
5794 4363 : env->height, env->slant, env->fontno, *env->col);
5795 4363 : sbuf_style = sty;
5796 : }
5797 :
5798 4460 : if (strncmp(s, "devtag:", strlen("devtag:")) == 0) {
5799 4460 : int r=font::res; /* resolution of the device */
5800 :
5801 4460 : page_contents->add_tag(&sbuf_style, string(s),
5802 : line_number,
5803 4460 : env->vpos-env->size*r/72, env->hpos,
5804 4460 : env->vpos , env->hpos);
5805 : }
5806 : }
5807 : }
5808 :
5809 :
5810 : /*
5811 : * taken from number.cpp in src/roff/troff, [hunits::hunits(units x)]
5812 : */
5813 :
5814 123 : int html_printer::round_width(int x)
5815 : {
5816 123 : int r = font::hor;
5817 : int n;
5818 :
5819 : // don't depend on rounding direction for division of negative ints
5820 123 : if (1 == r)
5821 0 : n = x;
5822 : else
5823 123 : n = (x < 0
5824 123 : ? -((-x + r/2 - 1)/r)
5825 123 : : (x + r/2 - 1)/r);
5826 123 : return n * r;
5827 : }
5828 :
5829 : /*
5830 : * handle_valid_flag - emits a valid XHTML 1.1 or HTML 4.01 button,
5831 : * provided -V was supplied on the command line.
5832 : */
5833 :
5834 80 : void html_printer::handle_valid_flag (int needs_para)
5835 : {
5836 80 : if (valid_flag) {
5837 0 : if (needs_para)
5838 0 : fputs("<p>", stdout);
5839 0 : if (xhtml == dialect)
5840 0 : fputs("<a href=\"http://validator.w3.org/check?uri=referer\">"
5841 : "<img src=\"http://www.w3.org/Icons/valid-xhtml11-blue\" "
5842 : "alt=\"Valid XHTML 1.1 Transitional\" "
5843 : "height=\"31\" width=\"88\" /></a>\n", stdout);
5844 : else
5845 0 : fputs("<a href=\"http://validator.w3.org/check?uri=referer\">"
5846 : "<img src=\"http://www.w3.org/Icons/valid-html401-blue\" "
5847 : "alt=\"Valid HTML 4.01 Transitional\" "
5848 : "height=\"31\" width=\"88\"></a>\n", stdout);
5849 0 : if (needs_para)
5850 0 : fputs("</p>", stdout);
5851 : }
5852 80 : }
5853 :
5854 17 : int main(int argc, char **argv)
5855 : {
5856 17 : program_name = argv[0];
5857 : static char stderr_buf[BUFSIZ];
5858 17 : setbuf(stderr, stderr_buf);
5859 : int c;
5860 : static const struct option long_options[] = {
5861 : { "help", no_argument, 0 /* nullptr */, CHAR_MAX + 1 },
5862 : { "version", no_argument, 0 /* nullptr */, 'v' },
5863 : { 0 /* nullptr */, 0, 0, 0 }
5864 : };
5865 31 : while ((c = getopt_long(argc, argv, ":a:bCdD:eF:g:Ghi:I:j:k:l"
5866 : "no:prs:S:vVx:y",
5867 : long_options, 0 /* nullptr */))
5868 31 : != EOF)
5869 14 : switch (c) {
5870 0 : case 'a':
5871 : /* text antialiasing bits - handled by pre-html */
5872 0 : break;
5873 1 : case 'b':
5874 : // set background color to white
5875 1 : default_background = new color;
5876 1 : default_background->set_gray(color::MAX_COLOR_VAL);
5877 1 : break;
5878 2 : case 'C':
5879 : // Don't write CreationDate HTML comments.
5880 2 : do_write_date_comment = FALSE;
5881 2 : break;
5882 0 : case 'd':
5883 : /* handled by pre-html */
5884 0 : break;
5885 2 : case 'D':
5886 : /* handled by pre-html */
5887 2 : break;
5888 0 : case 'e':
5889 : /* handled by pre-html */
5890 0 : break;
5891 0 : case 'F':
5892 0 : font::command_line_font_dir(optarg);
5893 0 : break;
5894 0 : case 'g':
5895 : /* graphic antialiasing bits - handled by pre-html */
5896 0 : break;
5897 2 : case 'G':
5898 : // Don't write Creator HTML comments.
5899 2 : do_write_creator_comment = FALSE;
5900 2 : break;
5901 0 : case 'h':
5902 : /* do not use the Hn headings of html, but manufacture our own */
5903 0 : manufacture_headings = TRUE;
5904 0 : break;
5905 0 : case 'i':
5906 : /* handled by pre-html */
5907 0 : break;
5908 3 : case 'I':
5909 : /* handled by pre-html */
5910 3 : break;
5911 2 : case 'j':
5912 2 : multiple_files = TRUE;
5913 2 : job_name = optarg;
5914 2 : break;
5915 0 : case 'k':
5916 0 : if (strcasecmp(optarg, "ascii") == 0)
5917 0 : charset_encoding = CHARSET_ASCII;
5918 0 : else if (strcasecmp(optarg, "mixed") == 0)
5919 0 : charset_encoding = CHARSET_MIXED;
5920 0 : else if ((strcasecmp(optarg, "utf8") == 0)
5921 0 : || (strcasecmp(optarg, "utf-8") == 0))
5922 0 : charset_encoding = CHARSET_UTF8;
5923 : else {
5924 0 : warning("unsupported character encoding '%1'; assuming UTF-8",
5925 0 : optarg);
5926 0 : charset_encoding = CHARSET_UTF8;
5927 : }
5928 0 : break;
5929 0 : case 'l':
5930 0 : auto_links = FALSE;
5931 0 : break;
5932 1 : case 'n':
5933 1 : simple_anchors = TRUE;
5934 1 : break;
5935 0 : case 'o':
5936 : /* handled by pre-html */
5937 0 : break;
5938 0 : case 'p':
5939 : /* handled by pre-html */
5940 0 : break;
5941 1 : case 'r':
5942 1 : auto_rule = FALSE;
5943 1 : break;
5944 0 : case 's':
5945 0 : base_point_size = atoi(optarg);
5946 0 : break;
5947 0 : case 'S':
5948 0 : split_level = atoi(optarg) + 1;
5949 0 : break;
5950 0 : case 'v':
5951 0 : printf("GNU post-grohtml (groff) version %s\n", Version_string);
5952 0 : exit(EXIT_SUCCESS);
5953 : break;
5954 0 : case 'V':
5955 0 : valid_flag = TRUE;
5956 0 : break;
5957 0 : case 'x':
5958 0 : if (strcmp(optarg, "x") == 0) {
5959 0 : dialect = xhtml;
5960 0 : simple_anchors = TRUE;
5961 0 : } else if (strcmp(optarg, "4") == 0)
5962 0 : dialect = html4;
5963 : else
5964 0 : warning("unsupported HTML dialect: '%1'", optarg);
5965 0 : break;
5966 0 : case 'y':
5967 0 : groff_sig = TRUE;
5968 0 : break;
5969 0 : case CHAR_MAX + 1: // --help
5970 0 : usage(stdout);
5971 0 : exit(EXIT_SUCCESS);
5972 : break;
5973 0 : case '?':
5974 0 : if (optopt != 0)
5975 0 : error("unrecognized command-line option '%1'", char(optopt));
5976 : else
5977 0 : error("unrecognized command-line option '%1'",
5978 0 : argv[(optind - 1)]);
5979 0 : usage(stderr);
5980 0 : exit(2);
5981 : break;
5982 0 : case ':':
5983 0 : error("command-line option '%1' requires an argument",
5984 0 : char(optopt));
5985 0 : usage(stderr);
5986 0 : exit(2);
5987 : break;
5988 0 : default:
5989 0 : assert(0 == "unhandled getopt_long return value");
5990 : }
5991 17 : if (optind >= argc) {
5992 17 : do_file("-");
5993 : } else {
5994 0 : for (int i = optind; i < argc; i++)
5995 0 : do_file(argv[i]);
5996 : }
5997 17 : return 0;
5998 : }
5999 :
6000 0 : static void usage(FILE *stream)
6001 : {
6002 0 : assert(stream != 0 /* nullptr */);
6003 0 : fprintf(stream,
6004 : "usage: %s [-bCGhlnrVy] [-F font-directory] [-j output-stem]"
6005 : " [-k encoding] [-s base-type-size] [-S heading-level]"
6006 : " [-x html-dialect] [file ...]\n"
6007 : "usage: %s {-v | --version}\n"
6008 : "usage: %s --help\n",
6009 : program_name, program_name, program_name);
6010 0 : if (stdout == stream)
6011 0 : fputs("\n"
6012 : "Translate the output of troff(1) into (X)HTML. See the grohtml(1)\n"
6013 : "manual page.\n", stream);
6014 0 : }
6015 :
6016 : // Local Variables:
6017 : // fill-column: 72
6018 : // mode: C++
6019 : // End:
6020 : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
|