]> git.cameronkatri.com Git - mandoc.git/blob - out.c
Tiny optimisation in mandoc_isdelim() check.
[mandoc.git] / out.c
1 /* $Id: out.c,v 1.39 2011/03/17 08:49:34 kristaps Exp $ */
2 /*
3 * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include <sys/types.h>
23
24 #include <assert.h>
25 #include <ctype.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30
31 #include "mandoc.h"
32 #include "out.h"
33
34 static void tblcalc_data(struct rofftbl *, struct roffcol *,
35 const struct tbl *, const struct tbl_dat *);
36 static void tblcalc_literal(struct rofftbl *, struct roffcol *,
37 const struct tbl_dat *);
38 static void tblcalc_number(struct rofftbl *, struct roffcol *,
39 const struct tbl *, const struct tbl_dat *);
40
41 /*
42 * Convert a `scaling unit' to a consistent form, or fail. Scaling
43 * units are documented in groff.7, mdoc.7, man.7.
44 */
45 int
46 a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
47 {
48 char buf[BUFSIZ], hasd;
49 int i;
50 enum roffscale unit;
51
52 if ('\0' == *src)
53 return(0);
54
55 i = hasd = 0;
56
57 switch (*src) {
58 case ('+'):
59 src++;
60 break;
61 case ('-'):
62 buf[i++] = *src++;
63 break;
64 default:
65 break;
66 }
67
68 if ('\0' == *src)
69 return(0);
70
71 while (i < BUFSIZ) {
72 if ( ! isdigit((u_char)*src)) {
73 if ('.' != *src)
74 break;
75 else if (hasd)
76 break;
77 else
78 hasd = 1;
79 }
80 buf[i++] = *src++;
81 }
82
83 if (BUFSIZ == i || (*src && *(src + 1)))
84 return(0);
85
86 buf[i] = '\0';
87
88 switch (*src) {
89 case ('c'):
90 unit = SCALE_CM;
91 break;
92 case ('i'):
93 unit = SCALE_IN;
94 break;
95 case ('P'):
96 unit = SCALE_PC;
97 break;
98 case ('p'):
99 unit = SCALE_PT;
100 break;
101 case ('f'):
102 unit = SCALE_FS;
103 break;
104 case ('v'):
105 unit = SCALE_VS;
106 break;
107 case ('m'):
108 unit = SCALE_EM;
109 break;
110 case ('\0'):
111 if (SCALE_MAX == def)
112 return(0);
113 unit = SCALE_BU;
114 break;
115 case ('u'):
116 unit = SCALE_BU;
117 break;
118 case ('M'):
119 unit = SCALE_MM;
120 break;
121 case ('n'):
122 unit = SCALE_EN;
123 break;
124 default:
125 return(0);
126 }
127
128 /* FIXME: do this in the caller. */
129 if ((dst->scale = atof(buf)) < 0)
130 dst->scale = 0;
131 dst->unit = unit;
132 return(1);
133 }
134
135
136 /*
137 * Correctly writes the time in nroff form, which differs from standard
138 * form in that a space isn't printed in lieu of the extra %e field for
139 * single-digit dates.
140 */
141 void
142 time2a(time_t t, char *dst, size_t sz)
143 {
144 struct tm tm;
145 char buf[5];
146 char *p;
147 size_t nsz;
148
149 assert(sz > 1);
150 localtime_r(&t, &tm);
151
152 p = dst;
153 nsz = 0;
154
155 dst[0] = '\0';
156
157 if (0 == (nsz = strftime(p, sz, "%B ", &tm)))
158 return;
159
160 p += (int)nsz;
161 sz -= nsz;
162
163 if (0 == strftime(buf, sizeof(buf), "%e, ", &tm))
164 return;
165
166 nsz = strlcat(p, buf + (' ' == buf[0] ? 1 : 0), sz);
167
168 if (nsz >= sz)
169 return;
170
171 p += (int)nsz;
172 sz -= nsz;
173
174 (void)strftime(p, sz, "%Y", &tm);
175 }
176
177
178 int
179 a2roffdeco(enum roffdeco *d, const char **word, size_t *sz)
180 {
181 int i, j, lim;
182 char term, c;
183 const char *wp;
184 enum roffdeco dd;
185
186 *d = DECO_NONE;
187 lim = i = 0;
188 term = '\0';
189 wp = *word;
190
191 switch ((c = wp[i++])) {
192 case ('('):
193 *d = DECO_SPECIAL;
194 lim = 2;
195 break;
196 case ('F'):
197 /* FALLTHROUGH */
198 case ('f'):
199 *d = 'F' == c ? DECO_FFONT : DECO_FONT;
200
201 switch (wp[i++]) {
202 case ('('):
203 lim = 2;
204 break;
205 case ('['):
206 term = ']';
207 break;
208 case ('3'):
209 /* FALLTHROUGH */
210 case ('B'):
211 *d = DECO_BOLD;
212 return(i);
213 case ('2'):
214 /* FALLTHROUGH */
215 case ('I'):
216 *d = DECO_ITALIC;
217 return(i);
218 case ('P'):
219 *d = DECO_PREVIOUS;
220 return(i);
221 case ('1'):
222 /* FALLTHROUGH */
223 case ('R'):
224 *d = DECO_ROMAN;
225 return(i);
226 default:
227 i--;
228 lim = 1;
229 break;
230 }
231 break;
232 case ('k'):
233 /* FALLTHROUGH */
234 case ('M'):
235 /* FALLTHROUGH */
236 case ('m'):
237 /* FALLTHROUGH */
238 case ('*'):
239 if ('*' == c)
240 *d = DECO_RESERVED;
241
242 switch (wp[i++]) {
243 case ('('):
244 lim = 2;
245 break;
246 case ('['):
247 term = ']';
248 break;
249 default:
250 i--;
251 lim = 1;
252 break;
253 }
254 break;
255
256 case ('N'):
257
258 /*
259 * Sequence of characters: backslash, 'N' (i = 0),
260 * starting delimiter (i = 1), character number (i = 2).
261 */
262
263 *word = wp + 2;
264 *sz = 0;
265
266 /*
267 * Cannot use a digit as a starting delimiter;
268 * but skip the digit anyway.
269 */
270
271 if (isdigit((int)wp[1]))
272 return(2);
273
274 /*
275 * Any non-digit terminates the character number.
276 * That is, the terminating delimiter need not
277 * match the starting delimiter.
278 */
279
280 for (i = 2; isdigit((int)wp[i]); i++)
281 (*sz)++;
282
283 /*
284 * This is only a numbered character
285 * if the character number has at least one digit.
286 */
287
288 if (*sz)
289 *d = DECO_NUMBERED;
290
291 /*
292 * Skip the terminating delimiter, even if it does not
293 * match, and even if there is no character number.
294 */
295
296 return(++i);
297
298 case ('h'):
299 /* FALLTHROUGH */
300 case ('v'):
301 /* FALLTHROUGH */
302 case ('s'):
303 j = 0;
304 if ('+' == wp[i] || '-' == wp[i]) {
305 i++;
306 j = 1;
307 }
308
309 switch (wp[i++]) {
310 case ('('):
311 lim = 2;
312 break;
313 case ('['):
314 term = ']';
315 break;
316 case ('\''):
317 term = '\'';
318 break;
319 case ('0'):
320 j = 1;
321 /* FALLTHROUGH */
322 default:
323 i--;
324 lim = 1;
325 break;
326 }
327
328 if ('+' == wp[i] || '-' == wp[i]) {
329 if (j)
330 return(i);
331 i++;
332 }
333
334 /* Handle embedded numerical subexp or escape. */
335
336 if ('(' == wp[i]) {
337 while (wp[i] && ')' != wp[i])
338 if ('\\' == wp[i++]) {
339 /* Handle embedded escape. */
340 *word = &wp[i];
341 i += a2roffdeco(&dd, word, sz);
342 }
343
344 if (')' == wp[i++])
345 break;
346
347 *d = DECO_NONE;
348 return(i - 1);
349 } else if ('\\' == wp[i]) {
350 *word = &wp[++i];
351 i += a2roffdeco(&dd, word, sz);
352 }
353
354 break;
355 case ('['):
356 *d = DECO_SPECIAL;
357 term = ']';
358 break;
359 case ('c'):
360 *d = DECO_NOSPACE;
361 return(i);
362 case ('z'):
363 *d = DECO_NONE;
364 if ('\\' == wp[i]) {
365 *word = &wp[++i];
366 return(i + a2roffdeco(&dd, word, sz));
367 } else
368 lim = 1;
369 break;
370 case ('o'):
371 /* FALLTHROUGH */
372 case ('w'):
373 if ('\'' == wp[i++]) {
374 term = '\'';
375 break;
376 }
377 /* FALLTHROUGH */
378 default:
379 *d = DECO_SSPECIAL;
380 i--;
381 lim = 1;
382 break;
383 }
384
385 assert(term || lim);
386 *word = &wp[i];
387
388 if (term) {
389 j = i;
390 while (wp[i] && wp[i] != term)
391 i++;
392 if ('\0' == wp[i]) {
393 *d = DECO_NONE;
394 return(i);
395 }
396
397 assert(i >= j);
398 *sz = (size_t)(i - j);
399
400 return(i + 1);
401 }
402
403 assert(lim > 0);
404 *sz = (size_t)lim;
405
406 for (j = 0; wp[i] && j < lim; j++)
407 i++;
408 if (j < lim)
409 *d = DECO_NONE;
410
411 return(i);
412 }
413
414 /*
415 * Calculate the abstract widths and decimal positions of columns in a
416 * table. This routine allocates the columns structures then runs over
417 * all rows and cells in the table. The function pointers in "tbl" are
418 * used for the actual width calculations.
419 */
420 void
421 tblcalc(struct rofftbl *tbl, const struct tbl_span *sp)
422 {
423 const struct tbl_dat *dp;
424 const struct tbl_head *hp;
425 struct roffcol *col;
426
427 /*
428 * Allocate the master column specifiers. These will hold the
429 * widths and decimal positions for all cells in the column. It
430 * must be freed and nullified by the caller.
431 */
432
433 assert(NULL == tbl->cols);
434 tbl->cols = mandoc_calloc
435 ((size_t)sp->tbl->cols, sizeof(struct roffcol));
436
437 hp = sp->head;
438
439 for ( ; sp; sp = sp->next) {
440 if (TBL_SPAN_DATA != sp->pos)
441 continue;
442 /*
443 * Account for the data cells in the layout, matching it
444 * to data cells in the data section.
445 */
446 for (dp = sp->first; dp; dp = dp->next) {
447 assert(dp->layout);
448 col = &tbl->cols[dp->layout->head->ident];
449 tblcalc_data(tbl, col, sp->tbl, dp);
450 }
451 }
452
453 /*
454 * Calculate width of the spanners. These get one space for a
455 * vertical line, two for a double-vertical line.
456 */
457
458 for ( ; hp; hp = hp->next) {
459 col = &tbl->cols[hp->ident];
460 switch (hp->pos) {
461 case (TBL_HEAD_VERT):
462 col->width = (*tbl->len)(1, tbl->arg);
463 break;
464 case (TBL_HEAD_DVERT):
465 col->width = (*tbl->len)(2, tbl->arg);
466 break;
467 default:
468 break;
469 }
470 }
471 }
472
473 static void
474 tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
475 const struct tbl *tp, const struct tbl_dat *dp)
476 {
477 size_t sz;
478
479 /* Branch down into data sub-types. */
480
481 switch (dp->layout->pos) {
482 case (TBL_CELL_HORIZ):
483 /* FALLTHROUGH */
484 case (TBL_CELL_DHORIZ):
485 sz = (*tbl->len)(1, tbl->arg);
486 if (col->width < sz)
487 col->width = sz;
488 break;
489 case (TBL_CELL_LONG):
490 /* FALLTHROUGH */
491 case (TBL_CELL_CENTRE):
492 /* FALLTHROUGH */
493 case (TBL_CELL_LEFT):
494 /* FALLTHROUGH */
495 case (TBL_CELL_RIGHT):
496 tblcalc_literal(tbl, col, dp);
497 break;
498 case (TBL_CELL_NUMBER):
499 tblcalc_number(tbl, col, tp, dp);
500 break;
501 case (TBL_CELL_DOWN):
502 break;
503 default:
504 abort();
505 /* NOTREACHED */
506 }
507 }
508
509 static void
510 tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
511 const struct tbl_dat *dp)
512 {
513 size_t sz, bufsz, spsz;
514 const char *str;
515
516 /*
517 * Calculate our width and use the spacing, with a minimum
518 * spacing dictated by position (centre, e.g,. gets a space on
519 * either side, while right/left get a single adjacent space).
520 */
521
522 bufsz = spsz = 0;
523 str = dp->string ? dp->string : "";
524 sz = (*tbl->slen)(str, tbl->arg);
525
526 /* FIXME: TBL_DATA_HORIZ et al.? */
527
528 assert(dp->layout);
529 switch (dp->layout->pos) {
530 case (TBL_CELL_LONG):
531 /* FALLTHROUGH */
532 case (TBL_CELL_CENTRE):
533 bufsz = (*tbl->len)(1, tbl->arg);
534 break;
535 default:
536 bufsz = (*tbl->len)(1, tbl->arg);
537 break;
538 }
539
540 if (dp->layout->spacing) {
541 spsz = (*tbl->len)(dp->layout->spacing, tbl->arg);
542 bufsz = bufsz > spsz ? bufsz : spsz;
543 }
544
545 sz += bufsz;
546 if (col->width < sz)
547 col->width = sz;
548 }
549
550 static void
551 tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
552 const struct tbl *tp, const struct tbl_dat *dp)
553 {
554 int i;
555 size_t sz, psz, ssz, d;
556 const char *str;
557 char *cp;
558 char buf[2];
559
560 /*
561 * First calculate number width and decimal place (last + 1 for
562 * no-decimal numbers). If the stored decimal is subsequent
563 * ours, make our size longer by that difference
564 * (right-"shifting"); similarly, if ours is subsequent the
565 * stored, then extend the stored size by the difference.
566 * Finally, re-assign the stored values.
567 */
568
569 str = dp->string ? dp->string : "";
570 sz = (*tbl->slen)(str, tbl->arg);
571
572 /* FIXME: TBL_DATA_HORIZ et al.? */
573
574 buf[0] = tp->decimal;
575 buf[1] = '\0';
576
577 psz = (*tbl->slen)(buf, tbl->arg);
578
579 if (NULL != (cp = strrchr(str, tp->decimal))) {
580 buf[1] = '\0';
581 for (ssz = 0, i = 0; cp != &str[i]; i++) {
582 buf[0] = str[i];
583 ssz += (*tbl->slen)(buf, tbl->arg);
584 }
585 d = ssz + psz;
586 } else
587 d = sz + psz;
588
589 /* Padding. */
590
591 sz += (*tbl->len)(2, tbl->arg);
592 d += (*tbl->len)(1, tbl->arg);
593
594 /* Adjust the settings for this column. */
595
596 if (col->decimal > d) {
597 sz += col->decimal - d;
598 d = col->decimal;
599 } else
600 col->width += d - col->decimal;
601
602 if (sz > col->width)
603 col->width = sz;
604 if (d > col->decimal)
605 col->decimal = d;
606
607 /* Adjust for stipulated width. */
608
609 if (col->width < dp->layout->spacing)
610 col->width = dp->layout->spacing;
611 }
612
613