1 1.7 wiz /* Id: man_macro.c,v 1.145 2020/09/09 17:01:10 schwarze Exp */ 2 1.1 joerg /* 3 1.2 christos * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps (at) bsd.lv> 4 1.7 wiz * Copyright (c) 2012-2015, 2017-2020 Ingo Schwarze <schwarze (at) openbsd.org> 5 1.2 christos * Copyright (c) 2013 Franco Fichtner <franco (at) lastsummer.de> 6 1.1 joerg * 7 1.1 joerg * Permission to use, copy, modify, and distribute this software for any 8 1.1 joerg * purpose with or without fee is hereby granted, provided that the above 9 1.1 joerg * copyright notice and this permission notice appear in all copies. 10 1.1 joerg * 11 1.3 christos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 12 1.1 joerg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 1.3 christos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 14 1.1 joerg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 1.1 joerg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 1.1 joerg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 1.1 joerg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 1.1 joerg */ 19 1.2 christos #include "config.h" 20 1.2 christos 21 1.2 christos #include <sys/types.h> 22 1.2 christos 23 1.1 joerg #include <assert.h> 24 1.1 joerg #include <ctype.h> 25 1.6 christos #include <stdio.h> 26 1.1 joerg #include <stdlib.h> 27 1.1 joerg #include <string.h> 28 1.1 joerg 29 1.3 christos #include "mandoc.h" 30 1.3 christos #include "roff.h" 31 1.2 christos #include "man.h" 32 1.2 christos #include "libmandoc.h" 33 1.3 christos #include "roff_int.h" 34 1.1 joerg #include "libman.h" 35 1.1 joerg 36 1.2 christos static void blk_close(MACRO_PROT_ARGS); 37 1.2 christos static void blk_exp(MACRO_PROT_ARGS); 38 1.2 christos static void blk_imp(MACRO_PROT_ARGS); 39 1.2 christos static void in_line_eoln(MACRO_PROT_ARGS); 40 1.3 christos static int man_args(struct roff_man *, int, 41 1.2 christos int *, char *, char **); 42 1.5 christos static void rew_scope(struct roff_man *, enum roff_tok); 43 1.1 joerg 44 1.6 christos static const struct man_macro man_macros[MAN_MAX - MAN_TH] = { 45 1.6 christos { in_line_eoln, MAN_XSCOPE }, /* TH */ 46 1.6 christos { blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* SH */ 47 1.6 christos { blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* SS */ 48 1.6 christos { blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* TP */ 49 1.6 christos { blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* TQ */ 50 1.6 christos { blk_imp, MAN_XSCOPE }, /* LP */ 51 1.6 christos { blk_imp, MAN_XSCOPE }, /* PP */ 52 1.6 christos { blk_imp, MAN_XSCOPE }, /* P */ 53 1.6 christos { blk_imp, MAN_XSCOPE }, /* IP */ 54 1.6 christos { blk_imp, MAN_XSCOPE }, /* HP */ 55 1.6 christos { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* SM */ 56 1.6 christos { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* SB */ 57 1.1 joerg { in_line_eoln, 0 }, /* BI */ 58 1.1 joerg { in_line_eoln, 0 }, /* IB */ 59 1.1 joerg { in_line_eoln, 0 }, /* BR */ 60 1.1 joerg { in_line_eoln, 0 }, /* RB */ 61 1.6 christos { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* R */ 62 1.6 christos { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* B */ 63 1.6 christos { in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* I */ 64 1.1 joerg { in_line_eoln, 0 }, /* IR */ 65 1.1 joerg { in_line_eoln, 0 }, /* RI */ 66 1.6 christos { blk_close, MAN_XSCOPE }, /* RE */ 67 1.6 christos { blk_exp, MAN_XSCOPE }, /* RS */ 68 1.1 joerg { in_line_eoln, 0 }, /* DT */ 69 1.1 joerg { in_line_eoln, 0 }, /* UC */ 70 1.3 christos { in_line_eoln, MAN_NSCOPED }, /* PD */ 71 1.2 christos { in_line_eoln, 0 }, /* AT */ 72 1.5 christos { in_line_eoln, MAN_NSCOPED }, /* in */ 73 1.6 christos { blk_imp, MAN_XSCOPE }, /* SY */ 74 1.6 christos { blk_close, MAN_XSCOPE }, /* YS */ 75 1.2 christos { in_line_eoln, 0 }, /* OP */ 76 1.6 christos { in_line_eoln, MAN_XSCOPE }, /* EX */ 77 1.6 christos { in_line_eoln, MAN_XSCOPE }, /* EE */ 78 1.6 christos { blk_exp, MAN_XSCOPE }, /* UR */ 79 1.6 christos { blk_close, MAN_XSCOPE }, /* UE */ 80 1.6 christos { blk_exp, MAN_XSCOPE }, /* MT */ 81 1.6 christos { blk_close, MAN_XSCOPE }, /* ME */ 82 1.1 joerg }; 83 1.1 joerg 84 1.1 joerg 85 1.6 christos const struct man_macro * 86 1.6 christos man_macro(enum roff_tok tok) 87 1.6 christos { 88 1.6 christos assert(tok >= MAN_TH && tok <= MAN_MAX); 89 1.6 christos return man_macros + (tok - MAN_TH); 90 1.6 christos } 91 1.6 christos 92 1.2 christos void 93 1.3 christos man_unscope(struct roff_man *man, const struct roff_node *to) 94 1.1 joerg { 95 1.3 christos struct roff_node *n; 96 1.1 joerg 97 1.2 christos to = to->parent; 98 1.2 christos n = man->last; 99 1.2 christos while (n != to) { 100 1.2 christos 101 1.2 christos /* Reached the end of the document? */ 102 1.2 christos 103 1.4 christos if (to == NULL && ! (n->flags & NODE_VALID)) { 104 1.2 christos if (man->flags & (MAN_BLINE | MAN_ELINE) && 105 1.6 christos man_macro(n->tok)->flags & 106 1.6 christos (MAN_BSCOPED | MAN_NSCOPED)) { 107 1.6 christos mandoc_msg(MANDOCERR_BLK_LINE, 108 1.6 christos n->line, n->pos, 109 1.5 christos "EOF breaks %s", roff_name[n->tok]); 110 1.7 wiz if (man->flags & MAN_ELINE) { 111 1.7 wiz if ((man_macro(n->parent->tok)->flags & 112 1.7 wiz MAN_ESCOPED) == 0) 113 1.7 wiz man->flags &= ~MAN_ELINE; 114 1.7 wiz } else { 115 1.3 christos assert(n->type == ROFFT_HEAD); 116 1.2 christos n = n->parent; 117 1.2 christos man->flags &= ~MAN_BLINE; 118 1.2 christos } 119 1.2 christos man->last = n; 120 1.2 christos n = n->parent; 121 1.3 christos roff_node_delete(man, man->last); 122 1.2 christos continue; 123 1.2 christos } 124 1.3 christos if (n->type == ROFFT_BLOCK && 125 1.6 christos man_macro(n->tok)->fp == blk_exp) 126 1.2 christos mandoc_msg(MANDOCERR_BLK_NOEND, 127 1.6 christos n->line, n->pos, "%s", 128 1.5 christos roff_name[n->tok]); 129 1.2 christos } 130 1.1 joerg 131 1.2 christos /* 132 1.2 christos * We might delete the man->last node 133 1.2 christos * in the post-validation phase. 134 1.2 christos * Save a pointer to the parent such that 135 1.2 christos * we know where to continue the iteration. 136 1.2 christos */ 137 1.2 christos 138 1.2 christos man->last = n; 139 1.2 christos n = n->parent; 140 1.4 christos man->last->flags |= NODE_VALID; 141 1.1 joerg } 142 1.1 joerg 143 1.2 christos /* 144 1.2 christos * If we ended up at the parent of the node we were 145 1.2 christos * supposed to rewind to, that means the target node 146 1.2 christos * got deleted, so add the next node we parse as a child 147 1.2 christos * of the parent instead of as a sibling of the target. 148 1.2 christos */ 149 1.2 christos 150 1.2 christos man->next = (man->last == to) ? 151 1.3 christos ROFF_NEXT_CHILD : ROFF_NEXT_SIBLING; 152 1.1 joerg } 153 1.1 joerg 154 1.1 joerg /* 155 1.1 joerg * Rewinding entails ascending the parse tree until a coherent point, 156 1.1 joerg * for example, the `SH' macro will close out any intervening `SS' 157 1.1 joerg * scopes. When a scope is closed, it must be validated and actioned. 158 1.1 joerg */ 159 1.2 christos static void 160 1.5 christos rew_scope(struct roff_man *man, enum roff_tok tok) 161 1.1 joerg { 162 1.3 christos struct roff_node *n; 163 1.3 christos 164 1.3 christos /* Preserve empty paragraphs before RS. */ 165 1.3 christos 166 1.3 christos n = man->last; 167 1.3 christos if (tok == MAN_RS && n->child == NULL && 168 1.3 christos (n->tok == MAN_P || n->tok == MAN_PP || n->tok == MAN_LP)) 169 1.3 christos return; 170 1.1 joerg 171 1.3 christos for (;;) { 172 1.3 christos if (n->type == ROFFT_ROOT) 173 1.3 christos return; 174 1.4 christos if (n->flags & NODE_VALID) { 175 1.3 christos n = n->parent; 176 1.3 christos continue; 177 1.3 christos } 178 1.3 christos if (n->type != ROFFT_BLOCK) { 179 1.3 christos if (n->parent->type == ROFFT_ROOT) { 180 1.3 christos man_unscope(man, n); 181 1.3 christos return; 182 1.3 christos } else { 183 1.3 christos n = n->parent; 184 1.3 christos continue; 185 1.3 christos } 186 1.3 christos } 187 1.3 christos if (tok != MAN_SH && (n->tok == MAN_SH || 188 1.3 christos (tok != MAN_SS && (n->tok == MAN_SS || 189 1.6 christos man_macro(n->tok)->fp == blk_exp)))) 190 1.2 christos return; 191 1.3 christos man_unscope(man, n); 192 1.3 christos n = man->last; 193 1.1 joerg } 194 1.1 joerg } 195 1.1 joerg 196 1.1 joerg 197 1.2 christos /* 198 1.2 christos * Close out a generic explicit macro. 199 1.2 christos */ 200 1.2 christos void 201 1.1 joerg blk_close(MACRO_PROT_ARGS) 202 1.1 joerg { 203 1.6 christos enum roff_tok ctok, ntok; 204 1.3 christos const struct roff_node *nn; 205 1.6 christos char *p, *ep; 206 1.6 christos int cline, cpos, la, nrew, target; 207 1.1 joerg 208 1.2 christos nrew = 1; 209 1.1 joerg switch (tok) { 210 1.2 christos case MAN_RE: 211 1.1 joerg ntok = MAN_RS; 212 1.6 christos la = *pos; 213 1.2 christos if ( ! man_args(man, line, pos, buf, &p)) 214 1.2 christos break; 215 1.2 christos for (nn = man->last->parent; nn; nn = nn->parent) 216 1.3 christos if (nn->tok == ntok && nn->type == ROFFT_BLOCK) 217 1.2 christos nrew++; 218 1.6 christos target = strtol(p, &ep, 10); 219 1.6 christos if (*ep != '\0') 220 1.6 christos mandoc_msg(MANDOCERR_ARG_EXCESS, line, 221 1.6 christos la + (buf[la] == '"') + (int)(ep - p), 222 1.6 christos "RE ... %s", ep); 223 1.6 christos free(p); 224 1.2 christos if (target == 0) 225 1.2 christos target = 1; 226 1.2 christos nrew -= target; 227 1.2 christos if (nrew < 1) { 228 1.6 christos mandoc_msg(MANDOCERR_RE_NOTOPEN, 229 1.2 christos line, ppos, "RE %d", target); 230 1.2 christos return; 231 1.2 christos } 232 1.2 christos break; 233 1.6 christos case MAN_YS: 234 1.6 christos ntok = MAN_SY; 235 1.6 christos break; 236 1.2 christos case MAN_UE: 237 1.2 christos ntok = MAN_UR; 238 1.1 joerg break; 239 1.5 christos case MAN_ME: 240 1.5 christos ntok = MAN_MT; 241 1.5 christos break; 242 1.1 joerg default: 243 1.1 joerg abort(); 244 1.1 joerg } 245 1.1 joerg 246 1.2 christos for (nn = man->last->parent; nn; nn = nn->parent) 247 1.3 christos if (nn->tok == ntok && nn->type == ROFFT_BLOCK && ! --nrew) 248 1.1 joerg break; 249 1.1 joerg 250 1.2 christos if (nn == NULL) { 251 1.6 christos mandoc_msg(MANDOCERR_BLK_NOTOPEN, 252 1.6 christos line, ppos, "%s", roff_name[tok]); 253 1.3 christos rew_scope(man, MAN_PP); 254 1.6 christos if (tok == MAN_RE) { 255 1.6 christos roff_elem_alloc(man, line, ppos, ROFF_br); 256 1.6 christos man->last->flags |= NODE_LINE | 257 1.6 christos NODE_VALID | NODE_ENDED; 258 1.6 christos man->next = ROFF_NEXT_SIBLING; 259 1.2 christos } 260 1.6 christos return; 261 1.6 christos } 262 1.6 christos 263 1.6 christos cline = man->last->line; 264 1.6 christos cpos = man->last->pos; 265 1.6 christos ctok = man->last->tok; 266 1.6 christos man_unscope(man, nn); 267 1.6 christos 268 1.6 christos if (tok == MAN_RE && nn->head->aux > 0) 269 1.6 christos roff_setreg(man->roff, "an-margin", nn->head->aux, '-'); 270 1.6 christos 271 1.6 christos /* Trailing text. */ 272 1.6 christos 273 1.6 christos if (buf[*pos] != '\0') { 274 1.6 christos roff_word_alloc(man, line, ppos, buf + *pos); 275 1.6 christos man->last->flags |= NODE_DELIMC; 276 1.6 christos if (mandoc_eos(man->last->string, strlen(man->last->string))) 277 1.6 christos man->last->flags |= NODE_EOS; 278 1.6 christos } 279 1.6 christos 280 1.6 christos /* Move a trailing paragraph behind the block. */ 281 1.6 christos 282 1.6 christos if (ctok == MAN_LP || ctok == MAN_PP || ctok == MAN_P) { 283 1.6 christos *pos = strlen(buf); 284 1.6 christos blk_imp(man, ctok, cline, cpos, pos, buf); 285 1.6 christos } 286 1.6 christos 287 1.6 christos /* Synopsis blocks need an explicit end marker for spacing. */ 288 1.6 christos 289 1.6 christos if (tok == MAN_YS && man->last == nn) { 290 1.6 christos roff_elem_alloc(man, line, ppos, tok); 291 1.6 christos man_unscope(man, man->last); 292 1.2 christos } 293 1.2 christos } 294 1.2 christos 295 1.2 christos void 296 1.2 christos blk_exp(MACRO_PROT_ARGS) 297 1.2 christos { 298 1.3 christos struct roff_node *head; 299 1.2 christos char *p; 300 1.2 christos int la; 301 1.1 joerg 302 1.6 christos if (tok == MAN_RS) { 303 1.6 christos rew_scope(man, tok); 304 1.6 christos man->flags |= ROFF_NONOFILL; 305 1.6 christos } 306 1.3 christos roff_block_alloc(man, line, ppos, tok); 307 1.3 christos head = roff_head_alloc(man, line, ppos, tok); 308 1.2 christos 309 1.2 christos la = *pos; 310 1.5 christos if (man_args(man, line, pos, buf, &p)) { 311 1.3 christos roff_word_alloc(man, line, la, p); 312 1.5 christos if (tok == MAN_RS) { 313 1.5 christos if (roff_getreg(man->roff, "an-margin") == 0) 314 1.5 christos roff_setreg(man->roff, "an-margin", 315 1.5 christos 7 * 24, '='); 316 1.5 christos if ((head->aux = strtod(p, NULL) * 24.0) > 0) 317 1.5 christos roff_setreg(man->roff, "an-margin", 318 1.5 christos head->aux, '+'); 319 1.5 christos } 320 1.6 christos free(p); 321 1.5 christos } 322 1.2 christos 323 1.2 christos if (buf[*pos] != '\0') 324 1.6 christos mandoc_msg(MANDOCERR_ARG_EXCESS, line, *pos, 325 1.6 christos "%s ... %s", roff_name[tok], buf + *pos); 326 1.2 christos 327 1.2 christos man_unscope(man, head); 328 1.3 christos roff_body_alloc(man, line, ppos, tok); 329 1.6 christos man->flags &= ~ROFF_NONOFILL; 330 1.1 joerg } 331 1.1 joerg 332 1.1 joerg /* 333 1.3 christos * Parse an implicit-block macro. These contain a ROFFT_HEAD and a 334 1.3 christos * ROFFT_BODY contained within a ROFFT_BLOCK. Rules for closing out other 335 1.1 joerg * scopes, such as `SH' closing out an `SS', are defined in the rew 336 1.1 joerg * routines. 337 1.1 joerg */ 338 1.2 christos void 339 1.1 joerg blk_imp(MACRO_PROT_ARGS) 340 1.1 joerg { 341 1.2 christos int la; 342 1.1 joerg char *p; 343 1.3 christos struct roff_node *n; 344 1.1 joerg 345 1.3 christos rew_scope(man, tok); 346 1.6 christos man->flags |= ROFF_NONOFILL; 347 1.6 christos if (tok == MAN_SH || tok == MAN_SS) 348 1.6 christos man->flags &= ~ROFF_NOFILL; 349 1.6 christos roff_block_alloc(man, line, ppos, tok); 350 1.3 christos n = roff_head_alloc(man, line, ppos, tok); 351 1.1 joerg 352 1.1 joerg /* Add line arguments. */ 353 1.1 joerg 354 1.1 joerg for (;;) { 355 1.1 joerg la = *pos; 356 1.2 christos if ( ! man_args(man, line, pos, buf, &p)) 357 1.1 joerg break; 358 1.3 christos roff_word_alloc(man, line, la, p); 359 1.6 christos free(p); 360 1.1 joerg } 361 1.1 joerg 362 1.3 christos /* 363 1.3 christos * For macros having optional next-line scope, 364 1.3 christos * keep the head open if there were no arguments. 365 1.6 christos * For `TP' and `TQ', always keep the head open. 366 1.3 christos */ 367 1.1 joerg 368 1.6 christos if (man_macro(tok)->flags & MAN_BSCOPED && 369 1.6 christos (tok == MAN_TP || tok == MAN_TQ || n == man->last)) { 370 1.3 christos man->flags |= MAN_BLINE; 371 1.3 christos return; 372 1.1 joerg } 373 1.3 christos 374 1.3 christos /* Close out the head and open the body. */ 375 1.3 christos 376 1.3 christos man_unscope(man, n); 377 1.3 christos roff_body_alloc(man, line, ppos, tok); 378 1.6 christos man->flags &= ~ROFF_NONOFILL; 379 1.1 joerg } 380 1.1 joerg 381 1.2 christos void 382 1.1 joerg in_line_eoln(MACRO_PROT_ARGS) 383 1.1 joerg { 384 1.2 christos int la; 385 1.1 joerg char *p; 386 1.3 christos struct roff_node *n; 387 1.1 joerg 388 1.3 christos roff_elem_alloc(man, line, ppos, tok); 389 1.2 christos n = man->last; 390 1.1 joerg 391 1.6 christos if (tok == MAN_EX) 392 1.6 christos man->flags |= ROFF_NOFILL; 393 1.6 christos else if (tok == MAN_EE) 394 1.6 christos man->flags &= ~ROFF_NOFILL; 395 1.6 christos 396 1.1 joerg for (;;) { 397 1.5 christos if (buf[*pos] != '\0' && man->last != n && tok == MAN_PD) { 398 1.6 christos mandoc_msg(MANDOCERR_ARG_EXCESS, line, *pos, 399 1.6 christos "%s ... %s", roff_name[tok], buf + *pos); 400 1.2 christos break; 401 1.2 christos } 402 1.1 joerg la = *pos; 403 1.2 christos if ( ! man_args(man, line, pos, buf, &p)) 404 1.2 christos break; 405 1.6 christos if (man_macro(tok)->flags & MAN_JOIN && 406 1.3 christos man->last->type == ROFFT_TEXT) 407 1.3 christos roff_word_append(man, p); 408 1.2 christos else 409 1.3 christos roff_word_alloc(man, line, la, p); 410 1.6 christos free(p); 411 1.2 christos } 412 1.1 joerg 413 1.2 christos /* 414 1.4 christos * Append NODE_EOS in case the last snipped argument 415 1.2 christos * ends with a dot, e.g. `.IR syslog (3).' 416 1.2 christos */ 417 1.2 christos 418 1.2 christos if (n != man->last && 419 1.2 christos mandoc_eos(man->last->string, strlen(man->last->string))) 420 1.4 christos man->last->flags |= NODE_EOS; 421 1.2 christos 422 1.2 christos /* 423 1.6 christos * If no arguments are specified and this is MAN_ESCOPED (i.e., 424 1.2 christos * next-line scoped), then set our mode to indicate that we're 425 1.2 christos * waiting for terms to load into our context. 426 1.2 christos */ 427 1.1 joerg 428 1.6 christos if (n == man->last && man_macro(tok)->flags & MAN_ESCOPED) { 429 1.2 christos man->flags |= MAN_ELINE; 430 1.2 christos return; 431 1.1 joerg } 432 1.1 joerg 433 1.3 christos assert(man->last->type != ROFFT_ROOT); 434 1.3 christos man->next = ROFF_NEXT_SIBLING; 435 1.1 joerg 436 1.3 christos /* Rewind our element scope. */ 437 1.1 joerg 438 1.2 christos for ( ; man->last; man->last = man->last->parent) { 439 1.6 christos man->last->flags |= NODE_VALID; 440 1.2 christos if (man->last == n) 441 1.1 joerg break; 442 1.1 joerg } 443 1.6 christos 444 1.6 christos /* Rewind next-line scoped ancestors, if any. */ 445 1.6 christos 446 1.6 christos if (man_macro(tok)->flags & MAN_ESCOPED) 447 1.6 christos man_descope(man, line, ppos, NULL); 448 1.2 christos } 449 1.2 christos 450 1.2 christos void 451 1.3 christos man_endparse(struct roff_man *man) 452 1.2 christos { 453 1.6 christos man_unscope(man, man->meta.first); 454 1.1 joerg } 455 1.1 joerg 456 1.2 christos static int 457 1.3 christos man_args(struct roff_man *man, int line, int *pos, char *buf, char **v) 458 1.1 joerg { 459 1.2 christos char *start; 460 1.1 joerg 461 1.2 christos assert(*pos); 462 1.2 christos *v = start = buf + *pos; 463 1.2 christos assert(' ' != *start); 464 1.1 joerg 465 1.2 christos if ('\0' == *start) 466 1.3 christos return 0; 467 1.1 joerg 468 1.6 christos *v = roff_getarg(man->roff, v, line, pos); 469 1.3 christos return 1; 470 1.1 joerg } 471