Annotation of texi2mdoc/util.c, Revision 1.1
1.1 ! kristaps 1: /* $Id: main.c,v 1.23 2015/02/19 20:55:56 kristaps Exp $ */
! 2: /*
! 3: * Copyright (c) 2015 Kristaps Dzonsons <kristaps@bsd.lv>
! 4: *
! 5: * Permission to use, copy, modify, and distribute this software for any
! 6: * purpose with or without fee is hereby granted, provided that the above
! 7: * copyright notice and this permission notice appear in all copies.
! 8: *
! 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 16: */
! 17: #include <sys/mman.h>
! 18: #include <sys/stat.h>
! 19:
! 20: #include <assert.h>
! 21: #include <ctype.h>
! 22: #include <fcntl.h>
! 23: #include <getopt.h>
! 24: #include <libgen.h>
! 25: #include <limits.h>
! 26: #include <stdarg.h>
! 27: #include <stdio.h>
! 28: #include <stdlib.h>
! 29: #include <string.h>
! 30: #include <time.h>
! 31: #include <unistd.h>
! 32:
! 33: #include "extern.h"
! 34:
! 35: /*
! 36: * Unmap the top-most file in the stack of files currently opened (that
! 37: * is, nested calls to parsefile()).
! 38: */
! 39: void
! 40: texifilepop(struct texi *p)
! 41: {
! 42: struct texifile *f;
! 43:
! 44: assert(p->filepos > 0);
! 45: f = &p->files[--p->filepos];
! 46: munmap(f->map, f->mapsz);
! 47: }
! 48:
! 49: /*
! 50: * Unmap all files that we're currently using and free all resources
! 51: * that we've allocated during the parse.
! 52: * The utility should exit(...) after this is called.
! 53: */
! 54: void
! 55: texiexit(struct texi *p)
! 56: {
! 57: size_t i;
! 58:
! 59: /* Make sure we're newline-terminated. */
! 60: if (p->outcol)
! 61: putchar('\n');
! 62:
! 63: /* Unmap all files. */
! 64: while (p->filepos > 0)
! 65: texifilepop(p);
! 66:
! 67: for (i = 0; i < p->dirsz; i++)
! 68: free(p->dirs[i]);
! 69:
! 70: for (i = 0; i < p->valsz; i++) {
! 71: free(p->vals[i].value);
! 72: free(p->vals[i].key);
! 73: }
! 74:
! 75: free(p->vals);
! 76: free(p->dirs);
! 77: free(p->subtitle);
! 78: free(p->title);
! 79: }
! 80:
! 81: /*
! 82: * Fatal error: unmap all files and exit.
! 83: * The "errstring" is passed to perror(3).
! 84: */
! 85: void
! 86: texiabort(struct texi *p, const char *errstring)
! 87: {
! 88:
! 89: perror(errstring);
! 90: texiexit(p);
! 91: exit(EXIT_FAILURE);
! 92: }
! 93:
! 94: /*
! 95: * Print a generic warning message (to stderr) tied to our current
! 96: * location in the parse sequence.
! 97: */
! 98: void
! 99: texiwarn(const struct texi *p, const char *fmt, ...)
! 100: {
! 101: va_list ap;
! 102:
! 103: fprintf(stderr, "%s:%zu:%zu: warning: ",
! 104: p->files[p->filepos - 1].name,
! 105: p->files[p->filepos - 1].line + 1,
! 106: p->files[p->filepos - 1].col + 1);
! 107: va_start(ap, fmt);
! 108: vfprintf(stderr, fmt, ap);
! 109: va_end(ap);
! 110: fputc('\n', stderr);
! 111: }
! 112:
! 113: /*
! 114: * Print an error message (to stderr) tied to our current location in
! 115: * the parse sequence, invoke texiexit(), then die.
! 116: */
! 117: void
! 118: texierr(struct texi *p, const char *fmt, ...)
! 119: {
! 120: va_list ap;
! 121:
! 122: fprintf(stderr, "%s:%zu:%zu: error: ",
! 123: p->files[p->filepos - 1].name,
! 124: p->files[p->filepos - 1].line + 1,
! 125: p->files[p->filepos - 1].col + 1);
! 126: va_start(ap, fmt);
! 127: vfprintf(stderr, fmt, ap);
! 128: va_end(ap);
! 129: fputc('\n', stderr);
! 130: texiexit(p);
! 131: exit(EXIT_FAILURE);
! 132: }
! 133:
! 134: /*
! 135: * Put a single data character to the output if we're not ignoring.
! 136: * Makes sure we don't spurriously start a macro.
! 137: * Adjusts our output status.
! 138: * This shouldn't be called for macros: just for ordinary text.
! 139: */
! 140: void
! 141: texiputchar(struct texi *p, char c)
! 142: {
! 143:
! 144: if (p->ign)
! 145: return;
! 146:
! 147: if ('.' == c && 0 == p->outcol)
! 148: fputs("\\&", stdout);
! 149:
! 150: putchar(c);
! 151: p->seenvs = 0;
! 152: if ('\n' == c) {
! 153: p->outcol = 0;
! 154: p->seenws = 0;
! 155: } else
! 156: p->outcol++;
! 157: }
! 158:
! 159: /*
! 160: * Put multiple characters (see texiputchar()).
! 161: * This shouldn't be called for macros: just for ordinary text.
! 162: */
! 163: void
! 164: texiputchars(struct texi *p, const char *s)
! 165: {
! 166:
! 167: while ('\0' != *s)
! 168: texiputchar(p, *s++);
! 169: }
! 170:
! 171: /*
! 172: * Close an mdoc(7) macro opened with teximacroopen().
! 173: * If there are no more macros on the line, prints a newline.
! 174: */
! 175: void
! 176: teximacroclose(struct texi *p)
! 177: {
! 178:
! 179: if (p->ign)
! 180: return;
! 181:
! 182: if (0 == --p->outmacro) {
! 183: putchar('\n');
! 184: p->outcol = p->seenws = 0;
! 185: }
! 186: }
! 187:
! 188: /*
! 189: * Open a mdoc(7) macro.
! 190: * This is used for line macros, e.g., Qq [foo bar baz].
! 191: * It can be invoked for nested macros, e.g., Qq Li foo .
! 192: * TODO: flush-right punctuation (e.g., parenthesis).
! 193: */
! 194: void
! 195: teximacroopen(struct texi *p, const char *s)
! 196: {
! 197: int rc;
! 198:
! 199: if (p->ign)
! 200: return;
! 201:
! 202: if (p->outcol && 0 == p->outmacro) {
! 203: putchar('\n');
! 204: p->outcol = 0;
! 205: }
! 206:
! 207: if (0 == p->outmacro)
! 208: putchar('.');
! 209: else
! 210: putchar(' ');
! 211:
! 212: if (EOF != (rc = fputs(s, stdout)))
! 213: p->outcol += rc;
! 214:
! 215: putchar(' ');
! 216: p->outcol++;
! 217: p->outmacro++;
! 218: p->seenws = 0;
! 219: }
! 220:
! 221: /*
! 222: * Put a stadnalone mdoc(7) command with the trailing newline.
! 223: */
! 224: void
! 225: teximacro(struct texi *p, const char *s)
! 226: {
! 227:
! 228: if (p->ign)
! 229: return;
! 230:
! 231: if (p->outmacro)
! 232: texierr(p, "\"%s\" in open line scope!?", s);
! 233: if (p->literal)
! 234: texierr(p, "\"%s\" in a literal scope!?", s);
! 235:
! 236: if (p->outcol)
! 237: putchar('\n');
! 238:
! 239: putchar('.');
! 240: puts(s);
! 241: p->outcol = p->seenws = 0;
! 242: }
! 243:
! 244: /*
! 245: * Introduce vertical space during normal (non-macro) input.
! 246: */
! 247: void
! 248: texivspace(struct texi *p)
! 249: {
! 250:
! 251: if (p->seenvs)
! 252: return;
! 253: teximacro(p, "Pp");
! 254: p->seenvs = 1;
! 255: }
! 256:
! 257: /*
! 258: * Advance by a single byte in the input stream, adjusting our location
! 259: * in the current input file.
! 260: */
! 261: void
! 262: advance(struct texi *p, const char *buf, size_t *pos)
! 263: {
! 264:
! 265: if ('\n' == buf[*pos]) {
! 266: p->files[p->filepos - 1].line++;
! 267: p->files[p->filepos - 1].col = 0;
! 268: } else
! 269: p->files[p->filepos - 1].col++;
! 270:
! 271: (*pos)++;
! 272: }
! 273:
! 274: /*
! 275: * It's common to wait punctuation to float on the right side of macro
! 276: * lines in mdoc(7), e.g., ".Em hello ) ."
! 277: * This function does so, and should be called before teximacroclose().
! 278: * It will detect that it's the last in the nested macros and
! 279: * appropriately flush-left punctuation alongside the macro.
! 280: */
! 281: void
! 282: texipunctuate(struct texi *p, const char *buf, size_t sz, size_t *pos)
! 283: {
! 284: size_t start, end;
! 285:
! 286: if (1 != p->outmacro)
! 287: return;
! 288:
! 289: for (start = end = *pos; end < sz; end++) {
! 290: switch (buf[end]) {
! 291: case (','):
! 292: case (')'):
! 293: case ('.'):
! 294: case ('"'):
! 295: case (':'):
! 296: case ('!'):
! 297: case ('?'):
! 298: continue;
! 299: default:
! 300: break;
! 301: }
! 302: break;
! 303: }
! 304: if (end == *pos)
! 305: return;
! 306: if (end + 1 == sz || ' ' == buf[end] || '\n' == buf[end]) {
! 307: for ( ; start < end; start++) {
! 308: texiputchar(p, ' ');
! 309: texiputchar(p, buf[start]);
! 310: advance(p, buf, pos);
! 311: }
! 312: }
! 313: }
! 314:
! 315: /*
! 316: * Advance to the next non-whitespace word in the input stream.
! 317: * If we're in literal mode, then print all of the whitespace as we're
! 318: * doing so.
! 319: */
! 320: static size_t
! 321: advancenext(struct texi *p, const char *buf, size_t sz, size_t *pos)
! 322: {
! 323:
! 324: if (p->literal) {
! 325: while (*pos < sz && ismspace(buf[*pos])) {
! 326: if (*pos && '\n' == buf[*pos] &&
! 327: '\\' == buf[*pos - 1])
! 328: texiputchar(p, 'e');
! 329: texiputchar(p, buf[*pos]);
! 330: advance(p, buf, pos);
! 331: }
! 332: return(*pos);
! 333: }
! 334:
! 335: while (*pos < sz && ismspace(buf[*pos])) {
! 336: p->seenws = 1;
! 337: /*
! 338: * If it looks like we've printed a double-line, then
! 339: * output a paragraph.
! 340: * FIXME: this is stupid.
! 341: */
! 342: if (*pos && '\n' == buf[*pos] && '\n' == buf[*pos - 1])
! 343: texivspace(p);
! 344: advance(p, buf, pos);
! 345: }
! 346: return(*pos);
! 347: }
! 348:
! 349: /*
! 350: * Advance to the EOLN in the input stream.
! 351: * NOTE: THIS SHOULD NOT BE CALLED ON BLANK TEXT, as it will read up to
! 352: * the @\n.
! 353: */
! 354: size_t
! 355: advanceeoln(struct texi *p, const char *buf,
! 356: size_t sz, size_t *pos, int consumenl)
! 357: {
! 358:
! 359: while (*pos < sz && '\n' != buf[*pos])
! 360: advance(p, buf, pos);
! 361: if (*pos < sz && consumenl)
! 362: advance(p, buf, pos);
! 363: return(*pos);
! 364: }
! 365:
! 366: /*
! 367: * Advance to position "end", which is an absolute position in the
! 368: * current buffer greater than or equal to the current position.
! 369: */
! 370: void
! 371: advanceto(struct texi *p, const char *buf, size_t *pos, size_t end)
! 372: {
! 373:
! 374: assert(*pos <= end);
! 375: while (*pos < end)
! 376: advance(p, buf, pos);
! 377: }
! 378:
! 379: /*
! 380: * Output a free-form word in the input stream, progressing to the next
! 381: * command or white-space.
! 382: * This also will advance the input stream.
! 383: */
! 384: static void
! 385: texiword(struct texi *p, const char *buf,
! 386: size_t sz, size_t *pos, char extra)
! 387: {
! 388:
! 389: if (p->seenws && 0 == p->outmacro &&
! 390: p->outcol > 72 && 0 == p->literal)
! 391: texiputchar(p, '\n');
! 392: /* FIXME: abstract this: we use it elsewhere. */
! 393: if (p->seenws && p->outcol && 0 == p->literal)
! 394: texiputchar(p, ' ');
! 395:
! 396: p->seenws = 0;
! 397:
! 398: while (*pos < sz && ! ismspace(buf[*pos])) {
! 399: switch (buf[*pos]) {
! 400: case ('@'):
! 401: case ('}'):
! 402: case ('{'):
! 403: return;
! 404: }
! 405: if ('\0' != extra && buf[*pos] == extra)
! 406: return;
! 407: if (*pos < sz - 1 &&
! 408: '`' == buf[*pos] &&
! 409: '`' == buf[*pos + 1]) {
! 410: texiputchars(p, "\\(lq");
! 411: advance(p, buf, pos);
! 412: } else if (*pos < sz - 1 &&
! 413: '\'' == buf[*pos] &&
! 414: '\'' == buf[*pos + 1]) {
! 415: texiputchars(p, "\\(rq");
! 416: advance(p, buf, pos);
! 417: } else
! 418: texiputchar(p, buf[*pos]);
! 419: advance(p, buf, pos);
! 420: }
! 421: }
! 422:
! 423: /*
! 424: * Look up the command at position "pos" in the buffer, returning it (or
! 425: * TEXICMD__MAX if none found) and setting "end" to be the absolute
! 426: * index after the command name.
! 427: */
! 428: enum texicmd
! 429: texicmd(struct texi *p, const char *buf,
! 430: size_t pos, size_t sz, size_t *end)
! 431: {
! 432: size_t i, len;
! 433:
! 434: assert('@' == buf[pos]);
! 435:
! 436: if ((*end = pos) == sz)
! 437: return(TEXICMD__MAX);
! 438: else if ((*end = ++pos) == sz)
! 439: return(TEXICMD__MAX);
! 440:
! 441: /* Alphabetic commands are special. */
! 442: if ( ! isalpha(buf[pos])) {
! 443: if ((*end = pos + 1) == sz)
! 444: return(TEXICMD__MAX);
! 445: for (i = 0; i < TEXICMD__MAX; i++) {
! 446: if (1 != texitoks[i].len)
! 447: continue;
! 448: if (0 == strncmp(texitoks[i].tok, &buf[pos], 1))
! 449: return(i);
! 450: }
! 451: texiwarn(p, "bad command: @%c", buf[pos]);
! 452: return(TEXICMD__MAX);
! 453: }
! 454:
! 455: for (*end = pos; *end < sz && ! ismspace(buf[*end]); (*end)++)
! 456: if ((*end > pos && ('@' == buf[*end] ||
! 457: '{' == buf[*end] || '}' == buf[*end])))
! 458: break;
! 459:
! 460: len = *end - pos;
! 461: for (i = 0; i < TEXICMD__MAX; i++) {
! 462: if (len != texitoks[i].len)
! 463: continue;
! 464: if (0 == strncmp(texitoks[i].tok, &buf[pos], len))
! 465: return(i);
! 466: }
! 467:
! 468: texiwarn(p, "bad command: @%.*s", (int)len, &buf[pos]);
! 469: return(TEXICMD__MAX);
! 470: }
! 471:
! 472: /*
! 473: * Parse an argument from a bracketed command, e.g., @url{foo, baz}.
! 474: * Num should be set to the argument we're currently parsing, although
! 475: * it suffixes for it to be zero or non-zero.
! 476: * This will return 1 if there are more arguments, 0 otherwise.
! 477: * This will stop (returning 0) in the event of EOF or if we're not at a
! 478: * bracket for the zeroth parse.
! 479: */
! 480: int
! 481: parsearg(struct texi *p, const char *buf,
! 482: size_t sz, size_t *pos, size_t num)
! 483: {
! 484: size_t end;
! 485: enum texicmd cmd;
! 486:
! 487: while (*pos < sz && ismspace(buf[*pos]))
! 488: advance(p, buf, pos);
! 489: if (*pos == sz || (0 == num && '{' != buf[*pos]))
! 490: return(0);
! 491: if (0 == num)
! 492: advance(p, buf, pos);
! 493:
! 494: while ((*pos = advancenext(p, buf, sz, pos)) < sz) {
! 495: switch (buf[*pos]) {
! 496: case (','):
! 497: advance(p, buf, pos);
! 498: return(1);
! 499: case ('}'):
! 500: advance(p, buf, pos);
! 501: return(0);
! 502: case ('{'):
! 503: if (0 == p->ign)
! 504: texiwarn(p, "unexpected \"{\"");
! 505: advance(p, buf, pos);
! 506: continue;
! 507: case ('@'):
! 508: break;
! 509: default:
! 510: texiword(p, buf, sz, pos, ',');
! 511: continue;
! 512: }
! 513:
! 514: cmd = texicmd(p, buf, *pos, sz, &end);
! 515: advanceto(p, buf, pos, end);
! 516: if (TEXICMD__MAX == cmd)
! 517: continue;
! 518: if (NULL != texitoks[cmd].fp)
! 519: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
! 520: }
! 521: return(0);
! 522: }
! 523:
! 524: /*
! 525: * Parse until the end of a bracketed statement, e.g., @foo{bar baz}.
! 526: * This will stop in the event of EOF or if we're not at a bracket.
! 527: */
! 528: void
! 529: parsebracket(struct texi *p, const char *buf, size_t sz, size_t *pos)
! 530: {
! 531: size_t end;
! 532: enum texicmd cmd;
! 533:
! 534: while (*pos < sz && ismspace(buf[*pos]))
! 535: advance(p, buf, pos);
! 536:
! 537: if (*pos == sz || '{' != buf[*pos])
! 538: return;
! 539: advance(p, buf, pos);
! 540:
! 541: while ((*pos = advancenext(p, buf, sz, pos)) < sz) {
! 542: switch (buf[*pos]) {
! 543: case ('}'):
! 544: advance(p, buf, pos);
! 545: return;
! 546: case ('{'):
! 547: if (0 == p->ign)
! 548: texiwarn(p, "unexpected \"{\"");
! 549: advance(p, buf, pos);
! 550: continue;
! 551: case ('@'):
! 552: break;
! 553: default:
! 554: texiword(p, buf, sz, pos, '\0');
! 555: continue;
! 556: }
! 557:
! 558: cmd = texicmd(p, buf, *pos, sz, &end);
! 559: advanceto(p, buf, pos, end);
! 560: if (TEXICMD__MAX == cmd)
! 561: continue;
! 562: if (NULL != texitoks[cmd].fp)
! 563: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
! 564: }
! 565: }
! 566:
! 567: /*
! 568: * This should be invoked when we're on a macro line and want to process
! 569: * to the end of the current input line, doing all of our macros along
! 570: * the way.
! 571: */
! 572: void
! 573: parseeoln(struct texi *p, const char *buf, size_t sz, size_t *pos)
! 574: {
! 575: size_t end;
! 576: enum texicmd cmd;
! 577:
! 578: while (*pos < sz && '\n' != buf[*pos]) {
! 579: while (*pos < sz && isws(buf[*pos])) {
! 580: p->seenws = 1;
! 581: if (p->literal)
! 582: texiputchar(p, buf[*pos]);
! 583: advance(p, buf, pos);
! 584: }
! 585: switch (buf[*pos]) {
! 586: case ('}'):
! 587: if (0 == p->ign)
! 588: texiwarn(p, "unexpected \"}\"");
! 589: advance(p, buf, pos);
! 590: continue;
! 591: case ('{'):
! 592: if (0 == p->ign)
! 593: texiwarn(p, "unexpected \"{\"");
! 594: advance(p, buf, pos);
! 595: continue;
! 596: case ('@'):
! 597: break;
! 598: default:
! 599: texiword(p, buf, sz, pos, '\0');
! 600: continue;
! 601: }
! 602:
! 603: cmd = texicmd(p, buf, *pos, sz, &end);
! 604: advanceto(p, buf, pos, end);
! 605: if (TEXICMD__MAX == cmd)
! 606: continue;
! 607: if (NULL != texitoks[cmd].fp)
! 608: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
! 609: }
! 610: }
! 611:
! 612: /*
! 613: * Parse a single word or command.
! 614: * This will return immediately at the EOF.
! 615: */
! 616: void
! 617: parsesingle(struct texi *p, const char *buf, size_t sz, size_t *pos)
! 618: {
! 619: size_t end;
! 620: enum texicmd cmd;
! 621:
! 622: if ((*pos = advancenext(p, buf, sz, pos)) >= sz)
! 623: return;
! 624:
! 625: switch (buf[*pos]) {
! 626: case ('}'):
! 627: if (0 == p->ign)
! 628: texiwarn(p, "unexpected \"}\"");
! 629: advance(p, buf, pos);
! 630: return;
! 631: case ('{'):
! 632: if (0 == p->ign)
! 633: texiwarn(p, "unexpected \"{\"");
! 634: advance(p, buf, pos);
! 635: return;
! 636: case ('@'):
! 637: break;
! 638: default:
! 639: texiword(p, buf, sz, pos, '\0');
! 640: return;
! 641: }
! 642:
! 643: cmd = texicmd(p, buf, *pos, sz, &end);
! 644: advanceto(p, buf, pos, end);
! 645: if (TEXICMD__MAX == cmd)
! 646: return;
! 647: if (NULL != texitoks[cmd].fp)
! 648: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
! 649: }
! 650:
! 651: /*
! 652: * This is used in the @deffn type of command.
! 653: * These have an arbitrary number of line arguments; however, these
! 654: * arguments may or may not be surrounded by brackets.
! 655: * In this function, we parse each one as either a bracketed or
! 656: * non-bracketed argument, returning 0 when we've reached the end of
! 657: * line or 1 otherwise.
! 658: */
! 659: int
! 660: parselinearg(struct texi *p, const char *buf, size_t sz, size_t *pos)
! 661: {
! 662:
! 663: while (*pos < sz && isws(buf[*pos])) {
! 664: p->seenws = 1;
! 665: advance(p, buf, pos);
! 666: }
! 667:
! 668: if (*pos < sz && '{' == buf[*pos])
! 669: parsebracket(p, buf, sz, pos);
! 670: else if ('\n' != buf[*pos])
! 671: parsesingle(p, buf, sz, pos);
! 672: else
! 673: return(0);
! 674:
! 675: return(1);
! 676: }
! 677:
! 678: /*
! 679: * Parse til the end of the buffer.
! 680: */
! 681: void
! 682: parseeof(struct texi *p, const char *buf, size_t sz)
! 683: {
! 684: size_t pos;
! 685:
! 686: for (pos = 0; pos < sz; )
! 687: parsesingle(p, buf, sz, &pos);
! 688: }
! 689:
! 690: /*
! 691: * Parse a block sequence until we have the "@end endtoken" command
! 692: * invocation.
! 693: * This will return immediately at EOF.
! 694: */
! 695: void
! 696: parseto(struct texi *p, const char *buf,
! 697: size_t sz, size_t *pos, const char *endtoken)
! 698: {
! 699: size_t end;
! 700: enum texicmd cmd;
! 701: size_t endtoksz;
! 702:
! 703: endtoksz = strlen(endtoken);
! 704: assert(endtoksz > 0);
! 705:
! 706: while ((*pos = advancenext(p, buf, sz, pos)) < sz) {
! 707: switch (buf[*pos]) {
! 708: case ('}'):
! 709: if (0 == p->ign)
! 710: texiwarn(p, "unexpected \"}\"");
! 711: advance(p, buf, pos);
! 712: continue;
! 713: case ('{'):
! 714: if (0 == p->ign)
! 715: texiwarn(p, "unexpected \"{\"");
! 716: advance(p, buf, pos);
! 717: continue;
! 718: case ('@'):
! 719: break;
! 720: default:
! 721: texiword(p, buf, sz, pos, '\0');
! 722: continue;
! 723: }
! 724:
! 725: cmd = texicmd(p, buf, *pos, sz, &end);
! 726: advanceto(p, buf, pos, end);
! 727: if (TEXICMD_END == cmd) {
! 728: while (*pos < sz && isws(buf[*pos]))
! 729: advance(p, buf, pos);
! 730: /*
! 731: * FIXME: check the full word, not just its
! 732: * initial substring!
! 733: */
! 734: if (sz - *pos >= endtoksz && 0 == strncmp
! 735: (&buf[*pos], endtoken, endtoksz)) {
! 736: advanceeoln(p, buf, sz, pos, 0);
! 737: break;
! 738: }
! 739: if (0 == p->ign)
! 740: texiwarn(p, "unexpected \"end\"");
! 741: advanceeoln(p, buf, sz, pos, 0);
! 742: continue;
! 743: } else if (TEXICMD__MAX != cmd)
! 744: if (NULL != texitoks[cmd].fp)
! 745: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
! 746: }
! 747: }
! 748:
! 749: /*
! 750: * Memory-map the file "fname" and begin parsing it unless "parse" is
! 751: * zero, in which case we just dump the file to stdout (making sure it
! 752: * doesn't trip up mdoc(7) along the way).
! 753: * This can be called in a nested context.
! 754: */
! 755: void
! 756: parsefile(struct texi *p, const char *fname, int parse)
! 757: {
! 758: struct texifile *f;
! 759: int fd;
! 760: struct stat st;
! 761: size_t i;
! 762:
! 763: assert(p->filepos < 64);
! 764: f = &p->files[p->filepos];
! 765: memset(f, 0, sizeof(struct texifile));
! 766:
! 767: f->name = fname;
! 768: if (-1 == (fd = open(fname, O_RDONLY, 0))) {
! 769: texiabort(p, fname);
! 770: } else if (-1 == fstat(fd, &st)) {
! 771: close(fd);
! 772: texiabort(p, fname);
! 773: }
! 774:
! 775: f->mapsz = st.st_size;
! 776: f->map = mmap(NULL, f->mapsz,
! 777: PROT_READ, MAP_SHARED, fd, 0);
! 778: close(fd);
! 779:
! 780: if (MAP_FAILED == f->map)
! 781: texiabort(p, fname);
! 782:
! 783: p->filepos++;
! 784: if ( ! parse) {
! 785: /*
! 786: * We're printing verbatim output.
! 787: * Make sure it doesn't get interpreted as mdoc by
! 788: * escaping escapes and making sure leading dots don't
! 789: * trigger mdoc(7) expansion.
! 790: */
! 791: for (i = 0; i < f->mapsz; i++) {
! 792: if (i > 0 && '.' == f->map[i])
! 793: if ('\n' == f->map[i - 1])
! 794: fputs("\\&", stdout);
! 795: putchar(f->map[i]);
! 796: if ('\\' == f->map[i])
! 797: putchar('e');
! 798: }
! 799: } else
! 800: parseeof(p, f->map, f->mapsz);
! 801: texifilepop(p);
! 802: }
! 803:
CVSweb