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