home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.bin / groff / troff / env.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-30  |  64.3 KB  |  2,954 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
  3.      Written by James Clark (jjc@jclark.uucp)
  4.  
  5. This file is part of groff.
  6.  
  7. groff is free software; you can redistribute it and/or modify it under
  8. the terms of the GNU General Public License as published by the Free
  9. Software Foundation; either version 1, or (at your option) any later
  10. version.
  11.  
  12. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  13. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15. for more details.
  16.  
  17. You should have received a copy of the GNU General Public License along
  18. with groff; see the file LICENSE.  If not, write to the Free Software
  19. Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  20.  
  21. #include "groff.h"
  22. #include "symbol.h"
  23. #include "dictionary.h"
  24. #include "hvunits.h"
  25. #include "env.h"
  26. #include "request.h"
  27. #include "node.h"
  28. #include "token.h"
  29. #include "div.h"
  30. #include "reg.h"
  31. #include "charinfo.h"
  32. #include <math.h>
  33.  
  34. symbol default_family("T");
  35.  
  36. enum { ADJUST_LEFT = 0, ADJUST_BOTH = 1, ADJUST_CENTER = 3, ADJUST_RIGHT = 5 };
  37.  
  38. enum { HYPHEN_LAST_LINE = 2, HYPHEN_LAST_CHARS = 4, HYPHEN_FIRST_CHARS = 8 };
  39.  
  40. struct env_list {
  41.   environment *env;
  42.   env_list *next;
  43.   env_list(environment *e, env_list *p) : env(e), next(p) {}
  44. };
  45.  
  46. env_list *env_stack;
  47. const int NENVIRONMENTS = 10;
  48. environment *env_table[NENVIRONMENTS];
  49. dictionary env_dictionary(10);
  50. environment *curenv;
  51. static int next_line_number = 0;
  52.  
  53. charinfo *field_delimiter_char;
  54. charinfo *padding_indicator_char;
  55.  
  56.  
  57.  
  58. class pending_output_line {
  59.   node *nd;
  60.   int no_fill;
  61.   vunits vs;
  62.   int ls;
  63.   hunits width;
  64. #ifdef WIDOW_CONTROL
  65.   int last_line;        // Is it the last line of the paragraph?
  66. #endif /* WIDOW_CONTROL */
  67. public:
  68.   pending_output_line *next;
  69.  
  70.   pending_output_line(node *, int, vunits, int, hunits,
  71.               pending_output_line * = 0);
  72.   ~pending_output_line();
  73.   int output();
  74.  
  75. #ifdef WIDOW_CONTROL
  76.   friend void environment::mark_last_line();
  77.   friend void environment::output(node *, int, vunits, int, hunits);
  78. #endif /* WIDOW_CONTROL */
  79. };
  80.  
  81. pending_output_line::pending_output_line(node *n, int nf, vunits v, int l,
  82.                      hunits w, pending_output_line *p)
  83. : nd(n), no_fill(nf), vs(v), ls(l), width(w),
  84. #ifdef WIDOW_CONTROL
  85.   last_line(0),
  86. #endif /* WIDOW_CONTROL */
  87.   next(p)
  88. {
  89. }
  90.  
  91. pending_output_line::~pending_output_line()
  92. {
  93.   delete_node_list(nd);
  94. }
  95.  
  96. int pending_output_line::output()
  97. {
  98.   if (trap_sprung_flag)
  99.     return 0;
  100. #ifdef WIDOW_CONTROL
  101.   if (next && next->last_line && !no_fill) {
  102.     curdiv->need(vs*ls + vunits(vresolution));
  103.     if (trap_sprung_flag) {
  104.       next->last_line = 0;    // Try to avoid infinite loops.
  105.       return 0;
  106.     }
  107.   }
  108. #endif
  109.   curdiv->output(nd, no_fill, vs, ls, width);
  110.   nd = 0;
  111.   return 1;
  112. }
  113.  
  114. void environment::output(node *nd, int no_fill, vunits vs, int ls,
  115.              hunits width)
  116. {
  117. #ifdef WIDOW_CONTROL
  118.   while (pending_lines) {
  119.     if (widow_control && !pending_lines->no_fill && !pending_lines->next)
  120.       break;
  121.     if (!pending_lines->output())
  122.       break;
  123.     pending_output_line *tem = pending_lines;
  124.     pending_lines = pending_lines->next;
  125.     delete tem;
  126.   }
  127. #else /* WIDOW_CONTROL */
  128.   output_pending_lines();
  129. #endif /* WIDOW_CONTROL */
  130.   if (!trap_sprung_flag && !pending_lines
  131. #ifdef WIDOW_CONTROL
  132.       && (!widow_control || no_fill)
  133. #endif /* WIDOW_CONTROL */
  134.       )
  135.     curdiv->output(nd, no_fill, vs, ls, width);
  136.   else {
  137.     for (pending_output_line **p = &pending_lines; *p; p = &(*p)->next)
  138.       ;
  139.     *p = new pending_output_line(nd, no_fill, vs, ls, width);
  140.   }
  141. }
  142.  
  143. // a line from .tl goes at the head of the queue
  144.  
  145. void environment::output_title(node *nd, int no_fill, vunits vs, int ls,
  146.                    hunits width)
  147. {
  148.   if (!trap_sprung_flag)
  149.     curdiv->output(nd, no_fill, vs, ls, width);
  150.   else
  151.     pending_lines = new pending_output_line(nd, no_fill, vs, ls, width,
  152.                         pending_lines);
  153. }
  154.  
  155. void environment::output_pending_lines()
  156. {
  157.   while (pending_lines && pending_lines->output()) {
  158.     pending_output_line *tem = pending_lines;
  159.     pending_lines = pending_lines->next;
  160.     delete tem;
  161.   }
  162. }
  163.  
  164. #ifdef WIDOW_CONTROL
  165.  
  166. void environment::mark_last_line()
  167. {
  168.   if (!widow_control || !pending_lines)
  169.     return;
  170.   for (pending_output_line *p = pending_lines; p->next; p = p->next)
  171.     ;
  172.   if (!p->no_fill)
  173.     p->last_line = 1;
  174. }
  175.  
  176. void widow_control_request()
  177. {
  178.   if (has_arg()) {
  179.     int n;
  180.     if (get_integer(&n))
  181.       curenv->widow_control = n != 0;
  182.   }
  183.   else
  184.     curenv->widow_control = 1;
  185.   skip_line();
  186. }
  187.  
  188. #endif /* WIDOW_CONTROL */
  189.  
  190. /* font_size functions */
  191.  
  192. size_range *font_size::size_table = 0;
  193. int font_size::nranges = 0;
  194.  
  195. static int compare_ranges(void *p1, void *p2)
  196. {
  197.   return ((size_range *)p1)->min - ((size_range *)p2)->min;
  198. }
  199.  
  200. void font_size::init_size_table(int *sizes)
  201. {
  202.   nranges = 0;
  203.   while (sizes[nranges*2] != 0)
  204.     nranges++;
  205.   assert(nranges > 0);
  206.   size_table = new size_range[nranges];
  207.   for (int i = 0; i < nranges; i++) {
  208.     size_table[i].min = sizes[i*2];
  209.     size_table[i].max = sizes[i*2 + 1];
  210.   }
  211.   qsort(size_table, nranges, sizeof(size_range), compare_ranges);
  212. }
  213.  
  214. font_size::font_size(int sp)
  215. {
  216.   for (int i = 0; i < nranges; i++) {
  217.     if (sp < size_table[i].min) {
  218.       if (i > 0 && size_table[i].min - sp >= sp - size_table[i - 1].max)
  219.     p = size_table[i - 1].max;
  220.       else
  221.     p = size_table[i].min;
  222.       return;
  223.     }
  224.     if (sp <= size_table[i].max) {
  225.       p = sp;
  226.       return;
  227.     }
  228.   }
  229.   p = size_table[nranges - 1].max;
  230. }
  231.  
  232. int font_size::to_units()
  233. {
  234.   return scale(p, units_per_inch, sizescale*72);
  235. }
  236.  
  237. // we can't do this in a static constructor because various dictionaries
  238. // have to get initialized first
  239.  
  240. void init_environments()
  241. {
  242.   curenv = env_table[0] = new environment("0");
  243. }
  244.  
  245. void tab_character()
  246. {
  247.   curenv->tab_char = get_optional_char();
  248.   skip_line();
  249. }
  250.  
  251. void leader_character()
  252. {
  253.   curenv->leader_char = get_optional_char();
  254.   skip_line();
  255. }
  256.  
  257. void environment::add_char(charinfo *ci)
  258. {
  259.   if (interrupted)
  260.     ;
  261.   // don't allow fields in dummy environments
  262.   else if (ci == field_delimiter_char && !dummy) {
  263.     if (current_field)
  264.       wrap_up_field();
  265.     else
  266.       start_field();
  267.   }
  268.   else if (current_field && ci == padding_indicator_char) {
  269.     if (current_tab) {
  270.       tab_contents = new space_node(H0, tab_contents);
  271.       tab_field_spaces++;
  272.     }
  273.     else {
  274.       line = new space_node(H0, line);
  275.       field_spaces++;
  276.     }
  277.   }
  278.   else if (current_tab) {
  279.     if (tab_contents == 0)
  280.       tab_contents = new line_start_node;
  281.     if (ci != hyphen_indicator_char)
  282.       tab_contents = tab_contents->add_char(ci, this, &tab_width);
  283.     else
  284.       tab_contents = tab_contents->add_discretionary_hyphen();
  285.   }
  286.   else {
  287.     if (line == 0)
  288.       start_line();
  289.     if (ci != hyphen_indicator_char)
  290.       line = line->add_char(ci, this, &width_total);
  291.     else
  292.       line = line->add_discretionary_hyphen();
  293.   }
  294. }
  295.  
  296. node *environment::make_char_node(charinfo *ci)
  297. {
  298.   return make_node(ci, this);
  299. }
  300.  
  301. void environment::add_node(node *n)
  302. {
  303.   assert(n != 0);
  304.   if (current_tab || current_field)
  305.     n->freeze_space();
  306.   if (interrupted) {
  307.     delete n;
  308.   }
  309.   else if (current_tab) {
  310.     n->next = tab_contents;
  311.     tab_contents = n;
  312.     tab_width += n->width();
  313.   }
  314.   else {
  315.     if (line == 0) {
  316.       if (discarding && n->discardable()) {
  317.     delete n;
  318.     return;
  319.       }
  320.       start_line();
  321.     }
  322.     width_total += n->width();
  323.     space_total += n->nspaces();
  324.     n->next = line;
  325.     line = n;
  326.   }
  327. }
  328.  
  329.  
  330. void environment::add_hyphen_indicator()
  331. {
  332.   if (current_tab || interrupted || current_field
  333.       || hyphen_indicator_char != 0)
  334.     return;
  335.   if (line == 0)
  336.     start_line();
  337.   line = line->add_discretionary_hyphen();
  338. }
  339.  
  340. int environment::get_hyphenation_flags()
  341. {
  342.   return hyphenation_flags;
  343. }
  344.  
  345. int environment::get_hyphen_line_max()
  346. {
  347.   return hyphen_line_max;
  348. }
  349.  
  350. int environment::get_hyphen_line_count()
  351. {
  352.   return hyphen_line_count;
  353. }
  354.  
  355. int environment::get_center_lines()
  356. {
  357.   return center_lines;
  358. }
  359.  
  360. int environment::get_right_justify_lines()
  361. {
  362.   return right_justify_lines;
  363. }
  364.  
  365. void environment::add_italic_correction()
  366. {
  367.   if (current_tab) {
  368.     if (tab_contents)
  369.       tab_contents = tab_contents->add_italic_correction(&tab_width);
  370.   }
  371.   else if (line)
  372.     line = line->add_italic_correction(&width_total);
  373. }
  374.  
  375. int environment::get_space_size()
  376. {
  377.   return space_size;
  378. }
  379.  
  380. int environment::get_sentence_space_size()
  381. {
  382.   return sentence_space_size;
  383. }
  384.  
  385. hunits environment::get_space_width()
  386.   return scale(env_space_width(this), space_size, 12);
  387. }
  388.  
  389. hunits environment::get_narrow_space_width()
  390. {
  391.   return env_narrow_space_width(this);
  392. }
  393.  
  394. hunits environment::get_half_narrow_space_width()
  395. {
  396.   return env_half_narrow_space_width(this);
  397. }
  398.  
  399. void environment::space_newline()
  400. {
  401.   assert(!current_tab && !current_field);
  402.   if (interrupted)
  403.     return;
  404.   int ss = space_size;
  405.   if (node_list_ends_sentence(line) == 1)
  406.     ss += sentence_space_size;
  407.   hunits x = scale(env_space_width(this), ss, 12);
  408.   if (line != 0 && line->merge_space(x)) {
  409.     width_total += x;
  410.     return;
  411.   }
  412.   add_node(new word_space_node(x));
  413.   possibly_break_line(spread_flag);
  414.   spread_flag = 0;
  415. }
  416.  
  417. void environment::space()
  418. {
  419.   if (interrupted)
  420.     return;
  421.   if (current_field && padding_indicator_char == 0) {
  422.     node **pp = current_tab ? &tab_contents : &line;
  423.     int *hp = current_tab ? &tab_field_spaces : &field_spaces;
  424.     *pp = new space_node(H0, *pp);
  425.     *hp += 1;
  426.     return;
  427.   }
  428.   hunits x = scale(env_space_width(this), space_size, 12);
  429.   node *p = current_tab ? tab_contents : line;
  430.   hunits *tp = current_tab ? &tab_width : &width_total;
  431.   if (p && p->nspaces() == 1 && p->width() == x
  432.       && node_list_ends_sentence(p->next) == 1) {
  433.     hunits xx = scale(env_space_width(this), sentence_space_size, 12);
  434.     if (p->merge_space(xx)) {
  435.       *tp += xx;
  436.       return;
  437.     }
  438.   }
  439.   if (p && p->merge_space(x)) {
  440.     *tp += x;
  441.     return;
  442.   }
  443.   add_node(new word_space_node(x));
  444.   possibly_break_line(spread_flag);
  445.   spread_flag = 0;
  446. }
  447.  
  448. void environment::set_font(symbol nm)
  449. {
  450.   if (interrupted)
  451.     return;
  452.   if (nm == symbol("P")) {
  453.     if (family->make_definite(prev_fontno) < 0)
  454.       return;
  455.     int tem = fontno;
  456.     fontno = prev_fontno;
  457.     prev_fontno = tem;
  458.   }
  459.   else {
  460.     int n = symbol_fontno(nm);
  461.     if (n < 0) {
  462.       n = next_available_font_position();
  463.       if (!mount_font(n, nm))
  464.     return;
  465.     }
  466.     if (family->make_definite(n) < 0)
  467.       return;
  468.     prev_fontno = fontno;
  469.     fontno = n;
  470.   }
  471. }
  472.  
  473. void environment::set_font(int n)
  474. {
  475.   if (interrupted)
  476.     return;
  477.   if (is_good_fontno(n)) {
  478.     prev_fontno = fontno;
  479.     fontno = n;
  480.   }
  481.   else
  482.     error("bad font number");
  483. }
  484.  
  485. void environment::set_family(symbol fam)
  486. {
  487.   if (fam.is_null()) {
  488.     if (prev_family->make_definite(fontno) < 0)
  489.       return;
  490.     font_family *tem = family;
  491.     family = prev_family;
  492.     prev_family = tem;
  493.   }
  494.   else {
  495.     font_family *f = lookup_family(fam);
  496.     if (f->make_definite(fontno) < 0)
  497.       return;
  498.     prev_family = family;
  499.     family = f;
  500.   }
  501. }
  502.  
  503. void environment::set_size(int n)
  504. {
  505.   if (interrupted)
  506.     return;
  507.   if (n == 0) {
  508.     font_size temp = prev_size;
  509.     prev_size = size;
  510.     size = temp;
  511.     int temp2 = prev_requested_size;
  512.     prev_requested_size = requested_size;
  513.     requested_size = temp2;
  514.   }
  515.   else {
  516.     prev_size = size;
  517.     size = font_size(n);
  518.     prev_requested_size = requested_size;
  519.     requested_size = n;
  520.   }
  521. }
  522.  
  523. void environment::set_char_height(int n)
  524. {
  525.   if (interrupted)
  526.     return;
  527.   if (n == requested_size || n <= 0)
  528.     char_height = 0;
  529.   else
  530.     char_height = n;
  531. }
  532.  
  533. void environment::set_char_slant(int n)
  534. {
  535.   if (interrupted)
  536.     return;
  537.   char_slant = n;
  538. }
  539.  
  540. environment::environment(symbol nm)
  541. : name(nm),
  542.   prev_line_length((units_per_inch*13)/2),
  543.   line_length((units_per_inch*13)/2),
  544.   prev_title_length((units_per_inch*13)/2),
  545.   title_length((units_per_inch*13)/2),
  546.   prev_size(sizescale*10),
  547.   size(sizescale*10),
  548.   requested_size(sizescale*10),
  549.   prev_requested_size(sizescale*10),
  550.   char_height(0),
  551.   char_slant(0),
  552.   space_size(12),
  553.   sentence_space_size(12),
  554.   adjust_mode(ADJUST_BOTH),
  555.   fill(1),
  556.   interrupted(0),
  557.   prev_line_interrupted(0),
  558.   center_lines(0),
  559.   right_justify_lines(0),
  560.   prev_vertical_spacing(points_to_units(12)),
  561.   vertical_spacing(points_to_units(12)),
  562.   prev_line_spacing(1),
  563.   line_spacing(1),
  564.   prev_indent(0),
  565.   indent(0),
  566.   have_temporary_indent(0),
  567.   temporary_indent(0),
  568.   underline_lines(0),
  569.   input_trap_count(0),
  570.   prev_text_length(0),
  571.   width_total(0),
  572.   space_total(0),
  573.   input_line_start(0),
  574.   control_char('.'),
  575.   no_break_control_char('\''),
  576.   hyphen_indicator_char(0),
  577.   spread_flag(0),
  578.   line(0),
  579.   pending_lines(0),
  580.   discarding(0),
  581.   tabs(units_per_inch/2, TAB_LEFT),
  582.   current_tab(TAB_NONE),
  583.   current_field(0),
  584.   margin_character_node(0),
  585.   margin_character_distance(points_to_units(10)),
  586.   numbering_nodes(0),
  587.   number_text_separation(1),
  588.   line_number_multiple(1),
  589.   line_number_indent(0),
  590.   no_number_count(0),
  591.   tab_char(0),
  592.   leader_char(charset_table['.']),
  593.   hyphenation_flags(1),
  594.   dummy(0),
  595.   leader_node(0),
  596. #ifdef WIDOW_CONTROL
  597.   widow_control(0),
  598. #endif /* WIDOW_CONTROL */
  599.   hyphen_line_count(0),
  600.   hyphen_line_max(-1),
  601.   hyphenation_space(H0),
  602.   hyphenation_margin(H0)
  603. {
  604.   prev_family = family = lookup_family(default_family);
  605.   prev_fontno = fontno = 1;
  606.   if (!is_good_fontno(1))
  607.     fatal("font number 1 not a valid font");
  608.   if (family->make_definite(1) < 0)
  609.     fatal("invalid default family `%1'", default_family.contents());
  610.   prev_fontno = fontno;
  611. }
  612.  
  613. environment::environment(const environment *e)
  614. : name(e->name),        // so that eg `.if "\n[.ev]"0"' works
  615.   prev_line_length(e->prev_line_length),
  616.   line_length(e->line_length),
  617.   prev_title_length(e->prev_title_length),
  618.   title_length(e->title_length),
  619.   prev_size(e->prev_size),
  620.   size(e->size),
  621.   prev_requested_size(e->prev_requested_size),
  622.   requested_size(e->requested_size),
  623.   char_height(e->char_height),
  624.   char_slant(e->char_slant),
  625.   space_size(e->space_size),
  626.   sentence_space_size(e->sentence_space_size),
  627.   adjust_mode(e->adjust_mode),
  628.   fill(e->fill),
  629.   interrupted(0),
  630.   prev_line_interrupted(0),
  631.   center_lines(0),
  632.   right_justify_lines(0),
  633.   prev_vertical_spacing(e->prev_vertical_spacing),
  634.   vertical_spacing(e->vertical_spacing),
  635.   prev_line_spacing(e->prev_line_spacing),
  636.   line_spacing(e->line_spacing),
  637.   prev_indent(e->prev_indent),
  638.   indent(e->indent),
  639.   have_temporary_indent(0),
  640.   temporary_indent(0),
  641.   underline_lines(0),
  642.   input_trap_count(0),
  643.   prev_text_length(e->prev_text_length),
  644.   width_total(0),
  645.   space_total(0),
  646.   input_line_start(0),
  647.   control_char(e->control_char),
  648.   no_break_control_char(e->no_break_control_char),
  649.   hyphen_indicator_char(e->hyphen_indicator_char),
  650.   spread_flag(0),
  651.   line(0),
  652.   pending_lines(0),
  653.   discarding(0),
  654.   tabs(e->tabs),
  655.   current_tab(TAB_NONE),
  656.   current_field(0),
  657.   margin_character_node(e->margin_character_node),
  658.   margin_character_distance(e->margin_character_distance),
  659.   numbering_nodes(0),
  660.   number_text_separation(e->number_text_separation),
  661.   line_number_multiple(e->line_number_multiple),
  662.   line_number_indent(e->line_number_indent),
  663.   no_number_count(e->no_number_count),
  664.   tab_char(e->tab_char),
  665.   leader_char(e->leader_char),
  666.   hyphenation_flags(e->hyphenation_flags),
  667.   fontno(e->fontno),
  668.   prev_fontno(e->prev_fontno),
  669.   dummy(1),
  670.   family(e->family),
  671.   prev_family(e->prev_family),
  672.   leader_node(0),
  673. #ifdef WIDOW_CONTROL
  674.   widow_control(e->widow_control),
  675. #endif /* WIDOW_CONTROL */
  676.   hyphen_line_max(e->hyphen_line_max),
  677.   hyphen_line_count(0),
  678.   hyphenation_space(e->hyphenation_space),
  679.   hyphenation_margin(e->hyphenation_margin)
  680. {
  681. }
  682.  
  683. environment::~environment()
  684. {
  685.   delete leader_node;
  686.   delete_node_list(line);
  687.   delete_node_list(numbering_nodes);
  688. }
  689.  
  690. hunits environment::get_input_line_position()
  691. {
  692.   hunits n;
  693.   if (line == 0)
  694.     n = -input_line_start;
  695.   else
  696.     n = width_total - input_line_start;
  697.   if (current_tab)
  698.     n += tab_width;
  699.   return n;
  700. }
  701.  
  702. void environment::set_input_line_position(hunits n)
  703. {
  704.   input_line_start = line == 0 ? -n : width_total - n;
  705.   if (current_tab)
  706.     input_line_start += tab_width;
  707. }
  708.  
  709. hunits environment::get_line_length()
  710. {
  711.   return line_length;
  712. }
  713.  
  714. hunits environment::get_saved_line_length()
  715. {
  716.   if (line)
  717.     return target_text_length + saved_indent;
  718.   else
  719.     return line_length;
  720. }
  721.  
  722. vunits environment::get_vertical_spacing()
  723. {
  724.   return vertical_spacing;
  725. }
  726.  
  727. int environment::get_line_spacing()
  728. {
  729.   return line_spacing;
  730. }
  731.  
  732. int environment::get_bold()
  733. {
  734.   return get_bold_fontno(fontno);
  735. }
  736.  
  737. hunits environment::get_digit_width()
  738. {
  739.   return env_digit_width(this);
  740.  
  741. int environment::get_adjust_mode()
  742. {
  743.   return adjust_mode;
  744. }
  745.  
  746. int environment::get_fill()
  747. {
  748.   return fill;
  749. }
  750.  
  751. hunits environment::get_indent()
  752. {
  753.   return indent;
  754. }
  755.  
  756. hunits environment::get_saved_indent()
  757. {
  758.   if (line)
  759.     return saved_indent;
  760.   else if (have_temporary_indent)
  761.     return temporary_indent;
  762.   else
  763.     return indent;
  764. }
  765.  
  766. hunits environment::get_temporary_indent()
  767. {
  768.   return temporary_indent;
  769. }
  770.  
  771. hunits environment::get_title_length()
  772. {
  773.   return title_length;
  774. }
  775.  
  776. node *environment::get_prev_char()
  777. {
  778.   for (node *n = current_tab ? tab_contents : line; n; n = n->next) {
  779.     node *last = n->last_char_node();
  780.     if (last)
  781.       return last;
  782.   }
  783.   return 0;
  784. }
  785.  
  786. hunits environment::get_prev_char_width()
  787. {
  788.   node *last = get_prev_char();
  789.   if (!last)
  790.     return H0;
  791.   return last->width();
  792. }
  793.  
  794. hunits environment::get_prev_char_skew()
  795. {
  796.   node *last = get_prev_char();
  797.   if (!last)
  798.     return H0;
  799.   return last->skew();
  800. }
  801.  
  802. vunits environment::get_prev_char_height()
  803. {
  804.   node *last = get_prev_char();
  805.   if (!last)
  806.     return V0;
  807.   vunits min, max;
  808.   last->vertical_extent(&min, &max);
  809.   return -min;
  810. }
  811.  
  812. vunits environment::get_prev_char_depth()
  813. {
  814.   node *last = get_prev_char();
  815.   if (!last)
  816.     return V0;
  817.   vunits min, max;
  818.   last->vertical_extent(&min, &max);
  819.   return max;
  820. }
  821.  
  822. hunits environment::get_text_length()
  823. {
  824.   hunits n = line == 0 ? H0 : width_total;
  825.   if (current_tab)
  826.     n += tab_width;
  827.   return n;
  828. }
  829.  
  830. hunits environment::get_prev_text_length()
  831. {
  832.   return prev_text_length;
  833. }
  834.  
  835.  
  836. static int sb_reg_contents = 0;
  837. static int st_reg_contents = 0;
  838. static int ct_reg_contents = 0;
  839. static int rsb_reg_contents = 0;
  840. static int rst_reg_contents = 0;
  841. static int skw_reg_contents = 0;
  842. static int ssc_reg_contents = 0;
  843.  
  844. void environment::width_registers()
  845. {
  846.   // this is used to implement \w; it sets the st, sb, ct registers
  847.   vunits min = 0, max = 0, cur = 0;
  848.   int character_type = 0;
  849.   ssc_reg_contents = line ? line->subscript_correction().to_units() : 0;
  850.   skw_reg_contents = line ? line->skew().to_units() : 0;
  851.   line = reverse_node_list(line);
  852.   vunits real_min = V0;
  853.   vunits real_max = V0;
  854.   vunits v1, v2;
  855.   for (node *tem = line; tem; tem = tem->next) {
  856.     tem->vertical_extent(&v1, &v2);
  857.     v1 += cur;
  858.     if (v1 < real_min)
  859.       real_min = v1;
  860.     v2 += cur;
  861.     if (v2 > real_max)
  862.       real_max = v2;
  863.     if ((cur += tem->vertical_width()) < min)
  864.       min = cur;
  865.     else if (cur > max)
  866.       max = cur;
  867.     character_type |= tem->character_type();
  868.   }
  869.   line = reverse_node_list(line);
  870.   st_reg_contents = -min.to_units();
  871.   sb_reg_contents = -max.to_units();
  872.   rst_reg_contents = -real_min.to_units();
  873.   rsb_reg_contents = -real_max.to_units();
  874.   ct_reg_contents = character_type;
  875. }
  876.  
  877. node *environment::extract_output_line()
  878. {
  879.   if (current_tab)
  880.     wrap_up_tab();
  881.   node *n = line;
  882.   line = 0;
  883.   return n;
  884. }
  885.  
  886. /* environment related requests */
  887.  
  888. void environment_switch()
  889. {
  890.   int pop = 0;    // 1 means pop, 2 means pop but no error message on underflow
  891.   if (curenv->is_dummy())
  892.     error("can't switch environments when current environment is dummy");
  893.   else if (!has_arg())
  894.     pop = 1;
  895.   else {
  896.     symbol nm;
  897.     if (!tok.delimiter()) {
  898.       // It looks like a number.
  899.       int n;
  900.       if (get_integer(&n)) {
  901.     if (n >= 0 && n < NENVIRONMENTS) {
  902.       env_stack = new env_list(curenv, env_stack);
  903.       if (env_table[n] == 0)
  904.         env_table[n] = new environment(itoa(n));
  905.       curenv = env_table[n];
  906.     }
  907.     else
  908.       nm = itoa(n);
  909.       }
  910.       else
  911.     pop = 2;
  912.     }
  913.     else {
  914.       nm = get_long_name(1);
  915.       if (nm.is_null())
  916.     pop = 2;
  917.     }
  918.     if (!nm.is_null()) {
  919.       environment *e = (environment *)env_dictionary.lookup(nm);
  920.       if (!e) {
  921.     e = new environment(nm);
  922.     (void)env_dictionary.lookup(nm, e);
  923.       }
  924.       env_stack = new env_list(curenv, env_stack);
  925.       curenv = e;
  926.     }
  927.   }
  928.   if (pop) {
  929.     if (env_stack == 0) {
  930.       if (pop == 1)
  931.     error("environment stack underflow");
  932.     }
  933.     else {
  934.       curenv = env_stack->env;
  935.       env_list *tem = env_stack;
  936.       env_stack = env_stack->next;
  937.       delete tem;
  938.     }
  939.   }
  940.   skip_line();
  941. }
  942.  
  943.  
  944. static symbol P_symbol("P");
  945.  
  946. void font_change()
  947. {
  948.   symbol s = get_name();
  949.   int is_number = 1;
  950.   if (s.is_null() || s == P_symbol) {
  951.     s = P_symbol;
  952.     is_number = 0;
  953.   }
  954.   else {
  955.     for (const char *p = s.contents(); p != 0 && *p != 0; p++)
  956.       if (!csdigit(*p)) {
  957.     is_number = 0;
  958.     break;
  959.       }
  960.   }
  961.   if (is_number)
  962.     curenv->set_font(atoi(s.contents()));
  963.   else
  964.     curenv->set_font(s);
  965.   skip_line();
  966. }
  967.  
  968. void family_change()
  969. {
  970.   symbol s = get_name(1);
  971.   if (!s.is_null())
  972.     curenv->set_family(s);
  973.   skip_line();
  974. }
  975.  
  976. #if 0
  977. void point_size()
  978. {
  979.   int n;
  980.   if (has_arg()) {
  981.     if (get_number(&n, 0, curenv->get_requested_point_size()/sizescale)) {
  982.       n *= sizescale;
  983.       if (n <= 0)
  984.     n = 1;
  985.       curenv->set_size(n);
  986.     }
  987.   }
  988.   else
  989.     curenv->set_size(0);
  990.   skip_line();
  991. }
  992. #endif
  993.  
  994. void point_size()
  995. {
  996.   int n;
  997.   if (has_arg()) {
  998.     if (get_number(&n, 'z', curenv->get_requested_point_size())) {
  999.       if (n <= 0)
  1000.     n = 1;
  1001.       curenv->set_size(n);
  1002.     }
  1003.   }
  1004.   else
  1005.     curenv->set_size(0);
  1006.   skip_line();
  1007. }
  1008.  
  1009. void space_size()
  1010. {
  1011.   int n;
  1012.   if (get_integer(&n)) {
  1013.     curenv->space_size = n;
  1014.     if (has_arg()) {
  1015.       if (get_integer(&n))
  1016.     curenv->sentence_space_size = n;
  1017.     }
  1018.     else
  1019.       curenv->sentence_space_size = curenv->space_size;
  1020.   }
  1021.   skip_line();
  1022. }
  1023.  
  1024. void fill()
  1025. {
  1026.   while (!tok.newline() && !tok.eof())
  1027.     tok.next();
  1028.   if (break_flag)
  1029.     curenv->do_break();
  1030.   curenv->fill = 1;
  1031.   tok.next();
  1032. }
  1033.  
  1034. void no_fill()
  1035. {
  1036.   while (!tok.newline() && !tok.eof())
  1037.     tok.next();
  1038.   if (break_flag)
  1039.     curenv->do_break();
  1040.   curenv->fill = 0;
  1041.   tok.next();
  1042. }
  1043.  
  1044. void center()
  1045. {
  1046.   int n;
  1047.   if (!has_arg() || !get_integer(&n))
  1048.     n = 1;
  1049.   while (!tok.newline() && !tok.eof())
  1050.     tok.next();
  1051.   if (break_flag)
  1052.     curenv->do_break();
  1053.   curenv->right_justify_lines = 0;
  1054.   curenv->center_lines = n;
  1055.   tok.next();
  1056. }
  1057.  
  1058. void right_justify()
  1059. {
  1060.   int n;
  1061.   if (!has_arg() || !get_integer(&n))
  1062.     n = 1;
  1063.   while (!tok.newline() && !tok.eof())
  1064.     tok.next();
  1065.   if (break_flag)
  1066.     curenv->do_break();
  1067.   curenv->center_lines = 0;
  1068.   curenv->right_justify_lines = n;
  1069.   tok.next();
  1070. }
  1071.  
  1072. void line_length()
  1073. {
  1074.   hunits temp;
  1075.   if (!has_arg()) {
  1076.     hunits temp = curenv->line_length;
  1077.     curenv->line_length = curenv->prev_line_length;
  1078.     curenv->prev_line_length = temp;
  1079.   }
  1080.   else if (get_hunits(&temp, 'm', curenv->line_length)) {
  1081.     if (temp < H0) {
  1082.       warning(WARN_RANGE, "bad line length %1u", temp.to_units());
  1083.       temp = H0;
  1084.     }
  1085.     curenv->prev_line_length = curenv->line_length;
  1086.     curenv->line_length = temp;
  1087.   }
  1088.   skip_line();
  1089. }
  1090.  
  1091. void title_length()
  1092. {
  1093.   hunits temp;
  1094.   if (!has_arg()) {
  1095.     hunits temp = curenv->title_length;
  1096.     curenv->title_length = curenv->prev_title_length;
  1097.     curenv->prev_title_length = temp;
  1098.   }
  1099.   else if (get_hunits(&temp, 'm', curenv->title_length)) {
  1100.     if (temp < H0) {
  1101.       warning(WARN_RANGE, "bad title length %1u", temp.to_units());
  1102.       temp = H0;
  1103.     }
  1104.     curenv->prev_title_length = curenv->title_length;
  1105.     curenv->title_length = temp;
  1106.   }
  1107.   skip_line();
  1108. }
  1109.  
  1110. void vertical_spacing()
  1111. {
  1112.   if (!has_arg()) {
  1113.     vunits temp = curenv->vertical_spacing;
  1114.     curenv->vertical_spacing = curenv->prev_vertical_spacing;
  1115.     curenv->prev_vertical_spacing = temp;
  1116.   }
  1117.   else {
  1118.     vunits temp;
  1119.     if (get_vunits(&temp, 'p', curenv->vertical_spacing)) {
  1120.       if (temp <= V0) {
  1121.     warning(WARN_RANGE, "vertical spacing must be greater than 0");
  1122.     temp = vresolution;
  1123.       }
  1124.       curenv->prev_vertical_spacing = curenv->vertical_spacing;
  1125.       curenv->vertical_spacing = temp;
  1126.     }
  1127.   }
  1128.   skip_line();
  1129. }
  1130.  
  1131. void line_spacing()
  1132. {
  1133.   int temp;
  1134.   if (!has_arg()) {
  1135.     temp = curenv->line_spacing;
  1136.     curenv->line_spacing = curenv->prev_line_spacing;
  1137.     curenv->prev_line_spacing = temp;
  1138.   }
  1139.   else if (get_integer(&temp)) {
  1140.     if (temp < 1) {
  1141.       warning(WARN_RANGE, "value %1 out of range: interpreted as 1", temp);
  1142.       temp = 1;
  1143.     }
  1144.     curenv->prev_line_spacing = curenv->line_spacing;
  1145.     curenv->line_spacing = temp;
  1146.   }
  1147.   skip_line();
  1148. }
  1149.  
  1150. void indent()
  1151. {
  1152.   hunits temp;
  1153.   int err = 0;
  1154.   if (!has_arg())
  1155.     temp = curenv->prev_indent;
  1156.   else if (!get_hunits(&temp, 'm', curenv->indent))
  1157.     err = 1;
  1158.   while (!tok.newline() && !tok.eof())
  1159.     tok.next();
  1160.   if (break_flag)
  1161.     curenv->do_break();
  1162.   if (temp < H0) {
  1163.     warning(WARN_RANGE, "indent cannot be negative");
  1164.     temp = H0;
  1165.   }
  1166.   if (!err) {
  1167.     curenv->have_temporary_indent = 0;
  1168.     curenv->prev_indent = curenv->indent;
  1169.     curenv->indent = temp;
  1170.   }
  1171.   tok.next();
  1172. }
  1173.  
  1174. void temporary_indent()
  1175. {
  1176.   int err = 0;
  1177.   hunits temp;
  1178.   if (!get_hunits(&temp, 'm', curenv->get_indent()))
  1179.     err = 1;
  1180.   while (!tok.newline() && !tok.eof())
  1181.     tok.next();
  1182.   if (break_flag)
  1183.     curenv->do_break();
  1184.   if (temp < H0) {
  1185.     warning(WARN_RANGE, "total indent cannot be negative");
  1186.     temp = H0;
  1187.   }
  1188.   if (!err) {
  1189.     curenv->temporary_indent = temp;
  1190.     curenv->have_temporary_indent = 1;
  1191.   }
  1192.   tok.next();
  1193. }
  1194.  
  1195. void underline()
  1196. {
  1197.   int n = 0;
  1198.   if (!has_arg())
  1199.     n = 1;
  1200.   else if (!get_integer(&n))
  1201.     n = 0;
  1202.   if (n <= 0) {
  1203.     if (curenv->underline_lines > 0) {
  1204.       curenv->prev_fontno = curenv->fontno;
  1205.       curenv->fontno = curenv->pre_underline_fontno;
  1206.     }
  1207.     curenv->underline_lines = 0;
  1208.   }
  1209.   else {
  1210.     curenv->underline_lines = n;
  1211.     curenv->pre_underline_fontno = curenv->fontno;
  1212.     curenv->fontno = get_underline_fontno();
  1213.   }
  1214.   skip_line();
  1215. }
  1216.  
  1217.  
  1218. void control_char()
  1219. {
  1220.   if (!has_arg())
  1221.     curenv->control_char = '.';
  1222.   else if (tok.ch() == 0)
  1223.     error("bad control character");
  1224.   else
  1225.     curenv->control_char = tok.ch();
  1226.   skip_line();
  1227. }
  1228.  
  1229. void no_break_control_char()
  1230. {
  1231.   if (!has_arg())
  1232.     curenv->no_break_control_char = '\'';
  1233.   else if (tok.ch() == 0)
  1234.     error("bad control character");
  1235.   else
  1236.     curenv->no_break_control_char = tok.ch();
  1237.   skip_line();
  1238. }
  1239.  
  1240.  
  1241. void margin_character()
  1242. {
  1243.   if (curenv->margin_character_node) {
  1244.     delete curenv->margin_character_node;
  1245.     curenv->margin_character_node = 0;
  1246.   }
  1247.   charinfo *ci = get_optional_char();
  1248.   if (ci) {
  1249.     curenv->margin_character_node = curenv->make_char_node(ci);
  1250.     hunits d;
  1251.     if (curenv->margin_character_node && has_arg() && get_hunits(&d, 'm'))
  1252.       curenv->margin_character_distance = d;
  1253.   }
  1254.   skip_line();
  1255. }
  1256.  
  1257. void number_lines()
  1258. {
  1259.   delete_node_list(curenv->numbering_nodes);
  1260.   curenv->numbering_nodes = 0;
  1261.   int n;
  1262.   if (has_arg() && get_integer(&n, next_line_number)) {
  1263.     next_line_number = n;
  1264.     if (next_line_number < 0) {
  1265.       warning(WARN_RANGE, "negative line number");
  1266.       next_line_number = 0;
  1267.     }
  1268.     node *nd = 0;
  1269.     for (int i = '9'; i >= '0'; i--) {
  1270.       node *tem = make_node(charset_table[i], curenv);
  1271.       if (!tem) {
  1272.     skip_line();
  1273.     return;
  1274.       }
  1275.       tem->next = nd;
  1276.       nd = tem;
  1277.     }
  1278.     curenv->numbering_nodes = nd;
  1279.     curenv->line_number_digit_width = env_digit_width(curenv);
  1280.     if (has_arg()) {
  1281.       if (!tok.delimiter()) {
  1282.     if (get_integer(&n)) {
  1283.       if (n <= 0) {
  1284.         warning(WARN_RANGE, "negative or zero line number multiple");
  1285.       }
  1286.       else
  1287.         curenv->line_number_multiple = n;
  1288.     }
  1289.       }
  1290.       else
  1291.     while (!tok.space() && !tok.newline() && !tok.eof())
  1292.       tok.next();
  1293.       if (has_arg()) {
  1294.     if (!tok.delimiter()) {
  1295.       if (get_integer(&n))
  1296.         curenv->number_text_separation = n;
  1297.     }
  1298.     else
  1299.       while (!tok.space() && !tok.newline() && !tok.eof())
  1300.         tok.next();
  1301.     if (has_arg() && !tok.delimiter() && get_integer(&n))
  1302.       curenv->line_number_indent = n;
  1303.       }
  1304.     }
  1305.   }
  1306.   skip_line();
  1307. }
  1308.  
  1309. void no_number()
  1310. {
  1311.   if (has_arg()) {
  1312.     int n;
  1313.     if (get_integer(&n))
  1314.       curenv->no_number_count = n > 0 ? n : 0;
  1315.   }
  1316.   else
  1317.     curenv->no_number_count = 1;
  1318.   skip_line();
  1319. }
  1320.  
  1321. void no_hyphenate()
  1322. {
  1323.   curenv->hyphenation_flags = 0;
  1324.   skip_line();
  1325. }
  1326.  
  1327. void hyphenate_request()
  1328. {
  1329.   int n;
  1330.   if (has_arg()) {
  1331.     if (get_integer(&n))
  1332.       curenv->hyphenation_flags = n;
  1333.   }
  1334.   else
  1335.     curenv->hyphenation_flags = 1;
  1336.   skip_line();
  1337. }
  1338.  
  1339. void hyphen_char()
  1340. {
  1341.   curenv->hyphen_indicator_char = get_optional_char();
  1342.   skip_line();
  1343. }
  1344.  
  1345. void hyphen_line_max_request()
  1346. {
  1347.   if (has_arg()) {
  1348.     int n;
  1349.     if (get_integer(&n))
  1350.       curenv->hyphen_line_max = n;
  1351.   }
  1352.   else
  1353.     curenv->hyphen_line_max = -1;
  1354.   skip_line();
  1355. }
  1356.  
  1357. void environment::interrupt()
  1358. {
  1359.   if (!dummy) {
  1360.     add_node(new transparent_dummy_node);
  1361.     interrupted = 1;
  1362.   }
  1363. }
  1364.  
  1365. void environment::newline()
  1366. {
  1367.   if (underline_lines > 0) {
  1368.     if (--underline_lines == 0) {
  1369.       prev_fontno = fontno;
  1370.       fontno = pre_underline_fontno;
  1371.     }
  1372.   }
  1373.   if (current_field)
  1374.     wrap_up_field();
  1375.   if (current_tab)
  1376.     wrap_up_tab();
  1377.   // strip trailing spaces
  1378.   while (line != 0 && line->discardable()) {
  1379.     width_total -= line->width();
  1380.     space_total -= line->nspaces();
  1381.     node *tem = line;
  1382.     line = line->next;
  1383.     delete tem;
  1384.   }
  1385.   node *to_be_output = 0;
  1386.   hunits to_be_output_width;
  1387.   prev_line_interrupted = interrupted;
  1388.   if (dummy)
  1389.     space_newline();
  1390.   else if (interrupted)
  1391.     interrupted = 0;
  1392.   else if (center_lines > 0) {
  1393.     --center_lines;
  1394.     hunits x = target_text_length - width_total;
  1395.     if (x > H0)
  1396.       saved_indent += x/2;
  1397.     to_be_output = line;
  1398.     to_be_output_width = width_total;
  1399.     line = 0;
  1400.   }
  1401.   else if (right_justify_lines > 0) {
  1402.     --right_justify_lines;
  1403.     hunits x = target_text_length - width_total;
  1404.     if (x > H0)
  1405.       saved_indent += x;
  1406.     to_be_output = line;
  1407.     to_be_output_width = width_total;
  1408.     line = 0;
  1409.   }
  1410.   else if (fill)
  1411.     space_newline();
  1412.   else {
  1413.     to_be_output = line;
  1414.     to_be_output_width = width_total;
  1415.     line = 0;
  1416.   }
  1417.   input_line_start = line == 0 ? H0 : width_total;
  1418.   if (to_be_output) {
  1419.     output_line(to_be_output, to_be_output_width);
  1420.     hyphen_line_count = 0;
  1421.   }
  1422.   if (input_trap_count > 0) {
  1423.     if (--input_trap_count == 0)
  1424.       spring_trap(input_trap);
  1425.   }
  1426. }
  1427.  
  1428. void environment::output_line(node *n, hunits width)
  1429. {
  1430.   prev_text_length = width;
  1431.   if (margin_character_node) {
  1432.     hunits d = line_length + margin_character_distance - saved_indent - width;
  1433.     if (d > 0) {
  1434.       n = new hmotion_node(d, n);
  1435.       width += d;
  1436.     }
  1437.     node *tem = margin_character_node->copy();
  1438.     tem->next = n;
  1439.     n = tem;
  1440.     width += tem->width();
  1441.   }
  1442.   node *nn = 0;
  1443.   while (n != 0) {
  1444.     node *tem = n->next;
  1445.     n->next = nn;
  1446.     nn = n;
  1447.     n = tem;
  1448.   }
  1449.   if (!saved_indent.is_zero())
  1450.     nn = new hmotion_node(saved_indent, nn);
  1451.   width += saved_indent;
  1452.   if (no_number_count > 0)
  1453.     --no_number_count;
  1454.   else if (numbering_nodes) {
  1455.     hunits w = (line_number_digit_width
  1456.         *(3+line_number_indent+number_text_separation));
  1457.     if (next_line_number % line_number_multiple != 0)
  1458.       nn = new hmotion_node(w, nn);
  1459.     else {
  1460.       hunits x = w;
  1461.       nn = new hmotion_node(number_text_separation*line_number_digit_width,
  1462.                 nn);
  1463.       x -= number_text_separation*line_number_digit_width;
  1464.       char buf[30];
  1465.       sprintf(buf, "%3d", next_line_number);
  1466.       for (char *p = strchr(buf, '\0') - 1; p >= buf && *p != ' '; --p) {
  1467.     node *gn = numbering_nodes;
  1468.     for (int count = *p - '0'; count > 0; count--)
  1469.       gn = gn->next;
  1470.     gn = gn->copy();
  1471.     x -= gn->width();
  1472.     gn->next = nn;
  1473.     nn = gn;
  1474.       }
  1475.       nn = new hmotion_node(x, nn);
  1476.     }
  1477.     width += w;
  1478.     ++next_line_number;
  1479.   }
  1480.   output(nn, !fill, vertical_spacing, line_spacing, width);
  1481. }
  1482.  
  1483. void environment::start_line()
  1484. {
  1485.   assert(line == 0);
  1486.   discarding = 0;
  1487.   line = new line_start_node;
  1488.   if (have_temporary_indent) {
  1489.     saved_indent = temporary_indent;
  1490.     have_temporary_indent = 0;
  1491.   }
  1492.   else
  1493.     saved_indent = indent;
  1494.   target_text_length = line_length - saved_indent;
  1495.   width_total = H0;
  1496.   space_total = 0;
  1497. }
  1498.  
  1499. hunits environment::get_hyphenation_space()
  1500. {
  1501.   return hyphenation_space;
  1502. }
  1503.  
  1504. void hyphenation_space_request()
  1505. {
  1506.   hunits n;
  1507.   if (get_hunits(&n, 'm')) {
  1508.     if (n < H0) {
  1509.       warning(WARN_RANGE, "hyphenation space cannot be negative");
  1510.       n = H0;
  1511.     }
  1512.     curenv->hyphenation_space = n;
  1513.   }
  1514.   skip_line();
  1515. }
  1516.  
  1517. hunits environment::get_hyphenation_margin()
  1518. {
  1519.   return hyphenation_margin;
  1520. }
  1521.  
  1522. void hyphenation_margin_request()
  1523. {
  1524.   hunits n;
  1525.   if (get_hunits(&n, 'm')) {
  1526.     if (n < H0) {
  1527.       warning(WARN_RANGE, "hyphenation margin cannot be negative");
  1528.       n = H0;
  1529.     }
  1530.     curenv->hyphenation_margin = n;
  1531.   }
  1532.   skip_line();
  1533. }
  1534.  
  1535. breakpoint *environment::choose_breakpoint()
  1536. {
  1537.   hunits x = width_total;
  1538.   int s = space_total;
  1539.   node *n = line;
  1540.   breakpoint *best_bp = 0;    // the best breakpoint so far
  1541.   int best_bp_fits = 0;
  1542.   while (n != 0) {
  1543.     x -= n->width();
  1544.     s -= n->nspaces();
  1545.     breakpoint *bp = n->get_breakpoints(x, s);
  1546.     while (bp != 0) {
  1547.       if (bp->width <= target_text_length) {
  1548.     if (!bp->hyphenated) {
  1549.       breakpoint *tem = bp->next;
  1550.       bp->next = 0;
  1551.       while (tem != 0) {
  1552.         breakpoint *tem1 = tem;
  1553.         tem = tem->next;
  1554.         delete tem1;
  1555.       }
  1556.       if (best_bp_fits
  1557.           // Decide whether to use the hyphenated breakpoint.
  1558.           && (hyphen_line_max < 0
  1559.           // Only choose the hyphenated breakpoint if it would not
  1560.           // exceed the maximum number of consecutive hyphenated
  1561.           // lines.
  1562.           || hyphen_line_count + 1 <= hyphen_line_max)
  1563.           && !(adjust_mode == ADJUST_BOTH
  1564.            // Don't choose the hyphenated breakpoint if the line
  1565.            // can be justified by adding no more than
  1566.            // hyphenation_space to any word space.
  1567.            ? (bp->nspaces > 0
  1568.               && (((target_text_length - bp->width
  1569.                 + (bp->nspaces - 1)*hresolution)/bp->nspaces)
  1570.               <= hyphenation_space))
  1571.            // Don't choose the hyphenated breakpoint if the line
  1572.            // is no more than hyphenation_margin short.
  1573.            : target_text_length - bp->width <= hyphenation_margin)) {
  1574.         delete bp;
  1575.         return best_bp;
  1576.       }
  1577.       if (best_bp)
  1578.         delete best_bp;
  1579.       return bp;
  1580.     }
  1581.     else {
  1582.       if ((adjust_mode == ADJUST_BOTH
  1583.            ? hyphenation_space == H0
  1584.            : hyphenation_margin == H0)
  1585.           && (hyphen_line_max < 0
  1586.           || hyphen_line_count + 1 <= hyphen_line_max)) {
  1587.         // No need to consider a non-hyphenated breakpoint.
  1588.         if (best_bp)
  1589.           delete best_bp;
  1590.         return bp;
  1591.       }
  1592.       // It fits but it's hyphenated.
  1593.       if (!best_bp_fits) {
  1594.         if (best_bp)
  1595.           delete best_bp;
  1596.         best_bp = bp;
  1597.         bp = bp->next;
  1598.         best_bp_fits = 1;
  1599.       }
  1600.       else {
  1601.         breakpoint *tem = bp;
  1602.         bp = bp->next;
  1603.         delete tem;
  1604.       }
  1605.     }
  1606.       }
  1607.       else {
  1608.     if (best_bp)
  1609.       delete best_bp;
  1610.     best_bp = bp;
  1611.     bp = bp->next;
  1612.       }
  1613.     }
  1614.     n = n->next;
  1615.   }
  1616.   if (best_bp) {
  1617.     if (!best_bp_fits)
  1618.       warning(WARN_BREAK, "can't break line");
  1619.     return best_bp;
  1620.   }
  1621.   return 0;
  1622. }
  1623.  
  1624. void environment::hyphenate_line()
  1625. {
  1626.   if (line == 0)
  1627.     return;
  1628.   hyphenation_type prev_type = line->get_hyphenation_type();
  1629.   for (node *tem = line->next; tem != 0; tem = tem->next) {
  1630.     hyphenation_type this_type = tem->get_hyphenation_type();
  1631.     if (prev_type == HYPHEN_BOUNDARY && this_type == HYPHEN_MIDDLE)
  1632.       break;
  1633.     prev_type = this_type;
  1634.   }
  1635.   if (tem == 0)
  1636.     return;
  1637.   node *start = tem;
  1638.   int i = 0;
  1639.   do {
  1640.     ++i;
  1641.     tem = tem->next;
  1642.   } while (tem != 0 && tem->get_hyphenation_type() == HYPHEN_MIDDLE);
  1643.   int inhibit = (tem != 0 && tem->get_hyphenation_type() == HYPHEN_INHIBIT);
  1644.   node *end = tem;
  1645.   hyphen_list *sl = 0;
  1646.   tem = start;
  1647.   node *forward = 0;
  1648.   while (tem != end) {
  1649.     sl = tem->get_hyphen_list(sl);
  1650.     node *tem1 = tem;
  1651.     tem = tem->next;
  1652.     tem1->next = forward;
  1653.     forward = tem1;
  1654.   }
  1655.   // this is for characters like hyphen and emdash
  1656.   int prev_code = 0;
  1657.   for (hyphen_list *h = sl; h; h = h->next) {
  1658.     h->breakable = (prev_code != 0
  1659.             && h->next != 0
  1660.             && h->next->hyphenation_code != 0);
  1661.     prev_code = h->hyphenation_code;
  1662.   }
  1663.   if (hyphenation_flags != 0
  1664.       && !inhibit
  1665.       // this may not be right if we have extra space on this line
  1666.       && !((hyphenation_flags & HYPHEN_LAST_LINE)
  1667.        && curdiv->distance_to_next_trap() <= line_spacing*vertical_spacing)
  1668.       && i >= 4)
  1669.     hyphenate(sl, hyphenation_flags);
  1670.   while (forward != 0) {
  1671.     node *tem1 = forward;
  1672.     forward = forward->next;
  1673.     tem1->next = 0;
  1674.     tem = tem1->add_self(tem, &sl);
  1675.   }
  1676.   node *new_start = tem;
  1677.   for (tem = line; tem->next != start; tem = tem->next)
  1678.     ;
  1679.   tem->next = new_start;
  1680. }
  1681.  
  1682. static node *node_list_reverse(node *n)
  1683. {
  1684.   node *res = 0;
  1685.   while (n) {
  1686.     node *tem = n;
  1687.     n = n->next;
  1688.     tem->next = res;
  1689.     res = tem;
  1690.   }
  1691.   return res;
  1692. }
  1693.  
  1694. static void distribute_space(node *n, int nspaces, hunits desired_space,
  1695.                  int force_forward = 0)
  1696. {
  1697.   static int reverse = 0;
  1698.   if (!force_forward && reverse)
  1699.     n = node_list_reverse(n);
  1700.   for (node *tem = n; tem; tem = tem->next)
  1701.     tem->spread_space(&nspaces, &desired_space);
  1702.   if (!force_forward) {
  1703.     if (reverse)
  1704.       (void)node_list_reverse(n);
  1705.     reverse = !reverse;
  1706.   }
  1707.   assert(desired_space.is_zero() && nspaces == 0);
  1708. }
  1709.  
  1710. void environment::possibly_break_line(int forced)
  1711. {
  1712.   if (!fill || current_tab || current_field || dummy)
  1713.     return;
  1714.   while (line != 0 && (forced || width_total > target_text_length)) {
  1715.     hyphenate_line();
  1716.     breakpoint *bp = choose_breakpoint();
  1717.     if (bp == 0)
  1718.       // we'll find one eventually
  1719.       return;
  1720.     node *pre, *post;
  1721.     bp->nd->split(bp->index, &pre, &post);
  1722.     hunits extra_space_width = H0;
  1723.     switch(adjust_mode) {
  1724.     case ADJUST_BOTH:
  1725.       if (bp->nspaces != 0)
  1726.     extra_space_width = target_text_length - bp->width;
  1727.       break;
  1728.     case ADJUST_CENTER:
  1729.       saved_indent += (target_text_length - bp->width)/2;
  1730.       break;
  1731.     case ADJUST_RIGHT:
  1732.       saved_indent += target_text_length - bp->width;
  1733.       break;
  1734.     }
  1735.     distribute_space(pre, bp->nspaces, extra_space_width);
  1736.     node *tem = line;
  1737.     line = 0;
  1738.     output_line(pre, bp->width + extra_space_width);
  1739.     line = tem;
  1740.     input_line_start -= bp->width + extra_space_width;
  1741.     if (line != bp->nd) {
  1742.       for (tem = line; tem->next != bp->nd; tem = tem->next)
  1743.     ;
  1744.       tem->next = post;
  1745.     }
  1746.     else
  1747.       line = post;
  1748.     if (bp->hyphenated)
  1749.       hyphen_line_count++;
  1750.     else
  1751.       hyphen_line_count = 0;
  1752.     delete bp;
  1753.     space_total = 0;
  1754.     width_total = 0;
  1755.     node *first_non_discardable = 0;
  1756.     for (tem = line; tem != 0; tem = tem->next)
  1757.       if (!tem->discardable())
  1758.     first_non_discardable = tem;
  1759.     node *to_be_discarded;
  1760.     if (first_non_discardable) {
  1761.       to_be_discarded = first_non_discardable->next;
  1762.       first_non_discardable->next = 0;
  1763.       for (tem = line; tem != 0; tem = tem->next) {
  1764.     width_total += tem->width();
  1765.     space_total += tem->nspaces();
  1766.       }
  1767.       discarding = 0;
  1768.     }
  1769.     else {
  1770.       discarding = 1;
  1771.       to_be_discarded = line;
  1772.       line = 0;
  1773.     }
  1774.     while (to_be_discarded != 0) {
  1775.       tem = to_be_discarded;
  1776.       to_be_discarded = to_be_discarded->next;
  1777.       input_line_start -= tem->width();
  1778.       delete tem;
  1779.     }
  1780.     if (line != 0) {
  1781.       if (have_temporary_indent) {
  1782.     saved_indent = temporary_indent;
  1783.     have_temporary_indent = 0;
  1784.       }
  1785.       else
  1786.     saved_indent = indent;
  1787.       target_text_length = line_length - saved_indent;
  1788.     }
  1789.   }
  1790. }
  1791.  
  1792. void environment::do_break()
  1793. {
  1794.   if (curdiv == topdiv && !topdiv->first_page_begun) {
  1795.     topdiv->begin_page();
  1796.     return;
  1797.   }
  1798.   if (current_tab)
  1799.     wrap_up_tab();
  1800.   if (line) {
  1801.     line = new space_node(H0, line); // this is so that hyphenation works
  1802.     space_total++;
  1803.     possibly_break_line();
  1804.   }
  1805.   while (line != 0 && line->discardable()) {
  1806.     width_total -= line->width();
  1807.     space_total -= line->nspaces();
  1808.     node *tem = line;
  1809.     line = line->next;
  1810.     delete tem;
  1811.   }
  1812.   discarding = 0;
  1813.   input_line_start = H0;
  1814.   if (line != 0) {
  1815.     if (fill) {
  1816.       switch (adjust_mode) {
  1817.       case ADJUST_CENTER:
  1818.     saved_indent += (target_text_length - width_total)/2;
  1819.     break;
  1820.       case ADJUST_RIGHT:
  1821.     saved_indent += target_text_length - width_total;
  1822.     break;
  1823.       }
  1824.     }
  1825.     node *tem = line;
  1826.     line = 0;
  1827.     output_line(tem, width_total);
  1828.     hyphen_line_count = 0;
  1829.   }
  1830. #ifdef WIDOW_CONTROL
  1831.   mark_last_line();
  1832.   output_pending_lines();
  1833. #endif /* WIDOW_CONTROL */
  1834. }
  1835.  
  1836. int environment::is_empty()
  1837. {
  1838.   return !current_tab && line == 0 && pending_lines == 0;
  1839. }
  1840.  
  1841. void break_request()
  1842. {
  1843.   while (!tok.newline() && !tok.eof())
  1844.     tok.next();
  1845.   if (break_flag)
  1846.     curenv->do_break();
  1847.   tok.next();
  1848. }
  1849.  
  1850. void title()
  1851. {
  1852.   if (curdiv == topdiv && !topdiv->first_page_begun) {
  1853.     handle_initial_title();
  1854.     return;
  1855.   }
  1856.   node *part[3];
  1857.   hunits part_width[3];
  1858.   part[0] = part[1] = part[2] = 0;
  1859.   environment env(curenv);
  1860.   environment *oldenv = curenv;
  1861.   curenv = &env;
  1862.   read_title_parts(part, part_width);
  1863.   curenv = oldenv;
  1864.   curenv->size = env.size;
  1865.   curenv->prev_size = env.prev_size;
  1866.   curenv->requested_size = env.requested_size;
  1867.   curenv->prev_requested_size = env.prev_requested_size;
  1868.   curenv->char_height = env.char_height;
  1869.   curenv->char_slant = env.char_slant;
  1870.   curenv->fontno = env.fontno;
  1871.   curenv->prev_fontno = env.prev_fontno;
  1872.   node *n = 0;
  1873.   node *p = part[2];
  1874.   while (p != 0) {
  1875.     node *tem = p;
  1876.     p = p->next;
  1877.     tem->next = n;
  1878.     n = tem;
  1879.   }
  1880.   hunits title_length(curenv->title_length);
  1881.   hunits f = title_length - part_width[1];
  1882.   hunits f2 = f/2;
  1883.   n = new hmotion_node(f2 - part_width[2], n);
  1884.   p = part[1];
  1885.   while (p != 0) {
  1886.     node *tem = p;
  1887.     p = p->next;
  1888.     tem->next = n;
  1889.     n = tem;
  1890.   }
  1891.   n = new hmotion_node(f - f2 - part_width[0], n);
  1892.   p = part[0];
  1893.   while (p != 0) {
  1894.     node *tem = p;
  1895.     p = p->next;
  1896.     tem->next = n;
  1897.     n = tem;
  1898.   }
  1899.   curenv->output_title(n, !curenv->fill, curenv->vertical_spacing,
  1900.                curenv->line_spacing, title_length);
  1901.   curenv->hyphen_line_count = 0;
  1902.   tok.next();
  1903. }  
  1904.  
  1905. void adjust()
  1906. {
  1907.   if (!has_arg())
  1908.     curenv->adjust_mode |= 1;
  1909.   else
  1910.     switch (tok.ch()) {
  1911.     case 'l':
  1912.       curenv->adjust_mode = ADJUST_LEFT;
  1913.       break;
  1914.     case 'r':
  1915.       curenv->adjust_mode = ADJUST_RIGHT;
  1916.       break;
  1917.     case 'c':
  1918.       curenv->adjust_mode = ADJUST_CENTER;
  1919.       break;
  1920.     case 'b':
  1921.     case 'n':
  1922.       curenv->adjust_mode = ADJUST_BOTH;
  1923.       break;
  1924.     default:
  1925.       int n;
  1926.       if (get_integer(&n)) {
  1927.     if (0 <= n && n <= 5)
  1928.       curenv->adjust_mode = n;
  1929.     else
  1930.       warning(WARN_RANGE, "adjustment mode `%1' out of range", n);
  1931.       }
  1932.     }
  1933.   skip_line();
  1934. }
  1935.  
  1936. void no_adjust()
  1937. {
  1938.   curenv->adjust_mode &= ~1;
  1939.   skip_line();
  1940. }
  1941.  
  1942. void input_trap()
  1943. {
  1944.   int n;
  1945.   if (!has_arg())
  1946.     curenv->input_trap_count = 0;
  1947.   else if (get_integer(&n)) {
  1948.     if (n <= 0)
  1949.       error("number of lines for input trap must be greater than zero");
  1950.     else {
  1951.       symbol s = get_name(1);
  1952.       if (!s.is_null()) {
  1953.     curenv->input_trap_count = n;
  1954.     curenv->input_trap = s;
  1955.       }
  1956.     }
  1957.   }
  1958.   skip_line();
  1959. }
  1960.  
  1961. /* tabs */
  1962.  
  1963. // must not be R or C or L or a legitimate part of a number expression
  1964. const char TAB_REPEAT_CHAR = 'T';
  1965.  
  1966. struct tab {
  1967.   tab *next;
  1968.   hunits pos;
  1969.   tab_type type;
  1970.   tab(hunits, tab_type);
  1971. #if 1
  1972.   enum { BLOCK = 1024 };
  1973.   static tab *free_list;
  1974.   void *operator new(size_t);
  1975.   void operator delete(void *);
  1976. #endif
  1977. };
  1978.  
  1979. #if 1
  1980. tab *tab::free_list = 0;
  1981.  
  1982. void *tab::operator new(size_t n)
  1983. {
  1984.   assert(n == sizeof(tab));
  1985.   if (!free_list) {
  1986.     free_list = (tab *)new char[sizeof(tab)*BLOCK];
  1987.     for (int i = 0; i < BLOCK - 1; i++)
  1988.       free_list[i].next = free_list + i + 1;
  1989.     free_list[BLOCK-1].next = 0;
  1990.   }
  1991.   tab *p = free_list;
  1992.   free_list = (tab *)(free_list->next);
  1993.   p->next = 0;
  1994.   return p;
  1995. }
  1996.  
  1997. #ifdef __GNUG__
  1998. /* cfront can't cope with this. */
  1999. inline
  2000. #endif
  2001. void tab::operator delete(void *p)
  2002. {
  2003.   if (p) {
  2004.     ((tab *)p)->next = free_list;
  2005.     free_list = (tab *)p;
  2006.   }
  2007. }
  2008.  
  2009. #endif
  2010.  
  2011. tab::tab(hunits x, tab_type t) : next(0), pos(x), type(t)
  2012. {
  2013. }
  2014.  
  2015. tab_stops::tab_stops(hunits distance, tab_type type) 
  2016.      : initial_list(0)
  2017. {
  2018.   repeated_list = new tab(distance, type);
  2019. }
  2020.  
  2021. tab_stops::~tab_stops()
  2022. {
  2023.   clear();
  2024. }
  2025.  
  2026. tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance)
  2027. {
  2028.   hunits lastpos = 0;
  2029.   for (tab *tem = initial_list; tem && tem->pos <= curpos; tem = tem->next)
  2030.     lastpos = tem->pos;
  2031.   if (tem) {
  2032.     *distance = tem->pos - curpos;
  2033.     return tem->type;
  2034.   }
  2035.   if (repeated_list == 0)
  2036.     return TAB_NONE;
  2037.   hunits base = lastpos;
  2038.   for (;;) {
  2039.     for (tem = repeated_list; tem && tem->pos + base <= curpos; tem = tem->next)
  2040.       lastpos = tem->pos;
  2041.     if (tem) {
  2042.       *distance = tem->pos + base - curpos;
  2043.       return tem->type;
  2044.     }
  2045.     assert(lastpos > 0);
  2046.     base += lastpos;
  2047.   }
  2048.   return TAB_NONE;
  2049. }
  2050.  
  2051. const char *tab_stops::to_string()
  2052. {
  2053.   static char *buf = 0;
  2054.   static int buf_size = 0;
  2055.   // figure out a maximum on the amount of space we can need
  2056.   int count = 0;
  2057.   for (tab *p = initial_list; p; p = p->next)
  2058.     ++count;
  2059.   for (p = repeated_list; p; p = p->next)
  2060.     ++count;
  2061.   // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0'
  2062.   int need = count*12 + 3;
  2063.   if (buf == 0 || need > buf_size) {
  2064.     if (buf)
  2065.       delete buf;
  2066.     buf_size = need;
  2067.     buf = new char[buf_size];
  2068.   }
  2069.   char *ptr = buf;
  2070.   for (p = initial_list; p; p = p->next) {
  2071.     strcpy(ptr, itoa(p->pos.to_units()));
  2072.     ptr = strchr(ptr, '\0');
  2073.     *ptr++ = 'u';
  2074.     *ptr = '\0';
  2075.     switch (p->type) {
  2076.     case TAB_LEFT:
  2077.       break;
  2078.     case TAB_RIGHT:
  2079.       *ptr++ = 'R';
  2080.       break;
  2081.     case TAB_CENTER:
  2082.       *ptr++ = 'C';
  2083.       break;
  2084.     case TAB_NONE:
  2085.     default:
  2086.       assert(0);
  2087.     }
  2088.   }
  2089.   if (repeated_list)
  2090.     *ptr++ = TAB_REPEAT_CHAR;
  2091.   for (p = repeated_list; p; p = p->next) {
  2092.     strcpy(ptr, itoa(p->pos.to_units()));
  2093.     ptr = strchr(ptr, '\0');
  2094.     *ptr++ = 'u';
  2095.     *ptr = '\0';
  2096.     switch (p->type) {
  2097.     case TAB_LEFT:
  2098.       break;
  2099.     case TAB_RIGHT:
  2100.       *ptr++ = 'R';
  2101.       break;
  2102.     case TAB_CENTER:
  2103.       *ptr++ = 'C';
  2104.       break;
  2105.     case TAB_NONE:
  2106.     default:
  2107.       assert(0);
  2108.     }
  2109.   }
  2110.   *ptr++ = '\0';
  2111.   return buf;
  2112. }
  2113.  
  2114. tab_stops::tab_stops() : initial_list(0), repeated_list(0)
  2115. {
  2116. }
  2117.  
  2118. tab_stops::tab_stops(const tab_stops &ts) 
  2119.      : initial_list(0), repeated_list(0)
  2120. {
  2121.   tab **p = &initial_list;
  2122.   tab *t = ts.initial_list;
  2123.   while (t) {
  2124.     *p = new tab(t->pos, t->type);
  2125.     t = t->next;
  2126.     p = &(*p)->next;
  2127.   }
  2128.   p = &repeated_list;
  2129.   t = ts.repeated_list;
  2130.   while (t) {
  2131.     *p = new tab(t->pos, t->type);
  2132.     t = t->next;
  2133.     p = &(*p)->next;
  2134.   }
  2135. }
  2136.  
  2137. void tab_stops::clear()
  2138. {
  2139.   while (initial_list) {
  2140.     tab *tem = initial_list;
  2141.     initial_list = initial_list->next;
  2142.     delete tem;
  2143.   }
  2144.   while (repeated_list) {
  2145.     tab *tem = repeated_list;
  2146.     repeated_list = repeated_list->next;
  2147.     delete tem;
  2148.   }
  2149. }
  2150.  
  2151. void tab_stops::add_tab(hunits pos, tab_type type, int repeated)
  2152. {
  2153.   for (tab **p = repeated ? &repeated_list : &initial_list; *p; p = &(*p)->next)
  2154.     ;
  2155.   *p = new tab(pos, type);
  2156. }
  2157.  
  2158.  
  2159. void tab_stops::operator=(const tab_stops &ts)
  2160. {
  2161.   clear();
  2162.   tab **p = &initial_list;
  2163.   tab *t = ts.initial_list;
  2164.   while (t) {
  2165.     *p = new tab(t->pos, t->type);
  2166.     t = t->next;
  2167.     p = &(*p)->next;
  2168.   }
  2169.   p = &repeated_list;
  2170.   t = ts.repeated_list;
  2171.   while (t) {
  2172.     *p = new tab(t->pos, t->type);
  2173.     t = t->next;
  2174.     p = &(*p)->next;
  2175.   }
  2176. }
  2177.     
  2178. void set_tabs()
  2179. {
  2180.   hunits pos;
  2181.   hunits prev_pos = 0;
  2182.   int repeated = 0;
  2183.   tab_stops tabs;
  2184.   while (has_arg()) {
  2185.     if (tok.ch() == TAB_REPEAT_CHAR) {
  2186.       tok.next();
  2187.       repeated = 1;
  2188.       prev_pos = 0;
  2189.     }
  2190.     if (!get_hunits(&pos, 'm', prev_pos))
  2191.       break;
  2192.     if (pos <= prev_pos) {
  2193.       warning(WARN_RANGE, "positions of tab stops must be strictly increasing");
  2194.       continue;
  2195.     }
  2196.     tab_type type = TAB_LEFT;
  2197.     if (tok.ch() == 'C') {
  2198.       tok.next();
  2199.       type = TAB_CENTER;
  2200.     }
  2201.     else if (tok.ch() == 'R') {
  2202.       tok.next();
  2203.       type = TAB_RIGHT;
  2204.     }
  2205.     else if (tok.ch() == 'L') {
  2206.       tok.next();
  2207.     }
  2208.     tabs.add_tab(pos, type, repeated);
  2209.     prev_pos = pos;
  2210.   }
  2211.   curenv->tabs = tabs;
  2212.   skip_line();
  2213. }
  2214.  
  2215. const char *environment::get_tabs()
  2216. {
  2217.   return tabs.to_string();
  2218. }
  2219.  
  2220. #if 0
  2221. tab_stops saved_tabs;
  2222.  
  2223. void tabs_save()
  2224. {
  2225.   saved_tabs = curenv->tabs;
  2226.   skip_line();
  2227. }
  2228.  
  2229. void tabs_restore()
  2230. {
  2231.   curenv->tabs = saved_tabs;
  2232.   skip_line();
  2233. }
  2234. #endif
  2235.  
  2236. tab_type environment::distance_to_next_tab(hunits *distance)
  2237. {
  2238.   return curenv->tabs.distance_to_next_tab(get_input_line_position(), distance);
  2239. }
  2240.  
  2241. void field_characters()
  2242. {
  2243.   field_delimiter_char = get_optional_char();
  2244.   if (field_delimiter_char)
  2245.     padding_indicator_char = get_optional_char();
  2246.   else
  2247.     padding_indicator_char = 0;
  2248.   skip_line();
  2249. }
  2250.  
  2251. void environment::wrap_up_tab()
  2252. {
  2253.   if (!current_tab)
  2254.     return;
  2255.   if (line == 0)
  2256.     start_line();
  2257.   hunits tab_amount;
  2258.   switch (current_tab) {
  2259.   case TAB_RIGHT:
  2260.     tab_amount = tab_distance - tab_width;
  2261.     line = make_tab_node(tab_amount, line);
  2262.     break;
  2263.   case TAB_CENTER:
  2264.     tab_amount = tab_distance - tab_width/2;
  2265.     line = make_tab_node(tab_amount, line);
  2266.     break;
  2267.   case TAB_NONE:
  2268.   case TAB_LEFT:
  2269.   default:
  2270.     assert(0);
  2271.   }
  2272.   width_total += tab_amount;
  2273.   width_total += tab_width;
  2274.   if (current_field) {
  2275.     if (tab_precedes_field) {
  2276.       pre_field_width += tab_amount;
  2277.       tab_precedes_field = 0;
  2278.     }
  2279.     field_distance -= tab_amount;
  2280.   }
  2281.   field_spaces += tab_field_spaces;
  2282.   if (tab_contents != 0) {
  2283.     for (node *tem = tab_contents; tem->next != 0; tem = tem->next)
  2284.       ;
  2285.     tem->next = line;
  2286.     line = tab_contents;
  2287.   }
  2288.   tab_field_spaces = 0;
  2289.   tab_contents = 0;
  2290.   tab_width =  H0;
  2291.   tab_distance = H0;
  2292.   current_tab = TAB_NONE;
  2293. }
  2294.  
  2295. node *environment::make_tab_node(hunits d, node *next)
  2296. {
  2297.   if (leader_node != 0 && d < 0) {
  2298.     error("motion generated by leader cannot be negative");
  2299.     delete leader_node;
  2300.     leader_node = 0;
  2301.   }
  2302.   if (!leader_node)
  2303.     return new hmotion_node(d, next);
  2304.   node *n = new hline_node(d, leader_node, next);
  2305.   leader_node = 0;
  2306.   return n;
  2307. }
  2308.  
  2309. void environment::handle_tab(int is_leader)
  2310. {
  2311.   hunits d;
  2312.   if (current_tab)
  2313.     wrap_up_tab();
  2314.   charinfo *ci = is_leader ? leader_char : tab_char;
  2315.   delete leader_node;
  2316.   leader_node = ci ? make_char_node(ci) : 0;
  2317.   tab_type t = distance_to_next_tab(&d);
  2318.   switch (t) {
  2319.   case TAB_NONE:
  2320.     return;
  2321.   case TAB_LEFT:
  2322.     add_node(make_tab_node(d));
  2323.     return;
  2324.   case TAB_RIGHT:
  2325.   case TAB_CENTER:
  2326.     tab_width = 0;
  2327.     tab_distance = d;
  2328.     tab_contents = 0;
  2329.     current_tab = t;
  2330.     tab_field_spaces = 0;
  2331.     return;
  2332.   default:
  2333.     assert(0);
  2334.   }
  2335. }
  2336.  
  2337. void environment::start_field()
  2338. {
  2339.   assert(!current_field);
  2340.   hunits d;
  2341.   if (distance_to_next_tab(&d) != TAB_NONE) {
  2342.     pre_field_width = get_text_length();
  2343.     field_distance = d;
  2344.     current_field = 1;
  2345.     field_spaces = 0;
  2346.     tab_field_spaces = 0;
  2347.     for (node *p = line; p; p = p->next)
  2348.       p->freeze_space();
  2349.     tab_precedes_field = current_tab != TAB_NONE;
  2350.   }
  2351.   else
  2352.     error("zero field width");
  2353. }
  2354.  
  2355. void environment::wrap_up_field()
  2356. {
  2357.   hunits padding = field_distance - (get_text_length() - pre_field_width);
  2358.   if (current_tab && tab_field_spaces != 0) {
  2359.     hunits tab_padding = scale(padding, 
  2360.                    tab_field_spaces, 
  2361.                    field_spaces + tab_field_spaces);
  2362.     padding -= tab_padding;
  2363.     distribute_space(tab_contents, tab_field_spaces, tab_padding, 1);
  2364.     tab_field_spaces = 0;
  2365.     tab_width += tab_padding;
  2366.   }
  2367.   if (field_spaces != 0) {
  2368.     distribute_space(line, field_spaces, padding, 1);
  2369.     width_total += padding;
  2370.     if (current_tab) {
  2371.       // the start of the tab has been moved to the right by padding, so
  2372.       tab_distance -= padding;
  2373.       if (tab_distance <= H0) {
  2374.     // use the next tab stop instead
  2375.     current_tab = tabs.distance_to_next_tab(get_input_line_position()
  2376.                         - tab_width,
  2377.                         &tab_distance);
  2378.     if (current_tab == TAB_NONE || current_tab == TAB_LEFT) {
  2379.       width_total += tab_width;
  2380.       if (current_tab == TAB_LEFT) {
  2381.         line = make_tab_node(tab_distance, line);
  2382.         width_total += tab_distance;
  2383.         current_tab = TAB_NONE;
  2384.       }
  2385.       if (tab_contents != 0) {
  2386.         for (node *tem = tab_contents; tem->next != 0; tem = tem->next)
  2387.           ;
  2388.         tem->next = line;
  2389.         line = tab_contents;
  2390.         tab_contents = 0;
  2391.       }
  2392.       tab_width = H0;
  2393.       tab_distance = H0;
  2394.     }
  2395.       }
  2396.     }
  2397.   }
  2398.   current_field = 0;
  2399. }
  2400.  
  2401. typedef int (environment::*INT_FUNCP)();
  2402. typedef vunits (environment::*VUNITS_FUNCP)();
  2403. typedef hunits (environment::*HUNITS_FUNCP)();
  2404. typedef const char *(environment::*STRING_FUNCP)();
  2405.  
  2406. class int_env_reg : public reg {
  2407.   INT_FUNCP func;
  2408.  public:
  2409.   int_env_reg(INT_FUNCP);
  2410.   const char *get_string();
  2411.   int get_value(units *val);
  2412. };
  2413.  
  2414. class vunits_env_reg : public reg {
  2415.   VUNITS_FUNCP func;
  2416.  public:
  2417.   vunits_env_reg(VUNITS_FUNCP f);
  2418.   const char *get_string();
  2419.   int get_value(units *val);
  2420. };
  2421.  
  2422.  
  2423. class hunits_env_reg : public reg {
  2424.   HUNITS_FUNCP func;
  2425.  public:
  2426.   hunits_env_reg(HUNITS_FUNCP f);
  2427.   const char *get_string();
  2428.   int get_value(units *val);
  2429. };
  2430.  
  2431. class string_env_reg : public reg {
  2432.   STRING_FUNCP func;
  2433. public:
  2434.   string_env_reg(STRING_FUNCP);
  2435.   const char *get_string();
  2436. };
  2437.  
  2438. int_env_reg::int_env_reg(INT_FUNCP f) : func(f)
  2439. {
  2440. }
  2441.  
  2442. int int_env_reg::get_value(units *val)
  2443. {
  2444.   *val = (curenv->*func)();
  2445.   return 1;
  2446. }
  2447.  
  2448. const char *int_env_reg::get_string()
  2449. {
  2450.   return itoa((curenv->*func)());
  2451. }
  2452.  
  2453. vunits_env_reg::vunits_env_reg(VUNITS_FUNCP f) : func(f)
  2454. {
  2455. }
  2456.  
  2457. int vunits_env_reg::get_value(units *val)
  2458. {
  2459.   *val = (curenv->*func)().to_units();
  2460.   return 1;
  2461. }
  2462.  
  2463. const char *vunits_env_reg::get_string()
  2464. {
  2465.   return itoa((curenv->*func)().to_units());
  2466. }
  2467.  
  2468. hunits_env_reg::hunits_env_reg(HUNITS_FUNCP f) : func(f)
  2469. {
  2470. }
  2471.  
  2472. int hunits_env_reg::get_value(units *val)
  2473. {
  2474.   *val = (curenv->*func)().to_units();
  2475.   return 1;
  2476. }
  2477.  
  2478. const char *hunits_env_reg::get_string()
  2479. {
  2480.   return itoa((curenv->*func)().to_units());
  2481. }
  2482.  
  2483. string_env_reg::string_env_reg(STRING_FUNCP f) : func(f)
  2484. {
  2485. }
  2486.  
  2487. const char *string_env_reg::get_string()
  2488. {
  2489.   return (curenv->*func)();
  2490. }
  2491.  
  2492. class horizontal_place_reg : public general_reg {
  2493. public:
  2494.   horizontal_place_reg();
  2495.   int get_value(units *);
  2496.   void set_value(units);
  2497. };
  2498.  
  2499. horizontal_place_reg::horizontal_place_reg()
  2500. {
  2501. }
  2502.  
  2503. int horizontal_place_reg::get_value(units *res)
  2504. {
  2505.   *res = curenv->get_input_line_position().to_units();
  2506.   return 1;
  2507. }
  2508.  
  2509. void horizontal_place_reg::set_value(units n)
  2510. {
  2511.   curenv->set_input_line_position(hunits(n));
  2512. }
  2513.  
  2514. const char *environment::get_font_family_string()
  2515. {
  2516.   return family->nm.contents();
  2517. }
  2518.  
  2519. const char *environment::get_name_string()
  2520. {
  2521.   return name.contents();
  2522. }
  2523.  
  2524. // Convert a quantity in scaled points to ascii decimal fraction.
  2525.  
  2526. const char *sptoa(int sp)
  2527. {
  2528.   assert(sp > 0);
  2529.   assert(sizescale > 0);
  2530.   if (sizescale == 1)
  2531.     return itoa(sp);
  2532.   if (sp % sizescale == 0)
  2533.     return itoa(sp/sizescale);
  2534.   // See if 1/sizescale is exactly representable as a decimal fraction,
  2535.   // ie its only prime factors are 2 and 5.
  2536.   int n = sizescale;
  2537.   int power2 = 0;
  2538.   while ((n & 1) == 0) {
  2539.     n >>= 1;
  2540.     power2++;
  2541.   }
  2542.   int power5 = 0;
  2543.   while ((n % 5) == 0) {
  2544.     n /= 5;
  2545.     power5++;
  2546.   }
  2547.   if (n == 1) {
  2548.     int decimal_point = power5 > power2 ? power5 : power2;
  2549.     if (decimal_point <= 10) {
  2550.       int factor = 1;
  2551.       int t;
  2552.       for (t = decimal_point - power2; --t >= 0;)
  2553.     factor *= 2;
  2554.       for (t = decimal_point - power5; --t >= 0;)
  2555.     factor *= 5;
  2556.       if (factor == 1 || sp <= INT_MAX/factor)
  2557.     return iftoa(sp*factor, decimal_point);
  2558.     }
  2559.   }
  2560.   double s = double(sp)/double(sizescale);
  2561.   double factor = 10.0;
  2562.   double val = s;
  2563.   int decimal_point = 0;
  2564.   do  {
  2565.     double v = ceil(s*factor);
  2566.     if (v > INT_MAX)
  2567.       break;
  2568.     val = v;
  2569.     factor *= 10.0;
  2570.   } while (++decimal_point < 10);
  2571.   return iftoa(int(val), decimal_point);
  2572. }
  2573.  
  2574. const char *environment::get_point_size_string()
  2575. {
  2576.   return sptoa(curenv->get_point_size());
  2577. }
  2578.  
  2579. const char *environment::get_requested_point_size_string()
  2580. {
  2581.   return sptoa(curenv->get_requested_point_size());
  2582. }
  2583.  
  2584. #define init_int_env_reg(name, func) \
  2585.   number_reg_dictionary.define(name, new int_env_reg(&environment::func))
  2586.  
  2587. #define init_vunits_env_reg(name, func) \
  2588.   number_reg_dictionary.define(name, new vunits_env_reg(&environment::func))
  2589.  
  2590. #define init_hunits_env_reg(name, func) \
  2591.   number_reg_dictionary.define(name, new hunits_env_reg(&environment::func))
  2592.  
  2593. #define init_string_env_reg(name, func) \
  2594.   number_reg_dictionary.define(name, new string_env_reg(&environment::func))
  2595.  
  2596. void init_env_requests()
  2597. {
  2598.   init_request("it", input_trap);
  2599.   init_request("ad", adjust);
  2600.   init_request("na", no_adjust);
  2601.   init_request("ev", environment_switch);
  2602.   init_request("lt", title_length);
  2603.   init_request("ps", point_size);
  2604.   init_request("ft", font_change);
  2605.   init_request("fam", family_change);
  2606.   init_request("ss", space_size);
  2607.   init_request("fi", fill);
  2608.   init_request("nf", no_fill);
  2609.   init_request("ce", center);
  2610.   init_request("rj", right_justify);
  2611.   init_request("vs", vertical_spacing);
  2612.   init_request("ls", line_spacing);
  2613.   init_request("ll", line_length);
  2614.   init_request("in", indent);
  2615.   init_request("ti", temporary_indent);
  2616.   init_request("ul", underline);
  2617.   init_request("cu", underline);
  2618.   init_request("cc", control_char);
  2619.   init_request("c2", no_break_control_char);
  2620.   init_request("br", break_request);
  2621.   init_request("tl", title);
  2622.   init_request("ta", set_tabs);
  2623.   init_request("fc", field_characters);
  2624.   init_request("mc", margin_character);
  2625.   init_request("nn", no_number);
  2626.   init_request("nm", number_lines);
  2627.   init_request("tc", tab_character);
  2628.   init_request("lc", leader_character);
  2629.   init_request("hy", hyphenate_request);
  2630.   init_request("hc", hyphen_char);
  2631.   init_request("nh", no_hyphenate);
  2632.   init_request("hlm", hyphen_line_max_request);
  2633. #ifdef WIDOW_CONTROL
  2634.   init_request("wdc", widow_control_request);
  2635. #endif /* WIDOW_CONTROL */
  2636. #if 0
  2637.   init_request("tas", tabs_save);
  2638.   init_request("tar", tabs_restore);
  2639. #endif  
  2640.   init_request("hys", hyphenation_space_request);
  2641.   init_request("hym", hyphenation_margin_request);
  2642.   init_int_env_reg(".f", get_font);
  2643.   init_int_env_reg(".b", get_bold);
  2644.   init_hunits_env_reg(".i", get_indent);
  2645.   init_hunits_env_reg(".in", get_saved_indent);
  2646.   init_int_env_reg(".j", get_adjust_mode);
  2647.   init_hunits_env_reg(".k", get_text_length);
  2648.   init_hunits_env_reg(".l", get_line_length);
  2649.   init_hunits_env_reg(".ll", get_saved_line_length);
  2650.   init_int_env_reg(".L", get_line_spacing);
  2651.   init_hunits_env_reg(".n", get_prev_text_length);
  2652.   init_string_env_reg(".s", get_point_size_string);
  2653.   init_string_env_reg(".sr", get_requested_point_size_string);
  2654.   init_int_env_reg(".ps", get_point_size);
  2655.   init_int_env_reg(".psr", get_requested_point_size);
  2656.   init_int_env_reg(".u", get_fill);
  2657.   init_vunits_env_reg(".v", get_vertical_spacing);
  2658.   init_hunits_env_reg(".w", get_prev_char_width);
  2659.   init_int_env_reg(".ss", get_space_size);
  2660.   init_int_env_reg(".sss", get_sentence_space_size);
  2661.   init_string_env_reg(".fam", get_font_family_string);
  2662.   init_string_env_reg(".ev", get_name_string);
  2663.   init_int_env_reg(".hy", get_hyphenation_flags);
  2664.   init_int_env_reg(".hlm", get_hyphen_line_max);
  2665.   init_int_env_reg(".hlc", get_hyphen_line_count);
  2666.   init_hunits_env_reg(".lt", get_title_length);
  2667.   init_string_env_reg(".tabs", get_tabs);
  2668.   init_hunits_env_reg(".csk", get_prev_char_skew);
  2669.   init_vunits_env_reg(".cht", get_prev_char_height);
  2670.   init_vunits_env_reg(".cdp", get_prev_char_depth);
  2671.   init_int_env_reg(".ce", get_center_lines);
  2672.   init_int_env_reg(".rj", get_right_justify_lines);
  2673.   init_hunits_env_reg(".hys", get_hyphenation_space);
  2674.   init_hunits_env_reg(".hym", get_hyphenation_margin);
  2675.   number_reg_dictionary.define("ln", new variable_reg(&next_line_number));
  2676.   number_reg_dictionary.define("ct", new variable_reg(&ct_reg_contents));
  2677.   number_reg_dictionary.define("sb", new variable_reg(&sb_reg_contents));
  2678.   number_reg_dictionary.define("st", new variable_reg(&st_reg_contents));
  2679.   number_reg_dictionary.define("rsb", new variable_reg(&rsb_reg_contents));
  2680.   number_reg_dictionary.define("rst", new variable_reg(&rst_reg_contents));
  2681.   number_reg_dictionary.define("ssc", new variable_reg(&ssc_reg_contents));
  2682.   number_reg_dictionary.define("skw", new variable_reg(&skw_reg_contents));
  2683.   number_reg_dictionary.define("hp", new horizontal_place_reg);
  2684. }
  2685.  
  2686. // Hyphenation - TeX's hyphenation algorithm with a less fancy implementation.
  2687.  
  2688. const int WORD_MAX = 1024;
  2689.  
  2690. dictionary exception_dictionary(501);
  2691.  
  2692. static void hyphen_word()
  2693. {
  2694.   char buf[WORD_MAX + 1];
  2695.   unsigned char pos[WORD_MAX + 2];
  2696.   for (;;) {
  2697.     tok.skip();
  2698.     if (tok.newline() || tok.eof())
  2699.       break;
  2700.     int i = 0;
  2701.     int npos = 0;
  2702.     while (i < WORD_MAX && !tok.space() && !tok.newline() && !tok.eof()) {
  2703.       charinfo *ci = tok.get_char(1);
  2704.       if (ci == 0) {
  2705.     skip_line();
  2706.     return;
  2707.       }
  2708.       tok.next();
  2709.       if (ci->get_ascii_code() == '-') {
  2710.     if (i > 0 && (npos == 0 || pos[npos - 1] != i))
  2711.       pos[npos++] = i;
  2712.       }
  2713.       else {
  2714.     int c = ci->get_hyphenation_code();
  2715.     if (c == 0)
  2716.       break;
  2717.     buf[i++] = c;
  2718.       }
  2719.     }
  2720.     if (i > 0) {
  2721.       pos[npos] = 0;
  2722.       buf[i] = 0;
  2723.       unsigned char *tem = new unsigned char[npos + 1];
  2724.       memcpy(tem, pos, npos+1);
  2725.       tem = (unsigned char *)exception_dictionary.lookup(symbol(buf), tem);
  2726.       if (tem)
  2727.     delete tem;
  2728.     }
  2729.   }
  2730.   skip_line();
  2731. }
  2732.  
  2733. struct trie_node;
  2734.  
  2735. class trie {
  2736.   trie_node *tp;
  2737.   virtual void do_match(int len, void *val) = 0;
  2738. public:
  2739.   trie() : tp(0) {}
  2740.   void insert(unsigned char *, int, void *);
  2741.   // find calls do_match for each match it finds
  2742.   void find(unsigned char *pat, int patlen);
  2743.   void clear();
  2744. };
  2745.  
  2746. struct trie_node {
  2747.   unsigned char c;
  2748.   trie_node *down;
  2749.   trie_node *right;
  2750.   void *val;
  2751.   trie_node(unsigned char, trie_node *);
  2752.   ~trie_node();
  2753. };
  2754.  
  2755. trie_node::trie_node(unsigned char ch, trie_node *p) 
  2756. : c(ch), right(p), down(0), val(0)
  2757. {
  2758. }
  2759.  
  2760. trie_node::~trie_node()
  2761. {
  2762.   if (down)
  2763.     delete down;
  2764.   if (right)
  2765.     delete right;
  2766.   if (val)
  2767.     delete val;
  2768. }
  2769.  
  2770. void trie::clear()
  2771. {
  2772.   if (tp) {
  2773.     delete tp;
  2774.     tp = 0;
  2775.   }
  2776. }
  2777.  
  2778. void trie::insert(unsigned char *pat, int patlen, void *val)
  2779. {
  2780.   trie_node **p = &tp;
  2781.   assert(patlen > 0 && pat != 0);
  2782.   for (;;) {
  2783.     while (*p != 0 && (*p)->c < pat[0])
  2784.       p = &((*p)->right);
  2785.     if (*p == 0 || (*p)->c != pat[0])
  2786.       *p = new trie_node(pat[0], *p);
  2787.     if (--patlen == 0) {
  2788.       (*p)->val = val;
  2789.       break;
  2790.     }
  2791.     ++pat;
  2792.     p = &((*p)->down);
  2793.   }
  2794. }
  2795.  
  2796. void trie::find(unsigned char *pat, int patlen)
  2797. {
  2798.   trie_node *p = tp;
  2799.   for (int i = 0; p != 0 && i < patlen; i++) {
  2800.     while (p != 0 && p->c < pat[i])
  2801.       p = p->right;
  2802.     if (p != 0 && p->c == pat[i]) {
  2803.       if (p->val != 0)
  2804.     do_match(i+1, p->val);
  2805.       p = p->down;
  2806.     }
  2807.     else
  2808.       break;
  2809.   }
  2810. }
  2811.  
  2812. class hyphen_trie : private trie {
  2813.   int *h;
  2814.   void do_match(int i, void *v);
  2815.   void insert_pattern(unsigned char *pat, int patlen, int *num);
  2816. public:
  2817.   hyphen_trie() {}
  2818.   void hyphenate(unsigned char *word, int len, int *hyphens);
  2819.   void read_patterns_file(const char *name);
  2820. };
  2821.  
  2822. struct operation {
  2823.   operation *next;
  2824.   short distance;
  2825.   short num;
  2826.   operation(int, int, operation *);
  2827. };
  2828.  
  2829. operation::operation(int i, int j, operation *op)
  2830. : num(i), distance(j), next(op)
  2831. {
  2832. }
  2833.  
  2834. void hyphen_trie::insert_pattern(unsigned char *pat, int patlen, int *num)
  2835. {
  2836.   operation *op = 0;
  2837.   for (int i = 0; i < patlen+1; i++)
  2838.     if (num[i] != 0)
  2839.       op = new operation(num[i], patlen - i, op);
  2840.   insert(pat, patlen, op);
  2841. }
  2842.  
  2843. void hyphen_trie::hyphenate(unsigned char *word, int len, int *hyphens)
  2844. {
  2845.   for (int j = 0; j < len+1; j++)
  2846.     hyphens[j] = 0;
  2847.   for (j = 0; j < len - 1; j++) {
  2848.     h = hyphens + j;
  2849.     find(word + j, len - j);
  2850.   }
  2851. }
  2852.  
  2853. inline int max(int m, int n)
  2854. {
  2855.   return m > n ? m : n;
  2856. }
  2857.  
  2858. void hyphen_trie::do_match(int i, void *v)
  2859. {
  2860.   operation *op = (operation *)v;
  2861.   while (op != 0) {
  2862.     h[i - op->distance] = max(h[i - op->distance], op->num);
  2863.     op = op->next;
  2864.   }
  2865. }
  2866.  
  2867.  
  2868. void hyphen_trie::read_patterns_file(const char *name)
  2869. {
  2870.   clear();
  2871.   unsigned char buf[WORD_MAX];
  2872.   int num[WORD_MAX+1];
  2873.   FILE *fp = fopen(name, "r");
  2874.   if (fp == 0)
  2875.     fatal("can't open hyphenation patterns file `%1': %2",
  2876.       name, strerror(errno));
  2877.   int c = getc(fp);
  2878.   for (;;) {
  2879.     while (c != EOF && csspace(c))
  2880.       c = getc(fp);
  2881.     if (c == EOF)
  2882.       break;
  2883.     int i = 0;
  2884.     num[0] = 0;
  2885.     do {
  2886.       if (csdigit(c))
  2887.     num[i] = c - '0';
  2888.       else {
  2889.     buf[i++] = c;
  2890.     num[i] = 0;
  2891.       }
  2892.       c = getc(fp);
  2893.     } while (i < WORD_MAX && c != EOF && !csspace(c));
  2894.     insert_pattern(buf, i, num);
  2895.   }
  2896.   fclose(fp);
  2897. }
  2898.  
  2899. hyphen_trie ht;
  2900.  
  2901. void hyphenate(hyphen_list *h, unsigned flags)
  2902. {
  2903.   while (h && h->hyphenation_code == 0)
  2904.     h = h->next;
  2905.   int len = 0;
  2906.   char hbuf[WORD_MAX+2];
  2907.   char *buf = hbuf + 1;
  2908.   for (hyphen_list *tem = h; tem && len < WORD_MAX; tem = tem->next) {
  2909.     if (tem->hyphenation_code != 0)
  2910.       buf[len++] = tem->hyphenation_code;
  2911.     else
  2912.       break;
  2913.   }
  2914.   if (len > 2) {
  2915.     buf[len] = 0;
  2916.     unsigned char *pos = (unsigned char *)exception_dictionary.lookup(buf);
  2917.     if (pos != 0) {
  2918.       int j = 0;
  2919.       int i = 1;
  2920.       for (tem = h; tem != 0; tem = tem->next, i++)
  2921.     if (pos[j] == i) {
  2922.       tem->hyphen = 1;
  2923.       j++;
  2924.     }
  2925.     }
  2926.     else {
  2927.       hbuf[len+1] = '.';
  2928.       int num[WORD_MAX+3];
  2929.       ht.hyphenate((unsigned char *)hbuf, len+2, num);
  2930.       int i;
  2931.       num[2] = 0;
  2932.       if (flags & 8)
  2933.     num[3] = 0;
  2934.       if (flags & 4)
  2935.     --len;
  2936.       for (i = 2, tem = h; i < len && tem; tem = tem->next, i++)
  2937.     if (num[i] & 1)
  2938.       tem->hyphen = 1;
  2939.     }
  2940.   }
  2941. }
  2942.  
  2943. void read_hyphen_file(const char *name)
  2944. {
  2945.   ht.read_patterns_file(name);
  2946. }
  2947.  
  2948. void init_hyphen_requests()
  2949. {
  2950.   init_request("hw", hyphen_word);
  2951. }
  2952.