sbase/ls.c

329 lines
6.3 KiB
C
Raw Normal View History

2011-05-26 03:01:20 +00:00
/* See LICENSE file for copyright and license details. */
2015-02-14 20:02:41 +00:00
#include <sys/stat.h>
2011-05-26 03:01:20 +00:00
#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"
2011-05-26 03:01:20 +00:00
#include "util.h"
2015-02-16 18:20:06 +00:00
struct entry {
char *name;
mode_t mode, tmode;
2011-05-26 03:01:20 +00:00
nlink_t nlink;
2015-02-16 18:20:06 +00:00
uid_t uid;
gid_t gid;
off_t size;
time_t t;
ino_t ino;
};
2011-05-26 03:01:20 +00:00
static int aflag = 0;
static int cflag = 0;
static int dflag = 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 qflag = 0;
static int rflag = 0;
static int tflag = 0;
static int Uflag = 0;
static int first = 1;
static int many;
2011-05-26 03:01:20 +00:00
2013-06-14 18:20:47 +00:00
static void
2015-02-16 18:20:06 +00:00
mkent(struct entry *ent, char *path, int dostat, int follow)
2011-05-26 03:01:20 +00:00
{
struct stat st;
2015-02-16 18:20:06 +00:00
ent->name = path;
if (!dostat)
2013-07-18 19:14:53 +00:00
return;
if ((follow ? stat : lstat)(path, &st) < 0)
eprintf("%s %s:", follow ? "stat" : "lstat", path);
2015-02-16 18:20:06 +00:00
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->t = cflag ? st.st_ctime : st.st_mtime;
ent->ino = st.st_ino;
if (S_ISLNK(ent->mode))
ent->tmode = stat(path, &st) == 0 ? st.st_mode : 0;
2011-05-26 03:01:20 +00:00
}
static char *
2014-02-17 14:41:09 +00:00
indicator(mode_t mode)
{
if (!Fflag)
2014-02-17 14:41:09 +00:00
return "";
if (S_ISLNK(mode))
2014-02-17 14:41:09 +00:00
return "@";
else if (S_ISDIR(mode))
2014-02-17 14:41:09 +00:00
return "/";
else if (S_ISFIFO(mode))
2014-02-17 14:41:09 +00:00
return "|";
else if (S_ISSOCK(mode))
2014-02-17 14:41:09 +00:00
return "=";
2015-02-16 18:20:06 +00:00
else if (mode & S_IXUSR || mode & S_IXGRP || mode & S_IXOTH)
2014-02-17 14:41:09 +00:00
return "*";
else
return "";
}
static void
2015-02-16 18:20:06 +00:00
output(const struct entry *ent)
2011-05-26 03:01:20 +00:00
{
struct group *gr;
struct passwd *pw;
2015-02-16 18:20:06 +00:00
ssize_t len;
char buf[BUFSIZ], *fmt,
pwname[_SC_LOGIN_NAME_MAX], grname[_SC_LOGIN_NAME_MAX],
mode[] = "----------";
2011-05-26 03:01:20 +00:00
2013-09-27 14:32:50 +00:00
if (iflag)
printf("%lu ", (unsigned long)ent->ino);
if (!lflag) {
2014-02-17 14:41:09 +00:00
printf("%s%s\n", ent->name, indicator(ent->mode));
2011-05-26 03:01:20 +00:00
return;
}
if (S_ISREG(ent->mode))
2011-05-26 03:01:20 +00:00
mode[0] = '-';
else if (S_ISBLK(ent->mode))
2011-05-26 03:01:20 +00:00
mode[0] = 'b';
else if (S_ISCHR(ent->mode))
2011-05-26 03:01:20 +00:00
mode[0] = 'c';
else if (S_ISDIR(ent->mode))
2011-05-26 03:01:20 +00:00
mode[0] = 'd';
else if (S_ISFIFO(ent->mode))
2011-05-26 03:01:20 +00:00
mode[0] = 'p';
else if (S_ISLNK(ent->mode))
2011-05-26 03:01:20 +00:00
mode[0] = 'l';
else if (S_ISSOCK(ent->mode))
2011-05-26 03:01:20 +00:00
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';
2011-05-27 22:56:43 +00:00
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';
2011-05-26 03:01:20 +00:00
pw = getpwuid(ent->uid);
if (pw)
2015-02-16 18:20:06 +00:00
snprintf(pwname, LEN(pwname), "%s", pw->pw_name);
else
2015-02-16 18:20:06 +00:00
snprintf(pwname, LEN(pwname), "%d", ent->uid);
2011-05-26 03:01:20 +00:00
gr = getgrgid(ent->gid);
if (gr)
2015-02-16 18:20:06 +00:00
snprintf(grname, LEN(grname), "%s", gr->gr_name);
else
2015-02-16 18:20:06 +00:00
snprintf(grname, LEN(grname), "%d", ent->gid);
2011-05-26 03:01:20 +00:00
2015-02-16 18:20:06 +00:00
if (time(NULL) > ent->t + (180 * 24 * 60 * 60)) /* 6 months ago? */
2011-06-04 11:40:05 +00:00
fmt = "%b %d %Y";
2011-05-26 03:01:20 +00:00
else
fmt = "%b %d %H:%M";
2015-02-16 18:20:06 +00:00
strftime(buf, sizeof(buf), fmt, localtime(&ent->t));
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)) {
2015-02-16 18:20:06 +00:00
if ((len = readlink(ent->name, buf, sizeof(buf) - 1)) < 0)
2011-06-04 11:40:05 +00:00
eprintf("readlink %s:", ent->name);
buf[len] = '\0';
printf(" -> %s%s", buf, indicator(ent->tmode));
2011-06-04 11:40:05 +00:00
}
putchar('\n');
2011-05-26 03:01:20 +00:00
}
2015-02-16 18:20:06 +00:00
static int
entcmp(const void *va, const void *vb)
{
const struct entry *a = va, *b = vb;
if (tflag)
return b->t - a->t;
else
return strcmp(a->name, b->name);
}
static void
lsdir(const char *path)
{
DIR *dp;
Rune r;
2015-02-16 18:20:06 +00:00
struct entry ent, *ents = NULL;
struct dirent *d;
size_t i, j, k, n = 0, len;
2015-02-16 18:20:06 +00:00
char *cwd, *p;
cwd = agetcwd();
if (!(dp = opendir(path)))
eprintf("opendir %s:", path);
if (chdir(path) < 0)
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, Lflag);
output(&ent);
} else {
ents = erealloc(ents, ++n * sizeof(*ents));
p = estrdup(d->d_name);
if (qflag) {
len = strlen(p);
for (i = 1, j = 0; j + i <= len;) {
if (!fullrune(p + j, i)) {
i++;
continue;
}
charntorune(&r, p + j, i);
if (isprintrune(r)) {
j += i;
i = 1;
continue;
}
p[j] = '?';
for (k = j + 1; k < len - i + 2; k++) {
p[k] = p[k + i - 1];
}
len -= i - 1;
j += 1;
i = 1;
}
}
2015-02-16 18:20:06 +00:00
mkent(&ents[n - 1], p, tflag || Fflag || lflag || iflag, Lflag);
}
}
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) < 0)
eprintf("chdir %s:", cwd);
free(ents);
free(cwd);
}
static void
ls(const struct entry *ent)
{
if ((S_ISDIR(ent->mode) || (S_ISLNK(ent->mode) &&
S_ISDIR(ent->tmode) && !Fflag && !lflag)) && !dflag)
lsdir(ent->name);
else
output(ent);
}
static void
usage(void)
{
eprintf("usage: %s [-1acdFHhiLlqrtU] [file ...]\n", argv0);
2015-02-16 18:20:06 +00:00
}
int
main(int argc, char *argv[])
{
struct entry *ents;
size_t i;
ARGBEGIN {
case '1':
/* ignore */
break;
case 'a':
aflag = 1;
break;
case 'c':
cflag = 1;
break;
case 'd':
dflag = 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 'q':
qflag = 1;
break;
2015-02-16 18:20:06 +00:00
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++;
ents = emalloc(argc * sizeof(*ents));
for (i = 0; i < argc; i++)
mkent(&ents[i], argv[i], 1, Hflag || Lflag);
qsort(ents, argc, sizeof(*ents), entcmp);
for (i = 0; i < argc; i++)
ls(&ents[rflag ? argc-i-1 : i]);
return 0;
}