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