1114402Sru// -*- C++ -*- 2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2002, 2004 3151497Sru Free Software Foundation, Inc. 4114402Sru Written by James Clark (jjc@jclark.com) 5114402Sru 6114402SruThis file is part of groff. 7114402Sru 8114402Srugroff is free software; you can redistribute it and/or modify it under 9114402Sruthe terms of the GNU General Public License as published by the Free 10114402SruSoftware Foundation; either version 2, or (at your option) any later 11114402Sruversion. 12114402Sru 13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY 14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or 15114402SruFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16114402Srufor more details. 17114402Sru 18114402SruYou should have received a copy of the GNU General Public License along 19114402Sruwith groff; see the file COPYING. If not, write to the Free Software 20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 21114402Sru 22114402Sru#include "eqn.h" 23114402Sru#include "pbox.h" 24114402Sru 25114402Sruconst char *current_roman_font; 26114402Sru 27114402Sruchar *gfont = 0; 28114402Sruchar *grfont = 0; 29114402Sruchar *gbfont = 0; 30114402Sruint gsize = 0; 31114402Sru 32114402Sruint script_size_reduction = -1; // negative means reduce by a percentage 33114402Sru 34114402Sruint positive_space = -1; 35114402Sruint negative_space = -1; 36114402Sru 37114402Sruint minimum_size = 5; 38114402Sru 39114402Sruint fat_offset = 4; 40114402Sruint body_height = 85; 41114402Sruint body_depth = 35; 42114402Sru 43114402Sruint over_hang = 0; 44114402Sruint accent_width = 31; 45114402Sruint delimiter_factor = 900; 46114402Sruint delimiter_shortfall = 50; 47114402Sru 48114402Sruint null_delimiter_space = 12; 49114402Sruint script_space = 5; 50114402Sruint thin_space = 17; 51114402Sruint medium_space = 22; 52114402Sruint thick_space = 28; 53114402Sru 54114402Sruint num1 = 70; 55114402Sruint num2 = 40; 56114402Sru// we don't use num3, because we don't have \atop 57114402Sruint denom1 = 70; 58114402Sruint denom2 = 36; 59114402Sruint axis_height = 26; // in 100ths of an em 60114402Sruint sup1 = 42; 61114402Sruint sup2 = 37; 62114402Sruint sup3 = 28; 63114402Sruint default_rule_thickness = 4; 64114402Sruint sub1 = 20; 65114402Sruint sub2 = 23; 66114402Sruint sup_drop = 38; 67114402Sruint sub_drop = 5; 68114402Sruint x_height = 45; 69114402Sruint big_op_spacing1 = 11; 70114402Sruint big_op_spacing2 = 17; 71114402Sruint big_op_spacing3 = 20; 72114402Sruint big_op_spacing4 = 60; 73114402Sruint big_op_spacing5 = 10; 74114402Sru 75114402Sru// These are for piles and matrices. 76114402Sru 77114402Sruint baseline_sep = 140; // = num1 + denom1 78114402Sruint shift_down = 26; // = axis_height 79114402Sruint column_sep = 100; // = em space 80114402Sruint matrix_side_sep = 17; // = thin space 81114402Sru 82114402Sruint nroff = 0; // should we grok ndefine or tdefine? 83114402Sru 84151497Srustruct S { 85114402Sru const char *name; 86114402Sru int *ptr; 87114402Sru} param_table[] = { 88114402Sru { "fat_offset", &fat_offset }, 89114402Sru { "over_hang", &over_hang }, 90114402Sru { "accent_width", &accent_width }, 91114402Sru { "delimiter_factor", &delimiter_factor }, 92114402Sru { "delimiter_shortfall", &delimiter_shortfall }, 93114402Sru { "null_delimiter_space", &null_delimiter_space }, 94114402Sru { "script_space", &script_space }, 95114402Sru { "thin_space", &thin_space }, 96114402Sru { "medium_space", &medium_space }, 97114402Sru { "thick_space", &thick_space }, 98114402Sru { "num1", &num1 }, 99114402Sru { "num2", &num2 }, 100114402Sru { "denom1", &denom1 }, 101114402Sru { "denom2", &denom2 }, 102114402Sru { "axis_height", &axis_height }, 103114402Sru { "sup1", ¹ }, 104114402Sru { "sup2", ² }, 105114402Sru { "sup3", ³ }, 106114402Sru { "default_rule_thickness", &default_rule_thickness }, 107114402Sru { "sub1", &sub1 }, 108114402Sru { "sub2", &sub2 }, 109114402Sru { "sup_drop", &sup_drop }, 110114402Sru { "sub_drop", &sub_drop }, 111114402Sru { "x_height", &x_height }, 112114402Sru { "big_op_spacing1", &big_op_spacing1 }, 113114402Sru { "big_op_spacing2", &big_op_spacing2 }, 114114402Sru { "big_op_spacing3", &big_op_spacing3 }, 115114402Sru { "big_op_spacing4", &big_op_spacing4 }, 116114402Sru { "big_op_spacing5", &big_op_spacing5 }, 117114402Sru { "minimum_size", &minimum_size }, 118114402Sru { "baseline_sep", &baseline_sep }, 119114402Sru { "shift_down", &shift_down }, 120114402Sru { "column_sep", &column_sep }, 121114402Sru { "matrix_side_sep", &matrix_side_sep }, 122114402Sru { "draw_lines", &draw_flag }, 123114402Sru { "body_height", &body_height }, 124114402Sru { "body_depth", &body_depth }, 125114402Sru { "nroff", &nroff }, 126114402Sru { 0, 0 } 127114402Sru}; 128114402Sru 129114402Sruvoid set_param(const char *name, int value) 130114402Sru{ 131114402Sru for (int i = 0; param_table[i].name != 0; i++) 132114402Sru if (strcmp(param_table[i].name, name) == 0) { 133114402Sru *param_table[i].ptr = value; 134114402Sru return; 135114402Sru } 136114402Sru error("unrecognised parameter `%1'", name); 137114402Sru} 138114402Sru 139114402Sruint script_style(int style) 140114402Sru{ 141114402Sru return style > SCRIPT_STYLE ? style - 2 : style; 142114402Sru} 143114402Sru 144114402Sruint cramped_style(int style) 145114402Sru{ 146114402Sru return (style & 1) ? style - 1 : style; 147114402Sru} 148114402Sru 149114402Sruvoid set_space(int n) 150114402Sru{ 151114402Sru if (n < 0) 152114402Sru negative_space = -n; 153114402Sru else 154114402Sru positive_space = n; 155114402Sru} 156114402Sru 157114402Sru// Return 0 if the specified size is bad. 158114402Sru// The caller is responsible for giving the error message. 159114402Sru 160114402Sruint set_gsize(const char *s) 161114402Sru{ 162114402Sru const char *p = (*s == '+' || *s == '-') ? s + 1 : s; 163114402Sru char *end; 164114402Sru long n = strtol(p, &end, 10); 165114402Sru if (n <= 0 || *end != '\0' || n > INT_MAX) 166114402Sru return 0; 167114402Sru if (p > s) { 168114402Sru if (!gsize) 169114402Sru gsize = 10; 170114402Sru if (*s == '+') { 171114402Sru if (gsize > INT_MAX - n) 172114402Sru return 0; 173114402Sru gsize += int(n); 174114402Sru } 175114402Sru else { 176114402Sru if (gsize - n <= 0) 177114402Sru return 0; 178114402Sru gsize -= int(n); 179114402Sru } 180114402Sru } 181114402Sru else 182114402Sru gsize = int(n); 183114402Sru return 1; 184114402Sru} 185114402Sru 186114402Sruvoid set_script_reduction(int n) 187114402Sru{ 188114402Sru script_size_reduction = n; 189114402Sru} 190114402Sru 191114402Sruconst char *get_gfont() 192114402Sru{ 193114402Sru return gfont ? gfont : "I"; 194114402Sru} 195114402Sru 196114402Sruconst char *get_grfont() 197114402Sru{ 198114402Sru return grfont ? grfont : "R"; 199114402Sru} 200114402Sru 201114402Sruconst char *get_gbfont() 202114402Sru{ 203114402Sru return gbfont ? gbfont : "B"; 204114402Sru} 205114402Sru 206114402Sruvoid set_gfont(const char *s) 207114402Sru{ 208114402Sru a_delete gfont; 209114402Sru gfont = strsave(s); 210114402Sru} 211114402Sru 212114402Sruvoid set_grfont(const char *s) 213114402Sru{ 214114402Sru a_delete grfont; 215114402Sru grfont = strsave(s); 216114402Sru} 217114402Sru 218114402Sruvoid set_gbfont(const char *s) 219114402Sru{ 220114402Sru a_delete gbfont; 221114402Sru gbfont = strsave(s); 222114402Sru} 223114402Sru 224114402Sru// this must be precisely 2 characters in length 225114402Sru#define COMPATIBLE_REG "0C" 226114402Sru 227114402Sruvoid start_string() 228114402Sru{ 229114402Sru printf(".nr " COMPATIBLE_REG " \\n(.C\n"); 230114402Sru printf(".cp 0\n"); 231114402Sru printf(".ds " LINE_STRING "\n"); 232114402Sru} 233114402Sru 234114402Sruvoid output_string() 235114402Sru{ 236114402Sru printf("\\*(" LINE_STRING "\n"); 237114402Sru} 238114402Sru 239114402Sruvoid restore_compatibility() 240114402Sru{ 241114402Sru printf(".cp \\n(" COMPATIBLE_REG "\n"); 242114402Sru} 243114402Sru 244114402Sruvoid do_text(const char *s) 245114402Sru{ 246114402Sru printf(".eo\n"); 247114402Sru printf(".as " LINE_STRING " \"%s\n", s); 248114402Sru printf(".ec\n"); 249114402Sru} 250114402Sru 251114402Sruvoid set_minimum_size(int n) 252114402Sru{ 253114402Sru minimum_size = n; 254114402Sru} 255114402Sru 256114402Sruvoid set_script_size() 257114402Sru{ 258114402Sru if (minimum_size < 0) 259114402Sru minimum_size = 0; 260114402Sru if (script_size_reduction >= 0) 261114402Sru printf(".ps \\n[.s]-%d>?%d\n", script_size_reduction, minimum_size); 262114402Sru else 263114402Sru printf(".ps (u;\\n[.ps]*7+5/10>?%d)\n", minimum_size); 264114402Sru} 265114402Sru 266114402Sruint box::next_uid = 0; 267114402Sru 268114402Srubox::box() : spacing_type(ORDINARY_TYPE), uid(next_uid++) 269114402Sru{ 270114402Sru} 271114402Sru 272114402Srubox::~box() 273114402Sru{ 274114402Sru} 275114402Sru 276114402Sruvoid box::top_level() 277114402Sru{ 278114402Sru // debug_print(); 279114402Sru // putc('\n', stderr); 280114402Sru box *b = this; 281114402Sru printf(".nr " SAVED_FONT_REG " \\n[.f]\n"); 282114402Sru printf(".ft\n"); 283114402Sru printf(".nr " SAVED_PREV_FONT_REG " \\n[.f]\n"); 284114402Sru printf(".ft %s\n", get_gfont()); 285114402Sru printf(".nr " SAVED_SIZE_REG " \\n[.ps]\n"); 286114402Sru if (gsize > 0) { 287114402Sru char buf[INT_DIGITS + 1]; 288114402Sru sprintf(buf, "%d", gsize); 289114402Sru b = new size_box(strsave(buf), b); 290114402Sru } 291114402Sru current_roman_font = get_grfont(); 292114402Sru // This catches tabs used within \Z (which aren't allowed). 293114402Sru b->check_tabs(0); 294114402Sru int r = b->compute_metrics(DISPLAY_STYLE); 295114402Sru printf(".ft \\n[" SAVED_PREV_FONT_REG "]\n"); 296114402Sru printf(".ft \\n[" SAVED_FONT_REG "]\n"); 297114402Sru printf(".nr " MARK_OR_LINEUP_FLAG_REG " %d\n", r); 298114402Sru if (r == FOUND_MARK) { 299114402Sru printf(".nr " SAVED_MARK_REG " \\n[" MARK_REG "]\n"); 300114402Sru printf(".nr " MARK_WIDTH_REG " 0\\n[" WIDTH_FORMAT "]\n", b->uid); 301114402Sru } 302114402Sru else if (r == FOUND_LINEUP) 303114402Sru printf(".if r" SAVED_MARK_REG " .as1 " LINE_STRING " \\h'\\n[" 304114402Sru SAVED_MARK_REG "]u-\\n[" MARK_REG "]u'\n"); 305114402Sru else 306114402Sru assert(r == FOUND_NOTHING); 307114402Sru // The problem here is that the argument to \f is read in copy mode, 308114402Sru // so we cannot use \E there; so we hide it in a string instead. 309114402Sru // Another problem is that if we use \R directly, then the space will 310114402Sru // prevent it working in a macro argument. 311114402Sru printf(".ds " SAVE_FONT_STRING " " 312114402Sru "\\R'" SAVED_INLINE_FONT_REG " \\\\n[.f]'" 313114402Sru "\\fP" 314114402Sru "\\R'" SAVED_INLINE_PREV_FONT_REG " \\\\n[.f]'" 315114402Sru "\\R'" SAVED_INLINE_SIZE_REG " \\\\n[.ps]'" 316114402Sru "\\s0" 317114402Sru "\\R'" SAVED_INLINE_PREV_SIZE_REG " \\\\n[.ps]'" 318114402Sru "\n" 319114402Sru ".ds " RESTORE_FONT_STRING " " 320114402Sru "\\f[\\\\n[" SAVED_INLINE_PREV_FONT_REG "]]" 321114402Sru "\\f[\\\\n[" SAVED_INLINE_FONT_REG "]]" 322114402Sru "\\s'\\\\n[" SAVED_INLINE_PREV_SIZE_REG "]u'" 323114402Sru "\\s'\\\\n[" SAVED_INLINE_SIZE_REG "]u'" 324114402Sru "\n"); 325114402Sru printf(".as1 " LINE_STRING " \\&\\E*[" SAVE_FONT_STRING "]"); 326114402Sru printf("\\f[%s]", get_gfont()); 327114402Sru printf("\\s'\\En[" SAVED_SIZE_REG "]u'"); 328114402Sru current_roman_font = get_grfont(); 329114402Sru b->output(); 330114402Sru printf("\\E*[" RESTORE_FONT_STRING "]\n"); 331114402Sru if (r == FOUND_LINEUP) 332114402Sru printf(".if r" SAVED_MARK_REG " .as1 " LINE_STRING " \\h'\\n[" 333114402Sru MARK_WIDTH_REG "]u-\\n[" SAVED_MARK_REG "]u-(\\n[" 334114402Sru WIDTH_FORMAT "]u-\\n[" MARK_REG "]u)'\n", 335114402Sru b->uid); 336114402Sru b->extra_space(); 337114402Sru if (!inline_flag) 338114402Sru printf(".ne \\n[" HEIGHT_FORMAT "]u-%dM>?0+(\\n[" 339114402Sru DEPTH_FORMAT "]u-%dM>?0)\n", 340114402Sru b->uid, body_height, b->uid, body_depth); 341114402Sru delete b; 342114402Sru next_uid = 0; 343114402Sru} 344114402Sru 345114402Sru// gpic defines this register so as to make geqn not produce `\x's 346114402Sru#define EQN_NO_EXTRA_SPACE_REG "0x" 347114402Sru 348114402Sruvoid box::extra_space() 349114402Sru{ 350114402Sru printf(".if !r" EQN_NO_EXTRA_SPACE_REG " " 351114402Sru ".nr " EQN_NO_EXTRA_SPACE_REG " 0\n"); 352114402Sru if (positive_space >= 0 || negative_space >= 0) { 353114402Sru if (positive_space > 0) 354114402Sru printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] " 355114402Sru ".as1 " LINE_STRING " \\x'-%dM'\n", positive_space); 356114402Sru if (negative_space > 0) 357114402Sru printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] " 358114402Sru ".as1 " LINE_STRING " \\x'%dM'\n", negative_space); 359114402Sru positive_space = negative_space = -1; 360114402Sru } 361114402Sru else { 362114402Sru printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] " 363114402Sru ".if \\n[" HEIGHT_FORMAT "]>%dM .as1 " LINE_STRING 364114402Sru " \\x'-(\\n[" HEIGHT_FORMAT 365114402Sru "]u-%dM)'\n", 366114402Sru uid, body_height, uid, body_height); 367114402Sru printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] " 368114402Sru ".if \\n[" DEPTH_FORMAT "]>%dM .as1 " LINE_STRING 369114402Sru " \\x'\\n[" DEPTH_FORMAT 370114402Sru "]u-%dM'\n", 371114402Sru uid, body_depth, uid, body_depth); 372114402Sru } 373114402Sru} 374114402Sru 375114402Sruint box::compute_metrics(int) 376114402Sru{ 377114402Sru printf(".nr " WIDTH_FORMAT " 0\n", uid); 378114402Sru printf(".nr " HEIGHT_FORMAT " 0\n", uid); 379114402Sru printf(".nr " DEPTH_FORMAT " 0\n", uid); 380114402Sru return FOUND_NOTHING; 381114402Sru} 382114402Sru 383114402Sruvoid box::compute_subscript_kern() 384114402Sru{ 385114402Sru printf(".nr " SUB_KERN_FORMAT " 0\n", uid); 386114402Sru} 387114402Sru 388114402Sruvoid box::compute_skew() 389114402Sru{ 390114402Sru printf(".nr " SKEW_FORMAT " 0\n", uid); 391114402Sru} 392114402Sru 393114402Sruvoid box::output() 394114402Sru{ 395114402Sru} 396114402Sru 397114402Sruvoid box::check_tabs(int) 398114402Sru{ 399114402Sru} 400114402Sru 401114402Sruint box::is_char() 402114402Sru{ 403114402Sru return 0; 404114402Sru} 405114402Sru 406114402Sruint box::left_is_italic() 407114402Sru{ 408114402Sru return 0; 409114402Sru} 410114402Sru 411114402Sruint box::right_is_italic() 412114402Sru{ 413114402Sru return 0; 414114402Sru} 415114402Sru 416114402Sruvoid box::hint(unsigned) 417114402Sru{ 418114402Sru} 419114402Sru 420114402Sruvoid box::handle_char_type(int, int) 421114402Sru{ 422114402Sru} 423114402Sru 424114402Sru 425114402Srubox_list::box_list(box *pp) 426114402Sru{ 427114402Sru p = new box*[10]; 428114402Sru for (int i = 0; i < 10; i++) 429114402Sru p[i] = 0; 430114402Sru maxlen = 10; 431114402Sru len = 1; 432114402Sru p[0] = pp; 433114402Sru} 434114402Sru 435114402Sruvoid box_list::append(box *pp) 436114402Sru{ 437114402Sru if (len + 1 > maxlen) { 438114402Sru box **oldp = p; 439114402Sru maxlen *= 2; 440114402Sru p = new box*[maxlen]; 441114402Sru memcpy(p, oldp, sizeof(box*)*len); 442114402Sru a_delete oldp; 443114402Sru } 444114402Sru p[len++] = pp; 445114402Sru} 446114402Sru 447114402Srubox_list::~box_list() 448114402Sru{ 449114402Sru for (int i = 0; i < len; i++) 450114402Sru delete p[i]; 451114402Sru a_delete p; 452114402Sru} 453114402Sru 454114402Sruvoid box_list::list_check_tabs(int level) 455114402Sru{ 456114402Sru for (int i = 0; i < len; i++) 457114402Sru p[i]->check_tabs(level); 458114402Sru} 459114402Sru 460114402Sru 461114402Srupointer_box::pointer_box(box *pp) : p(pp) 462114402Sru{ 463114402Sru spacing_type = p->spacing_type; 464114402Sru} 465114402Sru 466114402Srupointer_box::~pointer_box() 467114402Sru{ 468114402Sru delete p; 469114402Sru} 470114402Sru 471114402Sruint pointer_box::compute_metrics(int style) 472114402Sru{ 473114402Sru int r = p->compute_metrics(style); 474114402Sru printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); 475114402Sru printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); 476114402Sru printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); 477114402Sru return r; 478114402Sru} 479114402Sru 480114402Sruvoid pointer_box::compute_subscript_kern() 481114402Sru{ 482114402Sru p->compute_subscript_kern(); 483114402Sru printf(".nr " SUB_KERN_FORMAT " \\n[" SUB_KERN_FORMAT "]\n", uid, p->uid); 484114402Sru} 485114402Sru 486114402Sruvoid pointer_box::compute_skew() 487114402Sru{ 488114402Sru p->compute_skew(); 489114402Sru printf(".nr " SKEW_FORMAT " 0\\n[" SKEW_FORMAT "]\n", 490114402Sru uid, p->uid); 491114402Sru} 492114402Sru 493114402Sruvoid pointer_box::check_tabs(int level) 494114402Sru{ 495114402Sru p->check_tabs(level); 496114402Sru} 497114402Sru 498114402Sruint simple_box::compute_metrics(int) 499114402Sru{ 500114402Sru printf(".nr " WIDTH_FORMAT " 0\\w" DELIMITER_CHAR, uid); 501114402Sru output(); 502114402Sru printf(DELIMITER_CHAR "\n"); 503114402Sru printf(".nr " HEIGHT_FORMAT " 0>?\\n[rst]\n", uid); 504114402Sru printf(".nr " DEPTH_FORMAT " 0-\\n[rsb]>?0\n", uid); 505114402Sru printf(".nr " SUB_KERN_FORMAT " 0-\\n[ssc]>?0\n", uid); 506114402Sru printf(".nr " SKEW_FORMAT " 0\\n[skw]\n", uid); 507114402Sru return FOUND_NOTHING; 508114402Sru} 509114402Sru 510114402Sruvoid simple_box::compute_subscript_kern() 511114402Sru{ 512114402Sru // do nothing, we already computed it in do_metrics 513114402Sru} 514114402Sru 515114402Sruvoid simple_box::compute_skew() 516114402Sru{ 517114402Sru // do nothing, we already computed it in do_metrics 518114402Sru} 519114402Sru 520114402Sruint box::is_simple() 521114402Sru{ 522114402Sru return 0; 523114402Sru} 524114402Sru 525114402Sruint simple_box::is_simple() 526114402Sru{ 527114402Sru return 1; 528114402Sru} 529114402Sru 530114402Sruquoted_text_box::quoted_text_box(char *s) : text(s) 531114402Sru{ 532114402Sru} 533114402Sru 534114402Sruquoted_text_box::~quoted_text_box() 535114402Sru{ 536114402Sru a_delete text; 537114402Sru} 538114402Sru 539114402Sruvoid quoted_text_box::output() 540114402Sru{ 541114402Sru if (text) 542114402Sru fputs(text, stdout); 543114402Sru} 544114402Sru 545114402Srutab_box::tab_box() : disabled(0) 546114402Sru{ 547114402Sru} 548114402Sru 549114402Sru// We treat a tab_box as having width 0 for width computations. 550114402Sru 551114402Sruvoid tab_box::output() 552114402Sru{ 553114402Sru if (!disabled) 554114402Sru printf("\\t"); 555114402Sru} 556114402Sru 557114402Sruvoid tab_box::check_tabs(int level) 558114402Sru{ 559114402Sru if (level > 0) { 560114402Sru error("tabs allowed only at outermost level"); 561114402Sru disabled = 1; 562114402Sru } 563114402Sru} 564114402Sru 565114402Sruspace_box::space_box() 566114402Sru{ 567114402Sru spacing_type = SUPPRESS_TYPE; 568114402Sru} 569114402Sru 570114402Sruvoid space_box::output() 571114402Sru{ 572114402Sru printf("\\h'%dM'", thick_space); 573114402Sru} 574114402Sru 575114402Sruhalf_space_box::half_space_box() 576114402Sru{ 577114402Sru spacing_type = SUPPRESS_TYPE; 578114402Sru} 579114402Sru 580114402Sruvoid half_space_box::output() 581114402Sru{ 582114402Sru printf("\\h'%dM'", thin_space); 583114402Sru} 584114402Sru 585114402Sruvoid box_list::list_debug_print(const char *sep) 586114402Sru{ 587114402Sru p[0]->debug_print(); 588114402Sru for (int i = 1; i < len; i++) { 589114402Sru fprintf(stderr, "%s", sep); 590114402Sru p[i]->debug_print(); 591114402Sru } 592114402Sru} 593114402Sru 594114402Sruvoid quoted_text_box::debug_print() 595114402Sru{ 596114402Sru fprintf(stderr, "\"%s\"", (text ? text : "")); 597114402Sru} 598114402Sru 599114402Sruvoid half_space_box::debug_print() 600114402Sru{ 601114402Sru fprintf(stderr, "^"); 602114402Sru} 603114402Sru 604114402Sruvoid space_box::debug_print() 605114402Sru{ 606114402Sru fprintf(stderr, "~"); 607114402Sru} 608114402Sru 609114402Sruvoid tab_box::debug_print() 610114402Sru{ 611114402Sru fprintf(stderr, "<tab>"); 612114402Sru} 613