[BACK]Return to main.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / texi2mdoc

Annotation of texi2mdoc/main.c, Revision 1.4

1.4     ! kristaps    1: /*     $Id: main.c,v 1.3 2015/02/17 17:02:03 kristaps Exp $ */
1.1       kristaps    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>
1.2       kristaps   24: #include <libgen.h>
                     25: #include <limits.h>
1.1       kristaps   26: #include <stdarg.h>
                     27: #include <stdio.h>
                     28: #include <stdlib.h>
                     29: #include <string.h>
                     30:
                     31: /*
                     32:  * This defines each one of the Texinfo commands that we understand.
                     33:  * Obviously this only refers to native commands; overriden names are a
                     34:  * different story.
                     35:  */
                     36: enum   texicmd {
1.2       kristaps   37:        TEXICMD_ACRONYM,
1.1       kristaps   38:        TEXICMD_A4PAPER,
                     39:        TEXICMD_ANCHOR,
1.2       kristaps   40:        TEXICMD_APPENDIX,
                     41:        TEXICMD_APPENDIXSEC,
1.3       kristaps   42:        TEXICMD_ASTERISK,
1.1       kristaps   43:        TEXICMD_AT,
1.3       kristaps   44:        TEXICMD_AUTHOR,
                     45:        TEXICMD_BANG,
1.1       kristaps   46:        TEXICMD_BYE,
                     47:        TEXICMD_CHAPTER,
                     48:        TEXICMD_CINDEX,
1.3       kristaps   49:        TEXICMD_CITE,
1.1       kristaps   50:        TEXICMD_CODE,
1.3       kristaps   51:        TEXICMD_COLON,
1.1       kristaps   52:        TEXICMD_COMMAND,
                     53:        TEXICMD_COMMENT,
1.2       kristaps   54:        TEXICMD_COMMENT_LONG,
1.1       kristaps   55:        TEXICMD_CONTENTS,
                     56:        TEXICMD_COPYING,
                     57:        TEXICMD_COPYRIGHT,
1.3       kristaps   58:        TEXICMD_DEFTYPEFN,
                     59:        TEXICMD_DEFTYPEFNX,
                     60:        TEXICMD_DEFTYPEFUN,
                     61:        TEXICMD_DEFTYPEFUNX,
                     62:        TEXICMD_DEFTYPEVAR,
                     63:        TEXICMD_DEFTYPEVR,
1.1       kristaps   64:        TEXICMD_DETAILMENU,
1.3       kristaps   65:        TEXICMD_DFN,
1.1       kristaps   66:        TEXICMD_DIRCATEGORY,
                     67:        TEXICMD_DIRENTRY,
1.3       kristaps   68:        TEXICMD_DISPLAY,
1.2       kristaps   69:        TEXICMD_DOTS,
1.1       kristaps   70:        TEXICMD_EMAIL,
                     71:        TEXICMD_EMPH,
                     72:        TEXICMD_END,
1.2       kristaps   73:        TEXICMD_ENUMERATE,
1.3       kristaps   74:        TEXICMD_ENV,
1.1       kristaps   75:        TEXICMD_EXAMPLE,
                     76:        TEXICMD_FILE,
1.3       kristaps   77:        TEXICMD_GROUP,
1.2       kristaps   78:        TEXICMD_HEADING,
1.3       kristaps   79:        TEXICMD_HEADINGS,
                     80:        TEXICMD_HYPHEN,
1.1       kristaps   81:        TEXICMD_I,
1.3       kristaps   82:        TEXICMD_IFCLEAR,
1.1       kristaps   83:        TEXICMD_IFHTML,
1.3       kristaps   84:        TEXICMD_IFINFO,
1.1       kristaps   85:        TEXICMD_IFNOTTEX,
                     86:        TEXICMD_IFTEX,
1.3       kristaps   87:        TEXICMD_IFSET,
1.1       kristaps   88:        TEXICMD_IMAGE,
1.2       kristaps   89:        TEXICMD_INCLUDE,
1.1       kristaps   90:        TEXICMD_ITEM,
                     91:        TEXICMD_ITEMIZE,
                     92:        TEXICMD_KBD,
                     93:        TEXICMD_LATEX,
1.3       kristaps   94:        TEXICMD_MATH,
1.1       kristaps   95:        TEXICMD_MENU,
1.3       kristaps   96:        TEXICMD_NEWLINE,
1.1       kristaps   97:        TEXICMD_NODE,
1.3       kristaps   98:        TEXICMD_NOINDENT,
                     99:        TEXICMD_QUESTIONMARK,
1.1       kristaps  100:        TEXICMD_QUOTATION,
1.3       kristaps  101:        TEXICMD_PAGE,
1.1       kristaps  102:        TEXICMD_PARINDENT,
1.2       kristaps  103:        TEXICMD_PRINTINDEX,
1.1       kristaps  104:        TEXICMD_REF,
                    105:        TEXICMD_SAMP,
                    106:        TEXICMD_SECTION,
1.3       kristaps  107:        TEXICMD_SET,
1.1       kristaps  108:        TEXICMD_SETCHAPNEWPAGE,
                    109:        TEXICMD_SETFILENAME,
                    110:        TEXICMD_SETTITLE,
1.3       kristaps  111:        TEXICMD_SP,
                    112:        TEXICMD_SPACE,
                    113:        TEXICMD_SMALLEXAMPLE,
                    114:        TEXICMD_SQUIGGLE_LEFT,
                    115:        TEXICMD_SQUIGGLE_RIGHT,
1.1       kristaps  116:        TEXICMD_SUBSECTION,
1.3       kristaps  117:        TEXICMD_SUBTITLE,
                    118:        TEXICMD_TAB,
1.1       kristaps  119:        TEXICMD_TABLE,
                    120:        TEXICMD_TEX,
                    121:        TEXICMD_TEXSYM,
1.3       kristaps  122:        TEXICMD_TITLE,
1.1       kristaps  123:        TEXICMD_TITLEFONT,
                    124:        TEXICMD_TITLEPAGE,
                    125:        TEXICMD_TOP,
                    126:        TEXICMD_UNNUMBERED,
1.2       kristaps  127:        TEXICMD_UNNUMBEREDSEC,
1.3       kristaps  128:        TEXICMD_UREF,
1.1       kristaps  129:        TEXICMD_URL,
                    130:        TEXICMD_VAR,
1.3       kristaps  131:        TEXICMD_W,
1.1       kristaps  132:        TEXICMD__MAX
                    133: };
                    134:
                    135: /*
                    136:  * The file currently being parsed.
                    137:  * This keeps track of our location within that file.
                    138:  */
                    139: struct texifile {
                    140:        const char      *name; /* name of the file */
                    141:        size_t           line; /* current line (from zero) */
                    142:        size_t           col; /* current column in line (from zero) */
                    143:        char            *map; /* mmap'd file */
                    144:        size_t           mapsz; /* size of mmap */
                    145: };
                    146:
                    147: struct texi;
                    148:
1.2       kristaps  149: /*
                    150:  * Callback for functions implementing texi commands.
                    151:  */
1.1       kristaps  152: typedef        void (*texicmdfp)(struct texi *,
                    153:        enum texicmd, const char *, size_t, size_t *);
                    154:
                    155: /*
                    156:  * Describes Texinfo commands, whether native or overriden.
                    157:  */
                    158: struct texitok {
                    159:        texicmdfp        fp; /* callback (or NULL if none) */
                    160:        const char      *tok; /* name of the token */
                    161:        size_t           len; /* strlen(tok) */
                    162: };
                    163:
1.3       kristaps  164: enum   texilist {
                    165:        TEXILIST_NONE = 0,
                    166:        TEXILIST_ITEM,
                    167:        TEXILIST_NOITEM,
                    168: };
                    169:
1.1       kristaps  170: /*
                    171:  * The main parse structure.
                    172:  * This keeps any necessary information handy.
                    173:  */
                    174: struct texi {
                    175:        struct texifile  files[64];
                    176:        size_t           filepos;
                    177:        size_t           outcol; /* column of output */
                    178:        int              outmacro; /* whether output is in line macro */
                    179:        int              seenws; /* whitespace has been ignored */
1.3       kristaps  180:        int              ign; /* don't print anything */
                    181:        int              literal;
1.2       kristaps  182:        char            *dir; /* texi directory */
1.3       kristaps  183:        enum texilist    list;
1.1       kristaps  184: };
                    185:
1.2       kristaps  186: /* FIXME: don't use this crap. */
1.1       kristaps  187: #define        ismpunct(_x) \
                    188:        ('.' == (_x) || \
                    189:         ',' == (_x) || \
                    190:         ';' == (_x))
1.2       kristaps  191: #define        isws(_x) \
                    192:        (' ' == (_x) || '\t' == (_x))
