@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.
Files changed (44) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +32 -0
  3. package/dist/commands/cat.d.ts.map +1 -1
  4. package/dist/commands/cat.js +23 -4
  5. package/dist/commands/cat.js.map +1 -1
  6. package/dist/commands/echo.d.ts.map +1 -1
  7. package/dist/commands/echo.js +5 -2
  8. package/dist/commands/echo.js.map +1 -1
  9. package/dist/commands/grep.d.ts +4 -0
  10. package/dist/commands/grep.d.ts.map +1 -0
  11. package/dist/commands/grep.js +140 -0
  12. package/dist/commands/grep.js.map +1 -0
  13. package/dist/commands/head.d.ts +4 -0
  14. package/dist/commands/head.d.ts.map +1 -0
  15. package/dist/commands/head.js +162 -0
  16. package/dist/commands/head.js.map +1 -0
  17. package/dist/commands/hex.d.ts.map +1 -1
  18. package/dist/commands/hex.js +69 -17
  19. package/dist/commands/hex.js.map +1 -1
  20. package/dist/commands/ls.d.ts.map +1 -1
  21. package/dist/commands/ls.js +5 -3
  22. package/dist/commands/ls.js.map +1 -1
  23. package/dist/commands/passkey.d.ts +4 -0
  24. package/dist/commands/passkey.d.ts.map +1 -0
  25. package/dist/commands/passkey.js +184 -0
  26. package/dist/commands/passkey.js.map +1 -0
  27. package/dist/commands/tail.d.ts +4 -0
  28. package/dist/commands/tail.d.ts.map +1 -0
  29. package/dist/commands/tail.js +152 -0
  30. package/dist/commands/tail.js.map +1 -0
  31. package/dist/index.d.ts +2 -0
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +10 -0
  34. package/dist/index.js.map +1 -1
  35. package/package.json +2 -2
  36. package/src/commands/cat.ts +25 -4
  37. package/src/commands/echo.ts +5 -2
  38. package/src/commands/grep.ts +154 -0
  39. package/src/commands/head.ts +170 -0
  40. package/src/commands/hex.ts +76 -20
  41. package/src/commands/ls.ts +4 -2
  42. package/src/commands/passkey.ts +208 -0
  43. package/src/commands/tail.ts +164 -0
  44. 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":"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"}
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.3",
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.2"
32
+ "@ecmaos/types": "^0.4.4"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/ansi-escape-sequences": "^4.0.4",
@@ -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
- await writer.write(data.subarray(0, readSize))
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
- await writer.write(data.subarray(0, bytesToWrite))
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
  })
@@ -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 data = new TextEncoder().encode(text + '\n')
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(text + '\n')
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
+ }
@@ -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
- if (!filePath) {
22
- await writelnStderr(process, terminal, 'Usage: hex <file>')
23
- return 1
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
- const fullPath = path.resolve(shell.cwd, filePath)
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
- try {
29
- const exists = await shell.context.fs.promises.exists(fullPath)
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
- const stats = await shell.context.fs.promises.stat(fullPath)
36
- if (stats.isDirectory()) {
37
- await writelnStderr(process, terminal, `hex: ${filePath}: Is a directory`)
38
- return 1
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
- const data = await shell.context.fs.promises.readFile(fullPath)
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
- await writelnStderr(process, terminal, `hex: ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`)
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
  }
@@ -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
- const ext = file.name.split('.').pop()
190
- if (ext && descriptions.has('.' + ext)) return descriptions.get('.' + ext)
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