Merge branch 'vendor/MPFR'
[dragonfly.git] / contrib / mdocml / out.c
1 /*      $Id: out.c,v 1.30 2011/01/05 15:37:23 kristaps Exp $ */
2 /*
3  * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
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 "mandoc.h"
31 #include "out.h"
32
33 static  void    tblcalc_data(struct rofftbl *, struct roffcol *,
34                         const struct tbl *, const struct tbl_dat *);
35 static  void    tblcalc_literal(struct rofftbl *, struct roffcol *,
36                         const struct tbl_dat *);
37 static  void    tblcalc_number(struct rofftbl *, struct roffcol *,
38                         const struct tbl *, const struct tbl_dat *);
39
40 /* 
41  * Convert a `scaling unit' to a consistent form, or fail.  Scaling
42  * units are documented in groff.7, mdoc.7, man.7.
43  */
44 int
45 a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
46 {
47         char             buf[BUFSIZ], hasd;
48         int              i;
49         enum roffscale   unit;
50
51         if ('\0' == *src)
52                 return(0);
53
54         i = hasd = 0;
55
56         switch (*src) {
57         case ('+'):
58                 src++;
59                 break;
60         case ('-'):
61                 buf[i++] = *src++;
62                 break;
63         default:
64                 break;
65         }
66
67         if ('\0' == *src)
68                 return(0);
69
70         while (i < BUFSIZ) {
71                 if ( ! isdigit((u_char)*src)) {
72                         if ('.' != *src)
73                                 break;
74                         else if (hasd)
75                                 break;
76                         else
77                                 hasd = 1;
78                 }
79                 buf[i++] = *src++;
80         }
81
82         if (BUFSIZ == i || (*src && *(src + 1)))
83                 return(0);
84
85         buf[i] = '\0';
86
87         switch (*src) {
88         case ('c'):
89                 unit = SCALE_CM;
90                 break;
91         case ('i'):
92                 unit = SCALE_IN;
93                 break;
94         case ('P'):
95                 unit = SCALE_PC;
96                 break;
97         case ('p'):
98                 unit = SCALE_PT;
99                 break;
100         case ('f'):
101                 unit = SCALE_FS;
102                 break;
103         case ('v'):
104                 unit = SCALE_VS;
105                 break;
106         case ('m'):
107                 unit = SCALE_EM;
108                 break;
109         case ('\0'):
110                 if (SCALE_MAX == def)
111                         return(0);
112                 unit = SCALE_BU;
113                 break;
114         case ('u'):
115                 unit = SCALE_BU;
116                 break;
117         case ('M'):
118                 unit = SCALE_MM;
119                 break;
120         case ('n'):
121                 unit = SCALE_EN;
122                 break;
123         default:
124                 return(0);
125         }
126
127         /* FIXME: do this in the caller. */
128         if ((dst->scale = atof(buf)) < 0)
129                 dst->scale = 0;
130         dst->unit = unit;
131         return(1);
132 }
133
134
135 /*
136  * Correctly writes the time in nroff form, which differs from standard
137  * form in that a space isn't printed in lieu of the extra %e field for
138  * single-digit dates.
139  */
140 void
141 time2a(time_t t, char *dst, size_t sz)
142 {
143         struct tm        tm;
144         char             buf[5];
145         char            *p;
146         size_t           nsz;
147
148         assert(sz > 1);
149         localtime_r(&t, &tm);
150
151         p = dst;
152         nsz = 0;
153
154         dst[0] = '\0';
155
156         if (0 == (nsz = strftime(p, sz, "%B ", &tm)))
157                 return;
158
159         p += (int)nsz;
160         sz -= nsz;
161
162         if (0 == strftime(buf, sizeof(buf), "%e, ", &tm))
163                 return;
164
165         nsz = strlcat(p, buf + (' ' == buf[0] ? 1 : 0), sz);
166
167         if (nsz >= sz)
168                 return;
169
170         p += (int)nsz;
171         sz -= nsz;
172
173         (void)strftime(p, sz, "%Y", &tm);
174 }
175
176
177 int
178 a2roffdeco(enum roffdeco *d, const char **word, size_t *sz)
179 {
180         int              i, j, lim;
181         char             term, c;
182         const char      *wp;
183         enum roffdeco    dd;
184
185         *d = DECO_NONE;
186         lim = i = 0;
187         term = '\0';
188         wp = *word;
189
190         switch ((c = wp[i++])) {
191         case ('('):
192                 *d = DECO_SPECIAL;
193                 lim = 2;
194                 break;
195         case ('F'):
196                 /* FALLTHROUGH */
197         case ('f'):
198                 *d = 'F' == c ? DECO_FFONT : DECO_FONT;
199
200                 switch (wp[i++]) {
201                 case ('('):
202                         lim = 2;
203                         break;
204                 case ('['):
205                         term = ']';
206                         break;
207                 case ('3'):
208                         /* FALLTHROUGH */
209                 case ('B'):
210                         *d = DECO_BOLD;
211                         return(i);
212                 case ('2'):
213                         /* FALLTHROUGH */
214                 case ('I'):
215                         *d = DECO_ITALIC;
216                         return(i);
217                 case ('P'):
218                         *d = DECO_PREVIOUS;
219                         return(i);
220                 case ('1'):
221                         /* FALLTHROUGH */
222                 case ('R'):
223                         *d = DECO_ROMAN;
224                         return(i);
225                 default:
226                         i--;
227                         lim = 1;
228                         break;
229                 }
230                 break;
231         case ('k'):
232                 /* FALLTHROUGH */
233         case ('M'):
234                 /* FALLTHROUGH */
235         case ('m'):
236                 /* FALLTHROUGH */
237         case ('*'):
238                 if ('*' == c)
239                         *d = DECO_RESERVED;
240
241                 switch (wp[i++]) {
242                 case ('('):
243                         lim = 2;
244                         break;
245                 case ('['):
246                         term = ']';
247                         break;
248                 default:
249                         i--;
250                         lim = 1;
251                         break;
252                 }
253                 break;
254         case ('h'):
255                 /* FALLTHROUGH */
256         case ('v'):
257                 /* FALLTHROUGH */
258         case ('s'):
259                 j = 0;
260                 if ('+' == wp[i] || '-' == wp[i]) {
261                         i++;
262                         j = 1;
263                 }
264
265                 switch (wp[i++]) {
266                 case ('('):
267                         lim = 2;
268                         break;
269                 case ('['):
270                         term = ']';
271                         break;
272                 case ('\''):
273                         term = '\'';
274                         break;
275                 case ('0'):
276                         j = 1;
277                         /* FALLTHROUGH */
278                 default:
279                         i--;
280                         lim = 1;
281                         break;
282                 }
283
284                 if ('+' == wp[i] || '-' == wp[i]) {
285                         if (j)
286                                 return(i);
287                         i++;
288                 } 
289
290                 /* Handle embedded numerical subexp or escape. */
291
292                 if ('(' == wp[i]) {
293                         while (wp[i] && ')' != wp[i])
294                                 if ('\\' == wp[i++]) {
295                                         /* Handle embedded escape. */
296                                         *word = &wp[i];
297                                         i += a2roffdeco(&dd, word, sz);
298                                 }
299
300                         if (')' == wp[i++])
301                                 break;
302
303                         *d = DECO_NONE;
304                         return(i - 1);
305                 } else if ('\\' == wp[i]) {
306                         *word = &wp[++i];
307                         i += a2roffdeco(&dd, word, sz);
308                 }
309
310                 break;
311         case ('['):
312                 *d = DECO_SPECIAL;
313                 term = ']';
314                 break;
315         case ('c'):
316                 *d = DECO_NOSPACE;
317                 return(i);
318         case ('z'):
319                 *d = DECO_NONE;
320                 if ('\\' == wp[i]) {
321                         *word = &wp[++i];
322                         return(i + a2roffdeco(&dd, word, sz));
323                 } else
324                         lim = 1;
325                 break;
326         case ('o'):
327                 /* FALLTHROUGH */
328         case ('w'):
329                 if ('\'' == wp[i++]) {
330                         term = '\'';
331                         break;
332                 } 
333                 /* FALLTHROUGH */
334         default:
335                 *d = DECO_SSPECIAL;
336                 i--;
337                 lim = 1;
338                 break;
339         }
340
341         assert(term || lim);
342         *word = &wp[i];
343
344         if (term) {
345                 j = i;
346                 while (wp[i] && wp[i] != term)
347                         i++;
348                 if ('\0' == wp[i]) {
349                         *d = DECO_NONE;
350                         return(i);
351                 }
352
353                 assert(i >= j);
354                 *sz = (size_t)(i - j);
355
356                 return(i + 1);
357         }
358
359         assert(lim > 0);
360         *sz = (size_t)lim;
361
362         for (j = 0; wp[i] && j < lim; j++)
363                 i++;
364         if (j < lim)
365                 *d = DECO_NONE;
366
367         return(i);
368 }
369
370 /*
371  * Calculate the abstract widths and decimal positions of columns in a
372  * table.  This routine allocates the columns structures then runs over
373  * all rows and cells in the table.  The function pointers in "tbl" are
374  * used for the actual width calculations.
375  */
376 void
377 tblcalc(struct rofftbl *tbl, const struct tbl_span *sp)
378 {
379         const struct tbl_dat    *dp;
380         const struct tbl_head   *hp;
381         struct roffcol          *col;
382
383         /*
384          * Allocate the master column specifiers.  These will hold the
385          * widths and decimal positions for all cells in the column.  It
386          * must be freed and nullified by the caller.
387          */
388
389         assert(NULL == tbl->cols);
390         tbl->cols = calloc(sp->tbl->cols, sizeof(struct roffcol));
391
392         hp = sp->head;
393
394         for ( ; sp; sp = sp->next) {
395                 if (TBL_SPAN_DATA != sp->pos)
396                         continue;
397                 /*
398                  * Account for the data cells in the layout, matching it
399                  * to data cells in the data section.
400                  */
401                 for (dp = sp->first; dp; dp = dp->next) {
402                         if (NULL == dp->layout)
403                                 continue;
404                         col = &tbl->cols[dp->layout->head->ident];
405                         tblcalc_data(tbl, col, sp->tbl, dp);
406                 }
407         }
408
409         /* 
410          * Calculate width of the spanners.  These get one space for a
411          * vertical line, two for a double-vertical line. 
412          */
413
414         for ( ; hp; hp = hp->next) {
415                 col = &tbl->cols[hp->ident];
416                 switch (hp->pos) {
417                 case (TBL_HEAD_VERT):
418                         col->width = (*tbl->len)(1, tbl->arg);
419                         break;
420                 case (TBL_HEAD_DVERT):
421                         col->width = (*tbl->len)(2, tbl->arg);
422                         break;
423                 default:
424                         break;
425                 }
426         }
427 }
428
429 static void
430 tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
431                 const struct tbl *tp, const struct tbl_dat *dp)
432 {
433         size_t           sz;
434
435         /* Branch down into data sub-types. */
436
437         switch (dp->layout->pos) {
438         case (TBL_CELL_HORIZ):
439                 /* FALLTHROUGH */
440         case (TBL_CELL_DHORIZ):
441                 sz = (*tbl->len)(1, tbl->arg);
442                 if (col->width < sz)
443                         col->width = sz;
444                 break;
445         case (TBL_CELL_LONG):
446                 /* FALLTHROUGH */
447         case (TBL_CELL_CENTRE):
448                 /* FALLTHROUGH */
449         case (TBL_CELL_LEFT):
450                 /* FALLTHROUGH */
451         case (TBL_CELL_RIGHT):
452                 tblcalc_literal(tbl, col, dp);
453                 break;
454         case (TBL_CELL_NUMBER):
455                 tblcalc_number(tbl, col, tp, dp);
456                 break;
457         default:
458                 abort();
459                 /* NOTREACHED */
460         }
461 }
462
463 static void
464 tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
465                 const struct tbl_dat *dp)
466 {
467         size_t           sz, bufsz, spsz;
468
469         /* 
470          * Calculate our width and use the spacing, with a minimum
471          * spacing dictated by position (centre, e.g,. gets a space on
472          * either side, while right/left get a single adjacent space).
473          */
474
475         sz = bufsz = spsz = 0;
476         if (dp->string)
477                 sz = (*tbl->slen)(dp->string, tbl->arg);
478
479         assert(dp->layout);
480         switch (dp->layout->pos) {
481         case (TBL_CELL_LONG):
482                 /* FALLTHROUGH */
483         case (TBL_CELL_CENTRE):
484                 bufsz = (*tbl->len)(2, tbl->arg);
485                 break;
486         default:
487                 bufsz = (*tbl->len)(1, tbl->arg);
488                 break;
489         }
490
491         if (dp->layout->spacing) {
492                 spsz = (*tbl->len)(dp->layout->spacing, tbl->arg);
493                 bufsz = bufsz > spsz ? bufsz : spsz;
494         }
495
496         sz += bufsz;
497         if (col->width < sz)
498                 col->width = sz;
499 }
500
501 static void
502 tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
503                 const struct tbl *tp, const struct tbl_dat *dp)
504 {
505         int              i;
506         size_t           sz, psz, ssz, d;
507         char            *cp;
508         const char      *str;
509         char             buf[2];
510
511         /* TODO: use spacing modifier. */
512
513         /*
514          * First calculate number width and decimal place (last + 1 for
515          * no-decimal numbers).  If the stored decimal is subsequent
516          * ours, make our size longer by that difference
517          * (right-"shifting"); similarly, if ours is subsequent the
518          * stored, then extend the stored size by the difference.
519          * Finally, re-assign the stored values.
520          */
521
522         str = "";
523         if (dp->string)
524                 str = dp->string;
525
526         sz = (*tbl->slen)(str, tbl->arg);
527
528         buf[0] = tp->decimal;
529         buf[1] = '\0';
530
531         psz = (*tbl->slen)(buf, tbl->arg);
532
533         if (NULL != (cp = strchr(str, tp->decimal))) {
534                 buf[1] = '\0';
535                 for (ssz = 0, i = 0; cp != &str[i]; i++) {
536                         buf[0] = str[i];
537                         ssz += (*tbl->slen)(buf, tbl->arg);
538                 }
539                 d = ssz + psz;
540         } else
541                 d = sz + psz;
542
543         /* Padding. */
544
545         sz += (*tbl->len)(2, tbl->arg);
546         d += (*tbl->len)(1, tbl->arg);
547
548         /* Adjust the settings for this column. */
549
550         if (col->decimal > d) {
551                 sz += col->decimal - d;
552                 d = col->decimal;
553         } else
554                 col->width += d - col->decimal;
555
556         if (sz > col->width)
557                 col->width = sz;
558         if (d > col->decimal)
559                 col->decimal = d;
560 }
561
562