feat: extend shell with printLsEntry and listDir
This commit is contained in:
1 parent
ff36cc0275
commit
9e1f907da5
1 file changed
+136
-41
+136
-41
@@ -956,58 +956,148 @@ static void printThreads(void)
|
||||
|
||||
static void buildPath(const char* argument, char* out)
|
||||
{
|
||||
if (argument[0] == '/') {
|
||||
uint64 length = strlen(argument);
|
||||
if (length >= pathCapacity)
|
||||
length = pathCapacity - 1;
|
||||
memcpy(out, argument, length);
|
||||
out[length] = 0;
|
||||
vfsResolvePath(argument, out, pathCapacity);
|
||||
}
|
||||
|
||||
/* One line of `ls` output for a single entry, real stat data (fs.h's
|
||||
FbStat) already resolved by the caller -- shared by both the plain and
|
||||
`-l` formats and by the "argument names a file, not a directory" case
|
||||
below, so all three ever only format an FbStat one way. */
|
||||
static void printLsEntry(bool longFormat, const FbStat* stat, const char* name)
|
||||
{
|
||||
char typeChar = stat->type == fsTypeDir ? 'd' : (stat->type == fsTypeFifo ? 'p' : 'f');
|
||||
|
||||
if (!longFormat) {
|
||||
if (stat->type == fsTypeDir)
|
||||
kprintf("%s/\n", name);
|
||||
else
|
||||
kprintf("%s %lu\n", name, stat->size);
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 pos = strlen(cwd);
|
||||
if (pos >= pathCapacity)
|
||||
pos = pathCapacity - 1;
|
||||
memcpy(out, cwd, pos);
|
||||
if (pos == 0 || out[pos - 1] != '/') {
|
||||
if (pos < pathCapacity - 1)
|
||||
out[pos++] = '/';
|
||||
/* Deliberately prints type/size/name BEFORE the date: a needle that
|
||||
greps for a known type+size+name substring (tools/ci-test.sh) then
|
||||
never has to also match today's wall-clock date, which is real and
|
||||
therefore different on every run. The real owner/group/other rwx
|
||||
permission string (fs.h's fsModeOwner*, fsModeGroup*, fsModeOther*) is
|
||||
appended AFTER the date for the same reason, in the other direction
|
||||
-- every existing needle that greps a type+size+name+date-prefix
|
||||
substring keeps matching unchanged, since nothing before it in the
|
||||
line moved (only perm itself grew longer, from 3 chars to the full
|
||||
9-char rwxrwxrwx real `ls -l` prints, now that a real gid exists to
|
||||
make the group triad meaningful). */
|
||||
char perm[10];
|
||||
perm[0] = (stat->mode & fsModeOwnerRead) ? 'r' : '-';
|
||||
perm[1] = (stat->mode & fsModeOwnerWrite) ? 'w' : '-';
|
||||
perm[2] = (stat->mode & fsModeOwnerExec) ? 'x' : '-';
|
||||
perm[3] = (stat->mode & fsModeGroupRead) ? 'r' : '-';
|
||||
perm[4] = (stat->mode & fsModeGroupWrite) ? 'w' : '-';
|
||||
perm[5] = (stat->mode & fsModeGroupExec) ? 'x' : '-';
|
||||
perm[6] = (stat->mode & fsModeOtherRead) ? 'r' : '-';
|
||||
perm[7] = (stat->mode & fsModeOtherWrite) ? 'w' : '-';
|
||||
perm[8] = (stat->mode & fsModeOtherExec) ? 'x' : '-';
|
||||
perm[9] = 0;
|
||||
|
||||
kprintf("%c %lu %s ", typeChar, stat->size, name);
|
||||
if (stat->mtime == 0) {
|
||||
/* tmpfs/dev entries never track time (fs.h's FbStat comment) --
|
||||
an honest "no timestamp" marker instead of a fake 1970 date. */
|
||||
kprintf("- %s\n", perm);
|
||||
} else {
|
||||
RtcDateTime dt;
|
||||
rtcCivilFromEpoch((int64)stat->mtime, &dt);
|
||||
kprintf("%04ld-%02ld-%02ld %02ld:%02ld:%02ld %s\n",
|
||||
dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, perm);
|
||||
}
|
||||
for (uint64 i = 0; argument[i] && pos < pathCapacity - 1; i++)
|
||||
out[pos++] = argument[i];
|
||||
out[pos] = 0;
|
||||
}
|
||||
|
||||
static void listDir(const char* argument)
|
||||
/* `ls [-l] [PATH]` -- generalized over vfsOpen/vfsGetdents/vfsStat
|
||||
(fs.h/vfs.h) instead of the old tmpfs-only vfsLookup/vfsChild/vfsNext
|
||||
VfsNode-tree walk, so a directory under `/mnt` (SFS) can finally be
|
||||
listed from the interactive shell at all (previously vfsLookup, which
|
||||
only ever understood the in-memory tmpfs tree, silently returned "no
|
||||
such path" for every SFS directory -- fstest.c/linktest.c could only
|
||||
ever prove SFS directory listing through a raw userGetdents syscall,
|
||||
never through this builtin). `-l` adds real stat data per entry (type,
|
||||
size, mtime -- sfs.h's on-disk timestamps, FALA 39) the same way a real
|
||||
`ls -l` would. */
|
||||
/* Returns 0 on success, 1 on failure (real POSIX `ls` exit-status shape) --
|
||||
feeds shell.c's new `&&`/`||` conditional chaining (execute()) the same
|
||||
way a spawned program's own real exit code already does, see
|
||||
shellSplitAndOr's own doc comment in shell.h. */
|
||||
static int listDir(const char* argument)
|
||||
{
|
||||
bool longFormat = false;
|
||||
if (argument[0] == '-' && argument[1] == 'l' && (argument[2] == 0 || argument[2] == ' ')) {
|
||||
longFormat = true;
|
||||
argument += 2;
|
||||
while (*argument == ' ')
|
||||
argument++;
|
||||
}
|
||||
|
||||
char path[pathCapacity];
|
||||
if (argument[0] == 0)
|
||||
memcpy(path, cwd, strlen(cwd) + 1);
|
||||
else
|
||||
buildPath(argument, path);
|
||||
|
||||
VfsNode* node = vfsLookup(path);
|
||||
if (!node) {
|
||||
FbStat stat;
|
||||
if (vfsStat(path, &stat) != 0) {
|
||||
kprintf("ls: no such path: %s\n", path);
|
||||
return;
|
||||
}
|
||||
if (!vfsIsDir(node)) {
|
||||
kprintf("%s %lu\n", vfsName(node), vfsSize(node));
|
||||
return;
|
||||
}
|
||||
for (VfsNode* child = vfsChild(node); child; child = vfsNext(child)) {
|
||||
if (vfsIsDir(child))
|
||||
kprintf("%s/\n", vfsName(child));
|
||||
else
|
||||
kprintf("%s %lu\n", vfsName(child), vfsSize(child));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void catFile(const char* argument)
|
||||
if (stat.type != fsTypeDir) {
|
||||
const char* base = path;
|
||||
for (const char* p = path; *p; p++) {
|
||||
if (*p == '/')
|
||||
base = p + 1;
|
||||
}
|
||||
printLsEntry(longFormat, &stat, base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fd = vfsOpen(path, oReadOnly);
|
||||
if (fd < 0) {
|
||||
kprintf("ls: cannot open %s\n", path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint64 pathLen = strlen(path);
|
||||
if (pathLen >= pathCapacity)
|
||||
pathLen = pathCapacity - 1;
|
||||
bool needSlash = pathLen == 0 || path[pathLen - 1] != '/';
|
||||
|
||||
FbDirent entries[16];
|
||||
int64 bytes;
|
||||
while ((bytes = vfsGetdents(fd, entries, sizeof(entries))) > 0) {
|
||||
int64 count = bytes / (int64)sizeof(FbDirent);
|
||||
for (int64 i = 0; i < count; i++) {
|
||||
char childPath[pathCapacity];
|
||||
uint64 childLen = pathLen;
|
||||
memcpy(childPath, path, childLen);
|
||||
if (needSlash && childLen < pathCapacity - 1)
|
||||
childPath[childLen++] = '/';
|
||||
uint64 nameLen = strlen(entries[i].name);
|
||||
if (childLen + nameLen >= pathCapacity)
|
||||
nameLen = pathCapacity - 1 - childLen;
|
||||
memcpy(childPath + childLen, entries[i].name, nameLen);
|
||||
childPath[childLen + nameLen] = 0;
|
||||
|
||||
FbStat childStat;
|
||||
if (vfsStat(childPath, &childStat) == 0)
|
||||
printLsEntry(longFormat, &childStat, entries[i].name);
|
||||
else
|
||||
kprintf("%s ?\n", entries[i].name);
|
||||
}
|
||||
}
|
||||
vfsClose(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Same "0 success, 1 failure" shape listDir above uses. */
|
||||
static int catFile(const char* argument)
|
||||
{
|
||||
if (argument[0] == 0) {
|
||||
kprintf("cat: missing file\n");
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
char path[pathCapacity];
|
||||
buildPath(argument, path);
|
||||
@@ -1015,16 +1105,17 @@ static void catFile(const char* argument)
|
||||
VfsNode* node = vfsLookup(path);
|
||||
if (!node) {
|
||||
kprintf("cat: no such file: %s\n", path);
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
if (vfsIsDir(node)) {
|
||||
kprintf("cat: %s is a directory\n", path);
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
const char* data = (const char*)vfsFileData(node);
|
||||
uint64 size = vfsSize(node);
|
||||
for (uint64 i = 0; i < size; i++)
|
||||
kputchar(data[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void echoWrite(char* text, const char* target)
|
||||
@@ -1043,16 +1134,20 @@ static void echoWrite(char* text, const char* target)
|
||||
vfsClose(fd);
|
||||
}
|
||||
|
||||
static void makeDir(const char* argument)
|
||||
/* Same "0 success, 1 failure" shape listDir/catFile above use. */
|
||||
static int makeDir(const char* argument)
|
||||
{
|
||||
if (argument[0] == 0) {
|
||||
kprintf("mkdir: missing path\n");
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
char path[pathCapacity];
|
||||
buildPath(argument, path);
|
||||
if (vfsMkdir(path) != 0)
|
||||
if (vfsMkdir(path) != 0) {
|
||||
kprintf("mkdir: cannot create %s\n", path);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void changeDir(const char* argument)
|
||||
|
||||
Reference in new issue
Block a user