1// -*- C++ -*- 2/* Copyright (C) 1989, 1990, 1991, 1992, 2003 Free Software Foundation, Inc. 3 Written by James Clark (jjc@jclark.com) 4 5This file is part of groff. 6 7groff is free software; you can redistribute it and/or modify it under 8the terms of the GNU General Public License as published by the Free 9Software Foundation; either version 2, or (at your option) any later 10version. 11 12groff is distributed in the hope that it will be useful, but WITHOUT ANY 13WARRANTY; without even the implied warranty of MERCHANTABILITY or 14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15for more details. 16 17You should have received a copy of the GNU General Public License along 18with groff; see the file COPYING. If not, write to the Free Software 19Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 20 21#include "eqn.h" 22#include "pbox.h" 23#include "ptable.h" 24 25class char_box : public simple_box { 26 unsigned char c; 27 char next_is_italic; 28 char prev_is_italic; 29public: 30 char_box(unsigned char); 31 void debug_print(); 32 void output(); 33 int is_char(); 34 int left_is_italic(); 35 int right_is_italic(); 36 void hint(unsigned); 37 void handle_char_type(int, int); 38}; 39 40class special_char_box : public simple_box { 41 char *s; 42public: 43 special_char_box(const char *); 44 ~special_char_box(); 45 void output(); 46 void debug_print(); 47 int is_char(); 48 void handle_char_type(int, int); 49}; 50 51const char *spacing_type_table[] = { 52 "ordinary", 53 "operator", 54 "binary", 55 "relation", 56 "opening", 57 "closing", 58 "punctuation", 59 "inner", 60 "suppress", 61 0, 62}; 63 64const int DIGIT_TYPE = 0; 65const int LETTER_TYPE = 1; 66 67const char *font_type_table[] = { 68 "digit", 69 "letter", 70 0, 71}; 72 73struct char_info { 74 int spacing_type; 75 int font_type; 76 char_info(); 77}; 78 79char_info::char_info() 80: spacing_type(ORDINARY_TYPE), font_type(DIGIT_TYPE) 81{ 82} 83 84static char_info char_table[256]; 85 86declare_ptable(char_info) 87implement_ptable(char_info) 88 89PTABLE(char_info) special_char_table; 90 91static int get_special_char_spacing_type(const char *ch) 92{ 93 char_info *p = special_char_table.lookup(ch); 94 return p ? p->spacing_type : ORDINARY_TYPE; 95} 96 97static int get_special_char_font_type(const char *ch) 98{ 99 char_info *p = special_char_table.lookup(ch); 100 return p ? p->font_type : DIGIT_TYPE; 101} 102 103static void set_special_char_type(const char *ch, int st, int ft) 104{ 105 char_info *p = special_char_table.lookup(ch); 106 if (!p) { 107 p = new char_info[1]; 108 special_char_table.define(ch, p); 109 } 110 if (st >= 0) 111 p->spacing_type = st; 112 if (ft >= 0) 113 p->font_type = ft; 114} 115 116void init_char_table() 117{ 118 set_special_char_type("pl", 2, -1); // binary 119 set_special_char_type("mi", 2, -1); 120 set_special_char_type("eq", 3, -1); // relation 121 set_special_char_type("<=", 3, -1); 122 set_special_char_type(">=", 3, -1); 123 char_table['}'].spacing_type = 5; // closing 124 char_table[')'].spacing_type = 5; 125 char_table[']'].spacing_type = 5; 126 char_table['{'].spacing_type = 4; // opening 127 char_table['('].spacing_type = 4; 128 char_table['['].spacing_type = 4; 129 char_table[','].spacing_type = 6; // punctuation 130 char_table[';'].spacing_type = 6; 131 char_table[':'].spacing_type = 6; 132 char_table['.'].spacing_type = 6; 133 char_table['>'].spacing_type = 3; 134 char_table['<'].spacing_type = 3; 135 char_table['*'].spacing_type = 2; // binary 136 for (int i = 0; i < 256; i++) 137 if (csalpha(i)) 138 char_table[i].font_type = LETTER_TYPE; 139} 140 141static int lookup_spacing_type(const char *type) 142{ 143 for (int i = 0; spacing_type_table[i] != 0; i++) 144 if (strcmp(spacing_type_table[i], type) == 0) 145 return i; 146 return -1; 147} 148 149static int lookup_font_type(const char *type) 150{ 151 for (int i = 0; font_type_table[i] != 0; i++) 152 if (strcmp(font_type_table[i], type) == 0) 153 return i; 154 return -1; 155} 156 157void box::set_spacing_type(char *type) 158{ 159 int t = lookup_spacing_type(type); 160 if (t < 0) 161 error("unrecognised type `%1'", type); 162 else 163 spacing_type = t; 164 a_delete type; 165} 166 167char_box::char_box(unsigned char cc) 168: c(cc), next_is_italic(0), prev_is_italic(0) 169{ 170 spacing_type = char_table[c].spacing_type; 171} 172 173void char_box::hint(unsigned flags) 174{ 175 if (flags & HINT_PREV_IS_ITALIC) 176 prev_is_italic = 1; 177 if (flags & HINT_NEXT_IS_ITALIC) 178 next_is_italic = 1; 179} 180 181void char_box::output() 182{ 183 int font_type = char_table[c].font_type; 184 if (font_type != LETTER_TYPE) 185 printf("\\f[%s]", current_roman_font); 186 if (!prev_is_italic) 187 fputs("\\,", stdout); 188 if (c == '\\') 189 fputs("\\e", stdout); 190 else 191 putchar(c); 192 if (!next_is_italic) 193 fputs("\\/", stdout); 194 else 195 fputs("\\&", stdout); // suppress ligaturing and kerning 196 if (font_type != LETTER_TYPE) 197 fputs("\\fP", stdout); 198} 199 200int char_box::left_is_italic() 201{ 202 int font_type = char_table[c].font_type; 203 return font_type == LETTER_TYPE; 204} 205 206int char_box::right_is_italic() 207{ 208 int font_type = char_table[c].font_type; 209 return font_type == LETTER_TYPE; 210} 211 212int char_box::is_char() 213{ 214 return 1; 215} 216 217void char_box::debug_print() 218{ 219 if (c == '\\') { 220 putc('\\', stderr); 221 putc('\\', stderr); 222 } 223 else 224 putc(c, stderr); 225} 226 227special_char_box::special_char_box(const char *t) 228{ 229 s = strsave(t); 230 spacing_type = get_special_char_spacing_type(s); 231} 232 233special_char_box::~special_char_box() 234{ 235 a_delete s; 236} 237 238void special_char_box::output() 239{ 240 int font_type = get_special_char_font_type(s); 241 if (font_type != LETTER_TYPE) 242 printf("\\f[%s]", current_roman_font); 243 printf("\\,\\[%s]\\/", s); 244 if (font_type != LETTER_TYPE) 245 printf("\\fP"); 246} 247 248int special_char_box::is_char() 249{ 250 return 1; 251} 252 253void special_char_box::debug_print() 254{ 255 fprintf(stderr, "\\[%s]", s); 256} 257 258 259void char_box::handle_char_type(int st, int ft) 260{ 261 if (st >= 0) 262 char_table[c].spacing_type = st; 263 if (ft >= 0) 264 char_table[c].font_type = ft; 265} 266 267void special_char_box::handle_char_type(int st, int ft) 268{ 269 set_special_char_type(s, st, ft); 270} 271 272void set_char_type(const char *type, char *ch) 273{ 274 assert(ch != 0); 275 int st = lookup_spacing_type(type); 276 int ft = lookup_font_type(type); 277 if (st < 0 && ft < 0) { 278 error("bad character type `%1'", type); 279 a_delete ch; 280 return; 281 } 282 box *b = split_text(ch); 283 b->handle_char_type(st, ft); 284 delete b; 285} 286 287/* We give primes special treatment so that in ``x' sub 2'', the ``2'' 288will be tucked under the prime */ 289 290class prime_box : public pointer_box { 291 box *pb; 292public: 293 prime_box(box *); 294 ~prime_box(); 295 int compute_metrics(int style); 296 void output(); 297 void compute_subscript_kern(); 298 void debug_print(); 299 void handle_char_type(int, int); 300}; 301 302box *make_prime_box(box *pp) 303{ 304 return new prime_box(pp); 305} 306 307prime_box::prime_box(box *pp) : pointer_box(pp) 308{ 309 pb = new special_char_box("fm"); 310} 311 312prime_box::~prime_box() 313{ 314 delete pb; 315} 316 317int prime_box::compute_metrics(int style) 318{ 319 int res = p->compute_metrics(style); 320 pb->compute_metrics(style); 321 printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]" 322 "+\\n[" WIDTH_FORMAT "]\n", 323 uid, p->uid, pb->uid); 324 printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]" 325 ">?\\n[" HEIGHT_FORMAT "]\n", 326 uid, p->uid, pb->uid); 327 printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]" 328 ">?\\n[" DEPTH_FORMAT "]\n", 329 uid, p->uid, pb->uid); 330 return res; 331} 332 333void prime_box::compute_subscript_kern() 334{ 335 p->compute_subscript_kern(); 336 printf(".nr " SUB_KERN_FORMAT " 0\\n[" WIDTH_FORMAT "]" 337 "+\\n[" SUB_KERN_FORMAT "]>?0\n", 338 uid, pb->uid, p->uid); 339} 340 341void prime_box::output() 342{ 343 p->output(); 344 pb->output(); 345} 346 347void prime_box::handle_char_type(int st, int ft) 348{ 349 p->handle_char_type(st, ft); 350 pb->handle_char_type(st, ft); 351} 352 353void prime_box::debug_print() 354{ 355 p->debug_print(); 356 putc('\'', stderr); 357} 358 359box *split_text(char *text) 360{ 361 list_box *lb = 0; 362 box *fb = 0; 363 char *s = text; 364 while (*s != '\0') { 365 char c = *s++; 366 box *b = 0; 367 switch (c) { 368 case '+': 369 b = new special_char_box("pl"); 370 break; 371 case '-': 372 b = new special_char_box("mi"); 373 break; 374 case '=': 375 b = new special_char_box("eq"); 376 break; 377 case '\'': 378 b = new special_char_box("fm"); 379 break; 380 case '<': 381 if (*s == '=') { 382 b = new special_char_box("<="); 383 s++; 384 break; 385 } 386 goto normal_char; 387 case '>': 388 if (*s == '=') { 389 b = new special_char_box(">="); 390 s++; 391 break; 392 } 393 goto normal_char; 394 case '\\': 395 if (*s == '\0') { 396 lex_error("bad escape"); 397 break; 398 } 399 c = *s++; 400 switch (c) { 401 case '(': 402 { 403 char buf[3]; 404 if (*s != '\0') { 405 buf[0] = *s++; 406 if (*s != '\0') { 407 buf[1] = *s++; 408 buf[2] = '\0'; 409 b = new special_char_box(buf); 410 } 411 else { 412 lex_error("bad escape"); 413 } 414 } 415 else { 416 lex_error("bad escape"); 417 } 418 } 419 break; 420 case '[': 421 { 422 char *ch = s; 423 while (*s != ']' && *s != '\0') 424 s++; 425 if (*s == '\0') 426 lex_error("bad escape"); 427 else { 428 *s++ = '\0'; 429 b = new special_char_box(ch); 430 } 431 } 432 break; 433 case 'f': 434 case 'g': 435 case 'k': 436 case 'n': 437 case '*': 438 { 439 char *escape_start = s - 2; 440 switch (*s) { 441 case '(': 442 if (*++s != '\0') 443 ++s; 444 break; 445 case '[': 446 for (++s; *s != '\0' && *s != ']'; s++) 447 ; 448 break; 449 } 450 if (*s == '\0') 451 lex_error("bad escape"); 452 else { 453 ++s; 454 char *buf = new char[s - escape_start + 1]; 455 memcpy(buf, escape_start, s - escape_start); 456 buf[s - escape_start] = '\0'; 457 b = new quoted_text_box(buf); 458 } 459 } 460 break; 461 case '-': 462 case '_': 463 { 464 char buf[2]; 465 buf[0] = c; 466 buf[1] = '\0'; 467 b = new special_char_box(buf); 468 } 469 break; 470 case '`': 471 b = new special_char_box("ga"); 472 break; 473 case '\'': 474 b = new special_char_box("aa"); 475 break; 476 case 'e': 477 case '\\': 478 b = new char_box('\\'); 479 break; 480 case '^': 481 case '|': 482 case '0': 483 { 484 char buf[3]; 485 buf[0] = '\\'; 486 buf[1] = c; 487 buf[2] = '\0'; 488 b = new quoted_text_box(strsave(buf)); 489 break; 490 } 491 default: 492 lex_error("unquoted escape"); 493 b = new quoted_text_box(strsave(s - 2)); 494 s = strchr(s, '\0'); 495 break; 496 } 497 break; 498 default: 499 normal_char: 500 b = new char_box(c); 501 break; 502 } 503 while (*s == '\'') { 504 if (b == 0) 505 b = new quoted_text_box(0); 506 b = new prime_box(b); 507 s++; 508 } 509 if (b != 0) { 510 if (lb != 0) 511 lb->append(b); 512 else if (fb != 0) { 513 lb = new list_box(fb); 514 lb->append(b); 515 } 516 else 517 fb = b; 518 } 519 } 520 a_delete text; 521 if (lb != 0) 522 return lb; 523 else if (fb != 0) 524 return fb; 525 else 526 return new quoted_text_box(0); 527} 528 529