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