1 /* $Vendor-Id: tbl_term.c,v 1.21 2011/09/20 23:05:49 schwarze Exp $ */
3 * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2011 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
*,
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
*,
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
->tbl
->opts
)
100 tbl_hframe(tp
, sp
, 1);
101 if (TBL_OPT_DBOX
& sp
->tbl
->opts
||
102 TBL_OPT_BOX
& sp
->tbl
->opts
)
103 tbl_hframe(tp
, sp
, 0);
106 /* Vertical frame at the start of each row. */
108 if (TBL_OPT_BOX
& sp
->tbl
->opts
|| TBL_OPT_DBOX
& sp
->tbl
->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
) {
130 * If the current data header is invoked during
131 * a spanner ("spans" > 0), don't emit anything
135 case (TBL_HEAD_VERT
):
137 case (TBL_HEAD_DVERT
):
141 case (TBL_HEAD_DATA
):
149 * All cells get a leading blank, except the
150 * first one and those after double rulers.
153 if (hp
->prev
&& TBL_HEAD_DVERT
!= hp
->prev
->pos
)
154 tbl_char(tp
, ASCII_NBRSP
, 1);
156 col
= &tp
->tbl
.cols
[hp
->ident
];
157 tbl_data(tp
, sp
->tbl
, dp
, col
);
159 /* No trailing blanks. */
161 if (NULL
== hp
->next
)
165 * Add another blank between cells,
166 * or two when there is no vertical ruler.
169 tbl_char(tp
, ASCII_NBRSP
,
170 TBL_HEAD_VERT
== hp
->next
->pos
||
171 TBL_HEAD_DVERT
== hp
->next
->pos
? 1 : 2);
174 * Go to the next data cell and assign the
175 * number of subsequent spans, if applicable.
186 /* Vertical frame at the end of each row. */
188 if (TBL_OPT_BOX
& sp
->tbl
->opts
|| TBL_OPT_DBOX
& sp
->tbl
->opts
)
189 term_word(tp
, TBL_SPAN_HORIZ
== sp
->pos
||
190 TBL_SPAN_DHORIZ
== sp
->pos
? "+" : " |");
194 * If we're the last row, clean up after ourselves: clear the
195 * existing table configuration and set it to NULL.
198 if (TBL_SPAN_LAST
& sp
->flags
) {
199 if (TBL_OPT_DBOX
& sp
->tbl
->opts
||
200 TBL_OPT_BOX
& sp
->tbl
->opts
)
201 tbl_hframe(tp
, sp
, 0);
202 if (TBL_OPT_DBOX
& sp
->tbl
->opts
)
203 tbl_hframe(tp
, sp
, 1);
204 assert(tp
->tbl
.cols
);
209 tp
->flags
&= ~TERMP_NONOSPACE
;
210 tp
->rmargin
= rmargin
;
211 tp
->maxrmargin
= maxrmargin
;
216 * Horizontal rules extend across the entire table.
217 * Calculate the width by iterating over columns.
220 tbl_rulewidth(struct termp
*tp
, const struct tbl_head
*hp
)
224 width
= tp
->tbl
.cols
[hp
->ident
].width
;
225 if (TBL_HEAD_DATA
== hp
->pos
) {
226 /* Account for leading blanks. */
227 if (hp
->prev
&& TBL_HEAD_DVERT
!= hp
->prev
->pos
)
229 /* Account for trailing blanks. */
232 TBL_HEAD_VERT
!= hp
->next
->pos
&&
233 TBL_HEAD_DVERT
!= hp
->next
->pos
)
240 * Rules inside the table can be single or double
241 * and have crossings with vertical rules marked with pluses.
244 tbl_hrule(struct termp
*tp
, const struct tbl_span
*sp
)
246 const struct tbl_head
*hp
;
250 if (TBL_SPAN_DHORIZ
== sp
->pos
)
253 for (hp
= sp
->head
; hp
; hp
= hp
->next
)
255 TBL_HEAD_DATA
== hp
->pos
? c
: '+',
256 tbl_rulewidth(tp
, hp
));
260 * Rules above and below the table are always single
261 * and have an additional plus at the beginning and end.
262 * For double frames, this function is called twice,
263 * and the outer one does not have crossings.
266 tbl_hframe(struct termp
*tp
, const struct tbl_span
*sp
, int outer
)
268 const struct tbl_head
*hp
;
271 for (hp
= sp
->head
; hp
; hp
= hp
->next
)
273 outer
|| TBL_HEAD_DATA
== hp
->pos
? '-' : '+',
274 tbl_rulewidth(tp
, hp
));
280 tbl_data(struct termp
*tp
, const struct tbl
*tbl
,
281 const struct tbl_dat
*dp
,
282 const struct roffcol
*col
)
286 tbl_char(tp
, ASCII_NBRSP
, col
->width
);
292 case (TBL_DATA_NONE
):
293 tbl_char(tp
, ASCII_NBRSP
, col
->width
);
295 case (TBL_DATA_HORIZ
):
297 case (TBL_DATA_NHORIZ
):
298 tbl_char(tp
, '-', col
->width
);
300 case (TBL_DATA_NDHORIZ
):
302 case (TBL_DATA_DHORIZ
):
303 tbl_char(tp
, '=', col
->width
);
309 switch (dp
->layout
->pos
) {
310 case (TBL_CELL_HORIZ
):
311 tbl_char(tp
, '-', col
->width
);
313 case (TBL_CELL_DHORIZ
):
314 tbl_char(tp
, '=', col
->width
);
316 case (TBL_CELL_LONG
):
318 case (TBL_CELL_CENTRE
):
320 case (TBL_CELL_LEFT
):
322 case (TBL_CELL_RIGHT
):
323 tbl_literal(tp
, dp
, col
);
325 case (TBL_CELL_NUMBER
):
326 tbl_number(tp
, tbl
, dp
, col
);
328 case (TBL_CELL_DOWN
):
329 tbl_char(tp
, ASCII_NBRSP
, col
->width
);
338 tbl_vrule(struct termp
*tp
, const struct tbl_head
*hp
)
342 case (TBL_HEAD_VERT
):
345 case (TBL_HEAD_DVERT
):
354 tbl_char(struct termp
*tp
, char c
, size_t len
)
362 sz
= term_strlen(tp
, cp
);
364 for (i
= 0; i
< len
; i
+= sz
)
369 tbl_literal(struct termp
*tp
, const struct tbl_dat
*dp
,
370 const struct roffcol
*col
)
372 size_t len
, padl
, padr
;
375 len
= term_strlen(tp
, dp
->string
);
376 padr
= col
->width
> len
? col
->width
- len
: 0;
379 switch (dp
->layout
->pos
) {
380 case (TBL_CELL_LONG
):
381 padl
= term_len(tp
, 1);
382 padr
= padr
> padl
? padr
- padl
: 0;
384 case (TBL_CELL_CENTRE
):
390 case (TBL_CELL_RIGHT
):
398 tbl_char(tp
, ASCII_NBRSP
, padl
);
399 term_word(tp
, dp
->string
);
400 tbl_char(tp
, ASCII_NBRSP
, padr
);
404 tbl_number(struct termp
*tp
, const struct tbl
*tbl
,
405 const struct tbl_dat
*dp
,
406 const struct roffcol
*col
)
410 size_t sz
, psz
, ssz
, d
, padl
;
414 * See calc_data_number(). Left-pad by taking the offset of our
415 * and the maximum decimal; right-pad by the remaining amount.
420 sz
= term_strlen(tp
, dp
->string
);
422 buf
[0] = tbl
->decimal
;
425 psz
= term_strlen(tp
, buf
);
427 if (NULL
!= (cp
= strrchr(dp
->string
, tbl
->decimal
))) {
429 for (ssz
= 0, i
= 0; cp
!= &dp
->string
[i
]; i
++) {
430 buf
[0] = dp
->string
[i
];
431 ssz
+= term_strlen(tp
, buf
);
437 padl
= col
->decimal
- d
;
439 tbl_char(tp
, ASCII_NBRSP
, padl
);
440 term_word(tp
, dp
->string
);
441 if (col
->width
> sz
+ padl
)
442 tbl_char(tp
, ASCII_NBRSP
, col
->width
- sz
- padl
);