PageRenderTime 92ms CodeModel.GetById 2ms app.highlight 82ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/groff/src/roff/troff/div.cpp

https://bitbucket.org/freebsd/freebsd-head/
C++ | 1198 lines | 983 code | 116 blank | 99 comment | 229 complexity | eb69496b2bb3cfde7585d2f527d8e50c MD5 | raw file
   1// -*- C++ -*-
   2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2004
   3   Free Software Foundation, Inc.
   4     Written by James Clark (jjc@jclark.com)
   5
   6This file is part of groff.
   7
   8groff is free software; you can redistribute it and/or modify it under
   9the terms of the GNU General Public License as published by the Free
  10Software Foundation; either version 2, or (at your option) any later
  11version.
  12
  13groff is distributed in the hope that it will be useful, but WITHOUT ANY
  14WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16for more details.
  17
  18You should have received a copy of the GNU General Public License along
  19with groff; see the file COPYING.  If not, write to the Free Software
  20Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
  21
  22
  23// diversions
  24
  25#include "troff.h"
  26#include "dictionary.h"
  27#include "hvunits.h"
  28#include "stringclass.h"
  29#include "mtsm.h"
  30#include "env.h"
  31#include "request.h"
  32#include "node.h"
  33#include "token.h"
  34#include "div.h"
  35#include "reg.h"
  36
  37#include "nonposix.h"
  38
  39int exit_started = 0;		// the exit process has started
  40int done_end_macro = 0;		// the end macro (if any) has finished
  41int seen_last_page_ejector = 0;	// seen the LAST_PAGE_EJECTOR cookie
  42int last_page_number = 0;	// if > 0, the number of the last page
  43				// specified with -o
  44static int began_page_in_end_macro = 0;	// a new page was begun during the end macro
  45
  46static int last_post_line_extra_space = 0; // needed for \n(.a
  47static int nl_reg_contents = -1;
  48static int dl_reg_contents = 0;
  49static int dn_reg_contents = 0;
  50static int vertical_position_traps_flag = 1;
  51static vunits truncated_space;
  52static vunits needed_space;
  53
  54diversion::diversion(symbol s) 
  55: prev(0), nm(s), vertical_position(V0), high_water_mark(V0),
  56  any_chars_added(0), no_space_mode(0), needs_push(0), saved_seen_break(0),
  57  saved_seen_space(0), saved_seen_eol(0), saved_suppress_next_eol(0),
  58  marked_place(V0)
  59{
  60}
  61
  62struct vertical_size {
  63  vunits pre_extra, post_extra, pre, post;
  64  vertical_size(vunits vs, vunits post_vs);
  65};
  66
  67vertical_size::vertical_size(vunits vs, vunits post_vs)
  68: pre_extra(V0), post_extra(V0), pre(vs), post(post_vs)
  69{
  70}
  71
  72void node::set_vertical_size(vertical_size *)
  73{
  74}
  75
  76void extra_size_node::set_vertical_size(vertical_size *v)
  77{
  78  if (n < V0) {
  79    if (-n > v->pre_extra)
  80      v->pre_extra = -n;
  81  }
  82  else if (n > v->post_extra)
  83    v->post_extra = n;
  84}
  85
  86void vertical_size_node::set_vertical_size(vertical_size *v)
  87{
  88  if (n < V0)
  89    v->pre = -n;
  90  else
  91    v->post = n;
  92}
  93
  94top_level_diversion *topdiv;
  95
  96diversion *curdiv;
  97
  98void do_divert(int append, int boxing)
  99{
 100  tok.skip();
 101  symbol nm = get_name();
 102  if (nm.is_null()) {
 103    if (curdiv->prev) {
 104      curenv->seen_break = curdiv->saved_seen_break;
 105      curenv->seen_space = curdiv->saved_seen_space;
 106      curenv->seen_eol = curdiv->saved_seen_eol;
 107      curenv->suppress_next_eol = curdiv->saved_suppress_next_eol;
 108      if (boxing) {
 109	curenv->line = curdiv->saved_line;
 110	curenv->width_total = curdiv->saved_width_total;
 111	curenv->space_total = curdiv->saved_space_total;
 112	curenv->saved_indent = curdiv->saved_saved_indent;
 113	curenv->target_text_length = curdiv->saved_target_text_length;
 114	curenv->prev_line_interrupted = curdiv->saved_prev_line_interrupted;
 115      }
 116      diversion *temp = curdiv;
 117      curdiv = curdiv->prev;
 118      delete temp;
 119    }
 120    else
 121      warning(WARN_DI, "diversion stack underflow");
 122  }
 123  else {
 124    macro_diversion *md = new macro_diversion(nm, append);
 125    md->prev = curdiv;
 126    curdiv = md;
 127    curdiv->saved_seen_break = curenv->seen_break;
 128    curdiv->saved_seen_space = curenv->seen_space;
 129    curdiv->saved_seen_eol = curenv->seen_eol;
 130    curdiv->saved_suppress_next_eol = curenv->suppress_next_eol;
 131    curenv->seen_break = 0;
 132    curenv->seen_space = 0;
 133    curenv->seen_eol = 0;
 134    if (boxing) {
 135      curdiv->saved_line = curenv->line;
 136      curdiv->saved_width_total = curenv->width_total;
 137      curdiv->saved_space_total = curenv->space_total;
 138      curdiv->saved_saved_indent = curenv->saved_indent;
 139      curdiv->saved_target_text_length = curenv->target_text_length;
 140      curdiv->saved_prev_line_interrupted = curenv->prev_line_interrupted;
 141      curenv->line = 0;
 142      curenv->start_line();
 143    }
 144  }
 145  skip_line();
 146}
 147
 148void divert()
 149{
 150  do_divert(0, 0);
 151}
 152
 153void divert_append()
 154{
 155  do_divert(1, 0);
 156}
 157  
 158void box()
 159{
 160  do_divert(0, 1);
 161}
 162
 163void box_append()
 164{
 165  do_divert(1, 1);
 166}
 167
 168void diversion::need(vunits n)
 169{
 170  vunits d = distance_to_next_trap();
 171  if (d < n) {
 172    truncated_space = -d;
 173    needed_space = n;
 174    space(d, 1);
 175  }
 176}
 177
 178macro_diversion::macro_diversion(symbol s, int append)
 179: diversion(s), max_width(H0)
 180{
 181#if 0
 182  if (append) {
 183    /* We don't allow recursive appends eg:
 184
 185      .da a
 186      .a
 187      .di
 188      
 189      This causes an infinite loop in troff anyway.
 190      This is because the user could do
 191
 192      .as a foo
 193
 194      in the diversion, and this would mess things up royally,
 195      since there would be two things appending to the same
 196      macro_header.
 197      To make it work, we would have to copy the _contents_
 198      of the macro into which we were diverting; this doesn't
 199      strike me as worthwhile.
 200      However,
 201
 202      .di a
 203      .a
 204      .a
 205      .di
 206
 207       will work and will make `a' contain two copies of what it contained
 208       before; in troff, `a' would contain nothing. */
 209    request_or_macro *rm 
 210      = (request_or_macro *)request_dictionary.remove(s);
 211    if (!rm || (mac = rm->to_macro()) == 0)
 212      mac = new macro;
 213  }
 214  else
 215    mac = new macro;
 216#endif
 217  // We can now catch the situation described above by comparing
 218  // the length of the charlist in the macro_header with the length
 219  // stored in the macro. When we detect this, we copy the contents.
 220  mac = new macro(1);
 221  if (append) {
 222    request_or_macro *rm 
 223      = (request_or_macro *)request_dictionary.lookup(s);
 224    if (rm) {
 225      macro *m = rm->to_macro();
 226      if (m)
 227	*mac = *m;
 228    }
 229  }
 230}
 231
 232macro_diversion::~macro_diversion()
 233{
 234  request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
 235  macro *m = rm ? rm->to_macro() : 0;
 236  if (m) {
 237    *m = *mac;
 238    delete mac;
 239  }
 240  else
 241    request_dictionary.define(nm, mac);
 242  mac = 0;
 243  dl_reg_contents = max_width.to_units();
 244  dn_reg_contents = vertical_position.to_units();
 245}
 246
 247vunits macro_diversion::distance_to_next_trap()
 248{
 249  if (!diversion_trap.is_null() && diversion_trap_pos > vertical_position)
 250    return diversion_trap_pos - vertical_position;
 251  else
 252    // Substract vresolution so that vunits::vunits does not overflow.
 253    return vunits(INT_MAX - vresolution);
 254}
 255
 256void macro_diversion::transparent_output(unsigned char c)
 257{
 258  mac->append(c);
 259}
 260
 261void macro_diversion::transparent_output(node *n)
 262{
 263  mac->append(n);
 264}
 265
 266void macro_diversion::output(node *nd, int retain_size,
 267			     vunits vs, vunits post_vs, hunits width)
 268{
 269  no_space_mode = 0;
 270  vertical_size v(vs, post_vs);
 271  while (nd != 0) {
 272    nd->set_vertical_size(&v);
 273    node *temp = nd;
 274    nd = nd->next;
 275    if (temp->interpret(mac))
 276      delete temp;
 277    else {
 278#if 1
 279      temp->freeze_space();
 280#endif
 281      mac->append(temp);
 282    }
 283  }
 284  last_post_line_extra_space = v.post_extra.to_units();
 285  if (!retain_size) {
 286    v.pre = vs;
 287    v.post = post_vs;
 288  }
 289  if (width > max_width)
 290    max_width = width;
 291  vunits x = v.pre + v.pre_extra + v.post + v.post_extra;
 292  if (vertical_position_traps_flag
 293      && !diversion_trap.is_null() && diversion_trap_pos > vertical_position
 294      && diversion_trap_pos <= vertical_position + x) {
 295    vunits trunc = vertical_position + x - diversion_trap_pos;
 296    if (trunc > v.post)
 297      trunc = v.post;
 298    v.post -= trunc;
 299    x -= trunc;
 300    truncated_space = trunc;
 301    spring_trap(diversion_trap);
 302  }
 303  mac->append(new vertical_size_node(-v.pre));
 304  mac->append(new vertical_size_node(v.post));
 305  mac->append('\n');
 306  vertical_position += x;
 307  if (vertical_position - v.post > high_water_mark)
 308    high_water_mark = vertical_position - v.post;
 309}
 310
 311void macro_diversion::space(vunits n, int)
 312{
 313  if (vertical_position_traps_flag
 314      && !diversion_trap.is_null() && diversion_trap_pos > vertical_position
 315      && diversion_trap_pos <= vertical_position + n) {
 316    truncated_space = vertical_position + n - diversion_trap_pos;
 317    n = diversion_trap_pos - vertical_position;
 318    spring_trap(diversion_trap);
 319  }
 320  else if (n + vertical_position < V0)
 321    n = -vertical_position;
 322  mac->append(new diverted_space_node(n));
 323  vertical_position += n;
 324}
 325
 326void macro_diversion::copy_file(const char *filename)
 327{
 328  mac->append(new diverted_copy_file_node(filename));
 329}
 330
 331top_level_diversion::top_level_diversion()
 332: page_number(0), page_count(0), last_page_count(-1),
 333  page_length(units_per_inch*11),
 334  prev_page_offset(units_per_inch), page_offset(units_per_inch),
 335  page_trap_list(0), have_next_page_number(0),
 336  ejecting_page(0), before_first_page(1)
 337{
 338}
 339
 340// find the next trap after pos
 341
 342trap *top_level_diversion::find_next_trap(vunits *next_trap_pos)
 343{
 344  trap *next_trap = 0;
 345  for (trap *pt = page_trap_list; pt != 0; pt = pt->next)
 346    if (!pt->nm.is_null()) {
 347      if (pt->position >= V0) {
 348	if (pt->position > vertical_position 
 349	    && pt->position < page_length
 350	    && (next_trap == 0 || pt->position < *next_trap_pos)) {
 351	      next_trap = pt;
 352	      *next_trap_pos = pt->position;
 353	    }
 354      }
 355      else {
 356	vunits pos = pt->position;
 357	pos += page_length;
 358	if (pos > 0 && pos > vertical_position && (next_trap == 0 || pos < *next_trap_pos)) {
 359	  next_trap = pt;
 360	  *next_trap_pos = pos;
 361	}
 362      }
 363    }
 364  return next_trap;
 365}
 366
 367vunits top_level_diversion::distance_to_next_trap()
 368{
 369  vunits d;
 370  if (!find_next_trap(&d))
 371    return page_length - vertical_position;
 372  else
 373    return d - vertical_position;
 374}
 375
 376void top_level_diversion::output(node *nd, int retain_size,
 377				 vunits vs, vunits post_vs, hunits width)
 378{
 379  no_space_mode = 0;
 380  vunits next_trap_pos;
 381  trap *next_trap = find_next_trap(&next_trap_pos);
 382  if (before_first_page && begin_page()) 
 383    fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
 384  vertical_size v(vs, post_vs);
 385  for (node *tem = nd; tem != 0; tem = tem->next)
 386    tem->set_vertical_size(&v);
 387  last_post_line_extra_space = v.post_extra.to_units();
 388  if (!retain_size) {
 389    v.pre = vs;
 390    v.post = post_vs;
 391  }
 392  vertical_position += v.pre;
 393  vertical_position += v.pre_extra;
 394  the_output->print_line(page_offset, vertical_position, nd,
 395			 v.pre + v.pre_extra, v.post_extra, width);
 396  vertical_position += v.post_extra;
 397  if (vertical_position > high_water_mark)
 398    high_water_mark = vertical_position;
 399  if (vertical_position_traps_flag && vertical_position >= page_length)
 400    begin_page();
 401  else if (vertical_position_traps_flag
 402	   && next_trap != 0 && vertical_position >= next_trap_pos) {
 403    nl_reg_contents = vertical_position.to_units();
 404    truncated_space = v.post;
 405    spring_trap(next_trap->nm);
 406  }
 407  else if (v.post > V0) {
 408    vertical_position += v.post;
 409    if (vertical_position_traps_flag
 410	&& next_trap != 0 && vertical_position >= next_trap_pos) {
 411      truncated_space = vertical_position - next_trap_pos;
 412      vertical_position = next_trap_pos;
 413      nl_reg_contents = vertical_position.to_units();
 414      spring_trap(next_trap->nm);
 415    }
 416    else if (vertical_position_traps_flag && vertical_position >= page_length)
 417      begin_page();
 418    else
 419      nl_reg_contents = vertical_position.to_units();
 420  }
 421  else
 422    nl_reg_contents = vertical_position.to_units();
 423}
 424
 425void top_level_diversion::transparent_output(unsigned char c)
 426{
 427  if (before_first_page && begin_page())
 428    // This can only happen with the .output request.
 429    fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
 430  const char *s = asciify(c);
 431  while (*s)
 432    the_output->transparent_char(*s++);
 433}
 434
 435void top_level_diversion::transparent_output(node * /*n*/)
 436{
 437  error("can't transparently output node at top level");
 438}
 439
 440void top_level_diversion::copy_file(const char *filename)
 441{
 442  if (before_first_page && begin_page())
 443    fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
 444  the_output->copy_file(page_offset, vertical_position, filename);
 445}
 446
 447void top_level_diversion::space(vunits n, int forced)
 448{
 449  if (no_space_mode) {
 450    if (!forced)
 451      return;
 452    else
 453      no_space_mode = 0;
 454  }
 455  if (before_first_page) {
 456    begin_page(n);
 457    return;
 458  }
 459  vunits next_trap_pos;
 460  trap *next_trap = find_next_trap(&next_trap_pos);
 461  vunits y = vertical_position + n;
 462  if (curenv->get_vertical_spacing().to_units())
 463    curenv->seen_space += n.to_units()
 464			  / curenv->get_vertical_spacing().to_units();
 465  if (vertical_position_traps_flag && next_trap != 0 && y >= next_trap_pos) {
 466    vertical_position = next_trap_pos;
 467    nl_reg_contents = vertical_position.to_units();
 468    truncated_space = y - vertical_position;
 469    spring_trap(next_trap->nm);
 470  }
 471  else if (y < V0) {
 472    vertical_position = V0;
 473    nl_reg_contents = vertical_position.to_units();
 474  }
 475  else if (vertical_position_traps_flag && y >= page_length && n >= V0)
 476    begin_page(y - page_length);
 477  else {
 478    vertical_position = y;
 479    nl_reg_contents = vertical_position.to_units();
 480  }
 481}
 482
 483trap::trap(symbol s, vunits n, trap *p)
 484: next(p), position(n), nm(s)
 485{
 486}
 487
 488void top_level_diversion::add_trap(symbol nam, vunits pos)
 489{
 490  trap *first_free_slot = 0;
 491  trap **p;
 492  for (p = &page_trap_list; *p; p = &(*p)->next) {
 493    if ((*p)->nm.is_null()) {
 494      if (first_free_slot == 0)
 495	first_free_slot = *p;
 496    }
 497    else if ((*p)->position == pos) {
 498      (*p)->nm = nam;
 499      return;
 500    }
 501  }
 502  if (first_free_slot) {
 503    first_free_slot->nm = nam;
 504    first_free_slot->position = pos;
 505  }
 506  else
 507    *p = new trap(nam, pos, 0);
 508}  
 509
 510void top_level_diversion::remove_trap(symbol nam)
 511{
 512  for (trap *p = page_trap_list; p; p = p->next)
 513    if (p->nm == nam) {
 514      p->nm = NULL_SYMBOL;
 515      return;
 516    }
 517}
 518
 519void top_level_diversion::remove_trap_at(vunits pos)
 520{
 521  for (trap *p = page_trap_list; p; p = p->next)
 522    if (p->position == pos) {
 523      p->nm = NULL_SYMBOL;
 524      return;
 525    }
 526}
 527      
 528void top_level_diversion::change_trap(symbol nam, vunits pos)
 529{
 530  for (trap *p = page_trap_list; p; p = p->next)
 531    if (p->nm == nam) {
 532      p->position = pos;
 533      return;
 534    }
 535}
 536
 537void top_level_diversion::print_traps()
 538{
 539  for (trap *p = page_trap_list; p; p = p->next)
 540    if (p->nm.is_null())
 541      fprintf(stderr, "  empty\n");
 542    else
 543      fprintf(stderr, "%s\t%d\n", p->nm.contents(), p->position.to_units());
 544  fflush(stderr);
 545}
 546
 547void end_diversions()
 548{
 549  while (curdiv != topdiv) {
 550    error("automatically ending diversion `%1' on exit",
 551	    curdiv->nm.contents());
 552    diversion *tem = curdiv;
 553    curdiv = curdiv->prev;
 554    delete tem;
 555  }
 556}
 557
 558void cleanup_and_exit(int exit_code)
 559{
 560  if (the_output) {
 561    the_output->trailer(topdiv->get_page_length());
 562    delete the_output;
 563  }
 564  FLUSH_INPUT_PIPE(STDIN_FILENO);
 565  exit(exit_code);
 566}
 567
 568// Returns non-zero if it sprung a top-of-page trap.
 569// The optional parameter is for the .trunc register.
 570int top_level_diversion::begin_page(vunits n)
 571{
 572  if (exit_started) {
 573    if (page_count == last_page_count
 574	? curenv->is_empty()
 575	: (done_end_macro && (seen_last_page_ejector || began_page_in_end_macro)))
 576      cleanup_and_exit(0);
 577    if (!done_end_macro)
 578      began_page_in_end_macro = 1;
 579  }
 580  if (last_page_number > 0 && page_number == last_page_number)
 581    cleanup_and_exit(0);
 582  if (!the_output)
 583    init_output();
 584  ++page_count;
 585  if (have_next_page_number) {
 586    page_number = next_page_number;
 587    have_next_page_number = 0;
 588  }
 589  else if (before_first_page == 1)
 590    page_number = 1;
 591  else
 592    page_number++;
 593  // spring the top of page trap if there is one
 594  vunits next_trap_pos;
 595  vertical_position = -vresolution;
 596  trap *next_trap = find_next_trap(&next_trap_pos);
 597  vertical_position = V0;
 598  high_water_mark = V0;
 599  ejecting_page = 0;
 600  // If before_first_page was 2, then the top of page transition was undone
 601  // using eg .nr nl 0-1.  See nl_reg::set_value.
 602  if (before_first_page != 2)
 603    the_output->begin_page(page_number, page_length);
 604  before_first_page = 0;
 605  nl_reg_contents = vertical_position.to_units();
 606  if (vertical_position_traps_flag && next_trap != 0 && next_trap_pos == V0) {
 607    truncated_space = n;
 608    spring_trap(next_trap->nm);
 609    return 1;
 610  }
 611  else
 612    return 0;
 613}
 614
 615void continue_page_eject()
 616{
 617  if (topdiv->get_ejecting()) {
 618    if (curdiv != topdiv)
 619      error("can't continue page ejection because of current diversion");
 620    else if (!vertical_position_traps_flag)
 621      error("can't continue page ejection because vertical position traps disabled");
 622    else {
 623      push_page_ejector();
 624      topdiv->space(topdiv->get_page_length(), 1);
 625    }
 626  }
 627}
 628
 629void top_level_diversion::set_next_page_number(int n)
 630{
 631  next_page_number= n;
 632  have_next_page_number = 1;
 633}
 634
 635int top_level_diversion::get_next_page_number()
 636{
 637  return have_next_page_number ? next_page_number : page_number + 1;
 638}
 639
 640void top_level_diversion::set_page_length(vunits n)
 641{
 642  page_length = n;
 643}
 644
 645diversion::~diversion()
 646{
 647}
 648
 649void page_offset()
 650{
 651  hunits n;
 652  // The troff manual says that the default scaling indicator is v,
 653  // but it is in fact m: v wouldn't make sense for a horizontally
 654  // oriented request.
 655  if (!has_arg() || !get_hunits(&n, 'm', topdiv->page_offset))
 656    n = topdiv->prev_page_offset;
 657  topdiv->prev_page_offset = topdiv->page_offset;
 658  topdiv->page_offset = n;
 659  topdiv->modified_tag.incl(MTSM_PO);
 660  skip_line();
 661}
 662
 663void page_length()
 664{
 665  vunits n;
 666  if (has_arg() && get_vunits(&n, 'v', topdiv->get_page_length()))
 667    topdiv->set_page_length(n);
 668  else
 669    topdiv->set_page_length(11*units_per_inch);
 670  skip_line();
 671}
 672
 673void when_request()
 674{
 675  vunits n;
 676  if (get_vunits(&n, 'v')) {
 677    symbol s = get_name();
 678    if (s.is_null())
 679      topdiv->remove_trap_at(n);
 680    else
 681      topdiv->add_trap(s, n);
 682  }
 683  skip_line();
 684}
 685
 686void begin_page()
 687{
 688  int got_arg = 0;
 689  int n = 0;		/* pacify compiler */
 690  if (has_arg() && get_integer(&n, topdiv->get_page_number()))
 691    got_arg = 1;
 692  while (!tok.newline() && !tok.eof())
 693    tok.next();
 694  if (curdiv == topdiv) {
 695    if (topdiv->before_first_page) {
 696      if (!break_flag) {
 697	if (got_arg)
 698	  topdiv->set_next_page_number(n);
 699	if (got_arg || !topdiv->no_space_mode)
 700	  topdiv->begin_page();
 701      }
 702      else if (topdiv->no_space_mode && !got_arg)
 703	topdiv->begin_page();
 704      else {
 705	/* Given this
 706
 707         .wh 0 x
 708	 .de x
 709	 .tm \\n%
 710	 ..
 711	 .bp 3
 712
 713	 troff prints
 714
 715	 1
 716	 3
 717
 718	 This code makes groff do the same. */
 719
 720	push_page_ejector();
 721	topdiv->begin_page();
 722	if (got_arg)
 723	  topdiv->set_next_page_number(n);
 724	topdiv->set_ejecting();
 725      }
 726    }
 727    else {
 728      push_page_ejector();
 729      if (break_flag)
 730	curenv->do_break();
 731      if (got_arg)
 732	topdiv->set_next_page_number(n);
 733      if (!(topdiv->no_space_mode && !got_arg))
 734	topdiv->set_ejecting();
 735    }
 736  }
 737  tok.next();
 738}
 739
 740void no_space()
 741{
 742  curdiv->no_space_mode = 1;
 743  skip_line();
 744}
 745
 746void restore_spacing()
 747{
 748  curdiv->no_space_mode = 0;
 749  skip_line();
 750}
 751
 752/* It is necessary to generate a break before reading the argument,
 753because otherwise arguments using | will be wrong.  But if we just
 754generate a break as usual, then the line forced out may spring a trap
 755and thus push a macro onto the input stack before we have had a chance
 756to read the argument to the sp request.  We resolve this dilemma by
 757setting, before generating the break, a flag which will postpone the
 758actual pushing of the macro associated with the trap sprung by the
 759outputting of the line forced out by the break till after we have read
 760the argument to the request.  If the break did cause a trap to be
 761sprung, then we don't actually do the space. */
 762
 763void space_request()
 764{
 765  postpone_traps();
 766  if (break_flag)
 767    curenv->do_break();
 768  vunits n;
 769  if (!has_arg() || !get_vunits(&n, 'v'))
 770    n = curenv->get_vertical_spacing();
 771  while (!tok.newline() && !tok.eof())
 772    tok.next();
 773  if (!unpostpone_traps() && !curdiv->no_space_mode)
 774    curdiv->space(n);
 775  else
 776    // The line might have had line spacing that was truncated.
 777    truncated_space += n;
 778  
 779  tok.next();
 780}
 781
 782void blank_line()
 783{
 784  curenv->do_break();
 785  if (!trap_sprung_flag && !curdiv->no_space_mode)
 786    curdiv->space(curenv->get_vertical_spacing());
 787  else
 788    truncated_space += curenv->get_vertical_spacing();
 789}
 790
 791/* need_space might spring a trap and so we must be careful that the
 792BEGIN_TRAP token is not skipped over. */
 793
 794void need_space()
 795{
 796  vunits n;
 797  if (!has_arg() || !get_vunits(&n, 'v'))
 798    n = curenv->get_vertical_spacing();
 799  while (!tok.newline() && !tok.eof())
 800    tok.next();
 801  curdiv->need(n);
 802  tok.next();
 803}
 804
 805void page_number()
 806{
 807  int n;
 808
 809  // the ps4html register is set if we are using -Tps
 810  // to generate images for html
 811  reg *r = (reg *)number_reg_dictionary.lookup("ps4html");
 812  if (r == NULL)
 813    if (has_arg() && get_integer(&n, topdiv->get_page_number()))
 814      topdiv->set_next_page_number(n);
 815  skip_line();
 816}
 817
 818vunits saved_space;
 819
 820void save_vertical_space()
 821{
 822  vunits x;
 823  if (!has_arg() || !get_vunits(&x, 'v'))
 824    x = curenv->get_vertical_spacing();
 825  if (curdiv->distance_to_next_trap() > x)
 826    curdiv->space(x, 1);
 827  else
 828    saved_space = x;
 829  skip_line();
 830}
 831
 832void output_saved_vertical_space()
 833{
 834  while (!tok.newline() && !tok.eof())
 835    tok.next();
 836  if (saved_space > V0)
 837    curdiv->space(saved_space, 1);
 838  saved_space = V0;
 839  tok.next();
 840}
 841
 842void flush_output()
 843{
 844  while (!tok.newline() && !tok.eof())
 845    tok.next();
 846  if (break_flag)
 847    curenv->do_break();
 848  if (the_output)
 849    the_output->flush();
 850  tok.next();
 851}
 852
 853void macro_diversion::set_diversion_trap(symbol s, vunits n)
 854{
 855  diversion_trap = s;
 856  diversion_trap_pos = n;
 857}
 858
 859void macro_diversion::clear_diversion_trap()
 860{
 861  diversion_trap = NULL_SYMBOL;
 862}
 863
 864void top_level_diversion::set_diversion_trap(symbol, vunits)
 865{
 866  error("can't set diversion trap when no current diversion");
 867}
 868
 869void top_level_diversion::clear_diversion_trap()
 870{
 871  error("can't set diversion trap when no current diversion");
 872}
 873
 874void diversion_trap()
 875{
 876  vunits n;
 877  if (has_arg() && get_vunits(&n, 'v')) {
 878    symbol s = get_name();
 879    if (!s.is_null())
 880      curdiv->set_diversion_trap(s, n);
 881    else
 882      curdiv->clear_diversion_trap();
 883  }
 884  else
 885    curdiv->clear_diversion_trap();
 886  skip_line();
 887}
 888
 889void change_trap()
 890{
 891  symbol s = get_name(1);
 892  if (!s.is_null()) {
 893    vunits x;
 894    if (has_arg() && get_vunits(&x, 'v'))
 895      topdiv->change_trap(s, x);
 896    else
 897      topdiv->remove_trap(s);
 898  }
 899  skip_line();
 900}
 901
 902void print_traps()
 903{
 904  topdiv->print_traps();
 905  skip_line();
 906}
 907
 908void mark()
 909{
 910  symbol s = get_name();
 911  if (s.is_null())
 912    curdiv->marked_place = curdiv->get_vertical_position();
 913  else if (curdiv == topdiv)
 914    set_number_reg(s, nl_reg_contents);
 915  else
 916    set_number_reg(s, curdiv->get_vertical_position().to_units());
 917  skip_line();
 918}
 919
 920// This is truly bizarre.  It is documented in the SQ manual.
 921
 922void return_request()
 923{
 924  vunits dist = curdiv->marked_place - curdiv->get_vertical_position();
 925  if (has_arg()) {
 926    if (tok.ch() == '-') {
 927      tok.next();
 928      vunits x;
 929      if (get_vunits(&x, 'v'))
 930	dist = -x;
 931    }
 932    else {
 933      vunits x;
 934      if (get_vunits(&x, 'v'))
 935	dist = x >= V0 ? x - curdiv->get_vertical_position() : V0;
 936    }
 937  }
 938  if (dist < V0)
 939    curdiv->space(dist);
 940  skip_line();
 941}
 942
 943void vertical_position_traps()
 944{
 945  int n;
 946  if (has_arg() && get_integer(&n))
 947    vertical_position_traps_flag = (n != 0);
 948  else
 949    vertical_position_traps_flag = 1;
 950  skip_line();
 951}
 952
 953class page_offset_reg : public reg {
 954public:
 955  int get_value(units *);
 956  const char *get_string();
 957};
 958  
 959int page_offset_reg::get_value(units *res)
 960{
 961  *res = topdiv->get_page_offset().to_units();
 962  return 1;
 963}
 964
 965const char *page_offset_reg::get_string()
 966{
 967  return i_to_a(topdiv->get_page_offset().to_units());
 968}
 969
 970class page_length_reg : public reg {
 971public:
 972  int get_value(units *);
 973  const char *get_string();
 974};
 975  
 976int page_length_reg::get_value(units *res)
 977{
 978  *res = topdiv->get_page_length().to_units();
 979  return 1;
 980}
 981
 982const char *page_length_reg::get_string()
 983{
 984  return i_to_a(topdiv->get_page_length().to_units());
 985}
 986
 987class vertical_position_reg : public reg {
 988public:
 989  int get_value(units *);
 990  const char *get_string();
 991};
 992  
 993int vertical_position_reg::get_value(units *res)
 994{
 995  if (curdiv == topdiv && topdiv->before_first_page)
 996    *res = -1;
 997  else
 998    *res = curdiv->get_vertical_position().to_units();
 999  return 1;
1000}
1001
1002const char *vertical_position_reg::get_string()
1003{
1004  if (curdiv == topdiv && topdiv->before_first_page)
1005    return "-1";
1006  else
1007    return i_to_a(curdiv->get_vertical_position().to_units());
1008}
1009
1010class high_water_mark_reg : public reg {
1011public:
1012  int get_value(units *);
1013  const char *get_string();
1014};
1015  
1016int high_water_mark_reg::get_value(units *res)
1017{
1018  *res = curdiv->get_high_water_mark().to_units();
1019  return 1;
1020}
1021
1022const char *high_water_mark_reg::get_string()
1023{
1024  return i_to_a(curdiv->get_high_water_mark().to_units());
1025}
1026
1027class distance_to_next_trap_reg : public reg {
1028public:
1029  int get_value(units *);
1030  const char *get_string();
1031};
1032  
1033int distance_to_next_trap_reg::get_value(units *res)
1034{
1035  *res = curdiv->distance_to_next_trap().to_units();
1036  return 1;
1037}
1038
1039const char *distance_to_next_trap_reg::get_string()
1040{
1041  return i_to_a(curdiv->distance_to_next_trap().to_units());
1042}
1043
1044class diversion_name_reg : public reg {
1045public:
1046  const char *get_string();
1047};
1048
1049const char *diversion_name_reg::get_string()
1050{
1051  return curdiv->get_diversion_name();
1052}
1053
1054class page_number_reg : public general_reg {
1055public:
1056  page_number_reg();
1057  int get_value(units *);
1058  void set_value(units);
1059};
1060
1061page_number_reg::page_number_reg()
1062{
1063}
1064
1065void page_number_reg::set_value(units n)
1066{
1067  topdiv->set_page_number(n);
1068}
1069
1070int page_number_reg::get_value(units *res)
1071{
1072  *res = topdiv->get_page_number();
1073  return 1;
1074}
1075
1076class next_page_number_reg : public reg {
1077public:
1078  const char *get_string();
1079};
1080
1081const char *next_page_number_reg::get_string()
1082{
1083  return i_to_a(topdiv->get_next_page_number());
1084}
1085
1086class page_ejecting_reg : public reg {
1087public:
1088  const char *get_string();
1089};
1090
1091const char *page_ejecting_reg::get_string()
1092{
1093  return i_to_a(topdiv->get_ejecting());
1094}
1095
1096class constant_vunits_reg : public reg {
1097  vunits *p;
1098public:
1099  constant_vunits_reg(vunits *);
1100  const char *get_string();
1101};
1102
1103constant_vunits_reg::constant_vunits_reg(vunits *q) : p(q)
1104{
1105}
1106
1107const char *constant_vunits_reg::get_string()
1108{
1109  return i_to_a(p->to_units());
1110}
1111
1112class nl_reg : public variable_reg {
1113public:
1114  nl_reg();
1115  void set_value(units);
1116};
1117
1118nl_reg::nl_reg() : variable_reg(&nl_reg_contents)
1119{
1120}
1121
1122void nl_reg::set_value(units n)
1123{
1124  variable_reg::set_value(n);
1125  // Setting nl to a negative value when the vertical position in
1126  // the top-level diversion is 0 undoes the top of page transition,
1127  // so that the header macro will be called as if the top of page
1128  // transition hasn't happened.  This is used by Larry Wall's
1129  // wrapman program.  Setting before_first_page to 2 rather than 1,
1130  // tells top_level_diversion::begin_page not to call
1131  // output_file::begin_page again.
1132  if (n < 0 && topdiv->get_vertical_position() == V0)
1133    topdiv->before_first_page = 2;
1134}
1135
1136class no_space_mode_reg : public reg {
1137public:
1138  int get_value(units *);
1139  const char *get_string();
1140};
1141
1142int no_space_mode_reg::get_value(units *val)
1143{
1144  *val = curdiv->no_space_mode;
1145  return 1;
1146}
1147
1148const char *no_space_mode_reg::get_string()
1149{
1150  return curdiv->no_space_mode ? "1" : "0";
1151}
1152
1153void init_div_requests()
1154{
1155  init_request("box", box);
1156  init_request("boxa", box_append);
1157  init_request("bp", begin_page);
1158  init_request("ch", change_trap);
1159  init_request("da", divert_append);
1160  init_request("di", divert);
1161  init_request("dt", diversion_trap);
1162  init_request("fl", flush_output);
1163  init_request("mk", mark);
1164  init_request("ne", need_space);
1165  init_request("ns", no_space);
1166  init_request("os", output_saved_vertical_space);
1167  init_request("pl", page_length);
1168  init_request("pn", page_number);
1169  init_request("po", page_offset);
1170  init_request("ptr", print_traps);
1171  init_request("rs", restore_spacing);
1172  init_request("rt", return_request);
1173  init_request("sp", space_request);
1174  init_request("sv", save_vertical_space);
1175  init_request("vpt", vertical_position_traps);
1176  init_request("wh", when_request);
1177  number_reg_dictionary.define(".a",
1178		       new constant_int_reg(&last_post_line_extra_space));
1179  number_reg_dictionary.define(".d", new vertical_position_reg);
1180  number_reg_dictionary.define(".h", new high_water_mark_reg);
1181  number_reg_dictionary.define(".ne",
1182			       new constant_vunits_reg(&needed_space));
1183  number_reg_dictionary.define(".ns", new no_space_mode_reg);
1184  number_reg_dictionary.define(".o", new page_offset_reg);
1185  number_reg_dictionary.define(".p", new page_length_reg);
1186  number_reg_dictionary.define(".pe", new page_ejecting_reg);
1187  number_reg_dictionary.define(".pn", new next_page_number_reg);
1188  number_reg_dictionary.define(".t", new distance_to_next_trap_reg);
1189  number_reg_dictionary.define(".trunc",
1190			       new constant_vunits_reg(&truncated_space));
1191  number_reg_dictionary.define(".vpt", 
1192		       new constant_int_reg(&vertical_position_traps_flag));
1193  number_reg_dictionary.define(".z", new diversion_name_reg);
1194  number_reg_dictionary.define("dl", new variable_reg(&dl_reg_contents));
1195  number_reg_dictionary.define("dn", new variable_reg(&dn_reg_contents));
1196  number_reg_dictionary.define("nl", new nl_reg);
1197  number_reg_dictionary.define("%", new page_number_reg);
1198}