1*6d38604fSBaptiste Daroussin /* $Id: mdoc_macro.c,v 1.234 2020/01/19 18:02:00 schwarze Exp $ */ 261d06d6bSBaptiste Daroussin /* 361d06d6bSBaptiste Daroussin * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> 4*6d38604fSBaptiste Daroussin * Copyright (c) 2010, 2012-2020 Ingo Schwarze <schwarze@openbsd.org> 561d06d6bSBaptiste Daroussin * 661d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any 761d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above 861d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies. 961d06d6bSBaptiste Daroussin * 1061d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 1161d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1261d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 1361d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1461d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1561d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1661d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1761d06d6bSBaptiste Daroussin */ 1861d06d6bSBaptiste Daroussin #include "config.h" 1961d06d6bSBaptiste Daroussin 2061d06d6bSBaptiste Daroussin #include <sys/types.h> 2161d06d6bSBaptiste Daroussin 2261d06d6bSBaptiste Daroussin #include <assert.h> 2361d06d6bSBaptiste Daroussin #include <ctype.h> 2461d06d6bSBaptiste Daroussin #include <stdlib.h> 2561d06d6bSBaptiste Daroussin #include <stdio.h> 2661d06d6bSBaptiste Daroussin #include <string.h> 2761d06d6bSBaptiste Daroussin #include <time.h> 2861d06d6bSBaptiste Daroussin 2961d06d6bSBaptiste Daroussin #include "mandoc.h" 3061d06d6bSBaptiste Daroussin #include "roff.h" 3161d06d6bSBaptiste Daroussin #include "mdoc.h" 3261d06d6bSBaptiste Daroussin #include "libmandoc.h" 3361d06d6bSBaptiste Daroussin #include "roff_int.h" 3461d06d6bSBaptiste Daroussin #include "libmdoc.h" 3561d06d6bSBaptiste Daroussin 3661d06d6bSBaptiste Daroussin static void blk_full(MACRO_PROT_ARGS); 3761d06d6bSBaptiste Daroussin static void blk_exp_close(MACRO_PROT_ARGS); 3861d06d6bSBaptiste Daroussin static void blk_part_exp(MACRO_PROT_ARGS); 3961d06d6bSBaptiste Daroussin static void blk_part_imp(MACRO_PROT_ARGS); 4061d06d6bSBaptiste Daroussin static void ctx_synopsis(MACRO_PROT_ARGS); 4161d06d6bSBaptiste Daroussin static void in_line_eoln(MACRO_PROT_ARGS); 4261d06d6bSBaptiste Daroussin static void in_line_argn(MACRO_PROT_ARGS); 4361d06d6bSBaptiste Daroussin static void in_line(MACRO_PROT_ARGS); 4461d06d6bSBaptiste Daroussin static void phrase_ta(MACRO_PROT_ARGS); 4561d06d6bSBaptiste Daroussin 4661d06d6bSBaptiste Daroussin static void append_delims(struct roff_man *, int, int *, char *); 4761d06d6bSBaptiste Daroussin static void dword(struct roff_man *, int, int, const char *, 4861d06d6bSBaptiste Daroussin enum mdelim, int); 4961d06d6bSBaptiste Daroussin static int find_pending(struct roff_man *, enum roff_tok, 5061d06d6bSBaptiste Daroussin int, int, struct roff_node *); 5161d06d6bSBaptiste Daroussin static int lookup(struct roff_man *, int, int, int, const char *); 527295610fSBaptiste Daroussin static int macro_or_word(MACRO_PROT_ARGS, char *, int); 5361d06d6bSBaptiste Daroussin static void break_intermediate(struct roff_node *, 5461d06d6bSBaptiste Daroussin struct roff_node *); 5561d06d6bSBaptiste Daroussin static int parse_rest(struct roff_man *, enum roff_tok, 5661d06d6bSBaptiste Daroussin int, int *, char *); 5761d06d6bSBaptiste Daroussin static enum roff_tok rew_alt(enum roff_tok); 5861d06d6bSBaptiste Daroussin static void rew_elem(struct roff_man *, enum roff_tok); 5961d06d6bSBaptiste Daroussin static void rew_last(struct roff_man *, const struct roff_node *); 6061d06d6bSBaptiste Daroussin static void rew_pending(struct roff_man *, 6161d06d6bSBaptiste Daroussin const struct roff_node *); 6261d06d6bSBaptiste Daroussin 637295610fSBaptiste Daroussin static const struct mdoc_macro mdoc_macros[MDOC_MAX - MDOC_Dd] = { 64*6d38604fSBaptiste Daroussin { in_line_eoln, MDOC_PROLOGUE | MDOC_JOIN }, /* Dd */ 6561d06d6bSBaptiste Daroussin { in_line_eoln, MDOC_PROLOGUE }, /* Dt */ 6661d06d6bSBaptiste Daroussin { in_line_eoln, MDOC_PROLOGUE }, /* Os */ 6761d06d6bSBaptiste Daroussin { blk_full, MDOC_PARSED | MDOC_JOIN }, /* Sh */ 6861d06d6bSBaptiste Daroussin { blk_full, MDOC_PARSED | MDOC_JOIN }, /* Ss */ 6961d06d6bSBaptiste Daroussin { in_line_eoln, 0 }, /* Pp */ 7061d06d6bSBaptiste Daroussin { blk_part_imp, MDOC_PARSED | MDOC_JOIN }, /* D1 */ 7161d06d6bSBaptiste Daroussin { blk_part_imp, MDOC_PARSED | MDOC_JOIN }, /* Dl */ 7261d06d6bSBaptiste Daroussin { blk_full, MDOC_EXPLICIT }, /* Bd */ 7361d06d6bSBaptiste Daroussin { blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* Ed */ 7461d06d6bSBaptiste Daroussin { blk_full, MDOC_EXPLICIT }, /* Bl */ 7561d06d6bSBaptiste Daroussin { blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* El */ 7661d06d6bSBaptiste Daroussin { blk_full, MDOC_PARSED | MDOC_JOIN }, /* It */ 7761d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */ 7861d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* An */ 7961d06d6bSBaptiste Daroussin { in_line_argn, MDOC_CALLABLE | MDOC_PARSED | 8061d06d6bSBaptiste Daroussin MDOC_IGNDELIM | MDOC_JOIN }, /* Ap */ 8161d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ar */ 8261d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Cd */ 8361d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Cm */ 8461d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Dv */ 8561d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Er */ 8661d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ev */ 8761d06d6bSBaptiste Daroussin { in_line_eoln, 0 }, /* Ex */ 8861d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fa */ 8961d06d6bSBaptiste Daroussin { in_line_eoln, 0 }, /* Fd */ 9061d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fl */ 9161d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fn */ 9261d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ft */ 9361d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ic */ 9461d06d6bSBaptiste Daroussin { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* In */ 9561d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Li */ 9661d06d6bSBaptiste Daroussin { blk_full, MDOC_JOIN }, /* Nd */ 9761d06d6bSBaptiste Daroussin { ctx_synopsis, MDOC_CALLABLE | MDOC_PARSED }, /* Nm */ 9861d06d6bSBaptiste Daroussin { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Op */ 9961d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ot */ 10061d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pa */ 10161d06d6bSBaptiste Daroussin { in_line_eoln, 0 }, /* Rv */ 10261d06d6bSBaptiste Daroussin { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* St */ 10361d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Va */ 10461d06d6bSBaptiste Daroussin { ctx_synopsis, MDOC_CALLABLE | MDOC_PARSED }, /* Vt */ 10561d06d6bSBaptiste Daroussin { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Xr */ 10661d06d6bSBaptiste Daroussin { in_line_eoln, MDOC_JOIN }, /* %A */ 10761d06d6bSBaptiste Daroussin { in_line_eoln, MDOC_JOIN }, /* %B */ 10861d06d6bSBaptiste Daroussin { in_line_eoln, MDOC_JOIN }, /* %D */ 10961d06d6bSBaptiste Daroussin { in_line_eoln, MDOC_JOIN }, /* %I */ 11061d06d6bSBaptiste Daroussin { in_line_eoln, MDOC_JOIN }, /* %J */ 11161d06d6bSBaptiste Daroussin { in_line_eoln, 0 }, /* %N */ 11261d06d6bSBaptiste Daroussin { in_line_eoln, MDOC_JOIN }, /* %O */ 11361d06d6bSBaptiste Daroussin { in_line_eoln, 0 }, /* %P */ 11461d06d6bSBaptiste Daroussin { in_line_eoln, MDOC_JOIN }, /* %R */ 11561d06d6bSBaptiste Daroussin { in_line_eoln, MDOC_JOIN }, /* %T */ 11661d06d6bSBaptiste Daroussin { in_line_eoln, 0 }, /* %V */ 11761d06d6bSBaptiste Daroussin { blk_exp_close, MDOC_CALLABLE | MDOC_PARSED | 11861d06d6bSBaptiste Daroussin MDOC_EXPLICIT | MDOC_JOIN }, /* Ac */ 11961d06d6bSBaptiste Daroussin { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | 12061d06d6bSBaptiste Daroussin MDOC_EXPLICIT | MDOC_JOIN }, /* Ao */ 12161d06d6bSBaptiste Daroussin { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Aq */ 12261d06d6bSBaptiste Daroussin { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* At */ 12361d06d6bSBaptiste Daroussin { blk_exp_close, MDOC_CALLABLE | MDOC_PARSED | 12461d06d6bSBaptiste Daroussin MDOC_EXPLICIT | MDOC_JOIN }, /* Bc */ 12561d06d6bSBaptiste Daroussin { blk_full, MDOC_EXPLICIT }, /* Bf */ 12661d06d6bSBaptiste Daroussin { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | 12761d06d6bSBaptiste Daroussin MDOC_EXPLICIT | MDOC_JOIN }, /* Bo */ 12861d06d6bSBaptiste Daroussin { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Bq */ 12961d06d6bSBaptiste Daroussin { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bsx */ 13061d06d6bSBaptiste Daroussin { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bx */ 13161d06d6bSBaptiste Daroussin { in_line_eoln, 0 }, /* Db */ 13261d06d6bSBaptiste Daroussin { blk_exp_close, MDOC_CALLABLE | MDOC_PARSED | 13361d06d6bSBaptiste Daroussin MDOC_EXPLICIT | MDOC_JOIN }, /* Dc */ 13461d06d6bSBaptiste Daroussin { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | 13561d06d6bSBaptiste Daroussin MDOC_EXPLICIT | MDOC_JOIN }, /* Do */ 13661d06d6bSBaptiste Daroussin { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Dq */ 13761d06d6bSBaptiste Daroussin { blk_exp_close, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Ec */ 13861d06d6bSBaptiste Daroussin { blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* Ef */ 13961d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Em */ 14061d06d6bSBaptiste Daroussin { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Eo */ 14161d06d6bSBaptiste Daroussin { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Fx */ 14261d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ms */ 14361d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* No */ 14461d06d6bSBaptiste Daroussin { in_line_argn, MDOC_CALLABLE | MDOC_PARSED | 14561d06d6bSBaptiste Daroussin MDOC_IGNDELIM | MDOC_JOIN }, /* Ns */ 14661d06d6bSBaptiste Daroussin { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Nx */ 14761d06d6bSBaptiste Daroussin { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ox */ 14861d06d6bSBaptiste Daroussin { blk_exp_close, MDOC_CALLABLE | MDOC_PARSED | 14961d06d6bSBaptiste Daroussin MDOC_EXPLICIT | MDOC_JOIN }, /* Pc */ 15061d06d6bSBaptiste Daroussin { in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_IGNDELIM }, /* Pf */ 15161d06d6bSBaptiste Daroussin { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | 15261d06d6bSBaptiste Daroussin MDOC_EXPLICIT | MDOC_JOIN }, /* Po */ 15361d06d6bSBaptiste Daroussin { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Pq */ 15461d06d6bSBaptiste Daroussin { blk_exp_close, MDOC_CALLABLE | MDOC_PARSED | 15561d06d6bSBaptiste Daroussin MDOC_EXPLICIT | MDOC_JOIN }, /* Qc */ 15661d06d6bSBaptiste Daroussin { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ql */ 15761d06d6bSBaptiste Daroussin { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | 15861d06d6bSBaptiste Daroussin MDOC_EXPLICIT | MDOC_JOIN }, /* Qo */ 15961d06d6bSBaptiste Daroussin { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Qq */ 16061d06d6bSBaptiste Daroussin { blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* Re */ 16161d06d6bSBaptiste Daroussin { blk_full, MDOC_EXPLICIT }, /* Rs */ 16261d06d6bSBaptiste Daroussin { blk_exp_close, MDOC_CALLABLE | MDOC_PARSED | 16361d06d6bSBaptiste Daroussin MDOC_EXPLICIT | MDOC_JOIN }, /* Sc */ 16461d06d6bSBaptiste Daroussin { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | 16561d06d6bSBaptiste Daroussin MDOC_EXPLICIT | MDOC_JOIN }, /* So */ 16661d06d6bSBaptiste Daroussin { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Sq */ 16761d06d6bSBaptiste Daroussin { in_line_argn, 0 }, /* Sm */ 16861d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Sx */ 16961d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Sy */ 17061d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Tn */ 17161d06d6bSBaptiste Daroussin { in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ux */ 17261d06d6bSBaptiste Daroussin { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Xc */ 17361d06d6bSBaptiste Daroussin { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Xo */ 17461d06d6bSBaptiste Daroussin { blk_full, MDOC_EXPLICIT | MDOC_CALLABLE }, /* Fo */ 17561d06d6bSBaptiste Daroussin { blk_exp_close, MDOC_CALLABLE | MDOC_PARSED | 17661d06d6bSBaptiste Daroussin MDOC_EXPLICIT | MDOC_JOIN }, /* Fc */ 17761d06d6bSBaptiste Daroussin { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | 17861d06d6bSBaptiste Daroussin MDOC_EXPLICIT | MDOC_JOIN }, /* Oo */ 17961d06d6bSBaptiste Daroussin { blk_exp_close, MDOC_CALLABLE | MDOC_PARSED | 18061d06d6bSBaptiste Daroussin MDOC_EXPLICIT | MDOC_JOIN }, /* Oc */ 18161d06d6bSBaptiste Daroussin { blk_full, MDOC_EXPLICIT }, /* Bk */ 18261d06d6bSBaptiste Daroussin { blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* Ek */ 18361d06d6bSBaptiste Daroussin { in_line_eoln, 0 }, /* Bt */ 18461d06d6bSBaptiste Daroussin { in_line_eoln, 0 }, /* Hf */ 18561d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fr */ 18661d06d6bSBaptiste Daroussin { in_line_eoln, 0 }, /* Ud */ 18761d06d6bSBaptiste Daroussin { in_line, 0 }, /* Lb */ 18861d06d6bSBaptiste Daroussin { in_line_eoln, 0 }, /* Lp */ 18961d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Lk */ 19061d06d6bSBaptiste Daroussin { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Mt */ 19161d06d6bSBaptiste Daroussin { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Brq */ 19261d06d6bSBaptiste Daroussin { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | 19361d06d6bSBaptiste Daroussin MDOC_EXPLICIT | MDOC_JOIN }, /* Bro */ 19461d06d6bSBaptiste Daroussin { blk_exp_close, MDOC_CALLABLE | MDOC_PARSED | 19561d06d6bSBaptiste Daroussin MDOC_EXPLICIT | MDOC_JOIN }, /* Brc */ 19661d06d6bSBaptiste Daroussin { in_line_eoln, MDOC_JOIN }, /* %C */ 19761d06d6bSBaptiste Daroussin { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Es */ 19861d06d6bSBaptiste Daroussin { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* En */ 19961d06d6bSBaptiste Daroussin { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Dx */ 20061d06d6bSBaptiste Daroussin { in_line_eoln, MDOC_JOIN }, /* %Q */ 20161d06d6bSBaptiste Daroussin { in_line_eoln, 0 }, /* %U */ 20261d06d6bSBaptiste Daroussin { phrase_ta, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ta */ 203*6d38604fSBaptiste Daroussin { in_line_eoln, 0 }, /* Tg */ 20461d06d6bSBaptiste Daroussin }; 20561d06d6bSBaptiste Daroussin 20661d06d6bSBaptiste Daroussin 2077295610fSBaptiste Daroussin const struct mdoc_macro * 2087295610fSBaptiste Daroussin mdoc_macro(enum roff_tok tok) 2097295610fSBaptiste Daroussin { 2107295610fSBaptiste Daroussin assert(tok >= MDOC_Dd && tok < MDOC_MAX); 2117295610fSBaptiste Daroussin return mdoc_macros + (tok - MDOC_Dd); 2127295610fSBaptiste Daroussin } 2137295610fSBaptiste Daroussin 21461d06d6bSBaptiste Daroussin /* 21561d06d6bSBaptiste Daroussin * This is called at the end of parsing. It must traverse up the tree, 21661d06d6bSBaptiste Daroussin * closing out open [implicit] scopes. Obviously, open explicit scopes 21761d06d6bSBaptiste Daroussin * are errors. 21861d06d6bSBaptiste Daroussin */ 21961d06d6bSBaptiste Daroussin void 22061d06d6bSBaptiste Daroussin mdoc_endparse(struct roff_man *mdoc) 22161d06d6bSBaptiste Daroussin { 22261d06d6bSBaptiste Daroussin struct roff_node *n; 22361d06d6bSBaptiste Daroussin 22461d06d6bSBaptiste Daroussin /* Scan for open explicit scopes. */ 22561d06d6bSBaptiste Daroussin 22661d06d6bSBaptiste Daroussin n = mdoc->last->flags & NODE_VALID ? 22761d06d6bSBaptiste Daroussin mdoc->last->parent : mdoc->last; 22861d06d6bSBaptiste Daroussin 22961d06d6bSBaptiste Daroussin for ( ; n; n = n->parent) 23061d06d6bSBaptiste Daroussin if (n->type == ROFFT_BLOCK && 2317295610fSBaptiste Daroussin mdoc_macro(n->tok)->flags & MDOC_EXPLICIT) 2327295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_NOEND, 2337295610fSBaptiste Daroussin n->line, n->pos, "%s", roff_name[n->tok]); 23461d06d6bSBaptiste Daroussin 23561d06d6bSBaptiste Daroussin /* Rewind to the first. */ 23661d06d6bSBaptiste Daroussin 2377295610fSBaptiste Daroussin rew_last(mdoc, mdoc->meta.first); 23861d06d6bSBaptiste Daroussin } 23961d06d6bSBaptiste Daroussin 24061d06d6bSBaptiste Daroussin /* 24161d06d6bSBaptiste Daroussin * Look up the macro at *p called by "from", 24261d06d6bSBaptiste Daroussin * or as a line macro if from == TOKEN_NONE. 24361d06d6bSBaptiste Daroussin */ 24461d06d6bSBaptiste Daroussin static int 24561d06d6bSBaptiste Daroussin lookup(struct roff_man *mdoc, int from, int line, int ppos, const char *p) 24661d06d6bSBaptiste Daroussin { 24761d06d6bSBaptiste Daroussin enum roff_tok res; 24861d06d6bSBaptiste Daroussin 24961d06d6bSBaptiste Daroussin if (mdoc->flags & MDOC_PHRASEQF) { 25061d06d6bSBaptiste Daroussin mdoc->flags &= ~MDOC_PHRASEQF; 25161d06d6bSBaptiste Daroussin return TOKEN_NONE; 25261d06d6bSBaptiste Daroussin } 2537295610fSBaptiste Daroussin if (from == TOKEN_NONE || mdoc_macro(from)->flags & MDOC_PARSED) { 25461d06d6bSBaptiste Daroussin res = roffhash_find(mdoc->mdocmac, p, 0); 25561d06d6bSBaptiste Daroussin if (res != TOKEN_NONE) { 2567295610fSBaptiste Daroussin if (mdoc_macro(res)->flags & MDOC_CALLABLE) 25761d06d6bSBaptiste Daroussin return res; 2587295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_MACRO_CALL, line, ppos, "%s", p); 25961d06d6bSBaptiste Daroussin } 26061d06d6bSBaptiste Daroussin } 26161d06d6bSBaptiste Daroussin return TOKEN_NONE; 26261d06d6bSBaptiste Daroussin } 26361d06d6bSBaptiste Daroussin 26461d06d6bSBaptiste Daroussin /* 26561d06d6bSBaptiste Daroussin * Rewind up to and including a specific node. 26661d06d6bSBaptiste Daroussin */ 26761d06d6bSBaptiste Daroussin static void 26861d06d6bSBaptiste Daroussin rew_last(struct roff_man *mdoc, const struct roff_node *to) 26961d06d6bSBaptiste Daroussin { 27061d06d6bSBaptiste Daroussin 27161d06d6bSBaptiste Daroussin if (to->flags & NODE_VALID) 27261d06d6bSBaptiste Daroussin return; 27361d06d6bSBaptiste Daroussin 27461d06d6bSBaptiste Daroussin while (mdoc->last != to) { 27561d06d6bSBaptiste Daroussin mdoc_state(mdoc, mdoc->last); 27661d06d6bSBaptiste Daroussin mdoc->last->flags |= NODE_VALID | NODE_ENDED; 27761d06d6bSBaptiste Daroussin mdoc->last = mdoc->last->parent; 27861d06d6bSBaptiste Daroussin } 27961d06d6bSBaptiste Daroussin mdoc_state(mdoc, mdoc->last); 28061d06d6bSBaptiste Daroussin mdoc->last->flags |= NODE_VALID | NODE_ENDED; 28161d06d6bSBaptiste Daroussin mdoc->next = ROFF_NEXT_SIBLING; 28261d06d6bSBaptiste Daroussin } 28361d06d6bSBaptiste Daroussin 28461d06d6bSBaptiste Daroussin /* 28561d06d6bSBaptiste Daroussin * Rewind up to a specific block, including all blocks that broke it. 28661d06d6bSBaptiste Daroussin */ 28761d06d6bSBaptiste Daroussin static void 28861d06d6bSBaptiste Daroussin rew_pending(struct roff_man *mdoc, const struct roff_node *n) 28961d06d6bSBaptiste Daroussin { 29061d06d6bSBaptiste Daroussin 29161d06d6bSBaptiste Daroussin for (;;) { 29261d06d6bSBaptiste Daroussin rew_last(mdoc, n); 29361d06d6bSBaptiste Daroussin 29461d06d6bSBaptiste Daroussin if (mdoc->last == n) { 29561d06d6bSBaptiste Daroussin switch (n->type) { 29661d06d6bSBaptiste Daroussin case ROFFT_HEAD: 29761d06d6bSBaptiste Daroussin roff_body_alloc(mdoc, n->line, n->pos, 29861d06d6bSBaptiste Daroussin n->tok); 2997295610fSBaptiste Daroussin if (n->tok == MDOC_Ss) 3007295610fSBaptiste Daroussin mdoc->flags &= ~ROFF_NONOFILL; 30161d06d6bSBaptiste Daroussin break; 30261d06d6bSBaptiste Daroussin case ROFFT_BLOCK: 30361d06d6bSBaptiste Daroussin break; 30461d06d6bSBaptiste Daroussin default: 30561d06d6bSBaptiste Daroussin return; 30661d06d6bSBaptiste Daroussin } 30761d06d6bSBaptiste Daroussin if ( ! (n->flags & NODE_BROKEN)) 30861d06d6bSBaptiste Daroussin return; 30961d06d6bSBaptiste Daroussin } else 31061d06d6bSBaptiste Daroussin n = mdoc->last; 31161d06d6bSBaptiste Daroussin 31261d06d6bSBaptiste Daroussin for (;;) { 31361d06d6bSBaptiste Daroussin if ((n = n->parent) == NULL) 31461d06d6bSBaptiste Daroussin return; 31561d06d6bSBaptiste Daroussin 31661d06d6bSBaptiste Daroussin if (n->type == ROFFT_BLOCK || 31761d06d6bSBaptiste Daroussin n->type == ROFFT_HEAD) { 31861d06d6bSBaptiste Daroussin if (n->flags & NODE_ENDED) 31961d06d6bSBaptiste Daroussin break; 32061d06d6bSBaptiste Daroussin else 32161d06d6bSBaptiste Daroussin return; 32261d06d6bSBaptiste Daroussin } 32361d06d6bSBaptiste Daroussin } 32461d06d6bSBaptiste Daroussin } 32561d06d6bSBaptiste Daroussin } 32661d06d6bSBaptiste Daroussin 32761d06d6bSBaptiste Daroussin /* 32861d06d6bSBaptiste Daroussin * For a block closing macro, return the corresponding opening one. 32961d06d6bSBaptiste Daroussin * Otherwise, return the macro itself. 33061d06d6bSBaptiste Daroussin */ 33161d06d6bSBaptiste Daroussin static enum roff_tok 33261d06d6bSBaptiste Daroussin rew_alt(enum roff_tok tok) 33361d06d6bSBaptiste Daroussin { 33461d06d6bSBaptiste Daroussin switch (tok) { 33561d06d6bSBaptiste Daroussin case MDOC_Ac: 33661d06d6bSBaptiste Daroussin return MDOC_Ao; 33761d06d6bSBaptiste Daroussin case MDOC_Bc: 33861d06d6bSBaptiste Daroussin return MDOC_Bo; 33961d06d6bSBaptiste Daroussin case MDOC_Brc: 34061d06d6bSBaptiste Daroussin return MDOC_Bro; 34161d06d6bSBaptiste Daroussin case MDOC_Dc: 34261d06d6bSBaptiste Daroussin return MDOC_Do; 34361d06d6bSBaptiste Daroussin case MDOC_Ec: 34461d06d6bSBaptiste Daroussin return MDOC_Eo; 34561d06d6bSBaptiste Daroussin case MDOC_Ed: 34661d06d6bSBaptiste Daroussin return MDOC_Bd; 34761d06d6bSBaptiste Daroussin case MDOC_Ef: 34861d06d6bSBaptiste Daroussin return MDOC_Bf; 34961d06d6bSBaptiste Daroussin case MDOC_Ek: 35061d06d6bSBaptiste Daroussin return MDOC_Bk; 35161d06d6bSBaptiste Daroussin case MDOC_El: 35261d06d6bSBaptiste Daroussin return MDOC_Bl; 35361d06d6bSBaptiste Daroussin case MDOC_Fc: 35461d06d6bSBaptiste Daroussin return MDOC_Fo; 35561d06d6bSBaptiste Daroussin case MDOC_Oc: 35661d06d6bSBaptiste Daroussin return MDOC_Oo; 35761d06d6bSBaptiste Daroussin case MDOC_Pc: 35861d06d6bSBaptiste Daroussin return MDOC_Po; 35961d06d6bSBaptiste Daroussin case MDOC_Qc: 36061d06d6bSBaptiste Daroussin return MDOC_Qo; 36161d06d6bSBaptiste Daroussin case MDOC_Re: 36261d06d6bSBaptiste Daroussin return MDOC_Rs; 36361d06d6bSBaptiste Daroussin case MDOC_Sc: 36461d06d6bSBaptiste Daroussin return MDOC_So; 36561d06d6bSBaptiste Daroussin case MDOC_Xc: 36661d06d6bSBaptiste Daroussin return MDOC_Xo; 36761d06d6bSBaptiste Daroussin default: 36861d06d6bSBaptiste Daroussin return tok; 36961d06d6bSBaptiste Daroussin } 37061d06d6bSBaptiste Daroussin } 37161d06d6bSBaptiste Daroussin 37261d06d6bSBaptiste Daroussin static void 37361d06d6bSBaptiste Daroussin rew_elem(struct roff_man *mdoc, enum roff_tok tok) 37461d06d6bSBaptiste Daroussin { 37561d06d6bSBaptiste Daroussin struct roff_node *n; 37661d06d6bSBaptiste Daroussin 37761d06d6bSBaptiste Daroussin n = mdoc->last; 37861d06d6bSBaptiste Daroussin if (n->type != ROFFT_ELEM) 37961d06d6bSBaptiste Daroussin n = n->parent; 38061d06d6bSBaptiste Daroussin assert(n->type == ROFFT_ELEM); 38161d06d6bSBaptiste Daroussin assert(tok == n->tok); 38261d06d6bSBaptiste Daroussin rew_last(mdoc, n); 38361d06d6bSBaptiste Daroussin } 38461d06d6bSBaptiste Daroussin 38561d06d6bSBaptiste Daroussin static void 38661d06d6bSBaptiste Daroussin break_intermediate(struct roff_node *n, struct roff_node *breaker) 38761d06d6bSBaptiste Daroussin { 38861d06d6bSBaptiste Daroussin if (n != breaker && 38961d06d6bSBaptiste Daroussin n->type != ROFFT_BLOCK && n->type != ROFFT_HEAD && 39061d06d6bSBaptiste Daroussin (n->type != ROFFT_BODY || n->end != ENDBODY_NOT)) 39161d06d6bSBaptiste Daroussin n = n->parent; 39261d06d6bSBaptiste Daroussin while (n != breaker) { 39361d06d6bSBaptiste Daroussin if ( ! (n->flags & NODE_VALID)) 39461d06d6bSBaptiste Daroussin n->flags |= NODE_BROKEN; 39561d06d6bSBaptiste Daroussin n = n->parent; 39661d06d6bSBaptiste Daroussin } 39761d06d6bSBaptiste Daroussin } 39861d06d6bSBaptiste Daroussin 39961d06d6bSBaptiste Daroussin /* 40061d06d6bSBaptiste Daroussin * If there is an open sub-block of the target requiring 40161d06d6bSBaptiste Daroussin * explicit close-out, postpone closing out the target until 40261d06d6bSBaptiste Daroussin * the rew_pending() call closing out the sub-block. 40361d06d6bSBaptiste Daroussin */ 40461d06d6bSBaptiste Daroussin static int 40561d06d6bSBaptiste Daroussin find_pending(struct roff_man *mdoc, enum roff_tok tok, int line, int ppos, 40661d06d6bSBaptiste Daroussin struct roff_node *target) 40761d06d6bSBaptiste Daroussin { 40861d06d6bSBaptiste Daroussin struct roff_node *n; 40961d06d6bSBaptiste Daroussin int irc; 41061d06d6bSBaptiste Daroussin 41161d06d6bSBaptiste Daroussin if (target->flags & NODE_VALID) 41261d06d6bSBaptiste Daroussin return 0; 41361d06d6bSBaptiste Daroussin 41461d06d6bSBaptiste Daroussin irc = 0; 41561d06d6bSBaptiste Daroussin for (n = mdoc->last; n != NULL && n != target; n = n->parent) { 41661d06d6bSBaptiste Daroussin if (n->flags & NODE_ENDED) 41761d06d6bSBaptiste Daroussin continue; 41861d06d6bSBaptiste Daroussin if (n->type == ROFFT_BLOCK && 4197295610fSBaptiste Daroussin mdoc_macro(n->tok)->flags & MDOC_EXPLICIT) { 42061d06d6bSBaptiste Daroussin irc = 1; 42161d06d6bSBaptiste Daroussin break_intermediate(mdoc->last, target); 42261d06d6bSBaptiste Daroussin if (target->type == ROFFT_HEAD) 42361d06d6bSBaptiste Daroussin target->flags |= NODE_ENDED; 42461d06d6bSBaptiste Daroussin else if ( ! (target->flags & NODE_ENDED)) { 4257295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_NEST, 4267295610fSBaptiste Daroussin line, ppos, "%s breaks %s", 4277295610fSBaptiste Daroussin roff_name[tok], roff_name[n->tok]); 42861d06d6bSBaptiste Daroussin mdoc_endbody_alloc(mdoc, line, ppos, 42961d06d6bSBaptiste Daroussin tok, target); 43061d06d6bSBaptiste Daroussin } 43161d06d6bSBaptiste Daroussin } 43261d06d6bSBaptiste Daroussin } 43361d06d6bSBaptiste Daroussin return irc; 43461d06d6bSBaptiste Daroussin } 43561d06d6bSBaptiste Daroussin 43661d06d6bSBaptiste Daroussin /* 43761d06d6bSBaptiste Daroussin * Allocate a word and check whether it's punctuation or not. 43861d06d6bSBaptiste Daroussin * Punctuation consists of those tokens found in mdoc_isdelim(). 43961d06d6bSBaptiste Daroussin */ 44061d06d6bSBaptiste Daroussin static void 44161d06d6bSBaptiste Daroussin dword(struct roff_man *mdoc, int line, int col, const char *p, 44261d06d6bSBaptiste Daroussin enum mdelim d, int may_append) 44361d06d6bSBaptiste Daroussin { 44461d06d6bSBaptiste Daroussin 44561d06d6bSBaptiste Daroussin if (d == DELIM_MAX) 44661d06d6bSBaptiste Daroussin d = mdoc_isdelim(p); 44761d06d6bSBaptiste Daroussin 44861d06d6bSBaptiste Daroussin if (may_append && 44961d06d6bSBaptiste Daroussin ! (mdoc->flags & (MDOC_SYNOPSIS | MDOC_KEEP | MDOC_SMOFF)) && 45061d06d6bSBaptiste Daroussin d == DELIM_NONE && mdoc->last->type == ROFFT_TEXT && 45161d06d6bSBaptiste Daroussin mdoc_isdelim(mdoc->last->string) == DELIM_NONE) { 45261d06d6bSBaptiste Daroussin roff_word_append(mdoc, p); 45361d06d6bSBaptiste Daroussin return; 45461d06d6bSBaptiste Daroussin } 45561d06d6bSBaptiste Daroussin 45661d06d6bSBaptiste Daroussin roff_word_alloc(mdoc, line, col, p); 45761d06d6bSBaptiste Daroussin 45861d06d6bSBaptiste Daroussin /* 45961d06d6bSBaptiste Daroussin * If the word consists of a bare delimiter, 46061d06d6bSBaptiste Daroussin * flag the new node accordingly, 46161d06d6bSBaptiste Daroussin * unless doing so was vetoed by the invoking macro. 46261d06d6bSBaptiste Daroussin * Always clear the veto, it is only valid for one word. 46361d06d6bSBaptiste Daroussin */ 46461d06d6bSBaptiste Daroussin 46561d06d6bSBaptiste Daroussin if (d == DELIM_OPEN) 46661d06d6bSBaptiste Daroussin mdoc->last->flags |= NODE_DELIMO; 46761d06d6bSBaptiste Daroussin else if (d == DELIM_CLOSE && 46861d06d6bSBaptiste Daroussin ! (mdoc->flags & MDOC_NODELIMC) && 46961d06d6bSBaptiste Daroussin mdoc->last->parent->tok != MDOC_Fd) 47061d06d6bSBaptiste Daroussin mdoc->last->flags |= NODE_DELIMC; 47161d06d6bSBaptiste Daroussin mdoc->flags &= ~MDOC_NODELIMC; 47261d06d6bSBaptiste Daroussin } 47361d06d6bSBaptiste Daroussin 47461d06d6bSBaptiste Daroussin static void 47561d06d6bSBaptiste Daroussin append_delims(struct roff_man *mdoc, int line, int *pos, char *buf) 47661d06d6bSBaptiste Daroussin { 47761d06d6bSBaptiste Daroussin char *p; 47861d06d6bSBaptiste Daroussin int la; 4797295610fSBaptiste Daroussin enum margserr ac; 48061d06d6bSBaptiste Daroussin 48161d06d6bSBaptiste Daroussin if (buf[*pos] == '\0') 48261d06d6bSBaptiste Daroussin return; 48361d06d6bSBaptiste Daroussin 48461d06d6bSBaptiste Daroussin for (;;) { 48561d06d6bSBaptiste Daroussin la = *pos; 4867295610fSBaptiste Daroussin ac = mdoc_args(mdoc, line, pos, buf, TOKEN_NONE, &p); 4877295610fSBaptiste Daroussin if (ac == ARGS_EOLN) 48861d06d6bSBaptiste Daroussin break; 48961d06d6bSBaptiste Daroussin dword(mdoc, line, la, p, DELIM_MAX, 1); 49061d06d6bSBaptiste Daroussin 49161d06d6bSBaptiste Daroussin /* 49261d06d6bSBaptiste Daroussin * If we encounter end-of-sentence symbols, then trigger 49361d06d6bSBaptiste Daroussin * the double-space. 49461d06d6bSBaptiste Daroussin * 49561d06d6bSBaptiste Daroussin * XXX: it's easy to allow this to propagate outward to 49661d06d6bSBaptiste Daroussin * the last symbol, such that `. )' will cause the 49761d06d6bSBaptiste Daroussin * correct double-spacing. However, (1) groff isn't 49861d06d6bSBaptiste Daroussin * smart enough to do this and (2) it would require 49961d06d6bSBaptiste Daroussin * knowing which symbols break this behaviour, for 50061d06d6bSBaptiste Daroussin * example, `. ;' shouldn't propagate the double-space. 50161d06d6bSBaptiste Daroussin */ 50261d06d6bSBaptiste Daroussin 50361d06d6bSBaptiste Daroussin if (mandoc_eos(p, strlen(p))) 50461d06d6bSBaptiste Daroussin mdoc->last->flags |= NODE_EOS; 5057295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 5067295610fSBaptiste Daroussin free(p); 50761d06d6bSBaptiste Daroussin } 50861d06d6bSBaptiste Daroussin } 50961d06d6bSBaptiste Daroussin 51061d06d6bSBaptiste Daroussin /* 51161d06d6bSBaptiste Daroussin * Parse one word. 51261d06d6bSBaptiste Daroussin * If it is a macro, call it and return 1. 51361d06d6bSBaptiste Daroussin * Otherwise, allocate it and return 0. 51461d06d6bSBaptiste Daroussin */ 51561d06d6bSBaptiste Daroussin static int 5167295610fSBaptiste Daroussin macro_or_word(MACRO_PROT_ARGS, char *p, int parsed) 51761d06d6bSBaptiste Daroussin { 51861d06d6bSBaptiste Daroussin int ntok; 51961d06d6bSBaptiste Daroussin 5207295610fSBaptiste Daroussin ntok = buf[ppos] == '"' || parsed == 0 || 5217295610fSBaptiste Daroussin mdoc->flags & MDOC_PHRASELIT ? TOKEN_NONE : 5227295610fSBaptiste Daroussin lookup(mdoc, tok, line, ppos, p); 52361d06d6bSBaptiste Daroussin 52461d06d6bSBaptiste Daroussin if (ntok == TOKEN_NONE) { 52561d06d6bSBaptiste Daroussin dword(mdoc, line, ppos, p, DELIM_MAX, tok == TOKEN_NONE || 5267295610fSBaptiste Daroussin mdoc_macro(tok)->flags & MDOC_JOIN); 52761d06d6bSBaptiste Daroussin return 0; 52861d06d6bSBaptiste Daroussin } else { 52961d06d6bSBaptiste Daroussin if (tok != TOKEN_NONE && 5307295610fSBaptiste Daroussin mdoc_macro(tok)->fp == in_line_eoln) 53161d06d6bSBaptiste Daroussin rew_elem(mdoc, tok); 5327295610fSBaptiste Daroussin (*mdoc_macro(ntok)->fp)(mdoc, ntok, line, ppos, pos, buf); 53361d06d6bSBaptiste Daroussin if (tok == TOKEN_NONE) 53461d06d6bSBaptiste Daroussin append_delims(mdoc, line, pos, buf); 53561d06d6bSBaptiste Daroussin return 1; 53661d06d6bSBaptiste Daroussin } 53761d06d6bSBaptiste Daroussin } 53861d06d6bSBaptiste Daroussin 53961d06d6bSBaptiste Daroussin /* 54061d06d6bSBaptiste Daroussin * Close out block partial/full explicit. 54161d06d6bSBaptiste Daroussin */ 54261d06d6bSBaptiste Daroussin static void 54361d06d6bSBaptiste Daroussin blk_exp_close(MACRO_PROT_ARGS) 54461d06d6bSBaptiste Daroussin { 54561d06d6bSBaptiste Daroussin struct roff_node *body; /* Our own body. */ 54661d06d6bSBaptiste Daroussin struct roff_node *endbody; /* Our own end marker. */ 54761d06d6bSBaptiste Daroussin struct roff_node *itblk; /* An It block starting later. */ 54861d06d6bSBaptiste Daroussin struct roff_node *later; /* A sub-block starting later. */ 54961d06d6bSBaptiste Daroussin struct roff_node *n; /* Search back to our block. */ 55061d06d6bSBaptiste Daroussin struct roff_node *target; /* For find_pending(). */ 55161d06d6bSBaptiste Daroussin 55261d06d6bSBaptiste Daroussin int j, lastarg, maxargs, nl, pending; 55361d06d6bSBaptiste Daroussin enum margserr ac; 55461d06d6bSBaptiste Daroussin enum roff_tok atok, ntok; 55561d06d6bSBaptiste Daroussin char *p; 55661d06d6bSBaptiste Daroussin 55761d06d6bSBaptiste Daroussin nl = MDOC_NEWLINE & mdoc->flags; 55861d06d6bSBaptiste Daroussin 55961d06d6bSBaptiste Daroussin switch (tok) { 56061d06d6bSBaptiste Daroussin case MDOC_Ec: 56161d06d6bSBaptiste Daroussin maxargs = 1; 56261d06d6bSBaptiste Daroussin break; 56361d06d6bSBaptiste Daroussin case MDOC_Ek: 56461d06d6bSBaptiste Daroussin mdoc->flags &= ~MDOC_KEEP; 56561d06d6bSBaptiste Daroussin /* FALLTHROUGH */ 56661d06d6bSBaptiste Daroussin default: 56761d06d6bSBaptiste Daroussin maxargs = 0; 56861d06d6bSBaptiste Daroussin break; 56961d06d6bSBaptiste Daroussin } 57061d06d6bSBaptiste Daroussin 57161d06d6bSBaptiste Daroussin /* Search backwards for the beginning of our own body. */ 57261d06d6bSBaptiste Daroussin 57361d06d6bSBaptiste Daroussin atok = rew_alt(tok); 57461d06d6bSBaptiste Daroussin body = NULL; 57561d06d6bSBaptiste Daroussin for (n = mdoc->last; n; n = n->parent) { 57661d06d6bSBaptiste Daroussin if (n->flags & NODE_ENDED || n->tok != atok || 57761d06d6bSBaptiste Daroussin n->type != ROFFT_BODY || n->end != ENDBODY_NOT) 57861d06d6bSBaptiste Daroussin continue; 57961d06d6bSBaptiste Daroussin body = n; 58061d06d6bSBaptiste Daroussin break; 58161d06d6bSBaptiste Daroussin } 58261d06d6bSBaptiste Daroussin 58361d06d6bSBaptiste Daroussin /* 58461d06d6bSBaptiste Daroussin * Search backwards for beginnings of blocks, 58561d06d6bSBaptiste Daroussin * both of our own and of pending sub-blocks. 58661d06d6bSBaptiste Daroussin */ 58761d06d6bSBaptiste Daroussin 58861d06d6bSBaptiste Daroussin endbody = itblk = later = NULL; 58961d06d6bSBaptiste Daroussin for (n = mdoc->last; n; n = n->parent) { 59061d06d6bSBaptiste Daroussin if (n->flags & NODE_ENDED) 59161d06d6bSBaptiste Daroussin continue; 59261d06d6bSBaptiste Daroussin 59361d06d6bSBaptiste Daroussin /* 59461d06d6bSBaptiste Daroussin * Mismatching end macros can never break anything 59561d06d6bSBaptiste Daroussin * and we only care about the breaking of BLOCKs. 59661d06d6bSBaptiste Daroussin */ 59761d06d6bSBaptiste Daroussin 59861d06d6bSBaptiste Daroussin if (body == NULL || n->type != ROFFT_BLOCK) 59961d06d6bSBaptiste Daroussin continue; 60061d06d6bSBaptiste Daroussin 60161d06d6bSBaptiste Daroussin /* 60261d06d6bSBaptiste Daroussin * SYNOPSIS name blocks can not be broken themselves, 60361d06d6bSBaptiste Daroussin * but they do get broken together with a broken child. 60461d06d6bSBaptiste Daroussin */ 60561d06d6bSBaptiste Daroussin 60661d06d6bSBaptiste Daroussin if (n->tok == MDOC_Nm) { 60761d06d6bSBaptiste Daroussin if (later != NULL) 60861d06d6bSBaptiste Daroussin n->flags |= NODE_BROKEN | NODE_ENDED; 60961d06d6bSBaptiste Daroussin continue; 61061d06d6bSBaptiste Daroussin } 61161d06d6bSBaptiste Daroussin 61261d06d6bSBaptiste Daroussin if (n->tok == MDOC_It) { 61361d06d6bSBaptiste Daroussin itblk = n; 61461d06d6bSBaptiste Daroussin continue; 61561d06d6bSBaptiste Daroussin } 61661d06d6bSBaptiste Daroussin 61761d06d6bSBaptiste Daroussin if (atok == n->tok) { 61861d06d6bSBaptiste Daroussin 61961d06d6bSBaptiste Daroussin /* 62061d06d6bSBaptiste Daroussin * Found the start of our own block. 62161d06d6bSBaptiste Daroussin * When there is no pending sub block, 62261d06d6bSBaptiste Daroussin * just proceed to closing out. 62361d06d6bSBaptiste Daroussin */ 62461d06d6bSBaptiste Daroussin 62561d06d6bSBaptiste Daroussin if (later == NULL || 62661d06d6bSBaptiste Daroussin (tok == MDOC_El && itblk == NULL)) 62761d06d6bSBaptiste Daroussin break; 62861d06d6bSBaptiste Daroussin 62961d06d6bSBaptiste Daroussin /* 63061d06d6bSBaptiste Daroussin * When there is a pending sub block, postpone 63161d06d6bSBaptiste Daroussin * closing out the current block until the 63261d06d6bSBaptiste Daroussin * rew_pending() closing out the sub-block. 63361d06d6bSBaptiste Daroussin * Mark the place where the formatting - but not 63461d06d6bSBaptiste Daroussin * the scope - of the current block ends. 63561d06d6bSBaptiste Daroussin */ 63661d06d6bSBaptiste Daroussin 6377295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_NEST, 63861d06d6bSBaptiste Daroussin line, ppos, "%s breaks %s", 63961d06d6bSBaptiste Daroussin roff_name[atok], roff_name[later->tok]); 64061d06d6bSBaptiste Daroussin 64161d06d6bSBaptiste Daroussin endbody = mdoc_endbody_alloc(mdoc, line, ppos, 64261d06d6bSBaptiste Daroussin atok, body); 64361d06d6bSBaptiste Daroussin 64461d06d6bSBaptiste Daroussin if (tok == MDOC_El) 64561d06d6bSBaptiste Daroussin itblk->flags |= NODE_ENDED | NODE_BROKEN; 64661d06d6bSBaptiste Daroussin 64761d06d6bSBaptiste Daroussin /* 64861d06d6bSBaptiste Daroussin * If a block closing macro taking arguments 64961d06d6bSBaptiste Daroussin * breaks another block, put the arguments 65061d06d6bSBaptiste Daroussin * into the end marker. 65161d06d6bSBaptiste Daroussin */ 65261d06d6bSBaptiste Daroussin 65361d06d6bSBaptiste Daroussin if (maxargs) 65461d06d6bSBaptiste Daroussin mdoc->next = ROFF_NEXT_CHILD; 65561d06d6bSBaptiste Daroussin break; 65661d06d6bSBaptiste Daroussin } 65761d06d6bSBaptiste Daroussin 65861d06d6bSBaptiste Daroussin /* 65961d06d6bSBaptiste Daroussin * Explicit blocks close out description lines, but 66061d06d6bSBaptiste Daroussin * even those can get broken together with a child. 66161d06d6bSBaptiste Daroussin */ 66261d06d6bSBaptiste Daroussin 66361d06d6bSBaptiste Daroussin if (n->tok == MDOC_Nd) { 66461d06d6bSBaptiste Daroussin if (later != NULL) 66561d06d6bSBaptiste Daroussin n->flags |= NODE_BROKEN | NODE_ENDED; 66661d06d6bSBaptiste Daroussin else 66761d06d6bSBaptiste Daroussin rew_last(mdoc, n); 66861d06d6bSBaptiste Daroussin continue; 66961d06d6bSBaptiste Daroussin } 67061d06d6bSBaptiste Daroussin 67161d06d6bSBaptiste Daroussin /* Breaking an open sub block. */ 67261d06d6bSBaptiste Daroussin 67361d06d6bSBaptiste Daroussin break_intermediate(mdoc->last, body); 67461d06d6bSBaptiste Daroussin n->flags |= NODE_BROKEN; 67561d06d6bSBaptiste Daroussin if (later == NULL) 67661d06d6bSBaptiste Daroussin later = n; 67761d06d6bSBaptiste Daroussin } 67861d06d6bSBaptiste Daroussin 67961d06d6bSBaptiste Daroussin if (body == NULL) { 6807295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_NOTOPEN, line, ppos, 6817295610fSBaptiste Daroussin "%s", roff_name[tok]); 68261d06d6bSBaptiste Daroussin if (maxargs && endbody == NULL) { 68361d06d6bSBaptiste Daroussin /* 68461d06d6bSBaptiste Daroussin * Stray .Ec without previous .Eo: 68561d06d6bSBaptiste Daroussin * Break the output line, keep the arguments. 68661d06d6bSBaptiste Daroussin */ 68761d06d6bSBaptiste Daroussin roff_elem_alloc(mdoc, line, ppos, ROFF_br); 68861d06d6bSBaptiste Daroussin rew_elem(mdoc, ROFF_br); 68961d06d6bSBaptiste Daroussin } 69061d06d6bSBaptiste Daroussin } else if (endbody == NULL) { 69161d06d6bSBaptiste Daroussin rew_last(mdoc, body); 69261d06d6bSBaptiste Daroussin if (maxargs) 69361d06d6bSBaptiste Daroussin mdoc_tail_alloc(mdoc, line, ppos, atok); 69461d06d6bSBaptiste Daroussin } 69561d06d6bSBaptiste Daroussin 6967295610fSBaptiste Daroussin if ((mdoc_macro(tok)->flags & MDOC_PARSED) == 0) { 69761d06d6bSBaptiste Daroussin if (buf[*pos] != '\0') 6987295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ARG_SKIP, line, ppos, 6997295610fSBaptiste Daroussin "%s %s", roff_name[tok], buf + *pos); 70061d06d6bSBaptiste Daroussin if (endbody == NULL && n != NULL) 70161d06d6bSBaptiste Daroussin rew_pending(mdoc, n); 7027295610fSBaptiste Daroussin 7037295610fSBaptiste Daroussin /* 7047295610fSBaptiste Daroussin * Restore the fill mode that was set before the display. 7057295610fSBaptiste Daroussin * This needs to be done here rather than during validation 7067295610fSBaptiste Daroussin * such that subsequent nodes get the right flags. 7077295610fSBaptiste Daroussin */ 7087295610fSBaptiste Daroussin 7097295610fSBaptiste Daroussin if (tok == MDOC_Ed && body != NULL) { 7107295610fSBaptiste Daroussin if (body->flags & NODE_NOFILL) 7117295610fSBaptiste Daroussin mdoc->flags |= ROFF_NOFILL; 7127295610fSBaptiste Daroussin else 7137295610fSBaptiste Daroussin mdoc->flags &= ~ROFF_NOFILL; 7147295610fSBaptiste Daroussin } 71561d06d6bSBaptiste Daroussin return; 71661d06d6bSBaptiste Daroussin } 71761d06d6bSBaptiste Daroussin 71861d06d6bSBaptiste Daroussin if (endbody != NULL) 71961d06d6bSBaptiste Daroussin n = endbody; 72061d06d6bSBaptiste Daroussin 72161d06d6bSBaptiste Daroussin ntok = TOKEN_NONE; 72261d06d6bSBaptiste Daroussin for (j = 0; ; j++) { 72361d06d6bSBaptiste Daroussin lastarg = *pos; 72461d06d6bSBaptiste Daroussin 72561d06d6bSBaptiste Daroussin if (j == maxargs && n != NULL) 72661d06d6bSBaptiste Daroussin rew_last(mdoc, n); 72761d06d6bSBaptiste Daroussin 72861d06d6bSBaptiste Daroussin ac = mdoc_args(mdoc, line, pos, buf, tok, &p); 72961d06d6bSBaptiste Daroussin if (ac == ARGS_PUNCT || ac == ARGS_EOLN) 73061d06d6bSBaptiste Daroussin break; 73161d06d6bSBaptiste Daroussin 73261d06d6bSBaptiste Daroussin ntok = lookup(mdoc, tok, line, lastarg, p); 73361d06d6bSBaptiste Daroussin 73461d06d6bSBaptiste Daroussin if (ntok == TOKEN_NONE) { 73561d06d6bSBaptiste Daroussin dword(mdoc, line, lastarg, p, DELIM_MAX, 7367295610fSBaptiste Daroussin mdoc_macro(tok)->flags & MDOC_JOIN); 7377295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 7387295610fSBaptiste Daroussin free(p); 73961d06d6bSBaptiste Daroussin continue; 74061d06d6bSBaptiste Daroussin } 7417295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 7427295610fSBaptiste Daroussin free(p); 74361d06d6bSBaptiste Daroussin 74461d06d6bSBaptiste Daroussin if (n != NULL) 74561d06d6bSBaptiste Daroussin rew_last(mdoc, n); 74661d06d6bSBaptiste Daroussin mdoc->flags &= ~MDOC_NEWLINE; 7477295610fSBaptiste Daroussin (*mdoc_macro(ntok)->fp)(mdoc, ntok, line, lastarg, pos, buf); 74861d06d6bSBaptiste Daroussin break; 74961d06d6bSBaptiste Daroussin } 75061d06d6bSBaptiste Daroussin 75161d06d6bSBaptiste Daroussin if (n != NULL) { 75261d06d6bSBaptiste Daroussin pending = 0; 75361d06d6bSBaptiste Daroussin if (ntok != TOKEN_NONE && n->flags & NODE_BROKEN) { 75461d06d6bSBaptiste Daroussin target = n; 75561d06d6bSBaptiste Daroussin do 75661d06d6bSBaptiste Daroussin target = target->parent; 75761d06d6bSBaptiste Daroussin while ( ! (target->flags & NODE_ENDED)); 75861d06d6bSBaptiste Daroussin pending = find_pending(mdoc, ntok, line, ppos, target); 75961d06d6bSBaptiste Daroussin } 76061d06d6bSBaptiste Daroussin if ( ! pending) 76161d06d6bSBaptiste Daroussin rew_pending(mdoc, n); 76261d06d6bSBaptiste Daroussin } 76361d06d6bSBaptiste Daroussin if (nl) 76461d06d6bSBaptiste Daroussin append_delims(mdoc, line, pos, buf); 76561d06d6bSBaptiste Daroussin } 76661d06d6bSBaptiste Daroussin 76761d06d6bSBaptiste Daroussin static void 76861d06d6bSBaptiste Daroussin in_line(MACRO_PROT_ARGS) 76961d06d6bSBaptiste Daroussin { 77061d06d6bSBaptiste Daroussin int la, scope, cnt, firstarg, mayopen, nc, nl; 77161d06d6bSBaptiste Daroussin enum roff_tok ntok; 77261d06d6bSBaptiste Daroussin enum margserr ac; 77361d06d6bSBaptiste Daroussin enum mdelim d; 77461d06d6bSBaptiste Daroussin struct mdoc_arg *arg; 77561d06d6bSBaptiste Daroussin char *p; 77661d06d6bSBaptiste Daroussin 77761d06d6bSBaptiste Daroussin nl = MDOC_NEWLINE & mdoc->flags; 77861d06d6bSBaptiste Daroussin 77961d06d6bSBaptiste Daroussin /* 78061d06d6bSBaptiste Daroussin * Whether we allow ignored elements (those without content, 78161d06d6bSBaptiste Daroussin * usually because of reserved words) to squeak by. 78261d06d6bSBaptiste Daroussin */ 78361d06d6bSBaptiste Daroussin 78461d06d6bSBaptiste Daroussin switch (tok) { 78561d06d6bSBaptiste Daroussin case MDOC_An: 78661d06d6bSBaptiste Daroussin case MDOC_Ar: 78761d06d6bSBaptiste Daroussin case MDOC_Fl: 78861d06d6bSBaptiste Daroussin case MDOC_Mt: 78961d06d6bSBaptiste Daroussin case MDOC_Nm: 79061d06d6bSBaptiste Daroussin case MDOC_Pa: 79161d06d6bSBaptiste Daroussin nc = 1; 79261d06d6bSBaptiste Daroussin break; 79361d06d6bSBaptiste Daroussin default: 79461d06d6bSBaptiste Daroussin nc = 0; 79561d06d6bSBaptiste Daroussin break; 79661d06d6bSBaptiste Daroussin } 79761d06d6bSBaptiste Daroussin 79861d06d6bSBaptiste Daroussin mdoc_argv(mdoc, line, tok, &arg, pos, buf); 79961d06d6bSBaptiste Daroussin 80061d06d6bSBaptiste Daroussin d = DELIM_NONE; 80161d06d6bSBaptiste Daroussin firstarg = 1; 80261d06d6bSBaptiste Daroussin mayopen = 1; 80361d06d6bSBaptiste Daroussin for (cnt = scope = 0;; ) { 80461d06d6bSBaptiste Daroussin la = *pos; 80561d06d6bSBaptiste Daroussin ac = mdoc_args(mdoc, line, pos, buf, tok, &p); 80661d06d6bSBaptiste Daroussin 80761d06d6bSBaptiste Daroussin /* 80861d06d6bSBaptiste Daroussin * At the end of a macro line, 80961d06d6bSBaptiste Daroussin * opening delimiters do not suppress spacing. 81061d06d6bSBaptiste Daroussin */ 81161d06d6bSBaptiste Daroussin 81261d06d6bSBaptiste Daroussin if (ac == ARGS_EOLN) { 81361d06d6bSBaptiste Daroussin if (d == DELIM_OPEN) 81461d06d6bSBaptiste Daroussin mdoc->last->flags &= ~NODE_DELIMO; 81561d06d6bSBaptiste Daroussin break; 81661d06d6bSBaptiste Daroussin } 81761d06d6bSBaptiste Daroussin 81861d06d6bSBaptiste Daroussin /* 81961d06d6bSBaptiste Daroussin * The rest of the macro line is only punctuation, 82061d06d6bSBaptiste Daroussin * to be handled by append_delims(). 82161d06d6bSBaptiste Daroussin * If there were no other arguments, 82261d06d6bSBaptiste Daroussin * do not allow the first one to suppress spacing, 82361d06d6bSBaptiste Daroussin * even if it turns out to be a closing one. 82461d06d6bSBaptiste Daroussin */ 82561d06d6bSBaptiste Daroussin 82661d06d6bSBaptiste Daroussin if (ac == ARGS_PUNCT) { 82761d06d6bSBaptiste Daroussin if (cnt == 0 && (nc == 0 || tok == MDOC_An)) 82861d06d6bSBaptiste Daroussin mdoc->flags |= MDOC_NODELIMC; 82961d06d6bSBaptiste Daroussin break; 83061d06d6bSBaptiste Daroussin } 83161d06d6bSBaptiste Daroussin 83261d06d6bSBaptiste Daroussin ntok = (tok == MDOC_Fn && !cnt) ? 83361d06d6bSBaptiste Daroussin TOKEN_NONE : lookup(mdoc, tok, line, la, p); 83461d06d6bSBaptiste Daroussin 83561d06d6bSBaptiste Daroussin /* 83661d06d6bSBaptiste Daroussin * In this case, we've located a submacro and must 83761d06d6bSBaptiste Daroussin * execute it. Close out scope, if open. If no 83861d06d6bSBaptiste Daroussin * elements have been generated, either create one (nc) 83961d06d6bSBaptiste Daroussin * or raise a warning. 84061d06d6bSBaptiste Daroussin */ 84161d06d6bSBaptiste Daroussin 84261d06d6bSBaptiste Daroussin if (ntok != TOKEN_NONE) { 84361d06d6bSBaptiste Daroussin if (scope) 84461d06d6bSBaptiste Daroussin rew_elem(mdoc, tok); 84561d06d6bSBaptiste Daroussin if (nc && ! cnt) { 84661d06d6bSBaptiste Daroussin mdoc_elem_alloc(mdoc, line, ppos, tok, arg); 84761d06d6bSBaptiste Daroussin rew_last(mdoc, mdoc->last); 84861d06d6bSBaptiste Daroussin } else if ( ! nc && ! cnt) { 84961d06d6bSBaptiste Daroussin mdoc_argv_free(arg); 85061d06d6bSBaptiste Daroussin mandoc_msg(MANDOCERR_MACRO_EMPTY, 8517295610fSBaptiste Daroussin line, ppos, "%s", roff_name[tok]); 85261d06d6bSBaptiste Daroussin } 8537295610fSBaptiste Daroussin (*mdoc_macro(ntok)->fp)(mdoc, ntok, 8547295610fSBaptiste Daroussin line, la, pos, buf); 85561d06d6bSBaptiste Daroussin if (nl) 85661d06d6bSBaptiste Daroussin append_delims(mdoc, line, pos, buf); 8577295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 8587295610fSBaptiste Daroussin free(p); 85961d06d6bSBaptiste Daroussin return; 86061d06d6bSBaptiste Daroussin } 86161d06d6bSBaptiste Daroussin 86261d06d6bSBaptiste Daroussin /* 86361d06d6bSBaptiste Daroussin * Handle punctuation. Set up our scope, if a word; 86461d06d6bSBaptiste Daroussin * rewind the scope, if a delimiter; then append the word. 86561d06d6bSBaptiste Daroussin */ 86661d06d6bSBaptiste Daroussin 86761d06d6bSBaptiste Daroussin if ((d = mdoc_isdelim(p)) != DELIM_NONE) { 86861d06d6bSBaptiste Daroussin /* 86961d06d6bSBaptiste Daroussin * If we encounter closing punctuation, no word 87061d06d6bSBaptiste Daroussin * has been emitted, no scope is open, and we're 87161d06d6bSBaptiste Daroussin * allowed to have an empty element, then start 87261d06d6bSBaptiste Daroussin * a new scope. 87361d06d6bSBaptiste Daroussin */ 87461d06d6bSBaptiste Daroussin if ((d == DELIM_CLOSE || 87561d06d6bSBaptiste Daroussin (d == DELIM_MIDDLE && tok == MDOC_Fl)) && 87661d06d6bSBaptiste Daroussin !cnt && !scope && nc && mayopen) { 87761d06d6bSBaptiste Daroussin mdoc_elem_alloc(mdoc, line, ppos, tok, arg); 87861d06d6bSBaptiste Daroussin scope = 1; 87961d06d6bSBaptiste Daroussin cnt++; 88061d06d6bSBaptiste Daroussin if (tok == MDOC_Nm) 88161d06d6bSBaptiste Daroussin mayopen = 0; 88261d06d6bSBaptiste Daroussin } 88361d06d6bSBaptiste Daroussin /* 88461d06d6bSBaptiste Daroussin * Close out our scope, if one is open, before 88561d06d6bSBaptiste Daroussin * any punctuation. 88661d06d6bSBaptiste Daroussin */ 88761d06d6bSBaptiste Daroussin if (scope && tok != MDOC_Lk) { 88861d06d6bSBaptiste Daroussin rew_elem(mdoc, tok); 88961d06d6bSBaptiste Daroussin scope = 0; 89061d06d6bSBaptiste Daroussin if (tok == MDOC_Fn) 89161d06d6bSBaptiste Daroussin mayopen = 0; 89261d06d6bSBaptiste Daroussin } 89361d06d6bSBaptiste Daroussin } else if (mayopen && !scope) { 89461d06d6bSBaptiste Daroussin mdoc_elem_alloc(mdoc, line, ppos, tok, arg); 89561d06d6bSBaptiste Daroussin scope = 1; 89661d06d6bSBaptiste Daroussin cnt++; 89761d06d6bSBaptiste Daroussin } 89861d06d6bSBaptiste Daroussin 89961d06d6bSBaptiste Daroussin dword(mdoc, line, la, p, d, 9007295610fSBaptiste Daroussin mdoc_macro(tok)->flags & MDOC_JOIN); 9017295610fSBaptiste Daroussin 9027295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 9037295610fSBaptiste Daroussin free(p); 90461d06d6bSBaptiste Daroussin 90561d06d6bSBaptiste Daroussin /* 90661d06d6bSBaptiste Daroussin * If the first argument is a closing delimiter, 90761d06d6bSBaptiste Daroussin * do not suppress spacing before it. 90861d06d6bSBaptiste Daroussin */ 90961d06d6bSBaptiste Daroussin 91061d06d6bSBaptiste Daroussin if (firstarg && d == DELIM_CLOSE && !nc) 91161d06d6bSBaptiste Daroussin mdoc->last->flags &= ~NODE_DELIMC; 91261d06d6bSBaptiste Daroussin firstarg = 0; 91361d06d6bSBaptiste Daroussin 91461d06d6bSBaptiste Daroussin /* 91561d06d6bSBaptiste Daroussin * `Fl' macros have their scope re-opened with each new 91661d06d6bSBaptiste Daroussin * word so that the `-' can be added to each one without 91761d06d6bSBaptiste Daroussin * having to parse out spaces. 91861d06d6bSBaptiste Daroussin */ 91961d06d6bSBaptiste Daroussin if (scope && tok == MDOC_Fl) { 92061d06d6bSBaptiste Daroussin rew_elem(mdoc, tok); 92161d06d6bSBaptiste Daroussin scope = 0; 92261d06d6bSBaptiste Daroussin } 92361d06d6bSBaptiste Daroussin } 92461d06d6bSBaptiste Daroussin 92561d06d6bSBaptiste Daroussin if (scope && tok != MDOC_Lk) { 92661d06d6bSBaptiste Daroussin rew_elem(mdoc, tok); 92761d06d6bSBaptiste Daroussin scope = 0; 92861d06d6bSBaptiste Daroussin } 92961d06d6bSBaptiste Daroussin 93061d06d6bSBaptiste Daroussin /* 93161d06d6bSBaptiste Daroussin * If no elements have been collected and we're allowed to have 93261d06d6bSBaptiste Daroussin * empties (nc), open a scope and close it out. Otherwise, 93361d06d6bSBaptiste Daroussin * raise a warning. 93461d06d6bSBaptiste Daroussin */ 93561d06d6bSBaptiste Daroussin 93661d06d6bSBaptiste Daroussin if ( ! cnt) { 93761d06d6bSBaptiste Daroussin if (nc) { 93861d06d6bSBaptiste Daroussin mdoc_elem_alloc(mdoc, line, ppos, tok, arg); 93961d06d6bSBaptiste Daroussin rew_last(mdoc, mdoc->last); 94061d06d6bSBaptiste Daroussin } else { 94161d06d6bSBaptiste Daroussin mdoc_argv_free(arg); 9427295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_MACRO_EMPTY, 9437295610fSBaptiste Daroussin line, ppos, "%s", roff_name[tok]); 94461d06d6bSBaptiste Daroussin } 94561d06d6bSBaptiste Daroussin } 94661d06d6bSBaptiste Daroussin if (nl) 94761d06d6bSBaptiste Daroussin append_delims(mdoc, line, pos, buf); 94861d06d6bSBaptiste Daroussin if (scope) 94961d06d6bSBaptiste Daroussin rew_elem(mdoc, tok); 95061d06d6bSBaptiste Daroussin } 95161d06d6bSBaptiste Daroussin 95261d06d6bSBaptiste Daroussin static void 95361d06d6bSBaptiste Daroussin blk_full(MACRO_PROT_ARGS) 95461d06d6bSBaptiste Daroussin { 95561d06d6bSBaptiste Daroussin struct mdoc_arg *arg; 95661d06d6bSBaptiste Daroussin struct roff_node *blk; /* Our own or a broken block. */ 95761d06d6bSBaptiste Daroussin struct roff_node *head; /* Our own head. */ 95861d06d6bSBaptiste Daroussin struct roff_node *body; /* Our own body. */ 95961d06d6bSBaptiste Daroussin struct roff_node *n; 96061d06d6bSBaptiste Daroussin char *p; 9617295610fSBaptiste Daroussin size_t iarg; 9627295610fSBaptiste Daroussin int done, la, nl, parsed; 9637295610fSBaptiste Daroussin enum margserr ac, lac; 96461d06d6bSBaptiste Daroussin 96561d06d6bSBaptiste Daroussin nl = MDOC_NEWLINE & mdoc->flags; 96661d06d6bSBaptiste Daroussin 96761d06d6bSBaptiste Daroussin if (buf[*pos] == '\0' && (tok == MDOC_Sh || tok == MDOC_Ss)) { 9687295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_MACRO_EMPTY, 9697295610fSBaptiste Daroussin line, ppos, "%s", roff_name[tok]); 97061d06d6bSBaptiste Daroussin return; 97161d06d6bSBaptiste Daroussin } 97261d06d6bSBaptiste Daroussin 9737295610fSBaptiste Daroussin if ((mdoc_macro(tok)->flags & MDOC_EXPLICIT) == 0) { 97461d06d6bSBaptiste Daroussin 97561d06d6bSBaptiste Daroussin /* Here, tok is one of Sh Ss Nm Nd It. */ 97661d06d6bSBaptiste Daroussin 97761d06d6bSBaptiste Daroussin blk = NULL; 97861d06d6bSBaptiste Daroussin for (n = mdoc->last; n != NULL; n = n->parent) { 97961d06d6bSBaptiste Daroussin if (n->flags & NODE_ENDED) { 98061d06d6bSBaptiste Daroussin if ( ! (n->flags & NODE_VALID)) 98161d06d6bSBaptiste Daroussin n->flags |= NODE_BROKEN; 98261d06d6bSBaptiste Daroussin continue; 98361d06d6bSBaptiste Daroussin } 98461d06d6bSBaptiste Daroussin if (n->type != ROFFT_BLOCK) 98561d06d6bSBaptiste Daroussin continue; 98661d06d6bSBaptiste Daroussin 98761d06d6bSBaptiste Daroussin if (tok == MDOC_It && n->tok == MDOC_Bl) { 98861d06d6bSBaptiste Daroussin if (blk != NULL) { 9897295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_BROKEN, 9907295610fSBaptiste Daroussin line, ppos, "It breaks %s", 99161d06d6bSBaptiste Daroussin roff_name[blk->tok]); 99261d06d6bSBaptiste Daroussin rew_pending(mdoc, blk); 99361d06d6bSBaptiste Daroussin } 99461d06d6bSBaptiste Daroussin break; 99561d06d6bSBaptiste Daroussin } 99661d06d6bSBaptiste Daroussin 9977295610fSBaptiste Daroussin if (mdoc_macro(n->tok)->flags & MDOC_EXPLICIT) { 99861d06d6bSBaptiste Daroussin switch (tok) { 99961d06d6bSBaptiste Daroussin case MDOC_Sh: 100061d06d6bSBaptiste Daroussin case MDOC_Ss: 10017295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_BROKEN, 10027295610fSBaptiste Daroussin line, ppos, 100361d06d6bSBaptiste Daroussin "%s breaks %s", roff_name[tok], 100461d06d6bSBaptiste Daroussin roff_name[n->tok]); 100561d06d6bSBaptiste Daroussin rew_pending(mdoc, n); 100661d06d6bSBaptiste Daroussin n = mdoc->last; 100761d06d6bSBaptiste Daroussin continue; 100861d06d6bSBaptiste Daroussin case MDOC_It: 100961d06d6bSBaptiste Daroussin /* Delay in case it's astray. */ 101061d06d6bSBaptiste Daroussin blk = n; 101161d06d6bSBaptiste Daroussin continue; 101261d06d6bSBaptiste Daroussin default: 101361d06d6bSBaptiste Daroussin break; 101461d06d6bSBaptiste Daroussin } 101561d06d6bSBaptiste Daroussin break; 101661d06d6bSBaptiste Daroussin } 101761d06d6bSBaptiste Daroussin 101861d06d6bSBaptiste Daroussin /* Here, n is one of Sh Ss Nm Nd It. */ 101961d06d6bSBaptiste Daroussin 102061d06d6bSBaptiste Daroussin if (tok != MDOC_Sh && (n->tok == MDOC_Sh || 102161d06d6bSBaptiste Daroussin (tok != MDOC_Ss && (n->tok == MDOC_Ss || 102261d06d6bSBaptiste Daroussin (tok != MDOC_It && n->tok == MDOC_It))))) 102361d06d6bSBaptiste Daroussin break; 102461d06d6bSBaptiste Daroussin 102561d06d6bSBaptiste Daroussin /* Item breaking an explicit block. */ 102661d06d6bSBaptiste Daroussin 102761d06d6bSBaptiste Daroussin if (blk != NULL) { 10287295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_BROKEN, line, ppos, 102961d06d6bSBaptiste Daroussin "It breaks %s", roff_name[blk->tok]); 103061d06d6bSBaptiste Daroussin rew_pending(mdoc, blk); 103161d06d6bSBaptiste Daroussin blk = NULL; 103261d06d6bSBaptiste Daroussin } 103361d06d6bSBaptiste Daroussin 103461d06d6bSBaptiste Daroussin /* Close out prior implicit scopes. */ 103561d06d6bSBaptiste Daroussin 103661d06d6bSBaptiste Daroussin rew_pending(mdoc, n); 103761d06d6bSBaptiste Daroussin } 103861d06d6bSBaptiste Daroussin 103961d06d6bSBaptiste Daroussin /* Skip items outside lists. */ 104061d06d6bSBaptiste Daroussin 104161d06d6bSBaptiste Daroussin if (tok == MDOC_It && (n == NULL || n->tok != MDOC_Bl)) { 10427295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_IT_STRAY, 104361d06d6bSBaptiste Daroussin line, ppos, "It %s", buf + *pos); 104461d06d6bSBaptiste Daroussin roff_elem_alloc(mdoc, line, ppos, ROFF_br); 104561d06d6bSBaptiste Daroussin rew_elem(mdoc, ROFF_br); 104661d06d6bSBaptiste Daroussin return; 104761d06d6bSBaptiste Daroussin } 104861d06d6bSBaptiste Daroussin } 104961d06d6bSBaptiste Daroussin 105061d06d6bSBaptiste Daroussin /* 105161d06d6bSBaptiste Daroussin * This routine accommodates implicitly- and explicitly-scoped 105261d06d6bSBaptiste Daroussin * macro openings. Implicit ones first close out prior scope 105361d06d6bSBaptiste Daroussin * (seen above). Delay opening the head until necessary to 105461d06d6bSBaptiste Daroussin * allow leading punctuation to print. Special consideration 105561d06d6bSBaptiste Daroussin * for `It -column', which has phrase-part syntax instead of 105661d06d6bSBaptiste Daroussin * regular child nodes. 105761d06d6bSBaptiste Daroussin */ 105861d06d6bSBaptiste Daroussin 10597295610fSBaptiste Daroussin switch (tok) { 10607295610fSBaptiste Daroussin case MDOC_Sh: 10617295610fSBaptiste Daroussin mdoc->flags &= ~ROFF_NOFILL; 10627295610fSBaptiste Daroussin break; 10637295610fSBaptiste Daroussin case MDOC_Ss: 10647295610fSBaptiste Daroussin mdoc->flags |= ROFF_NONOFILL; 10657295610fSBaptiste Daroussin break; 10667295610fSBaptiste Daroussin default: 10677295610fSBaptiste Daroussin break; 10687295610fSBaptiste Daroussin } 106961d06d6bSBaptiste Daroussin mdoc_argv(mdoc, line, tok, &arg, pos, buf); 107061d06d6bSBaptiste Daroussin blk = mdoc_block_alloc(mdoc, line, ppos, tok, arg); 107161d06d6bSBaptiste Daroussin head = body = NULL; 107261d06d6bSBaptiste Daroussin 107361d06d6bSBaptiste Daroussin /* 107461d06d6bSBaptiste Daroussin * Exception: Heads of `It' macros in `-diag' lists are not 107561d06d6bSBaptiste Daroussin * parsed, even though `It' macros in general are parsed. 107661d06d6bSBaptiste Daroussin */ 107761d06d6bSBaptiste Daroussin 107861d06d6bSBaptiste Daroussin parsed = tok != MDOC_It || 107961d06d6bSBaptiste Daroussin mdoc->last->parent->tok != MDOC_Bl || 108061d06d6bSBaptiste Daroussin mdoc->last->parent->norm->Bl.type != LIST_diag; 108161d06d6bSBaptiste Daroussin 108261d06d6bSBaptiste Daroussin /* 108361d06d6bSBaptiste Daroussin * The `Nd' macro has all arguments in its body: it's a hybrid 108461d06d6bSBaptiste Daroussin * of block partial-explicit and full-implicit. Stupid. 108561d06d6bSBaptiste Daroussin */ 108661d06d6bSBaptiste Daroussin 108761d06d6bSBaptiste Daroussin if (tok == MDOC_Nd) { 108861d06d6bSBaptiste Daroussin head = roff_head_alloc(mdoc, line, ppos, tok); 108961d06d6bSBaptiste Daroussin rew_last(mdoc, head); 109061d06d6bSBaptiste Daroussin body = roff_body_alloc(mdoc, line, ppos, tok); 109161d06d6bSBaptiste Daroussin } 109261d06d6bSBaptiste Daroussin 109361d06d6bSBaptiste Daroussin if (tok == MDOC_Bk) 109461d06d6bSBaptiste Daroussin mdoc->flags |= MDOC_KEEP; 109561d06d6bSBaptiste Daroussin 109661d06d6bSBaptiste Daroussin ac = ARGS_EOLN; 109761d06d6bSBaptiste Daroussin for (;;) { 109861d06d6bSBaptiste Daroussin 109961d06d6bSBaptiste Daroussin /* 110061d06d6bSBaptiste Daroussin * If we are right after a tab character, 110161d06d6bSBaptiste Daroussin * do not parse the first word for macros. 110261d06d6bSBaptiste Daroussin */ 110361d06d6bSBaptiste Daroussin 110461d06d6bSBaptiste Daroussin if (mdoc->flags & MDOC_PHRASEQN) { 110561d06d6bSBaptiste Daroussin mdoc->flags &= ~MDOC_PHRASEQN; 110661d06d6bSBaptiste Daroussin mdoc->flags |= MDOC_PHRASEQF; 110761d06d6bSBaptiste Daroussin } 110861d06d6bSBaptiste Daroussin 110961d06d6bSBaptiste Daroussin la = *pos; 111061d06d6bSBaptiste Daroussin lac = ac; 111161d06d6bSBaptiste Daroussin ac = mdoc_args(mdoc, line, pos, buf, tok, &p); 111261d06d6bSBaptiste Daroussin if (ac == ARGS_EOLN) { 111361d06d6bSBaptiste Daroussin if (lac != ARGS_PHRASE || 111461d06d6bSBaptiste Daroussin ! (mdoc->flags & MDOC_PHRASEQF)) 111561d06d6bSBaptiste Daroussin break; 111661d06d6bSBaptiste Daroussin 111761d06d6bSBaptiste Daroussin /* 111861d06d6bSBaptiste Daroussin * This line ends in a tab; start the next 111961d06d6bSBaptiste Daroussin * column now, with a leading blank. 112061d06d6bSBaptiste Daroussin */ 112161d06d6bSBaptiste Daroussin 112261d06d6bSBaptiste Daroussin if (body != NULL) 112361d06d6bSBaptiste Daroussin rew_last(mdoc, body); 112461d06d6bSBaptiste Daroussin body = roff_body_alloc(mdoc, line, ppos, tok); 112561d06d6bSBaptiste Daroussin roff_word_alloc(mdoc, line, ppos, "\\&"); 112661d06d6bSBaptiste Daroussin break; 112761d06d6bSBaptiste Daroussin } 112861d06d6bSBaptiste Daroussin 112961d06d6bSBaptiste Daroussin if (tok == MDOC_Bd || tok == MDOC_Bk) { 11307295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ARG_EXCESS, line, la, 11317295610fSBaptiste Daroussin "%s ... %s", roff_name[tok], buf + la); 11327295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 11337295610fSBaptiste Daroussin free(p); 113461d06d6bSBaptiste Daroussin break; 113561d06d6bSBaptiste Daroussin } 113661d06d6bSBaptiste Daroussin if (tok == MDOC_Rs) { 11377295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ARG_SKIP, 113861d06d6bSBaptiste Daroussin line, la, "Rs %s", buf + la); 11397295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 11407295610fSBaptiste Daroussin free(p); 114161d06d6bSBaptiste Daroussin break; 114261d06d6bSBaptiste Daroussin } 114361d06d6bSBaptiste Daroussin if (ac == ARGS_PUNCT) 114461d06d6bSBaptiste Daroussin break; 114561d06d6bSBaptiste Daroussin 114661d06d6bSBaptiste Daroussin /* 114761d06d6bSBaptiste Daroussin * Emit leading punctuation (i.e., punctuation before 114861d06d6bSBaptiste Daroussin * the ROFFT_HEAD) for non-phrase types. 114961d06d6bSBaptiste Daroussin */ 115061d06d6bSBaptiste Daroussin 115161d06d6bSBaptiste Daroussin if (head == NULL && 115261d06d6bSBaptiste Daroussin ac != ARGS_PHRASE && 115361d06d6bSBaptiste Daroussin mdoc_isdelim(p) == DELIM_OPEN) { 115461d06d6bSBaptiste Daroussin dword(mdoc, line, la, p, DELIM_OPEN, 0); 11557295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 11567295610fSBaptiste Daroussin free(p); 115761d06d6bSBaptiste Daroussin continue; 115861d06d6bSBaptiste Daroussin } 115961d06d6bSBaptiste Daroussin 116061d06d6bSBaptiste Daroussin /* Open a head if one hasn't been opened. */ 116161d06d6bSBaptiste Daroussin 116261d06d6bSBaptiste Daroussin if (head == NULL) 116361d06d6bSBaptiste Daroussin head = roff_head_alloc(mdoc, line, ppos, tok); 116461d06d6bSBaptiste Daroussin 116561d06d6bSBaptiste Daroussin if (ac == ARGS_PHRASE) { 116661d06d6bSBaptiste Daroussin 116761d06d6bSBaptiste Daroussin /* 116861d06d6bSBaptiste Daroussin * If we haven't opened a body yet, rewind the 116961d06d6bSBaptiste Daroussin * head; if we have, rewind that instead. 117061d06d6bSBaptiste Daroussin */ 117161d06d6bSBaptiste Daroussin 117261d06d6bSBaptiste Daroussin rew_last(mdoc, body == NULL ? head : body); 117361d06d6bSBaptiste Daroussin body = roff_body_alloc(mdoc, line, ppos, tok); 117461d06d6bSBaptiste Daroussin 117561d06d6bSBaptiste Daroussin /* Process to the tab or to the end of the line. */ 117661d06d6bSBaptiste Daroussin 117761d06d6bSBaptiste Daroussin mdoc->flags |= MDOC_PHRASE; 117861d06d6bSBaptiste Daroussin parse_rest(mdoc, TOKEN_NONE, line, &la, buf); 117961d06d6bSBaptiste Daroussin mdoc->flags &= ~MDOC_PHRASE; 118061d06d6bSBaptiste Daroussin 118161d06d6bSBaptiste Daroussin /* There may have been `Ta' macros. */ 118261d06d6bSBaptiste Daroussin 118361d06d6bSBaptiste Daroussin while (body->next != NULL) 118461d06d6bSBaptiste Daroussin body = body->next; 118561d06d6bSBaptiste Daroussin continue; 118661d06d6bSBaptiste Daroussin } 118761d06d6bSBaptiste Daroussin 11887295610fSBaptiste Daroussin done = macro_or_word(mdoc, tok, line, la, pos, buf, p, parsed); 11897295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 11907295610fSBaptiste Daroussin free(p); 11917295610fSBaptiste Daroussin if (done) 119261d06d6bSBaptiste Daroussin break; 119361d06d6bSBaptiste Daroussin } 119461d06d6bSBaptiste Daroussin 119561d06d6bSBaptiste Daroussin if (blk->flags & NODE_VALID) 119661d06d6bSBaptiste Daroussin return; 119761d06d6bSBaptiste Daroussin if (head == NULL) 119861d06d6bSBaptiste Daroussin head = roff_head_alloc(mdoc, line, ppos, tok); 119961d06d6bSBaptiste Daroussin if (nl && tok != MDOC_Bd && tok != MDOC_Bl && tok != MDOC_Rs) 120061d06d6bSBaptiste Daroussin append_delims(mdoc, line, pos, buf); 120161d06d6bSBaptiste Daroussin if (body != NULL) 120261d06d6bSBaptiste Daroussin goto out; 120361d06d6bSBaptiste Daroussin if (find_pending(mdoc, tok, line, ppos, head)) 120461d06d6bSBaptiste Daroussin return; 120561d06d6bSBaptiste Daroussin 120661d06d6bSBaptiste Daroussin /* Close out scopes to remain in a consistent state. */ 120761d06d6bSBaptiste Daroussin 120861d06d6bSBaptiste Daroussin rew_last(mdoc, head); 120961d06d6bSBaptiste Daroussin body = roff_body_alloc(mdoc, line, ppos, tok); 12107295610fSBaptiste Daroussin if (tok == MDOC_Ss) 12117295610fSBaptiste Daroussin mdoc->flags &= ~ROFF_NONOFILL; 12127295610fSBaptiste Daroussin 12137295610fSBaptiste Daroussin /* 12147295610fSBaptiste Daroussin * Set up fill mode for display blocks. 12157295610fSBaptiste Daroussin * This needs to be done here up front rather than during 12167295610fSBaptiste Daroussin * validation such that child nodes get the right flags. 12177295610fSBaptiste Daroussin */ 12187295610fSBaptiste Daroussin 12197295610fSBaptiste Daroussin if (tok == MDOC_Bd && arg != NULL) { 12207295610fSBaptiste Daroussin for (iarg = 0; iarg < arg->argc; iarg++) { 12217295610fSBaptiste Daroussin switch (arg->argv[iarg].arg) { 12227295610fSBaptiste Daroussin case MDOC_Unfilled: 12237295610fSBaptiste Daroussin case MDOC_Literal: 12247295610fSBaptiste Daroussin mdoc->flags |= ROFF_NOFILL; 12257295610fSBaptiste Daroussin break; 12267295610fSBaptiste Daroussin case MDOC_Filled: 12277295610fSBaptiste Daroussin case MDOC_Ragged: 12287295610fSBaptiste Daroussin case MDOC_Centred: 12297295610fSBaptiste Daroussin mdoc->flags &= ~ROFF_NOFILL; 12307295610fSBaptiste Daroussin break; 12317295610fSBaptiste Daroussin default: 12327295610fSBaptiste Daroussin continue; 12337295610fSBaptiste Daroussin } 12347295610fSBaptiste Daroussin break; 12357295610fSBaptiste Daroussin } 12367295610fSBaptiste Daroussin } 123761d06d6bSBaptiste Daroussin out: 123861d06d6bSBaptiste Daroussin if (mdoc->flags & MDOC_FREECOL) { 123961d06d6bSBaptiste Daroussin rew_last(mdoc, body); 124061d06d6bSBaptiste Daroussin rew_last(mdoc, blk); 124161d06d6bSBaptiste Daroussin mdoc->flags &= ~MDOC_FREECOL; 124261d06d6bSBaptiste Daroussin } 124361d06d6bSBaptiste Daroussin } 124461d06d6bSBaptiste Daroussin 124561d06d6bSBaptiste Daroussin static void 124661d06d6bSBaptiste Daroussin blk_part_imp(MACRO_PROT_ARGS) 124761d06d6bSBaptiste Daroussin { 12487295610fSBaptiste Daroussin int done, la, nl; 124961d06d6bSBaptiste Daroussin enum margserr ac; 125061d06d6bSBaptiste Daroussin char *p; 125161d06d6bSBaptiste Daroussin struct roff_node *blk; /* saved block context */ 125261d06d6bSBaptiste Daroussin struct roff_node *body; /* saved body context */ 125361d06d6bSBaptiste Daroussin struct roff_node *n; 125461d06d6bSBaptiste Daroussin 125561d06d6bSBaptiste Daroussin nl = MDOC_NEWLINE & mdoc->flags; 125661d06d6bSBaptiste Daroussin 125761d06d6bSBaptiste Daroussin /* 125861d06d6bSBaptiste Daroussin * A macro that spans to the end of the line. This is generally 125961d06d6bSBaptiste Daroussin * (but not necessarily) called as the first macro. The block 126061d06d6bSBaptiste Daroussin * has a head as the immediate child, which is always empty, 126161d06d6bSBaptiste Daroussin * followed by zero or more opening punctuation nodes, then the 126261d06d6bSBaptiste Daroussin * body (which may be empty, depending on the macro), then zero 126361d06d6bSBaptiste Daroussin * or more closing punctuation nodes. 126461d06d6bSBaptiste Daroussin */ 126561d06d6bSBaptiste Daroussin 126661d06d6bSBaptiste Daroussin blk = mdoc_block_alloc(mdoc, line, ppos, tok, NULL); 126761d06d6bSBaptiste Daroussin rew_last(mdoc, roff_head_alloc(mdoc, line, ppos, tok)); 126861d06d6bSBaptiste Daroussin 126961d06d6bSBaptiste Daroussin /* 127061d06d6bSBaptiste Daroussin * Open the body scope "on-demand", that is, after we've 127161d06d6bSBaptiste Daroussin * processed all our the leading delimiters (open parenthesis, 127261d06d6bSBaptiste Daroussin * etc.). 127361d06d6bSBaptiste Daroussin */ 127461d06d6bSBaptiste Daroussin 127561d06d6bSBaptiste Daroussin for (body = NULL; ; ) { 127661d06d6bSBaptiste Daroussin la = *pos; 127761d06d6bSBaptiste Daroussin ac = mdoc_args(mdoc, line, pos, buf, tok, &p); 127861d06d6bSBaptiste Daroussin if (ac == ARGS_EOLN || ac == ARGS_PUNCT) 127961d06d6bSBaptiste Daroussin break; 128061d06d6bSBaptiste Daroussin 128161d06d6bSBaptiste Daroussin if (body == NULL && mdoc_isdelim(p) == DELIM_OPEN) { 128261d06d6bSBaptiste Daroussin dword(mdoc, line, la, p, DELIM_OPEN, 0); 12837295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 12847295610fSBaptiste Daroussin free(p); 128561d06d6bSBaptiste Daroussin continue; 128661d06d6bSBaptiste Daroussin } 128761d06d6bSBaptiste Daroussin 128861d06d6bSBaptiste Daroussin if (body == NULL) 128961d06d6bSBaptiste Daroussin body = roff_body_alloc(mdoc, line, ppos, tok); 129061d06d6bSBaptiste Daroussin 12917295610fSBaptiste Daroussin done = macro_or_word(mdoc, tok, line, la, pos, buf, p, 1); 12927295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 12937295610fSBaptiste Daroussin free(p); 12947295610fSBaptiste Daroussin if (done) 129561d06d6bSBaptiste Daroussin break; 129661d06d6bSBaptiste Daroussin } 129761d06d6bSBaptiste Daroussin if (body == NULL) 129861d06d6bSBaptiste Daroussin body = roff_body_alloc(mdoc, line, ppos, tok); 129961d06d6bSBaptiste Daroussin 130061d06d6bSBaptiste Daroussin if (find_pending(mdoc, tok, line, ppos, body)) 130161d06d6bSBaptiste Daroussin return; 130261d06d6bSBaptiste Daroussin 130361d06d6bSBaptiste Daroussin rew_last(mdoc, body); 130461d06d6bSBaptiste Daroussin if (nl) 130561d06d6bSBaptiste Daroussin append_delims(mdoc, line, pos, buf); 130661d06d6bSBaptiste Daroussin rew_pending(mdoc, blk); 130761d06d6bSBaptiste Daroussin 130861d06d6bSBaptiste Daroussin /* Move trailing .Ns out of scope. */ 130961d06d6bSBaptiste Daroussin 131061d06d6bSBaptiste Daroussin for (n = body->child; n && n->next; n = n->next) 131161d06d6bSBaptiste Daroussin /* Do nothing. */ ; 131261d06d6bSBaptiste Daroussin if (n && n->tok == MDOC_Ns) 13137295610fSBaptiste Daroussin roff_node_relink(mdoc, n); 131461d06d6bSBaptiste Daroussin } 131561d06d6bSBaptiste Daroussin 131661d06d6bSBaptiste Daroussin static void 131761d06d6bSBaptiste Daroussin blk_part_exp(MACRO_PROT_ARGS) 131861d06d6bSBaptiste Daroussin { 13197295610fSBaptiste Daroussin int done, la, nl; 132061d06d6bSBaptiste Daroussin enum margserr ac; 132161d06d6bSBaptiste Daroussin struct roff_node *head; /* keep track of head */ 132261d06d6bSBaptiste Daroussin char *p; 132361d06d6bSBaptiste Daroussin 132461d06d6bSBaptiste Daroussin nl = MDOC_NEWLINE & mdoc->flags; 132561d06d6bSBaptiste Daroussin 132661d06d6bSBaptiste Daroussin /* 132761d06d6bSBaptiste Daroussin * The opening of an explicit macro having zero or more leading 132861d06d6bSBaptiste Daroussin * punctuation nodes; a head with optional single element (the 132961d06d6bSBaptiste Daroussin * case of `Eo'); and a body that may be empty. 133061d06d6bSBaptiste Daroussin */ 133161d06d6bSBaptiste Daroussin 133261d06d6bSBaptiste Daroussin roff_block_alloc(mdoc, line, ppos, tok); 133361d06d6bSBaptiste Daroussin head = NULL; 133461d06d6bSBaptiste Daroussin for (;;) { 133561d06d6bSBaptiste Daroussin la = *pos; 133661d06d6bSBaptiste Daroussin ac = mdoc_args(mdoc, line, pos, buf, tok, &p); 133761d06d6bSBaptiste Daroussin if (ac == ARGS_PUNCT || ac == ARGS_EOLN) 133861d06d6bSBaptiste Daroussin break; 133961d06d6bSBaptiste Daroussin 134061d06d6bSBaptiste Daroussin /* Flush out leading punctuation. */ 134161d06d6bSBaptiste Daroussin 134261d06d6bSBaptiste Daroussin if (head == NULL && mdoc_isdelim(p) == DELIM_OPEN) { 134361d06d6bSBaptiste Daroussin dword(mdoc, line, la, p, DELIM_OPEN, 0); 13447295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 13457295610fSBaptiste Daroussin free(p); 134661d06d6bSBaptiste Daroussin continue; 134761d06d6bSBaptiste Daroussin } 134861d06d6bSBaptiste Daroussin 134961d06d6bSBaptiste Daroussin if (head == NULL) { 135061d06d6bSBaptiste Daroussin head = roff_head_alloc(mdoc, line, ppos, tok); 135161d06d6bSBaptiste Daroussin if (tok == MDOC_Eo) /* Not parsed. */ 135261d06d6bSBaptiste Daroussin dword(mdoc, line, la, p, DELIM_MAX, 0); 135361d06d6bSBaptiste Daroussin rew_last(mdoc, head); 135461d06d6bSBaptiste Daroussin roff_body_alloc(mdoc, line, ppos, tok); 13557295610fSBaptiste Daroussin if (tok == MDOC_Eo) { 13567295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 13577295610fSBaptiste Daroussin free(p); 135861d06d6bSBaptiste Daroussin continue; 135961d06d6bSBaptiste Daroussin } 13607295610fSBaptiste Daroussin } 136161d06d6bSBaptiste Daroussin 13627295610fSBaptiste Daroussin done = macro_or_word(mdoc, tok, line, la, pos, buf, p, 1); 13637295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 13647295610fSBaptiste Daroussin free(p); 13657295610fSBaptiste Daroussin if (done) 136661d06d6bSBaptiste Daroussin break; 136761d06d6bSBaptiste Daroussin } 136861d06d6bSBaptiste Daroussin 136961d06d6bSBaptiste Daroussin /* Clean-up to leave in a consistent state. */ 137061d06d6bSBaptiste Daroussin 137161d06d6bSBaptiste Daroussin if (head == NULL) { 137261d06d6bSBaptiste Daroussin rew_last(mdoc, roff_head_alloc(mdoc, line, ppos, tok)); 137361d06d6bSBaptiste Daroussin roff_body_alloc(mdoc, line, ppos, tok); 137461d06d6bSBaptiste Daroussin } 137561d06d6bSBaptiste Daroussin if (nl) 137661d06d6bSBaptiste Daroussin append_delims(mdoc, line, pos, buf); 137761d06d6bSBaptiste Daroussin } 137861d06d6bSBaptiste Daroussin 137961d06d6bSBaptiste Daroussin static void 138061d06d6bSBaptiste Daroussin in_line_argn(MACRO_PROT_ARGS) 138161d06d6bSBaptiste Daroussin { 138261d06d6bSBaptiste Daroussin struct mdoc_arg *arg; 138361d06d6bSBaptiste Daroussin char *p; 138461d06d6bSBaptiste Daroussin enum margserr ac; 138561d06d6bSBaptiste Daroussin enum roff_tok ntok; 138661d06d6bSBaptiste Daroussin int state; /* arg#; -1: not yet open; -2: closed */ 138761d06d6bSBaptiste Daroussin int la, maxargs, nl; 138861d06d6bSBaptiste Daroussin 138961d06d6bSBaptiste Daroussin nl = mdoc->flags & MDOC_NEWLINE; 139061d06d6bSBaptiste Daroussin 139161d06d6bSBaptiste Daroussin /* 139261d06d6bSBaptiste Daroussin * A line macro that has a fixed number of arguments (maxargs). 139361d06d6bSBaptiste Daroussin * Only open the scope once the first non-leading-punctuation is 139461d06d6bSBaptiste Daroussin * found (unless MDOC_IGNDELIM is noted, like in `Pf'), then 139561d06d6bSBaptiste Daroussin * keep it open until the maximum number of arguments are 139661d06d6bSBaptiste Daroussin * exhausted. 139761d06d6bSBaptiste Daroussin */ 139861d06d6bSBaptiste Daroussin 139961d06d6bSBaptiste Daroussin switch (tok) { 140061d06d6bSBaptiste Daroussin case MDOC_Ap: 140161d06d6bSBaptiste Daroussin case MDOC_Ns: 140261d06d6bSBaptiste Daroussin case MDOC_Ux: 140361d06d6bSBaptiste Daroussin maxargs = 0; 140461d06d6bSBaptiste Daroussin break; 140561d06d6bSBaptiste Daroussin case MDOC_Bx: 140661d06d6bSBaptiste Daroussin case MDOC_Es: 140761d06d6bSBaptiste Daroussin case MDOC_Xr: 140861d06d6bSBaptiste Daroussin maxargs = 2; 140961d06d6bSBaptiste Daroussin break; 141061d06d6bSBaptiste Daroussin default: 141161d06d6bSBaptiste Daroussin maxargs = 1; 141261d06d6bSBaptiste Daroussin break; 141361d06d6bSBaptiste Daroussin } 141461d06d6bSBaptiste Daroussin 141561d06d6bSBaptiste Daroussin mdoc_argv(mdoc, line, tok, &arg, pos, buf); 141661d06d6bSBaptiste Daroussin 141761d06d6bSBaptiste Daroussin state = -1; 141861d06d6bSBaptiste Daroussin p = NULL; 141961d06d6bSBaptiste Daroussin for (;;) { 142061d06d6bSBaptiste Daroussin la = *pos; 142161d06d6bSBaptiste Daroussin ac = mdoc_args(mdoc, line, pos, buf, tok, &p); 142261d06d6bSBaptiste Daroussin 14237295610fSBaptiste Daroussin if ((ac == ARGS_WORD || ac == ARGS_ALLOC) && state == -1 && 14247295610fSBaptiste Daroussin (mdoc_macro(tok)->flags & MDOC_IGNDELIM) == 0 && 142561d06d6bSBaptiste Daroussin mdoc_isdelim(p) == DELIM_OPEN) { 142661d06d6bSBaptiste Daroussin dword(mdoc, line, la, p, DELIM_OPEN, 0); 14277295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 14287295610fSBaptiste Daroussin free(p); 142961d06d6bSBaptiste Daroussin continue; 143061d06d6bSBaptiste Daroussin } 143161d06d6bSBaptiste Daroussin 143261d06d6bSBaptiste Daroussin if (state == -1 && tok != MDOC_In && 143361d06d6bSBaptiste Daroussin tok != MDOC_St && tok != MDOC_Xr) { 143461d06d6bSBaptiste Daroussin mdoc_elem_alloc(mdoc, line, ppos, tok, arg); 143561d06d6bSBaptiste Daroussin state = 0; 143661d06d6bSBaptiste Daroussin } 143761d06d6bSBaptiste Daroussin 143861d06d6bSBaptiste Daroussin if (ac == ARGS_PUNCT || ac == ARGS_EOLN) { 143961d06d6bSBaptiste Daroussin if (abs(state) < 2 && tok == MDOC_Pf) 14407295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_PF_SKIP, 14417295610fSBaptiste Daroussin line, ppos, "Pf %s", 144261d06d6bSBaptiste Daroussin p == NULL ? "at eol" : p); 144361d06d6bSBaptiste Daroussin break; 144461d06d6bSBaptiste Daroussin } 144561d06d6bSBaptiste Daroussin 144661d06d6bSBaptiste Daroussin if (state == maxargs) { 144761d06d6bSBaptiste Daroussin rew_elem(mdoc, tok); 144861d06d6bSBaptiste Daroussin state = -2; 144961d06d6bSBaptiste Daroussin } 145061d06d6bSBaptiste Daroussin 145161d06d6bSBaptiste Daroussin ntok = (tok == MDOC_Pf && state == 0) ? 145261d06d6bSBaptiste Daroussin TOKEN_NONE : lookup(mdoc, tok, line, la, p); 145361d06d6bSBaptiste Daroussin 145461d06d6bSBaptiste Daroussin if (ntok != TOKEN_NONE) { 145561d06d6bSBaptiste Daroussin if (state >= 0) { 145661d06d6bSBaptiste Daroussin rew_elem(mdoc, tok); 145761d06d6bSBaptiste Daroussin state = -2; 145861d06d6bSBaptiste Daroussin } 14597295610fSBaptiste Daroussin (*mdoc_macro(ntok)->fp)(mdoc, ntok, 14607295610fSBaptiste Daroussin line, la, pos, buf); 14617295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 14627295610fSBaptiste Daroussin free(p); 146361d06d6bSBaptiste Daroussin break; 146461d06d6bSBaptiste Daroussin } 146561d06d6bSBaptiste Daroussin 14667295610fSBaptiste Daroussin if (mdoc_macro(tok)->flags & MDOC_IGNDELIM || 146761d06d6bSBaptiste Daroussin mdoc_isdelim(p) == DELIM_NONE) { 146861d06d6bSBaptiste Daroussin if (state == -1) { 146961d06d6bSBaptiste Daroussin mdoc_elem_alloc(mdoc, line, ppos, tok, arg); 147061d06d6bSBaptiste Daroussin state = 1; 147161d06d6bSBaptiste Daroussin } else if (state >= 0) 147261d06d6bSBaptiste Daroussin state++; 147361d06d6bSBaptiste Daroussin } else if (state >= 0) { 147461d06d6bSBaptiste Daroussin rew_elem(mdoc, tok); 147561d06d6bSBaptiste Daroussin state = -2; 147661d06d6bSBaptiste Daroussin } 147761d06d6bSBaptiste Daroussin 147861d06d6bSBaptiste Daroussin dword(mdoc, line, la, p, DELIM_MAX, 14797295610fSBaptiste Daroussin mdoc_macro(tok)->flags & MDOC_JOIN); 14807295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 14817295610fSBaptiste Daroussin free(p); 14827295610fSBaptiste Daroussin p = mdoc->last->string; 148361d06d6bSBaptiste Daroussin } 148461d06d6bSBaptiste Daroussin 148561d06d6bSBaptiste Daroussin if (state == -1) { 14867295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_MACRO_EMPTY, 14877295610fSBaptiste Daroussin line, ppos, "%s", roff_name[tok]); 148861d06d6bSBaptiste Daroussin return; 148961d06d6bSBaptiste Daroussin } 149061d06d6bSBaptiste Daroussin 149161d06d6bSBaptiste Daroussin if (state == 0 && tok == MDOC_Pf) 149261d06d6bSBaptiste Daroussin append_delims(mdoc, line, pos, buf); 149361d06d6bSBaptiste Daroussin if (state >= 0) 149461d06d6bSBaptiste Daroussin rew_elem(mdoc, tok); 149561d06d6bSBaptiste Daroussin if (nl) 149661d06d6bSBaptiste Daroussin append_delims(mdoc, line, pos, buf); 149761d06d6bSBaptiste Daroussin } 149861d06d6bSBaptiste Daroussin 149961d06d6bSBaptiste Daroussin static void 150061d06d6bSBaptiste Daroussin in_line_eoln(MACRO_PROT_ARGS) 150161d06d6bSBaptiste Daroussin { 150261d06d6bSBaptiste Daroussin struct roff_node *n; 150361d06d6bSBaptiste Daroussin struct mdoc_arg *arg; 150461d06d6bSBaptiste Daroussin 150561d06d6bSBaptiste Daroussin if ((tok == MDOC_Pp || tok == MDOC_Lp) && 150661d06d6bSBaptiste Daroussin ! (mdoc->flags & MDOC_SYNOPSIS)) { 150761d06d6bSBaptiste Daroussin n = mdoc->last; 150861d06d6bSBaptiste Daroussin if (mdoc->next == ROFF_NEXT_SIBLING) 150961d06d6bSBaptiste Daroussin n = n->parent; 151061d06d6bSBaptiste Daroussin if (n->tok == MDOC_Nm) 151161d06d6bSBaptiste Daroussin rew_last(mdoc, n->parent); 151261d06d6bSBaptiste Daroussin } 151361d06d6bSBaptiste Daroussin 151461d06d6bSBaptiste Daroussin if (buf[*pos] == '\0' && 151561d06d6bSBaptiste Daroussin (tok == MDOC_Fd || *roff_name[tok] == '%')) { 15167295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_MACRO_EMPTY, 15177295610fSBaptiste Daroussin line, ppos, "%s", roff_name[tok]); 151861d06d6bSBaptiste Daroussin return; 151961d06d6bSBaptiste Daroussin } 152061d06d6bSBaptiste Daroussin 152161d06d6bSBaptiste Daroussin mdoc_argv(mdoc, line, tok, &arg, pos, buf); 152261d06d6bSBaptiste Daroussin mdoc_elem_alloc(mdoc, line, ppos, tok, arg); 152361d06d6bSBaptiste Daroussin if (parse_rest(mdoc, tok, line, pos, buf)) 152461d06d6bSBaptiste Daroussin return; 152561d06d6bSBaptiste Daroussin rew_elem(mdoc, tok); 152661d06d6bSBaptiste Daroussin } 152761d06d6bSBaptiste Daroussin 152861d06d6bSBaptiste Daroussin /* 152961d06d6bSBaptiste Daroussin * The simplest argument parser available: Parse the remaining 153061d06d6bSBaptiste Daroussin * words until the end of the phrase or line and return 0 153161d06d6bSBaptiste Daroussin * or until the next macro, call that macro, and return 1. 153261d06d6bSBaptiste Daroussin */ 153361d06d6bSBaptiste Daroussin static int 153461d06d6bSBaptiste Daroussin parse_rest(struct roff_man *mdoc, enum roff_tok tok, 153561d06d6bSBaptiste Daroussin int line, int *pos, char *buf) 153661d06d6bSBaptiste Daroussin { 15377295610fSBaptiste Daroussin char *p; 15387295610fSBaptiste Daroussin int done, la; 15397295610fSBaptiste Daroussin enum margserr ac; 154061d06d6bSBaptiste Daroussin 154161d06d6bSBaptiste Daroussin for (;;) { 154261d06d6bSBaptiste Daroussin la = *pos; 15437295610fSBaptiste Daroussin ac = mdoc_args(mdoc, line, pos, buf, tok, &p); 15447295610fSBaptiste Daroussin if (ac == ARGS_EOLN) 154561d06d6bSBaptiste Daroussin return 0; 15467295610fSBaptiste Daroussin done = macro_or_word(mdoc, tok, line, la, pos, buf, p, 1); 15477295610fSBaptiste Daroussin if (ac == ARGS_ALLOC) 15487295610fSBaptiste Daroussin free(p); 15497295610fSBaptiste Daroussin if (done) 155061d06d6bSBaptiste Daroussin return 1; 155161d06d6bSBaptiste Daroussin } 155261d06d6bSBaptiste Daroussin } 155361d06d6bSBaptiste Daroussin 155461d06d6bSBaptiste Daroussin static void 155561d06d6bSBaptiste Daroussin ctx_synopsis(MACRO_PROT_ARGS) 155661d06d6bSBaptiste Daroussin { 155761d06d6bSBaptiste Daroussin 155861d06d6bSBaptiste Daroussin if (~mdoc->flags & (MDOC_SYNOPSIS | MDOC_NEWLINE)) 155961d06d6bSBaptiste Daroussin in_line(mdoc, tok, line, ppos, pos, buf); 156061d06d6bSBaptiste Daroussin else if (tok == MDOC_Nm) 156161d06d6bSBaptiste Daroussin blk_full(mdoc, tok, line, ppos, pos, buf); 156261d06d6bSBaptiste Daroussin else { 156361d06d6bSBaptiste Daroussin assert(tok == MDOC_Vt); 156461d06d6bSBaptiste Daroussin blk_part_imp(mdoc, tok, line, ppos, pos, buf); 156561d06d6bSBaptiste Daroussin } 156661d06d6bSBaptiste Daroussin } 156761d06d6bSBaptiste Daroussin 156861d06d6bSBaptiste Daroussin /* 156961d06d6bSBaptiste Daroussin * Phrases occur within `Bl -column' entries, separated by `Ta' or tabs. 157061d06d6bSBaptiste Daroussin * They're unusual because they're basically free-form text until a 157161d06d6bSBaptiste Daroussin * macro is encountered. 157261d06d6bSBaptiste Daroussin */ 157361d06d6bSBaptiste Daroussin static void 157461d06d6bSBaptiste Daroussin phrase_ta(MACRO_PROT_ARGS) 157561d06d6bSBaptiste Daroussin { 157661d06d6bSBaptiste Daroussin struct roff_node *body, *n; 157761d06d6bSBaptiste Daroussin 157861d06d6bSBaptiste Daroussin /* Make sure we are in a column list or ignore this macro. */ 157961d06d6bSBaptiste Daroussin 158061d06d6bSBaptiste Daroussin body = NULL; 158161d06d6bSBaptiste Daroussin for (n = mdoc->last; n != NULL; n = n->parent) { 158261d06d6bSBaptiste Daroussin if (n->flags & NODE_ENDED) 158361d06d6bSBaptiste Daroussin continue; 158461d06d6bSBaptiste Daroussin if (n->tok == MDOC_It && n->type == ROFFT_BODY) 158561d06d6bSBaptiste Daroussin body = n; 158661d06d6bSBaptiste Daroussin if (n->tok == MDOC_Bl && n->end == ENDBODY_NOT) 158761d06d6bSBaptiste Daroussin break; 158861d06d6bSBaptiste Daroussin } 158961d06d6bSBaptiste Daroussin 159061d06d6bSBaptiste Daroussin if (n == NULL || n->norm->Bl.type != LIST_column) { 15917295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_TA_STRAY, line, ppos, "Ta"); 159261d06d6bSBaptiste Daroussin return; 159361d06d6bSBaptiste Daroussin } 159461d06d6bSBaptiste Daroussin 159561d06d6bSBaptiste Daroussin /* Advance to the next column. */ 159661d06d6bSBaptiste Daroussin 159761d06d6bSBaptiste Daroussin rew_last(mdoc, body); 159861d06d6bSBaptiste Daroussin roff_body_alloc(mdoc, line, ppos, MDOC_It); 159961d06d6bSBaptiste Daroussin parse_rest(mdoc, TOKEN_NONE, line, pos, buf); 160061d06d6bSBaptiste Daroussin } 1601