386BSD 0.0 development
[unix-history] / usr / src / usr.bin / groff / troff / column.cc
CommitLineData
6b8906f2
WJ
1// -*- C++ -*-
2/* Copyright (C) 1990, 1991 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.uucp)
4
5This file is part of groff.
6
7groff is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 1, or (at your option) any later
10version.
11
12groff is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License along
18with groff; see the file LICENSE. If not, write to the Free Software
19Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20
21#ifdef COLUMN
22
23#include "groff.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
35void output_file::vjustify(vunits, symbol)
36{
37 // do nothing
38}
39
40struct justification_spec;
41struct output_line;
42
43class column : public output_file {
44private:
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();
58public:
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
71column *the_column = 0;
72
73struct transparent_output_line;
74struct vjustify_output_line;
75
76class output_line {
77 output_line *next;
78public:
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
92class position_output_line : public output_line {
93 vunits dist;
94public:
95 position_output_line(vunits);
96 vunits distance();
97};
98
99class node_output_line : public position_output_line {
100 node *nd;
101 hunits page_offset;
102 vunits before;
103 vunits after;
104public:
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
112class vjustify_output_line : public position_output_line {
113 vunits current;
114 symbol typ;
115public:
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
124inline symbol vjustify_output_line::type()
125{
126 return typ;
127}
128
129class copy_file_output_line : public position_output_line {
130 symbol filename;
131 hunits hpos;
132public:
133 copy_file_output_line(vunits, const char *, hunits);
134 void output(output_file *, vunits);
135};
136
137class transparent_output_line : public output_line {
138 string buf;
139public:
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
146output_line::output_line() : next(0)
147{
148}
149
150output_line::~output_line()
151{
152}
153
154void output_line::reset()
155{
156}
157
158transparent_output_line *output_line::as_transparent_output_line()
159{
160 return 0;
161}
162
163vjustify_output_line *output_line::as_vjustify_output_line()
164{
165 return 0;
166}
167
168void output_line::output(output_file *, vunits)
169{
170}
171
172vunits output_line::distance()
173{
174 return V0;
175}
176
177vunits output_line::height()
178{
179 return V0;
180}
181
182vunits output_line::extra_space()
183{
184 return V0;
185}
186
187position_output_line::position_output_line(vunits d)
188: dist(d)
189{
190}
191
192vunits position_output_line::distance()
193{
194 return dist;
195}
196
197node_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
202node_output_line::~node_output_line()
203{
204 delete_node_list(nd);
205}
206
207void 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
213vunits node_output_line::height()
214{
215 return after;
216}
217
218vunits node_output_line::extra_space()
219{
220 return after;
221}
222
223vjustify_output_line::vjustify_output_line(vunits d, symbol t)
224: position_output_line(d), typ(t)
225{
226}
227
228void vjustify_output_line::reset()
229{
230 current = V0;
231}
232
233vunits vjustify_output_line::height()
234{
235 return current;
236}
237
238vjustify_output_line *vjustify_output_line::as_vjustify_output_line()
239{
240 return this;
241}
242
243inline void vjustify_output_line::vary(vunits amount)
244{
245 current += amount;
246}
247
248transparent_output_line::transparent_output_line()
249{
250}
251
252transparent_output_line *transparent_output_line::as_transparent_output_line()
253{
254 return this;
255}
256
257void transparent_output_line::append_char(unsigned char c)
258{
259 assert(c != 0);
260 buf += c;
261}
262
263void 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
270copy_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
275void copy_file_output_line::output(output_file *out, vunits pos)
276{
277 out->copy_file(hpos, pos, filename.contents());
278}
279
280column::column()
281: bottom(V0), col(0), tail(&col), out(0)
282{
283}
284
285column::~column()
286{
287 assert(out != 0);
288 error("automatically outputting column before exiting");
289 output();
290 delete the_output;
291}
292
293void 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
303void 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
316void column::flush()
317{
318 assert(out != 0);
319 out->flush();
320}
321
322int column::is_printing()
323{
324 assert(out != 0);
325 return out->is_printing();
326}
327
328vunits column::get_bottom()
329{
330 return bottom;
331}
332
333void 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
342void 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
349void column::vjustify(vunits pos, symbol typ)
350{
351 assert(out != 0);
352 add_output_line(new vjustify_output_line(pos - bottom, typ));
353}
354
355void 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
368void 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
374void 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
396void 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
406void 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
416void 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
436vunits 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
445class justification_spec {
446 vunits height;
447 symbol *type;
448 vunits *amount;
449 int n;
450 int maxn;
451public:
452 justification_spec(vunits);
453 ~justification_spec();
454 void append(symbol t, vunits v);
455 void justify(output_line *, vunits *bottomp) const;
456};
457
458justification_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
465justification_spec::~justification_spec()
466{
467 delete type;
468 delete amount;
469}
470
471void 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 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 delete old_amount;
495 }
496 assert(n < maxn);
497 type[n] = t;
498 amount[n] = v;
499 n++;
500}
501
502void 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
544void column::justify(const justification_spec &js)
545{
546 check_bottom();
547 js.justify(col, &bottom);
548 check_bottom();
549}
550
551void 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
584void 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
593void 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
602void 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
611void 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
620class column_bottom_reg : public reg {
621public:
622 const char *get_string();
623};
624
625const char *column_bottom_reg::get_string()
626{
627 return itoa(the_column->get_bottom().to_units());
628}
629
630class column_extra_space_reg : public reg {
631public:
632 const char *get_string();
633};
634
635const char *column_extra_space_reg::get_string()
636{
637 return itoa(the_column->get_last_extra_space().to_units());
638}
639
640class column_active_reg : public reg {
641public:
642 const char *get_string();
643};
644
645const char *column_active_reg::get_string()
646{
647 return the_column->is_active() ? "1" : "0";
648}
649
650static int no_vjustify_mode = 0;
651
652class vjustify_node : public node {
653 symbol typ;
654public:
655 vjustify_node(symbol);
656 int reread(int *);
657 const char *type();
658 int same(node *);
659 node *copy();
660};
661
662vjustify_node::vjustify_node(symbol t)
663: typ(t)
664{
665}
666
667node *vjustify_node::copy()
668{
669 return new vjustify_node(typ);
670}
671
672const char *vjustify_node::type()
673{
674 return "vjustify_node";
675}
676
677int vjustify_node::same(node *nd)
678{
679 return typ == ((vjustify_node *)nd)->typ;
680}
681
682int vjustify_node::reread(int *bolp)
683{
684 curdiv->vjustify(typ);
685 *bolp = 1;
686 return 1;
687}
688
689void macro_diversion::vjustify(symbol type)
690{
691 if (!no_vjustify_mode)
692 mac->append(new vjustify_node(type));
693}
694
695void 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
703void no_vjustify()
704{
705 skip_line();
706 no_vjustify_mode = 1;
707}
708
709void restore_vjustify()
710{
711 skip_line();
712 no_vjustify_mode = 0;
713}
714
715void 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 */