Annotation of mandoc/man_term.c, Revision 1.16
1.16 ! kristaps 1: /* $Id: man_term.c,v 1.15 2009/06/18 19:54:04 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);
1.16 ! kristaps 328:
1.1 kristaps 329: p->offset = INDENT;
330:
331: if (NULL == (nn = n->child))
332: return(1);
333:
334: if (nn->line == n->line) {
335: if (MAN_TEXT != nn->type)
336: errx(1, "expected text line argument");
1.3 kristaps 337: offs = (size_t)atoi(nn->string);
1.1 kristaps 338: nn = nn->next;
339: } else
340: offs = INDENT;
341:
342: for ( ; nn; nn = nn->next)
343: print_node(p, nn, m);
344:
345: term_flushln(p);
346: p->flags |= TERMP_NOSPACE;
347: p->offset += offs;
348: return(0);
349: }
350:
351:
1.3 kristaps 352: /* ARGSUSED */
1.1 kristaps 353: static int
354: pre_SS(DECL_ARGS)
355: {
356:
357: term_vspace(p);
358: p->flags |= TERMP_BOLD;
359: return(1);
360: }
361:
362:
1.3 kristaps 363: /* ARGSUSED */
1.1 kristaps 364: static void
365: post_SS(DECL_ARGS)
366: {
367:
368: term_flushln(p);
369: p->flags &= ~TERMP_BOLD;
370: p->flags |= TERMP_NOSPACE;
371: }
372:
373:
1.3 kristaps 374: /* ARGSUSED */
1.1 kristaps 375: static int
376: pre_SH(DECL_ARGS)
377: {
378:
379: term_vspace(p);
380: p->offset = 0;
381: p->flags |= TERMP_BOLD;
382: return(1);
383: }
384:
385:
1.3 kristaps 386: /* ARGSUSED */
1.1 kristaps 387: static void
388: post_SH(DECL_ARGS)
389: {
390:
391: term_flushln(p);
392: p->offset = INDENT;
393: p->flags &= ~TERMP_BOLD;
394: p->flags |= TERMP_NOSPACE;
395: }
396:
397:
398: static void
399: print_node(DECL_ARGS)
400: {
1.4 kristaps 401: int c, sz;
1.1 kristaps 402:
403: c = 1;
404:
405: switch (n->type) {
406: case(MAN_ELEM):
407: if (termacts[n->tok].pre)
408: c = (*termacts[n->tok].pre)(p, n, m);
409: break;
410: case(MAN_TEXT):
1.4 kristaps 411: if (0 == *n->string) {
412: term_vspace(p);
1.1 kristaps 413: break;
414: }
1.4 kristaps 415: /*
416: * Note! This is hacky. Here, we recognise the `\c'
417: * escape embedded in so many -man pages. It's supposed
418: * to remove the subsequent space, so we mark NOSPACE if
419: * it's encountered in the string.
420: */
421: sz = (int)strlen(n->string);
422: term_word(p, n->string);
423: if (sz >= 2 && n->string[sz - 1] == 'c' &&
424: n->string[sz - 2] == '\\')
425: p->flags |= TERMP_NOSPACE;
1.1 kristaps 426: break;
427: default:
428: break;
429: }
430:
431: if (c && n->child)
432: print_body(p, n->child, m);
433:
434: switch (n->type) {
435: case (MAN_ELEM):
436: if (termacts[n->tok].post)
437: (*termacts[n->tok].post)(p, n, m);
438: break;
439: default:
440: break;
441: }
442: }
443:
444:
445: static void
446: print_body(DECL_ARGS)
447: {
448: print_node(p, n, m);
449: if ( ! n->next)
450: return;
451: print_body(p, n->next, m);
452: }
453:
454:
455: static void
456: print_foot(struct termp *p, const struct man_meta *meta)
457: {
458: struct tm *tm;
459: char *buf;
460:
461: if (NULL == (buf = malloc(p->rmargin)))
462: err(1, "malloc");
463:
464: tm = localtime(&meta->date);
465:
466: if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
467: err(1, "strftime");
468:
469: term_vspace(p);
470:
471: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
472: p->rmargin = p->maxrmargin - strlen(buf);
473: p->offset = 0;
474:
475: if (meta->source)
476: term_word(p, meta->source);
477: if (meta->source)
478: term_word(p, "");
479: term_flushln(p);
480:
481: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
482: p->offset = p->rmargin;
483: p->rmargin = p->maxrmargin;
484: p->flags &= ~TERMP_NOBREAK;
485:
486: term_word(p, buf);
487: term_flushln(p);
488:
489: free(buf);
490: }
491:
492:
493: static void
494: print_head(struct termp *p, const struct man_meta *meta)
495: {
496: char *buf, *title;
497:
498: p->rmargin = p->maxrmargin;
499: p->offset = 0;
500:
501: if (NULL == (buf = malloc(p->rmargin)))
502: err(1, "malloc");
503: if (NULL == (title = malloc(p->rmargin)))
504: err(1, "malloc");
505:
506: if (meta->vol)
507: (void)strlcpy(buf, meta->vol, p->rmargin);
508: else
509: *buf = 0;
510:
511: (void)snprintf(title, p->rmargin, "%s(%d)",
512: meta->title, meta->msec);
513:
514: p->offset = 0;
1.10 kristaps 515: p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
1.1 kristaps 516: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
517:
518: term_word(p, title);
519: term_flushln(p);
520:
521: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
522: p->offset = p->rmargin;
523: p->rmargin = p->maxrmargin - strlen(title);
524:
525: term_word(p, buf);
526: term_flushln(p);
527:
528: p->offset = p->rmargin;
529: p->rmargin = p->maxrmargin;
530: p->flags &= ~TERMP_NOBREAK;
531: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
532:
533: term_word(p, title);
534: term_flushln(p);
535:
536: p->rmargin = p->maxrmargin;
537: p->offset = 0;
538: p->flags &= ~TERMP_NOSPACE;
539:
540: free(title);
541: free(buf);
542: }
543:
CVSweb