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