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