Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 10 Mar 2026 10:18:51 +0000
From:      Dag-Erling=?utf-8?Q? Sm=C3=B8rg?=rav <des@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: cf74b63d61b4 - main - yes: Completely overengineer
Message-ID:  <69aff00b.43817.51bdaf7b@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=cf74b63d61b49db848ecc20b87e7ee5f16671320

commit cf74b63d61b49db848ecc20b87e7ee5f16671320
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2026-03-10 10:18:08 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2026-03-10 10:18:08 +0000

    yes: Completely overengineer
    
    If we're going to overengineer this, we may as well go all the way.
    
    * If multiple arguments are given, concatenate them into a space-
      separated list like GNU coreutils does.
    
    * When duplicating the expletive, do so exponentially.
    
    * Most importantly, don't modify the memory that argv points to.
    
    MFC after:      1 week
    Sponsored by:   Klara, Inc.
    Reviewed by:    kevans, allanjude
    Differential Revision:  https://reviews.freebsd.org/D55617
---
 usr.bin/yes/yes.1 |  6 +++--
 usr.bin/yes/yes.c | 75 +++++++++++++++++++++++++++++++++++++++----------------
 2 files changed, 57 insertions(+), 24 deletions(-)

diff --git a/usr.bin/yes/yes.1 b/usr.bin/yes/yes.1
index 8ed8beab0d28..689031345dc3 100644
--- a/usr.bin/yes/yes.1
+++ b/usr.bin/yes/yes.1
@@ -25,7 +25,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd June 4, 2014
+.Dd March 2, 2026
 .Dt YES 1
 .Os
 .Sh NAME
@@ -33,7 +33,7 @@
 .Nd be repetitively affirmative
 .Sh SYNOPSIS
 .Nm
-.Op Ar expletive
+.Op Ar expletive ...
 .Sh DESCRIPTION
 The
 .Nm
@@ -42,6 +42,8 @@ utility outputs
 or, by default,
 .Dq y ,
 forever.
+If multiple arguments are given, they are concatenated into a single
+space-separated string.
 .Sh SEE ALSO
 .Xr jot 1 ,
 .Xr seq 1
diff --git a/usr.bin/yes/yes.c b/usr.bin/yes/yes.c
index d9e896b6ea27..53224603cf95 100644
--- a/usr.bin/yes/yes.c
+++ b/usr.bin/yes/yes.c
@@ -35,40 +35,71 @@
 #include <string.h>
 #include <unistd.h>
 
+/*
+ * Default expletive
+ */
+#define EXP	"y\n"
+#define EXPLEN	strlen(EXP)
+
+/*
+ * Optimum and maximum buffer size.  The optimum is just a little less
+ * than the default value of kern.ipc.pipe_mindirect; writing more than
+ * that is significantly slower, but we want to get as close as possible
+ * to minimize the number of system calls.  The maximum is enough for a
+ * maximal command line plus a newline and terminating NUL.
+ */
+#define OPTBUF	8190
+#define MAXBUF	(ARG_MAX + 2)
+
 int
 main(int argc, char **argv)
 {
-	char buf[8192];
-	char y[2] = { 'y', '\n' };
-	char * exp = y;
-	size_t buflen = 0;
-	size_t explen = sizeof(y);
-	size_t more;
-	ssize_t ret;
+	static char buf[MAXBUF] = EXP;
+	char *end = buf + sizeof(buf), *exp, *pos = buf + EXPLEN;
+	size_t buflen, explen = EXPLEN;
+	ssize_t wlen = 0;
 
 	if (caph_limit_stdio() < 0 || caph_enter() < 0)
 		err(1, "capsicum");
 
-	if (argc > 1) {
-		exp = argv[1];
-		explen = strlen(exp) + 1;
-		exp[explen - 1] = '\n';
-	}
+	argc -= 1;
+	argv += 1;
 
-	if (explen <= sizeof(buf)) {
-		while (buflen < sizeof(buf) - explen) {
-			memcpy(buf + buflen, exp, explen);
-			buflen += explen;
+	/* Assemble the expletive */
+	if (argc > 0) {
+		/* Copy positional arguments into expletive buffer */
+		for (pos = buf, end = buf + sizeof(buf);
+		     argc > 0 && pos < end; argc--, argv++) {
+			/* Separate with spaces */
+			if (pos > buf)
+				*pos++ = ' ';
+			exp = *argv;
+			while (*exp != '\0' && pos < end)
+				*pos++ = *exp++;
 		}
-		exp = buf;
-		explen = buflen;
+		/* This should not be possible, but check anyway */
+		if (pos > end - 2)
+			pos = end - 2;
+		*pos++ = '\n';
+		explen = pos - buf;
 	}
 
-	more = explen;
-	while ((ret = write(STDOUT_FILENO, exp + (explen - more), more)) > 0)
-		if ((more -= ret) == 0)
-			more = explen;
+	/*
+	 * Double until we're past OPTBUF, then reduce buflen to exactly
+	 * OPTBUF.  It doesn't matter if that's not a multiple of explen;
+	 * the modulo operation in the write loop will take care of that.
+	 */
+	for (buflen = explen; buflen < OPTBUF; pos += buflen, buflen += buflen)
+		memcpy(pos, buf, buflen);
+	if (explen < OPTBUF && buflen > OPTBUF)
+		buflen = OPTBUF;
 
+	/* Dump it to stdout */
+	end = (pos = buf) + buflen;
+	do {
+		pos = buf + (pos - buf + wlen) % explen;
+		wlen = write(STDOUT_FILENO, pos, end - pos);
+	} while (wlen > 0);
 	err(1, "stdout");
 	/*NOTREACHED*/
 }


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?69aff00b.43817.51bdaf7b>