BSD 4_3_Net_2 development
authorCSRG <csrg@ucbvax.Berkeley.EDU>
Tue, 30 Apr 1991 08:58:10 +0000 (00:58 -0800)
committerCSRG <csrg@ucbvax.Berkeley.EDU>
Tue, 30 Apr 1991 08:58:10 +0000 (00:58 -0800)
Work on file usr/src/usr.bin/groff/eqn/Makefile.gnu
Work on file usr/src/usr.bin/groff/eqn/NOTES
Work on file usr/src/usr.bin/groff/eqn/TODO
Work on file usr/src/usr.bin/groff/eqn/box.cc
Work on file usr/src/usr.bin/groff/eqn/box.h
Work on file usr/src/usr.bin/groff/eqn/delim.cc
Work on file usr/src/usr.bin/groff/eqn/lex.cc
Work on file usr/src/usr.bin/groff/eqn/limit.cc
Work on file usr/src/usr.bin/groff/eqn/list.cc
Work on file usr/src/usr.bin/groff/eqn/mark.cc
Work on file usr/src/usr.bin/groff/eqn/main.cc
Work on file usr/src/usr.bin/groff/eqn/pbox.h
Work on file usr/src/usr.bin/groff/eqn/pile.cc
Work on file usr/src/usr.bin/groff/eqn/script.cc
Work on file usr/src/usr.bin/groff/eqn/sqrt.cc
Work on file usr/src/usr.bin/groff/eqn/text.cc
Work on file usr/src/usr.bin/groff/eqn/other.cc
Work on file usr/src/usr.bin/groff/eqn/over.cc
Work on file usr/src/usr.bin/groff/eqn/eqn.1
Work on file usr/src/usr.bin/groff/eqn/eqn.y

Synthesized-from: CSRG/cd2/net.2

