groff: update vendor branch to v1.20.1
[dragonfly.git] / contrib / groff / src / roff / troff / div.cpp
CommitLineData
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
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
4d3e9548
JL
10Software Foundation, either version 3 of the License, or
11(at your option) any later version.
92d0a6a6
JR
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
4d3e9548
JL
18You should have received a copy of the GNU General Public License
19along 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
38int exit_started = 0; // the exit process has started
39int done_end_macro = 0; // the end macro (if any) has finished
40int seen_last_page_ejector = 0; // seen the LAST_PAGE_EJECTOR cookie
41int last_page_number = 0; // if > 0, the number of the last page
42 // specified with -o
43static int began_page_in_end_macro = 0; // a new page was begun during the end macro
44
45static int last_post_line_extra_space = 0; // needed for \n(.a
46static int nl_reg_contents = -1;
47static int dl_reg_contents = 0;
48static int dn_reg_contents = 0;
49static int vertical_position_traps_flag = 1;
50static vunits truncated_space;
51static vunits needed_space;
52
53diversion::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
61struct vertical_size {
62 vunits pre_extra, post_extra, pre, post;
63 vertical_size(vunits vs, vunits post_vs);
64};
65
66vertical_size::vertical_size(vunits vs, vunits post_vs)
67: pre_extra(V0), post_extra(V0), pre(vs), post(post_vs)
68{
69}
70
71void node::set_vertical_size(vertical_size *)
72{
73}
74
75void 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
85void 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
93top_level_diversion *topdiv;
94
95diversion *curdiv;
96
97void 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
147void divert()
148{
149 do_divert(0, 0);
150}
151
152void divert_append()
153{
154 do_divert(1, 0);
155}
156
157void box()
158{
159 do_divert(0, 1);
160}
161
162void box_append()
163{
164 do_divert(1, 1);
165}
166
167void 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
177macro_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
231macro_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
246vunits 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
255void macro_diversion::transparent_output(unsigned char c)
256{
257 mac->append(c);
258}
259
260void macro_diversion::transparent_output(node *n)
261{
262 mac->append(n);
263}
264
265void 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
310void 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
325void macro_diversion::copy_file(const char *filename)
326{
327 mac->append(new diverted_copy_file_node(filename));
328}
329
330top_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
341trap *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
368vunits 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
377void 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
426void 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
436void top_level_diversion::transparent_output(node * /*n*/)
437{
438 error("can't transparently output node at top level");
439}
440
441void 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
448void 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
484trap::trap(symbol s, vunits n, trap *p)
485: next(p), position(n), nm(s)
486{
487}
488
489void 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
511void 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
520void 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
529void 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
538void 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
548void 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
559void 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.
571int 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
616void 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
630void top_level_diversion::set_next_page_number(int n)
631{
632 next_page_number= n;
633 have_next_page_number = 1;
634}
635
636int top_level_diversion::get_next_page_number()
637{
638 return have_next_page_number ? next_page_number : page_number + 1;
639}
640
641void top_level_diversion::set_page_length(vunits n)
642{
643 page_length = n;
644}
645
646diversion::~diversion()
647{
648}
649
650void 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
664void 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
674void 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
687void 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
741void no_space()
742{
743 curdiv->no_space_mode = 1;
744 skip_line();
745}
746
747void 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,
754because otherwise arguments using | will be wrong. But if we just
92d0a6a6
JR
755generate a break as usual, then the line forced out may spring a trap
756and thus push a macro onto the input stack before we have had a chance
465b256c 757to read the argument to the sp request. We resolve this dilemma by
92d0a6a6
JR
758setting, before generating the break, a flag which will postpone the
759actual pushing of the macro associated with the trap sprung by the
760outputting of the line forced out by the break till after we have read
761the argument to the request. If the break did cause a trap to be
762sprung, then we don't actually do the space. */
763
764void 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
783void 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
793BEGIN_TRAP token is not skipped over. */
794
795void 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
806void 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
819vunits saved_space;
820
821void 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
833void 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
843void 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
854void macro_diversion::set_diversion_trap(symbol s, vunits n)
855{
856 diversion_trap = s;
857 diversion_trap_pos = n;
858}
859
860void macro_diversion::clear_diversion_trap()
861{
862 diversion_trap = NULL_SYMBOL;
863}
864
865void top_level_diversion::set_diversion_trap(symbol, vunits)
866{
867 error("can't set diversion trap when no current diversion");
868}
869
870void top_level_diversion::clear_diversion_trap()
871{
872 error("can't set diversion trap when no current diversion");
873}
874
875void 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
890void 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
903void print_traps()
904{
905 topdiv->print_traps();
906 skip_line();
907}
908
909void 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
923void 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
944void 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
954class page_offset_reg : public reg {
955public:
956 int get_value(units *);
957 const char *get_string();
958};
959
960int page_offset_reg::get_value(units *res)
961{
962 *res = topdiv->get_page_offset().to_units();
963 return 1;
964}
965
966const char *page_offset_reg::get_string()
967{
968 return i_to_a(topdiv->get_page_offset().to_units());
969}
970
971class page_length_reg : public reg {
972public:
973 int get_value(units *);
974 const char *get_string();
975};
976
977int page_length_reg::get_value(units *res)
978{
979 *res = topdiv->get_page_length().to_units();
980 return 1;
981}
982
983const char *page_length_reg::get_string()
984{
985 return i_to_a(topdiv->get_page_length().to_units());
986}
987
988class vertical_position_reg : public reg {
989public:
990 int get_value(units *);
991 const char *get_string();
992};
993
994int 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
1003const 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
1011class high_water_mark_reg : public reg {
1012public:
1013 int get_value(units *);
1014 const char *get_string();
1015};
1016
1017int high_water_mark_reg::get_value(units *res)
1018{
1019 *res = curdiv->get_high_water_mark().to_units();
1020 return 1;
1021}
1022
1023const char *high_water_mark_reg::get_string()
1024{
1025 return i_to_a(curdiv->get_high_water_mark().to_units());
1026}
1027
1028class distance_to_next_trap_reg : public reg {
1029public:
1030 int get_value(units *);
1031 const char *get_string();
1032};
1033
1034int distance_to_next_trap_reg::get_value(units *res)
1035{
1036 *res = curdiv->distance_to_next_trap().to_units();
1037 return 1;
1038}
1039
1040const char *distance_to_next_trap_reg::get_string()
1041{
1042 return i_to_a(curdiv->distance_to_next_trap().to_units());
1043}
1044
1045class diversion_name_reg : public reg {
1046public:
1047 const char *get_string();
1048};
1049
1050const char *diversion_name_reg::get_string()
1051{
1052 return curdiv->get_diversion_name();
1053}
1054
1055class page_number_reg : public general_reg {
1056public:
1057 page_number_reg();
1058 int get_value(units *);
1059 void set_value(units);
1060};
1061
1062page_number_reg::page_number_reg()
1063{
1064}
1065
1066void page_number_reg::set_value(units n)
1067{
1068 topdiv->set_page_number(n);
1069}
1070
1071int page_number_reg::get_value(units *res)
1072{
1073 *res = topdiv->get_page_number();
1074 return 1;
1075}
1076
1077class next_page_number_reg : public reg {
1078public:
1079 const char *get_string();
1080};
1081
1082const char *next_page_number_reg::get_string()
1083{
1084 return i_to_a(topdiv->get_next_page_number());
1085}
1086
1087class page_ejecting_reg : public reg {
1088public:
1089 const char *get_string();
1090};
1091
1092const char *page_ejecting_reg::get_string()
1093{
1094 return i_to_a(topdiv->get_ejecting());
1095}
1096
1097class constant_vunits_reg : public reg {
1098 vunits *p;
1099public:
1100 constant_vunits_reg(vunits *);
1101 const char *get_string();
1102};
1103
1104constant_vunits_reg::constant_vunits_reg(vunits *q) : p(q)
1105{
1106}
1107
1108const char *constant_vunits_reg::get_string()
1109{
1110 return i_to_a(p->to_units());
1111}
1112
1113class nl_reg : public variable_reg {
1114public:
1115 nl_reg();
1116 void set_value(units);
1117};
1118
1119nl_reg::nl_reg() : variable_reg(&nl_reg_contents)
1120{
1121}
1122
1123void 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
1137class no_space_mode_reg : public reg {
1138public:
1139 int get_value(units *);
1140 const char *get_string();
1141};
1142
1143int no_space_mode_reg::get_value(units *val)
1144{
1145 *val = curdiv->no_space_mode;
1146 return 1;
1147}
1148
1149const char *no_space_mode_reg::get_string()
1150{
1151 return curdiv->no_space_mode ? "1" : "0";
1152}
1153
1154void 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}