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