more features, flags now mostly work, added support for -R flag in ls with a custom bash method for recursive tree traversal

This commit is contained in:
2026-02-16 07:38:12 +01:00
parent 0ad0e362d6
commit 1faab39849
10 changed files with 104 additions and 67 deletions

View File

@@ -33,7 +33,7 @@
}, },
"Interactible": false, "Interactible": false,
"Func": null, "Func": null,
"Parent": null "Parent": 1
}, },
"2": { "2": {
"Inode": 2, "Inode": 2,

View File

@@ -0,0 +1,15 @@
import { ExitCode, type Bash } from "../bash";
import type { CommandArgs, ICommand, Result } from "../static";
export const cmd_clear = function(this: Bash, args: CommandArgs): Result {
let result: Result = { exitCode: ExitCode.ERROR, path: this.getCwd() };
result.exitCode = ExitCode.SUCCESS;
return result;
}
export const clear: ICommand = {
method: cmd_clear,
flags: [] as string[],
help: 'PATH TO HELP.md',
root: false
};

View File

@@ -76,7 +76,8 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
const flagInfo = checkFlags(args.flags); const flagInfo = checkFlags(args.flags);
let nodes: TreeNode[] = data; const nodes: TreeNode[] = data;
let nodeIndex: number = 0;
const f_a: boolean = flagInfo.has('a') || flagInfo.has('all'); const f_a: boolean = flagInfo.has('a') || flagInfo.has('all');
const f_A: boolean = flagInfo.has('A') || flagInfo.has('almost-all'); const f_A: boolean = flagInfo.has('A') || flagInfo.has('almost-all');
@@ -105,6 +106,18 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
const w: HTMLElement = document.createElement('div'); const w: HTMLElement = document.createElement('div');
if(f_R) {
const treeWalkResult: TreeNode[] = [];
const treeWalkCallback = (node: TreeNode) => { treeWalkResult.push(node); }
for(const node of nodes) {
treeWalkResult.push(node);
Fs.recursiveTraversalPre(node, treeWalkCallback);
}
nodes.length = 0;
nodes.push(...treeWalkResult.filter((node) => node.type != Type.File));
}
for (const node of nodes) { for (const node of nodes) {
const elem: HTMLElement = document.createElement('div'); const elem: HTMLElement = document.createElement('div');
const childrenMap: TreeNode[] = node.children.map((child) => Fs.getNodeByINode(child)); const childrenMap: TreeNode[] = node.children.map((child) => Fs.getNodeByINode(child));
@@ -121,11 +134,8 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
const current: TreeNode = node; const current: TreeNode = node;
current.name = '.'; current.name = '.';
let parent: TreeNode = current; const parent: TreeNode = Fs.getNodeByINode(node.parent);
if(node.parent) {
parent = Fs.getNodeByINode(node.parent);
parent.name = '..'; parent.name = '..';
}
children.unshift(current, parent); children.unshift(current, parent);
} }
@@ -153,8 +163,6 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
if (f_l || f_g || f_o) { if (f_l || f_g || f_o) {
const rows: string[] = []; const rows: string[] = [];
for (const node of nodes) {
const maxSizeWidth = Math.max( const maxSizeWidth = Math.max(
...children.map((child) => child.size.toString().length)); ...children.map((child) => child.size.toString().length));
@@ -178,10 +186,10 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
rows.unshift('total ' + node.children.length.toString()); rows.unshift('total ' + node.children.length.toString());
if (nodes.length > 1) { if (nodes.length > 1) {
const nodePath: string = const nodePath: HTMLElement = document.createElement('p');
node.name === '/' ? '/:' : `${Fs.getPathByInode(node.inode).slice(1)}:`; nodePath.innerText = `${Fs.getPathByInode(node.inode)}:`;
rows.unshift(nodePath); w.appendChild(nodePath);
rows.push('\n'); if(nodeIndex +1 < nodes.length) elem.style.marginBottom = `${this.getTerminalFontSize() * 2}px`;
} }
for (let i = 0; i < rows.length; i++) { for (let i = 0; i < rows.length; i++) {
@@ -189,12 +197,9 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
p.innerText = rows[i]; p.innerText = rows[i];
elem.appendChild(p); elem.appendChild(p);
} }
w.appendChild(elem); w.appendChild(elem);
} }
else {
return w;
}
const maxWidth: number = Math.ceil((this.getTerminalWidth() / this.getTerminalFontSize()) * 0.8); const maxWidth: number = Math.ceil((this.getTerminalWidth() / this.getTerminalFontSize()) * 0.8);
let columns: string[][] = []; let columns: string[][] = [];
@@ -227,8 +232,10 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
} }
const wrapper: HTMLElement = document.createElement('div'); const wrapper: HTMLElement = document.createElement('div');
wrapper.style.display = 'flex'; wrapper.style.display = 'flex';
wrapper.style.columnGap = `${this.getTerminalFontSize() * 2}px`; wrapper.style.columnGap = `${this.getTerminalFontSize() * 2}px`;
wrapper.style.marginBottom = `${this.getTerminalFontSize() * 2}px`;
let fileIndex = 0; let fileIndex = 0;
@@ -250,12 +257,14 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
if (nodes.length > 1) { if (nodes.length > 1) {
const nodePath: HTMLElement = document.createElement('p'); const nodePath: HTMLElement = document.createElement('p');
nodePath.innerText = node.name === '/' ? '/:' : `${Fs.getPathByInode(node.inode).slice(1)}:`; nodePath.innerText = `${Fs.getPathByInode(node.inode)}`;
w.appendChild(nodePath); w.appendChild(nodePath);
} }
w.appendChild(wrapper); w.appendChild(wrapper);
} }
nodeIndex++;
}
return w; return w;
} }
@@ -350,11 +359,11 @@ function formatModtime(node: TreeNode, sortBy: SortNodeBy.ATIME | SortNodeBy.CTI
} }
function formatName(node: TreeNode, flag: any, shouldShift: boolean) { function formatName(node: TreeNode, flag: any, shouldShift: boolean) {
let name: string; let name: string = node.name;
const char: string = flag.has('Q') ? '"' : "'"; const char: string = flag.has('Q') ? '"' : "'";
if (/\s/.test(node.name)) { if (/\s/.test(node.name)) {
name = `${char}${node.name}${char}` name = `${char}${name}${char}`
} else { } else {
//Shift non quoted names 1 char right to align if any names in group have a quote //Shift non quoted names 1 char right to align if any names in group have a quote
name = `${shouldShift ? ' ' : ''}${node.name}`; name = `${shouldShift ? ' ' : ''}${node.name}`;

View File

@@ -19,7 +19,7 @@ export type FsInitArgs = {
export type TreeNode = { export type TreeNode = {
inode: number; inode: number;
parent?: number; parent: number;
name: string; name: string;
type: Type; type: Type;
size: number; //Size in Bytes size: number; //Size in Bytes
@@ -110,11 +110,11 @@ export class VirtualFS {
return typeof path === 'string' && path.startsWith('/'); return typeof path === 'string' && path.startsWith('/');
}; };
getPathByInode(inode: number): string { public getPathByInode(inode: number): string {
return this._iNodeToPathString(inode); return this._iNodeToPathString(inode);
} }
formatPath(path: string): string { public formatPath(path: string): string {
console.log(path, 'formatPath'); console.log(path, 'formatPath');
const prefix = this._iNodeToPathString(this.home); const prefix = this._iNodeToPathString(this.home);
@@ -123,7 +123,7 @@ export class VirtualFS {
} else return path; } else return path;
} }
resolvePath(path: string): TreeNode { public resolvePath(path: string): TreeNode {
if (path === '/') return this.getNodeByINode(this.rootINode); if (path === '/') return this.getNodeByINode(this.rootINode);
let parsedPath: string = path; let parsedPath: string = path;
@@ -144,22 +144,32 @@ export class VirtualFS {
return Node; return Node;
} }
getNodeByINode(inode: number): TreeNode { public getNodeByINode(inode: number): TreeNode {
const node: TreeNode | undefined = this.FsTable.get(inode); const node: TreeNode | undefined = this.FsTable.get(inode);
if (!node) throw new Error('Could not get the node, no such i node exists'); if (!node) throw new Error(`Could not get the node, no such inode exists - ${inode}`);
return node; return node;
} }
/* private _getPathToNode(node: TreeNode): string[] { public recursiveTraversalPre(node: TreeNode, callback: (param: TreeNode) => void, childIndex?: number[], depthIndex?: number): void {
const path: string[] = []; if(!depthIndex) depthIndex = 0;
let current = node; if(!childIndex) childIndex = [0];
path.push(node.name);
while (current.parent) { if(node.type != Type.File && node.children[childIndex[depthIndex]]) {
current = current.parent; node = this.getNodeByINode(node.children[childIndex[depthIndex]]);
path.unshift(current.name); depthIndex++;
if(!childIndex[depthIndex]) childIndex[depthIndex] = 0;
callback(node);
}
else {
node = this.getNodeByINode(node.parent);
childIndex[depthIndex] = 0;
depthIndex--;
childIndex[depthIndex]++;
} }
return path; if(depthIndex < 0) return;
} */
this.recursiveTraversalPre(node, callback, childIndex, depthIndex);
}
} }

View File

@@ -20,7 +20,7 @@ export class Sort {
reverse: boolean = false, reverse: boolean = false,
sortBy: SortNodeBy = SortNodeBy.NAME sortBy: SortNodeBy = SortNodeBy.NAME
): TreeNode[] { ): TreeNode[] {
if (nodes.length === 0) throw new Error('Tried to sort an empty node array!'); if (nodes.length === 0) {console.warn('Tried to sort an empty node array!'); return [];}
const parsedNodes: TreeNode[] = []; const parsedNodes: TreeNode[] = [];
if (typeof nodes[0] === 'number') { if (typeof nodes[0] === 'number') {

View File

@@ -1,6 +1,7 @@
import { Bash, ExitCode, type Group, type User } from './bash'; import { Bash, ExitCode, type Group, type User } from './bash';
import { ls } from './commands/ls'; import { ls } from './commands/ls';
import { cd } from './commands/cd'; import { cd } from './commands/cd';
import { clear } from './commands/clear';
export type ICommand = { export type ICommand = {
method: (this: Bash, args: CommandArgs) => Result; method: (this: Bash, args: CommandArgs) => Result;
@@ -76,7 +77,8 @@ export const PASSWD: User[] = [
export const COMMANDS = { export const COMMANDS = {
cd, cd,
ls ls,
clear
} as const satisfies Record<string, ICommand>; } as const satisfies Record<string, ICommand>;
/* //export const commands { /* //export const commands {

View File

@@ -8,7 +8,7 @@ export function isInitializing(): boolean {
return initializing; return initializing;
} }
function jsonToNodeTable(data: any, parent?: number): Map<number, TreeNode> { function jsonToNodeTable(data: any): Map<number, TreeNode> {
const FsTable: Map<number, TreeNode> = new Map<number, TreeNode>(); const FsTable: Map<number, TreeNode> = new Map<number, TreeNode>();
const entryList = Object.entries(data); const entryList = Object.entries(data);

View File

@@ -35,7 +35,6 @@ export function print(e: HTMLElement, data: PrintData): void {
} }
export function clear(): void { export function clear(): void {
for (const n of outputInstances) { console.log("outInstances", outputInstances);
unmount(n); unmount(outputInstances);
}
} }

View File

@@ -22,6 +22,7 @@ export type PrintData = {
export type PageCallbacks = { export type PageCallbacks = {
print: (data: PrintData) => void; print: (data: PrintData) => void;
clear: () => void;
getWidth: () => number; getWidth: () => number;
getFontSize: () => number; getFontSize: () => number;
}; };

View File

@@ -73,6 +73,7 @@
if (!e) return; if (!e) return;
printOutput(e, data); printOutput(e, data);
}, },
clear: clear,
getWidth: getWidth, getWidth: getWidth,
getFontSize: getFontSize getFontSize: getFontSize
}; };