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