Greetings,
I found some bugs in ls.
1. option -S does not work on its own:
$ echo "aa" >a; echo "b" >b; echo "ccc">c
$ ls -S
a
b
c
First attached patch fixes this.
2. When filenames contain control chars, it only works fine when no
option among -tSFilpR is specified:
$ touch "$(printf "toto\ntiti")"
$ ls -tq
ls: lstat toto?titi: No such file or directory
Second attached patch fixes this. Basically the problem was that
ent->name was modified “in place”. With the patch, the non-printable
chars are “sanitized” only when printed out. This also makes -q work
with -U (ent->name was not modified in this code path).
For the sanitizing function, I choose not to return a modified string in
order to avoid strdup-ing and free-ing, given that the sanitized name is
never used more than once. It causes some one-line printf's to be
exploded into several puts, though.
Not sure about all my choices; criticism is welcome.
Cheers
-- AN
>From 02a5919fa0895f694eb8f2b94c387cf21601746b Mon Sep 17 00:00:00 2001
From: Alexandre Niveau <alexandre.niv...@gmail.com>
Date: Thu, 14 May 2015 23:37:54 +0200
Subject: [PATCH 1/2] ls: fix option -S for it to work without -lnpFi
---
ls.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ls.c b/ls.c
index bb6403f..cdfce4d 100644
--- a/ls.c
+++ b/ls.c
@@ -239,7 +239,7 @@ lsdir(const char *path)
}
*p = '\0';
}
- mkent(&ents[n - 1], name, tflag || Fflag || iflag || lflag || pflag || Rflag, Lflag);
+ mkent(&ents[n - 1], name, tflag || Sflag || Fflag || iflag || lflag || pflag || Rflag, Lflag);
}
}
closedir(dp);
--
2.1.4
>From 5193c50e4ec76d412d16773e23ea490c5ecfef07 Mon Sep 17 00:00:00 2001
From: Alexandre Niveau <alexandre.niv...@gmail.com>
Date: Fri, 15 May 2015 00:15:39 +0200
Subject: [PATCH 2/2] ls: fix -q with -tSFilpRn and -U
Handling of -q was modifying ent->name 'in place', which was
preventing further stat-ing (and opendir-ing) when filenames
contained non-printable chars. With this commit, those chars
are replaced with '?' in the output only. It also incidentally
makes -q work with -U.
---
ls.c | 47 +++++++++++++++++++++++++----------------------
1 file changed, 25 insertions(+), 22 deletions(-)
diff --git a/ls.c b/ls.c
index cdfce4d..2ca12ac 100644
--- a/ls.c
+++ b/ls.c
@@ -97,6 +97,21 @@ indicator(mode_t mode)
}
static void
+putname(const char *name)
+{
+ Rune r;
+
+ if (qflag)
+ while (*name)
+ if (name += chartorune(&r, name), isprintrune(r))
+ efputrune(&r, stdout, "<stdout>");
+ else
+ putchar('?');
+ else
+ fputs(name, stdout);
+}
+
+static void
output(const struct entry *ent)
{
struct group *gr;
@@ -109,7 +124,8 @@ output(const struct entry *ent)
if (iflag)
printf("%lu ", (unsigned long)ent->ino);
if (!lflag) {
- printf("%s%s\n", ent->name, indicator(ent->mode));
+ putname(ent->name);
+ puts(indicator(ent->mode));
return;
}
if (S_ISREG(ent->mode))
@@ -165,7 +181,9 @@ output(const struct entry *ent)
printf("%10s ", humansize(ent->size));
else
printf("%10lu ", (unsigned long)ent->size);
- printf("%s %s%s", buf, ent->name, indicator(ent->mode));
+ printf("%s ", buf);
+ putname(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);
@@ -193,11 +211,10 @@ static void
lsdir(const char *path)
{
DIR *dp;
- Rune r;
struct entry ent, *ents = NULL;
struct dirent *d;
- size_t i, n = 0, len;
- char cwd[PATH_MAX], *p, *q, *name;
+ size_t i, n = 0;
+ char cwd[PATH_MAX];
if (!getcwd(cwd, sizeof(cwd)))
eprintf("getcwd:");
@@ -209,7 +226,8 @@ lsdir(const char *path)
if (many || Rflag) {
if (!first)
putchar('\n');
- printf("%s:\n", path);
+ putname(path);
+ puts(":");
}
first = 0;
@@ -224,22 +242,7 @@ lsdir(const char *path)
ls(&ent, Rflag);
} else {
ents = ereallocarray(ents, ++n, sizeof(*ents));
- name = p = estrdup(d->d_name);
- if (qflag) {
- q = d->d_name;
- while (*q) {
- len = chartorune(&r, q);
- if (isprintrune(r)) {
- memcpy(p, q, len);
- p += len, q += len;
- } else {
- *p++ = '?';
- q += len;
- }
- }
- *p = '\0';
- }
- mkent(&ents[n - 1], name, tflag || Sflag || Fflag || iflag || lflag || pflag || Rflag, Lflag);
+ mkent(&ents[n - 1], estrdup(d->d_name), tflag || Sflag || Fflag || iflag || lflag || pflag || Rflag, Lflag);
}
}
closedir(dp);
--
2.1.4