20 files changed:
usr/src/usr.bin/groff/eqn/Makefile.gnu [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/NOTES [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/TODO [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/box.cc [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/box.h [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/delim.cc [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/eqn.1 [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/eqn.y [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/lex.cc [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/limit.cc [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/list.cc [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/main.cc [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/mark.cc [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/other.cc [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/over.cc [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/pbox.h [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/pile.cc [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/script.cc [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/sqrt.cc [new file with mode: 0644]
usr/src/usr.bin/groff/eqn/text.cc [new file with mode: 0644]

diff --git a/usr/src/usr.bin/groff/eqn/Makefile.gnu b/usr/src/usr.bin/groff/eqn/Makefile.gnu
new file mode 100644 (file)
index 0000000..385b780
--- /dev/null
@@ -0,0 +1,83 @@
+#Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+#     Written by James Clark (jjc@jclark.uucp)
+#
+#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 1, 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 LICENSE.  If not, write to the Free Software
+#Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+BINDIR=/usr/local/bin
+# default device
+DEVICE=ps
+CFLAGS=-g -O -Wall
+CC=g++
+INCLUDES=-I../lib
+DEFINES=-DDEVICE=\"$(DEVICE)\"
+YACC=bison -y
+YACCFLAGS=-v
+ETAGS=etags
+ETAGSFLAGS=-p
+OBJECTS=eqn.tab.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
+SOURCES=main.c lex.c eqn.y box.c limit.c list.c over.c text.c \
+       script.c mark.c other.c delim.c sqrt.c pile.c \
+       eqn.h box.h pbox.h
+
+.c.o:
+       $(CC) -c $(INCLUDES) $(CFLAGS) $(DEFINES) $<
+
+all: eqn
+
+eqn: $(OBJECTS) ../lib/libgroff.a
+       $(CC) $(LDFLAGS) -o $@ $(OBJECTS) ../lib/libgroff.a $(LIBS)
+
+eqn.tab.c: eqn.y
+       $(YACC) $(YACCFLAGS) -d eqn.y
+       mv y.tab.c eqn.tab.c
+       mv y.tab.h eqn.tab.h
+
+eqn.tab.o: eqn.h box.h
+box.o: eqn.h box.h pbox.h
+limit.o: eqn.h box.h pbox.h
+text.o: eqn.h box.h pbox.h
+over.o: eqn.h box.h pbox.h
+list.o: eqn.h box.h pbox.h
+script.o: eqn.h box.h pbox.h
+mark.o: eqn.h box.h pbox.h
+other.o: eqn.h box.h pbox.h
+delim.o: eqn.h box.h pbox.h
+sqrt.o: eqn.h box.h pbox.h
+pile.o: eqn.h box.h pbox.h
+main.o: eqn.h  box.h  ../lib/stringclass.h
+lex.o: eqn.h eqn.tab.c box.h ../lib/stringclass.h
+
+install.bin: eqn
+       -[ -d $(BINDIR) ] || mkdir $(BINDIR)
+       cp eqn $(BINDIR)/geqn
+
+install.nobin:
+
+install: install.bin install.nobin
+
+TAGS: $(SOURCES)
+       $(ETAGS) $(ETAGSFLAGS) $(SOURCES)
+
+clean:
+       -rm -f *.o core eqn gmon.out
+
+distclean: clean
+       -rm -f TAGS eqn.output y.output
+
+realclean: distclean
+       -rm -f eqn.tab.c eqn.tab.h
diff --git a/usr/src/usr.bin/groff/eqn/NOTES b/usr/src/usr.bin/groff/eqn/NOTES
new file mode 100644 (file)
index 0000000..3ccbd4c
--- /dev/null
@@ -0,0 +1,25 @@
+Differences between textstyle and displaystyle
+
+superscripts are a little higher in displaystyle
+(does not apply to cramped variants)
+
+operators are bigger in ds
+[not done automatically]
+
+more separation between square-root bar and top of contents
+in displaystyle
+
+fractions are different: in textstyle change to script style;
+also num and denom positions different; also minimum
+separation between bar and contents different.
+[dealt with by smallover]
+
+
+       D       D'      T       T'      S       S'
+sup    sup1    sup3    sup2    sup3    sup2    sup3
+sroot  xh      xh      rt      rt      rt      rt
+space  yes     yes     yes     yes     no      no
+
+If we make text-style--->display-style,
+
+too much separation square roots
diff --git a/usr/src/usr.bin/groff/eqn/TODO b/usr/src/usr.bin/groff/eqn/TODO
new file mode 100644 (file)
index 0000000..7d0deb5
--- /dev/null
@@ -0,0 +1,39 @@
+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 shuld 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.
diff --git a/usr/src/usr.bin/groff/eqn/box.cc b/usr/src/usr.bin/groff/eqn/box.cc
new file mode 100644 (file)
index 0000000..617bd09
--- /dev/null
@@ -0,0 +1,553 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
+     Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE.  If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 = 75;
+int body_depth = 25;
+
+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
+
+
+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", &sup1,
+"sup2", &sup2,
+"sup3", &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,
+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;
+}
+
+void set_gsize(int n)
+{
+  gsize = n;
+}
+
+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)
+{
+  delete gfont;
+  gfont = strsave(s);
+}
+
+void set_grfont(const char *s)
+{
+  delete grfont;
+  grfont = strsave(s);
+}
+
+void set_gbfont(const char *s)
+{
+  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() : uid(next_uid++), spacing_type(ORDINARY_TYPE)
+{
+}
+
+box::~box()
+{
+}
+
+void box::top_level()
+{
+  // debug_print();
+  // putc('\n', stderr);
+  box *b = this;
+  printf(".nr " SAVED_FONT_REG " \\n[.f]\n");
+  printf(".ft %s\n", get_gfont());
+  if (gsize > 0)
+    b = new size_box(strsave(itoa(gsize)), 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_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 " \\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);
+  printf(".as " LINE_STRING " \\f[%s]", get_gfont());
+  current_roman_font = get_grfont();
+  b->output();
+  printf("\\f[\\n[" SAVED_FONT_REG "]]\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()
+{
+  if (positive_space >= 0 || negative_space >= 0) {
+    if (positive_space > 0)
+      printf(".if !r" EQN_NO_EXTRA_SPACE_REG " "
+            ".as " LINE_STRING " \\x'-%dM'\n", positive_space);
+    if (negative_space > 0)
+      printf(".if !r" EQN_NO_EXTRA_SPACE_REG " "
+            ".as " LINE_STRING " \\x'%dM'\n", negative_space);
+    positive_space = negative_space = -1;
+  }
+  else {
+    printf(".if !r" 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 !r" 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);
+    delete oldp;
+  }
+  p[len++] = pp;
+}
+
+box_list::~box_list()
+{
+  for (int i = 0; i < len; i++)
+    delete p[i];
+  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 " \\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 " \\w" DELIMITER_CHAR, uid);
+  output();
+  printf(DELIMITER_CHAR "\n");
+  printf(".nr " HEIGHT_FORMAT " \\n[rst]>?0\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 " \\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()
+{
+  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/usr/src/usr.bin/groff/eqn/box.h b/usr/src/usr.bin/groff/eqn/box.h
new file mode 100644 (file)
index 0000000..4688861
--- /dev/null
@@ -0,0 +1,276 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
+     Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE.  If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 *);
+
+void set_space(int);
+void set_gsize(int);
+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/usr/src/usr.bin/groff/eqn/delim.cc b/usr/src/usr.bin/groff/eqn/delim.cc
new file mode 100644 (file)
index 0000000..307ae2a
--- /dev/null
@@ -0,0 +1,356 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
+     Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE.  If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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]",
+  },
+};
+
+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') {
+    delete l;
+    l = 0;
+  }
+  if (r != 0 && *r == '\0') {
+    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()
+{
+  delete left;
+  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 " \\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 " \\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 " \\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 " \\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;
+  for (int i = 0; i < DELIM_TABLE_SIZE; i++, d++)
+    if (strcmp(delim, d->name) == 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 " \\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 " \\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 " \\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 " \\n[" DELTA_REG "]*%d/500"
+        ">?(\\n[" DELTA_REG "]*2-%dM)\n",
+        delimiter_factor, delimiter_shortfall);
+  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()
+{
+  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);
+  p->debug_print();
+  fprintf(stderr, " }");
+  if (right)
+    fprintf(stderr, " right \"%s\"", right);
+}
+
diff --git a/usr/src/usr.bin/groff/eqn/eqn.1 b/usr/src/usr.bin/groff/eqn/eqn.1
new file mode 100644 (file)
index 0000000..2b5c671
--- /dev/null
@@ -0,0 +1,716 @@
+.\" -*- nroff -*-
+.ie \n(.V<\n(.v .ds tx T\h'-.1667m'\v'.224m'E\v'-.224m'\h'-.125m'X
+.el .ds tx TeX
+.TH GEQN @MAN1EXT@ "1 May 1990" "Groff Version @VERSION@"
+.SH NAME
+geqn \- groff equation formatter
+.SH SYNOPSIS
+.B geqn
+[
+.B \-C
+]
+[
+.B \-D
+]
+[
+.B \-N
+]
+[
+.BI \-d cc
+]
+[
+.BI \-T dev
+]
+[
+.B \-r
+]
+[
+.B \-v
+]
+[
+.BI \-f F
+]
+[
+.BI \-s n
+]
+[
+.BI \-p n
+]
+[
+.BI \-m n
+]
+[
+.IR files \|.\|.\|.
+]
+.SH DESCRIPTION
+.B geqn
+is a preprocessor for
+.B groff
+for formatting equations.
+Normally, it should be invoked using the
+.B \-e
+option of
+.BR groff .
+The syntax is quite compatible with
+.BR eqn .
+The output of
+.B geqn
+cannot be processed with
+.BR ditroff ;
+it must be processed with
+.BR gtroff .
+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 .EQ
+and
+.B .EN
+even when followed by a character other than space or newline.
+.TP
+.B \-D
+Draw fraction bars using the
+.B \eD
+escape sequence, rather than with the
+.B \el
+escape sequence and the
+.B \e(ru
+character.
+.TP
+.B \-N
+Don't allow newlines within delimiters.
+This option allows
+.B geqn
+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 .
+Geqn will not reduce the size of subscripts or superscripts to
+a smaller size than
+.IR n .
+.TP
+.BI \-T dev
+The output is for device
+.IR dev .
+A macro whose name is the name of the output device
+is automatically defined with a value of
+.BR 1 .
+Nothing else depends on what the output device is;
+geqn does not itself read font or device description files.
+The default output device is
+.BR @DEVICE@ .
+.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.
+Geqn 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 geqn and eqn are described here.
+.LP
+Most of the new features of
+.B geqn
+are based on \*(tx.
+There are some references to the differences between \*(tx and
+.B geqn
+below;
+these may safely be ignored if you do not know \*(tx.
+.SS Automatic spacing
+.LP
+.B geqn
+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 punctutation 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\ " text """"
+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 """" text """"
+.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 prcedence 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 .
+.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 geqn
+should assume an x height of 0.45 ems.
+.RS
+.LP
+Possible parameters are as follows.
+Values are in units of hundreths 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 geqn
+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.
+.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 argumenta.
+.TP
+.BI include\ " file """"
+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 geqn
+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 "SEE ALSO"
+.BR groff (@MAN1EXT@),
+.BR gtroff (@MAN1EXT@),
+.BR groff_font (@MAN5EXT@),
+.I The\ \*(txbook
diff --git a/usr/src/usr.bin/groff/eqn/eqn.y b/usr/src/usr.bin/groff/eqn/eqn.y
new file mode 100644 (file)
index 0000000..b7af88f
--- /dev/null
@@ -0,0 +1,328 @@
+/* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
+     Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE.  If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+%{
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "box.h"
+extern int non_empty_flag;
+char *strsave(const char *);
+#define YYDEBUG 1
+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
+
+/* 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
+%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); }
+       ;
+       
+number:
+       text
+               {
+                 int n;
+                 if (sscanf($1, "%d", &n) == 1)
+                   $$ = n;
+                 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/usr/src/usr.bin/groff/eqn/lex.cc b/usr/src/usr.bin/groff/eqn/lex.cc
new file mode 100644 (file)
index 0000000..0b68893
--- /dev/null
@@ -0,0 +1,1151 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+     Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE.  If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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), contents(0), is_simple(0)
+{
+}
+
+definition::~definition()
+{
+  if (is_macro)
+    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,
+};
+
+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", "\"^\"",
+  "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 ."
+};  
+
+void init_table(const char *device)
+{
+  for (int 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()
+{
+  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()
+{
+  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()
+{
+  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;
+}
+
+#define ARG1 11
+
+argument_macro_input::argument_macro_input(const char *body, int ac, 
+                                          char **av, input *x)
+: input(x), argc(ac), ap(0)
+{
+  for (int 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++)
+    delete argv[i];
+  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 == '\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;
+  for (int 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();
+  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) {
+    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';
+  char *ptr;
+  long n = strtol(token_buffer.contents(), &ptr, 10);
+  if (n == 0 && ptr == token_buffer.contents())
+    lex_error("bad argument `%1' to gsize command");
+  else
+    set_gsize(int(n));
+}
+
+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");
+  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 chartype");
+    return;
+  }
+  token_buffer += '\0';
+  string param = token_buffer;
+  t = get_token();
+  if (t != TEXT && t != QUOTED_TEXT) {
+    lex_error("bad chartype");
+    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 TDEFINE:
+    case DEFINE:
+      do_definition(0);
+      break;
+    case NDEFINE:
+      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/usr/src/usr.bin/groff/eqn/limit.cc b/usr/src/usr.bin/groff/eqn/limit.cc
new file mode 100644 (file)
index 0000000..b9bc6b9
--- /dev/null
@@ -0,0 +1,195 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
+     Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE.  If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 " "
+        "\\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 " "
+        "\\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/usr/src/usr.bin/groff/eqn/list.cc b/usr/src/usr.bin/groff/eqn/list.cc
new file mode 100644 (file)
index 0000000..03682cf
--- /dev/null
@@ -0,0 +1,236 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
+     Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE.  If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 (int 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;
+  for (int 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 (int 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/usr/src/usr.bin/groff/eqn/main.cc b/usr/src/usr.bin/groff/eqn/main.cc
new file mode 100644 (file)
index 0000000..5642540
--- /dev/null
@@ -0,0 +1,279 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+     Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE.  If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "eqn.h"
+#include "stringclass.h"
+
+#ifndef DEVICE
+#define DEVICE "ps"
+#endif
+
+extern int yyparse();
+
+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 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(const char *filename)
+{
+  string linebuf;
+  string str;
+  FILE *fp;
+  if (strcmp(filename, "-") == 0)
+    fp = stdin;
+  else {
+    fp = fopen(filename, "r");
+    if (fp == 0)
+      fatal("can't open `%1': %2", filename, strerror(errno));
+  }
+  printf(".lf 1 %s\n", filename);
+  current_filename = filename;
+  current_lineno = 0;
+  while (read_line(fp, &linebuf)) {
+    int i;
+    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() >= 4
+            && linebuf[0] == '.'
+            && linebuf[1] == 'E'
+            && linebuf[2] == 'Q'
+            && (linebuf[3] == ' ' || linebuf[3] == '\n' || compatible_flag)) {
+      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);
+    }
+    else if (start_delim != '\0' && (i = linebuf.search(start_delim)) >= 0
+            && (i == 0 || linebuf[i - 1] != '\\')) {
+      start_string();
+      linebuf += '\0';
+      char *ptr = &linebuf[0];
+      inline_flag = 1;
+      for (;;) {
+       char *start = strchr(ptr, start_delim);
+       if (start == 0) {
+         char *nl = strchr(ptr, '\n');
+         if (nl != 0)
+           *nl = '\0';
+          do_text(ptr);
+         break;
+        }
+       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';
+       init_lex(str.contents(), current_filename, start_lineno);
+       yyparse();
+      }
+      printf(".lf %d\n", current_lineno);
+      output_string();
+      restore_compatibility();
+      printf(".lf %d\n", current_lineno + 1);
+    }
+    else {
+      put_string(linebuf, stdout);
+    }
+  }
+  if (fp != stdin)
+    fclose(fp);
+  current_filename = 0;
+  current_lineno = 0;
+}
+
+void usage()
+{
+  fprintf(stderr, "usage: %s [ -vrDCN ] -dxx -fn -sn -pn -mn -Ts [ files ... ]\n",
+         program_name);
+  exit(1);
+}
+
+int main(int argc, char **argv)
+{
+  program_name = argv[0];
+  static char stderr_buf[BUFSIZ];
+  setbuf(stderr, stderr_buf);
+  int opt;
+  const char *device = DEVICE;
+  const char *tem = getenv("GROFF_TYPESETTER");
+  if (tem)
+    device = tem;
+  while ((opt = getopt(argc, argv, "DCvd:f:p:s:m:T:rN")) != EOF)
+    switch (opt) {
+    case 'C':
+      compatible_flag = 1;
+      break;
+    case 'v':
+      {
+       extern const char *version_string;
+       fprintf(stderr, "GNU eqn version %s\n", version_string);
+       fflush(stderr);
+       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;
+      break;
+    case 's':
+      {
+       int n;
+       if (sscanf(optarg, "%d", &n) == 1)
+         set_gsize(n);
+       else
+         error("bad 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':
+      draw_flag = 1;
+      break;
+    case 'N':
+      no_newline_in_delim_flag = 1;
+      break;
+    case '?':
+      usage();
+      break;
+    default:
+      assert(0);
+    }
+  init_table(device);
+  init_char_table();
+  if (optind >= argc)
+    do_file("-");
+  else
+    for (int i = optind; i < argc; i++)
+      do_file(argv[i]);
+  if (ferror(stdout) || fflush(stdout) < 0)
+    fatal("output error");
+  exit(0);
+}
+
+
diff --git a/usr/src/usr.bin/groff/eqn/mark.cc b/usr/src/usr.bin/groff/eqn/mark.cc
new file mode 100644 (file)
index 0000000..a03f56b
--- /dev/null
@@ -0,0 +1,121 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
+     Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE.  If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/usr/src/usr.bin/groff/eqn/other.cc b/usr/src/usr.bin/groff/eqn/other.cc
new file mode 100644 (file)
index 0000000..e23cba6
--- /dev/null
@@ -0,0 +1,601 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
+     Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE.  If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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) : ab(qq), pointer_box(pp)
+{
+}
+
+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) : size(s), pointer_box(pp)
+{
+}
+
+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()
+{
+  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()
+{
+  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) : n(i), pointer_box(pp)
+{
+}
+
+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) : n(i), pointer_box(pp)
+{
+}
+
+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/usr/src/usr.bin/groff/eqn/over.cc b/usr/src/usr.bin/groff/eqn/over.cc
new file mode 100644 (file)
index 0000000..83164e6
--- /dev/null
@@ -0,0 +1,196 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
+     Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE.  If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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)
+: num(pp), den(qq), reduce_size(is_small)
+{
+  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/usr/src/usr.bin/groff/eqn/pbox.h b/usr/src/usr.bin/groff/eqn/pbox.h
new file mode 100644 (file)
index 0000000..2bca4e1
--- /dev/null
@@ -0,0 +1,133 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
+     Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE.  If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 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/usr/src/usr.bin/groff/eqn/pile.cc b/usr/src/usr.bin/groff/eqn/pile.cc
new file mode 100644 (file)
index 0000000..54fed4a
--- /dev/null
@@ -0,0 +1,293 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
+     Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE.  If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 " \\n[" SUP_RAISE_FORMAT "]+(0",
+        uid, uid);
+  for (i = 0; i < len; i++)
+    printf(">?\\n[" HEIGHT_FORMAT "]", p[i]->p[0]->uid);
+  printf(")\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(")\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];
+  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);
+    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/usr/src/usr.bin/groff/eqn/script.cc b/usr/src/usr.bin/groff/eqn/script.cc
new file mode 100644 (file)
index 0000000..3efac36
--- /dev/null
@@ -0,0 +1,221 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
+     Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE.  If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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/usr/src/usr.bin/groff/eqn/sqrt.cc b/usr/src/usr.bin/groff/eqn/sqrt.cc
new file mode 100644 (file)
index 0000000..b7d50da
--- /dev/null
@@ -0,0 +1,179 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
+     Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE.  If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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 "\\(rn"
+
+#define SQRT_CHAIN "\\[sr\\\\n[" INDEX_REG "]]"
+#define BAR_CHAIN "\\[rn\\\\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
+        " \\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
+        " \\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
+        " \\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/usr/src/usr.bin/groff/eqn/text.cc b/usr/src/usr.bin/groff/eqn/text.cc
new file mode 100644 (file)
index 0000000..0a304e9
--- /dev/null
@@ -0,0 +1,526 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+     Written by James Clark (jjc@jclark.uucp)
+
+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 1, 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 LICENSE.  If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, 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;
+  delete type;
+}
+
+char_box::char_box(unsigned char cc)
+: c(cc), prev_is_italic(0), next_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()
+{
+  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);
+    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 " \\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 " \\n[" WIDTH_FORMAT "]"
+        "+\\n[" SUB_KERN_FORMAT "]\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);
+}
+