From b2eaffe1d5b1d730102b9fe941fd6cb66ef85f73 Mon Sep 17 00:00:00 2001 From: Quentin Rameau Date: Sun, 19 Apr 2015 00:19:31 +0200 Subject: [PATCH 2/4] ls: rework of the display and recursion logic ls now displays sorted operands and displays directory entries before recursing. Infinite loops are prevented but recursion continues. Symbolic links following is now constistent with given options. --- ls.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 153 insertions(+), 65 deletions(-) diff --git a/ls.c b/ls.c index d211342..87392c3 100644 --- a/ls.c +++ b/ls.c @@ -1,8 +1,10 @@ /* See LICENSE file for copyright and license details. */ #include +#include #include #include +#include #include #include #include @@ -10,6 +12,7 @@ #include #include +#include "arg.h" #include "utf.h" #include "util.h" @@ -21,9 +24,15 @@ struct entry { gid_t gid; off_t size; time_t t; - ino_t ino; + dev_t dev; + ino_t ino, tino; }; +static struct { + dev_t dev; + ino_t ino; +} tree[PATH_MAX] = { { 0, 0 } }; + static int Aflag = 0; static int aflag = 0; static int cflag = 0; @@ -39,15 +48,13 @@ static int nflag = 0; static int pflag = 0; static int qflag = 0; static int Rflag = 0; -static int Sflag = 0; static int rflag = 0; +static int Sflag = 0; static int tflag = 0; static int Uflag = 0; static int uflag = 0; -static int first = 1; -static int many; -static void ls(const struct entry *ent, int recurse); +static void ls(char *, struct entry *, int); static void mkent(struct entry *ent, char *path, int dostat, int follow) @@ -71,8 +78,16 @@ mkent(struct entry *ent, char *path, int dostat, int follow) else ent->t = st.st_mtime; ent->ino = st.st_ino; - if (S_ISLNK(ent->mode)) - ent->tmode = stat(path, &st) == 0 ? st.st_mode : 0; + ent->dev = st.st_dev; + if (S_ISLNK(ent->mode)) { + if (stat(path, &st) == 0) { + ent->tmode = st.st_mode; + ent->tino = st.st_ino; + ent->dev = st.st_dev; + } else { + ent->tmode = ent->tino = 0; + } + } } static char * @@ -199,78 +214,126 @@ cleanup: } static int +visit(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 int entcmp(const void *va, const void *vb) { int cmp = 0; - const struct entry *a = va, *b = vb; + const struct entry **a = (void *)va, **b = (void *)vb; if (Sflag) - cmp = b->size - a->size; + cmp = (*b)->size - (*a)->size; else if (tflag) - cmp = b->t - a->t; + cmp = (*b)->t - (*a)->t; - return cmp ? cmp : strcmp(a->name, b->name); + return cmp ? cmp : strcmp((*a)->name, (*b)->name); } static void -lsdir(const char *path) +lsdir(char *path, struct entry *dir) { DIR *dp; - struct entry ent, *ents = NULL; + struct entry *ent, **ents = NULL; struct dirent *d; - size_t i, n = 0; - char cwd[PATH_MAX], *name; - - if (!getcwd(cwd, sizeof(cwd))) - eprintf("getcwd:"); - if (!(dp = opendir(path))) - eprintf("opendir %s:", path); - if (chdir(path) < 0) - eprintf("chdir %s:", path); - - if (many || Rflag) { - if (!first) - putchar('\n'); - printf("%s:\n", path); - } - first = 0; + size_t nent = 0; + int i; + 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) + if (!(d->d_name[0] != '.' || aflag || + (Aflag && d->d_name[1] && d->d_name[1] != '.' && + d->d_name[2]))) continue; - else if (Aflag) - if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) - continue; - if (Uflag){ - mkent(&ent, d->d_name, Fflag || lflag || pflag || iflag || Rflag, Lflag); - ls(&ent, Rflag); - } else { - ents = ereallocarray(ents, ++n, sizeof(*ents)); - name = estrdup(d->d_name); - mkent(&ents[n - 1], name, tflag || Fflag || iflag || lflag || pflag || Rflag, Lflag); - } + + ent = emalloc(sizeof(struct entry)); + mkent(ent, estrdup(d->d_name), Fflag || iflag || lflag || + pflag || Rflag || Sflag, Lflag); + ents = ereallocarray(ents, ++nent, sizeof(ent)); + ents[nent - 1] = ent; } + closedir(dp); - if (!Uflag){ - qsort(ents, n, sizeof(*ents), entcmp); - for (i = 0; i < n; i++) { - ls(&ents[rflag ? (n - i - 1) : i], Rflag); - free(ents[rflag ? (n - i - 1) : i].name); + + if (!Uflag) + qsort(ents, nent, sizeof(ent), entcmp); + + if (path[0] || dir->name[0] != '.') + printf("%s:\n", dir->name); + for (i = 0; i < nent; i++) + output(ents[rflag ? (nent -1 -i) : i]); + printf("\n"); + + if (Rflag) { + if (snprintf(prefix, PATH_MAX, "%s%s/", path, dir->name) >= PATH_MAX) + eprintf("path too long\n"); + for (i = 0; i < nent; i++) { + ent = ents[rflag ? (nent -1 -i) : i]; + if (ent->name[0] == '.' && (!ent->name[1] || + (ent->name[1] == '.' && !ent->name[2]))) + continue; + if (S_ISLNK(ent->mode) && S_ISDIR(ent->tmode) && !Lflag) + continue; + ls(prefix, ent, Rflag); } } - if (chdir(cwd) < 0) - eprintf("chdir %s:", cwd); + + for (i = 0; i < nent; i++) { + free(ents[i]->name); + free(ents[i]); + } + free(ents); } static void -ls(const struct entry *ent, int recurse) +ls(char *path, struct entry *ent, int listdir) { - if (recurse && (S_ISDIR(ent->mode) || (S_ISLNK(ent->mode) && - S_ISDIR(ent->tmode) && !Fflag && !lflag)) && !dflag) - lsdir(ent->name); - else + 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\n", + path, ent->name); + return; + } + + if (!getcwd(cwd, PATH_MAX)) + eprintf("getcwd:"); + + printf("%s", path); + lsdir(path, ent); + tree[treeind].ino = 0; + + if (chdir(cwd) < 0) + eprintf("chdir %s:", cwd); + } } static void @@ -282,8 +345,8 @@ usage(void) int main(int argc, char *argv[]) { - struct entry *ents; - size_t i; + struct entry *ent, **fents, **dents; + size_t i, ds, fs; ARGBEGIN { case '1': @@ -329,8 +392,8 @@ main(int argc, char *argv[]) lflag = 1; break; case 'n': - lflag = 1; nflag = 1; + lflag = 1; break; case 'p': pflag = 1; @@ -368,17 +431,42 @@ main(int argc, char *argv[]) usage(); } ARGEND; - many = (argc > 1); - if (argc == 0) - *--argv = ".", argc++; + switch (argc) { + case 0: /* fallthrough */ + ++argc, *--argv = "."; + case 1: + ent = emalloc(sizeof(struct entry)); + 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) { + ent = emalloc(sizeof(struct entry)); + 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(ent)); + dents[ds - 1] = ent; + } else { + fents = ereallocarray(fents, ++fs, sizeof(ent)); + fents[fs - 1] = ent; + } + } + + qsort(fents, fs, sizeof(ent), entcmp); + qsort(dents, ds, sizeof(ent), entcmp); - ents = ereallocarray(NULL, argc, sizeof(*ents)); + for (i = 0; i < fs; ++i) + ls("", fents[rflag ? (fs -1 -i) : i], 0); + printf("\n"); + for (i = 0; i < ds; ++i) + ls("", dents[rflag ? (ds -1 -i) : i], 1); + break; + } - 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], 1); return fshut(stdout, ""); } -- 2.3.5