Annotation of src/usr.bin/mandoc/man_term.c, Revision 1.4
1.4 ! schwarze 1: /* $Id: man_term.c,v 1.3 2009/06/17 22:27:34 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] = {
55: { NULL, NULL }, /* __ */
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: { pre_PP, NULL }, /* br */
77: { NULL, NULL }, /* na */
78: { pre_I, post_I }, /* i */
79: };
80:
81: static void print_head(struct termp *,
82: const struct man_meta *);
83: static void print_body(DECL_ARGS);
84: static void print_node(DECL_ARGS);
85: static void print_foot(struct termp *,
86: const struct man_meta *);
87:
88:
89: int
90: man_run(struct termp *p, const struct man *m)
91: {
92:
93: print_head(p, man_meta(m));
94: p->flags |= TERMP_NOSPACE;
95: print_body(p, man_node(m), man_meta(m));
96: print_foot(p, man_meta(m));
97:
98: return(1);
99: }
100:
101:
102: /* ARGSUSED */
103: static int
104: pre_I(DECL_ARGS)
105: {
106:
107: p->flags |= TERMP_UNDER;
108: return(1);
109: }
110:
111:
112: /* ARGSUSED */
113: static void
114: post_I(DECL_ARGS)
115: {
116:
117: p->flags &= ~TERMP_UNDER;
118: }
119:
120:
121: /* ARGSUSED */
122: static int
123: pre_IR(DECL_ARGS)
124: {
125: const struct man_node *nn;
126: int i;
127:
128: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
129: if ( ! (i % 2))
130: p->flags |= TERMP_UNDER;
131: if (i > 0)
132: p->flags |= TERMP_NOSPACE;
133: print_node(p, nn, m);
134: if ( ! (i % 2))
135: p->flags &= ~TERMP_UNDER;
136: }
137: return(0);
138: }
139:
140:
141: /* ARGSUSED */
142: static int
143: pre_IB(DECL_ARGS)
144: {
145: const struct man_node *nn;
146: int i;
147:
148: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
149: p->flags |= i % 2 ? TERMP_BOLD : TERMP_UNDER;
150: if (i > 0)
151: p->flags |= TERMP_NOSPACE;
152: print_node(p, nn, m);
153: p->flags &= i % 2 ? ~TERMP_BOLD : ~TERMP_UNDER;
154: }
155: return(0);
156: }
157:
158:
159: /* ARGSUSED */
160: static int
161: pre_RB(DECL_ARGS)
162: {
163: const struct man_node *nn;
164: int i;
165:
166: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
167: if (i % 2)
168: p->flags |= TERMP_BOLD;
169: if (i > 0)
170: p->flags |= TERMP_NOSPACE;
171: print_node(p, nn, m);
172: if (i % 2)
173: p->flags &= ~TERMP_BOLD;
174: }
175: return(0);
176: }
177:
178:
179: /* ARGSUSED */
180: static int
181: pre_RI(DECL_ARGS)
182: {
183: const struct man_node *nn;
184: int i;
185:
186: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
187: if ( ! (i % 2))
188: p->flags |= TERMP_UNDER;
189: if (i > 0)
190: p->flags |= TERMP_NOSPACE;
191: print_node(p, nn, m);
192: if ( ! (i % 2))
193: p->flags &= ~TERMP_UNDER;
194: }
195: return(0);
196: }
197:
198:
199: /* ARGSUSED */
200: static int
201: pre_BR(DECL_ARGS)
202: {
203: const struct man_node *nn;
204: int i;
205:
206: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
207: if ( ! (i % 2))
208: p->flags |= TERMP_BOLD;
209: if (i > 0)
210: p->flags |= TERMP_NOSPACE;
211: print_node(p, nn, m);
212: if ( ! (i % 2))
213: p->flags &= ~TERMP_BOLD;
214: }
215: return(0);
216: }
217:
218:
219: /* ARGSUSED */
220: static int
221: pre_BI(DECL_ARGS)
222: {
223: const struct man_node *nn;
224: int i;
225:
226: for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
227: p->flags |= i % 2 ? TERMP_UNDER : TERMP_BOLD;
228: if (i > 0)
229: p->flags |= TERMP_NOSPACE;
230: print_node(p, nn, m);
231: p->flags &= i % 2 ? ~TERMP_UNDER : ~TERMP_BOLD;
232: }
233: return(0);
234: }
235:
236:
237: /* ARGSUSED */
238: static int
239: pre_B(DECL_ARGS)
240: {
241:
242: p->flags |= TERMP_BOLD;
243: return(1);
244: }
245:
246:
247: /* ARGSUSED */
248: static void
249: post_B(DECL_ARGS)
250: {
251:
252: p->flags &= ~TERMP_BOLD;
253: }
254:
255:
256: /* ARGSUSED */
257: static int
258: pre_PP(DECL_ARGS)
259: {
260:
261: term_vspace(p);
262: p->offset = INDENT;
263: return(0);
264: }
265:
266:
267: /* ARGSUSED */
268: static int
269: pre_IP(DECL_ARGS)
270: {
271: #if 0
272: const struct man_node *nn;
273: size_t offs;
274: #endif
275:
276: term_vspace(p);
277: p->offset = INDENT;
278:
279: #if 0
280: if (NULL == (nn = n->child))
281: return(1);
282: if (MAN_TEXT != nn->type)
283: errx(1, "expected text line argument");
284:
285: if (nn->next) {
286: if (MAN_TEXT != nn->next->type)
287: errx(1, "expected text line argument");
288: offs = (size_t)atoi(nn->next->string);
289: } else
290: offs = strlen(nn->string);
291:
1.4 ! schwarze 292: p->flags |= TERMP_NOSPACE;
! 293: /* FIXME */
! 294: if ((p->offset += offs) > p->rmargin)
! 295: errx(1, "line too long");
1.1 kristaps 296: #endif
1.4 ! schwarze 297:
1.1 kristaps 298: return(0);
299: }
300:
301:
302: /* ARGSUSED */
303: static int
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");
318: offs = (size_t)atoi(nn->string);
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:
333: /* ARGSUSED */
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:
344: /* ARGSUSED */
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:
355: /* ARGSUSED */
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:
367: /* ARGSUSED */
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: {
382: int c, sz;
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):
392: if (0 == *n->string) {
393: term_vspace(p);
394: break;
395: }
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;
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:
1.3 schwarze 447: if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
1.1 kristaps 448: err(1, "strftime");
449:
450: term_vspace(p);
451:
452: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
453: p->rmargin = p->maxrmargin - strlen(buf);
454: p->offset = 0;
455:
456: if (meta->source)
457: term_word(p, meta->source);
458: if (meta->source)
459: term_word(p, "");
460: term_flushln(p);
461:
462: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
463: p->offset = p->rmargin;
464: p->rmargin = p->maxrmargin;
465: p->flags &= ~TERMP_NOBREAK;
466:
467: term_word(p, buf);
468: term_flushln(p);
469:
470: free(buf);
471: }
472:
473:
474: static void
475: print_head(struct termp *p, const struct man_meta *meta)
476: {
477: char *buf, *title;
478:
479: p->rmargin = p->maxrmargin;
480: p->offset = 0;
481:
482: if (NULL == (buf = malloc(p->rmargin)))
483: err(1, "malloc");
484: if (NULL == (title = malloc(p->rmargin)))
485: err(1, "malloc");
486:
487: if (meta->vol)
488: (void)strlcpy(buf, meta->vol, p->rmargin);
489: else
490: *buf = 0;
491:
492: (void)snprintf(title, p->rmargin, "%s(%d)",
493: meta->title, meta->msec);
494:
495: p->offset = 0;
496: p->rmargin = (p->maxrmargin - strlen(buf)) / 2;
497: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
498:
499: term_word(p, title);
500: term_flushln(p);
501:
502: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
503: p->offset = p->rmargin;
504: p->rmargin = p->maxrmargin - strlen(title);
505:
506: term_word(p, buf);
507: term_flushln(p);
508:
509: p->offset = p->rmargin;
510: p->rmargin = p->maxrmargin;
511: p->flags &= ~TERMP_NOBREAK;
512: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
513:
514: term_word(p, title);
515: term_flushln(p);
516:
517: p->rmargin = p->maxrmargin;
518: p->offset = 0;
519: p->flags &= ~TERMP_NOSPACE;
520:
521: free(title);
522: free(buf);
523: }
524: