git: cf74b63d61b4 - main - yes: Completely overengineer
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 10 Mar 2026 10:18:51 UTC
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*/
}