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