diff options
Diffstat (limited to 'contrib/groff/troff/env.cc')
-rw-r--r-- | contrib/groff/troff/env.cc | 3215 |
1 files changed, 0 insertions, 3215 deletions
diff --git a/contrib/groff/troff/env.cc b/contrib/groff/troff/env.cc deleted file mode 100644 index fbea78edab51..000000000000 --- a/contrib/groff/troff/env.cc +++ /dev/null @@ -1,3215 +0,0 @@ -// -*- C++ -*- -/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. - Written by James Clark (jjc@jclark.com) - -This file is part of groff. - -groff is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free -Software Foundation; either version 2, or (at your option) any later -version. - -groff is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. - -You should have received a copy of the GNU General Public License along -with groff; see the file COPYING. If not, write to the Free Software -Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - -#include "troff.h" -#include "symbol.h" -#include "dictionary.h" -#include "hvunits.h" -#include "env.h" -#include "request.h" -#include "node.h" -#include "token.h" -#include "div.h" -#include "reg.h" -#include "charinfo.h" -#include "searchpath.h" -#include "macropath.h" -#include <math.h> - -symbol default_family("T"); - -enum { ADJUST_LEFT = 0, ADJUST_BOTH = 1, ADJUST_CENTER = 3, ADJUST_RIGHT = 5 }; - -enum { HYPHEN_LAST_LINE = 2, HYPHEN_LAST_CHARS = 4, HYPHEN_FIRST_CHARS = 8 }; - -struct env_list { - environment *env; - env_list *next; - env_list(environment *e, env_list *p) : env(e), next(p) {} -}; - -env_list *env_stack; -const int NENVIRONMENTS = 10; -environment *env_table[NENVIRONMENTS]; -dictionary env_dictionary(10); -environment *curenv; -static int next_line_number = 0; - -charinfo *field_delimiter_char; -charinfo *padding_indicator_char; - -int translate_space_to_dummy = 0; - -class pending_output_line { - node *nd; - int no_fill; - vunits vs; - vunits post_vs; - hunits width; -#ifdef WIDOW_CONTROL - int last_line; // Is it the last line of the paragraph? -#endif /* WIDOW_CONTROL */ -public: - pending_output_line *next; - - pending_output_line(node *, int, vunits, vunits, hunits, - pending_output_line * = 0); - ~pending_output_line(); - int output(); - -#ifdef WIDOW_CONTROL - friend void environment::mark_last_line(); - friend void environment::output(node *, int, vunits, vunits, hunits); -#endif /* WIDOW_CONTROL */ -}; - -pending_output_line::pending_output_line(node *n, int nf, vunits v, vunits pv, - hunits w, pending_output_line *p) -: nd(n), no_fill(nf), vs(v), post_vs(pv), width(w), -#ifdef WIDOW_CONTROL - last_line(0), -#endif /* WIDOW_CONTROL */ - next(p) -{ -} - -pending_output_line::~pending_output_line() -{ - delete_node_list(nd); -} - -int pending_output_line::output() -{ - if (trap_sprung_flag) - return 0; -#ifdef WIDOW_CONTROL - if (next && next->last_line && !no_fill) { - curdiv->need(vs + post_vs + vunits(vresolution)); - if (trap_sprung_flag) { - next->last_line = 0; // Try to avoid infinite loops. - return 0; - } - } -#endif - curdiv->output(nd, no_fill, vs, post_vs, width); - nd = 0; - return 1; -} - -void environment::output(node *nd, int no_fill, vunits vs, vunits post_vs, - hunits width) -{ -#ifdef WIDOW_CONTROL - while (pending_lines) { - if (widow_control && !pending_lines->no_fill && !pending_lines->next) - break; - if (!pending_lines->output()) - break; - pending_output_line *tem = pending_lines; - pending_lines = pending_lines->next; - delete tem; - } -#else /* WIDOW_CONTROL */ - output_pending_lines(); -#endif /* WIDOW_CONTROL */ - if (!trap_sprung_flag && !pending_lines -#ifdef WIDOW_CONTROL - && (!widow_control || no_fill) -#endif /* WIDOW_CONTROL */ - ) - curdiv->output(nd, no_fill, vs, post_vs, width); - else { - pending_output_line **p; - for (p = &pending_lines; *p; p = &(*p)->next) - ; - *p = new pending_output_line(nd, no_fill, vs, post_vs, width); - } -} - -// a line from .tl goes at the head of the queue - -void environment::output_title(node *nd, int no_fill, vunits vs, - vunits post_vs, hunits width) -{ - if (!trap_sprung_flag) - curdiv->output(nd, no_fill, vs, post_vs, width); - else - pending_lines = new pending_output_line(nd, no_fill, vs, post_vs, width, - pending_lines); -} - -void environment::output_pending_lines() -{ - while (pending_lines && pending_lines->output()) { - pending_output_line *tem = pending_lines; - pending_lines = pending_lines->next; - delete tem; - } -} - -#ifdef WIDOW_CONTROL - -void environment::mark_last_line() -{ - if (!widow_control || !pending_lines) - return; - for (pending_output_line *p = pending_lines; p->next; p = p->next) - ; - if (!p->no_fill) - p->last_line = 1; -} - -void widow_control_request() -{ - int n; - if (has_arg() && get_integer(&n)) - curenv->widow_control = n != 0; - else - curenv->widow_control = 1; - skip_line(); -} - -#endif /* WIDOW_CONTROL */ - -/* font_size functions */ - -size_range *font_size::size_table = 0; -int font_size::nranges = 0; - -extern "C" { - -int compare_ranges(const void *p1, const void *p2) -{ - return ((size_range *)p1)->min - ((size_range *)p2)->min; -} - -} - -void font_size::init_size_table(int *sizes) -{ - nranges = 0; - while (sizes[nranges*2] != 0) - nranges++; - assert(nranges > 0); - size_table = new size_range[nranges]; - for (int i = 0; i < nranges; i++) { - size_table[i].min = sizes[i*2]; - size_table[i].max = sizes[i*2 + 1]; - } - qsort(size_table, nranges, sizeof(size_range), compare_ranges); -} - -font_size::font_size(int sp) -{ - for (int i = 0; i < nranges; i++) { - if (sp < size_table[i].min) { - if (i > 0 && size_table[i].min - sp >= sp - size_table[i - 1].max) - p = size_table[i - 1].max; - else - p = size_table[i].min; - return; - } - if (sp <= size_table[i].max) { - p = sp; - return; - } - } - p = size_table[nranges - 1].max; -} - -int font_size::to_units() -{ - return scale(p, units_per_inch, sizescale*72); -} - -// we can't do this in a static constructor because various dictionaries -// have to get initialized first - -void init_environments() -{ - curenv = env_table[0] = new environment("0"); -} - -void tab_character() -{ - curenv->tab_char = get_optional_char(); - skip_line(); -} - -void leader_character() -{ - curenv->leader_char = get_optional_char(); - skip_line(); -} - -void environment::add_char(charinfo *ci) -{ - if (interrupted) - ; - // don't allow fields in dummy environments - else if (ci == field_delimiter_char && !dummy) { - if (current_field) - wrap_up_field(); - else - start_field(); - } - else if (current_field && ci == padding_indicator_char) - add_padding(); - else if (current_tab) { - if (tab_contents == 0) - tab_contents = new line_start_node; - if (ci != hyphen_indicator_char) - tab_contents = tab_contents->add_char(ci, this, &tab_width); - else - tab_contents = tab_contents->add_discretionary_hyphen(); - } - else { - if (line == 0) - start_line(); - if (ci != hyphen_indicator_char) - line = line->add_char(ci, this, &width_total); - else - line = line->add_discretionary_hyphen(); - } -} - -node *environment::make_char_node(charinfo *ci) -{ - return make_node(ci, this); -} - -void environment::add_node(node *n) -{ - assert(n != 0); - if (current_tab || current_field) - n->freeze_space(); - if (interrupted) { - delete n; - } - else if (current_tab) { - n->next = tab_contents; - tab_contents = n; - tab_width += n->width(); - } - else { - if (line == 0) { - if (discarding && n->discardable()) { - // XXX possibly: input_line_start -= n->width(); - delete n; - return; - } - start_line(); - } - width_total += n->width(); - space_total += n->nspaces(); - n->next = line; - line = n; - } -} - - -void environment::add_hyphen_indicator() -{ - if (current_tab || interrupted || current_field - || hyphen_indicator_char != 0) - return; - if (line == 0) - start_line(); - line = line->add_discretionary_hyphen(); -} - -int environment::get_hyphenation_flags() -{ - return hyphenation_flags; -} - -int environment::get_hyphen_line_max() -{ - return hyphen_line_max; -} - -int environment::get_hyphen_line_count() -{ - return hyphen_line_count; -} - -int environment::get_center_lines() -{ - return center_lines; -} - -int environment::get_right_justify_lines() -{ - return right_justify_lines; -} - -void environment::add_italic_correction() -{ - if (current_tab) { - if (tab_contents) - tab_contents = tab_contents->add_italic_correction(&tab_width); - } - else if (line) - line = line->add_italic_correction(&width_total); -} - -void environment::space_newline() -{ - assert(!current_tab && !current_field); - if (interrupted) - return; - hunits x = H0; - if (!translate_space_to_dummy) { - x = env_space_width(this); - if (node_list_ends_sentence(line) == 1) - x += env_sentence_space_width(this); - } - if (line != 0 && line->merge_space(x)) { - width_total += x; - return; - } - add_node(new word_space_node(x)); - possibly_break_line(spread_flag); - spread_flag = 0; -} - -void environment::space() -{ - if (interrupted) - return; - if (current_field && padding_indicator_char == 0) { - add_padding(); - return; - } - hunits x = translate_space_to_dummy ? H0 : env_space_width(this); - node *p = current_tab ? tab_contents : line; - hunits *tp = current_tab ? &tab_width : &width_total; - if (p && p->nspaces() == 1 && p->width() == x - && node_list_ends_sentence(p->next) == 1) { - hunits xx = translate_space_to_dummy ? H0 : env_sentence_space_width(this); - if (p->merge_space(xx)) { - *tp += xx; - return; - } - } - if (p && p->merge_space(x)) { - *tp += x; - return; - } - add_node(new word_space_node(x)); - possibly_break_line(spread_flag); - spread_flag = 0; -} - -void environment::set_font(symbol nm) -{ - if (interrupted) - return; - if (nm == symbol("P")) { - if (family->make_definite(prev_fontno) < 0) - return; - int tem = fontno; - fontno = prev_fontno; - prev_fontno = tem; - } - else { - prev_fontno = fontno; - int n = symbol_fontno(nm); - if (n < 0) { - n = next_available_font_position(); - if (!mount_font(n, nm)) - return; - } - if (family->make_definite(n) < 0) - return; - fontno = n; - } -} - -void environment::set_font(int n) -{ - if (interrupted) - return; - if (is_good_fontno(n)) { - prev_fontno = fontno; - fontno = n; - } - else - warning(WARN_FONT, "bad font number"); -} - -void environment::set_family(symbol fam) -{ - if (fam.is_null()) { - if (prev_family->make_definite(fontno) < 0) - return; - font_family *tem = family; - family = prev_family; - prev_family = tem; - } - else { - font_family *f = lookup_family(fam); - if (f->make_definite(fontno) < 0) - return; - prev_family = family; - family = f; - } -} - -void environment::set_size(int n) -{ - if (interrupted) - return; - if (n == 0) { - font_size temp = prev_size; - prev_size = size; - size = temp; - int temp2 = prev_requested_size; - prev_requested_size = requested_size; - requested_size = temp2; - } - else { - prev_size = size; - size = font_size(n); - prev_requested_size = requested_size; - requested_size = n; - } -} - -void environment::set_char_height(int n) -{ - if (interrupted) - return; - if (n == requested_size || n <= 0) - char_height = 0; - else - char_height = n; -} - -void environment::set_char_slant(int n) -{ - if (interrupted) - return; - char_slant = n; -} - -environment::environment(symbol nm) -: dummy(0), - prev_line_length((units_per_inch*13)/2), - line_length((units_per_inch*13)/2), - prev_title_length((units_per_inch*13)/2), - title_length((units_per_inch*13)/2), - prev_size(sizescale*10), - size(sizescale*10), - requested_size(sizescale*10), - prev_requested_size(sizescale*10), - char_height(0), - char_slant(0), - space_size(12), - sentence_space_size(12), - adjust_mode(ADJUST_BOTH), - fill(1), - interrupted(0), - prev_line_interrupted(0), - center_lines(0), - right_justify_lines(0), - prev_vertical_spacing(points_to_units(12)), - vertical_spacing(points_to_units(12)), - prev_post_vertical_spacing(0), - post_vertical_spacing(0), - prev_line_spacing(1), - line_spacing(1), - prev_indent(0), - indent(0), - temporary_indent(0), - have_temporary_indent(0), - underline_lines(0), - input_trap_count(0), - line(0), - prev_text_length(0), - width_total(0), - space_total(0), - input_line_start(0), - tabs(units_per_inch/2, TAB_LEFT), - current_tab(TAB_NONE), - leader_node(0), - tab_char(0), - leader_char(charset_table['.']), - current_field(0), - discarding(0), - spread_flag(0), - margin_character_flags(0), - margin_character_node(0), - margin_character_distance(points_to_units(10)), - numbering_nodes(0), - number_text_separation(1), - line_number_indent(0), - line_number_multiple(1), - no_number_count(0), - hyphenation_flags(1), - hyphen_line_count(0), - hyphen_line_max(-1), - hyphenation_space(H0), - hyphenation_margin(H0), - composite(0), - pending_lines(0), -#ifdef WIDOW_CONTROL - widow_control(0), -#endif /* WIDOW_CONTROL */ - name(nm), - control_char('.'), - no_break_control_char('\''), - hyphen_indicator_char(0) -{ - prev_family = family = lookup_family(default_family); - prev_fontno = fontno = 1; - if (!is_good_fontno(1)) - fatal("font number 1 not a valid font"); - if (family->make_definite(1) < 0) - fatal("invalid default family `%1'", default_family.contents()); - prev_fontno = fontno; -} - -environment::environment(const environment *e) -: dummy(1), - prev_line_length(e->prev_line_length), - line_length(e->line_length), - prev_title_length(e->prev_title_length), - title_length(e->title_length), - prev_size(e->prev_size), - size(e->size), - requested_size(e->requested_size), - prev_requested_size(e->prev_requested_size), - char_height(e->char_height), - char_slant(e->char_slant), - prev_fontno(e->prev_fontno), - fontno(e->fontno), - prev_family(e->prev_family), - family(e->family), - space_size(e->space_size), - sentence_space_size(e->sentence_space_size), - adjust_mode(e->adjust_mode), - fill(e->fill), - interrupted(0), - prev_line_interrupted(0), - center_lines(0), - right_justify_lines(0), - prev_vertical_spacing(e->prev_vertical_spacing), - vertical_spacing(e->vertical_spacing), - prev_post_vertical_spacing(e->prev_post_vertical_spacing), - post_vertical_spacing(e->post_vertical_spacing), - prev_line_spacing(e->prev_line_spacing), - line_spacing(e->line_spacing), - prev_indent(e->prev_indent), - indent(e->indent), - temporary_indent(0), - have_temporary_indent(0), - underline_lines(0), - input_trap_count(0), - line(0), - prev_text_length(e->prev_text_length), - width_total(0), - space_total(0), - input_line_start(0), - tabs(e->tabs), - current_tab(TAB_NONE), - leader_node(0), - tab_char(e->tab_char), - leader_char(e->leader_char), - current_field(0), - discarding(0), - spread_flag(0), - margin_character_flags(e->margin_character_flags), - margin_character_node(e->margin_character_node), - margin_character_distance(e->margin_character_distance), - numbering_nodes(0), - number_text_separation(e->number_text_separation), - line_number_indent(e->line_number_indent), - line_number_multiple(e->line_number_multiple), - no_number_count(e->no_number_count), - hyphenation_flags(e->hyphenation_flags), - hyphen_line_count(0), - hyphen_line_max(e->hyphen_line_max), - hyphenation_space(e->hyphenation_space), - hyphenation_margin(e->hyphenation_margin), - composite(0), - pending_lines(0), -#ifdef WIDOW_CONTROL - widow_control(e->widow_control), -#endif /* WIDOW_CONTROL */ - name(e->name), // so that eg `.if "\n[.ev]"0"' works - control_char(e->control_char), - no_break_control_char(e->no_break_control_char), - hyphen_indicator_char(e->hyphen_indicator_char) -{ -} - -void environment::copy(const environment *e) -{ - prev_line_length = e->prev_line_length; - line_length = e->line_length; - prev_title_length = e->prev_title_length; - title_length = e->title_length; - prev_size = e->prev_size; - size = e->size; - prev_requested_size = e->prev_requested_size; - requested_size = e->requested_size; - char_height = e->char_height; - char_slant = e->char_slant; - space_size = e->space_size; - sentence_space_size = e->sentence_space_size; - adjust_mode = e->adjust_mode; - fill = e->fill; - interrupted = 0; - prev_line_interrupted = 0; - center_lines = 0; - right_justify_lines = 0; - prev_vertical_spacing = e->prev_vertical_spacing; - vertical_spacing = e->vertical_spacing; - prev_post_vertical_spacing = e->prev_post_vertical_spacing, - post_vertical_spacing = e->post_vertical_spacing, - prev_line_spacing = e->prev_line_spacing; - line_spacing = e->line_spacing; - prev_indent = e->prev_indent; - indent = e->indent; - have_temporary_indent = 0; - temporary_indent = 0; - underline_lines = 0; - input_trap_count = 0; - prev_text_length = e->prev_text_length; - width_total = 0; - space_total = 0; - input_line_start = 0; - control_char = e->control_char; - no_break_control_char = e->no_break_control_char; - hyphen_indicator_char = e->hyphen_indicator_char; - spread_flag = 0; - line = 0; - pending_lines = 0; - discarding = 0; - tabs = e->tabs; - current_tab = TAB_NONE; - current_field = 0; - margin_character_flags = e->margin_character_flags; - margin_character_node = e->margin_character_node; - margin_character_distance = e->margin_character_distance; - numbering_nodes = 0; - number_text_separation = e->number_text_separation; - line_number_multiple = e->line_number_multiple; - line_number_indent = e->line_number_indent; - no_number_count = e->no_number_count; - tab_char = e->tab_char; - leader_char = e->leader_char; - hyphenation_flags = e->hyphenation_flags; - fontno = e->fontno; - prev_fontno = e->prev_fontno; - dummy = e->dummy; - family = e->family; - prev_family = e->prev_family; - leader_node = 0; -#ifdef WIDOW_CONTROL - widow_control = e->widow_control; -#endif /* WIDOW_CONTROL */ - hyphen_line_max = e->hyphen_line_max; - hyphen_line_count = 0; - hyphenation_space = e->hyphenation_space; - hyphenation_margin = e->hyphenation_margin; - composite = 0; -} - -environment::~environment() -{ - delete leader_node; - delete_node_list(line); - delete_node_list(numbering_nodes); -} - -hunits environment::get_input_line_position() -{ - hunits n; - if (line == 0) - n = -input_line_start; - else - n = width_total - input_line_start; - if (current_tab) - n += tab_width; - return n; -} - -void environment::set_input_line_position(hunits n) -{ - input_line_start = line == 0 ? -n : width_total - n; - if (current_tab) - input_line_start += tab_width; -} - -hunits environment::get_line_length() -{ - return line_length; -} - -hunits environment::get_saved_line_length() -{ - if (line) - return target_text_length + saved_indent; - else - return line_length; -} - -vunits environment::get_vertical_spacing() -{ - return vertical_spacing; -} - -vunits environment::get_post_vertical_spacing() -{ - return post_vertical_spacing; -} - -int environment::get_line_spacing() -{ - return line_spacing; -} - -vunits environment::total_post_vertical_spacing() -{ - vunits tem(post_vertical_spacing); - if (line_spacing > 1) - tem += (line_spacing - 1)*vertical_spacing; - return tem; -} - -int environment::get_bold() -{ - return get_bold_fontno(fontno); -} - -hunits environment::get_digit_width() -{ - return env_digit_width(this); -} - -int environment::get_adjust_mode() -{ - return adjust_mode; -} - -int environment::get_fill() -{ - return fill; -} - -hunits environment::get_indent() -{ - return indent; -} - -hunits environment::get_saved_indent() -{ - if (line) - return saved_indent; - else if (have_temporary_indent) - return temporary_indent; - else - return indent; -} - -hunits environment::get_temporary_indent() -{ - return temporary_indent; -} - -hunits environment::get_title_length() -{ - return title_length; -} - -node *environment::get_prev_char() -{ - for (node *n = current_tab ? tab_contents : line; n; n = n->next) { - node *last = n->last_char_node(); - if (last) - return last; - } - return 0; -} - -hunits environment::get_prev_char_width() -{ - node *last = get_prev_char(); - if (!last) - return H0; - return last->width(); -} - -hunits environment::get_prev_char_skew() -{ - node *last = get_prev_char(); - if (!last) - return H0; - return last->skew(); -} - -vunits environment::get_prev_char_height() -{ - node *last = get_prev_char(); - if (!last) - return V0; - vunits min, max; - last->vertical_extent(&min, &max); - return -min; -} - -vunits environment::get_prev_char_depth() -{ - node *last = get_prev_char(); - if (!last) - return V0; - vunits min, max; - last->vertical_extent(&min, &max); - return max; -} - -hunits environment::get_text_length() -{ - hunits n = line == 0 ? H0 : width_total; - if (current_tab) - n += tab_width; - return n; -} - -hunits environment::get_prev_text_length() -{ - return prev_text_length; -} - - -static int sb_reg_contents = 0; -static int st_reg_contents = 0; -static int ct_reg_contents = 0; -static int rsb_reg_contents = 0; -static int rst_reg_contents = 0; -static int skw_reg_contents = 0; -static int ssc_reg_contents = 0; - -void environment::width_registers() -{ - // this is used to implement \w; it sets the st, sb, ct registers - vunits min = 0, max = 0, cur = 0; - int character_type = 0; - ssc_reg_contents = line ? line->subscript_correction().to_units() : 0; - skw_reg_contents = line ? line->skew().to_units() : 0; - line = reverse_node_list(line); - vunits real_min = V0; - vunits real_max = V0; - vunits v1, v2; - for (node *tem = line; tem; tem = tem->next) { - tem->vertical_extent(&v1, &v2); - v1 += cur; - if (v1 < real_min) - real_min = v1; - v2 += cur; - if (v2 > real_max) - real_max = v2; - if ((cur += tem->vertical_width()) < min) - min = cur; - else if (cur > max) - max = cur; - character_type |= tem->character_type(); - } - line = reverse_node_list(line); - st_reg_contents = -min.to_units(); - sb_reg_contents = -max.to_units(); - rst_reg_contents = -real_min.to_units(); - rsb_reg_contents = -real_max.to_units(); - ct_reg_contents = character_type; -} - -node *environment::extract_output_line() -{ - if (current_tab) - wrap_up_tab(); - node *n = line; - line = 0; - return n; -} - -/* environment related requests */ - -void environment_switch() -{ - int pop = 0; // 1 means pop, 2 means pop but no error message on underflow - if (curenv->is_dummy()) - error("can't switch environments when current environment is dummy"); - else if (!has_arg()) - pop = 1; - else { - symbol nm; - if (!tok.delimiter()) { - // It looks like a number. - int n; - if (get_integer(&n)) { - if (n >= 0 && n < NENVIRONMENTS) { - env_stack = new env_list(curenv, env_stack); - if (env_table[n] == 0) - env_table[n] = new environment(i_to_a(n)); - curenv = env_table[n]; - } - else - nm = i_to_a(n); - } - else - pop = 2; - } - else { - nm = get_long_name(1); - if (nm.is_null()) - pop = 2; - } - if (!nm.is_null()) { - environment *e = (environment *)env_dictionary.lookup(nm); - if (!e) { - e = new environment(nm); - (void)env_dictionary.lookup(nm, e); - } - env_stack = new env_list(curenv, env_stack); - curenv = e; - } - } - if (pop) { - if (env_stack == 0) { - if (pop == 1) - error("environment stack underflow"); - } - else { - curenv = env_stack->env; - env_list *tem = env_stack; - env_stack = env_stack->next; - delete tem; - } - } - skip_line(); -} - -void environment_copy() -{ - symbol nm; - environment *e=0; - tok.skip(); - if (!tok.delimiter()) { - // It looks like a number. - int n; - if (get_integer(&n)) { - if (n >= 0 && n < NENVIRONMENTS) - e = env_table[n]; - else - nm = i_to_a(n); - } - } - else - nm = get_long_name(1); - if (!e && !nm.is_null()) - e = (environment *)env_dictionary.lookup(nm); - if (e == 0) { - error("No environment to copy from"); - return; - } - else - curenv->copy(e); - skip_line(); -} - -static symbol P_symbol("P"); - -void font_change() -{ - symbol s = get_name(); - int is_number = 1; - if (s.is_null() || s == P_symbol) { - s = P_symbol; - is_number = 0; - } - else { - for (const char *p = s.contents(); p != 0 && *p != 0; p++) - if (!csdigit(*p)) { - is_number = 0; - break; - } - } - if (is_number) - curenv->set_font(atoi(s.contents())); - else - curenv->set_font(s); - skip_line(); -} - -void family_change() -{ - symbol s = get_name(1); - if (!s.is_null()) - curenv->set_family(s); - skip_line(); -} - -void point_size() -{ - int n; - if (has_arg() && get_number(&n, 'z', curenv->get_requested_point_size())) { - if (n <= 0) - n = 1; - curenv->set_size(n); - } - else - curenv->set_size(0); - skip_line(); -} - -void space_size() -{ - int n; - if (get_integer(&n)) { - curenv->space_size = n; - if (has_arg() && get_integer(&n)) - curenv->sentence_space_size = n; - else - curenv->sentence_space_size = curenv->space_size; - } - skip_line(); -} - -void fill() -{ - while (!tok.newline() && !tok.eof()) - tok.next(); - if (break_flag) - curenv->do_break(); - curenv->fill = 1; - tok.next(); -} - -void no_fill() -{ - while (!tok.newline() && !tok.eof()) - tok.next(); - if (break_flag) - curenv->do_break(); - curenv->fill = 0; - tok.next(); -} - -void center() -{ - int n; - if (!has_arg() || !get_integer(&n)) - n = 1; - else if (n < 0) - n = 0; - while (!tok.newline() && !tok.eof()) - tok.next(); - if (break_flag) - curenv->do_break(); - curenv->right_justify_lines = 0; - curenv->center_lines = n; - tok.next(); -} - -void right_justify() -{ - int n; - if (!has_arg() || !get_integer(&n)) - n = 1; - else if (n < 0) - n = 0; - while (!tok.newline() && !tok.eof()) - tok.next(); - if (break_flag) - curenv->do_break(); - curenv->center_lines = 0; - curenv->right_justify_lines = n; - tok.next(); -} - -void line_length() -{ - hunits temp; - if (has_arg() && get_hunits(&temp, 'm', curenv->line_length)) { - if (temp < H0) { - warning(WARN_RANGE, "bad line length %1u", temp.to_units()); - temp = H0; - } - } - else - temp = curenv->prev_line_length; - curenv->prev_line_length = curenv->line_length; - curenv->line_length = temp; - skip_line(); -} - -void title_length() -{ - hunits temp; - if (has_arg() && get_hunits(&temp, 'm', curenv->title_length)) { - if (temp < H0) { - warning(WARN_RANGE, "bad title length %1u", temp.to_units()); - temp = H0; - } - } - else - temp = curenv->prev_title_length; - curenv->prev_title_length = curenv->title_length; - curenv->title_length = temp; - skip_line(); -} - -void vertical_spacing() -{ - vunits temp; - if (has_arg() && get_vunits(&temp, 'p', curenv->vertical_spacing)) { - if (temp <= V0) { - warning(WARN_RANGE, "vertical spacing must be greater than 0"); - temp = vresolution; - } - } - else - temp = curenv->prev_vertical_spacing; - curenv->prev_vertical_spacing = curenv->vertical_spacing; - curenv->vertical_spacing = temp; - skip_line(); -} - -void post_vertical_spacing() -{ - vunits temp; - if (has_arg() && get_vunits(&temp, 'p', curenv->post_vertical_spacing)) { - if (temp < V0) { - warning(WARN_RANGE, - "post vertical spacing must be greater than or equal to 0"); - temp = V0; - } - } - else - temp = curenv->prev_post_vertical_spacing; - curenv->prev_post_vertical_spacing = curenv->post_vertical_spacing; - curenv->post_vertical_spacing = temp; - skip_line(); -} - -void line_spacing() -{ - int temp; - if (has_arg() && get_integer(&temp)) { - if (temp < 1) { - warning(WARN_RANGE, "value %1 out of range: interpreted as 1", temp); - temp = 1; - } - } - else - temp = curenv->prev_line_spacing; - curenv->prev_line_spacing = curenv->line_spacing; - curenv->line_spacing = temp; - skip_line(); -} - -void indent() -{ - hunits temp; - if (has_arg() && get_hunits(&temp, 'm', curenv->indent)) { - if (temp < H0) { - warning(WARN_RANGE, "indent cannot be negative"); - temp = H0; - } - } - else - temp = curenv->prev_indent; - while (!tok.newline() && !tok.eof()) - tok.next(); - if (break_flag) - curenv->do_break(); - curenv->have_temporary_indent = 0; - curenv->prev_indent = curenv->indent; - curenv->indent = temp; - tok.next(); -} - -void temporary_indent() -{ - int err = 0; - hunits temp; - if (!get_hunits(&temp, 'm', curenv->get_indent())) - err = 1; - while (!tok.newline() && !tok.eof()) - tok.next(); - if (break_flag) - curenv->do_break(); - if (temp < H0) { - warning(WARN_RANGE, "total indent cannot be negative"); - temp = H0; - } - if (!err) { - curenv->temporary_indent = temp; - curenv->have_temporary_indent = 1; - } - tok.next(); -} - -void underline() -{ - int n; - if (!has_arg() || !get_integer(&n)) - n = 1; - if (n <= 0) { - if (curenv->underline_lines > 0) { - curenv->prev_fontno = curenv->fontno; - curenv->fontno = curenv->pre_underline_fontno; - } - curenv->underline_lines = 0; - } - else { - curenv->underline_lines = n; - curenv->pre_underline_fontno = curenv->fontno; - curenv->fontno = get_underline_fontno(); - } - skip_line(); -} - -void control_char() -{ - curenv->control_char = '.'; - if (has_arg()) { - if (tok.ch() == 0) - error("bad control character"); - else - curenv->control_char = tok.ch(); - } - skip_line(); -} - -void no_break_control_char() -{ - curenv->no_break_control_char = '\''; - if (has_arg()) { - if (tok.ch() == 0) - error("bad control character"); - else - curenv->no_break_control_char = tok.ch(); - } - skip_line(); -} - -void margin_character() -{ - while (tok.space()) - tok.next(); - charinfo *ci = tok.get_char(); - if (ci) { - // Call tok.next() only after making the node so that - // .mc \s+9\(br\s0 works. - node *nd = curenv->make_char_node(ci); - tok.next(); - if (nd) { - delete curenv->margin_character_node; - curenv->margin_character_node = nd; - curenv->margin_character_flags = (MARGIN_CHARACTER_ON - |MARGIN_CHARACTER_NEXT); - hunits d; - if (has_arg() && get_hunits(&d, 'm')) - curenv->margin_character_distance = d; - } - } - else { - check_missing_character(); - curenv->margin_character_flags &= ~MARGIN_CHARACTER_ON; - if (curenv->margin_character_flags == 0) { - delete curenv->margin_character_node; - curenv->margin_character_node = 0; - } - } - skip_line(); -} - -void number_lines() -{ - delete_node_list(curenv->numbering_nodes); - curenv->numbering_nodes = 0; - if (has_arg()) { - node *nd = 0; - for (int i = '9'; i >= '0'; i--) { - node *tem = make_node(charset_table[i], curenv); - if (!tem) { - skip_line(); - return; - } - tem->next = nd; - nd = tem; - } - curenv->numbering_nodes = nd; - curenv->line_number_digit_width = env_digit_width(curenv); - int n; - if (!tok.delimiter()) { - if (get_integer(&n, next_line_number)) { - next_line_number = n; - if (next_line_number < 0) { - warning(WARN_RANGE, "negative line number"); - next_line_number = 0; - } - } - } - else - while (!tok.space() && !tok.newline() && !tok.eof()) - tok.next(); - if (has_arg()) { - if (!tok.delimiter()) { - if (get_integer(&n)) { - if (n <= 0) { - warning(WARN_RANGE, "negative or zero line number multiple"); - } - else - curenv->line_number_multiple = n; - } - } - else - while (!tok.space() && !tok.newline() && !tok.eof()) - tok.next(); - if (has_arg()) { - if (!tok.delimiter()) { - if (get_integer(&n)) - curenv->number_text_separation = n; - } - else - while (!tok.space() && !tok.newline() && !tok.eof()) - tok.next(); - if (has_arg() && !tok.delimiter() && get_integer(&n)) - curenv->line_number_indent = n; - } - } - } - skip_line(); -} - -void no_number() -{ - int n; - if (has_arg() && get_integer(&n)) - curenv->no_number_count = n > 0 ? n : 0; - else - curenv->no_number_count = 1; - skip_line(); -} - -void no_hyphenate() -{ - curenv->hyphenation_flags = 0; - skip_line(); -} - -void hyphenate_request() -{ - int n; - if (has_arg() && get_integer(&n)) - curenv->hyphenation_flags = n; - else - curenv->hyphenation_flags = 1; - skip_line(); -} - -void hyphen_char() -{ - curenv->hyphen_indicator_char = get_optional_char(); - skip_line(); -} - -void hyphen_line_max_request() -{ - int n; - if (has_arg() && get_integer(&n)) - curenv->hyphen_line_max = n; - else - curenv->hyphen_line_max = -1; - skip_line(); -} - -void environment::interrupt() -{ - if (!dummy) { - add_node(new transparent_dummy_node); - interrupted = 1; - } -} - -void environment::newline() -{ - if (underline_lines > 0) { - if (--underline_lines == 0) { - prev_fontno = fontno; - fontno = pre_underline_fontno; - } - } - if (current_field) - wrap_up_field(); - if (current_tab) - wrap_up_tab(); - // strip trailing spaces - while (line != 0 && line->discardable()) { - width_total -= line->width(); - space_total -= line->nspaces(); - node *tem = line; - line = line->next; - delete tem; - } - node *to_be_output = 0; - hunits to_be_output_width; - prev_line_interrupted = 0; - if (dummy) - space_newline(); - else if (interrupted) { - interrupted = 0; - // see environment::final_break - prev_line_interrupted = exit_started ? 2 : 1; - } - else if (center_lines > 0) { - --center_lines; - hunits x = target_text_length - width_total; - if (x > H0) - saved_indent += x/2; - to_be_output = line; - to_be_output_width = width_total; - line = 0; - } - else if (right_justify_lines > 0) { - --right_justify_lines; - hunits x = target_text_length - width_total; - if (x > H0) - saved_indent += x; - to_be_output = line; - to_be_output_width = width_total; - line = 0; - } - else if (fill) - space_newline(); - else { - to_be_output = line; - to_be_output_width = width_total; - line = 0; - } - input_line_start = line == 0 ? H0 : width_total; - if (to_be_output) { - output_line(to_be_output, to_be_output_width); - hyphen_line_count = 0; - } - if (input_trap_count > 0) { - if (--input_trap_count == 0) - spring_trap(input_trap); - } -} - -void environment::output_line(node *n, hunits width) -{ - prev_text_length = width; - if (margin_character_flags) { - hunits d = line_length + margin_character_distance - saved_indent - width; - if (d > 0) { - n = new hmotion_node(d, n); - width += d; - } - margin_character_flags &= ~MARGIN_CHARACTER_NEXT; - node *tem; - if (!margin_character_flags) { - tem = margin_character_node; - margin_character_node = 0; - } - else - tem = margin_character_node->copy(); - tem->next = n; - n = tem; - width += tem->width(); - } - node *nn = 0; - while (n != 0) { - node *tem = n->next; - n->next = nn; - nn = n; - n = tem; - } - if (!saved_indent.is_zero()) - nn = new hmotion_node(saved_indent, nn); - width += saved_indent; - if (no_number_count > 0) - --no_number_count; - else if (numbering_nodes) { - hunits w = (line_number_digit_width - *(3+line_number_indent+number_text_separation)); - if (next_line_number % line_number_multiple != 0) - nn = new hmotion_node(w, nn); - else { - hunits x = w; - nn = new hmotion_node(number_text_separation*line_number_digit_width, - nn); - x -= number_text_separation*line_number_digit_width; - char buf[30]; - sprintf(buf, "%3d", next_line_number); - for (char *p = strchr(buf, '\0') - 1; p >= buf && *p != ' '; --p) { - node *gn = numbering_nodes; - for (int count = *p - '0'; count > 0; count--) - gn = gn->next; - gn = gn->copy(); - x -= gn->width(); - gn->next = nn; - nn = gn; - } - nn = new hmotion_node(x, nn); - } - width += w; - ++next_line_number; - } - output(nn, !fill, vertical_spacing, total_post_vertical_spacing(), width); -} - -void environment::start_line() -{ - assert(line == 0); - discarding = 0; - line = new line_start_node; - if (have_temporary_indent) { - saved_indent = temporary_indent; - have_temporary_indent = 0; - } - else - saved_indent = indent; - target_text_length = line_length - saved_indent; - width_total = H0; - space_total = 0; -} - -hunits environment::get_hyphenation_space() -{ - return hyphenation_space; -} - -void hyphenation_space_request() -{ - hunits n; - if (get_hunits(&n, 'm')) { - if (n < H0) { - warning(WARN_RANGE, "hyphenation space cannot be negative"); - n = H0; - } - curenv->hyphenation_space = n; - } - skip_line(); -} - -hunits environment::get_hyphenation_margin() -{ - return hyphenation_margin; -} - -void hyphenation_margin_request() -{ - hunits n; - if (get_hunits(&n, 'm')) { - if (n < H0) { - warning(WARN_RANGE, "hyphenation margin cannot be negative"); - n = H0; - } - curenv->hyphenation_margin = n; - } - skip_line(); -} - -breakpoint *environment::choose_breakpoint() -{ - hunits x = width_total; - int s = space_total; - node *n = line; - breakpoint *best_bp = 0; // the best breakpoint so far - int best_bp_fits = 0; - while (n != 0) { - x -= n->width(); - s -= n->nspaces(); - breakpoint *bp = n->get_breakpoints(x, s); - while (bp != 0) { - if (bp->width <= target_text_length) { - if (!bp->hyphenated) { - breakpoint *tem = bp->next; - bp->next = 0; - while (tem != 0) { - breakpoint *tem1 = tem; - tem = tem->next; - delete tem1; - } - if (best_bp_fits - // Decide whether to use the hyphenated breakpoint. - && (hyphen_line_max < 0 - // Only choose the hyphenated breakpoint if it would not - // exceed the maximum number of consecutive hyphenated - // lines. - || hyphen_line_count + 1 <= hyphen_line_max) - && !(adjust_mode == ADJUST_BOTH - // Don't choose the hyphenated breakpoint if the line - // can be justified by adding no more than - // hyphenation_space to any word space. - ? (bp->nspaces > 0 - && (((target_text_length - bp->width - + (bp->nspaces - 1)*hresolution)/bp->nspaces) - <= hyphenation_space)) - // Don't choose the hyphenated breakpoint if the line - // is no more than hyphenation_margin short. - : target_text_length - bp->width <= hyphenation_margin)) { - delete bp; - return best_bp; - } - if (best_bp) - delete best_bp; - return bp; - } - else { - if ((adjust_mode == ADJUST_BOTH - ? hyphenation_space == H0 - : hyphenation_margin == H0) - && (hyphen_line_max < 0 - || hyphen_line_count + 1 <= hyphen_line_max)) { - // No need to consider a non-hyphenated breakpoint. - if (best_bp) - delete best_bp; - return bp; - } - // It fits but it's hyphenated. - if (!best_bp_fits) { - if (best_bp) - delete best_bp; - best_bp = bp; - bp = bp->next; - best_bp_fits = 1; - } - else { - breakpoint *tem = bp; - bp = bp->next; - delete tem; - } - } - } - else { - if (best_bp) - delete best_bp; - best_bp = bp; - bp = bp->next; - } - } - n = n->next; - } - if (best_bp) { - if (!best_bp_fits) - warning(WARN_BREAK, "can't break line"); - return best_bp; - } - return 0; -} - -void environment::hyphenate_line() -{ - if (line == 0) - return; - hyphenation_type prev_type = line->get_hyphenation_type(); - node **startp; - for (startp = &line->next; *startp != 0; startp = &(*startp)->next) { - hyphenation_type this_type = (*startp)->get_hyphenation_type(); - if (prev_type == HYPHEN_BOUNDARY && this_type == HYPHEN_MIDDLE) - break; - prev_type = this_type; - } - if (*startp == 0) - return; - node *tem = *startp; - int i = 0; - do { - ++i; - tem = tem->next; - } while (tem != 0 && tem->get_hyphenation_type() == HYPHEN_MIDDLE); - int inhibit = (tem != 0 && tem->get_hyphenation_type() == HYPHEN_INHIBIT); - node *end = tem; - hyphen_list *sl = 0; - tem = *startp; - node *forward = 0; - while (tem != end) { - sl = tem->get_hyphen_list(sl); - node *tem1 = tem; - tem = tem->next; - tem1->next = forward; - forward = tem1; - } - if (!inhibit) { - // this is for characters like hyphen and emdash - int prev_code = 0; - for (hyphen_list *h = sl; h; h = h->next) { - h->breakable = (prev_code != 0 - && h->next != 0 - && h->next->hyphenation_code != 0); - prev_code = h->hyphenation_code; - } - } - if (hyphenation_flags != 0 - && !inhibit - // this may not be right if we have extra space on this line - && !((hyphenation_flags & HYPHEN_LAST_LINE) - && (curdiv->distance_to_next_trap() - <= vertical_spacing + total_post_vertical_spacing())) - && i >= 4) - hyphenate(sl, hyphenation_flags); - while (forward != 0) { - node *tem1 = forward; - forward = forward->next; - tem1->next = 0; - tem = tem1->add_self(tem, &sl); - } - *startp = tem; -} - -static node *node_list_reverse(node *n) -{ - node *res = 0; - while (n) { - node *tem = n; - n = n->next; - tem->next = res; - res = tem; - } - return res; -} - -static void distribute_space(node *n, int nspaces, hunits desired_space, - int force_reverse = 0) -{ - static int reverse = 0; - if (force_reverse || reverse) - n = node_list_reverse(n); - for (node *tem = n; tem; tem = tem->next) - tem->spread_space(&nspaces, &desired_space); - if (force_reverse || reverse) - (void)node_list_reverse(n); - if (!force_reverse) - reverse = !reverse; - assert(desired_space.is_zero() && nspaces == 0); -} - -void environment::possibly_break_line(int forced) -{ - if (!fill || current_tab || current_field || dummy) - return; - while (line != 0 - && (forced - // When a macro follows a paragraph in fill mode, the - // current line should not be empty. - || (width_total - line->width()) > target_text_length)) { - hyphenate_line(); - breakpoint *bp = choose_breakpoint(); - if (bp == 0) - // we'll find one eventually - return; - node *pre, *post; - node **ndp = &line; - while (*ndp != bp->nd) - ndp = &(*ndp)->next; - bp->nd->split(bp->index, &pre, &post); - *ndp = post; - hunits extra_space_width = H0; - switch(adjust_mode) { - case ADJUST_BOTH: - if (bp->nspaces != 0) - extra_space_width = target_text_length - bp->width; - break; - case ADJUST_CENTER: - saved_indent += (target_text_length - bp->width)/2; - break; - case ADJUST_RIGHT: - saved_indent += target_text_length - bp->width; - break; - } - distribute_space(pre, bp->nspaces, extra_space_width); - hunits output_width = bp->width + extra_space_width; - input_line_start -= output_width; - if (bp->hyphenated) - hyphen_line_count++; - else - hyphen_line_count = 0; - delete bp; - space_total = 0; - width_total = 0; - node *first_non_discardable = 0; - node *tem; - for (tem = line; tem != 0; tem = tem->next) - if (!tem->discardable()) - first_non_discardable = tem; - node *to_be_discarded; - if (first_non_discardable) { - to_be_discarded = first_non_discardable->next; - first_non_discardable->next = 0; - for (tem = line; tem != 0; tem = tem->next) { - width_total += tem->width(); - space_total += tem->nspaces(); - } - discarding = 0; - } - else { - discarding = 1; - to_be_discarded = line; - line = 0; - } - // Do output_line() here so that line will be 0 iff the - // the environment will be empty. - output_line(pre, output_width); - while (to_be_discarded != 0) { - tem = to_be_discarded; - to_be_discarded = to_be_discarded->next; - input_line_start -= tem->width(); - delete tem; - } - if (line != 0) { - if (have_temporary_indent) { - saved_indent = temporary_indent; - have_temporary_indent = 0; - } - else - saved_indent = indent; - target_text_length = line_length - saved_indent; - } - } -} - -/* -Do the break at the end of input after the end macro (if any). - -Unix troff behaves as follows: if the last line is - -foo bar\c - -it will output foo on the current page, and bar on the next page; -if the last line is - -foo\c - -or - -foo bar - -everything will be output on the current page. This behaviour must be -considered a bug. - -The problem is that some macro packages rely on this. For example, -the ATK macros have an end macro that emits \c if it needs to print a -table of contents but doesn't do a 'bp in the end macro; instead the -'bp is done in the bottom of page trap. This works with Unix troff, -provided that the current environment is not empty at the end of the -input file. - -The following will make macro packages that do that sort of thing work -even if the current environment is empty at the end of the input file. -If the last input line used \c and this line occurred in the end macro, -then we'll force everything out on the current page, but we'll make -sure that the environment isn't empty so that we won't exit at the -bottom of this page. -*/ - -void environment::final_break() -{ - if (prev_line_interrupted == 2) { - do_break(); - add_node(new transparent_dummy_node); - } - else - do_break(); -} - -void environment::do_break() -{ - if (curdiv == topdiv && topdiv->before_first_page) { - topdiv->begin_page(); - return; - } - if (current_tab) - wrap_up_tab(); - if (line) { - line = new space_node(H0, line); // this is so that hyphenation works - space_total++; - possibly_break_line(); - } - while (line != 0 && line->discardable()) { - width_total -= line->width(); - space_total -= line->nspaces(); - node *tem = line; - line = line->next; - delete tem; - } - discarding = 0; - input_line_start = H0; - if (line != 0) { - if (fill) { - switch (adjust_mode) { - case ADJUST_CENTER: - saved_indent += (target_text_length - width_total)/2; - break; - case ADJUST_RIGHT: - saved_indent += target_text_length - width_total; - break; - } - } - node *tem = line; - line = 0; - output_line(tem, width_total); - hyphen_line_count = 0; - } - prev_line_interrupted = 0; -#ifdef WIDOW_CONTROL - mark_last_line(); - output_pending_lines(); -#endif /* WIDOW_CONTROL */ -} - -int environment::is_empty() -{ - return !current_tab && line == 0 && pending_lines == 0; -} - -void break_request() -{ - while (!tok.newline() && !tok.eof()) - tok.next(); - if (break_flag) - curenv->do_break(); - tok.next(); -} - -void title() -{ - if (curdiv == topdiv && topdiv->before_first_page) { - handle_initial_title(); - return; - } - node *part[3]; - hunits part_width[3]; - part[0] = part[1] = part[2] = 0; - environment env(curenv); - environment *oldenv = curenv; - curenv = &env; - read_title_parts(part, part_width); - curenv = oldenv; - curenv->size = env.size; - curenv->prev_size = env.prev_size; - curenv->requested_size = env.requested_size; - curenv->prev_requested_size = env.prev_requested_size; - curenv->char_height = env.char_height; - curenv->char_slant = env.char_slant; - curenv->fontno = env.fontno; - curenv->prev_fontno = env.prev_fontno; - node *n = 0; - node *p = part[2]; - while (p != 0) { - node *tem = p; - p = p->next; - tem->next = n; - n = tem; - } - hunits title_length(curenv->title_length); - hunits f = title_length - part_width[1]; - hunits f2 = f/2; - n = new hmotion_node(f2 - part_width[2], n); - p = part[1]; - while (p != 0) { - node *tem = p; - p = p->next; - tem->next = n; - n = tem; - } - n = new hmotion_node(f - f2 - part_width[0], n); - p = part[0]; - while (p != 0) { - node *tem = p; - p = p->next; - tem->next = n; - n = tem; - } - curenv->output_title(n, !curenv->fill, curenv->vertical_spacing, - curenv->total_post_vertical_spacing(), title_length); - curenv->hyphen_line_count = 0; - tok.next(); -} - -void adjust() -{ - curenv->adjust_mode |= 1; - if (has_arg()) { - switch (tok.ch()) { - case 'l': - curenv->adjust_mode = ADJUST_LEFT; - break; - case 'r': - curenv->adjust_mode = ADJUST_RIGHT; - break; - case 'c': - curenv->adjust_mode = ADJUST_CENTER; - break; - case 'b': - case 'n': - curenv->adjust_mode = ADJUST_BOTH; - break; - default: - int n; - if (get_integer(&n)) { - if (n < 0) - warning(WARN_RANGE, "negative adjustment mode"); - else if (n > 5) { - curenv->adjust_mode = 5; - warning(WARN_RANGE, "adjustment mode `%1' out of range", n); - } - else - curenv->adjust_mode = n; - } - } - } - skip_line(); -} - -void no_adjust() -{ - curenv->adjust_mode &= ~1; - skip_line(); -} - -void input_trap() -{ - curenv->input_trap_count = 0; - int n; - if (has_arg() && get_integer(&n)) { - if (n <= 0) - warning(WARN_RANGE, - "number of lines for input trap must be greater than zero"); - else { - symbol s = get_name(1); - if (!s.is_null()) { - curenv->input_trap_count = n; - curenv->input_trap = s; - } - } - } - skip_line(); -} - -/* tabs */ - -// must not be R or C or L or a legitimate part of a number expression -const char TAB_REPEAT_CHAR = 'T'; - -struct tab { - tab *next; - hunits pos; - tab_type type; - tab(hunits, tab_type); - enum { BLOCK = 1024 }; - static tab *free_list; - void *operator new(size_t); - void operator delete(void *); -}; - -tab *tab::free_list = 0; - -void *tab::operator new(size_t n) -{ - assert(n == sizeof(tab)); - if (!free_list) { - free_list = (tab *)new char[sizeof(tab)*BLOCK]; - for (int i = 0; i < BLOCK - 1; i++) - free_list[i].next = free_list + i + 1; - free_list[BLOCK-1].next = 0; - } - tab *p = free_list; - free_list = (tab *)(free_list->next); - p->next = 0; - return p; -} - -#ifdef __GNUG__ -/* cfront can't cope with this. */ -inline -#endif -void tab::operator delete(void *p) -{ - if (p) { - ((tab *)p)->next = free_list; - free_list = (tab *)p; - } -} - -tab::tab(hunits x, tab_type t) : next(0), pos(x), type(t) -{ -} - -tab_stops::tab_stops(hunits distance, tab_type type) - : initial_list(0) -{ - repeated_list = new tab(distance, type); -} - -tab_stops::~tab_stops() -{ - clear(); -} - -tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance) -{ - hunits lastpos = 0; - tab *tem; - for (tem = initial_list; tem && tem->pos <= curpos; tem = tem->next) - lastpos = tem->pos; - if (tem) { - *distance = tem->pos - curpos; - return tem->type; - } - if (repeated_list == 0) - return TAB_NONE; - hunits base = lastpos; - for (;;) { - for (tem = repeated_list; tem && tem->pos + base <= curpos; tem = tem->next) - lastpos = tem->pos; - if (tem) { - *distance = tem->pos + base - curpos; - return tem->type; - } - assert(lastpos > 0); - base += lastpos; - } - return TAB_NONE; -} - -const char *tab_stops::to_string() -{ - static char *buf = 0; - static int buf_size = 0; - // figure out a maximum on the amount of space we can need - int count = 0; - tab *p; - for (p = initial_list; p; p = p->next) - ++count; - for (p = repeated_list; p; p = p->next) - ++count; - // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0' - int need = count*12 + 3; - if (buf == 0 || need > buf_size) { - if (buf) - a_delete buf; - buf_size = need; - buf = new char[buf_size]; - } - char *ptr = buf; - for (p = initial_list; p; p = p->next) { - strcpy(ptr, i_to_a(p->pos.to_units())); - ptr = strchr(ptr, '\0'); - *ptr++ = 'u'; - *ptr = '\0'; - switch (p->type) { - case TAB_LEFT: - break; - case TAB_RIGHT: - *ptr++ = 'R'; - break; - case TAB_CENTER: - *ptr++ = 'C'; - break; - case TAB_NONE: - default: - assert(0); - } - } - if (repeated_list) - *ptr++ = TAB_REPEAT_CHAR; - for (p = repeated_list; p; p = p->next) { - strcpy(ptr, i_to_a(p->pos.to_units())); - ptr = strchr(ptr, '\0'); - *ptr++ = 'u'; - *ptr = '\0'; - switch (p->type) { - case TAB_LEFT: - break; - case TAB_RIGHT: - *ptr++ = 'R'; - break; - case TAB_CENTER: - *ptr++ = 'C'; - break; - case TAB_NONE: - default: - assert(0); - } - } - *ptr++ = '\0'; - return buf; -} - -tab_stops::tab_stops() : initial_list(0), repeated_list(0) -{ -} - -tab_stops::tab_stops(const tab_stops &ts) - : initial_list(0), repeated_list(0) -{ - tab **p = &initial_list; - tab *t = ts.initial_list; - while (t) { - *p = new tab(t->pos, t->type); - t = t->next; - p = &(*p)->next; - } - p = &repeated_list; - t = ts.repeated_list; - while (t) { - *p = new tab(t->pos, t->type); - t = t->next; - p = &(*p)->next; - } -} - -void tab_stops::clear() -{ - while (initial_list) { - tab *tem = initial_list; - initial_list = initial_list->next; - delete tem; - } - while (repeated_list) { - tab *tem = repeated_list; - repeated_list = repeated_list->next; - delete tem; - } -} - -void tab_stops::add_tab(hunits pos, tab_type type, int repeated) -{ - tab **p; - for (p = repeated ? &repeated_list : &initial_list; *p; p = &(*p)->next) - ; - *p = new tab(pos, type); -} - - -void tab_stops::operator=(const tab_stops &ts) -{ - clear(); - tab **p = &initial_list; - tab *t = ts.initial_list; - while (t) { - *p = new tab(t->pos, t->type); - t = t->next; - p = &(*p)->next; - } - p = &repeated_list; - t = ts.repeated_list; - while (t) { - *p = new tab(t->pos, t->type); - t = t->next; - p = &(*p)->next; - } -} - -void set_tabs() -{ - hunits pos; - hunits prev_pos = 0; - int first = 1; - int repeated = 0; - tab_stops tabs; - while (has_arg()) { - if (tok.ch() == TAB_REPEAT_CHAR) { - tok.next(); - repeated = 1; - prev_pos = 0; - } - if (!get_hunits(&pos, 'm', prev_pos)) - break; - tab_type type = TAB_LEFT; - if (tok.ch() == 'C') { - tok.next(); - type = TAB_CENTER; - } - else if (tok.ch() == 'R') { - tok.next(); - type = TAB_RIGHT; - } - else if (tok.ch() == 'L') { - tok.next(); - } - if (pos <= prev_pos && !first) - warning(WARN_RANGE, - "positions of tab stops must be strictly increasing"); - else { - tabs.add_tab(pos, type, repeated); - prev_pos = pos; - first = 0; - } - } - curenv->tabs = tabs; - skip_line(); -} - -const char *environment::get_tabs() -{ - return tabs.to_string(); -} - -#if 0 -tab_stops saved_tabs; - -void tabs_save() -{ - saved_tabs = curenv->tabs; - skip_line(); -} - -void tabs_restore() -{ - curenv->tabs = saved_tabs; - skip_line(); -} -#endif - -tab_type environment::distance_to_next_tab(hunits *distance) -{ - return curenv->tabs.distance_to_next_tab(get_input_line_position(), distance); -} - -void field_characters() -{ - field_delimiter_char = get_optional_char(); - if (field_delimiter_char) - padding_indicator_char = get_optional_char(); - else - padding_indicator_char = 0; - skip_line(); -} - -void environment::wrap_up_tab() -{ - if (!current_tab) - return; - if (line == 0) - start_line(); - hunits tab_amount; - switch (current_tab) { - case TAB_RIGHT: - tab_amount = tab_distance - tab_width; - line = make_tab_node(tab_amount, line); - break; - case TAB_CENTER: - tab_amount = tab_distance - tab_width/2; - line = make_tab_node(tab_amount, line); - break; - case TAB_NONE: - case TAB_LEFT: - default: - assert(0); - } - width_total += tab_amount; - width_total += tab_width; - if (current_field) { - if (tab_precedes_field) { - pre_field_width += tab_amount; - tab_precedes_field = 0; - } - field_distance -= tab_amount; - field_spaces += tab_field_spaces; - } - if (tab_contents != 0) { - node *tem; - for (tem = tab_contents; tem->next != 0; tem = tem->next) - ; - tem->next = line; - line = tab_contents; - } - tab_field_spaces = 0; - tab_contents = 0; - tab_width = H0; - tab_distance = H0; - current_tab = TAB_NONE; -} - -node *environment::make_tab_node(hunits d, node *next) -{ - if (leader_node != 0 && d < 0) { - error("motion generated by leader cannot be negative"); - delete leader_node; - leader_node = 0; - } - if (!leader_node) - return new hmotion_node(d, next); - node *n = new hline_node(d, leader_node, next); - leader_node = 0; - return n; -} - -void environment::handle_tab(int is_leader) -{ - hunits d; - if (current_tab) - wrap_up_tab(); - charinfo *ci = is_leader ? leader_char : tab_char; - delete leader_node; - leader_node = ci ? make_char_node(ci) : 0; - tab_type t = distance_to_next_tab(&d); - switch (t) { - case TAB_NONE: - return; - case TAB_LEFT: - add_node(make_tab_node(d)); - return; - case TAB_RIGHT: - case TAB_CENTER: - tab_width = 0; - tab_distance = d; - tab_contents = 0; - current_tab = t; - tab_field_spaces = 0; - return; - default: - assert(0); - } -} - -void environment::start_field() -{ - assert(!current_field); - hunits d; - if (distance_to_next_tab(&d) != TAB_NONE) { - pre_field_width = get_text_length(); - field_distance = d; - current_field = 1; - field_spaces = 0; - tab_field_spaces = 0; - for (node *p = line; p; p = p->next) - if (p->nspaces()) { - p->freeze_space(); - space_total--; - } - tab_precedes_field = current_tab != TAB_NONE; - } - else - error("zero field width"); -} - -void environment::wrap_up_field() -{ - if (!current_tab && field_spaces == 0) - add_padding(); - hunits padding = field_distance - (get_text_length() - pre_field_width); - if (current_tab && tab_field_spaces != 0) { - hunits tab_padding = scale(padding, - tab_field_spaces, - field_spaces + tab_field_spaces); - padding -= tab_padding; - distribute_space(tab_contents, tab_field_spaces, tab_padding, 1); - tab_field_spaces = 0; - tab_width += tab_padding; - } - if (field_spaces != 0) { - distribute_space(line, field_spaces, padding, 1); - width_total += padding; - if (current_tab) { - // the start of the tab has been moved to the right by padding, so - tab_distance -= padding; - if (tab_distance <= H0) { - // use the next tab stop instead - current_tab = tabs.distance_to_next_tab(get_input_line_position() - - tab_width, - &tab_distance); - if (current_tab == TAB_NONE || current_tab == TAB_LEFT) { - width_total += tab_width; - if (current_tab == TAB_LEFT) { - line = make_tab_node(tab_distance, line); - width_total += tab_distance; - current_tab = TAB_NONE; - } - if (tab_contents != 0) { - node *tem; - for (tem = tab_contents; tem->next != 0; tem = tem->next) - ; - tem->next = line; - line = tab_contents; - tab_contents = 0; - } - tab_width = H0; - tab_distance = H0; - } - } - } - } - current_field = 0; -} - -void environment::add_padding() -{ - if (current_tab) { - tab_contents = new space_node(H0, tab_contents); - tab_field_spaces++; - } - else { - if (line == 0) - start_line(); - line = new space_node(H0, line); - field_spaces++; - } -} - -typedef int (environment::*INT_FUNCP)(); -typedef vunits (environment::*VUNITS_FUNCP)(); -typedef hunits (environment::*HUNITS_FUNCP)(); -typedef const char *(environment::*STRING_FUNCP)(); - -class int_env_reg : public reg { - INT_FUNCP func; - public: - int_env_reg(INT_FUNCP); - const char *get_string(); - int get_value(units *val); -}; - -class vunits_env_reg : public reg { - VUNITS_FUNCP func; - public: - vunits_env_reg(VUNITS_FUNCP f); - const char *get_string(); - int get_value(units *val); -}; - - -class hunits_env_reg : public reg { - HUNITS_FUNCP func; - public: - hunits_env_reg(HUNITS_FUNCP f); - const char *get_string(); - int get_value(units *val); -}; - -class string_env_reg : public reg { - STRING_FUNCP func; -public: - string_env_reg(STRING_FUNCP); - const char *get_string(); -}; - -int_env_reg::int_env_reg(INT_FUNCP f) : func(f) -{ -} - -int int_env_reg::get_value(units *val) -{ - *val = (curenv->*func)(); - return 1; -} - -const char *int_env_reg::get_string() -{ - return i_to_a((curenv->*func)()); -} - -vunits_env_reg::vunits_env_reg(VUNITS_FUNCP f) : func(f) -{ -} - -int vunits_env_reg::get_value(units *val) -{ - *val = (curenv->*func)().to_units(); - return 1; -} - -const char *vunits_env_reg::get_string() -{ - return i_to_a((curenv->*func)().to_units()); -} - -hunits_env_reg::hunits_env_reg(HUNITS_FUNCP f) : func(f) -{ -} - -int hunits_env_reg::get_value(units *val) -{ - *val = (curenv->*func)().to_units(); - return 1; -} - -const char *hunits_env_reg::get_string() -{ - return i_to_a((curenv->*func)().to_units()); -} - -string_env_reg::string_env_reg(STRING_FUNCP f) : func(f) -{ -} - -const char *string_env_reg::get_string() -{ - return (curenv->*func)(); -} - -class horizontal_place_reg : public general_reg { -public: - horizontal_place_reg(); - int get_value(units *); - void set_value(units); -}; - -horizontal_place_reg::horizontal_place_reg() -{ -} - -int horizontal_place_reg::get_value(units *res) -{ - *res = curenv->get_input_line_position().to_units(); - return 1; -} - -void horizontal_place_reg::set_value(units n) -{ - curenv->set_input_line_position(hunits(n)); -} - -const char *environment::get_font_family_string() -{ - return family->nm.contents(); -} - -const char *environment::get_name_string() -{ - return name.contents(); -} - -// Convert a quantity in scaled points to ascii decimal fraction. - -const char *sptoa(int sp) -{ - assert(sp > 0); - assert(sizescale > 0); - if (sizescale == 1) - return i_to_a(sp); - if (sp % sizescale == 0) - return i_to_a(sp/sizescale); - // See if 1/sizescale is exactly representable as a decimal fraction, - // ie its only prime factors are 2 and 5. - int n = sizescale; - int power2 = 0; - while ((n & 1) == 0) { - n >>= 1; - power2++; - } - int power5 = 0; - while ((n % 5) == 0) { - n /= 5; - power5++; - } - if (n == 1) { - int decimal_point = power5 > power2 ? power5 : power2; - if (decimal_point <= 10) { - int factor = 1; - int t; - for (t = decimal_point - power2; --t >= 0;) - factor *= 2; - for (t = decimal_point - power5; --t >= 0;) - factor *= 5; - if (factor == 1 || sp <= INT_MAX/factor) - return if_to_a(sp*factor, decimal_point); - } - } - double s = double(sp)/double(sizescale); - double factor = 10.0; - double val = s; - int decimal_point = 0; - do { - double v = ceil(s*factor); - if (v > INT_MAX) - break; - val = v; - factor *= 10.0; - } while (++decimal_point < 10); - return if_to_a(int(val), decimal_point); -} - -const char *environment::get_point_size_string() -{ - return sptoa(curenv->get_point_size()); -} - -const char *environment::get_requested_point_size_string() -{ - return sptoa(curenv->get_requested_point_size()); -} - -#define init_int_env_reg(name, func) \ - number_reg_dictionary.define(name, new int_env_reg(&environment::func)) - -#define init_vunits_env_reg(name, func) \ - number_reg_dictionary.define(name, new vunits_env_reg(&environment::func)) - -#define init_hunits_env_reg(name, func) \ - number_reg_dictionary.define(name, new hunits_env_reg(&environment::func)) - -#define init_string_env_reg(name, func) \ - number_reg_dictionary.define(name, new string_env_reg(&environment::func)) - -void init_env_requests() -{ - init_request("it", input_trap); - init_request("ad", adjust); - init_request("na", no_adjust); - init_request("ev", environment_switch); - init_request("evc", environment_copy); - init_request("lt", title_length); - init_request("ps", point_size); - init_request("ft", font_change); - init_request("fam", family_change); - init_request("ss", space_size); - init_request("fi", fill); - init_request("nf", no_fill); - init_request("ce", center); - init_request("rj", right_justify); - init_request("vs", vertical_spacing); - init_request("ls", line_spacing); - init_request("ll", line_length); - init_request("in", indent); - init_request("ti", temporary_indent); - init_request("ul", underline); - init_request("cu", underline); - init_request("cc", control_char); - init_request("c2", no_break_control_char); - init_request("br", break_request); - init_request("tl", title); - init_request("ta", set_tabs); - init_request("fc", field_characters); - init_request("mc", margin_character); - init_request("nn", no_number); - init_request("nm", number_lines); - init_request("tc", tab_character); - init_request("lc", leader_character); - init_request("hy", hyphenate_request); - init_request("hc", hyphen_char); - init_request("nh", no_hyphenate); - init_request("hlm", hyphen_line_max_request); -#ifdef WIDOW_CONTROL - init_request("wdc", widow_control_request); -#endif /* WIDOW_CONTROL */ -#if 0 - init_request("tas", tabs_save); - init_request("tar", tabs_restore); -#endif - init_request("hys", hyphenation_space_request); - init_request("hym", hyphenation_margin_request); - init_request("pvs", post_vertical_spacing); - init_int_env_reg(".f", get_font); - init_int_env_reg(".b", get_bold); - init_hunits_env_reg(".i", get_indent); - init_hunits_env_reg(".in", get_saved_indent); - init_int_env_reg(".j", get_adjust_mode); - init_hunits_env_reg(".k", get_text_length); - init_hunits_env_reg(".l", get_line_length); - init_hunits_env_reg(".ll", get_saved_line_length); - init_int_env_reg(".L", get_line_spacing); - init_hunits_env_reg(".n", get_prev_text_length); - init_string_env_reg(".s", get_point_size_string); - init_string_env_reg(".sr", get_requested_point_size_string); - init_int_env_reg(".ps", get_point_size); - init_int_env_reg(".psr", get_requested_point_size); - init_int_env_reg(".u", get_fill); - init_vunits_env_reg(".v", get_vertical_spacing); - init_vunits_env_reg(".pvs", get_post_vertical_spacing); - init_hunits_env_reg(".w", get_prev_char_width); - init_int_env_reg(".ss", get_space_size); - init_int_env_reg(".sss", get_sentence_space_size); - init_string_env_reg(".fam", get_font_family_string); - init_string_env_reg(".ev", get_name_string); - init_int_env_reg(".hy", get_hyphenation_flags); - init_int_env_reg(".hlm", get_hyphen_line_max); - init_int_env_reg(".hlc", get_hyphen_line_count); - init_hunits_env_reg(".lt", get_title_length); - init_string_env_reg(".tabs", get_tabs); - init_hunits_env_reg(".csk", get_prev_char_skew); - init_vunits_env_reg(".cht", get_prev_char_height); - init_vunits_env_reg(".cdp", get_prev_char_depth); - init_int_env_reg(".ce", get_center_lines); - init_int_env_reg(".rj", get_right_justify_lines); - init_hunits_env_reg(".hys", get_hyphenation_space); - init_hunits_env_reg(".hym", get_hyphenation_margin); - number_reg_dictionary.define("ln", new variable_reg(&next_line_number)); - number_reg_dictionary.define("ct", new variable_reg(&ct_reg_contents)); - number_reg_dictionary.define("sb", new variable_reg(&sb_reg_contents)); - number_reg_dictionary.define("st", new variable_reg(&st_reg_contents)); - number_reg_dictionary.define("rsb", new variable_reg(&rsb_reg_contents)); - number_reg_dictionary.define("rst", new variable_reg(&rst_reg_contents)); - number_reg_dictionary.define("ssc", new variable_reg(&ssc_reg_contents)); - number_reg_dictionary.define("skw", new variable_reg(&skw_reg_contents)); - number_reg_dictionary.define("hp", new horizontal_place_reg); -} - -// Hyphenation - TeX's hyphenation algorithm with a less fancy implementation. - -struct trie_node; - -class trie { - trie_node *tp; - virtual void do_match(int len, void *val) = 0; - virtual void do_delete(void *) = 0; - void delete_trie_node(trie_node *); -public: - trie() : tp(0) {} - virtual ~trie(); // virtual to shut up g++ - void insert(const char *, int, void *); - // find calls do_match for each match it finds - void find(const char *pat, int patlen); - void clear(); -}; - -class hyphen_trie : private trie { - int *h; - void do_match(int i, void *v); - void do_delete(void *v); - void insert_pattern(const char *pat, int patlen, int *num); -public: - hyphen_trie() {} - ~hyphen_trie() {} - void hyphenate(const char *word, int len, int *hyphens); - void read_patterns_file(const char *name); -}; - - -struct hyphenation_language { - symbol name; - dictionary exceptions; - hyphen_trie patterns; - hyphenation_language(symbol nm) : name(nm), exceptions(501) {} - ~hyphenation_language() { } -}; - -dictionary language_dictionary(5); -hyphenation_language *current_language = 0; - -static void set_hyphenation_language() -{ - symbol nm = get_name(1); - if (!nm.is_null()) { - current_language = (hyphenation_language *)language_dictionary.lookup(nm); - if (!current_language) { - current_language = new hyphenation_language(nm); - (void)language_dictionary.lookup(nm, (void *)current_language); - } - } - skip_line(); -} - -const int WORD_MAX = 1024; - -static void hyphen_word() -{ - if (!current_language) { - error("no current hyphenation language"); - skip_line(); - return; - } - char buf[WORD_MAX + 1]; - unsigned char pos[WORD_MAX + 2]; - for (;;) { - tok.skip(); - if (tok.newline() || tok.eof()) - break; - int i = 0; - int npos = 0; - while (i < WORD_MAX && !tok.space() && !tok.newline() && !tok.eof()) { - charinfo *ci = tok.get_char(1); - if (ci == 0) { - skip_line(); - return; - } - tok.next(); - if (ci->get_ascii_code() == '-') { - if (i > 0 && (npos == 0 || pos[npos - 1] != i)) - pos[npos++] = i; - } - else { - int c = ci->get_hyphenation_code(); - if (c == 0) - break; - buf[i++] = c; - } - } - if (i > 0) { - pos[npos] = 0; - buf[i] = 0; - unsigned char *tem = new unsigned char[npos + 1]; - memcpy(tem, pos, npos+1); - tem = (unsigned char *)current_language->exceptions.lookup(symbol(buf), - tem); - if (tem) - a_delete tem; - } - } - skip_line(); -} - -struct trie_node { - char c; - trie_node *down; - trie_node *right; - void *val; - trie_node(char, trie_node *); -}; - -trie_node::trie_node(char ch, trie_node *p) -: c(ch), down(0), right(p), val(0) -{ -} - -trie::~trie() -{ - clear(); -} - -void trie::clear() -{ - delete_trie_node(tp); - tp = 0; -} - - -void trie::delete_trie_node(trie_node *p) -{ - if (p) { - delete_trie_node(p->down); - delete_trie_node(p->right); - if (p->val) - do_delete(p->val); - delete p; - } -} - -void trie::insert(const char *pat, int patlen, void *val) -{ - trie_node **p = &tp; - assert(patlen > 0 && pat != 0); - for (;;) { - while (*p != 0 && (*p)->c < pat[0]) - p = &((*p)->right); - if (*p == 0 || (*p)->c != pat[0]) - *p = new trie_node(pat[0], *p); - if (--patlen == 0) { - (*p)->val = val; - break; - } - ++pat; - p = &((*p)->down); - } -} - -void trie::find(const char *pat, int patlen) -{ - trie_node *p = tp; - for (int i = 0; p != 0 && i < patlen; i++) { - while (p != 0 && p->c < pat[i]) - p = p->right; - if (p != 0 && p->c == pat[i]) { - if (p->val != 0) - do_match(i+1, p->val); - p = p->down; - } - else - break; - } -} - -struct operation { - operation *next; - short distance; - short num; - operation(int, int, operation *); -}; - -operation::operation(int i, int j, operation *op) -: next(op), distance(j), num(i) -{ -} - -void hyphen_trie::insert_pattern(const char *pat, int patlen, int *num) -{ - operation *op = 0; - for (int i = 0; i < patlen+1; i++) - if (num[i] != 0) - op = new operation(num[i], patlen - i, op); - insert(pat, patlen, op); -} - -void hyphen_trie::hyphenate(const char *word, int len, int *hyphens) -{ - int j; - for (j = 0; j < len+1; j++) - hyphens[j] = 0; - for (j = 0; j < len - 1; j++) { - h = hyphens + j; - find(word + j, len - j); - } -} - -inline int max(int m, int n) -{ - return m > n ? m : n; -} - -void hyphen_trie::do_match(int i, void *v) -{ - operation *op = (operation *)v; - while (op != 0) { - h[i - op->distance] = max(h[i - op->distance], op->num); - op = op->next; - } -} - -void hyphen_trie::do_delete(void *v) -{ - operation *op = (operation *)v; - while (op) { - operation *tem = op; - op = tem->next; - delete tem; - } -} - -void hyphen_trie::read_patterns_file(const char *name) -{ - clear(); - char buf[WORD_MAX]; - int num[WORD_MAX+1]; - errno = 0; - char *path = 0; - FILE *fp = macro_path.open_file(name, &path); - if (fp == 0) { - error("can't find hyphenation patterns file `%1'", name); - return; - } - int c = getc(fp); - for (;;) { - for (;;) { - if (c == '%') { - do { - c = getc(fp); - } while (c != EOF && c != '\n'); - } - if (c == EOF || !csspace(c)) - break; - c = getc(fp); - } - if (c == EOF) - break; - int i = 0; - num[0] = 0; - do { - if (csdigit(c)) - num[i] = c - '0'; - else { - buf[i++] = c; - num[i] = 0; - } - c = getc(fp); - } while (i < WORD_MAX && c != EOF && !csspace(c) && c != '%'); - insert_pattern(buf, i, num); - } - fclose(fp); - a_delete path; - return; -} - -void hyphenate(hyphen_list *h, unsigned flags) -{ - if (!current_language) - return; - while (h) { - while (h && h->hyphenation_code == 0) - h = h->next; - int len = 0; - char hbuf[WORD_MAX+2]; - char *buf = hbuf + 1; - hyphen_list *tem; - for (tem = h; tem && len < WORD_MAX; tem = tem->next) { - if (tem->hyphenation_code != 0) - buf[len++] = tem->hyphenation_code; - else - break; - } - hyphen_list *nexth = tem; - if (len > 2) { - buf[len] = 0; - unsigned char *pos - = (unsigned char *)current_language->exceptions.lookup(buf); - if (pos != 0) { - int j = 0; - int i = 1; - for (tem = h; tem != 0; tem = tem->next, i++) - if (pos[j] == i) { - tem->hyphen = 1; - j++; - } - } - else { - hbuf[0] = hbuf[len+1] = '.'; - int num[WORD_MAX+3]; - current_language->patterns.hyphenate(hbuf, len+2, num); - int i; - num[2] = 0; - if (flags & 8) - num[3] = 0; - if (flags & 4) - --len; - for (i = 2, tem = h; i < len && tem; tem = tem->next, i++) - if (num[i] & 1) - tem->hyphen = 1; - } - } - h = nexth; - } -} - -static void hyphenation_patterns_file() -{ - symbol name = get_long_name(1); - if (!name.is_null()) { - if (!current_language) - error("no current hyphenation language"); - else - current_language->patterns.read_patterns_file(name.contents()); - } - skip_line(); -} - -class hyphenation_language_reg : public reg { -public: - const char *get_string(); -}; - -const char *hyphenation_language_reg::get_string() -{ - return current_language ? current_language->name.contents() : ""; -} - -void init_hyphen_requests() -{ - init_request("hw", hyphen_word); - init_request("hla", set_hyphenation_language); - init_request("hpf", hyphenation_patterns_file); - number_reg_dictionary.define(".hla", new hyphenation_language_reg); -} |