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