From e9a910b33c7837b4b868e3abda18eb4810df7f02 Mon Sep 17 00:00:00 2001 From: Leah Rowe Date: Sat, 4 Oct 2025 09:14:33 +0100 Subject: config/git: import suckless sbase i currently use the output of sha512sum in several places of xbmk, which is a bit unreliable in case output changes. other cases where i use util outputs in variables are probably reliable, because i'm using mostly posix utilities in those. to mitigate this, i now import suckless sbase, which has a reasonable sha512sum implementation. *every* binary it builds is being placed in build.list, because i'll probably start using more of them. for example, i may start modifying the "date" implementation, adding the GNU-specific options that i need as mentioned on init.sh i'm importing it in util/ because the sha512sum util is needed for verifying project sources, so if sbase itself is a "project source", that means we can into a chicken and egg bootstrapping problem. this is sbase at revision: 055cc1ae1b3a13c3d8f25af0a4a3316590efcd48 Signed-off-by: Leah Rowe --- util/sbase/ls.c | 489 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 489 insertions(+) create mode 100644 util/sbase/ls.c (limited to 'util/sbase/ls.c') diff --git a/util/sbase/ls.c b/util/sbase/ls.c new file mode 100644 index 00000000..aa95fef2 --- /dev/null +++ b/util/sbase/ls.c @@ -0,0 +1,489 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#ifndef major +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + struct timespec t; + dev_t dev; + dev_t rdev; + ino_t ino, tino; +}; + +static struct { + dev_t dev; + ino_t ino; +} tree[PATH_MAX]; + +static int ret = 0; +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 int showdirs; + +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_ctim; + else if (uflag) + ent->t = st.st_atim; + else + ent->t = st.st_mtim; + 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 +printname(const char *name) +{ + const char *c; + Rune r; + size_t l; + + for (c = name; *c; c += l) { + l = chartorune(&r, c); + if (!qflag || isprintrune(r)) + fwrite(c, 1, l, stdout); + else + putchar('?'); + } +} + +static void +output(const struct entry *ent) +{ + struct group *gr; + struct passwd *pw; + struct tm *tm; + ssize_t len; + char *fmt, buf[BUFSIZ], pwname[_SC_LOGIN_NAME_MAX], + grname[_SC_LOGIN_NAME_MAX], mode[] = "----------"; + + if (iflag) + printf("%lu ", (unsigned long)ent->ino); + if (!lflag) { + printname(ent->name); + puts(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'; + + 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.tv_sec + (180 * 24 * 60 * 60)) /* 6 months ago? */ + fmt = "%b %d %Y"; + else + fmt = "%b %d %H:%M"; + + if ((tm = localtime(&ent->t.tv_sec))) + strftime(buf, sizeof(buf), fmt, tm); + else + snprintf(buf, sizeof(buf), "%lld", (long long)(ent->t.tv_sec)); + 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 ", buf); + printname(ent->name); + fputs(indicator(ent->mode), stdout); + 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'); +} + +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': + if (!(cmp = b->t.tv_sec - a->t.tv_sec)) + cmp = b->t.tv_nsec - a->t.tv_nsec; + 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))) { + ret = 1; + weprintf("opendir %s%s:", path, dir->name); + return; + } + 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] || showdirs) { + fputs(path, stdout); + printname(dir->name); + puts(":"); + } + 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, 1); + } + } + + 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; i < PATH_MAX && tree[i].ino; ++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) { + ret = 1; + weprintf("%s%s: Already visited\n", path, ent->name); + return; + } + + if (!getcwd(cwd, PATH_MAX)) + eprintf("getcwd:"); + + if (first) + first = 0; + else + putchar('\n'); + + 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': + /* force output to 1 entry per line */ + qflag = 1; + 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) && + !(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) && + !(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)); + } + } + + showdirs = ds > 1 || (ds && fs); + + 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, "") | ret); +} -- cgit v1.2.1