@ecmaos/coreutils 0.3.1 → 0.4.2
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 +48 -0
- package/dist/commands/awk.d.ts +4 -0
- package/dist/commands/awk.d.ts.map +1 -0
- package/dist/commands/awk.js +324 -0
- package/dist/commands/awk.js.map +1 -0
- package/dist/commands/chgrp.d.ts +4 -0
- package/dist/commands/chgrp.d.ts.map +1 -0
- package/dist/commands/chgrp.js +187 -0
- package/dist/commands/chgrp.js.map +1 -0
- package/dist/commands/chmod.d.ts.map +1 -1
- package/dist/commands/chmod.js +139 -2
- package/dist/commands/chmod.js.map +1 -1
- package/dist/commands/chown.d.ts +4 -0
- package/dist/commands/chown.d.ts.map +1 -0
- package/dist/commands/chown.js +257 -0
- package/dist/commands/chown.js.map +1 -0
- package/dist/commands/cksum.d.ts +4 -0
- package/dist/commands/cksum.d.ts.map +1 -0
- package/dist/commands/cksum.js +124 -0
- package/dist/commands/cksum.js.map +1 -0
- package/dist/commands/cmp.d.ts +4 -0
- package/dist/commands/cmp.d.ts.map +1 -0
- package/dist/commands/cmp.js +120 -0
- package/dist/commands/cmp.js.map +1 -0
- package/dist/commands/column.d.ts +4 -0
- package/dist/commands/column.d.ts.map +1 -0
- package/dist/commands/column.js +274 -0
- package/dist/commands/column.js.map +1 -0
- package/dist/commands/cp.d.ts.map +1 -1
- package/dist/commands/cp.js +81 -4
- package/dist/commands/cp.js.map +1 -1
- package/dist/commands/cron.d.ts.map +1 -1
- package/dist/commands/cron.js +116 -23
- package/dist/commands/cron.js.map +1 -1
- package/dist/commands/curl.d.ts +4 -0
- package/dist/commands/curl.d.ts.map +1 -0
- package/dist/commands/curl.js +238 -0
- package/dist/commands/curl.js.map +1 -0
- package/dist/commands/du.d.ts +4 -0
- package/dist/commands/du.d.ts.map +1 -0
- package/dist/commands/du.js +168 -0
- package/dist/commands/du.js.map +1 -0
- package/dist/commands/echo.d.ts.map +1 -1
- package/dist/commands/echo.js +125 -2
- package/dist/commands/echo.js.map +1 -1
- package/dist/commands/env.d.ts +4 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +129 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/expand.d.ts +4 -0
- package/dist/commands/expand.d.ts.map +1 -0
- package/dist/commands/expand.js +197 -0
- package/dist/commands/expand.js.map +1 -0
- package/dist/commands/factor.d.ts +4 -0
- package/dist/commands/factor.d.ts.map +1 -0
- package/dist/commands/factor.js +141 -0
- package/dist/commands/factor.js.map +1 -0
- package/dist/commands/fmt.d.ts +4 -0
- package/dist/commands/fmt.d.ts.map +1 -0
- package/dist/commands/fmt.js +278 -0
- package/dist/commands/fmt.js.map +1 -0
- package/dist/commands/fold.d.ts +4 -0
- package/dist/commands/fold.d.ts.map +1 -0
- package/dist/commands/fold.js +253 -0
- package/dist/commands/fold.js.map +1 -0
- package/dist/commands/groups.d.ts +4 -0
- package/dist/commands/groups.d.ts.map +1 -0
- package/dist/commands/groups.js +61 -0
- package/dist/commands/groups.js.map +1 -0
- package/dist/commands/head.d.ts.map +1 -1
- package/dist/commands/head.js +184 -77
- package/dist/commands/head.js.map +1 -1
- package/dist/commands/hostname.d.ts +4 -0
- package/dist/commands/hostname.d.ts.map +1 -0
- package/dist/commands/hostname.js +80 -0
- package/dist/commands/hostname.js.map +1 -0
- package/dist/commands/less.d.ts.map +1 -1
- package/dist/commands/less.js +1 -0
- package/dist/commands/less.js.map +1 -1
- package/dist/commands/man.d.ts.map +1 -1
- package/dist/commands/man.js +3 -1
- package/dist/commands/man.js.map +1 -1
- package/dist/commands/mount.d.ts +4 -0
- package/dist/commands/mount.d.ts.map +1 -0
- package/dist/commands/mount.js +1136 -0
- package/dist/commands/mount.js.map +1 -0
- package/dist/commands/od.d.ts +4 -0
- package/dist/commands/od.d.ts.map +1 -0
- package/dist/commands/od.js +342 -0
- package/dist/commands/od.js.map +1 -0
- package/dist/commands/pr.d.ts +4 -0
- package/dist/commands/pr.d.ts.map +1 -0
- package/dist/commands/pr.js +298 -0
- package/dist/commands/pr.js.map +1 -0
- package/dist/commands/printf.d.ts +4 -0
- package/dist/commands/printf.d.ts.map +1 -0
- package/dist/commands/printf.js +271 -0
- package/dist/commands/printf.js.map +1 -0
- package/dist/commands/readlink.d.ts +4 -0
- package/dist/commands/readlink.d.ts.map +1 -0
- package/dist/commands/readlink.js +104 -0
- package/dist/commands/readlink.js.map +1 -0
- package/dist/commands/realpath.d.ts +4 -0
- package/dist/commands/realpath.d.ts.map +1 -0
- package/dist/commands/realpath.js +111 -0
- package/dist/commands/realpath.js.map +1 -0
- package/dist/commands/rev.d.ts +4 -0
- package/dist/commands/rev.d.ts.map +1 -0
- package/dist/commands/rev.js +134 -0
- package/dist/commands/rev.js.map +1 -0
- package/dist/commands/shuf.d.ts +4 -0
- package/dist/commands/shuf.d.ts.map +1 -0
- package/dist/commands/shuf.js +221 -0
- package/dist/commands/shuf.js.map +1 -0
- package/dist/commands/sleep.d.ts +4 -0
- package/dist/commands/sleep.d.ts.map +1 -0
- package/dist/commands/sleep.js +102 -0
- package/dist/commands/sleep.js.map +1 -0
- package/dist/commands/strings.d.ts +4 -0
- package/dist/commands/strings.d.ts.map +1 -0
- package/dist/commands/strings.js +170 -0
- package/dist/commands/strings.js.map +1 -0
- package/dist/commands/tac.d.ts +4 -0
- package/dist/commands/tac.d.ts.map +1 -0
- package/dist/commands/tac.js +130 -0
- package/dist/commands/tac.js.map +1 -0
- package/dist/commands/time.d.ts +4 -0
- package/dist/commands/time.d.ts.map +1 -0
- package/dist/commands/time.js +126 -0
- package/dist/commands/time.js.map +1 -0
- package/dist/commands/umount.d.ts +4 -0
- package/dist/commands/umount.d.ts.map +1 -0
- package/dist/commands/umount.js +103 -0
- package/dist/commands/umount.js.map +1 -0
- package/dist/commands/uname.d.ts +4 -0
- package/dist/commands/uname.d.ts.map +1 -0
- package/dist/commands/uname.js +149 -0
- package/dist/commands/uname.js.map +1 -0
- package/dist/commands/unexpand.d.ts +4 -0
- package/dist/commands/unexpand.d.ts.map +1 -0
- package/dist/commands/unexpand.js +286 -0
- package/dist/commands/unexpand.js.map +1 -0
- package/dist/commands/uptime.d.ts +4 -0
- package/dist/commands/uptime.d.ts.map +1 -0
- package/dist/commands/uptime.js +62 -0
- package/dist/commands/uptime.js.map +1 -0
- package/dist/commands/view.d.ts +1 -0
- package/dist/commands/view.d.ts.map +1 -1
- package/dist/commands/view.js +408 -66
- package/dist/commands/view.js.map +1 -1
- package/dist/commands/yes.d.ts +4 -0
- package/dist/commands/yes.d.ts.map +1 -0
- package/dist/commands/yes.js +58 -0
- package/dist/commands/yes.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +82 -0
- package/dist/index.js.map +1 -1
- package/package.json +12 -3
- package/src/commands/awk.ts +340 -0
- package/src/commands/chmod.ts +141 -2
- package/src/commands/chown.ts +321 -0
- package/src/commands/cksum.ts +133 -0
- package/src/commands/cmp.ts +126 -0
- package/src/commands/column.ts +273 -0
- package/src/commands/cp.ts +93 -4
- package/src/commands/cron.ts +115 -23
- package/src/commands/curl.ts +231 -0
- package/src/commands/echo.ts +122 -2
- package/src/commands/env.ts +143 -0
- package/src/commands/expand.ts +207 -0
- package/src/commands/factor.ts +151 -0
- package/src/commands/fmt.ts +293 -0
- package/src/commands/fold.ts +257 -0
- package/src/commands/groups.ts +72 -0
- package/src/commands/head.ts +176 -77
- package/src/commands/hostname.ts +81 -0
- package/src/commands/less.ts +1 -0
- package/src/commands/man.ts +4 -1
- package/src/commands/mount.ts +1302 -0
- package/src/commands/od.ts +327 -0
- package/src/commands/pr.ts +291 -0
- package/src/commands/printf.ts +271 -0
- package/src/commands/readlink.ts +102 -0
- package/src/commands/realpath.ts +126 -0
- package/src/commands/rev.ts +143 -0
- package/src/commands/shuf.ts +218 -0
- package/src/commands/sleep.ts +109 -0
- package/src/commands/strings.ts +176 -0
- package/src/commands/tac.ts +138 -0
- package/src/commands/time.ts +144 -0
- package/src/commands/umount.ts +116 -0
- package/src/commands/uname.ts +130 -0
- package/src/commands/unexpand.ts +305 -0
- package/src/commands/uptime.ts +73 -0
- package/src/commands/view.ts +463 -73
- package/src/index.ts +82 -0
- package/tsconfig.json +4 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
3
|
+
import { TerminalEvents } from '@ecmaos/types'
|
|
4
|
+
import { TerminalCommand } from '../shared/terminal-command.js'
|
|
5
|
+
import { writelnStderr } from '../shared/helpers.js'
|
|
6
|
+
|
|
7
|
+
function printUsage(process: Process | undefined, terminal: Terminal): void {
|
|
8
|
+
const usage = `Usage: fold [OPTION]... [FILE]...
|
|
9
|
+
Wrap each input line to fit in specified width.
|
|
10
|
+
|
|
11
|
+
-w, --width=WIDTH use WIDTH columns instead of 80
|
|
12
|
+
-s, --spaces break at spaces when possible
|
|
13
|
+
-b, --bytes count bytes instead of columns
|
|
14
|
+
--help display this help and exit`
|
|
15
|
+
writelnStderr(process, terminal, usage)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function wrapLine(line: string, width: number, breakAtSpaces: boolean, countBytes: boolean): string[] {
|
|
19
|
+
if (!line) return ['']
|
|
20
|
+
|
|
21
|
+
const result: string[] = []
|
|
22
|
+
|
|
23
|
+
if (countBytes) {
|
|
24
|
+
const encoder = new TextEncoder()
|
|
25
|
+
let current = ''
|
|
26
|
+
let currentBytes = 0
|
|
27
|
+
|
|
28
|
+
for (let i = 0; i < line.length; i++) {
|
|
29
|
+
const char = line[i]
|
|
30
|
+
const charBytes = encoder.encode(char).length
|
|
31
|
+
|
|
32
|
+
if (currentBytes + charBytes > width && current.length > 0) {
|
|
33
|
+
if (breakAtSpaces) {
|
|
34
|
+
const lastSpace = current.lastIndexOf(' ')
|
|
35
|
+
if (lastSpace > 0) {
|
|
36
|
+
result.push(current.slice(0, lastSpace))
|
|
37
|
+
current = current.slice(lastSpace + 1) + char
|
|
38
|
+
currentBytes = encoder.encode(current).length
|
|
39
|
+
continue
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
result.push(current)
|
|
43
|
+
current = char || ''
|
|
44
|
+
currentBytes = charBytes
|
|
45
|
+
} else {
|
|
46
|
+
current += char
|
|
47
|
+
currentBytes += charBytes
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (current.length > 0) {
|
|
52
|
+
result.push(current)
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
let current = ''
|
|
56
|
+
|
|
57
|
+
for (let i = 0; i < line.length; i++) {
|
|
58
|
+
const char = line[i]
|
|
59
|
+
|
|
60
|
+
if (current.length >= width && current.length > 0) {
|
|
61
|
+
if (breakAtSpaces) {
|
|
62
|
+
const lastSpace = current.lastIndexOf(' ')
|
|
63
|
+
if (lastSpace > 0) {
|
|
64
|
+
result.push(current.slice(0, lastSpace))
|
|
65
|
+
current = current.slice(lastSpace + 1) + char
|
|
66
|
+
continue
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
result.push(current)
|
|
70
|
+
current = char || ''
|
|
71
|
+
} else {
|
|
72
|
+
current += char
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (current.length > 0) {
|
|
77
|
+
result.push(current)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return result.length > 0 ? result : ['']
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
85
|
+
return new TerminalCommand({
|
|
86
|
+
command: 'fold',
|
|
87
|
+
description: 'Wrap each input line to fit in specified width',
|
|
88
|
+
kernel,
|
|
89
|
+
shell,
|
|
90
|
+
terminal,
|
|
91
|
+
run: async (pid: number, argv: string[]) => {
|
|
92
|
+
const process = kernel.processes.get(pid) as Process | undefined
|
|
93
|
+
|
|
94
|
+
if (!process) return 1
|
|
95
|
+
|
|
96
|
+
if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
|
|
97
|
+
printUsage(process, terminal)
|
|
98
|
+
return 0
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let width = 80
|
|
102
|
+
let breakAtSpaces = false
|
|
103
|
+
let countBytes = false
|
|
104
|
+
const files: string[] = []
|
|
105
|
+
|
|
106
|
+
for (let i = 0; i < argv.length; i++) {
|
|
107
|
+
const arg = argv[i]
|
|
108
|
+
if (!arg) continue
|
|
109
|
+
|
|
110
|
+
if (arg === '--help' || arg === '-h') {
|
|
111
|
+
printUsage(process, terminal)
|
|
112
|
+
return 0
|
|
113
|
+
} else if (arg === '-w' || arg === '--width') {
|
|
114
|
+
if (i + 1 < argv.length) {
|
|
115
|
+
const widthStr = argv[++i]
|
|
116
|
+
if (widthStr !== undefined) {
|
|
117
|
+
const parsed = parseInt(widthStr, 10)
|
|
118
|
+
if (!isNaN(parsed) && parsed > 0) {
|
|
119
|
+
width = parsed
|
|
120
|
+
} else {
|
|
121
|
+
await writelnStderr(process, terminal, `fold: invalid width: ${widthStr}`)
|
|
122
|
+
return 1
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} else if (arg.startsWith('--width=')) {
|
|
127
|
+
const widthStr = arg.slice(8)
|
|
128
|
+
const parsed = parseInt(widthStr, 10)
|
|
129
|
+
if (!isNaN(parsed) && parsed > 0) {
|
|
130
|
+
width = parsed
|
|
131
|
+
} else {
|
|
132
|
+
await writelnStderr(process, terminal, `fold: invalid width: ${widthStr}`)
|
|
133
|
+
return 1
|
|
134
|
+
}
|
|
135
|
+
} else if (arg.startsWith('-w')) {
|
|
136
|
+
const widthStr = arg.slice(2)
|
|
137
|
+
if (widthStr) {
|
|
138
|
+
const parsed = parseInt(widthStr, 10)
|
|
139
|
+
if (!isNaN(parsed) && parsed > 0) {
|
|
140
|
+
width = parsed
|
|
141
|
+
} else {
|
|
142
|
+
await writelnStderr(process, terminal, `fold: invalid width: ${widthStr}`)
|
|
143
|
+
return 1
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
} else if (arg === '-s' || arg === '--spaces') {
|
|
147
|
+
breakAtSpaces = true
|
|
148
|
+
} else if (arg === '-b' || arg === '--bytes') {
|
|
149
|
+
countBytes = true
|
|
150
|
+
} else if (arg.startsWith('-')) {
|
|
151
|
+
const flags = arg.slice(1).split('')
|
|
152
|
+
if (flags.includes('s')) breakAtSpaces = true
|
|
153
|
+
if (flags.includes('b')) countBytes = true
|
|
154
|
+
const invalidFlags = flags.filter(f => !['s', 'b'].includes(f))
|
|
155
|
+
if (invalidFlags.length > 0) {
|
|
156
|
+
await writelnStderr(process, terminal, `fold: invalid option -- '${invalidFlags[0]}'`)
|
|
157
|
+
await writelnStderr(process, terminal, "Try 'fold --help' for more information.")
|
|
158
|
+
return 1
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
files.push(arg)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const writer = process.stdout.getWriter()
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
let lines: string[] = []
|
|
169
|
+
|
|
170
|
+
if (files.length === 0) {
|
|
171
|
+
if (!process.stdin) {
|
|
172
|
+
return 0
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const reader = process.stdin.getReader()
|
|
176
|
+
const decoder = new TextDecoder()
|
|
177
|
+
let buffer = ''
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
while (true) {
|
|
181
|
+
const { done, value } = await reader.read()
|
|
182
|
+
if (done) break
|
|
183
|
+
if (value) {
|
|
184
|
+
buffer += decoder.decode(value, { stream: true })
|
|
185
|
+
const newLines = buffer.split('\n')
|
|
186
|
+
buffer = newLines.pop() || ''
|
|
187
|
+
lines.push(...newLines)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (buffer) {
|
|
191
|
+
lines.push(buffer)
|
|
192
|
+
}
|
|
193
|
+
} finally {
|
|
194
|
+
reader.releaseLock()
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
for (const file of files) {
|
|
198
|
+
const fullPath = path.resolve(shell.cwd, file)
|
|
199
|
+
|
|
200
|
+
let interrupted = false
|
|
201
|
+
const interruptHandler = () => { interrupted = true }
|
|
202
|
+
kernel.terminal.events.on(TerminalEvents.INTERRUPT, interruptHandler)
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
if (fullPath.startsWith('/dev')) {
|
|
206
|
+
await writelnStderr(process, terminal, `fold: ${file}: cannot process device files`)
|
|
207
|
+
continue
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const handle = await shell.context.fs.promises.open(fullPath, 'r')
|
|
211
|
+
const stat = await shell.context.fs.promises.stat(fullPath)
|
|
212
|
+
|
|
213
|
+
const decoder = new TextDecoder()
|
|
214
|
+
let content = ''
|
|
215
|
+
let bytesRead = 0
|
|
216
|
+
const chunkSize = 1024
|
|
217
|
+
|
|
218
|
+
while (bytesRead < stat.size) {
|
|
219
|
+
if (interrupted) break
|
|
220
|
+
const data = new Uint8Array(chunkSize)
|
|
221
|
+
const readSize = Math.min(chunkSize, stat.size - bytesRead)
|
|
222
|
+
await handle.read(data, 0, readSize, bytesRead)
|
|
223
|
+
const chunk = data.subarray(0, readSize)
|
|
224
|
+
content += decoder.decode(chunk, { stream: true })
|
|
225
|
+
bytesRead += readSize
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const fileLines = content.split('\n')
|
|
229
|
+
if (fileLines[fileLines.length - 1] === '') {
|
|
230
|
+
fileLines.pop()
|
|
231
|
+
}
|
|
232
|
+
lines.push(...fileLines)
|
|
233
|
+
} catch (error) {
|
|
234
|
+
await writelnStderr(process, terminal, `fold: ${file}: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
235
|
+
} finally {
|
|
236
|
+
kernel.terminal.events.off(TerminalEvents.INTERRUPT, interruptHandler)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
for (const line of lines) {
|
|
242
|
+
const wrapped = wrapLine(line, width, breakAtSpaces, countBytes)
|
|
243
|
+
for (const wrappedLine of wrapped) {
|
|
244
|
+
await writer.write(new TextEncoder().encode(wrappedLine + '\n'))
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return 0
|
|
249
|
+
} catch (error) {
|
|
250
|
+
await writelnStderr(process, terminal, `fold: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
251
|
+
return 1
|
|
252
|
+
} finally {
|
|
253
|
+
writer.releaseLock()
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
})
|
|
257
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { Kernel, Process, Shell, Terminal, User } from '@ecmaos/types'
|
|
2
|
+
import { TerminalCommand } from '../shared/terminal-command.js'
|
|
3
|
+
import { writelnStderr, writelnStdout } from '../shared/helpers.js'
|
|
4
|
+
|
|
5
|
+
function printUsage(process: Process | undefined, terminal: Terminal): void {
|
|
6
|
+
const usage = `Usage: groups [USERNAME]...
|
|
7
|
+
Print the groups a user belongs to.
|
|
8
|
+
|
|
9
|
+
--help display this help and exit`
|
|
10
|
+
writelnStderr(process, terminal, usage)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
14
|
+
return new TerminalCommand({
|
|
15
|
+
command: 'groups',
|
|
16
|
+
description: 'Print the groups a user belongs to',
|
|
17
|
+
kernel,
|
|
18
|
+
shell,
|
|
19
|
+
terminal,
|
|
20
|
+
run: async (pid: number, argv: string[]) => {
|
|
21
|
+
const process = kernel.processes.get(pid) as Process | undefined
|
|
22
|
+
|
|
23
|
+
if (!process) return 1
|
|
24
|
+
|
|
25
|
+
if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
|
|
26
|
+
printUsage(process, terminal)
|
|
27
|
+
return 0
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const usernames: string[] = []
|
|
31
|
+
|
|
32
|
+
for (const arg of argv) {
|
|
33
|
+
if (!arg) continue
|
|
34
|
+
|
|
35
|
+
if (arg === '--help' || arg === '-h') {
|
|
36
|
+
printUsage(process, terminal)
|
|
37
|
+
return 0
|
|
38
|
+
} else if (!arg.startsWith('-')) {
|
|
39
|
+
usernames.push(arg)
|
|
40
|
+
} else {
|
|
41
|
+
await writelnStderr(process, terminal, `groups: invalid option -- '${arg.slice(1)}'`)
|
|
42
|
+
await writelnStderr(process, terminal, "Try 'groups --help' for more information.")
|
|
43
|
+
return 1
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const targets = usernames.length > 0 ? usernames : [shell.username]
|
|
48
|
+
|
|
49
|
+
for (const username of targets) {
|
|
50
|
+
const user = Array.from(kernel.users.all.values()).find(
|
|
51
|
+
(u): u is User => (u as User).username === username
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if (!user) {
|
|
55
|
+
await writelnStderr(process, terminal, `groups: '${username}': no such user`)
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const groups = shell.credentials.groups || []
|
|
60
|
+
const groupNames = groups.map(gid => {
|
|
61
|
+
const groupUser = kernel.users.get(gid)
|
|
62
|
+
return groupUser?.username || gid.toString()
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const output = `${username} : ${groupNames.join(' ')}`
|
|
66
|
+
await writelnStdout(process, terminal, output)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return 0
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
}
|
package/src/commands/head.ts
CHANGED
|
@@ -9,6 +9,7 @@ function printUsage(process: Process | undefined, terminal: Terminal): void {
|
|
|
9
9
|
Print the first 10 lines of each FILE to standard output.
|
|
10
10
|
|
|
11
11
|
-n, -nNUMBER print the first NUMBER lines instead of 10
|
|
12
|
+
-c, -cNUMBER print the first NUMBER bytes instead of lines
|
|
12
13
|
--help display this help and exit`
|
|
13
14
|
writelnStderr(process, terminal, usage)
|
|
14
15
|
}
|
|
@@ -31,6 +32,7 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
let numLines = 10
|
|
35
|
+
let numBytes: number | null = null
|
|
34
36
|
const files: string[] = []
|
|
35
37
|
|
|
36
38
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -52,6 +54,18 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
52
54
|
const num = parseInt(arg.slice(2), 10)
|
|
53
55
|
if (!isNaN(num)) numLines = num
|
|
54
56
|
}
|
|
57
|
+
} else if (arg === '-c' || arg.startsWith('-c')) {
|
|
58
|
+
if (arg === '-c' && i + 1 < argv.length) {
|
|
59
|
+
i++
|
|
60
|
+
const nextArg = argv[i]
|
|
61
|
+
if (nextArg !== undefined) {
|
|
62
|
+
const num = parseInt(nextArg, 10)
|
|
63
|
+
if (!isNaN(num)) numBytes = num
|
|
64
|
+
}
|
|
65
|
+
} else if (arg.startsWith('-c') && arg.length > 2) {
|
|
66
|
+
const num = parseInt(arg.slice(2), 10)
|
|
67
|
+
if (!isNaN(num)) numBytes = num
|
|
68
|
+
}
|
|
55
69
|
} else if (!arg.startsWith('-')) {
|
|
56
70
|
files.push(arg)
|
|
57
71
|
}
|
|
@@ -66,37 +80,83 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
66
80
|
}
|
|
67
81
|
|
|
68
82
|
const reader = process.stdin.getReader()
|
|
69
|
-
const decoder = new TextDecoder()
|
|
70
|
-
const lines: string[] = []
|
|
71
|
-
let buffer = ''
|
|
72
83
|
|
|
73
84
|
try {
|
|
74
|
-
|
|
75
|
-
let
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
if (numBytes !== null) {
|
|
86
|
+
let bytesRead = 0
|
|
87
|
+
const chunks: Uint8Array[] = []
|
|
88
|
+
|
|
89
|
+
while (bytesRead < numBytes) {
|
|
90
|
+
let readResult
|
|
91
|
+
try {
|
|
92
|
+
readResult = await reader.read()
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (error instanceof Error) {
|
|
95
|
+
throw error
|
|
96
|
+
}
|
|
97
|
+
break
|
|
81
98
|
}
|
|
82
|
-
|
|
99
|
+
|
|
100
|
+
const { done, value } = readResult
|
|
101
|
+
if (done) break
|
|
102
|
+
if (value) {
|
|
103
|
+
const remaining = numBytes - bytesRead
|
|
104
|
+
if (value.length <= remaining) {
|
|
105
|
+
chunks.push(value)
|
|
106
|
+
bytesRead += value.length
|
|
107
|
+
} else {
|
|
108
|
+
chunks.push(value.subarray(0, remaining))
|
|
109
|
+
bytesRead = numBytes
|
|
110
|
+
}
|
|
111
|
+
if (bytesRead >= numBytes) break
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0)
|
|
116
|
+
const result = new Uint8Array(totalLength)
|
|
117
|
+
let offset = 0
|
|
118
|
+
for (const chunk of chunks) {
|
|
119
|
+
result.set(chunk, offset)
|
|
120
|
+
offset += chunk.length
|
|
83
121
|
}
|
|
122
|
+
await writer.write(result)
|
|
123
|
+
} else {
|
|
124
|
+
const decoder = new TextDecoder()
|
|
125
|
+
const lines: string[] = []
|
|
126
|
+
let buffer = ''
|
|
84
127
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
128
|
+
while (true) {
|
|
129
|
+
let readResult
|
|
130
|
+
try {
|
|
131
|
+
readResult = await reader.read()
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (error instanceof Error) {
|
|
134
|
+
throw error
|
|
135
|
+
}
|
|
136
|
+
break
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const { done, value } = readResult
|
|
140
|
+
if (done) {
|
|
141
|
+
buffer += decoder.decode()
|
|
142
|
+
break
|
|
143
|
+
}
|
|
144
|
+
if (value) {
|
|
145
|
+
buffer += decoder.decode(value, { stream: true })
|
|
146
|
+
const newLines = buffer.split('\n')
|
|
147
|
+
buffer = newLines.pop() || ''
|
|
148
|
+
lines.push(...newLines)
|
|
149
|
+
if (lines.length >= numLines) break
|
|
150
|
+
}
|
|
89
151
|
}
|
|
90
|
-
if (
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
152
|
+
if (buffer && lines.length < numLines) {
|
|
153
|
+
lines.push(buffer)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const output = lines.slice(0, numLines).join('\n')
|
|
157
|
+
if (output) {
|
|
158
|
+
await writer.write(new TextEncoder().encode(output + '\n'))
|
|
96
159
|
}
|
|
97
|
-
}
|
|
98
|
-
if (buffer && lines.length < numLines) {
|
|
99
|
-
lines.push(buffer)
|
|
100
160
|
}
|
|
101
161
|
} finally {
|
|
102
162
|
try {
|
|
@@ -105,11 +165,6 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
105
165
|
}
|
|
106
166
|
}
|
|
107
167
|
|
|
108
|
-
const output = lines.slice(0, numLines).join('\n')
|
|
109
|
-
if (output) {
|
|
110
|
-
await writer.write(new TextEncoder().encode(output + '\n'))
|
|
111
|
-
}
|
|
112
|
-
|
|
113
168
|
return 0
|
|
114
169
|
}
|
|
115
170
|
|
|
@@ -130,66 +185,110 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
130
185
|
kernel.terminal.events.on(TerminalEvents.INTERRUPT, interruptHandler)
|
|
131
186
|
|
|
132
187
|
try {
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (interrupted) break
|
|
188
|
+
if (numBytes !== null) {
|
|
189
|
+
if (!fullPath.startsWith('/dev')) {
|
|
190
|
+
const handle = await shell.context.fs.promises.open(fullPath, 'r')
|
|
191
|
+
const stat = await shell.context.fs.promises.stat(fullPath)
|
|
192
|
+
const readSize = Math.min(numBytes, stat.size)
|
|
193
|
+
const data = new Uint8Array(readSize)
|
|
194
|
+
await handle.read(data, 0, readSize, 0)
|
|
195
|
+
await writer.write(data)
|
|
196
|
+
} else {
|
|
197
|
+
const device = await shell.context.fs.promises.open(fullPath)
|
|
198
|
+
const chunkSize = 1024
|
|
145
199
|
const data = new Uint8Array(chunkSize)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
buffer += decoder.decode(chunk, { stream: true })
|
|
150
|
-
const newLines = buffer.split('\n')
|
|
151
|
-
buffer = newLines.pop() || ''
|
|
152
|
-
lines.push(...newLines)
|
|
153
|
-
bytesRead += readSize
|
|
154
|
-
if (lines.length >= numLines) break
|
|
155
|
-
}
|
|
156
|
-
if (buffer && lines.length < numLines) {
|
|
157
|
-
lines.push(buffer)
|
|
158
|
-
}
|
|
200
|
+
let totalBytesRead = 0
|
|
201
|
+
let bytesRead = 0
|
|
202
|
+
const chunks: Uint8Array[] = []
|
|
159
203
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
204
|
+
do {
|
|
205
|
+
if (interrupted) break
|
|
206
|
+
const result = await device.read(data)
|
|
207
|
+
bytesRead = result.bytesRead
|
|
208
|
+
if (bytesRead > 0) {
|
|
209
|
+
const remaining = numBytes - totalBytesRead
|
|
210
|
+
if (bytesRead <= remaining) {
|
|
211
|
+
chunks.push(data.subarray(0, bytesRead))
|
|
212
|
+
totalBytesRead += bytesRead
|
|
213
|
+
} else {
|
|
214
|
+
chunks.push(data.subarray(0, remaining))
|
|
215
|
+
totalBytesRead = numBytes
|
|
216
|
+
}
|
|
217
|
+
if (totalBytesRead >= numBytes) break
|
|
218
|
+
}
|
|
219
|
+
} while (totalBytesRead < numBytes && bytesRead > 0)
|
|
220
|
+
|
|
221
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0)
|
|
222
|
+
const result = new Uint8Array(totalLength)
|
|
223
|
+
let offset = 0
|
|
224
|
+
for (const chunk of chunks) {
|
|
225
|
+
result.set(chunk, offset)
|
|
226
|
+
offset += chunk.length
|
|
227
|
+
}
|
|
228
|
+
await writer.write(result)
|
|
163
229
|
}
|
|
164
230
|
} else {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
let buffer = ''
|
|
169
|
-
const chunkSize = 1024
|
|
170
|
-
const data = new Uint8Array(chunkSize)
|
|
171
|
-
let bytesRead = 0
|
|
231
|
+
if (!fullPath.startsWith('/dev')) {
|
|
232
|
+
const handle = await shell.context.fs.promises.open(fullPath, 'r')
|
|
233
|
+
const stat = await shell.context.fs.promises.stat(fullPath)
|
|
172
234
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
bytesRead =
|
|
177
|
-
|
|
178
|
-
|
|
235
|
+
const decoder = new TextDecoder()
|
|
236
|
+
const lines: string[] = []
|
|
237
|
+
let buffer = ''
|
|
238
|
+
let bytesRead = 0
|
|
239
|
+
const chunkSize = 1024
|
|
240
|
+
|
|
241
|
+
while (bytesRead < stat.size && lines.length < numLines) {
|
|
242
|
+
if (interrupted) break
|
|
243
|
+
const data = new Uint8Array(chunkSize)
|
|
244
|
+
const readSize = Math.min(chunkSize, stat.size - bytesRead)
|
|
245
|
+
await handle.read(data, 0, readSize, bytesRead)
|
|
246
|
+
const chunk = data.subarray(0, readSize)
|
|
247
|
+
buffer += decoder.decode(chunk, { stream: true })
|
|
179
248
|
const newLines = buffer.split('\n')
|
|
180
249
|
buffer = newLines.pop() || ''
|
|
181
250
|
lines.push(...newLines)
|
|
251
|
+
bytesRead += readSize
|
|
182
252
|
if (lines.length >= numLines) break
|
|
183
253
|
}
|
|
184
|
-
|
|
254
|
+
if (buffer && lines.length < numLines) {
|
|
255
|
+
lines.push(buffer)
|
|
256
|
+
}
|
|
185
257
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
258
|
+
const output = lines.slice(0, numLines).join('\n')
|
|
259
|
+
if (output) {
|
|
260
|
+
await writer.write(new TextEncoder().encode(output + '\n'))
|
|
261
|
+
}
|
|
262
|
+
} else {
|
|
263
|
+
const device = await shell.context.fs.promises.open(fullPath)
|
|
264
|
+
const decoder = new TextDecoder()
|
|
265
|
+
const lines: string[] = []
|
|
266
|
+
let buffer = ''
|
|
267
|
+
const chunkSize = 1024
|
|
268
|
+
const data = new Uint8Array(chunkSize)
|
|
269
|
+
let bytesRead = 0
|
|
189
270
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
271
|
+
do {
|
|
272
|
+
if (interrupted) break
|
|
273
|
+
const result = await device.read(data)
|
|
274
|
+
bytesRead = result.bytesRead
|
|
275
|
+
if (bytesRead > 0) {
|
|
276
|
+
buffer += decoder.decode(data.subarray(0, bytesRead), { stream: true })
|
|
277
|
+
const newLines = buffer.split('\n')
|
|
278
|
+
buffer = newLines.pop() || ''
|
|
279
|
+
lines.push(...newLines)
|
|
280
|
+
if (lines.length >= numLines) break
|
|
281
|
+
}
|
|
282
|
+
} while (bytesRead > 0 && lines.length < numLines)
|
|
283
|
+
|
|
284
|
+
if (buffer && lines.length < numLines) {
|
|
285
|
+
lines.push(buffer)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const output = lines.slice(0, numLines).join('\n')
|
|
289
|
+
if (output) {
|
|
290
|
+
await writer.write(new TextEncoder().encode(output + '\n'))
|
|
291
|
+
}
|
|
193
292
|
}
|
|
194
293
|
}
|
|
195
294
|
} finally {
|