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