summaryrefslogtreecommitdiffstats
path: root/bin/ls
diff options
context:
space:
mode:
Diffstat (limited to 'bin/ls')
-rw-r--r--bin/ls/Makefile4
-rw-r--r--bin/ls/Makefile.depend1
-rw-r--r--bin/ls/ls.110
-rw-r--r--bin/ls/ls.c11
-rw-r--r--bin/ls/ls.h3
-rw-r--r--bin/ls/print.c56
-rw-r--r--bin/ls/tests/Makefile3
-rwxr-xr-xbin/ls/tests/ls_tests.sh256
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
}
OpenPOWER on IntegriCloud