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