Annotation of mandoc/man_term.c, Revision 1.2
1.2 ! kristaps 1: /* $Id: man_term.c,v 1.1 2009/03/26 14:38:11 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);
43: static int pre_I(DECL_ARGS);
44: static int pre_PP(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: { NULL, NULL }, /* IP */
64: { pre_PP, NULL }, /* HP */ /* XXX */
65: { NULL, NULL }, /* SM */
66: { pre_B, post_B }, /* SB */
67: { NULL, NULL }, /* BI */
68: { NULL, NULL }, /* IB */
69: { NULL, NULL }, /* BR */
70: { NULL, NULL }, /* RB */
71: { NULL, NULL }, /* R */
72: { pre_B, post_B }, /* B */
73: { pre_I, post_I }, /* I */
74: { NULL, NULL }, /* IR */
75: { NULL, NULL }, /* RI */
76: };
77:
78: static void print_head(struct termp *,
79: const struct man_meta *);
80: static void print_body(DECL_ARGS);
81: static void print_node(DECL_ARGS);
82: static void print_foot(struct termp *,
83: const struct man_meta *);
84:
85:
86: int
87: man_run(struct termp *p, const struct man *m)
88: {
89:
90: print_head(p, man_meta(m));
91: p->flags |= TERMP_NOSPACE;
92: print_body(p, man_node(m), man_meta(m));
93: print_foot(p, man_meta(m));
94:
95: return(1);
96: }
97:
98:
99: static int
100: pre_I(DECL_ARGS)
101: {
102:
103: p->flags |= TERMP_UNDER;
104: return(1);
105: }
106:
107:
108: static void
109: post_I(DECL_ARGS)
110: {
111:
112: p->flags &= ~TERMP_UNDER;
113: }
114:
115:
116: static int
117: pre_B(DECL_ARGS)
118: {
119:
120: p->flags |= TERMP_BOLD;
121: return(1);
122: }
123:
124:
125: static void
126: post_B(DECL_ARGS)
127: {
128:
129: p->flags &= ~TERMP_BOLD;
130: }
131:
132:
133: static int
134: pre_PP(DECL_ARGS)
135: {
136:
137: term_vspace(p);
138: p->offset = INDENT;
139: return(0);
140: }
141:
142:
143: static int
144: pre_TP(DECL_ARGS)
145: {
146: const struct man_node *nn;
147: size_t offs;
148:
149: term_vspace(p);
150: p->offset = INDENT;
151:
152: if (NULL == (nn = n->child))
153: return(1);
154:
155: if (nn->line == n->line) {
156: if (MAN_TEXT != nn->type)
157: errx(1, "expected text line argument");
158: offs = atoi(nn->string);
159: nn = nn->next;
160: } else
161: offs = INDENT;
162:
163: for ( ; nn; nn = nn->next)
164: print_node(p, nn, m);
165:
166: term_flushln(p);
167: p->flags |= TERMP_NOSPACE;
168: p->offset += offs;
169: return(0);
170: }
171:
172:
173: static int
174: pre_SS(DECL_ARGS)
175: {
176:
177: term_vspace(p);
178: p->flags |= TERMP_BOLD;
179: return(1);
180: }
181:
182:
183: static void
184: post_SS(DECL_ARGS)
185: {
186:
187: term_flushln(p);
188: p->flags &= ~TERMP_BOLD;
189: p->flags |= TERMP_NOSPACE;
190: }
191:
192:
193: static int
194: pre_SH(DECL_ARGS)
195: {
196:
197: term_vspace(p);
198: p->offset = 0;
199: p->flags |= TERMP_BOLD;
200: return(1);
201: }
202:
203:
204: static void
205: post_SH(DECL_ARGS)
206: {
207:
208: term_flushln(p);
209: p->offset = INDENT;
210: p->flags &= ~TERMP_BOLD;
211: p->flags |= TERMP_NOSPACE;
212: }
213:
214:
215: static void
216: print_node(DECL_ARGS)
217: {
218: int c;
219:
220: c = 1;
221:
222: switch (n->type) {
223: case(MAN_ELEM):
224: if (termacts[n->tok].pre)
225: c = (*termacts[n->tok].pre)(p, n, m);
226: break;
227: case(MAN_TEXT):
228: if (*n->string) {
229: term_word(p, n->string);
230: break;
231: }
232: term_vspace(p);
233: break;
234: default:
235: break;
236: }
237:
238: if (c && n->child)
239: print_body(p, n->child, m);
240:
241: switch (n->type) {
242: case (MAN_ELEM):
243: if (termacts[n->tok].post)
244: (*termacts[n->tok].post)(p, n, m);
245: break;
246: default:
247: break;
248: }
249: }
250:
251:
252: static void
253: print_body(DECL_ARGS)
254: {
255: print_node(p, n, m);
256: if ( ! n->next)
257: return;
258: print_body(p, n->next, m);
259: }
260:
261:
262: static void
263: print_foot(struct termp *p, const struct man_meta *meta)
264: {
265: struct tm *tm;
266: char *buf;
267:
268: if (NULL == (buf = malloc(p->rmargin)))
269: err(1, "malloc");
270:
271: tm = localtime(&meta->date);
272:
273: #ifdef __OpenBSD__
274: if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm))
275: #else
276: if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
277: #endif
278: err(1, "strftime");
279:
280: /*
281: * This is /slightly/ different from regular groff output
282: * because we don't have page numbers. Print the following:
283: *
284: * OS MDOCDATE
285: */
286:
287: term_vspace(p);
288:
289: p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
290: p->rmargin = p->maxrmargin - strlen(buf);
291: p->offset = 0;
292:
293: if (meta->source)
294: term_word(p, meta->source);
295: if (meta->source)
296: term_word(p, "");
297: term_flushln(p);
298:
299: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
300: p->offset = p->rmargin;
301: p->rmargin = p->maxrmargin;
302: p->flags &= ~TERMP_NOBREAK;
303:
304: term_word(p, buf);
305: term_flushln(p);
306:
307: free(buf);
308: }
309:
310:
311: static void
312: print_head(struct termp *p, const struct man_meta *meta)
313: {
314: char *buf, *title;
315:
316: p->rmargin = p->maxrmargin;
317: p->offset = 0;
318:
319: if (NULL == (buf = malloc(p->rmargin)))
320: err(1, "malloc");
321: if (NULL == (title = malloc(p->rmargin)))
322: err(1, "malloc");
323:
324: /*
325: * The header is strange. It has three components, which are
326: * really two with the first duplicated. It goes like this:
327: *
328: * IDENTIFIER TITLE IDENTIFIER
329: *
330: * The IDENTIFIER is NAME(SECTION), which is the command-name
331: * (if given, or "unknown" if not) followed by the manual page
332: * section. These are given in `Dt'. The TITLE is a free-form
333: * string depending on the manual volume. If not specified, it
334: * switches on the manual section.
335: */
336:
337: if (meta->vol)
338: (void)strlcpy(buf, meta->vol, p->rmargin);
339: else
340: *buf = 0;
341:
342: (void)snprintf(title, p->rmargin, "%s(%d)",
343: meta->title, meta->msec);
344:
345: p->offset = 0;
346: p->rmargin = (p->maxrmargin - strlen(buf)) / 2;
347: p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
348:
349: term_word(p, title);
350: term_flushln(p);
351:
352: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
353: p->offset = p->rmargin;
354: p->rmargin = p->maxrmargin - strlen(title);
355:
356: term_word(p, buf);
357: term_flushln(p);
358:
359: p->offset = p->rmargin;
360: p->rmargin = p->maxrmargin;
361: p->flags &= ~TERMP_NOBREAK;
362: p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
363:
364: term_word(p, title);
365: term_flushln(p);
366:
367: p->rmargin = p->maxrmargin;
368: p->offset = 0;
369: p->flags &= ~TERMP_NOSPACE;
370:
371: free(title);
372: free(buf);
373: }
374:
CVSweb