diff options
Diffstat (limited to 'contrib/groff/src/preproc')
77 files changed, 40824 insertions, 0 deletions
diff --git a/contrib/groff/src/preproc/eqn/Makefile.sub b/contrib/groff/src/preproc/eqn/Makefile.sub new file mode 100644 index 0000000..20421e1c --- /dev/null +++ b/contrib/groff/src/preproc/eqn/Makefile.sub @@ -0,0 +1,59 @@ +PROG=eqn +MAN1=eqn.n neqn.n +XLIBS=$(LIBGROFF) +OBJS=\ + eqn.o \ + main.o \ + lex.o \ + box.o \ + limit.o \ + list.o \ + over.o \ + text.o \ + script.o \ + mark.o \ + other.o \ + delim.o \ + sqrt.o \ + pile.o \ + special.o +CCSRCS=\ + $(srcdir)/main.cc \ + $(srcdir)/lex.cc \ + $(srcdir)/box.cc \ + $(srcdir)/limit.cc \ + $(srcdir)/list.cc \ + $(srcdir)/over.cc \ + $(srcdir)/text.cc \ + $(srcdir)/script.cc \ + $(srcdir)/mark.cc \ + $(srcdir)/other.cc \ + $(srcdir)/delim.cc \ + $(srcdir)/sqrt.cc \ + $(srcdir)/pile.cc \ + $(srcdir)/special.cc +HDRS=\ + $(srcdir)/box.h \ + $(srcdir)/eqn.h \ + $(srcdir)/pbox.h +GRAM=$(srcdir)/eqn.y +YTABC=$(srcdir)/eqn.cc +YTABH=$(srcdir)/eqn_tab.h +NAMEPREFIX=$(g) +CLEANADD=neqn + +all: neqn + +neqn: neqn.sh + -rm -f $@ + sed -e 's/@g@/$(g)/g' \ + -e 's|@BINDIR@|$(bindir)|g' \ + -e $(SH_SCRIPT_SED_CMD) $(srcdir)/neqn.sh >$@ + chmod +x $@ + +install_data: neqn + -rm -f $(bindir)/$(NAMEPREFIX)neqn + $(INSTALL_SCRIPT) neqn $(bindir)/$(NAMEPREFIX)neqn + +uninstall_sub: + -rm -f $(bindir)/$(NAMEPREFIX)neqn diff --git a/contrib/groff/src/preproc/eqn/TODO b/contrib/groff/src/preproc/eqn/TODO new file mode 100644 index 0000000..210d0ab --- /dev/null +++ b/contrib/groff/src/preproc/eqn/TODO @@ -0,0 +1,49 @@ +Use the same size increases for sum prod int as eqn does. + +Perhaps chartype should be renamed. + +TeX makes {sub,super}script on a single character with an accent +into an accent onto the (character with the script). Should we do this? + +Implement mark and lineups within scripts, matrices and piles, and accents. +(Why would this be useful?) + +Perhaps push hmotions down through lists to avoid upsetting spacing +adjustments. + +Possibly generate .lf commands during compute_metrics phase. + +Consider whether there should be extra space at the side of piles. + +Provide scriptstyle displaystyle etc. + +Provide a nicer matrix syntax, eg +matrix ccc { +a then b then c above +e then f then g above +h then i then k +} + +Perhaps generate syntax error messages using the style of gpic. + +Wide accents. + +More use of \Z. + +Extensible square roots. + +Vphantom + +Smash. + +Provide a variant of vec that extends over the length of the accentee. + +Support vertical arrow delimiters. + +Make the following work: +.EQ +delim @@ +.EN +.EQ @<-@ +some equation +.EN diff --git a/contrib/groff/src/preproc/eqn/box.cc b/contrib/groff/src/preproc/eqn/box.cc new file mode 100644 index 0000000..4e61b5d --- /dev/null +++ b/contrib/groff/src/preproc/eqn/box.cc @@ -0,0 +1,611 @@ +// -*- 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 "eqn.h" +#include "pbox.h" + +const char *current_roman_font; + +char *gfont = 0; +char *grfont = 0; +char *gbfont = 0; +int gsize = 0; + +int script_size_reduction = -1; // negative means reduce by a percentage + +int positive_space = -1; +int negative_space = -1; + +int minimum_size = 5; + +int fat_offset = 4; +int body_height = 85; +int body_depth = 35; + +int over_hang = 0; +int accent_width = 31; +int delimiter_factor = 900; +int delimiter_shortfall = 50; + +int null_delimiter_space = 12; +int script_space = 5; +int thin_space = 17; +int medium_space = 22; +int thick_space = 28; + +int num1 = 70; +int num2 = 40; +// we don't use num3, because we don't have \atop +int denom1 = 70; +int denom2 = 36; +int axis_height = 26; // in 100ths of an em +int sup1 = 42; +int sup2 = 37; +int sup3 = 28; +int default_rule_thickness = 4; +int sub1 = 20; +int sub2 = 23; +int sup_drop = 38; +int sub_drop = 5; +int x_height = 45; +int big_op_spacing1 = 11; +int big_op_spacing2 = 17; +int big_op_spacing3 = 20; +int big_op_spacing4 = 60; +int big_op_spacing5 = 10; + +// These are for piles and matrices. + +int baseline_sep = 140; // = num1 + denom1 +int shift_down = 26; // = axis_height +int column_sep = 100; // = em space +int matrix_side_sep = 17; // = thin space + +int nroff = 0; // should we grok ndefine or tdefine? + +struct { + const char *name; + int *ptr; +} param_table[] = { + { "fat_offset", &fat_offset }, + { "over_hang", &over_hang }, + { "accent_width", &accent_width }, + { "delimiter_factor", &delimiter_factor }, + { "delimiter_shortfall", &delimiter_shortfall }, + { "null_delimiter_space", &null_delimiter_space }, + { "script_space", &script_space }, + { "thin_space", &thin_space }, + { "medium_space", &medium_space }, + { "thick_space", &thick_space }, + { "num1", &num1 }, + { "num2", &num2 }, + { "denom1", &denom1 }, + { "denom2", &denom2 }, + { "axis_height", &axis_height }, + { "sup1", ¹ }, + { "sup2", ² }, + { "sup3", ³ }, + { "default_rule_thickness", &default_rule_thickness }, + { "sub1", &sub1 }, + { "sub2", &sub2 }, + { "sup_drop", &sup_drop }, + { "sub_drop", &sub_drop }, + { "x_height", &x_height }, + { "big_op_spacing1", &big_op_spacing1 }, + { "big_op_spacing2", &big_op_spacing2 }, + { "big_op_spacing3", &big_op_spacing3 }, + { "big_op_spacing4", &big_op_spacing4 }, + { "big_op_spacing5", &big_op_spacing5 }, + { "minimum_size", &minimum_size }, + { "baseline_sep", &baseline_sep }, + { "shift_down", &shift_down }, + { "column_sep", &column_sep }, + { "matrix_side_sep", &matrix_side_sep }, + { "draw_lines", &draw_flag }, + { "body_height", &body_height }, + { "body_depth", &body_depth }, + { "nroff", &nroff }, + { 0, 0 } +}; + +void set_param(const char *name, int value) +{ + for (int i = 0; param_table[i].name != 0; i++) + if (strcmp(param_table[i].name, name) == 0) { + *param_table[i].ptr = value; + return; + } + error("unrecognised parameter `%1'", name); +} + +int script_style(int style) +{ + return style > SCRIPT_STYLE ? style - 2 : style; +} + +int cramped_style(int style) +{ + return (style & 1) ? style - 1 : style; +} + +void set_space(int n) +{ + if (n < 0) + negative_space = -n; + else + positive_space = n; +} + +// Return 0 if the specified size is bad. +// The caller is responsible for giving the error message. + +int set_gsize(const char *s) +{ + const char *p = (*s == '+' || *s == '-') ? s + 1 : s; + char *end; + long n = strtol(p, &end, 10); + if (n <= 0 || *end != '\0' || n > INT_MAX) + return 0; + if (p > s) { + if (!gsize) + gsize = 10; + if (*s == '+') { + if (gsize > INT_MAX - n) + return 0; + gsize += int(n); + } + else { + if (gsize - n <= 0) + return 0; + gsize -= int(n); + } + } + else + gsize = int(n); + return 1; +} + +void set_script_reduction(int n) +{ + script_size_reduction = n; +} + +const char *get_gfont() +{ + return gfont ? gfont : "I"; +} + +const char *get_grfont() +{ + return grfont ? grfont : "R"; +} + +const char *get_gbfont() +{ + return gbfont ? gbfont : "B"; +} + +void set_gfont(const char *s) +{ + a_delete gfont; + gfont = strsave(s); +} + +void set_grfont(const char *s) +{ + a_delete grfont; + grfont = strsave(s); +} + +void set_gbfont(const char *s) +{ + a_delete gbfont; + gbfont = strsave(s); +} + +// this must be precisely 2 characters in length +#define COMPATIBLE_REG "0C" + +void start_string() +{ + printf(".nr " COMPATIBLE_REG " \\n(.C\n"); + printf(".cp 0\n"); + printf(".ds " LINE_STRING "\n"); +} + +void output_string() +{ + printf("\\*[" LINE_STRING "]\n"); +} + +void restore_compatibility() +{ + printf(".cp \\n(" COMPATIBLE_REG "\n"); +} + +void do_text(const char *s) +{ + printf(".eo\n"); + printf(".as " LINE_STRING " \"%s\n", s); + printf(".ec\n"); +} + +void set_minimum_size(int n) +{ + minimum_size = n; +} + +void set_script_size() +{ + if (minimum_size < 0) + minimum_size = 0; + if (script_size_reduction >= 0) + printf(".ps \\n[.s]-%d>?%d\n", script_size_reduction, minimum_size); + else + printf(".ps (u;\\n[.s]*7+5/10>?%d)*1z\n", minimum_size); +} + +int box::next_uid = 0; + +box::box() : spacing_type(ORDINARY_TYPE), uid(next_uid++) +{ +} + +box::~box() +{ +} + +void box::top_level() +{ + // debug_print(); + // putc('\n', stderr); + box *b = this; + printf(".nr " SAVED_FONT_REG " \\n[.f]\n"); + printf(".ft\n"); + printf(".nr " SAVED_PREV_FONT_REG " \\n[.f]\n"); + printf(".ft %s\n", get_gfont()); + printf(".nr " SAVED_SIZE_REG " \\n[.s]z\n"); + if (gsize > 0) { + char buf[INT_DIGITS + 1]; + sprintf(buf, "%d", gsize); + b = new size_box(strsave(buf), b); + } + current_roman_font = get_grfont(); + // This catches tabs used within \Z (which aren't allowed). + b->check_tabs(0); + int r = b->compute_metrics(DISPLAY_STYLE); + printf(".ft \\n[" SAVED_PREV_FONT_REG "]\n"); + printf(".ft \\n[" SAVED_FONT_REG "]\n"); + printf(".nr " MARK_OR_LINEUP_FLAG_REG " %d\n", r); + if (r == FOUND_MARK) { + printf(".nr " SAVED_MARK_REG " \\n[" MARK_REG "]\n"); + printf(".nr " MARK_WIDTH_REG " 0\\n[" WIDTH_FORMAT "]\n", b->uid); + } + else if (r == FOUND_LINEUP) + printf(".if r" SAVED_MARK_REG " .as " LINE_STRING " \\h'\\n[" + SAVED_MARK_REG "]u-\\n[" MARK_REG "]u'\n"); + else + assert(r == FOUND_NOTHING); + // The problem here is that the argument to \f is read in copy mode, + // so we cannot use \E there; so we hide it in a string instead. + // Another problem is that if we use \R directly, then the space will + // prevent it working in a macro argument. + printf(".ds " SAVE_FONT_STRING " " + "\\R'" SAVED_INLINE_FONT_REG " \\\\n[.f]'" + "\\fP" + "\\R'" SAVED_INLINE_PREV_FONT_REG " \\\\n[.f]'" + "\\R'" SAVED_INLINE_SIZE_REG " \\\\n[.s]z'" + "\\s0" + "\\R'" SAVED_INLINE_PREV_SIZE_REG " \\\\n[.s]z'" + "\n" + ".ds " RESTORE_FONT_STRING " " + "\\f[\\\\n[" SAVED_INLINE_PREV_FONT_REG "]]" + "\\f[\\\\n[" SAVED_INLINE_FONT_REG "]]" + "\\s'\\\\n[" SAVED_INLINE_PREV_SIZE_REG "]u'" + "\\s'\\\\n[" SAVED_INLINE_SIZE_REG "]u'" + "\n"); + printf(".as " LINE_STRING " \\&\\E*[" SAVE_FONT_STRING "]"); + printf("\\f[%s]", get_gfont()); + printf("\\s'\\En[" SAVED_SIZE_REG "]u'"); + current_roman_font = get_grfont(); + b->output(); + printf("\\E*[" RESTORE_FONT_STRING "]\n"); + if (r == FOUND_LINEUP) + printf(".if r" SAVED_MARK_REG " .as " LINE_STRING " \\h'\\n[" + MARK_WIDTH_REG "]u-\\n[" SAVED_MARK_REG "]u-(\\n[" + WIDTH_FORMAT "]u-\\n[" MARK_REG "]u)'\n", + b->uid); + b->extra_space(); + if (!inline_flag) + printf(".ne \\n[" HEIGHT_FORMAT "]u-%dM>?0+(\\n[" + DEPTH_FORMAT "]u-%dM>?0)\n", + b->uid, body_height, b->uid, body_depth); + delete b; + next_uid = 0; +} + +// gpic defines this register so as to make geqn not produce `\x's +#define EQN_NO_EXTRA_SPACE_REG "0x" + +void box::extra_space() +{ + printf(".if !r" EQN_NO_EXTRA_SPACE_REG " " + ".nr " EQN_NO_EXTRA_SPACE_REG " 0\n"); + if (positive_space >= 0 || negative_space >= 0) { + if (positive_space > 0) + printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] " + ".as " LINE_STRING " \\x'-%dM'\n", positive_space); + if (negative_space > 0) + printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] " + ".as " LINE_STRING " \\x'%dM'\n", negative_space); + positive_space = negative_space = -1; + } + else { + printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] " + ".if \\n[" HEIGHT_FORMAT "]>%dM .as " LINE_STRING + " \\x'-(\\n[" HEIGHT_FORMAT + "]u-%dM)'\n", + uid, body_height, uid, body_height); + printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] " + ".if \\n[" DEPTH_FORMAT "]>%dM .as " LINE_STRING + " \\x'\\n[" DEPTH_FORMAT + "]u-%dM'\n", + uid, body_depth, uid, body_depth); + } +} + +int box::compute_metrics(int) +{ + printf(".nr " WIDTH_FORMAT " 0\n", uid); + printf(".nr " HEIGHT_FORMAT " 0\n", uid); + printf(".nr " DEPTH_FORMAT " 0\n", uid); + return FOUND_NOTHING; +} + +void box::compute_subscript_kern() +{ + printf(".nr " SUB_KERN_FORMAT " 0\n", uid); +} + +void box::compute_skew() +{ + printf(".nr " SKEW_FORMAT " 0\n", uid); +} + +void box::output() +{ +} + +void box::check_tabs(int) +{ +} + +int box::is_char() +{ + return 0; +} + +int box::left_is_italic() +{ + return 0; +} + +int box::right_is_italic() +{ + return 0; +} + +void box::hint(unsigned) +{ +} + +void box::handle_char_type(int, int) +{ +} + + +box_list::box_list(box *pp) +{ + p = new box*[10]; + for (int i = 0; i < 10; i++) + p[i] = 0; + maxlen = 10; + len = 1; + p[0] = pp; +} + +void box_list::append(box *pp) +{ + if (len + 1 > maxlen) { + box **oldp = p; + maxlen *= 2; + p = new box*[maxlen]; + memcpy(p, oldp, sizeof(box*)*len); + a_delete oldp; + } + p[len++] = pp; +} + +box_list::~box_list() +{ + for (int i = 0; i < len; i++) + delete p[i]; + a_delete p; +} + +void box_list::list_check_tabs(int level) +{ + for (int i = 0; i < len; i++) + p[i]->check_tabs(level); +} + + +pointer_box::pointer_box(box *pp) : p(pp) +{ + spacing_type = p->spacing_type; +} + +pointer_box::~pointer_box() +{ + delete p; +} + +int pointer_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + return r; +} + +void pointer_box::compute_subscript_kern() +{ + p->compute_subscript_kern(); + printf(".nr " SUB_KERN_FORMAT " \\n[" SUB_KERN_FORMAT "]\n", uid, p->uid); +} + +void pointer_box::compute_skew() +{ + p->compute_skew(); + printf(".nr " SKEW_FORMAT " 0\\n[" SKEW_FORMAT "]\n", + uid, p->uid); +} + +void pointer_box::check_tabs(int level) +{ + p->check_tabs(level); +} + +int simple_box::compute_metrics(int) +{ + printf(".nr " WIDTH_FORMAT " 0\\w" DELIMITER_CHAR, uid); + output(); + printf(DELIMITER_CHAR "\n"); + printf(".nr " HEIGHT_FORMAT " 0>?\\n[rst]\n", uid); + printf(".nr " DEPTH_FORMAT " 0-\\n[rsb]>?0\n", uid); + printf(".nr " SUB_KERN_FORMAT " 0-\\n[ssc]>?0\n", uid); + printf(".nr " SKEW_FORMAT " 0\\n[skw]\n", uid); + return FOUND_NOTHING; +} + +void simple_box::compute_subscript_kern() +{ + // do nothing, we already computed it in do_metrics +} + +void simple_box::compute_skew() +{ + // do nothing, we already computed it in do_metrics +} + +int box::is_simple() +{ + return 0; +} + +int simple_box::is_simple() +{ + return 1; +} + +quoted_text_box::quoted_text_box(char *s) : text(s) +{ +} + +quoted_text_box::~quoted_text_box() +{ + a_delete text; +} + +void quoted_text_box::output() +{ + if (text) + fputs(text, stdout); +} + +tab_box::tab_box() : disabled(0) +{ +} + +// We treat a tab_box as having width 0 for width computations. + +void tab_box::output() +{ + if (!disabled) + printf("\\t"); +} + +void tab_box::check_tabs(int level) +{ + if (level > 0) { + error("tabs allowed only at outermost level"); + disabled = 1; + } +} + +space_box::space_box() +{ + spacing_type = SUPPRESS_TYPE; +} + +void space_box::output() +{ + printf("\\h'%dM'", thick_space); +} + +half_space_box::half_space_box() +{ + spacing_type = SUPPRESS_TYPE; +} + +void half_space_box::output() +{ + printf("\\h'%dM'", thin_space); +} + +void box_list::list_debug_print(const char *sep) +{ + p[0]->debug_print(); + for (int i = 1; i < len; i++) { + fprintf(stderr, "%s", sep); + p[i]->debug_print(); + } +} + +void quoted_text_box::debug_print() +{ + fprintf(stderr, "\"%s\"", (text ? text : "")); +} + +void half_space_box::debug_print() +{ + fprintf(stderr, "^"); +} + +void space_box::debug_print() +{ + fprintf(stderr, "~"); +} + +void tab_box::debug_print() +{ + fprintf(stderr, "<tab>"); +} diff --git a/contrib/groff/src/preproc/eqn/box.h b/contrib/groff/src/preproc/eqn/box.h new file mode 100644 index 0000000..01bfe96 --- /dev/null +++ b/contrib/groff/src/preproc/eqn/box.h @@ -0,0 +1,277 @@ +// -*- 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. */ + +struct list_box; + +class box { +private: + static int next_uid; +public: + int spacing_type; + const int uid; + box(); + virtual void debug_print() = 0; + virtual ~box(); + void top_level(); + virtual int compute_metrics(int); + virtual void compute_subscript_kern(); + virtual void compute_skew(); + virtual void output(); + void extra_space(); + virtual list_box *to_list_box(); + virtual int is_simple(); + virtual int is_char(); + virtual int left_is_italic(); + virtual int right_is_italic(); + virtual void handle_char_type(int, int); + enum { FOUND_NOTHING = 0, FOUND_MARK = 1, FOUND_LINEUP = 2 }; + void set_spacing_type(char *type); + virtual void hint(unsigned); + virtual void check_tabs(int); +}; + +class box_list { +private: + int maxlen; +public: + box **p; + int len; + + box_list(box *); + ~box_list(); + void append(box *); + void list_check_tabs(int); + void list_debug_print(const char *sep); + friend class list_box; +}; + +class list_box : public box { + int is_script; + box_list list; + int sty; +public: + list_box(box *); + void debug_print(); + int compute_metrics(int); + void compute_subscript_kern(); + void output(); + void check_tabs(int); + void append(box *); + list_box *to_list_box(); + void handle_char_type(int, int); + void compute_sublist_width(int n); + friend box *make_script_box(box *, box *, box *); + friend box *make_mark_box(box *); + friend box *make_lineup_box(box *); +}; + +enum alignment { LEFT_ALIGN, RIGHT_ALIGN, CENTER_ALIGN }; + +class column : public box_list { + alignment align; + int space; +public: + column(box *); + void set_alignment(alignment); + void set_space(int); + void debug_print(const char *); + + friend class matrix_box; + friend class pile_box; +}; + +class pile_box : public box { + column col; +public: + pile_box(box *); + int compute_metrics(int); + void output(); + void debug_print(); + void check_tabs(int); + void set_alignment(alignment a) { col.set_alignment(a); } + void set_space(int n) { col.set_space(n); } + void append(box *p) { col.append(p); } +}; + +class matrix_box : public box { +private: + int len; + int maxlen; + column **p; +public: + matrix_box(column *); + ~matrix_box(); + void append(column *); + int compute_metrics(int); + void output(); + void check_tabs(int); + void debug_print(); +}; + +class pointer_box : public box { +protected: + box *p; +public: + pointer_box(box *); + ~pointer_box(); + int compute_metrics(int); + void compute_subscript_kern(); + void compute_skew(); + void debug_print() = 0; + void check_tabs(int); +}; + +class vcenter_box : public pointer_box { +public: + vcenter_box(box *); + int compute_metrics(int); + void output(); + void debug_print(); +}; + +class simple_box : public box { +public: + int compute_metrics(int); + void compute_subscript_kern(); + void compute_skew(); + void output() = 0; + void debug_print() = 0; + int is_simple(); +}; + +class quoted_text_box : public simple_box { + char *text; +public: + quoted_text_box(char *); + ~quoted_text_box(); + void debug_print(); + void output(); +}; + +class half_space_box : public simple_box { +public: + half_space_box(); + void output(); + void debug_print(); +}; + +class space_box : public simple_box { +public: + space_box(); + void output(); + void debug_print(); +}; + +class tab_box : public box { + int disabled; +public: + tab_box(); + void output(); + void debug_print(); + void check_tabs(int); +}; + +class size_box : public pointer_box { +private: + char *size; +public: + size_box(char *, box *); + ~size_box(); + int compute_metrics(int); + void output(); + void debug_print(); +}; + +class font_box : public pointer_box { +private: + char *f; +public: + font_box(char *, box *); + ~font_box(); + int compute_metrics(int); + void output(); + void debug_print(); +}; + +class fat_box : public pointer_box { +public: + fat_box(box *); + int compute_metrics(int); + void output(); + void debug_print(); +}; + +class vmotion_box : public pointer_box { +private: + int n; // up is >= 0 +public: + vmotion_box(int, box *); + int compute_metrics(int); + void output(); + void debug_print(); +}; + +class hmotion_box : public pointer_box { + int n; +public: + hmotion_box(int, box *); + int compute_metrics(int); + void output(); + void debug_print(); +}; + +box *split_text(char *); +box *make_script_box(box *, box *, box *); +box *make_mark_box(box *); +box *make_lineup_box(box *); +box *make_delim_box(char *, box *, char *); +box *make_sqrt_box(box *); +box *make_prime_box(box *); +box *make_over_box(box *, box *); +box *make_small_over_box(box *, box *); +box *make_limit_box(box *, box *, box *); +box *make_accent_box(box *, box *); +box *make_uaccent_box(box *, box *); +box *make_overline_box(box *); +box *make_underline_box(box *); +box *make_special_box(char *, box *); + +void set_space(int); +int set_gsize(const char *); +void set_gfont(const char *); +void set_grfont(const char *); +void set_gbfont(const char *); +const char *get_gfont(); +const char *get_grfont(); +const char *get_gbfont(); +void start_string(); +void output_string(); +void do_text(const char *); +void restore_compatibility(); +void set_script_reduction(int n); +void set_minimum_size(int n); +void set_param(const char *name, int value); + +void set_char_type(const char *type, char *ch); + +void init_char_table(); +void init_extensible(); +void define_extensible(const char *name, const char *ext, const char *top = 0, + const char *mid = 0, const char *bot = 0); diff --git a/contrib/groff/src/preproc/eqn/delim.cc b/contrib/groff/src/preproc/eqn/delim.cc new file mode 100644 index 0000000..29deded --- /dev/null +++ b/contrib/groff/src/preproc/eqn/delim.cc @@ -0,0 +1,381 @@ +// -*- 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 "eqn.h" +#include "pbox.h" + +enum left_or_right_t { LEFT_DELIM = 01, RIGHT_DELIM = 02 }; + +// Small must be none-zero and must exist in each device. +// Small will be put in the roman font, others are assumed to be +// on the special font (so no font change will be necessary.) + +struct delimiter { + const char *name; + int flags; + const char *small; + const char *chain_format; + const char *ext; + const char *top; + const char *mid; + const char *bot; +} delim_table[] = { + { + "(", LEFT_DELIM|RIGHT_DELIM, "(", "\\[parenleft%s]", + "\\[parenleftex]", + "\\[parenlefttp]", + 0, + "\\[parenleftbt]", + }, + { + ")", LEFT_DELIM|RIGHT_DELIM, ")", "\\[parenright%s]", + "\\[parenrightex]", + "\\[parenrighttp]", + 0, + "\\[parenrightbt]", + }, + { + "[", LEFT_DELIM|RIGHT_DELIM, "[", "\\[bracketleft%s]", + "\\[bracketleftex]", + "\\[bracketlefttp]", + 0, + "\\[bracketleftbt]", + }, + { + "]", LEFT_DELIM|RIGHT_DELIM, "]", "\\[bracketright%s]", + "\\[bracketrightex]", + "\\[bracketrighttp]", + 0, + "\\[bracketrightbt]", + }, + { + "{", LEFT_DELIM|RIGHT_DELIM, "{", "\\[braceleft%s]", + "\\[braceleftex]", + "\\[bracelefttp]", + "\\[braceleftmid]", + "\\[braceleftbt]", + }, + { + "}", LEFT_DELIM|RIGHT_DELIM, "}", "\\[braceright%s]", + "\\[bracerightex]", + "\\[bracerighttp]", + "\\[bracerightmid]", + "\\[bracerightbt]", + }, + { + "|", LEFT_DELIM|RIGHT_DELIM, "|", "\\[bar%s]", + "\\[barex]", + }, + { + "floor", LEFT_DELIM, "\\(lf", "\\[floorleft%s]", + "\\[bracketleftex]", + 0, + 0, + "\\[bracketleftbt]", + }, + { + "floor", RIGHT_DELIM, "\\(rf", "\\[floorright%s]", + "\\[bracketrightex]", + 0, + 0, + "\\[bracketrightbt]", + }, + { + "ceiling", LEFT_DELIM, "\\(lc", "\\[ceilingleft%s]", + "\\[bracketleftex]", + "\\[bracketlefttp]", + }, + { + "ceiling", RIGHT_DELIM, "\\(rc", "\\[ceilingright%s]", + "\\[bracketrightex]", + "\\[bracketrighttp]", + }, + { + "||", LEFT_DELIM|RIGHT_DELIM, "|", "\\[bar%s]", + "\\[bardblex]", + }, + { + "<", LEFT_DELIM|RIGHT_DELIM, "\\(la", "\\[angleleft%s]", + }, + { + ">", LEFT_DELIM|RIGHT_DELIM, "\\(ra", "\\[angleright%s]", + }, + { + "uparrow", LEFT_DELIM|RIGHT_DELIM, "\\(ua", "\\[arrowup%s]", + "\\[arrowvertex]", + "\\[arrowverttp]", + }, + { + "downarrow", LEFT_DELIM|RIGHT_DELIM, "\\(da", "\\[arrowdown%s]", + "\\[arrowvertex]", + 0, + 0, + "\\[arrowvertbt]", + }, + { + "updownarrow", LEFT_DELIM|RIGHT_DELIM, "\\(va", "\\[arrowupdown%s]", + "\\[arrowvertex]", + "\\[arrowverttp]", + 0, + "\\[arrowvertbt]", + }, +}; + +const int DELIM_TABLE_SIZE = int(sizeof(delim_table)/sizeof(delim_table[0])); + +class delim_box : public box { +private: + char *left; + char *right; + box *p; +public: + delim_box(char *, box *, char *); + ~delim_box(); + int compute_metrics(int); + void output(); + void check_tabs(int); + void debug_print(); +}; + +box *make_delim_box(char *l, box *pp, char *r) +{ + if (l != 0 && *l == '\0') { + a_delete l; + l = 0; + } + if (r != 0 && *r == '\0') { + a_delete r; + r = 0; + } + return new delim_box(l, pp, r); +} + +delim_box::delim_box(char *l, box *pp, char *r) +: left(l), right(r), p(pp) +{ +} + +delim_box::~delim_box() +{ + a_delete left; + a_delete right; + delete p; +} + +static void build_extensible(const char *ext, const char *top, const char *mid, + const char *bot) +{ + assert(ext != 0); + printf(".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n", + ext); + printf(".nr " EXT_HEIGHT_REG " 0\\n[rst]\n"); + printf(".nr " EXT_DEPTH_REG " 0-\\n[rsb]\n"); + if (top) { + printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]" + ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n", + top); + printf(".nr " TOP_HEIGHT_REG " 0\\n[rst]\n"); + printf(".nr " TOP_DEPTH_REG " 0-\\n[rsb]\n"); + } + if (mid) { + printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]" + ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n", + mid); + printf(".nr " MID_HEIGHT_REG " 0\\n[rst]\n"); + printf(".nr " MID_DEPTH_REG " 0-\\n[rsb]\n"); + } + if (bot) { + printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]" + ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n", + bot); + printf(".nr " BOT_HEIGHT_REG " 0\\n[rst]\n"); + printf(".nr " BOT_DEPTH_REG " 0-\\n[rsb]\n"); + } + printf(".nr " TOTAL_HEIGHT_REG " 0"); + if (top) + printf("+\\n[" TOP_HEIGHT_REG "]+\\n[" TOP_DEPTH_REG "]"); + if (bot) + printf("+\\n[" BOT_HEIGHT_REG "]+\\n[" BOT_DEPTH_REG "]"); + if (mid) + printf("+\\n[" MID_HEIGHT_REG "]+\\n[" MID_DEPTH_REG "]"); + printf("\n"); + // determine how many extensible characters we need + printf(".nr " TEMP_REG " \\n[" DELTA_REG "]-\\n[" TOTAL_HEIGHT_REG "]"); + if (mid) + printf("/2"); + printf(">?0+\\n[" EXT_HEIGHT_REG "]+\\n[" EXT_DEPTH_REG "]-1/(\\n[" + EXT_HEIGHT_REG "]+\\n[" EXT_DEPTH_REG "])\n"); + + printf(".nr " TOTAL_HEIGHT_REG " +(\\n[" EXT_HEIGHT_REG "]+\\n[" + EXT_DEPTH_REG "]*\\n[" TEMP_REG "]"); + if (mid) + printf("*2"); + printf(")\n"); + printf(".ds " DELIM_STRING " \\Z" DELIMITER_CHAR + "\\v'-%dM-(\\n[" TOTAL_HEIGHT_REG "]u/2u)'\n", + axis_height); + if (top) + printf(".as " DELIM_STRING " \\v'\\n[" TOP_HEIGHT_REG "]u'" + "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR + "\\v'\\n[" TOP_DEPTH_REG "]u'\n", + top); + + // this macro appends $2 copies of $3 to string $1 + printf(".de " REPEAT_APPEND_STRING_MACRO "\n" + ".if \\\\$2 \\{.as \\\\$1 \"\\\\$3\n" + "." REPEAT_APPEND_STRING_MACRO " \\\\$1 \\\\$2-1 \"\\\\$3\"\n" + ".\\}\n" + "..\n"); + + printf("." REPEAT_APPEND_STRING_MACRO " " DELIM_STRING " \\n[" TEMP_REG "] " + "\\v'\\n[" EXT_HEIGHT_REG "]u'" + "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR + "\\v'\\n[" EXT_DEPTH_REG "]u'\n", + ext); + + if (mid) { + printf(".as " DELIM_STRING " \\v'\\n[" MID_HEIGHT_REG "]u'" + "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR + "\\v'\\n[" MID_DEPTH_REG "]u'\n", + mid); + printf("." REPEAT_APPEND_STRING_MACRO " " DELIM_STRING + " \\n[" TEMP_REG "] " + "\\v'\\n[" EXT_HEIGHT_REG "]u'" + "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR + "\\v'\\n[" EXT_DEPTH_REG "]u'\n", + ext); + } + if (bot) + printf(".as " DELIM_STRING " \\v'\\n[" BOT_HEIGHT_REG "]u'" + "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR + "\\v'\\n[" BOT_DEPTH_REG "]u'\n", + bot); + printf(".as " DELIM_STRING " " DELIMITER_CHAR "\n"); +} + +static void define_extensible_string(char *delim, int uid, + left_or_right_t left_or_right) +{ + printf(".ds " DELIM_STRING "\n"); + delimiter *d = delim_table; + int delim_len = strlen(delim); + int i; + for (i = 0; i < DELIM_TABLE_SIZE; i++, d++) + if (strncmp(delim, d->name, delim_len) == 0 + && (left_or_right & d->flags) != 0) + break; + if (i >= DELIM_TABLE_SIZE) { + error("there is no `%1' delimiter", delim); + printf(".nr " DELIM_WIDTH_REG " 0\n"); + return; + } + + printf(".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "\\f[%s]%s\\fP" DELIMITER_CHAR "\n" + ".ds " DELIM_STRING " \\Z" DELIMITER_CHAR + "\\v'\\n[rsb]u+\\n[rst]u/2u-%dM'\\f[%s]%s\\fP" DELIMITER_CHAR "\n" + ".nr " TOTAL_HEIGHT_REG " \\n[rst]-\\n[rsb]\n" + ".if \\n[" TOTAL_HEIGHT_REG "]<\\n[" DELTA_REG "] " + "\\{", + current_roman_font, d->small, axis_height, + current_roman_font, d->small); + + char buf[256]; + sprintf(buf, d->chain_format, "\\\\n[" INDEX_REG "]"); + printf(".nr " INDEX_REG " 0\n" + ".de " TEMP_MACRO "\n" + ".ie c%s \\{\\\n" + ".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n" + ".ds " DELIM_STRING " \\Z" DELIMITER_CHAR + "\\v'\\\\n[rsb]u+\\\\n[rst]u/2u-%dM'%s" DELIMITER_CHAR "\n" + ".nr " TOTAL_HEIGHT_REG " \\\\n[rst]-\\\\n[rsb]\n" + ".if \\\\n[" TOTAL_HEIGHT_REG "]<\\n[" DELTA_REG "] " + "\\{.nr " INDEX_REG " +1\n" + "." TEMP_MACRO "\n" + ".\\}\\}\n" + ".el .nr " INDEX_REG " 0-1\n" + "..\n" + "." TEMP_MACRO "\n", + buf, buf, axis_height, buf); + if (d->ext) { + printf(".if \\n[" INDEX_REG "]<0 \\{.if c%s \\{\\\n", d->ext); + build_extensible(d->ext, d->top, d->mid, d->bot); + printf(".\\}\\}\n"); + } + printf(".\\}\n"); + printf(".as " DELIM_STRING " \\h'\\n[" DELIM_WIDTH_REG "]u'\n"); + printf(".nr " WIDTH_FORMAT " +\\n[" DELIM_WIDTH_REG "]\n", uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]" + ">?(\\n[" TOTAL_HEIGHT_REG "]/2+%dM)\n", + uid, uid, axis_height); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]" + ">?(\\n[" TOTAL_HEIGHT_REG "]/2-%dM)\n", + uid, uid, axis_height); +} + +int delim_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + printf(".nr " DELTA_REG " \\n[" HEIGHT_FORMAT "]-%dM" + ">?(\\n[" DEPTH_FORMAT "]+%dM)\n", + p->uid, axis_height, p->uid, axis_height); + printf(".nr " DELTA_REG " 0\\n[" DELTA_REG "]*%d/500" + ">?(\\n[" DELTA_REG "]*2-%dM)\n", + delimiter_factor, delimiter_shortfall); + if (left) { + define_extensible_string(left, uid, LEFT_DELIM); + printf(".rn " DELIM_STRING " " LEFT_DELIM_STRING_FORMAT "\n", + uid); + if (r) + printf(".nr " MARK_REG " +\\n[" DELIM_WIDTH_REG "]\n"); + } + if (right) { + define_extensible_string(right, uid, RIGHT_DELIM); + printf(".rn " DELIM_STRING " " RIGHT_DELIM_STRING_FORMAT "\n", + uid); + } + return r; +} + +void delim_box::output() +{ + if (left) + printf("\\*[" LEFT_DELIM_STRING_FORMAT "]", uid); + p->output(); + if (right) + printf("\\*[" RIGHT_DELIM_STRING_FORMAT "]", uid); +} + +void delim_box::check_tabs(int level) +{ + p->check_tabs(level); +} + +void delim_box::debug_print() +{ + fprintf(stderr, "left \"%s\" { ", left ? left : ""); + p->debug_print(); + fprintf(stderr, " }"); + if (right) + fprintf(stderr, " right \"%s\"", right); +} + diff --git a/contrib/groff/src/preproc/eqn/eqn.cc b/contrib/groff/src/preproc/eqn/eqn.cc new file mode 100644 index 0000000..1fdda61 --- /dev/null +++ b/contrib/groff/src/preproc/eqn/eqn.cc @@ -0,0 +1,1277 @@ +#if defined(__STDC__) || defined(__cplusplus) +#define YYCONST const +#define YYPARAMS(x) x +#define YYDEFUN(name, arglist, args) name(args) +#define YYAND , +#define YYPTR void * +#else +#define YYCONST +#define YYPARAMS(x) () +#define YYDEFUN(name, arglist, args) name arglist args; +#define YYAND ; +#define YYPTR char * +#endif +#ifndef lint +YYCONST static char yysccsid[] = "@(#)yaccpar 1.8 (Berkeley +Cygnus.28) 01/20/91"; +#endif +#define YYBYACC 1 +#ifndef YYDONT_INCLUDE_STDIO +#include <stdio.h> +#endif +#ifdef __cplusplus +#include <stdlib.h> /* for malloc/realloc/free */ +#endif +#line 20 "eqn.y" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "lib.h" +#include "box.h" +extern int non_empty_flag; +char *strsave(const char *); +int yylex(); +void yyerror(const char *); +#line 32 "eqn.y" +typedef union { + char *str; + box *b; + pile_box *pb; + matrix_box *mb; + int n; + column *col; +} YYSTYPE; +#line 45 "y.tab.c" +#define OVER 257 +#define SMALLOVER 258 +#define SQRT 259 +#define SUB 260 +#define SUP 261 +#define LPILE 262 +#define RPILE 263 +#define CPILE 264 +#define PILE 265 +#define LEFT 266 +#define RIGHT 267 +#define TO 268 +#define FROM 269 +#define SIZE 270 +#define FONT 271 +#define ROMAN 272 +#define BOLD 273 +#define ITALIC 274 +#define FAT 275 +#define ACCENT 276 +#define BAR 277 +#define UNDER 278 +#define ABOVE 279 +#define TEXT 280 +#define QUOTED_TEXT 281 +#define FWD 282 +#define BACK 283 +#define DOWN 284 +#define UP 285 +#define MATRIX 286 +#define COL 287 +#define LCOL 288 +#define RCOL 289 +#define CCOL 290 +#define MARK 291 +#define LINEUP 292 +#define TYPE 293 +#define VCENTER 294 +#define PRIME 295 +#define SPLIT 296 +#define NOSPLIT 297 +#define UACCENT 298 +#define SPECIAL 299 +#define SPACE 300 +#define GFONT 301 +#define GSIZE 302 +#define DEFINE 303 +#define NDEFINE 304 +#define TDEFINE 305 +#define SDEFINE 306 +#define UNDEF 307 +#define IFDEF 308 +#define INCLUDE 309 +#define DELIM 310 +#define CHARTYPE 311 +#define SET 312 +#define GRFONT 313 +#define GBFONT 314 +#define YYERRCODE 256 +static YYCONST short yylhs[] = { -1, + 0, 0, 6, 6, 1, 1, 1, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 4, 4, 7, 7, + 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 8, 11, 11, 12, 12, 13, + 13, 16, 16, 15, 15, 14, 14, 14, 14, 9, + 9, 10, 10, 10, +}; +static YYCONST short yylen[] = { 2, + 0, 1, 1, 2, 1, 2, 2, 1, 3, 3, + 5, 5, 1, 2, 3, 3, 1, 3, 1, 3, + 5, 1, 1, 2, 2, 1, 1, 1, 3, 2, + 2, 2, 2, 4, 5, 3, 2, 2, 2, 3, + 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, + 3, 3, 2, 3, 1, 1, 3, 3, 4, 1, + 2, 1, 3, 3, 4, 2, 2, 2, 2, 1, + 1, 1, 1, 1, +}; +static YYCONST short yydefred[] = { 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 22, 23, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 26, 27, 28, 0, + 0, 3, 5, 0, 13, 0, 0, 17, 14, 70, + 71, 0, 0, 55, 31, 32, 33, 30, 73, 74, + 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 7, 0, 0, 24, 25, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 37, 38, + 39, 0, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, + 0, 29, 15, 16, 9, 0, 0, 20, 18, 40, + 41, 0, 58, 0, 0, 0, 0, 66, 67, 68, + 69, 34, 61, 0, 0, 0, 0, 59, 35, 0, + 0, 0, 11, 12, 21, 0, 64, 0, 0, 65, +}; +static YYCONST short yydgoto[] = { 31, + 32, 33, 34, 35, 36, 84, 38, 43, 44, 52, + 85, 45, 98, 99, 118, 131, +}; +static YYCONST short yysindex[] = { 1488, + 1527, -120, -120, -120, -120, -123, -249, -249, 1566, 1566, + 1566, 1566, 0, 0, -249, -249, -249, -249, -115, 1488, + 1488, -249, 1566, -256, -251, -249, 0, 0, 0, 1488, + 0, 0, 0, -221, 0, -233, 1488, 0, 0, 0, + 0, 1488, -85, 0, 0, 0, 0, 0, 0, 0, + 0, 1488, 1566, 1566, -195, -195, -195, -195, 1566, 1566, + 1566, 1566, -272, 0, 0, 1566, -195, 0, 0, 1566, + 1402, 1527, 1527, 1527, 1527, 1566, 1566, 1566, 0, 0, + 0, 1566, 0, 1488, -113, 1488, 1444, -195, -195, -195, + -195, -195, -195, -117, -117, -117, -117, -118, 0, -195, + -195, 0, 0, 0, 0, -167, -189, 0, 0, 0, + 0, 1488, 0, -106, -123, 1488, -83, 0, 0, 0, + 0, 0, 0, 1527, 1527, 1566, 1488, 0, 0, 1488, + -105, 1488, 0, 0, 0, 1488, 0, -104, 1488, 0, +}; +static YYCONST short yyrindex[] = { 41, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 1220, 46, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 85, 128, 363, 406, 0, 0, + 0, 0, 0, 0, 0, 0, 449, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -103, 0, 0, 185, 492, 727, 770, + 813, 856, 1091, 0, 0, 0, 0, 0, 0, 1134, + 1177, 0, 0, 0, 0, 42, 1220, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -102, 0, 0, -101, + 0, 0, 0, 0, 0, 0, 0, 0, -99, 0, +}; +static YYCONST short yygindex[] = { 0, + -7, -69, 3, -66, 458, 9, -26, 52, 27, -63, + -32, 54, 0, -35, 2, -59, +}; +#define YYTABLESIZE 1865 +static YYCONST short yytable[] = { 49, + 8, 50, 42, 39, 105, 116, 122, 63, 37, 8, + 109, 113, 64, 65, 94, 95, 96, 97, 128, 137, + 140, 56, 57, 62, 68, 63, 76, 77, 69, 83, + 40, 41, 51, 53, 54, 72, 73, 86, 71, 132, + 1, 10, 78, 79, 80, 2, 74, 75, 66, 108, + 10, 129, 70, 114, 133, 134, 46, 47, 48, 135, + 87, 81, 123, 83, 82, 0, 59, 60, 61, 62, + 76, 126, 138, 0, 103, 104, 83, 106, 0, 83, + 78, 79, 80, 0, 42, 0, 78, 79, 80, 72, + 73, 0, 0, 42, 8, 0, 119, 120, 121, 81, + 124, 125, 82, 0, 0, 81, 0, 0, 82, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, + 127, 0, 83, 8, 130, 8, 8, 43, 0, 0, + 0, 83, 0, 0, 0, 10, 43, 0, 0, 0, + 130, 51, 0, 0, 139, 117, 117, 117, 117, 0, + 0, 0, 0, 0, 0, 0, 40, 41, 0, 40, + 41, 0, 40, 41, 10, 112, 10, 10, 94, 95, + 96, 97, 112, 136, 136, 56, 57, 62, 42, 63, + 0, 0, 0, 0, 36, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 42, 0, 42, + 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 43, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 43, 0, 43, 43, 0, 0, 0, 0, 0, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, + 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 36, + 0, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, + 0, 0, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 0, 0, 44, 42, 42, 42, 42, 42, 42, 42, + 42, 44, 0, 0, 0, 42, 42, 42, 42, 0, + 42, 42, 0, 42, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 0, 0, 45, 43, 43, 43, 43, + 43, 43, 43, 43, 45, 0, 0, 0, 43, 43, + 43, 43, 0, 43, 43, 0, 43, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 36, 36, 0, 36, 36, 0, 0, 53, 0, + 0, 0, 36, 36, 0, 0, 44, 53, 0, 0, + 36, 36, 36, 36, 0, 0, 55, 56, 57, 58, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 67, 0, 36, 0, 0, 44, 0, 44, 44, 0, + 0, 47, 0, 0, 0, 0, 0, 0, 0, 45, + 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 88, 89, 0, 0, 0, 0, 90, 91, 92, 93, + 0, 0, 0, 100, 0, 0, 0, 101, 45, 0, + 45, 45, 0, 107, 0, 110, 0, 0, 0, 111, + 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 53, 0, 53, 53, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 47, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 47, 0, 47, 47, 0, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, + 0, 44, 44, 44, 44, 44, 44, 44, 44, 0, + 0, 0, 0, 44, 44, 44, 44, 0, 44, 44, + 0, 44, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 0, 0, 0, 45, 45, 45, 45, 45, 45, + 45, 45, 0, 0, 0, 0, 45, 45, 45, 45, + 0, 45, 45, 0, 45, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 0, 0, 46, 53, 53, 53, + 53, 53, 53, 53, 53, 46, 0, 0, 0, 53, + 53, 53, 53, 0, 53, 53, 0, 53, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 0, 0, 48, + 47, 47, 47, 47, 47, 47, 47, 47, 48, 0, + 0, 0, 47, 47, 47, 47, 0, 47, 47, 0, + 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, + 46, 49, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, + 0, 46, 46, 0, 0, 51, 0, 0, 0, 0, + 0, 0, 0, 48, 51, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 48, 0, 48, 48, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 49, 0, 49, 49, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, + 51, 51, 0, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 0, 0, 0, 46, 46, 46, 46, 46, + 46, 46, 46, 0, 0, 0, 0, 46, 46, 46, + 46, 0, 46, 46, 0, 46, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 0, 0, 0, 48, 48, + 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, + 48, 48, 48, 48, 0, 48, 48, 0, 48, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 0, 0, + 50, 49, 49, 49, 49, 49, 49, 49, 49, 50, + 0, 0, 0, 49, 49, 49, 49, 0, 49, 49, + 0, 49, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 0, 0, 52, 51, 51, 51, 51, 51, 51, + 51, 51, 52, 0, 0, 0, 51, 51, 51, 51, + 0, 51, 51, 0, 51, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, + 0, 0, 0, 0, 50, 54, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 50, 0, 50, 50, 0, 0, 19, + 0, 0, 0, 0, 0, 0, 0, 52, 19, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 52, 0, 52, 52, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 0, 54, 54, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 19, 0, 19, 19, 0, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 0, 0, 0, 50, + 50, 50, 50, 50, 50, 50, 50, 0, 0, 0, + 0, 50, 50, 50, 50, 0, 50, 50, 0, 50, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 0, + 29, 0, 52, 52, 52, 52, 52, 52, 52, 52, + 0, 0, 0, 0, 52, 52, 52, 52, 0, 52, + 52, 0, 52, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 29, 0, 0, 54, 54, 54, 54, 54, + 54, 54, 54, 0, 0, 0, 0, 54, 54, 54, + 54, 0, 54, 54, 0, 54, 19, 19, 19, 0, + 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 27, 29, 0, 19, 19, + 19, 19, 19, 19, 19, 19, 0, 0, 0, 0, + 19, 19, 19, 19, 0, 19, 19, 0, 19, 0, + 0, 0, 0, 0, 30, 0, 102, 28, 0, 0, + 0, 0, 0, 0, 0, 29, 0, 27, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 30, 0, 0, 28, + 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, + 0, 27, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 30, 0, 0, 28, 0, 0, 0, 0, 0, 0, + 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, + 0, 0, 28, 0, 0, 0, 0, 0, 0, 27, + 1, 0, 0, 2, 3, 4, 5, 6, 0, 0, + 0, 7, 8, 9, 10, 11, 12, 0, 0, 0, + 0, 13, 14, 15, 16, 17, 18, 19, 30, 0, + 0, 28, 20, 21, 22, 23, 0, 24, 25, 0, + 26, 0, 1, 0, 0, 2, 3, 4, 5, 6, + 115, 0, 0, 7, 8, 9, 10, 11, 12, 0, + 0, 0, 0, 13, 14, 15, 16, 17, 18, 19, + 0, 0, 0, 0, 20, 21, 22, 23, 0, 24, + 25, 0, 26, 0, 0, 0, 1, 0, 0, 2, + 3, 4, 5, 6, 0, 0, 0, 7, 8, 9, + 10, 11, 12, 0, 0, 0, 0, 13, 14, 15, + 16, 17, 18, 19, 0, 0, 0, 0, 20, 21, + 22, 23, 0, 24, 25, 1, 26, 0, 2, 3, + 4, 5, 6, 0, 0, 0, 7, 8, 9, 10, + 11, 12, 0, 0, 0, 0, 13, 14, 15, 16, + 17, 18, 19, 0, 0, 0, 0, 0, 0, 22, + 23, 0, 24, 25, 0, 26, 0, 2, 3, 4, + 5, 6, 0, 0, 0, 7, 8, 9, 10, 11, + 12, 0, 0, 0, 0, 13, 14, 15, 16, 17, + 18, 19, 0, 0, 0, 0, 0, 0, 22, 23, + 0, 24, 25, 0, 26, +}; +static YYCONST short yycheck[] = { 123, + 0, 125, 123, 1, 74, 123, 125, 123, 0, 9, + 77, 125, 20, 21, 287, 288, 289, 290, 125, 125, + 125, 125, 125, 125, 281, 125, 260, 261, 280, 37, + 280, 281, 6, 7, 8, 257, 258, 123, 30, 123, + 0, 0, 276, 277, 278, 0, 268, 269, 22, 76, + 9, 115, 26, 86, 124, 125, 3, 4, 5, 126, + 52, 295, 98, 71, 298, -1, 15, 16, 17, 18, + 260, 261, 132, -1, 72, 73, 84, 75, -1, 87, + 276, 277, 278, -1, 0, -1, 276, 277, 278, 257, + 258, -1, -1, 9, 94, -1, 95, 96, 97, 295, + 268, 269, 298, -1, -1, 295, -1, -1, 298, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 127, + 112, -1, 130, 123, 116, 125, 126, 0, -1, -1, + -1, 139, -1, -1, -1, 94, 9, -1, -1, -1, + 132, 115, -1, -1, 136, 94, 95, 96, 97, -1, + -1, -1, -1, -1, -1, -1, 280, 281, -1, 280, + 281, -1, 280, 281, 123, 279, 125, 126, 287, 288, + 289, 290, 279, 279, 279, 279, 279, 279, 94, 279, + -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 123, -1, 125, + 126, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 94, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 123, -1, 125, 126, -1, -1, -1, -1, -1, 259, + 260, 261, 262, 263, 264, 265, 266, 267, -1, -1, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, -1, -1, -1, + -1, 291, 292, 293, 294, 295, 296, 297, 298, 299, + 259, 260, 261, 262, 263, 264, 265, 266, 267, 125, + -1, 270, 271, 272, 273, 274, 275, 276, 277, 278, + 279, 280, 281, 282, 283, 284, 285, 286, -1, -1, + -1, -1, 291, 292, 293, 294, 295, 296, 297, 298, + 299, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + -1, -1, 0, 279, 280, 281, 282, 283, 284, 285, + 286, 9, -1, -1, -1, 291, 292, 293, 294, -1, + 296, 297, -1, 299, 257, 258, 259, 260, 261, 262, + 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, + 273, 274, 275, -1, -1, 0, 279, 280, 281, 282, + 283, 284, 285, 286, 9, -1, -1, -1, 291, 292, + 293, 294, -1, 296, 297, -1, 299, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 257, 258, -1, 260, 261, -1, -1, 0, -1, + -1, -1, 268, 269, -1, -1, 94, 9, -1, -1, + 276, 277, 278, 279, -1, -1, 9, 10, 11, 12, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 295, + 23, -1, 298, -1, -1, 123, -1, 125, 126, -1, + -1, 0, -1, -1, -1, -1, -1, -1, -1, 94, + 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 53, 54, -1, -1, -1, -1, 59, 60, 61, 62, + -1, -1, -1, 66, -1, -1, -1, 70, 123, -1, + 125, 126, -1, 76, -1, 78, -1, -1, -1, 82, + -1, -1, 94, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 123, -1, 125, 126, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 94, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 123, -1, 125, 126, -1, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, -1, -1, + -1, 279, 280, 281, 282, 283, 284, 285, 286, -1, + -1, -1, -1, 291, 292, 293, 294, -1, 296, 297, + -1, 299, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, -1, -1, -1, 279, 280, 281, 282, 283, 284, + 285, 286, -1, -1, -1, -1, 291, 292, 293, 294, + -1, 296, 297, -1, 299, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, -1, -1, 0, 279, 280, 281, + 282, 283, 284, 285, 286, 9, -1, -1, -1, 291, + 292, 293, 294, -1, 296, 297, -1, 299, 257, 258, + 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, + 269, 270, 271, 272, 273, 274, 275, -1, -1, 0, + 279, 280, 281, 282, 283, 284, 285, 286, 9, -1, + -1, -1, 291, 292, 293, 294, -1, 296, 297, -1, + 299, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, + 94, 9, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 123, + -1, 125, 126, -1, -1, 0, -1, -1, -1, -1, + -1, -1, -1, 94, 9, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 123, -1, 125, 126, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 94, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 123, -1, 125, 126, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 94, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 123, -1, + 125, 126, -1, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, -1, -1, -1, 279, 280, 281, 282, 283, + 284, 285, 286, -1, -1, -1, -1, 291, 292, 293, + 294, -1, 296, 297, -1, 299, 257, 258, 259, 260, + 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, + 271, 272, 273, 274, 275, -1, -1, -1, 279, 280, + 281, 282, 283, 284, 285, 286, -1, -1, -1, -1, + 291, 292, 293, 294, -1, 296, 297, -1, 299, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, -1, -1, + 0, 279, 280, 281, 282, 283, 284, 285, 286, 9, + -1, -1, -1, 291, 292, 293, 294, -1, 296, 297, + -1, 299, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, -1, -1, 0, 279, 280, 281, 282, 283, 284, + 285, 286, 9, -1, -1, -1, 291, 292, 293, 294, + -1, 296, 297, -1, 299, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, + -1, -1, -1, -1, 94, 9, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 123, -1, 125, 126, -1, -1, 0, + -1, -1, -1, -1, -1, -1, -1, 94, 9, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 123, -1, 125, 126, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 94, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 123, + -1, 125, 126, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 94, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 123, -1, 125, 126, -1, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, -1, -1, -1, 279, + 280, 281, 282, 283, 284, 285, 286, -1, -1, -1, + -1, 291, 292, 293, 294, -1, 296, 297, -1, 299, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, -1, + 9, -1, 279, 280, 281, 282, 283, 284, 285, 286, + -1, -1, -1, -1, 291, 292, 293, 294, -1, 296, + 297, -1, 299, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 9, -1, -1, 279, 280, 281, 282, 283, + 284, 285, 286, -1, -1, -1, -1, 291, 292, 293, + 294, -1, 296, 297, -1, 299, 257, 258, 259, -1, + -1, 262, 263, 264, 265, 266, 267, 268, 269, 270, + 271, 272, 273, 274, 275, 94, 9, -1, 279, 280, + 281, 282, 283, 284, 285, 286, -1, -1, -1, -1, + 291, 292, 293, 294, -1, 296, 297, -1, 299, -1, + -1, -1, -1, -1, 123, -1, 125, 126, -1, -1, + -1, -1, -1, -1, -1, 9, -1, 94, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 123, -1, -1, 126, + -1, -1, -1, -1, 9, -1, -1, -1, -1, -1, + -1, 94, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 123, -1, -1, 126, -1, -1, -1, -1, -1, -1, + 94, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 123, + -1, -1, 126, -1, -1, -1, -1, -1, -1, 94, + 259, -1, -1, 262, 263, 264, 265, 266, -1, -1, + -1, 270, 271, 272, 273, 274, 275, -1, -1, -1, + -1, 280, 281, 282, 283, 284, 285, 286, 123, -1, + -1, 126, 291, 292, 293, 294, -1, 296, 297, -1, + 299, -1, 259, -1, -1, 262, 263, 264, 265, 266, + 267, -1, -1, 270, 271, 272, 273, 274, 275, -1, + -1, -1, -1, 280, 281, 282, 283, 284, 285, 286, + -1, -1, -1, -1, 291, 292, 293, 294, -1, 296, + 297, -1, 299, -1, -1, -1, 259, -1, -1, 262, + 263, 264, 265, 266, -1, -1, -1, 270, 271, 272, + 273, 274, 275, -1, -1, -1, -1, 280, 281, 282, + 283, 284, 285, 286, -1, -1, -1, -1, 291, 292, + 293, 294, -1, 296, 297, 259, 299, -1, 262, 263, + 264, 265, 266, -1, -1, -1, 270, 271, 272, 273, + 274, 275, -1, -1, -1, -1, 280, 281, 282, 283, + 284, 285, 286, -1, -1, -1, -1, -1, -1, 293, + 294, -1, 296, 297, -1, 299, -1, 262, 263, 264, + 265, 266, -1, -1, -1, 270, 271, 272, 273, 274, + 275, -1, -1, -1, -1, 280, 281, 282, 283, 284, + 285, 286, -1, -1, -1, -1, -1, -1, 293, 294, + -1, 296, 297, -1, 299, +}; +#define YYFINAL 31 +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif +#define YYMAXTOKEN 314 +#if YYDEBUG +static YYCONST char *YYCONST yyname[] = { +"end-of-file",0,0,0,0,0,0,0,0,"'\\t'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'^'",0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'{'",0,"'}'","'~'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"OVER", +"SMALLOVER","SQRT","SUB","SUP","LPILE","RPILE","CPILE","PILE","LEFT","RIGHT", +"TO","FROM","SIZE","FONT","ROMAN","BOLD","ITALIC","FAT","ACCENT","BAR","UNDER", +"ABOVE","TEXT","QUOTED_TEXT","FWD","BACK","DOWN","UP","MATRIX","COL","LCOL", +"RCOL","CCOL","MARK","LINEUP","TYPE","VCENTER","PRIME","SPLIT","NOSPLIT", +"UACCENT","SPECIAL","SPACE","GFONT","GSIZE","DEFINE","NDEFINE","TDEFINE", +"SDEFINE","UNDEF","IFDEF","INCLUDE","DELIM","CHARTYPE","SET","GRFONT","GBFONT", +}; +static YYCONST char *YYCONST yyrule[] = { +"$accept : top", +"top :", +"top : equation", +"equation : mark", +"equation : equation mark", +"mark : from_to", +"mark : MARK mark", +"mark : LINEUP mark", +"from_to : sqrt_over", +"from_to : sqrt_over TO from_to", +"from_to : sqrt_over FROM sqrt_over", +"from_to : sqrt_over FROM sqrt_over TO from_to", +"from_to : sqrt_over FROM sqrt_over FROM from_to", +"sqrt_over : script", +"sqrt_over : SQRT sqrt_over", +"sqrt_over : sqrt_over OVER sqrt_over", +"sqrt_over : sqrt_over SMALLOVER sqrt_over", +"script : nonsup", +"script : simple SUP script", +"nonsup : simple", +"nonsup : simple SUB nonsup", +"nonsup : simple SUB simple SUP script", +"simple : TEXT", +"simple : QUOTED_TEXT", +"simple : SPLIT QUOTED_TEXT", +"simple : NOSPLIT TEXT", +"simple : '^'", +"simple : '~'", +"simple : '\\t'", +"simple : '{' equation '}'", +"simple : PILE pile_arg", +"simple : LPILE pile_arg", +"simple : RPILE pile_arg", +"simple : CPILE pile_arg", +"simple : MATRIX '{' column_list '}'", +"simple : LEFT delim equation RIGHT delim", +"simple : LEFT delim equation", +"simple : simple BAR", +"simple : simple UNDER", +"simple : simple PRIME", +"simple : simple ACCENT simple", +"simple : simple UACCENT simple", +"simple : ROMAN simple", +"simple : BOLD simple", +"simple : ITALIC simple", +"simple : FAT simple", +"simple : FONT text simple", +"simple : SIZE text simple", +"simple : FWD number simple", +"simple : BACK number simple", +"simple : UP number simple", +"simple : DOWN number simple", +"simple : TYPE text simple", +"simple : VCENTER simple", +"simple : SPECIAL text simple", +"number : text", +"pile_element_list : equation", +"pile_element_list : pile_element_list ABOVE equation", +"pile_arg : '{' pile_element_list '}'", +"pile_arg : number '{' pile_element_list '}'", +"column_list : column", +"column_list : column_list column", +"column_element_list : equation", +"column_element_list : column_element_list ABOVE equation", +"column_arg : '{' column_element_list '}'", +"column_arg : number '{' column_element_list '}'", +"column : COL column_arg", +"column : LCOL column_arg", +"column : RCOL column_arg", +"column : CCOL column_arg", +"text : TEXT", +"text : QUOTED_TEXT", +"delim : text", +"delim : '{'", +"delim : '}'", +}; +#endif +#define YYLEX yylex() +#define YYEMPTY -1 +#define yyclearin (yychar=(YYEMPTY)) +#define yyerrok (yyerrflag=0) +#ifndef YYINITDEPTH +#define YYINITDEPTH 200 +#endif +#ifdef YYSTACKSIZE +#undef YYMAXDEPTH +#define YYMAXDEPTH YYSTACKSIZE +#else +#ifdef YYMAXDEPTH +#define YYSTACKSIZE YYMAXDEPTH +#else +#define YYSTACKSIZE 10000 +#define YYMAXDEPTH 10000 +#endif +#endif +int yydebug; +int yynerrs; +int yyerrflag; +int yychar; +YYSTYPE yyval; +YYSTYPE yylval; +static short *yyss; +static YYSTYPE *yyvs; +static int yystacksize; +static int yygrow (); +static YYPTR yymalloc YYPARAMS((unsigned)); +static YYPTR yyrealloc YYPARAMS((YYPTR, unsigned)); +#define yyfree(x) free(x) +#define YYABORT goto yyabort +#define YYACCEPT goto yyaccept +#define YYERROR goto yyerrlab + +#if YYDEBUG +#ifdef __cplusplus +extern "C" char *getenv(); +#else +extern char *getenv(); +#endif +#endif + +int +yyparse() +{ + register int yym, yyn, yystate; + register YYSTYPE *yyvsp; + register short *yyssp; + short *yysse; +#if YYDEBUG + register YYCONST char *yys; + + if (yys = getenv("YYDEBUG")) + { + yyn = *yys; + if (yyn >= '0' && yyn <= '9') + yydebug = yyn - '0'; + } +#endif + + yynerrs = 0; + yyerrflag = 0; + yychar = (-1); + + if (yyss == 0) + { + yyss = (short *) yymalloc (YYINITDEPTH * sizeof (short)); + if (yyss == 0) + goto yyabort; + yyvs = (YYSTYPE *) yymalloc (YYINITDEPTH * sizeof (YYSTYPE)); + if (yyvs == 0) + { + yyfree (yyss); + goto yyabort; + } + yystacksize = YYINITDEPTH; + } + yysse = yyss + yystacksize - 1; + yyssp = yyss; + yyvsp = yyvs; + *yyssp = yystate = 0; + +yyloop: + if (yyn = yydefred[yystate]) goto yyreduce; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("yydebug: state %d, reading %d (%s)\n", yystate, + yychar, yys); + } +#endif + } + if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { +#if YYDEBUG + if (yydebug) + printf("yydebug: state %d, shifting to state %d\n", + yystate, yytable[yyn]); +#endif + if (yyssp >= yysse) + { + /* FIXME: Rework so there's only one of these. */ + int depth = yyssp - yyss; + if (yygrow () != 0) + goto yyoverflow; + yysse = yyss + yystacksize - 1; + yyssp = yyss + depth; + yyvsp = yyvs + depth; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + yychar = (-1); + if (yyerrflag > 0) --yyerrflag; + goto yyloop; + } + if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { + yyn = yytable[yyn]; + goto yyreduce; + } + if (yyerrflag) goto yyinrecovery; +#ifdef lint + goto yynewerror; +#endif +yynewerror: + yyerror("syntax error"); +#ifdef lint + goto yyerrlab; +#endif +yyerrlab: + ++yynerrs; +yyinrecovery: + if (yyerrflag < 3) + { + yyerrflag = 3; + for (;;) + { + if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) + { +#if YYDEBUG + if (yydebug) + printf("yydebug: state %d, error recovery shifting\ + to state %d\n", *yyssp, yytable[yyn]); +#endif + if (yyssp >= yysse) + { + int depth = yyssp - yyss; + if (yygrow () != 0) + goto yyoverflow; + yysse = yyss + yystacksize - 1; + yyssp = yyss + depth; + yyvsp = yyvs + depth; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + goto yyloop; + } + else + { +#if YYDEBUG + if (yydebug) + printf("yydebug: error recovery discarding state %d\n", + *yyssp); +#endif + if (yyssp <= yyss) goto yyabort; + --yyssp; + --yyvsp; + } + } + } + else + { + if (yychar == 0) goto yyabort; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("yydebug: state %d, error recovery discards token %d (%s)\n", + yystate, yychar, yys); + } +#endif + yychar = (-1); + goto yyloop; + } +yyreduce: +#if YYDEBUG + if (yydebug) + printf("yydebug: state %d, reducing by rule %d (%s)\n", + yystate, yyn, yyrule[yyn]); +#endif + yym = yylen[yyn]; + yyval = yyvsp[1-yym]; + switch (yyn) + { +case 2: +#line 126 "eqn.y" +{ yyvsp[0].b->top_level(); non_empty_flag = 1; } +break; +case 3: +#line 131 "eqn.y" +{ yyval.b = yyvsp[0].b; } +break; +case 4: +#line 133 "eqn.y" +{ + list_box *lb = yyvsp[-1].b->to_list_box(); + if (!lb) + lb = new list_box(yyvsp[-1].b); + lb->append(yyvsp[0].b); + yyval.b = lb; + } +break; +case 5: +#line 144 "eqn.y" +{ yyval.b = yyvsp[0].b; } +break; +case 6: +#line 146 "eqn.y" +{ yyval.b = make_mark_box(yyvsp[0].b); } +break; +case 7: +#line 148 "eqn.y" +{ yyval.b = make_lineup_box(yyvsp[0].b); } +break; +case 8: +#line 153 "eqn.y" +{ yyval.b = yyvsp[0].b; } +break; +case 9: +#line 155 "eqn.y" +{ yyval.b = make_limit_box(yyvsp[-2].b, 0, yyvsp[0].b); } +break; +case 10: +#line 157 "eqn.y" +{ yyval.b = make_limit_box(yyvsp[-2].b, yyvsp[0].b, 0); } +break; +case 11: +#line 159 "eqn.y" +{ yyval.b = make_limit_box(yyvsp[-4].b, yyvsp[-2].b, yyvsp[0].b); } +break; +case 12: +#line 161 "eqn.y" +{ yyval.b = make_limit_box(yyvsp[-4].b, make_limit_box(yyvsp[-2].b, yyvsp[0].b, 0), 0); } +break; +case 13: +#line 166 "eqn.y" +{ yyval.b = yyvsp[0].b; } +break; +case 14: +#line 168 "eqn.y" +{ yyval.b = make_sqrt_box(yyvsp[0].b); } +break; +case 15: +#line 170 "eqn.y" +{ yyval.b = make_over_box(yyvsp[-2].b, yyvsp[0].b); } +break; +case 16: +#line 172 "eqn.y" +{ yyval.b = make_small_over_box(yyvsp[-2].b, yyvsp[0].b); } +break; +case 17: +#line 177 "eqn.y" +{ yyval.b = yyvsp[0].b; } +break; +case 18: +#line 179 "eqn.y" +{ yyval.b = make_script_box(yyvsp[-2].b, 0, yyvsp[0].b); } +break; +case 19: +#line 184 "eqn.y" +{ yyval.b = yyvsp[0].b; } +break; +case 20: +#line 186 "eqn.y" +{ yyval.b = make_script_box(yyvsp[-2].b, yyvsp[0].b, 0); } +break; +case 21: +#line 188 "eqn.y" +{ yyval.b = make_script_box(yyvsp[-4].b, yyvsp[-2].b, yyvsp[0].b); } +break; +case 22: +#line 193 "eqn.y" +{ yyval.b = split_text(yyvsp[0].str); } +break; +case 23: +#line 195 "eqn.y" +{ yyval.b = new quoted_text_box(yyvsp[0].str); } +break; +case 24: +#line 197 "eqn.y" +{ yyval.b = split_text(yyvsp[0].str); } +break; +case 25: +#line 199 "eqn.y" +{ yyval.b = new quoted_text_box(yyvsp[0].str); } +break; +case 26: +#line 201 "eqn.y" +{ yyval.b = new half_space_box; } +break; +case 27: +#line 203 "eqn.y" +{ yyval.b = new space_box; } +break; +case 28: +#line 205 "eqn.y" +{ yyval.b = new tab_box; } +break; +case 29: +#line 207 "eqn.y" +{ yyval.b = yyvsp[-1].b; } +break; +case 30: +#line 209 "eqn.y" +{ yyvsp[0].pb->set_alignment(CENTER_ALIGN); yyval.b = yyvsp[0].pb; } +break; +case 31: +#line 211 "eqn.y" +{ yyvsp[0].pb->set_alignment(LEFT_ALIGN); yyval.b = yyvsp[0].pb; } +break; +case 32: +#line 213 "eqn.y" +{ yyvsp[0].pb->set_alignment(RIGHT_ALIGN); yyval.b = yyvsp[0].pb; } +break; +case 33: +#line 215 "eqn.y" +{ yyvsp[0].pb->set_alignment(CENTER_ALIGN); yyval.b = yyvsp[0].pb; } +break; +case 34: +#line 217 "eqn.y" +{ yyval.b = yyvsp[-1].mb; } +break; +case 35: +#line 219 "eqn.y" +{ yyval.b = make_delim_box(yyvsp[-3].str, yyvsp[-2].b, yyvsp[0].str); } +break; +case 36: +#line 221 "eqn.y" +{ yyval.b = make_delim_box(yyvsp[-1].str, yyvsp[0].b, 0); } +break; +case 37: +#line 223 "eqn.y" +{ yyval.b = make_overline_box(yyvsp[-1].b); } +break; +case 38: +#line 225 "eqn.y" +{ yyval.b = make_underline_box(yyvsp[-1].b); } +break; +case 39: +#line 227 "eqn.y" +{ yyval.b = make_prime_box(yyvsp[-1].b); } +break; +case 40: +#line 229 "eqn.y" +{ yyval.b = make_accent_box(yyvsp[-2].b, yyvsp[0].b); } +break; +case 41: +#line 231 "eqn.y" +{ yyval.b = make_uaccent_box(yyvsp[-2].b, yyvsp[0].b); } +break; +case 42: +#line 233 "eqn.y" +{ yyval.b = new font_box(strsave(get_grfont()), yyvsp[0].b); } +break; +case 43: +#line 235 "eqn.y" +{ yyval.b = new font_box(strsave(get_gbfont()), yyvsp[0].b); } +break; +case 44: +#line 237 "eqn.y" +{ yyval.b = new font_box(strsave(get_gfont()), yyvsp[0].b); } +break; +case 45: +#line 239 "eqn.y" +{ yyval.b = new fat_box(yyvsp[0].b); } +break; +case 46: +#line 241 "eqn.y" +{ yyval.b = new font_box(yyvsp[-1].str, yyvsp[0].b); } +break; +case 47: +#line 243 "eqn.y" +{ yyval.b = new size_box(yyvsp[-1].str, yyvsp[0].b); } +break; +case 48: +#line 245 "eqn.y" +{ yyval.b = new hmotion_box(yyvsp[-1].n, yyvsp[0].b); } +break; +case 49: +#line 247 "eqn.y" +{ yyval.b = new hmotion_box(-yyvsp[-1].n, yyvsp[0].b); } +break; +case 50: +#line 249 "eqn.y" +{ yyval.b = new vmotion_box(yyvsp[-1].n, yyvsp[0].b); } +break; +case 51: +#line 251 "eqn.y" +{ yyval.b = new vmotion_box(-yyvsp[-1].n, yyvsp[0].b); } +break; +case 52: +#line 253 "eqn.y" +{ yyvsp[0].b->set_spacing_type(yyvsp[-1].str); yyval.b = yyvsp[0].b; } +break; +case 53: +#line 255 "eqn.y" +{ yyval.b = new vcenter_box(yyvsp[0].b); } +break; +case 54: +#line 257 "eqn.y" +{ yyval.b = make_special_box(yyvsp[-1].str, yyvsp[0].b); } +break; +case 55: +#line 262 "eqn.y" +{ + int n; + if (sscanf(yyvsp[0].str, "%d", &n) == 1) + yyval.n = n; + a_delete yyvsp[0].str; + } +break; +case 56: +#line 272 "eqn.y" +{ yyval.pb = new pile_box(yyvsp[0].b); } +break; +case 57: +#line 274 "eqn.y" +{ yyvsp[-2].pb->append(yyvsp[0].b); yyval.pb = yyvsp[-2].pb; } +break; +case 58: +#line 279 "eqn.y" +{ yyval.pb = yyvsp[-1].pb; } +break; +case 59: +#line 281 "eqn.y" +{ yyvsp[-1].pb->set_space(yyvsp[-3].n); yyval.pb = yyvsp[-1].pb; } +break; +case 60: +#line 286 "eqn.y" +{ yyval.mb = new matrix_box(yyvsp[0].col); } +break; +case 61: +#line 288 "eqn.y" +{ yyvsp[-1].mb->append(yyvsp[0].col); yyval.mb = yyvsp[-1].mb; } +break; +case 62: +#line 293 "eqn.y" +{ yyval.col = new column(yyvsp[0].b); } +break; +case 63: +#line 295 "eqn.y" +{ yyvsp[-2].col->append(yyvsp[0].b); yyval.col = yyvsp[-2].col; } +break; +case 64: +#line 300 "eqn.y" +{ yyval.col = yyvsp[-1].col; } +break; +case 65: +#line 302 "eqn.y" +{ yyvsp[-1].col->set_space(yyvsp[-3].n); yyval.col = yyvsp[-1].col; } +break; +case 66: +#line 307 "eqn.y" +{ yyvsp[0].col->set_alignment(CENTER_ALIGN); yyval.col = yyvsp[0].col; } +break; +case 67: +#line 309 "eqn.y" +{ yyvsp[0].col->set_alignment(LEFT_ALIGN); yyval.col = yyvsp[0].col; } +break; +case 68: +#line 311 "eqn.y" +{ yyvsp[0].col->set_alignment(RIGHT_ALIGN); yyval.col = yyvsp[0].col; } +break; +case 69: +#line 313 "eqn.y" +{ yyvsp[0].col->set_alignment(CENTER_ALIGN); yyval.col = yyvsp[0].col; } +break; +case 70: +#line 317 "eqn.y" +{ yyval.str = yyvsp[0].str; } +break; +case 71: +#line 319 "eqn.y" +{ yyval.str = yyvsp[0].str; } +break; +case 72: +#line 324 "eqn.y" +{ yyval.str = yyvsp[0].str; } +break; +case 73: +#line 326 "eqn.y" +{ yyval.str = strsave("{"); } +break; +case 74: +#line 328 "eqn.y" +{ yyval.str = strsave("}"); } +break; +#line 1168 "y.tab.c" + } + yyssp -= yym; + yystate = *yyssp; + yyvsp -= yym; + yym = yylhs[yyn]; + if (yystate == 0 && yym == 0) + { +#if YYDEBUG + if (yydebug) + printf("yydebug: after reduction, shifting from state 0 to\ + state %d\n", YYFINAL); +#endif + yystate = YYFINAL; + *++yyssp = YYFINAL; + *++yyvsp = yyval; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("yydebug: state %d, reading %d (%s)\n", + YYFINAL, yychar, yys); + } +#endif + } + if (yychar == 0) goto yyaccept; + goto yyloop; + } + if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yystate) + yystate = yytable[yyn]; + else + yystate = yydgoto[yym]; +#if YYDEBUG + if (yydebug) + printf("yydebug: after reduction, shifting from state %d \ +to state %d\n", *yyssp, yystate); +#endif + if (yyssp >= yysse) + { + int depth = yyssp - yyss; + if (yygrow () != 0) + goto yyoverflow; + yysse = yyss + yystacksize - 1; + yyssp = yyss + depth; + yyvsp = yyvs + depth; + } + *++yyssp = yystate; + *++yyvsp = yyval; + goto yyloop; +yyoverflow: + yyerror("yacc stack overflow"); +yyabort: + return (1); +yyaccept: + return (0); +} + +static int +yygrow () +{ + int old_stacksize = yystacksize; + short *new_yyss; + YYSTYPE *new_yyvs; + + if (yystacksize >= YYMAXDEPTH) + return (1); + yystacksize *= 2; + if (yystacksize > YYMAXDEPTH) + yystacksize = YYMAXDEPTH; +#if YYDEBUG + if (yydebug) + printf("yydebug: growing stack size from %d to %d\n", + old_stacksize, yystacksize); +#endif + new_yyss = (short *) yyrealloc (yyss, yystacksize * sizeof (short)); + if (new_yyss == 0) + return (1); + new_yyvs = (YYSTYPE *) yyrealloc (yyvs, yystacksize * sizeof (YYSTYPE)); + if (new_yyvs == 0) + { + yyfree (new_yyss); + return (1); + } + yyss = new_yyss; + yyvs = new_yyvs; + return (0); +} + +static YYPTR +YYDEFUN (yymalloc, (bytes), unsigned bytes) +{ + YYPTR ptr = (YYPTR) malloc (bytes); + if (ptr != 0) return (ptr); + yyerror ("yyparse: memory exhausted"); + return (0); +} + +static YYPTR +YYDEFUN (yyrealloc, (old, bytes), YYPTR old YYAND unsigned bytes) +{ + YYPTR ptr = (YYPTR) realloc (old, bytes); + if (ptr != 0) return (ptr); + yyerror ("yyparse: memory exhausted"); + return (0); +} diff --git a/contrib/groff/src/preproc/eqn/eqn.h b/contrib/groff/src/preproc/eqn/eqn.h new file mode 100644 index 0000000..70b1927 --- /dev/null +++ b/contrib/groff/src/preproc/eqn/eqn.h @@ -0,0 +1,51 @@ +// -*- 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 <stdio.h> +#include <string.h> +#include <assert.h> +#include <stdlib.h> +#include <errno.h> +#include "cset.h" +#include "errarg.h" +#include "error.h" +#include "lib.h" + +#include "box.h" + +extern char start_delim; +extern char end_delim; +extern int non_empty_flag; +extern int inline_flag; +extern int draw_flag; +extern int one_size_reduction_flag; +extern int compatible_flag; +extern int nroff; + +void init_lex(const char *str, const char *filename, int lineno); +void lex_error(const char *message, + const errarg &arg1 = empty_errarg, + const errarg &arg2 = empty_errarg, + const errarg &arg3 = empty_errarg); + +void init_table(const char *device); + +// prefix for all registers, strings, macros +#define PREFIX "0" diff --git a/contrib/groff/src/preproc/eqn/eqn.man b/contrib/groff/src/preproc/eqn/eqn.man new file mode 100644 index 0000000..381d97d --- /dev/null +++ b/contrib/groff/src/preproc/eqn/eqn.man @@ -0,0 +1,882 @@ +.ig \"-*- nroff -*- +Copyright (C) 1989-2000 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the Free Software Foundation instead of in +the original English. +.. +.ie \n(.V<\n(.v .ds tx T\h'-.1667m'\v'.224m'E\v'-.224m'\h'-.125m'X +.el .ds tx TeX +.\" Like TP, but if specified indent is more than half +.\" the current line-length - indent, use the default indent. +.de Tp +.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP +.el .TP "\\$1" +.. +.\" The BSD man macros can't handle " in arguments to font change macros, +.\" so use \(ts instead of ". +.tr \(ts" +.TH @G@EQN @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" +.SH NAME +@g@eqn \- format equations for troff +.SH SYNOPSIS +.nr a \n(.j +.ad l +.nr i \n(.i +.in +\w'\fB@g@eqn 'u +.ti \niu +.B @g@eqn +.de OP +.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]" +.el .RB "[\ " "\\$1" "\ ]" +.. +.OP \-rvCNR +.OP \-d cc +.OP \-T name +.OP \-M dir +.OP \-f F +.OP \-s n +.OP \-p n +.OP \-m n +.RI "[\ " files\|.\|.\|. "\ ]" +.br +.ad \na +.PP +It is possible to have whitespace between a command line option and its +parameter. +.SH DESCRIPTION +This manual page describes the GNU version of +.BR eqn , +which is part of the groff document formatting system. +.B eqn +compiles descriptions of equations embedded within +.B troff +input files into commands that are understood by +.BR troff . +Normally, it should be invoked using the +.B \-e +option of +.BR groff . +The syntax is quite compatible with Unix eqn. +The output of GNU eqn cannot be processed with Unix troff; +it must be processed with GNU troff. +If no files are given on the command line, the standard input +will be read. +A filename of +.B \- +will cause the standard input to be read. +.LP +.B eqn +searches for the file +.B eqnrc +in the directories given with the +.B \-M +option first, then in +.BR @SYSTEMMACRODIR@ , +.BR @LOCALMACRODIR@ , +and finally in the standard macro directory +.BR @MACRODIR@ . +If it exists, eqn will process it before the other input files. +The +.B \-R +option prevents this. +.LP +GNU eqn does not provide the functionality of neqn: +it does not support low-resolution, typewriter-like devices +(although it may work adequately for very simple input). +.SH OPTIONS +.TP +.B \-C +Recognize +.B .EQ +and +.B .EN +even when followed by a character other than space or newline. +.TP +.B \-N +Don't allow newlines within delimiters. +This option allows +.B eqn +to recover better from missing closing delimiters. +.TP +.B \-v +Print the version number. +.TP +.B \-r +Only one size reduction. +.TP +.BI \-m n +The minimum point-size is +.IR n . +eqn will not reduce the size of subscripts or superscripts to +a smaller size than +.IR n . +.TP +.BI \-T name +The output is for device +.IR name . +The only effect of this is to define a macro +.I name +with a value of +.BR 1 . +Typically +.B eqnrc +will use this to provide definitions appropriate for the output device. +The default output device is +.BR @DEVICE@ . +.TP +.BI \-M dir +Search +.I dir +for +.B eqnrc +before the default directories. +.TP +.B \-R +Don't load +.BR eqnrc . +.TP +.BI \-f F +This is equivalent to a +.BI gfont\ F +command. +.TP +.BI \-s n +This is equivalent to a +.BI gsize\ n +command. +This option is deprecated. +eqn will normally set equations at whatever the current point size +is when the equation is encountered. +.TP +.BI \-p n +This says that subscripts and superscripts should be +.I n +points smaller than the surrounding text. +This option is deprecated. +Normally eqn makes sets subscripts and superscripts at 70% +of the size of the surrounding text. +.SH USAGE +Only the differences between GNU eqn and Unix eqn are described here. +.LP +Most of the new features of GNU eqn +are based on \*(tx. +There are some references to the differences between \*(tx and GNU eqn below; +these may safely be ignored if you do not know \*(tx. +.SS Automatic spacing +.LP +.B eqn +gives each component of an equation a type, and adjusts the spacing +between components using that type. +Possible types are: +.TP \w'punctuation'u+2n +ordinary +an ordinary character such as 1 or +.IR x ; +.TP +operator +a large operator such as +.ds Su \s+5\(*S\s0 +.if \n(.g .if !c\(*S .ds Su the summation operator +\*(Su; +.TP +binary +a binary operator such as +; +.TP +relation +a relation such as =; +.TP +opening +a opening bracket such as (; +.TP +closing +a closing bracket such as ); +.TP +punctuation +a punctuation character such as ,; +.TP +inner +a subformula contained within brackets; +.TP +suppress +spacing that suppresses automatic spacing adjustment. +.LP +Components of an equation get a type in one of two ways. +.TP +.BI type\ t\ e +This yields an equation component that contains +.I e +but that has type +.IR t , +where +.I t +is one of the types mentioned above. +For example, +.B times +is defined as +.RS +.IP +.B +type "binary" \e(mu +.RE +.IP +The name of the type doesn't have to be quoted, but quoting protects +from macro expansion. +.TP +.BI chartype\ t\ text +Unquoted groups of characters are split up into individual characters, +and the type of each character is looked up; +this changes the type that is stored for each character; +it says that the characters in +.I text +from now on have type +.IR t . +For example, +.RS +.IP +.B +chartype "punctuation" .,;: +.RE +.IP +would make the characters +.B .,;: +have type punctuation +whenever they subsequently appeared in an equation. +The type +.I t +can also be +.B letter +or +.BR digit ; +in these cases +.B chartype +changes the font type of the characters. +See the Fonts subsection. +.SS New primitives +.TP +.IB e1\ smallover\ e2 +This is similar to +.BR over ; +.B smallover +reduces the size of +.I e1 +and +.IR e2 ; +it also puts less vertical space between +.I e1 +or +.I e2 +and the fraction bar. +The +.B over +primitive corresponds to the \*(tx +.B \eover +primitive in display styles; +.B smallover +corresponds to +.B \eover +in non-display styles. +.TP +.BI vcenter\ e +This vertically centers +.I e +about the math axis. +The math axis is the vertical position about which characters +such as + and - are centered; also it is the vertical position +used for the bar of fractions. +For example, +.B sum +is defined as +.RS +.IP +.B +{ type "operator" vcenter size +5 \e(*S } +.RE +.TP +.IB e1\ accent\ e2 +This sets +.I e2 +as an accent over +.IR e1 . +.I e2 +is assumed to be at the correct height for a lowercase letter; +.I e2 +will be moved down according if +.I e1 +is taller or shorter than a lowercase letter. +For example, +.B hat +is defined as +.RS +.IP +.B +accent { "^" } +.RE +.IP +.BR dotdot , +.BR dot , +.BR tilde , +.B vec +and +.B dyad +are also defined using the +.B accent +primitive. +.TP +.IB e1\ uaccent\ e2 +This sets +.I e2 +as an accent under +.IR e1 . +.I e2 +is assumed to be at the correct height for a character without a descender; +.I e2 +will be moved down if +.I e1 +has a descender. +.B utilde +is pre-defined using +.B uaccent +as a tilde accent below the baseline. +.TP +.BI split\ \(ts text \(ts +This has the same effect as simply +.RS +.IP +.I text +.RE +.IP +but +.I text +is not subject to macro expansion because it is quoted; +.I text +will be split up and the spacing between individual characters +will be adjusted. +.TP +.BI nosplit\ text +This has the same effect as +.RS +.IP +.BI \(ts text \(ts +.RE +.IP +but because +.I text +is not quoted it will be subject to macro expansion; +.I text +will not be split up +and the spacing between individual characters will not be adjusted. +.TP +.IB e\ opprime +This is a variant of +.B prime +that acts as an operator on +.IR e . +It produces a different result from +.B prime +in a case such as +.BR A\ opprime\ sub\ 1 : +with +.B opprime +the +.B 1 +will be tucked under the prime as a subscript to the +.B A +(as is conventional in mathematical typesetting), +whereas with +.B prime +the +.B 1 +will be a subscript to the prime character. +The precedence of +.B opprime +is the same as that of +.B bar +and +.BR under , +which is higher than that of everything except +.B accent +and +.BR uaccent . +In unquoted text a +.B ' +that is not the first character will be treated like +.BR opprime . +.TP +.BI special\ text\ e +This constructs a new object from +.I e +using a +.BR @g@troff (@MAN1EXT@) +macro named +.IR text . +When the macro is called, +the string +.B 0s +will contain the output for +.IR e , +and the number registers +.BR 0w , +.BR 0h , +.BR 0d , +.BR 0skern +and +.BR 0skew +will contain the width, height, depth, subscript kern, and skew of +.IR e . +(The +.I "subscript kern" +of an object says how much a subscript on that object should be tucked in; +the +.I skew +of an object says how far to the right of the center of the object an +accent over the object should be placed.) +The macro must modify +.B 0s +so that it will output the desired result with its origin at the current +point, and increase the current horizontal position by the width +of the object. +The number registers must also be modified so that they correspond to the +result. +.RS +.LP +For example, suppose you wanted a construct that `cancels' an expression +by drawing a diagonal line through it. +.IP +.nf +.ft B +.ne 6+\n(.Vu +\&.EQ +define cancel 'special Ca' +\&.EN +\&.de Ca +\&.ds 0s \eZ'\e\e*(0s'\ev'\e\en(0du'\eD'l \e\en(0wu -\e\en(0hu-\e\en(0du'\ev'\e\en(0hu' +\&.. +.ft +.fi +.LP +Then you could cancel an expression +.I e +with +.BI cancel\ {\ e\ } +.LP +Here's a more complicated construct that draws a box round an expression: +.IP +.nf +.ft B +.ne 11+\n(.Vu +\&.EQ +define box 'special Bx' +\&.EN +\&.de Bx +\&.ds 0s \eZ'\eh'1n'\e\e*(0s'\e +\eZ'\ev'\e\en(0du+1n'\eD'l \e\en(0wu+2n 0'\eD'l 0 -\e\en(0hu-\e\en(0du-2n'\e +\eD'l -\e\en(0wu-2n 0'\eD'l 0 \e\en(0hu+\e\en(0du+2n''\eh'\e\en(0wu+2n' +\&.nr 0w +2n +\&.nr 0d +1n +\&.nr 0h +1n +\&.. +.ft +.fi +.RE +.SS Customization +The appearance of equations is controlled by +a large number of parameters. These can be set using +the +.B set +command. +.TP +.BI set\ p\ n +This sets parameter +.I p +to value +.I n ; +.I n +is an integer. +For example, +.RS +.IP +.B +set x_height 45 +.RE +.IP +says that +.B eqn +should assume an x height of 0.45 ems. +.RS +.LP +Possible parameters are as follows. +Values are in units of hundredths of an em unless otherwise stated. +These descriptions are intended to be expository rather than +definitive. +.TP \w'\fBdefault_rule_thickness'u+2n +.B minimum_size +.B eqn +will not set anything at a smaller point-size than this. +The value is in points. +.TP +.B fat_offset +The +.B fat +primitive emboldens an equation +by overprinting two copies of the equation +horizontally offset by this amount. +.TP +.B over_hang +A fraction bar will be longer by twice this amount than +the maximum of the widths of the numerator and denominator; +in other words, it will overhang the numerator and +denominator by at least this amount. +.TP +.B accent_width +When +.B bar +or +.B under +is applied to a single character, +the line will be this long. +Normally, +.B bar +or +.B under +produces a line whose length is the width of the object to which it applies; +in the case of a single character, +this tends to produce a line that looks too long. +.TP +.B delimiter_factor +Extensible delimiters produced with the +.B left +and +.B right +primitives will have a combined height and depth of at least this many +thousandths of twice the maximum amount by which the sub-equation that +the delimiters enclose extends away from the axis. +.TP +.B delimiter_shortfall +Extensible delimiters produced with the +.B left +and +.B right +primitives will have a combined height and depth +not less than the difference of +twice the maximum amount by which the sub-equation that +the delimiters enclose extends away from the axis +and this amount. +.TP +.B null_delimiter_space +This much horizontal space is inserted +on each side of a fraction. +.TP +.B script_space +The width of subscripts and superscripts is increased by this amount. +.TP +.B thin_space +This amount of space is automatically inserted after punctuation +characters. +.TP +.B medium_space +This amount of space is automatically inserted on either side +of binary operators. +.TP +.B thick_space +This amount of space is automatically inserted on either side of +relations. +.TP +.B x_height +The height of lowercase letters without ascenders such as x. +.TP +.B axis_height +The height above the baseline of the center of characters +such as \(pl and \(mi. +It is important that this value is correct for the font +you are using. +.TP +.B default_rule_thickness +This should set to the thickness of the +.B \e(ru +character, or the thickness of horizontal lines produced with the +.B \eD +escape sequence. +.TP +.B num1 +The +.B over +command will shift up the numerator by at least this amount. +.TP +.B num2 +The +.B smallover +command will shift up the numerator by at least this amount. +.TP +.B denom1 +The +.B over +command will shift down the denominator by at least this amount. +.TP +.B denom2 +The +.B smallover +command will shift down the denominator by at least this amount. +.TP +.B sup1 +Normally superscripts will be shifted up by at least this amount. +.TP +.B sup2 +Superscripts within superscripts or upper limits +or numerators of +.B smallover +fractions +will be shifted up by at least this amount. +This is usually less than sup1. +.TP +.B sup3 +Superscripts within denominators or square roots +or subscripts or lower limits will be shifted up by at least +this amount. +This is usually less than sup2. +.TP +.B sub1 +Subscripts will normally be shifted down by at least this amount. +.TP +.B sub2 +When there is both a subscript and a superscript, the subscript +will be shifted down by at least this amount. +.TP +.B sup_drop +The baseline of a superscript will be no more +than this much amount below the top of the object on +which the superscript is set. +.TP +.B sub_drop +The baseline of a subscript will be at least this much below +the bottom of the object on which the subscript is set. +.TP +.B big_op_spacing1 +The baseline of an upper limit will be at least this +much above the top of the object on which the limit is set. +.TP +.B big_op_spacing2 +The baseline of a lower limit will be at least this +much below the bottom of the object on which the limit is set. +.TP +.B big_op_spacing3 +The bottom of an upper limit will be at least this much above the +top of the object on which the limit is set. +.TP +.B big_op_spacing4 +The top of a lower limit will be at least this much below +the bottom of the object on which the limit is set. +.TP +.B big_op_spacing5 +This much vertical space will be added above and below limits. +.TP +.B baseline_sep +The baselines of the rows in a pile or matrix will normally be +this far apart. +In most cases this should be equal to the sum of +.B num1 +and +.BR denom1 . +.TP +.B shift_down +The midpoint between the top baseline and the bottom baseline +in a matrix or pile will be shifted down by this much from the axis. +In most cases this should be equal to +.BR axis_height . +.TP +.B column_sep +This much space will be added between columns in a matrix. +.TP +.B matrix_side_sep +This much space will be added at each side of a matrix. +.TP +.B draw_lines +If this is non-zero, lines will be drawn using the +.B \eD +escape sequence, rather than with the +.B \el +escape sequence and the +.B \e(ru +character. +.TP +.B body_height +The amount by which the height of the equation exceeds this +will be added as extra space before the line containing the equation +(using +.BR \ex .) +The default value is 85. +.TP +.B body_depth +The amount by which the depth of the equation exceeds this +will be added as extra space after the line containing the equation +(using +.BR \ex .) +The default value is 35. +.TP +.B nroff +If this is non-zero, +then +.B ndefine +will behave like +.B define +and +.B tdefine +will be ignored, +otherwise +.B tdefine +will behave like +.B define +and +.B ndefine +will be ignored. +The default value is 0 +(This is typically changed to 1 by the +.B eqnrc +file for the +.BR ascii , +.BR latin1 , +.BR utf8 , +and +.B cp1047 +devices.) +.LP +A more precise description of the role of many of these +parameters can be found in Appendix H of +.IR The\ \*(txbook . +.RE +.SS Macros +Macros can take arguments. +In a macro body, +.BI $ n +where +.I n +is between 1 and 9, +will be replaced by the +.IR n-th +argument if the macro is called with arguments; +if there are fewer than +.I n +arguments, it will be replaced by nothing. +A word containing a left parenthesis where the part of the word +before the left parenthesis has been defined using the +.B define +command +will be recognized as a macro call with arguments; +characters following the left parenthesis +up to a matching right parenthesis will be treated as comma-separated +arguments; +commas inside nested parentheses do not terminate an argument. +.TP +.BI sdefine\ name\ X\ anything\ X +This is like the +.B define +command, but +.I name +will not be recognized if called with arguments. +.TP +.BI include\ \(ts file \(ts +Include the contents of +.IR file . +Lines of +.I file +beginning with +.B .EQ +or +.B .EN +will be ignored. +.TP +.BI ifdef\ name\ X\ anything\ X +If +.I name +has been defined by +.B define +(or has been automatically defined because +.I name +is the output device) +process +.IR anything ; +otherwise ignore +.IR anything . +.I X +can be any character not appearing in +.IR anything . +.SS Fonts +.B eqn +normally uses at least two fonts to set an equation: +an italic font for letters, +and a roman font for everything else. +The existing +.B gfont +command +changes the font that is used as the italic font. +By default this is +.BR I . +The font that is used as the roman font can be changed +using the new +.B grfont +command. +.TP +.BI grfont\ f +Set the roman font to +.IR f . +.LP +The +.B italic +primitive uses the current italic font set by +.BR gfont ; +the +.B roman +primitive uses the current roman font set by +.BR grfont . +There is also a new +.B gbfont +command, which changes the font used by the +.B bold +primitive. +If you only use the +.BR roman , +.B italic +and +.B bold +primitives to changes fonts within an equation, +you can change all the fonts used by your equations +just by using +.BR gfont , +.B grfont +and +.B gbfont +commands. +.LP +You can control which characters are treated as letters +(and therefore set in italics) by using the +.B chartype +command described above. +A type of +.B letter +will cause a character to be set in italic type. +A type of +.B digit +will cause a character to be set in roman type. +.SH FILES +.Tp \w'\fB@MACRODIR@/eqnrc'u+2n +.B @MACRODIR@/eqnrc +Initialization file. +.SH BUGS +Inline equations will be set at the point size that is current at the +beginning of the input line. +.SH "SEE ALSO" +.BR groff (@MAN1EXT@), +.BR @g@troff (@MAN1EXT@), +.BR groff_font (@MAN5EXT@), +.I The\ \*(txbook diff --git a/contrib/groff/src/preproc/eqn/eqn.y b/contrib/groff/src/preproc/eqn/eqn.y new file mode 100644 index 0000000..833a0f0 --- /dev/null +++ b/contrib/groff/src/preproc/eqn/eqn.y @@ -0,0 +1,331 @@ +/* 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 <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "lib.h" +#include "box.h" +extern int non_empty_flag; +char *strsave(const char *); +int yylex(); +void yyerror(const char *); +%} + +%union { + char *str; + box *b; + pile_box *pb; + matrix_box *mb; + int n; + column *col; +} + +%token OVER +%token SMALLOVER +%token SQRT +%token SUB +%token SUP +%token LPILE +%token RPILE +%token CPILE +%token PILE +%token LEFT +%token RIGHT +%token TO +%token FROM +%token SIZE +%token FONT +%token ROMAN +%token BOLD +%token ITALIC +%token FAT +%token ACCENT +%token BAR +%token UNDER +%token ABOVE +%token <str> TEXT +%token <str> QUOTED_TEXT +%token FWD +%token BACK +%token DOWN +%token UP +%token MATRIX +%token COL +%token LCOL +%token RCOL +%token CCOL +%token MARK +%token LINEUP +%token TYPE +%token VCENTER +%token PRIME +%token SPLIT +%token NOSPLIT +%token UACCENT +%token SPECIAL + +/* these are handled in the lexer */ +%token SPACE +%token GFONT +%token GSIZE +%token DEFINE +%token NDEFINE +%token TDEFINE +%token SDEFINE +%token UNDEF +%token IFDEF +%token INCLUDE +%token DELIM +%token CHARTYPE +%token SET +%token GRFONT +%token GBFONT + +/* The original eqn manual says that `left' is right associative. It's lying. +Consider `left ( ~ left ( ~ right ) right )'. */ + +%right LEFT +%left RIGHT +%right LPILE RPILE CPILE PILE TEXT QUOTED_TEXT MATRIX MARK LINEUP '^' '~' '\t' '{' SPLIT NOSPLIT +%right FROM TO +%left SQRT OVER SMALLOVER +%right SUB SUP +%right ROMAN BOLD ITALIC FAT FONT SIZE FWD BACK DOWN UP TYPE VCENTER SPECIAL +%right BAR UNDER PRIME +%left ACCENT UACCENT + +%type <b> mark from_to sqrt_over script simple equation nonsup +%type <n> number +%type <str> text delim +%type <pb> pile_element_list pile_arg +%type <mb> column_list +%type <col> column column_arg column_element_list + +%% +top: + /* empty */ + | equation + { $1->top_level(); non_empty_flag = 1; } + ; + +equation: + mark + { $$ = $1; } + | equation mark + { + list_box *lb = $1->to_list_box(); + if (!lb) + lb = new list_box($1); + lb->append($2); + $$ = lb; + } + ; + +mark: + from_to + { $$ = $1; } + | MARK mark + { $$ = make_mark_box($2); } + | LINEUP mark + { $$ = make_lineup_box($2); } + ; + +from_to: + sqrt_over %prec FROM + { $$ = $1; } + | sqrt_over TO from_to + { $$ = make_limit_box($1, 0, $3); } + | sqrt_over FROM sqrt_over + { $$ = make_limit_box($1, $3, 0); } + | sqrt_over FROM sqrt_over TO from_to + { $$ = make_limit_box($1, $3, $5); } + | sqrt_over FROM sqrt_over FROM from_to + { $$ = make_limit_box($1, make_limit_box($3, $5, 0), 0); } + ; + +sqrt_over: + script + { $$ = $1; } + | SQRT sqrt_over + { $$ = make_sqrt_box($2); } + | sqrt_over OVER sqrt_over + { $$ = make_over_box($1, $3); } + | sqrt_over SMALLOVER sqrt_over + { $$ = make_small_over_box($1, $3); } + ; + +script: + nonsup + { $$ = $1; } + | simple SUP script + { $$ = make_script_box($1, 0, $3); } + ; + +nonsup: + simple %prec SUP + { $$ = $1; } + | simple SUB nonsup + { $$ = make_script_box($1, $3, 0); } + | simple SUB simple SUP script + { $$ = make_script_box($1, $3, $5); } + ; + +simple: + TEXT + { $$ = split_text($1); } + | QUOTED_TEXT + { $$ = new quoted_text_box($1); } + | SPLIT QUOTED_TEXT + { $$ = split_text($2); } + | NOSPLIT TEXT + { $$ = new quoted_text_box($2); } + | '^' + { $$ = new half_space_box; } + | '~' + { $$ = new space_box; } + | '\t' + { $$ = new tab_box; } + | '{' equation '}' + { $$ = $2; } + | PILE pile_arg + { $2->set_alignment(CENTER_ALIGN); $$ = $2; } + | LPILE pile_arg + { $2->set_alignment(LEFT_ALIGN); $$ = $2; } + | RPILE pile_arg + { $2->set_alignment(RIGHT_ALIGN); $$ = $2; } + | CPILE pile_arg + { $2->set_alignment(CENTER_ALIGN); $$ = $2; } + | MATRIX '{' column_list '}' + { $$ = $3; } + | LEFT delim equation RIGHT delim + { $$ = make_delim_box($2, $3, $5); } + | LEFT delim equation + { $$ = make_delim_box($2, $3, 0); } + | simple BAR + { $$ = make_overline_box($1); } + | simple UNDER + { $$ = make_underline_box($1); } + | simple PRIME + { $$ = make_prime_box($1); } + | simple ACCENT simple + { $$ = make_accent_box($1, $3); } + | simple UACCENT simple + { $$ = make_uaccent_box($1, $3); } + | ROMAN simple + { $$ = new font_box(strsave(get_grfont()), $2); } + | BOLD simple + { $$ = new font_box(strsave(get_gbfont()), $2); } + | ITALIC simple + { $$ = new font_box(strsave(get_gfont()), $2); } + | FAT simple + { $$ = new fat_box($2); } + | FONT text simple + { $$ = new font_box($2, $3); } + | SIZE text simple + { $$ = new size_box($2, $3); } + | FWD number simple + { $$ = new hmotion_box($2, $3); } + | BACK number simple + { $$ = new hmotion_box(-$2, $3); } + | UP number simple + { $$ = new vmotion_box($2, $3); } + | DOWN number simple + { $$ = new vmotion_box(-$2, $3); } + | TYPE text simple + { $3->set_spacing_type($2); $$ = $3; } + | VCENTER simple + { $$ = new vcenter_box($2); } + | SPECIAL text simple + { $$ = make_special_box($2, $3); } + ; + +number: + text + { + int n; + if (sscanf($1, "%d", &n) == 1) + $$ = n; + a_delete $1; + } + ; + +pile_element_list: + equation + { $$ = new pile_box($1); } + | pile_element_list ABOVE equation + { $1->append($3); $$ = $1; } + ; + +pile_arg: + '{' pile_element_list '}' + { $$ = $2; } + | number '{' pile_element_list '}' + { $3->set_space($1); $$ = $3; } + ; + +column_list: + column + { $$ = new matrix_box($1); } + | column_list column + { $1->append($2); $$ = $1; } + ; + +column_element_list: + equation + { $$ = new column($1); } + | column_element_list ABOVE equation + { $1->append($3); $$ = $1; } + ; + +column_arg: + '{' column_element_list '}' + { $$ = $2; } + | number '{' column_element_list '}' + { $3->set_space($1); $$ = $3; } + ; + +column: + COL column_arg + { $2->set_alignment(CENTER_ALIGN); $$ = $2; } + | LCOL column_arg + { $2->set_alignment(LEFT_ALIGN); $$ = $2; } + | RCOL column_arg + { $2->set_alignment(RIGHT_ALIGN); $$ = $2; } + | CCOL column_arg + { $2->set_alignment(CENTER_ALIGN); $$ = $2; } + ; + +text: TEXT + { $$ = $1; } + | QUOTED_TEXT + { $$ = $1; } + ; + +delim: + text + { $$ = $1; } + | '{' + { $$ = strsave("{"); } + | '}' + { $$ = strsave("}"); } + ; + +%% diff --git a/contrib/groff/src/preproc/eqn/eqn_tab.h b/contrib/groff/src/preproc/eqn/eqn_tab.h new file mode 100644 index 0000000..9a8b3cb --- /dev/null +++ b/contrib/groff/src/preproc/eqn/eqn_tab.h @@ -0,0 +1,67 @@ +#define OVER 257 +#define SMALLOVER 258 +#define SQRT 259 +#define SUB 260 +#define SUP 261 +#define LPILE 262 +#define RPILE 263 +#define CPILE 264 +#define PILE 265 +#define LEFT 266 +#define RIGHT 267 +#define TO 268 +#define FROM 269 +#define SIZE 270 +#define FONT 271 +#define ROMAN 272 +#define BOLD 273 +#define ITALIC 274 +#define FAT 275 +#define ACCENT 276 +#define BAR 277 +#define UNDER 278 +#define ABOVE 279 +#define TEXT 280 +#define QUOTED_TEXT 281 +#define FWD 282 +#define BACK 283 +#define DOWN 284 +#define UP 285 +#define MATRIX 286 +#define COL 287 +#define LCOL 288 +#define RCOL 289 +#define CCOL 290 +#define MARK 291 +#define LINEUP 292 +#define TYPE 293 +#define VCENTER 294 +#define PRIME 295 +#define SPLIT 296 +#define NOSPLIT 297 +#define UACCENT 298 +#define SPECIAL 299 +#define SPACE 300 +#define GFONT 301 +#define GSIZE 302 +#define DEFINE 303 +#define NDEFINE 304 +#define TDEFINE 305 +#define SDEFINE 306 +#define UNDEF 307 +#define IFDEF 308 +#define INCLUDE 309 +#define DELIM 310 +#define CHARTYPE 311 +#define SET 312 +#define GRFONT 313 +#define GBFONT 314 +typedef union { + char *str; + box *b; + pile_box *pb; + matrix_box *mb; + int n; + column *col; +} YYSTYPE; +extern YYSTYPE yylval; diff --git a/contrib/groff/src/preproc/eqn/lex.cc b/contrib/groff/src/preproc/eqn/lex.cc new file mode 100644 index 0000000..25faec2 --- /dev/null +++ b/contrib/groff/src/preproc/eqn/lex.cc @@ -0,0 +1,1165 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2000 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 "eqn.h" +#include "eqn_tab.h" +#include "stringclass.h" +#include "ptable.h" + +struct definition { + char is_macro; + char is_simple; + union { + int tok; + char *contents; + }; + definition(); + ~definition(); +}; + +definition::definition() : is_macro(1), is_simple(0) +{ + contents = 0; +} + +definition::~definition() +{ + if (is_macro) + a_delete contents; +} + +declare_ptable(definition) +implement_ptable(definition) + +PTABLE(definition) macro_table; + +static struct { + const char *name; + int token; +} token_table[] = { + { "over", OVER }, + { "smallover", SMALLOVER }, + { "sqrt", SQRT }, + { "sub", SUB }, + { "sup", SUP }, + { "lpile", LPILE }, + { "rpile", RPILE }, + { "cpile", CPILE }, + { "pile", PILE }, + { "left", LEFT }, + { "right", RIGHT }, + { "to", TO }, + { "from", FROM }, + { "size", SIZE }, + { "font", FONT }, + { "roman", ROMAN }, + { "bold", BOLD }, + { "italic", ITALIC }, + { "fat", FAT }, + { "bar", BAR }, + { "under", UNDER }, + { "accent", ACCENT }, + { "uaccent", UACCENT }, + { "above", ABOVE }, + { "fwd", FWD }, + { "back", BACK }, + { "down", DOWN }, + { "up", UP }, + { "matrix", MATRIX }, + { "col", COL }, + { "lcol", LCOL }, + { "rcol", RCOL }, + { "ccol", CCOL }, + { "mark", MARK }, + { "lineup", LINEUP }, + { "space", SPACE }, + { "gfont", GFONT }, + { "gsize", GSIZE }, + { "define", DEFINE }, + { "sdefine", SDEFINE }, + { "ndefine", NDEFINE }, + { "tdefine", TDEFINE }, + { "undef", UNDEF }, + { "ifdef", IFDEF }, + { "include", INCLUDE }, + { "copy", INCLUDE }, + { "delim", DELIM }, + { "chartype", CHARTYPE }, + { "type", TYPE }, + { "vcenter", VCENTER }, + { "set", SET }, + { "opprime", PRIME }, + { "grfont", GRFONT }, + { "gbfont", GBFONT }, + { "split", SPLIT }, + { "nosplit", NOSPLIT }, + { "special", SPECIAL }, +}; + +static struct { + const char *name; + const char *def; +} def_table[] = { + { "ALPHA", "\\(*A" }, + { "BETA", "\\(*B" }, + { "CHI", "\\(*X" }, + { "DELTA", "\\(*D" }, + { "EPSILON", "\\(*E" }, + { "ETA", "\\(*Y" }, + { "GAMMA", "\\(*G" }, + { "IOTA", "\\(*I" }, + { "KAPPA", "\\(*K" }, + { "LAMBDA", "\\(*L" }, + { "MU", "\\(*M" }, + { "NU", "\\(*N" }, + { "OMEGA", "\\(*W" }, + { "OMICRON", "\\(*O" }, + { "PHI", "\\(*F" }, + { "PI", "\\(*P" }, + { "PSI", "\\(*Q" }, + { "RHO", "\\(*R" }, + { "SIGMA", "\\(*S" }, + { "TAU", "\\(*T" }, + { "THETA", "\\(*H" }, + { "UPSILON", "\\(*U" }, + { "XI", "\\(*C" }, + { "ZETA", "\\(*Z" }, + { "Alpha", "\\(*A" }, + { "Beta", "\\(*B" }, + { "Chi", "\\(*X" }, + { "Delta", "\\(*D" }, + { "Epsilon", "\\(*E" }, + { "Eta", "\\(*Y" }, + { "Gamma", "\\(*G" }, + { "Iota", "\\(*I" }, + { "Kappa", "\\(*K" }, + { "Lambda", "\\(*L" }, + { "Mu", "\\(*M" }, + { "Nu", "\\(*N" }, + { "Omega", "\\(*W" }, + { "Omicron", "\\(*O" }, + { "Phi", "\\(*F" }, + { "Pi", "\\(*P" }, + { "Psi", "\\(*Q" }, + { "Rho", "\\(*R" }, + { "Sigma", "\\(*S" }, + { "Tau", "\\(*T" }, + { "Theta", "\\(*H" }, + { "Upsilon", "\\(*U" }, + { "Xi", "\\(*C" }, + { "Zeta", "\\(*Z" }, + { "alpha", "\\(*a" }, + { "beta", "\\(*b" }, + { "chi", "\\(*x" }, + { "delta", "\\(*d" }, + { "epsilon", "\\(*e" }, + { "eta", "\\(*y" }, + { "gamma", "\\(*g" }, + { "iota", "\\(*i" }, + { "kappa", "\\(*k" }, + { "lambda", "\\(*l" }, + { "mu", "\\(*m" }, + { "nu", "\\(*n" }, + { "omega", "\\(*w" }, + { "omicron", "\\(*o" }, + { "phi", "\\(*f" }, + { "pi", "\\(*p" }, + { "psi", "\\(*q" }, + { "rho", "\\(*r" }, + { "sigma", "\\(*s" }, + { "tau", "\\(*t" }, + { "theta", "\\(*h" }, + { "upsilon", "\\(*u" }, + { "xi", "\\(*c" }, + { "zeta", "\\(*z" }, + { "max", "{type \"operator\" roman \"max\"}" }, + { "min", "{type \"operator\" roman \"min\"}" }, + { "lim", "{type \"operator\" roman \"lim\"}" }, + { "sin", "{type \"operator\" roman \"sin\"}" }, + { "cos", "{type \"operator\" roman \"cos\"}" }, + { "tan", "{type \"operator\" roman \"tan\"}" }, + { "sinh", "{type \"operator\" roman \"sinh\"}" }, + { "cosh", "{type \"operator\" roman \"cosh\"}" }, + { "tanh", "{type \"operator\" roman \"tanh\"}" }, + { "arc", "{type \"operator\" roman \"arc\"}" }, + { "log", "{type \"operator\" roman \"log\"}" }, + { "ln", "{type \"operator\" roman \"ln\"}" }, + { "exp", "{type \"operator\" roman \"exp\"}" }, + { "Re", "{type \"operator\" roman \"Re\"}" }, + { "Im", "{type \"operator\" roman \"Im\"}" }, + { "det", "{type \"operator\" roman \"det\"}" }, + { "and", "{roman \"and\"}" }, + { "if", "{roman \"if\"}" }, + { "for", "{roman \"for\"}" }, + { "sum", "{type \"operator\" vcenter size +5 \\(*S}" }, + { "prod", "{type \"operator\" vcenter size +5 \\(*P}" }, + { "int", "{type \"operator\" vcenter size +8 \\(is}" }, + { "union", "{type \"operator\" vcenter size +5 \\(cu}" }, + { "inter", "{type \"operator\" vcenter size +5 \\(ca}" }, + { "times", "type \"binary\" \\(mu" }, + { "ldots", "type \"inner\" { . . . }" }, + { "inf", "\\(if" }, + { "partial", "\\(pd" }, + { "nothing", "\"\"" }, + { "half", "{1 smallover 2}" }, + { "hat_def", "roman \"^\"" }, + { "hat", "accent { hat_def }" }, + { "dot_def", "back 15 \"\\v'-52M'.\\v'52M'\"" }, + { "dot", "accent { dot_def }" }, + { "dotdot_def", "back 25 \"\\v'-52M'..\\v'52M'\"" }, + { "dotdot", "accent { dotdot_def }" }, + { "tilde_def", "\"~\"" }, + { "tilde", "accent { tilde_def }" }, + { "utilde_def", "\"\\v'75M'~\\v'-75M'\"" }, + { "utilde", "uaccent { utilde_def }" }, + { "vec_def", "up 52 size -5 \\(->" }, + { "vec", "accent { vec_def }" }, + { "dyad_def", "up 52 size -5 {\\(<- back 60 \\(->}" }, + { "dyad", "accent { dyad_def }" }, + { "==", "type \"relation\" \\(==" }, + { "!=", "type \"relation\" \\(!=" }, + { "+-", "type \"binary\" \\(+-" }, + { "->", "type \"relation\" \\(->" }, + { "<-", "type \"relation\" \\(<-" }, + { "<<", "{ < back 20 < }" }, + { ">>", "{ > back 20 > }" }, + { "...", "type \"inner\" vcenter { . . . }" }, + { "prime", "'" }, + { "approx", "type \"relation\" \"\\(~=\"" }, + { "grad", "\\(gr" }, + { "del", "\\(gr" }, + { "cdot", "type \"binary\" vcenter ." }, + { "dollar", "$" }, +}; + +void init_table(const char *device) +{ + int i; + for (i = 0; i < sizeof(token_table)/sizeof(token_table[0]); i++) { + definition *def = new definition; + def->is_macro = 0; + def->tok = token_table[i].token; + macro_table.define(token_table[i].name, def); + } + for (i = 0; i < sizeof(def_table)/sizeof(def_table[0]); i++) { + definition *def = new definition; + def->is_macro = 1; + def->contents = strsave(def_table[i].def); + def->is_simple = 1; + macro_table.define(def_table[i].name, def); + } + definition *def = new definition; + def->is_macro = 1; + def->contents = strsave("1"); + macro_table.define(device, def); +} + +class input { + input *next; +public: + input(input *p); + virtual ~input(); + virtual int get() = 0; + virtual int peek() = 0; + virtual int get_location(char **, int *); + + friend int get_char(); + friend int peek_char(); + friend int get_location(char **, int *); + friend void init_lex(const char *str, const char *filename, int lineno); +}; + +class file_input : public input { + FILE *fp; + char *filename; + int lineno; + string line; + const char *ptr; + int read_line(); +public: + file_input(FILE *, const char *, input *); + ~file_input(); + int get(); + int peek(); + int get_location(char **, int *); +}; + + +class macro_input : public input { + char *s; + char *p; +public: + macro_input(const char *, input *); + ~macro_input(); + int get(); + int peek(); +}; + +class top_input : public macro_input { + char *filename; + int lineno; + public: + top_input(const char *, const char *, int, input *); + ~top_input(); + int get(); + int get_location(char **, int *); +}; + +class argument_macro_input: public input { + char *s; + char *p; + char *ap; + int argc; + char *argv[9]; +public: + argument_macro_input(const char *, int, char **, input *); + ~argument_macro_input(); + int get(); + int peek(); +}; + +input::input(input *x) : next(x) +{ +} + +input::~input() +{ +} + +int input::get_location(char **, int *) +{ + return 0; +} + +file_input::file_input(FILE *f, const char *fn, input *p) +: input(p), lineno(0), ptr("") +{ + fp = f; + filename = strsave(fn); +} + +file_input::~file_input() +{ + a_delete filename; + fclose(fp); +} + +int file_input::read_line() +{ + for (;;) { + line.clear(); + lineno++; + for (;;) { + int c = getc(fp); + if (c == EOF) + break; + else if (illegal_input_char(c)) + lex_error("illegal input character code %1", c); + else { + line += char(c); + if (c == '\n') + break; + } + } + if (line.length() == 0) + return 0; + if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'E' + && (line[2] == 'Q' || line[2] == 'N') + && (line.length() == 3 || line[3] == ' ' || line[3] == '\n' + || compatible_flag))) { + line += '\0'; + ptr = line.contents(); + return 1; + } + } +} + +int file_input::get() +{ + if (*ptr != '\0' || read_line()) + return *ptr++ & 0377; + else + return EOF; +} + +int file_input::peek() +{ + if (*ptr != '\0' || read_line()) + return *ptr; + else + return EOF; +} + +int file_input::get_location(char **fnp, int *lnp) +{ + *fnp = filename; + *lnp = lineno; + return 1; +} + +macro_input::macro_input(const char *str, input *x) : input(x) +{ + p = s = strsave(str); +} + +macro_input::~macro_input() +{ + a_delete s; +} + +int macro_input::get() +{ + if (p == 0 || *p == '\0') + return EOF; + else + return *p++ & 0377; +} + +int macro_input::peek() +{ + if (p == 0 || *p == '\0') + return EOF; + else + return *p & 0377; +} + +top_input::top_input(const char *str, const char *fn, int ln, input *x) +: macro_input(str, x), lineno(ln) +{ + filename = strsave(fn); +} + +top_input::~top_input() +{ + a_delete filename; +} + +int top_input::get() +{ + int c = macro_input::get(); + if (c == '\n') + lineno++; + return c; +} + +int top_input::get_location(char **fnp, int *lnp) +{ + *fnp = filename; + *lnp = lineno; + return 1; +} + +// Character representing $1. Must be illegal input character. +#define ARG1 14 + +argument_macro_input::argument_macro_input(const char *body, int ac, + char **av, input *x) +: input(x), ap(0), argc(ac) +{ + int i; + for (i = 0; i < argc; i++) + argv[i] = av[i]; + p = s = strsave(body); + int j = 0; + for (i = 0; s[i] != '\0'; i++) + if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') { + if (s[i+1] != '0') + s[j++] = ARG1 + s[++i] - '1'; + } + else + s[j++] = s[i]; + s[j] = '\0'; +} + + +argument_macro_input::~argument_macro_input() +{ + for (int i = 0; i < argc; i++) + a_delete argv[i]; + a_delete s; +} + +int argument_macro_input::get() +{ + if (ap) { + if (*ap != '\0') + return *ap++ & 0377; + ap = 0; + } + if (p == 0) + return EOF; + while (*p >= ARG1 && *p <= ARG1 + 8) { + int i = *p++ - ARG1; + if (i < argc && argv[i] != 0 && argv[i][0] != '\0') { + ap = argv[i]; + return *ap++ & 0377; + } + } + if (*p == '\0') + return EOF; + return *p++ & 0377; +} + +int argument_macro_input::peek() +{ + if (ap) { + if (*ap != '\0') + return *ap & 0377; + ap = 0; + } + if (p == 0) + return EOF; + while (*p >= ARG1 && *p <= ARG1 + 8) { + int i = *p++ - ARG1; + if (i < argc && argv[i] != 0 && argv[i][0] != '\0') { + ap = argv[i]; + return *ap & 0377; + } + } + if (*p == '\0') + return EOF; + return *p & 0377; +} + +static input *current_input = 0; + +/* we insert a newline between input from different levels */ + +int get_char() +{ + if (current_input == 0) + return EOF; + else { + int c = current_input->get(); + if (c != EOF) + return c; + else { + input *tem = current_input; + current_input = current_input->next; + delete tem; + return '\n'; + } + } +} + +int peek_char() +{ + if (current_input == 0) + return EOF; + else { + int c = current_input->peek(); + if (c != EOF) + return c; + else + return '\n'; + } +} + +int get_location(char **fnp, int *lnp) +{ + for (input *p = current_input; p; p = p->next) + if (p->get_location(fnp, lnp)) + return 1; + return 0; +} + +string token_buffer; +const int NCONTEXT = 4; +string context_ring[NCONTEXT]; +int context_index = 0; + +void flush_context() +{ + for (int i = 0; i < NCONTEXT; i++) + context_ring[i] = ""; + context_index = 0; +} + +void show_context() +{ + int i = context_index; + fputs(" context is\n\t", stderr); + for (;;) { + int j = (i + 1) % NCONTEXT; + if (j == context_index) { + fputs(">>> ", stderr); + put_string(context_ring[i], stderr); + fputs(" <<<", stderr); + break; + } + else if (context_ring[i].length() > 0) { + put_string(context_ring[i], stderr); + putc(' ', stderr); + } + i = j; + } + putc('\n', stderr); +} + +void add_context(const string &s) +{ + context_ring[context_index] = s; + context_index = (context_index + 1) % NCONTEXT; +} + +void add_context(char c) +{ + context_ring[context_index] = c; + context_index = (context_index + 1) % NCONTEXT; +} + +void add_quoted_context(const string &s) +{ + string &r = context_ring[context_index]; + r = '"'; + for (int i = 0; i < s.length(); i++) + if (s[i] == '"') + r += "\\\""; + else + r += s[i]; + r += '"'; + context_index = (context_index + 1) % NCONTEXT; +} + +void init_lex(const char *str, const char *filename, int lineno) +{ + while (current_input != 0) { + input *tem = current_input; + current_input = current_input->next; + delete tem; + } + current_input = new top_input(str, filename, lineno, 0); + flush_context(); +} + + +void get_delimited_text() +{ + char *filename; + int lineno; + int got_location = get_location(&filename, &lineno); + int start = get_char(); + while (start == ' ' || start == '\t' || start == '\n') + start = get_char(); + token_buffer.clear(); + if (start == EOF) { + if (got_location) + error_with_file_and_line(filename, lineno, + "end of input while defining macro"); + else + error("end of input while defining macro"); + return; + } + for (;;) { + int c = get_char(); + if (c == EOF) { + if (got_location) + error_with_file_and_line(filename, lineno, + "end of input while defining macro"); + else + error("end of input while defining macro"); + add_context(start + token_buffer); + return; + } + if (c == start) + break; + token_buffer += char(c); + } + add_context(start + token_buffer + start); +} + +void interpolate_macro_with_args(const char *body) +{ + char *argv[9]; + int argc = 0; + int i; + for (i = 0; i < 9; i++) + argv[i] = 0; + int level = 0; + int c; + do { + token_buffer.clear(); + for (;;) { + c = get_char(); + if (c == EOF) { + lex_error("end of input while scanning macro arguments"); + break; + } + if (level == 0 && (c == ',' || c == ')')) { + if (token_buffer.length() > 0) { + token_buffer += '\0'; + argv[argc] = strsave(token_buffer.contents()); + } + // for `foo()', argc = 0 + if (argc > 0 || c != ')' || i > 0) + argc++; + break; + } + token_buffer += char(c); + if (c == '(') + level++; + else if (c == ')') + level--; + } + } while (c != ')' && c != EOF); + current_input = new argument_macro_input(body, argc, argv, current_input); +} + +/* If lookup flag is non-zero the token will be looked up to see +if it is macro. If it's 1, it will looked up to see if it's a token. +*/ + +int get_token(int lookup_flag = 0) +{ + for (;;) { + int c = get_char(); + while (c == ' ' || c == '\n') + c = get_char(); + switch (c) { + case EOF: + { + add_context("end of input"); + } + return 0; + case '"': + { + int quoted = 0; + token_buffer.clear(); + for (;;) { + c = get_char(); + if (c == EOF) { + lex_error("missing \""); + break; + } + else if (c == '\n') { + lex_error("newline before end of quoted text"); + break; + } + else if (c == '"') { + if (!quoted) + break; + token_buffer[token_buffer.length() - 1] = '"'; + quoted = 0; + } + else { + token_buffer += c; + quoted = quoted ? 0 : c == '\\'; + } + } + } + add_quoted_context(token_buffer); + return QUOTED_TEXT; + case '{': + case '}': + case '^': + case '~': + case '\t': + add_context(c); + return c; + default: + { + int break_flag = 0; + int quoted = 0; + token_buffer.clear(); + if (c == '\\') + quoted = 1; + else + token_buffer += c; + int done = 0; + while (!done) { + c = peek_char(); + if (!quoted && lookup_flag != 0 && c == '(') { + token_buffer += '\0'; + definition *def = macro_table.lookup(token_buffer.contents()); + if (def && def->is_macro && !def->is_simple) { + (void)get_char(); // skip initial '(' + interpolate_macro_with_args(def->contents); + break_flag = 1; + break; + } + token_buffer.set_length(token_buffer.length() - 1); + } + if (quoted) { + quoted = 0; + switch (c) { + case EOF: + lex_error("`\\' ignored at end of equation"); + done = 1; + break; + case '\n': + lex_error("`\\' ignored because followed by newline"); + done = 1; + break; + case '\t': + lex_error("`\\' ignored because followed by tab"); + done = 1; + break; + case '"': + (void)get_char(); + token_buffer += '"'; + break; + default: + (void)get_char(); + token_buffer += '\\'; + token_buffer += c; + break; + } + } + else { + switch (c) { + case EOF: + case '{': + case '}': + case '^': + case '~': + case '"': + case ' ': + case '\t': + case '\n': + done = 1; + break; + case '\\': + (void)get_char(); + quoted = 1; + break; + default: + (void)get_char(); + token_buffer += char(c); + break; + } + } + } + if (break_flag || token_buffer.length() == 0) + break; + if (lookup_flag != 0) { + token_buffer += '\0'; + definition *def = macro_table.lookup(token_buffer.contents()); + token_buffer.set_length(token_buffer.length() - 1); + if (def) { + if (def->is_macro) { + current_input = new macro_input(def->contents, current_input); + break; + } + else if (lookup_flag == 1) { + add_context(token_buffer); + return def->tok; + } + } + } + add_context(token_buffer); + return TEXT; + } + } + } +} + +void do_include() +{ + int t = get_token(2); + if (t != TEXT && t != QUOTED_TEXT) { + lex_error("bad filename for include"); + return; + } + token_buffer += '\0'; + const char *filename = token_buffer.contents(); + errno = 0; + FILE *fp = fopen(filename, "r"); + if (fp == 0) { + lex_error("can't open included file `%1'", filename); + return; + } + current_input = new file_input(fp, filename, current_input); +} + +void ignore_definition() +{ + int t = get_token(); + if (t != TEXT) { + lex_error("bad definition"); + return; + } + get_delimited_text(); +} + +void do_definition(int is_simple) +{ + int t = get_token(); + if (t != TEXT) { + lex_error("bad definition"); + return; + } + token_buffer += '\0'; + const char *name = token_buffer.contents(); + definition *def = macro_table.lookup(name); + if (def == 0) { + def = new definition; + macro_table.define(name, def); + } + else if (def->is_macro) { + a_delete def->contents; + } + get_delimited_text(); + token_buffer += '\0'; + def->is_macro = 1; + def->contents = strsave(token_buffer.contents()); + def->is_simple = is_simple; +} + +void do_undef() +{ + int t = get_token(); + if (t != TEXT) { + lex_error("bad undef command"); + return; + } + token_buffer += '\0'; + macro_table.define(token_buffer.contents(), 0); +} + +void do_gsize() +{ + int t = get_token(2); + if (t != TEXT && t != QUOTED_TEXT) { + lex_error("bad argument to gsize command"); + return; + } + token_buffer += '\0'; + if (!set_gsize(token_buffer.contents())) + lex_error("invalid size `%1'", token_buffer.contents()); +} + +void do_gfont() +{ + int t = get_token(2); + if (t != TEXT && t != QUOTED_TEXT) { + lex_error("bad argument to gfont command"); + return; + } + token_buffer += '\0'; + set_gfont(token_buffer.contents()); +} + +void do_grfont() +{ + int t = get_token(2); + if (t != TEXT && t != QUOTED_TEXT) { + lex_error("bad argument to grfont command"); + return; + } + token_buffer += '\0'; + set_grfont(token_buffer.contents()); +} + +void do_gbfont() +{ + int t = get_token(2); + if (t != TEXT && t != QUOTED_TEXT) { + lex_error("bad argument to gbfont command"); + return; + } + token_buffer += '\0'; + set_gbfont(token_buffer.contents()); +} + +void do_space() +{ + int t = get_token(2); + if (t != TEXT && t != QUOTED_TEXT) { + lex_error("bad argument to space command"); + return; + } + token_buffer += '\0'; + char *ptr; + long n = strtol(token_buffer.contents(), &ptr, 10); + if (n == 0 && ptr == token_buffer.contents()) + lex_error("bad argument `%1' to space command", token_buffer.contents()); + else + set_space(int(n)); +} + +void do_ifdef() +{ + int t = get_token(); + if (t != TEXT) { + lex_error("bad ifdef"); + return; + } + token_buffer += '\0'; + definition *def = macro_table.lookup(token_buffer.contents()); + int result = def && def->is_macro && !def->is_simple; + get_delimited_text(); + if (result) { + token_buffer += '\0'; + current_input = new macro_input(token_buffer.contents(), current_input); + } +} + +void do_delim() +{ + int c = get_char(); + while (c == ' ' || c == '\n') + c = get_char(); + int d; + if (c == EOF || (d = get_char()) == EOF) + lex_error("end of file while reading argument to `delim'"); + else { + if (c == 'o' && d == 'f' && peek_char() == 'f') { + (void)get_char(); + start_delim = end_delim = '\0'; + } + else { + start_delim = c; + end_delim = d; + } + } +} + +void do_chartype() +{ + int t = get_token(2); + if (t != TEXT && t != QUOTED_TEXT) { + lex_error("bad chartype"); + return; + } + token_buffer += '\0'; + string type = token_buffer; + t = get_token(); + if (t != TEXT && t != QUOTED_TEXT) { + lex_error("bad chartype"); + return; + } + token_buffer += '\0'; + set_char_type(type.contents(), strsave(token_buffer.contents())); +} + +void do_set() +{ + int t = get_token(2); + if (t != TEXT && t != QUOTED_TEXT) { + lex_error("bad set"); + return; + } + token_buffer += '\0'; + string param = token_buffer; + t = get_token(); + if (t != TEXT && t != QUOTED_TEXT) { + lex_error("bad set"); + return; + } + token_buffer += '\0'; + int n; + if (sscanf(&token_buffer[0], "%d", &n) != 1) { + lex_error("bad number `%1'", token_buffer.contents()); + return; + } + set_param(param.contents(), n); +} + +int yylex() +{ + for (;;) { + int tk = get_token(1); + switch(tk) { + case UNDEF: + do_undef(); + break; + case SDEFINE: + do_definition(1); + break; + case DEFINE: + do_definition(0); + break; + case TDEFINE: + if (!nroff) + do_definition(0); + else + ignore_definition(); + break; + case NDEFINE: + if (nroff) + do_definition(0); + else + ignore_definition(); + break; + case GSIZE: + do_gsize(); + break; + case GFONT: + do_gfont(); + break; + case GRFONT: + do_grfont(); + break; + case GBFONT: + do_gbfont(); + break; + case SPACE: + do_space(); + break; + case INCLUDE: + do_include(); + break; + case IFDEF: + do_ifdef(); + break; + case DELIM: + do_delim(); + break; + case CHARTYPE: + do_chartype(); + break; + case SET: + do_set(); + break; + case QUOTED_TEXT: + case TEXT: + token_buffer += '\0'; + yylval.str = strsave(token_buffer.contents()); + // fall through + default: + return tk; + } + } +} + +void lex_error(const char *message, + const errarg &arg1, + const errarg &arg2, + const errarg &arg3) +{ + char *filename; + int lineno; + if (!get_location(&filename, &lineno)) + error(message, arg1, arg2, arg3); + else + error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3); +} + +void yyerror(const char *s) +{ + char *filename; + int lineno; + if (!get_location(&filename, &lineno)) + error(s); + else + error_with_file_and_line(filename, lineno, s); + show_context(); +} + diff --git a/contrib/groff/src/preproc/eqn/limit.cc b/contrib/groff/src/preproc/eqn/limit.cc new file mode 100644 index 0000000..046885d --- /dev/null +++ b/contrib/groff/src/preproc/eqn/limit.cc @@ -0,0 +1,195 @@ +// -*- 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 "eqn.h" +#include "pbox.h" + +class limit_box : public box { +private: + box *p; + box *from; + box *to; +public: + limit_box(box *, box *, box *); + ~limit_box(); + int compute_metrics(int); + void output(); + void debug_print(); + void check_tabs(int); +}; + +box *make_limit_box(box *pp, box *qq, box *rr) +{ + return new limit_box(pp, qq, rr); +} + +limit_box::limit_box(box *pp, box *qq, box *rr) +: p(pp), from(qq), to(rr) +{ + spacing_type = p->spacing_type; +} + +limit_box::~limit_box() +{ + delete p; + delete from; + delete to; +} + +int limit_box::compute_metrics(int style) +{ + printf(".nr " SIZE_FORMAT " \\n[.s]\n", uid); + if (!(style <= SCRIPT_STYLE && one_size_reduction_flag)) + set_script_size(); + printf(".nr " SMALL_SIZE_FORMAT " \\n[.s]\n", uid); + int res = 0; + int mark_uid = -1; + if (from != 0) { + res = from->compute_metrics(cramped_style(script_style(style))); + if (res) + mark_uid = from->uid; + } + if (to != 0) { + int r = to->compute_metrics(script_style(style)); + if (res && r) + error("multiple marks and lineups"); + else { + mark_uid = to->uid; + res = r; + } + } + printf(".ps \\n[" SIZE_FORMAT "]\n", uid); + int r = p->compute_metrics(style); + p->compute_subscript_kern(); + if (res && r) + error("multiple marks and lineups"); + else { + mark_uid = p->uid; + res = r; + } + printf(".nr " LEFT_WIDTH_FORMAT " " + "0\\n[" WIDTH_FORMAT "]", + uid, p->uid); + if (from != 0) + printf(">?(\\n[" SUB_KERN_FORMAT "]+\\n[" WIDTH_FORMAT "])", + p->uid, from->uid); + if (to != 0) + printf(">?(-\\n[" SUB_KERN_FORMAT "]+\\n[" WIDTH_FORMAT "])", + p->uid, to->uid); + printf("/2\n"); + printf(".nr " WIDTH_FORMAT " " + "0\\n[" WIDTH_FORMAT "]", + uid, p->uid); + if (from != 0) + printf(">?(-\\n[" SUB_KERN_FORMAT "]+\\n[" WIDTH_FORMAT "])", + p->uid, from->uid); + if (to != 0) + printf(">?(\\n[" SUB_KERN_FORMAT "]+\\n[" WIDTH_FORMAT "])", + p->uid, to->uid); + printf("/2+\\n[" LEFT_WIDTH_FORMAT "]\n", uid); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]", uid, p->uid); + if (to != 0) + printf(">?\\n[" WIDTH_FORMAT "]", to->uid); + if (from != 0) + printf(">?\\n[" WIDTH_FORMAT "]", from->uid); + printf("\n"); + if (res) + printf(".nr " MARK_REG " +(\\n[" LEFT_WIDTH_FORMAT "]" + "-(\\n[" WIDTH_FORMAT "]/2))\n", + uid, mark_uid); + if (to != 0) { + printf(".nr " SUP_RAISE_FORMAT " %dM+\\n[" DEPTH_FORMAT + "]>?%dM+\\n[" HEIGHT_FORMAT "]\n", + uid, big_op_spacing1, to->uid, big_op_spacing3, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" SUP_RAISE_FORMAT "]+\\n[" + HEIGHT_FORMAT "]+%dM\n", + uid, uid, to->uid, big_op_spacing5); + } + else + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + if (from != 0) { + printf(".nr " SUB_LOWER_FORMAT " %dM+\\n[" HEIGHT_FORMAT + "]>?%dM+\\n[" DEPTH_FORMAT "]\n", + uid, big_op_spacing2, from->uid, big_op_spacing4, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" SUB_LOWER_FORMAT "]+\\n[" + DEPTH_FORMAT "]+%dM\n", + uid, uid, from->uid, big_op_spacing5); + } + else + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + return res; +} + +void limit_box::output() +{ + printf("\\s[\\n[" SMALL_SIZE_FORMAT "]]", uid); + if (to != 0) { + printf("\\Z" DELIMITER_CHAR); + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u" + "+(-\\n[" WIDTH_FORMAT "]u+\\n[" SUB_KERN_FORMAT "]u/2u)'", + uid, to->uid, p->uid); + to->output(); + printf(DELIMITER_CHAR); + } + if (from != 0) { + printf("\\Z" DELIMITER_CHAR); + printf("\\v'\\n[" SUB_LOWER_FORMAT "]u'", uid); + printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u" + "+(-\\n[" SUB_KERN_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u)'", + uid, p->uid, from->uid); + from->output(); + printf(DELIMITER_CHAR); + } + printf("\\s[\\n[" SIZE_FORMAT "]]", uid); + printf("\\Z" DELIMITER_CHAR); + printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u" + "-(\\n[" WIDTH_FORMAT "]u/2u)'", + uid, p->uid); + p->output(); + printf(DELIMITER_CHAR); + printf("\\h'\\n[" WIDTH_FORMAT "]u'", uid); +} + +void limit_box::debug_print() +{ + fprintf(stderr, "{ "); + p->debug_print(); + fprintf(stderr, " }"); + if (from) { + fprintf(stderr, " from { "); + from->debug_print(); + fprintf(stderr, " }"); + } + if (to) { + fprintf(stderr, " to { "); + to->debug_print(); + fprintf(stderr, " }"); + } +} + +void limit_box::check_tabs(int level) +{ + if (to) + to->check_tabs(level + 1); + if (from) + from->check_tabs(level + 1); + p->check_tabs(level + 1); +} diff --git a/contrib/groff/src/preproc/eqn/list.cc b/contrib/groff/src/preproc/eqn/list.cc new file mode 100644 index 0000000..1118fa1 --- /dev/null +++ b/contrib/groff/src/preproc/eqn/list.cc @@ -0,0 +1,237 @@ +// -*- 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 "eqn.h" +#include "pbox.h" + +list_box *box::to_list_box() +{ + return 0; +} + +list_box *list_box::to_list_box() +{ + return this; +} + +void list_box::append(box *pp) +{ + list_box *q = pp->to_list_box(); + if (q == 0) + list.append(pp); + else { + for (int i = 0; i < q->list.len; i++) { + list.append(q->list.p[i]); + q->list.p[i] = 0; + } + q->list.len = 0; + delete q; + } +} + +list_box::list_box(box *pp) : list(pp), sty(-1) +{ + list_box *q = pp->to_list_box(); + if (q != 0) { + // flatten it + list.p[0] = q->list.p[0]; + for (int i = 1; i < q->list.len; i++) { + list.append(q->list.p[i]); + q->list.p[i] = 0; + } + q->list.len = 0; + delete q; + } +} + +static int compute_spacing(int is_script, int left, int right) +{ + if (left == SUPPRESS_TYPE || right == SUPPRESS_TYPE) + return 0; + if (left == PUNCTUATION_TYPE) + return is_script ? 0 : thin_space; + if (left == OPENING_TYPE || right == CLOSING_TYPE) + return 0; + if (right == BINARY_TYPE || left == BINARY_TYPE) + return is_script ? 0 : medium_space; + if (right == RELATION_TYPE) { + if (left == RELATION_TYPE) + return 0; + else + return is_script ? 0 : thick_space; + } + if (left == RELATION_TYPE) + return is_script ? 0 : thick_space; + if (right == OPERATOR_TYPE) + return thin_space; + if (left == INNER_TYPE || right == INNER_TYPE) + return is_script ? 0 : thin_space; + if (left == OPERATOR_TYPE && right == ORDINARY_TYPE) + return thin_space; + return 0; +} + +int list_box::compute_metrics(int style) +{ + sty = style; + int i; + for (i = 0; i < list.len; i++) { + int t = list.p[i]->spacing_type; + // 5 + if (t == BINARY_TYPE) { + int prevt; + if (i == 0 + || (prevt = list.p[i-1]->spacing_type) == BINARY_TYPE + || prevt == OPERATOR_TYPE + || prevt == RELATION_TYPE + || prevt == OPENING_TYPE + || prevt == PUNCTUATION_TYPE) + list.p[i]->spacing_type = ORDINARY_TYPE; + } + // 7 + else if ((t == RELATION_TYPE || t == CLOSING_TYPE + || t == PUNCTUATION_TYPE) + && i > 0 && list.p[i-1]->spacing_type == BINARY_TYPE) + list.p[i-1]->spacing_type = ORDINARY_TYPE; + } + for (i = 0; i < list.len; i++) { + unsigned flags = 0; + if (i - 1 >= 0 && list.p[i - 1]->right_is_italic()) + flags |= HINT_PREV_IS_ITALIC; + if (i + 1 < list.len && list.p[i + 1]->left_is_italic()) + flags |= HINT_NEXT_IS_ITALIC; + if (flags) + list.p[i]->hint(flags); + } + is_script = (style <= SCRIPT_STYLE); + int total_spacing = 0; + for (i = 1; i < list.len; i++) + total_spacing += compute_spacing(is_script, list.p[i-1]->spacing_type, + list.p[i]->spacing_type); + int res = 0; + for (i = 0; i < list.len; i++) + if (!list.p[i]->is_simple()) { + int r = list.p[i]->compute_metrics(style); + if (r) { + if (res) + error("multiple marks and lineups"); + else { + compute_sublist_width(i); + printf(".nr " MARK_REG " +\\n[" TEMP_REG"]\n"); + res = r; + } + } + } + printf(".nr " WIDTH_FORMAT " %dM", uid, total_spacing); + for (i = 0; i < list.len; i++) + if (!list.p[i]->is_simple()) + printf("+\\n[" WIDTH_FORMAT "]", list.p[i]->uid); + printf("\n"); + printf(".nr " HEIGHT_FORMAT " 0", uid); + for (i = 0; i < list.len; i++) + if (!list.p[i]->is_simple()) + printf(">?\\n[" HEIGHT_FORMAT "]", list.p[i]->uid); + printf("\n"); + printf(".nr " DEPTH_FORMAT " 0", uid); + for (i = 0; i < list.len; i++) + if (!list.p[i]->is_simple()) + printf(">?\\n[" DEPTH_FORMAT "]", list.p[i]->uid); + printf("\n"); + int have_simple = 0; + for (i = 0; i < list.len && !have_simple; i++) + have_simple = list.p[i]->is_simple(); + if (have_simple) { + printf(".nr " WIDTH_FORMAT " +\\w" DELIMITER_CHAR, uid); + for (i = 0; i < list.len; i++) + if (list.p[i]->is_simple()) + list.p[i]->output(); + printf(DELIMITER_CHAR "\n"); + printf(".nr " HEIGHT_FORMAT " \\n[rst]>?\\n[" HEIGHT_FORMAT "]\n", + uid, uid); + printf(".nr " DEPTH_FORMAT " 0-\\n[rsb]>?\\n[" DEPTH_FORMAT "]\n", + uid, uid); + } + return res; +} + +void list_box::compute_sublist_width(int n) +{ + int total_spacing = 0; + int i; + for (i = 1; i < n + 1 && i < list.len; i++) + total_spacing += compute_spacing(is_script, list.p[i-1]->spacing_type, + list.p[i]->spacing_type); + printf(".nr " TEMP_REG " %dM", total_spacing); + for (i = 0; i < n; i++) + if (!list.p[i]->is_simple()) + printf("+\\n[" WIDTH_FORMAT "]", list.p[i]->uid); + int have_simple = 0; + for (i = 0; i < n && !have_simple; i++) + have_simple = list.p[i]->is_simple(); + if (have_simple) { + printf("+\\w" DELIMITER_CHAR); + for (i = 0; i < n; i++) + if (list.p[i]->is_simple()) + list.p[i]->output(); + printf(DELIMITER_CHAR); + } + printf("\n"); +} + +void list_box::compute_subscript_kern() +{ + // We can only call compute_subscript_kern if we have called + // compute_metrics first. + if (list.p[list.len-1]->is_simple()) + list.p[list.len-1]->compute_metrics(sty); + list.p[list.len-1]->compute_subscript_kern(); + printf(".nr " SUB_KERN_FORMAT " \\n[" SUB_KERN_FORMAT "]\n", + uid, list.p[list.len-1]->uid); +} + +void list_box::output() +{ + for (int i = 0; i < list.len; i++) { + if (i > 0) { + int n = compute_spacing(is_script, + list.p[i-1]->spacing_type, + list.p[i]->spacing_type); + if (n > 0) + printf("\\h'%dM'", n); + } + list.p[i]->output(); + } +} + +void list_box::handle_char_type(int st, int ft) +{ + for (int i = 0; i < list.len; i++) + list.p[i]->handle_char_type(st, ft); +} + +void list_box::debug_print() +{ + list.list_debug_print(" "); +} + +void list_box::check_tabs(int level) +{ + list.list_check_tabs(level); +} diff --git a/contrib/groff/src/preproc/eqn/main.cc b/contrib/groff/src/preproc/eqn/main.cc new file mode 100644 index 0000000..6dc03f0 --- /dev/null +++ b/contrib/groff/src/preproc/eqn/main.cc @@ -0,0 +1,419 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 + 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 "eqn.h" +#include "stringclass.h" +#include "device.h" +#include "searchpath.h" +#include "macropath.h" +#include "htmlindicate.h" +#include "pbox.h" + +#define STARTUP_FILE "eqnrc" + +extern int yyparse(); + +static char *delim_search (char *, int); +static int inline_equation (FILE *, string &, string &); + +char start_delim = '\0'; +char end_delim = '\0'; +int non_empty_flag; +int inline_flag; +int draw_flag = 0; +int one_size_reduction_flag = 0; +int compatible_flag = 0; +int no_newline_in_delim_flag = 0; +int html = 0; +// if we encounter a region marked as an image then we +// do not mark up inline equations. +int suppress_html = 0; + + +int read_line(FILE *fp, string *p) +{ + p->clear(); + int c = -1; + while ((c = getc(fp)) != EOF) { + if (!illegal_input_char(c)) + *p += char(c); + else + error("illegal input character code `%1'", c); + if (c == '\n') + break; + } + current_lineno++; + return p->length() > 0; +} + +void do_file(FILE *fp, const char *filename) +{ + string linebuf; + string str; + printf(".lf 1 %s\n", filename); + current_filename = filename; + current_lineno = 0; + while (read_line(fp, &linebuf)) { + if (linebuf.length() >= 4 + && linebuf[0] == '.' && linebuf[1] == 'l' && linebuf[2] == 'f' + && (linebuf[3] == ' ' || linebuf[3] == '\n' || compatible_flag)) { + put_string(linebuf, stdout); + linebuf += '\0'; + if (interpret_lf_args(linebuf.contents() + 3)) + current_lineno--; + } + else if (linebuf.length() >= 12 + && linebuf[0] == '.' && linebuf[1] == 'H' && linebuf[2] == 'T' + && linebuf[3] == 'M' && linebuf[4] == 'L' && linebuf[5] == '-' + && linebuf[6] == 'I' && linebuf[7] == 'M' && linebuf[8] == 'A' + && linebuf[9] == 'G' && linebuf[10] == 'E' + && linebuf[11] == '\n') { + put_string(linebuf, stdout); + suppress_html++; + } + else if (linebuf.length() >= 16 + && linebuf[0] == '.' && linebuf[1] == 'H' && linebuf[2] == 'T' + && linebuf[3] == 'M' && linebuf[4] == 'L' && linebuf[5] == '-' + && linebuf[6] == 'I' && linebuf[7] == 'M' && linebuf[8] == 'A' + && linebuf[9] == 'G' && linebuf[10] == 'E' && linebuf[11] == '-' + && linebuf[12] == 'E' && linebuf[13] == 'N' && linebuf[14] == 'D' + && linebuf[15] == '\n') { + put_string(linebuf, stdout); + suppress_html--; + } + else if (linebuf.length() >= 4 + && linebuf[0] == '.' + && linebuf[1] == 'E' + && linebuf[2] == 'Q' + && (linebuf[3] == ' ' || linebuf[3] == '\n' + || compatible_flag)) { + if (html && (suppress_html == 0)) + graphic_start(0); + put_string(linebuf, stdout); + int start_lineno = current_lineno + 1; + str.clear(); + for (;;) { + if (!read_line(fp, &linebuf)) + fatal("end of file before .EN"); + if (linebuf.length() >= 3 && linebuf[0] == '.' && linebuf[1] == 'E') { + if (linebuf[2] == 'N' + && (linebuf.length() == 3 || linebuf[3] == ' ' + || linebuf[3] == '\n' || compatible_flag)) + break; + else if (linebuf[2] == 'Q' && linebuf.length() > 3 + && (linebuf[3] == ' ' || linebuf[3] == '\n' + || compatible_flag)) + fatal("nested .EQ"); + } + str += linebuf; + } + str += '\0'; + start_string(); + init_lex(str.contents(), current_filename, start_lineno); + non_empty_flag = 0; + inline_flag = 0; + yyparse(); + if (non_empty_flag) { + printf(".lf %d\n", current_lineno - 1); + output_string(); + } + restore_compatibility(); + printf(".lf %d\n", current_lineno); + put_string(linebuf, stdout); + if (html && (suppress_html == 0)) + graphic_end(); + } + else if (start_delim != '\0' && linebuf.search(start_delim) >= 0 + && inline_equation(fp, linebuf, str)) + ; + else + put_string(linebuf, stdout); + } + current_filename = 0; + current_lineno = 0; +} + +// Handle an inline equation. Return 1 if it was an inline equation, +// otherwise. +static int inline_equation(FILE *fp, string &linebuf, string &str) +{ + linebuf += '\0'; + char *ptr = &linebuf[0]; + char *start = delim_search(ptr, start_delim); + if (!start) { + // It wasn't a delimiter after all. + linebuf.set_length(linebuf.length() - 1); // strip the '\0' + return 0; + } + start_string(); + inline_flag = 1; + for (;;) { + if (no_newline_in_delim_flag && strchr(start + 1, end_delim) == 0) { + error("missing `%1'", end_delim); + char *nl = strchr(start + 1, '\n'); + if (nl != 0) + *nl = '\0'; + do_text(ptr); + break; + } + int start_lineno = current_lineno; + *start = '\0'; + do_text(ptr); + ptr = start + 1; + str.clear(); + for (;;) { + char *end = strchr(ptr, end_delim); + if (end != 0) { + *end = '\0'; + str += ptr; + ptr = end + 1; + break; + } + str += ptr; + if (!read_line(fp, &linebuf)) + fatal("end of file before `%1'", end_delim); + linebuf += '\0'; + ptr = &linebuf[0]; + } + str += '\0'; + if (html && (suppress_html == 0)) { + printf(".as %s ", LINE_STRING); + graphic_start(1); + printf("\n"); + } + init_lex(str.contents(), current_filename, start_lineno); + yyparse(); + if (html && (suppress_html == 0)) { + printf(".as %s ", LINE_STRING); + graphic_end(); + printf("\n"); + } + start = delim_search(ptr, start_delim); + if (start == 0) { + char *nl = strchr(ptr, '\n'); + if (nl != 0) + *nl = '\0'; + do_text(ptr); + break; + } + } + printf(".lf %d\n", current_lineno); + output_string(); + restore_compatibility(); + printf(".lf %d\n", current_lineno + 1); + return 1; +} + +/* Search for delim. Skip over number register and string names etc. */ + +static char *delim_search(char *ptr, int delim) +{ + while (*ptr) { + if (*ptr == delim) + return ptr; + if (*ptr++ == '\\') { + switch (*ptr) { + case 'n': + case '*': + case 'f': + case 'g': + case 'k': + switch (*++ptr) { + case '\0': + case '\\': + break; + case '(': + if (*++ptr != '\\' && *ptr != '\0' + && *++ptr != '\\' && *ptr != '\0') + ptr++; + break; + case '[': + while (*++ptr != '\0') + if (*ptr == ']') { + ptr++; + break; + } + break; + default: + ptr++; + break; + } + break; + case '\\': + case '\0': + break; + default: + ptr++; + break; + } + } + } + return 0; +} + +void usage(FILE *stream) +{ + fprintf(stream, + "usage: %s [ -rvDCNR ] -dxx -fn -sn -pn -mn -Mdir -Ts [ files ... ]\n", + program_name); +} + +int main(int argc, char **argv) +{ + program_name = argv[0]; + static char stderr_buf[BUFSIZ]; + setbuf(stderr, stderr_buf); + int opt; + int load_startup_file = 1; + static const struct option long_options[] = { + { "help", no_argument, 0, CHAR_MAX + 1 }, + { "version", no_argument, 0, 'v' }, + { NULL, 0, 0, 0 } + }; + while ((opt = getopt_long(argc, argv, "DCRvd:f:p:s:m:T:M:rN", long_options, + NULL)) + != EOF) + switch (opt) { + case 'C': + compatible_flag = 1; + break; + case 'R': // don't load eqnrc + load_startup_file = 0; + break; + case 'M': + config_macro_path.command_line_dir(optarg); + break; + case 'v': + { + extern const char *Version_string; + printf("GNU eqn (groff) version %s\n", Version_string); + exit(0); + break; + } + case 'd': + if (optarg[0] == '\0' || optarg[1] == '\0') + error("-d requires two character argument"); + else if (illegal_input_char(optarg[0])) + error("bad delimiter `%1'", optarg[0]); + else if (illegal_input_char(optarg[1])) + error("bad delimiter `%1'", optarg[1]); + else { + start_delim = optarg[0]; + end_delim = optarg[1]; + } + break; + case 'f': + set_gfont(optarg); + break; + case 'T': + device = optarg; + if (strcmp(device, "ps:html") == 0) { + device = "ps"; + html = 1; + } + break; + case 's': + if (!set_gsize(optarg)) + error("invalid size `%1'", optarg); + break; + case 'p': + { + int n; + if (sscanf(optarg, "%d", &n) == 1) + set_script_reduction(n); + else + error("bad size `%1'", optarg); + } + break; + case 'm': + { + int n; + if (sscanf(optarg, "%d", &n) == 1) + set_minimum_size(n); + else + error("bad size `%1'", optarg); + } + break; + case 'r': + one_size_reduction_flag = 1; + break; + case 'D': + warning("-D option is obsolete: use `set draw_lines 1' instead"); + draw_flag = 1; + break; + case 'N': + no_newline_in_delim_flag = 1; + break; + case CHAR_MAX + 1: // --help + usage(stdout); + exit(0); + break; + case '?': + usage(stderr); + exit(1); + break; + default: + assert(0); + } + init_table(device); + init_char_table(); + printf(".if !'\\*(.T'%s' " + ".if !'\\*(.T'html' " // the html device uses `-Tps' to render + // equations as images + ".tm warning: %s should have been given a `-T\\*(.T' option\n", + device, program_name); + printf(".if '\\*(.T'html' " + ".if !'%s'ps' " + ".tm warning: %s should have been given a `-Tps' option\n", + device, program_name); + printf(".if '\\*(.T'html' " + ".if !'%s'ps' " + ".tm warning: (it is advisable to invoke groff via: groff -Thtml -e)\n", + device); + if (load_startup_file) { + char *path; + FILE *fp = config_macro_path.open_file(STARTUP_FILE, &path); + if (fp) { + do_file(fp, path); + fclose(fp); + a_delete path; + } + } + if (optind >= argc) + do_file(stdin, "-"); + else + for (int i = optind; i < argc; i++) + if (strcmp(argv[i], "-") == 0) + do_file(stdin, "-"); + else { + errno = 0; + FILE *fp = fopen(argv[i], "r"); + if (!fp) + fatal("can't open `%1': %2", argv[i], strerror(errno)); + else { + do_file(fp, argv[i]); + fclose(fp); + } + } + if (ferror(stdout) || fflush(stdout) < 0) + fatal("output error"); + return 0; +} diff --git a/contrib/groff/src/preproc/eqn/mark.cc b/contrib/groff/src/preproc/eqn/mark.cc new file mode 100644 index 0000000..99d1b75 --- /dev/null +++ b/contrib/groff/src/preproc/eqn/mark.cc @@ -0,0 +1,121 @@ +// -*- 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 "eqn.h" +#include "pbox.h" + +class mark_box : public pointer_box { +public: + mark_box(box *); + int compute_metrics(int); + void output(); + void debug_print(); +}; + +// we push down marks so that they don't interfere with spacing + +box *make_mark_box(box *p) +{ + list_box *b = p->to_list_box(); + if (b != 0) { + b->list.p[0] = make_mark_box(b->list.p[0]); + return b; + } + else + return new mark_box(p); +} + +mark_box::mark_box(box *pp) : pointer_box(pp) +{ +} + +void mark_box::output() +{ + p->output(); +} + +int mark_box::compute_metrics(int style) +{ + int res = p->compute_metrics(style); + if (res) + error("multiple marks and lineups"); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + printf(".nr " MARK_REG " 0\n"); + return FOUND_MARK; +} + +void mark_box::debug_print() +{ + fprintf(stderr, "mark { "); + p->debug_print(); + fprintf(stderr, " }"); +} + + +class lineup_box : public pointer_box { +public: + lineup_box(box *); + void output(); + int compute_metrics(int style); + void debug_print(); +}; + +// we push down lineups so that they don't interfere with spacing + +box *make_lineup_box(box *p) +{ + list_box *b = p->to_list_box(); + if (b != 0) { + b->list.p[0] = make_lineup_box(b->list.p[0]); + return b; + } + else + return new lineup_box(p); +} + +lineup_box::lineup_box(box *pp) : pointer_box(pp) +{ +} + +void lineup_box::output() +{ + p->output(); +} + +int lineup_box::compute_metrics(int style) +{ + int res = p->compute_metrics(style); + if (res) + error("multiple marks and lineups"); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + printf(".nr " MARK_REG " 0\n"); + return FOUND_LINEUP; +} + +void lineup_box::debug_print() +{ + fprintf(stderr, "lineup { "); + p->debug_print(); + fprintf(stderr, " }"); +} diff --git a/contrib/groff/src/preproc/eqn/neqn.man b/contrib/groff/src/preproc/eqn/neqn.man new file mode 100644 index 0000000..bca7dc2 --- /dev/null +++ b/contrib/groff/src/preproc/eqn/neqn.man @@ -0,0 +1,21 @@ +.TH @G@NEQN @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" +.SH NAME +@g@neqn \- format equations for ascii output +.SH SYNOPSIS +.B @g@neqn +[@g@eqn options] +.SH DESCRIPTION +The +.B @g@neqn +program is actually just a shell script which invokes the +.BR @g@eqn (@MAN1EXT@) +command with the +.B ascii +output device. +.LP +Note that +.B @g@eqn +does not support low-resolution, typewriter-like devices (although it may +work adequately for very simple input). +.SH "SEE ALSO" +.BR @g@eqn (@MAN1EXT@) diff --git a/contrib/groff/src/preproc/eqn/other.cc b/contrib/groff/src/preproc/eqn/other.cc new file mode 100644 index 0000000..eb9e50a --- /dev/null +++ b/contrib/groff/src/preproc/eqn/other.cc @@ -0,0 +1,601 @@ +// -*- 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 "eqn.h" +#include "pbox.h" + +class accent_box : public pointer_box { +private: + box *ab; +public: + accent_box(box *, box *); + ~accent_box(); + int compute_metrics(int); + void output(); + void debug_print(); + void check_tabs(int); +}; + +box *make_accent_box(box *p, box *q) +{ + return new accent_box(p, q); +} + +accent_box::accent_box(box *pp, box *qq) : pointer_box(pp), ab(qq) +{ +} + +accent_box::~accent_box() +{ + delete ab; +} + +#if 0 +int accent_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + p->compute_skew(); + ab->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + printf(".nr " SUP_RAISE_FORMAT " \\n[" HEIGHT_FORMAT "]-%dM>?0\n", + uid, p->uid, x_height); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]+\\n[" + SUP_RAISE_FORMAT "]\n", + uid, ab->uid, uid); + return r; +} + +void accent_box::output() +{ + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u+\\n[" + SKEW_FORMAT "]u'", + p->uid, ab->uid, p->uid); + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + ab->output(); + printf("\\h'-\\n[" WIDTH_FORMAT "]u'", ab->uid); + printf("\\v'\\n[" SUP_RAISE_FORMAT "]u'", uid); + printf("\\h'-(\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u+\\n[" + SKEW_FORMAT "]u)'", + p->uid, ab->uid, p->uid); + p->output(); +} +#endif + +/* This version copes with the possibility of an accent's being wider +than its accentee. LEFT_WIDTH_FORMAT gives the distance from the +left edge of the resulting box to the middle of the accentee's box.*/ + +int accent_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + p->compute_skew(); + ab->compute_metrics(style); + printf(".nr " LEFT_WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]/2" + ">?(\\n[" WIDTH_FORMAT "]/2-\\n[" SKEW_FORMAT "])\n", + uid, p->uid, ab->uid, p->uid); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]/2" + ">?(\\n[" WIDTH_FORMAT "]/2+\\n[" SKEW_FORMAT "])" + "+\\n[" LEFT_WIDTH_FORMAT "]\n", + uid, p->uid, ab->uid, p->uid, uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + printf(".nr " SUP_RAISE_FORMAT " \\n[" HEIGHT_FORMAT "]-%dM>?0\n", + uid, p->uid, x_height); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]+\\n[" + SUP_RAISE_FORMAT "]\n", + uid, ab->uid, uid); + if (r) + printf(".nr " MARK_REG " +\\n[" LEFT_WIDTH_FORMAT "]" + "-(\\n[" WIDTH_FORMAT "]/2)'\n", + uid, p->uid); + return r; +} + +void accent_box::output() +{ + printf("\\Z" DELIMITER_CHAR); + printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u+\\n[" SKEW_FORMAT "]u" + "-(\\n[" WIDTH_FORMAT "]u/2u)'", + uid, p->uid, ab->uid); + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + ab->output(); + printf(DELIMITER_CHAR); + printf("\\Z" DELIMITER_CHAR); + printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u-(\\n[" WIDTH_FORMAT "]u/2u)'", + uid, p->uid); + p->output(); + printf(DELIMITER_CHAR); + printf("\\h'\\n[" WIDTH_FORMAT "]u'", uid); +} + +void accent_box::check_tabs(int level) +{ + ab->check_tabs(level + 1); + p->check_tabs(level + 1); +} + +void accent_box::debug_print() +{ + fprintf(stderr, "{ "); + p->debug_print(); + fprintf(stderr, " } accent { "); + ab->debug_print(); + fprintf(stderr, " }"); +} + +class overline_char_box : public simple_box { +public: + overline_char_box(); + void output(); + void debug_print(); +}; + +overline_char_box::overline_char_box() +{ +} + +void overline_char_box::output() +{ + printf("\\v'-%dM/2u-%dM'", 7*default_rule_thickness, x_height); + printf((draw_flag ? "\\D'l%dM 0'" : "\\l'%dM\\&\\(ru'"), + accent_width); + printf("\\v'%dM/2u+%dM'", 7*default_rule_thickness, x_height); +} + +void overline_char_box::debug_print() +{ + fprintf(stderr, "<overline char>"); +} + +class overline_box : public pointer_box { +public: + overline_box(box *); + int compute_metrics(int); + void output(); + void debug_print(); +}; + +box *make_overline_box(box *p) +{ + if (p->is_char()) + return new accent_box(p, new overline_char_box); + else + return new overline_box(p); +} + +overline_box::overline_box(box *pp) : pointer_box(pp) +{ +} + +int overline_box::compute_metrics(int style) +{ + int r = p->compute_metrics(cramped_style(style)); + // 9 + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]+%dM\n", + uid, p->uid, default_rule_thickness*5); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + return r; +} + +void overline_box::output() +{ + // 9 + printf("\\Z" DELIMITER_CHAR); + printf("\\v'-\\n[" HEIGHT_FORMAT "]u-(%dM/2u)'", + p->uid, 7*default_rule_thickness); + if (draw_flag) + printf("\\D'l\\n[" WIDTH_FORMAT "]u 0'", p->uid); + else + printf("\\l'\\n[" WIDTH_FORMAT "]u\\&\\(ru'", p->uid); + printf(DELIMITER_CHAR); + p->output(); +} + +void overline_box::debug_print() +{ + fprintf(stderr, "{ "); + p->debug_print(); + fprintf(stderr, " } bar"); +} + +class uaccent_box : public pointer_box { + box *ab; +public: + uaccent_box(box *, box *); + ~uaccent_box(); + int compute_metrics(int); + void output(); + void compute_subscript_kern(); + void check_tabs(int); + void debug_print(); +}; + +box *make_uaccent_box(box *p, box *q) +{ + return new uaccent_box(p, q); +} + +uaccent_box::uaccent_box(box *pp, box *qq) +: pointer_box(pp), ab(qq) +{ +} + +uaccent_box::~uaccent_box() +{ + delete ab; +} + +int uaccent_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + ab->compute_metrics(style); + printf(".nr " LEFT_WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]/2" + ">?(\\n[" WIDTH_FORMAT "]/2)\n", + uid, p->uid, ab->uid); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]/2" + ">?(\\n[" WIDTH_FORMAT "]/2)" + "+\\n[" LEFT_WIDTH_FORMAT "]\n", + uid, p->uid, ab->uid, uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]" + "+\\n[" DEPTH_FORMAT "]\n", + uid, p->uid, ab->uid); + if (r) + printf(".nr " MARK_REG " +\\n[" LEFT_WIDTH_FORMAT "]" + "-(\\n[" WIDTH_FORMAT "]/2)'\n", + uid, p->uid); + return r; +} + +void uaccent_box::output() +{ + printf("\\Z" DELIMITER_CHAR); + printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u-(\\n[" WIDTH_FORMAT "]u/2u)'", + uid, ab->uid); + printf("\\v'\\n[" DEPTH_FORMAT "]u'", p->uid); + ab->output(); + printf(DELIMITER_CHAR); + printf("\\Z" DELIMITER_CHAR); + printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u-(\\n[" WIDTH_FORMAT "]u/2u)'", + uid, p->uid); + p->output(); + printf(DELIMITER_CHAR); + printf("\\h'\\n[" WIDTH_FORMAT "]u'", uid); +} + +void uaccent_box::check_tabs(int level) +{ + ab->check_tabs(level + 1); + p->check_tabs(level + 1); +} + +void uaccent_box::compute_subscript_kern() +{ + box::compute_subscript_kern(); // want 0 subscript kern +} + +void uaccent_box::debug_print() +{ + fprintf(stderr, "{ "); + p->debug_print(); + fprintf(stderr, " } uaccent { "); + ab->debug_print(); + fprintf(stderr, " }"); +} + +class underline_char_box : public simple_box { +public: + underline_char_box(); + void output(); + void debug_print(); +}; + +underline_char_box::underline_char_box() +{ +} + +void underline_char_box::output() +{ + printf("\\v'%dM/2u'", 7*default_rule_thickness); + printf((draw_flag ? "\\D'l%dM 0'" : "\\l'%dM\\&\\(ru'"), + accent_width); + printf("\\v'-%dM/2u'", 7*default_rule_thickness); +} + +void underline_char_box::debug_print() +{ + fprintf(stderr, "<underline char>"); +} + + +class underline_box : public pointer_box { +public: + underline_box(box *); + int compute_metrics(int); + void output(); + void compute_subscript_kern(); + void debug_print(); +}; + +box *make_underline_box(box *p) +{ + if (p->is_char()) + return new uaccent_box(p, new underline_char_box); + else + return new underline_box(p); +} + +underline_box::underline_box(box *pp) : pointer_box(pp) +{ +} + +int underline_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + // 10 + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]+%dM\n", + uid, p->uid, default_rule_thickness*5); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + return r; +} + +void underline_box::output() +{ + // 10 + printf("\\Z" DELIMITER_CHAR); + printf("\\v'\\n[" DEPTH_FORMAT "]u+(%dM/2u)'", + p->uid, 7*default_rule_thickness); + if (draw_flag) + printf("\\D'l\\n[" WIDTH_FORMAT "]u 0'", p->uid); + else + printf("\\l'\\n[" WIDTH_FORMAT "]u\\&\\(ru'", p->uid); + printf(DELIMITER_CHAR); + p->output(); +} + +// we want an underline box to have 0 subscript kern + +void underline_box::compute_subscript_kern() +{ + box::compute_subscript_kern(); +} + +void underline_box::debug_print() +{ + fprintf(stderr, "{ "); + p->debug_print(); + fprintf(stderr, " } under"); +} + +size_box::size_box(char *s, box *pp) : pointer_box(pp), size(s) +{ +} + +int size_box::compute_metrics(int style) +{ + printf(".nr " SIZE_FORMAT " \\n[.s]\n", uid); + printf(".ps %s\n", size); + printf(".nr " SMALL_SIZE_FORMAT " \\n[.s]\n", uid); + int r = p->compute_metrics(style); + printf(".ps \\n[" SIZE_FORMAT "]\n", uid); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + return r; +} + +void size_box::output() +{ + printf("\\s[\\n[" SMALL_SIZE_FORMAT "]]", uid); + p->output(); + printf("\\s[\\n[" SIZE_FORMAT "]]", uid); +} + +size_box::~size_box() +{ + a_delete size; +} + +void size_box::debug_print() +{ + fprintf(stderr, "size %s { ", size); + p->debug_print(); + fprintf(stderr, " }"); +} + + +font_box::font_box(char *s, box *pp) : pointer_box(pp), f(s) +{ +} + +font_box::~font_box() +{ + a_delete f; +} + +int font_box::compute_metrics(int style) +{ + const char *old_roman_font = current_roman_font; + current_roman_font = f; + printf(".nr " FONT_FORMAT " \\n[.f]\n", uid); + printf(".ft %s\n", f); + int r = p->compute_metrics(style); + current_roman_font = old_roman_font; + printf(".ft \\n[" FONT_FORMAT "]\n", uid); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + return r; +} + +void font_box::output() +{ + printf("\\f[%s]", f); + const char *old_roman_font = current_roman_font; + current_roman_font = f; + p->output(); + current_roman_font = old_roman_font; + printf("\\f[\\n[" FONT_FORMAT "]]", uid); +} + +void font_box::debug_print() +{ + fprintf(stderr, "font %s { ", f); + p->debug_print(); + fprintf(stderr, " }"); +} + +fat_box::fat_box(box *pp) : pointer_box(pp) +{ +} + +int fat_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]+%dM\n", + uid, p->uid, fat_offset); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + return r; +} + +void fat_box::output() +{ + p->output(); + printf("\\h'-\\n[" WIDTH_FORMAT "]u'", p->uid); + printf("\\h'%dM'", fat_offset); + p->output(); +} + + +void fat_box::debug_print() +{ + fprintf(stderr, "fat { "); + p->debug_print(); + fprintf(stderr, " }"); +} + + +vmotion_box::vmotion_box(int i, box *pp) : pointer_box(pp), n(i) +{ +} + +int vmotion_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + if (n > 0) { + printf(".nr " HEIGHT_FORMAT " %dM+\\n[" HEIGHT_FORMAT "]\n", + uid, n, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + } + else { + printf(".nr " DEPTH_FORMAT " %dM+\\n[" DEPTH_FORMAT "]>?0\n", + uid, -n, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", + uid, p->uid); + } + return r; +} + +void vmotion_box::output() +{ + printf("\\v'%dM'", -n); + p->output(); + printf("\\v'%dM'", n); +} + +void vmotion_box::debug_print() +{ + if (n >= 0) + fprintf(stderr, "up %d { ", n); + else + fprintf(stderr, "down %d { ", -n); + p->debug_print(); + fprintf(stderr, " }"); +} + +hmotion_box::hmotion_box(int i, box *pp) : pointer_box(pp), n(i) +{ +} + +int hmotion_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]+%dM\n", + uid, p->uid, n); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + if (r) + printf(".nr " MARK_REG " +%dM\n", n); + return r; +} + +void hmotion_box::output() +{ + printf("\\h'%dM'", n); + p->output(); +} + +void hmotion_box::debug_print() +{ + if (n >= 0) + fprintf(stderr, "fwd %d { ", n); + else + fprintf(stderr, "back %d { ", -n); + p->debug_print(); + fprintf(stderr, " }"); +} + +vcenter_box::vcenter_box(box *pp) : pointer_box(pp) +{ +} + +int vcenter_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " SUP_RAISE_FORMAT " \\n[" DEPTH_FORMAT "]-\\n[" + HEIGHT_FORMAT "]/2+%dM\n", + uid, p->uid, p->uid, axis_height); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]+\\n[" + SUP_RAISE_FORMAT "]>?0\n", uid, p->uid, uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]-\\n[" + SUP_RAISE_FORMAT "]>?0\n", uid, p->uid, uid); + + return r; +} + +void vcenter_box::output() +{ + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + p->output(); + printf("\\v'\\n[" SUP_RAISE_FORMAT "]u'", uid); +} + +void vcenter_box::debug_print() +{ + fprintf(stderr, "vcenter { "); + p->debug_print(); + fprintf(stderr, " }"); +} + diff --git a/contrib/groff/src/preproc/eqn/over.cc b/contrib/groff/src/preproc/eqn/over.cc new file mode 100644 index 0000000..06b0321 --- /dev/null +++ b/contrib/groff/src/preproc/eqn/over.cc @@ -0,0 +1,196 @@ +// -*- 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 "eqn.h" +#include "pbox.h" + +class over_box : public box { +private: + int reduce_size; + box *num; + box *den; +public: + over_box(int small, box *, box *); + ~over_box(); + void debug_print(); + int compute_metrics(int); + void output(); + void check_tabs(int); +}; + +box *make_over_box(box *pp, box *qq) +{ + return new over_box(0, pp, qq); +} + +box *make_small_over_box(box *pp, box *qq) +{ + return new over_box(1, pp, qq); +} + +over_box::over_box(int is_small, box *pp, box *qq) +: reduce_size(is_small), num(pp), den(qq) +{ + spacing_type = INNER_TYPE; +} + +over_box::~over_box() +{ + delete num; + delete den; +} + +int over_box::compute_metrics(int style) +{ + if (reduce_size) { + style = script_style(style); + printf(".nr " SIZE_FORMAT " \\n[.s]\n", uid); + set_script_size(); + printf(".nr " SMALL_SIZE_FORMAT " \\n[.s]\n", uid); + } + int mark_uid; + int res = num->compute_metrics(style); + if (res) + mark_uid = num->uid; + int r = den->compute_metrics(cramped_style(style)); + if (r && res) + error("multiple marks and lineups"); + else { + mark_uid = den->uid; + res = r; + } + if (reduce_size) + printf(".ps \\n[" SIZE_FORMAT "]\n", uid); + printf(".nr " WIDTH_FORMAT " (\\n[" WIDTH_FORMAT "]>?\\n[" WIDTH_FORMAT "]", + uid, num->uid, den->uid); + // allow for \(ru being wider than both the numerator and denominator + if (!draw_flag) + fputs(">?\\w" DELIMITER_CHAR "\\(ru" DELIMITER_CHAR, stdout); + printf(")+%dM\n", null_delimiter_space*2 + over_hang*2); + // 15b + printf(".nr " SUP_RAISE_FORMAT " %dM\n", + uid, (reduce_size ? num2 : num1)); + printf(".nr " SUB_LOWER_FORMAT " %dM\n", + uid, (reduce_size ? denom2 : denom1)); + + // 15d + printf(".nr " SUP_RAISE_FORMAT " +(\\n[" DEPTH_FORMAT + "]-\\n[" SUP_RAISE_FORMAT "]+%dM+(%dM/2)+%dM)>?0\n", + uid, num->uid, uid, axis_height, default_rule_thickness, + default_rule_thickness*(reduce_size ? 1 : 3)); + printf(".nr " SUB_LOWER_FORMAT " +(\\n[" HEIGHT_FORMAT + "]-\\n[" SUB_LOWER_FORMAT "]-%dM+(%dM/2)+%dM)>?0\n", + uid, den->uid, uid, axis_height, default_rule_thickness, + default_rule_thickness*(reduce_size ? 1 : 3)); + + + printf(".nr " HEIGHT_FORMAT " \\n[" SUP_RAISE_FORMAT "]+\\n[" + HEIGHT_FORMAT "]\n", + uid, uid, num->uid); + printf(".nr " DEPTH_FORMAT " \\n[" SUB_LOWER_FORMAT "]+\\n[" + DEPTH_FORMAT "]\n", + uid, uid, den->uid); + if (res) + printf(".nr " MARK_REG " +(\\n[" WIDTH_FORMAT "]-\\n[" + WIDTH_FORMAT "]/2)\n", uid, mark_uid); + return res; +} + +#define USE_Z + +void over_box::output() +{ + if (reduce_size) + printf("\\s[\\n[" SMALL_SIZE_FORMAT "]]", uid); +#ifdef USE_Z + printf("\\Z" DELIMITER_CHAR); +#endif + // move up to the numerator baseline + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + // move across so that it's centered + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u'", + uid, num->uid); + + // print the numerator + num->output(); + +#ifdef USE_Z + printf(DELIMITER_CHAR); +#else + // back again + printf("\\h'-\\n[" WIDTH_FORMAT "]u'", num->uid); + printf("\\h'-(\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u)'", + uid, num->uid); + // down again + printf("\\v'\\n[" SUP_RAISE_FORMAT "]u'", uid); +#endif +#ifdef USE_Z + printf("\\Z" DELIMITER_CHAR); +#endif + // move down to the denominator baseline + printf("\\v'\\n[" SUB_LOWER_FORMAT "]u'", uid); + + // move across so that it's centered + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u'", + uid, den->uid); + + // print the the denominator + den->output(); + +#ifdef USE_Z + printf(DELIMITER_CHAR); +#else + // back again + printf("\\h'-\\n[" WIDTH_FORMAT "]u'", den->uid); + printf("\\h'-(\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u)'", + uid, den->uid); + // up again + printf("\\v'-\\n[" SUB_LOWER_FORMAT "]u'", uid); +#endif + if (reduce_size) + printf("\\s[\\n[" SIZE_FORMAT "]]", uid); + // draw the line + printf("\\h'%dM'", null_delimiter_space); + printf("\\v'-%dM'", axis_height); + fputs(draw_flag ? "\\D'l" : "\\l'", stdout); + printf("\\n[" WIDTH_FORMAT "]u-%dM", + uid, 2*null_delimiter_space); + fputs(draw_flag ? " 0'" : "\\&\\(ru'", stdout); + printf("\\v'%dM'", axis_height); + printf("\\h'%dM'", null_delimiter_space); +} + +void over_box::debug_print() +{ + fprintf(stderr, "{ "); + num->debug_print(); + if (reduce_size) + fprintf(stderr, " } smallover { "); + else + fprintf(stderr, " } over { "); + den->debug_print(); + fprintf(stderr, " }"); +} + +void over_box::check_tabs(int level) +{ + num->check_tabs(level + 1); + den->check_tabs(level + 1); +} diff --git a/contrib/groff/src/preproc/eqn/pbox.h b/contrib/groff/src/preproc/eqn/pbox.h new file mode 100644 index 0000000..d1f16ac --- /dev/null +++ b/contrib/groff/src/preproc/eqn/pbox.h @@ -0,0 +1,141 @@ +// -*- 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. */ + +extern int fat_offset; + +extern int over_hang; +extern int accent_width; + +extern int delimiter_factor; +extern int delimiter_shortfall; + +extern int null_delimiter_space; +extern int script_space; +extern int thin_space; +extern int medium_space; +extern int thick_space; + +extern int num1; +extern int num2; +// we don't use num3, because we don't have \atop +extern int denom1; +extern int denom2; +extern int axis_height; +extern int sup1; +extern int sup2; +extern int sup3; +extern int default_rule_thickness; +extern int sub1; +extern int sub2; +extern int sup_drop; +extern int sub_drop; +extern int x_height; +extern int big_op_spacing1; +extern int big_op_spacing2; +extern int big_op_spacing3; +extern int big_op_spacing4; +extern int big_op_spacing5; + +extern int baseline_sep; +extern int shift_down; +extern int column_sep; +extern int matrix_side_sep; + +// ms.eqn relies on this! + +#define LINE_STRING "10" +#define MARK_OR_LINEUP_FLAG_REG "MK" + +#define WIDTH_FORMAT PREFIX "w%d" +#define HEIGHT_FORMAT PREFIX "h%d" +#define DEPTH_FORMAT PREFIX "d%d" +#define TOTAL_FORMAT PREFIX "t%d" +#define SIZE_FORMAT PREFIX "z%d" +#define SMALL_SIZE_FORMAT PREFIX "Z%d" +#define SUP_RAISE_FORMAT PREFIX "p%d" +#define SUB_LOWER_FORMAT PREFIX "b%d" +#define SUB_KERN_FORMAT PREFIX "k%d" +#define FONT_FORMAT PREFIX "f%d" +#define SKEW_FORMAT PREFIX "s%d" +#define LEFT_WIDTH_FORMAT PREFIX "lw%d" +#define LEFT_DELIM_STRING_FORMAT PREFIX "l%d" +#define RIGHT_DELIM_STRING_FORMAT PREFIX "r%d" +#define SQRT_STRING_FORMAT PREFIX "sqr%d" +#define SQRT_WIDTH_FORMAT PREFIX "sq%d" +#define BASELINE_SEP_FORMAT PREFIX "bs%d" +// this needs two parameters, the uid and the column index +#define COLUMN_WIDTH_FORMAT PREFIX "cw%d,%d" + +#define BAR_STRING PREFIX "sqb" +#define TEMP_REG PREFIX "temp" +#define MARK_REG PREFIX "mark" +#define MARK_WIDTH_REG PREFIX "mwidth" +#define SAVED_MARK_REG PREFIX "smark" +#define MAX_SIZE_REG PREFIX "mxsz" +#define REPEAT_APPEND_STRING_MACRO PREFIX "ras" +#define TOP_HEIGHT_REG PREFIX "th" +#define TOP_DEPTH_REG PREFIX "td" +#define MID_HEIGHT_REG PREFIX "mh" +#define MID_DEPTH_REG PREFIX "md" +#define BOT_HEIGHT_REG PREFIX "bh" +#define BOT_DEPTH_REG PREFIX "bd" +#define EXT_HEIGHT_REG PREFIX "eh" +#define EXT_DEPTH_REG PREFIX "ed" +#define TOTAL_HEIGHT_REG PREFIX "tot" +#define DELTA_REG PREFIX "delta" +#define DELIM_STRING PREFIX "delim" +#define DELIM_WIDTH_REG PREFIX "dwidth" +#define SAVED_FONT_REG PREFIX "sfont" +#define SAVED_PREV_FONT_REG PREFIX "spfont" +#define SAVED_INLINE_FONT_REG PREFIX "sifont" +#define SAVED_INLINE_PREV_FONT_REG PREFIX "sipfont" +#define SAVED_SIZE_REG PREFIX "ssize" +#define SAVED_INLINE_SIZE_REG PREFIX "sisize" +#define SAVED_INLINE_PREV_SIZE_REG PREFIX "sipsize" +#define SAVE_FONT_STRING PREFIX "sfont" +#define RESTORE_FONT_STRING PREFIX "rfont" +#define INDEX_REG PREFIX "i" +#define TEMP_MACRO PREFIX "tempmac" + +#define DELIMITER_CHAR "\\(EQ" + +const int CRAMPED_SCRIPT_STYLE = 0; +const int SCRIPT_STYLE = 1; +const int CRAMPED_DISPLAY_STYLE = 2; +const int DISPLAY_STYLE = 3; + +extern int script_style(int); +extern int cramped_style(int); + +const int ORDINARY_TYPE = 0; +const int OPERATOR_TYPE = 1; +const int BINARY_TYPE = 2; +const int RELATION_TYPE = 3; +const int OPENING_TYPE = 4; +const int CLOSING_TYPE = 5; +const int PUNCTUATION_TYPE = 6; +const int INNER_TYPE = 7; +const int SUPPRESS_TYPE = 8; + +void set_script_size(); + +enum { HINT_PREV_IS_ITALIC = 01, HINT_NEXT_IS_ITALIC = 02 }; + +extern const char *current_roman_font; diff --git a/contrib/groff/src/preproc/eqn/pile.cc b/contrib/groff/src/preproc/eqn/pile.cc new file mode 100644 index 0000000..0df5241 --- /dev/null +++ b/contrib/groff/src/preproc/eqn/pile.cc @@ -0,0 +1,293 @@ +// -*- 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. */ +// piles and matrices + +#include "eqn.h" +#include "pbox.h" + +// SUP_RAISE_FORMAT gives the first baseline +// BASELINE_SEP_FORMAT gives the separation between baselines + +int pile_box::compute_metrics(int style) +{ + int i; + for (i = 0; i < col.len; i++) + col.p[i]->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0", uid); + for (i = 0; i < col.len; i++) + printf(">?\\n[" WIDTH_FORMAT "]", col.p[i]->uid); + printf("\n"); + printf(".nr " BASELINE_SEP_FORMAT " %dM", + uid, baseline_sep+col.space); + for (i = 1; i < col.len; i++) + printf(">?(\\n[" DEPTH_FORMAT "]+\\n[" HEIGHT_FORMAT "]+%dM)", + col.p[i-1]->uid, col.p[i]->uid, default_rule_thickness*5); + // round it so that it's a multiple of the vertical resolution + printf("/\\n(.V+(\\n(.V/2)*\\n(.V\n"); + + printf(".nr " SUP_RAISE_FORMAT " \\n[" BASELINE_SEP_FORMAT "]*%d/2" + "+%dM\n", + uid, uid, col.len-1, axis_height - shift_down); + printf(".nr " HEIGHT_FORMAT " \\n[" SUP_RAISE_FORMAT "]+\\n[" + HEIGHT_FORMAT "]\n", + uid, uid, col.p[0]->uid); + printf(".nr " DEPTH_FORMAT " \\n[" BASELINE_SEP_FORMAT "]*%d+\\n[" + DEPTH_FORMAT "]-\\n[" SUP_RAISE_FORMAT "]\n", + uid, uid, col.len-1, col.p[col.len-1]->uid, uid); + return FOUND_NOTHING; +} + +void pile_box::output() +{ + int i; + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + for (i = 0; i < col.len; i++) { + switch (col.align) { + case LEFT_ALIGN: + break; + case CENTER_ALIGN: + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u'", + uid, col.p[i]->uid); + break; + case RIGHT_ALIGN: + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u'", + uid, col.p[i]->uid); + break; + default: + assert(0); + } + col.p[i]->output(); + printf("\\h'-\\n[" WIDTH_FORMAT "]u'", col.p[i]->uid); + switch (col.align) { + case LEFT_ALIGN: + break; + case CENTER_ALIGN: + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u'", + col.p[i]->uid, uid); + break; + case RIGHT_ALIGN: + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u'", + col.p[i]->uid, uid); + break; + default: + assert(0); + } + if (i != col.len - 1) + printf("\\v'\\n[" BASELINE_SEP_FORMAT "]u'", uid); + } + printf("\\v'\\n[" SUP_RAISE_FORMAT "]u'", uid); + printf("\\v'-(%du*\\n[" BASELINE_SEP_FORMAT "]u)'", col.len - 1, uid); + printf("\\h'\\n[" WIDTH_FORMAT "]u'", uid); +} + +pile_box::pile_box(box *pp) : col(pp) +{ +} + +void pile_box::check_tabs(int level) +{ + col.list_check_tabs(level); +} + +void pile_box::debug_print() +{ + col.debug_print("pile"); +} + +int matrix_box::compute_metrics(int style) +{ + int i, j; + int maxlen = 0; + int space = 0; + for (i = 0; i < len; i++) { + for (j = 0; j < p[i]->len; j++) + p[i]->p[j]->compute_metrics(style); + if (p[i]->len > maxlen) + maxlen = p[i]->len; + if (p[i]->space > space) + space = p[i]->space; + } + for (i = 0; i < len; i++) { + printf(".nr " COLUMN_WIDTH_FORMAT " 0", uid, i); + for (j = 0; j < p[i]->len; j++) + printf(">?\\n[" WIDTH_FORMAT "]", p[i]->p[j]->uid); + printf("\n"); + } + printf(".nr " WIDTH_FORMAT " %dM", + uid, column_sep*(len-1)+2*matrix_side_sep); + for (i = 0; i < len; i++) + printf("+\\n[" COLUMN_WIDTH_FORMAT "]", uid, i); + printf("\n"); + printf(".nr " BASELINE_SEP_FORMAT " %dM", + uid, baseline_sep+space); + for (i = 0; i < len; i++) + for (j = 1; j < p[i]->len; j++) + printf(">?(\\n[" DEPTH_FORMAT "]+\\n[" HEIGHT_FORMAT "]+%dM)", + p[i]->p[j-1]->uid, p[i]->p[j]->uid, default_rule_thickness*5); + // round it so that it's a multiple of the vertical resolution + printf("/\\n(.V+(\\n(.V/2)*\\n(.V\n"); + printf(".nr " SUP_RAISE_FORMAT " \\n[" BASELINE_SEP_FORMAT "]*%d/2" + "+%dM\n", + uid, uid, maxlen-1, axis_height - shift_down); + printf(".nr " HEIGHT_FORMAT " 0\\n[" SUP_RAISE_FORMAT "]+(0", + uid, uid); + for (i = 0; i < len; i++) + printf(">?\\n[" HEIGHT_FORMAT "]", p[i]->p[0]->uid); + printf(")>?0\n"); + printf(".nr " DEPTH_FORMAT " \\n[" BASELINE_SEP_FORMAT "]*%d-\\n[" + SUP_RAISE_FORMAT "]+(0", + uid, uid, maxlen-1, uid); + for (i = 0; i < len; i++) + if (p[i]->len == maxlen) + printf(">?\\n[" DEPTH_FORMAT "]", p[i]->p[maxlen-1]->uid); + printf(")>?0\n"); + return FOUND_NOTHING; +} + +void matrix_box::output() +{ + printf("\\h'%dM'", matrix_side_sep); + for (int i = 0; i < len; i++) { + int j; + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + for (j = 0; j < p[i]->len; j++) { + switch (p[i]->align) { + case LEFT_ALIGN: + break; + case CENTER_ALIGN: + printf("\\h'\\n[" COLUMN_WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u'", + uid, i, p[i]->p[j]->uid); + break; + case RIGHT_ALIGN: + printf("\\h'\\n[" COLUMN_WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u'", + uid, i, p[i]->p[j]->uid); + break; + default: + assert(0); + } + p[i]->p[j]->output(); + printf("\\h'-\\n[" WIDTH_FORMAT "]u'", p[i]->p[j]->uid); + switch (p[i]->align) { + case LEFT_ALIGN: + break; + case CENTER_ALIGN: + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" COLUMN_WIDTH_FORMAT "]u/2u'", + p[i]->p[j]->uid, uid, i); + break; + case RIGHT_ALIGN: + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" COLUMN_WIDTH_FORMAT "]u'", + p[i]->p[j]->uid, uid, i); + break; + default: + assert(0); + } + if (j != p[i]->len - 1) + printf("\\v'\\n[" BASELINE_SEP_FORMAT "]u'", uid); + } + printf("\\v'\\n[" SUP_RAISE_FORMAT "]u'", uid); + printf("\\v'-(%du*\\n[" BASELINE_SEP_FORMAT "]u)'", p[i]->len - 1, uid); + printf("\\h'\\n[" COLUMN_WIDTH_FORMAT "]u'", uid, i); + if (i != len - 1) + printf("\\h'%dM'", column_sep); + } + printf("\\h'%dM'", matrix_side_sep); +} + +matrix_box::matrix_box(column *pp) +{ + p = new column*[10]; + for (int i = 0; i < 10; i++) + p[i] = 0; + maxlen = 10; + len = 1; + p[0] = pp; +} + +matrix_box::~matrix_box() +{ + for (int i = 0; i < len; i++) + delete p[i]; + a_delete p; +} + +void matrix_box::append(column *pp) +{ + if (len + 1 > maxlen) { + column **oldp = p; + maxlen *= 2; + p = new column*[maxlen]; + memcpy(p, oldp, sizeof(column*)*len); + a_delete oldp; + } + p[len++] = pp; +} + +void matrix_box::check_tabs(int level) +{ + for (int i = 0; i < len; i++) + p[i]->list_check_tabs(level); +} + +void matrix_box::debug_print() +{ + fprintf(stderr, "matrix { "); + p[0]->debug_print("col"); + for (int i = 1; i < len; i++) { + fprintf(stderr, " "); + p[i]->debug_print("col"); + } + fprintf(stderr, " }"); +} + +column::column(box *pp) : box_list(pp), align(CENTER_ALIGN), space(0) +{ +} + +void column::set_alignment(alignment a) +{ + align = a; +} + +void column::set_space(int n) +{ + space = n; +} + +void column::debug_print(const char *s) +{ + char c = '\0'; // shut up -Wall + switch (align) { + case LEFT_ALIGN: + c = 'l'; + break; + case RIGHT_ALIGN: + c = 'r'; + break; + case CENTER_ALIGN: + c = 'c'; + break; + default: + assert(0); + } + fprintf(stderr, "%c%s %d { ", c, s, space); + list_debug_print(" above "); + fprintf(stderr, " }"); +} + diff --git a/contrib/groff/src/preproc/eqn/script.cc b/contrib/groff/src/preproc/eqn/script.cc new file mode 100644 index 0000000..7c2e6c2 --- /dev/null +++ b/contrib/groff/src/preproc/eqn/script.cc @@ -0,0 +1,221 @@ +// -*- 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 "eqn.h" +#include "pbox.h" + +class script_box : public pointer_box { +private: + box *sub; + box *sup; +public: + script_box(box *, box *, box *); + ~script_box(); + int compute_metrics(int); + void output(); + void debug_print(); + int left_is_italic(); + void hint(unsigned); + void check_tabs(int); +}; + +/* The idea is that the script should attach to the rightmost box +of a list. For example, given `2x sup 3', the superscript should +attach to `x' rather than `2x'. */ + +box *make_script_box(box *nuc, box *sub, box *sup) +{ + list_box *b = nuc->to_list_box(); + if (b != 0) { + b->list.p[b->list.len-1] = make_script_box(b->list.p[b->list.len - 1], + sub, + sup); + return b; + } + else + return new script_box(nuc, sub, sup); +} + +script_box::script_box(box *pp, box *qq, box *rr) +: pointer_box(pp), sub(qq), sup(rr) +{ +} + +script_box::~script_box() +{ + delete sub; + delete sup; +} + +int script_box::left_is_italic() +{ + return p->left_is_italic(); +} + +int script_box::compute_metrics(int style) +{ + int res = p->compute_metrics(style); + p->compute_subscript_kern(); + printf(".nr " SIZE_FORMAT " \\n[.s]\n", uid); + if (!(style <= SCRIPT_STYLE && one_size_reduction_flag)) + set_script_size(); + printf(".nr " SMALL_SIZE_FORMAT " \\n[.s]\n", uid); + if (sub != 0) + sub->compute_metrics(cramped_style(script_style(style))); + if (sup != 0) + sup->compute_metrics(script_style(style)); + // 18a + if (p->is_char()) { + printf(".nr " SUP_RAISE_FORMAT " 0\n", uid); + printf(".nr " SUB_LOWER_FORMAT " 0\n", uid); + } + else { + printf(".nr " SUP_RAISE_FORMAT " \\n[" HEIGHT_FORMAT "]-%dM>?0\n", + uid, p->uid, sup_drop); + printf(".nr " SUB_LOWER_FORMAT " \\n[" DEPTH_FORMAT "]+%dM\n", + uid, p->uid, sub_drop); + } + printf(".ps \\n[" SIZE_FORMAT "]\n", uid); + if (sup == 0) { + assert(sub != 0); + // 18b + printf(".nr " SUB_LOWER_FORMAT " \\n[" SUB_LOWER_FORMAT "]>?%dM>?(\\n[" + HEIGHT_FORMAT "]-(%dM*4/5))\n", + uid, uid, sub1, sub->uid, x_height); + } + else { + // sup != 0 + // 18c + int p; + if (style == DISPLAY_STYLE) + p = sup1; + else if (style & 1) // not cramped + p = sup2; + else + p = sup3; + printf(".nr " SUP_RAISE_FORMAT " \\n[" SUP_RAISE_FORMAT + "]>?%dM>?(\\n[" DEPTH_FORMAT "]+(%dM/4))\n", + uid, uid, p, sup->uid, x_height); + // 18d + if (sub != 0) { + printf(".nr " SUB_LOWER_FORMAT " \\n[" SUB_LOWER_FORMAT "]>?%dM\n", + uid, uid, sub2); + // 18e + printf(".nr " TEMP_REG " \\n[" DEPTH_FORMAT "]-\\n[" + SUP_RAISE_FORMAT "]+\\n[" HEIGHT_FORMAT "]-\\n[" + SUB_LOWER_FORMAT "]+(4*%dM)\n", + sup->uid, uid, sub->uid, uid, default_rule_thickness); + printf(".if \\n[" TEMP_REG "] \\{"); + printf(".nr " SUB_LOWER_FORMAT " +\\n[" TEMP_REG "]\n", uid); + printf(".nr " TEMP_REG " (%dM*4/5)-\\n[" SUP_RAISE_FORMAT + "]+\\n[" DEPTH_FORMAT "]>?0\n", + x_height, uid, sup->uid); + printf(".nr " SUP_RAISE_FORMAT " +\\n[" TEMP_REG "]\n", uid); + printf(".nr " SUB_LOWER_FORMAT " -\\n[" TEMP_REG "]\n", uid); + printf(".\\}\n"); + } + } + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]", uid, p->uid); + if (sub != 0 && sup != 0) + printf("+((\\n[" WIDTH_FORMAT "]-\\n[" SUB_KERN_FORMAT "]>?\\n[" + WIDTH_FORMAT "])+%dM)>?0\n", + sub->uid, p->uid, sup->uid, script_space); + else if (sub != 0) + printf("+(\\n[" WIDTH_FORMAT "]-\\n[" SUB_KERN_FORMAT "]+%dM)>?0\n", + sub->uid, p->uid, script_space); + else if (sup != 0) + printf("+(\\n[" WIDTH_FORMAT "]+%dM)>?0\n", sup->uid, script_space); + else + printf("\n"); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]", + uid, p->uid); + if (sup != 0) + printf(">?(\\n[" SUP_RAISE_FORMAT "]+\\n[" HEIGHT_FORMAT "])", + uid, sup->uid); + if (sub != 0) + printf(">?(-\\n[" SUB_LOWER_FORMAT "]+\\n[" HEIGHT_FORMAT "])", + uid, sub->uid); + printf("\n"); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]", + uid, p->uid); + if (sub != 0) + printf(">?(\\n[" SUB_LOWER_FORMAT "]+\\n[" DEPTH_FORMAT "])", + uid, sub->uid); + if (sup != 0) + printf(">?(-\\n[" SUP_RAISE_FORMAT "]+\\n[" DEPTH_FORMAT "])", + uid, sup->uid); + printf("\n"); + return res; +} + +void script_box::output() +{ + p->output(); + if (sup != 0) { + printf("\\Z" DELIMITER_CHAR); + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + printf("\\s[\\n[" SMALL_SIZE_FORMAT "]]", uid); + sup->output(); + printf("\\s[\\n[" SIZE_FORMAT "]]", uid); + printf(DELIMITER_CHAR); + } + if (sub != 0) { + printf("\\Z" DELIMITER_CHAR); + printf("\\v'\\n[" SUB_LOWER_FORMAT "]u'", uid); + printf("\\s[\\n[" SMALL_SIZE_FORMAT "]]", uid); + printf("\\h'-\\n[" SUB_KERN_FORMAT "]u'", p->uid); + sub->output(); + printf("\\s[\\n[" SIZE_FORMAT "]]", uid); + printf(DELIMITER_CHAR); + } + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u'", + uid, p->uid); +} + +void script_box::hint(unsigned flags) +{ + p->hint(flags & ~HINT_NEXT_IS_ITALIC); +} + +void script_box::debug_print() +{ + fprintf(stderr, "{ "); + p->debug_print(); + fprintf(stderr, " }"); + if (sub) { + fprintf(stderr, " sub { "); + sub->debug_print(); + fprintf(stderr, " }"); + } + if (sup) { + fprintf(stderr, " sup { "); + sup->debug_print(); + fprintf(stderr, " }"); + } +} + +void script_box::check_tabs(int level) +{ + if (sup) + sup->check_tabs(level + 1); + if (sub) + sub->check_tabs(level + 1); + p->check_tabs(level); +} diff --git a/contrib/groff/src/preproc/eqn/special.cc b/contrib/groff/src/preproc/eqn/special.cc new file mode 100644 index 0000000..310261a --- /dev/null +++ b/contrib/groff/src/preproc/eqn/special.cc @@ -0,0 +1,115 @@ +// -*- 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 "eqn.h" +#include "pbox.h" + +#define STRING_FORMAT PREFIX "str%d" + +#define SPECIAL_STRING "0s" +#define SPECIAL_WIDTH_REG "0w" +#define SPECIAL_HEIGHT_REG "0h" +#define SPECIAL_DEPTH_REG "0d" +#define SPECIAL_SUB_KERN_REG "0skern" +#define SPECIAL_SKEW_REG "0skew" + +/* +For example: + +.de Cl +.ds 0s \Z'\\*[0s]'\v'\\n(0du'\D'l \\n(0wu -\\n(0hu-\\n(0du'\v'\\n(0hu' +.. +.EQ +define cancel 'special Cl' +.EN +*/ + + +class special_box : public pointer_box { + char *macro_name; +public: + special_box(char *, box *); + ~special_box(); + int compute_metrics(int); + void compute_subscript_kern(); + void compute_skew(); + void output(); + void debug_print(); +}; + +box *make_special_box(char *s, box *p) +{ + return new special_box(s, p); +} + +special_box::special_box(char *s, box *pp) : pointer_box(pp), macro_name(s) +{ +} + +special_box::~special_box() +{ + a_delete macro_name; +} + +int special_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + p->compute_subscript_kern(); + p->compute_skew(); + printf(".ds " SPECIAL_STRING " \""); + p->output(); + printf("\n"); + printf(".nr " SPECIAL_WIDTH_REG " 0\\n[" WIDTH_FORMAT "]\n", p->uid); + printf(".nr " SPECIAL_HEIGHT_REG " \\n[" HEIGHT_FORMAT "]\n", p->uid); + printf(".nr " SPECIAL_DEPTH_REG " \\n[" DEPTH_FORMAT "]\n", p->uid); + printf(".nr " SPECIAL_SUB_KERN_REG " \\n[" SUB_KERN_FORMAT "]\n", p->uid); + printf(".nr " SPECIAL_SKEW_REG " 0\\n[" SKEW_FORMAT "]\n", p->uid); + printf(".%s\n", macro_name); + printf(".rn " SPECIAL_STRING " " STRING_FORMAT "\n", uid); + printf(".nr " WIDTH_FORMAT " 0\\n[" SPECIAL_WIDTH_REG "]\n", uid); + printf(".nr " HEIGHT_FORMAT " 0>?\\n[" SPECIAL_HEIGHT_REG "]\n", uid); + printf(".nr " DEPTH_FORMAT " 0>?\\n[" SPECIAL_DEPTH_REG "]\n", uid); + printf(".nr " SUB_KERN_FORMAT " 0>?\\n[" SPECIAL_SUB_KERN_REG "]\n", uid); + printf(".nr " SKEW_FORMAT " 0\\n[" SPECIAL_SKEW_REG "]\n", uid); + // User will have to change MARK_REG if appropriate. + return r; +} + +void special_box::compute_subscript_kern() +{ + // Already computed in compute_metrics(), so do nothing. +} + +void special_box::compute_skew() +{ + // Already computed in compute_metrics(), so do nothing. +} + +void special_box::output() +{ + printf("\\*[" STRING_FORMAT "]", uid); +} + +void special_box::debug_print() +{ + fprintf(stderr, "special %s { ", macro_name); + p->debug_print(); + fprintf(stderr, " }"); +} diff --git a/contrib/groff/src/preproc/eqn/sqrt.cc b/contrib/groff/src/preproc/eqn/sqrt.cc new file mode 100644 index 0000000..6109ffe --- /dev/null +++ b/contrib/groff/src/preproc/eqn/sqrt.cc @@ -0,0 +1,179 @@ +// -*- 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 "eqn.h" +#include "pbox.h" + + +class sqrt_box : public pointer_box { +public: + sqrt_box(box *); + int compute_metrics(int style); + void output(); + void debug_print(); + void check_tabs(int); +}; + +box *make_sqrt_box(box *pp) +{ + return new sqrt_box(pp); +} + +sqrt_box::sqrt_box(box *pp) : pointer_box(pp) +{ +} + +#define SQRT_CHAR "\\(sr" +#define RADICAL_EXTENSION_CHAR "\\[radicalex]" + +#define SQRT_CHAIN "\\[sr\\\\n[" INDEX_REG "]]" +#define BAR_CHAIN "\\[radicalex\\\\n[" INDEX_REG "]]" + +int sqrt_box::compute_metrics(int style) +{ + // 11 + int r = p->compute_metrics(cramped_style(style)); + printf(".nr " TEMP_REG " \\n[" HEIGHT_FORMAT "]+\\n[" DEPTH_FORMAT + "]+%dM+(%dM/4)\n", + p->uid, p->uid, default_rule_thickness, + (style > SCRIPT_STYLE ? x_height : default_rule_thickness)); + printf(".nr " SIZE_FORMAT " \\n[.s]\n", uid); + printf(".ds " SQRT_STRING_FORMAT " " SQRT_CHAR "\n", uid); + printf(".ds " BAR_STRING " " RADICAL_EXTENSION_CHAR "\n"); + printf(".nr " SQRT_WIDTH_FORMAT + " 0\\w" DELIMITER_CHAR SQRT_CHAR DELIMITER_CHAR "\n", + uid); + printf(".if \\n[rst]-\\n[rsb]-%dM<\\n[" TEMP_REG "] \\{", + default_rule_thickness); + + printf(".nr " INDEX_REG " 0\n" + ".de " TEMP_MACRO "\n" + ".ie c" SQRT_CHAIN " \\{" + ".ds " SQRT_STRING_FORMAT " " SQRT_CHAIN "\n" + ".ie c" BAR_CHAIN " .ds " BAR_STRING " " BAR_CHAIN "\n" + ".el .ds " BAR_STRING " " RADICAL_EXTENSION_CHAR "\n" + ".nr " SQRT_WIDTH_FORMAT + " 0\\w" DELIMITER_CHAR SQRT_CHAIN DELIMITER_CHAR "\n" + ".if \\\\n[rst]-\\\\n[rsb]-%dM<\\n[" TEMP_REG "] \\{" + ".nr " INDEX_REG " +1\n" + "." TEMP_MACRO "\n" + ".\\}\\}\n" + ".el .nr " INDEX_REG " 0-1\n" + "..\n" + "." TEMP_MACRO "\n", + uid, uid, default_rule_thickness); + + printf(".if \\n[" INDEX_REG "]<0 \\{"); + + // Determine the maximum point size + printf(".ps 1000\n"); + printf(".nr " MAX_SIZE_REG " \\n[.s]\n"); + printf(".ps \\n[" SIZE_FORMAT "]\n", uid); + // We define a macro that will increase the current point size + // until we get a radical sign that's tall enough or we reach + // the maximum point size. + printf(".de " TEMP_MACRO "\n" + ".nr " SQRT_WIDTH_FORMAT + " 0\\w" DELIMITER_CHAR "\\*[" SQRT_STRING_FORMAT "]" DELIMITER_CHAR "\n" + ".if \\\\n[rst]-\\\\n[rsb]-%dM<\\n[" TEMP_REG "]" + "&(\\\\n[.s]<\\n[" MAX_SIZE_REG "]) \\{" + ".ps +1\n" + "." TEMP_MACRO "\n" + ".\\}\n" + "..\n" + "." TEMP_MACRO "\n", + uid, uid, default_rule_thickness); + + printf(".\\}\\}\n"); + + printf(".nr " SMALL_SIZE_FORMAT " \\n[.s]\n", uid); + // set TEMP_REG to the amount by which the radical sign is too big + printf(".nr " TEMP_REG " \\n[rst]-\\n[rsb]-%dM-\\n[" TEMP_REG "]\n", + default_rule_thickness); + // If TEMP_REG is negative, the bottom of the radical sign should + // be -TEMP_REG above the bottom of p. If it's positive, the bottom + // of the radical sign should be TEMP_REG/2 below the bottom of p. + // This calculates the amount by which the baseline of the radical + // should be raised. + printf(".nr " SUP_RAISE_FORMAT " (-\\n[" TEMP_REG "]>?(-\\n[" TEMP_REG "]/2))" + "-\\n[rsb]-\\n[" DEPTH_FORMAT "]\n", uid, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]" + ">?(\\n[" SUP_RAISE_FORMAT "]+\\n[rst])\n", + uid, p->uid, uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]" + ">?(-\\n[" SUP_RAISE_FORMAT "]-\\n[rsb])\n", + uid, p->uid, uid); + // Do this last, so we don't lose height and depth information on + // the radical sign. + // Remember that the width of the bar might be greater than the width of p. + + printf(".nr " TEMP_REG " " + "\\n[" WIDTH_FORMAT "]" + ">?\\w" DELIMITER_CHAR "\\*[" BAR_STRING "]" DELIMITER_CHAR "\n", + p->uid); + printf(".as " SQRT_STRING_FORMAT " " + "\\l'\\n[" TEMP_REG "]u\\&\\*[" BAR_STRING "]'\n", + uid); + printf(".nr " WIDTH_FORMAT " \\n[" TEMP_REG "]" + "+\\n[" SQRT_WIDTH_FORMAT "]\n", + uid, uid); + + if (r) + printf(".nr " MARK_REG " +\\n[" SQRT_WIDTH_FORMAT "]\n", uid); + // the top of the bar might be higher than the top of the radical sign + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]" + ">?(\\n[" SUP_RAISE_FORMAT "]+\\n[rst])\n", + uid, p->uid, uid); + // put a bit of extra space above the bar + printf(".nr " HEIGHT_FORMAT " +%dM\n", uid, default_rule_thickness); + printf(".ps \\n[" SIZE_FORMAT "]\n", uid); + return r; +} + +void sqrt_box::output() +{ + printf("\\Z" DELIMITER_CHAR); + printf("\\s[\\n[" SMALL_SIZE_FORMAT "]]", uid); + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + printf("\\*[" SQRT_STRING_FORMAT "]", uid); + printf("\\s[\\n[" SIZE_FORMAT "]]", uid); + printf(DELIMITER_CHAR); + + printf("\\Z" DELIMITER_CHAR); + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u" + "+\\n[" SQRT_WIDTH_FORMAT "]u/2u'", + uid, p->uid, uid); + p->output(); + printf(DELIMITER_CHAR); + + printf("\\h'\\n[" WIDTH_FORMAT "]u'", uid); +} + +void sqrt_box::debug_print() +{ + fprintf(stderr, "sqrt { "); + p->debug_print(); + fprintf(stderr, " }"); +} + +void sqrt_box::check_tabs(int level) +{ + p->check_tabs(level + 1); +} diff --git a/contrib/groff/src/preproc/eqn/text.cc b/contrib/groff/src/preproc/eqn/text.cc new file mode 100644 index 0000000..b0f1700 --- /dev/null +++ b/contrib/groff/src/preproc/eqn/text.cc @@ -0,0 +1,528 @@ +// -*- 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 "eqn.h" +#include "pbox.h" +#include "ptable.h" + +class char_box : public simple_box { + unsigned char c; + char next_is_italic; + char prev_is_italic; +public: + char_box(unsigned char); + void debug_print(); + void output(); + int is_char(); + int left_is_italic(); + int right_is_italic(); + void hint(unsigned); + void handle_char_type(int, int); +}; + +class special_char_box : public simple_box { + char *s; +public: + special_char_box(const char *); + ~special_char_box(); + void output(); + void debug_print(); + int is_char(); + void handle_char_type(int, int); +}; + +const char *spacing_type_table[] = { + "ordinary", + "operator", + "binary", + "relation", + "opening", + "closing", + "punctuation", + "inner", + "suppress", + 0, +}; + +const int DIGIT_TYPE = 0; +const int LETTER_TYPE = 1; + +const char *font_type_table[] = { + "digit", + "letter", + 0, +}; + +struct char_info { + int spacing_type; + int font_type; + char_info(); +}; + +char_info::char_info() +: spacing_type(ORDINARY_TYPE), font_type(DIGIT_TYPE) +{ +} + +static char_info char_table[256]; + +declare_ptable(char_info) +implement_ptable(char_info) + +PTABLE(char_info) special_char_table; + +static int get_special_char_spacing_type(const char *ch) +{ + char_info *p = special_char_table.lookup(ch); + return p ? p->spacing_type : ORDINARY_TYPE; +} + +static int get_special_char_font_type(const char *ch) +{ + char_info *p = special_char_table.lookup(ch); + return p ? p->font_type : DIGIT_TYPE; +} + +static void set_special_char_type(const char *ch, int st, int ft) +{ + char_info *p = special_char_table.lookup(ch); + if (!p) { + p = new char_info; + special_char_table.define(ch, p); + } + if (st >= 0) + p->spacing_type = st; + if (ft >= 0) + p->font_type = ft; +} + +void init_char_table() +{ + set_special_char_type("pl", 2, -1); // binary + set_special_char_type("mi", 2, -1); + set_special_char_type("eq", 3, -1); // relation + set_special_char_type("<=", 3, -1); + set_special_char_type(">=", 3, -1); + char_table['}'].spacing_type = 5; // closing + char_table[')'].spacing_type = 5; + char_table[']'].spacing_type = 5; + char_table['{'].spacing_type = 4; // opening + char_table['('].spacing_type = 4; + char_table['['].spacing_type = 4; + char_table[','].spacing_type = 6; // punctuation + char_table[';'].spacing_type = 6; + char_table[':'].spacing_type = 6; + char_table['.'].spacing_type = 6; + char_table['>'].spacing_type = 3; + char_table['<'].spacing_type = 3; + char_table['*'].spacing_type = 2; // binary + for (int i = 0; i < 256; i++) + if (csalpha(i)) + char_table[i].font_type = LETTER_TYPE; +} + +static int lookup_spacing_type(const char *type) +{ + for (int i = 0; spacing_type_table[i] != 0; i++) + if (strcmp(spacing_type_table[i], type) == 0) + return i; + return -1; +} + +static int lookup_font_type(const char *type) +{ + for (int i = 0; font_type_table[i] != 0; i++) + if (strcmp(font_type_table[i], type) == 0) + return i; + return -1; +} + +void box::set_spacing_type(char *type) +{ + int t = lookup_spacing_type(type); + if (t < 0) + error("unrecognised type `%1'", type); + else + spacing_type = t; + a_delete type; +} + +char_box::char_box(unsigned char cc) +: c(cc), next_is_italic(0), prev_is_italic(0) +{ + spacing_type = char_table[c].spacing_type; +} + +void char_box::hint(unsigned flags) +{ + if (flags & HINT_PREV_IS_ITALIC) + prev_is_italic = 1; + if (flags & HINT_NEXT_IS_ITALIC) + next_is_italic = 1; +} + +void char_box::output() +{ + int font_type = char_table[c].font_type; + if (font_type != LETTER_TYPE) + printf("\\f[%s]", current_roman_font); + if (!prev_is_italic) + fputs("\\,", stdout); + if (c == '\\') + fputs("\\e", stdout); + else + putchar(c); + if (!next_is_italic) + fputs("\\/", stdout); + else + fputs("\\&", stdout); // suppress ligaturing and kerning + if (font_type != LETTER_TYPE) + fputs("\\fP", stdout); +} + +int char_box::left_is_italic() +{ + int font_type = char_table[c].font_type; + return font_type == LETTER_TYPE; +} + +int char_box::right_is_italic() +{ + int font_type = char_table[c].font_type; + return font_type == LETTER_TYPE; +} + +int char_box::is_char() +{ + return 1; +} + +void char_box::debug_print() +{ + if (c == '\\') { + putc('\\', stderr); + putc('\\', stderr); + } + else + putc(c, stderr); +} + +special_char_box::special_char_box(const char *t) +{ + s = strsave(t); + spacing_type = get_special_char_spacing_type(s); +} + +special_char_box::~special_char_box() +{ + a_delete s; +} + +void special_char_box::output() +{ + int font_type = get_special_char_font_type(s); + if (font_type != LETTER_TYPE) + printf("\\f[%s]", current_roman_font); + printf("\\,\\[%s]\\/", s); + if (font_type != LETTER_TYPE) + printf("\\fP"); +} + +int special_char_box::is_char() +{ + return 1; +} + +void special_char_box::debug_print() +{ + fprintf(stderr, "\\[%s]", s); +} + + +void char_box::handle_char_type(int st, int ft) +{ + if (st >= 0) + char_table[c].spacing_type = st; + if (ft >= 0) + char_table[c].font_type = ft; +} + +void special_char_box::handle_char_type(int st, int ft) +{ + set_special_char_type(s, st, ft); +} + +void set_char_type(const char *type, char *ch) +{ + assert(ch != 0); + int st = lookup_spacing_type(type); + int ft = lookup_font_type(type); + if (st < 0 && ft < 0) { + error("bad character type `%1'", type); + a_delete ch; + return; + } + box *b = split_text(ch); + b->handle_char_type(st, ft); + delete b; +} + +/* We give primes special treatment so that in ``x' sub 2'', the ``2'' +will be tucked under the prime */ + +class prime_box : public pointer_box { + box *pb; +public: + prime_box(box *); + ~prime_box(); + int compute_metrics(int style); + void output(); + void compute_subscript_kern(); + void debug_print(); + void handle_char_type(int, int); +}; + +box *make_prime_box(box *pp) +{ + return new prime_box(pp); +} + +prime_box::prime_box(box *pp) : pointer_box(pp) +{ + pb = new special_char_box("fm"); +} + +prime_box::~prime_box() +{ + delete pb; +} + +int prime_box::compute_metrics(int style) +{ + int res = p->compute_metrics(style); + pb->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]" + "+\\n[" WIDTH_FORMAT "]\n", + uid, p->uid, pb->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]" + ">?\\n[" HEIGHT_FORMAT "]\n", + uid, p->uid, pb->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]" + ">?\\n[" DEPTH_FORMAT "]\n", + uid, p->uid, pb->uid); + return res; +} + +void prime_box::compute_subscript_kern() +{ + p->compute_subscript_kern(); + printf(".nr " SUB_KERN_FORMAT " 0\\n[" WIDTH_FORMAT "]" + "+\\n[" SUB_KERN_FORMAT "]>?0\n", + uid, pb->uid, p->uid); +} + +void prime_box::output() +{ + p->output(); + pb->output(); +} + +void prime_box::handle_char_type(int st, int ft) +{ + p->handle_char_type(st, ft); + pb->handle_char_type(st, ft); +} + +void prime_box::debug_print() +{ + p->debug_print(); + putc('\'', stderr); +} + +box *split_text(char *text) +{ + list_box *lb = 0; + box *fb = 0; + char *s = text; + while (*s != '\0') { + char c = *s++; + box *b = 0; + switch (c) { + case '+': + b = new special_char_box("pl"); + break; + case '-': + b = new special_char_box("mi"); + break; + case '=': + b = new special_char_box("eq"); + break; + case '\'': + b = new special_char_box("fm"); + break; + case '<': + if (*s == '=') { + b = new special_char_box("<="); + s++; + break; + } + goto normal_char; + case '>': + if (*s == '=') { + b = new special_char_box(">="); + s++; + break; + } + goto normal_char; + case '\\': + if (*s == '\0') { + lex_error("bad escape"); + break; + } + c = *s++; + switch (c) { + case '(': + { + char buf[3]; + if (*s != '\0') { + buf[0] = *s++; + if (*s != '\0') { + buf[1] = *s++; + buf[2] = '\0'; + b = new special_char_box(buf); + } + else { + lex_error("bad escape"); + } + } + else { + lex_error("bad escape"); + } + } + break; + case '[': + { + char *ch = s; + while (*s != ']' && *s != '\0') + s++; + if (*s == '\0') + lex_error("bad escape"); + else { + *s++ = '\0'; + b = new special_char_box(ch); + } + } + break; + case 'f': + case 'g': + case 'k': + case 'n': + case '*': + { + char *escape_start = s - 2; + switch (*s) { + case '(': + if (*++s != '\0') + ++s; + break; + case '[': + for (++s; *s != '\0' && *s != ']'; s++) + ; + break; + } + if (*s == '\0') + lex_error("bad escape"); + else { + ++s; + char *buf = new char[s - escape_start + 1]; + memcpy(buf, escape_start, s - escape_start); + buf[s - escape_start] = '\0'; + b = new quoted_text_box(buf); + } + } + break; + case '-': + case '_': + { + char buf[2]; + buf[0] = c; + buf[1] = '\0'; + b = new special_char_box(buf); + } + break; + case '`': + b = new special_char_box("ga"); + break; + case '\'': + b = new special_char_box("aa"); + break; + case 'e': + case '\\': + b = new char_box('\\'); + break; + case '^': + case '|': + case '0': + { + char buf[3]; + buf[0] = '\\'; + buf[1] = c; + buf[2] = '\0'; + b = new quoted_text_box(strsave(buf)); + break; + } + default: + lex_error("unquoted escape"); + b = new quoted_text_box(strsave(s - 2)); + s = strchr(s, '\0'); + break; + } + break; + default: + normal_char: + b = new char_box(c); + break; + } + while (*s == '\'') { + if (b == 0) + b = new quoted_text_box(0); + b = new prime_box(b); + s++; + } + if (b != 0) { + if (lb != 0) + lb->append(b); + else if (fb != 0) { + lb = new list_box(fb); + lb->append(b); + } + else + fb = b; + } + } + delete text; + if (lb != 0) + return lb; + else if (fb != 0) + return fb; + else + return new quoted_text_box(0); +} + diff --git a/contrib/groff/src/preproc/grn/Makefile.sub b/contrib/groff/src/preproc/grn/Makefile.sub new file mode 100644 index 0000000..ffa0ad2 --- /dev/null +++ b/contrib/groff/src/preproc/grn/Makefile.sub @@ -0,0 +1,17 @@ +PROG=grn +MAN1=grn.n +MLIB=$(LIBM) +XLIBS=$(LIBGROFF) +OBJS=\ + hdb.o \ + hpoint.o \ + hgraph.o \ + main.o +CCSRCS=\ + $(srcdir)/hdb.cc \ + $(srcdir)/hpoint.cc \ + $(srcdir)/hgraph.cc \ + $(srcdir)/main.cc +HDRS=\ + $(srcdir)/gprint.h +NAMEPREFIX=$(g) diff --git a/contrib/groff/src/preproc/grn/README b/contrib/groff/src/preproc/grn/README new file mode 100644 index 0000000..b5b9fc9 --- /dev/null +++ b/contrib/groff/src/preproc/grn/README @@ -0,0 +1,60 @@ +This is grn from the Berkeley ditroff distribution. It has no +AT&T code and is therefore freely distributable. + +Tim Theisen <tim@cs.wisc.edu> + +===================================================================== + +This is the modified code for the groff. It uses the different +devxxx format that is ascii rather than binary as in the +Berkeley distribution. Since groff does not have the \Ds option +for line drawing (dotted, dashed, etc.), this version includes +the routines for drawing curves and arcs, so it does not use the +\D~, \Da nor \Dc. Although also included in here is a routine +for drawing the optional gremlin style curves, it is not used +because the gremlin editor uses the conventional spline +algorithm. The Berkeley grn has the choice of different +stipples. Here, only different shades of gray will be painted +depending on the gremlin file. It is possible to upgrade this at +a later time. (Daniel Senderowicz <daniel@synchrods.com> 12/28/99) + +===================================================================== + +It has been further modified by Werner Lemberg <wl@gnu.org> to fit +better into the groff package. + + . Replaced Makefile with Makefile.sub. + + . Removed dev.h since it is unused. + + . Renamed grn.1 to grn.man; this man page has been extensively + revised. + + . Used error() and fatal() from libgroff for all source files. + + . Renamed *.c to *.cc; updates as needed for C++ (prototypes, proper + casts, standard header files etc). Heavy formatting. + + . main.cc: + + Using groff's default values instead of DEVDIR, DEFAULTDEV, PRINTER, + TYPESETTER, and GREMLIB. + + `res' is now an integer. + + Added `-C' command flag (for compatibility mode) as with other + preprocessors. + + Added `-F' and `-v' option (similar to troff). + + Renamed `-L' option to `-M' for consistence. + + Removed `-P' option. + + Using font::load_desc() for scanning DESC files. + + Removed SYSV-specific code. + + Using macro_path.open_file() for getting gremlin graphic files. + + Added usage(). diff --git a/contrib/groff/src/preproc/grn/gprint.h b/contrib/groff/src/preproc/grn/gprint.h new file mode 100644 index 0000000..b25305b --- /dev/null +++ b/contrib/groff/src/preproc/grn/gprint.h @@ -0,0 +1,84 @@ +/* Last non-groff version: gprint.h 1.1 84/10/08 + * + * This file contains standard definitions used by the gprint program. + */ + +#include <stdio.h> +#include <math.h> + + +#define xorn(x,y) (x) + /* was 512 */ +#define yorn(x,y) (511 - (y)) /* switch direction for */ + /* y-coordinates */ + +#define STYLES 6 +#define SIZES 4 +#define FONTS 4 +#define SOLID -1 +#define DOTTED 004 /* 014 */ +#define DASHED 020 /* 034 */ +#define DOTDASHED 024 /* 054 */ +#define LONGDASHED 074 + +#define DEFTHICK -1 /* default thicknes */ +#define DEFSTYLE SOLID /* default line style */ + +#define TRUE 1 +#define FALSE 0 + +#define nullelt -1 +#define nullpt -1 +#define nullun NULL + +#define BOTLEFT 0 +#define BOTRIGHT 1 +#define CENTCENT 2 +#define VECTOR 3 +#define ARC 4 +#define CURVE 5 +#define POLYGON 6 +#define TOPLEFT 10 +#define TOPCENT 11 +#define TOPRIGHT 12 +#define CENTLEFT 13 +#define CENTRIGHT 14 +#define BOTCENT 15 +#define TEXT(t) ( (t <= CENTCENT) || (t >= TOPLEFT) ) + +/* WARNING * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING + * The above (TEXT) test is dependent on the relative values of the + * constants and will have to change if these values change or if new + * commands are added with value greater than BOTCENT + */ + +#define NUSER 4 +#define NFONTS 4 +#define NBRUSHES 6 +#define NSIZES 4 +#define NJUSTS 9 +#define NSTIPPLES 16 + +#define ADD 1 +#define DELETE 2 +#define MOD 3 + +typedef struct point { + float x, y; + struct point *nextpt; +} POINT; + +typedef struct elmt { + int type, brushf, size, textlength; + char *textpt; + POINT *ptlist; + struct elmt *nextelt, *setnext; +} ELT; + +#define DBNextElt(elt) (elt->nextelt) +#define DBNextofSet(elt) (elt->setnext) +#define DBNullelt(elt) (elt == NULL) +#define Nullpoint(pt) ((pt) == (POINT *) NULL) +#define PTNextPoint(pt) (pt->nextpt) + +/* EOF */ diff --git a/contrib/groff/src/preproc/grn/grn.man b/contrib/groff/src/preproc/grn/grn.man new file mode 100644 index 0000000..f2613da --- /dev/null +++ b/contrib/groff/src/preproc/grn/grn.man @@ -0,0 +1,636 @@ +.ig \"-*- nroff -*- +Copyright (C) 2000 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the Free Software Foundation instead of in +the original English. +.. +.de TQ +.br +.ns +.TP \\$1 +.. +.\" Like TP, but if specified indent is more than half +.\" the current line-length - indent, use the default indent. +.de Tp +.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP +.el .TP "\\$1" +.. +.TH @G@GRN @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" +.SH NAME +@g@grn \- groff preprocessor for gremlin files +.SH SYNOPSIS +.BR @g@grn +[ +.B \-Cv +] +[ +.BI \-T dev +] +[ +.BI \-M dir +] +[ +.BI \-F dir +] +[ +.IR file\.\.\. +] +.PP +It is possible to have whitespace between a command line option and its +parameter. +.SH DESCRIPTION +.I @g@grn +is a preprocessor for including +.I gremlin +pictures in +.I groff +input. +.I @g@grn +writes to standard output, processing only input lines between two that +start with +.B .GS +and +.BR .GE. +Those lines must contain +.I @g@grn +commands (see below). +These commands request a +.I gremlin +file, and the picture in that file is +converted and placed in the +.I @g@troff +input stream. +The +.B .GS +request may be followed by a C, L, or R to center, left, or right +justify the whole +.I gremlin +picture (default justification is center). +If no +.I file +is mentioned, the standard input is read. +At the end of the picture, the position on the page is the bottom of the +.I gremlin +picture. +If the +.I @g@grn +entry is ended with +.B .GF +instead of +.BR .GE , +the position is left at the top of the picture. +.PP +Please note that currently only the \-me macro package has support for +.BR .GS , +.BR .GE , +and +.BR .GF . +.PP +The following command-line options are understood: +.TP +.BI \-T dev +Prepare output for printer +.IR dev . +The default device is +.BR @DEVICE@ . +See +.BR groff (@MAN1EXT@) +for acceptable devices. +.TP +.BI \-M dir +Prepend +.I dir +to the default search path for +.I gremlin +files. +The default path is (in that order) the current directory, the home +directory, +.BR @SYSTEMMACRODIR@ , +.BR @LOCALMACRODIR@ , +and +.BR @MACRODIR@ . +.TP +.BI \-F dir +Search +.I dir +for subdirectories +.BI dev name +.RI ( name +is the name of the device) for the +.B DESC +file before the normal +.BR @FONTDIR@ . +.TP +.B \-C +Recognize +.B .GS +and +.B .GE +(resp. +.BR .GF ) +even when followed by a character other than space or newline. +.\".TP +.\".B \-s +.\"This switch causes the picture to be traversed twice: +.\"The first time, only the interiors of filled polygons (as borderless +.\"polygons) are printed. +.\"The second time, the outline is printed as a series of line segments. +.\"This way, postprocessors that overwrite rather than merge picture elements +.\"(such as Postscript) can still have text and graphics on a shaded +.\"background. +.TP +.B \-v +Print the version number. +.SH GRN COMMANDS +Each input line between +.B .GS +and +.B .GE +may have one +.I @g@grn +command. +Commands consist of one or two strings separated by white space, the first +string being the command and the second its operand. +Commands may be upper or lower case and abbreviated down to one character. +.PP +Commands that affect a picture's environment (those listed before +.BR default , +see below) are only in effect for the current picture: +The environment is reinitialized to the defaults at the start of the next +picture. +The commands are as follows: +.TP +.BI 1\ N +.TQ +.BI 2\ N +.TQ +.BI 3\ N +.TQ +.BI 4\ N +Set +.IR gremlin 's +text size number 1 (2, 3, or 4) to +.I N +points. +The default is 12 (resp. 16, 24, and 36). +.TP +.BI roman\ f +.TQ +.BI italics\ f +.TQ +.BI bold\ f +.TQ +.BI special\ f +Set the roman (italics, bold, or special) font to +.IR @g@troff 's +font +.I f +(either a name or number). +The default is R (resp. I, B, and S). +.TP +.BI l\ f +.TQ +.BI stipple\ f +Set the stipple font to +.IR @g@troff 's +stipple font +.I f +(name or number). +The command +.B stipple +may be abbreviated down as far as `st' (to avoid +confusion with +.BR special ). +There is +.I no +default for stipples (unless one is set by the default command), and it is +illegal to include a +.I gremlin +picture with polygons without specifying a +stipple font. +.TP +.BI x\ N +.TQ +.BI scale\ N +Magnify the picture (in addition to any default magnification) by +.IR N , +a floating point number larger than zero. +The command +.B scale +may be abbreviated down to `sc'. +.TP +.BI narrow\ N +.TQ +.BI medium\ N +.TQ +.BI thick\ N +Set the thickness of +.IR gremlin 's +narrow (resp. medium and thick) lines to +.I N +times 0.15pt (this value can be changed at compile time). +The default is 1.0 (resp. 3.0 and 5.0), which corresponds to 0.15pt +(resp. 0.45pt and 0.75pt). +A thickness value of zero selects the smallest available line thickness. +Negative values cause the line thickness to be proportional to the current +point size. +.TP +.BI pointscale\ <off/on> +Scale text to match the picture. +Gremlin text is usually printed in the point size specified with the +commands +.BR 1 ,\ 2 ,\ 3 ,\ or\ 4 +regardless of any scaling factors in the picture. +Setting +.B pointscale +will cause the point sizes to scale with the picture (within +.IR @g@troff 's +limitations, of course). +An operand of anything but +.I off +will turn text scaling on. +.TP +.B default +Reset the picture environment defaults to the settings in the current +picture. +This is meant to be used as a global parameter setting mechanism at the +beginning of the +.I @g@troff +input file, but can be used at any time to reset the +default settings. +.TP +.BI width\ N +Forces the picture to be +.I N +inches wide. +This overrides any scaling factors present in the same picture. +.RB ` width +.IR 0 ' +is ignored. +.TP +.BI height\ N +Forces picture to be +.I N +inches high, overriding other scaling factors. +If both `width' and `height' are specified the tighter constraint will +determine the scale of the picture. +.B Height +and +.B width +commands are not saved with a +.B default +command. +They will, however, affect point size scaling if that option is set. +.TP +.BI file\ name +Get picture from +.I gremlin +file +.I name +located the current directory (or in the library directory; see the +.B \-M +option above). +If two +.B file +commands are given, the second one overrides the first. +If +.I name +doesn't exist, an error message is reported and processing continues from +the +.B .GE +line. +.SH NOTES ABOUT GROFF +Since +.I @g@grn +is a preprocessor, it doesn't know about current indents, point sizes, +margins, number registers, etc. +Consequently, no +.I @g@troff +input can be placed between the +.B .GS +and +.B .GE +requests. +However, +.I gremlin +text is now processed by +.IR @g@troff , +so anything legal in a single line of +.I @g@troff +input is legal in a line of +.I gremlin +text (barring `.' directives at the beginning of a line). +Thus, it is possible to have equations within a +.I gremlin +figure by including in the +.I gremlin +file +.I eqn +expressions enclosed by previously defined delimiters (e.g. +.IR $$ ). +.PP +When using +.I @g@grn +along with other preprocessors, it is best to run +.I tbl +before +.IR @g@grn , +.IR pic , +and/or +.I ideal +to avoid overworking +.IR tbl . +.I Eqn +should always be run last. +.PP +A picture is considered an entity, but that doesn't stop +.I @g@troff +from trying to break it up if it falls off the end of a page. +Placing the picture between `keeps' in \-me macros will ensure proper +placement. +.PP +.I @g@grn +uses +.IR @g@troff 's +number registers +.B g1 +through +.B g9 +and sets registers +.B g1 +and +.B g2 +to the width and height of the +.I gremlin +figure (in device units) before entering the +.B .GS +request (this is for those who want to rewrite these macros). +.SH GREMLIN FILE FORMAT +There exist two distinct +.I gremlin +file formats, the original format from the +.I AED +graphic terminal version, and the +.I SUN +or +.I X11 +version. +An extension to the +.IR SUN / X11 +version allowing reference points with negative coordinates is +.B not +compatible with the +.I AED +version. +As long as a +.I gremlin +file does not contain negative coordinates, either format will be read +correctly by either version of +.I gremlin +or +.IR @g@grn . +The other difference to the +.IR SUN / X11 +format is the use of names for picture objects (e.g., POLYGON, CURVE) +instead of numbers. +Files representing the same picture are shown in Table 1 in each format. +.sp +.DS +.TS +center, tab(@); +l lw(0.1i) l. +sungremlinfile@@gremlinfile +0 240.00 128.00@@0 240.00 128.00 +CENTCENT@@2 +240.00 128.00@@240.00 128.00 +185.00 120.00@@185.00 120.00 +240.00 120.00@@240.00 120.00 +296.00 120.00@@296.00 120.00 +*@@-1.00 -1.00 +2 3@@2 3 +10 A Triangle@@10 A Triangle +POLYGON@@6 +224.00 416.00@@224.00 416.00 +96.00 160.00@@96.00 160.00 +384.00 160.00@@384.00 160.00 +*@@-1.00 -1.00 +5 1@@5 1 +0@@0 +-1@@-1 +.T& +css. +.sp +Table 1. File examples +.TE +.DE +.sp +.IP \(bu +The first line of each +.I gremlin +file contains either the string +.B gremlinfile +.RI ( AED +version) or +.B sungremlinfile +.RI ( SUN / X11 ) +.IP \(bu +The second line of the file contains an orientation, and +.B x +and +.B y +values for a positioning point, separated by spaces. +The orientation, either +.B 0 +or +.BR 1 , +is ignored by the +.IR SUN / X11 +version. +.B 0 +means that +.I gremlin +will display things in horizontal format (drawing area wider than it is +tall, with menu across top). +.B 1 +means that +.I gremlin +will display things in vertical format (drawing area taller than it is wide, +with menu on left side). +.B x +and +.B y +are floating point values giving a positioning point to be used when this +file is read into another file. +The stuff on this line really isn't all that important; a value of ``1 0.00 +0.00'' is suggested. +.IP \(bu +The rest of the file consists of zero or more element specifications. +After the last element specification is a line containing the string ``-1''. +.SH ELEMENT SPECIFICATIONS +.IP \(bu +The first line of each element contains a single decimal number giving the +type of the element +.RI ( AED +version) or its ASCII name +.RI ( SUN / X11 +version). +See Table 2. +.sp +.DS +.TS +center, tab(@); +css +ccc +nll. +\fIgremlin\fP File Format \(mi Object Type Specification +.sp +\fIAED\fP Number@\fISUN\fP/\fIX11\fP Name@Description +0@BOTLEFT@bottom-left-justified text +1@BOTRIGHT@bottom-right-justified text +2@CENTCENT@center-justified text +3@VECTOR@vector +4@ARC@arc +5@CURVE@curve +6@POLYGON@polygon +10@TOPLEFT@top-left-justified text +11@TOPCENT@top-center-justified text +12@TOPRIGHT@top-right-justified text +13@CENTLEFT@left-center-justified text +14@CENTRIGHT@right-center-justified text +15@BOTCENT@bottom-center-justified text +.T& +css. +.sp +Table 2. +Type Specifications in \fIgremlin\fP Files +.TE +.DE +.sp +.IP \(bu +After the object type comes a variable number of lines, each specifying a +point used to display the element. +Each line contains an x-coordinate and a y-coordinate in floating point +format, separated by spaces. +The list of points is terminated by a line containing the string ``-1.0 +-1.0'' +.RI ( AED +version) or a single asterisk, ``*'' +.RI ( SUN / X11 +version). +.IP \(bu +After the points comes a line containing two decimal values, giving the +brush and size for the element. +The brush determines the style in which things are drawn. +For vectors, arcs, and curves there are six legal brush values: +.sp +.DS +.TS +center, tab(@); +ncw(0.1i)l. +1 \(mi@@thin dotted lines +2 \(mi@@thin dot-dashed lines +3 \(mi@@thick solid lines +4 \(mi@@thin dashed lines +5 \(mi@@thin solid lines +6 \(mi@@medium solid lines +.TE +.DE +.sp +For polygons, one more value, 0, is legal. +It specifies a polygon with an invisible border. +For text, the brush selects a font as follows: +.sp +.DS +.TS +center, tab(@); +ncw(0.1i)l. +1 \(mi@@roman (R font in groff) +2 \(mi@@italics (I font in groff) +3 \(mi@@bold (B font in groff) +4 \(mi@@special (S font in groff) +.TE +.DE +.sp +If you're using +.I @g@grn +to run your pictures through +.IR groff , +the font is really just a starting font: +The text string can contain formatting sequences like +``\\fI'' +or +``\\d'' +which may change the font (as well as do many other things). +For text, the size field is a decimal value between 1 and 4. +It selects the size of the font in which the text will be drawn. +For polygons, this size field is interpreted as a stipple number to fill the +polygon with. +The number is used to index into a stipple font at print time. +.IP \(bu +The last line of each element contains a decimal number and a string of +characters, separated by a single space. +The number is a count of the number of characters in the string. +This information is only used for text elements, and contains the text +string. +There can be spaces inside the text. +For arcs, curves, and vectors, this line of the element contains the string +``0''. +.SH NOTES ON COORDINATES +.I gremlin +was designed for +.IR AED s, +and its coordinates reflect the +.I AED +coordinate space. +For vertical pictures, x-values range 116 to 511, and y-values from 0 to +483. +For horizontal pictures, x-values range from 0 to 511 and y-values range +from 0 to 367. +Although you needn't absolutely stick to this range, you'll get best results +if you at least stay in this vicinity. +Also, point lists are terminated by a point of (-1, -1), so you shouldn't +ever use negative coordinates. +.I gremlin +writes out coordinates using format ``%f1.2''; it's probably a good idea to +use the same format if you want to modify the +.I @g@grn +code. +.SH NOTES ON SUN/X11 COORDINATES +There is no longer a restriction on the range of coordinates used to create +objects in the +.IR SUN / X11 +version of +.IR gremlin . +However, files with negative coordinates +.B will +cause problems if displayed on the +.IR AED . +.SH FILES +.Tp \w'@FONTDIR@/devname/DESC'u+3n +.BI @FONTDIR@/dev name /DESC +Device description file for device +.IR name . +.SH SEE ALSO +.BR gremlin (1), +.BR groff (@MAN1EXT@), +.BR @g@pic (@MAN1EXT@), +.BR ideal (1) +.SH HISTORY +.PP +David Slattengren and Barry Roitblat wrote the original Berkeley +.IR @g@grn . +.PP +Daniel Senderowicz and Werner Lemberg modified it for +.IR groff . diff --git a/contrib/groff/src/preproc/grn/hdb.cc b/contrib/groff/src/preproc/grn/hdb.cc new file mode 100644 index 0000000..fd5bb48 --- /dev/null +++ b/contrib/groff/src/preproc/grn/hdb.cc @@ -0,0 +1,326 @@ +/* Last non-groff version: hdb.c 1.8 (Berkeley) 84/10/20 + * + * Copyright -C- 1982 Barry S. Roitblat + * + * This file contains database routines for the hard copy programs of the + * gremlin picture editor. + */ + +#include "gprint.h" +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "errarg.h" +#include "error.h" + +#define MAXSTRING 128 + +/* imports from main.cc */ + +extern int linenum; /* current line number in input file */ +extern char gremlinfile[]; /* name of file currently reading */ +extern int SUNFILE; /* TRUE if SUN gremlin file */ +extern void savebounds(float x, float y); + +/* imports from hpoint.cc */ + +extern POINT *PTInit(); +extern POINT *PTMakePoint(float x, float y, POINT ** pplist); + + +int DBGetType(register char *s); + + +/* + * This routine returns a pointer to an initialized database element which + * would be the only element in an empty list. + */ +ELT * +DBInit() +{ + return ((ELT *) NULL); +} /* end DBInit */ + + +/* + * This routine creates a new element with the specified attributes and + * links it into database. + */ +ELT * +DBCreateElt(int type, + POINT * pointlist, + int brush, + int size, + char *text, + ELT **db) +{ + register ELT *temp; + + temp = (ELT *) malloc(sizeof(ELT)); + temp->nextelt = *db; + temp->type = type; + temp->ptlist = pointlist; + temp->brushf = brush; + temp->size = size; + temp->textpt = text; + *db = temp; + return (temp); +} /* end CreateElt */ + + +/* + * This routine reads the specified file into a database and returns a + * pointer to that database. + */ +ELT * +DBRead(register FILE *file) +{ + register int i; + register int done; /* flag for input exhausted */ + register float nx; /* x holder so x is not set before orienting */ + int type; /* element type */ + ELT *elist; /* pointer to the file's elements */ + POINT *plist; /* pointer for reading in points */ + char string[MAXSTRING], *txt; + float x, y; /* x and y are read in point coords */ + int len, brush, size; + int lastpoint; + + SUNFILE = FALSE; + elist = DBInit(); + (void) fscanf(file, "%s\n", string); + if (strcmp(string, "gremlinfile")) { + if (strcmp(string, "sungremlinfile")) { + error("`%1' is not a gremlin file", gremlinfile); + return (elist); + } + SUNFILE = TRUE; + } + + (void) fscanf(file, "%d%f%f\n", &size, &x, &y); + /* ignore orientation and file positioning point */ + + done = FALSE; + while (!done) { + /* if (fscanf(file,"%s\n", string) == EOF) */ + /* I changed the scanf format because the element */ + /* can have two words (e.g. CURVE SPLINE) */ + if (fscanf(file, "\n%[^\n]\n", string) == EOF) { + error("`%1', error in file format", gremlinfile); + return (elist); + } + + type = DBGetType(string); /* interpret element type */ + if (type < 0) { /* no more data */ + done = TRUE; + (void) fclose(file); + } else { +#ifdef UW_FASTSCAN + (void) xscanf(file, &x, &y); /* always one point */ +#else + (void) fscanf(file, "%f%f\n", &x, &y); /* always one point */ +#endif /* UW_FASTSCAN */ + plist = PTInit(); /* NULL point list */ + + /* + * Files created on the SUN have point lists terminated by a line + * containing only an asterik ('*'). Files created on the AED have + * point lists terminated by the coordinate pair (-1.00 -1.00). + */ + if (TEXT(type)) { /* read only first point for TEXT elements */ + nx = xorn(x, y); + y = yorn(x, y); + (void) PTMakePoint(nx, y, &plist); + savebounds(nx, y); + +#ifdef UW_FASTSCAN + while (xscanf(file, &x, &y)); +#else + lastpoint = FALSE; + do { + fgets(string, MAXSTRING, file); + if (string[0] == '*') { /* SUN gremlin file */ + lastpoint = TRUE; + } else { + (void) sscanf(string, "%f%f", &x, &y); + if ((x == -1.00 && y == -1.00) && (!SUNFILE)) + lastpoint = TRUE; + } + } while (!lastpoint); +#endif /* UW_FASTSCAN */ + } else { /* not TEXT element */ +#ifdef UW_FASTSCAN + do { + nx = xorn(x, y); + y = yorn(x, y); + (void) PTMakePoint(nx, y, &plist); + savebounds(nx, y); + } while (xscanf(file, &x, &y)); +#else + lastpoint = FALSE; + while (!lastpoint) { + nx = xorn(x, y); + y = yorn(x, y); + (void) PTMakePoint(nx, y, &plist); + savebounds(nx, y); + + fgets(string, MAXSTRING, file); + if (string[0] == '*') { /* SUN gremlin file */ + lastpoint = TRUE; + } else { + (void) sscanf(string, "%f%f", &x, &y); + if ((x == -1.00 && y == -1.00) && (!SUNFILE)) + lastpoint = TRUE; + } + } +#endif /* UW_FASTSCAN */ + } + (void) fscanf(file, "%d%d\n", &brush, &size); + (void) fscanf(file, "%d", &len); /* text length */ + (void) getc(file); /* eat blank */ + txt = (char *) malloc((unsigned) len + 1); + for (i = 0; i < len; ++i) { /* read text */ + txt[i] = getc(file); + } + txt[len] = '\0'; + (void) DBCreateElt(type, plist, brush, size, txt, &elist); + } /* end else */ + } /* end while not done */ ; + return (elist); +} /* end DBRead */ + + +/* + * Interpret element type in string s. + * Old file format consisted of integer element types. + * New file format has literal names for element types. + */ +int +DBGetType(register char *s) +{ + if (isdigit(s[0]) || (s[0] == '-')) /* old element format or EOF */ + return (atoi(s)); + + switch (s[0]) { + case 'P': + return (POLYGON); + case 'V': + return (VECTOR); + case 'A': + return (ARC); + case 'C': + if (s[1] == 'U') + return (CURVE); + switch (s[4]) { + case 'L': + return (CENTLEFT); + case 'C': + return (CENTCENT); + case 'R': + return (CENTRIGHT); + default: + fatal("unknown element type"); + } + case 'B': + switch (s[3]) { + case 'L': + return (BOTLEFT); + case 'C': + return (BOTCENT); + case 'R': + return (BOTRIGHT); + default: + fatal("unknown element type"); + } + case 'T': + switch (s[3]) { + case 'L': + return (TOPLEFT); + case 'C': + return (TOPCENT); + case 'R': + return (TOPRIGHT); + default: + fatal("unknown element type"); + } + default: + fatal("unknown element type"); + } + + return 0; /* never reached */ +} + +#ifdef UW_FASTSCAN +/* + * Optimization hack added by solomon@crys.wisc.edu, 12/2/86. + * A huge fraction of the time was spent reading floating point numbers from + * the input file, but the numbers always have the format 'ddd.dd'. Thus + * the following special-purpose version of fscanf. + * + * xscanf(f,xp,yp) does roughly what fscanf(f,"%f%f",xp,yp) does except: + * -the next piece of input must be of the form + * <space>* <digit>*'.'<digit>* <space>* <digit>*'.'<digit>* + * -xscanf eats the character following the second number + * -xscanf returns 0 for "end-of-data" indication, 1 otherwise, where + * end-of-data is signalled by a '*' [in which case the rest of the + * line is gobbled], or by '-1.00 -1.00' [but only if !SUNFILE]. + */ +int +xscanf(FILE *f, + float *xp, + float *yp) +{ + register int c, i, j, m, frac; + int iscale = 1, jscale = 1; /* x = i/scale, y=j/jscale */ + + while ((c = getc(f)) == ' '); + if (c == '*') { + while ((c = getc(f)) != '\n'); + return 0; + } + i = m = frac = 0; + while (isdigit(c) || c == '.' || c == '-') { + if (c == '-') { + m++; + c = getc(f); + continue; + } + if (c == '.') + frac = 1; + else { + if (frac) + iscale *= 10; + i = 10 * i + c - '0'; + } + c = getc(f); + } + if (m) + i = -i; + *xp = (double) i / (double) iscale; + + while ((c = getc(f)) == ' '); + j = m = frac = 0; + while (isdigit(c) || c == '.' || c == '-') { + if (c == '-') { + m++; + c = getc(f); + continue; + } + if (c == '.') + frac = 1; + else { + if (frac) + jscale *= 10; + j = 10 * j + c - '0'; + } + c = getc(f); + } + if (m) + j = -j; + *yp = (double) j / (double) jscale; + return (SUNFILE || i != -iscale || j != -jscale); +} +#endif /* UW_FASTSCAN */ + +/* EOF */ diff --git a/contrib/groff/src/preproc/grn/hgraph.cc b/contrib/groff/src/preproc/grn/hgraph.cc new file mode 100644 index 0000000..7963720 --- /dev/null +++ b/contrib/groff/src/preproc/grn/hgraph.cc @@ -0,0 +1,1043 @@ +/* Last non-groff version: hgraph.c 1.14 (Berkeley) 84/11/27 + * + * This file contains the graphics routines for converting gremlin pictures + * to troff input. + */ + +#include "gprint.h" + +#ifdef NEED_DECLARATION_HYPOT +extern "C" { + double hypot(double, double); +} +#endif /* NEED_DECLARATION_HYPOT */ + +#define MAXVECT 40 +#define MAXPOINTS 200 +#define LINELENGTH 1 +#define PointsPerInterval 64 +#define pi 3.14159265358979324 +#define twopi (2.0 * pi) +#define len(a, b) hypot((double)(b.x-a.x), (double)(b.y-a.y)) + + +extern int dotshifter; /* for the length of dotted curves */ + +extern int style[]; /* line and character styles */ +extern double thick[]; +extern char *tfont[]; +extern int tsize[]; +extern int stipple_index[]; /* stipple font index for stipples 0 - 16 */ +extern char *stipple; /* stipple type (cf or ug) */ + + +extern double troffscale; /* imports from main.c */ +extern double linethickness; +extern int linmod; +extern int lastx; +extern int lasty; +extern int lastyline; +extern int ytop; +extern int ybottom; +extern int xleft; +extern int xright; +extern enum { + OUTLINE, FILL, BOTH +} polyfill; + +extern double adj1; +extern double adj2; +extern double adj3; +extern double adj4; +extern int res; + +void HGSetFont(int font, int size); +void HGPutText(int justify, POINT pnt, register char *string); +void HGSetBrush(int mode); +void tmove2(int px, int py); +void doarc(POINT cp, POINT sp, int angle); +void tmove(POINT * ptr); +void cr(); +void drawwig(POINT * ptr); +void HGtline(int x1, int y1); +void dx(double x); +void dy(double y); +void HGArc(register int cx, register int cy, int px, int py, int angle); +void picurve(register int *x, register int *y, int npts); +void Paramaterize(int x[], int y[], float h[], int n); +void PeriodicSpline(float h[], int z[], + float dz[], float d2z[], float d3z[], + int npoints); +void NaturalEndSpline(float h[], int z[], + float dz[], float d2z[], float d3z[], + int npoints); + + + +/*----------------------------------------------------------------------------* + | Routine: HGPrintElt (element_pointer, baseline) + | + | Results: Examines a picture element and calls the appropriate + | routine(s) to print them according to their type. After the + | picture is drawn, current position is (lastx, lasty). + *----------------------------------------------------------------------------*/ + +void +HGPrintElt(ELT *element, + int baseline) +{ + register POINT *p1; + register POINT *p2; + register int length; + register int graylevel; + + if (!DBNullelt(element) && !Nullpoint((p1 = element->ptlist))) { + /* p1 always has first point */ + if (TEXT(element->type)) { + HGSetFont(element->brushf, element->size); + switch (element->size) { + case 1: + p1->y += adj1; + break; + case 2: + p1->y += adj2; + break; + case 3: + p1->y += adj3; + break; + case 4: + p1->y += adj4; + break; + default: + break; + } + HGPutText(element->type, *p1, element->textpt); + } else { + if (element->brushf) /* if there is a brush, the */ + HGSetBrush(element->brushf); /* graphics need it set */ + + switch (element->type) { + + case ARC: + p2 = PTNextPoint(p1); + tmove(p2); + doarc(*p1, *p2, element->size); + cr(); + break; + + case CURVE: + length = 0; /* keep track of line length */ + drawwig(p1); + cr(); + break; + + case VECTOR: + length = 0; /* keep track of line length so */ + tmove(p1); /* single lines don't get long */ + while (!Nullpoint((p1 = PTNextPoint(p1)))) { + HGtline((int) (p1->x * troffscale), + (int) (p1->y * troffscale)); + if (length++ > LINELENGTH) { + length = 0; + printf("\\\n"); + } + } /* end while */ + cr(); + break; + + case POLYGON: + { + /* brushf = style of outline; size = color of fill: + * on first pass (polyfill=FILL), do the interior using 'P' + * unless size=0 + * on second pass (polyfill=OUTLINE), do the outline using a series + * of vectors. It might make more sense to use \D'p ...', + * but there is no uniform way to specify a 'fill character' + * that prints as 'no fill' on all output devices (and + * stipple fonts). + * If polyfill=BOTH, just use the \D'p ...' command. + */ + float firstx = p1->x; + float firsty = p1->y; + + length = 0; /* keep track of line length so */ + /* single lines don't get long */ + + if (polyfill == FILL || polyfill == BOTH) { + /* do the interior */ + char command = (polyfill == BOTH && element->brushf) ? 'p' : 'P'; + + /* include outline, if there is one and */ + /* the -p flag was set */ + + /* switch based on what gremlin gives */ + switch (element->size) { + case 1: + graylevel = 1; + break; + case 3: + graylevel = 2; + break; + case 12: + graylevel = 3; + break; + case 14: + graylevel = 4; + break; + case 16: + graylevel = 5; + break; + case 19: + graylevel = 6; + break; + case 21: + graylevel = 7; + break; + case 23: + graylevel = 8; + break; + default: /* who's giving something else? */ + graylevel = NSTIPPLES; + break; + } + /* int graylevel = element->size; */ + + if (graylevel < 0) + break; + if (graylevel > NSTIPPLES) + graylevel = NSTIPPLES; + printf("\\h'-%du'\\D'f %du'", + stipple_index[graylevel], + stipple_index[graylevel]); + cr(); + tmove(p1); + printf("\\D'%c", command); + + while (!Nullpoint((PTNextPoint(p1)))) { + p1 = PTNextPoint(p1); + dx((double) p1->x); + dy((double) p1->y); + if (length++ > LINELENGTH) { + length = 0; + printf("\\\n"); + } + } /* end while */ + + /* close polygon if not done so by user */ + if ((firstx != p1->x) || (firsty != p1->y)) { + dx((double) firstx); + dy((double) firsty); + } + putchar('\''); + cr(); + break; + } + /* else polyfill == OUTLINE; only draw the outline */ + if (!(element->brushf)) + break; + length = 0; /* keep track of line length */ + tmove(p1); + + while (!Nullpoint((PTNextPoint(p1)))) { + p1 = PTNextPoint(p1); + HGtline((int) (p1->x * troffscale), + (int) (p1->y * troffscale)); + if (length++ > LINELENGTH) { + length = 0; + printf("\\\n"); + } + } /* end while */ + + /* close polygon if not done so by user */ + if ((firstx != p1->x) || (firsty != p1->y)) { + HGtline((int) (firstx * troffscale), + (int) (firsty * troffscale)); + } + cr(); + break; + } /* end case POLYGON */ + } /* end switch */ + } /* end else Text */ + } /* end if */ +} /* end PrintElt */ + + +/*----------------------------------------------------------------------------* + | Routine: HGPutText (justification, position_point, string) + | + | Results: Given the justification, a point to position with, and a + | string to put, HGPutText first sends the string into a + | diversion, moves to the positioning point, then outputs + | local vertical and horizontal motions as needed to justify + | the text. After all motions are done, the diversion is + | printed out. + *----------------------------------------------------------------------------*/ + +void +HGPutText(int justify, + POINT pnt, + register char *string) +{ + int savelasty = lasty; /* vertical motion for text is to be */ + /* ignored. Save current y here */ + + printf(".nr g8 \\n(.d\n"); /* save current vertical position. */ + printf(".ds g9 \""); /* define string containing the text. */ + while (*string) { /* put out the string */ + if (*string == '\\' && + *(string + 1) == '\\') { /* one character at a */ + printf("\\\\\\"); /* time replacing // */ + string++; /* by //// to prevent */ + } /* interpretation at */ + printf("%c", *(string++)); /* printout time */ + } + printf("\n"); + + tmove(&pnt); /* move to positioning point */ + + switch (justify) { + /* local vertical motions */ + /* (the numbers here are used to be somewhat compatible with gprint) */ + case CENTLEFT: + case CENTCENT: + case CENTRIGHT: + printf("\\v'0.85n'"); /* down half */ + break; + + case TOPLEFT: + case TOPCENT: + case TOPRIGHT: + printf("\\v'1.7n'"); /* down whole */ + } + + switch (justify) { + /* local horizontal motions */ + case BOTCENT: + case CENTCENT: + case TOPCENT: + printf("\\h'-\\w'\\*(g9'u/2u'"); /* back half */ + break; + + case BOTRIGHT: + case CENTRIGHT: + case TOPRIGHT: + printf("\\h'-\\w'\\*(g9'u'"); /* back whole */ + } + + printf("\\&\\*(g9\n"); /* now print the text. */ + printf(".sp |\\n(g8u\n"); /* restore vertical position */ + lasty = savelasty; /* vertical position restored to where it */ + lastx = xleft; /* was before text, also horizontal is at */ + /* left */ +} /* end HGPutText */ + + +/*----------------------------------------------------------------------------* + | Routine: doarc (center_point, start_point, angle) + | + | Results: Produces either drawarc command or a drawcircle command + | depending on the angle needed to draw through. + *----------------------------------------------------------------------------*/ + +void +doarc(POINT cp, + POINT sp, + int angle) +{ + if (angle) /* arc with angle */ + HGArc((int) (cp.x * troffscale), (int) (cp.y * troffscale), + (int) (sp.x * troffscale), (int) (sp.y * troffscale), angle); + else /* a full circle (angle == 0) */ + HGArc((int) (cp.x * troffscale), (int) (cp.y * troffscale), + (int) (sp.x * troffscale), (int) (sp.y * troffscale), 0); +} + + +/*----------------------------------------------------------------------------* + | Routine: HGSetFont (font_number, Point_size) + | + | Results: ALWAYS outputs a .ft and .ps directive to troff. This is + | done because someone may change stuff inside a text string. + | Changes thickness back to default thickness. Default + | thickness depends on font and pointsize. + *----------------------------------------------------------------------------*/ + +void +HGSetFont(int font, + int size) +{ + printf(".ft %s\n" + ".ps %d\n", tfont[font - 1], tsize[size - 1]); + linethickness = DEFTHICK; +} + + +/*----------------------------------------------------------------------------* + | Routine: HGSetBrush (line_mode) + | + | Results: Generates the troff commands to set up the line width and + | style of subsequent lines. Does nothing if no change is + | needed. + | + | Side Efct: Sets `linmode' and `linethicknes'. + *----------------------------------------------------------------------------*/ + +void +HGSetBrush(int mode) +{ + register int printed = 0; + + if (linmod != style[--mode]) { + /* Groff doesn't understand \Ds, so we take it out */ + /* printf ("\\D's %du'", linmod = style[mode]); */ + linmod = style[mode]; + printed = 1; + } + if (linethickness != thick[mode]) { + linethickness = thick[mode]; + printf("\\h'-%.2fp'\\D't %.2fp'", linethickness, linethickness); + printed = 1; + } + if (printed) + cr(); +} + + +/*----------------------------------------------------------------------------* + | Routine: dx (x_destination) + | + | Results: Scales and outputs a number for delta x (with a leading + | space) given `lastx' and x_destination. + | + | Side Efct: Resets `lastx' to x_destination. + *----------------------------------------------------------------------------*/ + +void +dx(double x) +{ + register int ix = (int) (x * troffscale); + + printf(" %du", ix - lastx); + lastx = ix; +} + + +/*----------------------------------------------------------------------------* + | Routine: dy (y_destination) + | + | Results: Scales and outputs a number for delta y (with a leading + | space) given `lastyline' and y_destination. + | + | Side Efct: Resets `lastyline' to y_destination. Since `line' vertical + | motions don't affect `page' ones, `lasty' isn't updated. + *----------------------------------------------------------------------------*/ + +void +dy(double y) +{ + register int iy = (int) (y * troffscale); + + printf(" %du", iy - lastyline); + lastyline = iy; +} + + +/*----------------------------------------------------------------------------* + | Routine: tmove2 (px, py) + | + | Results: Produces horizontal and vertical moves for troff given the + | pair of points to move to and knowing the current position. + | Also puts out a horizontal move to start the line. This is + | a variation without the .sp command. + *----------------------------------------------------------------------------*/ + +void +tmove2(int px, + int py) +{ + register int dx; + register int dy; + + if ((dy = py - lasty)) { + printf("\\v'%du'", dy); + } + lastyline = lasty = py; /* lasty is always set to current */ + if ((dx = px - lastx)) { + printf("\\h'%du'", dx); + lastx = px; + } +} + + +/*----------------------------------------------------------------------------* + | Routine: tmove (point_pointer) + | + | Results: Produces horizontal and vertical moves for troff given the + | pointer of a point to move to and knowing the current + | position. Also puts out a horizontal move to start the + | line. + *----------------------------------------------------------------------------*/ + +void +tmove(POINT * ptr) +{ + register int ix = (int) (ptr->x * troffscale); + register int iy = (int) (ptr->y * troffscale); + register int dx; + register int dy; + + if ((dy = iy - lasty)) { + printf(".sp %du\n", dy); + } + lastyline = lasty = iy; /* lasty is always set to current */ + if ((dx = ix - lastx)) { + printf("\\h'%du'", dx); + lastx = ix; + } +} + + +/*----------------------------------------------------------------------------* + | Routine: cr ( ) + | + | Results: Ends off an input line. `.sp -1' is also added to counteract + | the vertical move done at the end of text lines. + | + | Side Efct: Sets `lastx' to `xleft' for troff's return to left margin. + *----------------------------------------------------------------------------*/ + +void +cr() +{ + printf("\n.sp -1\n"); + lastx = xleft; +} + + +/*----------------------------------------------------------------------------* + | Routine: line ( ) + | + | Results: Draws a single solid line to (x,y). + *----------------------------------------------------------------------------*/ + +void +line(int px, + int py) +{ + printf("\\D'l"); + printf(" %du", px - lastx); + printf(" %du'", py - lastyline); + lastx = px; + lastyline = lasty = py; +} + + +/*---------------------------------------------------------------------------- + | Routine: drawwig (ptr) + | + | Results: The point sequence found in the structure pointed by ptr is + | placed in integer arrays for further manipulation by the + | existing routing. With the proper parameters, HGCurve is + | called. + *----------------------------------------------------------------------------*/ + +void +drawwig(POINT * ptr) +{ + register int npts; /* point list index */ + int x[MAXPOINTS], y[MAXPOINTS]; /* point list */ + + for (npts = 1; !Nullpoint(ptr); ptr = PTNextPoint(ptr), npts++) { + x[npts] = (int) (ptr->x * troffscale); + y[npts] = (int) (ptr->y * troffscale); + } + if (--npts) { + /* HGCurve(&x[0], &y[0], npts); */ /*Gremlin looks different, so... */ + picurve(&x[0], &y[0], npts); + } +} + + +/*---------------------------------------------------------------------------- + | Routine: HGArc (xcenter, ycenter, xstart, ystart, angle) + | + | Results: This routine plots an arc centered about (cx, cy) counter + | clockwise starting from the point (px, py) through `angle' + | degrees. If angle is 0, a full circle is drawn. It does so + | by creating a draw-path around the arc whose density of + | points depends on the size of the arc. + *----------------------------------------------------------------------------*/ + +void +HGArc(register int cx, + register int cy, + int px, + int py, + int angle) +{ + double xs, ys, resolution, fullcircle; + int m; + register int mask; + register int extent; + register int nx; + register int ny; + register int length; + register double epsilon; + + xs = px - cx; + ys = py - cy; + + length = 0; + + resolution = (1.0 + hypot(xs, ys) / res) * PointsPerInterval; + /* mask = (1 << (int) log10(resolution + 1.0)) - 1; */ + (void) frexp(resolution, &m); /* A bit more elegant than log10 */ + for (mask = 1; mask < m; mask = mask << 1); + mask -= 1; + + epsilon = 1.0 / resolution; + fullcircle = (2.0 * pi) * resolution; + if (angle == 0) + extent = (int) fullcircle; + else + extent = (int) (angle * fullcircle / 360.0); + + HGtline(px, py); + while (--extent >= 0) { + xs += epsilon * ys; + nx = cx + (int) (xs + 0.5); + ys -= epsilon * xs; + ny = cy + (int) (ys + 0.5); + if (!(extent & mask)) { + HGtline(nx, ny); /* put out a point on circle */ + if (length++ > LINELENGTH) { + length = 0; + printf("\\\n"); + } + } + } /* end for */ +} /* end HGArc */ + + +/*---------------------------------------------------------------------------- + | Routine: picurve (xpoints, ypoints, num_of_points) + | + | Results: Draws a curve delimited by (not through) the line segments + | traced by (xpoints, ypoints) point list. This is the `Pic' + | style curve. + *----------------------------------------------------------------------------*/ + +void +picurve(register int *x, + register int *y, + int npts) +{ + register int nseg; /* effective resolution for each curve */ + register int xp; /* current point (and temporary) */ + register int yp; + int pxp, pyp; /* previous point (to make lines from) */ + int i; /* inner curve segment traverser */ + int length = 0; + double w; /* position factor */ + double t1, t2, t3; /* calculation temps */ + + if (x[1] == x[npts] && y[1] == y[npts]) { + x[0] = x[npts - 1]; /* if the lines' ends meet, make */ + y[0] = y[npts - 1]; /* sure the curve meets */ + x[npts + 1] = x[2]; + y[npts + 1] = y[2]; + } else { /* otherwise, make the ends of the */ + x[0] = x[1]; /* curve touch the ending points of */ + y[0] = y[1]; /* the line segments */ + x[npts + 1] = x[npts]; + y[npts + 1] = y[npts]; + } + + pxp = (x[0] + x[1]) / 2; /* make the last point pointers */ + pyp = (y[0] + y[1]) / 2; /* point to the start of the 1st line */ + tmove2(pxp, pyp); + + for (; npts--; x++, y++) { /* traverse the line segments */ + xp = x[0] - x[1]; + yp = y[0] - y[1]; + nseg = (int) hypot((double) xp, (double) yp); + xp = x[1] - x[2]; + yp = y[1] - y[2]; + /* `nseg' is the number of line */ + /* segments that will be drawn for */ + /* each curve segment. */ + nseg = (int) ((double) (nseg + (int) hypot((double) xp, (double) yp)) / + res * PointsPerInterval); + + for (i = 1; i < nseg; i++) { + w = (double) i / (double) nseg; + t1 = w * w; + t3 = t1 + 1.0 - (w + w); + t2 = 2.0 - (t3 + t1); + xp = (((int) (t1 * x[2] + t2 * x[1] + t3 * x[0])) + 1) / 2; + yp = (((int) (t1 * y[2] + t2 * y[1] + t3 * y[0])) + 1) / 2; + + HGtline(xp, yp); + if (length++ > LINELENGTH) { + length = 0; + printf("\\\n"); + } + } + } +} + + +/*---------------------------------------------------------------------------- + | Routine: HGCurve(xpoints, ypoints, num_points) + | + | Results: This routine generates a smooth curve through a set of + | points. The method used is the parametric spline curve on + | unit knot mesh described in `Spline Curve Techniques' by + | Patrick Baudelaire, Robert Flegal, and Robert Sproull -- + | Xerox Parc. + *----------------------------------------------------------------------------*/ + +void +HGCurve(int *x, + int *y, + int numpoints) +{ + float h[MAXPOINTS], dx[MAXPOINTS], dy[MAXPOINTS]; + float d2x[MAXPOINTS], d2y[MAXPOINTS], d3x[MAXPOINTS], d3y[MAXPOINTS]; + float t, t2, t3; + register int j; + register int k; + register int nx; + register int ny; + int lx, ly; + int length = 0; + + lx = x[1]; + ly = y[1]; + tmove2(lx, ly); + + /* + * Solve for derivatives of the curve at each point separately for x and y + * (parametric). + */ + Paramaterize(x, y, h, numpoints); + + /* closed curve */ + if ((x[1] == x[numpoints]) && (y[1] == y[numpoints])) { + PeriodicSpline(h, x, dx, d2x, d3x, numpoints); + PeriodicSpline(h, y, dy, d2y, d3y, numpoints); + } else { + NaturalEndSpline(h, x, dx, d2x, d3x, numpoints); + NaturalEndSpline(h, y, dy, d2y, d3y, numpoints); + } + + /* + * generate the curve using the above information and PointsPerInterval + * vectors between each specified knot. + */ + + for (j = 1; j < numpoints; ++j) { + if ((x[j] == x[j + 1]) && (y[j] == y[j + 1])) + continue; + for (k = 0; k <= PointsPerInterval; ++k) { + t = (float) k *h[j] / (float) PointsPerInterval; + t2 = t * t; + t3 = t * t * t; + nx = x[j] + (int) (t * dx[j] + t2 * d2x[j] / 2 + t3 * d3x[j] / 6); + ny = y[j] + (int) (t * dy[j] + t2 * d2y[j] / 2 + t3 * d3y[j] / 6); + HGtline(nx, ny); + if (length++ > LINELENGTH) { + length = 0; + printf("\\\n"); + } + } /* end for k */ + } /* end for j */ +} /* end HGCurve */ + + +/*---------------------------------------------------------------------------- + | Routine: Paramaterize (xpoints, ypoints, hparams, num_points) + | + | Results: This routine calculates parameteric values for use in + | calculating curves. The parametric values are returned + | in the array h. The values are an approximation of + | cumulative arc lengths of the curve (uses cord length). + | For additional information, see paper cited below. + *----------------------------------------------------------------------------*/ + +void +Paramaterize(int x[], + int y[], + float h[], + int n) +{ + register int dx; + register int dy; + register int i; + register int j; + float u[MAXPOINTS]; + + for (i = 1; i <= n; ++i) { + u[i] = 0; + for (j = 1; j < i; j++) { + dx = x[j + 1] - x[j]; + dy = y[j + 1] - y[j]; + /* Here was overflowing, so I changed it. */ + /* u[i] += sqrt ((double) (dx * dx + dy * dy)); */ + u[i] += hypot((double) dx, (double) dy); + } + } + for (i = 1; i < n; ++i) + h[i] = u[i + 1] - u[i]; +} /* end Paramaterize */ + + +/*---------------------------------------------------------------------------- + | Routine: PeriodicSpline (h, z, dz, d2z, d3z, npoints) + | + | Results: This routine solves for the cubic polynomial to fit a spline + | curve to the the points specified by the list of values. + | The Curve generated is periodic. The algorithms for this + | curve are from the `Spline Curve Techniques' paper cited + | above. + *----------------------------------------------------------------------------*/ + +void +PeriodicSpline(float h[], /* paramaterization */ + int z[], /* point list */ + float dz[], /* to return the 1st derivative */ + float d2z[], /* 2nd derivative */ + float d3z[], /* 3rd derivative */ + int npoints) /* number of valid points */ +{ + float d[MAXPOINTS]; + float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS]; + float c[MAXPOINTS], r[MAXPOINTS], s[MAXPOINTS]; + int i; + + /* step 1 */ + for (i = 1; i < npoints; ++i) { + deltaz[i] = h[i] ? ((double) (z[i + 1] - z[i])) / h[i] : 0; + } + h[0] = h[npoints - 1]; + deltaz[0] = deltaz[npoints - 1]; + + /* step 2 */ + for (i = 1; i < npoints - 1; ++i) { + d[i] = deltaz[i + 1] - deltaz[i]; + } + d[0] = deltaz[1] - deltaz[0]; + + /* step 3a */ + a[1] = 2 * (h[0] + h[1]); + b[1] = d[0]; + c[1] = h[0]; + for (i = 2; i < npoints - 1; ++i) { + a[i] = 2 * (h[i - 1] + h[i]) - + pow((double) h[i - 1], (double) 2.0) / a[i - 1]; + b[i] = d[i - 1] - h[i - 1] * b[i - 1] / a[i - 1]; + c[i] = -h[i - 1] * c[i - 1] / a[i - 1]; + } + + /* step 3b */ + r[npoints - 1] = 1; + s[npoints - 1] = 0; + for (i = npoints - 2; i > 0; --i) { + r[i] = -(h[i] * r[i + 1] + c[i]) / a[i]; + s[i] = (6 * b[i] - h[i] * s[i + 1]) / a[i]; + } + + /* step 4 */ + d2z[npoints - 1] = (6 * d[npoints - 2] - h[0] * s[1] + - h[npoints - 1] * s[npoints - 2]) + / (h[0] * r[1] + h[npoints - 1] * r[npoints - 2] + + 2 * (h[npoints - 2] + h[0])); + for (i = 1; i < npoints - 1; ++i) { + d2z[i] = r[i] * d2z[npoints - 1] + s[i]; + } + d2z[npoints] = d2z[1]; + + /* step 5 */ + for (i = 1; i < npoints; ++i) { + dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6; + d3z[i] = h[i] ? (d2z[i + 1] - d2z[i]) / h[i] : 0; + } +} /* end PeriodicSpline */ + + +/*---------------------------------------------------------------------------- + | Routine: NaturalEndSpline (h, z, dz, d2z, d3z, npoints) + | + | Results: This routine solves for the cubic polynomial to fit a spline + | curve the the points specified by the list of values. The + | alogrithms for this curve are from the `Spline Curve + | Techniques' paper cited above. + *----------------------------------------------------------------------------*/ + +void +NaturalEndSpline(float h[], /* parameterization */ + int z[], /* Point list */ + float dz[], /* to return the 1st derivative */ + float d2z[], /* 2nd derivative */ + float d3z[], /* 3rd derivative */ + int npoints) /* number of valid points */ +{ + float d[MAXPOINTS]; + float deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS]; + int i; + + /* step 1 */ + for (i = 1; i < npoints; ++i) { + deltaz[i] = h[i] ? ((double) (z[i + 1] - z[i])) / h[i] : 0; + } + deltaz[0] = deltaz[npoints - 1]; + + /* step 2 */ + for (i = 1; i < npoints - 1; ++i) { + d[i] = deltaz[i + 1] - deltaz[i]; + } + d[0] = deltaz[1] - deltaz[0]; + + /* step 3 */ + a[0] = 2 * (h[2] + h[1]); + b[0] = d[1]; + for (i = 1; i < npoints - 2; ++i) { + a[i] = 2 * (h[i + 1] + h[i + 2]) - + pow((double) h[i + 1], (double) 2.0) / a[i - 1]; + b[i] = d[i + 1] - h[i + 1] * b[i - 1] / a[i - 1]; + } + + /* step 4 */ + d2z[npoints] = d2z[1] = 0; + for (i = npoints - 1; i > 1; --i) { + d2z[i] = (6 * b[i - 2] - h[i] * d2z[i + 1]) / a[i - 2]; + } + + /* step 5 */ + for (i = 1; i < npoints; ++i) { + dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6; + d3z[i] = h[i] ? (d2z[i + 1] - d2z[i]) / h[i] : 0; + } +} /* end NaturalEndSpline */ + + +/*----------------------------------------------------------------------------* + | Routine: change (x_position, y_position, visible_flag) + | + | Results: As HGtline passes from the invisible to visible (or vice + | versa) portion of a line, change is called to either draw + | the line, or initialize the beginning of the next one. + | Change calls line to draw segments if visible_flag is set + | (which means we're leaving a visible area). + *----------------------------------------------------------------------------*/ + +void +change(register int x, + register int y, + register int vis) +{ + static int length = 0; + + if (vis) { /* leaving a visible area, draw it. */ + line(x, y); + if (length++ > LINELENGTH) { + length = 0; + printf("\\\n"); + } + } else { /* otherwise, we're entering one, remember */ + /* beginning */ + tmove2(x, y); + } +} + + +/*---------------------------------------------------------------------------- + | Routine: HGtline (xstart, ystart, xend, yend) + | + | Results: Draws a line from current position to (x1,y1) using line(x1, + | y1) to place individual segments of dotted or dashed lines. + *----------------------------------------------------------------------------*/ + +void +HGtline(int x1, + int y1) +{ + register int x0 = lastx; + register int y0 = lasty; + register int dx; + register int dy; + register int oldcoord; + register int res1; + register int visible; + register int res2; + register int xinc; + register int yinc; + register int dotcounter; + + if (linmod == SOLID) { + line(x1, y1); + return; + } + + /* for handling different resolutions */ + dotcounter = linmod << dotshifter; + + xinc = 1; + yinc = 1; + if ((dx = x1 - x0) < 0) { + xinc = -xinc; + dx = -dx; + } + if ((dy = y1 - y0) < 0) { + yinc = -yinc; + dy = -dy; + } + res1 = 0; + res2 = 0; + visible = 0; + if (dx >= dy) { + oldcoord = y0; + while (x0 != x1) { + if ((x0 & dotcounter) && !visible) { + change(x0, y0, 0); + visible = 1; + } else if (visible && !(x0 & dotcounter)) { + change(x0 - xinc, oldcoord, 1); + visible = 0; + } + if (res1 > res2) { + oldcoord = y0; + res2 += dx - res1; + res1 = 0; + y0 += yinc; + } + res1 += dy; + x0 += xinc; + } + } else { + oldcoord = x0; + while (y0 != y1) { + if ((y0 & dotcounter) && !visible) { + change(x0, y0, 0); + visible = 1; + } else if (visible && !(y0 & dotcounter)) { + change(oldcoord, y0 - yinc, 1); + visible = 0; + } + if (res1 > res2) { + oldcoord = x0; + res2 += dy - res1; + res1 = 0; + x0 += xinc; + } + res1 += dx; + y0 += yinc; + } + } + if (visible) + change(x1, y1, 1); + else + change(x1, y1, 0); +} + +/* EOF */ diff --git a/contrib/groff/src/preproc/grn/hpoint.cc b/contrib/groff/src/preproc/grn/hpoint.cc new file mode 100644 index 0000000..f4e1ca8 --- /dev/null +++ b/contrib/groff/src/preproc/grn/hpoint.cc @@ -0,0 +1,49 @@ +/* Last non-groff version: hpoint.c 1.1 84/10/08 */ + +/* + * This file contains routines for manipulating the point data structures + * for the gremlin picture editor. + */ + +#include <stdlib.h> +#include "gprint.h" + + +/* + * Return pointer to empty point list. + */ +POINT * +PTInit() +{ + return ((POINT *) NULL); +} + + +/* + * This routine creates a new point with coordinates x and y and links it + * into the pointlist. + */ +POINT * +PTMakePoint(float x, + float y, + POINT **pplist) +{ + register POINT *point; + + if (Nullpoint(point = *pplist)) { /* empty list */ + *pplist = (POINT *) malloc(sizeof(POINT)); + point = *pplist; + } else { + while (!Nullpoint(point->nextpt)) + point = point->nextpt; + point->nextpt = (POINT *) malloc(sizeof(POINT)); + point = point->nextpt; + } + + point->x = x; + point->y = y; + point->nextpt = PTInit(); + return (point); +} /* end PTMakePoint */ + +/* EOF */ diff --git a/contrib/groff/src/preproc/grn/main.cc b/contrib/groff/src/preproc/grn/main.cc new file mode 100644 index 0000000..92e64c6 --- /dev/null +++ b/contrib/groff/src/preproc/grn/main.cc @@ -0,0 +1,905 @@ +/* Last non-groff version: main.cc 1.23 (Berkeley) 85/08/05 + * + * Adapted to GNU troff by Daniel Senderowicz 99/12/29. + * + * Further refinements by Werner Lemberg 00/02/20. + * + * + * This file contains the main and file system dependent routines for + * processing gremlin files into troff input. The program watches input go + * by to standard output, only interpreting things between .GS and .GE + * lines. Default values (font, size, scale, thickness) may be overridden + * with a `default' command and are further overridden by commands in the + * input. + * + * Inside the GS and GE, commands are accepted to reconfigure the picture. + * At most one command may reside on each line, and each command is followed + * by a parameter separated by white space. The commands are as follows, + * and may be abbreviated down to one character (with exception of `scale' + * and `stipple' down to "sc" and "st") and may be upper or lower case. + * + * default - Make all settings in the current + * .GS/.GE the global defaults. Height, + * width and file are NOT saved. + * 1, 2, 3, 4 - Set size 1, 2, 3, or 4 (followed by an + * integer point size). + * roman, italics, bold, special - Set gremlin's fonts to any other troff + * font (one or two characters). + * stipple, l - Use a stipple font for polygons. Arg + * is troff font name. No Default. Can + * use only one stipple font per picture. + * (See below for stipple font index.) + * scale, x - Scale is IN ADDITION to the global + * scale factor from the default. + * pointscale - Turn on scaling point sizes to match + * `scale' commands. (Optional operand + * `off' to turn it off.) + * narrow, medium, thick - Set widths of lines. + * file - Set the file name to read the gremlin + * picture from. If the file isn't in + * the current directory, the gremlin + * library is tried. + * width, height - These two commands override any + * scaling factor that is in effect, and + * forces the picture to fit into either + * the height or width specified, + * whichever makes the picture smaller. + * The operand for these two commands is + * a floating-point number in units of + * inches. + * l<nn> (integer <nn>) - Set association between stipple <nn> + * and a stipple `character'. <nn> must + * be in the range 0 to NSTIPPLES (16) + * inclusive. The integer operand is an + * index in the stipple font selected. + * Valid cf (cifplot) indices are 1-32 + * (although 24 is not defined), valid ug + * (unigrafix) indices are 1-14, and + * valid gs (gray scale) indices are + * 0-16. Nonetheless, any number between + * 0 and 255 is accepted since new + * stipple fonts may be added. An + * integer operand is required. + * + * Troff number registers used: g1 through g9. g1 is the width of the + * picture, and g2 is the height. g3, and g4, save information, g8 and g9 + * are used for text processing and g5-g7 are reserved. + */ + + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include "gprint.h" + +#include "device.h" +#include "font.h" +#include "searchpath.h" +#include "macropath.h" + +#include "lib.h" +#include "errarg.h" +#include "error.h" +#include "defs.h" + +/* database imports */ + +extern void HGPrintElt(ELT *element, int baseline); +extern ELT *DBInit(); +extern ELT *DBRead(register FILE *file); +extern POINT *PTInit(); +extern POINT *PTMakePoint(float x, float y, POINT **pplist); + + +#define SUN_SCALEFACTOR 0.70 + +/* #define DEFSTIPPLE "gs" */ +#define DEFSTIPPLE "cf" + +#define MAXINLINE 100 /* input line length */ + +#define SCREENtoINCH 0.02 /* scaling factor, screen to inches */ + +#define BIG 999999999999.0 /* unweildly large floating number */ + + +static char sccsid[] = "@(#) (Berkeley) 8/5/85, 12/28/99"; + +int res; /* the printer's resolution goes here */ + +int dotshifter; /* for the length of dotted curves */ + +double linethickness; /* brush styles */ +int linmod; +int lastx; /* point registers for printing elements */ +int lasty; +int lastyline; /* A line's vertical position is NOT the */ + /* same after that line is over, so for a */ + /* line of drawing commands, vertical */ + /* spacing is kept in lastyline */ + +/* These are the default fonts, sizes, line styles, */ +/* and thicknesses. They can be modified from a */ +/* `default' command and are reset each time the */ +/* start of a picture (.GS) is found. */ + +char *deffont[] = +{"R", "I", "B", "S"}; +int defsize[] = +{10, 16, 24, 36}; +/* #define BASE_THICKNESS 1.0 */ +#define BASE_THICKNESS 0.15 +double defthick[STYLES] = +{1 * BASE_THICKNESS, + 1 * BASE_THICKNESS, + 5 * BASE_THICKNESS, + 1 * BASE_THICKNESS, + 1 * BASE_THICKNESS, + 3 * BASE_THICKNESS}; + +/* int cf_stipple_index[NSTIPPLES + 1] = */ +/* {0, 1, 3, 12, 14, 16, 19, 21, 23}; */ +/* a logarithmic scale looks better than a linear one for the gray shades */ +/* */ +/* int other_stipple_index[NSTIPPLES + 1] = */ +/* {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; */ + +int cf_stipple_index[NSTIPPLES + 1] = +{0, 18, 32, 56, 100, 178, 316, 562, 1000}; /* only 1-8 used */ +int other_stipple_index[NSTIPPLES + 1] = +{0, 62, 125, 187, 250, 312, 375, 437, 500, + 562, 625, 687, 750, 812, 875, 937, 1000}; + +/* int *defstipple_index = other_stipple_index; */ +int *defstipple_index = cf_stipple_index; + +int style[STYLES] = +{DOTTED, DOTDASHED, SOLID, DASHED, SOLID, SOLID}; +double scale = 1.0; /* no scaling, default */ +int defpoint = 0; /* flag for pointsize scaling */ +char *defstipple = (char *) 0; +enum { + OUTLINE, FILL, BOTH +} polyfill; + +/* flag to controll filling of polygons */ + +double adj1 = 0.0; +double adj2 = 0.0; +double adj3 = 0.0; +double adj4 = 0.0; + +double thick[STYLES]; /* thicknesses set by defaults, then by */ + /* commands */ +char *tfont[FONTS]; /* fonts originally set to deffont values, */ + /* then */ +int tsize[SIZES]; /* optionally changed by commands inside */ + /* grn */ +int stipple_index[NSTIPPLES + 1]; /* stipple font file indices */ +char *stipple; + +double xscale; /* scaling factor from individual pictures */ +double troffscale; /* scaling factor at output time */ + +double width; /* user-request maximum width for picture */ + /* (in inches) */ +double height; /* user-request height */ +int pointscale; /* flag for pointsize scaling */ +int setdefault; /* flag for a .GS/.GE to remember all */ + /* settings */ +int sflag; /* -s flag: sort order (do polyfill first) */ + +double toppoint; /* remember the picture */ +double bottompoint; /* bounds in these variables */ +double leftpoint; +double rightpoint; + +int ytop; /* these are integer versions of the above */ +int ybottom; /* so not to convert each time they're used */ +int xleft; +int xright; + +int linenum = 0; /* line number of input file */ +char inputline[MAXINLINE]; /* spot to filter through the file */ +char *c1 = inputline; /* c1, c2, and c3 will be used to */ +char *c2 = inputline + 1; /* hunt for lines that begin with */ +char *c3 = inputline + 2; /* ".GS" by looking individually */ +char *c4 = inputline + 3; /* needed for compatibility mode */ +char GScommand[MAXINLINE]; /* put user's ".GS" command line here */ +char gremlinfile[MAXINLINE]; /* filename to use for a picture */ +int SUNFILE = FALSE; /* TRUE if SUN gremlin file */ +int compatibility_flag = FALSE; /* TRUE if in compatibility mode */ + + +void getres(); +char *doinput(FILE *fp); +void conv(register FILE *fp, int baseline); +void savestate(); +int has_polygon(register ELT *elist); +void interpret(char *line); + + +void +usage(FILE *stream) +{ + fprintf(stream, + "usage: %s [ -vCs ] [ -M dir ] [ -F dir ] [ -T dev ] [ file ]\n", + program_name); +} + + +/*----------------------------------------------------------------------------* + | Routine: main (argument_count, argument_pointer) + | + | Results: Parses the command line, accumulating input file names, then + | reads the inputs, passing it directly to output until a `.GS' + | line is read. Main then passes control to `conv' to do the + | gremlin file conversions. + *----------------------------------------------------------------------------*/ + +int +main(int argc, + char **argv) +{ + program_name = argv[0]; + register FILE *fp; + register int k; + register char c; + register int gfil = 0; + char *file[50]; + char *operand(int *argcp, char ***argvp); + + while (--argc) { + if (**++argv != '-') + file[gfil++] = *argv; + else + switch (c = (*argv)[1]) { + + case 0: + file[gfil++] = NULL; + break; + + case 'C': /* compatibility mode */ + compatibility_flag = TRUE; + break; + + case 'F': /* font path to find DESC */ + font::command_line_font_dir(operand(&argc, &argv)); + break; + + case 'T': /* final output typesetter name */ + device = operand(&argc, &argv); + break; + + case 'M': /* set library directory */ + macro_path.command_line_dir(operand(&argc, &argv)); + break; + + case 's': /* preserve order of elements */ + sflag = 1; + break; + + case '-': + if (strcmp(*argv,"--version")==0) { + case 'v': + extern const char *Version_string; + printf("GNU grn (groff) version %s\n", Version_string); + exit(0); + break; + } + if (strcmp(*argv,"--help")==0) { + case '?': + usage(stdout); + exit(0); + break; + } + // fallthrough + default: + error("unknown switch: %1", c); + usage(stderr); + exit(1); + } + } + + getres(); /* set the resolution for an output device */ + + if (gfil == 0) { /* no filename, use standard input */ + file[0] = NULL; + gfil++; + } + + for (k = 0; k < gfil; k++) { + if (file[k] != NULL) { + if ((fp = fopen(file[k], "r")) == NULL) + fatal("can't open %1", file[k]); + } else + fp = stdin; + + while (doinput(fp) != NULL) { + if (*c1 == '.' && *c2 == 'G' && *c3 == 'S') { + if (compatibility_flag || + *c4 == '\n' || *c4 == ' ' || *c4 == '\0') + conv(fp, linenum); + else + fputs(inputline, stdout); + } else + fputs(inputline, stdout); + } + } +} + + +/*----------------------------------------------------------------------------* + | Routine: char * operand (& argc, & argv) + | + | Results: Returns address of the operand given with a command-line + | option. It uses either `-Xoperand' or `-X operand', whichever + | is present. The program is terminated if no option is + | present. + | + | Side Efct: argc and argv are updated as necessary. + *----------------------------------------------------------------------------*/ + +char * +operand(int *argcp, + char ***argvp) +{ + if ((**argvp)[2]) + return (**argvp + 2); /* operand immediately follows */ + if ((--*argcp) <= 0) { /* no operand */ + error("command-line option operand missing."); + exit(8); + } + return (*(++(*argvp))); /* operand is next word */ +} + + +/*----------------------------------------------------------------------------* + | Routine: getres () + | + | Results: Sets `res' to the resolution of the output device. + *----------------------------------------------------------------------------*/ + +void +getres() +{ + int linepiece; + + if (!font::load_desc()) + fatal("sorry, I can't continue"); + + res = font::res; + + /* Correct the brush thicknesses based on res */ + /* if (res >= 256) { + defthick[0] = res >> 8; + defthick[1] = res >> 8; + defthick[2] = res >> 4; + defthick[3] = res >> 8; + defthick[4] = res >> 8; + defthick[5] = res >> 6; + } */ + + linepiece = res >> 9; + for (dotshifter = 0; linepiece; dotshifter++) + linepiece = linepiece >> 1; +} + + +/*----------------------------------------------------------------------------* + | Routine: char * doinput (file_pointer) + | + | Results: A line of input is read into `inputline'. + | + | Side Efct: "linenum" is incremented. + | + | Bugs: Lines longer than MAXINLINE are NOT checked, except for + | updating `linenum'. + *----------------------------------------------------------------------------*/ + +char * +doinput(FILE *fp) +{ + char *k; + + if ((k = fgets(inputline, MAXINLINE, fp)) == NULL) + return k; + if (strchr(inputline, '\n')) /* ++ only if it's a complete line */ + linenum++; + return (char *) !NULL; +} + + +/*----------------------------------------------------------------------------* + | Routine: initpic ( ) + | + | Results: Sets all parameters to the normal defaults, possibly + | overridden by a setdefault command. Initialize the picture + | variables, and output the startup commands to troff to begin + | the picture. + *----------------------------------------------------------------------------*/ + +void +initpic() +{ + register int i; + + for (i = 0; i < STYLES; i++) { /* line thickness defaults */ + thick[i] = defthick[i]; + } + for (i = 0; i < FONTS; i++) { /* font name defaults */ + tfont[i] = deffont[i]; + } + for (i = 0; i < SIZES; i++) { /* font size defaults */ + tsize[i] = defsize[i]; + } + for (i = 0; i <= NSTIPPLES; i++) { /* stipple font file default indices */ + stipple_index[i] = defstipple_index[i]; + } + stipple = defstipple; + + gremlinfile[0] = 0; /* filename is `null' */ + setdefault = 0; /* this is not the default settings (yet) */ + + toppoint = BIG; /* set the picture bounds out */ + bottompoint = -BIG; /* of range so they'll be set */ + leftpoint = BIG; /* by `savebounds' on input */ + rightpoint = -BIG; + + pointscale = defpoint; /* flag for scaling point sizes default */ + xscale = scale; /* default scale of individual pictures */ + width = 0.0; /* size specifications input by user */ + height = 0.0; + + linethickness = DEFTHICK; /* brush styles */ + linmod = DEFSTYLE; +} + + +/*----------------------------------------------------------------------------* + | Routine: conv (file_pointer, starting_line) + | + | Results: At this point, we just passed a `.GS' line in the input + | file. conv reads the input and calls `interpret' to process + | commands, gathering up information until a `.GE' line is + | found. It then calls `HGPrint' to do the translation of the + | gremlin file to troff commands. + *----------------------------------------------------------------------------*/ + +void +conv(register FILE *fp, + int baseline) +{ + register FILE *gfp = NULL; /* input file pointer */ + register int done = 0; /* flag to remember if finished */ + register ELT *e; /* current element pointer */ + ELT *PICTURE; /* whole picture data base pointer */ + double temp; /* temporary calculating area */ + /* POINT ptr; */ /* coordinates of a point to pass to `mov' */ + /* routine */ + int flyback; /* flag `want to end up at the top of the */ + /* picture?' */ + int compat; /* test character after .GE or .GF */ + + + initpic(); /* set defaults, ranges, etc. */ + strcpy(GScommand, inputline); /* save `.GS' line for later */ + + do { + done = (doinput(fp) == NULL); /* test for EOF */ + flyback = (*c3 == 'F'); /* and .GE or .GF */ + compat = (compatibility_flag || + *c4 == '\n' || *c4 == ' ' || *c4 == '\0'); + done |= (*c1 == '.' && *c2 == 'G' && (*c3 == 'E' || flyback) && + compat); + + if (done) { + if (setdefault) + savestate(); + + if (!gremlinfile[0]) { + if (!setdefault) + error("at line %1: no picture filename.\n", baseline); + return; + } + char *path; + gfp = macro_path.open_file(gremlinfile, &path); + if (!gfp) + return; + PICTURE = DBRead(gfp); /* read picture file */ + fclose(gfp); + a_delete path; + if (DBNullelt(PICTURE)) + return; /* If a request is made to make the */ + /* picture fit into a specific area, */ + /* set the scale to do that. */ + + if (stipple == (char *) NULL) /* if user forgot stipple */ + if (has_polygon(PICTURE)) /* and picture has a polygon */ + stipple = DEFSTIPPLE; /* then set the default */ + + if ((temp = bottompoint - toppoint) < 0.1) + temp = 0.1; + temp = (height != 0.0) ? height / (temp * SCREENtoINCH) : BIG; + if ((troffscale = rightpoint - leftpoint) < 0.1) + troffscale = 0.1; + troffscale = (width != 0.0) ? + width / (troffscale * SCREENtoINCH) : BIG; + if (temp == BIG && troffscale == BIG) + troffscale = xscale; + else { + if (temp < troffscale) + troffscale = temp; + } /* here, troffscale is the */ + /* picture's scaling factor */ + if (pointscale) { + register int i; /* do pointscaling here, when */ + /* scale is known, before output */ + for (i = 0; i < SIZES; i++) + tsize[i] = (int) (troffscale * (double) tsize[i] + 0.5); + } + + /* change to device units */ + troffscale *= SCREENtoINCH * res; /* from screen units */ + + ytop = (int) (toppoint * troffscale); /* calculate integer */ + ybottom = (int) (bottompoint * troffscale); /* versions of the */ + xleft = (int) (leftpoint * troffscale); /* picture limits */ + xright = (int) (rightpoint * troffscale); + + /* save stuff in number registers, */ + /* register g1 = picture width and */ + /* register g2 = picture height, */ + /* set vertical spacing, no fill, */ + /* and break (to make sure picture */ + /* starts on left), and put out the */ + /* user's `.GS' line. */ + printf(".br\n" + ".nr g1 %du\n" + ".nr g2 %du\n" + "%s" + ".nr g3 \\n(.f\n" + ".nr g4 \\n(.s\n" + "\\0\n" + ".sp -1\n", + xright - xleft, ybottom - ytop, GScommand); + + if (stipple) /* stipple requested for this picture */ + printf(".st %s\n", stipple); + lastx = xleft; /* note where we are (upper left */ + lastyline = lasty = ytop; /* corner of the picture) */ + + /* Just dump everything in the order it appears. + * + * If -s command-line option, traverse picture twice: First time, + * print only the interiors of filled polygons (as borderless + * polygons). Second time, print the outline as series of line + * segments. This way, postprocessors that overwrite rather than + * merge picture elements (such as Postscript) can still have text and + * graphics on a shaded background. + */ + /* if (sflag) */ + if (!sflag) { /* changing the default for filled polygons */ + e = PICTURE; + polyfill = FILL; + while (!DBNullelt(e)) { + printf(".mk\n"); + if (e->type == POLYGON) + HGPrintElt(e, baseline); + printf(".rt\n"); + lastx = xleft; + lastyline = lasty = ytop; + e = DBNextElt(e); + } + } + e = PICTURE; + + /* polyfill = !sflag ? BOTH : OUTLINE; */ + polyfill = sflag ? BOTH : OUTLINE; /* changing the default */ + while (!DBNullelt(e)) { + printf(".mk\n"); + HGPrintElt(e, baseline); + printf(".rt\n"); + lastx = xleft; + lastyline = lasty = ytop; + e = DBNextElt(e); + } + + /* decide where to end picture */ + + /* I changed everything here. I always use the combination .mk and */ + /* .rt so once finished I just space down the heigth of the picture */ + /* that is \n(g2u */ + if (flyback) { /* end picture at upper left */ + /* ptr.x = leftpoint; + ptr.y = toppoint; */ + } else { /* end picture at lower left */ + /* ptr.x = leftpoint; + ptr.y = bottompoint; */ + printf(".sp \\n(g2u\n"); + } + + /* tmove(&ptr); */ /* restore default line parameters */ + + /* restore everything to the way it was before the .GS, then put */ + /* out the `.GE' line from user */ + + /* printf("\\D't %du'\\D's %du'\n", DEFTHICK, DEFSTYLE); */ + /* groff doesn't understand the \Ds command */ + + printf("\\D't %du'\n", DEFTHICK); + if (flyback) /* make sure we end up at top of */ + printf(".sp -1\n"); /* picture if `flying back' */ + if (stipple) /* restore stipple to previous */ + printf(".st\n"); + printf(".br\n" + ".ft \\n(g3\n" + ".ps \\n(g4\n" + "%s", inputline); + } else + interpret(inputline); /* take commands from the input file */ + } while (!done); +} + + +/*----------------------------------------------------------------------------* + | Routine: savestate ( ) + | + | Results: all the current scaling / font size / font name / thickness + | / pointscale settings are saved to be the defaults. Scaled + | point sizes are NOT saved. The scaling is done each time a + | new picture is started. + | + | Side Efct: scale, and def* are modified. + *----------------------------------------------------------------------------*/ + +void +savestate() +{ + register int i; + + for (i = 0; i < STYLES; i++) /* line thickness defaults */ + defthick[i] = thick[i]; + for (i = 0; i < FONTS; i++) /* font name defaults */ + deffont[i] = tfont[i]; + for (i = 0; i < SIZES; i++) /* font size defaults */ + defsize[i] = tsize[i]; + for (i = 0; i <= NSTIPPLES; i++) /* stipple font file default indices */ + defstipple_index[i] = stipple_index[i]; + + defstipple = stipple; /* if stipple has been set, it's remembered */ + scale *= xscale; /* default scale of individual pictures */ + defpoint = pointscale; /* flag for scaling pointsizes from x factors */ +} + + +/*----------------------------------------------------------------------------* + | Routine: savebounds (x_coordinate, y_coordinate) + | + | Results: Keeps track of the maximum and minimum extent of a picture + | in the global variables: left-, right-, top- and + | bottompoint. `savebounds' assumes that the points have been + | oriented to the correct direction. No scaling has taken + | place, though. + *----------------------------------------------------------------------------*/ + +void +savebounds(float x, + float y) +{ + if (x < leftpoint) + leftpoint = x; + if (x > rightpoint) + rightpoint = x; + if (y < toppoint) + toppoint = y; + if (y > bottompoint) + bottompoint = y; +} + + +/*----------------------------------------------------------------------------* + | Routine: interpret (character_string) + | + | Results: Commands are taken from the input string and performed. + | Commands are separated by the endofline, and are of the + | format: + | string1 string2 + | + | where string1 is the command and string2 is the argument. + | + | Side Efct: Font and size strings, plus the gremlin file name and the + | width and height variables are set by this routine. + *----------------------------------------------------------------------------*/ + +void +interpret(char *line) +{ + char str1[MAXINLINE]; + char str2[MAXINLINE]; + register char *chr; + register int i; + double par; + + str2[0] = '\0'; + sscanf(line, "%80s%80s", &str1[0], &str2[0]); + for (chr = &str1[0]; *chr; chr++) /* convert command to */ + if (isupper(*chr)) + *chr = tolower(*chr); /* lower case */ + + switch (str1[0]) { + + case '1': + case '2': /* font sizes */ + case '3': + case '4': + i = atoi(str2); + if (i > 0 && i < 1000) + tsize[str1[0] - '1'] = i; + else + error("bad font size value at line %1", linenum); + break; + + case 'r': /* roman */ + if (str2[0] < '0') + goto nofont; + tfont[0] = (char *) malloc(strlen(str2) + 1); + strcpy(tfont[0], str2); + break; + + case 'i': /* italics */ + if (str2[0] < '0') + goto nofont; + tfont[1] = (char *) malloc(strlen(str2) + 1); + strcpy(tfont[1], str2); + break; + + case 'b': /* bold */ + if (str2[0] < '0') + goto nofont; + tfont[2] = (char *) malloc(strlen(str2) + 1); + strcpy(tfont[2], str2); + break; + + case 's': /* special */ + if (str1[1] == 'c') + goto scalecommand; /* or scale */ + + if (str2[0] < '0') { + nofont: + error("no fontname specified in line %1", linenum); + break; + } + if (str1[1] == 't') + goto stipplecommand; /* or stipple */ + + tfont[3] = (char *) malloc(strlen(str2) + 1); + strcpy(tfont[3], str2); + break; + + case 'l': /* l */ + if (isdigit(str1[1])) { /* set stipple index */ + int index = atoi(str1 + 1), val; + + if (index < 0 || index > NSTIPPLES) { + error("bad stipple number %1 at line %2", index, linenum); + break; + } + if (!defstipple_index) + defstipple_index = other_stipple_index; + val = atoi(str2); + if (val >= 0 && val < 256) + stipple_index[index] = val; + else + error("bad stipple index value at line %1", linenum); + break; + } + + stipplecommand: /* set stipple name */ + stipple = (char *) malloc(strlen(str2) + 1); + strcpy(stipple, str2); + /* if its a `known' font (currently only `cf'), set indicies */ + if (strcmp(stipple, "cf") == 0) + defstipple_index = cf_stipple_index; + else + defstipple_index = other_stipple_index; + for (i = 0; i <= NSTIPPLES; i++) + stipple_index[i] = defstipple_index[i]; + break; + + case 'a': /* text adjust */ + par = atof(str2); + switch (str1[1]) { + case '1': + adj1 = par; + break; + case '2': + adj2 = par; + break; + case '3': + adj3 = par; + break; + case '4': + adj4 = par; + break; + default: + error("bad adjust command at line %1", linenum); + break; + } + break; + + case 't': /* thick */ + thick[2] = defthick[0] * atof(str2); + break; + + case 'm': /* medium */ + thick[5] = defthick[0] * atof(str2); + break; + + case 'n': /* narrow */ + thick[0] = thick[1] = thick[3] = thick[4] = + defthick[0] * atof(str2); + break; + + case 'x': /* x */ + scalecommand: /* scale */ + par = atof(str2); + if (par > 0.0) + xscale *= par; + else + error("illegal scale value on line %1", linenum); + break; + + case 'f': /* file */ + strcpy(gremlinfile, str2); + break; + + case 'w': /* width */ + width = atof(str2); + if (width < 0.0) + width = -width; + break; + + case 'h': /* height */ + height = atof(str2); + if (height < 0.0) + height = -height; + break; + + case 'd': /* defaults */ + setdefault = 1; + break; + + case 'p': /* pointscale */ + if (strcmp("off", str2)) + pointscale = 1; + else + pointscale = 0; + break; + + default: + error("unknown command `%1' on line %2", str1, linenum); + exit(8); + break; + }; +} + + +/* + * return TRUE if picture contains a polygon + * otherwise FALSE + */ + +int +has_polygon(register ELT *elist) +{ + while (!DBNullelt(elist)) { + if (elist->type == POLYGON) + return (1); + elist = DBNextElt(elist); + } + + return (0); +} + +/* EOF */ diff --git a/contrib/groff/src/preproc/html/Makefile.sub b/contrib/groff/src/preproc/html/Makefile.sub new file mode 100644 index 0000000..9d5045a --- /dev/null +++ b/contrib/groff/src/preproc/html/Makefile.sub @@ -0,0 +1,7 @@ +PROG=pre-grohtml +# MAN1=pre-grohtml.n +MAN1= +XLIBS=$(LIBGROFF) +OBJS=pre-html.o pushbackbuffer.o +CCSRCS=$(srcdir)/pre-html.cc $(srcdir)/pushbackbuffer.cc +NAMEPREFIX=$(g) diff --git a/contrib/groff/src/preproc/html/pre-html.cc b/contrib/groff/src/preproc/html/pre-html.cc new file mode 100644 index 0000000..8357dd6 --- /dev/null +++ b/contrib/groff/src/preproc/html/pre-html.cc @@ -0,0 +1,1160 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. + Written by Gaius Mulley (gaius@glam.ac.uk). + +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. */ + +#define PREHTMLC + +#include <stdio.h> +#include <signal.h> +#include <ctype.h> +#include <string.h> +#include <assert.h> +#include <stdlib.h> +#include <errno.h> +#include "lib.h" +#include "errarg.h" +#include "error.h" +#include "stringclass.h" +#include "posix.h" +#include "defs.h" + +#include <errno.h> +#include <sys/types.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef _POSIX_VERSION +#include <sys/wait.h> +#define PID_T pid_t +#else /* not _POSIX_VERSION */ +#define PID_T int +#endif /* not _POSIX_VERSION */ + +extern char *strerror(); + +#include "pre-html.h" +#include "pushbackbuffer.h" +#include "html-strings.h" + +#define POSTSCRIPTRES 72000 // maybe there is a better way to find this? --fixme-- +#define DEFAULT_IMAGE_RES 80 // 80 pixels per inch resolution +#define DEFAULT_VERTICAL_OFFSET 45 // DEFAULT_VERTICAL_OFFSET/72 of an inch +#define IMAGE_BOARDER_PIXELS 0 +#define MAX_WIDTH 8 // inches +#define INLINE_LEADER_CHAR '\\' + +#define TRANSPARENT "-background \"#FFF\" -transparent \"#FFF\"" + +#if 0 +# define DEBUGGING +# define DEBUG_HTML +#endif + +#if !defined(TRUE) +# define TRUE (1==1) +#endif +#if !defined(FALSE) +# define FALSE (1==0) +#endif + +void stop() {} + +typedef enum {CENTERED, LEFT, RIGHT, INLINE} IMAGE_ALIGNMENT; + +static int stdoutfd = 1; // output file descriptor - normally 1 but might move + // -1 means closed +static int copyofstdoutfd =-1; // a copy of stdout, so we can restore stdout when + // writing to post-html +static char *psFileName = 0; // name of postscript file +static char *regionFileName = 0; // name of file containing all image regions +static char *imagePageStem = 0; // stem of all files containing page images +static char *image_device = "pnmraw"; +static int image_res = DEFAULT_IMAGE_RES; +static int vertical_offset= DEFAULT_VERTICAL_OFFSET; +static char *image_template = 0; // image template filename +static int troff_arg = 0; // troff arg index +static char *command_prefix = 0; // optional prefix for some installations. +static char *troff_command = 0; +#if defined(DEBUGGING) +static int debug = FALSE; +static char *troffFileName = 0; // output of pre-html output which is sent to troff -Tps +static char *htmlFileName = 0; // output of pre-html output which is sent to troff -Thtml +#endif + + +/* + * Images are generated via postscript, gs and the pnm utilities. + */ + +#define IMAGE_DEVICE "-Tps" + +/* + * prototypes + */ +static int do_file(const char *filename); + +/* + * sys_fatal - writes a fatal error message. Taken from src/roff/groff/pipeline.c + */ + +void sys_fatal (const char *s) +{ + fprintf(stderr, "%s: %s: %s", program_name, s, strerror(errno)); +} + +/* + * the class and methods for retaining ascii text + */ + +struct char_block { + enum { SIZE = 256 }; + char buffer[SIZE]; + int used; + char_block *next; + + char_block(); +}; + +/* + * char_block - constructor, sets the, used, and, next, fields to zero. + */ + +char_block::char_block() +: used(0), next(0) +{ +} + +class char_buffer { +public: + char_buffer(); + ~char_buffer(); + int read_file(FILE *fp); + int do_html(int argc, char *argv[]); + int do_image(int argc, char *argv[]); + void write_file_html(void); + void write_file_troff(void); + void write_upto_newline (char_block **t, int *i, int is_html); + int can_see(char_block **t, int *i, char *string); + int skip_spaces(char_block **t, int *i); + void skip_to_newline(char_block **t, int *i); +private: + char_block *head; + char_block *tail; +}; + +/* + * char_buffer - constructor + */ + +char_buffer::char_buffer() +: head(0), tail(0) +{ +} + +/* + * char_buffer - deconstructor, throws aways the whole buffer list. + */ + +char_buffer::~char_buffer() +{ + while (head != 0) { + char_block *temp = head; + head = head->next; + delete temp; + } +} + +/* + * read_file - read in a complete file, fp, placing the contents inside char_blocks. + */ + +int char_buffer::read_file (FILE *fp) +{ + int i=0; + unsigned int old_used; + int n; + + while (! feof(fp)) { + if (tail == 0) { + tail = new char_block; + head = tail; + } else { + if (tail->used == char_block::SIZE) { + tail->next = new char_block; + tail = tail->next; + } + } + // at this point we have a tail which is ready for the next SIZE bytes of the file + + n = fread(tail->buffer, sizeof(char), char_block::SIZE-tail->used, fp); + if (n <= 0) { + // error + return( 0 ); + } else { + tail->used += n*sizeof(char); + } + } + return( 1 ); +} + +/* + * writeNbytes - writes n bytes to stdout. + */ + +static void writeNbytes (char *s, int l) +{ + int n=0; + int r; + + while (n<l) { + r = write(stdoutfd, s, l-n); + if (r<0) { + sys_fatal("write"); + } + n += r; + s += r; + } +} + +/* + * writeString - writes a string to stdout. + */ + +static void writeString (char *s) +{ + writeNbytes(s, strlen(s)); +} + +/* + * makeFileName - creates the image filename template. + */ + +void makeFileName () +{ + char buffer[8192]; + + sprintf(buffer, "grohtml-%d", (int)getpid()); + strcat(buffer, "-%d"); + image_template = (char *)malloc(strlen(buffer)+1); + strcpy(image_template, buffer); +} + +/* + * write_end_image - ends the image. It writes out the image extents if we are using -Tps. + */ + +static void write_end_image (int is_html) +{ + if (is_html) { + /* + * emit image name and enable output + */ + writeString("\\O2\\O1\\O4\n"); + } else { + /* + * postscript, therefore emit image boundaries + */ + writeString("\\O2\\O4\n"); + } +} + +/* + * write_start_image - writes the troff which will: + * + * (i) disable html output for the following image + * (ii) reset the max/min x/y registers during postscript + * rendering. + */ + +static void write_start_image (IMAGE_ALIGNMENT pos, int is_html) +{ + if (pos == INLINE) { + writeString("\\O3\\O5'"); + writeString(image_template); writeString(".png'"); + } else { + writeString(".begin \\{\\\n"); + switch (pos) { + + case LEFT: + writeString(". image l "); + break; + case RIGHT: + writeString(". image r "); + break; + case CENTERED: + default: + writeString(". image c "); + } + writeString(image_template); writeString(".png\n"); + if (! is_html) { + writeString(".bp\n"); + writeString(".tl ''''\n"); + } + writeString("\\}\n"); + } + if (is_html) { + writeString("\\O0\n"); + } else { + // reset min/max registers + writeString("\\O0\\O1\n"); + } +} + +/* + * write_upto_newline - writes the contents of the buffer until a newline is seen. + * It checks for HTML_IMAGE_INLINE_BEGIN and HTML_IMAGE_INLINE_END + * and if they are present it processes them. + */ + +void char_buffer::write_upto_newline (char_block **t, int *i, int is_html) +{ + int j=*i; + + if (*t) { + while ((j < (*t)->used) && ((*t)->buffer[j] != '\n') && + ((*t)->buffer[j] != INLINE_LEADER_CHAR)) { + j++; + } + if ((j < (*t)->used) && ((*t)->buffer[j] == '\n')) { + j++; + } + writeNbytes((*t)->buffer+(*i), j-(*i)); + if ((*t)->buffer[j] == INLINE_LEADER_CHAR) { + if (can_see(t, &j, HTML_IMAGE_INLINE_BEGIN)) + write_start_image(INLINE, is_html); + else if (can_see(t, &j, HTML_IMAGE_INLINE_END)) + write_end_image(is_html); + else { + if (j < (*t)->used) { + *i = j; + j++; + writeNbytes((*t)->buffer+(*i), j-(*i)); + } + } + } + if (j == (*t)->used) { + *i = 0; + if ((*t)->buffer[j-1] == '\n') { + *t = (*t)->next; + } else { + *t = (*t)->next; + write_upto_newline(t, i, is_html); + } + } else { + // newline was seen + *i = j; + } + } +} + +/* + * can_see - returns TRUE if we can see string in t->buffer[i] onwards + */ + +int char_buffer::can_see (char_block **t, int *i, char *string) +{ + int j = 0; + int l = strlen(string); + int k = *i; + char_block *s = *t; + + while (s) { + while ((k<s->used) && (j<l) && (s->buffer[k] == string[j])) { + j++; + k++; + } + if (j == l) { + *i = k; + *t = s; + return( TRUE ); + } else if ((k<s->used) && (s->buffer[k] != string[j])) { + return( FALSE ); + } + s = s->next; + k = 0; + } + return( FALSE ); +} + +/* + * skip_spaces - returns TRUE if we have not run out of data. + * It also consumes spaces. + */ + +int char_buffer::skip_spaces(char_block **t, int *i) +{ + char_block *s = *t; + int k = *i; + + while (s) { + while ((k<s->used) && (isspace(s->buffer[k]))) { + k++; + } + if (k == s->used) { + k = 0; + s = s->next; + } else { + *i = k; + return( TRUE ); + } + } + return( FALSE ); +} + +/* + * skip_to_newline - skips all characters until a newline is seen. + * The newline is also consumed. + */ + +void char_buffer::skip_to_newline (char_block **t, int *i) +{ + int j=*i; + + if (*t) { + while ((j < (*t)->used) && ((*t)->buffer[j] != '\n')) { + j++; + } + if ((j < (*t)->used) && ((*t)->buffer[j] == '\n')) { + j++; + } + if (j == (*t)->used) { + *i = 0; + if ((*t)->buffer[j-1] == '\n') { + *t = (*t)->next; + } else { + *t = (*t)->next; + skip_to_newline(t, i); + } + } else { + // newline was seen + *i = j; + } + } +} + +/* + * write_file_troff - writes the buffer to stdout (troff). + */ + +void char_buffer::write_file_troff (void) +{ + char_block *t=head; + int r; + int i=0; + + if (t != 0) { + do { + /* + * remember to check the shortest string last + */ + if (can_see(&t, &i, HTML_IMAGE_END)) { + write_end_image(FALSE); + skip_to_newline(&t, &i); + } else if (can_see(&t, &i, HTML_IMAGE_LEFT)) { + write_start_image(LEFT, FALSE); + skip_to_newline(&t, &i); + } else if (can_see(&t, &i, HTML_IMAGE_RIGHT)) { + write_start_image(RIGHT, FALSE); + skip_to_newline(&t, &i); + } else if (can_see(&t, &i, HTML_IMAGE_CENTERED)) { + write_start_image(CENTERED, FALSE); + skip_to_newline(&t, &i); + } else { + write_upto_newline(&t, &i, FALSE); + } + } while (t != 0); + } + if (close(stdoutfd) < 0) + sys_fatal("close"); + + // now we grab fd=1 so that the next pipe cannot use fd=1 + if (stdoutfd == 1) { + if (dup(2) != stdoutfd) { + sys_fatal("dup failed to use fd=1"); + } + } +} + +/* + * the image class remembers the position of all images in the postscript file + * and assigns names for each image. + */ + +struct imageItem { + imageItem *next; + int X1; + int Y1; + int X2; + int Y2; + char *imageName; + int resolution; + int maxx; + int pageNo; + + imageItem (int x1, int y1, int x2, int y2, int page, int res, int max_width, char *name); + ~imageItem (); +}; + +/* + * imageItem - constructor + */ + +imageItem::imageItem (int x1, int y1, int x2, int y2, int page, int res, int max_width, char *name) +{ + X1 = x1; + Y1 = y1; + X2 = x2; + Y2 = y2; + pageNo = page; + resolution = res; + maxx = max_width; + imageName = name; + next = 0; +} + +/* + * imageItem - deconstructor + */ + +imageItem::~imageItem () +{ +} + +/* + * imageList - class containing a list of imageItems. + */ + +class imageList { +private: + imageItem *head; + imageItem *tail; + int count; +public: + imageList(); + ~imageList(); + void add(int x1, int y1, int x2, int y2, int page, int res, int maxx, char *name); +}; + +/* + * imageList - constructor. + */ + +imageList::imageList () + : head(0), tail(0), count(0) +{ +} + +/* + * imageList - deconstructor. + */ + +imageList::~imageList () +{ + while (head != 0) { + imageItem *i = head; + head = head->next; + delete i; + } +} + +/* + * createAllPages - creates a set of images, one per page. + */ + +static void createAllPages (void) +{ + char buffer[4096]; + + sprintf(buffer, + "echo showpage | gs -q -dSAFER -sDEVICE=%s -r%d -sOutputFile=%s%%d %s - > /dev/null 2>&1 \n", + image_device, + image_res, + imagePageStem, + psFileName); +#if defined(DEBUGGING) + fwrite(buffer, sizeof(char), strlen(buffer), stderr); + fflush(stderr); +#endif + system(buffer); +} + +/* + * removeAllPages - removes all page images. + */ + +static void removeAllPages (void) +{ +#if !defined(DEBUGGING) + char buffer[4096]; + int i=1; + + do { + sprintf(buffer, "%s%d", imagePageStem, i); + i++; + } while (remove(buffer) == 0); +#endif +} + +/* + * abs - returns the absolute value. + */ + +int abs (int x) +{ + if (x < 0) { + return( -x ); + } else { + return( x ); + } +} + +/* + * min - returns the minimum of two numbers. + */ + +int min (int x, int y) +{ + if (x < y) { + return( x ); + } else { + return( y ); + } +} + +/* + * max - returns the maximum of two numbers. + */ + +int max (int x, int y) +{ + if (x > y) { + return( x ); + } else { + return( y ); + } +} + +/* + * createImage - generates a minimal png file from the set of page images. + */ + +static void createImage (imageItem *i) +{ + if (i->X1 != -1) { + char buffer[4096]; + int x1 = max(min(i->X1, i->X2)*image_res/POSTSCRIPTRES-1*IMAGE_BOARDER_PIXELS, 0); + int y1 = max((image_res*vertical_offset/72)+min(i->Y1, i->Y2)*image_res/POSTSCRIPTRES-IMAGE_BOARDER_PIXELS, 0); + int x2 = max(i->X1, i->X2)*image_res/POSTSCRIPTRES+1*IMAGE_BOARDER_PIXELS; + int y2 = (image_res*vertical_offset/72)+max(i->Y1, i->Y2)*image_res/POSTSCRIPTRES+1*IMAGE_BOARDER_PIXELS; + + sprintf(buffer, + "pnmcut %d %d %d %d < %s%d | pnmtopng %s > %s \n", + x1, y1, x2-x1+1, y2-y1+1, + imagePageStem, + i->pageNo, + TRANSPARENT, + i->imageName); +#if defined(DEBUGGING) + fprintf(stderr, buffer); +#endif + system(buffer); +#if defined(DEBUGGING) + } else { + fprintf(stderr, "ignoring image as x1 coord is -1\n"); + fflush(stderr); +#endif + } +} + +/* + * add - an image description to the imageList. + */ + +void imageList::add (int x1, int y1, int x2, int y2, int page, int res, int maxx, char *name) +{ + imageItem *i = new imageItem(x1, y1, x2, y2, page, res, maxx, name); + + if (head == 0) { + head = i; + tail = i; + } else { + tail->next = i; + tail = i; + } + createImage(i); +} + +static imageList listOfImages; // list of images defined by the region file. + +/* + * write_file_html - writes the buffer to stdout (troff). + * It writes out the file replacing template image names with + * actual image names. + */ + +void char_buffer::write_file_html (void) +{ + char_block *t =head; + char *name; + int i=0; + + if (t != 0) { + stop(); + do { + /* + * remember to check the shortest string last + */ + if (can_see(&t, &i, HTML_IMAGE_END)) { + write_end_image(TRUE); + skip_to_newline(&t, &i); + } else if (can_see(&t, &i, HTML_IMAGE_LEFT)) { + write_start_image(LEFT, TRUE); + skip_to_newline(&t, &i); + } else if (can_see(&t, &i, HTML_IMAGE_RIGHT)) { + write_start_image(RIGHT, TRUE); + skip_to_newline(&t, &i); + } else if (can_see(&t, &i, HTML_IMAGE_CENTERED)) { + stop(); + write_start_image(CENTERED, TRUE); + skip_to_newline(&t, &i); + } else { + write_upto_newline(&t, &i, TRUE); + } + } while (t != 0); + } + if (close(stdoutfd) < 0) + sys_fatal("close"); + + // now we grab fd=1 so that the next pipe cannot use fd=1 + if (stdoutfd == 1) { + if (dup(2) != stdoutfd) { + sys_fatal("dup failed to use fd=1"); + } + } +} + +/* + * generateImages - parses the region file and generates images + * from the postscript file. The region file + * contains the x1,y1 x2,y2 extents of each + * image. + */ + +static void generateImages (char *regionFileName) +{ + pushBackBuffer *f=new pushBackBuffer(regionFileName); + char ch; + + while (f->putPB(f->getPB()) != eof) { + if (f->isString("grohtml-info:page")) { + int page = f->readInt(); + int x1 = f->readInt(); + int y1 = f->readInt(); + int x2 = f->readInt(); + int y2 = f->readInt(); + int maxx = max(f->readInt(), MAX_WIDTH*image_res); + char *name = f->readString(); + int res = POSTSCRIPTRES; // --fixme-- prefer (f->readInt()) providing that troff can discover the value + listOfImages.add(x1, y1, x2, y2, page, res, maxx, name); + while ((f->putPB(f->getPB()) != '\n') && + (f->putPB(f->getPB()) != eof)) { + ch = f->getPB(); + } + if (f->putPB(f->getPB()) == '\n') { + ch = f->getPB(); + } + } else { + /* + * write any error messages out to the user + */ + fputc(f->getPB(), stderr); + } + } +} + +/* + * replaceFd - replace a file descriptor, was, with, willbe. + */ + +static void replaceFd (int was, int willbe) +{ + int dupres; + + if (was != willbe) { + if (close(was)<0) { + sys_fatal("close"); + } + dupres = dup(willbe); + if (dupres != was) { + sys_fatal("dup"); + fprintf(stderr, "trying to replace fd=%d with %d dup used %d\n", was, willbe, dupres); + if (willbe == 1) { + fprintf(stderr, "likely that stdout should be opened before %d\n", was); + } + exit(1); + } + if (close(willbe) < 0) { + sys_fatal("close"); + } + } +} + +/* + * waitForChild - waits for child, pid, to exit. + */ + +static void waitForChild (PID_T pid) +{ + PID_T waitpd; + int status; + + waitpd = wait(&status); + if (waitpd != pid) + sys_fatal("wait"); +} + +/* + * alterDeviceTo - if toImage is set then the arg list is altered to include + * IMAGE_DEVICE and we invoke groff rather than troff. + * else + * set -Thtml and troff + */ + +static void alterDeviceTo (int argc, char *argv[], int toImage) +{ + int i=0; + + if (toImage) { + while (i < argc) { + if (strcmp(argv[i], "-Thtml") == 0) { + argv[i] = IMAGE_DEVICE; + } + i++; + } + argv[troff_arg] = "groff"; /* rather than troff */ + } else { + while (i < argc) { + if (strcmp(argv[i], IMAGE_DEVICE) == 0) { + argv[i] = "-Thtml"; + } + i++; + } + argv[troff_arg] = troff_command; /* use troff */ + } +} + +/* + * do_html - sets the troff number htmlflip and + * writes out the buffer to troff -Thtml + */ + +int char_buffer::do_html(int argc, char *argv[]) +{ + int pdes[2]; + PID_T pid; + + if (pipe(pdes) < 0) + sys_fatal("pipe"); + + alterDeviceTo(argc, argv, 0); + argv += troff_arg; // skip all arguments up to troff/groff + argc -= troff_arg; + +#if defined(DEBUG_HTML) + write_file_html(); + writeString("--------------- troff --------------------------\n"); + write_file_troff(); +#else + pid = fork(); + if (pid < 0) + sys_fatal("fork"); + + if (pid == 0) { + // child + replaceFd(0, pdes[0]); + // close end we are not using + if (close(pdes[1])<0) + sys_fatal("close"); + replaceFd(1, copyofstdoutfd); // and restore stdout + + execvp(argv[0], argv); + error("couldn't exec %1: %2", argv[0], strerror(errno), (char *)0); + fflush(stderr); /* just in case error() doesn't */ + exit(1); + } else { + // parent + +#if defined(DEBUGGING) + /* + * slight security risk so only enabled if compiled with defined(DEBUGGING) + */ + if (debug) { + replaceFd(1, creat(htmlFileName, S_IWUSR|S_IRUSR)); + write_file_html(); + } +#endif + replaceFd(1, pdes[1]); + // close end we are not using + if (close(pdes[0])<0) + sys_fatal("close"); + + write_file_html(); + waitForChild(pid); + } +#endif + return( 0 ); +} + +/* + * addps4html - appends -rps4html=1 onto the command list for troff. + */ + +char **addps4html (int argc, char *argv[]) +{ + char **new_argv = (char **)malloc((argc+2)*sizeof(char *)); + int i=0; + + while (i<argc) { + new_argv[i] = argv[i]; + i++; + } + new_argv[argc] = "-rps4html=1"; + argc++; + new_argv[argc] = NULL; + return( new_argv ); +} + +/* + * do_image - writes out the buffer to troff -Tps + */ + +int char_buffer::do_image(int argc, char *argv[]) +{ + PID_T pid; + int pdes[2]; + + if (pipe(pdes) < 0) + sys_fatal("pipe"); + + alterDeviceTo(argc, argv, 1); + argv += troff_arg; // skip all arguments up to troff/groff + argc -= troff_arg; + argv = addps4html(argc, argv); + argc++; + + pid = fork(); + if (pid == 0) { + // child + + int psFd = creat(psFileName, S_IWUSR|S_IRUSR); + int regionFd = creat(regionFileName, S_IWUSR|S_IRUSR); + + replaceFd(1, psFd); + replaceFd(0, pdes[0]); + replaceFd(2, regionFd); + + // close end we are not using + if (close(pdes[1])<0) + sys_fatal("close"); + + execvp(argv[0], argv); + error("couldn't exec %1: %2", argv[0], strerror(errno), (char *)0); + fflush(stderr); /* just in case error() doesn't */ + exit(1); + } else { + // parent + +#if defined(DEBUGGING) + /* + * slight security risk so only enabled if compiled with defined(DEBUGGING) + */ + if (debug) { + replaceFd(1, creat(troffFileName, S_IWUSR|S_IRUSR)); + write_file_troff(); + } +#endif + replaceFd(1, pdes[1]); + write_file_troff(); + waitForChild(pid); + } + return( 0 ); +} + +static char_buffer inputFile; + + +/* + * usage - emit usage arguments. + */ + +void usage(FILE *stream) +{ + fprintf(stream, "usage: %s troffname [-P-o vertical_image_offset] [-P-i image_resolution] [troff flags] [files]\n", program_name); + fprintf(stream, " vertical_image_offset (default %d/72 of an inch)\n", vertical_offset); + fprintf(stream, " image_resolution (default %d) pixels per inch\n", image_res); +} + +/* + * scanArguments - scans for -P-i and -P-o arguments. + */ + +int scanArguments (int argc, char **argv) +{ + int i=1; + + while (i<argc) { + if (strncmp(argv[i], "-i", 2) == 0) { + image_res = atoi((char *)(argv[i]+2)); + } else if (strncmp(argv[i], "-o", 2) == 0) { + vertical_offset = atoi((char *)(argv[i]+2)); + } else if ((strcmp(argv[i], "-v") == 0) + || (strcmp(argv[i], "--version") == 0)) { + extern const char *Version_string; + printf("GNU pre-grohtml (groff) version %s\n", Version_string); + exit(0); + } else if ((strcmp(argv[i], "-h") == 0) + || (strcmp(argv[i], "--help") == 0) + || (strcmp(argv[i], "-?") == 0)) { + usage(stdout); + exit(0); + } else if (strcmp(argv[i], "troff") == 0) { + /* remember troff argument number */ + troff_arg = i; +#if defined(DEBUGGING) + } else if (strcmp(argv[i], "-d") == 0) { + debug = TRUE; +#endif + } else if (argv[i][0] != '-') { + return( i ); + } + i++; + } + return( argc ); +} + +/* + * makeTempFiles - name the temporary files + */ + +static void makeTempFiles (void) +{ +#if defined(DEBUGGING) + psFileName = "/tmp/prehtml-ps"; + regionFileName = "/tmp/prehtml-region"; + imagePageStem = "/tmp/prehtml-page"; + troffFileName = "/tmp/prehtml-troff"; + htmlFileName = "/tmp/prehtml-html"; +#else + psFileName = mktemp(xtmptemplate("-ps-")); + regionFileName = mktemp(xtmptemplate("-regions-")); + imagePageStem = mktemp(xtmptemplate("-page-")); +#endif +} + +/* + * removeTempFiles - remove the temporary files + */ + +static void removeTempFiles (void) +{ +#if !defined(DEBUGGING) + remove(psFileName); + remove(regionFileName); +#endif +} + +/* + * findPrefix - finds the optional prefix to the groff utilities. + * It also builds the 'troff' executable name. + */ + +static void findPrefix (void) +{ + command_prefix = getenv("GROFF_COMMAND_PREFIX"); + if (!command_prefix) + command_prefix = PROG_PREFIX; + troff_command = (char *)malloc(strlen("troff")+strlen(command_prefix)+1); + strcpy(troff_command, command_prefix); + strcat(troff_command, "troff"); +} + + +int main(int argc, char **argv) +{ + program_name = argv[0]; + int i; + int found=0; + int ok=1; + + findPrefix(); + makeFileName(); + i = scanArguments(argc, argv); + while (i < argc) { + if (argv[i][0] != '-') { + /* found source file */ + ok = do_file(argv[i]); + if (! ok) { + return( 0 ); + } + found = 1; + } + i++; + } + + copyofstdoutfd=dup(stdoutfd); + + if (! found) { + do_file("-"); + } + makeTempFiles(); + ok = inputFile.do_image(argc, argv); + if (ok == 0) { + createAllPages(); + generateImages(regionFileName); + ok = inputFile.do_html(argc, argv); + removeAllPages(); + } + removeTempFiles(); + return ok; +} + +static int do_file(const char *filename) +{ + FILE *fp; + + current_filename = filename; + if (strcmp(filename, "-") == 0) { + fp = stdin; + } else { + fp = fopen(filename, "r"); + if (fp == 0) { + error("can't open `%1': %2", filename, strerror(errno)); + return 0; + } + } + + if (inputFile.read_file(fp)) { + } + + if (fp != stdin) + fclose(fp); + current_filename = 0; + return 1; +} diff --git a/contrib/groff/src/preproc/html/pre-html.h b/contrib/groff/src/preproc/html/pre-html.h new file mode 100644 index 0000000..f9a590c --- /dev/null +++ b/contrib/groff/src/preproc/html/pre-html.h @@ -0,0 +1,37 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. + Written by Gaius Mulley (gaius@glam.ac.uk). + +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. */ + +/* + * defines functions implemented within pre-html.c + */ + +#if !defined(PREHTMLH) +# define PREHTMLH +# if defined(PREHTMLC) +# define EXTERN +# else +# define EXTERN extern +# endif + + +extern void sys_fatal (const char *s); + +#undef EXTERN +#endif diff --git a/contrib/groff/src/preproc/html/pushbackbuffer.cc b/contrib/groff/src/preproc/html/pushbackbuffer.cc new file mode 100644 index 0000000..1d380f4 --- /dev/null +++ b/contrib/groff/src/preproc/html/pushbackbuffer.cc @@ -0,0 +1,337 @@ +// -*- C++ -*- +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. + Written by Gaius Mulley (gaius@glam.ac.uk). + +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 <stdio.h> +#include <signal.h> +#include <ctype.h> +#include <string.h> +#include <assert.h> +#include <stdlib.h> +#include <errno.h> +#include "lib.h" +#include "errarg.h" +#include "error.h" +#include "stringclass.h" +#include "posix.h" + +#include <errno.h> +#include <sys/types.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "pushbackbuffer.h" +#include "pre-html.h" + +#if !defined(TRUE) +# define TRUE (1==1) +#endif + +#if !defined(FALSE) +# define FALSE (1==0) +#endif + +# define ERROR(X) (fprintf(stderr, "%s:%d error %s\n", __FILE__, __LINE__, X) && \ + (fflush(stderr)) && localexit(1)) + + +#define MAXPUSHBACKSTACK 4096 /* maximum number of character that can be pushed back */ + + +/* + * constructor for pushBackBuffer + */ + +pushBackBuffer::pushBackBuffer (char *filename) +{ + charStack = (char *)malloc(MAXPUSHBACKSTACK); + if (charStack == 0) { + sys_fatal("malloc"); + } + stackPtr = 0; /* index to push back stack */ + debug = 0; + verbose = 0; + eofFound = FALSE; + lineNo = 1; + if (strcmp(filename, "") != 0) { + stdIn = dup(0); + close(0); + if (open(filename, O_RDONLY) != 0) { + sys_fatal("when trying to open file"); + } else { + fileName = filename; + } + } +} + +pushBackBuffer::~pushBackBuffer () +{ + int old; + + if (charStack != 0) { + free(charStack); + } + close(0); + /* restore stdin in file descriptor 0 */ + old = dup(stdIn); + close(stdIn); +} + +/* + * localexit - wraps exit with a return code to aid the ERROR macro. + */ + +int localexit (int i) +{ + exit(i); + return( 1 ); +} + +/* + * getPB - returns a character, possibly a pushed back character. + */ + +char pushBackBuffer::getPB (void) +{ + if (stackPtr>0) { + stackPtr--; + return( charStack[stackPtr] ); + } else { + char ch; + + if (read(0, &ch, 1) == 1) { + if (verbose) { + printf("%c", ch); + } + if (ch == '\n') { + lineNo++; + } + return( ch ); + } else { + eofFound = TRUE; + return( eof ); + } + } +} + +/* + * putPB - pushes a character onto the push back stack. + * The same character is returned. + */ + +char pushBackBuffer::putPB (char ch) +{ + if (stackPtr<MAXPUSHBACKSTACK) { + charStack[stackPtr] = ch ; + stackPtr++; + } else { + ERROR("max push back stack exceeded, increase MAXPUSHBACKSTACK constant"); + } + return( ch ); +} + +/* + * isWhite - returns TRUE if a white character is found. This character is NOT consumed. + */ + +static int isWhite (char ch) +{ + return( (ch==' ') || (ch == '\t') || (ch == '\n') ); +} + +/* + * skipToNewline - skips characters until a newline is seen. + */ + +void pushBackBuffer::skipToNewline (void) +{ + char ch; + + while ((putPB(getPB()) != '\n') && (! eofFound)) { + ch = getPB(); + } +} + +/* + * skipUntilToken - skips until a token is seen + */ + +void pushBackBuffer::skipUntilToken (void) +{ + char ch; + + while ((isWhite(putPB(getPB())) || (putPB(getPB()) == '#')) && (! eofFound)) { + ch = getPB(); + if (ch == '#') { + skipToNewline(); + } + } +} + +/* + * isString - returns TRUE if the string, s, matches the pushed back string. + * if TRUE is returned then this string is consumed, otherwise it is + * left alone. + */ + +int pushBackBuffer::isString (char *s) +{ + int length=strlen(s); + int i=0; + int j; + + while ((i<length) && (putPB(getPB())==s[i])) { + if (getPB() != s[i]) { + ERROR("assert failed"); + } + i++; + } + if (i==length) { + return( TRUE ); + } else { + i--; + while (i>=0) { + if (putPB(s[i]) != s[i]) { + ERROR("assert failed"); + } + i--; + } + } + return( FALSE ); +} + +/* + * isDigit - returns TRUE if the character, ch, is a digit. + */ + +static int isDigit (char ch) +{ + return( ((ch>='0') && (ch<='9')) ); +} + +/* + * isHexDigit - returns TRUE if the character, ch, is a hex digit. + */ + +static int isHexDigit (char ch) +{ + return( (isDigit(ch)) || ((ch>='a') && (ch<='f')) ); +} + +/* + * readInt - returns an integer from the input stream. + */ + +int pushBackBuffer::readInt (void) +{ + int c =0; + int i =0; + int s =1; + char ch=getPB(); + + while (isWhite(ch)) { + ch=getPB(); + } + // now read integer + + if (ch == '-') { + s = -1; + ch = getPB(); + } + while (isDigit(ch)) { + i *= 10; + if ((ch>='0') && (ch<='9')) { + i += (int)(ch-'0'); + } + ch = getPB(); + c++; + } + if (ch != putPB(ch)) { + ERROR("assert failed"); + } + return( i*s ); +} + +/* + * convertToFloat - converts integers, a and b into a.b + */ + +static float convertToFloat (int a, int b) +{ + int c=10; + float f; + + while (b>c) { + c *= 10; + } + f = ((float)a) + (((float)b)/((float)c)); + return( f ); +} + +/* + * readNumber - returns a float representing the word just read. + */ + +float pushBackBuffer::readNumber (void) +{ + int integer; + int fraction; + char ch; + float f; + + integer = readInt(); + if (putPB(getPB()) == '.') { + ch = getPB(); + fraction = readInt(); + f = convertToFloat(integer, fraction); + return( f ); + } else { + return( (float)integer ); + } +} + +/* + * readString - reads a string terminated by white space + * and returns a malloced area of memory containing + * a copy of the characters. + */ + +char *pushBackBuffer::readString (void) +{ + char buffer[MAXPUSHBACKSTACK]; + char *string = 0; + int i=0; + char ch=getPB(); + + while (isWhite(ch)) { + ch=getPB(); + } + while ((i < MAXPUSHBACKSTACK) && (! isWhite(ch)) && (! eofFound)) { + buffer[i] = ch; + i++; + ch = getPB(); + } + if (i < MAXPUSHBACKSTACK) { + buffer[i] = (char)0; + string = (char *)malloc(strlen(buffer)+1); + strcpy(string, buffer); + } + return( string ); +} diff --git a/contrib/groff/src/preproc/html/pushbackbuffer.h b/contrib/groff/src/preproc/html/pushbackbuffer.h new file mode 100644 index 0000000..93cb3f1 --- /dev/null +++ b/contrib/groff/src/preproc/html/pushbackbuffer.h @@ -0,0 +1,54 @@ +// -*- C -*- +/* Copyright (C) 2000, 2001 Free Software Foundation, Inc. + Written by Gaius Mulley (gaius@glam.ac.uk). + +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. */ + + +#define eof (char)-1 + + +/* + * defines the class and methods implemented within pushbackbuffer.cc + */ + +class pushBackBuffer +{ + private: + char *charStack; + int stackPtr; /* index to push back stack */ + int debug; + int verbose; + int eofFound; + char *fileName; + int lineNo; + int stdIn; + + public: + pushBackBuffer (char *); + ~ pushBackBuffer (); + char getPB (void); + char putPB (char ch); + void skipUntilToken (void); + void skipToNewline (void); + float readNumber (void); + int readInt (void); + char *readString (void); + int isString (char *string); +}; + + diff --git a/contrib/groff/src/preproc/pic/Makefile.sub b/contrib/groff/src/preproc/pic/Makefile.sub new file mode 100644 index 0000000..f1e2927 --- /dev/null +++ b/contrib/groff/src/preproc/pic/Makefile.sub @@ -0,0 +1,31 @@ +PROG=pic +MAN1=pic.n +XLIBS=$(LIBGROFF) +MLIB=$(LIBM) +OBJS=\ + pic.o \ + lex.o \ + main.o \ + object.o \ + common.o \ + troff.o \ + tex.o + # fig.o +CCSRCS=\ + $(srcdir)/lex.cc \ + $(srcdir)/main.cc \ + $(srcdir)/object.cc \ + $(srcdir)/common.cc \ + $(srcdir)/troff.cc \ + $(srcdir)/tex.cc +HDRS=\ + $(srcdir)/common.h \ + $(srcdir)/object.h \ + $(srcdir)/output.h \ + $(srcdir)/pic.h \ + $(srcdir)/position.h \ + $(srcdir)/text.h +GRAM=$(srcdir)/pic.y +YTABC=$(srcdir)/pic.cc +YTABH=$(srcdir)/pic_tab.h +NAMEPREFIX=$(g) diff --git a/contrib/groff/src/preproc/pic/TODO b/contrib/groff/src/preproc/pic/TODO new file mode 100644 index 0000000..2346b57 --- /dev/null +++ b/contrib/groff/src/preproc/pic/TODO @@ -0,0 +1,37 @@ +Dotted and dashed ellipses. + +In troff mode, dotted and dashed splines. + +Make DELIMITED have type lstr; this would allow us to give better +error messages for problems within the body of for and if constructs. + +In troff mode without -x, fake \D't' with .ps commands. + +Perhaps an option to set command char. + +Add an output class for dumb line printers. It wouldn't be pretty but +it would be better than nothing. Integrate it with texinfo. Useful +for groff -Tascii as well. + +Option to allow better positioning of arrowheads on arcs. + +Perhaps add PostScript output mode. + +Change the interface to the output class so that output devices have +the opportunity to handle arrowheads themselves. + +Consider whether the line thickness should scale. + +Consider whether the test in a for loop should be fuzzy (as it +apparently is in grap). + +Possibly change fillval so that zero is black. + +Provide a way of getting text blocks (positioned with `.in' rather +than \h), into pic. Should be possible to use block of diverted text +in pic. Possibly something similar to T{ and T} in tbl. + +Option to provide macro backtraces. + +Have a path that is searched by `copy' statement. Set by environment +variable or command line option. diff --git a/contrib/groff/src/preproc/pic/common.cc b/contrib/groff/src/preproc/pic/common.cc new file mode 100644 index 0000000..e83ef31 --- /dev/null +++ b/contrib/groff/src/preproc/pic/common.cc @@ -0,0 +1,497 @@ +// -*- 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 "pic.h" +#include "common.h" + +// output a dashed circle as a series of arcs + +void common_output::dashed_circle(const position ¢, double rad, + const line_type <) +{ + assert(lt.type == line_type::dashed); + line_type slt = lt; + slt.type = line_type::solid; + double dash_angle = lt.dash_width/rad; + int ndashes; + double gap_angle; + if (dash_angle >= M_PI/4.0) { + if (dash_angle < M_PI/2.0) { + gap_angle = M_PI/2.0 - dash_angle; + ndashes = 4; + } + else if (dash_angle < M_PI) { + gap_angle = M_PI - dash_angle; + ndashes = 2; + } + else { + circle(cent, rad, slt, -1.0); + return; + } + } + else { + ndashes = 4*int(ceil(M_PI/(4.0*dash_angle))); + gap_angle = (M_PI*2.0)/ndashes - dash_angle; + } + for (int i = 0; i < ndashes; i++) { + double start_angle = i*(dash_angle+gap_angle) - dash_angle/2.0; + solid_arc(cent, rad, start_angle, start_angle + dash_angle, lt); + } +} + +// output a dotted circle as a series of dots + +void common_output::dotted_circle(const position ¢, double rad, + const line_type <) +{ + assert(lt.type == line_type::dotted); + double gap_angle = lt.dash_width/rad; + int ndots; + if (gap_angle >= M_PI/2.0) { + // always have at least 2 dots + gap_angle = M_PI; + ndots = 2; + } + else { + ndots = 4*int(M_PI/(2.0*gap_angle)); + gap_angle = (M_PI*2.0)/ndots; + } + double ang = 0.0; + for (int i = 0; i < ndots; i++, ang += gap_angle) + dot(cent + position(cos(ang), sin(ang))*rad, lt); +} + +// return non-zero iff we can compute a center + +int compute_arc_center(const position &start, const position ¢, + const position &end, position *result) +{ + // This finds the point along the vector from start to cent that + // is equidistant between start and end. + distance c = cent - start; + distance e = end - start; + double n = c*e; + if (n == 0.0) + return 0; + *result = start + c*((e*e)/(2.0*n)); + return 1; +} + +// output a dashed arc as a series of arcs + +void common_output::dashed_arc(const position &start, const position ¢, + const position &end, const line_type <) +{ + assert(lt.type == line_type::dashed); + position c; + if (!compute_arc_center(start, cent, end, &c)) { + line(start, &end, 1, lt); + return; + } + distance start_offset = start - c; + distance end_offset = end - c; + double start_angle = atan2(start_offset.y, start_offset.x); + double end_angle = atan2(end_offset.y, end_offset.x); + double rad = hypot(c - start); + double dash_angle = lt.dash_width/rad; + double total_angle = end_angle - start_angle; + while (total_angle < 0) + total_angle += M_PI + M_PI; + if (total_angle <= dash_angle*2.0) { + solid_arc(cent, rad, start_angle, end_angle, lt); + return; + } + int ndashes = int((total_angle - dash_angle)/(dash_angle*2.0) + .5); + double dash_and_gap_angle = (total_angle - dash_angle)/ndashes; + for (int i = 0; i <= ndashes; i++) + solid_arc(cent, rad, start_angle + i*dash_and_gap_angle, + start_angle + i*dash_and_gap_angle + dash_angle, lt); +} + +// output a dotted arc as a series of dots + +void common_output::dotted_arc(const position &start, const position ¢, + const position &end, const line_type <) +{ + assert(lt.type == line_type::dotted); + position c; + if (!compute_arc_center(start, cent, end, &c)) { + line(start, &end, 1, lt); + return; + } + distance start_offset = start - c; + distance end_offset = end - c; + double start_angle = atan2(start_offset.y, start_offset.x); + double total_angle = atan2(end_offset.y, end_offset.x) - start_angle; + while (total_angle < 0) + total_angle += M_PI + M_PI; + double rad = hypot(c - start); + int ndots = int(total_angle/(lt.dash_width/rad) + .5); + if (ndots == 0) + dot(start, lt); + else { + for (int i = 0; i <= ndots; i++) { + double a = start_angle + (total_angle*i)/ndots; + dot(cent + position(cos(a), sin(a))*rad, lt); + } + } +} + +void common_output::solid_arc(const position ¢, double rad, + double start_angle, double end_angle, + const line_type <) +{ + line_type slt = lt; + slt.type = line_type::solid; + arc(cent + position(cos(start_angle), sin(start_angle))*rad, + cent, + cent + position(cos(end_angle), sin(end_angle))*rad, + slt); +} + + +void common_output::rounded_box(const position ¢, const distance &dim, + double rad, const line_type <, double fill) +{ + if (fill >= 0.0) + filled_rounded_box(cent, dim, rad, fill); + switch (lt.type) { + case line_type::invisible: + break; + case line_type::dashed: + dashed_rounded_box(cent, dim, rad, lt); + break; + case line_type::dotted: + dotted_rounded_box(cent, dim, rad, lt); + break; + case line_type::solid: + solid_rounded_box(cent, dim, rad, lt); + break; + default: + assert(0); + } +} + + +void common_output::dashed_rounded_box(const position ¢, + const distance &dim, double rad, + const line_type <) +{ + line_type slt = lt; + slt.type = line_type::solid; + + double hor_length = dim.x + (M_PI/2.0 - 2.0)*rad; + int n_hor_dashes = int(hor_length/(lt.dash_width*2.0) + .5); + double hor_gap_width = (n_hor_dashes != 0 + ? hor_length/n_hor_dashes - lt.dash_width + : 0.0); + + double vert_length = dim.y + (M_PI/2.0 - 2.0)*rad; + int n_vert_dashes = int(vert_length/(lt.dash_width*2.0) + .5); + double vert_gap_width = (n_vert_dashes != 0 + ? vert_length/n_vert_dashes - lt.dash_width + : 0.0); + // Note that each corner arc has to be split into two for dashing, + // because one part is dashed using vert_gap_width, and the other + // using hor_gap_width. + double offset = lt.dash_width/2.0; + dash_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad, + -M_PI/4.0, 0, slt, lt.dash_width, vert_gap_width, &offset); + dash_line(cent + position(dim.x/2.0, -dim.y/2.0 + rad), + cent + position(dim.x/2.0, dim.y/2.0 - rad), + slt, lt.dash_width, vert_gap_width, &offset); + dash_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad, + 0, M_PI/4.0, slt, lt.dash_width, vert_gap_width, &offset); + + offset = lt.dash_width/2.0; + dash_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad, + M_PI/4.0, M_PI/2, slt, lt.dash_width, hor_gap_width, &offset); + dash_line(cent + position(dim.x/2.0 - rad, dim.y/2.0), + cent + position(-dim.x/2.0 + rad, dim.y/2.0), + slt, lt.dash_width, hor_gap_width, &offset); + dash_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad, + M_PI/2, 3*M_PI/4.0, slt, lt.dash_width, hor_gap_width, &offset); + + offset = lt.dash_width/2.0; + dash_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad, + 3.0*M_PI/4.0, M_PI, slt, lt.dash_width, vert_gap_width, &offset); + dash_line(cent + position(-dim.x/2.0, dim.y/2.0 - rad), + cent + position(-dim.x/2.0, -dim.y/2.0 + rad), + slt, lt.dash_width, vert_gap_width, &offset); + dash_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad, + M_PI, 5.0*M_PI/4.0, slt, lt.dash_width, vert_gap_width, &offset); + + offset = lt.dash_width/2.0; + dash_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad, + 5*M_PI/4.0, 3*M_PI/2.0, slt, lt.dash_width, hor_gap_width, &offset); + dash_line(cent + position(-dim.x/2.0 + rad, -dim.y/2.0), + cent + position(dim.x/2.0 - rad, -dim.y/2.0), + slt, lt.dash_width, hor_gap_width, &offset); + dash_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad, + 3*M_PI/2, 7*M_PI/4, slt, lt.dash_width, hor_gap_width, &offset); +} + +// Used by dashed_rounded_box. + +void common_output::dash_arc(const position ¢, double rad, + double start_angle, double end_angle, + const line_type <, + double dash_width, double gap_width, + double *offsetp) +{ + double length = (end_angle - start_angle)*rad; + double pos = 0.0; + for (;;) { + if (*offsetp >= dash_width) { + double rem = dash_width + gap_width - *offsetp; + if (pos + rem > length) { + *offsetp += length - pos; + break; + } + else { + pos += rem; + *offsetp = 0.0; + } + } + else { + double rem = dash_width - *offsetp; + if (pos + rem > length) { + solid_arc(cent, rad, start_angle + pos/rad, end_angle, lt); + *offsetp += length - pos; + break; + } + else { + solid_arc(cent, rad, start_angle + pos/rad, + start_angle + (pos + rem)/rad, lt); + pos += rem; + *offsetp = dash_width; + } + } + } +} + +// Used by dashed_rounded_box. + +void common_output::dash_line(const position &start, const position &end, + const line_type <, + double dash_width, double gap_width, + double *offsetp) +{ + distance dist = end - start; + double length = hypot(dist); + if (length == 0.0) + return; + double pos = 0.0; + for (;;) { + if (*offsetp >= dash_width) { + double rem = dash_width + gap_width - *offsetp; + if (pos + rem > length) { + *offsetp += length - pos; + break; + } + else { + pos += rem; + *offsetp = 0.0; + } + } + else { + double rem = dash_width - *offsetp; + if (pos + rem > length) { + line(start + dist*(pos/length), &end, 1, lt); + *offsetp += length - pos; + break; + } + else { + position p(start + dist*((pos + rem)/length)); + line(start + dist*(pos/length), &p, 1, lt); + pos += rem; + *offsetp = dash_width; + } + } + } +} + +void common_output::dotted_rounded_box(const position ¢, + const distance &dim, double rad, + const line_type <) +{ + line_type slt = lt; + slt.type = line_type::solid; + + double hor_length = dim.x + (M_PI/2.0 - 2.0)*rad; + int n_hor_dots = int(hor_length/lt.dash_width + .5); + double hor_gap_width = (n_hor_dots != 0 + ? hor_length/n_hor_dots + : lt.dash_width); + + double vert_length = dim.y + (M_PI/2.0 - 2.0)*rad; + int n_vert_dots = int(vert_length/lt.dash_width + .5); + double vert_gap_width = (n_vert_dots != 0 + ? vert_length/n_vert_dots + : lt.dash_width); + double epsilon = lt.dash_width/(rad*100.0); + + double offset = 0.0; + dot_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad, + -M_PI/4.0, 0, slt, vert_gap_width, &offset); + dot_line(cent + position(dim.x/2.0, -dim.y/2.0 + rad), + cent + position(dim.x/2.0, dim.y/2.0 - rad), + slt, vert_gap_width, &offset); + dot_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad, + 0, M_PI/4.0 - epsilon, slt, vert_gap_width, &offset); + + offset = 0.0; + dot_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad, + M_PI/4.0, M_PI/2, slt, hor_gap_width, &offset); + dot_line(cent + position(dim.x/2.0 - rad, dim.y/2.0), + cent + position(-dim.x/2.0 + rad, dim.y/2.0), + slt, hor_gap_width, &offset); + dot_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad, + M_PI/2, 3*M_PI/4.0 - epsilon, slt, hor_gap_width, &offset); + + offset = 0.0; + dot_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad, + 3.0*M_PI/4.0, M_PI, slt, vert_gap_width, &offset); + dot_line(cent + position(-dim.x/2.0, dim.y/2.0 - rad), + cent + position(-dim.x/2.0, -dim.y/2.0 + rad), + slt, vert_gap_width, &offset); + dot_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad, + M_PI, 5.0*M_PI/4.0 - epsilon, slt, vert_gap_width, &offset); + + offset = 0.0; + dot_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad, + 5*M_PI/4.0, 3*M_PI/2.0, slt, hor_gap_width, &offset); + dot_line(cent + position(-dim.x/2.0 + rad, -dim.y/2.0), + cent + position(dim.x/2.0 - rad, -dim.y/2.0), + slt, hor_gap_width, &offset); + dot_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad, + 3*M_PI/2, 7*M_PI/4 - epsilon, slt, hor_gap_width, &offset); +} + +// Used by dotted_rounded_box. + +void common_output::dot_arc(const position ¢, double rad, + double start_angle, double end_angle, + const line_type <, double gap_width, + double *offsetp) +{ + double length = (end_angle - start_angle)*rad; + double pos = 0.0; + for (;;) { + if (*offsetp == 0.0) { + double ang = start_angle + pos/rad; + dot(cent + position(cos(ang), sin(ang))*rad, lt); + } + double rem = gap_width - *offsetp; + if (pos + rem > length) { + *offsetp += length - pos; + break; + } + else { + pos += rem; + *offsetp = 0.0; + } + } +} + +// Used by dotted_rounded_box. + +void common_output::dot_line(const position &start, const position &end, + const line_type <, double gap_width, + double *offsetp) +{ + distance dist = end - start; + double length = hypot(dist); + if (length == 0.0) + return; + double pos = 0.0; + for (;;) { + if (*offsetp == 0.0) + dot(start + dist*(pos/length), lt); + double rem = gap_width - *offsetp; + if (pos + rem > length) { + *offsetp += length - pos; + break; + } + else { + pos += rem; + *offsetp = 0.0; + } + } +} + + +void common_output::solid_rounded_box(const position ¢, + const distance &dim, double rad, + const line_type <) +{ + position tem = cent - dim/2.0; + arc(tem + position(0.0, rad), + tem + position(rad, rad), + tem + position(rad, 0.0), + lt); + tem = cent + position(-dim.x/2.0, dim.y/2.0); + arc(tem + position(rad, 0.0), + tem + position(rad, -rad), + tem + position(0.0, -rad), + lt); + tem = cent + dim/2.0; + arc(tem + position(0.0, -rad), + tem + position(-rad, -rad), + tem + position(-rad, 0.0), + lt); + tem = cent + position(dim.x/2.0, -dim.y/2.0); + arc(tem + position(-rad, 0.0), + tem + position(-rad, rad), + tem + position(0.0, rad), + lt); + position end; + end = cent + position(-dim.x/2.0, dim.y/2.0 - rad); + line(cent - dim/2.0 + position(0.0, rad), &end, 1, lt); + end = cent + position(dim.x/2.0 - rad, dim.y/2.0); + line(cent + position(-dim.x/2.0 + rad, dim.y/2.0), &end, 1, lt); + end = cent + position(dim.x/2.0, -dim.y/2.0 + rad); + line(cent + position(dim.x/2.0, dim.y/2.0 - rad), &end, 1, lt); + end = cent + position(-dim.x/2.0 + rad, -dim.y/2.0); + line(cent + position(dim.x/2.0 - rad, -dim.y/2.0), &end, 1, lt); +} + +void common_output::filled_rounded_box(const position ¢, + const distance &dim, double rad, + double fill) +{ + line_type ilt; + ilt.type = line_type::invisible; + circle(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad, ilt, fill); + circle(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad, ilt, fill); + circle(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad, ilt, fill); + circle(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad, ilt, fill); + position vec[4]; + vec[0] = cent + position(dim.x/2.0, dim.y/2.0 - rad); + vec[1] = cent + position(-dim.x/2.0, dim.y/2.0 - rad); + vec[2] = cent + position(-dim.x/2.0, -dim.y/2.0 + rad); + vec[3] = cent + position(dim.x/2.0, -dim.y/2.0 + rad); + polygon(vec, 4, ilt, fill); + vec[0] = cent + position(dim.x/2.0 - rad, dim.y/2.0); + vec[1] = cent + position(-dim.x/2.0 + rad, dim.y/2.0); + vec[2] = cent + position(-dim.x/2.0 + rad, -dim.y/2.0); + vec[3] = cent + position(dim.x/2.0 - rad, -dim.y/2.0); + polygon(vec, 4, ilt, fill); +} diff --git a/contrib/groff/src/preproc/pic/common.h b/contrib/groff/src/preproc/pic/common.h new file mode 100644 index 0000000..25a6e10 --- /dev/null +++ b/contrib/groff/src/preproc/pic/common.h @@ -0,0 +1,70 @@ +// -*- 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. */ + +class common_output : public output { +private: + void dash_line(const position &start, const position &end, + const line_type <, double dash_width, double gap_width, + double *offsetp); + void dash_arc(const position ¢, double rad, + double start_angle, double end_angle, const line_type <, + double dash_width, double gap_width, double *offsetp); + void dot_line(const position &start, const position &end, + const line_type <, double gap_width, double *offsetp); + void dot_arc(const position ¢, double rad, + double start_angle, double end_angle, const line_type <, + double gap_width, double *offsetp); +protected: + virtual void dot(const position &, const line_type &) = 0; + void dashed_circle(const position &, double rad, const line_type &); + void dotted_circle(const position &, double rad, const line_type &); + void dashed_arc(const position &, const position &, const position &, + const line_type &); + void dotted_arc(const position &, const position &, const position &, + const line_type &); + virtual void solid_arc(const position ¢, double rad, double start_angle, + double end_angle, const line_type <); + void dashed_rounded_box(const position &, const distance &, double, + const line_type &); + void dotted_rounded_box(const position &, const distance &, double, + const line_type &); + void solid_rounded_box(const position &, const distance &, double, + const line_type &); + void filled_rounded_box(const position &, const distance &, double, double); +public: + void start_picture(double sc, const position &ll, const position &ur) = 0; + void finish_picture() = 0; + void circle(const position &, double rad, const line_type &, double) = 0; + void text(const position &, text_piece *, int, double) = 0; + void line(const position &, const position *, int n, const line_type &) = 0; + void polygon(const position *, int n, const line_type &, double) = 0; + void spline(const position &, const position *, int n, + const line_type &) = 0; + void arc(const position &, const position &, const position &, + const line_type &) = 0; + void ellipse(const position &, const distance &, + const line_type &, double) = 0; + void rounded_box(const position &, const distance &, double, + const line_type &, double); +}; + +int compute_arc_center(const position &start, const position ¢, + const position &end, position *result); + diff --git a/contrib/groff/src/preproc/pic/lex.cc b/contrib/groff/src/preproc/pic/lex.cc new file mode 100644 index 0000000..5b6d439 --- /dev/null +++ b/contrib/groff/src/preproc/pic/lex.cc @@ -0,0 +1,1940 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2000 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 "pic.h" +#include "ptable.h" +#include "object.h" +#include "pic_tab.h" + +declare_ptable(char) +implement_ptable(char) + +PTABLE(char) macro_table; + +class macro_input : public input { + char *s; + char *p; +public: + macro_input(const char *); + ~macro_input(); + int get(); + int peek(); +}; + +class argument_macro_input : public input { + char *s; + char *p; + char *ap; + int argc; + char *argv[9]; +public: + argument_macro_input(const char *, int, char **); + ~argument_macro_input(); + int get(); + int peek(); +}; + +input::input() : next(0) +{ +} + +input::~input() +{ +} + +int input::get_location(const char **, int *) +{ + return 0; +} + +file_input::file_input(FILE *f, const char *fn) +: fp(f), filename(fn), lineno(0), ptr("") +{ +} + +file_input::~file_input() +{ + fclose(fp); +} + +int file_input::read_line() +{ + for (;;) { + line.clear(); + lineno++; + for (;;) { + int c = getc(fp); + if (c == EOF) + break; + else if (illegal_input_char(c)) + lex_error("illegal input character code %1", c); + else { + line += char(c); + if (c == '\n') + break; + } + } + if (line.length() == 0) + return 0; + if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'P' + && (line[2] == 'S' || line[2] == 'E' || line[2] == 'F') + && (line.length() == 3 || line[3] == ' ' || line[3] == '\n' + || compatible_flag))) { + line += '\0'; + ptr = line.contents(); + return 1; + } + } +} + +int file_input::get() +{ + if (*ptr != '\0' || read_line()) + return (unsigned char)*ptr++; + else + return EOF; +} + +int file_input::peek() +{ + if (*ptr != '\0' || read_line()) + return (unsigned char)*ptr; + else + return EOF; +} + +int file_input::get_location(const char **fnp, int *lnp) +{ + *fnp = filename; + *lnp = lineno; + return 1; +} + +macro_input::macro_input(const char *str) +{ + p = s = strsave(str); +} + +macro_input::~macro_input() +{ + a_delete s; +} + +int macro_input::get() +{ + if (p == 0 || *p == '\0') + return EOF; + else + return (unsigned char)*p++; +} + +int macro_input::peek() +{ + if (p == 0 || *p == '\0') + return EOF; + else + return (unsigned char)*p; +} + +// Character representing $1. Must be illegal input character. +#define ARG1 14 + +char *process_body(const char *body) +{ + char *s = strsave(body); + int j = 0; + for (int i = 0; s[i] != '\0'; i++) + if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') { + if (s[i+1] != '0') + s[j++] = ARG1 + s[++i] - '1'; + } + else + s[j++] = s[i]; + s[j] = '\0'; + return s; +} + + +argument_macro_input::argument_macro_input(const char *body, int ac, char **av) +: ap(0), argc(ac) +{ + for (int i = 0; i < argc; i++) + argv[i] = av[i]; + p = s = process_body(body); +} + + +argument_macro_input::~argument_macro_input() +{ + for (int i = 0; i < argc; i++) + a_delete argv[i]; + a_delete s; +} + +int argument_macro_input::get() +{ + if (ap) { + if (*ap != '\0') + return (unsigned char)*ap++; + ap = 0; + } + if (p == 0) + return EOF; + while (*p >= ARG1 && *p <= ARG1 + 8) { + int i = *p++ - ARG1; + if (i < argc && argv[i] != 0 && argv[i][0] != '\0') { + ap = argv[i]; + return (unsigned char)*ap++; + } + } + if (*p == '\0') + return EOF; + return (unsigned char)*p++; +} + +int argument_macro_input::peek() +{ + if (ap) { + if (*ap != '\0') + return (unsigned char)*ap; + ap = 0; + } + if (p == 0) + return EOF; + while (*p >= ARG1 && *p <= ARG1 + 8) { + int i = *p++ - ARG1; + if (i < argc && argv[i] != 0 && argv[i][0] != '\0') { + ap = argv[i]; + return (unsigned char)*ap; + } + } + if (*p == '\0') + return EOF; + return (unsigned char)*p; +} + +class input_stack { + static input *current_input; + static int bol_flag; +public: + static void push(input *); + static void clear(); + static int get_char(); + static int peek_char(); + static int get_location(const char **fnp, int *lnp); + static void push_back(unsigned char c, int was_bol = 0); + static int bol(); +}; + +input *input_stack::current_input = 0; +int input_stack::bol_flag = 0; + +inline int input_stack::bol() +{ + return bol_flag; +} + +void input_stack::clear() +{ + while (current_input != 0) { + input *tem = current_input; + current_input = current_input->next; + delete tem; + } + bol_flag = 1; +} + +void input_stack::push(input *in) +{ + in->next = current_input; + current_input = in; +} + +void lex_init(input *top) +{ + input_stack::clear(); + input_stack::push(top); +} + +void lex_cleanup() +{ + while (input_stack::get_char() != EOF) + ; +} + +int input_stack::get_char() +{ + while (current_input != 0) { + int c = current_input->get(); + if (c != EOF) { + bol_flag = c == '\n'; + return c; + } + // don't pop the top-level input off the stack + if (current_input->next == 0) + return EOF; + input *tem = current_input; + current_input = current_input->next; + delete tem; + } + return EOF; +} + +int input_stack::peek_char() +{ + while (current_input != 0) { + int c = current_input->peek(); + if (c != EOF) + return c; + if (current_input->next == 0) + return EOF; + input *tem = current_input; + current_input = current_input->next; + delete tem; + } + return EOF; +} + +class char_input : public input { + int c; +public: + char_input(int); + int get(); + int peek(); +}; + +char_input::char_input(int n) : c((unsigned char)n) +{ +} + +int char_input::get() +{ + int n = c; + c = EOF; + return n; +} + +int char_input::peek() +{ + return c; +} + +void input_stack::push_back(unsigned char c, int was_bol) +{ + push(new char_input(c)); + bol_flag = was_bol; +} + +int input_stack::get_location(const char **fnp, int *lnp) +{ + for (input *p = current_input; p; p = p->next) + if (p->get_location(fnp, lnp)) + return 1; + return 0; +} + +string context_buffer; + +string token_buffer; +double token_double; +int token_int; + +void interpolate_macro_with_args(const char *body) +{ + char *argv[9]; + int argc = 0; + int i; + for (i = 0; i < 9; i++) + argv[i] = 0; + int level = 0; + int c; + enum { NORMAL, IN_STRING, IN_STRING_QUOTED } state = NORMAL; + do { + token_buffer.clear(); + for (;;) { + c = input_stack::get_char(); + if (c == EOF) { + lex_error("end of input while scanning macro arguments"); + break; + } + if (state == NORMAL && level == 0 && (c == ',' || c == ')')) { + if (token_buffer.length() > 0) { + token_buffer += '\0'; + argv[argc] = strsave(token_buffer.contents()); + } + // for `foo()', argc = 0 + if (argc > 0 || c != ')' || i > 0) + argc++; + break; + } + token_buffer += char(c); + switch (state) { + case NORMAL: + if (c == '"') + state = IN_STRING; + else if (c == '(') + level++; + else if (c == ')') + level--; + break; + case IN_STRING: + if (c == '"') + state = NORMAL; + else if (c == '\\') + state = IN_STRING_QUOTED; + break; + case IN_STRING_QUOTED: + state = IN_STRING; + break; + } + } + } while (c != ')' && c != EOF); + input_stack::push(new argument_macro_input(body, argc, argv)); +} + +static int docmp(const char *s1, int n1, const char *s2, int n2) +{ + if (n1 < n2) { + int r = memcmp(s1, s2, n1); + return r ? r : -1; + } + else if (n1 > n2) { + int r = memcmp(s1, s2, n2); + return r ? r : 1; + } + else + return memcmp(s1, s2, n1); +} + +int lookup_keyword(const char *str, int len) +{ + static struct keyword { + const char *name; + int token; + } table[] = { + { "Here", HERE }, + { "above", ABOVE }, + { "aligned", ALIGNED }, + { "and", AND }, + { "arc", ARC }, + { "arrow", ARROW }, + { "at", AT }, + { "atan2", ATAN2 }, + { "below", BELOW }, + { "between", BETWEEN }, + { "bottom", BOTTOM }, + { "box", BOX }, + { "by", BY }, + { "ccw", CCW }, + { "center", CENTER }, + { "chop", CHOP }, + { "circle", CIRCLE }, + { "command", COMMAND }, + { "copy", COPY }, + { "cos", COS }, + { "cw", CW }, + { "dashed", DASHED }, + { "define", DEFINE }, + { "diam", DIAMETER }, + { "diameter", DIAMETER }, + { "do", DO }, + { "dotted", DOTTED }, + { "down", DOWN }, + { "ellipse", ELLIPSE }, + { "else", ELSE }, + { "end", END }, + { "exp", EXP }, + { "fill", FILL }, + { "filled", FILL }, + { "for", FOR }, + { "from", FROM }, + { "height", HEIGHT }, + { "ht", HEIGHT }, + { "if", IF }, + { "int", INT }, + { "invis", INVISIBLE }, + { "invisible", INVISIBLE }, + { "last", LAST }, + { "left", LEFT }, + { "line", LINE }, + { "ljust", LJUST }, + { "log", LOG }, + { "lower", LOWER }, + { "max", K_MAX }, + { "min", K_MIN }, + { "move", MOVE }, + { "of", OF }, + { "plot", PLOT }, + { "print", PRINT }, + { "rad", RADIUS }, + { "radius", RADIUS }, + { "rand", RAND }, + { "reset", RESET }, + { "right", RIGHT }, + { "rjust", RJUST }, + { "same", SAME }, + { "sh", SH }, + { "sin", SIN }, + { "solid", SOLID }, + { "spline", SPLINE }, + { "sprintf", SPRINTF }, + { "sqrt", SQRT }, + { "srand", SRAND }, + { "start", START }, + { "the", THE }, + { "then", THEN }, + { "thick", THICKNESS }, + { "thickness", THICKNESS }, + { "thru", THRU }, + { "to", TO }, + { "top", TOP }, + { "undef", UNDEF }, + { "until", UNTIL }, + { "up", UP }, + { "upper", UPPER }, + { "way", WAY }, + { "wid", WIDTH }, + { "width", WIDTH }, + { "with", WITH }, + }; + + const keyword *start = table; + const keyword *end = table + sizeof(table)/sizeof(table[0]); + while (start < end) { + // start <= target < end + const keyword *mid = start + (end - start)/2; + + int cmp = docmp(str, len, mid->name, strlen(mid->name)); + if (cmp == 0) + return mid->token; + if (cmp < 0) + end = mid; + else + start = mid + 1; + } + return 0; +} + +int get_token_after_dot(int c) +{ + // get_token deals with the case where c is a digit + switch (c) { + case 'h': + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 't') { + input_stack::get_char(); + context_buffer = ".ht"; + return DOT_HT; + } + else if (c == 'e') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'i') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'g') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'h') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 't') { + input_stack::get_char(); + context_buffer = ".height"; + return DOT_HT; + } + input_stack::push_back('h'); + } + input_stack::push_back('g'); + } + input_stack::push_back('i'); + } + input_stack::push_back('e'); + } + input_stack::push_back('h'); + return '.'; + case 'x': + input_stack::get_char(); + context_buffer = ".x"; + return DOT_X; + case 'y': + input_stack::get_char(); + context_buffer = ".y"; + return DOT_Y; + case 'c': + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'e') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'n') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 't') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'e') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'r') { + input_stack::get_char(); + context_buffer = ".center"; + return DOT_C; + } + input_stack::push_back('e'); + } + input_stack::push_back('t'); + } + input_stack::push_back('n'); + } + input_stack::push_back('e'); + } + context_buffer = ".c"; + return DOT_C; + case 'n': + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'e') { + input_stack::get_char(); + context_buffer = ".ne"; + return DOT_NE; + } + else if (c == 'w') { + input_stack::get_char(); + context_buffer = ".nw"; + return DOT_NW; + } + else { + context_buffer = ".n"; + return DOT_N; + } + break; + case 'e': + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'n') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'd') { + input_stack::get_char(); + context_buffer = ".end"; + return DOT_END; + } + input_stack::push_back('n'); + context_buffer = ".e"; + return DOT_E; + } + context_buffer = ".e"; + return DOT_E; + case 'w': + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'i') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'd') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 't') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'h') { + input_stack::get_char(); + context_buffer = ".width"; + return DOT_WID; + } + input_stack::push_back('t'); + } + context_buffer = ".wid"; + return DOT_WID; + } + input_stack::push_back('i'); + } + context_buffer = ".w"; + return DOT_W; + case 's': + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'e') { + input_stack::get_char(); + context_buffer = ".se"; + return DOT_SE; + } + else if (c == 'w') { + input_stack::get_char(); + context_buffer = ".sw"; + return DOT_SW; + } + else { + if (c == 't') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'a') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'r') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 't') { + input_stack::get_char(); + context_buffer = ".start"; + return DOT_START; + } + input_stack::push_back('r'); + } + input_stack::push_back('a'); + } + input_stack::push_back('t'); + } + context_buffer = ".s"; + return DOT_S; + } + break; + case 't': + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'o') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'p') { + input_stack::get_char(); + context_buffer = ".top"; + return DOT_N; + } + input_stack::push_back('o'); + } + context_buffer = ".t"; + return DOT_N; + case 'l': + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'e') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'f') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 't') { + input_stack::get_char(); + context_buffer = ".left"; + return DOT_W; + } + input_stack::push_back('f'); + } + input_stack::push_back('e'); + } + context_buffer = ".l"; + return DOT_W; + case 'r': + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'a') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'd') { + input_stack::get_char(); + context_buffer = ".rad"; + return DOT_RAD; + } + input_stack::push_back('a'); + } + else if (c == 'i') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'g') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'h') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 't') { + input_stack::get_char(); + context_buffer = ".right"; + return DOT_E; + } + input_stack::push_back('h'); + } + input_stack::push_back('g'); + } + input_stack::push_back('i'); + } + context_buffer = ".r"; + return DOT_E; + case 'b': + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'o') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 't') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 't') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'o') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'm') { + input_stack::get_char(); + context_buffer = ".bottom"; + return DOT_S; + } + input_stack::push_back('o'); + } + input_stack::push_back('t'); + } + context_buffer = ".bot"; + return DOT_S; + } + input_stack::push_back('o'); + } + context_buffer = ".b"; + return DOT_S; + default: + context_buffer = '.'; + return '.'; + } +} + +int get_token(int lookup_flag) +{ + context_buffer.clear(); + for (;;) { + int n = 0; + int bol = input_stack::bol(); + int c = input_stack::get_char(); + if (bol && c == command_char) { + token_buffer.clear(); + token_buffer += c; + // the newline is not part of the token + for (;;) { + c = input_stack::peek_char(); + if (c == EOF || c == '\n') + break; + input_stack::get_char(); + token_buffer += char(c); + } + context_buffer = token_buffer; + return COMMAND_LINE; + } + switch (c) { + case EOF: + return EOF; + case ' ': + case '\t': + break; + case '\\': + { + int d = input_stack::peek_char(); + if (d != '\n') { + context_buffer = '\\'; + return '\\'; + } + input_stack::get_char(); + break; + } + case '#': + do { + c = input_stack::get_char(); + } while (c != '\n' && c != EOF); + if (c == '\n') + context_buffer = '\n'; + return c; + case '"': + context_buffer = '"'; + token_buffer.clear(); + for (;;) { + c = input_stack::get_char(); + if (c == '\\') { + context_buffer += '\\'; + c = input_stack::peek_char(); + if (c == '"') { + input_stack::get_char(); + token_buffer += '"'; + context_buffer += '"'; + } + else + token_buffer += '\\'; + } + else if (c == '\n') { + error("newline in string"); + break; + } + else if (c == EOF) { + error("missing `\"'"); + break; + } + else if (c == '"') { + context_buffer += '"'; + break; + } + else { + context_buffer += char(c); + token_buffer += char(c); + } + } + return TEXT; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + int overflow = 0; + n = 0; + for (;;) { + if (n > (INT_MAX - 9)/10) { + overflow = 1; + break; + } + n *= 10; + n += c - '0'; + context_buffer += char(c); + c = input_stack::peek_char(); + if (c == EOF || !csdigit(c)) + break; + c = input_stack::get_char(); + } + token_double = n; + if (overflow) { + for (;;) { + token_double *= 10.0; + token_double += c - '0'; + context_buffer += char(c); + c = input_stack::peek_char(); + if (c == EOF || !csdigit(c)) + break; + c = input_stack::get_char(); + } + // if somebody asks for 1000000000000th, we will silently + // give them INT_MAXth + double temp = token_double; // work around gas 1.34/sparc bug + if (token_double > INT_MAX) + n = INT_MAX; + else + n = int(temp); + } + } + switch (c) { + case 'i': + case 'I': + context_buffer += char(c); + input_stack::get_char(); + return NUMBER; + case '.': + { + context_buffer += '.'; + input_stack::get_char(); + got_dot: + double factor = 1.0; + for (;;) { + c = input_stack::peek_char(); + if (!c == EOF || !csdigit(c)) + break; + input_stack::get_char(); + context_buffer += char(c); + factor /= 10.0; + if (c != '0') + token_double += factor*(c - '0'); + } + if (c != 'e' && c != 'E') { + if (c == 'i' || c == 'I') { + context_buffer += char(c); + input_stack::get_char(); + } + return NUMBER; + } + } + // fall through + case 'e': + case 'E': + { + int echar = c; + input_stack::get_char(); + c = input_stack::peek_char(); + int sign = '+'; + if (c == '+' || c == '-') { + sign = c; + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == EOF || !csdigit(c)) { + input_stack::push_back(sign); + input_stack::push_back(echar); + return NUMBER; + } + context_buffer += char(echar); + context_buffer += char(sign); + } + else { + if (c == EOF || !csdigit(c)) { + input_stack::push_back(echar); + return NUMBER; + } + context_buffer += char(echar); + } + input_stack::get_char(); + context_buffer += char(c); + n = c - '0'; + for (;;) { + c = input_stack::peek_char(); + if (c == EOF || !csdigit(c)) + break; + input_stack::get_char(); + context_buffer += char(c); + n = n*10 + (c - '0'); + } + if (sign == '-') + n = -n; + if (c == 'i' || c == 'I') { + context_buffer += char(c); + input_stack::get_char(); + } + token_double *= pow(10.0, n); + return NUMBER; + } + case 'n': + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'd') { + input_stack::get_char(); + token_int = n; + context_buffer += "nd"; + return ORDINAL; + } + input_stack::push_back('n'); + return NUMBER; + case 'r': + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'd') { + input_stack::get_char(); + token_int = n; + context_buffer += "rd"; + return ORDINAL; + } + input_stack::push_back('r'); + return NUMBER; + case 't': + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'h') { + input_stack::get_char(); + token_int = n; + context_buffer += "th"; + return ORDINAL; + } + input_stack::push_back('t'); + return NUMBER; + case 's': + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 't') { + input_stack::get_char(); + token_int = n; + context_buffer += "st"; + return ORDINAL; + } + input_stack::push_back('s'); + return NUMBER; + default: + return NUMBER; + } + break; + case '\'': + { + c = input_stack::peek_char(); + if (c == 't') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == 'h') { + input_stack::get_char(); + context_buffer = "'th"; + return TH; + } + else + input_stack::push_back('t'); + } + context_buffer = "'"; + return '\''; + } + case '.': + { + c = input_stack::peek_char(); + if (c != EOF && csdigit(c)) { + n = 0; + token_double = 0.0; + context_buffer = '.'; + goto got_dot; + } + return get_token_after_dot(c); + } + case '<': + c = input_stack::peek_char(); + if (c == '-') { + input_stack::get_char(); + c = input_stack::peek_char(); + if (c == '>') { + input_stack::get_char(); + context_buffer = "<->"; + return DOUBLE_ARROW_HEAD; + } + context_buffer = "<-"; + return LEFT_ARROW_HEAD; + } + else if (c == '=') { + input_stack::get_char(); + context_buffer = "<="; + return LESSEQUAL; + } + context_buffer = "<"; + return '<'; + case '-': + c = input_stack::peek_char(); + if (c == '>') { + input_stack::get_char(); + context_buffer = "->"; + return RIGHT_ARROW_HEAD; + } + context_buffer = "-"; + return '-'; + case '!': + c = input_stack::peek_char(); + if (c == '=') { + input_stack::get_char(); + context_buffer = "!="; + return NOTEQUAL; + } + context_buffer = "!"; + return '!'; + case '>': + c = input_stack::peek_char(); + if (c == '=') { + input_stack::get_char(); + context_buffer = ">="; + return GREATEREQUAL; + } + context_buffer = ">"; + return '>'; + case '=': + c = input_stack::peek_char(); + if (c == '=') { + input_stack::get_char(); + context_buffer = "=="; + return EQUALEQUAL; + } + context_buffer = "="; + return '='; + case '&': + c = input_stack::peek_char(); + if (c == '&') { + input_stack::get_char(); + context_buffer = "&&"; + return ANDAND; + } + context_buffer = "&"; + return '&'; + case '|': + c = input_stack::peek_char(); + if (c == '|') { + input_stack::get_char(); + context_buffer = "||"; + return OROR; + } + context_buffer = "|"; + return '|'; + default: + if (c != EOF && csalpha(c)) { + token_buffer.clear(); + token_buffer = c; + for (;;) { + c = input_stack::peek_char(); + if (c == EOF || (!csalnum(c) && c != '_')) + break; + input_stack::get_char(); + token_buffer += char(c); + } + int tok = lookup_keyword(token_buffer.contents(), + token_buffer.length()); + if (tok != 0) { + context_buffer = token_buffer; + return tok; + } + char *def = 0; + if (lookup_flag) { + token_buffer += '\0'; + def = macro_table.lookup(token_buffer.contents()); + token_buffer.set_length(token_buffer.length() - 1); + if (def) { + if (c == '(') { + input_stack::get_char(); + interpolate_macro_with_args(def); + } + else + input_stack::push(new macro_input(def)); + } + } + if (!def) { + context_buffer = token_buffer; + if (csupper(token_buffer[0])) + return LABEL; + else + return VARIABLE; + } + } + else { + context_buffer = char(c); + return (unsigned char)c; + } + break; + } + } +} + +int get_delimited() +{ + token_buffer.clear(); + int c = input_stack::get_char(); + while (c == ' ' || c == '\t' || c == '\n') + c = input_stack::get_char(); + if (c == EOF) { + lex_error("missing delimiter"); + return 0; + } + context_buffer = char(c); + int had_newline = 0; + int start = c; + int level = 0; + enum { NORMAL, IN_STRING, IN_STRING_QUOTED, DELIM_END } state = NORMAL; + for (;;) { + c = input_stack::get_char(); + if (c == EOF) { + lex_error("missing closing delimiter"); + return 0; + } + if (c == '\n') + had_newline = 1; + else if (!had_newline) + context_buffer += char(c); + switch (state) { + case NORMAL: + if (start == '{') { + if (c == '{') { + level++; + break; + } + if (c == '}') { + if (--level < 0) + state = DELIM_END; + break; + } + } + else { + if (c == start) { + state = DELIM_END; + break; + } + } + if (c == '"') + state = IN_STRING; + break; + case IN_STRING_QUOTED: + if (c == '\n') + state = NORMAL; + else + state = IN_STRING; + break; + case IN_STRING: + if (c == '"' || c == '\n') + state = NORMAL; + else if (c == '\\') + state = IN_STRING_QUOTED; + break; + case DELIM_END: + // This case it just to shut cfront 2.0 up. + default: + assert(0); + } + if (state == DELIM_END) + break; + token_buffer += c; + } + return 1; +} + +void do_define() +{ + int t = get_token(0); // do not expand what we are defining + if (t != VARIABLE && t != LABEL) { + lex_error("can only define variable or placename"); + return; + } + token_buffer += '\0'; + string nm = token_buffer; + const char *name = nm.contents(); + if (!get_delimited()) + return; + token_buffer += '\0'; + macro_table.define(name, strsave(token_buffer.contents())); +} + +void do_undef() +{ + int t = get_token(0); // do not expand what we are undefining + if (t != VARIABLE && t != LABEL) { + lex_error("can only define variable or placename"); + return; + } + token_buffer += '\0'; + macro_table.define(token_buffer.contents(), 0); +} + + +class for_input : public input { + char *var; + char *body; + double to; + int by_is_multiplicative; + double by; + const char *p; + int done_newline; +public: + for_input(char *, double, int, double, char *); + ~for_input(); + int get(); + int peek(); +}; + +for_input::for_input(char *vr, double t, int bim, double b, char *bd) +: var(vr), body(bd), to(t), by_is_multiplicative(bim), by(b), p(body), + done_newline(0) +{ +} + +for_input::~for_input() +{ + a_delete var; + a_delete body; +} + +int for_input::get() +{ + if (p == 0) + return EOF; + for (;;) { + if (*p != '\0') + return (unsigned char)*p++; + if (!done_newline) { + done_newline = 1; + return '\n'; + } + double val; + if (!lookup_variable(var, &val)) { + lex_error("body of `for' terminated enclosing block"); + return EOF; + } + if (by_is_multiplicative) + val *= by; + else + val += by; + define_variable(var, val); + if (val > to) { + p = 0; + return EOF; + } + p = body; + done_newline = 0; + } +} + +int for_input::peek() +{ + if (p == 0) + return EOF; + if (*p != '\0') + return (unsigned char)*p; + if (!done_newline) + return '\n'; + double val; + if (!lookup_variable(var, &val)) + return EOF; + if (by_is_multiplicative) { + if (val * by > to) + return EOF; + } + else { + if (val + by > to) + return EOF; + } + if (*body == '\0') + return EOF; + return (unsigned char)*body; +} + +void do_for(char *var, double from, double to, int by_is_multiplicative, + double by, char *body) +{ + define_variable(var, from); + if (from <= to) + input_stack::push(new for_input(var, to, by_is_multiplicative, by, body)); +} + + +void do_copy(const char *filename) +{ + errno = 0; + FILE *fp = fopen(filename, "r"); + if (fp == 0) { + lex_error("can't open `%1': %2", filename, strerror(errno)); + return; + } + input_stack::push(new file_input(fp, filename)); +} + +class copy_thru_input : public input { + int done; + char *body; + char *until; + const char *p; + const char *ap; + int argv[9]; + int argc; + string line; + int get_line(); + virtual int inget() = 0; +public: + copy_thru_input(const char *b, const char *u); + ~copy_thru_input(); + int get(); + int peek(); +}; + +class copy_file_thru_input : public copy_thru_input { + input *in; +public: + copy_file_thru_input(input *, const char *b, const char *u); + ~copy_file_thru_input(); + int inget(); +}; + +copy_file_thru_input::copy_file_thru_input(input *i, const char *b, + const char *u) +: copy_thru_input(b, u), in(i) +{ +} + +copy_file_thru_input::~copy_file_thru_input() +{ + delete in; +} + +int copy_file_thru_input::inget() +{ + if (!in) + return EOF; + else + return in->get(); +} + +class copy_rest_thru_input : public copy_thru_input { +public: + copy_rest_thru_input(const char *, const char *u); + int inget(); +}; + +copy_rest_thru_input::copy_rest_thru_input(const char *b, const char *u) +: copy_thru_input(b, u) +{ +} + +int copy_rest_thru_input::inget() +{ + while (next != 0) { + int c = next->get(); + if (c != EOF) + return c; + if (next->next == 0) + return EOF; + input *tem = next; + next = next->next; + delete tem; + } + return EOF; + +} + +copy_thru_input::copy_thru_input(const char *b, const char *u) +: done(0) +{ + ap = 0; + body = process_body(b); + p = 0; + until = strsave(u); +} + + +copy_thru_input::~copy_thru_input() +{ + a_delete body; + a_delete until; +} + +int copy_thru_input::get() +{ + if (ap) { + if (*ap != '\0') + return (unsigned char)*ap++; + ap = 0; + } + for (;;) { + if (p == 0) { + if (!get_line()) + break; + p = body; + } + if (*p == '\0') { + p = 0; + return '\n'; + } + while (*p >= ARG1 && *p <= ARG1 + 8) { + int i = *p++ - ARG1; + if (i < argc && line[argv[i]] != '\0') { + ap = line.contents() + argv[i]; + return (unsigned char)*ap++; + } + } + if (*p != '\0') + return (unsigned char)*p++; + } + return EOF; +} + +int copy_thru_input::peek() +{ + if (ap) { + if (*ap != '\0') + return (unsigned char)*ap; + ap = 0; + } + for (;;) { + if (p == 0) { + if (!get_line()) + break; + p = body; + } + if (*p == '\0') + return '\n'; + while (*p >= ARG1 && *p <= ARG1 + 8) { + int i = *p++ - ARG1; + if (i < argc && line[argv[i]] != '\0') { + ap = line.contents() + argv[i]; + return (unsigned char)*ap; + } + } + if (*p != '\0') + return (unsigned char)*p; + } + return EOF; +} + +int copy_thru_input::get_line() +{ + if (done) + return 0; + line.clear(); + argc = 0; + int c = inget(); + for (;;) { + while (c == ' ') + c = inget(); + if (c == EOF || c == '\n') + break; + if (argc == 9) { + do { + c = inget(); + } while (c != '\n' && c != EOF); + break; + } + argv[argc++] = line.length(); + do { + line += char(c); + c = inget(); + } while (c != ' ' && c != '\n'); + line += '\0'; + } + if (until != 0 && argc > 0 && strcmp(&line[argv[0]], until) == 0) { + done = 1; + return 0; + } + return argc > 0 || c == '\n'; +} + +class simple_file_input : public input { + const char *filename; + int lineno; + FILE *fp; +public: + simple_file_input(FILE *, const char *); + ~simple_file_input(); + int get(); + int peek(); + int get_location(const char **, int *); +}; + +simple_file_input::simple_file_input(FILE *p, const char *s) +: filename(s), lineno(1), fp(p) +{ +} + +simple_file_input::~simple_file_input() +{ + // don't delete the filename + fclose(fp); +} + +int simple_file_input::get() +{ + int c = getc(fp); + while (illegal_input_char(c)) { + error("illegal input character code %1", c); + c = getc(fp); + } + if (c == '\n') + lineno++; + return c; +} + +int simple_file_input::peek() +{ + int c = getc(fp); + while (illegal_input_char(c)) { + error("illegal input character code %1", c); + c = getc(fp); + } + if (c != EOF) + ungetc(c, fp); + return c; +} + +int simple_file_input::get_location(const char **fnp, int *lnp) +{ + *fnp = filename; + *lnp = lineno; + return 1; +} + + +void copy_file_thru(const char *filename, const char *body, const char *until) +{ + errno = 0; + FILE *fp = fopen(filename, "r"); + if (fp == 0) { + lex_error("can't open `%1': %2", filename, strerror(errno)); + return; + } + input *in = new copy_file_thru_input(new simple_file_input(fp, filename), + body, until); + input_stack::push(in); +} + +void copy_rest_thru(const char *body, const char *until) +{ + input_stack::push(new copy_rest_thru_input(body, until)); +} + +void push_body(const char *s) +{ + input_stack::push(new char_input('\n')); + input_stack::push(new macro_input(s)); +} + +int delim_flag = 0; + +char *get_thru_arg() +{ + int c = input_stack::peek_char(); + while (c == ' ') { + input_stack::get_char(); + c = input_stack::peek_char(); + } + if (c != EOF && csalpha(c)) { + // looks like a macro + input_stack::get_char(); + token_buffer = c; + for (;;) { + c = input_stack::peek_char(); + if (c == EOF || (!csalnum(c) && c != '_')) + break; + input_stack::get_char(); + token_buffer += char(c); + } + context_buffer = token_buffer; + token_buffer += '\0'; + char *def = macro_table.lookup(token_buffer.contents()); + if (def) + return strsave(def); + // I guess it wasn't a macro after all; so push the macro name back. + // -2 because we added a '\0' + for (int i = token_buffer.length() - 2; i >= 0; i--) + input_stack::push_back(token_buffer[i]); + } + if (get_delimited()) { + token_buffer += '\0'; + return strsave(token_buffer.contents()); + } + else + return 0; +} + +int lookahead_token = -1; +string old_context_buffer; + +void do_lookahead() +{ + if (lookahead_token == -1) { + old_context_buffer = context_buffer; + lookahead_token = get_token(1); + } +} + +int yylex() +{ + if (delim_flag) { + assert(lookahead_token == -1); + if (delim_flag == 2) { + if ((yylval.str = get_thru_arg()) != 0) + return DELIMITED; + else + return 0; + } + else { + if (get_delimited()) { + token_buffer += '\0'; + yylval.str = strsave(token_buffer.contents()); + return DELIMITED; + } + else + return 0; + } + } + for (;;) { + int t; + if (lookahead_token >= 0) { + t = lookahead_token; + lookahead_token = -1; + } + else + t = get_token(1); + switch (t) { + case '\n': + return ';'; + case EOF: + return 0; + case DEFINE: + do_define(); + break; + case UNDEF: + do_undef(); + break; + case ORDINAL: + yylval.n = token_int; + return t; + case NUMBER: + yylval.x = token_double; + return t; + case COMMAND_LINE: + case TEXT: + token_buffer += '\0'; + if (!input_stack::get_location(&yylval.lstr.filename, + &yylval.lstr.lineno)) { + yylval.lstr.filename = 0; + yylval.lstr.lineno = -1; + } + yylval.lstr.str = strsave(token_buffer.contents()); + return t; + case LABEL: + case VARIABLE: + token_buffer += '\0'; + yylval.str = strsave(token_buffer.contents()); + return t; + case LEFT: + // change LEFT to LEFT_CORNER when followed by OF + old_context_buffer = context_buffer; + lookahead_token = get_token(1); + if (lookahead_token == OF) + return LEFT_CORNER; + else + return t; + case RIGHT: + // change RIGHT to RIGHT_CORNER when followed by OF + old_context_buffer = context_buffer; + lookahead_token = get_token(1); + if (lookahead_token == OF) + return RIGHT_CORNER; + else + return t; + case UPPER: + // recognise UPPER only before LEFT or RIGHT + old_context_buffer = context_buffer; + lookahead_token = get_token(1); + if (lookahead_token != LEFT && lookahead_token != RIGHT) { + yylval.str = strsave("upper"); + return VARIABLE; + } + else + return t; + case LOWER: + // recognise LOWER only before LEFT or RIGHT + old_context_buffer = context_buffer; + lookahead_token = get_token(1); + if (lookahead_token != LEFT && lookahead_token != RIGHT) { + yylval.str = strsave("lower"); + return VARIABLE; + } + else + return t; + case TOP: + // recognise TOP only before OF + old_context_buffer = context_buffer; + lookahead_token = get_token(1); + if (lookahead_token != OF) { + yylval.str = strsave("top"); + return VARIABLE; + } + else + return t; + case BOTTOM: + // recognise BOTTOM only before OF + old_context_buffer = context_buffer; + lookahead_token = get_token(1); + if (lookahead_token != OF) { + yylval.str = strsave("bottom"); + return VARIABLE; + } + else + return t; + case CENTER: + // recognise CENTER only before OF + old_context_buffer = context_buffer; + lookahead_token = get_token(1); + if (lookahead_token != OF) { + yylval.str = strsave("center"); + return VARIABLE; + } + else + return t; + case START: + // recognise START only before OF + old_context_buffer = context_buffer; + lookahead_token = get_token(1); + if (lookahead_token != OF) { + yylval.str = strsave("start"); + return VARIABLE; + } + else + return t; + case END: + // recognise END only before OF + old_context_buffer = context_buffer; + lookahead_token = get_token(1); + if (lookahead_token != OF) { + yylval.str = strsave("end"); + return VARIABLE; + } + else + return t; + default: + return t; + } + } +} + +void lex_error(const char *message, + const errarg &arg1, + const errarg &arg2, + const errarg &arg3) +{ + const char *filename; + int lineno; + if (!input_stack::get_location(&filename, &lineno)) + error(message, arg1, arg2, arg3); + else + error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3); +} + +void lex_warning(const char *message, + const errarg &arg1, + const errarg &arg2, + const errarg &arg3) +{ + const char *filename; + int lineno; + if (!input_stack::get_location(&filename, &lineno)) + warning(message, arg1, arg2, arg3); + else + warning_with_file_and_line(filename, lineno, message, arg1, arg2, arg3); +} + +void yyerror(const char *s) +{ + const char *filename; + int lineno; + const char *context = 0; + if (lookahead_token == -1) { + if (context_buffer.length() > 0) { + context_buffer += '\0'; + context = context_buffer.contents(); + } + } + else { + if (old_context_buffer.length() > 0) { + old_context_buffer += '\0'; + context = old_context_buffer.contents(); + } + } + if (!input_stack::get_location(&filename, &lineno)) { + if (context) { + if (context[0] == '\n' && context[1] == '\0') + error("%1 before newline", s); + else + error("%1 before `%2'", s, context); + } + else + error("%1 at end of picture", s); + } + else { + if (context) { + if (context[0] == '\n' && context[1] == '\0') + error_with_file_and_line(filename, lineno, "%1 before newline", s); + else + error_with_file_and_line(filename, lineno, "%1 before `%2'", + s, context); + } + else + error_with_file_and_line(filename, lineno, "%1 at end of picture", s); + } +} + diff --git a/contrib/groff/src/preproc/pic/main.cc b/contrib/groff/src/preproc/pic/main.cc new file mode 100644 index 0000000..87d2b93 --- /dev/null +++ b/contrib/groff/src/preproc/pic/main.cc @@ -0,0 +1,635 @@ +// -*- C++ -*- +/* Copyright (C) 1989-1992, 2000, 2001 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 "pic.h" + +extern int yyparse(); + +output *out; + +int flyback_flag; +int zero_length_line_flag = 0; +// Non-zero means we're using a groff driver. +int driver_extension_flag = 1; +int compatible_flag = 0; +int safer_flag = 1; +int command_char = '.'; // the character that introduces lines + // that should be passed through tranparently +static int lf_flag = 1; // non-zero if we should attempt to understand + // lines beginning with `.lf' + +// Non-zero means a parse error was encountered. +static int had_parse_error = 0; + +void do_file(const char *filename); + +class top_input : public input { + FILE *fp; + int bol; + int eof; + int push_back[3]; + int start_lineno; +public: + top_input(FILE *); + int get(); + int peek(); + int get_location(const char **, int *); +}; + +top_input::top_input(FILE *p) : fp(p), bol(1), eof(0) +{ + push_back[0] = push_back[1] = push_back[2] = EOF; + start_lineno = current_lineno; +} + +int top_input::get() +{ + if (eof) + return EOF; + if (push_back[2] != EOF) { + int c = push_back[2]; + push_back[2] = EOF; + return c; + } + else if (push_back[1] != EOF) { + int c = push_back[1]; + push_back[1] = EOF; + return c; + } + else if (push_back[0] != EOF) { + int c = push_back[0]; + push_back[0] = EOF; + return c; + } + int c = getc(fp); + while (illegal_input_char(c)) { + error("illegal input character code %1", int(c)); + c = getc(fp); + bol = 0; + } + if (bol && c == '.') { + c = getc(fp); + if (c == 'P') { + c = getc(fp); + if (c == 'F' || c == 'E') { + int d = getc(fp); + if (d != EOF) + ungetc(d, fp); + if (d == EOF || d == ' ' || d == '\n' || compatible_flag) { + eof = 1; + flyback_flag = c == 'F'; + return EOF; + } + push_back[0] = c; + push_back[1] = 'P'; + return '.'; + } + if (c == 'S') { + c = getc(fp); + if (c != EOF) + ungetc(c, fp); + if (c == EOF || c == ' ' || c == '\n' || compatible_flag) { + error("nested .PS"); + eof = 1; + return EOF; + } + push_back[0] = 'S'; + push_back[1] = 'P'; + return '.'; + } + if (c != EOF) + ungetc(c, fp); + push_back[0] = 'P'; + return '.'; + } + else { + if (c != EOF) + ungetc(c, fp); + return '.'; + } + } + if (c == '\n') { + bol = 1; + current_lineno++; + return '\n'; + } + bol = 0; + if (c == EOF) { + eof = 1; + error("end of file before .PE or .PF"); + error_with_file_and_line(current_filename, start_lineno - 1, + ".PS was here"); + } + return c; +} + +int top_input::peek() +{ + if (eof) + return EOF; + if (push_back[2] != EOF) + return push_back[2]; + if (push_back[1] != EOF) + return push_back[1]; + if (push_back[0] != EOF) + return push_back[0]; + int c = getc(fp); + while (illegal_input_char(c)) { + error("illegal input character code %1", int(c)); + c = getc(fp); + bol = 0; + } + if (bol && c == '.') { + c = getc(fp); + if (c == 'P') { + c = getc(fp); + if (c == 'F' || c == 'E') { + int d = getc(fp); + if (d != EOF) + ungetc(d, fp); + if (d == EOF || d == ' ' || d == '\n' || compatible_flag) { + eof = 1; + flyback_flag = c == 'F'; + return EOF; + } + push_back[0] = c; + push_back[1] = 'P'; + push_back[2] = '.'; + return '.'; + } + if (c == 'S') { + c = getc(fp); + if (c != EOF) + ungetc(c, fp); + if (c == EOF || c == ' ' || c == '\n' || compatible_flag) { + error("nested .PS"); + eof = 1; + return EOF; + } + push_back[0] = 'S'; + push_back[1] = 'P'; + push_back[2] = '.'; + return '.'; + } + if (c != EOF) + ungetc(c, fp); + push_back[0] = 'P'; + push_back[1] = '.'; + return '.'; + } + else { + if (c != EOF) + ungetc(c, fp); + push_back[0] = '.'; + return '.'; + } + } + if (c != EOF) + ungetc(c, fp); + if (c == '\n') + return '\n'; + return c; +} + +int top_input::get_location(const char **filenamep, int *linenop) +{ + *filenamep = current_filename; + *linenop = current_lineno; + return 1; +} + +void do_picture(FILE *fp) +{ + flyback_flag = 0; + int c; + while ((c = getc(fp)) == ' ') + ; + if (c == '<') { + string filename; + while ((c = getc(fp)) == ' ') + ; + while (c != EOF && c != ' ' && c != '\n') { + filename += char(c); + c = getc(fp); + } + if (c == ' ') { + do { + c = getc(fp); + } while (c != EOF && c != '\n'); + } + if (c == '\n') + current_lineno++; + if (filename.length() == 0) + error("missing filename after `<'"); + else { + filename += '\0'; + const char *old_filename = current_filename; + int old_lineno = current_lineno; + // filenames must be permanent + do_file(strsave(filename.contents())); + current_filename = old_filename; + current_lineno = old_lineno; + } + out->set_location(current_filename, current_lineno); + } + else { + out->set_location(current_filename, current_lineno); + string start_line; + while (c != EOF) { + if (c == '\n') { + current_lineno++; + break; + } + start_line += c; + c = getc(fp); + } + if (c == EOF) + return; + start_line += '\0'; + double wid, ht; + switch (sscanf(&start_line[0], "%lf %lf", &wid, &ht)) { + case 1: + ht = 0.0; + break; + case 2: + break; + default: + ht = wid = 0.0; + break; + } + out->set_desired_width_height(wid, ht); + out->set_args(start_line.contents()); + lex_init(new top_input(fp)); + if (yyparse()) { + had_parse_error = 1; + lex_error("giving up on this picture"); + } + parse_cleanup(); + lex_cleanup(); + + // skip the rest of the .PF/.PE line + while ((c = getc(fp)) != EOF && c != '\n') + ; + if (c == '\n') + current_lineno++; + out->set_location(current_filename, current_lineno); + } +} + +void do_file(const char *filename) +{ + FILE *fp; + if (strcmp(filename, "-") == 0) + fp = stdin; + else { + errno = 0; + fp = fopen(filename, "r"); + if (fp == 0) + fatal("can't open `%1': %2", filename, strerror(errno)); + } + out->set_location(filename, 1); + current_filename = filename; + current_lineno = 1; + enum { START, MIDDLE, HAD_DOT, HAD_P, HAD_PS, HAD_l, HAD_lf } state = START; + for (;;) { + int c = getc(fp); + if (c == EOF) + break; + switch (state) { + case START: + if (c == '.') + state = HAD_DOT; + else { + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case MIDDLE: + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + break; + case HAD_DOT: + if (c == 'P') + state = HAD_P; + else if (lf_flag && c == 'l') + state = HAD_l; + else { + putchar('.'); + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case HAD_P: + if (c == 'S') + state = HAD_PS; + else { + putchar('.'); + putchar('P'); + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case HAD_PS: + if (c == ' ' || c == '\n' || compatible_flag) { + ungetc(c, fp); + do_picture(fp); + state = START; + } + else { + fputs(".PS", stdout); + putchar(c); + state = MIDDLE; + } + break; + case HAD_l: + if (c == 'f') + state = HAD_lf; + else { + putchar('.'); + putchar('l'); + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case HAD_lf: + if (c == ' ' || c == '\n' || compatible_flag) { + string line; + while (c != EOF) { + line += c; + if (c == '\n') { + current_lineno++; + break; + } + c = getc(fp); + } + line += '\0'; + interpret_lf_args(line.contents()); + printf(".lf%s", line.contents()); + state = START; + } + else { + fputs(".lf", stdout); + putchar(c); + state = MIDDLE; + } + break; + default: + assert(0); + } + } + switch (state) { + case START: + break; + case MIDDLE: + putchar('\n'); + break; + case HAD_DOT: + fputs(".\n", stdout); + break; + case HAD_P: + fputs(".P\n", stdout); + break; + case HAD_PS: + fputs(".PS\n", stdout); + break; + case HAD_l: + fputs(".l\n", stdout); + break; + case HAD_lf: + fputs(".lf\n", stdout); + break; + } + if (fp != stdin) + fclose(fp); +} + +#ifdef FIG_SUPPORT +void do_whole_file(const char *filename) +{ + // Do not set current_filename. + FILE *fp; + if (strcmp(filename, "-") == 0) + fp = stdin; + else { + errno = 0; + fp = fopen(filename, "r"); + if (fp == 0) + fatal("can't open `%1': %2", filename, strerror(errno)); + } + lex_init(new file_input(fp, filename)); + if (yyparse()) + had_parse_error = 1; + parse_cleanup(); + lex_cleanup(); +} +#endif + +void usage(FILE *stream) +{ + fprintf(stream, "usage: %s [ -nvC ] [ filename ... ]\n", program_name); +#ifdef TEX_SUPPORT + fprintf(stream, " %s -t [ -cvzC ] [ filename ... ]\n", program_name); +#endif +#ifdef FIG_SUPPORT + fprintf(stream, " %s -f [ -v ] [ filename ]\n", program_name); +#endif +} + +#ifdef __MSDOS__ +static char *fix_program_name(char *arg, char *dflt) +{ + if (!arg) + return dflt; + char *prog = strchr(arg, '\0'); + for (;;) { + if (prog == arg) + break; + --prog; + if (strchr("\\/:", *prog)) { + prog++; + break; + } + } + char *ext = strchr(prog, '.'); + if (ext) + *ext = '\0'; + for (char *p = prog; *p; p++) + if ('A' <= *p && *p <= 'Z') + *p = 'a' + (*p - 'A'); + return prog; +} +#endif /* __MSDOS__ */ + +int main(int argc, char **argv) +{ +#ifdef __MSDOS__ + argv[0] = fix_program_name(argv[0], "pic"); +#endif /* __MSDOS__ */ + program_name = argv[0]; + static char stderr_buf[BUFSIZ]; + setbuf(stderr, stderr_buf); + int opt; +#ifdef TEX_SUPPORT + int tex_flag = 0; + int tpic_flag = 0; +#endif +#ifdef FIG_SUPPORT + int whole_file_flag = 0; + int fig_flag = 0; +#endif + static const struct option long_options[] = { + { "help", no_argument, 0, CHAR_MAX + 1 }, + { "version", no_argument, 0, 'v' }, + { NULL, 0, 0, 0 } + }; + while ((opt = getopt_long(argc, argv, "T:CDSUtcvnxzpf", long_options, NULL)) + != EOF) + switch (opt) { + case 'C': + compatible_flag = 1; + break; + case 'D': + case 'T': + break; + case 'S': + safer_flag = 1; + break; + case 'U': + safer_flag = 0; + break; + case 'f': +#ifdef FIG_SUPPORT + whole_file_flag++; + fig_flag++; +#else + fatal("fig support not included"); +#endif + break; + case 'n': + driver_extension_flag = 0; + break; + case 'p': + case 'x': + warning("-%1 option is obsolete", char(opt)); + break; + case 't': +#ifdef TEX_SUPPORT + tex_flag++; +#else + fatal("TeX support not included"); +#endif + break; + case 'c': +#ifdef TEX_SUPPORT + tpic_flag++; +#else + fatal("TeX support not included"); +#endif + break; + case 'v': + { + extern const char *Version_string; + printf("GNU pic (groff) version %s\n", Version_string); + exit(0); + break; + } + case 'z': + // zero length lines will be printed as dots + zero_length_line_flag++; + break; + case CHAR_MAX + 1: // --help + usage(stdout); + exit(0); + break; + case '?': + usage(stderr); + exit(1); + break; + default: + assert(0); + } + parse_init(); +#ifdef TEX_SUPPORT + if (tpic_flag) { + out = make_tpic_output(); + lf_flag = 0; + } + else if (tex_flag) { + out = make_tex_output(); + command_char = '\\'; + lf_flag = 0; + } + else +#endif +#ifdef FIG_SUPPORT + if (fig_flag) + out = make_fig_output(); + else +#endif + out = make_troff_output(); +#ifdef FIG_SUPPORT + if (whole_file_flag) { + if (optind >= argc) + do_whole_file("-"); + else if (argc - optind > 1) { + usage(stderr); + exit(1); + } else + do_whole_file(argv[optind]); + } + else { +#endif + if (optind >= argc) + do_file("-"); + else + for (int i = optind; i < argc; i++) + do_file(argv[i]); +#ifdef FIG_SUPPORT + } +#endif + delete out; + if (ferror(stdout) || fflush(stdout) < 0) + fatal("output error"); + return had_parse_error; +} + diff --git a/contrib/groff/src/preproc/pic/object.cc b/contrib/groff/src/preproc/pic/object.cc new file mode 100644 index 0000000..6b34633 --- /dev/null +++ b/contrib/groff/src/preproc/pic/object.cc @@ -0,0 +1,1833 @@ +// -*- 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 "pic.h" +#include "ptable.h" +#include "object.h" + +void print_object_list(object *); + +line_type::line_type() +: type(solid), thickness(1.0) +{ +} + +output::output() : args(0), desired_height(0.0), desired_width(0.0) +{ +} + +output::~output() +{ + a_delete args; +} + +void output::set_desired_width_height(double wid, double ht) +{ + desired_width = wid; + desired_height = ht; +} + +void output::set_args(const char *s) +{ + a_delete args; + if (s == 0 || *s == '\0') + args = 0; + else + args = strsave(s); +} + +void output::command(const char *, const char *, int) +{ +} + +void output::set_location(const char *, int) +{ +} + +int output::supports_filled_polygons() +{ + return 0; +} + +void output::begin_block(const position &, const position &) +{ +} + +void output::end_block() +{ +} + +double output::compute_scale(double sc, const position &ll, const position &ur) +{ + distance dim = ur - ll; + if (desired_width != 0.0 || desired_height != 0.0) { + sc = 0.0; + if (desired_width != 0.0) { + if (dim.x == 0.0) + error("width specified for picture with zero width"); + else + sc = dim.x/desired_width; + } + if (desired_height != 0.0) { + if (dim.y == 0.0) + error("height specified for picture with zero height"); + else { + double tem = dim.y/desired_height; + if (tem > sc) + sc = tem; + } + } + return sc == 0.0 ? 1.0 : sc; + } + else { + if (sc <= 0.0) + sc = 1.0; + distance sdim = dim/sc; + double max_width = 0.0; + lookup_variable("maxpswid", &max_width); + double max_height = 0.0; + lookup_variable("maxpsht", &max_height); + if ((max_width > 0.0 && sdim.x > max_width) + || (max_height > 0.0 && sdim.y > max_height)) { + double xscale = dim.x/max_width; + double yscale = dim.y/max_height; + return xscale > yscale ? xscale : yscale; + } + else + return sc; + } +} + +position::position(const place &pl) +{ + if (pl.obj != 0) { + // Use two statements to work around bug in SGI C++. + object *tem = pl.obj; + *this = tem->origin(); + } + else { + x = pl.x; + y = pl.y; + } +} + +position::position() : x(0.0), y(0.0) +{ +} + +position::position(double a, double b) : x(a), y(b) +{ +} + + +int operator==(const position &a, const position &b) +{ + return a.x == b.x && a.y == b.y; +} + +int operator!=(const position &a, const position &b) +{ + return a.x != b.x || a.y != b.y; +} + +position &position::operator+=(const position &a) +{ + x += a.x; + y += a.y; + return *this; +} + +position &position::operator-=(const position &a) +{ + x -= a.x; + y -= a.y; + return *this; +} + +position &position::operator*=(double a) +{ + x *= a; + y *= a; + return *this; +} + +position &position::operator/=(double a) +{ + x /= a; + y /= a; + return *this; +} + +position operator-(const position &a) +{ + return position(-a.x, -a.y); +} + +position operator+(const position &a, const position &b) +{ + return position(a.x + b.x, a.y + b.y); +} + +position operator-(const position &a, const position &b) +{ + return position(a.x - b.x, a.y - b.y); +} + +position operator/(const position &a, double n) +{ + return position(a.x/n, a.y/n); +} + +position operator*(const position &a, double n) +{ + return position(a.x*n, a.y*n); +} + +// dot product + +double operator*(const position &a, const position &b) +{ + return a.x*b.x + a.y*b.y; +} + +double hypot(const position &a) +{ + return hypot(a.x, a.y); +} + +struct arrow_head_type { + double height; + double width; + int solid; +}; + +void draw_arrow(const position &pos, const distance &dir, + const arrow_head_type &aht, const line_type <) +{ + double hyp = hypot(dir); + if (hyp == 0.0) { + error("cannot draw arrow on object with zero length"); + return; + } + position base = -dir; + base *= aht.height/hyp; + position n(dir.y, -dir.x); + n *= aht.width/(hyp*2.0); + line_type slt = lt; + slt.type = line_type::solid; + if (aht.solid && out->supports_filled_polygons()) { + position v[3]; + v[0] = pos; + v[1] = pos + base + n; + v[2] = pos + base - n; + // A value > 1 means fill with the current color. + out->polygon(v, 3, slt, 2.0); + } + else { + position v[2]; + v[0] = pos; + v[1] = pos + base + n; + out->line(pos + base - n, v, 2, slt); + } +} + +object::object() : prev(0), next(0) +{ +} + +object::~object() +{ +} + +void object::move_by(const position &) +{ +} + +void object::print() +{ +} + +void object::print_text() +{ +} + +int object::blank() +{ + return 0; +} + +struct bounding_box { + int blank; + position ll; + position ur; + + bounding_box(); + void encompass(const position &); +}; + +bounding_box::bounding_box() +: blank(1) +{ +} + +void bounding_box::encompass(const position &pos) +{ + if (blank) { + ll = pos; + ur = pos; + blank = 0; + } + else { + if (pos.x < ll.x) + ll.x = pos.x; + if (pos.y < ll.y) + ll.y = pos.y; + if (pos.x > ur.x) + ur.x = pos.x; + if (pos.y > ur.y) + ur.y = pos.y; + } +} + +void object::update_bounding_box(bounding_box *) +{ +} + +position object::origin() +{ + return position(0.0,0.0); +} + +position object::north() +{ + return origin(); +} + +position object::south() +{ + return origin(); +} + +position object::east() +{ + return origin(); +} + +position object::west() +{ + return origin(); +} + +position object::north_east() +{ + return origin(); +} + +position object::north_west() +{ + return origin(); +} + +position object::south_east() +{ + return origin(); +} + +position object::south_west() +{ + return origin(); +} + +position object::start() +{ + return origin(); +} + +position object::end() +{ + return origin(); +} + +position object::center() +{ + return origin(); +} + +double object::width() +{ + return 0.0; +} + +double object::radius() +{ + return 0.0; +} + +double object::height() +{ + return 0.0; +} + +place *object::find_label(const char *) +{ + return 0; +} + +segment::segment(const position &a, int n, segment *p) +: is_absolute(n), pos(a), next(p) +{ +} + +text_item::text_item(char *t, const char *fn, int ln) +: next(0), text(t), filename(fn), lineno(ln) +{ + adj.h = CENTER_ADJUST; + adj.v = NONE_ADJUST; +} + +text_item::~text_item() +{ + a_delete text; +} + +object_spec::object_spec(object_type t) : type(t) +{ + flags = 0; + tbl = 0; + segment_list = 0; + segment_width = segment_height = 0.0; + segment_is_absolute = 0; + text = 0; + with = 0; + dir = RIGHT_DIRECTION; +} + +object_spec::~object_spec() +{ + delete tbl; + while (segment_list != 0) { + segment *tem = segment_list; + segment_list = segment_list->next; + delete tem; + } + object *p = oblist.head; + while (p != 0) { + object *tem = p; + p = p->next; + delete tem; + } + while (text != 0) { + text_item *tem = text; + text = text->next; + delete tem; + } + delete with; +} + +class command_object : public object { + char *s; + const char *filename; + int lineno; +public: + command_object(char *, const char *, int); + ~command_object(); + object_type type() { return OTHER_OBJECT; } + void print(); +}; + +command_object::command_object(char *p, const char *fn, int ln) +: s(p), filename(fn), lineno(ln) +{ +} + +command_object::~command_object() +{ + a_delete s; +} + +void command_object::print() +{ + out->command(s, filename, lineno); +} + +object *make_command_object(char *s, const char *fn, int ln) +{ + return new command_object(s, fn, ln); +} + +class mark_object : public object { +public: + mark_object(); + object_type type(); +}; + +object *make_mark_object() +{ + return new mark_object(); +} + +mark_object::mark_object() +{ +} + +object_type mark_object::type() +{ + return MARK_OBJECT; +} + +object_list::object_list() : head(0), tail(0) +{ +} + +void object_list::append(object *obj) +{ + if (tail == 0) { + obj->next = obj->prev = 0; + head = tail = obj; + } + else { + obj->prev = tail; + obj->next = 0; + tail->next = obj; + tail = obj; + } +} + +void object_list::wrap_up_block(object_list *ol) +{ + object *p; + for (p = tail; p && p->type() != MARK_OBJECT; p = p->prev) + ; + assert(p != 0); + ol->head = p->next; + if (ol->head) { + ol->tail = tail; + ol->head->prev = 0; + } + else + ol->tail = 0; + tail = p->prev; + if (tail) + tail->next = 0; + else + head = 0; + delete p; +} + +text_piece::text_piece() +: text(0), filename(0), lineno(-1) +{ + adj.h = CENTER_ADJUST; + adj.v = NONE_ADJUST; +} + +text_piece::~text_piece() +{ + a_delete text; +} + +class graphic_object : public object { + int ntext; + text_piece *text; + int aligned; +protected: + line_type lt; +public: + graphic_object(); + ~graphic_object(); + object_type type() = 0; + void print_text(); + void add_text(text_item *, int); + void set_dotted(double); + void set_dashed(double); + void set_thickness(double); + void set_invisible(); + virtual void set_fill(double); +}; + +graphic_object::graphic_object() : ntext(0), text(0), aligned(0) +{ +} + +void graphic_object::set_dotted(double wid) +{ + lt.type = line_type::dotted; + lt.dash_width = wid; +} + +void graphic_object::set_dashed(double wid) +{ + lt.type = line_type::dashed; + lt.dash_width = wid; +} + +void graphic_object::set_thickness(double th) +{ + lt.thickness = th; +} + +void graphic_object::set_fill(double) +{ +} + +void graphic_object::set_invisible() +{ + lt.type = line_type::invisible; +} + +void graphic_object::add_text(text_item *t, int a) +{ + aligned = a; + int len = 0; + text_item *p; + for (p = t; p; p = p->next) + len++; + if (len == 0) + text = 0; + else { + text = new text_piece[len]; + for (p = t, len = 0; p; p = p->next, len++) { + text[len].text = p->text; + p->text = 0; + text[len].adj = p->adj; + text[len].filename = p->filename; + text[len].lineno = p->lineno; + } + } + ntext = len; +} + +void graphic_object::print_text() +{ + double angle = 0.0; + if (aligned) { + position d(end() - start()); + if (d.x != 0.0 || d.y != 0.0) + angle = atan2(d.y, d.x); + } + if (text != 0) + out->text(center(), text, ntext, angle); +} + +graphic_object::~graphic_object() +{ + if (text) + ad_delete(ntext) text; +} + +class rectangle_object : public graphic_object { +protected: + position cent; + position dim; +public: + rectangle_object(const position &); + double width() { return dim.x; } + double height() { return dim.y; } + position origin() { return cent; } + position center() { return cent; } + position north() { return position(cent.x, cent.y + dim.y/2.0); } + position south() { return position(cent.x, cent.y - dim.y/2.0); } + position east() { return position(cent.x + dim.x/2.0, cent.y); } + position west() { return position(cent.x - dim.x/2.0, cent.y); } + position north_east() { return position(cent.x + dim.x/2.0, cent.y + dim.y/2.0); } + position north_west() { return position(cent.x - dim.x/2.0, cent.y + dim.y/2.0); } + position south_east() { return position(cent.x + dim.x/2.0, cent.y - dim.y/2.0); } + position south_west() { return position(cent.x - dim.x/2.0, cent.y - dim.y/2.0); } + object_type type() = 0; + void update_bounding_box(bounding_box *); + void move_by(const position &); +}; + +rectangle_object::rectangle_object(const position &d) +: dim(d) +{ +} + +void rectangle_object::update_bounding_box(bounding_box *p) +{ + p->encompass(cent - dim/2.0); + p->encompass(cent + dim/2.0); +} + +void rectangle_object::move_by(const position &a) +{ + cent += a; +} + +class closed_object : public rectangle_object { +public: + closed_object(const position &); + object_type type() = 0; + void set_fill(double); +protected: + double fill; // < 0 if not filled +}; + +closed_object::closed_object(const position &pos) +: rectangle_object(pos), fill(-1.0) +{ +} + +void closed_object::set_fill(double f) +{ + assert(f >= 0.0); + fill = f; +} + + +class box_object : public closed_object { + double xrad; + double yrad; +public: + box_object(const position &, double); + object_type type() { return BOX_OBJECT; } + void print(); + position north_east(); + position north_west(); + position south_east(); + position south_west(); +}; + +box_object::box_object(const position &pos, double r) +: closed_object(pos), xrad(dim.x > 0 ? r : -r), yrad(dim.y > 0 ? r : -r) +{ +} + +const double CHOP_FACTOR = 1.0 - 1.0/M_SQRT2; + +position box_object::north_east() +{ + return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad, + cent.y + dim.y/2.0 - CHOP_FACTOR*yrad); +} + +position box_object::north_west() +{ + return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad, + cent.y + dim.y/2.0 - CHOP_FACTOR*yrad); +} + +position box_object::south_east() +{ + return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad, + cent.y - dim.y/2.0 + CHOP_FACTOR*yrad); +} + +position box_object::south_west() +{ + return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad, + cent.y - dim.y/2.0 + CHOP_FACTOR*yrad); +} + +void box_object::print() +{ + if (lt.type == line_type::invisible && fill < 0.0) + return; + if (xrad == 0.0) { + distance dim2 = dim/2.0; + position vec[4]; + vec[0] = cent + position(dim2.x, -dim2.y); + vec[1] = cent + position(dim2.x, dim2.y); + vec[2] = cent + position(-dim2.x, dim2.y); + vec[3] = cent + position(-dim2.x, -dim2.y); + out->polygon(vec, 4, lt, fill); + } + else { + distance abs_dim(fabs(dim.x), fabs(dim.y)); + out->rounded_box(cent, abs_dim, fabs(xrad), lt, fill); + } +} + +graphic_object *object_spec::make_box(position *curpos, direction *dirp) +{ + static double last_box_height; + static double last_box_width; + static double last_box_radius; + static int have_last_box = 0; + if (!(flags & HAS_HEIGHT)) { + if ((flags & IS_SAME) && have_last_box) + height = last_box_height; + else + lookup_variable("boxht", &height); + } + if (!(flags & HAS_WIDTH)) { + if ((flags & IS_SAME) && have_last_box) + width = last_box_width; + else + lookup_variable("boxwid", &width); + } + if (!(flags & HAS_RADIUS)) { + if ((flags & IS_SAME) && have_last_box) + radius = last_box_radius; + else + lookup_variable("boxrad", &radius); + } + last_box_width = width; + last_box_height = height; + last_box_radius = radius; + have_last_box = 1; + radius = fabs(radius); + if (radius*2.0 > fabs(width)) + radius = fabs(width/2.0); + if (radius*2.0 > fabs(height)) + radius = fabs(height/2.0); + box_object *p = new box_object(position(width, height), radius); + if (!position_rectangle(p, curpos, dirp)) { + delete p; + p = 0; + } + return p; +} + +// return non-zero for success + +int object_spec::position_rectangle(rectangle_object *p, + position *curpos, direction *dirp) +{ + position pos; + dir = *dirp; // ignore any direction in attribute list + position motion; + switch (dir) { + case UP_DIRECTION: + motion.y = p->height()/2.0; + break; + case DOWN_DIRECTION: + motion.y = -p->height()/2.0; + break; + case LEFT_DIRECTION: + motion.x = -p->width()/2.0; + break; + case RIGHT_DIRECTION: + motion.x = p->width()/2.0; + break; + default: + assert(0); + } + if (flags & HAS_AT) { + pos = at; + if (flags & HAS_WITH) { + place offset; + place here; + here.obj = p; + if (!with->follow(here, &offset)) + return 0; + pos -= offset; + } + } + else { + pos = *curpos; + pos += motion; + } + p->move_by(pos); + pos += motion; + *curpos = pos; + return 1; +} + +class block_object : public rectangle_object { + object_list oblist; + PTABLE(place) *tbl; +public: + block_object(const position &, const object_list &ol, PTABLE(place) *t); + ~block_object(); + place *find_label(const char *); + object_type type(); + void move_by(const position &); + void print(); +}; + +block_object::block_object(const position &d, const object_list &ol, + PTABLE(place) *t) +: rectangle_object(d), oblist(ol), tbl(t) +{ +} + +block_object::~block_object() +{ + delete tbl; + object *p = oblist.head; + while (p != 0) { + object *tem = p; + p = p->next; + delete tem; + } +} + +void block_object::print() +{ + out->begin_block(south_west(), north_east()); + print_object_list(oblist.head); + out->end_block(); +} + +static void adjust_objectless_places(PTABLE(place) *tbl, const position &a) +{ + // Adjust all the labels that aren't attached to objects. + PTABLE_ITERATOR(place) iter(tbl); + const char *key; + place *pl; + while (iter.next(&key, &pl)) + if (key && csupper(key[0]) && pl->obj == 0) { + pl->x += a.x; + pl->y += a.y; + } +} + +void block_object::move_by(const position &a) +{ + cent += a; + for (object *p = oblist.head; p; p = p->next) + p->move_by(a); + adjust_objectless_places(tbl, a); +} + + +place *block_object::find_label(const char *name) +{ + return tbl->lookup(name); +} + +object_type block_object::type() +{ + return BLOCK_OBJECT; +} + +graphic_object *object_spec::make_block(position *curpos, direction *dirp) +{ + bounding_box bb; + for (object *p = oblist.head; p; p = p->next) + p->update_bounding_box(&bb); + position dim; + if (!bb.blank) { + position m = -(bb.ll + bb.ur)/2.0; + for (object *p = oblist.head; p; p = p->next) + p->move_by(m); + adjust_objectless_places(tbl, m); + dim = bb.ur - bb.ll; + } + if (flags & HAS_WIDTH) + dim.x = width; + if (flags & HAS_HEIGHT) + dim.y = height; + block_object *block = new block_object(dim, oblist, tbl); + if (!position_rectangle(block, curpos, dirp)) { + delete block; + block = 0; + } + tbl = 0; + oblist.head = oblist.tail = 0; + return block; +} + +class text_object : public rectangle_object { +public: + text_object(const position &); + object_type type() { return TEXT_OBJECT; } +}; + +text_object::text_object(const position &d) +: rectangle_object(d) +{ +} + +graphic_object *object_spec::make_text(position *curpos, direction *dirp) +{ + if (!(flags & HAS_HEIGHT)) { + lookup_variable("textht", &height); + int nitems = 0; + for (text_item *t = text; t; t = t->next) + nitems++; + height *= nitems; + } + if (!(flags & HAS_WIDTH)) + lookup_variable("textwid", &width); + text_object *p = new text_object(position(width, height)); + if (!position_rectangle(p, curpos, dirp)) { + delete p; + p = 0; + } + return p; +} + + +class ellipse_object : public closed_object { +public: + ellipse_object(const position &); + position north_east() { return position(cent.x + dim.x/(M_SQRT2*2.0), + cent.y + dim.y/(M_SQRT2*2.0)); } + position north_west() { return position(cent.x - dim.x/(M_SQRT2*2.0), + cent.y + dim.y/(M_SQRT2*2.0)); } + position south_east() { return position(cent.x + dim.x/(M_SQRT2*2.0), + cent.y - dim.y/(M_SQRT2*2.0)); } + position south_west() { return position(cent.x - dim.x/(M_SQRT2*2.0), + cent.y - dim.y/(M_SQRT2*2.0)); } + double radius() { return dim.x/2.0; } + object_type type() { return ELLIPSE_OBJECT; } + void print(); +}; + +ellipse_object::ellipse_object(const position &d) +: closed_object(d) +{ +} + +void ellipse_object::print() +{ + if (lt.type == line_type::invisible && fill < 0.0) + return; + out->ellipse(cent, dim, lt, fill); +} + +graphic_object *object_spec::make_ellipse(position *curpos, direction *dirp) +{ + static double last_ellipse_height; + static double last_ellipse_width; + static int have_last_ellipse = 0; + if (!(flags & HAS_HEIGHT)) { + if ((flags & IS_SAME) && have_last_ellipse) + height = last_ellipse_height; + else + lookup_variable("ellipseht", &height); + } + if (!(flags & HAS_WIDTH)) { + if ((flags & IS_SAME) && have_last_ellipse) + width = last_ellipse_width; + else + lookup_variable("ellipsewid", &width); + } + last_ellipse_width = width; + last_ellipse_height = height; + have_last_ellipse = 1; + ellipse_object *p = new ellipse_object(position(width, height)); + if (!position_rectangle(p, curpos, dirp)) { + delete p; + return 0; + } + return p; +} + +class circle_object : public ellipse_object { +public: + circle_object(double); + object_type type() { return CIRCLE_OBJECT; } + void print(); +}; + +circle_object::circle_object(double diam) +: ellipse_object(position(diam, diam)) +{ +} + +void circle_object::print() +{ + if (lt.type == line_type::invisible && fill < 0.0) + return; + out->circle(cent, dim.x/2.0, lt, fill); +} + +graphic_object *object_spec::make_circle(position *curpos, direction *dirp) +{ + static double last_circle_radius; + static int have_last_circle = 0; + if (!(flags & HAS_RADIUS)) { + if ((flags & IS_SAME) && have_last_circle) + radius = last_circle_radius; + else + lookup_variable("circlerad", &radius); + } + last_circle_radius = radius; + have_last_circle = 1; + circle_object *p = new circle_object(radius*2.0); + if (!position_rectangle(p, curpos, dirp)) { + delete p; + return 0; + } + return p; +} + +class move_object : public graphic_object { + position strt; + position en; +public: + move_object(const position &s, const position &e); + position origin() { return en; } + object_type type() { return MOVE_OBJECT; } + void update_bounding_box(bounding_box *); + void move_by(const position &); +}; + +move_object::move_object(const position &s, const position &e) +: strt(s), en(e) +{ +} + +void move_object::update_bounding_box(bounding_box *p) +{ + p->encompass(strt); + p->encompass(en); +} + +void move_object::move_by(const position &a) +{ + strt += a; + en += a; +} + +graphic_object *object_spec::make_move(position *curpos, direction *dirp) +{ + static position last_move; + static int have_last_move = 0; + *dirp = dir; + // No need to look at at since `at' attribute sets `from' attribute. + position startpos = (flags & HAS_FROM) ? from : *curpos; + if (!(flags & HAS_SEGMENT)) { + if ((flags && IS_SAME) && have_last_move) + segment_pos = last_move; + else { + switch (dir) { + case UP_DIRECTION: + segment_pos.y = segment_height; + break; + case DOWN_DIRECTION: + segment_pos.y = -segment_height; + break; + case LEFT_DIRECTION: + segment_pos.x = -segment_width; + break; + case RIGHT_DIRECTION: + segment_pos.x = segment_width; + break; + default: + assert(0); + } + } + } + segment_list = new segment(segment_pos, segment_is_absolute, segment_list); + // Reverse the segment_list so that it's in forward order. + segment *old = segment_list; + segment_list = 0; + while (old != 0) { + segment *tem = old->next; + old->next = segment_list; + segment_list = old; + old = tem; + } + // Compute the end position. + position endpos = startpos; + for (segment *s = segment_list; s; s = s->next) + if (s->is_absolute) + endpos = s->pos; + else + endpos += s->pos; + have_last_move = 1; + last_move = endpos - startpos; + move_object *p = new move_object(startpos, endpos); + *curpos = endpos; + return p; +} + +class linear_object : public graphic_object { +protected: + char arrow_at_start; + char arrow_at_end; + arrow_head_type aht; + position strt; + position en; +public: + linear_object(const position &s, const position &e); + position start() { return strt; } + position end() { return en; } + void move_by(const position &); + void update_bounding_box(bounding_box *) = 0; + object_type type() = 0; + void add_arrows(int at_start, int at_end, const arrow_head_type &); +}; + +class line_object : public linear_object { +protected: + position *v; + int n; +public: + line_object(const position &s, const position &e, position *, int); + ~line_object(); + position origin() { return strt; } + position center() { return (strt + en)/2.0; } + position north() { return (en.y - strt.y) > 0 ? en : strt; } + position south() { return (en.y - strt.y) < 0 ? en : strt; } + position east() { return (en.x - strt.x) > 0 ? en : strt; } + position west() { return (en.x - strt.x) < 0 ? en : strt; } + object_type type() { return LINE_OBJECT; } + void update_bounding_box(bounding_box *); + void print(); + void move_by(const position &); +}; + +class arrow_object : public line_object { +public: + arrow_object(const position &, const position &, position *, int); + object_type type() { return ARROW_OBJECT; } +}; + +class spline_object : public line_object { +public: + spline_object(const position &, const position &, position *, int); + object_type type() { return SPLINE_OBJECT; } + void print(); + void update_bounding_box(bounding_box *); +}; + +linear_object::linear_object(const position &s, const position &e) +: arrow_at_start(0), arrow_at_end(0), strt(s), en(e) +{ +} + +void linear_object::move_by(const position &a) +{ + strt += a; + en += a; +} + +void linear_object::add_arrows(int at_start, int at_end, + const arrow_head_type &a) +{ + arrow_at_start = at_start; + arrow_at_end = at_end; + aht = a; +} + +line_object::line_object(const position &s, const position &e, + position *p, int i) +: linear_object(s, e), v(p), n(i) +{ +} + +void line_object::print() +{ + if (lt.type == line_type::invisible) + return; + out->line(strt, v, n, lt); + if (arrow_at_start) + draw_arrow(strt, strt-v[0], aht, lt); + if (arrow_at_end) + draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt); +} + +void line_object::update_bounding_box(bounding_box *p) +{ + p->encompass(strt); + for (int i = 0; i < n; i++) + p->encompass(v[i]); +} + +void line_object::move_by(const position &pos) +{ + linear_object::move_by(pos); + for (int i = 0; i < n; i++) + v[i] += pos; +} + +void spline_object::update_bounding_box(bounding_box *p) +{ + p->encompass(strt); + p->encompass(en); + /* + + If + + p1 = q1/2 + q2/2 + p2 = q1/6 + q2*5/6 + p3 = q2*5/6 + q3/6 + p4 = q2/2 + q3/2 + [ the points for the Bezier cubic ] + + and + + t = .5 + + then + + (1-t)^3*p1 + 3*t*(t - 1)^2*p2 + 3*t^2*(1-t)*p3 + t^3*p4 + [ the equation for the Bezier cubic ] + + = .125*q1 + .75*q2 + .125*q3 + + */ + for (int i = 1; i < n; i++) + p->encompass((i == 1 ? strt : v[i-2])*.125 + v[i-1]*.75 + v[i]*.125); +} + +arrow_object::arrow_object(const position &s, const position &e, + position *p, int i) +: line_object(s, e, p, i) +{ +} + +spline_object::spline_object(const position &s, const position &e, + position *p, int i) +: line_object(s, e, p, i) +{ +} + +void spline_object::print() +{ + if (lt.type == line_type::invisible) + return; + out->spline(strt, v, n, lt); + if (arrow_at_start) + draw_arrow(strt, strt-v[0], aht, lt); + if (arrow_at_end) + draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt); +} + +line_object::~line_object() +{ + a_delete v; +} + +linear_object *object_spec::make_line(position *curpos, direction *dirp) +{ + static position last_line; + static int have_last_line = 0; + *dirp = dir; + // No need to look at at since `at' attribute sets `from' attribute. + position startpos = (flags & HAS_FROM) ? from : *curpos; + if (!(flags & HAS_SEGMENT)) { + if ((flags & IS_SAME) && (type == LINE_OBJECT || type == ARROW_OBJECT) + && have_last_line) + segment_pos = last_line; + else + switch (dir) { + case UP_DIRECTION: + segment_pos.y = segment_height; + break; + case DOWN_DIRECTION: + segment_pos.y = -segment_height; + break; + case LEFT_DIRECTION: + segment_pos.x = -segment_width; + break; + case RIGHT_DIRECTION: + segment_pos.x = segment_width; + break; + default: + assert(0); + } + } + segment_list = new segment(segment_pos, segment_is_absolute, segment_list); + // reverse the segment_list so that it's in forward order + segment *old = segment_list; + segment_list = 0; + while (old != 0) { + segment *tem = old->next; + old->next = segment_list; + segment_list = old; + old = tem; + } + // Absolutise all movements + position endpos = startpos; + int nsegments = 0; + segment *s; + for (s = segment_list; s; s = s->next, nsegments++) + if (s->is_absolute) + endpos = s->pos; + else { + endpos += s->pos; + s->pos = endpos; + s->is_absolute = 1; // to avoid confusion + } + // handle chop + line_object *p = 0; + position *v = new position[nsegments]; + int i = 0; + for (s = segment_list; s; s = s->next, i++) + v[i] = s->pos; + if (flags & IS_DEFAULT_CHOPPED) { + lookup_variable("circlerad", &start_chop); + end_chop = start_chop; + flags |= IS_CHOPPED; + } + if (flags & IS_CHOPPED) { + position start_chop_vec, end_chop_vec; + if (start_chop != 0.0) { + start_chop_vec = v[0] - startpos; + start_chop_vec *= start_chop / hypot(start_chop_vec); + } + if (end_chop != 0.0) { + end_chop_vec = (v[nsegments - 1] + - (nsegments > 1 ? v[nsegments - 2] : startpos)); + end_chop_vec *= end_chop / hypot(end_chop_vec); + } + startpos += start_chop_vec; + v[nsegments - 1] -= end_chop_vec; + endpos -= end_chop_vec; + } + switch (type) { + case SPLINE_OBJECT: + p = new spline_object(startpos, endpos, v, nsegments); + break; + case ARROW_OBJECT: + p = new arrow_object(startpos, endpos, v, nsegments); + break; + case LINE_OBJECT: + p = new line_object(startpos, endpos, v, nsegments); + break; + default: + assert(0); + } + have_last_line = 1; + last_line = endpos - startpos; + *curpos = endpos; + return p; +} + +class arc_object : public linear_object { + int clockwise; + position cent; + double rad; +public: + arc_object(int, const position &, const position &, const position &); + position origin() { return cent; } + position center() { return cent; } + double radius() { return rad; } + position north(); + position south(); + position east(); + position west(); + position north_east(); + position north_west(); + position south_east(); + position south_west(); + void update_bounding_box(bounding_box *); + object_type type() { return ARC_OBJECT; } + void print(); + void move_by(const position &pos); +}; + +arc_object::arc_object(int cw, const position &s, const position &e, + const position &c) +: linear_object(s, e), clockwise(cw), cent(c) +{ + rad = hypot(c - s); +} + +void arc_object::move_by(const position &pos) +{ + linear_object::move_by(pos); + cent += pos; +} + +// we get arc corners from the corresponding circle + +position arc_object::north() +{ + position result(cent); + result.y += rad; + return result; +} + +position arc_object::south() +{ + position result(cent); + result.y -= rad; + return result; +} + +position arc_object::east() +{ + position result(cent); + result.x += rad; + return result; +} + +position arc_object::west() +{ + position result(cent); + result.x -= rad; + return result; +} + +position arc_object::north_east() +{ + position result(cent); + result.x += rad/M_SQRT2; + result.y += rad/M_SQRT2; + return result; +} + +position arc_object::north_west() +{ + position result(cent); + result.x -= rad/M_SQRT2; + result.y += rad/M_SQRT2; + return result; +} + +position arc_object::south_east() +{ + position result(cent); + result.x += rad/M_SQRT2; + result.y -= rad/M_SQRT2; + return result; +} + +position arc_object::south_west() +{ + position result(cent); + result.x -= rad/M_SQRT2; + result.y -= rad/M_SQRT2; + return result; +} + + +void arc_object::print() +{ + if (lt.type == line_type::invisible) + return; + if (clockwise) + out->arc(en, cent, strt, lt); + else + out->arc(strt, cent, en, lt); + if (arrow_at_start) { + position c = cent - strt; + draw_arrow(strt, + (clockwise ? position(c.y, -c.x) : position(-c.y, c.x)), + aht, lt); + } + if (arrow_at_end) { + position e = en - cent; + draw_arrow(en, + (clockwise ? position(e.y, -e.x) : position(-e.y, e.x)), + aht, lt); + } +} + +inline double max(double a, double b) +{ + return a > b ? a : b; +} + +void arc_object::update_bounding_box(bounding_box *p) +{ + p->encompass(strt); + p->encompass(en); + position start_offset = strt - cent; + if (start_offset.x == 0.0 && start_offset.y == 0.0) + return; + position end_offset = en - cent; + if (end_offset.x == 0.0 && end_offset.y == 0.0) + return; + double start_quad = atan2(start_offset.y, start_offset.x)/(M_PI/2.0); + double end_quad = atan2(end_offset.y, end_offset.x)/(M_PI/2.0); + if (clockwise) { + double temp = start_quad; + start_quad = end_quad; + end_quad = temp; + } + if (start_quad < 0.0) + start_quad += 4.0; + while (end_quad <= start_quad) + end_quad += 4.0; + double radius = max(hypot(start_offset), hypot(end_offset)); + for (int q = int(start_quad) + 1; q < end_quad; q++) { + position offset; + switch (q % 4) { + case 0: + offset.x = radius; + break; + case 1: + offset.y = radius; + break; + case 2: + offset.x = -radius; + break; + case 3: + offset.y = -radius; + break; + } + p->encompass(cent + offset); + } +} + +// We ignore the with attribute. The at attribute always refers to the center. + +linear_object *object_spec::make_arc(position *curpos, direction *dirp) +{ + *dirp = dir; + int cw = (flags & IS_CLOCKWISE) != 0; + // compute the start + position startpos; + if (flags & HAS_FROM) + startpos = from; + else + startpos = *curpos; + if (!(flags & HAS_RADIUS)) + lookup_variable("arcrad", &radius); + // compute the end + position endpos; + if (flags & HAS_TO) + endpos = to; + else { + position m(radius, radius); + // Adjust the signs. + if (cw) { + if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION) + m.x = -m.x; + if (dir == DOWN_DIRECTION || dir == RIGHT_DIRECTION) + m.y = -m.y; + *dirp = direction((dir + 3) % 4); + } + else { + if (dir == UP_DIRECTION || dir == LEFT_DIRECTION) + m.x = -m.x; + if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION) + m.y = -m.y; + *dirp = direction((dir + 1) % 4); + } + endpos = startpos + m; + } + // compute the center + position centerpos; + if (flags & HAS_AT) + centerpos = at; + else if (startpos == endpos) + centerpos = startpos; + else { + position h = (endpos - startpos)/2.0; + double d = hypot(h); + if (radius <= 0) + radius = .25; + // make the radius big enough + while (radius < d) + radius *= 2.0; + double alpha = acos(d/radius); + double theta = atan2(h.y, h.x); + if (cw) + theta -= alpha; + else + theta += alpha; + centerpos = position(cos(theta), sin(theta))*radius + startpos; + } + arc_object *p = new arc_object(cw, startpos, endpos, centerpos); + *curpos = endpos; + return p; +} + +graphic_object *object_spec::make_linear(position *curpos, direction *dirp) +{ + linear_object *obj; + if (type == ARC_OBJECT) + obj = make_arc(curpos, dirp); + else + obj = make_line(curpos, dirp); + if (type == ARROW_OBJECT + && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD)) == 0) + flags |= HAS_RIGHT_ARROW_HEAD; + if (obj && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD))) { + arrow_head_type a; + int at_start = (flags & HAS_LEFT_ARROW_HEAD) != 0; + int at_end = (flags & HAS_RIGHT_ARROW_HEAD) != 0; + if (flags & HAS_HEIGHT) + a.height = height; + else + lookup_variable("arrowht", &a.height); + if (flags & HAS_WIDTH) + a.width = width; + else + lookup_variable("arrowwid", &a.width); + double solid; + lookup_variable("arrowhead", &solid); + a.solid = solid != 0.0; + obj->add_arrows(at_start, at_end, a); + } + return obj; +} + +object *object_spec::make_object(position *curpos, direction *dirp) +{ + graphic_object *obj = 0; + switch (type) { + case BLOCK_OBJECT: + obj = make_block(curpos, dirp); + break; + case BOX_OBJECT: + obj = make_box(curpos, dirp); + break; + case TEXT_OBJECT: + obj = make_text(curpos, dirp); + break; + case ELLIPSE_OBJECT: + obj = make_ellipse(curpos, dirp); + break; + case CIRCLE_OBJECT: + obj = make_circle(curpos, dirp); + break; + case MOVE_OBJECT: + obj = make_move(curpos, dirp); + break; + case ARC_OBJECT: + case LINE_OBJECT: + case SPLINE_OBJECT: + case ARROW_OBJECT: + obj = make_linear(curpos, dirp); + break; + case MARK_OBJECT: + case OTHER_OBJECT: + default: + assert(0); + break; + } + if (obj) { + if (flags & IS_INVISIBLE) + obj->set_invisible(); + if (text != 0) + obj->add_text(text, (flags & IS_ALIGNED) != 0); + if (flags & IS_DOTTED) + obj->set_dotted(dash_width); + else if (flags & IS_DASHED) + obj->set_dashed(dash_width); + double th; + if (flags & HAS_THICKNESS) + th = thickness; + else + lookup_variable("linethick", &th); + obj->set_thickness(th); + if (flags & (IS_DEFAULT_FILLED|IS_FILLED)) { + if (flags & IS_DEFAULT_FILLED) + lookup_variable("fillval", &fill); + if (fill < 0.0) + error("bad fill value %1", fill); + else + obj->set_fill(fill); + } + } + return obj; +} + +struct string_list { + string_list *next; + char *str; + string_list(char *); + ~string_list(); +}; + +string_list::string_list(char *s) +: next(0), str(s) +{ +} + +string_list::~string_list() +{ + a_delete str; +} + +/* A path is used to hold the argument to the with attribute. For example, +`.nw' or `.A.s' or `.A'. The major operation on a path is to take a +place and follow the path through the place to place within the place. +Note that `.A.B.C.sw' will work. */ + +path::path(corner c) +: crn(c), label_list(0), ypath(0) +{ +} + +path::path(char *l, corner c) +: crn(c), ypath(0) +{ + label_list = new string_list(l); +} + +path::~path() +{ + while (label_list) { + string_list *tem = label_list; + label_list = label_list->next; + delete tem; + } + delete ypath; +} + +void path::append(corner c) +{ + assert(crn == 0); + crn = c; +} + +void path::append(char *s) +{ + string_list **p; + for (p = &label_list; *p; p = &(*p)->next) + ; + *p = new string_list(s); +} + +void path::set_ypath(path *p) +{ + ypath = p; +} + +// return non-zero for success + +int path::follow(const place &pl, place *result) const +{ + const place *p = &pl; + for (string_list *lb = label_list; lb; lb = lb->next) + if (p->obj == 0 || (p = p->obj->find_label(lb->str)) == 0) { + lex_error("object does not contain a place `%1'", lb->str); + return 0; + } + if (crn == 0 || p->obj == 0) + *result = *p; + else { + position pos = ((p->obj)->*(crn))(); + result->x = pos.x; + result->y = pos.y; + result->obj = 0; + } + if (ypath) { + place tem; + if (!ypath->follow(pl, &tem)) + return 0; + result->y = tem.y; + if (result->obj != tem.obj) + result->obj = 0; + } + return 1; +} + +void print_object_list(object *p) +{ + for (; p; p = p->next) { + p->print(); + p->print_text(); + } +} + +void print_picture(object *obj) +{ + bounding_box bb; + for (object *p = obj; p; p = p->next) + p->update_bounding_box(&bb); + double scale; + lookup_variable("scale", &scale); + out->start_picture(scale, bb.ll, bb.ur); + print_object_list(obj); + out->finish_picture(); +} + diff --git a/contrib/groff/src/preproc/pic/object.h b/contrib/groff/src/preproc/pic/object.h new file mode 100644 index 0000000..2748e81 --- /dev/null +++ b/contrib/groff/src/preproc/pic/object.h @@ -0,0 +1,217 @@ +// -*- 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. */ + +struct place; + +enum object_type { + OTHER_OBJECT, + BOX_OBJECT, + CIRCLE_OBJECT, + ELLIPSE_OBJECT, + ARC_OBJECT, + SPLINE_OBJECT, + LINE_OBJECT, + ARROW_OBJECT, + MOVE_OBJECT, + TEXT_OBJECT, + BLOCK_OBJECT, + MARK_OBJECT + }; + +struct bounding_box; + +struct object { + object *prev; + object *next; + object(); + virtual ~object(); + virtual position origin(); + virtual double width(); + virtual double radius(); + virtual double height(); + virtual position north(); + virtual position south(); + virtual position east(); + virtual position west(); + virtual position north_east(); + virtual position north_west(); + virtual position south_east(); + virtual position south_west(); + virtual position start(); + virtual position end(); + virtual position center(); + virtual place *find_label(const char *); + virtual void move_by(const position &); + virtual int blank(); + virtual void update_bounding_box(bounding_box *); + virtual object_type type() = 0; + virtual void print(); + virtual void print_text(); +}; + +typedef position (object::*corner)(); + +struct place { + object *obj; + double x, y; +}; + +struct string_list; + +class path { + corner crn; + string_list *label_list; + path *ypath; +public: + path(corner = 0); + path(char *, corner = 0); + ~path(); + void append(corner); + void append(char *); + void set_ypath(path *); + int follow(const place &, place *) const; +}; + +struct object_list { + object *head; + object *tail; + object_list(); + void append(object *); + void wrap_up_block(object_list *); +}; + +declare_ptable(place) + +// these go counterclockwise +enum direction { + RIGHT_DIRECTION, + UP_DIRECTION, + LEFT_DIRECTION, + DOWN_DIRECTION + }; + +struct graphics_state { + double x, y; + direction dir; +}; + +struct saved_state : public graphics_state { + saved_state *prev; + PTABLE(place) *tbl; +}; + + +struct text_item { + text_item *next; + char *text; + adjustment adj; + const char *filename; + int lineno; + + text_item(char *, const char *, int); + ~text_item(); +}; + +const unsigned long IS_DOTTED = 01; +const unsigned long IS_DASHED = 02; +const unsigned long IS_CLOCKWISE = 04; +const unsigned long IS_INVISIBLE = 020; +const unsigned long HAS_LEFT_ARROW_HEAD = 040; +const unsigned long HAS_RIGHT_ARROW_HEAD = 0100; +const unsigned long HAS_SEGMENT = 0200; +const unsigned long IS_SAME = 0400; +const unsigned long HAS_FROM = 01000; +const unsigned long HAS_AT = 02000; +const unsigned long HAS_WITH = 04000; +const unsigned long HAS_HEIGHT = 010000; +const unsigned long HAS_WIDTH = 020000; +const unsigned long HAS_RADIUS = 040000; +const unsigned long HAS_TO = 0100000; +const unsigned long IS_CHOPPED = 0200000; +const unsigned long IS_DEFAULT_CHOPPED = 0400000; +const unsigned long HAS_THICKNESS = 01000000; +const unsigned long IS_FILLED = 02000000; +const unsigned long IS_DEFAULT_FILLED = 04000000; +const unsigned long IS_ALIGNED = 010000000; + +struct segment { + int is_absolute; + position pos; + segment *next; + segment(const position &, int, segment *); +}; + +struct rectangle_object; +struct graphic_object; +struct linear_object; + +struct object_spec { + unsigned long flags; + object_type type; + object_list oblist; + PTABLE(place) *tbl; + double dash_width; + position from; + position to; + position at; + position by; + path *with; + text_item *text; + double height; + double radius; + double width; + double segment_width; + double segment_height; + double start_chop; + double end_chop; + double thickness; + double fill; + direction dir; + segment *segment_list; + position segment_pos; + int segment_is_absolute; + + object_spec(object_type); + ~object_spec(); + object *make_object(position *, direction *); + graphic_object *make_box(position *, direction *); + graphic_object *make_block(position *, direction *); + graphic_object *make_text(position *, direction *); + graphic_object *make_ellipse(position *, direction *); + graphic_object *make_circle(position *, direction *); + linear_object *make_line(position *, direction *); + linear_object *make_arc(position *, direction *); + graphic_object *make_linear(position *, direction *); + graphic_object *make_move(position *, direction *); + int position_rectangle(rectangle_object *p, position *curpos, + direction *dirp); +}; + + +object *make_object(object_spec *, position *, direction *); + +object *make_mark_object(); +object *make_command_object(char *, const char *, int); + +int lookup_variable(const char *name, double *val); +void define_variable(const char *name, double val); + +void print_picture(object *); + diff --git a/contrib/groff/src/preproc/pic/output.h b/contrib/groff/src/preproc/pic/output.h new file mode 100644 index 0000000..ac490db --- /dev/null +++ b/contrib/groff/src/preproc/pic/output.h @@ -0,0 +1,79 @@ +// -*- 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. */ + +struct line_type { + enum { invisible, solid, dotted, dashed } type; + double dash_width; + double thickness; // the thickness is in points + + line_type(); +}; + + +class output { +protected: + char *args; + double desired_height; // zero if no height specified + double desired_width; // zero if no depth specified + double compute_scale(double, const position &, const position &); +public: + output(); + virtual ~output(); + void set_desired_width_height(double wid, double ht); + void set_args(const char *); + virtual void start_picture(double sc, const position &ll, const position &ur) = 0; + virtual void finish_picture() = 0; + virtual void circle(const position &, double rad, + const line_type &, double) = 0; + virtual void text(const position &, text_piece *, int, double) = 0; + virtual void line(const position &, const position *, int n, + const line_type &) = 0; + virtual void polygon(const position *, int n, + const line_type &, double) = 0; + virtual void spline(const position &, const position *, int n, + const line_type &) = 0; + virtual void arc(const position &, const position &, const position &, + const line_type &) = 0; + virtual void ellipse(const position &, const distance &, + const line_type &, double) = 0; + virtual void rounded_box(const position &, const distance &, double, + const line_type &, double) = 0; + virtual void command(const char *, const char *, int); + virtual void set_location(const char *, int); + virtual int supports_filled_polygons(); + virtual void begin_block(const position &ll, const position &ur); + virtual void end_block(); +}; + +extern output *out; + +/* #define FIG_SUPPORT 1 */ +#define TEX_SUPPORT 1 + +output *make_troff_output(); + +#ifdef TEX_SUPPORT +output *make_tex_output(); +output *make_tpic_output(); +#endif /* TEX_SUPPORT */ + +#ifdef FIG_SUPPORT +output *make_fig_output(); +#endif /* FIG_SUPPORT */ diff --git a/contrib/groff/src/preproc/pic/pic.cc b/contrib/groff/src/preproc/pic/pic.cc new file mode 100644 index 0000000..f6d97bb --- /dev/null +++ b/contrib/groff/src/preproc/pic/pic.cc @@ -0,0 +1,5216 @@ +#ifndef lint +/*static char yysccsid[] = "from: @(#)yaccpar 1.9 (Berkeley) 02/21/93";*/ +static char yyrcsid[] = "$Id: pic.cc,v 1.3 2000/11/14 20:40:28 wlemb Exp $"; +#endif +#define YYBYACC 1 +#define YYMAJOR 1 +#define YYMINOR 9 +#define yyclearin (yychar=(-1)) +#define yyerrok (yyerrflag=0) +#define YYRECOVERING (yyerrflag!=0) +#define YYPREFIX "yy" +#line 20 "/home/cjk/groff/src/preproc/pic/pic.y" +#include "pic.h" +#include "ptable.h" +#include "object.h" + +extern int delim_flag; +extern void do_copy(const char *); +extern void copy_rest_thru(const char *, const char *); +extern void copy_file_thru(const char *, const char *, const char *); +extern void push_body(const char *); +extern void do_for(char *var, double from, double to, + int by_is_multiplicative, double by, char *body); +extern void do_lookahead(); + +#ifndef HAVE_FMOD +extern "C" { + double fmod(double, double); +} +#endif + +#undef rand +#undef srand +extern "C" { + int rand(); +#ifdef RET_TYPE_SRAND_IS_VOID + void srand(unsigned int); +#else + int srand(unsigned int); +#endif +} + +/* Maximum number of characters produced by printf("%g") */ +#define GDIGITS 14 + +int yylex(); +void yyerror(const char *); + +void reset(const char *nm); +void reset_all(); + +place *lookup_label(const char *); +void define_label(const char *label, const place *pl); + +direction current_direction; +position current_position; + +implement_ptable(place) + +PTABLE(place) top_table; + +PTABLE(place) *current_table = &top_table; +saved_state *current_saved_state = 0; + +object_list olist; + +const char *ordinal_postfix(int n); +const char *object_type_name(object_type type); +char *format_number(const char *form, double n); +char *do_sprintf(const char *form, const double *v, int nv); + +#line 82 "/home/cjk/groff/src/preproc/pic/pic.y" +typedef union { + char *str; + int n; + double x; + struct { double x, y; } pair; + struct { double x; char *body; } if_data; + struct { char *str; const char *filename; int lineno; } lstr; + struct { double *v; int nv; int maxv; } dv; + struct { double val; int is_multiplicative; } by; + place pl; + object *obj; + corner crn; + path *pth; + object_spec *spec; + saved_state *pstate; + graphics_state state; + object_type obtype; +} YYSTYPE; +#line 92 "y.tab.c" +#define LABEL 257 +#define VARIABLE 258 +#define NUMBER 259 +#define TEXT 260 +#define COMMAND_LINE 261 +#define DELIMITED 262 +#define ORDINAL 263 +#define TH 264 +#define LEFT_ARROW_HEAD 265 +#define RIGHT_ARROW_HEAD 266 +#define DOUBLE_ARROW_HEAD 267 +#define LAST 268 +#define UP 269 +#define DOWN 270 +#define LEFT 271 +#define RIGHT 272 +#define BOX 273 +#define CIRCLE 274 +#define ELLIPSE 275 +#define ARC 276 +#define LINE 277 +#define ARROW 278 +#define MOVE 279 +#define SPLINE 280 +#define HEIGHT 281 +#define RADIUS 282 +#define WIDTH 283 +#define DIAMETER 284 +#define FROM 285 +#define TO 286 +#define AT 287 +#define WITH 288 +#define BY 289 +#define THEN 290 +#define SOLID 291 +#define DOTTED 292 +#define DASHED 293 +#define CHOP 294 +#define SAME 295 +#define INVISIBLE 296 +#define LJUST 297 +#define RJUST 298 +#define ABOVE 299 +#define BELOW 300 +#define OF 301 +#define THE 302 +#define WAY 303 +#define BETWEEN 304 +#define AND 305 +#define HERE 306 +#define DOT_N 307 +#define DOT_E 308 +#define DOT_W 309 +#define DOT_S 310 +#define DOT_NE 311 +#define DOT_SE 312 +#define DOT_NW 313 +#define DOT_SW 314 +#define DOT_C 315 +#define DOT_START 316 +#define DOT_END 317 +#define DOT_X 318 +#define DOT_Y 319 +#define DOT_HT 320 +#define DOT_WID 321 +#define DOT_RAD 322 +#define SIN 323 +#define COS 324 +#define ATAN2 325 +#define LOG 326 +#define EXP 327 +#define SQRT 328 +#define K_MAX 329 +#define K_MIN 330 +#define INT 331 +#define RAND 332 +#define SRAND 333 +#define COPY 334 +#define THRU 335 +#define TOP 336 +#define BOTTOM 337 +#define UPPER 338 +#define LOWER 339 +#define SH 340 +#define PRINT 341 +#define CW 342 +#define CCW 343 +#define FOR 344 +#define DO 345 +#define IF 346 +#define ELSE 347 +#define ANDAND 348 +#define OROR 349 +#define NOTEQUAL 350 +#define EQUALEQUAL 351 +#define LESSEQUAL 352 +#define GREATEREQUAL 353 +#define LEFT_CORNER 354 +#define RIGHT_CORNER 355 +#define CENTER 356 +#define END 357 +#define START 358 +#define RESET 359 +#define UNTIL 360 +#define PLOT 361 +#define THICKNESS 362 +#define FILL 363 +#define ALIGNED 364 +#define SPRINTF 365 +#define COMMAND 366 +#define DEFINE 367 +#define UNDEF 368 +#define YYERRCODE 256 +short yylhs[] = { -1, + 0, 0, 16, 17, 17, 28, 28, 29, 29, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 31, 30, + 30, 32, 33, 30, 34, 35, 30, 36, 30, 30, + 37, 30, 30, 30, 38, 38, 38, 26, 26, 27, + 27, 27, 39, 7, 23, 23, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, + 15, 15, 15, 15, 40, 42, 15, 15, 41, 41, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 43, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 25, 25, 24, 24, 19, + 19, 6, 6, 6, 6, 6, 6, 44, 44, 5, + 5, 13, 13, 13, 13, 13, 14, 14, 14, 22, + 22, 21, 21, 8, 8, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 11, 11, 12, 12, 12, 10, + 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; +short yylen[] = { 2, + 1, 1, 3, 1, 3, 0, 1, 1, 2, 3, + 4, 1, 1, 1, 1, 1, 2, 2, 0, 3, + 2, 0, 0, 7, 0, 0, 6, 0, 10, 1, + 0, 4, 1, 1, 2, 2, 3, 1, 2, 1, + 1, 1, 0, 5, 0, 2, 1, 1, 3, 3, + 3, 3, 3, 3, 3, 3, 2, 0, 2, 3, + 1, 4, 4, 4, 0, 0, 6, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 3, 0, 4, 3, 3, 3, 3, 2, 2, 3, + 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, + 3, 2, 2, 2, 3, 2, 3, 2, 3, 2, + 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 3, 2, 1, 5, 0, 3, 1, + 1, 1, 3, 3, 5, 5, 6, 1, 4, 3, + 3, 1, 2, 2, 3, 1, 1, 1, 3, 1, + 3, 1, 2, 2, 2, 1, 1, 1, 1, 1, + 1, 1, 2, 1, 2, 3, 1, 1, 2, 1, + 5, 4, 3, 3, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 2, 3, 4, 4, + 6, 4, 4, 4, 6, 6, 4, 4, 3, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 2, +}; +short yydefred[] = { 0, + 8, 0, 2, 0, 0, 0, 0, 126, 16, 12, + 13, 14, 15, 71, 72, 73, 74, 75, 76, 77, + 78, 0, 19, 0, 0, 0, 0, 0, 0, 0, + 65, 82, 0, 4, 0, 0, 79, 68, 0, 9, + 0, 0, 0, 0, 25, 0, 147, 204, 205, 150, + 152, 189, 190, 146, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 187, 188, 0, 0, + 195, 196, 201, 203, 202, 0, 0, 0, 0, 0, + 132, 130, 148, 0, 0, 0, 0, 0, 0, 41, + 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, + 35, 0, 0, 0, 0, 0, 31, 3, 0, 114, + 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 102, 103, 0, 0, 0, + 112, 113, 120, 121, 122, 123, 117, 118, 0, 0, + 125, 0, 119, 36, 0, 0, 10, 0, 22, 0, + 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 191, 193, 197, 199, 192, 194, 198, 200, + 0, 0, 0, 0, 0, 0, 0, 0, 138, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 206, 207, 208, + 209, 210, 0, 143, 0, 0, 164, 156, 157, 158, + 159, 160, 161, 162, 0, 155, 153, 154, 39, 0, + 0, 57, 0, 0, 0, 43, 0, 0, 0, 0, + 81, 128, 0, 0, 0, 0, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 167, 100, 0, 170, 0, 0, + 101, 0, 0, 0, 0, 0, 37, 0, 0, 0, + 0, 0, 0, 62, 0, 11, 0, 26, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 229, 0, 0, + 218, 141, 0, 151, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 149, 133, 134, 163, 0, 0, 53, + 0, 0, 0, 0, 0, 51, 0, 0, 50, 49, + 0, 66, 83, 32, 175, 0, 0, 0, 0, 165, + 0, 169, 0, 0, 23, 0, 219, 220, 0, 222, + 223, 224, 0, 0, 227, 228, 230, 0, 0, 0, + 0, 0, 44, 0, 127, 0, 0, 174, 173, 0, + 166, 0, 0, 27, 0, 0, 0, 135, 139, 0, + 0, 0, 0, 70, 67, 172, 0, 24, 46, 221, + 225, 226, 137, 0, 0, 171, 0, 0, 28, 0, + 0, 29, +}; +short yydgoto[] = { 2, + 106, 182, 108, 405, 91, 92, 33, 93, 94, 266, + 267, 268, 109, 96, 34, 3, 35, 36, 97, 226, + 98, 99, 384, 341, 110, 101, 102, 244, 5, 38, + 46, 287, 382, 160, 356, 411, 246, 39, 334, 115, + 395, 376, 116, 205, +}; +short yysindex[] = { 25, + 0, 0, 0,11784, 59, -47, -5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -250, 0,10817, -218,10934, -197,11387, 90,10817, + 0, 0, -214, 0, 25,10516, 0, 0, -36, 0, + 25,10934, 75, -192, 0, -124, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 106, 107, 110, 111, 113, + 120, 122, 124, 143, 146, 153, 0, 0, -213, -41, + 0, 0, 0, 0, 0,11061,10934,11387,11387, 635, + 0, 0, 0, -61, -64, 2542, 44, 834, 356, 0, +10817, 0, 133,10934,10934, 1378, -91, -294, -64, -279, + 0, -28, -52,10817, 25, 25, 0, 0,11406, 0, + 0, 0,11694,11694,11694,11694,11387,11387,11387,11387, +11505,11505,11505, 3348,11609, 0, 0,11694,11694,11694, + 0, 0, 0, 0, 0, 0, 0, 0,11387,11694, + 0, 1491, 0, 0, -33,10189, 0,10934, 0, -46, + 0,10934,10934,10934,10934,10934,10934,10934,10934,10934, +10633,10934, 0, 0, 0, 0, 0, 0, 0, 0, + 1524, 194, 195, -23, -26, 150, 150, -56, 0,11387, +11387,11387,11387,11387,11387,11387,11505,11387,11387,11387, +11387,11387,11387,11387,11505, -29, 213, 0, 0, 0, + 0, 0, -7, 0,11609,11609, 0, 0, 0, 0, + 0, 0, 0, 0, 167, 0, 0, 0, 0,11387, + 150, 0,10934,10934,11387, 0,10934,10934, -230, -230, + 0, 0, 139,11784, 177, 11, 0, 1491, 1491, 1491, + 1491, 1491, 1491, 1491, 1491, 635, 44, 44, 44, 2101, + 432, 834, 2101, 17, 0, 0, 2435, 0,11178, 623, + 0, 1491, 1491, 1491, 1491, 1491, 0, -47, -5, 0, + 0, 0, -64, 0, 44, 0, 18, 0, 240, 246, + 244, 248, 249, 250, 252, 253, 257, 0, 260, 262, + 0, 0,11505, 0, 1, 1953, 1821, 158, 158, 4, + 4, 1491, -19, 48, 4, 200, 200, 150, 150, 150, + 150, -42, 213, 0, 0, 0, 0, 1044, 1953, 0, + 1924, -43, 4, 46, 1953, 0, 1924, -43, 0, 0, + 19, 0, 0, 0, 0, 834, 2101, 2101, 266, 0, + 49, 0, 1079, 195, 0, -49, 0, 0,10934, 0, + 0, 0,10934,10934, 0, 0, 0, 33, 8,11505, +11505,11387, 0,11387, 0,11784, 2101, 0, 0, 2101, + 0, -49, 55, 0, 275, 277, 290, 0, 0, 7, + 44, 1484, 1491, 0, 0, 0, 293, 0, 0, 0, + 0, 0, 0,11283, -10, 0,11387, 1491, 0, 1491, + 74, 0, +}; +short yyrindex[] = { 204, + 0, 0, 0, 338, 94, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, + 0, 0, 399, 0, 27, 412, 0, 0, 443, 0, +10384, 0, 0, 454, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8824, + 0, 0, 0, 0, 4444, 8610, 9125, 0, 0, 0, + 467, 0, 0, 0, 0, 954, 0, 970, 0, 0, + 0,10077, 0, 487,11820,11820, 0, 0, 31, 0, + 0, 0, 9459, 9500, 9245, 9351, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 9612, 9720, 9761, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 9868, + 0, 4996, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 299, 0, 114, 0, 0, 456, 566, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3115, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 5297, 5406, 5707, + 5816, 6117, 6226, 6527, 6636, 0, 6937, 7046, 7347, 0, + 0, 0, 0, 0, 0, 0, 8697, 0, 0, 0, + 0, 7456, 7757, 7866, 8167, 8276, 0, 9870, 1967, 3642, + 4085, 184, 233, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4001, 4110, 3224, 3558, 2229, + 2338, 4887, 9004, 0, 2672, 1786, 1895, 900, 1009, 1343, + 1452, 0, 3667, 0, 0, 0, 0, 0, 29, 0, + 38, 182, 2781, 0, 96, 0, 468, 578, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 299, 0, 0, 495, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 530, 0, 0, 0, 0, + 0, 495, 0, 0, 0, 0, 0, 0, 0, 0, + 4553, -4, 39, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, + 0, 0, +}; +short yygindex[] = { 0, + -24, 480, -89, 0, -18, 189, 0, 0, -48, 0, + 0, 354, 3172, -87, -114, -3, 0, 0, 1313, -74, + 0, 0, -21, 0, 9, 326, -57, 2, 324, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, +}; +#define YYTABLESIZE 12186 +short yytable[] = { 90, + 215, 4, 216, 112, 247, 90, 207, 155, 203, 44, + 41, 152, 37, 201, 199, 232, 200, 203, 202, 215, + 303, 216, 201, 199, 228, 200, 6, 202, 237, 8, + 7, 235, 100, 198, 87, 34, 118, 238, 100, 103, + 203, 284, 156, 229, 153, 201, 199, 214, 200, 215, + 202, 216, 43, 237, 238, 42, 229, 173, 174, 375, + 111, 181, 374, 186, 187, 204, 87, 183, 403, 237, + 239, 240, 237, 388, 204, 215, 90, 216, 238, 129, + 231, 238, 129, 1, 45, 265, 215, 237, 216, 90, + 215, 370, 216, 7, 34, 52, 238, 204, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 256, 256, 100, + 270, 243, 245, 272, 273, 274, 271, 40, 323, 6, + 241, 237, 100, 7, 275, 276, 7, 37, 34, 113, + 238, 256, 117, 7, 29, 158, 52, 161, 7, 52, + 175, 176, 159, 330, 332, 162, 163, 336, 338, 164, + 165, 6, 166, 237, 52, 7, 132, 132, 132, 167, + 34, 168, 238, 169, 37, 306, 307, 308, 309, 310, + 311, 312, 313, 315, 316, 317, 318, 319, 320, 321, + 256, 56, 170, 63, 7, 171, 347, 348, 52, 7, + 270, 270, 172, 230, 203, 47, 325, 326, 236, 201, + 199, 50, 200, 6, 202, 328, 51, 242, 329, 331, + 333, 265, 335, 337, 265, 288, 7, 235, 352, 198, + 52, 154, 56, 239, 277, 56, 130, 47, 130, 177, + 178, 8, 64, 50, 301, 302, 203, 304, 51, 206, + 56, 201, 63, 204, 353, 305, 202, 339, 340, 324, + 354, 204, 37, 208, 209, 210, 211, 212, 213, 327, + 239, 394, 371, 342, 239, 239, 239, 239, 239, 343, + 239, 377, 344, 350, 56, 131, 63, 131, 256, 355, + 357, 188, 239, 239, 189, 239, 358, 359, 360, 361, + 362, 64, 237, 204, 6, 363, 364, 365, 265, 265, + 366, 238, 367, 369, 237, 381, 56, 373, 63, 380, + 383, 389, 179, 180, 399, 400, 239, 401, 237, 190, + 191, 192, 193, 194, 195, 64, 6, 238, 265, 237, + 402, 265, 237, 406, 409, 412, 29, 1, 238, 47, + 58, 238, 59, 60, 282, 256, 256, 392, 239, 393, + 7, 7, 7, 7, 7, 114, 7, 64, 119, 52, + 398, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 0, 0, 237, 237, 0, 408, + 0, 0, 410, 0, 37, 52, 238, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 30, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 61, 0, 0, 0, 0, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 0, 7, + 7, 7, 7, 7, 7, 0, 0, 7, 0, 7, + 0, 0, 33, 52, 52, 56, 225, 7, 7, 7, + 7, 7, 7, 21, 7, 217, 0, 30, 7, 7, + 6, 6, 0, 6, 6, 0, 18, 55, 0, 0, + 61, 56, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 0, 0, 17, 239, 217, 0, + 0, 30, 217, 0, 45, 217, 217, 217, 217, 217, + 217, 33, 217, 0, 61, 107, 0, 0, 55, 194, + 195, 55, 21, 239, 217, 217, 0, 217, 0, 0, + 0, 157, 225, 30, 239, 18, 55, 239, 0, 69, + 56, 0, 0, 0, 0, 33, 61, 6, 0, 0, + 0, 0, 0, 6, 6, 17, 21, 6, 217, 6, + 0, 217, 0, 45, 0, 0, 0, 0, 0, 18, + 55, 0, 6, 0, 6, 239, 185, 33, 6, 6, + 0, 239, 239, 239, 239, 239, 239, 54, 21, 17, + 217, 0, 0, 0, 0, 0, 0, 45, 69, 0, + 0, 18, 55, 0, 0, 0, 0, 0, 239, 0, + 0, 0, 239, 0, 0, 239, 239, 239, 239, 239, + 239, 17, 239, 345, 0, 217, 349, 0, 54, 45, + 0, 54, 69, 227, 239, 239, 0, 239, 218, 219, + 220, 221, 222, 223, 0, 224, 54, 286, 0, 0, + 0, 289, 290, 291, 292, 293, 294, 295, 296, 297, + 299, 300, 0, 0, 69, 0, 0, 0, 239, 203, + 0, 239, 0, 0, 201, 199, 196, 200, 0, 202, + 54, 203, 0, 0, 0, 0, 201, 199, 196, 200, + 0, 202, 235, 0, 198, 0, 0, 0, 0, 0, + 239, 217, 0, 0, 197, 0, 198, 0, 0, 346, + 378, 379, 54, 0, 218, 219, 220, 221, 222, 223, + 0, 224, 217, 217, 217, 217, 204, 0, 217, 217, + 217, 217, 217, 217, 217, 217, 217, 217, 204, 0, + 396, 55, 0, 397, 0, 0, 217, 217, 217, 217, + 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, + 217, 217, 217, 217, 217, 217, 217, 55, 0, 217, + 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, + 217, 217, 217, 0, 0, 0, 0, 0, 217, 217, + 217, 217, 217, 217, 217, 217, 217, 217, 217, 0, + 0, 217, 217, 217, 217, 0, 0, 217, 217, 0, + 217, 0, 0, 217, 217, 217, 217, 217, 217, 217, + 217, 217, 217, 217, 0, 0, 55, 217, 217, 217, + 217, 0, 239, 239, 239, 239, 0, 0, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 385, 0, + 0, 54, 386, 387, 0, 0, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 54, 0, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 0, 0, 0, 0, 0, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 213, + 0, 239, 239, 239, 239, 0, 0, 239, 239, 0, + 239, 0, 0, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 225, 0, 54, 239, 239, 239, + 239, 0, 213, 0, 0, 188, 213, 0, 189, 213, + 213, 213, 213, 213, 213, 0, 213, 0, 0, 0, + 0, 0, 0, 47, 0, 0, 0, 0, 213, 213, + 0, 213, 0, 0, 0, 0, 0, 0, 0, 48, + 190, 191, 192, 193, 194, 195, 0, 0, 0, 0, + 0, 0, 190, 191, 192, 193, 194, 195, 0, 0, + 0, 0, 213, 0, 47, 213, 0, 47, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 214, 0, + 48, 0, 47, 48, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 213, 0, 0, 0, 48, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 214, 0, 0, 0, 214, 47, 0, 214, 214, + 214, 214, 214, 214, 0, 214, 0, 0, 0, 0, + 0, 0, 48, 0, 0, 0, 0, 214, 214, 0, + 214, 0, 0, 0, 0, 0, 0, 0, 47, 0, + 203, 0, 0, 0, 0, 201, 199, 0, 200, 0, + 202, 0, 0, 217, 48, 0, 0, 0, 0, 0, + 0, 214, 0, 235, 214, 198, 218, 219, 220, 221, + 222, 223, 0, 224, 0, 203, 0, 0, 0, 0, + 201, 199, 196, 200, 0, 202, 0, 0, 0, 0, + 0, 0, 0, 214, 0, 0, 0, 204, 235, 0, + 198, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 213, 213, 213, 213, + 0, 0, 213, 213, 213, 213, 213, 213, 213, 213, + 213, 213, 204, 0, 0, 0, 0, 0, 0, 0, + 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, + 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, + 213, 0, 0, 213, 213, 213, 213, 213, 213, 213, + 213, 213, 213, 213, 213, 213, 213, 47, 0, 0, + 0, 0, 213, 213, 213, 213, 213, 213, 213, 213, + 213, 213, 213, 48, 0, 213, 213, 213, 213, 0, + 0, 213, 213, 47, 213, 0, 0, 213, 213, 213, + 213, 213, 213, 213, 213, 213, 213, 213, 0, 48, + 0, 213, 213, 213, 213, 214, 214, 214, 214, 0, + 0, 214, 214, 214, 214, 214, 214, 214, 214, 214, + 214, 0, 0, 0, 0, 0, 0, 0, 0, 214, + 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, + 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, + 0, 0, 214, 214, 214, 214, 214, 214, 214, 214, + 214, 214, 214, 214, 214, 214, 0, 0, 0, 372, + 0, 214, 214, 214, 214, 214, 214, 214, 214, 214, + 214, 214, 215, 0, 214, 214, 214, 214, 0, 0, + 214, 214, 0, 214, 0, 0, 214, 214, 214, 214, + 214, 214, 214, 214, 214, 214, 214, 0, 0, 0, + 214, 214, 214, 214, 0, 215, 0, 0, 0, 215, + 0, 0, 215, 215, 215, 215, 215, 215, 0, 215, + 0, 190, 191, 192, 193, 194, 195, 0, 184, 0, + 0, 215, 215, 0, 215, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 203, 0, 0, 0, 0, 201, + 199, 0, 200, 0, 202, 0, 233, 234, 192, 193, + 194, 195, 0, 0, 0, 215, 0, 235, 215, 198, + 0, 0, 0, 257, 258, 259, 0, 0, 0, 0, + 0, 216, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 215, 285, 0, + 0, 204, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 0, 0, 0, 216, 0, + 0, 216, 216, 216, 216, 216, 216, 0, 216, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 314, + 216, 216, 0, 216, 0, 0, 0, 322, 0, 0, + 203, 0, 0, 0, 0, 201, 199, 203, 200, 0, + 202, 0, 201, 199, 0, 200, 0, 202, 0, 0, + 0, 0, 0, 235, 216, 198, 0, 216, 0, 0, + 235, 0, 198, 0, 0, 0, 0, 0, 0, 0, + 203, 0, 0, 0, 0, 201, 199, 196, 200, 0, + 202, 0, 0, 0, 0, 0, 216, 204, 0, 0, + 0, 0, 0, 197, 204, 198, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 215, + 215, 215, 215, 0, 0, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 368, 0, 204, 0, 0, + 0, 0, 0, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 0, 0, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 0, 0, 0, 0, 0, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 0, 0, 215, 215, + 215, 215, 390, 391, 215, 215, 0, 215, 0, 0, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 0, 0, 0, 215, 215, 215, 215, 216, 216, + 216, 216, 0, 0, 216, 216, 216, 216, 216, 216, + 216, 216, 216, 216, 0, 233, 234, 192, 193, 194, + 195, 0, 216, 216, 216, 216, 216, 216, 216, 216, + 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, + 216, 216, 216, 0, 0, 216, 216, 216, 216, 216, + 216, 216, 216, 216, 216, 216, 216, 216, 216, 0, + 0, 0, 404, 0, 216, 216, 216, 216, 216, 216, + 216, 216, 216, 216, 216, 211, 0, 216, 216, 216, + 216, 0, 0, 216, 216, 0, 216, 0, 0, 216, + 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, + 0, 0, 0, 216, 216, 216, 216, 0, 211, 0, + 0, 0, 0, 0, 188, 211, 211, 189, 211, 211, + 211, 190, 191, 192, 193, 194, 195, 0, 190, 191, + 192, 193, 194, 195, 211, 211, 0, 211, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 203, 0, 0, + 0, 0, 201, 199, 0, 200, 0, 202, 0, 0, + 0, 233, 234, 192, 193, 194, 195, 0, 211, 0, + 235, 211, 198, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 212, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 211, 0, 0, 0, 204, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 212, 0, 0, + 0, 0, 0, 0, 212, 212, 0, 212, 212, 212, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 212, 212, 0, 212, 0, 0, 0, + 203, 0, 0, 0, 0, 201, 199, 0, 200, 0, + 202, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 235, 0, 198, 0, 212, 0, 203, + 212, 0, 0, 0, 201, 199, 0, 200, 0, 202, + 0, 0, 0, 204, 0, 0, 0, 0, 204, 204, + 204, 204, 235, 204, 198, 0, 0, 204, 0, 212, + 0, 0, 0, 0, 0, 0, 204, 0, 204, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 211, 211, 211, 211, 204, 0, 211, 211, + 211, 211, 211, 211, 211, 211, 211, 211, 0, 0, + 204, 0, 0, 0, 0, 0, 211, 211, 211, 211, + 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, + 211, 211, 211, 211, 211, 211, 211, 0, 0, 211, + 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, + 211, 211, 211, 0, 0, 0, 0, 0, 211, 211, + 211, 211, 211, 211, 211, 211, 211, 211, 211, 0, + 0, 211, 211, 211, 211, 0, 0, 211, 211, 0, + 211, 0, 0, 211, 211, 211, 211, 211, 211, 211, + 211, 211, 211, 211, 0, 0, 264, 211, 211, 211, + 211, 212, 212, 212, 212, 0, 0, 212, 212, 212, + 212, 212, 212, 212, 212, 212, 212, 0, 190, 0, + 192, 193, 194, 195, 0, 212, 212, 212, 212, 212, + 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, + 212, 212, 212, 212, 212, 212, 0, 0, 212, 212, + 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, + 212, 212, 0, 0, 0, 0, 0, 212, 212, 212, + 212, 212, 212, 212, 212, 212, 212, 212, 232, 0, + 212, 212, 212, 212, 0, 0, 212, 212, 0, 212, + 0, 0, 212, 212, 212, 212, 212, 212, 212, 212, + 212, 212, 212, 0, 0, 0, 212, 212, 212, 212, + 0, 232, 0, 0, 0, 0, 0, 204, 232, 232, + 204, 233, 232, 192, 193, 194, 195, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 232, 232, 0, + 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 192, 193, 194, 195, 0, 0, 0, 0, + 0, 0, 0, 0, 204, 204, 204, 204, 204, 204, + 0, 232, 0, 0, 232, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 234, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 232, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 234, 52, 53, 0, 0, 0, 0, 234, 234, 0, + 0, 234, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 234, 234, 0, 234, + 0, 0, 0, 0, 0, 0, 0, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 234, 0, 0, 234, 0, 0, 77, 78, 79, 80, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 81, 82, 83, 84, 85, 0, + 0, 0, 234, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 351, 0, 0, 0, 0, 232, 232, 232, 232, 0, + 0, 232, 232, 232, 232, 232, 232, 232, 232, 232, + 232, 0, 0, 0, 0, 0, 0, 0, 0, 232, + 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, + 0, 0, 232, 232, 232, 232, 232, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 0, 0, 0, 0, + 0, 232, 232, 232, 232, 232, 232, 232, 232, 232, + 232, 232, 0, 0, 232, 232, 232, 232, 0, 0, + 232, 232, 0, 232, 0, 0, 232, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 213, 0, 0, + 232, 232, 232, 232, 234, 234, 234, 234, 0, 0, + 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, + 0, 0, 0, 0, 0, 0, 0, 0, 234, 234, + 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, + 234, 234, 234, 234, 234, 234, 234, 234, 234, 0, + 0, 234, 234, 234, 234, 234, 234, 234, 234, 234, + 234, 234, 234, 234, 234, 0, 0, 0, 0, 0, + 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, + 234, 233, 0, 234, 234, 234, 234, 0, 0, 234, + 234, 0, 234, 0, 0, 234, 234, 234, 234, 234, + 234, 234, 234, 234, 234, 234, 0, 0, 0, 234, + 234, 234, 234, 0, 233, 52, 53, 0, 0, 0, + 0, 233, 233, 0, 0, 233, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 233, 233, 0, 233, 0, 0, 0, 0, 0, 0, + 0, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 233, 0, 0, 233, 0, 0, + 77, 78, 79, 80, 0, 0, 0, 0, 0, 0, + 231, 0, 0, 0, 0, 0, 0, 0, 81, 82, + 83, 84, 85, 0, 0, 0, 233, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 231, 0, 0, 0, 0, 0, 0, + 231, 231, 0, 0, 231, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, + 231, 0, 231, 0, 0, 0, 0, 0, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 231, 0, 0, 231, 77, 78, 79, + 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 81, 82, 83, 84, 85, + 0, 0, 0, 0, 0, 231, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 233, 233, + 233, 233, 0, 0, 233, 233, 233, 233, 233, 233, + 233, 233, 233, 233, 0, 0, 0, 0, 0, 0, + 0, 0, 233, 233, 233, 233, 233, 233, 233, 233, + 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, + 233, 233, 233, 0, 0, 233, 233, 233, 233, 233, + 233, 233, 233, 233, 233, 233, 233, 233, 233, 0, + 0, 0, 0, 0, 233, 233, 233, 233, 233, 233, + 233, 233, 233, 233, 233, 0, 0, 233, 233, 233, + 233, 0, 0, 233, 233, 0, 233, 0, 0, 233, + 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, + 0, 0, 0, 233, 233, 233, 233, 231, 231, 231, + 231, 0, 0, 231, 231, 231, 231, 231, 231, 231, + 231, 231, 231, 0, 0, 0, 0, 0, 0, 0, + 0, 231, 231, 231, 231, 231, 231, 231, 231, 231, + 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, + 231, 231, 0, 0, 231, 231, 231, 231, 231, 231, + 231, 231, 231, 231, 231, 231, 231, 231, 0, 0, + 0, 0, 0, 231, 231, 231, 231, 231, 231, 231, + 231, 231, 231, 231, 144, 0, 231, 231, 231, 231, + 0, 0, 231, 231, 0, 231, 0, 0, 231, 231, + 231, 231, 231, 231, 231, 231, 231, 231, 231, 0, + 0, 0, 231, 231, 231, 231, 0, 144, 0, 0, + 0, 0, 0, 0, 144, 144, 0, 144, 144, 144, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 144, 0, 0, 144, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 95, 0, 0, 0, 0, + 0, 95, 0, 0, 0, 0, 0, 144, 0, 0, + 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 236, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 236, 95, 0, 0, + 0, 0, 0, 236, 236, 0, 0, 236, 0, 0, + 0, 0, 95, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 236, 0, 0, 95, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 95, 95, 95, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 236, 0, 0, 236, + 0, 0, 0, 0, 0, 0, 0, 283, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 236, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 95, 0, + 0, 144, 144, 144, 144, 0, 95, 144, 0, 144, + 144, 144, 144, 144, 144, 144, 144, 263, 0, 0, + 0, 0, 0, 264, 0, 144, 144, 144, 144, 144, + 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, + 144, 144, 144, 144, 144, 0, 0, 0, 0, 144, + 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, + 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, + 144, 144, 144, 144, 144, 144, 144, 144, 0, 0, + 144, 144, 144, 144, 0, 0, 144, 144, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 144, 144, + 144, 144, 144, 0, 95, 0, 144, 144, 144, 144, + 236, 236, 236, 236, 0, 0, 236, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 0, 0, 0, 0, + 0, 0, 0, 0, 236, 236, 236, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 236, 236, 236, 236, 0, 0, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 95, 95, 0, 0, 0, 236, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 235, 0, 236, + 236, 236, 236, 0, 0, 236, 236, 0, 236, 0, + 0, 236, 236, 236, 236, 0, 0, 236, 236, 236, + 236, 236, 0, 0, 0, 236, 236, 236, 236, 0, + 235, 0, 0, 0, 0, 0, 0, 235, 235, 0, + 0, 235, 0, 0, 260, 0, 0, 0, 0, 0, + 261, 0, 0, 0, 0, 262, 235, 0, 52, 53, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, + 235, 0, 0, 235, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 0, 145, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 235, 77, 78, 79, 80, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 145, + 14, 81, 82, 83, 84, 85, 145, 145, 0, 145, + 145, 145, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 145, 0, 0, 145, 0, + 0, 0, 0, 0, 14, 0, 0, 189, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 145, + 0, 0, 145, 0, 0, 0, 14, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 145, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 235, 235, 235, 235, 0, 0, + 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, + 0, 0, 0, 0, 0, 0, 0, 0, 235, 235, + 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, + 235, 235, 235, 235, 235, 235, 235, 235, 235, 0, + 0, 235, 235, 235, 235, 235, 235, 235, 235, 235, + 235, 235, 235, 235, 235, 0, 0, 0, 0, 0, + 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, + 235, 0, 0, 235, 235, 235, 235, 0, 189, 235, + 235, 0, 235, 0, 189, 235, 235, 235, 235, 189, + 0, 235, 235, 235, 235, 235, 0, 0, 0, 235, + 235, 235, 235, 145, 145, 145, 145, 0, 0, 145, + 0, 145, 145, 145, 145, 145, 145, 145, 145, 0, + 0, 0, 189, 0, 0, 0, 0, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 0, 0, 0, + 0, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 237, 0, 145, 145, 145, 145, 0, 0, 145, 145, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 145, 145, 145, 145, 145, 0, 0, 0, 145, 145, + 145, 145, 0, 237, 0, 0, 0, 0, 0, 0, + 237, 237, 0, 0, 237, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, + 0, 0, 0, 237, 0, 0, 237, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 237, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 238, 15, 0, 0, 0, 0, 0, 238, + 238, 0, 0, 238, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 238, 0, + 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, + 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 238, 0, 0, 238, 0, 0, 0, 15, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 238, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 237, 237, 237, + 237, 0, 0, 237, 237, 237, 237, 237, 237, 237, + 237, 237, 237, 0, 0, 0, 0, 0, 0, 0, + 0, 237, 237, 237, 237, 237, 237, 237, 237, 237, + 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, + 237, 237, 0, 0, 237, 237, 237, 237, 237, 237, + 237, 237, 237, 237, 237, 237, 237, 237, 0, 0, + 0, 0, 0, 237, 237, 237, 237, 237, 237, 237, + 237, 237, 237, 237, 0, 0, 237, 237, 237, 237, + 0, 190, 237, 237, 0, 237, 0, 190, 237, 237, + 0, 0, 190, 0, 237, 237, 237, 237, 237, 0, + 0, 0, 237, 237, 237, 237, 238, 238, 238, 238, + 0, 0, 238, 238, 238, 238, 238, 238, 238, 238, + 238, 238, 0, 0, 0, 190, 0, 0, 0, 0, + 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, + 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, + 238, 0, 0, 238, 238, 238, 238, 238, 238, 238, + 238, 238, 238, 238, 238, 238, 238, 0, 0, 0, + 0, 0, 238, 238, 238, 238, 238, 238, 238, 238, + 238, 238, 238, 131, 0, 238, 238, 238, 238, 0, + 0, 238, 238, 0, 238, 0, 0, 0, 238, 0, + 0, 0, 0, 238, 238, 238, 238, 238, 0, 0, + 0, 238, 238, 238, 238, 0, 131, 0, 0, 0, + 0, 0, 0, 131, 131, 0, 131, 131, 131, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 131, 0, 0, 131, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 131, 0, 0, 131, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 136, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 131, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 136, 0, 0, 0, 0, + 0, 0, 136, 136, 0, 0, 136, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 136, 0, 0, 136, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 136, 0, 0, 136, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 136, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 131, 131, 131, 131, 0, 0, 131, 0, 131, 131, + 131, 131, 131, 131, 131, 131, 0, 0, 0, 0, + 0, 0, 0, 0, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 0, 0, 0, 0, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 0, 0, 0, 0, 0, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 0, 0, 131, + 131, 131, 131, 0, 0, 131, 131, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 131, 131, 131, + 131, 131, 0, 0, 0, 131, 131, 131, 131, 136, + 136, 136, 136, 0, 0, 136, 0, 136, 136, 136, + 136, 136, 136, 136, 136, 0, 0, 0, 0, 0, + 0, 0, 0, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 0, 0, 0, 0, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 0, 0, 0, 0, 0, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 140, 0, 136, 136, + 136, 136, 0, 0, 136, 136, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 136, 136, 136, 136, + 136, 0, 0, 0, 136, 136, 136, 136, 0, 140, + 0, 0, 0, 0, 0, 0, 140, 140, 0, 0, + 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 140, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, + 0, 0, 140, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 88, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 140, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 88, 0, + 0, 0, 0, 0, 0, 88, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 88, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 88, 0, + 0, 88, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 140, 140, 140, 140, 0, 0, 140, + 0, 140, 140, 140, 140, 140, 140, 140, 140, 0, + 0, 0, 0, 0, 0, 0, 0, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 0, 0, 0, + 0, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 0, 0, 0, 0, 0, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 0, 0, 140, 140, 140, 140, 0, 0, 140, 140, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 140, 140, 140, 140, 140, 0, 0, 0, 140, 140, + 140, 140, 88, 88, 88, 88, 0, 0, 88, 0, + 88, 88, 88, 88, 88, 88, 88, 88, 0, 0, + 0, 0, 0, 0, 0, 0, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 90, 0, 0, 0, + 0, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 0, 0, 0, 0, 0, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 90, + 0, 88, 88, 88, 88, 0, 90, 88, 88, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, + 88, 88, 88, 88, 0, 90, 0, 88, 88, 88, + 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, + 0, 0, 90, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 92, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 90, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 92, 0, + 0, 0, 0, 0, 0, 92, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 92, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 92, 0, + 0, 92, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 90, 90, 90, 90, 0, 0, 90, + 0, 90, 90, 90, 90, 90, 90, 90, 90, 0, + 0, 0, 0, 0, 0, 0, 0, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 0, 0, 0, + 0, 0, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 0, 0, 0, 0, 0, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 0, 0, 90, 90, 90, 90, 0, 0, 90, 90, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 90, 90, 90, 90, 90, 0, 0, 0, 90, 90, + 90, 90, 92, 92, 92, 92, 0, 0, 92, 0, + 92, 92, 92, 92, 92, 92, 92, 92, 0, 0, + 0, 0, 0, 0, 0, 0, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 96, 0, 0, 0, + 0, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 0, 0, 0, 0, 0, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 96, + 0, 92, 92, 92, 92, 0, 96, 92, 92, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, + 92, 92, 92, 92, 0, 96, 0, 92, 92, 92, + 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 94, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 94, 0, + 0, 0, 0, 0, 0, 94, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 94, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 94, 0, + 0, 94, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 96, 96, 96, 0, 0, 96, + 0, 96, 96, 96, 96, 96, 96, 96, 96, 0, + 0, 0, 0, 0, 0, 0, 0, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 0, 0, 0, + 0, 0, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 0, 0, 0, 0, 0, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 0, 0, 96, 96, 96, 96, 0, 0, 96, 96, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 96, 96, 96, 96, 96, 0, 0, 0, 96, 96, + 96, 96, 94, 94, 94, 94, 0, 0, 94, 0, + 94, 94, 94, 94, 94, 94, 94, 94, 0, 0, + 0, 0, 0, 0, 0, 0, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 84, 0, 0, 0, + 0, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 0, 0, 0, 0, 0, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 84, + 0, 94, 94, 94, 94, 0, 84, 94, 94, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, + 94, 94, 94, 94, 0, 84, 0, 94, 94, 94, + 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, + 0, 0, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 85, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 84, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 85, 0, + 0, 0, 0, 0, 0, 85, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 85, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 85, 0, + 0, 85, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 84, 84, 84, 84, 0, 0, 84, + 0, 84, 84, 84, 84, 84, 84, 84, 84, 0, + 0, 0, 0, 0, 0, 0, 0, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 0, 0, 0, + 0, 0, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 0, 0, 0, 0, 0, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 0, 0, 84, 84, 84, 84, 0, 0, 84, 84, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 84, 84, 84, 84, 84, 0, 0, 0, 84, 84, + 84, 84, 85, 85, 85, 85, 0, 0, 85, 0, + 85, 85, 85, 85, 85, 85, 85, 85, 0, 0, + 0, 0, 0, 0, 0, 0, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 86, 0, 0, 0, + 0, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 0, 0, 0, 0, 0, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 86, + 0, 85, 85, 85, 85, 0, 86, 85, 85, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, + 85, 85, 85, 85, 0, 86, 0, 85, 85, 85, + 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, + 0, 0, 86, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 87, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 86, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 87, 0, + 0, 0, 0, 0, 0, 87, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 87, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 87, 0, + 0, 87, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 86, 86, 86, 86, 0, 0, 86, + 0, 86, 86, 86, 86, 86, 86, 86, 86, 0, + 0, 0, 0, 0, 0, 0, 0, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 0, 0, 0, + 0, 0, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 0, 0, 0, 0, 0, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 0, 0, 86, 86, 86, 86, 0, 0, 86, 86, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 86, 86, 86, 86, 86, 0, 0, 0, 86, 86, + 86, 86, 87, 87, 87, 87, 0, 0, 87, 0, + 87, 87, 87, 87, 87, 87, 87, 87, 0, 0, + 0, 0, 0, 0, 0, 0, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 97, 0, 0, 0, + 0, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 0, 0, 0, 0, 0, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 97, + 0, 87, 87, 87, 87, 0, 97, 87, 87, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, + 87, 87, 87, 87, 0, 97, 0, 87, 87, 87, + 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, + 0, 0, 97, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 97, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 98, 0, + 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 98, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 98, 0, + 0, 98, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 97, 97, 97, 97, 0, 0, 97, + 0, 97, 97, 97, 97, 97, 97, 97, 97, 0, + 0, 0, 0, 0, 0, 0, 0, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 0, 0, 0, + 0, 0, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 0, 0, 0, 0, 0, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 0, 0, 97, 97, 97, 97, 0, 0, 97, 97, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 97, 97, 97, 97, 97, 0, 0, 0, 97, 97, + 97, 97, 98, 98, 98, 98, 0, 0, 98, 0, + 98, 98, 98, 98, 98, 98, 98, 98, 0, 0, + 0, 0, 0, 0, 0, 0, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 99, 0, 0, 0, + 0, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 0, 0, 0, 0, 0, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 99, + 0, 98, 98, 98, 98, 0, 99, 98, 98, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, + 98, 98, 98, 98, 0, 99, 0, 98, 98, 98, + 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, + 0, 0, 99, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 105, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, + 0, 0, 0, 0, 0, 105, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 105, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, + 0, 105, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 99, 99, 99, 99, 0, 0, 99, + 0, 99, 99, 99, 99, 99, 99, 99, 99, 0, + 0, 0, 0, 0, 0, 0, 0, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 0, 0, 0, + 0, 0, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 0, 0, 0, 0, 0, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 0, 0, 99, 99, 99, 99, 0, 0, 99, 99, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 99, 99, 99, 99, 99, 0, 0, 0, 99, 99, + 99, 99, 105, 105, 105, 105, 0, 0, 105, 0, + 105, 105, 105, 105, 105, 105, 105, 105, 0, 0, + 0, 0, 0, 0, 0, 0, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 107, 0, 0, 0, + 0, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 0, 0, 0, 0, 0, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 107, + 0, 105, 105, 105, 105, 0, 107, 105, 105, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, + 105, 105, 105, 105, 0, 107, 0, 105, 105, 105, + 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, + 0, 0, 107, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 107, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, + 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, + 0, 111, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 107, 107, 107, 107, 0, 0, 107, + 0, 107, 107, 107, 107, 107, 107, 107, 107, 0, + 0, 0, 0, 0, 0, 0, 0, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 0, 0, 0, + 0, 0, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 0, 0, 0, 0, 0, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 0, 0, 107, 107, 107, 107, 0, 0, 107, 107, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 107, 107, 107, 107, 107, 0, 0, 0, 107, 107, + 107, 107, 111, 111, 111, 111, 0, 0, 111, 0, + 111, 111, 111, 111, 111, 111, 111, 111, 0, 0, + 0, 0, 0, 0, 0, 0, 111, 111, 111, 111, + 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, + 111, 111, 111, 111, 111, 111, 124, 0, 0, 0, + 0, 111, 111, 111, 111, 111, 111, 111, 111, 111, + 111, 111, 111, 0, 0, 0, 0, 0, 111, 111, + 111, 111, 111, 111, 111, 111, 111, 111, 111, 124, + 0, 111, 111, 111, 111, 0, 124, 111, 111, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, + 111, 111, 111, 111, 0, 124, 0, 111, 111, 111, + 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, + 0, 0, 124, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 109, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 124, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 109, 0, + 0, 0, 0, 0, 0, 109, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 109, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 109, 0, + 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 124, 124, 124, 124, 0, 0, 124, + 0, 124, 124, 124, 124, 124, 124, 124, 124, 0, + 0, 0, 0, 0, 0, 0, 0, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 0, 0, 0, + 0, 0, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 0, 0, 0, 0, 0, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 0, 0, 124, 124, 124, 124, 0, 0, 124, 124, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 124, 124, 124, 124, 124, 0, 0, 0, 124, 124, + 124, 124, 109, 109, 109, 109, 0, 0, 109, 0, + 109, 109, 109, 109, 109, 109, 109, 109, 0, 0, + 0, 0, 0, 0, 0, 0, 109, 109, 109, 109, + 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, + 109, 109, 109, 109, 109, 109, 0, 0, 0, 0, + 0, 109, 109, 109, 109, 109, 109, 109, 109, 109, + 109, 109, 109, 0, 0, 0, 0, 0, 109, 109, + 109, 109, 109, 109, 109, 109, 109, 109, 109, 142, + 0, 109, 109, 109, 109, 0, 0, 109, 109, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, + 109, 109, 109, 109, 0, 0, 0, 109, 109, 109, + 109, 0, 142, 0, 0, 0, 0, 0, 0, 142, + 142, 0, 142, 142, 142, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 142, 0, + 0, 142, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 168, 0, 0, 0, + 0, 0, 142, 0, 0, 142, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, + 0, 0, 0, 0, 142, 0, 168, 168, 0, 0, + 168, 168, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 168, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, + 0, 0, 168, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 168, 0, 40, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, + 0, 0, 0, 40, 0, 0, 142, 142, 142, 142, + 0, 0, 142, 0, 142, 142, 142, 142, 142, 142, + 142, 142, 40, 0, 0, 0, 0, 0, 0, 0, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 0, 0, 0, 0, 142, 142, 40, 0, 0, 40, + 0, 0, 0, 0, 0, 0, 0, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 0, 0, 0, 0, 0, 40, 0, + 0, 142, 142, 168, 168, 168, 168, 0, 0, 168, + 0, 168, 168, 168, 168, 168, 168, 0, 0, 0, + 0, 142, 142, 142, 142, 0, 0, 168, 168, 168, + 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, + 168, 168, 168, 168, 168, 168, 168, 0, 0, 0, + 0, 0, 168, 231, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, + 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, + 0, 0, 0, 0, 0, 0, 231, 0, 168, 168, + 0, 0, 0, 231, 231, 0, 0, 231, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 168, 168, + 168, 168, 231, 231, 0, 231, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 40, 40, 40, 40, 0, 0, 40, 0, 0, 0, + 0, 40, 0, 0, 40, 40, 231, 0, 0, 231, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 42, 0, 0, 0, 231, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 0, 0, 0, 0, 0, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 42, 0, 40, + 40, 40, 40, 0, 42, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 40, 40, 40, + 40, 40, 0, 42, 0, 0, 0, 0, 40, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, + 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 95, 0, 0, 0, 0, 42, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 231, 231, 231, 231, 0, 0, 231, 0, 0, 0, + 0, 231, 0, 0, 231, 231, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 95, 0, 0, 0, 0, 0, 231, + 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, + 231, 0, 0, 0, 0, 0, 231, 231, 231, 231, + 231, 231, 231, 231, 231, 231, 231, 95, 0, 231, + 231, 231, 231, 0, 0, 0, 0, 0, 0, 0, + 93, 231, 231, 231, 231, 231, 231, 231, 231, 231, + 231, 231, 0, 0, 0, 0, 0, 0, 231, 95, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 42, 42, 42, 42, 0, 0, 42, 0, 0, + 0, 0, 42, 0, 0, 42, 42, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 0, 93, 0, 0, 0, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 89, 0, + 42, 42, 42, 42, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 93, 0, 0, 42, 42, + 42, 42, 42, 0, 0, 0, 0, 0, 0, 42, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, + 0, 0, 0, 0, 95, 0, 0, 0, 0, 95, + 95, 95, 0, 95, 95, 95, 95, 89, 0, 0, + 0, 0, 0, 0, 0, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 189, 0, 0, 0, 0, + 0, 89, 0, 0, 0, 0, 0, 0, 91, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 89, 0, 0, 95, 95, 0, 0, + 0, 0, 91, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 95, 95, 95, 95, + 93, 104, 0, 0, 0, 93, 93, 93, 0, 93, + 93, 93, 93, 0, 91, 0, 0, 0, 0, 0, + 0, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 190, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 93, 93, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, + 0, 0, 93, 93, 93, 93, 0, 0, 89, 106, + 0, 0, 0, 89, 89, 89, 0, 89, 89, 89, + 89, 0, 0, 0, 0, 0, 104, 0, 0, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 91, + 110, 0, 0, 0, 91, 91, 91, 0, 91, 91, + 91, 91, 0, 0, 0, 0, 0, 0, 106, 0, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 89, 89, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 106, 0, 0, 0, 0, 0, 0, 110, + 89, 89, 89, 89, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 91, 91, 0, 106, 0, 0, 0, 0, 0, + 0, 0, 0, 110, 0, 0, 0, 0, 0, 0, + 0, 91, 91, 91, 91, 0, 0, 108, 0, 147, + 0, 104, 0, 0, 0, 0, 104, 104, 104, 0, + 104, 104, 104, 104, 0, 110, 0, 0, 0, 0, + 0, 0, 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 147, 0, 147, 147, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 108, 0, 147, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 104, 104, 0, 0, 0, 0, 0, + 108, 0, 147, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 104, 104, 104, 104, 0, 0, 106, + 0, 0, 0, 0, 106, 106, 106, 0, 106, 106, + 106, 106, 108, 0, 147, 0, 0, 0, 0, 0, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 110, 0, 0, 0, 0, 110, 110, 110, 0, 110, + 110, 110, 110, 0, 0, 0, 0, 0, 0, 0, + 0, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 106, 106, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, + 0, 106, 106, 106, 106, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 110, 110, 0, 0, 0, 0, 0, 80, + 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, + 0, 0, 110, 110, 110, 110, 0, 108, 0, 0, + 0, 0, 108, 108, 108, 80, 108, 108, 108, 108, + 147, 147, 0, 0, 0, 0, 0, 0, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 0, 80, + 0, 0, 80, 0, 0, 0, 147, 147, 147, 147, + 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, + 147, 147, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 80, 0, 0, 0, 147, 147, 147, 147, 108, + 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 89, 0, 147, 147, 147, 147, 147, 86, 108, + 108, 108, 108, 88, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, + 0, 0, 0, 0, 87, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 80, 80, 80, 0, 0, 0, 80, + 0, 80, 80, 80, 80, 80, 80, 80, 80, 0, + 0, 0, 0, 0, 0, 0, 0, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 0, 0, 0, + 0, 0, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 0, 0, 0, 0, 0, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 0, 0, 80, 80, 80, 80, 6, 0, 80, 80, + 0, 0, 0, 6, 0, 0, 0, 0, 6, 0, + 80, 80, 80, 80, 80, 0, 0, 0, 80, 80, + 80, 0, 0, 0, 0, 278, 279, 49, 8, 9, + 0, 50, 0, 0, 0, 0, 51, 10, 11, 280, + 281, 14, 15, 16, 17, 18, 19, 20, 21, 0, + 0, 0, 0, 0, 6, 0, 0, 0, 0, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 6, 0, 0, 0, + 0, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 22, 0, 77, 78, 79, 80, 23, 24, + 0, 0, 25, 0, 26, 0, 0, 0, 0, 0, + 0, 0, 81, 82, 83, 84, 85, 27, 89, 28, + 0, 0, 0, 29, 30, 104, 0, 0, 0, 0, + 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 87, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 6, 6, 6, 6, 0, 6, 0, 0, 0, + 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 0, 105, 0, 0, 0, 0, + 0, 0, 104, 298, 0, 0, 0, 88, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 0, 0, 0, 0, 0, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 0, 6, + 6, 6, 6, 6, 6, 0, 0, 6, 87, 6, + 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, + 6, 6, 6, 0, 6, 0, 0, 0, 6, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 47, 48, 49, 8, 0, 0, 50, 0, + 120, 121, 122, 51, 123, 124, 125, 126, 0, 0, + 0, 0, 0, 0, 0, 0, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 144, 145, 146, 0, 0, 0, 0, + 0, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 0, 0, 0, 0, 0, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 89, + 0, 77, 78, 79, 80, 0, 86, 147, 148, 0, + 0, 88, 0, 0, 0, 0, 0, 0, 0, 81, + 82, 83, 84, 85, 0, 0, 0, 149, 150, 151, + 29, 0, 0, 0, 0, 0, 0, 0, 0, 47, + 48, 49, 8, 0, 0, 50, 0, 0, 0, 0, + 51, 0, 0, 52, 53, 0, 0, 0, 0, 0, + 0, 0, 87, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 0, 0, 0, 0, 0, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 105, 0, 77, 78, + 79, 80, 0, 104, 0, 0, 0, 0, 88, 0, + 0, 0, 0, 0, 0, 0, 81, 82, 83, 84, + 85, 0, 0, 0, 0, 0, 0, 29, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 47, 48, 49, 8, 0, 0, 50, + 0, 0, 0, 0, 51, 0, 0, 52, 53, 0, + 0, 0, 0, 105, 0, 0, 0, 0, 0, 0, + 86, 0, 0, 0, 0, 88, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 0, 0, 0, 0, 0, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 0, 0, 77, 78, 79, 80, 87, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 81, 82, 83, 84, 85, 0, 0, 0, 0, 0, + 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, + 47, 48, 49, 8, 0, 0, 50, 0, 0, 0, + 0, 51, 0, 0, 52, 53, 0, 0, 0, 0, + 105, 0, 0, 0, 0, 0, 0, 269, 0, 0, + 0, 0, 88, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 0, 0, 0, 0, 0, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 0, 0, 77, + 78, 79, 80, 87, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 81, 82, 83, + 84, 85, 0, 0, 0, 0, 0, 0, 29, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 89, 0, 47, 48, 49, + 8, 0, 104, 50, 407, 0, 0, 88, 51, 0, + 0, 52, 53, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 87, 0, + 0, 0, 0, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 0, 0, 77, 78, 79, 80, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 81, 82, 83, 84, 85, 89, + 0, 0, 0, 0, 0, 29, 104, 0, 0, 0, + 0, 88, 0, 0, 47, 48, 49, 8, 0, 0, + 50, 0, 0, 0, 0, 51, 0, 0, 52, 53, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 87, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 0, 32, 0, 0, 0, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 0, 0, 77, 78, 79, 80, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 31, 0, + 0, 81, 82, 83, 84, 85, 0, 89, 0, 47, + 48, 49, 29, 0, 86, 50, 0, 0, 0, 88, + 51, 0, 0, 52, 53, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 87, 0, 0, 0, 0, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 0, 0, 77, 78, + 79, 80, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 81, 82, 83, 84, + 85, 89, 0, 47, 48, 49, 0, 0, 269, 50, + 0, 0, 0, 88, 51, 0, 0, 52, 53, 0, + 0, 0, 6, 7, 0, 8, 9, 0, 0, 0, + 0, 0, 0, 0, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 0, 0, 0, 0, + 0, 0, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 87, 0, 0, 0, 0, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 0, 0, 77, 78, 79, 80, 89, 0, 0, 0, + 0, 0, 0, 104, 0, 0, 0, 0, 88, 22, + 81, 82, 83, 84, 85, 23, 24, 0, 0, 25, + 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 47, 48, 49, 27, 0, 28, 50, 0, 0, + 29, 30, 51, 0, 0, 52, 53, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 0, 0, 0, 0, 0, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 0, 0, + 77, 78, 79, 80, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 81, 82, + 83, 84, 85, 0, 0, 47, 48, 49, 0, 0, + 0, 50, 0, 0, 32, 0, 51, 0, 0, 52, + 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 31, 0, 0, 0, + 6, 0, 0, 0, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 0, 0, 0, 0, + 0, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 6, 0, 77, 78, 79, 80, 0, 0, + 47, 48, 49, 0, 0, 0, 50, 0, 0, 0, + 0, 51, 81, 82, 83, 84, 85, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 0, 0, 0, 0, 0, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 0, 0, 77, + 78, 79, 80, 0, 0, 0, 0, 0, 0, 0, + 6, 7, 0, 8, 9, 0, 0, 81, 82, 83, + 84, 85, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 6, 6, 0, 6, + 6, 0, 0, 0, 0, 0, 0, 0, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, + 0, 0, 0, 23, 24, 0, 0, 25, 0, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 27, 0, 28, 0, 0, 0, 29, 30, + 0, 0, 0, 6, 0, 0, 0, 0, 0, 6, + 6, 0, 0, 6, 0, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, + 6, 0, 0, 0, 6, 6, +}; +short yycheck[] = { 24, + 43, 0, 45, 28, 119, 30, 94, 44, 37, 260, + 58, 36, 4, 42, 43, 105, 45, 37, 47, 43, + 44, 45, 42, 43, 99, 45, 0, 47, 0, 260, + 0, 60, 24, 62, 96, 0, 35, 0, 30, 258, + 37, 156, 41, 101, 36, 42, 43, 96, 45, 43, + 47, 45, 58, 348, 349, 61, 114, 271, 272, 41, + 258, 86, 44, 88, 89, 94, 96, 86, 62, 41, + 350, 351, 44, 41, 94, 43, 101, 45, 41, 41, + 105, 44, 44, 59, 335, 134, 43, 59, 45, 114, + 43, 44, 45, 0, 59, 0, 59, 94, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 101, + 135, 115, 116, 138, 139, 140, 135, 59, 206, 93, + 112, 93, 114, 93, 149, 150, 33, 119, 93, 40, + 93, 156, 347, 40, 365, 61, 41, 262, 45, 44, + 354, 355, 335, 233, 234, 40, 40, 237, 238, 40, + 40, 125, 40, 125, 59, 125, 43, 44, 45, 40, + 125, 40, 125, 40, 156, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 0, 40, 0, 91, 40, 261, 262, 93, 96, + 215, 216, 40, 61, 37, 257, 215, 216, 290, 42, + 43, 263, 45, 0, 47, 230, 268, 260, 233, 234, + 235, 260, 237, 238, 263, 262, 123, 60, 267, 62, + 125, 258, 41, 0, 258, 44, 43, 257, 45, 271, + 272, 260, 0, 263, 41, 41, 37, 264, 268, 301, + 59, 42, 59, 94, 269, 302, 47, 239, 240, 257, + 269, 94, 244, 318, 319, 320, 321, 322, 46, 93, + 37, 376, 305, 125, 41, 42, 43, 44, 45, 93, + 47, 346, 262, 257, 93, 43, 93, 45, 303, 262, + 41, 301, 59, 60, 304, 62, 41, 44, 41, 41, + 41, 59, 264, 94, 91, 44, 44, 41, 347, 348, + 41, 264, 41, 303, 348, 257, 125, 262, 125, 44, + 360, 304, 354, 355, 260, 41, 93, 41, 290, 348, + 349, 350, 351, 352, 353, 93, 123, 290, 377, 301, + 41, 380, 304, 41, 345, 262, 365, 0, 301, 41, + 345, 304, 345, 345, 156, 370, 371, 372, 125, 374, + 257, 258, 259, 260, 261, 30, 263, 125, 35, 264, + 382, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, -1, -1, 348, 349, -1, 404, + -1, -1, 407, -1, 376, 290, 349, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 0, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, 0, -1, -1, -1, -1, 323, 324, 325, 326, + 327, 328, 329, 330, 331, 332, 333, 334, -1, 336, + 337, 338, 339, 340, 341, -1, -1, 344, -1, 346, + -1, -1, 0, 348, 349, 264, 91, 354, 355, 356, + 357, 358, 359, 0, 361, 0, -1, 59, 365, 366, + 257, 258, -1, 260, 261, -1, 0, 0, -1, -1, + 59, 290, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, -1, -1, 0, 264, 33, -1, + -1, 93, 37, -1, 0, 40, 41, 42, 43, 44, + 45, 59, 47, -1, 93, 26, -1, -1, 41, 352, + 353, 44, 59, 290, 59, 60, -1, 62, -1, -1, + -1, 42, 91, 125, 301, 59, 59, 304, -1, 0, + 349, -1, -1, -1, -1, 93, 125, 334, -1, -1, + -1, -1, -1, 340, 341, 59, 93, 344, 93, 346, + -1, 96, -1, 59, -1, -1, -1, -1, -1, 93, + 93, -1, 359, -1, 361, 0, 87, 125, 365, 366, + -1, 348, 349, 350, 351, 352, 353, 0, 125, 93, + 125, -1, -1, -1, -1, -1, -1, 93, 59, -1, + -1, 125, 125, -1, -1, -1, -1, -1, 33, -1, + -1, -1, 37, -1, -1, 40, 41, 42, 43, 44, + 45, 125, 47, 260, -1, 260, 263, -1, 41, 125, + -1, 44, 93, 268, 59, 60, -1, 62, 273, 274, + 275, 276, 277, 278, -1, 280, 59, 158, -1, -1, + -1, 162, 163, 164, 165, 166, 167, 168, 169, 170, + 171, 172, -1, -1, 125, -1, -1, -1, 93, 37, + -1, 96, -1, -1, 42, 43, 44, 45, -1, 47, + 93, 37, -1, -1, -1, -1, 42, 43, 44, 45, + -1, 47, 60, -1, 62, -1, -1, -1, -1, -1, + 125, 260, -1, -1, 60, -1, 62, -1, -1, 268, + 347, 348, 125, -1, 273, 274, 275, 276, 277, 278, + -1, 280, 257, 258, 259, 260, 94, -1, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 94, -1, + 377, 264, -1, 380, -1, -1, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 290, -1, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, -1, -1, -1, -1, -1, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, -1, + -1, 336, 337, 338, 339, -1, -1, 342, 343, -1, + 345, -1, -1, 348, 349, 350, 351, 352, 353, 354, + 355, 356, 357, 358, -1, -1, 349, 362, 363, 364, + 365, -1, 257, 258, 259, 260, -1, -1, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 359, -1, + -1, 264, 363, 364, -1, -1, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 290, -1, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, -1, -1, -1, -1, -1, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 0, + -1, 336, 337, 338, 339, -1, -1, 342, 343, -1, + 345, -1, -1, 348, 349, 350, 351, 352, 353, 354, + 355, 356, 357, 358, 91, -1, 349, 362, 363, 364, + 365, -1, 33, -1, -1, 301, 37, -1, 304, 40, + 41, 42, 43, 44, 45, -1, 47, -1, -1, -1, + -1, -1, -1, 0, -1, -1, -1, -1, 59, 60, + -1, 62, -1, -1, -1, -1, -1, -1, -1, 0, + 348, 349, 350, 351, 352, 353, -1, -1, -1, -1, + -1, -1, 348, 349, 350, 351, 352, 353, -1, -1, + -1, -1, 93, -1, 41, 96, -1, 44, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, + 41, -1, 59, 44, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 125, -1, -1, -1, 59, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 33, -1, -1, -1, 37, 93, -1, 40, 41, + 42, 43, 44, 45, -1, 47, -1, -1, -1, -1, + -1, -1, 93, -1, -1, -1, -1, 59, 60, -1, + 62, -1, -1, -1, -1, -1, -1, -1, 125, -1, + 37, -1, -1, -1, -1, 42, 43, -1, 45, -1, + 47, -1, -1, 260, 125, -1, -1, -1, -1, -1, + -1, 93, -1, 60, 96, 62, 273, 274, 275, 276, + 277, 278, -1, 280, -1, 37, -1, -1, -1, -1, + 42, 43, 44, 45, -1, 47, -1, -1, -1, -1, + -1, -1, -1, 125, -1, -1, -1, 94, 60, -1, + 62, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 257, 258, 259, 260, + -1, -1, 263, 264, 265, 266, 267, 268, 269, 270, + 271, 272, 94, -1, -1, -1, -1, -1, -1, -1, + 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, + 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, + 301, -1, -1, 304, 305, 306, 307, 308, 309, 310, + 311, 312, 313, 314, 315, 316, 317, 264, -1, -1, + -1, -1, 323, 324, 325, 326, 327, 328, 329, 330, + 331, 332, 333, 264, -1, 336, 337, 338, 339, -1, + -1, 342, 343, 290, 345, -1, -1, 348, 349, 350, + 351, 352, 353, 354, 355, 356, 357, 358, -1, 290, + -1, 362, 363, 364, 365, 257, 258, 259, 260, -1, + -1, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, -1, -1, -1, -1, -1, -1, -1, -1, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, + -1, -1, 304, 305, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 315, 316, 317, -1, -1, -1, 286, + -1, 323, 324, 325, 326, 327, 328, 329, 330, 331, + 332, 333, 0, -1, 336, 337, 338, 339, -1, -1, + 342, 343, -1, 345, -1, -1, 348, 349, 350, 351, + 352, 353, 354, 355, 356, 357, 358, -1, -1, -1, + 362, 363, 364, 365, -1, 33, -1, -1, -1, 37, + -1, -1, 40, 41, 42, 43, 44, 45, -1, 47, + -1, 348, 349, 350, 351, 352, 353, -1, 86, -1, + -1, 59, 60, -1, 62, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 37, -1, -1, -1, -1, 42, + 43, -1, 45, -1, 47, -1, 348, 349, 350, 351, + 352, 353, -1, -1, -1, 93, -1, 60, 96, 62, + -1, -1, -1, 131, 132, 133, -1, -1, -1, -1, + -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 125, 156, -1, + -1, 94, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 33, -1, -1, -1, 37, -1, + -1, 40, 41, 42, 43, 44, 45, -1, 47, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 197, + 59, 60, -1, 62, -1, -1, -1, 205, -1, -1, + 37, -1, -1, -1, -1, 42, 43, 37, 45, -1, + 47, -1, 42, 43, -1, 45, -1, 47, -1, -1, + -1, -1, -1, 60, 93, 62, -1, 96, -1, -1, + 60, -1, 62, -1, -1, -1, -1, -1, -1, -1, + 37, -1, -1, -1, -1, 42, 43, 44, 45, -1, + 47, -1, -1, -1, -1, -1, 125, 94, -1, -1, + -1, -1, -1, 60, 94, 62, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 257, + 258, 259, 260, -1, -1, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 303, -1, 94, -1, -1, + -1, -1, -1, 281, 282, 283, 284, 285, 286, 287, + 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, + 298, 299, 300, 301, -1, -1, 304, 305, 306, 307, + 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, + -1, -1, -1, -1, -1, 323, 324, 325, 326, 327, + 328, 329, 330, 331, 332, 333, -1, -1, 336, 337, + 338, 339, 370, 371, 342, 343, -1, 345, -1, -1, + 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, + 358, -1, -1, -1, 362, 363, 364, 365, 257, 258, + 259, 260, -1, -1, 263, 264, 265, 266, 267, 268, + 269, 270, 271, 272, -1, 348, 349, 350, 351, 352, + 353, -1, 281, 282, 283, 284, 285, 286, 287, 288, + 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, + 299, 300, 301, -1, -1, 304, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, -1, + -1, -1, 289, -1, 323, 324, 325, 326, 327, 328, + 329, 330, 331, 332, 333, 0, -1, 336, 337, 338, + 339, -1, -1, 342, 343, -1, 345, -1, -1, 348, + 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, + -1, -1, -1, 362, 363, 364, 365, -1, 33, -1, + -1, -1, -1, -1, 301, 40, 41, 304, 43, 44, + 45, 348, 349, 350, 351, 352, 353, -1, 348, 349, + 350, 351, 352, 353, 59, 60, -1, 62, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 37, -1, -1, + -1, -1, 42, 43, -1, 45, -1, 47, -1, -1, + -1, 348, 349, 350, 351, 352, 353, -1, 93, -1, + 60, 96, 62, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 125, -1, -1, -1, 94, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 33, -1, -1, + -1, -1, -1, -1, 40, 41, -1, 43, 44, 45, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 59, 60, -1, 62, -1, -1, -1, + 37, -1, -1, -1, -1, 42, 43, -1, 45, -1, + 47, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 60, -1, 62, -1, 93, -1, 37, + 96, -1, -1, -1, 42, 43, -1, 45, -1, 47, + -1, -1, -1, 37, -1, -1, -1, -1, 42, 43, + 44, 45, 60, 47, 62, -1, -1, 94, -1, 125, + -1, -1, -1, -1, -1, -1, 60, -1, 62, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 257, 258, 259, 260, 94, -1, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, -1, -1, + 94, -1, -1, -1, -1, -1, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, -1, -1, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, -1, -1, -1, -1, -1, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, -1, + -1, 336, 337, 338, 339, -1, -1, 342, 343, -1, + 345, -1, -1, 348, 349, 350, 351, 352, 353, 354, + 355, 356, 357, 358, -1, -1, 46, 362, 363, 364, + 365, 257, 258, 259, 260, -1, -1, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, -1, 348, -1, + 350, 351, 352, 353, -1, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + 296, 297, 298, 299, 300, 301, -1, -1, 304, 305, + 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, + 316, 317, -1, -1, -1, -1, -1, 323, 324, 325, + 326, 327, 328, 329, 330, 331, 332, 333, 0, -1, + 336, 337, 338, 339, -1, -1, 342, 343, -1, 345, + -1, -1, 348, 349, 350, 351, 352, 353, 354, 355, + 356, 357, 358, -1, -1, -1, 362, 363, 364, 365, + -1, 33, -1, -1, -1, -1, -1, 301, 40, 41, + 304, 348, 44, 350, 351, 352, 353, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 59, 60, -1, + 62, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 350, 351, 352, 353, -1, -1, -1, -1, + -1, -1, -1, -1, 348, 349, 350, 351, 352, 353, + -1, 93, -1, -1, 96, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 125, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 33, 271, 272, -1, -1, -1, -1, 40, 41, -1, + -1, 44, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 59, 60, -1, 62, + -1, -1, -1, -1, -1, -1, -1, 307, 308, 309, + 310, 311, 312, 313, 314, 315, 316, 317, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 93, -1, -1, 96, -1, -1, 336, 337, 338, 339, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 354, 355, 356, 357, 358, -1, + -1, -1, 125, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 46, -1, -1, -1, -1, 257, 258, 259, 260, -1, + -1, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, -1, -1, -1, -1, -1, -1, -1, -1, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, + -1, -1, 304, 305, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 315, 316, 317, -1, -1, -1, -1, + -1, 323, 324, 325, 326, 327, 328, 329, 330, 331, + 332, 333, -1, -1, 336, 337, 338, 339, -1, -1, + 342, 343, -1, 345, -1, -1, 348, 349, 350, 351, + 352, 353, 354, 355, 356, 357, 358, 46, -1, -1, + 362, 363, 364, 365, 257, 258, 259, 260, -1, -1, + 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, + -1, -1, -1, -1, -1, -1, -1, -1, 281, 282, + 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, + 293, 294, 295, 296, 297, 298, 299, 300, 301, -1, + -1, 304, 305, 306, 307, 308, 309, 310, 311, 312, + 313, 314, 315, 316, 317, -1, -1, -1, -1, -1, + 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, + 333, 0, -1, 336, 337, 338, 339, -1, -1, 342, + 343, -1, 345, -1, -1, 348, 349, 350, 351, 352, + 353, 354, 355, 356, 357, 358, -1, -1, -1, 362, + 363, 364, 365, -1, 33, 271, 272, -1, -1, -1, + -1, 40, 41, -1, -1, 44, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 59, 60, -1, 62, -1, -1, -1, -1, -1, -1, + -1, 307, 308, 309, 310, 311, 312, 313, 314, 315, + 316, 317, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 93, -1, -1, 96, -1, -1, + 336, 337, 338, 339, -1, -1, -1, -1, -1, -1, + 0, -1, -1, -1, -1, -1, -1, -1, 354, 355, + 356, 357, 358, -1, -1, -1, 125, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 33, -1, -1, -1, -1, -1, -1, + 40, 41, -1, -1, 44, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 59, + 60, -1, 62, -1, -1, -1, -1, -1, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 93, -1, -1, 96, 336, 337, 338, + 339, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 354, 355, 356, 357, 358, + -1, -1, -1, -1, -1, 125, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 257, 258, + 259, 260, -1, -1, 263, 264, 265, 266, 267, 268, + 269, 270, 271, 272, -1, -1, -1, -1, -1, -1, + -1, -1, 281, 282, 283, 284, 285, 286, 287, 288, + 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, + 299, 300, 301, -1, -1, 304, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, -1, + -1, -1, -1, -1, 323, 324, 325, 326, 327, 328, + 329, 330, 331, 332, 333, -1, -1, 336, 337, 338, + 339, -1, -1, 342, 343, -1, 345, -1, -1, 348, + 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, + -1, -1, -1, 362, 363, 364, 365, 257, 258, 259, + 260, -1, -1, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, -1, -1, -1, -1, -1, -1, -1, + -1, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, + 300, 301, -1, -1, 304, 305, 306, 307, 308, 309, + 310, 311, 312, 313, 314, 315, 316, 317, -1, -1, + -1, -1, -1, 323, 324, 325, 326, 327, 328, 329, + 330, 331, 332, 333, 0, -1, 336, 337, 338, 339, + -1, -1, 342, 343, -1, 345, -1, -1, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 358, -1, + -1, -1, 362, 363, 364, 365, -1, 33, -1, -1, + -1, -1, -1, -1, 40, 41, -1, 43, 44, 45, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 59, -1, -1, 62, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 24, -1, -1, -1, -1, + -1, 30, -1, -1, -1, -1, -1, 93, -1, -1, + 96, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 125, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 33, 86, -1, -1, + -1, -1, -1, 40, 41, -1, -1, 44, -1, -1, + -1, -1, 101, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 59, -1, -1, 114, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 131, 132, 133, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 93, -1, -1, 96, + -1, -1, -1, -1, -1, -1, -1, 156, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 125, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 197, -1, + -1, 257, 258, 259, 260, -1, 205, 263, -1, 265, + 266, 267, 268, 269, 270, 271, 272, 40, -1, -1, + -1, -1, -1, 46, -1, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + 296, 297, 298, 299, 300, -1, -1, -1, -1, 305, + 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, + 326, 327, 328, 329, 330, 331, 332, 333, -1, -1, + 336, 337, 338, 339, -1, -1, 342, 343, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 354, 355, + 356, 357, 358, -1, 303, -1, 362, 363, 364, 365, + 257, 258, 259, 260, -1, -1, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, -1, -1, -1, -1, + -1, -1, -1, -1, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, -1, -1, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, 370, 371, -1, -1, -1, 323, 324, 325, 326, + 327, 328, 329, 330, 331, 332, 333, 0, -1, 336, + 337, 338, 339, -1, -1, 342, 343, -1, 345, -1, + -1, 348, 349, 350, 351, -1, -1, 354, 355, 356, + 357, 358, -1, -1, -1, 362, 363, 364, 365, -1, + 33, -1, -1, -1, -1, -1, -1, 40, 41, -1, + -1, 44, -1, -1, 257, -1, -1, -1, -1, -1, + 263, -1, -1, -1, -1, 268, 59, -1, 271, 272, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, + 93, -1, -1, 96, 307, 308, 309, 310, 311, 312, + 313, 314, 315, 316, 317, -1, 0, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 125, 336, 337, 338, 339, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 33, + 59, 354, 355, 356, 357, 358, 40, 41, -1, 43, + 44, 45, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 59, -1, -1, 62, -1, + -1, -1, -1, -1, 93, -1, -1, 96, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, + -1, -1, 96, -1, -1, -1, 125, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 125, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 257, 258, 259, 260, -1, -1, + 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, + -1, -1, -1, -1, -1, -1, -1, -1, 281, 282, + 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, + 293, 294, 295, 296, 297, 298, 299, 300, 301, -1, + -1, 304, 305, 306, 307, 308, 309, 310, 311, 312, + 313, 314, 315, 316, 317, -1, -1, -1, -1, -1, + 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, + 333, -1, -1, 336, 337, 338, 339, -1, 257, 342, + 343, -1, 345, -1, 263, 348, 349, 350, 351, 268, + -1, 354, 355, 356, 357, 358, -1, -1, -1, 362, + 363, 364, 365, 257, 258, 259, 260, -1, -1, 263, + -1, 265, 266, 267, 268, 269, 270, 271, 272, -1, + -1, -1, 301, -1, -1, -1, -1, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, -1, -1, -1, + -1, 305, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + 0, -1, 336, 337, 338, 339, -1, -1, 342, 343, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 354, 355, 356, 357, 358, -1, -1, -1, 362, 363, + 364, 365, -1, 33, -1, -1, -1, -1, -1, -1, + 40, 41, -1, -1, 44, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 59, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, + -1, -1, -1, 93, -1, -1, 96, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 125, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 33, 59, -1, -1, -1, -1, -1, 40, + 41, -1, -1, 44, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 59, -1, + -1, -1, -1, -1, -1, -1, -1, 93, -1, -1, + 96, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 93, -1, -1, 96, -1, -1, -1, 125, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 125, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 257, 258, 259, + 260, -1, -1, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, -1, -1, -1, -1, -1, -1, -1, + -1, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, + 300, 301, -1, -1, 304, 305, 306, 307, 308, 309, + 310, 311, 312, 313, 314, 315, 316, 317, -1, -1, + -1, -1, -1, 323, 324, 325, 326, 327, 328, 329, + 330, 331, 332, 333, -1, -1, 336, 337, 338, 339, + -1, 257, 342, 343, -1, 345, -1, 263, 348, 349, + -1, -1, 268, -1, 354, 355, 356, 357, 358, -1, + -1, -1, 362, 363, 364, 365, 257, 258, 259, 260, + -1, -1, 263, 264, 265, 266, 267, 268, 269, 270, + 271, 272, -1, -1, -1, 301, -1, -1, -1, -1, + 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, + 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, + 301, -1, -1, 304, 305, 306, 307, 308, 309, 310, + 311, 312, 313, 314, 315, 316, 317, -1, -1, -1, + -1, -1, 323, 324, 325, 326, 327, 328, 329, 330, + 331, 332, 333, 0, -1, 336, 337, 338, 339, -1, + -1, 342, 343, -1, 345, -1, -1, -1, 349, -1, + -1, -1, -1, 354, 355, 356, 357, 358, -1, -1, + -1, 362, 363, 364, 365, -1, 33, -1, -1, -1, + -1, -1, -1, 40, 41, -1, 43, 44, 45, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 59, -1, -1, 62, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 93, -1, -1, 96, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 125, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 33, -1, -1, -1, -1, + -1, -1, 40, 41, -1, -1, 44, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 59, -1, -1, 62, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 93, -1, -1, 96, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 125, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 257, 258, 259, 260, -1, -1, 263, -1, 265, 266, + 267, 268, 269, 270, 271, 272, -1, -1, -1, -1, + -1, -1, -1, -1, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, -1, -1, -1, -1, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, -1, -1, -1, -1, -1, 323, 324, 325, 326, + 327, 328, 329, 330, 331, 332, 333, -1, -1, 336, + 337, 338, 339, -1, -1, 342, 343, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 354, 355, 356, + 357, 358, -1, -1, -1, 362, 363, 364, 365, 257, + 258, 259, 260, -1, -1, 263, -1, 265, 266, 267, + 268, 269, 270, 271, 272, -1, -1, -1, -1, -1, + -1, -1, -1, 281, 282, 283, 284, 285, 286, 287, + 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, + 298, 299, 300, -1, -1, -1, -1, 305, 306, 307, + 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, + -1, -1, -1, -1, -1, 323, 324, 325, 326, 327, + 328, 329, 330, 331, 332, 333, 0, -1, 336, 337, + 338, 339, -1, -1, 342, 343, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 354, 355, 356, 357, + 358, -1, -1, -1, 362, 363, 364, 365, -1, 33, + -1, -1, -1, -1, -1, -1, 40, 41, -1, -1, + 44, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 59, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, + -1, -1, 96, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 125, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 33, -1, + -1, -1, -1, -1, -1, 40, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 59, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 93, -1, + -1, 96, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 125, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 257, 258, 259, 260, -1, -1, 263, + -1, 265, 266, 267, 268, 269, 270, 271, 272, -1, + -1, -1, -1, -1, -1, -1, -1, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, -1, -1, -1, + -1, 305, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, -1, -1, -1, -1, -1, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + -1, -1, 336, 337, 338, 339, -1, -1, 342, 343, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 354, 355, 356, 357, 358, -1, -1, -1, 362, 363, + 364, 365, 257, 258, 259, 260, -1, -1, 263, -1, + 265, 266, 267, 268, 269, 270, 271, 272, -1, -1, + -1, -1, -1, -1, -1, -1, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 0, -1, -1, -1, + -1, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, -1, -1, -1, -1, -1, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 33, + -1, 336, 337, 338, 339, -1, 40, 342, 343, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 354, + 355, 356, 357, 358, -1, 59, -1, 362, 363, 364, + 365, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, + -1, -1, 96, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 125, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 33, -1, + -1, -1, -1, -1, -1, 40, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 59, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 93, -1, + -1, 96, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 125, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 257, 258, 259, 260, -1, -1, 263, + -1, 265, 266, 267, 268, 269, 270, 271, 272, -1, + -1, -1, -1, -1, -1, -1, -1, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, -1, -1, -1, + -1, -1, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, -1, -1, -1, -1, -1, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + -1, -1, 336, 337, 338, 339, -1, -1, 342, 343, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 354, 355, 356, 357, 358, -1, -1, -1, 362, 363, + 364, 365, 257, 258, 259, 260, -1, -1, 263, -1, + 265, 266, 267, 268, 269, 270, 271, 272, -1, -1, + -1, -1, -1, -1, -1, -1, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 0, -1, -1, -1, + -1, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, -1, -1, -1, -1, -1, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 33, + -1, 336, 337, 338, 339, -1, 40, 342, 343, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 354, + 355, 356, 357, 358, -1, 59, -1, 362, 363, 364, + 365, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, + -1, -1, 96, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 125, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 33, -1, + -1, -1, -1, -1, -1, 40, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 59, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 93, -1, + -1, 96, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 125, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 257, 258, 259, 260, -1, -1, 263, + -1, 265, 266, 267, 268, 269, 270, 271, 272, -1, + -1, -1, -1, -1, -1, -1, -1, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, -1, -1, -1, + -1, -1, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, -1, -1, -1, -1, -1, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + -1, -1, 336, 337, 338, 339, -1, -1, 342, 343, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 354, 355, 356, 357, 358, -1, -1, -1, 362, 363, + 364, 365, 257, 258, 259, 260, -1, -1, 263, -1, + 265, 266, 267, 268, 269, 270, 271, 272, -1, -1, + -1, -1, -1, -1, -1, -1, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 0, -1, -1, -1, + -1, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, -1, -1, -1, -1, -1, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 33, + -1, 336, 337, 338, 339, -1, 40, 342, 343, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 354, + 355, 356, 357, 358, -1, 59, -1, 362, 363, 364, + 365, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, + -1, -1, 96, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 125, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 33, -1, + -1, -1, -1, -1, -1, 40, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 59, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 93, -1, + -1, 96, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 125, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 257, 258, 259, 260, -1, -1, 263, + -1, 265, 266, 267, 268, 269, 270, 271, 272, -1, + -1, -1, -1, -1, -1, -1, -1, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, -1, -1, -1, + -1, -1, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, -1, -1, -1, -1, -1, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + -1, -1, 336, 337, 338, 339, -1, -1, 342, 343, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 354, 355, 356, 357, 358, -1, -1, -1, 362, 363, + 364, 365, 257, 258, 259, 260, -1, -1, 263, -1, + 265, 266, 267, 268, 269, 270, 271, 272, -1, -1, + -1, -1, -1, -1, -1, -1, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 0, -1, -1, -1, + -1, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, -1, -1, -1, -1, -1, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 33, + -1, 336, 337, 338, 339, -1, 40, 342, 343, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 354, + 355, 356, 357, 358, -1, 59, -1, 362, 363, 364, + 365, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, + -1, -1, 96, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 125, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 33, -1, + -1, -1, -1, -1, -1, 40, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 59, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 93, -1, + -1, 96, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 125, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 257, 258, 259, 260, -1, -1, 263, + -1, 265, 266, 267, 268, 269, 270, 271, 272, -1, + -1, -1, -1, -1, -1, -1, -1, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, -1, -1, -1, + -1, -1, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, -1, -1, -1, -1, -1, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + -1, -1, 336, 337, 338, 339, -1, -1, 342, 343, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 354, 355, 356, 357, 358, -1, -1, -1, 362, 363, + 364, 365, 257, 258, 259, 260, -1, -1, 263, -1, + 265, 266, 267, 268, 269, 270, 271, 272, -1, -1, + -1, -1, -1, -1, -1, -1, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 0, -1, -1, -1, + -1, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, -1, -1, -1, -1, -1, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 33, + -1, 336, 337, 338, 339, -1, 40, 342, 343, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 354, + 355, 356, 357, 358, -1, 59, -1, 362, 363, 364, + 365, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, + -1, -1, 96, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 125, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 33, -1, + -1, -1, -1, -1, -1, 40, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 59, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 93, -1, + -1, 96, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 125, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 257, 258, 259, 260, -1, -1, 263, + -1, 265, 266, 267, 268, 269, 270, 271, 272, -1, + -1, -1, -1, -1, -1, -1, -1, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, -1, -1, -1, + -1, -1, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, -1, -1, -1, -1, -1, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + -1, -1, 336, 337, 338, 339, -1, -1, 342, 343, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 354, 355, 356, 357, 358, -1, -1, -1, 362, 363, + 364, 365, 257, 258, 259, 260, -1, -1, 263, -1, + 265, 266, 267, 268, 269, 270, 271, 272, -1, -1, + -1, -1, -1, -1, -1, -1, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 0, -1, -1, -1, + -1, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, -1, -1, -1, -1, -1, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 33, + -1, 336, 337, 338, 339, -1, 40, 342, 343, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 354, + 355, 356, 357, 358, -1, 59, -1, 362, 363, 364, + 365, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, + -1, -1, 96, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 125, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 33, -1, + -1, -1, -1, -1, -1, 40, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 59, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 93, -1, + -1, 96, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 125, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 257, 258, 259, 260, -1, -1, 263, + -1, 265, 266, 267, 268, 269, 270, 271, 272, -1, + -1, -1, -1, -1, -1, -1, -1, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, -1, -1, -1, + -1, -1, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, -1, -1, -1, -1, -1, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + -1, -1, 336, 337, 338, 339, -1, -1, 342, 343, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 354, 355, 356, 357, 358, -1, -1, -1, 362, 363, + 364, 365, 257, 258, 259, 260, -1, -1, 263, -1, + 265, 266, 267, 268, 269, 270, 271, 272, -1, -1, + -1, -1, -1, -1, -1, -1, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 0, -1, -1, -1, + -1, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, -1, -1, -1, -1, -1, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 33, + -1, 336, 337, 338, 339, -1, 40, 342, 343, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 354, + 355, 356, 357, 358, -1, 59, -1, 362, 363, 364, + 365, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, + -1, -1, 96, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 125, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 33, -1, + -1, -1, -1, -1, -1, 40, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 59, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 93, -1, + -1, 96, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 125, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 257, 258, 259, 260, -1, -1, 263, + -1, 265, 266, 267, 268, 269, 270, 271, 272, -1, + -1, -1, -1, -1, -1, -1, -1, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, -1, -1, -1, + -1, -1, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, -1, -1, -1, -1, -1, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + -1, -1, 336, 337, 338, 339, -1, -1, 342, 343, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 354, 355, 356, 357, 358, -1, -1, -1, 362, 363, + 364, 365, 257, 258, 259, 260, -1, -1, 263, -1, + 265, 266, 267, 268, 269, 270, 271, 272, -1, -1, + -1, -1, -1, -1, -1, -1, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 0, -1, -1, -1, + -1, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, -1, -1, -1, -1, -1, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 33, + -1, 336, 337, 338, 339, -1, 40, 342, 343, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 354, + 355, 356, 357, 358, -1, 59, -1, 362, 363, 364, + 365, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, + -1, -1, 96, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 125, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 33, -1, + -1, -1, -1, -1, -1, 40, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 59, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 93, -1, + -1, 96, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 125, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 257, 258, 259, 260, -1, -1, 263, + -1, 265, 266, 267, 268, 269, 270, 271, 272, -1, + -1, -1, -1, -1, -1, -1, -1, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, -1, -1, -1, + -1, -1, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, -1, -1, -1, -1, -1, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + -1, -1, 336, 337, 338, 339, -1, -1, 342, 343, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 354, 355, 356, 357, 358, -1, -1, -1, 362, 363, + 364, 365, 257, 258, 259, 260, -1, -1, 263, -1, + 265, 266, 267, 268, 269, 270, 271, 272, -1, -1, + -1, -1, -1, -1, -1, -1, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, -1, -1, -1, -1, + -1, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, -1, -1, -1, -1, -1, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 0, + -1, 336, 337, 338, 339, -1, -1, 342, 343, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 354, + 355, 356, 357, 358, -1, -1, -1, 362, 363, 364, + 365, -1, 33, -1, -1, -1, -1, -1, -1, 40, + 41, -1, 43, 44, 45, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 59, -1, + -1, 62, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, + -1, -1, 93, -1, -1, 96, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 33, + -1, -1, -1, -1, 125, -1, 40, 41, -1, -1, + 44, 45, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 59, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, + -1, -1, 96, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 125, -1, 0, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 33, -1, -1, -1, + -1, -1, -1, 40, -1, -1, 257, 258, 259, 260, + -1, -1, 263, -1, 265, 266, 267, 268, 269, 270, + 271, 272, 59, -1, -1, -1, -1, -1, -1, -1, + 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, + 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, + -1, -1, -1, -1, 305, 306, 93, -1, -1, 96, + -1, -1, -1, -1, -1, -1, -1, 318, 319, 320, + 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, + 331, 332, 333, -1, -1, -1, -1, -1, 125, -1, + -1, 342, 343, 257, 258, 259, 260, -1, -1, 263, + -1, 265, 266, 267, 268, 269, 270, -1, -1, -1, + -1, 362, 363, 364, 365, -1, -1, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, -1, -1, -1, + -1, -1, 306, 0, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + -1, -1, -1, -1, -1, -1, 33, -1, 342, 343, + -1, -1, -1, 40, 41, -1, -1, 44, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 362, 363, + 364, 365, 59, 60, -1, 62, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 257, 258, 259, 260, -1, -1, 263, -1, -1, -1, + -1, 268, -1, -1, 271, 272, 93, -1, -1, 96, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 0, -1, -1, -1, 125, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, -1, -1, -1, -1, -1, 323, 324, 325, 326, + 327, 328, 329, 330, 331, 332, 333, 33, -1, 336, + 337, 338, 339, -1, 40, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 354, 355, 356, + 357, 358, -1, 59, -1, -1, -1, -1, 365, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 93, -1, -1, + 96, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 0, -1, -1, -1, -1, 125, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 257, 258, 259, 260, -1, -1, 263, -1, -1, -1, + -1, 268, -1, -1, 271, 272, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 59, -1, -1, -1, -1, -1, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, -1, -1, -1, -1, -1, 323, 324, 325, 326, + 327, 328, 329, 330, 331, 332, 333, 93, -1, 336, + 337, 338, 339, -1, -1, -1, -1, -1, -1, -1, + 0, 348, 349, 350, 351, 352, 353, 354, 355, 356, + 357, 358, -1, -1, -1, -1, -1, -1, 365, 125, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 257, 258, 259, 260, -1, -1, 263, -1, -1, + -1, -1, 268, -1, -1, 271, 272, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 59, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, + 316, 317, -1, 93, -1, -1, -1, 323, 324, 325, + 326, 327, 328, 329, 330, 331, 332, 333, 0, -1, + 336, 337, 338, 339, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 125, -1, -1, 354, 355, + 356, 357, 358, -1, -1, -1, -1, -1, -1, 365, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, + -1, -1, -1, -1, 260, -1, -1, -1, -1, 265, + 266, 267, -1, 269, 270, 271, 272, 59, -1, -1, + -1, -1, -1, -1, -1, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + 296, 297, 298, 299, 300, 301, -1, -1, -1, -1, + -1, 93, -1, -1, -1, -1, -1, -1, 59, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 125, -1, -1, 342, 343, -1, -1, + -1, -1, 93, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 362, 363, 364, 365, + 260, 0, -1, -1, -1, 265, 266, 267, -1, 269, + 270, 271, 272, -1, 125, -1, -1, -1, -1, -1, + -1, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, + 300, 301, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 59, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 342, 343, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 93, -1, -1, -1, -1, -1, + -1, -1, 362, 363, 364, 365, -1, -1, 260, 0, + -1, -1, -1, 265, 266, 267, -1, 269, 270, 271, + 272, -1, -1, -1, -1, -1, 125, -1, -1, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 300, 260, + 0, -1, -1, -1, 265, 266, 267, -1, 269, 270, + 271, 272, -1, -1, -1, -1, -1, -1, 59, -1, + 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, + 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, + 342, 343, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 93, -1, -1, -1, -1, -1, -1, 59, + 362, 363, 364, 365, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 342, 343, -1, 125, -1, -1, -1, -1, -1, + -1, -1, -1, 93, -1, -1, -1, -1, -1, -1, + -1, 362, 363, 364, 365, -1, -1, 0, -1, 0, + -1, 260, -1, -1, -1, -1, 265, 266, 267, -1, + 269, 270, 271, 272, -1, 125, -1, -1, -1, -1, + -1, -1, 281, 282, 283, 284, 285, 286, 287, 288, + 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, + 299, 300, 43, -1, 45, 46, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 59, -1, 59, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 342, 343, -1, -1, -1, -1, -1, + 93, -1, 93, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 362, 363, 364, 365, -1, -1, 260, + -1, -1, -1, -1, 265, 266, 267, -1, 269, 270, + 271, 272, 125, -1, 125, -1, -1, -1, -1, -1, + 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, + 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, + 260, -1, -1, -1, -1, 265, 266, 267, -1, 269, + 270, 271, 272, -1, -1, -1, -1, -1, -1, -1, + -1, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, + 300, 342, 343, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, + -1, 362, 363, 364, 365, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 342, 343, -1, -1, -1, -1, -1, 33, + -1, -1, -1, -1, -1, -1, 40, -1, -1, -1, + -1, -1, 362, 363, 364, 365, -1, 260, -1, -1, + -1, -1, 265, 266, 267, 59, 269, 270, 271, 272, + 271, 272, -1, -1, -1, -1, -1, -1, 281, 282, + 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, + 293, 294, 295, 296, 297, 298, 299, 300, -1, 93, + -1, -1, 96, -1, -1, -1, 307, 308, 309, 310, + 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, + 321, 322, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 125, -1, -1, -1, 336, 337, 338, 339, 342, + 343, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 33, -1, 354, 355, 356, 357, 358, 40, 362, + 363, 364, 365, 45, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 91, + -1, -1, -1, -1, 96, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 123, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 257, 258, 259, -1, -1, -1, 263, + -1, 265, 266, 267, 268, 269, 270, 271, 272, -1, + -1, -1, -1, -1, -1, -1, -1, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, -1, -1, -1, + -1, -1, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, -1, -1, -1, -1, -1, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + -1, -1, 336, 337, 338, 339, 33, -1, 342, 343, + -1, -1, -1, 40, -1, -1, -1, -1, 45, -1, + 354, 355, 356, 357, 358, -1, -1, -1, 362, 363, + 364, -1, -1, -1, -1, 257, 258, 259, 260, 261, + -1, 263, -1, -1, -1, -1, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, -1, + -1, -1, -1, -1, 91, -1, -1, -1, -1, 96, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 315, 316, 317, 123, -1, -1, -1, + -1, 323, 324, 325, 326, 327, 328, 329, 330, 331, + 332, 333, 334, -1, 336, 337, 338, 339, 340, 341, + -1, -1, 344, -1, 346, -1, -1, -1, -1, -1, + -1, -1, 354, 355, 356, 357, 358, 359, 33, 361, + -1, -1, -1, 365, 366, 40, -1, -1, -1, -1, + 45, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 96, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 257, 258, 259, 260, 261, -1, 263, -1, -1, -1, + -1, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, -1, 33, -1, -1, -1, -1, + -1, -1, 40, 41, -1, -1, -1, 45, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, -1, -1, -1, -1, -1, 323, 324, 325, 326, + 327, 328, 329, 330, 331, 332, 333, 334, -1, 336, + 337, 338, 339, 340, 341, -1, -1, 344, 96, 346, + -1, -1, -1, -1, -1, -1, -1, 354, 355, 356, + 357, 358, 359, -1, 361, -1, -1, -1, 365, 366, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 257, 258, 259, 260, -1, -1, 263, -1, + 265, 266, 267, 268, 269, 270, 271, 272, -1, -1, + -1, -1, -1, -1, -1, -1, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, -1, -1, -1, -1, + -1, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, -1, -1, -1, -1, -1, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 33, + -1, 336, 337, 338, 339, -1, 40, 342, 343, -1, + -1, 45, -1, -1, -1, -1, -1, -1, -1, 354, + 355, 356, 357, 358, -1, -1, -1, 362, 363, 364, + 365, -1, -1, -1, -1, -1, -1, -1, -1, 257, + 258, 259, 260, -1, -1, 263, -1, -1, -1, -1, + 268, -1, -1, 271, 272, -1, -1, -1, -1, -1, + -1, -1, 96, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 306, 307, + 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, + -1, -1, -1, -1, -1, 323, 324, 325, 326, 327, + 328, 329, 330, 331, 332, 333, 33, -1, 336, 337, + 338, 339, -1, 40, -1, -1, -1, -1, 45, -1, + -1, -1, -1, -1, -1, -1, 354, 355, 356, 357, + 358, -1, -1, -1, -1, -1, -1, 365, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 96, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 257, 258, 259, 260, -1, -1, 263, + -1, -1, -1, -1, 268, -1, -1, 271, 272, -1, + -1, -1, -1, 33, -1, -1, -1, -1, -1, -1, + 40, -1, -1, -1, -1, 45, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, -1, -1, -1, -1, -1, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + -1, -1, 336, 337, 338, 339, 96, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 354, 355, 356, 357, 358, -1, -1, -1, -1, -1, + -1, 365, -1, -1, -1, -1, -1, -1, -1, -1, + 257, 258, 259, 260, -1, -1, 263, -1, -1, -1, + -1, 268, -1, -1, 271, 272, -1, -1, -1, -1, + 33, -1, -1, -1, -1, -1, -1, 40, -1, -1, + -1, -1, 45, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, -1, -1, -1, -1, -1, 323, 324, 325, 326, + 327, 328, 329, 330, 331, 332, 333, -1, -1, 336, + 337, 338, 339, 96, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 354, 355, 356, + 357, 358, -1, -1, -1, -1, -1, -1, 365, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 33, -1, 257, 258, 259, + 260, -1, 40, 263, 42, -1, -1, 45, 268, -1, + -1, 271, 272, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 306, 307, 308, 309, + 310, 311, 312, 313, 314, 315, 316, 317, 96, -1, + -1, -1, -1, 323, 324, 325, 326, 327, 328, 329, + 330, 331, 332, 333, -1, -1, 336, 337, 338, 339, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 354, 355, 356, 357, 358, 33, + -1, -1, -1, -1, -1, 365, 40, -1, -1, -1, + -1, 45, -1, -1, 257, 258, 259, 260, -1, -1, + 263, -1, -1, -1, -1, 268, -1, -1, 271, 272, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 59, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 96, 306, 307, 308, 309, 310, 311, 312, + 313, 314, 315, 316, 317, -1, 91, -1, -1, -1, + 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, + 333, -1, -1, 336, 337, 338, 339, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 123, -1, + -1, 354, 355, 356, 357, 358, -1, 33, -1, 257, + 258, 259, 365, -1, 40, 263, -1, -1, -1, 45, + 268, -1, -1, 271, 272, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 306, 307, + 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, + 96, -1, -1, -1, -1, 323, 324, 325, 326, 327, + 328, 329, 330, 331, 332, 333, -1, -1, 336, 337, + 338, 339, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 354, 355, 356, 357, + 358, 33, -1, 257, 258, 259, -1, -1, 40, 263, + -1, -1, -1, 45, 268, -1, -1, 271, 272, -1, + -1, -1, 257, 258, -1, 260, 261, -1, -1, -1, + -1, -1, -1, -1, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, -1, -1, -1, -1, + -1, -1, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, 96, -1, -1, -1, -1, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + -1, -1, 336, 337, 338, 339, 33, -1, -1, -1, + -1, -1, -1, 40, -1, -1, -1, -1, 45, 334, + 354, 355, 356, 357, 358, 340, 341, -1, -1, 344, + -1, 346, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 257, 258, 259, 359, -1, 361, 263, -1, -1, + 365, 366, 268, -1, -1, 271, 272, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 96, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, + 316, 317, -1, -1, -1, -1, -1, 323, 324, 325, + 326, 327, 328, 329, 330, 331, 332, 333, -1, -1, + 336, 337, 338, 339, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 354, 355, + 356, 357, 358, -1, -1, 257, 258, 259, -1, -1, + -1, 263, -1, -1, 91, -1, 268, -1, -1, 271, + 272, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 123, -1, -1, -1, + 91, -1, -1, -1, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 315, 316, 317, -1, -1, -1, -1, + -1, 323, 324, 325, 326, 327, 328, 329, 330, 331, + 332, 333, 123, -1, 336, 337, 338, 339, -1, -1, + 257, 258, 259, -1, -1, -1, 263, -1, -1, -1, + -1, 268, 354, 355, 356, 357, 358, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, -1, -1, -1, -1, -1, 323, 324, 325, 326, + 327, 328, 329, 330, 331, 332, 333, -1, -1, 336, + 337, 338, 339, -1, -1, -1, -1, -1, -1, -1, + 257, 258, -1, 260, 261, -1, -1, 354, 355, 356, + 357, 358, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 257, 258, -1, 260, + 261, -1, -1, -1, -1, -1, -1, -1, 269, 270, + 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 334, -1, -1, + -1, -1, -1, 340, 341, -1, -1, 344, -1, 346, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 359, -1, 361, -1, -1, -1, 365, 366, + -1, -1, -1, 334, -1, -1, -1, -1, -1, 340, + 341, -1, -1, 344, -1, 346, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 359, -1, + 361, -1, -1, -1, 365, 366, +}; +#define YYFINAL 2 +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif +#define YYMAXTOKEN 368 +#if YYDEBUG +char *yyname[] = { +"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +"'!'",0,0,0,"'%'",0,0,"'('","')'","'*'","'+'","','","'-'","'.'","'/'",0,0,0,0,0, +0,0,0,0,0,"':'","';'","'<'","'='","'>'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,"'['",0,"']'","'^'",0,"'`'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,"'{'",0,"'}'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"LABEL","VARIABLE","NUMBER", +"TEXT","COMMAND_LINE","DELIMITED","ORDINAL","TH","LEFT_ARROW_HEAD", +"RIGHT_ARROW_HEAD","DOUBLE_ARROW_HEAD","LAST","UP","DOWN","LEFT","RIGHT","BOX", +"CIRCLE","ELLIPSE","ARC","LINE","ARROW","MOVE","SPLINE","HEIGHT","RADIUS", +"WIDTH","DIAMETER","FROM","TO","AT","WITH","BY","THEN","SOLID","DOTTED", +"DASHED","CHOP","SAME","INVISIBLE","LJUST","RJUST","ABOVE","BELOW","OF","THE", +"WAY","BETWEEN","AND","HERE","DOT_N","DOT_E","DOT_W","DOT_S","DOT_NE","DOT_SE", +"DOT_NW","DOT_SW","DOT_C","DOT_START","DOT_END","DOT_X","DOT_Y","DOT_HT", +"DOT_WID","DOT_RAD","SIN","COS","ATAN2","LOG","EXP","SQRT","K_MAX","K_MIN", +"INT","RAND","SRAND","COPY","THRU","TOP","BOTTOM","UPPER","LOWER","SH","PRINT", +"CW","CCW","FOR","DO","IF","ELSE","ANDAND","OROR","NOTEQUAL","EQUALEQUAL", +"LESSEQUAL","GREATEREQUAL","LEFT_CORNER","RIGHT_CORNER","CENTER","END","START", +"RESET","UNTIL","PLOT","THICKNESS","FILL","ALIGNED","SPRINTF","COMMAND", +"DEFINE","UNDEF", +}; +char *yyrule[] = { +"$accept : top", +"top : optional_separator", +"top : element_list", +"element_list : optional_separator middle_element_list optional_separator", +"middle_element_list : element", +"middle_element_list : middle_element_list separator element", +"optional_separator :", +"optional_separator : separator", +"separator : ';'", +"separator : separator ';'", +"placeless_element : VARIABLE '=' any_expr", +"placeless_element : VARIABLE ':' '=' any_expr", +"placeless_element : UP", +"placeless_element : DOWN", +"placeless_element : LEFT", +"placeless_element : RIGHT", +"placeless_element : COMMAND_LINE", +"placeless_element : COMMAND print_args", +"placeless_element : PRINT print_args", +"$$1 :", +"placeless_element : SH $$1 DELIMITED", +"placeless_element : COPY TEXT", +"$$2 :", +"$$3 :", +"placeless_element : COPY TEXT THRU $$2 DELIMITED $$3 until", +"$$4 :", +"$$5 :", +"placeless_element : COPY THRU $$4 DELIMITED $$5 until", +"$$6 :", +"placeless_element : FOR VARIABLE '=' expr TO expr optional_by DO $$6 DELIMITED", +"placeless_element : simple_if", +"$$7 :", +"placeless_element : simple_if ELSE $$7 DELIMITED", +"placeless_element : reset_variables", +"placeless_element : RESET", +"reset_variables : RESET VARIABLE", +"reset_variables : reset_variables VARIABLE", +"reset_variables : reset_variables ',' VARIABLE", +"print_args : print_arg", +"print_args : print_args print_arg", +"print_arg : expr", +"print_arg : text", +"print_arg : position", +"$$8 :", +"simple_if : IF any_expr THEN $$8 DELIMITED", +"until :", +"until : UNTIL TEXT", +"any_expr : expr", +"any_expr : text_expr", +"text_expr : text EQUALEQUAL text", +"text_expr : text NOTEQUAL text", +"text_expr : text_expr ANDAND text_expr", +"text_expr : text_expr ANDAND expr", +"text_expr : expr ANDAND text_expr", +"text_expr : text_expr OROR text_expr", +"text_expr : text_expr OROR expr", +"text_expr : expr OROR text_expr", +"text_expr : '!' text_expr", +"optional_by :", +"optional_by : BY expr", +"optional_by : BY '*' expr", +"element : object_spec", +"element : LABEL ':' optional_separator element", +"element : LABEL ':' optional_separator position_not_place", +"element : LABEL ':' optional_separator place", +"$$9 :", +"$$10 :", +"element : '{' $$9 element_list '}' $$10 optional_element", +"element : placeless_element", +"optional_element :", +"optional_element : element", +"object_spec : BOX", +"object_spec : CIRCLE", +"object_spec : ELLIPSE", +"object_spec : ARC", +"object_spec : LINE", +"object_spec : ARROW", +"object_spec : MOVE", +"object_spec : SPLINE", +"object_spec : text", +"object_spec : PLOT expr", +"object_spec : PLOT expr text", +"$$11 :", +"object_spec : '[' $$11 element_list ']'", +"object_spec : object_spec HEIGHT expr", +"object_spec : object_spec RADIUS expr", +"object_spec : object_spec WIDTH expr", +"object_spec : object_spec DIAMETER expr", +"object_spec : object_spec expr", +"object_spec : object_spec UP", +"object_spec : object_spec UP expr", +"object_spec : object_spec DOWN", +"object_spec : object_spec DOWN expr", +"object_spec : object_spec RIGHT", +"object_spec : object_spec RIGHT expr", +"object_spec : object_spec LEFT", +"object_spec : object_spec LEFT expr", +"object_spec : object_spec FROM position", +"object_spec : object_spec TO position", +"object_spec : object_spec AT position", +"object_spec : object_spec WITH path", +"object_spec : object_spec BY expr_pair", +"object_spec : object_spec THEN", +"object_spec : object_spec SOLID", +"object_spec : object_spec DOTTED", +"object_spec : object_spec DOTTED expr", +"object_spec : object_spec DASHED", +"object_spec : object_spec DASHED expr", +"object_spec : object_spec FILL", +"object_spec : object_spec FILL expr", +"object_spec : object_spec CHOP", +"object_spec : object_spec CHOP expr", +"object_spec : object_spec SAME", +"object_spec : object_spec INVISIBLE", +"object_spec : object_spec LEFT_ARROW_HEAD", +"object_spec : object_spec RIGHT_ARROW_HEAD", +"object_spec : object_spec DOUBLE_ARROW_HEAD", +"object_spec : object_spec CW", +"object_spec : object_spec CCW", +"object_spec : object_spec text", +"object_spec : object_spec LJUST", +"object_spec : object_spec RJUST", +"object_spec : object_spec ABOVE", +"object_spec : object_spec BELOW", +"object_spec : object_spec THICKNESS expr", +"object_spec : object_spec ALIGNED", +"text : TEXT", +"text : SPRINTF '(' TEXT sprintf_args ')'", +"sprintf_args :", +"sprintf_args : sprintf_args ',' expr", +"position : position_not_place", +"position : place", +"position_not_place : expr_pair", +"position_not_place : position '+' expr_pair", +"position_not_place : position '-' expr_pair", +"position_not_place : '(' position ',' position ')'", +"position_not_place : expr between position AND position", +"position_not_place : expr '<' position ',' position '>'", +"between : BETWEEN", +"between : OF THE WAY BETWEEN", +"expr_pair : expr ',' expr", +"expr_pair : '(' expr_pair ')'", +"place : label", +"place : label corner", +"place : corner label", +"place : corner OF label", +"place : HERE", +"label : LABEL", +"label : nth_primitive", +"label : label '.' LABEL", +"ordinal : ORDINAL", +"ordinal : '`' any_expr TH", +"optional_ordinal_last : LAST", +"optional_ordinal_last : ordinal LAST", +"nth_primitive : ordinal object_type", +"nth_primitive : optional_ordinal_last object_type", +"object_type : BOX", +"object_type : CIRCLE", +"object_type : ELLIPSE", +"object_type : ARC", +"object_type : LINE", +"object_type : ARROW", +"object_type : SPLINE", +"object_type : '[' ']'", +"object_type : TEXT", +"label_path : '.' LABEL", +"label_path : label_path '.' LABEL", +"relative_path : corner", +"relative_path : label_path", +"relative_path : label_path corner", +"path : relative_path", +"path : '(' relative_path ',' relative_path ')'", +"path : ORDINAL LAST object_type relative_path", +"path : LAST object_type relative_path", +"path : ORDINAL object_type relative_path", +"path : LABEL relative_path", +"corner : DOT_N", +"corner : DOT_E", +"corner : DOT_W", +"corner : DOT_S", +"corner : DOT_NE", +"corner : DOT_SE", +"corner : DOT_NW", +"corner : DOT_SW", +"corner : DOT_C", +"corner : DOT_START", +"corner : DOT_END", +"corner : TOP", +"corner : BOTTOM", +"corner : LEFT", +"corner : RIGHT", +"corner : UPPER LEFT", +"corner : LOWER LEFT", +"corner : UPPER RIGHT", +"corner : LOWER RIGHT", +"corner : LEFT_CORNER", +"corner : RIGHT_CORNER", +"corner : UPPER LEFT_CORNER", +"corner : LOWER LEFT_CORNER", +"corner : UPPER RIGHT_CORNER", +"corner : LOWER RIGHT_CORNER", +"corner : CENTER", +"corner : START", +"corner : END", +"expr : VARIABLE", +"expr : NUMBER", +"expr : place DOT_X", +"expr : place DOT_Y", +"expr : place DOT_HT", +"expr : place DOT_WID", +"expr : place DOT_RAD", +"expr : expr '+' expr", +"expr : expr '-' expr", +"expr : expr '*' expr", +"expr : expr '/' expr", +"expr : expr '%' expr", +"expr : expr '^' expr", +"expr : '-' expr", +"expr : '(' any_expr ')'", +"expr : SIN '(' any_expr ')'", +"expr : COS '(' any_expr ')'", +"expr : ATAN2 '(' any_expr ',' any_expr ')'", +"expr : LOG '(' any_expr ')'", +"expr : EXP '(' any_expr ')'", +"expr : SQRT '(' any_expr ')'", +"expr : K_MAX '(' any_expr ',' any_expr ')'", +"expr : K_MIN '(' any_expr ',' any_expr ')'", +"expr : INT '(' any_expr ')'", +"expr : RAND '(' any_expr ')'", +"expr : RAND '(' ')'", +"expr : SRAND '(' any_expr ')'", +"expr : expr '<' expr", +"expr : expr LESSEQUAL expr", +"expr : expr '>' expr", +"expr : expr GREATEREQUAL expr", +"expr : expr EQUALEQUAL expr", +"expr : expr NOTEQUAL expr", +"expr : expr ANDAND expr", +"expr : expr OROR expr", +"expr : '!' expr", +}; +#endif +#ifdef YYSTACKSIZE +#undef YYMAXDEPTH +#define YYMAXDEPTH YYSTACKSIZE +#else +#ifdef YYMAXDEPTH +#define YYSTACKSIZE YYMAXDEPTH +#else +#define YYSTACKSIZE 500 +#define YYMAXDEPTH 500 +#endif +#endif +int yydebug; +int yynerrs; +int yyerrflag; +int yychar; +short *yyssp; +YYSTYPE *yyvsp; +YYSTYPE yyval; +YYSTYPE yylval; +short yyss[YYSTACKSIZE]; +YYSTYPE yyvs[YYSTACKSIZE]; +#define yystacksize YYSTACKSIZE +#line 1546 "/home/cjk/groff/src/preproc/pic/pic.y" + +/* bison defines const to be empty unless __STDC__ is defined, which it +isn't under cfront */ + +#ifdef const +#undef const +#endif + +static struct { + const char *name; + double val; + int scaled; // non-zero if val should be multiplied by scale +} defaults_table[] = { + { "arcrad", .25, 1 }, + { "arrowht", .1, 1 }, + { "arrowwid", .05, 1 }, + { "circlerad", .25, 1 }, + { "boxht", .5, 1 }, + { "boxwid", .75, 1 }, + { "boxrad", 0.0, 1 }, + { "dashwid", .05, 1 }, + { "ellipseht", .5, 1 }, + { "ellipsewid", .75, 1 }, + { "moveht", .5, 1 }, + { "movewid", .5, 1 }, + { "lineht", .5, 1 }, + { "linewid", .5, 1 }, + { "textht", 0.0, 1 }, + { "textwid", 0.0, 1 }, + { "scale", 1.0, 0 }, + { "linethick", -1.0, 0 }, // in points + { "fillval", .5, 0 }, + { "arrowhead", 1.0, 0 }, + { "maxpswid", 8.5, 0 }, + { "maxpsht", 11.0, 0 }, +}; + +place *lookup_label(const char *label) +{ + saved_state *state = current_saved_state; + PTABLE(place) *tbl = current_table; + for (;;) { + place *pl = tbl->lookup(label); + if (pl) + return pl; + if (!state) + return 0; + tbl = state->tbl; + state = state->prev; + } +} + +void define_label(const char *label, const place *pl) +{ + place *p = new place; + *p = *pl; + current_table->define(label, p); +} + +int lookup_variable(const char *name, double *val) +{ + place *pl = lookup_label(name); + if (pl) { + *val = pl->x; + return 1; + } + return 0; +} + +void define_variable(const char *name, double val) +{ + place *p = new place; + p->obj = 0; + p->x = val; + p->y = 0.0; + current_table->define(name, p); + if (strcmp(name, "scale") == 0) { + // When the scale changes, reset all scaled pre-defined variables to + // their default values. + for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++) + if (defaults_table[i].scaled) + define_variable(defaults_table[i].name, val*defaults_table[i].val); + } +} + +// called once only (not once per parse) + +void parse_init() +{ + current_direction = RIGHT_DIRECTION; + current_position.x = 0.0; + current_position.y = 0.0; + // This resets everything to its default value. + reset_all(); +} + +void reset(const char *nm) +{ + for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++) + if (strcmp(nm, defaults_table[i].name) == 0) { + double val = defaults_table[i].val; + if (defaults_table[i].scaled) { + double scale; + lookup_variable("scale", &scale); + val *= scale; + } + define_variable(defaults_table[i].name, val); + return; + } + lex_error("`%1' is not a predefined variable", nm); +} + +void reset_all() +{ + // We only have to explicitly reset the pre-defined variables that + // aren't scaled because `scale' is not scaled, and changing the + // value of `scale' will reset all the pre-defined variables that + // are scaled. + for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++) + if (!defaults_table[i].scaled) + define_variable(defaults_table[i].name, defaults_table[i].val); +} + +// called after each parse + +void parse_cleanup() +{ + while (current_saved_state != 0) { + delete current_table; + current_table = current_saved_state->tbl; + saved_state *tem = current_saved_state; + current_saved_state = current_saved_state->prev; + delete tem; + } + assert(current_table == &top_table); + PTABLE_ITERATOR(place) iter(current_table); + const char *key; + place *pl; + while (iter.next(&key, &pl)) + if (pl->obj != 0) { + position pos = pl->obj->origin(); + pl->obj = 0; + pl->x = pos.x; + pl->y = pos.y; + } + while (olist.head != 0) { + object *tem = olist.head; + olist.head = olist.head->next; + delete tem; + } + olist.tail = 0; + current_direction = RIGHT_DIRECTION; + current_position.x = 0.0; + current_position.y = 0.0; +} + +const char *ordinal_postfix(int n) +{ + if (n < 10 || n > 20) + switch (n % 10) { + case 1: + return "st"; + case 2: + return "nd"; + case 3: + return "rd"; + } + return "th"; +} + +const char *object_type_name(object_type type) +{ + switch (type) { + case BOX_OBJECT: + return "box"; + case CIRCLE_OBJECT: + return "circle"; + case ELLIPSE_OBJECT: + return "ellipse"; + case ARC_OBJECT: + return "arc"; + case SPLINE_OBJECT: + return "spline"; + case LINE_OBJECT: + return "line"; + case ARROW_OBJECT: + return "arrow"; + case MOVE_OBJECT: + return "move"; + case TEXT_OBJECT: + return "\"\""; + case BLOCK_OBJECT: + return "[]"; + case OTHER_OBJECT: + case MARK_OBJECT: + default: + break; + } + return "object"; +} + +static char sprintf_buf[1024]; + +char *format_number(const char *form, double n) +{ + if (form == 0) + form = "%g"; + else { + // this is a fairly feeble attempt at validation of the format + int nspecs = 0; + for (const char *p = form; *p != '\0'; p++) + if (*p == '%') { + if (p[1] == '%') + p++; + else + nspecs++; + } + if (nspecs > 1) { + lex_error("bad format `%1'", form); + return strsave(form); + } + } + sprintf(sprintf_buf, form, n); + return strsave(sprintf_buf); +} + +char *do_sprintf(const char *form, const double *v, int nv) +{ + string result; + int i = 0; + string one_format; + while (*form) { + if (*form == '%') { + one_format += *form++; + for (; *form != '\0' && strchr("#-+ 0123456789.", *form) != 0; form++) + one_format += *form; + if (*form == '\0' || strchr("eEfgG%", *form) == 0) { + lex_error("bad sprintf format"); + result += one_format; + result += form; + break; + } + if (*form == '%') { + one_format += *form++; + one_format += '\0'; + sprintf(sprintf_buf, one_format.contents()); + } + else { + if (i >= nv) { + lex_error("too few arguments to sprintf"); + result += one_format; + result += form; + break; + } + one_format += *form++; + one_format += '\0'; + sprintf(sprintf_buf, one_format.contents(), v[i++]); + } + one_format.clear(); + result += sprintf_buf; + } + else + result += *form++; + } + result += '\0'; + return strsave(result.contents()); +} +#line 3409 "y.tab.c" +#define YYABORT goto yyabort +#define YYREJECT goto yyabort +#define YYACCEPT goto yyaccept +#define YYERROR goto yyerrlab +int +#if defined(__STDC__) +yyparse(void) +#else +yyparse() +#endif +{ + register int yym, yyn, yystate; +#if YYDEBUG + register char *yys; + extern char *getenv(); + + if (yys = getenv("YYDEBUG")) + { + yyn = *yys; + if (yyn >= '0' && yyn <= '9') + yydebug = yyn - '0'; + } +#endif + + yynerrs = 0; + yyerrflag = 0; + yychar = (-1); + + yyssp = yyss; + yyvsp = yyvs; + *yyssp = yystate = 0; + +yyloop: + if ((yyn = yydefred[yystate]) != 0) goto yyreduce; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + } + if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, shifting to state %d\n", + YYPREFIX, yystate, yytable[yyn]); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + yychar = (-1); + if (yyerrflag > 0) --yyerrflag; + goto yyloop; + } + if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { + yyn = yytable[yyn]; + goto yyreduce; + } + if (yyerrflag) goto yyinrecovery; + yyerror("syntax error"); +#ifdef lint + goto yyerrlab; +#endif +yyerrlab: + ++yynerrs; +yyinrecovery: + if (yyerrflag < 3) + { + yyerrflag = 3; + for (;;) + { + if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, error recovery shifting\ + to state %d\n", YYPREFIX, *yyssp, yytable[yyn]); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + goto yyloop; + } + else + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: error recovery discarding state %d\n", + YYPREFIX, *yyssp); +#endif + if (yyssp <= yyss) goto yyabort; + --yyssp; + --yyvsp; + } + } + } + else + { + if (yychar == 0) goto yyabort; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, error recovery discards token %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + yychar = (-1); + goto yyloop; + } +yyreduce: +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, reducing by rule %d (%s)\n", + YYPREFIX, yystate, yyn, yyrule[yyn]); +#endif + yym = yylen[yyn]; + yyval = yyvsp[1-yym]; + switch (yyn) + { +case 2: +#line 283 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + if (olist.head) + print_picture(olist.head); + } +break; +case 3: +#line 292 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.pl = yyvsp[-1].pl; } +break; +case 4: +#line 297 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.pl = yyvsp[0].pl; } +break; +case 5: +#line 299 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.pl = yyvsp[-2].pl; } +break; +case 10: +#line 314 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + define_variable(yyvsp[-2].str, yyvsp[0].x); + a_delete yyvsp[-2].str; + } +break; +case 11: +#line 319 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + place *p = lookup_label(yyvsp[-3].str); + if (!p) { + lex_error("variable `%1' not defined", yyvsp[-3].str); + YYABORT; + } + p->obj = 0; + p->x = yyvsp[0].x; + p->y = 0.0; + a_delete yyvsp[-3].str; + } +break; +case 12: +#line 331 "/home/cjk/groff/src/preproc/pic/pic.y" +{ current_direction = UP_DIRECTION; } +break; +case 13: +#line 333 "/home/cjk/groff/src/preproc/pic/pic.y" +{ current_direction = DOWN_DIRECTION; } +break; +case 14: +#line 335 "/home/cjk/groff/src/preproc/pic/pic.y" +{ current_direction = LEFT_DIRECTION; } +break; +case 15: +#line 337 "/home/cjk/groff/src/preproc/pic/pic.y" +{ current_direction = RIGHT_DIRECTION; } +break; +case 16: +#line 339 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + olist.append(make_command_object(yyvsp[0].lstr.str, yyvsp[0].lstr.filename, + yyvsp[0].lstr.lineno)); + } +break; +case 17: +#line 344 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + olist.append(make_command_object(yyvsp[0].lstr.str, yyvsp[0].lstr.filename, + yyvsp[0].lstr.lineno)); + } +break; +case 18: +#line 349 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + fprintf(stderr, "%s\n", yyvsp[0].lstr.str); + a_delete yyvsp[0].lstr.str; + fflush(stderr); + } +break; +case 19: +#line 355 "/home/cjk/groff/src/preproc/pic/pic.y" +{ delim_flag = 1; } +break; +case 20: +#line 357 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + delim_flag = 0; + if (safer_flag) + lex_error("unsafe to run command `%1'", yyvsp[0].str); + else + system(yyvsp[0].str); + a_delete yyvsp[0].str; + } +break; +case 21: +#line 366 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + if (yychar < 0) + do_lookahead(); + do_copy(yyvsp[0].lstr.str); + /* do not delete the filename*/ + } +break; +case 22: +#line 373 "/home/cjk/groff/src/preproc/pic/pic.y" +{ delim_flag = 2; } +break; +case 23: +#line 375 "/home/cjk/groff/src/preproc/pic/pic.y" +{ delim_flag = 0; } +break; +case 24: +#line 377 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + if (yychar < 0) + do_lookahead(); + copy_file_thru(yyvsp[-5].lstr.str, yyvsp[-2].str, yyvsp[0].str); + /* do not delete the filename*/ + a_delete yyvsp[-2].str; + a_delete yyvsp[0].str; + } +break; +case 25: +#line 386 "/home/cjk/groff/src/preproc/pic/pic.y" +{ delim_flag = 2; } +break; +case 26: +#line 388 "/home/cjk/groff/src/preproc/pic/pic.y" +{ delim_flag = 0; } +break; +case 27: +#line 390 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + if (yychar < 0) + do_lookahead(); + copy_rest_thru(yyvsp[-2].str, yyvsp[0].str); + a_delete yyvsp[-2].str; + a_delete yyvsp[0].str; + } +break; +case 28: +#line 398 "/home/cjk/groff/src/preproc/pic/pic.y" +{ delim_flag = 1; } +break; +case 29: +#line 400 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + delim_flag = 0; + if (yychar < 0) + do_lookahead(); + do_for(yyvsp[-8].str, yyvsp[-6].x, yyvsp[-4].x, yyvsp[-3].by.is_multiplicative, yyvsp[-3].by.val, yyvsp[0].str); + } +break; +case 30: +#line 407 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + if (yychar < 0) + do_lookahead(); + if (yyvsp[0].if_data.x != 0.0) + push_body(yyvsp[0].if_data.body); + a_delete yyvsp[0].if_data.body; + } +break; +case 31: +#line 415 "/home/cjk/groff/src/preproc/pic/pic.y" +{ delim_flag = 1; } +break; +case 32: +#line 417 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + delim_flag = 0; + if (yychar < 0) + do_lookahead(); + if (yyvsp[-3].if_data.x != 0.0) + push_body(yyvsp[-3].if_data.body); + else + push_body(yyvsp[0].str); + a_delete yyvsp[-3].if_data.body; + a_delete yyvsp[0].str; + } +break; +case 34: +#line 430 "/home/cjk/groff/src/preproc/pic/pic.y" +{ define_variable("scale", 1.0); } +break; +case 35: +#line 435 "/home/cjk/groff/src/preproc/pic/pic.y" +{ reset(yyvsp[0].str); a_delete yyvsp[0].str; } +break; +case 36: +#line 437 "/home/cjk/groff/src/preproc/pic/pic.y" +{ reset(yyvsp[0].str); a_delete yyvsp[0].str; } +break; +case 37: +#line 439 "/home/cjk/groff/src/preproc/pic/pic.y" +{ reset(yyvsp[0].str); a_delete yyvsp[0].str; } +break; +case 38: +#line 444 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.lstr = yyvsp[0].lstr; } +break; +case 39: +#line 446 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.lstr.str = new char[strlen(yyvsp[-1].lstr.str) + strlen(yyvsp[0].lstr.str) + 1]; + strcpy(yyval.lstr.str, yyvsp[-1].lstr.str); + strcat(yyval.lstr.str, yyvsp[0].lstr.str); + a_delete yyvsp[-1].lstr.str; + a_delete yyvsp[0].lstr.str; + if (yyvsp[-1].lstr.filename) { + yyval.lstr.filename = yyvsp[-1].lstr.filename; + yyval.lstr.lineno = yyvsp[-1].lstr.lineno; + } + else if (yyvsp[0].lstr.filename) { + yyval.lstr.filename = yyvsp[0].lstr.filename; + yyval.lstr.lineno = yyvsp[0].lstr.lineno; + } + } +break; +case 40: +#line 465 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.lstr.str = new char[GDIGITS + 1]; + sprintf(yyval.lstr.str, "%g", yyvsp[0].x); + yyval.lstr.filename = 0; + yyval.lstr.lineno = 0; + } +break; +case 41: +#line 472 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.lstr = yyvsp[0].lstr; } +break; +case 42: +#line 474 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.lstr.str = new char[GDIGITS + 2 + GDIGITS + 1]; + sprintf(yyval.lstr.str, "%g, %g", yyvsp[0].pair.x, yyvsp[0].pair.y); + yyval.lstr.filename = 0; + yyval.lstr.lineno = 0; + } +break; +case 43: +#line 483 "/home/cjk/groff/src/preproc/pic/pic.y" +{ delim_flag = 1; } +break; +case 44: +#line 485 "/home/cjk/groff/src/preproc/pic/pic.y" +{ delim_flag = 0; yyval.if_data.x = yyvsp[-3].x; yyval.if_data.body = yyvsp[0].str; } +break; +case 45: +#line 490 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.str = 0; } +break; +case 46: +#line 492 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.str = yyvsp[0].lstr.str; } +break; +case 47: +#line 497 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = yyvsp[0].x; } +break; +case 48: +#line 499 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = yyvsp[0].x; } +break; +case 49: +#line 504 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.x = strcmp(yyvsp[-2].lstr.str, yyvsp[0].lstr.str) == 0; + a_delete yyvsp[-2].lstr.str; + a_delete yyvsp[0].lstr.str; + } +break; +case 50: +#line 510 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.x = strcmp(yyvsp[-2].lstr.str, yyvsp[0].lstr.str) != 0; + a_delete yyvsp[-2].lstr.str; + a_delete yyvsp[0].lstr.str; + } +break; +case 51: +#line 516 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = (yyvsp[-2].x != 0.0 && yyvsp[0].x != 0.0); } +break; +case 52: +#line 518 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = (yyvsp[-2].x != 0.0 && yyvsp[0].x != 0.0); } +break; +case 53: +#line 520 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = (yyvsp[-2].x != 0.0 && yyvsp[0].x != 0.0); } +break; +case 54: +#line 522 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = (yyvsp[-2].x != 0.0 || yyvsp[0].x != 0.0); } +break; +case 55: +#line 524 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = (yyvsp[-2].x != 0.0 || yyvsp[0].x != 0.0); } +break; +case 56: +#line 526 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = (yyvsp[-2].x != 0.0 || yyvsp[0].x != 0.0); } +break; +case 57: +#line 528 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = (yyvsp[0].x == 0.0); } +break; +case 58: +#line 534 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.by.val = 1.0; yyval.by.is_multiplicative = 0; } +break; +case 59: +#line 536 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.by.val = yyvsp[0].x; yyval.by.is_multiplicative = 0; } +break; +case 60: +#line 538 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.by.val = yyvsp[0].x; yyval.by.is_multiplicative = 1; } +break; +case 61: +#line 543 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pl.obj = yyvsp[0].spec->make_object(¤t_position, + ¤t_direction); + if (yyval.pl.obj == 0) + YYABORT; + delete yyvsp[0].spec; + if (yyval.pl.obj) + olist.append(yyval.pl.obj); + else { + yyval.pl.x = current_position.x; + yyval.pl.y = current_position.y; + } + } +break; +case 62: +#line 557 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.pl = yyvsp[0].pl; define_label(yyvsp[-3].str, & yyval.pl); a_delete yyvsp[-3].str; } +break; +case 63: +#line 559 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pl.obj = 0; + yyval.pl.x = yyvsp[0].pair.x; + yyval.pl.y = yyvsp[0].pair.y; + define_label(yyvsp[-3].str, & yyval.pl); + a_delete yyvsp[-3].str; + } +break; +case 64: +#line 567 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pl = yyvsp[0].pl; + define_label(yyvsp[-3].str, & yyval.pl); + a_delete yyvsp[-3].str; + } +break; +case 65: +#line 573 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.state.x = current_position.x; + yyval.state.y = current_position.y; + yyval.state.dir = current_direction; + } +break; +case 66: +#line 579 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + current_position.x = yyvsp[-2].state.x; + current_position.y = yyvsp[-2].state.y; + current_direction = yyvsp[-2].state.dir; + } +break; +case 67: +#line 585 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pl = yyvsp[-3].pl; + } +break; +case 68: +#line 589 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pl.obj = 0; + yyval.pl.x = current_position.x; + yyval.pl.y = current_position.y; + } +break; +case 69: +#line 598 "/home/cjk/groff/src/preproc/pic/pic.y" +{} +break; +case 70: +#line 600 "/home/cjk/groff/src/preproc/pic/pic.y" +{} +break; +case 71: +#line 605 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = new object_spec(BOX_OBJECT); + } +break; +case 72: +#line 609 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = new object_spec(CIRCLE_OBJECT); + } +break; +case 73: +#line 613 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = new object_spec(ELLIPSE_OBJECT); + } +break; +case 74: +#line 617 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = new object_spec(ARC_OBJECT); + yyval.spec->dir = current_direction; + } +break; +case 75: +#line 622 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = new object_spec(LINE_OBJECT); + lookup_variable("lineht", & yyval.spec->segment_height); + lookup_variable("linewid", & yyval.spec->segment_width); + yyval.spec->dir = current_direction; + } +break; +case 76: +#line 629 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = new object_spec(ARROW_OBJECT); + lookup_variable("lineht", & yyval.spec->segment_height); + lookup_variable("linewid", & yyval.spec->segment_width); + yyval.spec->dir = current_direction; + } +break; +case 77: +#line 636 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = new object_spec(MOVE_OBJECT); + lookup_variable("moveht", & yyval.spec->segment_height); + lookup_variable("movewid", & yyval.spec->segment_width); + yyval.spec->dir = current_direction; + } +break; +case 78: +#line 643 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = new object_spec(SPLINE_OBJECT); + lookup_variable("lineht", & yyval.spec->segment_height); + lookup_variable("linewid", & yyval.spec->segment_width); + yyval.spec->dir = current_direction; + } +break; +case 79: +#line 650 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = new object_spec(TEXT_OBJECT); + yyval.spec->text = new text_item(yyvsp[0].lstr.str, yyvsp[0].lstr.filename, yyvsp[0].lstr.lineno); + } +break; +case 80: +#line 655 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = new object_spec(TEXT_OBJECT); + yyval.spec->text = new text_item(format_number(0, yyvsp[0].x), 0, -1); + } +break; +case 81: +#line 660 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = new object_spec(TEXT_OBJECT); + yyval.spec->text = new text_item(format_number(yyvsp[0].lstr.str, yyvsp[-1].x), + yyvsp[0].lstr.filename, yyvsp[0].lstr.lineno); + a_delete yyvsp[0].lstr.str; + } +break; +case 82: +#line 667 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + saved_state *p = new saved_state; + yyval.pstate = p; + p->x = current_position.x; + p->y = current_position.y; + p->dir = current_direction; + p->tbl = current_table; + p->prev = current_saved_state; + current_position.x = 0.0; + current_position.y = 0.0; + current_table = new PTABLE(place); + current_saved_state = p; + olist.append(make_mark_object()); + } +break; +case 83: +#line 682 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + current_position.x = yyvsp[-2].pstate->x; + current_position.y = yyvsp[-2].pstate->y; + current_direction = yyvsp[-2].pstate->dir; + yyval.spec = new object_spec(BLOCK_OBJECT); + olist.wrap_up_block(& yyval.spec->oblist); + yyval.spec->tbl = current_table; + current_table = yyvsp[-2].pstate->tbl; + current_saved_state = yyvsp[-2].pstate->prev; + delete yyvsp[-2].pstate; + } +break; +case 84: +#line 694 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + yyval.spec->height = yyvsp[0].x; + yyval.spec->flags |= HAS_HEIGHT; + } +break; +case 85: +#line 700 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + yyval.spec->radius = yyvsp[0].x; + yyval.spec->flags |= HAS_RADIUS; + } +break; +case 86: +#line 706 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + yyval.spec->width = yyvsp[0].x; + yyval.spec->flags |= HAS_WIDTH; + } +break; +case 87: +#line 712 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + yyval.spec->radius = yyvsp[0].x/2.0; + yyval.spec->flags |= HAS_RADIUS; + } +break; +case 88: +#line 718 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + yyval.spec->flags |= HAS_SEGMENT; + switch (yyval.spec->dir) { + case UP_DIRECTION: + yyval.spec->segment_pos.y += yyvsp[0].x; + break; + case DOWN_DIRECTION: + yyval.spec->segment_pos.y -= yyvsp[0].x; + break; + case RIGHT_DIRECTION: + yyval.spec->segment_pos.x += yyvsp[0].x; + break; + case LEFT_DIRECTION: + yyval.spec->segment_pos.x -= yyvsp[0].x; + break; + } + } +break; +case 89: +#line 737 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + yyval.spec->dir = UP_DIRECTION; + yyval.spec->flags |= HAS_SEGMENT; + yyval.spec->segment_pos.y += yyval.spec->segment_height; + } +break; +case 90: +#line 744 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + yyval.spec->dir = UP_DIRECTION; + yyval.spec->flags |= HAS_SEGMENT; + yyval.spec->segment_pos.y += yyvsp[0].x; + } +break; +case 91: +#line 751 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + yyval.spec->dir = DOWN_DIRECTION; + yyval.spec->flags |= HAS_SEGMENT; + yyval.spec->segment_pos.y -= yyval.spec->segment_height; + } +break; +case 92: +#line 758 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + yyval.spec->dir = DOWN_DIRECTION; + yyval.spec->flags |= HAS_SEGMENT; + yyval.spec->segment_pos.y -= yyvsp[0].x; + } +break; +case 93: +#line 765 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + yyval.spec->dir = RIGHT_DIRECTION; + yyval.spec->flags |= HAS_SEGMENT; + yyval.spec->segment_pos.x += yyval.spec->segment_width; + } +break; +case 94: +#line 772 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + yyval.spec->dir = RIGHT_DIRECTION; + yyval.spec->flags |= HAS_SEGMENT; + yyval.spec->segment_pos.x += yyvsp[0].x; + } +break; +case 95: +#line 779 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + yyval.spec->dir = LEFT_DIRECTION; + yyval.spec->flags |= HAS_SEGMENT; + yyval.spec->segment_pos.x -= yyval.spec->segment_width; + } +break; +case 96: +#line 786 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + yyval.spec->dir = LEFT_DIRECTION; + yyval.spec->flags |= HAS_SEGMENT; + yyval.spec->segment_pos.x -= yyvsp[0].x; + } +break; +case 97: +#line 793 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + yyval.spec->flags |= HAS_FROM; + yyval.spec->from.x = yyvsp[0].pair.x; + yyval.spec->from.y = yyvsp[0].pair.y; + } +break; +case 98: +#line 800 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + if (yyval.spec->flags & HAS_SEGMENT) + yyval.spec->segment_list = new segment(yyval.spec->segment_pos, + yyval.spec->segment_is_absolute, + yyval.spec->segment_list); + yyval.spec->flags |= HAS_SEGMENT; + yyval.spec->segment_pos.x = yyvsp[0].pair.x; + yyval.spec->segment_pos.y = yyvsp[0].pair.y; + yyval.spec->segment_is_absolute = 1; + yyval.spec->flags |= HAS_TO; + yyval.spec->to.x = yyvsp[0].pair.x; + yyval.spec->to.y = yyvsp[0].pair.y; + } +break; +case 99: +#line 815 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + yyval.spec->flags |= HAS_AT; + yyval.spec->at.x = yyvsp[0].pair.x; + yyval.spec->at.y = yyvsp[0].pair.y; + if (yyval.spec->type != ARC_OBJECT) { + yyval.spec->flags |= HAS_FROM; + yyval.spec->from.x = yyvsp[0].pair.x; + yyval.spec->from.y = yyvsp[0].pair.y; + } + } +break; +case 100: +#line 827 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + yyval.spec->flags |= HAS_WITH; + yyval.spec->with = yyvsp[0].pth; + } +break; +case 101: +#line 833 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + yyval.spec->flags |= HAS_SEGMENT; + yyval.spec->segment_pos.x += yyvsp[0].pair.x; + yyval.spec->segment_pos.y += yyvsp[0].pair.y; + } +break; +case 102: +#line 840 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + if (yyval.spec->flags & HAS_SEGMENT) { + yyval.spec->segment_list = new segment(yyval.spec->segment_pos, + yyval.spec->segment_is_absolute, + yyval.spec->segment_list); + yyval.spec->flags &= ~HAS_SEGMENT; + yyval.spec->segment_pos.x = yyval.spec->segment_pos.y = 0.0; + yyval.spec->segment_is_absolute = 0; + } + } +break; +case 103: +#line 852 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; /* nothing*/ + } +break; +case 104: +#line 856 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + yyval.spec->flags |= IS_DOTTED; + lookup_variable("dashwid", & yyval.spec->dash_width); + } +break; +case 105: +#line 862 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + yyval.spec->flags |= IS_DOTTED; + yyval.spec->dash_width = yyvsp[0].x; + } +break; +case 106: +#line 868 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + yyval.spec->flags |= IS_DASHED; + lookup_variable("dashwid", & yyval.spec->dash_width); + } +break; +case 107: +#line 874 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + yyval.spec->flags |= IS_DASHED; + yyval.spec->dash_width = yyvsp[0].x; + } +break; +case 108: +#line 880 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + yyval.spec->flags |= IS_DEFAULT_FILLED; + } +break; +case 109: +#line 885 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + yyval.spec->flags |= IS_FILLED; + yyval.spec->fill = yyvsp[0].x; + } +break; +case 110: +#line 891 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + /* line chop chop means line chop 0 chop 0*/ + if (yyval.spec->flags & IS_DEFAULT_CHOPPED) { + yyval.spec->flags |= IS_CHOPPED; + yyval.spec->flags &= ~IS_DEFAULT_CHOPPED; + yyval.spec->start_chop = yyval.spec->end_chop = 0.0; + } + else if (yyval.spec->flags & IS_CHOPPED) { + yyval.spec->end_chop = 0.0; + } + else { + yyval.spec->flags |= IS_DEFAULT_CHOPPED; + } + } +break; +case 111: +#line 907 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + if (yyval.spec->flags & IS_DEFAULT_CHOPPED) { + yyval.spec->flags |= IS_CHOPPED; + yyval.spec->flags &= ~IS_DEFAULT_CHOPPED; + yyval.spec->start_chop = 0.0; + yyval.spec->end_chop = yyvsp[0].x; + } + else if (yyval.spec->flags & IS_CHOPPED) { + yyval.spec->end_chop = yyvsp[0].x; + } + else { + yyval.spec->start_chop = yyval.spec->end_chop = yyvsp[0].x; + yyval.spec->flags |= IS_CHOPPED; + } + } +break; +case 112: +#line 924 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + yyval.spec->flags |= IS_SAME; + } +break; +case 113: +#line 929 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + yyval.spec->flags |= IS_INVISIBLE; + } +break; +case 114: +#line 934 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + yyval.spec->flags |= HAS_LEFT_ARROW_HEAD; + } +break; +case 115: +#line 939 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + yyval.spec->flags |= HAS_RIGHT_ARROW_HEAD; + } +break; +case 116: +#line 944 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + yyval.spec->flags |= (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD); + } +break; +case 117: +#line 949 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + yyval.spec->flags |= IS_CLOCKWISE; + } +break; +case 118: +#line 954 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + yyval.spec->flags &= ~IS_CLOCKWISE; + } +break; +case 119: +#line 959 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + text_item **p; + for (p = & yyval.spec->text; *p; p = &(*p)->next) + ; + *p = new text_item(yyvsp[0].lstr.str, yyvsp[0].lstr.filename, yyvsp[0].lstr.lineno); + } +break; +case 120: +#line 967 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + if (yyval.spec->text) { + text_item *p; + for (p = yyval.spec->text; p->next; p = p->next) + ; + p->adj.h = LEFT_ADJUST; + } + } +break; +case 121: +#line 977 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + if (yyval.spec->text) { + text_item *p; + for (p = yyval.spec->text; p->next; p = p->next) + ; + p->adj.h = RIGHT_ADJUST; + } + } +break; +case 122: +#line 987 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + if (yyval.spec->text) { + text_item *p; + for (p = yyval.spec->text; p->next; p = p->next) + ; + p->adj.v = ABOVE_ADJUST; + } + } +break; +case 123: +#line 997 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + if (yyval.spec->text) { + text_item *p; + for (p = yyval.spec->text; p->next; p = p->next) + ; + p->adj.v = BELOW_ADJUST; + } + } +break; +case 124: +#line 1007 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-2].spec; + yyval.spec->flags |= HAS_THICKNESS; + yyval.spec->thickness = yyvsp[0].x; + } +break; +case 125: +#line 1013 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.spec = yyvsp[-1].spec; + yyval.spec->flags |= IS_ALIGNED; + } +break; +case 126: +#line 1021 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.lstr = yyvsp[0].lstr; + } +break; +case 127: +#line 1025 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.lstr.filename = yyvsp[-2].lstr.filename; + yyval.lstr.lineno = yyvsp[-2].lstr.lineno; + yyval.lstr.str = do_sprintf(yyvsp[-2].lstr.str, yyvsp[-1].dv.v, yyvsp[-1].dv.nv); + a_delete yyvsp[-1].dv.v; + a_delete yyvsp[-2].lstr.str; + } +break; +case 128: +#line 1036 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.dv.v = 0; + yyval.dv.nv = 0; + yyval.dv.maxv = 0; + } +break; +case 129: +#line 1042 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.dv = yyvsp[-2].dv; + if (yyval.dv.nv >= yyval.dv.maxv) { + if (yyval.dv.nv == 0) { + yyval.dv.v = new double[4]; + yyval.dv.maxv = 4; + } + else { + double *oldv = yyval.dv.v; + yyval.dv.maxv *= 2; + yyval.dv.v = new double[yyval.dv.maxv]; + memcpy(yyval.dv.v, oldv, yyval.dv.nv*sizeof(double)); + a_delete oldv; + } + } + yyval.dv.v[yyval.dv.nv] = yyvsp[0].x; + yyval.dv.nv += 1; + } +break; +case 130: +#line 1064 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.pair = yyvsp[0].pair; } +break; +case 131: +#line 1066 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + position pos = yyvsp[0].pl; + yyval.pair.x = pos.x; + yyval.pair.y = pos.y; + } +break; +case 132: +#line 1075 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.pair = yyvsp[0].pair; } +break; +case 133: +#line 1077 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pair.x = yyvsp[-2].pair.x + yyvsp[0].pair.x; + yyval.pair.y = yyvsp[-2].pair.y + yyvsp[0].pair.y; + } +break; +case 134: +#line 1082 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pair.x = yyvsp[-2].pair.x - yyvsp[0].pair.x; + yyval.pair.y = yyvsp[-2].pair.y - yyvsp[0].pair.y; + } +break; +case 135: +#line 1087 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pair.x = yyvsp[-3].pair.x; + yyval.pair.y = yyvsp[-1].pair.y; + } +break; +case 136: +#line 1092 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pair.x = (1.0 - yyvsp[-4].x)*yyvsp[-2].pair.x + yyvsp[-4].x*yyvsp[0].pair.x; + yyval.pair.y = (1.0 - yyvsp[-4].x)*yyvsp[-2].pair.y + yyvsp[-4].x*yyvsp[0].pair.y; + } +break; +case 137: +#line 1097 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pair.x = (1.0 - yyvsp[-5].x)*yyvsp[-3].pair.x + yyvsp[-5].x*yyvsp[-1].pair.x; + yyval.pair.y = (1.0 - yyvsp[-5].x)*yyvsp[-3].pair.y + yyvsp[-5].x*yyvsp[-1].pair.y; + } +break; +case 140: +#line 1110 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.pair.x = yyvsp[-2].x; yyval.pair.y = yyvsp[0].x; } +break; +case 141: +#line 1112 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.pair = yyvsp[-1].pair; } +break; +case 142: +#line 1117 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.pl = yyvsp[0].pl; } +break; +case 143: +#line 1119 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + path pth(yyvsp[0].crn); + if (!pth.follow(yyvsp[-1].pl, & yyval.pl)) + YYABORT; + } +break; +case 144: +#line 1125 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + path pth(yyvsp[-1].crn); + if (!pth.follow(yyvsp[0].pl, & yyval.pl)) + YYABORT; + } +break; +case 145: +#line 1131 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + path pth(yyvsp[-2].crn); + if (!pth.follow(yyvsp[0].pl, & yyval.pl)) + YYABORT; + } +break; +case 146: +#line 1137 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pl.x = current_position.x; + yyval.pl.y = current_position.y; + yyval.pl.obj = 0; + } +break; +case 147: +#line 1146 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + place *p = lookup_label(yyvsp[0].str); + if (!p) { + lex_error("there is no place `%1'", yyvsp[0].str); + YYABORT; + } + yyval.pl = *p; + a_delete yyvsp[0].str; + } +break; +case 148: +#line 1156 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pl.obj = yyvsp[0].obj; + } +break; +case 149: +#line 1160 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + path pth(yyvsp[0].str); + if (!pth.follow(yyvsp[-2].pl, & yyval.pl)) + YYABORT; + } +break; +case 150: +#line 1169 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.n = yyvsp[0].n; } +break; +case 151: +#line 1171 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + /* XXX Check for overflow (and non-integers?).*/ + yyval.n = (int)yyvsp[-1].x; + } +break; +case 152: +#line 1179 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.n = 1; } +break; +case 153: +#line 1181 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.n = yyvsp[-1].n; } +break; +case 154: +#line 1186 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + int count = 0; + object *p; + for (p = olist.head; p != 0; p = p->next) + if (p->type() == yyvsp[0].obtype && ++count == yyvsp[-1].n) { + yyval.obj = p; + break; + } + if (p == 0) { + lex_error("there is no %1%2 %3", yyvsp[-1].n, ordinal_postfix(yyvsp[-1].n), + object_type_name(yyvsp[0].obtype)); + YYABORT; + } + } +break; +case 155: +#line 1201 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + int count = 0; + object *p; + for (p = olist.tail; p != 0; p = p->prev) + if (p->type() == yyvsp[0].obtype && ++count == yyvsp[-1].n) { + yyval.obj = p; + break; + } + if (p == 0) { + lex_error("there is no %1%2 last %3", yyvsp[-1].n, + ordinal_postfix(yyvsp[-1].n), object_type_name(yyvsp[0].obtype)); + YYABORT; + } + } +break; +case 156: +#line 1219 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.obtype = BOX_OBJECT; } +break; +case 157: +#line 1221 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.obtype = CIRCLE_OBJECT; } +break; +case 158: +#line 1223 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.obtype = ELLIPSE_OBJECT; } +break; +case 159: +#line 1225 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.obtype = ARC_OBJECT; } +break; +case 160: +#line 1227 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.obtype = LINE_OBJECT; } +break; +case 161: +#line 1229 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.obtype = ARROW_OBJECT; } +break; +case 162: +#line 1231 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.obtype = SPLINE_OBJECT; } +break; +case 163: +#line 1233 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.obtype = BLOCK_OBJECT; } +break; +case 164: +#line 1235 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.obtype = TEXT_OBJECT; } +break; +case 165: +#line 1240 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pth = new path(yyvsp[0].str); + } +break; +case 166: +#line 1244 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pth = yyvsp[-2].pth; + yyval.pth->append(yyvsp[0].str); + } +break; +case 167: +#line 1252 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pth = new path(yyvsp[0].crn); + } +break; +case 168: +#line 1259 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pth = yyvsp[0].pth; + } +break; +case 169: +#line 1263 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pth = yyvsp[-1].pth; + yyval.pth->append(yyvsp[0].crn); + } +break; +case 170: +#line 1271 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pth = yyvsp[0].pth; + } +break; +case 171: +#line 1275 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + yyval.pth = yyvsp[-3].pth; + yyval.pth->set_ypath(yyvsp[-1].pth); + } +break; +case 172: +#line 1281 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + lex_warning("`%1%2 last %3' in `with' argument ignored", + yyvsp[-3].n, ordinal_postfix(yyvsp[-3].n), object_type_name(yyvsp[-1].obtype)); + yyval.pth = yyvsp[0].pth; + } +break; +case 173: +#line 1287 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + lex_warning("`last %1' in `with' argument ignored", + object_type_name(yyvsp[-1].obtype)); + yyval.pth = yyvsp[0].pth; + } +break; +case 174: +#line 1293 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + lex_warning("`%1%2 %3' in `with' argument ignored", + yyvsp[-2].n, ordinal_postfix(yyvsp[-2].n), object_type_name(yyvsp[-1].obtype)); + yyval.pth = yyvsp[0].pth; + } +break; +case 175: +#line 1299 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + lex_warning("initial `%1' in `with' argument ignored", yyvsp[-1].str); + a_delete yyvsp[-1].str; + yyval.pth = yyvsp[0].pth; + } +break; +case 176: +#line 1308 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::north; } +break; +case 177: +#line 1310 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::east; } +break; +case 178: +#line 1312 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::west; } +break; +case 179: +#line 1314 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::south; } +break; +case 180: +#line 1316 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::north_east; } +break; +case 181: +#line 1318 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object:: south_east; } +break; +case 182: +#line 1320 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::north_west; } +break; +case 183: +#line 1322 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::south_west; } +break; +case 184: +#line 1324 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::center; } +break; +case 185: +#line 1326 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::start; } +break; +case 186: +#line 1328 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::end; } +break; +case 187: +#line 1330 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::north; } +break; +case 188: +#line 1332 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::south; } +break; +case 189: +#line 1334 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::west; } +break; +case 190: +#line 1336 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::east; } +break; +case 191: +#line 1338 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::north_west; } +break; +case 192: +#line 1340 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::south_west; } +break; +case 193: +#line 1342 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::north_east; } +break; +case 194: +#line 1344 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::south_east; } +break; +case 195: +#line 1346 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::west; } +break; +case 196: +#line 1348 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::east; } +break; +case 197: +#line 1350 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::north_west; } +break; +case 198: +#line 1352 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::south_west; } +break; +case 199: +#line 1354 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::north_east; } +break; +case 200: +#line 1356 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::south_east; } +break; +case 201: +#line 1358 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::center; } +break; +case 202: +#line 1360 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::start; } +break; +case 203: +#line 1362 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.crn = &object::end; } +break; +case 204: +#line 1367 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + if (!lookup_variable(yyvsp[0].str, & yyval.x)) { + lex_error("there is no variable `%1'", yyvsp[0].str); + YYABORT; + } + a_delete yyvsp[0].str; + } +break; +case 205: +#line 1375 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = yyvsp[0].x; } +break; +case 206: +#line 1377 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + if (yyvsp[-1].pl.obj != 0) + yyval.x = yyvsp[-1].pl.obj->origin().x; + else + yyval.x = yyvsp[-1].pl.x; + } +break; +case 207: +#line 1384 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + if (yyvsp[-1].pl.obj != 0) + yyval.x = yyvsp[-1].pl.obj->origin().y; + else + yyval.x = yyvsp[-1].pl.y; + } +break; +case 208: +#line 1391 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + if (yyvsp[-1].pl.obj != 0) + yyval.x = yyvsp[-1].pl.obj->height(); + else + yyval.x = 0.0; + } +break; +case 209: +#line 1398 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + if (yyvsp[-1].pl.obj != 0) + yyval.x = yyvsp[-1].pl.obj->width(); + else + yyval.x = 0.0; + } +break; +case 210: +#line 1405 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + if (yyvsp[-1].pl.obj != 0) + yyval.x = yyvsp[-1].pl.obj->radius(); + else + yyval.x = 0.0; + } +break; +case 211: +#line 1412 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = yyvsp[-2].x + yyvsp[0].x; } +break; +case 212: +#line 1414 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = yyvsp[-2].x - yyvsp[0].x; } +break; +case 213: +#line 1416 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = yyvsp[-2].x * yyvsp[0].x; } +break; +case 214: +#line 1418 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + if (yyvsp[0].x == 0.0) { + lex_error("division by zero"); + YYABORT; + } + yyval.x = yyvsp[-2].x/yyvsp[0].x; + } +break; +case 215: +#line 1426 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + if (yyvsp[0].x == 0.0) { + lex_error("modulus by zero"); + YYABORT; + } + yyval.x = fmod(yyvsp[-2].x, yyvsp[0].x); + } +break; +case 216: +#line 1434 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + errno = 0; + yyval.x = pow(yyvsp[-2].x, yyvsp[0].x); + if (errno == EDOM) { + lex_error("arguments to `^' operator out of domain"); + YYABORT; + } + if (errno == ERANGE) { + lex_error("result of `^' operator out of range"); + YYABORT; + } + } +break; +case 217: +#line 1447 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = -yyvsp[0].x; } +break; +case 218: +#line 1449 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = yyvsp[-1].x; } +break; +case 219: +#line 1451 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + errno = 0; + yyval.x = sin(yyvsp[-1].x); + if (errno == ERANGE) { + lex_error("sin result out of range"); + YYABORT; + } + } +break; +case 220: +#line 1460 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + errno = 0; + yyval.x = cos(yyvsp[-1].x); + if (errno == ERANGE) { + lex_error("cos result out of range"); + YYABORT; + } + } +break; +case 221: +#line 1469 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + errno = 0; + yyval.x = atan2(yyvsp[-3].x, yyvsp[-1].x); + if (errno == EDOM) { + lex_error("atan2 argument out of domain"); + YYABORT; + } + if (errno == ERANGE) { + lex_error("atan2 result out of range"); + YYABORT; + } + } +break; +case 222: +#line 1482 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + errno = 0; + yyval.x = log10(yyvsp[-1].x); + if (errno == ERANGE) { + lex_error("log result out of range"); + YYABORT; + } + } +break; +case 223: +#line 1491 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + errno = 0; + yyval.x = pow(10.0, yyvsp[-1].x); + if (errno == ERANGE) { + lex_error("exp result out of range"); + YYABORT; + } + } +break; +case 224: +#line 1500 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + errno = 0; + yyval.x = sqrt(yyvsp[-1].x); + if (errno == EDOM) { + lex_error("sqrt argument out of domain"); + YYABORT; + } + } +break; +case 225: +#line 1509 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = yyvsp[-3].x > yyvsp[-1].x ? yyvsp[-3].x : yyvsp[-1].x; } +break; +case 226: +#line 1511 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = yyvsp[-3].x < yyvsp[-1].x ? yyvsp[-3].x : yyvsp[-1].x; } +break; +case 227: +#line 1513 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = floor(yyvsp[-1].x); } +break; +case 228: +#line 1515 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = 1.0 + floor(((rand()&0x7fff)/double(0x7fff))*yyvsp[-1].x); } +break; +case 229: +#line 1517 "/home/cjk/groff/src/preproc/pic/pic.y" +{ + /* return a random number in the range [0,1) */ + /* portable, but not very random */ + yyval.x = (rand() & 0x7fff) / double(0x8000); + } +break; +case 230: +#line 1523 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = 0; srand((unsigned int)yyvsp[-1].x); } +break; +case 231: +#line 1525 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = (yyvsp[-2].x < yyvsp[0].x); } +break; +case 232: +#line 1527 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = (yyvsp[-2].x <= yyvsp[0].x); } +break; +case 233: +#line 1529 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = (yyvsp[-2].x > yyvsp[0].x); } +break; +case 234: +#line 1531 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = (yyvsp[-2].x >= yyvsp[0].x); } +break; +case 235: +#line 1533 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = (yyvsp[-2].x == yyvsp[0].x); } +break; +case 236: +#line 1535 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = (yyvsp[-2].x != yyvsp[0].x); } +break; +case 237: +#line 1537 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = (yyvsp[-2].x != 0.0 && yyvsp[0].x != 0.0); } +break; +case 238: +#line 1539 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = (yyvsp[-2].x != 0.0 || yyvsp[0].x != 0.0); } +break; +case 239: +#line 1541 "/home/cjk/groff/src/preproc/pic/pic.y" +{ yyval.x = (yyvsp[0].x == 0.0); } +break; +#line 5161 "y.tab.c" + } + yyssp -= yym; + yystate = *yyssp; + yyvsp -= yym; + yym = yylhs[yyn]; + if (yystate == 0 && yym == 0) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state 0 to\ + state %d\n", YYPREFIX, YYFINAL); +#endif + yystate = YYFINAL; + *++yyssp = YYFINAL; + *++yyvsp = yyval; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, YYFINAL, yychar, yys); + } +#endif + } + if (yychar == 0) goto yyaccept; + goto yyloop; + } + if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yystate) + yystate = yytable[yyn]; + else + yystate = yydgoto[yym]; +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state %d \ +to state %d\n", YYPREFIX, *yyssp, yystate); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate; + *++yyvsp = yyval; + goto yyloop; +yyoverflow: + yyerror("yacc stack overflow"); +yyabort: + return (1); +yyaccept: + return (0); +} diff --git a/contrib/groff/src/preproc/pic/pic.h b/contrib/groff/src/preproc/pic/pic.h new file mode 100644 index 0000000..36c36d1 --- /dev/null +++ b/contrib/groff/src/preproc/pic/pic.h @@ -0,0 +1,104 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2000 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 <stdio.h> +#include <string.h> +#include <math.h> +#include <stdlib.h> +#include <errno.h> + +#ifdef NEED_DECLARATION_HYPOT +extern "C" { + double hypot(double, double); +} +#endif /* NEED_DECLARATION_HYPOT */ + +#include "assert.h" +#include "cset.h" +#include "lib.h" +#include "stringclass.h" +#include "errarg.h" +#include "error.h" +#include "position.h" +#include "text.h" +#include "output.h" + +#ifndef M_SQRT2 +#define M_SQRT2 1.41421356237309504880 +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +class input { + input *next; +public: + input(); + virtual ~input(); + virtual int get() = 0; + virtual int peek() = 0; + virtual int get_location(const char **, int *); + friend class input_stack; + friend class copy_rest_thru_input; +}; + +class file_input : public input { + FILE *fp; + const char *filename; + int lineno; + string line; + const char *ptr; + int read_line(); +public: + file_input(FILE *, const char *); + ~file_input(); + int get(); + int peek(); + int get_location(const char **, int *); +}; + +void lex_init(input *); +int get_location(char **, int *); + +void do_copy(const char *file); +void parse_init(); +void parse_cleanup(); + +void lex_error(const char *message, + const errarg &arg1 = empty_errarg, + const errarg &arg2 = empty_errarg, + const errarg &arg3 = empty_errarg); + +void lex_warning(const char *message, + const errarg &arg1 = empty_errarg, + const errarg &arg2 = empty_errarg, + const errarg &arg3 = empty_errarg); + +void lex_cleanup(); + +extern int flyback_flag; +extern int command_char; +// zero_length_line_flag is non-zero if zero-length lines are drawn +// as dots by the output device +extern int zero_length_line_flag; +extern int driver_extension_flag; +extern int compatible_flag; +extern int safer_flag; diff --git a/contrib/groff/src/preproc/pic/pic.man b/contrib/groff/src/preproc/pic/pic.man new file mode 100644 index 0000000..e187021 --- /dev/null +++ b/contrib/groff/src/preproc/pic/pic.man @@ -0,0 +1,883 @@ +.ig \"-*- nroff -*- +Copyright (C) 1989-2000 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the Free Software Foundation instead of in +the original English. +.. +.\" Like TP, but if specified indent is more than half +.\" the current line-length - indent, use the default indent. +.de Tp +.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP +.el .TP "\\$1" +.. +.ie t .ds tx T\h'-.1667m'\v'.224m'E\v'-.224m'\h'-.125m'X +.el .ds tx TeX +.ie \n(.g .ds ic \/ +.el .ds ic \^ +.\" The BSD man macros can't handle " in arguments to font change macros, +.\" so use \(ts instead of ". +.tr \(ts" +.TH @G@PIC @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" +.SH NAME +@g@pic \- compile pictures for troff or TeX +.SH SYNOPSIS +.B @g@pic +[ +.B \-nvCSU +] +[ +.I filename +\&.\|.\|. +] +.br +.B @g@pic +.B \-t +[ +.B \-cvzCSU +] +[ +.I filename +\&.\|.\|. +] +.SH DESCRIPTION +This manual page describes the GNU version of +.BR pic , +which is part of the groff document formatting system. +.B pic +compiles descriptions of pictures embedded within +.B troff +or \*(tx input files into commands that are understood by \*(tx or +.BR troff . +Each picture starts with a line beginning with +.B .PS +and ends with a line beginning with +.BR .PE . +Anything outside of +.B .PS +and +.B .PE +is passed through without change. +.LP +It is the user's responsibility to provide appropriate definitions of the +.B PS +and +.B PE +macros. +When the macro package being used does not supply such definitions +(for example, old versions of \-ms), +appropriate definitions can be obtained with +.BR \-mpic : +these will center each picture. +.SH OPTIONS +Options that do not take arguments may be grouped behind a single +.BR \- . +The special option +.B \-\^\- +can be used to mark the end of the options. +A filename of +.B \- +refers to the standard input. +.TP +.B \-C +Recognize +.B .PS +and +.B .PE +even when followed by a character other than space or newline. +.TP +.B \-S +Safer mode; do not execute +.B sh +commands. +This can be useful when operating on untrustworthy input. +(enabled by default) +.TP +.B \-U +Unsafe mode; revert the default option +.BR \-S . +.TP +.B \-n +Don't use the groff extensions to the troff drawing commands. +You should use this if you are using a postprocessor that doesn't support +these extensions. +The extensions are described in +.BR groff_out (@MAN5EXT@). +The +.B \-n +option also causes +.B pic +not to use zero-length lines to draw dots in troff mode. +.TP +.B \-t +\*(tx mode. +.TP +.B \-c +Be more compatible with +.BR tpic . +Implies +.BR \-t . +Lines beginning with +.B \e +are not passed through transparently. +Lines beginning with +.B . +are passed through with the initial +.B . +changed to +.BR \e . +A line beginning with +.B .ps +is given special treatment: +it takes an optional integer argument specifying +the line thickness (pen size) in milliinches; +a missing argument restores the previous line thickness; +the default line thickness is 8 milliinches. +The line thickness thus specified takes effect only +when a non-negative line thickness has not been +specified by use of the +.B thickness +attribute or by setting the +.B linethick +variable. +.TP +.B \-v +Print the version number. +.TP +.B \-z +In \*(tx mode draw dots using zero-length lines. +.LP +The following options supported by other versions of +.B pic +are ignored: +.TP +.B \-D +Draw all lines using the \eD escape sequence. +.B pic +always does this. +.TP +.BI \-T \ dev +Generate output for the +.B troff +device +.IR dev . +This is unnecessary because the +.B troff +output generated by +.B pic +is device-independent. +.SH USAGE +This section describes only the differences between GNU +.B pic +and the original version of +.BR pic . +Many of these differences also apply to newer versions of Unix +.BR pic . +.SS \*(tx mode +.LP +\*(tx mode is enabled by the +.B \-t +option. +In \*(tx mode, +.B pic +will define a vbox called +.B \egraph +for each picture. +You must yourself print that vbox using, for example, the command +.RS +.LP +.B +\ecenterline{\ebox\egraph} +.RE +.LP +Actually, since the vbox has a height of zero this will produce +slightly more vertical space above the picture than below it; +.RS +.LP +.B +\ecenterline{\eraise 1em\ebox\egraph} +.RE +.LP +would avoid this. +.LP +You must use a \*(tx driver that supports the +.B tpic +specials, version 2. +.LP +Lines beginning with +.B \e +are passed through transparently; a +.B % +is added to the end of the line to avoid unwanted spaces. +You can safely use this feature to change fonts or to +change the value of +.BR \ebaselineskip . +Anything else may well produce undesirable results; use at your own risk. +Lines beginning with a period are not given any special treatment. +.SS Commands +.TP +\fBfor\fR \fIvariable\fR \fB=\fR \fIexpr1\fR \fBto\fR \fIexpr2\fR \ +[\fBby\fR [\fB*\fR]\fIexpr3\fR] \fBdo\fR \fIX\fR \fIbody\fR \fIX\fR +Set +.I variable +to +.IR expr1 . +While the value of +.I variable +is less than or equal to +.IR expr2 , +do +.I body +and increment +.I variable +by +.IR expr3 ; +if +.B by +is not given, increment +.I variable +by 1. +If +.I expr3 +is prefixed by +.B * +then +.I variable +will instead be multiplied by +.IR expr3 . +.I X +can be any character not occurring in +.IR body . +.TP +\fBif\fR \fIexpr\fR \fBthen\fR \fIX\fR \fIif-true\fR \fIX\fR \ +[\fBelse\fR \fIY\fR \fIif-false\fR \fIY\fR] +Evaluate +.IR expr ; +if it is non-zero then do +.IR if-true , +otherwise do +.IR if-false . +.I X +can be any character not occurring in +.IR if-true . +.I Y +can be any character not occurring in +.IR if-false . +.TP +\fBprint\fR \fIarg\fR\|.\|.\|. +Concatenate the arguments and print as a line on stderr. +Each +.I arg +must be an expression, a position, or text. +This is useful for debugging. +.TP +\fBcommand\fR \fIarg\fR\|.\|.\|. +Concatenate the arguments +and pass them through as a line to troff or\*(tx. +Each +.I arg +must be an expression, a position, or text. +This has a similar effect to a line beginning with +.B . +or +.BR \e , +but allows the values of variables to be passed through. +.TP +\fBsh\fR \fIX\fR \fIcommand\fR \fIX\fR +Pass +.I command +to a shell. +.I X +can be any character not occurring in +.IR command . +.TP +\fBcopy\fR \fB"\fIfilename\fB"\fR +Include +.I filename +at this point in the file. +.TP +\fBcopy\fR [\fB"\fIfilename\fB"\fR] \fBthru\fR \fIX\fR \fIbody\fR \fIX\fR \ +[\fBuntil\fR \fB"\fIword\*(ic\fB"\fR] +.ns +.TP +\fBcopy\fR [\fB"\fIfilename\fB"\fR] \fBthru\fR \fImacro\fR \ +[\fBuntil\fR \fB"\fIword\*(ic\fB"\fR] +This construct does +.I body +once for each line of +.IR filename ; +the line is split into blank-delimited words, +and occurrences of +.BI $ i +in +.IR body , +for +.I i +between 1 and 9, +are replaced by the +.IR i -th +word of the line. +If +.I filename +is not given, lines are taken from the current input up to +.BR .PE . +If an +.B until +clause is specified, +lines will be read only until a line the first word of which is +.IR word ; +that line will then be discarded. +.I X +can be any character not occurring in +.IR body . +For example, +.RS +.IP +.ft B +.nf +\&.PS +copy thru % circle at ($1,$2) % until "END" +1 2 +3 4 +5 6 +END +box +\&.PE +.ft +.fi +.RE +.IP +is equivalent to +.RS +.IP +.ft B +.nf +\&.PS +circle at (1,2) +circle at (3,4) +circle at (5,6) +box +\&.PE +.ft +.fi +.RE +.IP +The commands to be performed for each line can also be taken +from a macro defined earlier by giving the name of the macro +as the argument to +.BR thru . +.LP +.B reset +.br +.ns +.TP +\fBreset\fI variable1\fB,\fI variable2 .\^.\^. +Reset pre-defined variables +.IR variable1 , +.I variable2 +\&.\^.\^. to their default values. +If no arguments are given, reset all pre-defined variables +to their default values. +Note that assigning a value to +.B scale +also causes all pre-defined variables that control dimensions +to be reset to their default values times the new value of scale. +.TP +\fBplot\fR \fIexpr\fR [\fB"\fItext\*(ic\fB"\fR] +This is a text object which is constructed by using +.I text +as a format string for sprintf +with an argument of +.IR expr . +If +.I text +is omitted a format string of +.B "\(ts%g\(ts" +is used. +Attributes can be specified in the same way as for a normal text +object. +Be very careful that you specify an appropriate format string; +.B pic +does only very limited checking of the string. +This is deprecated in favour of +.BR sprintf . +.TP +.IB variable := expr +This is similar to +.B = +except +.I variable +must already be defined, +and the value of +.I variable +will be changed only in the innermost block in which it is defined. +(By contrast, +.B = +defines the variable in the current block if it is not already defined there, +and then changes the value in the current block.) +.LP +Arguments of the form +.IP +.IR X\ anything\ X +.LP +are also allowed to be of the form +.IP +.BI {\ anything\ } +.LP +In this case +.I anything +can contain balanced occurrences of +.B { +and +.BR } . +Strings may contain +.I X +or imbalanced occurrences of +.B { +and +.BR } . +.SS Expressions +The syntax for expressions has been significantly extended: +.LP +.IB x\ ^\ y +(exponentiation) +.br +.BI sin( x ) +.br +.BI cos( x ) +.br +.BI atan2( y , \ x ) +.br +.BI log( x ) +(base 10) +.br +.BI exp( x ) +(base 10, ie 10\v'-.4m'\fIx\*(ic\fR\v'.4m') +.br +.BI sqrt( x ) +.br +.BI int( x ) +.br +.B rand() +(return a random number between 0 and 1) +.br +.BI rand( x ) +(return a random number between 1 and +.IR x ; +deprecated) +.br +.BI srand( x ) +(set the random number seed) +.br +.BI max( e1 , \ e2 ) +.br +.BI min( e1 , \ e2 ) +.br +.BI ! e +.br +\fIe1\fB && \fIe2\fR +.br +\fIe1\fB || \fIe2\fR +.br +\fIe1\fB == \fIe2\fR +.br +\fIe1\fB != \fIe2\fR +.br +\fIe1\fB >= \fIe2\fR +.br +\fIe1\fB > \fIe2\fR +.br +\fIe1\fB <= \fIe2\fR +.br +\fIe1\fB < \fIe2\fR +.br +\fB"\fIstr1\*(ic\fB" == "\fIstr2\*(ic\fB"\fR +.br +\fB"\fIstr1\*(ic\fB" != "\fIstr2\*(ic\fB"\fR +.br +.LP +String comparison expressions must be parenthesised in some contexts +to avoid ambiguity. +.SS Other Changes +.LP +A bare expression, +.IR expr , +is acceptable as an attribute; +it is equivalent to +.IR dir\ expr , +where +.I dir +is the current direction. +For example +.IP +.B line 2i +.LP +means draw a line 2 inches long in the current direction. +.LP +The maximum width and height of the picture are taken from the variables +.B maxpswid +and +.BR maxpsht . +Initially these have values 8.5 and 11. +.LP +Scientific notation is allowed for numbers. +For example +.RS +.B +x = 5e\-2 +.RE +.LP +Text attributes can be compounded. +For example, +.RS +.B +"foo" above ljust +.RE +is legal. +.LP +There is no limit to the depth to which blocks can be examined. +For example, +.RS +.B +[A: [B: [C: box ]]] with .A.B.C.sw at 1,2 +.br +.B +circle at last [\^].A.B.C +.RE +is acceptable. +.LP +Arcs now have compass points +determined by the circle of which the arc is a part. +.LP +Circles and arcs can be dotted or dashed. +In \*(tx mode splines can be dotted or dashed. +.LP +Boxes can have rounded corners. +The +.B rad +attribute specifies the radius of the quarter-circles at each corner. +If no +.B rad +or +.B diam +attribute is given, a radius of +.B boxrad +is used. +Initially, +.B boxrad +has a value of 0. +A box with rounded corners can be dotted or dashed. +.LP +The +.B .PS +line can have a second argument specifying a maximum height for +the picture. +If the width of zero is specified the width will be ignored in computing +the scaling factor for the picture. +Note that GNU +.B pic +will always scale a picture by the same amount vertically as horizontally. +This is different from the +.SM DWB +2.0 +.B pic +which may scale a picture by a different amount vertically than +horizontally if a height is specified. +.LP +Each text object has an invisible box associated with it. +The compass points of a text object are determined by this box. +The implicit motion associated with the object is also determined +by this box. +The dimensions of this box are taken from the width and height attributes; +if the width attribute is not supplied then the width will be taken to be +.BR textwid ; +if the height attribute is not supplied then the height will be taken to be +the number of text strings associated with the object +times +.BR textht . +Initially +.B textwid +and +.B textht +have a value of 0. +.LP +In places where a quoted text string can be used, +an expression of the form +.IP +.BI sprintf(\(ts format \(ts,\ arg ,\fR.\|.\|.\fB) +.LP +can also be used; +this will produce the arguments formatted according to +.IR format , +which should be a string as described in +.BR printf (3) +appropriate for the number of arguments supplied, +using only the +.BR e , +.BR f , +.B g +or +.B % +format characters. +.LP +The thickness of the lines used to draw objects is controlled by the +.B linethick +variable. +This gives the thickness of lines in points. +A negative value means use the default thickness: +in \*(tx output mode, this means use a thickness of 8 milliinches; +in \*(tx output mode with the +.B -c +option, this means use the line thickness specified by +.B .ps +lines; +in troff output mode, this means use a thickness proportional +to the pointsize. +A zero value means draw the thinnest possible line supported by +the output device. +Initially it has a value of -1. +There is also a +.BR thick [ ness ] +attribute. +For example, +.RS +.LP +.B circle thickness 1.5 +.RE +.LP +would draw a circle using a line with a thickness of 1.5 points. +The thickness of lines is not affected by the +value of the +.B scale +variable, nor by the width or height given in the +.B .PS +line. +.LP +Boxes (including boxes with rounded corners), +circles and ellipses can be filled by giving then an attribute of +.BR fill [ ed ]. +This takes an optional argument of an expression with a value between +0 and 1; 0 will fill it with white, 1 with black, values in between +with a proportionally gray shade. +A value greater than 1 can also be used: +this means fill with the +shade of gray that is currently being used for text and lines. +Normally this will be black, but output devices may provide +a mechanism for changing this. +Without an argument, then the value of the variable +.B fillval +will be used. +Initially this has a value of 0.5. +The invisible attribute does not affect the filling of objects. +Any text associated with a filled object will be added after the +object has been filled, so that the text will not be obscured +by the filling. +.LP +Arrow heads will be drawn as solid triangles if the variable +.B arrowhead +is non-zero and either \*(tx mode is enabled or +the +.B \-x +option has been given. +Initially +.B arrowhead +has a value of 1. +.LP +The troff output of +.B pic +is device-independent. +The +.B \-T +option is therefore redundant. +All numbers are taken to be in inches; numbers are never interpreted +to be in troff machine units. +.LP +Objects can have an +.B aligned +attribute. +This will only work when the postprocessor is +.BR grops . +Any text associated with an object having the +.B aligned +attribute will be rotated about the center of the object +so that it is aligned in the direction from the start point +to the end point of the object. +Note that this attribute will have no effect for objects whose start and +end points are coincident. +.LP +In places where +.IB n th +is allowed +.BI ` expr 'th +is also allowed. +Note that +.B 'th +is a single token: no space is allowed between the +.B ' +and the +.BR th . +For example, +.IP +.B +.nf +for i = 1 to 4 do { + line from `i'th box.nw to `i+1'th box.se +} +.fi +.SH CONVERSION +To obtain a stand-alone picture from a +.B pic +file, enclose your +.B pic +code with +.B .PS +and +.B .PE +requests; +.B roff +configuration commands may be added at the beginning of the file, but no +.B roff +text. +.LP +It is necessary to feed this file into +.B groff +without adding any page information, so you must check which +.B .PS +and +.B .PE +requests are actually called. +For example, the mm macro package adds a page number, which is very +annoying. +At the moment, calling standard +.B groff +without any macro package works. +Alternatively, you can define your own requests, e.g. to do nothing: +.LP +.RS +.nf +.ft B +\&.de PS +\&.. +\&.de PE +\&.. +.ft +.fi +.RE +.LP +.B groff +itself does not provide direct conversion into other graphics file +formats. +But there are lots of possibilities if you first transform your picture +into PostScript\*R format using the +.B groff +option +.BR -Tps . +Since this +.IR ps -file +lacks BoundingBox information it is not very useful by itself, but it +may be fed into other conversion programs, usually named +.BI ps2 other +or +.BI psto other +or the like. +Moreover, the PostScript interpreter +.B ghostscript +.RB ( gs ) +has built-in graphics conversion devices that are called with the option +.LP +.RS +.BI "gs -sDEVICE=" <devname> +.RE +.LP +Call +.RS +.B gs --help +.RE +.LP +for a list of the available devices. +.LP +As the Encapsulated PostScript File Format +.B EPS +is getting more and more important, and the conversion wasn't regarded +trivial in the past you might be interested to know that there is a +conversion tool named +.B ps2eps +which does the right job. +It is much better than the tool +.B ps2epsi +packaged with +.BR gs . +.LP +For bitmapped graphic formats, you should use +.BR pstopnm ; +the resulting (intermediate) +.B PNM +file can be then converted to virtually any graphics format using the tools +of the +.B netpbm +package . +.SH FILES +.Tp \w'\fB@MACRODIR@/pic.tmac'u+3n +.B +@MACRODIR@/pic.tmac +Example definitions of the +.B PS +and +.B PE +macros. +.SH "SEE ALSO" +.BR @g@troff (@MAN1EXT@), +.BR groff_out (@MAN5EXT@), +.BR tex (1), +.BR gs (1), +.BR ps2eps (1), +.BR pstopnm (1), +.BR ps2epsi (1), +.BR pnm (5) +.LP +Tpic: Pic for \*(tx +.LP +Brian W. Kernighan, +PIC \(em A Graphics Language for Typesetting (User Manual). +AT&T Bell Laboratories, Computing Science Technical Report No.\ 116 +<URL:http://cm.bell-labs.com/cm/cs/cstr/116.ps.gz> +(revised May, 1991). +.LP +.B ps2eps +is available from CTAN mirrors, e.g. +.br +<ftp://ftp.dante.de/tex-archive/support/ps2eps/> +.LP +W. Richard Stevens - Turning PIC Into HTML +.br +<http://www.kohala.com/start/troff/pic2html.html> +.LP +W. Richard Stevens - Examples of picMacros +.br +<http://www.kohala.com/start/troff/pic.examples.ps> +.SH BUGS +.LP +Input characters that are illegal for +.B groff +(ie those with +.SM ASCII +code 0 or between 013 and 037 octal or between 0200 and 0237 octal) +are rejected even in \*(tx mode. +.LP +The interpretation of +.B fillval +is incompatible with the pic in 10th edition Unix, +which interprets 0 as black and 1 as white. +.LP +PostScript\*R is a registered trademark of Adobe Systems Incorporation. diff --git a/contrib/groff/src/preproc/pic/pic.y b/contrib/groff/src/preproc/pic/pic.y new file mode 100644 index 0000000..38b960a --- /dev/null +++ b/contrib/groff/src/preproc/pic/pic.y @@ -0,0 +1,1812 @@ +/* Copyright (C) 1989, 1990, 1991, 1992, 2000 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 "pic.h" +#include "ptable.h" +#include "object.h" + +extern int delim_flag; +extern void do_copy(const char *); +extern void copy_rest_thru(const char *, const char *); +extern void copy_file_thru(const char *, const char *, const char *); +extern void push_body(const char *); +extern void do_for(char *var, double from, double to, + int by_is_multiplicative, double by, char *body); +extern void do_lookahead(); + +#ifndef HAVE_FMOD +extern "C" { + double fmod(double, double); +} +#endif + +#undef rand +#undef srand +extern "C" { + int rand(); +#ifdef RET_TYPE_SRAND_IS_VOID + void srand(unsigned int); +#else + int srand(unsigned int); +#endif +} + +/* Maximum number of characters produced by printf("%g") */ +#define GDIGITS 14 + +int yylex(); +void yyerror(const char *); + +void reset(const char *nm); +void reset_all(); + +place *lookup_label(const char *); +void define_label(const char *label, const place *pl); + +direction current_direction; +position current_position; + +implement_ptable(place) + +PTABLE(place) top_table; + +PTABLE(place) *current_table = &top_table; +saved_state *current_saved_state = 0; + +object_list olist; + +const char *ordinal_postfix(int n); +const char *object_type_name(object_type type); +char *format_number(const char *form, double n); +char *do_sprintf(const char *form, const double *v, int nv); + +%} + + +%union { + char *str; + int n; + double x; + struct { double x, y; } pair; + struct { double x; char *body; } if_data; + struct { char *str; const char *filename; int lineno; } lstr; + struct { double *v; int nv; int maxv; } dv; + struct { double val; int is_multiplicative; } by; + place pl; + object *obj; + corner crn; + path *pth; + object_spec *spec; + saved_state *pstate; + graphics_state state; + object_type obtype; +} + +%token <str> LABEL +%token <str> VARIABLE +%token <x> NUMBER +%token <lstr> TEXT +%token <lstr> COMMAND_LINE +%token <str> DELIMITED +%token <n> ORDINAL +%token TH +%token LEFT_ARROW_HEAD +%token RIGHT_ARROW_HEAD +%token DOUBLE_ARROW_HEAD +%token LAST +%token UP +%token DOWN +%token LEFT +%token RIGHT +%token BOX +%token CIRCLE +%token ELLIPSE +%token ARC +%token LINE +%token ARROW +%token MOVE +%token SPLINE +%token HEIGHT +%token RADIUS +%token WIDTH +%token DIAMETER +%token UP +%token DOWN +%token RIGHT +%token LEFT +%token FROM +%token TO +%token AT +%token WITH +%token BY +%token THEN +%token SOLID +%token DOTTED +%token DASHED +%token CHOP +%token SAME +%token INVISIBLE +%token LJUST +%token RJUST +%token ABOVE +%token BELOW +%token OF +%token THE +%token WAY +%token BETWEEN +%token AND +%token HERE +%token DOT_N +%token DOT_E +%token DOT_W +%token DOT_S +%token DOT_NE +%token DOT_SE +%token DOT_NW +%token DOT_SW +%token DOT_C +%token DOT_START +%token DOT_END +%token DOT_X +%token DOT_Y +%token DOT_HT +%token DOT_WID +%token DOT_RAD +%token SIN +%token COS +%token ATAN2 +%token LOG +%token EXP +%token SQRT +%token K_MAX +%token K_MIN +%token INT +%token RAND +%token SRAND +%token COPY +%token THRU +%token TOP +%token BOTTOM +%token UPPER +%token LOWER +%token SH +%token PRINT +%token CW +%token CCW +%token FOR +%token DO +%token IF +%token ELSE +%token ANDAND +%token OROR +%token NOTEQUAL +%token EQUALEQUAL +%token LESSEQUAL +%token GREATEREQUAL +%token LEFT_CORNER +%token RIGHT_CORNER +%token CENTER +%token END +%token START +%token RESET +%token UNTIL +%token PLOT +%token THICKNESS +%token FILL +%token ALIGNED +%token SPRINTF +%token COMMAND + +%token DEFINE +%token UNDEF + +/* this ensures that plot 17 "%g" parses as (plot 17 "%g") */ +%left PLOT +%left TEXT SPRINTF + +/* give text adjustments higher precedence than TEXT, so that +box "foo" above ljust == box ("foo" above ljust) +*/ + +%left LJUST RJUST ABOVE BELOW + +%left LEFT RIGHT +/* Give attributes that take an optional expression a higher +precedence than left and right, so that eg `line chop left' +parses properly. */ +%left CHOP SOLID DASHED DOTTED UP DOWN FILL +%left LABEL + +%left VARIABLE NUMBER '(' SIN COS ATAN2 LOG EXP SQRT K_MAX K_MIN INT RAND SRAND LAST +%left ORDINAL HERE '`' + +/* these need to be lower than '-' */ +%left HEIGHT RADIUS WIDTH DIAMETER FROM TO AT THICKNESS + +/* these must have higher precedence than CHOP so that `label %prec CHOP' +works */ +%left DOT_N DOT_E DOT_W DOT_S DOT_NE DOT_SE DOT_NW DOT_SW DOT_C +%left DOT_START DOT_END TOP BOTTOM LEFT_CORNER RIGHT_CORNER +%left UPPER LOWER CENTER START END + +%left ',' +%left OROR +%left ANDAND +%left EQUALEQUAL NOTEQUAL +%left '<' '>' LESSEQUAL GREATEREQUAL + +%left BETWEEN OF +%left AND + +%left '+' '-' +%left '*' '/' '%' +%right '!' +%right '^' + +%type <x> expr any_expr text_expr +%type <by> optional_by +%type <pair> expr_pair position_not_place +%type <if_data> simple_if +%type <obj> nth_primitive +%type <crn> corner +%type <pth> path label_path relative_path +%type <pl> place label element element_list middle_element_list +%type <spec> object_spec +%type <pair> position +%type <obtype> object_type +%type <n> optional_ordinal_last ordinal +%type <str> until +%type <dv> sprintf_args +%type <lstr> text print_args print_arg + +%% + +top: + optional_separator + | element_list + { + if (olist.head) + print_picture(olist.head); + } + ; + + +element_list: + optional_separator middle_element_list optional_separator + { $$ = $2; } + ; + +middle_element_list: + element + { $$ = $1; } + | middle_element_list separator element + { $$ = $1; } + ; + +optional_separator: + /* empty */ + | separator + ; + +separator: + ';' + | separator ';' + ; + +placeless_element: + VARIABLE '=' any_expr + { + define_variable($1, $3); + a_delete $1; + } + | VARIABLE ':' '=' any_expr + { + place *p = lookup_label($1); + if (!p) { + lex_error("variable `%1' not defined", $1); + YYABORT; + } + p->obj = 0; + p->x = $4; + p->y = 0.0; + a_delete $1; + } + | UP + { current_direction = UP_DIRECTION; } + | DOWN + { current_direction = DOWN_DIRECTION; } + | LEFT + { current_direction = LEFT_DIRECTION; } + | RIGHT + { current_direction = RIGHT_DIRECTION; } + | COMMAND_LINE + { + olist.append(make_command_object($1.str, $1.filename, + $1.lineno)); + } + | COMMAND print_args + { + olist.append(make_command_object($2.str, $2.filename, + $2.lineno)); + } + | PRINT print_args + { + fprintf(stderr, "%s\n", $2.str); + a_delete $2.str; + fflush(stderr); + } + | SH + { delim_flag = 1; } + DELIMITED + { + delim_flag = 0; + if (safer_flag) + lex_error("unsafe to run command `%1'", $3); + else + system($3); + a_delete $3; + } + | COPY TEXT + { + if (yychar < 0) + do_lookahead(); + do_copy($2.str); + // do not delete the filename + } + | COPY TEXT THRU + { delim_flag = 2; } + DELIMITED + { delim_flag = 0; } + until + { + if (yychar < 0) + do_lookahead(); + copy_file_thru($2.str, $5, $7); + // do not delete the filename + a_delete $5; + a_delete $7; + } + | COPY THRU + { delim_flag = 2; } + DELIMITED + { delim_flag = 0; } + until + { + if (yychar < 0) + do_lookahead(); + copy_rest_thru($4, $6); + a_delete $4; + a_delete $6; + } + | FOR VARIABLE '=' expr TO expr optional_by DO + { delim_flag = 1; } + DELIMITED + { + delim_flag = 0; + if (yychar < 0) + do_lookahead(); + do_for($2, $4, $6, $7.is_multiplicative, $7.val, $10); + } + | simple_if + { + if (yychar < 0) + do_lookahead(); + if ($1.x != 0.0) + push_body($1.body); + a_delete $1.body; + } + | simple_if ELSE + { delim_flag = 1; } + DELIMITED + { + delim_flag = 0; + if (yychar < 0) + do_lookahead(); + if ($1.x != 0.0) + push_body($1.body); + else + push_body($4); + a_delete $1.body; + a_delete $4; + } + | reset_variables + | RESET + { define_variable("scale", 1.0); } + ; + +reset_variables: + RESET VARIABLE + { reset($2); a_delete $2; } + | reset_variables VARIABLE + { reset($2); a_delete $2; } + | reset_variables ',' VARIABLE + { reset($3); a_delete $3; } + ; + +print_args: + print_arg + { $$ = $1; } + | print_args print_arg + { + $$.str = new char[strlen($1.str) + strlen($2.str) + 1]; + strcpy($$.str, $1.str); + strcat($$.str, $2.str); + a_delete $1.str; + a_delete $2.str; + if ($1.filename) { + $$.filename = $1.filename; + $$.lineno = $1.lineno; + } + else if ($2.filename) { + $$.filename = $2.filename; + $$.lineno = $2.lineno; + } + } + ; + +print_arg: + expr %prec ',' + { + $$.str = new char[GDIGITS + 1]; + sprintf($$.str, "%g", $1); + $$.filename = 0; + $$.lineno = 0; + } + | text + { $$ = $1; } + | position %prec ',' + { + $$.str = new char[GDIGITS + 2 + GDIGITS + 1]; + sprintf($$.str, "%g, %g", $1.x, $1.y); + $$.filename = 0; + $$.lineno = 0; + } + +simple_if: + IF any_expr THEN + { delim_flag = 1; } + DELIMITED + { delim_flag = 0; $$.x = $2; $$.body = $5; } + ; + +until: + /* empty */ + { $$ = 0; } + | UNTIL TEXT + { $$ = $2.str; } + ; + +any_expr: + expr + { $$ = $1; } + | text_expr + { $$ = $1; } + ; + +text_expr: + text EQUALEQUAL text + { + $$ = strcmp($1.str, $3.str) == 0; + a_delete $1.str; + a_delete $3.str; + } + | text NOTEQUAL text + { + $$ = strcmp($1.str, $3.str) != 0; + a_delete $1.str; + a_delete $3.str; + } + | text_expr ANDAND text_expr + { $$ = ($1 != 0.0 && $3 != 0.0); } + | text_expr ANDAND expr + { $$ = ($1 != 0.0 && $3 != 0.0); } + | expr ANDAND text_expr + { $$ = ($1 != 0.0 && $3 != 0.0); } + | text_expr OROR text_expr + { $$ = ($1 != 0.0 || $3 != 0.0); } + | text_expr OROR expr + { $$ = ($1 != 0.0 || $3 != 0.0); } + | expr OROR text_expr + { $$ = ($1 != 0.0 || $3 != 0.0); } + | '!' text_expr + { $$ = ($2 == 0.0); } + ; + + +optional_by: + /* empty */ + { $$.val = 1.0; $$.is_multiplicative = 0; } + | BY expr + { $$.val = $2; $$.is_multiplicative = 0; } + | BY '*' expr + { $$.val = $3; $$.is_multiplicative = 1; } + ; + +element: + object_spec + { + $$.obj = $1->make_object(¤t_position, + ¤t_direction); + if ($$.obj == 0) + YYABORT; + delete $1; + if ($$.obj) + olist.append($$.obj); + else { + $$.x = current_position.x; + $$.y = current_position.y; + } + } + | LABEL ':' optional_separator element + { $$ = $4; define_label($1, & $$); a_delete $1; } + | LABEL ':' optional_separator position_not_place + { + $$.obj = 0; + $$.x = $4.x; + $$.y = $4.y; + define_label($1, & $$); + a_delete $1; + } + | LABEL ':' optional_separator place + { + $$ = $4; + define_label($1, & $$); + a_delete $1; + } + | '{' + { + $<state>$.x = current_position.x; + $<state>$.y = current_position.y; + $<state>$.dir = current_direction; + } + element_list '}' + { + current_position.x = $<state>2.x; + current_position.y = $<state>2.y; + current_direction = $<state>2.dir; + } + optional_element + { + $$ = $3; + } + | placeless_element + { + $$.obj = 0; + $$.x = current_position.x; + $$.y = current_position.y; + } + ; + +optional_element: + /* empty */ + {} + | element + {} + ; + +object_spec: + BOX + { + $$ = new object_spec(BOX_OBJECT); + } + | CIRCLE + { + $$ = new object_spec(CIRCLE_OBJECT); + } + | ELLIPSE + { + $$ = new object_spec(ELLIPSE_OBJECT); + } + | ARC + { + $$ = new object_spec(ARC_OBJECT); + $$->dir = current_direction; + } + | LINE + { + $$ = new object_spec(LINE_OBJECT); + lookup_variable("lineht", & $$->segment_height); + lookup_variable("linewid", & $$->segment_width); + $$->dir = current_direction; + } + | ARROW + { + $$ = new object_spec(ARROW_OBJECT); + lookup_variable("lineht", & $$->segment_height); + lookup_variable("linewid", & $$->segment_width); + $$->dir = current_direction; + } + | MOVE + { + $$ = new object_spec(MOVE_OBJECT); + lookup_variable("moveht", & $$->segment_height); + lookup_variable("movewid", & $$->segment_width); + $$->dir = current_direction; + } + | SPLINE + { + $$ = new object_spec(SPLINE_OBJECT); + lookup_variable("lineht", & $$->segment_height); + lookup_variable("linewid", & $$->segment_width); + $$->dir = current_direction; + } + | text %prec TEXT + { + $$ = new object_spec(TEXT_OBJECT); + $$->text = new text_item($1.str, $1.filename, $1.lineno); + } + | PLOT expr + { + $$ = new object_spec(TEXT_OBJECT); + $$->text = new text_item(format_number(0, $2), 0, -1); + } + | PLOT expr text + { + $$ = new object_spec(TEXT_OBJECT); + $$->text = new text_item(format_number($3.str, $2), + $3.filename, $3.lineno); + a_delete $3.str; + } + | '[' + { + saved_state *p = new saved_state; + $<pstate>$ = p; + p->x = current_position.x; + p->y = current_position.y; + p->dir = current_direction; + p->tbl = current_table; + p->prev = current_saved_state; + current_position.x = 0.0; + current_position.y = 0.0; + current_table = new PTABLE(place); + current_saved_state = p; + olist.append(make_mark_object()); + } + element_list ']' + { + current_position.x = $<pstate>2->x; + current_position.y = $<pstate>2->y; + current_direction = $<pstate>2->dir; + $$ = new object_spec(BLOCK_OBJECT); + olist.wrap_up_block(& $$->oblist); + $$->tbl = current_table; + current_table = $<pstate>2->tbl; + current_saved_state = $<pstate>2->prev; + delete $<pstate>2; + } + | object_spec HEIGHT expr + { + $$ = $1; + $$->height = $3; + $$->flags |= HAS_HEIGHT; + } + | object_spec RADIUS expr + { + $$ = $1; + $$->radius = $3; + $$->flags |= HAS_RADIUS; + } + | object_spec WIDTH expr + { + $$ = $1; + $$->width = $3; + $$->flags |= HAS_WIDTH; + } + | object_spec DIAMETER expr + { + $$ = $1; + $$->radius = $3/2.0; + $$->flags |= HAS_RADIUS; + } + | object_spec expr %prec HEIGHT + { + $$ = $1; + $$->flags |= HAS_SEGMENT; + switch ($$->dir) { + case UP_DIRECTION: + $$->segment_pos.y += $2; + break; + case DOWN_DIRECTION: + $$->segment_pos.y -= $2; + break; + case RIGHT_DIRECTION: + $$->segment_pos.x += $2; + break; + case LEFT_DIRECTION: + $$->segment_pos.x -= $2; + break; + } + } + | object_spec UP + { + $$ = $1; + $$->dir = UP_DIRECTION; + $$->flags |= HAS_SEGMENT; + $$->segment_pos.y += $$->segment_height; + } + | object_spec UP expr + { + $$ = $1; + $$->dir = UP_DIRECTION; + $$->flags |= HAS_SEGMENT; + $$->segment_pos.y += $3; + } + | object_spec DOWN + { + $$ = $1; + $$->dir = DOWN_DIRECTION; + $$->flags |= HAS_SEGMENT; + $$->segment_pos.y -= $$->segment_height; + } + | object_spec DOWN expr + { + $$ = $1; + $$->dir = DOWN_DIRECTION; + $$->flags |= HAS_SEGMENT; + $$->segment_pos.y -= $3; + } + | object_spec RIGHT + { + $$ = $1; + $$->dir = RIGHT_DIRECTION; + $$->flags |= HAS_SEGMENT; + $$->segment_pos.x += $$->segment_width; + } + | object_spec RIGHT expr + { + $$ = $1; + $$->dir = RIGHT_DIRECTION; + $$->flags |= HAS_SEGMENT; + $$->segment_pos.x += $3; + } + | object_spec LEFT + { + $$ = $1; + $$->dir = LEFT_DIRECTION; + $$->flags |= HAS_SEGMENT; + $$->segment_pos.x -= $$->segment_width; + } + | object_spec LEFT expr + { + $$ = $1; + $$->dir = LEFT_DIRECTION; + $$->flags |= HAS_SEGMENT; + $$->segment_pos.x -= $3; + } + | object_spec FROM position + { + $$ = $1; + $$->flags |= HAS_FROM; + $$->from.x = $3.x; + $$->from.y = $3.y; + } + | object_spec TO position + { + $$ = $1; + if ($$->flags & HAS_SEGMENT) + $$->segment_list = new segment($$->segment_pos, + $$->segment_is_absolute, + $$->segment_list); + $$->flags |= HAS_SEGMENT; + $$->segment_pos.x = $3.x; + $$->segment_pos.y = $3.y; + $$->segment_is_absolute = 1; + $$->flags |= HAS_TO; + $$->to.x = $3.x; + $$->to.y = $3.y; + } + | object_spec AT position + { + $$ = $1; + $$->flags |= HAS_AT; + $$->at.x = $3.x; + $$->at.y = $3.y; + if ($$->type != ARC_OBJECT) { + $$->flags |= HAS_FROM; + $$->from.x = $3.x; + $$->from.y = $3.y; + } + } + | object_spec WITH path + { + $$ = $1; + $$->flags |= HAS_WITH; + $$->with = $3; + } + | object_spec BY expr_pair + { + $$ = $1; + $$->flags |= HAS_SEGMENT; + $$->segment_pos.x += $3.x; + $$->segment_pos.y += $3.y; + } + | object_spec THEN + { + $$ = $1; + if ($$->flags & HAS_SEGMENT) { + $$->segment_list = new segment($$->segment_pos, + $$->segment_is_absolute, + $$->segment_list); + $$->flags &= ~HAS_SEGMENT; + $$->segment_pos.x = $$->segment_pos.y = 0.0; + $$->segment_is_absolute = 0; + } + } + | object_spec SOLID + { + $$ = $1; // nothing + } + | object_spec DOTTED + { + $$ = $1; + $$->flags |= IS_DOTTED; + lookup_variable("dashwid", & $$->dash_width); + } + | object_spec DOTTED expr + { + $$ = $1; + $$->flags |= IS_DOTTED; + $$->dash_width = $3; + } + | object_spec DASHED + { + $$ = $1; + $$->flags |= IS_DASHED; + lookup_variable("dashwid", & $$->dash_width); + } + | object_spec DASHED expr + { + $$ = $1; + $$->flags |= IS_DASHED; + $$->dash_width = $3; + } + | object_spec FILL + { + $$ = $1; + $$->flags |= IS_DEFAULT_FILLED; + } + | object_spec FILL expr + { + $$ = $1; + $$->flags |= IS_FILLED; + $$->fill = $3; + } + | object_spec CHOP + { + $$ = $1; + // line chop chop means line chop 0 chop 0 + if ($$->flags & IS_DEFAULT_CHOPPED) { + $$->flags |= IS_CHOPPED; + $$->flags &= ~IS_DEFAULT_CHOPPED; + $$->start_chop = $$->end_chop = 0.0; + } + else if ($$->flags & IS_CHOPPED) { + $$->end_chop = 0.0; + } + else { + $$->flags |= IS_DEFAULT_CHOPPED; + } + } + | object_spec CHOP expr + { + $$ = $1; + if ($$->flags & IS_DEFAULT_CHOPPED) { + $$->flags |= IS_CHOPPED; + $$->flags &= ~IS_DEFAULT_CHOPPED; + $$->start_chop = 0.0; + $$->end_chop = $3; + } + else if ($$->flags & IS_CHOPPED) { + $$->end_chop = $3; + } + else { + $$->start_chop = $$->end_chop = $3; + $$->flags |= IS_CHOPPED; + } + } + | object_spec SAME + { + $$ = $1; + $$->flags |= IS_SAME; + } + | object_spec INVISIBLE + { + $$ = $1; + $$->flags |= IS_INVISIBLE; + } + | object_spec LEFT_ARROW_HEAD + { + $$ = $1; + $$->flags |= HAS_LEFT_ARROW_HEAD; + } + | object_spec RIGHT_ARROW_HEAD + { + $$ = $1; + $$->flags |= HAS_RIGHT_ARROW_HEAD; + } + | object_spec DOUBLE_ARROW_HEAD + { + $$ = $1; + $$->flags |= (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD); + } + | object_spec CW + { + $$ = $1; + $$->flags |= IS_CLOCKWISE; + } + | object_spec CCW + { + $$ = $1; + $$->flags &= ~IS_CLOCKWISE; + } + | object_spec text %prec TEXT + { + $$ = $1; + text_item **p; + for (p = & $$->text; *p; p = &(*p)->next) + ; + *p = new text_item($2.str, $2.filename, $2.lineno); + } + | object_spec LJUST + { + $$ = $1; + if ($$->text) { + text_item *p; + for (p = $$->text; p->next; p = p->next) + ; + p->adj.h = LEFT_ADJUST; + } + } + | object_spec RJUST + { + $$ = $1; + if ($$->text) { + text_item *p; + for (p = $$->text; p->next; p = p->next) + ; + p->adj.h = RIGHT_ADJUST; + } + } + | object_spec ABOVE + { + $$ = $1; + if ($$->text) { + text_item *p; + for (p = $$->text; p->next; p = p->next) + ; + p->adj.v = ABOVE_ADJUST; + } + } + | object_spec BELOW + { + $$ = $1; + if ($$->text) { + text_item *p; + for (p = $$->text; p->next; p = p->next) + ; + p->adj.v = BELOW_ADJUST; + } + } + | object_spec THICKNESS expr + { + $$ = $1; + $$->flags |= HAS_THICKNESS; + $$->thickness = $3; + } + | object_spec ALIGNED + { + $$ = $1; + $$->flags |= IS_ALIGNED; + } + ; + +text: + TEXT + { + $$ = $1; + } + | SPRINTF '(' TEXT sprintf_args ')' + { + $$.filename = $3.filename; + $$.lineno = $3.lineno; + $$.str = do_sprintf($3.str, $4.v, $4.nv); + a_delete $4.v; + a_delete $3.str; + } + ; + +sprintf_args: + /* empty */ + { + $$.v = 0; + $$.nv = 0; + $$.maxv = 0; + } + | sprintf_args ',' expr + { + $$ = $1; + if ($$.nv >= $$.maxv) { + if ($$.nv == 0) { + $$.v = new double[4]; + $$.maxv = 4; + } + else { + double *oldv = $$.v; + $$.maxv *= 2; + $$.v = new double[$$.maxv]; + memcpy($$.v, oldv, $$.nv*sizeof(double)); + a_delete oldv; + } + } + $$.v[$$.nv] = $3; + $$.nv += 1; + } + ; + +position: + position_not_place + { $$ = $1; } + | place + { + position pos = $1; + $$.x = pos.x; + $$.y = pos.y; + } + ; + +position_not_place: + expr_pair + { $$ = $1; } + | position '+' expr_pair + { + $$.x = $1.x + $3.x; + $$.y = $1.y + $3.y; + } + | position '-' expr_pair + { + $$.x = $1.x - $3.x; + $$.y = $1.y - $3.y; + } + | '(' position ',' position ')' + { + $$.x = $2.x; + $$.y = $4.y; + } + | expr between position AND position + { + $$.x = (1.0 - $1)*$3.x + $1*$5.x; + $$.y = (1.0 - $1)*$3.y + $1*$5.y; + } + | expr '<' position ',' position '>' + { + $$.x = (1.0 - $1)*$3.x + $1*$5.x; + $$.y = (1.0 - $1)*$3.y + $1*$5.y; + } + ; + +between: + BETWEEN + | OF THE WAY BETWEEN + ; + +expr_pair: + expr ',' expr + { $$.x = $1; $$.y = $3; } + | '(' expr_pair ')' + { $$ = $2; } + ; + +place: + label %prec CHOP /* line at A left == line (at A) left */ + { $$ = $1; } + | label corner + { + path pth($2); + if (!pth.follow($1, & $$)) + YYABORT; + } + | corner label + { + path pth($1); + if (!pth.follow($2, & $$)) + YYABORT; + } + | corner OF label + { + path pth($1); + if (!pth.follow($3, & $$)) + YYABORT; + } + | HERE + { + $$.x = current_position.x; + $$.y = current_position.y; + $$.obj = 0; + } + ; + +label: + LABEL + { + place *p = lookup_label($1); + if (!p) { + lex_error("there is no place `%1'", $1); + YYABORT; + } + $$ = *p; + a_delete $1; + } + | nth_primitive + { + $$.obj = $1; + } + | label '.' LABEL + { + path pth($3); + if (!pth.follow($1, & $$)) + YYABORT; + } + ; + +ordinal: + ORDINAL + { $$ = $1; } + | '`' any_expr TH + { + // XXX Check for overflow (and non-integers?). + $$ = (int)$2; + } + ; + +optional_ordinal_last: + LAST + { $$ = 1; } + | ordinal LAST + { $$ = $1; } + ; + +nth_primitive: + ordinal object_type + { + int count = 0; + object *p; + for (p = olist.head; p != 0; p = p->next) + if (p->type() == $2 && ++count == $1) { + $$ = p; + break; + } + if (p == 0) { + lex_error("there is no %1%2 %3", $1, ordinal_postfix($1), + object_type_name($2)); + YYABORT; + } + } + | optional_ordinal_last object_type + { + int count = 0; + object *p; + for (p = olist.tail; p != 0; p = p->prev) + if (p->type() == $2 && ++count == $1) { + $$ = p; + break; + } + if (p == 0) { + lex_error("there is no %1%2 last %3", $1, + ordinal_postfix($1), object_type_name($2)); + YYABORT; + } + } + ; + +object_type: + BOX + { $$ = BOX_OBJECT; } + | CIRCLE + { $$ = CIRCLE_OBJECT; } + | ELLIPSE + { $$ = ELLIPSE_OBJECT; } + | ARC + { $$ = ARC_OBJECT; } + | LINE + { $$ = LINE_OBJECT; } + | ARROW + { $$ = ARROW_OBJECT; } + | SPLINE + { $$ = SPLINE_OBJECT; } + | '[' ']' + { $$ = BLOCK_OBJECT; } + | TEXT + { $$ = TEXT_OBJECT; } + ; + +label_path: + '.' LABEL + { + $$ = new path($2); + } + | label_path '.' LABEL + { + $$ = $1; + $$->append($3); + } + ; + +relative_path: + corner + { + $$ = new path($1); + } + /* give this a lower precedence than LEFT and RIGHT so that + [A: box] with .A left == [A: box] with (.A left) */ + + | label_path %prec TEXT + { + $$ = $1; + } + | label_path corner + { + $$ = $1; + $$->append($2); + } + ; + +path: + relative_path + { + $$ = $1; + } + | '(' relative_path ',' relative_path ')' + { + $$ = $2; + $$->set_ypath($4); + } + /* The rest of these rules are a compatibility sop. */ + | ORDINAL LAST object_type relative_path + { + lex_warning("`%1%2 last %3' in `with' argument ignored", + $1, ordinal_postfix($1), object_type_name($3)); + $$ = $4; + } + | LAST object_type relative_path + { + lex_warning("`last %1' in `with' argument ignored", + object_type_name($2)); + $$ = $3; + } + | ORDINAL object_type relative_path + { + lex_warning("`%1%2 %3' in `with' argument ignored", + $1, ordinal_postfix($1), object_type_name($2)); + $$ = $3; + } + | LABEL relative_path + { + lex_warning("initial `%1' in `with' argument ignored", $1); + a_delete $1; + $$ = $2; + } + ; + +corner: + DOT_N + { $$ = &object::north; } + | DOT_E + { $$ = &object::east; } + | DOT_W + { $$ = &object::west; } + | DOT_S + { $$ = &object::south; } + | DOT_NE + { $$ = &object::north_east; } + | DOT_SE + { $$ = &object:: south_east; } + | DOT_NW + { $$ = &object::north_west; } + | DOT_SW + { $$ = &object::south_west; } + | DOT_C + { $$ = &object::center; } + | DOT_START + { $$ = &object::start; } + | DOT_END + { $$ = &object::end; } + | TOP + { $$ = &object::north; } + | BOTTOM + { $$ = &object::south; } + | LEFT + { $$ = &object::west; } + | RIGHT + { $$ = &object::east; } + | UPPER LEFT + { $$ = &object::north_west; } + | LOWER LEFT + { $$ = &object::south_west; } + | UPPER RIGHT + { $$ = &object::north_east; } + | LOWER RIGHT + { $$ = &object::south_east; } + | LEFT_CORNER + { $$ = &object::west; } + | RIGHT_CORNER + { $$ = &object::east; } + | UPPER LEFT_CORNER + { $$ = &object::north_west; } + | LOWER LEFT_CORNER + { $$ = &object::south_west; } + | UPPER RIGHT_CORNER + { $$ = &object::north_east; } + | LOWER RIGHT_CORNER + { $$ = &object::south_east; } + | CENTER + { $$ = &object::center; } + | START + { $$ = &object::start; } + | END + { $$ = &object::end; } + ; + +expr: + VARIABLE + { + if (!lookup_variable($1, & $$)) { + lex_error("there is no variable `%1'", $1); + YYABORT; + } + a_delete $1; + } + | NUMBER + { $$ = $1; } + | place DOT_X + { + if ($1.obj != 0) + $$ = $1.obj->origin().x; + else + $$ = $1.x; + } + | place DOT_Y + { + if ($1.obj != 0) + $$ = $1.obj->origin().y; + else + $$ = $1.y; + } + | place DOT_HT + { + if ($1.obj != 0) + $$ = $1.obj->height(); + else + $$ = 0.0; + } + | place DOT_WID + { + if ($1.obj != 0) + $$ = $1.obj->width(); + else + $$ = 0.0; + } + | place DOT_RAD + { + if ($1.obj != 0) + $$ = $1.obj->radius(); + else + $$ = 0.0; + } + | expr '+' expr + { $$ = $1 + $3; } + | expr '-' expr + { $$ = $1 - $3; } + | expr '*' expr + { $$ = $1 * $3; } + | expr '/' expr + { + if ($3 == 0.0) { + lex_error("division by zero"); + YYABORT; + } + $$ = $1/$3; + } + | expr '%' expr + { + if ($3 == 0.0) { + lex_error("modulus by zero"); + YYABORT; + } + $$ = fmod($1, $3); + } + | expr '^' expr + { + errno = 0; + $$ = pow($1, $3); + if (errno == EDOM) { + lex_error("arguments to `^' operator out of domain"); + YYABORT; + } + if (errno == ERANGE) { + lex_error("result of `^' operator out of range"); + YYABORT; + } + } + | '-' expr %prec '!' + { $$ = -$2; } + | '(' any_expr ')' + { $$ = $2; } + | SIN '(' any_expr ')' + { + errno = 0; + $$ = sin($3); + if (errno == ERANGE) { + lex_error("sin result out of range"); + YYABORT; + } + } + | COS '(' any_expr ')' + { + errno = 0; + $$ = cos($3); + if (errno == ERANGE) { + lex_error("cos result out of range"); + YYABORT; + } + } + | ATAN2 '(' any_expr ',' any_expr ')' + { + errno = 0; + $$ = atan2($3, $5); + if (errno == EDOM) { + lex_error("atan2 argument out of domain"); + YYABORT; + } + if (errno == ERANGE) { + lex_error("atan2 result out of range"); + YYABORT; + } + } + | LOG '(' any_expr ')' + { + errno = 0; + $$ = log10($3); + if (errno == ERANGE) { + lex_error("log result out of range"); + YYABORT; + } + } + | EXP '(' any_expr ')' + { + errno = 0; + $$ = pow(10.0, $3); + if (errno == ERANGE) { + lex_error("exp result out of range"); + YYABORT; + } + } + | SQRT '(' any_expr ')' + { + errno = 0; + $$ = sqrt($3); + if (errno == EDOM) { + lex_error("sqrt argument out of domain"); + YYABORT; + } + } + | K_MAX '(' any_expr ',' any_expr ')' + { $$ = $3 > $5 ? $3 : $5; } + | K_MIN '(' any_expr ',' any_expr ')' + { $$ = $3 < $5 ? $3 : $5; } + | INT '(' any_expr ')' + { $$ = floor($3); } + | RAND '(' any_expr ')' + { $$ = 1.0 + floor(((rand()&0x7fff)/double(0x7fff))*$3); } + | RAND '(' ')' + { + /* return a random number in the range [0,1) */ + /* portable, but not very random */ + $$ = (rand() & 0x7fff) / double(0x8000); + } + | SRAND '(' any_expr ')' + { $$ = 0; srand((unsigned int)$3); } + | expr '<' expr + { $$ = ($1 < $3); } + | expr LESSEQUAL expr + { $$ = ($1 <= $3); } + | expr '>' expr + { $$ = ($1 > $3); } + | expr GREATEREQUAL expr + { $$ = ($1 >= $3); } + | expr EQUALEQUAL expr + { $$ = ($1 == $3); } + | expr NOTEQUAL expr + { $$ = ($1 != $3); } + | expr ANDAND expr + { $$ = ($1 != 0.0 && $3 != 0.0); } + | expr OROR expr + { $$ = ($1 != 0.0 || $3 != 0.0); } + | '!' expr + { $$ = ($2 == 0.0); } + + ; + +%% + +/* bison defines const to be empty unless __STDC__ is defined, which it +isn't under cfront */ + +#ifdef const +#undef const +#endif + +static struct { + const char *name; + double val; + int scaled; // non-zero if val should be multiplied by scale +} defaults_table[] = { + { "arcrad", .25, 1 }, + { "arrowht", .1, 1 }, + { "arrowwid", .05, 1 }, + { "circlerad", .25, 1 }, + { "boxht", .5, 1 }, + { "boxwid", .75, 1 }, + { "boxrad", 0.0, 1 }, + { "dashwid", .05, 1 }, + { "ellipseht", .5, 1 }, + { "ellipsewid", .75, 1 }, + { "moveht", .5, 1 }, + { "movewid", .5, 1 }, + { "lineht", .5, 1 }, + { "linewid", .5, 1 }, + { "textht", 0.0, 1 }, + { "textwid", 0.0, 1 }, + { "scale", 1.0, 0 }, + { "linethick", -1.0, 0 }, // in points + { "fillval", .5, 0 }, + { "arrowhead", 1.0, 0 }, + { "maxpswid", 8.5, 0 }, + { "maxpsht", 11.0, 0 }, +}; + +place *lookup_label(const char *label) +{ + saved_state *state = current_saved_state; + PTABLE(place) *tbl = current_table; + for (;;) { + place *pl = tbl->lookup(label); + if (pl) + return pl; + if (!state) + return 0; + tbl = state->tbl; + state = state->prev; + } +} + +void define_label(const char *label, const place *pl) +{ + place *p = new place; + *p = *pl; + current_table->define(label, p); +} + +int lookup_variable(const char *name, double *val) +{ + place *pl = lookup_label(name); + if (pl) { + *val = pl->x; + return 1; + } + return 0; +} + +void define_variable(const char *name, double val) +{ + place *p = new place; + p->obj = 0; + p->x = val; + p->y = 0.0; + current_table->define(name, p); + if (strcmp(name, "scale") == 0) { + // When the scale changes, reset all scaled pre-defined variables to + // their default values. + for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++) + if (defaults_table[i].scaled) + define_variable(defaults_table[i].name, val*defaults_table[i].val); + } +} + +// called once only (not once per parse) + +void parse_init() +{ + current_direction = RIGHT_DIRECTION; + current_position.x = 0.0; + current_position.y = 0.0; + // This resets everything to its default value. + reset_all(); +} + +void reset(const char *nm) +{ + for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++) + if (strcmp(nm, defaults_table[i].name) == 0) { + double val = defaults_table[i].val; + if (defaults_table[i].scaled) { + double scale; + lookup_variable("scale", &scale); + val *= scale; + } + define_variable(defaults_table[i].name, val); + return; + } + lex_error("`%1' is not a predefined variable", nm); +} + +void reset_all() +{ + // We only have to explicitly reset the pre-defined variables that + // aren't scaled because `scale' is not scaled, and changing the + // value of `scale' will reset all the pre-defined variables that + // are scaled. + for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++) + if (!defaults_table[i].scaled) + define_variable(defaults_table[i].name, defaults_table[i].val); +} + +// called after each parse + +void parse_cleanup() +{ + while (current_saved_state != 0) { + delete current_table; + current_table = current_saved_state->tbl; + saved_state *tem = current_saved_state; + current_saved_state = current_saved_state->prev; + delete tem; + } + assert(current_table == &top_table); + PTABLE_ITERATOR(place) iter(current_table); + const char *key; + place *pl; + while (iter.next(&key, &pl)) + if (pl->obj != 0) { + position pos = pl->obj->origin(); + pl->obj = 0; + pl->x = pos.x; + pl->y = pos.y; + } + while (olist.head != 0) { + object *tem = olist.head; + olist.head = olist.head->next; + delete tem; + } + olist.tail = 0; + current_direction = RIGHT_DIRECTION; + current_position.x = 0.0; + current_position.y = 0.0; +} + +const char *ordinal_postfix(int n) +{ + if (n < 10 || n > 20) + switch (n % 10) { + case 1: + return "st"; + case 2: + return "nd"; + case 3: + return "rd"; + } + return "th"; +} + +const char *object_type_name(object_type type) +{ + switch (type) { + case BOX_OBJECT: + return "box"; + case CIRCLE_OBJECT: + return "circle"; + case ELLIPSE_OBJECT: + return "ellipse"; + case ARC_OBJECT: + return "arc"; + case SPLINE_OBJECT: + return "spline"; + case LINE_OBJECT: + return "line"; + case ARROW_OBJECT: + return "arrow"; + case MOVE_OBJECT: + return "move"; + case TEXT_OBJECT: + return "\"\""; + case BLOCK_OBJECT: + return "[]"; + case OTHER_OBJECT: + case MARK_OBJECT: + default: + break; + } + return "object"; +} + +static char sprintf_buf[1024]; + +char *format_number(const char *form, double n) +{ + if (form == 0) + form = "%g"; + else { + // this is a fairly feeble attempt at validation of the format + int nspecs = 0; + for (const char *p = form; *p != '\0'; p++) + if (*p == '%') { + if (p[1] == '%') + p++; + else + nspecs++; + } + if (nspecs > 1) { + lex_error("bad format `%1'", form); + return strsave(form); + } + } + sprintf(sprintf_buf, form, n); + return strsave(sprintf_buf); +} + +char *do_sprintf(const char *form, const double *v, int nv) +{ + string result; + int i = 0; + string one_format; + while (*form) { + if (*form == '%') { + one_format += *form++; + for (; *form != '\0' && strchr("#-+ 0123456789.", *form) != 0; form++) + one_format += *form; + if (*form == '\0' || strchr("eEfgG%", *form) == 0) { + lex_error("bad sprintf format"); + result += one_format; + result += form; + break; + } + if (*form == '%') { + one_format += *form++; + one_format += '\0'; + sprintf(sprintf_buf, one_format.contents()); + } + else { + if (i >= nv) { + lex_error("too few arguments to sprintf"); + result += one_format; + result += form; + break; + } + one_format += *form++; + one_format += '\0'; + sprintf(sprintf_buf, one_format.contents(), v[i++]); + } + one_format.clear(); + result += sprintf_buf; + } + else + result += *form++; + } + result += '\0'; + return strsave(result.contents()); +} diff --git a/contrib/groff/src/preproc/pic/pic_tab.h b/contrib/groff/src/preproc/pic/pic_tab.h new file mode 100644 index 0000000..1765882 --- /dev/null +++ b/contrib/groff/src/preproc/pic/pic_tab.h @@ -0,0 +1,131 @@ +#define LABEL 257 +#define VARIABLE 258 +#define NUMBER 259 +#define TEXT 260 +#define COMMAND_LINE 261 +#define DELIMITED 262 +#define ORDINAL 263 +#define TH 264 +#define LEFT_ARROW_HEAD 265 +#define RIGHT_ARROW_HEAD 266 +#define DOUBLE_ARROW_HEAD 267 +#define LAST 268 +#define UP 269 +#define DOWN 270 +#define LEFT 271 +#define RIGHT 272 +#define BOX 273 +#define CIRCLE 274 +#define ELLIPSE 275 +#define ARC 276 +#define LINE 277 +#define ARROW 278 +#define MOVE 279 +#define SPLINE 280 +#define HEIGHT 281 +#define RADIUS 282 +#define WIDTH 283 +#define DIAMETER 284 +#define FROM 285 +#define TO 286 +#define AT 287 +#define WITH 288 +#define BY 289 +#define THEN 290 +#define SOLID 291 +#define DOTTED 292 +#define DASHED 293 +#define CHOP 294 +#define SAME 295 +#define INVISIBLE 296 +#define LJUST 297 +#define RJUST 298 +#define ABOVE 299 +#define BELOW 300 +#define OF 301 +#define THE 302 +#define WAY 303 +#define BETWEEN 304 +#define AND 305 +#define HERE 306 +#define DOT_N 307 +#define DOT_E 308 +#define DOT_W 309 +#define DOT_S 310 +#define DOT_NE 311 +#define DOT_SE 312 +#define DOT_NW 313 +#define DOT_SW 314 +#define DOT_C 315 +#define DOT_START 316 +#define DOT_END 317 +#define DOT_X 318 +#define DOT_Y 319 +#define DOT_HT 320 +#define DOT_WID 321 +#define DOT_RAD 322 +#define SIN 323 +#define COS 324 +#define ATAN2 325 +#define LOG 326 +#define EXP 327 +#define SQRT 328 +#define K_MAX 329 +#define K_MIN 330 +#define INT 331 +#define RAND 332 +#define SRAND 333 +#define COPY 334 +#define THRU 335 +#define TOP 336 +#define BOTTOM 337 +#define UPPER 338 +#define LOWER 339 +#define SH 340 +#define PRINT 341 +#define CW 342 +#define CCW 343 +#define FOR 344 +#define DO 345 +#define IF 346 +#define ELSE 347 +#define ANDAND 348 +#define OROR 349 +#define NOTEQUAL 350 +#define EQUALEQUAL 351 +#define LESSEQUAL 352 +#define GREATEREQUAL 353 +#define LEFT_CORNER 354 +#define RIGHT_CORNER 355 +#define CENTER 356 +#define END 357 +#define START 358 +#define RESET 359 +#define UNTIL 360 +#define PLOT 361 +#define THICKNESS 362 +#define FILL 363 +#define ALIGNED 364 +#define SPRINTF 365 +#define COMMAND 366 +#define DEFINE 367 +#define UNDEF 368 +typedef union { + char *str; + int n; + double x; + struct { double x, y; } pair; + struct { double x; char *body; } if_data; + struct { char *str; const char *filename; int lineno; } lstr; + struct { double *v; int nv; int maxv; } dv; + struct { double val; int is_multiplicative; } by; + place pl; + object *obj; + corner crn; + path *pth; + object_spec *spec; + saved_state *pstate; + graphics_state state; + object_type obtype; +} YYSTYPE; +extern YYSTYPE yylval; diff --git a/contrib/groff/src/preproc/pic/position.h b/contrib/groff/src/preproc/pic/position.h new file mode 100644 index 0000000..ab7d546 --- /dev/null +++ b/contrib/groff/src/preproc/pic/position.h @@ -0,0 +1,47 @@ +// -*- 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. */ + +struct place; +struct position { + double x; + double y; + position(double, double ); + position(); + position(const place &); + position &operator+=(const position &); + position &operator-=(const position &); + position &operator*=(double); + position &operator/=(double); +}; + +position operator-(const position &); +position operator+(const position &, const position &); +position operator-(const position &, const position &); +position operator/(const position &, double); +position operator*(const position &, double); +// dot product +double operator*(const position &, const position &); +int operator==(const position &, const position &); +int operator!=(const position &, const position &); + +double hypot(const position &a); + +typedef position distance; + diff --git a/contrib/groff/src/preproc/pic/tex.cc b/contrib/groff/src/preproc/pic/tex.cc new file mode 100644 index 0000000..2a91b62 --- /dev/null +++ b/contrib/groff/src/preproc/pic/tex.cc @@ -0,0 +1,412 @@ +// -*- 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 "pic.h" + +#ifdef TEX_SUPPORT + +#include "common.h" + +class tex_output : public common_output { +public: + tex_output(); + ~tex_output(); + void start_picture(double, const position &ll, const position &ur); + void finish_picture(); + void text(const position &, text_piece *, int, double); + void line(const position &, const position *, int n, + const line_type &); + void polygon(const position *, int n, + const line_type &, double); + void spline(const position &, const position *, int n, + const line_type &); + void arc(const position &, const position &, const position &, + const line_type &); + void circle(const position &, double rad, const line_type &, double); + void ellipse(const position &, const distance &, const line_type &, double); + void command(const char *, const char *, int); + int supports_filled_polygons(); +private: + position upper_left; + double height; + double width; + double scale; + double pen_size; + + void point(const position &); + void dot(const position &, const line_type &); + void solid_arc(const position ¢, double rad, double start_angle, + double end_angle, const line_type <); + position transform(const position &); +protected: + virtual void set_pen_size(double ps); +}; + +// convert inches to milliinches + +inline int milliinches(double x) +{ + return int(x*1000.0 + .5); +} + +inline position tex_output::transform(const position &pos) +{ + return position((pos.x - upper_left.x)/scale, + (upper_left.y - pos.y)/scale); +} + +output *make_tex_output() +{ + return new tex_output; +} + +tex_output::tex_output() +{ +} + +tex_output::~tex_output() +{ +} + +const int DEFAULT_PEN_SIZE = 8; + +void tex_output::set_pen_size(double ps) +{ + if (ps < 0.0) + ps = -1.0; + if (ps != pen_size) { + pen_size = ps; + printf(" \\special{pn %d}%%\n", + ps < 0.0 ? DEFAULT_PEN_SIZE : int(ps*(1000.0/72.0) + .5)); + } +} + +void tex_output::start_picture(double sc, const position &ll, + const position &ur) +{ + upper_left.x = ll.x; + upper_left.y = ur.y; + scale = compute_scale(sc, ll, ur); + height = (ur.y - ll.y)/scale; + width = (ur.x - ll.x)/scale; + /* the point of \vskip 0pt is to ensure that the vtop gets + a height of 0 rather than the height of the hbox; this + might be non-zero if text from text attributes lies outside pic's + idea of the bounding box of the picture. */ + fputs("\\expandafter\\ifx\\csname graph\\endcsname\\relax \\csname newbox\\endcsname\\graph\\fi\n" + "\\expandafter\\ifx\\csname graphtemp\\endcsname\\relax \\csname newdimen\\endcsname\\graphtemp\\fi\n" + "\\setbox\\graph=\\vtop{\\vskip 0pt\\hbox{%\n", + stdout); + pen_size = -2.0; +} + +void tex_output::finish_picture() +{ + printf(" \\hbox{\\vrule depth%.3fin width0pt height 0pt}%%\n" + " \\kern %.3fin\n" + " }%%\n" + "}%%\n", + height, width); +} + +void tex_output::text(const position ¢er, text_piece *v, int n, double) +{ + position c = transform(center); + for (int i = 0; i < n; i++) + if (v[i].text != 0 && *v[i].text != '\0') { + int j = 2*i - n + 1; + if (v[i].adj.v == ABOVE_ADJUST) + j--; + else if (v[i].adj.v == BELOW_ADJUST) + j++; + if (j == 0) { + printf(" \\graphtemp=.5ex\\advance\\graphtemp by %.3fin\n", c.y); + } + else { + printf(" \\graphtemp=\\baselineskip" + "\\multiply\\graphtemp by %d" + "\\divide\\graphtemp by 2\n" + " \\advance\\graphtemp by .5ex" + "\\advance\\graphtemp by %.3fin\n", + j, c.y); + } + printf(" \\rlap{\\kern %.3fin\\lower\\graphtemp", c.x); + fputs("\\hbox to 0pt{", stdout); + if (v[i].adj.h != LEFT_ADJUST) + fputs("\\hss ", stdout); + fputs(v[i].text, stdout); + if (v[i].adj.h != RIGHT_ADJUST) + fputs("\\hss", stdout); + fputs("}}%\n", stdout); + } +} + +void tex_output::point(const position &pos) +{ + position p = transform(pos); + printf(" \\special{pa %d %d}%%\n", milliinches(p.x), milliinches(p.y)); +} + +void tex_output::line(const position &start, const position *v, int n, + const line_type <) +{ + set_pen_size(lt.thickness); + point(start); + for (int i = 0; i < n; i++) + point(v[i]); + fputs(" \\special{", stdout); + switch(lt.type) { + case line_type::invisible: + fputs("ip", stdout); + break; + case line_type::solid: + fputs("fp", stdout); + break; + case line_type::dotted: + printf("dt %.3f", lt.dash_width/scale); + break; + case line_type::dashed: + printf("da %.3f", lt.dash_width/scale); + break; + } + fputs("}%\n", stdout); +} + +void tex_output::polygon(const position *v, int n, + const line_type <, double fill) +{ + if (fill >= 0.0) { + if (fill > 1.0) + fill = 1.0; + printf(" \\special{sh %.3f}%%\n", fill); + } + line(v[n-1], v, n, lt); +} + +void tex_output::spline(const position &start, const position *v, int n, + const line_type <) +{ + if (lt.type == line_type::invisible) + return; + set_pen_size(lt.thickness); + point(start); + for (int i = 0; i < n; i++) + point(v[i]); + fputs(" \\special{sp", stdout); + switch(lt.type) { + case line_type::solid: + break; + case line_type::dotted: + printf(" %.3f", -lt.dash_width/scale); + break; + case line_type::dashed: + printf(" %.3f", lt.dash_width/scale); + break; + case line_type::invisible: + assert(0); + } + fputs("}%\n", stdout); +} + +void tex_output::solid_arc(const position ¢, double rad, + double start_angle, double end_angle, + const line_type <) +{ + set_pen_size(lt.thickness); + position c = transform(cent); + printf(" \\special{ar %d %d %d %d %f %f}%%\n", + milliinches(c.x), + milliinches(c.y), + milliinches(rad/scale), + milliinches(rad/scale), + -end_angle, + (-end_angle > -start_angle) ? (double)M_PI * 2 - start_angle + : -start_angle); +} + +void tex_output::arc(const position &start, const position ¢, + const position &end, const line_type <) +{ + switch (lt.type) { + case line_type::invisible: + break; + case line_type::dashed: + dashed_arc(start, cent, end, lt); + break; + case line_type::dotted: + dotted_arc(start, cent, end, lt); + break; + case line_type::solid: + { + position c; + if (!compute_arc_center(start, cent, end, &c)) { + line(start, &end, 1, lt); + break; + } + solid_arc(c, + hypot(cent - start), + atan2(start.y - c.y, start.x - c.x), + atan2(end.y - c.y, end.x - c.x), + lt); + break; + } + } +} + +void tex_output::circle(const position ¢, double rad, + const line_type <, double fill) +{ + if (fill >= 0.0 && lt.type != line_type::solid) { + if (fill > 1.0) + fill = 1.0; + line_type ilt; + ilt.type = line_type::invisible; + ellipse(cent, position(rad*2.0, rad*2.0), ilt, fill); + } + switch (lt.type) { + case line_type::dashed: + dashed_circle(cent, rad, lt); + break; + case line_type::invisible: + break; + case line_type::solid: + ellipse(cent, position(rad*2.0,rad*2.0), lt, fill); + break; + case line_type::dotted: + dotted_circle(cent, rad, lt); + break; + default: + assert(0); + } +} + +void tex_output::ellipse(const position ¢, const distance &dim, + const line_type <, double fill) +{ + if (lt.type == line_type::invisible) { + if (fill < 0.0) + return; + } + else + set_pen_size(lt.thickness); + if (fill >= 0.0) { + if (fill > 1.0) + fill = 1.0; + printf(" \\special{sh %.3f}%%\n", fill); + } + position c = transform(cent); + printf(" \\special{%s %d %d %d %d 0 6.28319}%%\n", + (lt.type == line_type::invisible ? "ia" : "ar"), + milliinches(c.x), + milliinches(c.y), + milliinches(dim.x/(2.0*scale)), + milliinches(dim.y/(2.0*scale))); +} + +void tex_output::command(const char *s, const char *, int) +{ + fputs(s, stdout); + putchar('%'); // avoid unwanted spaces + putchar('\n'); +} + +int tex_output::supports_filled_polygons() +{ + return 1; +} + +void tex_output::dot(const position &pos, const line_type <) +{ + if (zero_length_line_flag) { + line_type slt = lt; + slt.type = line_type::solid; + line(pos, &pos, 1, slt); + } + else { + int dot_rad = int(lt.thickness*(1000.0/(72.0*2)) + .5); + if (dot_rad == 0) + dot_rad = 1; + position p = transform(pos); + printf(" \\special{sh 1}%%\n" + " \\special{ia %d %d %d %d 0 6.28319}%%\n", + milliinches(p.x), milliinches(p.y), dot_rad, dot_rad); + } +} + +class tpic_output : public tex_output { +public: + tpic_output(); + void command(const char *, const char *, int); +private: + void set_pen_size(double ps); + int default_pen_size; + int prev_default_pen_size; +}; + +tpic_output::tpic_output() +: default_pen_size(DEFAULT_PEN_SIZE), prev_default_pen_size(DEFAULT_PEN_SIZE) +{ +} + +void tpic_output::command(const char *s, const char *filename, int lineno) +{ + assert(s[0] == '.'); + if (s[1] == 'p' && s[2] == 's' && (s[3] == '\0' || !csalpha(s[3]))) { + const char *p = s + 3; + while (csspace(*p)) + p++; + if (*p == '\0') { + int temp = default_pen_size; + default_pen_size = prev_default_pen_size; + prev_default_pen_size = temp; + } + else { + char *ptr; + int temp = (int)strtol(p, &ptr, 10); + if (temp == 0 && ptr == p) + error_with_file_and_line(filename, lineno, + "argument to `.ps' not an integer"); + else if (temp < 0) + error_with_file_and_line(filename, lineno, + "negative pen size"); + else { + prev_default_pen_size = default_pen_size; + default_pen_size = temp; + } + } + } + else + printf("\\%s%%\n", s + 1); +} + +void tpic_output::set_pen_size(double ps) +{ + if (ps < 0.0) + printf(" \\special{pn %d}%%\n", default_pen_size); + else + tex_output::set_pen_size(ps); +} + +output *make_tpic_output() +{ + return new tpic_output; +} + +#endif diff --git a/contrib/groff/src/preproc/pic/text.h b/contrib/groff/src/preproc/pic/text.h new file mode 100644 index 0000000..f9d3487 --- /dev/null +++ b/contrib/groff/src/preproc/pic/text.h @@ -0,0 +1,28 @@ +// -*- C++ -*- + +enum hadjustment { + CENTER_ADJUST, + LEFT_ADJUST, + RIGHT_ADJUST + }; + +enum vadjustment { + NONE_ADJUST, + ABOVE_ADJUST, + BELOW_ADJUST + }; + +struct adjustment { + hadjustment h; + vadjustment v; +}; + +struct text_piece { + char *text; + adjustment adj; + const char *filename; + int lineno; + + text_piece(); + ~text_piece(); +}; diff --git a/contrib/groff/src/preproc/pic/troff.cc b/contrib/groff/src/preproc/pic/troff.cc new file mode 100644 index 0000000..62fe540 --- /dev/null +++ b/contrib/groff/src/preproc/pic/troff.cc @@ -0,0 +1,504 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 + 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 "pic.h" +#include "common.h" +#include "htmlindicate.h" + + +const double RELATIVE_THICKNESS = -1.0; +const double BAD_THICKNESS = -2.0; + +class simple_output : public common_output { + virtual void simple_line(const position &, const position &) = 0; + virtual void simple_spline(const position &, const position *, int n) = 0; + virtual void simple_arc(const position &, const position &, + const position &) = 0; + virtual void simple_circle(int, const position &, double rad) = 0; + virtual void simple_ellipse(int, const position &, const distance &) = 0; + virtual void simple_polygon(int, const position *, int) = 0; + virtual void line_thickness(double) = 0; + virtual void set_fill(double) = 0; + void dot(const position &, const line_type &) = 0; +public: + void start_picture(double sc, const position &ll, const position &ur) = 0; + void finish_picture() = 0; + void text(const position &, text_piece *, int, double) = 0; + void line(const position &, const position *, int n, + const line_type &); + void polygon(const position *, int n, + const line_type &, double); + void spline(const position &, const position *, int n, + const line_type &); + void arc(const position &, const position &, const position &, + const line_type &); + void circle(const position &, double rad, const line_type &, double); + void ellipse(const position &, const distance &, const line_type &, double); + int supports_filled_polygons(); +}; + +int simple_output::supports_filled_polygons() +{ + return driver_extension_flag != 0; +} + +void simple_output::arc(const position &start, const position ¢, + const position &end, const line_type <) +{ + switch (lt.type) { + case line_type::solid: + line_thickness(lt.thickness); + simple_arc(start, cent, end); + break; + case line_type::invisible: + break; + case line_type::dashed: + dashed_arc(start, cent, end, lt); + break; + case line_type::dotted: + dotted_arc(start, cent, end, lt); + break; + } +} + +void simple_output::line(const position &start, const position *v, int n, + const line_type <) +{ + position pos = start; + line_thickness(lt.thickness); + for (int i = 0; i < n; i++) { + switch (lt.type) { + case line_type::solid: + simple_line(pos, v[i]); + break; + case line_type::dotted: + { + distance vec(v[i] - pos); + double dist = hypot(vec); + int ndots = int(dist/lt.dash_width + .5); + if (ndots == 0) + dot(pos, lt); + else { + vec /= double(ndots); + for (int j = 0; j <= ndots; j++) + dot(pos + vec*j, lt); + } + } + break; + case line_type::dashed: + { + distance vec(v[i] - pos); + double dist = hypot(vec); + if (dist <= lt.dash_width*2.0) + simple_line(pos, v[i]); + else { + int ndashes = int((dist - lt.dash_width)/(lt.dash_width*2.0) + .5); + distance dash_vec = vec*(lt.dash_width/dist); + double dash_gap = (dist - lt.dash_width)/ndashes; + distance dash_gap_vec = vec*(dash_gap/dist); + for (int j = 0; j <= ndashes; j++) { + position s(pos + dash_gap_vec*j); + simple_line(s, s + dash_vec); + } + } + } + break; + case line_type::invisible: + break; + default: + assert(0); + } + pos = v[i]; + } +} + +void simple_output::spline(const position &start, const position *v, int n, + const line_type <) +{ + line_thickness(lt.thickness); + simple_spline(start, v, n); +} + +void simple_output::polygon(const position *v, int n, + const line_type <, double fill) +{ + if (driver_extension_flag) { + if (fill >= 0.0) { + set_fill(fill); + simple_polygon(1, v, n); + } + } + if (lt.type == line_type::solid && driver_extension_flag) { + line_thickness(lt.thickness); + simple_polygon(0, v, n); + } + else if (lt.type != line_type::invisible) { + line_thickness(lt.thickness); + line(v[n - 1], v, n, lt); + } +} + +void simple_output::circle(const position ¢, double rad, + const line_type <, double fill) +{ + if (driver_extension_flag && fill >= 0.0) { + set_fill(fill); + simple_circle(1, cent, rad); + } + line_thickness(lt.thickness); + switch (lt.type) { + case line_type::invisible: + break; + case line_type::dashed: + dashed_circle(cent, rad, lt); + break; + case line_type::dotted: + dotted_circle(cent, rad, lt); + break; + case line_type::solid: + simple_circle(0, cent, rad); + break; + default: + assert(0); + } +} + +void simple_output::ellipse(const position ¢, const distance &dim, + const line_type <, double fill) +{ + if (driver_extension_flag && fill >= 0.0) { + set_fill(fill); + simple_ellipse(1, cent, dim); + } + if (lt.type != line_type::invisible) + line_thickness(lt.thickness); + switch (lt.type) { + case line_type::invisible: + break; + case line_type::dotted: + case line_type::dashed: + case line_type::solid: + simple_ellipse(0, cent, dim); + break; + default: + assert(0); + } +} + +#define FILL_MAX 1000 + +class troff_output : public simple_output { + const char *last_filename; + position upper_left; + double height; + double scale; + double last_line_thickness; + double last_fill; +public: + troff_output(); + ~troff_output(); + void start_picture(double, const position &ll, const position &ur); + void finish_picture(); + void text(const position &, text_piece *, int, double); + void dot(const position &, const line_type &); + void command(const char *, const char *, int); + void set_location(const char *, int); + void simple_line(const position &, const position &); + void simple_spline(const position &, const position *, int n); + void simple_arc(const position &, const position &, const position &); + void simple_circle(int, const position &, double rad); + void simple_ellipse(int, const position &, const distance &); + void simple_polygon(int, const position *, int); + void line_thickness(double p); + void set_fill(double); + position transform(const position &); +}; + +output *make_troff_output() +{ + return new troff_output; +} + +troff_output::troff_output() +: last_filename(0), last_line_thickness(BAD_THICKNESS), last_fill(-1.0) +{ +} + +troff_output::~troff_output() +{ +} + +inline position troff_output::transform(const position &pos) +{ + return position((pos.x - upper_left.x)/scale, + (upper_left.y - pos.y)/scale); +} + +#define FILL_REG "00" + +// If this register > 0, then pic will generate \X'ps: ...' commands +// if the aligned attribute is used. +#define GROPS_REG "0p" + +// If this register is defined, geqn won't produce `\x's. +#define EQN_NO_EXTRA_SPACE_REG "0x" + +void troff_output::start_picture(double sc, + const position &ll, const position &ur) +{ + upper_left.x = ll.x; + upper_left.y = ur.y; + scale = compute_scale(sc, ll, ur); + height = (ur.y - ll.y)/scale; + double width = (ur.x - ll.x)/scale; + graphic_start(0); + printf(".PS %.3fi %.3fi", height, width); + if (args) + printf(" %s\n", args); + else + putchar('\n'); + printf(".\\\" %g %g %g %g\n", ll.x, ll.y, ur.x, ur.y); + printf(".\\\" %.3fi %.3fi %.3fi %.3fi\n", 0.0, height, width, 0.0); + printf(".nr " FILL_REG " \\n(.u\n.nf\n"); + printf(".nr " EQN_NO_EXTRA_SPACE_REG " 1\n"); + // This guarantees that if the picture is used in a diversion it will + // have the right width. + printf("\\h'%.3fi'\n.sp -1\n", width); +} + +void troff_output::finish_picture() +{ + line_thickness(BAD_THICKNESS); + last_fill = -1.0; // force it to be reset for each picture + if (!flyback_flag) + printf(".sp %.3fi+1\n", height); + printf(".if \\n(" FILL_REG " .fi\n"); + printf(".br\n"); + printf(".nr " EQN_NO_EXTRA_SPACE_REG " 0\n"); + // this is a little gross + set_location(current_filename, current_lineno); + fputs(flyback_flag ? ".PF\n" : ".PE\n", stdout); + graphic_end(); +} + +void troff_output::command(const char *s, + const char *filename, int lineno) +{ + if (filename != 0) + set_location(filename, lineno); + fputs(s, stdout); + putchar('\n'); +} + +void troff_output::simple_circle(int filled, const position ¢, double rad) +{ + position c = transform(cent); + printf("\\h'%.3fi'" + "\\v'%.3fi'" + "\\D'%c%.3fi'" + "\n.sp -1\n", + c.x - rad/scale, + c.y, + (filled ? 'C' : 'c'), + rad*2.0/scale); +} + +void troff_output::simple_ellipse(int filled, const position ¢, + const distance &dim) +{ + position c = transform(cent); + printf("\\h'%.3fi'" + "\\v'%.3fi'" + "\\D'%c%.3fi %.3fi'" + "\n.sp -1\n", + c.x - dim.x/(2.0*scale), + c.y, + (filled ? 'E' : 'e'), + dim.x/scale, dim.y/scale); +} + +void troff_output::simple_arc(const position &start, const distance ¢, + const distance &end) +{ + position s = transform(start); + position c = transform(cent); + distance cv = c - s; + distance ev = transform(end) - c; + printf("\\h'%.3fi'" + "\\v'%.3fi'" + "\\D'a%.3fi %.3fi %.3fi %.3fi'" + "\n.sp -1\n", + s.x, s.y, cv.x, cv.y, ev.x, ev.y); +} + +void troff_output::simple_line(const position &start, const position &end) +{ + position s = transform(start); + distance ev = transform(end) - s; + printf("\\h'%.3fi'" + "\\v'%.3fi'" + "\\D'l%.3fi %.3fi'" + "\n.sp -1\n", + s.x, s.y, ev.x, ev.y); +} + +void troff_output::simple_spline(const position &start, + const position *v, int n) +{ + position pos = transform(start); + printf("\\h'%.3fi'" + "\\v'%.3fi'", + pos.x, pos.y); + fputs("\\D'~", stdout); + for (int i = 0; i < n; i++) { + position temp = transform(v[i]); + distance d = temp - pos; + pos = temp; + if (i != 0) + putchar(' '); + printf("%.3fi %.3fi", d.x, d.y); + } + printf("'\n.sp -1\n"); +} + +// a solid polygon + +void troff_output::simple_polygon(int filled, const position *v, int n) +{ + position pos = transform(v[0]); + printf("\\h'%.3fi'" + "\\v'%.3fi'", + pos.x, pos.y); + printf("\\D'%c", (filled ? 'P' : 'p')); + for (int i = 1; i < n; i++) { + position temp = transform(v[i]); + distance d = temp - pos; + pos = temp; + if (i != 1) + putchar(' '); + printf("%.3fi %.3fi", d.x, d.y); + } + printf("'\n.sp -1\n"); +} + +const double TEXT_AXIS = 0.22; // in ems + +static const char *choose_delimiter(const char *text) +{ + if (strchr(text, '\'') == 0) + return "'"; + else + return "\\(ts"; +} + +void troff_output::text(const position ¢er, text_piece *v, int n, + double ang) +{ + line_thickness(BAD_THICKNESS); // the text might use lines (eg in equations) + int rotate_flag = 0; + if (driver_extension_flag && ang != 0.0) { + rotate_flag = 1; + position c = transform(center); + printf(".if \\n(" GROPS_REG " \\{\\\n" + "\\h'%.3fi'" + "\\v'%.3fi'" + "\\X'ps: exec gsave currentpoint 2 copy translate %.4f rotate neg exch neg exch translate'" + "\n.sp -1\n" + ".\\}\n", + c.x, c.y, -ang*180.0/M_PI); + } + for (int i = 0; i < n; i++) + if (v[i].text != 0 && *v[i].text != '\0') { + position c = transform(center); + if (v[i].filename != 0) + set_location(v[i].filename, v[i].lineno); + printf("\\h'%.3fi", c.x); + const char *delim = choose_delimiter(v[i].text); + if (v[i].adj.h == RIGHT_ADJUST) + printf("-\\w%s%s%su", delim, v[i].text, delim); + else if (v[i].adj.h != LEFT_ADJUST) + printf("-(\\w%s%s%su/2u)", delim, v[i].text, delim); + putchar('\''); + printf("\\v'%.3fi-(%dv/2u)+%dv+%.2fm", + c.y, + n - 1, + i, + TEXT_AXIS); + if (v[i].adj.v == ABOVE_ADJUST) + printf("-.5v"); + else if (v[i].adj.v == BELOW_ADJUST) + printf("+.5v"); + putchar('\''); + fputs(v[i].text, stdout); + fputs("\n.sp -1\n", stdout); + } + if (rotate_flag) + printf(".if '\\*(.T'ps' \\{\\\n" + "\\X'ps: exec grestore'\n.sp -1\n" + ".\\}\n"); +} + +void troff_output::line_thickness(double p) +{ + if (p < 0.0) + p = RELATIVE_THICKNESS; + if (driver_extension_flag && p != last_line_thickness) { + printf("\\D't %.3fp'\\h'%.3fp'\n.sp -1\n", p, -p); + last_line_thickness = p; + } +} + +void troff_output::set_fill(double f) +{ + if (driver_extension_flag && f != last_fill) { + printf("\\D'f %du'\\h'%du'\n.sp -1\n", int(f*FILL_MAX), -int(f*FILL_MAX)); + last_fill = f; + } +} + +const double DOT_AXIS = .044; + +void troff_output::dot(const position ¢, const line_type <) +{ + if (driver_extension_flag) { + line_thickness(lt.thickness); + simple_line(cent, cent); + } + else { + position c = transform(cent); + printf("\\h'%.3fi-(\\w'.'u/2u)'" + "\\v'%.3fi+%.2fm'" + ".\n.sp -1\n", + c.x, + c.y, + DOT_AXIS); + } +} + +void troff_output::set_location(const char *s, int n) +{ + if (last_filename != 0 && strcmp(s, last_filename) == 0) + printf(".lf %d\n", n); + else { + printf(".lf %d %s\n", n, s); + last_filename = s; + } +} diff --git a/contrib/groff/src/preproc/refer/Makefile.sub b/contrib/groff/src/preproc/refer/Makefile.sub new file mode 100644 index 0000000..1631b5e --- /dev/null +++ b/contrib/groff/src/preproc/refer/Makefile.sub @@ -0,0 +1,23 @@ +PROG=refer +MAN1=refer.n +XLIBS=$(LIBBIB) $(LIBGROFF) +MLIB=$(LIBM) +OBJS=\ + command.o \ + label.o \ + ref.o \ + refer.o \ + token.o +CCSRCS=\ + $(srcdir)/command.cc \ + $(srcdir)/ref.cc \ + $(srcdir)/refer.cc \ + $(srcdir)/token.cc +HDRS=\ + $(srcdir)/refer.h \ + $(srcdir)/token.h \ + $(srcdir)/command.h \ + $(srcdir)/ref.h +GRAM=$(srcdir)/label.y +YTABC=$(srcdir)/label.cc +NAMEPREFIX=$(g) diff --git a/contrib/groff/src/preproc/refer/TODO b/contrib/groff/src/preproc/refer/TODO new file mode 100644 index 0000000..5bbd9bf --- /dev/null +++ b/contrib/groff/src/preproc/refer/TODO @@ -0,0 +1,124 @@ +inline references + +Some sort of macro/subroutine that can cover several references. + +move-punctuation should ignore multiple punctuation characters. + +Make the index files machine independent. + +Allow search keys to be negated (with !) to indicate that the +reference should not contain the key. Ignore negated keys during +indexed searching. + +Provide an option with lkbib and lookbib that prints the location +(filename, position) of each reference. Need to map filename_id's +back to filenames. + +Rename join-authors to join-fields. Have a separate label-join-fields +command used by @ and #. + +Have some sort of quantifier: eg $.n#A means execute `$.n' for each +instance of an A field, setting $ to that field, and then join the +results using the join-authors command. + +no-text-in-bracket command which says not to allow post_text and +pre_text when the [] flags has been given. Useful for superscripted +footnotes. + +Make it possible to translate - to \(en in page ranges. + +Trim eign a bit. + +In indexed searching discard all numeric keys except dates. + +Allow `\ ' to separate article from first word. + +%also + +Option automatically to supply [] flags in every reference. + +See if we can avoid requiring a comma before jr. and so on +in find_last_name(). + +Cache sortified authors in authors string during tentative evaluation of +label specification. + +Possibly don't allow * and % expressions in the first part of ?:, | or +& expressions. + +Handle better the case where <> occurs inside functions and in the +first operand of ~. Or perhaps implement <> using some magic character +in the string. + +Should special treatment be given to lines beginning with . in +references? (Unix refer seems to treat them like `%'). + +Add global flag to control whether all files should be stat-ed after +loading, and whether they should be stat-ed before each search. +Perhaps make this dependent on the number of files there are. + +Option to truncate keys to truncate_len in linear searching. + +Allow multiple -f options in indxbib. + +In indxbib, possibly store common words rather than common words +filename. In this case store only words that are actually present in +the file. + +Perhaps we should put out an obnoxious copyright message when lookbib +starts up. + +Provide an option that writes a file containing just the references +actually used. Useful if you want to distribute a document. + +Have a magic token such that +%A <sort stuff><magic token><print stuff> +will print as though it were +%A <print stuff> +but sort as though it were +%A <sort stuff> +Do we need this if we can specify author alternatives for sorting? +No, provided we have separate alternatives for @. + +In consider_authors when last names are ambiguous we might be able to +use just the first name and not Jr. bit. Or we might be able to +abbreviate the author. + +It ought to be possible to specify an alternative field to sort on +instead of date. (ie if there's a field giving the type of document -- +these references should sort after any years) + +Provide a way to execute a command using a command-line option. + +Option to set the label-spec as a command-line option (-L). + +Command to to specify which fields can occur multiple times: +multiple AE + +Command to specify how various fields sort: +aort-as-name A +sort-as-date D +sort-as-title T +sort-as-other O + +Command to specify which fields are author fields: +# if we don't have A use field Q +author-fields AQ + +Commands to set properties of tokens. +sortify-token \(ae ae +uppercase-token \[ae] \[AE] + +Command to set the names of months: +months january february march april may ... + +Perhaps provide some sort of macro capability: +# perhaps a macro capability +defmacro foo +annotation-field $1 +endef + +Command to control strings used in capitalization +capitalize-start \s+2 +capitalize-end \s-2 +(perhaps make these arguments to the capitalize command.) diff --git a/contrib/groff/src/preproc/refer/command.cc b/contrib/groff/src/preproc/refer/command.cc new file mode 100644 index 0000000..004189e --- /dev/null +++ b/contrib/groff/src/preproc/refer/command.cc @@ -0,0 +1,807 @@ +// -*- 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 "refer.h" +#include "refid.h" +#include "search.h" +#include "command.h" + +cset cs_field_name = csalpha; + +class input_item { + input_item *next; + char *filename; + int first_lineno; + string buffer; + const char *ptr; + const char *end; +public: + input_item(string &, const char *, int = 1); + ~input_item(); + int get_char(); + int peek_char(); + void skip_char(); + int get_location(const char **, int *); + + friend class input_stack; +}; + +input_item::input_item(string &s, const char *fn, int ln) +: filename(strsave(fn)), first_lineno(ln) +{ + buffer.move(s); + ptr = buffer.contents(); + end = ptr + buffer.length(); +} + +input_item::~input_item() +{ + a_delete filename; +} + +inline int input_item::peek_char() +{ + if (ptr >= end) + return EOF; + else + return (unsigned char)*ptr; +} + +inline int input_item::get_char() +{ + if (ptr >= end) + return EOF; + else + return (unsigned char)*ptr++; +} + +inline void input_item::skip_char() +{ + ptr++; +} + +int input_item::get_location(const char **filenamep, int *linenop) +{ + *filenamep = filename; + if (ptr == buffer.contents()) + *linenop = first_lineno; + else { + int ln = first_lineno; + const char *e = ptr - 1; + for (const char *p = buffer.contents(); p < e; p++) + if (*p == '\n') + ln++; + *linenop = ln; + } + return 1; +} + +class input_stack { + static input_item *top; +public: + static void init(); + static int get_char(); + static int peek_char(); + static void skip_char() { top->skip_char(); } + static void push_file(const char *); + static void push_string(string &, const char *, int); + static void error(const char *format, + const errarg &arg1 = empty_errarg, + const errarg &arg2 = empty_errarg, + const errarg &arg3 = empty_errarg); +}; + +input_item *input_stack::top = 0; + +void input_stack::init() +{ + while (top) { + input_item *tem = top; + top = top->next; + delete tem; + } +} + +int input_stack::get_char() +{ + while (top) { + int c = top->get_char(); + if (c >= 0) + return c; + input_item *tem = top; + top = top->next; + delete tem; + } + return -1; +} + +int input_stack::peek_char() +{ + while (top) { + int c = top->peek_char(); + if (c >= 0) + return c; + input_item *tem = top; + top = top->next; + delete tem; + } + return -1; +} + +void input_stack::push_file(const char *fn) +{ + FILE *fp; + if (strcmp(fn, "-") == 0) { + fp = stdin; + fn = "<standard input>"; + } + else { + errno = 0; + fp = fopen(fn, "r"); + if (fp == 0) { + error("can't open `%1': %2", fn, strerror(errno)); + return; + } + } + string buf; + int bol = 1; + int lineno = 1; + for (;;) { + int c = getc(fp); + if (bol && c == '.') { + // replace lines beginning with .R1 or .R2 with a blank line + c = getc(fp); + if (c == 'R') { + c = getc(fp); + if (c == '1' || c == '2') { + int cc = c; + c = getc(fp); + if (compatible_flag || c == ' ' || c == '\n' || c == EOF) { + while (c != '\n' && c != EOF) + c = getc(fp); + } + else { + buf += '.'; + buf += 'R'; + buf += cc; + } + } + else { + buf += '.'; + buf += 'R'; + } + } + else + buf += '.'; + } + if (c == EOF) + break; + if (illegal_input_char(c)) + error_with_file_and_line(fn, lineno, + "illegal input character code %1", int(c)); + else { + buf += c; + if (c == '\n') { + bol = 1; + lineno++; + } + else + bol = 0; + } + } + if (fp != stdin) + fclose(fp); + if (buf.length() > 0 && buf[buf.length() - 1] != '\n') + buf += '\n'; + input_item *it = new input_item(buf, fn); + it->next = top; + top = it; +} + +void input_stack::push_string(string &s, const char *filename, int lineno) +{ + input_item *it = new input_item(s, filename, lineno); + it->next = top; + top = it; +} + +void input_stack::error(const char *format, const errarg &arg1, + const errarg &arg2, const errarg &arg3) +{ + const char *filename; + int lineno; + for (input_item *it = top; it; it = it->next) + if (it->get_location(&filename, &lineno)) { + error_with_file_and_line(filename, lineno, format, arg1, arg2, arg3); + return; + } + ::error(format, arg1, arg2, arg3); +} + +void command_error(const char *format, const errarg &arg1, + const errarg &arg2, const errarg &arg3) +{ + input_stack::error(format, arg1, arg2, arg3); +} + +// # not recognized in "" +// \<newline> is recognized in "" +// # does not conceal newline +// if missing closing quote, word extends to end of line +// no special treatment of \ other than before newline +// \<newline> not recognized after # +// ; allowed as alternative to newline +// ; not recognized in "" +// don't clear word_buffer; just append on +// return -1 for EOF, 0 for newline, 1 for word + +int get_word(string &word_buffer) +{ + int c = input_stack::get_char(); + for (;;) { + if (c == '#') { + do { + c = input_stack::get_char(); + } while (c != '\n' && c != EOF); + break; + } + if (c == '\\' && input_stack::peek_char() == '\n') + input_stack::skip_char(); + else if (c != ' ' && c != '\t') + break; + c = input_stack::get_char(); + } + if (c == EOF) + return -1; + if (c == '\n' || c == ';') + return 0; + if (c == '"') { + for (;;) { + c = input_stack::peek_char(); + if (c == EOF || c == '\n') + break; + input_stack::skip_char(); + if (c == '"') { + int d = input_stack::peek_char(); + if (d == '"') + input_stack::skip_char(); + else + break; + } + else if (c == '\\') { + int d = input_stack::peek_char(); + if (d == '\n') + input_stack::skip_char(); + else + word_buffer += '\\'; + } + else + word_buffer += c; + } + return 1; + } + word_buffer += c; + for (;;) { + c = input_stack::peek_char(); + if (c == ' ' || c == '\t' || c == '\n' || c == '#' || c == ';') + break; + input_stack::skip_char(); + if (c == '\\') { + int d = input_stack::peek_char(); + if (d == '\n') + input_stack::skip_char(); + else + word_buffer += '\\'; + } + else + word_buffer += c; + } + return 1; +} + +union argument { + const char *s; + int n; +}; + +// This is for debugging. + +static void echo_command(int argc, argument *argv) +{ + for (int i = 0; i < argc; i++) + fprintf(stderr, "%s\n", argv[i].s); +} + +static void include_command(int argc, argument *argv) +{ + assert(argc == 1); + input_stack::push_file(argv[0].s); +} + +static void capitalize_command(int argc, argument *argv) +{ + if (argc > 0) + capitalize_fields = argv[0].s; + else + capitalize_fields.clear(); +} + +static void accumulate_command(int, argument *) +{ + accumulate = 1; +} + +static void no_accumulate_command(int, argument *) +{ + accumulate = 0; +} + +static void move_punctuation_command(int, argument *) +{ + move_punctuation = 1; +} + +static void no_move_punctuation_command(int, argument *) +{ + move_punctuation = 0; +} + +static void sort_command(int argc, argument *argv) +{ + if (argc == 0) + sort_fields = "AD"; + else + sort_fields = argv[0].s; + accumulate = 1; +} + +static void no_sort_command(int, argument *) +{ + sort_fields.clear(); +} + +static void articles_command(int argc, argument *argv) +{ + articles.clear(); + int i; + for (i = 0; i < argc; i++) { + articles += argv[i].s; + articles += '\0'; + } + int len = articles.length(); + for (i = 0; i < len; i++) + articles[i] = cmlower(articles[i]); +} + +static void database_command(int argc, argument *argv) +{ + for (int i = 0; i < argc; i++) + database_list.add_file(argv[i].s); +} + +static void default_database_command(int, argument *) +{ + search_default = 1; +} + +static void no_default_database_command(int, argument *) +{ + search_default = 0; +} + +static void bibliography_command(int argc, argument *argv) +{ + const char *saved_filename = current_filename; + int saved_lineno = current_lineno; + int saved_label_in_text = label_in_text; + label_in_text = 0; + if (!accumulate) + fputs(".]<\n", stdout); + for (int i = 0; i < argc; i++) + do_bib(argv[i].s); + if (accumulate) + output_references(); + else + fputs(".]>\n", stdout); + current_filename = saved_filename; + current_lineno = saved_lineno; + label_in_text = saved_label_in_text; +} + +static void annotate_command(int argc, argument *argv) +{ + if (argc > 0) + annotation_field = argv[0].s[0]; + else + annotation_field = 'X'; + if (argc == 2) + annotation_macro = argv[1].s; + else + annotation_macro = "AP"; +} + +static void no_annotate_command(int, argument *) +{ + annotation_macro.clear(); + annotation_field = -1; +} + +static void reverse_command(int, argument *argv) +{ + reverse_fields = argv[0].s; +} + +static void no_reverse_command(int, argument *) +{ + reverse_fields.clear(); +} + +static void abbreviate_command(int argc, argument *argv) +{ + abbreviate_fields = argv[0].s; + period_before_initial = argc > 1 ? argv[1].s : ". "; + period_before_last_name = argc > 2 ? argv[2].s : ". "; + period_before_other = argc > 3 ? argv[3].s : ". "; + period_before_hyphen = argc > 4 ? argv[4].s : "."; +} + +static void no_abbreviate_command(int, argument *) +{ + abbreviate_fields.clear(); +} + +string search_ignore_fields; + +static void search_ignore_command(int argc, argument *argv) +{ + if (argc > 0) + search_ignore_fields = argv[0].s; + else + search_ignore_fields = "XYZ"; + search_ignore_fields += '\0'; + linear_ignore_fields = search_ignore_fields.contents(); +} + +static void no_search_ignore_command(int, argument *) +{ + linear_ignore_fields = ""; +} + +static void search_truncate_command(int argc, argument *argv) +{ + if (argc > 0) + linear_truncate_len = argv[0].n; + else + linear_truncate_len = 6; +} + +static void no_search_truncate_command(int, argument *) +{ + linear_truncate_len = -1; +} + +static void discard_command(int argc, argument *argv) +{ + if (argc == 0) + discard_fields = "XYZ"; + else + discard_fields = argv[0].s; + accumulate = 1; +} + +static void no_discard_command(int, argument *) +{ + discard_fields.clear(); +} + +static void label_command(int, argument *argv) +{ + set_label_spec(argv[0].s); +} + +static void abbreviate_label_ranges_command(int argc, argument *argv) +{ + abbreviate_label_ranges = 1; + label_range_indicator = argc > 0 ? argv[0].s : "-"; +} + +static void no_abbreviate_label_ranges_command(int, argument *) +{ + abbreviate_label_ranges = 0; +} + +static void label_in_reference_command(int, argument *) +{ + label_in_reference = 1; +} + +static void no_label_in_reference_command(int, argument *) +{ + label_in_reference = 0; +} + +static void label_in_text_command(int, argument *) +{ + label_in_text = 1; +} + +static void no_label_in_text_command(int, argument *) +{ + label_in_text = 0; +} + +static void sort_adjacent_labels_command(int, argument *) +{ + sort_adjacent_labels = 1; +} + +static void no_sort_adjacent_labels_command(int, argument *) +{ + sort_adjacent_labels = 0; +} + +static void date_as_label_command(int argc, argument *argv) +{ + if (set_date_label_spec(argc > 0 ? argv[0].s : "D%a*")) + date_as_label = 1; +} + +static void no_date_as_label_command(int, argument *) +{ + date_as_label = 0; +} + +static void short_label_command(int, argument *argv) +{ + if (set_short_label_spec(argv[0].s)) + short_label_flag = 1; +} + +static void no_short_label_command(int, argument *) +{ + short_label_flag = 0; +} + +static void compatible_command(int, argument *) +{ + compatible_flag = 1; +} + +static void no_compatible_command(int, argument *) +{ + compatible_flag = 0; +} + +static void join_authors_command(int argc, argument *argv) +{ + join_authors_exactly_two = argv[0].s; + join_authors_default = argc > 1 ? argv[1].s : argv[0].s; + join_authors_last_two = argc == 3 ? argv[2].s : argv[0].s; +} + +static void bracket_label_command(int, argument *argv) +{ + pre_label = argv[0].s; + post_label = argv[1].s; + sep_label = argv[2].s; +} + +static void separate_label_second_parts_command(int, argument *argv) +{ + separate_label_second_parts = argv[0].s; +} + +static void et_al_command(int argc, argument *argv) +{ + et_al = argv[0].s; + et_al_min_elide = argv[1].n; + if (et_al_min_elide < 1) + et_al_min_elide = 1; + et_al_min_total = argc >= 3 ? argv[2].n : 0; +} + +static void no_et_al_command(int, argument *) +{ + et_al.clear(); + et_al_min_elide = 0; +} + +typedef void (*command_t)(int, argument *); + +/* arg_types is a string describing the numbers and types of arguments. +s means a string, i means an integer, f is a list of fields, F is +a single field, +? means that the previous argument is optional, * means that the +previous argument can occur any number of times. */ + +struct { + const char *name; + command_t func; + const char *arg_types; +} command_table[] = { + { "include", include_command, "s" }, + { "echo", echo_command, "s*" }, + { "capitalize", capitalize_command, "f?" }, + { "accumulate", accumulate_command, "" }, + { "no-accumulate", no_accumulate_command, "" }, + { "move-punctuation", move_punctuation_command, "" }, + { "no-move-punctuation", no_move_punctuation_command, "" }, + { "sort", sort_command, "s?" }, + { "no-sort", no_sort_command, "" }, + { "articles", articles_command, "s*" }, + { "database", database_command, "ss*" }, + { "default-database", default_database_command, "" }, + { "no-default-database", no_default_database_command, "" }, + { "bibliography", bibliography_command, "ss*" }, + { "annotate", annotate_command, "F?s?" }, + { "no-annotate", no_annotate_command, "" }, + { "reverse", reverse_command, "s" }, + { "no-reverse", no_reverse_command, "" }, + { "abbreviate", abbreviate_command, "ss?s?s?s?" }, + { "no-abbreviate", no_abbreviate_command, "" }, + { "search-ignore", search_ignore_command, "f?" }, + { "no-search-ignore", no_search_ignore_command, "" }, + { "search-truncate", search_truncate_command, "i?" }, + { "no-search-truncate", no_search_truncate_command, "" }, + { "discard", discard_command, "f?" }, + { "no-discard", no_discard_command, "" }, + { "label", label_command, "s" }, + { "abbreviate-label-ranges", abbreviate_label_ranges_command, "s?" }, + { "no-abbreviate-label-ranges", no_abbreviate_label_ranges_command, "" }, + { "label-in-reference", label_in_reference_command, "" }, + { "no-label-in-reference", no_label_in_reference_command, "" }, + { "label-in-text", label_in_text_command, "" }, + { "no-label-in-text", no_label_in_text_command, "" }, + { "sort-adjacent-labels", sort_adjacent_labels_command, "" }, + { "no-sort-adjacent-labels", no_sort_adjacent_labels_command, "" }, + { "date-as-label", date_as_label_command, "s?" }, + { "no-date-as-label", no_date_as_label_command, "" }, + { "short-label", short_label_command, "s" }, + { "no-short-label", no_short_label_command, "" }, + { "compatible", compatible_command, "" }, + { "no-compatible", no_compatible_command, "" }, + { "join-authors", join_authors_command, "sss?" }, + { "bracket-label", bracket_label_command, "sss" }, + { "separate-label-second-parts", separate_label_second_parts_command, "s" }, + { "et-al", et_al_command, "sii?" }, + { "no-et-al", no_et_al_command, "" }, +}; + +static int check_args(const char *types, const char *name, + int argc, argument *argv) +{ + int argno = 0; + while (*types) { + if (argc == 0) { + if (types[1] == '?') + break; + else if (types[1] == '*') { + assert(types[2] == '\0'); + break; + } + else { + input_stack::error("missing argument for command `%1'", name); + return 0; + } + } + switch (*types) { + case 's': + break; + case 'i': + { + char *ptr; + long n = strtol(argv->s, &ptr, 10); + if ((n == 0 && ptr == argv->s) + || *ptr != '\0') { + input_stack::error("argument %1 for command `%2' must be an integer", + argno + 1, name); + return 0; + } + argv->n = (int)n; + break; + } + case 'f': + { + for (const char *ptr = argv->s; *ptr != '\0'; ptr++) + if (!cs_field_name(*ptr)) { + input_stack::error("argument %1 for command `%2' must be a list of fields", + argno + 1, name); + return 0; + } + break; + } + case 'F': + if (argv->s[0] == '\0' || argv->s[1] != '\0' + || !cs_field_name(argv->s[0])) { + input_stack::error("argument %1 for command `%2' must be a field name", + argno + 1, name); + return 0; + } + break; + default: + assert(0); + } + if (types[1] == '?') + types += 2; + else if (types[1] != '*') + types += 1; + --argc; + ++argv; + ++argno; + } + if (argc > 0) { + input_stack::error("too many arguments for command `%1'", name); + return 0; + } + return 1; +} + +static void execute_command(const char *name, int argc, argument *argv) +{ + for (int i = 0; i < sizeof(command_table)/sizeof(command_table[0]); i++) + if (strcmp(name, command_table[i].name) == 0) { + if (check_args(command_table[i].arg_types, name, argc, argv)) + (*command_table[i].func)(argc, argv); + return; + } + input_stack::error("unknown command `%1'", name); +} + +static void command_loop() +{ + string command; + for (;;) { + command.clear(); + int res = get_word(command); + if (res != 1) { + if (res == 0) + continue; + break; + } + int argc = 0; + command += '\0'; + while ((res = get_word(command)) == 1) { + argc++; + command += '\0'; + } + argument *argv = new argument[argc]; + const char *ptr = command.contents(); + for (int i = 0; i < argc; i++) + argv[i].s = ptr = strchr(ptr, '\0') + 1; + execute_command(command.contents(), argc, argv); + a_delete argv; + if (res == -1) + break; + } +} + +void process_commands(const char *file) +{ + input_stack::init(); + input_stack::push_file(file); + command_loop(); +} + +void process_commands(string &s, const char *file, int lineno) +{ + input_stack::init(); + input_stack::push_string(s, file, lineno); + command_loop(); +} diff --git a/contrib/groff/src/preproc/refer/command.h b/contrib/groff/src/preproc/refer/command.h new file mode 100644 index 0000000..c7085db --- /dev/null +++ b/contrib/groff/src/preproc/refer/command.h @@ -0,0 +1,36 @@ +// -*- 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. */ + +void process_commands(const char *file); +void process_commands(string &s, const char *file, int lineno); + +extern int accumulate; +extern int move_punctuation; +extern int search_default; +extern search_list database_list; +extern int label_in_text; +extern int label_in_reference; +extern int sort_adjacent_labels; +extern string pre_label; +extern string post_label; +extern string sep_label; + +extern void do_bib(const char *); +extern void output_references(); diff --git a/contrib/groff/src/preproc/refer/label.cc b/contrib/groff/src/preproc/refer/label.cc new file mode 100644 index 0000000..c6dc07c --- /dev/null +++ b/contrib/groff/src/preproc/refer/label.cc @@ -0,0 +1,1602 @@ +#ifndef lint +/*static char yysccsid[] = "from: @(#)yaccpar 1.9 (Berkeley) 02/21/93";*/ +static char yyrcsid[] = "$Id: label.cc,v 1.2 2000/02/28 11:02:12 wlemb Exp $"; +#endif +#define YYBYACC 1 +#define YYMAJOR 1 +#define YYMINOR 9 +#define yyclearin (yychar=(-1)) +#define yyerrok (yyerrflag=0) +#define YYRECOVERING (yyerrflag!=0) +#define YYPREFIX "yy" +#line 22 "label.y" + +#include "refer.h" +#include "refid.h" +#include "ref.h" +#include "token.h" + +int yylex(); +void yyerror(const char *); +int yyparse(); + +static const char *format_serial(char c, int n); + +struct label_info { + int start; + int length; + int count; + int total; + label_info(const string &); +}; + +label_info *lookup_label(const string &label); + +struct expression { + enum { + /* Does the tentative label depend on the reference?*/ + CONTAINS_VARIABLE = 01, + CONTAINS_STAR = 02, + CONTAINS_FORMAT = 04, + CONTAINS_AT = 010 + }; + virtual ~expression() { } + virtual void evaluate(int, const reference &, string &, + substring_position &) = 0; + virtual unsigned analyze() { return 0; } +}; + +class at_expr : public expression { +public: + at_expr() { } + void evaluate(int, const reference &, string &, substring_position &); + unsigned analyze() { return CONTAINS_VARIABLE|CONTAINS_AT; } +}; + +class format_expr : public expression { + char type; + int width; + int first_number; +public: + format_expr(char c, int w = 0, int f = 1) + : type(c), width(w), first_number(f) { } + void evaluate(int, const reference &, string &, substring_position &); + unsigned analyze() { return CONTAINS_FORMAT; } +}; + +class field_expr : public expression { + int number; + char name; +public: + field_expr(char nm, int num) : number(num), name(nm) { } + void evaluate(int, const reference &, string &, substring_position &); + unsigned analyze() { return CONTAINS_VARIABLE; } +}; + +class literal_expr : public expression { + string s; +public: + literal_expr(const char *ptr, int len) : s(ptr, len) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +class unary_expr : public expression { +protected: + expression *expr; +public: + unary_expr(expression *e) : expr(e) { } + ~unary_expr() { delete expr; } + void evaluate(int, const reference &, string &, substring_position &) = 0; + unsigned analyze() { return expr ? expr->analyze() : 0; } +}; + +/* This caches the analysis of an expression.*/ + +class analyzed_expr : public unary_expr { + unsigned flags; +public: + analyzed_expr(expression *); + void evaluate(int, const reference &, string &, substring_position &); + unsigned analyze() { return flags; } +}; + +class star_expr : public unary_expr { +public: + star_expr(expression *e) : unary_expr(e) { } + void evaluate(int, const reference &, string &, substring_position &); + unsigned analyze() { + return ((expr ? (expr->analyze() & ~CONTAINS_VARIABLE) : 0) + | CONTAINS_STAR); + } +}; + +typedef void map_func(const char *, const char *, string &); + +class map_expr : public unary_expr { + map_func *func; +public: + map_expr(expression *e, map_func *f) : unary_expr(e), func(f) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +typedef const char *extractor_func(const char *, const char *, const char **); + +class extractor_expr : public unary_expr { + int part; + extractor_func *func; +public: + enum { BEFORE = +1, MATCH = 0, AFTER = -1 }; + extractor_expr(expression *e, extractor_func *f, int pt) + : unary_expr(e), part(pt), func(f) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +class truncate_expr : public unary_expr { + int n; +public: + truncate_expr(expression *e, int i) : unary_expr(e), n(i) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +class separator_expr : public unary_expr { +public: + separator_expr(expression *e) : unary_expr(e) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +class binary_expr : public expression { +protected: + expression *expr1; + expression *expr2; +public: + binary_expr(expression *e1, expression *e2) : expr1(e1), expr2(e2) { } + ~binary_expr() { delete expr1; delete expr2; } + void evaluate(int, const reference &, string &, substring_position &) = 0; + unsigned analyze() { + return (expr1 ? expr1->analyze() : 0) | (expr2 ? expr2->analyze() : 0); + } +}; + +class alternative_expr : public binary_expr { +public: + alternative_expr(expression *e1, expression *e2) : binary_expr(e1, e2) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +class list_expr : public binary_expr { +public: + list_expr(expression *e1, expression *e2) : binary_expr(e1, e2) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +class substitute_expr : public binary_expr { +public: + substitute_expr(expression *e1, expression *e2) : binary_expr(e1, e2) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +class ternary_expr : public expression { +protected: + expression *expr1; + expression *expr2; + expression *expr3; +public: + ternary_expr(expression *e1, expression *e2, expression *e3) + : expr1(e1), expr2(e2), expr3(e3) { } + ~ternary_expr() { delete expr1; delete expr2; delete expr3; } + void evaluate(int, const reference &, string &, substring_position &) = 0; + unsigned analyze() { + return ((expr1 ? expr1->analyze() : 0) + | (expr2 ? expr2->analyze() : 0) + | (expr3 ? expr3->analyze() : 0)); + } +}; + +class conditional_expr : public ternary_expr { +public: + conditional_expr(expression *e1, expression *e2, expression *e3) + : ternary_expr(e1, e2, e3) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +static expression *parsed_label = 0; +static expression *parsed_date_label = 0; +static expression *parsed_short_label = 0; + +static expression *parse_result; + +string literals; + +#line 221 "label.y" +typedef union { + int num; + expression *expr; + struct { int ndigits; int val; } dig; + struct { int start; int len; } str; +} YYSTYPE; +#line 218 "y.tab.c" +#define TOKEN_LETTER 257 +#define TOKEN_LITERAL 258 +#define TOKEN_DIGIT 259 +#define YYERRCODE 256 +short yylhs[] = { -1, + 0, 1, 1, 6, 6, 2, 2, 2, 3, 3, + 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 9, 9, 7, 7, 8, 8, + 10, 10, 10, +}; +short yylen[] = { 2, + 1, 1, 5, 0, 1, 1, 3, 3, 1, 2, + 1, 3, 1, 1, 1, 2, 2, 2, 5, 3, + 3, 2, 3, 3, 0, 1, 1, 2, 1, 2, + 0, 1, 1, +}; +short yydefred[] = { 0, + 0, 14, 13, 0, 0, 0, 0, 5, 0, 0, + 0, 0, 1, 27, 0, 17, 29, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 22, 0, 28, + 30, 23, 24, 0, 0, 0, 32, 33, 0, 0, + 0, 0, 0, 0, 3, 0, 19, +}; +short yydgoto[] = { 7, + 8, 9, 10, 11, 12, 13, 15, 18, 47, 39, +}; +short yysindex[] = { -32, + -257, 0, 0, -240, -32, -32, 0, 0, -18, -32, + -36, -114, 0, 0, -246, 0, 0, -241, -14, -39, + -32, -32, -32, -114, -21, -257, -257, 0, -32, 0, + 0, 0, 0, -25, -32, -32, 0, 0, -223, -246, + -246, -36, -32, -257, 0, -246, 0, +}; +short yyrindex[] = { 35, + 1, 0, 0, 0, -5, -4, 0, 0, 14, 208, + 159, 224, 0, 0, 11, 0, 0, 40, 0, 0, + 2, 0, 0, 253, -220, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 263, 281, 0, 0, 0, 50, + 105, 214, 0, 115, 0, 149, 0, +}; +short yygindex[] = { 0, + 19, 0, 7, 37, -10, 10, -23, 0, 0, 0, +}; +#define YYTABLESIZE 511 +short yytable[] = { 24, + 15, 14, 40, 41, 4, 28, 26, 5, 27, 25, + 16, 29, 30, 2, 19, 20, 16, 31, 17, 23, + 46, 37, 33, 38, 24, 24, 32, 6, 35, 36, + 34, 3, 43, 44, 4, 4, 31, 15, 15, 18, + 15, 15, 15, 15, 21, 15, 15, 16, 16, 20, + 16, 16, 16, 16, 2, 16, 16, 4, 15, 4, + 15, 45, 15, 15, 15, 42, 0, 0, 16, 0, + 16, 2, 16, 16, 16, 2, 18, 18, 0, 18, + 18, 18, 18, 0, 18, 18, 20, 20, 0, 20, + 20, 20, 20, 0, 20, 20, 0, 18, 0, 18, + 0, 18, 18, 18, 21, 22, 0, 20, 0, 20, + 0, 20, 20, 20, 25, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 15, 0, 15, 0, 0, 0, + 0, 0, 0, 0, 16, 0, 16, 0, 0, 0, + 0, 21, 21, 0, 21, 21, 21, 21, 26, 21, + 21, 25, 25, 0, 25, 25, 25, 25, 11, 25, + 25, 0, 21, 18, 21, 18, 21, 21, 21, 0, + 0, 0, 25, 20, 25, 20, 25, 25, 25, 0, + 0, 0, 0, 0, 0, 26, 26, 0, 26, 26, + 26, 26, 0, 26, 26, 11, 11, 0, 11, 11, + 0, 0, 0, 0, 0, 0, 26, 6, 26, 0, + 26, 26, 26, 12, 0, 0, 11, 0, 11, 0, + 11, 11, 11, 9, 1, 2, 0, 0, 21, 0, + 21, 0, 0, 0, 0, 0, 0, 0, 25, 0, + 25, 0, 0, 0, 0, 6, 0, 0, 6, 0, + 12, 12, 10, 12, 12, 0, 0, 15, 15, 0, + 9, 9, 7, 9, 9, 6, 0, 16, 16, 6, + 6, 12, 26, 12, 26, 12, 12, 12, 0, 0, + 8, 9, 11, 9, 11, 9, 9, 9, 0, 10, + 10, 0, 10, 10, 0, 0, 18, 18, 0, 0, + 7, 0, 0, 7, 0, 0, 20, 20, 0, 0, + 10, 0, 10, 0, 10, 10, 10, 0, 8, 0, + 7, 8, 0, 0, 7, 7, 0, 0, 0, 0, + 0, 6, 0, 0, 0, 0, 0, 12, 8, 12, + 0, 0, 8, 8, 0, 0, 0, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 21, 21, 0, 0, 0, 0, 0, 0, 0, + 0, 25, 25, 0, 0, 0, 10, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 26, 26, 0, 0, 0, + 0, 0, 0, 0, 0, 11, 11, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, + 10, +}; +short yycheck[] = { 10, + 0, 259, 26, 27, 37, 42, 43, 40, 45, 46, + 0, 126, 259, 0, 5, 6, 257, 259, 259, 38, + 44, 43, 62, 45, 35, 36, 41, 60, 22, 23, + 21, 64, 58, 257, 0, 41, 257, 37, 38, 0, + 40, 41, 42, 43, 63, 45, 46, 37, 38, 0, + 40, 41, 42, 43, 41, 45, 46, 62, 58, 58, + 60, 43, 62, 63, 64, 29, -1, -1, 58, -1, + 60, 58, 62, 63, 64, 62, 37, 38, -1, 40, + 41, 42, 43, -1, 45, 46, 37, 38, -1, 40, + 41, 42, 43, -1, 45, 46, -1, 58, -1, 60, + -1, 62, 63, 64, 0, 124, -1, 58, -1, 60, + -1, 62, 63, 64, 0, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 124, -1, 126, -1, -1, -1, + -1, -1, -1, -1, 124, -1, 126, -1, -1, -1, + -1, 37, 38, -1, 40, 41, 42, 43, 0, 45, + 46, 37, 38, -1, 40, 41, 42, 43, 0, 45, + 46, -1, 58, 124, 60, 126, 62, 63, 64, -1, + -1, -1, 58, 124, 60, 126, 62, 63, 64, -1, + -1, -1, -1, -1, -1, 37, 38, -1, 40, 41, + 42, 43, -1, 45, 46, 37, 38, -1, 40, 41, + -1, -1, -1, -1, -1, -1, 58, 0, 60, -1, + 62, 63, 64, 0, -1, -1, 58, -1, 60, -1, + 62, 63, 64, 0, 257, 258, -1, -1, 124, -1, + 126, -1, -1, -1, -1, -1, -1, -1, 124, -1, + 126, -1, -1, -1, -1, 38, -1, -1, 41, -1, + 37, 38, 0, 40, 41, -1, -1, 257, 258, -1, + 37, 38, 0, 40, 41, 58, -1, 257, 258, 62, + 63, 58, 124, 60, 126, 62, 63, 64, -1, -1, + 0, 58, 124, 60, 126, 62, 63, 64, -1, 37, + 38, -1, 40, 41, -1, -1, 257, 258, -1, -1, + 38, -1, -1, 41, -1, -1, 257, 258, -1, -1, + 58, -1, 60, -1, 62, 63, 64, -1, 38, -1, + 58, 41, -1, -1, 62, 63, -1, -1, -1, -1, + -1, 124, -1, -1, -1, -1, -1, 124, 58, 126, + -1, -1, 62, 63, -1, -1, -1, 124, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 257, 258, -1, -1, -1, -1, -1, -1, -1, + -1, 257, 258, -1, -1, -1, 124, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 124, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 124, 257, 258, -1, -1, -1, + -1, -1, -1, -1, -1, 257, 258, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 257, 258, -1, -1, -1, -1, -1, -1, -1, -1, + 257, 258, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 257, + 258, +}; +#define YYFINAL 7 +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif +#define YYMAXTOKEN 259 +#if YYDEBUG +char *yyname[] = { +"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,"'%'","'&'",0,"'('","')'","'*'","'+'",0,"'-'","'.'",0,0,0,0,0,0,0,0,0,0,0, +"':'",0,"'<'",0,"'>'","'?'","'@'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'|'",0, +"'~'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,"TOKEN_LETTER","TOKEN_LITERAL","TOKEN_DIGIT", +}; +char *yyrule[] = { +"$accept : expr", +"expr : optional_conditional", +"conditional : alternative", +"conditional : alternative '?' optional_conditional ':' conditional", +"optional_conditional :", +"optional_conditional : conditional", +"alternative : list", +"alternative : alternative '|' list", +"alternative : alternative '&' list", +"list : substitute", +"list : list substitute", +"substitute : string", +"substitute : substitute '~' string", +"string : '@'", +"string : TOKEN_LITERAL", +"string : TOKEN_LETTER", +"string : TOKEN_LETTER number", +"string : '%' TOKEN_LETTER", +"string : '%' digits", +"string : string '.' flag TOKEN_LETTER optional_number", +"string : string '+' number", +"string : string '-' number", +"string : string '*'", +"string : '(' optional_conditional ')'", +"string : '<' optional_conditional '>'", +"optional_number :", +"optional_number : number", +"number : TOKEN_DIGIT", +"number : number TOKEN_DIGIT", +"digits : TOKEN_DIGIT", +"digits : digits TOKEN_DIGIT", +"flag :", +"flag : '+'", +"flag : '-'", +}; +#endif +#ifdef YYSTACKSIZE +#undef YYMAXDEPTH +#define YYMAXDEPTH YYSTACKSIZE +#else +#ifdef YYMAXDEPTH +#define YYSTACKSIZE YYMAXDEPTH +#else +#define YYSTACKSIZE 500 +#define YYMAXDEPTH 500 +#endif +#endif +int yydebug; +int yynerrs; +int yyerrflag; +int yychar; +short *yyssp; +YYSTYPE *yyvsp; +YYSTYPE yyval; +YYSTYPE yylval; +short yyss[YYSTACKSIZE]; +YYSTYPE yyvs[YYSTACKSIZE]; +#define yystacksize YYSTACKSIZE +#line 397 "label.y" + +/* bison defines const to be empty unless __STDC__ is defined, which it +isn't under cfront */ + +#ifdef const +#undef const +#endif + +const char *spec_ptr; +const char *spec_end; +const char *spec_cur; + +int yylex() +{ + while (spec_ptr < spec_end && csspace(*spec_ptr)) + spec_ptr++; + spec_cur = spec_ptr; + if (spec_ptr >= spec_end) + return 0; + unsigned char c = *spec_ptr++; + if (csalpha(c)) { + yylval.num = c; + return TOKEN_LETTER; + } + if (csdigit(c)) { + yylval.num = c - '0'; + return TOKEN_DIGIT; + } + if (c == '\'') { + yylval.str.start = literals.length(); + for (; spec_ptr < spec_end; spec_ptr++) { + if (*spec_ptr == '\'') { + if (++spec_ptr < spec_end && *spec_ptr == '\'') + literals += '\''; + else { + yylval.str.len = literals.length() - yylval.str.start; + return TOKEN_LITERAL; + } + } + else + literals += *spec_ptr; + } + yylval.str.len = literals.length() - yylval.str.start; + return TOKEN_LITERAL; + } + return c; +} + +int set_label_spec(const char *label_spec) +{ + spec_cur = spec_ptr = label_spec; + spec_end = strchr(label_spec, '\0'); + literals.clear(); + if (yyparse()) + return 0; + delete parsed_label; + parsed_label = parse_result; + return 1; +} + +int set_date_label_spec(const char *label_spec) +{ + spec_cur = spec_ptr = label_spec; + spec_end = strchr(label_spec, '\0'); + literals.clear(); + if (yyparse()) + return 0; + delete parsed_date_label; + parsed_date_label = parse_result; + return 1; +} + +int set_short_label_spec(const char *label_spec) +{ + spec_cur = spec_ptr = label_spec; + spec_end = strchr(label_spec, '\0'); + literals.clear(); + if (yyparse()) + return 0; + delete parsed_short_label; + parsed_short_label = parse_result; + return 1; +} + +void yyerror(const char *message) +{ + if (spec_cur < spec_end) + command_error("label specification %1 before `%2'", message, spec_cur); + else + command_error("label specification %1 at end of string", + message, spec_cur); +} + +void at_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &) +{ + if (tentative) + ref.canonicalize_authors(result); + else { + const char *end, *start = ref.get_authors(&end); + if (start) + result.append(start, end - start); + } +} + +void format_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &) +{ + if (tentative) + return; + const label_info *lp = ref.get_label_ptr(); + int num = lp == 0 ? ref.get_number() : lp->count; + if (type != '0') + result += format_serial(type, num + 1); + else { + const char *ptr = i_to_a(num + first_number); + int pad = width - strlen(ptr); + while (--pad >= 0) + result += '0'; + result += ptr; + } +} + +static const char *format_serial(char c, int n) +{ + assert(n > 0); + static char buf[128]; // more than enough. + switch (c) { + case 'i': + case 'I': + { + char *p = buf; + // troff uses z and w to represent 10000 and 5000 in Roman + // numerals; I can find no historical basis for this usage + const char *s = c == 'i' ? "zwmdclxvi" : "ZWMDCLXVI"; + if (n >= 40000) + return i_to_a(n); + while (n >= 10000) { + *p++ = s[0]; + n -= 10000; + } + for (int i = 1000; i > 0; i /= 10, s += 2) { + int m = n/i; + n -= m*i; + switch (m) { + case 3: + *p++ = s[2]; + /* falls through */ + case 2: + *p++ = s[2]; + /* falls through */ + case 1: + *p++ = s[2]; + break; + case 4: + *p++ = s[2]; + *p++ = s[1]; + break; + case 8: + *p++ = s[1]; + *p++ = s[2]; + *p++ = s[2]; + *p++ = s[2]; + break; + case 7: + *p++ = s[1]; + *p++ = s[2]; + *p++ = s[2]; + break; + case 6: + *p++ = s[1]; + *p++ = s[2]; + break; + case 5: + *p++ = s[1]; + break; + case 9: + *p++ = s[2]; + *p++ = s[0]; + } + } + *p = 0; + break; + } + case 'a': + case 'A': + { + char *p = buf; + // this is derived from troff/reg.c + while (n > 0) { + int d = n % 26; + if (d == 0) + d = 26; + n -= d; + n /= 26; + *p++ = c + d - 1; // ASCII dependent + } + *p-- = 0; + // Reverse it. + char *q = buf; + while (q < p) { + char temp = *q; + *q = *p; + *p = temp; + --p; + ++q; + } + break; + } + default: + assert(0); + } + return buf; +} + +void field_expr::evaluate(int, const reference &ref, + string &result, substring_position &) +{ + const char *end; + const char *start = ref.get_field(name, &end); + if (start) { + start = nth_field(number, start, &end); + if (start) + result.append(start, end - start); + } +} + +void literal_expr::evaluate(int, const reference &, + string &result, substring_position &) +{ + result += s; +} + +analyzed_expr::analyzed_expr(expression *e) +: unary_expr(e), flags(e ? e->analyze() : 0) +{ +} + +void analyzed_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &pos) +{ + if (expr) + expr->evaluate(tentative, ref, result, pos); +} + +void star_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &pos) +{ + const label_info *lp = ref.get_label_ptr(); + if (!tentative + && (lp == 0 || lp->total > 1) + && expr) + expr->evaluate(tentative, ref, result, pos); +} + +void separator_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &pos) +{ + int start_length = result.length(); + int is_first = pos.start < 0; + if (expr) + expr->evaluate(tentative, ref, result, pos); + if (is_first) { + pos.start = start_length; + pos.length = result.length() - start_length; + } +} + +void map_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &) +{ + if (expr) { + string temp; + substring_position temp_pos; + expr->evaluate(tentative, ref, temp, temp_pos); + (*func)(temp.contents(), temp.contents() + temp.length(), result); + } +} + +void extractor_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &) +{ + if (expr) { + string temp; + substring_position temp_pos; + expr->evaluate(tentative, ref, temp, temp_pos); + const char *end, *start = (*func)(temp.contents(), + temp.contents() + temp.length(), + &end); + switch (part) { + case BEFORE: + if (start) + result.append(temp.contents(), start - temp.contents()); + else + result += temp; + break; + case MATCH: + if (start) + result.append(start, end - start); + break; + case AFTER: + if (start) + result.append(end, temp.contents() + temp.length() - end); + break; + default: + assert(0); + } + } +} + +static void first_part(int len, const char *ptr, const char *end, + string &result) +{ + for (;;) { + const char *token_start = ptr; + if (!get_token(&ptr, end)) + break; + const token_info *ti = lookup_token(token_start, ptr); + int counts = ti->sortify_non_empty(token_start, ptr); + if (counts && --len < 0) + break; + if (counts || ti->is_accent()) + result.append(token_start, ptr - token_start); + } +} + +static void last_part(int len, const char *ptr, const char *end, + string &result) +{ + const char *start = ptr; + int count = 0; + for (;;) { + const char *token_start = ptr; + if (!get_token(&ptr, end)) + break; + const token_info *ti = lookup_token(token_start, ptr); + if (ti->sortify_non_empty(token_start, ptr)) + count++; + } + ptr = start; + int skip = count - len; + if (skip > 0) { + for (;;) { + const char *token_start = ptr; + if (!get_token(&ptr, end)) + assert(0); + const token_info *ti = lookup_token(token_start, ptr); + if (ti->sortify_non_empty(token_start, ptr) && --skip < 0) { + ptr = token_start; + break; + } + } + } + first_part(len, ptr, end, result); +} + +void truncate_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &) +{ + if (expr) { + string temp; + substring_position temp_pos; + expr->evaluate(tentative, ref, temp, temp_pos); + const char *start = temp.contents(); + const char *end = start + temp.length(); + if (n > 0) + first_part(n, start, end, result); + else if (n < 0) + last_part(-n, start, end, result); + } +} + +void alternative_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &pos) +{ + int start_length = result.length(); + if (expr1) + expr1->evaluate(tentative, ref, result, pos); + if (result.length() == start_length && expr2) + expr2->evaluate(tentative, ref, result, pos); +} + +void list_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &pos) +{ + if (expr1) + expr1->evaluate(tentative, ref, result, pos); + if (expr2) + expr2->evaluate(tentative, ref, result, pos); +} + +void substitute_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &pos) +{ + int start_length = result.length(); + if (expr1) + expr1->evaluate(tentative, ref, result, pos); + if (result.length() > start_length && result[result.length() - 1] == '-') { + // ought to see if pos covers the - + result.set_length(result.length() - 1); + if (expr2) + expr2->evaluate(tentative, ref, result, pos); + } +} + +void conditional_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &pos) +{ + string temp; + substring_position temp_pos; + if (expr1) + expr1->evaluate(tentative, ref, temp, temp_pos); + if (temp.length() > 0) { + if (expr2) + expr2->evaluate(tentative, ref, result, pos); + } + else { + if (expr3) + expr3->evaluate(tentative, ref, result, pos); + } +} + +void reference::pre_compute_label() +{ + if (parsed_label != 0 + && (parsed_label->analyze() & expression::CONTAINS_VARIABLE)) { + label.clear(); + substring_position temp_pos; + parsed_label->evaluate(1, *this, label, temp_pos); + label_ptr = lookup_label(label); + } +} + +void reference::compute_label() +{ + label.clear(); + if (parsed_label) + parsed_label->evaluate(0, *this, label, separator_pos); + if (short_label_flag && parsed_short_label) + parsed_short_label->evaluate(0, *this, short_label, short_separator_pos); + if (date_as_label) { + string new_date; + if (parsed_date_label) { + substring_position temp_pos; + parsed_date_label->evaluate(0, *this, new_date, temp_pos); + } + set_date(new_date); + } + if (label_ptr) + label_ptr->count += 1; +} + +void reference::immediate_compute_label() +{ + if (label_ptr) + label_ptr->total = 2; // force use of disambiguator + compute_label(); +} + +int reference::merge_labels(reference **v, int n, label_type type, + string &result) +{ + if (abbreviate_label_ranges) + return merge_labels_by_number(v, n, type, result); + else + return merge_labels_by_parts(v, n, type, result); +} + +int reference::merge_labels_by_number(reference **v, int n, label_type type, + string &result) +{ + if (n <= 1) + return 0; + int num = get_number(); + // Only merge three or more labels. + if (v[0]->get_number() != num + 1 + || v[1]->get_number() != num + 2) + return 0; + int i; + for (i = 2; i < n; i++) + if (v[i]->get_number() != num + i + 1) + break; + result = get_label(type); + result += label_range_indicator; + result += v[i - 1]->get_label(type); + return i; +} + +const substring_position &reference::get_separator_pos(label_type type) const +{ + if (type == SHORT_LABEL && short_label_flag) + return short_separator_pos; + else + return separator_pos; +} + +const string &reference::get_label(label_type type) const +{ + if (type == SHORT_LABEL && short_label_flag) + return short_label; + else + return label; +} + +int reference::merge_labels_by_parts(reference **v, int n, label_type type, + string &result) +{ + if (n <= 0) + return 0; + const string &lb = get_label(type); + const substring_position &sp = get_separator_pos(type); + if (sp.start < 0 + || sp.start != v[0]->get_separator_pos(type).start + || memcmp(lb.contents(), v[0]->get_label(type).contents(), + sp.start) != 0) + return 0; + result = lb; + int i = 0; + do { + result += separate_label_second_parts; + const substring_position &s = v[i]->get_separator_pos(type); + int sep_end_pos = s.start + s.length; + result.append(v[i]->get_label(type).contents() + sep_end_pos, + v[i]->get_label(type).length() - sep_end_pos); + } while (++i < n + && sp.start == v[i]->get_separator_pos(type).start + && memcmp(lb.contents(), v[i]->get_label(type).contents(), + sp.start) == 0); + return i; +} + +string label_pool; + +label_info::label_info(const string &s) +: start(label_pool.length()), length(s.length()), count(0), total(1) +{ + label_pool += s; +} + +static label_info **label_table = 0; +static int label_table_size = 0; +static int label_table_used = 0; + +label_info *lookup_label(const string &label) +{ + if (label_table == 0) { + label_table = new label_info *[17]; + label_table_size = 17; + for (int i = 0; i < 17; i++) + label_table[i] = 0; + } + unsigned h = hash_string(label.contents(), label.length()) % label_table_size; + label_info **ptr; + for (ptr = label_table + h; + *ptr != 0; + (ptr == label_table) + ? (ptr = label_table + label_table_size - 1) + : ptr--) + if ((*ptr)->length == label.length() + && memcmp(label_pool.contents() + (*ptr)->start, label.contents(), + label.length()) == 0) { + (*ptr)->total += 1; + return *ptr; + } + label_info *result = *ptr = new label_info(label); + if (++label_table_used * 2 > label_table_size) { + // Rehash the table. + label_info **old_table = label_table; + int old_size = label_table_size; + label_table_size = next_size(label_table_size); + label_table = new label_info *[label_table_size]; + int i; + for (i = 0; i < label_table_size; i++) + label_table[i] = 0; + for (i = 0; i < old_size; i++) + if (old_table[i]) { + unsigned h = hash_string(label_pool.contents() + old_table[i]->start, + old_table[i]->length); + label_info **p; + for (p = label_table + (h % label_table_size); + *p != 0; + (p == label_table) + ? (p = label_table + label_table_size - 1) + : --p) + ; + *p = old_table[i]; + } + a_delete old_table; + } + return result; +} + +void clear_labels() +{ + for (int i = 0; i < label_table_size; i++) { + delete label_table[i]; + label_table[i] = 0; + } + label_table_used = 0; + label_pool.clear(); +} + +static void consider_authors(reference **start, reference **end, int i); + +void compute_labels(reference **v, int n) +{ + if (parsed_label + && (parsed_label->analyze() & expression::CONTAINS_AT) + && sort_fields.length() >= 2 + && sort_fields[0] == 'A' + && sort_fields[1] == '+') + consider_authors(v, v + n, 0); + for (int i = 0; i < n; i++) + v[i]->compute_label(); +} + + +/* A reference with a list of authors <A0,A1,...,AN> _needs_ author i +where 0 <= i <= N if there exists a reference with a list of authors +<B0,B1,...,BM> such that <A0,A1,...,AN> != <B0,B1,...,BM> and M >= i +and Aj = Bj for 0 <= j < i. In this case if we can't say ``A0, +A1,...,A(i-1) et al'' because this would match both <A0,A1,...,AN> and +<B0,B1,...,BM>. If a reference needs author i we only have to call +need_author(j) for some j >= i such that the reference also needs +author j. */ + +/* This function handles 2 tasks: +determine which authors are needed (cannot be elided with et al.); +determine which authors can have only last names in the labels. + +References >= start and < end have the same first i author names. +Also they're sorted by A+. */ + +static void consider_authors(reference **start, reference **end, int i) +{ + if (start >= end) + return; + reference **p = start; + if (i >= (*p)->get_nauthors()) { + for (++p; p < end && i >= (*p)->get_nauthors(); p++) + ; + if (p < end && i > 0) { + // If we have an author list <A B C> and an author list <A B C D>, + // then both lists need C. + for (reference **q = start; q < end; q++) + (*q)->need_author(i - 1); + } + start = p; + } + while (p < end) { + reference **last_name_start = p; + reference **name_start = p; + for (++p; + p < end && i < (*p)->get_nauthors() + && same_author_last_name(**last_name_start, **p, i); + p++) { + if (!same_author_name(**name_start, **p, i)) { + consider_authors(name_start, p, i + 1); + name_start = p; + } + } + consider_authors(name_start, p, i + 1); + if (last_name_start == name_start) { + for (reference **q = last_name_start; q < p; q++) + (*q)->set_last_name_unambiguous(i); + } + // If we have an author list <A B C D> and <A B C E>, then the lists + // need author D and E respectively. + if (name_start > start || p < end) { + for (reference **q = last_name_start; q < p; q++) + (*q)->need_author(i); + } + } +} + +int same_author_last_name(const reference &r1, const reference &r2, int n) +{ + const char *ae1; + const char *as1 = r1.get_sort_field(0, n, 0, &ae1); + assert(as1 != 0); + const char *ae2; + const char *as2 = r2.get_sort_field(0, n, 0, &ae2); + assert(as2 != 0); + return ae1 - as1 == ae2 - as2 && memcmp(as1, as2, ae1 - as1) == 0; +} + +int same_author_name(const reference &r1, const reference &r2, int n) +{ + const char *ae1; + const char *as1 = r1.get_sort_field(0, n, -1, &ae1); + assert(as1 != 0); + const char *ae2; + const char *as2 = r2.get_sort_field(0, n, -1, &ae2); + assert(as2 != 0); + return ae1 - as1 == ae2 - as2 && memcmp(as1, as2, ae1 - as1) == 0; +} + + +void int_set::set(int i) +{ + assert(i >= 0); + int bytei = i >> 3; + if (bytei >= v.length()) { + int old_length = v.length(); + v.set_length(bytei + 1); + for (int j = old_length; j <= bytei; j++) + v[j] = 0; + } + v[bytei] |= 1 << (i & 7); +} + +int int_set::get(int i) const +{ + assert(i >= 0); + int bytei = i >> 3; + return bytei >= v.length() ? 0 : (v[bytei] & (1 << (i & 7))) != 0; +} + +void reference::set_last_name_unambiguous(int i) +{ + last_name_unambiguous.set(i); +} + +void reference::need_author(int n) +{ + if (n > last_needed_author) + last_needed_author = n; +} + +const char *reference::get_authors(const char **end) const +{ + if (!computed_authors) { + ((reference *)this)->computed_authors = 1; + string &result = ((reference *)this)->authors; + int na = get_nauthors(); + result.clear(); + for (int i = 0; i < na; i++) { + if (last_name_unambiguous.get(i)) { + const char *e, *start = get_author_last_name(i, &e); + assert(start != 0); + result.append(start, e - start); + } + else { + const char *e, *start = get_author(i, &e); + assert(start != 0); + result.append(start, e - start); + } + if (i == last_needed_author + && et_al.length() > 0 + && et_al_min_elide > 0 + && last_needed_author + et_al_min_elide < na + && na >= et_al_min_total) { + result += et_al; + break; + } + if (i < na - 1) { + if (na == 2) + result += join_authors_exactly_two; + else if (i < na - 2) + result += join_authors_default; + else + result += join_authors_last_two; + } + } + } + const char *start = authors.contents(); + *end = start + authors.length(); + return start; +} + +int reference::get_nauthors() const +{ + if (nauthors < 0) { + const char *dummy; + int na; + for (na = 0; get_author(na, &dummy) != 0; na++) + ; + ((reference *)this)->nauthors = na; + } + return nauthors; +} +#line 1228 "y.tab.c" +#define YYABORT goto yyabort +#define YYREJECT goto yyabort +#define YYACCEPT goto yyaccept +#define YYERROR goto yyerrlab +int +#if defined(__STDC__) +yyparse(void) +#else +yyparse() +#endif +{ + register int yym, yyn, yystate; +#if YYDEBUG + register char *yys; + extern char *getenv(); + + if (yys = getenv("YYDEBUG")) + { + yyn = *yys; + if (yyn >= '0' && yyn <= '9') + yydebug = yyn - '0'; + } +#endif + + yynerrs = 0; + yyerrflag = 0; + yychar = (-1); + + yyssp = yyss; + yyvsp = yyvs; + *yyssp = yystate = 0; + +yyloop: + if ((yyn = yydefred[yystate]) != 0) goto yyreduce; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + } + if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, shifting to state %d\n", + YYPREFIX, yystate, yytable[yyn]); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + yychar = (-1); + if (yyerrflag > 0) --yyerrflag; + goto yyloop; + } + if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { + yyn = yytable[yyn]; + goto yyreduce; + } + if (yyerrflag) goto yyinrecovery; + yyerror("syntax error"); +#ifdef lint + goto yyerrlab; +#endif +yyerrlab: + ++yynerrs; +yyinrecovery: + if (yyerrflag < 3) + { + yyerrflag = 3; + for (;;) + { + if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, error recovery shifting\ + to state %d\n", YYPREFIX, *yyssp, yytable[yyn]); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + goto yyloop; + } + else + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: error recovery discarding state %d\n", + YYPREFIX, *yyssp); +#endif + if (yyssp <= yyss) goto yyabort; + --yyssp; + --yyvsp; + } + } + } + else + { + if (yychar == 0) goto yyabort; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, error recovery discards token %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + yychar = (-1); + goto yyloop; + } +yyreduce: +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, reducing by rule %d (%s)\n", + YYPREFIX, yystate, yyn, yyrule[yyn]); +#endif + yym = yylen[yyn]; + yyval = yyvsp[1-yym]; + switch (yyn) + { +case 1: +#line 250 "label.y" +{ parse_result = (yyvsp[0].expr ? new analyzed_expr(yyvsp[0].expr) : 0); } +break; +case 2: +#line 255 "label.y" +{ yyval.expr = yyvsp[0].expr; } +break; +case 3: +#line 257 "label.y" +{ yyval.expr = new conditional_expr(yyvsp[-4].expr, yyvsp[-2].expr, yyvsp[0].expr); } +break; +case 4: +#line 262 "label.y" +{ yyval.expr = 0; } +break; +case 5: +#line 264 "label.y" +{ yyval.expr = yyvsp[0].expr; } +break; +case 6: +#line 269 "label.y" +{ yyval.expr = yyvsp[0].expr; } +break; +case 7: +#line 271 "label.y" +{ yyval.expr = new alternative_expr(yyvsp[-2].expr, yyvsp[0].expr); } +break; +case 8: +#line 273 "label.y" +{ yyval.expr = new conditional_expr(yyvsp[-2].expr, yyvsp[0].expr, 0); } +break; +case 9: +#line 278 "label.y" +{ yyval.expr = yyvsp[0].expr; } +break; +case 10: +#line 280 "label.y" +{ yyval.expr = new list_expr(yyvsp[-1].expr, yyvsp[0].expr); } +break; +case 11: +#line 285 "label.y" +{ yyval.expr = yyvsp[0].expr; } +break; +case 12: +#line 287 "label.y" +{ yyval.expr = new substitute_expr(yyvsp[-2].expr, yyvsp[0].expr); } +break; +case 13: +#line 292 "label.y" +{ yyval.expr = new at_expr; } +break; +case 14: +#line 294 "label.y" +{ + yyval.expr = new literal_expr(literals.contents() + yyvsp[0].str.start, + yyvsp[0].str.len); + } +break; +case 15: +#line 299 "label.y" +{ yyval.expr = new field_expr(yyvsp[0].num, 0); } +break; +case 16: +#line 301 "label.y" +{ yyval.expr = new field_expr(yyvsp[-1].num, yyvsp[0].num - 1); } +break; +case 17: +#line 303 "label.y" +{ + switch (yyvsp[0].num) { + case 'I': + case 'i': + case 'A': + case 'a': + yyval.expr = new format_expr(yyvsp[0].num); + break; + default: + command_error("unrecognized format `%1'", char(yyvsp[0].num)); + yyval.expr = new format_expr('a'); + break; + } + } +break; +case 18: +#line 319 "label.y" +{ + yyval.expr = new format_expr('0', yyvsp[0].dig.ndigits, yyvsp[0].dig.val); + } +break; +case 19: +#line 323 "label.y" +{ + switch (yyvsp[-1].num) { + case 'l': + yyval.expr = new map_expr(yyvsp[-4].expr, lowercase); + break; + case 'u': + yyval.expr = new map_expr(yyvsp[-4].expr, uppercase); + break; + case 'c': + yyval.expr = new map_expr(yyvsp[-4].expr, capitalize); + break; + case 'r': + yyval.expr = new map_expr(yyvsp[-4].expr, reverse_name); + break; + case 'a': + yyval.expr = new map_expr(yyvsp[-4].expr, abbreviate_name); + break; + case 'y': + yyval.expr = new extractor_expr(yyvsp[-4].expr, find_year, yyvsp[-2].num); + break; + case 'n': + yyval.expr = new extractor_expr(yyvsp[-4].expr, find_last_name, yyvsp[-2].num); + break; + default: + yyval.expr = yyvsp[-4].expr; + command_error("unknown function `%1'", char(yyvsp[-1].num)); + break; + } + } +break; +case 20: +#line 354 "label.y" +{ yyval.expr = new truncate_expr(yyvsp[-2].expr, yyvsp[0].num); } +break; +case 21: +#line 356 "label.y" +{ yyval.expr = new truncate_expr(yyvsp[-2].expr, -yyvsp[0].num); } +break; +case 22: +#line 358 "label.y" +{ yyval.expr = new star_expr(yyvsp[-1].expr); } +break; +case 23: +#line 360 "label.y" +{ yyval.expr = yyvsp[-1].expr; } +break; +case 24: +#line 362 "label.y" +{ yyval.expr = new separator_expr(yyvsp[-1].expr); } +break; +case 25: +#line 367 "label.y" +{ yyval.num = -1; } +break; +case 26: +#line 369 "label.y" +{ yyval.num = yyvsp[0].num; } +break; +case 27: +#line 374 "label.y" +{ yyval.num = yyvsp[0].num; } +break; +case 28: +#line 376 "label.y" +{ yyval.num = yyvsp[-1].num*10 + yyvsp[0].num; } +break; +case 29: +#line 381 "label.y" +{ yyval.dig.ndigits = 1; yyval.dig.val = yyvsp[0].num; } +break; +case 30: +#line 383 "label.y" +{ yyval.dig.ndigits = yyvsp[-1].dig.ndigits + 1; yyval.dig.val = yyvsp[-1].dig.val*10 + yyvsp[0].num; } +break; +case 31: +#line 389 "label.y" +{ yyval.num = 0; } +break; +case 32: +#line 391 "label.y" +{ yyval.num = 1; } +break; +case 33: +#line 393 "label.y" +{ yyval.num = -1; } +break; +#line 1547 "y.tab.c" + } + yyssp -= yym; + yystate = *yyssp; + yyvsp -= yym; + yym = yylhs[yyn]; + if (yystate == 0 && yym == 0) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state 0 to\ + state %d\n", YYPREFIX, YYFINAL); +#endif + yystate = YYFINAL; + *++yyssp = YYFINAL; + *++yyvsp = yyval; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, YYFINAL, yychar, yys); + } +#endif + } + if (yychar == 0) goto yyaccept; + goto yyloop; + } + if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yystate) + yystate = yytable[yyn]; + else + yystate = yydgoto[yym]; +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state %d \ +to state %d\n", YYPREFIX, *yyssp, yystate); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate; + *++yyvsp = yyval; + goto yyloop; +yyoverflow: + yyerror("yacc stack overflow"); +yyabort: + return (1); +yyaccept: + return (0); +} diff --git a/contrib/groff/src/preproc/refer/label.y b/contrib/groff/src/preproc/refer/label.y new file mode 100644 index 0000000..2648b98 --- /dev/null +++ b/contrib/groff/src/preproc/refer/label.y @@ -0,0 +1,1177 @@ +/* -*- C++ -*- + Copyright (C) 1989, 1990, 1991, 1992, 2000 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 "refer.h" +#include "refid.h" +#include "ref.h" +#include "token.h" + +int yylex(); +void yyerror(const char *); +int yyparse(); + +static const char *format_serial(char c, int n); + +struct label_info { + int start; + int length; + int count; + int total; + label_info(const string &); +}; + +label_info *lookup_label(const string &label); + +struct expression { + enum { + // Does the tentative label depend on the reference? + CONTAINS_VARIABLE = 01, + CONTAINS_STAR = 02, + CONTAINS_FORMAT = 04, + CONTAINS_AT = 010 + }; + virtual ~expression() { } + virtual void evaluate(int, const reference &, string &, + substring_position &) = 0; + virtual unsigned analyze() { return 0; } +}; + +class at_expr : public expression { +public: + at_expr() { } + void evaluate(int, const reference &, string &, substring_position &); + unsigned analyze() { return CONTAINS_VARIABLE|CONTAINS_AT; } +}; + +class format_expr : public expression { + char type; + int width; + int first_number; +public: + format_expr(char c, int w = 0, int f = 1) + : type(c), width(w), first_number(f) { } + void evaluate(int, const reference &, string &, substring_position &); + unsigned analyze() { return CONTAINS_FORMAT; } +}; + +class field_expr : public expression { + int number; + char name; +public: + field_expr(char nm, int num) : number(num), name(nm) { } + void evaluate(int, const reference &, string &, substring_position &); + unsigned analyze() { return CONTAINS_VARIABLE; } +}; + +class literal_expr : public expression { + string s; +public: + literal_expr(const char *ptr, int len) : s(ptr, len) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +class unary_expr : public expression { +protected: + expression *expr; +public: + unary_expr(expression *e) : expr(e) { } + ~unary_expr() { delete expr; } + void evaluate(int, const reference &, string &, substring_position &) = 0; + unsigned analyze() { return expr ? expr->analyze() : 0; } +}; + +// This caches the analysis of an expression. + +class analyzed_expr : public unary_expr { + unsigned flags; +public: + analyzed_expr(expression *); + void evaluate(int, const reference &, string &, substring_position &); + unsigned analyze() { return flags; } +}; + +class star_expr : public unary_expr { +public: + star_expr(expression *e) : unary_expr(e) { } + void evaluate(int, const reference &, string &, substring_position &); + unsigned analyze() { + return ((expr ? (expr->analyze() & ~CONTAINS_VARIABLE) : 0) + | CONTAINS_STAR); + } +}; + +typedef void map_func(const char *, const char *, string &); + +class map_expr : public unary_expr { + map_func *func; +public: + map_expr(expression *e, map_func *f) : unary_expr(e), func(f) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +typedef const char *extractor_func(const char *, const char *, const char **); + +class extractor_expr : public unary_expr { + int part; + extractor_func *func; +public: + enum { BEFORE = +1, MATCH = 0, AFTER = -1 }; + extractor_expr(expression *e, extractor_func *f, int pt) + : unary_expr(e), part(pt), func(f) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +class truncate_expr : public unary_expr { + int n; +public: + truncate_expr(expression *e, int i) : unary_expr(e), n(i) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +class separator_expr : public unary_expr { +public: + separator_expr(expression *e) : unary_expr(e) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +class binary_expr : public expression { +protected: + expression *expr1; + expression *expr2; +public: + binary_expr(expression *e1, expression *e2) : expr1(e1), expr2(e2) { } + ~binary_expr() { delete expr1; delete expr2; } + void evaluate(int, const reference &, string &, substring_position &) = 0; + unsigned analyze() { + return (expr1 ? expr1->analyze() : 0) | (expr2 ? expr2->analyze() : 0); + } +}; + +class alternative_expr : public binary_expr { +public: + alternative_expr(expression *e1, expression *e2) : binary_expr(e1, e2) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +class list_expr : public binary_expr { +public: + list_expr(expression *e1, expression *e2) : binary_expr(e1, e2) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +class substitute_expr : public binary_expr { +public: + substitute_expr(expression *e1, expression *e2) : binary_expr(e1, e2) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +class ternary_expr : public expression { +protected: + expression *expr1; + expression *expr2; + expression *expr3; +public: + ternary_expr(expression *e1, expression *e2, expression *e3) + : expr1(e1), expr2(e2), expr3(e3) { } + ~ternary_expr() { delete expr1; delete expr2; delete expr3; } + void evaluate(int, const reference &, string &, substring_position &) = 0; + unsigned analyze() { + return ((expr1 ? expr1->analyze() : 0) + | (expr2 ? expr2->analyze() : 0) + | (expr3 ? expr3->analyze() : 0)); + } +}; + +class conditional_expr : public ternary_expr { +public: + conditional_expr(expression *e1, expression *e2, expression *e3) + : ternary_expr(e1, e2, e3) { } + void evaluate(int, const reference &, string &, substring_position &); +}; + +static expression *parsed_label = 0; +static expression *parsed_date_label = 0; +static expression *parsed_short_label = 0; + +static expression *parse_result; + +string literals; + +%} + +%union { + int num; + expression *expr; + struct { int ndigits; int val; } dig; + struct { int start; int len; } str; +} + +/* uppercase or lowercase letter */ +%token <num> TOKEN_LETTER +/* literal characters */ +%token <str> TOKEN_LITERAL +/* digit */ +%token <num> TOKEN_DIGIT + +%type <expr> conditional +%type <expr> alternative +%type <expr> list +%type <expr> string +%type <expr> substitute +%type <expr> optional_conditional +%type <num> number +%type <dig> digits +%type <num> optional_number +%type <num> flag + +%% + +expr: + optional_conditional + { parse_result = ($1 ? new analyzed_expr($1) : 0); } + ; + +conditional: + alternative + { $$ = $1; } + | alternative '?' optional_conditional ':' conditional + { $$ = new conditional_expr($1, $3, $5); } + ; + +optional_conditional: + /* empty */ + { $$ = 0; } + | conditional + { $$ = $1; } + ; + +alternative: + list + { $$ = $1; } + | alternative '|' list + { $$ = new alternative_expr($1, $3); } + | alternative '&' list + { $$ = new conditional_expr($1, $3, 0); } + ; + +list: + substitute + { $$ = $1; } + | list substitute + { $$ = new list_expr($1, $2); } + ; + +substitute: + string + { $$ = $1; } + | substitute '~' string + { $$ = new substitute_expr($1, $3); } + ; + +string: + '@' + { $$ = new at_expr; } + | TOKEN_LITERAL + { + $$ = new literal_expr(literals.contents() + $1.start, + $1.len); + } + | TOKEN_LETTER + { $$ = new field_expr($1, 0); } + | TOKEN_LETTER number + { $$ = new field_expr($1, $2 - 1); } + | '%' TOKEN_LETTER + { + switch ($2) { + case 'I': + case 'i': + case 'A': + case 'a': + $$ = new format_expr($2); + break; + default: + command_error("unrecognized format `%1'", char($2)); + $$ = new format_expr('a'); + break; + } + } + + | '%' digits + { + $$ = new format_expr('0', $2.ndigits, $2.val); + } + | string '.' flag TOKEN_LETTER optional_number + { + switch ($4) { + case 'l': + $$ = new map_expr($1, lowercase); + break; + case 'u': + $$ = new map_expr($1, uppercase); + break; + case 'c': + $$ = new map_expr($1, capitalize); + break; + case 'r': + $$ = new map_expr($1, reverse_name); + break; + case 'a': + $$ = new map_expr($1, abbreviate_name); + break; + case 'y': + $$ = new extractor_expr($1, find_year, $3); + break; + case 'n': + $$ = new extractor_expr($1, find_last_name, $3); + break; + default: + $$ = $1; + command_error("unknown function `%1'", char($4)); + break; + } + } + + | string '+' number + { $$ = new truncate_expr($1, $3); } + | string '-' number + { $$ = new truncate_expr($1, -$3); } + | string '*' + { $$ = new star_expr($1); } + | '(' optional_conditional ')' + { $$ = $2; } + | '<' optional_conditional '>' + { $$ = new separator_expr($2); } + ; + +optional_number: + /* empty */ + { $$ = -1; } + | number + { $$ = $1; } + ; + +number: + TOKEN_DIGIT + { $$ = $1; } + | number TOKEN_DIGIT + { $$ = $1*10 + $2; } + ; + +digits: + TOKEN_DIGIT + { $$.ndigits = 1; $$.val = $1; } + | digits TOKEN_DIGIT + { $$.ndigits = $1.ndigits + 1; $$.val = $1.val*10 + $2; } + ; + + +flag: + /* empty */ + { $$ = 0; } + | '+' + { $$ = 1; } + | '-' + { $$ = -1; } + ; + +%% + +/* bison defines const to be empty unless __STDC__ is defined, which it +isn't under cfront */ + +#ifdef const +#undef const +#endif + +const char *spec_ptr; +const char *spec_end; +const char *spec_cur; + +int yylex() +{ + while (spec_ptr < spec_end && csspace(*spec_ptr)) + spec_ptr++; + spec_cur = spec_ptr; + if (spec_ptr >= spec_end) + return 0; + unsigned char c = *spec_ptr++; + if (csalpha(c)) { + yylval.num = c; + return TOKEN_LETTER; + } + if (csdigit(c)) { + yylval.num = c - '0'; + return TOKEN_DIGIT; + } + if (c == '\'') { + yylval.str.start = literals.length(); + for (; spec_ptr < spec_end; spec_ptr++) { + if (*spec_ptr == '\'') { + if (++spec_ptr < spec_end && *spec_ptr == '\'') + literals += '\''; + else { + yylval.str.len = literals.length() - yylval.str.start; + return TOKEN_LITERAL; + } + } + else + literals += *spec_ptr; + } + yylval.str.len = literals.length() - yylval.str.start; + return TOKEN_LITERAL; + } + return c; +} + +int set_label_spec(const char *label_spec) +{ + spec_cur = spec_ptr = label_spec; + spec_end = strchr(label_spec, '\0'); + literals.clear(); + if (yyparse()) + return 0; + delete parsed_label; + parsed_label = parse_result; + return 1; +} + +int set_date_label_spec(const char *label_spec) +{ + spec_cur = spec_ptr = label_spec; + spec_end = strchr(label_spec, '\0'); + literals.clear(); + if (yyparse()) + return 0; + delete parsed_date_label; + parsed_date_label = parse_result; + return 1; +} + +int set_short_label_spec(const char *label_spec) +{ + spec_cur = spec_ptr = label_spec; + spec_end = strchr(label_spec, '\0'); + literals.clear(); + if (yyparse()) + return 0; + delete parsed_short_label; + parsed_short_label = parse_result; + return 1; +} + +void yyerror(const char *message) +{ + if (spec_cur < spec_end) + command_error("label specification %1 before `%2'", message, spec_cur); + else + command_error("label specification %1 at end of string", + message, spec_cur); +} + +void at_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &) +{ + if (tentative) + ref.canonicalize_authors(result); + else { + const char *end, *start = ref.get_authors(&end); + if (start) + result.append(start, end - start); + } +} + +void format_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &) +{ + if (tentative) + return; + const label_info *lp = ref.get_label_ptr(); + int num = lp == 0 ? ref.get_number() : lp->count; + if (type != '0') + result += format_serial(type, num + 1); + else { + const char *ptr = i_to_a(num + first_number); + int pad = width - strlen(ptr); + while (--pad >= 0) + result += '0'; + result += ptr; + } +} + +static const char *format_serial(char c, int n) +{ + assert(n > 0); + static char buf[128]; // more than enough. + switch (c) { + case 'i': + case 'I': + { + char *p = buf; + // troff uses z and w to represent 10000 and 5000 in Roman + // numerals; I can find no historical basis for this usage + const char *s = c == 'i' ? "zwmdclxvi" : "ZWMDCLXVI"; + if (n >= 40000) + return i_to_a(n); + while (n >= 10000) { + *p++ = s[0]; + n -= 10000; + } + for (int i = 1000; i > 0; i /= 10, s += 2) { + int m = n/i; + n -= m*i; + switch (m) { + case 3: + *p++ = s[2]; + /* falls through */ + case 2: + *p++ = s[2]; + /* falls through */ + case 1: + *p++ = s[2]; + break; + case 4: + *p++ = s[2]; + *p++ = s[1]; + break; + case 8: + *p++ = s[1]; + *p++ = s[2]; + *p++ = s[2]; + *p++ = s[2]; + break; + case 7: + *p++ = s[1]; + *p++ = s[2]; + *p++ = s[2]; + break; + case 6: + *p++ = s[1]; + *p++ = s[2]; + break; + case 5: + *p++ = s[1]; + break; + case 9: + *p++ = s[2]; + *p++ = s[0]; + } + } + *p = 0; + break; + } + case 'a': + case 'A': + { + char *p = buf; + // this is derived from troff/reg.c + while (n > 0) { + int d = n % 26; + if (d == 0) + d = 26; + n -= d; + n /= 26; + *p++ = c + d - 1; // ASCII dependent + } + *p-- = 0; + // Reverse it. + char *q = buf; + while (q < p) { + char temp = *q; + *q = *p; + *p = temp; + --p; + ++q; + } + break; + } + default: + assert(0); + } + return buf; +} + +void field_expr::evaluate(int, const reference &ref, + string &result, substring_position &) +{ + const char *end; + const char *start = ref.get_field(name, &end); + if (start) { + start = nth_field(number, start, &end); + if (start) + result.append(start, end - start); + } +} + +void literal_expr::evaluate(int, const reference &, + string &result, substring_position &) +{ + result += s; +} + +analyzed_expr::analyzed_expr(expression *e) +: unary_expr(e), flags(e ? e->analyze() : 0) +{ +} + +void analyzed_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &pos) +{ + if (expr) + expr->evaluate(tentative, ref, result, pos); +} + +void star_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &pos) +{ + const label_info *lp = ref.get_label_ptr(); + if (!tentative + && (lp == 0 || lp->total > 1) + && expr) + expr->evaluate(tentative, ref, result, pos); +} + +void separator_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &pos) +{ + int start_length = result.length(); + int is_first = pos.start < 0; + if (expr) + expr->evaluate(tentative, ref, result, pos); + if (is_first) { + pos.start = start_length; + pos.length = result.length() - start_length; + } +} + +void map_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &) +{ + if (expr) { + string temp; + substring_position temp_pos; + expr->evaluate(tentative, ref, temp, temp_pos); + (*func)(temp.contents(), temp.contents() + temp.length(), result); + } +} + +void extractor_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &) +{ + if (expr) { + string temp; + substring_position temp_pos; + expr->evaluate(tentative, ref, temp, temp_pos); + const char *end, *start = (*func)(temp.contents(), + temp.contents() + temp.length(), + &end); + switch (part) { + case BEFORE: + if (start) + result.append(temp.contents(), start - temp.contents()); + else + result += temp; + break; + case MATCH: + if (start) + result.append(start, end - start); + break; + case AFTER: + if (start) + result.append(end, temp.contents() + temp.length() - end); + break; + default: + assert(0); + } + } +} + +static void first_part(int len, const char *ptr, const char *end, + string &result) +{ + for (;;) { + const char *token_start = ptr; + if (!get_token(&ptr, end)) + break; + const token_info *ti = lookup_token(token_start, ptr); + int counts = ti->sortify_non_empty(token_start, ptr); + if (counts && --len < 0) + break; + if (counts || ti->is_accent()) + result.append(token_start, ptr - token_start); + } +} + +static void last_part(int len, const char *ptr, const char *end, + string &result) +{ + const char *start = ptr; + int count = 0; + for (;;) { + const char *token_start = ptr; + if (!get_token(&ptr, end)) + break; + const token_info *ti = lookup_token(token_start, ptr); + if (ti->sortify_non_empty(token_start, ptr)) + count++; + } + ptr = start; + int skip = count - len; + if (skip > 0) { + for (;;) { + const char *token_start = ptr; + if (!get_token(&ptr, end)) + assert(0); + const token_info *ti = lookup_token(token_start, ptr); + if (ti->sortify_non_empty(token_start, ptr) && --skip < 0) { + ptr = token_start; + break; + } + } + } + first_part(len, ptr, end, result); +} + +void truncate_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &) +{ + if (expr) { + string temp; + substring_position temp_pos; + expr->evaluate(tentative, ref, temp, temp_pos); + const char *start = temp.contents(); + const char *end = start + temp.length(); + if (n > 0) + first_part(n, start, end, result); + else if (n < 0) + last_part(-n, start, end, result); + } +} + +void alternative_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &pos) +{ + int start_length = result.length(); + if (expr1) + expr1->evaluate(tentative, ref, result, pos); + if (result.length() == start_length && expr2) + expr2->evaluate(tentative, ref, result, pos); +} + +void list_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &pos) +{ + if (expr1) + expr1->evaluate(tentative, ref, result, pos); + if (expr2) + expr2->evaluate(tentative, ref, result, pos); +} + +void substitute_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &pos) +{ + int start_length = result.length(); + if (expr1) + expr1->evaluate(tentative, ref, result, pos); + if (result.length() > start_length && result[result.length() - 1] == '-') { + // ought to see if pos covers the - + result.set_length(result.length() - 1); + if (expr2) + expr2->evaluate(tentative, ref, result, pos); + } +} + +void conditional_expr::evaluate(int tentative, const reference &ref, + string &result, substring_position &pos) +{ + string temp; + substring_position temp_pos; + if (expr1) + expr1->evaluate(tentative, ref, temp, temp_pos); + if (temp.length() > 0) { + if (expr2) + expr2->evaluate(tentative, ref, result, pos); + } + else { + if (expr3) + expr3->evaluate(tentative, ref, result, pos); + } +} + +void reference::pre_compute_label() +{ + if (parsed_label != 0 + && (parsed_label->analyze() & expression::CONTAINS_VARIABLE)) { + label.clear(); + substring_position temp_pos; + parsed_label->evaluate(1, *this, label, temp_pos); + label_ptr = lookup_label(label); + } +} + +void reference::compute_label() +{ + label.clear(); + if (parsed_label) + parsed_label->evaluate(0, *this, label, separator_pos); + if (short_label_flag && parsed_short_label) + parsed_short_label->evaluate(0, *this, short_label, short_separator_pos); + if (date_as_label) { + string new_date; + if (parsed_date_label) { + substring_position temp_pos; + parsed_date_label->evaluate(0, *this, new_date, temp_pos); + } + set_date(new_date); + } + if (label_ptr) + label_ptr->count += 1; +} + +void reference::immediate_compute_label() +{ + if (label_ptr) + label_ptr->total = 2; // force use of disambiguator + compute_label(); +} + +int reference::merge_labels(reference **v, int n, label_type type, + string &result) +{ + if (abbreviate_label_ranges) + return merge_labels_by_number(v, n, type, result); + else + return merge_labels_by_parts(v, n, type, result); +} + +int reference::merge_labels_by_number(reference **v, int n, label_type type, + string &result) +{ + if (n <= 1) + return 0; + int num = get_number(); + // Only merge three or more labels. + if (v[0]->get_number() != num + 1 + || v[1]->get_number() != num + 2) + return 0; + int i; + for (i = 2; i < n; i++) + if (v[i]->get_number() != num + i + 1) + break; + result = get_label(type); + result += label_range_indicator; + result += v[i - 1]->get_label(type); + return i; +} + +const substring_position &reference::get_separator_pos(label_type type) const +{ + if (type == SHORT_LABEL && short_label_flag) + return short_separator_pos; + else + return separator_pos; +} + +const string &reference::get_label(label_type type) const +{ + if (type == SHORT_LABEL && short_label_flag) + return short_label; + else + return label; +} + +int reference::merge_labels_by_parts(reference **v, int n, label_type type, + string &result) +{ + if (n <= 0) + return 0; + const string &lb = get_label(type); + const substring_position &sp = get_separator_pos(type); + if (sp.start < 0 + || sp.start != v[0]->get_separator_pos(type).start + || memcmp(lb.contents(), v[0]->get_label(type).contents(), + sp.start) != 0) + return 0; + result = lb; + int i = 0; + do { + result += separate_label_second_parts; + const substring_position &s = v[i]->get_separator_pos(type); + int sep_end_pos = s.start + s.length; + result.append(v[i]->get_label(type).contents() + sep_end_pos, + v[i]->get_label(type).length() - sep_end_pos); + } while (++i < n + && sp.start == v[i]->get_separator_pos(type).start + && memcmp(lb.contents(), v[i]->get_label(type).contents(), + sp.start) == 0); + return i; +} + +string label_pool; + +label_info::label_info(const string &s) +: start(label_pool.length()), length(s.length()), count(0), total(1) +{ + label_pool += s; +} + +static label_info **label_table = 0; +static int label_table_size = 0; +static int label_table_used = 0; + +label_info *lookup_label(const string &label) +{ + if (label_table == 0) { + label_table = new label_info *[17]; + label_table_size = 17; + for (int i = 0; i < 17; i++) + label_table[i] = 0; + } + unsigned h = hash_string(label.contents(), label.length()) % label_table_size; + label_info **ptr; + for (ptr = label_table + h; + *ptr != 0; + (ptr == label_table) + ? (ptr = label_table + label_table_size - 1) + : ptr--) + if ((*ptr)->length == label.length() + && memcmp(label_pool.contents() + (*ptr)->start, label.contents(), + label.length()) == 0) { + (*ptr)->total += 1; + return *ptr; + } + label_info *result = *ptr = new label_info(label); + if (++label_table_used * 2 > label_table_size) { + // Rehash the table. + label_info **old_table = label_table; + int old_size = label_table_size; + label_table_size = next_size(label_table_size); + label_table = new label_info *[label_table_size]; + int i; + for (i = 0; i < label_table_size; i++) + label_table[i] = 0; + for (i = 0; i < old_size; i++) + if (old_table[i]) { + unsigned h = hash_string(label_pool.contents() + old_table[i]->start, + old_table[i]->length); + label_info **p; + for (p = label_table + (h % label_table_size); + *p != 0; + (p == label_table) + ? (p = label_table + label_table_size - 1) + : --p) + ; + *p = old_table[i]; + } + a_delete old_table; + } + return result; +} + +void clear_labels() +{ + for (int i = 0; i < label_table_size; i++) { + delete label_table[i]; + label_table[i] = 0; + } + label_table_used = 0; + label_pool.clear(); +} + +static void consider_authors(reference **start, reference **end, int i); + +void compute_labels(reference **v, int n) +{ + if (parsed_label + && (parsed_label->analyze() & expression::CONTAINS_AT) + && sort_fields.length() >= 2 + && sort_fields[0] == 'A' + && sort_fields[1] == '+') + consider_authors(v, v + n, 0); + for (int i = 0; i < n; i++) + v[i]->compute_label(); +} + + +/* A reference with a list of authors <A0,A1,...,AN> _needs_ author i +where 0 <= i <= N if there exists a reference with a list of authors +<B0,B1,...,BM> such that <A0,A1,...,AN> != <B0,B1,...,BM> and M >= i +and Aj = Bj for 0 <= j < i. In this case if we can't say ``A0, +A1,...,A(i-1) et al'' because this would match both <A0,A1,...,AN> and +<B0,B1,...,BM>. If a reference needs author i we only have to call +need_author(j) for some j >= i such that the reference also needs +author j. */ + +/* This function handles 2 tasks: +determine which authors are needed (cannot be elided with et al.); +determine which authors can have only last names in the labels. + +References >= start and < end have the same first i author names. +Also they're sorted by A+. */ + +static void consider_authors(reference **start, reference **end, int i) +{ + if (start >= end) + return; + reference **p = start; + if (i >= (*p)->get_nauthors()) { + for (++p; p < end && i >= (*p)->get_nauthors(); p++) + ; + if (p < end && i > 0) { + // If we have an author list <A B C> and an author list <A B C D>, + // then both lists need C. + for (reference **q = start; q < end; q++) + (*q)->need_author(i - 1); + } + start = p; + } + while (p < end) { + reference **last_name_start = p; + reference **name_start = p; + for (++p; + p < end && i < (*p)->get_nauthors() + && same_author_last_name(**last_name_start, **p, i); + p++) { + if (!same_author_name(**name_start, **p, i)) { + consider_authors(name_start, p, i + 1); + name_start = p; + } + } + consider_authors(name_start, p, i + 1); + if (last_name_start == name_start) { + for (reference **q = last_name_start; q < p; q++) + (*q)->set_last_name_unambiguous(i); + } + // If we have an author list <A B C D> and <A B C E>, then the lists + // need author D and E respectively. + if (name_start > start || p < end) { + for (reference **q = last_name_start; q < p; q++) + (*q)->need_author(i); + } + } +} + +int same_author_last_name(const reference &r1, const reference &r2, int n) +{ + const char *ae1; + const char *as1 = r1.get_sort_field(0, n, 0, &ae1); + assert(as1 != 0); + const char *ae2; + const char *as2 = r2.get_sort_field(0, n, 0, &ae2); + assert(as2 != 0); + return ae1 - as1 == ae2 - as2 && memcmp(as1, as2, ae1 - as1) == 0; +} + +int same_author_name(const reference &r1, const reference &r2, int n) +{ + const char *ae1; + const char *as1 = r1.get_sort_field(0, n, -1, &ae1); + assert(as1 != 0); + const char *ae2; + const char *as2 = r2.get_sort_field(0, n, -1, &ae2); + assert(as2 != 0); + return ae1 - as1 == ae2 - as2 && memcmp(as1, as2, ae1 - as1) == 0; +} + + +void int_set::set(int i) +{ + assert(i >= 0); + int bytei = i >> 3; + if (bytei >= v.length()) { + int old_length = v.length(); + v.set_length(bytei + 1); + for (int j = old_length; j <= bytei; j++) + v[j] = 0; + } + v[bytei] |= 1 << (i & 7); +} + +int int_set::get(int i) const +{ + assert(i >= 0); + int bytei = i >> 3; + return bytei >= v.length() ? 0 : (v[bytei] & (1 << (i & 7))) != 0; +} + +void reference::set_last_name_unambiguous(int i) +{ + last_name_unambiguous.set(i); +} + +void reference::need_author(int n) +{ + if (n > last_needed_author) + last_needed_author = n; +} + +const char *reference::get_authors(const char **end) const +{ + if (!computed_authors) { + ((reference *)this)->computed_authors = 1; + string &result = ((reference *)this)->authors; + int na = get_nauthors(); + result.clear(); + for (int i = 0; i < na; i++) { + if (last_name_unambiguous.get(i)) { + const char *e, *start = get_author_last_name(i, &e); + assert(start != 0); + result.append(start, e - start); + } + else { + const char *e, *start = get_author(i, &e); + assert(start != 0); + result.append(start, e - start); + } + if (i == last_needed_author + && et_al.length() > 0 + && et_al_min_elide > 0 + && last_needed_author + et_al_min_elide < na + && na >= et_al_min_total) { + result += et_al; + break; + } + if (i < na - 1) { + if (na == 2) + result += join_authors_exactly_two; + else if (i < na - 2) + result += join_authors_default; + else + result += join_authors_last_two; + } + } + } + const char *start = authors.contents(); + *end = start + authors.length(); + return start; +} + +int reference::get_nauthors() const +{ + if (nauthors < 0) { + const char *dummy; + int na; + for (na = 0; get_author(na, &dummy) != 0; na++) + ; + ((reference *)this)->nauthors = na; + } + return nauthors; +} diff --git a/contrib/groff/src/preproc/refer/ref.cc b/contrib/groff/src/preproc/refer/ref.cc new file mode 100644 index 0000000..c3517b1 --- /dev/null +++ b/contrib/groff/src/preproc/refer/ref.cc @@ -0,0 +1,1160 @@ +// -*- 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 "refer.h" +#include "refid.h" +#include "ref.h" +#include "token.h" + +static const char *find_day(const char *, const char *, const char **); +static int find_month(const char *start, const char *end); +static void abbreviate_names(string &); + +#define DEFAULT_ARTICLES "the\000a\000an" + +string articles(DEFAULT_ARTICLES, sizeof(DEFAULT_ARTICLES)); + +// Multiple occurrences of fields are separated by FIELD_SEPARATOR. +const char FIELD_SEPARATOR = '\0'; + +const char MULTI_FIELD_NAMES[] = "AE"; +const char *AUTHOR_FIELDS = "AQ"; + +enum { OTHER, JOURNAL_ARTICLE, BOOK, ARTICLE_IN_BOOK, TECH_REPORT, BELL_TM }; + +const char *reference_types[] = { + "other", + "journal-article", + "book", + "article-in-book", + "tech-report", + "bell-tm", +}; + +static string temp_fields[256]; + +reference::reference(const char *start, int len, reference_id *ridp) +: h(0), merged(0), no(-1), field(0), nfields(0), label_ptr(0), + computed_authors(0), last_needed_author(-1), nauthors(-1) +{ + int i; + for (i = 0; i < 256; i++) + field_index[i] = NULL_FIELD_INDEX; + if (ridp) + rid = *ridp; + if (start == 0) + return; + if (len <= 0) + return; + const char *end = start + len; + const char *ptr = start; + assert(*ptr == '%'); + while (ptr < end) { + if (ptr + 1 < end && ptr[1] != '\0' + && ((ptr[1] != '%' && ptr[1] == annotation_field) + || (ptr + 2 < end && ptr[1] == '%' && ptr[2] != '\0' + && discard_fields.search(ptr[2]) < 0))) { + if (ptr[1] == '%') + ptr++; + string &f = temp_fields[(unsigned char)ptr[1]]; + ptr += 2; + while (ptr < end && csspace(*ptr)) + ptr++; + for (;;) { + for (;;) { + if (ptr >= end) { + f += '\n'; + break; + } + f += *ptr; + if (*ptr++ == '\n') + break; + } + if (ptr >= end || *ptr == '%') + break; + } + } + else if (ptr + 1 < end && ptr[1] != '\0' && ptr[1] != '%' + && discard_fields.search(ptr[1]) < 0) { + string &f = temp_fields[(unsigned char)ptr[1]]; + if (f.length() > 0) { + if (strchr(MULTI_FIELD_NAMES, ptr[1]) != 0) + f += FIELD_SEPARATOR; + else + f.clear(); + } + ptr += 2; + if (ptr < end) { + if (*ptr == ' ') + ptr++; + for (;;) { + const char *p = ptr; + while (ptr < end && *ptr != '\n') + ptr++; + // strip trailing white space + const char *q = ptr; + while (q > p && q[-1] != '\n' && csspace(q[-1])) + q--; + while (p < q) + f += *p++; + if (ptr >= end) + break; + ptr++; + if (ptr >= end) + break; + if (*ptr == '%') + break; + f += ' '; + } + } + } + else { + // skip this field + for (;;) { + while (ptr < end && *ptr++ != '\n') + ; + if (ptr >= end || *ptr == '%') + break; + } + } + } + for (i = 0; i < 256; i++) + if (temp_fields[i].length() > 0) + nfields++; + field = new string[nfields]; + int j = 0; + for (i = 0; i < 256; i++) + if (temp_fields[i].length() > 0) { + field[j].move(temp_fields[i]); + if (abbreviate_fields.search(i) >= 0) + abbreviate_names(field[j]); + field_index[i] = j; + j++; + } +} + +reference::~reference() +{ + if (nfields > 0) + ad_delete(nfields) field; +} + +// ref is the inline, this is the database ref + +void reference::merge(reference &ref) +{ + int i; + for (i = 0; i < 256; i++) + if (field_index[i] != NULL_FIELD_INDEX) + temp_fields[i].move(field[field_index[i]]); + for (i = 0; i < 256; i++) + if (ref.field_index[i] != NULL_FIELD_INDEX) + temp_fields[i].move(ref.field[ref.field_index[i]]); + for (i = 0; i < 256; i++) + field_index[i] = NULL_FIELD_INDEX; + int old_nfields = nfields; + nfields = 0; + for (i = 0; i < 256; i++) + if (temp_fields[i].length() > 0) + nfields++; + if (nfields != old_nfields) { + if (old_nfields > 0) + ad_delete(old_nfields) field; + field = new string[nfields]; + } + int j = 0; + for (i = 0; i < 256; i++) + if (temp_fields[i].length() > 0) { + field[j].move(temp_fields[i]); + field_index[i] = j; + j++; + } + merged = 1; +} + +void reference::insert_field(unsigned char c, string &s) +{ + assert(s.length() > 0); + if (field_index[c] != NULL_FIELD_INDEX) { + field[field_index[c]].move(s); + return; + } + assert(field_index[c] == NULL_FIELD_INDEX); + string *old_field = field; + field = new string[nfields + 1]; + int pos = 0; + int i; + for (i = 0; i < int(c); i++) + if (field_index[i] != NULL_FIELD_INDEX) + pos++; + for (i = 0; i < pos; i++) + field[i].move(old_field[i]); + field[pos].move(s); + for (i = pos; i < nfields; i++) + field[i + 1].move(old_field[i]); + if (nfields > 0) + ad_delete(nfields) old_field; + nfields++; + field_index[c] = pos; + for (i = c + 1; i < 256; i++) + if (field_index[i] != NULL_FIELD_INDEX) + field_index[i] += 1; +} + +void reference::delete_field(unsigned char c) +{ + if (field_index[c] == NULL_FIELD_INDEX) + return; + string *old_field = field; + field = new string[nfields - 1]; + int i; + for (i = 0; i < int(field_index[c]); i++) + field[i].move(old_field[i]); + for (i = field_index[c]; i < nfields - 1; i++) + field[i].move(old_field[i + 1]); + if (nfields > 0) + ad_delete(nfields) old_field; + nfields--; + field_index[c] = NULL_FIELD_INDEX; + for (i = c + 1; i < 256; i++) + if (field_index[i] != NULL_FIELD_INDEX) + field_index[i] -= 1; +} + +void reference::compute_hash_code() +{ + if (!rid.is_null()) + h = rid.hash(); + else { + h = 0; + for (int i = 0; i < nfields; i++) + if (field[i].length() > 0) { + h <<= 4; + h ^= hash_string(field[i].contents(), field[i].length()); + } + } +} + +void reference::set_number(int n) +{ + no = n; +} + +const char SORT_SEP = '\001'; +const char SORT_SUB_SEP = '\002'; +const char SORT_SUB_SUB_SEP = '\003'; + +// sep specifies additional word separators + +void sortify_words(const char *s, const char *end, const char *sep, + string &result) +{ + int non_empty = 0; + int need_separator = 0; + for (;;) { + const char *token_start = s; + if (!get_token(&s, end)) + break; + if ((s - token_start == 1 + && (*token_start == ' ' + || *token_start == '\n' + || (sep && *token_start != '\0' + && strchr(sep, *token_start) != 0))) + || (s - token_start == 2 + && token_start[0] == '\\' && token_start[1] == ' ')) { + if (non_empty) + need_separator = 1; + } + else { + const token_info *ti = lookup_token(token_start, s); + if (ti->sortify_non_empty(token_start, s)) { + if (need_separator) { + result += ' '; + need_separator = 0; + } + ti->sortify(token_start, s, result); + non_empty = 1; + } + } + } +} + +void sortify_word(const char *s, const char *end, string &result) +{ + for (;;) { + const char *token_start = s; + if (!get_token(&s, end)) + break; + const token_info *ti = lookup_token(token_start, s); + ti->sortify(token_start, s, result); + } +} + +void sortify_other(const char *s, int len, string &key) +{ + sortify_words(s, s + len, 0, key); +} + +void sortify_title(const char *s, int len, string &key) +{ + const char *end = s + len; + for (; s < end && (*s == ' ' || *s == '\n'); s++) + ; + const char *ptr = s; + for (;;) { + const char *token_start = ptr; + if (!get_token(&ptr, end)) + break; + if (ptr - token_start == 1 + && (*token_start == ' ' || *token_start == '\n')) + break; + } + if (ptr < end) { + int first_word_len = ptr - s - 1; + const char *ae = articles.contents() + articles.length(); + for (const char *a = articles.contents(); + a < ae; + a = strchr(a, '\0') + 1) + if (first_word_len == strlen(a)) { + int j; + for (j = 0; j < first_word_len; j++) + if (a[j] != cmlower(s[j])) + break; + if (j >= first_word_len) { + s = ptr; + for (; s < end && (*s == ' ' || *s == '\n'); s++) + ; + break; + } + } + } + sortify_words(s, end, 0, key); +} + +void sortify_name(const char *s, int len, string &key) +{ + const char *last_name_end; + const char *last_name = find_last_name(s, s + len, &last_name_end); + sortify_word(last_name, last_name_end, key); + key += SORT_SUB_SUB_SEP; + if (last_name > s) + sortify_words(s, last_name, ".", key); + key += SORT_SUB_SUB_SEP; + if (last_name_end < s + len) + sortify_words(last_name_end, s + len, ".,", key); +} + +void sortify_date(const char *s, int len, string &key) +{ + const char *year_end; + const char *year_start = find_year(s, s + len, &year_end); + if (!year_start) { + // Things without years are often `forthcoming', so it makes sense + // that they sort after things with explicit years. + key += 'A'; + sortify_words(s, s + len, 0, key); + return; + } + int n = year_end - year_start; + while (n < 4) { + key += '0'; + n++; + } + while (year_start < year_end) + key += *year_start++; + int m = find_month(s, s + len); + if (m < 0) + return; + key += 'A' + m; + const char *day_end; + const char *day_start = find_day(s, s + len, &day_end); + if (!day_start) + return; + if (day_end - day_start == 1) + key += '0'; + while (day_start < day_end) + key += *day_start++; +} + +// SORT_{SUB,SUB_SUB}_SEP can creep in from use of @ in label specification. + +void sortify_label(const char *s, int len, string &key) +{ + const char *end = s + len; + for (;;) { + const char *ptr; + for (ptr = s; + ptr < end && *ptr != SORT_SUB_SEP && *ptr != SORT_SUB_SUB_SEP; + ptr++) + ; + if (ptr > s) + sortify_words(s, ptr, 0, key); + s = ptr; + if (s >= end) + break; + key += *s++; + } +} + +void reference::compute_sort_key() +{ + if (sort_fields.length() == 0) + return; + sort_fields += '\0'; + const char *sf = sort_fields.contents(); + while (*sf != '\0') { + if (sf > sort_fields) + sort_key += SORT_SEP; + char f = *sf++; + int n = 1; + if (*sf == '+') { + n = INT_MAX; + sf++; + } + else if (csdigit(*sf)) { + char *ptr; + long l = strtol(sf, &ptr, 10); + if (l == 0 && ptr == sf) + ; + else { + sf = ptr; + if (l < 0) { + n = 1; + } + else { + n = int(l); + } + } + } + if (f == '.') + sortify_label(label.contents(), label.length(), sort_key); + else if (f == AUTHOR_FIELDS[0]) + sortify_authors(n, sort_key); + else + sortify_field(f, n, sort_key); + } + sort_fields.set_length(sort_fields.length() - 1); +} + +void reference::sortify_authors(int n, string &result) const +{ + for (const char *p = AUTHOR_FIELDS; *p != '\0'; p++) + if (contains_field(*p)) { + sortify_field(*p, n, result); + return; + } + sortify_field(AUTHOR_FIELDS[0], n, result); +} + +void reference::canonicalize_authors(string &result) const +{ + int len = result.length(); + sortify_authors(INT_MAX, result); + if (result.length() > len) + result += SORT_SUB_SEP; +} + +void reference::sortify_field(unsigned char f, int n, string &result) const +{ + typedef void (*sortify_t)(const char *, int, string &); + sortify_t sortifier = sortify_other; + switch (f) { + case 'A': + case 'E': + sortifier = sortify_name; + break; + case 'D': + sortifier = sortify_date; + break; + case 'B': + case 'J': + case 'T': + sortifier = sortify_title; + break; + } + int fi = field_index[(unsigned char)f]; + if (fi != NULL_FIELD_INDEX) { + string &str = field[fi]; + const char *start = str.contents(); + const char *end = start + str.length(); + for (int i = 0; i < n && start < end; i++) { + const char *p = start; + while (start < end && *start != FIELD_SEPARATOR) + start++; + if (i > 0) + result += SORT_SUB_SEP; + (*sortifier)(p, start - p, result); + if (start < end) + start++; + } + } +} + +int compare_reference(const reference &r1, const reference &r2) +{ + assert(r1.no >= 0); + assert(r2.no >= 0); + const char *s1 = r1.sort_key.contents(); + int n1 = r1.sort_key.length(); + const char *s2 = r2.sort_key.contents(); + int n2 = r2.sort_key.length(); + for (; n1 > 0 && n2 > 0; --n1, --n2, ++s1, ++s2) + if (*s1 != *s2) + return (int)(unsigned char)*s1 - (int)(unsigned char)*s2; + if (n2 > 0) + return -1; + if (n1 > 0) + return 1; + return r1.no - r2.no; +} + +int same_reference(const reference &r1, const reference &r2) +{ + if (!r1.rid.is_null() && r1.rid == r2.rid) + return 1; + if (r1.h != r2.h) + return 0; + if (r1.nfields != r2.nfields) + return 0; + int i = 0; + for (i = 0; i < 256; i++) + if (r1.field_index != r2.field_index) + return 0; + for (i = 0; i < r1.nfields; i++) + if (r1.field[i] != r2.field[i]) + return 0; + return 1; +} + +const char *find_last_name(const char *start, const char *end, + const char **endp) +{ + const char *ptr = start; + const char *last_word = start; + for (;;) { + const char *token_start = ptr; + if (!get_token(&ptr, end)) + break; + if (ptr - token_start == 1) { + if (*token_start == ',') { + *endp = token_start; + return last_word; + } + else if (*token_start == ' ' || *token_start == '\n') { + if (ptr < end && *ptr != ' ' && *ptr != '\n') + last_word = ptr; + } + } + } + *endp = end; + return last_word; +} + +void abbreviate_name(const char *ptr, const char *end, string &result) +{ + const char *last_name_end; + const char *last_name_start = find_last_name(ptr, end, &last_name_end); + int need_period = 0; + for (;;) { + const char *token_start = ptr; + if (!get_token(&ptr, last_name_start)) + break; + const token_info *ti = lookup_token(token_start, ptr); + if (need_period) { + if ((ptr - token_start == 1 && *token_start == ' ') + || (ptr - token_start == 2 && token_start[0] == '\\' + && token_start[1] == ' ')) + continue; + if (ti->is_upper()) + result += period_before_initial; + else + result += period_before_other; + need_period = 0; + } + result.append(token_start, ptr - token_start); + if (ti->is_upper()) { + const char *lower_ptr = ptr; + int first_token = 1; + for (;;) { + token_start = ptr; + if (!get_token(&ptr, last_name_start)) + break; + if ((ptr - token_start == 1 && *token_start == ' ') + || (ptr - token_start == 2 && token_start[0] == '\\' + && token_start[1] == ' ')) + break; + ti = lookup_token(token_start, ptr); + if (ti->is_hyphen()) { + const char *ptr1 = ptr; + if (get_token(&ptr1, last_name_start)) { + ti = lookup_token(ptr, ptr1); + if (ti->is_upper()) { + result += period_before_hyphen; + result.append(token_start, ptr1 - token_start); + ptr = ptr1; + } + } + } + else if (ti->is_upper()) { + // MacDougal -> MacD. + result.append(lower_ptr, ptr - lower_ptr); + lower_ptr = ptr; + first_token = 1; + } + else if (first_token && ti->is_accent()) { + result.append(token_start, ptr - token_start); + lower_ptr = ptr; + } + first_token = 0; + } + need_period = 1; + } + } + if (need_period) + result += period_before_last_name; + result.append(last_name_start, end - last_name_start); +} + +static void abbreviate_names(string &result) +{ + string str; + str.move(result); + const char *ptr = str.contents(); + const char *end = ptr + str.length(); + while (ptr < end) { + const char *name_end = (char *)memchr(ptr, FIELD_SEPARATOR, end - ptr); + if (name_end == 0) + name_end = end; + abbreviate_name(ptr, name_end, result); + if (name_end >= end) + break; + ptr = name_end + 1; + result += FIELD_SEPARATOR; + } +} + +void reverse_name(const char *ptr, const char *name_end, string &result) +{ + const char *last_name_end; + const char *last_name_start = find_last_name(ptr, name_end, &last_name_end); + result.append(last_name_start, last_name_end - last_name_start); + while (last_name_start > ptr + && (last_name_start[-1] == ' ' || last_name_start[-1] == '\n')) + last_name_start--; + if (last_name_start > ptr) { + result += ", "; + result.append(ptr, last_name_start - ptr); + } + if (last_name_end < name_end) + result.append(last_name_end, name_end - last_name_end); +} + +void reverse_names(string &result, int n) +{ + if (n <= 0) + return; + string str; + str.move(result); + const char *ptr = str.contents(); + const char *end = ptr + str.length(); + while (ptr < end) { + if (--n < 0) { + result.append(ptr, end - ptr); + break; + } + const char *name_end = (char *)memchr(ptr, FIELD_SEPARATOR, end - ptr); + if (name_end == 0) + name_end = end; + reverse_name(ptr, name_end, result); + if (name_end >= end) + break; + ptr = name_end + 1; + result += FIELD_SEPARATOR; + } +} + +// Return number of field separators. + +int join_fields(string &f) +{ + const char *ptr = f.contents(); + int len = f.length(); + int nfield_seps = 0; + int j; + for (j = 0; j < len; j++) + if (ptr[j] == FIELD_SEPARATOR) + nfield_seps++; + if (nfield_seps == 0) + return 0; + string temp; + int field_seps_left = nfield_seps; + for (j = 0; j < len; j++) { + if (ptr[j] == FIELD_SEPARATOR) { + if (nfield_seps == 1) + temp += join_authors_exactly_two; + else if (--field_seps_left == 0) + temp += join_authors_last_two; + else + temp += join_authors_default; + } + else + temp += ptr[j]; + } + f = temp; + return nfield_seps; +} + +void uppercase(const char *start, const char *end, string &result) +{ + for (;;) { + const char *token_start = start; + if (!get_token(&start, end)) + break; + const token_info *ti = lookup_token(token_start, start); + ti->upper_case(token_start, start, result); + } +} + +void lowercase(const char *start, const char *end, string &result) +{ + for (;;) { + const char *token_start = start; + if (!get_token(&start, end)) + break; + const token_info *ti = lookup_token(token_start, start); + ti->lower_case(token_start, start, result); + } +} + +void capitalize(const char *ptr, const char *end, string &result) +{ + int in_small_point_size = 0; + for (;;) { + const char *start = ptr; + if (!get_token(&ptr, end)) + break; + const token_info *ti = lookup_token(start, ptr); + const char *char_end = ptr; + int is_lower = ti->is_lower(); + if ((is_lower || ti->is_upper()) && get_token(&ptr, end)) { + const token_info *ti2 = lookup_token(char_end, ptr); + if (!ti2->is_accent()) + ptr = char_end; + } + if (is_lower) { + if (!in_small_point_size) { + result += "\\s-2"; + in_small_point_size = 1; + } + ti->upper_case(start, char_end, result); + result.append(char_end, ptr - char_end); + } + else { + if (in_small_point_size) { + result += "\\s+2"; + in_small_point_size = 0; + } + result.append(start, ptr - start); + } + } + if (in_small_point_size) + result += "\\s+2"; +} + +void capitalize_field(string &str) +{ + string temp; + capitalize(str.contents(), str.contents() + str.length(), temp); + str.move(temp); +} + +int is_terminated(const char *ptr, const char *end) +{ + const char *last_token = end; + for (;;) { + const char *p = ptr; + if (!get_token(&ptr, end)) + break; + last_token = p; + } + return end - last_token == 1 + && (*last_token == '.' || *last_token == '!' || *last_token == '?'); +} + +void reference::output(FILE *fp) +{ + fputs(".]-\n", fp); + for (int i = 0; i < 256; i++) + if (field_index[i] != NULL_FIELD_INDEX && i != annotation_field) { + string &f = field[field_index[i]]; + if (!csdigit(i)) { + int j = reverse_fields.search(i); + if (j >= 0) { + int n; + int len = reverse_fields.length(); + if (++j < len && csdigit(reverse_fields[j])) { + n = reverse_fields[j] - '0'; + for (++j; j < len && csdigit(reverse_fields[j]); j++) + // should check for overflow + n = n*10 + reverse_fields[j] - '0'; + } + else + n = INT_MAX; + reverse_names(f, n); + } + } + int is_multiple = join_fields(f) > 0; + if (capitalize_fields.search(i) >= 0) + capitalize_field(f); + if (memchr(f.contents(), '\n', f.length()) == 0) { + fprintf(fp, ".ds [%c ", i); + if (f[0] == ' ' || f[0] == '\\' || f[0] == '"') + putc('"', fp); + put_string(f, fp); + putc('\n', fp); + } + else { + fprintf(fp, ".de [%c\n", i); + put_string(f, fp); + fputs("..\n", fp); + } + if (i == 'P') { + int multiple_pages = 0; + const char *s = f.contents(); + const char *end = f.contents() + f.length(); + for (;;) { + const char *token_start = s; + if (!get_token(&s, end)) + break; + const token_info *ti = lookup_token(token_start, s); + if (ti->is_hyphen() || ti->is_range_sep()) { + multiple_pages = 1; + break; + } + } + fprintf(fp, ".nr [P %d\n", multiple_pages); + } + else if (i == 'E') + fprintf(fp, ".nr [E %d\n", is_multiple); + } + for (const char *p = "TAO"; *p; p++) { + int fi = field_index[(unsigned char)*p]; + if (fi != NULL_FIELD_INDEX) { + string &f = field[fi]; + fprintf(fp, ".nr [%c %d\n", *p, + is_terminated(f.contents(), f.contents() + f.length())); + } + } + int t = classify(); + fprintf(fp, ".][ %d %s\n", t, reference_types[t]); + if (annotation_macro.length() > 0 && annotation_field >= 0 + && field_index[annotation_field] != NULL_FIELD_INDEX) { + putc('.', fp); + put_string(annotation_macro, fp); + putc('\n', fp); + put_string(field[field_index[annotation_field]], fp); + } +} + +void reference::print_sort_key_comment(FILE *fp) +{ + fputs(".\\\"", fp); + put_string(sort_key, fp); + putc('\n', fp); +} + +const char *find_year(const char *start, const char *end, const char **endp) +{ + for (;;) { + while (start < end && !csdigit(*start)) + start++; + const char *ptr = start; + if (start == end) + break; + while (ptr < end && csdigit(*ptr)) + ptr++; + if (ptr - start == 4 || ptr - start == 3 + || (ptr - start == 2 + && (start[0] >= '4' || (start[0] == '3' && start[1] >= '2')))) { + *endp = ptr; + return start; + } + start = ptr; + } + return 0; +} + +static const char *find_day(const char *start, const char *end, + const char **endp) +{ + for (;;) { + while (start < end && !csdigit(*start)) + start++; + const char *ptr = start; + if (start == end) + break; + while (ptr < end && csdigit(*ptr)) + ptr++; + if ((ptr - start == 1 && start[0] != '0') + || (ptr - start == 2 && + (start[0] == '1' + || start[0] == '2' + || (start[0] == '3' && start[1] <= '1') + || (start[0] == '0' && start[1] != '0')))) { + *endp = ptr; + return start; + } + start = ptr; + } + return 0; +} + +static int find_month(const char *start, const char *end) +{ + static const char *months[] = { + "january", + "february", + "march", + "april", + "may", + "june", + "july", + "august", + "september", + "october", + "november", + "december", + }; + for (;;) { + while (start < end && !csalpha(*start)) + start++; + const char *ptr = start; + if (start == end) + break; + while (ptr < end && csalpha(*ptr)) + ptr++; + if (ptr - start >= 3) { + for (int i = 0; i < sizeof(months)/sizeof(months[0]); i++) { + const char *q = months[i]; + const char *p = start; + for (; p < ptr; p++, q++) + if (cmlower(*p) != *q) + break; + if (p >= ptr) + return i; + } + } + start = ptr; + } + return -1; +} + +int reference::contains_field(char c) const +{ + return field_index[(unsigned char)c] != NULL_FIELD_INDEX; +} + +int reference::classify() +{ + if (contains_field('J')) + return JOURNAL_ARTICLE; + if (contains_field('B')) + return ARTICLE_IN_BOOK; + if (contains_field('G')) + return TECH_REPORT; + if (contains_field('R')) + return TECH_REPORT; + if (contains_field('I')) + return BOOK; + if (contains_field('M')) + return BELL_TM; + return OTHER; +} + +const char *reference::get_year(const char **endp) const +{ + if (field_index['D'] != NULL_FIELD_INDEX) { + string &date = field[field_index['D']]; + const char *start = date.contents(); + const char *end = start + date.length(); + return find_year(start, end, endp); + } + else + return 0; +} + +const char *reference::get_field(unsigned char c, const char **endp) const +{ + if (field_index[c] != NULL_FIELD_INDEX) { + string &f = field[field_index[c]]; + const char *start = f.contents(); + *endp = start + f.length(); + return start; + } + else + return 0; +} + +const char *reference::get_date(const char **endp) const +{ + return get_field('D', endp); +} + +const char *nth_field(int i, const char *start, const char **endp) +{ + while (--i >= 0) { + start = (char *)memchr(start, FIELD_SEPARATOR, *endp - start); + if (!start) + return 0; + start++; + } + const char *e = (char *)memchr(start, FIELD_SEPARATOR, *endp - start); + if (e) + *endp = e; + return start; +} + +const char *reference::get_author(int i, const char **endp) const +{ + for (const char *f = AUTHOR_FIELDS; *f != '\0'; f++) { + const char *start = get_field(*f, endp); + if (start) { + if (strchr(MULTI_FIELD_NAMES, *f) != 0) + return nth_field(i, start, endp); + else if (i == 0) + return start; + else + return 0; + } + } + return 0; +} + +const char *reference::get_author_last_name(int i, const char **endp) const +{ + for (const char *f = AUTHOR_FIELDS; *f != '\0'; f++) { + const char *start = get_field(*f, endp); + if (start) { + if (strchr(MULTI_FIELD_NAMES, *f) != 0) { + start = nth_field(i, start, endp); + if (!start) + return 0; + } + if (*f == 'A') + return find_last_name(start, *endp, endp); + else + return start; + } + } + return 0; +} + +void reference::set_date(string &d) +{ + if (d.length() == 0) + delete_field('D'); + else + insert_field('D', d); +} + +int same_year(const reference &r1, const reference &r2) +{ + const char *ye1; + const char *ys1 = r1.get_year(&ye1); + const char *ye2; + const char *ys2 = r2.get_year(&ye2); + if (ys1 == 0) { + if (ys2 == 0) + return same_date(r1, r2); + else + return 0; + } + else if (ys2 == 0) + return 0; + else if (ye1 - ys1 != ye2 - ys2) + return 0; + else + return memcmp(ys1, ys2, ye1 - ys1) == 0; +} + +int same_date(const reference &r1, const reference &r2) +{ + const char *e1; + const char *s1 = r1.get_date(&e1); + const char *e2; + const char *s2 = r2.get_date(&e2); + if (s1 == 0) + return s2 == 0; + else if (s2 == 0) + return 0; + else if (e1 - s1 != e2 - s2) + return 0; + else + return memcmp(s1, s2, e1 - s1) == 0; +} + +const char *reference::get_sort_field(int i, int si, int ssi, + const char **endp) const +{ + const char *start = sort_key.contents(); + const char *end = start + sort_key.length(); + if (i < 0) { + *endp = end; + return start; + } + while (--i >= 0) { + start = (char *)memchr(start, SORT_SEP, end - start); + if (!start) + return 0; + start++; + } + const char *e = (char *)memchr(start, SORT_SEP, end - start); + if (e) + end = e; + if (si < 0) { + *endp = end; + return start; + } + while (--si >= 0) { + start = (char *)memchr(start, SORT_SUB_SEP, end - start); + if (!start) + return 0; + start++; + } + e = (char *)memchr(start, SORT_SUB_SEP, end - start); + if (e) + end = e; + if (ssi < 0) { + *endp = end; + return start; + } + while (--ssi >= 0) { + start = (char *)memchr(start, SORT_SUB_SUB_SEP, end - start); + if (!start) + return 0; + start++; + } + e = (char *)memchr(start, SORT_SUB_SUB_SEP, end - start); + if (e) + end = e; + *endp = end; + return start; +} + diff --git a/contrib/groff/src/preproc/refer/ref.h b/contrib/groff/src/preproc/refer/ref.h new file mode 100644 index 0000000..13a984a --- /dev/null +++ b/contrib/groff/src/preproc/refer/ref.h @@ -0,0 +1,120 @@ +// -*- 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. */ + +struct label_info; + +enum label_type { NORMAL_LABEL, SHORT_LABEL }; +const int N_LABEL_TYPES = 2; + +struct substring_position { + int start; + int length; + substring_position() : start(-1) { } +}; + +class int_set { + string v; +public: + int_set() { } + void set(int i); + int get(int i) const; +}; + +class reference { +private: + unsigned h; + reference_id rid; + int merged; + string sort_key; + int no; + string *field; + int nfields; + unsigned char field_index[256]; + enum { NULL_FIELD_INDEX = 255 }; + string label; + substring_position separator_pos; + string short_label; + substring_position short_separator_pos; + label_info *label_ptr; + string authors; + int computed_authors; + int last_needed_author; + int nauthors; + int_set last_name_unambiguous; + + int contains_field(char) const; + void insert_field(unsigned char, string &s); + void delete_field(unsigned char); + void set_date(string &); + const char *get_sort_field(int i, int si, int ssi, const char **endp) const; + int merge_labels_by_parts(reference **, int, label_type, string &); + int merge_labels_by_number(reference **, int, label_type, string &); +public: + reference(const char * = 0, int = -1, reference_id * = 0); + ~reference(); + void output(FILE *); + void print_sort_key_comment(FILE *); + void set_number(int); + int get_number() const { return no; } + unsigned hash() const { return h; } + const string &get_label(label_type type) const; + const substring_position &get_separator_pos(label_type) const; + int is_merged() const { return merged; } + void compute_sort_key(); + void compute_hash_code(); + void pre_compute_label(); + void compute_label(); + void immediate_compute_label(); + int classify(); + void merge(reference &); + int merge_labels(reference **, int, label_type, string &); + int get_nauthors() const; + void need_author(int); + void set_last_name_unambiguous(int); + void sortify_authors(int, string &) const; + void canonicalize_authors(string &) const; + void sortify_field(unsigned char, int, string &) const; + const char *get_author(int, const char **) const; + const char *get_author_last_name(int, const char **) const; + const char *get_date(const char **) const; + const char *get_year(const char **) const; + const char *get_field(unsigned char, const char **) const; + const label_info *get_label_ptr() const { return label_ptr; } + const char *get_authors(const char **) const; + // for sorting + friend int compare_reference(const reference &r1, const reference &r2); + // for merging + friend int same_reference(const reference &, const reference &); + friend int same_year(const reference &, const reference &); + friend int same_date(const reference &, const reference &); + friend int same_author_last_name(const reference &, const reference &, int); + friend int same_author_name(const reference &, const reference &, int); +}; + +const char *find_year(const char *, const char *, const char **); +const char *find_last_name(const char *, const char *, const char **); + +const char *nth_field(int i, const char *start, const char **endp); + +void capitalize(const char *ptr, const char *end, string &result); +void reverse_name(const char *ptr, const char *end, string &result); +void uppercase(const char *ptr, const char *end, string &result); +void lowercase(const char *ptr, const char *end, string &result); +void abbreviate_name(const char *ptr, const char *end, string &result); diff --git a/contrib/groff/src/preproc/refer/refer.cc b/contrib/groff/src/preproc/refer/refer.cc new file mode 100644 index 0000000..b6cefc5 --- /dev/null +++ b/contrib/groff/src/preproc/refer/refer.cc @@ -0,0 +1,1234 @@ +// -*- C++ -*- +/* Copyright (C) 1989-1992, 2000, 2001 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 "refer.h" +#include "refid.h" +#include "ref.h" +#include "token.h" +#include "search.h" +#include "command.h" + +const char PRE_LABEL_MARKER = '\013'; +const char POST_LABEL_MARKER = '\014'; +const char LABEL_MARKER = '\015'; // label_type is added on + +#define FORCE_LEFT_BRACKET 04 +#define FORCE_RIGHT_BRACKET 010 + +static FILE *outfp = stdout; + +string capitalize_fields; +string reverse_fields; +string abbreviate_fields; +string period_before_last_name = ". "; +string period_before_initial = "."; +string period_before_hyphen = ""; +string period_before_other = ". "; +string sort_fields; +int annotation_field = -1; +string annotation_macro; +string discard_fields = "XYZ"; +string pre_label = "\\*([."; +string post_label = "\\*(.]"; +string sep_label = ", "; +int accumulate = 0; +int move_punctuation = 0; +int abbreviate_label_ranges = 0; +string label_range_indicator; +int label_in_text = 1; +int label_in_reference = 1; +int date_as_label = 0; +int sort_adjacent_labels = 0; +// Join exactly two authors with this. +string join_authors_exactly_two = " and "; +// When there are more than two authors join the last two with this. +string join_authors_last_two = ", and "; +// Otherwise join authors with this. +string join_authors_default = ", "; +string separate_label_second_parts = ", "; +// Use this string to represent that there are other authors. +string et_al = " et al"; +// Use et al only if it can replace at least this many authors. +int et_al_min_elide = 2; +// Use et al only if the total number of authors is at least this. +int et_al_min_total = 3; + + +int compatible_flag = 0; + +int short_label_flag = 0; + +static int recognize_R1_R2 = 1; + +search_list database_list; +int search_default = 1; +static int default_database_loaded = 0; + +static reference **citation = 0; +static int ncitations = 0; +static int citation_max = 0; + +static reference **reference_hash_table = 0; +static int hash_table_size; +static int nreferences = 0; + +static int need_syncing = 0; +string pending_line; +string pending_lf_lines; + +static void output_pending_line(); +static unsigned immediately_handle_reference(const string &); +static void immediately_output_references(); +static unsigned store_reference(const string &); +static void divert_to_temporary_file(); +static reference *make_reference(const string &, unsigned *); +static void usage(FILE *stream); +static void do_file(const char *); +static void split_punct(string &line, string &punct); +static void output_citation_group(reference **v, int n, label_type, FILE *fp); +static void possibly_load_default_database(); + +int main(int argc, char **argv) +{ + program_name = argv[0]; + static char stderr_buf[BUFSIZ]; + setbuf(stderr, stderr_buf); + outfp = stdout; + int finished_options = 0; + int bib_flag = 0; + int done_spec = 0; + + for (--argc, ++argv; + !finished_options && argc > 0 && argv[0][0] == '-' + && argv[0][1] != '\0'; + argv++, argc--) { + const char *opt = argv[0] + 1; + while (opt != 0 && *opt != '\0') { + switch (*opt) { + case 'C': + compatible_flag = 1; + opt++; + break; + case 'B': + bib_flag = 1; + label_in_reference = 0; + label_in_text = 0; + ++opt; + if (*opt == '\0') { + annotation_field = 'X'; + annotation_macro = "AP"; + } + else if (csalnum(opt[0]) && opt[1] == '.' && opt[2] != '\0') { + annotation_field = opt[0]; + annotation_macro = opt + 2; + } + opt = 0; + break; + case 'P': + move_punctuation = 1; + opt++; + break; + case 'R': + recognize_R1_R2 = 0; + opt++; + break; + case 'S': + // Not a very useful spec. + set_label_spec("(A.n|Q)', '(D.y|D)"); + done_spec = 1; + pre_label = " ("; + post_label = ")"; + sep_label = "; "; + opt++; + break; + case 'V': + verify_flag = 1; + opt++; + break; + case 'f': + { + const char *num = 0; + if (*++opt == '\0') { + if (argc > 1) { + num = *++argv; + --argc; + } + else { + error("option `f' requires an argument"); + usage(stderr); + exit(1); + } + } + else { + num = opt; + opt = 0; + } + const char *ptr; + for (ptr = num; *ptr; ptr++) + if (!csdigit(*ptr)) { + error("bad character `%1' in argument to -f option", *ptr); + break; + } + if (*ptr == '\0') { + string spec; + spec = '%'; + spec += num; + spec += '\0'; + set_label_spec(spec.contents()); + done_spec = 1; + } + break; + } + case 'b': + label_in_text = 0; + label_in_reference = 0; + opt++; + break; + case 'e': + accumulate = 1; + opt++; + break; + case 'c': + capitalize_fields = ++opt; + opt = 0; + break; + case 'k': + { + char buf[5]; + if (csalpha(*++opt)) + buf[0] = *opt++; + else { + if (*opt != '\0') + error("bad field name `%1'", *opt++); + buf[0] = 'L'; + } + buf[1] = '~'; + buf[2] = '%'; + buf[3] = 'a'; + buf[4] = '\0'; + set_label_spec(buf); + done_spec = 1; + } + break; + case 'a': + { + const char *ptr; + for (ptr = ++opt; *ptr; ptr++) + if (!csdigit(*ptr)) { + error("argument to `a' option not a number"); + break; + } + if (*ptr == '\0') { + reverse_fields = 'A'; + reverse_fields += opt; + } + opt = 0; + } + break; + case 'i': + linear_ignore_fields = ++opt; + opt = 0; + break; + case 'l': + { + char buf[INT_DIGITS*2 + 11]; // A.n+2D.y-3%a + strcpy(buf, "A.n"); + if (*++opt != '\0' && *opt != ',') { + char *ptr; + long n = strtol(opt, &ptr, 10); + if (n == 0 && ptr == opt) { + error("bad integer `%1' in `l' option", opt); + opt = 0; + break; + } + if (n < 0) + n = 0; + opt = ptr; + sprintf(strchr(buf, '\0'), "+%ld", n); + } + strcat(buf, "D.y"); + if (*opt == ',') + opt++; + if (*opt != '\0') { + char *ptr; + long n = strtol(opt, &ptr, 10); + if (n == 0 && ptr == opt) { + error("bad integer `%1' in `l' option", opt); + opt = 0; + break; + } + if (n < 0) + n = 0; + sprintf(strchr(buf, '\0'), "-%ld", n); + opt = ptr; + if (*opt != '\0') + error("argument to `l' option not of form `m,n'"); + } + strcat(buf, "%a"); + if (!set_label_spec(buf)) + assert(0); + done_spec = 1; + } + break; + case 'n': + search_default = 0; + opt++; + break; + case 'p': + { + const char *filename = 0; + if (*++opt == '\0') { + if (argc > 1) { + filename = *++argv; + argc--; + } + else { + error("option `p' requires an argument"); + usage(stderr); + exit(1); + } + } + else { + filename = opt; + opt = 0; + } + database_list.add_file(filename); + } + break; + case 's': + if (*++opt == '\0') + sort_fields = "AD"; + else { + sort_fields = opt; + opt = 0; + } + accumulate = 1; + break; + case 't': + { + char *ptr; + long n = strtol(opt, &ptr, 10); + if (n == 0 && ptr == opt) { + error("bad integer `%1' in `t' option", opt); + opt = 0; + break; + } + if (n < 1) + n = 1; + linear_truncate_len = int(n); + opt = ptr; + break; + } + case '-': + if (opt[1] == '\0') { + finished_options = 1; + opt++; + break; + } + if (strcmp(opt,"-version")==0) { + case 'v': + extern const char *Version_string; + printf("GNU refer (groff) version %s\n", Version_string); + exit(0); + break; + } + if (strcmp(opt,"-help")==0) { + usage(stdout); + exit(0); + break; + } + // fall through + default: + error("unrecognized option `%1'", *opt); + usage(stderr); + exit(1); + break; + } + } + } + if (!done_spec) + set_label_spec("%1"); + if (argc <= 0) { + if (bib_flag) + do_bib("-"); + else + do_file("-"); + } + else { + for (int i = 0; i < argc; i++) { + if (bib_flag) + do_bib(argv[i]); + else + do_file(argv[i]); + } + } + if (accumulate) + output_references(); + if (fflush(stdout) < 0) + fatal("output error"); + return 0; +} + +static void usage(FILE *stream) +{ + fprintf(stream, +"usage: %s [-benvCPRS] [-aN] [-cXYZ] [-fN] [-iXYZ] [-kX] [-lM,N] [-p file]\n" +" [-sXYZ] [-tN] [-BL.M] [files ...]\n", + program_name); +} + +static void possibly_load_default_database() +{ + if (search_default && !default_database_loaded) { + char *filename = getenv("REFER"); + if (filename) + database_list.add_file(filename); + else + database_list.add_file(DEFAULT_INDEX, 1); + default_database_loaded = 1; + } +} + +static int is_list(const string &str) +{ + const char *start = str.contents(); + const char *end = start + str.length(); + while (end > start && csspace(end[-1])) + end--; + while (start < end && csspace(*start)) + start++; + return end - start == 6 && memcmp(start, "$LIST$", 6) == 0; +} + +static void do_file(const char *filename) +{ + FILE *fp; + if (strcmp(filename, "-") == 0) { + fp = stdin; + } + else { + errno = 0; + fp = fopen(filename, "r"); + if (fp == 0) { + error("can't open `%1': %2", filename, strerror(errno)); + return; + } + } + current_filename = filename; + fprintf(outfp, ".lf 1 %s\n", filename); + string line; + current_lineno = 0; + for (;;) { + line.clear(); + for (;;) { + int c = getc(fp); + if (c == EOF) { + if (line.length() > 0) + line += '\n'; + break; + } + if (illegal_input_char(c)) + error("illegal input character code %1", c); + else { + line += c; + if (c == '\n') + break; + } + } + int len = line.length(); + if (len == 0) + break; + current_lineno++; + if (len >= 2 && line[0] == '.' && line[1] == '[') { + int start_lineno = current_lineno; + int start_of_line = 1; + string str; + string post; + string pre(line.contents() + 2, line.length() - 3); + for (;;) { + int c = getc(fp); + if (c == EOF) { + error_with_file_and_line(current_filename, start_lineno, + "missing `.]' line"); + break; + } + if (start_of_line) + current_lineno++; + if (start_of_line && c == '.') { + int d = getc(fp); + if (d == ']') { + while ((d = getc(fp)) != '\n' && d != EOF) { + if (illegal_input_char(d)) + error("illegal input character code %1", d); + else + post += d; + } + break; + } + if (d != EOF) + ungetc(d, fp); + } + if (illegal_input_char(c)) + error("illegal input character code %1", c); + else + str += c; + start_of_line = (c == '\n'); + } + if (is_list(str)) { + output_pending_line(); + if (accumulate) + output_references(); + else + error("found `$LIST$' but not accumulating references"); + } + else { + unsigned flags = (accumulate + ? store_reference(str) + : immediately_handle_reference(str)); + if (label_in_text) { + if (accumulate && outfp == stdout) + divert_to_temporary_file(); + if (pending_line.length() == 0) { + warning("can't attach citation to previous line"); + } + else + pending_line.set_length(pending_line.length() - 1); + string punct; + if (move_punctuation) + split_punct(pending_line, punct); + int have_text = pre.length() > 0 || post.length() > 0; + label_type lt = label_type(flags & ~(FORCE_LEFT_BRACKET + |FORCE_RIGHT_BRACKET)); + if ((flags & FORCE_LEFT_BRACKET) || !have_text) + pending_line += PRE_LABEL_MARKER; + pending_line += pre; + char lm = LABEL_MARKER + (int)lt; + pending_line += lm; + pending_line += post; + if ((flags & FORCE_RIGHT_BRACKET) || !have_text) + pending_line += POST_LABEL_MARKER; + pending_line += punct; + pending_line += '\n'; + } + } + need_syncing = 1; + } + else if (len >= 4 + && line[0] == '.' && line[1] == 'l' && line[2] == 'f' + && (compatible_flag || line[3] == '\n' || line[3] == ' ')) { + pending_lf_lines += line; + line += '\0'; + if (interpret_lf_args(line.contents() + 3)) + current_lineno--; + } + else if (recognize_R1_R2 + && len >= 4 + && line[0] == '.' && line[1] == 'R' && line[2] == '1' + && (compatible_flag || line[3] == '\n' || line[3] == ' ')) { + line.clear(); + int start_of_line = 1; + int start_lineno = current_lineno; + for (;;) { + int c = getc(fp); + if (c != EOF && start_of_line) + current_lineno++; + if (start_of_line && c == '.') { + c = getc(fp); + if (c == 'R') { + c = getc(fp); + if (c == '2') { + c = getc(fp); + if (compatible_flag || c == ' ' || c == '\n' || c == EOF) { + while (c != EOF && c != '\n') + c = getc(fp); + break; + } + else { + line += '.'; + line += 'R'; + line += '2'; + } + } + else { + line += '.'; + line += 'R'; + } + } + else + line += '.'; + } + if (c == EOF) { + error_with_file_and_line(current_filename, start_lineno, + "missing `.R2' line"); + break; + } + if (illegal_input_char(c)) + error("illegal input character code %1", int(c)); + else { + line += c; + start_of_line = c == '\n'; + } + } + output_pending_line(); + if (accumulate) + output_references(); + else + nreferences = 0; + process_commands(line, current_filename, start_lineno + 1); + need_syncing = 1; + } + else { + output_pending_line(); + pending_line = line; + } + } + need_syncing = 0; + output_pending_line(); + if (fp != stdin) + fclose(fp); +} + +class label_processing_state { + enum { + NORMAL, + PENDING_LABEL, + PENDING_LABEL_POST, + PENDING_LABEL_POST_PRE, + PENDING_POST + } state; + label_type type; // type of pending labels + int count; // number of pending labels + reference **rptr; // pointer to next reference + int rcount; // number of references left + FILE *fp; + int handle_pending(int c); +public: + label_processing_state(reference **, int, FILE *); + ~label_processing_state(); + void process(int c); +}; + +static void output_pending_line() +{ + if (label_in_text && !accumulate && ncitations > 0) { + label_processing_state state(citation, ncitations, outfp); + int len = pending_line.length(); + for (int i = 0; i < len; i++) + state.process((unsigned char)(pending_line[i])); + } + else + put_string(pending_line, outfp); + pending_line.clear(); + if (pending_lf_lines.length() > 0) { + put_string(pending_lf_lines, outfp); + pending_lf_lines.clear(); + } + if (!accumulate) + immediately_output_references(); + if (need_syncing) { + fprintf(outfp, ".lf %d %s\n", current_lineno, current_filename); + need_syncing = 0; + } +} + +static void split_punct(string &line, string &punct) +{ + const char *start = line.contents(); + const char *end = start + line.length(); + const char *ptr = start; + const char *last_token_start = 0; + for (;;) { + if (ptr >= end) + break; + last_token_start = ptr; + if (*ptr == PRE_LABEL_MARKER || *ptr == POST_LABEL_MARKER + || (*ptr >= LABEL_MARKER && *ptr < LABEL_MARKER + N_LABEL_TYPES)) + ptr++; + else if (!get_token(&ptr, end)) + break; + } + if (last_token_start) { + const token_info *ti = lookup_token(last_token_start, end); + if (ti->is_punct()) { + punct.append(last_token_start, end - last_token_start); + line.set_length(last_token_start - start); + } + } +} + +static void divert_to_temporary_file() +{ + outfp = xtmpfile(); +} + +static void store_citation(reference *ref) +{ + if (ncitations >= citation_max) { + if (citation == 0) + citation = new reference*[citation_max = 100]; + else { + reference **old_citation = citation; + citation_max *= 2; + citation = new reference *[citation_max]; + memcpy(citation, old_citation, ncitations*sizeof(reference *)); + a_delete old_citation; + } + } + citation[ncitations++] = ref; +} + +static unsigned store_reference(const string &str) +{ + if (reference_hash_table == 0) { + reference_hash_table = new reference *[17]; + hash_table_size = 17; + for (int i = 0; i < hash_table_size; i++) + reference_hash_table[i] = 0; + } + unsigned flags; + reference *ref = make_reference(str, &flags); + ref->compute_hash_code(); + unsigned h = ref->hash(); + reference **ptr; + for (ptr = reference_hash_table + (h % hash_table_size); + *ptr != 0; + ((ptr == reference_hash_table) + ? (ptr = reference_hash_table + hash_table_size - 1) + : --ptr)) + if (same_reference(**ptr, *ref)) + break; + if (*ptr != 0) { + if (ref->is_merged()) + warning("fields ignored because reference already used"); + delete ref; + ref = *ptr; + } + else { + *ptr = ref; + ref->set_number(nreferences); + nreferences++; + ref->pre_compute_label(); + ref->compute_sort_key(); + if (nreferences*2 >= hash_table_size) { + // Rehash it. + reference **old_table = reference_hash_table; + int old_size = hash_table_size; + hash_table_size = next_size(hash_table_size); + reference_hash_table = new reference*[hash_table_size]; + int i; + for (i = 0; i < hash_table_size; i++) + reference_hash_table[i] = 0; + for (i = 0; i < old_size; i++) + if (old_table[i]) { + reference **p; + for (p = (reference_hash_table + + (old_table[i]->hash() % hash_table_size)); + *p; + ((p == reference_hash_table) + ? (p = reference_hash_table + hash_table_size - 1) + : --p)) + ; + *p = old_table[i]; + } + a_delete old_table; + } + } + if (label_in_text) + store_citation(ref); + return flags; +} + +unsigned immediately_handle_reference(const string &str) +{ + unsigned flags; + reference *ref = make_reference(str, &flags); + ref->set_number(nreferences); + if (label_in_text || label_in_reference) { + ref->pre_compute_label(); + ref->immediate_compute_label(); + } + nreferences++; + store_citation(ref); + return flags; +} + +static void immediately_output_references() +{ + for (int i = 0; i < ncitations; i++) { + reference *ref = citation[i]; + if (label_in_reference) { + fputs(".ds [F ", outfp); + const string &label = ref->get_label(NORMAL_LABEL); + if (label.length() > 0 + && (label[0] == ' ' || label[0] == '\\' || label[0] == '"')) + putc('"', outfp); + put_string(label, outfp); + putc('\n', outfp); + } + ref->output(outfp); + delete ref; + } + ncitations = 0; +} + +static void output_citation_group(reference **v, int n, label_type type, + FILE *fp) +{ + if (sort_adjacent_labels) { + // Do an insertion sort. Usually n will be very small. + for (int i = 1; i < n; i++) { + int num = v[i]->get_number(); + reference *temp = v[i]; + int j; + for (j = i - 1; j >= 0 && v[j]->get_number() > num; j--) + v[j + 1] = v[j]; + v[j + 1] = temp; + } + } + // This messes up if !accumulate. + if (accumulate && n > 1) { + // remove duplicates + int j = 1; + for (int i = 1; i < n; i++) + if (v[i]->get_label(type) != v[i - 1]->get_label(type)) + v[j++] = v[i]; + n = j; + } + string merged_label; + for (int i = 0; i < n; i++) { + int nmerged = v[i]->merge_labels(v + i + 1, n - i - 1, type, merged_label); + if (nmerged > 0) { + put_string(merged_label, fp); + i += nmerged; + } + else + put_string(v[i]->get_label(type), fp); + if (i < n - 1) + put_string(sep_label, fp); + } +} + + +label_processing_state::label_processing_state(reference **p, int n, FILE *f) +: state(NORMAL), count(0), rptr(p), rcount(n), fp(f) +{ +} + +label_processing_state::~label_processing_state() +{ + int handled = handle_pending(EOF); + assert(!handled); + assert(rcount == 0); +} + +int label_processing_state::handle_pending(int c) +{ + switch (state) { + case NORMAL: + break; + case PENDING_LABEL: + if (c == POST_LABEL_MARKER) { + state = PENDING_LABEL_POST; + return 1; + } + else { + output_citation_group(rptr, count, type, fp); + rptr += count ; + rcount -= count; + state = NORMAL; + } + break; + case PENDING_LABEL_POST: + if (c == PRE_LABEL_MARKER) { + state = PENDING_LABEL_POST_PRE; + return 1; + } + else { + output_citation_group(rptr, count, type, fp); + rptr += count; + rcount -= count; + put_string(post_label, fp); + state = NORMAL; + } + break; + case PENDING_LABEL_POST_PRE: + if (c >= LABEL_MARKER + && c < LABEL_MARKER + N_LABEL_TYPES + && c - LABEL_MARKER == type) { + count += 1; + state = PENDING_LABEL; + return 1; + } + else { + output_citation_group(rptr, count, type, fp); + rptr += count; + rcount -= count; + put_string(sep_label, fp); + state = NORMAL; + } + break; + case PENDING_POST: + if (c == PRE_LABEL_MARKER) { + put_string(sep_label, fp); + state = NORMAL; + return 1; + } + else { + put_string(post_label, fp); + state = NORMAL; + } + break; + } + return 0; +} + +void label_processing_state::process(int c) +{ + if (handle_pending(c)) + return; + assert(state == NORMAL); + switch (c) { + case PRE_LABEL_MARKER: + put_string(pre_label, fp); + state = NORMAL; + break; + case POST_LABEL_MARKER: + state = PENDING_POST; + break; + case LABEL_MARKER: + case LABEL_MARKER + 1: + count = 1; + state = PENDING_LABEL; + type = label_type(c - LABEL_MARKER); + break; + default: + state = NORMAL; + putc(c, fp); + break; + } +} + +extern "C" { + +int rcompare(const void *p1, const void *p2) +{ + return compare_reference(**(reference **)p1, **(reference **)p2); +} + +} + +void output_references() +{ + assert(accumulate); + if (nreferences > 0) { + int j = 0; + int i; + for (i = 0; i < hash_table_size; i++) + if (reference_hash_table[i] != 0) + reference_hash_table[j++] = reference_hash_table[i]; + assert(j == nreferences); + for (; j < hash_table_size; j++) + reference_hash_table[j] = 0; + qsort(reference_hash_table, nreferences, sizeof(reference*), rcompare); + for (i = 0; i < nreferences; i++) + reference_hash_table[i]->set_number(i); + compute_labels(reference_hash_table, nreferences); + } + if (outfp != stdout) { + rewind(outfp); + { + label_processing_state state(citation, ncitations, stdout); + int c; + while ((c = getc(outfp)) != EOF) + state.process(c); + } + ncitations = 0; + fclose(outfp); + outfp = stdout; + } + if (nreferences > 0) { + fputs(".]<\n", outfp); + for (int i = 0; i < nreferences; i++) { + if (sort_fields.length() > 0) + reference_hash_table[i]->print_sort_key_comment(outfp); + if (label_in_reference) { + fputs(".ds [F ", outfp); + const string &label = reference_hash_table[i]->get_label(NORMAL_LABEL); + if (label.length() > 0 + && (label[0] == ' ' || label[0] == '\\' || label[0] == '"')) + putc('"', outfp); + put_string(label, outfp); + putc('\n', outfp); + } + reference_hash_table[i]->output(outfp); + delete reference_hash_table[i]; + reference_hash_table[i] = 0; + } + fputs(".]>\n", outfp); + nreferences = 0; + } + clear_labels(); +} + +static reference *find_reference(const char *query, int query_len) +{ + // This is so that error messages look better. + while (query_len > 0 && csspace(query[query_len - 1])) + query_len--; + string str; + for (int i = 0; i < query_len; i++) + str += query[i] == '\n' ? ' ' : query[i]; + str += '\0'; + possibly_load_default_database(); + search_list_iterator iter(&database_list, str.contents()); + reference_id rid; + const char *start; + int len; + if (!iter.next(&start, &len, &rid)) { + error("no matches for `%1'", str.contents()); + return 0; + } + const char *end = start + len; + while (start < end) { + if (*start == '%') + break; + while (start < end && *start++ != '\n') + ; + } + if (start >= end) { + error("found a reference for `%1' but it didn't contain any fields", + str.contents()); + return 0; + } + reference *result = new reference(start, end - start, &rid); + if (iter.next(&start, &len, &rid)) + warning("multiple matches for `%1'", str.contents()); + return result; +} + +static reference *make_reference(const string &str, unsigned *flagsp) +{ + const char *start = str.contents(); + const char *end = start + str.length(); + const char *ptr = start; + while (ptr < end) { + if (*ptr == '%') + break; + while (ptr < end && *ptr++ != '\n') + ; + } + *flagsp = 0; + for (; start < ptr; start++) { + if (*start == '#') + *flagsp = (SHORT_LABEL | (*flagsp & (FORCE_RIGHT_BRACKET + | FORCE_LEFT_BRACKET))); + else if (*start == '[') + *flagsp |= FORCE_LEFT_BRACKET; + else if (*start == ']') + *flagsp |= FORCE_RIGHT_BRACKET; + else if (!csspace(*start)) + break; + } + if (start >= end) { + error("empty reference"); + return new reference; + } + reference *database_ref = 0; + if (start < ptr) + database_ref = find_reference(start, ptr - start); + reference *inline_ref = 0; + if (ptr < end) + inline_ref = new reference(ptr, end - ptr); + if (inline_ref) { + if (database_ref) { + database_ref->merge(*inline_ref); + delete inline_ref; + return database_ref; + } + else + return inline_ref; + } + else if (database_ref) + return database_ref; + else + return new reference; +} + +static void do_ref(const string &str) +{ + if (accumulate) + (void)store_reference(str); + else { + (void)immediately_handle_reference(str); + immediately_output_references(); + } +} + +static void trim_blanks(string &str) +{ + const char *start = str.contents(); + const char *end = start + str.length(); + while (end > start && end[-1] != '\n' && csspace(end[-1])) + --end; + str.set_length(end - start); +} + +void do_bib(const char *filename) +{ + FILE *fp; + if (strcmp(filename, "-") == 0) + fp = stdin; + else { + errno = 0; + fp = fopen(filename, "r"); + if (fp == 0) { + error("can't open `%1': %2", filename, strerror(errno)); + return; + } + current_filename = filename; + } + enum { + START, MIDDLE, BODY, BODY_START, BODY_BLANK, BODY_DOT + } state = START; + string body; + for (;;) { + int c = getc(fp); + if (c == EOF) + break; + if (illegal_input_char(c)) { + error("illegal input character code %1", c); + continue; + } + switch (state) { + case START: + if (c == '%') { + body = c; + state = BODY; + } + else if (c != '\n') + state = MIDDLE; + break; + case MIDDLE: + if (c == '\n') + state = START; + break; + case BODY: + body += c; + if (c == '\n') + state = BODY_START; + break; + case BODY_START: + if (c == '\n') { + do_ref(body); + state = START; + } + else if (c == '.') + state = BODY_DOT; + else if (csspace(c)) { + state = BODY_BLANK; + body += c; + } + else { + body += c; + state = BODY; + } + break; + case BODY_BLANK: + if (c == '\n') { + trim_blanks(body); + do_ref(body); + state = START; + } + else if (csspace(c)) + body += c; + else { + body += c; + state = BODY; + } + break; + case BODY_DOT: + if (c == ']') { + do_ref(body); + state = MIDDLE; + } + else { + body += '.'; + body += c; + state = c == '\n' ? BODY_START : BODY; + } + break; + default: + assert(0); + } + if (c == '\n') + current_lineno++; + } + switch (state) { + case START: + case MIDDLE: + break; + case BODY: + body += '\n'; + do_ref(body); + break; + case BODY_DOT: + case BODY_START: + do_ref(body); + break; + case BODY_BLANK: + trim_blanks(body); + do_ref(body); + break; + } + fclose(fp); +} + +// from the Dragon Book + +unsigned hash_string(const char *s, int len) +{ + const char *end = s + len; + unsigned h = 0, g; + while (s < end) { + h <<= 4; + h += *s++; + if ((g = h & 0xf0000000) != 0) { + h ^= g >> 24; + h ^= g; + } + } + return h; +} + +int next_size(int n) +{ + static const int table_sizes[] = { + 101, 503, 1009, 2003, 3001, 4001, 5003, 10007, 20011, 40009, + 80021, 160001, 500009, 1000003, 2000003, 4000037, 8000009, + 16000057, 32000011, 64000031, 128000003, 0 + }; + + const int *p; + for (p = table_sizes; *p <= n && *p != 0; p++) + ; + assert(*p != 0); + return *p; +} + diff --git a/contrib/groff/src/preproc/refer/refer.h b/contrib/groff/src/preproc/refer/refer.h new file mode 100644 index 0000000..f0ab3cd --- /dev/null +++ b/contrib/groff/src/preproc/refer/refer.h @@ -0,0 +1,78 @@ +// -*- 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 <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include <errno.h> + +#include "errarg.h" +#include "error.h" +#include "lib.h" +#include "stringclass.h" +#include "cset.h" +#include "cmap.h" + +#include "defs.h" + +unsigned hash_string(const char *, int); +int next_size(int); + +extern string capitalize_fields; +extern string reverse_fields; +extern string abbreviate_fields; +extern string period_before_last_name; +extern string period_before_initial; +extern string period_before_hyphen; +extern string period_before_other; +extern string sort_fields; +extern int annotation_field; +extern string annotation_macro; +extern string discard_fields; +extern string articles; +extern int abbreviate_label_ranges; +extern string label_range_indicator; +extern int date_as_label; +extern string join_authors_exactly_two; +extern string join_authors_last_two; +extern string join_authors_default; +extern string separate_label_second_parts; +extern string et_al; +extern int et_al_min_elide; +extern int et_al_min_total; + +extern int compatible_flag; + +extern int set_label_spec(const char *); +extern int set_date_label_spec(const char *); +extern int set_short_label_spec(const char *); + +extern int short_label_flag; + +void clear_labels(); +void command_error(const char *, + const errarg &arg1 = empty_errarg, + const errarg &arg2 = empty_errarg, + const errarg &arg3 = empty_errarg); + +struct reference; + +void compute_labels(reference **, int); diff --git a/contrib/groff/src/preproc/refer/refer.man b/contrib/groff/src/preproc/refer/refer.man new file mode 100644 index 0000000..13708cf --- /dev/null +++ b/contrib/groff/src/preproc/refer/refer.man @@ -0,0 +1,1302 @@ +.ig \"-*- nroff -*- +Copyright (C) 1989-2000 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the Free Software Foundation instead of in +the original English. +.. +.de TQ +.br +.ns +.TP \\$1 +.. +.\" Like TP, but if specified indent is more than half +.\" the current line-length - indent, use the default indent. +.de Tp +.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP +.el .TP "\\$1" +.. +.\" The BSD man macros can't handle " in arguments to font change macros, +.\" so use \(ts instead of ". +.tr \(ts" +.TH @G@REFER @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" +.SH NAME +@g@refer \- preprocess bibliographic references for groff +.SH SYNOPSIS +.nr a \n(.j +.ad l +.nr i \n(.i +.in +\w'\fB@g@refer 'u +.ti \niu +.B @g@refer +.de OP +.ie \\n(.$-1 .RI "[\ \fB\\$1\fP" "\\$2" "\ ]" +.el .RB "[\ " "\\$1" "\ ]" +.. +.OP \-benvCPRS +.OP \-a n +.OP \-c fields +.OP \-f n +.OP \-i fields +.OP \-k field +.OP \-l m,n +.OP \-p filename +.OP \-s fields +.OP \-t n +.OP \-B field.macro +.RI [\ filename \|.\|.\|.\ ] +.br +.ad \na +.PP +It is possible to have whitespace between a command line option and its +parameter. +.SH DESCRIPTION +This file documents the GNU version of +.BR refer , +which is part of the groff document formatting system. +.B refer +copies the contents of +.IR filename \|.\|.\|. +to the standard output, +except that lines between +.B .[ +and +.B .] +are interpreted as citations, +and lines between +.B .R1 +and +.B .R2 +are interpreted as commands about how citations are to be processed. +.LP +Each citation specifies a reference. +The citation can specify a reference that is contained in +a bibliographic database by giving a set of keywords +that only that reference contains. +Alternatively it can specify a reference by supplying a database +record in the citation. +A combination of these alternatives is also possible. +.LP +For each citation, +.B refer +can produce a mark in the text. +This mark consists of some label which can be separated from +the text and from other labels in various ways. +For each reference it also outputs +.B groff +commands that can be used by a macro package to produce a formatted +reference for each citation. +The output of +.B refer +must therefore be processed using a suitable macro package. +The +.B \-ms +and +.B \-me +macros are both suitable. +The commands to format a citation's reference can be output immediately after +the citation, +or the references may be accumulated, +and the commands output at some later point. +If the references are accumulated, then multiple citations of the same +reference will produce a single formatted reference. +.LP +The interpretation of lines between +.B .R1 +and +.B .R2 +as commands is a new feature of GNU refer. +Documents making use of this feature can still be processed by +Unix refer just by adding the lines +.RS +.LP +.nf +.ft B +\&.de R1 +\&.ig R2 +\&.. +.ft +.fi +.RE +to the beginning of the document. +This will cause +.B troff +to ignore everything between +.B .R1 +and +.BR .R2 . +The effect of some commands can also be achieved by options. +These options are supported mainly for compatibility with Unix refer. +It is usually more convenient to use commands. +.LP +.B refer +generates +.B .lf +lines so that filenames and line numbers in messages produced +by commands that read +.B refer +output will be correct; +it also interprets lines beginning with +.B .lf +so that filenames and line numbers in the messages and +.B .lf +lines that it produces will be accurate even if the input has been +preprocessed by a command such as +.BR @g@soelim (@MAN1EXT@). +.SH OPTIONS +.LP +Most options are equivalent to commands +(for a description of these commands see the +.B Commands +subsection): +.TP +.B \-b +.B +no-label-in-text; no-label-in-reference +.TP +.B \-e +.B accumulate +.TP +.B \-n +.B no-default-database +.TP +.B \-C +.B compatible +.TP +.B \-P +.B move-punctuation +.TP +.B \-S +.B +label "(A.n|Q) ', ' (D.y|D)"; bracket-label " (" ) "; " +.TP +.BI \-a n +.B reverse +.BI A n +.TP +.BI \-c fields +.B capitalize +.I fields +.TP +.BI \-f n +.B label +.BI % n +.TP +.BI \-i fields +.B search-ignore +.I fields +.TP +.B \-k +.B label +.B L\(ti%a +.TP +.BI \-k field +.B label +.IB field \(ti%a +.TP +.B \-l +.B label +.BI A.nD.y%a +.TP +.BI \-l m +.B label +.BI A.n+ m D.y%a +.TP +.BI \-l, n +.B label +.BI A.nD.y\- n %a +.TP +.BI \-l m , n +.B label +.BI A.n+ m D.y\- n %a +.TP +.BI \-p filename +.B database +.I filename +.TP +.BI \-s spec +.B sort +.I spec +.TP +.BI \-t n +.B search-truncate +.I n +.LP +These options are equivalent to the following commands with the +addition that the filenames specified on the command line are +processed as if they were arguments to the +.B bibliography +command instead of in the normal way: +.TP +.B \-B +.B +annotate X AP; no-label-in-reference +.TP +.BI \-B field . macro +.B annotate +.I field +.IB macro ; +.B no-label-in-reference +.LP +The following options have no equivalent commands: +.TP +.B \-v +Print the version number. +.TP +.B \-R +Don't recognize lines beginning with +.BR .R1 / .R2 . +.SH USAGE +.SS Bibliographic databases +The bibliographic database is a text file consisting of records +separated by one or more blank lines. +Within each record fields start with a +.B % +at the beginning of a line. +Each field has a one character name that immediately follows the +.BR % . +It is best to use only upper and lower case letters for the names +of fields. +The name of the field should be followed by exactly one space, +and then by the contents of the field. +Empty fields are ignored. +The conventional meaning of each field is as follows: +.TP +.B A +The name of an author. +If the name contains a title such as +.B Jr. +at the end, +it should be separated from the last name by a comma. +There can be multiple occurrences of the +.B A +field. +The order is significant. +It is a good idea always to supply an +.B A +field or a +.B Q +field. +.TP +.B B +For an article that is part of a book, the title of the book +.TP +.B C +The place (city) of publication. +.TP +.B D +The date of publication. +The year should be specified in full. +If the month is specified, the name rather than the number of the month +should be used, but only the first three letters are required. +It is a good idea always to supply a +.B D +field; +if the date is unknown, a value such as +.B in press +or +.B unknown +can be used. +.TP +.B E +For an article that is part of a book, the name of an editor of the book. +Where the work has editors and no authors, +the names of the editors should be given as +.B A +fields and +.B ,\ (ed) +or +.B ,\ (eds) +should be appended to the last author. +.TP +.B G +US Government ordering number. +.TP +.B I +The publisher (issuer). +.TP +.B J +For an article in a journal, the name of the journal. +.TP +.B K +Keywords to be used for searching. +.TP +.B L +Label. +.TP +.B N +Journal issue number. +.TP +.B O +Other information. +This is usually printed at the end of the reference. +.TP +.B P +Page number. +A range of pages can be specified as +.IB m \- n\fR. +.TP +.B Q +The name of the author, if the author is not a person. +This will only be used if there are no +.B A +fields. +There can only be one +.B Q +field. +.TP +.B R +Technical report number. +.TP +.B S +Series name. +.TP +.B T +Title. +For an article in a book or journal, +this should be the title of the article. +.TP +.B V +Volume number of the journal or book. +.TP +.B X +Annotation. +.LP +For all fields except +.B A +and +.BR E , +if there is more than one occurrence of a particular field in a record, +only the last such field will be used. +.LP +If accent strings are used, they should follow the character to be accented. +This means that the +.B AM +macro must be used with the +.B \-ms +macros. +Accent strings should not be quoted: +use one +.B \e +rather than two. +.SS Citations +The format of a citation is +.RS +.BI .[ opening-text +.br +.I +flags keywords +.br +.I fields +.br +.BI .] closing-text +.RE +.LP +The +.IR opening-text , +.IR closing-text +and +.I flags +components are optional. +Only one of the +.I keywords +and +.I fields +components need be specified. +.LP +The +.I keywords +component says to search the bibliographic databases for a reference +that contains all the words in +.IR keywords . +It is an error if more than one reference if found. +.LP +The +.I fields +components specifies additional fields to replace or supplement +those specified in the reference. +When references are being accumulated and the +.I keywords +component is non-empty, +then additional fields should be specified only on the first +occasion that a particular reference is cited, +and will apply to all citations of that reference. +.LP +The +.I opening-text +and +.I closing-text +component specifies strings to be used to bracket the label instead +of the strings specified in the +.B bracket-label +command. +If either of these components is non-empty, +the strings specified in the +.B bracket-label +command will not be used; +this behaviour can be altered using the +.B [ +and +.B ] +flags. +Note that leading and trailing spaces are significant for these components. +.LP +The +.I flags +component is a list of +non-alphanumeric characters each of which modifies the treatment +of this particular citation. +Unix refer will treat these flags as part of the keywords and +so will ignore them since they are non-alphanumeric. +The following flags are currently recognized: +.TP +.B # +This says to use the label specified by the +.B short-label +command, +instead of that specified by the +.B label +command. +If no short label has been specified, the normal label will be used. +Typically the short label is used with author-date labels +and consists of only the date and possibly a disambiguating letter; +the +.B # +is supposed to be suggestive of a numeric type of label. +.TP +.B [ +Precede +.I opening-text +with the first string specified in the +.B bracket-label +command. +.TP +.B ] +Follow +.I closing-text +with the second string specified in the +.B bracket-label +command. +.LP +One advantages of using the +.B [ +and +.B ] +flags rather than including the brackets in +.I opening-text +and +.I closing-text +is that +you can change the style of bracket used in the document just by changing the +.B bracket-label +command. +Another advantage is that sorting and merging of citations +will not necessarily be inhibited if the flags are used. +.LP +If a label is to be inserted into the text, +it will be attached to the line preceding the +.B .[ +line. +If there is no such line, then an extra line will be inserted before the +.B .[ +line and a warning will be given. +.LP +There is no special notation for making a citation to multiple references. +Just use a sequence of citations, one for each reference. +Don't put anything between the citations. +The labels for all the citations will be attached to the line preceding +the first citation. +The labels may also be sorted or merged. +See the description of the +.B <> +label expression, and of the +.B sort-adjacent-labels +and +.B abbreviate-label-ranges +command. +A label will not be merged if its citation has a non-empty +.I opening-text +or +.IR closing-text . +However, the labels for a citation using the +.B ] +flag and without any +.I closing-text +immediately followed by a citation using the +.B [ +flag and without any +.I opening-text +may be sorted and merged +even though the first citation's +.I opening-text +or the second citation's +.I closing-text +is non-empty. +(If you wish to prevent this just make the first citation's +.I closing-text +.BR \e& .) +.SS Commands +Commands are contained between lines starting with +.B .R1 +and +.BR .R2 . +Recognition of these lines can be prevented by the +.B \-R +option. +When a +.B .R1 +line is recognized any accumulated references are flushed out. +Neither +.B .R1 +nor +.B .R2 +lines, +nor anything between them +is output. +.LP +Commands are separated by newlines or +.BR ; s. +.B # +introduces a comment that extends to the end of the line +(but does not conceal the newline). +Each command is broken up into words. +Words are separated by spaces or tabs. +A word that begins with +.B \(ts +extends to the next +.B \(ts +that is not followed by another +.BR \(ts . +If there is no such +.B \(ts +the word extends to the end of the line. +Pairs of +.B \(ts +in a word beginning with +.B \(ts +collapse to a single +.BR \(ts . +Neither +.B # +nor +.B ; +are recognized inside +.BR \(ts s. +A line can be continued by ending it with +.BR \e ; +this works everywhere except after a +.BR # . +.LP +.ds n \fR* +Each command +.I name +that is marked with \*n has an associated negative command +.BI no- name +that undoes the effect of +.IR name . +For example, the +.B no-sort +command specifies that references should not be sorted. +The negative commands take no arguments. +.LP +In the following description each argument must be a single word; +.I field +is used for a single upper or lower case letter naming a field; +.I fields +is used for a sequence of such letters; +.I m +and +.I n +are used for a non-negative numbers; +.I string +is used for an arbitrary string; +.I filename +is used for the name of a file. +.Tp \w'\fBabbreviate-label-ranges'u+2n +.BI abbreviate\*n\ fields\ string1\ string2\ string3\ string4 +Abbreviate the first names of +.IR fields . +An initial letter will be separated from another initial letter by +.IR string1 , +from the last name by +.IR string2 , +and from anything else +(such as a +.B von +or +.BR de ) +by +.IR string3 . +These default to a period followed by a space. +In a hyphenated first name, +the initial of the first part of the name will be separated from the hyphen by +.IR string4 ; +this defaults to a period. +No attempt is made to handle any ambiguities that might +result from abbreviation. +Names are abbreviated before sorting and before +label construction. +.TP +.BI abbreviate-label-ranges\*n\ string +Three or more adjacent labels that refer to consecutive references +will be abbreviated to a label consisting +of the first label, followed by +.I string +followed by the last label. +This is mainly useful with numeric labels. +If +.I string +is omitted it defaults to +.BR \- . +.TP +.B accumulate\*n +Accumulate references instead of writing out each reference +as it is encountered. +Accumulated references will be written out whenever a reference +of the form +.RS +.IP +.B .[ +.br +.B $LIST$ +.br +.B .] +.LP +is encountered, +after all input files hve been processed, +and whenever +.B .R1 +line is recognized. +.RE +.TP +.BI annotate\*n\ field\ string +.I field +is an annotation; +print it at the end of the reference as a paragraph preceded by the line +.RS +.IP +.BI . string +.LP +If +.I macro +is omitted it will default to +.BR AP ; +if +.I field +is also omitted it will default to +.BR X . +Only one field can be an annotation. +.RE +.TP +.BI articles\ string \fR\|.\|.\|. +.IR string \|.\|.\|. +are definite or indefinite articles, and should be ignored at the beginning of +.B T +fields when sorting. +Initially, +.BR the , +.B a +and +.B an +are recognized as articles. +.TP +.BI bibliography\ filename \fR\|.\|.\|. +Write out all the references contained in the bibliographic databases +.IR filename \|.\|.\|. +.TP +.BI bracket-label\ string1\ string2\ string3 +In the text, bracket each label +with +.I string1 +and +.IR string2 . +An occurrence of +.I string2 +immediately followed by +.I string1 +will be turned into +.IR string3 . +The default behaviour is +.RS +.IP +.B +bracket-label \e*([. \e*(.] ", " +.RE +.TP +.BI capitalize\ fields +Convert +.I fields +to caps and small caps. +.TP +.B compatible\*n +Recognize +.B .R1 +and +.B .R2 +even when followed by a character other than space or newline. +.TP +.BI database\ filename \fR\|.\|.\|. +Search the bibliographic databases +.IR filename \|.\|.\|. +For each +.I filename +if an index +.IB filename @INDEX_SUFFIX@ +created by +.BR @g@indxbib (@MAN1EXT@) +exists, then it will be searched instead; +each index can cover multiple databases. +.TP +.BI date-as-label\*n\ string +.I string +is a label expression that specifies a string with which to replace the +.B D +field after constructing the label. +See the +.B "Label expressions" +subsection for a description of label expressions. +This command is useful if you do not want explicit labels in the +reference list, but instead want to handle any necessary +disambiguation by qualifying the date in some way. +The label used in the text would typically be some combination of the +author and date. +In most cases you should also use the +.B no-label-in-reference +command. +For example, +.RS +.IP +.B +date-as-label D.+yD.y%a*D.-y +.LP +would attach a disambiguating letter to the year part of the +.B D +field in the reference. +.RE +.TP +.B default-database\*n +The default database should be searched. +This is the default behaviour, so the negative version of +this command is more useful. +refer determines whether the default database should be searched +on the first occasion that it needs to do a search. +Thus a +.B no-default-database +command must be given before then, +in order to be effective. +.TP +.BI discard\*n\ fields +When the reference is read, +.I fields +should be discarded; +no string definitions for +.I fields +will be output. +Initially, +.I fields +are +.BR XYZ . +.TP +.BI et-al\*n\ string\ m\ n +Control use of +.B +et al +in the evaluation of +.B @ +expressions in label expressions. +If the number of authors needed to make the author sequence +unambiguous is +.I u +and the total number of authors is +.I t +then the last +.IR t \|\-\| u +authors will be replaced by +.I string +provided that +.IR t \|\-\| u +is not less than +.I m +and +.I t +is not less than +.IR n . +The default behaviour is +.RS +.IP +.B +et-al " et al" 2 3 +.RE +.TP +.BI include\ filename +Include +.I filename +and interpret the contents as commands. +.TP +.BI join-authors\ string1\ string2\ string3 +This says how authors should be joined together. +When there are exactly two authors, they will be joined with +.IR string1 . +When there are more than two authors, all but the last two will +be joined with +.IR string2 , +and the last two authors will be joined with +.IR string3 . +If +.I string3 +is omitted, +it will default to +.IR string1 ; +if +.I string2 +is also omitted it will also default to +.IR string1 . +For example, +.RS +.IP +.B +join-authors " and " ", " ", and " +.LP +will restore the default method for joining authors. +.RE +.TP +.B label-in-reference\*n +When outputting the reference, +define the string +.B [F +to be the reference's label. +This is the default behaviour; so the negative version +of this command is more useful. +.TP +.B label-in-text\*n +For each reference output a label in the text. +The label will be separated from the surrounding text as described in the +.B bracket-label +command. +This is the default behaviour; so the negative version +of this command is more useful. +.TP +.BI label\ string +.I string +is a label expression describing how to label each reference. +.TP +.BI separate-label-second-parts\ string +When merging two-part labels, separate the second part of the second +label from the first label with +.IR string . +See the description of the +.B <> +label expression. +.TP +.B move-punctuation\*n +In the text, move any punctuation at the end of line past the label. +It is usually a good idea to give this command unless you are using +superscripted numbers as labels. +.TP +.BI reverse\*n\ string +Reverse the fields whose names +are in +.IR string . +Each field name can be followed by a number which says +how many such fields should be reversed. +If no number is given for a field, all such fields will be reversed. +.TP +.BI search-ignore\*n\ fields +While searching for keys in databases for which no index exists, +ignore the contents of +.IR fields . +Initially, fields +.B XYZ +are ignored. +.TP +.BI search-truncate\*n\ n +Only require the first +.I n +characters of keys to be given. +In effect when searching for a given key +words in the database are truncated to the maximum of +.I n +and the length of the key. +Initially +.I n +is 6. +.TP +.BI short-label\*n\ string +.I string +is a label expression that specifies an alternative (usually shorter) +style of label. +This is used when the +.B # +flag is given in the citation. +When using author-date style labels, the identity of the author +or authors is sometimes clear from the context, and so it +may be desirable to omit the author or authors from the label. +The +.B short-label +command will typically be used to specify a label containing just +a date and possibly a disambiguating letter. +.TP +.BI sort\*n\ string +Sort references according to +.BR string . +References will automatically be accumulated. +.I string +should be a list of field names, each followed by a number, +indicating how many fields with the name should be used for sorting. +.B + +can be used to indicate that all the fields with the name should be used. +Also +.B . +can be used to indicate the references should be sorted using the +(tentative) label. +(The +.B +Label expressions +subsection describes the concept of a tentative label.) +.TP +.B sort-adjacent-labels\*n +Sort labels that are adjacent in the text according to their +position in the reference list. +This command should usually be given if the +.B abbreviate-label-ranges +command has been given, +or if the label expression contains a +.B <> +expression. +This will have no effect unless references are being accumulated. +.SS Label expressions +.LP +Label expressions can be evaluated both normally and tentatively. +The result of normal evaluation is used for output. +The result of tentative evaluation, called the +.I +tentative label, +is used to gather the information +that normal evaluation needs to disambiguate the label. +Label expressions specified by the +.B date-as-label +and +.B short-label +commands are not evaluated tentatively. +Normal and tentative evaluation are the same for all types +of expression other than +.BR @ , +.BR * , +and +.B % +expressions. +The description below applies to normal evaluation, +except where otherwise specified. +.TP +.I field +.TQ +.I field\ n +The +.IR n -th +part of +.IR field . +If +.I n +is omitted, it defaults to 1. +.TP +.BI ' string ' +The characters in +.I string +literally. +.TP +.B @ +All the authors joined as specified by the +.B join-authors +command. +The whole of each author's name will be used. +However, if the references are sorted by author +(that is the sort specification starts with +.BR A+ ), +then authors' last names will be used instead, provided that this does +not introduce ambiguity, +and also an initial subsequence of the authors may be used +instead of all the authors, again provided that this does not +introduce ambiguity. +The use of only the last name for the +.IR i -th +author of some reference +is considered to be ambiguous if +there is some other reference, +such that the first +.IR i \|-\|1 +authors of the references are the same, +the +.IR i -th +authors are not the same, +but the +.IR i -th +authors' last names are the same. +A proper initial subsequence of the sequence +of authors for some reference is considered to be ambiguous if there is +a reference with some other sequence of authors which also has +that subsequence as a proper initial subsequence. +When an initial subsequence of authors is used, the remaining +authors are replaced by the string specified by the +.B et-al +command; +this command may also specify additional requirements that must be +met before an initial subsequence can be used. +.B @ +tentatively evaluates to a canonical representation of the authors, +such that authors that compare equally for sorting purpose +will have the same representation. +.TP +.BI % n +.TQ +.B %a +.TQ +.B %A +.TQ +.B %i +.TQ +.B %I +The serial number of the reference formatted according to the character +following the +.BR % . +The serial number of a reference is 1 plus the number of earlier references +with same tentative label as this reference. +These expressions tentatively evaluate to an empty string. +.TP +.IB expr * +If there is another reference with the same tentative label as +this reference, then +.IR expr , +otherwise an empty string. +It tentatively evaluates to an empty string. +.TP +.IB expr + n +.TQ +.IB expr \- n +The first +.RB ( + ) +or last +.RB ( \- ) +.I n +upper or lower case letters or digits of +.IR expr . +Troff special characters (such as +.BR \e('a ) +count as a single letter. +Accent strings are retained but do not count towards the total. +.TP +.IB expr .l +.I expr +converted to lowercase. +.TP +.IB expr .u +.I expr +converted to uppercase. +.TP +.IB expr .c +.I expr +converted to caps and small caps. +.TP +.IB expr .r +.I expr +reversed so that the last name is first. +.TP +.IB expr .a +.I expr +with first names abbreviated. +Note that fields specified in the +.B abbreviate +command are abbreviated before any labels are evaluated. +Thus +.B .a +is useful only when you want a field to be abbreviated in a label +but not in a reference. +.TP +.IB expr .y +The year part of +.IR expr . +.TP +.IB expr .+y +The part of +.I expr +before the year, or the whole of +.I expr +if it does not contain a year. +.TP +.IB expr .\-y +The part of +.I expr +after the year, or an empty string if +.I expr +does not contain a year. +.TP +.IB expr .n +The last name part of +.IR expr . +.TP +.IB expr1 \(ti expr2 +.I expr1 +except that if the last character of +.I expr1 +is +.B \- +then it will be replaced by +.IR expr2 . +.TP +.I expr1\ expr2 +The concatenation of +.I expr1 +and +.IR expr2 . +.TP +.IB expr1 | expr2 +If +.I expr1 +is non-empty then +.I expr1 +otherwise +.IR expr2 . +.TP +.IB expr1 & expr2 +If +.I expr1 +is non-empty +then +.I expr2 +otherwise an empty string. +.TP +.IB expr1 ? expr2 : expr3 +If +.I expr1 +is non-empty +then +.I expr2 +otherwise +.IR expr3 . +.TP +.BI < expr > +The label is in two parts, which are separated by +.IR expr . +Two adjacent two-part labels which have the same first part will be +merged by appending the second part of the second label onto the first +label separated by the string specified in the +.B separate-label-second-parts +command (initially, a comma followed by a space); the resulting label +will also be a two-part label with the same first part as before +merging, and so additional labels can be merged into it. +Note that it is permissible for the first part to be empty; +this maybe desirable for expressions used in the +.B short-label +command. +.TP +.BI ( expr ) +The same as +.IR expr . +Used for grouping. +.LP +The above expressions are listed in order of precedence +(highest first); +.B & +and +.B | +have the same precedence. +.SS Macro interface +Each reference starts with a call to the macro +.BR ]- . +The string +.B [F +will be defined to be the label for this reference, +unless the +.B no-label-in-reference +command has been given. +There then follows a series of string definitions, +one for each field: +string +.BI [ X +corresponds to field +.IR X . +The number register +.B [P +is set to 1 if the +.B P +field contains a range of pages. +The +.BR [T , +.B [A +and +.B [O +number registers are set to 1 according as the +.BR T , +.B A +and +.B O +fields end with one of the characters +.BR .?! . +The +.B [E +number register will be set to 1 if the +.B [E +string contains more than one name. +The reference is followed by a call to the +.B ][ +macro. +The first argument to this macro gives a number representing +the type of the reference. +If a reference contains a +.B J +field, it will be classified as type 1, +otherwise if it contains a +.B B +field, it will type 3, +otherwise if it contains a +.B G +or +.B R +field it will be type 4, +otherwise if contains a +.B I +field it will be type 2, +otherwise it will be type 0. +The second argument is a symbolic name for the type: +.BR other , +.BR journal-article , +.BR book , +.B article-in-book +or +.BR tech-report . +Groups of references that have been accumulated +or are produced by the +.B bibliography +command are preceded by a call to the +.B ]< +macro and followed by a call to the +.B ]> +macro. +.SH FILES +.Tp \w'\fB@DEFAULT_INDEX@'u+2n +.B @DEFAULT_INDEX@ +Default database. +.TP +.IB file @INDEX_SUFFIX@ +Index files. +.SH "SEE ALSO" +.BR @g@indxbib (@MAN1EXT@), +.BR @g@lookbib (@MAN1EXT@), +.BR lkbib (@MAN1EXT@) +.br +.SH BUGS +In label expressions, +.B <> +expressions are ignored inside +.BI . char +expressions. diff --git a/contrib/groff/src/preproc/refer/token.cc b/contrib/groff/src/preproc/refer/token.cc new file mode 100644 index 0000000..1cf6890 --- /dev/null +++ b/contrib/groff/src/preproc/refer/token.cc @@ -0,0 +1,378 @@ +// -*- 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 "refer.h" +#include "token.h" + +#define TOKEN_TABLE_SIZE 1009 +// I believe in Icelandic thorn sorts after z. +#define THORN_SORT_KEY "{" + +struct token_table_entry { + const char *tok; + token_info ti; + token_table_entry(); +}; + +token_table_entry token_table[TOKEN_TABLE_SIZE]; +int ntokens = 0; + +static void skip_name(const char **ptr, const char *end) +{ + if (*ptr < end) { + switch (*(*ptr)++) { + case '(': + if (*ptr < end) { + *ptr += 1; + if (*ptr < end) + *ptr += 1; + } + break; + case '[': + while (*ptr < end) + if (*(*ptr)++ == ']') + break; + break; + } + } +} + +int get_token(const char **ptr, const char *end) +{ + if (*ptr >= end) + return 0; + char c = *(*ptr)++; + if (c == '\\' && *ptr < end) { + switch (**ptr) { + default: + *ptr += 1; + break; + case '(': + case '[': + skip_name(ptr, end); + break; + case '*': + case 'f': + *ptr += 1; + skip_name(ptr, end); + break; + } + } + return 1; +} + +token_info::token_info() +: type(TOKEN_OTHER), sort_key(0), other_case(0) +{ +} + +void token_info::set(token_type t, const char *sk, const char *oc) +{ + assert(oc == 0 || t == TOKEN_UPPER || t == TOKEN_LOWER); + type = t; + sort_key = sk; + other_case = oc; +} + +void token_info::sortify(const char *start, const char *end, string &result) + const +{ + if (sort_key) + result += sort_key; + else if (type == TOKEN_UPPER || type == TOKEN_LOWER) { + for (; start < end; start++) + if (csalpha(*start)) + result += cmlower(*start); + } +} + +int token_info::sortify_non_empty(const char *start, const char *end) const +{ + if (sort_key) + return *sort_key != '\0'; + if (type != TOKEN_UPPER && type != TOKEN_LOWER) + return 0; + for (; start < end; start++) + if (csalpha(*start)) + return 1; + return 0; +} + + +void token_info::lower_case(const char *start, const char *end, + string &result) const +{ + if (type != TOKEN_UPPER) { + while (start < end) + result += *start++; + } + else if (other_case) + result += other_case; + else { + while (start < end) + result += cmlower(*start++); + } +} + +void token_info::upper_case(const char *start, const char *end, + string &result) const +{ + if (type != TOKEN_LOWER) { + while (start < end) + result += *start++; + } + else if (other_case) + result += other_case; + else { + while (start < end) + result += cmupper(*start++); + } +} + +token_table_entry::token_table_entry() +: tok(0) +{ +} + +static void store_token(const char *tok, token_type typ, + const char *sk = 0, const char *oc = 0) +{ + unsigned n = hash_string(tok, strlen(tok)) % TOKEN_TABLE_SIZE; + for (;;) { + if (token_table[n].tok == 0) { + if (++ntokens == TOKEN_TABLE_SIZE) + assert(0); + token_table[n].tok = tok; + break; + } + if (strcmp(tok, token_table[n].tok) == 0) + break; + if (n == 0) + n = TOKEN_TABLE_SIZE - 1; + else + --n; + } + token_table[n].ti.set(typ, sk, oc); +} + + +token_info default_token_info; + +const token_info *lookup_token(const char *start, const char *end) +{ + unsigned n = hash_string(start, end - start) % TOKEN_TABLE_SIZE; + for (;;) { + if (token_table[n].tok == 0) + break; + if (strlen(token_table[n].tok) == end - start + && memcmp(token_table[n].tok, start, end - start) == 0) + return &(token_table[n].ti); + if (n == 0) + n = TOKEN_TABLE_SIZE - 1; + else + --n; + } + return &default_token_info; +} + +static void init_ascii() +{ + const char *p; + for (p = "abcdefghijklmnopqrstuvwxyz"; *p; p++) { + char buf[2]; + buf[0] = *p; + buf[1] = '\0'; + store_token(strsave(buf), TOKEN_LOWER); + buf[0] = cmupper(buf[0]); + store_token(strsave(buf), TOKEN_UPPER); + } + for (p = "0123456789"; *p; p++) { + char buf[2]; + buf[0] = *p; + buf[1] = '\0'; + const char *s = strsave(buf); + store_token(s, TOKEN_OTHER, s); + } + for (p = ".,:;?!"; *p; p++) { + char buf[2]; + buf[0] = *p; + buf[1] = '\0'; + store_token(strsave(buf), TOKEN_PUNCT); + } + store_token("-", TOKEN_HYPHEN); +} + +static void store_letter(const char *lower, const char *upper, + const char *sort_key = 0) +{ + store_token(lower, TOKEN_LOWER, sort_key, upper); + store_token(upper, TOKEN_UPPER, sort_key, lower); +} + +static void init_letter(unsigned char uc_code, unsigned char lc_code, + const char *sort_key) +{ + char lbuf[2]; + lbuf[0] = lc_code; + lbuf[1] = 0; + char ubuf[2]; + ubuf[0] = uc_code; + ubuf[1] = 0; + store_letter(strsave(lbuf), strsave(ubuf), sort_key); +} + +static void init_latin1() +{ + init_letter(0xc0, 0xe0, "a"); + init_letter(0xc1, 0xe1, "a"); + init_letter(0xc2, 0xe2, "a"); + init_letter(0xc3, 0xe3, "a"); + init_letter(0xc4, 0xe4, "a"); + init_letter(0xc5, 0xe5, "a"); + init_letter(0xc6, 0xe6, "ae"); + init_letter(0xc7, 0xe7, "c"); + init_letter(0xc8, 0xe8, "e"); + init_letter(0xc9, 0xe9, "e"); + init_letter(0xca, 0xea, "e"); + init_letter(0xcb, 0xeb, "e"); + init_letter(0xcc, 0xec, "i"); + init_letter(0xcd, 0xed, "i"); + init_letter(0xce, 0xee, "i"); + init_letter(0xcf, 0xef, "i"); + + init_letter(0xd0, 0xf0, "d"); + init_letter(0xd1, 0xf1, "n"); + init_letter(0xd2, 0xf2, "o"); + init_letter(0xd3, 0xf3, "o"); + init_letter(0xd4, 0xf4, "o"); + init_letter(0xd5, 0xf5, "o"); + init_letter(0xd6, 0xf6, "o"); + init_letter(0xd8, 0xf8, "o"); + init_letter(0xd9, 0xf9, "u"); + init_letter(0xda, 0xfa, "u"); + init_letter(0xdb, 0xfb, "u"); + init_letter(0xdc, 0xfc, "u"); + init_letter(0xdd, 0xfd, "y"); + init_letter(0xde, 0xfe, THORN_SORT_KEY); + + store_token("\337", TOKEN_LOWER, "ss", "SS"); + store_token("\377", TOKEN_LOWER, "y", "Y"); +} + +static void init_two_char_letter(char l1, char l2, char u1, char u2, + const char *sk = 0) +{ + char buf[6]; + buf[0] = '\\'; + buf[1] = '('; + buf[2] = l1; + buf[3] = l2; + buf[4] = '\0'; + const char *p = strsave(buf); + buf[2] = u1; + buf[3] = u2; + store_letter(p, strsave(buf), sk); + buf[1] = '['; + buf[4] = ']'; + buf[5] = '\0'; + p = strsave(buf); + buf[2] = l1; + buf[3] = l2; + store_letter(strsave(buf), p, sk); + +} + +static void init_special_chars() +{ + const char *p; + for (p = "':^`~"; *p; p++) + for (const char *q = "aeiouy"; *q; q++) { + // Use a variable to work around bug in gcc 2.0 + char c = cmupper(*q); + init_two_char_letter(*p, *q, *p, c); + } + for (p = "/l/o~n,coeaeij"; *p; p += 2) { + // Use variables to work around bug in gcc 2.0 + char c0 = cmupper(p[0]); + char c1 = cmupper(p[1]); + init_two_char_letter(p[0], p[1], c0, c1); + } + init_two_char_letter('v', 's', 'v', 'S', "s"); + init_two_char_letter('v', 'z', 'v', 'Z', "z"); + init_two_char_letter('o', 'a', 'o', 'A', "a"); + init_two_char_letter('T', 'p', 'T', 'P', THORN_SORT_KEY); + init_two_char_letter('-', 'd', '-', 'D'); + + store_token("\\(ss", TOKEN_LOWER, 0, "SS"); + store_token("\\[ss]", TOKEN_LOWER, 0, "SS"); + + store_token("\\(Sd", TOKEN_LOWER, "d", "\\(-D"); + store_token("\\[Sd]", TOKEN_LOWER, "d", "\\[-D]"); + store_token("\\(hy", TOKEN_HYPHEN); + store_token("\\[hy]", TOKEN_HYPHEN); + store_token("\\(en", TOKEN_RANGE_SEP); + store_token("\\[en]", TOKEN_RANGE_SEP); +} + +static void init_strings() +{ + char buf[6]; + buf[0] = '\\'; + buf[1] = '*'; + for (const char *p = "'`^^,:~v_o./;"; *p; p++) { + buf[2] = *p; + buf[3] = '\0'; + store_token(strsave(buf), TOKEN_ACCENT); + buf[2] = '['; + buf[3] = *p; + buf[4] = ']'; + buf[5] = '\0'; + store_token(strsave(buf), TOKEN_ACCENT); + } + + // -ms special letters + store_letter("\\*(th", "\\*(Th", THORN_SORT_KEY); + store_letter("\\*[th]", "\\*[Th]", THORN_SORT_KEY); + store_letter("\\*(d-", "\\*(D-"); + store_letter("\\*[d-]", "\\*[D-]"); + store_letter("\\*(ae", "\\*(Ae", "ae"); + store_letter("\\*[ae]", "\\*[Ae]", "ae"); + store_letter("\\*(oe", "\\*(Oe", "oe"); + store_letter("\\*[oe]", "\\*[Oe]", "oe"); + + store_token("\\*3", TOKEN_LOWER, "y", "Y"); + store_token("\\*8", TOKEN_LOWER, "ss", "SS"); + store_token("\\*q", TOKEN_LOWER, "o", "O"); +} + +struct token_initer { + token_initer(); +}; + +static token_initer the_token_initer; + +token_initer::token_initer() +{ + init_ascii(); + init_latin1(); + init_special_chars(); + init_strings(); + default_token_info.set(TOKEN_OTHER); +} diff --git a/contrib/groff/src/preproc/refer/token.h b/contrib/groff/src/preproc/refer/token.h new file mode 100644 index 0000000..6da430d --- /dev/null +++ b/contrib/groff/src/preproc/refer/token.h @@ -0,0 +1,88 @@ +// -*- 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. */ + +enum token_type { + TOKEN_OTHER, + TOKEN_UPPER, + TOKEN_LOWER, + TOKEN_ACCENT, + TOKEN_PUNCT, + TOKEN_HYPHEN, + TOKEN_RANGE_SEP +}; + +class token_info { +private: + token_type type; + const char *sort_key; + const char *other_case; +public: + token_info(); + void set(token_type, const char *sk = 0, const char *oc = 0); + void lower_case(const char *start, const char *end, string &result) const; + void upper_case(const char *start, const char *end, string &result) const; + void sortify(const char *start, const char *end, string &result) const; + int sortify_non_empty(const char *start, const char *end) const; + int is_upper() const; + int is_lower() const; + int is_accent() const; + int is_other() const; + int is_punct() const; + int is_hyphen() const; + int is_range_sep() const; +}; + +inline int token_info::is_upper() const +{ + return type == TOKEN_UPPER; +} + +inline int token_info::is_lower() const +{ + return type == TOKEN_LOWER; +} + +inline int token_info::is_accent() const +{ + return type == TOKEN_ACCENT; +} + +inline int token_info::is_other() const +{ + return type == TOKEN_OTHER; +} + +inline int token_info::is_punct() const +{ + return type == TOKEN_PUNCT; +} + +inline int token_info::is_hyphen() const +{ + return type == TOKEN_HYPHEN; +} + +inline int token_info::is_range_sep() const +{ + return type == TOKEN_RANGE_SEP; +} + +int get_token(const char **ptr, const char *end); +const token_info *lookup_token(const char *start, const char *end); diff --git a/contrib/groff/src/preproc/soelim/Makefile.sub b/contrib/groff/src/preproc/soelim/Makefile.sub new file mode 100644 index 0000000..77007e2 --- /dev/null +++ b/contrib/groff/src/preproc/soelim/Makefile.sub @@ -0,0 +1,6 @@ +PROG=soelim +MAN1=soelim.n +XLIBS=$(LIBGROFF) +OBJS=soelim.o +CCSRCS=$(srcdir)/soelim.cc +NAMEPREFIX=$(g) diff --git a/contrib/groff/src/preproc/soelim/TODO b/contrib/groff/src/preproc/soelim/TODO new file mode 100644 index 0000000..f2a3924 --- /dev/null +++ b/contrib/groff/src/preproc/soelim/TODO @@ -0,0 +1 @@ +Understand .pso. diff --git a/contrib/groff/src/preproc/soelim/soelim.cc b/contrib/groff/src/preproc/soelim/soelim.cc new file mode 100644 index 0000000..e05f240 --- /dev/null +++ b/contrib/groff/src/preproc/soelim/soelim.cc @@ -0,0 +1,347 @@ +// -*- C++ -*- +/* Copyright (C) 1989-1992, 2000, 2001 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 <stdio.h> +#include <ctype.h> +#include <string.h> +#include <assert.h> +#include <stdlib.h> +#include <errno.h> +#include "lib.h" +#include "errarg.h" +#include "error.h" +#include "stringclass.h" +#include "nonposix.h" + +static int include_list_length; +static char **include_list; + +int compatible_flag = 0; + +extern int interpret_lf_args(const char *); + +int do_file(const char *filename); + + +static void +include_path_append(char *path) +{ + ++include_list_length; + size_t nbytes = include_list_length * sizeof(char *); + if (include_list) + include_list = (char **)realloc((void *)include_list, nbytes); + else + include_list = (char **)malloc(nbytes); + if (include_list == NULL) + { + fprintf(stderr, "%s: out of memory\n", program_name); + exit(2); + } + include_list[include_list_length - 1] = path; +} + + +void usage(FILE *stream) +{ + fprintf(stream, "usage: %s [ -vC ] [ -I file ] [ files ]\n", program_name); +} + +int main(int argc, char **argv) +{ + program_name = argv[0]; + include_path_append("."); + int opt; + static const struct option long_options[] = { + { "help", no_argument, 0, CHAR_MAX + 1 }, + { "version", no_argument, 0, 'v' }, + { NULL, 0, 0, 0 } + }; + while ((opt = getopt_long(argc, argv, "CI:v", long_options, NULL)) != EOF) + switch (opt) { + case 'v': + { + extern const char *Version_string; + printf("GNU soelim (groff) version %s\n", Version_string); + exit(0); + break; + } + case 'C': + compatible_flag = 1; + break; + case 'I': + include_path_append(optarg); + break; + case CHAR_MAX + 1: // --help + usage(stdout); + exit(0); + break; + case '?': + usage(stderr); + exit(1); + break; + default: + assert(0); + } + int nbad = 0; + if (optind >= argc) + nbad += !do_file("-"); + else + for (int i = optind; i < argc; i++) + nbad += !do_file(argv[i]); + if (ferror(stdout) || fflush(stdout) < 0) + fatal("output error"); + return nbad != 0; +} + +void set_location() +{ + printf(".lf %d %s\n", current_lineno, current_filename); +} + +void do_so(const char *line) +{ + const char *p = line; + while (*p == ' ') + p++; + string filename; + int success = 1; + for (const char *q = p; + success && *q != '\0' && *q != '\n' && *q != ' '; + q++) + if (*q == '\\') { + switch (*++q) { + case 'e': + case '\\': + filename += '\\'; + break; + case ' ': + filename += ' '; + break; + default: + success = 0; + break; + } + } + else + filename += char(*q); + if (success && filename.length() > 0) { + filename += '\0'; + const char *fn = current_filename; + int ln = current_lineno; + current_lineno--; + if (do_file(filename.contents())) { + current_filename = fn; + current_lineno = ln; + set_location(); + return; + } + current_lineno++; + } + fputs(".so", stdout); + fputs(line, stdout); +} + +int do_file(const char *filename) +{ + FILE *fp; + string whole_filename; + if (strcmp(filename, "-") == 0) { + fp = stdin; + whole_filename = filename; + whole_filename += '\0'; + } + else if (IS_ABSOLUTE(filename)) { + whole_filename = filename; + whole_filename += '\0'; + errno = 0; + fp = fopen(filename, "r"); + if (fp == 0) { + error("can't open `%1': %2", filename, strerror(errno)); + return 0; + } + } + else { + size_t j; + for (j = 0; j < include_list_length; ++j) + { + char *path = include_list[j]; + if (0 == strcmp(path, ".")) + whole_filename = filename; + else + whole_filename = string(path) + "/" + filename; + whole_filename += '\0'; + errno = 0; + fp = fopen(whole_filename.contents(), "r"); + if (fp != 0) + break; + if (errno != ENOENT) { + error("can't open `%1': %2", + whole_filename.contents(), strerror(errno)); + return 0; + } + } + if (j >= include_list_length) + { + errno = ENOENT; + error("can't open `%1': %2", filename, strerror(errno)); + return 0; + } + } + current_filename = whole_filename.contents(); + current_lineno = 1; + set_location(); + enum { START, MIDDLE, HAD_DOT, HAD_s, HAD_so, HAD_l, HAD_lf } state = START; + for (;;) { + int c = getc(fp); + if (c == EOF) + break; + switch (state) { + case START: + if (c == '.') + state = HAD_DOT; + else { + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case MIDDLE: + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + break; + case HAD_DOT: + if (c == 's') + state = HAD_s; + else if (c == 'l') + state = HAD_l; + else { + putchar('.'); + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case HAD_s: + if (c == 'o') + state = HAD_so; + else { + putchar('.'); + putchar('s'); + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case HAD_so: + if (c == ' ' || c == '\n' || compatible_flag) { + string line; + for (; c != EOF && c != '\n'; c = getc(fp)) + line += c; + current_lineno++; + line += '\n'; + line += '\0'; + do_so(line.contents()); + state = START; + } + else { + fputs(".so", stdout); + putchar(c); + state = MIDDLE; + } + break; + case HAD_l: + if (c == 'f') + state = HAD_lf; + else { + putchar('.'); + putchar('l'); + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case HAD_lf: + if (c == ' ' || c == '\n' || compatible_flag) { + string line; + for (; c != EOF && c != '\n'; c = getc(fp)) + line += c; + current_lineno++; + line += '\n'; + line += '\0'; + interpret_lf_args(line.contents()); + printf(".lf%s", line.contents()); + state = START; + } + else { + fputs(".lf", stdout); + putchar(c); + state = MIDDLE; + } + break; + default: + assert(0); + } + } + switch (state) { + case HAD_DOT: + fputs(".\n", stdout); + break; + case HAD_l: + fputs(".l\n", stdout); + break; + case HAD_s: + fputs(".s\n", stdout); + break; + case HAD_lf: + fputs(".lf\n", stdout); + break; + case HAD_so: + fputs(".so\n", stdout); + break; + case MIDDLE: + putc('\n', stdout); + break; + case START: + break; + } + if (fp != stdin) + fclose(fp); + current_filename = 0; + return 1; +} diff --git a/contrib/groff/src/preproc/soelim/soelim.man b/contrib/groff/src/preproc/soelim/soelim.man new file mode 100644 index 0000000..b97ea61 --- /dev/null +++ b/contrib/groff/src/preproc/soelim/soelim.man @@ -0,0 +1,85 @@ +.ig \"-*- nroff -*- +Copyright (C) 1989-2000 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the Free Software Foundation instead of in +the original English. +.. +.TH @G@SOELIM @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" +.SH NAME +@g@soelim \- interpret .so requests in groff input +.SH SYNOPSIS +.B @g@soelim +[ +.B \-Cv +] +[ +.BI \-I dir +] +[ +.IR files \|.\|.\|.\| +] +.PP +It is possible to have whitespace between the +.B \-I +command line option and its parameter. +.SH DESCRIPTION +.B @g@soelim +reads +.I files +and replaces lines of the form +.IP +.BI .so\ file +.LP +by the contents of +.IR file . +It is useful if files included with +.B so +need to be preprocessed. +Normally, +.B @g@soelim +should be invoked with the +.B \-s +option of +.BR groff . +.PP +Note that there must be no whitespace between the leading dot and +the two characters `s' and `o'. Otherwise, only +.B groff +interprets the +.B .so +request (and +.B soelim +ignores it). +.SH OPTIONS +.TP +.B \-C +Recognize +.B .so +even when followed by a character other than space or newline. +.TP +.BI \-I dir +This option may be used to specify a directory to search for +files (both those on the command line and those named in +.B \&.so +lines). +The current directory is always searched first. +This option may be specified more than once, +the directories will be searched in the order specified. +No directory search is performed for files specified using an absolute path. +.TP +.B \-v +Print the version number. +.SH "SEE ALSO" +.BR groff (@MAN1EXT@) diff --git a/contrib/groff/src/preproc/tbl/Makefile.sub b/contrib/groff/src/preproc/tbl/Makefile.sub new file mode 100644 index 0000000..224baff --- /dev/null +++ b/contrib/groff/src/preproc/tbl/Makefile.sub @@ -0,0 +1,12 @@ +PROG=tbl +MAN1=tbl.n +XLIBS=$(LIBGROFF) +OBJS=\ + main.o \ + table.o +CCSRCS=\ + $(srcdir)/main.cc \ + $(srcdir)/table.cc +HDRS=\ + $(srcdir)/table.h +NAMEPREFIX=$(g) diff --git a/contrib/groff/src/preproc/tbl/main.cc b/contrib/groff/src/preproc/tbl/main.cc new file mode 100644 index 0000000..a08ea0b --- /dev/null +++ b/contrib/groff/src/preproc/tbl/main.cc @@ -0,0 +1,1528 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001 + 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 "table.h" +#include "htmlindicate.h" + +#define MAX_POINT_SIZE 99 +#define MAX_VERTICAL_SPACING 72 + +static int compatible_flag = 0; + +class table_input { + FILE *fp; + enum { START, MIDDLE, REREAD_T, REREAD_TE, REREAD_E, END, ERROR } state; + string unget_stack; +public: + table_input(FILE *); + int get(); + int ended() { return unget_stack.empty() && state == END; } + void unget(char); +}; + +table_input::table_input(FILE *p) +: fp(p), state(START) +{ +} + +void table_input::unget(char c) +{ + assert(c != '\0'); + unget_stack += c; + if (c == '\n') + current_lineno--; +} + +int table_input::get() +{ + int len = unget_stack.length(); + if (len != 0) { + unsigned char c = unget_stack[len - 1]; + unget_stack.set_length(len - 1); + if (c == '\n') + current_lineno++; + return c; + } + int c; + for (;;) { + switch (state) { + case START: + if ((c = getc(fp)) == '.') { + if ((c = getc(fp)) == 'T') { + if ((c = getc(fp)) == 'E') { + if (compatible_flag) { + state = END; + return EOF; + } + else { + c = getc(fp); + if (c != EOF) + ungetc(c, fp); + if (c == EOF || c == ' ' || c == '\n') { + state = END; + return EOF; + } + state = REREAD_TE; + return '.'; + } + } + else { + if (c != EOF) + ungetc(c, fp); + state = REREAD_T; + return '.'; + } + } + else { + if (c != EOF) + ungetc(c, fp); + state = MIDDLE; + return '.'; + } + } + else if (c == EOF) { + state = ERROR; + return EOF; + } + else { + if (c == '\n') + current_lineno++; + else { + state = MIDDLE; + if (c == '\0') { + error("illegal input character code 0"); + break; + } + } + return c; + } + break; + case MIDDLE: + // handle line continuation + if ((c = getc(fp)) == '\\') { + c = getc(fp); + if (c == '\n') + c = getc(fp); // perhaps state ought to be START now + else { + if (c != EOF) + ungetc(c, fp); + c = '\\'; + } + } + if (c == EOF) { + state = ERROR; + return EOF; + } + else { + if (c == '\n') { + state = START; + current_lineno++; + } + else if (c == '\0') { + error("illegal input character code 0"); + break; + } + return c; + } + case REREAD_T: + state = MIDDLE; + return 'T'; + case REREAD_TE: + state = REREAD_E; + return 'T'; + case REREAD_E: + state = MIDDLE; + return 'E'; + case END: + case ERROR: + return EOF; + } + } +} + +void process_input_file(FILE *); +void process_table(table_input &in); + +void process_input_file(FILE *fp) +{ + enum { START, MIDDLE, HAD_DOT, HAD_T, HAD_TS, HAD_l, HAD_lf } state; + state = START; + int c; + while ((c = getc(fp)) != EOF) + switch (state) { + case START: + if (c == '.') + state = HAD_DOT; + else { + if (c == '\n') + current_lineno++; + else + state = MIDDLE; + putchar(c); + } + break; + case MIDDLE: + if (c == '\n') { + current_lineno++; + state = START; + } + putchar(c); + break; + case HAD_DOT: + if (c == 'T') + state = HAD_T; + else if (c == 'l') + state = HAD_l; + else { + putchar('.'); + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case HAD_T: + if (c == 'S') + state = HAD_TS; + else { + putchar('.'); + putchar('T'); + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case HAD_TS: + if (c == ' ' || c == '\n' || compatible_flag) { + printf(".if '\\*(.T'html' \\X(table-start(\n"); + html_begin_suppress(0); + putchar('.'); + putchar('T'); + putchar('S'); + while (c != '\n') { + if (c == EOF) { + error("end of file at beginning of table"); + return; + } + putchar(c); + c = getc(fp); + } + putchar('\n'); + current_lineno++; + { + table_input input(fp); + process_table(input); + set_troff_location(current_filename, current_lineno); + if (input.ended()) { + fputs(".TE", stdout); + while ((c = getc(fp)) != '\n') { + if (c == EOF) { + printf(".if '\\*(.T'html' \\X(table-end(\n"); + html_end_suppress(0); + putchar('\n'); + return; + } + putchar(c); + } + putchar('\n'); + printf(".if '\\*(.T'html' \\X(table-end(\n"); + html_end_suppress(0); + current_lineno++; + } + } + state = START; + } + else { + fputs(".TS", stdout); + putchar(c); + state = MIDDLE; + } + break; + case HAD_l: + if (c == 'f') + state = HAD_lf; + else { + putchar('.'); + putchar('l'); + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case HAD_lf: + if (c == ' ' || c == '\n' || compatible_flag) { + string line; + while (c != EOF) { + line += c; + if (c == '\n') { + current_lineno++; + break; + } + c = getc(fp); + } + line += '\0'; + interpret_lf_args(line.contents()); + printf(".lf%s", line.contents()); + state = START; + } + else { + fputs(".lf", stdout); + putchar(c); + state = MIDDLE; + } + break; + default: + assert(0); + } + switch(state) { + case START: + break; + case MIDDLE: + putchar('\n'); + break; + case HAD_DOT: + fputs(".\n", stdout); + break; + case HAD_l: + fputs(".l\n", stdout); + break; + case HAD_T: + fputs(".T\n", stdout); + break; + case HAD_lf: + fputs(".lf\n", stdout); + break; + case HAD_TS: + fputs(".TS\n", stdout); + break; + } + if (fp != stdin) + fclose(fp); +} + +struct options { + unsigned flags; + int linesize; + char delim[2]; + char tab_char; + char decimal_point_char; + + options(); +}; + +options::options() +: flags(0), linesize(0), tab_char('\t'), decimal_point_char('.') +{ + delim[0] = delim[1] = '\0'; +} + +// Return non-zero if p and q are the same ignoring case. + +int strieq(const char *p, const char *q) +{ + for (; cmlower(*p) == cmlower(*q); p++, q++) + if (*p == '\0') + return 1; + return 0; +} + +// return 0 if we should give up in this table + +options *process_options(table_input &in) +{ + options *opt = new options; + string line; + int level = 0; + for (;;) { + int c = in.get(); + if (c == EOF) { + int i = line.length(); + while (--i >= 0) + in.unget(line[i]); + return opt; + } + if (c == '\n') { + in.unget(c); + int i = line.length(); + while (--i >= 0) + in.unget(line[i]); + return opt; + } + else if (c == '(') + level++; + else if (c == ')') + level--; + else if (c == ';' && level == 0) { + line += '\0'; + break; + } + line += c; + } + if (line.empty()) + return opt; + char *p = &line[0]; + for (;;) { + while (!csalpha(*p) && *p != '\0') + p++; + if (*p == '\0') + break; + char *q = p; + while (csalpha(*q)) + q++; + char *arg = 0; + if (*q != '(' && *q != '\0') + *q++ = '\0'; + while (csspace(*q)) + q++; + if (*q == '(') { + *q++ = '\0'; + arg = q; + while (*q != ')' && *q != '\0') + q++; + if (*q == '\0') + error("missing `)'"); + else + *q++ = '\0'; + } + if (*p == '\0') { + if (arg) + error("argument without option"); + } + else if (strieq(p, "tab")) { + if (!arg) + error("`tab' option requires argument in parentheses"); + else { + if (arg[0] == '\0' || arg[1] != '\0') + error("argument to `tab' option must be a single character"); + else + opt->tab_char = arg[0]; + } + } + else if (strieq(p, "linesize")) { + if (!arg) + error("`linesize' option requires argument in parentheses"); + else { + if (sscanf(arg, "%d", &opt->linesize) != 1) + error("bad linesize `%s'", arg); + else if (opt->linesize <= 0) { + error("linesize must be positive"); + opt->linesize = 0; + } + } + } + else if (strieq(p, "delim")) { + if (!arg) + error("`delim' option requires argument in parentheses"); + else if (arg[0] == '\0' || arg[1] == '\0' || arg[2] != '\0') + error("argument to `delim' option must be two characters"); + else { + opt->delim[0] = arg[0]; + opt->delim[1] = arg[1]; + } + } + else if (strieq(p, "center") || strieq(p, "centre")) { + if (arg) + error("`center' option does not take a argument"); + opt->flags |= table::CENTER; + } + else if (strieq(p, "expand")) { + if (arg) + error("`expand' option does not take a argument"); + opt->flags |= table::EXPAND; + } + else if (strieq(p, "box") || strieq(p, "frame")) { + if (arg) + error("`box' option does not take a argument"); + opt->flags |= table::BOX; + } + else if (strieq(p, "doublebox") || strieq(p, "doubleframe")) { + if (arg) + error("`doublebox' option does not take a argument"); + opt->flags |= table::DOUBLEBOX; + } + else if (strieq(p, "allbox")) { + if (arg) + error("`allbox' option does not take a argument"); + opt->flags |= table::ALLBOX; + } + else if (strieq(p, "nokeep")) { + if (arg) + error("`nokeep' option does not take a argument"); + opt->flags |= table::NOKEEP; + } + else if (strieq(p, "decimalpoint")) { + if (!arg) + error("`decimalpoint' option requires argument in parentheses"); + else { + if (arg[0] == '\0' || arg[1] != '\0') + error("argument to `decimalpoint' option must be a single character"); + else + opt->decimal_point_char = arg[0]; + } + } + else { + error("unrecognised global option `%1'", p); + // delete opt; + // return 0; + } + p = q; + } + return opt; +} + +entry_modifier::entry_modifier() +: vertical_alignment(CENTER), zero_width(0), stagger(0) +{ + vertical_spacing.inc = vertical_spacing.val = 0; + point_size.inc = point_size.val = 0; +} + +entry_modifier::~entry_modifier() +{ +} + +entry_format::entry_format() : type(FORMAT_LEFT) +{ +} + +entry_format::entry_format(format_type t) : type(t) +{ +} + +void entry_format::debug_print() const +{ + switch (type) { + case FORMAT_LEFT: + putc('l', stderr); + break; + case FORMAT_CENTER: + putc('c', stderr); + break; + case FORMAT_RIGHT: + putc('r', stderr); + break; + case FORMAT_NUMERIC: + putc('n', stderr); + break; + case FORMAT_ALPHABETIC: + putc('a', stderr); + break; + case FORMAT_SPAN: + putc('s', stderr); + break; + case FORMAT_VSPAN: + putc('^', stderr); + break; + case FORMAT_HLINE: + putc('_', stderr); + break; + case FORMAT_DOUBLE_HLINE: + putc('=', stderr); + break; + default: + assert(0); + break; + } + if (point_size.val != 0) { + putc('p', stderr); + if (point_size.inc > 0) + putc('+', stderr); + else if (point_size.inc < 0) + putc('-', stderr); + fprintf(stderr, "%d ", point_size.val); + } + if (vertical_spacing.val != 0) { + putc('v', stderr); + if (vertical_spacing.inc > 0) + putc('+', stderr); + else if (vertical_spacing.inc < 0) + putc('-', stderr); + fprintf(stderr, "%d ", vertical_spacing.val); + } + if (!font.empty()) { + putc('f', stderr); + put_string(font, stderr); + putc(' ', stderr); + } + switch (vertical_alignment) { + case entry_modifier::CENTER: + break; + case entry_modifier::TOP: + putc('t', stderr); + break; + case entry_modifier::BOTTOM: + putc('d', stderr); + break; + } + if (zero_width) + putc('z', stderr); + if (stagger) + putc('u', stderr); +} + +struct format { + int nrows; + int ncolumns; + int *separation; + string *width; + char *equal; + entry_format **entry; + char **vline; + + format(int nr, int nc); + ~format(); + void add_rows(int n); +}; + +format::format(int nr, int nc) : nrows(nr), ncolumns(nc) +{ + int i; + separation = ncolumns > 1 ? new int[ncolumns - 1] : 0; + for (i = 0; i < ncolumns-1; i++) + separation[i] = -1; + width = new string[ncolumns]; + equal = new char[ncolumns]; + for (i = 0; i < ncolumns; i++) + equal[i] = 0; + entry = new entry_format *[nrows]; + for (i = 0; i < nrows; i++) + entry[i] = new entry_format[ncolumns]; + vline = new char*[nrows]; + for (i = 0; i < nrows; i++) { + vline[i] = new char[ncolumns+1]; + for (int j = 0; j < ncolumns+1; j++) + vline[i][j] = 0; + } +} + +void format::add_rows(int n) +{ + int i; + char **old_vline = vline; + vline = new char*[nrows + n]; + for (i = 0; i < nrows; i++) + vline[i] = old_vline[i]; + a_delete old_vline; + for (i = 0; i < n; i++) { + vline[nrows + i] = new char[ncolumns + 1]; + for (int j = 0; j < ncolumns + 1; j++) + vline[nrows + i][j] = 0; + } + entry_format **old_entry = entry; + entry = new entry_format *[nrows + n]; + for (i = 0; i < nrows; i++) + entry[i] = old_entry[i]; + a_delete old_entry; + for (i = 0; i < n; i++) + entry[nrows + i] = new entry_format[ncolumns]; + nrows += n; +} + +format::~format() +{ + a_delete separation; + ad_delete(ncolumns) width; + a_delete equal; + for (int i = 0; i < nrows; i++) { + a_delete vline[i]; + ad_delete(ncolumns) entry[i]; + } + a_delete vline; + a_delete entry; +} + +struct input_entry_format : public entry_format { + input_entry_format *next; + string width; + int separation; + int vline; + int pre_vline; + int last_column; + int equal; + input_entry_format(format_type, input_entry_format * = 0); + ~input_entry_format(); + void debug_print(); +}; + +input_entry_format::input_entry_format(format_type t, input_entry_format *p) +: entry_format(t), next(p) +{ + separation = -1; + last_column = 0; + vline = 0; + pre_vline = 0; + equal = 0; +} + +input_entry_format::~input_entry_format() +{ +} + +void free_input_entry_format_list(input_entry_format *list) +{ + while (list) { + input_entry_format *tem = list; + list = list->next; + delete tem; + } +} + +void input_entry_format::debug_print() +{ + int i; + for (i = 0; i < pre_vline; i++) + putc('|', stderr); + entry_format::debug_print(); + if (!width.empty()) { + putc('w', stderr); + putc('(', stderr); + put_string(width, stderr); + putc(')', stderr); + } + if (equal) + putc('e', stderr); + if (separation >= 0) + fprintf(stderr, "%d", separation); + for (i = 0; i < vline; i++) + putc('|', stderr); + if (last_column) + putc(',', stderr); +} + +// Return zero if we should give up on this table. +// If this is a continuation format line, current_format will be the current +// format line. + +format *process_format(table_input &in, options *opt, + format *current_format = 0) +{ + input_entry_format *list = 0; + int c = in.get(); + for (;;) { + int pre_vline = 0; + int got_format = 0; + int got_period = 0; + format_type t; + for (;;) { + if (c == EOF) { + error("end of input while processing format"); + free_input_entry_format_list(list); + return 0; + } + switch (c) { + case 'n': + case 'N': + t = FORMAT_NUMERIC; + got_format = 1; + break; + case 'a': + case 'A': + got_format = 1; + t = FORMAT_ALPHABETIC; + break; + case 'c': + case 'C': + got_format = 1; + t = FORMAT_CENTER; + break; + case 'l': + case 'L': + got_format = 1; + t = FORMAT_LEFT; + break; + case 'r': + case 'R': + got_format = 1; + t = FORMAT_RIGHT; + break; + case 's': + case 'S': + got_format = 1; + t = FORMAT_SPAN; + break; + case '^': + got_format = 1; + t = FORMAT_VSPAN; + break; + case '_': + case '-': // tbl also accepts this + got_format = 1; + t = FORMAT_HLINE; + break; + case '=': + got_format = 1; + t = FORMAT_DOUBLE_HLINE; + break; + case '.': + got_period = 1; + break; + case '|': + pre_vline++; + break; + case ' ': + case '\t': + case '\n': + break; + default: + if (c == opt->tab_char) + break; + error("unrecognised format `%1'", char(c)); + free_input_entry_format_list(list); + return 0; + } + if (got_period) + break; + c = in.get(); + if (got_format) + break; + } + if (got_period) + break; + list = new input_entry_format(t, list); + if (pre_vline) + list->pre_vline = pre_vline; + int success = 1; + do { + switch (c) { + case 't': + case 'T': + c = in.get(); + list->vertical_alignment = entry_modifier::TOP; + break; + case 'd': + case 'D': + c = in.get(); + list->vertical_alignment = entry_modifier::BOTTOM; + break; + case 'u': + case 'U': + c = in.get(); + list->stagger = 1; + break; + case 'z': + case 'Z': + c = in.get(); + list->zero_width = 1; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + int w = 0; + do { + w = w*10 + (c - '0'); + c = in.get(); + } while (c != EOF && csdigit(c)); + list->separation = w; + } + break; + case 'f': + case 'F': + do { + c = in.get(); + } while (c == ' ' || c == '\t'); + if (c == EOF) { + error("missing font name"); + break; + } + if (c == '(') { + for (;;) { + c = in.get(); + if (c == EOF || c == ' ' || c == '\t') { + error("missing `)'"); + break; + } + if (c == ')') { + c = in.get(); + break; + } + list->font += char(c); + } + } + else { + list->font = c; + char cc = c; + c = in.get(); + if (!csdigit(cc) + && c != EOF && c != ' ' && c != '\t' && c != '.' && c != '\n') { + list->font += char(c); + c = in.get(); + } + } + break; + case 'v': + case 'V': + c = in.get(); + list->vertical_spacing.val = 0; + list->vertical_spacing.inc = 0; + if (c == '+' || c == '-') { + list->vertical_spacing.inc = (c == '+' ? 1 : -1); + c = in.get(); + } + if (c == EOF || !csdigit(c)) { + error("`v' modifier must be followed by number"); + list->vertical_spacing.inc = 0; + } + else { + do { + list->vertical_spacing.val *= 10; + list->vertical_spacing.val += c - '0'; + c = in.get(); + } while (c != EOF && csdigit(c)); + } + if (list->vertical_spacing.val > MAX_VERTICAL_SPACING + || list->vertical_spacing.val < -MAX_VERTICAL_SPACING) { + error("unreasonable point size"); + list->vertical_spacing.val = 0; + list->vertical_spacing.inc = 0; + } + break; + case 'p': + case 'P': + c = in.get(); + list->point_size.val = 0; + list->point_size.inc = 0; + if (c == '+' || c == '-') { + list->point_size.inc = (c == '+' ? 1 : -1); + c = in.get(); + } + if (c == EOF || !csdigit(c)) { + error("`p' modifier must be followed by number"); + list->point_size.inc = 0; + } + else { + do { + list->point_size.val *= 10; + list->point_size.val += c - '0'; + c = in.get(); + } while (c != EOF && csdigit(c)); + } + if (list->point_size.val > MAX_POINT_SIZE + || list->point_size.val < -MAX_POINT_SIZE) { + error("unreasonable point size"); + list->point_size.val = 0; + list->point_size.inc = 0; + } + break; + case 'w': + case 'W': + c = in.get(); + while (c == ' ' || c == '\t') + c = in.get(); + if (c == '(') { + list->width = ""; + c = in.get(); + while (c != ')') { + if (c == EOF || c == '\n') { + error("missing `)'"); + free_input_entry_format_list(list); + return 0; + } + list->width += c; + c = in.get(); + } + c = in.get(); + } + else { + if (c == '+' || c == '-') { + list->width = char(c); + c = in.get(); + } + else + list->width = ""; + if (c == EOF || !csdigit(c)) + error("bad argument for `w' modifier"); + else { + do { + list->width += char(c); + c = in.get(); + } while (c != EOF && csdigit(c)); + } + } + break; + case 'e': + case 'E': + c = in.get(); + list->equal++; + break; + case '|': + c = in.get(); + list->vline++; + break; + case 'B': + case 'b': + c = in.get(); + list->font = "B"; + break; + case 'I': + case 'i': + c = in.get(); + list->font = "I"; + break; + case ' ': + case '\t': + c = in.get(); + break; + default: + if (c == opt->tab_char) + c = in.get(); + else + success = 0; + break; + } + } while (success); + if (list->vline > 2) { + list->vline = 2; + error("more than 2 vertical bars between key letters"); + } + if (c == '\n' || c == ',') { + c = in.get(); + list->last_column = 1; + } + } + if (c == '.') { + do { + c = in.get(); + } while (c == ' ' || c == '\t'); + if (c != '\n') { + error("`.' not last character on line"); + free_input_entry_format_list(list); + return 0; + } + } + if (!list) { + error("no format"); + free_input_entry_format_list(list); + return 0; + } + list->last_column = 1; + // now reverse the list so that the first row is at the beginning + input_entry_format *rev = 0; + while (list != 0) { + input_entry_format *tem = list->next; + list->next = rev; + rev = list; + list = tem; + } + list = rev; + input_entry_format *tem; + +#if 0 + for (tem = list; tem; tem = tem->next) + tem->debug_print(); + putc('\n', stderr); +#endif + // compute number of columns and rows + int ncolumns = 0; + int nrows = 0; + int col = 0; + for (tem = list; tem; tem = tem->next) { + if (tem->last_column) { + if (col >= ncolumns) + ncolumns = col + 1; + col = 0; + nrows++; + } + else + col++; + } + int row; + format *f; + if (current_format) { + if (ncolumns > current_format->ncolumns) { + error("cannot increase the number of columns in a continued format"); + free_input_entry_format_list(list); + return 0; + } + f = current_format; + row = f->nrows; + f->add_rows(nrows); + } + else { + f = new format(nrows, ncolumns); + row = 0; + } + col = 0; + for (tem = list; tem; tem = tem->next) { + f->entry[row][col] = *tem; + if (col < ncolumns-1) { + // use the greatest separation + if (tem->separation > f->separation[col]) { + if (current_format) + error("cannot change column separation in continued format"); + else + f->separation[col] = tem->separation; + } + } + else if (tem->separation >= 0) + error("column separation specified for last column"); + if (tem->equal && !f->equal[col]) { + if (current_format) + error("cannot change which columns are equal in continued format"); + else + f->equal[col] = 1; + } + if (!tem->width.empty()) { + // use the last width + if (!f->width[col].empty() && f->width[col] != tem->width) + error("multiple widths for column %1", col+1); + f->width[col] = tem->width; + } + if (tem->pre_vline) { + assert(col == 0); + f->vline[row][col] = tem->pre_vline; + } + f->vline[row][col+1] = tem->vline; + if (tem->last_column) { + row++; + col = 0; + } + else + col++; + } + free_input_entry_format_list(list); + for (col = 0; col < ncolumns; col++) { + entry_format *e = f->entry[f->nrows-1] + col; + if (e->type != FORMAT_HLINE + && e->type != FORMAT_DOUBLE_HLINE + && e->type != FORMAT_SPAN) + break; + } + if (col >= ncolumns) { + error("last row of format is all lines"); + delete f; + return 0; + } + return f; +} + +table *process_data(table_input &in, format *f, options *opt) +{ + char tab_char = opt->tab_char; + int ncolumns = f->ncolumns; + int current_row = 0; + int format_index = 0; + int give_up = 0; + enum { DATA_INPUT_LINE, TROFF_INPUT_LINE, SINGLE_HLINE, DOUBLE_HLINE } type; + table *tbl = new table(ncolumns, opt->flags, opt->linesize, + opt->decimal_point_char); + if (opt->delim[0] != '\0') + tbl->set_delim(opt->delim[0], opt->delim[1]); + for (;;) { + // first determine what type of line this is + int c = in.get(); + if (c == EOF) + break; + if (c == '.') { + int d = in.get(); + if (d != EOF && csdigit(d)) { + in.unget(d); + type = DATA_INPUT_LINE; + } + else { + in.unget(d); + type = TROFF_INPUT_LINE; + } + } + else if (c == '_' || c == '=') { + int d = in.get(); + if (d == '\n') { + if (c == '_') + type = SINGLE_HLINE; + else + type = DOUBLE_HLINE; + } + else { + in.unget(d); + type = DATA_INPUT_LINE; + } + } + else { + type = DATA_INPUT_LINE; + } + switch (type) { + case DATA_INPUT_LINE: + { + string input_entry; + if (format_index >= f->nrows) + format_index = f->nrows - 1; + // A format row that is all lines doesn't use up a data line. + while (format_index < f->nrows - 1) { + int c; + for (c = 0; c < ncolumns; c++) { + entry_format *e = f->entry[format_index] + c; + if (e->type != FORMAT_HLINE + && e->type != FORMAT_DOUBLE_HLINE + // Unfortunately tbl treats a span as needing data. + // && e->type != FORMAT_SPAN + ) + break; + } + if (c < ncolumns) + break; + for (c = 0; c < ncolumns; c++) + tbl->add_entry(current_row, c, input_entry, + f->entry[format_index] + c, current_filename, + current_lineno); + tbl->add_vlines(current_row, f->vline[format_index]); + format_index++; + current_row++; + } + entry_format *line_format = f->entry[format_index]; + int col = 0; + int row_comment = 0; + for (;;) { + if (c == tab_char || c == '\n') { + int ln = current_lineno; + if (c == '\n') + --ln; + while (col < ncolumns + && line_format[col].type == FORMAT_SPAN) { + tbl->add_entry(current_row, col, "", &line_format[col], + current_filename, ln); + col++; + } + if (c == '\n' && input_entry.length() == 2 + && input_entry[0] == 'T' && input_entry[1] == '{') { + input_entry = ""; + ln++; + enum { + START, MIDDLE, GOT_T, GOT_RIGHT_BRACE, GOT_DOT, + GOT_l, GOT_lf, END + } state = START; + while (state != END) { + c = in.get(); + if (c == EOF) + break; + switch (state) { + case START: + if (c == 'T') + state = GOT_T; + else if (c == '.') + state = GOT_DOT; + else { + input_entry += c; + if (c != '\n') + state = MIDDLE; + } + break; + case GOT_T: + if (c == '}') + state = GOT_RIGHT_BRACE; + else { + input_entry += 'T'; + input_entry += c; + state = c == '\n' ? START : MIDDLE; + } + break; + case GOT_DOT: + if (c == 'l') + state = GOT_l; + else { + input_entry += '.'; + input_entry += c; + state = c == '\n' ? START : MIDDLE; + } + break; + case GOT_l: + if (c == 'f') + state = GOT_lf; + else { + input_entry += ".l"; + input_entry += c; + state = c == '\n' ? START : MIDDLE; + } + break; + case GOT_lf: + if (c == ' ' || c == '\n' || compatible_flag) { + string args; + input_entry += ".lf"; + while (c != EOF) { + args += c; + if (c == '\n') + break; + c = in.get(); + } + args += '\0'; + interpret_lf_args(args.contents()); + // remove the '\0' + args.set_length(args.length() - 1); + input_entry += args; + state = START; + } + else { + input_entry += ".lf"; + input_entry += c; + state = MIDDLE; + } + break; + case GOT_RIGHT_BRACE: + if (c == '\n' || c == tab_char) + state = END; + else { + input_entry += 'T'; + input_entry += '}'; + input_entry += c; + state = c == '\n' ? START : MIDDLE; + } + break; + case MIDDLE: + if (c == '\n') + state = START; + input_entry += c; + break; + case END: + default: + assert(0); + } + } + if (c == EOF) { + error("end of data in middle of text block"); + give_up = 1; + break; + } + } + if (col >= ncolumns) { + if (!input_entry.empty()) { + if (input_entry.length() >= 2 + && input_entry[0] == '\\' + && input_entry[1] == '"') + row_comment = 1; + else if (!row_comment) { + if (c == '\n') + in.unget(c); + input_entry += '\0'; + error("excess data entry `%1' discarded", + input_entry.contents()); + if (c == '\n') + (void)in.get(); + } + } + } + else + tbl->add_entry(current_row, col, input_entry, + &line_format[col], current_filename, ln); + col++; + if (c == '\n') + break; + input_entry = ""; + } + else + input_entry += c; + c = in.get(); + if (c == EOF) + break; + } + if (give_up) + break; + input_entry = ""; + for (; col < ncolumns; col++) + tbl->add_entry(current_row, col, input_entry, &line_format[col], + current_filename, current_lineno - 1); + tbl->add_vlines(current_row, f->vline[format_index]); + current_row++; + format_index++; + } + break; + case TROFF_INPUT_LINE: + { + string line; + int ln = current_lineno; + for (;;) { + line += c; + if (c == '\n') + break; + c = in.get(); + if (c == EOF) { + break; + } + } + tbl->add_text_line(current_row, line, current_filename, ln); + if (line.length() >= 4 + && line[0] == '.' && line[1] == 'T' && line[2] == '&') { + format *newf = process_format(in, opt, f); + if (newf == 0) + give_up = 1; + else + f = newf; + } + if (line.length() >= 3 + && line[0] == '.' && line[1] == 'f' && line[2] == 'f') { + line += '\0'; + interpret_lf_args(line.contents() + 3); + } + } + break; + case SINGLE_HLINE: + tbl->add_single_hline(current_row); + break; + case DOUBLE_HLINE: + tbl->add_double_hline(current_row); + break; + default: + assert(0); + } + if (give_up) + break; + } + if (!give_up && current_row == 0) { + error("no real data"); + give_up = 1; + } + if (give_up) { + delete tbl; + return 0; + } + // Do this here rather than at the beginning in case continued formats + // change it. + int i; + for (i = 0; i < ncolumns - 1; i++) + if (f->separation[i] >= 0) + tbl->set_column_separation(i, f->separation[i]); + for (i = 0; i < ncolumns; i++) + if (!f->width[i].empty()) + tbl->set_minimum_width(i, f->width[i]); + for (i = 0; i < ncolumns; i++) + if (f->equal[i]) + tbl->set_equal_column(i); + return tbl; +} + +void process_table(table_input &in) +{ + int c; + options *opt = 0; + format *form = 0; + table *tbl = 0; + if ((opt = process_options(in)) != 0 + && (form = process_format(in, opt)) != 0 + && (tbl = process_data(in, form, opt)) != 0) { + tbl->print(); + delete tbl; + } + else { + error("giving up on this table"); + while ((c = in.get()) != EOF) + ; + } + delete opt; + delete form; + if (!in.ended()) + error("premature end of file"); +} + +static void usage(FILE *stream) +{ + fprintf(stream, "usage: %s [ -vC ] [ files... ]\n", program_name); +} + +int main(int argc, char **argv) +{ + program_name = argv[0]; + static char stderr_buf[BUFSIZ]; + setbuf(stderr, stderr_buf); + int opt; + static const struct option long_options[] = { + { "help", no_argument, 0, CHAR_MAX + 1 }, + { "version", no_argument, 0, 'v' }, + { NULL, 0, 0, 0 } + }; + while ((opt = getopt_long(argc, argv, "vCT:", long_options, NULL)) != EOF) + switch (opt) { + case 'C': + compatible_flag = 1; + break; + case 'v': + { + extern const char *Version_string; + printf("GNU tbl (groff) version %s\n", Version_string); + exit(0); + break; + } + case 'T': + // I'm sick of getting bug reports from IRIX users + break; + case CHAR_MAX + 1: // --help + usage(stdout); + exit(0); + break; + case '?': + usage(stderr); + exit(1); + break; + default: + assert(0); + } + printf(".if !\\n(.g .ab GNU tbl requires GNU troff.\n" + ".if !dTS .ds TS\n" + ".if !dTE .ds TE\n"); + if (argc > optind) { + for (int i = optind; i < argc; i++) + if (argv[i][0] == '-' && argv[i][1] == '\0') { + current_filename = "-"; + current_lineno = 1; + printf(".lf 1 -\n"); + process_input_file(stdin); + } + else { + errno = 0; + FILE *fp = fopen(argv[i], "r"); + if (fp == 0) { + current_lineno = -1; + error("can't open `%1': %2", argv[i], strerror(errno)); + } + else { + current_lineno = 1; + current_filename = argv[i]; + printf(".lf 1 %s\n", current_filename); + process_input_file(fp); + } + } + } + else { + current_filename = "-"; + current_lineno = 1; + printf(".lf 1 -\n"); + process_input_file(stdin); + } + if (ferror(stdout) || fflush(stdout) < 0) + fatal("output error"); + return 0; +} + diff --git a/contrib/groff/src/preproc/tbl/table.cc b/contrib/groff/src/preproc/tbl/table.cc new file mode 100644 index 0000000..c7f96cd --- /dev/null +++ b/contrib/groff/src/preproc/tbl/table.cc @@ -0,0 +1,2778 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992, 2000 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 "table.h" + +#define BAR_HEIGHT ".25m" +#define DOUBLE_LINE_SEP "2p" +#define HALF_DOUBLE_LINE_SEP "1p" +#define LINE_SEP "2p" +#define BODY_DEPTH ".25m" + +const int DEFAULT_COLUMN_SEPARATION = 3; + +#define DELIMITER_CHAR "\\[tbl]" +#define PREFIX "3" +#define SEPARATION_FACTOR_REG PREFIX "sep" +#define BOTTOM_REG PREFIX "bot" +#define RESET_MACRO_NAME PREFIX "init" +#define LINESIZE_REG PREFIX "lps" +#define TOP_REG PREFIX "top" +#define CURRENT_ROW_REG PREFIX "crow" +#define LAST_PASSED_ROW_REG PREFIX "passed" +#define TRANSPARENT_STRING_NAME PREFIX "trans" +#define QUOTE_STRING_NAME PREFIX "quote" +#define SECTION_DIVERSION_NAME PREFIX "section" +#define SECTION_DIVERSION_FLAG_REG PREFIX "sflag" +#define SAVED_VERTICAL_POS_REG PREFIX "vert" +#define NEED_BOTTOM_RULE_REG PREFIX "brule" +#define KEEP_MACRO_NAME PREFIX "keep" +#define RELEASE_MACRO_NAME PREFIX "release" +#define SAVED_FONT_REG PREFIX "fnt" +#define SAVED_SIZE_REG PREFIX "sz" +#define SAVED_FILL_REG PREFIX "fll" +#define SAVED_INDENT_REG PREFIX "ind" +#define SAVED_CENTER_REG PREFIX "cent" +#define TABLE_DIVERSION_NAME PREFIX "table" +#define TABLE_DIVERSION_FLAG_REG PREFIX "tflag" +#define TABLE_KEEP_MACRO_NAME PREFIX "tkeep" +#define TABLE_RELEASE_MACRO_NAME PREFIX "trelease" +#define NEEDED_REG PREFIX "needed" +#define REPEATED_MARK_MACRO PREFIX "rmk" +#define REPEATED_VPT_MACRO PREFIX "rvpt" +#define SUPPRESS_BOTTOM_REG PREFIX "supbot" +#define SAVED_DN_REG PREFIX "dn" + +// this must be one character +#define COMPATIBLE_REG PREFIX "c" + +#define BLOCK_WIDTH_PREFIX PREFIX "tbw" +#define BLOCK_DIVERSION_PREFIX PREFIX "tbd" +#define BLOCK_HEIGHT_PREFIX PREFIX "tbh" +#define SPAN_WIDTH_PREFIX PREFIX "w" +#define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw" +#define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw" +#define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw" +#define COLUMN_SEPARATION_PREFIX PREFIX "cs" +#define ROW_START_PREFIX PREFIX "rs" +#define COLUMN_START_PREFIX PREFIX "cl" +#define COLUMN_END_PREFIX PREFIX "ce" +#define COLUMN_DIVIDE_PREFIX PREFIX "cd" +#define ROW_TOP_PREFIX PREFIX "rt" + +string block_width_reg(int r, int c); +string block_diversion_name(int r, int c); +string block_height_reg(int r, int c); +string span_width_reg(int start_col, int end_col); +string span_left_numeric_width_reg(int start_col, int end_col); +string span_right_numeric_width_reg(int start_col, int end_col); +string span_alphabetic_width_reg(int start_col, int end_col); +string column_separation_reg(int col); +string row_start_reg(int r); +string column_start_reg(int c); +string column_end_reg(int c); +string column_divide_reg(int c); +string row_top_reg(int r); + +void set_inline_modifier(const entry_modifier *); +void restore_inline_modifier(const entry_modifier *m); +void set_modifier(const entry_modifier *); +int find_decimal_point(const char *s, char decimal_point_char, + const char *delim); + +string an_empty_string; +int location_force_filename = 0; + +void printfs(const char *, + const string &arg1 = an_empty_string, + const string &arg2 = an_empty_string, + const string &arg3 = an_empty_string, + const string &arg4 = an_empty_string, + const string &arg5 = an_empty_string); + +void prints(const string &); + +inline void prints(char c) +{ + putchar(c); +} + +inline void prints(const char *s) +{ + fputs(s, stdout); +} + +void prints(const string &s) +{ + if (!s.empty()) + fwrite(s.contents(), 1, s.length(), stdout); +} + +struct horizontal_span { + horizontal_span *next; + short start_col; + short end_col; + horizontal_span(int, int, horizontal_span *); +}; + +struct single_line_entry; +struct double_line_entry; +struct simple_entry; + +class table_entry { +friend class table; + table_entry *next; + int input_lineno; + const char *input_filename; +protected: + int start_row; + int end_row; + short start_col; + short end_col; + const entry_modifier *mod; +public: + void set_location(); + table_entry(const entry_modifier *); + virtual ~table_entry(); + virtual int divert(int ncols, const string *mw, int *sep); + virtual void do_width(); + virtual void do_depth(); + virtual void print() = 0; + virtual void position_vertically() = 0; + virtual single_line_entry *to_single_line_entry(); + virtual double_line_entry *to_double_line_entry(); + virtual simple_entry *to_simple_entry(); + virtual int line_type(); + virtual void note_double_vrule_on_right(int); + virtual void note_double_vrule_on_left(int); +}; + +class simple_entry : public table_entry { +public: + simple_entry(const entry_modifier *); + void print(); + void position_vertically(); + simple_entry *to_simple_entry(); + virtual void add_tab(); + virtual void simple_print(int); +}; + +class empty_entry : public simple_entry { +public: + empty_entry(const entry_modifier *); + int line_type(); +}; + +class text_entry : public simple_entry { +protected: + char *contents; + void print_contents(); +public: + text_entry(char *, const entry_modifier *); + ~text_entry(); +}; + +void text_entry::print_contents() +{ + set_inline_modifier(mod); + prints(contents); + restore_inline_modifier(mod); +} + +class repeated_char_entry : public text_entry { +public: + repeated_char_entry(char *s, const entry_modifier *m); + void simple_print(int); +}; + +class simple_text_entry : public text_entry { +public: + simple_text_entry(char *s, const entry_modifier *m); + void do_width(); +}; + +class left_text_entry : public simple_text_entry { +public: + left_text_entry(char *s, const entry_modifier *m); + void simple_print(int); + void add_tab(); +}; + +class right_text_entry : public simple_text_entry { +public: + right_text_entry(char *s, const entry_modifier *m); + void simple_print(int); + void add_tab(); +}; + +class center_text_entry : public simple_text_entry { +public: + center_text_entry(char *s, const entry_modifier *m); + void simple_print(int); + void add_tab(); +}; + +class numeric_text_entry : public text_entry { + int dot_pos; +public: + numeric_text_entry(char *s, const entry_modifier *m, int pos); + void do_width(); + void simple_print(int); +}; + +class alphabetic_text_entry : public text_entry { +public: + alphabetic_text_entry(char *s, const entry_modifier *m); + void do_width(); + void simple_print(int); + void add_tab(); +}; + +class line_entry : public simple_entry { +protected: + char double_vrule_on_right; + char double_vrule_on_left; +public: + line_entry(const entry_modifier *); + void note_double_vrule_on_right(int); + void note_double_vrule_on_left(int); + void simple_print(int) = 0; +}; + +class single_line_entry : public line_entry { +public: + single_line_entry(const entry_modifier *m); + void simple_print(int); + single_line_entry *to_single_line_entry(); + int line_type(); +}; + +class double_line_entry : public line_entry { +public: + double_line_entry(const entry_modifier *m); + void simple_print(int); + double_line_entry *to_double_line_entry(); + int line_type(); +}; + +class short_line_entry : public simple_entry { +public: + short_line_entry(const entry_modifier *m); + void simple_print(int); + int line_type(); +}; + +class short_double_line_entry : public simple_entry { +public: + short_double_line_entry(const entry_modifier *m); + void simple_print(int); + int line_type(); +}; + +class block_entry : public table_entry { + char *contents; +protected: + void do_divert(int alphabetic, int ncols, const string *mw, int *sep); +public: + block_entry(char *s, const entry_modifier *m); + ~block_entry(); + int divert(int ncols, const string *mw, int *sep); + void do_width(); + void do_depth(); + void position_vertically(); + void print() = 0; +}; + +class left_block_entry : public block_entry { +public: + left_block_entry(char *s, const entry_modifier *m); + void print(); +}; + +class right_block_entry : public block_entry { +public: + right_block_entry(char *s, const entry_modifier *m); + void print(); +}; + +class center_block_entry : public block_entry { +public: + center_block_entry(char *s, const entry_modifier *m); + void print(); +}; + +class alphabetic_block_entry : public block_entry { +public: + alphabetic_block_entry(char *s, const entry_modifier *m); + void print(); + int divert(int ncols, const string *mw, int *sep); +}; + +table_entry::table_entry(const entry_modifier *m) +: next(0), input_lineno(-1), input_filename(0), + start_row(-1), end_row(-1), start_col(-1), end_col(-1), mod(m) +{ +} + +table_entry::~table_entry() +{ +} + +int table_entry::divert(int, const string *, int *) +{ + return 0; +} + +void table_entry::do_width() +{ +} + +single_line_entry *table_entry::to_single_line_entry() +{ + return 0; +} + +double_line_entry *table_entry::to_double_line_entry() +{ + return 0; +} + +simple_entry *table_entry::to_simple_entry() +{ + return 0; +} + +void table_entry::do_depth() +{ +} + +void table_entry::set_location() +{ + set_troff_location(input_filename, input_lineno); +} + +int table_entry::line_type() +{ + return -1; +} + +void table_entry::note_double_vrule_on_right(int) +{ +} + +void table_entry::note_double_vrule_on_left(int) +{ +} + +simple_entry::simple_entry(const entry_modifier *m) : table_entry(m) +{ +} + +void simple_entry::add_tab() +{ + // do nothing +} + +void simple_entry::simple_print(int) +{ + // do nothing +} + +void simple_entry::position_vertically() +{ + if (start_row != end_row) + switch (mod->vertical_alignment) { + case entry_modifier::TOP: + printfs(".sp |\\n[%1]u\n", row_start_reg(start_row)); + break; + case entry_modifier::CENTER: + // Peform the motion in two stages so that the center is rounded + // vertically upwards even if net vertical motion is upwards. + printfs(".sp |\\n[%1]u\n", row_start_reg(start_row)); + printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-1v/2u\n", + row_start_reg(start_row)); + break; + case entry_modifier::BOTTOM: + printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-1v\n", + row_start_reg(start_row)); + break; + default: + assert(0); + } +} + +void simple_entry::print() +{ + prints(".ta"); + add_tab(); + prints('\n'); + set_location(); + prints("\\&"); + simple_print(0); + prints('\n'); +} + +simple_entry *simple_entry::to_simple_entry() +{ + return this; +} + +empty_entry::empty_entry(const entry_modifier *m) +: simple_entry(m) +{ +} + +int empty_entry::line_type() +{ + return 0; +} + +text_entry::text_entry(char *s, const entry_modifier *m) +: simple_entry(m), contents(s) +{ +} + +text_entry::~text_entry() +{ + a_delete contents; +} + + +repeated_char_entry::repeated_char_entry(char *s, const entry_modifier *m) +: text_entry(s, m) +{ +} + +void repeated_char_entry::simple_print(int) +{ + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + set_inline_modifier(mod); + printfs("\\l" DELIMITER_CHAR "\\n[%1]u\\&", + span_width_reg(start_col, end_col)); + prints(contents); + prints(DELIMITER_CHAR); + restore_inline_modifier(mod); +} + +simple_text_entry::simple_text_entry(char *s, const entry_modifier *m) +: text_entry(s, m) +{ +} + +void simple_text_entry::do_width() +{ + set_location(); + printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR, + span_width_reg(start_col, end_col)); + print_contents(); + prints(DELIMITER_CHAR "\n"); +} + +left_text_entry::left_text_entry(char *s, const entry_modifier *m) +: simple_text_entry(s, m) +{ +} + +void left_text_entry::simple_print(int) +{ + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + print_contents(); +} + +// The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr. + +void left_text_entry::add_tab() +{ + printfs(" \\n[%1]u", column_end_reg(end_col)); +} + +right_text_entry::right_text_entry(char *s, const entry_modifier *m) +: simple_text_entry(s, m) +{ +} + +void right_text_entry::simple_print(int) +{ + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + prints("\002\003"); + print_contents(); + prints("\002"); +} + +void right_text_entry::add_tab() +{ + printfs(" \\n[%1]u", column_end_reg(end_col)); +} + +center_text_entry::center_text_entry(char *s, const entry_modifier *m) +: simple_text_entry(s, m) +{ +} + +void center_text_entry::simple_print(int) +{ + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + prints("\002\003"); + print_contents(); + prints("\003\002"); +} + +void center_text_entry::add_tab() +{ + printfs(" \\n[%1]u", column_end_reg(end_col)); +} + +numeric_text_entry::numeric_text_entry(char *s, const entry_modifier *m, int pos) +: text_entry(s, m), dot_pos(pos) +{ +} + +void numeric_text_entry::do_width() +{ + if (dot_pos != 0) { + set_location(); + printfs(".nr %1 0\\w" DELIMITER_CHAR, + block_width_reg(start_row, start_col)); + set_inline_modifier(mod); + for (int i = 0; i < dot_pos; i++) + prints(contents[i]); + restore_inline_modifier(mod); + prints(DELIMITER_CHAR "\n"); + printfs(".nr %1 \\n[%1]>?\\n[%2]\n", + span_left_numeric_width_reg(start_col, end_col), + block_width_reg(start_row, start_col)); + } + else + printfs(".nr %1 0\n", block_width_reg(start_row, start_col)); + if (contents[dot_pos] != '\0') { + set_location(); + printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR, + span_right_numeric_width_reg(start_col, end_col)); + set_inline_modifier(mod); + prints(contents + dot_pos); + restore_inline_modifier(mod); + prints(DELIMITER_CHAR "\n"); + } +} + +void numeric_text_entry::simple_print(int) +{ + printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'", + span_width_reg(start_col, end_col), + span_left_numeric_width_reg(start_col, end_col), + span_right_numeric_width_reg(start_col, end_col), + column_start_reg(start_col), + block_width_reg(start_row, start_col)); + print_contents(); +} + +alphabetic_text_entry::alphabetic_text_entry(char *s, const entry_modifier *m) +: text_entry(s, m) +{ +} + +void alphabetic_text_entry::do_width() +{ + set_location(); + printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR, + span_alphabetic_width_reg(start_col, end_col)); + print_contents(); + prints(DELIMITER_CHAR "\n"); +} + +void alphabetic_text_entry::simple_print(int) +{ + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + printfs("\\h'\\n[%1]u-\\n[%2]u/2u'", + span_width_reg(start_col, end_col), + span_alphabetic_width_reg(start_col, end_col)); + print_contents(); +} + +// The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr. + +void alphabetic_text_entry::add_tab() +{ + printfs(" \\n[%1]u", column_end_reg(end_col)); +} + +block_entry::block_entry(char *s, const entry_modifier *m) +: table_entry(m), contents(s) +{ +} + +block_entry::~block_entry() +{ + a_delete contents; +} + +void block_entry::position_vertically() +{ + if (start_row != end_row) + switch(mod->vertical_alignment) { + case entry_modifier::TOP: + printfs(".sp |\\n[%1]u\n", row_start_reg(start_row)); + break; + case entry_modifier::CENTER: + // Peform the motion in two stages so that the center is rounded + // vertically upwards even if net vertical motion is upwards. + printfs(".sp |\\n[%1]u\n", row_start_reg(start_row)); + printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u/2u\n", + row_start_reg(start_row), + block_height_reg(start_row, start_col)); + break; + case entry_modifier::BOTTOM: + printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u\n", + row_start_reg(start_row), + block_height_reg(start_row, start_col)); + break; + default: + assert(0); + } + if (mod->stagger) + prints(".sp -.5v\n"); +} + +int block_entry::divert(int ncols, const string *mw, int *sep) +{ + do_divert(0, ncols, mw, sep); + return 1; +} + +void block_entry::do_divert(int alphabetic, int ncols, const string *mw, + int *sep) +{ + printfs(".di %1\n", block_diversion_name(start_row, start_col)); + prints(".if \\n[" SAVED_FILL_REG "] .fi\n" + ".in 0\n"); + prints(".ll "); + int i; + for (i = start_col; i <= end_col; i++) + if (mw[i].empty()) + break; + if (i > end_col) { + // Every column spanned by this entry has a minimum width. + for (int j = start_col; j <= end_col; j++) { + if (j > start_col) { + if (sep) + printfs("+%1n", as_string(sep[j - 1])); + prints('+'); + } + printfs("(n;%1)", mw[j]); + } + printfs(">?\\n[%1]u", span_width_reg(start_col, end_col)); + } + else + printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))", + span_width_reg(start_col, end_col), + as_string(end_col - start_col + 1), + as_string(ncols + 1)); + if (alphabetic) + prints("-2n"); + prints("\n"); + set_modifier(mod); + prints(".cp \\n(" COMPATIBLE_REG "\n"); + set_location(); + prints(contents); + prints(".br\n.di\n.cp 0\n"); + if (!mod->zero_width) { + if (alphabetic) { + printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n", + span_width_reg(start_col, end_col)); + printfs(".nr %1 \\n[%1]>?\\n[dl]\n", + span_alphabetic_width_reg(start_col, end_col)); + } + else + printfs(".nr %1 \\n[%1]>?\\n[dl]\n", span_width_reg(start_col, end_col)); + } + printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row, start_col)); + printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row, start_col)); + prints("." RESET_MACRO_NAME "\n" + ".in \\n[" SAVED_INDENT_REG "]u\n" + ".nf\n"); + // the block might have contained .lf commands + location_force_filename = 1; +} + +void block_entry::do_width() +{ + // do nothing; the action happens in divert +} + +void block_entry::do_depth() +{ + printfs(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?(\\n[%1]+\\n[%2])\n", + row_start_reg(start_row), + block_height_reg(start_row, start_col)); +} + +left_block_entry::left_block_entry(char *s, const entry_modifier *m) +: block_entry(s, m) +{ +} + +void left_block_entry::print() +{ + printfs(".in +\\n[%1]u\n", column_start_reg(start_col)); + printfs(".%1\n", block_diversion_name(start_row, start_col)); + prints(".in\n"); +} + + + +right_block_entry::right_block_entry(char *s, const entry_modifier *m) +: block_entry(s, m) +{ +} + +void right_block_entry::print() +{ + printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n", + column_start_reg(start_col), + span_width_reg(start_col, end_col), + block_width_reg(start_row, start_col)); + printfs(".%1\n", block_diversion_name(start_row, start_col)); + prints(".in\n"); +} + +center_block_entry::center_block_entry(char *s, const entry_modifier *m) +: block_entry(s, m) +{ +} + +void center_block_entry::print() +{ + printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n", + column_start_reg(start_col), + span_width_reg(start_col, end_col), + block_width_reg(start_row, start_col)); + printfs(".%1\n", block_diversion_name(start_row, start_col)); + prints(".in\n"); +} + +alphabetic_block_entry::alphabetic_block_entry(char *s, + const entry_modifier *m) +: block_entry(s, m) +{ +} + +int alphabetic_block_entry::divert(int ncols, const string *mw, int *sep) +{ + do_divert(1, ncols, mw, sep); + return 1; +} + +void alphabetic_block_entry::print() +{ + printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n", + column_start_reg(start_col), + span_width_reg(start_col, end_col), + span_alphabetic_width_reg(start_col, end_col)); + printfs(".%1\n", block_diversion_name(start_row, start_col)); + prints(".in\n"); +} + +line_entry::line_entry(const entry_modifier *m) +: simple_entry(m), double_vrule_on_right(0), double_vrule_on_left(0) +{ +} + +void line_entry::note_double_vrule_on_right(int is_corner) +{ + double_vrule_on_right = is_corner ? 1 : 2; +} + +void line_entry::note_double_vrule_on_left(int is_corner) +{ + double_vrule_on_left = is_corner ? 1 : 2; +} + + +single_line_entry::single_line_entry(const entry_modifier *m) +: line_entry(m) +{ +} + +int single_line_entry::line_type() +{ + return 1; +} + +void single_line_entry::simple_print(int dont_move) +{ + printfs("\\h'|\\n[%1]u", + column_divide_reg(start_col)); + if (double_vrule_on_left) { + prints(double_vrule_on_left == 1 ? "-" : "+"); + prints(HALF_DOUBLE_LINE_SEP); + } + prints("'"); + if (!dont_move) + prints("\\v'-" BAR_HEIGHT "'"); + printfs("\\s[\\n[" LINESIZE_REG "]]" "\\D'l |\\n[%1]u", + column_divide_reg(end_col+1)); + if (double_vrule_on_right) { + prints(double_vrule_on_left == 1 ? "+" : "-"); + prints(HALF_DOUBLE_LINE_SEP); + } + prints("0'\\s0"); + if (!dont_move) + prints("\\v'" BAR_HEIGHT "'"); +} + +single_line_entry *single_line_entry::to_single_line_entry() +{ + return this; +} + +double_line_entry::double_line_entry(const entry_modifier *m) +: line_entry(m) +{ +} + +int double_line_entry::line_type() +{ + return 2; +} + +void double_line_entry::simple_print(int dont_move) +{ + if (!dont_move) + prints("\\v'-" BAR_HEIGHT "'"); + printfs("\\h'|\\n[%1]u", + column_divide_reg(start_col)); + if (double_vrule_on_left) { + prints(double_vrule_on_left == 1 ? "-" : "+"); + prints(HALF_DOUBLE_LINE_SEP); + } + prints("'"); + printfs("\\v'-" HALF_DOUBLE_LINE_SEP "'" + "\\s[\\n[" LINESIZE_REG "]]" + "\\D'l |\\n[%1]u", + column_divide_reg(end_col+1)); + if (double_vrule_on_right) + prints("-" HALF_DOUBLE_LINE_SEP); + prints(" 0'"); + printfs("\\v'" DOUBLE_LINE_SEP "'" + "\\D'l |\\n[%1]u", + column_divide_reg(start_col)); + if (double_vrule_on_right) { + prints(double_vrule_on_left == 1 ? "+" : "-"); + prints(HALF_DOUBLE_LINE_SEP); + } + prints(" 0'"); + prints("\\s0" + "\\v'-" HALF_DOUBLE_LINE_SEP "'"); + if (!dont_move) + prints("\\v'" BAR_HEIGHT "'"); +} + +double_line_entry *double_line_entry::to_double_line_entry() +{ + return this; +} + +short_line_entry::short_line_entry(const entry_modifier *m) +: simple_entry(m) +{ +} + +int short_line_entry::line_type() +{ + return 1; +} + +void short_line_entry::simple_print(int dont_move) +{ + if (mod->stagger) + prints("\\v'-.5v'"); + if (!dont_move) + prints("\\v'-" BAR_HEIGHT "'"); + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + printfs("\\s[\\n[" LINESIZE_REG "]]" + "\\D'l \\n[%1]u 0'" + "\\s0", + span_width_reg(start_col, end_col)); + if (!dont_move) + prints("\\v'" BAR_HEIGHT "'"); + if (mod->stagger) + prints("\\v'.5v'"); +} + +short_double_line_entry::short_double_line_entry(const entry_modifier *m) +: simple_entry(m) +{ +} + +int short_double_line_entry::line_type() +{ + return 2; +} + +void short_double_line_entry::simple_print(int dont_move) +{ + if (mod->stagger) + prints("\\v'-.5v'"); + if (!dont_move) + prints("\\v'-" BAR_HEIGHT "'"); + printfs("\\h'|\\n[%2]u'" + "\\v'-" HALF_DOUBLE_LINE_SEP "'" + "\\s[\\n[" LINESIZE_REG "]]" + "\\D'l \\n[%1]u 0'" + "\\v'" DOUBLE_LINE_SEP "'" + "\\D'l |\\n[%2]u 0'" + "\\s0" + "\\v'-" HALF_DOUBLE_LINE_SEP "'", + span_width_reg(start_col, end_col), + column_start_reg(start_col)); + if (!dont_move) + prints("\\v'" BAR_HEIGHT "'"); + if (mod->stagger) + prints("\\v'.5v'"); +} + +void set_modifier(const entry_modifier *m) +{ + if (!m->font.empty()) + printfs(".ft %1\n", m->font); + if (m->point_size.val != 0) { + prints(".ps "); + if (m->point_size.inc > 0) + prints('+'); + else if (m->point_size.inc < 0) + prints('-'); + printfs("%1\n", as_string(m->point_size.val)); + } + if (m->vertical_spacing.val != 0) { + prints(".vs "); + if (m->vertical_spacing.inc > 0) + prints('+'); + else if (m->vertical_spacing.inc < 0) + prints('-'); + printfs("%1\n", as_string(m->vertical_spacing.val)); + } +} + +void set_inline_modifier(const entry_modifier *m) +{ + if (!m->font.empty()) + printfs("\\f[%1]", m->font); + if (m->point_size.val != 0) { + prints("\\s["); + if (m->point_size.inc > 0) + prints('+'); + else if (m->point_size.inc < 0) + prints('-'); + printfs("%1]", as_string(m->point_size.val)); + } + if (m->stagger) + prints("\\v'-.5v'"); +} + +void restore_inline_modifier(const entry_modifier *m) +{ + if (!m->font.empty()) + prints("\\f[\\n[" SAVED_FONT_REG "]]"); + if (m->point_size.val != 0) + prints("\\s[\\n[" SAVED_SIZE_REG "]]"); + if (m->stagger) + prints("\\v'.5v'"); +} + + +struct stuff { + stuff *next; + int row; // occurs before row `row' + char printed; // has it been printed? + + stuff(int); + virtual void print(table *) = 0; + virtual ~stuff(); + virtual int is_single_line() { return 0; }; + virtual int is_double_line() { return 0; }; +}; + +stuff::stuff(int r) : next(0), row(r), printed(0) +{ +} + +stuff::~stuff() +{ +} + +struct text_stuff : public stuff { + string contents; + const char *filename; + int lineno; + + text_stuff(const string &, int r, const char *fn, int ln); + ~text_stuff(); + void print(table *); +}; + + +text_stuff::text_stuff(const string &s, int r, const char *fn, int ln) +: stuff(r), contents(s), filename(fn), lineno(ln) +{ +} + +text_stuff::~text_stuff() +{ +} + +void text_stuff::print(table *) +{ + printed = 1; + prints(".cp \\n(" COMPATIBLE_REG "\n"); + set_troff_location(filename, lineno); + prints(contents); + prints(".cp 0\n"); + location_force_filename = 1; // it might have been a .lf command +} + +struct single_hline_stuff : public stuff { + single_hline_stuff(int r); + void print(table *); + int is_single_line(); +}; + +single_hline_stuff::single_hline_stuff(int r) : stuff(r) +{ +} + +void single_hline_stuff::print(table *tbl) +{ + printed = 1; + tbl->print_single_hline(row); +} + +int single_hline_stuff::is_single_line() +{ + return 1; +} + +struct double_hline_stuff : stuff { + double_hline_stuff(int r); + void print(table *); + int is_double_line(); +}; + +double_hline_stuff::double_hline_stuff(int r) : stuff(r) +{ +} + +void double_hline_stuff::print(table *tbl) +{ + printed = 1; + tbl->print_double_hline(row); +} + +int double_hline_stuff::is_double_line() +{ + return 1; +} + +struct vertical_rule { + vertical_rule *next; + int start_row; + int end_row; + short col; + char is_double; + string top_adjust; + string bot_adjust; + + vertical_rule(int sr, int er, int c, int dbl, vertical_rule *); + ~vertical_rule(); + void contribute_to_bottom_macro(table *); + void print(); +}; + +vertical_rule::vertical_rule(int sr, int er, int c, int dbl, vertical_rule *p) +: next(p), start_row(sr), end_row(er), col(c), is_double(dbl) +{ +} + +vertical_rule::~vertical_rule() +{ +} + +void vertical_rule::contribute_to_bottom_macro(table *tbl) +{ + printfs(".if \\n[" CURRENT_ROW_REG "]>=%1", + as_string(start_row)); + if (end_row != tbl->get_nrows() - 1) + printfs("&(\\n[" CURRENT_ROW_REG "]<%1)", + as_string(end_row)); + prints(" \\{"); + printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG "] .nr %2 \\n[#T]\n", + as_string(start_row), + row_top_reg(start_row)); + const char *offset_table[3]; + if (is_double) { + offset_table[0] = "-" HALF_DOUBLE_LINE_SEP; + offset_table[1] = "+" HALF_DOUBLE_LINE_SEP; + offset_table[2] = 0; + } + else { + offset_table[0] = ""; + offset_table[1] = 0; + } + for (const char **offsetp = offset_table; *offsetp; offsetp++) { + prints(".sp -1\n" + "\\v'" BODY_DEPTH); + if (!bot_adjust.empty()) + printfs("+%1", bot_adjust); + prints("'"); + printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG "]]\\D'l 0 |\\n[%2]u-1v", + column_divide_reg(col), + row_top_reg(start_row), + *offsetp); + if (!bot_adjust.empty()) + printfs("-(%1)", bot_adjust); + // don't perform the top adjustment if the top is actually #T + if (!top_adjust.empty()) + printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG "]))", + top_adjust, + as_string(start_row)); + prints("'\\s0\n"); + } + prints(".\\}\n"); +} + +void vertical_rule::print() +{ + printfs("\\*[" TRANSPARENT_STRING_NAME "]" + ".if %1<=\\*[" QUOTE_STRING_NAME "]\\n[" LAST_PASSED_ROW_REG "] " + ".nr %2 \\*[" QUOTE_STRING_NAME "]\\n[#T]\n", + as_string(start_row), + row_top_reg(start_row)); + const char *offset_table[3]; + if (is_double) { + offset_table[0] = "-" HALF_DOUBLE_LINE_SEP; + offset_table[1] = "+" HALF_DOUBLE_LINE_SEP; + offset_table[2] = 0; + } + else { + offset_table[0] = ""; + offset_table[1] = 0; + } + for (const char **offsetp = offset_table; *offsetp; offsetp++) { + prints("\\*[" TRANSPARENT_STRING_NAME "].sp -1\n" + "\\*[" TRANSPARENT_STRING_NAME "]\\v'" BODY_DEPTH); + if (!bot_adjust.empty()) + printfs("+%1", bot_adjust); + prints("'"); + printfs("\\h'\\n[%1]u%3'" + "\\s[\\n[" LINESIZE_REG "]]" + "\\D'l 0 |\\*[" QUOTE_STRING_NAME "]\\n[%2]u-1v", + column_divide_reg(col), + row_top_reg(start_row), + *offsetp); + if (!bot_adjust.empty()) + printfs("-(%1)", bot_adjust); + // don't perform the top adjustment if the top is actually #T + if (!top_adjust.empty()) + printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME "]\\n[" + LAST_PASSED_ROW_REG "]))", + top_adjust, + as_string(start_row)); + prints("'" + "\\s0\n"); + } +} + +table::table(int nc, unsigned f, int ls, char dpc) +: flags(f), nrows(0), ncolumns(nc), linesize(ls), decimal_point_char(dpc), + vrule_list(0), stuff_list(0), span_list(0), + entry_list(0), entry_list_tailp(&entry_list), entry(0), + vline(0), row_is_all_lines(0), left_separation(0), right_separation(0), + allocated_rows(0) +{ + minimum_width = new string[ncolumns]; + column_separation = ncolumns > 1 ? new int[ncolumns - 1] : 0; + equal = new char[ncolumns]; + int i; + for (i = 0; i < ncolumns; i++) + equal[i] = 0; + for (i = 0; i < ncolumns-1; i++) + column_separation[i] = DEFAULT_COLUMN_SEPARATION; + delim[0] = delim[1] = '\0'; +} + +table::~table() +{ + for (int i = 0; i < nrows; i++) { + a_delete entry[i]; + a_delete vline[i]; + } + a_delete entry; + a_delete vline; + while (entry_list) { + table_entry *tem = entry_list; + entry_list = entry_list->next; + delete tem; + } + ad_delete(ncolumns) minimum_width; + a_delete column_separation; + a_delete equal; + while (stuff_list) { + stuff *tem = stuff_list; + stuff_list = stuff_list->next; + delete tem; + } + while (vrule_list) { + vertical_rule *tem = vrule_list; + vrule_list = vrule_list->next; + delete tem; + } + a_delete row_is_all_lines; + while (span_list) { + horizontal_span *tem = span_list; + span_list = span_list->next; + delete tem; + } +} + +void table::set_delim(char c1, char c2) +{ + delim[0] = c1; + delim[1] = c2; +} + +void table::set_minimum_width(int c, const string &w) +{ + assert(c >= 0 && c < ncolumns); + minimum_width[c] = w; +} + +void table::set_column_separation(int c, int n) +{ + assert(c >= 0 && c < ncolumns - 1); + column_separation[c] = n; +} + +void table::set_equal_column(int c) +{ + assert(c >= 0 && c < ncolumns); + equal[c] = 1; +} + +void table::add_stuff(stuff *p) +{ + stuff **pp; + for (pp = &stuff_list; *pp; pp = &(*pp)->next) + ; + *pp = p; +} + +void table::add_text_line(int r, const string &s, const char *filename, int lineno) +{ + add_stuff(new text_stuff(s, r, filename, lineno)); +} + +void table::add_single_hline(int r) +{ + add_stuff(new single_hline_stuff(r)); +} + +void table::add_double_hline(int r) +{ + add_stuff(new double_hline_stuff(r)); +} + +void table::allocate(int r) +{ + if (r >= nrows) { + typedef table_entry **PPtable_entry; // work around g++ 1.36.1 bug + if (r >= allocated_rows) { + if (allocated_rows == 0) { + allocated_rows = 16; + if (allocated_rows <= r) + allocated_rows = r + 1; + entry = new PPtable_entry[allocated_rows]; + vline = new char*[allocated_rows]; + } + else { + table_entry ***old_entry = entry; + int old_allocated_rows = allocated_rows; + allocated_rows *= 2; + if (allocated_rows <= r) + allocated_rows = r + 1; + entry = new PPtable_entry[allocated_rows]; + memcpy(entry, old_entry, sizeof(table_entry**)*old_allocated_rows); + a_delete old_entry; + char **old_vline = vline; + vline = new char*[allocated_rows]; + memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows); + a_delete old_vline; + } + } + assert(allocated_rows > r); + while (nrows <= r) { + entry[nrows] = new table_entry*[ncolumns]; + int i; + for (i = 0; i < ncolumns; i++) + entry[nrows][i] = 0; + vline[nrows] = new char[ncolumns+1]; + for (i = 0; i < ncolumns+1; i++) + vline[nrows][i] = 0; + nrows++; + } + } +} + +void table::do_hspan(int r, int c) +{ + assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns); + if (c == 0) { + error("first column cannot be horizontally spanned"); + return; + } + table_entry *e = entry[r][c]; + if (e) { + assert(e->start_row <= r && r <= e->end_row + && e->start_col <= c && c <= e->end_col + && e->end_row - e->start_row > 0 + && e->end_col - e->start_col > 0); + return; + } + e = entry[r][c-1]; + // e can be 0 if we had an empty entry or an error + if (e == 0) + return; + if (e->start_row != r) { + /* + l l + ^ s */ + error("impossible horizontal span at row %1, column %2", r + 1, c + 1); + } + else { + e->end_col = c; + entry[r][c] = e; + } +} + +void table::do_vspan(int r, int c) +{ + assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns); + if (r == 0) { + error("first row cannot be vertically spanned"); + return; + } + table_entry *e = entry[r][c]; + if (e) { + assert(e->start_row <= r && r <= e->end_row + && e->start_col <= c && c <= e->end_col + && e->end_row - e->start_row > 0 + && e->end_col - e->start_col > 0); + return; + } + e = entry[r-1][c]; + // e can be 0 if we had an empty entry or an error + if (e == 0) + return; + if (e->start_col != c) { + /* l s + l ^ */ + error("impossible vertical span at row %1, column %2", r + 1, c + 1); + } + else { + for (int i = c; i <= e->end_col; i++) { + assert(entry[r][i] == 0); + entry[r][i] = e; + } + e->end_row = r; + } +} + +int find_decimal_point(const char *s, char decimal_point_char, + const char *delim) +{ + if (s == 0 || *s == '\0') + return -1; + const char *p; + int in_delim = 0; // is p within eqn delimiters? + // tbl recognises \& even within eqn delimiters; I don't + for (p = s; *p; p++) + if (in_delim) { + if (*p == delim[1]) + in_delim = 0; + } + else if (*p == delim[0]) + in_delim = 1; + else if (p[0] == '\\' && p[1] == '&') + return p - s; + int possible_pos = -1; + in_delim = 0; + for (p = s; *p; p++) + if (in_delim) { + if (*p == delim[1]) + in_delim = 0; + } + else if (*p == delim[0]) + in_delim = 1; + else if (p[0] == decimal_point_char && csdigit(p[1])) + possible_pos = p - s; + if (possible_pos >= 0) + return possible_pos; + in_delim = 0; + for (p = s; *p; p++) + if (in_delim) { + if (*p == delim[1]) + in_delim = 0; + } + else if (*p == delim[0]) + in_delim = 1; + else if (csdigit(*p)) + possible_pos = p + 1 - s; + return possible_pos; +} + +void table::add_entry(int r, int c, const string &str, const entry_format *f, + const char *fn, int ln) +{ + allocate(r); + table_entry *e = 0; + if (str == "\\_") { + e = new short_line_entry(f); + } + else if (str == "\\=") { + e = new short_double_line_entry(f); + } + else if (str == "_") { + single_line_entry *lefte; + if (c > 0 && entry[r][c-1] != 0 && + (lefte = entry[r][c-1]->to_single_line_entry()) != 0 + && lefte->start_row == r + && lefte->mod->stagger == f->stagger) { + lefte->end_col = c; + entry[r][c] = lefte; + } + else + e = new single_line_entry(f); + } + else if (str == "=") { + double_line_entry *lefte; + if (c > 0 && entry[r][c-1] != 0 && + (lefte = entry[r][c-1]->to_double_line_entry()) != 0 + && lefte->start_row == r + && lefte->mod->stagger == f->stagger) { + lefte->end_col = c; + entry[r][c] = lefte; + } + else + e = new double_line_entry(f); + } + else if (str == "\\^") { + do_vspan(r, c); + } + else if (str.length() > 2 && str[0] == '\\' && str[1] == 'R') { + if (str.search('\n') >= 0) + error_with_file_and_line(fn, ln, "bad repeated character"); + else { + char *s = str.substring(2, str.length() - 2).extract(); + e = new repeated_char_entry(s, f); + } + } + else { + int is_block = str.search('\n') >= 0; + char *s; + switch (f->type) { + case FORMAT_SPAN: + assert(str.empty()); + do_hspan(r, c); + break; + case FORMAT_LEFT: + if (!str.empty()) { + s = str.extract(); + if (is_block) + e = new left_block_entry(s, f); + else + e = new left_text_entry(s, f); + } + else + e = new empty_entry(f); + break; + case FORMAT_CENTER: + if (!str.empty()) { + s = str.extract(); + if (is_block) + e = new center_block_entry(s, f); + else + e = new center_text_entry(s, f); + } + else + e = new empty_entry(f); + break; + case FORMAT_RIGHT: + if (!str.empty()) { + s = str.extract(); + if (is_block) + e = new right_block_entry(s, f); + else + e = new right_text_entry(s, f); + } + else + e = new empty_entry(f); + break; + case FORMAT_NUMERIC: + if (!str.empty()) { + s = str.extract(); + if (is_block) { + error_with_file_and_line(fn, ln, "can't have numeric text block"); + e = new left_block_entry(s, f); + } + else { + int pos = find_decimal_point(s, decimal_point_char, delim); + if (pos < 0) + e = new center_text_entry(s, f); + else + e = new numeric_text_entry(s, f, pos); + } + } + else + e = new empty_entry(f); + break; + case FORMAT_ALPHABETIC: + if (!str.empty()) { + s = str.extract(); + if (is_block) + e = new alphabetic_block_entry(s, f); + else + e = new alphabetic_text_entry(s, f); + } + else + e = new empty_entry(f); + break; + case FORMAT_VSPAN: + do_vspan(r, c); + break; + case FORMAT_HLINE: + if (str.length() != 0) + error_with_file_and_line(fn, ln, + "non-empty data entry for `_' format ignored"); + e = new single_line_entry(f); + break; + case FORMAT_DOUBLE_HLINE: + if (str.length() != 0) + error_with_file_and_line(fn, ln, + "non-empty data entry for `=' format ignored"); + e = new double_line_entry(f); + break; + default: + assert(0); + } + } + if (e) { + table_entry *preve = entry[r][c]; + if (preve) { + /* c s + ^ l */ + error_with_file_and_line(fn, ln, "row %1, column %2 already spanned", + r + 1, c + 1); + delete e; + } + else { + e->input_lineno = ln; + e->input_filename = fn; + e->start_row = e->end_row = r; + e->start_col = e->end_col = c; + *entry_list_tailp = e; + entry_list_tailp = &e->next; + entry[r][c] = e; + } + } +} + +// add vertical lines for row r + +void table::add_vlines(int r, const char *v) +{ + allocate(r); + for (int i = 0; i < ncolumns+1; i++) + vline[r][i] = v[i]; +} + +void table::check() +{ + table_entry *p = entry_list; + int i, j; + while (p) { + for (i = p->start_row; i <= p->end_row; i++) + for (j = p->start_col; j <= p->end_col; j++) + assert(entry[i][j] == p); + p = p->next; + } +} + +void table::print() +{ + location_force_filename = 1; + check(); + init_output(); + determine_row_type(); + compute_widths(); + if (!(flags & CENTER)) + prints(".if \\n[" SAVED_CENTER_REG "] \\{"); + prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2>?-\\n[.i])\n" + ".nr " SAVED_INDENT_REG " \\n[.i]\n"); + if (!(flags & CENTER)) + prints(".\\}\n"); + build_vrule_list(); + define_bottom_macro(); + do_top(); + for (int i = 0; i < nrows; i++) + do_row(i); + do_bottom(); +} + +void table::determine_row_type() +{ + row_is_all_lines = new char[nrows]; + for (int i = 0; i < nrows; i++) { + int had_single = 0; + int had_double = 0; + int had_non_line = 0; + for (int c = 0; c < ncolumns; c++) { + table_entry *e = entry[i][c]; + if (e != 0) { + if (e->start_row == e->end_row) { + int t = e->line_type(); + switch (t) { + case -1: + had_non_line = 1; + break; + case 0: + // empty + break; + case 1: + had_single = 1; + break; + case 2: + had_double = 1; + break; + default: + assert(0); + } + if (had_non_line) + break; + } + c = e->end_col; + } + } + if (had_non_line) + row_is_all_lines[i] = 0; + else if (had_double) + row_is_all_lines[i] = 2; + else if (had_single) + row_is_all_lines[i] = 1; + else + row_is_all_lines[i] = 0; + } +} + + +void table::init_output() +{ + prints(".nr " COMPATIBLE_REG " \\n(.C\n" + ".cp 0\n"); + if (linesize > 0) + printfs(".nr " LINESIZE_REG " %1\n", as_string(linesize)); + else + prints(".nr " LINESIZE_REG " \\n[.s]\n"); + if (!(flags & CENTER)) + prints(".nr " SAVED_CENTER_REG " \\n[.ce]\n"); + prints(".de " RESET_MACRO_NAME "\n" + ".ft \\n[.f]\n" + ".ps \\n[.s]\n" + ".vs \\n[.v]u\n" + ".in \\n[.i]u\n" + ".ll \\n[.l]u\n" + ".ls \\n[.L]\n" + ".ad \\n[.j]\n" + ".ie \\n[.u] .fi\n" + ".el .nf\n" + ".ce \\n[.ce]\n" + "..\n" + ".nr " SAVED_INDENT_REG " \\n[.i]\n" + ".nr " SAVED_FONT_REG " \\n[.f]\n" + ".nr " SAVED_SIZE_REG " \\n[.s]\n" + ".nr " SAVED_FILL_REG " \\n[.u]\n" + ".nr T. 0\n" + ".nr " CURRENT_ROW_REG " 0-1\n" + ".nr " LAST_PASSED_ROW_REG " 0-1\n" + ".nr " SECTION_DIVERSION_FLAG_REG " 0\n" + ".ds " TRANSPARENT_STRING_NAME "\n" + ".ds " QUOTE_STRING_NAME "\n" + ".nr " NEED_BOTTOM_RULE_REG " 1\n" + ".nr " SUPPRESS_BOTTOM_REG " 0\n" + ".eo\n" + ".de " REPEATED_MARK_MACRO "\n" + ".mk \\$1\n" + ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO " \"\\$1\"\n" + "..\n" + ".de " REPEATED_VPT_MACRO "\n" + ".vpt \\$1\n" + ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO " \"\\$1\"\n" + "..\n"); + if (!(flags & NOKEEP)) + prints(".de " KEEP_MACRO_NAME "\n" + ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME " \\\\\n" + ".ds " TRANSPARENT_STRING_NAME " \\!\n" + ".di " SECTION_DIVERSION_NAME "\n" + ".nr " SECTION_DIVERSION_FLAG_REG " 1\n" + ".in 0\n" + ".\\}\n" + "..\n" + ".de " RELEASE_MACRO_NAME "\n" + ".if \\n[" SECTION_DIVERSION_FLAG_REG "] \\{" + ".di\n" + ".in \\n[" SAVED_INDENT_REG "]u\n" + ".nr " SAVED_DN_REG " \\n[dn]\n" + ".ds " QUOTE_STRING_NAME "\n" + ".ds " TRANSPARENT_STRING_NAME "\n" + ".nr " SECTION_DIVERSION_FLAG_REG " 0\n" + ".if \\n[.t]<=\\n[dn] \\{" + ".nr T. 1\n" + ".T#\n" + ".nr " SUPPRESS_BOTTOM_REG " 1\n" + ".sp \\n[.t]u\n" + ".nr " SUPPRESS_BOTTOM_REG " 0\n" + ".mk #T\n" + ".\\}\n" + ".if \\n[.t]<=\\n[" SAVED_DN_REG "] " + /* Since we turn off traps, it won't get into an infinite loop + when we try and print it; it will just go off the bottom of the + page. */ + ".tm warning: page \\n%: table text block will not fit on one page\n" + ".nf\n" + ".ls 1\n" + "." SECTION_DIVERSION_NAME "\n" + ".ls\n" + ".rm " SECTION_DIVERSION_NAME "\n" + ".\\}\n" + "..\n" + ".nr " TABLE_DIVERSION_FLAG_REG " 0\n" + ".de " TABLE_KEEP_MACRO_NAME "\n" + ".if '\\n[.z]'' \\{" + ".di " TABLE_DIVERSION_NAME "\n" + ".nr " TABLE_DIVERSION_FLAG_REG " 1\n" + ".\\}\n" + "..\n" + ".de " TABLE_RELEASE_MACRO_NAME "\n" + ".if \\n[" TABLE_DIVERSION_FLAG_REG "] \\{.br\n" + ".di\n" + ".nr " SAVED_DN_REG " \\n[dn]\n" + ".ne \\n[dn]u+\\n[.V]u\n" + ".ie \\n[.t]<=\\n[" SAVED_DN_REG "] " + ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n" + ".el \\{" + ".in 0\n" + ".ls 1\n" + ".nf\n" + "." TABLE_DIVERSION_NAME "\n" + ".\\}\n" + ".rm " TABLE_DIVERSION_NAME "\n" + ".\\}\n" + "..\n"); + prints(".ec\n" + ".ce 0\n" + ".nf\n"); +} + +string block_width_reg(int r, int c) +{ + static char name[sizeof(BLOCK_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, BLOCK_WIDTH_PREFIX "%d,%d", r, c); + return string(name); +} + +string block_diversion_name(int r, int c) +{ + static char name[sizeof(BLOCK_DIVERSION_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, BLOCK_DIVERSION_PREFIX "%d,%d", r, c); + return string(name); +} + +string block_height_reg(int r, int c) +{ + static char name[sizeof(BLOCK_HEIGHT_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, BLOCK_HEIGHT_PREFIX "%d,%d", r, c); + return string(name); +} + +string span_width_reg(int start_col, int end_col) +{ + static char name[sizeof(SPAN_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, SPAN_WIDTH_PREFIX "%d", start_col); + if (end_col != start_col) + sprintf(strchr(name, '\0'), ",%d", end_col); + return string(name); +} + +string span_left_numeric_width_reg(int start_col, int end_col) +{ + static char name[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, SPAN_LEFT_NUMERIC_WIDTH_PREFIX "%d", start_col); + if (end_col != start_col) + sprintf(strchr(name, '\0'), ",%d", end_col); + return string(name); +} + +string span_right_numeric_width_reg(int start_col, int end_col) +{ + static char name[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX "%d", start_col); + if (end_col != start_col) + sprintf(strchr(name, '\0'), ",%d", end_col); + return string(name); +} + +string span_alphabetic_width_reg(int start_col, int end_col) +{ + static char name[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, SPAN_ALPHABETIC_WIDTH_PREFIX "%d", start_col); + if (end_col != start_col) + sprintf(strchr(name, '\0'), ",%d", end_col); + return string(name); +} + + +string column_separation_reg(int col) +{ + static char name[sizeof(COLUMN_SEPARATION_PREFIX)+INT_DIGITS]; + sprintf(name, COLUMN_SEPARATION_PREFIX "%d", col); + return string(name); +} + +string row_start_reg(int row) +{ + static char name[sizeof(ROW_START_PREFIX)+INT_DIGITS]; + sprintf(name, ROW_START_PREFIX "%d", row); + return string(name); +} + +string column_start_reg(int col) +{ + static char name[sizeof(COLUMN_START_PREFIX)+INT_DIGITS]; + sprintf(name, COLUMN_START_PREFIX "%d", col); + return string(name); +} + +string column_end_reg(int col) +{ + static char name[sizeof(COLUMN_END_PREFIX)+INT_DIGITS]; + sprintf(name, COLUMN_END_PREFIX "%d", col); + return string(name); +} + +string column_divide_reg(int col) +{ + static char name[sizeof(COLUMN_DIVIDE_PREFIX)+INT_DIGITS]; + sprintf(name, COLUMN_DIVIDE_PREFIX "%d", col); + return string(name); +} + +string row_top_reg(int row) +{ + static char name[sizeof(ROW_TOP_PREFIX)+INT_DIGITS]; + sprintf(name, ROW_TOP_PREFIX "%d", row); + return string(name); +} + +void init_span_reg(int start_col, int end_col) +{ + printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n", + span_width_reg(start_col, end_col), + span_alphabetic_width_reg(start_col, end_col), + span_left_numeric_width_reg(start_col, end_col), + span_right_numeric_width_reg(start_col, end_col)); +} + +void compute_span_width(int start_col, int end_col) +{ + printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n" + ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n", + span_width_reg(start_col, end_col), + span_left_numeric_width_reg(start_col, end_col), + span_right_numeric_width_reg(start_col, end_col), + span_alphabetic_width_reg(start_col, end_col)); + +} + +// Increase the widths of columns so that the width of any spanning entry +// is no greater than the sum of the widths of the columns that it spans. +// Ensure that the widths of columns remain equal. + +void table::divide_span(int start_col, int end_col) +{ + assert(end_col > start_col); + printfs(".nr " NEEDED_REG " \\n[%1]-(\\n[%2]", + span_width_reg(start_col, end_col), + span_width_reg(start_col, start_col)); + int i; + for (i = start_col + 1; i <= end_col; i++) { + // The column separation may shrink with the expand option. + if (!(flags & EXPAND)) + printfs("+%1n", as_string(column_separation[i - 1])); + printfs("+\\n[%1]", span_width_reg(i, i)); + } + prints(")\n"); + printfs(".nr " NEEDED_REG " \\n[" NEEDED_REG "]/%1\n", + as_string(end_col - start_col + 1)); + prints(".if \\n[" NEEDED_REG "] \\{"); + for (i = start_col; i <= end_col; i++) + printfs(".nr %1 +\\n[" NEEDED_REG "]\n", + span_width_reg(i, i)); + int equal_flag = 0; + for (i = start_col; i <= end_col && !equal_flag; i++) + if (equal[i]) + equal_flag = 1; + if (equal_flag) { + for (i = 0; i < ncolumns; i++) + if (i < start_col || i > end_col) + printfs(".nr %1 +\\n[" NEEDED_REG "]\n", + span_width_reg(i, i)); + } + prints(".\\}\n"); +} + + +void table::sum_columns(int start_col, int end_col) +{ + assert(end_col > start_col); + printfs(".nr %1 \\n[%2]", + span_width_reg(start_col, end_col), + span_width_reg(start_col, start_col)); + for (int i = start_col + 1; i <= end_col; i++) + printfs("+(%1*\\n[" SEPARATION_FACTOR_REG "])+\\n[%2]", + as_string(column_separation[i - 1]), + span_width_reg(i, i)); + prints('\n'); +} + +horizontal_span::horizontal_span(int sc, int ec, horizontal_span *p) +: next(p), start_col(sc), end_col(ec) +{ +} + +void table::build_span_list() +{ + span_list = 0; + table_entry *p = entry_list; + while (p) { + if (p->end_col != p->start_col) { + horizontal_span *q; + for (q = span_list; q; q = q->next) + if (q->start_col == p->start_col + && q->end_col == p->end_col) + break; + if (!q) + span_list = new horizontal_span(p->start_col, p->end_col, span_list); + } + p = p->next; + } + // Now sort span_list primarily by order of end_row, and secondarily + // by reverse order of start_row. This ensures that if we divide + // spans using the order in span_list, we will get reasonable results. + horizontal_span *unsorted = span_list; + span_list = 0; + while (unsorted) { + horizontal_span **pp; + for (pp = &span_list; *pp; pp = &(*pp)->next) + if (unsorted->end_col < (*pp)->end_col + || (unsorted->end_col == (*pp)->end_col + && (unsorted->start_col > (*pp)->start_col))) + break; + horizontal_span *tem = unsorted->next; + unsorted->next = *pp; + *pp = unsorted; + unsorted = tem; + } +} + + +void table::compute_separation_factor() +{ + if (flags & (ALLBOX|BOX|DOUBLEBOX)) + left_separation = right_separation = 1; + else { + for (int i = 0; i < nrows; i++) { + if (vline[i][0] > 0) + left_separation = 1; + if (vline[i][ncolumns] > 0) + right_separation = 1; + } + } + if (flags & EXPAND) { + int total_sep = left_separation + right_separation; + int i; + for (i = 0; i < ncolumns - 1; i++) + total_sep += column_separation[i]; + if (total_sep != 0) { + // Don't let the separation factor be negative. + prints(".nr " SEPARATION_FACTOR_REG " \\n[.l]-\\n[.i]"); + for (i = 0; i < ncolumns; i++) + printfs("-\\n[%1]", span_width_reg(i, i)); + printfs("/%1>?0\n", as_string(total_sep)); + } + } +} + +void table::compute_column_positions() +{ + printfs(".nr %1 0\n", column_divide_reg(0)); + printfs(".nr %1 %2*\\n[" SEPARATION_FACTOR_REG "]\n", + column_start_reg(0), + as_string(left_separation)); + int i; + for (i = 1;; i++) { + printfs(".nr %1 \\n[%2]+\\n[%3]\n", + column_end_reg(i-1), + column_start_reg(i-1), + span_width_reg(i-1, i-1)); + if (i >= ncolumns) + break; + printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n", + column_start_reg(i), + column_end_reg(i-1), + as_string(column_separation[i-1])); + printfs(".nr %1 \\n[%2]+\\n[%3]/2\n", + column_divide_reg(i), + column_end_reg(i-1), + column_start_reg(i)); + } + printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n", + column_divide_reg(ncolumns), + column_end_reg(i-1), + as_string(right_separation)); + printfs(".nr TW \\n[%1]\n", + column_divide_reg(ncolumns)); + if (flags & DOUBLEBOX) { + printfs(".nr %1 +" DOUBLE_LINE_SEP "\n", column_divide_reg(0)); + printfs(".nr %1 -" DOUBLE_LINE_SEP "\n", column_divide_reg(ncolumns)); + } +} + +void table::make_columns_equal() +{ + int first = -1; // index of first equal column + int i; + for (i = 0; i < ncolumns; i++) + if (equal[i]) { + if (first < 0) { + printfs(".nr %1 \\n[%1]", span_width_reg(i, i)); + first = i; + } + else + printfs(">?\\n[%1]", span_width_reg(i, i)); + } + if (first >= 0) { + prints('\n'); + for (i = first + 1; i < ncolumns; i++) + if (equal[i]) + printfs(".nr %1 \\n[%2]\n", + span_width_reg(i, i), + span_width_reg(first, first)); + } +} + +void table::compute_widths() +{ + build_span_list(); + int i; + horizontal_span *p; + prints(".nr " SEPARATION_FACTOR_REG " 1n\n"); + for (i = 0; i < ncolumns; i++) { + init_span_reg(i, i); + if (!minimum_width[i].empty()) + printfs(".nr %1 %2\n", span_width_reg(i, i), minimum_width[i]); + } + for (p = span_list; p; p = p->next) + init_span_reg(p->start_col, p->end_col); + table_entry *q; + for (q = entry_list; q; q = q->next) + if (!q->mod->zero_width) + q->do_width(); + for (i = 0; i < ncolumns; i++) + compute_span_width(i, i); + for (p = span_list; p; p = p->next) + compute_span_width(p->start_col, p->end_col); + make_columns_equal(); + // Note that divide_span keeps equal width columns equal. + for (p = span_list; p; p = p->next) + divide_span(p->start_col, p->end_col); + for (p = span_list; p; p = p->next) + sum_columns(p->start_col, p->end_col); + int had_spanning_block = 0; + int had_equal_block = 0; + for (q = entry_list; q; q = q->next) + if (q->divert(ncolumns, minimum_width, + (flags & EXPAND) ? column_separation : 0)) { + if (q->end_col > q->start_col) + had_spanning_block = 1; + for (i = q->start_col; i <= q->end_col && !had_equal_block; i++) + if (equal[i]) + had_equal_block = 1; + } + if (had_equal_block) + make_columns_equal(); + if (had_spanning_block) + for (p = span_list; p; p = p->next) + divide_span(p->start_col, p->end_col); + compute_separation_factor(); + for (p = span_list; p; p = p->next) + sum_columns(p->start_col, p->end_col); + compute_column_positions(); +} + +void table::print_single_hline(int r) +{ + prints(".vs " LINE_SEP ">?\\n[.V]u\n" + ".ls 1\n" + "\\v'" BODY_DEPTH "'" + "\\s[\\n[" LINESIZE_REG "]]"); + if (r > nrows - 1) + prints("\\D'l |\\n[TW]u 0'"); + else { + int start_col = 0; + for (;;) { + while (start_col < ncolumns + && entry[r][start_col] != 0 + && entry[r][start_col]->start_row != r) + start_col++; + int end_col; + for (end_col = start_col; + end_col < ncolumns + && (entry[r][end_col] == 0 + || entry[r][end_col]->start_row == r); + end_col++) + ; + if (end_col <= start_col) + break; + printfs("\\h'|\\n[%1]u", + column_divide_reg(start_col)); + if ((r > 0 && vline[r-1][start_col] == 2) + || (r < nrows && vline[r][start_col] == 2)) + prints("-" HALF_DOUBLE_LINE_SEP); + prints("'"); + printfs("\\D'l |\\n[%1]u", + column_divide_reg(end_col)); + if ((r > 0 && vline[r-1][end_col] == 2) + || (r < nrows && vline[r][end_col] == 2)) + prints("+" HALF_DOUBLE_LINE_SEP); + prints(" 0'"); + start_col = end_col; + } + } + prints("\\s0\n"); + prints(".ls\n" + ".vs\n"); +} + +void table::print_double_hline(int r) +{ + prints(".vs " LINE_SEP "+" DOUBLE_LINE_SEP + ">?\\n[.V]u\n" + ".ls 1\n" + "\\v'" BODY_DEPTH "'" + "\\s[\\n[" LINESIZE_REG "]]"); + if (r > nrows - 1) + prints("\\v'-" DOUBLE_LINE_SEP "'" + "\\D'l |\\n[TW]u 0'" + "\\v'" DOUBLE_LINE_SEP "'" + "\\h'|0'" + "\\D'l |\\n[TW]u 0'"); + else { + int start_col = 0; + for (;;) { + while (start_col < ncolumns + && entry[r][start_col] != 0 + && entry[r][start_col]->start_row != r) + start_col++; + int end_col; + for (end_col = start_col; + end_col < ncolumns + && (entry[r][end_col] == 0 + || entry[r][end_col]->start_row == r); + end_col++) + ; + if (end_col <= start_col) + break; + const char *left_adjust = 0; + if ((r > 0 && vline[r-1][start_col] == 2) + || (r < nrows && vline[r][start_col] == 2)) + left_adjust = "-" HALF_DOUBLE_LINE_SEP; + const char *right_adjust = 0; + if ((r > 0 && vline[r-1][end_col] == 2) + || (r < nrows && vline[r][end_col] == 2)) + right_adjust = "+" HALF_DOUBLE_LINE_SEP; + printfs("\\v'-" DOUBLE_LINE_SEP "'" + "\\h'|\\n[%1]u", + column_divide_reg(start_col)); + if (left_adjust) + prints(left_adjust); + prints("'"); + printfs("\\D'l |\\n[%1]u", + column_divide_reg(end_col)); + if (right_adjust) + prints(right_adjust); + prints(" 0'"); + printfs("\\v'" DOUBLE_LINE_SEP "'" + "\\h'|\\n[%1]u", + column_divide_reg(start_col)); + if (left_adjust) + prints(left_adjust); + prints("'"); + printfs("\\D'l |\\n[%1]u", + column_divide_reg(end_col)); + if (right_adjust) + prints(right_adjust); + prints(" 0'"); + start_col = end_col; + } + } + prints("\\s0\n" + ".ls\n" + ".vs\n"); +} + +void table::compute_vrule_top_adjust(int start_row, int col, string &result) +{ + if (row_is_all_lines[start_row] && start_row < nrows - 1) { + if (row_is_all_lines[start_row] == 2) + result = LINE_SEP ">?\\n[.V]u" "+" DOUBLE_LINE_SEP; + else + result = LINE_SEP ">?\\n[.V]u"; + start_row++; + } + else { + result = ""; + if (start_row == 0) + return; + for (stuff *p = stuff_list; p && p->row <= start_row; p = p->next) + if (p->row == start_row + && (p->is_single_line() || p->is_double_line())) + return; + } + int left = 0; + if (col > 0) { + table_entry *e = entry[start_row-1][col-1]; + if (e && e->start_row == e->end_row) { + if (e->to_double_line_entry() != 0) + left = 2; + else if (e->to_single_line_entry() != 0) + left = 1; + } + } + int right = 0; + if (col < ncolumns) { + table_entry *e = entry[start_row-1][col]; + if (e && e->start_row == e->end_row) { + if (e->to_double_line_entry() != 0) + right = 2; + else if (e->to_single_line_entry() != 0) + right = 1; + } + } + if (row_is_all_lines[start_row-1] == 0) { + if (left > 0 || right > 0) { + result += "-" BODY_DEPTH "-" BAR_HEIGHT; + if ((left == 2 && right != 2) || (right == 2 && left != 2)) + result += "-" HALF_DOUBLE_LINE_SEP; + else if (left == 2 && right == 2) + result += "+" HALF_DOUBLE_LINE_SEP; + } + } + else if (row_is_all_lines[start_row-1] == 2) { + if ((left == 2 && right != 2) || (right == 2 && left != 2)) + result += "-" DOUBLE_LINE_SEP; + else if (left == 1 || right == 1) + result += "-" HALF_DOUBLE_LINE_SEP; + } +} + +void table::compute_vrule_bot_adjust(int end_row, int col, string &result) +{ + if (row_is_all_lines[end_row] && end_row > 0) { + end_row--; + result = ""; + } + else { + stuff *p; + for (p = stuff_list; p && p->row < end_row + 1; p = p->next) + ; + if (p && p->row == end_row + 1 && p->is_double_line()) { + result = "-" DOUBLE_LINE_SEP; + return; + } + if ((p != 0 && p->row == end_row + 1) + || end_row == nrows - 1) { + result = ""; + return; + } + if (row_is_all_lines[end_row+1] == 1) + result = LINE_SEP; + else if (row_is_all_lines[end_row+1] == 2) + result = LINE_SEP "+" DOUBLE_LINE_SEP; + else + result = ""; + } + int left = 0; + if (col > 0) { + table_entry *e = entry[end_row+1][col-1]; + if (e && e->start_row == e->end_row) { + if (e->to_double_line_entry() != 0) + left = 2; + else if (e->to_single_line_entry() != 0) + left = 1; + } + } + int right = 0; + if (col < ncolumns) { + table_entry *e = entry[end_row+1][col]; + if (e && e->start_row == e->end_row) { + if (e->to_double_line_entry() != 0) + right = 2; + else if (e->to_single_line_entry() != 0) + right = 1; + } + } + if (row_is_all_lines[end_row+1] == 0) { + if (left > 0 || right > 0) { + result = "1v-" BODY_DEPTH "-" BAR_HEIGHT; + if ((left == 2 && right != 2) || (right == 2 && left != 2)) + result += "+" HALF_DOUBLE_LINE_SEP; + else if (left == 2 && right == 2) + result += "-" HALF_DOUBLE_LINE_SEP; + } + } + else if (row_is_all_lines[end_row+1] == 2) { + if (left == 2 && right == 2) + result += "-" DOUBLE_LINE_SEP; + else if (left != 2 && right != 2 && (left == 1 || right == 1)) + result += "-" HALF_DOUBLE_LINE_SEP; + } +} + +void table::add_vertical_rule(int start_row, int end_row, int col, int is_double) +{ + vrule_list = new vertical_rule(start_row, end_row, col, is_double, + vrule_list); + compute_vrule_top_adjust(start_row, col, vrule_list->top_adjust); + compute_vrule_bot_adjust(end_row, col, vrule_list->bot_adjust); +} + +void table::build_vrule_list() +{ + int col; + if (flags & ALLBOX) { + for (col = 1; col < ncolumns; col++) { + int start_row = 0; + for (;;) { + while (start_row < nrows && vline_spanned(start_row, col)) + start_row++; + if (start_row >= nrows) + break; + int end_row = start_row; + while (end_row < nrows && !vline_spanned(end_row, col)) + end_row++; + end_row--; + add_vertical_rule(start_row, end_row, col, 0); + start_row = end_row + 1; + } + } + } + if (flags & (BOX|ALLBOX|DOUBLEBOX)) { + add_vertical_rule(0, nrows - 1, 0, 0); + add_vertical_rule(0, nrows - 1, ncolumns, 0); + } + for (int end_row = 0; end_row < nrows; end_row++) + for (col = 0; col < ncolumns+1; col++) + if (vline[end_row][col] > 0 + && !vline_spanned(end_row, col) + && (end_row == nrows - 1 + || vline[end_row+1][col] != vline[end_row][col] + || vline_spanned(end_row+1, col))) { + int start_row; + for (start_row = end_row - 1; + start_row >= 0 + && vline[start_row][col] == vline[end_row][col] + && !vline_spanned(start_row, col); + start_row--) + ; + start_row++; + add_vertical_rule(start_row, end_row, col, vline[end_row][col] > 1); + } + for (vertical_rule *p = vrule_list; p; p = p->next) + if (p->is_double) + for (int r = p->start_row; r <= p->end_row; r++) { + if (p->col > 0 && entry[r][p->col-1] != 0 + && entry[r][p->col-1]->end_col == p->col-1) { + int is_corner = r == p->start_row || r == p->end_row; + entry[r][p->col-1]->note_double_vrule_on_right(is_corner); + } + if (p->col < ncolumns && entry[r][p->col] != 0 + && entry[r][p->col]->start_col == p->col) { + int is_corner = r == p->start_row || r == p->end_row; + entry[r][p->col]->note_double_vrule_on_left(is_corner); + } + } +} + +void table::define_bottom_macro() +{ + prints(".eo\n" + ".de T#\n" + ".if !\\n[" SUPPRESS_BOTTOM_REG "] \\{" + "." REPEATED_VPT_MACRO " 0\n" + ".mk " SAVED_VERTICAL_POS_REG "\n"); + if (flags & (BOX|ALLBOX|DOUBLEBOX)) { + prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG "] \\{"); + print_single_hline(0); + prints(".\\}\n"); + } + prints(".ls 1\n"); + for (vertical_rule *p = vrule_list; p; p = p->next) + p->contribute_to_bottom_macro(this); + if (flags & DOUBLEBOX) + prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n" + "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]" + "\\D'l \\n[TW]u 0'\\s0\n" + ".vs\n" + ".\\}\n" + ".if \\n[" LAST_PASSED_ROW_REG "]>=0 " + ".nr " TOP_REG " \\n[#T]-" DOUBLE_LINE_SEP "\n" + ".sp -1\n" + "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]" + "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n" + ".sp -1\n" + "\\v'" BODY_DEPTH "'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG "]]" + "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n"); + prints(".ls\n"); + prints(".nr " LAST_PASSED_ROW_REG " \\n[" CURRENT_ROW_REG "]\n" + ".sp |\\n[" SAVED_VERTICAL_POS_REG "]u\n" + "." REPEATED_VPT_MACRO " 1\n" + ".\\}\n" + "..\n" + ".ec\n"); +} + + +// is the vertical line before column c in row r horizontally spanned? + +int table::vline_spanned(int r, int c) +{ + assert(r >= 0 && r < nrows && c >= 0 && c < ncolumns + 1); + return (c != 0 && c != ncolumns && entry[r][c] != 0 + && entry[r][c]->start_col != c + // horizontally spanning lines don't count + && entry[r][c]->to_double_line_entry() == 0 + && entry[r][c]->to_single_line_entry() == 0); +} + +int table::row_begins_section(int r) +{ + assert(r >= 0 && r < nrows); + for (int i = 0; i < ncolumns; i++) + if (entry[r][i] && entry[r][i]->start_row != r) + return 0; + return 1; +} + +int table::row_ends_section(int r) +{ + assert(r >= 0 && r < nrows); + for (int i = 0; i < ncolumns; i++) + if (entry[r][i] && entry[r][i]->end_row != r) + return 0; + return 1; +} + +void table::do_row(int r) +{ + if (!(flags & NOKEEP) && row_begins_section(r)) + prints("." KEEP_MACRO_NAME "\n"); + int had_line = 0; + stuff *p; + for (p = stuff_list; p && p->row < r; p = p->next) + ; + for (stuff *p1 = p; p1 && p1->row == r; p1 = p1->next) + if (!p1->printed && (p1->is_single_line() || p1->is_double_line())) { + had_line = 1; + break; + } + if (!had_line && !row_is_all_lines[r]) + printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r)); + had_line = 0; + for (; p && p->row == r; p = p->next) + if (!p->printed) { + p->print(this); + if (!had_line && (p->is_single_line() || p->is_double_line())) { + printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r)); + had_line = 1; + } + } + // Change the row *after* printing the stuff list (which might contain .TH). + printfs("\\*[" TRANSPARENT_STRING_NAME "].nr " CURRENT_ROW_REG " %1\n", + as_string(r)); + if (!had_line && row_is_all_lines[r]) + printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r)); + // we might have had a .TH, for example, since we last tried + if (!(flags & NOKEEP) && row_begins_section(r)) + prints("." KEEP_MACRO_NAME "\n"); + printfs(".mk %1\n", row_start_reg(r)); + prints(".mk " BOTTOM_REG "\n" + "." REPEATED_VPT_MACRO " 0\n"); + int c; + int row_is_blank = 1; + int first_start_row = r; + for (c = 0; c < ncolumns; c++) { + table_entry *e = entry[r][c]; + if (e) { + if (e->end_row == r) { + e->do_depth(); + if (e->start_row < first_start_row) + first_start_row = e->start_row; + row_is_blank = 0; + } + c = e->end_col; + } + } + if (row_is_blank) + prints(".nr " BOTTOM_REG " +1v\n"); + if (row_is_all_lines[r]) { + prints(".vs " LINE_SEP); + if (row_is_all_lines[r] == 2) + prints("+" DOUBLE_LINE_SEP); + prints(">?\\n[.V]u\n.ls 1\n"); + prints("\\&"); + prints("\\v'" BODY_DEPTH); + if (row_is_all_lines[r] == 2) + prints("-" HALF_DOUBLE_LINE_SEP); + prints("'"); + for (c = 0; c < ncolumns; c++) { + table_entry *e = entry[r][c]; + if (e) { + if (e->end_row == e->start_row) + e->to_simple_entry()->simple_print(1); + c = e->end_col; + } + } + prints("\n"); + prints(".ls\n" + ".vs\n"); + prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n"); + printfs(".sp |\\n[%1]u\n", row_start_reg(r)); + } + for (int i = row_is_all_lines[r] ? r - 1 : r; + i >= first_start_row; + i--) { + simple_entry *first = 0; + for (c = 0; c < ncolumns; c++) { + table_entry *e = entry[r][c]; + if (e) { + if (e->end_row == r && e->start_row == i) { + simple_entry *simple = e->to_simple_entry(); + if (simple) { + if (!first) { + prints(".ta"); + first = simple; + } + simple->add_tab(); + } + } + c = e->end_col; + } + } + if (first) { + prints('\n'); + first->position_vertically(); + first->set_location(); + prints("\\&"); + first->simple_print(0); + for (c = first->end_col + 1; c < ncolumns; c++) { + table_entry *e = entry[r][c]; + if (e) { + if (e->end_row == r && e->start_row == i) { + simple_entry *simple = e->to_simple_entry(); + if (simple) + simple->simple_print(0); + } + c = e->end_col; + } + } + prints('\n'); + prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n"); + printfs(".sp |\\n[%1]u\n", row_start_reg(r)); + } + } + for (c = 0; c < ncolumns; c++) { + table_entry *e = entry[r][c]; + if (e) { + if (e->end_row == r && e->to_simple_entry() == 0) { + e->position_vertically(); + e->print(); + prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n"); + printfs(".sp |\\n[%1]u\n", row_start_reg(r)); + } + c = e->end_col; + } + } + prints("." REPEATED_VPT_MACRO " 1\n" + ".sp |\\n[" BOTTOM_REG "]u\n" + "\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 1\n"); + if (r != nrows - 1 && (flags & ALLBOX)) { + print_single_hline(r + 1); + prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 0\n"); + } + if (r != nrows - 1) { + if (p && p->row == r + 1 + && (p->is_single_line() || p->is_double_line())) { + p->print(this); + prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG + " 0\n"); + } + int printed_one = 0; + for (vertical_rule *vr = vrule_list; vr; vr = vr->next) + if (vr->end_row == r) { + if (!printed_one) { + prints("." REPEATED_VPT_MACRO " 0\n"); + printed_one = 1; + } + vr->print(); + } + if (printed_one) + prints("." REPEATED_VPT_MACRO " 1\n"); + if (!(flags & NOKEEP) && row_ends_section(r)) + prints("." RELEASE_MACRO_NAME "\n"); + } +} + +void table::do_top() +{ + prints(".fc \002\003\n"); + if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX))) + prints("." TABLE_KEEP_MACRO_NAME "\n"); + if (flags & DOUBLEBOX) { + prints(".ls 1\n" + ".vs " LINE_SEP ">?\\n[.V]u\n" + "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]\\D'l \\n[TW]u 0'\\s0\n" + ".vs\n" + "." REPEATED_MARK_MACRO " " TOP_REG "\n" + ".vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n"); + printfs("\\v'" BODY_DEPTH "'" + "\\s[\\n[" LINESIZE_REG "]]" + "\\h'\\n[%1]u'" + "\\D'l |\\n[%2]u 0'" + "\\s0" + "\n", + column_divide_reg(0), + column_divide_reg(ncolumns)); + prints(".ls\n" + ".vs\n"); + } + else if (flags & (ALLBOX|BOX)) { + print_single_hline(0); + } + //printfs(".mk %1\n", row_top_reg(0)); +} + +void table::do_bottom() +{ + // print stuff after last row + for (stuff *p = stuff_list; p; p = p->next) + if (p->row > nrows - 1) + p->print(this); + if (!(flags & NOKEEP)) + prints("." RELEASE_MACRO_NAME "\n"); + printfs(".mk %1\n", row_top_reg(nrows)); + prints(".nr " NEED_BOTTOM_RULE_REG " 1\n" + ".nr T. 1\n" + ".T#\n"); + if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX))) + prints("." TABLE_RELEASE_MACRO_NAME "\n"); + if (flags & DOUBLEBOX) + prints(".sp " DOUBLE_LINE_SEP "\n"); + prints("." RESET_MACRO_NAME "\n" + ".fc\n" + ".cp \\n(" COMPATIBLE_REG "\n"); +} + +int table::get_nrows() +{ + return nrows; +} + +const char *last_filename = 0; + +void set_troff_location(const char *fn, int ln) +{ + if (!location_force_filename && last_filename != 0 + && strcmp(fn, last_filename) == 0) + printfs(".lf %1\n", as_string(ln)); + else { + printfs(".lf %1 %2\n", as_string(ln), fn); + last_filename = fn; + location_force_filename = 0; + } +} + +void printfs(const char *s, const string &arg1, const string &arg2, + const string &arg3, const string &arg4, const string &arg5) +{ + if (s) { + char c; + while ((c = *s++) != '\0') { + if (c == '%') { + switch (*s++) { + case '1': + prints(arg1); + break; + case '2': + prints(arg2); + break; + case '3': + prints(arg3); + break; + case '4': + prints(arg4); + break; + case '5': + prints(arg5); + break; + case '6': + case '7': + case '8': + case '9': + break; + case '%': + prints('%'); + break; + default: + assert(0); + } + } + else + prints(c); + } + } +} + diff --git a/contrib/groff/src/preproc/tbl/table.h b/contrib/groff/src/preproc/tbl/table.h new file mode 100644 index 0000000..ca55b80 --- /dev/null +++ b/contrib/groff/src/preproc/tbl/table.h @@ -0,0 +1,152 @@ +// -*- 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 <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <ctype.h> +#include <errno.h> + +#include "cset.h" +#include "cmap.h" +#include "stringclass.h" +#include "errarg.h" +#include "error.h" +#include "lib.h" + +struct inc_number { + short inc; + short val; +}; + +struct entry_modifier { + inc_number point_size; + inc_number vertical_spacing; + string font; + enum { CENTER, TOP, BOTTOM } vertical_alignment; + char zero_width; + char stagger; + + entry_modifier(); + ~entry_modifier(); +}; + +enum format_type { + FORMAT_LEFT, + FORMAT_CENTER, + FORMAT_RIGHT, + FORMAT_NUMERIC, + FORMAT_ALPHABETIC, + FORMAT_SPAN, + FORMAT_VSPAN, + FORMAT_HLINE, + FORMAT_DOUBLE_HLINE +}; + +struct entry_format : public entry_modifier { + format_type type; + + entry_format(format_type); + entry_format(); + void debug_print() const; +}; + +struct table_entry; +struct horizontal_span; +struct stuff; +struct vertical_rule; + +class table { + unsigned flags; + int nrows; + int ncolumns; + int linesize; + char delim[2]; + char decimal_point_char; + vertical_rule *vrule_list; + stuff *stuff_list; + horizontal_span *span_list; + table_entry *entry_list; + table_entry **entry_list_tailp; + table_entry ***entry; + char **vline; + char *row_is_all_lines; + string *minimum_width; + int *column_separation; + char *equal; + int left_separation; + int right_separation; + int allocated_rows; + void build_span_list(); + void do_hspan(int r, int c); + void do_vspan(int r, int c); + void allocate(int r); + void compute_widths(); + void divide_span(int, int); + void sum_columns(int, int); + void compute_separation_factor(); + void compute_column_positions(); + void do_row(int); + void init_output(); + void add_stuff(stuff *); + void do_top(); + void do_bottom(); + void do_vertical_rules(); + void build_vrule_list(); + void add_vertical_rule(int, int, int, int); + void define_bottom_macro(); + int vline_spanned(int r, int c); + int row_begins_section(int); + int row_ends_section(int); + void make_columns_equal(); + void compute_vrule_top_adjust(int, int, string &); + void compute_vrule_bot_adjust(int, int, string &); + void determine_row_type(); +public: + /* used by flags */ + enum { + CENTER = 01, + EXPAND = 02, + BOX = 04, + ALLBOX = 010, + DOUBLEBOX = 020, + NOKEEP = 040 + }; + table(int nc, unsigned flags, int linesize, char decimal_point_char); + ~table(); + + void add_text_line(int r, const string &, const char *, int); + void add_single_hline(int r); + void add_double_hline(int r); + void add_entry(int r, int c, const string &, const entry_format *, + const char *, int lineno); + void add_vlines(int r, const char *); + void check(); + void print(); + void set_minimum_width(int c, const string &w); + void set_column_separation(int c, int n); + void set_equal_column(int c); + void set_delim(char c1, char c2); + void print_single_hline(int r); + void print_double_hline(int r); + int get_nrows(); +}; + +void set_troff_location(const char *, int); diff --git a/contrib/groff/src/preproc/tbl/tbl.man b/contrib/groff/src/preproc/tbl/tbl.man new file mode 100644 index 0000000..6016ddf --- /dev/null +++ b/contrib/groff/src/preproc/tbl/tbl.man @@ -0,0 +1,178 @@ +.ig \"-*- nroff -*- +Copyright (C) 1989-1995 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the Free Software Foundation instead of in +the original English. +.. +.TH @G@TBL @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" +.SH NAME +@g@tbl \- format tables for troff +.SH SYNOPSIS +.B @g@tbl +[ +.B \-Cv +] +[ +.IR files \|.\|.\|. +] +.SH DESCRIPTION +This manual page describes the GNU version of +.BR tbl , +which is part of the groff document formatting system. +.B tbl +compiles descriptions of tables embedded within +.B troff +input files into commands that are understood by +.BR troff . +Normally, it should be invoked using the +.B \-t +option of +.B groff. +It is highly compatible with Unix +.BR tbl . +The output generated by GNU +.B tbl +cannot be processed with Unix +.BR troff ; +it must be processed with GNU +.BR troff . +If no files are given on the command line, the standard input +will be read. +A filename of +.B \- +will cause the standard input to be read. +.SH OPTIONS +.TP +.B \-C +Recognize +.B .TS +and +.B .TE +even when followed by a character other than space or newline. +.TP +.B \-v +Print the version number. +.SH USAGE +Only the differences between GNU +.B tbl +and Unix +.B tbl +are described here. +.LP +Normally +.B tbl +attempts to prevent undesirable breaks in the table by using diversions. +This can sometimes interact badly with macro packages' own use of diversions, +when footnotes, for example, are used. +The +.B nokeep +option tells +.B tbl +not to try and prevent breaks in this way. +.LP +The +.B decimalpoint +option specifies the character to be recognized as the decimal +point character in place of the default period. +It takes an argument in parentheses, which must be a single +character, as for the +.B tab +option. +.LP +The +.B f +format modifier can be followed by an arbitrary length +font name in parentheses. +.LP +There is a +.B d +format modifier which means that a vertically spanning entry +should be aligned at the bottom of its range. +.LP +There is no limit on the number of columns in a table, nor any limit +on the number of text blocks. +All the lines of a table are considered in deciding column +widths, not just the first 200. +Table continuation +.RB ( .T& ) +lines are not restricted to the first 200 lines. +.LP +Numeric and alphabetic items may appear in the same column. +.LP +Numeric and alphabetic items may span horizontally. +.LP +.B tbl +uses register, string, macro and diversion names beginning with +.BR 3 . +When using +.B tbl +you should avoid using any names beginning with a +.BR 3 . +.SH BUGS +You should use +.BR .TS\ H / .TH +in conjunction with a supporting macro package for +.I all +multi-page boxed tables. +If there is no header that you wish to appear at the top of each page +of the table, place the +.B .TH +line immediately after the format section. +Do not enclose a multi-page table within keep/release macros, +or divert it in any other way. +.LP +A text block within a table must be able to fit on one page. +.LP +The +.B bp +request cannot be used to force a page-break in a multi-page table. +Instead, define +.B BP +as follows +.IP +.B .de BP +.br +.B .ie '\e\en(.z'' .bp \e\e$1 +.br +.B .el \e!.BP \e\e$1 +.br +.B .. +.br +.LP +and use +.B BP +instead of +.BR bp . +.LP +Using \ea directly in a table to get leaders will not work. +This is correct behaviour: \ea is a +.B uninterpreted +leader. +To get leaders use a real leader, either by using a control A or like +this: +.IP +.nf +.ft B +\&.ds a \ea +\&.TS +tab(;); +lw(1i) l. +A\e*a;B +\&.TE +.ft +.fi +.SH "SEE ALSO" +.BR groff (@MAN1EXT@), +.BR @g@troff (@MAN1EXT@) |