1 /* $Id: mdoc.c,v 1.275 2020/04/06 10:16:17 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2010, 2012-2018, 2020 Ingo Schwarze <schwarze@openbsd.org> 4 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 * Top level and utility functions of the mdoc(7) parser for mandoc(1). 19 */ 20 #include "config.h" 21 22 #include <sys/types.h> 23 24 #include <assert.h> 25 #include <ctype.h> 26 #include <stdarg.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <time.h> 31 32 #include "mandoc_aux.h" 33 #include "mandoc.h" 34 #include "roff.h" 35 #include "mdoc.h" 36 #include "libmandoc.h" 37 #include "roff_int.h" 38 #include "libmdoc.h" 39 40 const char *const __mdoc_argnames[MDOC_ARG_MAX] = { 41 "split", "nosplit", "ragged", 42 "unfilled", "literal", "file", 43 "offset", "bullet", "dash", 44 "hyphen", "item", "enum", 45 "tag", "diag", "hang", 46 "ohang", "inset", "column", 47 "width", "compact", "std", 48 "filled", "words", "emphasis", 49 "symbolic", "nested", "centered" 50 }; 51 const char * const *mdoc_argnames = __mdoc_argnames; 52 53 static int mdoc_ptext(struct roff_man *, int, char *, int); 54 static int mdoc_pmacro(struct roff_man *, int, char *, int); 55 56 57 /* 58 * Main parse routine. Parses a single line -- really just hands off to 59 * the macro (mdoc_pmacro()) or text parser (mdoc_ptext()). 60 */ 61 int 62 mdoc_parseln(struct roff_man *mdoc, int ln, char *buf, int offs) 63 { 64 65 if (mdoc->last->type != ROFFT_EQN || ln > mdoc->last->line) 66 mdoc->flags |= MDOC_NEWLINE; 67 68 /* 69 * Let the roff nS register switch SYNOPSIS mode early, 70 * such that the parser knows at all times 71 * whether this mode is on or off. 72 * Note that this mode is also switched by the Sh macro. 73 */ 74 if (roff_getreg(mdoc->roff, "nS")) 75 mdoc->flags |= MDOC_SYNOPSIS; 76 else 77 mdoc->flags &= ~MDOC_SYNOPSIS; 78 79 return roff_getcontrol(mdoc->roff, buf, &offs) ? 80 mdoc_pmacro(mdoc, ln, buf, offs) : 81 mdoc_ptext(mdoc, ln, buf, offs); 82 } 83 84 void 85 mdoc_tail_alloc(struct roff_man *mdoc, int line, int pos, enum roff_tok tok) 86 { 87 struct roff_node *p; 88 89 p = roff_node_alloc(mdoc, line, pos, ROFFT_TAIL, tok); 90 roff_node_append(mdoc, p); 91 mdoc->next = ROFF_NEXT_CHILD; 92 } 93 94 struct roff_node * 95 mdoc_endbody_alloc(struct roff_man *mdoc, int line, int pos, 96 enum roff_tok tok, struct roff_node *body) 97 { 98 struct roff_node *p; 99 100 body->flags |= NODE_ENDED; 101 body->parent->flags |= NODE_ENDED; 102 p = roff_node_alloc(mdoc, line, pos, ROFFT_BODY, tok); 103 p->body = body; 104 p->norm = body->norm; 105 p->end = ENDBODY_SPACE; 106 roff_node_append(mdoc, p); 107 mdoc->next = ROFF_NEXT_SIBLING; 108 return p; 109 } 110 111 struct roff_node * 112 mdoc_block_alloc(struct roff_man *mdoc, int line, int pos, 113 enum roff_tok tok, struct mdoc_arg *args) 114 { 115 struct roff_node *p; 116 117 p = roff_node_alloc(mdoc, line, pos, ROFFT_BLOCK, tok); 118 p->args = args; 119 if (p->args) 120 (args->refcnt)++; 121 122 switch (tok) { 123 case MDOC_Bd: 124 case MDOC_Bf: 125 case MDOC_Bl: 126 case MDOC_En: 127 case MDOC_Rs: 128 p->norm = mandoc_calloc(1, sizeof(union mdoc_data)); 129 break; 130 default: 131 break; 132 } 133 roff_node_append(mdoc, p); 134 mdoc->next = ROFF_NEXT_CHILD; 135 return p; 136 } 137 138 void 139 mdoc_elem_alloc(struct roff_man *mdoc, int line, int pos, 140 enum roff_tok tok, struct mdoc_arg *args) 141 { 142 struct roff_node *p; 143 144 p = roff_node_alloc(mdoc, line, pos, ROFFT_ELEM, tok); 145 p->args = args; 146 if (p->args) 147 (args->refcnt)++; 148 149 switch (tok) { 150 case MDOC_An: 151 p->norm = mandoc_calloc(1, sizeof(union mdoc_data)); 152 break; 153 default: 154 break; 155 } 156 roff_node_append(mdoc, p); 157 mdoc->next = ROFF_NEXT_CHILD; 158 } 159 160 /* 161 * Parse free-form text, that is, a line that does not begin with the 162 * control character. 163 */ 164 static int 165 mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs) 166 { 167 struct roff_node *n; 168 const char *cp, *sp; 169 char *c, *ws, *end; 170 171 n = mdoc->last; 172 173 /* 174 * If a column list contains plain text, assume an implicit item 175 * macro. This can happen one or more times at the beginning 176 * of such a list, intermixed with non-It mdoc macros and with 177 * nodes generated on the roff level, for example by tbl. 178 */ 179 180 if ((n->tok == MDOC_Bl && n->type == ROFFT_BODY && 181 n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) || 182 (n->parent != NULL && n->parent->tok == MDOC_Bl && 183 n->parent->norm->Bl.type == LIST_column)) { 184 mdoc->flags |= MDOC_FREECOL; 185 (*mdoc_macro(MDOC_It)->fp)(mdoc, MDOC_It, 186 line, offs, &offs, buf); 187 return 1; 188 } 189 190 /* 191 * Search for the beginning of unescaped trailing whitespace (ws) 192 * and for the first character not to be output (end). 193 */ 194 195 /* FIXME: replace with strcspn(). */ 196 ws = NULL; 197 for (c = end = buf + offs; *c; c++) { 198 switch (*c) { 199 case ' ': 200 if (NULL == ws) 201 ws = c; 202 continue; 203 case '\t': 204 /* 205 * Always warn about trailing tabs, 206 * even outside literal context, 207 * where they should be put on the next line. 208 */ 209 if (NULL == ws) 210 ws = c; 211 /* 212 * Strip trailing tabs in literal context only; 213 * outside, they affect the next line. 214 */ 215 if (mdoc->flags & ROFF_NOFILL) 216 continue; 217 break; 218 case '\\': 219 /* Skip the escaped character, too, if any. */ 220 if (c[1]) 221 c++; 222 /* FALLTHROUGH */ 223 default: 224 ws = NULL; 225 break; 226 } 227 end = c + 1; 228 } 229 *end = '\0'; 230 231 if (ws) 232 mandoc_msg(MANDOCERR_SPACE_EOL, line, (int)(ws - buf), NULL); 233 234 /* 235 * Blank lines are allowed in no-fill mode 236 * and cancel preceding \c, 237 * but add a single vertical space elsewhere. 238 */ 239 240 if (buf[offs] == '\0' && (mdoc->flags & ROFF_NOFILL) == 0) { 241 switch (mdoc->last->type) { 242 case ROFFT_TEXT: 243 sp = mdoc->last->string; 244 cp = end = strchr(sp, '\0') - 2; 245 if (cp < sp || cp[0] != '\\' || cp[1] != 'c') 246 break; 247 while (cp > sp && cp[-1] == '\\') 248 cp--; 249 if ((end - cp) % 2) 250 break; 251 *end = '\0'; 252 return 1; 253 default: 254 break; 255 } 256 mandoc_msg(MANDOCERR_FI_BLANK, line, (int)(c - buf), NULL); 257 roff_elem_alloc(mdoc, line, offs, ROFF_sp); 258 mdoc->last->flags |= NODE_VALID | NODE_ENDED; 259 mdoc->next = ROFF_NEXT_SIBLING; 260 return 1; 261 } 262 263 roff_word_alloc(mdoc, line, offs, buf+offs); 264 265 if (mdoc->flags & ROFF_NOFILL) 266 return 1; 267 268 /* 269 * End-of-sentence check. If the last character is an unescaped 270 * EOS character, then flag the node as being the end of a 271 * sentence. The front-end will know how to interpret this. 272 */ 273 274 assert(buf < end); 275 276 if (mandoc_eos(buf+offs, (size_t)(end-buf-offs))) 277 mdoc->last->flags |= NODE_EOS; 278 279 for (c = buf + offs; c != NULL; c = strchr(c + 1, '.')) { 280 if (c - buf < offs + 2) 281 continue; 282 if (end - c < 3) 283 break; 284 if (c[1] != ' ' || 285 isalnum((unsigned char)c[-2]) == 0 || 286 isalnum((unsigned char)c[-1]) == 0 || 287 (c[-2] == 'n' && c[-1] == 'c') || 288 (c[-2] == 'v' && c[-1] == 's')) 289 continue; 290 c += 2; 291 if (*c == ' ') 292 c++; 293 if (*c == ' ') 294 c++; 295 if (isupper((unsigned char)(*c))) 296 mandoc_msg(MANDOCERR_EOS, line, (int)(c - buf), NULL); 297 } 298 299 return 1; 300 } 301 302 /* 303 * Parse a macro line, that is, a line beginning with the control 304 * character. 305 */ 306 static int 307 mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs) 308 { 309 struct roff_node *n; 310 const char *cp; 311 size_t sz; 312 enum roff_tok tok; 313 int sv; 314 315 /* Determine the line macro. */ 316 317 sv = offs; 318 tok = TOKEN_NONE; 319 for (sz = 0; sz < 4 && strchr(" \t\\", buf[offs]) == NULL; sz++) 320 offs++; 321 if (sz == 2 || sz == 3) 322 tok = roffhash_find(mdoc->mdocmac, buf + sv, sz); 323 if (tok == TOKEN_NONE) { 324 mandoc_msg(MANDOCERR_MACRO, ln, sv, "%s", buf + sv - 1); 325 return 1; 326 } 327 328 /* Skip a leading escape sequence or tab. */ 329 330 switch (buf[offs]) { 331 case '\\': 332 cp = buf + offs + 1; 333 mandoc_escape(&cp, NULL, NULL); 334 offs = cp - buf; 335 break; 336 case '\t': 337 offs++; 338 break; 339 default: 340 break; 341 } 342 343 /* Jump to the next non-whitespace word. */ 344 345 while (buf[offs] == ' ') 346 offs++; 347 348 /* 349 * Trailing whitespace. Note that tabs are allowed to be passed 350 * into the parser as "text", so we only warn about spaces here. 351 */ 352 353 if ('\0' == buf[offs] && ' ' == buf[offs - 1]) 354 mandoc_msg(MANDOCERR_SPACE_EOL, ln, offs - 1, NULL); 355 356 /* 357 * If an initial or transparent macro or a list invocation, 358 * divert directly into macro processing. 359 */ 360 361 n = mdoc->last; 362 if (n == NULL || tok == MDOC_It || tok == MDOC_El || 363 roff_tok_transparent(tok)) { 364 (*mdoc_macro(tok)->fp)(mdoc, tok, ln, sv, &offs, buf); 365 return 1; 366 } 367 368 /* 369 * If a column list contains a non-It macro, assume an implicit 370 * item macro. This can happen one or more times at the 371 * beginning of such a list, intermixed with text lines and 372 * with nodes generated on the roff level, for example by tbl. 373 */ 374 375 if ((n->tok == MDOC_Bl && n->type == ROFFT_BODY && 376 n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) || 377 (n->parent != NULL && n->parent->tok == MDOC_Bl && 378 n->parent->norm->Bl.type == LIST_column)) { 379 mdoc->flags |= MDOC_FREECOL; 380 (*mdoc_macro(MDOC_It)->fp)(mdoc, MDOC_It, ln, sv, &sv, buf); 381 return 1; 382 } 383 384 /* Normal processing of a macro. */ 385 386 (*mdoc_macro(tok)->fp)(mdoc, tok, ln, sv, &offs, buf); 387 388 /* In quick mode (for mandocdb), abort after the NAME section. */ 389 390 if (mdoc->quick && MDOC_Sh == tok && 391 SEC_NAME != mdoc->last->sec) 392 return 2; 393 394 return 1; 395 } 396 397 enum mdelim 398 mdoc_isdelim(const char *p) 399 { 400 401 if ('\0' == p[0]) 402 return DELIM_NONE; 403 404 if ('\0' == p[1]) 405 switch (p[0]) { 406 case '(': 407 case '[': 408 return DELIM_OPEN; 409 case '|': 410 return DELIM_MIDDLE; 411 case '.': 412 case ',': 413 case ';': 414 case ':': 415 case '?': 416 case '!': 417 case ')': 418 case ']': 419 return DELIM_CLOSE; 420 default: 421 return DELIM_NONE; 422 } 423 424 if ('\\' != p[0]) 425 return DELIM_NONE; 426 427 if (0 == strcmp(p + 1, ".")) 428 return DELIM_CLOSE; 429 if (0 == strcmp(p + 1, "fR|\\fP")) 430 return DELIM_MIDDLE; 431 432 return DELIM_NONE; 433 } 434