1*c1c95addSBrooks Davis /* $Id: man.c,v 1.189 2022/08/16 23:01:09 schwarze Exp $ */ 261d06d6bSBaptiste Daroussin /* 3*c1c95addSBrooks Davis * Copyright (c) 2013-2015,2017-2019,2022 Ingo Schwarze <schwarze@openbsd.org> 461d06d6bSBaptiste Daroussin * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 561d06d6bSBaptiste Daroussin * Copyright (c) 2011 Joerg Sonnenberger <joerg@netbsd.org> 661d06d6bSBaptiste Daroussin * 761d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any 861d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above 961d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies. 1061d06d6bSBaptiste Daroussin * 1161d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 1261d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1361d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 1461d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1561d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1661d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1761d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1861d06d6bSBaptiste Daroussin */ 1961d06d6bSBaptiste Daroussin #include "config.h" 2061d06d6bSBaptiste Daroussin 2161d06d6bSBaptiste Daroussin #include <sys/types.h> 2261d06d6bSBaptiste Daroussin 2361d06d6bSBaptiste Daroussin #include <assert.h> 2461d06d6bSBaptiste Daroussin #include <ctype.h> 2561d06d6bSBaptiste Daroussin #include <stdarg.h> 2661d06d6bSBaptiste Daroussin #include <stdlib.h> 2761d06d6bSBaptiste Daroussin #include <stdio.h> 2861d06d6bSBaptiste Daroussin #include <string.h> 2961d06d6bSBaptiste Daroussin 3061d06d6bSBaptiste Daroussin #include "mandoc_aux.h" 3161d06d6bSBaptiste Daroussin #include "mandoc.h" 3261d06d6bSBaptiste Daroussin #include "roff.h" 3361d06d6bSBaptiste Daroussin #include "man.h" 3461d06d6bSBaptiste Daroussin #include "libmandoc.h" 3561d06d6bSBaptiste Daroussin #include "roff_int.h" 3661d06d6bSBaptiste Daroussin #include "libman.h" 3761d06d6bSBaptiste Daroussin 387295610fSBaptiste Daroussin static char *man_hasc(char *); 3961d06d6bSBaptiste Daroussin static int man_ptext(struct roff_man *, int, char *, int); 4061d06d6bSBaptiste Daroussin static int man_pmacro(struct roff_man *, int, char *, int); 4161d06d6bSBaptiste Daroussin 4261d06d6bSBaptiste Daroussin 4361d06d6bSBaptiste Daroussin int 4461d06d6bSBaptiste Daroussin man_parseln(struct roff_man *man, int ln, char *buf, int offs) 4561d06d6bSBaptiste Daroussin { 4661d06d6bSBaptiste Daroussin 4761d06d6bSBaptiste Daroussin if (man->last->type != ROFFT_EQN || ln > man->last->line) 4861d06d6bSBaptiste Daroussin man->flags |= MAN_NEWLINE; 4961d06d6bSBaptiste Daroussin 5061d06d6bSBaptiste Daroussin return roff_getcontrol(man->roff, buf, &offs) ? 5161d06d6bSBaptiste Daroussin man_pmacro(man, ln, buf, offs) : 5261d06d6bSBaptiste Daroussin man_ptext(man, ln, buf, offs); 5361d06d6bSBaptiste Daroussin } 5461d06d6bSBaptiste Daroussin 557295610fSBaptiste Daroussin /* 567295610fSBaptiste Daroussin * If the string ends with \c, return a pointer to the backslash. 577295610fSBaptiste Daroussin * Otherwise, return NULL. 587295610fSBaptiste Daroussin */ 597295610fSBaptiste Daroussin static char * 607295610fSBaptiste Daroussin man_hasc(char *start) 6161d06d6bSBaptiste Daroussin { 627295610fSBaptiste Daroussin char *cp, *ep; 637295610fSBaptiste Daroussin 647295610fSBaptiste Daroussin ep = strchr(start, '\0') - 2; 657295610fSBaptiste Daroussin if (ep < start || ep[0] != '\\' || ep[1] != 'c') 667295610fSBaptiste Daroussin return NULL; 677295610fSBaptiste Daroussin for (cp = ep; cp > start; cp--) 687295610fSBaptiste Daroussin if (cp[-1] != '\\') 697295610fSBaptiste Daroussin break; 707295610fSBaptiste Daroussin return (ep - cp) % 2 ? NULL : ep; 717295610fSBaptiste Daroussin } 727295610fSBaptiste Daroussin 73*c1c95addSBrooks Davis /* 74*c1c95addSBrooks Davis * Rewind all open next-line scopes. 75*c1c95addSBrooks Davis */ 767295610fSBaptiste Daroussin void 777295610fSBaptiste Daroussin man_descope(struct roff_man *man, int line, int offs, char *start) 787295610fSBaptiste Daroussin { 79*c1c95addSBrooks Davis /* First close out all next-line element scopes, if any. */ 8061d06d6bSBaptiste Daroussin 8161d06d6bSBaptiste Daroussin if (man->flags & MAN_ELINE) { 827295610fSBaptiste Daroussin while (man->last->parent->type != ROFFT_ROOT && 837295610fSBaptiste Daroussin man_macro(man->last->parent->tok)->flags & MAN_ESCOPED) 8461d06d6bSBaptiste Daroussin man_unscope(man, man->last->parent); 857295610fSBaptiste Daroussin man->flags &= ~MAN_ELINE; 8661d06d6bSBaptiste Daroussin } 87*c1c95addSBrooks Davis 88*c1c95addSBrooks Davis /* Trailing \c keeps next-line block scope open. */ 89*c1c95addSBrooks Davis 90*c1c95addSBrooks Davis if (start != NULL && man_hasc(start) != NULL) 91*c1c95addSBrooks Davis return; 92*c1c95addSBrooks Davis 93*c1c95addSBrooks Davis /* Close out the next-line block scope, if there is one. */ 94*c1c95addSBrooks Davis 9561d06d6bSBaptiste Daroussin if ( ! (man->flags & MAN_BLINE)) 9661d06d6bSBaptiste Daroussin return; 9761d06d6bSBaptiste Daroussin man_unscope(man, man->last->parent); 9861d06d6bSBaptiste Daroussin roff_body_alloc(man, line, offs, man->last->tok); 997295610fSBaptiste Daroussin man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); 10061d06d6bSBaptiste Daroussin } 10161d06d6bSBaptiste Daroussin 10261d06d6bSBaptiste Daroussin static int 10361d06d6bSBaptiste Daroussin man_ptext(struct roff_man *man, int line, char *buf, int offs) 10461d06d6bSBaptiste Daroussin { 10561d06d6bSBaptiste Daroussin int i; 10661d06d6bSBaptiste Daroussin char *ep; 10761d06d6bSBaptiste Daroussin 1087295610fSBaptiste Daroussin /* In no-fill mode, whitespace is preserved on text lines. */ 10961d06d6bSBaptiste Daroussin 1107295610fSBaptiste Daroussin if (man->flags & ROFF_NOFILL) { 11161d06d6bSBaptiste Daroussin roff_word_alloc(man, line, offs, buf + offs); 1127295610fSBaptiste Daroussin man_descope(man, line, offs, buf + offs); 11361d06d6bSBaptiste Daroussin return 1; 11461d06d6bSBaptiste Daroussin } 11561d06d6bSBaptiste Daroussin 11661d06d6bSBaptiste Daroussin for (i = offs; buf[i] == ' '; i++) 11761d06d6bSBaptiste Daroussin /* Skip leading whitespace. */ ; 11861d06d6bSBaptiste Daroussin 11961d06d6bSBaptiste Daroussin /* 12061d06d6bSBaptiste Daroussin * Blank lines are ignored in next line scope 12161d06d6bSBaptiste Daroussin * and right after headings and cancel preceding \c, 12261d06d6bSBaptiste Daroussin * but add a single vertical space elsewhere. 12361d06d6bSBaptiste Daroussin */ 12461d06d6bSBaptiste Daroussin 12561d06d6bSBaptiste Daroussin if (buf[i] == '\0') { 12661d06d6bSBaptiste Daroussin if (man->flags & (MAN_ELINE | MAN_BLINE)) { 1277295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_BLANK, line, 0, NULL); 12861d06d6bSBaptiste Daroussin return 1; 12961d06d6bSBaptiste Daroussin } 13061d06d6bSBaptiste Daroussin if (man->last->tok == MAN_SH || man->last->tok == MAN_SS) 13161d06d6bSBaptiste Daroussin return 1; 1327295610fSBaptiste Daroussin if (man->last->type == ROFFT_TEXT && 1337295610fSBaptiste Daroussin ((ep = man_hasc(man->last->string)) != NULL)) { 13461d06d6bSBaptiste Daroussin *ep = '\0'; 13561d06d6bSBaptiste Daroussin return 1; 13661d06d6bSBaptiste Daroussin } 13761d06d6bSBaptiste Daroussin roff_elem_alloc(man, line, offs, ROFF_sp); 13861d06d6bSBaptiste Daroussin man->next = ROFF_NEXT_SIBLING; 13961d06d6bSBaptiste Daroussin return 1; 14061d06d6bSBaptiste Daroussin } 14161d06d6bSBaptiste Daroussin 14261d06d6bSBaptiste Daroussin /* 14361d06d6bSBaptiste Daroussin * Warn if the last un-escaped character is whitespace. Then 14461d06d6bSBaptiste Daroussin * strip away the remaining spaces (tabs stay!). 14561d06d6bSBaptiste Daroussin */ 14661d06d6bSBaptiste Daroussin 14761d06d6bSBaptiste Daroussin i = (int)strlen(buf); 14861d06d6bSBaptiste Daroussin assert(i); 14961d06d6bSBaptiste Daroussin 15061d06d6bSBaptiste Daroussin if (' ' == buf[i - 1] || '\t' == buf[i - 1]) { 15161d06d6bSBaptiste Daroussin if (i > 1 && '\\' != buf[i - 2]) 1527295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_SPACE_EOL, line, i - 1, NULL); 15361d06d6bSBaptiste Daroussin 15461d06d6bSBaptiste Daroussin for (--i; i && ' ' == buf[i]; i--) 15561d06d6bSBaptiste Daroussin /* Spin back to non-space. */ ; 15661d06d6bSBaptiste Daroussin 15761d06d6bSBaptiste Daroussin /* Jump ahead of escaped whitespace. */ 15861d06d6bSBaptiste Daroussin i += '\\' == buf[i] ? 2 : 1; 15961d06d6bSBaptiste Daroussin 16061d06d6bSBaptiste Daroussin buf[i] = '\0'; 16161d06d6bSBaptiste Daroussin } 16261d06d6bSBaptiste Daroussin roff_word_alloc(man, line, offs, buf + offs); 16361d06d6bSBaptiste Daroussin 16461d06d6bSBaptiste Daroussin /* 16561d06d6bSBaptiste Daroussin * End-of-sentence check. If the last character is an unescaped 16661d06d6bSBaptiste Daroussin * EOS character, then flag the node as being the end of a 16761d06d6bSBaptiste Daroussin * sentence. The front-end will know how to interpret this. 16861d06d6bSBaptiste Daroussin */ 16961d06d6bSBaptiste Daroussin 17061d06d6bSBaptiste Daroussin assert(i); 17161d06d6bSBaptiste Daroussin if (mandoc_eos(buf, (size_t)i)) 17261d06d6bSBaptiste Daroussin man->last->flags |= NODE_EOS; 17361d06d6bSBaptiste Daroussin 1747295610fSBaptiste Daroussin man_descope(man, line, offs, buf + offs); 17561d06d6bSBaptiste Daroussin return 1; 17661d06d6bSBaptiste Daroussin } 17761d06d6bSBaptiste Daroussin 17861d06d6bSBaptiste Daroussin static int 17961d06d6bSBaptiste Daroussin man_pmacro(struct roff_man *man, int ln, char *buf, int offs) 18061d06d6bSBaptiste Daroussin { 18161d06d6bSBaptiste Daroussin struct roff_node *n; 18261d06d6bSBaptiste Daroussin const char *cp; 18361d06d6bSBaptiste Daroussin size_t sz; 18461d06d6bSBaptiste Daroussin enum roff_tok tok; 18561d06d6bSBaptiste Daroussin int ppos; 18661d06d6bSBaptiste Daroussin int bline; 18761d06d6bSBaptiste Daroussin 18861d06d6bSBaptiste Daroussin /* Determine the line macro. */ 18961d06d6bSBaptiste Daroussin 19061d06d6bSBaptiste Daroussin ppos = offs; 19161d06d6bSBaptiste Daroussin tok = TOKEN_NONE; 19261d06d6bSBaptiste Daroussin for (sz = 0; sz < 4 && strchr(" \t\\", buf[offs]) == NULL; sz++) 19361d06d6bSBaptiste Daroussin offs++; 19461d06d6bSBaptiste Daroussin if (sz > 0 && sz < 4) 19561d06d6bSBaptiste Daroussin tok = roffhash_find(man->manmac, buf + ppos, sz); 19661d06d6bSBaptiste Daroussin if (tok == TOKEN_NONE) { 1977295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_MACRO, ln, ppos, "%s", buf + ppos - 1); 19861d06d6bSBaptiste Daroussin return 1; 19961d06d6bSBaptiste Daroussin } 20061d06d6bSBaptiste Daroussin 20161d06d6bSBaptiste Daroussin /* Skip a leading escape sequence or tab. */ 20261d06d6bSBaptiste Daroussin 20361d06d6bSBaptiste Daroussin switch (buf[offs]) { 20461d06d6bSBaptiste Daroussin case '\\': 20561d06d6bSBaptiste Daroussin cp = buf + offs + 1; 20661d06d6bSBaptiste Daroussin mandoc_escape(&cp, NULL, NULL); 20761d06d6bSBaptiste Daroussin offs = cp - buf; 20861d06d6bSBaptiste Daroussin break; 20961d06d6bSBaptiste Daroussin case '\t': 21061d06d6bSBaptiste Daroussin offs++; 21161d06d6bSBaptiste Daroussin break; 21261d06d6bSBaptiste Daroussin default: 21361d06d6bSBaptiste Daroussin break; 21461d06d6bSBaptiste Daroussin } 21561d06d6bSBaptiste Daroussin 21661d06d6bSBaptiste Daroussin /* Jump to the next non-whitespace word. */ 21761d06d6bSBaptiste Daroussin 21861d06d6bSBaptiste Daroussin while (buf[offs] == ' ') 21961d06d6bSBaptiste Daroussin offs++; 22061d06d6bSBaptiste Daroussin 22161d06d6bSBaptiste Daroussin /* 22261d06d6bSBaptiste Daroussin * Trailing whitespace. Note that tabs are allowed to be passed 22361d06d6bSBaptiste Daroussin * into the parser as "text", so we only warn about spaces here. 22461d06d6bSBaptiste Daroussin */ 22561d06d6bSBaptiste Daroussin 22661d06d6bSBaptiste Daroussin if (buf[offs] == '\0' && buf[offs - 1] == ' ') 2277295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_SPACE_EOL, ln, offs - 1, NULL); 22861d06d6bSBaptiste Daroussin 22961d06d6bSBaptiste Daroussin /* 23061d06d6bSBaptiste Daroussin * Some macros break next-line scopes; otherwise, remember 23161d06d6bSBaptiste Daroussin * whether we are in next-line scope for a block head. 23261d06d6bSBaptiste Daroussin */ 23361d06d6bSBaptiste Daroussin 23461d06d6bSBaptiste Daroussin man_breakscope(man, tok); 23561d06d6bSBaptiste Daroussin bline = man->flags & MAN_BLINE; 23661d06d6bSBaptiste Daroussin 23761d06d6bSBaptiste Daroussin /* 23861d06d6bSBaptiste Daroussin * If the line in next-line scope ends with \c, keep the 23961d06d6bSBaptiste Daroussin * next-line scope open for the subsequent input line. 24061d06d6bSBaptiste Daroussin * That is not at all portable, only groff >= 1.22.4 24161d06d6bSBaptiste Daroussin * does it, but *if* this weird idiom occurs in a manual 24261d06d6bSBaptiste Daroussin * page, that's very likely what the author intended. 24361d06d6bSBaptiste Daroussin */ 24461d06d6bSBaptiste Daroussin 2457295610fSBaptiste Daroussin if (bline && man_hasc(buf + offs)) 24661d06d6bSBaptiste Daroussin bline = 0; 24761d06d6bSBaptiste Daroussin 24861d06d6bSBaptiste Daroussin /* Call to handler... */ 24961d06d6bSBaptiste Daroussin 2507295610fSBaptiste Daroussin (*man_macro(tok)->fp)(man, tok, ln, ppos, &offs, buf); 25161d06d6bSBaptiste Daroussin 25261d06d6bSBaptiste Daroussin /* In quick mode (for mandocdb), abort after the NAME section. */ 25361d06d6bSBaptiste Daroussin 25461d06d6bSBaptiste Daroussin if (man->quick && tok == MAN_SH) { 25561d06d6bSBaptiste Daroussin n = man->last; 25661d06d6bSBaptiste Daroussin if (n->type == ROFFT_BODY && 25761d06d6bSBaptiste Daroussin strcmp(n->prev->child->string, "NAME")) 25861d06d6bSBaptiste Daroussin return 2; 25961d06d6bSBaptiste Daroussin } 26061d06d6bSBaptiste Daroussin 26161d06d6bSBaptiste Daroussin /* 26261d06d6bSBaptiste Daroussin * If we are in a next-line scope for a block head, 26361d06d6bSBaptiste Daroussin * close it out now and switch to the body, 26461d06d6bSBaptiste Daroussin * unless the next-line scope is allowed to continue. 26561d06d6bSBaptiste Daroussin */ 26661d06d6bSBaptiste Daroussin 2677295610fSBaptiste Daroussin if (bline == 0 || 2687295610fSBaptiste Daroussin (man->flags & MAN_BLINE) == 0 || 2697295610fSBaptiste Daroussin man->flags & MAN_ELINE || 2707295610fSBaptiste Daroussin man_macro(tok)->flags & MAN_NSCOPED) 27161d06d6bSBaptiste Daroussin return 1; 27261d06d6bSBaptiste Daroussin 27361d06d6bSBaptiste Daroussin man_unscope(man, man->last->parent); 27461d06d6bSBaptiste Daroussin roff_body_alloc(man, ln, ppos, man->last->tok); 2757295610fSBaptiste Daroussin man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); 27661d06d6bSBaptiste Daroussin return 1; 27761d06d6bSBaptiste Daroussin } 27861d06d6bSBaptiste Daroussin 279*c1c95addSBrooks Davis /* 280*c1c95addSBrooks Davis * Rewind open next-line scopes 281*c1c95addSBrooks Davis * unless the tok request or macro is allowed inside them. 282*c1c95addSBrooks Davis */ 28361d06d6bSBaptiste Daroussin void 28461d06d6bSBaptiste Daroussin man_breakscope(struct roff_man *man, int tok) 28561d06d6bSBaptiste Daroussin { 28661d06d6bSBaptiste Daroussin struct roff_node *n; 28761d06d6bSBaptiste Daroussin 28861d06d6bSBaptiste Daroussin /* 28961d06d6bSBaptiste Daroussin * An element next line scope is open, 29061d06d6bSBaptiste Daroussin * and the new macro is not allowed inside elements. 29161d06d6bSBaptiste Daroussin * Delete the element that is being broken. 29261d06d6bSBaptiste Daroussin */ 29361d06d6bSBaptiste Daroussin 29461d06d6bSBaptiste Daroussin if (man->flags & MAN_ELINE && (tok < MAN_TH || 2957295610fSBaptiste Daroussin (man_macro(tok)->flags & MAN_NSCOPED) == 0)) { 29661d06d6bSBaptiste Daroussin n = man->last; 29761d06d6bSBaptiste Daroussin if (n->type == ROFFT_TEXT) 29861d06d6bSBaptiste Daroussin n = n->parent; 29961d06d6bSBaptiste Daroussin if (n->tok < MAN_TH || 3007295610fSBaptiste Daroussin (man_macro(n->tok)->flags & (MAN_NSCOPED | MAN_ESCOPED)) 3017295610fSBaptiste Daroussin == MAN_NSCOPED) 30261d06d6bSBaptiste Daroussin n = n->parent; 303*c1c95addSBrooks Davis for (;;) { 3047295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_LINE, n->line, n->pos, 3057295610fSBaptiste Daroussin "%s breaks %s", roff_name[tok], roff_name[n->tok]); 306*c1c95addSBrooks Davis if (n->parent->type != ROFFT_ELEM || 307*c1c95addSBrooks Davis (man_macro(n->parent->tok)->flags & 308*c1c95addSBrooks Davis MAN_ESCOPED) == 0) 309*c1c95addSBrooks Davis break; 310*c1c95addSBrooks Davis n = n->parent; 311*c1c95addSBrooks Davis } 31261d06d6bSBaptiste Daroussin roff_node_delete(man, n); 31361d06d6bSBaptiste Daroussin man->flags &= ~MAN_ELINE; 31461d06d6bSBaptiste Daroussin } 31561d06d6bSBaptiste Daroussin 31661d06d6bSBaptiste Daroussin /* 31761d06d6bSBaptiste Daroussin * Weird special case: 31861d06d6bSBaptiste Daroussin * Switching fill mode closes section headers. 31961d06d6bSBaptiste Daroussin */ 32061d06d6bSBaptiste Daroussin 32161d06d6bSBaptiste Daroussin if (man->flags & MAN_BLINE && 3227295610fSBaptiste Daroussin (tok == ROFF_nf || tok == ROFF_fi) && 32361d06d6bSBaptiste Daroussin (man->last->tok == MAN_SH || man->last->tok == MAN_SS)) { 32461d06d6bSBaptiste Daroussin n = man->last; 32561d06d6bSBaptiste Daroussin man_unscope(man, n); 32661d06d6bSBaptiste Daroussin roff_body_alloc(man, n->line, n->pos, n->tok); 3277295610fSBaptiste Daroussin man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); 32861d06d6bSBaptiste Daroussin } 32961d06d6bSBaptiste Daroussin 33061d06d6bSBaptiste Daroussin /* 33161d06d6bSBaptiste Daroussin * A block header next line scope is open, 33261d06d6bSBaptiste Daroussin * and the new macro is not allowed inside block headers. 33361d06d6bSBaptiste Daroussin * Delete the block that is being broken. 33461d06d6bSBaptiste Daroussin */ 33561d06d6bSBaptiste Daroussin 3367295610fSBaptiste Daroussin if (man->flags & MAN_BLINE && tok != ROFF_nf && tok != ROFF_fi && 3377295610fSBaptiste Daroussin (tok < MAN_TH || man_macro(tok)->flags & MAN_XSCOPE)) { 33861d06d6bSBaptiste Daroussin n = man->last; 33961d06d6bSBaptiste Daroussin if (n->type == ROFFT_TEXT) 34061d06d6bSBaptiste Daroussin n = n->parent; 34161d06d6bSBaptiste Daroussin if (n->tok < MAN_TH || 3427295610fSBaptiste Daroussin (man_macro(n->tok)->flags & MAN_XSCOPE) == 0) 34361d06d6bSBaptiste Daroussin n = n->parent; 34461d06d6bSBaptiste Daroussin 34561d06d6bSBaptiste Daroussin assert(n->type == ROFFT_HEAD); 34661d06d6bSBaptiste Daroussin n = n->parent; 34761d06d6bSBaptiste Daroussin assert(n->type == ROFFT_BLOCK); 3487295610fSBaptiste Daroussin assert(man_macro(n->tok)->flags & MAN_BSCOPED); 34961d06d6bSBaptiste Daroussin 3507295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_LINE, n->line, n->pos, 3517295610fSBaptiste Daroussin "%s breaks %s", roff_name[tok], roff_name[n->tok]); 35261d06d6bSBaptiste Daroussin 35361d06d6bSBaptiste Daroussin roff_node_delete(man, n); 3547295610fSBaptiste Daroussin man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); 35561d06d6bSBaptiste Daroussin } 35661d06d6bSBaptiste Daroussin } 357