]> git.cameronkatri.com Git - mandoc.git/blob - man_term.c
Backed-out .IP changes for libman (needs work).
[mandoc.git] / man_term.c
1 /* $Id: man_term.c,v 1.12 2009/06/11 13:18:42 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 #if 0
277 const struct man_node *nn;
278 size_t offs;
279 #endif
280
281 term_vspace(p);
282 p->offset = INDENT;
283
284 #if 0
285 if (NULL == (nn = n->child))
286 return(1);
287 if (MAN_TEXT != nn->type)
288 errx(1, "expected text line argument");
289
290 if (nn->next) {
291 if (MAN_TEXT != nn->next->type)
292 errx(1, "expected text line argument");
293 offs = (size_t)atoi(nn->next->string);
294 } else
295 offs = strlen(nn->string);
296
297 p->flags |= TERMP_NOSPACE;
298 /* FIXME */
299 if ((p->offset += offs) > p->rmargin)
300 errx(1, "line too long");
301 #endif
302
303 return(0);
304 }
305
306
307 /* ARGSUSED */
308 static int
309 pre_TP(DECL_ARGS)
310 {
311 const struct man_node *nn;
312 size_t offs;
313
314 term_vspace(p);
315 p->offset = INDENT;
316
317 if (NULL == (nn = n->child))
318 return(1);
319
320 if (nn->line == n->line) {
321 if (MAN_TEXT != nn->type)
322 errx(1, "expected text line argument");
323 offs = (size_t)atoi(nn->string);
324 nn = nn->next;
325 } else
326 offs = INDENT;
327
328 for ( ; nn; nn = nn->next)
329 print_node(p, nn, m);
330
331 term_flushln(p);
332 p->flags |= TERMP_NOSPACE;
333 p->offset += offs;
334 return(0);
335 }
336
337
338 /* ARGSUSED */
339 static int
340 pre_SS(DECL_ARGS)
341 {
342
343 term_vspace(p);
344 p->flags |= TERMP_BOLD;
345 return(1);
346 }
347
348
349 /* ARGSUSED */
350 static void
351 post_SS(DECL_ARGS)
352 {
353
354 term_flushln(p);
355 p->flags &= ~TERMP_BOLD;
356 p->flags |= TERMP_NOSPACE;
357 }
358
359
360 /* ARGSUSED */
361 static int
362 pre_SH(DECL_ARGS)
363 {
364
365 term_vspace(p);
366 p->offset = 0;
367 p->flags |= TERMP_BOLD;
368 return(1);
369 }
370
371
372 /* ARGSUSED */
373 static void
374 post_SH(DECL_ARGS)
375 {
376
377 term_flushln(p);
378 p->offset = INDENT;
379 p->flags &= ~TERMP_BOLD;
380 p->flags |= TERMP_NOSPACE;
381 }
382
383
384 static void
385 print_node(DECL_ARGS)
386 {
387 int c, sz;
388
389 c = 1;
390
391 switch (n->type) {
392 case(MAN_ELEM):
393 if (termacts[n->tok].pre)
394 c = (*termacts[n->tok].pre)(p, n, m);
395 break;
396 case(MAN_TEXT):
397 if (0 == *n->string) {
398 term_vspace(p);
399 break;
400 }
401 /*
402 * Note! This is hacky. Here, we recognise the `\c'
403 * escape embedded in so many -man pages. It's supposed
404 * to remove the subsequent space, so we mark NOSPACE if
405 * it's encountered in the string.
406 */
407 sz = (int)strlen(n->string);
408 term_word(p, n->string);
409 if (sz >= 2 && n->string[sz - 1] == 'c' &&
410 n->string[sz - 2] == '\\')
411 p->flags |= TERMP_NOSPACE;
412 break;
413 default:
414 break;
415 }
416
417 if (c && n->child)
418 print_body(p, n->child, m);
419
420 switch (n->type) {
421 case (MAN_ELEM):
422 if (termacts[n->tok].post)
423 (*termacts[n->tok].post)(p, n, m);
424 break;
425 default:
426 break;
427 }
428 }
429
430
431 static void
432 print_body(DECL_ARGS)
433 {
434 print_node(p, n, m);
435 if ( ! n->next)
436 return;
437 print_body(p, n->next, m);
438 }
439
440
441 static void
442 print_foot(struct termp *p, const struct man_meta *meta)
443 {
444 struct tm *tm;
445 char *buf;
446
447 if (NULL == (buf = malloc(p->rmargin)))
448 err(1, "malloc");
449
450 tm = localtime(&meta->date);
451
452 #ifdef __OpenBSD__
453 if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm))
454 #else
455 if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
456 #endif
457 err(1, "strftime");
458
459 term_vspace(p);
460
461 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
462 p->rmargin = p->maxrmargin - strlen(buf);
463 p->offset = 0;
464
465 if (meta->source)
466 term_word(p, meta->source);
467 if (meta->source)
468 term_word(p, "");
469 term_flushln(p);
470
471 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
472 p->offset = p->rmargin;
473 p->rmargin = p->maxrmargin;
474 p->flags &= ~TERMP_NOBREAK;
475
476 term_word(p, buf);
477 term_flushln(p);
478
479 free(buf);
480 }
481
482
483 static void
484 print_head(struct termp *p, const struct man_meta *meta)
485 {
486 char *buf, *title;
487
488 p->rmargin = p->maxrmargin;
489 p->offset = 0;
490
491 if (NULL == (buf = malloc(p->rmargin)))
492 err(1, "malloc");
493 if (NULL == (title = malloc(p->rmargin)))
494 err(1, "malloc");
495
496 if (meta->vol)
497 (void)strlcpy(buf, meta->vol, p->rmargin);
498 else
499 *buf = 0;
500
501 (void)snprintf(title, p->rmargin, "%s(%d)",
502 meta->title, meta->msec);
503
504 p->offset = 0;
505 p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
506 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
507
508 term_word(p, title);
509 term_flushln(p);
510
511 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
512 p->offset = p->rmargin;
513 p->rmargin = p->maxrmargin - strlen(title);
514
515 term_word(p, buf);
516 term_flushln(p);
517
518 p->offset = p->rmargin;
519 p->rmargin = p->maxrmargin;
520 p->flags &= ~TERMP_NOBREAK;
521 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
522
523 term_word(p, title);
524 term_flushln(p);
525
526 p->rmargin = p->maxrmargin;
527 p->offset = 0;
528 p->flags &= ~TERMP_NOSPACE;
529
530 free(title);
531 free(buf);
532 }
533