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