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