1.1       kristaps  193:
                    194: static void doarg1(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.3       kristaps  195: static void doblock(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.1       kristaps  196: static void dobracket(struct texi *, enum texicmd, const char *, size_t, size_t *);
                    197: static void dobye(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.3       kristaps  198: static void dochapter(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.1       kristaps  199: static void docommand(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.3       kristaps  200: static void dodeftypefun(struct texi *, enum texicmd, const char *, size_t, size_t *);
                    201: static void dodeftypevar(struct texi *, enum texicmd, const char *, size_t, size_t *);
                    202: static void dodisplay(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.1       kristaps  203: static void doemph(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.2       kristaps  204: static void doenumerate(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.3       kristaps  205: static void doenv(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.1       kristaps  206: static void doexample(struct texi *, enum texicmd, const char *, size_t, size_t *);
                    207: static void dofile(struct texi *, enum texicmd, const char *, size_t, size_t *);
                    208: static void doignblock(struct texi *, enum texicmd, const char *, size_t, size_t *);
                    209: static void doignbracket(struct texi *, enum texicmd, const char *, size_t, size_t *);
                    210: static void doignline(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.2       kristaps  211: static void doinclude(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.1       kristaps  212: static void doitalic(struct texi *, enum texicmd, const char *, size_t, size_t *);
                    213: static void doitem(struct texi *, enum texicmd, const char *, size_t, size_t *);
                    214: static void doitemize(struct texi *, enum texicmd, const char *, size_t, size_t *);
                    215: static void doliteral(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.3       kristaps  216: static void domath(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.1       kristaps  217: static void doquotation(struct texi *, enum texicmd, const char *, size_t, size_t *);
                    218: static void dotable(struct texi *, enum texicmd, const char *, size_t, size_t *);
                    219: static void dotop(struct texi *, enum texicmd, const char *, size_t, size_t *);
                    220: static void dosection(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.3       kristaps  221: static void dosp(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.1       kristaps  222: static void dosubsection(struct texi *, enum texicmd, const char *, size_t, size_t *);
                    223: static void dosymbol(struct texi *, enum texicmd, const char *, size_t, size_t *);
                    224:
                    225: static const struct texitok texitoks[TEXICMD__MAX] = {
1.2       kristaps  226:        { doarg1, "acronym", 7 }, /* TEXICMD_ACRONYM */
1.1       kristaps  227:        { doignline, "afourpaper", 10 }, /* TEXICMD_A4PAPER */
                    228:        { doignbracket, "anchor", 6 }, /* TEXICMD_ANCHOR */
1.3       kristaps  229:        { dochapter, "appendix", 8 }, /* TEXICMD_APPENDIX */
                    230:        { dochapter, "appendixsec", 11 }, /* TEXICMD_APPENDIXSEC */
                    231:        { dosymbol, "*", 1 }, /* TEXICMD_ASTERISK */
1.1       kristaps  232:        { dosymbol, "@", 1 }, /* TEXICMD_AT */
1.3       kristaps  233:        { doignline, "author", 6 }, /* TEXICMD_AUTHOR */
                    234:        { dosymbol, "!", 1 }, /* TEXICMD_BANG */
1.1       kristaps  235:        { dobye, "bye", 3 }, /* TEXICMD_BYE */
1.3       kristaps  236:        { dochapter, "chapter", 7 }, /* TEXICMD_CHAPTER */
1.1       kristaps  237:        { doignline, "cindex", 6 }, /* TEXICMD_CINDEX */
                    238:        { doliteral, "code", 4 }, /* TEXICMD_CODE */
1.3       kristaps  239:        { doitalic, "cite", 4 }, /* TEXICMD_CITE */
                    240:        { dosymbol, ":", 1 }, /* TEXICMD_COLON */
1.1       kristaps  241:        { docommand, "command", 7 }, /* TEXICMD_COMMAND */
                    242:        { doignline, "c", 1 }, /* TEXICMD_COMMENT */
1.2       kristaps  243:        { doignline, "comment", 7 }, /* TEXICMD_COMMENT_LONG */
1.1       kristaps  244:        { doignline, "contents", 8 }, /* TEXICMD_CONTENTS */
                    245:        { doignblock, "copying", 7 }, /* TEXICMD_COPYING */
                    246:        { dosymbol, "copyright", 9 }, /* TEXICMD_COPYRIGHT */
1.3       kristaps  247:        { dodeftypefun, "deftypefn", 9 }, /* TEXICMD_DEFTYPEFN */
                    248:        { dodeftypefun, "deftypefnx", 10 }, /* TEXICMD_DEFTYPEFNX */
                    249:        { dodeftypefun, "deftypefun", 10 }, /* TEXICMD_DEFTYPEFUN */
                    250:        { dodeftypefun, "deftypefunx", 11 }, /* TEXICMD_DEFTYPEFUNX */
                    251:        { dodeftypevar, "deftypevar", 10 }, /* TEXICMD_DEFTYPEVAR */
                    252:        { dodeftypevar, "deftypevr", 9 }, /* TEXICMD_DEFTYPEVR */
1.1       kristaps  253:        { doignblock, "detailmenu", 10 }, /* TEXICMD_DETAILMENU */
1.3       kristaps  254:        { doitalic, "dfn", 3 }, /* TEXICMD_DFN */
1.1       kristaps  255:        { doignline, "dircategory", 11 }, /* TEXICMD_DIRCATEGORY */
                    256:        { doignblock, "direntry", 8 }, /* TEXICMD_DIRENTRY */
1.3       kristaps  257:        { dodisplay, "display", 7 }, /* TEXICMD_DISPLAY */
1.2       kristaps  258:        { dosymbol, "dots", 4 }, /* TEXICMD_DOTS */
1.1       kristaps  259:        { doarg1, "email", 5 }, /* TEXICMD_EMAIL */
                    260:        { doemph, "emph", 4 }, /* TEXICMD_EMPH */
                    261:        { NULL, "end", 3 }, /* TEXICMD_END */
1.2       kristaps  262:        { doenumerate, "enumerate", 9 }, /* TEXICMD_ENUMERATE */
1.3       kristaps  263:        { doenv, "env", 3 }, /* TEXICMD_ENV */
1.1       kristaps  264:        { doexample, "example", 7 }, /* TEXICMD_EXAMPLE */
                    265:        { dofile, "file", 4 }, /* TEXICMD_FILE */
1.3       kristaps  266:        { doblock, "group", 5 }, /* TEXICMD_GROUP */
1.2       kristaps  267:        { dosection, "heading", 7 }, /* TEXICMD_HEADING */
1.3       kristaps  268:        { doignline, "headings", 8 }, /* TEXICMD_HEADINGS */
                    269:        { dosymbol, "-", 1 }, /* TEXICMD_HYPHEN */
1.1       kristaps  270:        { doitalic, "i", 1 }, /* TEXICMD_I */
1.3       kristaps  271:        { doignblock, "ifclear", 7 }, /* TEXICMD_IFCLEAR */
1.1       kristaps  272:        { doignblock, "ifhtml", 6 }, /* TEXICMD_IFHTML */
1.3       kristaps  273:        { doignblock, "ifinfo", 6 }, /* TEXICMD_IFINFO */
                    274:        { doblock, "ifnottex", 8 }, /* TEXICMD_IFNOTTEX */
1.1       kristaps  275:        { doignblock, "iftex", 5 }, /* TEXICMD_IFTEX */
1.3       kristaps  276:        { doignblock, "ifset", 5 }, /* TEXICMD_IFSET */
1.1       kristaps  277:        { doignbracket, "image", 5 }, /* TEXICMD_IMAGE */
1.2       kristaps  278:        { doinclude, "include", 7 }, /* TEXICMD_INCLUDE */
1.1       kristaps  279:        { doitem, "item", 4 }, /* TEXICMD_ITEM */
                    280:        { doitemize, "itemize", 7 }, /* TEXICMD_ITEMIZE */
                    281:        { doliteral, "kbd", 3 }, /* TEXICMD_KBD */
                    282:        { dosymbol, "LaTeX", 5 }, /* TEXICMD_LATEX */
1.3       kristaps  283:        { domath, "math", 4 }, /* TEXICMD_MATH */
1.1       kristaps  284:        { doignblock, "menu", 4 }, /* TEXICMD_MENU */
1.3       kristaps  285:        { dosymbol, "\n", 1 }, /* TEXICMD_NEWLINE */
1.1       kristaps  286:        { doignline, "node", 4 }, /* TEXICMD_NODE */
1.3       kristaps  287:        { doignline, "noindent", 8 }, /* TEXICMD_NOINDENT */
                    288:        { dosymbol, "?", 1 }, /* TEXICMD_QUESTIONMARK */
1.1       kristaps  289:        { doquotation, "quotation", 9 }, /* TEXICMD_QUOTATION */
1.3       kristaps  290:        { doignline, "page", 4 }, /* TEXICMD_PAGE */
                    291:        { doignline, "paragraphindent", 14 }, /* TEXICMD_PARINDENT */
1.2       kristaps  292:        { doignline, "printindex", 10 }, /* TEXICMD_PRINTINDEX */
1.1       kristaps  293:        { dobracket, "ref", 3 }, /* TEXICMD_REF */
                    294:        { doliteral, "samp", 4 }, /* TEXICMD_SAMP */
                    295:        { dosection, "section", 7 }, /* TEXICMD_SECTION */
1.3       kristaps  296:        { doignline, "set", 3 }, /* TEXICMD_SET */
1.1       kristaps  297:        { doignline, "setchapternewpage", 17 }, /* TEXICMD_SETCHAPNEWPAGE */
                    298:        { doignline, "setfilename", 11 }, /* TEXICMD_SETFILENAME */
1.3       kristaps  299:        { dosp, "sp", 2 }, /* TEXICMD_SP */
                    300:        { dosymbol, " ", 1 }, /* TEXICMD_SPACE */
                    301:        { doexample, "smallexample", 12 }, /* TEXICMD_SMALLEXAMPLE */
1.1       kristaps  302:        { doignline, "settitle", 8 }, /* TEXICMD_SETTITLE */
1.3       kristaps  303:        { dosymbol, "{", 1 }, /* TEXICMD_SQUIGGLE_LEFT */
                    304:        { dosymbol, "}", 1 }, /* TEXICMD_SQUIGGLE_RIGHT */
1.1       kristaps  305:        { dosubsection, "subsection", 10 }, /* TEXICMD_SUBSECTION */
1.3       kristaps  306:        { doignline, "subtitle", 8 }, /* TEXICMD_SUBTITLE */
                    307:        { dosymbol, "\t", 1 }, /* TEXICMD_TAB */
1.1       kristaps  308:        { dotable, "table", 5 }, /* TEXICMD_TABLE */
                    309:        { doignblock, "tex", 3 }, /* TEXICMD_TEX */
                    310:        { dosymbol, "TeX", 3 }, /* TEXICMD_TEXSYM */
1.3       kristaps  311:        { doignline, "title", 5 }, /* TEXICMD_TITLE */
1.1       kristaps  312:        { dobracket, "titlefont", 9 }, /* TEXICMD_TITLEFONT */
                    313:        { doignblock, "titlepage", 9 }, /* TEXICMD_TITLEPAGE */
                    314:        { dotop, "top", 3 }, /* TEXICMD_TOP */
1.3       kristaps  315:        { dochapter, "unnumbered", 10 }, /* TEXICMD_UNNUMBERED */
1.2       kristaps  316:        { dosection, "unnumberedsec", 13 }, /* TEXICMD_UNNUMBEREDSEC */
1.3       kristaps  317:        { doarg1, "uref", 4 }, /* TEXICMD_UREF */
1.1       kristaps  318:        { doarg1, "url", 3 }, /* TEXICMD_URL */
                    319:        { doliteral, "var", 3 }, /* TEXICMD_VAR */
1.3       kristaps  320:        { dobracket, "w", 1 }, /* TEXICMD_W */
1.1       kristaps  321: };
                    322:
1.2       kristaps  323: /*
                    324:  * Unmap the top-most file that we're using.
                    325:  */
1.1       kristaps  326: static void
                    327: texifilepop(struct texi *p)
                    328: {
                    329:        struct texifile *f;
                    330:
                    331:        assert(p->filepos > 0);
                    332:        f = &p->files[--p->filepos];
                    333:        munmap(f->map, f->mapsz);
                    334: }
                    335:
1.2       kristaps  336: /*
                    337:  * Unmap all files that we're currently using.
                    338:  * The utility should exit(...) after this is called.
                    339:  */
1.1       kristaps  340: static void
                    341: texiexit(struct texi *p)
                    342: {
                    343:
                    344:        while (p->filepos > 0)
                    345:                texifilepop(p);
1.2       kristaps  346:        free(p->dir);
1.3       kristaps  347:        if (p->outcol)
                    348:                putchar('\n');
1.1       kristaps  349: }
                    350:
1.2       kristaps  351: /*
                    352:  * Fatal error: unmap all files and exit.
                    353:  * The "errstring" is passed to perror(3).
                    354:  */
1.1       kristaps  355: static void
1.2       kristaps  356: texiabort(struct texi *p, const char *errstring)
1.1       kristaps  357: {
                    358:
                    359:        perror(errstring);
                    360:        texiexit(p);
                    361:        exit(EXIT_FAILURE);
                    362: }
                    363:
                    364: /*
                    365:  * Print a generic warning message (to stderr) tied to our current
                    366:  * location in the parse sequence.
                    367:  */
                    368: static void
                    369: texiwarn(const struct texi *p, const char *fmt, ...)
                    370: {
                    371:        va_list  ap;
                    372:
1.2       kristaps  373:        fprintf(stderr, "%s:%zu:%zu: warning: ",
1.1       kristaps  374:                p->files[p->filepos - 1].name,
                    375:                p->files[p->filepos - 1].line + 1,
                    376:                p->files[p->filepos - 1].col + 1);
                    377:        va_start(ap, fmt);
                    378:        vfprintf(stderr, fmt, ap);
                    379:        va_end(ap);
                    380:        fputc('\n', stderr);
                    381: }
                    382:
1.2       kristaps  383: static void
                    384: texierr(struct texi *p, const char *fmt, ...)
                    385: {
                    386:        va_list  ap;
                    387:
                    388:        fprintf(stderr, "%s:%zu:%zu: error: ",
                    389:                p->files[p->filepos - 1].name,
                    390:                p->files[p->filepos - 1].line + 1,
                    391:                p->files[p->filepos - 1].col + 1);
                    392:        va_start(ap, fmt);
                    393:        vfprintf(stderr, fmt, ap);
                    394:        va_end(ap);
                    395:        fputc('\n', stderr);
                    396:        texiexit(p);
                    397:        exit(EXIT_FAILURE);
                    398: }
                    399:
1.1       kristaps  400: /*
                    401:  * Put a single data character.
                    402:  * This MUST NOT be a mdoc(7) command: it should be free text that's
                    403:  * outputted to the screen.
                    404:  */
                    405: static void
                    406: texiputchar(struct texi *p, char c)
                    407: {
                    408:
1.3       kristaps  409:        if (p->ign)
1.1       kristaps  410:                return;
                    411:        putchar(c);
                    412:        if ('\n' == c) {
                    413:                p->outcol = 0;
                    414:                p->seenws = 0;
                    415:        } else
                    416:                p->outcol++;
                    417: }
                    418:
                    419: /*
                    420:  * Put multiple characters (see texiputchar()).
                    421:  */
                    422: static void
                    423: texiputchars(struct texi *p, const char *s)
                    424: {
                    425:
                    426:        while ('\0' != *s)
                    427:                texiputchar(p, *s++);
                    428: }
                    429:
                    430: /*
                    431:  * Put an mdoc(7) command without the trailing newline.
                    432:  * This should ONLY be used for mdoc(7) commands!
                    433:  */
                    434: static void
1.3       kristaps  435: teximacroclose(struct texi *p)
                    436: {
                    437:
                    438:        p->outmacro--;
                    439:        if (p->ign)
                    440:                return;
                    441:        texiputchar(p, '\n');
                    442: }
                    443:
                    444: /*
                    445:  * Put an mdoc(7) command without the trailing newline.
                    446:  * This should ONLY be used for mdoc(7) commands!
                    447:  */
                    448: static void
                    449: teximacroopen(struct texi *p, const char *s)
1.1       kristaps  450: {
                    451:        int      rc;
                    452:
1.3       kristaps  453:        p->outmacro++;
                    454:        if (p->ign)
1.1       kristaps  455:                return;
                    456:        if (p->outcol)
                    457:                texiputchar(p, '\n');
                    458:        if (EOF != (rc = fputs(s, stdout)))
                    459:                p->outcol += rc;
                    460: }
                    461:
                    462: /*
                    463:  * Put an mdoc(7) command with the trailing newline.
                    464:  * This should ONLY be used for mdoc(7) commands!
                    465:  */
                    466: static void
                    467: teximacro(struct texi *p, const char *s)
                    468: {
                    469:
1.4     ! kristaps  470:        if (p->outmacro)
        !           471:                texierr(p, "\"%s\" in open line scope!?", s);
        !           472:        else if (p->literal)
        !           473:                texierr(p, "\"%s\" in a literal scope!?", s);
        !           474:
1.3       kristaps  475:        if (p->ign)
1.1       kristaps  476:                return;
                    477:        if (p->outcol)
                    478:                texiputchar(p, '\n');
                    479:        puts(s);
                    480:        p->outcol = 0;
                    481:        p->seenws = 0;
                    482: }
                    483:
                    484: /*
                    485:  * Advance by a single byte in the input stream.
                    486:  */
                    487: static void
                    488: advance(struct texi *p, const char *buf, size_t *pos)
                    489: {
                    490:
                    491:        if ('\n' == buf[*pos]) {
                    492:                p->files[p->filepos - 1].line++;
                    493:                p->files[p->filepos - 1].col = 0;
                    494:        } else
                    495:                p->files[p->filepos - 1].col++;
                    496:
                    497:        (*pos)++;
                    498: }
                    499:
                    500: /*
                    501:  * Advance to the next non-whitespace word in the input stream.
                    502:  * If we're in literal mode, then print all of the whitespace as we're
                    503:  * doing so.
                    504:  */
                    505: static size_t
                    506: advancenext(struct texi *p, const char *buf, size_t sz, size_t *pos)
                    507: {
                    508:
1.3       kristaps  509:        if (p->literal) {
1.1       kristaps  510:                while (*pos < sz && isspace(buf[*pos])) {
                    511:                        texiputchar(p, buf[*pos]);
                    512:                        advance(p, buf, pos);
                    513:                }
                    514:                return(*pos);
                    515:        }
                    516:
                    517:        while (*pos < sz && isspace(buf[*pos])) {
                    518:                p->seenws = 1;
                    519:                /*
                    520:                 * If it looks like we've printed a double-line, then
                    521:                 * output a paragraph.
                    522:                 * FIXME: this is stupid.
                    523:                 */
                    524:                if (*pos && '\n' == buf[*pos] && '\n' == buf[*pos - 1])
                    525:                        teximacro(p, ".Pp");
                    526:                advance(p, buf, pos);
                    527:        }
                    528:        return(*pos);
                    529: }
                    530:
                    531: /*
                    532:  * Advance to the EOLN in the input stream.
                    533:  */
                    534: static size_t
1.3       kristaps  535: advanceeoln(struct texi *p, const char *buf,
                    536:        size_t sz, size_t *pos, int consumenl)
1.1       kristaps  537: {
                    538:
                    539:        while (*pos < sz && '\n' != buf[*pos])
                    540:                advance(p, buf, pos);
1.3       kristaps  541:        if (*pos < sz && consumenl)
                    542:                advance(p, buf, pos);
1.1       kristaps  543:        return(*pos);
                    544: }
                    545:
                    546: /*
                    547:  * Advance to position "end", which is an absolute position in the
                    548:  * current buffer greater than or equal to the current position.
                    549:  */
                    550: static void
                    551: advanceto(struct texi *p, const char *buf, size_t *pos, size_t end)
                    552: {
                    553:
                    554:        assert(*pos <= end);
                    555:        while (*pos < end)
                    556:                advance(p, buf, pos);
                    557: }
                    558:
                    559: /*
                    560:  * Output a free-form word in the input stream, progressing to the next
                    561:  * command or white-space.
                    562:  * This also will advance the input stream.
                    563:  */
                    564: static void
                    565: texiword(struct texi *p, const char *buf, size_t sz, size_t *pos)
                    566: {
                    567:
                    568:        /*
                    569:         * XXX: if we're in literal mode, then we shouldn't do any
                    570:         * reflowing of text here.
                    571:         */
1.3       kristaps  572:        if (0 == p->outmacro && p->outcol > 72 && 0 == p->literal)
1.1       kristaps  573:                texiputchar(p, '\n');
                    574:
1.3       kristaps  575:        if (p->seenws && p->outcol && 0 == p->literal)
1.1       kristaps  576:                texiputchar(p, ' ');
                    577:
                    578:        p->seenws = 0;
                    579:
                    580:        while (*pos < sz && ! isspace(buf[*pos])) {
                    581:                switch (buf[*pos]) {
                    582:                case ('@'):
                    583:                case ('}'):
                    584:                case ('{'):
                    585:                        return;
                    586:                }
                    587:                if (*pos < sz - 1 &&
                    588:                         '`' == buf[*pos] &&
                    589:                         '`' == buf[*pos + 1]) {
                    590:                        texiputchars(p, "\\(lq");
                    591:                        advance(p, buf, pos);
                    592:                } else if (*pos < sz - 1 &&
                    593:                         '\'' == buf[*pos] &&
                    594:                         '\'' == buf[*pos + 1]) {
                    595:                        texiputchars(p, "\\(rq");
                    596:                        advance(p, buf, pos);
                    597:                } else
                    598:                        texiputchar(p, buf[*pos]);
                    599:                advance(p, buf, pos);
                    600:        }
                    601: }
                    602:
                    603: static enum texicmd
                    604: texicmd(struct texi *p, const char *buf,
                    605:        size_t pos, size_t sz, size_t *end)
                    606: {
                    607:        size_t   i, len;
                    608:
                    609:        assert('@' == buf[pos]);
1.3       kristaps  610:
                    611:        if (++pos >= sz)
                    612:                return(TEXICMD__MAX);
                    613:
                    614:        /* Alphabetic commands are special. */
                    615:        if ( ! isalpha(buf[pos])) {
                    616:                *end = pos + 1;
                    617:                for (i = 0; i < TEXICMD__MAX; i++) {
                    618:                        if (1 != texitoks[i].len)
                    619:                                continue;
                    620:                        if (0 == strncmp(texitoks[i].tok, &buf[pos], 1))
                    621:                                return(i);
                    622:                }
                    623:                texiwarn(p, "bad command: @%c", buf[pos]);
                    624:                return(TEXICMD__MAX);
                    625:        }
                    626:
                    627:        for (*end = pos; *end < sz && ! isspace(buf[*end]); (*end)++)
                    628:                if ((*end > pos && ('@' == buf[*end] ||
                    629:                          '{' == buf[*end] || '}' == buf[*end])))
1.1       kristaps  630:                        break;
                    631:
                    632:        len = *end - pos;
                    633:        for (i = 0; i < TEXICMD__MAX; i++) {
                    634:                if (len != texitoks[i].len)
                    635:                        continue;
                    636:                if (0 == strncmp(texitoks[i].tok, &buf[pos], len))
                    637:                        return(i);
                    638:        }
                    639:
1.3       kristaps  640:        texiwarn(p, "bad command: @%.*s", (int)len, &buf[pos]);
1.1       kristaps  641:        return(TEXICMD__MAX);
                    642: }
                    643:
                    644: static void
                    645: parseeof(struct texi *p, const char *buf, size_t sz)
                    646: {
                    647:        size_t           pos = 0;
                    648:        enum texicmd     cmd;
                    649:        size_t           end;
                    650:
                    651:        while ((pos = advancenext(p, buf, sz, &pos)) < sz) {
                    652:                switch (buf[pos]) {
                    653:                case ('}'):
1.3       kristaps  654:                        if (0 == p->ign)
                    655:                                texiwarn(p, "unexpected \"}\"");
1.1       kristaps  656:                        advance(p, buf, &pos);
                    657:                        continue;
                    658:                case ('{'):
1.3       kristaps  659:                        if (0 == p->ign)
                    660:                                texiwarn(p, "unexpected \"{\"");
1.1       kristaps  661:                        advance(p, buf, &pos);
                    662:                        continue;
                    663:                case ('@'):
                    664:                        break;
                    665:                default:
                    666:                        texiword(p, buf, sz, &pos);
                    667:                        continue;
                    668:                }
                    669:
                    670:                cmd = texicmd(p, buf, pos, sz, &end);
                    671:                advanceto(p, buf, &pos, end);
                    672:                if (TEXICMD__MAX == cmd)
                    673:                        continue;
                    674:                if (NULL != texitoks[cmd].fp)
                    675:                        (*texitoks[cmd].fp)(p, cmd, buf, sz, &pos);
                    676:        }
                    677: }
                    678:
                    679: static void
                    680: parsebracket(struct texi *p, const char *buf, size_t sz, size_t *pos)
                    681: {
                    682:        size_t           end;
                    683:        enum texicmd     cmd;
                    684:
1.3       kristaps  685:        while (*pos < sz && isspace(buf[*pos]))
                    686:                advance(p, buf, pos);
                    687:
1.1       kristaps  688:        if (*pos == sz || '{' != buf[*pos])
                    689:                return;
                    690:        advance(p, buf, pos);
                    691:
                    692:        while ((*pos = advancenext(p, buf, sz, pos)) < sz) {
                    693:                switch (buf[*pos]) {
                    694:                case ('}'):
                    695:                        advance(p, buf, pos);
                    696:                        return;
                    697:                case ('{'):
1.3       kristaps  698:                        if (0 == p->ign)
                    699:                                texiwarn(p, "unexpected \"{\"");
                    700:                        advance(p, buf, pos);
                    701:                        continue;
                    702:                case ('@'):
                    703:                        break;
                    704:                default:
                    705:                        texiword(p, buf, sz, pos);
                    706:                        continue;
                    707:                }
                    708:
                    709:                cmd = texicmd(p, buf, *pos, sz, &end);
                    710:                advanceto(p, buf, pos, end);
                    711:                if (TEXICMD__MAX == cmd)
                    712:                        continue;
                    713:                if (NULL != texitoks[cmd].fp)
                    714:                        (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
                    715:        }
                    716: }
                    717:
                    718: /*
                    719:  * This should be invoked when we're on a macro line and want to process
                    720:  * to the end of the current input line, doing all of our macros along
                    721:  * the way.
                    722:  */
                    723: static void
                    724: parseeoln(struct texi *p, const char *buf, size_t sz, size_t *pos)
                    725: {
                    726:        size_t           end;
                    727:        enum texicmd     cmd;
                    728:
                    729:        assert(0 == p->literal);
                    730:
                    731:        while (*pos < sz && '\n' != buf[*pos]) {
                    732:                while (*pos < sz && isws(buf[*pos])) {
                    733:                        p->seenws = 1;
                    734:                        advance(p, buf, pos);
                    735:                }
                    736:                switch (buf[*pos]) {
                    737:                case ('}'):
                    738:                        if (0 == p->ign)
                    739:                                texiwarn(p, "unexpected \"}\"");
                    740:                        advance(p, buf, pos);
                    741:                        continue;
                    742:                case ('{'):
                    743:                        if (0 == p->ign)
                    744:                                texiwarn(p, "unexpected \"{\"");
1.1       kristaps  745:                        advance(p, buf, pos);
                    746:                        continue;
                    747:                case ('@'):
                    748:                        break;
                    749:                default:
                    750:                        texiword(p, buf, sz, pos);
                    751:                        continue;
                    752:                }
                    753:
                    754:                cmd = texicmd(p, buf, *pos, sz, &end);
                    755:                advanceto(p, buf, pos, end);
                    756:                if (TEXICMD__MAX == cmd)
                    757:                        continue;
                    758:                if (NULL != texitoks[cmd].fp)
                    759:                        (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
                    760:        }
                    761: }
                    762:
                    763: static void
1.3       kristaps  764: parsesingle(struct texi *p, const char *buf, size_t sz, size_t *pos)
                    765: {
                    766:        size_t           end;
                    767:        enum texicmd     cmd;
                    768:
                    769:        if ((*pos = advancenext(p, buf, sz, pos)) >= sz)
                    770:                return;
                    771:
                    772:        switch (buf[*pos]) {
                    773:        case ('}'):
                    774:                if (0 == p->ign)
                    775:                        texiwarn(p, "unexpected \"}\"");
                    776:                advance(p, buf, pos);
                    777:                return;
                    778:        case ('{'):
                    779:                if (0 == p->ign)
                    780:                        texiwarn(p, "unexpected \"{\"");
                    781:                advance(p, buf, pos);
                    782:                return;
                    783:        case ('@'):
                    784:                break;
                    785:        default:
                    786:                texiword(p, buf, sz, pos);
                    787:                return;
                    788:        }
                    789:
                    790:        cmd = texicmd(p, buf, *pos, sz, &end);
                    791:        advanceto(p, buf, pos, end);
                    792:        if (TEXICMD__MAX == cmd)
                    793:                return;
                    794:        if (NULL != texitoks[cmd].fp)
                    795:                (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
                    796: }
                    797:
                    798: static void
1.1       kristaps  799: parseto(struct texi *p, const char *buf,
                    800:        size_t sz, size_t *pos, const char *endtoken)
                    801: {
                    802:        size_t           end;
                    803:        enum texicmd     cmd;
                    804:        size_t           endtoksz;
                    805:
                    806:        endtoksz = strlen(endtoken);
                    807:        assert(endtoksz > 0);
                    808:
                    809:        while ((*pos = advancenext(p, buf, sz, pos)) < sz) {
                    810:                switch (buf[*pos]) {
                    811:                case ('}'):
1.3       kristaps  812:                        if (0 == p->ign)
                    813:                                texiwarn(p, "unexpected \"}\"");
1.1       kristaps  814:                        advance(p, buf, pos);
                    815:                        continue;
                    816:                case ('{'):
1.3       kristaps  817:                        if (0 == p->ign)
                    818:                                texiwarn(p, "unexpected \"{\"");
1.1       kristaps  819:                        advance(p, buf, pos);
                    820:                        continue;
                    821:                case ('@'):
                    822:                        break;
                    823:                default:
                    824:                        texiword(p, buf, sz, pos);
                    825:                        continue;
                    826:                }
                    827:
                    828:                cmd = texicmd(p, buf, *pos, sz, &end);
                    829:                advanceto(p, buf, pos, end);
                    830:                if (TEXICMD_END == cmd) {
1.2       kristaps  831:                        while (*pos < sz && isws(buf[*pos]))
1.1       kristaps  832:                                advance(p, buf, pos);
                    833:                        /*
                    834:                         * FIXME: skip tabs and also check the full
                    835:                         * word, not just its initial substring!
                    836:                         */
                    837:                        if (sz - *pos >= endtoksz && 0 == strncmp
                    838:                                 (&buf[*pos], endtoken, endtoksz)) {
1.3       kristaps  839:                                advanceeoln(p, buf, sz, pos, 0);
1.1       kristaps  840:                                break;
                    841:                        }
1.3       kristaps  842:                        if (0 == p->ign)
                    843:                                texiwarn(p, "unexpected \"end\"");
                    844:                        advanceeoln(p, buf, sz, pos, 0);
1.1       kristaps  845:                        continue;
                    846:                } else if (TEXICMD__MAX != cmd)
                    847:                        if (NULL != texitoks[cmd].fp)
                    848:                                (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
                    849:        }
                    850: }
                    851:
                    852: static void
1.2       kristaps  853: parsefile(struct texi *p, const char *fname)
                    854: {
                    855:        struct texifile  *f;
                    856:        int               fd;
                    857:        struct stat       st;
                    858:
                    859:        assert(p->filepos < 64);
                    860:        f = &p->files[p->filepos];
                    861:        memset(f, 0, sizeof(struct texifile));
                    862:
                    863:        f->name = fname;
                    864:        if (-1 == (fd = open(fname, O_RDONLY, 0))) {
                    865:                texiabort(p, fname);
                    866:        } else if (-1 == fstat(fd, &st)) {
                    867:                close(fd);
                    868:                texiabort(p, fname);
                    869:        }
                    870:
                    871:        f->mapsz = st.st_size;
                    872:        f->map = mmap(NULL, f->mapsz,
                    873:                PROT_READ, MAP_SHARED, fd, 0);
                    874:        close(fd);
                    875:
                    876:        if (MAP_FAILED == f->map)
                    877:                texiabort(p, fname);
                    878:
                    879:        p->filepos++;
                    880:        parseeof(p, f->map, f->mapsz);
                    881:        texifilepop(p);
                    882: }
                    883:
                    884: static void
1.3       kristaps  885: dodeftypevar(struct texi *p, enum texicmd cmd,
                    886:        const char *buf, size_t sz, size_t *pos)
                    887: {
                    888:        const char      *blk;
                    889:
                    890:        blk = TEXICMD_DEFTYPEVR == cmd ?
                    891:                "deftypevr" : "deftypevar";
                    892:
                    893:        if (p->ign) {
                    894:                parseto(p, buf, sz, pos, blk);
                    895:                return;
                    896:        }
                    897:
                    898:        teximacro(p, ".Pp");
                    899:        if (TEXICMD_DEFTYPEVR == cmd) {
                    900:                parsebracket(p, buf, sz, pos);
                    901:                texiputchars(p, ":\n");
                    902:        }
                    903:        teximacroopen(p, ".Vt ");
1.4     ! kristaps  904:        parseeoln(p, buf, sz, pos);
1.3       kristaps  905:        teximacroclose(p);
                    906:        teximacro(p, ".Pp");
                    907:        parseto(p, buf, sz, pos, blk);
                    908: }
                    909:
                    910: static void
                    911: dodeftypefun(struct texi *p, enum texicmd cmd,
                    912:        const char *buf, size_t sz, size_t *pos)
                    913: {
                    914:        const char      *blk;
                    915:
                    916:        switch (cmd) {
                    917:        case (TEXICMD_DEFTYPEFN):
                    918:                blk = "deftypefn";
                    919:                break;
                    920:        case (TEXICMD_DEFTYPEFUN):
                    921:                blk = "deftypefun";
                    922:                break;
                    923:        case (TEXICMD_DEFTYPEFNX):
                    924:        case (TEXICMD_DEFTYPEFUNX):
                    925:                blk = NULL;
                    926:                break;
                    927:        default:
                    928:                abort();
                    929:        }
                    930:
                    931:        if (p->ign) {
                    932:                if (NULL != blk)
                    933:                        parseto(p, buf, sz, pos, blk);
                    934:                return;
                    935:        }
                    936:
                    937:        switch (cmd) {
                    938:        case (TEXICMD_DEFTYPEFN):
                    939:        case (TEXICMD_DEFTYPEFUN):
                    940:                teximacro(p, ".Pp");
                    941:                break;
                    942:        default:
                    943:                break;
                    944:        }
                    945:        if (TEXICMD_DEFTYPEFN == cmd ||
                    946:                        TEXICMD_DEFTYPEFNX == cmd) {
                    947:                parsebracket(p, buf, sz, pos);
                    948:                texiputchars(p, ":\n");
                    949:        }
                    950:        teximacroopen(p, ".Ft ");
                    951:        parsesingle(p, buf, sz, pos);
                    952:        teximacroclose(p);
                    953:        teximacroopen(p, ".Fn ");
                    954:        parsesingle(p, buf, sz, pos);
                    955:        teximacroclose(p);
                    956:        teximacroopen(p, ".Li ");
1.4     ! kristaps  957:        parseeoln(p, buf, sz, pos);
1.3       kristaps  958:        teximacroclose(p);
                    959:        teximacro(p, ".Pp");
                    960:        if (NULL != blk)
                    961:                parseto(p, buf, sz, pos, blk);
                    962: }
                    963:
                    964: static void
1.1       kristaps  965: doignblock(struct texi *p, enum texicmd cmd,
                    966:        const char *buf, size_t sz, size_t *pos)
                    967: {
1.3       kristaps  968:        const char      *blk;
1.1       kristaps  969:
                    970:        switch (cmd) {
                    971:        case (TEXICMD_COPYING):
1.3       kristaps  972:                blk = "copying";
1.1       kristaps  973:                break;
                    974:        case (TEXICMD_DETAILMENU):
1.3       kristaps  975:                blk = "detailmenu";
1.1       kristaps  976:                break;
                    977:        case (TEXICMD_DIRENTRY):
1.3       kristaps  978:                blk = "direntry";
                    979:                break;
                    980:        case (TEXICMD_IFCLEAR):
                    981:                blk = "ifclear";
1.1       kristaps  982:                break;
                    983:        case (TEXICMD_IFHTML):
1.3       kristaps  984:                blk = "ifhtml";
                    985:                break;
                    986:        case (TEXICMD_IFINFO):
                    987:                blk = "ifinfo";
                    988:                break;
                    989:        case (TEXICMD_IFSET):
                    990:                blk = "ifset";
1.1       kristaps  991:                break;
                    992:        case (TEXICMD_IFTEX):
1.3       kristaps  993:                blk = "iftex";
1.1       kristaps  994:                break;
                    995:        case (TEXICMD_MENU):
1.3       kristaps  996:                blk = "menu";
1.1       kristaps  997:                break;
                    998:        case (TEXICMD_TEX):
1.3       kristaps  999:                blk = "tex";
1.1       kristaps 1000:                break;
                   1001:        case (TEXICMD_TITLEPAGE):
1.3       kristaps 1002:                blk = "titlepage";
1.1       kristaps 1003:                break;
                   1004:        default:
                   1005:                abort();
                   1006:        }
1.3       kristaps 1007:        p->ign++;
                   1008:        parseto(p, buf, sz, pos, blk);
                   1009:        p->ign--;
1.1       kristaps 1010: }
                   1011:
                   1012: static void
1.3       kristaps 1013: doblock(struct texi *p, enum texicmd cmd,
1.1       kristaps 1014:        const char *buf, size_t sz, size_t *pos)
                   1015: {
1.3       kristaps 1016:        const char      *blk;
                   1017:
                   1018:        switch (cmd) {
                   1019:        case (TEXICMD_GROUP):
                   1020:                blk = "group";
                   1021:                break;
                   1022:        case (TEXICMD_IFNOTTEX):
                   1023:                blk = "ifnottex";
                   1024:                break;
                   1025:        default:
                   1026:                abort();
                   1027:        }
1.1       kristaps 1028:
1.3       kristaps 1029:        parseto(p, buf, sz, pos, blk);
1.1       kristaps 1030: }
                   1031:
                   1032: static void
                   1033: doinline(struct texi *p, const char *buf,
                   1034:        size_t sz, size_t *pos, const char *macro)
                   1035: {
1.3       kristaps 1036:        int      open = 0;
                   1037:
                   1038:        if (0 == p->outmacro) {
                   1039:                open = 1;
                   1040:                teximacroopen(p, ".");
                   1041:        } else
                   1042:                texiputchar(p, ' ');
1.1       kristaps 1043:
                   1044:        texiputchars(p, macro);
                   1045:        texiputchar(p, ' ');
                   1046:        p->seenws = 0;
                   1047:        parsebracket(p, buf, sz, pos);
                   1048:        if (*pos < sz - 1 &&
                   1049:                 ismpunct(buf[*pos]) &&
                   1050:                 isspace(buf[*pos + 1])) {
                   1051:                texiputchar(p, ' ');
                   1052:                texiputchar(p, buf[*pos]);
                   1053:                advance(p, buf, pos);
                   1054:        }
1.3       kristaps 1055:        if (open)
                   1056:                teximacroclose(p);
1.1       kristaps 1057: }
                   1058:
                   1059: static void
1.2       kristaps 1060: doinclude(struct texi *p, enum texicmd cmd,
                   1061:        const char *buf, size_t sz, size_t *pos)
                   1062: {
                   1063:        char     fname[PATH_MAX], path[PATH_MAX];
                   1064:        size_t   i;
                   1065:        int      rc;
                   1066:
                   1067:        while (*pos < sz && ' ' == buf[*pos])
                   1068:                advance(p, buf, pos);
                   1069:
                   1070:        /* Read in the filename. */
                   1071:        for (i = 0; *pos < sz && '\n' != buf[*pos]; i++) {
                   1072:                if (i == sizeof(fname) - 1)
                   1073:                        break;
                   1074:                fname[i] = buf[*pos];
                   1075:                advance(p, buf, pos);
                   1076:        }
                   1077:
                   1078:        if (i == 0)
                   1079:                texierr(p, "path too short");
                   1080:        else if ('\n' != buf[*pos])
                   1081:                texierr(p, "path too long");
                   1082:        else if ('/' == fname[0])
                   1083:                texierr(p, "no absolute paths");
                   1084:        fname[i] = '\0';
                   1085:
                   1086:        if (strstr(fname, "../") || strstr(fname, "/.."))
                   1087:                texierr(p, "insecure path");
                   1088:
                   1089:        /* Append filename to original name's directory. */
                   1090:        rc = snprintf(path, sizeof(path), "%s/%s", p->dir, fname);
                   1091:        if (rc < 0)
                   1092:                texierr(p, "couldn't format filename");
                   1093:        else if ((size_t)rc >= sizeof(path))
                   1094:                texierr(p, "path too long");
                   1095:
                   1096:        /* Pump through to parser. */
                   1097:        parsefile(p, path);
                   1098: }
                   1099:
                   1100: static void
1.1       kristaps 1101: doitalic(struct texi *p, enum texicmd cmd,
                   1102:        const char *buf, size_t sz, size_t *pos)
                   1103: {
                   1104:
                   1105:        texiputchars(p, "\\fI");
                   1106:        parsebracket(p, buf, sz, pos);
                   1107:        texiputchars(p, "\\fP");
                   1108: }
                   1109:
                   1110: static void
1.3       kristaps 1111: doenv(struct texi *p, enum texicmd cmd,
                   1112:        const char *buf, size_t sz, size_t *pos)
                   1113: {
                   1114:
                   1115:        if (p->literal)
                   1116:                parsebracket(p, buf, sz, pos);
                   1117:        else
                   1118:                doinline(p, buf, sz, pos, "Ev");
                   1119: }
                   1120:
                   1121: static void
1.1       kristaps 1122: doliteral(struct texi *p, enum texicmd cmd,
                   1123:        const char *buf, size_t sz, size_t *pos)
                   1124: {
                   1125:
1.3       kristaps 1126:        if (p->literal)
1.1       kristaps 1127:                parsebracket(p, buf, sz, pos);
                   1128:        else
                   1129:                doinline(p, buf, sz, pos, "Li");
                   1130: }
                   1131:
                   1132: static void
                   1133: doemph(struct texi *p, enum texicmd cmd,
                   1134:        const char *buf, size_t sz, size_t *pos)
                   1135: {
                   1136:
1.3       kristaps 1137:        if (p->literal)
1.1       kristaps 1138:                doitalic(p, cmd, buf, sz, pos);
                   1139:        else
                   1140:                doinline(p, buf, sz, pos, "Em");
                   1141: }
                   1142:
                   1143: static void
                   1144: docommand(struct texi *p, enum texicmd cmd,
                   1145:        const char *buf, size_t sz, size_t *pos)
                   1146: {
                   1147:
                   1148:        doinline(p, buf, sz, pos, "Xr");
                   1149: }
                   1150:
                   1151: static void
                   1152: dobracket(struct texi *p, enum texicmd cmd,
                   1153:        const char *buf, size_t sz, size_t *pos)
                   1154: {
                   1155:
                   1156:        parsebracket(p, buf, sz, pos);
                   1157: }
                   1158:
                   1159: static void
                   1160: dofile(struct texi *p, enum texicmd cmd,
                   1161:        const char *buf, size_t sz, size_t *pos)
                   1162: {
                   1163:
1.3       kristaps 1164:        if (p->literal)
1.1       kristaps 1165:                parsebracket(p, buf, sz, pos);
                   1166:        else
                   1167:                doinline(p, buf, sz, pos, "Pa");
                   1168: }
                   1169:
                   1170: static void
1.3       kristaps 1171: dodisplay(struct texi *p, enum texicmd cmd,
                   1172:        const char *buf, size_t sz, size_t *pos)
                   1173: {
                   1174:
                   1175:        teximacro(p, ".Bd -display");
                   1176:        advanceeoln(p, buf, sz, pos, 1);
                   1177:        parseto(p, buf, sz, pos, "display");
                   1178:        teximacro(p, ".Ed");
                   1179: }
                   1180:
                   1181: static void
1.1       kristaps 1182: doexample(struct texi *p, enum texicmd cmd,
                   1183:        const char *buf, size_t sz, size_t *pos)
                   1184: {
1.3       kristaps 1185:        const char      *blk;
                   1186:
                   1187:        blk = TEXICMD_EXAMPLE == cmd ?  "example" : "smallexample";
1.1       kristaps 1188:
                   1189:        teximacro(p, ".Bd -literal");
1.3       kristaps 1190:        advanceeoln(p, buf, sz, pos, 1);
                   1191:        p->literal++;
                   1192:        parseto(p, buf, sz, pos, blk);
                   1193:        p->literal--;
1.1       kristaps 1194:        teximacro(p, ".Ed");
                   1195: }
                   1196:
                   1197: static void
                   1198: dobye(struct texi *p, enum texicmd cmd,
                   1199:        const char *buf, size_t sz, size_t *pos)
                   1200: {
                   1201:
                   1202:        texiexit(p);
                   1203:        exit(EXIT_SUCCESS);
                   1204: }
                   1205:
                   1206: static void
                   1207: dosymbol(struct texi *p, enum texicmd cmd,
                   1208:        const char *buf, size_t sz, size_t *pos)
                   1209: {
                   1210:
1.3       kristaps 1211:        if (p->seenws && p->outcol && 0 == p->literal) {
                   1212:                texiputchar(p, ' ');
                   1213:                p->seenws = 0;
                   1214:        }
                   1215:
1.1       kristaps 1216:        switch (cmd) {
1.3       kristaps 1217:        case (TEXICMD_ASTERISK):
                   1218:        case (TEXICMD_NEWLINE):
                   1219:        case (TEXICMD_SPACE):
                   1220:        case (TEXICMD_TAB):
                   1221:                texiputchar(p, ' ');
                   1222:                break;
1.1       kristaps 1223:        case (TEXICMD_AT):
1.3       kristaps 1224:                texiputchar(p, '@');
                   1225:                break;
                   1226:        case (TEXICMD_BANG):
                   1227:                texiputchar(p, '!');
1.1       kristaps 1228:                break;
                   1229:        case (TEXICMD_COPYRIGHT):
                   1230:                texiputchars(p, "\\(co");
                   1231:                break;
1.2       kristaps 1232:        case (TEXICMD_DOTS):
                   1233:                texiputchars(p, "...");
                   1234:                break;
1.1       kristaps 1235:        case (TEXICMD_LATEX):
                   1236:                texiputchars(p, "LaTeX");
                   1237:                break;
1.3       kristaps 1238:        case (TEXICMD_QUESTIONMARK):
                   1239:                texiputchar(p, '?');
                   1240:                break;
                   1241:        case (TEXICMD_SQUIGGLE_LEFT):
                   1242:                texiputchars(p, "{");
                   1243:                break;
                   1244:        case (TEXICMD_SQUIGGLE_RIGHT):
                   1245:                texiputchars(p, "}");
                   1246:                break;
1.1       kristaps 1247:        case (TEXICMD_TEXSYM):
                   1248:                texiputchars(p, "TeX");
                   1249:                break;
1.3       kristaps 1250:        case (TEXICMD_COLON):
                   1251:        case (TEXICMD_HYPHEN):
                   1252:                break;
1.1       kristaps 1253:        default:
                   1254:                abort();
                   1255:        }
                   1256:
                   1257:        doignbracket(p, cmd, buf, sz, pos);
                   1258: }
                   1259:
                   1260: static void
                   1261: doquotation(struct texi *p, enum texicmd cmd,
                   1262:        const char *buf, size_t sz, size_t *pos)
                   1263: {
                   1264:
                   1265:        teximacro(p, ".Qo");
                   1266:        parseto(p, buf, sz, pos, "quotation");
                   1267:        teximacro(p, ".Qc");
                   1268: }
                   1269:
1.3       kristaps 1270: /* FIXME */
                   1271: static void
                   1272: domath(struct texi *p, enum texicmd cmd,
                   1273:        const char *buf, size_t sz, size_t *pos)
                   1274: {
                   1275:        size_t   nest;
                   1276:
                   1277:        /*
                   1278:         * Math handling is different from everything else.
                   1279:         * We don't allow any subcomponents, and we ignore the rules in
                   1280:         * terms of @-commands.
                   1281:         * This departs from GNU's rules, but whatever.
                   1282:         */
                   1283:        while (*pos < sz && isws(buf[*pos]))
                   1284:                advance(p, buf, pos);
                   1285:        if (*pos == sz || '{' != buf[*pos])
                   1286:                return;
                   1287:        advance(p, buf, pos);
                   1288:        if (p->seenws && p->outcol && 0 == p->literal)
                   1289:                texiputchar(p, ' ');
                   1290:        p->seenws = 0;
                   1291:        for (nest = 1; *pos < sz && nest > 0; ) {
                   1292:                if ('{' == buf[*pos])
                   1293:                        nest++;
                   1294:                else if ('}' == buf[*pos])
                   1295:                        if (0 == --nest)
                   1296:                                continue;
                   1297:                texiputchar(p, buf[*pos]);
                   1298:                advance(p, buf, pos);
                   1299:        }
                   1300:        if (*pos == sz)
                   1301:                return;
                   1302:        assert('}' == buf[*pos]);
                   1303:        advance(p, buf, pos);
                   1304: }
                   1305:
                   1306: /* FIXME */
1.1       kristaps 1307: static void
                   1308: doarg1(struct texi *p, enum texicmd cmd,
                   1309:        const char *buf, size_t sz, size_t *pos)
                   1310: {
1.3       kristaps 1311:        int      open = 0;
1.1       kristaps 1312:
                   1313:        if (*pos == sz || '{' != buf[*pos])
                   1314:                return;
                   1315:        advance(p, buf, pos);
                   1316:        switch (cmd) {
                   1317:        case (TEXICMD_EMAIL):
1.3       kristaps 1318:                if ( ! p->outmacro) {
                   1319:                        open = 1;
                   1320:                        teximacroopen(p, ".");
                   1321:                }
1.1       kristaps 1322:                texiputchars(p, "Lk ");
                   1323:                break;
1.3       kristaps 1324:        case (TEXICMD_UREF):
1.1       kristaps 1325:        case (TEXICMD_URL):
1.3       kristaps 1326:                if ( ! p->outmacro) {
                   1327:                        open = 1;
                   1328:                        teximacroopen(p, ".");
                   1329:                }
1.1       kristaps 1330:                texiputchars(p, "Mt ");
                   1331:                break;
                   1332:        default:
1.3       kristaps 1333:                break;
1.1       kristaps 1334:        }
1.3       kristaps 1335:        /* FIXME: this is ugly */
1.1       kristaps 1336:        while (*pos < sz && '}' != buf[*pos] && ',' != buf[*pos]) {
                   1337:                texiputchar(p, buf[*pos]);
                   1338:                advance(p, buf, pos);
                   1339:        }
                   1340:        while (*pos < sz && '}' != buf[*pos])
                   1341:                advance(p, buf, pos);
                   1342:        if (*pos < sz)
                   1343:                advance(p, buf, pos);
                   1344:        if (*pos < sz - 1 &&
                   1345:                 ismpunct(buf[*pos]) &&
                   1346:                 isspace(buf[*pos + 1])) {
                   1347:                texiputchar(p, ' ');
                   1348:                texiputchar(p, buf[*pos]);
                   1349:                advance(p, buf, pos);
                   1350:        }
1.3       kristaps 1351:        if (open)
                   1352:                teximacroclose(p);
1.1       kristaps 1353: }
                   1354:
                   1355: static void
                   1356: dosubsection(struct texi *p, enum texicmd cmd,
                   1357:                const char *buf, size_t sz, size_t *pos)
                   1358: {
                   1359:
1.3       kristaps 1360:        teximacro(p, ".Pp");
                   1361:        parseeoln(p, buf, sz, pos);
                   1362:        teximacro(p, ".Pp");
1.1       kristaps 1363: }
                   1364:
                   1365: static void
                   1366: dosection(struct texi *p, enum texicmd cmd,
                   1367:                const char *buf, size_t sz, size_t *pos)
                   1368: {
                   1369:
1.3       kristaps 1370:        if (p->outmacro)
                   1371:                texierr(p, "subsection in open line scope!?");
                   1372:        else if (p->literal)
                   1373:                texierr(p, "subsection in a literal scope!?");
                   1374:
                   1375:        teximacroopen(p, ".Ss ");
                   1376:        parseeoln(p, buf, sz, pos);
                   1377:        teximacroclose(p);
                   1378: }
                   1379:
                   1380: static void
                   1381: dosp(struct texi *p, enum texicmd cmd,
                   1382:        const char *buf, size_t sz, size_t *pos)
                   1383: {
                   1384:
                   1385:        teximacro(p, ".Pp");
                   1386:        advanceeoln(p, buf, sz, pos, 1);
1.1       kristaps 1387: }
                   1388:
                   1389: static void
1.3       kristaps 1390: dochapter(struct texi *p, enum texicmd cmd,
1.1       kristaps 1391:        const char *buf, size_t sz, size_t *pos)
                   1392: {
                   1393:
1.3       kristaps 1394:        if (p->outmacro)
                   1395:                texierr(p, "section in open line scope!?");
                   1396:        else if (p->literal)
                   1397:                texierr(p, "section in a literal scope!?");
                   1398:
                   1399:        teximacroopen(p, ".Sh ");
                   1400:        parseeoln(p, buf, sz, pos);
                   1401:        teximacroclose(p);
1.1       kristaps 1402: }
                   1403:
                   1404: static void
                   1405: dotop(struct texi *p, enum texicmd cmd,
                   1406:        const char *buf, size_t sz, size_t *pos)
                   1407: {
                   1408:
1.3       kristaps 1409:        p->ign--;
                   1410:        advanceeoln(p, buf, sz, pos, 1);
                   1411:        teximacro(p, ".Dd $Mdocdate: February 17 2015 $");
1.1       kristaps 1412:        teximacro(p, ".Dt SOMETHING 7");
                   1413:        teximacro(p, ".Os");
                   1414:        teximacro(p, ".Sh NAME");
                   1415:        teximacro(p, ".Nm Something");
                   1416:        teximacro(p, ".Nd Something");
                   1417: }
                   1418:
                   1419: static void
                   1420: doitem(struct texi *p, enum texicmd cmd,
                   1421:        const char *buf, size_t sz, size_t *pos)
                   1422: {
                   1423:
1.3       kristaps 1424:        if (p->outmacro)
                   1425:                texierr(p, "item in open line scope!?");
                   1426:        else if (p->literal)
                   1427:                texierr(p, "item in a literal scope!?");
                   1428:
                   1429:        switch (p->list) {
                   1430:        case (TEXILIST_ITEM):
                   1431:                teximacroopen(p, ".It");
                   1432:                break;
                   1433:        case (TEXILIST_NOITEM):
                   1434:                teximacro(p, ".It");
                   1435:                break;
                   1436:        default:
                   1437:                teximacro(p, ".Pp");
                   1438:                break;
                   1439:        }
                   1440:
                   1441:        parseeoln(p, buf, sz, pos);
1.1       kristaps 1442:
1.3       kristaps 1443:        if (TEXILIST_ITEM == p->list)
                   1444:                teximacroclose(p);
                   1445:        else
1.1       kristaps 1446:                texiputchar(p, '\n');
                   1447: }
                   1448:
                   1449: static void
                   1450: dotable(struct texi *p, enum texicmd cmd,
                   1451:        const char *buf, size_t sz, size_t *pos)
                   1452: {
1.3       kristaps 1453:        enum texilist   sv = p->list;
                   1454:
                   1455:        p->list = TEXILIST_ITEM;
1.1       kristaps 1456:        teximacro(p, ".Bl -tag -width Ds");
                   1457:        parseto(p, buf, sz, pos, "table");
                   1458:        teximacro(p, ".El");
1.3       kristaps 1459:        p->list = sv;
1.1       kristaps 1460: }
                   1461:
                   1462: static void
1.2       kristaps 1463: doenumerate(struct texi *p, enum texicmd cmd,
                   1464:        const char *buf, size_t sz, size_t *pos)
                   1465: {
1.3       kristaps 1466:        enum texilist    sv = p->list;
1.2       kristaps 1467:
1.3       kristaps 1468:        p->list = TEXILIST_NOITEM;
1.2       kristaps 1469:        teximacro(p, ".Bl -enum");
                   1470:        parseto(p, buf, sz, pos, "enumerate");
                   1471:        teximacro(p, ".El");
1.3       kristaps 1472:        p->list = sv;
1.2       kristaps 1473: }
                   1474:
                   1475: static void
1.1       kristaps 1476: doitemize(struct texi *p, enum texicmd cmd,
                   1477:        const char *buf, size_t sz, size_t *pos)
                   1478: {
1.3       kristaps 1479:        enum texilist   sv = p->list;
1.1       kristaps 1480:
1.3       kristaps 1481:        p->list = TEXILIST_ITEM;
1.1       kristaps 1482:        teximacro(p, ".Bl -bullet");
                   1483:        parseto(p, buf, sz, pos, "itemize");
                   1484:        teximacro(p, ".El");
1.3       kristaps 1485:        p->list = sv;
1.1       kristaps 1486: }
                   1487:
                   1488: static void
                   1489: doignbracket(struct texi *p, enum texicmd cmd,
                   1490:        const char *buf, size_t sz, size_t *pos)
                   1491: {
                   1492:
1.3       kristaps 1493:        p->ign++;
1.1       kristaps 1494:        parsebracket(p, buf, sz, pos);
1.3       kristaps 1495:        p->ign--;
1.1       kristaps 1496: }
                   1497:
                   1498: static void
                   1499: doignline(struct texi *p, enum texicmd cmd,
                   1500:        const char *buf, size_t sz, size_t *pos)
                   1501: {
                   1502:
1.3       kristaps 1503:        advanceeoln(p, buf, sz, pos, 1);
1.1       kristaps 1504: }
                   1505:
                   1506: int
                   1507: main(int argc, char *argv[])
                   1508: {
                   1509:        struct texi      texi;
1.2       kristaps 1510:        int              c;
                   1511:        char            *path, *dir;
1.1       kristaps 1512:        const char      *progname;
                   1513:
                   1514:        progname = strrchr(argv[0], '/');
                   1515:        if (progname == NULL)
                   1516:                progname = argv[0];
                   1517:        else
                   1518:                ++progname;
                   1519:
                   1520:        while (-1 != (c = getopt(argc, argv, "")))
                   1521:                switch (c) {
                   1522:                default:
                   1523:                        goto usage;
                   1524:                }
                   1525:
                   1526:        argv += optind;
                   1527:        if (0 == (argc -= optind))
                   1528:                goto usage;
                   1529:
1.2       kristaps 1530:        if (NULL == (path = strdup(argv[0]))) {
                   1531:                perror(NULL);
                   1532:                exit(EXIT_FAILURE);
                   1533:        } else if (NULL == (dir = dirname(path))) {
                   1534:                perror(argv[0]);
                   1535:                free(path);
                   1536:                exit(EXIT_FAILURE);
                   1537:        }
                   1538:        free(path);
                   1539:
1.1       kristaps 1540:        memset(&texi, 0, sizeof(struct texi));
1.3       kristaps 1541:        texi.ign = 1;
1.2       kristaps 1542:        texi.dir = strdup(dir);
                   1543:        parsefile(&texi, argv[0]);
                   1544:        texiexit(&texi);
                   1545:        return(EXIT_FAILURE);
1.1       kristaps 1546: usage:
                   1547:        fprintf(stderr, "usage: %s file\n", progname);
                   1548:        return(EXIT_FAILURE);
                   1549: }

CVSweb