xref: /netbsd-src/external/bsd/mdocml/dist/out.c (revision 3816d47b2c42fcd6e549e3407f842a5b1a1d23ad)
1 /*	$Vendor-Id: out.c,v 1.11 2009/11/12 08:21:05 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 #include <sys/types.h>
18 
19 #include <assert.h>
20 #include <ctype.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <time.h>
25 
26 #include "out.h"
27 
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 
48 #ifdef __linux__
49 extern	size_t	  strlcat(char *, const char *, size_t);
50 #endif
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, 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 }
357