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