Annotation of texi2mdoc/main.c, Revision 1.1
1.1 ! kristaps 1: /* $Id: main.c,v 1.216 2015/01/16 21:15:05 schwarze 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 <stdarg.h>
! 25: #include <stdio.h>
! 26: #include <stdlib.h>
! 27: #include <string.h>
! 28:
! 29: /*
! 30: * This defines each one of the Texinfo commands that we understand.
! 31: * Obviously this only refers to native commands; overriden names are a
! 32: * different story.
! 33: */
! 34: enum texicmd {
! 35: TEXICMD_A4PAPER,
! 36: TEXICMD_ANCHOR,
! 37: TEXICMD_AT,
! 38: TEXICMD_BYE,
! 39: TEXICMD_CHAPTER,
! 40: TEXICMD_CINDEX,
! 41: TEXICMD_CODE,
! 42: TEXICMD_COMMAND,
! 43: TEXICMD_COMMENT,
! 44: TEXICMD_CONTENTS,
! 45: TEXICMD_COPYING,
! 46: TEXICMD_COPYRIGHT,
! 47: TEXICMD_DETAILMENU,
! 48: TEXICMD_DIRCATEGORY,
! 49: TEXICMD_DIRENTRY,
! 50: TEXICMD_EMAIL,
! 51: TEXICMD_EMPH,
! 52: TEXICMD_END,
! 53: TEXICMD_EXAMPLE,
! 54: TEXICMD_FILE,
! 55: TEXICMD_I,
! 56: TEXICMD_IFHTML,
! 57: TEXICMD_IFNOTTEX,
! 58: TEXICMD_IFTEX,
! 59: TEXICMD_IMAGE,
! 60: TEXICMD_ITEM,
! 61: TEXICMD_ITEMIZE,
! 62: TEXICMD_KBD,
! 63: TEXICMD_LATEX,
! 64: TEXICMD_MENU,
! 65: TEXICMD_NODE,
! 66: TEXICMD_QUOTATION,
! 67: TEXICMD_PARINDENT,
! 68: TEXICMD_REF,
! 69: TEXICMD_SAMP,
! 70: TEXICMD_SECTION,
! 71: TEXICMD_SETCHAPNEWPAGE,
! 72: TEXICMD_SETFILENAME,
! 73: TEXICMD_SETTITLE,
! 74: TEXICMD_SUBSECTION,
! 75: TEXICMD_TABLE,
! 76: TEXICMD_TEX,
! 77: TEXICMD_TEXSYM,
! 78: TEXICMD_TITLEFONT,
! 79: TEXICMD_TITLEPAGE,
! 80: TEXICMD_TOP,
! 81: TEXICMD_UNNUMBERED,
! 82: TEXICMD_URL,
! 83: TEXICMD_VAR,
! 84: TEXICMD__MAX
! 85: };
! 86:
! 87: /*
! 88: * The file currently being parsed.
! 89: * This keeps track of our location within that file.
! 90: */
! 91: struct texifile {
! 92: const char *name; /* name of the file */
! 93: size_t line; /* current line (from zero) */
! 94: size_t col; /* current column in line (from zero) */
! 95: char *map; /* mmap'd file */
! 96: size_t mapsz; /* size of mmap */
! 97: };
! 98:
! 99: struct texi;
! 100:
! 101: typedef void (*texicmdfp)(struct texi *,
! 102: enum texicmd, const char *, size_t, size_t *);
! 103:
! 104: /*
! 105: * Describes Texinfo commands, whether native or overriden.
! 106: */
! 107: struct texitok {
! 108: texicmdfp fp; /* callback (or NULL if none) */
! 109: const char *tok; /* name of the token */
! 110: size_t len; /* strlen(tok) */
! 111: };
! 112:
! 113: /*
! 114: * The main parse structure.
! 115: * This keeps any necessary information handy.
! 116: */
! 117: struct texi {
! 118: struct texifile files[64];
! 119: size_t filepos;
! 120: unsigned flags;
! 121: #define TEXI_IGN 0x01 /* don't print anything */
! 122: #define TEXI_HEADER (TEXI_IGN | 0x02) /* haven't seen @top yet */
! 123: #define TEXI_LITERAL 0x04 /* output all whitespace */
! 124: size_t outcol; /* column of output */
! 125: int outmacro; /* whether output is in line macro */
! 126: int seenws; /* whitespace has been ignored */
! 127: };
! 128:
! 129: #define ismpunct(_x) \
! 130: ('.' == (_x) || \
! 131: ',' == (_x) || \
! 132: ';' == (_x))
! 133:
! 134: static void doarg1(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 135: static void dobracket(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 136: static void dobye(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 137: static void docommand(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 138: static void doemph(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 139: static void doexample(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 140: static void dofile(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 141: static void doifnottex(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 142: static void doignblock(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 143: static void doignbracket(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 144: static void doignline(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 145: static void doitalic(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 146: static void doitem(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 147: static void doitemize(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 148: static void doliteral(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 149: static void doquotation(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 150: static void dotable(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 151: static void dotop(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 152: static void dosection(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 153: static void dosh(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 154: static void dosubsection(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 155: static void dosymbol(struct texi *, enum texicmd, const char *, size_t, size_t *);
! 156:
! 157: static const struct texitok texitoks[TEXICMD__MAX] = {
! 158: { doignline, "afourpaper", 10 }, /* TEXICMD_A4PAPER */
! 159: { doignbracket, "anchor", 6 }, /* TEXICMD_ANCHOR */
! 160: { dosymbol, "@", 1 }, /* TEXICMD_AT */
! 161: { dobye, "bye", 3 }, /* TEXICMD_BYE */
! 162: { dosh, "chapter", 7 }, /* TEXICMD_CHAPTER */
! 163: { doignline, "cindex", 6 }, /* TEXICMD_CINDEX */
! 164: { doliteral, "code", 4 }, /* TEXICMD_CODE */
! 165: { docommand, "command", 7 }, /* TEXICMD_COMMAND */
! 166: { doignline, "c", 1 }, /* TEXICMD_COMMENT */
! 167: { doignline, "contents", 8 }, /* TEXICMD_CONTENTS */
! 168: { doignblock, "copying", 7 }, /* TEXICMD_COPYING */
! 169: { dosymbol, "copyright", 9 }, /* TEXICMD_COPYRIGHT */
! 170: { doignblock, "detailmenu", 10 }, /* TEXICMD_DETAILMENU */
! 171: { doignline, "dircategory", 11 }, /* TEXICMD_DIRCATEGORY */
! 172: { doignblock, "direntry", 8 }, /* TEXICMD_DIRENTRY */
! 173: { doarg1, "email", 5 }, /* TEXICMD_EMAIL */
! 174: { doemph, "emph", 4 }, /* TEXICMD_EMPH */
! 175: { NULL, "end", 3 }, /* TEXICMD_END */
! 176: { doexample, "example", 7 }, /* TEXICMD_EXAMPLE */
! 177: { dofile, "file", 4 }, /* TEXICMD_FILE */
! 178: { doitalic, "i", 1 }, /* TEXICMD_I */
! 179: { doignblock, "ifhtml", 6 }, /* TEXICMD_IFHTML */
! 180: { doifnottex, "ifnottex", 8 }, /* TEXICMD_IFNOTTEX */
! 181: { doignblock, "iftex", 5 }, /* TEXICMD_IFTEX */
! 182: { doignbracket, "image", 5 }, /* TEXICMD_IMAGE */
! 183: { doitem, "item", 4 }, /* TEXICMD_ITEM */
! 184: { doitemize, "itemize", 7 }, /* TEXICMD_ITEMIZE */
! 185: { doliteral, "kbd", 3 }, /* TEXICMD_KBD */
! 186: { dosymbol, "LaTeX", 5 }, /* TEXICMD_LATEX */
! 187: { doignblock, "menu", 4 }, /* TEXICMD_MENU */
! 188: { doignline, "node", 4 }, /* TEXICMD_NODE */
! 189: { doquotation, "quotation", 9 }, /* TEXICMD_QUOTATION */
! 190: { doignline, "paragraphindent", 14 }, /* TEXICMD_PARINDENT */
! 191: { dobracket, "ref", 3 }, /* TEXICMD_REF */
! 192: { doliteral, "samp", 4 }, /* TEXICMD_SAMP */
! 193: { dosection, "section", 7 }, /* TEXICMD_SECTION */
! 194: { doignline, "setchapternewpage", 17 }, /* TEXICMD_SETCHAPNEWPAGE */
! 195: { doignline, "setfilename", 11 }, /* TEXICMD_SETFILENAME */
! 196: { doignline, "settitle", 8 }, /* TEXICMD_SETTITLE */
! 197: { dosubsection, "subsection", 10 }, /* TEXICMD_SUBSECTION */
! 198: { dotable, "table", 5 }, /* TEXICMD_TABLE */
! 199: { doignblock, "tex", 3 }, /* TEXICMD_TEX */
! 200: { dosymbol, "TeX", 3 }, /* TEXICMD_TEXSYM */
! 201: { dobracket, "titlefont", 9 }, /* TEXICMD_TITLEFONT */
! 202: { doignblock, "titlepage", 9 }, /* TEXICMD_TITLEPAGE */
! 203: { dotop, "top", 3 }, /* TEXICMD_TOP */
! 204: { dosh, "unnumbered", 10 }, /* TEXICMD_UNNUMBERED */
! 205: { doarg1, "url", 3 }, /* TEXICMD_URL */
! 206: { doliteral, "var", 3 }, /* TEXICMD_VAR */
! 207: };
! 208:
! 209: static void
! 210: texifilepop(struct texi *p)
! 211: {
! 212: struct texifile *f;
! 213:
! 214: assert(p->filepos > 0);
! 215: f = &p->files[--p->filepos];
! 216: munmap(f->map, f->mapsz);
! 217: }
! 218:
! 219: static void
! 220: texiexit(struct texi *p)
! 221: {
! 222:
! 223: while (p->filepos > 0)
! 224: texifilepop(p);
! 225: }
! 226:
! 227: static void
! 228: texifatal(struct texi *p, const char *errstring)
! 229: {
! 230:
! 231: perror(errstring);
! 232: texiexit(p);
! 233: exit(EXIT_FAILURE);
! 234: }
! 235:
! 236: /*
! 237: * Print a generic warning message (to stderr) tied to our current
! 238: * location in the parse sequence.
! 239: */
! 240: static void
! 241: texiwarn(const struct texi *p, const char *fmt, ...)
! 242: {
! 243: va_list ap;
! 244:
! 245: fprintf(stderr, "%s:%zu:%zu: ",
! 246: p->files[p->filepos - 1].name,
! 247: p->files[p->filepos - 1].line + 1,
! 248: p->files[p->filepos - 1].col + 1);
! 249: va_start(ap, fmt);
! 250: vfprintf(stderr, fmt, ap);
! 251: va_end(ap);
! 252: fputc('\n', stderr);
! 253: }
! 254:
! 255: /*
! 256: * Put a single data character.
! 257: * This MUST NOT be a mdoc(7) command: it should be free text that's
! 258: * outputted to the screen.
! 259: */
! 260: static void
! 261: texiputchar(struct texi *p, char c)
! 262: {
! 263:
! 264: if (TEXI_IGN & p->flags)
! 265: return;
! 266:
! 267: putchar(c);
! 268: if ('\n' == c) {
! 269: p->outcol = 0;
! 270: p->outmacro = 0;
! 271: p->seenws = 0;
! 272: } else
! 273: p->outcol++;
! 274: }
! 275:
! 276: /*
! 277: * Put multiple characters (see texiputchar()).
! 278: */
! 279: static void
! 280: texiputchars(struct texi *p, const char *s)
! 281: {
! 282:
! 283: while ('\0' != *s)
! 284: texiputchar(p, *s++);
! 285: }
! 286:
! 287: /*
! 288: * Put an mdoc(7) command without the trailing newline.
! 289: * This should ONLY be used for mdoc(7) commands!
! 290: */
! 291: static void
! 292: texifputs(struct texi *p, const char *s)
! 293: {
! 294: int rc;
! 295:
! 296: if (TEXI_IGN & p->flags)
! 297: return;
! 298: if (p->outcol)
! 299: texiputchar(p, '\n');
! 300: if (EOF != (rc = fputs(s, stdout)))
! 301: p->outcol += rc;
! 302: }
! 303:
! 304: /*
! 305: * Put an mdoc(7) command with the trailing newline.
! 306: * This should ONLY be used for mdoc(7) commands!
! 307: */
! 308: static void
! 309: teximacro(struct texi *p, const char *s)
! 310: {
! 311:
! 312: if (TEXI_IGN & p->flags)
! 313: return;
! 314: if (p->outcol)
! 315: texiputchar(p, '\n');
! 316: puts(s);
! 317: p->outcol = 0;
! 318: p->seenws = 0;
! 319: }
! 320:
! 321: /*
! 322: * Advance by a single byte in the input stream.
! 323: */
! 324: static void
! 325: advance(struct texi *p, const char *buf, size_t *pos)
! 326: {
! 327:
! 328: if ('\n' == buf[*pos]) {
! 329: p->files[p->filepos - 1].line++;
! 330: p->files[p->filepos - 1].col = 0;
! 331: } else
! 332: p->files[p->filepos - 1].col++;
! 333:
! 334: (*pos)++;
! 335: }
! 336:
! 337: /*
! 338: * Advance to the next non-whitespace word in the input stream.
! 339: * If we're in literal mode, then print all of the whitespace as we're
! 340: * doing so.
! 341: */
! 342: static size_t
! 343: advancenext(struct texi *p, const char *buf, size_t sz, size_t *pos)
! 344: {
! 345:
! 346: if (TEXI_LITERAL & p->flags) {
! 347: while (*pos < sz && isspace(buf[*pos])) {
! 348: texiputchar(p, buf[*pos]);
! 349: advance(p, buf, pos);
! 350: }
! 351: return(*pos);
! 352: }
! 353:
! 354: while (*pos < sz && isspace(buf[*pos])) {
! 355: p->seenws = 1;
! 356: /*
! 357: * If it looks like we've printed a double-line, then
! 358: * output a paragraph.
! 359: * FIXME: this is stupid.
! 360: */
! 361: if (*pos && '\n' == buf[*pos] && '\n' == buf[*pos - 1])
! 362: teximacro(p, ".Pp");
! 363: advance(p, buf, pos);
! 364: }
! 365: return(*pos);
! 366: }
! 367:
! 368: /*
! 369: * Advance to the EOLN in the input stream.
! 370: */
! 371: static size_t
! 372: advanceeoln(struct texi *p, const char *buf, size_t sz, size_t *pos)
! 373: {
! 374:
! 375: while (*pos < sz && '\n' != buf[*pos])
! 376: advance(p, buf, pos);
! 377: return(*pos);
! 378: }
! 379:
! 380: /*
! 381: * Advance to position "end", which is an absolute position in the
! 382: * current buffer greater than or equal to the current position.
! 383: */
! 384: static void
! 385: advanceto(struct texi *p, const char *buf, size_t *pos, size_t end)
! 386: {
! 387:
! 388: assert(*pos <= end);
! 389: while (*pos < end)
! 390: advance(p, buf, pos);
! 391: }
! 392:
! 393: /*
! 394: * Output a free-form word in the input stream, progressing to the next
! 395: * command or white-space.
! 396: * This also will advance the input stream.
! 397: */
! 398: static void
! 399: texiword(struct texi *p, const char *buf, size_t sz, size_t *pos)
! 400: {
! 401:
! 402: /*
! 403: * XXX: if we're in literal mode, then we shouldn't do any
! 404: * reflowing of text here.
! 405: */
! 406: if (p->outcol > 72 && ! (TEXI_LITERAL & p->flags))
! 407: texiputchar(p, '\n');
! 408:
! 409: if (p->seenws && p->outcol && ! (TEXI_LITERAL & p->flags))
! 410: texiputchar(p, ' ');
! 411:
! 412: p->seenws = 0;
! 413:
! 414: while (*pos < sz && ! isspace(buf[*pos])) {
! 415: switch (buf[*pos]) {
! 416: case ('@'):
! 417: case ('}'):
! 418: case ('{'):
! 419: return;
! 420: }
! 421: if (*pos < sz - 1 &&
! 422: '`' == buf[*pos] &&
! 423: '`' == buf[*pos + 1]) {
! 424: texiputchars(p, "\\(lq");
! 425: advance(p, buf, pos);
! 426: } else if (*pos < sz - 1 &&
! 427: '\'' == buf[*pos] &&
! 428: '\'' == buf[*pos + 1]) {
! 429: texiputchars(p, "\\(rq");
! 430: advance(p, buf, pos);
! 431: } else
! 432: texiputchar(p, buf[*pos]);
! 433: advance(p, buf, pos);
! 434: }
! 435: }
! 436:
! 437: static enum texicmd
! 438: texicmd(struct texi *p, const char *buf,
! 439: size_t pos, size_t sz, size_t *end)
! 440: {
! 441: size_t i, len;
! 442:
! 443: assert('@' == buf[pos]);
! 444: for (*end = ++pos; *end < sz && ! isspace(buf[*end]); (*end)++)
! 445: if ('@' == buf[*end] || '{' == buf[*end])
! 446: break;
! 447:
! 448: len = *end - pos;
! 449: for (i = 0; i < TEXICMD__MAX; i++) {
! 450: if (len != texitoks[i].len)
! 451: continue;
! 452: if (0 == strncmp(texitoks[i].tok, &buf[pos], len))
! 453: return(i);
! 454: }
! 455:
! 456: texiwarn(p, "bad command: %.*s", (int)len, &buf[pos]);
! 457: return(TEXICMD__MAX);
! 458: }
! 459:
! 460: static void
! 461: parseeof(struct texi *p, const char *buf, size_t sz)
! 462: {
! 463: size_t pos = 0;
! 464: enum texicmd cmd;
! 465: size_t end;
! 466:
! 467: while ((pos = advancenext(p, buf, sz, &pos)) < sz) {
! 468: switch (buf[pos]) {
! 469: case ('}'):
! 470: texiwarn(p, "unexpected \"}\"");
! 471: advance(p, buf, &pos);
! 472: continue;
! 473: case ('{'):
! 474: texiwarn(p, "unexpected \"{\"");
! 475: advance(p, buf, &pos);
! 476: continue;
! 477: case ('@'):
! 478: break;
! 479: default:
! 480: texiword(p, buf, sz, &pos);
! 481: continue;
! 482: }
! 483:
! 484: cmd = texicmd(p, buf, pos, sz, &end);
! 485: advanceto(p, buf, &pos, end);
! 486: if (TEXICMD__MAX == cmd)
! 487: continue;
! 488: if (NULL != texitoks[cmd].fp)
! 489: (*texitoks[cmd].fp)(p, cmd, buf, sz, &pos);
! 490: }
! 491: }
! 492:
! 493: static void
! 494: parsebracket(struct texi *p, const char *buf, size_t sz, size_t *pos)
! 495: {
! 496: size_t end;
! 497: enum texicmd cmd;
! 498:
! 499: if (*pos == sz || '{' != buf[*pos])
! 500: return;
! 501: advance(p, buf, pos);
! 502:
! 503: while ((*pos = advancenext(p, buf, sz, pos)) < sz) {
! 504: switch (buf[*pos]) {
! 505: case ('}'):
! 506: advance(p, buf, pos);
! 507: return;
! 508: case ('{'):
! 509: texiwarn(p, "unexpected \"{\"");
! 510: advance(p, buf, pos);
! 511: continue;
! 512: case ('@'):
! 513: break;
! 514: default:
! 515: texiword(p, buf, sz, pos);
! 516: continue;
! 517: }
! 518:
! 519: cmd = texicmd(p, buf, *pos, sz, &end);
! 520: advanceto(p, buf, pos, end);
! 521: if (TEXICMD__MAX == cmd)
! 522: continue;
! 523: if (NULL != texitoks[cmd].fp)
! 524: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
! 525: }
! 526: }
! 527:
! 528: static void
! 529: parseto(struct texi *p, const char *buf,
! 530: size_t sz, size_t *pos, const char *endtoken)
! 531: {
! 532: size_t end;
! 533: enum texicmd cmd;
! 534: size_t endtoksz;
! 535:
! 536: endtoksz = strlen(endtoken);
! 537: assert(endtoksz > 0);
! 538:
! 539: while ((*pos = advancenext(p, buf, sz, pos)) < sz) {
! 540: switch (buf[*pos]) {
! 541: case ('}'):
! 542: texiwarn(p, "unexpected \"}\"");
! 543: advance(p, buf, pos);
! 544: continue;
! 545: case ('{'):
! 546: texiwarn(p, "unexpected \"{\"");
! 547: advance(p, buf, pos);
! 548: continue;
! 549: case ('@'):
! 550: break;
! 551: default:
! 552: texiword(p, buf, sz, pos);
! 553: continue;
! 554: }
! 555:
! 556: cmd = texicmd(p, buf, *pos, sz, &end);
! 557: advanceto(p, buf, pos, end);
! 558: if (TEXICMD_END == cmd) {
! 559: while (*pos < sz && ' ' == buf[*pos])
! 560: advance(p, buf, pos);
! 561: /*
! 562: * FIXME: skip tabs and also check the full
! 563: * word, not just its initial substring!
! 564: */
! 565: if (sz - *pos >= endtoksz && 0 == strncmp
! 566: (&buf[*pos], endtoken, endtoksz)) {
! 567: advanceeoln(p, buf, sz, pos);
! 568: break;
! 569: }
! 570: texiwarn(p, "unexpected \"end\"");
! 571: advanceeoln(p, buf, sz, pos);
! 572: continue;
! 573: } else if (TEXICMD__MAX != cmd)
! 574: if (NULL != texitoks[cmd].fp)
! 575: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
! 576: }
! 577: }
! 578:
! 579: static void
! 580: doignblock(struct texi *p, enum texicmd cmd,
! 581: const char *buf, size_t sz, size_t *pos)
! 582: {
! 583: unsigned int sv = p->flags;
! 584: const char *blockname;
! 585:
! 586: p->flags |= TEXI_IGN;
! 587: switch (cmd) {
! 588: case (TEXICMD_COPYING):
! 589: blockname = "copying";
! 590: break;
! 591: case (TEXICMD_DETAILMENU):
! 592: blockname = "detailmenu";
! 593: break;
! 594: case (TEXICMD_DIRENTRY):
! 595: blockname = "direntry";
! 596: break;
! 597: case (TEXICMD_IFHTML):
! 598: blockname = "ifhtml";
! 599: break;
! 600: case (TEXICMD_IFTEX):
! 601: blockname = "iftex";
! 602: break;
! 603: case (TEXICMD_MENU):
! 604: blockname = "menu";
! 605: break;
! 606: case (TEXICMD_TEX):
! 607: blockname = "tex";
! 608: break;
! 609: case (TEXICMD_TITLEPAGE):
! 610: blockname = "titlepage";
! 611: break;
! 612: default:
! 613: abort();
! 614: }
! 615: parseto(p, buf, sz, pos, blockname);
! 616: p->flags = sv;
! 617: }
! 618:
! 619: static void
! 620: doifnottex(struct texi *p, enum texicmd cmd,
! 621: const char *buf, size_t sz, size_t *pos)
! 622: {
! 623:
! 624: parseto(p, buf, sz, pos, "ifnottex");
! 625: }
! 626:
! 627: static void
! 628: doinline(struct texi *p, const char *buf,
! 629: size_t sz, size_t *pos, const char *macro)
! 630: {
! 631:
! 632: if ( ! p->outmacro)
! 633: texifputs(p, ".");
! 634: texiputchars(p, macro);
! 635: texiputchar(p, ' ');
! 636: p->seenws = 0;
! 637: p->outmacro++;
! 638: parsebracket(p, buf, sz, pos);
! 639: p->outmacro--;
! 640: if (*pos < sz - 1 &&
! 641: ismpunct(buf[*pos]) &&
! 642: isspace(buf[*pos + 1])) {
! 643: texiputchar(p, ' ');
! 644: texiputchar(p, buf[*pos]);
! 645: advance(p, buf, pos);
! 646: }
! 647: if ( ! p->outmacro)
! 648: texiputchar(p, '\n');
! 649: }
! 650:
! 651: static void
! 652: doitalic(struct texi *p, enum texicmd cmd,
! 653: const char *buf, size_t sz, size_t *pos)
! 654: {
! 655:
! 656: texiputchars(p, "\\fI");
! 657: parsebracket(p, buf, sz, pos);
! 658: texiputchars(p, "\\fP");
! 659: }
! 660:
! 661: static void
! 662: doliteral(struct texi *p, enum texicmd cmd,
! 663: const char *buf, size_t sz, size_t *pos)
! 664: {
! 665:
! 666: if (TEXI_LITERAL & p->flags)
! 667: parsebracket(p, buf, sz, pos);
! 668: else
! 669: doinline(p, buf, sz, pos, "Li");
! 670: }
! 671:
! 672: static void
! 673: doemph(struct texi *p, enum texicmd cmd,
! 674: const char *buf, size_t sz, size_t *pos)
! 675: {
! 676:
! 677: if (TEXI_LITERAL & p->flags)
! 678: doitalic(p, cmd, buf, sz, pos);
! 679: else
! 680: doinline(p, buf, sz, pos, "Em");
! 681: }
! 682:
! 683: static void
! 684: docommand(struct texi *p, enum texicmd cmd,
! 685: const char *buf, size_t sz, size_t *pos)
! 686: {
! 687:
! 688: doinline(p, buf, sz, pos, "Xr");
! 689: }
! 690:
! 691: static void
! 692: dobracket(struct texi *p, enum texicmd cmd,
! 693: const char *buf, size_t sz, size_t *pos)
! 694: {
! 695:
! 696: parsebracket(p, buf, sz, pos);
! 697: }
! 698:
! 699: static void
! 700: dofile(struct texi *p, enum texicmd cmd,
! 701: const char *buf, size_t sz, size_t *pos)
! 702: {
! 703:
! 704: if (TEXI_LITERAL & p->flags)
! 705: parsebracket(p, buf, sz, pos);
! 706: else
! 707: doinline(p, buf, sz, pos, "Pa");
! 708: }
! 709:
! 710: static void
! 711: doexample(struct texi *p, enum texicmd cmd,
! 712: const char *buf, size_t sz, size_t *pos)
! 713: {
! 714: unsigned int sv;
! 715:
! 716: teximacro(p, ".Bd -literal");
! 717: advanceeoln(p, buf, sz, pos);
! 718: if ('\n' == buf[*pos])
! 719: advance(p, buf, pos);
! 720: sv = p->flags;
! 721: p->flags |= TEXI_LITERAL;
! 722: parseto(p, buf, sz, pos, "example");
! 723: p->flags = sv;
! 724: teximacro(p, ".Ed");
! 725: }
! 726:
! 727: static void
! 728: dobye(struct texi *p, enum texicmd cmd,
! 729: const char *buf, size_t sz, size_t *pos)
! 730: {
! 731:
! 732: texiexit(p);
! 733: exit(EXIT_SUCCESS);
! 734: }
! 735:
! 736: static void
! 737: dosymbol(struct texi *p, enum texicmd cmd,
! 738: const char *buf, size_t sz, size_t *pos)
! 739: {
! 740:
! 741: switch (cmd) {
! 742: case (TEXICMD_AT):
! 743: texiputchars(p, "@");
! 744: break;
! 745: case (TEXICMD_COPYRIGHT):
! 746: texiputchars(p, "\\(co");
! 747: break;
! 748: case (TEXICMD_LATEX):
! 749: texiputchars(p, "LaTeX");
! 750: break;
! 751: case (TEXICMD_TEXSYM):
! 752: texiputchars(p, "TeX");
! 753: break;
! 754: default:
! 755: abort();
! 756: }
! 757:
! 758: doignbracket(p, cmd, buf, sz, pos);
! 759: }
! 760:
! 761: static void
! 762: doquotation(struct texi *p, enum texicmd cmd,
! 763: const char *buf, size_t sz, size_t *pos)
! 764: {
! 765:
! 766: teximacro(p, ".Qo");
! 767: parseto(p, buf, sz, pos, "quotation");
! 768: teximacro(p, ".Qc");
! 769: }
! 770:
! 771: static void
! 772: doarg1(struct texi *p, enum texicmd cmd,
! 773: const char *buf, size_t sz, size_t *pos)
! 774: {
! 775:
! 776: if (*pos == sz || '{' != buf[*pos])
! 777: return;
! 778: advance(p, buf, pos);
! 779: if ( ! p->outmacro)
! 780: texifputs(p, ".");
! 781: switch (cmd) {
! 782: case (TEXICMD_EMAIL):
! 783: texiputchars(p, "Lk ");
! 784: break;
! 785: case (TEXICMD_URL):
! 786: texiputchars(p, "Mt ");
! 787: break;
! 788: default:
! 789: abort();
! 790: }
! 791: while (*pos < sz && '}' != buf[*pos] && ',' != buf[*pos]) {
! 792: texiputchar(p, buf[*pos]);
! 793: advance(p, buf, pos);
! 794: }
! 795: while (*pos < sz && '}' != buf[*pos])
! 796: advance(p, buf, pos);
! 797: if (*pos < sz)
! 798: advance(p, buf, pos);
! 799: if (*pos < sz - 1 &&
! 800: ismpunct(buf[*pos]) &&
! 801: isspace(buf[*pos + 1])) {
! 802: texiputchar(p, ' ');
! 803: texiputchar(p, buf[*pos]);
! 804: advance(p, buf, pos);
! 805: }
! 806: if ( ! p->outmacro)
! 807: texiputchar(p, '\n');
! 808: }
! 809:
! 810: static void
! 811: dosubsection(struct texi *p, enum texicmd cmd,
! 812: const char *buf, size_t sz, size_t *pos)
! 813: {
! 814:
! 815: if (TEXI_IGN & p->flags) {
! 816: advanceeoln(p, buf, sz, pos);
! 817: return;
! 818: }
! 819: while (*pos < sz && ' ' == buf[*pos])
! 820: advance(p, buf, pos);
! 821: texifputs(p, ".Pp");
! 822: while (*pos < sz && '\n' != buf[*pos]) {
! 823: texiputchar(p, buf[*pos]);
! 824: advance(p, buf, pos);
! 825: }
! 826: texifputs(p, ".Pp");
! 827: }
! 828:
! 829: static void
! 830: dosection(struct texi *p, enum texicmd cmd,
! 831: const char *buf, size_t sz, size_t *pos)
! 832: {
! 833:
! 834: if (TEXI_IGN & p->flags) {
! 835: advanceeoln(p, buf, sz, pos);
! 836: return;
! 837: }
! 838: while (*pos < sz && ' ' == buf[*pos])
! 839: advance(p, buf, pos);
! 840: texifputs(p, ".Ss ");
! 841: while (*pos < sz && '\n' != buf[*pos]) {
! 842: texiputchar(p, buf[*pos]);
! 843: advance(p, buf, pos);
! 844: }
! 845: texiputchar(p, '\n');
! 846: }
! 847:
! 848: static void
! 849: dosh(struct texi *p, enum texicmd cmd,
! 850: const char *buf, size_t sz, size_t *pos)
! 851: {
! 852:
! 853: if (TEXI_IGN & p->flags) {
! 854: advanceeoln(p, buf, sz, pos);
! 855: return;
! 856: }
! 857: while (*pos < sz && ' ' == buf[*pos])
! 858: advance(p, buf, pos);
! 859: texifputs(p, ".Sh ");
! 860: while (*pos < sz && '\n' != buf[*pos]) {
! 861: texiputchar(p, toupper(buf[*pos]));
! 862: advance(p, buf, pos);
! 863: }
! 864: texiputchar(p, '\n');
! 865: }
! 866:
! 867: static void
! 868: dotop(struct texi *p, enum texicmd cmd,
! 869: const char *buf, size_t sz, size_t *pos)
! 870: {
! 871:
! 872: p->flags &= ~TEXI_HEADER;
! 873: advanceeoln(p, buf, sz, pos);
! 874: teximacro(p, ".Dd $Mdocdate$");
! 875: teximacro(p, ".Dt SOMETHING 7");
! 876: teximacro(p, ".Os");
! 877: teximacro(p, ".Sh NAME");
! 878: teximacro(p, ".Nm Something");
! 879: teximacro(p, ".Nd Something");
! 880: }
! 881:
! 882: static void
! 883: doitem(struct texi *p, enum texicmd cmd,
! 884: const char *buf, size_t sz, size_t *pos)
! 885: {
! 886: size_t end;
! 887:
! 888: /* See if we have arguments... */
! 889: for (end = *pos; end < sz; end++)
! 890: if (' ' != buf[end] && '\t' != buf[end])
! 891: break;
! 892:
! 893: /* If we have arguments, print them too. */
! 894: if ('\n' != buf[end]) {
! 895: texifputs(p, ".It");
! 896: /* FIXME: process commands. */
! 897: while (*pos < sz && '\n' != buf[*pos]) {
! 898: texiputchar(p, buf[*pos]);
! 899: advance(p, buf, pos);
! 900: }
! 901: texiputchar(p, '\n');
! 902: } else
! 903: teximacro(p, ".It");
! 904: }
! 905:
! 906: static void
! 907: dotable(struct texi *p, enum texicmd cmd,
! 908: const char *buf, size_t sz, size_t *pos)
! 909: {
! 910:
! 911: teximacro(p, ".Bl -tag -width Ds");
! 912: parseto(p, buf, sz, pos, "table");
! 913: teximacro(p, ".El");
! 914: }
! 915:
! 916: static void
! 917: doitemize(struct texi *p, enum texicmd cmd,
! 918: const char *buf, size_t sz, size_t *pos)
! 919: {
! 920:
! 921: teximacro(p, ".Bl -bullet");
! 922: parseto(p, buf, sz, pos, "itemize");
! 923: teximacro(p, ".El");
! 924: }
! 925:
! 926: static void
! 927: doignbracket(struct texi *p, enum texicmd cmd,
! 928: const char *buf, size_t sz, size_t *pos)
! 929: {
! 930: unsigned int sv = p->flags;
! 931:
! 932: p->flags |= TEXI_IGN;
! 933: parsebracket(p, buf, sz, pos);
! 934: p->flags = sv;
! 935: }
! 936:
! 937: static void
! 938: doignline(struct texi *p, enum texicmd cmd,
! 939: const char *buf, size_t sz, size_t *pos)
! 940: {
! 941:
! 942: advanceeoln(p, buf, sz, pos);
! 943: if (*pos < sz)
! 944: advance(p, buf, pos);
! 945: }
! 946:
! 947: static int
! 948: parsefile(struct texi *p, const char *fname)
! 949: {
! 950: struct texifile *f;
! 951: int fd;
! 952: struct stat st;
! 953:
! 954: assert(p->filepos < 64);
! 955: f = &p->files[p->filepos];
! 956: memset(f, 0, sizeof(struct texifile));
! 957:
! 958: f->name = fname;
! 959: if (-1 == (fd = open(fname, O_RDONLY, 0))) {
! 960: texifatal(p, fname);
! 961: } else if (-1 == fstat(fd, &st)) {
! 962: close(fd);
! 963: texifatal(p, fname);
! 964: }
! 965:
! 966: f->mapsz = st.st_size;
! 967: f->map = mmap(NULL, f->mapsz,
! 968: PROT_READ, MAP_SHARED, fd, 0);
! 969: close(fd);
! 970:
! 971: if (MAP_FAILED == f->map) {
! 972: texifatal(p, fname);
! 973: return(0);
! 974: }
! 975:
! 976: p->filepos++;
! 977: parseeof(p, f->map, f->mapsz);
! 978: texifilepop(p);
! 979: return(1);
! 980: }
! 981:
! 982: int
! 983: main(int argc, char *argv[])
! 984: {
! 985: struct texi texi;
! 986: int c, rc;
! 987: const char *progname;
! 988:
! 989: progname = strrchr(argv[0], '/');
! 990: if (progname == NULL)
! 991: progname = argv[0];
! 992: else
! 993: ++progname;
! 994:
! 995: while (-1 != (c = getopt(argc, argv, "")))
! 996: switch (c) {
! 997: default:
! 998: goto usage;
! 999: }
! 1000:
! 1001: argv += optind;
! 1002: if (0 == (argc -= optind))
! 1003: goto usage;
! 1004:
! 1005: memset(&texi, 0, sizeof(struct texi));
! 1006: texi.flags = TEXI_HEADER;
! 1007: rc = parsefile(&texi, argv[0]);
! 1008: return(rc ? EXIT_SUCCESS : EXIT_FAILURE);
! 1009:
! 1010: usage:
! 1011: fprintf(stderr, "usage: %s file\n", progname);
! 1012: return(EXIT_FAILURE);
! 1013: }
CVSweb