Annotation of texi2mdoc/util.c, Revision 1.6
1.6 ! kristaps 1: /* $Id: util.c,v 1.5 2015/02/21 21:49: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>
24: #include <libgen.h>
25: #include <limits.h>
26: #include <stdarg.h>
27: #include <stdio.h>
28: #include <stdlib.h>
29: #include <string.h>
30: #include <time.h>
31: #include <unistd.h>
32:
33: #include "extern.h"
34:
35: /*
36: * Unmap the top-most file in the stack of files currently opened (that
37: * is, nested calls to parsefile()).
38: */
39: void
40: texifilepop(struct texi *p)
41: {
42: struct texifile *f;
43:
44: assert(p->filepos > 0);
45: f = &p->files[--p->filepos];
46: munmap(f->map, f->mapsz);
47: }
48:
49: /*
50: * Unmap all files that we're currently using and free all resources
51: * that we've allocated during the parse.
52: * The utility should exit(...) after this is called.
53: */
54: void
55: texiexit(struct texi *p)
56: {
57: size_t i;
58:
59: /* Make sure we're newline-terminated. */
60: if (p->outcol)
61: putchar('\n');
62:
63: /* Unmap all files. */
64: while (p->filepos > 0)
65: texifilepop(p);
66:
67: for (i = 0; i < p->dirsz; i++)
68: free(p->dirs[i]);
69:
1.4 kristaps 70: for (i = 0; i < p->indexsz; i++)
71: free(p->indexs[i]);
72:
1.1 kristaps 73: for (i = 0; i < p->valsz; i++) {
74: free(p->vals[i].value);
75: free(p->vals[i].key);
76: }
77:
78: free(p->vals);
1.4 kristaps 79: free(p->indexs);
1.1 kristaps 80: free(p->dirs);
81: free(p->subtitle);
82: free(p->title);
83: }
84:
85: /*
86: * Fatal error: unmap all files and exit.
87: * The "errstring" is passed to perror(3).
88: */
89: void
90: texiabort(struct texi *p, const char *errstring)
91: {
92:
93: perror(errstring);
94: texiexit(p);
95: exit(EXIT_FAILURE);
96: }
97:
98: /*
99: * Print a generic warning message (to stderr) tied to our current
100: * location in the parse sequence.
101: */
102: void
103: texiwarn(const struct texi *p, const char *fmt, ...)
104: {
105: va_list ap;
106:
107: fprintf(stderr, "%s:%zu:%zu: warning: ",
108: p->files[p->filepos - 1].name,
109: p->files[p->filepos - 1].line + 1,
110: p->files[p->filepos - 1].col + 1);
111: va_start(ap, fmt);
112: vfprintf(stderr, fmt, ap);
113: va_end(ap);
114: fputc('\n', stderr);
115: }
116:
117: /*
118: * Print an error message (to stderr) tied to our current location in
119: * the parse sequence, invoke texiexit(), then die.
120: */
121: void
122: texierr(struct texi *p, const char *fmt, ...)
123: {
124: va_list ap;
125:
126: fprintf(stderr, "%s:%zu:%zu: error: ",
127: p->files[p->filepos - 1].name,
128: p->files[p->filepos - 1].line + 1,
129: p->files[p->filepos - 1].col + 1);
130: va_start(ap, fmt);
131: vfprintf(stderr, fmt, ap);
132: va_end(ap);
133: fputc('\n', stderr);
134: texiexit(p);
135: exit(EXIT_FAILURE);
136: }
137:
138: /*
139: * Put a single data character to the output if we're not ignoring.
140: * Makes sure we don't spurriously start a macro.
141: * Adjusts our output status.
142: * This shouldn't be called for macros: just for ordinary text.
143: */
144: void
145: texiputchar(struct texi *p, char c)
146: {
147:
148: if (p->ign)
149: return;
150:
151: if ('.' == c && 0 == p->outcol)
152: fputs("\\&", stdout);
153:
154: putchar(c);
155: p->seenvs = 0;
156: if ('\n' == c) {
157: p->outcol = 0;
158: p->seenws = 0;
159: } else
160: p->outcol++;
161: }
162:
163: /*
164: * Put multiple characters (see texiputchar()).
165: * This shouldn't be called for macros: just for ordinary text.
166: */
167: void
168: texiputchars(struct texi *p, const char *s)
169: {
170:
171: while ('\0' != *s)
172: texiputchar(p, *s++);
173: }
174:
175: /*
176: * Close an mdoc(7) macro opened with teximacroopen().
177: * If there are no more macros on the line, prints a newline.
178: */
179: void
180: teximacroclose(struct texi *p)
181: {
182:
183: if (p->ign)
184: return;
185:
186: if (0 == --p->outmacro) {
187: putchar('\n');
188: p->outcol = p->seenws = 0;
189: }
190: }
191:
192: /*
193: * Open a mdoc(7) macro.
194: * This is used for line macros, e.g., Qq [foo bar baz].
195: * It can be invoked for nested macros, e.g., Qq Li foo .
196: * TODO: flush-right punctuation (e.g., parenthesis).
197: */
198: void
199: teximacroopen(struct texi *p, const char *s)
200: {
201: int rc;
202:
203: if (p->ign)
204: return;
205:
206: if (p->outcol && 0 == p->outmacro) {
207: putchar('\n');
208: p->outcol = 0;
209: }
210:
211: if (0 == p->outmacro)
212: putchar('.');
213: else
214: putchar(' ');
215:
216: if (EOF != (rc = fputs(s, stdout)))
217: p->outcol += rc;
218:
219: putchar(' ');
220: p->outcol++;
221: p->outmacro++;
222: p->seenws = 0;
223: }
224:
225: /*
226: * Put a stadnalone mdoc(7) command with the trailing newline.
227: */
228: void
229: teximacro(struct texi *p, const char *s)
230: {
231:
232: if (p->ign)
233: return;
234:
235: if (p->outmacro)
236: texierr(p, "\"%s\" in open line scope!?", s);
237: if (p->literal)
238: texierr(p, "\"%s\" in a literal scope!?", s);
239:
240: if (p->outcol)
241: putchar('\n');
242:
243: putchar('.');
244: puts(s);
245: p->outcol = p->seenws = 0;
246: }
247:
248: /*
249: * Introduce vertical space during normal (non-macro) input.
250: */
251: void
252: texivspace(struct texi *p)
253: {
254:
1.5 kristaps 255: if (p->seenvs || TEXILIST_TABLE == p->list)
1.1 kristaps 256: return;
257: teximacro(p, "Pp");
258: p->seenvs = 1;
259: }
260:
261: /*
262: * Advance by a single byte in the input stream, adjusting our location
263: * in the current input file.
264: */
265: void
266: advance(struct texi *p, const char *buf, size_t *pos)
267: {
268:
269: if ('\n' == buf[*pos]) {
270: p->files[p->filepos - 1].line++;
271: p->files[p->filepos - 1].col = 0;
272: } else
273: p->files[p->filepos - 1].col++;
274:
275: (*pos)++;
276: }
277:
278: /*
279: * It's common to wait punctuation to float on the right side of macro
280: * lines in mdoc(7), e.g., ".Em hello ) ."
281: * This function does so, and should be called before teximacroclose().
282: * It will detect that it's the last in the nested macros and
283: * appropriately flush-left punctuation alongside the macro.
284: */
285: void
286: texipunctuate(struct texi *p, const char *buf, size_t sz, size_t *pos)
287: {
288: size_t start, end;
289:
290: if (1 != p->outmacro)
291: return;
292:
293: for (start = end = *pos; end < sz; end++) {
294: switch (buf[end]) {
295: case (','):
296: case (')'):
297: case ('.'):
298: case ('"'):
299: case (':'):
300: case ('!'):
301: case ('?'):
302: continue;
303: default:
304: break;
305: }
306: break;
307: }
308: if (end == *pos)
309: return;
310: if (end + 1 == sz || ' ' == buf[end] || '\n' == buf[end]) {
311: for ( ; start < end; start++) {
312: texiputchar(p, ' ');
313: texiputchar(p, buf[start]);
314: advance(p, buf, pos);
315: }
316: }
317: }
318:
319: /*
320: * Advance to the next non-whitespace word in the input stream.
321: * If we're in literal mode, then print all of the whitespace as we're
322: * doing so.
323: */
324: static size_t
325: advancenext(struct texi *p, const char *buf, size_t sz, size_t *pos)
326: {
327:
328: if (p->literal) {
329: while (*pos < sz && ismspace(buf[*pos])) {
330: if (*pos && '\n' == buf[*pos] &&
331: '\\' == buf[*pos - 1])
332: texiputchar(p, 'e');
333: texiputchar(p, buf[*pos]);
334: advance(p, buf, pos);
335: }
336: return(*pos);
337: }
338:
339: while (*pos < sz && ismspace(buf[*pos])) {
340: p->seenws = 1;
341: /*
342: * If it looks like we've printed a double-line, then
343: * output a paragraph.
344: * FIXME: this is stupid.
345: */
346: if (*pos && '\n' == buf[*pos] && '\n' == buf[*pos - 1])
347: texivspace(p);
348: advance(p, buf, pos);
349: }
350: return(*pos);
351: }
352:
353: /*
354: * Advance to the EOLN in the input stream.
355: * NOTE: THIS SHOULD NOT BE CALLED ON BLANK TEXT, as it will read up to
356: * the @\n.
357: */
358: size_t
359: advanceeoln(struct texi *p, const char *buf,
360: size_t sz, size_t *pos, int consumenl)
361: {
362:
363: while (*pos < sz && '\n' != buf[*pos])
364: advance(p, buf, pos);
365: if (*pos < sz && consumenl)
366: advance(p, buf, pos);
367: return(*pos);
368: }
369:
370: /*
371: * Advance to position "end", which is an absolute position in the
372: * current buffer greater than or equal to the current position.
373: */
374: void
375: advanceto(struct texi *p, const char *buf, size_t *pos, size_t end)
376: {
377:
378: assert(*pos <= end);
379: while (*pos < end)
380: advance(p, buf, pos);
381: }
382:
383: /*
384: * Output a free-form word in the input stream, progressing to the next
385: * command or white-space.
386: * This also will advance the input stream.
387: */
388: static void
389: texiword(struct texi *p, const char *buf,
390: size_t sz, size_t *pos, char extra)
391: {
392:
393: if (p->seenws && 0 == p->outmacro &&
394: p->outcol > 72 && 0 == p->literal)
395: texiputchar(p, '\n');
396: /* FIXME: abstract this: we use it elsewhere. */
397: if (p->seenws && p->outcol && 0 == p->literal)
398: texiputchar(p, ' ');
399:
400: p->seenws = 0;
401:
402: while (*pos < sz && ! ismspace(buf[*pos])) {
403: switch (buf[*pos]) {
404: case ('@'):
405: case ('}'):
406: case ('{'):
407: return;
408: }
409: if ('\0' != extra && buf[*pos] == extra)
410: return;
411: if (*pos < sz - 1 &&
412: '`' == buf[*pos] &&
413: '`' == buf[*pos + 1]) {
414: texiputchars(p, "\\(lq");
415: advance(p, buf, pos);
416: } else if (*pos < sz - 1 &&
417: '\'' == buf[*pos] &&
418: '\'' == buf[*pos + 1]) {
419: texiputchars(p, "\\(rq");
420: advance(p, buf, pos);
421: } else
422: texiputchar(p, buf[*pos]);
423: advance(p, buf, pos);
424: }
425: }
426:
427: /*
428: * Look up the command at position "pos" in the buffer, returning it (or
429: * TEXICMD__MAX if none found) and setting "end" to be the absolute
430: * index after the command name.
431: */
432: enum texicmd
433: texicmd(struct texi *p, const char *buf,
434: size_t pos, size_t sz, size_t *end)
435: {
1.4 kristaps 436: size_t i, len, toksz;
1.1 kristaps 437:
438: assert('@' == buf[pos]);
439:
440: if ((*end = pos) == sz)
441: return(TEXICMD__MAX);
442: else if ((*end = ++pos) == sz)
443: return(TEXICMD__MAX);
444:
445: /* Alphabetic commands are special. */
446: if ( ! isalpha(buf[pos])) {
447: if ((*end = pos + 1) == sz)
448: return(TEXICMD__MAX);
449: for (i = 0; i < TEXICMD__MAX; i++) {
450: if (1 != texitoks[i].len)
451: continue;
452: if (0 == strncmp(texitoks[i].tok, &buf[pos], 1))
453: return(i);
454: }
455: texiwarn(p, "bad command: @%c", buf[pos]);
456: return(TEXICMD__MAX);
457: }
458:
1.4 kristaps 459: /* Scan to the end of the possible command name. */
1.1 kristaps 460: for (*end = pos; *end < sz && ! ismspace(buf[*end]); (*end)++)
461: if ((*end > pos && ('@' == buf[*end] ||
462: '{' == buf[*end] || '}' == buf[*end])))
463: break;
464:
1.4 kristaps 465: /* Look for the command. */
1.1 kristaps 466: len = *end - pos;
467: for (i = 0; i < TEXICMD__MAX; i++) {
468: if (len != texitoks[i].len)
469: continue;
470: if (0 == strncmp(texitoks[i].tok, &buf[pos], len))
471: return(i);
472: }
473:
1.4 kristaps 474: /* Look for it in our indices. */
475: for (i = 0; i < p->indexsz; i++) {
476: toksz = strlen(p->indexs[i]);
477: if (len != 5 + toksz)
478: continue;
479: if (strncmp(&buf[pos], p->indexs[i], toksz))
480: continue;
481: if (0 == strncmp(&buf[pos + toksz], "index", 5))
482: return(TEXICMD_INDEX);
483: }
484:
1.1 kristaps 485: texiwarn(p, "bad command: @%.*s", (int)len, &buf[pos]);
486: return(TEXICMD__MAX);
487: }
488:
489: /*
490: * Parse an argument from a bracketed command, e.g., @url{foo, baz}.
491: * Num should be set to the argument we're currently parsing, although
492: * it suffixes for it to be zero or non-zero.
493: * This will return 1 if there are more arguments, 0 otherwise.
494: * This will stop (returning 0) in the event of EOF or if we're not at a
495: * bracket for the zeroth parse.
496: */
497: int
498: parsearg(struct texi *p, const char *buf,
499: size_t sz, size_t *pos, size_t num)
500: {
501: size_t end;
502: enum texicmd cmd;
503:
504: while (*pos < sz && ismspace(buf[*pos]))
505: advance(p, buf, pos);
506: if (*pos == sz || (0 == num && '{' != buf[*pos]))
507: return(0);
508: if (0 == num)
509: advance(p, buf, pos);
510:
511: while ((*pos = advancenext(p, buf, sz, pos)) < sz) {
512: switch (buf[*pos]) {
513: case (','):
514: advance(p, buf, pos);
515: return(1);
516: case ('}'):
517: advance(p, buf, pos);
518: return(0);
519: case ('{'):
520: if (0 == p->ign)
521: texiwarn(p, "unexpected \"{\"");
522: advance(p, buf, pos);
523: continue;
524: case ('@'):
525: break;
526: default:
527: texiword(p, buf, sz, pos, ',');
528: continue;
529: }
530:
531: cmd = texicmd(p, buf, *pos, sz, &end);
532: advanceto(p, buf, pos, end);
533: if (TEXICMD__MAX == cmd)
534: continue;
535: if (NULL != texitoks[cmd].fp)
536: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
537: }
538: return(0);
539: }
540:
541: /*
542: * Parse until the end of a bracketed statement, e.g., @foo{bar baz}.
543: * This will stop in the event of EOF or if we're not at a bracket.
544: */
545: void
546: parsebracket(struct texi *p, const char *buf, size_t sz, size_t *pos)
547: {
548: size_t end;
549: enum texicmd cmd;
550:
551: while (*pos < sz && ismspace(buf[*pos]))
552: advance(p, buf, pos);
553:
554: if (*pos == sz || '{' != buf[*pos])
555: return;
556: advance(p, buf, pos);
557:
558: while ((*pos = advancenext(p, buf, sz, pos)) < sz) {
559: switch (buf[*pos]) {
560: case ('}'):
561: advance(p, buf, pos);
562: return;
563: case ('{'):
564: if (0 == p->ign)
565: texiwarn(p, "unexpected \"{\"");
566: advance(p, buf, pos);
567: continue;
568: case ('@'):
569: break;
570: default:
571: texiword(p, buf, sz, pos, '\0');
572: continue;
573: }
574:
575: cmd = texicmd(p, buf, *pos, sz, &end);
576: advanceto(p, buf, pos, end);
577: if (TEXICMD__MAX == cmd)
578: continue;
579: if (NULL != texitoks[cmd].fp)
580: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
581: }
582: }
583:
584: /*
585: * This should be invoked when we're on a macro line and want to process
586: * to the end of the current input line, doing all of our macros along
587: * the way.
588: */
589: void
590: parseeoln(struct texi *p, const char *buf, size_t sz, size_t *pos)
591: {
592: size_t end;
593: enum texicmd cmd;
594:
595: while (*pos < sz && '\n' != buf[*pos]) {
596: while (*pos < sz && isws(buf[*pos])) {
597: p->seenws = 1;
598: if (p->literal)
599: texiputchar(p, buf[*pos]);
600: advance(p, buf, pos);
601: }
602: switch (buf[*pos]) {
603: case ('}'):
604: if (0 == p->ign)
605: texiwarn(p, "unexpected \"}\"");
606: advance(p, buf, pos);
607: continue;
608: case ('{'):
609: if (0 == p->ign)
610: texiwarn(p, "unexpected \"{\"");
611: advance(p, buf, pos);
612: continue;
613: case ('@'):
614: break;
615: default:
616: texiword(p, buf, sz, pos, '\0');
617: continue;
618: }
619:
620: cmd = texicmd(p, buf, *pos, sz, &end);
621: advanceto(p, buf, pos, end);
622: if (TEXICMD__MAX == cmd)
623: continue;
624: if (NULL != texitoks[cmd].fp)
625: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
626: }
627: }
628:
629: /*
630: * Parse a single word or command.
631: * This will return immediately at the EOF.
632: */
633: void
634: parsesingle(struct texi *p, const char *buf, size_t sz, size_t *pos)
635: {
636: size_t end;
637: enum texicmd cmd;
638:
639: if ((*pos = advancenext(p, buf, sz, pos)) >= sz)
640: return;
641:
642: switch (buf[*pos]) {
643: case ('}'):
644: if (0 == p->ign)
645: texiwarn(p, "unexpected \"}\"");
646: advance(p, buf, pos);
647: return;
648: case ('{'):
649: if (0 == p->ign)
650: texiwarn(p, "unexpected \"{\"");
651: advance(p, buf, pos);
652: return;
653: case ('@'):
654: break;
655: default:
656: texiword(p, buf, sz, pos, '\0');
657: return;
658: }
659:
660: cmd = texicmd(p, buf, *pos, sz, &end);
661: advanceto(p, buf, pos, end);
662: if (TEXICMD__MAX == cmd)
663: return;
664: if (NULL != texitoks[cmd].fp)
665: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
666: }
667:
668: /*
669: * This is used in the @deffn type of command.
670: * These have an arbitrary number of line arguments; however, these
671: * arguments may or may not be surrounded by brackets.
672: * In this function, we parse each one as either a bracketed or
673: * non-bracketed argument, returning 0 when we've reached the end of
674: * line or 1 otherwise.
675: */
676: int
677: parselinearg(struct texi *p, const char *buf, size_t sz, size_t *pos)
678: {
679:
680: while (*pos < sz && isws(buf[*pos])) {
681: p->seenws = 1;
682: advance(p, buf, pos);
683: }
684:
685: if (*pos < sz && '{' == buf[*pos])
686: parsebracket(p, buf, sz, pos);
1.3 kristaps 687: else if (*pos < sz && '\n' != buf[*pos])
1.1 kristaps 688: parsesingle(p, buf, sz, pos);
689: else
690: return(0);
691:
692: return(1);
693: }
694:
695: /*
696: * Parse til the end of the buffer.
697: */
698: void
699: parseeof(struct texi *p, const char *buf, size_t sz)
700: {
701: size_t pos;
702:
703: for (pos = 0; pos < sz; )
704: parsesingle(p, buf, sz, &pos);
705: }
706:
707: /*
708: * Parse a block sequence until we have the "@end endtoken" command
709: * invocation.
710: * This will return immediately at EOF.
711: */
712: void
713: parseto(struct texi *p, const char *buf,
714: size_t sz, size_t *pos, const char *endtoken)
715: {
716: size_t end;
717: enum texicmd cmd;
718: size_t endtoksz;
719:
720: endtoksz = strlen(endtoken);
721: assert(endtoksz > 0);
722:
723: while ((*pos = advancenext(p, buf, sz, pos)) < sz) {
724: switch (buf[*pos]) {
725: case ('}'):
726: if (0 == p->ign)
727: texiwarn(p, "unexpected \"}\"");
728: advance(p, buf, pos);
729: continue;
730: case ('{'):
731: if (0 == p->ign)
732: texiwarn(p, "unexpected \"{\"");
733: advance(p, buf, pos);
734: continue;
735: case ('@'):
736: break;
737: default:
738: texiword(p, buf, sz, pos, '\0');
739: continue;
740: }
741:
742: cmd = texicmd(p, buf, *pos, sz, &end);
743: advanceto(p, buf, pos, end);
744: if (TEXICMD_END == cmd) {
745: while (*pos < sz && isws(buf[*pos]))
746: advance(p, buf, pos);
747: /*
748: * FIXME: check the full word, not just its
749: * initial substring!
750: */
751: if (sz - *pos >= endtoksz && 0 == strncmp
752: (&buf[*pos], endtoken, endtoksz)) {
753: advanceeoln(p, buf, sz, pos, 0);
754: break;
755: }
756: if (0 == p->ign)
757: texiwarn(p, "unexpected \"end\"");
758: advanceeoln(p, buf, sz, pos, 0);
759: continue;
760: } else if (TEXICMD__MAX != cmd)
761: if (NULL != texitoks[cmd].fp)
762: (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);
763: }
764: }
765:
766: /*
767: * Memory-map the file "fname" and begin parsing it unless "parse" is
768: * zero, in which case we just dump the file to stdout (making sure it
769: * doesn't trip up mdoc(7) along the way).
770: * This can be called in a nested context.
771: */
772: void
773: parsefile(struct texi *p, const char *fname, int parse)
774: {
775: struct texifile *f;
776: int fd;
777: struct stat st;
778: size_t i;
779:
1.5 kristaps 780: if (64 == p->filepos)
1.6 ! kristaps 781: texierr(p, "too many open files");
1.1 kristaps 782: f = &p->files[p->filepos];
783: memset(f, 0, sizeof(struct texifile));
784:
785: f->name = fname;
786: if (-1 == (fd = open(fname, O_RDONLY, 0))) {
787: texiabort(p, fname);
788: } else if (-1 == fstat(fd, &st)) {
789: close(fd);
790: texiabort(p, fname);
791: }
792:
793: f->mapsz = st.st_size;
794: f->map = mmap(NULL, f->mapsz,
795: PROT_READ, MAP_SHARED, fd, 0);
796: close(fd);
797:
798: if (MAP_FAILED == f->map)
799: texiabort(p, fname);
800:
801: p->filepos++;
802: if ( ! parse) {
803: /*
804: * We're printing verbatim output.
805: * Make sure it doesn't get interpreted as mdoc by
806: * escaping escapes and making sure leading dots don't
807: * trigger mdoc(7) expansion.
808: */
809: for (i = 0; i < f->mapsz; i++) {
810: if (i > 0 && '.' == f->map[i])
811: if ('\n' == f->map[i - 1])
812: fputs("\\&", stdout);
813: putchar(f->map[i]);
814: if ('\\' == f->map[i])
815: putchar('e');
816: }
817: } else
818: parseeof(p, f->map, f->mapsz);
819: texifilepop(p);
820: }
821:
1.2 kristaps 822: /*
823: * Look up the value to a stored pair's value starting in "buf" from
824: * start to end.
825: * Return the pointer to the value memory, which can be NULL if the
826: * pointer key does not exist.
827: * The pointer can point to NULL if the value has been unset.
828: */
829: static char **
830: valuequery(const struct texi *p,
831: const char *buf, size_t start, size_t end)
832: {
833: size_t i, sz, len;
834:
835: assert(end >= start);
836: /* Ignore zero-length. */
837: if (0 == (len = (end - start)))
838: return(NULL);
839: for (i = 0; i < p->valsz; i++) {
840: sz = strlen(p->vals[i].key);
841: if (sz != len)
842: continue;
843: if (0 == strncmp(p->vals[i].key, &buf[start], len))
844: return(&p->vals[i].value);
845: }
846: return(NULL);
847: }
848:
849: /*
850: * Parse a key until the end of line, e.g., @clear foo\n, and return the
851: * pointer to its value via valuequery().
852: */
853: static char **
854: valuelquery(struct texi *p, const char *buf, size_t sz, size_t *pos)
855: {
856: size_t start, end;
857: char **ret;
858:
859: while (*pos < sz && isws(buf[*pos]))
860: advance(p, buf, pos);
861: if (*pos == sz)
862: return(NULL);
863: for (start = end = *pos; end < sz; end++)
864: if ('\n' == buf[end])
865: break;
866: advanceto(p, buf, pos, end);
867: if (*pos < sz) {
868: assert('\n' == buf[*pos]);
869: advance(p, buf, pos);
870: }
871: if (NULL == (ret = valuequery(p, buf, start, end)))
872: return(NULL);
873: return(ret);
874: }
875:
876: void
877: valuelclear(struct texi *p, const char *buf, size_t sz, size_t *pos)
878: {
879: char **ret;
880:
881: if (NULL == (ret = valuelquery(p, buf, sz, pos)))
882: return;
883: free(*ret);
884: *ret = NULL;
885: }
886:
887: const char *
888: valuellookup(struct texi *p, const char *buf, size_t sz, size_t *pos)
889: {
890: char **ret;
891:
892: if (NULL == (ret = valuelquery(p, buf, sz, pos)))
893: return(NULL);
894: return(*ret);
895: }
896:
897: /*
898: * Parse a key from a bracketed string, e.g., @value{foo}, and return
899: * the pointer to its value.
900: * If the returned pointer is NULL, either there was no string within
901: * the brackets (or no brackets), or the value was not found, or the
902: * value had previously been unset.
903: */
904: const char *
905: valueblookup(struct texi *p, const char *buf, size_t sz, size_t *pos)
906: {
907: size_t start, end;
908: char **ret;
909:
910: while (*pos < sz && isws(buf[*pos]))
911: advance(p, buf, pos);
912: if (*pos == sz || '{' != buf[*pos])
913: return(NULL);
914: advance(p, buf, pos);
915: for (start = end = *pos; end < sz; end++)
916: if ('}' == buf[end])
917: break;
918: advanceto(p, buf, pos, end);
919: if (*pos < sz) {
920: assert('}' == buf[*pos]);
921: advance(p, buf, pos);
922: }
923: if (NULL == (ret = valuequery(p, buf, start, end)))
924: return(NULL);
925: return(*ret);
926: }
927:
928: void
929: valueadd(struct texi *p, char *key, char *val)
930: {
931: size_t i;
932:
933: assert(NULL != key);
934: assert(NULL != val);
935:
936: for (i = 0; i < p->valsz; i++)
937: if (0 == strcmp(p->vals[i].key, key))
938: break;
939:
940: if (i < p->valsz) {
941: free(key);
942: free(p->vals[i].value);
943: p->vals[i].value = val;
944: } else {
1.4 kristaps 945: /* FIXME: reallocarray() */
1.2 kristaps 946: p->vals = realloc(p->vals,
947: (p->valsz + 1) *
948: sizeof(struct texivalue));
1.4 kristaps 949: if (NULL == p->vals)
950: texiabort(p, NULL);
1.2 kristaps 951: p->vals[p->valsz].key = key;
952: p->vals[p->valsz].value = val;
953: p->valsz++;
954: }
955: }
CVSweb