Download raw body.
gotwebd: convert newfile/closefile() to push/popfile()
hello =)
this is a preparatory step to introduce the types {} block like httpd
has. It is easier to have include support if we have a per-file push
back buffer, like many other parse.y have.
For the whole picture, please take a look at op/mime.
This is the first of three steps.
To be clear I'm not introducing support for a generic include, though
it can be done easily now, but just a `types { include "mime" }'
ok?
commit 0a31df8c0bc50588eaf6b3811cba75171e9d785a
from: Omar Polo <op@omarpolo.com>
date: Sat Nov 8 17:57:00 2025 UTC
gotwebd: convert newfile/closefile() to push/popfile()
reduces the differences with other parse.y and ease the porting of
the types {} block from httpd.
diff 8a51fc8e116e5b7b8b7ef7d1dfddccb7370292f0 0a31df8c0bc50588eaf6b3811cba75171e9d785a
commit - 8a51fc8e116e5b7b8b7ef7d1dfddccb7370292f0
commit + 0a31df8c0bc50588eaf6b3811cba75171e9d785a
blob - e40746b876fd3e13f4abf439dcafafe4fc8c7376
blob + 2d2ee4101b7e053f0ff9407470cd0426fe330675
--- gotwebd/parse.y
+++ gotwebd/parse.y
@@ -61,11 +61,15 @@ static struct file {
TAILQ_ENTRY(file) entry;
FILE *stream;
char *name;
+ size_t ungetpos;
+ size_t ungetsize;
+ unsigned char *ungetbuf;
+ int eof_reached;
int lineno;
int errors;
-} *file;
-struct file *newfile(const char *, int);
-static void closefile(struct file *);
+} *file, *topfile;
+struct file *pushfile(const char *, int);
+static int popfile(void);
int check_file_secrecy(int, const char *);
int yyparse(void);
int yylex(void);
@@ -74,8 +78,9 @@ int yyerror(const char *, ...)
__attribute__((__nonnull__ (1)));
int kw_cmp(const void *, const void *);
int lookup(char *);
+int igetc(void);
int lgetc(int);
-int lungetc(int);
+void lungetc(int);
int findeol(void);
TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
@@ -791,69 +796,90 @@ lookup(char *s)
return (STRING);
}
-#define MAXPUSHBACK 128
+#define START_EXPAND 1
+#define DONE_EXPAND 2
-unsigned char *parsebuf;
-int parseindex;
-unsigned char pushback_buffer[MAXPUSHBACK];
-int pushback_index = 0;
+static int expanding;
int
+igetc(void)
+{
+ int c;
+
+ while (1) {
+ if (file->ungetpos > 0)
+ c = file->ungetbuf[--file->ungetpos];
+ else
+ c = getc(file->stream);
+
+ if (c == START_EXPAND)
+ expanding = 1;
+ else if (c == DONE_EXPAND)
+ expanding = 0;
+ else
+ break;
+ }
+ return c;
+}
+
+int
lgetc(int quotec)
{
int c, next;
- if (parsebuf) {
- /* Read character from the parsebuffer instead of input. */
- if (parseindex >= 0) {
- c = parsebuf[parseindex++];
- if (c != '\0')
- return (c);
- parsebuf = NULL;
- } else
- parseindex++;
- }
-
- if (pushback_index)
- return (pushback_buffer[--pushback_index]);
-
if (quotec) {
- c = getc(file->stream);
- if (c == EOF)
+ if ((c = igetc()) == EOF) {
yyerror("reached end of file while parsing "
"quoted string");
- return (c);
+ if (file == topfile || popfile() == EOF)
+ return EOF;
+ return quotec;
+ }
+ return c;
}
- c = getc(file->stream);
- while (c == '\\') {
- next = getc(file->stream);
+ while ((c = igetc()) == '\\') {
+ next = igetc();
if (next != '\n') {
c = next;
break;
}
yylval.lineno = file->lineno;
file->lineno++;
- c = getc(file->stream);
}
+ if (c == EOF) {
+ /*
+ * Fake EOL when hit EOF for the first time. This gets line
+ * count right if last line in included file is syntactically
+ * invalid and has no newline.
+ */
+ if (file->eof_reached == 0) {
+ file->eof_reached = 1;
+ return '\n';
+ }
+ while (c == EOF) {
+ if (file == topfile || popfile() == EOF)
+ return EOF;
+ c = igetc();
+ }
+ }
return (c);
}
-int
+void
lungetc(int c)
{
if (c == EOF)
- return (EOF);
- if (parsebuf) {
- parseindex--;
- if (parseindex >= 0)
- return (c);
+ return;
+ if (file->ungetpos >= file->ungetsize) {
+ void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
+ if (p == NULL)
+ fatal("reallocarray");
+ file->ungetbuf = p;
+ file->ungetsize *= 2;
}
- if (pushback_index < MAXPUSHBACK-1)
- return (pushback_buffer[pushback_index++] = c);
- else
- return (EOF);
+ file->ungetbuf[file->ungetpos++] = c;
}
int
@@ -861,14 +887,9 @@ findeol(void)
{
int c;
- parsebuf = NULL;
-
/* Skip to either EOF or the first real EOL. */
while (1) {
- if (pushback_index)
- c = pushback_buffer[--pushback_index];
- else
- c = lgetc(0);
+ c = lgetc(0);
if (c == '\n') {
file->lineno++;
break;
@@ -887,7 +908,6 @@ yylex(void)
int quotec, next, c;
int token;
-top:
p = buf;
c = lgetc(0);
while (c == ' ' || c == '\t')
@@ -899,7 +919,7 @@ top:
while (c != '\n' && c != EOF)
c = lgetc(0); /* nothing */
}
- if (c == '$' && parsebuf == NULL) {
+ if (c == '$' && !expanding) {
while (1) {
c = lgetc(0);
if (c == EOF)
@@ -922,9 +942,11 @@ top:
yyerror("macro '%s' not defined", buf);
return (findeol());
}
- parsebuf = val;
- parseindex = 0;
- goto top;
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ err(1, "yylex: strdup");
+
+ return STRING;
}
switch (c) {
@@ -1060,7 +1082,7 @@ check_file_secrecy(int fd, const char *fname)
}
struct file *
-newfile(const char *name, int secret)
+pushfile(const char *name, int secret)
{
struct file *nfile;
@@ -1078,6 +1100,8 @@ newfile(const char *name, int secret)
nfile->stream = fopen(nfile->name, "r");
if (nfile->stream == NULL) {
/* no warning, we don't require a conf file */
+ if (topfile != NULL)
+ log_warn("can't open %s", nfile->name);
free(nfile->name);
free(nfile);
return (NULL);
@@ -1089,15 +1113,34 @@ newfile(const char *name, int secret)
return (NULL);
}
nfile->lineno = 1;
+ nfile->ungetsize = 16;
+ nfile->ungetbuf = calloc(1, nfile->ungetsize);
+ if (nfile->ungetbuf == NULL) {
+ log_warn("calloc");
+ fclose(nfile->stream);
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ }
+ TAILQ_INSERT_TAIL(&files, nfile, entry);
return (nfile);
}
-static void
-closefile(struct file *xfile)
+static int
+popfile(void)
{
- fclose(xfile->stream);
- free(xfile->name);
- free(xfile);
+ struct file *prev;
+
+ if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
+ prev->errors += file->errors;
+
+ TAILQ_REMOVE(&files, file, entry);
+ fclose(file->stream);
+ free(file->name);
+ free(file->ungetbuf);
+ free(file);
+ file = prev;
+ return file ? 0 : EOF;
}
static void
@@ -1119,12 +1162,13 @@ parse_config(const char *filename, struct gotwebd *env
gotwebd = env;
- file = newfile(filename, 0);
+ file = pushfile(filename, 0);
if (file != NULL) {
/* we don't require a config file */
+ topfile = file;
yyparse();
errors = file->errors;
- closefile(file);
+ popfile();
}
/* Free macros and check which have not been used. */
gotwebd: convert newfile/closefile() to push/popfile()