]> git.cameronkatri.com Git - mandoc.git/blob - tbl_term.c
Add back in a check that the leading `-' exists for arguments. This
[mandoc.git] / tbl_term.c
1 /* $Id: tbl_term.c,v 1.19 2011/01/25 12:07:30 schwarze Exp $ */
2 /*
3 * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@kth.se>
4 * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
5 *
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.
9 *
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.
17 */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include <assert.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "mandoc.h"
28 #include "out.h"
29 #include "term.h"
30
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 void tbl_hframe(struct termp *, const struct tbl_span *);
38 static void tbl_literal(struct termp *, const struct tbl_dat *,
39 const struct roffcol *);
40 static void tbl_number(struct termp *, const struct tbl *,
41 const struct tbl_dat *,
42 const struct roffcol *);
43 static void tbl_hrule(struct termp *, const struct tbl_span *);
44 static void tbl_vframe(struct termp *, const struct tbl *);
45 static void tbl_vrule(struct termp *, const struct tbl_head *);
46
47
48 static size_t
49 term_tbl_strlen(const char *p, void *arg)
50 {
51
52 return(term_strlen((const struct termp *)arg, p));
53 }
54
55 static size_t
56 term_tbl_len(size_t sz, void *arg)
57 {
58
59 return(term_len((const struct termp *)arg, sz));
60 }
61
62 void
63 term_tbl(struct termp *tp, const struct tbl_span *sp)
64 {
65 const struct tbl_head *hp;
66 const struct tbl_dat *dp;
67 struct roffcol *col;
68 int spans;
69 size_t rmargin, maxrmargin;
70
71 rmargin = tp->rmargin;
72 maxrmargin = tp->maxrmargin;
73
74 tp->rmargin = tp->maxrmargin = TERM_MAXMARGIN;
75
76 /* Inhibit printing of spaces: we do padding ourselves. */
77
78 tp->flags |= TERMP_NONOSPACE;
79 tp->flags |= TERMP_NOSPACE;
80
81 /*
82 * The first time we're invoked for a given table block,
83 * calculate the table widths and decimal positions.
84 */
85
86 if (TBL_SPAN_FIRST & sp->flags) {
87 term_flushln(tp);
88
89 tp->tbl.len = term_tbl_len;
90 tp->tbl.slen = term_tbl_strlen;
91 tp->tbl.arg = tp;
92
93 tblcalc(&tp->tbl, sp);
94 }
95
96 /* Horizontal frame at the start of boxed tables. */
97
98 if (TBL_SPAN_FIRST & sp->flags)
99 tbl_hframe(tp, sp);
100
101 /* Vertical frame at the start of each row. */
102
103 tbl_vframe(tp, sp->tbl);
104
105 /*
106 * Now print the actual data itself depending on the span type.
107 * Spanner spans get a horizontal rule; data spanners have their
108 * data printed by matching data to header.
109 */
110
111 switch (sp->pos) {
112 case (TBL_SPAN_HORIZ):
113 /* FALLTHROUGH */
114 case (TBL_SPAN_DHORIZ):
115 tbl_hrule(tp, sp);
116 break;
117 case (TBL_SPAN_DATA):
118 /* Iterate over template headers. */
119 dp = sp->first;
120 spans = 0;
121 for (hp = sp->head; hp; hp = hp->next) {
122 /*
123 * If the current data header is invoked during
124 * a spanner ("spans" > 0), don't emit anything
125 * at all.
126 */
127 switch (hp->pos) {
128 case (TBL_HEAD_VERT):
129 /* FALLTHROUGH */
130 case (TBL_HEAD_DVERT):
131 if (spans <= 0)
132 tbl_vrule(tp, hp);
133 continue;
134 case (TBL_HEAD_DATA):
135 break;
136 }
137
138 if (--spans >= 0)
139 continue;
140
141 col = &tp->tbl.cols[hp->ident];
142 tbl_data(tp, sp->tbl, dp, col);
143
144 /*
145 * Go to the next data cell and assign the
146 * number of subsequent spans, if applicable.
147 */
148
149 if (dp) {
150 spans = dp->spans;
151 dp = dp->next;
152 }
153 }
154 break;
155 }
156
157 tbl_vframe(tp, sp->tbl);
158 term_flushln(tp);
159
160 /*
161 * If we're the last row, clean up after ourselves: clear the
162 * existing table configuration and set it to NULL.
163 */
164
165 if (TBL_SPAN_LAST & sp->flags) {
166 tbl_hframe(tp, sp);
167 assert(tp->tbl.cols);
168 free(tp->tbl.cols);
169 tp->tbl.cols = NULL;
170 }
171
172 tp->flags &= ~TERMP_NONOSPACE;
173 tp->rmargin = rmargin;
174 tp->maxrmargin = maxrmargin;
175
176 }
177
178 static void
179 tbl_hrule(struct termp *tp, const struct tbl_span *sp)
180 {
181 const struct tbl_head *hp;
182 char c;
183 size_t width;
184
185 /*
186 * An hrule extends across the entire table and is demarked by a
187 * standalone `_' or whatnot in lieu of a table row. Spanning
188 * headers are marked by a `+', as are table boundaries.
189 */
190
191 c = '-';
192 if (TBL_SPAN_DHORIZ == sp->pos)
193 c = '=';
194
195 /* FIXME: don't use `+' between data and a spanner! */
196
197 for (hp = sp->head; hp; hp = hp->next) {
198 width = tp->tbl.cols[hp->ident].width;
199 switch (hp->pos) {
200 case (TBL_HEAD_DATA):
201 if (hp->next)
202 width += 2;
203 tbl_char(tp, c, width);
204 break;
205 case (TBL_HEAD_DVERT):
206 tbl_char(tp, '+', width);
207 /* FALLTHROUGH */
208 case (TBL_HEAD_VERT):
209 tbl_char(tp, '+', width);
210 break;
211 default:
212 abort();
213 /* NOTREACHED */
214 }
215 }
216 }
217
218 static void
219 tbl_hframe(struct termp *tp, const struct tbl_span *sp)
220 {
221 const struct tbl_head *hp;
222 size_t width;
223
224 if ( ! (TBL_OPT_BOX & sp->tbl->opts ||
225 TBL_OPT_DBOX & sp->tbl->opts))
226 return;
227
228 /*
229 * Print out the horizontal part of a frame or double frame. A
230 * double frame has an unbroken `-' outer line the width of the
231 * table, bordered by `+'. The frame (or inner frame, in the
232 * case of the double frame) is a `-' bordered by `+' and broken
233 * by `+' whenever a span is encountered.
234 */
235
236 if (TBL_OPT_DBOX & sp->tbl->opts) {
237 term_word(tp, "+");
238 for (hp = sp->head; hp; hp = hp->next) {
239 width = tp->tbl.cols[hp->ident].width;
240 tbl_char(tp, '-', width);
241 }
242 term_word(tp, "+");
243 term_flushln(tp);
244 }
245
246 term_word(tp, "+");
247 for (hp = sp->head; hp; hp = hp->next) {
248 width = tp->tbl.cols[hp->ident].width;
249 switch (hp->pos) {
250 case (TBL_HEAD_DATA):
251 tbl_char(tp, '-', width);
252 break;
253 default:
254 tbl_char(tp, '+', width);
255 break;
256 }
257 }
258 term_word(tp, "+");
259 term_flushln(tp);
260 }
261
262 static void
263 tbl_data(struct termp *tp, const struct tbl *tbl,
264 const struct tbl_dat *dp,
265 const struct roffcol *col)
266 {
267
268 if (NULL == dp) {
269 tbl_char(tp, ASCII_NBRSP, col->width);
270 return;
271 }
272 assert(dp->layout);
273
274 switch (dp->pos) {
275 case (TBL_DATA_NONE):
276 tbl_char(tp, ASCII_NBRSP, col->width);
277 return;
278 case (TBL_DATA_HORIZ):
279 /* FALLTHROUGH */
280 case (TBL_DATA_NHORIZ):
281 tbl_char(tp, '-', col->width);
282 return;
283 case (TBL_DATA_NDHORIZ):
284 /* FALLTHROUGH */
285 case (TBL_DATA_DHORIZ):
286 tbl_char(tp, '=', col->width);
287 return;
288 default:
289 break;
290 }
291
292 switch (dp->layout->pos) {
293 case (TBL_CELL_HORIZ):
294 tbl_char(tp, '-', col->width);
295 break;
296 case (TBL_CELL_DHORIZ):
297 tbl_char(tp, '=', col->width);
298 break;
299 case (TBL_CELL_LONG):
300 /* FALLTHROUGH */
301 case (TBL_CELL_CENTRE):
302 /* FALLTHROUGH */
303 case (TBL_CELL_LEFT):
304 /* FALLTHROUGH */
305 case (TBL_CELL_RIGHT):
306 tbl_literal(tp, dp, col);
307 break;
308 case (TBL_CELL_NUMBER):
309 tbl_number(tp, tbl, dp, col);
310 break;
311 case (TBL_CELL_DOWN):
312 tbl_char(tp, ASCII_NBRSP, col->width);
313 break;
314 default:
315 abort();
316 /* NOTREACHED */
317 }
318 }
319
320 static void
321 tbl_vrule(struct termp *tp, const struct tbl_head *hp)
322 {
323
324 switch (hp->pos) {
325 case (TBL_HEAD_VERT):
326 term_word(tp, "|");
327 break;
328 case (TBL_HEAD_DVERT):
329 term_word(tp, "||");
330 break;
331 default:
332 break;
333 }
334 }
335
336 static void
337 tbl_vframe(struct termp *tp, const struct tbl *tbl)
338 {
339
340 if (TBL_OPT_BOX & tbl->opts || TBL_OPT_DBOX & tbl->opts)
341 term_word(tp, "|");
342 }
343
344 static void
345 tbl_char(struct termp *tp, char c, size_t len)
346 {
347 size_t i, sz;
348 char cp[2];
349
350 cp[0] = c;
351 cp[1] = '\0';
352
353 sz = term_strlen(tp, cp);
354
355 for (i = 0; i < len; i += sz)
356 term_word(tp, cp);
357 }
358
359 static void
360 tbl_literal(struct termp *tp, const struct tbl_dat *dp,
361 const struct roffcol *col)
362 {
363 size_t padl, padr, ssz;
364
365 padl = padr = 0;
366
367 assert(dp->string);
368
369 ssz = term_len(tp, 1);
370
371 switch (dp->layout->pos) {
372 case (TBL_CELL_LONG):
373 padl = ssz;
374 padr = col->width - term_strlen(tp, dp->string) - ssz;
375 break;
376 case (TBL_CELL_CENTRE):
377 padr = col->width - term_strlen(tp, dp->string);
378 if (3 > padr)
379 break;
380 padl = (padr - 1) / 2;
381 padr -= padl;
382 break;
383 case (TBL_CELL_RIGHT):
384 padl = col->width - term_strlen(tp, dp->string);
385 break;
386 default:
387 padr = col->width - term_strlen(tp, dp->string);
388 break;
389 }
390
391 tbl_char(tp, ASCII_NBRSP, padl);
392 term_word(tp, dp->string);
393 tbl_char(tp, ASCII_NBRSP, padr + 2);
394 }
395
396 static void
397 tbl_number(struct termp *tp, const struct tbl *tbl,
398 const struct tbl_dat *dp,
399 const struct roffcol *col)
400 {
401 char *cp;
402 char buf[2];
403 size_t sz, psz, ssz, d, padl;
404 int i;
405
406 /*
407 * See calc_data_number(). Left-pad by taking the offset of our
408 * and the maximum decimal; right-pad by the remaining amount.
409 */
410
411 assert(dp->string);
412
413 sz = term_strlen(tp, dp->string);
414
415 buf[0] = tbl->decimal;
416 buf[1] = '\0';
417
418 psz = term_strlen(tp, buf);
419
420 if (NULL != (cp = strrchr(dp->string, tbl->decimal))) {
421 buf[1] = '\0';
422 for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
423 buf[0] = dp->string[i];
424 ssz += term_strlen(tp, buf);
425 }
426 d = ssz + psz;
427 } else
428 d = sz + psz;
429
430 sz += term_len(tp, 2);
431 d += term_len(tp, 1);
432
433 padl = col->decimal - d;
434
435 tbl_char(tp, ASCII_NBRSP, padl);
436 term_word(tp, dp->string);
437 tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
438 }
439