Update to groff 1.19.2.
[dragonfly.git] / contrib / groff-1.19 / src / roff / troff / column.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000 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 2, or (at your option) any later
10 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 along
18 with groff; see the file COPYING.  If not, write to the Free Software
19 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
20
21 #ifdef COLUMN
22
23 #include "troff.h"
24 #include "symbol.h"
25 #include "dictionary.h"
26 #include "hvunits.h"
27 #include "env.h"
28 #include "request.h"
29 #include "node.h"
30 #include "token.h"
31 #include "div.h"
32 #include "reg.h"
33 #include "stringclass.h"
34
35 void output_file::vjustify(vunits, symbol)
36 {
37   // do nothing
38 }
39
40 struct justification_spec;
41 struct output_line;
42
43 class column : public output_file {
44 private:
45   output_file *out;
46   vunits bottom;
47   output_line *col;
48   output_line **tail;
49   void add_output_line(output_line *);
50   void begin_page(int pageno, vunits page_length);
51   void flush();
52   void print_line(hunits, vunits, node *, vunits, vunits);
53   void vjustify(vunits, symbol);
54   void transparent_char(unsigned char c);
55   void copy_file(hunits, vunits, const char *);
56   int is_printing();
57   void check_bottom();
58 public:
59   column();
60   ~column();
61   void start();
62   void output();
63   void justify(const justification_spec &);
64   void trim();
65   void reset();
66   vunits get_bottom();
67   vunits get_last_extra_space();
68   int is_active() { return out != 0; }
69 };
70
71 column *the_column = 0;
72
73 struct transparent_output_line;
74 struct vjustify_output_line;
75
76 class output_line {
77   output_line *next;
78 public:
79   output_line();
80   virtual ~output_line();
81   virtual void output(output_file *, vunits);
82   virtual transparent_output_line *as_transparent_output_line();
83   virtual vjustify_output_line *as_vjustify_output_line();
84   virtual vunits distance();
85   virtual vunits height();
86   virtual void reset();
87   virtual vunits extra_space(); // post line
88   friend class column;
89   friend class justification_spec;
90 };
91
92 class position_output_line : public output_line {
93   vunits dist;
94 public:
95   position_output_line(vunits);
96   vunits distance();
97 };
98   
99 class node_output_line : public position_output_line {
100   node *nd;
101   hunits page_offset;
102   vunits before;
103   vunits after;
104 public:
105   node_output_line(vunits, node *, hunits, vunits, vunits);
106   ~node_output_line();
107   void output(output_file *, vunits);
108   vunits height();
109   vunits extra_space();
110 };
111
112 class vjustify_output_line : public position_output_line {
113   vunits current;
114   symbol typ;
115 public:
116   vjustify_output_line(vunits dist, symbol);
117   vunits height();
118   vjustify_output_line *as_vjustify_output_line();
119   void vary(vunits amount);
120   void reset();
121   symbol type();
122 };
123
124 inline symbol vjustify_output_line::type()
125 {
126   return typ;
127 }
128
129 class copy_file_output_line : public position_output_line {
130   symbol filename;
131   hunits hpos;
132 public:
133   copy_file_output_line(vunits, const char *, hunits);
134   void output(output_file *, vunits);
135 };
136
137 class transparent_output_line : public output_line {
138   string buf;
139 public:
140   transparent_output_line();
141   void output(output_file *, vunits);
142   void append_char(unsigned char c);
143   transparent_output_line *as_transparent_output_line();
144 };
145
146 output_line::output_line() : next(0)
147 {
148 }
149
150 output_line::~output_line()
151 {
152 }
153
154 void output_line::reset()
155 {
156 }
157
158 transparent_output_line *output_line::as_transparent_output_line()
159 {
160   return 0;
161 }
162
163 vjustify_output_line *output_line::as_vjustify_output_line()
164 {
165   return 0;
166 }
167
168 void output_line::output(output_file *, vunits)
169 {
170 }
171
172 vunits output_line::distance()
173 {
174   return V0;
175 }
176
177 vunits output_line::height()
178 {
179   return V0;
180 }
181
182 vunits output_line::extra_space()
183 {
184   return V0;
185 }
186
187 position_output_line::position_output_line(vunits d)
188 : dist(d)
189 {
190 }
191
192 vunits position_output_line::distance()
193 {
194   return dist;
195 }
196
197 node_output_line::node_output_line(vunits d, node *n, hunits po, vunits b, vunits a)
198 : position_output_line(d), nd(n), page_offset(po), before(b), after(a)
199 {
200 }
201
202 node_output_line::~node_output_line()
203 {
204   delete_node_list(nd);
205 }
206
207 void node_output_line::output(output_file *out, vunits pos)
208 {
209   out->print_line(page_offset, pos, nd, before, after);
210   nd = 0;
211 }
212
213 vunits node_output_line::height()
214 {
215   return after;
216 }
217
218 vunits node_output_line::extra_space()
219 {
220   return after;
221 }
222
223 vjustify_output_line::vjustify_output_line(vunits d, symbol t)
224 : position_output_line(d), typ(t)
225 {
226 }
227
228 void vjustify_output_line::reset()
229 {
230   current = V0;
231 }
232
233 vunits vjustify_output_line::height()
234 {
235   return current;
236 }
237
238 vjustify_output_line *vjustify_output_line::as_vjustify_output_line()
239 {
240   return this;
241 }
242
243 inline void vjustify_output_line::vary(vunits amount)
244 {
245   current += amount;
246 }
247
248 transparent_output_line::transparent_output_line()
249 {
250 }
251
252 transparent_output_line *transparent_output_line::as_transparent_output_line()
253 {
254   return this;
255 }
256
257 void transparent_output_line::append_char(unsigned char c)
258 {
259   assert(c != 0);
260   buf += c;
261 }
262
263 void transparent_output_line::output(output_file *out, vunits)
264 {
265   int len = buf.length();
266   for (int i = 0; i < len; i++)
267     out->transparent_char(buf[i]);
268 }
269
270 copy_file_output_line::copy_file_output_line(vunits d, const char *f, hunits h)
271 : position_output_line(d), hpos(h), filename(f)
272 {
273 }
274
275 void copy_file_output_line::output(output_file *out, vunits pos)
276 {
277   out->copy_file(hpos, pos, filename.contents());
278 }
279
280 column::column()
281 : bottom(V0), col(0), tail(&col), out(0)
282 {
283 }
284
285 column::~column()
286 {
287   assert(out != 0);
288   error("automatically outputting column before exiting");
289   output();
290   delete the_output;
291 }
292
293 void column::start()
294 {
295   assert(out == 0);
296   if (!the_output)
297     init_output();
298   assert(the_output != 0);
299   out = the_output;
300   the_output = this;
301 }
302
303 void column::begin_page(int pageno, vunits page_length)
304 {
305   assert(out != 0);
306   if (col) {
307     error("automatically outputting column before beginning next page");
308     output();
309     the_output->begin_page(pageno, page_length);
310   }
311   else
312     out->begin_page(pageno, page_length);
313     
314 }
315
316 void column::flush()
317 {
318   assert(out != 0);
319   out->flush();
320 }
321
322 int column::is_printing()
323 {
324   assert(out != 0);
325   return out->is_printing();
326 }
327
328 vunits column::get_bottom()
329 {
330   return bottom;
331 }
332
333 void column::add_output_line(output_line *ln)
334 {
335   *tail = ln;
336   bottom += ln->distance();
337   bottom += ln->height();
338   ln->next = 0;
339   tail = &(*tail)->next;
340 }
341
342 void column::print_line(hunits page_offset, vunits pos, node *nd,
343                         vunits before, vunits after)
344 {
345   assert(out != 0);
346   add_output_line(new node_output_line(pos - bottom, nd, page_offset, before, after));
347 }
348
349 void column::vjustify(vunits pos, symbol typ)
350 {
351   assert(out != 0);
352   add_output_line(new vjustify_output_line(pos - bottom, typ));
353 }
354
355 void column::transparent_char(unsigned char c)
356 {
357   assert(out != 0);
358   transparent_output_line *tl = 0;
359   if (*tail)
360     tl = (*tail)->as_transparent_output_line();
361   if (!tl) {
362     tl = new transparent_output_line;
363     add_output_line(tl);
364   }
365   tl->append_char(c);
366 }
367
368 void column::copy_file(hunits page_offset, vunits pos, const char *filename)
369 {
370   assert(out != 0);
371   add_output_line(new copy_file_output_line(pos - bottom, filename, page_offset));
372 }
373
374 void column::trim()
375 {
376   output_line **spp = 0;
377   for (output_line **pp = &col; *pp; pp = &(*pp)->next)
378     if ((*pp)->as_vjustify_output_line() == 0)
379       spp = 0;
380     else if (!spp)
381       spp = pp;
382   if (spp) {
383     output_line *ln = *spp;
384     *spp = 0;
385     tail = spp;
386     while (ln) {
387       output_line *tem = ln->next;
388       bottom -= ln->distance();
389       bottom -= ln->height();
390       delete ln;
391       ln = tem;
392     }
393   }
394 }
395
396 void column::reset()
397 {
398   bottom = V0;
399   for (output_line *ln = col; ln; ln = ln->next) {
400     bottom += ln->distance();
401     ln->reset();
402     bottom += ln->height();
403   }
404 }
405
406 void column::check_bottom()
407 {
408   vunits b;
409   for (output_line *ln = col; ln; ln = ln->next) {
410     b += ln->distance();
411     b += ln->height();
412   }
413   assert(b == bottom);
414 }
415
416 void column::output()
417 {
418   assert(out != 0);
419   vunits vpos(V0);
420   output_line *ln = col;
421   while (ln) {
422     vpos += ln->distance();
423     ln->output(out, vpos);
424     vpos += ln->height();
425     output_line *tem = ln->next;
426     delete ln;
427     ln = tem;
428   }
429   tail = &col;
430   bottom = V0;
431   col = 0;
432   the_output = out;
433   out = 0;
434 }
435
436 vunits column::get_last_extra_space()
437 {
438   if (!col)
439     return V0;
440   for (output_line *p = col; p->next; p = p->next)
441     ;
442   return p->extra_space();
443 }
444
445 class justification_spec {
446   vunits height;
447   symbol *type;
448   vunits *amount;
449   int n;
450   int maxn;
451 public:
452   justification_spec(vunits);
453   ~justification_spec();
454   void append(symbol t, vunits v);
455   void justify(output_line *, vunits *bottomp) const;
456 };
457
458 justification_spec::justification_spec(vunits h)
459 : height(h), n(0), maxn(10)
460 {
461   type = new symbol[maxn];
462   amount = new vunits[maxn];
463 }
464
465 justification_spec::~justification_spec()
466 {
467   a_delete type;
468   a_delete amount;
469 }
470
471 void justification_spec::append(symbol t, vunits v)
472 {
473   if (v <= V0) {
474     if (v < V0)
475       warning(WARN_RANGE,
476               "maximum space for vertical justification must not be negative");
477     else
478       warning(WARN_RANGE,
479               "maximum space for vertical justification must not be zero");
480     return;
481   }
482   if (n >= maxn) {
483     maxn *= 2;
484     symbol *old_type = type;
485     type = new symbol[maxn];
486     int i;
487     for (i = 0; i < n; i++)
488       type[i] = old_type[i];
489     a_delete old_type;
490     vunits *old_amount = amount;
491     amount = new vunits[maxn];
492     for (i = 0; i < n; i++)
493       amount[i] = old_amount[i];
494     a_delete old_amount;
495   }
496   assert(n < maxn);
497   type[n] = t;
498   amount[n] = v;
499   n++;
500 }
501
502 void justification_spec::justify(output_line *col, vunits *bottomp) const
503 {
504   if (*bottomp >= height)
505     return;
506   vunits total;
507   output_line *p;
508   for (p = col; p; p = p->next) {
509     vjustify_output_line *sp = p->as_vjustify_output_line();
510     if (sp) {
511       symbol t = sp->type();
512       for (int i = 0; i < n; i++) {
513         if (t == type[i])
514           total += amount[i];
515       }
516     }
517   }
518   vunits gap = height - *bottomp;
519   for (p = col; p; p = p->next) {
520     vjustify_output_line *sp = p->as_vjustify_output_line();
521     if (sp) {
522       symbol t = sp->type();
523       for (int i = 0; i < n; i++) {
524         if (t == type[i]) {
525           if (total <= gap) {
526             sp->vary(amount[i]);
527             gap -= amount[i];
528           }
529           else {
530             // gap < total
531             vunits v = scale(amount[i], gap, total);
532             sp->vary(v);
533             gap -= v;
534           }
535           total -= amount[i];
536         }
537       }
538     }
539   }
540   assert(total == V0);
541   *bottomp = height - gap;
542 }
543   
544 void column::justify(const justification_spec &js)
545 {
546   check_bottom();
547   js.justify(col, &bottom);
548   check_bottom();
549 }
550
551 void column_justify()
552 {
553   vunits height;
554   if (!the_column->is_active())
555     error("can't justify column - column not active");
556   else if (get_vunits(&height, 'v')) {
557     justification_spec js(height);
558     symbol nm = get_long_name(1);
559     if (!nm.is_null()) {
560       vunits v;
561       if (get_vunits(&v, 'v')) {
562         js.append(nm, v);
563         int err = 0;
564         while (has_arg()) {
565           nm = get_long_name(1);
566           if (nm.is_null()) {
567             err = 1;
568             break;
569           }
570           if (!get_vunits(&v, 'v')) {
571             err = 1;
572             break;
573           }
574           js.append(nm, v);
575         }
576         if (!err)
577           the_column->justify(js);
578       }
579     }
580   }
581   skip_line();
582 }
583
584 void column_start()
585 {
586   if (the_column->is_active())
587     error("can't start column - column already active");
588   else
589     the_column->start();
590   skip_line();
591 }
592
593 void column_output()
594 {
595   if (!the_column->is_active())
596     error("can't output column - column not active");
597   else
598     the_column->output();
599   skip_line();
600 }
601
602 void column_trim()
603 {
604   if (!the_column->is_active())
605     error("can't trim column - column not active");
606   else
607     the_column->trim();
608   skip_line();
609 }
610
611 void column_reset()
612 {
613   if (!the_column->is_active())
614     error("can't reset column - column not active");
615   else
616     the_column->reset();
617   skip_line();
618 }
619
620 class column_bottom_reg : public reg {
621 public:
622   const char *get_string();
623 };
624
625 const char *column_bottom_reg::get_string()
626 {
627   return i_to_a(the_column->get_bottom().to_units());
628 }
629
630 class column_extra_space_reg : public reg {
631 public:
632   const char *get_string();
633 };
634
635 const char *column_extra_space_reg::get_string()
636 {
637   return i_to_a(the_column->get_last_extra_space().to_units());
638 }
639
640 class column_active_reg : public reg {
641 public:
642   const char *get_string();
643 };
644
645 const char *column_active_reg::get_string()
646 {
647   return the_column->is_active() ? "1" : "0";
648 }
649
650 static int no_vjustify_mode = 0;
651
652 class vjustify_node : public node {
653   symbol typ;
654 public:
655   vjustify_node(symbol);
656   int reread(int *);
657   const char *type();
658   int same(node *);
659   node *copy();
660 };
661
662 vjustify_node::vjustify_node(symbol t)
663 : typ(t)
664 {
665 }
666
667 node *vjustify_node::copy()
668 {
669   return new vjustify_node(typ, div_nest_level);
670 }
671
672 const char *vjustify_node::type()
673 {
674   return "vjustify_node";
675 }
676
677 int vjustify_node::same(node *nd)
678 {
679   return typ == ((vjustify_node *)nd)->typ;
680 }
681
682 int vjustify_node::reread(int *bolp)
683 {
684   curdiv->vjustify(typ);
685   *bolp = 1;
686   return 1;
687 }
688
689 void macro_diversion::vjustify(symbol type)
690 {
691   if (!no_vjustify_mode)
692     mac->append(new vjustify_node(type));
693 }
694
695 void top_level_diversion::vjustify(symbol type)
696 {
697   if (no_space_mode || no_vjustify_mode)
698     return;
699   assert(first_page_begun);     // I'm not sure about this.
700   the_output->vjustify(vertical_position, type);
701 }
702
703 void no_vjustify()
704 {
705   skip_line();
706   no_vjustify_mode = 1;
707 }
708
709 void restore_vjustify()
710 {
711   skip_line();
712   no_vjustify_mode = 0;
713 }
714
715 void init_column_requests()
716 {
717   the_column = new column;
718   init_request("cols", column_start);
719   init_request("colo", column_output);
720   init_request("colj", column_justify);
721   init_request("colr", column_reset);
722   init_request("colt", column_trim);
723   init_request("nvj", no_vjustify);
724   init_request("rvj", restore_vjustify);
725   number_reg_dictionary.define(".colb", new column_bottom_reg);
726   number_reg_dictionary.define(".colx", new column_extra_space_reg);
727   number_reg_dictionary.define(".cola", new column_active_reg);
728   number_reg_dictionary.define(".nvj",
729                                new constant_int_reg(&no_vjustify_mode));
730 }
731
732 #endif /* COLUMN */