@ecmaos/coreutils 0.5.1 → 0.5.3
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/dd.d.ts +4 -0
- package/dist/commands/dd.d.ts.map +1 -0
- package/dist/commands/dd.js +525 -0
- package/dist/commands/dd.js.map +1 -0
- package/dist/commands/git.d.ts +4 -0
- package/dist/commands/git.d.ts.map +1 -0
- package/dist/commands/git.js +708 -0
- package/dist/commands/git.js.map +1 -0
- package/dist/commands/ls.d.ts.map +1 -1
- package/dist/commands/ls.js +25 -19
- package/dist/commands/ls.js.map +1 -1
- package/dist/commands/lsx.d.ts +4 -0
- package/dist/commands/lsx.d.ts.map +1 -0
- package/dist/commands/lsx.js +308 -0
- package/dist/commands/lsx.js.map +1 -0
- package/dist/commands/mkdir.d.ts.map +1 -1
- package/dist/commands/mkdir.js +155 -10
- package/dist/commands/mkdir.js.map +1 -1
- package/dist/commands/sed.d.ts.map +1 -1
- package/dist/commands/sed.js +63 -63
- package/dist/commands/sed.js.map +1 -1
- package/dist/commands/view.d.ts.map +1 -1
- package/dist/commands/view.js +0 -2
- package/dist/commands/view.js.map +1 -1
- package/dist/index.d.ts +2 -93
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -94
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/commands/dd.ts +547 -0
- package/src/commands/git.ts +843 -0
- package/src/commands/ls.ts +26 -20
- package/src/commands/mkdir.ts +150 -10
- package/src/commands/sed.ts +69 -70
- package/src/commands/view.ts +0 -3
- package/src/index.ts +7 -95
package/src/commands/ls.ts
CHANGED
|
@@ -5,7 +5,6 @@ import humanFormat from 'human-format'
|
|
|
5
5
|
import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
6
6
|
import { TerminalCommand } from '../shared/terminal-command.js'
|
|
7
7
|
import { writelnStdout, writelnStderr } from '../shared/helpers.js'
|
|
8
|
-
import { CoreutilsDescriptions } from '../index.js'
|
|
9
8
|
|
|
10
9
|
function printUsage(process: Process | undefined, terminal: Terminal): void {
|
|
11
10
|
const usage = `Usage: ls [OPTION]... [FILE]...
|
|
@@ -64,8 +63,6 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
64
63
|
|
|
65
64
|
if (targets.length === 0) targets.push(shell.cwd)
|
|
66
65
|
|
|
67
|
-
const descriptions = kernel.filesystem.descriptions(kernel.i18n.t)
|
|
68
|
-
|
|
69
66
|
// Process each target and collect all entries
|
|
70
67
|
// We'll determine if each entry is a directory when we stat it later
|
|
71
68
|
const allEntries: Array<{ fullPath: string, entry: string }> = []
|
|
@@ -138,6 +135,8 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
138
135
|
return ''
|
|
139
136
|
}
|
|
140
137
|
|
|
138
|
+
const descriptions = kernel.filesystem.descriptions(kernel.i18n.ns.filesystem)
|
|
139
|
+
|
|
141
140
|
const filesMap = await Promise.all(allEntries
|
|
142
141
|
.map(async ({ fullPath: entryFullPath, entry }) => {
|
|
143
142
|
const target = path.resolve(entryFullPath, entry)
|
|
@@ -209,7 +208,9 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
209
208
|
|
|
210
209
|
// Check if any entry is in /dev directory
|
|
211
210
|
const isDevDirectory = allEntries.some(e => e.fullPath.startsWith('/dev'))
|
|
212
|
-
const columns = isDevDirectory
|
|
211
|
+
const columns = isDevDirectory
|
|
212
|
+
? [kernel.i18n.ns.common('Name'), kernel.i18n.ns.common('Mode'), kernel.i18n.ns.common('Owner'), kernel.i18n.ns.common('Info')]
|
|
213
|
+
: [kernel.i18n.ns.common('Name'), kernel.i18n.ns.common('Size'), kernel.i18n.ns.common('Modified'), kernel.i18n.ns.common('Mode'), kernel.i18n.ns.common('Owner'), kernel.i18n.ns.common('Info')]
|
|
213
214
|
|
|
214
215
|
const directoryRows = directories.sort((a, b) => a.name.localeCompare(b.name)).map(directory => {
|
|
215
216
|
const displayName = directory.linkTarget
|
|
@@ -229,15 +230,15 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
229
230
|
: chalk.green(displayName)
|
|
230
231
|
|
|
231
232
|
const row: Record<string, string> = {
|
|
232
|
-
Name: coloredName,
|
|
233
|
-
Mode: chalk.gray(modeString),
|
|
234
|
-
Owner: directory.stats ? chalk.gray(getOwnerString(directory.stats)) : '',
|
|
235
|
-
Info: truncateInfo(chalk.gray(linkInfo))
|
|
233
|
+
[kernel.i18n.ns.common('Name')]: coloredName,
|
|
234
|
+
[kernel.i18n.ns.common('Mode')]: chalk.gray(modeString),
|
|
235
|
+
[kernel.i18n.ns.common('Owner')]: directory.stats ? chalk.gray(getOwnerString(directory.stats)) : '',
|
|
236
|
+
[kernel.i18n.ns.common('Info')]: truncateInfo(chalk.gray(linkInfo))
|
|
236
237
|
}
|
|
237
238
|
|
|
238
239
|
if (!isDevDirectory) {
|
|
239
|
-
row.Size = ''
|
|
240
|
-
row.Modified = directory.stats ? chalk.gray(getTimestampString(directory.stats.mtime)) : ''
|
|
240
|
+
row[kernel.i18n.ns.common('Size')] = ''
|
|
241
|
+
row[kernel.i18n.ns.common('Modified')] = directory.stats ? chalk.gray(getTimestampString(directory.stats.mtime)) : ''
|
|
241
242
|
}
|
|
242
243
|
|
|
243
244
|
return row
|
|
@@ -263,11 +264,13 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
263
264
|
const linkInfo = getLinkInfo(file.linkTarget, file.linkStats, file.stats)
|
|
264
265
|
if (linkInfo) return linkInfo
|
|
265
266
|
|
|
266
|
-
// Check if this is a command in /bin/ and use
|
|
267
|
+
// Check if this is a command in /bin/ and use coreutils translations
|
|
267
268
|
if (file.target.startsWith('/bin/')) {
|
|
268
269
|
const commandName = path.basename(file.target)
|
|
269
|
-
|
|
270
|
-
|
|
270
|
+
const translatedDescription = kernel.i18n.ns.coreutils(commandName)
|
|
271
|
+
// Only use translation if it exists (i18next returns the key if translation is missing)
|
|
272
|
+
if (translatedDescription !== commandName) {
|
|
273
|
+
return translatedDescription
|
|
271
274
|
}
|
|
272
275
|
}
|
|
273
276
|
|
|
@@ -278,22 +281,25 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
278
281
|
}
|
|
279
282
|
if (!file.stats) return ''
|
|
280
283
|
if (file.stats.isBlockDevice() || file.stats.isCharacterDevice()) {
|
|
281
|
-
|
|
284
|
+
const devicePackage = kernel.devices.get(path.basename(file.target))
|
|
285
|
+
const description = devicePackage?.device?.pkg?.description || ''
|
|
286
|
+
const hasCLI = devicePackage?.device?.cli !== undefined
|
|
287
|
+
return `${description}${hasCLI ? ' ' + `${chalk.bold(`(${kernel.i18n.t('CLI')})`)}${chalk.reset()}` : ''}`
|
|
282
288
|
}
|
|
283
289
|
|
|
284
290
|
return ''
|
|
285
291
|
})()
|
|
286
292
|
|
|
287
293
|
const row: Record<string, string> = {
|
|
288
|
-
Name: coloredName,
|
|
289
|
-
Mode: chalk.gray(modeString),
|
|
290
|
-
Owner: file.stats ? chalk.gray(getOwnerString(file.stats)) : '',
|
|
291
|
-
Info: truncateInfo(chalk.gray(info))
|
|
294
|
+
[kernel.i18n.ns.common('Name')]: coloredName,
|
|
295
|
+
[kernel.i18n.ns.common('Mode')]: chalk.gray(modeString),
|
|
296
|
+
[kernel.i18n.ns.common('Owner')]: file.stats ? chalk.gray(getOwnerString(file.stats)) : '',
|
|
297
|
+
[kernel.i18n.ns.common('Info')]: truncateInfo(chalk.gray(info))
|
|
292
298
|
}
|
|
293
299
|
|
|
294
300
|
if (!isDevDirectory) {
|
|
295
|
-
row.Size = file.stats ? chalk.gray(humanFormat(file.stats.size)) : ''
|
|
296
|
-
row.Modified = file.stats ? chalk.gray(getTimestampString(file.stats.mtime)) : ''
|
|
301
|
+
row[kernel.i18n.ns.common('Size')] = file.stats ? chalk.gray(humanFormat(file.stats.size)) : ''
|
|
302
|
+
row[kernel.i18n.ns.common('Modified')] = file.stats ? chalk.gray(getTimestampString(file.stats.mtime)) : ''
|
|
297
303
|
}
|
|
298
304
|
|
|
299
305
|
return row
|
package/src/commands/mkdir.ts
CHANGED
|
@@ -1,16 +1,31 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
3
3
|
import { TerminalCommand } from '../shared/terminal-command.js'
|
|
4
|
-
import { writelnStderr } from '../shared/helpers.js'
|
|
4
|
+
import { writelnStderr, writelnStdout } from '../shared/helpers.js'
|
|
5
5
|
|
|
6
6
|
function printUsage(process: Process | undefined, terminal: Terminal): void {
|
|
7
7
|
const usage = `Usage: mkdir [OPTION]... DIRECTORY...
|
|
8
8
|
Create the DIRECTORY(ies), if they do not already exist.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Mandatory arguments to long options are mandatory for short options too.
|
|
11
|
+
-m, --mode=MODE set file mode (as in chmod), not a=rwx - umask
|
|
12
|
+
-p, --parents no error if existing, make parent directories as needed,
|
|
13
|
+
with their file modes unaffected by any -m option.
|
|
14
|
+
-v, --verbose print a message for each created directory
|
|
15
|
+
--help display this help and exit`
|
|
11
16
|
writelnStderr(process, terminal, usage)
|
|
12
17
|
}
|
|
13
18
|
|
|
19
|
+
function parseNumericMode(mode: string): number | null {
|
|
20
|
+
if (/^0?[0-7]{1,4}$/.test(mode)) {
|
|
21
|
+
return parseInt(mode, 8)
|
|
22
|
+
}
|
|
23
|
+
if (/^0o[0-7]{1,4}$/i.test(mode)) {
|
|
24
|
+
return parseInt(mode.slice(2), 8)
|
|
25
|
+
}
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
|
|
14
29
|
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
15
30
|
return new TerminalCommand({
|
|
16
31
|
command: 'mkdir',
|
|
@@ -26,7 +41,104 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
26
41
|
return 0
|
|
27
42
|
}
|
|
28
43
|
|
|
29
|
-
|
|
44
|
+
let parents = false
|
|
45
|
+
let verbose = false
|
|
46
|
+
let mode: number | undefined = undefined
|
|
47
|
+
const directories: string[] = []
|
|
48
|
+
|
|
49
|
+
let i = 0
|
|
50
|
+
while (i < argv.length) {
|
|
51
|
+
const arg = argv[i]
|
|
52
|
+
if (!arg) {
|
|
53
|
+
i++
|
|
54
|
+
continue
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (arg === '--') {
|
|
58
|
+
i++
|
|
59
|
+
while (i < argv.length) {
|
|
60
|
+
const dirArg = argv[i]
|
|
61
|
+
if (dirArg) {
|
|
62
|
+
directories.push(dirArg)
|
|
63
|
+
}
|
|
64
|
+
i++
|
|
65
|
+
}
|
|
66
|
+
break
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (arg.startsWith('--')) {
|
|
70
|
+
if (arg === '--parents') {
|
|
71
|
+
parents = true
|
|
72
|
+
} else if (arg === '--verbose') {
|
|
73
|
+
verbose = true
|
|
74
|
+
} else if (arg.startsWith('--mode=')) {
|
|
75
|
+
const modeStr = arg.slice(7)
|
|
76
|
+
const parsedMode = parseNumericMode(modeStr)
|
|
77
|
+
if (parsedMode === null) {
|
|
78
|
+
await writelnStderr(process, terminal, `mkdir: invalid mode '${modeStr}'`)
|
|
79
|
+
return 1
|
|
80
|
+
}
|
|
81
|
+
mode = parsedMode
|
|
82
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
83
|
+
printUsage(process, terminal)
|
|
84
|
+
return 0
|
|
85
|
+
} else {
|
|
86
|
+
await writelnStderr(process, terminal, `mkdir: unrecognized option '${arg}'`)
|
|
87
|
+
await writelnStderr(process, terminal, "Try 'mkdir --help' for more information.")
|
|
88
|
+
return 1
|
|
89
|
+
}
|
|
90
|
+
} else if (arg.startsWith('-') && arg.length > 1) {
|
|
91
|
+
for (let j = 1; j < arg.length; j++) {
|
|
92
|
+
const flag = arg[j]
|
|
93
|
+
if (!flag) continue
|
|
94
|
+
|
|
95
|
+
if (flag === 'p') {
|
|
96
|
+
parents = true
|
|
97
|
+
} else if (flag === 'v') {
|
|
98
|
+
verbose = true
|
|
99
|
+
} else if (flag === 'm') {
|
|
100
|
+
if (j + 1 < arg.length) {
|
|
101
|
+
const modeStr = arg.slice(j + 1)
|
|
102
|
+
const parsedMode = parseNumericMode(modeStr)
|
|
103
|
+
if (parsedMode === null) {
|
|
104
|
+
await writelnStderr(process, terminal, `mkdir: invalid mode '${modeStr}'`)
|
|
105
|
+
return 1
|
|
106
|
+
}
|
|
107
|
+
mode = parsedMode
|
|
108
|
+
break
|
|
109
|
+
} else if (i + 1 < argv.length) {
|
|
110
|
+
const modeStr = argv[i + 1]
|
|
111
|
+
if (!modeStr) {
|
|
112
|
+
await writelnStderr(process, terminal, "mkdir: option requires an argument -- 'm'")
|
|
113
|
+
await writelnStderr(process, terminal, "Try 'mkdir --help' for more information.")
|
|
114
|
+
return 1
|
|
115
|
+
}
|
|
116
|
+
const parsedMode = parseNumericMode(modeStr)
|
|
117
|
+
if (parsedMode === null) {
|
|
118
|
+
await writelnStderr(process, terminal, `mkdir: invalid mode '${modeStr}'`)
|
|
119
|
+
return 1
|
|
120
|
+
}
|
|
121
|
+
mode = parsedMode
|
|
122
|
+
i++
|
|
123
|
+
break
|
|
124
|
+
} else {
|
|
125
|
+
await writelnStderr(process, terminal, "mkdir: option requires an argument -- 'm'")
|
|
126
|
+
await writelnStderr(process, terminal, "Try 'mkdir --help' for more information.")
|
|
127
|
+
return 1
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
await writelnStderr(process, terminal, `mkdir: invalid option -- '${flag}'`)
|
|
131
|
+
await writelnStderr(process, terminal, "Try 'mkdir --help' for more information.")
|
|
132
|
+
return 1
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
directories.push(arg)
|
|
137
|
+
}
|
|
138
|
+
i++
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (directories.length === 0) {
|
|
30
142
|
await writelnStderr(process, terminal, 'mkdir: missing operand')
|
|
31
143
|
await writelnStderr(process, terminal, "Try 'mkdir --help' for more information.")
|
|
32
144
|
return 1
|
|
@@ -34,17 +146,45 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
34
146
|
|
|
35
147
|
let hasError = false
|
|
36
148
|
|
|
37
|
-
for (const target of
|
|
38
|
-
if (!target
|
|
149
|
+
for (const target of directories) {
|
|
150
|
+
if (!target) continue
|
|
39
151
|
|
|
40
|
-
const fullPath =
|
|
152
|
+
const fullPath = path.resolve(shell.cwd, target)
|
|
41
153
|
|
|
42
154
|
try {
|
|
43
|
-
|
|
155
|
+
const mkdirOptions: { recursive?: boolean; mode?: number } = {}
|
|
156
|
+
if (parents) {
|
|
157
|
+
mkdirOptions.recursive = true
|
|
158
|
+
}
|
|
159
|
+
if (mode !== undefined) {
|
|
160
|
+
mkdirOptions.mode = mode
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
let existedBefore = false
|
|
164
|
+
if (parents) {
|
|
165
|
+
try {
|
|
166
|
+
await shell.context.fs.promises.stat(fullPath)
|
|
167
|
+
existedBefore = true
|
|
168
|
+
} catch {
|
|
169
|
+
existedBefore = false
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
await shell.context.fs.promises.mkdir(fullPath, mkdirOptions)
|
|
174
|
+
|
|
175
|
+
if (verbose && !existedBefore) {
|
|
176
|
+
const relativePath = path.relative(shell.cwd, fullPath) || target
|
|
177
|
+
await writelnStdout(process, terminal, `mkdir: created directory '${relativePath}'`)
|
|
178
|
+
}
|
|
44
179
|
} catch (error) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
180
|
+
const err = error as { code?: string; message?: string }
|
|
181
|
+
if (parents && err.code === 'EEXIST') {
|
|
182
|
+
continue
|
|
183
|
+
} else {
|
|
184
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
185
|
+
await writelnStderr(process, terminal, `mkdir: ${target}: ${errorMessage}`)
|
|
186
|
+
hasError = true
|
|
187
|
+
}
|
|
48
188
|
}
|
|
49
189
|
}
|
|
50
190
|
|
package/src/commands/sed.ts
CHANGED
|
@@ -236,9 +236,9 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
236
236
|
const trimmed = arg.trim()
|
|
237
237
|
return (
|
|
238
238
|
trimmed.startsWith('s/') ||
|
|
239
|
-
trimmed
|
|
240
|
-
/^\d+[sd]
|
|
241
|
-
/^\d+,\d*[sd]
|
|
239
|
+
/^\/.+?\/[dp]$/.test(trimmed) ||
|
|
240
|
+
/^\d+[sd]$/.test(trimmed) ||
|
|
241
|
+
/^\d+,\d*[sd]$/.test(trimmed) ||
|
|
242
242
|
/^\d+s\//.test(trimmed) ||
|
|
243
243
|
/^\d+,\d*s\//.test(trimmed)
|
|
244
244
|
)
|
|
@@ -341,73 +341,6 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
341
341
|
return content.split('\n')
|
|
342
342
|
}
|
|
343
343
|
|
|
344
|
-
let inputLines: string[] = []
|
|
345
|
-
|
|
346
|
-
if (files.length > 0) {
|
|
347
|
-
for (const file of files) {
|
|
348
|
-
const expandedPath = shell.expandTilde(file)
|
|
349
|
-
const fullPath = path.resolve(shell.cwd, expandedPath)
|
|
350
|
-
const lines = await processFile(fullPath)
|
|
351
|
-
inputLines.push(...lines)
|
|
352
|
-
if (lines.length > 0 && inputLines.length > lines.length) {
|
|
353
|
-
inputLines.push('')
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
} else {
|
|
357
|
-
if (!process.stdin) {
|
|
358
|
-
await writelnStderr(process, terminal, 'sed: No input provided')
|
|
359
|
-
return 1
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const reader = process.stdin.getReader()
|
|
363
|
-
const decoder = new TextDecoder()
|
|
364
|
-
const chunks: string[] = []
|
|
365
|
-
|
|
366
|
-
try {
|
|
367
|
-
while (true) {
|
|
368
|
-
const { done, value } = await reader.read()
|
|
369
|
-
if (done) break
|
|
370
|
-
chunks.push(decoder.decode(value, { stream: true }))
|
|
371
|
-
}
|
|
372
|
-
chunks.push(decoder.decode(new Uint8Array(), { stream: false }))
|
|
373
|
-
} finally {
|
|
374
|
-
reader.releaseLock()
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
const content = chunks.join('')
|
|
378
|
-
inputLines = content.split('\n')
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
const outputLines: string[] = []
|
|
382
|
-
const totalLines = inputLines.length
|
|
383
|
-
|
|
384
|
-
for (let i = 0; i < inputLines.length; i++) {
|
|
385
|
-
let line = inputLines[i] || ''
|
|
386
|
-
let lineNum = i + 1
|
|
387
|
-
let shouldPrint = false
|
|
388
|
-
|
|
389
|
-
for (const command of commands) {
|
|
390
|
-
const { result, shouldPrint: print } = applySedCommand(line, lineNum, totalLines, command)
|
|
391
|
-
if (result === null) {
|
|
392
|
-
line = null as unknown as string
|
|
393
|
-
break
|
|
394
|
-
}
|
|
395
|
-
line = result
|
|
396
|
-
if (print) {
|
|
397
|
-
shouldPrint = true
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
if (line !== null) {
|
|
402
|
-
outputLines.push(line)
|
|
403
|
-
if (shouldPrint && !quiet) {
|
|
404
|
-
outputLines.push(line)
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
const output = outputLines.join('\n')
|
|
410
|
-
|
|
411
344
|
if (inplace !== undefined && files.length > 0) {
|
|
412
345
|
for (const file of files) {
|
|
413
346
|
const expandedPath = shell.expandTilde(file)
|
|
@@ -455,6 +388,72 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
455
388
|
await shell.context.fs.promises.writeFile(fullPath, fileOutput)
|
|
456
389
|
}
|
|
457
390
|
} else {
|
|
391
|
+
let inputLines: string[] = []
|
|
392
|
+
|
|
393
|
+
if (files.length > 0) {
|
|
394
|
+
for (const file of files) {
|
|
395
|
+
const expandedPath = shell.expandTilde(file)
|
|
396
|
+
const fullPath = path.resolve(shell.cwd, expandedPath)
|
|
397
|
+
const lines = await processFile(fullPath)
|
|
398
|
+
inputLines.push(...lines)
|
|
399
|
+
if (lines.length > 0 && inputLines.length > lines.length) {
|
|
400
|
+
inputLines.push('')
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
} else {
|
|
404
|
+
if (!process.stdin) {
|
|
405
|
+
await writelnStderr(process, terminal, 'sed: No input provided')
|
|
406
|
+
return 1
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const reader = process.stdin.getReader()
|
|
410
|
+
const decoder = new TextDecoder()
|
|
411
|
+
const chunks: string[] = []
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
while (true) {
|
|
415
|
+
const { done, value } = await reader.read()
|
|
416
|
+
if (done) break
|
|
417
|
+
chunks.push(decoder.decode(value, { stream: true }))
|
|
418
|
+
}
|
|
419
|
+
chunks.push(decoder.decode(new Uint8Array(), { stream: false }))
|
|
420
|
+
} finally {
|
|
421
|
+
reader.releaseLock()
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const content = chunks.join('')
|
|
425
|
+
inputLines = content.split('\n')
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const outputLines: string[] = []
|
|
429
|
+
const totalLines = inputLines.length
|
|
430
|
+
|
|
431
|
+
for (let i = 0; i < inputLines.length; i++) {
|
|
432
|
+
let line = inputLines[i] || ''
|
|
433
|
+
let lineNum = i + 1
|
|
434
|
+
let shouldPrint = false
|
|
435
|
+
|
|
436
|
+
for (const command of commands) {
|
|
437
|
+
const { result, shouldPrint: print } = applySedCommand(line, lineNum, totalLines, command)
|
|
438
|
+
if (result === null) {
|
|
439
|
+
line = null as unknown as string
|
|
440
|
+
break
|
|
441
|
+
}
|
|
442
|
+
line = result
|
|
443
|
+
if (print) {
|
|
444
|
+
shouldPrint = true
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (line !== null) {
|
|
449
|
+
outputLines.push(line)
|
|
450
|
+
if (shouldPrint && !quiet) {
|
|
451
|
+
outputLines.push(line)
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const output = outputLines.join('\n')
|
|
458
457
|
await writer.write(new TextEncoder().encode(output))
|
|
459
458
|
}
|
|
460
459
|
|
package/src/commands/view.ts
CHANGED
|
@@ -306,7 +306,6 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
306
306
|
}
|
|
307
307
|
|
|
308
308
|
// Read file
|
|
309
|
-
await writelnStdout(process, terminal, chalk.blue(`Loading: ${file}...`))
|
|
310
309
|
const fileData = await shell.context.fs.promises.readFile(fullPath)
|
|
311
310
|
const fileType = detectFileType(fullPath)
|
|
312
311
|
const mimeType = getMimeType(fullPath, fileType)
|
|
@@ -374,8 +373,6 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
374
373
|
})
|
|
375
374
|
|
|
376
375
|
win.mount(container)
|
|
377
|
-
|
|
378
|
-
await writelnStdout(process, terminal, chalk.green(`Viewing: ${file}`))
|
|
379
376
|
} else if (fileType === 'json') {
|
|
380
377
|
// Read and parse JSON file
|
|
381
378
|
const jsonText = new TextDecoder().decode(fileData)
|
package/src/index.ts
CHANGED
|
@@ -61,6 +61,7 @@ import { createCommand as createColumn } from './commands/column.js'
|
|
|
61
61
|
import { createCommand as createComm } from './commands/comm.js'
|
|
62
62
|
import { createCommand as createCurl } from './commands/curl.js'
|
|
63
63
|
import { createCommand as createCut } from './commands/cut.js'
|
|
64
|
+
import { createCommand as createDd } from './commands/dd.js'
|
|
64
65
|
import { createCommand as createDate } from './commands/date.js'
|
|
65
66
|
import { createCommand as createDiff } from './commands/diff.js'
|
|
66
67
|
import { createCommand as createDirname } from './commands/dirname.js'
|
|
@@ -98,6 +99,7 @@ import { createCommand as createZip } from './commands/zip.js'
|
|
|
98
99
|
import { createCommand as createUnzip } from './commands/unzip.js'
|
|
99
100
|
import { createCommand as createVideo } from './commands/video.js'
|
|
100
101
|
import { createCommand as createView } from './commands/view.js'
|
|
102
|
+
import { createCommand as createGit } from './commands/git.js'
|
|
101
103
|
|
|
102
104
|
// Export individual command factories
|
|
103
105
|
export { createCommand as createCat } from './commands/cat.js'
|
|
@@ -147,6 +149,7 @@ export { createCommand as createCmp } from './commands/cmp.js'
|
|
|
147
149
|
export { createCommand as createColumn } from './commands/column.js'
|
|
148
150
|
export { createCommand as createCurl } from './commands/curl.js'
|
|
149
151
|
export { createCommand as createCut } from './commands/cut.js'
|
|
152
|
+
export { createCommand as createDd } from './commands/dd.js'
|
|
150
153
|
export { createCommand as createDate } from './commands/date.js'
|
|
151
154
|
export { createCommand as createDiff } from './commands/diff.js'
|
|
152
155
|
export { createCommand as createDirname } from './commands/dirname.js'
|
|
@@ -172,6 +175,7 @@ export { createCommand as createZip } from './commands/zip.js'
|
|
|
172
175
|
export { createCommand as createUnzip } from './commands/unzip.js'
|
|
173
176
|
export { createCommand as createVideo } from './commands/video.js'
|
|
174
177
|
export { createCommand as createView } from './commands/view.js'
|
|
178
|
+
export { createCommand as createGit } from './commands/git.js'
|
|
175
179
|
|
|
176
180
|
/**
|
|
177
181
|
* Creates all coreutils commands.
|
|
@@ -233,6 +237,7 @@ export function createAllCommands(kernel: Kernel, shell: Shell, terminal: Termin
|
|
|
233
237
|
comm: createComm(kernel, shell, terminal),
|
|
234
238
|
curl: createCurl(kernel, shell, terminal),
|
|
235
239
|
cut: createCut(kernel, shell, terminal),
|
|
240
|
+
dd: createDd(kernel, shell, terminal),
|
|
236
241
|
date: createDate(kernel, shell, terminal),
|
|
237
242
|
diff: createDiff(kernel, shell, terminal),
|
|
238
243
|
dirname: createDirname(kernel, shell, terminal),
|
|
@@ -269,103 +274,10 @@ export function createAllCommands(kernel: Kernel, shell: Shell, terminal: Termin
|
|
|
269
274
|
zip: createZip(kernel, shell, terminal),
|
|
270
275
|
unzip: createUnzip(kernel, shell, terminal),
|
|
271
276
|
video: createVideo(kernel, shell, terminal),
|
|
272
|
-
view: createView(kernel, shell, terminal)
|
|
277
|
+
view: createView(kernel, shell, terminal),
|
|
278
|
+
git: createGit(kernel, shell, terminal)
|
|
273
279
|
}
|
|
274
280
|
}
|
|
275
281
|
|
|
276
282
|
// For backward compatibility, export as TerminalCommands
|
|
277
283
|
export { createAllCommands as TerminalCommands }
|
|
278
|
-
|
|
279
|
-
export const CoreutilsDescriptions = {
|
|
280
|
-
awk: 'Pattern scanning and text processing language',
|
|
281
|
-
basename: 'Strip directory and suffix from filenames',
|
|
282
|
-
cal: 'Display a calendar',
|
|
283
|
-
cat: 'Concatenate files and print on the standard output',
|
|
284
|
-
cd: 'Change the shell working directory',
|
|
285
|
-
chmod: 'Change file mode bits',
|
|
286
|
-
chown: 'Change file owner and group',
|
|
287
|
-
cksum: 'Print CRC checksum and byte count',
|
|
288
|
-
cmp: 'Compare two files byte by byte',
|
|
289
|
-
column: 'Format input into columns',
|
|
290
|
-
comm: 'Compare two sorted files line by line',
|
|
291
|
-
cp: 'Copy files',
|
|
292
|
-
cron: 'Manage scheduled tasks (crontabs)',
|
|
293
|
-
curl: 'Transfer data from or to a server',
|
|
294
|
-
cut: 'Remove sections from each line of files',
|
|
295
|
-
date: 'Print or set the system date and time',
|
|
296
|
-
diff: 'Compare files line by line',
|
|
297
|
-
dirname: 'Strip last component from file path',
|
|
298
|
-
echo: 'Print arguments to the standard output',
|
|
299
|
-
env: 'Run a program in a modified environment',
|
|
300
|
-
expand: 'Convert tabs to spaces',
|
|
301
|
-
factor: 'Print prime factors of numbers',
|
|
302
|
-
false: 'Return an unsuccessful exit status',
|
|
303
|
-
fetch: 'Fetch a resource from the network',
|
|
304
|
-
find: 'Search for files in a directory hierarchy',
|
|
305
|
-
fmt: 'Reformat paragraph text',
|
|
306
|
-
fold: 'Wrap each input line to fit in specified width',
|
|
307
|
-
format: 'Delete all IndexedDB and localStorage data',
|
|
308
|
-
grep: 'Search for patterns in files or standard input',
|
|
309
|
-
groups: 'Print the groups a user belongs to',
|
|
310
|
-
hash: 'Compute and display hash values for files or standard input',
|
|
311
|
-
head: 'Print the first lines of files',
|
|
312
|
-
hostname: 'Print the system hostname',
|
|
313
|
-
id: 'Print user and group IDs',
|
|
314
|
-
join: 'Join lines of two files on a common field',
|
|
315
|
-
less: 'View file contents interactively',
|
|
316
|
-
ln: 'Create links between files',
|
|
317
|
-
ls: 'List directory contents',
|
|
318
|
-
man: 'Display manual pages',
|
|
319
|
-
mkdir: 'Create a directory',
|
|
320
|
-
mktemp: 'Create a temporary file or directory',
|
|
321
|
-
mount: 'Mount a filesystem',
|
|
322
|
-
mv: 'Move or rename files',
|
|
323
|
-
nc: 'Network utilities',
|
|
324
|
-
nl: 'Number lines of files',
|
|
325
|
-
od: 'Dump files in octal and other formats',
|
|
326
|
-
open: 'Open a file or URL',
|
|
327
|
-
passkey: 'Manage passkey credentials for WebAuthn authentication',
|
|
328
|
-
paste: 'Merge lines of files',
|
|
329
|
-
play: 'Play an audio file',
|
|
330
|
-
pr: 'Paginate or columnate files for printing',
|
|
331
|
-
printf: 'Format and print data',
|
|
332
|
-
pwd: 'Print the shell working directory',
|
|
333
|
-
readlink: 'Print value of a symbolic link or canonical file name',
|
|
334
|
-
realpath: 'Print the resolved absolute file name',
|
|
335
|
-
rev: 'Reverse the characters of each line',
|
|
336
|
-
rm: 'Remove files or directories',
|
|
337
|
-
rmdir: 'Remove a directory',
|
|
338
|
-
sed: 'Stream editor for filtering and transforming text',
|
|
339
|
-
seq: 'Print a sequence of numbers',
|
|
340
|
-
shuf: 'Write a random permutation of the input lines',
|
|
341
|
-
sleep: 'Delay for a specified amount of time',
|
|
342
|
-
sockets: 'Manage socket connections (WebSocket and WebTransport)',
|
|
343
|
-
sort: 'Sort lines of text files',
|
|
344
|
-
split: 'Split a file into pieces',
|
|
345
|
-
stat: 'Display information about a file or directory',
|
|
346
|
-
strings: 'Print the sequences of printable characters in files',
|
|
347
|
-
tac: 'Write FILE in reverse line order',
|
|
348
|
-
tail: 'Print the last lines of files',
|
|
349
|
-
tar: 'Create, extract, or list tar archives',
|
|
350
|
-
tee: 'Read from standard input and write to standard output and files',
|
|
351
|
-
test: 'Check file types and compare values',
|
|
352
|
-
time: 'Measure command execution time',
|
|
353
|
-
touch: 'Create an empty file',
|
|
354
|
-
tr: 'Translate or delete characters',
|
|
355
|
-
true: 'Return a successful exit status',
|
|
356
|
-
umount: 'Unmount a filesystem',
|
|
357
|
-
uname: 'Print system information',
|
|
358
|
-
unexpand: 'Convert spaces to tabs',
|
|
359
|
-
uniq: 'Report or omit repeated lines',
|
|
360
|
-
unzip: 'Extract zip archives',
|
|
361
|
-
uptime: 'Print how long the system has been running',
|
|
362
|
-
user: 'Manage users on the system',
|
|
363
|
-
video: 'Play a video file',
|
|
364
|
-
view: 'View files in a new window (PDF, images, audio, video)',
|
|
365
|
-
wc: 'Print newline, word, and byte counts for each file',
|
|
366
|
-
web: 'Open a URL in a browser window',
|
|
367
|
-
which: 'Locate a command',
|
|
368
|
-
whoami: 'Print effective user ID',
|
|
369
|
-
xxd: 'Display file contents or stdin in hexadecimal format',
|
|
370
|
-
zip: 'Create zip archives'
|
|
371
|
-
}
|