]> git.cameronkatri.com Git - mandoc.git/blob - out.c
The numerical column type centres on the *last* decimal point.
[mandoc.git] / out.c
1 /* $Id: out.c,v 1.32 2011/01/08 17:16:48 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, max;
507 char *cp;
508 const char *str;
509 char buf[2];
510
511 /*
512 * First calculate number width and decimal place (last + 1 for
513 * no-decimal numbers). If the stored decimal is subsequent
514 * ours, make our size longer by that difference
515 * (right-"shifting"); similarly, if ours is subsequent the
516 * stored, then extend the stored size by the difference.
517 * Finally, re-assign the stored values.
518 */
519
520 str = dp && dp->string ? dp->string : "";
521 max = dp && dp->layout ? dp->layout->spacing : 0;
522
523 sz = (*tbl->slen)(str, tbl->arg);
524
525 buf[0] = tp->decimal;
526 buf[1] = '\0';
527
528 psz = (*tbl->slen)(buf, tbl->arg);
529
530 if (NULL != (cp = strrchr(str, tp->decimal))) {
531 buf[1] = '\0';
532 for (ssz = 0, i = 0; cp != &str[i]; i++) {
533 buf[0] = str[i];
534 ssz += (*tbl->slen)(buf, tbl->arg);
535 }
536 d = ssz + psz;
537 } else
538 d = sz + psz;
539
540 /* Padding. */
541
542 sz += (*tbl->len)(2, tbl->arg);
543 d += (*tbl->len)(1, tbl->arg);
544
545 /* Adjust the settings for this column. */
546
547 if (col->decimal > d) {
548 sz += col->decimal - d;
549 d = col->decimal;
550 } else
551 col->width += d - col->decimal;
552
553 if (sz > col->width)
554 col->width = sz;
555 if (d > col->decimal)
556 col->decimal = d;
557
558 /* Adjust for stipulated width. */
559
560 if (col->width < max)
561 col->width = max;
562 }
563
564