mandoc(1): Update to 1.9.15.
[dragonfly.git] / usr.bin / mandoc / out.c
blobf3fe011e77d179db9d86d8a481039be67cfa3d62
1 /* $Id: out.c,v 1.12 2010/01/01 17:14:30 kristaps Exp $ */
2 /*
3 * Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se>
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.
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.
17 #include <sys/types.h>
19 #include <assert.h>
20 #include <ctype.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <time.h>
26 #include "out.h"
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)
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)
49 * Convert a `scaling unit' to a consistent form, or fail. Scaling
50 * units are documented in groff.7, mdoc.7, man.7.
52 int
53 a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
55 char buf[BUFSIZ], hasd;
56 int i;
57 enum roffscale unit;
59 if ('\0' == *src)
60 return(0);
62 i = hasd = 0;
64 switch (*src) {
65 case ('+'):
66 src++;
67 break;
68 case ('-'):
69 buf[i++] = *src++;
70 break;
71 default:
72 break;
75 if ('\0' == *src)
76 return(0);
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;
87 buf[i++] = *src++;
90 if (BUFSIZ == i || (*src && *(src + 1)))
91 return(0);
93 buf[i] = '\0';
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);
135 if ((dst->scale = atof(buf)) < 0)
136 dst->scale = 0;
137 dst->unit = unit;
138 dst->pt = hasd;
140 return(1);
145 * Correctly writes the time in nroff form, which differs from standard
146 * form in that a space isn't printed in lieu of the extra %e field for
147 * single-digit dates.
149 void
150 time2a(time_t t, char *dst, size_t sz)
152 struct tm tm;
153 char buf[5];
154 char *p;
155 size_t nsz;
157 assert(sz > 1);
158 localtime_r(&t, &tm);
160 p = dst;
161 nsz = 0;
163 dst[0] = '\0';
165 if (0 == (nsz = strftime(p, sz, "%B ", &tm)))
166 return;
168 p += (int)nsz;
169 sz -= nsz;
171 if (0 == strftime(buf, sizeof(buf), "%e, ", &tm))
172 return;
174 nsz = strlcat(p, buf + (' ' == buf[0] ? 1 : 0), sz);
176 if (nsz >= sz)
177 return;
179 p += (int)nsz;
180 sz -= nsz;
182 (void)strftime(p, sz, "%Y", &tm);
187 * Returns length of parsed string (the leading "\" should NOT be
188 * included). This can be zero if the current character is the nil
189 * terminator. "d" is set to the type of parsed decorator, which may
190 * have an adjoining "word" of size "sz" (e.g., "(ab" -> "ab", 2).
193 a2roffdeco(enum roffdeco *d,
194 const char **word, size_t *sz)
196 int j, type, term, lim;
197 const char *wp, *sp;
199 *d = DECO_NONE;
200 wp = *word;
201 type = 1;
203 switch (*wp) {
204 case ('\0'):
205 return(0);
207 case ('('):
208 if ('\0' == *(++wp))
209 return(1);
210 if ('\0' == *(wp + 1))
211 return(2);
213 *d = DECO_SPECIAL;
214 *sz = 2;
215 *word = wp;
216 return(3);
218 case ('*'):
219 switch (*(++wp)) {
220 case ('\0'):
221 return(1);
223 case ('('):
224 if ('\0' == *(++wp))
225 return(2);
226 if ('\0' == *(wp + 1))
227 return(3);
229 *d = DECO_RESERVED;
230 *sz = 2;
231 *word = wp;
232 return(4);
234 case ('['):
235 type = 0;
236 break;
238 default:
239 *d = DECO_RESERVED;
240 *sz = 1;
241 *word = wp;
242 return(2);
244 break;
246 case ('s'):
247 sp = wp;
248 if ('\0' == *(++wp))
249 return(1);
251 C2LIM(*wp, lim);
252 C2TERM(*wp, term);
254 if (term)
255 wp++;
257 *word = wp;
259 if (*wp == '+' || *wp == '-')
260 ++wp;
262 switch (*wp) {
263 case ('\''):
264 /* FALLTHROUGH */
265 case ('['):
266 /* FALLTHROUGH */
267 case ('('):
268 if (term)
269 return((int)(wp - sp));
271 C2LIM(*wp, lim);
272 C2TERM(*wp, term);
273 wp++;
274 break;
275 default:
276 break;
279 if ( ! isdigit((u_char)*wp))
280 return((int)(wp - sp));
282 for (j = 0; isdigit((u_char)*wp); j++) {
283 if (lim && j >= lim)
284 break;
285 ++wp;
288 if (term && term < 3) {
289 if (1 == term && *wp != '\'')
290 return((int)(wp - sp));
291 if (2 == term && *wp != ']')
292 return((int)(wp - sp));
293 ++wp;
296 *d = DECO_SIZE;
297 return((int)(wp - sp));
299 case ('f'):
300 switch (*(++wp)) {
301 case ('\0'):
302 return(1);
303 case ('3'):
304 /* FALLTHROUGH */
305 case ('B'):
306 *d = DECO_BOLD;
307 break;
308 case ('2'):
309 /* FALLTHROUGH */
310 case ('I'):
311 *d = DECO_ITALIC;
312 break;
313 case ('P'):
314 *d = DECO_PREVIOUS;
315 break;
316 case ('1'):
317 /* FALLTHROUGH */
318 case ('R'):
319 *d = DECO_ROMAN;
320 break;
321 default:
322 break;
325 return(2);
327 case ('['):
328 break;
330 case ('c'):
331 *d = DECO_NOSPACE;
332 *sz = 1;
333 return(1);
335 default:
336 *d = DECO_SPECIAL;
337 *word = wp;
338 *sz = 1;
339 return(1);
342 *word = ++wp;
343 for (j = 0; *wp && ']' != *wp; wp++, j++)
344 /* Loop... */ ;
346 if ('\0' == *wp)
347 return(j + 1);
349 *d = type ? DECO_SPECIAL : DECO_RESERVED;
350 *sz = (size_t)j;
351 return (j + 2);