]> git.cameronkatri.com Git - mandoc.git/blob - out.c
Ignore .ns (no-space mode), .ps (change point size), .ta (tab control)
[mandoc.git] / out.c
1 /* $Id: out.c,v 1.35 2011/01/11 14:12:01 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 assert(dp->layout);
403 col = &tbl->cols[dp->layout->head->ident];
404 tblcalc_data(tbl, col, sp->tbl, dp);
405 }
406 }
407
408 /*
409 * Calculate width of the spanners. These get one space for a
410 * vertical line, two for a double-vertical line.
411 */
412
413 for ( ; hp; hp = hp->next) {
414 col = &tbl->cols[hp->ident];
415 switch (hp->pos) {
416 case (TBL_HEAD_VERT):
417 col->width = (*tbl->len)(1, tbl->arg);
418 break;
419 case (TBL_HEAD_DVERT):
420 col->width = (*tbl->len)(2, tbl->arg);
421 break;
422 default:
423 break;
424 }
425 }
426 }
427
428 static void
429 tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
430 const struct tbl *tp, const struct tbl_dat *dp)
431 {
432 size_t sz;
433
434 /* Branch down into data sub-types. */
435
436 switch (dp->layout->pos) {
437 case (TBL_CELL_HORIZ):
438 /* FALLTHROUGH */
439 case (TBL_CELL_DHORIZ):
440 sz = (*tbl->len)(1, tbl->arg);
441 if (col->width < sz)
442 col->width = sz;
443 break;
444 case (TBL_CELL_LONG):
445 /* FALLTHROUGH */
446 case (TBL_CELL_CENTRE):
447 /* FALLTHROUGH */
448 case (TBL_CELL_LEFT):
449 /* FALLTHROUGH */
450 case (TBL_CELL_RIGHT):
451 tblcalc_literal(tbl, col, dp);
452 break;
453 case (TBL_CELL_NUMBER):
454 tblcalc_number(tbl, col, tp, dp);
455 break;
456 case (TBL_CELL_DOWN):
457 break;
458 default:
459 abort();
460 /* NOTREACHED */
461 }
462 }
463
464 static void
465 tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
466 const struct tbl_dat *dp)
467 {
468 size_t sz, bufsz, spsz;
469 const char *str;
470
471 /*
472 * Calculate our width and use the spacing, with a minimum
473 * spacing dictated by position (centre, e.g,. gets a space on
474 * either side, while right/left get a single adjacent space).
475 */
476
477 bufsz = spsz = 0;
478 str = dp->string ? dp->string : "";
479 sz = (*tbl->slen)(str, tbl->arg);
480
481 /* FIXME: TBL_DATA_HORIZ et al.? */
482
483 assert(dp->layout);
484 switch (dp->layout->pos) {
485 case (TBL_CELL_LONG):
486 /* FALLTHROUGH */
487 case (TBL_CELL_CENTRE):
488 bufsz = (*tbl->len)(2, tbl->arg);
489 break;
490 default:
491 bufsz = (*tbl->len)(1, tbl->arg);
492 break;
493 }
494
495 if (dp->layout->spacing) {
496 spsz = (*tbl->len)(dp->layout->spacing, tbl->arg);
497 bufsz = bufsz > spsz ? bufsz : spsz;
498 }
499
500 sz += bufsz;
501 if (col->width < sz)
502 col->width = sz;
503 }
504
505 static void
506 tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
507 const struct tbl *tp, const struct tbl_dat *dp)
508 {
509 int i;
510 size_t sz, psz, ssz, d;
511 const char *str;
512 char *cp;
513 char buf[2];
514
515 /*
516 * First calculate number width and decimal place (last + 1 for
517 * no-decimal numbers). If the stored decimal is subsequent
518 * ours, make our size longer by that difference
519 * (right-"shifting"); similarly, if ours is subsequent the
520 * stored, then extend the stored size by the difference.
521 * Finally, re-assign the stored values.
522 */
523
524 str = dp->string ? dp->string : "";
525 sz = (*tbl->slen)(str, tbl->arg);
526
527 /* FIXME: TBL_DATA_HORIZ et al.? */
528
529 buf[0] = tp->decimal;
530 buf[1] = '\0';
531
532 psz = (*tbl->slen)(buf, tbl->arg);
533
534 if (NULL != (cp = strrchr(str, tp->decimal))) {
535 buf[1] = '\0';
536 for (ssz = 0, i = 0; cp != &str[i]; i++) {
537 buf[0] = str[i];
538 ssz += (*tbl->slen)(buf, tbl->arg);
539 }
540 d = ssz + psz;
541 } else
542 d = sz + psz;
543
544 /* Padding. */
545
546 sz += (*tbl->len)(2, tbl->arg);
547 d += (*tbl->len)(1, tbl->arg);
548
549 /* Adjust the settings for this column. */
550
551 if (col->decimal > d) {
552 sz += col->decimal - d;
553 d = col->decimal;
554 } else
555 col->width += d - col->decimal;
556
557 if (sz > col->width)
558 col->width = sz;
559 if (d > col->decimal)
560 col->decimal = d;
561
562 /* Adjust for stipulated width. */
563
564 if (col->width < dp->layout->spacing)
565 col->width = dp->layout->spacing;
566 }
567
568