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