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