Annotation of mandoc/out.c, Revision 1.11
1.11 ! kristaps 1: /* $Id: out.c,v 1.10 2009/11/08 09:23:35 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: */
17: #include <sys/types.h>
18:
1.6 kristaps 19: #include <assert.h>
1.1 kristaps 20: #include <ctype.h>
1.3 kristaps 21: #include <stdio.h>
1.1 kristaps 22: #include <stdlib.h>
1.6 kristaps 23: #include <string.h>
1.7 kristaps 24: #include <time.h>
1.1 kristaps 25:
26: #include "out.h"
27:
1.10 kristaps 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.7 kristaps 48: #ifdef __linux__
49: extern size_t strlcat(char *, const char *, size_t);
50: #endif
1.3 kristaps 51:
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.10 kristaps 200: int j, type, term, lim;
201: const char *wp, *sp;
1.8 kristaps 202:
203: *d = DECO_NONE;
204: wp = *word;
205: type = 1;
206:
207: switch (*wp) {
208: case ('\0'):
209: return(0);
210:
211: case ('('):
1.10 kristaps 212: if ('\0' == *(++wp))
1.8 kristaps 213: return(1);
214: if ('\0' == *(wp + 1))
215: return(2);
216:
217: *d = DECO_SPECIAL;
218: *sz = 2;
219: *word = wp;
220: return(3);
221:
222: case ('*'):
1.10 kristaps 223: switch (*(++wp)) {
1.8 kristaps 224: case ('\0'):
225: return(1);
226:
227: case ('('):
1.10 kristaps 228: if ('\0' == *(++wp))
1.8 kristaps 229: return(2);
230: if ('\0' == *(wp + 1))
231: return(3);
232:
233: *d = DECO_RESERVED;
234: *sz = 2;
235: *word = wp;
236: return(4);
237:
238: case ('['):
239: type = 0;
240: break;
241:
242: default:
243: *d = DECO_RESERVED;
244: *sz = 1;
245: *word = wp;
1.9 kristaps 246: return(2);
1.8 kristaps 247: }
248: break;
249:
250: case ('s'):
1.10 kristaps 251: sp = wp;
252: if ('\0' == *(++wp))
253: return(1);
1.8 kristaps 254:
1.10 kristaps 255: C2LIM(*wp, lim);
256: C2TERM(*wp, term);
1.8 kristaps 257:
1.10 kristaps 258: if (term)
259: wp++;
1.8 kristaps 260:
1.10 kristaps 261: *word = wp;
1.8 kristaps 262:
263: if (*wp == '+' || *wp == '-')
264: ++wp;
265:
1.10 kristaps 266: switch (*wp) {
267: case ('\''):
268: /* FALLTHROUGH */
269: case ('['):
270: /* FALLTHROUGH */
271: case ('('):
272: if (term)
273: return((int)(wp - sp));
274:
275: C2LIM(*wp, lim);
276: C2TERM(*wp, term);
277: wp++;
278: break;
279: default:
280: break;
1.8 kristaps 281: }
282:
1.10 kristaps 283: if ( ! isdigit((u_char)*wp))
284: return((int)(wp - sp));
1.8 kristaps 285:
286: for (j = 0; isdigit((u_char)*wp); j++) {
287: if (lim && j >= lim)
288: break;
289: ++wp;
290: }
291:
1.10 kristaps 292: if (term && term < 3) {
293: if (1 == term && *wp != '\'')
294: return((int)(wp - sp));
295: if (2 == term && *wp != ']')
296: return((int)(wp - sp));
1.8 kristaps 297: ++wp;
298: }
1.10 kristaps 299:
300: *d = DECO_SIZE;
301: return((int)(wp - sp));
1.8 kristaps 302:
303: case ('f'):
1.10 kristaps 304: switch (*(++wp)) {
1.8 kristaps 305: case ('\0'):
306: return(1);
307: case ('3'):
308: /* FALLTHROUGH */
309: case ('B'):
310: *d = DECO_BOLD;
311: break;
312: case ('2'):
313: /* FALLTHROUGH */
314: case ('I'):
315: *d = DECO_ITALIC;
316: break;
317: case ('P'):
318: *d = DECO_PREVIOUS;
319: break;
320: case ('1'):
321: /* FALLTHROUGH */
322: case ('R'):
323: *d = DECO_ROMAN;
324: break;
325: default:
326: break;
327: }
328:
329: return(2);
330:
331: case ('['):
332: break;
333:
1.11 ! kristaps 334: case ('c'):
! 335: *d = DECO_NOSPACE;
! 336: *sz = 1;
! 337: return(1);
! 338:
1.8 kristaps 339: default:
340: *d = DECO_SPECIAL;
341: *word = wp;
342: *sz = 1;
343: return(1);
344: }
345:
346: *word = ++wp;
347: for (j = 0; *wp && ']' != *wp; wp++, j++)
348: /* Loop... */ ;
349:
350: if ('\0' == *wp)
351: return(j + 1);
352:
353: *d = type ? DECO_SPECIAL : DECO_RESERVED;
1.10 kristaps 354: *sz = (size_t)j;
1.8 kristaps 355: return (j + 2);
356: }
CVSweb