diff options
Diffstat (limited to 'bin/ls')
-rw-r--r-- | bin/ls/Makefile | 4 | ||||
-rw-r--r-- | bin/ls/Makefile.depend | 1 | ||||
-rw-r--r-- | bin/ls/ls.1 | 10 | ||||
-rw-r--r-- | bin/ls/ls.c | 11 | ||||
-rw-r--r-- | bin/ls/ls.h | 3 | ||||
-rw-r--r-- | bin/ls/print.c | 56 | ||||
-rw-r--r-- | bin/ls/tests/Makefile | 3 | ||||
-rwxr-xr-x | bin/ls/tests/ls_tests.sh | 256 |
8 files changed, 318 insertions, 26 deletions
diff --git a/bin/ls/Makefile b/bin/ls/Makefile index 84df0ef..e57bf35 100644 --- a/bin/ls/Makefile +++ b/bin/ls/Makefile @@ -5,12 +5,12 @@ PROG= ls SRCS= cmp.c ls.c print.c util.c -LIBADD= util +LIBADD= xo util .if !defined(RELEASE_CRUNCH) && \ ${MK_LS_COLORS} != no CFLAGS+= -DCOLORLS -LIBADD+= termcapw xo +LIBADD+= termcapw .endif .if ${MK_TESTS} != "no" diff --git a/bin/ls/Makefile.depend b/bin/ls/Makefile.depend index e36f602..6151c71 100644 --- a/bin/ls/Makefile.depend +++ b/bin/ls/Makefile.depend @@ -10,6 +10,7 @@ DIRDEPS = \ lib/libc \ lib/libcompiler_rt \ lib/libutil \ + lib/libxo \ lib/ncurses/ncursesw \ diff --git a/bin/ls/ls.1 b/bin/ls/ls.1 index 3e02229..6bca9aa 100644 --- a/bin/ls/ls.1 +++ b/bin/ls/ls.1 @@ -32,7 +32,7 @@ .\" @(#)ls.1 8.7 (Berkeley) 7/29/94 .\" $FreeBSD$ .\" -.Dd June 8, 2015 +.Dd September 27, 2015 .Dt LS 1 .Os .Sh NAME @@ -296,9 +296,9 @@ subsection below, except (if the long format is not also requested) the directory totals are not output when the output is in a single column, even if multi-column output is requested. .It Fl t -Sort by descending time modified (most recently modified first). If two files -have the same modification timestamp, sort their names in ascending -lexicographical order. +Sort by descending time modified (most recently modified first). +If two files have the same modification timestamp, sort their names +in ascending lexicographical order. The .Fl r option reverses both of these sort orders. @@ -320,7 +320,7 @@ or use the option. This causes .Nm -to reverse the lexicographal sort order when sorting files with the +to reverse the lexicographical sort order when sorting files with the same modification timestamp. .It Fl u Use time of last access, diff --git a/bin/ls/ls.c b/bin/ls/ls.c index 926d2bc..91ef9ea 100644 --- a/bin/ls/ls.c +++ b/bin/ls/ls.c @@ -119,7 +119,7 @@ static int f_nofollow; /* don't follow symbolic link arguments */ int f_nonprint; /* show unprintables as ? */ static int f_nosort; /* don't sort output */ int f_notabs; /* don't use tab-separated multi-col output */ -static int f_numericonly; /* don't convert uid/gid to name */ + int f_numericonly; /* don't convert uid/gid to name */ int f_octal; /* show unprintables as \xxx */ int f_octal_escape; /* like f_octal but use C escapes if possible */ static int f_recursive; /* ls subdirectories also */ @@ -158,6 +158,7 @@ main(int argc, char *argv[]) struct winsize win; int ch, fts_options, notused; char *p; + const char *errstr = NULL; #ifdef COLORLS char termcapbuf[1024]; /* termcap definition buffer */ char tcapbuf[512]; /* capability buffer */ @@ -170,7 +171,7 @@ main(int argc, char *argv[]) if (isatty(STDOUT_FILENO)) { termwidth = 80; if ((p = getenv("COLUMNS")) != NULL && *p != '\0') - termwidth = atoi(p); + termwidth = strtonum(p, 0, INT_MAX, &errstr); else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 && win.ws_col > 0) termwidth = win.ws_col; @@ -180,9 +181,12 @@ main(int argc, char *argv[]) /* retrieve environment variable, in case of explicit -C */ p = getenv("COLUMNS"); if (p) - termwidth = atoi(p); + termwidth = strtonum(p, 0, INT_MAX, &errstr); } + if (errstr) + termwidth = 80; + fts_options = FTS_PHYSICAL; if (getenv("LS_SAMESORT")) f_samesort = 1; @@ -191,6 +195,7 @@ main(int argc, char *argv[]) if (argc < 0) return (1); xo_set_flags(NULL, XOF_COLUMNS); + xo_set_version(LS_XO_VERSION); while ((ch = getopt(argc, argv, "1ABCD:FGHILPRSTUWXZabcdfghiklmnopqrstuwxy,")) != -1) { diff --git a/bin/ls/ls.h b/bin/ls/ls.h index 1a45eb4..e486777 100644 --- a/bin/ls/ls.h +++ b/bin/ls/ls.h @@ -37,6 +37,8 @@ #define HUMANVALSTR_LEN 5 +#define LS_XO_VERSION "1" + extern long blocksize; /* block size units */ extern int f_accesstime; /* use time of last access */ @@ -58,6 +60,7 @@ extern int f_statustime; /* use time of last mode change */ extern int f_thousands; /* show file sizes with thousands separators */ extern char *f_timeformat; /* user-specified time format */ extern int f_notabs; /* don't use tab-separated multi-col output */ +extern int f_numericonly; /* don't convert uid/gid to name */ extern int f_type; /* add type character for non-regular files */ #ifdef COLORLS extern int f_color; /* add type in color for non-regular files */ diff --git a/bin/ls/print.c b/bin/ls/print.c index c96fe7c..70f53ba 100644 --- a/bin/ls/print.c +++ b/bin/ls/print.c @@ -171,31 +171,55 @@ printlong(const DISPLAY *dp) xo_open_list("entry"); for (p = dp->list; p; p = p->fts_link) { - char *name; + char *name, *type; if (IS_NOPRINT(p)) continue; xo_open_instance("entry"); sp = p->fts_statp; name = getname(p->fts_name); if (name) - xo_emit("{ke:name}", name); + xo_emit("{ke:name/%hs}", name); if (f_inode) - xo_emit("{:inode/%*ju} ", + xo_emit("{t:inode/%*ju} ", dp->s_inode, (uintmax_t)sp->st_ino); if (f_size) - xo_emit("{:blocks/%*jd} ", + xo_emit("{t:blocks/%*jd} ", dp->s_block, howmany(sp->st_blocks, blocksize)); strmode(sp->st_mode, buf); aclmode(buf, p); np = p->fts_pointer; xo_attr("value", "%03o", (int) sp->st_mode & ALLPERMS); - xo_emit("{t:mode/%s} {:links/%*u} {:user/%-*s} {:group/%-*s} ", - buf, dp->s_nlink, sp->st_nlink, - dp->s_user, np->user, dp->s_group, np->group); + if (f_numericonly) { + xo_emit("{t:mode/%s}{e:mode_octal/%03o} {t:links/%*u} {td:user/%-*s}{e:user/%ju} {td:group/%-*s}{e:group/%ju} ", + buf, (int) sp->st_mode & ALLPERMS, dp->s_nlink, sp->st_nlink, + dp->s_user, np->user, (uintmax_t)sp->st_uid, dp->s_group, np->group, (uintmax_t)sp->st_gid); + } else { + xo_emit("{t:mode/%s}{e:mode_octal/%03o} {t:links/%*u} {t:user/%-*s} {t:group/%-*s} ", + buf, (int) sp->st_mode & ALLPERMS, dp->s_nlink, sp->st_nlink, + dp->s_user, np->user, dp->s_group, np->group); + } + if (S_ISBLK(sp->st_mode)) + asprintf(&type, "block"); + if (S_ISCHR(sp->st_mode)) + asprintf(&type, "character"); + if (S_ISDIR(sp->st_mode)) + asprintf(&type, "directory"); + if (S_ISFIFO(sp->st_mode)) + asprintf(&type, "fifo"); + if (S_ISLNK(sp->st_mode)) + asprintf(&type, "symlink"); + if (S_ISREG(sp->st_mode)) + asprintf(&type, "regular"); + if (S_ISSOCK(sp->st_mode)) + asprintf(&type, "socket"); + if (S_ISWHT(sp->st_mode)) + asprintf(&type, "whiteout"); + xo_emit("{e:type/%s}", type); + free(type); if (f_flags) xo_emit("{:flags/%-*s} ", dp->s_flags, np->flags); if (f_label) - xo_emit("{:label/%-*s} ", dp->s_label, np->label); + xo_emit("{t:label/%-*s} ", dp->s_label, np->label); if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) printdev(dp->s_size, sp->st_rdev); else @@ -214,7 +238,7 @@ printlong(const DISPLAY *dp) #endif if (name) { - xo_emit("{dk:name}", name); + xo_emit("{dk:name/%hs}", name); free(name); } @@ -238,6 +262,7 @@ printstream(const DISPLAY *dp) FTSENT *p; int chcnt; + xo_open_list("entry"); for (p = dp->list, chcnt = 0; p; p = p->fts_link) { if (p->fts_number == NO_PRINT) continue; @@ -247,12 +272,15 @@ printstream(const DISPLAY *dp) xo_emit("\n"); chcnt = 0; } + xo_open_instance("file"); chcnt += printaname(p, dp->s_inode, dp->s_block); + xo_close_instance("file"); if (p->fts_link) { xo_emit(", "); chcnt += 2; } } + xo_close_list("entry"); if (chcnt) xo_emit("\n"); } @@ -369,10 +397,10 @@ printaname(const FTSENT *p, u_long inodefield, u_long sizefield) sp = p->fts_statp; chcnt = 0; if (f_inode) - chcnt += xo_emit("{:inode/%*ju} ", + chcnt += xo_emit("{t:inode/%*ju} ", (int)inodefield, (uintmax_t)sp->st_ino); if (f_size) - chcnt += xo_emit("{:size/%*jd} ", + chcnt += xo_emit("{t:size/%*jd} ", (int)sizefield, howmany(sp->st_blocks, blocksize)); #ifdef COLORLS if (f_color) @@ -425,9 +453,11 @@ printtime(const char *field, time_t ftime) format = d_first ? "%e %b %Y" : "%b %e %Y"; strftime(longstring, sizeof(longstring), format, localtime(&ftime)); - snprintf(fmt, sizeof(fmt), "{:%s/%%s} ", field); + snprintf(fmt, sizeof(fmt), "{d:%s/%%hs} ", field); xo_attr("value", "%ld", (long) ftime); xo_emit(fmt, longstring); + snprintf(fmt, sizeof(fmt), "{en:%s/%%ld}", field); + xo_emit(fmt, (long) ftime); } static int @@ -456,7 +486,7 @@ printtype(u_int mode) xo_emit("{D:=}{e:type/socket}"); return (1); case S_IFWHT: - xo_emit("{D:%}{e:type/whiteout}"); + xo_emit("{D:%%}{e:type/whiteout}"); return (1); default: break; diff --git a/bin/ls/tests/Makefile b/bin/ls/tests/Makefile index 91a1650..38770e3 100644 --- a/bin/ls/tests/Makefile +++ b/bin/ls/tests/Makefile @@ -1,8 +1,11 @@ +# $FreeBSD$ + TESTSDIR= ${TESTSBASE}/bin/ls ATF_TESTS_SH+= ls_tests # This seems like overkill, but the idea in mind is that all of the testcases # should be runnable as !root TEST_METADATA.ls_tests+= required_user="unprivileged" +TEST_METADATA.ls_tests+= required_files="/usr/bin/nc" .include <bsd.test.mk> diff --git a/bin/ls/tests/ls_tests.sh b/bin/ls/tests/ls_tests.sh index 5e791b7..3d0d1e9 100755 --- a/bin/ls/tests/ls_tests.sh +++ b/bin/ls/tests/ls_tests.sh @@ -27,9 +27,11 @@ # $FreeBSD$ # -create_test_inputs() +create_test_dir() { - ATF_TMPDIR=$(pwd) + [ -z "$ATF_TMPDIR" ] || return 0 + + export ATF_TMPDIR=$(pwd) # XXX: need to nest this because of how kyua creates $TMPDIR; otherwise # it will run into EPERM issues later @@ -37,6 +39,12 @@ create_test_inputs() atf_check -e empty -s exit:0 mkdir -m 0777 -p $TEST_INPUTS_DIR cd $TEST_INPUTS_DIR +} + +create_test_inputs() +{ + create_test_dir + atf_check -e empty -s exit:0 mkdir -m 0755 -p a/b atf_check -e empty -s exit:0 ln -s a/b c atf_check -e empty -s exit:0 touch d @@ -44,16 +52,76 @@ create_test_inputs() atf_check -e empty -s exit:0 touch .f atf_check -e empty -s exit:0 mkdir .g atf_check -e empty -s exit:0 mkfifo h + atf_check -e ignore -s exit:0 dd if=/dev/zero of=i count=1000 bs=1 + atf_check -e empty -s exit:0 \ + sh -c "pid=${ATF_TMPDIR}/nc.pid; daemon -p \$pid nc -lU j; sleep 2; pkill -F \$pid" + atf_check -e empty -s exit:0 touch klmn + atf_check -e empty -s exit:0 touch opqr + atf_check -e empty -s exit:0 touch stuv + atf_check -e empty -s exit:0 touch wxyz + atf_check -e empty -s exit:0 touch 0b00000001 + atf_check -e empty -s exit:0 touch 0b00000010 + atf_check -e empty -s exit:0 touch 0b00000011 + atf_check -e empty -s exit:0 touch 0b00000100 + atf_check -e empty -s exit:0 touch 0b00000101 + atf_check -e empty -s exit:0 touch 0b00000110 + atf_check -e empty -s exit:0 touch 0b00000111 + atf_check -e empty -s exit:0 touch 0b00001000 + atf_check -e empty -s exit:0 touch 0b00001001 + atf_check -e empty -s exit:0 touch 0b00001010 + atf_check -e empty -s exit:0 touch 0b00001011 + atf_check -e empty -s exit:0 touch 0b00001100 + atf_check -e empty -s exit:0 touch 0b00001101 + atf_check -e empty -s exit:0 touch 0b00001110 + atf_check -e empty -s exit:0 touch 0b00001111 +} + +atf_test_case a_flag +a_flag_head() +{ + atf_set "descr" "Verify -a support" +} + +a_flag_body() +{ + create_test_dir + + # Make sure "." and ".." show up with -a + atf_check -e empty -o match:'\.[[:space:]]+\.\.' -s exit:0 ls -ax + + create_test_inputs + + WITH_a=$PWD/../with_a.out + WITHOUT_a=$PWD/../without_a.out + + atf_check -e empty -o save:$WITH_a -s exit:0 ls -A + atf_check -e empty -o save:$WITHOUT_a -s exit:0 ls + + echo "-A usage" + cat $WITH_a + echo "No -A usage" + cat $WITHOUT_a + + for dot_path in '\.f' '\.g'; do + atf_check -e empty -o not-empty -s exit:0 grep "${dot_path}" \ + $WITH_a + atf_check -e empty -o empty -s not-exit:0 grep "${dot_path}" \ + $WITHOUT_a + done } atf_test_case A_flag A_flag_head() { - atf_set "require.user" "unprivileged" + atf_set "descr" "Verify -A support with unprivileged users" } A_flag_body() { + create_test_dir + + atf_check -e empty -o empty -s exit:0 ls -A + create_test_inputs WITH_A=$PWD/../with_A.out @@ -84,6 +152,10 @@ A_flag_implied_when_root_head() A_flag_implied_when_root_body() { + create_test_dir + + atf_check -e empty -o empty -s exit:0 ls -A + create_test_inputs WITH_EXPLICIT=$PWD/../with_explicit_A.out @@ -100,9 +172,187 @@ A_flag_implied_when_root_body() atf_check_equal "$(cat $WITH_EXPLICIT)" "$(cat $WITH_IMPLIED)" } +atf_test_case B_flag +B_flag_head() +{ + atf_set "descr" "Verify that the output from ls -B prints out non-printable characters" +} + +B_flag_body() +{ + + atf_check -e empty -o empty -s exit:0 touch "$(printf "y\013z")" + atf_check -e empty -o match:'y\\013z' -s exit:0 ls -B +} + +atf_test_case C_flag +C_flag_head() +{ + atf_set "descr" "Verify that the output from ls -C is multi-column, sorted down" +} + +C_flag_body() +{ + create_test_inputs + + WITH_C=$PWD/../with_C.out + + atf_check -e empty -o save:$WITH_C -s exit:0 ls -C + + echo "With -C usage" + cat $WITH_C + + atf_check -e ignore -o not-empty -s exit:0 \ + egrep "0b00000001[[:space:]]+0b00000111[[:space:]]+0b00001101[[:space:]]+e[[:space:]]+stuv" $WITH_C + atf_check -e ignore -o not-empty -s exit:0 \ + egrep "0b00000010[[:space:]]+0b00001000[[:space:]]+0b00001110[[:space:]]+h[[:space:]]+wxyz" $WITH_C +} + +atf_test_case I_flag +I_flag_head() +{ + atf_set "descr" "Verify that the output from ls -I is the same as ls for an unprivileged user" +} + +I_flag_body() +{ + create_test_inputs + + WITH_I=$PWD/../with_I.out + WITHOUT_I=$PWD/../without_I.out + + atf_check -e empty -o save:$WITH_I -s exit:0 ls -I + atf_check -e empty -o save:$WITHOUT_I -s exit:0 ls + + echo "Explicit -I usage" + cat $WITH_I + echo "No -I usage" + cat $WITHOUT_I + + atf_check_equal "$(cat $WITH_I)" "$(cat $WITHOUT_I)" +} + +atf_test_case I_flag_voids_implied_A_flag_when_root +I_flag_voids_implied_A_flag_when_root_head() +{ + atf_set "descr" "Verify that -I voids out implied -A for root" + atf_set "require.user" "root" +} + +I_flag_voids_implied_A_flag_when_root_body() +{ + create_test_inputs + + atf_check -o not-match:'\.f' -s exit:0 ls -I + atf_check -o not-match:'\.g' -s exit:0 ls -I + + atf_check -o match:'\.f' -s exit:0 ls -A -I + atf_check -o match:'\.g' -s exit:0 ls -A -I +} + +lcomma_flag_head() +{ + atf_set "descr" "Verify that -l, prints out the size with , delimiters" +} + +lcomma_flag_body() +{ + create_test_inputs + + atf_check \ + -o match:'\-rw\-r\-\-r\-\-[[:space:]]+.+[[:space:]]+1,000[[:space:]]+.+i' \ + env LC_ALL=en_US.ISO8859-1 ls -l, i +} + +x_flag_head() +{ + atf_set "descr" "Verify that -x prints out one item per line" +} + +x_flag_body() +{ + create_test_inputs + + WITH_x=$PWD/../with_x.out + + atf_check -e empty -o save:$WITH_x -s exit:0 ls -x + + echo "With -x usage" + cat $WITH_x + + atf_check -e ignore -o not-empty -s exit:0 \ + egrep "a[[:space:]]+c[[:space:]]+d[[:space:]]+e[[:space:]]+h" $WITH_x + atf_check -e ignore -o not-empty -s exit:0 \ + egrep "i[[:space:]]+j[[:space:]]+klmn[[:space:]]+opqr[[:space:]]+stuv" $WITH_x +} + +1_flag_head() +{ + atf_set "descr" "Verify that -1 prints out one item per line" +} + +1_flag_body() +{ + create_test_inputs + + WITH_1=$PWD/../with_1.out + WITHOUT_1=$PWD/../without_1.out + + atf_check -e empty -o save:$WITH_1 -s exit:0 ls -1 + atf_check -e empty -o save:$WITHOUT_1 -s exit:0 \ + sh -c 'for i in $(ls); do echo $i; done' + + echo "Explicit -1 usage" + cat $WITH_1 + echo "No -1 usage" + cat $WITHOUT_1 + + atf_check_equal "$(cat $WITH_1)" "$(cat $WITHOUT_1)" +} + atf_init_test_cases() { atf_add_test_case A_flag atf_add_test_case A_flag_implied_when_root + atf_add_test_case B_flag + atf_add_test_case C_flag + #atf_add_test_case D_flag + #atf_add_test_case F_flag + #atf_add_test_case G_flag + #atf_add_test_case H_flag + atf_add_test_case I_flag + atf_add_test_case I_flag_voids_implied_A_flag_when_root + #atf_add_test_case L_flag + #atf_add_test_case P_flag + #atf_add_test_case R_flag + #atf_add_test_case S_flag + #atf_add_test_case T_flag + #atf_add_test_case U_flag + #atf_add_test_case W_flag + #atf_add_test_case Z_flag + atf_add_test_case a_flag + #atf_add_test_case b_flag + #atf_add_test_case c_flag + #atf_add_test_case d_flag + #atf_add_test_case f_flag + #atf_add_test_case g_flag + #atf_add_test_case h_flag + #atf_add_test_case i_flag + #atf_add_test_case k_flag + #atf_add_test_case l_flag + atf_add_test_case lcomma_flag + #atf_add_test_case m_flag + #atf_add_test_case n_flag + #atf_add_test_case o_flag + #atf_add_test_case p_flag + #atf_add_test_case q_flag + #atf_add_test_case r_flag + #atf_add_test_case s_flag + #atf_add_test_case t_flag + #atf_add_test_case u_flag + #atf_add_test_case w_flag + atf_add_test_case x_flag + #atf_add_test_case y_flag + atf_add_test_case 1_flag } |