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