From 3798a533952df10483096c8557134cb579287614 Mon Sep 17 00:00:00 2001 From: binekrasik Date: Fri, 22 May 2026 23:27:04 +0200 Subject: [PATCH] chore: rewrite loadprg & fix some issues --- src/fs/Item.ts | 2 +- src/program/Cat.ts | 17 +++++++----- src/program/Edit.ts | 4 +-- src/program/Eval.ts | 16 ++++++++++- src/program/Loadprg.ts | 62 ++++++++++++++++++++++++++++++++++-------- src/shell/wush/Wush.ts | 11 ++++++-- 6 files changed, 87 insertions(+), 25 deletions(-) diff --git a/src/fs/Item.ts b/src/fs/Item.ts index 80dfc2b..b8d4913 100644 --- a/src/fs/Item.ts +++ b/src/fs/Item.ts @@ -172,7 +172,7 @@ export class Item { return this.path === '/' ? '/' : this.path.substring(this.path.lastIndexOf('/') + 1) } - ReadData(): string | null { + GetData(): string | null { return this.data } diff --git a/src/program/Cat.ts b/src/program/Cat.ts index cce5c39..76e4334 100644 --- a/src/program/Cat.ts +++ b/src/program/Cat.ts @@ -9,20 +9,23 @@ export class Cat extends Program { return 1 } - const item = await Item.open(Item.NormalizePath(args[1].startsWith('/') ? args[1] : `${workdir.GetPath()}/${args[1]}`)) + const path = Item.NormalizePath(args[1].startsWith('/') ? args[1] : `${workdir.GetPath()}/${args[1]}`) + let item: Item - if (!(await item.Exists())) { - stdout.emit(`cat: error: item ${item.GetPath()} doesn't exist.\n`) + try { + item = await Item.open(path) + } catch (err) { + stdout.emit(`cat: error: item ${path} is a directory\n`) return 2 } - if (item.IsDirectory()) { - stdout.emit(`cat: error: can't read data from a directory; item ${item.GetPath()} is a directory.\n`) + if (!(await item.Exists())) { + stdout.emit(`cat: error: item ${item.GetPath()} doesn't exist.\n`) return 3 } - stdout.emit(`${item.ReadData()}`) - stdout.emit('<- EOF\n') + stdout.emit(`${item.GetData()}`) + stdout.emit('\x1B[0;30m\x1B[47m EOF \x1B[0m\n') return 0 } diff --git a/src/program/Edit.ts b/src/program/Edit.ts index 221070f..92037d0 100644 --- a/src/program/Edit.ts +++ b/src/program/Edit.ts @@ -32,7 +32,7 @@ export class Edit extends Program { let filePath = resolvePath(args[1]) let file = await Item.open(filePath, '') let fileExists = await file.Exists() - let lines = this.normalizeLines(file.ReadData()) + let lines = this.normalizeLines(file.GetData()) let dirty = false let cursorLine = 0 @@ -93,7 +93,7 @@ export class Edit extends Program { const resolved = resolvePath(path) const nextFile = await Item.open(resolved, '') const exists = await nextFile.Exists() - const content = nextFile.ReadData() + const content = nextFile.GetData() filePath = resolved file = nextFile diff --git a/src/program/Eval.ts b/src/program/Eval.ts index 81470c8..4694f56 100644 --- a/src/program/Eval.ts +++ b/src/program/Eval.ts @@ -4,7 +4,21 @@ import { Program } from './Program' export class Eval extends Program { async Exec(stdin: SimpleStream, stdout: SimpleStream, _workdir: Item, args: string[]): Promise { - let javascript: string = args.slice(1).join(' ') + const inlineArgs = args.slice(1) + const source = inlineArgs.length > 0 + ? inlineArgs.join(' ') + : await stdin.wait() + + if (source.trim().length === 0) { + stdout.emit('eval: error: missing javascript input\n') + return 1 + } + + try { + this.RunJs(source, stdout) + } catch { + return 1 + } return 0 } diff --git a/src/program/Loadprg.ts b/src/program/Loadprg.ts index 8900fe1..66f7475 100644 --- a/src/program/Loadprg.ts +++ b/src/program/Loadprg.ts @@ -1,4 +1,4 @@ -import type { Item } from '../fs/Item' +import { Item } from '../fs/Item' import type { Shell } from '../shell/Shell' import type { SimpleStream } from '../utils/SimpleStream' import { Program } from './Program' @@ -11,21 +11,59 @@ export class Loadprg extends Program { this.shell = shell } - async Exec(stdin: SimpleStream, stdout: SimpleStream, __: Item, args: string[]): Promise { - const javascript = args.slice(2).join(' ') - - stdin.on(data => stdout.emit(`loadprg stdin: ${data}\x1B[0;30m\x1B[47m<- EOF\x1B[0m\n`)) + async Exec(_: SimpleStream, stdout: SimpleStream, workdir: Item, args: string[]): Promise { + const path = Item.NormalizePath(args[1].startsWith('/') ? args[1] : `${workdir.GetPath()}/${args[1]}`) + let file: Item try { - const program = eval(javascript) - - this.shell.LoadProgram(new program(), args[1]) - } catch (e) { - stdout.emit(`${String(e)}\n`) - stdout.emit(`${String((e as Error).stack)}\n`) - return 1 + file = await Item.open(path) + } catch (err) { + stdout.emit(`loadprg: error: item ${path} is a directory\n`) + return 2 } + if (!(await file.Exists())) { + stdout.emit(`loadprg: error: item ${file.GetPath()} doesn't exist.\n`) + return 3 + } + + let programName: string | null = null + + // check if we have a name argument + // use the file name if the program name wasn't explicitly specified + const nameArgIndex = args.findIndex(value => value === "--name") + if (nameArgIndex !== -1 && args[nameArgIndex + 1]) { + programName = args[nameArgIndex + 1] + } else programName = file.GetName() + + // read the file + const javascript = file.GetData() + if (!javascript) { + stdout.emit(`loadprg: error: could not read the program data\n`) + return 3 + } + + // load the program + type EntrypointFunction = InstanceType['Exec'] + let programEntrypoint: EntrypointFunction + + try { + // due to some inconveniences and limitations, + // we only allow for loading the program main method + // instead of an entire program class. + programEntrypoint = new Function("stdin", "stdout", "workdir", "args", javascript) as EntrypointFunction + } catch (err) { + stdout.emit(`loadprg: error: could not load the program: ${err}\n`) + stdout.emit(`-> Stacktrace: ${(err as Error).stack}\n`) + return 4 + } + + // create a wrapper class for the program + const wrapper: Program = new (class ProgramWrapper extends Program { Exec = programEntrypoint }) + + this.shell.LoadProgram(wrapper, programName) + stdout.emit(`-> Loaded program from ${file.GetPath()} as ${programName}\n`) + return 0 } } diff --git a/src/shell/wush/Wush.ts b/src/shell/wush/Wush.ts index fab3e62..4229138 100644 --- a/src/shell/wush/Wush.ts +++ b/src/shell/wush/Wush.ts @@ -29,6 +29,8 @@ import { Environment } from './Environment' import { InputManager } from './InputManager' import { Edit } from '../../program/Edit' import { Mv } from '../../program/Mv' +import { Tree } from '../../program/Tree' +import { Cp } from '../../program/Cp' export class Wush extends Shell { public readonly Version = "0.3.2" @@ -96,6 +98,8 @@ export class Wush extends Shell { this.programs['printf'] = new Printf() this.programs['edit'] = new Edit() this.programs['mv'] = new Mv() + this.programs['cp'] = new Cp() + this.programs['tree'] = new Tree() // reset exit code this.SetExitCode(0) @@ -685,7 +689,7 @@ export class Wush extends Shell { if (item.IsDirectory()) throw new Error(`input file ${resolved} is a directory`) - return { stream: new SimpleStream(), data: item.ReadData() ?? '' } + return { stream: new SimpleStream(), data: item.GetData() ?? '' } } const openOutputRedirect = async (path: string, append: boolean) => { @@ -894,11 +898,14 @@ export class Wush extends Shell { this.terminal.NewPage() return true } + case 'm': { + this.terminal.ApplyCellStyleCodes(values) + return true + } default: return false } } - private GetWrapWidth(): number { const width = Math.floor(this.terminal.GetWidthCells()) if (!Number.isFinite(width) || width <= 0)