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