- Use errx(3) instead of err(3) when checking if snprintf(3) succeeded.

snprintf(3) doesn't set errno in the tested cases.
- If the same argument reference (for example %1) was specified more than
  once, the command didn't necessarily fit to the final command buffer. Fix
  this using a dynamic sbuf buffer. Add a few regression tests for the case.

PR:		bin/95079
No objections:	freebsd-hackers
This commit is contained in:
Jaakko Heinonen 2010-03-05 15:23:01 +00:00
parent 333fb1c996
commit 12794df244
10 changed files with 74 additions and 36 deletions

View File

@ -1,6 +1,7 @@
# $FreeBSD$
SUBDIR= calendar comm file2c join jot m4 printf sed tr uudecode uuencode xargs
SUBDIR= apply calendar comm file2c join jot m4 printf sed tr \
uudecode uuencode xargs
.if !defined(AUTOMATED)
SUBDIR+= lastcomm
.endif

View File

@ -0,0 +1,4 @@
# $FreeBSD$
all:
@m4 ${.CURDIR}/../regress.m4 ${.CURDIR}/regress.sh | sh /dev/stdin ${.CURDIR}

View File

@ -0,0 +1 @@
12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012

View File

@ -0,0 +1 @@
12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012

View File

@ -0,0 +1 @@
apply: Argument list too long

View File

@ -0,0 +1,15 @@
#!/bin/sh
# $FreeBSD$
SHELL=/bin/sh; export SHELL
ARG_MAX=$(getconf ARG_MAX)
ARG_MAX_HALF=$((ARG_MAX / 2))
apply 'echo %1 %1 %1' $(jot $ARG_MAX_HALF 1 1 | tr -d '\n') 2>&1
if [ $? -eq 0 ]; then
return 1
else
return 0
fi

View File

@ -0,0 +1,10 @@
# $FreeBSD$
echo 1..2
REGRESSION_START($1)
REGRESSION_TEST(`00', `apply "echo %1 %1 %1 %1" $(cat regress.00.in)')
REGRESSION_TEST(`01', `sh regress.01.sh')
REGRESSION_END()

View File

@ -0,0 +1,6 @@
#!/bin/sh
# $FreeBSD$
cd `dirname $0`
m4 ../regress.m4 regress.sh | sh

View File

@ -2,5 +2,7 @@
# $FreeBSD$
PROG= apply
DPADD= ${LIBSBUF}
LDADD= -lsbuf
.include <bsd.prog.mk>

View File

@ -44,10 +44,12 @@ static char sccsid[] = "@(#)apply.c 8.4 (Berkeley) 4/4/94";
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/sbuf.h>
#include <sys/wait.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
@ -61,10 +63,13 @@ static int exec_shell(const char *, char *, char *);
static void usage(void);
int
main(int argc, char *argv[]) {
main(int argc, char *argv[])
{
struct sbuf *cmdbuf;
long arg_max;
int ch, debug, i, magic, n, nargs, offset, rval;
size_t clen, cmdsize, l;
char *c, *cmd, *name, *p, *q, *shell, *slashp, *tmpshell;
size_t cmdsize;
char *cmd, *name, *p, *shell, *slashp, *tmpshell;
debug = 0;
magic = '%'; /* Default magic char is `%'. */
@ -144,13 +149,13 @@ main(int argc, char *argv[]) {
p = cmd;
offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]);
if ((size_t)offset >= cmdsize)
err(1, "snprintf() failed");
errx(1, "snprintf() failed");
p += offset;
cmdsize -= offset;
for (i = 1; i <= nargs; i++) {
offset = snprintf(p, cmdsize, " %c%d", magic, i);
if ((size_t)offset >= cmdsize)
err(1, "snprintf() failed");
errx(1, "snprintf() failed");
p += offset;
cmdsize -= offset;
}
@ -164,61 +169,53 @@ main(int argc, char *argv[]) {
} else {
offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]);
if ((size_t)offset >= cmdsize)
err(1, "snprintf() failed");
errx(1, "snprintf() failed");
nargs = n;
}
/*
* Grab some space in which to build the command. Allocate
* as necessary later, but no reason to build it up slowly
* for the normal case.
*/
if ((c = malloc(clen = 1024)) == NULL)
cmdbuf = sbuf_new(NULL, NULL, 1024, SBUF_AUTOEXTEND);
if (cmdbuf == NULL)
err(1, NULL);
arg_max = sysconf(_SC_ARG_MAX);
/*
* (argc) and (argv) are still offset by one to make it simpler to
* expand %digit references. At the end of the loop check for (argc)
* equals 1 means that all the (argv) has been consumed.
*/
for (rval = 0; argc > nargs; argc -= nargs, argv += nargs) {
/*
* Find a max value for the command length, and ensure
* there's enough space to build it.
*/
for (l = strlen(cmd), i = 0; i < nargs; i++)
l += strlen(argv[i+1]);
if (l > clen && (c = realloc(c, clen = l)) == NULL)
err(1, NULL);
sbuf_clear(cmdbuf);
/* Expand command argv references. */
for (p = cmd, q = c; *p != '\0'; ++p)
for (p = cmd; *p != '\0'; ++p) {
if (p[0] == magic && isdigit(p[1]) && p[1] != '0') {
offset = snprintf(q, l, "%s",
argv[(++p)[0] - '0']);
if ((size_t)offset >= l)
err(1, "snprintf() failed");
q += offset;
l -= offset;
} else
*q++ = *p;
if (sbuf_cat(cmdbuf, argv[(++p)[0] - '0'])
== -1)
errc(1, ENOMEM, "sbuf");
} else {
if (sbuf_putc(cmdbuf, *p) == -1)
errc(1, ENOMEM, "sbuf");
}
if (sbuf_len(cmdbuf) > arg_max)
errc(1, E2BIG, NULL);
}
/* Terminate the command string. */
*q = '\0';
sbuf_finish(cmdbuf);
/* Run the command. */
if (debug)
(void)printf("%s\n", c);
(void)printf("%s\n", sbuf_data(cmdbuf));
else
if (exec_shell(c, shell, name))
if (exec_shell(sbuf_data(cmdbuf), shell, name))
rval = 1;
}
if (argc != 1)
errx(1, "expecting additional argument%s after \"%s\"",
(nargs - argc) ? "s" : "", argv[argc - 1]);
(nargs - argc) ? "s" : "", argv[argc - 1]);
free(cmd);
free(c);
sbuf_delete(cmdbuf);
free(shell);
exit(rval);
}