Annotation of mandoc/out.c, Revision 1.17
1.17 ! kristaps 1: /* $Id: out.c,v 1.16 2010/06/19 20:46:28 kristaps Exp $ */
1.1 kristaps 2: /*
1.16 kristaps 3: * Copyright (c) 2009 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:
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;
142: return(1);
1.1 kristaps 143: }
1.6 kristaps 144:
145:
146: /*
147: * Correctly writes the time in nroff form, which differs from standard
148: * form in that a space isn't printed in lieu of the extra %e field for
149: * single-digit dates.
150: */
151: void
152: time2a(time_t t, char *dst, size_t sz)
153: {
154: struct tm tm;
155: char buf[5];
156: char *p;
157: size_t nsz;
158:
159: assert(sz > 1);
160: localtime_r(&t, &tm);
161:
162: p = dst;
163: nsz = 0;
164:
165: dst[0] = '\0';
166:
167: if (0 == (nsz = strftime(p, sz, "%B ", &tm)))
168: return;
169:
170: p += (int)nsz;
171: sz -= nsz;
172:
173: if (0 == strftime(buf, sizeof(buf), "%e, ", &tm))
174: return;
175:
176: nsz = strlcat(p, buf + (' ' == buf[0] ? 1 : 0), sz);
177:
178: if (nsz >= sz)
179: return;
180:
181: p += (int)nsz;
182: sz -= nsz;
183:
184: (void)strftime(p, sz, "%Y", &tm);
185: }
186:
1.8 kristaps 187:
1.10 kristaps 188: /*
189: * Returns length of parsed string (the leading "\" should NOT be
190: * included). This can be zero if the current character is the nil
191: * terminator. "d" is set to the type of parsed decorator, which may
192: * have an adjoining "word" of size "sz" (e.g., "(ab" -> "ab", 2).
193: */
1.8 kristaps 194: int
195: a2roffdeco(enum roffdeco *d,
196: const char **word, size_t *sz)
197: {
1.15 kristaps 198: int j, term, lim;
1.14 kristaps 199: char set;
1.10 kristaps 200: const char *wp, *sp;
1.8 kristaps 201:
202: *d = DECO_NONE;
203: wp = *word;
204:
1.14 kristaps 205: switch ((set = *wp)) {
1.8 kristaps 206: case ('\0'):
207: return(0);
208:
209: case ('('):
1.10 kristaps 210: if ('\0' == *(++wp))
1.8 kristaps 211: return(1);
212: if ('\0' == *(wp + 1))
213: return(2);
214:
215: *d = DECO_SPECIAL;
216: *sz = 2;
217: *word = wp;
218: return(3);
219:
1.14 kristaps 220: case ('F'):
221: /* FALLTHROUGH */
222: case ('f'):
223: /*
224: * FIXME: this needs work and consolidation (it should
225: * follow the sequence that special characters do, for
226: * one), but isn't a priority at the moment. Note, for
227: * one, that in reality \fB != \FB, although here we let
228: * these slip by.
229: */
230: switch (*(++wp)) {
231: case ('\0'):
232: return(1);
233: case ('3'):
234: /* FALLTHROUGH */
235: case ('B'):
236: *d = DECO_BOLD;
237: return(2);
238: case ('2'):
239: /* FALLTHROUGH */
240: case ('I'):
241: *d = DECO_ITALIC;
242: return(2);
243: case ('P'):
244: *d = DECO_PREVIOUS;
245: return(2);
246: case ('1'):
247: /* FALLTHROUGH */
248: case ('R'):
249: *d = DECO_ROMAN;
250: return(2);
251: case ('('):
252: if ('\0' == *(++wp))
253: return(2);
254: if ('\0' == *(wp + 1))
255: return(3);
256:
257: *d = 'F' == set ? DECO_FFONT : DECO_FONT;
258: *sz = 2;
259: *word = wp;
260: return(4);
261: case ('['):
262: *word = ++wp;
263: for (j = 0; *wp && ']' != *wp; wp++, j++)
264: /* Loop... */ ;
265:
266: if ('\0' == *wp)
267: return(j + 2);
268:
269: *d = 'F' == set ? DECO_FFONT : DECO_FONT;
270: *sz = (size_t)j;
271: return(j + 3);
272: default:
273: break;
274: }
275:
276: *d = 'F' == set ? DECO_FFONT : DECO_FONT;
277: *sz = 1;
278: *word = wp;
279: return(2);
280:
1.8 kristaps 281: case ('*'):
1.10 kristaps 282: switch (*(++wp)) {
1.8 kristaps 283: case ('\0'):
284: return(1);
285:
286: case ('('):
1.10 kristaps 287: if ('\0' == *(++wp))
1.8 kristaps 288: return(2);
289: if ('\0' == *(wp + 1))
290: return(3);
291:
292: *d = DECO_RESERVED;
293: *sz = 2;
294: *word = wp;
295: return(4);
296:
297: case ('['):
1.14 kristaps 298: *word = ++wp;
299: for (j = 0; *wp && ']' != *wp; wp++, j++)
300: /* Loop... */ ;
301:
302: if ('\0' == *wp)
303: return(j + 2);
304:
305: *d = DECO_RESERVED;
306: *sz = (size_t)j;
307: return(j + 3);
1.8 kristaps 308:
309: default:
1.14 kristaps 310: break;
1.8 kristaps 311: }
1.14 kristaps 312:
313: *d = DECO_RESERVED;
314: *sz = 1;
315: *word = wp;
316: return(2);
1.8 kristaps 317:
318: case ('s'):
1.10 kristaps 319: sp = wp;
320: if ('\0' == *(++wp))
321: return(1);
1.8 kristaps 322:
1.10 kristaps 323: C2LIM(*wp, lim);
324: C2TERM(*wp, term);
1.8 kristaps 325:
1.10 kristaps 326: if (term)
327: wp++;
1.8 kristaps 328:
1.10 kristaps 329: *word = wp;
1.8 kristaps 330:
331: if (*wp == '+' || *wp == '-')
332: ++wp;
333:
1.10 kristaps 334: switch (*wp) {
335: case ('\''):
336: /* FALLTHROUGH */
337: case ('['):
338: /* FALLTHROUGH */
339: case ('('):
340: if (term)
341: return((int)(wp - sp));
342:
343: C2LIM(*wp, lim);
344: C2TERM(*wp, term);
345: wp++;
346: break;
347: default:
348: break;
1.8 kristaps 349: }
350:
1.10 kristaps 351: if ( ! isdigit((u_char)*wp))
352: return((int)(wp - sp));
1.8 kristaps 353:
354: for (j = 0; isdigit((u_char)*wp); j++) {
355: if (lim && j >= lim)
356: break;
357: ++wp;
358: }
359:
1.10 kristaps 360: if (term && term < 3) {
361: if (1 == term && *wp != '\'')
362: return((int)(wp - sp));
363: if (2 == term && *wp != ']')
364: return((int)(wp - sp));
1.8 kristaps 365: ++wp;
366: }
1.10 kristaps 367:
368: *d = DECO_SIZE;
369: return((int)(wp - sp));
1.8 kristaps 370:
1.14 kristaps 371: case ('['):
372: *word = ++wp;
373:
374: for (j = 0; *wp && ']' != *wp; wp++, j++)
375: /* Loop... */ ;
1.8 kristaps 376:
1.14 kristaps 377: if ('\0' == *wp)
378: return(j + 1);
1.8 kristaps 379:
1.14 kristaps 380: *d = DECO_SPECIAL;
381: *sz = (size_t)j;
382: return(j + 2);
1.8 kristaps 383:
1.11 kristaps 384: case ('c'):
385: *d = DECO_NOSPACE;
386: *sz = 1;
387: return(1);
388:
1.8 kristaps 389: default:
1.14 kristaps 390: break;
1.8 kristaps 391: }
392:
1.14 kristaps 393: *d = DECO_SPECIAL;
394: *word = wp;
395: *sz = 1;
396: return(1);
1.8 kristaps 397: }
CVSweb