tbl_term.c revision 1.1.1.5 1 1.1.1.5 christos /* $Id: tbl_term.c,v 1.1.1.5 2015/12/17 21:58:48 christos Exp $ */
2 1.1 joerg /*
3 1.1.1.3 joerg * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps (at) bsd.lv>
4 1.1.1.5 christos * Copyright (c) 2011, 2012, 2014, 2015 Ingo Schwarze <schwarze (at) openbsd.org>
5 1.1 joerg *
6 1.1 joerg * Permission to use, copy, modify, and distribute this software for any
7 1.1 joerg * purpose with or without fee is hereby granted, provided that the above
8 1.1 joerg * copyright notice and this permission notice appear in all copies.
9 1.1 joerg *
10 1.1 joerg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 1.1 joerg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 1.1 joerg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 1.1 joerg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 1.1 joerg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 1.1 joerg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 1.1 joerg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 1.1 joerg */
18 1.1 joerg #include "config.h"
19 1.1.1.5 christos
20 1.1.1.5 christos #include <sys/types.h>
21 1.1 joerg
22 1.1 joerg #include <assert.h>
23 1.1 joerg #include <stdio.h>
24 1.1 joerg #include <stdlib.h>
25 1.1 joerg #include <string.h>
26 1.1 joerg
27 1.1 joerg #include "mandoc.h"
28 1.1 joerg #include "out.h"
29 1.1 joerg #include "term.h"
30 1.1 joerg
31 1.1 joerg static size_t term_tbl_len(size_t, void *);
32 1.1 joerg static size_t term_tbl_strlen(const char *, void *);
33 1.1 joerg static void tbl_char(struct termp *, char, size_t);
34 1.1.1.4 joerg static void tbl_data(struct termp *, const struct tbl_opts *,
35 1.1.1.5 christos const struct tbl_dat *,
36 1.1 joerg const struct roffcol *);
37 1.1.1.5 christos static void tbl_literal(struct termp *, const struct tbl_dat *,
38 1.1 joerg const struct roffcol *);
39 1.1.1.5 christos static void tbl_number(struct termp *, const struct tbl_opts *,
40 1.1.1.5 christos const struct tbl_dat *,
41 1.1 joerg const struct roffcol *);
42 1.1.1.5 christos static void tbl_hrule(struct termp *, const struct tbl_span *, int);
43 1.1.1.5 christos static void tbl_word(struct termp *, const struct tbl_dat *);
44 1.1 joerg
45 1.1 joerg
46 1.1 joerg static size_t
47 1.1 joerg term_tbl_strlen(const char *p, void *arg)
48 1.1 joerg {
49 1.1 joerg
50 1.1 joerg return(term_strlen((const struct termp *)arg, p));
51 1.1 joerg }
52 1.1 joerg
53 1.1 joerg static size_t
54 1.1 joerg term_tbl_len(size_t sz, void *arg)
55 1.1 joerg {
56 1.1 joerg
57 1.1 joerg return(term_len((const struct termp *)arg, sz));
58 1.1 joerg }
59 1.1 joerg
60 1.1 joerg void
61 1.1 joerg term_tbl(struct termp *tp, const struct tbl_span *sp)
62 1.1 joerg {
63 1.1.1.5 christos const struct tbl_cell *cp;
64 1.1 joerg const struct tbl_dat *dp;
65 1.1.1.5 christos static size_t offset;
66 1.1.1.5 christos size_t rmargin, maxrmargin, tsz;
67 1.1.1.5 christos int ic, horiz, spans, vert;
68 1.1 joerg
69 1.1 joerg rmargin = tp->rmargin;
70 1.1 joerg maxrmargin = tp->maxrmargin;
71 1.1 joerg
72 1.1 joerg tp->rmargin = tp->maxrmargin = TERM_MAXMARGIN;
73 1.1 joerg
74 1.1 joerg /* Inhibit printing of spaces: we do padding ourselves. */
75 1.1 joerg
76 1.1 joerg tp->flags |= TERMP_NONOSPACE;
77 1.1 joerg tp->flags |= TERMP_NOSPACE;
78 1.1 joerg
79 1.1 joerg /*
80 1.1 joerg * The first time we're invoked for a given table block,
81 1.1 joerg * calculate the table widths and decimal positions.
82 1.1 joerg */
83 1.1 joerg
84 1.1.1.5 christos if (tp->tbl.cols == NULL) {
85 1.1 joerg tp->tbl.len = term_tbl_len;
86 1.1 joerg tp->tbl.slen = term_tbl_strlen;
87 1.1 joerg tp->tbl.arg = tp;
88 1.1 joerg
89 1.1.1.5 christos tblcalc(&tp->tbl, sp, rmargin - tp->offset);
90 1.1 joerg
91 1.1.1.5 christos /* Center the table as a whole. */
92 1.1 joerg
93 1.1.1.5 christos offset = tp->offset;
94 1.1.1.5 christos if (sp->opts->opts & TBL_OPT_CENTRE) {
95 1.1.1.5 christos tsz = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)
96 1.1.1.5 christos ? 2 : !!sp->opts->lvert + !!sp->opts->rvert;
97 1.1.1.5 christos for (ic = 0; ic < sp->opts->cols; ic++)
98 1.1.1.5 christos tsz += tp->tbl.cols[ic].width + 3;
99 1.1.1.5 christos tsz -= 3;
100 1.1.1.5 christos if (offset + tsz > rmargin)
101 1.1.1.5 christos tsz -= 1;
102 1.1.1.5 christos tp->offset = (offset + rmargin > tsz) ?
103 1.1.1.5 christos (offset + rmargin - tsz) / 2 : 0;
104 1.1.1.5 christos }
105 1.1.1.5 christos
106 1.1.1.5 christos /* Horizontal frame at the start of boxed tables. */
107 1.1.1.5 christos
108 1.1.1.5 christos if (sp->opts->opts & TBL_OPT_DBOX)
109 1.1.1.5 christos tbl_hrule(tp, sp, 2);
110 1.1.1.5 christos if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX))
111 1.1.1.5 christos tbl_hrule(tp, sp, 1);
112 1.1.1.3 joerg }
113 1.1 joerg
114 1.1 joerg /* Vertical frame at the start of each row. */
115 1.1 joerg
116 1.1.1.5 christos horiz = sp->pos == TBL_SPAN_HORIZ || sp->pos == TBL_SPAN_DHORIZ;
117 1.1.1.5 christos
118 1.1.1.5 christos if (sp->layout->vert ||
119 1.1.1.5 christos (sp->prev != NULL && sp->prev->layout->vert) ||
120 1.1.1.5 christos sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX))
121 1.1.1.5 christos term_word(tp, horiz ? "+" : "|");
122 1.1.1.5 christos else if (sp->opts->lvert)
123 1.1.1.5 christos tbl_char(tp, horiz ? '-' : ASCII_NBRSP, 1);
124 1.1 joerg
125 1.1 joerg /*
126 1.1 joerg * Now print the actual data itself depending on the span type.
127 1.1.1.5 christos * Match data cells to column numbers.
128 1.1 joerg */
129 1.1 joerg
130 1.1.1.5 christos if (sp->pos == TBL_SPAN_DATA) {
131 1.1.1.5 christos cp = sp->layout->first;
132 1.1 joerg dp = sp->first;
133 1.1.1.2 joerg spans = 0;
134 1.1.1.5 christos for (ic = 0; ic < sp->opts->cols; ic++) {
135 1.1.1.4 joerg
136 1.1.1.5 christos /*
137 1.1.1.5 christos * Remeber whether we need a vertical bar
138 1.1.1.5 christos * after this cell.
139 1.1.1.2 joerg */
140 1.1 joerg
141 1.1.1.5 christos vert = cp == NULL ? 0 : cp->vert;
142 1.1.1.3 joerg
143 1.1.1.5 christos /*
144 1.1.1.5 christos * Print the data and advance to the next cell.
145 1.1.1.5 christos */
146 1.1.1.3 joerg
147 1.1.1.5 christos if (spans == 0) {
148 1.1.1.5 christos tbl_data(tp, sp->opts, dp, tp->tbl.cols + ic);
149 1.1.1.5 christos if (dp != NULL) {
150 1.1.1.5 christos spans = dp->spans;
151 1.1.1.5 christos dp = dp->next;
152 1.1.1.5 christos }
153 1.1.1.5 christos } else
154 1.1.1.5 christos spans--;
155 1.1.1.5 christos if (cp != NULL)
156 1.1.1.5 christos cp = cp->next;
157 1.1.1.5 christos
158 1.1.1.5 christos /*
159 1.1.1.5 christos * Separate columns, except in the middle
160 1.1.1.5 christos * of spans and after the last cell.
161 1.1.1.2 joerg */
162 1.1.1.2 joerg
163 1.1.1.5 christos if (ic + 1 == sp->opts->cols || spans)
164 1.1.1.5 christos continue;
165 1.1.1.5 christos
166 1.1.1.5 christos tbl_char(tp, ASCII_NBRSP, 1);
167 1.1.1.5 christos if (vert > 0)
168 1.1.1.5 christos tbl_char(tp, '|', vert);
169 1.1.1.5 christos if (vert < 2)
170 1.1.1.5 christos tbl_char(tp, ASCII_NBRSP, 2 - vert);
171 1.1 joerg }
172 1.1.1.5 christos } else if (horiz)
173 1.1.1.5 christos tbl_hrule(tp, sp, 0);
174 1.1 joerg
175 1.1.1.3 joerg /* Vertical frame at the end of each row. */
176 1.1.1.3 joerg
177 1.1.1.5 christos if (sp->layout->last->vert ||
178 1.1.1.5 christos (sp->prev != NULL && sp->prev->layout->last->vert) ||
179 1.1.1.5 christos (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)))
180 1.1.1.5 christos term_word(tp, horiz ? "+" : " |");
181 1.1.1.5 christos else if (sp->opts->rvert)
182 1.1.1.5 christos tbl_char(tp, horiz ? '-' : ASCII_NBRSP, 1);
183 1.1 joerg term_flushln(tp);
184 1.1 joerg
185 1.1 joerg /*
186 1.1 joerg * If we're the last row, clean up after ourselves: clear the
187 1.1 joerg * existing table configuration and set it to NULL.
188 1.1 joerg */
189 1.1 joerg
190 1.1.1.5 christos if (sp->next == NULL) {
191 1.1.1.5 christos if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) {
192 1.1.1.5 christos tbl_hrule(tp, sp, 1);
193 1.1.1.4 joerg tp->skipvsp = 1;
194 1.1.1.4 joerg }
195 1.1.1.5 christos if (sp->opts->opts & TBL_OPT_DBOX) {
196 1.1.1.5 christos tbl_hrule(tp, sp, 2);
197 1.1.1.4 joerg tp->skipvsp = 2;
198 1.1.1.4 joerg }
199 1.1 joerg assert(tp->tbl.cols);
200 1.1 joerg free(tp->tbl.cols);
201 1.1 joerg tp->tbl.cols = NULL;
202 1.1.1.5 christos tp->offset = offset;
203 1.1 joerg }
204 1.1 joerg
205 1.1 joerg tp->flags &= ~TERMP_NONOSPACE;
206 1.1 joerg tp->rmargin = rmargin;
207 1.1 joerg tp->maxrmargin = maxrmargin;
208 1.1 joerg }
209 1.1 joerg
210 1.1.1.3 joerg /*
211 1.1.1.5 christos * Kinds of horizontal rulers:
212 1.1.1.5 christos * 0: inside the table (single or double line with crossings)
213 1.1.1.5 christos * 1: inner frame (single line with crossings and ends)
214 1.1.1.5 christos * 2: outer frame (single line without crossings with ends)
215 1.1.1.3 joerg */
216 1.1 joerg static void
217 1.1.1.5 christos tbl_hrule(struct termp *tp, const struct tbl_span *sp, int kind)
218 1.1 joerg {
219 1.1.1.5 christos const struct tbl_cell *c1, *c2;
220 1.1.1.5 christos int vert;
221 1.1.1.5 christos char line, cross;
222 1.1.1.5 christos
223 1.1.1.5 christos line = (kind == 0 && TBL_SPAN_DHORIZ == sp->pos) ? '=' : '-';
224 1.1.1.5 christos cross = (kind < 2) ? '+' : '-';
225 1.1.1.5 christos
226 1.1.1.5 christos if (kind)
227 1.1.1.5 christos term_word(tp, "+");
228 1.1.1.5 christos c1 = sp->layout->first;
229 1.1.1.5 christos c2 = sp->prev == NULL ? NULL : sp->prev->layout->first;
230 1.1.1.5 christos if (c2 == c1)
231 1.1.1.5 christos c2 = NULL;
232 1.1.1.5 christos for (;;) {
233 1.1.1.5 christos tbl_char(tp, line, tp->tbl.cols[c1->col].width + 1);
234 1.1.1.5 christos vert = c1->vert;
235 1.1.1.5 christos if ((c1 = c1->next) == NULL)
236 1.1.1.5 christos break;
237 1.1.1.5 christos if (c2 != NULL) {
238 1.1.1.5 christos if (vert < c2->vert)
239 1.1.1.5 christos vert = c2->vert;
240 1.1.1.5 christos c2 = c2->next;
241 1.1.1.5 christos }
242 1.1.1.5 christos if (vert)
243 1.1.1.5 christos tbl_char(tp, cross, vert);
244 1.1.1.5 christos if (vert < 2)
245 1.1.1.5 christos tbl_char(tp, line, 2 - vert);
246 1.1.1.4 joerg }
247 1.1.1.5 christos if (kind) {
248 1.1.1.5 christos term_word(tp, "+");
249 1.1.1.5 christos term_flushln(tp);
250 1.1.1.4 joerg }
251 1.1 joerg }
252 1.1 joerg
253 1.1 joerg static void
254 1.1.1.4 joerg tbl_data(struct termp *tp, const struct tbl_opts *opts,
255 1.1.1.5 christos const struct tbl_dat *dp,
256 1.1.1.5 christos const struct roffcol *col)
257 1.1 joerg {
258 1.1 joerg
259 1.1.1.5 christos if (dp == NULL) {
260 1.1 joerg tbl_char(tp, ASCII_NBRSP, col->width);
261 1.1 joerg return;
262 1.1 joerg }
263 1.1 joerg
264 1.1 joerg switch (dp->pos) {
265 1.1.1.5 christos case TBL_DATA_NONE:
266 1.1 joerg tbl_char(tp, ASCII_NBRSP, col->width);
267 1.1 joerg return;
268 1.1.1.5 christos case TBL_DATA_HORIZ:
269 1.1 joerg /* FALLTHROUGH */
270 1.1.1.5 christos case TBL_DATA_NHORIZ:
271 1.1 joerg tbl_char(tp, '-', col->width);
272 1.1 joerg return;
273 1.1.1.5 christos case TBL_DATA_NDHORIZ:
274 1.1 joerg /* FALLTHROUGH */
275 1.1.1.5 christos case TBL_DATA_DHORIZ:
276 1.1 joerg tbl_char(tp, '=', col->width);
277 1.1 joerg return;
278 1.1 joerg default:
279 1.1 joerg break;
280 1.1 joerg }
281 1.1.1.5 christos
282 1.1.1.2 joerg switch (dp->layout->pos) {
283 1.1.1.5 christos case TBL_CELL_HORIZ:
284 1.1 joerg tbl_char(tp, '-', col->width);
285 1.1 joerg break;
286 1.1.1.5 christos case TBL_CELL_DHORIZ:
287 1.1 joerg tbl_char(tp, '=', col->width);
288 1.1 joerg break;
289 1.1.1.5 christos case TBL_CELL_LONG:
290 1.1 joerg /* FALLTHROUGH */
291 1.1.1.5 christos case TBL_CELL_CENTRE:
292 1.1 joerg /* FALLTHROUGH */
293 1.1.1.5 christos case TBL_CELL_LEFT:
294 1.1 joerg /* FALLTHROUGH */
295 1.1.1.5 christos case TBL_CELL_RIGHT:
296 1.1 joerg tbl_literal(tp, dp, col);
297 1.1 joerg break;
298 1.1.1.5 christos case TBL_CELL_NUMBER:
299 1.1.1.4 joerg tbl_number(tp, opts, dp, col);
300 1.1 joerg break;
301 1.1.1.5 christos case TBL_CELL_DOWN:
302 1.1.1.2 joerg tbl_char(tp, ASCII_NBRSP, col->width);
303 1.1.1.2 joerg break;
304 1.1 joerg default:
305 1.1 joerg abort();
306 1.1 joerg /* NOTREACHED */
307 1.1 joerg }
308 1.1 joerg }
309 1.1 joerg
310 1.1 joerg static void
311 1.1 joerg tbl_char(struct termp *tp, char c, size_t len)
312 1.1 joerg {
313 1.1 joerg size_t i, sz;
314 1.1 joerg char cp[2];
315 1.1 joerg
316 1.1 joerg cp[0] = c;
317 1.1 joerg cp[1] = '\0';
318 1.1 joerg
319 1.1 joerg sz = term_strlen(tp, cp);
320 1.1 joerg
321 1.1 joerg for (i = 0; i < len; i += sz)
322 1.1 joerg term_word(tp, cp);
323 1.1 joerg }
324 1.1 joerg
325 1.1 joerg static void
326 1.1.1.5 christos tbl_literal(struct termp *tp, const struct tbl_dat *dp,
327 1.1 joerg const struct roffcol *col)
328 1.1 joerg {
329 1.1.1.5 christos size_t len, padl, padr, width;
330 1.1.1.5 christos int ic, spans;
331 1.1 joerg
332 1.1.1.2 joerg assert(dp->string);
333 1.1.1.3 joerg len = term_strlen(tp, dp->string);
334 1.1.1.4 joerg width = col->width;
335 1.1.1.5 christos ic = dp->layout->col;
336 1.1.1.5 christos spans = dp->spans;
337 1.1.1.5 christos while (spans--)
338 1.1.1.5 christos width += tp->tbl.cols[++ic].width + 3;
339 1.1.1.4 joerg
340 1.1.1.4 joerg padr = width > len ? width - len : 0;
341 1.1.1.3 joerg padl = 0;
342 1.1 joerg
343 1.1.1.2 joerg switch (dp->layout->pos) {
344 1.1.1.5 christos case TBL_CELL_LONG:
345 1.1.1.3 joerg padl = term_len(tp, 1);
346 1.1.1.3 joerg padr = padr > padl ? padr - padl : 0;
347 1.1 joerg break;
348 1.1.1.5 christos case TBL_CELL_CENTRE:
349 1.1.1.3 joerg if (2 > padr)
350 1.1.1.2 joerg break;
351 1.1.1.3 joerg padl = padr / 2;
352 1.1.1.2 joerg padr -= padl;
353 1.1 joerg break;
354 1.1.1.5 christos case TBL_CELL_RIGHT:
355 1.1.1.3 joerg padl = padr;
356 1.1.1.3 joerg padr = 0;
357 1.1 joerg break;
358 1.1 joerg default:
359 1.1 joerg break;
360 1.1 joerg }
361 1.1 joerg
362 1.1 joerg tbl_char(tp, ASCII_NBRSP, padl);
363 1.1.1.5 christos tbl_word(tp, dp);
364 1.1.1.3 joerg tbl_char(tp, ASCII_NBRSP, padr);
365 1.1 joerg }
366 1.1 joerg
367 1.1 joerg static void
368 1.1.1.4 joerg tbl_number(struct termp *tp, const struct tbl_opts *opts,
369 1.1 joerg const struct tbl_dat *dp,
370 1.1 joerg const struct roffcol *col)
371 1.1 joerg {
372 1.1 joerg char *cp;
373 1.1 joerg char buf[2];
374 1.1 joerg size_t sz, psz, ssz, d, padl;
375 1.1 joerg int i;
376 1.1 joerg
377 1.1 joerg /*
378 1.1 joerg * See calc_data_number(). Left-pad by taking the offset of our
379 1.1 joerg * and the maximum decimal; right-pad by the remaining amount.
380 1.1 joerg */
381 1.1 joerg
382 1.1.1.2 joerg assert(dp->string);
383 1.1 joerg
384 1.1.1.2 joerg sz = term_strlen(tp, dp->string);
385 1.1 joerg
386 1.1.1.4 joerg buf[0] = opts->decimal;
387 1.1 joerg buf[1] = '\0';
388 1.1 joerg
389 1.1 joerg psz = term_strlen(tp, buf);
390 1.1 joerg
391 1.1.1.5 christos if ((cp = strrchr(dp->string, opts->decimal)) != NULL) {
392 1.1.1.2 joerg for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
393 1.1.1.2 joerg buf[0] = dp->string[i];
394 1.1 joerg ssz += term_strlen(tp, buf);
395 1.1 joerg }
396 1.1 joerg d = ssz + psz;
397 1.1 joerg } else
398 1.1 joerg d = sz + psz;
399 1.1 joerg
400 1.1.1.5 christos if (col->decimal > d && col->width > sz) {
401 1.1.1.5 christos padl = col->decimal - d;
402 1.1.1.5 christos if (padl + sz > col->width)
403 1.1.1.5 christos padl = col->width - sz;
404 1.1.1.5 christos tbl_char(tp, ASCII_NBRSP, padl);
405 1.1.1.5 christos } else
406 1.1.1.5 christos padl = 0;
407 1.1.1.5 christos tbl_word(tp, dp);
408 1.1.1.3 joerg if (col->width > sz + padl)
409 1.1.1.3 joerg tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
410 1.1 joerg }
411 1.1 joerg
412 1.1.1.5 christos static void
413 1.1.1.5 christos tbl_word(struct termp *tp, const struct tbl_dat *dp)
414 1.1.1.5 christos {
415 1.1.1.5 christos int prev_font;
416 1.1.1.5 christos
417 1.1.1.5 christos prev_font = tp->fonti;
418 1.1.1.5 christos if (dp->layout->flags & TBL_CELL_BOLD)
419 1.1.1.5 christos term_fontpush(tp, TERMFONT_BOLD);
420 1.1.1.5 christos else if (dp->layout->flags & TBL_CELL_ITALIC)
421 1.1.1.5 christos term_fontpush(tp, TERMFONT_UNDER);
422 1.1.1.5 christos
423 1.1.1.5 christos term_word(tp, dp->string);
424 1.1.1.5 christos
425 1.1.1.5 christos term_fontpopq(tp, prev_font);
426 1.1.1.5 christos }
427