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