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