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