PageRenderTime 148ms CodeModel.GetById 19ms app.highlight 112ms RepoModel.GetById 1ms app.codeStats 1ms

/contrib/groff/src/devices/grohtml/post-html.cpp

https://bitbucket.org/freebsd/freebsd-head/
C++ | 5053 lines | 3641 code | 623 blank | 789 comment | 1039 complexity | d2253a29ccdb5e77327c9607fe5b9b93 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1// -*- C++ -*-
   2/* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005
   3 * Free Software Foundation, Inc.
   4 *
   5 *  Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cpp
   6 *  but it owes a huge amount of ideas and raw code from
   7 *  James Clark (jjc@jclark.com) grops/ps.cpp.
   8 */
   9
  10/*
  11This file is part of groff.
  12
  13groff is free software; you can redistribute it and/or modify it under
  14the terms of the GNU General Public License as published by the Free
  15Software Foundation; either version 2, or (at your option) any later
  16version.
  17
  18groff is distributed in the hope that it will be useful, but WITHOUT ANY
  19WARRANTY; without even the implied warranty of MERCHANTABILITY or
  20FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  21for more details.
  22
  23You should have received a copy of the GNU General Public License along
  24with groff; see the file COPYING.  If not, write to the Free Software
  25Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
  26
  27#include "driver.h"
  28#include "stringclass.h"
  29#include "cset.h"
  30#include "html.h"
  31#include "html-text.h"
  32#include "html-table.h"
  33
  34#include <time.h>
  35
  36#ifdef HAVE_UNISTD_H
  37#include <unistd.h>
  38#endif
  39
  40#include <stdio.h>
  41#include <fcntl.h>
  42#include <string.h>
  43
  44extern "C" const char *Version_string;
  45
  46#if !defined(TRUE)
  47#   define TRUE  (1==1)
  48#endif
  49#if !defined(FALSE)
  50#   define FALSE (1==0)
  51#endif
  52
  53#define MAX_LINE_LENGTH                60            /* maximum characters we want in a line      */
  54#define SIZE_INCREMENT                  2            /* font size increment <big> = +2            */
  55#define CENTER_TOLERANCE                2            /* how many pixels off center do we allow    */
  56#define ANCHOR_TEMPLATE         "heading"            /* if simple anchor is set we use this       */
  57#define UNICODE_DESC_START           0x80            /* all character entities above this are     */
  58                                                     /* either encoded by their glyph names or if */
  59                                                     /* there is no name then we use &#nnn;       */
  60typedef enum {CENTERED, LEFT, RIGHT, INLINE} TAG_ALIGNMENT;
  61typedef enum {col_tag, tab_tag, tab0_tag, none} colType;
  62
  63#undef DEBUG_TABLES
  64// #define DEBUG_TABLES
  65
  66/*
  67 *  prototypes
  68 */
  69
  70char *get_html_translation (font *f, const string &name);
  71int char_translate_to_html (font *f, char *buf, int buflen, unsigned char ch, int b, int and_single);
  72
  73
  74static int auto_links = TRUE;                        /* by default we enable automatic links at  */
  75                                                     /* top of the document.                     */
  76static int auto_rule  = TRUE;                        /* by default we enable an automatic rule   */
  77                                                     /* at the top and bottom of the document    */
  78static int simple_anchors = FALSE;                   /* default to anchors with heading text     */
  79static int manufacture_headings = FALSE;             /* default is to use the Hn html headings,  */
  80                                                     /* rather than manufacture our own.         */
  81static color *default_background = NULL;             /* has user requested initial bg color?     */
  82static string job_name;                              /* if set then the output is split into     */
  83                                                     /* multiple files with `job_name'-%d.html   */
  84static int multiple_files = FALSE;                   /* must we the output be divided into       */
  85                                                     /* multiple html files, one for each        */
  86                                                     /* heading?                                 */
  87static int base_point_size = 0;                      /* which troff font size maps onto html     */
  88                                                     /* size 3?                                  */
  89static int split_level = 2;                          /* what heading level to split at?          */
  90static string head_info;                             /* user supplied information to be placed   */
  91                                                     /* into <head> </head>                      */
  92
  93
  94/*
  95 *  start with a few favorites
  96 */
  97
  98void stop () {}
  99
 100static int min (int a, int b)
 101{
 102  if (a < b)
 103    return a;
 104  else
 105    return b;
 106}
 107
 108static int max (int a, int b)
 109{
 110  if (a > b)
 111    return a;
 112  else
 113    return b;
 114}
 115
 116/*
 117 *  is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
 118 */
 119
 120static int is_intersection (int a1, int a2, int b1, int b2)
 121{
 122  // easier to prove NOT outside limits
 123  return ! ((a1 > b2) || (a2 < b1));
 124}
 125
 126/*
 127 *  is_digit - returns TRUE if character, ch, is a digit.
 128 */
 129
 130static int is_digit (char ch)
 131{
 132  return (ch >= '0') && (ch <= '9');
 133}
 134
 135/*
 136 *  the classes and methods for maintaining a list of files.
 137 */
 138
 139struct file {
 140  FILE    *fp;
 141  file    *next;
 142  int      new_output_file;
 143  int      require_links;
 144  string   output_file_name;
 145
 146  file     (FILE *f);
 147};
 148
 149/*
 150 *  file - initialize all fields to NULL
 151 */
 152
 153file::file (FILE *f)
 154  : fp(f), next(NULL), new_output_file(FALSE),
 155    require_links(FALSE), output_file_name("")
 156{
 157}
 158
 159class files {
 160public:
 161              files              ();
 162  FILE       *get_file           (void);
 163  void        start_of_list      (void);
 164  void        move_next          (void);
 165  void        add_new_file       (FILE *f);
 166  void        set_file_name      (string name);
 167  void        set_links_required (void);
 168  int         are_links_required (void);
 169  int         is_new_output_file (void);
 170  string      file_name          (void);
 171  string      next_file_name     (void);
 172private:
 173  file       *head;
 174  file       *tail;
 175  file       *ptr;
 176};
 177
 178/*
 179 *  files - create an empty list of files.
 180 */
 181
 182files::files ()
 183  : head(NULL), tail(NULL), ptr(NULL)
 184{
 185}
 186
 187/*
 188 *  get_file - returns the FILE associated with ptr.
 189 */
 190
 191FILE *files::get_file (void)
 192{
 193  if (ptr)
 194    return ptr->fp;
 195  else
 196    return NULL;
 197}
 198
 199/*
 200 *  start_of_list - reset the ptr to the start of the list.
 201 */
 202
 203void files::start_of_list (void)
 204{
 205  ptr = head;
 206}
 207
 208/*
 209 *  move_next - moves the ptr to the next element on the list.
 210 */
 211
 212void files::move_next (void)
 213{
 214  if (ptr != NULL)
 215    ptr = ptr->next;
 216}
 217
 218/*
 219 *  add_new_file - adds a new file, f, to the list.
 220 */
 221
 222void files::add_new_file (FILE *f)
 223{
 224  if (head == NULL) {
 225    head = new file(f);
 226    tail = head;
 227  } else {
 228    tail->next = new file(f);
 229    tail       = tail->next;
 230  }
 231  ptr = tail;
 232}
 233
 234/*
 235 *  set_file_name - sets the final file name to contain the html
 236 *                  data to name.
 237 */
 238
 239void files::set_file_name (string name)
 240{
 241  if (ptr != NULL) {
 242    ptr->output_file_name = name;
 243    ptr->new_output_file = TRUE;
 244  }
 245}
 246
 247/*
 248 *  set_links_required - issue links when processing this component
 249 *                       of the file.
 250 */
 251
 252void files::set_links_required (void)
 253{
 254  if (ptr != NULL)
 255    ptr->require_links = TRUE;
 256}
 257
 258/*
 259 *  are_links_required - returns TRUE if this section of the file
 260 *                       requires that links should be issued.
 261 */
 262
 263int files::are_links_required (void)
 264{
 265  if (ptr != NULL)
 266    return ptr->require_links;
 267  return FALSE;
 268}
 269
 270/*
 271 *  is_new_output_file - returns TRUE if this component of the file
 272 *                       is the start of a new output file.
 273 */
 274
 275int files::is_new_output_file (void)
 276{
 277  if (ptr != NULL)
 278    return ptr->new_output_file;
 279  return FALSE;
 280}
 281
 282/*
 283 *  file_name - returns the name of the file.
 284 */
 285
 286string files::file_name (void)
 287{
 288  if (ptr != NULL)
 289    return ptr->output_file_name;
 290  return string("");
 291}
 292
 293/*
 294 *  next_file_name - returns the name of the next file.
 295 */
 296
 297string files::next_file_name (void)
 298{
 299  if (ptr != NULL && ptr->next != NULL)
 300    return ptr->next->output_file_name;
 301  return string("");
 302}
 303
 304/*
 305 *  the class and methods for styles
 306 */
 307
 308struct style {
 309  font        *f;
 310  int          point_size;
 311  int          font_no;
 312  int          height;
 313  int          slant;
 314  color        col;
 315               style       ();
 316               style       (font *, int, int, int, int, color);
 317  int          operator == (const style &) const;
 318  int          operator != (const style &) const;
 319};
 320
 321style::style()
 322  : f(NULL)
 323{
 324}
 325
 326style::style(font *p, int sz, int h, int sl, int no, color c)
 327  : f(p), point_size(sz), font_no(no), height(h), slant(sl), col(c)
 328{
 329}
 330
 331int style::operator==(const style &s) const
 332{
 333  return (f == s.f && point_size == s.point_size
 334	  && height == s.height && slant == s.slant && col == s.col);
 335}
 336
 337int style::operator!=(const style &s) const
 338{
 339  return !(*this == s);
 340}
 341
 342/*
 343 *  the class and methods for retaining ascii text
 344 */
 345
 346struct char_block {
 347  enum { SIZE = 256 };
 348  char         *buffer;
 349  int           used;
 350  char_block   *next;
 351
 352  char_block();
 353  char_block(int length);
 354  ~char_block();
 355};
 356
 357char_block::char_block()
 358: buffer(NULL), used(0), next(NULL)
 359{
 360}
 361
 362char_block::char_block(int length)
 363: used(0), next(NULL)
 364{
 365  buffer = new char[max(length, char_block::SIZE)];
 366  if (buffer == NULL)
 367    fatal("out of memory error");
 368}
 369
 370char_block::~char_block()
 371{
 372  if (buffer != NULL)
 373    a_delete buffer;
 374}
 375
 376class char_buffer {
 377public:
 378  char_buffer();
 379  ~char_buffer();
 380  char  *add_string(const char *, unsigned int);
 381  char  *add_string(const string &);
 382private:
 383  char_block *head;
 384  char_block *tail;
 385};
 386
 387char_buffer::char_buffer()
 388: head(NULL), tail(NULL)
 389{
 390}
 391
 392char_buffer::~char_buffer()
 393{
 394  while (head != NULL) {
 395    char_block *temp = head;
 396    head = head->next;
 397    delete temp;
 398  }
 399}
 400
 401char *char_buffer::add_string (const char *s, unsigned int length)
 402{
 403  int i=0;
 404  unsigned int old_used;
 405
 406  if (s == NULL || length == 0)
 407    return NULL;
 408
 409  if (tail == NULL) {
 410    tail = new char_block(length+1);
 411    head = tail;
 412  } else {
 413    if (tail->used + length+1 > char_block::SIZE) {
 414      tail->next  = new char_block(length+1);
 415      tail        = tail->next;
 416    }
 417  }
 418
 419  old_used = tail->used;
 420  do {
 421    tail->buffer[tail->used] = s[i];
 422    tail->used++;
 423    i++;
 424    length--;
 425  } while (length>0);
 426
 427  // add terminating nul character
 428
 429  tail->buffer[tail->used] = '\0';
 430  tail->used++;
 431
 432  // and return start of new string
 433
 434  return &tail->buffer[old_used];
 435}
 436
 437char *char_buffer::add_string (const string &s)
 438{
 439  return add_string(s.contents(), s.length());
 440}
 441
 442/*
 443 *  the classes and methods for maintaining glyph positions.
 444 */
 445
 446class text_glob {
 447public:
 448  void text_glob_html      (style *s, char *str, int length,
 449			    int min_vertical, int min_horizontal,
 450			    int max_vertical, int max_horizontal);
 451  void text_glob_special   (style *s, char *str, int length,
 452			    int min_vertical, int min_horizontal,
 453			    int max_vertical, int max_horizontal);
 454  void text_glob_line      (style *s,
 455			    int min_vertical, int min_horizontal,
 456			    int max_vertical, int max_horizontal,
 457			    int thickness);
 458  void text_glob_auto_image(style *s, char *str, int length,
 459			    int min_vertical, int min_horizontal,
 460			    int max_vertical, int max_horizontal);
 461  void text_glob_tag       (style *s, char *str, int length,
 462			    int min_vertical, int min_horizontal,
 463			    int max_vertical, int max_horizontal);
 464		       
 465  text_glob                (void);
 466  ~text_glob               (void);
 467  int  is_a_line           (void);
 468  int  is_a_tag            (void);
 469  int  is_eol              (void);
 470  int  is_auto_img         (void);
 471  int  is_br               (void);
 472  int  is_in               (void);
 473  int  is_po               (void);
 474  int  is_ti               (void);
 475  int  is_ll               (void);
 476  int  is_ce               (void);
 477  int  is_tl               (void);
 478  int  is_eo_tl            (void);
 479  int  is_eol_ce           (void);
 480  int  is_col              (void);
 481  int  is_tab              (void);
 482  int  is_tab0             (void);
 483  int  is_ta               (void);
 484  int  is_tab_ts           (void);
 485  int  is_tab_te           (void);
 486  int  is_nf               (void);
 487  int  is_fi               (void);
 488  int  is_eo_h             (void);
 489  int  get_arg             (void);
 490  int  get_tab_args        (char *align);
 491
 492  void        remember_table (html_table *t);
 493  html_table *get_table      (void);
 494
 495  style           text_style;
 496  const char     *text_string;
 497  unsigned int    text_length;
 498  int             minv, minh, maxv, maxh;
 499  int             is_tag;               // is this a .br, .sp, .tl etc
 500  int             is_img_auto;          // image created by eqn delim
 501  int             is_special;           // text has come via 'x X html:'
 502  int             is_line;              // is the command a <line>?
 503  int             thickness;            // the thickness of a line
 504  html_table     *tab;                  // table description
 505
 506private:
 507  text_glob           (style *s, const char *str, int length,
 508		       int min_vertical , int min_horizontal,
 509		       int max_vertical , int max_horizontal,
 510		       bool is_troff_command,
 511		       bool is_auto_image, bool is_special_command,
 512		       bool is_a_line    , int  thickness);
 513};
 514
 515text_glob::text_glob (style *s, const char *str, int length,
 516		      int min_vertical, int min_horizontal,
 517		      int max_vertical, int max_horizontal,
 518		      bool is_troff_command,
 519		      bool is_auto_image, bool is_special_command,
 520		      bool is_a_line_flag, int line_thickness)
 521  : text_style(*s), text_string(str), text_length(length),
 522    minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal),
 523    is_tag(is_troff_command), is_img_auto(is_auto_image), is_special(is_special_command),
 524    is_line(is_a_line_flag), thickness(line_thickness), tab(NULL)
 525{
 526}
 527
 528text_glob::text_glob ()
 529  : text_string(NULL), text_length(0), minv(-1), minh(-1), maxv(-1), maxh(-1),
 530    is_tag(FALSE), is_special(FALSE), is_line(FALSE), thickness(0), tab(NULL)
 531{
 532}
 533
 534text_glob::~text_glob ()
 535{
 536  if (tab != NULL)
 537    delete tab;
 538}
 539
 540/*
 541 *  text_glob_html - used to place html text into the glob buffer.
 542 */
 543
 544void text_glob::text_glob_html (style *s, char *str, int length,
 545				int min_vertical , int min_horizontal,
 546				int max_vertical , int max_horizontal)
 547{
 548  text_glob *g = new text_glob(s, str, length,
 549			       min_vertical, min_horizontal, max_vertical, max_horizontal,
 550			       FALSE, FALSE, FALSE, FALSE, 0);
 551  *this = *g;
 552  delete g;
 553}
 554
 555/*
 556 *  text_glob_html - used to place html specials into the glob buffer.
 557 *                   This text is essentially html commands coming through
 558 *                   from the macro sets, with special designated sequences of
 559 *                   characters translated into html. See add_and_encode.
 560 */
 561
 562void text_glob::text_glob_special (style *s, char *str, int length,
 563				   int min_vertical , int min_horizontal,
 564				   int max_vertical , int max_horizontal)
 565{
 566  text_glob *g = new text_glob(s, str, length,
 567			       min_vertical, min_horizontal, max_vertical, max_horizontal,
 568			       FALSE, FALSE, TRUE, FALSE, 0);
 569  *this = *g;
 570  delete g;
 571}
 572
 573/*
 574 *  text_glob_line - record horizontal draw line commands.
 575 */
 576
 577void text_glob::text_glob_line (style *s,
 578				int min_vertical , int min_horizontal,
 579				int max_vertical , int max_horizontal,
 580				int thickness_value)
 581{
 582  text_glob *g = new text_glob(s, "", 0,
 583			       min_vertical, min_horizontal, max_vertical, max_horizontal,
 584			       FALSE, FALSE, FALSE, TRUE, thickness_value);
 585  *this = *g;
 586  delete g;
 587}
 588
 589/*
 590 *  text_glob_auto_image - record the presence of a .auto-image tag command.
 591 *                         Used to mark that an image has been created automatically
 592 *                         by a preprocessor and (pre-grohtml/troff) combination.
 593 *                         Under some circumstances images may not be created.
 594 *                         (consider .EQ
 595 *                                   delim $$
 596 *                                   .EN
 597 *                                   .TS
 598 *                                   tab(!), center;
 599 *                                   l!l.
 600 *                                   $1 over x$!recripical of x
 601 *                                   .TE
 602 *
 603 *                          the first auto-image marker is created via .EQ/.EN pair
 604 *                          and no image is created.
 605 *                          The second auto-image marker occurs at $1 over x$
 606 *                          Currently this image will not be created
 607 *                          as the whole of the table is created as an image.
 608 *                          (Once html tables are handled by grohtml this will change.
 609 *                           Shortly this will be the case).
 610 */
 611
 612void text_glob::text_glob_auto_image(style *s, char *str, int length,
 613				     int min_vertical, int min_horizontal,
 614				     int max_vertical, int max_horizontal)
 615{
 616  text_glob *g = new text_glob(s, str, length,
 617			       min_vertical, min_horizontal, max_vertical, max_horizontal,
 618			       TRUE, TRUE, FALSE, FALSE, 0);
 619  *this = *g;
 620  delete g;
 621}
 622
 623/*
 624 *  text_glob_tag - records a troff tag.
 625 */
 626
 627void text_glob::text_glob_tag (style *s, char *str, int length,
 628			       int min_vertical, int min_horizontal,
 629			       int max_vertical, int max_horizontal)
 630{
 631  text_glob *g = new text_glob(s, str, length,
 632			       min_vertical, min_horizontal, max_vertical, max_horizontal,
 633			       TRUE, FALSE, FALSE, FALSE, 0);
 634  *this = *g;
 635  delete g;
 636}
 637
 638/*
 639 *  is_a_line - returns TRUE if glob should be converted into an <hr>
 640 */
 641
 642int text_glob::is_a_line (void)
 643{
 644  return is_line;
 645}
 646
 647/*
 648 *  is_a_tag - returns TRUE if glob contains a troff directive.
 649 */
 650
 651int text_glob::is_a_tag (void)
 652{
 653  return is_tag;
 654}
 655
 656/*
 657 *  is_eol - returns TRUE if glob contains the tag eol
 658 */
 659
 660int text_glob::is_eol (void)
 661{
 662  return is_tag && (strcmp(text_string, "devtag:.eol") == 0);
 663}
 664
 665/*
 666 *  is_eol_ce - returns TRUE if glob contains the tag eol.ce
 667 */
 668
 669int text_glob::is_eol_ce (void)
 670{
 671  return is_tag && (strcmp(text_string, "devtag:eol.ce") == 0);
 672}
 673
 674/*
 675 *  is_tl - returns TRUE if glob contains the tag .tl
 676 */
 677
 678int text_glob::is_tl (void)
 679{
 680  return is_tag && (strcmp(text_string, "devtag:.tl") == 0);
 681}
 682
 683/*
 684 *  is_eo_tl - returns TRUE if glob contains the tag eo.tl
 685 */
 686
 687int text_glob::is_eo_tl (void)
 688{
 689  return is_tag && (strcmp(text_string, "devtag:.eo.tl") == 0);
 690}
 691
 692/*
 693 *  is_nf - returns TRUE if glob contains the tag .fi 0
 694 */
 695
 696int text_glob::is_nf (void)
 697{
 698  return is_tag && (strncmp(text_string, "devtag:.fi",
 699			    strlen("devtag:.fi")) == 0) &&
 700         (get_arg() == 0);
 701}
 702
 703/*
 704 *  is_fi - returns TRUE if glob contains the tag .fi 1
 705 */
 706
 707int text_glob::is_fi (void)
 708{
 709  return( is_tag && (strncmp(text_string, "devtag:.fi",
 710			     strlen("devtag:.fi")) == 0) &&
 711	  (get_arg() == 1) );
 712}
 713
 714/*
 715 *  is_eo_h - returns TRUE if glob contains the tag .eo.h
 716 */
 717
 718int text_glob::is_eo_h (void)
 719{
 720  return is_tag && (strcmp(text_string, "devtag:.eo.h") == 0);
 721}
 722
 723/*
 724 *  is_ce - returns TRUE if glob contains the tag .ce
 725 */
 726
 727int text_glob::is_ce (void)
 728{
 729  return is_tag && (strncmp(text_string, "devtag:.ce",
 730			    strlen("devtag:.ce")) == 0);
 731}
 732
 733/*
 734 *  is_in - returns TRUE if glob contains the tag .in
 735 */
 736
 737int text_glob::is_in (void)
 738{
 739  return is_tag && (strncmp(text_string, "devtag:.in ",
 740			    strlen("devtag:.in ")) == 0);
 741}
 742
 743/*
 744 *  is_po - returns TRUE if glob contains the tag .po
 745 */
 746
 747int text_glob::is_po (void)
 748{
 749  return is_tag && (strncmp(text_string, "devtag:.po ",
 750			    strlen("devtag:.po ")) == 0);
 751}
 752
 753/*
 754 *  is_ti - returns TRUE if glob contains the tag .ti
 755 */
 756
 757int text_glob::is_ti (void)
 758{
 759  return is_tag && (strncmp(text_string, "devtag:.ti ",
 760			    strlen("devtag:.ti ")) == 0);
 761}
 762
 763/*
 764 *  is_ll - returns TRUE if glob contains the tag .ll
 765 */
 766
 767int text_glob::is_ll (void)
 768{
 769  return is_tag && (strncmp(text_string, "devtag:.ll ",
 770			    strlen("devtag:.ll ")) == 0);
 771}
 772
 773/*
 774 *  is_col - returns TRUE if glob contains the tag .col
 775 */
 776
 777int text_glob::is_col (void)
 778{
 779  return is_tag && (strncmp(text_string, "devtag:.col",
 780			    strlen("devtag:.col")) == 0);
 781}
 782
 783/*
 784 *  is_tab_ts - returns TRUE if glob contains the tag .tab_ts
 785 */
 786
 787int text_glob::is_tab_ts (void)
 788{
 789  return is_tag && (strcmp(text_string, "devtag:.tab-ts") == 0);
 790}
 791
 792/*
 793 *  is_tab_te - returns TRUE if glob contains the tag .tab_te
 794 */
 795
 796int text_glob::is_tab_te (void)
 797{
 798  return is_tag && (strcmp(text_string, "devtag:.tab-te") == 0);
 799}
 800
 801/*
 802 *  is_ta - returns TRUE if glob contains the tag .ta
 803 */
 804
 805int text_glob::is_ta (void)
 806{
 807  return is_tag && (strncmp(text_string, "devtag:.ta ",
 808			    strlen("devtag:.ta ")) == 0);
 809}
 810
 811/*
 812 *  is_tab - returns TRUE if glob contains the tag tab
 813 */
 814
 815int text_glob::is_tab (void)
 816{
 817  return is_tag && (strncmp(text_string, "devtag:tab ",
 818			    strlen("devtag:tab ")) == 0);
 819}
 820
 821/*
 822 *  is_tab0 - returns TRUE if glob contains the tag tab0
 823 */
 824
 825int text_glob::is_tab0 (void)
 826{
 827  return is_tag && (strncmp(text_string, "devtag:tab0",
 828			    strlen("devtag:tab0")) == 0);
 829}
 830
 831/*
 832 *  is_auto_img - returns TRUE if the glob contains an automatically
 833 *                generated image.
 834 */
 835
 836int text_glob::is_auto_img (void)
 837{
 838  return is_img_auto;
 839}
 840
 841/*
 842 *  is_br - returns TRUE if the glob is a tag containing a .br
 843 *          or an implied .br. Note that we do not include .nf or .fi
 844 *          as grohtml will place a .br after these commands if they
 845 *          should break the line.
 846 */
 847
 848int text_glob::is_br (void)
 849{
 850  return is_a_tag() && ((strcmp ("devtag:.br", text_string) == 0) ||
 851			(strncmp("devtag:.sp", text_string,
 852				 strlen("devtag:.sp")) == 0));
 853}
 854
 855int text_glob::get_arg (void)
 856{
 857  if (strncmp("devtag:", text_string, strlen("devtag:")) == 0) {
 858    const char *p = text_string;
 859
 860    while ((*p != (char)0) && (!isspace(*p)))
 861      p++;
 862    while ((*p != (char)0) && (isspace(*p)))
 863      p++;
 864    if (*p == (char)0)
 865      return -1;
 866    return atoi(p);
 867  }
 868  return -1;
 869}
 870
 871/*
 872 *  get_tab_args - returns the tab position and alignment of the tab tag
 873 */
 874
 875int text_glob::get_tab_args (char *align)
 876{
 877  if (strncmp("devtag:", text_string, strlen("devtag:")) == 0) {
 878    const char *p = text_string;
 879
 880    // firstly the alignment C|R|L
 881    while ((*p != (char)0) && (!isspace(*p)))
 882      p++;
 883    while ((*p != (char)0) && (isspace(*p)))
 884      p++;
 885    *align = *p;
 886    // now the int value
 887    while ((*p != (char)0) && (!isspace(*p)))
 888      p++;
 889    while ((*p != (char)0) && (isspace(*p)))
 890      p++;
 891    if (*p == (char)0)
 892      return -1;
 893    return atoi(p);
 894  }
 895  return -1;
 896}
 897
 898/*
 899 *  remember_table - saves table, t, in the text_glob.
 900 */
 901
 902void text_glob::remember_table (html_table *t)
 903{
 904  if (tab != NULL)
 905    delete tab;
 906  tab = t;
 907}
 908
 909/*
 910 *  get_table - returns the stored table description.
 911 */
 912
 913html_table *text_glob::get_table (void)
 914{
 915  return tab;
 916}
 917
 918/*
 919 *  the class and methods used to construct ordered double linked
 920 *  lists.  In a previous implementation we used templates via
 921 *  #include "ordered-list.h", but this does assume that all C++
 922 *  compilers can handle this feature. Pragmatically it is safer to
 923 *  assume this is not the case.
 924 */
 925
 926struct element_list {
 927  element_list *right;
 928  element_list *left;
 929  text_glob    *datum;
 930  int           lineno;
 931  int           minv, minh, maxv, maxh;
 932
 933  element_list  (text_glob *d,
 934		 int line_number,
 935		 int min_vertical, int min_horizontal,
 936		 int max_vertical, int max_horizontal);
 937  element_list  ();
 938  ~element_list ();
 939};
 940
 941element_list::element_list ()
 942  : right(0), left(0), datum(0), lineno(0), minv(-1), minh(-1), maxv(-1), maxh(-1)
 943{
 944}
 945
 946/*
 947 *  element_list - create a list element assigning the datum and region parameters.
 948 */
 949
 950element_list::element_list (text_glob *in,
 951			    int line_number,
 952			    int min_vertical, int min_horizontal,
 953			    int max_vertical, int max_horizontal)
 954  : right(0), left(0), datum(in), lineno(line_number),
 955    minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal)
 956{
 957}
 958
 959element_list::~element_list ()
 960{
 961  if (datum != NULL)
 962    delete datum;
 963}
 964
 965class list {
 966public:
 967       list             ();
 968      ~list             ();
 969  int  is_less          (element_list *a, element_list *b);
 970  void add              (text_glob *in,
 971		         int line_number,
 972		         int min_vertical, int min_horizontal,
 973		         int max_vertical, int max_horizontal);
 974  void                  sub_move_right      (void);
 975  void                  move_right          (void);
 976  void                  move_left           (void);
 977  int                   is_empty            (void);
 978  int                   is_equal_to_tail    (void);
 979  int                   is_equal_to_head    (void);
 980  void                  start_from_head     (void);
 981  void                  start_from_tail     (void);
 982  void                  insert              (text_glob *in);
 983  void                  move_to             (text_glob *in);
 984  text_glob            *move_right_get_data (void);
 985  text_glob            *move_left_get_data  (void);
 986  text_glob            *get_data            (void);
 987private:
 988  element_list *head;
 989  element_list *tail;
 990  element_list *ptr;
 991};
 992
 993/*
 994 *  list - construct an empty list.
 995 */
 996
 997list::list ()
 998  : head(NULL), tail(NULL), ptr(NULL)
 999{
1000}
1001
1002/*
1003 *  ~list - destroy a complete list.
1004 */
1005
1006list::~list()
1007{
1008  element_list *temp=head;
1009
1010  do {
1011    temp = head;
1012    if (temp != NULL) {
1013      head = head->right;
1014      delete temp;
1015    }
1016  } while ((head != NULL) && (head != tail));
1017}
1018
1019/*
1020 *  is_less - returns TRUE if a is left of b if on the same line or
1021 *            if a is higher up the page than b.
1022 */
1023
1024int list::is_less (element_list *a, element_list *b)
1025{
1026  // was if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
1027  if (a->lineno < b->lineno) {
1028    return( TRUE );
1029  } else if (a->lineno > b->lineno) {
1030    return( FALSE );
1031  } else if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) {
1032    return( a->minh < b->minh );
1033  } else {
1034    return( a->maxv < b->maxv );
1035  }
1036}
1037
1038/*
1039 *  add - adds a datum to the list in the order specified by the
1040 *        region position.
1041 */
1042
1043void list::add (text_glob *in, int line_number, int min_vertical, int min_horizontal, int max_vertical, int max_horizontal)
1044{
1045  // create a new list element with datum and position fields initialized
1046  element_list *t    = new element_list(in, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1047  element_list *last;
1048
1049#if 0
1050  fprintf(stderr, "[%s %d,%d,%d,%d] ",
1051	  in->text_string, min_vertical, min_horizontal, max_vertical, max_horizontal);
1052  fflush(stderr);
1053#endif
1054
1055  if (head == NULL) {
1056    head     = t;
1057    tail     = t;
1058    ptr      = t;
1059    t->left  = t;
1060    t->right = t;
1061  } else {
1062    last = tail;
1063
1064    while ((last != head) && (is_less(t, last)))
1065      last = last->left;
1066
1067    if (is_less(t, last)) {
1068      t->right          = last;
1069      last->left->right = t;
1070      t->left           = last->left;
1071      last->left        = t;
1072      // now check for a new head
1073      if (last == head)
1074	head = t;
1075    } else {
1076      // add t beyond last
1077      t->right          = last->right;
1078      t->left           = last;
1079      last->right->left = t;
1080      last->right       = t;
1081      // now check for a new tail
1082      if (last == tail)
1083	tail = t;
1084    }
1085  }
1086}
1087
1088/*
1089 *  sub_move_right - removes the element which is currently pointed to by ptr
1090 *                   from the list and moves ptr to the right.
1091 */
1092
1093void list::sub_move_right (void)
1094{
1095  element_list *t=ptr->right;
1096
1097  if (head == tail) {
1098    head = NULL;
1099    if (tail != NULL)
1100      delete tail;
1101    
1102    tail = NULL;
1103    ptr  = NULL;
1104  } else {
1105    if (head == ptr)
1106      head = head->right;
1107    if (tail == ptr)
1108      tail = tail->left;
1109    ptr->left->right = ptr->right;
1110    ptr->right->left = ptr->left;
1111    ptr = t;
1112  }
1113}
1114
1115/*
1116 *  start_from_head - assigns ptr to the head.
1117 */
1118
1119void list::start_from_head (void)
1120{
1121  ptr = head;
1122}
1123
1124/*
1125 *  start_from_tail - assigns ptr to the tail.
1126 */
1127
1128void list::start_from_tail (void)
1129{
1130  ptr = tail;
1131}
1132
1133/*
1134 *  is_empty - returns TRUE if the list has no elements.
1135 */
1136
1137int list::is_empty (void)
1138{
1139  return head == NULL;
1140}
1141
1142/*
1143 *  is_equal_to_tail - returns TRUE if the ptr equals the tail.
1144 */
1145
1146int list::is_equal_to_tail (void)
1147{
1148  return ptr == tail;
1149}
1150
1151/*
1152 *  is_equal_to_head - returns TRUE if the ptr equals the head.
1153 */
1154
1155int list::is_equal_to_head (void)
1156{
1157  return ptr == head;
1158}
1159
1160/*
1161 *  move_left - moves the ptr left.
1162 */
1163
1164void list::move_left (void)
1165{
1166  ptr = ptr->left;
1167}
1168
1169/*
1170 *  move_right - moves the ptr right.
1171 */
1172
1173void list::move_right (void)
1174{
1175  ptr = ptr->right;
1176}
1177
1178/*
1179 *  get_datum - returns the datum referenced via ptr.
1180 */
1181
1182text_glob* list::get_data (void)
1183{
1184  return ptr->datum;
1185}
1186
1187/*
1188 *  move_right_get_data - returns the datum referenced via ptr and moves
1189 *                        ptr right.
1190 */
1191
1192text_glob* list::move_right_get_data (void)
1193{
1194  ptr = ptr->right;
1195  if (ptr == head)
1196    return NULL;
1197  else
1198    return ptr->datum;
1199}
1200
1201/*
1202 *  move_left_get_data - returns the datum referenced via ptr and moves
1203 *                       ptr right.
1204 */
1205
1206text_glob* list::move_left_get_data (void)
1207{
1208  ptr = ptr->left;
1209  if (ptr == tail)
1210    return NULL;
1211  else
1212    return ptr->datum;
1213}
1214
1215/*
1216 *  insert - inserts data after the current position.
1217 */
1218
1219void list::insert (text_glob *in)
1220{
1221  if (is_empty())
1222    fatal("list must not be empty if we are inserting data");
1223  else {
1224    if (ptr == NULL)
1225      ptr = head;
1226    
1227    element_list *t = new element_list(in, ptr->lineno, ptr->minv, ptr->minh, ptr->maxv, ptr->maxh);
1228    if (ptr == tail)
1229      tail = t;
1230    ptr->right->left = t;
1231    t->right = ptr->right;
1232    ptr->right = t;
1233    t->left = ptr;
1234  }
1235}
1236
1237/*
1238 *  move_to - moves the current position to the point where data, in, exists.
1239 *            This is an expensive method and should be used sparingly.
1240 */
1241
1242void list::move_to (text_glob *in)
1243{
1244  ptr = head;
1245  while (ptr != tail && ptr->datum != in)
1246    ptr = ptr->right;
1247}
1248
1249/*
1250 *  page class and methods
1251 */
1252
1253class page {
1254public:
1255                              page            (void);
1256  void                        add             (style *s, const string &str,
1257					       int line_number,
1258					       int min_vertical, int min_horizontal,
1259					       int max_vertical, int max_horizontal);
1260  void                        add_tag         (style *s, const string &str,
1261					       int line_number,
1262					       int min_vertical, int min_horizontal,
1263					       int max_vertical, int max_horizontal);
1264  void                        add_and_encode  (style *s, const string &str,
1265					       int line_number,
1266					       int min_vertical, int min_horizontal,
1267					       int max_vertical, int max_horizontal,
1268					       int is_tag);
1269  void                        add_line        (style *s,
1270					       int line_number,
1271					       int x1, int y1, int x2, int y2,
1272					       int thickness);
1273  void                        insert_tag      (const string &str);
1274  void                        dump_page       (void);   // debugging method
1275
1276  // and the data
1277
1278  list                        glyphs;         // position of glyphs and specials on page
1279  char_buffer                 buffer;         // all characters for this page
1280};
1281
1282page::page()
1283{
1284}
1285
1286/*
1287 *  insert_tag - inserts a tag after the current position.
1288 */
1289
1290void page::insert_tag (const string &str)
1291{
1292  if (str.length() > 0) {
1293    text_glob *g=new text_glob();
1294    text_glob *f=glyphs.get_data();
1295    g->text_glob_tag(&f->text_style, buffer.add_string(str), str.length(),
1296		     f->minv, f->minh, f->maxv, f->maxh);
1297    glyphs.insert(g);
1298  }
1299}
1300
1301/*
1302 *  add - add html text to the list of glyphs.
1303 */
1304
1305void page::add (style *s, const string &str,
1306		int line_number,
1307		int min_vertical, int min_horizontal,
1308		int max_vertical, int max_horizontal)
1309{
1310  if (str.length() > 0) {
1311    text_glob *g=new text_glob();
1312    g->text_glob_html(s, buffer.add_string(str), str.length(),
1313		      min_vertical, min_horizontal, max_vertical, max_horizontal);
1314    glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1315  }
1316}
1317
1318/*
1319 *  add_tag - adds a troff tag, for example: .tl .sp .br
1320 */
1321
1322void page::add_tag (style *s, const string &str,
1323		    int line_number,
1324		    int min_vertical, int min_horizontal,
1325		    int max_vertical, int max_horizontal)
1326{
1327  if (str.length() > 0) {
1328    text_glob *g;
1329
1330    if (strncmp((str+'\0').contents(), "devtag:.auto-image",
1331		strlen("devtag:.auto-image")) == 0) {
1332      g = new text_glob();
1333      g->text_glob_auto_image(s, buffer.add_string(str), str.length(),
1334			      min_vertical, min_horizontal, max_vertical, max_horizontal);
1335    } else {
1336      g = new text_glob();
1337      g->text_glob_tag(s, buffer.add_string(str), str.length(),
1338		       min_vertical, min_horizontal, max_vertical, max_horizontal);
1339    }
1340    glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
1341  }
1342}
1343
1344/*
1345 *  add_line - adds the <line> primitive providing that y1==y2
1346 */
1347
1348void page::add_line (style *s,
1349		     int line_number,
1350		     int x_1, int y_1, int x_2, int y_2,
1351		     int thickness)
1352{
1353  if (y_1 == y_2) {
1354    text_glob *g = new text_glob();
1355    g->text_glob_line(s,
1356		      min(y_1, y_2), min(x_1, x_2),
1357		      max(y_1, y_2), max(x_1, x_2),
1358		      thickness);
1359    glyphs.add(g, line_number,
1360	       min(y_1, y_2), min(x_1, x_2),
1361	       max(y_1, y_2), max(x_1, x_2));
1362  }
1363}
1364
1365/*
1366 *  to_unicode - returns a unicode translation of int, ch.
1367 */
1368
1369static char *to_unicode (unsigned int ch)
1370{
1371  static char buf[30];
1372
1373  sprintf(buf, "&#%u;", ch);
1374  return buf;
1375}
1376
1377/*
1378 *  add_and_encode - adds a special string to the page, it translates the string
1379 *                   into html glyphs. The special string will have come from x X html:
1380 *                   and can contain troff character encodings which appear as
1381 *                   \(char\). A sequence of \\ represents \.
1382 *                   So for example we can write:
1383 *                      "cost = \(Po\)3.00 file = \\foo\\bar"
1384 *                   which is translated into:
1385 *                      "cost = &pound;3.00 file = \foo\bar"
1386 */
1387
1388void page::add_and_encode (style *s, const string &str,
1389			   int line_number,
1390			   int min_vertical, int min_horizontal,
1391			   int max_vertical, int max_horizontal,
1392			   int is_tag)
1393{
1394  string html_string;
1395  char *html_glyph;
1396  int i=0;
1397
1398  if (s->f == NULL)
1399    return;
1400  while (i < str.length()) {
1401    if ((i+1<str.length()) && (str.substring(i, 2) == string("\\("))) {
1402      // start of escape
1403      i += 2; // move over \(
1404      int a = i;
1405      while ((i+1<str.length()) && (str.substring(i, 2) != string("\\)"))) {
1406	i++;
1407      }
1408      int n = i;
1409      if ((i+1<str.length()) && (str.substring(i, 2) == string("\\)")))
1410	i++;
1411      else
1412	n = -1;
1413      if (n > 0) {
1414	string troff_charname = str.substring(a, n-a);
1415	html_glyph = get_html_translation(s->f, troff_charname);
1416	if (html_glyph)
1417	  html_string += html_glyph;
1418	else {
1419	  int idx=s->f->name_to_index((troff_charname + '\0').contents());
1420	  
1421	  if (s->f->contains(idx) && (idx != 0))
1422	    html_string += s->f->get_code(idx);
1423	}
1424      }
1425    } else
1426      html_string += str[i];
1427    i++;
1428  }
1429  if (html_string.length() > 0) {
1430    text_glob *g=new text_glob();
1431    if (is_tag)
1432      g->text_glob_tag(s, buffer.add_string(html_string),
1433		       html_string.length(),
1434		       min_vertical, min_horizontal,
1435		       max_vertical, max_horizontal);
1436    else
1437      g->text_glob_special(s, buffer.add_string(html_string),
1438			   html_string.length(),
1439			   min_vertical, min_horizontal,
1440			   max_vertical, max_horizontal);
1441    glyphs.add(g, line_number, min_vertical,
1442	       min_horizontal, max_vertical, max_horizontal);
1443  }
1444}
1445
1446/*
1447 *  dump_page - dump the page contents for debugging purposes.
1448 */
1449
1450void page::dump_page(void)
1451{
1452#if defined(DEBUG_TABLES)
1453  text_glob *old_pos = glyphs.get_data();
1454  text_glob *g;
1455
1456  printf("\n<!--\n");
1457  printf("\n\ndebugging start\n");
1458  glyphs.start_from_head();
1459  do {
1460    g = glyphs.get_data();
1461    if (g->is_tab_ts()) {
1462      printf("\n\n");
1463      if (g->get_table() != NULL)
1464	g->get_table()->dump_table();
1465    }
1466    printf("%s ", g->text_string);
1467    if (g->is_tab_te())
1468      printf("\n\n");
1469    glyphs.move_right();
1470  } while (! glyphs.is_equal_to_head());
1471  glyphs.move_to(old_pos);
1472  printf("\ndebugging end\n\n");
1473  printf("\n-->\n");
1474  fflush(stdout);
1475#endif
1476}
1477
1478/*
1479 *  font classes and methods
1480 */
1481
1482class html_font : public font {
1483  html_font(const char *);
1484public:
1485  int encoding_index;
1486  char *encoding;
1487  char *reencoded_name;
1488  ~html_font();
1489  static html_font *load_html_font(const char *);
1490};
1491
1492html_font *html_font::load_html_font(const char *s)
1493{
1494  html_font *f = new html_font(s);
1495  if (!f->load()) {
1496    delete f;
1497    return 0;
1498  }
1499  return f;
1500}
1501
1502html_font::html_font(const char *nm)
1503: font(nm)
1504{
1505}
1506
1507html_font::~html_font()
1508{
1509}
1510
1511/*
1512 *  a simple class to contain the header to this document
1513 */
1514
1515class title_desc {
1516public:
1517          title_desc ();
1518         ~title_desc ();
1519
1520  int     has_been_written;
1521  int     has_been_found;
1522  int     with_h1;
1523  string  text;
1524};
1525
1526
1527title_desc::title_desc ()
1528  : has_been_written(FALSE), has_been_found(FALSE), with_h1(FALSE)
1529{
1530}
1531
1532title_desc::~title_desc ()
1533{
1534}
1535
1536class header_desc {
1537public:
1538                            header_desc ();
1539                           ~header_desc ();
1540
1541  int                       no_of_level_one_headings; // how many .SH or .NH 1 have we found?
1542  int                       no_of_headings;           // how many headings have we found?
1543  char_buffer               headings;                 // all the headings used in the document
1544  list                      headers;                  // list of headers built from .NH and .SH
1545  list                      header_filename;          // in which file is this header?
1546  int                       header_level;             // current header level
1547  int                       written_header;           // have we written the header yet?
1548  string                    header_buffer;            // current header text
1549
1550  void                      write_headings (FILE *f, int force);
1551};
1552
1553header_desc::header_desc ()
1554  :   no_of_level_one_headings(0), no_of_headings(0),
1555      header_level(2), written_header(0)
1556{
1557}
1558
1559header_desc::~header_desc ()
1560{
1561}
1562
1563/*
1564 *  write_headings - emits a list of links for the headings in this document
1565 */
1566
1567void header_desc::write_headings (FILE *f, int force)
1568{
1569  text_glob *g;
1570
1571  if (auto_links || force) {
1572    if (! headers.is_empty()) {
1573      int h=1;
1574
1575      headers.start_from_head();
1576      header_filename.start_from_head();
1577      do {
1578	g = headers.get_data();
1579	fputs("<a href=\"", f);
1580	if (multiple_files && (! header_filename.is_empty())) {
1581	  text_glob *fn = header_filename.get_data();
1582	  fputs(fn->text_string, f);
1583	}
1584	fputs("#", f);
1585	if (simple_anchors) {
1586	  string buffer(ANCHOR_TEMPLATE);
1587
1588	  buffer += as_string(h);
1589	  buffer += '\0';
1590	  fputs(buffer.contents(), f);
1591	} else
1592	  fputs(g->text_string, f);
1593	h++;
1594	fputs("\">", f);
1595	fputs(g->text_string, f);
1596        fputs("</a><br>\n", f);
1597	headers.move_right();
1598	if (multiple_files && (! header_filename.is_empty()))
1599	  header_filename.move_right();
1600      } while (! headers.is_equal_to_head());
1601      fputs("\n", f);
1602    }
1603  }
1604}
1605
1606struct assert_pos {
1607  assert_pos *next;
1608  const char *val;
1609  const char *id;
1610};
1611
1612class assert_state {
1613public:
1614        assert_state ();
1615        ~assert_state ();
1616
1617  void  addx (const char *c, const char *i, const char *v,
1618	      const char *f, const char *l);
1619  void  addy (const char *c, const char *i, const char *v,
1620	      const char *f, const char *l);
1621  void  build(const char *c, const char *v,
1622	      const char *f, const char *l);
1623  void  check_br (int br);
1624  void  check_ce (int ce);
1625  void  check_fi (int fi);
1626  void  check_sp (int sp);
1627  void  reset    (void);
1628
1629private:
1630  int check_br_flag;
1631  int check_ce_flag;
1632  int check_fi_flag;
1633  int check_sp_flag;
1634  const char *val_br;
1635  const char *val_ce;
1636  const char *val_fi;
1637  const char *val_sp;
1638  const char *file_br;
1639  const char *file_ce;
1640  const char *file_fi;
1641  const char *file_sp;
1642  const char *line_br;
1643  const char *line_ce;
1644  const char *line_fi;
1645  const char *line_sp;
1646
1647  assert_pos *xhead;
1648  assert_pos *yhead;
1649
1650  void add (assert_pos **h,
1651	    const char *c, const char *i, const char *v,
1652	    const char *f, const char *l);
1653  void compare(assert_pos *t,
1654	       const char *v, const char *f, const char *l);
1655  void close (const char *c);
1656  void set (const char *c, const char *v,
1657	    const char *f, const char *l);
1658  void check_value (const char *s, int v, const char *name,
1659		    const char *f, const char *l, int *flag);
1660  int check_value_error (int c, int v, const char *s,
1661			 const char *name,
1662			 const char *f, const char *l, int flag);
1663};
1664
1665assert_state::assert_state ()
1666{
1667  reset();
1668  val_br   = NULL;
1669  val_ce   = NULL;
1670  val_fi   = NULL;
1671  val_sp   = NULL;
1672  file_br  = NULL;
1673  file_ce  = NULL;
1674  file_fi  = NULL;
1675  file_sp  = NULL;
1676  line_br  = NULL;
1677  line_ce  = NULL;
1678  line_fi  = NULL;
1679  line_sp  = NULL;
1680  xhead    = NULL;
1681  yhead    = NULL;
1682}
1683
1684assert_state::~assert_state ()
1685{
1686  assert_pos *t;
1687
1688  while (xhead != NULL) {
1689    t = xhead;
1690    xhead = xhead->next;
1691    a_delete (char *)t->val;
1692    a_delete (char *)t->id;
1693    delete t;
1694  }
1695  while (yhead != NULL) {
1696    t = yhead;
1697    yhead = yhead->next;
1698    a_delete (char *)t->val;
1699    a_delete (char *)t->id;
1700    delete t;
1701  }
1702}
1703
1704void assert_state::reset (void)
1705{
1706  check_br_flag = 0;
1707  check_ce_flag = 0;
1708  check_fi_flag = 0;
1709  check_sp_flag = 0;
1710}
1711
1712void assert_state::add (assert_pos **h,
1713			const char *c, const char *i, const char *v,
1714			const char *f, const char *l)
1715{
1716  assert_pos *t = *h;
1717
1718  while (t != NULL) {
1719    if (strcmp(t->id, i) == 0)
1720      break;
1721    t = t->next;
1722  }
1723  if (t != NULL && v != NULL && (v[0] != '='))
1724    compare(t, v, f, l);
1725  else {
1726    if (t == NULL) {
1727      t = new assert_pos;
1728      t->next = *h;
1729      (*h) = t;
1730    }
1731    if (v == NULL || v[0] != '=') {
1732      if (f == NULL)
1733	f = "stdin";
1734      if (l == NULL)
1735	l = "<none>";
1736      if (v == NULL)
1737	v = "no value at all";
1738      fprintf(stderr, "%s:%s:error in assert format of id=%s expecting value to be prefixed with an `=' got %s\n",
1739	      f, l, i, v);
1740    }
1741    t->id = i;
1742    t->val = v;
1743    a_delete (char *)c;
1744    a_delete (char *)f;
1745    a_delete (char *)l;
1746  }
1747}
1748
1749void assert_state::addx (const char *c, const char *i, const char *v,
1750			 const char *f, const char *l)
1751{
1752  add(&xhead, c, i, v, f, l);
1753}
1754
1755void assert_state::addy (const char *c, const char *i, const char *v,
1756			 const char *f, const char *l)
1757{
1758  add(&yhead, c, i, v, f, l);
1759}
1760
1761void assert_state::compare(assert_pos *t,
1762			   const char *v, const char *f, const char *l)
1763{
1764  const char *s=t->val;
1765
1766  while ((*v) == '=')
1767    v++;
1768  while ((*s) == '=')
1769    s++;
1770  
1771  if (strcmp(v, s) != 0) {
1772    if (f == NULL)
1773      f = "stdin";
1774    if (l == NULL)
1775      l = "<none>";
1776    fprintf(stderr, "%s:%s: grohtml assertion failed at id%s expecting %s and was given %s\n",
1777	    f, l, t->id, s, v);
1778  }
1779}
1780
1781void assert_state::close (const char *c)
1782{
1783  if (strcmp(c, "sp") == 0)
1784    check_sp_flag = 0;
1785  else if (strcmp(c, "br") == 0)
1786    check_br_flag = 0;
1787  else if (strcmp(c, "fi") == 0)
1788    check_fi_flag = 0;
1789  else if (strcmp(c, "nf") == 0)
1790    check_fi_flag = 0;
1791  else if (strcmp(c, "ce") == 0)
1792    check_ce_flag = 0;
1793  else
1794    fprintf(stderr, "internal error: unrecognised tag in grohtml (%s)\n", c);
1795}
1796
1797const char *replace_negate_str (const char *before, char *after)
1798{
1799  if (before != NULL)
1800    a_delete (char *)before;
1801
1802  if (strlen(after) > 0) {
1803    int d = atoi(after);
1804
1805    if (d < 0 || d > 1) {
1806      fprintf(stderr, "expecting nf/fi value to be 0 or 1 not %d\n", d);
1807      d = 0;
1808    }
1809    if (d == 0)
1810      after[0] = '1';
1811    else
1812      after[0] = '0';
1813    after[1] = (char)0;
1814  }
1815  return after;
1816}
1817
1818const char *replace_str (const char *before, const char *after)
1819{
1820  if (before != NULL)
1821    a_delete (char *)before;
1822  return after;
1823}
1824
1825void assert_state::set (const char *c, const char *v,
1826			const char *f, const char *l)
1827{
1828  if (l == NULL)
1829    l = "<none>";
1830  if (f == NULL)
1831    f = "stdin";
1832
1833  // fprintf(stderr, "%s:%s:setting %s to %s\n", f, l, c, v);
1834  if (strcmp(c, "sp") == 0) {
1835    check_sp_flag = 1;
1836    val_sp = replace_str(val_sp, strsave(v));
1837    file_sp = replace_str(file_sp, strsave(f));
1838    line_sp = replace_str(line_sp, strsave(l));
1839  } else if (strcmp(c, "br") == 0) {
1840    check_br_flag = 1;
1841    val_br = replace_str(val_br, strsave(v));
1842    file_br = replace_str(file_br, strsave(f));
1843    line_br = replace_str(line_br, strsave(l));
1844  } else if (strcmp(c, "fi") == 0) {
1845    check_fi_flag = 1;
1846    val_fi = replace_str(val_fi, strsave(v));
1847    file_fi = replace_str(file_fi, strsave(f));
1848    line_fi = replace_str(line_fi, strsave(l));
1849  } else if (strcmp(c, "nf") == 0) {
1850    check_fi_flag = 1;
1851    val_fi = replace_negate_str(val_fi, strsave(v));
1852    file_fi = replace_str(file_fi, strsave(f));
1853    line_fi = replace_str(line_fi, strsave(l));
1854  } else if (strcmp(c, "ce") == 0) {
1855    check_ce_flag = 1;
1856    val_ce = replace_str(val_ce, strsave(v));
1857    file_ce = replace_str(file_ce, strsave(f));
1858    line_ce = replace_str(line_ce, strsave(l));
1859  }
1860}
1861
1862/*
1863 *  build - builds the troff state assertion.
1864 *          see tmac/www.tmac for cmd examples.
1865 */
1866
1867void assert_state::build (const char *c, const char *v,
1868			  const char *f, const char *l)
1869{
1870  if (c[0] == '{')
1871    set(&c[1], v, f, l);
1872  if (c[0] == '}')
1873    close(&c[1]);
1874}
1875
1876int assert_state::check_value_error (int c, int v, const char *s,
1877				     const char *name,
1878				     const char *f, const char *l, int flag)
1879{
1880  if (! c) {
1881    if (f == NULL)
1882      f = "stdin";
1883    if (l == NULL)
1884      l = "<none>";
1885    fprintf(stderr, "%s:%s:grohtml (troff state) assertion failed, expected %s to be %s but found it to contain %d\n",
1886	    f, l, name, s, v);
1887    return 0;
1888  }
1889  return flag;
1890}
1891
1892void assert_state::check_value (const char *s, int v, const char *name,
1893				const char *f, const char *l, int *flag)
1894{
1895  if (strncmp(s, "<=", 2) == 0)
1896    *flag = check_value_error(v <= atoi(&s[2]), v, s, name, f, l, *flag);
1897  else if (strncmp(s, ">=", 2) == 0)
1898    *flag = check_value_error(v >= atoi(&s[2]), v, s, name, f, l, *flag);
1899  else if (strncmp(s, "==", 2) == 0)
1900    *flag = check_value_error(v == atoi(&s[2]), v, s, name, f, l, *flag);
1901  else if (strncmp(s, "!=", 2) == 0)
1902    *flag = check_value_error(v != atoi(&s[2]), v, s, name, f, l, *flag);
1903  else if (strncmp(s, "<", 1) == 0)
1904    *flag = check_value_error(v < atoi(&s[2]), v, s, name, f, l, *flag);
1905  else if (strncmp(s, ">", 1) == 0)
1906    *flag = check_value_error(v > atoi(&s[2]), v, s, name, f, l, *flag);
1907  else if (strncmp(s, "=", 1) == 0)
1908    *flag = check_value_error(v == atoi(&s[1]), v, s, name, f, l, *flag);
1909  else
1910    *flag = check_value_error(v == atoi(s), v, s, name, f, l, *flag);
1911}
1912
1913void assert_state::check_sp (int sp)
1914{
1915  if (check_sp_flag)
1916    check_value(val_sp, sp, "sp", file_sp, line_sp, &check_sp_flag);
1917}
1918
1919void assert_state::check_fi (int fi)
1920{
1921  if (check_fi_flag)
1922    check_value(val_fi, fi, "fi", file_fi, line_fi, &check_fi_flag);
1923}
1924
1925void assert_state::check_br (int br)
1926{
1927  if (check_br_flag)
1928    check_value(val_br, br, "br", file_br, line_br, &check_br_flag);
1929}
1930
1931void assert_state::check_ce (int ce)
1932{
1933  if (check_ce_flag)
1934    check_value(val_ce, ce, "ce", file_ce, line_ce, &check_ce_flag);
1935}
1936
1937class html_printer : public printer {
1938  files                file_list;
1939  simple_output        html;
1940  int                  res;
1941  int                  space_char_index;
1942  int                  space_width;
1943  int                  no_of_printed_pages;
1944  int                  paper_length;
1945  string               sbuf;
1946  int                  sbuf_start_hpos;
1947  int                  sbuf_vpos;
1948  int                  sbuf_end_hpos;
1949  int                  sbuf_prev_hpos;
1950  int                  sbuf_kern;
1951  style                sbuf_style;
1952  int                  last_sbuf_length;
1953  int                  overstrike_detected;
1954  style                output_style;
1955  int                  output_hpos;
1956  int                  output_vpos;
1957  int                  output_vpos_max;
1958  int                  output_draw_point_size;
1959  int                  line_thickness;
1960  int                  output_line_thickness;
1961  unsigned char        output_space_code;
1962  char                *inside_font_style;
1963  int                  page_number;
1964  title_desc           title;
1965  header_desc          header;
1966  int                  header_indent;
1967  int                  supress_sub_sup;
1968  int                  cutoff_heading;
1969  page                *page_contents;
1970  html_text           *current_paragraph;
1971  html_indent         *indent;
1972  html_table          *table;
1973  int                  end_center;
1974  int                  end_tempindent;
1975  TAG_ALIGNMENT        next_tag;
1976  int                  fill_on;
1977  int                  max_linelength;
1978  int                  linelength;
1979  int                  pageoffset;
1980  int                  troff_indent;
1981  int                  device_indent;
1982  int                  temp_indent;
1983  int                  pointsize;
1984  int                  vertical_spacing;
1985  int                  line_number;
1986  color               *background;
1987  int                  seen_indent;
1988  int                  next_indent;
1989  int                  seen_pageoffset;
1990  int                  next_pageoffset;
1991  int                  seen_linelength;
1992  int                  next_linelength;
1993  int                  seen_center;
1994  int                  next_center;
1995  int                  seen_space;
1996  int                  seen_break;
1997  int                  current_column;
1998  int                  row_space;
1999  assert_state         as;
2000
2001  void  flush_sbuf                    ();
2002  void  set_style                     (const style &);
2003  void  set_space_code                (unsigned char c);
2004  void  do_exec                       (char *, const environment *);
2005  void  do_import                     (char *, const environment *);
2006  void  do_def                        (char *, const environment *);
2007  void  do_mdef                       (char *, c

Large files files are truncated, but you can click here to view the full file