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