1114402Sru// -*- C++ -*- 2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2004 3114402Sru 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 23114402Sru// diversions 24114402Sru 25114402Sru#include "troff.h" 26114402Sru#include "dictionary.h" 27114402Sru#include "hvunits.h" 28151497Sru#include "stringclass.h" 29151497Sru#include "mtsm.h" 30114402Sru#include "env.h" 31114402Sru#include "request.h" 32114402Sru#include "node.h" 33114402Sru#include "token.h" 34114402Sru#include "div.h" 35114402Sru#include "reg.h" 36114402Sru 37151497Sru#include "nonposix.h" 38151497Sru 39114402Sruint exit_started = 0; // the exit process has started 40114402Sruint done_end_macro = 0; // the end macro (if any) has finished 41114402Sruint seen_last_page_ejector = 0; // seen the LAST_PAGE_EJECTOR cookie 42114402Sruint last_page_number = 0; // if > 0, the number of the last page 43114402Sru // specified with -o 44114402Srustatic int began_page_in_end_macro = 0; // a new page was begun during the end macro 45114402Sru 46114402Srustatic int last_post_line_extra_space = 0; // needed for \n(.a 47114402Srustatic int nl_reg_contents = -1; 48114402Srustatic int dl_reg_contents = 0; 49114402Srustatic int dn_reg_contents = 0; 50114402Srustatic int vertical_position_traps_flag = 1; 51114402Srustatic vunits truncated_space; 52114402Srustatic vunits needed_space; 53114402Sru 54114402Srudiversion::diversion(symbol s) 55114402Sru: prev(0), nm(s), vertical_position(V0), high_water_mark(V0), 56151497Sru any_chars_added(0), no_space_mode(0), needs_push(0), saved_seen_break(0), 57151497Sru saved_seen_space(0), saved_seen_eol(0), saved_suppress_next_eol(0), 58151497Sru marked_place(V0) 59114402Sru{ 60114402Sru} 61114402Sru 62114402Srustruct vertical_size { 63114402Sru vunits pre_extra, post_extra, pre, post; 64114402Sru vertical_size(vunits vs, vunits post_vs); 65114402Sru}; 66114402Sru 67114402Sruvertical_size::vertical_size(vunits vs, vunits post_vs) 68114402Sru: pre_extra(V0), post_extra(V0), pre(vs), post(post_vs) 69114402Sru{ 70114402Sru} 71114402Sru 72114402Sruvoid node::set_vertical_size(vertical_size *) 73114402Sru{ 74114402Sru} 75114402Sru 76114402Sruvoid extra_size_node::set_vertical_size(vertical_size *v) 77114402Sru{ 78114402Sru if (n < V0) { 79114402Sru if (-n > v->pre_extra) 80114402Sru v->pre_extra = -n; 81114402Sru } 82114402Sru else if (n > v->post_extra) 83114402Sru v->post_extra = n; 84114402Sru} 85114402Sru 86114402Sruvoid vertical_size_node::set_vertical_size(vertical_size *v) 87114402Sru{ 88114402Sru if (n < V0) 89114402Sru v->pre = -n; 90114402Sru else 91114402Sru v->post = n; 92114402Sru} 93114402Sru 94114402Srutop_level_diversion *topdiv; 95114402Sru 96114402Srudiversion *curdiv; 97114402Sru 98114402Sruvoid do_divert(int append, int boxing) 99114402Sru{ 100114402Sru tok.skip(); 101114402Sru symbol nm = get_name(); 102114402Sru if (nm.is_null()) { 103114402Sru if (curdiv->prev) { 104151497Sru curenv->seen_break = curdiv->saved_seen_break; 105151497Sru curenv->seen_space = curdiv->saved_seen_space; 106151497Sru curenv->seen_eol = curdiv->saved_seen_eol; 107151497Sru curenv->suppress_next_eol = curdiv->saved_suppress_next_eol; 108114402Sru if (boxing) { 109114402Sru curenv->line = curdiv->saved_line; 110114402Sru curenv->width_total = curdiv->saved_width_total; 111114402Sru curenv->space_total = curdiv->saved_space_total; 112114402Sru curenv->saved_indent = curdiv->saved_saved_indent; 113114402Sru curenv->target_text_length = curdiv->saved_target_text_length; 114114402Sru curenv->prev_line_interrupted = curdiv->saved_prev_line_interrupted; 115114402Sru } 116114402Sru diversion *temp = curdiv; 117114402Sru curdiv = curdiv->prev; 118114402Sru delete temp; 119114402Sru } 120114402Sru else 121114402Sru warning(WARN_DI, "diversion stack underflow"); 122114402Sru } 123114402Sru else { 124114402Sru macro_diversion *md = new macro_diversion(nm, append); 125114402Sru md->prev = curdiv; 126114402Sru curdiv = md; 127151497Sru curdiv->saved_seen_break = curenv->seen_break; 128151497Sru curdiv->saved_seen_space = curenv->seen_space; 129151497Sru curdiv->saved_seen_eol = curenv->seen_eol; 130151497Sru curdiv->saved_suppress_next_eol = curenv->suppress_next_eol; 131151497Sru curenv->seen_break = 0; 132151497Sru curenv->seen_space = 0; 133151497Sru curenv->seen_eol = 0; 134114402Sru if (boxing) { 135114402Sru curdiv->saved_line = curenv->line; 136114402Sru curdiv->saved_width_total = curenv->width_total; 137114402Sru curdiv->saved_space_total = curenv->space_total; 138114402Sru curdiv->saved_saved_indent = curenv->saved_indent; 139114402Sru curdiv->saved_target_text_length = curenv->target_text_length; 140114402Sru curdiv->saved_prev_line_interrupted = curenv->prev_line_interrupted; 141114402Sru curenv->line = 0; 142114402Sru curenv->start_line(); 143114402Sru } 144114402Sru } 145114402Sru skip_line(); 146114402Sru} 147114402Sru 148114402Sruvoid divert() 149114402Sru{ 150114402Sru do_divert(0, 0); 151114402Sru} 152114402Sru 153114402Sruvoid divert_append() 154114402Sru{ 155114402Sru do_divert(1, 0); 156114402Sru} 157114402Sru 158114402Sruvoid box() 159114402Sru{ 160114402Sru do_divert(0, 1); 161114402Sru} 162114402Sru 163114402Sruvoid box_append() 164114402Sru{ 165114402Sru do_divert(1, 1); 166114402Sru} 167114402Sru 168114402Sruvoid diversion::need(vunits n) 169114402Sru{ 170114402Sru vunits d = distance_to_next_trap(); 171114402Sru if (d < n) { 172114402Sru truncated_space = -d; 173114402Sru needed_space = n; 174114402Sru space(d, 1); 175114402Sru } 176114402Sru} 177114402Sru 178114402Srumacro_diversion::macro_diversion(symbol s, int append) 179114402Sru: diversion(s), max_width(H0) 180114402Sru{ 181114402Sru#if 0 182114402Sru if (append) { 183114402Sru /* We don't allow recursive appends eg: 184114402Sru 185114402Sru .da a 186114402Sru .a 187114402Sru .di 188114402Sru 189114402Sru This causes an infinite loop in troff anyway. 190114402Sru This is because the user could do 191114402Sru 192114402Sru .as a foo 193114402Sru 194114402Sru in the diversion, and this would mess things up royally, 195114402Sru since there would be two things appending to the same 196114402Sru macro_header. 197114402Sru To make it work, we would have to copy the _contents_ 198114402Sru of the macro into which we were diverting; this doesn't 199114402Sru strike me as worthwhile. 200114402Sru However, 201114402Sru 202114402Sru .di a 203114402Sru .a 204114402Sru .a 205114402Sru .di 206114402Sru 207114402Sru will work and will make `a' contain two copies of what it contained 208114402Sru before; in troff, `a' would contain nothing. */ 209114402Sru request_or_macro *rm 210114402Sru = (request_or_macro *)request_dictionary.remove(s); 211114402Sru if (!rm || (mac = rm->to_macro()) == 0) 212114402Sru mac = new macro; 213114402Sru } 214114402Sru else 215114402Sru mac = new macro; 216114402Sru#endif 217114402Sru // We can now catch the situation described above by comparing 218114402Sru // the length of the charlist in the macro_header with the length 219114402Sru // stored in the macro. When we detect this, we copy the contents. 220151497Sru mac = new macro(1); 221114402Sru if (append) { 222114402Sru request_or_macro *rm 223114402Sru = (request_or_macro *)request_dictionary.lookup(s); 224114402Sru if (rm) { 225114402Sru macro *m = rm->to_macro(); 226114402Sru if (m) 227114402Sru *mac = *m; 228114402Sru } 229114402Sru } 230114402Sru} 231114402Sru 232114402Srumacro_diversion::~macro_diversion() 233114402Sru{ 234114402Sru request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm); 235114402Sru macro *m = rm ? rm->to_macro() : 0; 236114402Sru if (m) { 237114402Sru *m = *mac; 238114402Sru delete mac; 239114402Sru } 240114402Sru else 241114402Sru request_dictionary.define(nm, mac); 242114402Sru mac = 0; 243114402Sru dl_reg_contents = max_width.to_units(); 244114402Sru dn_reg_contents = vertical_position.to_units(); 245114402Sru} 246114402Sru 247114402Sruvunits macro_diversion::distance_to_next_trap() 248114402Sru{ 249114402Sru if (!diversion_trap.is_null() && diversion_trap_pos > vertical_position) 250114402Sru return diversion_trap_pos - vertical_position; 251114402Sru else 252114402Sru // Substract vresolution so that vunits::vunits does not overflow. 253114402Sru return vunits(INT_MAX - vresolution); 254114402Sru} 255114402Sru 256114402Sruvoid macro_diversion::transparent_output(unsigned char c) 257114402Sru{ 258114402Sru mac->append(c); 259114402Sru} 260114402Sru 261114402Sruvoid macro_diversion::transparent_output(node *n) 262114402Sru{ 263114402Sru mac->append(n); 264114402Sru} 265114402Sru 266114402Sruvoid macro_diversion::output(node *nd, int retain_size, 267114402Sru vunits vs, vunits post_vs, hunits width) 268114402Sru{ 269114402Sru no_space_mode = 0; 270114402Sru vertical_size v(vs, post_vs); 271114402Sru while (nd != 0) { 272114402Sru nd->set_vertical_size(&v); 273114402Sru node *temp = nd; 274114402Sru nd = nd->next; 275151497Sru if (temp->interpret(mac)) 276114402Sru delete temp; 277114402Sru else { 278114402Sru#if 1 279114402Sru temp->freeze_space(); 280114402Sru#endif 281114402Sru mac->append(temp); 282114402Sru } 283114402Sru } 284114402Sru last_post_line_extra_space = v.post_extra.to_units(); 285114402Sru if (!retain_size) { 286114402Sru v.pre = vs; 287114402Sru v.post = post_vs; 288114402Sru } 289114402Sru if (width > max_width) 290114402Sru max_width = width; 291114402Sru vunits x = v.pre + v.pre_extra + v.post + v.post_extra; 292114402Sru if (vertical_position_traps_flag 293114402Sru && !diversion_trap.is_null() && diversion_trap_pos > vertical_position 294114402Sru && diversion_trap_pos <= vertical_position + x) { 295114402Sru vunits trunc = vertical_position + x - diversion_trap_pos; 296114402Sru if (trunc > v.post) 297114402Sru trunc = v.post; 298114402Sru v.post -= trunc; 299114402Sru x -= trunc; 300114402Sru truncated_space = trunc; 301114402Sru spring_trap(diversion_trap); 302114402Sru } 303114402Sru mac->append(new vertical_size_node(-v.pre)); 304114402Sru mac->append(new vertical_size_node(v.post)); 305114402Sru mac->append('\n'); 306114402Sru vertical_position += x; 307114402Sru if (vertical_position - v.post > high_water_mark) 308114402Sru high_water_mark = vertical_position - v.post; 309114402Sru} 310114402Sru 311114402Sruvoid macro_diversion::space(vunits n, int) 312114402Sru{ 313114402Sru if (vertical_position_traps_flag 314114402Sru && !diversion_trap.is_null() && diversion_trap_pos > vertical_position 315114402Sru && diversion_trap_pos <= vertical_position + n) { 316114402Sru truncated_space = vertical_position + n - diversion_trap_pos; 317114402Sru n = diversion_trap_pos - vertical_position; 318114402Sru spring_trap(diversion_trap); 319114402Sru } 320114402Sru else if (n + vertical_position < V0) 321114402Sru n = -vertical_position; 322114402Sru mac->append(new diverted_space_node(n)); 323114402Sru vertical_position += n; 324114402Sru} 325114402Sru 326114402Sruvoid macro_diversion::copy_file(const char *filename) 327114402Sru{ 328114402Sru mac->append(new diverted_copy_file_node(filename)); 329114402Sru} 330114402Sru 331114402Srutop_level_diversion::top_level_diversion() 332114402Sru: page_number(0), page_count(0), last_page_count(-1), 333114402Sru page_length(units_per_inch*11), 334114402Sru prev_page_offset(units_per_inch), page_offset(units_per_inch), 335114402Sru page_trap_list(0), have_next_page_number(0), 336114402Sru ejecting_page(0), before_first_page(1) 337114402Sru{ 338114402Sru} 339114402Sru 340114402Sru// find the next trap after pos 341114402Sru 342114402Srutrap *top_level_diversion::find_next_trap(vunits *next_trap_pos) 343114402Sru{ 344114402Sru trap *next_trap = 0; 345114402Sru for (trap *pt = page_trap_list; pt != 0; pt = pt->next) 346114402Sru if (!pt->nm.is_null()) { 347114402Sru if (pt->position >= V0) { 348114402Sru if (pt->position > vertical_position 349114402Sru && pt->position < page_length 350114402Sru && (next_trap == 0 || pt->position < *next_trap_pos)) { 351114402Sru next_trap = pt; 352114402Sru *next_trap_pos = pt->position; 353114402Sru } 354114402Sru } 355114402Sru else { 356114402Sru vunits pos = pt->position; 357114402Sru pos += page_length; 358114402Sru if (pos > 0 && pos > vertical_position && (next_trap == 0 || pos < *next_trap_pos)) { 359114402Sru next_trap = pt; 360114402Sru *next_trap_pos = pos; 361114402Sru } 362114402Sru } 363114402Sru } 364114402Sru return next_trap; 365114402Sru} 366114402Sru 367114402Sruvunits top_level_diversion::distance_to_next_trap() 368114402Sru{ 369114402Sru vunits d; 370114402Sru if (!find_next_trap(&d)) 371114402Sru return page_length - vertical_position; 372114402Sru else 373114402Sru return d - vertical_position; 374114402Sru} 375114402Sru 376114402Sruvoid top_level_diversion::output(node *nd, int retain_size, 377114402Sru vunits vs, vunits post_vs, hunits width) 378114402Sru{ 379114402Sru no_space_mode = 0; 380114402Sru vunits next_trap_pos; 381114402Sru trap *next_trap = find_next_trap(&next_trap_pos); 382114402Sru if (before_first_page && begin_page()) 383114402Sru fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request"); 384114402Sru vertical_size v(vs, post_vs); 385114402Sru for (node *tem = nd; tem != 0; tem = tem->next) 386114402Sru tem->set_vertical_size(&v); 387114402Sru last_post_line_extra_space = v.post_extra.to_units(); 388114402Sru if (!retain_size) { 389114402Sru v.pre = vs; 390114402Sru v.post = post_vs; 391114402Sru } 392114402Sru vertical_position += v.pre; 393114402Sru vertical_position += v.pre_extra; 394114402Sru the_output->print_line(page_offset, vertical_position, nd, 395114402Sru v.pre + v.pre_extra, v.post_extra, width); 396114402Sru vertical_position += v.post_extra; 397114402Sru if (vertical_position > high_water_mark) 398114402Sru high_water_mark = vertical_position; 399114402Sru if (vertical_position_traps_flag && vertical_position >= page_length) 400114402Sru begin_page(); 401114402Sru else if (vertical_position_traps_flag 402114402Sru && next_trap != 0 && vertical_position >= next_trap_pos) { 403114402Sru nl_reg_contents = vertical_position.to_units(); 404114402Sru truncated_space = v.post; 405114402Sru spring_trap(next_trap->nm); 406114402Sru } 407114402Sru else if (v.post > V0) { 408114402Sru vertical_position += v.post; 409114402Sru if (vertical_position_traps_flag 410114402Sru && next_trap != 0 && vertical_position >= next_trap_pos) { 411114402Sru truncated_space = vertical_position - next_trap_pos; 412114402Sru vertical_position = next_trap_pos; 413114402Sru nl_reg_contents = vertical_position.to_units(); 414114402Sru spring_trap(next_trap->nm); 415114402Sru } 416114402Sru else if (vertical_position_traps_flag && vertical_position >= page_length) 417114402Sru begin_page(); 418114402Sru else 419114402Sru nl_reg_contents = vertical_position.to_units(); 420114402Sru } 421114402Sru else 422114402Sru nl_reg_contents = vertical_position.to_units(); 423114402Sru} 424114402Sru 425114402Sruvoid top_level_diversion::transparent_output(unsigned char c) 426114402Sru{ 427114402Sru if (before_first_page && begin_page()) 428114402Sru // This can only happen with the .output request. 429114402Sru fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request"); 430114402Sru const char *s = asciify(c); 431114402Sru while (*s) 432114402Sru the_output->transparent_char(*s++); 433114402Sru} 434114402Sru 435114402Sruvoid top_level_diversion::transparent_output(node * /*n*/) 436114402Sru{ 437114402Sru error("can't transparently output node at top level"); 438114402Sru} 439114402Sru 440114402Sruvoid top_level_diversion::copy_file(const char *filename) 441114402Sru{ 442114402Sru if (before_first_page && begin_page()) 443114402Sru fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request"); 444114402Sru the_output->copy_file(page_offset, vertical_position, filename); 445114402Sru} 446114402Sru 447114402Sruvoid top_level_diversion::space(vunits n, int forced) 448114402Sru{ 449114402Sru if (no_space_mode) { 450114402Sru if (!forced) 451114402Sru return; 452114402Sru else 453114402Sru no_space_mode = 0; 454114402Sru } 455114402Sru if (before_first_page) { 456114402Sru begin_page(n); 457114402Sru return; 458114402Sru } 459114402Sru vunits next_trap_pos; 460114402Sru trap *next_trap = find_next_trap(&next_trap_pos); 461114402Sru vunits y = vertical_position + n; 462151497Sru if (curenv->get_vertical_spacing().to_units()) 463151497Sru curenv->seen_space += n.to_units() 464151497Sru / curenv->get_vertical_spacing().to_units(); 465114402Sru if (vertical_position_traps_flag && next_trap != 0 && y >= next_trap_pos) { 466114402Sru vertical_position = next_trap_pos; 467114402Sru nl_reg_contents = vertical_position.to_units(); 468114402Sru truncated_space = y - vertical_position; 469114402Sru spring_trap(next_trap->nm); 470114402Sru } 471114402Sru else if (y < V0) { 472114402Sru vertical_position = V0; 473114402Sru nl_reg_contents = vertical_position.to_units(); 474114402Sru } 475114402Sru else if (vertical_position_traps_flag && y >= page_length && n >= V0) 476114402Sru begin_page(y - page_length); 477114402Sru else { 478114402Sru vertical_position = y; 479114402Sru nl_reg_contents = vertical_position.to_units(); 480114402Sru } 481114402Sru} 482114402Sru 483114402Srutrap::trap(symbol s, vunits n, trap *p) 484114402Sru: next(p), position(n), nm(s) 485114402Sru{ 486114402Sru} 487114402Sru 488151497Sruvoid top_level_diversion::add_trap(symbol nam, vunits pos) 489114402Sru{ 490114402Sru trap *first_free_slot = 0; 491114402Sru trap **p; 492114402Sru for (p = &page_trap_list; *p; p = &(*p)->next) { 493114402Sru if ((*p)->nm.is_null()) { 494114402Sru if (first_free_slot == 0) 495114402Sru first_free_slot = *p; 496114402Sru } 497114402Sru else if ((*p)->position == pos) { 498151497Sru (*p)->nm = nam; 499114402Sru return; 500114402Sru } 501114402Sru } 502114402Sru if (first_free_slot) { 503151497Sru first_free_slot->nm = nam; 504114402Sru first_free_slot->position = pos; 505114402Sru } 506114402Sru else 507151497Sru *p = new trap(nam, pos, 0); 508114402Sru} 509114402Sru 510151497Sruvoid top_level_diversion::remove_trap(symbol nam) 511114402Sru{ 512114402Sru for (trap *p = page_trap_list; p; p = p->next) 513151497Sru if (p->nm == nam) { 514114402Sru p->nm = NULL_SYMBOL; 515114402Sru return; 516114402Sru } 517114402Sru} 518114402Sru 519114402Sruvoid top_level_diversion::remove_trap_at(vunits pos) 520114402Sru{ 521114402Sru for (trap *p = page_trap_list; p; p = p->next) 522114402Sru if (p->position == pos) { 523114402Sru p->nm = NULL_SYMBOL; 524114402Sru return; 525114402Sru } 526114402Sru} 527114402Sru 528151497Sruvoid top_level_diversion::change_trap(symbol nam, vunits pos) 529114402Sru{ 530114402Sru for (trap *p = page_trap_list; p; p = p->next) 531151497Sru if (p->nm == nam) { 532114402Sru p->position = pos; 533114402Sru return; 534114402Sru } 535114402Sru} 536114402Sru 537114402Sruvoid top_level_diversion::print_traps() 538114402Sru{ 539114402Sru for (trap *p = page_trap_list; p; p = p->next) 540114402Sru if (p->nm.is_null()) 541114402Sru fprintf(stderr, " empty\n"); 542114402Sru else 543114402Sru fprintf(stderr, "%s\t%d\n", p->nm.contents(), p->position.to_units()); 544114402Sru fflush(stderr); 545114402Sru} 546114402Sru 547114402Sruvoid end_diversions() 548114402Sru{ 549114402Sru while (curdiv != topdiv) { 550114402Sru error("automatically ending diversion `%1' on exit", 551114402Sru curdiv->nm.contents()); 552114402Sru diversion *tem = curdiv; 553114402Sru curdiv = curdiv->prev; 554114402Sru delete tem; 555114402Sru } 556114402Sru} 557114402Sru 558114402Sruvoid cleanup_and_exit(int exit_code) 559114402Sru{ 560114402Sru if (the_output) { 561114402Sru the_output->trailer(topdiv->get_page_length()); 562114402Sru delete the_output; 563114402Sru } 564151497Sru FLUSH_INPUT_PIPE(STDIN_FILENO); 565114402Sru exit(exit_code); 566114402Sru} 567114402Sru 568114402Sru// Returns non-zero if it sprung a top-of-page trap. 569114402Sru// The optional parameter is for the .trunc register. 570114402Sruint top_level_diversion::begin_page(vunits n) 571114402Sru{ 572114402Sru if (exit_started) { 573114402Sru if (page_count == last_page_count 574114402Sru ? curenv->is_empty() 575114402Sru : (done_end_macro && (seen_last_page_ejector || began_page_in_end_macro))) 576114402Sru cleanup_and_exit(0); 577114402Sru if (!done_end_macro) 578114402Sru began_page_in_end_macro = 1; 579114402Sru } 580114402Sru if (last_page_number > 0 && page_number == last_page_number) 581114402Sru cleanup_and_exit(0); 582114402Sru if (!the_output) 583114402Sru init_output(); 584114402Sru ++page_count; 585114402Sru if (have_next_page_number) { 586114402Sru page_number = next_page_number; 587114402Sru have_next_page_number = 0; 588114402Sru } 589114402Sru else if (before_first_page == 1) 590114402Sru page_number = 1; 591114402Sru else 592114402Sru page_number++; 593114402Sru // spring the top of page trap if there is one 594114402Sru vunits next_trap_pos; 595114402Sru vertical_position = -vresolution; 596114402Sru trap *next_trap = find_next_trap(&next_trap_pos); 597114402Sru vertical_position = V0; 598114402Sru high_water_mark = V0; 599114402Sru ejecting_page = 0; 600114402Sru // If before_first_page was 2, then the top of page transition was undone 601114402Sru // using eg .nr nl 0-1. See nl_reg::set_value. 602114402Sru if (before_first_page != 2) 603114402Sru the_output->begin_page(page_number, page_length); 604114402Sru before_first_page = 0; 605114402Sru nl_reg_contents = vertical_position.to_units(); 606114402Sru if (vertical_position_traps_flag && next_trap != 0 && next_trap_pos == V0) { 607114402Sru truncated_space = n; 608114402Sru spring_trap(next_trap->nm); 609114402Sru return 1; 610114402Sru } 611114402Sru else 612114402Sru return 0; 613114402Sru} 614114402Sru 615114402Sruvoid continue_page_eject() 616114402Sru{ 617114402Sru if (topdiv->get_ejecting()) { 618114402Sru if (curdiv != topdiv) 619114402Sru error("can't continue page ejection because of current diversion"); 620114402Sru else if (!vertical_position_traps_flag) 621114402Sru error("can't continue page ejection because vertical position traps disabled"); 622114402Sru else { 623114402Sru push_page_ejector(); 624114402Sru topdiv->space(topdiv->get_page_length(), 1); 625114402Sru } 626114402Sru } 627114402Sru} 628114402Sru 629114402Sruvoid top_level_diversion::set_next_page_number(int n) 630114402Sru{ 631114402Sru next_page_number= n; 632114402Sru have_next_page_number = 1; 633114402Sru} 634114402Sru 635114402Sruint top_level_diversion::get_next_page_number() 636114402Sru{ 637114402Sru return have_next_page_number ? next_page_number : page_number + 1; 638114402Sru} 639114402Sru 640114402Sruvoid top_level_diversion::set_page_length(vunits n) 641114402Sru{ 642114402Sru page_length = n; 643114402Sru} 644114402Sru 645114402Srudiversion::~diversion() 646114402Sru{ 647114402Sru} 648114402Sru 649114402Sruvoid page_offset() 650114402Sru{ 651114402Sru hunits n; 652114402Sru // The troff manual says that the default scaling indicator is v, 653114402Sru // but it is in fact m: v wouldn't make sense for a horizontally 654114402Sru // oriented request. 655114402Sru if (!has_arg() || !get_hunits(&n, 'm', topdiv->page_offset)) 656114402Sru n = topdiv->prev_page_offset; 657114402Sru topdiv->prev_page_offset = topdiv->page_offset; 658114402Sru topdiv->page_offset = n; 659151497Sru topdiv->modified_tag.incl(MTSM_PO); 660114402Sru skip_line(); 661114402Sru} 662114402Sru 663114402Sruvoid page_length() 664114402Sru{ 665114402Sru vunits n; 666114402Sru if (has_arg() && get_vunits(&n, 'v', topdiv->get_page_length())) 667114402Sru topdiv->set_page_length(n); 668114402Sru else 669114402Sru topdiv->set_page_length(11*units_per_inch); 670114402Sru skip_line(); 671114402Sru} 672114402Sru 673114402Sruvoid when_request() 674114402Sru{ 675114402Sru vunits n; 676114402Sru if (get_vunits(&n, 'v')) { 677114402Sru symbol s = get_name(); 678114402Sru if (s.is_null()) 679114402Sru topdiv->remove_trap_at(n); 680114402Sru else 681114402Sru topdiv->add_trap(s, n); 682114402Sru } 683114402Sru skip_line(); 684114402Sru} 685114402Sru 686114402Sruvoid begin_page() 687114402Sru{ 688114402Sru int got_arg = 0; 689151497Sru int n = 0; /* pacify compiler */ 690114402Sru if (has_arg() && get_integer(&n, topdiv->get_page_number())) 691114402Sru got_arg = 1; 692114402Sru while (!tok.newline() && !tok.eof()) 693114402Sru tok.next(); 694114402Sru if (curdiv == topdiv) { 695114402Sru if (topdiv->before_first_page) { 696114402Sru if (!break_flag) { 697114402Sru if (got_arg) 698114402Sru topdiv->set_next_page_number(n); 699114402Sru if (got_arg || !topdiv->no_space_mode) 700114402Sru topdiv->begin_page(); 701114402Sru } 702114402Sru else if (topdiv->no_space_mode && !got_arg) 703114402Sru topdiv->begin_page(); 704114402Sru else { 705114402Sru /* Given this 706114402Sru 707114402Sru .wh 0 x 708114402Sru .de x 709114402Sru .tm \\n% 710114402Sru .. 711114402Sru .bp 3 712114402Sru 713114402Sru troff prints 714114402Sru 715114402Sru 1 716114402Sru 3 717114402Sru 718114402Sru This code makes groff do the same. */ 719114402Sru 720114402Sru push_page_ejector(); 721114402Sru topdiv->begin_page(); 722114402Sru if (got_arg) 723114402Sru topdiv->set_next_page_number(n); 724114402Sru topdiv->set_ejecting(); 725114402Sru } 726114402Sru } 727114402Sru else { 728114402Sru push_page_ejector(); 729114402Sru if (break_flag) 730114402Sru curenv->do_break(); 731114402Sru if (got_arg) 732114402Sru topdiv->set_next_page_number(n); 733114402Sru if (!(topdiv->no_space_mode && !got_arg)) 734114402Sru topdiv->set_ejecting(); 735114402Sru } 736114402Sru } 737114402Sru tok.next(); 738114402Sru} 739114402Sru 740114402Sruvoid no_space() 741114402Sru{ 742114402Sru curdiv->no_space_mode = 1; 743114402Sru skip_line(); 744114402Sru} 745114402Sru 746114402Sruvoid restore_spacing() 747114402Sru{ 748114402Sru curdiv->no_space_mode = 0; 749114402Sru skip_line(); 750114402Sru} 751114402Sru 752151497Sru/* It is necessary to generate a break before reading the argument, 753151497Srubecause otherwise arguments using | will be wrong. But if we just 754114402Srugenerate a break as usual, then the line forced out may spring a trap 755114402Sruand thus push a macro onto the input stack before we have had a chance 756151497Sruto read the argument to the sp request. We resolve this dilemma by 757114402Srusetting, before generating the break, a flag which will postpone the 758114402Sruactual pushing of the macro associated with the trap sprung by the 759114402Sruoutputting of the line forced out by the break till after we have read 760114402Sruthe argument to the request. If the break did cause a trap to be 761114402Srusprung, then we don't actually do the space. */ 762114402Sru 763114402Sruvoid space_request() 764114402Sru{ 765114402Sru postpone_traps(); 766114402Sru if (break_flag) 767114402Sru curenv->do_break(); 768114402Sru vunits n; 769114402Sru if (!has_arg() || !get_vunits(&n, 'v')) 770114402Sru n = curenv->get_vertical_spacing(); 771114402Sru while (!tok.newline() && !tok.eof()) 772114402Sru tok.next(); 773114402Sru if (!unpostpone_traps() && !curdiv->no_space_mode) 774114402Sru curdiv->space(n); 775114402Sru else 776114402Sru // The line might have had line spacing that was truncated. 777114402Sru truncated_space += n; 778151497Sru 779114402Sru tok.next(); 780114402Sru} 781114402Sru 782114402Sruvoid blank_line() 783114402Sru{ 784114402Sru curenv->do_break(); 785151497Sru if (!trap_sprung_flag && !curdiv->no_space_mode) 786114402Sru curdiv->space(curenv->get_vertical_spacing()); 787114402Sru else 788114402Sru truncated_space += curenv->get_vertical_spacing(); 789114402Sru} 790114402Sru 791114402Sru/* need_space might spring a trap and so we must be careful that the 792114402SruBEGIN_TRAP token is not skipped over. */ 793114402Sru 794114402Sruvoid need_space() 795114402Sru{ 796114402Sru vunits n; 797114402Sru if (!has_arg() || !get_vunits(&n, 'v')) 798114402Sru n = curenv->get_vertical_spacing(); 799114402Sru while (!tok.newline() && !tok.eof()) 800114402Sru tok.next(); 801114402Sru curdiv->need(n); 802114402Sru tok.next(); 803114402Sru} 804114402Sru 805114402Sruvoid page_number() 806114402Sru{ 807114402Sru int n; 808114402Sru 809114402Sru // the ps4html register is set if we are using -Tps 810114402Sru // to generate images for html 811114402Sru reg *r = (reg *)number_reg_dictionary.lookup("ps4html"); 812114402Sru if (r == NULL) 813114402Sru if (has_arg() && get_integer(&n, topdiv->get_page_number())) 814114402Sru topdiv->set_next_page_number(n); 815114402Sru skip_line(); 816114402Sru} 817114402Sru 818114402Sruvunits saved_space; 819114402Sru 820114402Sruvoid save_vertical_space() 821114402Sru{ 822114402Sru vunits x; 823114402Sru if (!has_arg() || !get_vunits(&x, 'v')) 824114402Sru x = curenv->get_vertical_spacing(); 825114402Sru if (curdiv->distance_to_next_trap() > x) 826114402Sru curdiv->space(x, 1); 827114402Sru else 828114402Sru saved_space = x; 829114402Sru skip_line(); 830114402Sru} 831114402Sru 832114402Sruvoid output_saved_vertical_space() 833114402Sru{ 834114402Sru while (!tok.newline() && !tok.eof()) 835114402Sru tok.next(); 836114402Sru if (saved_space > V0) 837114402Sru curdiv->space(saved_space, 1); 838114402Sru saved_space = V0; 839114402Sru tok.next(); 840114402Sru} 841114402Sru 842114402Sruvoid flush_output() 843114402Sru{ 844114402Sru while (!tok.newline() && !tok.eof()) 845114402Sru tok.next(); 846114402Sru if (break_flag) 847114402Sru curenv->do_break(); 848114402Sru if (the_output) 849114402Sru the_output->flush(); 850114402Sru tok.next(); 851114402Sru} 852114402Sru 853114402Sruvoid macro_diversion::set_diversion_trap(symbol s, vunits n) 854114402Sru{ 855114402Sru diversion_trap = s; 856114402Sru diversion_trap_pos = n; 857114402Sru} 858114402Sru 859114402Sruvoid macro_diversion::clear_diversion_trap() 860114402Sru{ 861114402Sru diversion_trap = NULL_SYMBOL; 862114402Sru} 863114402Sru 864114402Sruvoid top_level_diversion::set_diversion_trap(symbol, vunits) 865114402Sru{ 866114402Sru error("can't set diversion trap when no current diversion"); 867114402Sru} 868114402Sru 869114402Sruvoid top_level_diversion::clear_diversion_trap() 870114402Sru{ 871114402Sru error("can't set diversion trap when no current diversion"); 872114402Sru} 873114402Sru 874114402Sruvoid diversion_trap() 875114402Sru{ 876114402Sru vunits n; 877114402Sru if (has_arg() && get_vunits(&n, 'v')) { 878114402Sru symbol s = get_name(); 879114402Sru if (!s.is_null()) 880114402Sru curdiv->set_diversion_trap(s, n); 881114402Sru else 882114402Sru curdiv->clear_diversion_trap(); 883114402Sru } 884114402Sru else 885114402Sru curdiv->clear_diversion_trap(); 886114402Sru skip_line(); 887114402Sru} 888114402Sru 889114402Sruvoid change_trap() 890114402Sru{ 891114402Sru symbol s = get_name(1); 892114402Sru if (!s.is_null()) { 893114402Sru vunits x; 894114402Sru if (has_arg() && get_vunits(&x, 'v')) 895114402Sru topdiv->change_trap(s, x); 896114402Sru else 897114402Sru topdiv->remove_trap(s); 898114402Sru } 899114402Sru skip_line(); 900114402Sru} 901114402Sru 902114402Sruvoid print_traps() 903114402Sru{ 904114402Sru topdiv->print_traps(); 905114402Sru skip_line(); 906114402Sru} 907114402Sru 908114402Sruvoid mark() 909114402Sru{ 910114402Sru symbol s = get_name(); 911114402Sru if (s.is_null()) 912114402Sru curdiv->marked_place = curdiv->get_vertical_position(); 913114402Sru else if (curdiv == topdiv) 914114402Sru set_number_reg(s, nl_reg_contents); 915114402Sru else 916114402Sru set_number_reg(s, curdiv->get_vertical_position().to_units()); 917114402Sru skip_line(); 918114402Sru} 919114402Sru 920114402Sru// This is truly bizarre. It is documented in the SQ manual. 921114402Sru 922114402Sruvoid return_request() 923114402Sru{ 924114402Sru vunits dist = curdiv->marked_place - curdiv->get_vertical_position(); 925114402Sru if (has_arg()) { 926114402Sru if (tok.ch() == '-') { 927114402Sru tok.next(); 928114402Sru vunits x; 929114402Sru if (get_vunits(&x, 'v')) 930114402Sru dist = -x; 931114402Sru } 932114402Sru else { 933114402Sru vunits x; 934114402Sru if (get_vunits(&x, 'v')) 935114402Sru dist = x >= V0 ? x - curdiv->get_vertical_position() : V0; 936114402Sru } 937114402Sru } 938114402Sru if (dist < V0) 939114402Sru curdiv->space(dist); 940114402Sru skip_line(); 941114402Sru} 942114402Sru 943114402Sruvoid vertical_position_traps() 944114402Sru{ 945114402Sru int n; 946114402Sru if (has_arg() && get_integer(&n)) 947114402Sru vertical_position_traps_flag = (n != 0); 948114402Sru else 949114402Sru vertical_position_traps_flag = 1; 950114402Sru skip_line(); 951114402Sru} 952114402Sru 953114402Sruclass page_offset_reg : public reg { 954114402Srupublic: 955114402Sru int get_value(units *); 956114402Sru const char *get_string(); 957114402Sru}; 958114402Sru 959114402Sruint page_offset_reg::get_value(units *res) 960114402Sru{ 961114402Sru *res = topdiv->get_page_offset().to_units(); 962114402Sru return 1; 963114402Sru} 964114402Sru 965114402Sruconst char *page_offset_reg::get_string() 966114402Sru{ 967114402Sru return i_to_a(topdiv->get_page_offset().to_units()); 968114402Sru} 969114402Sru 970114402Sruclass page_length_reg : public reg { 971114402Srupublic: 972114402Sru int get_value(units *); 973114402Sru const char *get_string(); 974114402Sru}; 975114402Sru 976114402Sruint page_length_reg::get_value(units *res) 977114402Sru{ 978114402Sru *res = topdiv->get_page_length().to_units(); 979114402Sru return 1; 980114402Sru} 981114402Sru 982114402Sruconst char *page_length_reg::get_string() 983114402Sru{ 984114402Sru return i_to_a(topdiv->get_page_length().to_units()); 985114402Sru} 986114402Sru 987114402Sruclass vertical_position_reg : public reg { 988114402Srupublic: 989114402Sru int get_value(units *); 990114402Sru const char *get_string(); 991114402Sru}; 992114402Sru 993114402Sruint vertical_position_reg::get_value(units *res) 994114402Sru{ 995114402Sru if (curdiv == topdiv && topdiv->before_first_page) 996114402Sru *res = -1; 997114402Sru else 998114402Sru *res = curdiv->get_vertical_position().to_units(); 999114402Sru return 1; 1000114402Sru} 1001114402Sru 1002114402Sruconst char *vertical_position_reg::get_string() 1003114402Sru{ 1004114402Sru if (curdiv == topdiv && topdiv->before_first_page) 1005114402Sru return "-1"; 1006114402Sru else 1007114402Sru return i_to_a(curdiv->get_vertical_position().to_units()); 1008114402Sru} 1009114402Sru 1010114402Sruclass high_water_mark_reg : public reg { 1011114402Srupublic: 1012114402Sru int get_value(units *); 1013114402Sru const char *get_string(); 1014114402Sru}; 1015114402Sru 1016114402Sruint high_water_mark_reg::get_value(units *res) 1017114402Sru{ 1018114402Sru *res = curdiv->get_high_water_mark().to_units(); 1019114402Sru return 1; 1020114402Sru} 1021114402Sru 1022114402Sruconst char *high_water_mark_reg::get_string() 1023114402Sru{ 1024114402Sru return i_to_a(curdiv->get_high_water_mark().to_units()); 1025114402Sru} 1026114402Sru 1027114402Sruclass distance_to_next_trap_reg : public reg { 1028114402Srupublic: 1029114402Sru int get_value(units *); 1030114402Sru const char *get_string(); 1031114402Sru}; 1032114402Sru 1033114402Sruint distance_to_next_trap_reg::get_value(units *res) 1034114402Sru{ 1035114402Sru *res = curdiv->distance_to_next_trap().to_units(); 1036114402Sru return 1; 1037114402Sru} 1038114402Sru 1039114402Sruconst char *distance_to_next_trap_reg::get_string() 1040114402Sru{ 1041114402Sru return i_to_a(curdiv->distance_to_next_trap().to_units()); 1042114402Sru} 1043114402Sru 1044114402Sruclass diversion_name_reg : public reg { 1045114402Srupublic: 1046114402Sru const char *get_string(); 1047114402Sru}; 1048114402Sru 1049114402Sruconst char *diversion_name_reg::get_string() 1050114402Sru{ 1051114402Sru return curdiv->get_diversion_name(); 1052114402Sru} 1053114402Sru 1054114402Sruclass page_number_reg : public general_reg { 1055114402Srupublic: 1056114402Sru page_number_reg(); 1057114402Sru int get_value(units *); 1058114402Sru void set_value(units); 1059114402Sru}; 1060114402Sru 1061114402Srupage_number_reg::page_number_reg() 1062114402Sru{ 1063114402Sru} 1064114402Sru 1065114402Sruvoid page_number_reg::set_value(units n) 1066114402Sru{ 1067114402Sru topdiv->set_page_number(n); 1068114402Sru} 1069114402Sru 1070114402Sruint page_number_reg::get_value(units *res) 1071114402Sru{ 1072114402Sru *res = topdiv->get_page_number(); 1073114402Sru return 1; 1074114402Sru} 1075114402Sru 1076114402Sruclass next_page_number_reg : public reg { 1077114402Srupublic: 1078114402Sru const char *get_string(); 1079114402Sru}; 1080114402Sru 1081114402Sruconst char *next_page_number_reg::get_string() 1082114402Sru{ 1083114402Sru return i_to_a(topdiv->get_next_page_number()); 1084114402Sru} 1085114402Sru 1086114402Sruclass page_ejecting_reg : public reg { 1087114402Srupublic: 1088114402Sru const char *get_string(); 1089114402Sru}; 1090114402Sru 1091114402Sruconst char *page_ejecting_reg::get_string() 1092114402Sru{ 1093114402Sru return i_to_a(topdiv->get_ejecting()); 1094114402Sru} 1095114402Sru 1096114402Sruclass constant_vunits_reg : public reg { 1097114402Sru vunits *p; 1098114402Srupublic: 1099114402Sru constant_vunits_reg(vunits *); 1100114402Sru const char *get_string(); 1101114402Sru}; 1102114402Sru 1103114402Sruconstant_vunits_reg::constant_vunits_reg(vunits *q) : p(q) 1104114402Sru{ 1105114402Sru} 1106114402Sru 1107114402Sruconst char *constant_vunits_reg::get_string() 1108114402Sru{ 1109114402Sru return i_to_a(p->to_units()); 1110114402Sru} 1111114402Sru 1112114402Sruclass nl_reg : public variable_reg { 1113114402Srupublic: 1114114402Sru nl_reg(); 1115114402Sru void set_value(units); 1116114402Sru}; 1117114402Sru 1118114402Srunl_reg::nl_reg() : variable_reg(&nl_reg_contents) 1119114402Sru{ 1120114402Sru} 1121114402Sru 1122114402Sruvoid nl_reg::set_value(units n) 1123114402Sru{ 1124114402Sru variable_reg::set_value(n); 1125114402Sru // Setting nl to a negative value when the vertical position in 1126114402Sru // the top-level diversion is 0 undoes the top of page transition, 1127114402Sru // so that the header macro will be called as if the top of page 1128114402Sru // transition hasn't happened. This is used by Larry Wall's 1129114402Sru // wrapman program. Setting before_first_page to 2 rather than 1, 1130114402Sru // tells top_level_diversion::begin_page not to call 1131114402Sru // output_file::begin_page again. 1132114402Sru if (n < 0 && topdiv->get_vertical_position() == V0) 1133114402Sru topdiv->before_first_page = 2; 1134114402Sru} 1135114402Sru 1136114402Sruclass no_space_mode_reg : public reg { 1137114402Srupublic: 1138114402Sru int get_value(units *); 1139114402Sru const char *get_string(); 1140114402Sru}; 1141114402Sru 1142114402Sruint no_space_mode_reg::get_value(units *val) 1143114402Sru{ 1144114402Sru *val = curdiv->no_space_mode; 1145114402Sru return 1; 1146114402Sru} 1147114402Sru 1148114402Sruconst char *no_space_mode_reg::get_string() 1149114402Sru{ 1150114402Sru return curdiv->no_space_mode ? "1" : "0"; 1151114402Sru} 1152114402Sru 1153114402Sruvoid init_div_requests() 1154114402Sru{ 1155114402Sru init_request("box", box); 1156114402Sru init_request("boxa", box_append); 1157114402Sru init_request("bp", begin_page); 1158114402Sru init_request("ch", change_trap); 1159114402Sru init_request("da", divert_append); 1160114402Sru init_request("di", divert); 1161114402Sru init_request("dt", diversion_trap); 1162114402Sru init_request("fl", flush_output); 1163114402Sru init_request("mk", mark); 1164114402Sru init_request("ne", need_space); 1165114402Sru init_request("ns", no_space); 1166114402Sru init_request("os", output_saved_vertical_space); 1167114402Sru init_request("pl", page_length); 1168114402Sru init_request("pn", page_number); 1169114402Sru init_request("po", page_offset); 1170114402Sru init_request("ptr", print_traps); 1171114402Sru init_request("rs", restore_spacing); 1172114402Sru init_request("rt", return_request); 1173114402Sru init_request("sp", space_request); 1174114402Sru init_request("sv", save_vertical_space); 1175114402Sru init_request("vpt", vertical_position_traps); 1176114402Sru init_request("wh", when_request); 1177114402Sru number_reg_dictionary.define(".a", 1178114402Sru new constant_int_reg(&last_post_line_extra_space)); 1179114402Sru number_reg_dictionary.define(".d", new vertical_position_reg); 1180114402Sru number_reg_dictionary.define(".h", new high_water_mark_reg); 1181114402Sru number_reg_dictionary.define(".ne", 1182114402Sru new constant_vunits_reg(&needed_space)); 1183114402Sru number_reg_dictionary.define(".ns", new no_space_mode_reg); 1184114402Sru number_reg_dictionary.define(".o", new page_offset_reg); 1185114402Sru number_reg_dictionary.define(".p", new page_length_reg); 1186114402Sru number_reg_dictionary.define(".pe", new page_ejecting_reg); 1187114402Sru number_reg_dictionary.define(".pn", new next_page_number_reg); 1188114402Sru number_reg_dictionary.define(".t", new distance_to_next_trap_reg); 1189114402Sru number_reg_dictionary.define(".trunc", 1190114402Sru new constant_vunits_reg(&truncated_space)); 1191114402Sru number_reg_dictionary.define(".vpt", 1192114402Sru new constant_int_reg(&vertical_position_traps_flag)); 1193114402Sru number_reg_dictionary.define(".z", new diversion_name_reg); 1194114402Sru number_reg_dictionary.define("dl", new variable_reg(&dl_reg_contents)); 1195114402Sru number_reg_dictionary.define("dn", new variable_reg(&dn_reg_contents)); 1196114402Sru number_reg_dictionary.define("nl", new nl_reg); 1197114402Sru number_reg_dictionary.define("%", new page_number_reg); 1198114402Sru} 1199