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