mandoc(1): Update to 1.9.14.
[dragonfly.git] / usr.bin / mandoc / out.c
CommitLineData
4a06b3fd 1/* $Id: out.c,v 1.11 2009/11/12 08:21:05 kristaps Exp $ */
589e7c1d
SW
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
cbce6d97 19#include <assert.h>
589e7c1d
SW
20#include <ctype.h>
21#include <stdio.h>
22#include <stdlib.h>
cbce6d97
SW
23#include <string.h>
24#include <time.h>
589e7c1d
SW
25
26#include "out.h"
27
4a06b3fd
SW
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
0dd4f147
SW
48#ifdef __linux__
49extern size_t strlcat(char *, const char *, size_t);
50#endif
51
589e7c1d
SW
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 */
56int
57a2roffsu(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}
cbce6d97
SW
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 */
153void
154time2a(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}
4a06b3fd
SW
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 */
196int
197a2roffdeco(enum roffdeco *d,
198 const char **word, size_t *sz)
199{
200 int j, type, term, lim;
201 const char *wp, *sp;
202
203 *d = DECO_NONE;
204 wp = *word;
205 type = 1;
206
207 switch (*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 ('*'):
223 switch (*(++wp)) {
224 case ('\0'):
225 return(1);
226
227 case ('('):
228 if ('\0' == *(++wp))
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;
246 return(2);
247 }
248 break;
249
250 case ('s'):
251 sp = wp;
252 if ('\0' == *(++wp))
253 return(1);
254
255 C2LIM(*wp, lim);
256 C2TERM(*wp, term);
257
258 if (term)
259 wp++;
260
261 *word = wp;
262
263 if (*wp == '+' || *wp == '-')
264 ++wp;
265
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;
281 }
282
283 if ( ! isdigit((u_char)*wp))
284 return((int)(wp - sp));
285
286 for (j = 0; isdigit((u_char)*wp); j++) {
287 if (lim && j >= lim)
288 break;
289 ++wp;
290 }
291
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));
297 ++wp;
298 }
299
300 *d = DECO_SIZE;
301 return((int)(wp - sp));
302
303 case ('f'):
304 switch (*(++wp)) {
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
334 case ('c'):
335 *d = DECO_NOSPACE;
336 *sz = 1;
337 return(1);
338
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;
354 *sz = (size_t)j;
355 return (j + 2);
356}