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