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