Annotation of mandoc/man_term.c, Revision 1.3
1.3 ! kristaps 1: /* $Id: man_term.c,v 1.2 2009/03/26 14:44:41 kristaps Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
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
7: * above copyright notice and this permission notice appear in all
8: * copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11: * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12: * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13: * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14: * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15: * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16: * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17: * PERFORMANCE OF THIS SOFTWARE.
18: */
19: #include <assert.h>
20: #include <err.h>
21: #include <stdio.h>
22: #include <stdlib.h>
23: #include <string.h>
24:
25: #include "term.h"
26: #include "man.h"
27:
1.2 kristaps 28: #ifdef __linux__
29: extern size_t strlcpy(char *, const char *, size_t);
30: extern size_t strlcat(char *, const char *, size_t);
31: #endif
32:
1.1 kristaps 33: #define DECL_ARGS struct termp *p, \
34: const struct man_node *n, \
35: const struct man_meta *m
36:
37: struct termact {
38: int (*pre)(DECL_ARGS);
39: void (*post)(DECL_ARGS);
40: };
41:
42: static int pre_B(DECL_ARGS);
1.3 ! kristaps 43: static int pre_BI(DECL_ARGS);
! 44: static int pre_BR(DECL_ARGS);
1.1 kristaps 45: static int pre_I(DECL_ARGS);
1.3 ! kristaps 46: static int pre_IB(DECL_ARGS);
! 47: static int pre_IR(DECL_ARGS);
1.1 kristaps 48: static int pre_PP(DECL_ARGS);
1.3 ! kristaps 49: static int pre_RB(DECL_ARGS);
! 50: static int pre_RI(DECL_ARGS);
1.1 kristaps 51: static int pre_SH(DECL_ARGS);
52: static int pre_SS(DECL_ARGS);
53: static int pre_TP(DECL_ARGS);
54:
55: static void post_B(DECL_ARGS);
56: static void post_I(DECL_ARGS);
57: static void post_SH(DECL_ARGS);
58: static void post_SS(DECL_ARGS);
59:
60: static const struct termact termacts[MAN_MAX] = {
61: { NULL, NULL }, /* __ */
62: { NULL, NULL }, /* TH */
63: { pre_SH, post_SH }, /* SH */
64: { pre_SS, post_SS }, /* SS */
65: { pre_TP, NULL }, /* TP */
66: { pre_PP, NULL }, /* LP */
67: { pre_PP, NULL }, /* PP */
68: { pre_PP, NULL }, /* P */
69: { NULL, NULL }, /* IP */
70: { pre_PP, NULL }, /* HP */ /* XXX */
71: { NULL, NULL }, /* SM */
72: { pre_B, post_B }, /* SB */
1.3 ! kristaps 73: { pre_BI, NULL }, /* BI */
! 74: { pre_IB, NULL }, /* IB */
! 75: { pre_BR, NULL }, /* BR */
! 76: { pre_RB, NULL }, /* RB */
1.1 kristaps 77: { NULL, NULL }, /* R */
78: { pre_B, post_B }, /* B */
79: { pre_I, post_I }, /* I */
1.3 ! kristaps 80: { pre_IR, NULL }, /* IR */
! 81: { pre_RI, NULL }, /* RI */
1.1 kristaps 82: };
83:
84: static void print_head(struct termp *,
85: const struct man_meta *);
86: static void print_body(DECL_ARGS);
87: static void print_node(DECL_ARGS);
88: static void print_foot(struct termp *,
89: const struct man_meta *);
90:
91:
92: int
93: man_run(struct termp *p, const struct man *m)
94: {
95:
96: print_head(p, man_meta(m));
97: p->flags |= TERMP_NOSPACE;
98: print_body(p, man_node(m), man_meta(m));
99: print_foot(p, man_meta(m));
100:
101: return(1);
102: }
103:
104:
1.3 ! kristaps 105: /* ARGSUSED */
1.1 kristaps 106: static int
107: pre_I(DECL_ARGS)
108: {
109:
110: p->flags |= TERMP_UNDER;
111: return(1);
112: }
113:
114:
1.3 ! kristaps 115: /* ARGSUSED */
1.1 kristaps 116: static void
117: post_I(DECL_ARGS)
118: {
119:
120: p->flags &= ~TERMP_UNDER;
121: }
122:
123:
1.3 ! kristaps 124: /* ARGSUSED */
! 125: static int
! 126: pre_IR(DECL_ARGS)
! 127: {
! 128: const struct man_node *nn;
! 129: int i;
! 130:
! 131: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
! 132: if ( ! (i % 2))
! 133: p->flags |= TERMP_UNDER;
! 134: print_node(p, nn, m);
! 135: if ( ! (i % 2))
! 136: p->flags &= ~TERMP_UNDER;
! 137: }
! 138: return(0);
! 139: }
! 140:
! 141:
! 142: /* ARGSUSED */
! 143: static int
! 144: pre_IB(DECL_ARGS)
! 145: {
! 146: const struct man_node *nn;
! 147: int i;
! 148:
! 149: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
! 150: p->flags |= i % 2 ? TERMP_BOLD : TERMP_UNDER;
! 151: print_node(p, nn, m);
! 152: p->flags &= i % 2 ? ~TERMP_BOLD : ~TERMP_UNDER;
! 153: }
! 154: return(0);
! 155: }
! 156:
! 157:
! 158: /* ARGSUSED */
! 159: static int
! 160: pre_RB(DECL_ARGS)
! 161: {
! 162: const struct man_node *nn;
! 163: int i;
! 164:
! 165: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
! 166: if (i % 2)
! 167: p->flags |= TERMP_BOLD;
! 168: print_node(p, nn, m);
! 169: if (i % 2)
! 170: p->flags &= ~TERMP_BOLD;
! 171: }
! 172: return(0);
! 173: }
! 174:
! 175:
! 176: /* ARGSUSED */
! 177: static int
! 178: pre_RI(DECL_ARGS)
! 179: {
! 180: const struct man_node *nn;
! 181: int i;
! 182:
! 183: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
! 184: if ( ! (i % 2))
! 185: p->flags |= TERMP_UNDER;
! 186: print_node(p, nn, m);
! 187: if ( ! (i % 2))
! 188: p->flags &= ~TERMP_UNDER;
! 189: }
! 190: return(0);
! 191: }
! 192:
! 193:
! 194: /* ARGSUSED */
! 195: static int
! 196: pre_BR(DECL_ARGS)
! 197: {
! 198: const struct man_node *nn;
! 199: int i;
! 200:
! 201: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
! 202: if ( ! (i % 2))
! 203: p->flags |= TERMP_BOLD;
! 204: print_node(p, nn, m);
! 205: if ( ! (i % 2))
! 206: p->flags &= ~TERMP_BOLD;
! 207: }
! 208: return(0);
! 209: }
! 210:
! 211:
! 212: /* ARGSUSED */
! 213: static int
! 214: pre_BI(DECL_ARGS)
! 215: {
! 216: const struct man_node *nn;
! 217: int i;
! 218:
! 219: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
! 220: p->flags |= i % 2 ? TERMP_UNDER : TERMP_BOLD;
! 221: print_node(p, nn, m);
! 222: p->flags &= i % 2 ? ~TERMP_UNDER : ~TERMP_BOLD;
! 223: }
! 224: return(0);
! 225: }
! 226:
! 227:
! 228: /* ARGSUSED */
1.1 kristaps 229: static int
230: pre_B(DECL_ARGS)
231: {
232:
233: p->flags |= TERMP_BOLD;
234: return(1);
235: }
236:
237:
1.3 ! kristaps 238: /* ARGSUSED */
1.1 kristaps 239: static void
240: post_B(DECL_ARGS)
241: {
242:
243: p->flags &= ~TERMP_BOLD;
244: }
245:
246:
1.3 ! kristaps 247: /* ARGSUSED */
1.1 kristaps 248: static int
249: pre_PP(DECL_ARGS)
250: {
251:
252: term_vspace(p);
253: p->offset = INDENT;
254: return(0);
255: }
256:
257:
1.3 ! kristaps 258: /* ARGSUSED */
1.1 kristaps 259: static int
260: pre_TP(DECL_ARGS)
261: {
262: const struct man_node *nn;
263: size_t offs;
264:
265: term_vspace(p);
266: p->offset = INDENT;
267:
268: if (NULL == (nn = n->child))
269: return(1);
270:
271: if (nn->line == n->line) {
272: if (MAN_TEXT != nn->type)
273: errx(1, "expected text line argument");
1.3 ! kristaps 274: offs = (size_t)atoi(nn->string);
1.1 kristaps 275: nn = nn->next;
276: } else
277: offs = INDENT;
278:
279: for ( ; nn; nn = nn->next)
280: print_node(p, nn, m);
281:
282: term_flushln(p);
283: p->flags |= TERMP_NOSPACE;
284: p->offset += offs;
285: return(0);
286: }
287:
288:
1.3 ! kristaps 289: /* ARGSUSED */
1.1 kristaps 290: static int
291: pre_SS(DECL_ARGS)
292: {
293:
294: term_vspace(p);
295: p->flags |= TERMP_BOLD;
296: return(1);
297: }
298:
299:
1.3 ! kristaps 300: /* ARGSUSED */
1.1 kristaps 301: static void
302: post_SS(DECL_ARGS)
303: {
304:
305: term_flushln(p);
306: p->flags &= ~TERMP_BOLD;
307: p->flags |= TERMP_NOSPACE;
308: }
309:
310:
1.3 ! kristaps 311: /* ARGSUSED */
1.1 kristaps 312: static int
313: pre_SH(DECL_ARGS)
314: {
315:
316: term_vspace(p);
317: p->offset = 0;
318: p->flags |= TERMP_BOLD;
319: return(1);
320: }
321:
322:
1.3 ! kristaps 323: /* ARGSUSED */
1.1 kristaps 324: static void
325: post_SH(DECL_ARGS)
326: {
327:
328: term_flushln(p);
329: p->offset = INDENT;
330: p->flags &= ~TERMP_BOLD;
331: p->flags |= TERMP_NOSPACE;
332: }
333:
334:
335: static void
336: print_node(DECL_ARGS)
337: {
338: int c;
339:
340: c = 1;
341:
342: switch (n->type) {
343: case(MAN_ELEM):
344: if (termacts[n->tok].pre)
345: c = (*termacts[n->tok].pre)(p, n, m);
346: break;
347: case(MAN_TEXT):
348: if (*n->string) {
349: term_word(p, n->string);
350: break;
351: }
352: term_vspace(p);
353: break;
354: default:
355: break;
356: }
357:
358: if (c && n->child)
359: print_body(p, n->child, m);
360:
361: switch (n->type) {
362: case (MAN_ELEM):
363: if (termacts[n->tok].post)
364: (*termacts[n->tok].post)(p, n, m);
365: break;
366: default:
367: break;
368: }
369: }
370:
371:
372: static void
373: print_body(DECL_ARGS)
374: {
375: print_node(p, n, m);
376: if ( ! n->next)
377: return;
378: print_body(p, n->next, m);
379: }
380:
381:
382: static void
383: print_foot(struct termp *p, const struct man_meta *meta)
384: {
385: struct tm *tm;
386: char *buf;
387:
388: if (NULL == (buf = malloc(p->rmargin)))
389: err(1, "malloc");
390:
391: tm = localtime(&meta->date);
392:
393: #ifdef __OpenBSD__
394: if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm))
395: #else
396: if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
397: #endif
398: err(1, "strftime");
399:
400: /*
401: * This is /slightly/ different from regular groff output
402: * because we don't have page numbers. Print the following:
403: *
404: * OS MDOCDATE
405: */
406:
407: term_vspace(p);
408:
409: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
410: p->rmargin = p->maxrmargin - strlen(buf);
411: p->offset = 0;
412:
413: if (meta->source)
414: term_word(p, meta->source);
415: if (meta->source)
416: term_word(p, "");
417: term_flushln(p);
418:
419: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
420: p->offset = p->rmargin;
421: p->rmargin = p->maxrmargin;
422: p->flags &= ~TERMP_NOBREAK;
423:
424: term_word(p, buf);
425: term_flushln(p);
426:
427: free(buf);
428: }
429:
430:
431: static void
432: print_head(struct termp *p, const struct man_meta *meta)
433: {
434: char *buf, *title;
435:
436: p->rmargin = p->maxrmargin;
437: p->offset = 0;
438:
439: if (NULL == (buf = malloc(p->rmargin)))
440: err(1, "malloc");
441: if (NULL == (title = malloc(p->rmargin)))
442: err(1, "malloc");
443:
444: /*
445: * The header is strange. It has three components, which are
446: * really two with the first duplicated. It goes like this:
447: *
448: * IDENTIFIER TITLE IDENTIFIER
449: *
450: * The IDENTIFIER is NAME(SECTION), which is the command-name
451: * (if given, or "unknown" if not) followed by the manual page
452: * section. These are given in `Dt'. The TITLE is a free-form
453: * string depending on the manual volume. If not specified, it
454: * switches on the manual section.
455: */
456:
457: if (meta->vol)
458: (void)strlcpy(buf, meta->vol, p->rmargin);
459: else
460: *buf = 0;
461:
462: (void)snprintf(title, p->rmargin, "%s(%d)",
463: meta->title, meta->msec);
464:
465: p->offset = 0;
466: p->rmargin = (p->maxrmargin - strlen(buf)) / 2;
467: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
468:
469: term_word(p, title);
470: term_flushln(p);
471:
472: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
473: p->offset = p->rmargin;
474: p->rmargin = p->maxrmargin - strlen(title);
475:
476: term_word(p, buf);
477: term_flushln(p);
478:
479: p->offset = p->rmargin;
480: p->rmargin = p->maxrmargin;
481: p->flags &= ~TERMP_NOBREAK;
482: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
483:
484: term_word(p, title);
485: term_flushln(p);
486:
487: p->rmargin = p->maxrmargin;
488: p->offset = 0;
489: p->flags &= ~TERMP_NOSPACE;
490:
491: free(title);
492: free(buf);
493: }
494:
CVSweb