Annotation of mandoc/man_term.c, Revision 1.7
1.7 ! kristaps 1: /* $Id: man_term.c,v 1.6 2009/04/02 06:51:44 kristaps Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the
7: * above copyright notice and this permission notice appear in all
8: * copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11: * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12: * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13: * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14: * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15: * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16: * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17: * PERFORMANCE OF THIS SOFTWARE.
18: */
19: #include <assert.h>
20: #include <err.h>
21: #include <stdio.h>
22: #include <stdlib.h>
23: #include <string.h>
24:
25: #include "term.h"
26: #include "man.h"
27:
1.2 kristaps 28: #ifdef __linux__
29: extern size_t strlcpy(char *, const char *, size_t);
30: extern size_t strlcat(char *, const char *, size_t);
31: #endif
32:
1.1 kristaps 33: #define DECL_ARGS struct termp *p, \
34: const struct man_node *n, \
35: const struct man_meta *m
36:
37: struct termact {
38: int (*pre)(DECL_ARGS);
39: void (*post)(DECL_ARGS);
40: };
41:
42: static int pre_B(DECL_ARGS);
1.3 kristaps 43: static int pre_BI(DECL_ARGS);
44: static int pre_BR(DECL_ARGS);
1.1 kristaps 45: static int pre_I(DECL_ARGS);
1.3 kristaps 46: static int pre_IB(DECL_ARGS);
1.4 kristaps 47: static int pre_IP(DECL_ARGS);
1.3 kristaps 48: static int pre_IR(DECL_ARGS);
1.1 kristaps 49: static int pre_PP(DECL_ARGS);
1.3 kristaps 50: static int pre_RB(DECL_ARGS);
51: static int pre_RI(DECL_ARGS);
1.1 kristaps 52: static int pre_SH(DECL_ARGS);
53: static int pre_SS(DECL_ARGS);
54: static int pre_TP(DECL_ARGS);
55:
56: static void post_B(DECL_ARGS);
57: static void post_I(DECL_ARGS);
58: static void post_SH(DECL_ARGS);
59: static void post_SS(DECL_ARGS);
60:
61: static const struct termact termacts[MAN_MAX] = {
62: { NULL, NULL }, /* __ */
63: { NULL, NULL }, /* TH */
64: { pre_SH, post_SH }, /* SH */
65: { pre_SS, post_SS }, /* SS */
66: { pre_TP, NULL }, /* TP */
67: { pre_PP, NULL }, /* LP */
68: { pre_PP, NULL }, /* PP */
69: { pre_PP, NULL }, /* P */
1.4 kristaps 70: { pre_IP, NULL }, /* IP */
71: { pre_PP, NULL }, /* HP */ /* FIXME */
1.1 kristaps 72: { NULL, NULL }, /* SM */
73: { pre_B, post_B }, /* SB */
1.3 kristaps 74: { pre_BI, NULL }, /* BI */
75: { pre_IB, NULL }, /* IB */
76: { pre_BR, NULL }, /* BR */
77: { pre_RB, NULL }, /* RB */
1.1 kristaps 78: { NULL, NULL }, /* R */
79: { pre_B, post_B }, /* B */
80: { pre_I, post_I }, /* I */
1.3 kristaps 81: { pre_IR, NULL }, /* IR */
82: { pre_RI, NULL }, /* RI */
1.5 kristaps 83: { pre_PP, NULL }, /* br */
1.6 kristaps 84: { NULL, NULL }, /* na */
1.7 ! kristaps 85: { pre_I, post_I }, /* i */
1.1 kristaps 86: };
87:
88: static void print_head(struct termp *,
89: const struct man_meta *);
90: static void print_body(DECL_ARGS);
91: static void print_node(DECL_ARGS);
92: static void print_foot(struct termp *,
93: const struct man_meta *);
94:
95:
96: int
97: man_run(struct termp *p, const struct man *m)
98: {
99:
100: print_head(p, man_meta(m));
101: p->flags |= TERMP_NOSPACE;
102: print_body(p, man_node(m), man_meta(m));
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: {
278: const struct man_node *nn;
279: size_t offs;
280:
281: term_vspace(p);
282: p->offset = INDENT;
283:
284: if (NULL == (nn = n->child))
285: return(1);
286: if (MAN_TEXT != nn->type)
287: errx(1, "expected text line argument");
288:
1.7 ! kristaps 289: if (nn->next) {
! 290: if (MAN_TEXT != nn->next->type)
! 291: errx(1, "expected text line argument");
! 292: offs = (size_t)atoi(nn->next->string);
! 293: } else
! 294: offs = strlen(nn->string);
1.4 kristaps 295:
296: p->flags |= TERMP_NOSPACE;
297: p->offset += offs;
298: return(0);
299: }
300:
301:
302: /* ARGSUSED */
303: static int
1.1 kristaps 304: pre_TP(DECL_ARGS)
305: {
306: const struct man_node *nn;
307: size_t offs;
308:
309: term_vspace(p);
310: p->offset = INDENT;
311:
312: if (NULL == (nn = n->child))
313: return(1);
314:
315: if (nn->line == n->line) {
316: if (MAN_TEXT != nn->type)
317: errx(1, "expected text line argument");
1.3 kristaps 318: offs = (size_t)atoi(nn->string);
1.1 kristaps 319: nn = nn->next;
320: } else
321: offs = INDENT;
322:
323: for ( ; nn; nn = nn->next)
324: print_node(p, nn, m);
325:
326: term_flushln(p);
327: p->flags |= TERMP_NOSPACE;
328: p->offset += offs;
329: return(0);
330: }
331:
332:
1.3 kristaps 333: /* ARGSUSED */
1.1 kristaps 334: static int
335: pre_SS(DECL_ARGS)
336: {
337:
338: term_vspace(p);
339: p->flags |= TERMP_BOLD;
340: return(1);
341: }
342:
343:
1.3 kristaps 344: /* ARGSUSED */
1.1 kristaps 345: static void
346: post_SS(DECL_ARGS)
347: {
348:
349: term_flushln(p);
350: p->flags &= ~TERMP_BOLD;
351: p->flags |= TERMP_NOSPACE;
352: }
353:
354:
1.3 kristaps 355: /* ARGSUSED */
1.1 kristaps 356: static int
357: pre_SH(DECL_ARGS)
358: {
359:
360: term_vspace(p);
361: p->offset = 0;
362: p->flags |= TERMP_BOLD;
363: return(1);
364: }
365:
366:
1.3 kristaps 367: /* ARGSUSED */
1.1 kristaps 368: static void
369: post_SH(DECL_ARGS)
370: {
371:
372: term_flushln(p);
373: p->offset = INDENT;
374: p->flags &= ~TERMP_BOLD;
375: p->flags |= TERMP_NOSPACE;
376: }
377:
378:
379: static void
380: print_node(DECL_ARGS)
381: {
1.4 kristaps 382: int c, sz;
1.1 kristaps 383:
384: c = 1;
385:
386: switch (n->type) {
387: case(MAN_ELEM):
388: if (termacts[n->tok].pre)
389: c = (*termacts[n->tok].pre)(p, n, m);
390: break;
391: case(MAN_TEXT):
1.4 kristaps 392: if (0 == *n->string) {
393: term_vspace(p);
1.1 kristaps 394: break;
395: }
1.4 kristaps 396: /*
397: * Note! This is hacky. Here, we recognise the `\c'
398: * escape embedded in so many -man pages. It's supposed
399: * to remove the subsequent space, so we mark NOSPACE if
400: * it's encountered in the string.
401: */
402: sz = (int)strlen(n->string);
403: term_word(p, n->string);
404: if (sz >= 2 && n->string[sz - 1] == 'c' &&
405: n->string[sz - 2] == '\\')
406: p->flags |= TERMP_NOSPACE;
1.1 kristaps 407: break;
408: default:
409: break;
410: }
411:
412: if (c && n->child)
413: print_body(p, n->child, m);
414:
415: switch (n->type) {
416: case (MAN_ELEM):
417: if (termacts[n->tok].post)
418: (*termacts[n->tok].post)(p, n, m);
419: break;
420: default:
421: break;
422: }
423: }
424:
425:
426: static void
427: print_body(DECL_ARGS)
428: {
429: print_node(p, n, m);
430: if ( ! n->next)
431: return;
432: print_body(p, n->next, m);
433: }
434:
435:
436: static void
437: print_foot(struct termp *p, const struct man_meta *meta)
438: {
439: struct tm *tm;
440: char *buf;
441:
442: if (NULL == (buf = malloc(p->rmargin)))
443: err(1, "malloc");
444:
445: tm = localtime(&meta->date);
446:
447: #ifdef __OpenBSD__
448: if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm))
449: #else
450: if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
451: #endif
452: err(1, "strftime");
453:
454: term_vspace(p);
455:
456: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
457: p->rmargin = p->maxrmargin - strlen(buf);
458: p->offset = 0;
459:
460: if (meta->source)
461: term_word(p, meta->source);
462: if (meta->source)
463: term_word(p, "");
464: term_flushln(p);
465:
466: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
467: p->offset = p->rmargin;
468: p->rmargin = p->maxrmargin;
469: p->flags &= ~TERMP_NOBREAK;
470:
471: term_word(p, buf);
472: term_flushln(p);
473:
474: free(buf);
475: }
476:
477:
478: static void
479: print_head(struct termp *p, const struct man_meta *meta)
480: {
481: char *buf, *title;
482:
483: p->rmargin = p->maxrmargin;
484: p->offset = 0;
485:
486: if (NULL == (buf = malloc(p->rmargin)))
487: err(1, "malloc");
488: if (NULL == (title = malloc(p->rmargin)))
489: err(1, "malloc");
490:
491: if (meta->vol)
492: (void)strlcpy(buf, meta->vol, p->rmargin);
493: else
494: *buf = 0;
495:
496: (void)snprintf(title, p->rmargin, "%s(%d)",
497: meta->title, meta->msec);
498:
499: p->offset = 0;
500: p->rmargin = (p->maxrmargin - strlen(buf)) / 2;
501: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
502:
503: term_word(p, title);
504: term_flushln(p);
505:
506: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
507: p->offset = p->rmargin;
508: p->rmargin = p->maxrmargin - strlen(title);
509:
510: term_word(p, buf);
511: term_flushln(p);
512:
513: p->offset = p->rmargin;
514: p->rmargin = p->maxrmargin;
515: p->flags &= ~TERMP_NOBREAK;
516: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
517:
518: term_word(p, title);
519: term_flushln(p);
520:
521: p->rmargin = p->maxrmargin;
522: p->offset = 0;
523: p->flags &= ~TERMP_NOSPACE;
524:
525: free(title);
526: free(buf);
527: }
528:
CVSweb