xref: /netbsd-src/external/gpl2/groff/dist/src/roff/troff/div.cpp (revision 89a07cf815a29524268025a1139fac4c5190f765)
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