sbase/ls.c
FRIGN ec8246bbc6 Un-boolify sbase
It actually makes the binaries smaller, the code easier to read
(gems like "val == true", "val == false" are gone) and actually
predictable in the sense of that we actually know what we're
working with (one bitwise operator was quite adventurous and
should now be fixed).

This is also more consistent with the other suckless projects
around which don't use boolean types.
2014-11-14 10:54:20 +00:00

304 lines
5.8 KiB
C

/* See LICENSE file for copyright and license details. */
#include <dirent.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "util.h"
typedef struct {
char *name;
mode_t mode;
nlink_t nlink;
uid_t uid;
gid_t gid;
off_t size;
time_t mtime;
ino_t ino;
} Entry;
static int entcmp(const void *, const void *);
static void ls(Entry *);
static void lsdir(const char *);
static void mkent(Entry *, char *, int);
static void output(Entry *);
static int aflag = 0;
static int dflag = 0;
static int Fflag = 0;
static int hflag = 0;
static int iflag = 0;
static int lflag = 0;
static int rflag = 0;
static int tflag = 0;
static int Uflag = 0;
static int first = 1;
static int many;
static void
usage(void)
{
eprintf("usage: %s [-1adFhilrtU] [FILE...]\n", argv0);
}
int
main(int argc, char *argv[])
{
int i;
Entry *ents;
ARGBEGIN {
case '1':
/* ignore */
break;
case 'a':
aflag = 1;
break;
case 'd':
dflag = 1;
break;
case 'F':
Fflag = 1;
break;
case 'h':
hflag = 1;
break;
case 'i':
iflag = 1;
break;
case 'l':
lflag = 1;
break;
case 'r':
rflag = 1;
break;
case 't':
tflag = 1;
break;
case 'U':
Uflag = 1;
break;
default:
usage();
} ARGEND;
many = (argc > 1);
if (argc == 0)
*--argv = ".", argc++;
if (!(ents = malloc(argc * sizeof *ents)))
eprintf("malloc:");
for (i = 0; i < argc; i++)
mkent(&ents[i], argv[i], 1);
qsort(ents, argc, sizeof *ents, entcmp);
for (i = 0; i < argc; i++)
ls(&ents[rflag ? argc-i-1 : i]);
return 0;
}
static int
entcmp(const void *va, const void *vb)
{
const Entry *a = va, *b = vb;
if (tflag)
return b->mtime - a->mtime;
else
return strcmp(a->name, b->name);
}
static void
ls(Entry *ent)
{
if (S_ISDIR(ent->mode) && !dflag) {
lsdir(ent->name);
} else {
output(ent);
}
}
static void
lsdir(const char *path)
{
char *cwd, *p;
long i, n = 0;
struct dirent *d;
DIR *dp;
Entry ent, *ents = NULL;
size_t sz;
cwd = agetcwd();
if (!(dp = opendir(path)))
eprintf("opendir %s:", path);
if (chdir(path) == -1)
eprintf("chdir %s:", path);
if (many) {
if (!first)
putchar('\n');
printf("%s:\n", path);
first = 0;
}
while ((d = readdir(dp))) {
if (d->d_name[0] == '.' && !aflag)
continue;
if (Uflag){
mkent(&ent, d->d_name, Fflag || lflag || iflag);
output(&ent);
} else {
if (!(ents = realloc(ents, ++n * sizeof *ents)))
eprintf("realloc:");
if (!(p = malloc((sz = strlen(d->d_name)+1))))
eprintf("malloc:");
memcpy(p, d->d_name, sz);
mkent(&ents[n-1], p, tflag || Fflag || lflag || iflag);
}
}
closedir(dp);
if (!Uflag){
qsort(ents, n, sizeof *ents, entcmp);
for (i = 0; i < n; i++) {
output(&ents[rflag ? n-i-1 : i]);
free(ents[rflag ? n-i-1 : i].name);
}
}
if (chdir(cwd) == -1)
eprintf("chdir %s:", cwd);
free(ents);
free(cwd);
}
static void
mkent(Entry *ent, char *path, int dostat)
{
struct stat st;
ent->name = path;
if (!dostat)
return;
if (lstat(path, &st) == -1)
eprintf("lstat %s:", 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;
ent->mtime = st.st_mtime;
ent->ino = st.st_ino;
}
static char *
indicator(mode_t mode)
{
if (!Fflag)
return "";
if (S_ISLNK(mode))
return "@";
else if (S_ISDIR(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 "*";
else
return "";
}
static void
output(Entry *ent)
{
char buf[BUFSIZ], *fmt;
char mode[] = "----------";
ssize_t len;
struct group *gr;
struct passwd *pw;
char pwname[_SC_LOGIN_NAME_MAX];
char grname[_SC_LOGIN_NAME_MAX];
Entry entlnk;
if (iflag)
printf("%lu ", (unsigned long)ent->ino);
if (!lflag) {
printf("%s%s\n", ent->name, indicator(ent->mode));
return;
}
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';
errno = 0;
pw = getpwuid(ent->uid);
if (errno || !pw)
snprintf(pwname, sizeof(pwname), "%d", ent->uid);
else
snprintf(pwname, sizeof(pwname), "%s", pw->pw_name);
errno = 0;
gr = getgrgid(ent->gid);
if (errno || !gr)
snprintf(grname, sizeof(grname), "%d", ent->gid);
else
snprintf(grname, sizeof(grname), "%s", gr->gr_name);
if (time(NULL) > ent->mtime + (180*24*60*60)) /* 6 months ago? */
fmt = "%b %d %Y";
else
fmt = "%b %d %H:%M";
strftime(buf, sizeof buf, fmt, localtime(&ent->mtime));
printf("%s %4ld %-8.8s %-8.8s ", mode, (long)ent->nlink, pwname, grname);
if (hflag)
printf("%10s ", humansize((unsigned long)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)
eprintf("readlink %s:", ent->name);
buf[len] = '\0';
mkent(&entlnk, buf, Fflag);
printf(" -> %s%s", buf, indicator(entlnk.mode));
}
putchar('\n');
}