@ecmaos/coreutils 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +32 -0
- package/dist/commands/cat.d.ts.map +1 -1
- package/dist/commands/cat.js +23 -4
- package/dist/commands/cat.js.map +1 -1
- package/dist/commands/echo.d.ts.map +1 -1
- package/dist/commands/echo.js +5 -2
- package/dist/commands/echo.js.map +1 -1
- package/dist/commands/grep.d.ts +4 -0
- package/dist/commands/grep.d.ts.map +1 -0
- package/dist/commands/grep.js +140 -0
- package/dist/commands/grep.js.map +1 -0
- package/dist/commands/head.d.ts +4 -0
- package/dist/commands/head.d.ts.map +1 -0
- package/dist/commands/head.js +162 -0
- package/dist/commands/head.js.map +1 -0
- package/dist/commands/hex.d.ts.map +1 -1
- package/dist/commands/hex.js +69 -17
- package/dist/commands/hex.js.map +1 -1
- package/dist/commands/ls.d.ts.map +1 -1
- package/dist/commands/ls.js +5 -3
- package/dist/commands/ls.js.map +1 -1
- package/dist/commands/passkey.d.ts +4 -0
- package/dist/commands/passkey.d.ts.map +1 -0
- package/dist/commands/passkey.js +184 -0
- package/dist/commands/passkey.js.map +1 -0
- package/dist/commands/tail.d.ts +4 -0
- package/dist/commands/tail.d.ts.map +1 -0
- package/dist/commands/tail.js +152 -0
- package/dist/commands/tail.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/commands/cat.ts +25 -4
- package/src/commands/echo.ts +5 -2
- package/src/commands/grep.ts +154 -0
- package/src/commands/head.ts +170 -0
- package/src/commands/hex.ts +76 -20
- package/src/commands/ls.ts +4 -2
- package/src/commands/passkey.ts +208 -0
- package/src/commands/tail.ts +164 -0
- package/src/index.ts +10 -0
package/dist/index.js
CHANGED
|
@@ -7,6 +7,8 @@ import { createCommand as createCd } from './commands/cd.js';
|
|
|
7
7
|
import { createCommand as createChmod } from './commands/chmod.js';
|
|
8
8
|
import { createCommand as createCp } from './commands/cp.js';
|
|
9
9
|
import { createCommand as createEcho } from './commands/echo.js';
|
|
10
|
+
import { createCommand as createGrep } from './commands/grep.js';
|
|
11
|
+
import { createCommand as createHead } from './commands/head.js';
|
|
10
12
|
import { createCommand as createLn } from './commands/ln.js';
|
|
11
13
|
import { createCommand as createLs } from './commands/ls.js';
|
|
12
14
|
import { createCommand as createMkdir } from './commands/mkdir.js';
|
|
@@ -18,14 +20,17 @@ import { createCommand as createStat } from './commands/stat.js';
|
|
|
18
20
|
import { createCommand as createTouch } from './commands/touch.js';
|
|
19
21
|
import { createCommand as createHex } from './commands/hex.js';
|
|
20
22
|
import { createCommand as createLess } from './commands/less.js';
|
|
23
|
+
import { createCommand as createPasskey } from './commands/passkey.js';
|
|
21
24
|
import { createCommand as createSed } from './commands/sed.js';
|
|
22
25
|
import { createCommand as createTee } from './commands/tee.js';
|
|
26
|
+
import { createCommand as createTail } from './commands/tail.js';
|
|
23
27
|
// Export individual command factories
|
|
24
28
|
export { createCommand as createCat } from './commands/cat.js';
|
|
25
29
|
export { createCommand as createCd } from './commands/cd.js';
|
|
26
30
|
export { createCommand as createChmod } from './commands/chmod.js';
|
|
27
31
|
export { createCommand as createCp } from './commands/cp.js';
|
|
28
32
|
export { createCommand as createEcho } from './commands/echo.js';
|
|
33
|
+
export { createCommand as createGrep } from './commands/grep.js';
|
|
29
34
|
export { createCommand as createLn } from './commands/ln.js';
|
|
30
35
|
export { createCommand as createLs } from './commands/ls.js';
|
|
31
36
|
export { createCommand as createMkdir } from './commands/mkdir.js';
|
|
@@ -39,6 +44,7 @@ export { createCommand as createHex } from './commands/hex.js';
|
|
|
39
44
|
export { createCommand as createLess } from './commands/less.js';
|
|
40
45
|
export { createCommand as createSed } from './commands/sed.js';
|
|
41
46
|
export { createCommand as createTee } from './commands/tee.js';
|
|
47
|
+
export { createCommand as createTail } from './commands/tail.js';
|
|
42
48
|
/**
|
|
43
49
|
* Creates all coreutils commands.
|
|
44
50
|
* This function replaces the TerminalCommands function from the kernel.
|
|
@@ -50,6 +56,8 @@ export function createAllCommands(kernel, shell, terminal) {
|
|
|
50
56
|
chmod: createChmod(kernel, shell, terminal),
|
|
51
57
|
cp: createCp(kernel, shell, terminal),
|
|
52
58
|
echo: createEcho(kernel, shell, terminal),
|
|
59
|
+
grep: createGrep(kernel, shell, terminal),
|
|
60
|
+
head: createHead(kernel, shell, terminal),
|
|
53
61
|
ln: createLn(kernel, shell, terminal),
|
|
54
62
|
ls: createLs(kernel, shell, terminal),
|
|
55
63
|
mkdir: createMkdir(kernel, shell, terminal),
|
|
@@ -61,7 +69,9 @@ export function createAllCommands(kernel, shell, terminal) {
|
|
|
61
69
|
touch: createTouch(kernel, shell, terminal),
|
|
62
70
|
hex: createHex(kernel, shell, terminal),
|
|
63
71
|
less: createLess(kernel, shell, terminal),
|
|
72
|
+
passkey: createPasskey(kernel, shell, terminal),
|
|
64
73
|
sed: createSed(kernel, shell, terminal),
|
|
74
|
+
tail: createTail(kernel, shell, terminal),
|
|
65
75
|
tee: createTee(kernel, shell, terminal)
|
|
66
76
|
};
|
|
67
77
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,+BAA+B;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAE9D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAE5F,2BAA2B;AAC3B,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,aAAa,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecmaos/coreutils",
|
|
3
3
|
"description": "ecmaOS: Core utilities for the ecmaOS framework",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.5",
|
|
5
5
|
"license": "MIT+Apache-2.0",
|
|
6
6
|
"homepage": "https://ecmaos.sh",
|
|
7
7
|
"repository": "https://github.com/ecmaos/ecmaos",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"command-line-args": "^6.0.0",
|
|
30
30
|
"command-line-usage": "^7.0.3",
|
|
31
31
|
"human-format": "^1.2.1",
|
|
32
|
-
"@ecmaos/types": "^0.4.
|
|
32
|
+
"@ecmaos/types": "^0.4.4"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/ansi-escape-sequences": "^4.0.4",
|
package/src/commands/cat.ts
CHANGED
|
@@ -3,7 +3,6 @@ import type { CommandLineOptions } from 'command-line-args'
|
|
|
3
3
|
import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
4
4
|
import { TerminalEvents } from '@ecmaos/types'
|
|
5
5
|
import { TerminalCommand } from '../shared/terminal-command.js'
|
|
6
|
-
import { writeStdout } from '../shared/helpers.js'
|
|
7
6
|
|
|
8
7
|
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
9
8
|
return new TerminalCommand({
|
|
@@ -22,6 +21,8 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
22
21
|
|
|
23
22
|
// Get a single writer for the entire operation
|
|
24
23
|
const writer = process.stdout.getWriter()
|
|
24
|
+
const isTTY = process.stdoutIsTTY ?? false
|
|
25
|
+
let lastByte: number | undefined
|
|
25
26
|
|
|
26
27
|
try {
|
|
27
28
|
// If no files specified, read from stdin
|
|
@@ -32,12 +33,20 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
32
33
|
while (true) {
|
|
33
34
|
const { done, value } = await reader.read()
|
|
34
35
|
if (done) break
|
|
36
|
+
if (value.length > 0) {
|
|
37
|
+
lastByte = value[value.length - 1]
|
|
38
|
+
}
|
|
35
39
|
await writer.write(value)
|
|
36
40
|
}
|
|
37
41
|
} finally {
|
|
38
42
|
reader.releaseLock()
|
|
39
43
|
}
|
|
40
44
|
|
|
45
|
+
// Add newline at end if outputting to terminal and last byte wasn't newline
|
|
46
|
+
if (isTTY && lastByte !== undefined && lastByte !== 0x0A) {
|
|
47
|
+
await writer.write(new Uint8Array([0x0A]))
|
|
48
|
+
}
|
|
49
|
+
|
|
41
50
|
return 0
|
|
42
51
|
}
|
|
43
52
|
|
|
@@ -64,7 +73,11 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
64
73
|
const data = new Uint8Array(chunkSize)
|
|
65
74
|
const readSize = Math.min(chunkSize, stat.size - bytesRead)
|
|
66
75
|
await handle.read(data, 0, readSize, bytesRead)
|
|
67
|
-
|
|
76
|
+
const chunk = data.subarray(0, readSize)
|
|
77
|
+
if (chunk.length > 0) {
|
|
78
|
+
lastByte = chunk[chunk.length - 1]
|
|
79
|
+
}
|
|
80
|
+
await writer.write(chunk)
|
|
68
81
|
bytesRead += readSize
|
|
69
82
|
}
|
|
70
83
|
} else {
|
|
@@ -82,7 +95,11 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
82
95
|
if (bytesRead > 0) {
|
|
83
96
|
const bytesToWrite = maxBytes ? Math.min(bytesRead, maxBytes - totalBytesRead) : bytesRead
|
|
84
97
|
if (bytesToWrite > 0) {
|
|
85
|
-
|
|
98
|
+
const chunk = data.subarray(0, bytesToWrite)
|
|
99
|
+
if (chunk.length > 0) {
|
|
100
|
+
lastByte = chunk[chunk.length - 1]
|
|
101
|
+
}
|
|
102
|
+
await writer.write(chunk)
|
|
86
103
|
totalBytesRead += bytesToWrite
|
|
87
104
|
}
|
|
88
105
|
}
|
|
@@ -93,10 +110,14 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
93
110
|
}
|
|
94
111
|
}
|
|
95
112
|
|
|
113
|
+
// Add newline at end if outputting to terminal and last byte wasn't newline
|
|
114
|
+
if (isTTY && lastByte !== undefined && lastByte !== 0x0A) {
|
|
115
|
+
await writer.write(new Uint8Array([0x0A]))
|
|
116
|
+
}
|
|
117
|
+
|
|
96
118
|
return 0
|
|
97
119
|
} finally {
|
|
98
120
|
writer.releaseLock()
|
|
99
|
-
await writeStdout(process, terminal, '\n')
|
|
100
121
|
}
|
|
101
122
|
}
|
|
102
123
|
})
|
package/src/commands/echo.ts
CHANGED
|
@@ -11,11 +11,14 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
11
11
|
terminal,
|
|
12
12
|
options: [
|
|
13
13
|
{ name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
|
|
14
|
+
{ name: 'n', type: Boolean, alias: 'n', description: 'Do not output the trailing newline' },
|
|
14
15
|
{ name: 'text', type: String, typeLabel: '{underline text}', defaultOption: true, multiple: true, description: 'The text to print' }
|
|
15
16
|
],
|
|
16
17
|
run: async (argv: CommandLineOptions, process?: Process) => {
|
|
18
|
+
const noNewline = (argv.n as boolean) || false
|
|
17
19
|
const text = ((argv.text as string[]) || []).join(' ')
|
|
18
|
-
const
|
|
20
|
+
const output = noNewline ? text : text + '\n'
|
|
21
|
+
const data = new TextEncoder().encode(output)
|
|
19
22
|
|
|
20
23
|
if (process) {
|
|
21
24
|
const writer = process.stdout.getWriter()
|
|
@@ -25,7 +28,7 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
25
28
|
writer.releaseLock()
|
|
26
29
|
}
|
|
27
30
|
} else {
|
|
28
|
-
terminal.write(
|
|
31
|
+
terminal.write(output)
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
return 0
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import type { CommandLineOptions } from 'command-line-args'
|
|
3
|
+
import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
4
|
+
import { TerminalEvents } from '@ecmaos/types'
|
|
5
|
+
import { TerminalCommand } from '../shared/terminal-command.js'
|
|
6
|
+
import { writelnStderr } from '../shared/helpers.js'
|
|
7
|
+
|
|
8
|
+
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
9
|
+
return new TerminalCommand({
|
|
10
|
+
command: 'grep',
|
|
11
|
+
description: 'Search for patterns in files or standard input',
|
|
12
|
+
kernel,
|
|
13
|
+
shell,
|
|
14
|
+
terminal,
|
|
15
|
+
options: [
|
|
16
|
+
{ name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
|
|
17
|
+
{ name: 'ignore-case', type: Boolean, alias: 'i', description: 'Ignore case distinctions' },
|
|
18
|
+
{ name: 'line-number', type: Boolean, alias: 'n', description: 'Print line number with output lines' },
|
|
19
|
+
{ name: 'args', type: String, defaultOption: true, multiple: true, description: 'Pattern and file(s) to search' }
|
|
20
|
+
],
|
|
21
|
+
run: async (argv: CommandLineOptions, process?: Process) => {
|
|
22
|
+
if (!process) return 1
|
|
23
|
+
|
|
24
|
+
const args = (argv.args as string[]) || []
|
|
25
|
+
if (args.length === 0 || !args[0]) {
|
|
26
|
+
await writelnStderr(process, terminal, 'grep: pattern is required')
|
|
27
|
+
return 1
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const pattern = args[0]
|
|
31
|
+
const files = args.slice(1)
|
|
32
|
+
const ignoreCase = (argv['ignore-case'] as boolean) || false
|
|
33
|
+
const showLineNumbers = (argv['line-number'] as boolean) || false
|
|
34
|
+
|
|
35
|
+
const flags = ignoreCase ? 'i' : ''
|
|
36
|
+
let regex: RegExp
|
|
37
|
+
try {
|
|
38
|
+
regex = new RegExp(pattern, flags)
|
|
39
|
+
} catch (error) {
|
|
40
|
+
await writelnStderr(process, terminal, `grep: invalid pattern: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
41
|
+
return 1
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const writer = process.stdout.getWriter()
|
|
45
|
+
let exitCode = 0
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
if (files.length === 0) {
|
|
49
|
+
if (!process.stdin) {
|
|
50
|
+
await writelnStderr(process, terminal, 'grep: No input provided')
|
|
51
|
+
return 1
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const reader = process.stdin.getReader()
|
|
55
|
+
let currentLineNumber = 1
|
|
56
|
+
let buffer = ''
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
while (true) {
|
|
60
|
+
const { done, value } = await reader.read()
|
|
61
|
+
if (done) break
|
|
62
|
+
|
|
63
|
+
const chunk = new TextDecoder().decode(value, { stream: true })
|
|
64
|
+
buffer += chunk
|
|
65
|
+
|
|
66
|
+
const lines = buffer.split('\n')
|
|
67
|
+
buffer = lines.pop() || ''
|
|
68
|
+
|
|
69
|
+
for (const line of lines) {
|
|
70
|
+
if (regex.test(line)) {
|
|
71
|
+
const output = showLineNumbers ? `${currentLineNumber}:${line}\n` : `${line}\n`
|
|
72
|
+
await writer.write(new TextEncoder().encode(output))
|
|
73
|
+
}
|
|
74
|
+
currentLineNumber++
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (buffer && regex.test(buffer)) {
|
|
79
|
+
const output = showLineNumbers ? `${currentLineNumber}:${buffer}\n` : `${buffer}\n`
|
|
80
|
+
await writer.write(new TextEncoder().encode(output))
|
|
81
|
+
}
|
|
82
|
+
} finally {
|
|
83
|
+
reader.releaseLock()
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
for (const file of files) {
|
|
87
|
+
const fullPath = path.resolve(shell.cwd, file)
|
|
88
|
+
|
|
89
|
+
let interrupted = false
|
|
90
|
+
const interruptHandler = () => { interrupted = true }
|
|
91
|
+
kernel.terminal.events.on(TerminalEvents.INTERRUPT, interruptHandler)
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
if (fullPath.startsWith('/dev')) {
|
|
95
|
+
await writelnStderr(process, terminal, `grep: ${file}: cannot search device files`)
|
|
96
|
+
exitCode = 1
|
|
97
|
+
continue
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const handle = await shell.context.fs.promises.open(fullPath, 'r')
|
|
101
|
+
const stat = await shell.context.fs.promises.stat(fullPath)
|
|
102
|
+
|
|
103
|
+
let bytesRead = 0
|
|
104
|
+
const chunkSize = 1024
|
|
105
|
+
let buffer = ''
|
|
106
|
+
let currentLineNumber = 1
|
|
107
|
+
|
|
108
|
+
while (bytesRead < stat.size) {
|
|
109
|
+
if (interrupted) break
|
|
110
|
+
const data = new Uint8Array(chunkSize)
|
|
111
|
+
const readSize = Math.min(chunkSize, stat.size - bytesRead)
|
|
112
|
+
await handle.read(data, 0, readSize, bytesRead)
|
|
113
|
+
const chunk = data.subarray(0, readSize)
|
|
114
|
+
const text = new TextDecoder().decode(chunk, { stream: true })
|
|
115
|
+
buffer += text
|
|
116
|
+
|
|
117
|
+
const lines = buffer.split('\n')
|
|
118
|
+
buffer = lines.pop() || ''
|
|
119
|
+
|
|
120
|
+
for (const line of lines) {
|
|
121
|
+
if (regex.test(line)) {
|
|
122
|
+
const prefix = files.length > 1 ? `${file}:` : ''
|
|
123
|
+
const lineNumPrefix = showLineNumbers ? `${currentLineNumber}:` : ''
|
|
124
|
+
const output = `${prefix}${lineNumPrefix}${line}\n`
|
|
125
|
+
await writer.write(new TextEncoder().encode(output))
|
|
126
|
+
}
|
|
127
|
+
currentLineNumber++
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
bytesRead += readSize
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (buffer && regex.test(buffer)) {
|
|
134
|
+
const prefix = files.length > 1 ? `${file}:` : ''
|
|
135
|
+
const lineNumPrefix = showLineNumbers ? `${currentLineNumber}:` : ''
|
|
136
|
+
const output = `${prefix}${lineNumPrefix}${buffer}\n`
|
|
137
|
+
await writer.write(new TextEncoder().encode(output))
|
|
138
|
+
}
|
|
139
|
+
} catch (error) {
|
|
140
|
+
await writelnStderr(process, terminal, `grep: ${file}: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
141
|
+
exitCode = 1
|
|
142
|
+
} finally {
|
|
143
|
+
kernel.terminal.events.off(TerminalEvents.INTERRUPT, interruptHandler)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return exitCode
|
|
149
|
+
} finally {
|
|
150
|
+
writer.releaseLock()
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import type { CommandLineOptions } from 'command-line-args'
|
|
3
|
+
import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
4
|
+
import { TerminalEvents } from '@ecmaos/types'
|
|
5
|
+
import { TerminalCommand } from '../shared/terminal-command.js'
|
|
6
|
+
|
|
7
|
+
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
8
|
+
return new TerminalCommand({
|
|
9
|
+
command: 'head',
|
|
10
|
+
description: 'Print the first lines of files',
|
|
11
|
+
kernel,
|
|
12
|
+
shell,
|
|
13
|
+
terminal,
|
|
14
|
+
options: [
|
|
15
|
+
{ name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
|
|
16
|
+
{ name: 'lines', type: Number, alias: 'n', description: 'Print the first NUM lines instead of the first 10' },
|
|
17
|
+
{ name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, multiple: true, description: 'The path(s) to the file(s) to read' }
|
|
18
|
+
],
|
|
19
|
+
run: async (argv: CommandLineOptions, process?: Process) => {
|
|
20
|
+
if (!process) return 1
|
|
21
|
+
|
|
22
|
+
const writer = process.stdout.getWriter()
|
|
23
|
+
const numLines = (argv.lines as number) ?? 10
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
if (!argv.path || !(argv.path as string[])[0]) {
|
|
27
|
+
if (!process.stdin) {
|
|
28
|
+
return 0
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const reader = process.stdin.getReader()
|
|
32
|
+
const decoder = new TextDecoder()
|
|
33
|
+
const lines: string[] = []
|
|
34
|
+
let buffer = ''
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
while (true) {
|
|
38
|
+
let readResult
|
|
39
|
+
try {
|
|
40
|
+
readResult = await reader.read()
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error instanceof Error) {
|
|
43
|
+
throw error
|
|
44
|
+
}
|
|
45
|
+
break
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const { done, value } = readResult
|
|
49
|
+
if (done) {
|
|
50
|
+
buffer += decoder.decode()
|
|
51
|
+
break
|
|
52
|
+
}
|
|
53
|
+
if (value) {
|
|
54
|
+
buffer += decoder.decode(value, { stream: true })
|
|
55
|
+
const newLines = buffer.split('\n')
|
|
56
|
+
buffer = newLines.pop() || ''
|
|
57
|
+
lines.push(...newLines)
|
|
58
|
+
if (lines.length >= numLines) break
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (buffer && lines.length < numLines) {
|
|
62
|
+
lines.push(buffer)
|
|
63
|
+
}
|
|
64
|
+
} finally {
|
|
65
|
+
try {
|
|
66
|
+
reader.releaseLock()
|
|
67
|
+
} catch {
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const output = lines.slice(0, numLines).join('\n')
|
|
72
|
+
if (output) {
|
|
73
|
+
await writer.write(new TextEncoder().encode(output + '\n'))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return 0
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const files = (argv.path as string[]) || []
|
|
80
|
+
const isMultipleFiles = files.length > 1
|
|
81
|
+
|
|
82
|
+
for (let i = 0; i < files.length; i++) {
|
|
83
|
+
const file = files[i]
|
|
84
|
+
if (!file) continue
|
|
85
|
+
const fullPath = path.resolve(shell.cwd, file)
|
|
86
|
+
|
|
87
|
+
if (isMultipleFiles) {
|
|
88
|
+
const header = i > 0 ? '\n' : ''
|
|
89
|
+
await writer.write(new TextEncoder().encode(`${header}==> ${file} <==\n`))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let interrupted = false
|
|
93
|
+
const interruptHandler = () => { interrupted = true }
|
|
94
|
+
kernel.terminal.events.on(TerminalEvents.INTERRUPT, interruptHandler)
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
if (!fullPath.startsWith('/dev')) {
|
|
98
|
+
const handle = await shell.context.fs.promises.open(fullPath, 'r')
|
|
99
|
+
const stat = await shell.context.fs.promises.stat(fullPath)
|
|
100
|
+
|
|
101
|
+
const decoder = new TextDecoder()
|
|
102
|
+
const lines: string[] = []
|
|
103
|
+
let buffer = ''
|
|
104
|
+
let bytesRead = 0
|
|
105
|
+
const chunkSize = 1024
|
|
106
|
+
|
|
107
|
+
while (bytesRead < stat.size && lines.length < numLines) {
|
|
108
|
+
if (interrupted) break
|
|
109
|
+
const data = new Uint8Array(chunkSize)
|
|
110
|
+
const readSize = Math.min(chunkSize, stat.size - bytesRead)
|
|
111
|
+
await handle.read(data, 0, readSize, bytesRead)
|
|
112
|
+
const chunk = data.subarray(0, readSize)
|
|
113
|
+
buffer += decoder.decode(chunk, { stream: true })
|
|
114
|
+
const newLines = buffer.split('\n')
|
|
115
|
+
buffer = newLines.pop() || ''
|
|
116
|
+
lines.push(...newLines)
|
|
117
|
+
bytesRead += readSize
|
|
118
|
+
if (lines.length >= numLines) break
|
|
119
|
+
}
|
|
120
|
+
if (buffer && lines.length < numLines) {
|
|
121
|
+
lines.push(buffer)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const output = lines.slice(0, numLines).join('\n')
|
|
125
|
+
if (output) {
|
|
126
|
+
await writer.write(new TextEncoder().encode(output + '\n'))
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
const device = await shell.context.fs.promises.open(fullPath)
|
|
130
|
+
const decoder = new TextDecoder()
|
|
131
|
+
const lines: string[] = []
|
|
132
|
+
let buffer = ''
|
|
133
|
+
const chunkSize = 1024
|
|
134
|
+
const data = new Uint8Array(chunkSize)
|
|
135
|
+
let bytesRead = 0
|
|
136
|
+
|
|
137
|
+
do {
|
|
138
|
+
if (interrupted) break
|
|
139
|
+
const result = await device.read(data)
|
|
140
|
+
bytesRead = result.bytesRead
|
|
141
|
+
if (bytesRead > 0) {
|
|
142
|
+
buffer += decoder.decode(data.subarray(0, bytesRead), { stream: true })
|
|
143
|
+
const newLines = buffer.split('\n')
|
|
144
|
+
buffer = newLines.pop() || ''
|
|
145
|
+
lines.push(...newLines)
|
|
146
|
+
if (lines.length >= numLines) break
|
|
147
|
+
}
|
|
148
|
+
} while (bytesRead > 0 && lines.length < numLines)
|
|
149
|
+
|
|
150
|
+
if (buffer && lines.length < numLines) {
|
|
151
|
+
lines.push(buffer)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const output = lines.slice(0, numLines).join('\n')
|
|
155
|
+
if (output) {
|
|
156
|
+
await writer.write(new TextEncoder().encode(output + '\n'))
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
} finally {
|
|
160
|
+
kernel.terminal.events.off(TerminalEvents.INTERRUPT, interruptHandler)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return 0
|
|
165
|
+
} finally {
|
|
166
|
+
writer.releaseLock()
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
}
|
package/src/commands/hex.ts
CHANGED
|
@@ -7,38 +7,93 @@ import { writelnStdout, writelnStderr } from '../shared/helpers.js'
|
|
|
7
7
|
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
8
8
|
return new TerminalCommand({
|
|
9
9
|
command: 'hex',
|
|
10
|
-
description: 'Display file contents in hexadecimal format',
|
|
10
|
+
description: 'Display file contents or stdin in hexadecimal format',
|
|
11
11
|
kernel,
|
|
12
12
|
shell,
|
|
13
13
|
terminal,
|
|
14
14
|
options: [
|
|
15
15
|
{ name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
|
|
16
|
-
{ name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, description: 'The path to the file to display' }
|
|
16
|
+
{ name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, description: 'The path to the file to display (if omitted, reads from stdin)' }
|
|
17
17
|
],
|
|
18
18
|
run: async (argv: CommandLineOptions, process?: Process) => {
|
|
19
|
+
if (!process) return 1
|
|
20
|
+
|
|
19
21
|
const filePath = argv.path as string | undefined
|
|
22
|
+
let data: Uint8Array
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
try {
|
|
25
|
+
if (!filePath) {
|
|
26
|
+
if (!process.stdin) {
|
|
27
|
+
await writelnStderr(process, terminal, 'Usage: hex <file>')
|
|
28
|
+
await writelnStderr(process, terminal, ' or: <command> | hex')
|
|
29
|
+
return 1
|
|
30
|
+
}
|
|
25
31
|
|
|
26
|
-
|
|
32
|
+
if (process.stdinIsTTY) {
|
|
33
|
+
await writelnStderr(process, terminal, 'Usage: hex <file>')
|
|
34
|
+
await writelnStderr(process, terminal, ' or: <command> | hex')
|
|
35
|
+
return 1
|
|
36
|
+
}
|
|
27
37
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (!exists) {
|
|
31
|
-
await writelnStderr(process, terminal, `hex: ${filePath}: No such file or directory`)
|
|
32
|
-
return 1
|
|
33
|
-
}
|
|
38
|
+
const reader = process.stdin.getReader()
|
|
39
|
+
const chunks: Uint8Array[] = []
|
|
34
40
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
try {
|
|
42
|
+
const first = await reader.read()
|
|
43
|
+
|
|
44
|
+
if (first.done && !first.value) {
|
|
45
|
+
await writelnStderr(process, terminal, 'Usage: hex <file>')
|
|
46
|
+
await writelnStderr(process, terminal, ' or: <command> | hex')
|
|
47
|
+
return 1
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (first.value) {
|
|
51
|
+
chunks.push(first.value)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!first.done) {
|
|
55
|
+
while (true) {
|
|
56
|
+
const { done, value } = await reader.read()
|
|
57
|
+
if (done) break
|
|
58
|
+
if (value) {
|
|
59
|
+
chunks.push(value)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} finally {
|
|
64
|
+
reader.releaseLock()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0)
|
|
68
|
+
if (totalLength === 0) {
|
|
69
|
+
await writelnStderr(process, terminal, 'Usage: hex <file>')
|
|
70
|
+
await writelnStderr(process, terminal, ' or: <command> | hex')
|
|
71
|
+
return 1
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
data = new Uint8Array(totalLength)
|
|
75
|
+
let offset = 0
|
|
76
|
+
for (const chunk of chunks) {
|
|
77
|
+
data.set(chunk, offset)
|
|
78
|
+
offset += chunk.length
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
const fullPath = path.resolve(shell.cwd, filePath)
|
|
40
82
|
|
|
41
|
-
|
|
83
|
+
const exists = await shell.context.fs.promises.exists(fullPath)
|
|
84
|
+
if (!exists) {
|
|
85
|
+
await writelnStderr(process, terminal, `hex: ${filePath}: No such file or directory`)
|
|
86
|
+
return 1
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const stats = await shell.context.fs.promises.stat(fullPath)
|
|
90
|
+
if (stats.isDirectory()) {
|
|
91
|
+
await writelnStderr(process, terminal, `hex: ${filePath}: Is a directory`)
|
|
92
|
+
return 1
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
data = await shell.context.fs.promises.readFile(fullPath)
|
|
96
|
+
}
|
|
42
97
|
const bytesPerLine = 16
|
|
43
98
|
|
|
44
99
|
for (let offset = 0; offset < data.length; offset += bytesPerLine) {
|
|
@@ -84,7 +139,8 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
84
139
|
|
|
85
140
|
return 0
|
|
86
141
|
} catch (error) {
|
|
87
|
-
|
|
142
|
+
const errorPath = filePath || 'stdin'
|
|
143
|
+
await writelnStderr(process, terminal, `hex: ${errorPath}: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
88
144
|
return 1
|
|
89
145
|
}
|
|
90
146
|
}
|
package/src/commands/ls.ts
CHANGED
|
@@ -186,8 +186,10 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
186
186
|
if (linkInfo) return linkInfo
|
|
187
187
|
|
|
188
188
|
if (descriptions.has(path.resolve(fullPath, file.name))) return descriptions.get(path.resolve(fullPath, file.name))
|
|
189
|
-
|
|
190
|
-
|
|
189
|
+
if (file.name.includes('.')) {
|
|
190
|
+
const ext = file.name.split('.').pop()
|
|
191
|
+
if (ext && descriptions.has('.' + ext)) return descriptions.get('.' + ext)
|
|
192
|
+
}
|
|
191
193
|
if (!file.stats) return ''
|
|
192
194
|
if (file.stats.isBlockDevice() || file.stats.isCharacterDevice()) {
|
|
193
195
|
// TODO: zenfs `fs.mounts` is deprecated - use a better way of getting device info
|