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