Annotation of mandoc/man_term.c, Revision 1.18
1.18 ! kristaps 1: /* $Id: man_term.c,v 1.17 2009/07/24 20:22:24 kristaps Exp $ */
1.1 kristaps 2: /*
1.9 kristaps 3: * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
1.1 kristaps 4: *
5: * Permission to use, copy, modify, and distribute this software for any
1.8 kristaps 6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
1.1 kristaps 8: *
1.8 kristaps 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.
1.1 kristaps 16: */
17: #include <assert.h>
1.18 ! kristaps 18: #include <ctype.h>
1.1 kristaps 19: #include <err.h>
20: #include <stdio.h>
21: #include <stdlib.h>
22: #include <string.h>
23:
24: #include "term.h"
25: #include "man.h"
26:
1.18 ! kristaps 27: #define INDENT 7
! 28: #define HALFINDENT 3
! 29:
1.2 kristaps 30: #ifdef __linux__
31: extern size_t strlcpy(char *, const char *, size_t);
32: extern size_t strlcat(char *, const char *, size_t);
33: #endif
34:
1.1 kristaps 35: #define DECL_ARGS struct termp *p, \
36: const struct man_node *n, \
37: const struct man_meta *m
38:
39: struct termact {
40: int (*pre)(DECL_ARGS);
41: void (*post)(DECL_ARGS);
42: };
43:
44: static int pre_B(DECL_ARGS);
1.3 kristaps 45: static int pre_BI(DECL_ARGS);
46: static int pre_BR(DECL_ARGS);
1.15 kristaps 47: static int pre_br(DECL_ARGS);
1.1 kristaps 48: static int pre_I(DECL_ARGS);
1.3 kristaps 49: static int pre_IB(DECL_ARGS);
1.4 kristaps 50: static int pre_IP(DECL_ARGS);
1.3 kristaps 51: static int pre_IR(DECL_ARGS);
1.1 kristaps 52: static int pre_PP(DECL_ARGS);
1.3 kristaps 53: static int pre_RB(DECL_ARGS);
54: static int pre_RI(DECL_ARGS);
1.1 kristaps 55: static int pre_SH(DECL_ARGS);
56: static int pre_SS(DECL_ARGS);
57: static int pre_TP(DECL_ARGS);
58:
59: static void post_B(DECL_ARGS);
60: static void post_I(DECL_ARGS);
61: static void post_SH(DECL_ARGS);
62: static void post_SS(DECL_ARGS);
63:
64: static const struct termact termacts[MAN_MAX] = {
1.15 kristaps 65: { pre_br, NULL }, /* br */
1.1 kristaps 66: { NULL, NULL }, /* TH */
67: { pre_SH, post_SH }, /* SH */
68: { pre_SS, post_SS }, /* SS */
69: { pre_TP, NULL }, /* TP */
70: { pre_PP, NULL }, /* LP */
71: { pre_PP, NULL }, /* PP */
72: { pre_PP, NULL }, /* P */
1.4 kristaps 73: { pre_IP, NULL }, /* IP */
74: { pre_PP, NULL }, /* HP */ /* FIXME */
1.1 kristaps 75: { NULL, NULL }, /* SM */
76: { pre_B, post_B }, /* SB */
1.3 kristaps 77: { pre_BI, NULL }, /* BI */
78: { pre_IB, NULL }, /* IB */
79: { pre_BR, NULL }, /* BR */
80: { pre_RB, NULL }, /* RB */
1.1 kristaps 81: { NULL, NULL }, /* R */
82: { pre_B, post_B }, /* B */
83: { pre_I, post_I }, /* I */
1.3 kristaps 84: { pre_IR, NULL }, /* IR */
85: { pre_RI, NULL }, /* RI */
1.6 kristaps 86: { NULL, NULL }, /* na */
1.7 kristaps 87: { pre_I, post_I }, /* i */
1.17 kristaps 88: { NULL, NULL }, /* sp */
1.1 kristaps 89: };
90:
91: static void print_head(struct termp *,
92: const struct man_meta *);
93: static void print_body(DECL_ARGS);
94: static void print_node(DECL_ARGS);
95: static void print_foot(struct termp *,
96: const struct man_meta *);
1.18 ! kristaps 97: static void fmt_block_vspace(struct termp *,
! 98: const struct man_node *);
! 99: static int arg_width(const struct man_node *);
1.1 kristaps 100:
101:
102: int
103: man_run(struct termp *p, const struct man *m)
104: {
105:
106: print_head(p, man_meta(m));
107: p->flags |= TERMP_NOSPACE;
1.14 kristaps 108: assert(man_node(m));
109: assert(MAN_ROOT == man_node(m)->type);
110: if (man_node(m)->child)
111: print_body(p, man_node(m)->child, man_meta(m));
1.1 kristaps 112: print_foot(p, man_meta(m));
113:
114: return(1);
115: }
116:
117:
1.18 ! kristaps 118: static void
! 119: fmt_block_vspace(struct termp *p, const struct man_node *n)
! 120: {
! 121: term_newln(p);
! 122:
! 123: if (NULL == n->prev)
! 124: return;
! 125:
! 126: if (MAN_SS == n->prev->tok)
! 127: return;
! 128: if (MAN_SH == n->prev->tok)
! 129: return;
! 130:
! 131: term_vspace(p);
! 132: }
! 133:
! 134:
! 135: static int
! 136: arg_width(const struct man_node *n)
! 137: {
! 138: int i, len;
! 139: const char *p;
! 140:
! 141: assert(MAN_TEXT == n->type);
! 142: assert(n->string);
! 143:
! 144: p = n->string;
! 145:
! 146: if (0 == (len = (int)strlen(p)))
! 147: return(-1);
! 148:
! 149: for (i = 0; i < len; i++)
! 150: if ( ! isdigit((u_char)p[i]))
! 151: break;
! 152:
! 153: if (i == len - 1) {
! 154: if ('n' == p[len - 1] || 'm' == p[len - 1])
! 155: return(atoi(p));
! 156: } else if (i == len)
! 157: return(atoi(p));
! 158:
! 159: return(-1);
! 160: }
! 161:
! 162:
1.3 kristaps 163: /* ARGSUSED */
1.1 kristaps 164: static int
165: pre_I(DECL_ARGS)
166: {
167:
168: p->flags |= TERMP_UNDER;
169: return(1);
170: }
171:
172:
1.3 kristaps 173: /* ARGSUSED */
1.1 kristaps 174: static void
175: post_I(DECL_ARGS)
176: {
177:
178: p->flags &= ~TERMP_UNDER;
179: }
180:
181:
1.3 kristaps 182: /* ARGSUSED */
183: static int
184: pre_IR(DECL_ARGS)
185: {
186: const struct man_node *nn;
187: int i;
188:
189: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
190: if ( ! (i % 2))
191: p->flags |= TERMP_UNDER;
1.4 kristaps 192: if (i > 0)
193: p->flags |= TERMP_NOSPACE;
1.3 kristaps 194: print_node(p, nn, m);
195: if ( ! (i % 2))
196: p->flags &= ~TERMP_UNDER;
197: }
198: return(0);
199: }
200:
201:
202: /* ARGSUSED */
203: static int
204: pre_IB(DECL_ARGS)
205: {
206: const struct man_node *nn;
207: int i;
208:
209: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
210: p->flags |= i % 2 ? TERMP_BOLD : TERMP_UNDER;
1.4 kristaps 211: if (i > 0)
212: p->flags |= TERMP_NOSPACE;
1.3 kristaps 213: print_node(p, nn, m);
214: p->flags &= i % 2 ? ~TERMP_BOLD : ~TERMP_UNDER;
215: }
216: return(0);
217: }
218:
219:
220: /* ARGSUSED */
221: static int
222: pre_RB(DECL_ARGS)
223: {
224: const struct man_node *nn;
225: int i;
226:
227: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
228: if (i % 2)
229: p->flags |= TERMP_BOLD;
1.4 kristaps 230: if (i > 0)
231: p->flags |= TERMP_NOSPACE;
1.3 kristaps 232: print_node(p, nn, m);
233: if (i % 2)
234: p->flags &= ~TERMP_BOLD;
235: }
236: return(0);
237: }
238:
239:
240: /* ARGSUSED */
241: static int
242: pre_RI(DECL_ARGS)
243: {
244: const struct man_node *nn;
245: int i;
246:
247: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
248: if ( ! (i % 2))
249: p->flags |= TERMP_UNDER;
1.4 kristaps 250: if (i > 0)
251: p->flags |= TERMP_NOSPACE;
1.3 kristaps 252: print_node(p, nn, m);
253: if ( ! (i % 2))
254: p->flags &= ~TERMP_UNDER;
255: }
256: return(0);
257: }
258:
259:
260: /* ARGSUSED */
261: static int
262: pre_BR(DECL_ARGS)
263: {
264: const struct man_node *nn;
265: int i;
266:
267: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
268: if ( ! (i % 2))
269: p->flags |= TERMP_BOLD;
1.4 kristaps 270: if (i > 0)
271: p->flags |= TERMP_NOSPACE;
1.3 kristaps 272: print_node(p, nn, m);
273: if ( ! (i % 2))
274: p->flags &= ~TERMP_BOLD;
275: }
276: return(0);
277: }
278:
279:
280: /* ARGSUSED */
281: static int
282: pre_BI(DECL_ARGS)
283: {
284: const struct man_node *nn;
285: int i;
286:
287: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
288: p->flags |= i % 2 ? TERMP_UNDER : TERMP_BOLD;
1.4 kristaps 289: if (i > 0)
290: p->flags |= TERMP_NOSPACE;
1.3 kristaps 291: print_node(p, nn, m);
292: p->flags &= i % 2 ? ~TERMP_UNDER : ~TERMP_BOLD;
293: }
294: return(0);
295: }
296:
297:
298: /* ARGSUSED */
1.1 kristaps 299: static int
300: pre_B(DECL_ARGS)
301: {
302:
303: p->flags |= TERMP_BOLD;
304: return(1);
305: }
306:
307:
1.3 kristaps 308: /* ARGSUSED */
1.1 kristaps 309: static void
310: post_B(DECL_ARGS)
311: {
312:
313: p->flags &= ~TERMP_BOLD;
314: }
315:
316:
1.3 kristaps 317: /* ARGSUSED */
1.1 kristaps 318: static int
1.15 kristaps 319: pre_br(DECL_ARGS)
320: {
321:
322: term_newln(p);
323: return(0);
324: }
325:
326:
327: /* ARGSUSED */
328: static int
1.1 kristaps 329: pre_PP(DECL_ARGS)
330: {
331:
332: term_vspace(p);
1.18 ! kristaps 333: term_vspace(p);
1.1 kristaps 334: p->offset = INDENT;
1.18 ! kristaps 335: p->flags |= TERMP_NOSPACE;
1.1 kristaps 336: return(0);
337: }
338:
339:
1.3 kristaps 340: /* ARGSUSED */
1.1 kristaps 341: static int
1.4 kristaps 342: pre_IP(DECL_ARGS)
343: {
344: const struct man_node *nn;
1.18 ! kristaps 345: size_t offs, sv;
! 346: int ival;
! 347:
! 348: fmt_block_vspace(p, n);
! 349:
! 350: p->flags |= TERMP_NOSPACE;
1.4 kristaps 351:
1.18 ! kristaps 352: sv = p->offset;
1.4 kristaps 353: p->offset = INDENT;
354:
1.18 ! kristaps 355: if (NULL == n->child)
1.4 kristaps 356: return(1);
357:
1.18 ! kristaps 358: p->flags |= TERMP_NOBREAK;
! 359:
! 360: offs = sv;
! 361:
! 362: /*
! 363: * If the last token is number-looking (3m, 3n, 3) then
! 364: * interpret it as the width specifier, else we stick with the
! 365: * prior saved offset. XXX - obviously not documented.
! 366: */
! 367: for (nn = n->child; nn; nn = nn->next) {
! 368: if (NULL == nn->next) {
! 369: ival = arg_width(nn);
! 370: if (ival >= 0) {
! 371: offs = (size_t)ival;
! 372: break;
! 373: }
! 374: }
! 375: print_node(p, nn, m);
! 376: }
! 377:
! 378: p->rmargin = p->offset + offs;
! 379:
! 380: term_flushln(p);
! 381:
! 382: p->offset = offs;
! 383: p->rmargin = p->maxrmargin;
1.4 kristaps 384:
1.18 ! kristaps 385: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1.12 kristaps 386:
1.4 kristaps 387: return(0);
388: }
389:
390:
391: /* ARGSUSED */
392: static int
1.1 kristaps 393: pre_TP(DECL_ARGS)
394: {
395: const struct man_node *nn;
396: size_t offs;
397:
398: term_vspace(p);
1.16 kristaps 399:
1.1 kristaps 400: p->offset = INDENT;
401:
402: if (NULL == (nn = n->child))
403: return(1);
404:
405: if (nn->line == n->line) {
406: if (MAN_TEXT != nn->type)
407: errx(1, "expected text line argument");
1.3 kristaps 408: offs = (size_t)atoi(nn->string);
1.1 kristaps 409: nn = nn->next;
410: } else
411: offs = INDENT;
412:
413: for ( ; nn; nn = nn->next)
414: print_node(p, nn, m);
415:
416: term_flushln(p);
417: p->flags |= TERMP_NOSPACE;
418: p->offset += offs;
419: return(0);
420: }
421:
422:
1.3 kristaps 423: /* ARGSUSED */
1.1 kristaps 424: static int
425: pre_SS(DECL_ARGS)
426: {
427:
428: term_vspace(p);
429: p->flags |= TERMP_BOLD;
430: return(1);
431: }
432:
433:
1.3 kristaps 434: /* ARGSUSED */
1.1 kristaps 435: static void
436: post_SS(DECL_ARGS)
437: {
438:
439: term_flushln(p);
440: p->flags &= ~TERMP_BOLD;
441: p->flags |= TERMP_NOSPACE;
442: }
443:
444:
1.3 kristaps 445: /* ARGSUSED */
1.1 kristaps 446: static int
447: pre_SH(DECL_ARGS)
448: {
449:
450: term_vspace(p);
451: p->offset = 0;
452: p->flags |= TERMP_BOLD;
453: return(1);
454: }
455:
456:
1.3 kristaps 457: /* ARGSUSED */
1.1 kristaps 458: static void
459: post_SH(DECL_ARGS)
460: {
461:
462: term_flushln(p);
463: p->offset = INDENT;
464: p->flags &= ~TERMP_BOLD;
465: p->flags |= TERMP_NOSPACE;
466: }
467:
468:
469: static void
470: print_node(DECL_ARGS)
471: {
1.4 kristaps 472: int c, sz;
1.1 kristaps 473:
474: c = 1;
475:
476: switch (n->type) {
477: case(MAN_ELEM):
478: if (termacts[n->tok].pre)
479: c = (*termacts[n->tok].pre)(p, n, m);
480: break;
481: case(MAN_TEXT):
1.4 kristaps 482: if (0 == *n->string) {
483: term_vspace(p);
1.1 kristaps 484: break;
485: }
1.4 kristaps 486: /*
487: * Note! This is hacky. Here, we recognise the `\c'
488: * escape embedded in so many -man pages. It's supposed
489: * to remove the subsequent space, so we mark NOSPACE if
490: * it's encountered in the string.
491: */
492: sz = (int)strlen(n->string);
493: term_word(p, n->string);
494: if (sz >= 2 && n->string[sz - 1] == 'c' &&
495: n->string[sz - 2] == '\\')
496: p->flags |= TERMP_NOSPACE;
1.1 kristaps 497: break;
498: default:
499: break;
500: }
501:
502: if (c && n->child)
503: print_body(p, n->child, m);
504:
505: switch (n->type) {
506: case (MAN_ELEM):
507: if (termacts[n->tok].post)
508: (*termacts[n->tok].post)(p, n, m);
509: break;
510: default:
511: break;
512: }
513: }
514:
515:
516: static void
517: print_body(DECL_ARGS)
518: {
519: print_node(p, n, m);
520: if ( ! n->next)
521: return;
522: print_body(p, n->next, m);
523: }
524:
525:
526: static void
527: print_foot(struct termp *p, const struct man_meta *meta)
528: {
529: struct tm *tm;
530: char *buf;
531:
532: if (NULL == (buf = malloc(p->rmargin)))
533: err(1, "malloc");
534:
535: tm = localtime(&meta->date);
536:
537: if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
538: err(1, "strftime");
539:
540: term_vspace(p);
541:
542: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
543: p->rmargin = p->maxrmargin - strlen(buf);
544: p->offset = 0;
545:
546: if (meta->source)
547: term_word(p, meta->source);
548: if (meta->source)
549: term_word(p, "");
550: term_flushln(p);
551:
552: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
553: p->offset = p->rmargin;
554: p->rmargin = p->maxrmargin;
555: p->flags &= ~TERMP_NOBREAK;
556:
557: term_word(p, buf);
558: term_flushln(p);
559:
560: free(buf);
561: }
562:
563:
564: static void
565: print_head(struct termp *p, const struct man_meta *meta)
566: {
567: char *buf, *title;
568:
569: p->rmargin = p->maxrmargin;
570: p->offset = 0;
571:
572: if (NULL == (buf = malloc(p->rmargin)))
573: err(1, "malloc");
574: if (NULL == (title = malloc(p->rmargin)))
575: err(1, "malloc");
576:
577: if (meta->vol)
578: (void)strlcpy(buf, meta->vol, p->rmargin);
579: else
580: *buf = 0;
581:
582: (void)snprintf(title, p->rmargin, "%s(%d)",
583: meta->title, meta->msec);
584:
585: p->offset = 0;
1.10 kristaps 586: p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
1.1 kristaps 587: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
588:
589: term_word(p, title);
590: term_flushln(p);
591:
592: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
593: p->offset = p->rmargin;
594: p->rmargin = p->maxrmargin - strlen(title);
595:
596: term_word(p, buf);
597: term_flushln(p);
598:
599: p->offset = p->rmargin;
600: p->rmargin = p->maxrmargin;
601: p->flags &= ~TERMP_NOBREAK;
602: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
603:
604: term_word(p, title);
605: term_flushln(p);
606:
607: p->rmargin = p->maxrmargin;
608: p->offset = 0;
609: p->flags &= ~TERMP_NOSPACE;
610:
611: free(title);
612: free(buf);
613: }
614:
CVSweb