From ac4ad426926f003c15d7a15950b12f96ec914047 Mon Sep 17 00:00:00 2001 From: Tai Chi Minh Ralph Eastwood Date: Sun, 15 Feb 2015 12:16:55 +0000 Subject: [PATCH 2/3] ls: add -R recursive and -C column output --- README | 2 +- ls.1 | 4 ++ ls.c | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 155 insertions(+), 31 deletions(-) diff --git a/README b/README index 3cd5059..d1f4682 100644 --- a/README +++ b/README @@ -40,7 +40,7 @@ The following tools are implemented ('*' == finished, '#' == UTF-8 support, =* ln yes none =* logger yes none =* logname yes none -= ls no -C, -R, -q, -u += ls no -q, -u =* md5sum non-posix none =* mkdir yes none =* mkfifo yes none diff --git a/ls.1 b/ls.1 index 993d29e..ad90b37 100644 --- a/ls.1 +++ b/ls.1 @@ -16,6 +16,8 @@ are given the current directory is listed. .Bl -tag -width Ds .It Fl a Show hidden files (those beginning with '.'). +.It Fl C +Display output in columns if the output devices is a terminal .It Fl c Use time file's status was last changed instead of last modification time for sorting or printing. @@ -36,6 +38,8 @@ themselves. .It Fl l List detailed information about each file, including their type, permissions, links, owner, group, size, and last file status/modification time. +.It Fl R +Traverse directories recursively. .It Fl r Reverse the sort order. .It Fl t diff --git a/ls.c b/ls.c index 6c0fdef..eec5145 100644 --- a/ls.c +++ b/ls.c @@ -1,5 +1,6 @@ /* See LICENSE file for copyright and license details. */ #include +#include #include #include @@ -23,11 +24,13 @@ typedef struct { ino_t ino; } Entry; +static int entcmpdir(const void *, const void *); static int entcmp(const void *, const void *); -static void ls(Entry *); +static int ls(Entry *, FILE *, int); static void lsdir(const char *); static void mkent(Entry *, char *, int, int); -static void output(Entry *); +static void output(Entry *, FILE *); +static void columns(char *, size_t, int); static int aflag = 0; static int cflag = 0; @@ -41,20 +44,26 @@ static int lflag = 0; static int rflag = 0; static int tflag = 0; static int Uflag = 0; -static int first = 1; +static int Cflag = 0; +static int Rflag = 0; static int many; +static int first = 1; +static struct winsize ws; static void usage(void) { - eprintf("usage: %s [-1acdFHhiLlrtU] [file ...]\n", argv0); + eprintf("usage: %s [-1aCcdFHhiLlRrtU] [file ...]\n", argv0); } int main(int argc, char *argv[]) { - int i; + int i, c = 0; Entry *ents; + FILE *mfp; + char *buf; + size_t size; ARGBEGIN { case '1': @@ -96,6 +105,13 @@ main(int argc, char *argv[]) case 'U': Uflag = 1; break; + case 'C': + if ((Cflag = isatty(fileno(stdout)))) + ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); + break; + case 'R': + Rflag = 1; + break; default: usage(); } ARGEND; @@ -106,19 +122,47 @@ main(int argc, char *argv[]) ents = emalloc(argc * sizeof(*ents)); + if (Cflag) + mfp = open_memstream(&buf, &size); + else + mfp = stdout; + 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]); + c += ls(&ents[rflag ? argc-i-1 : i], mfp, 1); + + if (Cflag) { + fclose(mfp); + columns(buf, size, c); + free(buf); + } return 0; } static int +entcmpdir(const void *va, const void *vb) +{ + const Entry *a = va, *b = vb; + int ad, bd; + + ad = (S_ISDIR(a->mode) || (S_ISLNK(a->mode) && S_ISDIR(a->tmode) && !Fflag && !lflag)); + bd = (S_ISDIR(b->mode) || (S_ISLNK(b->mode) && S_ISDIR(b->tmode) && !Fflag && !lflag)); + return ad - bd; +} + +static int entcmp(const void *va, const void *vb) { const Entry *a = va, *b = vb; + int d = 0; + + if (many && !dflag) { + if ((d = entcmpdir(va, vb))) + return d; + } if (tflag) return b->t - a->t; @@ -126,13 +170,15 @@ entcmp(const void *va, const void *vb) return strcoll(a->name, b->name); } -static void -ls(Entry *ent) +static int +ls(Entry *ent, FILE *mfp, int recurse) { - if ((S_ISDIR(ent->mode) || (S_ISLNK(ent->mode) && S_ISDIR(ent->tmode) && !Fflag && !lflag)) && !dflag) { + if (recurse && (S_ISDIR(ent->mode) || (S_ISLNK(ent->mode) && S_ISDIR(ent->tmode) && !Fflag && !lflag)) && !dflag) { lsdir(ent->name); + return 0; } else { - output(ent); + output(ent, mfp); + return 1; } } @@ -143,8 +189,11 @@ lsdir(const char *path) long i, n = 0; struct dirent *d; DIR *dp; - Entry ent, *ents = NULL; + Entry ent, *entp, *ents = NULL; size_t sz; + FILE *mfp; + char *buf; + size_t size; cwd = agetcwd(); if (!(dp = opendir(path))) @@ -152,36 +201,62 @@ lsdir(const char *path) if (chdir(path) < 0) eprintf("chdir %s:", path); - if (many) { + if (many || Rflag) { if (!first) putchar('\n'); printf("%s:\n", path); - first = 0; } + first = 0; + + if (Cflag) + mfp = open_memstream(&buf, &size); + else + mfp = stdout; while ((d = readdir(dp))) { if (d->d_name[0] == '.' && !aflag) continue; - if (Uflag){ - mkent(&ent, d->d_name, Fflag || lflag || iflag, Lflag); - output(&ent); + if (Uflag && !Rflag){ + mkent(&ent, d->d_name, Fflag || lflag || iflag || Rflag, Lflag); + n += ls(&ent, mfp, Rflag); } else { ents = erealloc(ents, ++n * sizeof *ents); p = emalloc((sz = strlen(d->d_name)+1)); memcpy(p, d->d_name, sz); - mkent(&ents[n-1], p, tflag || Fflag || lflag || iflag, Lflag); + mkent(&ents[n-1], p, tflag || Fflag || lflag || iflag || Rflag, Lflag); } } closedir(dp); - if (!Uflag){ - qsort(ents, n, sizeof *ents, entcmp); + if (!Uflag || Rflag) { + qsort(ents, n, sizeof *ents, Uflag ? entcmpdir : entcmp); for (i = 0; i < n; i++) { - output(&ents[rflag ? n-i-1 : i]); - free(ents[rflag ? n-i-1 : i].name); + entp = &ents[rflag ? n-i-1 : i]; + if ((S_ISDIR(entp->mode) || + (S_ISLNK(entp->mode) && + S_ISDIR(entp->tmode) && + !Fflag && !lflag)) && Rflag) + break; + ls(entp, mfp, Rflag); + free(entp->name); } } + if (chdir(cwd) < 0) eprintf("chdir %s:", cwd); + + if (Cflag) { + fclose(mfp); + columns(buf, size, i); + free(buf); + } + + if (Rflag) { + for (; i < n; i++) { + ls(&ents[rflag ? n-i-1 : i], mfp, Rflag); + free(ents[rflag ? n-i-1 : i].name); + } + } + free(ents); free(cwd); } @@ -230,7 +305,7 @@ indicator(mode_t mode) } static void -output(Entry *ent) +output(Entry *ent, FILE *f) { char buf[BUFSIZ], *fmt; char mode[] = "----------"; @@ -240,10 +315,11 @@ output(Entry *ent) char pwname[_SC_LOGIN_NAME_MAX]; char grname[_SC_LOGIN_NAME_MAX]; + first = 0; if (iflag) - printf("%lu ", (unsigned long)ent->ino); + fprintf(f, "%lu ", (unsigned long)ent->ino); if (!lflag) { - printf("%s%s\n", ent->name, indicator(ent->mode)); + fprintf(f, "%s%s\n", ent->name, indicator(ent->mode)); return; } if (S_ISREG(ent->mode)) @@ -295,17 +371,61 @@ output(Entry *ent) 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); + fprintf(f, "%s %4ld %-8.8s %-8.8s ", mode, (long)ent->nlink, pwname, grname); if (hflag) - printf("%10s ", humansize((unsigned long)ent->size)); + fprintf(f, "%10s ", humansize((unsigned long)ent->size)); else - printf("%10lu ", (unsigned long)ent->size); - printf("%s %s%s", buf, ent->name, indicator(ent->mode)); + fprintf(f, "%10lu ", (unsigned long)ent->size); + fprintf(f, "%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)); + fprintf(f, " -> %s%s", buf, indicator(ent->tmode)); } - putchar('\n'); + fputc('\n', f); +} + +static void columns(char *buf, size_t sz, int cnt) +{ + int colw[16]; + char *p = buf, *lp = buf; + int i = 0, j, k, t, l, s; + int *ll = emalloc(sizeof(*ll) * cnt); + char **sp = emalloc(sizeof(char *) * cnt); + + while ((p = strchr(lp, '\n'))) { + *p = '\0'; + ll[i] = strlen(lp); + sp[i++] = lp; + lp = p + 1; + } + + for (l = 16; l > 0; --l) { /* each col size */ + t = k = 0; + s = cnt / l + !!(cnt % l); + memset(colw, 0, sizeof(*colw) * l); + for (j = 0; j < cnt; j++) { + k += (j && !(j % s)); + if (colw[k] < ll[j]) { + t += ll[j] - colw[k]; + colw[k] = ll[j]; + } + if (t > ws.ws_col - (l - 1) * 2) + break; + } + if (t <= ws.ws_col - (l - 1) * 2) + break; /* fits */ + } + if (l == 0) + l = 1; + for (j = 0; j < s; j++) { + for (i = 0; i < l && i * s + j < cnt; i++) + printf("%-*s", colw[i] + (i == l - 1 ? 0 : 2), + sp[i * s + j]); + putchar('\n'); + } + + free(ll); + free(sp); } -- 2.3.0