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