aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2012-06-09 22:25:49 -0500
committerRob Landley <rob@landley.net>2012-06-09 22:25:49 -0500
commit4af1e1d784fa757e1d50b3fa42bd27f0a9b7782c (patch)
treee6e247c6b617c1eb2d739c4a1baa8d49e8e9903a
parent98356226c81109227faa89b721674e5fabf04b42 (diff)
downloadtoybox-4af1e1d784fa757e1d50b3fa42bd27f0a9b7782c.tar.gz
Implement DIRTREE_SYMFOLLOW and ls -cSHL.
-rw-r--r--lib/dirtree.c14
-rw-r--r--lib/lib.h4
-rw-r--r--toys/ls.c100
3 files changed, 70 insertions, 48 deletions
diff --git a/lib/dirtree.c b/lib/dirtree.c
index 291f99a4..ff274275 100644
--- a/lib/dirtree.c
+++ b/lib/dirtree.c
@@ -10,7 +10,7 @@
// (This doesn't open directory filehandles yet so as not to exhaust the
// filehandle space on large trees. handle_callback() does that instead.)
-struct dirtree *dirtree_add_node(int dirfd, char *name)
+struct dirtree *dirtree_add_node(int dirfd, char *name, int symfollow)
{
struct dirtree *dt = NULL;
struct stat st;
@@ -18,7 +18,8 @@ struct dirtree *dirtree_add_node(int dirfd, char *name)
int len = 0, linklen = 0;
if (name) {
- if (fstatat(dirfd, name, &st, AT_SYMLINK_NOFOLLOW)) goto error;
+ if (fstatat(dirfd, name, &st, symfollow ? 0 : AT_SYMLINK_NOFOLLOW))
+ goto error;
if (S_ISLNK(st.st_mode)) {
if (0>(linklen = readlinkat(dirfd, name, buf, 4095))) goto error;
buf[linklen++]=0;
@@ -120,7 +121,7 @@ struct dirtree *handle_callback(struct dirtree *new,
if (dir) {
if (flags & (DIRTREE_RECURSE|DIRTREE_COMEAGAIN)) {
- dirtree_recurse(new, callback);
+ dirtree_recurse(new, callback, flags & DIRTREE_SYMFOLLOW);
if (flags & DIRTREE_COMEAGAIN) flags = callback(new);
} else close(new->data);
}
@@ -138,7 +139,7 @@ struct dirtree *handle_callback(struct dirtree *new,
// filtering through callback().
void dirtree_recurse(struct dirtree *node,
- int (*callback)(struct dirtree *node))
+ int (*callback)(struct dirtree *node), int symfollow)
{
struct dirtree *new, **ddt = &(node->child);
struct dirent *entry;
@@ -158,7 +159,8 @@ void dirtree_recurse(struct dirtree *node,
// The extra parentheses are to shut the stupid compiler up.
while ((entry = readdir(dir))) {
- if (!(new = dirtree_add_node(node->data, entry->d_name))) continue;
+ if (!(new = dirtree_add_node(node->data, entry->d_name, symfollow)))
+ continue;
new->parent = node;
new = handle_callback(new, callback);
if (new == DIRTREE_ABORTVAL) break;
@@ -179,7 +181,7 @@ void dirtree_recurse(struct dirtree *node,
struct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node))
{
- struct dirtree *root = dirtree_add_node(AT_FDCWD, path);
+ struct dirtree *root = dirtree_add_node(AT_FDCWD, path, 0);
return root ? handle_callback(root, callback) : DIRTREE_ABORTVAL;
}
diff --git a/lib/lib.h b/lib/lib.h
index add0c14e..c3aa15ce 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -71,14 +71,14 @@ struct dirtree {
char name[];
};
-struct dirtree *dirtree_add_node(int dirfd, char *name);
+struct dirtree *dirtree_add_node(int dirfd, char *name, int symfollow);
char *dirtree_path(struct dirtree *node, int *plen);
int dirtree_notdotdot(struct dirtree *catch);
int dirtree_opennode(struct dirtree *try);
struct dirtree *handle_callback(struct dirtree *new,
int (*callback)(struct dirtree *node));
void dirtree_recurse(struct dirtree *node,
- int (*callback)(struct dirtree *node));
+ int (*callback)(struct dirtree *node), int symfollow);
struct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node));
// lib.c
diff --git a/toys/ls.c b/toys/ls.c
index 1ddbd08b..6c770a6b 100644
--- a/toys/ls.c
+++ b/toys/ls.c
@@ -8,7 +8,7 @@
* See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
// "[-Cl]"
-USE_LS(NEWTOY(ls, "oACFHLRSacdfiklmnpqrstux1", TOYFLAG_BIN))
+USE_LS(NEWTOY(ls, "goACFHLRSacdfiklmnpqrstux1", TOYFLAG_BIN))
config LS
bool "ls"
@@ -19,6 +19,7 @@ config LS
what to show:
-a all files including .hidden
+ -c use ctime for timestamps
-d directory, not contents
-i inode number
-k block sizes in kilobytes
@@ -27,22 +28,26 @@ config LS
-s size (in blocks)
-u use access time for timestamps
-A list all files except . and ..
+ -H follow command line symlinks
+ -L follow symlinks
-R recursively list files in subdirectories
-F append file type indicator (/=dir, *=exe, @=symlink, |=FIFO)
output formats:
-1 list one file per line
-C columns (sorted vertically)
- -x columns (sorted horizontally)
+ -g like -l but no owner
-l long (show full details for each file)
-m comma separated
-n like -l but numeric uid/gid
-o like -l but no group
+ -x columns (sorted horizontally)
- sorting:
+ sorting (default is alphabetical):
-f unsorted
-r reverse
- -t by timestamp
+ -t timestamp
+ -S size
*/
#include "toys.h"
@@ -62,16 +67,17 @@ config LS
#define FLAG_i (1<<12)
#define FLAG_f (1<<13)
#define FLAG_d (1<<14)
-//#define FLAG_c (1<<15)
+#define FLAG_c (1<<15)
#define FLAG_a (1<<16)
-//#define FLAG_S (1<<17)
+#define FLAG_S (1<<17)
#define FLAG_R (1<<18)
-//#define FLAG_L (1<<19)
-//#define FLAG_H (1<<20)
+#define FLAG_L (1<<19)
+#define FLAG_H (1<<20)
#define FLAG_F (1<<21)
#define FLAG_C (1<<22)
#define FLAG_A (1<<23)
#define FLAG_o (1<<24)
+#define FLAG_g (1<<25)
// test sst output (suid/sticky in ls flaglist)
@@ -136,7 +142,7 @@ static void entrylen(struct dirtree *dt, unsigned *len)
if (flags & FLAG_m) ++*len;
if (flags & FLAG_i) *len += (len[1] = numlen(st->st_ino));
- if (flags & (FLAG_l|FLAG_o|FLAG_n)) {
+ if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) {
unsigned fn = flags & FLAG_n;
len[2] = numlen(st->st_nlink);
len[3] = strlen(fn ? utoa(st->st_uid) : getusername(st->st_uid));
@@ -152,6 +158,10 @@ static int compare(void *a, void *b)
struct dirtree *dtb = *(struct dirtree **)b;
int ret = 0, reverse = (toys.optflags & FLAG_r) ? -1 : 1;
+ if (toys.optflags & FLAG_S) {
+ if (dta->st.st_size > dtb->st.st_size) ret = -1;
+ else if (dta->st.st_size < dtb->st.st_size) ret = 1;
+ }
if (toys.optflags & FLAG_t) {
if (dta->st.st_mtime > dtb->st.st_mtime) ret = -1;
else if (dta->st.st_mtime < dtb->st.st_mtime) ret = 1;
@@ -178,6 +188,7 @@ static int filter(struct dirtree *new)
}
if (flags & FLAG_u) new->st.st_mtime = new->st.st_atime;
+ if (flags & FLAG_c) new->st.st_mtime = new->st.st_ctime;
if (flags & FLAG_k) new->st.st_blocks = (new->st.st_blocks + 1) / 2;
return dirtree_notdotdot(new);
}
@@ -248,7 +259,7 @@ static void listfiles(int dirfd, struct dirtree *indir)
} else {
// Read directory contents. We dup() the fd because this will close it.
indir->data = dup(dirfd);
- dirtree_recurse(indir, filter);
+ dirtree_recurse(indir, filter, (flags&FLAG_L));
}
// Copy linked list to array and sort it. Directories go in array because
@@ -265,6 +276,16 @@ static void listfiles(int dirfd, struct dirtree *indir)
continue;
}
+ // Label directory if not top of tree, or if -R
+ if (indir->parent && (!indir->extra || (flags & FLAG_R)))
+ {
+ char *path = dirtree_path(indir, 0);
+
+ if (TT.nl_title++) xputc('\n');
+ xprintf("%s:\n", path);
+ free(path);
+ }
+
if (!(flags & FLAG_f)) qsort(sort, dtlen, sizeof(void *), (void *)compare);
// Find largest entry in each field for display alignment
@@ -293,26 +314,20 @@ static void listfiles(int dirfd, struct dirtree *indir)
// If it fit, stop here
if (ul == dtlen) break;
}
- } else if (flags & (FLAG_l|FLAG_o|FLAG_n)) for (ul = 0; ul<dtlen; ul++) {
- entrylen(sort[ul], len);
- for (width=0; width<6; width++)
- if (len[width] > totals[width]) totals[width] = len[width];
- }
-
- // Label directory if not top of tree, or if -R
- if (indir->parent && (!indir->extra || (flags & FLAG_R)))
- {
- char *path = dirtree_path(indir, 0);
+ } else if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g|FLAG_s)) {
+ unsigned long blocks = 0;
+
+ for (ul = 0; ul<dtlen; ul++)
+ {
+ entrylen(sort[ul], len);
+ for (width=0; width<6; width++)
+ if (len[width] > totals[width]) totals[width] = len[width];
+ blocks += sort[ul]->st.st_blocks;
+ }
- if (TT.nl_title++) xputc('\n');
- xprintf("%s:\n", path);
- free(path);
+ if (indir->parent) xprintf("total %lu\n", blocks);
}
- // This is wrong, should be blocks used not file count.
- if (indir->parent && (flags & (FLAG_l|FLAG_o|FLAG_n)))
- xprintf("total %lu\n", dtlen);
-
// Loop through again to produce output.
memset(toybuf, ' ', 256);
width = 0;
@@ -348,9 +363,9 @@ static void listfiles(int dirfd, struct dirtree *indir)
if (flags & FLAG_s)
xprintf("% *lu ", len[6], (unsigned long)st->st_blocks);
- if (flags & (FLAG_l|FLAG_o|FLAG_n)) {
+ if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) {
struct tm *tm;
- char perm[11], thyme[64], c, d, *usr, buf[12], *grp, *grpad;
+ char perm[11], thyme[64], c, d, *usr, *upad, buf[12], *grp, *grpad;
int i, bit;
perm[10]=0;
@@ -376,26 +391,31 @@ static void listfiles(int dirfd, struct dirtree *indir)
tm = localtime(&(st->st_mtime));
strftime(thyme, sizeof(thyme), "%F %H:%M", tm);
- if (flags&FLAG_n) {
- usr = buf;
- utoa_to_buf(st->st_uid, buf, 12);
- } else usr = getusername(st->st_uid);
if (flags&FLAG_o) grp = grpad = toybuf+256;
else {
grp = (flags&FLAG_n) ? utoa(st->st_gid)
: getgroupname(st->st_gid);
grpad = toybuf+256-(totals[4]-len[4]);
}
+
+ if (flags&FLAG_g) usr = upad = toybuf+256;
+ else {
+ upad = toybuf+255-(totals[3]-len[3]);
+ if (flags&FLAG_n) {
+ usr = buf;
+ utoa_to_buf(st->st_uid, buf, 12);
+ } else usr = getusername(st->st_uid);
+ }
+
xprintf("%s% *d %s%s%s%s% *d %s ", perm, totals[2]+1, st->st_nlink,
- usr, toybuf+255-(totals[3]-len[3]),
- grp, grpad, totals[5]+1, st->st_size, thyme);
+ usr, upad, grp, grpad, totals[5]+1, st->st_size, thyme);
}
if (flags & FLAG_q) {
char *p;
for (p=sort[next]->name; *p; p++) xputc(isprint(*p) ? *p : '?');
} else xprintf("%s", sort[next]->name);
- if ((flags & (FLAG_l|FLAG_o|FLAG_n)) && S_ISLNK(mode))
+ if ((flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) && S_ISLNK(mode))
xprintf(" -> %s", sort[next]->symlink);
if (et) xputc(et);
@@ -412,7 +432,6 @@ static void listfiles(int dirfd, struct dirtree *indir)
// Free directory entries, recursing first if necessary.
for (ul = 0; ul<dtlen; free(sort[ul++])) {
-// TODO follow symlinks when?
if ((flags & FLAG_d) || !S_ISDIR(sort[ul]->st.st_mode)
|| !dirtree_notdotdot(sort[ul])) continue;
@@ -430,7 +449,7 @@ void ls_main(void)
struct dirtree *dt;
// Do we have an implied -1
- if (!isatty(1) || (toys.optflags&(FLAG_l|FLAG_o|FLAG_n)))
+ if (!isatty(1) || (toys.optflags&(FLAG_l|FLAG_o|FLAG_n|FLAG_g)))
toys.optflags |= FLAG_1;
else {
TT.screen_width = 80;
@@ -443,9 +462,10 @@ void ls_main(void)
// Iterate through command line arguments, collecting directories and files.
// Non-absolute paths are relative to current directory.
- TT.files = dirtree_add_node(0, 0);
+ TT.files = dirtree_add_node(0, 0, 0);
for (s = *toys.optargs ? toys.optargs : noargs; *s; s++) {
- dt = dirtree_add_node(AT_FDCWD, *s);
+ dt = dirtree_add_node(AT_FDCWD, *s,
+ (toys.optflags & (FLAG_L|FLAG_H|FLAG_l))^FLAG_l);
if (!dt) {
toys.exitval = 1;