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