]> git.cameronkatri.com Git - mandoc.git/blob - out.c
Explicitly account for \*(Ba when checking for delims. Noted by Jason McIntyre via...
[mandoc.git] / out.c
1 /* $Id: out.c,v 1.15 2010/04/07 11:29:55 kristaps Exp $ */
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 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include <sys/types.h>
22
23 #include <assert.h>
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <time.h>
29
30 #include "out.h"
31
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
52 /*
53 * Convert a `scaling unit' to a consistent form, or fail. Scaling
54 * units are documented in groff.7, mdoc.7, man.7.
55 */
56 int
57 a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
58 {
59 char buf[BUFSIZ], hasd;
60 int i;
61 enum roffscale unit;
62
63 if ('\0' == *src)
64 return(0);
65
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
79 if ('\0' == *src)
80 return(0);
81
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 }
93
94 if (BUFSIZ == i || (*src && *(src + 1)))
95 return(0);
96
97 buf[i] = '\0';
98
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'):
122 if (SCALE_MAX == def)
123 return(0);
124 unit = SCALE_BU;
125 break;
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:
136 return(0);
137 }
138
139 if ((dst->scale = atof(buf)) < 0)
140 dst->scale = 0;
141 dst->unit = unit;
142 dst->pt = hasd;
143
144 return(1);
145 }
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
189
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 */
196 int
197 a2roffdeco(enum roffdeco *d,
198 const char **word, size_t *sz)
199 {
200 int j, term, lim;
201 char set;
202 const char *wp, *sp;
203
204 *d = DECO_NONE;
205 wp = *word;
206
207 switch ((set = *wp)) {
208 case ('\0'):
209 return(0);
210
211 case ('('):
212 if ('\0' == *(++wp))
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 ('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
283 case ('*'):
284 switch (*(++wp)) {
285 case ('\0'):
286 return(1);
287
288 case ('('):
289 if ('\0' == *(++wp))
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 ('['):
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);
310
311 default:
312 break;
313 }
314
315 *d = DECO_RESERVED;
316 *sz = 1;
317 *word = wp;
318 return(2);
319
320 case ('s'):
321 sp = wp;
322 if ('\0' == *(++wp))
323 return(1);
324
325 C2LIM(*wp, lim);
326 C2TERM(*wp, term);
327
328 if (term)
329 wp++;
330
331 *word = wp;
332
333 if (*wp == '+' || *wp == '-')
334 ++wp;
335
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;
351 }
352
353 if ( ! isdigit((u_char)*wp))
354 return((int)(wp - sp));
355
356 for (j = 0; isdigit((u_char)*wp); j++) {
357 if (lim && j >= lim)
358 break;
359 ++wp;
360 }
361
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));
367 ++wp;
368 }
369
370 *d = DECO_SIZE;
371 return((int)(wp - sp));
372
373 case ('['):
374 *word = ++wp;
375
376 for (j = 0; *wp && ']' != *wp; wp++, j++)
377 /* Loop... */ ;
378
379 if ('\0' == *wp)
380 return(j + 1);
381
382 *d = DECO_SPECIAL;
383 *sz = (size_t)j;
384 return(j + 2);
385
386 case ('c'):
387 *d = DECO_NOSPACE;
388 *sz = 1;
389 return(1);
390
391 default:
392 break;
393 }
394
395 *d = DECO_SPECIAL;
396 *word = wp;
397 *sz = 1;
398 return(1);
399 }