xref: /netbsd-src/external/bsd/mdocml/dist/out.c (revision bbde328be4e75ea9ad02e9715ea13ca54b797ada)
1 /*	$Vendor-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 }
400