xref: /openbsd-src/usr.bin/mandoc/out.c (revision cd1eb269cafb12c415be1749cd4a4b5422710415)
1 /*	$Id: out.c,v 1.4 2010/04/07 23:15:05 schwarze 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 /*
49  * Convert a `scaling unit' to a consistent form, or fail.  Scaling
50  * units are documented in groff.7, mdoc.7, man.7.
51  */
52 int
53 a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
54 {
55 	char		 buf[BUFSIZ], hasd;
56 	int		 i;
57 	enum roffscale	 unit;
58 
59 	if ('\0' == *src)
60 		return(0);
61 
62 	i = hasd = 0;
63 
64 	switch (*src) {
65 	case ('+'):
66 		src++;
67 		break;
68 	case ('-'):
69 		buf[i++] = *src++;
70 		break;
71 	default:
72 		break;
73 	}
74 
75 	if ('\0' == *src)
76 		return(0);
77 
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;
86 		}
87 		buf[i++] = *src++;
88 	}
89 
90 	if (BUFSIZ == i || (*src && *(src + 1)))
91 		return(0);
92 
93 	buf[i] = '\0';
94 
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);
133 	}
134 
135 	if ((dst->scale = atof(buf)) < 0)
136 		dst->scale = 0;
137 	dst->unit = unit;
138 	dst->pt = hasd;
139 
140 	return(1);
141 }
142 
143 
144 /*
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.
148  */
149 void
150 time2a(time_t t, char *dst, size_t sz)
151 {
152 	struct tm	 tm;
153 	char		 buf[5];
154 	char		*p;
155 	size_t		 nsz;
156 
157 	assert(sz > 1);
158 	localtime_r(&t, &tm);
159 
160 	p = dst;
161 	nsz = 0;
162 
163 	dst[0] = '\0';
164 
165 	if (0 == (nsz = strftime(p, sz, "%B ", &tm)))
166 		return;
167 
168 	p += (int)nsz;
169 	sz -= nsz;
170 
171 	if (0 == strftime(buf, sizeof(buf), "%e, ", &tm))
172 		return;
173 
174 	nsz = strlcat(p, buf + (' ' == buf[0] ? 1 : 0), sz);
175 
176 	if (nsz >= sz)
177 		return;
178 
179 	p += (int)nsz;
180 	sz -= nsz;
181 
182 	(void)strftime(p, sz, "%Y", &tm);
183 }
184 
185 
186 /*
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).
191  */
192 int
193 a2roffdeco(enum roffdeco *d,
194 		const char **word, size_t *sz)
195 {
196 	int		 j, term, lim;
197 	char		 set;
198 	const char	*wp, *sp;
199 
200 	*d = DECO_NONE;
201 	wp = *word;
202 
203 	switch ((set = *wp)) {
204 	case ('\0'):
205 		return(0);
206 
207 	case ('('):
208 		if ('\0' == *(++wp))
209 			return(1);
210 		if ('\0' == *(wp + 1))
211 			return(2);
212 
213 		*d = DECO_SPECIAL;
214 		*sz = 2;
215 		*word = wp;
216 		return(3);
217 
218 	case ('F'):
219 		/* FALLTHROUGH */
220 	case ('f'):
221 		/*
222 		 * FIXME: this needs work and consolidation (it should
223 		 * follow the sequence that special characters do, for
224 		 * one), but isn't a priority at the moment.  Note, for
225 		 * one, that in reality \fB != \FB, although here we let
226 		 * these slip by.
227 		 */
228 		switch (*(++wp)) {
229 		case ('\0'):
230 			return(1);
231 		case ('3'):
232 			/* FALLTHROUGH */
233 		case ('B'):
234 			*d = DECO_BOLD;
235 			return(2);
236 		case ('2'):
237 			/* FALLTHROUGH */
238 		case ('I'):
239 			*d = DECO_ITALIC;
240 			return(2);
241 		case ('P'):
242 			*d = DECO_PREVIOUS;
243 			return(2);
244 		case ('1'):
245 			/* FALLTHROUGH */
246 		case ('R'):
247 			*d = DECO_ROMAN;
248 			return(2);
249 		case ('('):
250 			if ('\0' == *(++wp))
251 				return(2);
252 			if ('\0' == *(wp + 1))
253 				return(3);
254 
255 			*d = 'F' == set ? DECO_FFONT : DECO_FONT;
256 			*sz = 2;
257 			*word = wp;
258 			return(4);
259 		case ('['):
260 			*word = ++wp;
261 			for (j = 0; *wp && ']' != *wp; wp++, j++)
262 				/* Loop... */ ;
263 
264 			if ('\0' == *wp)
265 				return(j + 2);
266 
267 			*d = 'F' == set ? DECO_FFONT : DECO_FONT;
268 			*sz = (size_t)j;
269 			return(j + 3);
270 		default:
271 			break;
272 		}
273 
274 		*d = 'F' == set ? DECO_FFONT : DECO_FONT;
275 		*sz = 1;
276 		*word = wp;
277 		return(2);
278 
279 	case ('*'):
280 		switch (*(++wp)) {
281 		case ('\0'):
282 			return(1);
283 
284 		case ('('):
285 			if ('\0' == *(++wp))
286 				return(2);
287 			if ('\0' == *(wp + 1))
288 				return(3);
289 
290 			*d = DECO_RESERVED;
291 			*sz = 2;
292 			*word = wp;
293 			return(4);
294 
295 		case ('['):
296 			*word = ++wp;
297 			for (j = 0; *wp && ']' != *wp; wp++, j++)
298 				/* Loop... */ ;
299 
300 			if ('\0' == *wp)
301 				return(j + 2);
302 
303 			*d = DECO_RESERVED;
304 			*sz = (size_t)j;
305 			return(j + 3);
306 
307 		default:
308 			break;
309 		}
310 
311 		*d = DECO_RESERVED;
312 		*sz = 1;
313 		*word = wp;
314 		return(2);
315 
316 	case ('s'):
317 		sp = wp;
318 		if ('\0' == *(++wp))
319 			return(1);
320 
321 		C2LIM(*wp, lim);
322 		C2TERM(*wp, term);
323 
324 		if (term)
325 			wp++;
326 
327 		*word = wp;
328 
329 		if (*wp == '+' || *wp == '-')
330 			++wp;
331 
332 		switch (*wp) {
333 		case ('\''):
334 			/* FALLTHROUGH */
335 		case ('['):
336 			/* FALLTHROUGH */
337 		case ('('):
338 			if (term)
339 				return((int)(wp - sp));
340 
341 			C2LIM(*wp, lim);
342 			C2TERM(*wp, term);
343 			wp++;
344 			break;
345 		default:
346 			break;
347 		}
348 
349 		if ( ! isdigit((u_char)*wp))
350 			return((int)(wp - sp));
351 
352 		for (j = 0; isdigit((u_char)*wp); j++) {
353 			if (lim && j >= lim)
354 				break;
355 			++wp;
356 		}
357 
358 		if (term && term < 3) {
359 			if (1 == term && *wp != '\'')
360 				return((int)(wp - sp));
361 			if (2 == term && *wp != ']')
362 				return((int)(wp - sp));
363 			++wp;
364 		}
365 
366 		*d = DECO_SIZE;
367 		return((int)(wp - sp));
368 
369 	case ('['):
370 		*word = ++wp;
371 
372 		for (j = 0; *wp && ']' != *wp; wp++, j++)
373 			/* Loop... */ ;
374 
375 		if ('\0' == *wp)
376 			return(j + 1);
377 
378 		*d = DECO_SPECIAL;
379 		*sz = (size_t)j;
380 		return(j + 2);
381 
382 	case ('c'):
383 		*d = DECO_NOSPACE;
384 		*sz = 1;
385 		return(1);
386 
387 	default:
388 		break;
389 	}
390 
391 	*d = DECO_SPECIAL;
392 	*word = wp;
393 	*sz = 1;
394 	return(1);
395 }
396