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