version 1.3, 2015/02/21 12:44:45 |
version 1.11, 2015/02/23 17:24:51 |
Line 46 texifilepop(struct texi *p) |
|
Line 46 texifilepop(struct texi *p) |
|
munmap(f->map, f->mapsz); |
munmap(f->map, f->mapsz); |
} |
} |
|
|
|
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 85 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++) |
|
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 146 texiputchar(struct texi *p, char c) |
|
Line 170 texiputchar(struct texi *p, char c) |
|
|
|
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); |
p->seenvs = 0; |
p->seenvs = 0; |
Line 169 texiputchars(struct texi *p, const char *s) |
|
Line 195 texiputchars(struct texi *p, const char *s) |
|
} |
} |
|
|
/* |
/* |
|
* 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. |
*/ |
*/ |
|
|
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 376 advanceto(struct texi *p, const char *buf, size_t *pos |
|
Line 417 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 414 texiword(struct texi *p, const char *buf, |
|
Line 548 texiword(struct texi *p, const char *buf, |
|
'\'' == buf[*pos + 1]) { |
'\'' == buf[*pos + 1]) { |
texiputchars(p, "\\(rq"); |
texiputchars(p, "\\(rq"); |
advance(p, buf, pos); |
advance(p, buf, pos); |
|
} else if ('\\' == buf[*pos]) { |
|
texiputchar(p, buf[*pos]); |
|
texiputchar(p, 'e'); |
} else |
} else |
texiputchar(p, buf[*pos]); |
texiputchar(p, buf[*pos]); |
advance(p, buf, pos); |
advance(p, buf, pos); |
Line 426 texiword(struct texi *p, const char *buf, |
|
Line 563 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; |
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 452 texicmd(struct texi *p, const char *buf, |
|
Line 592 texicmd(struct texi *p, const char *buf, |
|
return(TEXICMD__MAX); |
return(TEXICMD__MAX); |
} |
} |
|
|
|
/* Scan to the end of the possible command name. */ |
for (*end = pos; *end < sz && ! ismspace(buf[*end]); (*end)++) |
for (*end = pos; *end < sz && ! ismspace(buf[*end]); (*end)++) |
if ((*end > pos && ('@' == buf[*end] || |
if ((*end > pos && ('@' == buf[*end] || |
'{' == buf[*end] || '}' == buf[*end]))) |
'{' == buf[*end] || '}' == buf[*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) |
Line 465 texicmd(struct texi *p, const char *buf, |
|
Line 607 texicmd(struct texi *p, const char *buf, |
|
return(i); |
return(i); |
} |
} |
|
|
|
/* 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[pos], p->indexs[i], toksz)) |
|
continue; |
|
if (0 == strncmp(&buf[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[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 511 parsearg(struct texi *p, const char *buf, |
|
Line 675 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 528 parsearg(struct texi *p, const char *buf, |
|
Line 694 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 555 parsebracket(struct texi *p, const char *buf, size_t s |
|
Line 722 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 572 parsebracket(struct texi *p, const char *buf, size_t s |
|
Line 741 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 600 parseeoln(struct texi *p, const char *buf, size_t sz, |
|
Line 770 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 616 parseeoln(struct texi *p, const char *buf, size_t sz, |
|
Line 788 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 640 parsesingle(struct texi *p, const char *buf, size_t sz |
|
Line 813 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 688 parseeof(struct texi *p, const char *buf, size_t sz) |
|
Line 863 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 722 parseto(struct texi *p, const char *buf, |
|
Line 926 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 740 parseto(struct texi *p, const char *buf, |
|
Line 944 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); |
} |
} |
} |
} |
|
|
Line 760 parsefile(struct texi *p, const char *fname, int parse |
|
Line 968 parsefile(struct texi *p, const char *fname, int parse |
|
struct stat st; |
struct stat st; |
size_t i; |
size_t i; |
|
|
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)); |
|
|
Line 924 valueadd(struct texi *p, char *key, char *val) |
|
Line 1133 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, 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); |
} |
} |