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
This commit is contained in:
Dag-Erling Smørgrav
2026-03-10 11:18:08 +01:00
parent df5b2cf2b3
commit cf74b63d61
2 changed files with 57 additions and 24 deletions
+4 -2
View File
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE. .\" SUCH DAMAGE.
.\" .\"
.Dd June 4, 2014 .Dd March 2, 2026
.Dt YES 1 .Dt YES 1
.Os .Os
.Sh NAME .Sh NAME
@@ -33,7 +33,7 @@
.Nd be repetitively affirmative .Nd be repetitively affirmative
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Ar expletive .Op Ar expletive ...
.Sh DESCRIPTION .Sh DESCRIPTION
The The
.Nm .Nm
@@ -42,6 +42,8 @@ utility outputs
or, by default, or, by default,
.Dq y , .Dq y ,
forever. forever.
If multiple arguments are given, they are concatenated into a single
space-separated string.
.Sh SEE ALSO .Sh SEE ALSO
.Xr jot 1 , .Xr jot 1 ,
.Xr seq 1 .Xr seq 1
+55 -24
View File
@@ -35,40 +35,71 @@
#include <string.h> #include <string.h>
#include <unistd.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 int
main(int argc, char **argv) main(int argc, char **argv)
{ {
char buf[8192]; static char buf[MAXBUF] = EXP;
char y[2] = { 'y', '\n' }; char *end = buf + sizeof(buf), *exp, *pos = buf + EXPLEN;
char * exp = y; size_t buflen, explen = EXPLEN;
size_t buflen = 0; ssize_t wlen = 0;
size_t explen = sizeof(y);
size_t more;
ssize_t ret;
if (caph_limit_stdio() < 0 || caph_enter() < 0) if (caph_limit_stdio() < 0 || caph_enter() < 0)
err(1, "capsicum"); err(1, "capsicum");
if (argc > 1) { argc -= 1;
exp = argv[1]; argv += 1;
explen = strlen(exp) + 1;
exp[explen - 1] = '\n'; /* 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++;
}
/* This should not be possible, but check anyway */
if (pos > end - 2)
pos = end - 2;
*pos++ = '\n';
explen = pos - buf;
} }
if (explen <= sizeof(buf)) { /*
while (buflen < sizeof(buf) - explen) { * Double until we're past OPTBUF, then reduce buflen to exactly
memcpy(buf + buflen, exp, explen); * OPTBUF. It doesn't matter if that's not a multiple of explen;
buflen += explen; * the modulo operation in the write loop will take care of that.
} */
exp = buf; for (buflen = explen; buflen < OPTBUF; pos += buflen, buflen += buflen)
explen = buflen; memcpy(pos, buf, buflen);
} if (explen < OPTBUF && buflen > OPTBUF)
buflen = OPTBUF;
more = explen;
while ((ret = write(STDOUT_FILENO, exp + (explen - more), more)) > 0)
if ((more -= ret) == 0)
more = explen;
/* 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"); err(1, "stdout");
/*NOTREACHED*/ /*NOTREACHED*/
} }