1241675Suqs/* $Id: man_term.c,v 1.127 2012/01/03 15:16:24 kristaps Exp $ */ 2241675Suqs/* 3241675Suqs * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4241675Suqs * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org> 5241675Suqs * 6241675Suqs * Permission to use, copy, modify, and distribute this software for any 7241675Suqs * purpose with or without fee is hereby granted, provided that the above 8241675Suqs * copyright notice and this permission notice appear in all copies. 9241675Suqs * 10241675Suqs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11241675Suqs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12241675Suqs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13241675Suqs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14241675Suqs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15241675Suqs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16241675Suqs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17241675Suqs */ 18241675Suqs#ifdef HAVE_CONFIG_H 19241675Suqs#include "config.h" 20241675Suqs#endif 21241675Suqs 22241675Suqs#include <sys/types.h> 23241675Suqs 24241675Suqs#include <assert.h> 25241675Suqs#include <ctype.h> 26241675Suqs#include <stdio.h> 27241675Suqs#include <stdlib.h> 28241675Suqs#include <string.h> 29241675Suqs 30241675Suqs#include "mandoc.h" 31241675Suqs#include "out.h" 32241675Suqs#include "man.h" 33241675Suqs#include "term.h" 34241675Suqs#include "main.h" 35241675Suqs 36241675Suqs#define MAXMARGINS 64 /* maximum number of indented scopes */ 37241675Suqs 38241675Suqs/* FIXME: have PD set the default vspace width. */ 39241675Suqs 40241675Suqsstruct mtermp { 41241675Suqs int fl; 42241675Suqs#define MANT_LITERAL (1 << 0) 43241675Suqs size_t lmargin[MAXMARGINS]; /* margins (incl. visible page) */ 44241675Suqs int lmargincur; /* index of current margin */ 45241675Suqs int lmarginsz; /* actual number of nested margins */ 46241675Suqs size_t offset; /* default offset to visible page */ 47241675Suqs}; 48241675Suqs 49241675Suqs#define DECL_ARGS struct termp *p, \ 50241675Suqs struct mtermp *mt, \ 51241675Suqs const struct man_node *n, \ 52241675Suqs const struct man_meta *m 53241675Suqs 54241675Suqsstruct termact { 55241675Suqs int (*pre)(DECL_ARGS); 56241675Suqs void (*post)(DECL_ARGS); 57241675Suqs int flags; 58241675Suqs#define MAN_NOTEXT (1 << 0) /* Never has text children. */ 59241675Suqs}; 60241675Suqs 61241675Suqsstatic int a2width(const struct termp *, const char *); 62241675Suqsstatic size_t a2height(const struct termp *, const char *); 63241675Suqs 64241675Suqsstatic void print_man_nodelist(DECL_ARGS); 65241675Suqsstatic void print_man_node(DECL_ARGS); 66241675Suqsstatic void print_man_head(struct termp *, const void *); 67241675Suqsstatic void print_man_foot(struct termp *, const void *); 68241675Suqsstatic void print_bvspace(struct termp *, 69241675Suqs const struct man_node *); 70241675Suqs 71241675Suqsstatic int pre_B(DECL_ARGS); 72241675Suqsstatic int pre_HP(DECL_ARGS); 73241675Suqsstatic int pre_I(DECL_ARGS); 74241675Suqsstatic int pre_IP(DECL_ARGS); 75241675Suqsstatic int pre_OP(DECL_ARGS); 76241675Suqsstatic int pre_PP(DECL_ARGS); 77241675Suqsstatic int pre_RS(DECL_ARGS); 78241675Suqsstatic int pre_SH(DECL_ARGS); 79241675Suqsstatic int pre_SS(DECL_ARGS); 80241675Suqsstatic int pre_TP(DECL_ARGS); 81241675Suqsstatic int pre_alternate(DECL_ARGS); 82241675Suqsstatic int pre_ft(DECL_ARGS); 83241675Suqsstatic int pre_ign(DECL_ARGS); 84241675Suqsstatic int pre_in(DECL_ARGS); 85241675Suqsstatic int pre_literal(DECL_ARGS); 86241675Suqsstatic int pre_sp(DECL_ARGS); 87241675Suqs 88241675Suqsstatic void post_IP(DECL_ARGS); 89241675Suqsstatic void post_HP(DECL_ARGS); 90241675Suqsstatic void post_RS(DECL_ARGS); 91241675Suqsstatic void post_SH(DECL_ARGS); 92241675Suqsstatic void post_SS(DECL_ARGS); 93241675Suqsstatic void post_TP(DECL_ARGS); 94241675Suqs 95241675Suqsstatic const struct termact termacts[MAN_MAX] = { 96241675Suqs { pre_sp, NULL, MAN_NOTEXT }, /* br */ 97241675Suqs { NULL, NULL, 0 }, /* TH */ 98241675Suqs { pre_SH, post_SH, 0 }, /* SH */ 99241675Suqs { pre_SS, post_SS, 0 }, /* SS */ 100241675Suqs { pre_TP, post_TP, 0 }, /* TP */ 101241675Suqs { pre_PP, NULL, 0 }, /* LP */ 102241675Suqs { pre_PP, NULL, 0 }, /* PP */ 103241675Suqs { pre_PP, NULL, 0 }, /* P */ 104241675Suqs { pre_IP, post_IP, 0 }, /* IP */ 105241675Suqs { pre_HP, post_HP, 0 }, /* HP */ 106241675Suqs { NULL, NULL, 0 }, /* SM */ 107241675Suqs { pre_B, NULL, 0 }, /* SB */ 108241675Suqs { pre_alternate, NULL, 0 }, /* BI */ 109241675Suqs { pre_alternate, NULL, 0 }, /* IB */ 110241675Suqs { pre_alternate, NULL, 0 }, /* BR */ 111241675Suqs { pre_alternate, NULL, 0 }, /* RB */ 112241675Suqs { NULL, NULL, 0 }, /* R */ 113241675Suqs { pre_B, NULL, 0 }, /* B */ 114241675Suqs { pre_I, NULL, 0 }, /* I */ 115241675Suqs { pre_alternate, NULL, 0 }, /* IR */ 116241675Suqs { pre_alternate, NULL, 0 }, /* RI */ 117241675Suqs { pre_ign, NULL, MAN_NOTEXT }, /* na */ 118241675Suqs { pre_sp, NULL, MAN_NOTEXT }, /* sp */ 119241675Suqs { pre_literal, NULL, 0 }, /* nf */ 120241675Suqs { pre_literal, NULL, 0 }, /* fi */ 121241675Suqs { NULL, NULL, 0 }, /* RE */ 122241675Suqs { pre_RS, post_RS, 0 }, /* RS */ 123241675Suqs { pre_ign, NULL, 0 }, /* DT */ 124241675Suqs { pre_ign, NULL, 0 }, /* UC */ 125241675Suqs { pre_ign, NULL, 0 }, /* PD */ 126241675Suqs { pre_ign, NULL, 0 }, /* AT */ 127241675Suqs { pre_in, NULL, MAN_NOTEXT }, /* in */ 128241675Suqs { pre_ft, NULL, MAN_NOTEXT }, /* ft */ 129241675Suqs { pre_OP, NULL, 0 }, /* OP */ 130241675Suqs}; 131241675Suqs 132241675Suqs 133241675Suqs 134241675Suqsvoid 135241675Suqsterminal_man(void *arg, const struct man *man) 136241675Suqs{ 137241675Suqs struct termp *p; 138241675Suqs const struct man_node *n; 139241675Suqs const struct man_meta *m; 140241675Suqs struct mtermp mt; 141241675Suqs 142241675Suqs p = (struct termp *)arg; 143241675Suqs 144241675Suqs if (0 == p->defindent) 145241675Suqs p->defindent = 7; 146241675Suqs 147241675Suqs p->overstep = 0; 148241675Suqs p->maxrmargin = p->defrmargin; 149241675Suqs p->tabwidth = term_len(p, 5); 150241675Suqs 151241675Suqs if (NULL == p->symtab) 152241675Suqs p->symtab = mchars_alloc(); 153241675Suqs 154241675Suqs n = man_node(man); 155241675Suqs m = man_meta(man); 156241675Suqs 157241675Suqs term_begin(p, print_man_head, print_man_foot, m); 158241675Suqs p->flags |= TERMP_NOSPACE; 159241675Suqs 160241675Suqs memset(&mt, 0, sizeof(struct mtermp)); 161241675Suqs 162241675Suqs mt.lmargin[mt.lmargincur] = term_len(p, p->defindent); 163241675Suqs mt.offset = term_len(p, p->defindent); 164241675Suqs 165241675Suqs if (n->child) 166241675Suqs print_man_nodelist(p, &mt, n->child, m); 167241675Suqs 168241675Suqs term_end(p); 169241675Suqs} 170241675Suqs 171241675Suqs 172241675Suqsstatic size_t 173241675Suqsa2height(const struct termp *p, const char *cp) 174241675Suqs{ 175241675Suqs struct roffsu su; 176241675Suqs 177241675Suqs if ( ! a2roffsu(cp, &su, SCALE_VS)) 178241675Suqs SCALE_VS_INIT(&su, atoi(cp)); 179241675Suqs 180241675Suqs return(term_vspan(p, &su)); 181241675Suqs} 182241675Suqs 183241675Suqs 184241675Suqsstatic int 185241675Suqsa2width(const struct termp *p, const char *cp) 186241675Suqs{ 187241675Suqs struct roffsu su; 188241675Suqs 189241675Suqs if ( ! a2roffsu(cp, &su, SCALE_BU)) 190241675Suqs return(-1); 191241675Suqs 192241675Suqs return((int)term_hspan(p, &su)); 193241675Suqs} 194241675Suqs 195241675Suqs/* 196241675Suqs * Printing leading vertical space before a block. 197241675Suqs * This is used for the paragraph macros. 198241675Suqs * The rules are pretty simple, since there's very little nesting going 199241675Suqs * on here. Basically, if we're the first within another block (SS/SH), 200241675Suqs * then don't emit vertical space. If we are (RS), then do. If not the 201241675Suqs * first, print it. 202241675Suqs */ 203241675Suqsstatic void 204241675Suqsprint_bvspace(struct termp *p, const struct man_node *n) 205241675Suqs{ 206241675Suqs 207241675Suqs term_newln(p); 208241675Suqs 209241675Suqs if (n->body && n->body->child) 210241675Suqs if (MAN_TBL == n->body->child->type) 211241675Suqs return; 212241675Suqs 213241675Suqs if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok) 214241675Suqs if (NULL == n->prev) 215241675Suqs return; 216241675Suqs 217241675Suqs term_vspace(p); 218241675Suqs} 219241675Suqs 220241675Suqs/* ARGSUSED */ 221241675Suqsstatic int 222241675Suqspre_ign(DECL_ARGS) 223241675Suqs{ 224241675Suqs 225241675Suqs return(0); 226241675Suqs} 227241675Suqs 228241675Suqs 229241675Suqs/* ARGSUSED */ 230241675Suqsstatic int 231241675Suqspre_I(DECL_ARGS) 232241675Suqs{ 233241675Suqs 234241675Suqs term_fontrepl(p, TERMFONT_UNDER); 235241675Suqs return(1); 236241675Suqs} 237241675Suqs 238241675Suqs 239241675Suqs/* ARGSUSED */ 240241675Suqsstatic int 241241675Suqspre_literal(DECL_ARGS) 242241675Suqs{ 243241675Suqs 244241675Suqs term_newln(p); 245241675Suqs 246241675Suqs if (MAN_nf == n->tok) 247241675Suqs mt->fl |= MANT_LITERAL; 248241675Suqs else 249241675Suqs mt->fl &= ~MANT_LITERAL; 250241675Suqs 251241675Suqs /* 252241675Suqs * Unlike .IP and .TP, .HP does not have a HEAD. 253241675Suqs * So in case a second call to term_flushln() is needed, 254241675Suqs * indentation has to be set up explicitly. 255241675Suqs */ 256241675Suqs if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) { 257241675Suqs p->offset = p->rmargin; 258241675Suqs p->rmargin = p->maxrmargin; 259241675Suqs p->flags &= ~(TERMP_NOBREAK | TERMP_TWOSPACE); 260241675Suqs p->flags |= TERMP_NOSPACE; 261241675Suqs } 262241675Suqs 263241675Suqs return(0); 264241675Suqs} 265241675Suqs 266241675Suqs/* ARGSUSED */ 267241675Suqsstatic int 268241675Suqspre_alternate(DECL_ARGS) 269241675Suqs{ 270241675Suqs enum termfont font[2]; 271241675Suqs const struct man_node *nn; 272241675Suqs int savelit, i; 273241675Suqs 274241675Suqs switch (n->tok) { 275241675Suqs case (MAN_RB): 276241675Suqs font[0] = TERMFONT_NONE; 277241675Suqs font[1] = TERMFONT_BOLD; 278241675Suqs break; 279241675Suqs case (MAN_RI): 280241675Suqs font[0] = TERMFONT_NONE; 281241675Suqs font[1] = TERMFONT_UNDER; 282241675Suqs break; 283241675Suqs case (MAN_BR): 284241675Suqs font[0] = TERMFONT_BOLD; 285241675Suqs font[1] = TERMFONT_NONE; 286241675Suqs break; 287241675Suqs case (MAN_BI): 288241675Suqs font[0] = TERMFONT_BOLD; 289241675Suqs font[1] = TERMFONT_UNDER; 290241675Suqs break; 291241675Suqs case (MAN_IR): 292241675Suqs font[0] = TERMFONT_UNDER; 293241675Suqs font[1] = TERMFONT_NONE; 294241675Suqs break; 295241675Suqs case (MAN_IB): 296241675Suqs font[0] = TERMFONT_UNDER; 297241675Suqs font[1] = TERMFONT_BOLD; 298241675Suqs break; 299241675Suqs default: 300241675Suqs abort(); 301241675Suqs } 302241675Suqs 303241675Suqs savelit = MANT_LITERAL & mt->fl; 304241675Suqs mt->fl &= ~MANT_LITERAL; 305241675Suqs 306241675Suqs for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) { 307241675Suqs term_fontrepl(p, font[i]); 308241675Suqs if (savelit && NULL == nn->next) 309241675Suqs mt->fl |= MANT_LITERAL; 310241675Suqs print_man_node(p, mt, nn, m); 311241675Suqs if (nn->next) 312241675Suqs p->flags |= TERMP_NOSPACE; 313241675Suqs } 314241675Suqs 315241675Suqs return(0); 316241675Suqs} 317241675Suqs 318241675Suqs/* ARGSUSED */ 319241675Suqsstatic int 320241675Suqspre_B(DECL_ARGS) 321241675Suqs{ 322241675Suqs 323241675Suqs term_fontrepl(p, TERMFONT_BOLD); 324241675Suqs return(1); 325241675Suqs} 326241675Suqs 327241675Suqs/* ARGSUSED */ 328241675Suqsstatic int 329241675Suqspre_OP(DECL_ARGS) 330241675Suqs{ 331241675Suqs 332241675Suqs term_word(p, "["); 333241675Suqs p->flags |= TERMP_NOSPACE; 334241675Suqs 335241675Suqs if (NULL != (n = n->child)) { 336241675Suqs term_fontrepl(p, TERMFONT_BOLD); 337241675Suqs term_word(p, n->string); 338241675Suqs } 339241675Suqs if (NULL != n && NULL != n->next) { 340241675Suqs term_fontrepl(p, TERMFONT_UNDER); 341241675Suqs term_word(p, n->next->string); 342241675Suqs } 343241675Suqs 344241675Suqs term_fontrepl(p, TERMFONT_NONE); 345241675Suqs p->flags |= TERMP_NOSPACE; 346241675Suqs term_word(p, "]"); 347241675Suqs return(0); 348241675Suqs} 349241675Suqs 350241675Suqs/* ARGSUSED */ 351241675Suqsstatic int 352241675Suqspre_ft(DECL_ARGS) 353241675Suqs{ 354241675Suqs const char *cp; 355241675Suqs 356241675Suqs if (NULL == n->child) { 357241675Suqs term_fontlast(p); 358241675Suqs return(0); 359241675Suqs } 360241675Suqs 361241675Suqs cp = n->child->string; 362241675Suqs switch (*cp) { 363241675Suqs case ('4'): 364241675Suqs /* FALLTHROUGH */ 365241675Suqs case ('3'): 366241675Suqs /* FALLTHROUGH */ 367241675Suqs case ('B'): 368241675Suqs term_fontrepl(p, TERMFONT_BOLD); 369241675Suqs break; 370241675Suqs case ('2'): 371241675Suqs /* FALLTHROUGH */ 372241675Suqs case ('I'): 373241675Suqs term_fontrepl(p, TERMFONT_UNDER); 374241675Suqs break; 375241675Suqs case ('P'): 376241675Suqs term_fontlast(p); 377241675Suqs break; 378241675Suqs case ('1'): 379241675Suqs /* FALLTHROUGH */ 380241675Suqs case ('C'): 381241675Suqs /* FALLTHROUGH */ 382241675Suqs case ('R'): 383241675Suqs term_fontrepl(p, TERMFONT_NONE); 384241675Suqs break; 385241675Suqs default: 386241675Suqs break; 387241675Suqs } 388241675Suqs return(0); 389241675Suqs} 390241675Suqs 391241675Suqs/* ARGSUSED */ 392241675Suqsstatic int 393241675Suqspre_in(DECL_ARGS) 394241675Suqs{ 395241675Suqs int len, less; 396241675Suqs size_t v; 397241675Suqs const char *cp; 398241675Suqs 399241675Suqs term_newln(p); 400241675Suqs 401241675Suqs if (NULL == n->child) { 402241675Suqs p->offset = mt->offset; 403241675Suqs return(0); 404241675Suqs } 405241675Suqs 406241675Suqs cp = n->child->string; 407241675Suqs less = 0; 408241675Suqs 409241675Suqs if ('-' == *cp) 410241675Suqs less = -1; 411241675Suqs else if ('+' == *cp) 412241675Suqs less = 1; 413241675Suqs else 414241675Suqs cp--; 415241675Suqs 416241675Suqs if ((len = a2width(p, ++cp)) < 0) 417241675Suqs return(0); 418241675Suqs 419241675Suqs v = (size_t)len; 420241675Suqs 421241675Suqs if (less < 0) 422241675Suqs p->offset -= p->offset > v ? v : p->offset; 423241675Suqs else if (less > 0) 424241675Suqs p->offset += v; 425241675Suqs else 426241675Suqs p->offset = v; 427241675Suqs 428241675Suqs /* Don't let this creep beyond the right margin. */ 429241675Suqs 430241675Suqs if (p->offset > p->rmargin) 431241675Suqs p->offset = p->rmargin; 432241675Suqs 433241675Suqs return(0); 434241675Suqs} 435241675Suqs 436241675Suqs 437241675Suqs/* ARGSUSED */ 438241675Suqsstatic int 439241675Suqspre_sp(DECL_ARGS) 440241675Suqs{ 441241675Suqs size_t i, len; 442241675Suqs 443241675Suqs if ((NULL == n->prev && n->parent)) { 444241675Suqs if (MAN_SS == n->parent->tok) 445241675Suqs return(0); 446241675Suqs if (MAN_SH == n->parent->tok) 447241675Suqs return(0); 448241675Suqs } 449241675Suqs 450241675Suqs switch (n->tok) { 451241675Suqs case (MAN_br): 452241675Suqs len = 0; 453241675Suqs break; 454241675Suqs default: 455241675Suqs len = n->child ? a2height(p, n->child->string) : 1; 456241675Suqs break; 457241675Suqs } 458241675Suqs 459241675Suqs if (0 == len) 460241675Suqs term_newln(p); 461241675Suqs for (i = 0; i < len; i++) 462241675Suqs term_vspace(p); 463241675Suqs 464241675Suqs return(0); 465241675Suqs} 466241675Suqs 467241675Suqs 468241675Suqs/* ARGSUSED */ 469241675Suqsstatic int 470241675Suqspre_HP(DECL_ARGS) 471241675Suqs{ 472241675Suqs size_t len, one; 473241675Suqs int ival; 474241675Suqs const struct man_node *nn; 475241675Suqs 476241675Suqs switch (n->type) { 477241675Suqs case (MAN_BLOCK): 478241675Suqs print_bvspace(p, n); 479241675Suqs return(1); 480241675Suqs case (MAN_BODY): 481241675Suqs p->flags |= TERMP_NOBREAK; 482241675Suqs p->flags |= TERMP_TWOSPACE; 483241675Suqs break; 484241675Suqs default: 485241675Suqs return(0); 486241675Suqs } 487241675Suqs 488241675Suqs len = mt->lmargin[mt->lmargincur]; 489241675Suqs ival = -1; 490241675Suqs 491241675Suqs /* Calculate offset. */ 492241675Suqs 493241675Suqs if (NULL != (nn = n->parent->head->child)) 494241675Suqs if ((ival = a2width(p, nn->string)) >= 0) 495241675Suqs len = (size_t)ival; 496241675Suqs 497241675Suqs one = term_len(p, 1); 498241675Suqs if (len < one) 499241675Suqs len = one; 500241675Suqs 501241675Suqs p->offset = mt->offset; 502241675Suqs p->rmargin = mt->offset + len; 503241675Suqs 504241675Suqs if (ival >= 0) 505241675Suqs mt->lmargin[mt->lmargincur] = (size_t)ival; 506241675Suqs 507241675Suqs return(1); 508241675Suqs} 509241675Suqs 510241675Suqs 511241675Suqs/* ARGSUSED */ 512241675Suqsstatic void 513241675Suqspost_HP(DECL_ARGS) 514241675Suqs{ 515241675Suqs 516241675Suqs switch (n->type) { 517241675Suqs case (MAN_BLOCK): 518241675Suqs term_flushln(p); 519241675Suqs break; 520241675Suqs case (MAN_BODY): 521241675Suqs term_flushln(p); 522241675Suqs p->flags &= ~TERMP_NOBREAK; 523241675Suqs p->flags &= ~TERMP_TWOSPACE; 524241675Suqs p->offset = mt->offset; 525241675Suqs p->rmargin = p->maxrmargin; 526241675Suqs break; 527241675Suqs default: 528241675Suqs break; 529241675Suqs } 530241675Suqs} 531241675Suqs 532241675Suqs 533241675Suqs/* ARGSUSED */ 534241675Suqsstatic int 535241675Suqspre_PP(DECL_ARGS) 536241675Suqs{ 537241675Suqs 538241675Suqs switch (n->type) { 539241675Suqs case (MAN_BLOCK): 540241675Suqs mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); 541241675Suqs print_bvspace(p, n); 542241675Suqs break; 543241675Suqs default: 544241675Suqs p->offset = mt->offset; 545241675Suqs break; 546241675Suqs } 547241675Suqs 548241675Suqs return(MAN_HEAD != n->type); 549241675Suqs} 550241675Suqs 551241675Suqs 552241675Suqs/* ARGSUSED */ 553241675Suqsstatic int 554241675Suqspre_IP(DECL_ARGS) 555241675Suqs{ 556241675Suqs const struct man_node *nn; 557241675Suqs size_t len; 558241675Suqs int savelit, ival; 559241675Suqs 560241675Suqs switch (n->type) { 561241675Suqs case (MAN_BODY): 562241675Suqs p->flags |= TERMP_NOSPACE; 563241675Suqs break; 564241675Suqs case (MAN_HEAD): 565241675Suqs p->flags |= TERMP_NOBREAK; 566241675Suqs break; 567241675Suqs case (MAN_BLOCK): 568241675Suqs print_bvspace(p, n); 569241675Suqs /* FALLTHROUGH */ 570241675Suqs default: 571241675Suqs return(1); 572241675Suqs } 573241675Suqs 574241675Suqs len = mt->lmargin[mt->lmargincur]; 575241675Suqs ival = -1; 576241675Suqs 577241675Suqs /* Calculate the offset from the optional second argument. */ 578241675Suqs if (NULL != (nn = n->parent->head->child)) 579241675Suqs if (NULL != (nn = nn->next)) 580241675Suqs if ((ival = a2width(p, nn->string)) >= 0) 581241675Suqs len = (size_t)ival; 582241675Suqs 583241675Suqs switch (n->type) { 584241675Suqs case (MAN_HEAD): 585241675Suqs /* Handle zero-width lengths. */ 586241675Suqs if (0 == len) 587241675Suqs len = term_len(p, 1); 588241675Suqs 589241675Suqs p->offset = mt->offset; 590241675Suqs p->rmargin = mt->offset + len; 591241675Suqs if (ival < 0) 592241675Suqs break; 593241675Suqs 594241675Suqs /* Set the saved left-margin. */ 595241675Suqs mt->lmargin[mt->lmargincur] = (size_t)ival; 596241675Suqs 597241675Suqs savelit = MANT_LITERAL & mt->fl; 598241675Suqs mt->fl &= ~MANT_LITERAL; 599241675Suqs 600241675Suqs if (n->child) 601241675Suqs print_man_node(p, mt, n->child, m); 602241675Suqs 603241675Suqs if (savelit) 604241675Suqs mt->fl |= MANT_LITERAL; 605241675Suqs 606241675Suqs return(0); 607241675Suqs case (MAN_BODY): 608241675Suqs p->offset = mt->offset + len; 609241675Suqs p->rmargin = p->maxrmargin; 610241675Suqs break; 611241675Suqs default: 612241675Suqs break; 613241675Suqs } 614241675Suqs 615241675Suqs return(1); 616241675Suqs} 617241675Suqs 618241675Suqs 619241675Suqs/* ARGSUSED */ 620241675Suqsstatic void 621241675Suqspost_IP(DECL_ARGS) 622241675Suqs{ 623241675Suqs 624241675Suqs switch (n->type) { 625241675Suqs case (MAN_HEAD): 626241675Suqs term_flushln(p); 627241675Suqs p->flags &= ~TERMP_NOBREAK; 628241675Suqs p->rmargin = p->maxrmargin; 629241675Suqs break; 630241675Suqs case (MAN_BODY): 631241675Suqs term_newln(p); 632241675Suqs break; 633241675Suqs default: 634241675Suqs break; 635241675Suqs } 636241675Suqs} 637241675Suqs 638241675Suqs 639241675Suqs/* ARGSUSED */ 640241675Suqsstatic int 641241675Suqspre_TP(DECL_ARGS) 642241675Suqs{ 643241675Suqs const struct man_node *nn; 644241675Suqs size_t len; 645241675Suqs int savelit, ival; 646241675Suqs 647241675Suqs switch (n->type) { 648241675Suqs case (MAN_HEAD): 649241675Suqs p->flags |= TERMP_NOBREAK; 650241675Suqs break; 651241675Suqs case (MAN_BODY): 652241675Suqs p->flags |= TERMP_NOSPACE; 653241675Suqs break; 654241675Suqs case (MAN_BLOCK): 655241675Suqs print_bvspace(p, n); 656241675Suqs /* FALLTHROUGH */ 657241675Suqs default: 658241675Suqs return(1); 659241675Suqs } 660241675Suqs 661241675Suqs len = (size_t)mt->lmargin[mt->lmargincur]; 662241675Suqs ival = -1; 663241675Suqs 664241675Suqs /* Calculate offset. */ 665241675Suqs 666241675Suqs if (NULL != (nn = n->parent->head->child)) 667241675Suqs if (nn->string && nn->parent->line == nn->line) 668241675Suqs if ((ival = a2width(p, nn->string)) >= 0) 669241675Suqs len = (size_t)ival; 670241675Suqs 671241675Suqs switch (n->type) { 672241675Suqs case (MAN_HEAD): 673241675Suqs /* Handle zero-length properly. */ 674241675Suqs if (0 == len) 675241675Suqs len = term_len(p, 1); 676241675Suqs 677241675Suqs p->offset = mt->offset; 678241675Suqs p->rmargin = mt->offset + len; 679241675Suqs 680241675Suqs savelit = MANT_LITERAL & mt->fl; 681241675Suqs mt->fl &= ~MANT_LITERAL; 682241675Suqs 683241675Suqs /* Don't print same-line elements. */ 684241675Suqs for (nn = n->child; nn; nn = nn->next) 685241675Suqs if (nn->line > n->line) 686241675Suqs print_man_node(p, mt, nn, m); 687241675Suqs 688241675Suqs if (savelit) 689241675Suqs mt->fl |= MANT_LITERAL; 690241675Suqs if (ival >= 0) 691241675Suqs mt->lmargin[mt->lmargincur] = (size_t)ival; 692241675Suqs 693241675Suqs return(0); 694241675Suqs case (MAN_BODY): 695241675Suqs p->offset = mt->offset + len; 696241675Suqs p->rmargin = p->maxrmargin; 697241675Suqs break; 698241675Suqs default: 699241675Suqs break; 700241675Suqs } 701241675Suqs 702241675Suqs return(1); 703241675Suqs} 704241675Suqs 705241675Suqs 706241675Suqs/* ARGSUSED */ 707241675Suqsstatic void 708241675Suqspost_TP(DECL_ARGS) 709241675Suqs{ 710241675Suqs 711241675Suqs switch (n->type) { 712241675Suqs case (MAN_HEAD): 713241675Suqs term_flushln(p); 714241675Suqs p->flags &= ~TERMP_NOBREAK; 715241675Suqs p->flags &= ~TERMP_TWOSPACE; 716241675Suqs p->rmargin = p->maxrmargin; 717241675Suqs break; 718241675Suqs case (MAN_BODY): 719241675Suqs term_newln(p); 720241675Suqs break; 721241675Suqs default: 722241675Suqs break; 723241675Suqs } 724241675Suqs} 725241675Suqs 726241675Suqs 727241675Suqs/* ARGSUSED */ 728241675Suqsstatic int 729241675Suqspre_SS(DECL_ARGS) 730241675Suqs{ 731241675Suqs 732241675Suqs switch (n->type) { 733241675Suqs case (MAN_BLOCK): 734241675Suqs mt->fl &= ~MANT_LITERAL; 735241675Suqs mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); 736241675Suqs mt->offset = term_len(p, p->defindent); 737241675Suqs /* If following a prior empty `SS', no vspace. */ 738241675Suqs if (n->prev && MAN_SS == n->prev->tok) 739241675Suqs if (NULL == n->prev->body->child) 740241675Suqs break; 741241675Suqs if (NULL == n->prev) 742241675Suqs break; 743241675Suqs term_vspace(p); 744241675Suqs break; 745241675Suqs case (MAN_HEAD): 746241675Suqs term_fontrepl(p, TERMFONT_BOLD); 747241675Suqs p->offset = term_len(p, p->defindent/2); 748241675Suqs break; 749241675Suqs case (MAN_BODY): 750241675Suqs p->offset = mt->offset; 751241675Suqs break; 752241675Suqs default: 753241675Suqs break; 754241675Suqs } 755241675Suqs 756241675Suqs return(1); 757241675Suqs} 758241675Suqs 759241675Suqs 760241675Suqs/* ARGSUSED */ 761241675Suqsstatic void 762241675Suqspost_SS(DECL_ARGS) 763241675Suqs{ 764241675Suqs 765241675Suqs switch (n->type) { 766241675Suqs case (MAN_HEAD): 767241675Suqs term_newln(p); 768241675Suqs break; 769241675Suqs case (MAN_BODY): 770241675Suqs term_newln(p); 771241675Suqs break; 772241675Suqs default: 773241675Suqs break; 774241675Suqs } 775241675Suqs} 776241675Suqs 777241675Suqs 778241675Suqs/* ARGSUSED */ 779241675Suqsstatic int 780241675Suqspre_SH(DECL_ARGS) 781241675Suqs{ 782241675Suqs 783241675Suqs switch (n->type) { 784241675Suqs case (MAN_BLOCK): 785241675Suqs mt->fl &= ~MANT_LITERAL; 786241675Suqs mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); 787241675Suqs mt->offset = term_len(p, p->defindent); 788241675Suqs /* If following a prior empty `SH', no vspace. */ 789241675Suqs if (n->prev && MAN_SH == n->prev->tok) 790241675Suqs if (NULL == n->prev->body->child) 791241675Suqs break; 792241675Suqs /* If the first macro, no vspae. */ 793241675Suqs if (NULL == n->prev) 794241675Suqs break; 795241675Suqs term_vspace(p); 796241675Suqs break; 797241675Suqs case (MAN_HEAD): 798241675Suqs term_fontrepl(p, TERMFONT_BOLD); 799241675Suqs p->offset = 0; 800241675Suqs break; 801241675Suqs case (MAN_BODY): 802241675Suqs p->offset = mt->offset; 803241675Suqs break; 804241675Suqs default: 805241675Suqs break; 806241675Suqs } 807241675Suqs 808241675Suqs return(1); 809241675Suqs} 810241675Suqs 811241675Suqs 812241675Suqs/* ARGSUSED */ 813241675Suqsstatic void 814241675Suqspost_SH(DECL_ARGS) 815241675Suqs{ 816241675Suqs 817241675Suqs switch (n->type) { 818241675Suqs case (MAN_HEAD): 819241675Suqs term_newln(p); 820241675Suqs break; 821241675Suqs case (MAN_BODY): 822241675Suqs term_newln(p); 823241675Suqs break; 824241675Suqs default: 825241675Suqs break; 826241675Suqs } 827241675Suqs} 828241675Suqs 829241675Suqs/* ARGSUSED */ 830241675Suqsstatic int 831241675Suqspre_RS(DECL_ARGS) 832241675Suqs{ 833241675Suqs int ival; 834241675Suqs size_t sz; 835241675Suqs 836241675Suqs switch (n->type) { 837241675Suqs case (MAN_BLOCK): 838241675Suqs term_newln(p); 839241675Suqs return(1); 840241675Suqs case (MAN_HEAD): 841241675Suqs return(0); 842241675Suqs default: 843241675Suqs break; 844241675Suqs } 845241675Suqs 846241675Suqs sz = term_len(p, p->defindent); 847241675Suqs 848241675Suqs if (NULL != (n = n->parent->head->child)) 849241675Suqs if ((ival = a2width(p, n->string)) >= 0) 850241675Suqs sz = (size_t)ival; 851241675Suqs 852241675Suqs mt->offset += sz; 853241675Suqs p->rmargin = p->maxrmargin; 854241675Suqs p->offset = mt->offset < p->rmargin ? mt->offset : p->rmargin; 855241675Suqs 856241675Suqs if (++mt->lmarginsz < MAXMARGINS) 857241675Suqs mt->lmargincur = mt->lmarginsz; 858241675Suqs 859241675Suqs mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1]; 860241675Suqs return(1); 861241675Suqs} 862241675Suqs 863241675Suqs/* ARGSUSED */ 864241675Suqsstatic void 865241675Suqspost_RS(DECL_ARGS) 866241675Suqs{ 867241675Suqs int ival; 868241675Suqs size_t sz; 869241675Suqs 870241675Suqs switch (n->type) { 871241675Suqs case (MAN_BLOCK): 872241675Suqs return; 873241675Suqs case (MAN_HEAD): 874241675Suqs return; 875241675Suqs default: 876241675Suqs term_newln(p); 877241675Suqs break; 878241675Suqs } 879241675Suqs 880241675Suqs sz = term_len(p, p->defindent); 881241675Suqs 882241675Suqs if (NULL != (n = n->parent->head->child)) 883241675Suqs if ((ival = a2width(p, n->string)) >= 0) 884241675Suqs sz = (size_t)ival; 885241675Suqs 886241675Suqs mt->offset = mt->offset < sz ? 0 : mt->offset - sz; 887241675Suqs p->offset = mt->offset; 888241675Suqs 889241675Suqs if (--mt->lmarginsz < MAXMARGINS) 890241675Suqs mt->lmargincur = mt->lmarginsz; 891241675Suqs} 892241675Suqs 893241675Suqsstatic void 894241675Suqsprint_man_node(DECL_ARGS) 895241675Suqs{ 896241675Suqs size_t rm, rmax; 897241675Suqs int c; 898241675Suqs 899241675Suqs switch (n->type) { 900241675Suqs case(MAN_TEXT): 901241675Suqs /* 902241675Suqs * If we have a blank line, output a vertical space. 903241675Suqs * If we have a space as the first character, break 904241675Suqs * before printing the line's data. 905241675Suqs */ 906241675Suqs if ('\0' == *n->string) { 907241675Suqs term_vspace(p); 908241675Suqs return; 909241675Suqs } else if (' ' == *n->string && MAN_LINE & n->flags) 910241675Suqs term_newln(p); 911241675Suqs 912241675Suqs term_word(p, n->string); 913241675Suqs 914241675Suqs /* 915241675Suqs * If we're in a literal context, make sure that words 916241675Suqs * togehter on the same line stay together. This is a 917241675Suqs * POST-printing call, so we check the NEXT word. Since 918241675Suqs * -man doesn't have nested macros, we don't need to be 919241675Suqs * more specific than this. 920241675Suqs */ 921241675Suqs if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) && 922241675Suqs (NULL == n->next || 923241675Suqs n->next->line > n->line)) { 924241675Suqs rm = p->rmargin; 925241675Suqs rmax = p->maxrmargin; 926241675Suqs p->rmargin = p->maxrmargin = TERM_MAXMARGIN; 927241675Suqs p->flags |= TERMP_NOSPACE; 928241675Suqs term_flushln(p); 929241675Suqs p->rmargin = rm; 930241675Suqs p->maxrmargin = rmax; 931241675Suqs } 932241675Suqs 933241675Suqs if (MAN_EOS & n->flags) 934241675Suqs p->flags |= TERMP_SENTENCE; 935241675Suqs return; 936241675Suqs case (MAN_EQN): 937241675Suqs term_eqn(p, n->eqn); 938241675Suqs return; 939241675Suqs case (MAN_TBL): 940241675Suqs /* 941241675Suqs * Tables are preceded by a newline. Then process a 942241675Suqs * table line, which will cause line termination, 943241675Suqs */ 944241675Suqs if (TBL_SPAN_FIRST & n->span->flags) 945241675Suqs term_newln(p); 946241675Suqs term_tbl(p, n->span); 947241675Suqs return; 948241675Suqs default: 949241675Suqs break; 950241675Suqs } 951241675Suqs 952241675Suqs if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) 953241675Suqs term_fontrepl(p, TERMFONT_NONE); 954241675Suqs 955241675Suqs c = 1; 956241675Suqs if (termacts[n->tok].pre) 957241675Suqs c = (*termacts[n->tok].pre)(p, mt, n, m); 958241675Suqs 959241675Suqs if (c && n->child) 960241675Suqs print_man_nodelist(p, mt, n->child, m); 961241675Suqs 962241675Suqs if (termacts[n->tok].post) 963241675Suqs (*termacts[n->tok].post)(p, mt, n, m); 964241675Suqs if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) 965241675Suqs term_fontrepl(p, TERMFONT_NONE); 966241675Suqs 967241675Suqs if (MAN_EOS & n->flags) 968241675Suqs p->flags |= TERMP_SENTENCE; 969241675Suqs} 970241675Suqs 971241675Suqs 972241675Suqsstatic void 973241675Suqsprint_man_nodelist(DECL_ARGS) 974241675Suqs{ 975241675Suqs 976241675Suqs print_man_node(p, mt, n, m); 977241675Suqs if ( ! n->next) 978241675Suqs return; 979241675Suqs print_man_nodelist(p, mt, n->next, m); 980241675Suqs} 981241675Suqs 982241675Suqs 983241675Suqsstatic void 984241675Suqsprint_man_foot(struct termp *p, const void *arg) 985241675Suqs{ 986241675Suqs char title[BUFSIZ]; 987241675Suqs size_t datelen; 988241675Suqs const struct man_meta *meta; 989241675Suqs 990241675Suqs meta = (const struct man_meta *)arg; 991241675Suqs assert(meta->title); 992241675Suqs assert(meta->msec); 993241675Suqs assert(meta->date); 994241675Suqs 995241675Suqs term_fontrepl(p, TERMFONT_NONE); 996241675Suqs 997241675Suqs term_vspace(p); 998241675Suqs 999241675Suqs /* 1000241675Suqs * Temporary, undocumented option to imitate mdoc(7) output. 1001241675Suqs * In the bottom right corner, use the source instead of 1002241675Suqs * the title. 1003241675Suqs */ 1004241675Suqs 1005241675Suqs if ( ! p->mdocstyle) { 1006241675Suqs term_vspace(p); 1007241675Suqs term_vspace(p); 1008241675Suqs snprintf(title, BUFSIZ, "%s(%s)", meta->title, meta->msec); 1009241675Suqs } else if (meta->source) { 1010241675Suqs strlcpy(title, meta->source, BUFSIZ); 1011241675Suqs } else { 1012241675Suqs title[0] = '\0'; 1013241675Suqs } 1014241675Suqs datelen = term_strlen(p, meta->date); 1015241675Suqs 1016241675Suqs /* Bottom left corner: manual source. */ 1017241675Suqs 1018241675Suqs p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; 1019241675Suqs p->offset = 0; 1020241675Suqs p->rmargin = (p->maxrmargin - datelen + term_len(p, 1)) / 2; 1021241675Suqs 1022241675Suqs if (meta->source) 1023241675Suqs term_word(p, meta->source); 1024241675Suqs term_flushln(p); 1025241675Suqs 1026241675Suqs /* At the bottom in the middle: manual date. */ 1027241675Suqs 1028241675Suqs p->flags |= TERMP_NOSPACE; 1029241675Suqs p->offset = p->rmargin; 1030241675Suqs p->rmargin = p->maxrmargin - term_strlen(p, title); 1031241675Suqs if (p->offset + datelen >= p->rmargin) 1032241675Suqs p->rmargin = p->offset + datelen; 1033241675Suqs 1034241675Suqs term_word(p, meta->date); 1035241675Suqs term_flushln(p); 1036241675Suqs 1037241675Suqs /* Bottom right corner: manual title and section. */ 1038241675Suqs 1039241675Suqs p->flags &= ~TERMP_NOBREAK; 1040241675Suqs p->flags |= TERMP_NOSPACE; 1041241675Suqs p->offset = p->rmargin; 1042241675Suqs p->rmargin = p->maxrmargin; 1043241675Suqs 1044241675Suqs term_word(p, title); 1045241675Suqs term_flushln(p); 1046241675Suqs} 1047241675Suqs 1048241675Suqs 1049241675Suqsstatic void 1050241675Suqsprint_man_head(struct termp *p, const void *arg) 1051241675Suqs{ 1052241675Suqs char buf[BUFSIZ], title[BUFSIZ]; 1053241675Suqs size_t buflen, titlen; 1054241675Suqs const struct man_meta *m; 1055241675Suqs 1056241675Suqs m = (const struct man_meta *)arg; 1057241675Suqs assert(m->title); 1058241675Suqs assert(m->msec); 1059241675Suqs 1060241675Suqs if (m->vol) 1061241675Suqs strlcpy(buf, m->vol, BUFSIZ); 1062241675Suqs else 1063241675Suqs buf[0] = '\0'; 1064241675Suqs buflen = term_strlen(p, buf); 1065241675Suqs 1066241675Suqs /* Top left corner: manual title and section. */ 1067241675Suqs 1068241675Suqs snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec); 1069241675Suqs titlen = term_strlen(p, title); 1070241675Suqs 1071241675Suqs p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; 1072241675Suqs p->offset = 0; 1073241675Suqs p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ? 1074241675Suqs (p->maxrmargin - 1075241675Suqs term_strlen(p, buf) + term_len(p, 1)) / 2 : 1076241675Suqs p->maxrmargin - buflen; 1077241675Suqs 1078241675Suqs term_word(p, title); 1079241675Suqs term_flushln(p); 1080241675Suqs 1081241675Suqs /* At the top in the middle: manual volume. */ 1082241675Suqs 1083241675Suqs p->flags |= TERMP_NOSPACE; 1084241675Suqs p->offset = p->rmargin; 1085241675Suqs p->rmargin = p->offset + buflen + titlen < p->maxrmargin ? 1086241675Suqs p->maxrmargin - titlen : p->maxrmargin; 1087241675Suqs 1088241675Suqs term_word(p, buf); 1089241675Suqs term_flushln(p); 1090241675Suqs 1091241675Suqs /* Top right corner: title and section, again. */ 1092241675Suqs 1093241675Suqs p->flags &= ~TERMP_NOBREAK; 1094241675Suqs if (p->rmargin + titlen <= p->maxrmargin) { 1095241675Suqs p->flags |= TERMP_NOSPACE; 1096241675Suqs p->offset = p->rmargin; 1097241675Suqs p->rmargin = p->maxrmargin; 1098241675Suqs term_word(p, title); 1099241675Suqs term_flushln(p); 1100241675Suqs } 1101241675Suqs 1102241675Suqs p->flags &= ~TERMP_NOSPACE; 1103241675Suqs p->offset = 0; 1104241675Suqs p->rmargin = p->maxrmargin; 1105241675Suqs 1106241675Suqs /* 1107241675Suqs * Groff prints three blank lines before the content. 1108241675Suqs * Do the same, except in the temporary, undocumented 1109241675Suqs * mode imitating mdoc(7) output. 1110241675Suqs */ 1111241675Suqs 1112241675Suqs term_vspace(p); 1113241675Suqs if ( ! p->mdocstyle) { 1114241675Suqs term_vspace(p); 1115241675Suqs term_vspace(p); 1116241675Suqs } 1117241675Suqs} 1118