Annotation of mandoc/out.c, Revision 1.15
1.15 ! kristaps 1: /* $Id: out.c,v 1.14 2010/04/07 11:25: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.15 ! kristaps 200: int j, 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;
206:
1.14 kristaps 207: switch ((set = *wp)) {
1.8 kristaps 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:
1.14 kristaps 222: case ('F'):
223: /* FALLTHROUGH */
224: case ('f'):
225: /*
226: * FIXME: this needs work and consolidation (it should
227: * follow the sequence that special characters do, for
228: * one), but isn't a priority at the moment. Note, for
229: * one, that in reality \fB != \FB, although here we let
230: * these slip by.
231: */
232: switch (*(++wp)) {
233: case ('\0'):
234: return(1);
235: case ('3'):
236: /* FALLTHROUGH */
237: case ('B'):
238: *d = DECO_BOLD;
239: return(2);
240: case ('2'):
241: /* FALLTHROUGH */
242: case ('I'):
243: *d = DECO_ITALIC;
244: return(2);
245: case ('P'):
246: *d = DECO_PREVIOUS;
247: return(2);
248: case ('1'):
249: /* FALLTHROUGH */
250: case ('R'):
251: *d = DECO_ROMAN;
252: return(2);
253: case ('('):
254: if ('\0' == *(++wp))
255: return(2);
256: if ('\0' == *(wp + 1))
257: return(3);
258:
259: *d = 'F' == set ? DECO_FFONT : DECO_FONT;
260: *sz = 2;
261: *word = wp;
262: return(4);
263: case ('['):
264: *word = ++wp;
265: for (j = 0; *wp && ']' != *wp; wp++, j++)
266: /* Loop... */ ;
267:
268: if ('\0' == *wp)
269: return(j + 2);
270:
271: *d = 'F' == set ? DECO_FFONT : DECO_FONT;
272: *sz = (size_t)j;
273: return(j + 3);
274: default:
275: break;
276: }
277:
278: *d = 'F' == set ? DECO_FFONT : DECO_FONT;
279: *sz = 1;
280: *word = wp;
281: return(2);
282:
1.8 kristaps 283: case ('*'):
1.10 kristaps 284: switch (*(++wp)) {
1.8 kristaps 285: case ('\0'):
286: return(1);
287:
288: case ('('):
1.10 kristaps 289: if ('\0' == *(++wp))
1.8 kristaps 290: return(2);
291: if ('\0' == *(wp + 1))
292: return(3);
293:
294: *d = DECO_RESERVED;
295: *sz = 2;
296: *word = wp;
297: return(4);
298:
299: case ('['):
1.14 kristaps 300: *word = ++wp;
301: for (j = 0; *wp && ']' != *wp; wp++, j++)
302: /* Loop... */ ;
303:
304: if ('\0' == *wp)
305: return(j + 2);
306:
307: *d = DECO_RESERVED;
308: *sz = (size_t)j;
309: return(j + 3);
1.8 kristaps 310:
311: default:
1.14 kristaps 312: break;
1.8 kristaps 313: }
1.14 kristaps 314:
315: *d = DECO_RESERVED;
316: *sz = 1;
317: *word = wp;
318: return(2);
1.8 kristaps 319:
320: case ('s'):
1.10 kristaps 321: sp = wp;
322: if ('\0' == *(++wp))
323: return(1);
1.8 kristaps 324:
1.10 kristaps 325: C2LIM(*wp, lim);
326: C2TERM(*wp, term);
1.8 kristaps 327:
1.10 kristaps 328: if (term)
329: wp++;
1.8 kristaps 330:
1.10 kristaps 331: *word = wp;
1.8 kristaps 332:
333: if (*wp == '+' || *wp == '-')
334: ++wp;
335:
1.10 kristaps 336: switch (*wp) {
337: case ('\''):
338: /* FALLTHROUGH */
339: case ('['):
340: /* FALLTHROUGH */
341: case ('('):
342: if (term)
343: return((int)(wp - sp));
344:
345: C2LIM(*wp, lim);
346: C2TERM(*wp, term);
347: wp++;
348: break;
349: default:
350: break;
1.8 kristaps 351: }
352:
1.10 kristaps 353: if ( ! isdigit((u_char)*wp))
354: return((int)(wp - sp));
1.8 kristaps 355:
356: for (j = 0; isdigit((u_char)*wp); j++) {
357: if (lim && j >= lim)
358: break;
359: ++wp;
360: }
361:
1.10 kristaps 362: if (term && term < 3) {
363: if (1 == term && *wp != '\'')
364: return((int)(wp - sp));
365: if (2 == term && *wp != ']')
366: return((int)(wp - sp));
1.8 kristaps 367: ++wp;
368: }
1.10 kristaps 369:
370: *d = DECO_SIZE;
371: return((int)(wp - sp));
1.8 kristaps 372:
1.14 kristaps 373: case ('['):
374: *word = ++wp;
375:
376: for (j = 0; *wp && ']' != *wp; wp++, j++)
377: /* Loop... */ ;
1.8 kristaps 378:
1.14 kristaps 379: if ('\0' == *wp)
380: return(j + 1);
1.8 kristaps 381:
1.14 kristaps 382: *d = DECO_SPECIAL;
383: *sz = (size_t)j;
384: return(j + 2);
1.8 kristaps 385:
1.11 kristaps 386: case ('c'):
387: *d = DECO_NOSPACE;
388: *sz = 1;
389: return(1);
390:
1.8 kristaps 391: default:
1.14 kristaps 392: break;
1.8 kristaps 393: }
394:
1.14 kristaps 395: *d = DECO_SPECIAL;
396: *word = wp;
397: *sz = 1;
398: return(1);
1.8 kristaps 399: }
CVSweb