1 /* $Id: mandoc.c,v 1.98 2015/11/12 22:44:27 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2011-2015 Ingo Schwarze <schwarze@openbsd.org> 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 #include "config.h" 19 20 #include <sys/types.h> 21 22 #include <assert.h> 23 #include <ctype.h> 24 #include <errno.h> 25 #include <limits.h> 26 #include <stdlib.h> 27 #include <stdio.h> 28 #include <string.h> 29 #include <time.h> 30 31 #include "mandoc.h" 32 #include "mandoc_aux.h" 33 #include "libmandoc.h" 34 35 static int a2time(time_t *, const char *, const char *); 36 static char *time2a(time_t); 37 38 39 enum mandoc_esc 40 mandoc_escape(const char **end, const char **start, int *sz) 41 { 42 const char *local_start; 43 int local_sz; 44 char term; 45 enum mandoc_esc gly; 46 47 /* 48 * When the caller doesn't provide return storage, 49 * use local storage. 50 */ 51 52 if (NULL == start) 53 start = &local_start; 54 if (NULL == sz) 55 sz = &local_sz; 56 57 /* 58 * Beyond the backslash, at least one input character 59 * is part of the escape sequence. With one exception 60 * (see below), that character won't be returned. 61 */ 62 63 gly = ESCAPE_ERROR; 64 *start = ++*end; 65 *sz = 0; 66 term = '\0'; 67 68 switch ((*start)[-1]) { 69 /* 70 * First the glyphs. There are several different forms of 71 * these, but each eventually returns a substring of the glyph 72 * name. 73 */ 74 case '(': 75 gly = ESCAPE_SPECIAL; 76 *sz = 2; 77 break; 78 case '[': 79 gly = ESCAPE_SPECIAL; 80 term = ']'; 81 break; 82 case 'C': 83 if ('\'' != **start) 84 return ESCAPE_ERROR; 85 *start = ++*end; 86 gly = ESCAPE_SPECIAL; 87 term = '\''; 88 break; 89 90 /* 91 * Escapes taking no arguments at all. 92 */ 93 case 'd': 94 case 'u': 95 case ',': 96 case '/': 97 return ESCAPE_IGNORE; 98 99 /* 100 * The \z escape is supposed to output the following 101 * character without advancing the cursor position. 102 * Since we are mostly dealing with terminal mode, 103 * let us just skip the next character. 104 */ 105 case 'z': 106 return ESCAPE_SKIPCHAR; 107 108 /* 109 * Handle all triggers matching \X(xy, \Xx, and \X[xxxx], where 110 * 'X' is the trigger. These have opaque sub-strings. 111 */ 112 case 'F': 113 case 'g': 114 case 'k': 115 case 'M': 116 case 'm': 117 case 'n': 118 case 'V': 119 case 'Y': 120 gly = ESCAPE_IGNORE; 121 /* FALLTHROUGH */ 122 case 'f': 123 if (ESCAPE_ERROR == gly) 124 gly = ESCAPE_FONT; 125 switch (**start) { 126 case '(': 127 *start = ++*end; 128 *sz = 2; 129 break; 130 case '[': 131 *start = ++*end; 132 term = ']'; 133 break; 134 default: 135 *sz = 1; 136 break; 137 } 138 break; 139 140 /* 141 * These escapes are of the form \X'Y', where 'X' is the trigger 142 * and 'Y' is any string. These have opaque sub-strings. 143 * The \B and \w escapes are handled in roff.c, roff_res(). 144 */ 145 case 'A': 146 case 'b': 147 case 'D': 148 case 'R': 149 case 'X': 150 case 'Z': 151 gly = ESCAPE_IGNORE; 152 /* FALLTHROUGH */ 153 case 'o': 154 if (**start == '\0') 155 return ESCAPE_ERROR; 156 if (gly == ESCAPE_ERROR) 157 gly = ESCAPE_OVERSTRIKE; 158 term = **start; 159 *start = ++*end; 160 break; 161 162 /* 163 * These escapes are of the form \X'N', where 'X' is the trigger 164 * and 'N' resolves to a numerical expression. 165 */ 166 case 'h': 167 case 'H': 168 case 'L': 169 case 'l': 170 case 'S': 171 case 'v': 172 case 'x': 173 if (strchr(" %&()*+-./0123456789:<=>", **start)) { 174 if ('\0' != **start) 175 ++*end; 176 return ESCAPE_ERROR; 177 } 178 gly = ESCAPE_IGNORE; 179 term = **start; 180 *start = ++*end; 181 break; 182 183 /* 184 * Special handling for the numbered character escape. 185 * XXX Do any other escapes need similar handling? 186 */ 187 case 'N': 188 if ('\0' == **start) 189 return ESCAPE_ERROR; 190 (*end)++; 191 if (isdigit((unsigned char)**start)) { 192 *sz = 1; 193 return ESCAPE_IGNORE; 194 } 195 (*start)++; 196 while (isdigit((unsigned char)**end)) 197 (*end)++; 198 *sz = *end - *start; 199 if ('\0' != **end) 200 (*end)++; 201 return ESCAPE_NUMBERED; 202 203 /* 204 * Sizes get a special category of their own. 205 */ 206 case 's': 207 gly = ESCAPE_IGNORE; 208 209 /* See +/- counts as a sign. */ 210 if ('+' == **end || '-' == **end || ASCII_HYPH == **end) 211 *start = ++*end; 212 213 switch (**end) { 214 case '(': 215 *start = ++*end; 216 *sz = 2; 217 break; 218 case '[': 219 *start = ++*end; 220 term = ']'; 221 break; 222 case '\'': 223 *start = ++*end; 224 term = '\''; 225 break; 226 case '3': 227 case '2': 228 case '1': 229 *sz = (*end)[-1] == 's' && 230 isdigit((unsigned char)(*end)[1]) ? 2 : 1; 231 break; 232 default: 233 *sz = 1; 234 break; 235 } 236 237 break; 238 239 /* 240 * Anything else is assumed to be a glyph. 241 * In this case, pass back the character after the backslash. 242 */ 243 default: 244 gly = ESCAPE_SPECIAL; 245 *start = --*end; 246 *sz = 1; 247 break; 248 } 249 250 assert(ESCAPE_ERROR != gly); 251 252 /* 253 * Read up to the terminating character, 254 * paying attention to nested escapes. 255 */ 256 257 if ('\0' != term) { 258 while (**end != term) { 259 switch (**end) { 260 case '\0': 261 return ESCAPE_ERROR; 262 case '\\': 263 (*end)++; 264 if (ESCAPE_ERROR == 265 mandoc_escape(end, NULL, NULL)) 266 return ESCAPE_ERROR; 267 break; 268 default: 269 (*end)++; 270 break; 271 } 272 } 273 *sz = (*end)++ - *start; 274 } else { 275 assert(*sz > 0); 276 if ((size_t)*sz > strlen(*start)) 277 return ESCAPE_ERROR; 278 *end += *sz; 279 } 280 281 /* Run post-processors. */ 282 283 switch (gly) { 284 case ESCAPE_FONT: 285 if (2 == *sz) { 286 if ('C' == **start) { 287 /* 288 * Treat constant-width font modes 289 * just like regular font modes. 290 */ 291 (*start)++; 292 (*sz)--; 293 } else { 294 if ('B' == (*start)[0] && 'I' == (*start)[1]) 295 gly = ESCAPE_FONTBI; 296 break; 297 } 298 } else if (1 != *sz) 299 break; 300 301 switch (**start) { 302 case '3': 303 case 'B': 304 gly = ESCAPE_FONTBOLD; 305 break; 306 case '2': 307 case 'I': 308 gly = ESCAPE_FONTITALIC; 309 break; 310 case 'P': 311 gly = ESCAPE_FONTPREV; 312 break; 313 case '1': 314 case 'R': 315 gly = ESCAPE_FONTROMAN; 316 break; 317 } 318 break; 319 case ESCAPE_SPECIAL: 320 if (1 == *sz && 'c' == **start) 321 gly = ESCAPE_NOSPACE; 322 /* 323 * Unicode escapes are defined in groff as \[u0000] 324 * to \[u10FFFF], where the contained value must be 325 * a valid Unicode codepoint. Here, however, only 326 * check the length and range. 327 */ 328 if (**start != 'u' || *sz < 5 || *sz > 7) 329 break; 330 if (*sz == 7 && ((*start)[1] != '1' || (*start)[2] != '0')) 331 break; 332 if (*sz == 6 && (*start)[1] == '0') 333 break; 334 if (*sz == 5 && (*start)[1] == 'D' && 335 strchr("89ABCDEF", (*start)[2]) != NULL) 336 break; 337 if ((int)strspn(*start + 1, "0123456789ABCDEFabcdef") 338 + 1 == *sz) 339 gly = ESCAPE_UNICODE; 340 break; 341 default: 342 break; 343 } 344 345 return gly; 346 } 347 348 /* 349 * Parse a quoted or unquoted roff-style request or macro argument. 350 * Return a pointer to the parsed argument, which is either the original 351 * pointer or advanced by one byte in case the argument is quoted. 352 * NUL-terminate the argument in place. 353 * Collapse pairs of quotes inside quoted arguments. 354 * Advance the argument pointer to the next argument, 355 * or to the NUL byte terminating the argument line. 356 */ 357 char * 358 mandoc_getarg(struct mparse *parse, char **cpp, int ln, int *pos) 359 { 360 char *start, *cp; 361 int quoted, pairs, white; 362 363 /* Quoting can only start with a new word. */ 364 start = *cpp; 365 quoted = 0; 366 if ('"' == *start) { 367 quoted = 1; 368 start++; 369 } 370 371 pairs = 0; 372 white = 0; 373 for (cp = start; '\0' != *cp; cp++) { 374 375 /* 376 * Move the following text left 377 * after quoted quotes and after "\\" and "\t". 378 */ 379 if (pairs) 380 cp[-pairs] = cp[0]; 381 382 if ('\\' == cp[0]) { 383 /* 384 * In copy mode, translate double to single 385 * backslashes and backslash-t to literal tabs. 386 */ 387 switch (cp[1]) { 388 case 't': 389 cp[0] = '\t'; 390 /* FALLTHROUGH */ 391 case '\\': 392 pairs++; 393 cp++; 394 break; 395 case ' ': 396 /* Skip escaped blanks. */ 397 if (0 == quoted) 398 cp++; 399 break; 400 default: 401 break; 402 } 403 } else if (0 == quoted) { 404 if (' ' == cp[0]) { 405 /* Unescaped blanks end unquoted args. */ 406 white = 1; 407 break; 408 } 409 } else if ('"' == cp[0]) { 410 if ('"' == cp[1]) { 411 /* Quoted quotes collapse. */ 412 pairs++; 413 cp++; 414 } else { 415 /* Unquoted quotes end quoted args. */ 416 quoted = 2; 417 break; 418 } 419 } 420 } 421 422 /* Quoted argument without a closing quote. */ 423 if (1 == quoted) 424 mandoc_msg(MANDOCERR_ARG_QUOTE, parse, ln, *pos, NULL); 425 426 /* NUL-terminate this argument and move to the next one. */ 427 if (pairs) 428 cp[-pairs] = '\0'; 429 if ('\0' != *cp) { 430 *cp++ = '\0'; 431 while (' ' == *cp) 432 cp++; 433 } 434 *pos += (int)(cp - start) + (quoted ? 1 : 0); 435 *cpp = cp; 436 437 if ('\0' == *cp && (white || ' ' == cp[-1])) 438 mandoc_msg(MANDOCERR_SPACE_EOL, parse, ln, *pos, NULL); 439 440 return start; 441 } 442 443 static int 444 a2time(time_t *t, const char *fmt, const char *p) 445 { 446 struct tm tm; 447 char *pp; 448 449 memset(&tm, 0, sizeof(struct tm)); 450 451 pp = NULL; 452 #if HAVE_STRPTIME 453 pp = strptime(p, fmt, &tm); 454 #endif 455 if (NULL != pp && '\0' == *pp) { 456 *t = mktime(&tm); 457 return 1; 458 } 459 460 return 0; 461 } 462 463 static char * 464 time2a(time_t t) 465 { 466 struct tm *tm; 467 char *buf, *p; 468 size_t ssz; 469 int isz; 470 471 tm = localtime(&t); 472 if (tm == NULL) 473 return NULL; 474 475 /* 476 * Reserve space: 477 * up to 9 characters for the month (September) + blank 478 * up to 2 characters for the day + comma + blank 479 * 4 characters for the year and a terminating '\0' 480 */ 481 482 p = buf = mandoc_malloc(10 + 4 + 4 + 1); 483 484 if ((ssz = strftime(p, 10 + 1, "%B ", tm)) == 0) 485 goto fail; 486 p += (int)ssz; 487 488 /* 489 * The output format is just "%d" here, not "%2d" or "%02d". 490 * That's also the reason why we can't just format the 491 * date as a whole with "%B %e, %Y" or "%B %d, %Y". 492 * Besides, the present approach is less prone to buffer 493 * overflows, in case anybody should ever introduce the bug 494 * of looking at LC_TIME. 495 */ 496 497 if ((isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday)) == -1) 498 goto fail; 499 p += isz; 500 501 if (strftime(p, 4 + 1, "%Y", tm) == 0) 502 goto fail; 503 return buf; 504 505 fail: 506 free(buf); 507 return NULL; 508 } 509 510 char * 511 mandoc_normdate(struct mparse *parse, char *in, int ln, int pos) 512 { 513 time_t t; 514 515 /* No date specified: use today's date. */ 516 517 if (in == NULL || *in == '\0' || strcmp(in, "$" "Mdocdate$") == 0) { 518 mandoc_msg(MANDOCERR_DATE_MISSING, parse, ln, pos, NULL); 519 return time2a(time(NULL)); 520 } 521 522 /* Valid mdoc(7) date format. */ 523 524 if (a2time(&t, "$" "Mdocdate: %b %d %Y $", in) || 525 a2time(&t, "%b %d, %Y", in)) 526 return time2a(t); 527 528 /* Do not warn about the legacy man(7) format. */ 529 530 if ( ! a2time(&t, "%Y-%m-%d", in)) 531 mandoc_msg(MANDOCERR_DATE_BAD, parse, ln, pos, in); 532 533 /* Use any non-mdoc(7) date verbatim. */ 534 535 return mandoc_strdup(in); 536 } 537 538 int 539 mandoc_eos(const char *p, size_t sz) 540 { 541 const char *q; 542 int enclosed, found; 543 544 if (0 == sz) 545 return 0; 546 547 /* 548 * End-of-sentence recognition must include situations where 549 * some symbols, such as `)', allow prior EOS punctuation to 550 * propagate outward. 551 */ 552 553 enclosed = found = 0; 554 for (q = p + (int)sz - 1; q >= p; q--) { 555 switch (*q) { 556 case '\"': 557 case '\'': 558 case ']': 559 case ')': 560 if (0 == found) 561 enclosed = 1; 562 break; 563 case '.': 564 case '!': 565 case '?': 566 found = 1; 567 break; 568 default: 569 return found && 570 (!enclosed || isalnum((unsigned char)*q)); 571 } 572 } 573 574 return found && !enclosed; 575 } 576 577 /* 578 * Convert a string to a long that may not be <0. 579 * If the string is invalid, or is less than 0, return -1. 580 */ 581 int 582 mandoc_strntoi(const char *p, size_t sz, int base) 583 { 584 char buf[32]; 585 char *ep; 586 long v; 587 588 if (sz > 31) 589 return -1; 590 591 memcpy(buf, p, sz); 592 buf[(int)sz] = '\0'; 593 594 errno = 0; 595 v = strtol(buf, &ep, base); 596 597 if (buf[0] == '\0' || *ep != '\0') 598 return -1; 599 600 if (v > INT_MAX) 601 v = INT_MAX; 602 if (v < INT_MIN) 603 v = INT_MIN; 604 605 return (int)v; 606 } 607