version 1.2, 2015/02/20 12:25:25 |
version 1.22, 2015/03/01 16:57:39 |
|
|
#include <assert.h> |
#include <assert.h> |
#include <ctype.h> |
#include <ctype.h> |
#include <fcntl.h> |
#include <fcntl.h> |
#include <getopt.h> |
|
#include <libgen.h> |
|
#include <limits.h> |
#include <limits.h> |
#include <stdarg.h> |
#include <stdarg.h> |
#include <stdio.h> |
#include <stdio.h> |
Line 43 texifilepop(struct texi *p) |
|
Line 41 texifilepop(struct texi *p) |
|
|
|
assert(p->filepos > 0); |
assert(p->filepos > 0); |
f = &p->files[--p->filepos]; |
f = &p->files[--p->filepos]; |
munmap(f->map, f->mapsz); |
free(f->map); |
} |
} |
|
|
|
static void |
|
teximacrofree(struct teximacro *p) |
|
{ |
|
size_t i; |
|
|
|
for (i = 0; i < p->argsz; i++) |
|
free(p->args[i]); |
|
|
|
free(p->args); |
|
free(p->key); |
|
free(p->value); |
|
} |
|
|
|
static void |
|
texivaluefree(struct texivalue *p) |
|
{ |
|
|
|
free(p->key); |
|
free(p->value); |
|
} |
|
|
/* |
/* |
* Unmap all files that we're currently using and free all resources |
* Unmap all files that we're currently using and free all resources |
* that we've allocated during the parse. |
* that we've allocated during the parse. |
Line 58 texiexit(struct texi *p) |
|
Line 77 texiexit(struct texi *p) |
|
|
|
/* Make sure we're newline-terminated. */ |
/* Make sure we're newline-terminated. */ |
if (p->outcol) |
if (p->outcol) |
putchar('\n'); |
fputc('\n', p->outfile); |
|
if (NULL != p->chapters) |
|
teximdocclose(p, 1); |
|
|
/* Unmap all files. */ |
/* Unmap all files. */ |
while (p->filepos > 0) |
while (p->filepos > 0) |
texifilepop(p); |
texifilepop(p); |
|
|
|
for (i = 0; i < p->macrosz; i++) |
|
teximacrofree(&p->macros[i]); |
for (i = 0; i < p->dirsz; i++) |
for (i = 0; i < p->dirsz; i++) |
free(p->dirs[i]); |
free(p->dirs[i]); |
|
for (i = 0; i < p->indexsz; i++) |
|
free(p->indexs[i]); |
|
for (i = 0; i < p->valsz; i++) |
|
texivaluefree(&p->vals[i]); |
|
|
for (i = 0; i < p->valsz; i++) { |
free(p->macros); |
free(p->vals[i].value); |
|
free(p->vals[i].key); |
|
} |
|
|
|
free(p->vals); |
free(p->vals); |
|
free(p->indexs); |
free(p->dirs); |
free(p->dirs); |
free(p->subtitle); |
free(p->subtitle); |
free(p->title); |
free(p->title); |
Line 98 texiabort(struct texi *p, const char *errstring) |
|
Line 122 texiabort(struct texi *p, const char *errstring) |
|
void |
void |
texiwarn(const struct texi *p, const char *fmt, ...) |
texiwarn(const struct texi *p, const char *fmt, ...) |
{ |
{ |
va_list ap; |
va_list ap; |
|
const struct texifile *f; |
|
|
fprintf(stderr, "%s:%zu:%zu: warning: ", |
f = &p->files[p->filepos - 1]; |
p->files[p->filepos - 1].name, |
|
p->files[p->filepos - 1].line + 1, |
if (f->insplice) |
p->files[p->filepos - 1].col + 1); |
fprintf(stderr, "%s:%zu:%zu (%zuB left in splice): " |
|
"warning: ", f->name, f->line + 1, |
|
f->col + 1, f->insplice); |
|
else |
|
fprintf(stderr, "%s:%zu:%zu: warning: ", |
|
f->name, f->line + 1, f->col + 1); |
|
|
va_start(ap, fmt); |
va_start(ap, fmt); |
vfprintf(stderr, fmt, ap); |
vfprintf(stderr, fmt, ap); |
va_end(ap); |
va_end(ap); |
Line 117 texiwarn(const struct texi *p, const char *fmt, ...) |
|
Line 148 texiwarn(const struct texi *p, const char *fmt, ...) |
|
void |
void |
texierr(struct texi *p, const char *fmt, ...) |
texierr(struct texi *p, const char *fmt, ...) |
{ |
{ |
va_list ap; |
va_list ap; |
|
struct texifile *f; |
|
|
fprintf(stderr, "%s:%zu:%zu: error: ", |
f = &p->files[p->filepos - 1]; |
p->files[p->filepos - 1].name, |
|
p->files[p->filepos - 1].line + 1, |
if (f->insplice) |
p->files[p->filepos - 1].col + 1); |
fprintf(stderr, "%s:%zu:%zu: (%zuB left in splice): " |
|
"error: ", f->name, f->line + 1, |
|
f->col + 1, f->insplice); |
|
else |
|
fprintf(stderr, "%s:%zu:%zu: error: ", |
|
f->name, f->line + 1, f->col + 1); |
|
|
va_start(ap, fmt); |
va_start(ap, fmt); |
vfprintf(stderr, fmt, ap); |
vfprintf(stderr, fmt, ap); |
va_end(ap); |
va_end(ap); |
Line 133 texierr(struct texi *p, const char *fmt, ...) |
|
Line 171 texierr(struct texi *p, const char *fmt, ...) |
|
|
|
/* |
/* |
* Put a single data character to the output if we're not ignoring. |
* Put a single data character to the output if we're not ignoring. |
* Makes sure we don't spurriously start a macro. |
* Escape starting a line with a control character and slashes. |
* Adjusts our output status. |
|
* This shouldn't be called for macros: just for ordinary text. |
|
*/ |
*/ |
void |
void |
texiputchar(struct texi *p, char c) |
texiputchar(struct texi *p, char c) |
Line 143 texiputchar(struct texi *p, char c) |
|
Line 179 texiputchar(struct texi *p, char c) |
|
|
|
if (p->ign) |
if (p->ign) |
return; |
return; |
|
|
if ('.' == c && 0 == p->outcol) |
if ('.' == c && 0 == p->outcol) |
fputs("\\&", stdout); |
fputs("\\&", p->outfile); |
|
if ('\'' == c && 0 == p->outcol) |
|
fputs("\\&", p->outfile); |
|
|
putchar(c); |
fputc(c, p->outfile); |
|
if ('\\' == c) |
|
fputc('e', p->outfile); |
p->seenvs = 0; |
p->seenvs = 0; |
if ('\n' == c) { |
if ('\n' == c) { |
p->outcol = 0; |
p->outcol = 0; |
Line 157 texiputchar(struct texi *p, char c) |
|
Line 196 texiputchar(struct texi *p, char c) |
|
} |
} |
|
|
/* |
/* |
* Put multiple characters (see texiputchar()). |
* Put an opaque series of characters. |
* This shouldn't be called for macros: just for ordinary text. |
* Characters starting a line with a control character are escaped, but |
|
* that's it, so don't use this for non-controlled sequences of text. |
*/ |
*/ |
void |
void |
texiputchars(struct texi *p, const char *s) |
texiputchars(struct texi *p, const char *s) |
{ |
{ |
|
|
while ('\0' != *s) |
if (p->ign) |
texiputchar(p, *s++); |
return; |
|
if ('.' == *s && 0 == p->outcol) |
|
fputs("\\&", p->outfile); |
|
if ('\'' == *s && 0 == p->outcol) |
|
fputs("\\&", p->outfile); |
|
p->outcol += fputs(s, p->outfile); |
|
p->seenvs = 0; |
} |
} |
|
|
/* |
/* |
|
* This puts all characters onto the output stream but makes sure to |
|
* escape mdoc(7) slashes. |
|
* FIXME: useless. |
|
*/ |
|
void |
|
texiputbuf(struct texi *p, size_t start, size_t end) |
|
{ |
|
|
|
for ( ; start < end; start++) |
|
texiputchar(p, BUF(p)[start]); |
|
} |
|
|
|
/* |
* Close an mdoc(7) macro opened with teximacroopen(). |
* Close an mdoc(7) macro opened with teximacroopen(). |
* If there are no more macros on the line, prints a newline. |
* If there are no more macros on the line, prints a newline. |
*/ |
*/ |
Line 180 teximacroclose(struct texi *p) |
|
Line 239 teximacroclose(struct texi *p) |
|
return; |
return; |
|
|
if (0 == --p->outmacro) { |
if (0 == --p->outmacro) { |
putchar('\n'); |
fputc('\n', p->outfile); |
p->outcol = p->seenws = 0; |
p->outcol = p->seenws = 0; |
} |
} |
} |
} |
Line 200 teximacroopen(struct texi *p, const char *s) |
|
Line 259 teximacroopen(struct texi *p, const char *s) |
|
return; |
return; |
|
|
if (p->outcol && 0 == p->outmacro) { |
if (p->outcol && 0 == p->outmacro) { |
putchar('\n'); |
fputc('\n', p->outfile); |
p->outcol = 0; |
p->outcol = 0; |
} |
} |
|
|
if (0 == p->outmacro) |
if (0 == p->outmacro) |
putchar('.'); |
fputc('.', p->outfile); |
else |
else |
putchar(' '); |
fputc(' ', p->outfile); |
|
|
if (EOF != (rc = fputs(s, stdout))) |
if (EOF != (rc = fputs(s, p->outfile))) |
p->outcol += rc; |
p->outcol += rc; |
|
|
putchar(' '); |
fputc(' ', p->outfile); |
p->outcol++; |
p->outcol++; |
p->outmacro++; |
p->outmacro++; |
p->seenws = 0; |
p->seenws = 0; |
Line 234 teximacro(struct texi *p, const char *s) |
|
Line 293 teximacro(struct texi *p, const char *s) |
|
texierr(p, "\"%s\" in a literal scope!?", s); |
texierr(p, "\"%s\" in a literal scope!?", s); |
|
|
if (p->outcol) |
if (p->outcol) |
putchar('\n'); |
fputc('\n', p->outfile); |
|
|
putchar('.'); |
fputc('.', p->outfile); |
puts(s); |
fputs(s, p->outfile); |
|
fputc('\n', p->outfile); |
p->outcol = p->seenws = 0; |
p->outcol = p->seenws = 0; |
} |
} |
|
|
|
|
texivspace(struct texi *p) |
texivspace(struct texi *p) |
{ |
{ |
|
|
if (p->seenvs) |
if (p->seenvs || TEXILIST_TABLE == p->list) |
return; |
return; |
teximacro(p, "Pp"); |
teximacro(p, "Pp"); |
p->seenvs = 1; |
p->seenvs = 1; |
Line 259 texivspace(struct texi *p) |
|
Line 319 texivspace(struct texi *p) |
|
* in the current input file. |
* in the current input file. |
*/ |
*/ |
void |
void |
advance(struct texi *p, const char *buf, size_t *pos) |
advance(struct texi *p, size_t *pos) |
{ |
{ |
|
struct texifile *f; |
|
|
if ('\n' == buf[*pos]) { |
f = &p->files[p->filepos - 1]; |
p->files[p->filepos - 1].line++; |
|
p->files[p->filepos - 1].col = 0; |
|
} else |
|
p->files[p->filepos - 1].col++; |
|
|
|
|
if (0 == f->insplice) { |
|
if ('\n' == BUF(p)[*pos]) { |
|
f->line++; |
|
f->col = 0; |
|
} else |
|
f->col++; |
|
} else { |
|
--f->insplice; |
|
if (0 == f->insplice) |
|
f->depth = 0; |
|
} |
|
|
(*pos)++; |
(*pos)++; |
} |
} |
|
|
Line 279 advance(struct texi *p, const char *buf, size_t *pos) |
|
Line 348 advance(struct texi *p, const char *buf, size_t *pos) |
|
* appropriately flush-left punctuation alongside the macro. |
* appropriately flush-left punctuation alongside the macro. |
*/ |
*/ |
void |
void |
texipunctuate(struct texi *p, const char *buf, size_t sz, size_t *pos) |
texipunctuate(struct texi *p, size_t *pos) |
{ |
{ |
size_t start, end; |
size_t start, end; |
|
|
if (1 != p->outmacro) |
if (1 != p->outmacro) |
return; |
return; |
|
|
for (start = end = *pos; end < sz; end++) { |
for (start = end = *pos; end < BUFSZ(p); end++) { |
switch (buf[end]) { |
switch (BUF(p)[end]) { |
case (','): |
case (','): |
case (')'): |
case (')'): |
case ('.'): |
case ('.'): |
case ('"'): |
case ('"'): |
case (':'): |
case (':'): |
|
case (';'): |
case ('!'): |
case ('!'): |
case ('?'): |
case ('?'): |
continue; |
continue; |
Line 303 texipunctuate(struct texi *p, const char *buf, size_t |
|
Line 373 texipunctuate(struct texi *p, const char *buf, size_t |
|
} |
} |
if (end == *pos) |
if (end == *pos) |
return; |
return; |
if (end + 1 == sz || ' ' == buf[end] || '\n' == buf[end]) { |
if (end + 1 == BUFSZ(p) || ' ' == BUF(p)[end] || |
|
'\n' == BUF(p)[end]) { |
for ( ; start < end; start++) { |
for ( ; start < end; start++) { |
texiputchar(p, ' '); |
texiputchar(p, ' '); |
texiputchar(p, buf[start]); |
texiputchar(p, BUF(p)[start]); |
advance(p, buf, pos); |
advance(p, pos); |
} |
} |
} |
} |
} |
} |
Line 318 texipunctuate(struct texi *p, const char *buf, size_t |
|
Line 389 texipunctuate(struct texi *p, const char *buf, size_t |
|
* doing so. |
* doing so. |
*/ |
*/ |
static size_t |
static size_t |
advancenext(struct texi *p, const char *buf, size_t sz, size_t *pos) |
advancenext(struct texi *p, size_t *pos) |
{ |
{ |
|
|
if (p->literal) { |
if (p->literal) { |
while (*pos < sz && ismspace(buf[*pos])) { |
while (*pos < BUFSZ(p) && ismspace(BUF(p)[*pos])) { |
if (*pos && '\n' == buf[*pos] && |
texiputchar(p, BUF(p)[*pos]); |
'\\' == buf[*pos - 1]) |
advance(p, pos); |
texiputchar(p, 'e'); |
|
texiputchar(p, buf[*pos]); |
|
advance(p, buf, pos); |
|
} |
} |
return(*pos); |
return(*pos); |
} |
} |
|
|
while (*pos < sz && ismspace(buf[*pos])) { |
while (*pos < BUFSZ(p) && ismspace(BUF(p)[*pos])) { |
p->seenws = 1; |
p->seenws = 1; |
/* |
/* |
* If it looks like we've printed a double-line, then |
* If it looks like we've printed a double-line, then |
* output a paragraph. |
* output a paragraph. |
* FIXME: this is stupid. |
* FIXME: this is stupid. |
*/ |
*/ |
if (*pos && '\n' == buf[*pos] && '\n' == buf[*pos - 1]) |
if (*pos && '\n' == BUF(p)[*pos] && '\n' == BUF(p)[*pos - 1]) |
texivspace(p); |
texivspace(p); |
advance(p, buf, pos); |
advance(p, pos); |
} |
} |
return(*pos); |
return(*pos); |
} |
} |
|
|
/* |
/* |
* Advance to the EOLN in the input stream. |
* Advance to the EOLN in the input stream. |
* NOTE: THIS SHOULD NOT BE CALLED ON BLANK TEXT, as it will read up to |
* This will skip over '@' markers in an effort to ignore escaped |
* the @\n. |
* newlines. |
*/ |
*/ |
size_t |
size_t |
advanceeoln(struct texi *p, const char *buf, |
advanceeoln(struct texi *p, size_t *pos, int consumenl) |
size_t sz, size_t *pos, int consumenl) |
|
{ |
{ |
|
|
while (*pos < sz && '\n' != buf[*pos]) |
while (*pos < BUFSZ(p) && '\n' != BUF(p)[*pos]) { |
advance(p, buf, pos); |
if ('@' == BUF(p)[*pos]) |
if (*pos < sz && consumenl) |
advance(p, pos); |
advance(p, buf, pos); |
advance(p, pos); |
|
} |
|
if (*pos < BUFSZ(p) && consumenl) |
|
advance(p, pos); |
return(*pos); |
return(*pos); |
} |
} |
|
|
Line 368 advanceeoln(struct texi *p, const char *buf, |
|
Line 438 advanceeoln(struct texi *p, const char *buf, |
|
* current buffer greater than or equal to the current position. |
* current buffer greater than or equal to the current position. |
*/ |
*/ |
void |
void |
advanceto(struct texi *p, const char *buf, size_t *pos, size_t end) |
advanceto(struct texi *p, size_t *pos, size_t end) |
{ |
{ |
|
|
assert(*pos <= end); |
assert(*pos <= end); |
while (*pos < end) |
while (*pos < end) |
advance(p, buf, pos); |
advance(p, pos); |
} |
} |
|
|
|
static void |
|
texiexecmacro(struct texi *p, struct teximacro *m, size_t sv, size_t *pos) |
|
{ |
|
size_t valsz, realsz, aasz, asz, |
|
ssz, i, j, k, start, end; |
|
char *val; |
|
char **args; |
|
const char *cp; |
|
|
|
/* Disregard empty macros. */ |
|
if (0 == (valsz = realsz = strlen(m->value))) { |
|
args = argparse(p, pos, &asz, m->argsz); |
|
for (i = 0; i < asz; i++) |
|
free(args[i]); |
|
free(args); |
|
return; |
|
} |
|
|
|
/* |
|
* This is important: it protect us from macros that invoke more |
|
* macros, possibly going on infinitely. |
|
* We use "sv" instead of the current position because we might |
|
* be invoked at the end of the macro (i.e., insplice == 0). |
|
* The "sv" value was initialised at the start of the macro. |
|
*/ |
|
if (sv > 0) |
|
if (++p->files[p->filepos].depth > 64) |
|
texierr(p, "maximium recursive depth"); |
|
|
|
args = argparse(p, pos, &asz, m->argsz); |
|
if (asz != m->argsz) |
|
texiwarn(p, "invalid macro argument length"); |
|
aasz = asz < m->argsz ? asz : m->argsz; |
|
|
|
if (0 == aasz) { |
|
texisplice(p, m->value, valsz, *pos); |
|
return; |
|
} |
|
|
|
val = strdup(m->value); |
|
|
|
for (i = j = 0; i < realsz; i++) { |
|
/* Parse blindly til the backslash delimiter. */ |
|
if ('\\' != m->value[i]) { |
|
val[j++] = m->value[i]; |
|
val[j] = '\0'; |
|
continue; |
|
} else if (i == realsz - 1) |
|
texierr(p, "trailing argument name delimiter"); |
|
|
|
/* Double-backslash is escaped. */ |
|
if ('\\' == m->value[i + 1]) { |
|
val[j++] = m->value[i++]; |
|
val[j] = '\0'; |
|
continue; |
|
} |
|
|
|
assert('\\' == m->value[i] && i < realsz - 1); |
|
|
|
/* Parse to terminating delimiter. */ |
|
/* FIXME: embedded, escaped delimiters? */ |
|
for (start = end = i + 1; end < realsz; end++) |
|
if ('\\' == m->value[end]) |
|
break; |
|
if (end == realsz) |
|
texierr(p, "unterminated argument name"); |
|
|
|
for (k = 0; k < aasz; k++) { |
|
if ((ssz = strlen(m->args[k])) != (end - start)) |
|
continue; |
|
if (strncmp(&m->value[start], m->args[k], ssz)) |
|
continue; |
|
break; |
|
} |
|
|
|
/* |
|
* Argument didn't exist in argument table. |
|
* Just ignore it. |
|
*/ |
|
if (k == aasz) { |
|
i = end; |
|
continue; |
|
} |
|
|
|
if (strlen(args[k]) > ssz) { |
|
valsz += strlen(args[k]); |
|
val = realloc(val, valsz + 1); |
|
if (NULL == val) |
|
texiabort(p, NULL); |
|
} |
|
|
|
for (cp = args[k]; '\0' != *cp; cp++) |
|
val[j++] = *cp; |
|
|
|
val[j] = '\0'; |
|
i = end; |
|
} |
|
|
|
texisplice(p, val, strlen(val), *pos); |
|
|
|
for (i = 0; i < asz; i++) |
|
free(args[i]); |
|
free(args); |
|
free(val); |
|
} |
|
|
/* |
/* |
* Output a free-form word in the input stream, progressing to the next |
* Output a free-form word in the input stream, progressing to the next |
* command or white-space. |
* command or white-space. |
* This also will advance the input stream. |
* This also will advance the input stream. |
*/ |
*/ |
static void |
static void |
texiword(struct texi *p, const char *buf, |
parseword(struct texi *p, size_t *pos, char extra) |
size_t sz, size_t *pos, char extra) |
|
{ |
{ |
|
|
if (p->seenws && 0 == p->outmacro && |
if (p->seenws && 0 == p->outmacro && |
Line 395 texiword(struct texi *p, const char *buf, |
|
Line 570 texiword(struct texi *p, const char *buf, |
|
|
|
p->seenws = 0; |
p->seenws = 0; |
|
|
while (*pos < sz && ! ismspace(buf[*pos])) { |
while (*pos < BUFSZ(p) && ! ismspace(BUF(p)[*pos])) { |
switch (buf[*pos]) { |
switch (BUF(p)[*pos]) { |
case ('@'): |
case ('@'): |
case ('}'): |
case ('}'): |
case ('{'): |
case ('{'): |
return; |
return; |
} |
} |
if ('\0' != extra && buf[*pos] == extra) |
if ('\0' != extra && BUF(p)[*pos] == extra) |
return; |
return; |
if (*pos < sz - 1 && |
if (*pos < BUFSZ(p) - 1 && |
'`' == buf[*pos] && |
'`' == BUF(p)[*pos] && |
'`' == buf[*pos + 1]) { |
'`' == BUF(p)[*pos + 1]) { |
texiputchars(p, "\\(lq"); |
texiputchars(p, "\\(lq"); |
advance(p, buf, pos); |
advance(p, pos); |
} else if (*pos < sz - 1 && |
} else if (*pos < BUFSZ(p) - 1 && |
'\'' == buf[*pos] && |
'\'' == BUF(p)[*pos] && |
'\'' == buf[*pos + 1]) { |
'\'' == BUF(p)[*pos + 1]) { |
texiputchars(p, "\\(rq"); |
texiputchars(p, "\\(rq"); |
advance(p, buf, pos); |
advance(p, pos); |
} else |
} else |
texiputchar(p, buf[*pos]); |
texiputchar(p, BUF(p)[*pos]); |
advance(p, buf, pos); |
advance(p, pos); |
} |
} |
} |
} |
|
|
Line 426 texiword(struct texi *p, const char *buf, |
|
Line 601 texiword(struct texi *p, const char *buf, |
|
* index after the command name. |
* index after the command name. |
*/ |
*/ |
enum texicmd |
enum texicmd |
texicmd(struct texi *p, const char *buf, |
texicmd(const struct texi *p, size_t pos, size_t *end, struct teximacro **macro) |
size_t pos, size_t sz, size_t *end) |
|
{ |
{ |
size_t i, len; |
size_t i, len, toksz; |
|
|
assert('@' == buf[pos]); |
assert('@' == BUF(p)[pos]); |
|
|
if ((*end = pos) == sz) |
if (NULL != macro) |
|
*macro = NULL; |
|
|
|
if ((*end = pos) == BUFSZ(p)) |
return(TEXICMD__MAX); |
return(TEXICMD__MAX); |
else if ((*end = ++pos) == sz) |
else if ((*end = ++pos) == BUFSZ(p)) |
return(TEXICMD__MAX); |
return(TEXICMD__MAX); |
|
|
/* Alphabetic commands are special. */ |
/* Alphabetic commands are special. */ |
if ( ! isalpha(buf[pos])) { |
if ( ! isalpha((unsigned char)BUF(p)[pos])) { |
if ((*end = pos + 1) == sz) |
if ((*end = pos + 1) == BUFSZ(p)) |
return(TEXICMD__MAX); |
return(TEXICMD__MAX); |
for (i = 0; i < TEXICMD__MAX; i++) { |
for (i = 0; i < TEXICMD__MAX; i++) { |
if (1 != texitoks[i].len) |
if (1 != texitoks[i].len) |
continue; |
continue; |
if (0 == strncmp(texitoks[i].tok, &buf[pos], 1)) |
if (0 == strncmp(texitoks[i].tok, &BUF(p)[pos], 1)) |
return(i); |
return(i); |
} |
} |
texiwarn(p, "bad command: @%c", buf[pos]); |
texiwarn(p, "bad command: @%c", BUF(p)[pos]); |
return(TEXICMD__MAX); |
return(TEXICMD__MAX); |
} |
} |
|
|
for (*end = pos; *end < sz && ! ismspace(buf[*end]); (*end)++) |
/* Scan to the end of the possible command name. */ |
if ((*end > pos && ('@' == buf[*end] || |
for (*end = pos; *end < BUFSZ(p) && ! ismspace(BUF(p)[*end]); (*end)++) |
'{' == buf[*end] || '}' == buf[*end]))) |
if ((*end > pos && ('@' == BUF(p)[*end] || |
|
'{' == BUF(p)[*end] || '}' == BUF(p)[*end]))) |
break; |
break; |
|
|
|
/* Look for the command. */ |
len = *end - pos; |
len = *end - pos; |
for (i = 0; i < TEXICMD__MAX; i++) { |
for (i = 0; i < TEXICMD__MAX; i++) { |
if (len != texitoks[i].len) |
if (len != texitoks[i].len) |
continue; |
continue; |
if (0 == strncmp(texitoks[i].tok, &buf[pos], len)) |
if (0 == strncmp(texitoks[i].tok, &BUF(p)[pos], len)) |
return(i); |
return(i); |
} |
} |
|
|
texiwarn(p, "bad command: @%.*s", (int)len, &buf[pos]); |
/* Look for it in our indices. */ |
|
for (i = 0; i < p->indexsz; i++) { |
|
toksz = strlen(p->indexs[i]); |
|
if (len != 5 + toksz) |
|
continue; |
|
if (strncmp(&BUF(p)[pos], p->indexs[i], toksz)) |
|
continue; |
|
if (0 == strncmp(&BUF(p)[pos + toksz], "index", 5)) |
|
return(TEXICMD_USER_INDEX); |
|
} |
|
|
|
for (i = 0; i < p->macrosz; i++) { |
|
if (len != strlen(p->macros[i].key)) |
|
continue; |
|
if (strncmp(&BUF(p)[pos], p->macros[i].key, len)) |
|
continue; |
|
if (NULL != macro) |
|
*macro = &p->macros[i]; |
|
return(TEXICMD__MAX); |
|
} |
|
|
|
texiwarn(p, "bad command: @%.*s", (int)len, &BUF(p)[pos]); |
return(TEXICMD__MAX); |
return(TEXICMD__MAX); |
} |
} |
|
|
Line 478 texicmd(struct texi *p, const char *buf, |
|
Line 678 texicmd(struct texi *p, const char *buf, |
|
* bracket for the zeroth parse. |
* bracket for the zeroth parse. |
*/ |
*/ |
int |
int |
parsearg(struct texi *p, const char *buf, |
parsearg(struct texi *p, size_t *pos, size_t num) |
size_t sz, size_t *pos, size_t num) |
|
{ |
{ |
size_t end; |
size_t end, sv; |
enum texicmd cmd; |
enum texicmd cmd; |
|
struct teximacro *macro; |
|
|
while (*pos < sz && ismspace(buf[*pos])) |
while (*pos < BUFSZ(p) && ismspace(BUF(p)[*pos])) |
advance(p, buf, pos); |
advance(p, pos); |
if (*pos == sz || (0 == num && '{' != buf[*pos])) |
if (*pos == BUFSZ(p) || (0 == num && '{' != BUF(p)[*pos])) |
return(0); |
return(0); |
if (0 == num) |
if (0 == num) |
advance(p, buf, pos); |
advance(p, pos); |
|
|
while ((*pos = advancenext(p, buf, sz, pos)) < sz) { |
while ((*pos = advancenext(p, pos)) < BUFSZ(p)) { |
switch (buf[*pos]) { |
switch (BUF(p)[*pos]) { |
case (','): |
case (','): |
advance(p, buf, pos); |
advance(p, pos); |
return(1); |
return(1); |
case ('}'): |
case ('}'): |
advance(p, buf, pos); |
advance(p, pos); |
return(0); |
return(0); |
case ('{'): |
case ('{'): |
if (0 == p->ign) |
if (0 == p->ign) |
texiwarn(p, "unexpected \"{\""); |
texiwarn(p, "unexpected \"{\""); |
advance(p, buf, pos); |
advance(p, pos); |
continue; |
continue; |
case ('@'): |
case ('@'): |
break; |
break; |
default: |
default: |
texiword(p, buf, sz, pos, ','); |
parseword(p, pos, ','); |
continue; |
continue; |
} |
} |
|
|
cmd = texicmd(p, buf, *pos, sz, &end); |
sv = p->files[p->filepos - 1].insplice; |
advanceto(p, buf, pos, end); |
cmd = texicmd(p, *pos, &end, ¯o); |
|
advanceto(p, pos, end); |
|
if (NULL != macro) |
|
texiexecmacro(p, macro, sv, pos); |
if (TEXICMD__MAX == cmd) |
if (TEXICMD__MAX == cmd) |
continue; |
continue; |
if (NULL != texitoks[cmd].fp) |
if (NULL != texitoks[cmd].fp) |
(*texitoks[cmd].fp)(p, cmd, buf, sz, pos); |
(*texitoks[cmd].fp)(p, cmd, pos); |
} |
} |
return(0); |
return(0); |
} |
} |
Line 526 parsearg(struct texi *p, const char *buf, |
|
Line 729 parsearg(struct texi *p, const char *buf, |
|
* This will stop in the event of EOF or if we're not at a bracket. |
* This will stop in the event of EOF or if we're not at a bracket. |
*/ |
*/ |
void |
void |
parsebracket(struct texi *p, const char *buf, size_t sz, size_t *pos) |
parsebracket(struct texi *p, size_t *pos, int dostack) |
{ |
{ |
size_t end; |
size_t end, sv, stack; |
enum texicmd cmd; |
enum texicmd cmd; |
|
struct teximacro *macro; |
|
|
while (*pos < sz && ismspace(buf[*pos])) |
while (*pos < BUFSZ(p) && ismspace(BUF(p)[*pos])) |
advance(p, buf, pos); |
advance(p, pos); |
|
|
if (*pos == sz || '{' != buf[*pos]) |
if (*pos == BUFSZ(p) || '{' != BUF(p)[*pos]) |
return; |
return; |
advance(p, buf, pos); |
advance(p, pos); |
|
|
while ((*pos = advancenext(p, buf, sz, pos)) < sz) { |
stack = 0; |
switch (buf[*pos]) { |
while ((*pos = advancenext(p, pos)) < BUFSZ(p)) { |
|
switch (BUF(p)[*pos]) { |
case ('}'): |
case ('}'): |
advance(p, buf, pos); |
if (stack > 0) { |
|
stack--; |
|
advance(p, pos); |
|
texiputchar(p, '}'); |
|
continue; |
|
} |
|
advance(p, pos); |
return; |
return; |
case ('{'): |
case ('{'): |
|
if (dostack) { |
|
stack++; |
|
advance(p, pos); |
|
texiputchar(p, '{'); |
|
continue; |
|
} |
if (0 == p->ign) |
if (0 == p->ign) |
texiwarn(p, "unexpected \"{\""); |
texiwarn(p, "unexpected \"{\""); |
advance(p, buf, pos); |
advance(p, pos); |
continue; |
continue; |
case ('@'): |
case ('@'): |
break; |
break; |
default: |
default: |
texiword(p, buf, sz, pos, '\0'); |
parseword(p, pos, '\0'); |
continue; |
continue; |
} |
} |
|
|
cmd = texicmd(p, buf, *pos, sz, &end); |
sv = p->files[p->filepos - 1].insplice; |
advanceto(p, buf, pos, end); |
cmd = texicmd(p, *pos, &end, ¯o); |
|
advanceto(p, pos, end); |
|
if (NULL != macro) |
|
texiexecmacro(p, macro, sv, pos); |
if (TEXICMD__MAX == cmd) |
if (TEXICMD__MAX == cmd) |
continue; |
continue; |
if (NULL != texitoks[cmd].fp) |
if (NULL != texitoks[cmd].fp) |
(*texitoks[cmd].fp)(p, cmd, buf, sz, pos); |
(*texitoks[cmd].fp)(p, cmd, pos); |
} |
} |
} |
} |
|
|
Line 570 parsebracket(struct texi *p, const char *buf, size_t s |
|
Line 790 parsebracket(struct texi *p, const char *buf, size_t s |
|
* the way. |
* the way. |
*/ |
*/ |
void |
void |
parseeoln(struct texi *p, const char *buf, size_t sz, size_t *pos) |
parseeoln(struct texi *p, size_t *pos) |
{ |
{ |
size_t end; |
size_t end, sv; |
enum texicmd cmd; |
enum texicmd cmd; |
|
struct teximacro *macro; |
|
|
while (*pos < sz && '\n' != buf[*pos]) { |
while (*pos < BUFSZ(p) && '\n' != BUF(p)[*pos]) { |
while (*pos < sz && isws(buf[*pos])) { |
while (*pos < BUFSZ(p) && isws(BUF(p)[*pos])) { |
p->seenws = 1; |
p->seenws = 1; |
if (p->literal) |
if (p->literal) |
texiputchar(p, buf[*pos]); |
texiputchar(p, BUF(p)[*pos]); |
advance(p, buf, pos); |
advance(p, pos); |
} |
} |
switch (buf[*pos]) { |
switch (BUF(p)[*pos]) { |
case ('}'): |
case ('}'): |
if (0 == p->ign) |
if (0 == p->ign) |
texiwarn(p, "unexpected \"}\""); |
texiwarn(p, "unexpected \"}\""); |
advance(p, buf, pos); |
advance(p, pos); |
continue; |
continue; |
case ('{'): |
case ('{'): |
if (0 == p->ign) |
if (0 == p->ign) |
texiwarn(p, "unexpected \"{\""); |
texiwarn(p, "unexpected \"{\""); |
advance(p, buf, pos); |
advance(p, pos); |
continue; |
continue; |
case ('@'): |
case ('@'): |
break; |
break; |
default: |
default: |
texiword(p, buf, sz, pos, '\0'); |
parseword(p, pos, '\0'); |
continue; |
continue; |
} |
} |
|
|
cmd = texicmd(p, buf, *pos, sz, &end); |
sv = p->files[p->filepos - 1].insplice; |
advanceto(p, buf, pos, end); |
cmd = texicmd(p, *pos, &end, ¯o); |
|
advanceto(p, pos, end); |
|
if (NULL != macro) |
|
texiexecmacro(p, macro, sv, pos); |
if (TEXICMD__MAX == cmd) |
if (TEXICMD__MAX == cmd) |
continue; |
continue; |
if (NULL != texitoks[cmd].fp) |
if (NULL != texitoks[cmd].fp) |
(*texitoks[cmd].fp)(p, cmd, buf, sz, pos); |
(*texitoks[cmd].fp)(p, cmd, pos); |
} |
} |
|
|
|
if (*pos < BUFSZ(p) && '\n' == BUF(p)[*pos]) |
|
advance(p, pos); |
} |
} |
|
|
/* |
/* |
|
* Peek to see if there's a command after subsequent whitespace. |
|
* If so, return the macro identifier. |
|
* This DOES NOT work with user-defined macros. |
|
*/ |
|
enum texicmd |
|
peekcmd(const struct texi *p, size_t pos) |
|
{ |
|
size_t end; |
|
|
|
while (pos < BUFSZ(p) && ismspace(BUF(p)[pos])) |
|
pos++; |
|
if (pos == BUFSZ(p) || '@' != BUF(p)[pos]) |
|
return(TEXICMD__MAX); |
|
return(texicmd(p, pos, &end, NULL)); |
|
} |
|
|
|
/* |
* Parse a single word or command. |
* Parse a single word or command. |
* This will return immediately at the EOF. |
* This will return immediately at the EOF. |
*/ |
*/ |
void |
static void |
parsesingle(struct texi *p, const char *buf, size_t sz, size_t *pos) |
parsesingle(struct texi *p, size_t *pos) |
{ |
{ |
size_t end; |
size_t end, sv; |
enum texicmd cmd; |
enum texicmd cmd; |
|
struct teximacro *macro; |
|
|
if ((*pos = advancenext(p, buf, sz, pos)) >= sz) |
if ((*pos = advancenext(p, pos)) >= BUFSZ(p)) |
return; |
return; |
|
|
switch (buf[*pos]) { |
switch (BUF(p)[*pos]) { |
case ('}'): |
case ('}'): |
if (0 == p->ign) |
if (0 == p->ign) |
texiwarn(p, "unexpected \"}\""); |
texiwarn(p, "unexpected \"}\""); |
advance(p, buf, pos); |
advance(p, pos); |
return; |
return; |
case ('{'): |
case ('{'): |
if (0 == p->ign) |
if (0 == p->ign) |
texiwarn(p, "unexpected \"{\""); |
texiwarn(p, "unexpected \"{\""); |
advance(p, buf, pos); |
advance(p, pos); |
return; |
return; |
case ('@'): |
case ('@'): |
break; |
break; |
default: |
default: |
texiword(p, buf, sz, pos, '\0'); |
parseword(p, pos, '\0'); |
return; |
return; |
} |
} |
|
|
cmd = texicmd(p, buf, *pos, sz, &end); |
sv = p->files[p->filepos - 1].insplice; |
advanceto(p, buf, pos, end); |
cmd = texicmd(p, *pos, &end, ¯o); |
|
advanceto(p, pos, end); |
|
if (NULL != macro) |
|
texiexecmacro(p, macro, sv, pos); |
if (TEXICMD__MAX == cmd) |
if (TEXICMD__MAX == cmd) |
return; |
return; |
if (NULL != texitoks[cmd].fp) |
if (NULL != texitoks[cmd].fp) |
(*texitoks[cmd].fp)(p, cmd, buf, sz, pos); |
(*texitoks[cmd].fp)(p, cmd, pos); |
} |
} |
|
|
/* |
/* |
Line 657 parsesingle(struct texi *p, const char *buf, size_t sz |
|
Line 905 parsesingle(struct texi *p, const char *buf, size_t sz |
|
* line or 1 otherwise. |
* line or 1 otherwise. |
*/ |
*/ |
int |
int |
parselinearg(struct texi *p, const char *buf, size_t sz, size_t *pos) |
parselinearg(struct texi *p, size_t *pos) |
{ |
{ |
|
|
while (*pos < sz && isws(buf[*pos])) { |
while (*pos < BUFSZ(p) && isws(BUF(p)[*pos])) { |
p->seenws = 1; |
p->seenws = 1; |
advance(p, buf, pos); |
advance(p, pos); |
} |
} |
|
|
if (*pos < sz && '{' == buf[*pos]) |
if (*pos < BUFSZ(p) && '{' == BUF(p)[*pos]) |
parsebracket(p, buf, sz, pos); |
parsebracket(p, pos, 0); |
else if ('\n' != buf[*pos]) |
else if (*pos < BUFSZ(p) && '\n' != BUF(p)[*pos]) |
parsesingle(p, buf, sz, pos); |
parsesingle(p, pos); |
else |
else |
return(0); |
return(0); |
|
|
Line 678 parselinearg(struct texi *p, const char *buf, size_t s |
|
Line 926 parselinearg(struct texi *p, const char *buf, size_t s |
|
/* |
/* |
* Parse til the end of the buffer. |
* Parse til the end of the buffer. |
*/ |
*/ |
void |
static void |
parseeof(struct texi *p, const char *buf, size_t sz) |
parseeof(struct texi *p) |
{ |
{ |
size_t pos; |
size_t pos; |
|
|
for (pos = 0; pos < sz; ) |
for (pos = 0; pos < BUFSZ(p); ) |
parsesingle(p, buf, sz, &pos); |
parsesingle(p, &pos); |
} |
} |
|
|
|
void |
|
texisplice(struct texi *p, const char *buf, size_t sz, size_t pos) |
|
{ |
|
char *cp; |
|
struct texifile *f; |
|
|
|
assert(p->filepos > 0); |
|
f = &p->files[p->filepos - 1]; |
|
|
|
if (f->mapsz + sz > f->mapmaxsz) { |
|
f->mapmaxsz = f->mapsz + sz + 1024; |
|
cp = realloc(f->map, f->mapmaxsz); |
|
if (NULL == cp) |
|
texiabort(p, NULL); |
|
f->map = cp; |
|
} |
|
|
|
f->insplice += sz; |
|
memmove(f->map + pos + sz, f->map + pos, f->mapsz - pos); |
|
memcpy(f->map + pos, buf, sz); |
|
f->mapsz += sz; |
|
} |
|
|
/* |
/* |
* Parse a block sequence until we have the "@end endtoken" command |
* Parse a block sequence until we have the "@end endtoken" command |
* invocation. |
* invocation. |
* This will return immediately at EOF. |
* This will return immediately at EOF. |
*/ |
*/ |
void |
void |
parseto(struct texi *p, const char *buf, |
parseto(struct texi *p, size_t *pos, const char *endtoken) |
size_t sz, size_t *pos, const char *endtoken) |
|
{ |
{ |
size_t end; |
size_t end, sv; |
enum texicmd cmd; |
enum texicmd cmd; |
size_t endtoksz; |
size_t endtoksz; |
|
struct teximacro *macro; |
|
|
endtoksz = strlen(endtoken); |
endtoksz = strlen(endtoken); |
assert(endtoksz > 0); |
assert(endtoksz > 0); |
|
|
while ((*pos = advancenext(p, buf, sz, pos)) < sz) { |
while ((*pos = advancenext(p, pos)) < BUFSZ(p)) { |
switch (buf[*pos]) { |
switch (BUF(p)[*pos]) { |
case ('}'): |
case ('}'): |
if (0 == p->ign) |
if (0 == p->ign) |
texiwarn(p, "unexpected \"}\""); |
texiwarn(p, "unexpected \"}\""); |
advance(p, buf, pos); |
advance(p, pos); |
continue; |
continue; |
case ('{'): |
case ('{'): |
if (0 == p->ign) |
if (0 == p->ign) |
texiwarn(p, "unexpected \"{\""); |
texiwarn(p, "unexpected \"{\""); |
advance(p, buf, pos); |
advance(p, pos); |
continue; |
continue; |
case ('@'): |
case ('@'): |
break; |
break; |
default: |
default: |
texiword(p, buf, sz, pos, '\0'); |
parseword(p, pos, '\0'); |
continue; |
continue; |
} |
} |
|
|
cmd = texicmd(p, buf, *pos, sz, &end); |
sv = p->files[p->filepos - 1].insplice; |
advanceto(p, buf, pos, end); |
cmd = texicmd(p, *pos, &end, ¯o); |
|
advanceto(p, pos, end); |
if (TEXICMD_END == cmd) { |
if (TEXICMD_END == cmd) { |
while (*pos < sz && isws(buf[*pos])) |
while (*pos < BUFSZ(p) && isws(BUF(p)[*pos])) |
advance(p, buf, pos); |
advance(p, pos); |
/* |
/* |
* FIXME: check the full word, not just its |
* FIXME: check the full word, not just its |
* initial substring! |
* initial substring! |
*/ |
*/ |
if (sz - *pos >= endtoksz && 0 == strncmp |
if (BUFSZ(p) - *pos >= endtoksz && 0 == strncmp |
(&buf[*pos], endtoken, endtoksz)) { |
(&BUF(p)[*pos], endtoken, endtoksz)) { |
advanceeoln(p, buf, sz, pos, 0); |
advanceeoln(p, pos, 0); |
break; |
break; |
} |
} |
if (0 == p->ign) |
if (0 == p->ign) |
texiwarn(p, "unexpected \"end\""); |
texiwarn(p, "unexpected \"end\""); |
advanceeoln(p, buf, sz, pos, 0); |
advanceeoln(p, pos, 0); |
continue; |
continue; |
} else if (TEXICMD__MAX != cmd) |
} |
if (NULL != texitoks[cmd].fp) |
if (NULL != macro) |
(*texitoks[cmd].fp)(p, cmd, buf, sz, pos); |
texiexecmacro(p, macro, sv, pos); |
|
if (TEXICMD__MAX == cmd) |
|
continue; |
|
if (NULL != texitoks[cmd].fp) |
|
(*texitoks[cmd].fp)(p, cmd, pos); |
} |
} |
} |
} |
|
|
/* |
/* |
|
* Like parsefile() but used for reading from stdandard input. |
|
* This can only be called for the first file! |
|
*/ |
|
void |
|
parsestdin(struct texi *p) |
|
{ |
|
struct texifile *f; |
|
ssize_t ssz; |
|
|
|
assert(0 == p->filepos); |
|
f = &p->files[p->filepos]; |
|
memset(f, 0, sizeof(struct texifile)); |
|
|
|
f->type = TEXISRC_STDIN; |
|
f->name = "<stdin>"; |
|
|
|
for (f->mapsz = 0; ; f->mapsz += (size_t)ssz) { |
|
if (f->mapsz == f->mapmaxsz) { |
|
if (f->mapmaxsz == (1U << 31)) |
|
texierr(p, "stdin buffer too long"); |
|
f->mapmaxsz = f->mapmaxsz > 65536 / 2 ? |
|
2 * f->mapmaxsz : 65536; |
|
f->map = realloc(f->map, f->mapmaxsz); |
|
if (NULL == f->map) |
|
texiabort(p, NULL); |
|
} |
|
ssz = read(STDIN_FILENO, f->map + |
|
(int)f->mapsz, f->mapmaxsz - f->mapsz); |
|
if (0 == ssz) |
|
break; |
|
else if (-1 == ssz) |
|
texiabort(p, NULL); |
|
} |
|
|
|
p->filepos++; |
|
parseeof(p); |
|
texifilepop(p); |
|
} |
|
|
|
/* |
* Memory-map the file "fname" and begin parsing it unless "parse" is |
* Memory-map the file "fname" and begin parsing it unless "parse" is |
* zero, in which case we just dump the file to stdout (making sure it |
* zero, in which case we just dump the file to stdout (making sure it |
* doesn't trip up mdoc(7) along the way). |
* doesn't trip up mdoc(7) along the way). |
Line 759 parsefile(struct texi *p, const char *fname, int parse |
|
Line 1075 parsefile(struct texi *p, const char *fname, int parse |
|
int fd; |
int fd; |
struct stat st; |
struct stat st; |
size_t i; |
size_t i; |
|
char *map; |
|
|
assert(p->filepos < 64); |
if (64 == p->filepos) |
|
texierr(p, "too many open files"); |
f = &p->files[p->filepos]; |
f = &p->files[p->filepos]; |
memset(f, 0, sizeof(struct texifile)); |
memset(f, 0, sizeof(struct texifile)); |
|
|
|
f->type = TEXISRC_FILE; |
f->name = fname; |
f->name = fname; |
if (-1 == (fd = open(fname, O_RDONLY, 0))) { |
if (-1 == (fd = open(fname, O_RDONLY, 0))) { |
texiabort(p, fname); |
texiabort(p, fname); |
Line 772 parsefile(struct texi *p, const char *fname, int parse |
|
Line 1091 parsefile(struct texi *p, const char *fname, int parse |
|
texiabort(p, fname); |
texiabort(p, fname); |
} |
} |
|
|
f->mapsz = st.st_size; |
f->mapsz = f->mapmaxsz = st.st_size; |
f->map = mmap(NULL, f->mapsz, |
map = mmap(NULL, f->mapsz, |
PROT_READ, MAP_SHARED, fd, 0); |
PROT_READ, MAP_SHARED, fd, 0); |
close(fd); |
close(fd); |
|
|
if (MAP_FAILED == f->map) |
if (MAP_FAILED == map) |
texiabort(p, fname); |
texiabort(p, fname); |
|
|
p->filepos++; |
|
if ( ! parse) { |
if ( ! parse) { |
/* |
for (i = 0; i < f->mapsz; i++) |
* We're printing verbatim output. |
texiputchar(p, map[i]); |
* Make sure it doesn't get interpreted as mdoc by |
if (p->outcol) |
* escaping escapes and making sure leading dots don't |
texiputchar(p, '\n'); |
* trigger mdoc(7) expansion. |
munmap(map, f->mapsz); |
*/ |
return; |
for (i = 0; i < f->mapsz; i++) { |
} |
if (i > 0 && '.' == f->map[i]) |
|
if ('\n' == f->map[i - 1]) |
p->filepos++; |
fputs("\\&", stdout); |
f->map = malloc(f->mapsz); |
putchar(f->map[i]); |
memcpy(f->map, map, f->mapsz); |
if ('\\' == f->map[i]) |
munmap(map, f->mapsz); |
putchar('e'); |
parseeof(p); |
} |
|
} else |
|
parseeof(p, f->map, f->mapsz); |
|
texifilepop(p); |
texifilepop(p); |
} |
} |
|
|
Line 809 parsefile(struct texi *p, const char *fname, int parse |
|
Line 1124 parsefile(struct texi *p, const char *fname, int parse |
|
* The pointer can point to NULL if the value has been unset. |
* The pointer can point to NULL if the value has been unset. |
*/ |
*/ |
static char ** |
static char ** |
valuequery(const struct texi *p, |
valuequery(const struct texi *p, size_t start, size_t end) |
const char *buf, size_t start, size_t end) |
|
{ |
{ |
size_t i, sz, len; |
size_t i, sz, len; |
|
|
Line 822 valuequery(const struct texi *p, |
|
Line 1136 valuequery(const struct texi *p, |
|
sz = strlen(p->vals[i].key); |
sz = strlen(p->vals[i].key); |
if (sz != len) |
if (sz != len) |
continue; |
continue; |
if (0 == strncmp(p->vals[i].key, &buf[start], len)) |
if (0 == strncmp(p->vals[i].key, &BUF(p)[start], len)) |
return(&p->vals[i].value); |
return(&p->vals[i].value); |
} |
} |
return(NULL); |
return(NULL); |
Line 833 valuequery(const struct texi *p, |
|
Line 1147 valuequery(const struct texi *p, |
|
* pointer to its value via valuequery(). |
* pointer to its value via valuequery(). |
*/ |
*/ |
static char ** |
static char ** |
valuelquery(struct texi *p, const char *buf, size_t sz, size_t *pos) |
valuelquery(struct texi *p, size_t *pos) |
{ |
{ |
size_t start, end; |
size_t start, end; |
char **ret; |
char **ret; |
|
|
while (*pos < sz && isws(buf[*pos])) |
while (*pos < BUFSZ(p) && isws(BUF(p)[*pos])) |
advance(p, buf, pos); |
advance(p, pos); |
if (*pos == sz) |
if (*pos == BUFSZ(p)) |
return(NULL); |
return(NULL); |
for (start = end = *pos; end < sz; end++) |
for (start = end = *pos; end < BUFSZ(p); end++) |
if ('\n' == buf[end]) |
if ('\n' == BUF(p)[end]) |
break; |
break; |
advanceto(p, buf, pos, end); |
advanceto(p, pos, end); |
if (*pos < sz) { |
if (*pos < BUFSZ(p)) { |
assert('\n' == buf[*pos]); |
assert('\n' == BUF(p)[*pos]); |
advance(p, buf, pos); |
advance(p, pos); |
} |
} |
if (NULL == (ret = valuequery(p, buf, start, end))) |
if (NULL == (ret = valuequery(p, start, end))) |
return(NULL); |
return(NULL); |
return(ret); |
return(ret); |
} |
} |
|
|
void |
void |
valuelclear(struct texi *p, const char *buf, size_t sz, size_t *pos) |
valuelclear(struct texi *p, size_t *pos) |
{ |
{ |
char **ret; |
char **ret; |
|
|
if (NULL == (ret = valuelquery(p, buf, sz, pos))) |
if (NULL == (ret = valuelquery(p, pos))) |
return; |
return; |
free(*ret); |
free(*ret); |
*ret = NULL; |
*ret = NULL; |
} |
} |
|
|
const char * |
const char * |
valuellookup(struct texi *p, const char *buf, size_t sz, size_t *pos) |
valuellookup(struct texi *p, size_t *pos) |
{ |
{ |
char **ret; |
char **ret; |
|
|
if (NULL == (ret = valuelquery(p, buf, sz, pos))) |
if (NULL == (ret = valuelquery(p, pos))) |
return(NULL); |
return(NULL); |
return(*ret); |
return(*ret); |
} |
} |
Line 884 valuellookup(struct texi *p, const char *buf, size_t s |
|
Line 1198 valuellookup(struct texi *p, const char *buf, size_t s |
|
* value had previously been unset. |
* value had previously been unset. |
*/ |
*/ |
const char * |
const char * |
valueblookup(struct texi *p, const char *buf, size_t sz, size_t *pos) |
valueblookup(struct texi *p, size_t *pos) |
{ |
{ |
size_t start, end; |
size_t start, end; |
char **ret; |
char **ret; |
|
|
while (*pos < sz && isws(buf[*pos])) |
while (*pos < BUFSZ(p) && isws(BUF(p)[*pos])) |
advance(p, buf, pos); |
advance(p, pos); |
if (*pos == sz || '{' != buf[*pos]) |
if (*pos == BUFSZ(p) || '{' != BUF(p)[*pos]) |
return(NULL); |
return(NULL); |
advance(p, buf, pos); |
advance(p, pos); |
for (start = end = *pos; end < sz; end++) |
for (start = end = *pos; end < BUFSZ(p); end++) |
if ('}' == buf[end]) |
if ('}' == BUF(p)[end]) |
break; |
break; |
advanceto(p, buf, pos, end); |
advanceto(p, pos, end); |
if (*pos < sz) { |
if (*pos < BUFSZ(p)) { |
assert('}' == buf[*pos]); |
assert('}' == BUF(p)[*pos]); |
advance(p, buf, pos); |
advance(p, pos); |
} |
} |
if (NULL == (ret = valuequery(p, buf, start, end))) |
if (NULL == (ret = valuequery(p, start, end))) |
return(NULL); |
return(NULL); |
return(*ret); |
return(*ret); |
} |
} |
Line 924 valueadd(struct texi *p, char *key, char *val) |
|
Line 1238 valueadd(struct texi *p, char *key, char *val) |
|
free(p->vals[i].value); |
free(p->vals[i].value); |
p->vals[i].value = val; |
p->vals[i].value = val; |
} else { |
} else { |
|
/* FIXME: reallocarray() */ |
p->vals = realloc(p->vals, |
p->vals = realloc(p->vals, |
(p->valsz + 1) * |
(p->valsz + 1) * |
sizeof(struct texivalue)); |
sizeof(struct texivalue)); |
if (NULL == p->vals) { |
if (NULL == p->vals) |
perror(NULL); |
texiabort(p, NULL); |
exit(EXIT_FAILURE); |
|
} |
|
p->vals[p->valsz].key = key; |
p->vals[p->valsz].key = key; |
p->vals[p->valsz].value = val; |
p->vals[p->valsz].value = val; |
p->valsz++; |
p->valsz++; |
} |
} |
} |
} |
|
|
|
/* |
|
* Take the arguments to a macro, e.g., @foo{bar, baz, xyzzy} (or the |
|
* declaration form, @macro foo {arg1, ...}) and textually convert it to |
|
* an array of arguments of size "argsz". |
|
* These need to be freed individually and as a whole. |
|
* NOTE: this will puke on @, or @} macros, which can trick it into |
|
* stopping argument parsing earlier. |
|
* Ergo, textual: this doesn't interpret the arguments in any way. |
|
*/ |
|
char ** |
|
argparse(struct texi *p, size_t *pos, size_t *argsz, size_t hint) |
|
{ |
|
char **args; |
|
size_t start, end, stack; |
|
|
|
while (*pos < BUFSZ(p) && isws(BUF(p)[*pos])) |
|
advance(p, pos); |
|
|
|
args = NULL; |
|
*argsz = 0; |
|
|
|
if (*pos == BUFSZ(p)) |
|
return(args); |
|
|
|
if ('{' != BUF(p)[*pos] && hint) { |
|
/* |
|
* Special case: if we encounter an unbracketed argument |
|
* and we're being invoked with non-zero arguments |
|
* (versus being set, i.e., hint>0), then parse until |
|
* the end of line. |
|
*/ |
|
*argsz = 1; |
|
args = calloc(1, sizeof(char *)); |
|
if (NULL == args) |
|
texiabort(p, NULL); |
|
start = *pos; |
|
while (*pos < BUFSZ(p)) { |
|
if ('\n' == BUF(p)[*pos]) |
|
break; |
|
advance(p, pos); |
|
} |
|
args[0] = malloc(*pos - start + 1); |
|
memcpy(args[0], &BUF(p)[start], *pos - start); |
|
args[0][*pos - start] = '\0'; |
|
if (*pos < BUFSZ(p) && '\n' == BUF(p)[*pos]) |
|
advance(p, pos); |
|
return(args); |
|
} else if ('{' != BUF(p)[*pos]) |
|
return(args); |
|
|
|
assert('{' == BUF(p)[*pos]); |
|
|
|
/* Parse til the closing '}', putting into the array. */ |
|
advance(p, pos); |
|
while (*pos < BUFSZ(p)) { |
|
while (*pos < BUFSZ(p) && isws(BUF(p)[*pos])) |
|
advance(p, pos); |
|
start = *pos; |
|
stack = 0; |
|
while (*pos < BUFSZ(p)) { |
|
/* |
|
* According to the manual, commas within |
|
* embedded commands are escaped. |
|
* We keep track of embedded-ness in the "stack" |
|
* state anyway, so this is free. |
|
*/ |
|
if (',' == BUF(p)[*pos] && 0 == stack && 1 != hint) |
|
break; |
|
else if (0 == stack && '}' == BUF(p)[*pos]) |
|
break; |
|
else if (0 != stack && '}' == BUF(p)[*pos]) |
|
stack--; |
|
else if ('{' == BUF(p)[*pos]) |
|
stack++; |
|
advance(p, pos); |
|
} |
|
if (stack) |
|
texiwarn(p, "unterminated macro " |
|
"in macro arguments"); |
|
if ((end = *pos) == BUFSZ(p)) |
|
break; |
|
/* Test for zero-length '{ }'. */ |
|
if (start == end && '}' == BUF(p)[*pos] && 0 == *argsz) |
|
break; |
|
/* FIXME: use reallocarray. */ |
|
args = realloc |
|
(args, sizeof(char *) * |
|
(*argsz + 1)); |
|
if (NULL == args) |
|
texiabort(p, NULL); |
|
args[*argsz] = malloc(end - start + 1); |
|
if (NULL == args[*argsz]) |
|
texiabort(p, NULL); |
|
memcpy(args[*argsz], |
|
&BUF(p)[start], end - start); |
|
args[*argsz][end - start] = '\0'; |
|
(*argsz)++; |
|
if ('}' == BUF(p)[*pos]) |
|
break; |
|
advance(p, pos); |
|
} |
|
|
|
if (*pos == BUFSZ(p)) |
|
texierr(p, "unterminated arguments"); |
|
assert('}' == BUF(p)[*pos]); |
|
advance(p, pos); |
|
return(args); |
|
} |
|
|
|
/* |
|
* If we're printing chapters, then do some naviation here and then |
|
* close our outfile. |
|
* I want to call this the SEE ALSO section, but that's not really what |
|
* it is: we'll refer to the "initial" (top) node and the next and |
|
* previous chapters. |
|
*/ |
|
void |
|
teximdocclose(struct texi *p, int last) |
|
{ |
|
char buf[PATH_MAX]; |
|
|
|
if (NULL == p->chapters || 0 == p->chapnum) |
|
return; |
|
|
|
teximacro(p, "Sh INFO NAVIGATION"); |
|
|
|
/* Print a reference to the "top" node. */ |
|
if (p->chapnum > 1) { |
|
texiputchars(p, "Top node,"); |
|
snprintf(buf, sizeof(buf), "node1 7"); |
|
teximacroopen(p, "Xr "); |
|
texiputchars(p, buf); |
|
texiputchars(p, " ;"); |
|
teximacroclose(p); |
|
} |
|
|
|
/* Print a reference to the previous node. */ |
|
if (p->chapnum > 2) { |
|
texiputchars(p, "previous node,"); |
|
snprintf(buf, sizeof(buf), |
|
"node%zu 7", p->chapnum - 1); |
|
teximacroopen(p, "Xr "); |
|
texiputchars(p, buf); |
|
if ( ! last) |
|
texiputchars(p, " ;"); |
|
teximacroclose(p); |
|
} |
|
|
|
/* Print a reference to the next node. */ |
|
if ( ! last) { |
|
if (1 == p->chapnum) |
|
texiputchars(p, "Next node,"); |
|
else |
|
texiputchars(p, "next node,"); |
|
snprintf(buf, sizeof(buf), |
|
"node%zu 7", p->chapnum + 1); |
|
teximacroopen(p, "Xr "); |
|
texiputchars(p, buf); |
|
teximacroclose(p); |
|
} |
|
|
|
fclose(p->outfile); |
|
} |
|
|
|
/* |
|
* Open a mdoc(7) context. |
|
* If we're printing chapters, then open the outfile here, too. |
|
* Otherwise just print the mdoc(7) prologue. |
|
*/ |
|
void |
|
teximdocopen(struct texi *p, size_t *pos) |
|
{ |
|
const char *cp; |
|
time_t t; |
|
char date[32]; |
|
char fname[PATH_MAX]; |
|
|
|
if (NULL != p->chapters) { |
|
snprintf(fname, sizeof(fname), "%s/node%zu.7", |
|
p->chapters, ++p->chapnum); |
|
p->outfile = fopen(fname, "w"); |
|
if (NULL == p->outfile) |
|
texiabort(p, fname); |
|
} |
|
|
|
/* |
|
* Here we print our standard mdoc(7) prologue. |
|
* We use the title set with @settitle for the `Nd' description |
|
* and the source document filename (the first one as invoked on |
|
* the command line) for the title. |
|
* The date is set to the current date. |
|
*/ |
|
t = time(NULL); |
|
strftime(date, sizeof(date), "%F", localtime(&t)); |
|
|
|
teximacroopen(p, "Dd"); |
|
texiputchars(p, date); |
|
teximacroclose(p); |
|
teximacroopen(p, "Dt"); |
|
for (cp = p->title; '\0' != *cp; cp++) |
|
texiputchar(p, toupper((unsigned int)*cp)); |
|
texiputchars(p, " 7"); |
|
teximacroclose(p); |
|
teximacro(p, "Os"); |
|
teximacro(p, "Sh NAME"); |
|
teximacroopen(p, "Nm"); |
|
for (cp = p->title; '\0' != *cp; cp++) |
|
texiputchar(p, *cp); |
|
teximacroclose(p); |
|
teximacroopen(p, "Nd"); |
|
/* |
|
* The subtitle `Nd' can consist of arbitrary macros, so paste |
|
* it and parse to the end of the line. |
|
*/ |
|
if (NULL != p->subtitle) { |
|
texisplice(p, p->subtitle, strlen(p->subtitle), *pos); |
|
parseeoln(p, pos); |
|
} else |
|
texiputchars(p, "Unknown description"); |
|
teximacroclose(p); |
|
p->seenvs = 1; |
|
} |
|
|