Annotation of texi2mdoc/main.c, Revision 1.3
1.3 ! kristaps 1: /* $Id: main.c,v 1.2 2015/02/17 10:27:18 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.3 ! kristaps 470: if (p->ign)
1.1 kristaps 471: return;
472: if (p->outcol)
473: texiputchar(p, '\n');
474: puts(s);
475: p->outcol = 0;
476: p->seenws = 0;
477: }
478:
479: /*
480: * Advance by a single byte in the input stream.
481: */
482: static void
483: advance(struct texi *p, const char *buf, size_t *pos)
484: {
485:
486: if ('\n' == buf[*pos]) {
487: p->files[p->filepos - 1].line++;
488: p->files[p->filepos - 1].col = 0;
489: } else
490: p->files[p->filepos - 1].col++;
491:
492: (*pos)++;
493: }
494:
495: /*
496: * Advance to the next non-whitespace word in the input stream.
497: * If we're in literal mode, then print all of the whitespace as we're
498: * doing so.
499: */
500: static size_t
501: advancenext(struct texi *p, const char *buf, size_t sz, size_t *pos)
502: {
503:
1.3 ! kristaps 504: if (p->literal) {
1.1 kristaps 505: while (*pos < sz && isspace(buf[*pos])) {
506: texiputchar(p, buf[*pos]);
507: advance(p, buf, pos);
508: }
509: return(*pos);
510: }
511:
512: while (*pos < sz && isspace(buf[*pos])) {
513: p->seenws = 1;
514: /*
515: * If it looks like we've printed a double-line, then
516: * output a paragraph.
517: * FIXME: this is stupid.
518: */
519: if (*pos && '\n' == buf[*pos] && '\n' == buf[*pos - 1])
520: teximacro(p, ".Pp");
521: advance(p, buf, pos);
522: }
523: return(*pos);
524: }
525:
526: /*
527: * Advance to the EOLN in the input stream.
528: */
529: static size_t
1.3 ! kristaps 530: advanceeoln(struct texi *p, const char *buf,
! 531: size_t sz, size_t *pos, int consumenl)
1.1 kristaps 532: {
533:
534: while (*pos < sz && '\n' != buf[*pos])
535: advance(p, buf, pos);
1.3 ! kristaps 536: if (*pos < sz && consumenl)
! 537: advance(p, buf, pos);
1.1 kristaps 538: return(*pos);
539: }
540:
541: /*
542: * Advance to position "end", which is an absolute position in the
543: * current buffer greater than or equal to the current position.
544: */
545: static void
546: advanceto(struct texi *p, const char *buf, size_t *pos, size_t end)
547: {
548:
549: assert(*pos <= end);
550: while (*pos < end)
551: advance(p, buf, pos);
552: }
553:
554: /*
555: * Output a free-form word in the input stream, progressing to the next
556: * command or white-space.
557: * This also will advance the input stream.
558: */
559: static void
560: texiword(struct texi *p, const char *buf, size_t sz, size_t *pos)
561: {
562:
563: /*
564: * XXX: if we're in literal mode, then we shouldn't do any
565: * reflowing of text here.
566: */
1.3 ! kristaps 567: if (0 == p->outmacro && p->outcol > 72 && 0 == p->literal)
1.1 kristaps 568: texiputchar(p, '\n');
569:
1.3 ! kristaps 570: if (p->seenws && p->outcol && 0 == p->literal)
1.1 kristaps 571: texiputchar(p, ' ');
572:
573: p->seenws = 0;
574:
575: while (*pos < sz && ! isspace(buf[*pos])) {
576: switch (buf[*pos]) {
577: case ('@'):
578: case ('}'):
579: case ('{'):
580: return;
581: }
582: if (*pos < sz - 1 &&
583: '`' == buf[*pos] &&
584: '`' == buf[*pos + 1]) {
585: texiputchars(p, "\\(lq");
586: advance(p, buf, pos);
587: } else if (*pos < sz - 1 &&
588: '\'' == buf[*pos] &&
589: '\'' == buf[*pos + 1]) {
590: texiputchars(p, "\\(rq");
591: advance(p, buf, pos);
592: } else
593: texiputchar(p, buf[*pos]);
594: advance(p, buf, pos);
595: }
596: }
597:
598: static enum texicmd
599: texicmd(struct texi *p, const char *buf,
600: size_t pos, size_t sz, size_t *end)
601: {
602: size_t i, len;
603:
604: assert('@' == buf[pos]);
1.3 ! kristaps 605:
! 606: if (++pos >= sz)
! 607: return(TEXICMD__MAX);
! 608:
! 609: /* Alphabetic commands are special. */
! 610: if ( ! isalpha(buf[pos])) {
! 611: *end = pos + 1;
! 612: for (i = 0; i < TEXICMD__MAX; i++) {
! 613: if (1 != texitoks[i].len)
! 614: continue;
! 615: if (0 == strncmp(texitoks[i].tok, &buf[pos], 1))
! 616: return(i);
! 617: }
! 618: texiwarn(p, "bad command: @%c", buf[pos]);
! 619: return(TEXICMD__MAX);
! 620: }
! 621:
! 622: for (*end = pos; *end < sz && ! isspace(buf[*end]); (*end)++)
! 623: if ((*end > pos && ('@' == buf[*end] ||
! 624: '{' == buf[*end] || '}' == buf[*end])))
1.1 kristaps 625: break;
626:
627: len = *end - pos;
628: for (i = 0; i < TEXICMD__MAX; i++) {
629: if (len != texitoks[i].len)
630: continue;
631: if (0 == strncmp(texitoks[i].tok, &buf[pos], len))
632: return(i);
633: }
634:
1.3 ! kristaps 635: texiwarn(p, "bad command: @%.*s", (int)len, &buf[pos]);
1.1 kristaps 636: return(TEXICMD__MAX);
637: }
638:
639: static void
640: parseeof(struct texi *p, const char *buf, size_t sz)
641: {
642: size_t pos = 0;
643: enum texicmd cmd;
644: size_t end;
645:
646: while ((pos = advancenext(p, buf, sz, &pos)) < sz) {
647: switch (buf[pos]) {
648: case ('}'):
1.3 ! kristaps 649: if (0 == p->ign)
! 650: texiwarn(p, "unexpected \"}\"");
1.1 kristaps 651: advance(p, buf, &pos);
652: continue;
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 ('@'):
659: break;
660: default:
661: texiword(p, buf, sz, &pos);
662: continue;
663: }
664:
665: cmd = texicmd(p, buf, pos, sz, &end);
666: advanceto(p, buf, &pos, end);
667: if (TEXICMD__MAX == cmd)
668: continue;
669: if (NULL != texitoks[cmd].fp)
670: (*texitoks[cmd].fp)(p, cmd, buf, sz, &pos);
671: }
672: }
673:
674: static void
675: parsebracket(struct texi *p, const char *buf, size_t sz, size_t *pos)
676: {
677: size_t end;
678: enum texicmd cmd;
679:
1.3 ! kristaps 680: while (*pos < sz && isspace(buf[*pos]))
! 681: advance(p, buf, pos);
! 682:
1.1 kristaps 683: if (*pos == sz || '{' != buf[*pos])
684: return;
685: advance(p, buf, pos);
686:
687: while ((*pos = advancenext(p, buf, sz, pos)) < sz) {
688: switch (buf[*pos]) {
689: case ('}'):
690: advance(p, buf, pos);
691: return;
692: case ('{'):
1.3 ! kristaps 693: if (0 == p->ign)
! 694: texiwarn(p, "unexpected \"{\"");
! 695: advance(p, buf, pos);
! 696: continue;
! 697: case ('@'):
! 698: break;
! 699: default:
! 700: texiword(p, buf, sz, pos);
! 701: continue;
! 702: }
! 703:
! 704: cmd = texicmd(p, buf, *pos, sz, &end);
! 705: advanceto(p, buf, pos, end);
! 706: if (TEXICMD__MAX == cmd)
! 707: continue;
! 708: if (NULL != texitoks[cmd].fp)
! 709: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
! 710: }
! 711: }
! 712:
! 713: /*
! 714: * This should be invoked when we're on a macro line and want to process
! 715: * to the end of the current input line, doing all of our macros along
! 716: * the way.
! 717: */
! 718: static void
! 719: parseeoln(struct texi *p, const char *buf, size_t sz, size_t *pos)
! 720: {
! 721: size_t end;
! 722: enum texicmd cmd;
! 723:
! 724: assert(0 == p->literal);
! 725:
! 726: while (*pos < sz && '\n' != buf[*pos]) {
! 727: while (*pos < sz && isws(buf[*pos])) {
! 728: p->seenws = 1;
! 729: advance(p, buf, pos);
! 730: }
! 731: switch (buf[*pos]) {
! 732: case ('}'):
! 733: if (0 == p->ign)
! 734: texiwarn(p, "unexpected \"}\"");
! 735: advance(p, buf, pos);
! 736: continue;
! 737: case ('{'):
! 738: if (0 == p->ign)
! 739: texiwarn(p, "unexpected \"{\"");
1.1 kristaps 740: advance(p, buf, pos);
741: continue;
742: case ('@'):
743: break;
744: default:
745: texiword(p, buf, sz, pos);
746: continue;
747: }
748:
749: cmd = texicmd(p, buf, *pos, sz, &end);
750: advanceto(p, buf, pos, end);
751: if (TEXICMD__MAX == cmd)
752: continue;
753: if (NULL != texitoks[cmd].fp)
754: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
755: }
756: }
757:
758: static void
1.3 ! kristaps 759: parsesingle(struct texi *p, const char *buf, size_t sz, size_t *pos)
! 760: {
! 761: size_t end;
! 762: enum texicmd cmd;
! 763:
! 764: if ((*pos = advancenext(p, buf, sz, pos)) >= sz)
! 765: return;
! 766:
! 767: switch (buf[*pos]) {
! 768: case ('}'):
! 769: if (0 == p->ign)
! 770: texiwarn(p, "unexpected \"}\"");
! 771: advance(p, buf, pos);
! 772: return;
! 773: case ('{'):
! 774: if (0 == p->ign)
! 775: texiwarn(p, "unexpected \"{\"");
! 776: advance(p, buf, pos);
! 777: return;
! 778: case ('@'):
! 779: break;
! 780: default:
! 781: texiword(p, buf, sz, pos);
! 782: return;
! 783: }
! 784:
! 785: cmd = texicmd(p, buf, *pos, sz, &end);
! 786: advanceto(p, buf, pos, end);
! 787: if (TEXICMD__MAX == cmd)
! 788: return;
! 789: if (NULL != texitoks[cmd].fp)
! 790: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
! 791: }
! 792:
! 793: static void
1.1 kristaps 794: parseto(struct texi *p, const char *buf,
795: size_t sz, size_t *pos, const char *endtoken)
796: {
797: size_t end;
798: enum texicmd cmd;
799: size_t endtoksz;
800:
801: endtoksz = strlen(endtoken);
802: assert(endtoksz > 0);
803:
804: while ((*pos = advancenext(p, buf, sz, pos)) < sz) {
805: switch (buf[*pos]) {
806: case ('}'):
1.3 ! kristaps 807: if (0 == p->ign)
! 808: texiwarn(p, "unexpected \"}\"");
1.1 kristaps 809: advance(p, buf, pos);
810: continue;
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 ('@'):
817: break;
818: default:
819: texiword(p, buf, sz, pos);
820: continue;
821: }
822:
823: cmd = texicmd(p, buf, *pos, sz, &end);
824: advanceto(p, buf, pos, end);
825: if (TEXICMD_END == cmd) {
1.2 kristaps 826: while (*pos < sz && isws(buf[*pos]))
1.1 kristaps 827: advance(p, buf, pos);
828: /*
829: * FIXME: skip tabs and also check the full
830: * word, not just its initial substring!
831: */
832: if (sz - *pos >= endtoksz && 0 == strncmp
833: (&buf[*pos], endtoken, endtoksz)) {
1.3 ! kristaps 834: advanceeoln(p, buf, sz, pos, 0);
1.1 kristaps 835: break;
836: }
1.3 ! kristaps 837: if (0 == p->ign)
! 838: texiwarn(p, "unexpected \"end\"");
! 839: advanceeoln(p, buf, sz, pos, 0);
1.1 kristaps 840: continue;
841: } else if (TEXICMD__MAX != cmd)
842: if (NULL != texitoks[cmd].fp)
843: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
844: }
845: }
846:
847: static void
1.2 kristaps 848: parsefile(struct texi *p, const char *fname)
849: {
850: struct texifile *f;
851: int fd;
852: struct stat st;
853:
854: assert(p->filepos < 64);
855: f = &p->files[p->filepos];
856: memset(f, 0, sizeof(struct texifile));
857:
858: f->name = fname;
859: if (-1 == (fd = open(fname, O_RDONLY, 0))) {
860: texiabort(p, fname);
861: } else if (-1 == fstat(fd, &st)) {
862: close(fd);
863: texiabort(p, fname);
864: }
865:
866: f->mapsz = st.st_size;
867: f->map = mmap(NULL, f->mapsz,
868: PROT_READ, MAP_SHARED, fd, 0);
869: close(fd);
870:
871: if (MAP_FAILED == f->map)
872: texiabort(p, fname);
873:
874: p->filepos++;
875: parseeof(p, f->map, f->mapsz);
876: texifilepop(p);
877: }
878:
879: static void
1.3 ! kristaps 880: dodeftypevar(struct texi *p, enum texicmd cmd,
! 881: const char *buf, size_t sz, size_t *pos)
! 882: {
! 883: const char *blk;
! 884:
! 885: blk = TEXICMD_DEFTYPEVR == cmd ?
! 886: "deftypevr" : "deftypevar";
! 887:
! 888: if (p->ign) {
! 889: parseto(p, buf, sz, pos, blk);
! 890: return;
! 891: }
! 892:
! 893: teximacro(p, ".Pp");
! 894: if (TEXICMD_DEFTYPEVR == cmd) {
! 895: parsebracket(p, buf, sz, pos);
! 896: texiputchars(p, ":\n");
! 897: }
! 898: p->literal++;
! 899: teximacroopen(p, ".Vt ");
! 900: while (*pos < sz && '\n' != buf[*pos])
! 901: parsesingle(p, buf, sz, pos);
! 902: teximacroclose(p);
! 903: p->literal--;
! 904: teximacro(p, ".Pp");
! 905: parseto(p, buf, sz, pos, blk);
! 906: }
! 907:
! 908: static void
! 909: dodeftypefun(struct texi *p, enum texicmd cmd,
! 910: const char *buf, size_t sz, size_t *pos)
! 911: {
! 912: const char *blk;
! 913:
! 914: switch (cmd) {
! 915: case (TEXICMD_DEFTYPEFN):
! 916: blk = "deftypefn";
! 917: break;
! 918: case (TEXICMD_DEFTYPEFUN):
! 919: blk = "deftypefun";
! 920: break;
! 921: case (TEXICMD_DEFTYPEFNX):
! 922: case (TEXICMD_DEFTYPEFUNX):
! 923: blk = NULL;
! 924: break;
! 925: default:
! 926: abort();
! 927: }
! 928:
! 929: if (p->ign) {
! 930: if (NULL != blk)
! 931: parseto(p, buf, sz, pos, blk);
! 932: return;
! 933: }
! 934:
! 935: switch (cmd) {
! 936: case (TEXICMD_DEFTYPEFN):
! 937: case (TEXICMD_DEFTYPEFUN):
! 938: teximacro(p, ".Pp");
! 939: break;
! 940: default:
! 941: break;
! 942: }
! 943: if (TEXICMD_DEFTYPEFN == cmd ||
! 944: TEXICMD_DEFTYPEFNX == cmd) {
! 945: parsebracket(p, buf, sz, pos);
! 946: texiputchars(p, ":\n");
! 947: }
! 948: teximacroopen(p, ".Ft ");
! 949: parsesingle(p, buf, sz, pos);
! 950: teximacroclose(p);
! 951: teximacroopen(p, ".Fn ");
! 952: parsesingle(p, buf, sz, pos);
! 953: teximacroclose(p);
! 954: teximacroopen(p, ".Li ");
! 955: while (*pos < sz && '\n' != buf[*pos])
! 956: parsesingle(p, buf, sz, pos);
! 957: teximacroclose(p);
! 958: teximacro(p, ".Pp");
! 959: if (NULL != blk)
! 960: parseto(p, buf, sz, pos, blk);
! 961: }
! 962:
! 963: static void
1.1 kristaps 964: doignblock(struct texi *p, enum texicmd cmd,
965: const char *buf, size_t sz, size_t *pos)
966: {
1.3 ! kristaps 967: const char *blk;
1.1 kristaps 968:
969: switch (cmd) {
970: case (TEXICMD_COPYING):
1.3 ! kristaps 971: blk = "copying";
1.1 kristaps 972: break;
973: case (TEXICMD_DETAILMENU):
1.3 ! kristaps 974: blk = "detailmenu";
1.1 kristaps 975: break;
976: case (TEXICMD_DIRENTRY):
1.3 ! kristaps 977: blk = "direntry";
! 978: break;
! 979: case (TEXICMD_IFCLEAR):
! 980: blk = "ifclear";
1.1 kristaps 981: break;
982: case (TEXICMD_IFHTML):
1.3 ! kristaps 983: blk = "ifhtml";
! 984: break;
! 985: case (TEXICMD_IFINFO):
! 986: blk = "ifinfo";
! 987: break;
! 988: case (TEXICMD_IFSET):
! 989: blk = "ifset";
1.1 kristaps 990: break;
991: case (TEXICMD_IFTEX):
1.3 ! kristaps 992: blk = "iftex";
1.1 kristaps 993: break;
994: case (TEXICMD_MENU):
1.3 ! kristaps 995: blk = "menu";
1.1 kristaps 996: break;
997: case (TEXICMD_TEX):
1.3 ! kristaps 998: blk = "tex";
1.1 kristaps 999: break;
1000: case (TEXICMD_TITLEPAGE):
1.3 ! kristaps 1001: blk = "titlepage";
1.1 kristaps 1002: break;
1003: default:
1004: abort();
1005: }
1.3 ! kristaps 1006: p->ign++;
! 1007: parseto(p, buf, sz, pos, blk);
! 1008: p->ign--;
1.1 kristaps 1009: }
1010:
1011: static void
1.3 ! kristaps 1012: doblock(struct texi *p, enum texicmd cmd,
1.1 kristaps 1013: const char *buf, size_t sz, size_t *pos)
1014: {
1.3 ! kristaps 1015: const char *blk;
! 1016:
! 1017: switch (cmd) {
! 1018: case (TEXICMD_GROUP):
! 1019: blk = "group";
! 1020: break;
! 1021: case (TEXICMD_IFNOTTEX):
! 1022: blk = "ifnottex";
! 1023: break;
! 1024: default:
! 1025: abort();
! 1026: }
1.1 kristaps 1027:
1.3 ! kristaps 1028: parseto(p, buf, sz, pos, blk);
1.1 kristaps 1029: }
1030:
1031: static void
1032: doinline(struct texi *p, const char *buf,
1033: size_t sz, size_t *pos, const char *macro)
1034: {
1.3 ! kristaps 1035: int open = 0;
! 1036:
! 1037: if (0 == p->outmacro) {
! 1038: open = 1;
! 1039: teximacroopen(p, ".");
! 1040: } else
! 1041: texiputchar(p, ' ');
1.1 kristaps 1042:
1043: texiputchars(p, macro);
1044: texiputchar(p, ' ');
1045: p->seenws = 0;
1046: parsebracket(p, buf, sz, pos);
1047: if (*pos < sz - 1 &&
1048: ismpunct(buf[*pos]) &&
1049: isspace(buf[*pos + 1])) {
1050: texiputchar(p, ' ');
1051: texiputchar(p, buf[*pos]);
1052: advance(p, buf, pos);
1053: }
1.3 ! kristaps 1054: if (open)
! 1055: teximacroclose(p);
1.1 kristaps 1056: }
1057:
1058: static void
1.2 kristaps 1059: doinclude(struct texi *p, enum texicmd cmd,
1060: const char *buf, size_t sz, size_t *pos)
1061: {
1062: char fname[PATH_MAX], path[PATH_MAX];
1063: size_t i;
1064: int rc;
1065:
1066: while (*pos < sz && ' ' == buf[*pos])
1067: advance(p, buf, pos);
1068:
1069: /* Read in the filename. */
1070: for (i = 0; *pos < sz && '\n' != buf[*pos]; i++) {
1071: if (i == sizeof(fname) - 1)
1072: break;
1073: fname[i] = buf[*pos];
1074: advance(p, buf, pos);
1075: }
1076:
1077: if (i == 0)
1078: texierr(p, "path too short");
1079: else if ('\n' != buf[*pos])
1080: texierr(p, "path too long");
1081: else if ('/' == fname[0])
1082: texierr(p, "no absolute paths");
1083: fname[i] = '\0';
1084:
1085: if (strstr(fname, "../") || strstr(fname, "/.."))
1086: texierr(p, "insecure path");
1087:
1088: /* Append filename to original name's directory. */
1089: rc = snprintf(path, sizeof(path), "%s/%s", p->dir, fname);
1090: if (rc < 0)
1091: texierr(p, "couldn't format filename");
1092: else if ((size_t)rc >= sizeof(path))
1093: texierr(p, "path too long");
1094:
1095: /* Pump through to parser. */
1096: parsefile(p, path);
1097: }
1098:
1099: static void
1.1 kristaps 1100: doitalic(struct texi *p, enum texicmd cmd,
1101: const char *buf, size_t sz, size_t *pos)
1102: {
1103:
1104: texiputchars(p, "\\fI");
1105: parsebracket(p, buf, sz, pos);
1106: texiputchars(p, "\\fP");
1107: }
1108:
1109: static void
1.3 ! kristaps 1110: doenv(struct texi *p, enum texicmd cmd,
! 1111: const char *buf, size_t sz, size_t *pos)
! 1112: {
! 1113:
! 1114: if (p->literal)
! 1115: parsebracket(p, buf, sz, pos);
! 1116: else
! 1117: doinline(p, buf, sz, pos, "Ev");
! 1118: }
! 1119:
! 1120: static void
1.1 kristaps 1121: doliteral(struct texi *p, enum texicmd cmd,
1122: const char *buf, size_t sz, size_t *pos)
1123: {
1124:
1.3 ! kristaps 1125: if (p->literal)
1.1 kristaps 1126: parsebracket(p, buf, sz, pos);
1127: else
1128: doinline(p, buf, sz, pos, "Li");
1129: }
1130:
1131: static void
1132: doemph(struct texi *p, enum texicmd cmd,
1133: const char *buf, size_t sz, size_t *pos)
1134: {
1135:
1.3 ! kristaps 1136: if (p->literal)
1.1 kristaps 1137: doitalic(p, cmd, buf, sz, pos);
1138: else
1139: doinline(p, buf, sz, pos, "Em");
1140: }
1141:
1142: static void
1143: docommand(struct texi *p, enum texicmd cmd,
1144: const char *buf, size_t sz, size_t *pos)
1145: {
1146:
1147: doinline(p, buf, sz, pos, "Xr");
1148: }
1149:
1150: static void
1151: dobracket(struct texi *p, enum texicmd cmd,
1152: const char *buf, size_t sz, size_t *pos)
1153: {
1154:
1155: parsebracket(p, buf, sz, pos);
1156: }
1157:
1158: static void
1159: dofile(struct texi *p, enum texicmd cmd,
1160: const char *buf, size_t sz, size_t *pos)
1161: {
1162:
1.3 ! kristaps 1163: if (p->literal)
1.1 kristaps 1164: parsebracket(p, buf, sz, pos);
1165: else
1166: doinline(p, buf, sz, pos, "Pa");
1167: }
1168:
1169: static void
1.3 ! kristaps 1170: dodisplay(struct texi *p, enum texicmd cmd,
! 1171: const char *buf, size_t sz, size_t *pos)
! 1172: {
! 1173:
! 1174: if (p->outmacro)
! 1175: texierr(p, "display in open line scope!?");
! 1176: else if (p->literal)
! 1177: texierr(p, "display in a literal scope!?");
! 1178:
! 1179: teximacro(p, ".Bd -display");
! 1180: advanceeoln(p, buf, sz, pos, 1);
! 1181: parseto(p, buf, sz, pos, "display");
! 1182: teximacro(p, ".Ed");
! 1183: }
! 1184:
! 1185: static void
1.1 kristaps 1186: doexample(struct texi *p, enum texicmd cmd,
1187: const char *buf, size_t sz, size_t *pos)
1188: {
1.3 ! kristaps 1189: const char *blk;
! 1190:
! 1191: if (p->outmacro)
! 1192: texierr(p, "example in open line scope!?");
! 1193: else if (p->literal)
! 1194: texierr(p, "example in a literal scope!?");
! 1195:
! 1196: blk = TEXICMD_EXAMPLE == cmd ? "example" : "smallexample";
1.1 kristaps 1197:
1198: teximacro(p, ".Bd -literal");
1.3 ! kristaps 1199: advanceeoln(p, buf, sz, pos, 1);
! 1200: p->literal++;
! 1201: parseto(p, buf, sz, pos, blk);
! 1202: p->literal--;
1.1 kristaps 1203: teximacro(p, ".Ed");
1204: }
1205:
1206: static void
1207: dobye(struct texi *p, enum texicmd cmd,
1208: const char *buf, size_t sz, size_t *pos)
1209: {
1210:
1211: texiexit(p);
1212: exit(EXIT_SUCCESS);
1213: }
1214:
1215: static void
1216: dosymbol(struct texi *p, enum texicmd cmd,
1217: const char *buf, size_t sz, size_t *pos)
1218: {
1219:
1.3 ! kristaps 1220: if (p->seenws && p->outcol && 0 == p->literal) {
! 1221: texiputchar(p, ' ');
! 1222: p->seenws = 0;
! 1223: }
! 1224:
1.1 kristaps 1225: switch (cmd) {
1.3 ! kristaps 1226: case (TEXICMD_ASTERISK):
! 1227: case (TEXICMD_NEWLINE):
! 1228: case (TEXICMD_SPACE):
! 1229: case (TEXICMD_TAB):
! 1230: texiputchar(p, ' ');
! 1231: break;
1.1 kristaps 1232: case (TEXICMD_AT):
1.3 ! kristaps 1233: texiputchar(p, '@');
! 1234: break;
! 1235: case (TEXICMD_BANG):
! 1236: texiputchar(p, '!');
1.1 kristaps 1237: break;
1238: case (TEXICMD_COPYRIGHT):
1239: texiputchars(p, "\\(co");
1240: break;
1.2 kristaps 1241: case (TEXICMD_DOTS):
1242: texiputchars(p, "...");
1243: break;
1.1 kristaps 1244: case (TEXICMD_LATEX):
1245: texiputchars(p, "LaTeX");
1246: break;
1.3 ! kristaps 1247: case (TEXICMD_QUESTIONMARK):
! 1248: texiputchar(p, '?');
! 1249: break;
! 1250: case (TEXICMD_SQUIGGLE_LEFT):
! 1251: texiputchars(p, "{");
! 1252: break;
! 1253: case (TEXICMD_SQUIGGLE_RIGHT):
! 1254: texiputchars(p, "}");
! 1255: break;
1.1 kristaps 1256: case (TEXICMD_TEXSYM):
1257: texiputchars(p, "TeX");
1258: break;
1.3 ! kristaps 1259: case (TEXICMD_COLON):
! 1260: case (TEXICMD_HYPHEN):
! 1261: break;
1.1 kristaps 1262: default:
1263: abort();
1264: }
1265:
1266: doignbracket(p, cmd, buf, sz, pos);
1267: }
1268:
1269: static void
1270: doquotation(struct texi *p, enum texicmd cmd,
1271: const char *buf, size_t sz, size_t *pos)
1272: {
1273:
1274: teximacro(p, ".Qo");
1275: parseto(p, buf, sz, pos, "quotation");
1276: teximacro(p, ".Qc");
1277: }
1278:
1.3 ! kristaps 1279: /* FIXME */
! 1280: static void
! 1281: domath(struct texi *p, enum texicmd cmd,
! 1282: const char *buf, size_t sz, size_t *pos)
! 1283: {
! 1284: size_t nest;
! 1285:
! 1286: /*
! 1287: * Math handling is different from everything else.
! 1288: * We don't allow any subcomponents, and we ignore the rules in
! 1289: * terms of @-commands.
! 1290: * This departs from GNU's rules, but whatever.
! 1291: */
! 1292: while (*pos < sz && isws(buf[*pos]))
! 1293: advance(p, buf, pos);
! 1294: if (*pos == sz || '{' != buf[*pos])
! 1295: return;
! 1296: advance(p, buf, pos);
! 1297: if (p->seenws && p->outcol && 0 == p->literal)
! 1298: texiputchar(p, ' ');
! 1299: p->seenws = 0;
! 1300: for (nest = 1; *pos < sz && nest > 0; ) {
! 1301: if ('{' == buf[*pos])
! 1302: nest++;
! 1303: else if ('}' == buf[*pos])
! 1304: if (0 == --nest)
! 1305: continue;
! 1306: texiputchar(p, buf[*pos]);
! 1307: advance(p, buf, pos);
! 1308: }
! 1309: if (*pos == sz)
! 1310: return;
! 1311: assert('}' == buf[*pos]);
! 1312: advance(p, buf, pos);
! 1313: }
! 1314:
! 1315: /* FIXME */
1.1 kristaps 1316: static void
1317: doarg1(struct texi *p, enum texicmd cmd,
1318: const char *buf, size_t sz, size_t *pos)
1319: {
1.3 ! kristaps 1320: int open = 0;
1.1 kristaps 1321:
1322: if (*pos == sz || '{' != buf[*pos])
1323: return;
1324: advance(p, buf, pos);
1325: switch (cmd) {
1326: case (TEXICMD_EMAIL):
1.3 ! kristaps 1327: if ( ! p->outmacro) {
! 1328: open = 1;
! 1329: teximacroopen(p, ".");
! 1330: }
1.1 kristaps 1331: texiputchars(p, "Lk ");
1332: break;
1.3 ! kristaps 1333: case (TEXICMD_UREF):
1.1 kristaps 1334: case (TEXICMD_URL):
1.3 ! kristaps 1335: if ( ! p->outmacro) {
! 1336: open = 1;
! 1337: teximacroopen(p, ".");
! 1338: }
1.1 kristaps 1339: texiputchars(p, "Mt ");
1340: break;
1341: default:
1.3 ! kristaps 1342: break;
1.1 kristaps 1343: }
1.3 ! kristaps 1344: /* FIXME: this is ugly */
1.1 kristaps 1345: while (*pos < sz && '}' != buf[*pos] && ',' != buf[*pos]) {
1346: texiputchar(p, buf[*pos]);
1347: advance(p, buf, pos);
1348: }
1349: while (*pos < sz && '}' != buf[*pos])
1350: advance(p, buf, pos);
1351: if (*pos < sz)
1352: advance(p, buf, pos);
1353: if (*pos < sz - 1 &&
1354: ismpunct(buf[*pos]) &&
1355: isspace(buf[*pos + 1])) {
1356: texiputchar(p, ' ');
1357: texiputchar(p, buf[*pos]);
1358: advance(p, buf, pos);
1359: }
1.3 ! kristaps 1360: if (open)
! 1361: teximacroclose(p);
1.1 kristaps 1362: }
1363:
1364: static void
1365: dosubsection(struct texi *p, enum texicmd cmd,
1366: const char *buf, size_t sz, size_t *pos)
1367: {
1368:
1.3 ! kristaps 1369: if (p->outmacro)
! 1370: texierr(p, "subsubsection in open line scope!?");
! 1371: else if (p->literal)
! 1372: texierr(p, "subsubsection in a literal scope!?");
! 1373:
! 1374: teximacro(p, ".Pp");
! 1375: parseeoln(p, buf, sz, pos);
! 1376: teximacro(p, ".Pp");
1.1 kristaps 1377: }
1378:
1379: static void
1380: dosection(struct texi *p, enum texicmd cmd,
1381: const char *buf, size_t sz, size_t *pos)
1382: {
1383:
1.3 ! kristaps 1384: if (p->outmacro)
! 1385: texierr(p, "subsection in open line scope!?");
! 1386: else if (p->literal)
! 1387: texierr(p, "subsection in a literal scope!?");
! 1388:
! 1389: teximacroopen(p, ".Ss ");
! 1390: parseeoln(p, buf, sz, pos);
! 1391: teximacroclose(p);
! 1392: }
! 1393:
! 1394: static void
! 1395: dosp(struct texi *p, enum texicmd cmd,
! 1396: const char *buf, size_t sz, size_t *pos)
! 1397: {
! 1398:
! 1399: if (p->outmacro)
! 1400: texierr(p, "spacing in open line scope!?");
! 1401: else if (p->literal)
! 1402: texierr(p, "spacing in a literal scope!?");
! 1403:
! 1404: teximacro(p, ".Pp");
! 1405: advanceeoln(p, buf, sz, pos, 1);
1.1 kristaps 1406: }
1407:
1408: static void
1.3 ! kristaps 1409: dochapter(struct texi *p, enum texicmd cmd,
1.1 kristaps 1410: const char *buf, size_t sz, size_t *pos)
1411: {
1412:
1.3 ! kristaps 1413: if (p->outmacro)
! 1414: texierr(p, "section in open line scope!?");
! 1415: else if (p->literal)
! 1416: texierr(p, "section in a literal scope!?");
! 1417:
! 1418: teximacroopen(p, ".Sh ");
! 1419: parseeoln(p, buf, sz, pos);
! 1420: teximacroclose(p);
1.1 kristaps 1421: }
1422:
1423: static void
1424: dotop(struct texi *p, enum texicmd cmd,
1425: const char *buf, size_t sz, size_t *pos)
1426: {
1427:
1.3 ! kristaps 1428: p->ign--;
! 1429: advanceeoln(p, buf, sz, pos, 1);
! 1430: teximacro(p, ".Dd $Mdocdate: February 17 2015 $");
1.1 kristaps 1431: teximacro(p, ".Dt SOMETHING 7");
1432: teximacro(p, ".Os");
1433: teximacro(p, ".Sh NAME");
1434: teximacro(p, ".Nm Something");
1435: teximacro(p, ".Nd Something");
1436: }
1437:
1438: static void
1439: doitem(struct texi *p, enum texicmd cmd,
1440: const char *buf, size_t sz, size_t *pos)
1441: {
1442:
1.3 ! kristaps 1443: if (p->outmacro)
! 1444: texierr(p, "item in open line scope!?");
! 1445: else if (p->literal)
! 1446: texierr(p, "item in a literal scope!?");
! 1447:
! 1448: switch (p->list) {
! 1449: case (TEXILIST_ITEM):
! 1450: teximacroopen(p, ".It");
! 1451: break;
! 1452: case (TEXILIST_NOITEM):
! 1453: teximacro(p, ".It");
! 1454: break;
! 1455: default:
! 1456: teximacro(p, ".Pp");
! 1457: break;
! 1458: }
! 1459:
! 1460: parseeoln(p, buf, sz, pos);
1.1 kristaps 1461:
1.3 ! kristaps 1462: if (TEXILIST_ITEM == p->list)
! 1463: teximacroclose(p);
! 1464: else
1.1 kristaps 1465: texiputchar(p, '\n');
1466: }
1467:
1468: static void
1469: dotable(struct texi *p, enum texicmd cmd,
1470: const char *buf, size_t sz, size_t *pos)
1471: {
1.3 ! kristaps 1472: enum texilist sv = p->list;
! 1473:
! 1474: if (p->outmacro)
! 1475: texierr(p, "table in open line scope!?");
! 1476: else if (p->literal)
! 1477: texierr(p, "table in a literal scope!?");
1.1 kristaps 1478:
1.3 ! kristaps 1479: p->list = TEXILIST_ITEM;
1.1 kristaps 1480: teximacro(p, ".Bl -tag -width Ds");
1481: parseto(p, buf, sz, pos, "table");
1482: teximacro(p, ".El");
1.3 ! kristaps 1483: p->list = sv;
1.1 kristaps 1484: }
1485:
1486: static void
1.2 kristaps 1487: doenumerate(struct texi *p, enum texicmd cmd,
1488: const char *buf, size_t sz, size_t *pos)
1489: {
1.3 ! kristaps 1490: enum texilist sv = p->list;
1.2 kristaps 1491:
1.3 ! kristaps 1492: if (p->outmacro)
! 1493: texierr(p, "enumerate in open line scope!?");
! 1494: else if (p->literal)
! 1495: texierr(p, "enumerate in a literal scope!?");
! 1496:
! 1497: p->list = TEXILIST_NOITEM;
1.2 kristaps 1498: teximacro(p, ".Bl -enum");
1499: parseto(p, buf, sz, pos, "enumerate");
1500: teximacro(p, ".El");
1.3 ! kristaps 1501: p->list = sv;
1.2 kristaps 1502: }
1503:
1504: static void
1.1 kristaps 1505: doitemize(struct texi *p, enum texicmd cmd,
1506: const char *buf, size_t sz, size_t *pos)
1507: {
1.3 ! kristaps 1508: enum texilist sv = p->list;
! 1509:
! 1510: if (p->outmacro)
! 1511: texierr(p, "itemize in open line scope!?");
! 1512: else if (p->literal)
! 1513: texierr(p, "itemize in a literal scope!?");
1.1 kristaps 1514:
1.3 ! kristaps 1515: p->list = TEXILIST_ITEM;
1.1 kristaps 1516: teximacro(p, ".Bl -bullet");
1517: parseto(p, buf, sz, pos, "itemize");
1518: teximacro(p, ".El");
1.3 ! kristaps 1519: p->list = sv;
1.1 kristaps 1520: }
1521:
1522: static void
1523: doignbracket(struct texi *p, enum texicmd cmd,
1524: const char *buf, size_t sz, size_t *pos)
1525: {
1526:
1.3 ! kristaps 1527: p->ign++;
1.1 kristaps 1528: parsebracket(p, buf, sz, pos);
1.3 ! kristaps 1529: p->ign--;
1.1 kristaps 1530: }
1531:
1532: static void
1533: doignline(struct texi *p, enum texicmd cmd,
1534: const char *buf, size_t sz, size_t *pos)
1535: {
1536:
1.3 ! kristaps 1537: advanceeoln(p, buf, sz, pos, 1);
1.1 kristaps 1538: }
1539:
1540: int
1541: main(int argc, char *argv[])
1542: {
1543: struct texi texi;
1.2 kristaps 1544: int c;
1545: char *path, *dir;
1.1 kristaps 1546: const char *progname;
1547:
1548: progname = strrchr(argv[0], '/');
1549: if (progname == NULL)
1550: progname = argv[0];
1551: else
1552: ++progname;
1553:
1554: while (-1 != (c = getopt(argc, argv, "")))
1555: switch (c) {
1556: default:
1557: goto usage;
1558: }
1559:
1560: argv += optind;
1561: if (0 == (argc -= optind))
1562: goto usage;
1563:
1.2 kristaps 1564: if (NULL == (path = strdup(argv[0]))) {
1565: perror(NULL);
1566: exit(EXIT_FAILURE);
1567: } else if (NULL == (dir = dirname(path))) {
1568: perror(argv[0]);
1569: free(path);
1570: exit(EXIT_FAILURE);
1571: }
1572: free(path);
1573:
1.1 kristaps 1574: memset(&texi, 0, sizeof(struct texi));
1.3 ! kristaps 1575: texi.ign = 1;
1.2 kristaps 1576: texi.dir = strdup(dir);
1577: parsefile(&texi, argv[0]);
1578: texiexit(&texi);
1579: return(EXIT_FAILURE);
1.1 kristaps 1580: usage:
1581: fprintf(stderr, "usage: %s file\n", progname);
1582: return(EXIT_FAILURE);
1583: }
CVSweb