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