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