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