1 /* Id: tbl_term.c,v 1.25 2013/05/31 21:37:17 schwarze Exp */
3 * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2011, 2012 Ingo Schwarze <schwarze@openbsd.org>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
31 static size_t term_tbl_len(size_t, void *);
32 static size_t term_tbl_strlen(const char *, void *);
33 static void tbl_char(struct termp
*, char, size_t);
34 static void tbl_data(struct termp
*, const struct tbl_opts
*,
35 const struct tbl_dat
*,
36 const struct roffcol
*);
37 static size_t tbl_rulewidth(struct termp
*, const struct tbl_head
*);
38 static void tbl_hframe(struct termp
*, const struct tbl_span
*, int);
39 static void tbl_literal(struct termp
*, const struct tbl_dat
*,
40 const struct roffcol
*);
41 static void tbl_number(struct termp
*, const struct tbl_opts
*,
42 const struct tbl_dat
*,
43 const struct roffcol
*);
44 static void tbl_hrule(struct termp
*, const struct tbl_span
*);
45 static void tbl_vrule(struct termp
*, const struct tbl_head
*);
49 term_tbl_strlen(const char *p
, void *arg
)
52 return(term_strlen((const struct termp
*)arg
, p
));
56 term_tbl_len(size_t sz
, void *arg
)
59 return(term_len((const struct termp
*)arg
, sz
));
63 term_tbl(struct termp
*tp
, const struct tbl_span
*sp
)
65 const struct tbl_head
*hp
;
66 const struct tbl_dat
*dp
;
69 size_t rmargin
, maxrmargin
;
71 rmargin
= tp
->rmargin
;
72 maxrmargin
= tp
->maxrmargin
;
74 tp
->rmargin
= tp
->maxrmargin
= TERM_MAXMARGIN
;
76 /* Inhibit printing of spaces: we do padding ourselves. */
78 tp
->flags
|= TERMP_NONOSPACE
;
79 tp
->flags
|= TERMP_NOSPACE
;
82 * The first time we're invoked for a given table block,
83 * calculate the table widths and decimal positions.
86 if (TBL_SPAN_FIRST
& sp
->flags
) {
89 tp
->tbl
.len
= term_tbl_len
;
90 tp
->tbl
.slen
= term_tbl_strlen
;
93 tblcalc(&tp
->tbl
, sp
);
96 /* Horizontal frame at the start of boxed tables. */
98 if (TBL_SPAN_FIRST
& sp
->flags
) {
99 if (TBL_OPT_DBOX
& sp
->opts
->opts
)
100 tbl_hframe(tp
, sp
, 1);
101 if (TBL_OPT_DBOX
& sp
->opts
->opts
||
102 TBL_OPT_BOX
& sp
->opts
->opts
)
103 tbl_hframe(tp
, sp
, 0);
106 /* Vertical frame at the start of each row. */
108 if (TBL_OPT_BOX
& sp
->opts
->opts
|| TBL_OPT_DBOX
& sp
->opts
->opts
)
109 term_word(tp
, TBL_SPAN_HORIZ
== sp
->pos
||
110 TBL_SPAN_DHORIZ
== sp
->pos
? "+" : "|");
113 * Now print the actual data itself depending on the span type.
114 * Spanner spans get a horizontal rule; data spanners have their
115 * data printed by matching data to header.
119 case (TBL_SPAN_HORIZ
):
121 case (TBL_SPAN_DHORIZ
):
124 case (TBL_SPAN_DATA
):
125 /* Iterate over template headers. */
128 for (hp
= sp
->head
; hp
; hp
= hp
->next
) {
131 * If the current data header is invoked during
132 * a spanner ("spans" > 0), don't emit anything
139 /* Separate columns. */
141 if (NULL
!= hp
->prev
)
144 col
= &tp
->tbl
.cols
[hp
->ident
];
145 tbl_data(tp
, sp
->opts
, dp
, col
);
148 * Go to the next data cell and assign the
149 * number of subsequent spans, if applicable.
160 /* Vertical frame at the end of each row. */
162 if (TBL_OPT_BOX
& sp
->opts
->opts
|| TBL_OPT_DBOX
& sp
->opts
->opts
)
163 term_word(tp
, TBL_SPAN_HORIZ
== sp
->pos
||
164 TBL_SPAN_DHORIZ
== sp
->pos
? "+" : " |");
168 * If we're the last row, clean up after ourselves: clear the
169 * existing table configuration and set it to NULL.
172 if (TBL_SPAN_LAST
& sp
->flags
) {
173 if (TBL_OPT_DBOX
& sp
->opts
->opts
||
174 TBL_OPT_BOX
& sp
->opts
->opts
) {
175 tbl_hframe(tp
, sp
, 0);
178 if (TBL_OPT_DBOX
& sp
->opts
->opts
) {
179 tbl_hframe(tp
, sp
, 1);
182 assert(tp
->tbl
.cols
);
187 tp
->flags
&= ~TERMP_NONOSPACE
;
188 tp
->rmargin
= rmargin
;
189 tp
->maxrmargin
= maxrmargin
;
194 * Horizontal rules extend across the entire table.
195 * Calculate the width by iterating over columns.
198 tbl_rulewidth(struct termp
*tp
, const struct tbl_head
*hp
)
202 width
= tp
->tbl
.cols
[hp
->ident
].width
;
204 /* Account for leading blanks. */
206 width
+= 2 - hp
->vert
;
208 /* Account for trailing blank. */
215 * Rules inside the table can be single or double
216 * and have crossings with vertical rules marked with pluses.
219 tbl_hrule(struct termp
*tp
, const struct tbl_span
*sp
)
221 const struct tbl_head
*hp
;
225 if (TBL_SPAN_DHORIZ
== sp
->pos
)
228 for (hp
= sp
->head
; hp
; hp
= hp
->next
) {
229 if (hp
->prev
&& hp
->vert
)
230 tbl_char(tp
, '+', hp
->vert
);
231 tbl_char(tp
, c
, tbl_rulewidth(tp
, hp
));
236 * Rules above and below the table are always single
237 * and have an additional plus at the beginning and end.
238 * For double frames, this function is called twice,
239 * and the outer one does not have crossings.
242 tbl_hframe(struct termp
*tp
, const struct tbl_span
*sp
, int outer
)
244 const struct tbl_head
*hp
;
247 for (hp
= sp
->head
; hp
; hp
= hp
->next
) {
248 if (hp
->prev
&& hp
->vert
)
249 tbl_char(tp
, (outer
? '-' : '+'), hp
->vert
);
250 tbl_char(tp
, '-', tbl_rulewidth(tp
, hp
));
257 tbl_data(struct termp
*tp
, const struct tbl_opts
*opts
,
258 const struct tbl_dat
*dp
,
259 const struct roffcol
*col
)
263 tbl_char(tp
, ASCII_NBRSP
, col
->width
);
269 case (TBL_DATA_NONE
):
270 tbl_char(tp
, ASCII_NBRSP
, col
->width
);
272 case (TBL_DATA_HORIZ
):
274 case (TBL_DATA_NHORIZ
):
275 tbl_char(tp
, '-', col
->width
);
277 case (TBL_DATA_NDHORIZ
):
279 case (TBL_DATA_DHORIZ
):
280 tbl_char(tp
, '=', col
->width
);
286 switch (dp
->layout
->pos
) {
287 case (TBL_CELL_HORIZ
):
288 tbl_char(tp
, '-', col
->width
);
290 case (TBL_CELL_DHORIZ
):
291 tbl_char(tp
, '=', col
->width
);
293 case (TBL_CELL_LONG
):
295 case (TBL_CELL_CENTRE
):
297 case (TBL_CELL_LEFT
):
299 case (TBL_CELL_RIGHT
):
300 tbl_literal(tp
, dp
, col
);
302 case (TBL_CELL_NUMBER
):
303 tbl_number(tp
, opts
, dp
, col
);
305 case (TBL_CELL_DOWN
):
306 tbl_char(tp
, ASCII_NBRSP
, col
->width
);
315 tbl_vrule(struct termp
*tp
, const struct tbl_head
*hp
)
318 tbl_char(tp
, ASCII_NBRSP
, 1);
320 tbl_char(tp
, '|', hp
->vert
);
322 tbl_char(tp
, ASCII_NBRSP
, 2 - hp
->vert
);
326 tbl_char(struct termp
*tp
, char c
, size_t len
)
334 sz
= term_strlen(tp
, cp
);
336 for (i
= 0; i
< len
; i
+= sz
)
341 tbl_literal(struct termp
*tp
, const struct tbl_dat
*dp
,
342 const struct roffcol
*col
)
345 size_t width
, len
, padl
, padr
;
349 len
= term_strlen(tp
, dp
->string
);
351 hp
= dp
->layout
->head
->next
;
353 for (spans
= dp
->spans
; spans
--; hp
= hp
->next
)
354 width
+= tp
->tbl
.cols
[hp
->ident
].width
+ 3;
356 padr
= width
> len
? width
- len
: 0;
359 switch (dp
->layout
->pos
) {
360 case (TBL_CELL_LONG
):
361 padl
= term_len(tp
, 1);
362 padr
= padr
> padl
? padr
- padl
: 0;
364 case (TBL_CELL_CENTRE
):
370 case (TBL_CELL_RIGHT
):
378 tbl_char(tp
, ASCII_NBRSP
, padl
);
379 term_word(tp
, dp
->string
);
380 tbl_char(tp
, ASCII_NBRSP
, padr
);
384 tbl_number(struct termp
*tp
, const struct tbl_opts
*opts
,
385 const struct tbl_dat
*dp
,
386 const struct roffcol
*col
)
390 size_t sz
, psz
, ssz
, d
, padl
;
394 * See calc_data_number(). Left-pad by taking the offset of our
395 * and the maximum decimal; right-pad by the remaining amount.
400 sz
= term_strlen(tp
, dp
->string
);
402 buf
[0] = opts
->decimal
;
405 psz
= term_strlen(tp
, buf
);
407 if (NULL
!= (cp
= strrchr(dp
->string
, opts
->decimal
))) {
409 for (ssz
= 0, i
= 0; cp
!= &dp
->string
[i
]; i
++) {
410 buf
[0] = dp
->string
[i
];
411 ssz
+= term_strlen(tp
, buf
);
417 padl
= col
->decimal
- d
;
419 tbl_char(tp
, ASCII_NBRSP
, padl
);
420 term_word(tp
, dp
->string
);
421 if (col
->width
> sz
+ padl
)
422 tbl_char(tp
, ASCII_NBRSP
, col
->width
- sz
- padl
);