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