]> git.cameronkatri.com Git - mandoc.git/blob - man_term.c
Fixed email address in manual AUTHOR reference.
[mandoc.git] / man_term.c
1 /* $Id: man_term.c,v 1.10 2009/06/11 07:26:35 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
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 above
7 * copyright notice and this permission notice appear in all copies.
8 *
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.
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 #ifdef __linux__
27 extern size_t strlcpy(char *, const char *, size_t);
28 extern size_t strlcat(char *, const char *, size_t);
29 #endif
30
31 #define DECL_ARGS struct termp *p, \
32 const struct man_node *n, \
33 const struct man_meta *m
34
35 struct termact {
36 int (*pre)(DECL_ARGS);
37 void (*post)(DECL_ARGS);
38 };
39
40 static int pre_B(DECL_ARGS);
41 static int pre_BI(DECL_ARGS);
42 static int pre_BR(DECL_ARGS);
43 static int pre_I(DECL_ARGS);
44 static int pre_IB(DECL_ARGS);
45 static int pre_IP(DECL_ARGS);
46 static int pre_IR(DECL_ARGS);
47 static int pre_PP(DECL_ARGS);
48 static int pre_RB(DECL_ARGS);
49 static int pre_RI(DECL_ARGS);
50 static int pre_SH(DECL_ARGS);
51 static int pre_SS(DECL_ARGS);
52 static int pre_TP(DECL_ARGS);
53
54 static void post_B(DECL_ARGS);
55 static void post_I(DECL_ARGS);
56 static void post_SH(DECL_ARGS);
57 static void post_SS(DECL_ARGS);
58
59 static const struct termact termacts[MAN_MAX] = {
60 { NULL, NULL }, /* __ */
61 { NULL, NULL }, /* TH */
62 { pre_SH, post_SH }, /* SH */
63 { pre_SS, post_SS }, /* SS */
64 { pre_TP, NULL }, /* TP */
65 { pre_PP, NULL }, /* LP */
66 { pre_PP, NULL }, /* PP */
67 { pre_PP, NULL }, /* P */
68 { pre_IP, NULL }, /* IP */
69 { pre_PP, NULL }, /* HP */ /* FIXME */
70 { NULL, NULL }, /* SM */
71 { pre_B, post_B }, /* SB */
72 { pre_BI, NULL }, /* BI */
73 { pre_IB, NULL }, /* IB */
74 { pre_BR, NULL }, /* BR */
75 { pre_RB, NULL }, /* RB */
76 { NULL, NULL }, /* R */
77 { pre_B, post_B }, /* B */
78 { pre_I, post_I }, /* I */
79 { pre_IR, NULL }, /* IR */
80 { pre_RI, NULL }, /* RI */
81 { pre_PP, NULL }, /* br */
82 { NULL, NULL }, /* na */
83 { pre_I, post_I }, /* i */
84 };
85
86 static void print_head(struct termp *,
87 const struct man_meta *);
88 static void print_body(DECL_ARGS);
89 static void print_node(DECL_ARGS);
90 static void print_foot(struct termp *,
91 const struct man_meta *);
92
93
94 int
95 man_run(struct termp *p, const struct man *m)
96 {
97
98 print_head(p, man_meta(m));
99 p->flags |= TERMP_NOSPACE;
100 print_body(p, man_node(m), man_meta(m));
101 print_foot(p, man_meta(m));
102
103 return(1);
104 }
105
106
107 /* ARGSUSED */
108 static int
109 pre_I(DECL_ARGS)
110 {
111
112 p->flags |= TERMP_UNDER;
113 return(1);
114 }
115
116
117 /* ARGSUSED */
118 static void
119 post_I(DECL_ARGS)
120 {
121
122 p->flags &= ~TERMP_UNDER;
123 }
124
125
126 /* ARGSUSED */
127 static int
128 pre_IR(DECL_ARGS)
129 {
130 const struct man_node *nn;
131 int i;
132
133 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
134 if ( ! (i % 2))
135 p->flags |= TERMP_UNDER;
136 if (i > 0)
137 p->flags |= TERMP_NOSPACE;
138 print_node(p, nn, m);
139 if ( ! (i % 2))
140 p->flags &= ~TERMP_UNDER;
141 }
142 return(0);
143 }
144
145
146 /* ARGSUSED */
147 static int
148 pre_IB(DECL_ARGS)
149 {
150 const struct man_node *nn;
151 int i;
152
153 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
154 p->flags |= i % 2 ? TERMP_BOLD : TERMP_UNDER;
155 if (i > 0)
156 p->flags |= TERMP_NOSPACE;
157 print_node(p, nn, m);
158 p->flags &= i % 2 ? ~TERMP_BOLD : ~TERMP_UNDER;
159 }
160 return(0);
161 }
162
163
164 /* ARGSUSED */
165 static int
166 pre_RB(DECL_ARGS)
167 {
168 const struct man_node *nn;
169 int i;
170
171 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
172 if (i % 2)
173 p->flags |= TERMP_BOLD;
174 if (i > 0)
175 p->flags |= TERMP_NOSPACE;
176 print_node(p, nn, m);
177 if (i % 2)
178 p->flags &= ~TERMP_BOLD;
179 }
180 return(0);
181 }
182
183
184 /* ARGSUSED */
185 static int
186 pre_RI(DECL_ARGS)
187 {
188 const struct man_node *nn;
189 int i;
190
191 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
192 if ( ! (i % 2))
193 p->flags |= TERMP_UNDER;
194 if (i > 0)
195 p->flags |= TERMP_NOSPACE;
196 print_node(p, nn, m);
197 if ( ! (i % 2))
198 p->flags &= ~TERMP_UNDER;
199 }
200 return(0);
201 }
202
203
204 /* ARGSUSED */
205 static int
206 pre_BR(DECL_ARGS)
207 {
208 const struct man_node *nn;
209 int i;
210
211 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
212 if ( ! (i % 2))
213 p->flags |= TERMP_BOLD;
214 if (i > 0)
215 p->flags |= TERMP_NOSPACE;
216 print_node(p, nn, m);
217 if ( ! (i % 2))
218 p->flags &= ~TERMP_BOLD;
219 }
220 return(0);
221 }
222
223
224 /* ARGSUSED */
225 static int
226 pre_BI(DECL_ARGS)
227 {
228 const struct man_node *nn;
229 int i;
230
231 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
232 p->flags |= i % 2 ? TERMP_UNDER : TERMP_BOLD;
233 if (i > 0)
234 p->flags |= TERMP_NOSPACE;
235 print_node(p, nn, m);
236 p->flags &= i % 2 ? ~TERMP_UNDER : ~TERMP_BOLD;
237 }
238 return(0);
239 }
240
241
242 /* ARGSUSED */
243 static int
244 pre_B(DECL_ARGS)
245 {
246
247 p->flags |= TERMP_BOLD;
248 return(1);
249 }
250
251
252 /* ARGSUSED */
253 static void
254 post_B(DECL_ARGS)
255 {
256
257 p->flags &= ~TERMP_BOLD;
258 }
259
260
261 /* ARGSUSED */
262 static int
263 pre_PP(DECL_ARGS)
264 {
265
266 term_vspace(p);
267 p->offset = INDENT;
268 return(0);
269 }
270
271
272 /* ARGSUSED */
273 static int
274 pre_IP(DECL_ARGS)
275 {
276 const struct man_node *nn;
277 size_t offs;
278
279 term_vspace(p);
280 p->offset = INDENT;
281
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
294 p->flags |= TERMP_NOSPACE;
295 p->offset += offs;
296 return(0);
297 }
298
299
300 /* ARGSUSED */
301 static int
302 pre_TP(DECL_ARGS)
303 {
304 const struct man_node *nn;
305 size_t offs;
306
307 term_vspace(p);
308 p->offset = INDENT;
309
310 if (NULL == (nn = n->child))
311 return(1);
312
313 if (nn->line == n->line) {
314 if (MAN_TEXT != nn->type)
315 errx(1, "expected text line argument");
316 offs = (size_t)atoi(nn->string);
317 nn = nn->next;
318 } else
319 offs = INDENT;
320
321 for ( ; nn; nn = nn->next)
322 print_node(p, nn, m);
323
324 term_flushln(p);
325 p->flags |= TERMP_NOSPACE;
326 p->offset += offs;
327 return(0);
328 }
329
330
331 /* ARGSUSED */
332 static int
333 pre_SS(DECL_ARGS)
334 {
335
336 term_vspace(p);
337 p->flags |= TERMP_BOLD;
338 return(1);
339 }
340
341
342 /* ARGSUSED */
343 static void
344 post_SS(DECL_ARGS)
345 {
346
347 term_flushln(p);
348 p->flags &= ~TERMP_BOLD;
349 p->flags |= TERMP_NOSPACE;
350 }
351
352
353 /* ARGSUSED */
354 static int
355 pre_SH(DECL_ARGS)
356 {
357
358 term_vspace(p);
359 p->offset = 0;
360 p->flags |= TERMP_BOLD;
361 return(1);
362 }
363
364
365 /* ARGSUSED */
366 static void
367 post_SH(DECL_ARGS)
368 {
369
370 term_flushln(p);
371 p->offset = INDENT;
372 p->flags &= ~TERMP_BOLD;
373 p->flags |= TERMP_NOSPACE;
374 }
375
376
377 static void
378 print_node(DECL_ARGS)
379 {
380 int c, sz;
381
382 c = 1;
383
384 switch (n->type) {
385 case(MAN_ELEM):
386 if (termacts[n->tok].pre)
387 c = (*termacts[n->tok].pre)(p, n, m);
388 break;
389 case(MAN_TEXT):
390 if (0 == *n->string) {
391 term_vspace(p);
392 break;
393 }
394 /*
395 * Note! This is hacky. Here, we recognise the `\c'
396 * escape embedded in so many -man pages. It's supposed
397 * to remove the subsequent space, so we mark NOSPACE if
398 * it's encountered in the string.
399 */
400 sz = (int)strlen(n->string);
401 term_word(p, n->string);
402 if (sz >= 2 && n->string[sz - 1] == 'c' &&
403 n->string[sz - 2] == '\\')
404 p->flags |= TERMP_NOSPACE;
405 break;
406 default:
407 break;
408 }
409
410 if (c && n->child)
411 print_body(p, n->child, m);
412
413 switch (n->type) {
414 case (MAN_ELEM):
415 if (termacts[n->tok].post)
416 (*termacts[n->tok].post)(p, n, m);
417 break;
418 default:
419 break;
420 }
421 }
422
423
424 static void
425 print_body(DECL_ARGS)
426 {
427 print_node(p, n, m);
428 if ( ! n->next)
429 return;
430 print_body(p, n->next, m);
431 }
432
433
434 static void
435 print_foot(struct termp *p, const struct man_meta *meta)
436 {
437 struct tm *tm;
438 char *buf;
439
440 if (NULL == (buf = malloc(p->rmargin)))
441 err(1, "malloc");
442
443 tm = localtime(&meta->date);
444
445 #ifdef __OpenBSD__
446 if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm))
447 #else
448 if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
449 #endif
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;
498 p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
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