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

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

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