node.cpp revision 302408
1// -*- C++ -*-
2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
3   Free Software Foundation, Inc.
4     Written by James Clark (jjc@jclark.com)
5
6This file is part of groff.
7
8groff is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
10Software Foundation; either version 2, or (at your option) any later
11version.
12
13groff is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License along
19with groff; see the file COPYING.  If not, write to the Free Software
20Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21
22extern int debug_state;
23
24#include "troff.h"
25
26#ifdef HAVE_UNISTD_H
27#include <unistd.h>
28#endif
29
30#include "dictionary.h"
31#include "hvunits.h"
32#include "stringclass.h"
33#include "mtsm.h"
34#include "env.h"
35#include "request.h"
36#include "node.h"
37#include "token.h"
38#include "div.h"
39#include "reg.h"
40#include "charinfo.h"
41#include "font.h"
42#include "input.h"
43#include "geometry.h"
44
45#include "nonposix.h"
46
47#ifdef _POSIX_VERSION
48
49#include <sys/wait.h>
50
51#else /* not _POSIX_VERSION */
52
53/* traditional Unix */
54
55#define WIFEXITED(s) (((s) & 0377) == 0)
56#define WEXITSTATUS(s) (((s) >> 8) & 0377)
57#define WTERMSIG(s) ((s) & 0177)
58#define WIFSTOPPED(s) (((s) & 0377) == 0177)
59#define WSTOPSIG(s) (((s) >> 8) & 0377)
60#define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
61
62#endif /* not _POSIX_VERSION */
63
64// declarations to avoid friend name injections
65class tfont;
66class tfont_spec;
67tfont *make_tfont(tfont_spec &);
68
69
70/*
71 *  how many boundaries of images have been written? Useful for
72 *  debugging grohtml
73 */
74
75int image_no = 0;
76static int suppress_start_page = 0;
77
78#define STORE_WIDTH 1
79
80symbol HYPHEN_SYMBOL("hy");
81
82// Character used when a hyphen is inserted at a line break.
83static charinfo *soft_hyphen_char;
84
85enum constant_space_type {
86  CONSTANT_SPACE_NONE,
87  CONSTANT_SPACE_RELATIVE,
88  CONSTANT_SPACE_ABSOLUTE
89  };
90
91struct special_font_list {
92  int n;
93  special_font_list *next;
94};
95
96special_font_list *global_special_fonts;
97static int global_ligature_mode = 1;
98static int global_kern_mode = 1;
99
100class track_kerning_function {
101  int non_zero;
102  units min_size;
103  hunits min_amount;
104  units max_size;
105  hunits max_amount;
106public:
107  track_kerning_function();
108  track_kerning_function(units, hunits, units, hunits);
109  int operator==(const track_kerning_function &);
110  int operator!=(const track_kerning_function &);
111  hunits compute(int point_size);
112};
113
114// embolden fontno when this is the current font
115
116struct conditional_bold {
117  conditional_bold *next;
118  int fontno;
119  hunits offset;
120  conditional_bold(int, hunits, conditional_bold * = 0);
121};
122
123class font_info {
124  tfont *last_tfont;
125  int number;
126  font_size last_size;
127  int last_height;
128  int last_slant;
129  symbol internal_name;
130  symbol external_name;
131  font *fm;
132  char is_bold;
133  hunits bold_offset;
134  track_kerning_function track_kern;
135  constant_space_type is_constant_spaced;
136  units constant_space;
137  int last_ligature_mode;
138  int last_kern_mode;
139  conditional_bold *cond_bold_list;
140  void flush();
141public:
142  special_font_list *sf;
143  font_info(symbol, int, symbol, font *);
144  int contains(charinfo *);
145  void set_bold(hunits);
146  void unbold();
147  void set_conditional_bold(int, hunits);
148  void conditional_unbold(int);
149  void set_track_kern(track_kerning_function &);
150  void set_constant_space(constant_space_type, units = 0);
151  int is_named(symbol);
152  symbol get_name();
153  tfont *get_tfont(font_size, int, int, int);
154  hunits get_space_width(font_size, int);
155  hunits get_narrow_space_width(font_size);
156  hunits get_half_narrow_space_width(font_size);
157  int get_bold(hunits *);
158  int is_special();
159  int is_style();
160  friend symbol get_font_name(int, environment *);
161  friend symbol get_style_name(int);
162};
163
164class tfont_spec {
165protected:
166  symbol name;
167  int input_position;
168  font *fm;
169  font_size size;
170  char is_bold;
171  char is_constant_spaced;
172  int ligature_mode;
173  int kern_mode;
174  hunits bold_offset;
175  hunits track_kern;			// add this to the width
176  hunits constant_space_width;
177  int height;
178  int slant;
179public:
180  tfont_spec(symbol, int, font *, font_size, int, int);
181  tfont_spec(const tfont_spec &spec) { *this = spec; }
182  tfont_spec plain();
183  int operator==(const tfont_spec &);
184  friend tfont *font_info::get_tfont(font_size fs, int, int, int);
185};
186
187class tfont : public tfont_spec {
188  static tfont *tfont_list;
189  tfont *next;
190  tfont *plain_version;
191public:
192  tfont(tfont_spec &);
193  int contains(charinfo *);
194  hunits get_width(charinfo *c);
195  int get_bold(hunits *);
196  int get_constant_space(hunits *);
197  hunits get_track_kern();
198  tfont *get_plain();
199  font_size get_size();
200  symbol get_name();
201  charinfo *get_lig(charinfo *c1, charinfo *c2);
202  int get_kern(charinfo *c1, charinfo *c2, hunits *res);
203  int get_input_position();
204  int get_character_type(charinfo *);
205  int get_height();
206  int get_slant();
207  vunits get_char_height(charinfo *);
208  vunits get_char_depth(charinfo *);
209  hunits get_char_skew(charinfo *);
210  hunits get_italic_correction(charinfo *);
211  hunits get_left_italic_correction(charinfo *);
212  hunits get_subscript_correction(charinfo *);
213  friend tfont *make_tfont(tfont_spec &);
214};
215
216inline int env_definite_font(environment *env)
217{
218  return env->get_family()->make_definite(env->get_font());
219}
220
221/* font_info functions */
222
223static font_info **font_table = 0;
224static int font_table_size = 0;
225
226font_info::font_info(symbol nm, int n, symbol enm, font *f)
227: last_tfont(0), number(n), last_size(0),
228  internal_name(nm), external_name(enm), fm(f),
229  is_bold(0), is_constant_spaced(CONSTANT_SPACE_NONE), last_ligature_mode(1),
230  last_kern_mode(1), cond_bold_list(0), sf(0)
231{
232}
233
234inline int font_info::contains(charinfo *ci)
235{
236  return fm != 0 && fm->contains(ci->get_index());
237}
238
239inline int font_info::is_special()
240{
241  return fm != 0 && fm->is_special();
242}
243
244inline int font_info::is_style()
245{
246  return fm == 0;
247}
248
249tfont *make_tfont(tfont_spec &spec)
250{
251  for (tfont *p = tfont::tfont_list; p; p = p->next)
252    if (*p == spec)
253      return p;
254  return new tfont(spec);
255}
256
257// this is the current_font, fontno is where we found the character,
258// presumably a special font
259
260tfont *font_info::get_tfont(font_size fs, int height, int slant, int fontno)
261{
262  if (last_tfont == 0 || fs != last_size
263      || height != last_height || slant != last_slant
264      || global_ligature_mode != last_ligature_mode
265      || global_kern_mode != last_kern_mode
266      || fontno != number) {
267	font_info *f = font_table[fontno];
268	tfont_spec spec(f->external_name, f->number, f->fm, fs, height, slant);
269	for (conditional_bold *p = cond_bold_list; p; p = p->next)
270	  if (p->fontno == fontno) {
271	    spec.is_bold = 1;
272	    spec.bold_offset = p->offset;
273	    break;
274	  }
275	if (!spec.is_bold && is_bold) {
276	  spec.is_bold = 1;
277	  spec.bold_offset = bold_offset;
278	}
279	spec.track_kern = track_kern.compute(fs.to_scaled_points());
280	spec.ligature_mode = global_ligature_mode;
281	spec.kern_mode = global_kern_mode;
282	switch (is_constant_spaced) {
283	case CONSTANT_SPACE_NONE:
284	  break;
285	case CONSTANT_SPACE_ABSOLUTE:
286	  spec.is_constant_spaced = 1;
287	  spec.constant_space_width = constant_space;
288	  break;
289	case CONSTANT_SPACE_RELATIVE:
290	  spec.is_constant_spaced = 1;
291	  spec.constant_space_width
292	    = scale(constant_space*fs.to_scaled_points(),
293		    units_per_inch,
294		    36*72*sizescale);
295	  break;
296	default:
297	  assert(0);
298	}
299	if (fontno != number)
300	  return make_tfont(spec);
301	last_tfont = make_tfont(spec);
302	last_size = fs;
303	last_height = height;
304	last_slant = slant;
305	last_ligature_mode = global_ligature_mode;
306	last_kern_mode = global_kern_mode;
307      }
308  return last_tfont;
309}
310
311int font_info::get_bold(hunits *res)
312{
313  if (is_bold) {
314    *res = bold_offset;
315    return 1;
316  }
317  else
318    return 0;
319}
320
321void font_info::unbold()
322{
323  if (is_bold) {
324    is_bold = 0;
325    flush();
326  }
327}
328
329void font_info::set_bold(hunits offset)
330{
331  if (!is_bold || offset != bold_offset) {
332    is_bold = 1;
333    bold_offset = offset;
334    flush();
335  }
336}
337
338void font_info::set_conditional_bold(int fontno, hunits offset)
339{
340  for (conditional_bold *p = cond_bold_list; p; p = p->next)
341    if (p->fontno == fontno) {
342      if (offset != p->offset) {
343	p->offset = offset;
344	flush();
345      }
346      return;
347    }
348  cond_bold_list = new conditional_bold(fontno, offset, cond_bold_list);
349}
350
351conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x)
352: next(x), fontno(f), offset(h)
353{
354}
355
356void font_info::conditional_unbold(int fontno)
357{
358  for (conditional_bold **p = &cond_bold_list; *p; p = &(*p)->next)
359    if ((*p)->fontno == fontno) {
360      conditional_bold *tem = *p;
361      *p = (*p)->next;
362      delete tem;
363      flush();
364      return;
365    }
366}
367
368void font_info::set_constant_space(constant_space_type type, units x)
369{
370  if (type != is_constant_spaced
371      || (type != CONSTANT_SPACE_NONE && x != constant_space)) {
372    flush();
373    is_constant_spaced = type;
374    constant_space = x;
375  }
376}
377
378void font_info::set_track_kern(track_kerning_function &tk)
379{
380  if (track_kern != tk) {
381    track_kern = tk;
382    flush();
383  }
384}
385
386void font_info::flush()
387{
388  last_tfont = 0;
389}
390
391int font_info::is_named(symbol s)
392{
393  return internal_name == s;
394}
395
396symbol font_info::get_name()
397{
398  return internal_name;
399}
400
401symbol get_font_name(int fontno, environment *env)
402{
403  symbol f = font_table[fontno]->get_name();
404  if (font_table[fontno]->is_style()) {
405    return concat(env->get_family()->nm, f);
406  }
407  return f;
408}
409
410symbol get_style_name(int fontno)
411{
412  if (font_table[fontno]->is_style())
413    return font_table[fontno]->get_name();
414  else
415    return EMPTY_SYMBOL;
416}
417
418hunits font_info::get_space_width(font_size fs, int space_sz)
419{
420  if (is_constant_spaced == CONSTANT_SPACE_NONE)
421    return scale(hunits(fm->get_space_width(fs.to_scaled_points())),
422			space_sz, 12);
423  else if (is_constant_spaced == CONSTANT_SPACE_ABSOLUTE)
424    return constant_space;
425  else
426    return scale(constant_space*fs.to_scaled_points(),
427		 units_per_inch, 36*72*sizescale);
428}
429
430hunits font_info::get_narrow_space_width(font_size fs)
431{
432  charinfo *ci = get_charinfo(symbol("|"));
433  if (fm->contains(ci->get_index()))
434    return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
435  else
436    return hunits(fs.to_units()/6);
437}
438
439hunits font_info::get_half_narrow_space_width(font_size fs)
440{
441  charinfo *ci = get_charinfo(symbol("^"));
442  if (fm->contains(ci->get_index()))
443    return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
444  else
445    return hunits(fs.to_units()/12);
446}
447
448/* tfont */
449
450tfont_spec::tfont_spec(symbol nm, int n, font *f,
451		       font_size s, int h, int sl)
452: name(nm), input_position(n), fm(f), size(s),
453  is_bold(0), is_constant_spaced(0), ligature_mode(1), kern_mode(1),
454  height(h), slant(sl)
455{
456  if (height == size.to_scaled_points())
457    height = 0;
458}
459
460int tfont_spec::operator==(const tfont_spec &spec)
461{
462  if (fm == spec.fm
463      && size == spec.size
464      && input_position == spec.input_position
465      && name == spec.name
466      && height == spec.height
467      && slant == spec.slant
468      && (is_bold
469	  ? (spec.is_bold && bold_offset == spec.bold_offset)
470	  : !spec.is_bold)
471      && track_kern == spec.track_kern
472      && (is_constant_spaced
473	  ? (spec.is_constant_spaced
474	     && constant_space_width == spec.constant_space_width)
475	  : !spec.is_constant_spaced)
476      && ligature_mode == spec.ligature_mode
477      && kern_mode == spec.kern_mode)
478    return 1;
479  else
480    return 0;
481}
482
483tfont_spec tfont_spec::plain()
484{
485  return tfont_spec(name, input_position, fm, size, height, slant);
486}
487
488hunits tfont::get_width(charinfo *c)
489{
490  if (is_constant_spaced)
491    return constant_space_width;
492  else if (is_bold)
493    return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
494	    + track_kern + bold_offset);
495  else
496    return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
497	    + track_kern);
498}
499
500vunits tfont::get_char_height(charinfo *c)
501{
502  vunits v = fm->get_height(c->get_index(), size.to_scaled_points());
503  if (height != 0 && height != size.to_scaled_points())
504    return scale(v, height, size.to_scaled_points());
505  else
506    return v;
507}
508
509vunits tfont::get_char_depth(charinfo *c)
510{
511  vunits v = fm->get_depth(c->get_index(), size.to_scaled_points());
512  if (height != 0 && height != size.to_scaled_points())
513    return scale(v, height, size.to_scaled_points());
514  else
515    return v;
516}
517
518hunits tfont::get_char_skew(charinfo *c)
519{
520  return hunits(fm->get_skew(c->get_index(), size.to_scaled_points(), slant));
521}
522
523hunits tfont::get_italic_correction(charinfo *c)
524{
525  return hunits(fm->get_italic_correction(c->get_index(), size.to_scaled_points()));
526}
527
528hunits tfont::get_left_italic_correction(charinfo *c)
529{
530  return hunits(fm->get_left_italic_correction(c->get_index(),
531					       size.to_scaled_points()));
532}
533
534hunits tfont::get_subscript_correction(charinfo *c)
535{
536  return hunits(fm->get_subscript_correction(c->get_index(),
537					     size.to_scaled_points()));
538}
539
540inline int tfont::get_input_position()
541{
542  return input_position;
543}
544
545inline int tfont::contains(charinfo *ci)
546{
547  return fm->contains(ci->get_index());
548}
549
550inline int tfont::get_character_type(charinfo *ci)
551{
552  return fm->get_character_type(ci->get_index());
553}
554
555inline int tfont::get_bold(hunits *res)
556{
557  if (is_bold) {
558    *res = bold_offset;
559    return 1;
560  }
561  else
562    return 0;
563}
564
565inline int tfont::get_constant_space(hunits *res)
566{
567  if (is_constant_spaced) {
568    *res = constant_space_width;
569    return 1;
570  }
571  else
572    return 0;
573}
574
575inline hunits tfont::get_track_kern()
576{
577  return track_kern;
578}
579
580inline tfont *tfont::get_plain()
581{
582  return plain_version;
583}
584
585inline font_size tfont::get_size()
586{
587  return size;
588}
589
590inline symbol tfont::get_name()
591{
592  return name;
593}
594
595inline int tfont::get_height()
596{
597  return height;
598}
599
600inline int tfont::get_slant()
601{
602  return slant;
603}
604
605symbol SYMBOL_ff("ff");
606symbol SYMBOL_fi("fi");
607symbol SYMBOL_fl("fl");
608symbol SYMBOL_Fi("Fi");
609symbol SYMBOL_Fl("Fl");
610
611charinfo *tfont::get_lig(charinfo *c1, charinfo *c2)
612{
613  if (ligature_mode == 0)
614    return 0;
615  charinfo *ci = 0;
616  if (c1->get_ascii_code() == 'f') {
617    switch (c2->get_ascii_code()) {
618    case 'f':
619      if (fm->has_ligature(font::LIG_ff))
620	ci = get_charinfo(SYMBOL_ff);
621      break;
622    case 'i':
623      if (fm->has_ligature(font::LIG_fi))
624	ci = get_charinfo(SYMBOL_fi);
625      break;
626    case 'l':
627      if (fm->has_ligature(font::LIG_fl))
628	ci = get_charinfo(SYMBOL_fl);
629      break;
630    }
631  }
632  else if (ligature_mode != 2 && c1->nm == SYMBOL_ff) {
633    switch (c2->get_ascii_code()) {
634    case 'i':
635      if (fm->has_ligature(font::LIG_ffi))
636	ci = get_charinfo(SYMBOL_Fi);
637      break;
638    case 'l':
639      if (fm->has_ligature(font::LIG_ffl))
640	ci = get_charinfo(SYMBOL_Fl);
641      break;
642    }
643  }
644  if (ci != 0 && fm->contains(ci->get_index()))
645    return ci;
646  return 0;
647}
648
649inline int tfont::get_kern(charinfo *c1, charinfo *c2, hunits *res)
650{
651  if (kern_mode == 0)
652    return 0;
653  else {
654    int n = fm->get_kern(c1->get_index(),
655			 c2->get_index(),
656			 size.to_scaled_points());
657    if (n) {
658      *res = hunits(n);
659      return 1;
660    }
661    else
662      return 0;
663  }
664}
665
666tfont *tfont::tfont_list = 0;
667
668tfont::tfont(tfont_spec &spec) : tfont_spec(spec)
669{
670  next = tfont_list;
671  tfont_list = this;
672  tfont_spec plain_spec = plain();
673  tfont *p;
674  for (p = tfont_list; p; p = p->next)
675    if (*p == plain_spec) {
676      plain_version = p;
677      break;
678    }
679  if (!p)
680    plain_version = new tfont(plain_spec);
681}
682
683/* output_file */
684
685class real_output_file : public output_file {
686#ifndef POPEN_MISSING
687  int piped;
688#endif
689  int printing;        // decision via optional page list
690  int output_on;       // \O[0] or \O[1] escape calls
691  virtual void really_transparent_char(unsigned char) = 0;
692  virtual void really_print_line(hunits x, vunits y, node *n,
693				 vunits before, vunits after, hunits width) = 0;
694  virtual void really_begin_page(int pageno, vunits page_length) = 0;
695  virtual void really_copy_file(hunits x, vunits y, const char *filename);
696  virtual void really_put_filename(const char *filename);
697  virtual void really_on();
698  virtual void really_off();
699public:
700  FILE *fp;
701  real_output_file();
702  ~real_output_file();
703  void flush();
704  void transparent_char(unsigned char);
705  void print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
706  void begin_page(int pageno, vunits page_length);
707  void put_filename(const char *filename);
708  void on();
709  void off();
710  int is_on();
711  int is_printing();
712  void copy_file(hunits x, vunits y, const char *filename);
713};
714
715class suppress_output_file : public real_output_file {
716public:
717  suppress_output_file();
718  void really_transparent_char(unsigned char);
719  void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
720  void really_begin_page(int pageno, vunits page_length);
721};
722
723class ascii_output_file : public real_output_file {
724public:
725  ascii_output_file();
726  void really_transparent_char(unsigned char);
727  void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
728  void really_begin_page(int pageno, vunits page_length);
729  void outc(unsigned char c);
730  void outs(const char *s);
731};
732
733void ascii_output_file::outc(unsigned char c)
734{
735  fputc(c, fp);
736}
737
738void ascii_output_file::outs(const char *s)
739{
740  fputc('<', fp);
741  if (s)
742    fputs(s, fp);
743  fputc('>', fp);
744}
745
746struct hvpair;
747
748class troff_output_file : public real_output_file {
749  units hpos;
750  units vpos;
751  units output_vpos;
752  units output_hpos;
753  int force_motion;
754  int current_size;
755  int current_slant;
756  int current_height;
757  tfont *current_tfont;
758  color *current_fill_color;
759  color *current_glyph_color;
760  int current_font_number;
761  symbol *font_position;
762  int nfont_positions;
763  enum { TBUF_SIZE = 256 };
764  char tbuf[TBUF_SIZE];
765  int tbuf_len;
766  int tbuf_kern;
767  int begun_page;
768  int cur_div_level;
769  string tag_list;
770  void do_motion();
771  void put(char c);
772  void put(unsigned char c);
773  void put(int i);
774  void put(unsigned int i);
775  void put(const char *s);
776  void set_font(tfont *tf);
777  void flush_tbuf();
778public:
779  troff_output_file();
780  ~troff_output_file();
781  void trailer(vunits page_length);
782  void put_char(charinfo *, tfont *, color *, color *);
783  void put_char_width(charinfo *, tfont *, color *, color *, hunits, hunits);
784  void right(hunits);
785  void down(vunits);
786  void moveto(hunits, vunits);
787  void start_special(tfont *, color *, color *, int = 0);
788  void start_special();
789  void special_char(unsigned char c);
790  void end_special();
791  void word_marker();
792  void really_transparent_char(unsigned char c);
793  void really_print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
794  void really_begin_page(int pageno, vunits page_length);
795  void really_copy_file(hunits x, vunits y, const char *filename);
796  void really_put_filename(const char *filename);
797  void really_on();
798  void really_off();
799  void draw(char, hvpair *, int, font_size, color *, color *);
800  void determine_line_limits (char code, hvpair *point, int npoints);
801  void check_charinfo(tfont *tf, charinfo *ci);
802  void glyph_color(color *c);
803  void fill_color(color *c);
804  int get_hpos() { return hpos; }
805  int get_vpos() { return vpos; }
806  void add_to_tag_list(string s);
807  friend void space_char_hmotion_node::tprint(troff_output_file *);
808  friend void unbreakable_space_node::tprint(troff_output_file *);
809};
810
811static void put_string(const char *s, FILE *fp)
812{
813  for (; *s != '\0'; ++s)
814    putc(*s, fp);
815}
816
817inline void troff_output_file::put(char c)
818{
819  putc(c, fp);
820}
821
822inline void troff_output_file::put(unsigned char c)
823{
824  putc(c, fp);
825}
826
827inline void troff_output_file::put(const char *s)
828{
829  put_string(s, fp);
830}
831
832inline void troff_output_file::put(int i)
833{
834  put_string(i_to_a(i), fp);
835}
836
837inline void troff_output_file::put(unsigned int i)
838{
839  put_string(ui_to_a(i), fp);
840}
841
842void troff_output_file::start_special(tfont *tf, color *gcol, color *fcol,
843				      int no_init_string)
844{
845  set_font(tf);
846  glyph_color(gcol);
847  fill_color(fcol);
848  flush_tbuf();
849  do_motion();
850  if (!no_init_string)
851    put("x X ");
852}
853
854void troff_output_file::start_special()
855{
856  flush_tbuf();
857  do_motion();
858  put("x X ");
859}
860
861void troff_output_file::special_char(unsigned char c)
862{
863  put(c);
864  if (c == '\n')
865    put('+');
866}
867
868void troff_output_file::end_special()
869{
870  put('\n');
871}
872
873inline void troff_output_file::moveto(hunits h, vunits v)
874{
875  hpos = h.to_units();
876  vpos = v.to_units();
877}
878
879void troff_output_file::really_print_line(hunits x, vunits y, node *n,
880					  vunits before, vunits after, hunits)
881{
882  moveto(x, y);
883  while (n != 0) {
884    // Check whether we should push the current troff state and use
885    // the state at the start of the invocation of this diversion.
886    if (n->div_nest_level > cur_div_level && n->push_state) {
887      state.push_state(n->push_state);
888      cur_div_level = n->div_nest_level;
889    }
890    // Has the current diversion level decreased?  Then we must pop the
891    // troff state.
892    while (n->div_nest_level < cur_div_level) {
893      state.pop_state();
894      cur_div_level = n->div_nest_level;
895    }
896    // Now check whether the state has changed.
897    if ((is_on() || n->force_tprint())
898	&& (state.changed(n->state) || n->is_tag() || n->is_special)) {
899      flush_tbuf();
900      do_motion();
901      force_motion = 1;
902      flush();
903      state.flush(fp, n->state, tag_list);
904      tag_list = string("");
905      flush();
906    }
907    n->tprint(this);
908    n = n->next;
909  }
910  flush_tbuf();
911  // This ensures that transparent throughput will have a more predictable
912  // position.
913  do_motion();
914  force_motion = 1;
915  hpos = 0;
916  put('n');
917  put(before.to_units());
918  put(' ');
919  put(after.to_units());
920  put('\n');
921}
922
923inline void troff_output_file::word_marker()
924{
925  flush_tbuf();
926  if (is_on())
927    put('w');
928}
929
930inline void troff_output_file::right(hunits n)
931{
932  hpos += n.to_units();
933}
934
935inline void troff_output_file::down(vunits n)
936{
937  vpos += n.to_units();
938}
939
940void troff_output_file::do_motion()
941{
942  if (force_motion) {
943    put('V');
944    put(vpos);
945    put('\n');
946    put('H');
947    put(hpos);
948    put('\n');
949  }
950  else {
951    if (hpos != output_hpos) {
952      units n = hpos - output_hpos;
953      if (n > 0 && n < hpos) {
954	put('h');
955	put(n);
956      }
957      else {
958	put('H');
959	put(hpos);
960      }
961      put('\n');
962    }
963    if (vpos != output_vpos) {
964      units n = vpos - output_vpos;
965      if (n > 0 && n < vpos) {
966	put('v');
967	put(n);
968      }
969      else {
970	put('V');
971	put(vpos);
972      }
973      put('\n');
974    }
975  }
976  output_vpos = vpos;
977  output_hpos = hpos;
978  force_motion = 0;
979}
980
981void troff_output_file::flush_tbuf()
982{
983  if (!is_on()) {
984    tbuf_len = 0;
985    return;
986  }
987
988  if (tbuf_len == 0)
989    return;
990  if (tbuf_kern == 0)
991    put('t');
992  else {
993    put('u');
994    put(tbuf_kern);
995    put(' ');
996  }
997  check_output_limits(hpos, vpos);
998  check_output_limits(hpos, vpos - current_size);
999
1000  for (int i = 0; i < tbuf_len; i++)
1001    put(tbuf[i]);
1002  put('\n');
1003  tbuf_len = 0;
1004}
1005
1006void troff_output_file::check_charinfo(tfont *tf, charinfo *ci)
1007{
1008  if (!is_on())
1009    return;
1010
1011  int height = tf->get_char_height(ci).to_units();
1012  int width = tf->get_width(ci).to_units()
1013	      + tf->get_italic_correction(ci).to_units();
1014  int depth = tf->get_char_depth(ci).to_units();
1015  check_output_limits(output_hpos, output_vpos - height);
1016  check_output_limits(output_hpos + width, output_vpos + depth);
1017}
1018
1019void troff_output_file::put_char_width(charinfo *ci, tfont *tf,
1020				       color *gcol, color *fcol,
1021				       hunits w, hunits k)
1022{
1023  int kk = k.to_units();
1024  if (!is_on()) {
1025    flush_tbuf();
1026    hpos += w.to_units() + kk;
1027    return;
1028  }
1029  set_font(tf);
1030  unsigned char c = ci->get_ascii_code();
1031  if (c == '\0') {
1032    glyph_color(gcol);
1033    fill_color(fcol);
1034    flush_tbuf();
1035    do_motion();
1036    check_charinfo(tf, ci);
1037    if (ci->numbered()) {
1038      put('N');
1039      put(ci->get_number());
1040    }
1041    else {
1042      put('C');
1043      const char *s = ci->nm.contents();
1044      if (s[1] == 0) {
1045	put('\\');
1046	put(s[0]);
1047      }
1048      else
1049	put(s);
1050    }
1051    put('\n');
1052    hpos += w.to_units() + kk;
1053  }
1054  else if (tcommand_flag) {
1055    if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
1056	&& (!gcol || gcol == current_glyph_color)
1057	&& (!fcol || fcol == current_fill_color)
1058	&& kk == tbuf_kern
1059	&& tbuf_len < TBUF_SIZE) {
1060      check_charinfo(tf, ci);
1061      tbuf[tbuf_len++] = c;
1062      output_hpos += w.to_units() + kk;
1063      hpos = output_hpos;
1064      return;
1065    }
1066    glyph_color(gcol);
1067    fill_color(fcol);
1068    flush_tbuf();
1069    do_motion();
1070    check_charinfo(tf, ci);
1071    tbuf[tbuf_len++] = c;
1072    output_hpos += w.to_units() + kk;
1073    tbuf_kern = kk;
1074    hpos = output_hpos;
1075  }
1076  else {
1077    // flush_tbuf();
1078    int n = hpos - output_hpos;
1079    check_charinfo(tf, ci);
1080    // check_output_limits(output_hpos, output_vpos);
1081    if (vpos == output_vpos
1082	&& (!gcol || gcol == current_glyph_color)
1083	&& (!fcol || fcol == current_fill_color)
1084	&& n > 0 && n < 100 && !force_motion) {
1085      put(char(n/10 + '0'));
1086      put(char(n%10 + '0'));
1087      put(c);
1088      output_hpos = hpos;
1089    }
1090    else {
1091      glyph_color(gcol);
1092      fill_color(fcol);
1093      do_motion();
1094      put('c');
1095      put(c);
1096    }
1097    hpos += w.to_units() + kk;
1098  }
1099}
1100
1101void troff_output_file::put_char(charinfo *ci, tfont *tf,
1102				 color *gcol, color *fcol)
1103{
1104  flush_tbuf();
1105  if (!is_on())
1106    return;
1107  set_font(tf);
1108  unsigned char c = ci->get_ascii_code();
1109  if (c == '\0') {
1110    glyph_color(gcol);
1111    fill_color(fcol);
1112    flush_tbuf();
1113    do_motion();
1114    if (ci->numbered()) {
1115      put('N');
1116      put(ci->get_number());
1117    }
1118    else {
1119      put('C');
1120      const char *s = ci->nm.contents();
1121      if (s[1] == 0) {
1122	put('\\');
1123	put(s[0]);
1124      }
1125      else
1126	put(s);
1127    }
1128    put('\n');
1129  }
1130  else {
1131    int n = hpos - output_hpos;
1132    if (vpos == output_vpos
1133	&& (!gcol || gcol == current_glyph_color)
1134	&& (!fcol || fcol == current_fill_color)
1135	&& n > 0 && n < 100) {
1136      put(char(n/10 + '0'));
1137      put(char(n%10 + '0'));
1138      put(c);
1139      output_hpos = hpos;
1140    }
1141    else {
1142      glyph_color(gcol);
1143      fill_color(fcol);
1144      flush_tbuf();
1145      do_motion();
1146      put('c');
1147      put(c);
1148    }
1149  }
1150}
1151
1152// set_font calls `flush_tbuf' if necessary.
1153
1154void troff_output_file::set_font(tfont *tf)
1155{
1156  if (current_tfont == tf)
1157    return;
1158  flush_tbuf();
1159  int n = tf->get_input_position();
1160  symbol nm = tf->get_name();
1161  if (n >= nfont_positions || font_position[n] != nm) {
1162    put("x font ");
1163    put(n);
1164    put(' ');
1165    put(nm.contents());
1166    put('\n');
1167    if (n >= nfont_positions) {
1168      int old_nfont_positions = nfont_positions;
1169      symbol *old_font_position = font_position;
1170      nfont_positions *= 3;
1171      nfont_positions /= 2;
1172      if (nfont_positions <= n)
1173	nfont_positions = n + 10;
1174      font_position = new symbol[nfont_positions];
1175      memcpy(font_position, old_font_position,
1176	     old_nfont_positions*sizeof(symbol));
1177      a_delete old_font_position;
1178    }
1179    font_position[n] = nm;
1180  }
1181  if (current_font_number != n) {
1182    put('f');
1183    put(n);
1184    put('\n');
1185    current_font_number = n;
1186  }
1187  int size = tf->get_size().to_scaled_points();
1188  if (current_size != size) {
1189    put('s');
1190    put(size);
1191    put('\n');
1192    current_size = size;
1193  }
1194  int slant = tf->get_slant();
1195  if (current_slant != slant) {
1196    put("x Slant ");
1197    put(slant);
1198    put('\n');
1199    current_slant = slant;
1200  }
1201  int height = tf->get_height();
1202  if (current_height != height) {
1203    put("x Height ");
1204    put(height == 0 ? current_size : height);
1205    put('\n');
1206    current_height = height;
1207  }
1208  current_tfont = tf;
1209}
1210
1211// fill_color calls `flush_tbuf' and `do_motion' if necessary.
1212
1213void troff_output_file::fill_color(color *col)
1214{
1215  if (!col || current_fill_color == col)
1216    return;
1217  current_fill_color = col;
1218  if (!color_flag)
1219    return;
1220  flush_tbuf();
1221  do_motion();
1222  put("DF");
1223  unsigned int components[4];
1224  color_scheme cs;
1225  cs = col->get_components(components);
1226  switch (cs) {
1227  case DEFAULT:
1228    put('d');
1229    break;
1230  case RGB:
1231    put("r ");
1232    put(Red);
1233    put(' ');
1234    put(Green);
1235    put(' ');
1236    put(Blue);
1237    break;
1238  case CMY:
1239    put("c ");
1240    put(Cyan);
1241    put(' ');
1242    put(Magenta);
1243    put(' ');
1244    put(Yellow);
1245    break;
1246  case CMYK:
1247    put("k ");
1248    put(Cyan);
1249    put(' ');
1250    put(Magenta);
1251    put(' ');
1252    put(Yellow);
1253    put(' ');
1254    put(Black);
1255    break;
1256  case GRAY:
1257    put("g ");
1258    put(Gray);
1259    break;
1260  }
1261  put('\n');
1262}
1263
1264// glyph_color calls `flush_tbuf' and `do_motion' if necessary.
1265
1266void troff_output_file::glyph_color(color *col)
1267{
1268  if (!col || current_glyph_color == col)
1269    return;
1270  current_glyph_color = col;
1271  if (!color_flag)
1272    return;
1273  flush_tbuf();
1274  // grotty doesn't like a color command if the vertical position is zero.
1275  do_motion();
1276  put("m");
1277  unsigned int components[4];
1278  color_scheme cs;
1279  cs = col->get_components(components);
1280  switch (cs) {
1281  case DEFAULT:
1282    put('d');
1283    break;
1284  case RGB:
1285    put("r ");
1286    put(Red);
1287    put(' ');
1288    put(Green);
1289    put(' ');
1290    put(Blue);
1291    break;
1292  case CMY:
1293    put("c ");
1294    put(Cyan);
1295    put(' ');
1296    put(Magenta);
1297    put(' ');
1298    put(Yellow);
1299    break;
1300  case CMYK:
1301    put("k ");
1302    put(Cyan);
1303    put(' ');
1304    put(Magenta);
1305    put(' ');
1306    put(Yellow);
1307    put(' ');
1308    put(Black);
1309    break;
1310  case GRAY:
1311    put("g ");
1312    put(Gray);
1313    break;
1314  }
1315  put('\n');
1316}
1317
1318void troff_output_file::add_to_tag_list(string s)
1319{
1320  if (tag_list == string(""))
1321    tag_list = s;
1322  else {
1323    tag_list += string("\n");
1324    tag_list += s;
1325  }
1326}
1327
1328// determine_line_limits - works out the smallest box which will contain
1329//			   the entity, code, built from the point array.
1330void troff_output_file::determine_line_limits(char code, hvpair *point,
1331					      int npoints)
1332{
1333  int i, x, y;
1334
1335  if (!is_on())
1336    return;
1337
1338  switch (code) {
1339  case 'c':
1340  case 'C':
1341    // only the h field is used when defining a circle
1342    check_output_limits(output_hpos,
1343			output_vpos - point[0].h.to_units()/2);
1344    check_output_limits(output_hpos + point[0].h.to_units(),
1345			output_vpos + point[0].h.to_units()/2);
1346    break;
1347  case 'E':
1348  case 'e':
1349    check_output_limits(output_hpos,
1350			output_vpos - point[0].v.to_units()/2);
1351    check_output_limits(output_hpos + point[0].h.to_units(),
1352			output_vpos + point[0].v.to_units()/2);
1353    break;
1354  case 'P':
1355  case 'p':
1356    x = output_hpos;
1357    y = output_vpos;
1358    check_output_limits(x, y);
1359    for (i = 0; i < npoints; i++) {
1360      x += point[i].h.to_units();
1361      y += point[i].v.to_units();
1362      check_output_limits(x, y);
1363    }
1364    break;
1365  case 't':
1366    x = output_hpos;
1367    y = output_vpos;
1368    for (i = 0; i < npoints; i++) {
1369      x += point[i].h.to_units();
1370      y += point[i].v.to_units();
1371      check_output_limits(x, y);
1372    }
1373    break;
1374  case 'a':
1375    double c[2];
1376    int p[4];
1377    int minx, miny, maxx, maxy;
1378    x = output_hpos;
1379    y = output_vpos;
1380    p[0] = point[0].h.to_units();
1381    p[1] = point[0].v.to_units();
1382    p[2] = point[1].h.to_units();
1383    p[3] = point[1].v.to_units();
1384    if (adjust_arc_center(p, c)) {
1385      check_output_arc_limits(x, y,
1386			      p[0], p[1], p[2], p[3],
1387			      c[0], c[1],
1388			      &minx, &maxx, &miny, &maxy);
1389      check_output_limits(minx, miny);
1390      check_output_limits(maxx, maxy);
1391      break;
1392    }
1393    // fall through
1394  case 'l':
1395    x = output_hpos;
1396    y = output_vpos;
1397    check_output_limits(x, y);
1398    for (i = 0; i < npoints; i++) {
1399      x += point[i].h.to_units();
1400      y += point[i].v.to_units();
1401      check_output_limits(x, y);
1402    }
1403    break;
1404  default:
1405    x = output_hpos;
1406    y = output_vpos;
1407    for (i = 0; i < npoints; i++) {
1408      x += point[i].h.to_units();
1409      y += point[i].v.to_units();
1410      check_output_limits(x, y);
1411    }
1412  }
1413}
1414
1415void troff_output_file::draw(char code, hvpair *point, int npoints,
1416			     font_size fsize, color *gcol, color *fcol)
1417{
1418  int i;
1419  glyph_color(gcol);
1420  fill_color(fcol);
1421  flush_tbuf();
1422  do_motion();
1423  if (is_on()) {
1424    int size = fsize.to_scaled_points();
1425    if (current_size != size) {
1426      put('s');
1427      put(size);
1428      put('\n');
1429      current_size = size;
1430      current_tfont = 0;
1431    }
1432    put('D');
1433    put(code);
1434    if (code == 'c') {
1435      put(' ');
1436      put(point[0].h.to_units());
1437    }
1438    else
1439      for (i = 0; i < npoints; i++) {
1440	put(' ');
1441	put(point[i].h.to_units());
1442	put(' ');
1443	put(point[i].v.to_units());
1444      }
1445    determine_line_limits(code, point, npoints);
1446  }
1447
1448  for (i = 0; i < npoints; i++)
1449    output_hpos += point[i].h.to_units();
1450  hpos = output_hpos;
1451  if (code != 'e') {
1452    for (i = 0; i < npoints; i++)
1453      output_vpos += point[i].v.to_units();
1454    vpos = output_vpos;
1455  }
1456  if (is_on())
1457    put('\n');
1458}
1459
1460void troff_output_file::really_on()
1461{
1462  flush_tbuf();
1463  force_motion = 1;
1464  do_motion();
1465}
1466
1467void troff_output_file::really_off()
1468{
1469  flush_tbuf();
1470}
1471
1472void troff_output_file::really_put_filename(const char *filename)
1473{
1474  flush_tbuf();
1475  put("F ");
1476  put(filename);
1477  put('\n');
1478}
1479
1480void troff_output_file::really_begin_page(int pageno, vunits page_length)
1481{
1482  flush_tbuf();
1483  if (begun_page) {
1484    if (page_length > V0) {
1485      put('V');
1486      put(page_length.to_units());
1487      put('\n');
1488    }
1489  }
1490  else
1491    begun_page = 1;
1492  current_tfont = 0;
1493  current_font_number = -1;
1494  current_size = 0;
1495  // current_height = 0;
1496  // current_slant = 0;
1497  hpos = 0;
1498  vpos = 0;
1499  output_hpos = 0;
1500  output_vpos = 0;
1501  force_motion = 1;
1502  for (int i = 0; i < nfont_positions; i++)
1503    font_position[i] = NULL_SYMBOL;
1504  put('p');
1505  put(pageno);
1506  put('\n');
1507}
1508
1509void troff_output_file::really_copy_file(hunits x, vunits y,
1510					 const char *filename)
1511{
1512  moveto(x, y);
1513  flush_tbuf();
1514  do_motion();
1515  errno = 0;
1516  FILE *ifp = include_search_path.open_file_cautious(filename);
1517  if (ifp == 0)
1518    error("can't open `%1': %2", filename, strerror(errno));
1519  else {
1520    int c;
1521    while ((c = getc(ifp)) != EOF)
1522      put(char(c));
1523    fclose(ifp);
1524  }
1525  force_motion = 1;
1526  current_size = 0;
1527  current_tfont = 0;
1528  current_font_number = -1;
1529  for (int i = 0; i < nfont_positions; i++)
1530    font_position[i] = NULL_SYMBOL;
1531}
1532
1533void troff_output_file::really_transparent_char(unsigned char c)
1534{
1535  put(c);
1536}
1537
1538troff_output_file::~troff_output_file()
1539{
1540  a_delete font_position;
1541}
1542
1543void troff_output_file::trailer(vunits page_length)
1544{
1545  flush_tbuf();
1546  if (page_length > V0) {
1547    put("x trailer\n");
1548    put('V');
1549    put(page_length.to_units());
1550    put('\n');
1551  }
1552  put("x stop\n");
1553}
1554
1555troff_output_file::troff_output_file()
1556: current_slant(0), current_height(0), current_fill_color(0),
1557  current_glyph_color(0), nfont_positions(10), tbuf_len(0), begun_page(0),
1558  cur_div_level(0)
1559{
1560  font_position = new symbol[nfont_positions];
1561  put("x T ");
1562  put(device);
1563  put('\n');
1564  put("x res ");
1565  put(units_per_inch);
1566  put(' ');
1567  put(hresolution);
1568  put(' ');
1569  put(vresolution);
1570  put('\n');
1571  put("x init\n");
1572}
1573
1574/* output_file */
1575
1576output_file *the_output = 0;
1577
1578output_file::output_file()
1579{
1580}
1581
1582output_file::~output_file()
1583{
1584}
1585
1586void output_file::trailer(vunits)
1587{
1588}
1589
1590void output_file::put_filename(const char *)
1591{
1592}
1593
1594void output_file::on()
1595{
1596}
1597
1598void output_file::off()
1599{
1600}
1601
1602real_output_file::real_output_file()
1603: printing(0), output_on(1)
1604{
1605#ifndef POPEN_MISSING
1606  if (pipe_command) {
1607    if ((fp = popen(pipe_command, POPEN_WT)) != 0) {
1608      piped = 1;
1609      return;
1610    }
1611    error("pipe open failed: %1", strerror(errno));
1612  }
1613  piped = 0;
1614#endif /* not POPEN_MISSING */
1615  fp = stdout;
1616}
1617
1618real_output_file::~real_output_file()
1619{
1620  if (!fp)
1621    return;
1622  // To avoid looping, set fp to 0 before calling fatal().
1623  if (ferror(fp) || fflush(fp) < 0) {
1624    fp = 0;
1625    fatal("error writing output file");
1626  }
1627#ifndef POPEN_MISSING
1628  if (piped) {
1629    int result = pclose(fp);
1630    fp = 0;
1631    if (result < 0)
1632      fatal("pclose failed");
1633    if (!WIFEXITED(result))
1634      error("output process `%1' got fatal signal %2",
1635	    pipe_command,
1636	    WIFSIGNALED(result) ? WTERMSIG(result) : WSTOPSIG(result));
1637    else {
1638      int exit_status = WEXITSTATUS(result);
1639      if (exit_status != 0)
1640	error("output process `%1' exited with status %2",
1641	      pipe_command, exit_status);
1642    }
1643  }
1644  else
1645#endif /* not POPEN MISSING */
1646  if (fclose(fp) < 0) {
1647    fp = 0;
1648    fatal("error closing output file");
1649  }
1650}
1651
1652void real_output_file::flush()
1653{
1654  if (fflush(fp) < 0)
1655    fatal("error writing output file");
1656}
1657
1658int real_output_file::is_printing()
1659{
1660  return printing;
1661}
1662
1663void real_output_file::begin_page(int pageno, vunits page_length)
1664{
1665  printing = in_output_page_list(pageno);
1666  if (printing)
1667    really_begin_page(pageno, page_length);
1668}
1669
1670void real_output_file::copy_file(hunits x, vunits y, const char *filename)
1671{
1672  if (printing && output_on)
1673    really_copy_file(x, y, filename);
1674  check_output_limits(x.to_units(), y.to_units());
1675}
1676
1677void real_output_file::transparent_char(unsigned char c)
1678{
1679  if (printing && output_on)
1680    really_transparent_char(c);
1681}
1682
1683void real_output_file::print_line(hunits x, vunits y, node *n,
1684			     vunits before, vunits after, hunits width)
1685{
1686  if (printing)
1687    really_print_line(x, y, n, before, after, width);
1688  delete_node_list(n);
1689}
1690
1691void real_output_file::really_copy_file(hunits, vunits, const char *)
1692{
1693  // do nothing
1694}
1695
1696void real_output_file::put_filename(const char *filename)
1697{
1698  really_put_filename(filename);
1699}
1700
1701void real_output_file::really_put_filename(const char *)
1702{
1703}
1704
1705void real_output_file::on()
1706{
1707  really_on();
1708  if (output_on == 0)
1709    output_on = 1;
1710}
1711
1712void real_output_file::off()
1713{
1714  really_off();
1715  output_on = 0;
1716}
1717
1718int real_output_file::is_on()
1719{
1720  return output_on;
1721}
1722
1723void real_output_file::really_on()
1724{
1725}
1726
1727void real_output_file::really_off()
1728{
1729}
1730
1731/* ascii_output_file */
1732
1733void ascii_output_file::really_transparent_char(unsigned char c)
1734{
1735  putc(c, fp);
1736}
1737
1738void ascii_output_file::really_print_line(hunits, vunits, node *n,
1739					  vunits, vunits, hunits)
1740{
1741  while (n != 0) {
1742    n->ascii_print(this);
1743    n = n->next;
1744  }
1745  fputc('\n', fp);
1746}
1747
1748void ascii_output_file::really_begin_page(int /*pageno*/, vunits /*page_length*/)
1749{
1750  fputs("<beginning of page>\n", fp);
1751}
1752
1753ascii_output_file::ascii_output_file()
1754{
1755}
1756
1757/* suppress_output_file */
1758
1759suppress_output_file::suppress_output_file()
1760{
1761}
1762
1763void suppress_output_file::really_print_line(hunits, vunits, node *, vunits, vunits, hunits)
1764{
1765}
1766
1767void suppress_output_file::really_begin_page(int, vunits)
1768{
1769}
1770
1771void suppress_output_file::really_transparent_char(unsigned char)
1772{
1773}
1774
1775/* glyphs, ligatures, kerns, discretionary breaks */
1776
1777class charinfo_node : public node {
1778protected:
1779  charinfo *ci;
1780public:
1781  charinfo_node(charinfo *, statem *, int, node * = 0);
1782  int ends_sentence();
1783  int overlaps_vertically();
1784  int overlaps_horizontally();
1785};
1786
1787charinfo_node::charinfo_node(charinfo *c, statem *s, int pop, node *x)
1788: node(x, s, pop), ci(c)
1789{
1790}
1791
1792int charinfo_node::ends_sentence()
1793{
1794  if (ci->ends_sentence())
1795    return 1;
1796  else if (ci->transparent())
1797    return 2;
1798  else
1799    return 0;
1800}
1801
1802int charinfo_node::overlaps_horizontally()
1803{
1804  return ci->overlaps_horizontally();
1805}
1806
1807int charinfo_node::overlaps_vertically()
1808{
1809  return ci->overlaps_vertically();
1810}
1811
1812class glyph_node : public charinfo_node {
1813  static glyph_node *free_list;
1814protected:
1815  tfont *tf;
1816  color *gcol;
1817  color *fcol;		/* this is needed for grotty */
1818#ifdef STORE_WIDTH
1819  hunits wid;
1820  glyph_node(charinfo *, tfont *, color *, color *, hunits,
1821	     statem *, int, node * = 0);
1822#endif
1823public:
1824  void *operator new(size_t);
1825  void operator delete(void *);
1826  glyph_node(charinfo *, tfont *, color *, color *,
1827	     statem *, int, node * = 0);
1828  ~glyph_node() {}
1829  node *copy();
1830  node *merge_glyph_node(glyph_node *);
1831  node *merge_self(node *);
1832  hunits width();
1833  node *last_char_node();
1834  units size();
1835  void vertical_extent(vunits *, vunits *);
1836  hunits subscript_correction();
1837  hunits italic_correction();
1838  hunits left_italic_correction();
1839  hunits skew();
1840  hyphenation_type get_hyphenation_type();
1841  tfont *get_tfont();
1842  color *get_glyph_color();
1843  color *get_fill_color();
1844  void tprint(troff_output_file *);
1845  void zero_width_tprint(troff_output_file *);
1846  hyphen_list *get_hyphen_list(hyphen_list *, int *);
1847  node *add_self(node *, hyphen_list **);
1848  void ascii_print(ascii_output_file *);
1849  void asciify(macro *);
1850  int character_type();
1851  int same(node *);
1852  const char *type();
1853  int force_tprint();
1854  int is_tag();
1855  void debug_node();
1856};
1857
1858glyph_node *glyph_node::free_list = 0;
1859
1860class ligature_node : public glyph_node {
1861  node *n1;
1862  node *n2;
1863#ifdef STORE_WIDTH
1864  ligature_node(charinfo *, tfont *, color *, color *, hunits,
1865		node *, node *, statem *, int, node * = 0);
1866#endif
1867public:
1868  void *operator new(size_t);
1869  void operator delete(void *);
1870  ligature_node(charinfo *, tfont *, color *, color *,
1871		node *, node *, statem *, int, node * = 0);
1872  ~ligature_node();
1873  node *copy();
1874  node *add_self(node *, hyphen_list **);
1875  hyphen_list *get_hyphen_list(hyphen_list *, int *);
1876  void ascii_print(ascii_output_file *);
1877  void asciify(macro *);
1878  int same(node *);
1879  const char *type();
1880  int force_tprint();
1881  int is_tag();
1882};
1883
1884class kern_pair_node : public node {
1885  hunits amount;
1886  node *n1;
1887  node *n2;
1888public:
1889  kern_pair_node(hunits, node *, node *, statem *, int, node * = 0);
1890  ~kern_pair_node();
1891  node *copy();
1892  node *merge_glyph_node(glyph_node *);
1893  node *add_self(node *, hyphen_list **);
1894  hyphen_list *get_hyphen_list(hyphen_list *, int *);
1895  node *add_discretionary_hyphen();
1896  hunits width();
1897  node *last_char_node();
1898  hunits italic_correction();
1899  hunits subscript_correction();
1900  void tprint(troff_output_file *);
1901  hyphenation_type get_hyphenation_type();
1902  int ends_sentence();
1903  void ascii_print(ascii_output_file *);
1904  void asciify(macro *);
1905  int same(node *);
1906  const char *type();
1907  int force_tprint();
1908  int is_tag();
1909  void vertical_extent(vunits *, vunits *);
1910};
1911
1912class dbreak_node : public node {
1913  node *none;
1914  node *pre;
1915  node *post;
1916public:
1917  dbreak_node(node *, node *, statem *, int, node * = 0);
1918  ~dbreak_node();
1919  node *copy();
1920  node *merge_glyph_node(glyph_node *);
1921  node *add_discretionary_hyphen();
1922  hunits width();
1923  node *last_char_node();
1924  hunits italic_correction();
1925  hunits subscript_correction();
1926  void tprint(troff_output_file *);
1927  breakpoint *get_breakpoints(hunits width, int ns, breakpoint *rest = 0,
1928			      int is_inner = 0);
1929  int nbreaks();
1930  int ends_sentence();
1931  void split(int, node **, node **);
1932  hyphenation_type get_hyphenation_type();
1933  void ascii_print(ascii_output_file *);
1934  void asciify(macro *);
1935  int same(node *);
1936  const char *type();
1937  int force_tprint();
1938  int is_tag();
1939};
1940
1941void *glyph_node::operator new(size_t n)
1942{
1943  assert(n == sizeof(glyph_node));
1944  if (!free_list) {
1945    const int BLOCK = 1024;
1946    free_list = (glyph_node *)new char[sizeof(glyph_node)*BLOCK];
1947    for (int i = 0; i < BLOCK - 1; i++)
1948      free_list[i].next = free_list + i + 1;
1949    free_list[BLOCK-1].next = 0;
1950  }
1951  glyph_node *p = free_list;
1952  free_list = (glyph_node *)(free_list->next);
1953  p->next = 0;
1954  return p;
1955}
1956
1957void *ligature_node::operator new(size_t n)
1958{
1959  return new char[n];
1960}
1961
1962void glyph_node::operator delete(void *p)
1963{
1964  if (p) {
1965    ((glyph_node *)p)->next = free_list;
1966    free_list = (glyph_node *)p;
1967  }
1968}
1969
1970void ligature_node::operator delete(void *p)
1971{
1972  delete[] (char *)p;
1973}
1974
1975glyph_node::glyph_node(charinfo *c, tfont *t, color *gc, color *fc,
1976		       statem *s, int pop, node *x)
1977: charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc)
1978{
1979#ifdef STORE_WIDTH
1980  wid = tf->get_width(ci);
1981#endif
1982}
1983
1984#ifdef STORE_WIDTH
1985glyph_node::glyph_node(charinfo *c, tfont *t,
1986		       color *gc, color *fc, hunits w,
1987		       statem *s, int pop, node *x)
1988: charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc), wid(w)
1989{
1990}
1991#endif
1992
1993node *glyph_node::copy()
1994{
1995#ifdef STORE_WIDTH
1996  return new glyph_node(ci, tf, gcol, fcol, wid, state, div_nest_level);
1997#else
1998  return new glyph_node(ci, tf, gcol, fcol, state, div_nest_level);
1999#endif
2000}
2001
2002node *glyph_node::merge_self(node *nd)
2003{
2004  return nd->merge_glyph_node(this);
2005}
2006
2007int glyph_node::character_type()
2008{
2009  return tf->get_character_type(ci);
2010}
2011
2012node *glyph_node::add_self(node *n, hyphen_list **p)
2013{
2014  assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
2015  next = 0;
2016  node *nn;
2017  if (n == 0 || (nn = n->merge_glyph_node(this)) == 0) {
2018    next = n;
2019    nn = this;
2020  }
2021  if ((*p)->hyphen)
2022    nn = nn->add_discretionary_hyphen();
2023  hyphen_list *pp = *p;
2024  *p = (*p)->next;
2025  delete pp;
2026  return nn;
2027}
2028
2029units glyph_node::size()
2030{
2031  return tf->get_size().to_units();
2032}
2033
2034hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail, int *count)
2035{
2036  (*count)++;
2037  return new hyphen_list(ci->get_hyphenation_code(), tail);
2038}
2039
2040tfont *node::get_tfont()
2041{
2042  return 0;
2043}
2044
2045tfont *glyph_node::get_tfont()
2046{
2047  return tf;
2048}
2049
2050color *node::get_glyph_color()
2051{
2052  return 0;
2053}
2054
2055color *glyph_node::get_glyph_color()
2056{
2057  return gcol;
2058}
2059
2060color *node::get_fill_color()
2061{
2062  return 0;
2063}
2064
2065color *glyph_node::get_fill_color()
2066{
2067  return fcol;
2068}
2069
2070node *node::merge_glyph_node(glyph_node *)
2071{
2072  return 0;
2073}
2074
2075node *glyph_node::merge_glyph_node(glyph_node *gn)
2076{
2077  if (tf == gn->tf && gcol == gn->gcol && fcol == gn->fcol) {
2078    charinfo *lig;
2079    if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
2080      node *next1 = next;
2081      next = 0;
2082      return new ligature_node(lig, tf, gcol, fcol, this, gn, state,
2083			       gn->div_nest_level, next1);
2084    }
2085    hunits kern;
2086    if (tf->get_kern(ci, gn->ci, &kern)) {
2087      node *next1 = next;
2088      next = 0;
2089      return new kern_pair_node(kern, this, gn, state,
2090				gn->div_nest_level, next1);
2091    }
2092  }
2093  return 0;
2094}
2095
2096#ifdef STORE_WIDTH
2097inline
2098#endif
2099hunits glyph_node::width()
2100{
2101#ifdef STORE_WIDTH
2102  return wid;
2103#else
2104  return tf->get_width(ci);
2105#endif
2106}
2107
2108node *glyph_node::last_char_node()
2109{
2110  return this;
2111}
2112
2113void glyph_node::vertical_extent(vunits *min, vunits *max)
2114{
2115  *min = -tf->get_char_height(ci);
2116  *max = tf->get_char_depth(ci);
2117}
2118
2119hunits glyph_node::skew()
2120{
2121  return tf->get_char_skew(ci);
2122}
2123
2124hunits glyph_node::subscript_correction()
2125{
2126  return tf->get_subscript_correction(ci);
2127}
2128
2129hunits glyph_node::italic_correction()
2130{
2131  return tf->get_italic_correction(ci);
2132}
2133
2134hunits glyph_node::left_italic_correction()
2135{
2136  return tf->get_left_italic_correction(ci);
2137}
2138
2139hyphenation_type glyph_node::get_hyphenation_type()
2140{
2141  return HYPHEN_MIDDLE;
2142}
2143
2144void glyph_node::ascii_print(ascii_output_file *ascii)
2145{
2146  unsigned char c = ci->get_ascii_code();
2147  if (c != 0)
2148    ascii->outc(c);
2149  else
2150    ascii->outs(ci->nm.contents());
2151}
2152
2153void glyph_node::debug_node()
2154{
2155  unsigned char c = ci->get_ascii_code();
2156  fprintf(stderr, "{ %s [", type());
2157  if (c)
2158    fprintf(stderr, "%c", c);
2159  else
2160    fputs(ci->nm.contents(), stderr);
2161  if (push_state)
2162    fprintf(stderr, " <push_state>");
2163  if (state)
2164    state->display_state();
2165  fprintf(stderr, " nest level %d", div_nest_level);
2166  fprintf(stderr, "]}\n");
2167  fflush(stderr);
2168}
2169
2170ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2171			     node *gn1, node *gn2, statem *s,
2172			     int pop, node *x)
2173: glyph_node(c, t, gc, fc, s, pop, x), n1(gn1), n2(gn2)
2174{
2175}
2176
2177#ifdef STORE_WIDTH
2178ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2179			     hunits w, node *gn1, node *gn2, statem *s,
2180			     int pop, node *x)
2181: glyph_node(c, t, gc, fc, w, s, pop, x), n1(gn1), n2(gn2)
2182{
2183}
2184#endif
2185
2186ligature_node::~ligature_node()
2187{
2188  delete n1;
2189  delete n2;
2190}
2191
2192node *ligature_node::copy()
2193{
2194#ifdef STORE_WIDTH
2195  return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(), n2->copy(),
2196			   state, div_nest_level);
2197#else
2198  return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy(),
2199			   state, div_nest_level);
2200#endif
2201}
2202
2203void ligature_node::ascii_print(ascii_output_file *ascii)
2204{
2205  n1->ascii_print(ascii);
2206  n2->ascii_print(ascii);
2207}
2208
2209hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail, int *count)
2210{
2211  hyphen_list *hl = n2->get_hyphen_list(tail, count);
2212  return n1->get_hyphen_list(hl, count);
2213}
2214
2215node *ligature_node::add_self(node *n, hyphen_list **p)
2216{
2217  n = n1->add_self(n, p);
2218  n = n2->add_self(n, p);
2219  n1 = n2 = 0;
2220  delete this;
2221  return n;
2222}
2223
2224kern_pair_node::kern_pair_node(hunits n, node *first, node *second,
2225			       statem* s, int pop, node *x)
2226: node(x, s, pop), amount(n), n1(first), n2(second)
2227{
2228}
2229
2230dbreak_node::dbreak_node(node *n, node *p, statem *s, int pop, node *x)
2231: node(x, s, pop), none(n), pre(p), post(0)
2232{
2233}
2234
2235node *dbreak_node::merge_glyph_node(glyph_node *gn)
2236{
2237  glyph_node *gn2 = (glyph_node *)gn->copy();
2238  node *new_none = none ? none->merge_glyph_node(gn) : 0;
2239  node *new_post = post ? post->merge_glyph_node(gn2) : 0;
2240  if (new_none == 0 && new_post == 0) {
2241    delete gn2;
2242    return 0;
2243  }
2244  if (new_none != 0)
2245    none = new_none;
2246  else {
2247    gn->next = none;
2248    none = gn;
2249  }
2250  if (new_post != 0)
2251    post = new_post;
2252  else {
2253    gn2->next = post;
2254    post = gn2;
2255  }
2256  return this;
2257}
2258
2259node *kern_pair_node::merge_glyph_node(glyph_node *gn)
2260{
2261  node *nd = n2->merge_glyph_node(gn);
2262  if (nd == 0)
2263    return 0;
2264  n2 = nd;
2265  nd = n2->merge_self(n1);
2266  if (nd) {
2267    nd->next = next;
2268    n1 = 0;
2269    n2 = 0;
2270    delete this;
2271    return nd;
2272  }
2273  return this;
2274}
2275
2276hunits kern_pair_node::italic_correction()
2277{
2278  return n2->italic_correction();
2279}
2280
2281hunits kern_pair_node::subscript_correction()
2282{
2283  return n2->subscript_correction();
2284}
2285
2286void kern_pair_node::vertical_extent(vunits *min, vunits *max)
2287{
2288  n1->vertical_extent(min, max);
2289  vunits min2, max2;
2290  n2->vertical_extent(&min2, &max2);
2291  if (min2 < *min)
2292    *min = min2;
2293  if (max2 > *max)
2294    *max = max2;
2295}
2296
2297node *kern_pair_node::add_discretionary_hyphen()
2298{
2299  tfont *tf = n2->get_tfont();
2300  if (tf) {
2301    if (tf->contains(soft_hyphen_char)) {
2302      color *gcol = n2->get_glyph_color();
2303      color *fcol = n2->get_fill_color();
2304      node *next1 = next;
2305      next = 0;
2306      node *n = copy();
2307      glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
2308				      state, div_nest_level);
2309      node *nn = n->merge_glyph_node(gn);
2310      if (nn == 0) {
2311	gn->next = n;
2312	nn = gn;
2313      }
2314      return new dbreak_node(this, nn, state, div_nest_level, next1);
2315    }
2316  }
2317  return this;
2318}
2319
2320kern_pair_node::~kern_pair_node()
2321{
2322  if (n1 != 0)
2323    delete n1;
2324  if (n2 != 0)
2325    delete n2;
2326}
2327
2328dbreak_node::~dbreak_node()
2329{
2330  delete_node_list(pre);
2331  delete_node_list(post);
2332  delete_node_list(none);
2333}
2334
2335node *kern_pair_node::copy()
2336{
2337  return new kern_pair_node(amount, n1->copy(), n2->copy(), state,
2338			    div_nest_level);
2339}
2340
2341node *copy_node_list(node *n)
2342{
2343  node *p = 0;
2344  while (n != 0) {
2345    node *nn = n->copy();
2346    nn->next = p;
2347    p = nn;
2348    n = n->next;
2349  }
2350  while (p != 0) {
2351    node *pp = p->next;
2352    p->next = n;
2353    n = p;
2354    p = pp;
2355  }
2356  return n;
2357}
2358
2359void delete_node_list(node *n)
2360{
2361  while (n != 0) {
2362    node *tem = n;
2363    n = n->next;
2364    delete tem;
2365  }
2366}
2367
2368node *dbreak_node::copy()
2369{
2370  dbreak_node *p = new dbreak_node(copy_node_list(none), copy_node_list(pre),
2371				   state, div_nest_level);
2372  p->post = copy_node_list(post);
2373  return p;
2374}
2375
2376hyphen_list *node::get_hyphen_list(hyphen_list *tail, int *)
2377{
2378  return tail;
2379}
2380
2381hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail, int *count)
2382{
2383  hyphen_list *hl = n2->get_hyphen_list(tail, count);
2384  return n1->get_hyphen_list(hl, count);
2385}
2386
2387class hyphen_inhibitor_node : public node {
2388public:
2389  hyphen_inhibitor_node(node * = 0);
2390  node *copy();
2391  int same(node *);
2392  const char *type();
2393  int force_tprint();
2394  int is_tag();
2395  hyphenation_type get_hyphenation_type();
2396};
2397
2398hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd)
2399{
2400}
2401
2402node *hyphen_inhibitor_node::copy()
2403{
2404  return new hyphen_inhibitor_node;
2405}
2406
2407int hyphen_inhibitor_node::same(node *)
2408{
2409  return 1;
2410}
2411
2412const char *hyphen_inhibitor_node::type()
2413{
2414  return "hyphen_inhibitor_node";
2415}
2416
2417int hyphen_inhibitor_node::force_tprint()
2418{
2419  return 0;
2420}
2421
2422int hyphen_inhibitor_node::is_tag()
2423{
2424  return 0;
2425}
2426
2427hyphenation_type hyphen_inhibitor_node::get_hyphenation_type()
2428{
2429  return HYPHEN_INHIBIT;
2430}
2431
2432/* add_discretionary_hyphen methods */
2433
2434node *dbreak_node::add_discretionary_hyphen()
2435{
2436  if (post)
2437    post = post->add_discretionary_hyphen();
2438  if (none)
2439    none = none->add_discretionary_hyphen();
2440  return this;
2441}
2442
2443node *node::add_discretionary_hyphen()
2444{
2445  tfont *tf = get_tfont();
2446  if (!tf)
2447    return new hyphen_inhibitor_node(this);
2448  if (tf->contains(soft_hyphen_char)) {
2449    color *gcol = get_glyph_color();
2450    color *fcol = get_fill_color();
2451    node *next1 = next;
2452    next = 0;
2453    node *n = copy();
2454    glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
2455				    state, div_nest_level);
2456    node *n1 = n->merge_glyph_node(gn);
2457    if (n1 == 0) {
2458      gn->next = n;
2459      n1 = gn;
2460    }
2461    return new dbreak_node(this, n1, state, div_nest_level, next1);
2462  }
2463  return this;
2464}
2465
2466node *node::merge_self(node *)
2467{
2468  return 0;
2469}
2470
2471node *node::add_self(node *n, hyphen_list ** /*p*/)
2472{
2473  next = n;
2474  return this;
2475}
2476
2477node *kern_pair_node::add_self(node *n, hyphen_list **p)
2478{
2479  n = n1->add_self(n, p);
2480  n = n2->add_self(n, p);
2481  n1 = n2 = 0;
2482  delete this;
2483  return n;
2484}
2485
2486hunits node::width()
2487{
2488  return H0;
2489}
2490
2491node *node::last_char_node()
2492{
2493  return 0;
2494}
2495
2496int node::force_tprint()
2497{
2498  return 0;
2499}
2500
2501int node::is_tag()
2502{
2503  return 0;
2504}
2505
2506hunits hmotion_node::width()
2507{
2508  return n;
2509}
2510
2511units node::size()
2512{
2513  return points_to_units(10);
2514}
2515
2516void node::debug_node()
2517{
2518  fprintf(stderr, "{ %s ", type());
2519  if (push_state)
2520    fprintf(stderr, " <push_state>");
2521  if (state)
2522    fprintf(stderr, " <state>");
2523  fprintf(stderr, " nest level %d", div_nest_level);
2524  fprintf(stderr, " }\n");
2525  fflush(stderr);
2526}
2527
2528void node::debug_node_list()
2529{
2530  node *n = next;
2531
2532  debug_node();
2533  while (n != 0) {
2534    n->debug_node();
2535    n = n->next;
2536  }
2537}
2538
2539hunits kern_pair_node::width()
2540{
2541  return n1->width() + n2->width() + amount;
2542}
2543
2544node *kern_pair_node::last_char_node()
2545{
2546  node *nd = n2->last_char_node();
2547  if (nd)
2548    return nd;
2549  return n1->last_char_node();
2550}
2551
2552hunits dbreak_node::width()
2553{
2554  hunits x = H0;
2555  for (node *n = none; n != 0; n = n->next)
2556    x += n->width();
2557  return x;
2558}
2559
2560node *dbreak_node::last_char_node()
2561{
2562  for (node *n = none; n; n = n->next) {
2563    node *last_node = n->last_char_node();
2564    if (last_node)
2565      return last_node;
2566  }
2567  return 0;
2568}
2569
2570hunits dbreak_node::italic_correction()
2571{
2572  return none ? none->italic_correction() : H0;
2573}
2574
2575hunits dbreak_node::subscript_correction()
2576{
2577  return none ? none->subscript_correction() : H0;
2578}
2579
2580class italic_corrected_node : public node {
2581  node *n;
2582  hunits x;
2583public:
2584  italic_corrected_node(node *, hunits, statem *, int, node * = 0);
2585  ~italic_corrected_node();
2586  node *copy();
2587  void ascii_print(ascii_output_file *);
2588  void asciify(macro *);
2589  hunits width();
2590  node *last_char_node();
2591  void vertical_extent(vunits *, vunits *);
2592  int ends_sentence();
2593  int overlaps_horizontally();
2594  int overlaps_vertically();
2595  int same(node *);
2596  hyphenation_type get_hyphenation_type();
2597  tfont *get_tfont();
2598  hyphen_list *get_hyphen_list(hyphen_list *, int *);
2599  int character_type();
2600  void tprint(troff_output_file *);
2601  hunits subscript_correction();
2602  hunits skew();
2603  node *add_self(node *, hyphen_list **);
2604  const char *type();
2605  int force_tprint();
2606  int is_tag();
2607};
2608
2609node *node::add_italic_correction(hunits *wd)
2610{
2611  hunits ic = italic_correction();
2612  if (ic.is_zero())
2613    return this;
2614  else {
2615    node *next1 = next;
2616    next = 0;
2617    *wd += ic;
2618    return new italic_corrected_node(this, ic, state, div_nest_level, next1);
2619  }
2620}
2621
2622italic_corrected_node::italic_corrected_node(node *nn, hunits xx, statem *s,
2623					     int pop, node *p)
2624: node(p, s, pop), n(nn), x(xx)
2625{
2626  assert(n != 0);
2627}
2628
2629italic_corrected_node::~italic_corrected_node()
2630{
2631  delete n;
2632}
2633
2634node *italic_corrected_node::copy()
2635{
2636  return new italic_corrected_node(n->copy(), x, state, div_nest_level);
2637}
2638
2639hunits italic_corrected_node::width()
2640{
2641  return n->width() + x;
2642}
2643
2644void italic_corrected_node::vertical_extent(vunits *min, vunits *max)
2645{
2646  n->vertical_extent(min, max);
2647}
2648
2649void italic_corrected_node::tprint(troff_output_file *out)
2650{
2651  n->tprint(out);
2652  out->right(x);
2653}
2654
2655hunits italic_corrected_node::skew()
2656{
2657  return n->skew() - x/2;
2658}
2659
2660hunits italic_corrected_node::subscript_correction()
2661{
2662  return n->subscript_correction() - x;
2663}
2664
2665void italic_corrected_node::ascii_print(ascii_output_file *out)
2666{
2667  n->ascii_print(out);
2668}
2669
2670int italic_corrected_node::ends_sentence()
2671{
2672  return n->ends_sentence();
2673}
2674
2675int italic_corrected_node::overlaps_horizontally()
2676{
2677  return n->overlaps_horizontally();
2678}
2679
2680int italic_corrected_node::overlaps_vertically()
2681{
2682  return n->overlaps_vertically();
2683}
2684
2685node *italic_corrected_node::last_char_node()
2686{
2687  return n->last_char_node();
2688}
2689
2690tfont *italic_corrected_node::get_tfont()
2691{
2692  return n->get_tfont();
2693}
2694
2695hyphenation_type italic_corrected_node::get_hyphenation_type()
2696{
2697  return n->get_hyphenation_type();
2698}
2699
2700node *italic_corrected_node::add_self(node *nd, hyphen_list **p)
2701{
2702  nd = n->add_self(nd, p);
2703  hunits not_interested;
2704  nd = nd->add_italic_correction(&not_interested);
2705  n = 0;
2706  delete this;
2707  return nd;
2708}
2709
2710hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail,
2711						    int *count)
2712{
2713  return n->get_hyphen_list(tail, count);
2714}
2715
2716int italic_corrected_node::character_type()
2717{
2718  return n->character_type();
2719}
2720
2721class break_char_node : public node {
2722  node *ch;
2723  char break_code;
2724  color *col;
2725public:
2726  break_char_node(node *, int, color *, node * = 0);
2727  break_char_node(node *, int, color *, statem *, int, node * = 0);
2728  ~break_char_node();
2729  node *copy();
2730  hunits width();
2731  vunits vertical_width();
2732  node *last_char_node();
2733  int character_type();
2734  int ends_sentence();
2735  node *add_self(node *, hyphen_list **);
2736  hyphen_list *get_hyphen_list(hyphen_list *, int *);
2737  void tprint(troff_output_file *);
2738  void zero_width_tprint(troff_output_file *);
2739  void ascii_print(ascii_output_file *);
2740  void asciify(macro *);
2741  hyphenation_type get_hyphenation_type();
2742  int overlaps_vertically();
2743  int overlaps_horizontally();
2744  units size();
2745  tfont *get_tfont();
2746  int same(node *);
2747  const char *type();
2748  int force_tprint();
2749  int is_tag();
2750};
2751
2752break_char_node::break_char_node(node *n, int bc, color *c, node *x)
2753: node(x), ch(n), break_code(bc), col(c)
2754{
2755}
2756
2757break_char_node::break_char_node(node *n, int bc, color *c, statem *s,
2758				 int pop, node *x)
2759: node(x, s, pop), ch(n), break_code(bc), col(c)
2760{
2761}
2762
2763break_char_node::~break_char_node()
2764{
2765  delete ch;
2766}
2767
2768node *break_char_node::copy()
2769{
2770  return new break_char_node(ch->copy(), break_code, col, state,
2771			     div_nest_level);
2772}
2773
2774hunits break_char_node::width()
2775{
2776  return ch->width();
2777}
2778
2779vunits break_char_node::vertical_width()
2780{
2781  return ch->vertical_width();
2782}
2783
2784node *break_char_node::last_char_node()
2785{
2786  return ch->last_char_node();
2787}
2788
2789int break_char_node::character_type()
2790{
2791  return ch->character_type();
2792}
2793
2794int break_char_node::ends_sentence()
2795{
2796  return ch->ends_sentence();
2797}
2798
2799node *break_char_node::add_self(node *n, hyphen_list **p)
2800{
2801  assert((*p)->hyphenation_code == 0);
2802  if ((*p)->breakable && (break_code & 1)) {
2803    n = new space_node(H0, col, n);
2804    n->freeze_space();
2805  }
2806  next = n;
2807  n = this;
2808  if ((*p)->breakable && (break_code & 2)) {
2809    n = new space_node(H0, col, n);
2810    n->freeze_space();
2811  }
2812  hyphen_list *pp = *p;
2813  *p = (*p)->next;
2814  delete pp;
2815  return n;
2816}
2817
2818hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail, int *)
2819{
2820  return new hyphen_list(0, tail);
2821}
2822
2823hyphenation_type break_char_node::get_hyphenation_type()
2824{
2825  return HYPHEN_MIDDLE;
2826}
2827
2828void break_char_node::ascii_print(ascii_output_file *ascii)
2829{
2830  ch->ascii_print(ascii);
2831}
2832
2833int break_char_node::overlaps_vertically()
2834{
2835  return ch->overlaps_vertically();
2836}
2837
2838int break_char_node::overlaps_horizontally()
2839{
2840  return ch->overlaps_horizontally();
2841}
2842
2843units break_char_node::size()
2844{
2845  return ch->size();
2846}
2847
2848tfont *break_char_node::get_tfont()
2849{
2850  return ch->get_tfont();
2851}
2852
2853node *extra_size_node::copy()
2854{
2855  return new extra_size_node(n, state, div_nest_level);
2856}
2857
2858extra_size_node::extra_size_node(vunits i, statem *s, int pop)
2859: node(0, s, pop), n(i)
2860{
2861}
2862
2863extra_size_node::extra_size_node(vunits i)
2864: n(i)
2865{
2866}
2867
2868node *vertical_size_node::copy()
2869{
2870  return new vertical_size_node(n, state, div_nest_level);
2871}
2872
2873vertical_size_node::vertical_size_node(vunits i, statem *s, int pop)
2874: node(0, s, pop), n(i)
2875{
2876}
2877
2878vertical_size_node::vertical_size_node(vunits i)
2879: n(i)
2880{
2881}
2882
2883node *hmotion_node::copy()
2884{
2885  return new hmotion_node(n, was_tab, unformat, col, state, div_nest_level);
2886}
2887
2888node *space_char_hmotion_node::copy()
2889{
2890  return new space_char_hmotion_node(n, col, state, div_nest_level);
2891}
2892
2893vmotion_node::vmotion_node(vunits i, color *c)
2894: n(i), col(c)
2895{
2896}
2897
2898vmotion_node::vmotion_node(vunits i, color *c, statem *s, int pop)
2899: node(0, s, pop), n(i), col(c)
2900{
2901}
2902
2903node *vmotion_node::copy()
2904{
2905  return new vmotion_node(n, col, state, div_nest_level);
2906}
2907
2908node *dummy_node::copy()
2909{
2910  return new dummy_node;
2911}
2912
2913node *transparent_dummy_node::copy()
2914{
2915  return new transparent_dummy_node;
2916}
2917
2918hline_node::~hline_node()
2919{
2920  if (n)
2921    delete n;
2922}
2923
2924hline_node::hline_node(hunits i, node *c, node *nxt)
2925: node(nxt), x(i), n(c)
2926{
2927}
2928
2929hline_node::hline_node(hunits i, node *c, statem *s, int pop, node *nxt)
2930: node(nxt, s, pop), x(i), n(c)
2931{
2932}
2933
2934node *hline_node::copy()
2935{
2936  return new hline_node(x, n ? n->copy() : 0, state, div_nest_level);
2937}
2938
2939hunits hline_node::width()
2940{
2941  return x < H0 ? H0 : x;
2942}
2943
2944vline_node::vline_node(vunits i, node *c, node *nxt)
2945: node(nxt), x(i), n(c)
2946{
2947}
2948
2949vline_node::vline_node(vunits i, node *c, statem *s, int pop, node *nxt)
2950: node(nxt, s, pop), x(i), n(c)
2951{
2952}
2953
2954vline_node::~vline_node()
2955{
2956  if (n)
2957    delete n;
2958}
2959
2960node *vline_node::copy()
2961{
2962  return new vline_node(x, n ? n->copy() : 0, state, div_nest_level);
2963}
2964
2965hunits vline_node::width()
2966{
2967  return n == 0 ? H0 : n->width();
2968}
2969
2970zero_width_node::zero_width_node(node *nd, statem *s, int pop)
2971: node(0, s, pop), n(nd)
2972{
2973}
2974
2975zero_width_node::zero_width_node(node *nd)
2976: n(nd)
2977{
2978}
2979
2980zero_width_node::~zero_width_node()
2981{
2982  delete_node_list(n);
2983}
2984
2985node *zero_width_node::copy()
2986{
2987  return new zero_width_node(copy_node_list(n), state, div_nest_level);
2988}
2989
2990int node_list_character_type(node *p)
2991{
2992  int t = 0;
2993  for (; p; p = p->next)
2994    t |= p->character_type();
2995  return t;
2996}
2997
2998int zero_width_node::character_type()
2999{
3000  return node_list_character_type(n);
3001}
3002
3003void node_list_vertical_extent(node *p, vunits *min, vunits *max)
3004{
3005  *min = V0;
3006  *max = V0;
3007  vunits cur_vpos = V0;
3008  vunits v1, v2;
3009  for (; p; p = p->next) {
3010    p->vertical_extent(&v1, &v2);
3011    v1 += cur_vpos;
3012    if (v1 < *min)
3013      *min = v1;
3014    v2 += cur_vpos;
3015    if (v2 > *max)
3016      *max = v2;
3017    cur_vpos += p->vertical_width();
3018  }
3019}
3020
3021void zero_width_node::vertical_extent(vunits *min, vunits *max)
3022{
3023  node_list_vertical_extent(n, min, max);
3024}
3025
3026overstrike_node::overstrike_node()
3027: list(0), max_width(H0)
3028{
3029}
3030
3031overstrike_node::overstrike_node(statem *s, int pop)
3032: node(0, s, pop), list(0), max_width(H0)
3033{
3034}
3035
3036overstrike_node::~overstrike_node()
3037{
3038  delete_node_list(list);
3039}
3040
3041node *overstrike_node::copy()
3042{
3043  overstrike_node *on = new overstrike_node(state, div_nest_level);
3044  for (node *tem = list; tem; tem = tem->next)
3045    on->overstrike(tem->copy());
3046  return on;
3047}
3048
3049void overstrike_node::overstrike(node *n)
3050{
3051  if (n == 0)
3052    return;
3053  hunits w = n->width();
3054  if (w > max_width)
3055    max_width = w;
3056  node **p;
3057  for (p = &list; *p; p = &(*p)->next)
3058    ;
3059  n->next = 0;
3060  *p = n;
3061}
3062
3063hunits overstrike_node::width()
3064{
3065  return max_width;
3066}
3067
3068bracket_node::bracket_node()
3069: list(0), max_width(H0)
3070{
3071}
3072
3073bracket_node::bracket_node(statem *s, int pop)
3074: node(0, s, pop), list(0), max_width(H0)
3075{
3076}
3077
3078bracket_node::~bracket_node()
3079{
3080  delete_node_list(list);
3081}
3082
3083node *bracket_node::copy()
3084{
3085  bracket_node *on = new bracket_node(state, div_nest_level);
3086  node *last_node = 0;
3087  node *tem;
3088  if (list)
3089    list->last = 0;
3090  for (tem = list; tem; tem = tem->next) {
3091    if (tem->next)
3092      tem->next->last = tem;
3093    last_node = tem;
3094  }
3095  for (tem = last_node; tem; tem = tem->last)
3096    on->bracket(tem->copy());
3097  return on;
3098}
3099
3100void bracket_node::bracket(node *n)
3101{
3102  if (n == 0)
3103    return;
3104  hunits w = n->width();
3105  if (w > max_width)
3106    max_width = w;
3107  n->next = list;
3108  list = n;
3109}
3110
3111hunits bracket_node::width()
3112{
3113  return max_width;
3114}
3115
3116int node::nspaces()
3117{
3118  return 0;
3119}
3120
3121int node::merge_space(hunits, hunits, hunits)
3122{
3123  return 0;
3124}
3125
3126#if 0
3127space_node *space_node::free_list = 0;
3128
3129void *space_node::operator new(size_t n)
3130{
3131  assert(n == sizeof(space_node));
3132  if (!free_list) {
3133    free_list = (space_node *)new char[sizeof(space_node)*BLOCK];
3134    for (int i = 0; i < BLOCK - 1; i++)
3135      free_list[i].next = free_list + i + 1;
3136    free_list[BLOCK-1].next = 0;
3137  }
3138  space_node *p = free_list;
3139  free_list = (space_node *)(free_list->next);
3140  p->next = 0;
3141  return p;
3142}
3143
3144inline void space_node::operator delete(void *p)
3145{
3146  if (p) {
3147    ((space_node *)p)->next = free_list;
3148    free_list = (space_node *)p;
3149  }
3150}
3151#endif
3152
3153space_node::space_node(hunits nn, color *c, node *p)
3154: node(p, 0, 0), n(nn), set(0), was_escape_colon(0), col(c)
3155{
3156}
3157
3158space_node::space_node(hunits nn, color *c, statem *s, int pop, node *p)
3159: node(p, s, pop), n(nn), set(0), was_escape_colon(0), col(c)
3160{
3161}
3162
3163space_node::space_node(hunits nn, int s, int flag, color *c, statem *st,
3164		       int pop, node *p)
3165: node(p, st, pop), n(nn), set(s), was_escape_colon(flag), col(c)
3166{
3167}
3168
3169#if 0
3170space_node::~space_node()
3171{
3172}
3173#endif
3174
3175node *space_node::copy()
3176{
3177  return new space_node(n, set, was_escape_colon, col, state, div_nest_level);
3178}
3179
3180int space_node::force_tprint()
3181{
3182  return 0;
3183}
3184
3185int space_node::is_tag()
3186{
3187  return 0;
3188}
3189
3190int space_node::nspaces()
3191{
3192  return set ? 0 : 1;
3193}
3194
3195int space_node::merge_space(hunits h, hunits, hunits)
3196{
3197  n += h;
3198  return 1;
3199}
3200
3201hunits space_node::width()
3202{
3203  return n;
3204}
3205
3206void node::spread_space(int*, hunits*)
3207{
3208}
3209
3210void space_node::spread_space(int *n_spaces, hunits *desired_space)
3211{
3212  if (!set) {
3213    assert(*n_spaces > 0);
3214    if (*n_spaces == 1) {
3215      n += *desired_space;
3216      *desired_space = H0;
3217    }
3218    else {
3219      hunits extra = *desired_space / *n_spaces;
3220      *desired_space -= extra;
3221      n += extra;
3222    }
3223    *n_spaces -= 1;
3224    set = 1;
3225  }
3226}
3227
3228void node::freeze_space()
3229{
3230}
3231
3232void space_node::freeze_space()
3233{
3234  set = 1;
3235}
3236
3237void node::is_escape_colon()
3238{
3239}
3240
3241void space_node::is_escape_colon()
3242{
3243  was_escape_colon = 1;
3244}
3245
3246diverted_space_node::diverted_space_node(vunits d, statem *s, int pop,
3247					 node *p)
3248: node(p, s, pop), n(d)
3249{
3250}
3251
3252diverted_space_node::diverted_space_node(vunits d, node *p)
3253: node(p), n(d)
3254{
3255}
3256
3257node *diverted_space_node::copy()
3258{
3259  return new diverted_space_node(n, state, div_nest_level);
3260}
3261
3262diverted_copy_file_node::diverted_copy_file_node(symbol s, statem *st,
3263						 int pop, node *p)
3264: node(p, st, pop), filename(s)
3265{
3266}
3267
3268diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p)
3269: node(p), filename(s)
3270{
3271}
3272
3273node *diverted_copy_file_node::copy()
3274{
3275  return new diverted_copy_file_node(filename, state, div_nest_level);
3276}
3277
3278int node::ends_sentence()
3279{
3280  return 0;
3281}
3282
3283int kern_pair_node::ends_sentence()
3284{
3285  switch (n2->ends_sentence()) {
3286  case 0:
3287    return 0;
3288  case 1:
3289    return 1;
3290  case 2:
3291    break;
3292  default:
3293    assert(0);
3294  }
3295  return n1->ends_sentence();
3296}
3297
3298int node_list_ends_sentence(node *n)
3299{
3300  for (; n != 0; n = n->next)
3301    switch (n->ends_sentence()) {
3302    case 0:
3303      return 0;
3304    case 1:
3305      return 1;
3306    case 2:
3307      break;
3308    default:
3309      assert(0);
3310    }
3311  return 2;
3312}
3313
3314int dbreak_node::ends_sentence()
3315{
3316  return node_list_ends_sentence(none);
3317}
3318
3319int node::overlaps_horizontally()
3320{
3321  return 0;
3322}
3323
3324int node::overlaps_vertically()
3325{
3326  return 0;
3327}
3328
3329int node::discardable()
3330{
3331  return 0;
3332}
3333
3334int space_node::discardable()
3335{
3336  return set ? 0 : 1;
3337}
3338
3339vunits node::vertical_width()
3340{
3341  return V0;
3342}
3343
3344vunits vline_node::vertical_width()
3345{
3346  return x;
3347}
3348
3349vunits vmotion_node::vertical_width()
3350{
3351  return n;
3352}
3353
3354int node::set_unformat_flag()
3355{
3356  return 1;
3357}
3358
3359int node::character_type()
3360{
3361  return 0;
3362}
3363
3364hunits node::subscript_correction()
3365{
3366  return H0;
3367}
3368
3369hunits node::italic_correction()
3370{
3371  return H0;
3372}
3373
3374hunits node::left_italic_correction()
3375{
3376  return H0;
3377}
3378
3379hunits node::skew()
3380{
3381  return H0;
3382}
3383
3384/* vertical_extent methods */
3385
3386void node::vertical_extent(vunits *min, vunits *max)
3387{
3388  vunits v = vertical_width();
3389  if (v < V0) {
3390    *min = v;
3391    *max = V0;
3392  }
3393  else {
3394    *max = v;
3395    *min = V0;
3396  }
3397}
3398
3399void vline_node::vertical_extent(vunits *min, vunits *max)
3400{
3401  if (n == 0)
3402    node::vertical_extent(min, max);
3403  else {
3404    vunits cmin, cmax;
3405    n->vertical_extent(&cmin, &cmax);
3406    vunits h = n->size();
3407    if (x < V0) {
3408      if (-x < h) {
3409	*min = x;
3410	*max = V0;
3411      }
3412      else {
3413	// we print the first character and then move up, so
3414	*max = cmax;
3415	// we print the last character and then move up h
3416	*min = cmin + h;
3417	if (*min > V0)
3418	  *min = V0;
3419	*min += x;
3420      }
3421    }
3422    else {
3423      if (x < h) {
3424	*max = x;
3425	*min = V0;
3426      }
3427      else {
3428	// we move down by h and then print the first character, so
3429	*min = cmin + h;
3430	if (*min > V0)
3431	  *min = V0;
3432	*max = x + cmax;
3433      }
3434    }
3435  }
3436}
3437
3438/* ascii_print methods */
3439
3440static void ascii_print_reverse_node_list(ascii_output_file *ascii, node *n)
3441{
3442  if (n == 0)
3443    return;
3444  ascii_print_reverse_node_list(ascii, n->next);
3445  n->ascii_print(ascii);
3446}
3447
3448void dbreak_node::ascii_print(ascii_output_file *ascii)
3449{
3450  ascii_print_reverse_node_list(ascii, none);
3451}
3452
3453void kern_pair_node::ascii_print(ascii_output_file *ascii)
3454{
3455  n1->ascii_print(ascii);
3456  n2->ascii_print(ascii);
3457}
3458
3459void node::ascii_print(ascii_output_file *)
3460{
3461}
3462
3463void space_node::ascii_print(ascii_output_file *ascii)
3464{
3465  if (!n.is_zero())
3466    ascii->outc(' ');
3467}
3468
3469void hmotion_node::ascii_print(ascii_output_file *ascii)
3470{
3471  // this is pretty arbitrary
3472  if (n >= points_to_units(2))
3473    ascii->outc(' ');
3474}
3475
3476void space_char_hmotion_node::ascii_print(ascii_output_file *ascii)
3477{
3478  ascii->outc(' ');
3479}
3480
3481/* asciify methods */
3482
3483void node::asciify(macro *m)
3484{
3485  m->append(this);
3486}
3487
3488void glyph_node::asciify(macro *m)
3489{
3490  unsigned char c = ci->get_asciify_code();
3491  if (c == 0)
3492    c = ci->get_ascii_code();
3493  if (c != 0) {
3494    m->append(c);
3495    delete this;
3496  }
3497  else
3498    m->append(this);
3499}
3500
3501void kern_pair_node::asciify(macro *m)
3502{
3503  n1->asciify(m);
3504  n2->asciify(m);
3505  n1 = n2 = 0;
3506  delete this;
3507}
3508
3509static void asciify_reverse_node_list(macro *m, node *n)
3510{
3511  if (n == 0)
3512    return;
3513  asciify_reverse_node_list(m, n->next);
3514  n->asciify(m);
3515}
3516
3517void dbreak_node::asciify(macro *m)
3518{
3519  asciify_reverse_node_list(m, none);
3520  none = 0;
3521  delete this;
3522}
3523
3524void ligature_node::asciify(macro *m)
3525{
3526  n1->asciify(m);
3527  n2->asciify(m);
3528  n1 = n2 = 0;
3529  delete this;
3530}
3531
3532void break_char_node::asciify(macro *m)
3533{
3534  ch->asciify(m);
3535  ch = 0;
3536  delete this;
3537}
3538
3539void italic_corrected_node::asciify(macro *m)
3540{
3541  n->asciify(m);
3542  n = 0;
3543  delete this;
3544}
3545
3546void left_italic_corrected_node::asciify(macro *m)
3547{
3548  if (n) {
3549    n->asciify(m);
3550    n = 0;
3551  }
3552  delete this;
3553}
3554
3555void hmotion_node::asciify(macro *m)
3556{
3557  if (was_tab) {
3558    m->append('\t');
3559    delete this;
3560  }
3561  else
3562    m->append(this);
3563}
3564
3565space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3566						 statem *s, int pop,
3567						 node *nxt)
3568: hmotion_node(i, c, s, pop, nxt)
3569{
3570}
3571
3572space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3573 						 node *nxt)
3574: hmotion_node(i, c, 0, 0, nxt)
3575{
3576}
3577
3578void space_char_hmotion_node::asciify(macro *m)
3579{
3580  m->append(ESCAPE_SPACE);
3581  delete this;
3582}
3583
3584void space_node::asciify(macro *m)
3585{
3586  if (was_escape_colon) {
3587    m->append(ESCAPE_COLON);
3588    delete this;
3589  }
3590  else
3591    m->append(this);
3592}
3593
3594void word_space_node::asciify(macro *m)
3595{
3596  for (width_list *w = orig_width; w; w = w->next)
3597    m->append(' ');
3598  delete this;
3599}
3600
3601void unbreakable_space_node::asciify(macro *m)
3602{
3603  m->append(ESCAPE_TILDE);
3604  delete this;
3605}
3606
3607void line_start_node::asciify(macro *)
3608{
3609  delete this;
3610}
3611
3612void vertical_size_node::asciify(macro *)
3613{
3614  delete this;
3615}
3616
3617breakpoint *node::get_breakpoints(hunits /*width*/, int /*nspaces*/,
3618				  breakpoint *rest, int /*is_inner*/)
3619{
3620  return rest;
3621}
3622
3623int node::nbreaks()
3624{
3625  return 0;
3626}
3627
3628breakpoint *space_node::get_breakpoints(hunits wd, int ns,
3629					breakpoint *rest, int is_inner)
3630{
3631  if (next && next->discardable())
3632    return rest;
3633  breakpoint *bp = new breakpoint;
3634  bp->next = rest;
3635  bp->width = wd;
3636  bp->nspaces = ns;
3637  bp->hyphenated = 0;
3638  if (is_inner) {
3639    assert(rest != 0);
3640    bp->index = rest->index + 1;
3641    bp->nd = rest->nd;
3642  }
3643  else {
3644    bp->nd = this;
3645    bp->index = 0;
3646  }
3647  return bp;
3648}
3649
3650int space_node::nbreaks()
3651{
3652  if (next && next->discardable())
3653    return 0;
3654  else
3655    return 1;
3656}
3657
3658static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp,
3659					     int ns, breakpoint *rest)
3660{
3661  if (p != 0) {
3662    rest = p->get_breakpoints(*widthp,
3663			      ns,
3664			      node_list_get_breakpoints(p->next, widthp, ns,
3665							rest),
3666			      1);
3667    *widthp += p->width();
3668  }
3669  return rest;
3670}
3671
3672breakpoint *dbreak_node::get_breakpoints(hunits wd, int ns,
3673					 breakpoint *rest, int is_inner)
3674{
3675  breakpoint *bp = new breakpoint;
3676  bp->next = rest;
3677  bp->width = wd;
3678  for (node *tem = pre; tem != 0; tem = tem->next)
3679    bp->width += tem->width();
3680  bp->nspaces = ns;
3681  bp->hyphenated = 1;
3682  if (is_inner) {
3683    assert(rest != 0);
3684    bp->index = rest->index + 1;
3685    bp->nd = rest->nd;
3686  }
3687  else {
3688    bp->nd = this;
3689    bp->index = 0;
3690  }
3691  return node_list_get_breakpoints(none, &wd, ns, bp);
3692}
3693
3694int dbreak_node::nbreaks()
3695{
3696  int i = 1;
3697  for (node *tem = none; tem != 0; tem = tem->next)
3698    i += tem->nbreaks();
3699  return i;
3700}
3701
3702void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/)
3703{
3704  assert(0);
3705}
3706
3707void space_node::split(int where, node **pre, node **post)
3708{
3709  assert(where == 0);
3710  *pre = next;
3711  *post = 0;
3712  delete this;
3713}
3714
3715static void node_list_split(node *p, int *wherep, node **prep, node **postp)
3716{
3717  if (p == 0)
3718    return;
3719  int nb = p->nbreaks();
3720  node_list_split(p->next, wherep, prep, postp);
3721  if (*wherep < 0) {
3722    p->next = *postp;
3723    *postp = p;
3724  }
3725  else if (*wherep < nb) {
3726    p->next = *prep;
3727    p->split(*wherep, prep, postp);
3728  }
3729  else {
3730    p->next = *prep;
3731    *prep = p;
3732  }
3733  *wherep -= nb;
3734}
3735
3736void dbreak_node::split(int where, node **prep, node **postp)
3737{
3738  assert(where >= 0);
3739  if (where == 0) {
3740    *postp = post;
3741    post = 0;
3742    if (pre == 0)
3743      *prep = next;
3744    else {
3745      node *tem;
3746      for (tem = pre; tem->next != 0; tem = tem->next)
3747	;
3748      tem->next = next;
3749      *prep = pre;
3750    }
3751    pre = 0;
3752    delete this;
3753  }
3754  else {
3755    *prep = next;
3756    where -= 1;
3757    node_list_split(none, &where, prep, postp);
3758    none = 0;
3759    delete this;
3760  }
3761}
3762
3763hyphenation_type node::get_hyphenation_type()
3764{
3765  return HYPHEN_BOUNDARY;
3766}
3767
3768hyphenation_type dbreak_node::get_hyphenation_type()
3769{
3770  return HYPHEN_INHIBIT;
3771}
3772
3773hyphenation_type kern_pair_node::get_hyphenation_type()
3774{
3775  return HYPHEN_MIDDLE;
3776}
3777
3778hyphenation_type dummy_node::get_hyphenation_type()
3779{
3780  return HYPHEN_MIDDLE;
3781}
3782
3783hyphenation_type transparent_dummy_node::get_hyphenation_type()
3784{
3785  return HYPHEN_MIDDLE;
3786}
3787
3788hyphenation_type hmotion_node::get_hyphenation_type()
3789{
3790  return HYPHEN_MIDDLE;
3791}
3792
3793hyphenation_type space_char_hmotion_node::get_hyphenation_type()
3794{
3795  return HYPHEN_MIDDLE;
3796}
3797
3798hyphenation_type overstrike_node::get_hyphenation_type()
3799{
3800  return HYPHEN_MIDDLE;
3801}
3802
3803hyphenation_type space_node::get_hyphenation_type()
3804{
3805  if (was_escape_colon)
3806    return HYPHEN_MIDDLE;
3807  return HYPHEN_BOUNDARY;
3808}
3809
3810hyphenation_type unbreakable_space_node::get_hyphenation_type()
3811{
3812  return HYPHEN_MIDDLE;
3813}
3814
3815int node::interpret(macro *)
3816{
3817  return 0;
3818}
3819
3820special_node::special_node(const macro &m, int n)
3821: mac(m), no_init_string(n)
3822{
3823  font_size fs = curenv->get_font_size();
3824  int char_height = curenv->get_char_height();
3825  int char_slant = curenv->get_char_slant();
3826  int fontno = env_definite_font(curenv);
3827  tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fontno);
3828  if (curenv->is_composite())
3829    tf = tf->get_plain();
3830  gcol = curenv->get_glyph_color();
3831  fcol = curenv->get_fill_color();
3832  is_special = 1;
3833}
3834
3835special_node::special_node(const macro &m, tfont *t,
3836			   color *gc, color *fc,
3837			   statem *s, int pop,
3838			   int n)
3839: node(0, s, pop), mac(m), tf(t), gcol(gc), fcol(fc), no_init_string(n)
3840{
3841  is_special = 1;
3842}
3843
3844int special_node::same(node *n)
3845{
3846  return mac == ((special_node *)n)->mac
3847	 && tf == ((special_node *)n)->tf
3848	 && gcol == ((special_node *)n)->gcol
3849	 && fcol == ((special_node *)n)->fcol
3850	 && no_init_string == ((special_node *)n)->no_init_string;
3851}
3852
3853const char *special_node::type()
3854{
3855  return "special_node";
3856}
3857
3858int special_node::ends_sentence()
3859{
3860  return 2;
3861}
3862
3863int special_node::force_tprint()
3864{
3865  return 0;
3866}
3867
3868int special_node::is_tag()
3869{
3870  return 0;
3871}
3872
3873node *special_node::copy()
3874{
3875  return new special_node(mac, tf, gcol, fcol, state, div_nest_level,
3876			  no_init_string);
3877}
3878
3879void special_node::tprint_start(troff_output_file *out)
3880{
3881  out->start_special(tf, gcol, fcol, no_init_string);
3882}
3883
3884void special_node::tprint_char(troff_output_file *out, unsigned char c)
3885{
3886  out->special_char(c);
3887}
3888
3889void special_node::tprint_end(troff_output_file *out)
3890{
3891  out->end_special();
3892}
3893
3894tfont *special_node::get_tfont()
3895{
3896  return tf;
3897}
3898
3899/* suppress_node */
3900
3901suppress_node::suppress_node(int on_or_off, int issue_limits)
3902: is_on(on_or_off), emit_limits(issue_limits), filename(0), position(0),
3903  image_id(0)
3904{
3905}
3906
3907suppress_node::suppress_node(symbol f, char p, int id)
3908: is_on(2), emit_limits(0), filename(f), position(p), image_id(id)
3909{
3910  is_special = 1;
3911}
3912
3913suppress_node::suppress_node(int issue_limits, int on_or_off,
3914			     symbol f, char p, int id,
3915			     statem *s, int pop)
3916: node(0, s, pop), is_on(on_or_off), emit_limits(issue_limits), filename(f),
3917  position(p), image_id(id)
3918{
3919}
3920
3921int suppress_node::same(node *n)
3922{
3923  return ((is_on == ((suppress_node *)n)->is_on)
3924	  && (emit_limits == ((suppress_node *)n)->emit_limits)
3925	  && (filename == ((suppress_node *)n)->filename)
3926	  && (position == ((suppress_node *)n)->position)
3927	  && (image_id == ((suppress_node *)n)->image_id));
3928}
3929
3930const char *suppress_node::type()
3931{
3932  return "suppress_node";
3933}
3934
3935node *suppress_node::copy()
3936{
3937  return new suppress_node(emit_limits, is_on, filename, position, image_id,
3938			   state, div_nest_level);
3939}
3940
3941/* tag_node */
3942
3943tag_node::tag_node()
3944: delayed(0)
3945{
3946  is_special = 1;
3947}
3948
3949tag_node::tag_node(string s, int delay)
3950: tag_string(s), delayed(delay)
3951{
3952  is_special = !delay;
3953}
3954
3955tag_node::tag_node(string s, statem *st, int pop, int delay)
3956: node(0, st, pop), tag_string(s), delayed(delay)
3957{
3958  is_special = !delay;
3959}
3960
3961node *tag_node::copy()
3962{
3963  return new tag_node(tag_string, state, div_nest_level, delayed);
3964}
3965
3966void tag_node::tprint(troff_output_file *out)
3967{
3968  if (delayed)
3969    out->add_to_tag_list(tag_string);
3970  else
3971    out->state.add_tag(out->fp, tag_string);
3972}
3973
3974int tag_node::same(node *nd)
3975{
3976  return tag_string == ((tag_node *)nd)->tag_string
3977	 && delayed == ((tag_node *)nd)->delayed;
3978}
3979
3980const char *tag_node::type()
3981{
3982  return "tag_node";
3983}
3984
3985int tag_node::force_tprint()
3986{
3987  return !delayed;
3988}
3989
3990int tag_node::is_tag()
3991{
3992  return !delayed;
3993}
3994
3995int tag_node::ends_sentence()
3996{
3997  return 2;
3998}
3999
4000int get_reg_int(const char *p)
4001{
4002  reg *r = (reg *)number_reg_dictionary.lookup(p);
4003  units prev_value;
4004  if (r && (r->get_value(&prev_value)))
4005    return (int)prev_value;
4006  else
4007    warning(WARN_REG, "number register `%1' not defined", p);
4008  return 0;
4009}
4010
4011const char *get_reg_str(const char *p)
4012{
4013  reg *r = (reg *)number_reg_dictionary.lookup(p);
4014  if (r)
4015    return r->get_string();
4016  else
4017    warning(WARN_REG, "register `%1' not defined", p);
4018  return 0;
4019}
4020
4021void suppress_node::put(troff_output_file *out, const char *s)
4022{
4023  int i = 0;
4024  while (s[i] != (char)0) {
4025    out->special_char(s[i]);
4026    i++;
4027  }
4028}
4029
4030/*
4031 *  We need to remember the start of the image and its name.
4032 */
4033
4034static char last_position = 0;
4035static const char *last_image_filename = 0;
4036static int last_image_id = 0;
4037
4038inline int min(int a, int b)
4039{
4040  return a < b ? a : b;
4041}
4042
4043/*
4044 *  tprint - if (is_on == 2)
4045 *               remember current position (l, r, c, i) and filename
4046 *           else
4047 *               if (emit_limits)
4048 *                   if (html)
4049 *                      emit image tag
4050 *                   else
4051 *                      emit postscript bounds for image
4052 *               else
4053 *                  if (suppress boolean differs from current state)
4054 *                      alter state
4055 *                  reset registers
4056 *                  record current page
4057 *                  set low water mark.
4058 */
4059
4060void suppress_node::tprint(troff_output_file *out)
4061{
4062  int current_page = topdiv->get_page_number();
4063  // firstly check to see whether this suppress node contains
4064  // an image filename & position.
4065  if (is_on == 2) {
4066    // remember position and filename
4067    last_position = position;
4068    char *tem = (char *)last_image_filename;
4069    last_image_filename = strsave(filename.contents());
4070    if (tem)
4071      a_delete tem;
4072    last_image_id = image_id;
4073    // printf("start of image and page = %d\n", current_page);
4074  }
4075  else {
4076    // now check whether the suppress node requires us to issue limits.
4077    if (emit_limits) {
4078      char name[8192];
4079      // remember that the filename will contain a %d in which the
4080      // last_image_id is placed
4081      if (last_image_filename == (char *) 0)
4082	*name = '\0';
4083      else
4084	sprintf(name, last_image_filename, last_image_id);
4085      if (is_html) {
4086	switch (last_position) {
4087	case 'c':
4088	  out->start_special();
4089	  put(out, "devtag:.centered-image");
4090	  break;
4091	case 'r':
4092	  out->start_special();
4093	  put(out, "devtag:.right-image");
4094	  break;
4095	case 'l':
4096	  out->start_special();
4097	  put(out, "devtag:.left-image");
4098	  break;
4099	case 'i':
4100	  ;
4101	default:
4102	  ;
4103	}
4104	out->end_special();
4105	out->start_special();
4106	put(out, "devtag:.auto-image ");
4107	put(out, name);
4108	out->end_special();
4109      }
4110      else {
4111	// postscript (or other device)
4112	if (suppress_start_page > 0 && current_page != suppress_start_page)
4113	  error("suppression limit registers span more than one page;\n"
4114	        "image description %1 will be wrong", image_no);
4115	// if (topdiv->get_page_number() != suppress_start_page)
4116	//  fprintf(stderr, "end of image and topdiv page = %d   and  suppress_start_page = %d\n",
4117	//	  topdiv->get_page_number(), suppress_start_page);
4118
4119	// remember that the filename will contain a %d in which the
4120	// image_no is placed
4121	fprintf(stderr,
4122		"grohtml-info:page %d  %d  %d  %d  %d  %d  %s  %d  %d  %s\n",
4123		topdiv->get_page_number(),
4124		get_reg_int("opminx"), get_reg_int("opminy"),
4125		get_reg_int("opmaxx"), get_reg_int("opmaxy"),
4126		// page offset + line length
4127		get_reg_int(".o") + get_reg_int(".l"),
4128		name, hresolution, vresolution, get_reg_str(".F"));
4129	fflush(stderr);
4130      }
4131    }
4132    else {
4133      if (is_on) {
4134	out->on();
4135	// lastly we reset the output registers
4136	reset_output_registers();
4137      }
4138      else
4139	out->off();
4140      suppress_start_page = current_page;
4141    }
4142  }
4143}
4144
4145int suppress_node::force_tprint()
4146{
4147  return is_on;
4148}
4149
4150int suppress_node::is_tag()
4151{
4152  return is_on;
4153}
4154
4155hunits suppress_node::width()
4156{
4157  return H0;
4158}
4159
4160/* composite_node */
4161
4162class composite_node : public charinfo_node {
4163  node *n;
4164  tfont *tf;
4165public:
4166  composite_node(node *, charinfo *, tfont *, statem *, int, node * = 0);
4167  ~composite_node();
4168  node *copy();
4169  hunits width();
4170  node *last_char_node();
4171  units size();
4172  void tprint(troff_output_file *);
4173  hyphenation_type get_hyphenation_type();
4174  void ascii_print(ascii_output_file *);
4175  void asciify(macro *);
4176  hyphen_list *get_hyphen_list(hyphen_list *, int *);
4177  node *add_self(node *, hyphen_list **);
4178  tfont *get_tfont();
4179  int same(node *);
4180  const char *type();
4181  int force_tprint();
4182  int is_tag();
4183  void vertical_extent(vunits *, vunits *);
4184  vunits vertical_width();
4185};
4186
4187composite_node::composite_node(node *p, charinfo *c, tfont *t, statem *s,
4188			       int pop, node *x)
4189: charinfo_node(c, s, pop, x), n(p), tf(t)
4190{
4191}
4192
4193composite_node::~composite_node()
4194{
4195  delete_node_list(n);
4196}
4197
4198node *composite_node::copy()
4199{
4200  return new composite_node(copy_node_list(n), ci, tf, state, div_nest_level);
4201}
4202
4203hunits composite_node::width()
4204{
4205  hunits x;
4206  if (tf->get_constant_space(&x))
4207    return x;
4208  x = H0;
4209  for (node *tem = n; tem; tem = tem->next)
4210    x += tem->width();
4211  hunits offset;
4212  if (tf->get_bold(&offset))
4213    x += offset;
4214  x += tf->get_track_kern();
4215  return x;
4216}
4217
4218node *composite_node::last_char_node()
4219{
4220  return this;
4221}
4222
4223vunits composite_node::vertical_width()
4224{
4225  vunits v = V0;
4226  for (node *tem = n; tem; tem = tem->next)
4227    v += tem->vertical_width();
4228  return v;
4229}
4230
4231units composite_node::size()
4232{
4233  return tf->get_size().to_units();
4234}
4235
4236hyphenation_type composite_node::get_hyphenation_type()
4237{
4238  return HYPHEN_MIDDLE;
4239}
4240
4241void composite_node::asciify(macro *m)
4242{
4243  unsigned char c = ci->get_asciify_code();
4244  if (c == 0)
4245    c = ci->get_ascii_code();
4246  if (c != 0) {
4247    m->append(c);
4248    delete this;
4249  }
4250  else
4251    m->append(this);
4252}
4253
4254void composite_node::ascii_print(ascii_output_file *ascii)
4255{
4256  unsigned char c = ci->get_ascii_code();
4257  if (c != 0)
4258    ascii->outc(c);
4259  else
4260    ascii->outs(ci->nm.contents());
4261
4262}
4263
4264hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail, int *count)
4265{
4266  (*count)++;
4267  return new hyphen_list(ci->get_hyphenation_code(), tail);
4268}
4269
4270node *composite_node::add_self(node *nn, hyphen_list **p)
4271{
4272  assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
4273  next = nn;
4274  nn = this;
4275  if ((*p)->hyphen)
4276    nn = nn->add_discretionary_hyphen();
4277  hyphen_list *pp = *p;
4278  *p = (*p)->next;
4279  delete pp;
4280  return nn;
4281}
4282
4283tfont *composite_node::get_tfont()
4284{
4285  return tf;
4286}
4287
4288node *reverse_node_list(node *n)
4289{
4290  node *r = 0;
4291  while (n) {
4292    node *tem = n;
4293    n = n->next;
4294    tem->next = r;
4295    r = tem;
4296  }
4297  return r;
4298}
4299
4300void composite_node::vertical_extent(vunits *minimum, vunits *maximum)
4301{
4302  n = reverse_node_list(n);
4303  node_list_vertical_extent(n, minimum, maximum);
4304  n = reverse_node_list(n);
4305}
4306
4307width_list::width_list(hunits w, hunits s)
4308: width(w), sentence_width(s), next(0)
4309{
4310}
4311
4312width_list::width_list(width_list *w)
4313: width(w->width), sentence_width(w->sentence_width), next(0)
4314{
4315}
4316
4317word_space_node::word_space_node(hunits d, color *c, width_list *w, node *x)
4318: space_node(d, c, x), orig_width(w), unformat(0)
4319{
4320}
4321
4322word_space_node::word_space_node(hunits d, int s, color *c, width_list *w,
4323				 int flag, statem *st, int pop, node *x)
4324: space_node(d, s, 0, c, st, pop, x), orig_width(w), unformat(flag)
4325{
4326}
4327
4328word_space_node::~word_space_node()
4329{
4330  width_list *w = orig_width;
4331  while (w != 0) {
4332    width_list *tmp = w;
4333    w = w->next;
4334    delete tmp;
4335  }
4336}
4337
4338node *word_space_node::copy()
4339{
4340  assert(orig_width != 0);
4341  width_list *w_old_curr = orig_width;
4342  width_list *w_new_curr = new width_list(w_old_curr);
4343  width_list *w_new = w_new_curr;
4344  w_old_curr = w_old_curr->next;
4345  while (w_old_curr != 0) {
4346    w_new_curr->next = new width_list(w_old_curr);
4347    w_new_curr = w_new_curr->next;
4348    w_old_curr = w_old_curr->next;
4349  }
4350  return new word_space_node(n, set, col, w_new, unformat, state,
4351			     div_nest_level);
4352}
4353
4354int word_space_node::set_unformat_flag()
4355{
4356  unformat = 1;
4357  return 1;
4358}
4359
4360void word_space_node::tprint(troff_output_file *out)
4361{
4362  out->fill_color(col);
4363  out->word_marker();
4364  out->right(n);
4365}
4366
4367int word_space_node::merge_space(hunits h, hunits sw, hunits ssw)
4368{
4369  n += h;
4370  assert(orig_width != 0);
4371  width_list *w = orig_width;
4372  for (; w->next; w = w->next)
4373    ;
4374  w->next = new width_list(sw, ssw);
4375  return 1;
4376}
4377
4378unbreakable_space_node::unbreakable_space_node(hunits d, color *c, node *x)
4379: word_space_node(d, c, 0, x)
4380{
4381}
4382
4383unbreakable_space_node::unbreakable_space_node(hunits d, int s,
4384					       color *c, statem *st, int pop,
4385					       node *x)
4386: word_space_node(d, s, c, 0, 0, st, pop, x)
4387{
4388}
4389
4390node *unbreakable_space_node::copy()
4391{
4392  return new unbreakable_space_node(n, set, col, state, div_nest_level);
4393}
4394
4395int unbreakable_space_node::force_tprint()
4396{
4397  return 0;
4398}
4399
4400int unbreakable_space_node::is_tag()
4401{
4402  return 0;
4403}
4404
4405breakpoint *unbreakable_space_node::get_breakpoints(hunits, int,
4406						    breakpoint *rest, int)
4407{
4408  return rest;
4409}
4410
4411int unbreakable_space_node::nbreaks()
4412{
4413  return 0;
4414}
4415
4416void unbreakable_space_node::split(int, node **, node **)
4417{
4418  assert(0);
4419}
4420
4421int unbreakable_space_node::merge_space(hunits, hunits, hunits)
4422{
4423  return 0;
4424}
4425
4426hvpair::hvpair()
4427{
4428}
4429
4430draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4431		     color *gc, color *fc)
4432: npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4433{
4434  point = new hvpair[npoints];
4435  for (int i = 0; i < npoints; i++)
4436    point[i] = p[i];
4437}
4438
4439draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4440		     color *gc, color *fc, statem *st, int pop)
4441: node(0, st, pop), npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4442{
4443  point = new hvpair[npoints];
4444  for (int i = 0; i < npoints; i++)
4445    point[i] = p[i];
4446}
4447
4448int draw_node::same(node *n)
4449{
4450  draw_node *nd = (draw_node *)n;
4451  if (code != nd->code || npoints != nd->npoints || sz != nd->sz
4452      || gcol != nd->gcol || fcol != nd->fcol)
4453    return 0;
4454  for (int i = 0; i < npoints; i++)
4455    if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v)
4456      return 0;
4457  return 1;
4458}
4459
4460const char *draw_node::type()
4461{
4462  return "draw_node";
4463}
4464
4465int draw_node::force_tprint()
4466{
4467  return 0;
4468}
4469
4470int draw_node::is_tag()
4471{
4472  return 0;
4473}
4474
4475draw_node::~draw_node()
4476{
4477  if (point)
4478    a_delete point;
4479}
4480
4481hunits draw_node::width()
4482{
4483  hunits x = H0;
4484  for (int i = 0; i < npoints; i++)
4485    x += point[i].h;
4486  return x;
4487}
4488
4489vunits draw_node::vertical_width()
4490{
4491  if (code == 'e')
4492    return V0;
4493  vunits x = V0;
4494  for (int i = 0; i < npoints; i++)
4495    x += point[i].v;
4496  return x;
4497}
4498
4499node *draw_node::copy()
4500{
4501  return new draw_node(code, point, npoints, sz, gcol, fcol, state,
4502		       div_nest_level);
4503}
4504
4505void draw_node::tprint(troff_output_file *out)
4506{
4507  out->draw(code, point, npoints, sz, gcol, fcol);
4508}
4509
4510/* tprint methods */
4511
4512void glyph_node::tprint(troff_output_file *out)
4513{
4514  tfont *ptf = tf->get_plain();
4515  if (ptf == tf)
4516    out->put_char_width(ci, ptf, gcol, fcol, width(), H0);
4517  else {
4518    hunits offset;
4519    int bold = tf->get_bold(&offset);
4520    hunits w = ptf->get_width(ci);
4521    hunits k = H0;
4522    hunits x;
4523    int cs = tf->get_constant_space(&x);
4524    if (cs) {
4525      x -= w;
4526      if (bold)
4527	x -= offset;
4528      hunits x2 = x/2;
4529      out->right(x2);
4530      k = x - x2;
4531    }
4532    else
4533      k = tf->get_track_kern();
4534    if (bold) {
4535      out->put_char(ci, ptf, gcol, fcol);
4536      out->right(offset);
4537    }
4538    out->put_char_width(ci, ptf, gcol, fcol, w, k);
4539  }
4540}
4541
4542void glyph_node::zero_width_tprint(troff_output_file *out)
4543{
4544  tfont *ptf = tf->get_plain();
4545  hunits offset;
4546  int bold = tf->get_bold(&offset);
4547  hunits x;
4548  int cs = tf->get_constant_space(&x);
4549  if (cs) {
4550    x -= ptf->get_width(ci);
4551    if (bold)
4552      x -= offset;
4553    x = x/2;
4554    out->right(x);
4555  }
4556  out->put_char(ci, ptf, gcol, fcol);
4557  if (bold) {
4558    out->right(offset);
4559    out->put_char(ci, ptf, gcol, fcol);
4560    out->right(-offset);
4561  }
4562  if (cs)
4563    out->right(-x);
4564}
4565
4566void break_char_node::tprint(troff_output_file *t)
4567{
4568  ch->tprint(t);
4569}
4570
4571void break_char_node::zero_width_tprint(troff_output_file *t)
4572{
4573  ch->zero_width_tprint(t);
4574}
4575
4576void hline_node::tprint(troff_output_file *out)
4577{
4578  if (x < H0) {
4579    out->right(x);
4580    x = -x;
4581  }
4582  if (n == 0) {
4583    out->right(x);
4584    return;
4585  }
4586  hunits w = n->width();
4587  if (w <= H0) {
4588    error("horizontal line drawing character must have positive width");
4589    out->right(x);
4590    return;
4591  }
4592  int i = int(x/w);
4593  if (i == 0) {
4594    hunits xx = x - w;
4595    hunits xx2 = xx/2;
4596    out->right(xx2);
4597    if (out->is_on())
4598      n->tprint(out);
4599    out->right(xx - xx2);
4600  }
4601  else {
4602    hunits rem = x - w*i;
4603    if (rem > H0) {
4604      if (n->overlaps_horizontally()) {
4605	if (out->is_on())
4606	  n->tprint(out);
4607	out->right(rem - w);
4608      } else
4609	out->right(rem);
4610    }
4611    while (--i >= 0)
4612      if (out->is_on())
4613	n->tprint(out);
4614  }
4615}
4616
4617void vline_node::tprint(troff_output_file *out)
4618{
4619  if (n == 0) {
4620    out->down(x);
4621    return;
4622  }
4623  vunits h = n->size();
4624  int overlaps = n->overlaps_vertically();
4625  vunits y = x;
4626  if (y < V0) {
4627    y = -y;
4628    int i = y / h;
4629    vunits rem = y - i*h;
4630    if (i == 0) {
4631      out->right(n->width());
4632      out->down(-rem);
4633    }
4634    else {
4635      while (--i > 0) {
4636	n->zero_width_tprint(out);
4637	out->down(-h);
4638      }
4639      if (overlaps) {
4640	n->zero_width_tprint(out);
4641	out->down(-rem);
4642	if (out->is_on())
4643	  n->tprint(out);
4644	out->down(-h);
4645      }
4646      else {
4647	if (out->is_on())
4648	  n->tprint(out);
4649	out->down(-h - rem);
4650      }
4651    }
4652  }
4653  else {
4654    int i = y / h;
4655    vunits rem = y - i*h;
4656    if (i == 0) {
4657      out->down(rem);
4658      out->right(n->width());
4659    }
4660    else {
4661      out->down(h);
4662      if (overlaps)
4663	n->zero_width_tprint(out);
4664      out->down(rem);
4665      while (--i > 0) {
4666	n->zero_width_tprint(out);
4667	out->down(h);
4668      }
4669      if (out->is_on())
4670	n->tprint(out);
4671    }
4672  }
4673}
4674
4675void zero_width_node::tprint(troff_output_file *out)
4676{
4677  if (!n)
4678    return;
4679  if (!n->next) {
4680    n->zero_width_tprint(out);
4681    return;
4682  }
4683  int hpos = out->get_hpos();
4684  int vpos = out->get_vpos();
4685  node *tem = n;
4686  while (tem) {
4687    tem->tprint(out);
4688    tem = tem->next;
4689  }
4690  out->moveto(hpos, vpos);
4691}
4692
4693void overstrike_node::tprint(troff_output_file *out)
4694{
4695  hunits pos = H0;
4696  for (node *tem = list; tem; tem = tem->next) {
4697    hunits x = (max_width - tem->width())/2;
4698    out->right(x - pos);
4699    pos = x;
4700    tem->zero_width_tprint(out);
4701  }
4702  out->right(max_width - pos);
4703}
4704
4705void bracket_node::tprint(troff_output_file *out)
4706{
4707  if (list == 0)
4708    return;
4709  int npieces = 0;
4710  node *tem;
4711  for (tem = list; tem; tem = tem->next)
4712    ++npieces;
4713  vunits h = list->size();
4714  vunits totalh = h*npieces;
4715  vunits y = (totalh - h)/2;
4716  out->down(y);
4717  for (tem = list; tem; tem = tem->next) {
4718    tem->zero_width_tprint(out);
4719    out->down(-h);
4720  }
4721  out->right(max_width);
4722  out->down(totalh - y);
4723}
4724
4725void node::tprint(troff_output_file *)
4726{
4727}
4728
4729void node::zero_width_tprint(troff_output_file *out)
4730{
4731  int hpos = out->get_hpos();
4732  int vpos = out->get_vpos();
4733  tprint(out);
4734  out->moveto(hpos, vpos);
4735}
4736
4737void space_node::tprint(troff_output_file *out)
4738{
4739  out->fill_color(col);
4740  out->right(n);
4741}
4742
4743void hmotion_node::tprint(troff_output_file *out)
4744{
4745  out->fill_color(col);
4746  out->right(n);
4747}
4748
4749void space_char_hmotion_node::tprint(troff_output_file *out)
4750{
4751  out->fill_color(col);
4752  if (is_html) {
4753    // we emit the space width as a negative glyph index
4754    out->flush_tbuf();
4755    out->do_motion();
4756    out->put('N');
4757    out->put(-n.to_units());
4758    out->put('\n');
4759  }
4760  out->right(n);
4761}
4762
4763void vmotion_node::tprint(troff_output_file *out)
4764{
4765  out->fill_color(col);
4766  out->down(n);
4767}
4768
4769void kern_pair_node::tprint(troff_output_file *out)
4770{
4771  n1->tprint(out);
4772  out->right(amount);
4773  n2->tprint(out);
4774}
4775
4776static void tprint_reverse_node_list(troff_output_file *out, node *n)
4777{
4778  if (n == 0)
4779    return;
4780  tprint_reverse_node_list(out, n->next);
4781  n->tprint(out);
4782}
4783
4784void dbreak_node::tprint(troff_output_file *out)
4785{
4786  tprint_reverse_node_list(out, none);
4787}
4788
4789void composite_node::tprint(troff_output_file *out)
4790{
4791  hunits bold_offset;
4792  int is_bold = tf->get_bold(&bold_offset);
4793  hunits track_kern = tf->get_track_kern();
4794  hunits constant_space;
4795  int is_constant_spaced = tf->get_constant_space(&constant_space);
4796  hunits x = H0;
4797  if (is_constant_spaced) {
4798    x = constant_space;
4799    for (node *tem = n; tem; tem = tem->next)
4800      x -= tem->width();
4801    if (is_bold)
4802      x -= bold_offset;
4803    hunits x2 = x/2;
4804    out->right(x2);
4805    x -= x2;
4806  }
4807  if (is_bold) {
4808    int hpos = out->get_hpos();
4809    int vpos = out->get_vpos();
4810    tprint_reverse_node_list(out, n);
4811    out->moveto(hpos, vpos);
4812    out->right(bold_offset);
4813  }
4814  tprint_reverse_node_list(out, n);
4815  if (is_constant_spaced)
4816    out->right(x);
4817  else
4818    out->right(track_kern);
4819}
4820
4821node *make_composite_node(charinfo *s, environment *env)
4822{
4823  int fontno = env_definite_font(env);
4824  if (fontno < 0) {
4825    error("no current font");
4826    return 0;
4827  }
4828  assert(fontno < font_table_size && font_table[fontno] != 0);
4829  node *n = charinfo_to_node_list(s, env);
4830  font_size fs = env->get_font_size();
4831  int char_height = env->get_char_height();
4832  int char_slant = env->get_char_slant();
4833  tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
4834					    fontno);
4835  if (env->is_composite())
4836    tf = tf->get_plain();
4837  return new composite_node(n, s, tf, 0, 0, 0);
4838}
4839
4840node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0)
4841{
4842  int fontno = env_definite_font(env);
4843  if (fontno < 0) {
4844    error("no current font");
4845    return 0;
4846  }
4847  assert(fontno < font_table_size && font_table[fontno] != 0);
4848  int fn = fontno;
4849  int found = font_table[fontno]->contains(s);
4850  if (!found) {
4851    macro *mac = s->get_macro();
4852    if (mac && s->is_fallback())
4853      return make_composite_node(s, env);
4854    if (s->numbered()) {
4855      if (!no_error_message)
4856	warning(WARN_CHAR, "can't find numbered character %1",
4857		s->get_number());
4858      return 0;
4859    }
4860    special_font_list *sf = font_table[fontno]->sf;
4861    while (sf != 0 && !found) {
4862      fn = sf->n;
4863      if (font_table[fn])
4864	found = font_table[fn]->contains(s);
4865      sf = sf->next;
4866    }
4867    if (!found) {
4868      symbol f = font_table[fontno]->get_name();
4869      string gl(f.contents());
4870      gl += ' ';
4871      gl += s->nm.contents();
4872      gl += '\0';
4873      charinfo *ci = get_charinfo(symbol(gl.contents()));
4874      if (ci && ci->get_macro())
4875	return make_composite_node(ci, env);
4876    }
4877    if (!found) {
4878      sf = global_special_fonts;
4879      while (sf != 0 && !found) {
4880	fn = sf->n;
4881	if (font_table[fn])
4882	  found = font_table[fn]->contains(s);
4883	sf = sf->next;
4884      }
4885    }
4886    if (!found)
4887      if (mac && s->is_special())
4888	return make_composite_node(s, env);
4889    if (!found) {
4890      for (fn = 0; fn < font_table_size; fn++)
4891	if (font_table[fn]
4892	    && font_table[fn]->is_special()
4893	    && font_table[fn]->contains(s)) {
4894	  found = 1;
4895	  break;
4896	}
4897    }
4898    if (!found) {
4899      if (!no_error_message && s->first_time_not_found()) {
4900	unsigned char input_code = s->get_ascii_code();
4901	if (input_code != 0) {
4902	  if (csgraph(input_code))
4903	    warning(WARN_CHAR, "can't find character `%1'", input_code);
4904	  else
4905	    warning(WARN_CHAR, "can't find character with input code %1",
4906		    int(input_code));
4907	}
4908	else if (s->nm.contents())
4909	  warning(WARN_CHAR, "can't find special character `%1'",
4910		  s->nm.contents());
4911      }
4912      return 0;
4913    }
4914  }
4915  font_size fs = env->get_font_size();
4916  int char_height = env->get_char_height();
4917  int char_slant = env->get_char_slant();
4918  tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fn);
4919  if (env->is_composite())
4920    tf = tf->get_plain();
4921  color *gcol = env->get_glyph_color();
4922  color *fcol = env->get_fill_color();
4923  return new glyph_node(s, tf, gcol, fcol, 0, 0);
4924}
4925
4926node *make_node(charinfo *ci, environment *env)
4927{
4928  switch (ci->get_special_translation()) {
4929  case charinfo::TRANSLATE_SPACE:
4930    return new space_char_hmotion_node(env->get_space_width(),
4931				       env->get_fill_color());
4932  case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4933    return new unbreakable_space_node(env->get_space_width(),
4934				      env->get_fill_color());
4935  case charinfo::TRANSLATE_DUMMY:
4936    return new dummy_node;
4937  case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4938    error("translation to \\% ignored in this context");
4939    break;
4940  }
4941  charinfo *tem = ci->get_translation();
4942  if (tem)
4943    ci = tem;
4944  macro *mac = ci->get_macro();
4945  if (mac && ci->is_normal())
4946    return make_composite_node(ci, env);
4947  else
4948    return make_glyph_node(ci, env);
4949}
4950
4951int character_exists(charinfo *ci, environment *env)
4952{
4953  if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
4954    return 1;
4955  charinfo *tem = ci->get_translation();
4956  if (tem)
4957    ci = tem;
4958  if (ci->get_macro())
4959    return 1;
4960  node *nd = make_glyph_node(ci, env, 1);
4961  if (nd) {
4962    delete nd;
4963    return 1;
4964  }
4965  return 0;
4966}
4967
4968node *node::add_char(charinfo *ci, environment *env,
4969		     hunits *widthp, int *spacep, node **glyph_comp_np)
4970{
4971  node *res;
4972  switch (ci->get_special_translation()) {
4973  case charinfo::TRANSLATE_SPACE:
4974    res = new space_char_hmotion_node(env->get_space_width(),
4975				      env->get_fill_color(), this);
4976    *widthp += res->width();
4977    return res;
4978  case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4979    res = new unbreakable_space_node(env->get_space_width(),
4980				     env->get_fill_color(), this);
4981    res->freeze_space();
4982    *widthp += res->width();
4983    *spacep += res->nspaces();
4984    return res;
4985  case charinfo::TRANSLATE_DUMMY:
4986    return new dummy_node(this);
4987  case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4988    return add_discretionary_hyphen();
4989  }
4990  charinfo *tem = ci->get_translation();
4991  if (tem)
4992    ci = tem;
4993  macro *mac = ci->get_macro();
4994  if (mac && ci->is_normal()) {
4995    res = make_composite_node(ci, env);
4996    if (res) {
4997      res->next = this;
4998      *widthp += res->width();
4999      if (glyph_comp_np)
5000	*glyph_comp_np = res;
5001    }
5002    else {
5003      if (glyph_comp_np)
5004	*glyph_comp_np = res;
5005      return this;
5006    }
5007  }
5008  else {
5009    node *gn = make_glyph_node(ci, env);
5010    if (gn == 0)
5011      return this;
5012    else {
5013      hunits old_width = width();
5014      node *p = gn->merge_self(this);
5015      if (p == 0) {
5016	*widthp += gn->width();
5017	gn->next = this;
5018	res = gn;
5019      }
5020      else {
5021	*widthp += p->width() - old_width;
5022	res = p;
5023      }
5024      if (glyph_comp_np)
5025	*glyph_comp_np = res;
5026    }
5027  }
5028  int break_code = 0;
5029  if (ci->can_break_before())
5030    break_code = 1;
5031  if (ci->can_break_after())
5032    break_code |= 2;
5033  if (break_code) {
5034    node *next1 = res->next;
5035    res->next = 0;
5036    res = new break_char_node(res, break_code, env->get_fill_color(), next1);
5037  }
5038  return res;
5039}
5040
5041#ifdef __GNUG__
5042inline
5043#endif
5044int same_node(node *n1, node *n2)
5045{
5046  if (n1 != 0) {
5047    if (n2 != 0)
5048      return n1->type() == n2->type() && n1->same(n2);
5049    else
5050      return 0;
5051  }
5052  else
5053    return n2 == 0;
5054}
5055
5056int same_node_list(node *n1, node *n2)
5057{
5058  while (n1 && n2) {
5059    if (n1->type() != n2->type() || !n1->same(n2))
5060      return 0;
5061    n1 = n1->next;
5062    n2 = n2->next;
5063  }
5064  return !n1 && !n2;
5065}
5066
5067int extra_size_node::same(node *nd)
5068{
5069  return n == ((extra_size_node *)nd)->n;
5070}
5071
5072const char *extra_size_node::type()
5073{
5074  return "extra_size_node";
5075}
5076
5077int extra_size_node::force_tprint()
5078{
5079  return 0;
5080}
5081
5082int extra_size_node::is_tag()
5083{
5084  return 0;
5085}
5086
5087int vertical_size_node::same(node *nd)
5088{
5089  return n == ((vertical_size_node *)nd)->n;
5090}
5091
5092const char *vertical_size_node::type()
5093{
5094  return "vertical_size_node";
5095}
5096
5097int vertical_size_node::set_unformat_flag()
5098{
5099  return 0;
5100}
5101
5102int vertical_size_node::force_tprint()
5103{
5104  return 0;
5105}
5106
5107int vertical_size_node::is_tag()
5108{
5109  return 0;
5110}
5111
5112int hmotion_node::same(node *nd)
5113{
5114  return n == ((hmotion_node *)nd)->n
5115	 && col == ((hmotion_node *)nd)->col;
5116}
5117
5118const char *hmotion_node::type()
5119{
5120  return "hmotion_node";
5121}
5122
5123int hmotion_node::set_unformat_flag()
5124{
5125  unformat = 1;
5126  return 1;
5127}
5128
5129int hmotion_node::force_tprint()
5130{
5131  return 0;
5132}
5133
5134int hmotion_node::is_tag()
5135{
5136  return 0;
5137}
5138
5139node *hmotion_node::add_self(node *nd, hyphen_list **p)
5140{
5141  next = nd;
5142  hyphen_list *pp = *p;
5143  *p = (*p)->next;
5144  delete pp;
5145  return this;
5146}
5147
5148hyphen_list *hmotion_node::get_hyphen_list(hyphen_list *tail, int *)
5149{
5150  return new hyphen_list(0, tail);
5151}
5152
5153int space_char_hmotion_node::same(node *nd)
5154{
5155  return n == ((space_char_hmotion_node *)nd)->n
5156	 && col == ((space_char_hmotion_node *)nd)->col;
5157}
5158
5159const char *space_char_hmotion_node::type()
5160{
5161  return "space_char_hmotion_node";
5162}
5163
5164int space_char_hmotion_node::force_tprint()
5165{
5166  return 0;
5167}
5168
5169int space_char_hmotion_node::is_tag()
5170{
5171  return 0;
5172}
5173
5174node *space_char_hmotion_node::add_self(node *nd, hyphen_list **p)
5175{
5176  next = nd;
5177  hyphen_list *pp = *p;
5178  *p = (*p)->next;
5179  delete pp;
5180  return this;
5181}
5182
5183hyphen_list *space_char_hmotion_node::get_hyphen_list(hyphen_list *tail,
5184						      int *)
5185{
5186  return new hyphen_list(0, tail);
5187}
5188
5189int vmotion_node::same(node *nd)
5190{
5191  return n == ((vmotion_node *)nd)->n
5192	 && col == ((vmotion_node *)nd)->col;
5193}
5194
5195const char *vmotion_node::type()
5196{
5197  return "vmotion_node";
5198}
5199
5200int vmotion_node::force_tprint()
5201{
5202  return 0;
5203}
5204
5205int vmotion_node::is_tag()
5206{
5207  return 0;
5208}
5209
5210int hline_node::same(node *nd)
5211{
5212  return x == ((hline_node *)nd)->x && same_node(n, ((hline_node *)nd)->n);
5213}
5214
5215const char *hline_node::type()
5216{
5217  return "hline_node";
5218}
5219
5220int hline_node::force_tprint()
5221{
5222  return 0;
5223}
5224
5225int hline_node::is_tag()
5226{
5227  return 0;
5228}
5229
5230int vline_node::same(node *nd)
5231{
5232  return x == ((vline_node *)nd)->x && same_node(n, ((vline_node *)nd)->n);
5233}
5234
5235const char *vline_node::type()
5236{
5237  return "vline_node";
5238}
5239
5240int vline_node::force_tprint()
5241{
5242  return 0;
5243}
5244
5245int vline_node::is_tag()
5246{
5247  return 0;
5248}
5249
5250int dummy_node::same(node * /*nd*/)
5251{
5252  return 1;
5253}
5254
5255const char *dummy_node::type()
5256{
5257  return "dummy_node";
5258}
5259
5260int dummy_node::force_tprint()
5261{
5262  return 0;
5263}
5264
5265int dummy_node::is_tag()
5266{
5267  return 0;
5268}
5269
5270int transparent_dummy_node::same(node * /*nd*/)
5271{
5272  return 1;
5273}
5274
5275const char *transparent_dummy_node::type()
5276{
5277  return "transparent_dummy_node";
5278}
5279
5280int transparent_dummy_node::force_tprint()
5281{
5282  return 0;
5283}
5284
5285int transparent_dummy_node::is_tag()
5286{
5287  return 0;
5288}
5289
5290int transparent_dummy_node::ends_sentence()
5291{
5292  return 2;
5293}
5294
5295int zero_width_node::same(node *nd)
5296{
5297  return same_node_list(n, ((zero_width_node *)nd)->n);
5298}
5299
5300const char *zero_width_node::type()
5301{
5302  return "zero_width_node";
5303}
5304
5305int zero_width_node::force_tprint()
5306{
5307  return 0;
5308}
5309
5310int zero_width_node::is_tag()
5311{
5312  return 0;
5313}
5314
5315int italic_corrected_node::same(node *nd)
5316{
5317  return (x == ((italic_corrected_node *)nd)->x
5318	  && same_node(n, ((italic_corrected_node *)nd)->n));
5319}
5320
5321const char *italic_corrected_node::type()
5322{
5323  return "italic_corrected_node";
5324}
5325
5326int italic_corrected_node::force_tprint()
5327{
5328  return 0;
5329}
5330
5331int italic_corrected_node::is_tag()
5332{
5333  return 0;
5334}
5335
5336left_italic_corrected_node::left_italic_corrected_node(node *xx)
5337: node(xx), n(0)
5338{
5339}
5340
5341left_italic_corrected_node::left_italic_corrected_node(statem *s, int pop,
5342						       node *xx)
5343: node(xx, s, pop), n(0)
5344{
5345}
5346
5347left_italic_corrected_node::~left_italic_corrected_node()
5348{
5349  delete n;
5350}
5351
5352node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn)
5353{
5354  if (n == 0) {
5355    hunits lic = gn->left_italic_correction();
5356    if (!lic.is_zero()) {
5357      x = lic;
5358      n = gn;
5359      return this;
5360    }
5361  }
5362  else {
5363    node *nd = n->merge_glyph_node(gn);
5364    if (nd) {
5365      n = nd;
5366      x = n->left_italic_correction();
5367      return this;
5368    }
5369  }
5370  return 0;
5371}
5372
5373node *left_italic_corrected_node::copy()
5374{
5375  left_italic_corrected_node *nd =
5376    new left_italic_corrected_node(state, div_nest_level);
5377  if (n) {
5378    nd->n = n->copy();
5379    nd->x = x;
5380  }
5381  return nd;
5382}
5383
5384void left_italic_corrected_node::tprint(troff_output_file *out)
5385{
5386  if (n) {
5387    out->right(x);
5388    n->tprint(out);
5389  }
5390}
5391
5392const char *left_italic_corrected_node::type()
5393{
5394  return "left_italic_corrected_node";
5395}
5396
5397int left_italic_corrected_node::force_tprint()
5398{
5399  return 0;
5400}
5401
5402int left_italic_corrected_node::is_tag()
5403{
5404  return 0;
5405}
5406
5407int left_italic_corrected_node::same(node *nd)
5408{
5409  return (x == ((left_italic_corrected_node *)nd)->x
5410	  && same_node(n, ((left_italic_corrected_node *)nd)->n));
5411}
5412
5413void left_italic_corrected_node::ascii_print(ascii_output_file *out)
5414{
5415  if (n)
5416    n->ascii_print(out);
5417}
5418
5419hunits left_italic_corrected_node::width()
5420{
5421  return n ? n->width() + x : H0;
5422}
5423
5424void left_italic_corrected_node::vertical_extent(vunits *minimum,
5425						 vunits *maximum)
5426{
5427  if (n)
5428    n->vertical_extent(minimum, maximum);
5429  else
5430    node::vertical_extent(minimum, maximum);
5431}
5432
5433hunits left_italic_corrected_node::skew()
5434{
5435  return n ? n->skew() + x/2 : H0;
5436}
5437
5438hunits left_italic_corrected_node::subscript_correction()
5439{
5440  return n ? n->subscript_correction() : H0;
5441}
5442
5443hunits left_italic_corrected_node::italic_correction()
5444{
5445  return n ? n->italic_correction() : H0;
5446}
5447
5448int left_italic_corrected_node::ends_sentence()
5449{
5450  return n ? n->ends_sentence() : 0;
5451}
5452
5453int left_italic_corrected_node::overlaps_horizontally()
5454{
5455  return n ? n->overlaps_horizontally() : 0;
5456}
5457
5458int left_italic_corrected_node::overlaps_vertically()
5459{
5460  return n ? n->overlaps_vertically() : 0;
5461}
5462
5463node *left_italic_corrected_node::last_char_node()
5464{
5465  return n ? n->last_char_node() : 0;
5466}
5467
5468tfont *left_italic_corrected_node::get_tfont()
5469{
5470  return n ? n->get_tfont() : 0;
5471}
5472
5473hyphenation_type left_italic_corrected_node::get_hyphenation_type()
5474{
5475  if (n)
5476    return n->get_hyphenation_type();
5477  else
5478    return HYPHEN_MIDDLE;
5479}
5480
5481hyphen_list *left_italic_corrected_node::get_hyphen_list(hyphen_list *tail,
5482							 int *count)
5483{
5484  return n ? n->get_hyphen_list(tail, count) : tail;
5485}
5486
5487node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
5488{
5489  if (n) {
5490    nd = new left_italic_corrected_node(state, div_nest_level, nd);
5491    nd = n->add_self(nd, p);
5492    n = 0;
5493    delete this;
5494  }
5495  return nd;
5496}
5497
5498int left_italic_corrected_node::character_type()
5499{
5500  return n ? n->character_type() : 0;
5501}
5502
5503int overstrike_node::same(node *nd)
5504{
5505  return same_node_list(list, ((overstrike_node *)nd)->list);
5506}
5507
5508const char *overstrike_node::type()
5509{
5510  return "overstrike_node";
5511}
5512
5513int overstrike_node::force_tprint()
5514{
5515  return 0;
5516}
5517
5518int overstrike_node::is_tag()
5519{
5520  return 0;
5521}
5522
5523node *overstrike_node::add_self(node *n, hyphen_list **p)
5524{
5525  next = n;
5526  hyphen_list *pp = *p;
5527  *p = (*p)->next;
5528  delete pp;
5529  return this;
5530}
5531
5532hyphen_list *overstrike_node::get_hyphen_list(hyphen_list *tail, int *)
5533{
5534  return new hyphen_list(0, tail);
5535}
5536
5537int bracket_node::same(node *nd)
5538{
5539  return same_node_list(list, ((bracket_node *)nd)->list);
5540}
5541
5542const char *bracket_node::type()
5543{
5544  return "bracket_node";
5545}
5546
5547int bracket_node::force_tprint()
5548{
5549  return 0;
5550}
5551
5552int bracket_node::is_tag()
5553{
5554  return 0;
5555}
5556
5557int composite_node::same(node *nd)
5558{
5559  return ci == ((composite_node *)nd)->ci
5560    && same_node_list(n, ((composite_node *)nd)->n);
5561}
5562
5563const char *composite_node::type()
5564{
5565  return "composite_node";
5566}
5567
5568int composite_node::force_tprint()
5569{
5570  return 0;
5571}
5572
5573int composite_node::is_tag()
5574{
5575  return 0;
5576}
5577
5578int glyph_node::same(node *nd)
5579{
5580  return ci == ((glyph_node *)nd)->ci
5581	 && tf == ((glyph_node *)nd)->tf
5582	 && gcol == ((glyph_node *)nd)->gcol
5583	 && fcol == ((glyph_node *)nd)->fcol;
5584}
5585
5586const char *glyph_node::type()
5587{
5588  return "glyph_node";
5589}
5590
5591int glyph_node::force_tprint()
5592{
5593  return 0;
5594}
5595
5596int glyph_node::is_tag()
5597{
5598  return 0;
5599}
5600
5601int ligature_node::same(node *nd)
5602{
5603  return (same_node(n1, ((ligature_node *)nd)->n1)
5604	  && same_node(n2, ((ligature_node *)nd)->n2)
5605	  && glyph_node::same(nd));
5606}
5607
5608const char *ligature_node::type()
5609{
5610  return "ligature_node";
5611}
5612
5613int ligature_node::force_tprint()
5614{
5615  return 0;
5616}
5617
5618int ligature_node::is_tag()
5619{
5620  return 0;
5621}
5622
5623int kern_pair_node::same(node *nd)
5624{
5625  return (amount == ((kern_pair_node *)nd)->amount
5626	  && same_node(n1, ((kern_pair_node *)nd)->n1)
5627	  && same_node(n2, ((kern_pair_node *)nd)->n2));
5628}
5629
5630const char *kern_pair_node::type()
5631{
5632  return "kern_pair_node";
5633}
5634
5635int kern_pair_node::force_tprint()
5636{
5637  return 0;
5638}
5639
5640int kern_pair_node::is_tag()
5641{
5642  return 0;
5643}
5644
5645int dbreak_node::same(node *nd)
5646{
5647  return (same_node_list(none, ((dbreak_node *)nd)->none)
5648	  && same_node_list(pre, ((dbreak_node *)nd)->pre)
5649	  && same_node_list(post, ((dbreak_node *)nd)->post));
5650}
5651
5652const char *dbreak_node::type()
5653{
5654  return "dbreak_node";
5655}
5656
5657int dbreak_node::force_tprint()
5658{
5659  return 0;
5660}
5661
5662int dbreak_node::is_tag()
5663{
5664  return 0;
5665}
5666
5667int break_char_node::same(node *nd)
5668{
5669  return break_code == ((break_char_node *)nd)->break_code
5670	 && col == ((break_char_node *)nd)->col
5671	 && same_node(ch, ((break_char_node *)nd)->ch);
5672}
5673
5674const char *break_char_node::type()
5675{
5676  return "break_char_node";
5677}
5678
5679int break_char_node::force_tprint()
5680{
5681  return 0;
5682}
5683
5684int break_char_node::is_tag()
5685{
5686  return 0;
5687}
5688
5689int line_start_node::same(node * /*nd*/)
5690{
5691  return 1;
5692}
5693
5694const char *line_start_node::type()
5695{
5696  return "line_start_node";
5697}
5698
5699int line_start_node::force_tprint()
5700{
5701  return 0;
5702}
5703
5704int line_start_node::is_tag()
5705{
5706  return 0;
5707}
5708
5709int space_node::same(node *nd)
5710{
5711  return n == ((space_node *)nd)->n
5712	      && set == ((space_node *)nd)->set
5713	      && col == ((space_node *)nd)->col;
5714}
5715
5716const char *space_node::type()
5717{
5718  return "space_node";
5719}
5720
5721int word_space_node::same(node *nd)
5722{
5723  return n == ((word_space_node *)nd)->n
5724	 && set == ((word_space_node *)nd)->set
5725	 && col == ((word_space_node *)nd)->col;
5726}
5727
5728const char *word_space_node::type()
5729{
5730  return "word_space_node";
5731}
5732
5733int word_space_node::force_tprint()
5734{
5735  return 0;
5736}
5737
5738int word_space_node::is_tag()
5739{
5740  return 0;
5741}
5742
5743void unbreakable_space_node::tprint(troff_output_file *out)
5744{
5745  out->fill_color(col);
5746  if (is_html) {
5747    // we emit the space width as a negative glyph index
5748    out->flush_tbuf();
5749    out->do_motion();
5750    out->put('N');
5751    out->put(-n.to_units());
5752    out->put('\n');
5753  }
5754  out->right(n);
5755}
5756
5757int unbreakable_space_node::same(node *nd)
5758{
5759  return n == ((unbreakable_space_node *)nd)->n
5760	 && set == ((unbreakable_space_node *)nd)->set
5761	 && col == ((unbreakable_space_node *)nd)->col;
5762}
5763
5764const char *unbreakable_space_node::type()
5765{
5766  return "unbreakable_space_node";
5767}
5768
5769node *unbreakable_space_node::add_self(node *nd, hyphen_list **p)
5770{
5771  next = nd;
5772  hyphen_list *pp = *p;
5773  *p = (*p)->next;
5774  delete pp;
5775  return this;
5776}
5777
5778hyphen_list *unbreakable_space_node::get_hyphen_list(hyphen_list *tail, int *)
5779{
5780  return new hyphen_list(0, tail);
5781}
5782
5783int diverted_space_node::same(node *nd)
5784{
5785  return n == ((diverted_space_node *)nd)->n;
5786}
5787
5788const char *diverted_space_node::type()
5789{
5790  return "diverted_space_node";
5791}
5792
5793int diverted_space_node::force_tprint()
5794{
5795  return 0;
5796}
5797
5798int diverted_space_node::is_tag()
5799{
5800  return 0;
5801}
5802
5803int diverted_copy_file_node::same(node *nd)
5804{
5805  return filename == ((diverted_copy_file_node *)nd)->filename;
5806}
5807
5808const char *diverted_copy_file_node::type()
5809{
5810  return "diverted_copy_file_node";
5811}
5812
5813int diverted_copy_file_node::force_tprint()
5814{
5815  return 0;
5816}
5817
5818int diverted_copy_file_node::is_tag()
5819{
5820  return 0;
5821}
5822
5823// Grow the font_table so that its size is > n.
5824
5825static void grow_font_table(int n)
5826{
5827  assert(n >= font_table_size);
5828  font_info **old_font_table = font_table;
5829  int old_font_table_size = font_table_size;
5830  font_table_size = font_table_size ? (font_table_size*3)/2 : 10;
5831  if (font_table_size <= n)
5832    font_table_size = n + 10;
5833  font_table = new font_info *[font_table_size];
5834  if (old_font_table_size)
5835    memcpy(font_table, old_font_table,
5836	   old_font_table_size*sizeof(font_info *));
5837  a_delete old_font_table;
5838  for (int i = old_font_table_size; i < font_table_size; i++)
5839    font_table[i] = 0;
5840}
5841
5842dictionary font_translation_dictionary(17);
5843
5844static symbol get_font_translation(symbol nm)
5845{
5846  void *p = font_translation_dictionary.lookup(nm);
5847  return p ? symbol((char *)p) : nm;
5848}
5849
5850dictionary font_dictionary(50);
5851
5852static int mount_font_no_translate(int n, symbol name, symbol external_name,
5853				   int check_only = 0)
5854{
5855  assert(n >= 0);
5856  // We store the address of this char in font_dictionary to indicate
5857  // that we've previously tried to mount the font and failed.
5858  static char a_char;
5859  font *fm = 0;
5860  void *p = font_dictionary.lookup(external_name);
5861  if (p == 0) {
5862    int not_found;
5863    fm = font::load_font(external_name.contents(), &not_found, check_only);
5864    if (check_only)
5865      return fm != 0;
5866    if (!fm) {
5867      if (not_found)
5868	warning(WARN_FONT, "can't find font `%1'", external_name.contents());
5869      (void)font_dictionary.lookup(external_name, &a_char);
5870      return 0;
5871    }
5872    (void)font_dictionary.lookup(name, fm);
5873  }
5874  else if (p == &a_char) {
5875#if 0
5876    error("invalid font `%1'", external_name.contents());
5877#endif
5878    return 0;
5879  }
5880  else
5881    fm = (font*)p;
5882  if (check_only)
5883    return 1;
5884  if (n >= font_table_size) {
5885    if (n - font_table_size > 1000) {
5886      error("font position too much larger than first unused position");
5887      return 0;
5888    }
5889    grow_font_table(n);
5890  }
5891  else if (font_table[n] != 0)
5892    delete font_table[n];
5893  font_table[n] = new font_info(name, n, external_name, fm);
5894  font_family::invalidate_fontno(n);
5895  return 1;
5896}
5897
5898int mount_font(int n, symbol name, symbol external_name)
5899{
5900  assert(n >= 0);
5901  name = get_font_translation(name);
5902  if (external_name.is_null())
5903    external_name = name;
5904  else
5905    external_name = get_font_translation(external_name);
5906  return mount_font_no_translate(n, name, external_name);
5907}
5908
5909int check_font(symbol fam, symbol name)
5910{
5911  if (check_style(name))
5912    name = concat(fam, name);
5913  return mount_font_no_translate(0, name, name, 1);
5914}
5915
5916int check_style(symbol s)
5917{
5918  int i = symbol_fontno(s);
5919  return i < 0 ? 0 : font_table[i]->is_style();
5920}
5921
5922void mount_style(int n, symbol name)
5923{
5924  assert(n >= 0);
5925  if (n >= font_table_size) {
5926    if (n - font_table_size > 1000) {
5927      error("font position too much larger than first unused position");
5928      return;
5929    }
5930    grow_font_table(n);
5931  }
5932  else if (font_table[n] != 0)
5933    delete font_table[n];
5934  font_table[n] = new font_info(get_font_translation(name), n, NULL_SYMBOL, 0);
5935  font_family::invalidate_fontno(n);
5936}
5937
5938/* global functions */
5939
5940void font_translate()
5941{
5942  symbol from = get_name(1);
5943  if (!from.is_null()) {
5944    symbol to = get_name();
5945    if (to.is_null() || from == to)
5946      font_translation_dictionary.remove(from);
5947    else
5948      (void)font_translation_dictionary.lookup(from, (void *)to.contents());
5949  }
5950  skip_line();
5951}
5952
5953void font_position()
5954{
5955  int n;
5956  if (get_integer(&n)) {
5957    if (n < 0)
5958      error("negative font position");
5959    else {
5960      symbol internal_name = get_name(1);
5961      if (!internal_name.is_null()) {
5962	symbol external_name = get_long_name();
5963	mount_font(n, internal_name, external_name); // ignore error
5964      }
5965    }
5966  }
5967  skip_line();
5968}
5969
5970font_family::font_family(symbol s)
5971: map_size(10), nm(s)
5972{
5973  map = new int[map_size];
5974  for (int i = 0; i < map_size; i++)
5975    map[i] = -1;
5976}
5977
5978font_family::~font_family()
5979{
5980  a_delete map;
5981}
5982
5983int font_family::make_definite(int i)
5984{
5985  if (i >= 0) {
5986    if (i < map_size && map[i] >= 0)
5987      return map[i];
5988    else {
5989      if (i < font_table_size && font_table[i] != 0) {
5990	if (i >= map_size) {
5991	  int old_map_size = map_size;
5992	  int *old_map = map;
5993	  map_size *= 3;
5994	  map_size /= 2;
5995	  if (i >= map_size)
5996	    map_size = i + 10;
5997	  map = new int[map_size];
5998	  memcpy(map, old_map, old_map_size*sizeof(int));
5999	  a_delete old_map;
6000	  for (int j = old_map_size; j < map_size; j++)
6001	    map[j] = -1;
6002	}
6003	if (font_table[i]->is_style()) {
6004	  symbol sty = font_table[i]->get_name();
6005	  symbol f = concat(nm, sty);
6006	  int n;
6007	  // don't use symbol_fontno, because that might return a style
6008	  // and because we don't want to translate the name
6009	  for (n = 0; n < font_table_size; n++)
6010	    if (font_table[n] != 0 && font_table[n]->is_named(f)
6011		&& !font_table[n]->is_style())
6012	      break;
6013	  if (n >= font_table_size) {
6014	    n = next_available_font_position();
6015	    if (!mount_font_no_translate(n, f, f))
6016	      return -1;
6017	  }
6018	  return map[i] = n;
6019	}
6020	else
6021	  return map[i] = i;
6022      }
6023      else
6024	return -1;
6025    }
6026  }
6027  else
6028    return -1;
6029}
6030
6031dictionary family_dictionary(5);
6032
6033font_family *lookup_family(symbol nm)
6034{
6035  font_family *f = (font_family *)family_dictionary.lookup(nm);
6036  if (!f) {
6037    f = new font_family(nm);
6038    (void)family_dictionary.lookup(nm, f);
6039  }
6040  return f;
6041}
6042
6043void font_family::invalidate_fontno(int n)
6044{
6045  assert(n >= 0 && n < font_table_size);
6046  dictionary_iterator iter(family_dictionary);
6047  symbol nam;
6048  font_family *fam;
6049  while (iter.get(&nam, (void **)&fam)) {
6050    int mapsize = fam->map_size;
6051    if (n < mapsize)
6052      fam->map[n] = -1;
6053    for (int i = 0; i < mapsize; i++)
6054      if (fam->map[i] == n)
6055	fam->map[i] = -1;
6056  }
6057}
6058
6059void style()
6060{
6061  int n;
6062  if (get_integer(&n)) {
6063    if (n < 0)
6064      error("negative font position");
6065    else {
6066      symbol internal_name = get_name(1);
6067      if (!internal_name.is_null())
6068	mount_style(n, internal_name);
6069    }
6070  }
6071  skip_line();
6072}
6073
6074static int get_fontno()
6075{
6076  int n;
6077  tok.skip();
6078  if (tok.delimiter()) {
6079    symbol s = get_name(1);
6080    if (!s.is_null()) {
6081      n = symbol_fontno(s);
6082      if (n < 0) {
6083	n = next_available_font_position();
6084	if (!mount_font(n, s))
6085	  return -1;
6086      }
6087      return curenv->get_family()->make_definite(n);
6088    }
6089  }
6090  else if (get_integer(&n)) {
6091    if (n < 0 || n >= font_table_size || font_table[n] == 0)
6092      error("bad font number");
6093    else
6094      return curenv->get_family()->make_definite(n);
6095  }
6096  return -1;
6097}
6098
6099static int underline_fontno = 2;
6100
6101void underline_font()
6102{
6103  int n = get_fontno();
6104  if (n >= 0)
6105    underline_fontno = n;
6106  skip_line();
6107}
6108
6109int get_underline_fontno()
6110{
6111  return underline_fontno;
6112}
6113
6114void define_font_special_character()
6115{
6116  int n = get_fontno();
6117  if (n < 0) {
6118    skip_line();
6119    return;
6120  }
6121  symbol f = font_table[n]->get_name();
6122  do_define_character(CHAR_FONT_SPECIAL, f.contents());
6123}
6124
6125void remove_font_special_character()
6126{
6127  int n = get_fontno();
6128  if (n < 0) {
6129    skip_line();
6130    return;
6131  }
6132  symbol f = font_table[n]->get_name();
6133  while (!tok.newline() && !tok.eof()) {
6134    if (!tok.space() && !tok.tab()) {
6135      charinfo *s = tok.get_char(1);
6136      string gl(f.contents());
6137      gl += ' ';
6138      gl += s->nm.contents();
6139      gl += '\0';
6140      charinfo *ci = get_charinfo(symbol(gl.contents()));
6141      if (!ci)
6142	break;
6143      macro *m = ci->set_macro(0);
6144      if (m)
6145	delete m;
6146    }
6147    tok.next();
6148  }
6149  skip_line();
6150}
6151
6152static void read_special_fonts(special_font_list **sp)
6153{
6154  special_font_list *s = *sp;
6155  *sp = 0;
6156  while (s != 0) {
6157    special_font_list *tem = s;
6158    s = s->next;
6159    delete tem;
6160  }
6161  special_font_list **p = sp;
6162  while (has_arg()) {
6163    int i = get_fontno();
6164    if (i >= 0) {
6165      special_font_list *tem = new special_font_list;
6166      tem->n = i;
6167      tem->next = 0;
6168      *p = tem;
6169      p = &(tem->next);
6170    }
6171  }
6172}
6173
6174void font_special_request()
6175{
6176  int n = get_fontno();
6177  if (n >= 0)
6178    read_special_fonts(&font_table[n]->sf);
6179  skip_line();
6180}
6181
6182void special_request()
6183{
6184  read_special_fonts(&global_special_fonts);
6185  skip_line();
6186}
6187
6188int next_available_font_position()
6189{
6190  int i;
6191  for (i = 1; i < font_table_size && font_table[i] != 0; i++)
6192    ;
6193  return i;
6194}
6195
6196int symbol_fontno(symbol s)
6197{
6198  s = get_font_translation(s);
6199  for (int i = 0; i < font_table_size; i++)
6200    if (font_table[i] != 0 && font_table[i]->is_named(s))
6201      return i;
6202  return -1;
6203}
6204
6205int is_good_fontno(int n)
6206{
6207  return n >= 0 && n < font_table_size && font_table[n] != 0;
6208}
6209
6210int get_bold_fontno(int n)
6211{
6212  if (n >= 0 && n < font_table_size && font_table[n] != 0) {
6213    hunits offset;
6214    if (font_table[n]->get_bold(&offset))
6215      return offset.to_units() + 1;
6216    else
6217      return 0;
6218  }
6219  else
6220    return 0;
6221}
6222
6223hunits env_digit_width(environment *env)
6224{
6225  node *n = make_glyph_node(charset_table['0'], env);
6226  if (n) {
6227    hunits x = n->width();
6228    delete n;
6229    return x;
6230  }
6231  else
6232    return H0;
6233}
6234
6235hunits env_space_width(environment *env)
6236{
6237  int fn = env_definite_font(env);
6238  font_size fs = env->get_font_size();
6239  if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6240    return scale(fs.to_units()/3, env->get_space_size(), 12);
6241  else
6242    return font_table[fn]->get_space_width(fs, env->get_space_size());
6243}
6244
6245hunits env_sentence_space_width(environment *env)
6246{
6247  int fn = env_definite_font(env);
6248  font_size fs = env->get_font_size();
6249  if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6250    return scale(fs.to_units()/3, env->get_sentence_space_size(), 12);
6251  else
6252    return font_table[fn]->get_space_width(fs, env->get_sentence_space_size());
6253}
6254
6255hunits env_half_narrow_space_width(environment *env)
6256{
6257  int fn = env_definite_font(env);
6258  font_size fs = env->get_font_size();
6259  if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6260    return 0;
6261  else
6262    return font_table[fn]->get_half_narrow_space_width(fs);
6263}
6264
6265hunits env_narrow_space_width(environment *env)
6266{
6267  int fn = env_definite_font(env);
6268  font_size fs = env->get_font_size();
6269  if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6270    return 0;
6271  else
6272    return font_table[fn]->get_narrow_space_width(fs);
6273}
6274
6275void bold_font()
6276{
6277  int n = get_fontno();
6278  if (n >= 0) {
6279    if (has_arg()) {
6280      if (tok.delimiter()) {
6281	int f = get_fontno();
6282	if (f >= 0) {
6283	  units offset;
6284	  if (has_arg() && get_number(&offset, 'u') && offset >= 1)
6285	    font_table[f]->set_conditional_bold(n, hunits(offset - 1));
6286	  else
6287	    font_table[f]->conditional_unbold(n);
6288	}
6289      }
6290      else {
6291	units offset;
6292	if (get_number(&offset, 'u') && offset >= 1)
6293	  font_table[n]->set_bold(hunits(offset - 1));
6294	else
6295	  font_table[n]->unbold();
6296      }
6297    }
6298    else
6299      font_table[n]->unbold();
6300  }
6301  skip_line();
6302}
6303
6304track_kerning_function::track_kerning_function() : non_zero(0)
6305{
6306}
6307
6308track_kerning_function::track_kerning_function(int min_s, hunits min_a,
6309					       int max_s, hunits max_a)
6310: non_zero(1), min_size(min_s), min_amount(min_a), max_size(max_s),
6311  max_amount(max_a)
6312{
6313}
6314
6315int track_kerning_function::operator==(const track_kerning_function &tk)
6316{
6317  if (non_zero)
6318    return (tk.non_zero
6319	    && min_size == tk.min_size
6320	    && min_amount == tk.min_amount
6321	    && max_size == tk.max_size
6322	    && max_amount == tk.max_amount);
6323  else
6324    return !tk.non_zero;
6325}
6326
6327int track_kerning_function::operator!=(const track_kerning_function &tk)
6328{
6329  if (non_zero)
6330    return (!tk.non_zero
6331	    || min_size != tk.min_size
6332	    || min_amount != tk.min_amount
6333	    || max_size != tk.max_size
6334	    || max_amount != tk.max_amount);
6335  else
6336    return tk.non_zero;
6337}
6338
6339hunits track_kerning_function::compute(int size)
6340{
6341  if (non_zero) {
6342    if (max_size <= min_size)
6343      return min_amount;
6344    else if (size <= min_size)
6345      return min_amount;
6346    else if (size >= max_size)
6347      return max_amount;
6348    else
6349      return (scale(max_amount, size - min_size, max_size - min_size)
6350	      + scale(min_amount, max_size - size, max_size - min_size));
6351  }
6352  else
6353    return H0;
6354}
6355
6356void track_kern()
6357{
6358  int n = get_fontno();
6359  if (n >= 0) {
6360    int min_s, max_s;
6361    hunits min_a, max_a;
6362    if (has_arg()
6363	&& get_number(&min_s, 'z')
6364	&& get_hunits(&min_a, 'p')
6365	&& get_number(&max_s, 'z')
6366	&& get_hunits(&max_a, 'p')) {
6367      track_kerning_function tk(min_s, min_a, max_s, max_a);
6368      font_table[n]->set_track_kern(tk);
6369    }
6370    else {
6371      track_kerning_function tk;
6372      font_table[n]->set_track_kern(tk);
6373    }
6374  }
6375  skip_line();
6376}
6377
6378void constant_space()
6379{
6380  int n = get_fontno();
6381  if (n >= 0) {
6382    int x, y;
6383    if (!has_arg() || !get_integer(&x))
6384      font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
6385    else {
6386      if (!has_arg() || !get_number(&y, 'z'))
6387	font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
6388      else
6389	font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
6390					  scale(y*x,
6391						units_per_inch,
6392						36*72*sizescale));
6393    }
6394  }
6395  skip_line();
6396}
6397
6398void ligature()
6399{
6400  int lig;
6401  if (has_arg() && get_integer(&lig) && lig >= 0 && lig <= 2)
6402    global_ligature_mode = lig;
6403  else
6404    global_ligature_mode = 1;
6405  skip_line();
6406}
6407
6408void kern_request()
6409{
6410  int k;
6411  if (has_arg() && get_integer(&k))
6412    global_kern_mode = k != 0;
6413  else
6414    global_kern_mode = 1;
6415  skip_line();
6416}
6417
6418void set_soft_hyphen_char()
6419{
6420  soft_hyphen_char = get_optional_char();
6421  if (!soft_hyphen_char)
6422    soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
6423  skip_line();
6424}
6425
6426void init_output()
6427{
6428  if (suppress_output_flag)
6429    the_output = new suppress_output_file;
6430  else if (ascii_output_flag)
6431    the_output = new ascii_output_file;
6432  else
6433    the_output = new troff_output_file;
6434}
6435
6436class next_available_font_position_reg : public reg {
6437public:
6438  const char *get_string();
6439};
6440
6441const char *next_available_font_position_reg::get_string()
6442{
6443  return i_to_a(next_available_font_position());
6444}
6445
6446class printing_reg : public reg {
6447public:
6448  const char *get_string();
6449};
6450
6451const char *printing_reg::get_string()
6452{
6453  if (the_output)
6454    return the_output->is_printing() ? "1" : "0";
6455  else
6456    return "0";
6457}
6458
6459void init_node_requests()
6460{
6461  init_request("bd", bold_font);
6462  init_request("cs", constant_space);
6463  init_request("fp", font_position);
6464  init_request("fschar", define_font_special_character);
6465  init_request("fspecial", font_special_request);
6466  init_request("ftr", font_translate);
6467  init_request("kern", kern_request);
6468  init_request("lg", ligature);
6469  init_request("rfschar", remove_font_special_character);
6470  init_request("shc", set_soft_hyphen_char);
6471  init_request("special", special_request);
6472  init_request("sty", style);
6473  init_request("tkf", track_kern);
6474  init_request("uf", underline_font);
6475  number_reg_dictionary.define(".fp", new next_available_font_position_reg);
6476  number_reg_dictionary.define(".kern",
6477			       new constant_int_reg(&global_kern_mode));
6478  number_reg_dictionary.define(".lg",
6479			       new constant_int_reg(&global_ligature_mode));
6480  number_reg_dictionary.define(".P", new printing_reg);
6481  soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
6482}
6483