The current sed(1) will not process a given line until it has read a
character of the following line (to detect EOF conditions).  This means
that the following command

  (echo foo; sleep 2; echo bar; sleep 2; echo baz; sleep 2; echo quux) | sed

Will wait two seconds before printing anything, and baz and quux will
print at the same time.  The -u option does not change this behaviour.

While this isn't a mission-critical issue, it can be inconvenient when
using sed(1) to process interactive/slow network streams.  None of the
other sed(1) implementations I can find exhibit this behaviour.  I have
constructed a patch for the mf_fgets function whose logic is inspired by
NetBSDs implementation.  I have not merely copied the NetBSD mf_fget, as
it has some funky logic regarding backup files that I do not
comprehend.

This is the first patch I have submitted to OpenBSD, so please tell me
if I have overlooked something.

Index: main.c
===================================================================
RCS file: /cvs/src/usr.bin/sed/main.c,v
retrieving revision 1.18
diff -u -p -r1.18 main.c
--- main.c	26 Nov 2014 18:34:51 -0000	1.18
+++ main.c	17 Aug 2015 12:57:58 -0000
@@ -262,30 +262,41 @@ mf_fgets(SPACE *sp, enum e_spflag spflag
 	size_t len;
 	char *p;
 	int c;
+	static int firstfile = 1;
 
-	if (f == NULL)
-		/* Advance to first non-empty file */
-		for (;;) {
-			if (files == NULL) {
-				lastline = 1;
-				return (0);
-			}
-			if (files->fname == NULL) {
-				f = stdin;
-				fname = "stdin";
-			} else {
-				fname = files->fname;
-				if ((f = fopen(fname, "r")) == NULL)
-					err(FATAL, "%s: %s",
-					    fname, strerror(errno));
-			}
-			if ((c = getc(f)) != EOF) {
-				(void)ungetc(c, f);
-				break;
-			}
-			(void)fclose(f);
+	/* Advance to next (possibly first) non-empty file */
+	for (;;) {
+		if (f != NULL && (c = getc(f)) != EOF) {
+			(void)ungetc(c, f);
+			break;;
+		}
+		/* If we are here then either eof or no files are open yet */
+		if (f != NULL)
+			fclose(f);
+		if (firstfile == 0)
 			files = files->next;
+		else
+			firstfile = 0;
+		if (files == NULL) {
+			lastline = 1;
+			return (0);
+		}
+		if (files->fname == NULL) {
+			f = stdin;
+			fname = "stdin";
+		} else {
+			fname = files->fname;
+			if ((f = fopen(fname, "r")) == NULL)
+				err(FATAL, "%s: %s",
+				    fname, strerror(errno));
+		}
+		if ((c = getc(f)) != EOF) {
+			(void)ungetc(c, f);
+			break;
 		}
+		(void)fclose(f);
+		files = files->next;
+	}
 
 	if (lastline) {
 		sp->len = 0;
@@ -303,24 +314,6 @@ mf_fgets(SPACE *sp, enum e_spflag spflag
 	cspace(sp, p, len, spflag);
 
 	linenum++;
-	/* Advance to next non-empty file */
-	while ((c = getc(f)) == EOF) {
-		(void)fclose(f);
-		files = files->next;
-		if (files == NULL) {
-			lastline = 1;
-			return (1);
-		}
-		if (files->fname == NULL) {
-			f = stdin;
-			fname = "stdin";
-		} else {
-			fname = files->fname;
-			if ((f = fopen(fname, "r")) == NULL)
-				err(FATAL, "%s: %s", fname, strerror(errno));
-		}
-	}
-	(void)ungetc(c, f);
 	return (1);
 }
 
-- 
\  Troels
/\ Henriksen

Reply via email to