I've made an implementation of recursive output of ls and columns. ls -C is stated in the POSIX standard so I've tried to implement it sucklessly (as opposed to using the cols command). It dynamically resizes based on the contents of each column, which differs from ls | cols output.
ls -q replaces non printable characters with '?'; I'm not sure how to make files with non-printable characters as yet, all I know is this code path works with input that does not have non-printable characters. Let me know how this works for you. Cheers, Ralph -- Tai Chi Minh Ralph Eastwood tcmreastw...@gmail.com
From ae0cc9726105825d162f697d854938ce55daaa96 Mon Sep 17 00:00:00 2001 From: Tai Chi Minh Ralph Eastwood <tcmreastw...@gmail.com> Date: Thu, 12 Feb 2015 15:03:31 +0000 Subject: [PATCH 1/3] ls: fix using strcoll instead of strcmp --- ls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ls.c b/ls.c index 81b7f7d..6c0fdef 100644 --- a/ls.c +++ b/ls.c @@ -123,7 +123,7 @@ entcmp(const void *va, const void *vb) if (tflag) return b->t - a->t; else - return strcmp(a->name, b->name); + return strcoll(a->name, b->name); } static void -- 2.3.0
From ac4ad426926f003c15d7a15950b12f96ec914047 Mon Sep 17 00:00:00 2001 From: Tai Chi Minh Ralph Eastwood <tcmreastw...@gmail.com> 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 <sys/stat.h> +#include <sys/ioctl.h> #include <dirent.h> #include <grp.h> @@ -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
From 399a5a2f4923b38306828b0e1b4993be9e4146bb Mon Sep 17 00:00:00 2001 From: Tai Chi Minh Ralph Eastwood <tcmreastw...@gmail.com> Date: Sun, 15 Feb 2015 10:23:21 +0000 Subject: [PATCH 3/3] ls: add implementation of -q and -u flags --- ls.1 | 5 +++++ ls.c | 45 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/ls.1 b/ls.1 index ad90b37..2aa37b2 100644 --- a/ls.1 +++ b/ls.1 @@ -38,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 q +When printing file names, replace all non-printable characters with 'q'. .It Fl R Traverse directories recursively. .It Fl r @@ -46,6 +48,9 @@ Reverse the sort order. Sort files by last file status/modification time instead of by name. .It Fl U Keep the list unsorted. +.It Fl u +Use time file was last access instead of last modification time for sorting +or printing. .El .Sh SEE ALSO .Xr stat 2 diff --git a/ls.c b/ls.c index eec5145..ae5ec34 100644 --- a/ls.c +++ b/ls.c @@ -12,6 +12,7 @@ #include <unistd.h> #include "util.h" +#include "utf.h" typedef struct { char *name; @@ -46,6 +47,8 @@ static int tflag = 0; static int Uflag = 0; static int Cflag = 0; static int Rflag = 0; +static int qflag = 0; +static int uflag = 0; static int many; static int first = 1; static struct winsize ws; @@ -53,7 +56,7 @@ static struct winsize ws; static void usage(void) { - eprintf("usage: %s [-1aCcdFHhiLlRrtU] [file ...]\n", argv0); + eprintf("usage: %s [-1aCcdFHhiLlqRrtUu] [file ...]\n", argv0); } int @@ -74,6 +77,7 @@ main(int argc, char *argv[]) break; case 'c': cflag = 1; + uflag = 0; break; case 'd': dflag = 1; @@ -112,6 +116,13 @@ main(int argc, char *argv[]) case 'R': Rflag = 1; break; + case 'q': + qflag = 1; + break; + case 'u': + uflag = 1; + cflag = 0; + break; default: usage(); } ARGEND; @@ -276,7 +287,12 @@ mkent(Entry *ent, char *path, int dostat, int follow) ent->uid = st.st_uid; ent->gid = st.st_gid; ent->size = st.st_size; - ent->t = cflag ? st.st_ctime : st.st_mtime; + if (cflag) + ent->t = st.st_ctime; + else if (uflag) + ent->t = st.st_atime; + 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; @@ -314,8 +330,27 @@ output(Entry *ent, FILE *f) struct passwd *pw; char pwname[_SC_LOGIN_NAME_MAX]; char grname[_SC_LOGIN_NAME_MAX]; + char *name = ent->name, *p, *n; + Rune r; first = 0; + if (qflag) { + n = name = emalloc(utflen(ent->name) + 1); + strcpy(name, (p = ent->name)); + while (*p) { + len = chartorune(&r, p); + if (isprintrune(r)) { + while (len) { + *p++ = *n++; + --len; + } + } else { + *n++ = '?'; + p += len; + } + } + *n++ = '\0'; + } if (iflag) fprintf(f, "%lu ", (unsigned long)ent->ino); if (!lflag) { @@ -376,10 +411,10 @@ output(Entry *ent, FILE *f) fprintf(f, "%10s ", humansize((unsigned long)ent->size)); else fprintf(f, "%10lu ", (unsigned long)ent->size); - fprintf(f, "%s %s%s", buf, ent->name, indicator(ent->mode)); + fprintf(f, "%s %s%s", buf, name, indicator(ent->mode)); if (S_ISLNK(ent->mode)) { - if ((len = readlink(ent->name, buf, sizeof buf - 1)) < 0) - eprintf("readlink %s:", ent->name); + if ((len = readlink(name, buf, sizeof buf - 1)) < 0) + eprintf("readlink %s:", name); buf[len] = '\0'; fprintf(f, " -> %s%s", buf, indicator(ent->tmode)); } -- 2.3.0