version 1.16, 2015/02/25 15:50:56 |
version 1.35, 2018/11/13 08:45:29 |
|
|
#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> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
#include <time.h> |
|
#include <unistd.h> |
#include <unistd.h> |
|
|
#include "extern.h" |
#include "extern.h" |
|
|
/* |
/* |
|
* Table of macros. |
|
* These ABSOLUTELY MUST BE 2 or three characters long. |
|
*/ |
|
static const char *const mdocs[] = { |
|
"Ap", "Dd", "Dt", "Os", |
|
"Sh", "Ss", "Pp", "D1", |
|
"Dl", "Bd", "Ed", "Bl", |
|
"El", "It", "Ad", "An", |
|
"Ar", "Cd", "Cm", "Dv", |
|
"Er", "Ev", "Ex", "Fa", |
|
"Fd", "Fl", "Fn", "Ft", |
|
"Ic", "In", "Li", "Nd", |
|
"Nm", "Op", "Ot", "Pa", |
|
"Rv", "St", "Va", "Vt", |
|
"Xr", "%A", "%B", "%D", |
|
"%I", "%J", "%N", "%O", |
|
"%P", "%R", "%T", "%V", |
|
"Ac", "Ao", "Aq", "At", |
|
"Bc", "Bf", "Bo", "Bq", |
|
"Bsx", "Bx", "Db", "Dc", |
|
"Do", "Dq", "Ec", "Ef", |
|
"Em", "Eo", "Fx", "Ms", |
|
"No", "Ns", "Nx", "Ox", |
|
"Pc", "Pf", "Po", "Pq", |
|
"Qc", "Ql", "Qo", "Qq", |
|
"Re", "Rs", "Sc", "So", |
|
"Sq", "Sm", "Sx", "Sy", |
|
"Tn", "Ux", "Xc", "Xo", |
|
"Fo", "Fc", "Oo", "Oc", |
|
"Bk", "Ek", "Bt", "Hf", |
|
"Fr", "Ud", "Lb", "Lp", |
|
"Lk", "Mt", "Brq", "Bro", |
|
"Brc", "%C", "Es", "En", |
|
"Dx", "%Q", "br", "sp", |
|
"%U", "Ta", "ll", NULL, |
|
}; |
|
|
|
/* |
* Unmap the top-most file in the stack of files currently opened (that |
* Unmap the top-most file in the stack of files currently opened (that |
* is, nested calls to parsefile()). |
* is, nested calls to parsefile()). |
*/ |
*/ |
Line 67 texivaluefree(struct texivalue *p) |
|
Line 102 texivaluefree(struct texivalue *p) |
|
free(p->value); |
free(p->value); |
} |
} |
|
|
|
static void |
|
texidex_free(struct texidex *p) |
|
{ |
|
size_t i; |
|
|
|
for (i = 0; i < p->indexsz; i++) |
|
free(p->index[i].term); |
|
|
|
free(p->index); |
|
free(p->name); |
|
p->index = NULL; |
|
p->indexsz = 0; |
|
} |
|
|
/* |
/* |
|
* Add the text beginning at "index" and of "sz" bytes to the index |
|
* named "tok" with name size "toksz". |
|
* This will also output the necessary mdoc(7) to construct the index. |
|
*/ |
|
void |
|
texindex(struct texi *p, const char *tok, |
|
size_t toksz, const char *index, size_t sz) |
|
{ |
|
size_t i, isz; |
|
#ifdef HAVE_INDEX |
|
char *cp; |
|
#endif |
|
|
|
if (0 == sz) { |
|
texiwarn(p, "zero-length index entry"); |
|
return; |
|
} |
|
|
|
/* Look for the index. (Must be found.) */ |
|
for (i = 0; i < p->indexsz; i++) { |
|
if (strlen(p->indexs[i].name) != toksz) |
|
continue; |
|
if (strncmp(p->indexs[i].name, tok, toksz)) |
|
continue; |
|
break; |
|
} |
|
|
|
assert(i < p->indexsz); |
|
isz = p->indexs[i].indexsz; |
|
/* Reallocate index's terms. */ |
|
p->indexs[i].index = realloc |
|
(p->indexs[i].index, |
|
(isz + 1) * sizeof(struct texiterm)); |
|
if (NULL == p->indexs[i].index) |
|
texiabort(p, NULL); |
|
|
|
/* Add term to term array. */ |
|
p->indexs[i].index[isz].chapter = p->nodecur; |
|
p->indexs[i].index[isz].term = malloc(sz + 1); |
|
if (NULL == p->indexs[i].index[isz].term) |
|
texiabort(p, NULL); |
|
memcpy(p->indexs[i].index[isz].term, index, sz); |
|
p->indexs[i].index[isz].term[sz] = '\0'; |
|
|
|
/* Output mdoc(7) for index. */ |
|
#ifdef HAVE_INDEX |
|
p->seenvs = -1; |
|
teximacroopen(p, "Ix"); |
|
texiputchars(p, "idx"); |
|
texiputchars(p, p->indexs[i].name); |
|
cp = p->indexs[i].index[isz].term; |
|
while ('\n' != *cp) |
|
texiputchar(p, *cp++); |
|
teximacroclose(p); |
|
#endif |
|
p->indexs[i].indexsz++; |
|
} |
|
|
|
/* |
|
* Add an index entry named "tok" of length "sz". |
|
* This usually consists of two letters, e.g., "cp" or "vr". |
|
* This does nothing if the index exists or is zero-sized. |
|
*/ |
|
void |
|
texindex_add(struct texi *p, const char *tok, size_t sz) |
|
{ |
|
size_t i; |
|
char *cp; |
|
|
|
if (0 == sz) |
|
return; |
|
|
|
/* Make sure we don't have a duplicate. */ |
|
for (i = 0; i < p->indexsz; i++) { |
|
if (strlen(p->indexs[i].name) != sz) |
|
continue; |
|
if (strncmp(p->indexs[i].name, tok, sz)) |
|
continue; |
|
return; |
|
} |
|
|
|
/* Reallocate indices. */ |
|
p->indexs = realloc(p->indexs, |
|
sizeof(struct texidex) * |
|
(p->indexsz + 1)); |
|
if (NULL == p->indexs) |
|
texiabort(p, NULL); |
|
if (NULL == (cp = malloc(sz + 1))) |
|
texiabort(p, NULL); |
|
memcpy(cp, tok, sz); |
|
cp[sz] = '\0'; |
|
p->indexs[p->indexsz].name = cp; |
|
p->indexs[p->indexsz].index = NULL; |
|
p->indexs[p->indexsz].indexsz = 0; |
|
p->indexsz++; |
|
} |
|
|
|
/* |
* 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. |
* The utility should exit(...) after this is called. |
* The utility should exit(...) after this is called. |
Line 79 texiexit(struct texi *p) |
|
Line 226 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) |
Line 90 texiexit(struct texi *p) |
|
Line 239 texiexit(struct texi *p) |
|
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]); |
texidex_free(&p->indexs[i]); |
for (i = 0; i < p->valsz; i++) |
for (i = 0; i < p->valsz; i++) |
texivaluefree(&p->vals[i]); |
texivaluefree(&p->vals[i]); |
|
|
|
free(p->nodecache); |
free(p->macros); |
free(p->macros); |
free(p->vals); |
free(p->vals); |
free(p->indexs); |
free(p->indexs); |
free(p->dirs); |
free(p->dirs); |
free(p->subtitle); |
free(p->subtitle); |
free(p->title); |
free(p->title); |
|
free(p->copying); |
} |
} |
|
|
/* |
/* |
Line 122 texiabort(struct texi *p, const char *errstring) |
|
Line 273 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; |
const struct texifile *f; |
|
|
f = &p->files[p->filepos - 1]; |
f = &p->files[p->filepos - 1]; |
|
|
if (f->insplice) |
if (f->insplice) |
fprintf(stderr, "%s:%zu:%zu (%zuB left in splice): " |
fprintf(stderr, "%s:%zu:%zu (%zuB left in splice): " |
"warning: ", f->name, f->line + 1, |
"warning: ", f->name, f->line + 1, |
f->col + 1, f->insplice); |
f->col + 1, f->insplice); |
else |
else |
fprintf(stderr, "%s:%zu:%zu: warning: ", |
fprintf(stderr, "%s:%zu:%zu: warning: ", |
Line 148 texiwarn(const struct texi *p, const char *fmt, ...) |
|
Line 299 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; |
struct texifile *f; |
|
|
f = &p->files[p->filepos - 1]; |
f = &p->files[p->filepos - 1]; |
|
|
if (f->insplice) |
if (f->insplice) |
fprintf(stderr, "%s:%zu:%zu: (%zuB left in splice): " |
fprintf(stderr, "%s:%zu:%zu: (%zuB left in splice): " |
"error: ", f->name, f->line + 1, |
"error: ", f->name, f->line + 1, |
f->col + 1, f->insplice); |
f->col + 1, f->insplice); |
else |
else |
fprintf(stderr, "%s:%zu:%zu: error: ", |
fprintf(stderr, "%s:%zu:%zu: error: ", |
Line 180 texiputchar(struct texi *p, char c) |
|
Line 331 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) |
if ('\'' == c && 0 == p->outcol) |
fputs("\\&", stdout); |
fputs("\\&", p->outfile); |
|
|
putchar(c); |
if (p->uppercase) |
|
fputc(toupper((unsigned int)c), p->outfile); |
|
else |
|
fputc(c, p->outfile); |
if ('\\' == c) |
if ('\\' == c) |
putchar('e'); |
fputc('e', p->outfile); |
p->seenvs = 0; |
|
if ('\n' == c) { |
if ('\n' == c) { |
p->outcol = 0; |
p->outcol = 0; |
p->seenws = 0; |
p->seenws = 0; |
Line 207 texiputchars(struct texi *p, const char *s) |
|
Line 360 texiputchars(struct texi *p, const char *s) |
|
if (p->ign) |
if (p->ign) |
return; |
return; |
if ('.' == *s && 0 == p->outcol) |
if ('.' == *s && 0 == p->outcol) |
fputs("\\&", stdout); |
fputs("\\&", p->outfile); |
if ('\'' == *s && 0 == p->outcol) |
if ('\'' == *s && 0 == p->outcol) |
fputs("\\&", stdout); |
fputs("\\&", p->outfile); |
p->outcol += fputs(s, stdout); |
if (p->uppercase) |
p->seenvs = 0; |
for ( ; '\0' != *s; s++) |
|
p->outcol += fputc(toupper |
|
((unsigned int)*s), p->outfile); |
|
else |
|
p->outcol += fputs(s, p->outfile); |
} |
} |
|
|
/* |
/* |
|
|
teximacroclose(struct texi *p) |
teximacroclose(struct texi *p) |
{ |
{ |
|
|
if (p->ign) |
if (p->ign || p->literal|| TEXILIST_TABLE == p->list) |
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 255 teximacroopen(struct texi *p, const char *s) |
|
Line 412 teximacroopen(struct texi *p, const char *s) |
|
{ |
{ |
int rc; |
int rc; |
|
|
if (p->ign) |
if (p->ign || p->literal|| TEXILIST_TABLE == p->list) |
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 (p->seenvs > 0 && 0 == p->outmacro) |
|
fputs(".Pp\n", p->outfile); |
|
|
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 = p->seenvs = 0; |
} |
} |
|
|
/* |
/* |
Line 291 teximacro(struct texi *p, const char *s) |
|
Line 451 teximacro(struct texi *p, const char *s) |
|
texierr(p, "\"%s\" in open line scope!?", s); |
texierr(p, "\"%s\" in open line scope!?", s); |
if (p->literal) |
if (p->literal) |
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); |
|
if (p->seenvs > 0) |
|
fputs(".Pp\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 || TEXILIST_TABLE == p->list) |
if (TEXILIST_TABLE != p->list && p->seenvs >= 0) |
return; |
p->seenvs = 1; |
teximacro(p, "Pp"); |
|
p->seenvs = 1; |
|
} |
} |
|
|
/* |
/* |
Line 330 advance(struct texi *p, size_t *pos) |
|
Line 490 advance(struct texi *p, size_t *pos) |
|
f->col = 0; |
f->col = 0; |
} else |
} else |
f->col++; |
f->col++; |
} else |
} else { |
--f->insplice; |
--f->insplice; |
|
if (0 == f->insplice) |
|
f->depth = 0; |
|
} |
|
|
(*pos)++; |
(*pos)++; |
} |
} |
Line 358 texipunctuate(struct texi *p, size_t *pos) |
|
Line 521 texipunctuate(struct texi *p, size_t *pos) |
|
case ('.'): |
case ('.'): |
case ('"'): |
case ('"'): |
case (':'): |
case (':'): |
|
case (';'): |
case ('!'): |
case ('!'): |
case ('?'): |
case ('?'): |
continue; |
continue; |
Line 368 texipunctuate(struct texi *p, size_t *pos) |
|
Line 532 texipunctuate(struct texi *p, size_t *pos) |
|
} |
} |
if (end == *pos) |
if (end == *pos) |
return; |
return; |
if (end + 1 == BUFSZ(p) || ' ' == BUF(p)[end] || |
if (end + 1 == BUFSZ(p) || ' ' == BUF(p)[end] || |
'\n' == BUF(p)[end]) { |
'@' == BUF(p)[end] || '\n' == BUF(p)[end]) { |
for ( ; start < end; start++) { |
for ( ; start < end; start++) { |
texiputchar(p, ' '); |
texiputchar(p, ' '); |
texiputchar(p, BUF(p)[start]); |
texiputchar(p, BUF(p)[start]); |
Line 386 texipunctuate(struct texi *p, size_t *pos) |
|
Line 550 texipunctuate(struct texi *p, size_t *pos) |
|
static size_t |
static size_t |
advancenext(struct texi *p, size_t *pos) |
advancenext(struct texi *p, size_t *pos) |
{ |
{ |
|
|
if (p->literal) { |
if (p->literal) { |
while (*pos < BUFSZ(p) && ismspace(BUF(p)[*pos])) { |
while (*pos < BUFSZ(p) && ismspace(BUF(p)[*pos])) { |
texiputchar(p, BUF(p)[*pos]); |
texiputchar(p, BUF(p)[*pos]); |
advance(p, pos); |
advance(p, pos); |
} |
} |
return(*pos); |
return(*pos); |
} |
} |
|
|
while (*pos < BUFSZ(p) && ismspace(BUF(p)[*pos])) { |
while (*pos < BUFSZ(p) && ismspace(BUF(p)[*pos])) { |
p->seenws = 1; |
p->seenws = 1; |
/* |
if (0 == p->seenvs && '\n' == BUF(p)[*pos]) |
* If it looks like we've printed a double-line, then |
if (*pos + 1 < BUFSZ(p) && '\n' == BUF(p)[*pos + 1]) |
* output a paragraph. |
p->seenvs = 1; |
* FIXME: this is stupid. |
|
*/ |
|
if (*pos && '\n' == BUF(p)[*pos] && '\n' == BUF(p)[*pos - 1]) |
|
texivspace(p); |
|
advance(p, pos); |
advance(p, pos); |
} |
} |
return(*pos); |
return(*pos); |
Line 411 advancenext(struct texi *p, size_t *pos) |
|
Line 571 advancenext(struct texi *p, size_t *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, size_t *pos, int consumenl) |
advanceeoln(struct texi *p, size_t *pos, int consumenl) |
{ |
{ |
|
|
while (*pos < BUFSZ(p) && '\n' != BUF(p)[*pos]) |
while (*pos < BUFSZ(p) && '\n' != BUF(p)[*pos]) { |
advance(p, pos); |
if ('@' == BUF(p)[*pos]) |
|
advance(p, pos); |
|
if (*pos < BUFSZ(p)) |
|
advance(p, pos); |
|
} |
if (*pos < BUFSZ(p) && consumenl) |
if (*pos < BUFSZ(p) && consumenl) |
advance(p, pos); |
advance(p, pos); |
return(*pos); |
return(*pos); |
Line 434 advanceto(struct texi *p, size_t *pos, size_t end) |
|
Line 598 advanceto(struct texi *p, size_t *pos, size_t end) |
|
{ |
{ |
|
|
assert(*pos <= end); |
assert(*pos <= end); |
while (*pos < end) |
while (*pos < end) |
advance(p, pos); |
advance(p, pos); |
} |
} |
|
|
static void |
static void |
texiexecmacro(struct texi *p, struct teximacro *m, size_t *pos) |
texiexecmacro(struct texi *p, struct teximacro *m, size_t sv, size_t *pos) |
{ |
{ |
size_t valsz, realsz, aasz, asz, |
size_t valsz, realsz, aasz, asz, |
ssz, i, j, k, start, end; |
ssz, i, j, k, start, end; |
char *val; |
char *val; |
char **args; |
char **args; |
const char *cp; |
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 - 1].depth > 64) |
|
texierr(p, "maximium recursive depth"); |
|
|
args = argparse(p, pos, &asz, m->argsz); |
args = argparse(p, pos, &asz, m->argsz); |
if (asz != m->argsz) |
if (asz != m->argsz) |
texiwarn(p, "invalid macro argument length"); |
texiwarn(p, "invalid macro argument length"); |
aasz = asz < m->argsz ? asz : m->argsz; |
aasz = asz < m->argsz ? asz : m->argsz; |
|
|
if (0 == aasz) { |
if (0 == aasz) { |
texisplice(p, m->value, strlen(m->value), pos); |
texisplice(p, m->value, valsz, *pos); |
return; |
return; |
} |
} |
|
|
valsz = realsz = strlen(m->value); |
|
val = strdup(m->value); |
val = strdup(m->value); |
|
|
for (i = j = 0; i < realsz; i++) { |
for (i = j = 0; i < realsz; i++) { |
Line 480 texiexecmacro(struct texi *p, struct teximacro *m, siz |
|
Line 663 texiexecmacro(struct texi *p, struct teximacro *m, siz |
|
|
|
/* Parse to terminating delimiter. */ |
/* Parse to terminating delimiter. */ |
/* FIXME: embedded, escaped delimiters? */ |
/* FIXME: embedded, escaped delimiters? */ |
for (start = end = i + 1; end < realsz; end++) |
for (start = end = i + 1; end < realsz; end++) |
if ('\\' == m->value[end]) |
if ('\\' == m->value[end]) |
break; |
break; |
if (end == realsz) |
if (end == realsz) |
Line 494 texiexecmacro(struct texi *p, struct teximacro *m, siz |
|
Line 677 texiexecmacro(struct texi *p, struct teximacro *m, siz |
|
break; |
break; |
} |
} |
|
|
/* |
/* |
* Argument didn't exist in argument table. |
* Argument didn't exist in argument table. |
* Just ignore it. |
* Just ignore it. |
*/ |
*/ |
if (k == aasz) { |
if (k == aasz) { |
Line 510 texiexecmacro(struct texi *p, struct teximacro *m, siz |
|
Line 693 texiexecmacro(struct texi *p, struct teximacro *m, siz |
|
texiabort(p, NULL); |
texiabort(p, NULL); |
} |
} |
|
|
for (cp = args[k]; '\0' != *cp; cp++) |
for (cp = args[k]; '\0' != *cp; cp++) |
val[j++] = *cp; |
val[j++] = *cp; |
|
|
val[j] = '\0'; |
val[j] = '\0'; |
i = end; |
i = end; |
} |
} |
|
|
texisplice(p, val, strlen(val), pos); |
texisplice(p, val, strlen(val), *pos); |
|
|
for (i = 0; i < asz; i++) |
for (i = 0; i < asz; i++) |
free(args[i]); |
free(args[i]); |
free(args); |
free(args); |
free(val); |
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 |
Line 533 texiexecmacro(struct texi *p, struct teximacro *m, siz |
|
Line 716 texiexecmacro(struct texi *p, struct teximacro *m, siz |
|
static void |
static void |
parseword(struct texi *p, size_t *pos, char extra) |
parseword(struct texi *p, size_t *pos, char extra) |
{ |
{ |
|
size_t i, end, len; |
|
int c; |
|
|
if (p->seenws && 0 == p->outmacro && |
/* |
|
* If a prior word had a terminating double-newline, then begin |
|
* this text block with a `Pp'. |
|
* We don't do this if we're in a literal context (we'll print |
|
* out the newlines themselves) nor in a `TS' table. |
|
*/ |
|
if (p->seenvs > 0 && 0 == p->literal && TEXILIST_TABLE != p->list) { |
|
if (p->outcol > 0) |
|
fputc('\n', p->outfile); |
|
fputs(".Pp\n", p->outfile); |
|
p->outcol = 0; |
|
} |
|
|
|
/* |
|
* Some line control: if we (non-macro, non-literal) already |
|
* have more than 72 characters written to the screen, then |
|
* output a newline before getting started. |
|
*/ |
|
if (p->seenws && 0 == p->outmacro && |
p->outcol > 72 && 0 == p->literal) |
p->outcol > 72 && 0 == p->literal) |
texiputchar(p, '\n'); |
texiputchar(p, '\n'); |
/* FIXME: abstract this: we use it elsewhere. */ |
|
|
/* Usual padding in the case of seen whitespace. */ |
if (p->seenws && p->outcol && 0 == p->literal) |
if (p->seenws && p->outcol && 0 == p->literal) |
texiputchar(p, ' '); |
texiputchar(p, ' '); |
|
|
p->seenws = 0; |
p->seenws = 0; |
|
|
|
/* |
|
* If we're in a macro line, we might want to print text that |
|
* happens to be the same as an mdoc(7) macro. |
|
* Obviously, we need to escape these words. |
|
*/ |
|
if (p->outmacro) { |
|
end = *pos; |
|
/* Read ahead to get the word length. */ |
|
while (end < BUFSZ(p) && ! ismspace(BUF(p)[end])) { |
|
switch ((c = BUF(p)[end])) { |
|
case ('@'): |
|
case ('}'): |
|
case ('{'): |
|
break; |
|
default: |
|
if ('\0' != extra && extra == c) |
|
break; |
|
end++; |
|
continue; |
|
} |
|
break; |
|
} |
|
len = end - *pos; |
|
/* See if we have a match. */ |
|
for (i = 0; NULL != mdocs[i]; i++) { |
|
/* All macros are 2 or three letters. */ |
|
if (len < 2 || len > 3) |
|
continue; |
|
/* Check the macro word length. */ |
|
if ('\0' == mdocs[i][2] && 2 != len) |
|
continue; |
|
else if ('\0' == mdocs[i][3] && 3 != len) |
|
continue; |
|
if (strncmp(mdocs[i], &BUF(p)[*pos], len)) |
|
continue; |
|
texiputchars(p, "\\&"); |
|
break; |
|
} |
|
} |
|
|
while (*pos < BUFSZ(p) && ! ismspace(BUF(p)[*pos])) { |
while (*pos < BUFSZ(p) && ! ismspace(BUF(p)[*pos])) { |
switch (BUF(p)[*pos]) { |
switch (BUF(p)[*pos]) { |
case ('@'): |
case ('@'): |
Line 552 parseword(struct texi *p, size_t *pos, char extra) |
|
Line 796 parseword(struct texi *p, size_t *pos, char extra) |
|
} |
} |
if ('\0' != extra && BUF(p)[*pos] == extra) |
if ('\0' != extra && BUF(p)[*pos] == extra) |
return; |
return; |
if (*pos < BUFSZ(p) - 1 && |
|
'`' == BUF(p)[*pos] && |
if (p->literal) { |
|
texiputchar(p, BUF(p)[*pos]); |
|
advance(p, pos); |
|
continue; |
|
} |
|
|
|
if ('"' == BUF(p)[*pos]) { |
|
texiputchars(p, "\\(dq"); |
|
} else if (*pos < BUFSZ(p) - 2 && |
|
'-' == BUF(p)[*pos] && |
|
'-' == BUF(p)[*pos + 1] && |
|
'-' == BUF(p)[*pos + 2]) { |
|
texiputchars(p, "\\(em"); |
|
advance(p, pos); |
|
advance(p, pos); |
|
} else if (*pos < BUFSZ(p) - 1 && |
|
'-' == BUF(p)[*pos] && |
|
'-' == BUF(p)[*pos + 1]) { |
|
texiputchars(p, "\\(en"); |
|
advance(p, pos); |
|
} else if (*pos < BUFSZ(p) - 1 && |
|
'`' == BUF(p)[*pos] && |
'`' == BUF(p)[*pos + 1]) { |
'`' == BUF(p)[*pos + 1]) { |
texiputchars(p, "\\(lq"); |
texiputchars(p, "\\(lq"); |
advance(p, pos); |
advance(p, pos); |
} else if (*pos < BUFSZ(p) - 1 && |
} else if (*pos < BUFSZ(p) - 1 && |
'\'' == BUF(p)[*pos] && |
'\'' == BUF(p)[*pos] && |
'\'' == BUF(p)[*pos + 1]) { |
'\'' == BUF(p)[*pos + 1]) { |
texiputchars(p, "\\(rq"); |
texiputchars(p, "\\(rq"); |
advance(p, pos); |
advance(p, pos); |
} else |
} else |
texiputchar(p, BUF(p)[*pos]); |
texiputchar(p, BUF(p)[*pos]); |
|
|
advance(p, pos); |
advance(p, pos); |
} |
} |
|
|
|
/* |
|
* New sentence, new line:if we (non-macro, non-literal) see a |
|
* period at the end of the last printed word, then open a |
|
* newline. |
|
*/ |
|
if (0 == p->literal && 0 == p->outmacro && *pos < BUFSZ(p)) |
|
switch (BUF(p)[*pos - 1]) { |
|
case ('.'): |
|
case ('!'): |
|
case ('?'): |
|
texiputchar(p, '\n'); |
|
break; |
|
default: |
|
break; |
|
} |
|
|
|
p->seenvs = 0; |
} |
} |
|
|
/* |
/* |
Line 574 parseword(struct texi *p, size_t *pos, char extra) |
|
Line 858 parseword(struct texi *p, size_t *pos, char extra) |
|
* index after the command name. |
* index after the command name. |
*/ |
*/ |
enum texicmd |
enum texicmd |
texicmd(struct texi *p, size_t pos, size_t *end, struct teximacro **macro) |
texicmd(const struct texi *p, size_t pos, size_t *end, struct teximacro **macro) |
{ |
{ |
size_t i, len, toksz; |
size_t i, len, toksz; |
|
|
Line 589 texicmd(struct texi *p, size_t pos, size_t *end, struc |
|
Line 873 texicmd(struct texi *p, size_t pos, size_t *end, struc |
|
return(TEXICMD__MAX); |
return(TEXICMD__MAX); |
|
|
/* Alphabetic commands are special. */ |
/* Alphabetic commands are special. */ |
if ( ! isalpha((unsigned char)BUF(p)[pos])) { |
if ( ! isalpha((unsigned int)BUF(p)[pos])) { |
if ((*end = pos + 1) == BUFSZ(p)) |
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++) { |
Line 604 texicmd(struct texi *p, size_t pos, size_t *end, struc |
|
Line 888 texicmd(struct texi *p, size_t pos, size_t *end, struc |
|
|
|
/* Scan to the end of the possible command name. */ |
/* Scan to the end of the possible command name. */ |
for (*end = pos; *end < BUFSZ(p) && ! ismspace(BUF(p)[*end]); (*end)++) |
for (*end = pos; *end < BUFSZ(p) && ! ismspace(BUF(p)[*end]); (*end)++) |
if ((*end > pos && ('@' == BUF(p)[*end] || |
if ((*end > pos && ('@' == BUF(p)[*end] || |
'{' == BUF(p)[*end] || '}' == BUF(p)[*end]))) |
'{' == BUF(p)[*end] || '}' == BUF(p)[*end]))) |
break; |
break; |
|
|
Line 619 texicmd(struct texi *p, size_t pos, size_t *end, struc |
|
Line 903 texicmd(struct texi *p, size_t pos, size_t *end, struc |
|
|
|
/* Look for it in our indices. */ |
/* Look for it in our indices. */ |
for (i = 0; i < p->indexsz; i++) { |
for (i = 0; i < p->indexsz; i++) { |
toksz = strlen(p->indexs[i]); |
toksz = strlen(p->indexs[i].name); |
if (len != 5 + toksz) |
if (len != 5 + toksz) |
continue; |
continue; |
if (strncmp(&BUF(p)[pos], p->indexs[i], toksz)) |
if (strncmp(&BUF(p)[pos], p->indexs[i].name, toksz)) |
continue; |
continue; |
if (0 == strncmp(&BUF(p)[pos + toksz], "index", 5)) |
if (0 == strncmp(&BUF(p)[pos + toksz], "index", 5)) |
return(TEXICMD_USER_INDEX); |
return(TEXICMD_USER_INDEX); |
Line 653 texicmd(struct texi *p, size_t pos, size_t *end, struc |
|
Line 937 texicmd(struct texi *p, size_t pos, size_t *end, struc |
|
int |
int |
parsearg(struct texi *p, size_t *pos, size_t num) |
parsearg(struct texi *p, size_t *pos, size_t num) |
{ |
{ |
size_t end; |
size_t end, sv; |
enum texicmd cmd; |
enum texicmd cmd; |
struct teximacro *macro; |
struct teximacro *macro; |
|
|
Line 684 parsearg(struct texi *p, size_t *pos, size_t num) |
|
Line 968 parsearg(struct texi *p, size_t *pos, size_t num) |
|
continue; |
continue; |
} |
} |
|
|
|
sv = p->files[p->filepos - 1].insplice; |
cmd = texicmd(p, *pos, &end, ¯o); |
cmd = texicmd(p, *pos, &end, ¯o); |
advanceto(p, pos, end); |
advanceto(p, pos, end); |
if (NULL != macro) |
if (NULL != macro) |
texiexecmacro(p, macro, pos); |
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, pos); |
(*texitoks[cmd].fp)(p, cmd, pos); |
Line 701 parsearg(struct texi *p, size_t *pos, size_t num) |
|
Line 986 parsearg(struct texi *p, size_t *pos, size_t num) |
|
* 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, 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; |
struct teximacro *macro; |
|
|
Line 714 parsebracket(struct texi *p, size_t *pos) |
|
Line 999 parsebracket(struct texi *p, size_t *pos) |
|
return; |
return; |
advance(p, pos); |
advance(p, pos); |
|
|
|
stack = 0; |
while ((*pos = advancenext(p, pos)) < BUFSZ(p)) { |
while ((*pos = advancenext(p, pos)) < BUFSZ(p)) { |
switch (BUF(p)[*pos]) { |
switch (BUF(p)[*pos]) { |
case ('}'): |
case ('}'): |
|
if (stack > 0) { |
|
stack--; |
|
advance(p, pos); |
|
texiputchar(p, '}'); |
|
continue; |
|
} |
advance(p, pos); |
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, pos); |
advance(p, pos); |
Line 731 parsebracket(struct texi *p, size_t *pos) |
|
Line 1029 parsebracket(struct texi *p, size_t *pos) |
|
continue; |
continue; |
} |
} |
|
|
|
sv = p->files[p->filepos - 1].insplice; |
cmd = texicmd(p, *pos, &end, ¯o); |
cmd = texicmd(p, *pos, &end, ¯o); |
advanceto(p, pos, end); |
advanceto(p, pos, end); |
if (NULL != macro) |
if (NULL != macro) |
texiexecmacro(p, macro, pos); |
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, pos); |
(*texitoks[cmd].fp)(p, cmd, pos); |
Line 750 parsebracket(struct texi *p, size_t *pos) |
|
Line 1049 parsebracket(struct texi *p, size_t *pos) |
|
void |
void |
parseeoln(struct texi *p, 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; |
struct teximacro *macro; |
|
|
Line 761 parseeoln(struct texi *p, size_t *pos) |
|
Line 1060 parseeoln(struct texi *p, size_t *pos) |
|
texiputchar(p, BUF(p)[*pos]); |
texiputchar(p, BUF(p)[*pos]); |
advance(p, pos); |
advance(p, pos); |
} |
} |
|
if (*pos == BUFSZ(p)) { |
|
texiwarn(p, "unexpected EOF"); |
|
return; |
|
} |
switch (BUF(p)[*pos]) { |
switch (BUF(p)[*pos]) { |
case ('}'): |
case ('}'): |
if (0 == p->ign) |
if (0 == p->ign) |
Line 772 parseeoln(struct texi *p, size_t *pos) |
|
Line 1075 parseeoln(struct texi *p, size_t *pos) |
|
texiwarn(p, "unexpected \"{\""); |
texiwarn(p, "unexpected \"{\""); |
advance(p, pos); |
advance(p, pos); |
continue; |
continue; |
|
case ('\n'): |
|
continue; |
case ('@'): |
case ('@'): |
break; |
break; |
default: |
default: |
Line 779 parseeoln(struct texi *p, size_t *pos) |
|
Line 1084 parseeoln(struct texi *p, size_t *pos) |
|
continue; |
continue; |
} |
} |
|
|
|
sv = p->files[p->filepos - 1].insplice; |
cmd = texicmd(p, *pos, &end, ¯o); |
cmd = texicmd(p, *pos, &end, ¯o); |
advanceto(p, pos, end); |
advanceto(p, pos, end); |
if (NULL != macro) |
if (NULL != macro) |
texiexecmacro(p, macro, pos); |
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, pos); |
(*texitoks[cmd].fp)(p, cmd, pos); |
Line 793 parseeoln(struct texi *p, size_t *pos) |
|
Line 1099 parseeoln(struct texi *p, size_t *pos) |
|
advance(p, pos); |
advance(p, pos); |
} |
} |
|
|
|
enum texicmd |
|
peeklinecmd(const struct texi *p, size_t pos) |
|
{ |
|
size_t end; |
|
|
|
while (pos < BUFSZ(p) && isws(BUF(p)[pos])) |
|
pos++; |
|
if (pos == BUFSZ(p) || '@' != BUF(p)[pos]) |
|
return(TEXICMD__MAX); |
|
return(texicmd(p, pos, &end, NULL)); |
|
} |
|
|
/* |
/* |
|
* 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. |
*/ |
*/ |
static void |
void |
parsesingle(struct texi *p, 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; |
struct teximacro *macro; |
|
|
Line 825 parsesingle(struct texi *p, size_t *pos) |
|
Line 1160 parsesingle(struct texi *p, size_t *pos) |
|
return; |
return; |
} |
} |
|
|
|
sv = p->files[p->filepos - 1].insplice; |
cmd = texicmd(p, *pos, &end, ¯o); |
cmd = texicmd(p, *pos, &end, ¯o); |
advanceto(p, pos, end); |
advanceto(p, pos, end); |
if (NULL != macro) |
if (NULL != macro) |
texiexecmacro(p, macro, pos); |
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, pos); |
(*texitoks[cmd].fp)(p, cmd, pos); |
Line 853 parselinearg(struct texi *p, size_t *pos) |
|
Line 1189 parselinearg(struct texi *p, size_t *pos) |
|
} |
} |
|
|
if (*pos < BUFSZ(p) && '{' == BUF(p)[*pos]) |
if (*pos < BUFSZ(p) && '{' == BUF(p)[*pos]) |
parsebracket(p, pos); |
parsebracket(p, pos, 0); |
else if (*pos < BUFSZ(p) && '\n' != BUF(p)[*pos]) |
else if (*pos < BUFSZ(p) && '\n' != BUF(p)[*pos]) |
parsesingle(p, pos); |
parsesingle(p, pos); |
else |
else |
Line 870 parseeof(struct texi *p) |
|
Line 1206 parseeof(struct texi *p) |
|
{ |
{ |
size_t pos; |
size_t pos; |
|
|
for (pos = 0; pos < BUFSZ(p); ) |
for (pos = 0; pos < BUFSZ(p); ) |
parsesingle(p, &pos); |
parsesingle(p, &pos); |
} |
} |
|
|
void |
void |
texisplice(struct texi *p, const char *buf, size_t sz, size_t *pos) |
texisplice(struct texi *p, const char *buf, size_t sz, size_t pos) |
{ |
{ |
char *cp; |
char *cp; |
struct texifile *f; |
struct texifile *f; |
Line 892 texisplice(struct texi *p, const char *buf, size_t sz, |
|
Line 1228 texisplice(struct texi *p, const char *buf, size_t sz, |
|
} |
} |
|
|
f->insplice += sz; |
f->insplice += sz; |
memmove(f->map + *pos + sz, f->map + *pos, f->mapsz - *pos); |
memmove(f->map + pos + sz, f->map + pos, f->mapsz - pos); |
memcpy(f->map + *pos, buf, sz); |
memcpy(f->map + pos, buf, sz); |
f->mapsz += sz; |
f->mapsz += sz; |
} |
} |
|
|
Line 905 texisplice(struct texi *p, const char *buf, size_t sz, |
|
Line 1241 texisplice(struct texi *p, const char *buf, size_t sz, |
|
void |
void |
parseto(struct texi *p, size_t *pos, const char *endtoken) |
parseto(struct texi *p, 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; |
struct teximacro *macro; |
|
|
endtoksz = strlen(endtoken); |
endtoksz = strlen(endtoken); |
assert(endtoksz > 0); |
assert(endtoksz > 0); |
|
|
while ((*pos = advancenext(p, pos)) < BUFSZ(p)) { |
while ((*pos = advancenext(p, pos)) < BUFSZ(p)) { |
switch (BUF(p)[*pos]) { |
switch (BUF(p)[*pos]) { |
case ('}'): |
case ('}'): |
Line 932 parseto(struct texi *p, size_t *pos, const char *endto |
|
Line 1268 parseto(struct texi *p, size_t *pos, const char *endto |
|
continue; |
continue; |
} |
} |
|
|
|
sv = p->files[p->filepos - 1].insplice; |
cmd = texicmd(p, *pos, &end, ¯o); |
cmd = texicmd(p, *pos, &end, ¯o); |
advanceto(p, pos, end); |
advanceto(p, pos, end); |
if (TEXICMD_END == cmd) { |
if (TEXICMD_END == cmd) { |
while (*pos < BUFSZ(p) && isws(BUF(p)[*pos])) |
while (*pos < BUFSZ(p) && isws(BUF(p)[*pos])) |
advance(p, pos); |
advance(p, pos); |
/* |
/* |
* FIXME: check the full word, not just its |
* FIXME: check the full word, not just its |
* initial substring! |
* initial substring! |
*/ |
*/ |
Line 950 parseto(struct texi *p, size_t *pos, const char *endto |
|
Line 1287 parseto(struct texi *p, size_t *pos, const char *endto |
|
texiwarn(p, "unexpected \"end\""); |
texiwarn(p, "unexpected \"end\""); |
advanceeoln(p, pos, 0); |
advanceeoln(p, pos, 0); |
continue; |
continue; |
} |
} |
if (NULL != macro) |
if (NULL != macro) |
texiexecmacro(p, macro, pos); |
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, pos); |
(*texitoks[cmd].fp)(p, cmd, pos); |
} |
} |
|
|
|
if (*pos == BUFSZ(p)) |
|
texiwarn(p, "EOF expecting \"%s\" end\n", endtoken); |
} |
} |
|
|
/* |
/* |
Line 981 parsestdin(struct texi *p) |
|
Line 1321 parsestdin(struct texi *p) |
|
if (f->mapsz == f->mapmaxsz) { |
if (f->mapsz == f->mapmaxsz) { |
if (f->mapmaxsz == (1U << 31)) |
if (f->mapmaxsz == (1U << 31)) |
texierr(p, "stdin buffer too long"); |
texierr(p, "stdin buffer too long"); |
f->mapmaxsz = f->mapmaxsz > 65536 / 2 ? |
f->mapmaxsz = f->mapmaxsz > 65536 / 2 ? |
2 * f->mapmaxsz : 65536; |
2 * f->mapmaxsz : 65536; |
f->map = realloc(f->map, f->mapmaxsz); |
f->map = realloc(f->map, f->mapmaxsz); |
if (NULL == f->map) |
if (NULL == f->map) |
texiabort(p, NULL); |
texiabort(p, NULL); |
} |
} |
ssz = read(STDIN_FILENO, f->map + |
ssz = read(STDIN_FILENO, f->map + |
(int)f->mapsz, f->mapmaxsz - f->mapsz); |
(int)f->mapsz, f->mapmaxsz - f->mapsz); |
if (0 == ssz) |
if (0 == ssz) |
break; |
break; |
else if (-1 == ssz) |
else if (-1 == ssz) |
texiabort(p, NULL); |
texiabort(p, NULL); |
} |
} |
|
|
Line 1015 parsefile(struct texi *p, const char *fname, int parse |
|
Line 1355 parsefile(struct texi *p, const char *fname, int parse |
|
size_t i; |
size_t i; |
char *map; |
char *map; |
|
|
if (64 == p->filepos) |
if (64 == p->filepos) |
texierr(p, "too many open files"); |
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 1027 parsefile(struct texi *p, const char *fname, int parse |
|
Line 1367 parsefile(struct texi *p, const char *fname, int parse |
|
} else if (-1 == fstat(fd, &st)) { |
} else if (-1 == fstat(fd, &st)) { |
close(fd); |
close(fd); |
texiabort(p, fname); |
texiabort(p, fname); |
} |
} |
|
|
f->mapsz = f->mapmaxsz = st.st_size; |
f->mapsz = f->mapmaxsz = st.st_size; |
map = mmap(NULL, f->mapsz, |
map = mmap(NULL, f->mapsz, |
Line 1177 valueadd(struct texi *p, char *key, char *val) |
|
Line 1517 valueadd(struct texi *p, char *key, char *val) |
|
p->vals[i].value = val; |
p->vals[i].value = val; |
} else { |
} else { |
/* FIXME: reallocarray() */ |
/* 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) |
Line 1209 argparse(struct texi *p, size_t *pos, size_t *argsz, s |
|
Line 1549 argparse(struct texi *p, size_t *pos, size_t *argsz, s |
|
args = NULL; |
args = NULL; |
*argsz = 0; |
*argsz = 0; |
|
|
|
if (*pos == BUFSZ(p)) |
|
return(args); |
|
|
if ('{' != BUF(p)[*pos] && hint) { |
if ('{' != BUF(p)[*pos] && hint) { |
/* |
/* |
* Special case: if we encounter an unbracketed argument |
* Special case: if we encounter an unbracketed argument |
Line 1235 argparse(struct texi *p, size_t *pos, size_t *argsz, s |
|
Line 1578 argparse(struct texi *p, size_t *pos, size_t *argsz, s |
|
} else if ('{' != BUF(p)[*pos]) |
} else if ('{' != BUF(p)[*pos]) |
return(args); |
return(args); |
|
|
|
assert('{' == BUF(p)[*pos]); |
|
|
/* Parse til the closing '}', putting into the array. */ |
/* Parse til the closing '}', putting into the array. */ |
advance(p, pos); |
advance(p, pos); |
while (*pos < BUFSZ(p)) { |
while (*pos < BUFSZ(p)) { |
Line 1243 argparse(struct texi *p, size_t *pos, size_t *argsz, s |
|
Line 1588 argparse(struct texi *p, size_t *pos, size_t *argsz, s |
|
start = *pos; |
start = *pos; |
stack = 0; |
stack = 0; |
while (*pos < BUFSZ(p)) { |
while (*pos < BUFSZ(p)) { |
/* |
/* |
* According to the manual, commas within |
* According to the manual, commas within |
* embedded commands are escaped. |
* embedded commands are escaped. |
* We keep track of embedded-ness in the "stack" |
* We keep track of embedded-ness in the "stack" |
Line 1285 argparse(struct texi *p, size_t *pos, size_t *argsz, s |
|
Line 1630 argparse(struct texi *p, size_t *pos, size_t *argsz, s |
|
advance(p, pos); |
advance(p, pos); |
} |
} |
|
|
if (*pos == BUFSZ(p)) |
if (*pos == BUFSZ(p)) |
texierr(p, "unterminated arguments"); |
texierr(p, "unterminated arguments"); |
assert('}' == BUF(p)[*pos]); |
assert('}' == BUF(p)[*pos]); |
advance(p, pos); |
advance(p, pos); |
return(args); |
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 || 1 == p->nodesz) |
|
return; |
|
|
|
teximacro(p, "Sh INFO NAVIGATION"); |
|
|
|
/* Print a reference to the "top" node. */ |
|
if (-1 != p->nodecache[p->nodecur].up) { |
|
texiputchars(p, "Top node,"); |
|
snprintf(buf, sizeof(buf), "%s-%zd 7", |
|
p->chapters, p->nodecache[p->nodecur].up); |
|
p->seenvs = 0; |
|
teximacroopen(p, "Xr "); |
|
texiputchars(p, buf); |
|
texiputchars(p, " ;"); |
|
teximacroclose(p); |
|
} |
|
|
|
if (-1 != p->nodecache[p->nodecur].prev) { |
|
texiputchars(p, "previous node,"); |
|
snprintf(buf, sizeof(buf), "%s-%zd 7", |
|
p->chapters, p->nodecache[p->nodecur].prev); |
|
p->seenvs = 0; |
|
teximacroopen(p, "Xr "); |
|
texiputchars(p, buf); |
|
if ( ! last) |
|
texiputchars(p, " ;"); |
|
teximacroclose(p); |
|
} |
|
|
|
if (-1 != p->nodecache[p->nodecur].next) { |
|
texiputchars(p, "next node,"); |
|
snprintf(buf, sizeof(buf), "%s-%zd 7", |
|
p->chapters, p->nodecache[p->nodecur].next); |
|
p->seenvs = 0; |
|
teximacroopen(p, "Xr "); |
|
texiputchars(p, buf); |
|
teximacroclose(p); |
|
} |
|
|
|
fclose(p->outfile); |
|
p->outfile = NULL; |
|
} |
|
|
|
ssize_t |
|
texicache(struct texi *p, const char *buf, size_t sz) |
|
{ |
|
size_t i; |
|
|
|
for (i = 0; i < p->nodecachesz; i++) { |
|
if (sz != strlen(p->nodecache[i].name)) |
|
continue; |
|
if (strncmp(buf, p->nodecache[i].name, sz)) |
|
continue; |
|
break; |
|
} |
|
if (i < p->nodecachesz) |
|
return(i); |
|
if (NULL == buf) |
|
return(-1); |
|
p->nodecache = realloc |
|
(p->nodecache, |
|
(p->nodecachesz + 1) * sizeof(struct texinode)); |
|
if (NULL == p->nodecache) |
|
texiabort(p, NULL); |
|
p->nodecache[p->nodecachesz].name = malloc(sz + 1); |
|
if (NULL == p->nodecache[p->nodecachesz].name) |
|
texiabort(p, NULL); |
|
memcpy(p->nodecache[p->nodecachesz].name, buf, sz); |
|
p->nodecache[p->nodecachesz].name[sz] = '\0'; |
|
p->nodecache[p->nodecachesz].up = |
|
p->nodecache[p->nodecachesz].next = |
|
p->nodecache[p->nodecachesz].prev = -1; |
|
p->nodecachesz++; |
|
return(p->nodecachesz - 1); |
|
} |
|
|
|
/* |
|
* 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 modification time of the input. |
|
*/ |
|
void |
|
teximdocopen(struct texi *p, size_t *pos) |
|
{ |
|
const char *cp; |
|
|
|
p->seenvs = -1; |
|
teximacroopen(p, "Dd"); |
|
texiputchars(p, 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); |
|
} |
|
|