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