1 1.16 wiz /* Id: man_term.c,v 1.236 2021/06/28 19:50:15 schwarze Exp */ 2 1.1 joerg /* 3 1.16 wiz * Copyright (c) 2010-2015, 2017-2020 Ingo Schwarze <schwarze (at) openbsd.org> 4 1.10 joerg * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps (at) bsd.lv> 5 1.1 joerg * 6 1.1 joerg * Permission to use, copy, modify, and distribute this software for any 7 1.1 joerg * purpose with or without fee is hereby granted, provided that the above 8 1.1 joerg * copyright notice and this permission notice appear in all copies. 9 1.1 joerg * 10 1.12 christos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 11 1.1 joerg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 1.12 christos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 13 1.1 joerg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 1.1 joerg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 1.1 joerg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 1.1 joerg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 1.16 wiz * 18 1.16 wiz * Plain text formatter for man(7), used by mandoc(1) 19 1.16 wiz * for ASCII, UTF-8, PostScript, and PDF output. 20 1.1 joerg */ 21 1.2 joerg #include "config.h" 22 1.2 joerg 23 1.1 joerg #include <sys/types.h> 24 1.1 joerg 25 1.1 joerg #include <assert.h> 26 1.1 joerg #include <ctype.h> 27 1.11 christos #include <limits.h> 28 1.1 joerg #include <stdio.h> 29 1.1 joerg #include <stdlib.h> 30 1.1 joerg #include <string.h> 31 1.1 joerg 32 1.12 christos #include "mandoc_aux.h" 33 1.16 wiz #include "mandoc.h" 34 1.12 christos #include "roff.h" 35 1.12 christos #include "man.h" 36 1.1 joerg #include "out.h" 37 1.1 joerg #include "term.h" 38 1.16 wiz #include "term_tag.h" 39 1.1 joerg #include "main.h" 40 1.1 joerg 41 1.8 joerg #define MAXMARGINS 64 /* maximum number of indented scopes */ 42 1.1 joerg 43 1.1 joerg struct mtermp { 44 1.11 christos int lmargin[MAXMARGINS]; /* margins (incl. vis. page) */ 45 1.8 joerg int lmargincur; /* index of current margin */ 46 1.8 joerg int lmarginsz; /* actual number of nested margins */ 47 1.8 joerg size_t offset; /* default offset to visible page */ 48 1.10 joerg int pardist; /* vert. space before par., unit: [v] */ 49 1.1 joerg }; 50 1.1 joerg 51 1.11 christos #define DECL_ARGS struct termp *p, \ 52 1.1 joerg struct mtermp *mt, \ 53 1.12 christos struct roff_node *n, \ 54 1.12 christos const struct roff_meta *meta 55 1.1 joerg 56 1.15 christos struct man_term_act { 57 1.1 joerg int (*pre)(DECL_ARGS); 58 1.1 joerg void (*post)(DECL_ARGS); 59 1.2 joerg int flags; 60 1.2 joerg #define MAN_NOTEXT (1 << 0) /* Never has text children. */ 61 1.1 joerg }; 62 1.1 joerg 63 1.2 joerg static void print_man_nodelist(DECL_ARGS); 64 1.2 joerg static void print_man_node(DECL_ARGS); 65 1.12 christos static void print_man_head(struct termp *, 66 1.12 christos const struct roff_meta *); 67 1.12 christos static void print_man_foot(struct termp *, 68 1.12 christos const struct roff_meta *); 69 1.11 christos static void print_bvspace(struct termp *, 70 1.16 wiz struct roff_node *, int); 71 1.1 joerg 72 1.1 joerg static int pre_B(DECL_ARGS); 73 1.14 christos static int pre_DT(DECL_ARGS); 74 1.1 joerg static int pre_HP(DECL_ARGS); 75 1.1 joerg static int pre_I(DECL_ARGS); 76 1.1 joerg static int pre_IP(DECL_ARGS); 77 1.9 joerg static int pre_OP(DECL_ARGS); 78 1.10 joerg static int pre_PD(DECL_ARGS); 79 1.1 joerg static int pre_PP(DECL_ARGS); 80 1.1 joerg static int pre_RS(DECL_ARGS); 81 1.1 joerg static int pre_SH(DECL_ARGS); 82 1.1 joerg static int pre_SS(DECL_ARGS); 83 1.15 christos static int pre_SY(DECL_ARGS); 84 1.1 joerg static int pre_TP(DECL_ARGS); 85 1.10 joerg static int pre_UR(DECL_ARGS); 86 1.15 christos static int pre_abort(DECL_ARGS); 87 1.9 joerg static int pre_alternate(DECL_ARGS); 88 1.1 joerg static int pre_ign(DECL_ARGS); 89 1.5 joerg static int pre_in(DECL_ARGS); 90 1.5 joerg static int pre_literal(DECL_ARGS); 91 1.1 joerg 92 1.1 joerg static void post_IP(DECL_ARGS); 93 1.1 joerg static void post_HP(DECL_ARGS); 94 1.1 joerg static void post_RS(DECL_ARGS); 95 1.1 joerg static void post_SH(DECL_ARGS); 96 1.15 christos static void post_SY(DECL_ARGS); 97 1.1 joerg static void post_TP(DECL_ARGS); 98 1.10 joerg static void post_UR(DECL_ARGS); 99 1.1 joerg 100 1.15 christos static const struct man_term_act man_term_acts[MAN_MAX - MAN_TH] = { 101 1.2 joerg { NULL, NULL, 0 }, /* TH */ 102 1.2 joerg { pre_SH, post_SH, 0 }, /* SH */ 103 1.15 christos { pre_SS, post_SH, 0 }, /* SS */ 104 1.2 joerg { pre_TP, post_TP, 0 }, /* TP */ 105 1.15 christos { pre_TP, post_TP, 0 }, /* TQ */ 106 1.15 christos { pre_abort, NULL, 0 }, /* LP */ 107 1.2 joerg { pre_PP, NULL, 0 }, /* PP */ 108 1.15 christos { pre_abort, NULL, 0 }, /* P */ 109 1.2 joerg { pre_IP, post_IP, 0 }, /* IP */ 110 1.11 christos { pre_HP, post_HP, 0 }, /* HP */ 111 1.2 joerg { NULL, NULL, 0 }, /* SM */ 112 1.2 joerg { pre_B, NULL, 0 }, /* SB */ 113 1.6 joerg { pre_alternate, NULL, 0 }, /* BI */ 114 1.6 joerg { pre_alternate, NULL, 0 }, /* IB */ 115 1.6 joerg { pre_alternate, NULL, 0 }, /* BR */ 116 1.6 joerg { pre_alternate, NULL, 0 }, /* RB */ 117 1.2 joerg { NULL, NULL, 0 }, /* R */ 118 1.2 joerg { pre_B, NULL, 0 }, /* B */ 119 1.2 joerg { pre_I, NULL, 0 }, /* I */ 120 1.6 joerg { pre_alternate, NULL, 0 }, /* IR */ 121 1.6 joerg { pre_alternate, NULL, 0 }, /* RI */ 122 1.2 joerg { NULL, NULL, 0 }, /* RE */ 123 1.2 joerg { pre_RS, post_RS, 0 }, /* RS */ 124 1.14 christos { pre_DT, NULL, 0 }, /* DT */ 125 1.11 christos { pre_ign, NULL, MAN_NOTEXT }, /* UC */ 126 1.10 joerg { pre_PD, NULL, MAN_NOTEXT }, /* PD */ 127 1.2 joerg { pre_ign, NULL, 0 }, /* AT */ 128 1.5 joerg { pre_in, NULL, MAN_NOTEXT }, /* in */ 129 1.15 christos { pre_SY, post_SY, 0 }, /* SY */ 130 1.15 christos { NULL, NULL, 0 }, /* YS */ 131 1.9 joerg { pre_OP, NULL, 0 }, /* OP */ 132 1.10 joerg { pre_literal, NULL, 0 }, /* EX */ 133 1.10 joerg { pre_literal, NULL, 0 }, /* EE */ 134 1.10 joerg { pre_UR, post_UR, 0 }, /* UR */ 135 1.10 joerg { NULL, NULL, 0 }, /* UE */ 136 1.14 christos { pre_UR, post_UR, 0 }, /* MT */ 137 1.14 christos { NULL, NULL, 0 }, /* ME */ 138 1.1 joerg }; 139 1.15 christos static const struct man_term_act *man_term_act(enum roff_tok); 140 1.1 joerg 141 1.1 joerg 142 1.15 christos static const struct man_term_act * 143 1.15 christos man_term_act(enum roff_tok tok) 144 1.15 christos { 145 1.15 christos assert(tok >= MAN_TH && tok <= MAN_MAX); 146 1.15 christos return man_term_acts + (tok - MAN_TH); 147 1.15 christos } 148 1.15 christos 149 1.1 joerg void 150 1.15 christos terminal_man(void *arg, const struct roff_meta *man) 151 1.1 joerg { 152 1.15 christos struct mtermp mt; 153 1.1 joerg struct termp *p; 154 1.16 wiz struct roff_node *n, *nc, *nn; 155 1.13 christos size_t save_defindent; 156 1.1 joerg 157 1.1 joerg p = (struct termp *)arg; 158 1.14 christos save_defindent = p->defindent; 159 1.14 christos if (p->synopsisonly == 0 && p->defindent == 0) 160 1.14 christos p->defindent = 7; 161 1.14 christos p->tcol->rmargin = p->maxrmargin = p->defrmargin; 162 1.14 christos term_tab_set(p, NULL); 163 1.14 christos term_tab_set(p, "T"); 164 1.14 christos term_tab_set(p, ".5i"); 165 1.2 joerg 166 1.15 christos memset(&mt, 0, sizeof(mt)); 167 1.9 joerg mt.lmargin[mt.lmargincur] = term_len(p, p->defindent); 168 1.9 joerg mt.offset = term_len(p, p->defindent); 169 1.10 joerg mt.pardist = 1; 170 1.1 joerg 171 1.12 christos n = man->first->child; 172 1.11 christos if (p->synopsisonly) { 173 1.16 wiz for (nn = NULL; n != NULL; n = n->next) { 174 1.16 wiz if (n->tok != MAN_SH) 175 1.16 wiz continue; 176 1.16 wiz nc = n->child->child; 177 1.16 wiz if (nc->type != ROFFT_TEXT) 178 1.16 wiz continue; 179 1.16 wiz if (strcmp(nc->string, "SYNOPSIS") == 0) 180 1.11 christos break; 181 1.16 wiz if (nn == NULL && strcmp(nc->string, "NAME") == 0) 182 1.16 wiz nn = n; 183 1.11 christos } 184 1.16 wiz if (n == NULL) 185 1.16 wiz n = nn; 186 1.16 wiz p->flags |= TERMP_NOSPACE; 187 1.16 wiz if (n != NULL && (n = n->child->next->child) != NULL) 188 1.16 wiz print_man_nodelist(p, &mt, n, man); 189 1.16 wiz term_newln(p); 190 1.11 christos } else { 191 1.15 christos term_begin(p, print_man_head, print_man_foot, man); 192 1.11 christos p->flags |= TERMP_NOSPACE; 193 1.11 christos if (n != NULL) 194 1.15 christos print_man_nodelist(p, &mt, n, man); 195 1.11 christos term_end(p); 196 1.11 christos } 197 1.14 christos p->defindent = save_defindent; 198 1.1 joerg } 199 1.1 joerg 200 1.8 joerg /* 201 1.8 joerg * Printing leading vertical space before a block. 202 1.8 joerg * This is used for the paragraph macros. 203 1.8 joerg * The rules are pretty simple, since there's very little nesting going 204 1.8 joerg * on here. Basically, if we're the first within another block (SS/SH), 205 1.8 joerg * then don't emit vertical space. If we are (RS), then do. If not the 206 1.8 joerg * first, print it. 207 1.8 joerg */ 208 1.1 joerg static void 209 1.16 wiz print_bvspace(struct termp *p, struct roff_node *n, int pardist) 210 1.1 joerg { 211 1.16 wiz struct roff_node *nch; 212 1.16 wiz int i; 213 1.8 joerg 214 1.1 joerg term_newln(p); 215 1.1 joerg 216 1.16 wiz if (n->body != NULL && 217 1.16 wiz (nch = roff_node_child(n->body)) != NULL && 218 1.16 wiz nch->type == ROFFT_TBL) 219 1.16 wiz return; 220 1.7 joerg 221 1.16 wiz if (n->parent->tok != MAN_RS && roff_node_prev(n) == NULL) 222 1.16 wiz return; 223 1.1 joerg 224 1.10 joerg for (i = 0; i < pardist; i++) 225 1.10 joerg term_vspace(p); 226 1.1 joerg } 227 1.1 joerg 228 1.11 christos 229 1.1 joerg static int 230 1.15 christos pre_abort(DECL_ARGS) 231 1.15 christos { 232 1.15 christos abort(); 233 1.15 christos } 234 1.15 christos 235 1.15 christos static int 236 1.1 joerg pre_ign(DECL_ARGS) 237 1.1 joerg { 238 1.12 christos return 0; 239 1.1 joerg } 240 1.1 joerg 241 1.11 christos static int 242 1.1 joerg pre_I(DECL_ARGS) 243 1.1 joerg { 244 1.2 joerg term_fontrepl(p, TERMFONT_UNDER); 245 1.12 christos return 1; 246 1.1 joerg } 247 1.1 joerg 248 1.1 joerg static int 249 1.5 joerg pre_literal(DECL_ARGS) 250 1.1 joerg { 251 1.5 joerg term_newln(p); 252 1.6 joerg 253 1.8 joerg /* 254 1.8 joerg * Unlike .IP and .TP, .HP does not have a HEAD. 255 1.8 joerg * So in case a second call to term_flushln() is needed, 256 1.8 joerg * indentation has to be set up explicitly. 257 1.8 joerg */ 258 1.14 christos if (n->parent->tok == MAN_HP && p->tcol->rmargin < p->maxrmargin) { 259 1.14 christos p->tcol->offset = p->tcol->rmargin; 260 1.14 christos p->tcol->rmargin = p->maxrmargin; 261 1.10 joerg p->trailspace = 0; 262 1.11 christos p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); 263 1.8 joerg p->flags |= TERMP_NOSPACE; 264 1.8 joerg } 265 1.12 christos return 0; 266 1.1 joerg } 267 1.1 joerg 268 1.1 joerg static int 269 1.10 joerg pre_PD(DECL_ARGS) 270 1.10 joerg { 271 1.11 christos struct roffsu su; 272 1.10 joerg 273 1.10 joerg n = n->child; 274 1.11 christos if (n == NULL) { 275 1.10 joerg mt->pardist = 1; 276 1.12 christos return 0; 277 1.10 joerg } 278 1.12 christos assert(n->type == ROFFT_TEXT); 279 1.14 christos if (a2roffsu(n->string, &su, SCALE_VS) != NULL) 280 1.11 christos mt->pardist = term_vspan(p, &su); 281 1.12 christos return 0; 282 1.10 joerg } 283 1.10 joerg 284 1.10 joerg static int 285 1.6 joerg pre_alternate(DECL_ARGS) 286 1.1 joerg { 287 1.6 joerg enum termfont font[2]; 288 1.12 christos struct roff_node *nn; 289 1.15 christos int i; 290 1.1 joerg 291 1.6 joerg switch (n->tok) { 292 1.11 christos case MAN_RB: 293 1.6 joerg font[0] = TERMFONT_NONE; 294 1.6 joerg font[1] = TERMFONT_BOLD; 295 1.6 joerg break; 296 1.11 christos case MAN_RI: 297 1.6 joerg font[0] = TERMFONT_NONE; 298 1.6 joerg font[1] = TERMFONT_UNDER; 299 1.6 joerg break; 300 1.11 christos case MAN_BR: 301 1.6 joerg font[0] = TERMFONT_BOLD; 302 1.6 joerg font[1] = TERMFONT_NONE; 303 1.6 joerg break; 304 1.11 christos case MAN_BI: 305 1.6 joerg font[0] = TERMFONT_BOLD; 306 1.6 joerg font[1] = TERMFONT_UNDER; 307 1.6 joerg break; 308 1.11 christos case MAN_IR: 309 1.6 joerg font[0] = TERMFONT_UNDER; 310 1.6 joerg font[1] = TERMFONT_NONE; 311 1.6 joerg break; 312 1.11 christos case MAN_IB: 313 1.6 joerg font[0] = TERMFONT_UNDER; 314 1.6 joerg font[1] = TERMFONT_BOLD; 315 1.6 joerg break; 316 1.6 joerg default: 317 1.6 joerg abort(); 318 1.6 joerg } 319 1.15 christos for (i = 0, nn = n->child; nn != NULL; nn = nn->next, i = 1 - i) { 320 1.6 joerg term_fontrepl(p, font[i]); 321 1.12 christos assert(nn->type == ROFFT_TEXT); 322 1.12 christos term_word(p, nn->string); 323 1.13 christos if (nn->flags & NODE_EOS) 324 1.16 wiz p->flags |= TERMP_SENTENCE; 325 1.15 christos if (nn->next != NULL) 326 1.6 joerg p->flags |= TERMP_NOSPACE; 327 1.1 joerg } 328 1.12 christos return 0; 329 1.1 joerg } 330 1.1 joerg 331 1.1 joerg static int 332 1.6 joerg pre_B(DECL_ARGS) 333 1.1 joerg { 334 1.6 joerg term_fontrepl(p, TERMFONT_BOLD); 335 1.12 christos return 1; 336 1.1 joerg } 337 1.1 joerg 338 1.1 joerg static int 339 1.9 joerg pre_OP(DECL_ARGS) 340 1.9 joerg { 341 1.9 joerg term_word(p, "["); 342 1.15 christos p->flags |= TERMP_KEEP | TERMP_NOSPACE; 343 1.9 joerg 344 1.15 christos if ((n = n->child) != NULL) { 345 1.9 joerg term_fontrepl(p, TERMFONT_BOLD); 346 1.9 joerg term_word(p, n->string); 347 1.9 joerg } 348 1.15 christos if (n != NULL && n->next != NULL) { 349 1.9 joerg term_fontrepl(p, TERMFONT_UNDER); 350 1.9 joerg term_word(p, n->next->string); 351 1.9 joerg } 352 1.9 joerg term_fontrepl(p, TERMFONT_NONE); 353 1.15 christos p->flags &= ~TERMP_KEEP; 354 1.9 joerg p->flags |= TERMP_NOSPACE; 355 1.9 joerg term_word(p, "]"); 356 1.12 christos return 0; 357 1.9 joerg } 358 1.9 joerg 359 1.9 joerg static int 360 1.5 joerg pre_in(DECL_ARGS) 361 1.1 joerg { 362 1.11 christos struct roffsu su; 363 1.11 christos const char *cp; 364 1.5 joerg size_t v; 365 1.11 christos int less; 366 1.5 joerg 367 1.5 joerg term_newln(p); 368 1.5 joerg 369 1.14 christos if (n->child == NULL) { 370 1.14 christos p->tcol->offset = mt->offset; 371 1.12 christos return 0; 372 1.5 joerg } 373 1.5 joerg 374 1.5 joerg cp = n->child->string; 375 1.5 joerg less = 0; 376 1.5 joerg 377 1.15 christos if (*cp == '-') 378 1.5 joerg less = -1; 379 1.15 christos else if (*cp == '+') 380 1.5 joerg less = 1; 381 1.5 joerg else 382 1.5 joerg cp--; 383 1.5 joerg 384 1.14 christos if (a2roffsu(++cp, &su, SCALE_EN) == NULL) 385 1.12 christos return 0; 386 1.1 joerg 387 1.14 christos v = term_hen(p, &su); 388 1.1 joerg 389 1.5 joerg if (less < 0) 390 1.14 christos p->tcol->offset -= p->tcol->offset > v ? v : p->tcol->offset; 391 1.5 joerg else if (less > 0) 392 1.14 christos p->tcol->offset += v; 393 1.11 christos else 394 1.14 christos p->tcol->offset = v; 395 1.14 christos if (p->tcol->offset > SHRT_MAX) 396 1.14 christos p->tcol->offset = term_len(p, p->defindent); 397 1.7 joerg 398 1.12 christos return 0; 399 1.1 joerg } 400 1.1 joerg 401 1.1 joerg static int 402 1.14 christos pre_DT(DECL_ARGS) 403 1.1 joerg { 404 1.14 christos term_tab_set(p, NULL); 405 1.14 christos term_tab_set(p, "T"); 406 1.14 christos term_tab_set(p, ".5i"); 407 1.12 christos return 0; 408 1.1 joerg } 409 1.1 joerg 410 1.1 joerg static int 411 1.1 joerg pre_HP(DECL_ARGS) 412 1.1 joerg { 413 1.11 christos struct roffsu su; 414 1.12 christos const struct roff_node *nn; 415 1.11 christos int len; 416 1.1 joerg 417 1.1 joerg switch (n->type) { 418 1.12 christos case ROFFT_BLOCK: 419 1.10 joerg print_bvspace(p, n, mt->pardist); 420 1.12 christos return 1; 421 1.15 christos case ROFFT_HEAD: 422 1.15 christos return 0; 423 1.12 christos case ROFFT_BODY: 424 1.1 joerg break; 425 1.1 joerg default: 426 1.15 christos abort(); 427 1.15 christos } 428 1.15 christos 429 1.15 christos if (n->child == NULL) 430 1.12 christos return 0; 431 1.1 joerg 432 1.15 christos if ((n->child->flags & NODE_NOFILL) == 0) { 433 1.11 christos p->flags |= TERMP_NOBREAK | TERMP_BRIND; 434 1.10 joerg p->trailspace = 2; 435 1.10 joerg } 436 1.10 joerg 437 1.1 joerg /* Calculate offset. */ 438 1.1 joerg 439 1.11 christos if ((nn = n->parent->head->child) != NULL && 440 1.14 christos a2roffsu(nn->string, &su, SCALE_EN) != NULL) { 441 1.14 christos len = term_hen(p, &su); 442 1.11 christos if (len < 0 && (size_t)(-len) > mt->offset) 443 1.11 christos len = -mt->offset; 444 1.11 christos else if (len > SHRT_MAX) 445 1.11 christos len = term_len(p, p->defindent); 446 1.11 christos mt->lmargin[mt->lmargincur] = len; 447 1.11 christos } else 448 1.11 christos len = mt->lmargin[mt->lmargincur]; 449 1.1 joerg 450 1.14 christos p->tcol->offset = mt->offset; 451 1.14 christos p->tcol->rmargin = mt->offset + len; 452 1.12 christos return 1; 453 1.1 joerg } 454 1.1 joerg 455 1.1 joerg static void 456 1.1 joerg post_HP(DECL_ARGS) 457 1.1 joerg { 458 1.1 joerg switch (n->type) { 459 1.15 christos case ROFFT_BLOCK: 460 1.15 christos case ROFFT_HEAD: 461 1.15 christos break; 462 1.12 christos case ROFFT_BODY: 463 1.10 joerg term_newln(p); 464 1.12 christos 465 1.12 christos /* 466 1.12 christos * Compatibility with a groff bug. 467 1.12 christos * The .HP macro uses the undocumented .tag request 468 1.12 christos * which causes a line break and cancels no-space 469 1.12 christos * mode even if there isn't any output. 470 1.12 christos */ 471 1.12 christos 472 1.12 christos if (n->child == NULL) 473 1.12 christos term_vspace(p); 474 1.12 christos 475 1.11 christos p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); 476 1.10 joerg p->trailspace = 0; 477 1.14 christos p->tcol->offset = mt->offset; 478 1.14 christos p->tcol->rmargin = p->maxrmargin; 479 1.1 joerg break; 480 1.1 joerg default: 481 1.15 christos abort(); 482 1.1 joerg } 483 1.1 joerg } 484 1.1 joerg 485 1.1 joerg static int 486 1.1 joerg pre_PP(DECL_ARGS) 487 1.1 joerg { 488 1.1 joerg switch (n->type) { 489 1.12 christos case ROFFT_BLOCK: 490 1.9 joerg mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); 491 1.10 joerg print_bvspace(p, n, mt->pardist); 492 1.1 joerg break; 493 1.15 christos case ROFFT_HEAD: 494 1.15 christos return 0; 495 1.15 christos case ROFFT_BODY: 496 1.14 christos p->tcol->offset = mt->offset; 497 1.1 joerg break; 498 1.15 christos default: 499 1.15 christos abort(); 500 1.1 joerg } 501 1.15 christos return 1; 502 1.1 joerg } 503 1.1 joerg 504 1.1 joerg static int 505 1.1 joerg pre_IP(DECL_ARGS) 506 1.1 joerg { 507 1.11 christos struct roffsu su; 508 1.12 christos const struct roff_node *nn; 509 1.15 christos int len; 510 1.1 joerg 511 1.1 joerg switch (n->type) { 512 1.15 christos case ROFFT_BLOCK: 513 1.15 christos print_bvspace(p, n, mt->pardist); 514 1.15 christos return 1; 515 1.12 christos case ROFFT_HEAD: 516 1.1 joerg p->flags |= TERMP_NOBREAK; 517 1.10 joerg p->trailspace = 1; 518 1.1 joerg break; 519 1.15 christos case ROFFT_BODY: 520 1.15 christos p->flags |= TERMP_NOSPACE; 521 1.15 christos break; 522 1.1 joerg default: 523 1.15 christos abort(); 524 1.1 joerg } 525 1.1 joerg 526 1.6 joerg /* Calculate the offset from the optional second argument. */ 527 1.11 christos if ((nn = n->parent->head->child) != NULL && 528 1.11 christos (nn = nn->next) != NULL && 529 1.14 christos a2roffsu(nn->string, &su, SCALE_EN) != NULL) { 530 1.14 christos len = term_hen(p, &su); 531 1.11 christos if (len < 0 && (size_t)(-len) > mt->offset) 532 1.11 christos len = -mt->offset; 533 1.11 christos else if (len > SHRT_MAX) 534 1.11 christos len = term_len(p, p->defindent); 535 1.11 christos mt->lmargin[mt->lmargincur] = len; 536 1.11 christos } else 537 1.11 christos len = mt->lmargin[mt->lmargincur]; 538 1.1 joerg 539 1.1 joerg switch (n->type) { 540 1.12 christos case ROFFT_HEAD: 541 1.14 christos p->tcol->offset = mt->offset; 542 1.14 christos p->tcol->rmargin = mt->offset + len; 543 1.15 christos if (n->child != NULL) 544 1.10 joerg print_man_node(p, mt, n->child, meta); 545 1.12 christos return 0; 546 1.12 christos case ROFFT_BODY: 547 1.14 christos p->tcol->offset = mt->offset + len; 548 1.14 christos p->tcol->rmargin = p->maxrmargin; 549 1.1 joerg break; 550 1.1 joerg default: 551 1.15 christos abort(); 552 1.1 joerg } 553 1.12 christos return 1; 554 1.1 joerg } 555 1.1 joerg 556 1.1 joerg static void 557 1.1 joerg post_IP(DECL_ARGS) 558 1.1 joerg { 559 1.1 joerg switch (n->type) { 560 1.15 christos case ROFFT_BLOCK: 561 1.15 christos break; 562 1.12 christos case ROFFT_HEAD: 563 1.1 joerg term_flushln(p); 564 1.1 joerg p->flags &= ~TERMP_NOBREAK; 565 1.10 joerg p->trailspace = 0; 566 1.14 christos p->tcol->rmargin = p->maxrmargin; 567 1.1 joerg break; 568 1.12 christos case ROFFT_BODY: 569 1.6 joerg term_newln(p); 570 1.14 christos p->tcol->offset = mt->offset; 571 1.1 joerg break; 572 1.1 joerg default: 573 1.15 christos abort(); 574 1.1 joerg } 575 1.1 joerg } 576 1.1 joerg 577 1.1 joerg static int 578 1.1 joerg pre_TP(DECL_ARGS) 579 1.1 joerg { 580 1.11 christos struct roffsu su; 581 1.12 christos struct roff_node *nn; 582 1.15 christos int len; 583 1.1 joerg 584 1.1 joerg switch (n->type) { 585 1.15 christos case ROFFT_BLOCK: 586 1.15 christos if (n->tok == MAN_TP) 587 1.15 christos print_bvspace(p, n, mt->pardist); 588 1.15 christos return 1; 589 1.12 christos case ROFFT_HEAD: 590 1.12 christos p->flags |= TERMP_NOBREAK | TERMP_BRTRSP; 591 1.10 joerg p->trailspace = 1; 592 1.1 joerg break; 593 1.12 christos case ROFFT_BODY: 594 1.1 joerg p->flags |= TERMP_NOSPACE; 595 1.1 joerg break; 596 1.1 joerg default: 597 1.15 christos abort(); 598 1.1 joerg } 599 1.1 joerg 600 1.1 joerg /* Calculate offset. */ 601 1.1 joerg 602 1.11 christos if ((nn = n->parent->head->child) != NULL && 603 1.13 christos nn->string != NULL && ! (NODE_LINE & nn->flags) && 604 1.14 christos a2roffsu(nn->string, &su, SCALE_EN) != NULL) { 605 1.14 christos len = term_hen(p, &su); 606 1.11 christos if (len < 0 && (size_t)(-len) > mt->offset) 607 1.11 christos len = -mt->offset; 608 1.11 christos else if (len > SHRT_MAX) 609 1.11 christos len = term_len(p, p->defindent); 610 1.11 christos mt->lmargin[mt->lmargincur] = len; 611 1.11 christos } else 612 1.11 christos len = mt->lmargin[mt->lmargincur]; 613 1.1 joerg 614 1.1 joerg switch (n->type) { 615 1.12 christos case ROFFT_HEAD: 616 1.14 christos p->tcol->offset = mt->offset; 617 1.14 christos p->tcol->rmargin = mt->offset + len; 618 1.1 joerg 619 1.1 joerg /* Don't print same-line elements. */ 620 1.11 christos nn = n->child; 621 1.15 christos while (nn != NULL && (nn->flags & NODE_LINE) == 0) 622 1.11 christos nn = nn->next; 623 1.11 christos 624 1.15 christos while (nn != NULL) { 625 1.11 christos print_man_node(p, mt, nn, meta); 626 1.11 christos nn = nn->next; 627 1.11 christos } 628 1.12 christos return 0; 629 1.12 christos case ROFFT_BODY: 630 1.14 christos p->tcol->offset = mt->offset + len; 631 1.14 christos p->tcol->rmargin = p->maxrmargin; 632 1.10 joerg p->trailspace = 0; 633 1.12 christos p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP); 634 1.1 joerg break; 635 1.1 joerg default: 636 1.15 christos abort(); 637 1.1 joerg } 638 1.12 christos return 1; 639 1.1 joerg } 640 1.1 joerg 641 1.1 joerg static void 642 1.1 joerg post_TP(DECL_ARGS) 643 1.1 joerg { 644 1.1 joerg switch (n->type) { 645 1.15 christos case ROFFT_BLOCK: 646 1.15 christos break; 647 1.12 christos case ROFFT_HEAD: 648 1.1 joerg term_flushln(p); 649 1.1 joerg break; 650 1.12 christos case ROFFT_BODY: 651 1.6 joerg term_newln(p); 652 1.14 christos p->tcol->offset = mt->offset; 653 1.1 joerg break; 654 1.1 joerg default: 655 1.15 christos abort(); 656 1.1 joerg } 657 1.1 joerg } 658 1.1 joerg 659 1.1 joerg static int 660 1.1 joerg pre_SS(DECL_ARGS) 661 1.1 joerg { 662 1.10 joerg int i; 663 1.1 joerg 664 1.1 joerg switch (n->type) { 665 1.12 christos case ROFFT_BLOCK: 666 1.9 joerg mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); 667 1.9 joerg mt->offset = term_len(p, p->defindent); 668 1.11 christos 669 1.11 christos /* 670 1.11 christos * No vertical space before the first subsection 671 1.11 christos * and after an empty subsection. 672 1.11 christos */ 673 1.11 christos 674 1.16 wiz if ((n = roff_node_prev(n)) == NULL || 675 1.16 wiz (n->tok == MAN_SS && roff_node_child(n->body) == NULL)) 676 1.1 joerg break; 677 1.11 christos 678 1.10 joerg for (i = 0; i < mt->pardist; i++) 679 1.10 joerg term_vspace(p); 680 1.1 joerg break; 681 1.12 christos case ROFFT_HEAD: 682 1.2 joerg term_fontrepl(p, TERMFONT_BOLD); 683 1.14 christos p->tcol->offset = term_len(p, 3); 684 1.14 christos p->tcol->rmargin = mt->offset; 685 1.12 christos p->trailspace = mt->offset; 686 1.12 christos p->flags |= TERMP_NOBREAK | TERMP_BRIND; 687 1.1 joerg break; 688 1.12 christos case ROFFT_BODY: 689 1.14 christos p->tcol->offset = mt->offset; 690 1.14 christos p->tcol->rmargin = p->maxrmargin; 691 1.12 christos p->trailspace = 0; 692 1.12 christos p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); 693 1.1 joerg break; 694 1.1 joerg default: 695 1.1 joerg break; 696 1.1 joerg } 697 1.12 christos return 1; 698 1.1 joerg } 699 1.1 joerg 700 1.1 joerg static int 701 1.1 joerg pre_SH(DECL_ARGS) 702 1.1 joerg { 703 1.10 joerg int i; 704 1.1 joerg 705 1.1 joerg switch (n->type) { 706 1.12 christos case ROFFT_BLOCK: 707 1.9 joerg mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); 708 1.9 joerg mt->offset = term_len(p, p->defindent); 709 1.11 christos 710 1.11 christos /* 711 1.11 christos * No vertical space before the first section 712 1.11 christos * and after an empty section. 713 1.11 christos */ 714 1.11 christos 715 1.16 wiz if ((n = roff_node_prev(n)) == NULL || 716 1.16 wiz (n->tok == MAN_SH && roff_node_child(n->body) == NULL)) 717 1.2 joerg break; 718 1.11 christos 719 1.10 joerg for (i = 0; i < mt->pardist; i++) 720 1.10 joerg term_vspace(p); 721 1.1 joerg break; 722 1.12 christos case ROFFT_HEAD: 723 1.2 joerg term_fontrepl(p, TERMFONT_BOLD); 724 1.14 christos p->tcol->offset = 0; 725 1.14 christos p->tcol->rmargin = mt->offset; 726 1.12 christos p->trailspace = mt->offset; 727 1.12 christos p->flags |= TERMP_NOBREAK | TERMP_BRIND; 728 1.1 joerg break; 729 1.12 christos case ROFFT_BODY: 730 1.14 christos p->tcol->offset = mt->offset; 731 1.14 christos p->tcol->rmargin = p->maxrmargin; 732 1.12 christos p->trailspace = 0; 733 1.12 christos p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); 734 1.1 joerg break; 735 1.1 joerg default: 736 1.15 christos abort(); 737 1.1 joerg } 738 1.12 christos return 1; 739 1.1 joerg } 740 1.1 joerg 741 1.1 joerg static void 742 1.1 joerg post_SH(DECL_ARGS) 743 1.1 joerg { 744 1.1 joerg switch (n->type) { 745 1.15 christos case ROFFT_BLOCK: 746 1.15 christos break; 747 1.12 christos case ROFFT_HEAD: 748 1.12 christos case ROFFT_BODY: 749 1.1 joerg term_newln(p); 750 1.1 joerg break; 751 1.1 joerg default: 752 1.15 christos abort(); 753 1.1 joerg } 754 1.1 joerg } 755 1.1 joerg 756 1.1 joerg static int 757 1.1 joerg pre_RS(DECL_ARGS) 758 1.1 joerg { 759 1.11 christos struct roffsu su; 760 1.1 joerg 761 1.1 joerg switch (n->type) { 762 1.12 christos case ROFFT_BLOCK: 763 1.1 joerg term_newln(p); 764 1.12 christos return 1; 765 1.12 christos case ROFFT_HEAD: 766 1.12 christos return 0; 767 1.15 christos case ROFFT_BODY: 768 1.15 christos break; 769 1.1 joerg default: 770 1.15 christos abort(); 771 1.1 joerg } 772 1.1 joerg 773 1.11 christos n = n->parent->head; 774 1.11 christos n->aux = SHRT_MAX + 1; 775 1.12 christos if (n->child == NULL) 776 1.12 christos n->aux = mt->lmargin[mt->lmargincur]; 777 1.14 christos else if (a2roffsu(n->child->string, &su, SCALE_EN) != NULL) 778 1.14 christos n->aux = term_hen(p, &su); 779 1.11 christos if (n->aux < 0 && (size_t)(-n->aux) > mt->offset) 780 1.11 christos n->aux = -mt->offset; 781 1.11 christos else if (n->aux > SHRT_MAX) 782 1.11 christos n->aux = term_len(p, p->defindent); 783 1.8 joerg 784 1.11 christos mt->offset += n->aux; 785 1.14 christos p->tcol->offset = mt->offset; 786 1.14 christos p->tcol->rmargin = p->maxrmargin; 787 1.1 joerg 788 1.8 joerg if (++mt->lmarginsz < MAXMARGINS) 789 1.8 joerg mt->lmargincur = mt->lmarginsz; 790 1.1 joerg 791 1.12 christos mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); 792 1.12 christos return 1; 793 1.1 joerg } 794 1.1 joerg 795 1.1 joerg static void 796 1.1 joerg post_RS(DECL_ARGS) 797 1.1 joerg { 798 1.1 joerg switch (n->type) { 799 1.12 christos case ROFFT_BLOCK: 800 1.12 christos case ROFFT_HEAD: 801 1.8 joerg return; 802 1.15 christos case ROFFT_BODY: 803 1.15 christos break; 804 1.1 joerg default: 805 1.15 christos abort(); 806 1.1 joerg } 807 1.15 christos term_newln(p); 808 1.11 christos mt->offset -= n->parent->head->aux; 809 1.14 christos p->tcol->offset = mt->offset; 810 1.8 joerg if (--mt->lmarginsz < MAXMARGINS) 811 1.8 joerg mt->lmargincur = mt->lmarginsz; 812 1.1 joerg } 813 1.1 joerg 814 1.10 joerg static int 815 1.15 christos pre_SY(DECL_ARGS) 816 1.15 christos { 817 1.15 christos const struct roff_node *nn; 818 1.15 christos int len; 819 1.15 christos 820 1.15 christos switch (n->type) { 821 1.15 christos case ROFFT_BLOCK: 822 1.16 wiz if ((nn = roff_node_prev(n)) == NULL || nn->tok != MAN_SY) 823 1.15 christos print_bvspace(p, n, mt->pardist); 824 1.15 christos return 1; 825 1.15 christos case ROFFT_HEAD: 826 1.15 christos case ROFFT_BODY: 827 1.15 christos break; 828 1.15 christos default: 829 1.15 christos abort(); 830 1.15 christos } 831 1.15 christos 832 1.15 christos nn = n->parent->head->child; 833 1.15 christos len = nn == NULL ? 1 : term_strlen(p, nn->string) + 1; 834 1.15 christos 835 1.15 christos switch (n->type) { 836 1.15 christos case ROFFT_HEAD: 837 1.15 christos p->tcol->offset = mt->offset; 838 1.15 christos p->tcol->rmargin = mt->offset + len; 839 1.15 christos if (n->next->child == NULL || 840 1.15 christos (n->next->child->flags & NODE_NOFILL) == 0) 841 1.15 christos p->flags |= TERMP_NOBREAK; 842 1.15 christos term_fontrepl(p, TERMFONT_BOLD); 843 1.15 christos break; 844 1.15 christos case ROFFT_BODY: 845 1.15 christos mt->lmargin[mt->lmargincur] = len; 846 1.15 christos p->tcol->offset = mt->offset + len; 847 1.15 christos p->tcol->rmargin = p->maxrmargin; 848 1.15 christos p->flags |= TERMP_NOSPACE; 849 1.15 christos break; 850 1.15 christos default: 851 1.15 christos abort(); 852 1.15 christos } 853 1.15 christos return 1; 854 1.15 christos } 855 1.15 christos 856 1.15 christos static void 857 1.15 christos post_SY(DECL_ARGS) 858 1.15 christos { 859 1.15 christos switch (n->type) { 860 1.15 christos case ROFFT_BLOCK: 861 1.15 christos break; 862 1.15 christos case ROFFT_HEAD: 863 1.15 christos term_flushln(p); 864 1.15 christos p->flags &= ~TERMP_NOBREAK; 865 1.15 christos break; 866 1.15 christos case ROFFT_BODY: 867 1.15 christos term_newln(p); 868 1.15 christos p->tcol->offset = mt->offset; 869 1.15 christos break; 870 1.15 christos default: 871 1.15 christos abort(); 872 1.15 christos } 873 1.15 christos } 874 1.15 christos 875 1.15 christos static int 876 1.10 joerg pre_UR(DECL_ARGS) 877 1.10 joerg { 878 1.12 christos return n->type != ROFFT_HEAD; 879 1.10 joerg } 880 1.10 joerg 881 1.10 joerg static void 882 1.10 joerg post_UR(DECL_ARGS) 883 1.10 joerg { 884 1.12 christos if (n->type != ROFFT_BLOCK) 885 1.10 joerg return; 886 1.10 joerg 887 1.10 joerg term_word(p, "<"); 888 1.10 joerg p->flags |= TERMP_NOSPACE; 889 1.10 joerg 890 1.15 christos if (n->child->child != NULL) 891 1.10 joerg print_man_node(p, mt, n->child->child, meta); 892 1.10 joerg 893 1.10 joerg p->flags |= TERMP_NOSPACE; 894 1.10 joerg term_word(p, ">"); 895 1.10 joerg } 896 1.10 joerg 897 1.1 joerg static void 898 1.2 joerg print_man_node(DECL_ARGS) 899 1.1 joerg { 900 1.15 christos const struct man_term_act *act; 901 1.15 christos int c; 902 1.1 joerg 903 1.16 wiz if (n->flags & NODE_ID) 904 1.16 wiz term_tag_write(n, p->line); 905 1.16 wiz 906 1.1 joerg switch (n->type) { 907 1.12 christos case ROFFT_TEXT: 908 1.7 joerg /* 909 1.7 joerg * If we have a blank line, output a vertical space. 910 1.7 joerg * If we have a space as the first character, break 911 1.7 joerg * before printing the line's data. 912 1.7 joerg */ 913 1.14 christos if (*n->string == '\0') { 914 1.14 christos if (p->flags & TERMP_NONEWLINE) 915 1.14 christos term_newln(p); 916 1.14 christos else 917 1.14 christos term_vspace(p); 918 1.7 joerg return; 919 1.14 christos } else if (*n->string == ' ' && n->flags & NODE_LINE && 920 1.14 christos (p->flags & TERMP_NONEWLINE) == 0) 921 1.7 joerg term_newln(p); 922 1.15 christos else if (n->flags & NODE_DELIMC) 923 1.15 christos p->flags |= TERMP_NOSPACE; 924 1.2 joerg 925 1.1 joerg term_word(p, n->string); 926 1.10 joerg goto out; 927 1.14 christos case ROFFT_COMMENT: 928 1.14 christos return; 929 1.12 christos case ROFFT_EQN: 930 1.13 christos if ( ! (n->flags & NODE_LINE)) 931 1.11 christos p->flags |= TERMP_NOSPACE; 932 1.8 joerg term_eqn(p, n->eqn); 933 1.13 christos if (n->next != NULL && ! (n->next->flags & NODE_LINE)) 934 1.11 christos p->flags |= TERMP_NOSPACE; 935 1.7 joerg return; 936 1.12 christos case ROFFT_TBL: 937 1.11 christos if (p->tbl.cols == NULL) 938 1.11 christos term_vspace(p); 939 1.6 joerg term_tbl(p, n->span); 940 1.7 joerg return; 941 1.1 joerg default: 942 1.1 joerg break; 943 1.1 joerg } 944 1.1 joerg 945 1.14 christos if (n->tok < ROFF_MAX) { 946 1.14 christos roff_term_pre(p, n); 947 1.14 christos return; 948 1.14 christos } 949 1.14 christos 950 1.15 christos act = man_term_act(n->tok); 951 1.15 christos if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM) 952 1.7 joerg term_fontrepl(p, TERMFONT_NONE); 953 1.7 joerg 954 1.7 joerg c = 1; 955 1.15 christos if (act->pre != NULL) 956 1.15 christos c = (*act->pre)(p, mt, n, meta); 957 1.7 joerg 958 1.15 christos if (c && n->child != NULL) 959 1.10 joerg print_man_nodelist(p, mt, n->child, meta); 960 1.1 joerg 961 1.15 christos if (act->post != NULL) 962 1.15 christos (*act->post)(p, mt, n, meta); 963 1.15 christos if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM) 964 1.7 joerg term_fontrepl(p, TERMFONT_NONE); 965 1.2 joerg 966 1.10 joerg out: 967 1.10 joerg /* 968 1.10 joerg * If we're in a literal context, make sure that words 969 1.10 joerg * together on the same line stay together. This is a 970 1.10 joerg * POST-printing call, so we check the NEXT word. Since 971 1.10 joerg * -man doesn't have nested macros, we don't need to be 972 1.10 joerg * more specific than this. 973 1.10 joerg */ 974 1.15 christos if (n->flags & NODE_NOFILL && 975 1.11 christos ! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) && 976 1.13 christos (n->next == NULL || n->next->flags & NODE_LINE)) { 977 1.14 christos p->flags |= TERMP_BRNEVER | TERMP_NOSPACE; 978 1.11 christos if (n->string != NULL && *n->string != '\0') 979 1.10 joerg term_flushln(p); 980 1.10 joerg else 981 1.10 joerg term_newln(p); 982 1.14 christos p->flags &= ~TERMP_BRNEVER; 983 1.14 christos if (p->tcol->rmargin < p->maxrmargin && 984 1.14 christos n->parent->tok == MAN_HP) { 985 1.14 christos p->tcol->offset = p->tcol->rmargin; 986 1.14 christos p->tcol->rmargin = p->maxrmargin; 987 1.14 christos } 988 1.10 joerg } 989 1.15 christos if (n->flags & NODE_EOS) 990 1.2 joerg p->flags |= TERMP_SENTENCE; 991 1.1 joerg } 992 1.1 joerg 993 1.1 joerg static void 994 1.2 joerg print_man_nodelist(DECL_ARGS) 995 1.1 joerg { 996 1.11 christos while (n != NULL) { 997 1.11 christos print_man_node(p, mt, n, meta); 998 1.11 christos n = n->next; 999 1.11 christos } 1000 1.1 joerg } 1001 1.1 joerg 1002 1.1 joerg static void 1003 1.12 christos print_man_foot(struct termp *p, const struct roff_meta *meta) 1004 1.1 joerg { 1005 1.11 christos char *title; 1006 1.11 christos size_t datelen, titlen; 1007 1.2 joerg 1008 1.9 joerg assert(meta->title); 1009 1.9 joerg assert(meta->msec); 1010 1.9 joerg assert(meta->date); 1011 1.1 joerg 1012 1.2 joerg term_fontrepl(p, TERMFONT_NONE); 1013 1.1 joerg 1014 1.11 christos if (meta->hasbody) 1015 1.11 christos term_vspace(p); 1016 1.9 joerg 1017 1.9 joerg /* 1018 1.9 joerg * Temporary, undocumented option to imitate mdoc(7) output. 1019 1.12 christos * In the bottom right corner, use the operating system 1020 1.12 christos * instead of the title. 1021 1.9 joerg */ 1022 1.9 joerg 1023 1.9 joerg if ( ! p->mdocstyle) { 1024 1.11 christos mandoc_asprintf(&title, "%s(%s)", 1025 1.11 christos meta->title, meta->msec); 1026 1.15 christos } else if (meta->os != NULL) { 1027 1.12 christos title = mandoc_strdup(meta->os); 1028 1.9 joerg } else { 1029 1.11 christos title = mandoc_strdup(""); 1030 1.9 joerg } 1031 1.9 joerg datelen = term_strlen(p, meta->date); 1032 1.9 joerg 1033 1.12 christos /* Bottom left corner: operating system. */ 1034 1.1 joerg 1035 1.1 joerg p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; 1036 1.10 joerg p->trailspace = 1; 1037 1.14 christos p->tcol->offset = 0; 1038 1.14 christos p->tcol->rmargin = p->maxrmargin > datelen ? 1039 1.11 christos (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0; 1040 1.6 joerg 1041 1.12 christos if (meta->os) 1042 1.12 christos term_word(p, meta->os); 1043 1.1 joerg term_flushln(p); 1044 1.1 joerg 1045 1.9 joerg /* At the bottom in the middle: manual date. */ 1046 1.9 joerg 1047 1.14 christos p->tcol->offset = p->tcol->rmargin; 1048 1.11 christos titlen = term_strlen(p, title); 1049 1.14 christos p->tcol->rmargin = p->maxrmargin > titlen ? 1050 1.14 christos p->maxrmargin - titlen : 0; 1051 1.9 joerg p->flags |= TERMP_NOSPACE; 1052 1.9 joerg 1053 1.9 joerg term_word(p, meta->date); 1054 1.9 joerg term_flushln(p); 1055 1.9 joerg 1056 1.9 joerg /* Bottom right corner: manual title and section. */ 1057 1.9 joerg 1058 1.9 joerg p->flags &= ~TERMP_NOBREAK; 1059 1.8 joerg p->flags |= TERMP_NOSPACE; 1060 1.10 joerg p->trailspace = 0; 1061 1.14 christos p->tcol->offset = p->tcol->rmargin; 1062 1.14 christos p->tcol->rmargin = p->maxrmargin; 1063 1.1 joerg 1064 1.9 joerg term_word(p, title); 1065 1.1 joerg term_flushln(p); 1066 1.14 christos 1067 1.14 christos /* 1068 1.14 christos * Reset the terminal state for more output after the footer: 1069 1.14 christos * Some output modes, in particular PostScript and PDF, print 1070 1.14 christos * the header and the footer into a buffer such that it can be 1071 1.14 christos * reused for multiple output pages, then go on to format the 1072 1.14 christos * main text. 1073 1.14 christos */ 1074 1.14 christos 1075 1.14 christos p->tcol->offset = 0; 1076 1.14 christos p->flags = 0; 1077 1.14 christos 1078 1.11 christos free(title); 1079 1.1 joerg } 1080 1.1 joerg 1081 1.1 joerg static void 1082 1.12 christos print_man_head(struct termp *p, const struct roff_meta *meta) 1083 1.1 joerg { 1084 1.11 christos const char *volume; 1085 1.11 christos char *title; 1086 1.11 christos size_t vollen, titlen; 1087 1.2 joerg 1088 1.10 joerg assert(meta->title); 1089 1.10 joerg assert(meta->msec); 1090 1.1 joerg 1091 1.11 christos volume = NULL == meta->vol ? "" : meta->vol; 1092 1.11 christos vollen = term_strlen(p, volume); 1093 1.1 joerg 1094 1.9 joerg /* Top left corner: manual title and section. */ 1095 1.9 joerg 1096 1.11 christos mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec); 1097 1.4 joerg titlen = term_strlen(p, title); 1098 1.1 joerg 1099 1.8 joerg p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; 1100 1.10 joerg p->trailspace = 1; 1101 1.14 christos p->tcol->offset = 0; 1102 1.14 christos p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ? 1103 1.11 christos (p->maxrmargin - vollen + term_len(p, 1)) / 2 : 1104 1.11 christos vollen < p->maxrmargin ? p->maxrmargin - vollen : 0; 1105 1.1 joerg 1106 1.1 joerg term_word(p, title); 1107 1.1 joerg term_flushln(p); 1108 1.1 joerg 1109 1.9 joerg /* At the top in the middle: manual volume. */ 1110 1.9 joerg 1111 1.8 joerg p->flags |= TERMP_NOSPACE; 1112 1.14 christos p->tcol->offset = p->tcol->rmargin; 1113 1.14 christos p->tcol->rmargin = p->tcol->offset + vollen + titlen < 1114 1.14 christos p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin; 1115 1.1 joerg 1116 1.11 christos term_word(p, volume); 1117 1.1 joerg term_flushln(p); 1118 1.1 joerg 1119 1.9 joerg /* Top right corner: title and section, again. */ 1120 1.9 joerg 1121 1.1 joerg p->flags &= ~TERMP_NOBREAK; 1122 1.10 joerg p->trailspace = 0; 1123 1.14 christos if (p->tcol->rmargin + titlen <= p->maxrmargin) { 1124 1.8 joerg p->flags |= TERMP_NOSPACE; 1125 1.14 christos p->tcol->offset = p->tcol->rmargin; 1126 1.14 christos p->tcol->rmargin = p->maxrmargin; 1127 1.2 joerg term_word(p, title); 1128 1.2 joerg term_flushln(p); 1129 1.2 joerg } 1130 1.1 joerg 1131 1.8 joerg p->flags &= ~TERMP_NOSPACE; 1132 1.14 christos p->tcol->offset = 0; 1133 1.14 christos p->tcol->rmargin = p->maxrmargin; 1134 1.1 joerg 1135 1.11 christos /* 1136 1.9 joerg * Groff prints three blank lines before the content. 1137 1.9 joerg * Do the same, except in the temporary, undocumented 1138 1.9 joerg * mode imitating mdoc(7) output. 1139 1.2 joerg */ 1140 1.2 joerg 1141 1.2 joerg term_vspace(p); 1142 1.11 christos free(title); 1143 1.1 joerg } 1144