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