Sync-to-go: update copyright for 2015
[s-roff.git] / src / troff / div.cpp
blob21e2b6bed6abd4648744e3b26efe5227889d0262
1 /*@ Diversions.
3 * Copyright (c) 2014 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
5 * Copyright (C) 1989 - 1992, 2000 - 2002, 2004
6 * Free Software Foundation, Inc.
7 * Written by James Clark (jjc@jclark.com)
9 * This is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License as published by the Free
11 * Software Foundation; either version 2, or (at your option) any later
12 * version.
14 * This is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 * for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with groff; see the file COPYING. If not, write to the Free Software
21 * Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
24 #include "config.h"
25 #include "troff-config.h"
27 #include "nonposix.h"
28 #include "stringclass.h"
30 #include "dictionary.h"
31 #include "div.h"
32 #include "env.h"
33 #include "hvunits.h"
34 #include "mtsm.h"
35 #include "node.h"
36 #include "reg.h"
37 #include "request.h"
38 #include "token.h"
39 #include "troff.h"
41 int exit_started = 0; // the exit process has started
42 int done_end_macro = 0; // the end macro (if any) has finished
43 int seen_last_page_ejector = 0; // seen the LAST_PAGE_EJECTOR cookie
44 int last_page_number = 0; // if > 0, the number of the last page
45 // specified with -o
46 static int began_page_in_end_macro = 0; // a new page was begun during the end macro
48 static int last_post_line_extra_space = 0; // needed for \n(.a
49 static int nl_reg_contents = -1;
50 static int dl_reg_contents = 0;
51 static int dn_reg_contents = 0;
52 static int vertical_position_traps_flag = 1;
53 static vunits truncated_space;
54 static vunits needed_space;
56 diversion::diversion(symbol s)
57 : prev(0), nm(s), vertical_position(V0), high_water_mark(V0),
58 any_chars_added(0), no_space_mode(0), needs_push(0), saved_seen_break(0),
59 saved_seen_space(0), saved_seen_eol(0), saved_suppress_next_eol(0),
60 marked_place(V0)
64 struct vertical_size {
65 vunits pre_extra, post_extra, pre, post;
66 vertical_size(vunits vs, vunits post_vs);
69 vertical_size::vertical_size(vunits vs, vunits post_vs)
70 : pre_extra(V0), post_extra(V0), pre(vs), post(post_vs)
74 void node::set_vertical_size(vertical_size *)
78 void extra_size_node::set_vertical_size(vertical_size *v)
80 if (n < V0) {
81 if (-n > v->pre_extra)
82 v->pre_extra = -n;
84 else if (n > v->post_extra)
85 v->post_extra = n;
88 void vertical_size_node::set_vertical_size(vertical_size *v)
90 if (n < V0)
91 v->pre = -n;
92 else
93 v->post = n;
96 top_level_diversion *topdiv;
98 diversion *curdiv;
100 void do_divert(int append, int boxing)
102 tok.skip();
103 symbol nm = get_name();
104 if (nm.is_null()) {
105 if (curdiv->prev) {
106 curenv->seen_break = curdiv->saved_seen_break;
107 curenv->seen_space = curdiv->saved_seen_space;
108 curenv->seen_eol = curdiv->saved_seen_eol;
109 curenv->suppress_next_eol = curdiv->saved_suppress_next_eol;
110 if (boxing) {
111 curenv->line = curdiv->saved_line;
112 curenv->width_total = curdiv->saved_width_total;
113 curenv->space_total = curdiv->saved_space_total;
114 curenv->saved_indent = curdiv->saved_saved_indent;
115 curenv->target_text_length = curdiv->saved_target_text_length;
116 curenv->prev_line_interrupted = curdiv->saved_prev_line_interrupted;
118 diversion *temp = curdiv;
119 curdiv = curdiv->prev;
120 delete temp;
122 else
123 warning(WARN_DI, "diversion stack underflow");
125 else {
126 macro_diversion *md = new macro_diversion(nm, append);
127 md->prev = curdiv;
128 curdiv = md;
129 curdiv->saved_seen_break = curenv->seen_break;
130 curdiv->saved_seen_space = curenv->seen_space;
131 curdiv->saved_seen_eol = curenv->seen_eol;
132 curdiv->saved_suppress_next_eol = curenv->suppress_next_eol;
133 curenv->seen_break = 0;
134 curenv->seen_space = 0;
135 curenv->seen_eol = 0;
136 if (boxing) {
137 curdiv->saved_line = curenv->line;
138 curdiv->saved_width_total = curenv->width_total;
139 curdiv->saved_space_total = curenv->space_total;
140 curdiv->saved_saved_indent = curenv->saved_indent;
141 curdiv->saved_target_text_length = curenv->target_text_length;
142 curdiv->saved_prev_line_interrupted = curenv->prev_line_interrupted;
143 curenv->line = 0;
144 curenv->start_line();
147 skip_line();
150 void divert()
152 do_divert(0, 0);
155 void divert_append()
157 do_divert(1, 0);
160 void box()
162 do_divert(0, 1);
165 void box_append()
167 do_divert(1, 1);
170 void diversion::need(vunits n)
172 vunits d = distance_to_next_trap();
173 if (d < n) {
174 truncated_space = -d;
175 needed_space = n;
176 space(d, 1);
180 macro_diversion::macro_diversion(symbol s, int append)
181 : diversion(s), max_width(H0)
183 #if 0
184 if (append) {
185 /* We don't allow recursive appends eg:
187 .da a
191 This causes an infinite loop in troff anyway.
192 This is because the user could do
194 .as a foo
196 in the diversion, and this would mess things up royally,
197 since there would be two things appending to the same
198 macro_header.
199 To make it work, we would have to copy the _contents_
200 of the macro into which we were diverting; this doesn't
201 strike me as worthwhile.
202 However,
204 .di a
209 will work and will make `a' contain two copies of what it contained
210 before; in troff, `a' would contain nothing. */
211 request_or_macro *rm
212 = (request_or_macro *)request_dictionary.remove(s);
213 if (!rm || (mac = rm->to_macro()) == 0)
214 mac = new macro;
216 else
217 mac = new macro;
218 #endif
219 // We can now catch the situation described above by comparing
220 // the length of the charlist in the macro_header with the length
221 // stored in the macro. When we detect this, we copy the contents.
222 mac = new macro(1);
223 if (append) {
224 request_or_macro *rm
225 = (request_or_macro *)request_dictionary.lookup(s);
226 if (rm) {
227 macro *m = rm->to_macro();
228 if (m)
229 *mac = *m;
234 macro_diversion::~macro_diversion()
236 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
237 macro *m = rm ? rm->to_macro() : 0;
238 if (m) {
239 *m = *mac;
240 delete mac;
242 else
243 request_dictionary.define(nm, mac);
244 mac = 0;
245 dl_reg_contents = max_width.to_units();
246 dn_reg_contents = vertical_position.to_units();
249 vunits macro_diversion::distance_to_next_trap()
251 if (!diversion_trap.is_null() && diversion_trap_pos > vertical_position)
252 return diversion_trap_pos - vertical_position;
253 else
254 // Substract vresolution so that vunits::vunits does not overflow.
255 return vunits(INT_MAX - vresolution);
258 void macro_diversion::transparent_output(unsigned char c)
260 mac->append(c);
263 void macro_diversion::transparent_output(node *n)
265 mac->append(n);
268 void macro_diversion::output(node *nd, int retain_size,
269 vunits vs, vunits post_vs, hunits width)
271 no_space_mode = 0;
272 vertical_size v(vs, post_vs);
273 while (nd != 0) {
274 nd->set_vertical_size(&v);
275 node *temp = nd;
276 nd = nd->next;
277 if (temp->interpret(mac))
278 delete temp;
279 else {
280 #if 1
281 temp->freeze_space();
282 #endif
283 mac->append(temp);
286 last_post_line_extra_space = v.post_extra.to_units();
287 if (!retain_size) {
288 v.pre = vs;
289 v.post = post_vs;
291 if (width > max_width)
292 max_width = width;
293 vunits x = v.pre + v.pre_extra + v.post + v.post_extra;
294 if (vertical_position_traps_flag
295 && !diversion_trap.is_null() && diversion_trap_pos > vertical_position
296 && diversion_trap_pos <= vertical_position + x) {
297 vunits trunc = vertical_position + x - diversion_trap_pos;
298 if (trunc > v.post)
299 trunc = v.post;
300 v.post -= trunc;
301 x -= trunc;
302 truncated_space = trunc;
303 spring_trap(diversion_trap);
305 mac->append(new vertical_size_node(-v.pre));
306 mac->append(new vertical_size_node(v.post));
307 mac->append('\n');
308 vertical_position += x;
309 if (vertical_position - v.post > high_water_mark)
310 high_water_mark = vertical_position - v.post;
313 void macro_diversion::space(vunits n, int)
315 if (vertical_position_traps_flag
316 && !diversion_trap.is_null() && diversion_trap_pos > vertical_position
317 && diversion_trap_pos <= vertical_position + n) {
318 truncated_space = vertical_position + n - diversion_trap_pos;
319 n = diversion_trap_pos - vertical_position;
320 spring_trap(diversion_trap);
322 else if (n + vertical_position < V0)
323 n = -vertical_position;
324 mac->append(new diverted_space_node(n));
325 vertical_position += n;
328 void macro_diversion::copy_file(const char *filename)
330 mac->append(new diverted_copy_file_node(filename));
333 top_level_diversion::top_level_diversion()
334 : page_number(0), page_count(0), last_page_count(-1),
335 page_length(units_per_inch*11),
336 prev_page_offset(units_per_inch), page_offset(units_per_inch),
337 page_trap_list(0), have_next_page_number(0),
338 ejecting_page(0), before_first_page(1)
342 // find the next trap after pos
344 trap *top_level_diversion::find_next_trap(vunits *next_trap_pos)
346 trap *next_trap = 0;
347 for (trap *pt = page_trap_list; pt != 0; pt = pt->next)
348 if (!pt->nm.is_null()) {
349 if (pt->position >= V0) {
350 if (pt->position > vertical_position
351 && pt->position < page_length
352 && (next_trap == 0 || pt->position < *next_trap_pos)) {
353 next_trap = pt;
354 *next_trap_pos = pt->position;
357 else {
358 vunits pos = pt->position;
359 pos += page_length;
360 if (pos > 0
361 && pos > vertical_position
362 && (next_trap == 0 || pos < *next_trap_pos)) {
363 next_trap = pt;
364 *next_trap_pos = pos;
368 return next_trap;
371 vunits top_level_diversion::distance_to_next_trap()
373 vunits d;
374 if (!find_next_trap(&d))
375 return page_length - vertical_position;
376 else
377 return d - vertical_position;
380 void top_level_diversion::output(node *nd, int retain_size,
381 vunits vs, vunits post_vs, hunits width)
383 no_space_mode = 0;
384 vunits next_trap_pos;
385 trap *next_trap = find_next_trap(&next_trap_pos);
386 if (before_first_page && begin_page())
387 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
388 vertical_size v(vs, post_vs);
389 for (node *tem = nd; tem != 0; tem = tem->next)
390 tem->set_vertical_size(&v);
391 last_post_line_extra_space = v.post_extra.to_units();
392 if (!retain_size) {
393 v.pre = vs;
394 v.post = post_vs;
396 vertical_position += v.pre;
397 vertical_position += v.pre_extra;
398 the_output->print_line(page_offset, vertical_position, nd,
399 v.pre + v.pre_extra, v.post_extra, width);
400 vertical_position += v.post_extra;
401 if (vertical_position > high_water_mark)
402 high_water_mark = vertical_position;
403 if (vertical_position_traps_flag && vertical_position >= page_length)
404 begin_page();
405 else if (vertical_position_traps_flag
406 && next_trap != 0 && vertical_position >= next_trap_pos) {
407 nl_reg_contents = vertical_position.to_units();
408 truncated_space = v.post;
409 spring_trap(next_trap->nm);
411 else if (v.post > V0) {
412 vertical_position += v.post;
413 if (vertical_position_traps_flag
414 && next_trap != 0 && vertical_position >= next_trap_pos) {
415 truncated_space = vertical_position - next_trap_pos;
416 vertical_position = next_trap_pos;
417 nl_reg_contents = vertical_position.to_units();
418 spring_trap(next_trap->nm);
420 else if (vertical_position_traps_flag && vertical_position >= page_length)
421 begin_page();
422 else
423 nl_reg_contents = vertical_position.to_units();
425 else
426 nl_reg_contents = vertical_position.to_units();
429 void top_level_diversion::transparent_output(unsigned char c)
431 if (before_first_page && begin_page())
432 // This can only happen with the .output request.
433 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
434 const char *s = asciify(c);
435 while (*s)
436 the_output->transparent_char(*s++);
439 void top_level_diversion::transparent_output(node * /*n*/)
441 error("can't transparently output node at top level");
444 void top_level_diversion::copy_file(const char *filename)
446 if (before_first_page && begin_page())
447 fatal("sorry, I didn't manage to begin the first page in time: use an explicit .br request");
448 the_output->copy_file(page_offset, vertical_position, filename);
451 void top_level_diversion::space(vunits n, int forced)
453 if (no_space_mode) {
454 if (!forced)
455 return;
456 else
457 no_space_mode = 0;
459 if (before_first_page) {
460 begin_page(n);
461 return;
463 vunits next_trap_pos;
464 trap *next_trap = find_next_trap(&next_trap_pos);
465 vunits y = vertical_position + n;
466 if (curenv->get_vertical_spacing().to_units())
467 curenv->seen_space += n.to_units()
468 / curenv->get_vertical_spacing().to_units();
469 if (vertical_position_traps_flag && next_trap != 0 && y >= next_trap_pos) {
470 vertical_position = next_trap_pos;
471 nl_reg_contents = vertical_position.to_units();
472 truncated_space = y - vertical_position;
473 spring_trap(next_trap->nm);
475 else if (y < V0) {
476 vertical_position = V0;
477 nl_reg_contents = vertical_position.to_units();
479 else if (vertical_position_traps_flag && y >= page_length && n >= V0)
480 begin_page(y - page_length);
481 else {
482 vertical_position = y;
483 nl_reg_contents = vertical_position.to_units();
487 trap::trap(symbol s, vunits n, trap *p)
488 : next(p), position(n), nm(s)
492 void top_level_diversion::add_trap(symbol nam, vunits pos)
494 trap *first_free_slot = 0;
495 trap **p;
496 for (p = &page_trap_list; *p; p = &(*p)->next) {
497 if ((*p)->nm.is_null()) {
498 if (first_free_slot == 0)
499 first_free_slot = *p;
501 else if ((*p)->position == pos) {
502 (*p)->nm = nam;
503 return;
506 if (first_free_slot) {
507 first_free_slot->nm = nam;
508 first_free_slot->position = pos;
510 else
511 *p = new trap(nam, pos, 0);
514 void top_level_diversion::remove_trap(symbol nam)
516 for (trap *p = page_trap_list; p; p = p->next)
517 if (p->nm == nam) {
518 p->nm = NULL_SYMBOL;
519 return;
523 void top_level_diversion::remove_trap_at(vunits pos)
525 for (trap *p = page_trap_list; p; p = p->next)
526 if (p->position == pos) {
527 p->nm = NULL_SYMBOL;
528 return;
532 void top_level_diversion::change_trap(symbol nam, vunits pos)
534 for (trap *p = page_trap_list; p; p = p->next)
535 if (p->nm == nam) {
536 p->position = pos;
537 return;
541 void top_level_diversion::print_traps()
543 for (trap *p = page_trap_list; p; p = p->next)
544 if (p->nm.is_null())
545 fprintf(stderr, " empty\n");
546 else
547 fprintf(stderr, "%s\t%d\n", p->nm.contents(), p->position.to_units());
548 fflush(stderr);
551 void end_diversions()
553 while (curdiv != topdiv) {
554 error("automatically ending diversion `%1' on exit",
555 curdiv->nm.contents());
556 diversion *tem = curdiv;
557 curdiv = curdiv->prev;
558 delete tem;
562 void cleanup_and_exit(int exit_code)
564 if (the_output) {
565 the_output->trailer(topdiv->get_page_length());
566 delete the_output;
568 FLUSH_INPUT_PIPE(STDIN_FILENO);
569 exit(exit_code);
572 // Returns non-zero if it sprung a top-of-page trap.
573 // The optional parameter is for the .trunc register.
574 int top_level_diversion::begin_page(vunits n)
576 if (exit_started) {
577 if (page_count == last_page_count
578 ? curenv->is_empty()
579 : (done_end_macro && (seen_last_page_ejector || began_page_in_end_macro)))
580 cleanup_and_exit(0);
581 if (!done_end_macro)
582 began_page_in_end_macro = 1;
584 if (last_page_number > 0 && page_number == last_page_number)
585 cleanup_and_exit(0);
586 if (!the_output)
587 init_output();
588 ++page_count;
589 if (have_next_page_number) {
590 page_number = next_page_number;
591 have_next_page_number = 0;
593 else if (before_first_page == 1)
594 page_number = 1;
595 else
596 page_number++;
597 // spring the top of page trap if there is one
598 vunits next_trap_pos;
599 vertical_position = -vresolution;
600 trap *next_trap = find_next_trap(&next_trap_pos);
601 vertical_position = V0;
602 high_water_mark = V0;
603 ejecting_page = 0;
604 // If before_first_page was 2, then the top of page transition was undone
605 // using eg .nr nl 0-1. See nl_reg::set_value.
606 if (before_first_page != 2)
607 the_output->begin_page(page_number, page_length);
608 before_first_page = 0;
609 nl_reg_contents = vertical_position.to_units();
610 if (vertical_position_traps_flag && next_trap != 0 && next_trap_pos == V0) {
611 truncated_space = n;
612 spring_trap(next_trap->nm);
613 return 1;
615 else
616 return 0;
619 void continue_page_eject()
621 if (topdiv->get_ejecting()) {
622 if (curdiv != topdiv)
623 error("can't continue page ejection because of current diversion");
624 else if (!vertical_position_traps_flag)
625 error("can't continue page ejection because vertical position traps disabled");
626 else {
627 push_page_ejector();
628 topdiv->space(topdiv->get_page_length(), 1);
633 void top_level_diversion::set_next_page_number(int n)
635 next_page_number= n;
636 have_next_page_number = 1;
639 int top_level_diversion::get_next_page_number()
641 return have_next_page_number ? next_page_number : page_number + 1;
644 void top_level_diversion::set_page_length(vunits n)
646 page_length = n;
649 diversion::~diversion()
653 void page_offset()
655 hunits n;
656 // The troff manual says that the default scaling indicator is v,
657 // but it is in fact m: v wouldn't make sense for a horizontally
658 // oriented request.
659 if (!has_arg() || !get_hunits(&n, 'm', topdiv->page_offset))
660 n = topdiv->prev_page_offset;
661 topdiv->prev_page_offset = topdiv->page_offset;
662 topdiv->page_offset = n;
663 topdiv->modified_tag.incl(MTSM_PO);
664 skip_line();
667 void page_length()
669 vunits n;
670 if (has_arg() && get_vunits(&n, 'v', topdiv->get_page_length()))
671 topdiv->set_page_length(n);
672 else
673 topdiv->set_page_length(11*units_per_inch);
674 skip_line();
677 void when_request()
679 vunits n;
680 if (get_vunits(&n, 'v')) {
681 symbol s = get_name();
682 if (s.is_null())
683 topdiv->remove_trap_at(n);
684 else
685 topdiv->add_trap(s, n);
687 skip_line();
690 void begin_page()
692 int got_arg = 0;
693 int n = 0; /* pacify compiler */
694 if (has_arg() && get_integer(&n, topdiv->get_page_number()))
695 got_arg = 1;
696 while (!tok.newline() && !tok.eof())
697 tok.next();
698 if (curdiv == topdiv) {
699 if (topdiv->before_first_page) {
700 if (!break_flag) {
701 if (got_arg)
702 topdiv->set_next_page_number(n);
703 if (got_arg || !topdiv->no_space_mode)
704 topdiv->begin_page();
706 else if (topdiv->no_space_mode && !got_arg)
707 topdiv->begin_page();
708 else {
709 /* Given this
711 .wh 0 x
712 .de x
713 .tm \\n%
715 .bp 3
717 troff prints
722 This code makes groff do the same. */
724 push_page_ejector();
725 topdiv->begin_page();
726 if (got_arg)
727 topdiv->set_next_page_number(n);
728 topdiv->set_ejecting();
731 else {
732 push_page_ejector();
733 if (break_flag)
734 curenv->do_break();
735 if (got_arg)
736 topdiv->set_next_page_number(n);
737 if (!(topdiv->no_space_mode && !got_arg))
738 topdiv->set_ejecting();
741 tok.next();
744 void no_space()
746 curdiv->no_space_mode = 1;
747 skip_line();
750 void restore_spacing()
752 curdiv->no_space_mode = 0;
753 skip_line();
756 /* It is necessary to generate a break before reading the argument,
757 because otherwise arguments using | will be wrong. But if we just
758 generate a break as usual, then the line forced out may spring a trap
759 and thus push a macro onto the input stack before we have had a chance
760 to read the argument to the sp request. We resolve this dilemma by
761 setting, before generating the break, a flag which will postpone the
762 actual pushing of the macro associated with the trap sprung by the
763 outputting of the line forced out by the break till after we have read
764 the argument to the request. If the break did cause a trap to be
765 sprung, then we don't actually do the space. */
767 void space_request()
769 postpone_traps();
770 if (break_flag)
771 curenv->do_break();
772 vunits n;
773 if (!has_arg() || !get_vunits(&n, 'v'))
774 n = curenv->get_vertical_spacing();
775 while (!tok.newline() && !tok.eof())
776 tok.next();
777 if (!unpostpone_traps() && !curdiv->no_space_mode)
778 curdiv->space(n);
779 else
780 // The line might have had line spacing that was truncated.
781 truncated_space += n;
783 tok.next();
786 void blank_line()
788 curenv->do_break();
789 if (!trap_sprung_flag && !curdiv->no_space_mode)
790 curdiv->space(curenv->get_vertical_spacing());
791 else
792 truncated_space += curenv->get_vertical_spacing();
795 /* need_space might spring a trap and so we must be careful that the
796 BEGIN_TRAP token is not skipped over. */
798 void need_space()
800 vunits n;
801 if (!has_arg() || !get_vunits(&n, 'v'))
802 n = curenv->get_vertical_spacing();
803 while (!tok.newline() && !tok.eof())
804 tok.next();
805 curdiv->need(n);
806 tok.next();
809 void page_number()
811 int n;
813 // the ps4html register is set if we are using -Tps
814 // to generate images for html
815 reg *r = (reg *)number_reg_dictionary.lookup("ps4html");
816 if (r == NULL)
817 if (has_arg() && get_integer(&n, topdiv->get_page_number()))
818 topdiv->set_next_page_number(n);
819 skip_line();
822 vunits saved_space;
824 void save_vertical_space()
826 vunits x;
827 if (!has_arg() || !get_vunits(&x, 'v'))
828 x = curenv->get_vertical_spacing();
829 if (curdiv->distance_to_next_trap() > x)
830 curdiv->space(x, 1);
831 else
832 saved_space = x;
833 skip_line();
836 void output_saved_vertical_space()
838 while (!tok.newline() && !tok.eof())
839 tok.next();
840 if (saved_space > V0)
841 curdiv->space(saved_space, 1);
842 saved_space = V0;
843 tok.next();
846 void flush_output()
848 while (!tok.newline() && !tok.eof())
849 tok.next();
850 if (break_flag)
851 curenv->do_break();
852 if (the_output)
853 the_output->flush();
854 tok.next();
857 void macro_diversion::set_diversion_trap(symbol s, vunits n)
859 diversion_trap = s;
860 diversion_trap_pos = n;
863 void macro_diversion::clear_diversion_trap()
865 diversion_trap = NULL_SYMBOL;
868 void top_level_diversion::set_diversion_trap(symbol, vunits)
870 error("can't set diversion trap when no current diversion");
873 void top_level_diversion::clear_diversion_trap()
875 error("can't set diversion trap when no current diversion");
878 void diversion_trap()
880 vunits n;
881 if (has_arg() && get_vunits(&n, 'v')) {
882 symbol s = get_name();
883 if (!s.is_null())
884 curdiv->set_diversion_trap(s, n);
885 else
886 curdiv->clear_diversion_trap();
888 else
889 curdiv->clear_diversion_trap();
890 skip_line();
893 void change_trap()
895 symbol s = get_name(1);
896 if (!s.is_null()) {
897 vunits x;
898 if (has_arg() && get_vunits(&x, 'v'))
899 topdiv->change_trap(s, x);
900 else
901 topdiv->remove_trap(s);
903 skip_line();
906 void print_traps()
908 topdiv->print_traps();
909 skip_line();
912 void mark()
914 symbol s = get_name();
915 if (s.is_null())
916 curdiv->marked_place = curdiv->get_vertical_position();
917 else if (curdiv == topdiv)
918 set_number_reg(s, nl_reg_contents);
919 else
920 set_number_reg(s, curdiv->get_vertical_position().to_units());
921 skip_line();
924 // This is truly bizarre. It is documented in the SQ manual.
926 void return_request()
928 vunits dist = curdiv->marked_place - curdiv->get_vertical_position();
929 if (has_arg()) {
930 if (tok.ch() == '-') {
931 tok.next();
932 vunits x;
933 if (get_vunits(&x, 'v'))
934 dist = -x;
936 else {
937 vunits x;
938 if (get_vunits(&x, 'v'))
939 dist = x >= V0 ? x - curdiv->get_vertical_position() : V0;
942 if (dist < V0)
943 curdiv->space(dist);
944 skip_line();
947 void vertical_position_traps()
949 int n;
950 if (has_arg() && get_integer(&n))
951 vertical_position_traps_flag = (n != 0);
952 else
953 vertical_position_traps_flag = 1;
954 skip_line();
957 class page_offset_reg
958 : public reg
960 public:
961 int get_value(units *);
962 const char *get_string();
965 int page_offset_reg::get_value(units *res)
967 *res = topdiv->get_page_offset().to_units();
968 return 1;
971 const char *page_offset_reg::get_string()
973 return i_to_a(topdiv->get_page_offset().to_units());
976 class page_length_reg
977 : public reg
979 public:
980 int get_value(units *);
981 const char *get_string();
984 int page_length_reg::get_value(units *res)
986 *res = topdiv->get_page_length().to_units();
987 return 1;
990 const char *page_length_reg::get_string()
992 return i_to_a(topdiv->get_page_length().to_units());
995 class vertical_position_reg
996 : public reg
998 public:
999 int get_value(units *);
1000 const char *get_string();
1003 int vertical_position_reg::get_value(units *res)
1005 if (curdiv == topdiv && topdiv->before_first_page)
1006 *res = -1;
1007 else
1008 *res = curdiv->get_vertical_position().to_units();
1009 return 1;
1012 const char *vertical_position_reg::get_string()
1014 if (curdiv == topdiv && topdiv->before_first_page)
1015 return "-1";
1016 else
1017 return i_to_a(curdiv->get_vertical_position().to_units());
1020 class high_water_mark_reg
1021 : public reg
1023 public:
1024 int get_value(units *);
1025 const char *get_string();
1028 int high_water_mark_reg::get_value(units *res)
1030 *res = curdiv->get_high_water_mark().to_units();
1031 return 1;
1034 const char *high_water_mark_reg::get_string()
1036 return i_to_a(curdiv->get_high_water_mark().to_units());
1039 class distance_to_next_trap_reg
1040 : public reg
1042 public:
1043 int get_value(units *);
1044 const char *get_string();
1047 int distance_to_next_trap_reg::get_value(units *res)
1049 *res = curdiv->distance_to_next_trap().to_units();
1050 return 1;
1053 const char *distance_to_next_trap_reg::get_string()
1055 return i_to_a(curdiv->distance_to_next_trap().to_units());
1058 class diversion_name_reg
1059 : public reg
1061 public:
1062 const char *get_string();
1065 const char *diversion_name_reg::get_string()
1067 return curdiv->get_diversion_name();
1070 class page_number_reg
1071 : public general_reg
1073 public:
1074 page_number_reg();
1075 int get_value(units *);
1076 void set_value(units);
1079 page_number_reg::page_number_reg()
1083 void page_number_reg::set_value(units n)
1085 topdiv->set_page_number(n);
1088 int page_number_reg::get_value(units *res)
1090 *res = topdiv->get_page_number();
1091 return 1;
1094 class next_page_number_reg
1095 : public reg
1097 public:
1098 const char *get_string();
1101 const char *next_page_number_reg::get_string()
1103 return i_to_a(topdiv->get_next_page_number());
1106 class page_ejecting_reg
1107 : public reg
1109 public:
1110 const char *get_string();
1113 const char *page_ejecting_reg::get_string()
1115 return i_to_a(topdiv->get_ejecting());
1118 class constant_vunits_reg
1119 : public reg
1121 vunits *p;
1123 public:
1124 constant_vunits_reg(vunits *);
1125 const char *get_string();
1128 constant_vunits_reg::constant_vunits_reg(vunits *q) : p(q)
1132 const char *constant_vunits_reg::get_string()
1134 return i_to_a(p->to_units());
1137 class nl_reg
1138 : public variable_reg
1140 public:
1141 nl_reg();
1142 void set_value(units);
1145 nl_reg::nl_reg() : variable_reg(&nl_reg_contents)
1149 void nl_reg::set_value(units n)
1151 variable_reg::set_value(n);
1152 // Setting nl to a negative value when the vertical position in
1153 // the top-level diversion is 0 undoes the top of page transition,
1154 // so that the header macro will be called as if the top of page
1155 // transition hasn't happened. This is used by Larry Wall's
1156 // wrapman program. Setting before_first_page to 2 rather than 1,
1157 // tells top_level_diversion::begin_page not to call
1158 // output_file::begin_page again.
1159 if (n < 0 && topdiv->get_vertical_position() == V0)
1160 topdiv->before_first_page = 2;
1163 class no_space_mode_reg
1164 : public reg
1166 public:
1167 int get_value(units *);
1168 const char *get_string();
1171 int no_space_mode_reg::get_value(units *val)
1173 *val = curdiv->no_space_mode;
1174 return 1;
1177 const char *no_space_mode_reg::get_string()
1179 return curdiv->no_space_mode ? "1" : "0";
1182 void init_div_requests()
1184 init_request("box", box);
1185 init_request("boxa", box_append);
1186 init_request("bp", begin_page);
1187 init_request("ch", change_trap);
1188 init_request("da", divert_append);
1189 init_request("di", divert);
1190 init_request("dt", diversion_trap);
1191 init_request("fl", flush_output);
1192 init_request("mk", mark);
1193 init_request("ne", need_space);
1194 init_request("ns", no_space);
1195 init_request("os", output_saved_vertical_space);
1196 init_request("pl", page_length);
1197 init_request("pn", page_number);
1198 init_request("po", page_offset);
1199 init_request("ptr", print_traps);
1200 init_request("rs", restore_spacing);
1201 init_request("rt", return_request);
1202 init_request("sp", space_request);
1203 init_request("sv", save_vertical_space);
1204 init_request("vpt", vertical_position_traps);
1205 init_request("wh", when_request);
1206 number_reg_dictionary.define(".a",
1207 new constant_int_reg(&last_post_line_extra_space));
1208 number_reg_dictionary.define(".d", new vertical_position_reg);
1209 number_reg_dictionary.define(".h", new high_water_mark_reg);
1210 number_reg_dictionary.define(".ne",
1211 new constant_vunits_reg(&needed_space));
1212 number_reg_dictionary.define(".ns", new no_space_mode_reg);
1213 number_reg_dictionary.define(".o", new page_offset_reg);
1214 number_reg_dictionary.define(".p", new page_length_reg);
1215 number_reg_dictionary.define(".pe", new page_ejecting_reg);
1216 number_reg_dictionary.define(".pn", new next_page_number_reg);
1217 number_reg_dictionary.define(".t", new distance_to_next_trap_reg);
1218 number_reg_dictionary.define(".trunc",
1219 new constant_vunits_reg(&truncated_space));
1220 number_reg_dictionary.define(".vpt",
1221 new constant_int_reg(&vertical_position_traps_flag));
1222 number_reg_dictionary.define(".z", new diversion_name_reg);
1223 number_reg_dictionary.define("dl", new variable_reg(&dl_reg_contents));
1224 number_reg_dictionary.define("dn", new variable_reg(&dn_reg_contents));
1225 number_reg_dictionary.define("nl", new nl_reg);
1226 number_reg_dictionary.define("%", new page_number_reg);
1229 // s-it2-mode