Annotation of src/usr.bin/mandoc/out.c, Revision 1.11
1.11 ! schwarze 1: /* $Id: out.c,v 1.10 2011/01/16 01:11:50 schwarze Exp $ */
1.1 schwarze 2: /*
1.9 schwarze 3: * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.1 schwarze 4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17: #include <sys/types.h>
18:
1.2 schwarze 19: #include <assert.h>
1.1 schwarze 20: #include <ctype.h>
21: #include <stdio.h>
22: #include <stdlib.h>
1.2 schwarze 23: #include <string.h>
24: #include <time.h>
1.1 schwarze 25:
1.9 schwarze 26: #include "mandoc.h"
1.1 schwarze 27: #include "out.h"
28:
1.9 schwarze 29: static void tblcalc_data(struct rofftbl *, struct roffcol *,
30: const struct tbl *, const struct tbl_dat *);
31: static void tblcalc_literal(struct rofftbl *, struct roffcol *,
32: const struct tbl_dat *);
33: static void tblcalc_number(struct rofftbl *, struct roffcol *,
34: const struct tbl *, const struct tbl_dat *);
35:
1.1 schwarze 36: /*
37: * Convert a `scaling unit' to a consistent form, or fail. Scaling
38: * units are documented in groff.7, mdoc.7, man.7.
39: */
40: int
41: a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
42: {
43: char buf[BUFSIZ], hasd;
44: int i;
45: enum roffscale unit;
46:
47: if ('\0' == *src)
48: return(0);
49:
50: i = hasd = 0;
51:
52: switch (*src) {
53: case ('+'):
54: src++;
55: break;
56: case ('-'):
57: buf[i++] = *src++;
58: break;
59: default:
60: break;
61: }
62:
63: if ('\0' == *src)
64: return(0);
65:
66: while (i < BUFSIZ) {
67: if ( ! isdigit((u_char)*src)) {
68: if ('.' != *src)
69: break;
70: else if (hasd)
71: break;
72: else
73: hasd = 1;
74: }
75: buf[i++] = *src++;
76: }
77:
78: if (BUFSIZ == i || (*src && *(src + 1)))
79: return(0);
80:
81: buf[i] = '\0';
82:
83: switch (*src) {
84: case ('c'):
85: unit = SCALE_CM;
86: break;
87: case ('i'):
88: unit = SCALE_IN;
89: break;
90: case ('P'):
91: unit = SCALE_PC;
92: break;
93: case ('p'):
94: unit = SCALE_PT;
95: break;
96: case ('f'):
97: unit = SCALE_FS;
98: break;
99: case ('v'):
100: unit = SCALE_VS;
101: break;
102: case ('m'):
103: unit = SCALE_EM;
104: break;
105: case ('\0'):
106: if (SCALE_MAX == def)
107: return(0);
108: unit = SCALE_BU;
109: break;
110: case ('u'):
111: unit = SCALE_BU;
112: break;
113: case ('M'):
114: unit = SCALE_MM;
115: break;
116: case ('n'):
117: unit = SCALE_EN;
118: break;
119: default:
120: return(0);
121: }
122:
1.6 schwarze 123: /* FIXME: do this in the caller. */
1.1 schwarze 124: if ((dst->scale = atof(buf)) < 0)
125: dst->scale = 0;
126: dst->unit = unit;
127: return(1);
128: }
1.2 schwarze 129:
130:
131: /*
132: * Correctly writes the time in nroff form, which differs from standard
133: * form in that a space isn't printed in lieu of the extra %e field for
134: * single-digit dates.
135: */
136: void
137: time2a(time_t t, char *dst, size_t sz)
138: {
139: struct tm tm;
140: char buf[5];
141: char *p;
142: size_t nsz;
143:
144: assert(sz > 1);
145: localtime_r(&t, &tm);
146:
147: p = dst;
148: nsz = 0;
149:
150: dst[0] = '\0';
151:
152: if (0 == (nsz = strftime(p, sz, "%B ", &tm)))
153: return;
154:
155: p += (int)nsz;
156: sz -= nsz;
157:
158: if (0 == strftime(buf, sizeof(buf), "%e, ", &tm))
159: return;
160:
161: nsz = strlcat(p, buf + (' ' == buf[0] ? 1 : 0), sz);
162:
163: if (nsz >= sz)
164: return;
165:
166: p += (int)nsz;
167: sz -= nsz;
168:
169: (void)strftime(p, sz, "%Y", &tm);
170: }
171:
1.3 schwarze 172:
173: int
1.6 schwarze 174: a2roffdeco(enum roffdeco *d, const char **word, size_t *sz)
1.3 schwarze 175: {
1.6 schwarze 176: int i, j, lim;
177: char term, c;
178: const char *wp;
1.8 schwarze 179: enum roffdeco dd;
1.3 schwarze 180:
181: *d = DECO_NONE;
1.6 schwarze 182: lim = i = 0;
183: term = '\0';
1.3 schwarze 184: wp = *word;
185:
1.6 schwarze 186: switch ((c = wp[i++])) {
1.3 schwarze 187: case ('('):
188: *d = DECO_SPECIAL;
1.6 schwarze 189: lim = 2;
190: break;
1.4 schwarze 191: case ('F'):
192: /* FALLTHROUGH */
193: case ('f'):
1.6 schwarze 194: *d = 'F' == c ? DECO_FFONT : DECO_FONT;
195:
196: switch (wp[i++]) {
197: case ('('):
198: lim = 2;
199: break;
200: case ('['):
201: term = ']';
202: break;
1.4 schwarze 203: case ('3'):
204: /* FALLTHROUGH */
205: case ('B'):
206: *d = DECO_BOLD;
1.6 schwarze 207: return(i);
1.4 schwarze 208: case ('2'):
209: /* FALLTHROUGH */
210: case ('I'):
211: *d = DECO_ITALIC;
1.6 schwarze 212: return(i);
1.4 schwarze 213: case ('P'):
214: *d = DECO_PREVIOUS;
1.6 schwarze 215: return(i);
1.4 schwarze 216: case ('1'):
217: /* FALLTHROUGH */
218: case ('R'):
219: *d = DECO_ROMAN;
1.6 schwarze 220: return(i);
1.4 schwarze 221: default:
1.6 schwarze 222: i--;
223: lim = 1;
1.4 schwarze 224: break;
225: }
1.6 schwarze 226: break;
1.8 schwarze 227: case ('k'):
228: /* FALLTHROUGH */
1.6 schwarze 229: case ('M'):
230: /* FALLTHROUGH */
231: case ('m'):
232: /* FALLTHROUGH */
1.3 schwarze 233: case ('*'):
1.6 schwarze 234: if ('*' == c)
235: *d = DECO_RESERVED;
1.3 schwarze 236:
1.6 schwarze 237: switch (wp[i++]) {
1.3 schwarze 238: case ('('):
1.6 schwarze 239: lim = 2;
240: break;
1.3 schwarze 241: case ('['):
1.6 schwarze 242: term = ']';
243: break;
1.3 schwarze 244: default:
1.6 schwarze 245: i--;
246: lim = 1;
1.4 schwarze 247: break;
1.3 schwarze 248: }
1.6 schwarze 249: break;
1.7 schwarze 250: case ('h'):
251: /* FALLTHROUGH */
252: case ('v'):
253: /* FALLTHROUGH */
1.3 schwarze 254: case ('s'):
1.7 schwarze 255: j = 0;
256: if ('+' == wp[i] || '-' == wp[i]) {
1.6 schwarze 257: i++;
1.7 schwarze 258: j = 1;
259: }
1.3 schwarze 260:
1.6 schwarze 261: switch (wp[i++]) {
262: case ('('):
263: lim = 2;
264: break;
265: case ('['):
266: term = ']';
267: break;
1.3 schwarze 268: case ('\''):
1.6 schwarze 269: term = '\'';
270: break;
271: case ('0'):
1.7 schwarze 272: j = 1;
1.3 schwarze 273: /* FALLTHROUGH */
274: default:
1.6 schwarze 275: i--;
276: lim = 1;
1.3 schwarze 277: break;
278: }
279:
1.6 schwarze 280: if ('+' == wp[i] || '-' == wp[i]) {
1.7 schwarze 281: if (j)
1.6 schwarze 282: return(i);
283: i++;
284: }
1.8 schwarze 285:
286: /* Handle embedded numerical subexp or escape. */
287:
288: if ('(' == wp[i]) {
289: while (wp[i] && ')' != wp[i])
290: if ('\\' == wp[i++]) {
291: /* Handle embedded escape. */
292: *word = &wp[i];
293: i += a2roffdeco(&dd, word, sz);
294: }
295:
296: if (')' == wp[i++])
297: break;
298:
299: *d = DECO_NONE;
300: return(i - 1);
301: } else if ('\\' == wp[i]) {
302: *word = &wp[++i];
303: i += a2roffdeco(&dd, word, sz);
304: }
305:
1.6 schwarze 306: break;
307: case ('['):
308: *d = DECO_SPECIAL;
309: term = ']';
310: break;
311: case ('c'):
312: *d = DECO_NOSPACE;
313: return(i);
1.8 schwarze 314: case ('z'):
315: *d = DECO_NONE;
316: if ('\\' == wp[i]) {
317: *word = &wp[++i];
318: return(i + a2roffdeco(&dd, word, sz));
319: } else
320: lim = 1;
321: break;
322: case ('o'):
323: /* FALLTHROUGH */
324: case ('w'):
325: if ('\'' == wp[i++]) {
326: term = '\'';
327: break;
328: }
329: /* FALLTHROUGH */
1.6 schwarze 330: default:
331: *d = DECO_SSPECIAL;
332: i--;
333: lim = 1;
334: break;
335: }
1.3 schwarze 336:
1.6 schwarze 337: assert(term || lim);
338: *word = &wp[i];
1.3 schwarze 339:
1.6 schwarze 340: if (term) {
341: j = i;
342: while (wp[i] && wp[i] != term)
343: i++;
344: if ('\0' == wp[i]) {
345: *d = DECO_NONE;
346: return(i);
1.3 schwarze 347: }
348:
1.6 schwarze 349: assert(i >= j);
350: *sz = (size_t)(i - j);
1.3 schwarze 351:
1.6 schwarze 352: return(i + 1);
353: }
1.3 schwarze 354:
1.6 schwarze 355: assert(lim > 0);
356: *sz = (size_t)lim;
1.3 schwarze 357:
1.6 schwarze 358: for (j = 0; wp[i] && j < lim; j++)
359: i++;
360: if (j < lim)
361: *d = DECO_NONE;
1.3 schwarze 362:
1.6 schwarze 363: return(i);
1.3 schwarze 364: }
1.9 schwarze 365:
366: /*
367: * Calculate the abstract widths and decimal positions of columns in a
368: * table. This routine allocates the columns structures then runs over
369: * all rows and cells in the table. The function pointers in "tbl" are
370: * used for the actual width calculations.
371: */
372: void
373: tblcalc(struct rofftbl *tbl, const struct tbl_span *sp)
374: {
375: const struct tbl_dat *dp;
376: const struct tbl_head *hp;
377: struct roffcol *col;
378:
379: /*
380: * Allocate the master column specifiers. These will hold the
381: * widths and decimal positions for all cells in the column. It
382: * must be freed and nullified by the caller.
383: */
384:
385: assert(NULL == tbl->cols);
386: tbl->cols = calloc(sp->tbl->cols, sizeof(struct roffcol));
387:
388: hp = sp->head;
389:
390: for ( ; sp; sp = sp->next) {
391: if (TBL_SPAN_DATA != sp->pos)
392: continue;
393: /*
394: * Account for the data cells in the layout, matching it
395: * to data cells in the data section.
396: */
397: for (dp = sp->first; dp; dp = dp->next) {
1.10 schwarze 398: assert(dp->layout);
1.9 schwarze 399: col = &tbl->cols[dp->layout->head->ident];
400: tblcalc_data(tbl, col, sp->tbl, dp);
401: }
402: }
403:
404: /*
405: * Calculate width of the spanners. These get one space for a
406: * vertical line, two for a double-vertical line.
407: */
408:
409: for ( ; hp; hp = hp->next) {
410: col = &tbl->cols[hp->ident];
411: switch (hp->pos) {
412: case (TBL_HEAD_VERT):
413: col->width = (*tbl->len)(1, tbl->arg);
414: break;
415: case (TBL_HEAD_DVERT):
416: col->width = (*tbl->len)(2, tbl->arg);
417: break;
418: default:
419: break;
420: }
421: }
422: }
423:
424: static void
425: tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
426: const struct tbl *tp, const struct tbl_dat *dp)
427: {
428: size_t sz;
429:
430: /* Branch down into data sub-types. */
431:
432: switch (dp->layout->pos) {
433: case (TBL_CELL_HORIZ):
434: /* FALLTHROUGH */
435: case (TBL_CELL_DHORIZ):
436: sz = (*tbl->len)(1, tbl->arg);
437: if (col->width < sz)
438: col->width = sz;
439: break;
440: case (TBL_CELL_LONG):
441: /* FALLTHROUGH */
442: case (TBL_CELL_CENTRE):
443: /* FALLTHROUGH */
444: case (TBL_CELL_LEFT):
445: /* FALLTHROUGH */
446: case (TBL_CELL_RIGHT):
447: tblcalc_literal(tbl, col, dp);
448: break;
449: case (TBL_CELL_NUMBER):
450: tblcalc_number(tbl, col, tp, dp);
451: break;
1.10 schwarze 452: case (TBL_CELL_DOWN):
453: break;
1.9 schwarze 454: default:
455: abort();
456: /* NOTREACHED */
457: }
458: }
459:
460: static void
461: tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
462: const struct tbl_dat *dp)
463: {
464: size_t sz, bufsz, spsz;
1.10 schwarze 465: const char *str;
1.9 schwarze 466:
467: /*
468: * Calculate our width and use the spacing, with a minimum
469: * spacing dictated by position (centre, e.g,. gets a space on
470: * either side, while right/left get a single adjacent space).
471: */
472:
1.10 schwarze 473: bufsz = spsz = 0;
474: str = dp->string ? dp->string : "";
475: sz = (*tbl->slen)(str, tbl->arg);
476:
477: /* FIXME: TBL_DATA_HORIZ et al.? */
1.9 schwarze 478:
479: assert(dp->layout);
480: switch (dp->layout->pos) {
481: case (TBL_CELL_LONG):
482: /* FALLTHROUGH */
483: case (TBL_CELL_CENTRE):
1.11 ! schwarze 484: bufsz = (*tbl->len)(1, tbl->arg);
1.9 schwarze 485: break;
486: default:
487: bufsz = (*tbl->len)(1, tbl->arg);
488: break;
489: }
490:
491: if (dp->layout->spacing) {
492: spsz = (*tbl->len)(dp->layout->spacing, tbl->arg);
493: bufsz = bufsz > spsz ? bufsz : spsz;
494: }
495:
496: sz += bufsz;
497: if (col->width < sz)
498: col->width = sz;
499: }
500:
501: static void
502: tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
503: const struct tbl *tp, const struct tbl_dat *dp)
504: {
505: int i;
1.10 schwarze 506: size_t sz, psz, ssz, d;
507: const char *str;
1.9 schwarze 508: char *cp;
509: char buf[2];
510:
511: /*
512: * First calculate number width and decimal place (last + 1 for
513: * no-decimal numbers). If the stored decimal is subsequent
514: * ours, make our size longer by that difference
515: * (right-"shifting"); similarly, if ours is subsequent the
516: * stored, then extend the stored size by the difference.
517: * Finally, re-assign the stored values.
518: */
519:
1.10 schwarze 520: str = dp->string ? dp->string : "";
521: sz = (*tbl->slen)(str, tbl->arg);
1.9 schwarze 522:
1.10 schwarze 523: /* FIXME: TBL_DATA_HORIZ et al.? */
1.9 schwarze 524:
525: buf[0] = tp->decimal;
526: buf[1] = '\0';
527:
528: psz = (*tbl->slen)(buf, tbl->arg);
529:
530: if (NULL != (cp = strrchr(str, tp->decimal))) {
531: buf[1] = '\0';
532: for (ssz = 0, i = 0; cp != &str[i]; i++) {
533: buf[0] = str[i];
534: ssz += (*tbl->slen)(buf, tbl->arg);
535: }
536: d = ssz + psz;
537: } else
538: d = sz + psz;
539:
540: /* Padding. */
541:
542: sz += (*tbl->len)(2, tbl->arg);
543: d += (*tbl->len)(1, tbl->arg);
544:
545: /* Adjust the settings for this column. */
546:
547: if (col->decimal > d) {
548: sz += col->decimal - d;
549: d = col->decimal;
550: } else
551: col->width += d - col->decimal;
552:
553: if (sz > col->width)
554: col->width = sz;
555: if (d > col->decimal)
556: col->decimal = d;
557:
558: /* Adjust for stipulated width. */
559:
1.10 schwarze 560: if (col->width < dp->layout->spacing)
561: col->width = dp->layout->spacing;
1.9 schwarze 562: }
563:
564: