1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005
3151497Sru * Free Software Foundation, Inc.
4114402Sru *
5114402Sru *  Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cpp
6114402Sru *  but it owes a huge amount of ideas and raw code from
7114402Sru *  James Clark (jjc@jclark.com) grops/ps.cpp.
8114402Sru */
9114402Sru
10114402Sru/*
11114402SruThis file is part of groff.
12114402Sru
13114402Srugroff is free software; you can redistribute it and/or modify it under
14114402Sruthe terms of the GNU General Public License as published by the Free
15114402SruSoftware Foundation; either version 2, or (at your option) any later
16114402Sruversion.
17114402Sru
18114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
19114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
20114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21114402Srufor more details.
22114402Sru
23114402SruYou should have received a copy of the GNU General Public License along
24114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
25151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
26114402Sru
27114402Sru#include "driver.h"
28114402Sru#include "stringclass.h"
29114402Sru#include "cset.h"
30114402Sru#include "html.h"
31114402Sru#include "html-text.h"
32114402Sru#include "html-table.h"
33114402Sru
34114402Sru#include <time.h>
35114402Sru
36114402Sru#ifdef HAVE_UNISTD_H
37114402Sru#include <unistd.h>
38114402Sru#endif
39114402Sru
40114402Sru#include <stdio.h>
41114402Sru#include <fcntl.h>
42151497Sru#include <string.h>
43114402Sru
44114402Sruextern "C" const char *Version_string;
45114402Sru
46114402Sru#if !defined(TRUE)
47114402Sru#   define TRUE  (1==1)
48114402Sru#endif
49114402Sru#if !defined(FALSE)
50114402Sru#   define FALSE (1==0)
51114402Sru#endif
52114402Sru
53114402Sru#define MAX_LINE_LENGTH                60            /* maximum characters we want in a line      */
54114402Sru#define SIZE_INCREMENT                  2            /* font size increment <big> = +2            */
55151497Sru#define CENTER_TOLERANCE                2            /* how many pixels off center do we allow    */
56114402Sru#define ANCHOR_TEMPLATE         "heading"            /* if simple anchor is set we use this       */
57114402Sru#define UNICODE_DESC_START           0x80            /* all character entities above this are     */
58114402Sru                                                     /* either encoded by their glyph names or if */
59114402Sru                                                     /* there is no name then we use &#nnn;       */
60114402Srutypedef enum {CENTERED, LEFT, RIGHT, INLINE} TAG_ALIGNMENT;
61114402Srutypedef enum {col_tag, tab_tag, tab0_tag, none} colType;
62114402Sru
63114402Sru#undef DEBUG_TABLES
64151497Sru// #define DEBUG_TABLES
65114402Sru
66114402Sru/*
67114402Sru *  prototypes
68114402Sru */
69114402Sru
70114402Sruchar *get_html_translation (font *f, const string &name);
71114402Sruint char_translate_to_html (font *f, char *buf, int buflen, unsigned char ch, int b, int and_single);
72114402Sru
73114402Sru
74114402Srustatic int auto_links = TRUE;                        /* by default we enable automatic links at  */
75114402Sru                                                     /* top of the document.                     */
76114402Srustatic int auto_rule  = TRUE;                        /* by default we enable an automatic rule   */
77114402Sru                                                     /* at the top and bottom of the document    */
78114402Srustatic int simple_anchors = FALSE;                   /* default to anchors with heading text     */
79114402Srustatic int manufacture_headings = FALSE;             /* default is to use the Hn html headings,  */
80114402Sru                                                     /* rather than manufacture our own.         */
81114402Srustatic color *default_background = NULL;             /* has user requested initial bg color?     */
82151497Srustatic string job_name;                              /* if set then the output is split into     */
83151497Sru                                                     /* multiple files with `job_name'-%d.html   */
84151497Srustatic int multiple_files = FALSE;                   /* must we the output be divided into       */
85151497Sru                                                     /* multiple html files, one for each        */
86151497Sru                                                     /* heading?                                 */
87151497Srustatic int base_point_size = 0;                      /* which troff font size maps onto html     */
88151497Sru                                                     /* size 3?                                  */
89151497Srustatic int split_level = 2;                          /* what heading level to split at?          */
90151497Srustatic string head_info;                             /* user supplied information to be placed   */
91151497Sru                                                     /* into <head> </head>                      */
92114402Sru
93114402Sru
94114402Sru/*
95114402Sru *  start with a few favorites
96114402Sru */
97114402Sru
98114402Sruvoid stop () {}
99114402Sru
100114402Srustatic int min (int a, int b)
101114402Sru{
102114402Sru  if (a < b)
103114402Sru    return a;
104114402Sru  else
105114402Sru    return b;
106114402Sru}
107114402Sru
108114402Srustatic int max (int a, int b)
109114402Sru{
110114402Sru  if (a > b)
111114402Sru    return a;
112114402Sru  else
113114402Sru    return b;
114114402Sru}
115114402Sru
116114402Sru/*
117114402Sru *  is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
118114402Sru */
119114402Sru
120114402Srustatic int is_intersection (int a1, int a2, int b1, int b2)
121114402Sru{
122114402Sru  // easier to prove NOT outside limits
123151497Sru  return ! ((a1 > b2) || (a2 < b1));
124114402Sru}
125114402Sru
126114402Sru/*
127114402Sru *  is_digit - returns TRUE if character, ch, is a digit.
128114402Sru */
129114402Sru
130114402Srustatic int is_digit (char ch)
131114402Sru{
132151497Sru  return (ch >= '0') && (ch <= '9');
133114402Sru}
134114402Sru
135114402Sru/*
136114402Sru *  the classes and methods for maintaining a list of files.
137114402Sru */
138114402Sru
139114402Srustruct file {
140114402Sru  FILE    *fp;
141114402Sru  file    *next;
142151497Sru  int      new_output_file;
143151497Sru  int      require_links;
144151497Sru  string   output_file_name;
145114402Sru
146114402Sru  file     (FILE *f);
147114402Sru};
148114402Sru
149114402Sru/*
150114402Sru *  file - initialize all fields to NULL
151114402Sru */
152114402Sru
153114402Srufile::file (FILE *f)
154151497Sru  : fp(f), next(NULL), new_output_file(FALSE),
155151497Sru    require_links(FALSE), output_file_name("")
156114402Sru{
157114402Sru}
158114402Sru
159114402Sruclass files {
160114402Srupublic:
161151497Sru              files              ();
162151497Sru  FILE       *get_file           (void);
163151497Sru  void        start_of_list      (void);
164151497Sru  void        move_next          (void);
165151497Sru  void        add_new_file       (FILE *f);
166151497Sru  void        set_file_name      (string name);
167151497Sru  void        set_links_required (void);
168151497Sru  int         are_links_required (void);
169151497Sru  int         is_new_output_file (void);
170151497Sru  string      file_name          (void);
171151497Sru  string      next_file_name     (void);
172114402Sruprivate:
173151497Sru  file       *head;
174151497Sru  file       *tail;
175151497Sru  file       *ptr;
176114402Sru};
177114402Sru
178114402Sru/*
179114402Sru *  files - create an empty list of files.
180114402Sru */
181114402Sru
182114402Srufiles::files ()
183151497Sru  : head(NULL), tail(NULL), ptr(NULL)
184114402Sru{
185114402Sru}
186114402Sru
187114402Sru/*
188114402Sru *  get_file - returns the FILE associated with ptr.
189114402Sru */
190114402Sru
191114402SruFILE *files::get_file (void)
192114402Sru{
193151497Sru  if (ptr)
194151497Sru    return ptr->fp;
195151497Sru  else
196151497Sru    return NULL;
197114402Sru}
198114402Sru
199114402Sru/*
200114402Sru *  start_of_list - reset the ptr to the start of the list.
201114402Sru */
202114402Sru
203114402Sruvoid files::start_of_list (void)
204114402Sru{
205114402Sru  ptr = head;
206114402Sru}
207114402Sru
208114402Sru/*
209114402Sru *  move_next - moves the ptr to the next element on the list.
210114402Sru */
211114402Sru
212114402Sruvoid files::move_next (void)
213114402Sru{
214151497Sru  if (ptr != NULL)
215114402Sru    ptr = ptr->next;
216114402Sru}
217114402Sru
218114402Sru/*
219114402Sru *  add_new_file - adds a new file, f, to the list.
220114402Sru */
221114402Sru
222114402Sruvoid files::add_new_file (FILE *f)
223114402Sru{
224151497Sru  if (head == NULL) {
225114402Sru    head = new file(f);
226114402Sru    tail = head;
227114402Sru  } else {
228114402Sru    tail->next = new file(f);
229114402Sru    tail       = tail->next;
230114402Sru  }
231114402Sru  ptr = tail;
232114402Sru}
233114402Sru
234114402Sru/*
235151497Sru *  set_file_name - sets the final file name to contain the html
236151497Sru *                  data to name.
237151497Sru */
238151497Sru
239151497Sruvoid files::set_file_name (string name)
240151497Sru{
241151497Sru  if (ptr != NULL) {
242151497Sru    ptr->output_file_name = name;
243151497Sru    ptr->new_output_file = TRUE;
244151497Sru  }
245151497Sru}
246151497Sru
247151497Sru/*
248151497Sru *  set_links_required - issue links when processing this component
249151497Sru *                       of the file.
250151497Sru */
251151497Sru
252151497Sruvoid files::set_links_required (void)
253151497Sru{
254151497Sru  if (ptr != NULL)
255151497Sru    ptr->require_links = TRUE;
256151497Sru}
257151497Sru
258151497Sru/*
259151497Sru *  are_links_required - returns TRUE if this section of the file
260151497Sru *                       requires that links should be issued.
261151497Sru */
262151497Sru
263151497Sruint files::are_links_required (void)
264151497Sru{
265151497Sru  if (ptr != NULL)
266151497Sru    return ptr->require_links;
267151497Sru  return FALSE;
268151497Sru}
269151497Sru
270151497Sru/*
271151497Sru *  is_new_output_file - returns TRUE if this component of the file
272151497Sru *                       is the start of a new output file.
273151497Sru */
274151497Sru
275151497Sruint files::is_new_output_file (void)
276151497Sru{
277151497Sru  if (ptr != NULL)
278151497Sru    return ptr->new_output_file;
279151497Sru  return FALSE;
280151497Sru}
281151497Sru
282151497Sru/*
283151497Sru *  file_name - returns the name of the file.
284151497Sru */
285151497Sru
286151497Srustring files::file_name (void)
287151497Sru{
288151497Sru  if (ptr != NULL)
289151497Sru    return ptr->output_file_name;
290151497Sru  return string("");
291151497Sru}
292151497Sru
293151497Sru/*
294151497Sru *  next_file_name - returns the name of the next file.
295151497Sru */
296151497Sru
297151497Srustring files::next_file_name (void)
298151497Sru{
299151497Sru  if (ptr != NULL && ptr->next != NULL)
300151497Sru    return ptr->next->output_file_name;
301151497Sru  return string("");
302151497Sru}
303151497Sru
304151497Sru/*
305114402Sru *  the class and methods for styles
306114402Sru */
307114402Sru
308114402Srustruct style {
309114402Sru  font        *f;
310114402Sru  int          point_size;
311114402Sru  int          font_no;
312114402Sru  int          height;
313114402Sru  int          slant;
314114402Sru  color        col;
315114402Sru               style       ();
316114402Sru               style       (font *, int, int, int, int, color);
317114402Sru  int          operator == (const style &) const;
318114402Sru  int          operator != (const style &) const;
319114402Sru};
320114402Sru
321114402Srustyle::style()
322151497Sru  : f(NULL)
323114402Sru{
324114402Sru}
325114402Sru
326114402Srustyle::style(font *p, int sz, int h, int sl, int no, color c)
327114402Sru  : f(p), point_size(sz), font_no(no), height(h), slant(sl), col(c)
328114402Sru{
329114402Sru}
330114402Sru
331114402Sruint style::operator==(const style &s) const
332114402Sru{
333114402Sru  return (f == s.f && point_size == s.point_size
334114402Sru	  && height == s.height && slant == s.slant && col == s.col);
335114402Sru}
336114402Sru
337114402Sruint style::operator!=(const style &s) const
338114402Sru{
339114402Sru  return !(*this == s);
340114402Sru}
341114402Sru
342114402Sru/*
343114402Sru *  the class and methods for retaining ascii text
344114402Sru */
345114402Sru
346114402Srustruct char_block {
347114402Sru  enum { SIZE = 256 };
348114402Sru  char         *buffer;
349114402Sru  int           used;
350114402Sru  char_block   *next;
351114402Sru
352114402Sru  char_block();
353114402Sru  char_block(int length);
354114402Sru  ~char_block();
355114402Sru};
356114402Sru
357114402Sruchar_block::char_block()
358151497Sru: buffer(NULL), used(0), next(NULL)
359114402Sru{
360114402Sru}
361114402Sru
362114402Sruchar_block::char_block(int length)
363151497Sru: used(0), next(NULL)
364114402Sru{
365151497Sru  buffer = new char[max(length, char_block::SIZE)];
366114402Sru  if (buffer == NULL)
367114402Sru    fatal("out of memory error");
368114402Sru}
369114402Sru
370114402Sruchar_block::~char_block()
371114402Sru{
372114402Sru  if (buffer != NULL)
373151497Sru    a_delete buffer;
374114402Sru}
375114402Sru
376114402Sruclass char_buffer {
377114402Srupublic:
378114402Sru  char_buffer();
379114402Sru  ~char_buffer();
380114402Sru  char  *add_string(const char *, unsigned int);
381114402Sru  char  *add_string(const string &);
382114402Sruprivate:
383114402Sru  char_block *head;
384114402Sru  char_block *tail;
385114402Sru};
386114402Sru
387114402Sruchar_buffer::char_buffer()
388151497Sru: head(NULL), tail(NULL)
389114402Sru{
390114402Sru}
391114402Sru
392114402Sruchar_buffer::~char_buffer()
393114402Sru{
394151497Sru  while (head != NULL) {
395114402Sru    char_block *temp = head;
396114402Sru    head = head->next;
397114402Sru    delete temp;
398114402Sru  }
399114402Sru}
400114402Sru
401114402Sruchar *char_buffer::add_string (const char *s, unsigned int length)
402114402Sru{
403114402Sru  int i=0;
404114402Sru  unsigned int old_used;
405114402Sru
406114402Sru  if (s == NULL || length == 0)
407114402Sru    return NULL;
408114402Sru
409151497Sru  if (tail == NULL) {
410114402Sru    tail = new char_block(length+1);
411114402Sru    head = tail;
412114402Sru  } else {
413114402Sru    if (tail->used + length+1 > char_block::SIZE) {
414114402Sru      tail->next  = new char_block(length+1);
415114402Sru      tail        = tail->next;
416114402Sru    }
417114402Sru  }
418114402Sru
419114402Sru  old_used = tail->used;
420114402Sru  do {
421114402Sru    tail->buffer[tail->used] = s[i];
422114402Sru    tail->used++;
423114402Sru    i++;
424114402Sru    length--;
425114402Sru  } while (length>0);
426114402Sru
427114402Sru  // add terminating nul character
428114402Sru
429114402Sru  tail->buffer[tail->used] = '\0';
430114402Sru  tail->used++;
431114402Sru
432114402Sru  // and return start of new string
433114402Sru
434151497Sru  return &tail->buffer[old_used];
435114402Sru}
436114402Sru
437114402Sruchar *char_buffer::add_string (const string &s)
438114402Sru{
439114402Sru  return add_string(s.contents(), s.length());
440114402Sru}
441114402Sru
442114402Sru/*
443114402Sru *  the classes and methods for maintaining glyph positions.
444114402Sru */
445114402Sru
446114402Sruclass text_glob {
447114402Srupublic:
448114402Sru  void text_glob_html      (style *s, char *str, int length,
449114402Sru			    int min_vertical, int min_horizontal,
450114402Sru			    int max_vertical, int max_horizontal);
451114402Sru  void text_glob_special   (style *s, char *str, int length,
452114402Sru			    int min_vertical, int min_horizontal,
453114402Sru			    int max_vertical, int max_horizontal);
454114402Sru  void text_glob_line      (style *s,
455114402Sru			    int min_vertical, int min_horizontal,
456114402Sru			    int max_vertical, int max_horizontal,
457114402Sru			    int thickness);
458114402Sru  void text_glob_auto_image(style *s, char *str, int length,
459114402Sru			    int min_vertical, int min_horizontal,
460114402Sru			    int max_vertical, int max_horizontal);
461114402Sru  void text_glob_tag       (style *s, char *str, int length,
462114402Sru			    int min_vertical, int min_horizontal,
463114402Sru			    int max_vertical, int max_horizontal);
464114402Sru
465114402Sru  text_glob                (void);
466114402Sru  ~text_glob               (void);
467114402Sru  int  is_a_line           (void);
468114402Sru  int  is_a_tag            (void);
469114402Sru  int  is_eol              (void);
470114402Sru  int  is_auto_img         (void);
471114402Sru  int  is_br               (void);
472114402Sru  int  is_in               (void);
473114402Sru  int  is_po               (void);
474114402Sru  int  is_ti               (void);
475151497Sru  int  is_ll               (void);
476114402Sru  int  is_ce               (void);
477151497Sru  int  is_tl               (void);
478151497Sru  int  is_eo_tl            (void);
479114402Sru  int  is_eol_ce           (void);
480114402Sru  int  is_col              (void);
481114402Sru  int  is_tab              (void);
482114402Sru  int  is_tab0             (void);
483114402Sru  int  is_ta               (void);
484114402Sru  int  is_tab_ts           (void);
485114402Sru  int  is_tab_te           (void);
486114402Sru  int  is_nf               (void);
487114402Sru  int  is_fi               (void);
488151497Sru  int  is_eo_h             (void);
489114402Sru  int  get_arg             (void);
490114402Sru  int  get_tab_args        (char *align);
491114402Sru
492114402Sru  void        remember_table (html_table *t);
493114402Sru  html_table *get_table      (void);
494114402Sru
495114402Sru  style           text_style;
496114402Sru  const char     *text_string;
497114402Sru  unsigned int    text_length;
498114402Sru  int             minv, minh, maxv, maxh;
499114402Sru  int             is_tag;               // is this a .br, .sp, .tl etc
500114402Sru  int             is_img_auto;          // image created by eqn delim
501114402Sru  int             is_special;           // text has come via 'x X html:'
502114402Sru  int             is_line;              // is the command a <line>?
503114402Sru  int             thickness;            // the thickness of a line
504114402Sru  html_table     *tab;                  // table description
505114402Sru
506114402Sruprivate:
507114402Sru  text_glob           (style *s, const char *str, int length,
508114402Sru		       int min_vertical , int min_horizontal,
509114402Sru		       int max_vertical , int max_horizontal,
510114402Sru		       bool is_troff_command,
511114402Sru		       bool is_auto_image, bool is_special_command,
512114402Sru		       bool is_a_line    , int  thickness);
513114402Sru};
514114402Sru
515114402Srutext_glob::text_glob (style *s, const char *str, int length,
516114402Sru		      int min_vertical, int min_horizontal,
517114402Sru		      int max_vertical, int max_horizontal,
518114402Sru		      bool is_troff_command,
519114402Sru		      bool is_auto_image, bool is_special_command,
520151497Sru		      bool is_a_line_flag, int line_thickness)
521114402Sru  : text_style(*s), text_string(str), text_length(length),
522114402Sru    minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal),
523114402Sru    is_tag(is_troff_command), is_img_auto(is_auto_image), is_special(is_special_command),
524151497Sru    is_line(is_a_line_flag), thickness(line_thickness), tab(NULL)
525114402Sru{
526114402Sru}
527114402Sru
528114402Srutext_glob::text_glob ()
529151497Sru  : text_string(NULL), text_length(0), minv(-1), minh(-1), maxv(-1), maxh(-1),
530114402Sru    is_tag(FALSE), is_special(FALSE), is_line(FALSE), thickness(0), tab(NULL)
531114402Sru{
532114402Sru}
533114402Sru
534114402Srutext_glob::~text_glob ()
535114402Sru{
536114402Sru  if (tab != NULL)
537114402Sru    delete tab;
538114402Sru}
539114402Sru
540114402Sru/*
541114402Sru *  text_glob_html - used to place html text into the glob buffer.
542114402Sru */
543114402Sru
544114402Sruvoid text_glob::text_glob_html (style *s, char *str, int length,
545114402Sru				int min_vertical , int min_horizontal,
546114402Sru				int max_vertical , int max_horizontal)
547114402Sru{
548114402Sru  text_glob *g = new text_glob(s, str, length,
549114402Sru			       min_vertical, min_horizontal, max_vertical, max_horizontal,
550114402Sru			       FALSE, FALSE, FALSE, FALSE, 0);
551114402Sru  *this = *g;
552114402Sru  delete g;
553114402Sru}
554114402Sru
555114402Sru/*
556114402Sru *  text_glob_html - used to place html specials into the glob buffer.
557114402Sru *                   This text is essentially html commands coming through
558114402Sru *                   from the macro sets, with special designated sequences of
559114402Sru *                   characters translated into html. See add_and_encode.
560114402Sru */
561114402Sru
562114402Sruvoid text_glob::text_glob_special (style *s, char *str, int length,
563114402Sru				   int min_vertical , int min_horizontal,
564114402Sru				   int max_vertical , int max_horizontal)
565114402Sru{
566114402Sru  text_glob *g = new text_glob(s, str, length,
567114402Sru			       min_vertical, min_horizontal, max_vertical, max_horizontal,
568114402Sru			       FALSE, FALSE, TRUE, FALSE, 0);
569114402Sru  *this = *g;
570114402Sru  delete g;
571114402Sru}
572114402Sru
573114402Sru/*
574114402Sru *  text_glob_line - record horizontal draw line commands.
575114402Sru */
576114402Sru
577114402Sruvoid text_glob::text_glob_line (style *s,
578114402Sru				int min_vertical , int min_horizontal,
579114402Sru				int max_vertical , int max_horizontal,
580151497Sru				int thickness_value)
581114402Sru{
582114402Sru  text_glob *g = new text_glob(s, "", 0,
583114402Sru			       min_vertical, min_horizontal, max_vertical, max_horizontal,
584151497Sru			       FALSE, FALSE, FALSE, TRUE, thickness_value);
585114402Sru  *this = *g;
586114402Sru  delete g;
587114402Sru}
588114402Sru
589114402Sru/*
590114402Sru *  text_glob_auto_image - record the presence of a .auto-image tag command.
591114402Sru *                         Used to mark that an image has been created automatically
592114402Sru *                         by a preprocessor and (pre-grohtml/troff) combination.
593114402Sru *                         Under some circumstances images may not be created.
594114402Sru *                         (consider .EQ
595114402Sru *                                   delim $$
596114402Sru *                                   .EN
597114402Sru *                                   .TS
598114402Sru *                                   tab(!), center;
599114402Sru *                                   l!l.
600114402Sru *                                   $1 over x$!recripical of x
601114402Sru *                                   .TE
602114402Sru *
603114402Sru *                          the first auto-image marker is created via .EQ/.EN pair
604114402Sru *                          and no image is created.
605114402Sru *                          The second auto-image marker occurs at $1 over x$
606114402Sru *                          Currently this image will not be created
607114402Sru *                          as the whole of the table is created as an image.
608114402Sru *                          (Once html tables are handled by grohtml this will change.
609114402Sru *                           Shortly this will be the case).
610114402Sru */
611114402Sru
612114402Sruvoid text_glob::text_glob_auto_image(style *s, char *str, int length,
613114402Sru				     int min_vertical, int min_horizontal,
614114402Sru				     int max_vertical, int max_horizontal)
615114402Sru{
616114402Sru  text_glob *g = new text_glob(s, str, length,
617114402Sru			       min_vertical, min_horizontal, max_vertical, max_horizontal,
618114402Sru			       TRUE, TRUE, FALSE, FALSE, 0);
619114402Sru  *this = *g;
620114402Sru  delete g;
621114402Sru}
622114402Sru
623114402Sru/*
624114402Sru *  text_glob_tag - records a troff tag.
625114402Sru */
626114402Sru
627114402Sruvoid text_glob::text_glob_tag (style *s, char *str, int length,
628114402Sru			       int min_vertical, int min_horizontal,
629114402Sru			       int max_vertical, int max_horizontal)
630114402Sru{
631114402Sru  text_glob *g = new text_glob(s, str, length,
632114402Sru			       min_vertical, min_horizontal, max_vertical, max_horizontal,
633114402Sru			       TRUE, FALSE, FALSE, FALSE, 0);
634114402Sru  *this = *g;
635114402Sru  delete g;
636114402Sru}
637114402Sru
638114402Sru/*
639114402Sru *  is_a_line - returns TRUE if glob should be converted into an <hr>
640114402Sru */
641114402Sru
642114402Sruint text_glob::is_a_line (void)
643114402Sru{
644114402Sru  return is_line;
645114402Sru}
646114402Sru
647114402Sru/*
648114402Sru *  is_a_tag - returns TRUE if glob contains a troff directive.
649114402Sru */
650114402Sru
651114402Sruint text_glob::is_a_tag (void)
652114402Sru{
653114402Sru  return is_tag;
654114402Sru}
655114402Sru
656114402Sru/*
657114402Sru *  is_eol - returns TRUE if glob contains the tag eol
658114402Sru */
659114402Sru
660114402Sruint text_glob::is_eol (void)
661114402Sru{
662151497Sru  return is_tag && (strcmp(text_string, "devtag:.eol") == 0);
663114402Sru}
664114402Sru
665114402Sru/*
666114402Sru *  is_eol_ce - returns TRUE if glob contains the tag eol.ce
667114402Sru */
668114402Sru
669114402Sruint text_glob::is_eol_ce (void)
670114402Sru{
671151497Sru  return is_tag && (strcmp(text_string, "devtag:eol.ce") == 0);
672114402Sru}
673114402Sru
674151497Sru/*
675151497Sru *  is_tl - returns TRUE if glob contains the tag .tl
676151497Sru */
677114402Sru
678151497Sruint text_glob::is_tl (void)
679151497Sru{
680151497Sru  return is_tag && (strcmp(text_string, "devtag:.tl") == 0);
681151497Sru}
682151497Sru
683114402Sru/*
684151497Sru *  is_eo_tl - returns TRUE if glob contains the tag eo.tl
685114402Sru */
686114402Sru
687151497Sruint text_glob::is_eo_tl (void)
688151497Sru{
689151497Sru  return is_tag && (strcmp(text_string, "devtag:.eo.tl") == 0);
690151497Sru}
691151497Sru
692151497Sru/*
693151497Sru *  is_nf - returns TRUE if glob contains the tag .fi 0
694151497Sru */
695151497Sru
696114402Sruint text_glob::is_nf (void)
697114402Sru{
698151497Sru  return is_tag && (strncmp(text_string, "devtag:.fi",
699151497Sru			    strlen("devtag:.fi")) == 0) &&
700151497Sru         (get_arg() == 0);
701114402Sru}
702114402Sru
703114402Sru/*
704151497Sru *  is_fi - returns TRUE if glob contains the tag .fi 1
705114402Sru */
706114402Sru
707114402Sruint text_glob::is_fi (void)
708114402Sru{
709151497Sru  return( is_tag && (strncmp(text_string, "devtag:.fi",
710151497Sru			     strlen("devtag:.fi")) == 0) &&
711151497Sru	  (get_arg() == 1) );
712114402Sru}
713114402Sru
714114402Sru/*
715151497Sru *  is_eo_h - returns TRUE if glob contains the tag .eo.h
716151497Sru */
717151497Sru
718151497Sruint text_glob::is_eo_h (void)
719151497Sru{
720151497Sru  return is_tag && (strcmp(text_string, "devtag:.eo.h") == 0);
721151497Sru}
722151497Sru
723151497Sru/*
724114402Sru *  is_ce - returns TRUE if glob contains the tag .ce
725114402Sru */
726114402Sru
727114402Sruint text_glob::is_ce (void)
728114402Sru{
729151497Sru  return is_tag && (strncmp(text_string, "devtag:.ce",
730151497Sru			    strlen("devtag:.ce")) == 0);
731114402Sru}
732114402Sru
733114402Sru/*
734114402Sru *  is_in - returns TRUE if glob contains the tag .in
735114402Sru */
736114402Sru
737114402Sruint text_glob::is_in (void)
738114402Sru{
739151497Sru  return is_tag && (strncmp(text_string, "devtag:.in ",
740151497Sru			    strlen("devtag:.in ")) == 0);
741114402Sru}
742114402Sru
743114402Sru/*
744114402Sru *  is_po - returns TRUE if glob contains the tag .po
745114402Sru */
746114402Sru
747114402Sruint text_glob::is_po (void)
748114402Sru{
749151497Sru  return is_tag && (strncmp(text_string, "devtag:.po ",
750151497Sru			    strlen("devtag:.po ")) == 0);
751114402Sru}
752114402Sru
753114402Sru/*
754114402Sru *  is_ti - returns TRUE if glob contains the tag .ti
755114402Sru */
756114402Sru
757114402Sruint text_glob::is_ti (void)
758114402Sru{
759151497Sru  return is_tag && (strncmp(text_string, "devtag:.ti ",
760151497Sru			    strlen("devtag:.ti ")) == 0);
761114402Sru}
762114402Sru
763114402Sru/*
764151497Sru *  is_ll - returns TRUE if glob contains the tag .ll
765151497Sru */
766151497Sru
767151497Sruint text_glob::is_ll (void)
768151497Sru{
769151497Sru  return is_tag && (strncmp(text_string, "devtag:.ll ",
770151497Sru			    strlen("devtag:.ll ")) == 0);
771151497Sru}
772151497Sru
773151497Sru/*
774114402Sru *  is_col - returns TRUE if glob contains the tag .col
775114402Sru */
776114402Sru
777114402Sruint text_glob::is_col (void)
778114402Sru{
779151497Sru  return is_tag && (strncmp(text_string, "devtag:.col",
780151497Sru			    strlen("devtag:.col")) == 0);
781114402Sru}
782114402Sru
783114402Sru/*
784114402Sru *  is_tab_ts - returns TRUE if glob contains the tag .tab_ts
785114402Sru */
786114402Sru
787114402Sruint text_glob::is_tab_ts (void)
788114402Sru{
789151497Sru  return is_tag && (strcmp(text_string, "devtag:.tab-ts") == 0);
790114402Sru}
791114402Sru
792114402Sru/*
793114402Sru *  is_tab_te - returns TRUE if glob contains the tag .tab_te
794114402Sru */
795114402Sru
796114402Sruint text_glob::is_tab_te (void)
797114402Sru{
798151497Sru  return is_tag && (strcmp(text_string, "devtag:.tab-te") == 0);
799114402Sru}
800114402Sru
801114402Sru/*
802114402Sru *  is_ta - returns TRUE if glob contains the tag .ta
803114402Sru */
804114402Sru
805114402Sruint text_glob::is_ta (void)
806114402Sru{
807151497Sru  return is_tag && (strncmp(text_string, "devtag:.ta ",
808151497Sru			    strlen("devtag:.ta ")) == 0);
809114402Sru}
810114402Sru
811114402Sru/*
812114402Sru *  is_tab - returns TRUE if glob contains the tag tab
813114402Sru */
814114402Sru
815114402Sruint text_glob::is_tab (void)
816114402Sru{
817151497Sru  return is_tag && (strncmp(text_string, "devtag:tab ",
818151497Sru			    strlen("devtag:tab ")) == 0);
819114402Sru}
820114402Sru
821114402Sru/*
822114402Sru *  is_tab0 - returns TRUE if glob contains the tag tab0
823114402Sru */
824114402Sru
825114402Sruint text_glob::is_tab0 (void)
826114402Sru{
827151497Sru  return is_tag && (strncmp(text_string, "devtag:tab0",
828151497Sru			    strlen("devtag:tab0")) == 0);
829114402Sru}
830114402Sru
831114402Sru/*
832114402Sru *  is_auto_img - returns TRUE if the glob contains an automatically
833114402Sru *                generated image.
834114402Sru */
835114402Sru
836114402Sruint text_glob::is_auto_img (void)
837114402Sru{
838114402Sru  return is_img_auto;
839114402Sru}
840114402Sru
841114402Sru/*
842114402Sru *  is_br - returns TRUE if the glob is a tag containing a .br
843114402Sru *          or an implied .br. Note that we do not include .nf or .fi
844114402Sru *          as grohtml will place a .br after these commands if they
845114402Sru *          should break the line.
846114402Sru */
847114402Sru
848114402Sruint text_glob::is_br (void)
849114402Sru{
850151497Sru  return is_a_tag() && ((strcmp ("devtag:.br", text_string) == 0) ||
851151497Sru			(strncmp("devtag:.sp", text_string,
852151497Sru				 strlen("devtag:.sp")) == 0));
853114402Sru}
854114402Sru
855114402Sruint text_glob::get_arg (void)
856114402Sru{
857151497Sru  if (strncmp("devtag:", text_string, strlen("devtag:")) == 0) {
858114402Sru    const char *p = text_string;
859114402Sru
860114402Sru    while ((*p != (char)0) && (!isspace(*p)))
861114402Sru      p++;
862114402Sru    while ((*p != (char)0) && (isspace(*p)))
863114402Sru      p++;
864114402Sru    if (*p == (char)0)
865114402Sru      return -1;
866114402Sru    return atoi(p);
867114402Sru  }
868114402Sru  return -1;
869114402Sru}
870114402Sru
871114402Sru/*
872114402Sru *  get_tab_args - returns the tab position and alignment of the tab tag
873114402Sru */
874114402Sru
875114402Sruint text_glob::get_tab_args (char *align)
876114402Sru{
877151497Sru  if (strncmp("devtag:", text_string, strlen("devtag:")) == 0) {
878114402Sru    const char *p = text_string;
879114402Sru
880114402Sru    // firstly the alignment C|R|L
881114402Sru    while ((*p != (char)0) && (!isspace(*p)))
882114402Sru      p++;
883114402Sru    while ((*p != (char)0) && (isspace(*p)))
884114402Sru      p++;
885114402Sru    *align = *p;
886114402Sru    // now the int value
887114402Sru    while ((*p != (char)0) && (!isspace(*p)))
888114402Sru      p++;
889114402Sru    while ((*p != (char)0) && (isspace(*p)))
890114402Sru      p++;
891114402Sru    if (*p == (char)0)
892114402Sru      return -1;
893114402Sru    return atoi(p);
894114402Sru  }
895114402Sru  return -1;
896114402Sru}
897114402Sru
898114402Sru/*
899114402Sru *  remember_table - saves table, t, in the text_glob.
900114402Sru */
901114402Sru
902114402Sruvoid text_glob::remember_table (html_table *t)
903114402Sru{
904114402Sru  if (tab != NULL)
905114402Sru    delete tab;
906114402Sru  tab = t;
907114402Sru}
908114402Sru
909114402Sru/*
910114402Sru *  get_table - returns the stored table description.
911114402Sru */
912114402Sru
913114402Sruhtml_table *text_glob::get_table (void)
914114402Sru{
915114402Sru  return tab;
916114402Sru}
917114402Sru
918114402Sru/*
919151497Sru *  the class and methods used to construct ordered double linked
920151497Sru *  lists.  In a previous implementation we used templates via
921151497Sru *  #include "ordered-list.h", but this does assume that all C++
922151497Sru *  compilers can handle this feature. Pragmatically it is safer to
923151497Sru *  assume this is not the case.
924114402Sru */
925114402Sru
926114402Srustruct element_list {
927114402Sru  element_list *right;
928114402Sru  element_list *left;
929114402Sru  text_glob    *datum;
930114402Sru  int           lineno;
931114402Sru  int           minv, minh, maxv, maxh;
932114402Sru
933114402Sru  element_list  (text_glob *d,
934114402Sru		 int line_number,
935114402Sru		 int min_vertical, int min_horizontal,
936114402Sru		 int max_vertical, int max_horizontal);
937114402Sru  element_list  ();
938114402Sru  ~element_list ();
939114402Sru};
940114402Sru
941114402Sruelement_list::element_list ()
942114402Sru  : right(0), left(0), datum(0), lineno(0), minv(-1), minh(-1), maxv(-1), maxh(-1)
943114402Sru{
944114402Sru}
945114402Sru
946114402Sru/*
947114402Sru *  element_list - create a list element assigning the datum and region parameters.
948114402Sru */
949114402Sru
950114402Sruelement_list::element_list (text_glob *in,
951114402Sru			    int line_number,
952114402Sru			    int min_vertical, int min_horizontal,
953114402Sru			    int max_vertical, int max_horizontal)
954114402Sru  : right(0), left(0), datum(in), lineno(line_number),
955114402Sru    minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal)
956114402Sru{
957114402Sru}
958114402Sru
959114402Sruelement_list::~element_list ()
960114402Sru{
961114402Sru  if (datum != NULL)
962114402Sru    delete datum;
963114402Sru}
964114402Sru
965114402Sruclass list {
966114402Srupublic:
967114402Sru       list             ();
968114402Sru      ~list             ();
969114402Sru  int  is_less          (element_list *a, element_list *b);
970114402Sru  void add              (text_glob *in,
971114402Sru		         int line_number,
972114402Sru		         int min_vertical, int min_horizontal,
973114402Sru		         int max_vertical, int max_horizontal);
974114402Sru  void                  sub_move_right      (void);
975114402Sru  void                  move_right          (void);
976114402Sru  void                  move_left           (void);
977114402Sru  int                   is_empty            (void);
978114402Sru  int                   is_equal_to_tail    (void);
979114402Sru  int                   is_equal_to_head    (void);
980114402Sru  void                  start_from_head     (void);
981114402Sru  void                  start_from_tail     (void);
982114402Sru  void                  insert              (text_glob *in);
983114402Sru  void                  move_to             (text_glob *in);
984114402Sru  text_glob            *move_right_get_data (void);
985114402Sru  text_glob            *move_left_get_data  (void);
986114402Sru  text_glob            *get_data            (void);
987114402Sruprivate:
988114402Sru  element_list *head;
989114402Sru  element_list *tail;
990114402Sru  element_list *ptr;
991114402Sru};
992114402Sru
993114402Sru/*
994114402Sru *  list - construct an empty list.
995114402Sru */
996114402Sru
997114402Srulist::list ()
998114402Sru  : head(NULL), tail(NULL), ptr(NULL)
999114402Sru{
1000114402Sru}
1001114402Sru
1002114402Sru/*
1003114402Sru *  ~list - destroy a complete list.
1004114402Sru */
1005114402Sru
1006114402Srulist::~list()
1007114402Sru{
1008114402Sru  element_list *temp=head;
1009114402Sru
1010114402Sru  do {
1011114402Sru    temp = head;
1012114402Sru    if (temp != NULL) {
1013114402Sru      head = head->right;
1014114402Sru      delete temp;
1015114402Sru    }
1016114402Sru  } while ((head != NULL) && (head != tail));
1017114402Sru}
1018114402Sru
1019114402Sru/*
1020114402Sru *  is_less - returns TRUE if a is left of b if on the same line or
1021114402Sru *            if a is higher up the page than b.
1022114402Sru */
1023114402Sru
1024114402Sruint list::is_less (element_list *a, element_list *b)
1025114402Sru{
1026114402Sru  // was if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
1027114402Sru  if (a->lineno < b->lineno) {
1028114402Sru    return( TRUE );
1029114402Sru  } else if (a->lineno > b->lineno) {
1030114402Sru    return( FALSE );
1031114402Sru  } else if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) {
1032114402Sru    return( a->minh < b->minh );
1033114402Sru  } else {
1034114402Sru    return( a->maxv < b->maxv );
1035114402Sru  }
1036114402Sru}
1037114402Sru
1038114402Sru/*
1039151497Sru *  add - adds a datum to the list in the order specified by the
1040151497Sru *        region position.
1041114402Sru */
1042114402Sru
1043114402Sruvoid list::add (text_glob *in, int line_number, int min_vertical, int min_horizontal, int max_vertical, int max_horizontal)
1044114402Sru{
1045114402Sru  // create a new list element with datum and position fields initialized
1046114402Sru  element_list *t    = new element_list(in, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1047114402Sru  element_list *last;
1048114402Sru
1049151497Sru#if 0
1050151497Sru  fprintf(stderr, "[%s %d,%d,%d,%d] ",
1051151497Sru	  in->text_string, min_vertical, min_horizontal, max_vertical, max_horizontal);
1052151497Sru  fflush(stderr);
1053151497Sru#endif
1054151497Sru
1055151497Sru  if (head == NULL) {
1056114402Sru    head     = t;
1057114402Sru    tail     = t;
1058114402Sru    ptr      = t;
1059114402Sru    t->left  = t;
1060114402Sru    t->right = t;
1061114402Sru  } else {
1062114402Sru    last = tail;
1063114402Sru
1064151497Sru    while ((last != head) && (is_less(t, last)))
1065114402Sru      last = last->left;
1066114402Sru
1067114402Sru    if (is_less(t, last)) {
1068114402Sru      t->right          = last;
1069114402Sru      last->left->right = t;
1070114402Sru      t->left           = last->left;
1071114402Sru      last->left        = t;
1072114402Sru      // now check for a new head
1073151497Sru      if (last == head)
1074114402Sru	head = t;
1075114402Sru    } else {
1076114402Sru      // add t beyond last
1077114402Sru      t->right          = last->right;
1078114402Sru      t->left           = last;
1079114402Sru      last->right->left = t;
1080114402Sru      last->right       = t;
1081114402Sru      // now check for a new tail
1082151497Sru      if (last == tail)
1083114402Sru	tail = t;
1084114402Sru    }
1085114402Sru  }
1086114402Sru}
1087114402Sru
1088114402Sru/*
1089114402Sru *  sub_move_right - removes the element which is currently pointed to by ptr
1090114402Sru *                   from the list and moves ptr to the right.
1091114402Sru */
1092114402Sru
1093114402Sruvoid list::sub_move_right (void)
1094114402Sru{
1095114402Sru  element_list *t=ptr->right;
1096114402Sru
1097114402Sru  if (head == tail) {
1098151497Sru    head = NULL;
1099151497Sru    if (tail != NULL)
1100114402Sru      delete tail;
1101151497Sru
1102151497Sru    tail = NULL;
1103151497Sru    ptr  = NULL;
1104114402Sru  } else {
1105151497Sru    if (head == ptr)
1106114402Sru      head = head->right;
1107151497Sru    if (tail == ptr)
1108114402Sru      tail = tail->left;
1109114402Sru    ptr->left->right = ptr->right;
1110114402Sru    ptr->right->left = ptr->left;
1111151497Sru    ptr = t;
1112114402Sru  }
1113114402Sru}
1114114402Sru
1115114402Sru/*
1116114402Sru *  start_from_head - assigns ptr to the head.
1117114402Sru */
1118114402Sru
1119114402Sruvoid list::start_from_head (void)
1120114402Sru{
1121114402Sru  ptr = head;
1122114402Sru}
1123114402Sru
1124114402Sru/*
1125114402Sru *  start_from_tail - assigns ptr to the tail.
1126114402Sru */
1127114402Sru
1128114402Sruvoid list::start_from_tail (void)
1129114402Sru{
1130114402Sru  ptr = tail;
1131114402Sru}
1132114402Sru
1133114402Sru/*
1134114402Sru *  is_empty - returns TRUE if the list has no elements.
1135114402Sru */
1136114402Sru
1137114402Sruint list::is_empty (void)
1138114402Sru{
1139151497Sru  return head == NULL;
1140114402Sru}
1141114402Sru
1142114402Sru/*
1143114402Sru *  is_equal_to_tail - returns TRUE if the ptr equals the tail.
1144114402Sru */
1145114402Sru
1146114402Sruint list::is_equal_to_tail (void)
1147114402Sru{
1148151497Sru  return ptr == tail;
1149114402Sru}
1150114402Sru
1151114402Sru/*
1152114402Sru *  is_equal_to_head - returns TRUE if the ptr equals the head.
1153114402Sru */
1154114402Sru
1155114402Sruint list::is_equal_to_head (void)
1156114402Sru{
1157151497Sru  return ptr == head;
1158114402Sru}
1159114402Sru
1160114402Sru/*
1161114402Sru *  move_left - moves the ptr left.
1162114402Sru */
1163114402Sru
1164114402Sruvoid list::move_left (void)
1165114402Sru{
1166114402Sru  ptr = ptr->left;
1167114402Sru}
1168114402Sru
1169114402Sru/*
1170114402Sru *  move_right - moves the ptr right.
1171114402Sru */
1172114402Sru
1173114402Sruvoid list::move_right (void)
1174114402Sru{
1175114402Sru  ptr = ptr->right;
1176114402Sru}
1177114402Sru
1178114402Sru/*
1179114402Sru *  get_datum - returns the datum referenced via ptr.
1180114402Sru */
1181114402Sru
1182114402Srutext_glob* list::get_data (void)
1183114402Sru{
1184151497Sru  return ptr->datum;
1185114402Sru}
1186114402Sru
1187114402Sru/*
1188114402Sru *  move_right_get_data - returns the datum referenced via ptr and moves
1189114402Sru *                        ptr right.
1190114402Sru */
1191114402Sru
1192114402Srutext_glob* list::move_right_get_data (void)
1193114402Sru{
1194114402Sru  ptr = ptr->right;
1195151497Sru  if (ptr == head)
1196151497Sru    return NULL;
1197151497Sru  else
1198151497Sru    return ptr->datum;
1199114402Sru}
1200114402Sru
1201114402Sru/*
1202114402Sru *  move_left_get_data - returns the datum referenced via ptr and moves
1203114402Sru *                       ptr right.
1204114402Sru */
1205114402Sru
1206114402Srutext_glob* list::move_left_get_data (void)
1207114402Sru{
1208114402Sru  ptr = ptr->left;
1209151497Sru  if (ptr == tail)
1210151497Sru    return NULL;
1211151497Sru  else
1212151497Sru    return ptr->datum;
1213114402Sru}
1214114402Sru
1215114402Sru/*
1216114402Sru *  insert - inserts data after the current position.
1217114402Sru */
1218114402Sru
1219114402Sruvoid list::insert (text_glob *in)
1220114402Sru{
1221114402Sru  if (is_empty())
1222114402Sru    fatal("list must not be empty if we are inserting data");
1223114402Sru  else {
1224151497Sru    if (ptr == NULL)
1225114402Sru      ptr = head;
1226114402Sru
1227114402Sru    element_list *t = new element_list(in, ptr->lineno, ptr->minv, ptr->minh, ptr->maxv, ptr->maxh);
1228114402Sru    if (ptr == tail)
1229114402Sru      tail = t;
1230114402Sru    ptr->right->left = t;
1231114402Sru    t->right = ptr->right;
1232114402Sru    ptr->right = t;
1233114402Sru    t->left = ptr;
1234114402Sru  }
1235114402Sru}
1236114402Sru
1237114402Sru/*
1238114402Sru *  move_to - moves the current position to the point where data, in, exists.
1239114402Sru *            This is an expensive method and should be used sparingly.
1240114402Sru */
1241114402Sru
1242114402Sruvoid list::move_to (text_glob *in)
1243114402Sru{
1244114402Sru  ptr = head;
1245114402Sru  while (ptr != tail && ptr->datum != in)
1246114402Sru    ptr = ptr->right;
1247114402Sru}
1248114402Sru
1249114402Sru/*
1250114402Sru *  page class and methods
1251114402Sru */
1252114402Sru
1253114402Sruclass page {
1254114402Srupublic:
1255114402Sru                              page            (void);
1256114402Sru  void                        add             (style *s, const string &str,
1257114402Sru					       int line_number,
1258114402Sru					       int min_vertical, int min_horizontal,
1259114402Sru					       int max_vertical, int max_horizontal);
1260114402Sru  void                        add_tag         (style *s, const string &str,
1261114402Sru					       int line_number,
1262114402Sru					       int min_vertical, int min_horizontal,
1263114402Sru					       int max_vertical, int max_horizontal);
1264114402Sru  void                        add_and_encode  (style *s, const string &str,
1265114402Sru					       int line_number,
1266114402Sru					       int min_vertical, int min_horizontal,
1267151497Sru					       int max_vertical, int max_horizontal,
1268151497Sru					       int is_tag);
1269114402Sru  void                        add_line        (style *s,
1270114402Sru					       int line_number,
1271114402Sru					       int x1, int y1, int x2, int y2,
1272114402Sru					       int thickness);
1273114402Sru  void                        insert_tag      (const string &str);
1274114402Sru  void                        dump_page       (void);   // debugging method
1275114402Sru
1276114402Sru  // and the data
1277114402Sru
1278114402Sru  list                        glyphs;         // position of glyphs and specials on page
1279114402Sru  char_buffer                 buffer;         // all characters for this page
1280114402Sru};
1281114402Sru
1282114402Srupage::page()
1283114402Sru{
1284114402Sru}
1285114402Sru
1286114402Sru/*
1287114402Sru *  insert_tag - inserts a tag after the current position.
1288114402Sru */
1289114402Sru
1290114402Sruvoid page::insert_tag (const string &str)
1291114402Sru{
1292114402Sru  if (str.length() > 0) {
1293114402Sru    text_glob *g=new text_glob();
1294114402Sru    text_glob *f=glyphs.get_data();
1295114402Sru    g->text_glob_tag(&f->text_style, buffer.add_string(str), str.length(),
1296114402Sru		     f->minv, f->minh, f->maxv, f->maxh);
1297114402Sru    glyphs.insert(g);
1298114402Sru  }
1299114402Sru}
1300114402Sru
1301114402Sru/*
1302114402Sru *  add - add html text to the list of glyphs.
1303114402Sru */
1304114402Sru
1305114402Sruvoid page::add (style *s, const string &str,
1306114402Sru		int line_number,
1307114402Sru		int min_vertical, int min_horizontal,
1308114402Sru		int max_vertical, int max_horizontal)
1309114402Sru{
1310114402Sru  if (str.length() > 0) {
1311114402Sru    text_glob *g=new text_glob();
1312114402Sru    g->text_glob_html(s, buffer.add_string(str), str.length(),
1313114402Sru		      min_vertical, min_horizontal, max_vertical, max_horizontal);
1314114402Sru    glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1315114402Sru  }
1316114402Sru}
1317114402Sru
1318114402Sru/*
1319114402Sru *  add_tag - adds a troff tag, for example: .tl .sp .br
1320114402Sru */
1321114402Sru
1322114402Sruvoid page::add_tag (style *s, const string &str,
1323114402Sru		    int line_number,
1324114402Sru		    int min_vertical, int min_horizontal,
1325114402Sru		    int max_vertical, int max_horizontal)
1326114402Sru{
1327114402Sru  if (str.length() > 0) {
1328114402Sru    text_glob *g;
1329114402Sru
1330151497Sru    if (strncmp((str+'\0').contents(), "devtag:.auto-image",
1331151497Sru		strlen("devtag:.auto-image")) == 0) {
1332114402Sru      g = new text_glob();
1333114402Sru      g->text_glob_auto_image(s, buffer.add_string(str), str.length(),
1334114402Sru			      min_vertical, min_horizontal, max_vertical, max_horizontal);
1335114402Sru    } else {
1336114402Sru      g = new text_glob();
1337114402Sru      g->text_glob_tag(s, buffer.add_string(str), str.length(),
1338114402Sru		       min_vertical, min_horizontal, max_vertical, max_horizontal);
1339114402Sru    }
1340114402Sru    glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1341114402Sru  }
1342114402Sru}
1343114402Sru
1344114402Sru/*
1345114402Sru *  add_line - adds the <line> primitive providing that y1==y2
1346114402Sru */
1347114402Sru
1348114402Sruvoid page::add_line (style *s,
1349114402Sru		     int line_number,
1350151497Sru		     int x_1, int y_1, int x_2, int y_2,
1351114402Sru		     int thickness)
1352114402Sru{
1353151497Sru  if (y_1 == y_2) {
1354114402Sru    text_glob *g = new text_glob();
1355114402Sru    g->text_glob_line(s,
1356151497Sru		      min(y_1, y_2), min(x_1, x_2),
1357151497Sru		      max(y_1, y_2), max(x_1, x_2),
1358114402Sru		      thickness);
1359151497Sru    glyphs.add(g, line_number,
1360151497Sru	       min(y_1, y_2), min(x_1, x_2),
1361151497Sru	       max(y_1, y_2), max(x_1, x_2));
1362114402Sru  }
1363114402Sru}
1364114402Sru
1365114402Sru/*
1366114402Sru *  to_unicode - returns a unicode translation of int, ch.
1367114402Sru */
1368114402Sru
1369114402Srustatic char *to_unicode (unsigned int ch)
1370114402Sru{
1371114402Sru  static char buf[30];
1372114402Sru
1373114402Sru  sprintf(buf, "&#%u;", ch);
1374114402Sru  return buf;
1375114402Sru}
1376114402Sru
1377114402Sru/*
1378114402Sru *  add_and_encode - adds a special string to the page, it translates the string
1379114402Sru *                   into html glyphs. The special string will have come from x X html:
1380114402Sru *                   and can contain troff character encodings which appear as
1381114402Sru *                   \(char\). A sequence of \\ represents \.
1382114402Sru *                   So for example we can write:
1383114402Sru *                      "cost = \(Po\)3.00 file = \\foo\\bar"
1384114402Sru *                   which is translated into:
1385114402Sru *                      "cost = &pound;3.00 file = \foo\bar"
1386114402Sru */
1387114402Sru
1388114402Sruvoid page::add_and_encode (style *s, const string &str,
1389114402Sru			   int line_number,
1390114402Sru			   int min_vertical, int min_horizontal,
1391151497Sru			   int max_vertical, int max_horizontal,
1392151497Sru			   int is_tag)
1393114402Sru{
1394114402Sru  string html_string;
1395114402Sru  char *html_glyph;
1396114402Sru  int i=0;
1397114402Sru
1398114402Sru  if (s->f == NULL)
1399114402Sru    return;
1400114402Sru  while (i < str.length()) {
1401114402Sru    if ((i+1<str.length()) && (str.substring(i, 2) == string("\\("))) {
1402114402Sru      // start of escape
1403114402Sru      i += 2; // move over \(
1404114402Sru      int a = i;
1405114402Sru      while ((i+1<str.length()) && (str.substring(i, 2) != string("\\)"))) {
1406114402Sru	i++;
1407114402Sru      }
1408114402Sru      int n = i;
1409114402Sru      if ((i+1<str.length()) && (str.substring(i, 2) == string("\\)")))
1410114402Sru	i++;
1411114402Sru      else
1412114402Sru	n = -1;
1413114402Sru      if (n > 0) {
1414114402Sru	string troff_charname = str.substring(a, n-a);
1415114402Sru	html_glyph = get_html_translation(s->f, troff_charname);
1416114402Sru	if (html_glyph)
1417114402Sru	  html_string += html_glyph;
1418114402Sru	else {
1419151497Sru	  int idx=s->f->name_to_index((troff_charname + '\0').contents());
1420114402Sru
1421151497Sru	  if (s->f->contains(idx) && (idx != 0))
1422151497Sru	    html_string += s->f->get_code(idx);
1423114402Sru	}
1424114402Sru      }
1425114402Sru    } else
1426114402Sru      html_string += str[i];
1427114402Sru    i++;
1428114402Sru  }
1429114402Sru  if (html_string.length() > 0) {
1430114402Sru    text_glob *g=new text_glob();
1431151497Sru    if (is_tag)
1432151497Sru      g->text_glob_tag(s, buffer.add_string(html_string),
1433151497Sru		       html_string.length(),
1434151497Sru		       min_vertical, min_horizontal,
1435151497Sru		       max_vertical, max_horizontal);
1436151497Sru    else
1437151497Sru      g->text_glob_special(s, buffer.add_string(html_string),
1438151497Sru			   html_string.length(),
1439151497Sru			   min_vertical, min_horizontal,
1440151497Sru			   max_vertical, max_horizontal);
1441151497Sru    glyphs.add(g, line_number, min_vertical,
1442151497Sru	       min_horizontal, max_vertical, max_horizontal);
1443114402Sru  }
1444114402Sru}
1445114402Sru
1446114402Sru/*
1447114402Sru *  dump_page - dump the page contents for debugging purposes.
1448114402Sru */
1449114402Sru
1450114402Sruvoid page::dump_page(void)
1451114402Sru{
1452114402Sru#if defined(DEBUG_TABLES)
1453114402Sru  text_glob *old_pos = glyphs.get_data();
1454114402Sru  text_glob *g;
1455114402Sru
1456114402Sru  printf("\n<!--\n");
1457114402Sru  printf("\n\ndebugging start\n");
1458114402Sru  glyphs.start_from_head();
1459114402Sru  do {
1460114402Sru    g = glyphs.get_data();
1461114402Sru    if (g->is_tab_ts()) {
1462114402Sru      printf("\n\n");
1463114402Sru      if (g->get_table() != NULL)
1464114402Sru	g->get_table()->dump_table();
1465114402Sru    }
1466114402Sru    printf("%s ", g->text_string);
1467114402Sru    if (g->is_tab_te())
1468114402Sru      printf("\n\n");
1469114402Sru    glyphs.move_right();
1470114402Sru  } while (! glyphs.is_equal_to_head());
1471114402Sru  glyphs.move_to(old_pos);
1472114402Sru  printf("\ndebugging end\n\n");
1473114402Sru  printf("\n-->\n");
1474114402Sru  fflush(stdout);
1475114402Sru#endif
1476114402Sru}
1477114402Sru
1478114402Sru/*
1479114402Sru *  font classes and methods
1480114402Sru */
1481114402Sru
1482114402Sruclass html_font : public font {
1483114402Sru  html_font(const char *);
1484114402Srupublic:
1485114402Sru  int encoding_index;
1486114402Sru  char *encoding;
1487114402Sru  char *reencoded_name;
1488114402Sru  ~html_font();
1489114402Sru  static html_font *load_html_font(const char *);
1490114402Sru};
1491114402Sru
1492114402Sruhtml_font *html_font::load_html_font(const char *s)
1493114402Sru{
1494114402Sru  html_font *f = new html_font(s);
1495114402Sru  if (!f->load()) {
1496114402Sru    delete f;
1497114402Sru    return 0;
1498114402Sru  }
1499114402Sru  return f;
1500114402Sru}
1501114402Sru
1502114402Sruhtml_font::html_font(const char *nm)
1503114402Sru: font(nm)
1504114402Sru{
1505114402Sru}
1506114402Sru
1507114402Sruhtml_font::~html_font()
1508114402Sru{
1509114402Sru}
1510114402Sru
1511114402Sru/*
1512114402Sru *  a simple class to contain the header to this document
1513114402Sru */
1514114402Sru
1515114402Sruclass title_desc {
1516114402Srupublic:
1517114402Sru          title_desc ();
1518114402Sru         ~title_desc ();
1519114402Sru
1520114402Sru  int     has_been_written;
1521114402Sru  int     has_been_found;
1522114402Sru  int     with_h1;
1523114402Sru  string  text;
1524114402Sru};
1525114402Sru
1526114402Sru
1527114402Srutitle_desc::title_desc ()
1528114402Sru  : has_been_written(FALSE), has_been_found(FALSE), with_h1(FALSE)
1529114402Sru{
1530114402Sru}
1531114402Sru
1532114402Srutitle_desc::~title_desc ()
1533114402Sru{
1534114402Sru}
1535114402Sru
1536114402Sruclass header_desc {
1537114402Srupublic:
1538114402Sru                            header_desc ();
1539114402Sru                           ~header_desc ();
1540114402Sru
1541151497Sru  int                       no_of_level_one_headings; // how many .SH or .NH 1 have we found?
1542151497Sru  int                       no_of_headings;           // how many headings have we found?
1543151497Sru  char_buffer               headings;                 // all the headings used in the document
1544151497Sru  list                      headers;                  // list of headers built from .NH and .SH
1545151497Sru  list                      header_filename;          // in which file is this header?
1546151497Sru  int                       header_level;             // current header level
1547151497Sru  int                       written_header;           // have we written the header yet?
1548151497Sru  string                    header_buffer;            // current header text
1549114402Sru
1550114402Sru  void                      write_headings (FILE *f, int force);
1551114402Sru};
1552114402Sru
1553114402Sruheader_desc::header_desc ()
1554151497Sru  :   no_of_level_one_headings(0), no_of_headings(0),
1555151497Sru      header_level(2), written_header(0)
1556114402Sru{
1557114402Sru}
1558114402Sru
1559114402Sruheader_desc::~header_desc ()
1560114402Sru{
1561114402Sru}
1562114402Sru
1563114402Sru/*
1564114402Sru *  write_headings - emits a list of links for the headings in this document
1565114402Sru */
1566114402Sru
1567114402Sruvoid header_desc::write_headings (FILE *f, int force)
1568114402Sru{
1569114402Sru  text_glob *g;
1570114402Sru
1571114402Sru  if (auto_links || force) {
1572114402Sru    if (! headers.is_empty()) {
1573114402Sru      int h=1;
1574114402Sru
1575114402Sru      headers.start_from_head();
1576151497Sru      header_filename.start_from_head();
1577114402Sru      do {
1578114402Sru	g = headers.get_data();
1579151497Sru	fputs("<a href=\"", f);
1580151497Sru	if (multiple_files && (! header_filename.is_empty())) {
1581151497Sru	  text_glob *fn = header_filename.get_data();
1582151497Sru	  fputs(fn->text_string, f);
1583151497Sru	}
1584151497Sru	fputs("#", f);
1585114402Sru	if (simple_anchors) {
1586114402Sru	  string buffer(ANCHOR_TEMPLATE);
1587114402Sru
1588114402Sru	  buffer += as_string(h);
1589114402Sru	  buffer += '\0';
1590222083Sbenl	  fputs(buffer.contents(), f);
1591114402Sru	} else
1592114402Sru	  fputs(g->text_string, f);
1593114402Sru	h++;
1594114402Sru	fputs("\">", f);
1595114402Sru	fputs(g->text_string, f);
1596114402Sru        fputs("</a><br>\n", f);
1597114402Sru	headers.move_right();
1598151497Sru	if (multiple_files && (! header_filename.is_empty()))
1599151497Sru	  header_filename.move_right();
1600114402Sru      } while (! headers.is_equal_to_head());
1601114402Sru      fputs("\n", f);
1602114402Sru    }
1603114402Sru  }
1604114402Sru}
1605114402Sru
1606151497Srustruct assert_pos {
1607151497Sru  assert_pos *next;
1608151497Sru  const char *val;
1609151497Sru  const char *id;
1610151497Sru};
1611151497Sru
1612151497Sruclass assert_state {
1613151497Srupublic:
1614151497Sru        assert_state ();
1615151497Sru        ~assert_state ();
1616151497Sru
1617151497Sru  void  addx (const char *c, const char *i, const char *v,
1618151497Sru	      const char *f, const char *l);
1619151497Sru  void  addy (const char *c, const char *i, const char *v,
1620151497Sru	      const char *f, const char *l);
1621151497Sru  void  build(const char *c, const char *v,
1622151497Sru	      const char *f, const char *l);
1623151497Sru  void  check_br (int br);
1624151497Sru  void  check_ce (int ce);
1625151497Sru  void  check_fi (int fi);
1626151497Sru  void  check_sp (int sp);
1627151497Sru  void  reset    (void);
1628151497Sru
1629151497Sruprivate:
1630151497Sru  int check_br_flag;
1631151497Sru  int check_ce_flag;
1632151497Sru  int check_fi_flag;
1633151497Sru  int check_sp_flag;
1634151497Sru  const char *val_br;
1635151497Sru  const char *val_ce;
1636151497Sru  const char *val_fi;
1637151497Sru  const char *val_sp;
1638151497Sru  const char *file_br;
1639151497Sru  const char *file_ce;
1640151497Sru  const char *file_fi;
1641151497Sru  const char *file_sp;
1642151497Sru  const char *line_br;
1643151497Sru  const char *line_ce;
1644151497Sru  const char *line_fi;
1645151497Sru  const char *line_sp;
1646151497Sru
1647151497Sru  assert_pos *xhead;
1648151497Sru  assert_pos *yhead;
1649151497Sru
1650151497Sru  void add (assert_pos **h,
1651151497Sru	    const char *c, const char *i, const char *v,
1652151497Sru	    const char *f, const char *l);
1653151497Sru  void compare(assert_pos *t,
1654151497Sru	       const char *v, const char *f, const char *l);
1655151497Sru  void close (const char *c);
1656151497Sru  void set (const char *c, const char *v,
1657151497Sru	    const char *f, const char *l);
1658151497Sru  void check_value (const char *s, int v, const char *name,
1659151497Sru		    const char *f, const char *l, int *flag);
1660151497Sru  int check_value_error (int c, int v, const char *s,
1661151497Sru			 const char *name,
1662151497Sru			 const char *f, const char *l, int flag);
1663151497Sru};
1664151497Sru
1665151497Sruassert_state::assert_state ()
1666151497Sru{
1667151497Sru  reset();
1668151497Sru  val_br   = NULL;
1669151497Sru  val_ce   = NULL;
1670151497Sru  val_fi   = NULL;
1671151497Sru  val_sp   = NULL;
1672151497Sru  file_br  = NULL;
1673151497Sru  file_ce  = NULL;
1674151497Sru  file_fi  = NULL;
1675151497Sru  file_sp  = NULL;
1676151497Sru  line_br  = NULL;
1677151497Sru  line_ce  = NULL;
1678151497Sru  line_fi  = NULL;
1679151497Sru  line_sp  = NULL;
1680151497Sru  xhead    = NULL;
1681151497Sru  yhead    = NULL;
1682151497Sru}
1683151497Sru
1684151497Sruassert_state::~assert_state ()
1685151497Sru{
1686151497Sru  assert_pos *t;
1687151497Sru
1688151497Sru  while (xhead != NULL) {
1689151497Sru    t = xhead;
1690151497Sru    xhead = xhead->next;
1691151497Sru    a_delete (char *)t->val;
1692151497Sru    a_delete (char *)t->id;
1693151497Sru    delete t;
1694151497Sru  }
1695151497Sru  while (yhead != NULL) {
1696151497Sru    t = yhead;
1697151497Sru    yhead = yhead->next;
1698151497Sru    a_delete (char *)t->val;
1699151497Sru    a_delete (char *)t->id;
1700151497Sru    delete t;
1701151497Sru  }
1702151497Sru}
1703151497Sru
1704151497Sruvoid assert_state::reset (void)
1705151497Sru{
1706151497Sru  check_br_flag = 0;
1707151497Sru  check_ce_flag = 0;
1708151497Sru  check_fi_flag = 0;
1709151497Sru  check_sp_flag = 0;
1710151497Sru}
1711151497Sru
1712151497Sruvoid assert_state::add (assert_pos **h,
1713151497Sru			const char *c, const char *i, const char *v,
1714151497Sru			const char *f, const char *l)
1715151497Sru{
1716151497Sru  assert_pos *t = *h;
1717151497Sru
1718151497Sru  while (t != NULL) {
1719151497Sru    if (strcmp(t->id, i) == 0)
1720151497Sru      break;
1721151497Sru    t = t->next;
1722151497Sru  }
1723151497Sru  if (t != NULL && v != NULL && (v[0] != '='))
1724151497Sru    compare(t, v, f, l);
1725151497Sru  else {
1726151497Sru    if (t == NULL) {
1727151497Sru      t = new assert_pos;
1728151497Sru      t->next = *h;
1729151497Sru      (*h) = t;
1730151497Sru    }
1731151497Sru    if (v == NULL || v[0] != '=') {
1732151497Sru      if (f == NULL)
1733151497Sru	f = "stdin";
1734151497Sru      if (l == NULL)
1735151497Sru	l = "<none>";
1736151497Sru      if (v == NULL)
1737151497Sru	v = "no value at all";
1738151497Sru      fprintf(stderr, "%s:%s:error in assert format of id=%s expecting value to be prefixed with an `=' got %s\n",
1739151497Sru	      f, l, i, v);
1740151497Sru    }
1741151497Sru    t->id = i;
1742151497Sru    t->val = v;
1743151497Sru    a_delete (char *)c;
1744151497Sru    a_delete (char *)f;
1745151497Sru    a_delete (char *)l;
1746151497Sru  }
1747151497Sru}
1748151497Sru
1749151497Sruvoid assert_state::addx (const char *c, const char *i, const char *v,
1750151497Sru			 const char *f, const char *l)
1751151497Sru{
1752151497Sru  add(&xhead, c, i, v, f, l);
1753151497Sru}
1754151497Sru
1755151497Sruvoid assert_state::addy (const char *c, const char *i, const char *v,
1756151497Sru			 const char *f, const char *l)
1757151497Sru{
1758151497Sru  add(&yhead, c, i, v, f, l);
1759151497Sru}
1760151497Sru
1761151497Sruvoid assert_state::compare(assert_pos *t,
1762151497Sru			   const char *v, const char *f, const char *l)
1763151497Sru{
1764151497Sru  const char *s=t->val;
1765151497Sru
1766151497Sru  while ((*v) == '=')
1767151497Sru    v++;
1768151497Sru  while ((*s) == '=')
1769151497Sru    s++;
1770151497Sru
1771151497Sru  if (strcmp(v, s) != 0) {
1772151497Sru    if (f == NULL)
1773151497Sru      f = "stdin";
1774151497Sru    if (l == NULL)
1775151497Sru      l = "<none>";
1776151497Sru    fprintf(stderr, "%s:%s: grohtml assertion failed at id%s expecting %s and was given %s\n",
1777151497Sru	    f, l, t->id, s, v);
1778151497Sru  }
1779151497Sru}
1780151497Sru
1781151497Sruvoid assert_state::close (const char *c)
1782151497Sru{
1783151497Sru  if (strcmp(c, "sp") == 0)
1784151497Sru    check_sp_flag = 0;
1785151497Sru  else if (strcmp(c, "br") == 0)
1786151497Sru    check_br_flag = 0;
1787151497Sru  else if (strcmp(c, "fi") == 0)
1788151497Sru    check_fi_flag = 0;
1789151497Sru  else if (strcmp(c, "nf") == 0)
1790151497Sru    check_fi_flag = 0;
1791151497Sru  else if (strcmp(c, "ce") == 0)
1792151497Sru    check_ce_flag = 0;
1793151497Sru  else
1794151497Sru    fprintf(stderr, "internal error: unrecognised tag in grohtml (%s)\n", c);
1795151497Sru}
1796151497Sru
1797151497Sruconst char *replace_negate_str (const char *before, char *after)
1798151497Sru{
1799151497Sru  if (before != NULL)
1800151497Sru    a_delete (char *)before;
1801151497Sru
1802151497Sru  if (strlen(after) > 0) {
1803151497Sru    int d = atoi(after);
1804151497Sru
1805151497Sru    if (d < 0 || d > 1) {
1806151497Sru      fprintf(stderr, "expecting nf/fi value to be 0 or 1 not %d\n", d);
1807151497Sru      d = 0;
1808151497Sru    }
1809151497Sru    if (d == 0)
1810151497Sru      after[0] = '1';
1811151497Sru    else
1812151497Sru      after[0] = '0';
1813151497Sru    after[1] = (char)0;
1814151497Sru  }
1815151497Sru  return after;
1816151497Sru}
1817151497Sru
1818151497Sruconst char *replace_str (const char *before, const char *after)
1819151497Sru{
1820151497Sru  if (before != NULL)
1821151497Sru    a_delete (char *)before;
1822151497Sru  return after;
1823151497Sru}
1824151497Sru
1825151497Sruvoid assert_state::set (const char *c, const char *v,
1826151497Sru			const char *f, const char *l)
1827151497Sru{
1828151497Sru  if (l == NULL)
1829151497Sru    l = "<none>";
1830151497Sru  if (f == NULL)
1831151497Sru    f = "stdin";
1832151497Sru
1833151497Sru  // fprintf(stderr, "%s:%s:setting %s to %s\n", f, l, c, v);
1834151497Sru  if (strcmp(c, "sp") == 0) {
1835151497Sru    check_sp_flag = 1;
1836151497Sru    val_sp = replace_str(val_sp, strsave(v));
1837151497Sru    file_sp = replace_str(file_sp, strsave(f));
1838151497Sru    line_sp = replace_str(line_sp, strsave(l));
1839151497Sru  } else if (strcmp(c, "br") == 0) {
1840151497Sru    check_br_flag = 1;
1841151497Sru    val_br = replace_str(val_br, strsave(v));
1842151497Sru    file_br = replace_str(file_br, strsave(f));
1843151497Sru    line_br = replace_str(line_br, strsave(l));
1844151497Sru  } else if (strcmp(c, "fi") == 0) {
1845151497Sru    check_fi_flag = 1;
1846151497Sru    val_fi = replace_str(val_fi, strsave(v));
1847151497Sru    file_fi = replace_str(file_fi, strsave(f));
1848151497Sru    line_fi = replace_str(line_fi, strsave(l));
1849151497Sru  } else if (strcmp(c, "nf") == 0) {
1850151497Sru    check_fi_flag = 1;
1851151497Sru    val_fi = replace_negate_str(val_fi, strsave(v));
1852151497Sru    file_fi = replace_str(file_fi, strsave(f));
1853151497Sru    line_fi = replace_str(line_fi, strsave(l));
1854151497Sru  } else if (strcmp(c, "ce") == 0) {
1855151497Sru    check_ce_flag = 1;
1856151497Sru    val_ce = replace_str(val_ce, strsave(v));
1857151497Sru    file_ce = replace_str(file_ce, strsave(f));
1858151497Sru    line_ce = replace_str(line_ce, strsave(l));
1859151497Sru  }
1860151497Sru}
1861151497Sru
1862151497Sru/*
1863151497Sru *  build - builds the troff state assertion.
1864151497Sru *          see tmac/www.tmac for cmd examples.
1865151497Sru */
1866151497Sru
1867151497Sruvoid assert_state::build (const char *c, const char *v,
1868151497Sru			  const char *f, const char *l)
1869151497Sru{
1870151497Sru  if (c[0] == '{')
1871151497Sru    set(&c[1], v, f, l);
1872151497Sru  if (c[0] == '}')
1873151497Sru    close(&c[1]);
1874151497Sru}
1875151497Sru
1876151497Sruint assert_state::check_value_error (int c, int v, const char *s,
1877151497Sru				     const char *name,
1878151497Sru				     const char *f, const char *l, int flag)
1879151497Sru{
1880151497Sru  if (! c) {
1881151497Sru    if (f == NULL)
1882151497Sru      f = "stdin";
1883151497Sru    if (l == NULL)
1884151497Sru      l = "<none>";
1885151497Sru    fprintf(stderr, "%s:%s:grohtml (troff state) assertion failed, expected %s to be %s but found it to contain %d\n",
1886151497Sru	    f, l, name, s, v);
1887151497Sru    return 0;
1888151497Sru  }
1889151497Sru  return flag;
1890151497Sru}
1891151497Sru
1892151497Sruvoid assert_state::check_value (const char *s, int v, const char *name,
1893151497Sru				const char *f, const char *l, int *flag)
1894151497Sru{
1895151497Sru  if (strncmp(s, "<=", 2) == 0)
1896151497Sru    *flag = check_value_error(v <= atoi(&s[2]), v, s, name, f, l, *flag);
1897151497Sru  else if (strncmp(s, ">=", 2) == 0)
1898151497Sru    *flag = check_value_error(v >= atoi(&s[2]), v, s, name, f, l, *flag);
1899151497Sru  else if (strncmp(s, "==", 2) == 0)
1900151497Sru    *flag = check_value_error(v == atoi(&s[2]), v, s, name, f, l, *flag);
1901151497Sru  else if (strncmp(s, "!=", 2) == 0)
1902151497Sru    *flag = check_value_error(v != atoi(&s[2]), v, s, name, f, l, *flag);
1903151497Sru  else if (strncmp(s, "<", 1) == 0)
1904151497Sru    *flag = check_value_error(v < atoi(&s[2]), v, s, name, f, l, *flag);
1905151497Sru  else if (strncmp(s, ">", 1) == 0)
1906151497Sru    *flag = check_value_error(v > atoi(&s[2]), v, s, name, f, l, *flag);
1907151497Sru  else if (strncmp(s, "=", 1) == 0)
1908151497Sru    *flag = check_value_error(v == atoi(&s[1]), v, s, name, f, l, *flag);
1909151497Sru  else
1910151497Sru    *flag = check_value_error(v == atoi(s), v, s, name, f, l, *flag);
1911151497Sru}
1912151497Sru
1913151497Sruvoid assert_state::check_sp (int sp)
1914151497Sru{
1915151497Sru  if (check_sp_flag)
1916151497Sru    check_value(val_sp, sp, "sp", file_sp, line_sp, &check_sp_flag);
1917151497Sru}
1918151497Sru
1919151497Sruvoid assert_state::check_fi (int fi)
1920151497Sru{
1921151497Sru  if (check_fi_flag)
1922151497Sru    check_value(val_fi, fi, "fi", file_fi, line_fi, &check_fi_flag);
1923151497Sru}
1924151497Sru
1925151497Sruvoid assert_state::check_br (int br)
1926151497Sru{
1927151497Sru  if (check_br_flag)
1928151497Sru    check_value(val_br, br, "br", file_br, line_br, &check_br_flag);
1929151497Sru}
1930151497Sru
1931151497Sruvoid assert_state::check_ce (int ce)
1932151497Sru{
1933151497Sru  if (check_ce_flag)
1934151497Sru    check_value(val_ce, ce, "ce", file_ce, line_ce, &check_ce_flag);
1935151497Sru}
1936151497Sru
1937114402Sruclass html_printer : public printer {
1938114402Sru  files                file_list;
1939114402Sru  simple_output        html;
1940114402Sru  int                  res;
1941114402Sru  int                  space_char_index;
1942114402Sru  int                  space_width;
1943114402Sru  int                  no_of_printed_pages;
1944114402Sru  int                  paper_length;
1945114402Sru  string               sbuf;
1946114402Sru  int                  sbuf_start_hpos;
1947114402Sru  int                  sbuf_vpos;
1948114402Sru  int                  sbuf_end_hpos;
1949114402Sru  int                  sbuf_prev_hpos;
1950114402Sru  int                  sbuf_kern;
1951114402Sru  style                sbuf_style;
1952114402Sru  int                  last_sbuf_length;
1953114402Sru  int                  overstrike_detected;
1954114402Sru  style                output_style;
1955114402Sru  int                  output_hpos;
1956114402Sru  int                  output_vpos;
1957114402Sru  int                  output_vpos_max;
1958114402Sru  int                  output_draw_point_size;
1959114402Sru  int                  line_thickness;
1960114402Sru  int                  output_line_thickness;
1961114402Sru  unsigned char        output_space_code;
1962114402Sru  char                *inside_font_style;
1963114402Sru  int                  page_number;
1964114402Sru  title_desc           title;
1965114402Sru  header_desc          header;
1966114402Sru  int                  header_indent;
1967114402Sru  int                  supress_sub_sup;
1968114402Sru  int                  cutoff_heading;
1969114402Sru  page                *page_contents;
1970114402Sru  html_text           *current_paragraph;
1971114402Sru  html_indent         *indent;
1972114402Sru  html_table          *table;
1973114402Sru  int                  end_center;
1974114402Sru  int                  end_tempindent;
1975114402Sru  TAG_ALIGNMENT        next_tag;
1976114402Sru  int                  fill_on;
1977114402Sru  int                  max_linelength;
1978114402Sru  int                  linelength;
1979114402Sru  int                  pageoffset;
1980151497Sru  int                  troff_indent;
1981151497Sru  int                  device_indent;
1982151497Sru  int                  temp_indent;
1983114402Sru  int                  pointsize;
1984114402Sru  int                  vertical_spacing;
1985114402Sru  int                  line_number;
1986114402Sru  color               *background;
1987151497Sru  int                  seen_indent;
1988151497Sru  int                  next_indent;
1989151497Sru  int                  seen_pageoffset;
1990151497Sru  int                  next_pageoffset;
1991151497Sru  int                  seen_linelength;
1992151497Sru  int                  next_linelength;
1993151497Sru  int                  seen_center;
1994151497Sru  int                  next_center;
1995151497Sru  int                  seen_space;
1996151497Sru  int                  seen_break;
1997151497Sru  int                  current_column;
1998151497Sru  int                  row_space;
1999151497Sru  assert_state         as;
2000114402Sru
2001114402Sru  void  flush_sbuf                    ();
2002114402Sru  void  set_style                     (const style &);
2003114402Sru  void  set_space_code                (unsigned char c);
2004114402Sru  void  do_exec                       (char *, const environment *);
2005114402Sru  void  do_import                     (char *, const environment *);
2006114402Sru  void  do_def                        (char *, const environment *);
2007114402Sru  void  do_mdef                       (char *, const environment *);
2008114402Sru  void  do_file                       (char *, const environment *);
2009114402Sru  void  set_line_thickness            (const environment *);
2010114402Sru  void  terminate_current_font        (void);
2011114402Sru  void  flush_font                    (void);
2012114402Sru  void  add_to_sbuf                   (int index, const string &s);
2013114402Sru  void  write_title                   (int in_head);
2014114402Sru  int   sbuf_continuation             (int index, const char *name, const environment *env, int w);
2015114402Sru  void  flush_page                    (void);
2016114402Sru  void  troff_tag                     (text_glob *g);
2017114402Sru  void  flush_globs                   (void);
2018114402Sru  void  emit_line                     (text_glob *g);
2019114402Sru  void  emit_raw                      (text_glob *g);
2020114402Sru  void  emit_html                     (text_glob *g);
2021114402Sru  void  determine_space               (text_glob *g);
2022114402Sru  void  start_font                    (const char *name);
2023114402Sru  void  end_font                      (const char *name);
2024114402Sru  int   is_font_courier               (font *f);
2025151497Sru  int   is_line_start                 (int nf);
2026114402Sru  int   is_courier_until_eol          (void);
2027114402Sru  void  start_size                    (int from, int to);
2028114402Sru  void  do_font                       (text_glob *g);
2029114402Sru  void  do_center                     (char *arg);
2030151497Sru  void  do_check_center               (void);
2031114402Sru  void  do_break                      (void);
2032151497Sru  void  do_space                      (char *arg);
2033114402Sru  void  do_eol                        (void);
2034114402Sru  void  do_eol_ce                     (void);
2035114402Sru  void  do_title                      (void);
2036151497Sru  void  do_fill                       (char *arg);
2037114402Sru  void  do_heading                    (char *arg);
2038114402Sru  void  write_header                  (void);
2039114402Sru  void  determine_header_level        (int level);
2040114402Sru  void  do_linelength                 (char *arg);
2041114402Sru  void  do_pageoffset                 (char *arg);
2042114402Sru  void  do_indentation                (char *arg);
2043114402Sru  void  do_tempindent                 (char *arg);
2044114402Sru  void  do_indentedparagraph          (void);
2045114402Sru  void  do_verticalspacing            (char *arg);
2046114402Sru  void  do_pointsize                  (char *arg);
2047114402Sru  void  do_centered_image             (void);
2048114402Sru  void  do_left_image                 (void);
2049114402Sru  void  do_right_image                (void);
2050114402Sru  void  do_auto_image                 (text_glob *g, const char *filename);
2051114402Sru  void  do_links                      (void);
2052114402Sru  void  do_flush                      (void);
2053151497Sru  void  do_job_name                   (char *name);
2054151497Sru  void  do_head                       (char *name);
2055151497Sru  void  insert_split_file             (void);
2056114402Sru  int   is_in_middle                  (int left, int right);
2057114402Sru  void  do_sup_or_sub                 (text_glob *g);
2058114402Sru  int   start_subscript               (text_glob *g);
2059114402Sru  int   end_subscript                 (text_glob *g);
2060114402Sru  int   start_superscript             (text_glob *g);
2061114402Sru  int   end_superscript               (text_glob *g);
2062114402Sru  void  outstanding_eol               (int n);
2063114402Sru  int   is_bold                       (font *f);
2064114402Sru  font *make_bold                     (font *f);
2065114402Sru  int   overstrike                    (int index, const char *name, const environment *env, int w);
2066114402Sru  void  do_body                       (void);
2067114402Sru  int   next_horiz_pos                (text_glob *g, int nf);
2068114402Sru  void  lookahead_for_tables          (void);
2069114402Sru  void  insert_tab_te                 (void);
2070114402Sru  text_glob *insert_tab_ts            (text_glob *where);
2071114402Sru  void insert_tab0_foreach_tab        (void);
2072114402Sru  void insert_tab_0                   (text_glob *where);
2073114402Sru  void do_indent                      (int in, int pageoff, int linelen);
2074114402Sru  void shutdown_table                 (void);
2075114402Sru  void do_tab_ts                      (text_glob *g);
2076114402Sru  void do_tab_te                      (void);
2077114402Sru  void do_col                         (char *s);
2078114402Sru  void do_tab                         (char *s);
2079114402Sru  void do_tab0                        (void);
2080114402Sru  int  calc_nf                        (text_glob *g, int nf);
2081114402Sru  void calc_po_in                     (text_glob *g, int nf);
2082114402Sru  void remove_tabs                    (void);
2083114402Sru  void remove_courier_tabs            (void);
2084114402Sru  void update_min_max                 (colType type_of_col, int *minimum, int *maximum, text_glob *g);
2085114402Sru  void add_table_end                  (const char *);
2086151497Sru  void do_file_components             (void);
2087151497Sru  void write_navigation               (const string &top, const string &prev,
2088151497Sru				       const string &next, const string &current);
2089151497Sru  void emit_link                      (const string &to, const char *name);
2090151497Sru  int  get_troff_indent               (void);
2091151497Sru  void restore_troff_indent           (void);
2092151497Sru  void handle_assertion               (int minv, int minh, int maxv, int maxh, const char *s);
2093151497Sru  void handle_state_assertion         (text_glob *g);
2094151497Sru  void do_end_para                    (text_glob *g);
2095151497Sru  int  round_width                    (int x);
2096151497Sru  void handle_tag_within_title        (text_glob *g);
2097151497Sru  void writeHeadMetaStyle             (void);
2098114402Sru  // ADD HERE
2099114402Sru
2100114402Srupublic:
2101114402Sru  html_printer          ();
2102114402Sru  ~html_printer         ();
2103114402Sru  void set_char         (int i, font *f, const environment *env, int w, const char *name);
2104114402Sru  void set_numbered_char(int num, const environment *env, int *widthp);
2105151497Sru  int set_char_and_width(const char *nm, const environment *env,
2106151497Sru			 int *widthp, font **f);
2107114402Sru  void draw             (int code, int *p, int np, const environment *env);
2108114402Sru  void begin_page       (int);
2109114402Sru  void end_page         (int);
2110114402Sru  void special          (char *arg, const environment *env, char type);
2111151497Sru  void devtag           (char *arg, const environment *env, char type);
2112114402Sru  font *make_font       (const char *);
2113114402Sru  void end_of_line      ();
2114114402Sru};
2115114402Sru
2116114402Sruprinter *make_printer()
2117114402Sru{
2118114402Sru  return new html_printer;
2119114402Sru}
2120114402Sru
2121114402Srustatic void usage(FILE *stream);
2122114402Sru
2123114402Sruvoid html_printer::set_style(const style &sty)
2124114402Sru{
2125114402Sru  const char *fontname = sty.f->get_name();
2126114402Sru  if (fontname == NULL)
2127114402Sru    fatal("no internalname specified for font");
2128114402Sru
2129114402Sru#if 0
2130114402Sru  change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size);
2131114402Sru#endif
2132114402Sru}
2133114402Sru
2134114402Sru/*
2135114402Sru *  is_bold - returns TRUE if font, f, is bold.
2136114402Sru */
2137114402Sru
2138114402Sruint html_printer::is_bold (font *f)
2139114402Sru{
2140114402Sru  const char *fontname = f->get_name();
2141114402Sru  return (strcmp(fontname, "B") == 0) || (strcmp(fontname, "BI") == 0);
2142114402Sru}
2143114402Sru
2144114402Sru/*
2145114402Sru *  make_bold - if a bold font of, f, exists then return it.
2146114402Sru */
2147114402Sru
2148114402Srufont *html_printer::make_bold (font *f)
2149114402Sru{
2150114402Sru  const char *fontname = f->get_name();
2151114402Sru
2152114402Sru  if (strcmp(fontname, "B") == 0)
2153114402Sru    return f;
2154114402Sru  if (strcmp(fontname, "I") == 0)
2155114402Sru    return font::load_font("BI");
2156114402Sru  if (strcmp(fontname, "BI") == 0)
2157114402Sru    return f;
2158114402Sru  return NULL;
2159114402Sru}
2160114402Sru
2161114402Sruvoid html_printer::end_of_line()
2162114402Sru{
2163114402Sru  flush_sbuf();
2164114402Sru  line_number++;
2165114402Sru}
2166114402Sru
2167114402Sru/*
2168114402Sru *  emit_line - writes out a horizontal rule.
2169114402Sru */
2170114402Sru
2171114402Sruvoid html_printer::emit_line (text_glob *)
2172114402Sru{
2173114402Sru  // --fixme-- needs to know the length in percentage
2174114402Sru  html.put_string("<hr>");
2175114402Sru}
2176114402Sru
2177114402Sru/*
2178151497Sru *  restore_troff_indent - is called when we have temporarily shutdown
2179151497Sru *                         indentation (typically done when we have
2180151497Sru *                         centered an image).
2181151497Sru */
2182151497Sru
2183151497Sruvoid html_printer::restore_troff_indent (void)
2184151497Sru{
2185151497Sru  troff_indent = next_indent;
2186151497Sru  if (troff_indent > 0) {
2187151497Sru    /*
2188151497Sru     *  force device indentation
2189151497Sru     */
2190151497Sru    device_indent = 0;
2191151497Sru    do_indent(get_troff_indent(), pageoffset, linelength);
2192151497Sru  }
2193151497Sru}
2194151497Sru
2195151497Sru/*
2196114402Sru *  emit_raw - writes the raw html information directly to the device.
2197114402Sru */
2198114402Sru
2199114402Sruvoid html_printer::emit_raw (text_glob *g)
2200114402Sru{
2201114402Sru  do_font(g);
2202114402Sru  if (next_tag == INLINE) {
2203114402Sru    determine_space(g);
2204114402Sru    current_paragraph->do_emittext(g->text_string, g->text_length);
2205114402Sru  } else {
2206151497Sru    int space = current_paragraph->retrieve_para_space() || seen_space;
2207151497Sru
2208114402Sru    current_paragraph->done_para();
2209151497Sru    shutdown_table();
2210114402Sru    switch (next_tag) {
2211114402Sru
2212114402Sru    case CENTERED:
2213151497Sru      current_paragraph->do_para("align=center", space);
2214114402Sru      break;
2215114402Sru    case LEFT:
2216151497Sru      current_paragraph->do_para(&html, "align=left", get_troff_indent(), pageoffset, linelength, space);
2217114402Sru      break;
2218114402Sru    case RIGHT:
2219151497Sru      current_paragraph->do_para(&html, "align=right", get_troff_indent(), pageoffset, linelength, space);
2220114402Sru      break;
2221114402Sru    default:
2222114402Sru      fatal("unknown enumeration");
2223114402Sru    }
2224114402Sru    current_paragraph->do_emittext(g->text_string, g->text_length);
2225114402Sru    current_paragraph->done_para();
2226114402Sru    next_tag        = INLINE;
2227114402Sru    supress_sub_sup = TRUE;
2228151497Sru    seen_space      = FALSE;
2229151497Sru    restore_troff_indent();
2230114402Sru  }
2231114402Sru}
2232114402Sru
2233114402Sru/*
2234151497Sru *  handle_tag_within_title - handle a limited number of tags within
2235151497Sru *                            the context of a table. Those tags which
2236151497Sru *                            set values rather than generate spaces
2237151497Sru *                            and paragraphs.
2238151497Sru */
2239151497Sru
2240151497Sruvoid html_printer::handle_tag_within_title (text_glob *g)
2241151497Sru{
2242151497Sru  if (g->is_in() || g->is_ti() || g->is_po() || g->is_ce() || g->is_ll()
2243151497Sru      || g->is_fi() || g->is_nf())
2244151497Sru    troff_tag(g);
2245151497Sru}
2246151497Sru
2247151497Sru/*
2248114402Sru *  do_center - handle the .ce commands from troff.
2249114402Sru */
2250114402Sru
2251114402Sruvoid html_printer::do_center (char *arg)
2252114402Sru{
2253151497Sru  next_center = atoi(arg);
2254151497Sru  seen_center = TRUE;
2255114402Sru}
2256114402Sru
2257114402Sru/*
2258151497Sru *  do_centered_image - set a flag such that the next devtag is
2259114402Sru *                      placed inside a centered paragraph.
2260114402Sru */
2261114402Sru
2262114402Sruvoid html_printer::do_centered_image (void)
2263114402Sru{
2264114402Sru  next_tag = CENTERED;
2265114402Sru}
2266114402Sru
2267114402Sru/*
2268151497Sru *  do_right_image - set a flag such that the next devtag is
2269114402Sru *                   placed inside a right aligned paragraph.
2270114402Sru */
2271114402Sru
2272114402Sruvoid html_printer::do_right_image (void)
2273114402Sru{
2274114402Sru  next_tag = RIGHT;
2275114402Sru}
2276114402Sru
2277114402Sru/*
2278151497Sru *  do_left_image - set a flag such that the next devtag is
2279114402Sru *                  placed inside a left aligned paragraph.
2280114402Sru */
2281114402Sru
2282114402Sruvoid html_printer::do_left_image (void)
2283114402Sru{
2284114402Sru  next_tag = LEFT;
2285114402Sru}
2286114402Sru
2287114402Sru/*
2288114402Sru *  exists - returns TRUE if filename exists.
2289114402Sru */
2290114402Sru
2291114402Srustatic int exists (const char *filename)
2292114402Sru{
2293114402Sru  FILE *fp = fopen(filename, "r");
2294114402Sru
2295114402Sru  if (fp == 0) {
2296114402Sru    return( FALSE );
2297114402Sru  } else {
2298114402Sru    fclose(fp);
2299114402Sru    return( TRUE );
2300114402Sru  }
2301114402Sru}
2302114402Sru
2303114402Sru/*
2304114402Sru *  generate_img_src - returns a html image tag for the filename
2305114402Sru *                     providing that the image exists.
2306114402Sru */
2307114402Sru
2308114402Srustatic string &generate_img_src (const char *filename)
2309114402Sru{
2310114402Sru  string *s = new string("");
2311114402Sru
2312114402Sru  while (filename && (filename[0] == ' ')) {
2313114402Sru    filename++;
2314114402Sru  }
2315114402Sru  if (exists(filename))
2316151497Sru    *s += string("<img src=\"") + filename + "\" "
2317151497Sru	  + "alt=\"Image " + filename + "\">";
2318114402Sru  return *s;
2319114402Sru}
2320114402Sru
2321114402Sru/*
2322114402Sru *  do_auto_image - tests whether the image, indicated by filename,
2323114402Sru *                  is present, if so then it emits an html image tag.
2324114402Sru *                  An image tag may be passed through from pic, eqn
2325114402Sru *                  but the corresponding image might not be created.
2326114402Sru *                  Consider .EQ delim $$ .EN  or an empty .PS .PE.
2327114402Sru */
2328114402Sru
2329114402Sruvoid html_printer::do_auto_image (text_glob *g, const char *filename)
2330114402Sru{
2331114402Sru  string buffer = generate_img_src(filename);
2332114402Sru
2333114402Sru  if (! buffer.empty()) {
2334114402Sru    /*
2335114402Sru     *  utilize emit_raw by creating a new text_glob.
2336114402Sru     */
2337114402Sru    text_glob h = *g;
2338114402Sru
2339114402Sru    h.text_string = buffer.contents();
2340114402Sru    h.text_length = buffer.length();
2341114402Sru    emit_raw(&h);
2342114402Sru  } else
2343114402Sru    next_tag = INLINE;
2344114402Sru}
2345114402Sru
2346114402Sru/*
2347114402Sru *  outstanding_eol - call do_eol, n, times.
2348114402Sru */
2349114402Sru
2350114402Sruvoid html_printer::outstanding_eol (int n)
2351114402Sru{
2352114402Sru  while (n > 0) {
2353114402Sru    do_eol();
2354114402Sru    n--;
2355114402Sru  }
2356114402Sru}
2357114402Sru
2358114402Sru/*
2359114402Sru *  do_title - handle the .tl commands from troff.
2360114402Sru */
2361114402Sru
2362114402Sruvoid html_printer::do_title (void)
2363114402Sru{
2364114402Sru  text_glob    *t;
2365114402Sru  int           removed_from_head;
2366114402Sru
2367114402Sru  if (page_number == 1) {
2368114402Sru    int found_title_start  = FALSE;
2369114402Sru    if (! page_contents->glyphs.is_empty()) {
2370114402Sru      page_contents->glyphs.sub_move_right();       /* move onto next word */
2371114402Sru      do {
2372114402Sru	t = page_contents->glyphs.get_data();
2373114402Sru	removed_from_head = FALSE;
2374114402Sru	if (t->is_auto_img()) {
2375114402Sru	  string img = generate_img_src((char *)(t->text_string + 20));
2376114402Sru
2377114402Sru	  if (! img.empty()) {
2378114402Sru	    if (found_title_start)
2379114402Sru	      title.text += " ";
2380114402Sru	    found_title_start = TRUE;
2381114402Sru	    title.has_been_found = TRUE;
2382114402Sru	    title.text += img;
2383114402Sru	  }
2384114402Sru	  page_contents->glyphs.sub_move_right(); 	  /* move onto next word */
2385114402Sru	  removed_from_head = ((!page_contents->glyphs.is_empty()) &&
2386114402Sru			       (page_contents->glyphs.is_equal_to_head()));
2387151497Sru	} else if (t->is_eo_tl()) {
2388114402Sru	  /* end of title found
2389114402Sru	   */
2390114402Sru	  title.has_been_found = TRUE;
2391114402Sru	  return;
2392114402Sru	} else if (t->is_a_tag()) {
2393151497Sru	  handle_tag_within_title(t);
2394151497Sru	  page_contents->glyphs.sub_move_right(); 	  /* move onto next word */
2395151497Sru	  removed_from_head = ((!page_contents->glyphs.is_empty()) &&
2396151497Sru			       (page_contents->glyphs.is_equal_to_head()));
2397114402Sru	} else if (found_title_start) {
2398114402Sru	  title.text += " " + string(t->text_string, t->text_length);
2399114402Sru	  page_contents->glyphs.sub_move_right(); 	  /* move onto next word */
2400114402Sru	  removed_from_head = ((!page_contents->glyphs.is_empty()) &&
2401114402Sru			       (page_contents->glyphs.is_equal_to_head()));
2402114402Sru	} else {
2403114402Sru	  title.text += string(t->text_string, t->text_length);
2404114402Sru	  found_title_start    = TRUE;
2405114402Sru	  title.has_been_found = TRUE;
2406114402Sru	  page_contents->glyphs.sub_move_right(); 	  /* move onto next word */
2407114402Sru	  removed_from_head = ((!page_contents->glyphs.is_empty()) &&
2408114402Sru			       (page_contents->glyphs.is_equal_to_head()));
2409114402Sru	}
2410151497Sru      } while ((! page_contents->glyphs.is_equal_to_head()) ||
2411151497Sru	       (removed_from_head));
2412114402Sru    }
2413114402Sru  }
2414114402Sru}
2415114402Sru
2416114402Sruvoid html_printer::write_header (void)
2417114402Sru{
2418114402Sru  if (! header.header_buffer.empty()) {
2419151497Sru    int space = current_paragraph->retrieve_para_space() || seen_space;
2420151497Sru
2421114402Sru    if (header.header_level > 7) {
2422114402Sru      header.header_level = 7;
2423114402Sru    }
2424114402Sru
2425114402Sru    // firstly we must terminate any font and type faces
2426114402Sru    current_paragraph->done_para();
2427114402Sru    supress_sub_sup = TRUE;
2428114402Sru
2429114402Sru    if (cutoff_heading+2 > header.header_level) {
2430114402Sru      // now we save the header so we can issue a list of links
2431114402Sru      header.no_of_headings++;
2432114402Sru      style st;
2433114402Sru
2434114402Sru      text_glob *h=new text_glob();
2435114402Sru      h->text_glob_html(&st,
2436114402Sru			header.headings.add_string(header.header_buffer),
2437114402Sru			header.header_buffer.length(),
2438114402Sru			header.no_of_headings, header.header_level,
2439114402Sru			header.no_of_headings, header.header_level);
2440114402Sru
2441114402Sru      header.headers.add(h,
2442114402Sru			 header.no_of_headings,
2443114402Sru			 header.no_of_headings, header.no_of_headings,
2444114402Sru			 header.no_of_headings, header.no_of_headings);   // and add this header to the header list
2445114402Sru
2446114402Sru      // lastly we generate a tag
2447114402Sru
2448151497Sru      html.nl().nl().put_string("<a name=\"");
2449114402Sru      if (simple_anchors) {
2450114402Sru	string buffer(ANCHOR_TEMPLATE);
2451114402Sru
2452114402Sru	buffer += as_string(header.no_of_headings);
2453114402Sru	buffer += '\0';
2454114402Sru	html.put_string(buffer.contents());
2455114402Sru      } else {
2456114402Sru	html.put_string(header.header_buffer);
2457114402Sru      }
2458114402Sru      html.put_string("\"></a>").nl();
2459114402Sru    }
2460114402Sru
2461114402Sru    if (manufacture_headings) {
2462114402Sru      // line break before a header
2463114402Sru      if (!current_paragraph->emitted_text())
2464114402Sru	current_paragraph->do_space();
2465114402Sru      // user wants manufactured headings which look better than <Hn></Hn>
2466114402Sru      if (header.header_level<4) {
2467114402Sru	html.put_string("<b><font size=\"+1\">");
2468114402Sru	html.put_string(header.header_buffer);
2469114402Sru	html.put_string("</font></b>").nl();
2470114402Sru      }
2471114402Sru      else {
2472114402Sru	html.put_string("<b>");
2473114402Sru	html.put_string(header.header_buffer);
2474114402Sru	html.put_string("</b>").nl();
2475114402Sru      }
2476114402Sru    }
2477114402Sru    else {
2478114402Sru      // and now we issue the real header
2479114402Sru      html.put_string("<h");
2480114402Sru      html.put_number(header.header_level);
2481114402Sru      html.put_string(">");
2482114402Sru      html.put_string(header.header_buffer);
2483114402Sru      html.put_string("</h");
2484114402Sru      html.put_number(header.header_level);
2485114402Sru      html.put_string(">").nl();
2486114402Sru    }
2487114402Sru
2488151497Sru    /* and now we save the file name in which this header will occur */
2489151497Sru
2490151497Sru    style st;   // fake style to enable us to use the list data structure
2491151497Sru
2492151497Sru    text_glob *h=new text_glob();
2493151497Sru    h->text_glob_html(&st,
2494151497Sru		      header.headings.add_string(file_list.file_name()),
2495151497Sru		      file_list.file_name().length(),
2496151497Sru		      header.no_of_headings, header.header_level,
2497151497Sru		      header.no_of_headings, header.header_level);
2498151497Sru
2499151497Sru    header.header_filename.add(h,
2500151497Sru			       header.no_of_headings,
2501151497Sru			       header.no_of_headings, header.no_of_headings,
2502151497Sru			       header.no_of_headings, header.no_of_headings);
2503151497Sru
2504151497Sru    current_paragraph->do_para(&html, "", get_troff_indent(), pageoffset, linelength, space);
2505114402Sru  }
2506114402Sru}
2507114402Sru
2508114402Sruvoid html_printer::determine_header_level (int level)
2509114402Sru{
2510114402Sru  if (level == 0) {
2511114402Sru    int i;
2512114402Sru
2513114402Sru    for (i=0; ((i<header.header_buffer.length())
2514114402Sru	       && ((header.header_buffer[i] == '.')
2515114402Sru		   || is_digit(header.header_buffer[i]))) ; i++) {
2516114402Sru      if (header.header_buffer[i] == '.') {
2517114402Sru	level++;
2518114402Sru      }
2519114402Sru    }
2520114402Sru  }
2521114402Sru  header.header_level = level+1;
2522151497Sru  if (header.header_level >= 2 && header.header_level <= split_level) {
2523151497Sru    header.no_of_level_one_headings++;
2524151497Sru    insert_split_file();
2525151497Sru  }
2526114402Sru}
2527114402Sru
2528114402Sru/*
2529114402Sru *  do_heading - handle the .SH and .NH and equivalent commands from troff.
2530114402Sru */
2531114402Sru
2532114402Sruvoid html_printer::do_heading (char *arg)
2533114402Sru{
2534114402Sru  text_glob *g;
2535114402Sru  int  level=atoi(arg);
2536151497Sru  int  horiz;
2537114402Sru
2538114402Sru  header.header_buffer.clear();
2539114402Sru  page_contents->glyphs.move_right();
2540114402Sru  if (! page_contents->glyphs.is_equal_to_head()) {
2541114402Sru    g = page_contents->glyphs.get_data();
2542151497Sru    horiz = g->minh;
2543114402Sru    do {
2544114402Sru      if (g->is_auto_img()) {
2545114402Sru	string img=generate_img_src((char *)(g->text_string + 20));
2546114402Sru
2547114402Sru	if (! img.empty()) {
2548114402Sru	  simple_anchors = TRUE;  // we cannot use full heading anchors with images
2549151497Sru	  if (horiz < g->minh)
2550114402Sru	    header.header_buffer += " ";
2551114402Sru
2552114402Sru	  header.header_buffer += img;
2553114402Sru	}
2554151497Sru      }
2555151497Sru      else if (g->is_in() || g->is_ti() || g->is_po() || g->is_ce() || g->is_ll())
2556151497Sru	troff_tag(g);
2557151497Sru      else if (g->is_fi())
2558151497Sru	fill_on = 1;
2559151497Sru      else if (g->is_nf())
2560151497Sru	fill_on = 0;
2561151497Sru      else if (! (g->is_a_line() || g->is_a_tag())) {
2562114402Sru	/*
2563151497Sru	 *  we ignore the other tag commands when constructing a heading
2564114402Sru	 */
2565151497Sru	if (horiz < g->minh)
2566114402Sru	  header.header_buffer += " ";
2567114402Sru
2568151497Sru	horiz = g->maxh;
2569114402Sru	header.header_buffer += string(g->text_string, g->text_length);
2570114402Sru      }
2571114402Sru      page_contents->glyphs.move_right();
2572114402Sru      g = page_contents->glyphs.get_data();
2573114402Sru    } while ((! page_contents->glyphs.is_equal_to_head()) &&
2574151497Sru	     (! g->is_eo_h()));
2575114402Sru  }
2576114402Sru
2577114402Sru  determine_header_level(level);
2578114402Sru  write_header();
2579114402Sru
2580114402Sru  // finally set the output to neutral for after the header
2581114402Sru  g = page_contents->glyphs.get_data();
2582114402Sru  page_contents->glyphs.move_left();     // so that next time we use old g
2583114402Sru}
2584114402Sru
2585114402Sru/*
2586114402Sru *  is_courier_until_eol - returns TRUE if we can see a whole line which is courier
2587114402Sru */
2588114402Sru
2589114402Sruint html_printer::is_courier_until_eol (void)
2590114402Sru{
2591114402Sru  text_glob *orig = page_contents->glyphs.get_data();
2592114402Sru  int result      = TRUE;
2593114402Sru  text_glob *g;
2594114402Sru
2595114402Sru  if (! page_contents->glyphs.is_equal_to_tail()) {
2596114402Sru    page_contents->glyphs.move_right();
2597114402Sru    do {
2598114402Sru      g = page_contents->glyphs.get_data();
2599114402Sru      if (! g->is_a_tag() && (! is_font_courier(g->text_style.f)))
2600114402Sru	result = FALSE;
2601114402Sru      page_contents->glyphs.move_right();
2602114402Sru    } while (result &&
2603114402Sru	     (! page_contents->glyphs.is_equal_to_head()) &&
2604114402Sru	     (! g->is_fi()) && (! g->is_eol()));
2605114402Sru
2606114402Sru    /*
2607114402Sru     *  now restore our previous position.
2608114402Sru     */
2609114402Sru    while (page_contents->glyphs.get_data() != orig)
2610114402Sru      page_contents->glyphs.move_left();
2611114402Sru  }
2612114402Sru  return result;
2613114402Sru}
2614114402Sru
2615114402Sru/*
2616114402Sru *  do_linelength - handle the .ll command from troff.
2617114402Sru */
2618114402Sru
2619114402Sruvoid html_printer::do_linelength (char *arg)
2620114402Sru{
2621114402Sru  if (max_linelength == -1)
2622114402Sru    max_linelength = atoi(arg);
2623114402Sru
2624151497Sru  next_linelength = atoi(arg);
2625151497Sru  seen_linelength = TRUE;
2626114402Sru}
2627114402Sru
2628114402Sru/*
2629114402Sru *  do_pageoffset - handle the .po command from troff.
2630114402Sru */
2631114402Sru
2632114402Sruvoid html_printer::do_pageoffset (char *arg)
2633114402Sru{
2634151497Sru  next_pageoffset = atoi(arg);
2635151497Sru  seen_pageoffset = TRUE;
2636114402Sru}
2637114402Sru
2638114402Sru/*
2639151497Sru *  get_troff_indent - returns the indent value.
2640151497Sru */
2641151497Sru
2642151497Sruint html_printer::get_troff_indent (void)
2643151497Sru{
2644151497Sru  if (end_tempindent > 0)
2645151497Sru    return temp_indent;
2646151497Sru  else
2647151497Sru    return troff_indent;
2648151497Sru}
2649151497Sru
2650151497Sru/*
2651114402Sru *  do_indentation - handle the .in command from troff.
2652114402Sru */
2653114402Sru
2654114402Sruvoid html_printer::do_indentation (char *arg)
2655114402Sru{
2656151497Sru  next_indent = atoi(arg);
2657151497Sru  seen_indent = TRUE;
2658114402Sru}
2659114402Sru
2660114402Sru/*
2661114402Sru *  do_tempindent - handle the .ti command from troff.
2662114402Sru */
2663114402Sru
2664114402Sruvoid html_printer::do_tempindent (char *arg)
2665114402Sru{
2666114402Sru  if (fill_on) {
2667151497Sru    /*
2668151497Sru     *  we set the end_tempindent to 2 as the first .br
2669151497Sru     *  activates the .ti and the second terminates it.
2670151497Sru     */
2671151497Sru    end_tempindent = 2;
2672151497Sru    temp_indent = atoi(arg);
2673114402Sru  }
2674114402Sru}
2675114402Sru
2676114402Sru/*
2677114402Sru *  shutdown_table - shuts down the current table.
2678114402Sru */
2679114402Sru
2680114402Sruvoid html_printer::shutdown_table (void)
2681114402Sru{
2682114402Sru  if (table != NULL) {
2683114402Sru    current_paragraph->done_para();
2684114402Sru    table->emit_finish_table();
2685114402Sru    // dont delete this table as it will be deleted when we destroy the text_glob
2686114402Sru    table = NULL;
2687114402Sru  }
2688114402Sru}
2689114402Sru
2690114402Sru/*
2691114402Sru *  do_indent - remember the indent parameters and if
2692114402Sru *              indent is > pageoff and indent has changed
2693114402Sru *              then we start a html table to implement the indentation.
2694114402Sru */
2695114402Sru
2696114402Sruvoid html_printer::do_indent (int in, int pageoff, int linelen)
2697114402Sru{
2698151497Sru  if ((device_indent != -1) &&
2699151497Sru      (pageoffset+device_indent != in+pageoff)) {
2700151497Sru
2701151497Sru    int space = current_paragraph->retrieve_para_space() || seen_space;
2702114402Sru    current_paragraph->done_para();
2703114402Sru
2704151497Sru    device_indent = in;
2705114402Sru    pageoffset  = pageoff;
2706114402Sru    if (linelen <= max_linelength)
2707114402Sru      linelength  = linelen;
2708114402Sru
2709151497Sru    current_paragraph->do_para(&html, "", device_indent,
2710151497Sru			       pageoffset, max_linelength, space);
2711114402Sru  }
2712114402Sru}
2713114402Sru
2714114402Sru/*
2715114402Sru *  do_verticalspacing - handle the .vs command from troff.
2716114402Sru */
2717114402Sru
2718114402Sruvoid html_printer::do_verticalspacing (char *arg)
2719114402Sru{
2720114402Sru  vertical_spacing = atoi(arg);
2721114402Sru}
2722114402Sru
2723114402Sru/*
2724114402Sru *  do_pointsize - handle the .ps command from troff.
2725114402Sru */
2726114402Sru
2727114402Sruvoid html_printer::do_pointsize (char *arg)
2728114402Sru{
2729151497Sru  /*
2730151497Sru   *  firstly check to see whether this point size is really associated with a .tl tag
2731151497Sru   */
2732151497Sru
2733151497Sru  if (! page_contents->glyphs.is_empty()) {
2734151497Sru    text_glob *g = page_contents->glyphs.get_data();
2735151497Sru    text_glob *t = page_contents->glyphs.get_data();
2736151497Sru
2737151497Sru    while (t->is_a_tag() && (! page_contents->glyphs.is_equal_to_head())) {
2738151497Sru      if (t->is_tl()) {
2739151497Sru	/*
2740151497Sru	 *  found title therefore ignore this .ps tag
2741151497Sru	 */
2742151497Sru	while (t != g) {
2743151497Sru	  page_contents->glyphs.move_left();
2744151497Sru	  t = page_contents->glyphs.get_data();
2745151497Sru	}
2746151497Sru	return;
2747151497Sru      }
2748151497Sru      page_contents->glyphs.move_right();
2749151497Sru      t = page_contents->glyphs.get_data();
2750151497Sru    }
2751151497Sru    /*
2752151497Sru     *  move back to original position
2753151497Sru     */
2754151497Sru    while (t != g) {
2755151497Sru      page_contents->glyphs.move_left();
2756151497Sru      t = page_contents->glyphs.get_data();
2757151497Sru    }
2758151497Sru    /*
2759151497Sru     *  collect legal pointsize
2760151497Sru     */
2761151497Sru    pointsize = atoi(arg);
2762151497Sru  }
2763114402Sru}
2764114402Sru
2765114402Sru/*
2766114402Sru *  do_fill - records whether troff has requested that text be filled.
2767114402Sru */
2768114402Sru
2769151497Sruvoid html_printer::do_fill (char *arg)
2770114402Sru{
2771151497Sru  int on = atoi(arg);
2772151497Sru
2773151497Sru  output_hpos = get_troff_indent()+pageoffset;
2774114402Sru  supress_sub_sup = TRUE;
2775114402Sru
2776114402Sru  if (fill_on != on) {
2777114402Sru    if (on)
2778151497Sru      current_paragraph->do_para("", seen_space);
2779114402Sru    fill_on = on;
2780114402Sru  }
2781114402Sru}
2782114402Sru
2783114402Sru/*
2784114402Sru *  do_eol - handle the end of line
2785114402Sru */
2786114402Sru
2787114402Sruvoid html_printer::do_eol (void)
2788114402Sru{
2789114402Sru  if (! fill_on) {
2790114402Sru    if (current_paragraph->ever_emitted_text()) {
2791114402Sru      current_paragraph->do_newline();
2792114402Sru      current_paragraph->do_break();
2793114402Sru    }
2794114402Sru  }
2795151497Sru  output_hpos = get_troff_indent()+pageoffset;
2796114402Sru}
2797114402Sru
2798114402Sru/*
2799151497Sru *  do_check_center - checks to see whether we have seen a `.ce' tag
2800151497Sru *                    during the previous line.
2801151497Sru */
2802151497Sru
2803151497Sruvoid html_printer::do_check_center(void)
2804151497Sru{
2805151497Sru  if (seen_center) {
2806151497Sru    seen_center = FALSE;
2807151497Sru    if (next_center > 0) {
2808151497Sru      if (end_center == 0) {
2809151497Sru	int space = current_paragraph->retrieve_para_space() || seen_space;
2810151497Sru	current_paragraph->done_para();
2811151497Sru	supress_sub_sup = TRUE;
2812151497Sru	current_paragraph->do_para("align=center", space);
2813151497Sru      } else
2814151497Sru	if (strcmp("align=center",
2815151497Sru		   current_paragraph->get_alignment()) != 0) {
2816151497Sru	  /*
2817151497Sru	   *  different alignment, so shutdown paragraph and open
2818151497Sru	   *  a new one.
2819151497Sru	   */
2820151497Sru	  int space = current_paragraph->retrieve_para_space() || seen_space;
2821151497Sru	  current_paragraph->done_para();
2822151497Sru	  supress_sub_sup = TRUE;
2823151497Sru	  current_paragraph->do_para("align=center", space);
2824151497Sru	} else
2825151497Sru	  /*
2826151497Sru	   *  same alignment, if we have emitted text then issue a break.
2827151497Sru	   */
2828151497Sru	  if (current_paragraph->emitted_text())
2829151497Sru	    current_paragraph->do_break();
2830151497Sru    } else
2831151497Sru      /*
2832151497Sru       *  next_center == 0
2833151497Sru       */
2834151497Sru      if (end_center > 0) {
2835151497Sru	seen_space = seen_space || current_paragraph->retrieve_para_space();
2836151497Sru	current_paragraph->done_para();
2837151497Sru	supress_sub_sup = TRUE;
2838151497Sru	current_paragraph->do_para("", seen_space);
2839151497Sru      }
2840151497Sru    end_center = next_center;
2841151497Sru  }
2842151497Sru}
2843151497Sru
2844151497Sru/*
2845114402Sru *  do_eol_ce - handle end of line specifically for a .ce
2846114402Sru */
2847114402Sru
2848114402Sruvoid html_printer::do_eol_ce (void)
2849114402Sru{
2850114402Sru  if (end_center > 0) {
2851114402Sru    if (end_center > 1)
2852114402Sru      if (current_paragraph->emitted_text())
2853114402Sru	current_paragraph->do_break();
2854114402Sru
2855114402Sru    end_center--;
2856114402Sru    if (end_center == 0) {
2857114402Sru      current_paragraph->done_para();
2858114402Sru      supress_sub_sup = TRUE;
2859114402Sru    }
2860114402Sru  }
2861114402Sru}
2862114402Sru
2863114402Sru/*
2864114402Sru *  do_flush - flushes all output and tags.
2865114402Sru */
2866114402Sru
2867114402Sruvoid html_printer::do_flush (void)
2868114402Sru{
2869114402Sru  current_paragraph->done_para();
2870114402Sru}
2871114402Sru
2872114402Sru/*
2873114402Sru *  do_links - moves onto a new temporary file and sets auto_links to FALSE.
2874114402Sru */
2875114402Sru
2876114402Sruvoid html_printer::do_links (void)
2877114402Sru{
2878151497Sru  html.end_line();                      // flush line
2879114402Sru  auto_links = FALSE;   /* from now on only emit under user request */
2880114402Sru  file_list.add_new_file(xtmpfile());
2881151497Sru  file_list.set_links_required();
2882114402Sru  html.set_file(file_list.get_file());
2883114402Sru}
2884114402Sru
2885114402Sru/*
2886151497Sru *  insert_split_file -
2887151497Sru */
2888151497Sru
2889151497Sruvoid html_printer::insert_split_file (void)
2890151497Sru{
2891151497Sru  if (multiple_files) {
2892151497Sru    current_paragraph->done_para();       // flush paragraph
2893151497Sru    html.end_line();                      // flush line
2894151497Sru    html.set_file(file_list.get_file());  // flush current file
2895151497Sru    file_list.add_new_file(xtmpfile());
2896151497Sru    string split_file = job_name;
2897151497Sru
2898151497Sru    split_file += string("-");
2899151497Sru    split_file += as_string(header.no_of_level_one_headings);
2900151497Sru    split_file += string(".html");
2901151497Sru    split_file += '\0';
2902151497Sru
2903151497Sru    file_list.set_file_name(split_file);
2904151497Sru    html.set_file(file_list.get_file());
2905151497Sru  }
2906151497Sru}
2907151497Sru
2908151497Sru/*
2909151497Sru *  do_job_name - assigns the job_name to name.
2910151497Sru */
2911151497Sru
2912151497Sruvoid html_printer::do_job_name (char *name)
2913151497Sru{
2914151497Sru  if (! multiple_files) {
2915151497Sru    multiple_files = TRUE;
2916151497Sru    while (name != NULL && (*name != (char)0) && (*name == ' '))
2917151497Sru      name++;
2918151497Sru    job_name = name;
2919151497Sru  }
2920151497Sru}
2921151497Sru
2922151497Sru/*
2923151497Sru *  do_head - adds a string to head_info which is to be included into
2924151497Sru *            the <head> </head> section of the html document.
2925151497Sru */
2926151497Sru
2927151497Sruvoid html_printer::do_head (char *name)
2928151497Sru{
2929151497Sru  head_info += string(name);
2930151497Sru  head_info += '\n';
2931151497Sru}
2932151497Sru
2933151497Sru/*
2934114402Sru *  do_break - handles the ".br" request and also
2935151497Sru *             undoes an outstanding ".ti" command
2936151497Sru *             and calls indent if the indentation
2937151497Sru *             related registers have changed.
2938114402Sru */
2939114402Sru
2940114402Sruvoid html_printer::do_break (void)
2941114402Sru{
2942151497Sru  int seen_temp_indent = FALSE;
2943151497Sru
2944114402Sru  current_paragraph->do_break();
2945114402Sru  if (end_tempindent > 0) {
2946114402Sru    end_tempindent--;
2947151497Sru    if (end_tempindent > 0)
2948151497Sru      seen_temp_indent = TRUE;
2949114402Sru  }
2950151497Sru  if (seen_indent || seen_pageoffset || seen_linelength || seen_temp_indent) {
2951151497Sru    if (seen_indent && (! seen_temp_indent))
2952151497Sru      troff_indent = next_indent;
2953151497Sru    if (! seen_pageoffset)
2954151497Sru      next_pageoffset = pageoffset;
2955151497Sru    if (! seen_linelength)
2956151497Sru      next_linelength = linelength;
2957151497Sru    do_indent(get_troff_indent(), next_pageoffset, next_linelength);
2958151497Sru  }
2959151497Sru  seen_indent     = seen_temp_indent;
2960151497Sru  seen_linelength = FALSE;
2961151497Sru  seen_pageoffset = FALSE;
2962151497Sru  do_check_center();
2963151497Sru  output_hpos     = get_troff_indent()+pageoffset;
2964114402Sru  supress_sub_sup = TRUE;
2965114402Sru}
2966114402Sru
2967151497Sruvoid html_printer::do_space (char *arg)
2968151497Sru{
2969151497Sru  int n = atoi(arg);
2970151497Sru
2971151497Sru  seen_space = atoi(arg);
2972151497Sru  as.check_sp(seen_space);
2973151497Sru#if 0
2974151497Sru  if (n>0 && table)
2975151497Sru    table->set_space(TRUE);
2976151497Sru#endif
2977151497Sru
2978151497Sru  while (n>0) {
2979151497Sru    current_paragraph->do_space();
2980151497Sru    n--;
2981151497Sru  }
2982151497Sru  supress_sub_sup = TRUE;
2983151497Sru}
2984151497Sru
2985114402Sru/*
2986114402Sru *  do_tab_ts - start a table, which will have already been defined.
2987114402Sru */
2988114402Sru
2989114402Sruvoid html_printer::do_tab_ts (text_glob *g)
2990114402Sru{
2991114402Sru  html_table *t = g->get_table();
2992114402Sru
2993114402Sru  if (t != NULL) {
2994151497Sru    current_column = 0;
2995114402Sru    current_paragraph->done_pre();
2996114402Sru    current_paragraph->done_para();
2997151497Sru    current_paragraph->remove_para_space();
2998114402Sru
2999151497Sru#if defined(DEBUG_TABLES)
3000114402Sru    html.simple_comment("TABS");
3001151497Sru#endif
3002114402Sru
3003114402Sru    t->set_linelength(max_linelength);
3004114402Sru    t->add_indent(pageoffset);
3005151497Sru#if 0
3006151497Sru    t->emit_table_header(seen_space);
3007151497Sru#else
3008114402Sru    t->emit_table_header(FALSE);
3009151497Sru    row_space = current_paragraph->retrieve_para_space() || seen_space;
3010151497Sru    seen_space = FALSE;
3011151497Sru#endif
3012114402Sru  }
3013114402Sru
3014114402Sru  table = t;
3015114402Sru}
3016114402Sru
3017114402Sru/*
3018114402Sru *  do_tab_te - finish a table.
3019114402Sru */
3020114402Sru
3021114402Sruvoid html_printer::do_tab_te (void)
3022114402Sru{
3023114402Sru  if (table) {
3024114402Sru    current_paragraph->done_para();
3025151497Sru    current_paragraph->remove_para_space();
3026114402Sru    table->emit_finish_table();
3027114402Sru  }
3028114402Sru
3029114402Sru  table = NULL;
3030151497Sru  restore_troff_indent();
3031114402Sru}
3032114402Sru
3033114402Sru/*
3034151497Sru *  do_tab - handle the "devtag:tab" tag
3035114402Sru */
3036114402Sru
3037114402Sruvoid html_printer::do_tab (char *s)
3038114402Sru{
3039114402Sru  if (table) {
3040114402Sru    while (isspace(*s))
3041114402Sru      s++;
3042114402Sru    s++;
3043151497Sru    int col = table->find_column(atoi(s) + pageoffset + get_troff_indent());
3044114402Sru    if (col > 0) {
3045114402Sru      current_paragraph->done_para();
3046114402Sru      table->emit_col(col);
3047114402Sru    }
3048114402Sru  }
3049114402Sru}
3050114402Sru
3051114402Sru/*
3052151497Sru *  do_tab0 - handle the "devtag:tab0" tag
3053114402Sru */
3054114402Sru
3055114402Sruvoid html_printer::do_tab0 (void)
3056114402Sru{
3057114402Sru  if (table) {
3058151497Sru    int col = table->find_column(pageoffset+get_troff_indent());
3059114402Sru    if (col > 0) {
3060114402Sru      current_paragraph->done_para();
3061114402Sru      table->emit_col(col);
3062114402Sru    }
3063114402Sru  }
3064114402Sru}
3065114402Sru
3066114402Sru/*
3067114402Sru *  do_col - start column, s.
3068114402Sru */
3069114402Sru
3070114402Sruvoid html_printer::do_col (char *s)
3071114402Sru{
3072114402Sru  if (table) {
3073151497Sru    if (atoi(s) < current_column)
3074151497Sru      row_space = seen_space;
3075151497Sru
3076151497Sru    current_column = atoi(s);
3077114402Sru    current_paragraph->done_para();
3078151497Sru    table->emit_col(current_column);
3079151497Sru    current_paragraph->do_para("", row_space);
3080114402Sru  }
3081114402Sru}
3082114402Sru
3083114402Sru/*
3084151497Sru *  troff_tag - processes the troff tag and manipulates the troff
3085151497Sru *              state machine.
3086114402Sru */
3087114402Sru
3088114402Sruvoid html_printer::troff_tag (text_glob *g)
3089114402Sru{
3090114402Sru  /*
3091151497Sru   *  firstly skip over devtag:
3092114402Sru   */
3093151497Sru  char *t=(char *)g->text_string+strlen("devtag:");
3094114402Sru
3095151497Sru  if (strncmp(g->text_string, "html</p>:", strlen("html</p>:")) == 0) {
3096151497Sru    do_end_para(g);
3097151497Sru  } else if (g->is_eol()) {
3098114402Sru    do_eol();
3099114402Sru  } else if (g->is_eol_ce()) {
3100114402Sru    do_eol_ce();
3101114402Sru  } else if (strncmp(t, ".sp", 3) == 0) {
3102151497Sru    char *a = (char *)t+3;
3103151497Sru    do_space(a);
3104114402Sru  } else if (strncmp(t, ".br", 3) == 0) {
3105151497Sru    seen_break = 1;
3106151497Sru    as.check_br(1);
3107114402Sru    do_break();
3108114402Sru  } else if (strcmp(t, ".centered-image") == 0) {
3109114402Sru    do_centered_image();
3110114402Sru  } else if (strcmp(t, ".right-image") == 0) {
3111114402Sru    do_right_image();
3112114402Sru  } else if (strcmp(t, ".left-image") == 0) {
3113114402Sru    do_left_image();
3114114402Sru  } else if (strncmp(t, ".auto-image", 11) == 0) {
3115114402Sru    char *a = (char *)t+11;
3116114402Sru    do_auto_image(g, a);
3117114402Sru  } else if (strncmp(t, ".ce", 3) == 0) {
3118114402Sru    char *a = (char *)t+3;
3119114402Sru    supress_sub_sup = TRUE;
3120114402Sru    do_center(a);
3121151497Sru  } else if (g->is_tl()) {
3122114402Sru    supress_sub_sup = TRUE;
3123114402Sru    title.with_h1 = TRUE;
3124114402Sru    do_title();
3125114402Sru  } else if (strncmp(t, ".html-tl", 8) == 0) {
3126114402Sru    supress_sub_sup = TRUE;
3127114402Sru    title.with_h1 = FALSE;
3128114402Sru    do_title();
3129114402Sru  } else if (strncmp(t, ".fi", 3) == 0) {
3130151497Sru    char *a = (char *)t+3;
3131151497Sru    do_fill(a);
3132114402Sru  } else if ((strncmp(t, ".SH", 3) == 0) || (strncmp(t, ".NH", 3) == 0)) {
3133114402Sru    char *a = (char *)t+3;
3134114402Sru    do_heading(a);
3135114402Sru  } else if (strncmp(t, ".ll", 3) == 0) {
3136114402Sru    char *a = (char *)t+3;
3137114402Sru    do_linelength(a);
3138114402Sru  } else if (strncmp(t, ".po", 3) == 0) {
3139114402Sru    char *a = (char *)t+3;
3140114402Sru    do_pageoffset(a);
3141114402Sru  } else if (strncmp(t, ".in", 3) == 0) {
3142114402Sru    char *a = (char *)t+3;
3143114402Sru    do_indentation(a);
3144114402Sru  } else if (strncmp(t, ".ti", 3) == 0) {
3145114402Sru    char *a = (char *)t+3;
3146114402Sru    do_tempindent(a);
3147114402Sru  } else if (strncmp(t, ".vs", 3) == 0) {
3148114402Sru    char *a = (char *)t+3;
3149114402Sru    do_verticalspacing(a);
3150114402Sru  } else if (strncmp(t, ".ps", 3) == 0) {
3151114402Sru    char *a = (char *)t+3;
3152114402Sru    do_pointsize(a);
3153114402Sru  } else if (strcmp(t, ".links") == 0) {
3154114402Sru    do_links();
3155151497Sru  } else if (strncmp(t, ".job-name", 9) == 0) {
3156151497Sru    char *a = (char *)t+9;
3157151497Sru    do_job_name(a);
3158151497Sru  } else if (strncmp(t, ".head", 5) == 0) {
3159151497Sru    char *a = (char *)t+5;
3160151497Sru    do_head(a);
3161114402Sru  } else if (strcmp(t, ".no-auto-rule") == 0) {
3162114402Sru    auto_rule = FALSE;
3163114402Sru  } else if (strcmp(t, ".tab-ts") == 0) {
3164114402Sru    do_tab_ts(g);
3165114402Sru  } else if (strcmp(t, ".tab-te") == 0) {
3166114402Sru    do_tab_te();
3167114402Sru  } else if (strncmp(t, ".col ", 5) == 0) {
3168114402Sru    char *a = (char *)t+4;
3169114402Sru    do_col(a);
3170114402Sru  } else if (strncmp(t, "tab ", 4) == 0) {
3171114402Sru    char *a = (char *)t+3;
3172114402Sru    do_tab(a);
3173114402Sru  } else if (strncmp(t, "tab0", 4) == 0) {
3174114402Sru    do_tab0();
3175114402Sru  }
3176114402Sru}
3177114402Sru
3178114402Sru/*
3179114402Sru *  is_in_middle - returns TRUE if the positions left..right are in the center of the page.
3180114402Sru */
3181114402Sru
3182114402Sruint html_printer::is_in_middle (int left, int right)
3183114402Sru{
3184151497Sru  return( abs(abs(left-pageoffset) - abs(pageoffset+linelength-right))
3185151497Sru	  <= CENTER_TOLERANCE );
3186114402Sru}
3187114402Sru
3188114402Sru/*
3189114402Sru *  flush_globs - runs through the text glob list and emits html.
3190114402Sru */
3191114402Sru
3192114402Sruvoid html_printer::flush_globs (void)
3193114402Sru{
3194114402Sru  text_glob *g;
3195114402Sru
3196114402Sru  if (! page_contents->glyphs.is_empty()) {
3197114402Sru    page_contents->glyphs.start_from_head();
3198114402Sru    do {
3199114402Sru      g = page_contents->glyphs.get_data();
3200151497Sru#if 0
3201151497Sru      fprintf(stderr, "[%s:%d:%d:%d:%d]",
3202151497Sru	      g->text_string, g->minv, g->minh, g->maxv, g->maxh) ;
3203151497Sru      fflush(stderr);
3204151497Sru#endif
3205114402Sru
3206151497Sru      handle_state_assertion(g);
3207151497Sru
3208114402Sru      if (strcmp(g->text_string, "XXXXXXX") == 0)
3209114402Sru	stop();
3210114402Sru
3211151497Sru      if (g->is_a_tag())
3212114402Sru	troff_tag(g);
3213151497Sru      else if (g->is_a_line())
3214114402Sru	emit_line(g);
3215151497Sru      else {
3216151497Sru	as.check_sp(seen_space);
3217151497Sru	as.check_br(seen_break);
3218151497Sru	seen_break = 0;
3219151497Sru	seen_space = 0;
3220114402Sru	emit_html(g);
3221114402Sru      }
3222151497Sru
3223151497Sru      as.check_fi(fill_on);
3224151497Sru      as.check_ce(end_center);
3225114402Sru      /*
3226114402Sru       *  after processing the title (and removing it) the glyph list might be empty
3227114402Sru       */
3228114402Sru      if (! page_contents->glyphs.is_empty()) {
3229114402Sru	page_contents->glyphs.move_right();
3230114402Sru      }
3231114402Sru    } while (! page_contents->glyphs.is_equal_to_head());
3232114402Sru  }
3233114402Sru}
3234114402Sru
3235114402Sru/*
3236114402Sru *  calc_nf - calculates the _no_ format flag, given the
3237114402Sru *            text glob, g.
3238114402Sru */
3239114402Sru
3240114402Sruint html_printer::calc_nf (text_glob *g, int nf)
3241114402Sru{
3242114402Sru  if (g != NULL) {
3243151497Sru    if (g->is_fi()) {
3244151497Sru      as.check_fi(TRUE);
3245114402Sru      return FALSE;
3246151497Sru    }
3247151497Sru    if (g->is_nf()) {
3248151497Sru      as.check_fi(FALSE);
3249114402Sru      return TRUE;
3250151497Sru    }
3251114402Sru  }
3252151497Sru  as.check_fi(! nf);
3253114402Sru  return nf;
3254114402Sru}
3255114402Sru
3256114402Sru/*
3257114402Sru *  calc_po_in - calculates the, in, po, registers
3258114402Sru */
3259114402Sru
3260114402Sruvoid html_printer::calc_po_in (text_glob *g, int nf)
3261114402Sru{
3262114402Sru  if (g->is_in())
3263151497Sru    troff_indent = g->get_arg();
3264114402Sru  else if (g->is_po())
3265114402Sru    pageoffset = g->get_arg();
3266114402Sru  else if (g->is_ti()) {
3267151497Sru    temp_indent = g->get_arg();
3268151497Sru    end_tempindent = 2;
3269151497Sru  } else if (g->is_br() || (nf && g->is_eol())) {
3270151497Sru    if (end_tempindent > 0)
3271151497Sru      end_tempindent--;
3272114402Sru  }
3273114402Sru}
3274114402Sru
3275114402Sru/*
3276114402Sru *  next_horiz_pos - returns the next horiz position.
3277114402Sru *                   -1 is returned if it doesn't exist.
3278114402Sru */
3279114402Sru
3280114402Sruint html_printer::next_horiz_pos (text_glob *g, int nf)
3281114402Sru{
3282151497Sru  int next = -1;
3283114402Sru
3284114402Sru  if ((g != NULL) && (g->is_br() || (nf && g->is_eol())))
3285114402Sru    if (! page_contents->glyphs.is_empty()) {
3286114402Sru      page_contents->glyphs.move_right_get_data();
3287151497Sru      if (g == NULL) {
3288114402Sru	page_contents->glyphs.start_from_head();
3289151497Sru	as.reset();
3290151497Sru      }
3291114402Sru      else {
3292114402Sru	next = g->minh;
3293114402Sru	page_contents->glyphs.move_left();
3294114402Sru      }
3295114402Sru    }
3296114402Sru  return next;
3297114402Sru}
3298114402Sru
3299114402Sru/*
3300114402Sru *  insert_tab_ts - inserts a tab-ts before, where.
3301114402Sru */
3302114402Sru
3303114402Srutext_glob *html_printer::insert_tab_ts (text_glob *where)
3304114402Sru{
3305114402Sru  text_glob *start_of_table;
3306114402Sru  text_glob *old_pos = page_contents->glyphs.get_data();
3307114402Sru
3308114402Sru  page_contents->glyphs.move_to(where);
3309114402Sru  page_contents->glyphs.move_left();
3310151497Sru  page_contents->insert_tag(string("devtag:.tab-ts"));  // tab table start
3311114402Sru  page_contents->glyphs.move_right();
3312114402Sru  start_of_table = page_contents->glyphs.get_data();
3313114402Sru  page_contents->glyphs.move_to(old_pos);
3314114402Sru  return start_of_table;
3315114402Sru}
3316114402Sru
3317114402Sru/*
3318114402Sru *  insert_tab_te - inserts a tab-te before the current position
3319114402Sru *                  (it skips backwards over .sp/.br)
3320114402Sru */
3321114402Sru
3322114402Sruvoid html_printer::insert_tab_te (void)
3323114402Sru{
3324114402Sru  text_glob *g = page_contents->glyphs.get_data();
3325114402Sru  page_contents->dump_page();
3326114402Sru
3327114402Sru  while (page_contents->glyphs.get_data()->is_a_tag())
3328114402Sru    page_contents->glyphs.move_left();
3329114402Sru
3330151497Sru  page_contents->insert_tag(string("devtag:.tab-te"));  // tab table end
3331114402Sru  while (g != page_contents->glyphs.get_data())
3332114402Sru    page_contents->glyphs.move_right();
3333114402Sru  page_contents->dump_page();
3334114402Sru}
3335114402Sru
3336114402Sru/*
3337114402Sru *  insert_tab_0 - inserts a tab0 before, where.
3338114402Sru */
3339114402Sru
3340114402Sruvoid html_printer::insert_tab_0 (text_glob *where)
3341114402Sru{
3342114402Sru  text_glob *old_pos = page_contents->glyphs.get_data();
3343114402Sru
3344114402Sru  page_contents->glyphs.move_to(where);
3345114402Sru  page_contents->glyphs.move_left();
3346151497Sru  page_contents->insert_tag(string("devtag:tab0"));  // tab0 start of line
3347114402Sru  page_contents->glyphs.move_right();
3348114402Sru  page_contents->glyphs.move_to(old_pos);
3349114402Sru}
3350114402Sru
3351114402Sru/*
3352114402Sru *  remove_tabs - removes the tabs tags on this line.
3353114402Sru */
3354114402Sru
3355114402Sruvoid html_printer::remove_tabs (void)
3356114402Sru{
3357114402Sru  text_glob *orig = page_contents->glyphs.get_data();
3358114402Sru  text_glob *g;
3359114402Sru
3360114402Sru  if (! page_contents->glyphs.is_equal_to_tail()) {
3361114402Sru    do {
3362114402Sru      g = page_contents->glyphs.get_data();
3363114402Sru      if (g->is_tab()) {
3364114402Sru	page_contents->glyphs.sub_move_right();
3365114402Sru	if (g == orig)
3366114402Sru	  orig = page_contents->glyphs.get_data();
3367114402Sru      }	else
3368114402Sru	page_contents->glyphs.move_right();
3369114402Sru    } while ((! page_contents->glyphs.is_equal_to_head()) &&
3370114402Sru	     (! g->is_eol()));
3371114402Sru
3372114402Sru    /*
3373114402Sru     *  now restore our previous position.
3374114402Sru     */
3375114402Sru    while (page_contents->glyphs.get_data() != orig)
3376114402Sru      page_contents->glyphs.move_left();
3377114402Sru  }
3378114402Sru}
3379114402Sru
3380114402Sruvoid html_printer::remove_courier_tabs (void)
3381114402Sru{
3382114402Sru  text_glob  *g;
3383114402Sru  int line_start = TRUE;
3384114402Sru  int nf         = FALSE;
3385114402Sru
3386114402Sru  if (! page_contents->glyphs.is_empty()) {
3387114402Sru    page_contents->glyphs.start_from_head();
3388151497Sru    as.reset();
3389114402Sru    line_start = TRUE;
3390114402Sru    do {
3391114402Sru      g = page_contents->glyphs.get_data();
3392151497Sru      handle_state_assertion(g);
3393114402Sru      nf = calc_nf(g, nf);
3394114402Sru
3395114402Sru      if (line_start) {
3396114402Sru	if (line_start && nf && is_courier_until_eol()) {
3397114402Sru	  remove_tabs();
3398114402Sru	  g = page_contents->glyphs.get_data();
3399114402Sru	}
3400114402Sru      }
3401114402Sru
3402151497Sru      // line_start = g->is_br() || g->is_nf() || g->is_fi() || (nf && g->is_eol());
3403151497Sru      line_start = g->is_br() || (nf && g->is_eol());
3404114402Sru      page_contents->glyphs.move_right();
3405114402Sru    } while (! page_contents->glyphs.is_equal_to_head());
3406114402Sru  }
3407114402Sru}
3408114402Sru
3409114402Sruvoid html_printer::insert_tab0_foreach_tab (void)
3410114402Sru{
3411114402Sru  text_glob  *start_of_line  = NULL;
3412114402Sru  text_glob  *g              = NULL;
3413114402Sru  int seen_tab               = FALSE;
3414114402Sru  int seen_col               = FALSE;
3415114402Sru  int nf                     = FALSE;
3416114402Sru
3417114402Sru  if (! page_contents->glyphs.is_empty()) {
3418114402Sru    page_contents->glyphs.start_from_head();
3419151497Sru    as.reset();
3420114402Sru    start_of_line = page_contents->glyphs.get_data();
3421114402Sru    do {
3422114402Sru      g = page_contents->glyphs.get_data();
3423151497Sru      handle_state_assertion(g);
3424114402Sru      nf = calc_nf(g, nf);
3425114402Sru
3426114402Sru      if (g->is_tab())
3427114402Sru	seen_tab = TRUE;
3428114402Sru
3429114402Sru      if (g->is_col())
3430114402Sru	seen_col = TRUE;
3431114402Sru
3432114402Sru      if (g->is_br() || (nf && g->is_eol())) {
3433114402Sru	do {
3434114402Sru	  page_contents->glyphs.move_right();
3435114402Sru	  g = page_contents->glyphs.get_data();
3436151497Sru	  handle_state_assertion(g);
3437114402Sru	  nf = calc_nf(g, nf);
3438114402Sru	  if (page_contents->glyphs.is_equal_to_head()) {
3439114402Sru	    if (seen_tab && !seen_col)
3440114402Sru	      insert_tab_0(start_of_line);
3441114402Sru	    return;
3442114402Sru	  }
3443114402Sru	} while (g->is_br() || (nf && g->is_eol()) || g->is_ta());
3444114402Sru	// printf("\nstart_of_line is: %s\n", g->text_string);
3445114402Sru	if (seen_tab && !seen_col) {
3446114402Sru	  insert_tab_0(start_of_line);
3447114402Sru	  page_contents->glyphs.move_to(g);
3448114402Sru	}
3449114402Sru
3450114402Sru	seen_tab = FALSE;
3451114402Sru	seen_col = FALSE;
3452114402Sru	start_of_line = g;
3453114402Sru      }
3454114402Sru      page_contents->glyphs.move_right();
3455114402Sru    } while (! page_contents->glyphs.is_equal_to_head());
3456114402Sru    if (seen_tab && !seen_col)
3457114402Sru      insert_tab_0(start_of_line);
3458114402Sru
3459114402Sru  }
3460114402Sru}
3461114402Sru
3462114402Sru/*
3463114402Sru *  update_min_max - updates the extent of a column, given the left and right
3464114402Sru *                   extents of a glyph, g.
3465114402Sru */
3466114402Sru
3467114402Sruvoid html_printer::update_min_max (colType type_of_col, int *minimum, int *maximum, text_glob *g)
3468114402Sru{
3469114402Sru  switch (type_of_col) {
3470114402Sru
3471114402Sru  case tab_tag:
3472114402Sru    break;
3473114402Sru  case tab0_tag:
3474114402Sru    *minimum = g->minh;
3475114402Sru    break;
3476114402Sru  case col_tag:
3477114402Sru    *minimum = g->minh;
3478114402Sru    *maximum = g->maxh;
3479114402Sru    break;
3480114402Sru  default:
3481114402Sru    break;
3482114402Sru  }
3483114402Sru}
3484114402Sru
3485114402Sru/*
3486114402Sru *  add_table_end - moves left one glyph, adds a table end tag and adds a
3487114402Sru *                  debugging string.
3488114402Sru */
3489114402Sru
3490114402Sruvoid html_printer::add_table_end (const char *
3491114402Sru#if defined(DEBUG_TABLES)
3492114402Sru  debug_string
3493114402Sru#endif
3494114402Sru)
3495114402Sru{
3496114402Sru  page_contents->glyphs.move_left();
3497114402Sru  insert_tab_te();
3498114402Sru#if defined(DEBUG_TABLES)
3499114402Sru  page_contents->insert_tag(string(debug_string));
3500114402Sru#endif
3501114402Sru}
3502114402Sru
3503114402Sru/*
3504151497Sru *  lookahead_for_tables - checks for .col tags and inserts table
3505151497Sru *                         start/end tags
3506114402Sru */
3507114402Sru
3508114402Sruvoid html_printer::lookahead_for_tables (void)
3509114402Sru{
3510114402Sru  text_glob  *g;
3511114402Sru  text_glob  *start_of_line  = NULL;
3512114402Sru  text_glob  *start_of_table = NULL;
3513114402Sru  text_glob  *last           = NULL;
3514114402Sru  colType     type_of_col    = none;
3515114402Sru  int         left           = 0;
3516114402Sru  int         found_col      = FALSE;
3517114402Sru  int         seen_text      = FALSE;
3518114402Sru  int         ncol           = 0;
3519151497Sru  int         colmin         = 0;		// pacify compiler
3520151497Sru  int         colmax         = 0;		// pacify compiler
3521151497Sru  html_table *tbl            = new html_table(&html, -1);
3522114402Sru  const char *tab_defs       = NULL;
3523114402Sru  char        align          = 'L';
3524114402Sru  int         nf             = FALSE;
3525114402Sru  int         old_pageoffset = pageoffset;
3526114402Sru
3527114402Sru  remove_courier_tabs();
3528114402Sru  page_contents->dump_page();
3529114402Sru  insert_tab0_foreach_tab();
3530114402Sru  page_contents->dump_page();
3531114402Sru  if (! page_contents->glyphs.is_empty()) {
3532114402Sru    page_contents->glyphs.start_from_head();
3533151497Sru    as.reset();
3534114402Sru    g = page_contents->glyphs.get_data();
3535151497Sru    if (g->is_br()) {
3536151497Sru      g = page_contents->glyphs.move_right_get_data();
3537151497Sru      handle_state_assertion(g);
3538151497Sru      if (page_contents->glyphs.is_equal_to_head()) {
3539151497Sru	if (tbl != NULL) {
3540151497Sru	  delete tbl;
3541151497Sru	  tbl = NULL;
3542151497Sru	}
3543151497Sru	return;
3544151497Sru      }
3545151497Sru
3546151497Sru      start_of_line = g;
3547151497Sru      seen_text = FALSE;
3548151497Sru      ncol = 0;
3549151497Sru      left = next_horiz_pos(g, nf);
3550151497Sru      if (found_col)
3551151497Sru	last = g;
3552151497Sru      found_col = FALSE;
3553151497Sru    }
3554151497Sru
3555114402Sru    do {
3556114402Sru#if defined(DEBUG_TABLES)
3557114402Sru      fprintf(stderr, " [") ;
3558114402Sru      fprintf(stderr, g->text_string) ;
3559114402Sru      fprintf(stderr, "] ") ;
3560114402Sru      fflush(stderr);
3561114402Sru      if (strcmp(g->text_string, "XXXXXXX") == 0)
3562114402Sru	stop();
3563114402Sru#endif
3564114402Sru
3565114402Sru      nf = calc_nf(g, nf);
3566114402Sru      calc_po_in(g, nf);
3567114402Sru      if (g->is_col()) {
3568114402Sru	if (type_of_col == tab_tag && start_of_table != NULL) {
3569114402Sru	  page_contents->glyphs.move_left();
3570114402Sru	  insert_tab_te();
3571151497Sru	  start_of_table->remember_table(tbl);
3572151497Sru	  tbl = new html_table(&html, -1);
3573114402Sru	  page_contents->insert_tag(string("*** TAB -> COL ***"));
3574114402Sru	  if (tab_defs != NULL)
3575151497Sru	    tbl->tab_stops->init(tab_defs);
3576114402Sru	  start_of_table = NULL;
3577114402Sru	  last = NULL;
3578114402Sru	}
3579114402Sru	type_of_col = col_tag;
3580114402Sru	found_col = TRUE;
3581114402Sru	ncol = g->get_arg();
3582114402Sru	align = 'L';
3583114402Sru	colmin = 0;
3584114402Sru	colmax = 0;
3585114402Sru      } else if (g->is_tab()) {
3586114402Sru	type_of_col = tab_tag;
3587114402Sru	colmin = g->get_tab_args(&align);
3588114402Sru	align = 'L'; // for now as 'C' and 'R' are broken
3589151497Sru	ncol = tbl->find_tab_column(colmin);
3590151497Sru	colmin += pageoffset + get_troff_indent();
3591151497Sru	colmax = tbl->get_tab_pos(ncol+1);
3592114402Sru	if (colmax > 0)
3593151497Sru	  colmax += pageoffset + get_troff_indent();
3594114402Sru      } else if (g->is_tab0()) {
3595114402Sru	if (type_of_col == col_tag && start_of_table != NULL) {
3596114402Sru	  page_contents->glyphs.move_left();
3597114402Sru	  insert_tab_te();
3598151497Sru	  start_of_table->remember_table(tbl);
3599151497Sru	  tbl = new html_table(&html, -1);
3600114402Sru	  page_contents->insert_tag(string("*** COL -> TAB ***"));
3601114402Sru	  start_of_table = NULL;
3602114402Sru	  last = NULL;
3603114402Sru	}
3604114402Sru	if (tab_defs != NULL)
3605151497Sru	  tbl->tab_stops->init(tab_defs);
3606114402Sru
3607114402Sru	type_of_col = tab0_tag;
3608114402Sru	ncol = 1;
3609114402Sru	colmin = 0;
3610151497Sru	colmax = tbl->get_tab_pos(2) + pageoffset + get_troff_indent();
3611114402Sru      } else if (! g->is_a_tag())
3612114402Sru	update_min_max(type_of_col, &colmin, &colmax, g);
3613114402Sru
3614114402Sru      if ((! g->is_a_tag()) || g->is_tab())
3615114402Sru	seen_text = TRUE;
3616114402Sru
3617114402Sru      if ((g->is_col() || g->is_tab() || g->is_tab0())
3618114402Sru	  && (start_of_line != NULL) && (start_of_table == NULL)) {
3619114402Sru	start_of_table = insert_tab_ts(start_of_line);
3620114402Sru	start_of_line = NULL;
3621114402Sru	seen_text = FALSE;
3622114402Sru      } else if (g->is_ce() && (start_of_table != NULL)) {
3623114402Sru	add_table_end("*** CE ***");
3624151497Sru	start_of_table->remember_table(tbl);
3625151497Sru 	tbl = new html_table(&html, -1);
3626114402Sru	start_of_table = NULL;
3627114402Sru	last = NULL;
3628114402Sru      } else if (g->is_ta()) {
3629114402Sru	tab_defs = g->text_string;
3630151497Sru
3631151497Sru	if (type_of_col == col_tag)
3632151497Sru	  tbl->tab_stops->check_init(tab_defs);
3633151497Sru
3634151497Sru	if (!tbl->tab_stops->compatible(tab_defs)) {
3635114402Sru	  if (start_of_table != NULL) {
3636114402Sru	    add_table_end("*** TABS ***");
3637151497Sru	    start_of_table->remember_table(tbl);
3638151497Sru	    tbl = new html_table(&html, -1);
3639114402Sru	    start_of_table = NULL;
3640114402Sru	    type_of_col = none;
3641114402Sru	    last = NULL;
3642114402Sru	  }
3643151497Sru	  tbl->tab_stops->init(tab_defs);
3644114402Sru	}
3645114402Sru      }
3646114402Sru
3647114402Sru      if (((! g->is_a_tag()) || g->is_tab()) && (start_of_table != NULL)) {
3648114402Sru	// we are in a table and have a glyph
3649151497Sru	if ((ncol == 0) || (! tbl->add_column(ncol, colmin, colmax, align))) {
3650114402Sru	  if (ncol == 0)
3651114402Sru	    add_table_end("*** NCOL == 0 ***");
3652114402Sru	  else
3653114402Sru	    add_table_end("*** CROSSED COLS ***");
3654114402Sru
3655151497Sru	  start_of_table->remember_table(tbl);
3656151497Sru	  tbl = new html_table(&html, -1);
3657114402Sru	  start_of_table = NULL;
3658114402Sru	  type_of_col = none;
3659114402Sru	  last = NULL;
3660114402Sru	}
3661114402Sru      }
3662114402Sru
3663114402Sru      /*
3664114402Sru       *  move onto next glob, check whether we are starting a new line
3665114402Sru       */
3666114402Sru      g = page_contents->glyphs.move_right_get_data();
3667151497Sru      handle_state_assertion(g);
3668114402Sru
3669114402Sru      if (g == NULL) {
3670114402Sru	if (found_col) {
3671114402Sru	  page_contents->glyphs.start_from_head();
3672151497Sru	  as.reset();
3673114402Sru	  last = g;
3674114402Sru	  found_col = FALSE;
3675114402Sru	}
3676114402Sru      } else if (g->is_br() || (nf && g->is_eol())) {
3677114402Sru	do {
3678114402Sru	  g = page_contents->glyphs.move_right_get_data();
3679151497Sru	  handle_state_assertion(g);
3680114402Sru	  nf = calc_nf(g, nf);
3681114402Sru	} while ((g != NULL) && (g->is_br() || (nf && g->is_eol())));
3682114402Sru	start_of_line = g;
3683114402Sru	seen_text = FALSE;
3684114402Sru	ncol = 0;
3685114402Sru	left = next_horiz_pos(g, nf);
3686114402Sru	if (found_col)
3687114402Sru	  last = g;
3688114402Sru	found_col = FALSE;
3689114402Sru      }
3690114402Sru    } while ((g != NULL) && (! page_contents->glyphs.is_equal_to_head()));
3691114402Sru
3692114402Sru#if defined(DEBUG_TABLES)
3693114402Sru    fprintf(stderr, "finished scanning for tables\n");
3694114402Sru#endif
3695114402Sru
3696114402Sru    page_contents->glyphs.start_from_head();
3697114402Sru    if (start_of_table != NULL) {
3698114402Sru      if (last != NULL)
3699114402Sru	while (last != page_contents->glyphs.get_data())
3700114402Sru	  page_contents->glyphs.move_left();
3701114402Sru
3702114402Sru      insert_tab_te();
3703151497Sru      start_of_table->remember_table(tbl);
3704151497Sru      tbl = NULL;
3705114402Sru      page_contents->insert_tag(string("*** LAST ***"));
3706114402Sru    }
3707114402Sru  }
3708151497Sru  if (tbl != NULL) {
3709151497Sru    delete tbl;
3710151497Sru    tbl = NULL;
3711151497Sru  }
3712114402Sru
3713114402Sru  // and reset the registers
3714114402Sru  pageoffset = old_pageoffset;
3715151497Sru  troff_indent = 0;
3716151497Sru  temp_indent = 0;
3717114402Sru  end_tempindent = 0;
3718114402Sru}
3719114402Sru
3720114402Sruvoid html_printer::flush_page (void)
3721114402Sru{
3722114402Sru  supress_sub_sup = TRUE;
3723114402Sru  flush_sbuf();
3724114402Sru  page_contents->dump_page();
3725114402Sru  lookahead_for_tables();
3726114402Sru  page_contents->dump_page();
3727114402Sru
3728114402Sru  flush_globs();
3729114402Sru  current_paragraph->done_para();
3730114402Sru
3731114402Sru  // move onto a new page
3732114402Sru  delete page_contents;
3733114402Sru#if defined(DEBUG_TABLES)
3734114402Sru  fprintf(stderr, "\n\n*** flushed page ***\n\n");
3735114402Sru
3736114402Sru  html.simple_comment("new page called");
3737114402Sru#endif
3738114402Sru  page_contents = new page;
3739114402Sru}
3740114402Sru
3741114402Sru/*
3742114402Sru *  determine_space - works out whether we need to write a space.
3743114402Sru *                    If last glyph is ajoining then no space emitted.
3744114402Sru */
3745114402Sru
3746114402Sruvoid html_printer::determine_space (text_glob *g)
3747114402Sru{
3748114402Sru  if (current_paragraph->is_in_pre()) {
3749114402Sru    /*
3750114402Sru     *  .nf has been specified
3751114402Sru     */
3752114402Sru    while (output_hpos < g->minh) {
3753114402Sru      output_hpos += space_width;
3754114402Sru      current_paragraph->emit_space();
3755114402Sru    }
3756114402Sru  } else {
3757114402Sru    if ((output_vpos != g->minv) || (output_hpos < g->minh)) {
3758114402Sru      current_paragraph->emit_space();
3759114402Sru    }
3760114402Sru  }
3761114402Sru}
3762114402Sru
3763114402Sru/*
3764151497Sru *  is_line_start - returns TRUE if we are at the start of a line.
3765151497Sru */
3766151497Sru
3767151497Sruint html_printer::is_line_start (int nf)
3768151497Sru{
3769151497Sru  int line_start  = FALSE;
3770151497Sru  int result      = TRUE;
3771151497Sru  text_glob *orig = page_contents->glyphs.get_data();
3772151497Sru  text_glob *g;
3773151497Sru
3774151497Sru  if (! page_contents->glyphs.is_equal_to_head()) {
3775151497Sru    do {
3776151497Sru      page_contents->glyphs.move_left();
3777151497Sru      g = page_contents->glyphs.get_data();
3778151497Sru      result = g->is_a_tag();
3779151497Sru      if (g->is_fi())
3780151497Sru	nf = FALSE;
3781151497Sru      else if (g->is_nf())
3782151497Sru	nf = TRUE;
3783151497Sru      line_start = g->is_col() || g->is_br() || (nf && g->is_eol());
3784151497Sru    } while ((!line_start) && (result));
3785151497Sru    /*
3786151497Sru     *  now restore our previous position.
3787151497Sru     */
3788151497Sru    while (page_contents->glyphs.get_data() != orig)
3789151497Sru      page_contents->glyphs.move_right();
3790151497Sru  }
3791151497Sru  return result;
3792151497Sru}
3793151497Sru
3794151497Sru/*
3795114402Sru *  is_font_courier - returns TRUE if the font, f, is courier.
3796114402Sru */
3797114402Sru
3798114402Sruint html_printer::is_font_courier (font *f)
3799114402Sru{
3800114402Sru  if (f != 0) {
3801114402Sru    const char *fontname = f->get_name();
3802114402Sru
3803114402Sru    return( (fontname != 0) && (fontname[0] == 'C') );
3804114402Sru  }
3805151497Sru  return FALSE;
3806114402Sru}
3807114402Sru
3808114402Sru/*
3809114402Sru *  end_font - shuts down the font corresponding to fontname.
3810114402Sru */
3811114402Sru
3812114402Sruvoid html_printer::end_font (const char *fontname)
3813114402Sru{
3814114402Sru  if (strcmp(fontname, "B") == 0) {
3815114402Sru    current_paragraph->done_bold();
3816114402Sru  } else if (strcmp(fontname, "I") == 0) {
3817114402Sru    current_paragraph->done_italic();
3818114402Sru  } else if (strcmp(fontname, "BI") == 0) {
3819114402Sru    current_paragraph->done_bold();
3820114402Sru    current_paragraph->done_italic();
3821114402Sru  } else if (strcmp(fontname, "CR") == 0) {
3822114402Sru    current_paragraph->done_tt();
3823114402Sru  } else if (strcmp(fontname, "CI") == 0) {
3824114402Sru    current_paragraph->done_italic();
3825114402Sru    current_paragraph->done_tt();
3826114402Sru  } else if (strcmp(fontname, "CB") == 0) {
3827114402Sru    current_paragraph->done_bold();
3828114402Sru    current_paragraph->done_tt();
3829114402Sru  } else if (strcmp(fontname, "CBI") == 0) {
3830114402Sru    current_paragraph->done_bold();
3831114402Sru    current_paragraph->done_italic();
3832114402Sru    current_paragraph->done_tt();
3833114402Sru  }
3834114402Sru}
3835114402Sru
3836114402Sru/*
3837114402Sru *  start_font - starts the font corresponding to name.
3838114402Sru */
3839114402Sru
3840114402Sruvoid html_printer::start_font (const char *fontname)
3841114402Sru{
3842114402Sru  if (strcmp(fontname, "R") == 0) {
3843114402Sru    current_paragraph->done_bold();
3844114402Sru    current_paragraph->done_italic();
3845114402Sru    current_paragraph->done_tt();
3846114402Sru  } else if (strcmp(fontname, "B") == 0) {
3847114402Sru    current_paragraph->do_bold();
3848114402Sru  } else if (strcmp(fontname, "I") == 0) {
3849114402Sru    current_paragraph->do_italic();
3850114402Sru  } else if (strcmp(fontname, "BI") == 0) {
3851114402Sru    current_paragraph->do_bold();
3852114402Sru    current_paragraph->do_italic();
3853114402Sru  } else if (strcmp(fontname, "CR") == 0) {
3854151497Sru    if ((! fill_on) && (is_courier_until_eol()) &&
3855151497Sru	is_line_start(! fill_on)) {
3856114402Sru      current_paragraph->do_pre();
3857114402Sru    }
3858114402Sru    current_paragraph->do_tt();
3859114402Sru  } else if (strcmp(fontname, "CI") == 0) {
3860151497Sru    if ((! fill_on) && (is_courier_until_eol()) &&
3861151497Sru	is_line_start(! fill_on)) {
3862114402Sru      current_paragraph->do_pre();
3863114402Sru    }
3864114402Sru    current_paragraph->do_tt();
3865114402Sru    current_paragraph->do_italic();
3866114402Sru  } else if (strcmp(fontname, "CB") == 0) {
3867151497Sru    if ((! fill_on) && (is_courier_until_eol()) &&
3868151497Sru	is_line_start(! fill_on)) {
3869114402Sru      current_paragraph->do_pre();
3870114402Sru    }
3871114402Sru    current_paragraph->do_tt();
3872114402Sru    current_paragraph->do_bold();
3873114402Sru  } else if (strcmp(fontname, "CBI") == 0) {
3874151497Sru    if ((! fill_on) && (is_courier_until_eol()) &&
3875151497Sru	is_line_start(! fill_on)) {
3876114402Sru      current_paragraph->do_pre();
3877114402Sru    }
3878114402Sru    current_paragraph->do_tt();
3879114402Sru    current_paragraph->do_italic();
3880114402Sru    current_paragraph->do_bold();
3881114402Sru  }
3882114402Sru}
3883114402Sru
3884114402Sru/*
3885114402Sru *  start_size - from is old font size, to is the new font size.
3886114402Sru *               The html increase <big> and <small> decrease alters the
3887114402Sru *               font size by 20%. We try and map these onto glyph sizes.
3888114402Sru */
3889114402Sru
3890114402Sruvoid html_printer::start_size (int from, int to)
3891114402Sru{
3892114402Sru  if (from < to) {
3893114402Sru    while (from < to) {
3894114402Sru      current_paragraph->do_big();
3895114402Sru      from += SIZE_INCREMENT;
3896114402Sru    }
3897114402Sru  } else if (from > to) {
3898114402Sru    while (from > to) {
3899114402Sru      current_paragraph->do_small();
3900114402Sru      from -= SIZE_INCREMENT;
3901114402Sru    }
3902114402Sru  }
3903114402Sru}
3904114402Sru
3905114402Sru/*
3906114402Sru *  do_font - checks to see whether we need to alter the html font.
3907114402Sru */
3908114402Sru
3909114402Sruvoid html_printer::do_font (text_glob *g)
3910114402Sru{
3911114402Sru  /*
3912114402Sru   *  check if the output_style.point_size has not been set yet
3913114402Sru   *  this allow users to place .ps at the top of their troff files
3914114402Sru   *  and grohtml can then treat the .ps value as the base font size (3)
3915114402Sru   */
3916114402Sru  if (output_style.point_size == -1) {
3917114402Sru    output_style.point_size = pointsize;
3918114402Sru  }
3919114402Sru
3920114402Sru  if (g->text_style.f != output_style.f) {
3921114402Sru    if (output_style.f != 0) {
3922114402Sru      end_font(output_style.f->get_name());
3923114402Sru    }
3924114402Sru    output_style.f = g->text_style.f;
3925114402Sru    if (output_style.f != 0) {
3926114402Sru      start_font(output_style.f->get_name());
3927114402Sru    }
3928114402Sru  }
3929114402Sru  if (output_style.point_size != g->text_style.point_size) {
3930114402Sru    do_sup_or_sub(g);
3931114402Sru    if ((output_style.point_size > 0) &&
3932114402Sru	(g->text_style.point_size > 0)) {
3933114402Sru      start_size(output_style.point_size, g->text_style.point_size);
3934114402Sru    }
3935114402Sru    if (g->text_style.point_size > 0) {
3936114402Sru      output_style.point_size = g->text_style.point_size;
3937114402Sru    }
3938114402Sru  }
3939114402Sru  if (output_style.col != g->text_style.col) {
3940114402Sru    current_paragraph->done_color();
3941114402Sru    output_style.col = g->text_style.col;
3942114402Sru    current_paragraph->do_color(&output_style.col);
3943114402Sru  }
3944114402Sru}
3945114402Sru
3946114402Sru/*
3947114402Sru *  start_subscript - returns TRUE if, g, looks like a subscript start.
3948114402Sru */
3949114402Sru
3950114402Sruint html_printer::start_subscript (text_glob *g)
3951114402Sru{
3952114402Sru  int r        = font::res;
3953114402Sru  int height   = output_style.point_size*r/72;
3954114402Sru
3955114402Sru  return( (output_style.point_size != 0) &&
3956114402Sru	  (output_vpos < g->minv) &&
3957114402Sru	  (output_vpos-height > g->maxv) &&
3958114402Sru	  (output_style.point_size > g->text_style.point_size) );
3959114402Sru}
3960114402Sru
3961114402Sru/*
3962114402Sru *  start_superscript - returns TRUE if, g, looks like a superscript start.
3963114402Sru */
3964114402Sru
3965114402Sruint html_printer::start_superscript (text_glob *g)
3966114402Sru{
3967114402Sru  int r        = font::res;
3968114402Sru  int height   = output_style.point_size*r/72;
3969114402Sru
3970114402Sru  return( (output_style.point_size != 0) &&
3971114402Sru	  (output_vpos > g->minv) &&
3972114402Sru	  (output_vpos-height < g->maxv) &&
3973114402Sru	  (output_style.point_size > g->text_style.point_size) );
3974114402Sru}
3975114402Sru
3976114402Sru/*
3977114402Sru *  end_subscript - returns TRUE if, g, looks like the end of a subscript.
3978114402Sru */
3979114402Sru
3980114402Sruint html_printer::end_subscript (text_glob *g)
3981114402Sru{
3982114402Sru  int r        = font::res;
3983114402Sru  int height   = output_style.point_size*r/72;
3984114402Sru
3985114402Sru  return( (output_style.point_size != 0) &&
3986114402Sru	  (g->minv < output_vpos) &&
3987114402Sru	  (output_vpos-height > g->maxv) &&
3988114402Sru	  (output_style.point_size < g->text_style.point_size) );
3989114402Sru}
3990114402Sru
3991114402Sru/*
3992114402Sru *  end_superscript - returns TRUE if, g, looks like the end of a superscript.
3993114402Sru */
3994114402Sru
3995114402Sruint html_printer::end_superscript (text_glob *g)
3996114402Sru{
3997114402Sru  int r        = font::res;
3998114402Sru  int height   = output_style.point_size*r/72;
3999114402Sru
4000114402Sru  return( (output_style.point_size != 0) &&
4001114402Sru	  (g->minv > output_vpos) &&
4002114402Sru	  (output_vpos-height < g->maxv) &&
4003114402Sru	  (output_style.point_size < g->text_style.point_size) );
4004114402Sru}
4005114402Sru
4006114402Sru/*
4007114402Sru *  do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript
4008114402Sru *                  start/end and it calls the services of html-text to issue the
4009114402Sru *                  appropriate tags.
4010114402Sru */
4011114402Sru
4012114402Sruvoid html_printer::do_sup_or_sub (text_glob *g)
4013114402Sru{
4014114402Sru  if (! supress_sub_sup) {
4015114402Sru    if (start_subscript(g)) {
4016114402Sru      current_paragraph->do_sub();
4017114402Sru    } else if (start_superscript(g)) {
4018114402Sru      current_paragraph->do_sup();
4019114402Sru    } else if (end_subscript(g)) {
4020114402Sru      current_paragraph->done_sub();
4021114402Sru    } else if (end_superscript(g)) {
4022114402Sru      current_paragraph->done_sup();
4023114402Sru    }
4024114402Sru  }
4025114402Sru}
4026114402Sru
4027114402Sru/*
4028151497Sru *  do_end_para - writes out the html text after shutting down the
4029151497Sru *                current paragraph.
4030151497Sru */
4031151497Sru
4032151497Sruvoid html_printer::do_end_para (text_glob *g)
4033151497Sru{
4034151497Sru  do_font(g);
4035151497Sru  current_paragraph->done_para();
4036151497Sru  current_paragraph->remove_para_space();
4037151497Sru  html.put_string(g->text_string+9);
4038151497Sru  output_vpos     = g->minv;
4039151497Sru  output_hpos     = g->maxh;
4040151497Sru  output_vpos_max = g->maxv;
4041151497Sru  supress_sub_sup = FALSE;
4042151497Sru}
4043151497Sru
4044151497Sru/*
4045114402Sru *  emit_html - write out the html text
4046114402Sru */
4047114402Sru
4048114402Sruvoid html_printer::emit_html (text_glob *g)
4049114402Sru{
4050114402Sru  do_font(g);
4051114402Sru  determine_space(g);
4052114402Sru  current_paragraph->do_emittext(g->text_string, g->text_length);
4053114402Sru  output_vpos     = g->minv;
4054114402Sru  output_hpos     = g->maxh;
4055114402Sru  output_vpos_max = g->maxv;
4056114402Sru  supress_sub_sup = FALSE;
4057114402Sru}
4058114402Sru
4059114402Sru/*
4060114402Sru *  flush_sbuf - flushes the current sbuf into the list of glyphs.
4061114402Sru */
4062114402Sru
4063114402Sruvoid html_printer::flush_sbuf()
4064114402Sru{
4065114402Sru  if (sbuf.length() > 0) {
4066114402Sru    int r=font::res;   // resolution of the device
4067114402Sru    set_style(sbuf_style);
4068151497Sru
4069114402Sru    if (overstrike_detected && (! is_bold(sbuf_style.f))) {
4070114402Sru      font *bold_font = make_bold(sbuf_style.f);
4071114402Sru      if (bold_font != NULL)
4072114402Sru	sbuf_style.f = bold_font;
4073114402Sru    }
4074114402Sru
4075114402Sru    page_contents->add(&sbuf_style, sbuf,
4076114402Sru		       line_number,
4077114402Sru		       sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos,
4078114402Sru		       sbuf_vpos                           , sbuf_end_hpos);
4079114402Sru
4080114402Sru    output_hpos = sbuf_end_hpos;
4081114402Sru    output_vpos = sbuf_vpos;
4082114402Sru    last_sbuf_length = 0;
4083114402Sru    sbuf_prev_hpos = sbuf_end_hpos;
4084114402Sru    overstrike_detected = FALSE;
4085114402Sru    sbuf.clear();
4086114402Sru  }
4087114402Sru}
4088114402Sru
4089114402Sruvoid html_printer::set_line_thickness(const environment *env)
4090114402Sru{
4091114402Sru  line_thickness = env->size;
4092114402Sru}
4093114402Sru
4094114402Sruvoid html_printer::draw(int code, int *p, int np, const environment *env)
4095114402Sru{
4096114402Sru  switch (code) {
4097114402Sru
4098114402Sru  case 'l':
4099114402Sru# if 0
4100114402Sru    if (np == 2) {
4101114402Sru      page_contents->add_line(&sbuf_style,
4102114402Sru			      line_number,
4103114402Sru			      env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1], line_thickness);
4104114402Sru    } else {
4105114402Sru      error("2 arguments required for line");
4106114402Sru    }
4107114402Sru# endif
4108114402Sru    break;
4109114402Sru  case 't':
4110114402Sru    {
4111114402Sru      if (np == 0) {
4112114402Sru	line_thickness = -1;
4113114402Sru      } else {
4114114402Sru	// troff gratuitously adds an extra 0
4115114402Sru	if (np != 1 && np != 2) {
4116114402Sru	  error("0 or 1 argument required for thickness");
4117114402Sru	  break;
4118114402Sru	}
4119114402Sru	line_thickness = p[0];
4120114402Sru      }
4121114402Sru      break;
4122114402Sru    }
4123114402Sru
4124114402Sru  case 'P':
4125114402Sru    break;
4126114402Sru  case 'p':
4127114402Sru    break;
4128114402Sru  case 'E':
4129114402Sru    break;
4130114402Sru  case 'e':
4131114402Sru    break;
4132114402Sru  case 'C':
4133114402Sru    break;
4134114402Sru  case 'c':
4135114402Sru    break;
4136114402Sru  case 'a':
4137114402Sru    break;
4138114402Sru  case '~':
4139114402Sru    break;
4140114402Sru  case 'f':
4141114402Sru    break;
4142114402Sru  case 'F':
4143114402Sru    // fill with color env->fill
4144114402Sru    if (background != NULL)
4145114402Sru      delete background;
4146114402Sru    background = new color;
4147114402Sru    *background = *env->fill;
4148114402Sru    break;
4149114402Sru
4150114402Sru  default:
4151114402Sru    error("unrecognised drawing command `%1'", char(code));
4152114402Sru    break;
4153114402Sru  }
4154114402Sru}
4155114402Sru
4156114402Sruhtml_printer::html_printer()
4157114402Sru: html(0, MAX_LINE_LENGTH),
4158114402Sru  no_of_printed_pages(0),
4159114402Sru  last_sbuf_length(0),
4160114402Sru  overstrike_detected(FALSE),
4161114402Sru  output_hpos(-1),
4162114402Sru  output_vpos(-1),
4163114402Sru  output_vpos_max(-1),
4164114402Sru  line_thickness(-1),
4165114402Sru  inside_font_style(0),
4166114402Sru  page_number(0),
4167114402Sru  header_indent(-1),
4168114402Sru  supress_sub_sup(TRUE),
4169114402Sru  cutoff_heading(100),
4170114402Sru  indent(NULL),
4171114402Sru  table(NULL),
4172114402Sru  end_center(0),
4173114402Sru  end_tempindent(0),
4174114402Sru  next_tag(INLINE),
4175114402Sru  fill_on(TRUE),
4176114402Sru  max_linelength(-1),
4177114402Sru  linelength(0),
4178114402Sru  pageoffset(0),
4179151497Sru  troff_indent(0),
4180151497Sru  device_indent(0),
4181151497Sru  temp_indent(0),
4182151497Sru  pointsize(base_point_size),
4183114402Sru  line_number(0),
4184151497Sru  background(default_background),
4185151497Sru  seen_indent(FALSE),
4186151497Sru  next_indent(0),
4187151497Sru  seen_pageoffset(FALSE),
4188151497Sru  next_pageoffset(0),
4189151497Sru  seen_linelength(FALSE),
4190151497Sru  next_linelength(0),
4191151497Sru  seen_center(FALSE),
4192151497Sru  next_center(0),
4193151497Sru  seen_space(0),
4194151497Sru  seen_break(0),
4195151497Sru  current_column(0),
4196151497Sru  row_space(FALSE)
4197114402Sru{
4198114402Sru  file_list.add_new_file(xtmpfile());
4199114402Sru  html.set_file(file_list.get_file());
4200114402Sru  if (font::hor != 24)
4201114402Sru    fatal("horizontal resolution must be 24");
4202114402Sru  if (font::vert != 40)
4203114402Sru    fatal("vertical resolution must be 40");
4204114402Sru#if 0
4205114402Sru  // should be sorted html..
4206114402Sru  if (font::res % (font::sizescale*72) != 0)
4207114402Sru    fatal("res must be a multiple of 72*sizescale");
4208114402Sru#endif
4209114402Sru  int r = font::res;
4210114402Sru  int point = 0;
4211114402Sru  while (r % 10 == 0) {
4212114402Sru    r /= 10;
4213114402Sru    point++;
4214114402Sru  }
4215114402Sru  res               = r;
4216114402Sru  html.set_fixed_point(point);
4217114402Sru  space_char_index  = font::name_to_index("space");
4218114402Sru  space_width       = font::hor;
4219114402Sru  paper_length      = font::paperlength;
4220114402Sru  linelength        = font::res*13/2;
4221114402Sru  if (paper_length == 0)
4222114402Sru    paper_length    = 11*font::res;
4223114402Sru
4224114402Sru  page_contents = new page();
4225114402Sru}
4226114402Sru
4227114402Sru/*
4228114402Sru *  add_to_sbuf - adds character code or name to the sbuf.
4229114402Sru */
4230114402Sru
4231151497Sruvoid html_printer::add_to_sbuf (int idx, const string &s)
4232114402Sru{
4233114402Sru  if (sbuf_style.f == NULL)
4234114402Sru    return;
4235114402Sru
4236114402Sru  char *html_glyph = NULL;
4237151497Sru  unsigned int code = sbuf_style.f->get_code(idx);
4238114402Sru
4239114402Sru  if (s.empty()) {
4240151497Sru    if (sbuf_style.f->contains(idx))
4241151497Sru      html_glyph = (char *)sbuf_style.f->get_special_device_encoding(idx);
4242114402Sru    else
4243114402Sru      html_glyph = NULL;
4244114402Sru
4245114402Sru    if ((html_glyph == NULL) && (code >= UNICODE_DESC_START))
4246114402Sru      html_glyph = to_unicode(code);
4247114402Sru  } else
4248114402Sru    html_glyph = get_html_translation(sbuf_style.f, s);
4249114402Sru
4250114402Sru  last_sbuf_length = sbuf.length();
4251114402Sru  if (html_glyph == NULL)
4252114402Sru    sbuf += ((char)code);
4253114402Sru  else
4254114402Sru    sbuf += html_glyph;
4255114402Sru}
4256114402Sru
4257151497Sruint html_printer::sbuf_continuation (int idx, const char *name,
4258114402Sru				     const environment *env, int w)
4259114402Sru{
4260114402Sru  /*
4261114402Sru   *  lets see whether the glyph is closer to the end of sbuf
4262114402Sru   */
4263114402Sru  if ((sbuf_end_hpos == env->hpos)
4264114402Sru      || ((sbuf_prev_hpos < sbuf_end_hpos)
4265114402Sru	  && (env->hpos < sbuf_end_hpos)
4266114402Sru	  && ((sbuf_end_hpos-env->hpos < env->hpos-sbuf_prev_hpos)))) {
4267151497Sru    add_to_sbuf(idx, name);
4268114402Sru    sbuf_prev_hpos = sbuf_end_hpos;
4269114402Sru    sbuf_end_hpos += w + sbuf_kern;
4270114402Sru    return TRUE;
4271114402Sru  } else {
4272114402Sru    if ((env->hpos >= sbuf_end_hpos) &&
4273114402Sru	((sbuf_kern == 0) || (sbuf_end_hpos - sbuf_kern != env->hpos))) {
4274114402Sru      /*
4275114402Sru       *  lets see whether a space is needed or not
4276114402Sru       */
4277114402Sru
4278114402Sru      if (env->hpos-sbuf_end_hpos < space_width) {
4279151497Sru	add_to_sbuf(idx, name);
4280114402Sru	sbuf_prev_hpos = sbuf_end_hpos;
4281114402Sru	sbuf_end_hpos = env->hpos + w;
4282114402Sru	return TRUE;
4283114402Sru      }
4284114402Sru    }
4285114402Sru  }
4286114402Sru  return FALSE ;
4287114402Sru}
4288114402Sru
4289114402Sru/*
4290114402Sru *  get_html_translation - given the position of the character and its name
4291114402Sru *                         return the device encoding for such character.
4292114402Sru */
4293114402Sru
4294114402Sruchar *get_html_translation (font *f, const string &name)
4295114402Sru{
4296151497Sru  int idx;
4297114402Sru
4298114402Sru  if ((f == 0) || name.empty())
4299114402Sru    return NULL;
4300114402Sru  else {
4301151497Sru    idx = f->name_to_index((char *)(name + '\0').contents());
4302151497Sru    if (idx == 0) {
4303114402Sru      error("character `%s' not found", (name + '\0').contents());
4304114402Sru      return NULL;
4305114402Sru    } else
4306151497Sru      if (f->contains(idx))
4307151497Sru	return (char *)f->get_special_device_encoding(idx);
4308114402Sru      else
4309114402Sru	return NULL;
4310114402Sru  }
4311114402Sru}
4312114402Sru
4313114402Sru/*
4314114402Sru *  overstrike - returns TRUE if the glyph (i, name) is going to overstrike
4315114402Sru *               a previous glyph in sbuf.
4316114402Sru *               If TRUE the font is changed to bold and the previous sbuf
4317114402Sru *               is flushed.
4318114402Sru */
4319114402Sru
4320151497Sruint html_printer::overstrike(int idx, const char *name, const environment *env, int w)
4321114402Sru{
4322114402Sru  if ((env->hpos < sbuf_end_hpos)
4323114402Sru      || ((sbuf_kern != 0) && (sbuf_end_hpos - sbuf_kern < env->hpos))) {
4324114402Sru    /*
4325114402Sru     *  at this point we have detected an overlap
4326114402Sru     */
4327114402Sru    if (overstrike_detected) {
4328114402Sru      /* already detected, remove previous glyph and use this glyph */
4329114402Sru      sbuf.set_length(last_sbuf_length);
4330151497Sru      add_to_sbuf(idx, name);
4331114402Sru      sbuf_end_hpos = env->hpos + w;
4332114402Sru      return TRUE;
4333114402Sru    } else {
4334114402Sru      /* first time we have detected an overstrike in the sbuf */
4335114402Sru      sbuf.set_length(last_sbuf_length); /* remove previous glyph */
4336114402Sru      if (! is_bold(sbuf_style.f))
4337114402Sru	flush_sbuf();
4338114402Sru      overstrike_detected = TRUE;
4339151497Sru      add_to_sbuf(idx, name);
4340114402Sru      sbuf_end_hpos = env->hpos + w;
4341114402Sru      return TRUE;
4342114402Sru    }
4343114402Sru  }
4344114402Sru  return FALSE ;
4345114402Sru}
4346114402Sru
4347114402Sru/*
4348151497Sru *  set_char - adds a character into the sbuf if it is a continuation
4349151497Sru *             with the previous word otherwise flush the current sbuf
4350151497Sru *             and add character anew.
4351114402Sru */
4352114402Sru
4353151497Sruvoid html_printer::set_char(int i, font *f, const environment *env,
4354151497Sru			    int w, const char *name)
4355114402Sru{
4356114402Sru  style sty(f, env->size, env->height, env->slant, env->fontno, *env->col);
4357114402Sru  if (sty.slant != 0) {
4358114402Sru    if (sty.slant > 80 || sty.slant < -80) {
4359114402Sru      error("silly slant `%1' degrees", sty.slant);
4360114402Sru      sty.slant = 0;
4361114402Sru    }
4362114402Sru  }
4363114402Sru  if (((! sbuf.empty()) && (sty == sbuf_style) && (sbuf_vpos == env->vpos))
4364114402Sru      && (sbuf_continuation(i, name, env, w) || overstrike(i, name, env, w)))
4365114402Sru    return;
4366114402Sru
4367114402Sru  flush_sbuf();
4368151497Sru  if (sbuf_style.f == NULL)
4369151497Sru    sbuf_style = sty;
4370114402Sru  add_to_sbuf(i, name);
4371114402Sru  sbuf_end_hpos = env->hpos + w;
4372114402Sru  sbuf_start_hpos = env->hpos;
4373114402Sru  sbuf_prev_hpos = env->hpos;
4374114402Sru  sbuf_vpos = env->vpos;
4375114402Sru  sbuf_style = sty;
4376114402Sru  sbuf_kern = 0;
4377114402Sru}
4378114402Sru
4379114402Sru/*
4380114402Sru *  set_numbered_char - handle numbered characters.
4381114402Sru *                      Negative values are interpreted as unbreakable spaces;
4382114402Sru *                      the value (taken positive) gives the width.
4383114402Sru */
4384114402Sru
4385114402Sruvoid html_printer::set_numbered_char(int num, const environment *env,
4386114402Sru				     int *widthp)
4387114402Sru{
4388114402Sru  int nbsp_width = 0;
4389114402Sru  if (num < 0) {
4390114402Sru    nbsp_width = -num;
4391114402Sru    num = 160;		// &nbsp;
4392114402Sru  }
4393114402Sru  int i = font::number_to_index(num);
4394114402Sru  int fn = env->fontno;
4395114402Sru  if (fn < 0 || fn >= nfonts) {
4396114402Sru    error("bad font position `%1'", fn);
4397114402Sru    return;
4398114402Sru  }
4399114402Sru  font *f = font_table[fn];
4400114402Sru  if (f == 0) {
4401114402Sru    error("no font mounted at `%1'", fn);
4402114402Sru    return;
4403114402Sru  }
4404114402Sru  if (!f->contains(i)) {
4405114402Sru    error("font `%1' does not contain numbered character %2",
4406114402Sru	  f->get_name(),
4407114402Sru	  num);
4408114402Sru    return;
4409114402Sru  }
4410114402Sru  int w;
4411114402Sru  if (nbsp_width)
4412114402Sru    w = nbsp_width;
4413114402Sru  else
4414114402Sru    w = f->get_width(i, env->size);
4415151497Sru  w = round_width(w);
4416114402Sru  if (widthp)
4417114402Sru    *widthp = w;
4418114402Sru  set_char(i, f, env, w, 0);
4419114402Sru}
4420114402Sru
4421151497Sruint html_printer::set_char_and_width(const char *nm, const environment *env,
4422151497Sru				     int *widthp, font **f)
4423151497Sru{
4424151497Sru  int i = font::name_to_index(nm);
4425151497Sru  int fn = env->fontno;
4426151497Sru  if (fn < 0 || fn >= nfonts) {
4427151497Sru    error("bad font position `%1'", fn);
4428151497Sru    return -1;
4429151497Sru  }
4430151497Sru  *f = font_table[fn];
4431151497Sru  if (*f == 0) {
4432151497Sru    error("no font mounted at `%1'", fn);
4433151497Sru    return -1;
4434151497Sru  }
4435151497Sru  if (!(*f)->contains(i)) {
4436151497Sru    if (nm[0] != '\0' && nm[1] == '\0')
4437151497Sru      error("font `%1' does not contain ascii character `%2'",
4438151497Sru	    (*f)->get_name(),
4439151497Sru	    nm[0]);
4440151497Sru    else
4441151497Sru      error("font `%1' does not contain special character `%2'",
4442151497Sru	    (*f)->get_name(),
4443151497Sru	    nm);
4444151497Sru    return -1;
4445151497Sru  }
4446151497Sru  int w = (*f)->get_width(i, env->size);
4447151497Sru  w = round_width(w);
4448151497Sru  if (widthp)
4449151497Sru    *widthp = w;
4450151497Sru  return i;
4451151497Sru}
4452151497Sru
4453114402Sru/*
4454114402Sru *  write_title - writes the title to this document
4455114402Sru */
4456114402Sru
4457114402Sruvoid html_printer::write_title (int in_head)
4458114402Sru{
4459114402Sru  if (title.has_been_found) {
4460114402Sru    if (in_head) {
4461114402Sru      html.put_string("<title>");
4462114402Sru      html.put_string(title.text);
4463114402Sru      html.put_string("</title>").nl().nl();
4464114402Sru    } else {
4465114402Sru      title.has_been_written = TRUE;
4466114402Sru      if (title.with_h1) {
4467114402Sru	html.put_string("<h1 align=center>");
4468114402Sru	html.put_string(title.text);
4469114402Sru	html.put_string("</h1>").nl().nl();
4470114402Sru      }
4471114402Sru    }
4472114402Sru  } else if (in_head) {
4473114402Sru    // place empty title tags to help conform to `tidy'
4474114402Sru    html.put_string("<title></title>").nl();
4475114402Sru  }
4476114402Sru}
4477114402Sru
4478114402Sru/*
4479114402Sru *  write_rule - emits a html rule tag, if the auto_rule boolean is true.
4480114402Sru */
4481114402Sru
4482114402Srustatic void write_rule (void)
4483114402Sru{
4484114402Sru  if (auto_rule)
4485114402Sru    fputs("<hr>\n", stdout);
4486114402Sru}
4487114402Sru
4488114402Sruvoid html_printer::begin_page(int n)
4489114402Sru{
4490114402Sru  page_number            =  n;
4491114402Sru#if defined(DEBUGGING)
4492114402Sru  html.begin_comment("Page: ").put_string(i_to_a(page_number)).end_comment();;
4493114402Sru#endif
4494114402Sru  no_of_printed_pages++;
4495114402Sru
4496114402Sru  output_style.f         =  0;
4497114402Sru  output_style.point_size= -1;
4498114402Sru  output_space_code      = 32;
4499114402Sru  output_draw_point_size = -1;
4500114402Sru  output_line_thickness  = -1;
4501114402Sru  output_hpos            = -1;
4502114402Sru  output_vpos            = -1;
4503114402Sru  output_vpos_max        = -1;
4504114402Sru  current_paragraph      = new html_text(&html);
4505151497Sru  do_indent(get_troff_indent(), pageoffset, linelength);
4506151497Sru  current_paragraph->do_para("", FALSE);
4507114402Sru}
4508114402Sru
4509114402Sruvoid html_printer::end_page(int)
4510114402Sru{
4511114402Sru  flush_sbuf();
4512114402Sru  flush_page();
4513114402Sru}
4514114402Sru
4515114402Srufont *html_printer::make_font(const char *nm)
4516114402Sru{
4517114402Sru  return html_font::load_html_font(nm);
4518114402Sru}
4519114402Sru
4520114402Sruvoid html_printer::do_body (void)
4521114402Sru{
4522114402Sru  if (background == NULL)
4523114402Sru    fputs("<body>\n\n", stdout);
4524114402Sru  else {
4525114402Sru    unsigned int r, g, b;
4526114402Sru    char buf[6+1];
4527114402Sru
4528114402Sru    background->get_rgb(&r, &g, &b);
4529114402Sru    // we have to scale 0..0xFFFF to 0..0xFF
4530114402Sru    sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101);
4531114402Sru
4532114402Sru    fputs("<body bgcolor=\"#", stdout);
4533114402Sru    fputs(buf, stdout);
4534114402Sru    fputs("\">\n\n", stdout);
4535114402Sru  }
4536114402Sru}
4537114402Sru
4538151497Sru/*
4539151497Sru *  emit_link - generates: <a href="to">name</a>
4540151497Sru */
4541151497Sru
4542151497Sruvoid html_printer::emit_link (const string &to, const char *name)
4543151497Sru{
4544151497Sru  fputs("<a href=\"", stdout);
4545151497Sru  fputs(to.contents(), stdout);
4546151497Sru  fputs("\">", stdout);
4547151497Sru  fputs(name, stdout);
4548151497Sru  fputs("</a>", stdout);
4549151497Sru}
4550151497Sru
4551151497Sru/*
4552151497Sru *  write_navigation - writes out the links which navigate between
4553151497Sru *                     file fragments.
4554151497Sru */
4555151497Sru
4556151497Sruvoid html_printer::write_navigation (const string &top, const string &prev,
4557151497Sru				     const string &next, const string &current)
4558151497Sru{
4559151497Sru  int need_bar = FALSE;
4560151497Sru
4561151497Sru  if (multiple_files) {
4562151497Sru    write_rule();
4563151497Sru    fputs("[ ", stdout);
4564151497Sru    if ((strcmp(prev.contents(), "") != 0) && prev != top && prev != current) {
4565151497Sru      emit_link(prev, "prev");
4566151497Sru      need_bar = TRUE;
4567151497Sru    }
4568151497Sru    if ((strcmp(next.contents(), "") != 0) && next != top && next != current) {
4569151497Sru      if (need_bar)
4570151497Sru	fputs(" | ", stdout);
4571151497Sru      emit_link(next, "next");
4572151497Sru      need_bar = TRUE;
4573151497Sru    }
4574151497Sru    if (top != "<standard input>" && (strcmp(top.contents(), "") != 0) && top != current) {
4575151497Sru      if (need_bar)
4576151497Sru	fputs(" | ", stdout);
4577151497Sru      emit_link(top, "top");
4578151497Sru    }
4579151497Sru    fputs(" ]\n", stdout);
4580151497Sru    write_rule();
4581151497Sru  }
4582151497Sru}
4583151497Sru
4584151497Sru/*
4585151497Sru *  do_file_components - scan the file list copying each temporary
4586151497Sru *                       file in turn.  This is used twofold:
4587151497Sru *
4588151497Sru *                       firstly to emit section heading links,
4589151497Sru *                       between file fragments if required and
4590151497Sru *                       secondly to generate jobname file fragments
4591151497Sru *                       if required.
4592151497Sru */
4593151497Sru
4594151497Sruvoid html_printer::do_file_components (void)
4595151497Sru{
4596151497Sru  int fragment_no = 1;
4597151497Sru  string top;
4598151497Sru  string prev;
4599151497Sru  string next;
4600151497Sru  string current;
4601151497Sru
4602151497Sru  file_list.start_of_list();
4603151497Sru  top = string(job_name);
4604151497Sru  top += string(".html");
4605151497Sru  top += '\0';
4606151497Sru  next = file_list.next_file_name();
4607151497Sru  next += '\0';
4608151497Sru  current = next;
4609151497Sru  while (file_list.get_file() != 0) {
4610151497Sru    if (fseek(file_list.get_file(), 0L, 0) < 0)
4611151497Sru      fatal("fseek on temporary file failed");
4612151497Sru    html.copy_file(file_list.get_file());
4613151497Sru    fclose(file_list.get_file());
4614151497Sru
4615151497Sru    file_list.move_next();
4616151497Sru    if (file_list.is_new_output_file()) {
4617151497Sru      if (fragment_no > 1)
4618151497Sru	write_navigation(top, prev, next, current);
4619151497Sru      prev = current;
4620151497Sru      current = next;
4621151497Sru      next = file_list.next_file_name();
4622151497Sru      next += '\0';
4623151497Sru      string split_file = file_list.file_name();
4624151497Sru      split_file += '\0';
4625151497Sru      fflush(stdout);
4626151497Sru      freopen(split_file.contents(), "w", stdout);
4627151497Sru      fragment_no++;
4628151497Sru      writeHeadMetaStyle();
4629151497Sru      write_navigation(top, prev, next, current);
4630151497Sru    }
4631151497Sru    if (file_list.are_links_required())
4632151497Sru      header.write_headings(stdout, TRUE);
4633151497Sru  }
4634151497Sru  if (fragment_no > 1)
4635151497Sru    write_navigation(top, prev, next, current);
4636151497Sru  else
4637151497Sru    write_rule();
4638151497Sru}
4639151497Sru
4640151497Sru/*
4641151497Sru *  writeHeadMetaStyle - emits the <head> <meta> and <style> tags and
4642151497Sru *                       related information.
4643151497Sru */
4644151497Sru
4645151497Sruvoid html_printer::writeHeadMetaStyle (void)
4646151497Sru{
4647151497Sru  fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n", stdout);
4648151497Sru  fputs("\"http://www.w3.org/TR/html4/loose.dtd\">\n", stdout);
4649151497Sru
4650151497Sru  fputs("<html>\n", stdout);
4651151497Sru  fputs("<head>\n", stdout);
4652151497Sru  fputs("<meta name=\"generator\" "
4653151497Sru	      "content=\"groff -Thtml, see www.gnu.org\">\n", stdout);
4654151497Sru  fputs("<meta http-equiv=\"Content-Type\" "
4655151497Sru	      "content=\"text/html; charset=US-ASCII\">\n", stdout);
4656151497Sru  fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout);
4657151497Sru
4658151497Sru  fputs("<style type=\"text/css\">\n", stdout);
4659151497Sru  fputs("       p     { margin-top: 0; margin-bottom: 0; }\n", stdout);
4660151497Sru  fputs("       pre   { margin-top: 0; margin-bottom: 0; }\n", stdout);
4661151497Sru  fputs("       table { margin-top: 0; margin-bottom: 0; }\n", stdout);
4662151497Sru  fputs("</style>\n", stdout);
4663151497Sru}
4664151497Sru
4665114402Sruhtml_printer::~html_printer()
4666114402Sru{
4667114402Sru#ifdef LONG_FOR_TIME_T
4668114402Sru  long t;
4669114402Sru#else
4670114402Sru  time_t t;
4671114402Sru#endif
4672114402Sru
4673114402Sru  current_paragraph->flush_text();
4674114402Sru  html.end_line();
4675114402Sru  html.set_file(stdout);
4676114402Sru  html.begin_comment("Creator     : ")
4677114402Sru    .put_string("groff ")
4678114402Sru    .put_string("version ")
4679114402Sru    .put_string(Version_string)
4680114402Sru    .end_comment();
4681114402Sru
4682114402Sru  t = time(0);
4683114402Sru  html.begin_comment("CreationDate: ")
4684114402Sru    .put_string(ctime(&t), strlen(ctime(&t))-1)
4685114402Sru    .end_comment();
4686114402Sru
4687151497Sru  writeHeadMetaStyle();
4688114402Sru
4689114402Sru  write_title(TRUE);
4690151497Sru  head_info += '\0';
4691151497Sru  fputs(head_info.contents(), stdout);
4692114402Sru  fputs("</head>\n", stdout);
4693114402Sru  do_body();
4694114402Sru
4695114402Sru  write_title(FALSE);
4696114402Sru  header.write_headings(stdout, FALSE);
4697114402Sru  write_rule();
4698114402Sru#if defined(DEBUGGING)
4699114402Sru  html.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages)).end_comment();
4700114402Sru#endif
4701114402Sru  html.end_line();
4702114402Sru  html.end_line();
4703151497Sru
4704151497Sru  if (multiple_files) {
4705151497Sru    fputs("</body>\n", stdout);
4706151497Sru    fputs("</html>\n", stdout);
4707151497Sru    do_file_components();
4708151497Sru  } else {
4709151497Sru    do_file_components();
4710151497Sru    fputs("</body>\n", stdout);
4711151497Sru    fputs("</html>\n", stdout);
4712114402Sru  }
4713114402Sru}
4714114402Sru
4715114402Sru/*
4716151497Sru *  get_str - returns a dupicate of string, s. The duplicate
4717151497Sru *            string is terminated at the next ',' or ']'.
4718114402Sru */
4719114402Sru
4720151497Srustatic char *get_str (const char *s, char **n)
4721151497Sru{
4722151497Sru  int i=0;
4723151497Sru  char *v;
4724151497Sru
4725151497Sru  while ((s[i] != (char)0) && (s[i] != ',') && (s[i] != ']'))
4726151497Sru    i++;
4727151497Sru  if (i>0) {
4728151497Sru    v = new char[i+1];
4729151497Sru    memcpy(v, s, i+1);
4730151497Sru    v[i] = (char)0;
4731151497Sru    if (s[i] == ',')
4732151497Sru      (*n) = (char *)&s[i+1];
4733151497Sru    else
4734151497Sru      (*n) = (char *)&s[i];
4735151497Sru    return v;
4736151497Sru  }
4737151497Sru  if (s[i] == ',')
4738151497Sru    (*n) = (char *)&s[1];
4739151497Sru  else
4740151497Sru    (*n) = (char *)s;
4741151497Sru  return NULL;
4742151497Sru}
4743151497Sru
4744151497Sru/*
4745151497Sru *  make_val - creates a string from if s is NULL.
4746151497Sru */
4747151497Sru
4748151497Sruchar *make_val (char *s, int v, char *id, char *f, char *l)
4749151497Sru{
4750151497Sru  if (s == NULL) {
4751151497Sru    char buf[30];
4752151497Sru
4753151497Sru    sprintf(buf, "%d", v);
4754151497Sru    return strsave(buf);
4755151497Sru  }
4756151497Sru  else {
4757151497Sru    /*
4758151497Sru     *  check that value, s, is the same as, v.
4759151497Sru     */
4760151497Sru    char *t = s;
4761151497Sru
4762151497Sru    while (*t == '=')
4763151497Sru      t++;
4764151497Sru    if (atoi(t) != v) {
4765151497Sru      if (f == NULL)
4766151497Sru	f = (char *)"stdin";
4767151497Sru      if (l == NULL)
4768151497Sru	l = (char *)"<none>";
4769151497Sru      fprintf(stderr, "%s:%s: grohtml assertion failed at id%s expecting %d and was given %s\n",
4770151497Sru	      f, l, id, v, s);
4771151497Sru    }
4772151497Sru    return s;
4773151497Sru  }
4774151497Sru}
4775151497Sru
4776151497Sru/*
4777151497Sru *  handle_assertion - handles the assertions created via .www:ASSERT
4778151497Sru *                     in www.tmac. See www.tmac for examples.
4779151497Sru *                     This method should be called as we are
4780151497Sru *                     parsing the ditroff input. It checks the x, y
4781151497Sru *                     position assertions. It does _not_ check the
4782151497Sru *                     troff state assertions as these are unknown at this
4783151497Sru *                     point.
4784151497Sru */
4785151497Sru
4786151497Sruvoid html_printer::handle_assertion (int minv, int minh, int maxv, int maxh, const char *s)
4787151497Sru{
4788151497Sru  char *n;
4789151497Sru  char *cmd = get_str(s, &n);
4790151497Sru  char *id  = get_str(n, &n);
4791151497Sru  char *val = get_str(n, &n);
4792151497Sru  char *file= get_str(n, &n);
4793151497Sru  char *line= get_str(n, &n);
4794151497Sru
4795151497Sru  if (strcmp(cmd, "assertion:[x") == 0)
4796151497Sru    as.addx(cmd, id, make_val(val, minh, id, file, line), file, line);
4797151497Sru  else if (strcmp(cmd, "assertion:[y") == 0)
4798151497Sru    as.addy(cmd, id, make_val(val, minv, id, file, line), file, line);
4799151497Sru  else
4800151497Sru    if (strncmp(cmd, "assertion:[", strlen("assertion:[")) == 0)
4801151497Sru      page_contents->add_tag(&sbuf_style, string(s),
4802151497Sru			     line_number, minv, minh, maxv, maxh);
4803151497Sru}
4804151497Sru
4805151497Sru/*
4806151497Sru *  build_state_assertion - builds the troff state assertions.
4807151497Sru */
4808151497Sru
4809151497Sruvoid html_printer::handle_state_assertion (text_glob *g)
4810151497Sru{
4811151497Sru  if (g != NULL && g->is_a_tag() &&
4812151497Sru      (strncmp(g->text_string, "assertion:[", 11) == 0)) {
4813151497Sru    char *n   = (char *)&g->text_string[11];
4814151497Sru    char *cmd = get_str(n, &n);
4815151497Sru    char *val = get_str(n, &n);
4816151497Sru    (void)get_str(n, &n);	// unused
4817151497Sru    char *file= get_str(n, &n);
4818151497Sru    char *line= get_str(n, &n);
4819151497Sru
4820151497Sru    as.build(cmd, val, file, line);
4821151497Sru  }
4822151497Sru}
4823151497Sru
4824151497Sru/*
4825151497Sru *  special - handle all x X requests from troff. For post-html they
4826151497Sru *            allow users to pass raw html commands, turn auto linked
4827151497Sru *            headings off/on etc.
4828151497Sru */
4829151497Sru
4830114402Sruvoid html_printer::special(char *s, const environment *env, char type)
4831114402Sru{
4832114402Sru  if (type != 'p')
4833114402Sru    return;
4834114402Sru  if (s != 0) {
4835114402Sru    flush_sbuf();
4836114402Sru    if (env->fontno >= 0) {
4837114402Sru      style sty(get_font_from_index(env->fontno), env->size, env->height,
4838114402Sru		env->slant, env->fontno, *env->col);
4839114402Sru      sbuf_style = sty;
4840114402Sru    }
4841114402Sru
4842114402Sru    if (strncmp(s, "html:", 5) == 0) {
4843114402Sru      int r=font::res;   /* resolution of the device */
4844114402Sru      font *f=sbuf_style.f;
4845114402Sru
4846114402Sru      if (f == NULL) {
4847114402Sru	int found=FALSE;
4848114402Sru
4849114402Sru	f = font::load_font("TR", &found);
4850114402Sru      }
4851114402Sru
4852114402Sru      /*
4853114402Sru       *  need to pass rest of string through to html output during flush
4854114402Sru       */
4855114402Sru      page_contents->add_and_encode(&sbuf_style, string(&s[5]),
4856114402Sru				    line_number,
4857114402Sru				    env->vpos-env->size*r/72, env->hpos,
4858151497Sru				    env->vpos               , env->hpos,
4859151497Sru				    FALSE);
4860114402Sru
4861114402Sru      /*
4862151497Sru       * assume that the html command has no width, if it does then
4863151497Sru       * hopefully troff will have fudged this in a macro by
4864151497Sru       * requesting that the formatting move right by the appropriate
4865151497Sru       * amount.
4866114402Sru       */
4867151497Sru    } else if (strncmp(s, "html</p>:", 9) == 0) {
4868151497Sru      int r=font::res;   /* resolution of the device */
4869151497Sru      font *f=sbuf_style.f;
4870151497Sru
4871151497Sru      if (f == NULL) {
4872151497Sru	int found=FALSE;
4873151497Sru
4874151497Sru	f = font::load_font("TR", &found);
4875151497Sru      }
4876151497Sru
4877151497Sru      /*
4878151497Sru       *  need to pass all of string through to html output during flush
4879151497Sru       */
4880151497Sru      page_contents->add_and_encode(&sbuf_style, string(s),
4881151497Sru				    line_number,
4882151497Sru				    env->vpos-env->size*r/72, env->hpos,
4883151497Sru				    env->vpos               , env->hpos,
4884151497Sru				    TRUE);
4885151497Sru
4886151497Sru      /*
4887151497Sru       * assume that the html command has no width, if it does then
4888151497Sru       * hopefully troff will have fudged this in a macro by
4889151497Sru       * requesting that the formatting move right by the appropriate
4890151497Sru       * amount.
4891151497Sru       */
4892114402Sru    } else if (strncmp(s, "index:", 6) == 0) {
4893114402Sru      cutoff_heading = atoi(&s[6]);
4894151497Sru    } else if (strncmp(s, "assertion:[", 11) == 0) {
4895114402Sru      int r=font::res;   /* resolution of the device */
4896114402Sru
4897151497Sru      handle_assertion(env->vpos-env->size*r/72, env->hpos,
4898151497Sru		       env->vpos, env->hpos, s);
4899151497Sru    }
4900151497Sru  }
4901151497Sru}
4902151497Sru
4903151497Sru/*
4904151497Sru *  devtag - handles device troff tags sent from the `troff'.
4905151497Sru *           These include the troff state machine tags:
4906151497Sru *           .br, .sp, .in, .tl, .ll etc
4907151497Sru *
4908151497Sru *           (see man 5 grohtml_tags).
4909151497Sru */
4910151497Sru
4911151497Sruvoid html_printer::devtag (char *s, const environment *env, char type)
4912151497Sru{
4913151497Sru  if (type != 'p')
4914151497Sru    return;
4915151497Sru
4916151497Sru  if (s != 0) {
4917151497Sru    flush_sbuf();
4918151497Sru    if (env->fontno >= 0) {
4919151497Sru      style sty(get_font_from_index(env->fontno), env->size, env->height,
4920151497Sru		env->slant, env->fontno, *env->col);
4921151497Sru      sbuf_style = sty;
4922151497Sru    }
4923151497Sru
4924151497Sru    if (strncmp(s, "devtag:", strlen("devtag:")) == 0) {
4925151497Sru      int r=font::res;   /* resolution of the device */
4926151497Sru
4927114402Sru      page_contents->add_tag(&sbuf_style, string(s),
4928114402Sru			     line_number,
4929114402Sru			     env->vpos-env->size*r/72, env->hpos,
4930114402Sru			     env->vpos               , env->hpos);
4931114402Sru    }
4932114402Sru  }
4933114402Sru}
4934114402Sru
4935151497Sru
4936151497Sru/*
4937151497Sru *  taken from number.cpp in src/roff/troff, [hunits::hunits(units x)]
4938151497Sru */
4939151497Sru
4940151497Sruint html_printer::round_width(int x)
4941151497Sru{
4942151497Sru  int r = font::hor;
4943151497Sru  int n;
4944151497Sru
4945151497Sru  // don't depend on the rounding direction for division of negative integers
4946151497Sru  if (r == 1)
4947151497Sru    n = x;
4948151497Sru  else
4949151497Sru    n = (x < 0
4950151497Sru	 ? -((-x + r/2 - 1)/r)
4951151497Sru	 : (x + r/2 - 1)/r);
4952151497Sru  return n * r;
4953151497Sru}
4954151497Sru
4955114402Sruint main(int argc, char **argv)
4956114402Sru{
4957114402Sru  program_name = argv[0];
4958114402Sru  static char stderr_buf[BUFSIZ];
4959114402Sru  setbuf(stderr, stderr_buf);
4960114402Sru  int c;
4961114402Sru  static const struct option long_options[] = {
4962114402Sru    { "help", no_argument, 0, CHAR_MAX + 1 },
4963114402Sru    { "version", no_argument, 0, 'v' },
4964114402Sru    { NULL, 0, 0, 0 }
4965114402Sru  };
4966151497Sru  while ((c = getopt_long(argc, argv, "a:bdD:F:g:hi:I:j:lno:prs:S:v",
4967151497Sru			  long_options, NULL))
4968114402Sru	 != EOF)
4969114402Sru    switch(c) {
4970114402Sru    case 'a':
4971114402Sru      /* text antialiasing bits - handled by pre-html */
4972114402Sru      break;
4973114402Sru    case 'b':
4974114402Sru      // set background color to white
4975114402Sru      default_background = new color;
4976114402Sru      default_background->set_gray(color::MAX_COLOR_VAL);
4977114402Sru      break;
4978151497Sru    case 'd':
4979151497Sru      /* handled by pre-html */
4980151497Sru      break;
4981151497Sru    case 'D':
4982151497Sru      /* handled by pre-html */
4983151497Sru      break;
4984114402Sru    case 'F':
4985114402Sru      font::command_line_font_dir(optarg);
4986114402Sru      break;
4987151497Sru    case 'g':
4988151497Sru      /* graphic antialiasing bits - handled by pre-html */
4989114402Sru      break;
4990114402Sru    case 'h':
4991114402Sru      /* do not use the Hn headings of html, but manufacture our own */
4992114402Sru      manufacture_headings = TRUE;
4993114402Sru      break;
4994114402Sru    case 'i':
4995114402Sru      /* handled by pre-html */
4996114402Sru      break;
4997114402Sru    case 'I':
4998114402Sru      /* handled by pre-html */
4999114402Sru      break;
5000151497Sru    case 'j':
5001151497Sru      multiple_files = TRUE;
5002151497Sru      job_name = optarg;
5003114402Sru      break;
5004151497Sru    case 'l':
5005151497Sru      auto_links = FALSE;
5006151497Sru      break;
5007114402Sru    case 'n':
5008114402Sru      simple_anchors = TRUE;
5009114402Sru      break;
5010151497Sru    case 'o':
5011151497Sru      /* handled by pre-html */
5012151497Sru      break;
5013151497Sru    case 'p':
5014151497Sru      /* handled by pre-html */
5015151497Sru      break;
5016151497Sru    case 'r':
5017151497Sru      auto_rule = FALSE;
5018151497Sru      break;
5019151497Sru    case 's':
5020151497Sru      base_point_size = atoi(optarg);
5021151497Sru      break;
5022151497Sru    case 'S':
5023151497Sru      split_level = atoi(optarg) + 1;
5024151497Sru      break;
5025151497Sru    case 'v':
5026151497Sru      printf("GNU post-grohtml (groff) version %s\n", Version_string);
5027151497Sru      exit(0);
5028151497Sru      break;
5029114402Sru    case CHAR_MAX + 1: // --help
5030114402Sru      usage(stdout);
5031114402Sru      exit(0);
5032114402Sru      break;
5033114402Sru    case '?':
5034114402Sru      usage(stderr);
5035114402Sru      exit(1);
5036114402Sru      break;
5037114402Sru    default:
5038114402Sru      assert(0);
5039114402Sru    }
5040114402Sru  if (optind >= argc) {
5041114402Sru    do_file("-");
5042114402Sru  } else {
5043114402Sru    for (int i = optind; i < argc; i++)
5044114402Sru      do_file(argv[i]);
5045114402Sru  }
5046114402Sru  return 0;
5047114402Sru}
5048114402Sru
5049114402Srustatic void usage(FILE *stream)
5050114402Sru{
5051114402Sru  fprintf(stream, "usage: %s [-vblnh] [-D dir] [-I image_stem] [-F dir] [files ...]\n",
5052114402Sru	  program_name);
5053114402Sru}
5054