From 1faab39849a42774d5f0ed1889918f5131d6667c Mon Sep 17 00:00:00 2001 From: Kamil Olszewski Date: Mon, 16 Feb 2026 07:38:12 +0100 Subject: [PATCH] more features, flags now mostly work, added support for -R flag in ls with a custom bash method for recursive tree traversal --- src/lib/assets/fs/fs.json | 2 +- src/lib/stores/bash/commands/clear.ts | 15 ++++ src/lib/stores/bash/commands/ls.ts | 99 ++++++++++++++------------ src/lib/stores/bash/fs.ts | 40 +++++++---- src/lib/stores/bash/sort.ts | 2 +- src/lib/stores/bash/static.ts | 4 +- src/lib/stores/terminal/init.svelte.ts | 2 +- src/lib/stores/terminal/stdio.ts | 5 +- src/lib/stores/terminal/terminal.ts | 1 + src/modules/Terminal.svelte | 1 + 10 files changed, 104 insertions(+), 67 deletions(-) create mode 100644 src/lib/stores/bash/commands/clear.ts diff --git a/src/lib/assets/fs/fs.json b/src/lib/assets/fs/fs.json index 59e4714..95ed8b6 100755 --- a/src/lib/assets/fs/fs.json +++ b/src/lib/assets/fs/fs.json @@ -33,7 +33,7 @@ }, "Interactible": false, "Func": null, - "Parent": null + "Parent": 1 }, "2": { "Inode": 2, diff --git a/src/lib/stores/bash/commands/clear.ts b/src/lib/stores/bash/commands/clear.ts new file mode 100644 index 0000000..3b04632 --- /dev/null +++ b/src/lib/stores/bash/commands/clear.ts @@ -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 +}; \ No newline at end of file diff --git a/src/lib/stores/bash/commands/ls.ts b/src/lib/stores/bash/commands/ls.ts index 2662907..e3e60e0 100755 --- a/src/lib/stores/bash/commands/ls.ts +++ b/src/lib/stores/bash/commands/ls.ts @@ -76,7 +76,8 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement { 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('almost-all'); @@ -105,6 +106,18 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement { 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) { const elem: HTMLElement = document.createElement('div'); 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; current.name = '.'; - let parent: TreeNode = current; - if(node.parent) { - parent = Fs.getNodeByINode(node.parent); - parent.name = '..'; - } + const parent: TreeNode = Fs.getNodeByINode(node.parent); + parent.name = '..'; children.unshift(current, parent); } @@ -153,48 +163,43 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement { if (f_l || f_g || f_o) { const rows: string[] = []; + const maxSizeWidth = Math.max( + ...children.map((child) => child.size.toString().length)); - for (const node of nodes) { - const maxSizeWidth = Math.max( - ...children.map((child) => child.size.toString().length)); + for (const child of children) { + const entry: LsEntry = { + inode: null, + perms: formatPermission(child), + children: formatChildren(child), + owners: formatOwners.call(this, child, flagInfo), + size: formatSize.call(this, f_h, child, maxSizeWidth, f_si), + modt: formatModtime(child, timestamp), + name: formatName(child, flagInfo, shouldNamesShift) + }; - for (const child of children) { - const entry: LsEntry = { - inode: null, - perms: formatPermission(child), - children: formatChildren(child), - owners: formatOwners.call(this, child, flagInfo), - size: formatSize.call(this, f_h, child, maxSizeWidth, f_si), - modt: formatModtime(child, timestamp), - name: formatName(child, flagInfo, shouldNamesShift) - }; + if (f_i) entry.inode = child.inode; - if (f_i) entry.inode = child.inode; - - rows.push(LsEntryUtils.toString(entry)); - } - - //TODO: Calculate the total size of contents in the node - rows.unshift('total ' + node.children.length.toString()); - - if (nodes.length > 1) { - const nodePath: string = - node.name === '/' ? '/:' : `${Fs.getPathByInode(node.inode).slice(1)}:`; - rows.unshift(nodePath); - rows.push('\n'); - } - - for (let i = 0; i < rows.length; i++) { - const p: HTMLElement = document.createElement('p'); - p.innerText = rows[i]; - elem.appendChild(p); - } - - w.appendChild(elem); + rows.push(LsEntryUtils.toString(entry)); } - return w; + //TODO: Calculate the total size of contents in the node + rows.unshift('total ' + node.children.length.toString()); + + if (nodes.length > 1) { + const nodePath: HTMLElement = document.createElement('p'); + nodePath.innerText = `${Fs.getPathByInode(node.inode)}:`; + w.appendChild(nodePath); + if(nodeIndex +1 < nodes.length) elem.style.marginBottom = `${this.getTerminalFontSize() * 2}px`; + } + + for (let i = 0; i < rows.length; i++) { + const p: HTMLElement = document.createElement('p'); + p.innerText = rows[i]; + elem.appendChild(p); + } + w.appendChild(elem); } + else { const maxWidth: number = Math.ceil((this.getTerminalWidth() / this.getTerminalFontSize()) * 0.8); let columns: string[][] = []; @@ -227,8 +232,10 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement { } const wrapper: HTMLElement = document.createElement('div'); + wrapper.style.display = 'flex'; wrapper.style.columnGap = `${this.getTerminalFontSize() * 2}px`; + wrapper.style.marginBottom = `${this.getTerminalFontSize() * 2}px`; let fileIndex = 0; @@ -250,11 +257,13 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement { if (nodes.length > 1) { 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(wrapper); + } + nodeIndex++; } return w; } @@ -350,11 +359,11 @@ function formatModtime(node: TreeNode, sortBy: SortNodeBy.ATIME | SortNodeBy.CTI } function formatName(node: TreeNode, flag: any, shouldShift: boolean) { - let name: string; + let name: string = node.name; const char: string = flag.has('Q') ? '"' : "'"; if (/\s/.test(node.name)) { - name = `${char}${node.name}${char}` + name = `${char}${name}${char}` } else { //Shift non quoted names 1 char right to align if any names in group have a quote name = `${shouldShift ? ' ' : ''}${node.name}`; diff --git a/src/lib/stores/bash/fs.ts b/src/lib/stores/bash/fs.ts index 71f7d44..e3290a9 100755 --- a/src/lib/stores/bash/fs.ts +++ b/src/lib/stores/bash/fs.ts @@ -19,7 +19,7 @@ export type FsInitArgs = { export type TreeNode = { inode: number; - parent?: number; + parent: number; name: string; type: Type; size: number; //Size in Bytes @@ -110,11 +110,11 @@ export class VirtualFS { return typeof path === 'string' && path.startsWith('/'); }; - getPathByInode(inode: number): string { + public getPathByInode(inode: number): string { return this._iNodeToPathString(inode); } - formatPath(path: string): string { + public formatPath(path: string): string { console.log(path, 'formatPath'); const prefix = this._iNodeToPathString(this.home); @@ -123,7 +123,7 @@ export class VirtualFS { } else return path; } - resolvePath(path: string): TreeNode { + public resolvePath(path: string): TreeNode { if (path === '/') return this.getNodeByINode(this.rootINode); let parsedPath: string = path; @@ -144,22 +144,32 @@ export class VirtualFS { return Node; } - getNodeByINode(inode: number): TreeNode { + public getNodeByINode(inode: number): TreeNode { 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; } - /* private _getPathToNode(node: TreeNode): string[] { - const path: string[] = []; - let current = node; - path.push(node.name); + public recursiveTraversalPre(node: TreeNode, callback: (param: TreeNode) => void, childIndex?: number[], depthIndex?: number): void { + if(!depthIndex) depthIndex = 0; + if(!childIndex) childIndex = [0]; - while (current.parent) { - current = current.parent; - path.unshift(current.name); + if(node.type != Type.File && node.children[childIndex[depthIndex]]) { + node = this.getNodeByINode(node.children[childIndex[depthIndex]]); + 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); + } } diff --git a/src/lib/stores/bash/sort.ts b/src/lib/stores/bash/sort.ts index 2b8d7b2..7647f5e 100755 --- a/src/lib/stores/bash/sort.ts +++ b/src/lib/stores/bash/sort.ts @@ -20,7 +20,7 @@ export class Sort { reverse: boolean = false, sortBy: SortNodeBy = SortNodeBy.NAME ): 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[] = []; if (typeof nodes[0] === 'number') { diff --git a/src/lib/stores/bash/static.ts b/src/lib/stores/bash/static.ts index de724ce..9ebb505 100755 --- a/src/lib/stores/bash/static.ts +++ b/src/lib/stores/bash/static.ts @@ -1,6 +1,7 @@ import { Bash, ExitCode, type Group, type User } from './bash'; import { ls } from './commands/ls'; import { cd } from './commands/cd'; +import { clear } from './commands/clear'; export type ICommand = { method: (this: Bash, args: CommandArgs) => Result; @@ -76,7 +77,8 @@ export const PASSWD: User[] = [ export const COMMANDS = { cd, - ls + ls, + clear } as const satisfies Record; /* //export const commands { diff --git a/src/lib/stores/terminal/init.svelte.ts b/src/lib/stores/terminal/init.svelte.ts index 1174312..2cf2317 100755 --- a/src/lib/stores/terminal/init.svelte.ts +++ b/src/lib/stores/terminal/init.svelte.ts @@ -8,7 +8,7 @@ export function isInitializing(): boolean { return initializing; } -function jsonToNodeTable(data: any, parent?: number): Map { +function jsonToNodeTable(data: any): Map { const FsTable: Map = new Map(); const entryList = Object.entries(data); diff --git a/src/lib/stores/terminal/stdio.ts b/src/lib/stores/terminal/stdio.ts index ca8e6af..59cf097 100755 --- a/src/lib/stores/terminal/stdio.ts +++ b/src/lib/stores/terminal/stdio.ts @@ -35,7 +35,6 @@ export function print(e: HTMLElement, data: PrintData): void { } export function clear(): void { - for (const n of outputInstances) { - unmount(n); - } + console.log("outInstances", outputInstances); + unmount(outputInstances); } diff --git a/src/lib/stores/terminal/terminal.ts b/src/lib/stores/terminal/terminal.ts index 25f456c..e03b47c 100755 --- a/src/lib/stores/terminal/terminal.ts +++ b/src/lib/stores/terminal/terminal.ts @@ -22,6 +22,7 @@ export type PrintData = { export type PageCallbacks = { print: (data: PrintData) => void; + clear: () => void; getWidth: () => number; getFontSize: () => number; }; diff --git a/src/modules/Terminal.svelte b/src/modules/Terminal.svelte index 2c4772e..fbe15b3 100755 --- a/src/modules/Terminal.svelte +++ b/src/modules/Terminal.svelte @@ -73,6 +73,7 @@ if (!e) return; printOutput(e, data); }, + clear: clear, getWidth: getWidth, getFontSize: getFontSize };