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