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