"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
"Omar Polo" <op@omarpolo.com>
Subject:
gotwebd: convert newfile/closefile() to push/popfile()
To:
gameoftrees@openbsd.org
Date:
Sat, 08 Nov 2025 19:04:06 +0100

Download raw body.

Thread
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. */