Annotation of mandoc/out.c, Revision 1.35
1.35 ! kristaps 1: /* $Id: out.c,v 1.34 2011/01/10 15:31:00 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) {
1.33 kristaps 402: assert(dp->layout);
1.30 kristaps 403: col = &tbl->cols[dp->layout->head->ident];
404: tblcalc_data(tbl, col, sp->tbl, dp);
405: }
406: }
407:
408: /*
409: * Calculate width of the spanners. These get one space for a
410: * vertical line, two for a double-vertical line.
411: */
412:
413: for ( ; hp; hp = hp->next) {
414: col = &tbl->cols[hp->ident];
415: switch (hp->pos) {
416: case (TBL_HEAD_VERT):
417: col->width = (*tbl->len)(1, tbl->arg);
418: break;
419: case (TBL_HEAD_DVERT):
420: col->width = (*tbl->len)(2, tbl->arg);
421: break;
422: default:
423: break;
424: }
425: }
426: }
427:
428: static void
429: tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
430: const struct tbl *tp, const struct tbl_dat *dp)
431: {
432: size_t sz;
433:
434: /* Branch down into data sub-types. */
435:
436: switch (dp->layout->pos) {
437: case (TBL_CELL_HORIZ):
438: /* FALLTHROUGH */
439: case (TBL_CELL_DHORIZ):
440: sz = (*tbl->len)(1, tbl->arg);
441: if (col->width < sz)
442: col->width = sz;
443: break;
444: case (TBL_CELL_LONG):
445: /* FALLTHROUGH */
446: case (TBL_CELL_CENTRE):
447: /* FALLTHROUGH */
448: case (TBL_CELL_LEFT):
449: /* FALLTHROUGH */
450: case (TBL_CELL_RIGHT):
451: tblcalc_literal(tbl, col, dp);
452: break;
453: case (TBL_CELL_NUMBER):
454: tblcalc_number(tbl, col, tp, dp);
1.35 ! kristaps 455: break;
! 456: case (TBL_CELL_DOWN):
1.30 kristaps 457: break;
458: default:
459: abort();
460: /* NOTREACHED */
461: }
462: }
463:
464: static void
465: tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
466: const struct tbl_dat *dp)
467: {
468: size_t sz, bufsz, spsz;
1.34 kristaps 469: const char *str;
1.30 kristaps 470:
471: /*
472: * Calculate our width and use the spacing, with a minimum
473: * spacing dictated by position (centre, e.g,. gets a space on
474: * either side, while right/left get a single adjacent space).
475: */
476:
1.34 kristaps 477: bufsz = spsz = 0;
478: str = dp->string ? dp->string : "";
479: sz = (*tbl->slen)(str, tbl->arg);
480:
481: /* FIXME: TBL_DATA_HORIZ et al.? */
1.30 kristaps 482:
483: assert(dp->layout);
484: switch (dp->layout->pos) {
485: case (TBL_CELL_LONG):
486: /* FALLTHROUGH */
487: case (TBL_CELL_CENTRE):
488: bufsz = (*tbl->len)(2, tbl->arg);
489: break;
490: default:
491: bufsz = (*tbl->len)(1, tbl->arg);
492: break;
493: }
494:
495: if (dp->layout->spacing) {
496: spsz = (*tbl->len)(dp->layout->spacing, tbl->arg);
497: bufsz = bufsz > spsz ? bufsz : spsz;
498: }
499:
500: sz += bufsz;
501: if (col->width < sz)
502: col->width = sz;
503: }
504:
505: static void
506: tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
507: const struct tbl *tp, const struct tbl_dat *dp)
508: {
509: int i;
1.34 kristaps 510: size_t sz, psz, ssz, d;
511: const char *str;
1.30 kristaps 512: char *cp;
513: char buf[2];
514:
515: /*
516: * First calculate number width and decimal place (last + 1 for
517: * no-decimal numbers). If the stored decimal is subsequent
518: * ours, make our size longer by that difference
519: * (right-"shifting"); similarly, if ours is subsequent the
520: * stored, then extend the stored size by the difference.
521: * Finally, re-assign the stored values.
522: */
523:
1.34 kristaps 524: str = dp->string ? dp->string : "";
525: sz = (*tbl->slen)(str, tbl->arg);
1.30 kristaps 526:
1.34 kristaps 527: /* FIXME: TBL_DATA_HORIZ et al.? */
1.30 kristaps 528:
529: buf[0] = tp->decimal;
530: buf[1] = '\0';
531:
532: psz = (*tbl->slen)(buf, tbl->arg);
533:
1.32 kristaps 534: if (NULL != (cp = strrchr(str, tp->decimal))) {
1.30 kristaps 535: buf[1] = '\0';
536: for (ssz = 0, i = 0; cp != &str[i]; i++) {
537: buf[0] = str[i];
538: ssz += (*tbl->slen)(buf, tbl->arg);
539: }
540: d = ssz + psz;
541: } else
542: d = sz + psz;
543:
544: /* Padding. */
545:
546: sz += (*tbl->len)(2, tbl->arg);
547: d += (*tbl->len)(1, tbl->arg);
548:
549: /* Adjust the settings for this column. */
550:
551: if (col->decimal > d) {
552: sz += col->decimal - d;
553: d = col->decimal;
554: } else
555: col->width += d - col->decimal;
556:
557: if (sz > col->width)
558: col->width = sz;
559: if (d > col->decimal)
560: col->decimal = d;
1.31 kristaps 561:
562: /* Adjust for stipulated width. */
563:
1.34 kristaps 564: if (col->width < dp->layout->spacing)
565: col->width = dp->layout->spacing;
1.30 kristaps 566: }
567:
568:
CVSweb