Merge branch 'vendor/FILE'
[dragonfly.git] / usr.bin / mandoc / out.c
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>
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, type, term, lim;
197         const char      *wp, *sp;
198
199         *d = DECO_NONE;
200         wp = *word;
201         type = 1;
202
203         switch (*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 ('*'):
219                 switch (*(++wp)) {
220                 case ('\0'):
221                         return(1);
222
223                 case ('('):
224                         if ('\0' == *(++wp))
225                                 return(2);
226                         if ('\0' == *(wp + 1))
227                                 return(3);
228
229                         *d = DECO_RESERVED;
230                         *sz = 2;
231                         *word = wp;
232                         return(4);
233
234                 case ('['):
235                         type = 0;
236                         break;
237
238                 default:
239                         *d = DECO_RESERVED;
240                         *sz = 1;
241                         *word = wp;
242                         return(2);
243                 }
244                 break;
245
246         case ('s'):
247                 sp = wp;
248                 if ('\0' == *(++wp))
249                         return(1);
250
251                 C2LIM(*wp, lim);
252                 C2TERM(*wp, term);
253
254                 if (term)
255                         wp++;
256
257                 *word = wp;
258
259                 if (*wp == '+' || *wp == '-')
260                         ++wp;
261
262                 switch (*wp) {
263                 case ('\''):
264                         /* FALLTHROUGH */
265                 case ('['):
266                         /* FALLTHROUGH */
267                 case ('('):
268                         if (term)
269                                 return((int)(wp - sp));
270
271                         C2LIM(*wp, lim);
272                         C2TERM(*wp, term);
273                         wp++;
274                         break;
275                 default:
276                         break;
277                 }
278
279                 if ( ! isdigit((u_char)*wp))
280                         return((int)(wp - sp));
281
282                 for (j = 0; isdigit((u_char)*wp); j++) {
283                         if (lim && j >= lim)
284                                 break;
285                         ++wp;
286                 }
287
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;
294                 }
295
296                 *d = DECO_SIZE;
297                 return((int)(wp - sp));
298
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;
323                 }
324
325                 return(2);
326
327         case ('['):
328                 break;
329
330         case ('c'):
331                 *d = DECO_NOSPACE;
332                 *sz = 1;
333                 return(1);
334
335         default:
336                 *d = DECO_SPECIAL;
337                 *word = wp;
338                 *sz = 1;
339                 return(1);
340         }
341
342         *word = ++wp;
343         for (j = 0; *wp && ']' != *wp; wp++, j++)
344                 /* Loop... */ ;
345
346         if ('\0' == *wp)
347                 return(j + 1);
348
349         *d = type ? DECO_SPECIAL : DECO_RESERVED;
350         *sz = (size_t)j;
351         return (j + 2);
352 }