mandoc(1): Update to 1.9.14.
[dragonfly.git] / usr.bin / mandoc / out.c
1 /*      $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 }