1114402Sru// -*- C++ -*- 2114402Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2003 Free Software Foundation, Inc. 3114402Sru Written by James Clark (jjc@jclark.com) 4114402Sru 5114402SruThis file is part of groff. 6114402Sru 7114402Srugroff is free software; you can redistribute it and/or modify it under 8114402Sruthe terms of the GNU General Public License as published by the Free 9114402SruSoftware Foundation; either version 2, or (at your option) any later 10114402Sruversion. 11114402Sru 12114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY 13114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or 14114402SruFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15114402Srufor more details. 16114402Sru 17114402SruYou should have received a copy of the GNU General Public License along 18114402Sruwith groff; see the file COPYING. If not, write to the Free Software 19151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 20114402Sru 21114402Sru#include "eqn.h" 22114402Sru#include "pbox.h" 23114402Sru#include "ptable.h" 24114402Sru 25114402Sruclass char_box : public simple_box { 26114402Sru unsigned char c; 27114402Sru char next_is_italic; 28114402Sru char prev_is_italic; 29114402Srupublic: 30114402Sru char_box(unsigned char); 31114402Sru void debug_print(); 32114402Sru void output(); 33114402Sru int is_char(); 34114402Sru int left_is_italic(); 35114402Sru int right_is_italic(); 36114402Sru void hint(unsigned); 37114402Sru void handle_char_type(int, int); 38114402Sru}; 39114402Sru 40114402Sruclass special_char_box : public simple_box { 41114402Sru char *s; 42114402Srupublic: 43114402Sru special_char_box(const char *); 44114402Sru ~special_char_box(); 45114402Sru void output(); 46114402Sru void debug_print(); 47114402Sru int is_char(); 48114402Sru void handle_char_type(int, int); 49114402Sru}; 50114402Sru 51114402Sruconst char *spacing_type_table[] = { 52114402Sru "ordinary", 53114402Sru "operator", 54114402Sru "binary", 55114402Sru "relation", 56114402Sru "opening", 57114402Sru "closing", 58114402Sru "punctuation", 59114402Sru "inner", 60114402Sru "suppress", 61114402Sru 0, 62114402Sru}; 63114402Sru 64114402Sruconst int DIGIT_TYPE = 0; 65114402Sruconst int LETTER_TYPE = 1; 66114402Sru 67114402Sruconst char *font_type_table[] = { 68114402Sru "digit", 69114402Sru "letter", 70114402Sru 0, 71114402Sru}; 72114402Sru 73114402Srustruct char_info { 74114402Sru int spacing_type; 75114402Sru int font_type; 76114402Sru char_info(); 77114402Sru}; 78114402Sru 79114402Sruchar_info::char_info() 80114402Sru: spacing_type(ORDINARY_TYPE), font_type(DIGIT_TYPE) 81114402Sru{ 82114402Sru} 83114402Sru 84114402Srustatic char_info char_table[256]; 85114402Sru 86114402Srudeclare_ptable(char_info) 87114402Sruimplement_ptable(char_info) 88114402Sru 89114402SruPTABLE(char_info) special_char_table; 90114402Sru 91114402Srustatic int get_special_char_spacing_type(const char *ch) 92114402Sru{ 93114402Sru char_info *p = special_char_table.lookup(ch); 94114402Sru return p ? p->spacing_type : ORDINARY_TYPE; 95114402Sru} 96114402Sru 97114402Srustatic int get_special_char_font_type(const char *ch) 98114402Sru{ 99114402Sru char_info *p = special_char_table.lookup(ch); 100114402Sru return p ? p->font_type : DIGIT_TYPE; 101114402Sru} 102114402Sru 103114402Srustatic void set_special_char_type(const char *ch, int st, int ft) 104114402Sru{ 105114402Sru char_info *p = special_char_table.lookup(ch); 106114402Sru if (!p) { 107114402Sru p = new char_info[1]; 108114402Sru special_char_table.define(ch, p); 109114402Sru } 110114402Sru if (st >= 0) 111114402Sru p->spacing_type = st; 112114402Sru if (ft >= 0) 113114402Sru p->font_type = ft; 114114402Sru} 115114402Sru 116114402Sruvoid init_char_table() 117114402Sru{ 118114402Sru set_special_char_type("pl", 2, -1); // binary 119114402Sru set_special_char_type("mi", 2, -1); 120114402Sru set_special_char_type("eq", 3, -1); // relation 121114402Sru set_special_char_type("<=", 3, -1); 122114402Sru set_special_char_type(">=", 3, -1); 123114402Sru char_table['}'].spacing_type = 5; // closing 124114402Sru char_table[')'].spacing_type = 5; 125114402Sru char_table[']'].spacing_type = 5; 126114402Sru char_table['{'].spacing_type = 4; // opening 127114402Sru char_table['('].spacing_type = 4; 128114402Sru char_table['['].spacing_type = 4; 129114402Sru char_table[','].spacing_type = 6; // punctuation 130114402Sru char_table[';'].spacing_type = 6; 131114402Sru char_table[':'].spacing_type = 6; 132114402Sru char_table['.'].spacing_type = 6; 133114402Sru char_table['>'].spacing_type = 3; 134114402Sru char_table['<'].spacing_type = 3; 135114402Sru char_table['*'].spacing_type = 2; // binary 136114402Sru for (int i = 0; i < 256; i++) 137114402Sru if (csalpha(i)) 138114402Sru char_table[i].font_type = LETTER_TYPE; 139114402Sru} 140114402Sru 141114402Srustatic int lookup_spacing_type(const char *type) 142114402Sru{ 143114402Sru for (int i = 0; spacing_type_table[i] != 0; i++) 144114402Sru if (strcmp(spacing_type_table[i], type) == 0) 145114402Sru return i; 146114402Sru return -1; 147114402Sru} 148114402Sru 149114402Srustatic int lookup_font_type(const char *type) 150114402Sru{ 151114402Sru for (int i = 0; font_type_table[i] != 0; i++) 152114402Sru if (strcmp(font_type_table[i], type) == 0) 153114402Sru return i; 154114402Sru return -1; 155114402Sru} 156114402Sru 157114402Sruvoid box::set_spacing_type(char *type) 158114402Sru{ 159114402Sru int t = lookup_spacing_type(type); 160114402Sru if (t < 0) 161114402Sru error("unrecognised type `%1'", type); 162114402Sru else 163114402Sru spacing_type = t; 164114402Sru a_delete type; 165114402Sru} 166114402Sru 167114402Sruchar_box::char_box(unsigned char cc) 168114402Sru: c(cc), next_is_italic(0), prev_is_italic(0) 169114402Sru{ 170114402Sru spacing_type = char_table[c].spacing_type; 171114402Sru} 172114402Sru 173114402Sruvoid char_box::hint(unsigned flags) 174114402Sru{ 175114402Sru if (flags & HINT_PREV_IS_ITALIC) 176114402Sru prev_is_italic = 1; 177114402Sru if (flags & HINT_NEXT_IS_ITALIC) 178114402Sru next_is_italic = 1; 179114402Sru} 180114402Sru 181114402Sruvoid char_box::output() 182114402Sru{ 183114402Sru int font_type = char_table[c].font_type; 184114402Sru if (font_type != LETTER_TYPE) 185114402Sru printf("\\f[%s]", current_roman_font); 186114402Sru if (!prev_is_italic) 187114402Sru fputs("\\,", stdout); 188114402Sru if (c == '\\') 189114402Sru fputs("\\e", stdout); 190114402Sru else 191114402Sru putchar(c); 192114402Sru if (!next_is_italic) 193114402Sru fputs("\\/", stdout); 194114402Sru else 195114402Sru fputs("\\&", stdout); // suppress ligaturing and kerning 196114402Sru if (font_type != LETTER_TYPE) 197114402Sru fputs("\\fP", stdout); 198114402Sru} 199114402Sru 200114402Sruint char_box::left_is_italic() 201114402Sru{ 202114402Sru int font_type = char_table[c].font_type; 203114402Sru return font_type == LETTER_TYPE; 204114402Sru} 205114402Sru 206114402Sruint char_box::right_is_italic() 207114402Sru{ 208114402Sru int font_type = char_table[c].font_type; 209114402Sru return font_type == LETTER_TYPE; 210114402Sru} 211114402Sru 212114402Sruint char_box::is_char() 213114402Sru{ 214114402Sru return 1; 215114402Sru} 216114402Sru 217114402Sruvoid char_box::debug_print() 218114402Sru{ 219114402Sru if (c == '\\') { 220114402Sru putc('\\', stderr); 221114402Sru putc('\\', stderr); 222114402Sru } 223114402Sru else 224114402Sru putc(c, stderr); 225114402Sru} 226114402Sru 227114402Sruspecial_char_box::special_char_box(const char *t) 228114402Sru{ 229114402Sru s = strsave(t); 230114402Sru spacing_type = get_special_char_spacing_type(s); 231114402Sru} 232114402Sru 233114402Sruspecial_char_box::~special_char_box() 234114402Sru{ 235114402Sru a_delete s; 236114402Sru} 237114402Sru 238114402Sruvoid special_char_box::output() 239114402Sru{ 240114402Sru int font_type = get_special_char_font_type(s); 241114402Sru if (font_type != LETTER_TYPE) 242114402Sru printf("\\f[%s]", current_roman_font); 243114402Sru printf("\\,\\[%s]\\/", s); 244114402Sru if (font_type != LETTER_TYPE) 245114402Sru printf("\\fP"); 246114402Sru} 247114402Sru 248114402Sruint special_char_box::is_char() 249114402Sru{ 250114402Sru return 1; 251114402Sru} 252114402Sru 253114402Sruvoid special_char_box::debug_print() 254114402Sru{ 255114402Sru fprintf(stderr, "\\[%s]", s); 256114402Sru} 257114402Sru 258114402Sru 259114402Sruvoid char_box::handle_char_type(int st, int ft) 260114402Sru{ 261114402Sru if (st >= 0) 262114402Sru char_table[c].spacing_type = st; 263114402Sru if (ft >= 0) 264114402Sru char_table[c].font_type = ft; 265114402Sru} 266114402Sru 267114402Sruvoid special_char_box::handle_char_type(int st, int ft) 268114402Sru{ 269114402Sru set_special_char_type(s, st, ft); 270114402Sru} 271114402Sru 272114402Sruvoid set_char_type(const char *type, char *ch) 273114402Sru{ 274114402Sru assert(ch != 0); 275114402Sru int st = lookup_spacing_type(type); 276114402Sru int ft = lookup_font_type(type); 277114402Sru if (st < 0 && ft < 0) { 278114402Sru error("bad character type `%1'", type); 279114402Sru a_delete ch; 280114402Sru return; 281114402Sru } 282114402Sru box *b = split_text(ch); 283114402Sru b->handle_char_type(st, ft); 284114402Sru delete b; 285114402Sru} 286114402Sru 287114402Sru/* We give primes special treatment so that in ``x' sub 2'', the ``2'' 288114402Sruwill be tucked under the prime */ 289114402Sru 290114402Sruclass prime_box : public pointer_box { 291114402Sru box *pb; 292114402Srupublic: 293114402Sru prime_box(box *); 294114402Sru ~prime_box(); 295114402Sru int compute_metrics(int style); 296114402Sru void output(); 297114402Sru void compute_subscript_kern(); 298114402Sru void debug_print(); 299114402Sru void handle_char_type(int, int); 300114402Sru}; 301114402Sru 302114402Srubox *make_prime_box(box *pp) 303114402Sru{ 304114402Sru return new prime_box(pp); 305114402Sru} 306114402Sru 307114402Sruprime_box::prime_box(box *pp) : pointer_box(pp) 308114402Sru{ 309114402Sru pb = new special_char_box("fm"); 310114402Sru} 311114402Sru 312114402Sruprime_box::~prime_box() 313114402Sru{ 314114402Sru delete pb; 315114402Sru} 316114402Sru 317114402Sruint prime_box::compute_metrics(int style) 318114402Sru{ 319114402Sru int res = p->compute_metrics(style); 320114402Sru pb->compute_metrics(style); 321114402Sru printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]" 322114402Sru "+\\n[" WIDTH_FORMAT "]\n", 323114402Sru uid, p->uid, pb->uid); 324114402Sru printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]" 325114402Sru ">?\\n[" HEIGHT_FORMAT "]\n", 326114402Sru uid, p->uid, pb->uid); 327114402Sru printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]" 328114402Sru ">?\\n[" DEPTH_FORMAT "]\n", 329114402Sru uid, p->uid, pb->uid); 330114402Sru return res; 331114402Sru} 332114402Sru 333114402Sruvoid prime_box::compute_subscript_kern() 334114402Sru{ 335114402Sru p->compute_subscript_kern(); 336114402Sru printf(".nr " SUB_KERN_FORMAT " 0\\n[" WIDTH_FORMAT "]" 337114402Sru "+\\n[" SUB_KERN_FORMAT "]>?0\n", 338114402Sru uid, pb->uid, p->uid); 339114402Sru} 340114402Sru 341114402Sruvoid prime_box::output() 342114402Sru{ 343114402Sru p->output(); 344114402Sru pb->output(); 345114402Sru} 346114402Sru 347114402Sruvoid prime_box::handle_char_type(int st, int ft) 348114402Sru{ 349114402Sru p->handle_char_type(st, ft); 350114402Sru pb->handle_char_type(st, ft); 351114402Sru} 352114402Sru 353114402Sruvoid prime_box::debug_print() 354114402Sru{ 355114402Sru p->debug_print(); 356114402Sru putc('\'', stderr); 357114402Sru} 358114402Sru 359114402Srubox *split_text(char *text) 360114402Sru{ 361114402Sru list_box *lb = 0; 362114402Sru box *fb = 0; 363114402Sru char *s = text; 364114402Sru while (*s != '\0') { 365114402Sru char c = *s++; 366114402Sru box *b = 0; 367114402Sru switch (c) { 368114402Sru case '+': 369114402Sru b = new special_char_box("pl"); 370114402Sru break; 371114402Sru case '-': 372114402Sru b = new special_char_box("mi"); 373114402Sru break; 374114402Sru case '=': 375114402Sru b = new special_char_box("eq"); 376114402Sru break; 377114402Sru case '\'': 378114402Sru b = new special_char_box("fm"); 379114402Sru break; 380114402Sru case '<': 381114402Sru if (*s == '=') { 382114402Sru b = new special_char_box("<="); 383114402Sru s++; 384114402Sru break; 385114402Sru } 386114402Sru goto normal_char; 387114402Sru case '>': 388114402Sru if (*s == '=') { 389114402Sru b = new special_char_box(">="); 390114402Sru s++; 391114402Sru break; 392114402Sru } 393114402Sru goto normal_char; 394114402Sru case '\\': 395114402Sru if (*s == '\0') { 396114402Sru lex_error("bad escape"); 397114402Sru break; 398114402Sru } 399114402Sru c = *s++; 400114402Sru switch (c) { 401114402Sru case '(': 402114402Sru { 403114402Sru char buf[3]; 404114402Sru if (*s != '\0') { 405114402Sru buf[0] = *s++; 406114402Sru if (*s != '\0') { 407114402Sru buf[1] = *s++; 408114402Sru buf[2] = '\0'; 409114402Sru b = new special_char_box(buf); 410114402Sru } 411114402Sru else { 412114402Sru lex_error("bad escape"); 413114402Sru } 414114402Sru } 415114402Sru else { 416114402Sru lex_error("bad escape"); 417114402Sru } 418114402Sru } 419114402Sru break; 420114402Sru case '[': 421114402Sru { 422114402Sru char *ch = s; 423114402Sru while (*s != ']' && *s != '\0') 424114402Sru s++; 425114402Sru if (*s == '\0') 426114402Sru lex_error("bad escape"); 427114402Sru else { 428114402Sru *s++ = '\0'; 429114402Sru b = new special_char_box(ch); 430114402Sru } 431114402Sru } 432114402Sru break; 433114402Sru case 'f': 434114402Sru case 'g': 435114402Sru case 'k': 436114402Sru case 'n': 437114402Sru case '*': 438114402Sru { 439114402Sru char *escape_start = s - 2; 440114402Sru switch (*s) { 441114402Sru case '(': 442114402Sru if (*++s != '\0') 443114402Sru ++s; 444114402Sru break; 445114402Sru case '[': 446114402Sru for (++s; *s != '\0' && *s != ']'; s++) 447114402Sru ; 448114402Sru break; 449114402Sru } 450114402Sru if (*s == '\0') 451114402Sru lex_error("bad escape"); 452114402Sru else { 453114402Sru ++s; 454114402Sru char *buf = new char[s - escape_start + 1]; 455114402Sru memcpy(buf, escape_start, s - escape_start); 456114402Sru buf[s - escape_start] = '\0'; 457114402Sru b = new quoted_text_box(buf); 458114402Sru } 459114402Sru } 460114402Sru break; 461114402Sru case '-': 462114402Sru case '_': 463114402Sru { 464114402Sru char buf[2]; 465114402Sru buf[0] = c; 466114402Sru buf[1] = '\0'; 467114402Sru b = new special_char_box(buf); 468114402Sru } 469114402Sru break; 470114402Sru case '`': 471114402Sru b = new special_char_box("ga"); 472114402Sru break; 473114402Sru case '\'': 474114402Sru b = new special_char_box("aa"); 475114402Sru break; 476114402Sru case 'e': 477114402Sru case '\\': 478114402Sru b = new char_box('\\'); 479114402Sru break; 480114402Sru case '^': 481114402Sru case '|': 482114402Sru case '0': 483114402Sru { 484114402Sru char buf[3]; 485114402Sru buf[0] = '\\'; 486114402Sru buf[1] = c; 487114402Sru buf[2] = '\0'; 488114402Sru b = new quoted_text_box(strsave(buf)); 489114402Sru break; 490114402Sru } 491114402Sru default: 492114402Sru lex_error("unquoted escape"); 493114402Sru b = new quoted_text_box(strsave(s - 2)); 494114402Sru s = strchr(s, '\0'); 495114402Sru break; 496114402Sru } 497114402Sru break; 498114402Sru default: 499114402Sru normal_char: 500114402Sru b = new char_box(c); 501114402Sru break; 502114402Sru } 503114402Sru while (*s == '\'') { 504114402Sru if (b == 0) 505114402Sru b = new quoted_text_box(0); 506114402Sru b = new prime_box(b); 507114402Sru s++; 508114402Sru } 509114402Sru if (b != 0) { 510114402Sru if (lb != 0) 511114402Sru lb->append(b); 512114402Sru else if (fb != 0) { 513114402Sru lb = new list_box(fb); 514114402Sru lb->append(b); 515114402Sru } 516114402Sru else 517114402Sru fb = b; 518114402Sru } 519114402Sru } 520114402Sru a_delete text; 521114402Sru if (lb != 0) 522114402Sru return lb; 523114402Sru else if (fb != 0) 524114402Sru return fb; 525114402Sru else 526114402Sru return new quoted_text_box(0); 527114402Sru} 528114402Sru 529