// -*- C++ -*-
/* Copyright (C) 1989-2020 Free Software Foundation, Inc.
Written by James Clark (jjc@jclark.com)
This file is part of groff.
groff is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, 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 this program. If not, see . */
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include "eqn.h"
#include "pbox.h"
#include "ptable.h"
struct map {
const char *from;
const char *to;
};
struct map entity_table[] = {
// Classic troff special characters
{"%", ""}, // ISOnum
{"'", "´"}, // ISOdia
{"!=", "≠"}, // ISOtech
{"**", "∗"}, // ISOtech
{"*a", "α"}, // ISOgrk3
{"*A", "A"},
{"*b", "β"}, // ISOgrk3
{"*B", "B"},
{"*d", "δ"}, // ISOgrk3
{"*D", "Δ"}, // ISOgrk3
{"*e", "ε"}, // ISOgrk3
{"*E", "E"},
{"*f", "φ"}, // ISOgrk3
{"*F", "Φ"}, // ISOgrk3
{"*g", "γ"}, // ISOgrk3
{"*G", "Γ"}, // ISOgrk3
{"*h", "θ"}, // ISOgrk3
{"*H", "Θ"}, // ISOgrk3
{"*i", "ι"}, // ISOgrk3
{"*I", "I"},
{"*k", "κ"}, // ISOgrk3
{"*K", "K;"},
{"*l", "λ"}, // ISOgrk3
{"*L", "Λ"}, // ISOgrk3
{"*m", "μ"}, // ISOgrk3
{"*M", "M"},
{"*n", "ν"}, // ISOgrk3
{"*N", "N"},
{"*o", "o"},
{"*O", "O"},
{"*p", "π"}, // ISOgrk3
{"*P", "Π"}, // ISOgrk3
{"*q", "ψ"}, // ISOgrk3
{"*Q", "Ψ"}, // ISOgrk3
{"*r", "ρ"}, // ISOgrk3
{"*R", "R"},
{"*s", "σ"}, // ISOgrk3
{"*S", "Σ"}, // ISOgrk3
{"*t", "τ"}, // ISOgrk3
{"*T", "Τ"}, // ISOgrk3
{"*u", "υ"}, // ISOgrk3
{"*U", "Υ"}, // ISOgrk3
{"*w", "ω"}, // ISOgrk3
{"*W", "Ω"}, // ISOgrk3
{"*x", "χ"}, // ISOgrk3
{"*X", "Χ"}, // ISOgrk3
{"*y", "η"}, // ISOgrk3
{"*Y", "Η"}, // ISOgrk3
{"*z", "ζ"}, // ISOgrk3
{"*Z", "Ζ"}, // ISOgrk3
{"+-", "±"}, // ISOnum
{"->", "→"}, // ISOnum
{"12", "½"}, // ISOnum
{"14", "¼"}, // ISOnum
{"34", "¾"}, // ISOnum
{"<-", "←"}, // ISOnum
{"==", "≡"}, // ISOtech
{"Fi", "ffi"}, // ISOpub
{"Fl", "ffl"}, // ISOpub
{"aa", "´"}, // ISOdia
{"ap", "∼"}, // ISOtech
{"bl", "&phonexb;"}, // ISOpub
{"br", "│"}, // ISObox
{"bs", "☎"}, // ISOpub (for the Bell logo)
{"bu", "•"}, // ISOpub
{"bv", "|"}, // ISOnum
{"ca", "∩"}, // ISOtech
{"ci", "○"}, // ISOpub
{"co", "©"}, // ISOnum
{"ct", "¢"}, // ISOnum
{"cu", "∪"}, // ISOtech
{"da", "↓"}, // ISOnum
{"de", "°"}, // ISOnum
{"dg", "†"}, // ISOpub
{"dd", "‡"}, // ISOpub
{"di", "÷"}, // ISOnum
{"em", "—"}, // ISOpub
{"eq", "="}, // ISOnum
{"es", "∅"}, // ISOamso
{"ff", "ff"}, // ISOpub
{"fi", "fi"}, // ISOpub
{"fl", "fl"}, // ISOpub
{"fm", "′"}, // ISOtech
{"ge", "≥"}, // ISOtech
{"gr", "∇"}, // ISOtech
{"hy", "‐"}, // ISOnum
{"ib", "⊆"}, // ISOtech
{"if", "∞"}, // ISOtech
{"ip", "⊇"}, // ISOtech
{"is", "∫"}, // ISOtech
{"le", "≤"}, // ISOtech
// Some pile characters go here
{"mi", "−"}, // ISOtech
{"mo", "∈"}, // ISOtech
{"mu", "×"}, // ISOnum
{"no", "¬"}, // ISOnum
{"or", "|"}, // ISOnum
{"pl", "+"}, // ISOnum
{"pt", "∝"}, // ISOtech
{"rg", "™"}, // ISOnum
// More pile characters go here
{"rn", "¯"}, // ISOdia
{"ru", "_"}, // ISOnum
{"sb", "⊂"}, // ISOtech
{"sc", "§"}, // ISOnum
{"sl", "/"},
{"sp", "⊃"}, // ISOtech
{"sq", "▪"}, // ISOpub
{"sr", "√"}, // ISOtech
{"ts", "ς"}, // ISOgrk3
{"ua", "↑"}, // ISOnum
{"ul", "_"},
{"~=", "≅"}, // ISOtech
// Extended specials supported by groff; see groff_char(7).
// These are listed in the order they occur on that man page.
{"-D", "Ð"}, // ISOlat: Icelandic uppercase eth
{"Sd", "ð"}, // ISOlat1: Icelandic lowercase eth
{"TP", "Þ"}, // ISOlat1: Icelandic uppercase thorn
{"Tp", "þ"}, // ISOlat1: Icelandic lowercase thorn
{"ss", "ß"}, // ISOlat1
// Ligatures
// ff, fi, fl, ffi, ffl from old troff go here
{"AE", "Æ"}, // ISOlat1
{"ae", "æ"}, // ISOlat1
{"OE", "Œ"}, // ISOlat2
{"oe", "œ"}, // ISOlat2
{"IJ", "ij"}, // ISOlat2: Dutch IJ ligature
{"ij", "IJ"}, // ISOlat2: Dutch ij ligature
{".i", "ı"}, // ISOlat2,ISOamso
{".j", "&jnodot;"}, // ISOamso (undocumented but in 1.19)
// Accented characters
{"'A", "Á"}, // ISOlat1
{"'C", "Ć"}, // ISOlat2
{"'E", "É"}, // ISOlat1
{"'I", "Í"}, // ISOlat1
{"'O", "Ó"}, // ISOlat1
{"'U", "Ú"}, // ISOlat1
{"'Y", "Ý"}, // ISOlat1
{"'a", "á"}, // ISOlat1
{"'c", "ć"}, // ISOlat2
{"'e", "é"}, // ISOlat1
{"'i", "í"}, // ISOlat1
{"'o", "ó"}, // ISOlat1
{"'u", "ú"}, // ISOlat1
{"'y", "ý"}, // ISOlat1
{":A", "Ä"}, // ISOlat1
{":E", "Ë"}, // ISOlat1
{":I", "Ï"}, // ISOlat1
{":O", "Ö"}, // ISOlat1
{":U", "Ü"}, // ISOlat1
{":Y", "Ÿ"}, // ISOlat2
{":a", "ä"}, // ISOlat1
{":e", "ë"}, // ISOlat1
{":i", "ï"}, // ISOlat1
{":o", "ö"}, // ISOlat1
{":u", "ü"}, // ISOlat1
{":y", "ÿ"}, // ISOlat1
{"^A", "Â"}, // ISOlat1
{"^E", "Ê"}, // ISOlat1
{"^I", "Î"}, // ISOlat1
{"^O", "Ô"}, // ISOlat1
{"^U", "Û"}, // ISOlat1
{"^a", "â"}, // ISOlat1
{"^e", "ê"}, // ISOlat1
{"^i", "î"}, // ISOlat1
{"^o", "ô"}, // ISOlat1
{"^u", "û"}, // ISOlat1
{"`A", "À"}, // ISOlat1
{"`E", "È"}, // ISOlat1
{"`I", "Ì"}, // ISOlat1
{"`O", "Ò"}, // ISOlat1
{"`U", "Ù"}, // ISOlat1
{"`a", "à"}, // ISOlat1
{"`e", "è"}, // ISOlat1
{"`i", "ì"}, // ISOlat1
{"`o", "ò"}, // ISOlat1
{"`u", "ù"}, // ISOlat1
{"~A", "Ã"}, // ISOlat1
{"~N", "Ñ"}, // ISOlat1
{"~O", "Õ"}, // ISOlat1
{"~a", "ã"}, // ISOlat1
{"~n", "ñ"}, // ISOlat1
{"~o", "õ"}, // ISOlat1
{"vS", "Š"}, // ISOlat2
{"vs", "š"}, // ISOlat2
{"vZ", "Ž"}, // ISOlat2
{"vz", "ž"}, // ISOlat2
{",C", "Ç"}, // ISOlat1
{",c", "ç"}, // ISOlat1
{"/L", "Ł"}, // ISOlat2: Polish L with a slash
{"/l", "ł"}, // ISOlat2: Polish l with a slash
{"/O", "Ø"}, // ISOlat1
{"/o", "ø"}, // ISOlat1
{"oA", "Å"}, // ISOlat1
{"oa", "å"}, // ISOlat1
// Accents
{"a\"","˝"}, // ISOdia: double acute accent (Hungarian umlaut)
{"a-", "¯"}, // ISOdia: macron or bar accent
{"a.", "˙"}, // ISOdia: dot above
{"a^", "ˆ"}, // ISOdia: circumflex accent
{"aa", "´"}, // ISOdia: acute accent
{"ga", "`"}, // ISOdia: grave accent
{"ab", "˘"}, // ISOdia: breve accent
{"ac", "¸"}, // ISOdia: cedilla accent
{"ad", "¨"}, // ISOdia: umlaut or dieresis
{"ah", "ˇ"}, // ISOdia: caron (aka hacek accent)
{"ao", "˚"}, // ISOdia: ring or circle accent
{"a~", "˜"}, // ISOdia: tilde accent
{"ho", "˛"}, // ISOdia: hook or ogonek accent
{"ha", "^"}, // ASCII circumflex, hat, caret
{"ti", "~"}, // ASCII tilde, large tilde
// Quotes
{"Bq", "‚"}, // ISOpub: low double comma quote
{"bq", "„"}, // ISOpub: low single comma quote
{"lq", "“"}, // ISOnum
{"rq", "”"}, // ISOpub
{"oq", "‘"}, // ISOnum: single open quote
{"cq", "’"}, // ISOnum: single closing quote (ASCII 39)
{"aq", "&zerosp;'"}, // apostrophe quote
{"dq", "\""}, // double quote (ASCII 34)
{"Fo", "«"}, // ISOnum
{"Fc", "»"}, // ISOnum
//{"fo", "&fo;"},
//{"fc", "&fc;"},
// Punctuation
{"r!", "¡"}, // ISOnum
{"r?", "¿"}, // ISOnum
// Old troff \(em goes here
{"en", "–"}, // ISOpub: en dash
// Old troff \(hy goes here
// Brackets
{"lB", "["}, // ISOnum: left (square) bracket
{"rB", "]"}, // ISOnum: right (square) bracket
{"lC", "{"}, // ISOnum: left (curly) brace
{"rC", "}"}, // ISOnum: right (curly) brace
{"la", "〈"}, // ISOtech: left angle bracket
{"ra", "〉"}, // ISOtech: right angle bracket
// Old troff \(bv goes here
// Bracket-pile characters could go here.
// Arrows
// Old troff \(<- and \(-> go here
{"<>", "↔"}, // ISOamsa
{"da", "↓"}, // ISOnum
{"ua", "↑"}, // ISOnum
{"lA", "⇐"}, // ISOtech
{"rA", "⇒"}, // ISOtech
{"hA", "⇔"}, // ISOtech: horizontal double-headed arrow
{"dA", "⇓"}, // ISOamsa
{"uA", "⇑"}, // ISOamsa
{"vA", "⇕"}, // ISOamsa: vertical double-headed double arrow
//{"an", "&an;"},
// Lines
{"-h", "ℏ"}, // ISOamso: h-bar (Planck's constant)
// Old troff \(or goes here
{"ba", "|"}, // ISOnum
// Old troff \(br, \{u, \(ul, \(bv go here
{"bb", "¦"}, // ISOnum
{"sl", "/"},
{"rs", "\"}, // ISOnum
// Text markers
// Old troff \(ci, \(bu, \(dd, \(dg go here
{"lz", "◊"}, // ISOpub
// Old troff sq goes here
{"ps", "¶"}, // ISOnum: paragraph or pilcrow sign
{"sc", "§"}, // ISOnum (in old troff)
// Old troff \(lh, \{h go here
{"at", "@"}, // ISOnum
{"sh", "#"}, // ISOnum
//{"CR", "&CR;"},
{"OK", "✓"}, // ISOpub
// Legalize
// Old troff \(co, \{g go here
{"tm", "™"}, // ISOnum
// Currency symbols
{"Do", "$"}, // ISOnum
{"ct", "¢"}, // ISOnum
{"eu", "€"},
{"Eu", "€"},
{"Ye", "¥"}, // ISOnum
{"Po", "£"}, // ISOnum
{"Cs", "¤"}, // ISOnum: currency sign
{"Fn", "&fnof"}, // ISOtech
// Units
// Old troff de goes here
{"%0", "‰"}, // ISOtech: per thousand, per mille sign
// Old troff \(fm goes here
{"sd", "″"}, // ISOtech
{"mc", "µ"}, // ISOnum
{"Of", "ª"}, // ISOnum
{"Om", "º"}, // ISOnum
// Logical symbols
{"AN", "∧"}, // ISOtech
{"OR", "∨"}, // ISOtech
// Old troff \(no goes here
{"te", "∃"}, // ISOtech: there exists, existential quantifier
{"fa", "∀"}, // ISOtech: for all, universal quantifier
{"st", "&bepsi"}, // ISOamsr: such that
{"3d", "∴"}, // ISOtech
{"tf", "∴"}, // ISOtech
// Mathematical symbols
// Old troff "12", "14", "34" goes here
{"S1", "¹"}, // ISOnum
{"S2", "²"}, // ISOnum
{"S3", "³"}, // ISOnum
// Old troff \(pl", \-, \(+- go here
{"t+-", "±"}, // ISOnum
{"-+", "∓"}, // ISOtech
{"pc", "·"}, // ISOnum
{"md", "·"}, // ISOnum
// Old troff \(mu goes here
{"tmu", "×"}, // ISOnum
{"c*", "⊗"}, // ISOamsb: multiply sign in a circle
{"c+", "⊕"}, // ISOamsb: plus sign in a circle
// Old troff \(di goes here
{"tdi", "÷"}, // ISOnum
{"f/", "―"}, // ISOnum: horizintal bar for fractions
// Old troff \(** goes here
{"<=", "≤"}, // ISOtech
{">=", "≥"}, // ISOtech
{"<<", "≪"}, // ISOamsr
{">>", "≫"}, // ISOamsr
{"!=", "≠"}, // ISOtech
// Old troff \(eq and \(== go here
{"=~", "≅"}, // ISOamsr
// Old troff \(ap goes here
{"~~", "≈"}, // ISOtech
// This appears to be an error in the groff table.
// It clashes with the Bell Labs use of ~= for a congruence sign
// {"~=", "≈"}, // ISOamsr
// Old troff \(pt, \(es, \(mo go here
{"nm", "∉"}, // ISOtech
{"nb", "⊄"}, // ISOamsr
{"nc", "⊅"}, // ISOamsn
{"ne", "≢"}, // ISOamsn
// Old troff \(sb, \(sp, \(ib, \(ip, \(ca, \(cu go here
{"/_", "∠"}, // ISOamso
{"pp", "⊥"}, // ISOtech
// Old troff \(is goes here
{"sum", "∑"}, // ISOamsb
{"product", "∏"}, // ISOamsb
{"gr", "∇"}, // ISOtech
// Old troff \(sr. \{n, \(if go here
{"Ah", "ℵ"}, // ISOtech
{"Im", "ℑ"}, // ISOamso: Fraktur I, imaginary
{"Re", "ℜ"}, // ISOamso: Fraktur R, real
{"wp", "℘"}, // ISOamso
{"pd", "∂"}, // ISOtech: partial differentiation sign
// Their table duplicates the Greek letters here.
// We list only the variant forms here, mapping them into
// the ISO Greek 4 variants (which may or may not be correct :-()
{"+f", "&b.phiv;"}, // ISOgrk4: variant phi
{"+h", "&b.thetas;"}, // ISOgrk4: variant theta
{"+p", "&b.omega;"}, // ISOgrk4: variant pi, looking like omega
// Card symbols
{"CL", "♣"}, // ISOpub: club suit
{"SP", "♠"}, // ISOpub: spade suit
{"HE", "♥"}, // ISOpub: heart suit
{"DI", "♦"}, // ISOpub: diamond suit
};
const char *special_to_entity(const char *sp)
{
struct map *mp;
for (mp = entity_table;
mp < entity_table + sizeof(entity_table)/sizeof(entity_table[0]);
mp++) {
if (strcmp(mp->from, sp) == 0)
return mp->to;
}
return NULL;
}
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);
};
enum spacing_type {
s_ordinary,
s_operator,
s_binary,
s_relation,
s_opening,
s_closing,
s_punctuation,
s_inner,
s_suppress
};
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[1];
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", s_binary, -1);
set_special_char_type("mi", s_binary, -1);
set_special_char_type("eq", s_relation, -1);
set_special_char_type("<=", s_relation, -1);
set_special_char_type(">=", s_relation, -1);
char_table['}'].spacing_type = s_closing;
char_table[')'].spacing_type = s_closing;
char_table[']'].spacing_type = s_closing;
char_table['{'].spacing_type = s_opening;
char_table['('].spacing_type = s_opening;
char_table['['].spacing_type = s_opening;
char_table[','].spacing_type = s_punctuation;
char_table[';'].spacing_type = s_punctuation;
char_table[':'].spacing_type = s_punctuation;
char_table['.'].spacing_type = s_punctuation;
char_table['>'].spacing_type = s_relation;
char_table['<'].spacing_type = s_relation;
char_table['*'].spacing_type = s_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;
free(type);
}
char_box::char_box(unsigned char cc)
: c(cc), next_is_italic(0), prev_is_italic(0)
{
spacing_type = char_table[c].spacing_type;
}
void char_box::hint(unsigned flags)
{
if (flags & HINT_PREV_IS_ITALIC)
prev_is_italic = 1;
if (flags & HINT_NEXT_IS_ITALIC)
next_is_italic = 1;
}
void char_box::output()
{
if (output_format == troff) {
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);
}
else if (output_format == mathml) {
if (isdigit(c))
printf("");
else if (char_table[c].spacing_type)
printf("");
else
printf("");
if (c == '<')
printf("<");
else if (c == '>')
printf(">");
else if (c == '&')
printf("&");
else
putchar(c);
if (isdigit(c))
printf("");
else if (char_table[c].spacing_type)
printf("");
else
printf("");
}
}
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()
{
free(s);
}
void special_char_box::output()
{
if (output_format == troff) {
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");
}
else if (output_format == mathml) {
const char *entity = special_to_entity(s);
if (entity != NULL)
printf("%s", entity);
else
printf("unknown eqn/troff special char %s", s);
}
}
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 " 0\\n[" WIDTH_FORMAT "]"
"+\\n[" WIDTH_FORMAT "]\n",
uid, p->uid, pb->uid);
printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]"
">?\\n[" HEIGHT_FORMAT "]\n",
uid, p->uid, pb->uid);
printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]"
">?\\n[" DEPTH_FORMAT "]\n",
uid, p->uid, pb->uid);
return res;
}
void prime_box::compute_subscript_kern()
{
p->compute_subscript_kern();
printf(".nr " SUB_KERN_FORMAT " 0\\n[" WIDTH_FORMAT "]"
"+\\n[" SUB_KERN_FORMAT "]>?0\n",
uid, pb->uid, p->uid);
}
void prime_box::output()
{
p->output();
pb->output();
}
void prime_box::handle_char_type(int st, int ft)
{
p->handle_char_type(st, ft);
pb->handle_char_type(st, ft);
}
void prime_box::debug_print()
{
p->debug_print();
putc('\'', stderr);
}
box *split_text(char *text)
{
list_box *lb = 0;
box *fb = 0;
char *s = text;
while (*s != '\0') {
char c = *s++;
box *b = 0;
switch (c) {
case '+':
b = new special_char_box("pl");
break;
case '-':
b = new special_char_box("mi");
break;
case '=':
b = new special_char_box("eq");
break;
case '\'':
b = new special_char_box("fm");
break;
case '<':
if (*s == '=') {
b = new special_char_box("<=");
s++;
break;
}
goto normal_char;
case '>':
if (*s == '=') {
b = new special_char_box(">=");
s++;
break;
}
goto normal_char;
case '\\':
if (*s == '\0') {
lex_error("bad escape");
break;
}
c = *s++;
switch (c) {
case '(':
{
char buf[3];
if (*s != '\0') {
buf[0] = *s++;
if (*s != '\0') {
buf[1] = *s++;
buf[2] = '\0';
b = new special_char_box(buf);
}
else {
lex_error("bad escape");
}
}
else {
lex_error("bad escape");
}
}
break;
case '[':
{
char *ch = s;
while (*s != ']' && *s != '\0')
s++;
if (*s == '\0')
lex_error("bad escape");
else {
*s++ = '\0';
b = new special_char_box(ch);
}
}
break;
case 'f':
case 'g':
case 'k':
case 'n':
case '*':
{
char *escape_start = s - 2;
switch (*s) {
case '(':
if (*++s != '\0')
++s;
break;
case '[':
for (++s; *s != '\0' && *s != ']'; s++)
;
break;
}
if (*s == '\0')
lex_error("bad escape");
else {
++s;
char *buf = new char[s - escape_start + 1];
memcpy(buf, escape_start, s - escape_start);
buf[s - escape_start] = '\0';
b = new quoted_text_box(buf);
}
}
break;
case '-':
case '_':
{
char buf[2];
buf[0] = c;
buf[1] = '\0';
b = new special_char_box(buf);
}
break;
case '`':
b = new special_char_box("ga");
break;
case '\'':
b = new special_char_box("aa");
break;
case 'e':
case '\\':
b = new char_box('\\');
break;
case '^':
case '|':
case '0':
{
char buf[3];
buf[0] = '\\';
buf[1] = c;
buf[2] = '\0';
b = new quoted_text_box(strsave(buf));
break;
}
default:
lex_error("unquoted escape");
b = new quoted_text_box(strsave(s - 2));
s = strchr(s, '\0');
break;
}
break;
default:
normal_char:
b = new char_box(c);
break;
}
while (*s == '\'') {
if (b == 0)
b = new quoted_text_box(0);
b = new prime_box(b);
s++;
}
if (b != 0) {
if (lb != 0)
lb->append(b);
else if (fb != 0) {
lb = new list_box(fb);
lb->append(b);
}
else
fb = b;
}
}
free(text);
if (lb != 0)
return lb;
else if (fb != 0)
return fb;
else
return new quoted_text_box(0);
}