Annotation of texi2mdoc/main.c, Revision 1.23
1.23 ! kristaps 1: /* $Id: main.c,v 1.22 2015/02/19 16:44:26 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>
1.10 kristaps 30: #include <time.h>
1.6 kristaps 31: #include <unistd.h>
1.1 kristaps 32:
33: /*
34: * This defines each one of the Texinfo commands that we understand.
35: * Obviously this only refers to native commands; overriden names are a
36: * different story.
37: */
38: enum texicmd {
1.2 kristaps 39: TEXICMD_ACRONYM,
1.16 kristaps 40: TEXICMD_ACUTE,
1.1 kristaps 41: TEXICMD_A4PAPER,
42: TEXICMD_ANCHOR,
1.2 kristaps 43: TEXICMD_APPENDIX,
44: TEXICMD_APPENDIXSEC,
1.20 kristaps 45: TEXICMD_APPENDIXSUBSEC,
1.23 ! kristaps 46: TEXICMD_APPENDIXSUBSUBSEC,
1.3 kristaps 47: TEXICMD_ASTERISK,
1.1 kristaps 48: TEXICMD_AT,
1.3 kristaps 49: TEXICMD_AUTHOR,
1.12 kristaps 50: TEXICMD_B,
1.3 kristaps 51: TEXICMD_BANG,
1.7 kristaps 52: TEXICMD_BULLET,
1.1 kristaps 53: TEXICMD_BYE,
1.5 kristaps 54: TEXICMD_CENTER,
1.1 kristaps 55: TEXICMD_CHAPTER,
56: TEXICMD_CINDEX,
1.16 kristaps 57: TEXICMD_CIRCUMFLEX,
1.3 kristaps 58: TEXICMD_CITE,
1.1 kristaps 59: TEXICMD_CODE,
1.3 kristaps 60: TEXICMD_COLON,
1.18 kristaps 61: TEXICMD_COLUMNFRACTIONS,
1.1 kristaps 62: TEXICMD_COMMAND,
63: TEXICMD_COMMENT,
1.2 kristaps 64: TEXICMD_COMMENT_LONG,
1.1 kristaps 65: TEXICMD_CONTENTS,
66: TEXICMD_COPYING,
67: TEXICMD_COPYRIGHT,
1.15 kristaps 68: TEXICMD_DEFFN,
69: TEXICMD_DEFFNX,
70: TEXICMD_DEFMAC,
71: TEXICMD_DEFMACX,
72: TEXICMD_DEFTP,
73: TEXICMD_DEFTPX,
1.3 kristaps 74: TEXICMD_DEFTYPEFN,
75: TEXICMD_DEFTYPEFNX,
76: TEXICMD_DEFTYPEFUN,
77: TEXICMD_DEFTYPEFUNX,
78: TEXICMD_DEFTYPEVAR,
1.15 kristaps 79: TEXICMD_DEFTYPEVARX,
1.3 kristaps 80: TEXICMD_DEFTYPEVR,
1.15 kristaps 81: TEXICMD_DEFTYPEVRX,
82: TEXICMD_DEFUN,
83: TEXICMD_DEFUNX,
84: TEXICMD_DEFVAR,
85: TEXICMD_DEFVARX,
86: TEXICMD_DEFVR,
87: TEXICMD_DEFVRX,
1.1 kristaps 88: TEXICMD_DETAILMENU,
1.3 kristaps 89: TEXICMD_DFN,
1.1 kristaps 90: TEXICMD_DIRCATEGORY,
91: TEXICMD_DIRENTRY,
1.3 kristaps 92: TEXICMD_DISPLAY,
1.2 kristaps 93: TEXICMD_DOTS,
1.1 kristaps 94: TEXICMD_EMAIL,
95: TEXICMD_EMPH,
96: TEXICMD_END,
1.2 kristaps 97: TEXICMD_ENUMERATE,
1.3 kristaps 98: TEXICMD_ENV,
1.15 kristaps 99: TEXICMD_ERROR,
1.1 kristaps 100: TEXICMD_EXAMPLE,
1.17 kristaps 101: TEXICMD_EXPANSION,
1.1 kristaps 102: TEXICMD_FILE,
1.17 kristaps 103: TEXICMD_FINALOUT,
1.20 kristaps 104: TEXICMD_FINDEX,
105: TEXICMD_FTABLE,
106: TEXICMD_FORMAT,
1.16 kristaps 107: TEXICMD_GRAVE,
1.3 kristaps 108: TEXICMD_GROUP,
1.2 kristaps 109: TEXICMD_HEADING,
1.3 kristaps 110: TEXICMD_HEADINGS,
1.18 kristaps 111: TEXICMD_HEADITEM,
1.3 kristaps 112: TEXICMD_HYPHEN,
1.1 kristaps 113: TEXICMD_I,
1.3 kristaps 114: TEXICMD_IFCLEAR,
1.14 kristaps 115: TEXICMD_IFDOCBOOK,
1.1 kristaps 116: TEXICMD_IFHTML,
1.3 kristaps 117: TEXICMD_IFINFO,
1.14 kristaps 118: TEXICMD_IFNOTDOCBOOK,
119: TEXICMD_IFNOTHTML,
120: TEXICMD_IFNOTINFO,
121: TEXICMD_IFNOTPLAINTEXT,
1.1 kristaps 122: TEXICMD_IFNOTTEX,
1.14 kristaps 123: TEXICMD_IFNOTXML,
124: TEXICMD_IFPLAINTEXT,
1.1 kristaps 125: TEXICMD_IFTEX,
1.3 kristaps 126: TEXICMD_IFSET,
1.14 kristaps 127: TEXICMD_IFXML,
1.17 kristaps 128: TEXICMD_IGNORE,
1.1 kristaps 129: TEXICMD_IMAGE,
1.2 kristaps 130: TEXICMD_INCLUDE,
1.13 kristaps 131: TEXICMD_INDENTBLOCK,
1.5 kristaps 132: TEXICMD_INSERTCOPYING,
1.1 kristaps 133: TEXICMD_ITEM,
134: TEXICMD_ITEMIZE,
1.20 kristaps 135: TEXICMD_ITEMX,
136: TEXICMD_KBD,
1.18 kristaps 137: TEXICMD_KEY,
1.20 kristaps 138: TEXICMD_KINDEX,
1.1 kristaps 139: TEXICMD_LATEX,
1.23 ! kristaps 140: TEXICMD_LOWERSECTIONS,
1.3 kristaps 141: TEXICMD_MATH,
1.1 kristaps 142: TEXICMD_MENU,
1.18 kristaps 143: TEXICMD_MULTITABLE,
1.15 kristaps 144: TEXICMD_NEED,
1.3 kristaps 145: TEXICMD_NEWLINE,
1.1 kristaps 146: TEXICMD_NODE,
1.3 kristaps 147: TEXICMD_NOINDENT,
1.16 kristaps 148: TEXICMD_OPTION,
1.8 kristaps 149: TEXICMD_PXREF,
1.3 kristaps 150: TEXICMD_QUESTIONMARK,
1.1 kristaps 151: TEXICMD_QUOTATION,
1.3 kristaps 152: TEXICMD_PAGE,
1.1 kristaps 153: TEXICMD_PARINDENT,
1.2 kristaps 154: TEXICMD_PRINTINDEX,
1.12 kristaps 155: TEXICMD_R,
1.23 ! kristaps 156: TEXICMD_RAISESECTIONS,
1.1 kristaps 157: TEXICMD_REF,
1.15 kristaps 158: TEXICMD_RESULT,
1.1 kristaps 159: TEXICMD_SAMP,
1.12 kristaps 160: TEXICMD_SANSSERIF,
1.7 kristaps 161: TEXICMD_SC,
1.1 kristaps 162: TEXICMD_SECTION,
1.3 kristaps 163: TEXICMD_SET,
1.1 kristaps 164: TEXICMD_SETCHAPNEWPAGE,
165: TEXICMD_SETFILENAME,
166: TEXICMD_SETTITLE,
1.12 kristaps 167: TEXICMD_SLANTED,
1.3 kristaps 168: TEXICMD_SP,
169: TEXICMD_SPACE,
1.17 kristaps 170: TEXICMD_SMALLBOOK,
1.12 kristaps 171: TEXICMD_SMALLDISPLAY,
1.3 kristaps 172: TEXICMD_SMALLEXAMPLE,
1.20 kristaps 173: TEXICMD_SMALLFORMAT,
1.13 kristaps 174: TEXICMD_SMALLINDENTBLOCK,
1.3 kristaps 175: TEXICMD_SQUIGGLE_LEFT,
176: TEXICMD_SQUIGGLE_RIGHT,
1.8 kristaps 177: TEXICMD_STRONG,
1.20 kristaps 178: TEXICMD_SUBHEADING,
1.1 kristaps 179: TEXICMD_SUBSECTION,
1.23 ! kristaps 180: TEXICMD_SUBSUBSECTION,
1.3 kristaps 181: TEXICMD_SUBTITLE,
1.20 kristaps 182: TEXICMD_SYNCODEINDEX,
1.12 kristaps 183: TEXICMD_T,
1.3 kristaps 184: TEXICMD_TAB,
1.18 kristaps 185: TEXICMD_TABSYM,
1.1 kristaps 186: TEXICMD_TABLE,
187: TEXICMD_TEX,
188: TEXICMD_TEXSYM,
1.16 kristaps 189: TEXICMD_TILDE,
1.3 kristaps 190: TEXICMD_TITLE,
1.1 kristaps 191: TEXICMD_TITLEFONT,
192: TEXICMD_TITLEPAGE,
193: TEXICMD_TOP,
1.16 kristaps 194: TEXICMD_UMLAUT,
1.1 kristaps 195: TEXICMD_UNNUMBERED,
1.2 kristaps 196: TEXICMD_UNNUMBEREDSEC,
1.20 kristaps 197: TEXICMD_UNNUMBEREDSUBSEC,
1.23 ! kristaps 198: TEXICMD_UNNUMBEREDSUBSUBSEC,
1.3 kristaps 199: TEXICMD_UREF,
1.1 kristaps 200: TEXICMD_URL,
201: TEXICMD_VAR,
1.16 kristaps 202: TEXICMD_VERBATIMINCLUDE,
1.18 kristaps 203: TEXICMD_VINDEX,
1.9 kristaps 204: TEXICMD_VSKIP,
1.20 kristaps 205: TEXICMD_VTABLE,
1.3 kristaps 206: TEXICMD_W,
1.8 kristaps 207: TEXICMD_XREF,
1.1 kristaps 208: TEXICMD__MAX
209: };
210:
1.23 ! kristaps 211: #define SECTSZ 4
! 212: static const char *const sects[SECTSZ] = {
! 213: "Sh",
! 214: "Ss",
! 215: "Em",
! 216: "No",
! 217: };
! 218:
1.1 kristaps 219: /*
220: * The file currently being parsed.
221: * This keeps track of our location within that file.
222: */
223: struct texifile {
224: const char *name; /* name of the file */
225: size_t line; /* current line (from zero) */
226: size_t col; /* current column in line (from zero) */
227: char *map; /* mmap'd file */
228: size_t mapsz; /* size of mmap */
229: };
230:
231: struct texi;
232:
1.2 kristaps 233: /*
234: * Callback for functions implementing texi commands.
235: */
1.1 kristaps 236: typedef void (*texicmdfp)(struct texi *,
237: enum texicmd, const char *, size_t, size_t *);
238:
239: /*
240: * Describes Texinfo commands, whether native or overriden.
241: */
242: struct texitok {
243: texicmdfp fp; /* callback (or NULL if none) */
244: const char *tok; /* name of the token */
245: size_t len; /* strlen(tok) */
246: };
247:
1.3 kristaps 248: enum texilist {
249: TEXILIST_NONE = 0,
250: TEXILIST_ITEM,
251: TEXILIST_NOITEM,
1.18 kristaps 252: TEXILIST_TABLE
1.3 kristaps 253: };
254:
1.1 kristaps 255: /*
256: * The main parse structure.
257: * This keeps any necessary information handy.
258: */
259: struct texi {
1.5 kristaps 260: struct texifile files[64]; /* stack of open files */
261: size_t filepos; /* number of open files */
262: size_t outcol; /* column in output line */
263: char **dirs; /* texi directories */
264: size_t dirsz; /* number of texi directories */
1.11 kristaps 265: char *title; /* title of document */
266: char *subtitle; /* subtitle of document */
1.23 ! kristaps 267: int secoffs; /* see sectioner() */
1.11 kristaps 268: /*
269: * The following control what we output to the screen.
270: * The complexity is required to accomodate for mdoc(7).
271: */
1.8 kristaps 272: enum texilist list; /* current list (set recursively) */
273: int outmacro; /* if >0, output is in line macro */
274: int seenws; /* ws has been seen (and ignored) */
1.11 kristaps 275: int seenvs; /* newline has been Pp'd */
1.8 kristaps 276: int ign; /* if >0, don't print anything */
277: int literal; /* if >0, literal context */
1.1 kristaps 278: };
279:
1.8 kristaps 280: /* Texi disregards spaces and tabs. */
1.2 kristaps 281: #define isws(_x) \
282: (' ' == (_x) || '\t' == (_x))
1.9 kristaps 283: #define ismspace(_x) \
1.10 kristaps 284: (isws((_x)) || '\n' == (_x))
1.1 kristaps 285:
1.16 kristaps 286: static void doaccent(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.3 kristaps 287: static void doblock(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.1 kristaps 288: static void dobracket(struct texi *, enum texicmd, const char *, size_t, size_t *);
289: static void dobye(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.15 kristaps 290: static void dodefn(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.3 kristaps 291: static void dodisplay(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.2 kristaps 292: static void doenumerate(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.1 kristaps 293: static void doexample(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.8 kristaps 294: static void doignargn(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.1 kristaps 295: static void doignblock(struct texi *, enum texicmd, const char *, size_t, size_t *);
296: static void doignbracket(struct texi *, enum texicmd, const char *, size_t, size_t *);
297: static void doignline(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.12 kristaps 298: static void doinline(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.2 kristaps 299: static void doinclude(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.1 kristaps 300: static void doitem(struct texi *, enum texicmd, const char *, size_t, size_t *);
301: static void doitemize(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.8 kristaps 302: static void dolink(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.3 kristaps 303: static void domath(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.18 kristaps 304: static void domultitable(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.1 kristaps 305: static void doquotation(struct texi *, enum texicmd, const char *, size_t, size_t *);
306: static void dotable(struct texi *, enum texicmd, const char *, size_t, size_t *);
307: static void dotop(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.23 ! kristaps 308: static void dosecoffs(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.1 kristaps 309: static void dosection(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.3 kristaps 310: static void dosp(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.1 kristaps 311: static void dosubsection(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.23 ! kristaps 312: static void dosubsubsection(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.1 kristaps 313: static void dosymbol(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.18 kristaps 314: static void dotab(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.10 kristaps 315: static void dotitle(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.16 kristaps 316: static void doverbinclude(struct texi *, enum texicmd, const char *, size_t, size_t *);
1.1 kristaps 317:
318: static const struct texitok texitoks[TEXICMD__MAX] = {
1.20 kristaps 319: /* TEXICMD__BEGIN */
1.8 kristaps 320: { doignargn, "acronym", 7 }, /* TEXICMD_ACRONYM */
1.16 kristaps 321: { doaccent, "'", 1 }, /* TEXICMD_ACUTE */
1.1 kristaps 322: { doignline, "afourpaper", 10 }, /* TEXICMD_A4PAPER */
323: { doignbracket, "anchor", 6 }, /* TEXICMD_ANCHOR */
1.12 kristaps 324: { dosection, "appendix", 8 }, /* TEXICMD_APPENDIX */
325: { dosection, "appendixsec", 11 }, /* TEXICMD_APPENDIXSEC */
1.20 kristaps 326: { dosubsection, "appendixsubsec", 14 }, /* TEXICMD_APPENDIXSUBSEC */
1.23 ! kristaps 327: { dosubsubsection, "appendixsubsubsec", 17 }, /* TEXICMD_APPENDIXSUBSUBSEC */
1.3 kristaps 328: { dosymbol, "*", 1 }, /* TEXICMD_ASTERISK */
1.1 kristaps 329: { dosymbol, "@", 1 }, /* TEXICMD_AT */
1.3 kristaps 330: { doignline, "author", 6 }, /* TEXICMD_AUTHOR */
1.21 kristaps 331: { doinline, "b", 1 }, /* TEXICMD_BOLD */
1.3 kristaps 332: { dosymbol, "!", 1 }, /* TEXICMD_BANG */
1.7 kristaps 333: { dosymbol, "bullet", 6 }, /* TEXICMD_BULLET */
1.1 kristaps 334: { dobye, "bye", 3 }, /* TEXICMD_BYE */
1.12 kristaps 335: { doignline, "center", 6 }, /* TEXICMD_CENTER */
336: { dosection, "chapter", 7 }, /* TEXICMD_CHAPTER */
1.1 kristaps 337: { doignline, "cindex", 6 }, /* TEXICMD_CINDEX */
1.16 kristaps 338: { doaccent, "^", 1 }, /* TEXICMD_CIRCUMFLEX */
1.21 kristaps 339: { doinline, "code", 4 }, /* TEXICMD_CODE */
340: { doinline, "cite", 4 }, /* TEXICMD_CITE */
1.3 kristaps 341: { dosymbol, ":", 1 }, /* TEXICMD_COLON */
1.18 kristaps 342: { NULL, "columnfractions", 15 }, /* TEXICMD_COLUMNFRACTIONS */
1.12 kristaps 343: { doinline, "command", 7 }, /* TEXICMD_COMMAND */
1.1 kristaps 344: { doignline, "c", 1 }, /* TEXICMD_COMMENT */
1.2 kristaps 345: { doignline, "comment", 7 }, /* TEXICMD_COMMENT_LONG */
1.1 kristaps 346: { doignline, "contents", 8 }, /* TEXICMD_CONTENTS */
347: { doignblock, "copying", 7 }, /* TEXICMD_COPYING */
348: { dosymbol, "copyright", 9 }, /* TEXICMD_COPYRIGHT */
1.15 kristaps 349: { dodefn, "deffn", 5 }, /* TEXICMD_DEFFN */
350: { dodefn, "deffnx", 6 }, /* TEXICMD_DEFFNX */
351: { dodefn, "defmac", 6 }, /* TEXICMD_DEFMAC */
352: { dodefn, "defmacx", 7 }, /* TEXICMD_DEFMACX */
353: { dodefn, "deftp", 5 }, /* TEXICMD_DEFTP */
354: { dodefn, "deftpx", 6 }, /* TEXICMD_DEFTPX */
355: { dodefn, "deftypefn", 9 }, /* TEXICMD_DEFTYPEFN */
356: { dodefn, "deftypefnx", 10 }, /* TEXICMD_DEFTYPEFNX */
357: { dodefn, "deftypefun", 10 }, /* TEXICMD_DEFTYPEFUN */
358: { dodefn, "deftypefunx", 11 }, /* TEXICMD_DEFTYPEFUNX */
359: { dodefn, "deftypevar", 10 }, /* TEXICMD_DEFTYPEVAR */
360: { dodefn, "deftypevarx", 11 }, /* TEXICMD_DEFTYPEVARX */
361: { dodefn, "deftypevr", 9 }, /* TEXICMD_DEFTYPEVR */
362: { dodefn, "deftypevrx", 10 }, /* TEXICMD_DEFTYPEVRX */
363: { dodefn, "defun", 5 }, /* TEXICMD_DEFUN */
364: { dodefn, "defunx", 6 }, /* TEXICMD_DEFUNX */
365: { dodefn, "defvar", 6 }, /* TEXICMD_DEFVAR */
366: { dodefn, "defvarx", 7 }, /* TEXICMD_DEFVARX */
367: { dodefn, "defvr", 5 }, /* TEXICMD_DEFVR */
368: { dodefn, "defvrx", 6 }, /* TEXICMD_DEFVRX */
1.1 kristaps 369: { doignblock, "detailmenu", 10 }, /* TEXICMD_DETAILMENU */
1.21 kristaps 370: { doinline, "dfn", 3 }, /* TEXICMD_DFN */
1.1 kristaps 371: { doignline, "dircategory", 11 }, /* TEXICMD_DIRCATEGORY */
372: { doignblock, "direntry", 8 }, /* TEXICMD_DIRENTRY */
1.3 kristaps 373: { dodisplay, "display", 7 }, /* TEXICMD_DISPLAY */
1.2 kristaps 374: { dosymbol, "dots", 4 }, /* TEXICMD_DOTS */
1.8 kristaps 375: { dolink, "email", 5 }, /* TEXICMD_EMAIL */
1.21 kristaps 376: { doinline, "emph", 4 }, /* TEXICMD_EMPH */
1.1 kristaps 377: { NULL, "end", 3 }, /* TEXICMD_END */
1.2 kristaps 378: { doenumerate, "enumerate", 9 }, /* TEXICMD_ENUMERATE */
1.12 kristaps 379: { doinline, "env", 3 }, /* TEXICMD_ENV */
1.15 kristaps 380: { dosymbol, "error", 5 }, /* TEXICMD_ERROR */
1.1 kristaps 381: { doexample, "example", 7 }, /* TEXICMD_EXAMPLE */
1.17 kristaps 382: { dosymbol, "expansion", 9 }, /* TEXICMD_EXPANSION */
1.12 kristaps 383: { doinline, "file", 4 }, /* TEXICMD_FILE */
1.17 kristaps 384: { doignline, "finalout", 8 }, /* TEXICMD_FINALOUT */
1.20 kristaps 385: { doignline, "findex", 6 }, /* TEXICMD_FINDEX */
386: { dotable, "ftable", 6 }, /* TEXICMD_FTABLE */
387: { dodisplay, "format", 6 }, /* TEXICMD_FORMAT */
1.16 kristaps 388: { doaccent, "`", 1 }, /* TEXICMD_GRAVE */
1.3 kristaps 389: { doblock, "group", 5 }, /* TEXICMD_GROUP */
1.2 kristaps 390: { dosection, "heading", 7 }, /* TEXICMD_HEADING */
1.3 kristaps 391: { doignline, "headings", 8 }, /* TEXICMD_HEADINGS */
1.18 kristaps 392: { doitem, "headitem", 8 }, /* TEXICMD_HEADITEM */
1.3 kristaps 393: { dosymbol, "-", 1 }, /* TEXICMD_HYPHEN */
1.21 kristaps 394: { doinline, "i", 1 }, /* TEXICMD_I */
1.3 kristaps 395: { doignblock, "ifclear", 7 }, /* TEXICMD_IFCLEAR */
1.14 kristaps 396: { doignblock, "ifdocbook", 9 }, /* TEXICMD_IFDOCBOOK */
1.1 kristaps 397: { doignblock, "ifhtml", 6 }, /* TEXICMD_IFHTML */
1.3 kristaps 398: { doignblock, "ifinfo", 6 }, /* TEXICMD_IFINFO */
1.14 kristaps 399: { doblock, "ifnotdocbook", 12 }, /* TEXICMD_IFNOTDOCBOOK */
400: { doblock, "ifnothtml", 9 }, /* TEXICMD_IFNOTHTML */
401: { doblock, "ifnotinfo", 9 }, /* TEXICMD_IFNOTINFO */
402: { doignblock, "ifnotplaintext", 14 }, /* TEXICMD_IFNOTPLAINTEXT */
1.3 kristaps 403: { doblock, "ifnottex", 8 }, /* TEXICMD_IFNOTTEX */
1.14 kristaps 404: { doblock, "ifnotxml", 8 }, /* TEXICMD_IFNOTXML */
405: { doblock, "ifplaintext", 11 }, /* TEXICMD_IFPLAINTEXT */
1.1 kristaps 406: { doignblock, "iftex", 5 }, /* TEXICMD_IFTEX */
1.3 kristaps 407: { doignblock, "ifset", 5 }, /* TEXICMD_IFSET */
1.14 kristaps 408: { doignblock, "ifxml", 5 }, /* TEXICMD_IFXML */
1.17 kristaps 409: { doignblock, "ignore", 6 }, /* TEXICMD_IGNORE */
1.1 kristaps 410: { doignbracket, "image", 5 }, /* TEXICMD_IMAGE */
1.2 kristaps 411: { doinclude, "include", 7 }, /* TEXICMD_INCLUDE */
1.13 kristaps 412: { dodisplay, "indentblock", 11 }, /* TEXICMD_INDENTBLOCK */
1.5 kristaps 413: { doignline, "insertcopying", 13 }, /* TEXICMD_INSERTCOPYING */
1.1 kristaps 414: { doitem, "item", 4 }, /* TEXICMD_ITEM */
415: { doitemize, "itemize", 7 }, /* TEXICMD_ITEMIZE */
1.20 kristaps 416: { doitem, "itemx", 5 }, /* TEXICMD_ITEMX */
1.21 kristaps 417: { doinline, "kbd", 3 }, /* TEXICMD_KBD */
1.18 kristaps 418: { dobracket, "key", 3 }, /* TEXICMD_KEY */
1.20 kristaps 419: { doignline, "kindex", 6 }, /* TEXICMD_KINDEX */
1.1 kristaps 420: { dosymbol, "LaTeX", 5 }, /* TEXICMD_LATEX */
1.23 ! kristaps 421: { dosecoffs, "lowersections", 13 }, /* TEXICMD_LOWERSECTIONS */
1.3 kristaps 422: { domath, "math", 4 }, /* TEXICMD_MATH */
1.1 kristaps 423: { doignblock, "menu", 4 }, /* TEXICMD_MENU */
1.18 kristaps 424: { domultitable, "multitable", 10 }, /* TEXICMD_MULTITABLE */
1.15 kristaps 425: { doignline, "need", 4 }, /* TEXICMD_NEED */
1.3 kristaps 426: { dosymbol, "\n", 1 }, /* TEXICMD_NEWLINE */
1.1 kristaps 427: { doignline, "node", 4 }, /* TEXICMD_NODE */
1.3 kristaps 428: { doignline, "noindent", 8 }, /* TEXICMD_NOINDENT */
1.16 kristaps 429: { doinline, "option", 6 }, /* TEXICMD_OPTION */
1.8 kristaps 430: { dolink, "pxref", 5 }, /* TEXICMD_PXREF */
1.3 kristaps 431: { dosymbol, "?", 1 }, /* TEXICMD_QUESTIONMARK */
1.1 kristaps 432: { doquotation, "quotation", 9 }, /* TEXICMD_QUOTATION */
1.3 kristaps 433: { doignline, "page", 4 }, /* TEXICMD_PAGE */
434: { doignline, "paragraphindent", 14 }, /* TEXICMD_PARINDENT */
1.2 kristaps 435: { doignline, "printindex", 10 }, /* TEXICMD_PRINTINDEX */
1.21 kristaps 436: { doinline, "r", 1 }, /* TEXICMD_R */
1.23 ! kristaps 437: { dosecoffs, "raisesections", 13 }, /* TEXICMD_RAISESECTIONS */
1.1 kristaps 438: { dobracket, "ref", 3 }, /* TEXICMD_REF */
1.15 kristaps 439: { dosymbol, "result", 6 }, /* TEXICMD_RESULT */
1.21 kristaps 440: { doinline, "samp", 4 }, /* TEXICMD_SAMP */
441: { doinline, "sansserif", 9 }, /* TEXICMD_SANSSERIF */
1.7 kristaps 442: { dobracket, "sc", 2 }, /* TEXICMD_SC */
1.1 kristaps 443: { dosection, "section", 7 }, /* TEXICMD_SECTION */
1.3 kristaps 444: { doignline, "set", 3 }, /* TEXICMD_SET */
1.1 kristaps 445: { doignline, "setchapternewpage", 17 }, /* TEXICMD_SETCHAPNEWPAGE */
446: { doignline, "setfilename", 11 }, /* TEXICMD_SETFILENAME */
1.10 kristaps 447: { dotitle, "settitle", 8 }, /* TEXICMD_SETTITLE */
1.21 kristaps 448: { doinline, "slanted", 7 }, /* TEXICMD_SLANTED */
1.3 kristaps 449: { dosp, "sp", 2 }, /* TEXICMD_SP */
450: { dosymbol, " ", 1 }, /* TEXICMD_SPACE */
1.17 kristaps 451: { doignline, "smallbook", 9 }, /* TEXICMD_SMALLBOOK */
1.12 kristaps 452: { dodisplay, "smalldisplay", 12 }, /* TEXICMD_SMALLDISPLAY */
1.3 kristaps 453: { doexample, "smallexample", 12 }, /* TEXICMD_SMALLEXAMPLE */
1.20 kristaps 454: { dodisplay, "smallformat", 11 }, /* TEXICMD_SMALLFORMAT */
1.13 kristaps 455: { dodisplay, "smallindentblock", 16 }, /* TEXICMD_SMALLINDENTBLOCK */
1.3 kristaps 456: { dosymbol, "{", 1 }, /* TEXICMD_SQUIGGLE_LEFT */
457: { dosymbol, "}", 1 }, /* TEXICMD_SQUIGGLE_RIGHT */
1.21 kristaps 458: { doinline, "strong", 6 }, /* TEXICMD_STRONG */
1.20 kristaps 459: { dosubsection, "subheading", 10 }, /* TEXICMD_SUBHEADING */
1.1 kristaps 460: { dosubsection, "subsection", 10 }, /* TEXICMD_SUBSECTION */
1.23 ! kristaps 461: { dosubsubsection, "subsubsection", 13 }, /* TEXICMD_SUBSUBSECTION */
1.3 kristaps 462: { doignline, "subtitle", 8 }, /* TEXICMD_SUBTITLE */
1.20 kristaps 463: { doignline, "syncodeindex", 12 }, /* TEXICMD_SYNCODEINDEX */
1.21 kristaps 464: { doinline, "t", 1 }, /* TEXICMD_T */
1.18 kristaps 465: { dotab, "tab", 3 }, /* TEXICMD_TAB */
466: { dosymbol, "\t", 1 }, /* TEXICMD_TABSYM */
1.1 kristaps 467: { dotable, "table", 5 }, /* TEXICMD_TABLE */
468: { doignblock, "tex", 3 }, /* TEXICMD_TEX */
469: { dosymbol, "TeX", 3 }, /* TEXICMD_TEXSYM */
1.16 kristaps 470: { doaccent, "~", 1 }, /* TEXICMD_TILDE */
1.3 kristaps 471: { doignline, "title", 5 }, /* TEXICMD_TITLE */
1.1 kristaps 472: { dobracket, "titlefont", 9 }, /* TEXICMD_TITLEFONT */
473: { doignblock, "titlepage", 9 }, /* TEXICMD_TITLEPAGE */
474: { dotop, "top", 3 }, /* TEXICMD_TOP */
1.16 kristaps 475: { doaccent, "\"", 1 }, /* TEXICMD_UMLAUT */
1.12 kristaps 476: { dosection, "unnumbered", 10 }, /* TEXICMD_UNNUMBERED */
1.2 kristaps 477: { dosection, "unnumberedsec", 13 }, /* TEXICMD_UNNUMBEREDSEC */
1.20 kristaps 478: { dosubsection, "unnumberedsubsec", 16 }, /* TEXICMD_UNNUMBEREDSUBSEC */
1.23 ! kristaps 479: { dosubsubsection, "unnumberedsubsubsec", 19 }, /* TEXICMD_UNNUMBEREDSUBSUBSEC */
1.8 kristaps 480: { dolink, "uref", 4 }, /* TEXICMD_UREF */
481: { dolink, "url", 3 }, /* TEXICMD_URL */
1.12 kristaps 482: { doinline, "var", 3 }, /* TEXICMD_VAR */
1.16 kristaps 483: { doverbinclude, "verbatiminclude", 15 }, /* TEXICMD_VERBATIMINCLUDE */
1.18 kristaps 484: { doignline, "vindex", 6 }, /* TEXICMD_VINDEX */
1.9 kristaps 485: { dosp, "vskip", 5 }, /* TEXICMD_VSKIP */
1.20 kristaps 486: { dotable, "vtable", 6 }, /* TEXICMD_VTABLE */
1.3 kristaps 487: { dobracket, "w", 1 }, /* TEXICMD_W */
1.8 kristaps 488: { dolink, "xref", 4 }, /* TEXICMD_XREF */
1.20 kristaps 489: /* TEXICMD__END */
1.1 kristaps 490: };
491:
1.2 kristaps 492: /*
493: * Unmap the top-most file that we're using.
494: */
1.1 kristaps 495: static void
496: texifilepop(struct texi *p)
497: {
498: struct texifile *f;
499:
500: assert(p->filepos > 0);
501: f = &p->files[--p->filepos];
502: munmap(f->map, f->mapsz);
503: }
504:
1.2 kristaps 505: /*
1.8 kristaps 506: * Unmap all files that we're currently using and free all resources
507: * that we've allocated during the parse.
1.2 kristaps 508: * The utility should exit(...) after this is called.
509: */
1.1 kristaps 510: static void
511: texiexit(struct texi *p)
512: {
1.5 kristaps 513: size_t i;
514:
515: if (p->outcol)
516: putchar('\n');
1.1 kristaps 517:
518: while (p->filepos > 0)
519: texifilepop(p);
1.5 kristaps 520:
521: for (i = 0; i < p->dirsz; i++)
522: free(p->dirs[i]);
1.10 kristaps 523:
1.5 kristaps 524: free(p->dirs);
1.10 kristaps 525: free(p->subtitle);
526: free(p->title);
1.1 kristaps 527: }
528:
1.2 kristaps 529: /*
530: * Fatal error: unmap all files and exit.
531: * The "errstring" is passed to perror(3).
532: */
1.1 kristaps 533: static void
1.2 kristaps 534: texiabort(struct texi *p, const char *errstring)
1.1 kristaps 535: {
536:
537: perror(errstring);
538: texiexit(p);
539: exit(EXIT_FAILURE);
540: }
541:
542: /*
543: * Print a generic warning message (to stderr) tied to our current
544: * location in the parse sequence.
545: */
546: static void
547: texiwarn(const struct texi *p, const char *fmt, ...)
548: {
549: va_list ap;
550:
1.2 kristaps 551: fprintf(stderr, "%s:%zu:%zu: warning: ",
1.1 kristaps 552: p->files[p->filepos - 1].name,
553: p->files[p->filepos - 1].line + 1,
554: p->files[p->filepos - 1].col + 1);
555: va_start(ap, fmt);
556: vfprintf(stderr, fmt, ap);
557: va_end(ap);
558: fputc('\n', stderr);
559: }
560:
1.8 kristaps 561: /*
562: * Print an error message (to stderr) tied to our current location in
563: * the parse sequence, invoke texiexit(), then die.
564: */
1.2 kristaps 565: static void
566: texierr(struct texi *p, const char *fmt, ...)
567: {
568: va_list ap;
569:
570: fprintf(stderr, "%s:%zu:%zu: error: ",
571: p->files[p->filepos - 1].name,
572: p->files[p->filepos - 1].line + 1,
573: p->files[p->filepos - 1].col + 1);
574: va_start(ap, fmt);
575: vfprintf(stderr, fmt, ap);
576: va_end(ap);
577: fputc('\n', stderr);
578: texiexit(p);
579: exit(EXIT_FAILURE);
580: }
581:
1.1 kristaps 582: /*
1.8 kristaps 583: * Put a single data character to the output if we're not ignoring.
584: * Adjusts our output status.
1.18 kristaps 585: * This shouldn't be called for macros: just for ordinary text.
1.1 kristaps 586: */
587: static void
588: texiputchar(struct texi *p, char c)
589: {
590:
1.3 kristaps 591: if (p->ign)
1.1 kristaps 592: return;
1.18 kristaps 593:
594: if ('.' == c && 0 == p->outcol)
595: fputs("\\&", stdout);
596:
1.1 kristaps 597: putchar(c);
1.11 kristaps 598: p->seenvs = 0;
1.1 kristaps 599: if ('\n' == c) {
600: p->outcol = 0;
601: p->seenws = 0;
602: } else
603: p->outcol++;
604: }
605:
606: /*
607: * Put multiple characters (see texiputchar()).
1.18 kristaps 608: * This shouldn't be called for macros: just for ordinary text.
1.1 kristaps 609: */
610: static void
611: texiputchars(struct texi *p, const char *s)
612: {
613:
614: while ('\0' != *s)
615: texiputchar(p, *s++);
616: }
617:
618: /*
1.8 kristaps 619: * Close an mdoc(7) macro opened with teximacroopen().
620: * If there are no more macros on the line, prints a newline.
1.1 kristaps 621: */
622: static void
1.3 kristaps 623: teximacroclose(struct texi *p)
624: {
625:
1.18 kristaps 626: if (p->ign)
627: return;
628:
629: if (0 == --p->outmacro) {
630: putchar('\n');
631: p->outcol = p->seenws = 0;
632: }
1.3 kristaps 633: }
634:
635: /*
1.8 kristaps 636: * Open a mdoc(7) macro.
637: * This is used for line macros, e.g., Qq [foo bar baz].
638: * It can be invoked for nested macros, e.g., Qq Li foo .
1.3 kristaps 639: */
640: static void
641: teximacroopen(struct texi *p, const char *s)
1.1 kristaps 642: {
1.18 kristaps 643: int rc;
644:
645: if (p->ign)
646: return;
647:
648: if (p->outcol && 0 == p->outmacro) {
649: putchar('\n');
650: p->outcol = 0;
651: }
1.1 kristaps 652:
1.5 kristaps 653: if (0 == p->outmacro)
1.18 kristaps 654: putchar('.');
1.5 kristaps 655: else
1.18 kristaps 656: putchar(' ');
657:
658: if (EOF != (rc = fputs(s, stdout)))
659: p->outcol += rc;
660:
661: putchar(' ');
662: p->outcol++;
1.3 kristaps 663: p->outmacro++;
1.5 kristaps 664: p->seenws = 0;
1.1 kristaps 665: }
666:
667: /*
1.8 kristaps 668: * Put a stadnalone mdoc(7) command with the trailing newline.
1.1 kristaps 669: */
670: static void
671: teximacro(struct texi *p, const char *s)
672: {
673:
1.18 kristaps 674: if (p->ign)
675: return;
676:
1.4 kristaps 677: if (p->outmacro)
678: texierr(p, "\"%s\" in open line scope!?", s);
1.18 kristaps 679: if (p->literal)
1.4 kristaps 680: texierr(p, "\"%s\" in a literal scope!?", s);
681:
1.1 kristaps 682: if (p->outcol)
1.18 kristaps 683: putchar('\n');
1.5 kristaps 684:
1.18 kristaps 685: putchar('.');
686: puts(s);
687: p->outcol = p->seenws = 0;
1.1 kristaps 688: }
689:
1.11 kristaps 690: static void
691: texivspace(struct texi *p)
692: {
693:
694: if (p->seenvs)
695: return;
696: teximacro(p, "Pp");
697: p->seenvs = 1;
698: }
699:
1.1 kristaps 700: /*
701: * Advance by a single byte in the input stream.
702: */
703: static void
704: advance(struct texi *p, const char *buf, size_t *pos)
705: {
706:
707: if ('\n' == buf[*pos]) {
708: p->files[p->filepos - 1].line++;
709: p->files[p->filepos - 1].col = 0;
710: } else
711: p->files[p->filepos - 1].col++;
712:
713: (*pos)++;
714: }
715:
1.11 kristaps 716: static void
717: texipunctuate(struct texi *p, const char *buf, size_t sz, size_t *pos)
718: {
719: size_t start, end;
720:
721: if (1 != p->outmacro)
722: return;
723:
724: for (start = end = *pos; end < sz; end++) {
725: switch (buf[end]) {
726: case (','):
727: case (')'):
728: case ('.'):
729: case ('"'):
730: case (':'):
731: case ('!'):
732: case ('?'):
733: continue;
734: default:
735: break;
736: }
737: break;
738: }
739: if (end == *pos)
740: return;
741: if (end + 1 == sz || ' ' == buf[end] || '\n' == buf[end]) {
742: for ( ; start < end; start++) {
743: texiputchar(p, ' ');
744: texiputchar(p, buf[start]);
745: advance(p, buf, pos);
746: }
747: }
748: }
749:
1.1 kristaps 750: /*
751: * Advance to the next non-whitespace word in the input stream.
752: * If we're in literal mode, then print all of the whitespace as we're
753: * doing so.
754: */
755: static size_t
756: advancenext(struct texi *p, const char *buf, size_t sz, size_t *pos)
757: {
758:
1.3 kristaps 759: if (p->literal) {
1.9 kristaps 760: while (*pos < sz && ismspace(buf[*pos])) {
1.5 kristaps 761: if (*pos && '\n' == buf[*pos] &&
762: '\\' == buf[*pos - 1])
763: texiputchar(p, 'e');
1.1 kristaps 764: texiputchar(p, buf[*pos]);
765: advance(p, buf, pos);
766: }
767: return(*pos);
768: }
769:
1.9 kristaps 770: while (*pos < sz && ismspace(buf[*pos])) {
1.1 kristaps 771: p->seenws = 1;
772: /*
773: * If it looks like we've printed a double-line, then
774: * output a paragraph.
775: * FIXME: this is stupid.
776: */
1.11 kristaps 777: if (*pos && '\n' == buf[*pos] && '\n' == buf[*pos - 1])
778: texivspace(p);
1.1 kristaps 779: advance(p, buf, pos);
780: }
781: return(*pos);
782: }
783:
784: /*
785: * Advance to the EOLN in the input stream.
786: */
787: static size_t
1.3 kristaps 788: advanceeoln(struct texi *p, const char *buf,
789: size_t sz, size_t *pos, int consumenl)
1.1 kristaps 790: {
791:
1.8 kristaps 792: /* FIXME: disregards @NEWLINE. */
1.1 kristaps 793: while (*pos < sz && '\n' != buf[*pos])
794: advance(p, buf, pos);
1.3 kristaps 795: if (*pos < sz && consumenl)
796: advance(p, buf, pos);
1.1 kristaps 797: return(*pos);
798: }
799:
800: /*
801: * Advance to position "end", which is an absolute position in the
802: * current buffer greater than or equal to the current position.
803: */
804: static void
805: advanceto(struct texi *p, const char *buf, size_t *pos, size_t end)
806: {
807:
808: assert(*pos <= end);
809: while (*pos < end)
810: advance(p, buf, pos);
811: }
812:
813: /*
814: * Output a free-form word in the input stream, progressing to the next
815: * command or white-space.
816: * This also will advance the input stream.
817: */
818: static void
1.8 kristaps 819: texiword(struct texi *p, const char *buf,
820: size_t sz, size_t *pos, char extra)
1.1 kristaps 821: {
822:
1.18 kristaps 823: if (p->seenws && 0 == p->outmacro && p->outcol > 72 && 0 == p->literal)
1.1 kristaps 824: texiputchar(p, '\n');
1.8 kristaps 825: /* FIXME: abstract this: we use it elsewhere. */
1.3 kristaps 826: if (p->seenws && p->outcol && 0 == p->literal)
1.1 kristaps 827: texiputchar(p, ' ');
828:
829: p->seenws = 0;
830:
1.9 kristaps 831: while (*pos < sz && ! ismspace(buf[*pos])) {
1.1 kristaps 832: switch (buf[*pos]) {
833: case ('@'):
834: case ('}'):
835: case ('{'):
836: return;
837: }
1.8 kristaps 838: if ('\0' != extra && buf[*pos] == extra)
839: return;
1.1 kristaps 840: if (*pos < sz - 1 &&
841: '`' == buf[*pos] &&
842: '`' == buf[*pos + 1]) {
843: texiputchars(p, "\\(lq");
844: advance(p, buf, pos);
845: } else if (*pos < sz - 1 &&
846: '\'' == buf[*pos] &&
847: '\'' == buf[*pos + 1]) {
848: texiputchars(p, "\\(rq");
849: advance(p, buf, pos);
850: } else
851: texiputchar(p, buf[*pos]);
852: advance(p, buf, pos);
853: }
854: }
855:
1.8 kristaps 856: /*
857: * Look up the command at position "pos" in the buffer, returning it (or
858: * TEXICMD__MAX if none found) and setting "end" to be the absolute
859: * index after the command name.
860: */
1.1 kristaps 861: static enum texicmd
862: texicmd(struct texi *p, const char *buf,
863: size_t pos, size_t sz, size_t *end)
864: {
865: size_t i, len;
866:
867: assert('@' == buf[pos]);
1.3 kristaps 868:
1.9 kristaps 869: if ((*end = pos) == sz)
870: return(TEXICMD__MAX);
871: else if ((*end = ++pos) == sz)
1.3 kristaps 872: return(TEXICMD__MAX);
873:
874: /* Alphabetic commands are special. */
875: if ( ! isalpha(buf[pos])) {
1.9 kristaps 876: if ((*end = pos + 1) == sz)
877: return(TEXICMD__MAX);
1.3 kristaps 878: for (i = 0; i < TEXICMD__MAX; i++) {
879: if (1 != texitoks[i].len)
880: continue;
881: if (0 == strncmp(texitoks[i].tok, &buf[pos], 1))
882: return(i);
883: }
884: texiwarn(p, "bad command: @%c", buf[pos]);
885: return(TEXICMD__MAX);
886: }
887:
1.9 kristaps 888: for (*end = pos; *end < sz && ! ismspace(buf[*end]); (*end)++)
1.3 kristaps 889: if ((*end > pos && ('@' == buf[*end] ||
890: '{' == buf[*end] || '}' == buf[*end])))
1.1 kristaps 891: break;
892:
893: len = *end - pos;
894: for (i = 0; i < TEXICMD__MAX; i++) {
895: if (len != texitoks[i].len)
896: continue;
897: if (0 == strncmp(texitoks[i].tok, &buf[pos], len))
898: return(i);
899: }
900:
1.3 kristaps 901: texiwarn(p, "bad command: @%.*s", (int)len, &buf[pos]);
1.1 kristaps 902: return(TEXICMD__MAX);
903: }
904:
1.8 kristaps 905: /*
906: * Parse an argument from a bracketed command, e.g., @url{foo, baz}.
907: * Num should be set to the argument we're currently parsing, although
908: * it suffixes for it to be zero or non-zero.
909: * This will return 1 if there are more arguments, 0 otherwise.
910: * This will stop (returning 0) in the event of EOF or if we're not at a
911: * bracket for the zeroth parse.
912: */
913: static int
914: parsearg(struct texi *p, const char *buf,
915: size_t sz, size_t *pos, size_t num)
916: {
917: size_t end;
918: enum texicmd cmd;
919:
1.9 kristaps 920: while (*pos < sz && ismspace(buf[*pos]))
1.8 kristaps 921: advance(p, buf, pos);
922: if (*pos == sz || (0 == num && '{' != buf[*pos]))
923: return(0);
924: if (0 == num)
925: advance(p, buf, pos);
926:
927: while ((*pos = advancenext(p, buf, sz, pos)) < sz) {
928: switch (buf[*pos]) {
929: case (','):
930: advance(p, buf, pos);
931: return(1);
932: case ('}'):
933: advance(p, buf, pos);
934: return(0);
935: case ('{'):
936: if (0 == p->ign)
937: texiwarn(p, "unexpected \"{\"");
938: advance(p, buf, pos);
939: continue;
940: case ('@'):
941: break;
942: default:
943: texiword(p, buf, sz, pos, ',');
944: continue;
945: }
946:
947: cmd = texicmd(p, buf, *pos, sz, &end);
948: advanceto(p, buf, pos, end);
949: if (TEXICMD__MAX == cmd)
950: continue;
951: if (NULL != texitoks[cmd].fp)
952: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
953: }
954: return(0);
955: }
956:
957: /*
958: * Parse until the end of a bracketed statement, e.g., @foo{bar baz}.
959: * This will stop in the event of EOF or if we're not at a bracket.
960: */
1.1 kristaps 961: static void
962: parsebracket(struct texi *p, const char *buf, size_t sz, size_t *pos)
963: {
964: size_t end;
965: enum texicmd cmd;
966:
1.9 kristaps 967: while (*pos < sz && ismspace(buf[*pos]))
1.3 kristaps 968: advance(p, buf, pos);
969:
1.1 kristaps 970: if (*pos == sz || '{' != buf[*pos])
971: return;
972: advance(p, buf, pos);
973:
974: while ((*pos = advancenext(p, buf, sz, pos)) < sz) {
975: switch (buf[*pos]) {
976: case ('}'):
977: advance(p, buf, pos);
978: return;
979: case ('{'):
1.3 kristaps 980: if (0 == p->ign)
981: texiwarn(p, "unexpected \"{\"");
982: advance(p, buf, pos);
983: continue;
984: case ('@'):
985: break;
986: default:
1.8 kristaps 987: texiword(p, buf, sz, pos, '\0');
1.3 kristaps 988: continue;
989: }
990:
991: cmd = texicmd(p, buf, *pos, sz, &end);
992: advanceto(p, buf, pos, end);
993: if (TEXICMD__MAX == cmd)
994: continue;
995: if (NULL != texitoks[cmd].fp)
996: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
997: }
998: }
999:
1000: /*
1001: * This should be invoked when we're on a macro line and want to process
1002: * to the end of the current input line, doing all of our macros along
1003: * the way.
1004: */
1005: static void
1006: parseeoln(struct texi *p, const char *buf, size_t sz, size_t *pos)
1007: {
1008: size_t end;
1009: enum texicmd cmd;
1010:
1011: while (*pos < sz && '\n' != buf[*pos]) {
1012: while (*pos < sz && isws(buf[*pos])) {
1013: p->seenws = 1;
1.19 kristaps 1014: if (p->literal)
1015: texiputchar(p, buf[*pos]);
1.3 kristaps 1016: advance(p, buf, pos);
1017: }
1018: switch (buf[*pos]) {
1019: case ('}'):
1020: if (0 == p->ign)
1021: texiwarn(p, "unexpected \"}\"");
1022: advance(p, buf, pos);
1023: continue;
1024: case ('{'):
1025: if (0 == p->ign)
1026: texiwarn(p, "unexpected \"{\"");
1.1 kristaps 1027: advance(p, buf, pos);
1028: continue;
1029: case ('@'):
1030: break;
1031: default:
1.8 kristaps 1032: texiword(p, buf, sz, pos, '\0');
1.1 kristaps 1033: continue;
1034: }
1035:
1036: cmd = texicmd(p, buf, *pos, sz, &end);
1037: advanceto(p, buf, pos, end);
1038: if (TEXICMD__MAX == cmd)
1039: continue;
1040: if (NULL != texitoks[cmd].fp)
1041: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
1042: }
1043: }
1044:
1.8 kristaps 1045: /*
1046: * Parse a single word or command.
1047: * This will return immediately at the EOF.
1048: */
1.1 kristaps 1049: static void
1.3 kristaps 1050: parsesingle(struct texi *p, const char *buf, size_t sz, size_t *pos)
1051: {
1052: size_t end;
1053: enum texicmd cmd;
1054:
1055: if ((*pos = advancenext(p, buf, sz, pos)) >= sz)
1056: return;
1057:
1058: switch (buf[*pos]) {
1059: case ('}'):
1060: if (0 == p->ign)
1061: texiwarn(p, "unexpected \"}\"");
1062: advance(p, buf, pos);
1063: return;
1064: case ('{'):
1065: if (0 == p->ign)
1066: texiwarn(p, "unexpected \"{\"");
1067: advance(p, buf, pos);
1068: return;
1069: case ('@'):
1070: break;
1071: default:
1.8 kristaps 1072: texiword(p, buf, sz, pos, '\0');
1.3 kristaps 1073: return;
1074: }
1075:
1076: cmd = texicmd(p, buf, *pos, sz, &end);
1077: advanceto(p, buf, pos, end);
1078: if (TEXICMD__MAX == cmd)
1079: return;
1080: if (NULL != texitoks[cmd].fp)
1081: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
1082: }
1083:
1.15 kristaps 1084: static int
1085: parselinearg(struct texi *p, const char *buf, size_t sz, size_t *pos)
1086: {
1087:
1088: while (*pos < sz && isws(buf[*pos])) {
1089: p->seenws = 1;
1090: advance(p, buf, pos);
1091: }
1092:
1093: if (*pos < sz && '{' == buf[*pos])
1094: parsebracket(p, buf, sz, pos);
1095: else if ('\n' != buf[*pos])
1096: parsesingle(p, buf, sz, pos);
1097: else
1098: return(0);
1099:
1100: return(1);
1101: }
1102:
1.8 kristaps 1103: /*
1104: * Parse til the end of the buffer.
1105: */
1.3 kristaps 1106: static void
1.7 kristaps 1107: parseeof(struct texi *p, const char *buf, size_t sz)
1108: {
1109: size_t pos;
1110:
1111: for (pos = 0; pos < sz; )
1112: parsesingle(p, buf, sz, &pos);
1113: }
1114:
1.8 kristaps 1115: /*
1116: * Parse a block sequence until we have the "@end endtoken" command
1117: * invocation.
1118: * This will return immediately at EOF.
1119: */
1.7 kristaps 1120: static void
1.1 kristaps 1121: parseto(struct texi *p, const char *buf,
1122: size_t sz, size_t *pos, const char *endtoken)
1123: {
1124: size_t end;
1125: enum texicmd cmd;
1126: size_t endtoksz;
1127:
1128: endtoksz = strlen(endtoken);
1129: assert(endtoksz > 0);
1130:
1131: while ((*pos = advancenext(p, buf, sz, pos)) < sz) {
1132: switch (buf[*pos]) {
1133: case ('}'):
1.3 kristaps 1134: if (0 == p->ign)
1135: texiwarn(p, "unexpected \"}\"");
1.1 kristaps 1136: advance(p, buf, pos);
1137: continue;
1138: case ('{'):
1.3 kristaps 1139: if (0 == p->ign)
1140: texiwarn(p, "unexpected \"{\"");
1.1 kristaps 1141: advance(p, buf, pos);
1142: continue;
1143: case ('@'):
1144: break;
1145: default:
1.8 kristaps 1146: texiword(p, buf, sz, pos, '\0');
1.1 kristaps 1147: continue;
1148: }
1149:
1150: cmd = texicmd(p, buf, *pos, sz, &end);
1151: advanceto(p, buf, pos, end);
1152: if (TEXICMD_END == cmd) {
1.2 kristaps 1153: while (*pos < sz && isws(buf[*pos]))
1.1 kristaps 1154: advance(p, buf, pos);
1155: /*
1.8 kristaps 1156: * FIXME: check the full word, not just its
1157: * initial substring!
1.1 kristaps 1158: */
1159: if (sz - *pos >= endtoksz && 0 == strncmp
1160: (&buf[*pos], endtoken, endtoksz)) {
1.3 kristaps 1161: advanceeoln(p, buf, sz, pos, 0);
1.1 kristaps 1162: break;
1163: }
1.3 kristaps 1164: if (0 == p->ign)
1165: texiwarn(p, "unexpected \"end\"");
1166: advanceeoln(p, buf, sz, pos, 0);
1.1 kristaps 1167: continue;
1168: } else if (TEXICMD__MAX != cmd)
1169: if (NULL != texitoks[cmd].fp)
1170: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
1171: }
1172: }
1173:
1.8 kristaps 1174: /*
1175: * Memory-map the file "fname" and begin parsing it.
1176: * This can be called in a nested context.
1177: */
1.1 kristaps 1178: static void
1.16 kristaps 1179: parsefile(struct texi *p, const char *fname, int parse)
1.2 kristaps 1180: {
1.16 kristaps 1181: struct texifile *f;
1182: int fd;
1183: struct stat st;
1184: size_t i;
1.2 kristaps 1185:
1186: assert(p->filepos < 64);
1187: f = &p->files[p->filepos];
1188: memset(f, 0, sizeof(struct texifile));
1189:
1190: f->name = fname;
1191: if (-1 == (fd = open(fname, O_RDONLY, 0))) {
1192: texiabort(p, fname);
1193: } else if (-1 == fstat(fd, &st)) {
1194: close(fd);
1195: texiabort(p, fname);
1196: }
1197:
1198: f->mapsz = st.st_size;
1199: f->map = mmap(NULL, f->mapsz,
1200: PROT_READ, MAP_SHARED, fd, 0);
1201: close(fd);
1202:
1203: if (MAP_FAILED == f->map)
1204: texiabort(p, fname);
1205:
1206: p->filepos++;
1.16 kristaps 1207: if ( ! parse) {
1208: /*
1209: * We're printing verbatim output.
1210: * Make sure it doesn't get interpreted as mdoc by
1211: * escaping escapes and making sure leading dots don't
1212: * trigger mdoc(7) expansion.
1213: */
1214: for (i = 0; i < f->mapsz; i++) {
1215: if (i > 0 && '.' == f->map[i])
1216: if ('\n' == f->map[i - 1])
1217: fputs("\\&", stdout);
1218: putchar(f->map[i]);
1219: if ('\\' == f->map[i])
1220: putchar('e');
1221: }
1222: } else
1223: parseeof(p, f->map, f->mapsz);
1.2 kristaps 1224: texifilepop(p);
1225: }
1226:
1227: static void
1.15 kristaps 1228: dodefn(struct texi *p, enum texicmd cmd,
1.3 kristaps 1229: const char *buf, size_t sz, size_t *pos)
1230: {
1231: const char *blk;
1232:
1.5 kristaps 1233: blk = NULL;
1.3 kristaps 1234: switch (cmd) {
1.15 kristaps 1235: case (TEXICMD_DEFFN):
1236: case (TEXICMD_DEFTP):
1.3 kristaps 1237: case (TEXICMD_DEFTYPEFN):
1238: case (TEXICMD_DEFTYPEFUN):
1.15 kristaps 1239: case (TEXICMD_DEFTYPEVAR):
1240: case (TEXICMD_DEFTYPEVR):
1241: case (TEXICMD_DEFUN):
1242: case (TEXICMD_DEFVAR):
1243: case (TEXICMD_DEFVR):
1.5 kristaps 1244: blk = texitoks[cmd].tok;
1.3 kristaps 1245: break;
1.5 kristaps 1246: default:
1.3 kristaps 1247: break;
1248: }
1249:
1250: if (p->ign) {
1.15 kristaps 1251: NULL != blk ?
1252: parseto(p, buf, sz, pos, blk) :
1253: parseeoln(p, buf, sz, pos);
1.3 kristaps 1254: return;
1255: }
1256:
1.15 kristaps 1257: if (NULL != blk)
1258: texivspace(p);
1259:
1.3 kristaps 1260: switch (cmd) {
1.15 kristaps 1261: case (TEXICMD_DEFMAC):
1262: case (TEXICMD_DEFMACX):
1263: texiputchars(p, "Macro");
1264: break;
1265: case (TEXICMD_DEFTYPEVAR):
1266: case (TEXICMD_DEFTYPEVARX):
1267: case (TEXICMD_DEFVAR):
1268: case (TEXICMD_DEFVARX):
1269: texiputchars(p, "Variable");
1270: break;
1.3 kristaps 1271: case (TEXICMD_DEFTYPEFUN):
1.15 kristaps 1272: case (TEXICMD_DEFTYPEFUNX):
1273: case (TEXICMD_DEFUN):
1274: case (TEXICMD_DEFUNX):
1275: texiputchars(p, "Function");
1.3 kristaps 1276: break;
1277: default:
1.15 kristaps 1278: parselinearg(p, buf, sz, pos);
1.3 kristaps 1279: break;
1280: }
1.15 kristaps 1281:
1282: texiputchars(p, ":\n");
1283:
1284: switch (cmd) {
1285: case (TEXICMD_DEFMAC):
1286: case (TEXICMD_DEFMACX):
1287: teximacroopen(p, "Dv");
1288: while (parselinearg(p, buf, sz, pos))
1289: /* Spin. */ ;
1290: teximacroclose(p);
1291: break;
1292: case (TEXICMD_DEFFN):
1293: case (TEXICMD_DEFFNX):
1294: case (TEXICMD_DEFUN):
1295: case (TEXICMD_DEFUNX):
1296: teximacroopen(p, "Fo");
1297: parselinearg(p, buf, sz, pos);
1298: teximacroclose(p);
1299: teximacroopen(p, "Fa");
1300: while (parselinearg(p, buf, sz, pos))
1301: /* Spin. */ ;
1302: teximacroclose(p);
1303: teximacro(p, "Fc");
1304: break;
1305: case (TEXICMD_DEFTYPEFUN):
1306: case (TEXICMD_DEFTYPEFUNX):
1307: case (TEXICMD_DEFTYPEFN):
1308: case (TEXICMD_DEFTYPEFNX):
1309: teximacroopen(p, "Ft");
1310: parselinearg(p, buf, sz, pos);
1311: teximacroclose(p);
1312: teximacroopen(p, "Fo");
1313: parselinearg(p, buf, sz, pos);
1314: teximacroclose(p);
1315: teximacroopen(p, "Fa");
1316: while (parselinearg(p, buf, sz, pos))
1317: /* Spin. */ ;
1318: teximacroclose(p);
1319: teximacro(p, "Fc");
1320: break;
1321: case (TEXICMD_DEFTP):
1322: case (TEXICMD_DEFTPX):
1323: case (TEXICMD_DEFTYPEVAR):
1324: case (TEXICMD_DEFTYPEVARX):
1325: case (TEXICMD_DEFTYPEVR):
1326: case (TEXICMD_DEFTYPEVRX):
1327: teximacroopen(p, "Vt");
1328: while (parselinearg(p, buf, sz, pos))
1329: /* Spin. */ ;
1330: teximacroclose(p);
1331: break;
1332: case (TEXICMD_DEFVAR):
1333: case (TEXICMD_DEFVARX):
1334: case (TEXICMD_DEFVR):
1335: case (TEXICMD_DEFVRX):
1336: teximacroopen(p, "Va");
1337: while (parselinearg(p, buf, sz, pos))
1338: /* Spin. */ ;
1339: teximacroclose(p);
1340: break;
1341: default:
1342: abort();
1.3 kristaps 1343: }
1.15 kristaps 1344:
1.11 kristaps 1345: texivspace(p);
1.3 kristaps 1346: if (NULL != blk)
1347: parseto(p, buf, sz, pos, blk);
1348: }
1349:
1350: static void
1.1 kristaps 1351: doignblock(struct texi *p, enum texicmd cmd,
1352: const char *buf, size_t sz, size_t *pos)
1353: {
1354:
1.3 kristaps 1355: p->ign++;
1.5 kristaps 1356: parseto(p, buf, sz, pos, texitoks[cmd].tok);
1.3 kristaps 1357: p->ign--;
1.1 kristaps 1358: }
1359:
1360: static void
1.3 kristaps 1361: doblock(struct texi *p, enum texicmd cmd,
1.1 kristaps 1362: const char *buf, size_t sz, size_t *pos)
1363: {
1364:
1.5 kristaps 1365: parseto(p, buf, sz, pos, texitoks[cmd].tok);
1.1 kristaps 1366: }
1367:
1368: static void
1.12 kristaps 1369: doinline(struct texi *p, enum texicmd cmd,
1370: const char *buf, size_t sz, size_t *pos)
1.1 kristaps 1371: {
1.21 kristaps 1372: const char *macro = NULL;
1.12 kristaps 1373:
1374: switch (cmd) {
1.21 kristaps 1375: case (TEXICMD_CODE):
1376: case (TEXICMD_KBD):
1377: case (TEXICMD_SAMP):
1378: case (TEXICMD_T):
1379: macro = "Li";
1380: break;
1381: case (TEXICMD_CITE):
1382: case (TEXICMD_DFN):
1383: case (TEXICMD_EMPH):
1384: case (TEXICMD_I):
1385: case (TEXICMD_SLANTED):
1386: macro = "Em";
1387: break;
1388: case (TEXICMD_B):
1389: case (TEXICMD_STRONG):
1390: macro = "Sy";
1391: break;
1.12 kristaps 1392: case (TEXICMD_COMMAND):
1393: macro = "Xr";
1394: break;
1395: case (TEXICMD_ENV):
1396: macro = "Ev";
1397: break;
1398: case (TEXICMD_FILE):
1399: macro = "Pa";
1400: break;
1.16 kristaps 1401: case (TEXICMD_OPTION):
1402: macro = "Op";
1403: break;
1.12 kristaps 1404: case (TEXICMD_VAR):
1405: macro = "Va";
1406: break;
1407: default:
1.22 kristaps 1408: break;
1.12 kristaps 1409: }
1410:
1.21 kristaps 1411: if (NULL == macro || p->literal) {
1.12 kristaps 1412: parsebracket(p, buf, sz, pos);
1413: return;
1414: }
1.1 kristaps 1415:
1.5 kristaps 1416: teximacroopen(p, macro);
1.1 kristaps 1417: p->seenws = 0;
1418: parsebracket(p, buf, sz, pos);
1.11 kristaps 1419: texipunctuate(p, buf, sz, pos);
1.5 kristaps 1420: teximacroclose(p);
1.1 kristaps 1421: }
1422:
1423: static void
1.16 kristaps 1424: doverbinclude(struct texi *p, enum texicmd cmd,
1425: const char *buf, size_t sz, size_t *pos)
1426: {
1427: char fname[PATH_MAX], path[PATH_MAX];
1428: int rc;
1429: size_t i;
1430:
1431: while (*pos < sz && ' ' == buf[*pos])
1432: advance(p, buf, pos);
1433:
1434: /* Read in the filename. */
1435: for (i = 0; *pos < sz && '\n' != buf[*pos]; i++) {
1436: if (i == sizeof(fname) - 1)
1437: break;
1438: fname[i] = buf[*pos];
1439: advance(p, buf, pos);
1440: }
1441:
1442: if (i == 0)
1443: texierr(p, "path too short");
1444: else if ('\n' != buf[*pos])
1445: texierr(p, "path too long");
1446: else if ('/' == fname[0])
1447: texierr(p, "no absolute paths");
1448: fname[i] = '\0';
1449:
1450: if (strstr(fname, "../") || strstr(fname, "/.."))
1451: texierr(p, "insecure path");
1452:
1453: rc = snprintf(path, sizeof(path),
1454: "%s/%s", p->dirs[0], fname);
1455: if (rc < 0)
1456: texierr(p, "couldn't format path");
1457: else if ((size_t)rc >= sizeof(path))
1458: texierr(p, "path too long");
1459:
1460: parsefile(p, path, 0);
1461: }
1462:
1463: static void
1.2 kristaps 1464: doinclude(struct texi *p, enum texicmd cmd,
1465: const char *buf, size_t sz, size_t *pos)
1466: {
1467: char fname[PATH_MAX], path[PATH_MAX];
1468: size_t i;
1469: int rc;
1470:
1471: while (*pos < sz && ' ' == buf[*pos])
1472: advance(p, buf, pos);
1473:
1474: /* Read in the filename. */
1475: for (i = 0; *pos < sz && '\n' != buf[*pos]; i++) {
1476: if (i == sizeof(fname) - 1)
1477: break;
1478: fname[i] = buf[*pos];
1479: advance(p, buf, pos);
1480: }
1481:
1482: if (i == 0)
1483: texierr(p, "path too short");
1484: else if ('\n' != buf[*pos])
1485: texierr(p, "path too long");
1486: else if ('/' == fname[0])
1487: texierr(p, "no absolute paths");
1488: fname[i] = '\0';
1489:
1490: if (strstr(fname, "../") || strstr(fname, "/.."))
1491: texierr(p, "insecure path");
1492:
1.5 kristaps 1493: for (i = 0; i < p->dirsz; i++) {
1494: rc = snprintf(path, sizeof(path),
1495: "%s/%s", p->dirs[i], fname);
1496: if (rc < 0)
1497: texierr(p, "couldn't format path");
1498: else if ((size_t)rc >= sizeof(path))
1499: texierr(p, "path too long");
1500: else if (-1 == access(path, R_OK))
1501: continue;
1502:
1.16 kristaps 1503: parsefile(p, path, 1);
1.5 kristaps 1504: return;
1505: }
1.2 kristaps 1506:
1.5 kristaps 1507: texierr(p, "couldn't find %s in includes", fname);
1.2 kristaps 1508: }
1509:
1510: static void
1.1 kristaps 1511: dobracket(struct texi *p, enum texicmd cmd,
1512: const char *buf, size_t sz, size_t *pos)
1513: {
1514:
1515: parsebracket(p, buf, sz, pos);
1516: }
1517:
1518: static void
1.3 kristaps 1519: dodisplay(struct texi *p, enum texicmd cmd,
1520: const char *buf, size_t sz, size_t *pos)
1521: {
1522:
1.20 kristaps 1523: switch (cmd) {
1524: case (TEXICMD_FORMAT):
1525: case (TEXICMD_SMALLFORMAT):
1526: teximacro(p, "Bd -filled");
1527: break;
1528: default:
1529: teximacro(p, "Bd -filled -offset indent");
1530: break;
1531: }
1532:
1.11 kristaps 1533: p->seenvs = 1;
1.12 kristaps 1534: /* FIXME: ignore and parseeoln. */
1.3 kristaps 1535: advanceeoln(p, buf, sz, pos, 1);
1.13 kristaps 1536: parseto(p, buf, sz, pos, texitoks[cmd].tok);
1.5 kristaps 1537: teximacro(p, "Ed");
1.3 kristaps 1538: }
1539:
1540: static void
1.1 kristaps 1541: doexample(struct texi *p, enum texicmd cmd,
1542: const char *buf, size_t sz, size_t *pos)
1543: {
1544:
1.5 kristaps 1545: teximacro(p, "Bd -literal -offset indent");
1.12 kristaps 1546: /* FIXME: ignore and parseeoln. */
1.3 kristaps 1547: advanceeoln(p, buf, sz, pos, 1);
1548: p->literal++;
1.13 kristaps 1549: parseto(p, buf, sz, pos, texitoks[cmd].tok);
1.3 kristaps 1550: p->literal--;
1.5 kristaps 1551: teximacro(p, "Ed");
1.1 kristaps 1552: }
1553:
1554: static void
1555: dobye(struct texi *p, enum texicmd cmd,
1556: const char *buf, size_t sz, size_t *pos)
1557: {
1558:
1559: texiexit(p);
1560: exit(EXIT_SUCCESS);
1561: }
1562:
1563: static void
1.10 kristaps 1564: dotitle(struct texi *p, enum texicmd cmd,
1565: const char *buf, size_t sz, size_t *pos)
1566: {
1567: size_t start, end;
1568:
1569: while (*pos < sz && isws(buf[*pos]))
1570: advance(p, buf, pos);
1571: start = end = *pos;
1572: while (end < sz && '\n' != buf[end])
1573: end++;
1574: free(p->subtitle);
1575: p->subtitle = malloc(end - start + 1);
1576: memcpy(p->subtitle, &buf[start], end - start);
1577: p->subtitle[end - start] = '\0';
1578: }
1579:
1580: static void
1.16 kristaps 1581: doaccent(struct texi *p, enum texicmd cmd,
1582: const char *buf, size_t sz, size_t *pos)
1583: {
1584:
1585: if (*pos == sz)
1586: return;
1587: advance(p, buf, pos);
1588: switch (cmd) {
1589: case (TEXICMD_ACUTE):
1590: switch (buf[*pos]) {
1591: case ('a'): case ('A'):
1592: case ('e'): case ('E'):
1593: case ('i'): case ('I'):
1594: case ('o'): case ('O'):
1595: case ('u'): case ('U'):
1596: texiputchars(p, "\\(\'");
1597: texiputchar(p, buf[*pos]);
1598: break;
1599: default:
1600: texiputchar(p, buf[*pos]);
1601: }
1602: break;
1603: case (TEXICMD_CIRCUMFLEX):
1604: switch (buf[*pos]) {
1605: case ('a'): case ('A'):
1606: case ('e'): case ('E'):
1607: case ('i'): case ('I'):
1608: case ('o'): case ('O'):
1609: case ('u'): case ('U'):
1610: texiputchars(p, "\\(^");
1611: texiputchar(p, buf[*pos]);
1612: break;
1613: default:
1614: texiputchar(p, buf[*pos]);
1615: }
1616: break;
1617: case (TEXICMD_GRAVE):
1618: switch (buf[*pos]) {
1619: case ('a'): case ('A'):
1620: case ('e'): case ('E'):
1621: case ('i'): case ('I'):
1622: case ('o'): case ('O'):
1623: case ('u'): case ('U'):
1624: texiputchars(p, "\\(`");
1625: texiputchar(p, buf[*pos]);
1626: break;
1627: default:
1628: texiputchar(p, buf[*pos]);
1629: }
1630: break;
1631: case (TEXICMD_TILDE):
1632: switch (buf[*pos]) {
1633: case ('a'): case ('A'):
1634: case ('n'): case ('N'):
1635: case ('o'): case ('O'):
1636: texiputchars(p, "\\(~");
1637: texiputchar(p, buf[*pos]);
1638: break;
1639: default:
1640: texiputchar(p, buf[*pos]);
1641: }
1642: break;
1643: case (TEXICMD_UMLAUT):
1644: switch (buf[*pos]) {
1645: case ('a'): case ('A'):
1646: case ('e'): case ('E'):
1647: case ('i'): case ('I'):
1648: case ('o'): case ('O'):
1649: case ('u'): case ('U'):
1650: case ('y'):
1651: texiputchars(p, "\\(:");
1652: texiputchar(p, buf[*pos]);
1653: break;
1654: default:
1655: texiputchar(p, buf[*pos]);
1656: }
1657: break;
1658: default:
1659: abort();
1660: }
1661: }
1662:
1663: static void
1.1 kristaps 1664: dosymbol(struct texi *p, enum texicmd cmd,
1665: const char *buf, size_t sz, size_t *pos)
1666: {
1667:
1.3 kristaps 1668: if (p->seenws && p->outcol && 0 == p->literal) {
1669: texiputchar(p, ' ');
1670: p->seenws = 0;
1671: }
1672:
1.1 kristaps 1673: switch (cmd) {
1.3 kristaps 1674: case (TEXICMD_ASTERISK):
1675: case (TEXICMD_NEWLINE):
1676: case (TEXICMD_SPACE):
1.18 kristaps 1677: case (TEXICMD_TABSYM):
1.3 kristaps 1678: texiputchar(p, ' ');
1679: break;
1.1 kristaps 1680: case (TEXICMD_AT):
1.3 kristaps 1681: texiputchar(p, '@');
1682: break;
1683: case (TEXICMD_BANG):
1684: texiputchar(p, '!');
1.7 kristaps 1685: break;
1686: case (TEXICMD_BULLET):
1687: texiputchars(p, "\\(bu");
1.1 kristaps 1688: break;
1689: case (TEXICMD_COPYRIGHT):
1690: texiputchars(p, "\\(co");
1691: break;
1.2 kristaps 1692: case (TEXICMD_DOTS):
1693: texiputchars(p, "...");
1694: break;
1.15 kristaps 1695: case (TEXICMD_ERROR):
1696: texiputchars(p, "error\\(->");
1.17 kristaps 1697: break;
1698: case (TEXICMD_EXPANSION):
1699: texiputchars(p, "\\(->");
1.15 kristaps 1700: break;
1.1 kristaps 1701: case (TEXICMD_LATEX):
1702: texiputchars(p, "LaTeX");
1703: break;
1.3 kristaps 1704: case (TEXICMD_QUESTIONMARK):
1705: texiputchar(p, '?');
1.15 kristaps 1706: break;
1707: case (TEXICMD_RESULT):
1708: texiputchars(p, "\\(rA");
1.3 kristaps 1709: break;
1710: case (TEXICMD_SQUIGGLE_LEFT):
1711: texiputchars(p, "{");
1712: break;
1713: case (TEXICMD_SQUIGGLE_RIGHT):
1714: texiputchars(p, "}");
1715: break;
1.1 kristaps 1716: case (TEXICMD_TEXSYM):
1717: texiputchars(p, "TeX");
1718: break;
1.3 kristaps 1719: case (TEXICMD_COLON):
1720: case (TEXICMD_HYPHEN):
1721: break;
1.1 kristaps 1722: default:
1.5 kristaps 1723: texiwarn(p, "sym: %d", cmd);
1.1 kristaps 1724: abort();
1725: }
1726:
1.5 kristaps 1727: if (texitoks[cmd].len > 1)
1728: doignbracket(p, cmd, buf, sz, pos);
1.1 kristaps 1729: }
1730:
1731: static void
1732: doquotation(struct texi *p, enum texicmd cmd,
1733: const char *buf, size_t sz, size_t *pos)
1734: {
1735:
1.5 kristaps 1736: teximacro(p, "Qo");
1.1 kristaps 1737: parseto(p, buf, sz, pos, "quotation");
1.5 kristaps 1738: teximacro(p, "Qc");
1.1 kristaps 1739: }
1740:
1.3 kristaps 1741: static void
1742: domath(struct texi *p, enum texicmd cmd,
1743: const char *buf, size_t sz, size_t *pos)
1744: {
1745: size_t nest;
1746:
1747: /*
1748: * Math handling is different from everything else.
1749: * We don't allow any subcomponents, and we ignore the rules in
1750: * terms of @-commands.
1751: * This departs from GNU's rules, but whatever.
1752: */
1753: while (*pos < sz && isws(buf[*pos]))
1754: advance(p, buf, pos);
1755: if (*pos == sz || '{' != buf[*pos])
1756: return;
1757: advance(p, buf, pos);
1758: if (p->seenws && p->outcol && 0 == p->literal)
1759: texiputchar(p, ' ');
1760: p->seenws = 0;
1761: for (nest = 1; *pos < sz && nest > 0; ) {
1762: if ('{' == buf[*pos])
1763: nest++;
1764: else if ('}' == buf[*pos])
1765: if (0 == --nest)
1766: continue;
1767: texiputchar(p, buf[*pos]);
1768: advance(p, buf, pos);
1769: }
1770: if (*pos == sz)
1771: return;
1772: assert('}' == buf[*pos]);
1773: advance(p, buf, pos);
1774: }
1775:
1.1 kristaps 1776: static void
1.8 kristaps 1777: dolink(struct texi *p, enum texicmd cmd,
1.1 kristaps 1778: const char *buf, size_t sz, size_t *pos)
1779: {
1.8 kristaps 1780: int c;
1.1 kristaps 1781:
1782: switch (cmd) {
1783: case (TEXICMD_EMAIL):
1.5 kristaps 1784: teximacroopen(p, "Mt");
1.1 kristaps 1785: break;
1.3 kristaps 1786: case (TEXICMD_UREF):
1.1 kristaps 1787: case (TEXICMD_URL):
1.5 kristaps 1788: teximacroopen(p, "Lk");
1.1 kristaps 1789: break;
1.8 kristaps 1790: case (TEXICMD_XREF):
1791: texiputchars(p, "See Section");
1792: teximacroopen(p, "Qq");
1793: break;
1794: case (TEXICMD_PXREF):
1795: texiputchars(p, "see Section");
1796: teximacroopen(p, "Qq");
1797: break;
1.1 kristaps 1798: default:
1.8 kristaps 1799: abort();
1.1 kristaps 1800: }
1.8 kristaps 1801:
1802: c = parsearg(p, buf, sz, pos, 0);
1803: p->ign++;
1804: while (c > 0)
1805: c = parsearg(p, buf, sz, pos, 1);
1806: p->ign--;
1807:
1.11 kristaps 1808: texipunctuate(p, buf, sz, pos);
1.8 kristaps 1809: teximacroclose(p);
1810: }
1811:
1812: static void
1813: doignargn(struct texi *p, enum texicmd cmd,
1814: const char *buf, size_t sz, size_t *pos)
1815: {
1816: int c;
1817:
1818: c = parsearg(p, buf, sz, pos, 0);
1819: p->ign++;
1820: while (c > 0)
1821: c = parsearg(p, buf, sz, pos, 1);
1822: p->ign--;
1.1 kristaps 1823: }
1824:
1.23 ! kristaps 1825: /*
! 1826: * Sections can be made subsections and so on by way of the
! 1827: * @raiseections and @lowersections commands.
! 1828: * Perform this check here and return the actual section number adjusted
! 1829: * to the raise level.
! 1830: */
! 1831: static int
! 1832: sectioner(struct texi *p, int sec)
! 1833: {
! 1834:
! 1835: if ((sec -= p->secoffs) < 0) {
! 1836: texiwarn(p, "section below minimum, clamping");
! 1837: return(0);
! 1838: } else if (sec >= SECTSZ) {
! 1839: texiwarn(p, "section above maximum, clamping");
! 1840: return(SECTSZ - 1);
! 1841: }
! 1842: return(sec);
! 1843: }
! 1844:
! 1845: static void
! 1846: dosubsubsection(struct texi *p, enum texicmd cmd,
! 1847: const char *buf, size_t sz, size_t *pos)
! 1848: {
! 1849: int sec;
! 1850:
! 1851: sec = sectioner(p, 3);
! 1852:
! 1853: /* We don't have a subsubsubsection, so make one up. */
! 1854: texivspace(p);
! 1855: teximacroopen(p, sects[sec]);
! 1856: parseeoln(p, buf, sz, pos);
! 1857: teximacroclose(p);
! 1858: texivspace(p);
! 1859: }
! 1860:
1.1 kristaps 1861: static void
1862: dosubsection(struct texi *p, enum texicmd cmd,
1863: const char *buf, size_t sz, size_t *pos)
1864: {
1.23 ! kristaps 1865: int sec;
! 1866:
! 1867: sec = sectioner(p, 2);
1.13 kristaps 1868:
1869: if (p->outmacro)
1.23 ! kristaps 1870: texierr(p, "\"%s\" in open line scope!?", sects[sec]);
1.13 kristaps 1871: else if (p->literal)
1.23 ! kristaps 1872: texierr(p, "\"%s\" in a literal scope!?", sects[sec]);
1.1 kristaps 1873:
1.21 kristaps 1874: /* We don't have a subsubsection, so make one up. */
1.11 kristaps 1875: texivspace(p);
1.23 ! kristaps 1876: teximacroopen(p, sects[sec]);
1.3 kristaps 1877: parseeoln(p, buf, sz, pos);
1.5 kristaps 1878: teximacroclose(p);
1.11 kristaps 1879: texivspace(p);
1.1 kristaps 1880: }
1881:
1882: static void
1.23 ! kristaps 1883: dosecoffs(struct texi *p, enum texicmd cmd,
! 1884: const char *buf, size_t sz, size_t *pos)
! 1885: {
! 1886:
! 1887: if (TEXICMD_RAISESECTIONS == cmd)
! 1888: p->secoffs++;
! 1889: else
! 1890: p->secoffs--;
! 1891: }
! 1892:
! 1893: static void
1.1 kristaps 1894: dosection(struct texi *p, enum texicmd cmd,
1.23 ! kristaps 1895: const char *buf, size_t sz, size_t *pos)
1.1 kristaps 1896: {
1.23 ! kristaps 1897: int sec;
1.12 kristaps 1898:
1899: switch (cmd) {
1900: case (TEXICMD_APPENDIX):
1901: case (TEXICMD_CHAPTER):
1902: case (TEXICMD_TOP):
1903: case (TEXICMD_UNNUMBERED):
1.23 ! kristaps 1904: sec = sectioner(p, 0);
1.12 kristaps 1905: break;
1906: case (TEXICMD_APPENDIXSEC):
1907: case (TEXICMD_HEADING):
1908: case (TEXICMD_SECTION):
1909: case (TEXICMD_UNNUMBEREDSEC):
1.23 ! kristaps 1910: sec = sectioner(p, 1);
1.12 kristaps 1911: break;
1912: default:
1913: abort();
1914: }
1.1 kristaps 1915:
1.3 kristaps 1916: if (p->outmacro)
1.23 ! kristaps 1917: texierr(p, "\"%s\" in open line scope!?", sects[sec]);
1.3 kristaps 1918: else if (p->literal)
1.23 ! kristaps 1919: texierr(p, "\"%s\" in a literal scope!?", sects[sec]);
1.3 kristaps 1920:
1.23 ! kristaps 1921: teximacroopen(p, sects[sec]);
1.3 kristaps 1922: parseeoln(p, buf, sz, pos);
1923: teximacroclose(p);
1.11 kristaps 1924: p->seenvs = 1;
1.3 kristaps 1925: }
1926:
1927: static void
1928: dosp(struct texi *p, enum texicmd cmd,
1929: const char *buf, size_t sz, size_t *pos)
1930: {
1931:
1.11 kristaps 1932: texivspace(p);
1.12 kristaps 1933: /* FIXME: ignore and parseeoln. */
1.3 kristaps 1934: advanceeoln(p, buf, sz, pos, 1);
1.1 kristaps 1935: }
1936:
1937: static void
1938: dotop(struct texi *p, enum texicmd cmd,
1939: const char *buf, size_t sz, size_t *pos)
1940: {
1.10 kristaps 1941: const char *cp;
1942: time_t t;
1943: char date[32];
1944:
1.18 kristaps 1945: /*
1946: * Here we print our standard mdoc(7) prologue.
1947: * We use the title set with @settitle for the `Nd' description
1948: * and the source document filename (the first one as invoked on
1949: * the command line) for the title.
1950: * The date is set to the current date.
1951: */
1.10 kristaps 1952: t = time(NULL);
1953: strftime(date, sizeof(date), "%F", localtime(&t));
1.1 kristaps 1954:
1.3 kristaps 1955: p->ign--;
1.10 kristaps 1956: teximacroopen(p, "Dd");
1957: texiputchars(p, date);
1958: teximacroclose(p);
1959: teximacroopen(p, "Dt");
1960: for (cp = p->title; '\0' != *cp; cp++)
1961: texiputchar(p, toupper(*cp));
1.11 kristaps 1962: texiputchars(p, " 7");
1.10 kristaps 1963: teximacroclose(p);
1.5 kristaps 1964: teximacro(p, "Os");
1965: teximacro(p, "Sh NAME");
1.10 kristaps 1966: teximacroopen(p, "Nm");
1967: texiputchars(p, p->title);
1968: teximacroclose(p);
1969: teximacroopen(p, "Nd");
1970: texiputchars(p, NULL != p->subtitle ?
1971: p->subtitle : "Unknown description");
1972: teximacroclose(p);
1.11 kristaps 1973: p->seenvs = 1;
1.12 kristaps 1974: dosection(p, cmd, buf, sz, pos);
1.1 kristaps 1975: }
1976:
1977: static void
1978: doitem(struct texi *p, enum texicmd cmd,
1979: const char *buf, size_t sz, size_t *pos)
1980: {
1981:
1.18 kristaps 1982: /* Multitable is using raw tbl(7). */
1983: if (TEXILIST_TABLE == p->list) {
1984: texiputchar(p, '\n');
1985: return;
1986: }
1987:
1.3 kristaps 1988: if (p->outmacro)
1989: texierr(p, "item in open line scope!?");
1990: else if (p->literal)
1991: texierr(p, "item in a literal scope!?");
1992:
1993: switch (p->list) {
1994: case (TEXILIST_ITEM):
1.5 kristaps 1995: teximacroopen(p, "It");
1.3 kristaps 1996: break;
1997: case (TEXILIST_NOITEM):
1.5 kristaps 1998: teximacro(p, "It");
1.3 kristaps 1999: break;
2000: default:
1.11 kristaps 2001: texivspace(p);
1.3 kristaps 2002: break;
2003: }
1.18 kristaps 2004:
2005: /* Trick so we don't start with Pp. */
1.11 kristaps 2006: p->seenvs = 1;
1.3 kristaps 2007: parseeoln(p, buf, sz, pos);
1.1 kristaps 2008:
1.3 kristaps 2009: if (TEXILIST_ITEM == p->list)
2010: teximacroclose(p);
1.9 kristaps 2011: else if (p->outcol > 0)
1.1 kristaps 2012: texiputchar(p, '\n');
1.18 kristaps 2013: }
2014:
2015: static void
2016: dotab(struct texi *p, enum texicmd cmd,
2017: const char *buf, size_t sz, size_t *pos)
2018: {
2019:
2020: /* This command is only useful in @multitable. */
2021: if (TEXILIST_TABLE == p->list)
2022: texiputchar(p, '\t');
2023: }
2024:
2025: static void
2026: domultitable(struct texi *p, enum texicmd cmd,
2027: const char *buf, size_t sz, size_t *pos)
2028: {
2029: enum texilist sv = p->list;
2030: enum texicmd type;
2031: size_t i, end, columns;
2032:
2033: p->list = TEXILIST_TABLE;
2034: teximacro(p, "TS");
2035: columns = 0;
2036:
2037: /* Advance to the first argument... */
2038: while (*pos < sz && isws(buf[*pos]))
2039: advance(p, buf, pos);
2040:
2041: /* Make sure we don't print anything when scanning. */
2042: p->ign++;
2043: if ('@' == buf[*pos]) {
2044: /*
2045: * Look for @columnfractions.
2046: * We ignore these, but we do use the number of
2047: * arguments to set the number of columns that we'll
2048: * have.
2049: */
2050: type = texicmd(p, buf, *pos, sz, &end);
2051: advanceto(p, buf, pos, end);
2052: if (TEXICMD_COLUMNFRACTIONS != type)
2053: texierr(p, "unknown multitable type");
2054: while (*pos < sz && '\n' != buf[*pos]) {
2055: while (*pos < sz && isws(buf[*pos]))
2056: advance(p, buf, pos);
2057: while (*pos < sz && ! isws(buf[*pos])) {
2058: if ('\n' == buf[*pos])
2059: break;
2060: advance(p, buf, pos);
2061: }
2062: columns++;
2063: }
2064: } else
2065: /*
2066: * We have arguments.
2067: * We could parse these, but it's easier to just let
2068: * tbl(7) figure it out.
2069: * So use this only to count arguments.
2070: */
2071: while (parselinearg(p, buf, sz, pos) > 0)
2072: columns++;
2073: p->ign--;
2074:
2075: /* Left-justify each table entry. */
2076: for (i = 0; i < columns; i++) {
2077: if (i > 0)
2078: texiputchar(p, ' ');
2079: texiputchar(p, 'l');
2080: }
2081: texiputchars(p, ".\n");
2082: p->outmacro++;
2083: parseto(p, buf, sz, pos, texitoks[cmd].tok);
2084: p->outmacro--;
2085: teximacro(p, "TE");
2086: p->list = sv;
1.1 kristaps 2087: }
2088:
2089: static void
2090: dotable(struct texi *p, enum texicmd cmd,
2091: const char *buf, size_t sz, size_t *pos)
2092: {
1.3 kristaps 2093: enum texilist sv = p->list;
2094:
2095: p->list = TEXILIST_ITEM;
1.5 kristaps 2096: teximacro(p, "Bl -tag -width Ds");
1.12 kristaps 2097: /* FIXME: ignore and parseeoln. */
2098: advanceeoln(p, buf, sz, pos, 1);
1.11 kristaps 2099: p->seenvs = 1;
1.20 kristaps 2100: parseto(p, buf, sz, pos, texitoks[cmd].tok);
1.5 kristaps 2101: teximacro(p, "El");
1.3 kristaps 2102: p->list = sv;
1.1 kristaps 2103: }
2104:
2105: static void
1.2 kristaps 2106: doenumerate(struct texi *p, enum texicmd cmd,
2107: const char *buf, size_t sz, size_t *pos)
2108: {
1.3 kristaps 2109: enum texilist sv = p->list;
1.2 kristaps 2110:
1.3 kristaps 2111: p->list = TEXILIST_NOITEM;
1.5 kristaps 2112: teximacro(p, "Bl -enum");
1.11 kristaps 2113: p->seenvs = 1;
1.12 kristaps 2114: /* FIXME: ignore and parseeoln. */
2115: advanceeoln(p, buf, sz, pos, 1);
1.2 kristaps 2116: parseto(p, buf, sz, pos, "enumerate");
1.5 kristaps 2117: teximacro(p, "El");
1.3 kristaps 2118: p->list = sv;
1.2 kristaps 2119: }
2120:
2121: static void
1.1 kristaps 2122: doitemize(struct texi *p, enum texicmd cmd,
2123: const char *buf, size_t sz, size_t *pos)
2124: {
1.3 kristaps 2125: enum texilist sv = p->list;
1.1 kristaps 2126:
1.21 kristaps 2127: p->list = TEXILIST_NOITEM;
1.5 kristaps 2128: teximacro(p, "Bl -bullet");
1.11 kristaps 2129: p->seenvs = 1;
1.12 kristaps 2130: /* FIXME: ignore and parseeoln. */
2131: advanceeoln(p, buf, sz, pos, 1);
1.1 kristaps 2132: parseto(p, buf, sz, pos, "itemize");
1.5 kristaps 2133: teximacro(p, "El");
1.3 kristaps 2134: p->list = sv;
1.1 kristaps 2135: }
2136:
2137: static void
2138: doignbracket(struct texi *p, enum texicmd cmd,
2139: const char *buf, size_t sz, size_t *pos)
2140: {
2141:
1.3 kristaps 2142: p->ign++;
1.1 kristaps 2143: parsebracket(p, buf, sz, pos);
1.3 kristaps 2144: p->ign--;
1.1 kristaps 2145: }
2146:
2147: static void
2148: doignline(struct texi *p, enum texicmd cmd,
2149: const char *buf, size_t sz, size_t *pos)
2150: {
2151:
1.12 kristaps 2152: /* FIXME: ignore and parseeoln. */
1.3 kristaps 2153: advanceeoln(p, buf, sz, pos, 1);
1.1 kristaps 2154: }
2155:
1.8 kristaps 2156: /*
2157: * Parse colon-separated directories from "cp" (if not NULL) and returns
2158: * the array of pointers.
2159: * Prepends "base" to the array.
2160: * This does NOT sanitise the directories!
2161: */
1.5 kristaps 2162: static char **
2163: parsedirs(const char *base, const char *cp, size_t *sz)
2164: {
2165: char *tok, *str, *tofree;
2166: const char *cpp;
2167: size_t i;
2168: char **dirs;
2169:
2170: *sz = NULL != (cpp = cp) ? 2 : 1;
2171: if (*sz > 1)
2172: for ( ; NULL != (cpp = strchr(cpp, ':')); (*sz)++)
2173: cpp++;
2174:
2175: dirs = calloc(*sz, sizeof(char *));
2176: if (NULL == dirs) {
2177: perror(NULL);
2178: exit(EXIT_FAILURE);
2179: } else if (NULL == (dirs[0] = strdup(base))) {
2180: perror(NULL);
2181: exit(EXIT_FAILURE);
2182: }
2183:
2184: if (NULL == cp)
2185: return(dirs);
2186:
2187: if (NULL == (tofree = tok = str = strdup(cp))) {
2188: perror(NULL);
2189: exit(EXIT_FAILURE);
2190: }
2191:
2192: for (i = 1; NULL != (tok = strsep(&str, ":")); i++)
2193: if (NULL == (dirs[i] = strdup(tok))) {
2194: perror(NULL);
2195: exit(EXIT_FAILURE);
2196: }
2197:
2198: free(tofree);
2199: return(dirs);
2200: }
2201:
1.1 kristaps 2202: int
2203: main(int argc, char *argv[])
2204: {
2205: struct texi texi;
1.2 kristaps 2206: int c;
2207: char *path, *dir;
1.10 kristaps 2208: const char *progname, *Idir, *cp;
1.1 kristaps 2209:
2210: progname = strrchr(argv[0], '/');
2211: if (progname == NULL)
2212: progname = argv[0];
2213: else
2214: ++progname;
2215:
1.10 kristaps 2216: memset(&texi, 0, sizeof(struct texi));
1.5 kristaps 2217: Idir = NULL;
1.10 kristaps 2218:
1.5 kristaps 2219: while (-1 != (c = getopt(argc, argv, "I:")))
1.1 kristaps 2220: switch (c) {
1.5 kristaps 2221: case ('I'):
2222: Idir = optarg;
2223: break;
1.1 kristaps 2224: default:
2225: goto usage;
2226: }
2227:
2228: argv += optind;
2229: if (0 == (argc -= optind))
2230: goto usage;
2231:
1.2 kristaps 2232: if (NULL == (path = strdup(argv[0]))) {
2233: perror(NULL);
2234: exit(EXIT_FAILURE);
2235: } else if (NULL == (dir = dirname(path))) {
2236: perror(argv[0]);
2237: free(path);
2238: exit(EXIT_FAILURE);
2239: }
2240: free(path);
2241:
1.10 kristaps 2242: if (NULL != (cp = strrchr(argv[0], '/')))
2243: texi.title = strdup(cp + 1);
2244: else
2245: texi.title = strdup(argv[0]);
2246:
2247: if (NULL == texi.title) {
2248: perror(NULL);
2249: exit(EXIT_FAILURE);
2250: } else if (NULL != (path = strchr(texi.title, '.')))
2251: *path = '\0';
2252:
1.3 kristaps 2253: texi.ign = 1;
1.5 kristaps 2254: texi.dirs = parsedirs(dir, Idir, &texi.dirsz);
1.16 kristaps 2255: parsefile(&texi, argv[0], 1);
1.5 kristaps 2256: /* We shouldn't get here. */
1.2 kristaps 2257: texiexit(&texi);
2258: return(EXIT_FAILURE);
1.1 kristaps 2259: usage:
1.8 kristaps 2260: fprintf(stderr, "usage: %s [-Idirs] file\n", progname);
1.1 kristaps 2261: return(EXIT_FAILURE);
2262: }
CVSweb