version 1.6, 2015/02/21 22:01:32 |
version 1.13, 2015/02/24 14:35:40 |
Line 43 texifilepop(struct texi *p) |
|
Line 43 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); |
if (TEXISRC_FILE == f->type) |
|
munmap(f->map, f->mapsz); |
|
else |
|
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 64 texiexit(struct texi *p) |
|
Line 88 texiexit(struct texi *p) |
|
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++) |
for (i = 0; i < p->indexsz; i++) |
free(p->indexs[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->indexs); |
free(p->dirs); |
free(p->dirs); |
Line 137 texierr(struct texi *p, const char *fmt, ...) |
|
Line 160 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 147 texiputchar(struct texi *p, char c) |
|
Line 168 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("\\&", stdout); |
|
if ('\'' == c && 0 == p->outcol) |
|
fputs("\\&", stdout); |
|
|
putchar(c); |
putchar(c); |
|
if ('\\' == c) |
|
putchar('e'); |
p->seenvs = 0; |
p->seenvs = 0; |
if ('\n' == c) { |
if ('\n' == c) { |
p->outcol = 0; |
p->outcol = 0; |
Line 161 texiputchar(struct texi *p, char c) |
|
Line 185 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("\\&", stdout); |
|
if ('\'' == *s && 0 == p->outcol) |
|
fputs("\\&", stdout); |
|
p->outcol += fputs(s, stdout); |
|
p->seenvs = 0; |
} |
} |
|
|
/* |
/* |
|
* This puts all characters onto the output stream but makes sure to |
|
* escape mdoc(7) slashes. |
|
*/ |
|
void |
|
texiputbuf(struct texi *p, const char *buf, size_t start, size_t end) |
|
{ |
|
|
|
for ( ; start < end; start++) { |
|
texiputchar(p, buf[start]); |
|
if ('\\' == buf[start]) |
|
texiputchar(p, 'e'); |
|
} |
|
} |
|
|
|
/* |
* 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 327 advancenext(struct texi *p, const char *buf, size_t sz |
|
Line 373 advancenext(struct texi *p, const char *buf, size_t sz |
|
|
|
if (p->literal) { |
if (p->literal) { |
while (*pos < sz && ismspace(buf[*pos])) { |
while (*pos < sz && ismspace(buf[*pos])) { |
if (*pos && '\n' == buf[*pos] && |
|
'\\' == buf[*pos - 1]) |
|
texiputchar(p, 'e'); |
|
texiputchar(p, buf[*pos]); |
texiputchar(p, buf[*pos]); |
advance(p, buf, pos); |
advance(p, buf, pos); |
} |
} |
Line 380 advanceto(struct texi *p, const char *buf, size_t *pos |
|
Line 423 advanceto(struct texi *p, const char *buf, size_t *pos |
|
advance(p, buf, pos); |
advance(p, buf, pos); |
} |
} |
|
|
|
static void |
|
texiexecmacro(struct texi *p, struct teximacro *m, |
|
const char *buf, size_t sz, size_t *pos) |
|
{ |
|
size_t valsz, realsz, aasz, asz, |
|
ssz, i, j, k, start, end; |
|
char *val; |
|
char **args; |
|
const char *cp; |
|
|
|
args = argparse(p, buf, sz, pos, &asz, m->argsz); |
|
if (asz != m->argsz) |
|
texiwarn(p, "invalid macro argument length"); |
|
aasz = asz < m->argsz ? asz : m->argsz; |
|
|
|
if (0 == aasz) { |
|
parsemembuf(p, m->value, strlen(m->value)); |
|
return; |
|
} |
|
|
|
valsz = realsz = strlen(m->value); |
|
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. |
|
* No need to reallocate here: we just copy the text |
|
* directly from the macro value into the buffer. |
|
*/ |
|
if (k == aasz) { |
|
for ( ; i < end; i++) |
|
val[j++] = m->value[i]; |
|
assert('\\' == m->value[i]); |
|
val[j++] = m->value[i]; |
|
val[j] = '\0'; |
|
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; |
|
} |
|
|
|
parsemembuf(p, val, strlen(val)); |
|
|
|
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. |
Line 430 texiword(struct texi *p, const char *buf, |
|
Line 566 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(struct texi *p, const char *buf, size_t pos, |
size_t pos, size_t sz, size_t *end) |
size_t sz, size_t *end, struct teximacro **macro) |
{ |
{ |
size_t i, len, toksz; |
size_t i, len, toksz; |
|
|
assert('@' == buf[pos]); |
assert('@' == buf[pos]); |
|
|
|
if (NULL != macro) |
|
*macro = NULL; |
|
|
if ((*end = pos) == sz) |
if ((*end = pos) == sz) |
return(TEXICMD__MAX); |
return(TEXICMD__MAX); |
else if ((*end = ++pos) == sz) |
else if ((*end = ++pos) == sz) |
Line 479 texicmd(struct texi *p, const char *buf, |
|
Line 618 texicmd(struct texi *p, const char *buf, |
|
if (strncmp(&buf[pos], p->indexs[i], toksz)) |
if (strncmp(&buf[pos], p->indexs[i], toksz)) |
continue; |
continue; |
if (0 == strncmp(&buf[pos + toksz], "index", 5)) |
if (0 == strncmp(&buf[pos + toksz], "index", 5)) |
return(TEXICMD_INDEX); |
return(TEXICMD_USER_INDEX); |
} |
} |
|
|
|
for (i = 0; i < p->macrosz; i++) { |
|
if (len != strlen(p->macros[i].key)) |
|
continue; |
|
if (strncmp(&buf[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[pos]); |
texiwarn(p, "bad command: @%.*s", (int)len, &buf[pos]); |
return(TEXICMD__MAX); |
return(TEXICMD__MAX); |
} |
} |
|
|
parsearg(struct texi *p, const char *buf, |
parsearg(struct texi *p, const char *buf, |
size_t sz, size_t *pos, size_t num) |
size_t sz, size_t *pos, size_t num) |
{ |
{ |
size_t end; |
size_t end; |
enum texicmd cmd; |
enum texicmd cmd; |
|
struct teximacro *macro; |
|
|
while (*pos < sz && ismspace(buf[*pos])) |
while (*pos < sz && ismspace(buf[*pos])) |
advance(p, buf, pos); |
advance(p, buf, pos); |
Line 528 parsearg(struct texi *p, const char *buf, |
|
Line 678 parsearg(struct texi *p, const char *buf, |
|
continue; |
continue; |
} |
} |
|
|
cmd = texicmd(p, buf, *pos, sz, &end); |
cmd = texicmd(p, buf, *pos, sz, &end, ¯o); |
advanceto(p, buf, pos, end); |
advanceto(p, buf, pos, end); |
|
if (NULL != macro) |
|
texiexecmacro(p, macro, buf, sz, pos); |
if (TEXICMD__MAX == cmd) |
if (TEXICMD__MAX == cmd) |
continue; |
continue; |
if (NULL != texitoks[cmd].fp) |
if (NULL != texitoks[cmd].fp) |
Line 545 parsearg(struct texi *p, const char *buf, |
|
Line 697 parsearg(struct texi *p, const char *buf, |
|
void |
void |
parsebracket(struct texi *p, const char *buf, size_t sz, size_t *pos) |
parsebracket(struct texi *p, const char *buf, size_t sz, size_t *pos) |
{ |
{ |
size_t end; |
size_t end; |
enum texicmd cmd; |
enum texicmd cmd; |
|
struct teximacro *macro; |
|
|
while (*pos < sz && ismspace(buf[*pos])) |
while (*pos < sz && ismspace(buf[*pos])) |
advance(p, buf, pos); |
advance(p, buf, pos); |
Line 572 parsebracket(struct texi *p, const char *buf, size_t s |
|
Line 725 parsebracket(struct texi *p, const char *buf, size_t s |
|
continue; |
continue; |
} |
} |
|
|
cmd = texicmd(p, buf, *pos, sz, &end); |
cmd = texicmd(p, buf, *pos, sz, &end, ¯o); |
advanceto(p, buf, pos, end); |
advanceto(p, buf, pos, end); |
|
if (NULL != macro) |
|
texiexecmacro(p, macro, buf, sz, pos); |
if (TEXICMD__MAX == cmd) |
if (TEXICMD__MAX == cmd) |
continue; |
continue; |
if (NULL != texitoks[cmd].fp) |
if (NULL != texitoks[cmd].fp) |
Line 589 parsebracket(struct texi *p, const char *buf, size_t s |
|
Line 744 parsebracket(struct texi *p, const char *buf, size_t s |
|
void |
void |
parseeoln(struct texi *p, const char *buf, size_t sz, size_t *pos) |
parseeoln(struct texi *p, const char *buf, size_t sz, size_t *pos) |
{ |
{ |
size_t end; |
size_t end; |
enum texicmd cmd; |
enum texicmd cmd; |
|
struct teximacro *macro; |
|
|
while (*pos < sz && '\n' != buf[*pos]) { |
while (*pos < sz && '\n' != buf[*pos]) { |
while (*pos < sz && isws(buf[*pos])) { |
while (*pos < sz && isws(buf[*pos])) { |
Line 617 parseeoln(struct texi *p, const char *buf, size_t sz, |
|
Line 773 parseeoln(struct texi *p, const char *buf, size_t sz, |
|
continue; |
continue; |
} |
} |
|
|
cmd = texicmd(p, buf, *pos, sz, &end); |
cmd = texicmd(p, buf, *pos, sz, &end, ¯o); |
advanceto(p, buf, pos, end); |
advanceto(p, buf, pos, end); |
|
if (NULL != macro) |
|
texiexecmacro(p, macro, buf, sz, pos); |
if (TEXICMD__MAX == cmd) |
if (TEXICMD__MAX == cmd) |
continue; |
continue; |
if (NULL != texitoks[cmd].fp) |
if (NULL != texitoks[cmd].fp) |
Line 633 parseeoln(struct texi *p, const char *buf, size_t sz, |
|
Line 791 parseeoln(struct texi *p, const char *buf, size_t sz, |
|
void |
void |
parsesingle(struct texi *p, const char *buf, size_t sz, size_t *pos) |
parsesingle(struct texi *p, const char *buf, size_t sz, size_t *pos) |
{ |
{ |
size_t end; |
size_t end; |
enum texicmd cmd; |
enum texicmd cmd; |
|
struct teximacro *macro; |
|
|
if ((*pos = advancenext(p, buf, sz, pos)) >= sz) |
if ((*pos = advancenext(p, buf, sz, pos)) >= sz) |
return; |
return; |
Line 657 parsesingle(struct texi *p, const char *buf, size_t sz |
|
Line 816 parsesingle(struct texi *p, const char *buf, size_t sz |
|
return; |
return; |
} |
} |
|
|
cmd = texicmd(p, buf, *pos, sz, &end); |
cmd = texicmd(p, buf, *pos, sz, &end, ¯o); |
advanceto(p, buf, pos, end); |
advanceto(p, buf, pos, end); |
|
if (NULL != macro) |
|
texiexecmacro(p, macro, buf, sz, pos); |
if (TEXICMD__MAX == cmd) |
if (TEXICMD__MAX == cmd) |
return; |
return; |
if (NULL != texitoks[cmd].fp) |
if (NULL != texitoks[cmd].fp) |
Line 705 parseeof(struct texi *p, const char *buf, size_t sz) |
|
Line 866 parseeof(struct texi *p, const char *buf, size_t sz) |
|
} |
} |
|
|
/* |
/* |
|
* This is like parseeof() except that it's to be invoked on memory |
|
* buffers while parsing a larger scope. |
|
* This is useful for parsing macro sequences. |
|
* The line, column, and name of the calling file context are saved, the |
|
* column and line reset, then all of these restored after parse. |
|
*/ |
|
void |
|
parsemembuf(struct texi *p, const char *buf, size_t sz) |
|
{ |
|
size_t svln, svcol; |
|
const char *svname; |
|
|
|
svln = p->files[p->filepos - 1].line; |
|
svcol = p->files[p->filepos - 1].col; |
|
svname = p->files[p->filepos - 1].name; |
|
|
|
p->files[p->filepos - 1].line = 0; |
|
p->files[p->filepos - 1].col = 0; |
|
p->files[p->filepos - 1].name = "<macro buffer>"; |
|
|
|
parseeof(p, buf, sz); |
|
|
|
p->files[p->filepos - 1].line = svln; |
|
p->files[p->filepos - 1].col = svcol; |
|
p->files[p->filepos - 1].name = svname; |
|
} |
|
|
|
/* |
* 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. |
|
|
parseto(struct texi *p, const char *buf, |
parseto(struct texi *p, const char *buf, |
size_t sz, size_t *pos, const char *endtoken) |
size_t sz, size_t *pos, const char *endtoken) |
{ |
{ |
size_t end; |
size_t end; |
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); |
Line 739 parseto(struct texi *p, const char *buf, |
|
Line 929 parseto(struct texi *p, const char *buf, |
|
continue; |
continue; |
} |
} |
|
|
cmd = texicmd(p, buf, *pos, sz, &end); |
cmd = texicmd(p, buf, *pos, sz, &end, ¯o); |
advanceto(p, buf, pos, end); |
advanceto(p, buf, pos, end); |
if (TEXICMD_END == cmd) { |
if (TEXICMD_END == cmd) { |
while (*pos < sz && isws(buf[*pos])) |
while (*pos < sz && isws(buf[*pos])) |
Line 757 parseto(struct texi *p, const char *buf, |
|
Line 947 parseto(struct texi *p, const char *buf, |
|
texiwarn(p, "unexpected \"end\""); |
texiwarn(p, "unexpected \"end\""); |
advanceeoln(p, buf, sz, pos, 0); |
advanceeoln(p, buf, sz, 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, buf, sz, pos); |
|
if (TEXICMD__MAX == cmd) |
|
continue; |
|
if (NULL != texitoks[cmd].fp) |
|
(*texitoks[cmd].fp)(p, cmd, buf, sz, 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; |
|
size_t off; |
|
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 (off = 0; ; off += (size_t)ssz) { |
|
if (off == f->mapsz) { |
|
if (f->mapsz == (1U << 31)) |
|
texierr(p, "stdin buffer too long"); |
|
f->mapsz = f->mapsz > 65536 / 2 ? |
|
2 * f->mapsz : 65536; |
|
f->map = realloc(f->map, f->mapsz); |
|
if (NULL == f->map) |
|
texiabort(p, NULL); |
|
} |
|
ssz = read(STDIN_FILENO, |
|
f->map + (int)off, f->mapsz - off); |
|
if (0 == ssz) |
|
break; |
|
else if (-1 == ssz) |
|
texiabort(p, NULL); |
|
} |
|
|
|
p->filepos++; |
|
parseeof(p, f->map, off); |
|
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 782 parsefile(struct texi *p, const char *fname, int parse |
|
Line 1017 parsefile(struct texi *p, const char *fname, int parse |
|
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 800 parsefile(struct texi *p, const char *fname, int parse |
|
Line 1036 parsefile(struct texi *p, const char *fname, int parse |
|
|
|
p->filepos++; |
p->filepos++; |
if ( ! parse) { |
if ( ! parse) { |
/* |
for (i = 0; i < f->mapsz; i++) |
* We're printing verbatim output. |
texiputchar(p, f->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. |
|
*/ |
|
for (i = 0; i < f->mapsz; i++) { |
|
if (i > 0 && '.' == f->map[i]) |
|
if ('\n' == f->map[i - 1]) |
|
fputs("\\&", stdout); |
|
putchar(f->map[i]); |
|
if ('\\' == f->map[i]) |
|
putchar('e'); |
|
} |
|
} else |
} else |
parseeof(p, f->map, f->mapsz); |
parseeof(p, f->map, f->mapsz); |
texifilepop(p); |
texifilepop(p); |
Line 952 valueadd(struct texi *p, char *key, char *val) |
|
Line 1178 valueadd(struct texi *p, char *key, char *val) |
|
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, const char *buf, |
|
size_t sz, size_t *pos, size_t *argsz, size_t hint) |
|
{ |
|
char **args; |
|
size_t start, end, stack; |
|
|
|
while (*pos < sz && isws(buf[*pos])) |
|
advance(p, buf, pos); |
|
|
|
args = NULL; |
|
*argsz = 0; |
|
|
|
if ('{' != buf[*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 < sz) { |
|
if ('\n' == buf[*pos]) |
|
break; |
|
advance(p, buf, pos); |
|
} |
|
args[0] = malloc(*pos - start + 1); |
|
memcpy(args[0], &buf[start], *pos - start); |
|
args[0][*pos - start] = '\0'; |
|
if (*pos < sz && '\n' == buf[*pos]) |
|
advance(p, buf, pos); |
|
return(args); |
|
} else if ('{' != buf[*pos]) |
|
return(args); |
|
|
|
/* Parse til the closing '}', putting into the array. */ |
|
advance(p, buf, pos); |
|
while (*pos < sz) { |
|
while (*pos < sz && isws(buf[*pos])) |
|
advance(p, buf, pos); |
|
start = *pos; |
|
stack = 0; |
|
while (*pos < sz) { |
|
/* |
|
* 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[*pos] && 0 == stack && 1 != hint) |
|
break; |
|
else if (0 == stack && '}' == buf[*pos]) |
|
break; |
|
else if (0 != stack && '}' == buf[*pos]) |
|
stack--; |
|
else if ('{' == buf[*pos]) |
|
stack++; |
|
advance(p, buf, pos); |
|
} |
|
if (stack) |
|
texiwarn(p, "unterminated macro " |
|
"in macro arguments"); |
|
if ((end = *pos) == sz) |
|
break; |
|
/* Test for zero-length '{ }'. */ |
|
if (start == end && '}' == buf[*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[start], end - start); |
|
args[*argsz][end - start] = '\0'; |
|
(*argsz)++; |
|
if ('}' == buf[*pos]) |
|
break; |
|
advance(p, buf, pos); |
|
} |
|
|
|
if (*pos == sz) |
|
texierr(p, "unterminated arguments"); |
|
assert('}' == buf[*pos]); |
|
advance(p, buf, pos); |
|
return(args); |
} |
} |