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