feat: extend shell with printLsEntry and listDir
This commit is contained in:
1 parent
ff36cc0275
commit
9e1f907da5
1 file changed
+141
-46
+141
-46
@@ -956,58 +956,148 @@ static void printThreads(void)
|
|||||||
|
|
||||||
static void buildPath(const char* argument, char* out)
|
static void buildPath(const char* argument, char* out)
|
||||||
{
|
{
|
||||||
if (argument[0] == '/') {
|
vfsResolvePath(argument, out, pathCapacity);
|
||||||
uint64 length = strlen(argument);
|
|
||||||
if (length >= pathCapacity)
|
|
||||||
length = pathCapacity - 1;
|
|
||||||
memcpy(out, argument, length);
|
|
||||||
out[length] = 0;
|
|
||||||
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++] = '/';
|
|
||||||
}
|
|
||||||
for (uint64 i = 0; argument[i] && pos < pathCapacity - 1; i++)
|
|
||||||
out[pos++] = argument[i];
|
|
||||||
out[pos] = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void listDir(const char* argument)
|
/* 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 path[pathCapacity];
|
char typeChar = stat->type == fsTypeDir ? 'd' : (stat->type == fsTypeFifo ? 'p' : 'f');
|
||||||
if (argument[0] == 0)
|
|
||||||
memcpy(path, cwd, strlen(cwd) + 1);
|
|
||||||
else
|
|
||||||
buildPath(argument, path);
|
|
||||||
|
|
||||||
VfsNode* node = vfsLookup(path);
|
if (!longFormat) {
|
||||||
if (!node) {
|
if (stat->type == fsTypeDir)
|
||||||
kprintf("ls: no such path: %s\n", path);
|
kprintf("%s/\n", name);
|
||||||
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
|
else
|
||||||
kprintf("%s %lu\n", vfsName(child), vfsSize(child));
|
kprintf("%s %lu\n", name, stat->size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void catFile(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];
|
||||||
|
buildPath(argument, path);
|
||||||
|
|
||||||
|
FbStat stat;
|
||||||
|
if (vfsStat(path, &stat) != 0) {
|
||||||
|
kprintf("ls: no such path: %s\n", path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
if (argument[0] == 0) {
|
||||||
kprintf("cat: missing file\n");
|
kprintf("cat: missing file\n");
|
||||||
return;
|
return 1;
|
||||||
}
|
}
|
||||||
char path[pathCapacity];
|
char path[pathCapacity];
|
||||||
buildPath(argument, path);
|
buildPath(argument, path);
|
||||||
@@ -1015,16 +1105,17 @@ static void catFile(const char* argument)
|
|||||||
VfsNode* node = vfsLookup(path);
|
VfsNode* node = vfsLookup(path);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
kprintf("cat: no such file: %s\n", path);
|
kprintf("cat: no such file: %s\n", path);
|
||||||
return;
|
return 1;
|
||||||
}
|
}
|
||||||
if (vfsIsDir(node)) {
|
if (vfsIsDir(node)) {
|
||||||
kprintf("cat: %s is a directory\n", path);
|
kprintf("cat: %s is a directory\n", path);
|
||||||
return;
|
return 1;
|
||||||
}
|
}
|
||||||
const char* data = (const char*)vfsFileData(node);
|
const char* data = (const char*)vfsFileData(node);
|
||||||
uint64 size = vfsSize(node);
|
uint64 size = vfsSize(node);
|
||||||
for (uint64 i = 0; i < size; i++)
|
for (uint64 i = 0; i < size; i++)
|
||||||
kputchar(data[i]);
|
kputchar(data[i]);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void echoWrite(char* text, const char* target)
|
static void echoWrite(char* text, const char* target)
|
||||||
@@ -1043,16 +1134,20 @@ static void echoWrite(char* text, const char* target)
|
|||||||
vfsClose(fd);
|
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) {
|
if (argument[0] == 0) {
|
||||||
kprintf("mkdir: missing path\n");
|
kprintf("mkdir: missing path\n");
|
||||||
return;
|
return 1;
|
||||||
}
|
}
|
||||||
char path[pathCapacity];
|
char path[pathCapacity];
|
||||||
buildPath(argument, path);
|
buildPath(argument, path);
|
||||||
if (vfsMkdir(path) != 0)
|
if (vfsMkdir(path) != 0) {
|
||||||
kprintf("mkdir: cannot create %s\n", path);
|
kprintf("mkdir: cannot create %s\n", path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void changeDir(const char* argument)
|
static void changeDir(const char* argument)
|
||||||
|
|||||||
Reference in new issue
Block a user