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