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