BSD 4_3_Net_2 release
[unix-history] / usr / src / usr.bin / groff / eqn / text.cc
// -*- 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);
}