Annotation of src/usr.bin/mandoc/tbl_term.c, Revision 1.4
1.4 ! schwarze 1: /* $Id: tbl_term.c,v 1.3 2010/10/15 22:07:12 schwarze Exp $ */
1.1 schwarze 2: /*
3: * Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se>
1.2 schwarze 4: * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
1.1 schwarze 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: #include <sys/queue.h>
19:
20: #include <assert.h>
21: #include <stdio.h>
22: #include <stdlib.h>
23: #include <string.h>
24:
1.2 schwarze 25: #include "out.h"
26: #include "term.h"
1.1 schwarze 27: #include "tbl_extern.h"
28:
29: /* FIXME: `n' modifier doesn't always do the right thing. */
30: /* FIXME: `n' modifier doesn't use the cell-spacing buffer. */
31:
1.3 schwarze 32: static void calc_data(struct termp *, struct tbl_data *);
33: static void calc_data_literal(struct termp *, struct tbl_data *);
34: static void calc_data_number(struct termp *, struct tbl_data *);
35: static void calc_data_spanner(struct termp *, struct tbl_data *);
1.2 schwarze 36: static inline void write_char(struct termp *, char, int);
37: static void write_data(struct termp *,
38: const struct tbl_data *, int);
39: static void write_data_literal(struct termp *,
40: const struct tbl_data *, int);
41: static void write_data_number(struct termp *,
42: const struct tbl_data *, int);
43: static void write_data_spanner(struct termp *,
44: const struct tbl_data *, int);
45: static void write_hframe(struct termp *, const struct tbl *);
46: static void write_hrule(struct termp *, const struct tbl_span *);
47: static void write_spanner(struct termp *, const struct tbl_head *);
48: static void write_vframe(struct termp *, const struct tbl *);
1.1 schwarze 49:
50:
51: int
1.2 schwarze 52: tbl_write_term(struct termp *p, const struct tbl *tbl)
1.1 schwarze 53: {
54: const struct tbl_span *span;
55: const struct tbl_data *data;
56: const struct tbl_head *head;
57:
58: /*
59: * Note that the absolute widths and decimal places for headers
60: * were set when tbl_calc_term was called.
61: */
62:
1.2 schwarze 63: term_newln(p);
64: p->flags |= TERMP_NOSPACE | TERMP_NONOSPACE;
65:
1.1 schwarze 66: /* First, write out our head horizontal frame. */
67:
1.2 schwarze 68: write_hframe(p, tbl);
1.1 schwarze 69:
70: /*
71: * Iterate through each span, and inside, through the global
72: * headers. If the global header's a spanner, print it
73: * directly; if it's data, use the corresponding data in the
74: * span as the object to print.
75: */
76:
77: TAILQ_FOREACH(span, &tbl->span, entries) {
1.2 schwarze 78: write_vframe(p, tbl);
1.1 schwarze 79:
80: /* Accomodate for the horizontal rule. */
81: if (TBL_DATA_DHORIZ & span->flags ||
82: TBL_DATA_HORIZ & span->flags) {
1.2 schwarze 83: write_hrule(p, span);
84: write_vframe(p, tbl);
85: term_flushln(p);
1.1 schwarze 86: continue;
87: }
88:
89: data = TAILQ_FIRST(&span->data);
90: TAILQ_FOREACH(head, &tbl->head, entries) {
91: switch (head->pos) {
92: case (TBL_HEAD_VERT):
93: /* FALLTHROUGH */
94: case (TBL_HEAD_DVERT):
1.2 schwarze 95: write_spanner(p, head);
1.1 schwarze 96: break;
97: case (TBL_HEAD_DATA):
1.2 schwarze 98: write_data(p, data, head->width);
1.1 schwarze 99: if (data)
100: data = TAILQ_NEXT(data, entries);
101: break;
102: default:
103: abort();
104: /* NOTREACHED */
105: }
106: }
1.2 schwarze 107: write_vframe(p, tbl);
108: term_flushln(p);
1.1 schwarze 109: }
110:
111: /* Last, write out our tail horizontal frame. */
112:
1.2 schwarze 113: write_hframe(p, tbl);
114:
115: p->flags &= ~TERMP_NONOSPACE;
1.1 schwarze 116:
117: return(1);
118: }
119:
120:
121: int
1.3 schwarze 122: tbl_calc_term(struct termp *p, struct tbl *tbl)
1.1 schwarze 123: {
124: struct tbl_span *span;
125: struct tbl_data *data;
126: struct tbl_head *head;
127:
128: /* Calculate width as the max of column cells' widths. */
129:
130: TAILQ_FOREACH(span, &tbl->span, entries) {
131: if (TBL_DATA_HORIZ & span->flags)
132: continue;
133: if (TBL_DATA_DHORIZ & span->flags)
134: continue;
135: if (TBL_DATA_NHORIZ & span->flags)
136: continue;
137: if (TBL_DATA_NDHORIZ & span->flags)
138: continue;
139: TAILQ_FOREACH(data, &span->data, entries)
1.3 schwarze 140: calc_data(p, data);
1.1 schwarze 141: }
142:
143: /* Calculate width as the simple spanner value. */
144:
145: TAILQ_FOREACH(head, &tbl->head, entries)
146: switch (head->pos) {
147: case (TBL_HEAD_VERT):
1.3 schwarze 148: head->width = term_len(p, 1);
1.1 schwarze 149: break;
150: case (TBL_HEAD_DVERT):
1.3 schwarze 151: head->width = term_len(p, 2);
1.1 schwarze 152: break;
153: default:
154: break;
155: }
156:
157: return(1);
158: }
159:
160:
161: static void
1.2 schwarze 162: write_hrule(struct termp *p, const struct tbl_span *span)
1.1 schwarze 163: {
164: const struct tbl_head *head;
165: char c;
166:
167: /*
168: * An hrule extends across the entire table and is demarked by a
169: * standalone `_' or whatnot in lieu of a table row. Spanning
170: * headers are marked by a `+', as are table boundaries.
171: */
172:
173: c = '-';
174: if (TBL_SPAN_DHORIZ & span->flags)
175: c = '=';
176:
177: /* FIXME: don't use `+' between data and a spanner! */
178:
179: TAILQ_FOREACH(head, &span->tbl->head, entries) {
180: switch (head->pos) {
181: case (TBL_HEAD_DATA):
1.2 schwarze 182: write_char(p, c, head->width);
1.1 schwarze 183: break;
184: case (TBL_HEAD_DVERT):
1.2 schwarze 185: write_char(p, '+', head->width);
1.1 schwarze 186: /* FALLTHROUGH */
187: case (TBL_HEAD_VERT):
1.2 schwarze 188: write_char(p, '+', head->width);
1.1 schwarze 189: break;
190: default:
191: abort();
192: /* NOTREACHED */
193: }
194: }
195: }
196:
197:
198: static void
1.2 schwarze 199: write_hframe(struct termp *p, const struct tbl *tbl)
1.1 schwarze 200: {
201: const struct tbl_head *head;
202:
203: if ( ! (TBL_OPT_BOX & tbl->opts || TBL_OPT_DBOX & tbl->opts))
204: return;
205:
206: /*
207: * Print out the horizontal part of a frame or double frame. A
208: * double frame has an unbroken `-' outer line the width of the
209: * table, bordered by `+'. The frame (or inner frame, in the
210: * case of the double frame) is a `-' bordered by `+' and broken
211: * by `+' whenever a span is encountered.
212: */
213:
214: if (TBL_OPT_DBOX & tbl->opts) {
1.2 schwarze 215: term_word(p, "+");
1.1 schwarze 216: TAILQ_FOREACH(head, &tbl->head, entries)
1.2 schwarze 217: write_char(p, '-', head->width);
218: term_word(p, "+");
219: term_flushln(p);
1.1 schwarze 220: }
221:
1.2 schwarze 222: term_word(p, "+");
1.1 schwarze 223: TAILQ_FOREACH(head, &tbl->head, entries) {
224: switch (head->pos) {
225: case (TBL_HEAD_DATA):
1.2 schwarze 226: write_char(p, '-', head->width);
1.1 schwarze 227: break;
228: default:
1.2 schwarze 229: write_char(p, '+', head->width);
1.1 schwarze 230: break;
231: }
232: }
1.2 schwarze 233: term_word(p, "+");
234: term_flushln(p);
1.1 schwarze 235: }
236:
237:
238: static void
1.2 schwarze 239: write_vframe(struct termp *p, const struct tbl *tbl)
1.1 schwarze 240: {
241: /* Always just a single vertical line. */
242:
243: if ( ! (TBL_OPT_BOX & tbl->opts || TBL_OPT_DBOX & tbl->opts))
244: return;
1.2 schwarze 245: term_word(p, "|");
1.1 schwarze 246: }
247:
248:
249: static void
1.3 schwarze 250: calc_data_spanner(struct termp *p, struct tbl_data *data)
1.1 schwarze 251: {
252:
253: /* N.B., these are horiz spanners (not vert) so always 1. */
1.3 schwarze 254: data->cell->head->width = term_len(p, 1);
1.1 schwarze 255: }
256:
257:
258: static void
1.3 schwarze 259: calc_data_number(struct termp *p, struct tbl_data *data)
1.1 schwarze 260: {
261: int sz, d;
262: char *dp, pnt;
263:
264: /*
265: * First calculate number width and decimal place (last + 1 for
266: * no-decimal numbers). If the stored decimal is subsequent
267: * ours, make our size longer by that difference
268: * (right-"shifting"); similarly, if ours is subsequent the
269: * stored, then extend the stored size by the difference.
270: * Finally, re-assign the stored values.
271: */
272:
273: /* TODO: use spacing modifier. */
274:
275: assert(data->string);
1.3 schwarze 276: sz = (int)term_strlen(p, data->string);
1.1 schwarze 277: pnt = data->span->tbl->decimal;
278:
1.3 schwarze 279: dp = strchr(data->string, pnt);
280: d = dp ? sz - (int)term_strlen(p, dp) : sz;
281: d += term_len(p, 1);
1.1 schwarze 282:
1.3 schwarze 283: sz += term_len(p, 2);
1.1 schwarze 284:
285: if (data->cell->head->decimal > d) {
286: sz += data->cell->head->decimal - d;
287: d = data->cell->head->decimal;
288: } else
289: data->cell->head->width +=
290: d - data->cell->head->decimal;
291:
292: if (sz > data->cell->head->width)
293: data->cell->head->width = sz;
294: if (d > data->cell->head->decimal)
295: data->cell->head->decimal = d;
296: }
297:
298:
299: static void
1.3 schwarze 300: calc_data_literal(struct termp *p, struct tbl_data *data)
1.1 schwarze 301: {
302: int sz, bufsz;
303:
304: /*
305: * Calculate our width and use the spacing, with a minimum
306: * spacing dictated by position (centre, e.g,. gets a space on
307: * either side, while right/left get a single adjacent space).
308: */
309:
310: assert(data->string);
1.3 schwarze 311: sz = (int)term_strlen(p, data->string);
1.1 schwarze 312:
313: switch (data->cell->pos) {
314: case (TBL_CELL_LONG):
315: /* FALLTHROUGH */
316: case (TBL_CELL_CENTRE):
317: bufsz = 2;
318: break;
319: default:
320: bufsz = 1;
321: break;
322: }
323:
324: if (data->cell->spacing)
325: bufsz = bufsz > data->cell->spacing ?
326: bufsz : data->cell->spacing;
327:
1.3 schwarze 328: sz += term_len(p, bufsz);
1.1 schwarze 329: if (data->cell->head->width < sz)
330: data->cell->head->width = sz;
331: }
332:
333:
334: static void
1.3 schwarze 335: calc_data(struct termp *p, struct tbl_data *data)
1.1 schwarze 336: {
337:
338: switch (data->cell->pos) {
339: case (TBL_CELL_HORIZ):
340: /* FALLTHROUGH */
341: case (TBL_CELL_DHORIZ):
1.3 schwarze 342: calc_data_spanner(p, data);
1.1 schwarze 343: break;
344: case (TBL_CELL_LONG):
345: /* FALLTHROUGH */
346: case (TBL_CELL_CENTRE):
347: /* FALLTHROUGH */
348: case (TBL_CELL_LEFT):
349: /* FALLTHROUGH */
350: case (TBL_CELL_RIGHT):
1.3 schwarze 351: calc_data_literal(p, data);
1.1 schwarze 352: break;
353: case (TBL_CELL_NUMBER):
1.3 schwarze 354: calc_data_number(p, data);
1.1 schwarze 355: break;
1.4 ! schwarze 356: case (TBL_CELL_SPAN):
! 357: data->cell->head->width = 0;
! 358: break;
1.1 schwarze 359: default:
360: abort();
361: /* NOTREACHED */
362: }
363: }
364:
365:
366: static void
1.2 schwarze 367: write_data_spanner(struct termp *p, const struct tbl_data *data, int width)
1.1 schwarze 368: {
369:
370: /*
371: * Write spanners dictated by both our cell designation (in the
372: * layout) or as data.
373: */
374: if (TBL_DATA_HORIZ & data->flags)
1.2 schwarze 375: write_char(p, '-', width);
1.1 schwarze 376: else if (TBL_DATA_DHORIZ & data->flags)
1.2 schwarze 377: write_char(p, '=', width);
1.1 schwarze 378: else if (TBL_CELL_HORIZ == data->cell->pos)
1.2 schwarze 379: write_char(p, '-', width);
1.1 schwarze 380: else if (TBL_CELL_DHORIZ == data->cell->pos)
1.2 schwarze 381: write_char(p, '=', width);
1.1 schwarze 382: }
383:
384:
385: static void
1.2 schwarze 386: write_data_number(struct termp *p, const struct tbl_data *data, int width)
1.1 schwarze 387: {
388: char *dp, pnt;
389: int d, padl, sz;
390:
391: /*
392: * See calc_data_number(). Left-pad by taking the offset of our
393: * and the maximum decimal; right-pad by the remaining amount.
394: */
395:
1.3 schwarze 396: sz = (int)term_strlen(p, data->string);
1.1 schwarze 397: pnt = data->span->tbl->decimal;
398:
399: if (NULL == (dp = strchr(data->string, pnt))) {
400: d = sz + 1;
401: } else {
402: d = (int)(dp - data->string) + 1;
403: }
404:
405: assert(d <= data->cell->head->decimal);
406: assert(sz - d <= data->cell->head->width -
407: data->cell->head->decimal);
408:
409: padl = data->cell->head->decimal - d + 1;
410: assert(width - sz - padl);
411:
1.2 schwarze 412: write_char(p, ' ', padl);
413: term_word(p, data->string);
414: write_char(p, ' ', width - sz - padl);
1.1 schwarze 415: }
416:
417:
418: static void
1.2 schwarze 419: write_data_literal(struct termp *p, const struct tbl_data *data, int width)
1.1 schwarze 420: {
421: int padl, padr;
422:
423: padl = padr = 0;
424:
425: switch (data->cell->pos) {
426: case (TBL_CELL_LONG):
427: padl = 1;
1.2 schwarze 428: padr = width - (int)term_strlen(p, data->string) - 1;
1.1 schwarze 429: break;
430: case (TBL_CELL_CENTRE):
1.2 schwarze 431: padl = width - (int)term_strlen(p, data->string);
1.1 schwarze 432: if (padl % 2)
433: padr++;
434: padl /= 2;
435: padr += padl;
436: break;
437: case (TBL_CELL_RIGHT):
1.2 schwarze 438: padl = width - (int)term_strlen(p, data->string);
1.1 schwarze 439: break;
440: default:
1.2 schwarze 441: padr = width - (int)term_strlen(p, data->string);
1.1 schwarze 442: break;
443: }
444:
1.2 schwarze 445: write_char(p, ' ', padl);
446: term_word(p, data->string);
447: write_char(p, ' ', padr);
1.1 schwarze 448: }
449:
450:
451: static void
1.2 schwarze 452: write_data(struct termp *p, const struct tbl_data *data, int width)
1.1 schwarze 453: {
454:
455: if (NULL == data) {
1.2 schwarze 456: write_char(p, ' ', width);
1.1 schwarze 457: return;
458: }
459:
460: if (TBL_DATA_HORIZ & data->flags ||
461: TBL_DATA_DHORIZ & data->flags) {
1.2 schwarze 462: write_data_spanner(p, data, width);
1.1 schwarze 463: return;
464: }
465:
466: switch (data->cell->pos) {
467: case (TBL_CELL_HORIZ):
468: /* FALLTHROUGH */
469: case (TBL_CELL_DHORIZ):
1.2 schwarze 470: write_data_spanner(p, data, width);
1.1 schwarze 471: break;
472: case (TBL_CELL_LONG):
473: /* FALLTHROUGH */
474: case (TBL_CELL_CENTRE):
475: /* FALLTHROUGH */
476: case (TBL_CELL_LEFT):
477: /* FALLTHROUGH */
478: case (TBL_CELL_RIGHT):
1.2 schwarze 479: write_data_literal(p, data, width);
1.1 schwarze 480: break;
481: case (TBL_CELL_NUMBER):
1.2 schwarze 482: write_data_number(p, data, width);
1.4 ! schwarze 483: break;
! 484: case (TBL_CELL_SPAN):
1.1 schwarze 485: break;
486: default:
487: abort();
488: /* NOTREACHED */
489: }
490: }
491:
492:
493: static void
1.2 schwarze 494: write_spanner(struct termp *p, const struct tbl_head *head)
1.1 schwarze 495: {
1.2 schwarze 496: char *w;
1.1 schwarze 497:
1.2 schwarze 498: w = NULL;
1.1 schwarze 499: switch (head->pos) {
500: case (TBL_HEAD_VERT):
1.2 schwarze 501: w = "|";
1.1 schwarze 502: break;
503: case (TBL_HEAD_DVERT):
1.2 schwarze 504: w = "||";
1.1 schwarze 505: break;
506: default:
507: break;
508: }
509:
510: assert(p);
1.2 schwarze 511: term_word(p, w);
1.1 schwarze 512: }
513:
514:
515: static inline void
1.2 schwarze 516: write_char(struct termp *p, char c, int len)
1.1 schwarze 517: {
518: int i;
1.2 schwarze 519: static char w[2];
1.1 schwarze 520:
1.2 schwarze 521: w[0] = c;
1.1 schwarze 522: for (i = 0; i < len; i++)
1.2 schwarze 523: term_word(p, w);
1.1 schwarze 524: }