sbase/ls.c
Quentin Rameau 57d220b3dc ls: fix entities arrays handling in main()
Entities arrays in main() were arrays of pointer to entities and were
not compatible with entcmp().
They have been changed to being arrays of entities.
Thanks to Michael Forney <mforney@mforney.org> for having seen that.
2015-11-01 17:29:39 +00:00

477 lines
9.1 KiB
C

/* See LICENSE file for copyright and license details. */
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "utf.h"
#include "util.h"
struct entry {
char *name;
mode_t mode, tmode;
nlink_t nlink;
uid_t uid;
gid_t gid;
off_t size;
time_t t;
dev_t dev;
dev_t rdev;
ino_t ino, tino;
};
static struct {
dev_t dev;
ino_t ino;
} tree[PATH_MAX];
static int Aflag = 0;
static int aflag = 0;
static int cflag = 0;
static int dflag = 0;
static int Fflag = 0;
static int fflag = 0;
static int Hflag = 0;
static int hflag = 0;
static int iflag = 0;
static int Lflag = 0;
static int lflag = 0;
static int nflag = 0;
static int pflag = 0;
static int qflag = 0;
static int Rflag = 0;
static int rflag = 0;
static int Uflag = 0;
static int uflag = 0;
static int first = 1;
static char sort = 0;
static void ls(const char *, const struct entry *, int);
static void
mkent(struct entry *ent, char *path, int dostat, int follow)
{
struct stat st;
ent->name = path;
if (!dostat)
return;
if ((follow ? stat : lstat)(path, &st) < 0)
eprintf("%s %s:", follow ? "stat" : "lstat", path);
ent->mode = st.st_mode;
ent->nlink = st.st_nlink;
ent->uid = st.st_uid;
ent->gid = st.st_gid;
ent->size = st.st_size;
if (cflag)
ent->t = st.st_ctime;
else if (uflag)
ent->t = st.st_atime;
else
ent->t = st.st_mtime;
ent->dev = st.st_dev;
ent->rdev = st.st_rdev;
ent->ino = st.st_ino;
if (S_ISLNK(ent->mode)) {
if (stat(path, &st) == 0) {
ent->tmode = st.st_mode;
ent->dev = st.st_dev;
ent->tino = st.st_ino;
} else {
ent->tmode = ent->tino = 0;
}
}
}
static char *
indicator(mode_t mode)
{
if (pflag || Fflag)
if (S_ISDIR(mode))
return "/";
if (Fflag) {
if (S_ISLNK(mode))
return "@";
else if (S_ISFIFO(mode))
return "|";
else if (S_ISSOCK(mode))
return "=";
else if (mode & S_IXUSR || mode & S_IXGRP || mode & S_IXOTH)
return "*";
}
return "";
}
static void
output(const struct entry *ent)
{
struct group *gr;
struct passwd *pw;
ssize_t len;
size_t l;
char *name, *c, *u, *fmt, buf[BUFSIZ],
pwname[_SC_LOGIN_NAME_MAX], grname[_SC_LOGIN_NAME_MAX],
mode[] = "----------";
Rune r;
if (qflag) {
name = emalloc(strlen(ent->name) + 1);
for (c = name, u = ent->name; *u; u += l) {
l = chartorune(&r, u);
if (isprintrune(r)) {
memcpy(c, u, l);
c += l;
} else {
*c++ = '?';
}
}
*c = '\0';
} else {
name = ent->name;
}
if (iflag)
printf("%lu ", (unsigned long)ent->ino);
if (!lflag) {
printf("%s%s\n", name, indicator(ent->mode));
goto cleanup;
}
if (S_ISREG(ent->mode))
mode[0] = '-';
else if (S_ISBLK(ent->mode))
mode[0] = 'b';
else if (S_ISCHR(ent->mode))
mode[0] = 'c';
else if (S_ISDIR(ent->mode))
mode[0] = 'd';
else if (S_ISFIFO(ent->mode))
mode[0] = 'p';
else if (S_ISLNK(ent->mode))
mode[0] = 'l';
else if (S_ISSOCK(ent->mode))
mode[0] = 's';
else
mode[0] = '?';
if (ent->mode & S_IRUSR) mode[1] = 'r';
if (ent->mode & S_IWUSR) mode[2] = 'w';
if (ent->mode & S_IXUSR) mode[3] = 'x';
if (ent->mode & S_IRGRP) mode[4] = 'r';
if (ent->mode & S_IWGRP) mode[5] = 'w';
if (ent->mode & S_IXGRP) mode[6] = 'x';
if (ent->mode & S_IROTH) mode[7] = 'r';
if (ent->mode & S_IWOTH) mode[8] = 'w';
if (ent->mode & S_IXOTH) mode[9] = 'x';
if (ent->mode & S_ISUID) mode[3] = (mode[3] == 'x') ? 's' : 'S';
if (ent->mode & S_ISGID) mode[6] = (mode[6] == 'x') ? 's' : 'S';
if (ent->mode & S_ISVTX) mode[9] = (mode[9] == 'x') ? 't' : 'T';
if (!nflag && (pw = getpwuid(ent->uid)))
snprintf(pwname, sizeof(pwname), "%s", pw->pw_name);
else
snprintf(pwname, sizeof(pwname), "%d", ent->uid);
if (!nflag && (gr = getgrgid(ent->gid)))
snprintf(grname, sizeof(grname), "%s", gr->gr_name);
else
snprintf(grname, sizeof(grname), "%d", ent->gid);
if (time(NULL) > ent->t + (180 * 24 * 60 * 60)) /* 6 months ago? */
fmt = "%b %d %Y";
else
fmt = "%b %d %H:%M";
strftime(buf, sizeof(buf), fmt, localtime(&ent->t));
printf("%s %4ld %-8.8s %-8.8s ", mode, (long)ent->nlink, pwname, grname);
if (S_ISBLK(ent->mode) || S_ISCHR(ent->mode))
printf("%4u, %4u ", major(ent->rdev), minor(ent->rdev));
else if (hflag)
printf("%10s ", humansize(ent->size));
else
printf("%10lu ", (unsigned long)ent->size);
printf("%s %s%s", buf, ent->name, indicator(ent->mode));
if (S_ISLNK(ent->mode)) {
if ((len = readlink(ent->name, buf, sizeof(buf) - 1)) < 0)
eprintf("readlink %s:", ent->name);
buf[len] = '\0';
printf(" -> %s%s", buf, indicator(ent->tmode));
}
putchar('\n');
cleanup:
if (qflag)
free(name);
}
static int
entcmp(const void *va, const void *vb)
{
int cmp = 0;
const struct entry *a = va, *b = vb;
switch (sort) {
case 'S':
cmp = b->size - a->size;
break;
case 't':
cmp = b->t - a->t;
break;
}
if (!cmp)
cmp = strcmp(a->name, b->name);
return rflag ? 0 - cmp : cmp;
}
static void
lsdir(const char *path, const struct entry *dir)
{
DIR *dp;
struct entry *ent, *ents = NULL;
struct dirent *d;
size_t i, n = 0;
char prefix[PATH_MAX];
if (!(dp = opendir(dir->name)))
eprintf("opendir %s:", dir->name);
if (chdir(dir->name) < 0)
eprintf("chdir %s:", dir->name);
while ((d = readdir(dp))) {
if (d->d_name[0] == '.' && !aflag && !Aflag)
continue;
else if (Aflag)
if (strcmp(d->d_name, ".") == 0 ||
strcmp(d->d_name, "..") == 0)
continue;
ents = ereallocarray(ents, ++n, sizeof(*ents));
mkent(&ents[n - 1], estrdup(d->d_name), Fflag || iflag ||
lflag || pflag || Rflag || sort, Lflag);
}
closedir(dp);
if (!Uflag)
qsort(ents, n, sizeof(*ents), entcmp);
if (path[0] || dir->name[0] != '.')
printf("%s:\n", dir->name);
for (i = 0; i < n; i++)
output(&ents[i]);
if (Rflag) {
if (snprintf(prefix, PATH_MAX, "%s%s/", path, dir->name) >=
PATH_MAX)
eprintf("path too long: %s%s\n", path, dir->name);
for (i = 0; i < n; i++) {
ent = &ents[i];
if (strcmp(ent->name, ".") == 0 ||
strcmp(ent->name, "..") == 0)
continue;
if (S_ISLNK(ent->mode) && S_ISDIR(ent->tmode) && !Lflag)
continue;
ls(prefix, ent, Rflag);
}
}
for (i = 0; i < n; ++i)
free(ents[i].name);
free(ents);
}
static int
visit(const struct entry *ent)
{
dev_t dev;
ino_t ino;
int i;
dev = ent->dev;
ino = S_ISLNK(ent->mode) ? ent->tino : ent->ino;
for (i = 0; tree[i].ino && i < PATH_MAX; ++i) {
if (ino == tree[i].ino && dev == tree[i].dev)
return -1;
}
tree[i].ino = ino;
tree[i].dev = dev;
return i;
}
static void
ls(const char *path, const struct entry *ent, int listdir)
{
int treeind;
char cwd[PATH_MAX];
if (!listdir) {
output(ent);
} else if (S_ISDIR(ent->mode) ||
(S_ISLNK(ent->mode) && S_ISDIR(ent->tmode))) {
if ((treeind = visit(ent)) < 0) {
fprintf(stderr, "%s%s: already visited\n",
path, ent->name);
return;
}
if (!getcwd(cwd, PATH_MAX))
eprintf("getcwd:");
if (first)
first = !first;
else
putchar('\n');
fputs(path, stdout);
lsdir(path, ent);
tree[treeind].ino = 0;
if (chdir(cwd) < 0)
eprintf("chdir %s:", cwd);
}
}
static void
usage(void)
{
eprintf("usage: %s [-1AacdFfHhiLlnpqRrtUu] [file ...]\n", argv0);
}
int
main(int argc, char *argv[])
{
struct entry ent, *dents, *fents;
size_t i, ds, fs;
ARGBEGIN {
case '1':
/* ignore */
break;
case 'A':
Aflag = 1;
break;
case 'a':
aflag = 1;
break;
case 'c':
cflag = 1;
uflag = 0;
break;
case 'd':
dflag = 1;
break;
case 'f':
aflag = 1;
fflag = 1;
Uflag = 1;
break;
case 'F':
Fflag = 1;
break;
case 'H':
Hflag = 1;
break;
case 'h':
hflag = 1;
break;
case 'i':
iflag = 1;
break;
case 'L':
Lflag = 1;
break;
case 'l':
lflag = 1;
break;
case 'n':
lflag = 1;
nflag = 1;
break;
case 'p':
pflag = 1;
break;
case 'q':
qflag = 1;
break;
case 'R':
Rflag = 1;
break;
case 'r':
rflag = 1;
break;
case 'S':
sort = 'S';
break;
case 't':
sort = 't';
break;
case 'U':
Uflag = 1;
break;
case 'u':
uflag = 1;
cflag = 0;
break;
default:
usage();
} ARGEND
switch (argc) {
case 0: /* fallthrough */
*--argv = ".", ++argc;
case 1:
mkent(&ent, argv[0], 1, Hflag || Lflag);
ls("", &ent, (!dflag && S_ISDIR(ent.mode)) ||
((S_ISLNK(ent.mode) && S_ISDIR(ent.tmode)) &&
((Hflag || Lflag) || !(dflag || Fflag || lflag))));
break;
default:
for (i = ds = fs = 0, fents = dents = NULL; i < argc; ++i) {
mkent(&ent, argv[i], 1, Hflag || Lflag);
if ((!dflag && S_ISDIR(ent.mode)) ||
((S_ISLNK(ent.mode) && S_ISDIR(ent.tmode)) &&
((Hflag || Lflag) || !(dflag || Fflag || lflag)))) {
dents = ereallocarray(dents, ++ds, sizeof(*dents));
memcpy(&dents[ds - 1], &ent, sizeof(ent));
} else {
fents = ereallocarray(fents, ++fs, sizeof(*fents));
memcpy(&fents[fs - 1], &ent, sizeof(ent));
}
}
qsort(fents, fs, sizeof(ent), entcmp);
qsort(dents, ds, sizeof(ent), entcmp);
for (i = 0; i < fs; ++i)
ls("", &fents[i], 0);
free(fents);
if (fs && ds)
putchar('\n');
for (i = 0; i < ds; ++i)
ls("", &dents[i], 1);
free(dents);
}
return fshut(stdout, "<stdout>");
}