From a02e855f6fd788720ab057ae4df3e837836f7629 Mon Sep 17 00:00:00 2001 From: Cody Peter Mello Date: Wed, 21 Jun 2017 00:52:48 +0000 Subject: [PATCH] 8671 tail(1) ignores arguments after -c/b/l Reviewed by: Robert Mustacchi Approved by: Gordon Ross --- usr/src/cmd/tail/tail.c | 114 ++++++++++++++++--- usr/src/cmd/tail/tests/sun_solaris_tail.sh | 15 +++ usr/src/cmd/tail/tests/tailtests.sh | 174 ++++++++++++++++++++++++----- 3 files changed, 259 insertions(+), 44 deletions(-) mode change 100644 => 100755 usr/src/cmd/tail/tests/sun_solaris_tail.sh mode change 100644 => 100755 usr/src/cmd/tail/tests/tailtests.sh diff --git a/usr/src/cmd/tail/tail.c b/usr/src/cmd/tail/tail.c index c641cfa592..8e83ae4079 100644 --- a/usr/src/cmd/tail/tail.c +++ b/usr/src/cmd/tail/tail.c @@ -30,9 +30,14 @@ * SUCH DAMAGE. */ +/* + * Copyright 2017, Joyent, Inc. + */ + #include #include +#include #include #include #include @@ -226,6 +231,24 @@ main(int argc, char *argv[]) exit(rval); } +static boolean_t +iscount(const char *ap) +{ + char c; + + if (ap == NULL) { + return (B_FALSE); + } + + c = ap[0]; + + if (c == '+' || c == '-') { + c = ap[1]; + } + + return (isdigit(c) ? B_TRUE : B_FALSE); +} + /* * Convert the obsolete argument form into something that getopt can handle. * This means that anything of the form [+-][0-9][0-9]*[lbc][Ffr] that isn't @@ -293,28 +316,83 @@ obsolete(char *argv[]) /* * Legacy Solaris tail supports "+c" "-c", "+l", "-l", - * "+b", and "-b" with an default number of 10. Map - * these arguments to an explicit +/-10 for FreeBSD. - * New argument will be of the form -[bcn][+-]10 + * "+b", and "-b" with a default value of 10. We need + * to determine here whether or not a count has been + * provided after the flag, and create a new, explicit + * argument as appropriate. [+-]l isn't allowed to have + * any numbers after it, but [+-][bc] can, potentially + * in the next command-line argument. We therefore + * handle them in two separate cases below. */ + case 'l': + len = strlen(ap); + start = NULL; + + if (len > 2) { + errx(1, "illegal option -- %s", *argv); + } + + /* The only characters following should be flags */ + if (len == 2 && !isalpha(ap[1])) { + errx(1, "illegal option -- %s", *argv); + } + + if (asprintf(&start, "-%sn%c10", + ap + 1, *argv[0]) == -1) { + err(1, "asprintf"); + } + + *argv = start; + + continue; case 'b': case 'c': - case 'l': - if ((start = p = malloc(6)) == NULL) - err(1, "malloc"); - *p++ = '-'; - switch (ap[0]) { - case 'c': - *p++ = ap[0]; - break; - case 'b': - *p++ = ap[0]; - break; - case 'l': - *p++ = 'n'; - break; + len = strlen(ap); + start = NULL; + + if (len == 1) { + /* + * The option is just the flag name. Check if + * the next argument is a count, so we know + * whether we need to default to 10. + */ + if (iscount(argv[1])) { + ++argv; + continue; + } else { + if (asprintf(&start, + "-%c%c10", ap[0], *argv[0]) == -1) { + err(1, "asprintf"); + } + } + } else { + /* + * The option has characters following the c/b. + * If the characters following the option are a + * count, then we use those. This invocation is + * only allowed when '-' is used. + * + * Otherwise, we need to honor the following + * flags, and default to 10. + */ + if (iscount(ap + 1)) { + if (*argv[0] != '-') { + errx(1, "illegal option -- %s", + *argv); + } + + if (asprintf(&start, "-%c%s", + ap[0], ap + 1) == -1) { + err(1, "asprintf"); + } + } else { + if (asprintf(&start, "-%s%c%c10", + ap + 1, ap[0], *argv[0]) == -1) { + err(1, "asprintf"); + } + } } - (void) snprintf(p, 6, "%c10", *argv[0]); + *argv = start; continue; diff --git a/usr/src/cmd/tail/tests/sun_solaris_tail.sh b/usr/src/cmd/tail/tests/sun_solaris_tail.sh old mode 100644 new mode 100755 index 9f57c0044a..b78e487d26 --- a/usr/src/cmd/tail/tests/sun_solaris_tail.sh +++ b/usr/src/cmd/tail/tests/sun_solaris_tail.sh @@ -21,6 +21,7 @@ # # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2017, Joyent, Inc. # # @@ -30,6 +31,20 @@ # TAIL=/usr/bin/tail +while [[ $# -gt 0 ]]; do + case $1 in + -o) + TAIL=$(cd $(dirname $2); pwd)/$(basename $2) + shift 2 + ;; + *) + echo "Usage: sun_solaris_tail.sh" \ + "[-o ]" + exit 1 + ;; + esac +done + # test setup function err_exit { diff --git a/usr/src/cmd/tail/tests/tailtests.sh b/usr/src/cmd/tail/tests/tailtests.sh old mode 100644 new mode 100755 index d3a8bb1cf2..69ca0e3ad1 --- a/usr/src/cmd/tail/tests/tailtests.sh +++ b/usr/src/cmd/tail/tests/tailtests.sh @@ -13,9 +13,15 @@ # # Copyright 2010 Chris Love. All rights reserved. -# Copyright (c) 2013, Joyent, Inc. All rights reserved. +# Copyright 2017, Joyent, Inc. # +BLOCK="" +for i in {1..512}; do + BLOCK+="." +done + + checktest() { local actual=$1 @@ -31,6 +37,17 @@ checktest() fi } +checkfail() +{ + printf "foobar" | $PROG $* &> /dev/null + + if [[ $? -eq 0 ]]; then + printf '%s: test "test %s": was supposed to fail\n' "$CMD" "$*" + else + printf '%s: test "%s": pass\n' "$CMD" "$*" + fi +} + # # Test cases for 'tail', some based on CoreUtils test cases (validated # with legacy Solaris 'tail' and/or xpg4 'tail'). Note that this is designed @@ -137,18 +154,34 @@ o=`echo -e "y\n"` a=`echo -e "x\ny\n" | $PROG +2` checktest "$a" "$o" 15 -o=`echo -e "yyz"` -a=`echo -e "xyyyyyyyyyyz" | $PROG +10c` +o=`printf "yyz\n"` +a=`printf "xyyyyyyyyyyz\n" | $PROG +10c` checktest "$a" "$o" 16 -o=`echo -e "y\ny\nz"` -a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG +10l` +o=`printf "y\ny\nz\n"` +a=`printf "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz\n" | $PROG +10l` checktest "$a" "$o" 17 -o=`echo -e "y\ny\ny\ny\ny\ny\ny\ny\ny\nz"` -a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG -10l` +o=`printf "y\ny\ny\ny\ny\ny\ny\ny\ny\nz\n"` +a=`printf "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz\n" | $PROG -10l` checktest "$a" "$o" 18 +a=`printf "o\nn\nm\nl\nk\nj\ni\nh\ng\n"` +o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG +10lr` +checktest "$a" "$o" 19 + +a=`printf "o\nn\nm\nl\nk\nj\ni\nh\ng\nf\n"` +o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG -10lr` +checktest "$a" "$o" 20 + +a=`printf "o\nn\nm\nl\n"` +o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG +10cr` +checktest "$a" "$o" 21 + +a=`printf "o\nn\nm\nl\nk\n"` +o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG -10cr` +checktest "$a" "$o" 22 + # # For reasons that are presumably as accidental as they are ancient, legacy # (and closed) Solaris tail(1) allows +c, +l and -l to be aliases for +10c, @@ -156,22 +189,54 @@ checktest "$a" "$o" 18 # behavior is functional. # if [[ `uname -s` == "SunOS" ]]; then - o=`echo -e "yyz"` - a=`echo -e "xyyyyyyyyyyz" | $PROG +c` + o=`printf "yyz\n"` + a=`printf "xyyyyyyyyyyz\n" | $PROG +c` checktest "$a" "$o" 16a - o=`echo -e "y\ny\nz"` - a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG +l` + o=`printf "y\ny\nz\n"` + a=`printf "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz\n" | $PROG +l` checktest "$a" "$o" 17a - o=`echo -e "y\ny\ny\ny\ny\ny\ny\ny\ny\nz"` - a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG -l` + o=`printf "y\ny\ny\ny\ny\ny\ny\ny\ny\nz\n"` + a=`printf "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz\n" | $PROG -l` checktest "$a" "$o" 18a + + a=`printf "o\nn\nm\nl\nk\nj\ni\nh\ng\n"` + + o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG +lr` + checktest "$a" "$o" 19a + + o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG +l -r` + checktest "$a" "$o" 19a + + a=`printf "o\nn\nm\nl\nk\nj\ni\nh\ng\nf\n"` + + o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG -lr` + checktest "$a" "$o" 20a + + o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG -l -r` + checktest "$a" "$o" 20b + + a=`printf "o\nn\nm\nl\n"` + + o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG +cr` + checktest "$a" "$o" 21a + + o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG +c -r` + checktest "$a" "$o" 21a + + a=`printf "o\nn\nm\nl\nk\n"` + + o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG -cr` + checktest "$a" "$o" 22a + + o=`printf "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\n" | $PROG -c -r` + checktest "$a" "$o" 22b fi o=`echo -e "c\nb\na"` a=`echo -e "a\nb\nc" | $PROG -r` -checktest "$a" "$o" 19 +checktest "$a" "$o" 23 # # Now we want to do a series of follow tests. @@ -201,7 +266,7 @@ sleep 1 o=`echo -e "a\nb\nc\nd\ne\nf\n"` a=`cat $out` -checktest "$a" "$o" 20 +checktest "$a" "$o" 24 rm $follow # @@ -220,7 +285,7 @@ sleep 1 o=`echo -e "a\nb\nc\nd\ne\nf\n"` a=`cat $out` -checktest "$a" "$o" 21 +checktest "$a" "$o" 25 rm $moved # @@ -239,7 +304,7 @@ sleep 1 o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"` a=`cat $out` -checktest "$a" "$o" 22 +checktest "$a" "$o" 26 rm $follow # @@ -259,7 +324,7 @@ sleep 1 o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\no\np\nq"` a=`cat $out` -checktest "$a" "$o" 23 +checktest "$a" "$o" 27 rm $follow # @@ -280,7 +345,7 @@ sleep 1 o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\no\np\nq"` a=`cat $out` -checktest "$a" "$o" 24 +checktest "$a" "$o" 28 rm $moved # @@ -299,7 +364,7 @@ sleep 1 o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"` a=`cat $out` -checktest "$a" "$o" 25 +checktest "$a" "$o" 29 rm $follow # @@ -326,7 +391,7 @@ sleep 1 o=`echo -e "a\nb\nc\nd\ne\nf\n"` a=`cat $out` -checktest "$a" "$o" 26 +checktest "$a" "$o" 30 rm $moved # @@ -365,7 +430,7 @@ four ==> $follow <== five" a=`cat $out` -checktest "$a" "$o" 27 +checktest "$a" "$o" 31 rm $follow $moved if [[ `uname -s` == "SunOS" ]]; then @@ -401,7 +466,7 @@ EOF o=`echo -e "0\n1\n2\n3\n5\n6\n7\n8\n9\n"` a=`cat $out` - checktest "$a" "$o" 27a + checktest "$a" "$o" 31a rm $follow cat /dev/null > $follow @@ -433,7 +498,7 @@ EOF o=`echo -e "0\n1\n2\n3\n5\n6\n7\n8\n9\n"` a=`cat $out` - checktest "$a" "$o" 27b + checktest "$a" "$o" 31b rm $moved # @@ -453,7 +518,7 @@ EOF o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"` a=`cat $out` - checktest "$a" "$o" 27c + checktest "$a" "$o" 31c fi # @@ -472,7 +537,64 @@ kill $child a=`sort $out | uniq -c | sort -n | tail -1 | awk '{ print $1 }'` o=1 -checktest "$a" "$o" 28 +checktest "$a" "$o" 32 + +# Test different ways of specifying character offsets +o=`printf "d\n"` + +a=`printf "hello\nworld\n" | $PROG -c2` +checktest "$a" "$o" 33 + +a=`printf "hello\nworld\n" | $PROG -c-2` +checktest "$a" "$o" 34 + +a=`printf "hello\nworld\n" | $PROG -c 2` +checktest "$a" "$o" 35 + +a=`printf "hello\nworld\n" | $PROG -c -2` +checktest "$a" "$o" 36 + +a=`printf "hello\nworld\n" | $PROG -2c` +checktest "$a" "$o" 37 + +o=`printf "llo\nworld\n"` + +a=`printf "hello\nworld\n" | $PROG -c +3` +checktest "$a" "$o" 38 + +a=`printf "hello\nworld\n" | $PROG -c+3` +checktest "$a" "$o" 39 + +a=`printf "hello\nworld\n" | $PROG +3c` +checktest "$a" "$o" 40 + +# Test various ways of specifying block offsets +o=`printf "$BLOCK"` + +a=`printf "${BLOCK//./x}$BLOCK" | $PROG -b1` +checktest "$a" "$o" 41 + +a=`printf "${BLOCK//./x}$BLOCK" | $PROG -b 1` +checktest "$a" "$o" 42 + +a=`printf "${BLOCK//./x}$BLOCK" | $PROG -b -1` +checktest "$a" "$o" 43 + +a=`printf "${BLOCK//./x}$BLOCK" | $PROG -b +2` +checktest "$a" "$o" 44 + +# Test that illegal arguments aren't allowed + +checkfail +b2 +checkfail +c3 +checkfail -l3 +checkfail -cz +checkfail -bz +checkfail -nz +checkfail -3n +checkfail +3n +checkfail +n3 +checkfail -lfoobar echo "$CMD: completed" -- 2.11.4.GIT