]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_term.c
2bd7cd2f3027be0ce252749125f34d51d398086a
[mandoc.git] / mdoc_term.c
1 /* $Id: mdoc_term.c,v 1.68 2009/08/20 13:22:48 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 <sys/types.h>
18
19 #include <assert.h>
20 #include <ctype.h>
21 #include <err.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "term.h"
27 #include "mdoc.h"
28
29 #define INDENT 5
30 #define HALFINDENT 3
31
32 #define TTYPE_PROG 0
33 #define TTYPE_CMD_FLAG 1
34 #define TTYPE_CMD_ARG 2
35 #define TTYPE_SECTION 3
36 #define TTYPE_FUNC_DECL 4
37 #define TTYPE_VAR_DECL 5
38 #define TTYPE_FUNC_TYPE 6
39 #define TTYPE_FUNC_NAME 7
40 #define TTYPE_FUNC_ARG 8
41 #define TTYPE_LINK 9
42 #define TTYPE_SSECTION 10
43 #define TTYPE_FILE 11
44 #define TTYPE_EMPH 12
45 #define TTYPE_CONFIG 13
46 #define TTYPE_CMD 14
47 #define TTYPE_INCLUDE 15
48 #define TTYPE_SYMB 16
49 #define TTYPE_SYMBOL 17
50 #define TTYPE_DIAG 18
51 #define TTYPE_LINK_ANCHOR 19
52 #define TTYPE_LINK_TEXT 20
53 #define TTYPE_REF_JOURNAL 21
54 #define TTYPE_REF_TITLE 22
55 #define TTYPE_LIST 23
56 #define TTYPE_NMAX 24
57
58 const int ttypes[TTYPE_NMAX] = {
59 TERMP_BOLD, /* TTYPE_PROG */
60 TERMP_BOLD, /* TTYPE_CMD_FLAG */
61 TERMP_UNDER, /* TTYPE_CMD_ARG */
62 TERMP_BOLD, /* TTYPE_SECTION */
63 TERMP_BOLD, /* TTYPE_FUNC_DECL */
64 TERMP_UNDER, /* TTYPE_VAR_DECL */
65 TERMP_UNDER, /* TTYPE_FUNC_TYPE */
66 TERMP_BOLD, /* TTYPE_FUNC_NAME */
67 TERMP_UNDER, /* TTYPE_FUNC_ARG */
68 TERMP_UNDER, /* TTYPE_LINK */
69 TERMP_BOLD, /* TTYPE_SSECTION */
70 TERMP_UNDER, /* TTYPE_FILE */
71 TERMP_UNDER, /* TTYPE_EMPH */
72 TERMP_BOLD, /* TTYPE_CONFIG */
73 TERMP_BOLD, /* TTYPE_CMD */
74 TERMP_BOLD, /* TTYPE_INCLUDE */
75 TERMP_BOLD, /* TTYPE_SYMB */
76 TERMP_BOLD, /* TTYPE_SYMBOL */
77 TERMP_BOLD, /* TTYPE_DIAG */
78 TERMP_UNDER, /* TTYPE_LINK_ANCHOR */
79 TERMP_BOLD, /* TTYPE_LINK_TEXT */
80 TERMP_UNDER, /* TTYPE_REF_JOURNAL */
81 TERMP_UNDER, /* TTYPE_REF_TITLE */
82 TERMP_BOLD /* TTYPE_LIST */
83 };
84
85 struct termpair {
86 struct termpair *ppair;
87 int flag;
88 int count;
89 };
90
91 #define DECL_ARGS struct termp *p, \
92 struct termpair *pair, \
93 const struct mdoc_meta *meta, \
94 const struct mdoc_node *node
95
96 struct termact {
97 int (*pre)(DECL_ARGS);
98 void (*post)(DECL_ARGS);
99 };
100
101 static void termp____post(DECL_ARGS);
102 static void termp_an_post(DECL_ARGS);
103 static void termp_aq_post(DECL_ARGS);
104 static void termp_bd_post(DECL_ARGS);
105 static void termp_bl_post(DECL_ARGS);
106 static void termp_bq_post(DECL_ARGS);
107 static void termp_brq_post(DECL_ARGS);
108 static void termp_bx_post(DECL_ARGS);
109 static void termp_d1_post(DECL_ARGS);
110 static void termp_dq_post(DECL_ARGS);
111 static void termp_fd_post(DECL_ARGS);
112 static void termp_fn_post(DECL_ARGS);
113 static void termp_fo_post(DECL_ARGS);
114 static void termp_ft_post(DECL_ARGS);
115 static void termp_in_post(DECL_ARGS);
116 static void termp_it_post(DECL_ARGS);
117 static void termp_lb_post(DECL_ARGS);
118 static void termp_op_post(DECL_ARGS);
119 static void termp_pf_post(DECL_ARGS);
120 static void termp_pq_post(DECL_ARGS);
121 static void termp_qq_post(DECL_ARGS);
122 static void termp_sh_post(DECL_ARGS);
123 static void termp_sq_post(DECL_ARGS);
124 static void termp_ss_post(DECL_ARGS);
125 static void termp_vt_post(DECL_ARGS);
126
127 static int termp__j_pre(DECL_ARGS);
128 static int termp__t_pre(DECL_ARGS);
129 static int termp_an_pre(DECL_ARGS);
130 static int termp_ap_pre(DECL_ARGS);
131 static int termp_aq_pre(DECL_ARGS);
132 static int termp_ar_pre(DECL_ARGS);
133 static int termp_bd_pre(DECL_ARGS);
134 static int termp_bf_pre(DECL_ARGS);
135 static int termp_bq_pre(DECL_ARGS);
136 static int termp_br_pre(DECL_ARGS);
137 static int termp_brq_pre(DECL_ARGS);
138 static int termp_bt_pre(DECL_ARGS);
139 static int termp_cd_pre(DECL_ARGS);
140 static int termp_cm_pre(DECL_ARGS);
141 static int termp_d1_pre(DECL_ARGS);
142 static int termp_dq_pre(DECL_ARGS);
143 static int termp_em_pre(DECL_ARGS);
144 static int termp_ex_pre(DECL_ARGS);
145 static int termp_fa_pre(DECL_ARGS);
146 static int termp_fd_pre(DECL_ARGS);
147 static int termp_fl_pre(DECL_ARGS);
148 static int termp_fn_pre(DECL_ARGS);
149 static int termp_fo_pre(DECL_ARGS);
150 static int termp_ft_pre(DECL_ARGS);
151 static int termp_ic_pre(DECL_ARGS);
152 static int termp_in_pre(DECL_ARGS);
153 static int termp_it_pre(DECL_ARGS);
154 static int termp_lk_pre(DECL_ARGS);
155 static int termp_ms_pre(DECL_ARGS);
156 static int termp_mt_pre(DECL_ARGS);
157 static int termp_nd_pre(DECL_ARGS);
158 static int termp_nm_pre(DECL_ARGS);
159 static int termp_ns_pre(DECL_ARGS);
160 static int termp_op_pre(DECL_ARGS);
161 static int termp_pa_pre(DECL_ARGS);
162 static int termp_pf_pre(DECL_ARGS);
163 static int termp_pp_pre(DECL_ARGS);
164 static int termp_pq_pre(DECL_ARGS);
165 static int termp_qq_pre(DECL_ARGS);
166 static int termp_rs_pre(DECL_ARGS);
167 static int termp_rv_pre(DECL_ARGS);
168 static int termp_sh_pre(DECL_ARGS);
169 static int termp_sm_pre(DECL_ARGS);
170 static int termp_sp_pre(DECL_ARGS);
171 static int termp_sq_pre(DECL_ARGS);
172 static int termp_ss_pre(DECL_ARGS);
173 static int termp_sx_pre(DECL_ARGS);
174 static int termp_sy_pre(DECL_ARGS);
175 static int termp_ud_pre(DECL_ARGS);
176 static int termp_va_pre(DECL_ARGS);
177 static int termp_vt_pre(DECL_ARGS);
178 static int termp_xr_pre(DECL_ARGS);
179 static int termp_xx_pre(DECL_ARGS);
180
181 static const struct termact termacts[MDOC_MAX] = {
182 { termp_ap_pre, NULL }, /* Ap */
183 { NULL, NULL }, /* Dd */
184 { NULL, NULL }, /* Dt */
185 { NULL, NULL }, /* Os */
186 { termp_sh_pre, termp_sh_post }, /* Sh */
187 { termp_ss_pre, termp_ss_post }, /* Ss */
188 { termp_pp_pre, NULL }, /* Pp */
189 { termp_d1_pre, termp_d1_post }, /* D1 */
190 { termp_d1_pre, termp_d1_post }, /* Dl */
191 { termp_bd_pre, termp_bd_post }, /* Bd */
192 { NULL, NULL }, /* Ed */
193 { NULL, termp_bl_post }, /* Bl */
194 { NULL, NULL }, /* El */
195 { termp_it_pre, termp_it_post }, /* It */
196 { NULL, NULL }, /* Ad */
197 { termp_an_pre, termp_an_post }, /* An */
198 { termp_ar_pre, NULL }, /* Ar */
199 { termp_cd_pre, NULL }, /* Cd */
200 { termp_cm_pre, NULL }, /* Cm */
201 { NULL, NULL }, /* Dv */
202 { NULL, NULL }, /* Er */
203 { NULL, NULL }, /* Ev */
204 { termp_ex_pre, NULL }, /* Ex */
205 { termp_fa_pre, NULL }, /* Fa */
206 { termp_fd_pre, termp_fd_post }, /* Fd */
207 { termp_fl_pre, NULL }, /* Fl */
208 { termp_fn_pre, termp_fn_post }, /* Fn */
209 { termp_ft_pre, termp_ft_post }, /* Ft */
210 { termp_ic_pre, NULL }, /* Ic */
211 { termp_in_pre, termp_in_post }, /* In */
212 { NULL, NULL }, /* Li */
213 { termp_nd_pre, NULL }, /* Nd */
214 { termp_nm_pre, NULL }, /* Nm */
215 { termp_op_pre, termp_op_post }, /* Op */
216 { NULL, NULL }, /* Ot */
217 { termp_pa_pre, NULL }, /* Pa */
218 { termp_rv_pre, NULL }, /* Rv */
219 { NULL, NULL }, /* St */
220 { termp_va_pre, NULL }, /* Va */
221 { termp_vt_pre, termp_vt_post }, /* Vt */
222 { termp_xr_pre, NULL }, /* Xr */
223 { NULL, termp____post }, /* %A */
224 { NULL, termp____post }, /* %B */
225 { NULL, termp____post }, /* %D */
226 { NULL, termp____post }, /* %I */
227 { termp__j_pre, termp____post }, /* %J */
228 { NULL, termp____post }, /* %N */
229 { NULL, termp____post }, /* %O */
230 { NULL, termp____post }, /* %P */
231 { NULL, termp____post }, /* %R */
232 { termp__t_pre, termp____post }, /* %T */
233 { NULL, termp____post }, /* %V */
234 { NULL, NULL }, /* Ac */
235 { termp_aq_pre, termp_aq_post }, /* Ao */
236 { termp_aq_pre, termp_aq_post }, /* Aq */
237 { NULL, NULL }, /* At */
238 { NULL, NULL }, /* Bc */
239 { termp_bf_pre, NULL }, /* Bf */
240 { termp_bq_pre, termp_bq_post }, /* Bo */
241 { termp_bq_pre, termp_bq_post }, /* Bq */
242 { termp_xx_pre, NULL }, /* Bsx */
243 { NULL, termp_bx_post }, /* Bx */
244 { NULL, NULL }, /* Db */
245 { NULL, NULL }, /* Dc */
246 { termp_dq_pre, termp_dq_post }, /* Do */
247 { termp_dq_pre, termp_dq_post }, /* Dq */
248 { NULL, NULL }, /* Ec */
249 { NULL, NULL }, /* Ef */
250 { termp_em_pre, NULL }, /* Em */
251 { NULL, NULL }, /* Eo */
252 { termp_xx_pre, NULL }, /* Fx */
253 { termp_ms_pre, NULL }, /* Ms */
254 { NULL, NULL }, /* No */
255 { termp_ns_pre, NULL }, /* Ns */
256 { termp_xx_pre, NULL }, /* Nx */
257 { termp_xx_pre, NULL }, /* Ox */
258 { NULL, NULL }, /* Pc */
259 { termp_pf_pre, termp_pf_post }, /* Pf */
260 { termp_pq_pre, termp_pq_post }, /* Po */
261 { termp_pq_pre, termp_pq_post }, /* Pq */
262 { NULL, NULL }, /* Qc */
263 { termp_sq_pre, termp_sq_post }, /* Ql */
264 { termp_qq_pre, termp_qq_post }, /* Qo */
265 { termp_qq_pre, termp_qq_post }, /* Qq */
266 { NULL, NULL }, /* Re */
267 { termp_rs_pre, NULL }, /* Rs */
268 { NULL, NULL }, /* Sc */
269 { termp_sq_pre, termp_sq_post }, /* So */
270 { termp_sq_pre, termp_sq_post }, /* Sq */
271 { termp_sm_pre, NULL }, /* Sm */
272 { termp_sx_pre, NULL }, /* Sx */
273 { termp_sy_pre, NULL }, /* Sy */
274 { NULL, NULL }, /* Tn */
275 { termp_xx_pre, NULL }, /* Ux */
276 { NULL, NULL }, /* Xc */
277 { NULL, NULL }, /* Xo */
278 { termp_fo_pre, termp_fo_post }, /* Fo */
279 { NULL, NULL }, /* Fc */
280 { termp_op_pre, termp_op_post }, /* Oo */
281 { NULL, NULL }, /* Oc */
282 { NULL, NULL }, /* Bk */
283 { NULL, NULL }, /* Ek */
284 { termp_bt_pre, NULL }, /* Bt */
285 { NULL, NULL }, /* Hf */
286 { NULL, NULL }, /* Fr */
287 { termp_ud_pre, NULL }, /* Ud */
288 { NULL, termp_lb_post }, /* Lb */
289 { termp_pp_pre, NULL }, /* Lp */
290 { termp_lk_pre, NULL }, /* Lk */
291 { termp_mt_pre, NULL }, /* Mt */
292 { termp_brq_pre, termp_brq_post }, /* Brq */
293 { termp_brq_pre, termp_brq_post }, /* Bro */
294 { NULL, NULL }, /* Brc */
295 { NULL, NULL }, /* %C */
296 { NULL, NULL }, /* Es */
297 { NULL, NULL }, /* En */
298 { termp_xx_pre, NULL }, /* Dx */
299 { NULL, NULL }, /* %Q */
300 { termp_br_pre, NULL }, /* br */
301 { termp_sp_pre, NULL }, /* sp */
302 };
303
304 #ifdef __linux__
305 extern size_t strlcpy(char *, const char *, size_t);
306 extern size_t strlcat(char *, const char *, size_t);
307 #endif
308
309 static int arg_hasattr(int, const struct mdoc_node *);
310 static int arg_getattrs(const int *, int *, size_t,
311 const struct mdoc_node *);
312 static int arg_getattr(int, const struct mdoc_node *);
313 static size_t arg_offset(const struct mdoc_argv *);
314 static size_t arg_width(const struct mdoc_argv *, int);
315 static int arg_listtype(const struct mdoc_node *);
316 static void fmt_block_vspace(struct termp *,
317 const struct mdoc_node *,
318 const struct mdoc_node *);
319 static void print_node(DECL_ARGS);
320 static void print_head(struct termp *,
321 const struct mdoc_meta *);
322 static void print_body(DECL_ARGS);
323 static void print_foot(struct termp *,
324 const struct mdoc_meta *);
325
326
327 int
328 mdoc_run(struct termp *p, const struct mdoc *m)
329 {
330 /*
331 * Main output function. When this is called, assume that the
332 * tree is properly formed.
333 */
334 print_head(p, mdoc_meta(m));
335 assert(mdoc_node(m));
336 assert(MDOC_ROOT == mdoc_node(m)->type);
337 if (mdoc_node(m)->child)
338 print_body(p, NULL, mdoc_meta(m), mdoc_node(m)->child);
339 print_foot(p, mdoc_meta(m));
340 return(1);
341 }
342
343
344 static void
345 print_body(DECL_ARGS)
346 {
347
348 print_node(p, pair, meta, node);
349 if ( ! node->next)
350 return;
351 print_body(p, pair, meta, node->next);
352 }
353
354
355 static void
356 print_node(DECL_ARGS)
357 {
358 int dochild;
359 struct termpair npair;
360 size_t offset, rmargin;
361
362 dochild = 1;
363 offset = p->offset;
364 rmargin = p->rmargin;
365
366 npair.ppair = pair;
367 npair.flag = 0;
368 npair.count = 0;
369
370 /*
371 * Note on termpair. This allows a pre function to set a termp
372 * flag that is automatically unset after the body, but before
373 * the post function. Thus, if a pre uses a termpair flag, it
374 * must be reapplied in the post for use.
375 */
376
377 if (MDOC_TEXT != node->type) {
378 if (termacts[node->tok].pre)
379 if ( ! (*termacts[node->tok].pre)(p, &npair, meta, node))
380 dochild = 0;
381 } else /* MDOC_TEXT == node->type */
382 term_word(p, node->string);
383
384 /* Children. */
385
386 p->flags |= npair.flag;
387
388 if (dochild && node->child)
389 print_body(p, &npair, meta, node->child);
390
391 p->flags &= ~npair.flag;
392
393 /* Post-processing. */
394
395 if (MDOC_TEXT != node->type)
396 if (termacts[node->tok].post)
397 (*termacts[node->tok].post)(p, &npair, meta, node);
398
399 p->offset = offset;
400 p->rmargin = rmargin;
401 }
402
403
404 static void
405 print_foot(struct termp *p, const struct mdoc_meta *meta)
406 {
407 struct tm *tm;
408 char *buf, *os;
409
410 /*
411 * Output the footer in new-groff style, that is, three columns
412 * with the middle being the manual date and flanking columns
413 * being the operating system:
414 *
415 * SYSTEM DATE SYSTEM
416 */
417
418 if (NULL == (buf = malloc(p->rmargin)))
419 err(1, "malloc");
420 if (NULL == (os = malloc(p->rmargin)))
421 err(1, "malloc");
422
423 tm = localtime(&meta->date);
424
425 if (0 == strftime(buf, p->rmargin, "%B %e, %Y", tm))
426 err(1, "strftime");
427
428 (void)strlcpy(os, meta->os, p->rmargin);
429
430 term_vspace(p);
431
432 p->offset = 0;
433 p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
434 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
435
436 term_word(p, os);
437 term_flushln(p);
438
439 p->offset = p->rmargin;
440 p->rmargin = p->maxrmargin - strlen(os);
441 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
442
443 term_word(p, buf);
444 term_flushln(p);
445
446 p->offset = p->rmargin;
447 p->rmargin = p->maxrmargin;
448 p->flags &= ~TERMP_NOBREAK;
449 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
450
451 term_word(p, os);
452 term_flushln(p);
453
454 p->offset = 0;
455 p->rmargin = p->maxrmargin;
456 p->flags = 0;
457
458 free(buf);
459 free(os);
460 }
461
462
463 static void
464 print_head(struct termp *p, const struct mdoc_meta *meta)
465 {
466 char *buf, *title;
467
468 p->rmargin = p->maxrmargin;
469 p->offset = 0;
470
471 if (NULL == (buf = malloc(p->rmargin)))
472 err(1, "malloc");
473 if (NULL == (title = malloc(p->rmargin)))
474 err(1, "malloc");
475
476 /*
477 * The header is strange. It has three components, which are
478 * really two with the first duplicated. It goes like this:
479 *
480 * IDENTIFIER TITLE IDENTIFIER
481 *
482 * The IDENTIFIER is NAME(SECTION), which is the command-name
483 * (if given, or "unknown" if not) followed by the manual page
484 * section. These are given in `Dt'. The TITLE is a free-form
485 * string depending on the manual volume. If not specified, it
486 * switches on the manual section.
487 */
488
489 assert(meta->vol);
490 (void)strlcpy(buf, meta->vol, p->rmargin);
491
492 if (meta->arch) {
493 (void)strlcat(buf, " (", p->rmargin);
494 (void)strlcat(buf, meta->arch, p->rmargin);
495 (void)strlcat(buf, ")", p->rmargin);
496 }
497
498 (void)snprintf(title, p->rmargin, "%s(%d)",
499 meta->title, meta->msec);
500
501 p->offset = 0;
502 p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
503 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
504
505 term_word(p, title);
506 term_flushln(p);
507
508 p->offset = p->rmargin;
509 p->rmargin = p->maxrmargin - strlen(title);
510 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
511
512 term_word(p, buf);
513 term_flushln(p);
514
515 p->offset = p->rmargin;
516 p->rmargin = p->maxrmargin;
517 p->flags &= ~TERMP_NOBREAK;
518 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
519
520 term_word(p, title);
521 term_flushln(p);
522
523 p->offset = 0;
524 p->rmargin = p->maxrmargin;
525 p->flags &= ~TERMP_NOSPACE;
526
527 free(title);
528 free(buf);
529 }
530
531
532 static size_t
533 arg_width(const struct mdoc_argv *arg, int pos)
534 {
535 int i, len;
536 const char *p;
537
538 assert(pos < (int)arg->sz && pos >= 0);
539 assert(arg->value[pos]);
540
541 p = arg->value[pos];
542
543 if (0 == (len = (int)strlen(p)))
544 return(0);
545
546 for (i = 0; i < len - 1; i++)
547 if ( ! isdigit((u_char)p[i]))
548 break;
549
550 if (i == len - 1)
551 if ('n' == p[len - 1] || 'm' == p[len - 1])
552 return((size_t)atoi(p) + 2);
553
554 return((size_t)len + 2);
555 }
556
557
558 static int
559 arg_listtype(const struct mdoc_node *n)
560 {
561 int i, len;
562
563 assert(MDOC_BLOCK == n->type);
564
565 len = (int)(n->args ? n->args->argc : 0);
566
567 for (i = 0; i < len; i++)
568 switch (n->args->argv[i].arg) {
569 case (MDOC_Bullet):
570 /* FALLTHROUGH */
571 case (MDOC_Dash):
572 /* FALLTHROUGH */
573 case (MDOC_Enum):
574 /* FALLTHROUGH */
575 case (MDOC_Hyphen):
576 /* FALLTHROUGH */
577 case (MDOC_Tag):
578 /* FALLTHROUGH */
579 case (MDOC_Inset):
580 /* FALLTHROUGH */
581 case (MDOC_Diag):
582 /* FALLTHROUGH */
583 case (MDOC_Item):
584 /* FALLTHROUGH */
585 case (MDOC_Column):
586 /* FALLTHROUGH */
587 case (MDOC_Hang):
588 /* FALLTHROUGH */
589 case (MDOC_Ohang):
590 return(n->args->argv[i].arg);
591 default:
592 break;
593 }
594
595 return(-1);
596 }
597
598
599 static size_t
600 arg_offset(const struct mdoc_argv *arg)
601 {
602 int len, i;
603 const char *p;
604
605 assert(*arg->value);
606 p = *arg->value;
607
608 if (0 == strcmp(p, "left"))
609 return(0);
610 if (0 == strcmp(p, "indent"))
611 return(INDENT + 1);
612 if (0 == strcmp(p, "indent-two"))
613 return((INDENT + 1) * 2);
614
615 if (0 == (len = (int)strlen(p)))
616 return(0);
617
618 for (i = 0; i < len - 1; i++)
619 if ( ! isdigit((u_char)p[i]))
620 break;
621
622 if (i == len - 1)
623 if ('n' == p[len - 1] || 'm' == p[len - 1])
624 return((size_t)atoi(p));
625
626 return((size_t)len);
627 }
628
629
630 static int
631 arg_hasattr(int arg, const struct mdoc_node *n)
632 {
633
634 return(-1 != arg_getattr(arg, n));
635 }
636
637
638 static int
639 arg_getattr(int v, const struct mdoc_node *n)
640 {
641 int val;
642
643 return(arg_getattrs(&v, &val, 1, n) ? val : -1);
644 }
645
646
647 static int
648 arg_getattrs(const int *keys, int *vals,
649 size_t sz, const struct mdoc_node *n)
650 {
651 int i, j, k;
652
653 if (NULL == n->args)
654 return(0);
655
656 for (k = i = 0; i < (int)n->args->argc; i++)
657 for (j = 0; j < (int)sz; j++)
658 if (n->args->argv[i].arg == keys[j]) {
659 vals[j] = i;
660 k++;
661 }
662 return(k);
663 }
664
665
666 /* ARGSUSED */
667 static void
668 fmt_block_vspace(struct termp *p,
669 const struct mdoc_node *bl,
670 const struct mdoc_node *node)
671 {
672 const struct mdoc_node *n;
673
674 term_newln(p);
675
676 if (MDOC_Bl == bl->tok && arg_hasattr(MDOC_Compact, bl))
677 return;
678 assert(node);
679
680 /*
681 * Search through our prior nodes. If we follow a `Ss' or `Sh',
682 * then don't vspace.
683 */
684
685 for (n = node; n; n = n->parent) {
686 if (MDOC_BLOCK != n->type)
687 continue;
688 if (MDOC_Ss == n->tok)
689 return;
690 if (MDOC_Sh == n->tok)
691 return;
692 if (NULL == n->prev)
693 continue;
694 break;
695 }
696
697 /*
698 * XXX - not documented: a `-column' does not ever assert vspace
699 * within the list.
700 */
701
702 if (MDOC_Bl == bl->tok && arg_hasattr(MDOC_Column, bl))
703 if (node->prev && MDOC_It == node->prev->tok)
704 return;
705
706 /*
707 * XXX - not documented: a `-diag' without a body does not
708 * assert a vspace prior to the next element.
709 */
710 if (MDOC_Bl == bl->tok && arg_hasattr(MDOC_Diag, bl))
711 if (node->prev && MDOC_It == node->prev->tok) {
712 assert(node->prev->body);
713 if (NULL == node->prev->body->child)
714 return;
715 }
716
717 term_vspace(p);
718 }
719
720
721 /* ARGSUSED */
722 static int
723 termp_dq_pre(DECL_ARGS)
724 {
725
726 if (MDOC_BODY != node->type)
727 return(1);
728
729 term_word(p, "\\(lq");
730 p->flags |= TERMP_NOSPACE;
731 return(1);
732 }
733
734
735 /* ARGSUSED */
736 static void
737 termp_dq_post(DECL_ARGS)
738 {
739
740 if (MDOC_BODY != node->type)
741 return;
742
743 p->flags |= TERMP_NOSPACE;
744 term_word(p, "\\(rq");
745 }
746
747
748 /* ARGSUSED */
749 static int
750 termp_it_pre(DECL_ARGS)
751 {
752 const struct mdoc_node *bl, *n;
753 char buf[7];
754 int i, type, keys[3], vals[3];
755 size_t width, offset;
756
757 if (MDOC_BLOCK == node->type) {
758 fmt_block_vspace(p, node->parent->parent, node);
759 return(1);
760 }
761
762 bl = node->parent->parent->parent;
763
764 /* Save parent attributes. */
765
766 pair->flag = p->flags;
767
768 /* Get list width and offset. */
769
770 keys[0] = MDOC_Width;
771 keys[1] = MDOC_Offset;
772 keys[2] = MDOC_Column;
773
774 vals[0] = vals[1] = vals[2] = -1;
775
776 width = offset = 0;
777
778 (void)arg_getattrs(keys, vals, 3, bl);
779
780 type = arg_listtype(bl);
781 assert(-1 != type);
782
783 /* Calculate real width and offset. */
784
785 switch (type) {
786 case (MDOC_Column):
787 if (MDOC_BODY == node->type)
788 break;
789 /*
790 * Work around groff's column handling. The offset is
791 * equal to the sum of all widths leading to the current
792 * column (plus the -offset value). If this column
793 * exceeds the stated number of columns, the width is
794 * set as 0, else it's the stated column width (later
795 * the 0 will be adjusted to default 10 or, if in the
796 * last column case, set to stretch to the margin).
797 */
798 for (i = 0, n = node->prev; n && n &&
799 i < (int)bl->args[vals[2]].argv->sz;
800 n = n->prev, i++)
801 offset += arg_width
802 (&bl->args->argv[vals[2]], i);
803
804 /* Whether exceeds maximum column. */
805 if (i < (int)bl->args[vals[2]].argv->sz)
806 width = arg_width(&bl->args->argv[vals[2]], i);
807 else
808 width = 0;
809
810 if (vals[1] >= 0)
811 offset += arg_offset(&bl->args->argv[vals[1]]);
812 break;
813 default:
814 if (vals[0] >= 0)
815 width = arg_width(&bl->args->argv[vals[0]], 0);
816 if (vals[1] >= 0)
817 offset += arg_offset(&bl->args->argv[vals[1]]);
818 break;
819 }
820
821 /*
822 * List-type can override the width in the case of fixed-head
823 * values (bullet, dash/hyphen, enum). Tags need a non-zero
824 * offset.
825 */
826
827 switch (type) {
828 case (MDOC_Bullet):
829 /* FALLTHROUGH */
830 case (MDOC_Dash):
831 /* FALLTHROUGH */
832 case (MDOC_Hyphen):
833 if (width < 4)
834 width = 4;
835 break;
836 case (MDOC_Enum):
837 if (width < 5)
838 width = 5;
839 break;
840 case (MDOC_Hang):
841 if (0 == width)
842 width = 8;
843 break;
844 case (MDOC_Column):
845 /* FALLTHROUGH */
846 case (MDOC_Tag):
847 if (0 == width)
848 width = 10;
849 break;
850 default:
851 break;
852 }
853
854 /*
855 * Whitespace control. Inset bodies need an initial space,
856 * while diagonal bodies need two.
857 */
858
859 p->flags |= TERMP_NOSPACE;
860
861 switch (type) {
862 case (MDOC_Diag):
863 if (MDOC_BODY == node->type)
864 term_word(p, "\\ \\ ");
865 break;
866 case (MDOC_Inset):
867 if (MDOC_BODY == node->type)
868 term_word(p, "\\ ");
869 break;
870 default:
871 break;
872 }
873
874 p->flags |= TERMP_NOSPACE;
875
876 /*
877 * Style flags. Diagnostic heads need TTYPE_DIAG.
878 */
879
880 switch (type) {
881 case (MDOC_Diag):
882 if (MDOC_HEAD == node->type)
883 p->flags |= ttypes[TTYPE_DIAG];
884 break;
885 default:
886 break;
887 }
888
889 /*
890 * Pad and break control. This is the tricker part. Lists with
891 * set right-margins for the head get TERMP_NOBREAK because, if
892 * they overrun the margin, they wrap to the new margin.
893 * Correspondingly, the body for these types don't left-pad, as
894 * the head will pad out to to the right.
895 */
896
897 switch (type) {
898 case (MDOC_Bullet):
899 /* FALLTHROUGH */
900 case (MDOC_Dash):
901 /* FALLTHROUGH */
902 case (MDOC_Enum):
903 /* FALLTHROUGH */
904 case (MDOC_Hyphen):
905 if (MDOC_HEAD == node->type)
906 p->flags |= TERMP_NOBREAK;
907 else
908 p->flags |= TERMP_NOLPAD;
909 break;
910 case (MDOC_Hang):
911 if (MDOC_HEAD == node->type)
912 p->flags |= TERMP_NOBREAK;
913 else
914 p->flags |= TERMP_NOLPAD;
915
916 if (MDOC_HEAD != node->type)
917 break;
918
919 /*
920 * This is ugly. If `-hang' is specified and the body
921 * is a `Bl' or `Bd', then we want basically to nullify
922 * the "overstep" effect in term_flushln() and treat
923 * this as a `-ohang' list instead.
924 */
925 if (node->next->child &&
926 (MDOC_Bl == node->next->child->tok ||
927 MDOC_Bd == node->next->child->tok)) {
928 p->flags &= ~TERMP_NOBREAK;
929 p->flags &= ~TERMP_NOLPAD;
930 } else
931 p->flags |= TERMP_HANG;
932 break;
933 case (MDOC_Tag):
934 if (MDOC_HEAD == node->type)
935 p->flags |= TERMP_NOBREAK | TERMP_TWOSPACE;
936 else
937 p->flags |= TERMP_NOLPAD;
938
939 if (MDOC_HEAD != node->type)
940 break;
941 if (NULL == node->next || NULL == node->next->child)
942 p->flags |= TERMP_DANGLE;
943 break;
944 case (MDOC_Column):
945 if (MDOC_HEAD == node->type) {
946 assert(node->next);
947 if (MDOC_BODY == node->next->type)
948 p->flags &= ~TERMP_NOBREAK;
949 else
950 p->flags |= TERMP_NOBREAK;
951 if (node->prev)
952 p->flags |= TERMP_NOLPAD;
953 }
954 break;
955 case (MDOC_Diag):
956 if (MDOC_HEAD == node->type)
957 p->flags |= TERMP_NOBREAK;
958 break;
959 default:
960 break;
961 }
962
963 /*
964 * Margin control. Set-head-width lists have their right
965 * margins shortened. The body for these lists has the offset
966 * necessarily lengthened. Everybody gets the offset.
967 */
968
969 p->offset += offset;
970
971 switch (type) {
972 case (MDOC_Hang):
973 /*
974 * Same stipulation as above, regarding `-hang'. We
975 * don't want to recalculate rmargin and offsets when
976 * using `Bd' or `Bl' within `-hang' overstep lists.
977 */
978 if (MDOC_HEAD == node->type && node->next->child &&
979 (MDOC_Bl == node->next->child->tok ||
980 MDOC_Bd == node->next->child->tok))
981 break;
982 /* FALLTHROUGH */
983 case (MDOC_Bullet):
984 /* FALLTHROUGH */
985 case (MDOC_Dash):
986 /* FALLTHROUGH */
987 case (MDOC_Enum):
988 /* FALLTHROUGH */
989 case (MDOC_Hyphen):
990 /* FALLTHROUGH */
991 case (MDOC_Tag):
992 assert(width);
993 if (MDOC_HEAD == node->type)
994 p->rmargin = p->offset + width;
995 else
996 p->offset += width;
997 break;
998 case (MDOC_Column):
999 assert(width);
1000 p->rmargin = p->offset + width;
1001 /*
1002 * XXX - this behaviour is not documented: the
1003 * right-most column is filled to the right margin.
1004 */
1005 if (MDOC_HEAD == node->type &&
1006 MDOC_BODY == node->next->type)
1007 p->rmargin = p->maxrmargin;
1008 break;
1009 default:
1010 break;
1011 }
1012
1013 /*
1014 * The dash, hyphen, bullet and enum lists all have a special
1015 * HEAD character (temporarily bold, in some cases).
1016 */
1017
1018 if (MDOC_HEAD == node->type)
1019 switch (type) {
1020 case (MDOC_Bullet):
1021 p->flags |= TERMP_BOLD;
1022 term_word(p, "\\[bu]");
1023 p->flags &= ~TERMP_BOLD;
1024 break;
1025 case (MDOC_Dash):
1026 /* FALLTHROUGH */
1027 case (MDOC_Hyphen):
1028 p->flags |= TERMP_BOLD;
1029 term_word(p, "\\(hy");
1030 p->flags &= ~TERMP_BOLD;
1031 break;
1032 case (MDOC_Enum):
1033 (pair->ppair->ppair->count)++;
1034 (void)snprintf(buf, sizeof(buf), "%d.",
1035 pair->ppair->ppair->count);
1036 term_word(p, buf);
1037 break;
1038 default:
1039 break;
1040 }
1041
1042 /*
1043 * If we're not going to process our children, indicate so here.
1044 */
1045
1046 switch (type) {
1047 case (MDOC_Bullet):
1048 /* FALLTHROUGH */
1049 case (MDOC_Item):
1050 /* FALLTHROUGH */
1051 case (MDOC_Dash):
1052 /* FALLTHROUGH */
1053 case (MDOC_Hyphen):
1054 /* FALLTHROUGH */
1055 case (MDOC_Enum):
1056 if (MDOC_HEAD == node->type)
1057 return(0);
1058 break;
1059 case (MDOC_Column):
1060 if (MDOC_BODY == node->type)
1061 return(0);
1062 break;
1063 default:
1064 break;
1065 }
1066
1067 return(1);
1068 }
1069
1070
1071 /* ARGSUSED */
1072 static void
1073 termp_it_post(DECL_ARGS)
1074 {
1075 int type;
1076
1077 if (MDOC_BODY != node->type && MDOC_HEAD != node->type)
1078 return;
1079
1080 type = arg_listtype(node->parent->parent->parent);
1081 assert(-1 != type);
1082
1083 switch (type) {
1084 case (MDOC_Item):
1085 /* FALLTHROUGH */
1086 case (MDOC_Diag):
1087 /* FALLTHROUGH */
1088 case (MDOC_Inset):
1089 if (MDOC_BODY == node->type)
1090 term_flushln(p);
1091 break;
1092 case (MDOC_Column):
1093 if (MDOC_HEAD == node->type)
1094 term_flushln(p);
1095 break;
1096 default:
1097 term_flushln(p);
1098 break;
1099 }
1100
1101 p->flags = pair->flag;
1102 }
1103
1104
1105 /* ARGSUSED */
1106 static int
1107 termp_nm_pre(DECL_ARGS)
1108 {
1109
1110 if (SEC_SYNOPSIS == node->sec)
1111 term_newln(p);
1112
1113 pair->flag |= ttypes[TTYPE_PROG];
1114 p->flags |= ttypes[TTYPE_PROG];
1115
1116 if (NULL == node->child)
1117 term_word(p, meta->name);
1118
1119 return(1);
1120 }
1121
1122
1123 /* ARGSUSED */
1124 static int
1125 termp_fl_pre(DECL_ARGS)
1126 {
1127
1128 pair->flag |= ttypes[TTYPE_CMD_FLAG];
1129 p->flags |= ttypes[TTYPE_CMD_FLAG];
1130 term_word(p, "\\-");
1131 p->flags |= TERMP_NOSPACE;
1132 return(1);
1133 }
1134
1135
1136 /* ARGSUSED */
1137 static int
1138 termp_an_pre(DECL_ARGS)
1139 {
1140
1141 if (NULL == node->child)
1142 return(1);
1143
1144 /*
1145 * XXX: this is poorly documented. If not in the AUTHORS
1146 * section, `An -split' will cause newlines to occur before the
1147 * author name. If in the AUTHORS section, by default, the
1148 * first `An' invocation is nosplit, then all subsequent ones,
1149 * regardless of whether interspersed with other macros/text,
1150 * are split. -split, in this case, will override the condition
1151 * of the implied first -nosplit.
1152 */
1153
1154 if (node->sec == SEC_AUTHORS) {
1155 if ( ! (TERMP_ANPREC & p->flags)) {
1156 if (TERMP_SPLIT & p->flags)
1157 term_newln(p);
1158 return(1);
1159 }
1160 if (TERMP_NOSPLIT & p->flags)
1161 return(1);
1162 term_newln(p);
1163 return(1);
1164 }
1165
1166 if (TERMP_SPLIT & p->flags)
1167 term_newln(p);
1168
1169 return(1);
1170 }
1171
1172
1173 /* ARGSUSED */
1174 static void
1175 termp_an_post(DECL_ARGS)
1176 {
1177
1178 if (node->child) {
1179 if (SEC_AUTHORS == node->sec)
1180 p->flags |= TERMP_ANPREC;
1181 return;
1182 }
1183
1184 if (arg_getattr(MDOC_Split, node) > -1) {
1185 p->flags &= ~TERMP_NOSPLIT;
1186 p->flags |= TERMP_SPLIT;
1187 } else {
1188 p->flags &= ~TERMP_SPLIT;
1189 p->flags |= TERMP_NOSPLIT;
1190 }
1191
1192 }
1193
1194
1195 /* ARGSUSED */
1196 static int
1197 termp_ar_pre(DECL_ARGS)
1198 {
1199
1200 pair->flag |= ttypes[TTYPE_CMD_ARG];
1201 return(1);
1202 }
1203
1204
1205 /* ARGSUSED */
1206 static int
1207 termp_ns_pre(DECL_ARGS)
1208 {
1209
1210 p->flags |= TERMP_NOSPACE;
1211 return(1);
1212 }
1213
1214
1215 /* ARGSUSED */
1216 static int
1217 termp_pp_pre(DECL_ARGS)
1218 {
1219
1220 term_vspace(p);
1221 return(1);
1222 }
1223
1224
1225 /* ARGSUSED */
1226 static int
1227 termp_rs_pre(DECL_ARGS)
1228 {
1229
1230 if (MDOC_BLOCK == node->type && node->prev)
1231 term_vspace(p);
1232 return(1);
1233 }
1234
1235
1236 /* ARGSUSED */
1237 static int
1238 termp_rv_pre(DECL_ARGS)
1239 {
1240 const struct mdoc_node *nn;
1241
1242 term_newln(p);
1243 term_word(p, "The");
1244
1245 nn = node->child;
1246 assert(nn);
1247 for ( ; nn; nn = nn->next) {
1248 p->flags |= ttypes[TTYPE_FUNC_NAME];
1249 term_word(p, nn->string);
1250 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1251 p->flags |= TERMP_NOSPACE;
1252 if (nn->next && NULL == nn->next->next)
1253 term_word(p, "(), and");
1254 else if (nn->next)
1255 term_word(p, "(),");
1256 else
1257 term_word(p, "()");
1258 }
1259
1260 if (node->child->next)
1261 term_word(p, "functions return");
1262 else
1263 term_word(p, "function returns");
1264
1265 term_word(p, "the value 0 if successful; otherwise the value "
1266 "-1 is returned and the global variable");
1267
1268 p->flags |= ttypes[TTYPE_VAR_DECL];
1269 term_word(p, "errno");
1270 p->flags &= ~ttypes[TTYPE_VAR_DECL];
1271
1272 term_word(p, "is set to indicate the error.");
1273
1274 return(0);
1275 }
1276
1277
1278 /* ARGSUSED */
1279 static int
1280 termp_ex_pre(DECL_ARGS)
1281 {
1282 const struct mdoc_node *nn;
1283
1284 term_word(p, "The");
1285
1286 nn = node->child;
1287 assert(nn);
1288 for ( ; nn; nn = nn->next) {
1289 p->flags |= ttypes[TTYPE_PROG];
1290 term_word(p, nn->string);
1291 p->flags &= ~ttypes[TTYPE_PROG];
1292 p->flags |= TERMP_NOSPACE;
1293 if (nn->next && NULL == nn->next->next)
1294 term_word(p, ", and");
1295 else if (nn->next)
1296 term_word(p, ",");
1297 else
1298 p->flags &= ~TERMP_NOSPACE;
1299 }
1300
1301 if (node->child->next)
1302 term_word(p, "utilities exit");
1303 else
1304 term_word(p, "utility exits");
1305
1306 term_word(p, "0 on success, and >0 if an error occurs.");
1307
1308 return(0);
1309 }
1310
1311
1312 /* ARGSUSED */
1313 static int
1314 termp_nd_pre(DECL_ARGS)
1315 {
1316
1317 if (MDOC_BODY != node->type)
1318 return(1);
1319
1320 #if defined(__OpenBSD__) || defined(__linux__)
1321 term_word(p, "\\(en");
1322 #else
1323 term_word(p, "\\(em");
1324 #endif
1325 return(1);
1326 }
1327
1328
1329 /* ARGSUSED */
1330 static void
1331 termp_bl_post(DECL_ARGS)
1332 {
1333
1334 if (MDOC_BLOCK == node->type)
1335 term_newln(p);
1336 }
1337
1338
1339 /* ARGSUSED */
1340 static void
1341 termp_op_post(DECL_ARGS)
1342 {
1343
1344 if (MDOC_BODY != node->type)
1345 return;
1346 p->flags |= TERMP_NOSPACE;
1347 term_word(p, "\\(rB");
1348 }
1349
1350
1351 /* ARGSUSED */
1352 static int
1353 termp_xr_pre(DECL_ARGS)
1354 {
1355 const struct mdoc_node *n;
1356
1357 assert(node->child && MDOC_TEXT == node->child->type);
1358 n = node->child;
1359
1360 term_word(p, n->string);
1361 if (NULL == (n = n->next))
1362 return(0);
1363 p->flags |= TERMP_NOSPACE;
1364 term_word(p, "(");
1365 p->flags |= TERMP_NOSPACE;
1366 term_word(p, n->string);
1367 p->flags |= TERMP_NOSPACE;
1368 term_word(p, ")");
1369 return(0);
1370 }
1371
1372
1373 /* ARGSUSED */
1374 static int
1375 termp_vt_pre(DECL_ARGS)
1376 {
1377
1378 /* FIXME: this can be "type name". */
1379 pair->flag |= ttypes[TTYPE_VAR_DECL];
1380 return(1);
1381 }
1382
1383
1384 /* ARGSUSED */
1385 static void
1386 termp_vt_post(DECL_ARGS)
1387 {
1388
1389 if (node->sec != SEC_SYNOPSIS)
1390 return;
1391 if (node->next && MDOC_Vt == node->next->tok)
1392 term_newln(p);
1393 else if (node->next)
1394 term_vspace(p);
1395 }
1396
1397
1398 /* ARGSUSED */
1399 static int
1400 termp_fd_pre(DECL_ARGS)
1401 {
1402
1403 pair->flag |= ttypes[TTYPE_FUNC_DECL];
1404 return(1);
1405 }
1406
1407
1408 /* ARGSUSED */
1409 static void
1410 termp_fd_post(DECL_ARGS)
1411 {
1412
1413 if (node->sec != SEC_SYNOPSIS)
1414 return;
1415
1416 term_newln(p);
1417 if (node->next && MDOC_Fd != node->next->tok)
1418 term_vspace(p);
1419 }
1420
1421
1422 /* ARGSUSED */
1423 static int
1424 termp_sh_pre(DECL_ARGS)
1425 {
1426 /*
1427 * XXX: undocumented: using two `Sh' macros in sequence has no
1428 * vspace between calls, only a newline.
1429 */
1430 switch (node->type) {
1431 case (MDOC_BLOCK):
1432 if (node->prev && MDOC_Sh == node->prev->tok)
1433 if (NULL == node->prev->body->child)
1434 break;
1435 term_vspace(p);
1436 break;
1437 case (MDOC_HEAD):
1438 pair->flag |= ttypes[TTYPE_SECTION];
1439 break;
1440 case (MDOC_BODY):
1441 p->offset = INDENT;
1442 break;
1443 default:
1444 break;
1445 }
1446 return(1);
1447 }
1448
1449
1450 /* ARGSUSED */
1451 static void
1452 termp_sh_post(DECL_ARGS)
1453 {
1454
1455 switch (node->type) {
1456 case (MDOC_HEAD):
1457 term_newln(p);
1458 break;
1459 case (MDOC_BODY):
1460 term_newln(p);
1461 p->offset = 0;
1462 break;
1463 default:
1464 break;
1465 }
1466 }
1467
1468
1469 /* ARGSUSED */
1470 static int
1471 termp_op_pre(DECL_ARGS)
1472 {
1473
1474 switch (node->type) {
1475 case (MDOC_BODY):
1476 term_word(p, "\\(lB");
1477 p->flags |= TERMP_NOSPACE;
1478 break;
1479 default:
1480 break;
1481 }
1482 return(1);
1483 }
1484
1485
1486 /* ARGSUSED */
1487 static int
1488 termp_bt_pre(DECL_ARGS)
1489 {
1490
1491 term_word(p, "is currently in beta test.");
1492 return(1);
1493 }
1494
1495
1496 /* ARGSUSED */
1497 static void
1498 termp_lb_post(DECL_ARGS)
1499 {
1500
1501 term_newln(p);
1502 }
1503
1504
1505 /* ARGSUSED */
1506 static int
1507 termp_ud_pre(DECL_ARGS)
1508 {
1509
1510 term_word(p, "currently under development.");
1511 return(1);
1512 }
1513
1514
1515 /* ARGSUSED */
1516 static int
1517 termp_d1_pre(DECL_ARGS)
1518 {
1519
1520 if (MDOC_BLOCK != node->type)
1521 return(1);
1522 term_newln(p);
1523 p->offset += (INDENT + 1);
1524 return(1);
1525 }
1526
1527
1528 /* ARGSUSED */
1529 static void
1530 termp_d1_post(DECL_ARGS)
1531 {
1532
1533 if (MDOC_BLOCK != node->type)
1534 return;
1535 term_newln(p);
1536 }
1537
1538
1539 /* ARGSUSED */
1540 static int
1541 termp_aq_pre(DECL_ARGS)
1542 {
1543
1544 if (MDOC_BODY != node->type)
1545 return(1);
1546 term_word(p, "\\(la");
1547 p->flags |= TERMP_NOSPACE;
1548 return(1);
1549 }
1550
1551
1552 /* ARGSUSED */
1553 static void
1554 termp_aq_post(DECL_ARGS)
1555 {
1556
1557 if (MDOC_BODY != node->type)
1558 return;
1559 p->flags |= TERMP_NOSPACE;
1560 term_word(p, "\\(ra");
1561 }
1562
1563
1564 /* ARGSUSED */
1565 static int
1566 termp_ft_pre(DECL_ARGS)
1567 {
1568
1569 if (SEC_SYNOPSIS == node->sec)
1570 if (node->prev && MDOC_Fo == node->prev->tok)
1571 term_vspace(p);
1572 pair->flag |= ttypes[TTYPE_FUNC_TYPE];
1573 return(1);
1574 }
1575
1576
1577 /* ARGSUSED */
1578 static void
1579 termp_ft_post(DECL_ARGS)
1580 {
1581
1582 if (SEC_SYNOPSIS == node->sec)
1583 term_newln(p);
1584 }
1585
1586
1587 /* ARGSUSED */
1588 static int
1589 termp_fn_pre(DECL_ARGS)
1590 {
1591 const struct mdoc_node *n;
1592
1593 assert(node->child && MDOC_TEXT == node->child->type);
1594
1595 /* FIXME: can be "type funcname" "type varname"... */
1596
1597 p->flags |= ttypes[TTYPE_FUNC_NAME];
1598 term_word(p, node->child->string);
1599 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1600
1601 p->flags |= TERMP_NOSPACE;
1602 term_word(p, "(");
1603
1604 for (n = node->child->next; n; n = n->next) {
1605 p->flags |= ttypes[TTYPE_FUNC_ARG];
1606 term_word(p, n->string);
1607 p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1608 if (n->next)
1609 term_word(p, ",");
1610 }
1611
1612 term_word(p, ")");
1613
1614 if (SEC_SYNOPSIS == node->sec)
1615 term_word(p, ";");
1616
1617 return(0);
1618 }
1619
1620
1621 /* ARGSUSED */
1622 static void
1623 termp_fn_post(DECL_ARGS)
1624 {
1625
1626 if (node->sec == SEC_SYNOPSIS && node->next)
1627 term_vspace(p);
1628 }
1629
1630
1631 /* ARGSUSED */
1632 static int
1633 termp_sx_pre(DECL_ARGS)
1634 {
1635
1636 pair->flag |= ttypes[TTYPE_LINK];
1637 return(1);
1638 }
1639
1640
1641 /* ARGSUSED */
1642 static int
1643 termp_fa_pre(DECL_ARGS)
1644 {
1645 struct mdoc_node *n;
1646
1647 if (node->parent->tok != MDOC_Fo) {
1648 pair->flag |= ttypes[TTYPE_FUNC_ARG];
1649 return(1);
1650 }
1651
1652 for (n = node->child; n; n = n->next) {
1653 p->flags |= ttypes[TTYPE_FUNC_ARG];
1654 term_word(p, n->string);
1655 p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1656 if (n->next)
1657 term_word(p, ",");
1658 }
1659
1660 if (node->child && node->next && node->next->tok == MDOC_Fa)
1661 term_word(p, ",");
1662
1663 return(0);
1664 }
1665
1666
1667 /* ARGSUSED */
1668 static int
1669 termp_va_pre(DECL_ARGS)
1670 {
1671
1672 pair->flag |= ttypes[TTYPE_VAR_DECL];
1673 return(1);
1674 }
1675
1676
1677 /* ARGSUSED */
1678 static int
1679 termp_bd_pre(DECL_ARGS)
1680 {
1681 int i, type;
1682
1683 /*
1684 * This is fairly tricky due primarily to crappy documentation.
1685 * If -ragged or -filled are specified, the block does nothing
1686 * but change the indentation.
1687 *
1688 * If, on the other hand, -unfilled or -literal are specified,
1689 * then the game changes. Text is printed exactly as entered in
1690 * the display: if a macro line, a newline is appended to the
1691 * line. Blank lines are allowed.
1692 */
1693
1694 if (MDOC_BLOCK == node->type) {
1695 fmt_block_vspace(p, node, node);
1696 return(1);
1697 } else if (MDOC_BODY != node->type)
1698 return(1);
1699
1700 assert(node->parent->args);
1701
1702 for (type = -1, i = 0; -1 == type &&
1703 i < (int)node->parent->args->argc; i++) {
1704 switch (node->parent->args->argv[i].arg) {
1705 case (MDOC_Ragged):
1706 /* FALLTHROUGH */
1707 case (MDOC_Filled):
1708 /* FALLTHROUGH */
1709 case (MDOC_Unfilled):
1710 /* FALLTHROUGH */
1711 case (MDOC_Literal):
1712 type = node->parent->args->argv[i].arg;
1713 break;
1714 default:
1715 break;
1716 }
1717 }
1718
1719 assert(type > -1);
1720
1721 i = arg_getattr(MDOC_Offset, node->parent);
1722 if (-1 != i)
1723 p->offset += arg_offset(&node->parent->args->argv[i]);
1724
1725 switch (type) {
1726 case (MDOC_Literal):
1727 /* FALLTHROUGH */
1728 case (MDOC_Unfilled):
1729 break;
1730 default:
1731 return(1);
1732 }
1733
1734 for (node = node->child; node; node = node->next) {
1735 p->flags |= TERMP_NOSPACE;
1736 print_node(p, pair, meta, node);
1737 if (node->next)
1738 term_flushln(p);
1739 }
1740
1741 return(0);
1742 }
1743
1744
1745 /* ARGSUSED */
1746 static void
1747 termp_bd_post(DECL_ARGS)
1748 {
1749
1750 if (MDOC_BODY != node->type)
1751 return;
1752 p->flags |= TERMP_NOSPACE;
1753 term_flushln(p);
1754 }
1755
1756
1757 /* ARGSUSED */
1758 static int
1759 termp_qq_pre(DECL_ARGS)
1760 {
1761
1762 if (MDOC_BODY != node->type)
1763 return(1);
1764 term_word(p, "\"");
1765 p->flags |= TERMP_NOSPACE;
1766 return(1);
1767 }
1768
1769
1770 /* ARGSUSED */
1771 static void
1772 termp_qq_post(DECL_ARGS)
1773 {
1774
1775 if (MDOC_BODY != node->type)
1776 return;
1777 p->flags |= TERMP_NOSPACE;
1778 term_word(p, "\"");
1779 }
1780
1781
1782 /* ARGSUSED */
1783 static void
1784 termp_bx_post(DECL_ARGS)
1785 {
1786
1787 if (node->child)
1788 p->flags |= TERMP_NOSPACE;
1789 term_word(p, "BSD");
1790 }
1791
1792
1793 /* ARGSUSED */
1794 static int
1795 termp_xx_pre(DECL_ARGS)
1796 {
1797 const char *pp;
1798
1799 pp = NULL;
1800 switch (node->tok) {
1801 case (MDOC_Bsx):
1802 pp = "BSDI BSD/OS";
1803 break;
1804 case (MDOC_Dx):
1805 pp = "DragonFlyBSD";
1806 break;
1807 case (MDOC_Fx):
1808 pp = "FreeBSD";
1809 break;
1810 case (MDOC_Nx):
1811 pp = "NetBSD";
1812 break;
1813 case (MDOC_Ox):
1814 pp = "OpenBSD";
1815 break;
1816 case (MDOC_Ux):
1817 pp = "UNIX";
1818 break;
1819 default:
1820 break;
1821 }
1822
1823 assert(pp);
1824 term_word(p, pp);
1825 return(1);
1826 }
1827
1828
1829 /* ARGSUSED */
1830 static int
1831 termp_sq_pre(DECL_ARGS)
1832 {
1833
1834 if (MDOC_BODY != node->type)
1835 return(1);
1836 term_word(p, "\\(oq");
1837 p->flags |= TERMP_NOSPACE;
1838 return(1);
1839 }
1840
1841
1842 /* ARGSUSED */
1843 static void
1844 termp_sq_post(DECL_ARGS)
1845 {
1846
1847 if (MDOC_BODY != node->type)
1848 return;
1849 p->flags |= TERMP_NOSPACE;
1850 term_word(p, "\\(aq");
1851 }
1852
1853
1854 /* ARGSUSED */
1855 static int
1856 termp_pa_pre(DECL_ARGS)
1857 {
1858
1859 pair->flag |= ttypes[TTYPE_FILE];
1860 return(1);
1861 }
1862
1863
1864 /* ARGSUSED */
1865 static int
1866 termp_pf_pre(DECL_ARGS)
1867 {
1868
1869 p->flags |= TERMP_IGNDELIM;
1870 return(1);
1871 }
1872
1873
1874 /* ARGSUSED */
1875 static void
1876 termp_pf_post(DECL_ARGS)
1877 {
1878
1879 p->flags &= ~TERMP_IGNDELIM;
1880 p->flags |= TERMP_NOSPACE;
1881 }
1882
1883
1884 /* ARGSUSED */
1885 static int
1886 termp_ss_pre(DECL_ARGS)
1887 {
1888
1889 switch (node->type) {
1890 case (MDOC_BLOCK):
1891 term_newln(p);
1892 if (node->prev)
1893 term_vspace(p);
1894 break;
1895 case (MDOC_HEAD):
1896 pair->flag |= ttypes[TTYPE_SSECTION];
1897 p->offset = HALFINDENT;
1898 break;
1899 default:
1900 break;
1901 }
1902
1903 return(1);
1904 }
1905
1906
1907 /* ARGSUSED */
1908 static void
1909 termp_ss_post(DECL_ARGS)
1910 {
1911
1912 if (MDOC_HEAD == node->type)
1913 term_newln(p);
1914 }
1915
1916
1917 /* ARGSUSED */
1918 static int
1919 termp_em_pre(DECL_ARGS)
1920 {
1921
1922 pair->flag |= ttypes[TTYPE_EMPH];
1923 return(1);
1924 }
1925
1926
1927 /* ARGSUSED */
1928 static int
1929 termp_cd_pre(DECL_ARGS)
1930 {
1931
1932 pair->flag |= ttypes[TTYPE_CONFIG];
1933 term_newln(p);
1934 return(1);
1935 }
1936
1937
1938 /* ARGSUSED */
1939 static int
1940 termp_cm_pre(DECL_ARGS)
1941 {
1942
1943 pair->flag |= ttypes[TTYPE_CMD_FLAG];
1944 return(1);
1945 }
1946
1947
1948 /* ARGSUSED */
1949 static int
1950 termp_ic_pre(DECL_ARGS)
1951 {
1952
1953 pair->flag |= ttypes[TTYPE_CMD];
1954 return(1);
1955 }
1956
1957
1958 /* ARGSUSED */
1959 static int
1960 termp_in_pre(DECL_ARGS)
1961 {
1962
1963 pair->flag |= ttypes[TTYPE_INCLUDE];
1964 p->flags |= ttypes[TTYPE_INCLUDE];
1965
1966 if (SEC_SYNOPSIS == node->sec)
1967 term_word(p, "#include");
1968
1969 term_word(p, "<");
1970 p->flags |= TERMP_NOSPACE;
1971 return(1);
1972 }
1973
1974
1975 /* ARGSUSED */
1976 static void
1977 termp_in_post(DECL_ARGS)
1978 {
1979
1980 p->flags |= TERMP_NOSPACE | ttypes[TTYPE_INCLUDE];
1981 term_word(p, ">");
1982 p->flags &= ~ttypes[TTYPE_INCLUDE];
1983
1984 if (SEC_SYNOPSIS != node->sec)
1985 return;
1986
1987 term_newln(p);
1988 /*
1989 * XXX Not entirely correct. If `.In foo bar' is specified in
1990 * the SYNOPSIS section, then it produces a single break after
1991 * the <foo>; mandoc asserts a vertical space. Since this
1992 * construction is rarely used, I think it's fine.
1993 */
1994 if (node->next && MDOC_In != node->next->tok)
1995 term_vspace(p);
1996 }
1997
1998
1999 /* ARGSUSED */
2000 static int
2001 termp_sp_pre(DECL_ARGS)
2002 {
2003 int i, len;
2004
2005 if (NULL == node->child) {
2006 term_vspace(p);
2007 return(0);
2008 }
2009
2010 len = atoi(node->child->string);
2011 if (0 == len)
2012 term_newln(p);
2013 for (i = 0; i < len; i++)
2014 term_vspace(p);
2015
2016 return(0);
2017 }
2018
2019
2020 /* ARGSUSED */
2021 static int
2022 termp_br_pre(DECL_ARGS)
2023 {
2024
2025 term_newln(p);
2026 return(1);
2027 }
2028
2029
2030 /* ARGSUSED */
2031 static int
2032 termp_brq_pre(DECL_ARGS)
2033 {
2034
2035 if (MDOC_BODY != node->type)
2036 return(1);
2037 term_word(p, "\\(lC");
2038 p->flags |= TERMP_NOSPACE;
2039 return(1);
2040 }
2041
2042
2043 /* ARGSUSED */
2044 static void
2045 termp_brq_post(DECL_ARGS)
2046 {
2047
2048 if (MDOC_BODY != node->type)
2049 return;
2050 p->flags |= TERMP_NOSPACE;
2051 term_word(p, "\\(rC");
2052 }
2053
2054
2055 /* ARGSUSED */
2056 static int
2057 termp_bq_pre(DECL_ARGS)
2058 {
2059
2060 if (MDOC_BODY != node->type)
2061 return(1);
2062 term_word(p, "\\(lB");
2063 p->flags |= TERMP_NOSPACE;
2064 return(1);
2065 }
2066
2067
2068 /* ARGSUSED */
2069 static void
2070 termp_bq_post(DECL_ARGS)
2071 {
2072
2073 if (MDOC_BODY != node->type)
2074 return;
2075 p->flags |= TERMP_NOSPACE;
2076 term_word(p, "\\(rB");
2077 }
2078
2079
2080 /* ARGSUSED */
2081 static int
2082 termp_pq_pre(DECL_ARGS)
2083 {
2084
2085 if (MDOC_BODY != node->type)
2086 return(1);
2087 term_word(p, "\\&(");
2088 p->flags |= TERMP_NOSPACE;
2089 return(1);
2090 }
2091
2092
2093 /* ARGSUSED */
2094 static void
2095 termp_pq_post(DECL_ARGS)
2096 {
2097
2098 if (MDOC_BODY != node->type)
2099 return;
2100 term_word(p, ")");
2101 }
2102
2103
2104 /* ARGSUSED */
2105 static int
2106 termp_fo_pre(DECL_ARGS)
2107 {
2108 const struct mdoc_node *n;
2109
2110 if (MDOC_BODY == node->type) {
2111 p->flags |= TERMP_NOSPACE;
2112 term_word(p, "(");
2113 p->flags |= TERMP_NOSPACE;
2114 return(1);
2115 } else if (MDOC_HEAD != node->type)
2116 return(1);
2117
2118 p->flags |= ttypes[TTYPE_FUNC_NAME];
2119 for (n = node->child; n; n = n->next) {
2120 assert(MDOC_TEXT == n->type);
2121 term_word(p, n->string);
2122 }
2123 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
2124
2125 return(0);
2126 }
2127
2128
2129 /* ARGSUSED */
2130 static void
2131 termp_fo_post(DECL_ARGS)
2132 {
2133
2134 if (MDOC_BODY != node->type)
2135 return;
2136 p->flags |= TERMP_NOSPACE;
2137 term_word(p, ")");
2138 p->flags |= TERMP_NOSPACE;
2139 term_word(p, ";");
2140 term_newln(p);
2141 }
2142
2143
2144 /* ARGSUSED */
2145 static int
2146 termp_bf_pre(DECL_ARGS)
2147 {
2148 const struct mdoc_node *n;
2149
2150 if (MDOC_HEAD == node->type)
2151 return(0);
2152 else if (MDOC_BLOCK != node->type)
2153 return(1);
2154
2155 if (NULL == (n = node->head->child)) {
2156 if (arg_hasattr(MDOC_Emphasis, node))
2157 pair->flag |= ttypes[TTYPE_EMPH];
2158 else if (arg_hasattr(MDOC_Symbolic, node))
2159 pair->flag |= ttypes[TTYPE_SYMB];
2160
2161 return(1);
2162 }
2163
2164 assert(MDOC_TEXT == n->type);
2165 if (0 == strcmp("Em", n->string))
2166 pair->flag |= ttypes[TTYPE_EMPH];
2167 else if (0 == strcmp("Sy", n->string))
2168 pair->flag |= ttypes[TTYPE_SYMB];
2169
2170 return(1);
2171 }
2172
2173
2174 /* ARGSUSED */
2175 static int
2176 termp_sy_pre(DECL_ARGS)
2177 {
2178
2179 pair->flag |= ttypes[TTYPE_SYMB];
2180 return(1);
2181 }
2182
2183
2184 /* ARGSUSED */
2185 static int
2186 termp_ms_pre(DECL_ARGS)
2187 {
2188
2189 pair->flag |= ttypes[TTYPE_SYMBOL];
2190 return(1);
2191 }
2192
2193
2194
2195 /* ARGSUSED */
2196 static int
2197 termp_sm_pre(DECL_ARGS)
2198 {
2199
2200 assert(node->child && MDOC_TEXT == node->child->type);
2201 if (0 == strcmp("on", node->child->string)) {
2202 p->flags &= ~TERMP_NONOSPACE;
2203 p->flags &= ~TERMP_NOSPACE;
2204 } else
2205 p->flags |= TERMP_NONOSPACE;
2206
2207 return(0);
2208 }
2209
2210
2211 /* ARGSUSED */
2212 static int
2213 termp_ap_pre(DECL_ARGS)
2214 {
2215
2216 p->flags |= TERMP_NOSPACE;
2217 term_word(p, "\\(aq");
2218 p->flags |= TERMP_NOSPACE;
2219 return(1);
2220 }
2221
2222
2223 /* ARGSUSED */
2224 static int
2225 termp__j_pre(DECL_ARGS)
2226 {
2227
2228 pair->flag |= ttypes[TTYPE_REF_JOURNAL];
2229 return(1);
2230 }
2231
2232
2233 /* ARGSUSED */
2234 static int
2235 termp__t_pre(DECL_ARGS)
2236 {
2237
2238 pair->flag |= ttypes[TTYPE_REF_TITLE];
2239 return(1);
2240 }
2241
2242
2243 /* ARGSUSED */
2244 static void
2245 termp____post(DECL_ARGS)
2246 {
2247
2248 p->flags |= TERMP_NOSPACE;
2249 term_word(p, node->next ? "," : ".");
2250 }
2251
2252
2253 /* ARGSUSED */
2254 static int
2255 termp_lk_pre(DECL_ARGS)
2256 {
2257 const struct mdoc_node *n;
2258
2259 assert(node->child);
2260 n = node->child;
2261
2262 if (NULL == n->next) {
2263 pair->flag |= ttypes[TTYPE_LINK_ANCHOR];
2264 return(1);
2265 }
2266
2267 p->flags |= ttypes[TTYPE_LINK_ANCHOR];
2268 term_word(p, n->string);
2269 p->flags |= TERMP_NOSPACE;
2270 term_word(p, ":");
2271 p->flags &= ~ttypes[TTYPE_LINK_ANCHOR];
2272
2273 p->flags |= ttypes[TTYPE_LINK_TEXT];
2274 for (n = n->next; n; n = n->next)
2275 term_word(p, n->string);
2276
2277 p->flags &= ~ttypes[TTYPE_LINK_TEXT];
2278 return(0);
2279 }
2280
2281
2282 /* ARGSUSED */
2283 static int
2284 termp_mt_pre(DECL_ARGS)
2285 {
2286
2287 pair->flag |= ttypes[TTYPE_LINK_ANCHOR];
2288 return(1);
2289 }
2290
2291