Annotation of mandoc/out.c, Revision 1.14
1.14 ! kristaps 1: /* $Id: out.c,v 1.13 2010/04/07 07:49:38 kristaps Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se>
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:
30: #include "out.h"
31:
1.10 kristaps 32: /* See a2roffdeco(). */
33: #define C2LIM(c, l) do { \
34: (l) = 1; \
35: if ('[' == (c) || '\'' == (c)) \
36: (l) = 0; \
37: else if ('(' == (c)) \
38: (l) = 2; } \
39: while (/* CONSTCOND */ 0)
40:
41: /* See a2roffdeco(). */
42: #define C2TERM(c, t) do { \
43: (t) = 0; \
44: if ('\'' == (c)) \
45: (t) = 1; \
46: else if ('[' == (c)) \
47: (t) = 2; \
48: else if ('(' == (c)) \
49: (t) = 3; } \
50: while (/* CONSTCOND */ 0)
51:
1.3 kristaps 52: /*
53: * Convert a `scaling unit' to a consistent form, or fail. Scaling
1.5 kristaps 54: * units are documented in groff.7, mdoc.7, man.7.
1.3 kristaps 55: */
1.1 kristaps 56: int
1.5 kristaps 57: a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
1.1 kristaps 58: {
1.4 kristaps 59: char buf[BUFSIZ], hasd;
1.1 kristaps 60: int i;
1.3 kristaps 61: enum roffscale unit;
1.1 kristaps 62:
1.5 kristaps 63: if ('\0' == *src)
64: return(0);
65:
1.4 kristaps 66: i = hasd = 0;
67:
68: switch (*src) {
69: case ('+'):
70: src++;
71: break;
72: case ('-'):
73: buf[i++] = *src++;
74: break;
75: default:
76: break;
77: }
78:
1.5 kristaps 79: if ('\0' == *src)
80: return(0);
81:
1.4 kristaps 82: while (i < BUFSIZ) {
83: if ( ! isdigit((u_char)*src)) {
84: if ('.' != *src)
85: break;
86: else if (hasd)
87: break;
88: else
89: hasd = 1;
90: }
91: buf[i++] = *src++;
92: }
1.1 kristaps 93:
1.3 kristaps 94: if (BUFSIZ == i || (*src && *(src + 1)))
1.1 kristaps 95: return(0);
96:
1.4 kristaps 97: buf[i] = '\0';
1.1 kristaps 98:
1.3 kristaps 99: switch (*src) {
100: case ('c'):
101: unit = SCALE_CM;
102: break;
103: case ('i'):
104: unit = SCALE_IN;
105: break;
106: case ('P'):
107: unit = SCALE_PC;
108: break;
109: case ('p'):
110: unit = SCALE_PT;
111: break;
112: case ('f'):
113: unit = SCALE_FS;
114: break;
115: case ('v'):
116: unit = SCALE_VS;
117: break;
118: case ('m'):
119: unit = SCALE_EM;
120: break;
121: case ('\0'):
1.5 kristaps 122: if (SCALE_MAX == def)
123: return(0);
124: unit = SCALE_BU;
125: break;
1.3 kristaps 126: case ('u'):
127: unit = SCALE_BU;
128: break;
129: case ('M'):
130: unit = SCALE_MM;
131: break;
132: case ('n'):
133: unit = SCALE_EN;
134: break;
135: default:
1.1 kristaps 136: return(0);
1.3 kristaps 137: }
1.1 kristaps 138:
1.4 kristaps 139: if ((dst->scale = atof(buf)) < 0)
1.3 kristaps 140: dst->scale = 0;
141: dst->unit = unit;
1.4 kristaps 142: dst->pt = hasd;
143:
1.3 kristaps 144: return(1);
1.1 kristaps 145: }
1.6 kristaps 146:
147:
148: /*
149: * Correctly writes the time in nroff form, which differs from standard
150: * form in that a space isn't printed in lieu of the extra %e field for
151: * single-digit dates.
152: */
153: void
154: time2a(time_t t, char *dst, size_t sz)
155: {
156: struct tm tm;
157: char buf[5];
158: char *p;
159: size_t nsz;
160:
161: assert(sz > 1);
162: localtime_r(&t, &tm);
163:
164: p = dst;
165: nsz = 0;
166:
167: dst[0] = '\0';
168:
169: if (0 == (nsz = strftime(p, sz, "%B ", &tm)))
170: return;
171:
172: p += (int)nsz;
173: sz -= nsz;
174:
175: if (0 == strftime(buf, sizeof(buf), "%e, ", &tm))
176: return;
177:
178: nsz = strlcat(p, buf + (' ' == buf[0] ? 1 : 0), sz);
179:
180: if (nsz >= sz)
181: return;
182:
183: p += (int)nsz;
184: sz -= nsz;
185:
186: (void)strftime(p, sz, "%Y", &tm);
187: }
188:
1.8 kristaps 189:
1.10 kristaps 190: /*
191: * Returns length of parsed string (the leading "\" should NOT be
192: * included). This can be zero if the current character is the nil
193: * terminator. "d" is set to the type of parsed decorator, which may
194: * have an adjoining "word" of size "sz" (e.g., "(ab" -> "ab", 2).
195: */
1.8 kristaps 196: int
197: a2roffdeco(enum roffdeco *d,
198: const char **word, size_t *sz)
199: {
1.13 kristaps 200: int j, offs, term, lim;
1.14 ! kristaps 201: char set;
1.10 kristaps 202: const char *wp, *sp;
1.8 kristaps 203:
204: *d = DECO_NONE;
205: wp = *word;
1.13 kristaps 206: offs = 0;
1.8 kristaps 207:
1.14 ! kristaps 208: switch ((set = *wp)) {
1.8 kristaps 209: case ('\0'):
210: return(0);
211:
212: case ('('):
1.10 kristaps 213: if ('\0' == *(++wp))
1.8 kristaps 214: return(1);
215: if ('\0' == *(wp + 1))
216: return(2);
217:
218: *d = DECO_SPECIAL;
219: *sz = 2;
220: *word = wp;
221: return(3);
222:
1.14 ! kristaps 223: case ('F'):
! 224: /* FALLTHROUGH */
! 225: case ('f'):
! 226: /*
! 227: * FIXME: this needs work and consolidation (it should
! 228: * follow the sequence that special characters do, for
! 229: * one), but isn't a priority at the moment. Note, for
! 230: * one, that in reality \fB != \FB, although here we let
! 231: * these slip by.
! 232: */
! 233: switch (*(++wp)) {
! 234: case ('\0'):
! 235: return(1);
! 236: case ('3'):
! 237: /* FALLTHROUGH */
! 238: case ('B'):
! 239: *d = DECO_BOLD;
! 240: return(2);
! 241: case ('2'):
! 242: /* FALLTHROUGH */
! 243: case ('I'):
! 244: *d = DECO_ITALIC;
! 245: return(2);
! 246: case ('P'):
! 247: *d = DECO_PREVIOUS;
! 248: return(2);
! 249: case ('1'):
! 250: /* FALLTHROUGH */
! 251: case ('R'):
! 252: *d = DECO_ROMAN;
! 253: return(2);
! 254: case ('('):
! 255: if ('\0' == *(++wp))
! 256: return(2);
! 257: if ('\0' == *(wp + 1))
! 258: return(3);
! 259:
! 260: *d = 'F' == set ? DECO_FFONT : DECO_FONT;
! 261: *sz = 2;
! 262: *word = wp;
! 263: return(4);
! 264: case ('['):
! 265: *word = ++wp;
! 266: for (j = 0; *wp && ']' != *wp; wp++, j++)
! 267: /* Loop... */ ;
! 268:
! 269: if ('\0' == *wp)
! 270: return(j + 2);
! 271:
! 272: *d = 'F' == set ? DECO_FFONT : DECO_FONT;
! 273: *sz = (size_t)j;
! 274: return(j + 3);
! 275: default:
! 276: break;
! 277: }
! 278:
! 279: *d = 'F' == set ? DECO_FFONT : DECO_FONT;
! 280: *sz = 1;
! 281: *word = wp;
! 282: return(2);
! 283:
1.8 kristaps 284: case ('*'):
1.10 kristaps 285: switch (*(++wp)) {
1.8 kristaps 286: case ('\0'):
287: return(1);
288:
289: case ('('):
1.10 kristaps 290: if ('\0' == *(++wp))
1.8 kristaps 291: return(2);
292: if ('\0' == *(wp + 1))
293: return(3);
294:
295: *d = DECO_RESERVED;
296: *sz = 2;
297: *word = wp;
298: return(4);
299:
300: case ('['):
1.14 ! kristaps 301: *word = ++wp;
! 302: for (j = 0; *wp && ']' != *wp; wp++, j++)
! 303: /* Loop... */ ;
! 304:
! 305: if ('\0' == *wp)
! 306: return(j + 2);
! 307:
! 308: *d = DECO_RESERVED;
! 309: *sz = (size_t)j;
! 310: return(j + 3);
1.8 kristaps 311:
312: default:
1.14 ! kristaps 313: break;
1.8 kristaps 314: }
1.14 ! kristaps 315:
! 316: *d = DECO_RESERVED;
! 317: *sz = 1;
! 318: *word = wp;
! 319: return(2);
1.8 kristaps 320:
321: case ('s'):
1.10 kristaps 322: sp = wp;
323: if ('\0' == *(++wp))
324: return(1);
1.8 kristaps 325:
1.10 kristaps 326: C2LIM(*wp, lim);
327: C2TERM(*wp, term);
1.8 kristaps 328:
1.10 kristaps 329: if (term)
330: wp++;
1.8 kristaps 331:
1.10 kristaps 332: *word = wp;
1.8 kristaps 333:
334: if (*wp == '+' || *wp == '-')
335: ++wp;
336:
1.10 kristaps 337: switch (*wp) {
338: case ('\''):
339: /* FALLTHROUGH */
340: case ('['):
341: /* FALLTHROUGH */
342: case ('('):
343: if (term)
344: return((int)(wp - sp));
345:
346: C2LIM(*wp, lim);
347: C2TERM(*wp, term);
348: wp++;
349: break;
350: default:
351: break;
1.8 kristaps 352: }
353:
1.10 kristaps 354: if ( ! isdigit((u_char)*wp))
355: return((int)(wp - sp));
1.8 kristaps 356:
357: for (j = 0; isdigit((u_char)*wp); j++) {
358: if (lim && j >= lim)
359: break;
360: ++wp;
361: }
362:
1.10 kristaps 363: if (term && term < 3) {
364: if (1 == term && *wp != '\'')
365: return((int)(wp - sp));
366: if (2 == term && *wp != ']')
367: return((int)(wp - sp));
1.8 kristaps 368: ++wp;
369: }
1.10 kristaps 370:
371: *d = DECO_SIZE;
372: return((int)(wp - sp));
1.8 kristaps 373:
1.14 ! kristaps 374: case ('['):
! 375: *word = ++wp;
! 376:
! 377: for (j = 0; *wp && ']' != *wp; wp++, j++)
! 378: /* Loop... */ ;
1.8 kristaps 379:
1.14 ! kristaps 380: if ('\0' == *wp)
! 381: return(j + 1);
1.8 kristaps 382:
1.14 ! kristaps 383: *d = DECO_SPECIAL;
! 384: *sz = (size_t)j;
! 385: return(j + 2);
1.8 kristaps 386:
1.11 kristaps 387: case ('c'):
388: *d = DECO_NOSPACE;
389: *sz = 1;
390: return(1);
391:
1.8 kristaps 392: default:
1.14 ! kristaps 393: break;
1.8 kristaps 394: }
395:
1.14 ! kristaps 396: *d = DECO_SPECIAL;
! 397: *word = wp;
! 398: *sz = 1;
! 399: return(1);
1.8 kristaps 400: }
CVSweb