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