Annotation of texi2mdoc/main.c, Revision 1.1.1.1
1.1 kristaps 1: /* $Id: main.c,v 1.216 2015/01/16 21:15:05 schwarze Exp $ */
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>
24: #include <stdarg.h>
25: #include <stdio.h>
26: #include <stdlib.h>
27: #include <string.h>
28:
29: /*
30: * This defines each one of the Texinfo commands that we understand.
31: * Obviously this only refers to native commands; overriden names are a
32: * different story.
33: */
34: enum texicmd {
35: TEXICMD_A4PAPER,
36: TEXICMD_ANCHOR,
37: TEXICMD_AT,
38: TEXICMD_BYE,
39: TEXICMD_CHAPTER,
40: TEXICMD_CINDEX,
41: TEXICMD_CODE,
42: TEXICMD_COMMAND,
43: TEXICMD_COMMENT,
44: TEXICMD_CONTENTS,
45: TEXICMD_COPYING,
46: TEXICMD_COPYRIGHT,
47: TEXICMD_DETAILMENU,
48: TEXICMD_DIRCATEGORY,
49: TEXICMD_DIRENTRY,
50: TEXICMD_EMAIL,
51: TEXICMD_EMPH,
52: TEXICMD_END,
53: TEXICMD_EXAMPLE,
54: TEXICMD_FILE,
55: TEXICMD_I,
56: TEXICMD_IFHTML,
57: TEXICMD_IFNOTTEX,
58: TEXICMD_IFTEX,
59: TEXICMD_IMAGE,
60: TEXICMD_ITEM,
61: TEXICMD_ITEMIZE,
62: TEXICMD_KBD,
63: TEXICMD_LATEX,
64: TEXICMD_MENU,
65: TEXICMD_NODE,
66: TEXICMD_QUOTATION,
67: TEXICMD_PARINDENT,
68: TEXICMD_REF,
69: TEXICMD_SAMP,
70: TEXICMD_SECTION,
71: TEXICMD_SETCHAPNEWPAGE,
72: TEXICMD_SETFILENAME,
73: TEXICMD_SETTITLE,
74: TEXICMD_SUBSECTION,
75: TEXICMD_TABLE,
76: TEXICMD_TEX,
77: TEXICMD_TEXSYM,
78: TEXICMD_TITLEFONT,
79: TEXICMD_TITLEPAGE,
80: TEXICMD_TOP,
81: TEXICMD_UNNUMBERED,
82: TEXICMD_URL,
83: TEXICMD_VAR,
84: TEXICMD__MAX
85: };
86:
87: /*
88: * The file currently being parsed.
89: * This keeps track of our location within that file.
90: */
91: struct texifile {
92: const char *name; /* name of the file */
93: size_t line; /* current line (from zero) */
94: size_t col; /* current column in line (from zero) */
95: char *map; /* mmap'd file */
96: size_t mapsz; /* size of mmap */
97: };
98:
99: struct texi;
100:
101: typedef void (*texicmdfp)(struct texi *,
102: enum texicmd, const char *, size_t, size_t *);
103:
104: /*
105: * Describes Texinfo commands, whether native or overriden.
106: */
107: struct texitok {
108: texicmdfp fp; /* callback (or NULL if none) */
109: const char *tok; /* name of the token */
110: size_t len; /* strlen(tok) */
111: };
112:
113: /*
114: * The main parse structure.
115: * This keeps any necessary information handy.
116: */
117: struct texi {
118: struct texifile files[64];
119: size_t filepos;
120: unsigned flags;
121: #define TEXI_IGN 0x01 /* don't print anything */
122: #define TEXI_HEADER (TEXI_IGN | 0x02) /* haven't seen @top yet */
123: #define TEXI_LITERAL 0x04 /* output all whitespace */
124: size_t outcol; /* column of output */
125: int outmacro; /* whether output is in line macro */
126: int seenws; /* whitespace has been ignored */
127: };
128:
129: #define ismpunct(_x) \
130: ('.' == (_x) || \
131: ',' == (_x) || \
132: ';' == (_x))
133:
134: static void doarg1(struct texi *, enum texicmd, const char *, size_t, size_t *);
135: static void dobracket(struct texi *, enum texicmd, const char *, size_t, size_t *);
136: static void dobye(struct texi *, enum texicmd, const char *, size_t, size_t *);
137: static void docommand(struct texi *, enum texicmd, const char *, size_t, size_t *);
138: static void doemph(struct texi *, enum texicmd, const char *, size_t, size_t *);
139: static void doexample(struct texi *, enum texicmd, const char *, size_t, size_t *);
140: static void dofile(struct texi *, enum texicmd, const char *, size_t, size_t *);
141: static void doifnottex(struct texi *, enum texicmd, const char *, size_t, size_t *);
142: static void doignblock(struct texi *, enum texicmd, const char *, size_t, size_t *);
143: static void doignbracket(struct texi *, enum texicmd, const char *, size_t, size_t *);
144: static void doignline(struct texi *, enum texicmd, const char *, size_t, size_t *);
145: static void doitalic(struct texi *, enum texicmd, const char *, size_t, size_t *);
146: static void doitem(struct texi *, enum texicmd, const char *, size_t, size_t *);
147: static void doitemize(struct texi *, enum texicmd, const char *, size_t, size_t *);
148: static void doliteral(struct texi *, enum texicmd, const char *, size_t, size_t *);
149: static void doquotation(struct texi *, enum texicmd, const char *, size_t, size_t *);
150: static void dotable(struct texi *, enum texicmd, const char *, size_t, size_t *);
151: static void dotop(struct texi *, enum texicmd, const char *, size_t, size_t *);
152: static void dosection(struct texi *, enum texicmd, const char *, size_t, size_t *);
153: static void dosh(struct texi *, enum texicmd, const char *, size_t, size_t *);
154: static void dosubsection(struct texi *, enum texicmd, const char *, size_t, size_t *);
155: static void dosymbol(struct texi *, enum texicmd, const char *, size_t, size_t *);
156:
157: static const struct texitok texitoks[TEXICMD__MAX] = {
158: { doignline, "afourpaper", 10 }, /* TEXICMD_A4PAPER */
159: { doignbracket, "anchor", 6 }, /* TEXICMD_ANCHOR */
160: { dosymbol, "@", 1 }, /* TEXICMD_AT */
161: { dobye, "bye", 3 }, /* TEXICMD_BYE */
162: { dosh, "chapter", 7 }, /* TEXICMD_CHAPTER */
163: { doignline, "cindex", 6 }, /* TEXICMD_CINDEX */
164: { doliteral, "code", 4 }, /* TEXICMD_CODE */
165: { docommand, "command", 7 }, /* TEXICMD_COMMAND */
166: { doignline, "c", 1 }, /* TEXICMD_COMMENT */
167: { doignline, "contents", 8 }, /* TEXICMD_CONTENTS */
168: { doignblock, "copying", 7 }, /* TEXICMD_COPYING */
169: { dosymbol, "copyright", 9 }, /* TEXICMD_COPYRIGHT */
170: { doignblock, "detailmenu", 10 }, /* TEXICMD_DETAILMENU */
171: { doignline, "dircategory", 11 }, /* TEXICMD_DIRCATEGORY */
172: { doignblock, "direntry", 8 }, /* TEXICMD_DIRENTRY */
173: { doarg1, "email", 5 }, /* TEXICMD_EMAIL */
174: { doemph, "emph", 4 }, /* TEXICMD_EMPH */
175: { NULL, "end", 3 }, /* TEXICMD_END */
176: { doexample, "example", 7 }, /* TEXICMD_EXAMPLE */
177: { dofile, "file", 4 }, /* TEXICMD_FILE */
178: { doitalic, "i", 1 }, /* TEXICMD_I */
179: { doignblock, "ifhtml", 6 }, /* TEXICMD_IFHTML */
180: { doifnottex, "ifnottex", 8 }, /* TEXICMD_IFNOTTEX */
181: { doignblock, "iftex", 5 }, /* TEXICMD_IFTEX */
182: { doignbracket, "image", 5 }, /* TEXICMD_IMAGE */
183: { doitem, "item", 4 }, /* TEXICMD_ITEM */
184: { doitemize, "itemize", 7 }, /* TEXICMD_ITEMIZE */
185: { doliteral, "kbd", 3 }, /* TEXICMD_KBD */
186: { dosymbol, "LaTeX", 5 }, /* TEXICMD_LATEX */
187: { doignblock, "menu", 4 }, /* TEXICMD_MENU */
188: { doignline, "node", 4 }, /* TEXICMD_NODE */
189: { doquotation, "quotation", 9 }, /* TEXICMD_QUOTATION */
190: { doignline, "paragraphindent", 14 }, /* TEXICMD_PARINDENT */
191: { dobracket, "ref", 3 }, /* TEXICMD_REF */
192: { doliteral, "samp", 4 }, /* TEXICMD_SAMP */
193: { dosection, "section", 7 }, /* TEXICMD_SECTION */
194: { doignline, "setchapternewpage", 17 }, /* TEXICMD_SETCHAPNEWPAGE */
195: { doignline, "setfilename", 11 }, /* TEXICMD_SETFILENAME */
196: { doignline, "settitle", 8 }, /* TEXICMD_SETTITLE */
197: { dosubsection, "subsection", 10 }, /* TEXICMD_SUBSECTION */
198: { dotable, "table", 5 }, /* TEXICMD_TABLE */
199: { doignblock, "tex", 3 }, /* TEXICMD_TEX */
200: { dosymbol, "TeX", 3 }, /* TEXICMD_TEXSYM */
201: { dobracket, "titlefont", 9 }, /* TEXICMD_TITLEFONT */
202: { doignblock, "titlepage", 9 }, /* TEXICMD_TITLEPAGE */
203: { dotop, "top", 3 }, /* TEXICMD_TOP */
204: { dosh, "unnumbered", 10 }, /* TEXICMD_UNNUMBERED */
205: { doarg1, "url", 3 }, /* TEXICMD_URL */
206: { doliteral, "var", 3 }, /* TEXICMD_VAR */
207: };
208:
209: static void
210: texifilepop(struct texi *p)
211: {
212: struct texifile *f;
213:
214: assert(p->filepos > 0);
215: f = &p->files[--p->filepos];
216: munmap(f->map, f->mapsz);
217: }
218:
219: static void
220: texiexit(struct texi *p)
221: {
222:
223: while (p->filepos > 0)
224: texifilepop(p);
225: }
226:
227: static void
228: texifatal(struct texi *p, const char *errstring)
229: {
230:
231: perror(errstring);
232: texiexit(p);
233: exit(EXIT_FAILURE);
234: }
235:
236: /*
237: * Print a generic warning message (to stderr) tied to our current
238: * location in the parse sequence.
239: */
240: static void
241: texiwarn(const struct texi *p, const char *fmt, ...)
242: {
243: va_list ap;
244:
245: fprintf(stderr, "%s:%zu:%zu: ",
246: p->files[p->filepos - 1].name,
247: p->files[p->filepos - 1].line + 1,
248: p->files[p->filepos - 1].col + 1);
249: va_start(ap, fmt);
250: vfprintf(stderr, fmt, ap);
251: va_end(ap);
252: fputc('\n', stderr);
253: }
254:
255: /*
256: * Put a single data character.
257: * This MUST NOT be a mdoc(7) command: it should be free text that's
258: * outputted to the screen.
259: */
260: static void
261: texiputchar(struct texi *p, char c)
262: {
263:
264: if (TEXI_IGN & p->flags)
265: return;
266:
267: putchar(c);
268: if ('\n' == c) {
269: p->outcol = 0;
270: p->outmacro = 0;
271: p->seenws = 0;
272: } else
273: p->outcol++;
274: }
275:
276: /*
277: * Put multiple characters (see texiputchar()).
278: */
279: static void
280: texiputchars(struct texi *p, const char *s)
281: {
282:
283: while ('\0' != *s)
284: texiputchar(p, *s++);
285: }
286:
287: /*
288: * Put an mdoc(7) command without the trailing newline.
289: * This should ONLY be used for mdoc(7) commands!
290: */
291: static void
292: texifputs(struct texi *p, const char *s)
293: {
294: int rc;
295:
296: if (TEXI_IGN & p->flags)
297: return;
298: if (p->outcol)
299: texiputchar(p, '\n');
300: if (EOF != (rc = fputs(s, stdout)))
301: p->outcol += rc;
302: }
303:
304: /*
305: * Put an mdoc(7) command with the trailing newline.
306: * This should ONLY be used for mdoc(7) commands!
307: */
308: static void
309: teximacro(struct texi *p, const char *s)
310: {
311:
312: if (TEXI_IGN & p->flags)
313: return;
314: if (p->outcol)
315: texiputchar(p, '\n');
316: puts(s);
317: p->outcol = 0;
318: p->seenws = 0;
319: }
320:
321: /*
322: * Advance by a single byte in the input stream.
323: */
324: static void
325: advance(struct texi *p, const char *buf, size_t *pos)
326: {
327:
328: if ('\n' == buf[*pos]) {
329: p->files[p->filepos - 1].line++;
330: p->files[p->filepos - 1].col = 0;
331: } else
332: p->files[p->filepos - 1].col++;
333:
334: (*pos)++;
335: }
336:
337: /*
338: * Advance to the next non-whitespace word in the input stream.
339: * If we're in literal mode, then print all of the whitespace as we're
340: * doing so.
341: */
342: static size_t
343: advancenext(struct texi *p, const char *buf, size_t sz, size_t *pos)
344: {
345:
346: if (TEXI_LITERAL & p->flags) {
347: while (*pos < sz && isspace(buf[*pos])) {
348: texiputchar(p, buf[*pos]);
349: advance(p, buf, pos);
350: }
351: return(*pos);
352: }
353:
354: while (*pos < sz && isspace(buf[*pos])) {
355: p->seenws = 1;
356: /*
357: * If it looks like we've printed a double-line, then
358: * output a paragraph.
359: * FIXME: this is stupid.
360: */
361: if (*pos && '\n' == buf[*pos] && '\n' == buf[*pos - 1])
362: teximacro(p, ".Pp");
363: advance(p, buf, pos);
364: }
365: return(*pos);
366: }
367:
368: /*
369: * Advance to the EOLN in the input stream.
370: */
371: static size_t
372: advanceeoln(struct texi *p, const char *buf, size_t sz, size_t *pos)
373: {
374:
375: while (*pos < sz && '\n' != buf[*pos])
376: advance(p, buf, pos);
377: return(*pos);
378: }
379:
380: /*
381: * Advance to position "end", which is an absolute position in the
382: * current buffer greater than or equal to the current position.
383: */
384: static void
385: advanceto(struct texi *p, const char *buf, size_t *pos, size_t end)
386: {
387:
388: assert(*pos <= end);
389: while (*pos < end)
390: advance(p, buf, pos);
391: }
392:
393: /*
394: * Output a free-form word in the input stream, progressing to the next
395: * command or white-space.
396: * This also will advance the input stream.
397: */
398: static void
399: texiword(struct texi *p, const char *buf, size_t sz, size_t *pos)
400: {
401:
402: /*
403: * XXX: if we're in literal mode, then we shouldn't do any
404: * reflowing of text here.
405: */
406: if (p->outcol > 72 && ! (TEXI_LITERAL & p->flags))
407: texiputchar(p, '\n');
408:
409: if (p->seenws && p->outcol && ! (TEXI_LITERAL & p->flags))
410: texiputchar(p, ' ');
411:
412: p->seenws = 0;
413:
414: while (*pos < sz && ! isspace(buf[*pos])) {
415: switch (buf[*pos]) {
416: case ('@'):
417: case ('}'):
418: case ('{'):
419: return;
420: }
421: if (*pos < sz - 1 &&
422: '`' == buf[*pos] &&
423: '`' == buf[*pos + 1]) {
424: texiputchars(p, "\\(lq");
425: advance(p, buf, pos);
426: } else if (*pos < sz - 1 &&
427: '\'' == buf[*pos] &&
428: '\'' == buf[*pos + 1]) {
429: texiputchars(p, "\\(rq");
430: advance(p, buf, pos);
431: } else
432: texiputchar(p, buf[*pos]);
433: advance(p, buf, pos);
434: }
435: }
436:
437: static enum texicmd
438: texicmd(struct texi *p, const char *buf,
439: size_t pos, size_t sz, size_t *end)
440: {
441: size_t i, len;
442:
443: assert('@' == buf[pos]);
444: for (*end = ++pos; *end < sz && ! isspace(buf[*end]); (*end)++)
445: if ('@' == buf[*end] || '{' == buf[*end])
446: break;
447:
448: len = *end - pos;
449: for (i = 0; i < TEXICMD__MAX; i++) {
450: if (len != texitoks[i].len)
451: continue;
452: if (0 == strncmp(texitoks[i].tok, &buf[pos], len))
453: return(i);
454: }
455:
456: texiwarn(p, "bad command: %.*s", (int)len, &buf[pos]);
457: return(TEXICMD__MAX);
458: }
459:
460: static void
461: parseeof(struct texi *p, const char *buf, size_t sz)
462: {
463: size_t pos = 0;
464: enum texicmd cmd;
465: size_t end;
466:
467: while ((pos = advancenext(p, buf, sz, &pos)) < sz) {
468: switch (buf[pos]) {
469: case ('}'):
470: texiwarn(p, "unexpected \"}\"");
471: advance(p, buf, &pos);
472: continue;
473: case ('{'):
474: texiwarn(p, "unexpected \"{\"");
475: advance(p, buf, &pos);
476: continue;
477: case ('@'):
478: break;
479: default:
480: texiword(p, buf, sz, &pos);
481: continue;
482: }
483:
484: cmd = texicmd(p, buf, pos, sz, &end);
485: advanceto(p, buf, &pos, end);
486: if (TEXICMD__MAX == cmd)
487: continue;
488: if (NULL != texitoks[cmd].fp)
489: (*texitoks[cmd].fp)(p, cmd, buf, sz, &pos);
490: }
491: }
492:
493: static void
494: parsebracket(struct texi *p, const char *buf, size_t sz, size_t *pos)
495: {
496: size_t end;
497: enum texicmd cmd;
498:
499: if (*pos == sz || '{' != buf[*pos])
500: return;
501: advance(p, buf, pos);
502:
503: while ((*pos = advancenext(p, buf, sz, pos)) < sz) {
504: switch (buf[*pos]) {
505: case ('}'):
506: advance(p, buf, pos);
507: return;
508: case ('{'):
509: texiwarn(p, "unexpected \"{\"");
510: advance(p, buf, pos);
511: continue;
512: case ('@'):
513: break;
514: default:
515: texiword(p, buf, sz, pos);
516: continue;
517: }
518:
519: cmd = texicmd(p, buf, *pos, sz, &end);
520: advanceto(p, buf, pos, end);
521: if (TEXICMD__MAX == cmd)
522: continue;
523: if (NULL != texitoks[cmd].fp)
524: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
525: }
526: }
527:
528: static void
529: parseto(struct texi *p, const char *buf,
530: size_t sz, size_t *pos, const char *endtoken)
531: {
532: size_t end;
533: enum texicmd cmd;
534: size_t endtoksz;
535:
536: endtoksz = strlen(endtoken);
537: assert(endtoksz > 0);
538:
539: while ((*pos = advancenext(p, buf, sz, pos)) < sz) {
540: switch (buf[*pos]) {
541: case ('}'):
542: texiwarn(p, "unexpected \"}\"");
543: advance(p, buf, pos);
544: continue;
545: case ('{'):
546: texiwarn(p, "unexpected \"{\"");
547: advance(p, buf, pos);
548: continue;
549: case ('@'):
550: break;
551: default:
552: texiword(p, buf, sz, pos);
553: continue;
554: }
555:
556: cmd = texicmd(p, buf, *pos, sz, &end);
557: advanceto(p, buf, pos, end);
558: if (TEXICMD_END == cmd) {
559: while (*pos < sz && ' ' == buf[*pos])
560: advance(p, buf, pos);
561: /*
562: * FIXME: skip tabs and also check the full
563: * word, not just its initial substring!
564: */
565: if (sz - *pos >= endtoksz && 0 == strncmp
566: (&buf[*pos], endtoken, endtoksz)) {
567: advanceeoln(p, buf, sz, pos);
568: break;
569: }
570: texiwarn(p, "unexpected \"end\"");
571: advanceeoln(p, buf, sz, pos);
572: continue;
573: } else if (TEXICMD__MAX != cmd)
574: if (NULL != texitoks[cmd].fp)
575: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
576: }
577: }
578:
579: static void
580: doignblock(struct texi *p, enum texicmd cmd,
581: const char *buf, size_t sz, size_t *pos)
582: {
583: unsigned int sv = p->flags;
584: const char *blockname;
585:
586: p->flags |= TEXI_IGN;
587: switch (cmd) {
588: case (TEXICMD_COPYING):
589: blockname = "copying";
590: break;
591: case (TEXICMD_DETAILMENU):
592: blockname = "detailmenu";
593: break;
594: case (TEXICMD_DIRENTRY):
595: blockname = "direntry";
596: break;
597: case (TEXICMD_IFHTML):
598: blockname = "ifhtml";
599: break;
600: case (TEXICMD_IFTEX):
601: blockname = "iftex";
602: break;
603: case (TEXICMD_MENU):
604: blockname = "menu";
605: break;
606: case (TEXICMD_TEX):
607: blockname = "tex";
608: break;
609: case (TEXICMD_TITLEPAGE):
610: blockname = "titlepage";
611: break;
612: default:
613: abort();
614: }
615: parseto(p, buf, sz, pos, blockname);
616: p->flags = sv;
617: }
618:
619: static void
620: doifnottex(struct texi *p, enum texicmd cmd,
621: const char *buf, size_t sz, size_t *pos)
622: {
623:
624: parseto(p, buf, sz, pos, "ifnottex");
625: }
626:
627: static void
628: doinline(struct texi *p, const char *buf,
629: size_t sz, size_t *pos, const char *macro)
630: {
631:
632: if ( ! p->outmacro)
633: texifputs(p, ".");
634: texiputchars(p, macro);
635: texiputchar(p, ' ');
636: p->seenws = 0;
637: p->outmacro++;
638: parsebracket(p, buf, sz, pos);
639: p->outmacro--;
640: if (*pos < sz - 1 &&
641: ismpunct(buf[*pos]) &&
642: isspace(buf[*pos + 1])) {
643: texiputchar(p, ' ');
644: texiputchar(p, buf[*pos]);
645: advance(p, buf, pos);
646: }
647: if ( ! p->outmacro)
648: texiputchar(p, '\n');
649: }
650:
651: static void
652: doitalic(struct texi *p, enum texicmd cmd,
653: const char *buf, size_t sz, size_t *pos)
654: {
655:
656: texiputchars(p, "\\fI");
657: parsebracket(p, buf, sz, pos);
658: texiputchars(p, "\\fP");
659: }
660:
661: static void
662: doliteral(struct texi *p, enum texicmd cmd,
663: const char *buf, size_t sz, size_t *pos)
664: {
665:
666: if (TEXI_LITERAL & p->flags)
667: parsebracket(p, buf, sz, pos);
668: else
669: doinline(p, buf, sz, pos, "Li");
670: }
671:
672: static void
673: doemph(struct texi *p, enum texicmd cmd,
674: const char *buf, size_t sz, size_t *pos)
675: {
676:
677: if (TEXI_LITERAL & p->flags)
678: doitalic(p, cmd, buf, sz, pos);
679: else
680: doinline(p, buf, sz, pos, "Em");
681: }
682:
683: static void
684: docommand(struct texi *p, enum texicmd cmd,
685: const char *buf, size_t sz, size_t *pos)
686: {
687:
688: doinline(p, buf, sz, pos, "Xr");
689: }
690:
691: static void
692: dobracket(struct texi *p, enum texicmd cmd,
693: const char *buf, size_t sz, size_t *pos)
694: {
695:
696: parsebracket(p, buf, sz, pos);
697: }
698:
699: static void
700: dofile(struct texi *p, enum texicmd cmd,
701: const char *buf, size_t sz, size_t *pos)
702: {
703:
704: if (TEXI_LITERAL & p->flags)
705: parsebracket(p, buf, sz, pos);
706: else
707: doinline(p, buf, sz, pos, "Pa");
708: }
709:
710: static void
711: doexample(struct texi *p, enum texicmd cmd,
712: const char *buf, size_t sz, size_t *pos)
713: {
714: unsigned int sv;
715:
716: teximacro(p, ".Bd -literal");
717: advanceeoln(p, buf, sz, pos);
718: if ('\n' == buf[*pos])
719: advance(p, buf, pos);
720: sv = p->flags;
721: p->flags |= TEXI_LITERAL;
722: parseto(p, buf, sz, pos, "example");
723: p->flags = sv;
724: teximacro(p, ".Ed");
725: }
726:
727: static void
728: dobye(struct texi *p, enum texicmd cmd,
729: const char *buf, size_t sz, size_t *pos)
730: {
731:
732: texiexit(p);
733: exit(EXIT_SUCCESS);
734: }
735:
736: static void
737: dosymbol(struct texi *p, enum texicmd cmd,
738: const char *buf, size_t sz, size_t *pos)
739: {
740:
741: switch (cmd) {
742: case (TEXICMD_AT):
743: texiputchars(p, "@");
744: break;
745: case (TEXICMD_COPYRIGHT):
746: texiputchars(p, "\\(co");
747: break;
748: case (TEXICMD_LATEX):
749: texiputchars(p, "LaTeX");
750: break;
751: case (TEXICMD_TEXSYM):
752: texiputchars(p, "TeX");
753: break;
754: default:
755: abort();
756: }
757:
758: doignbracket(p, cmd, buf, sz, pos);
759: }
760:
761: static void
762: doquotation(struct texi *p, enum texicmd cmd,
763: const char *buf, size_t sz, size_t *pos)
764: {
765:
766: teximacro(p, ".Qo");
767: parseto(p, buf, sz, pos, "quotation");
768: teximacro(p, ".Qc");
769: }
770:
771: static void
772: doarg1(struct texi *p, enum texicmd cmd,
773: const char *buf, size_t sz, size_t *pos)
774: {
775:
776: if (*pos == sz || '{' != buf[*pos])
777: return;
778: advance(p, buf, pos);
779: if ( ! p->outmacro)
780: texifputs(p, ".");
781: switch (cmd) {
782: case (TEXICMD_EMAIL):
783: texiputchars(p, "Lk ");
784: break;
785: case (TEXICMD_URL):
786: texiputchars(p, "Mt ");
787: break;
788: default:
789: abort();
790: }
791: while (*pos < sz && '}' != buf[*pos] && ',' != buf[*pos]) {
792: texiputchar(p, buf[*pos]);
793: advance(p, buf, pos);
794: }
795: while (*pos < sz && '}' != buf[*pos])
796: advance(p, buf, pos);
797: if (*pos < sz)
798: advance(p, buf, pos);
799: if (*pos < sz - 1 &&
800: ismpunct(buf[*pos]) &&
801: isspace(buf[*pos + 1])) {
802: texiputchar(p, ' ');
803: texiputchar(p, buf[*pos]);
804: advance(p, buf, pos);
805: }
806: if ( ! p->outmacro)
807: texiputchar(p, '\n');
808: }
809:
810: static void
811: dosubsection(struct texi *p, enum texicmd cmd,
812: const char *buf, size_t sz, size_t *pos)
813: {
814:
815: if (TEXI_IGN & p->flags) {
816: advanceeoln(p, buf, sz, pos);
817: return;
818: }
819: while (*pos < sz && ' ' == buf[*pos])
820: advance(p, buf, pos);
821: texifputs(p, ".Pp");
822: while (*pos < sz && '\n' != buf[*pos]) {
823: texiputchar(p, buf[*pos]);
824: advance(p, buf, pos);
825: }
826: texifputs(p, ".Pp");
827: }
828:
829: static void
830: dosection(struct texi *p, enum texicmd cmd,
831: const char *buf, size_t sz, size_t *pos)
832: {
833:
834: if (TEXI_IGN & p->flags) {
835: advanceeoln(p, buf, sz, pos);
836: return;
837: }
838: while (*pos < sz && ' ' == buf[*pos])
839: advance(p, buf, pos);
840: texifputs(p, ".Ss ");
841: while (*pos < sz && '\n' != buf[*pos]) {
842: texiputchar(p, buf[*pos]);
843: advance(p, buf, pos);
844: }
845: texiputchar(p, '\n');
846: }
847:
848: static void
849: dosh(struct texi *p, enum texicmd cmd,
850: const char *buf, size_t sz, size_t *pos)
851: {
852:
853: if (TEXI_IGN & p->flags) {
854: advanceeoln(p, buf, sz, pos);
855: return;
856: }
857: while (*pos < sz && ' ' == buf[*pos])
858: advance(p, buf, pos);
859: texifputs(p, ".Sh ");
860: while (*pos < sz && '\n' != buf[*pos]) {
861: texiputchar(p, toupper(buf[*pos]));
862: advance(p, buf, pos);
863: }
864: texiputchar(p, '\n');
865: }
866:
867: static void
868: dotop(struct texi *p, enum texicmd cmd,
869: const char *buf, size_t sz, size_t *pos)
870: {
871:
872: p->flags &= ~TEXI_HEADER;
873: advanceeoln(p, buf, sz, pos);
874: teximacro(p, ".Dd $Mdocdate$");
875: teximacro(p, ".Dt SOMETHING 7");
876: teximacro(p, ".Os");
877: teximacro(p, ".Sh NAME");
878: teximacro(p, ".Nm Something");
879: teximacro(p, ".Nd Something");
880: }
881:
882: static void
883: doitem(struct texi *p, enum texicmd cmd,
884: const char *buf, size_t sz, size_t *pos)
885: {
886: size_t end;
887:
888: /* See if we have arguments... */
889: for (end = *pos; end < sz; end++)
890: if (' ' != buf[end] && '\t' != buf[end])
891: break;
892:
893: /* If we have arguments, print them too. */
894: if ('\n' != buf[end]) {
895: texifputs(p, ".It");
896: /* FIXME: process commands. */
897: while (*pos < sz && '\n' != buf[*pos]) {
898: texiputchar(p, buf[*pos]);
899: advance(p, buf, pos);
900: }
901: texiputchar(p, '\n');
902: } else
903: teximacro(p, ".It");
904: }
905:
906: static void
907: dotable(struct texi *p, enum texicmd cmd,
908: const char *buf, size_t sz, size_t *pos)
909: {
910:
911: teximacro(p, ".Bl -tag -width Ds");
912: parseto(p, buf, sz, pos, "table");
913: teximacro(p, ".El");
914: }
915:
916: static void
917: doitemize(struct texi *p, enum texicmd cmd,
918: const char *buf, size_t sz, size_t *pos)
919: {
920:
921: teximacro(p, ".Bl -bullet");
922: parseto(p, buf, sz, pos, "itemize");
923: teximacro(p, ".El");
924: }
925:
926: static void
927: doignbracket(struct texi *p, enum texicmd cmd,
928: const char *buf, size_t sz, size_t *pos)
929: {
930: unsigned int sv = p->flags;
931:
932: p->flags |= TEXI_IGN;
933: parsebracket(p, buf, sz, pos);
934: p->flags = sv;
935: }
936:
937: static void
938: doignline(struct texi *p, enum texicmd cmd,
939: const char *buf, size_t sz, size_t *pos)
940: {
941:
942: advanceeoln(p, buf, sz, pos);
943: if (*pos < sz)
944: advance(p, buf, pos);
945: }
946:
947: static int
948: parsefile(struct texi *p, const char *fname)
949: {
950: struct texifile *f;
951: int fd;
952: struct stat st;
953:
954: assert(p->filepos < 64);
955: f = &p->files[p->filepos];
956: memset(f, 0, sizeof(struct texifile));
957:
958: f->name = fname;
959: if (-1 == (fd = open(fname, O_RDONLY, 0))) {
960: texifatal(p, fname);
961: } else if (-1 == fstat(fd, &st)) {
962: close(fd);
963: texifatal(p, fname);
964: }
965:
966: f->mapsz = st.st_size;
967: f->map = mmap(NULL, f->mapsz,
968: PROT_READ, MAP_SHARED, fd, 0);
969: close(fd);
970:
971: if (MAP_FAILED == f->map) {
972: texifatal(p, fname);
973: return(0);
974: }
975:
976: p->filepos++;
977: parseeof(p, f->map, f->mapsz);
978: texifilepop(p);
979: return(1);
980: }
981:
982: int
983: main(int argc, char *argv[])
984: {
985: struct texi texi;
986: int c, rc;
987: const char *progname;
988:
989: progname = strrchr(argv[0], '/');
990: if (progname == NULL)
991: progname = argv[0];
992: else
993: ++progname;
994:
995: while (-1 != (c = getopt(argc, argv, "")))
996: switch (c) {
997: default:
998: goto usage;
999: }
1000:
1001: argv += optind;
1002: if (0 == (argc -= optind))
1003: goto usage;
1004:
1005: memset(&texi, 0, sizeof(struct texi));
1006: texi.flags = TEXI_HEADER;
1007: rc = parsefile(&texi, argv[0]);
1008: return(rc ? EXIT_SUCCESS : EXIT_FAILURE);
1009:
1010: usage:
1011: fprintf(stderr, "usage: %s file\n", progname);
1012: return(EXIT_FAILURE);
1013: }
CVSweb