@ecmaos/coreutils 0.1.4 → 0.2.0
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 +55 -0
- package/dist/commands/basename.d.ts +4 -0
- package/dist/commands/basename.d.ts.map +1 -0
- package/dist/commands/basename.js +78 -0
- package/dist/commands/basename.js.map +1 -0
- package/dist/commands/cal.d.ts +4 -0
- package/dist/commands/cal.d.ts.map +1 -0
- package/dist/commands/cal.js +105 -0
- package/dist/commands/cal.js.map +1 -0
- package/dist/commands/cat.d.ts.map +1 -1
- package/dist/commands/cat.js +49 -22
- package/dist/commands/cat.js.map +1 -1
- package/dist/commands/cd.d.ts.map +1 -1
- package/dist/commands/cd.js +38 -10
- package/dist/commands/cd.js.map +1 -1
- package/dist/commands/chmod.d.ts.map +1 -1
- package/dist/commands/chmod.js +31 -9
- package/dist/commands/chmod.js.map +1 -1
- package/dist/commands/comm.d.ts +4 -0
- package/dist/commands/comm.d.ts.map +1 -0
- package/dist/commands/comm.js +162 -0
- package/dist/commands/comm.js.map +1 -0
- package/dist/commands/cp.d.ts.map +1 -1
- package/dist/commands/cp.js +61 -12
- package/dist/commands/cp.js.map +1 -1
- package/dist/commands/cut.d.ts +4 -0
- package/dist/commands/cut.d.ts.map +1 -0
- package/dist/commands/cut.js +208 -0
- package/dist/commands/cut.js.map +1 -0
- package/dist/commands/date.d.ts +4 -0
- package/dist/commands/date.d.ts.map +1 -0
- package/dist/commands/date.js +100 -0
- package/dist/commands/date.js.map +1 -0
- package/dist/commands/diff.d.ts +4 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +194 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/dirname.d.ts +4 -0
- package/dist/commands/dirname.d.ts.map +1 -0
- package/dist/commands/dirname.js +50 -0
- package/dist/commands/dirname.js.map +1 -0
- package/dist/commands/echo.d.ts.map +1 -1
- package/dist/commands/echo.js +51 -9
- package/dist/commands/echo.js.map +1 -1
- package/dist/commands/false.d.ts +4 -0
- package/dist/commands/false.d.ts.map +1 -0
- package/dist/commands/false.js +27 -0
- package/dist/commands/false.js.map +1 -0
- package/dist/commands/find.d.ts +4 -0
- package/dist/commands/find.d.ts.map +1 -0
- package/dist/commands/find.js +181 -0
- package/dist/commands/find.js.map +1 -0
- package/dist/commands/grep.d.ts +4 -0
- package/dist/commands/grep.d.ts.map +1 -0
- package/dist/commands/grep.js +175 -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 +199 -0
- package/dist/commands/head.js.map +1 -0
- package/dist/commands/hex.d.ts.map +1 -1
- package/dist/commands/hex.js +82 -20
- package/dist/commands/hex.js.map +1 -1
- package/dist/commands/id.d.ts +4 -0
- package/dist/commands/id.d.ts.map +1 -0
- package/dist/commands/id.js +97 -0
- package/dist/commands/id.js.map +1 -0
- package/dist/commands/join.d.ts +4 -0
- package/dist/commands/join.d.ts.map +1 -0
- package/dist/commands/join.js +152 -0
- package/dist/commands/join.js.map +1 -0
- package/dist/commands/less.d.ts.map +1 -1
- package/dist/commands/less.js +16 -7
- package/dist/commands/less.js.map +1 -1
- package/dist/commands/ln.d.ts.map +1 -1
- package/dist/commands/ln.js +54 -12
- package/dist/commands/ln.js.map +1 -1
- package/dist/commands/ls.d.ts.map +1 -1
- package/dist/commands/ls.js +19 -9
- package/dist/commands/ls.js.map +1 -1
- package/dist/commands/mkdir.d.ts.map +1 -1
- package/dist/commands/mkdir.js +34 -9
- package/dist/commands/mkdir.js.map +1 -1
- package/dist/commands/mv.d.ts.map +1 -1
- package/dist/commands/mv.js +25 -7
- package/dist/commands/mv.js.map +1 -1
- package/dist/commands/nc.d.ts +4 -0
- package/dist/commands/nc.d.ts.map +1 -0
- package/dist/commands/nc.js +451 -0
- package/dist/commands/nc.js.map +1 -0
- package/dist/commands/nl.d.ts +4 -0
- package/dist/commands/nl.d.ts.map +1 -0
- package/dist/commands/nl.js +208 -0
- package/dist/commands/nl.js.map +1 -0
- package/dist/commands/passkey.d.ts.map +1 -1
- package/dist/commands/passkey.js +44 -18
- package/dist/commands/passkey.js.map +1 -1
- package/dist/commands/paste.d.ts +4 -0
- package/dist/commands/paste.d.ts.map +1 -0
- package/dist/commands/paste.js +161 -0
- package/dist/commands/paste.js.map +1 -0
- package/dist/commands/pwd.d.ts.map +1 -1
- package/dist/commands/pwd.js +13 -4
- package/dist/commands/pwd.js.map +1 -1
- package/dist/commands/rm.d.ts.map +1 -1
- package/dist/commands/rm.js +105 -12
- package/dist/commands/rm.js.map +1 -1
- package/dist/commands/rmdir.d.ts.map +1 -1
- package/dist/commands/rmdir.js +34 -9
- package/dist/commands/rmdir.js.map +1 -1
- package/dist/commands/sed.d.ts.map +1 -1
- package/dist/commands/sed.js +73 -28
- package/dist/commands/sed.js.map +1 -1
- package/dist/commands/seq.d.ts +4 -0
- package/dist/commands/seq.d.ts.map +1 -0
- package/dist/commands/seq.js +148 -0
- package/dist/commands/seq.js.map +1 -0
- package/dist/commands/sockets.d.ts +4 -0
- package/dist/commands/sockets.d.ts.map +1 -0
- package/dist/commands/sockets.js +239 -0
- package/dist/commands/sockets.js.map +1 -0
- package/dist/commands/sort.d.ts +4 -0
- package/dist/commands/sort.d.ts.map +1 -0
- package/dist/commands/sort.js +175 -0
- package/dist/commands/sort.js.map +1 -0
- package/dist/commands/split.d.ts +4 -0
- package/dist/commands/split.d.ts.map +1 -0
- package/dist/commands/split.js +147 -0
- package/dist/commands/split.js.map +1 -0
- package/dist/commands/stat.d.ts.map +1 -1
- package/dist/commands/stat.js +14 -6
- package/dist/commands/stat.js.map +1 -1
- package/dist/commands/tail.d.ts +4 -0
- package/dist/commands/tail.d.ts.map +1 -0
- package/dist/commands/tail.js +189 -0
- package/dist/commands/tail.js.map +1 -0
- package/dist/commands/tee.d.ts.map +1 -1
- package/dist/commands/tee.js +45 -10
- package/dist/commands/tee.js.map +1 -1
- package/dist/commands/test.d.ts +4 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +99 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/touch.d.ts.map +1 -1
- package/dist/commands/touch.js +34 -9
- package/dist/commands/touch.js.map +1 -1
- package/dist/commands/tr.d.ts +4 -0
- package/dist/commands/tr.d.ts.map +1 -0
- package/dist/commands/tr.js +162 -0
- package/dist/commands/tr.js.map +1 -0
- package/dist/commands/true.d.ts +4 -0
- package/dist/commands/true.d.ts.map +1 -0
- package/dist/commands/true.js +27 -0
- package/dist/commands/true.js.map +1 -0
- package/dist/commands/uniq.d.ts +4 -0
- package/dist/commands/uniq.d.ts.map +1 -0
- package/dist/commands/uniq.js +187 -0
- package/dist/commands/uniq.js.map +1 -0
- package/dist/commands/wc.d.ts +4 -0
- package/dist/commands/wc.d.ts.map +1 -0
- package/dist/commands/wc.js +178 -0
- package/dist/commands/wc.js.map +1 -0
- package/dist/commands/which.d.ts +4 -0
- package/dist/commands/which.d.ts.map +1 -0
- package/dist/commands/which.js +78 -0
- package/dist/commands/which.js.map +1 -0
- package/dist/commands/whoami.d.ts +4 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +28 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +72 -1
- package/dist/index.js.map +1 -1
- package/dist/shared/terminal-command.d.ts +8 -2
- package/dist/shared/terminal-command.d.ts.map +1 -1
- package/dist/shared/terminal-command.js +42 -17
- package/dist/shared/terminal-command.js.map +1 -1
- package/package.json +4 -2
- package/src/commands/basename.ts +84 -0
- package/src/commands/cal.ts +111 -0
- package/src/commands/cat.ts +55 -24
- package/src/commands/cd.ts +40 -12
- package/src/commands/chmod.ts +37 -11
- package/src/commands/comm.ts +169 -0
- package/src/commands/cp.ts +73 -15
- package/src/commands/cut.ts +214 -0
- package/src/commands/date.ts +97 -0
- package/src/commands/diff.ts +204 -0
- package/src/commands/dirname.ts +57 -0
- package/src/commands/echo.ts +53 -10
- package/src/commands/false.ts +31 -0
- package/src/commands/find.ts +184 -0
- package/src/commands/grep.ts +187 -0
- package/src/commands/head.ts +206 -0
- package/src/commands/hex.ts +93 -25
- package/src/commands/id.ts +94 -0
- package/src/commands/join.ts +162 -0
- package/src/commands/less.ts +20 -8
- package/src/commands/ln.ts +50 -13
- package/src/commands/ls.ts +21 -10
- package/src/commands/mkdir.ts +41 -12
- package/src/commands/mv.ts +31 -9
- package/src/commands/nc.ts +499 -0
- package/src/commands/nl.ts +201 -0
- package/src/commands/passkey.ts +46 -19
- package/src/commands/paste.ts +172 -0
- package/src/commands/pwd.ts +16 -4
- package/src/commands/rm.ts +118 -13
- package/src/commands/rmdir.ts +41 -12
- package/src/commands/sed.ts +64 -30
- package/src/commands/seq.ts +147 -0
- package/src/commands/sockets.ts +283 -0
- package/src/commands/sort.ts +175 -0
- package/src/commands/split.ts +154 -0
- package/src/commands/stat.ts +17 -8
- package/src/commands/tail.ts +200 -0
- package/src/commands/tee.ts +43 -11
- package/src/commands/test.ts +116 -0
- package/src/commands/touch.ts +41 -12
- package/src/commands/tr.ts +170 -0
- package/src/commands/true.ts +31 -0
- package/src/commands/uniq.ts +189 -0
- package/src/commands/wc.ts +181 -0
- package/src/commands/which.ts +89 -0
- package/src/commands/whoami.ts +32 -0
- package/src/index.ts +72 -1
- package/src/shared/terminal-command.ts +43 -16
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
3
|
+
import { TerminalCommand } from '../shared/terminal-command.js'
|
|
4
|
+
import { writelnStderr } from '../shared/helpers.js'
|
|
5
|
+
|
|
6
|
+
function printUsage(process: Process | undefined, terminal: Terminal): void {
|
|
7
|
+
const usage = `Usage: test EXPRESSION
|
|
8
|
+
test [OPTION]
|
|
9
|
+
Check file types and compare values.
|
|
10
|
+
|
|
11
|
+
-f FILE FILE exists and is a regular file
|
|
12
|
+
-d FILE FILE exists and is a directory
|
|
13
|
+
-e FILE FILE exists
|
|
14
|
+
-r FILE FILE exists and is readable
|
|
15
|
+
-w FILE FILE exists and is writable
|
|
16
|
+
-x FILE FILE exists and is executable
|
|
17
|
+
-n STRING STRING is not empty
|
|
18
|
+
-z STRING STRING is empty (zero length)
|
|
19
|
+
STRING1 = STRING2 strings are equal
|
|
20
|
+
STRING1 != STRING2 strings are not equal
|
|
21
|
+
--help display this help and exit`
|
|
22
|
+
writelnStderr(process, terminal, usage)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
26
|
+
return new TerminalCommand({
|
|
27
|
+
command: 'test',
|
|
28
|
+
description: 'Check file types and compare values',
|
|
29
|
+
kernel,
|
|
30
|
+
shell,
|
|
31
|
+
terminal,
|
|
32
|
+
run: async (pid: number, argv: string[]) => {
|
|
33
|
+
const process = kernel.processes.get(pid) as Process | undefined
|
|
34
|
+
|
|
35
|
+
if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
|
|
36
|
+
printUsage(process, terminal)
|
|
37
|
+
return 0
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const checkFile = async (filePath: string, check: string): Promise<boolean> => {
|
|
41
|
+
const fullPath = path.resolve(shell.cwd, filePath)
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const stat = await shell.context.fs.promises.stat(fullPath)
|
|
45
|
+
|
|
46
|
+
switch (check) {
|
|
47
|
+
case 'f':
|
|
48
|
+
return stat.isFile()
|
|
49
|
+
case 'd':
|
|
50
|
+
return stat.isDirectory()
|
|
51
|
+
case 'e':
|
|
52
|
+
return true
|
|
53
|
+
case 'r':
|
|
54
|
+
case 'w':
|
|
55
|
+
case 'x':
|
|
56
|
+
return true
|
|
57
|
+
default:
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
return false
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (argv.length === 0) {
|
|
66
|
+
return 1
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const operator = argv[0]
|
|
70
|
+
|
|
71
|
+
if (operator === '-f' && argv[1]) {
|
|
72
|
+
return (await checkFile(argv[1], 'f')) ? 0 : 1
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (operator === '-d' && argv[1]) {
|
|
76
|
+
return (await checkFile(argv[1], 'd')) ? 0 : 1
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (operator === '-e' && argv[1]) {
|
|
80
|
+
return (await checkFile(argv[1], 'e')) ? 0 : 1
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (operator === '-r' && argv[1]) {
|
|
84
|
+
return (await checkFile(argv[1], 'r')) ? 0 : 1
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (operator === '-w' && argv[1]) {
|
|
88
|
+
return (await checkFile(argv[1], 'w')) ? 0 : 1
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (operator === '-x' && argv[1]) {
|
|
92
|
+
return (await checkFile(argv[1], 'x')) ? 0 : 1
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (operator === '-n' && argv[1]) {
|
|
96
|
+
return argv[1].length > 0 ? 0 : 1
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (operator === '-z' && argv[1]) {
|
|
100
|
+
return argv[1].length === 0 ? 0 : 1
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (argv.length === 3) {
|
|
104
|
+
const [left, op, right] = argv
|
|
105
|
+
if (op === '=') {
|
|
106
|
+
return left === right ? 0 : 1
|
|
107
|
+
}
|
|
108
|
+
if (op === '!=') {
|
|
109
|
+
return left !== right ? 0 : 1
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return 1
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
}
|
package/src/commands/touch.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
|
-
import type {
|
|
3
|
-
import type { Kernel, Shell, Terminal } from '@ecmaos/types'
|
|
2
|
+
import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
4
3
|
import { TerminalCommand } from '../shared/terminal-command.js'
|
|
4
|
+
import { writelnStdout, writelnStderr } from '../shared/helpers.js'
|
|
5
|
+
|
|
6
|
+
function printUsage(process: Process | undefined, terminal: Terminal): void {
|
|
7
|
+
const usage = `Usage: touch [OPTION]... FILE...
|
|
8
|
+
Update the access and modification times of each FILE to the current time.
|
|
9
|
+
|
|
10
|
+
--help display this help and exit`
|
|
11
|
+
writelnStdout(process, terminal, usage)
|
|
12
|
+
}
|
|
5
13
|
|
|
6
14
|
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
7
15
|
return new TerminalCommand({
|
|
@@ -10,16 +18,37 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
10
18
|
kernel,
|
|
11
19
|
shell,
|
|
12
20
|
terminal,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
run: async (pid: number, argv: string[]) => {
|
|
22
|
+
const process = kernel.processes.get(pid) as Process | undefined
|
|
23
|
+
|
|
24
|
+
if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
|
|
25
|
+
printUsage(process, terminal)
|
|
26
|
+
return 0
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (argv.length === 0) {
|
|
30
|
+
await writelnStderr(process, terminal, 'touch: missing file operand')
|
|
31
|
+
await writelnStderr(process, terminal, "Try 'touch --help' for more information.")
|
|
32
|
+
return 1
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let hasError = false
|
|
36
|
+
|
|
37
|
+
for (const target of argv) {
|
|
38
|
+
if (!target || target.startsWith('-')) continue
|
|
39
|
+
|
|
40
|
+
const fullPath = target ? path.resolve(shell.cwd, target) : shell.cwd
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
await shell.context.fs.promises.appendFile(fullPath, '')
|
|
44
|
+
} catch (error) {
|
|
45
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
46
|
+
await writelnStderr(process, terminal, `touch: ${target}: ${errorMessage}`)
|
|
47
|
+
hasError = true
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return hasError ? 1 : 0
|
|
22
52
|
}
|
|
23
53
|
})
|
|
24
54
|
}
|
|
25
|
-
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
2
|
+
import { TerminalCommand } from '../shared/terminal-command.js'
|
|
3
|
+
import { writelnStderr } from '../shared/helpers.js'
|
|
4
|
+
|
|
5
|
+
function printUsage(process: Process | undefined, terminal: Terminal): void {
|
|
6
|
+
const usage = `Usage: tr [OPTION]... SET1 [SET2]
|
|
7
|
+
Translate or delete characters.
|
|
8
|
+
|
|
9
|
+
-d, --delete delete characters in SET1
|
|
10
|
+
-s, --squeeze replace each sequence of a repeated character
|
|
11
|
+
--help display this help and exit`
|
|
12
|
+
writelnStderr(process, terminal, usage)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
16
|
+
return new TerminalCommand({
|
|
17
|
+
command: 'tr',
|
|
18
|
+
description: 'Translate or delete characters',
|
|
19
|
+
kernel,
|
|
20
|
+
shell,
|
|
21
|
+
terminal,
|
|
22
|
+
run: async (pid: number, argv: string[]) => {
|
|
23
|
+
const process = kernel.processes.get(pid) as Process | undefined
|
|
24
|
+
|
|
25
|
+
if (!process) return 1
|
|
26
|
+
|
|
27
|
+
if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
|
|
28
|
+
printUsage(process, terminal)
|
|
29
|
+
return 0
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const args: string[] = []
|
|
33
|
+
let deleteMode = false
|
|
34
|
+
let squeeze = false
|
|
35
|
+
|
|
36
|
+
for (const arg of argv) {
|
|
37
|
+
if (arg === '--help' || arg === '-h') {
|
|
38
|
+
printUsage(process, terminal)
|
|
39
|
+
return 0
|
|
40
|
+
} else if (arg === '-d' || arg === '--delete') {
|
|
41
|
+
deleteMode = true
|
|
42
|
+
} else if (arg === '-s' || arg === '--squeeze') {
|
|
43
|
+
squeeze = true
|
|
44
|
+
} else if (arg.startsWith('-')) {
|
|
45
|
+
const flags = arg.slice(1).split('')
|
|
46
|
+
if (flags.includes('d')) deleteMode = true
|
|
47
|
+
if (flags.includes('s')) squeeze = true
|
|
48
|
+
const invalidFlags = flags.filter(f => !['d', 's'].includes(f))
|
|
49
|
+
if (invalidFlags.length > 0) {
|
|
50
|
+
await writelnStderr(process, terminal, `tr: invalid option -- '${invalidFlags[0]}'`)
|
|
51
|
+
return 1
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
args.push(arg)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (args.length === 0) {
|
|
59
|
+
await writelnStderr(process, terminal, 'tr: missing operand')
|
|
60
|
+
return 1
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const set1 = args[0] || ''
|
|
64
|
+
const set2 = args[1] || ''
|
|
65
|
+
|
|
66
|
+
if (!deleteMode && !squeeze && !set2) {
|
|
67
|
+
await writelnStderr(process, terminal, 'tr: missing operand after SET1')
|
|
68
|
+
return 1
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const writer = process.stdout.getWriter()
|
|
72
|
+
|
|
73
|
+
const expandSet = (set: string): string => {
|
|
74
|
+
let result = ''
|
|
75
|
+
let i = 0
|
|
76
|
+
while (i < set.length) {
|
|
77
|
+
if (i < set.length - 2 && set[i + 1] === '-') {
|
|
78
|
+
const startChar = set[i]
|
|
79
|
+
const endChar = set[i + 2]
|
|
80
|
+
if (startChar !== undefined && endChar !== undefined) {
|
|
81
|
+
const start = startChar.charCodeAt(0)
|
|
82
|
+
const end = endChar.charCodeAt(0)
|
|
83
|
+
for (let j = start; j <= end; j++) {
|
|
84
|
+
result += String.fromCharCode(j)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
i += 3
|
|
88
|
+
} else {
|
|
89
|
+
const char = set[i]
|
|
90
|
+
if (char !== undefined) {
|
|
91
|
+
result += char
|
|
92
|
+
}
|
|
93
|
+
i++
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return result
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
if (!process.stdin) {
|
|
101
|
+
return 0
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const reader = process.stdin.getReader()
|
|
105
|
+
const decoder = new TextDecoder()
|
|
106
|
+
let content = ''
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
while (true) {
|
|
110
|
+
const { done, value } = await reader.read()
|
|
111
|
+
if (done) break
|
|
112
|
+
if (value) {
|
|
113
|
+
content += decoder.decode(value, { stream: true })
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
} finally {
|
|
117
|
+
reader.releaseLock()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const expandedSet1 = expandSet(set1)
|
|
121
|
+
const expandedSet2 = deleteMode ? '' : expandSet(set2)
|
|
122
|
+
|
|
123
|
+
let result = ''
|
|
124
|
+
|
|
125
|
+
if (deleteMode) {
|
|
126
|
+
const set1Chars = new Set(expandedSet1)
|
|
127
|
+
for (const char of content) {
|
|
128
|
+
if (!set1Chars.has(char)) {
|
|
129
|
+
result += char
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
const map = new Map<string, string>()
|
|
134
|
+
const maxLen = Math.max(expandedSet1.length, expandedSet2.length)
|
|
135
|
+
|
|
136
|
+
for (let i = 0; i < maxLen; i++) {
|
|
137
|
+
const from = expandedSet1[i] || (expandedSet1.length > 0 ? expandedSet1[expandedSet1.length - 1] : '')
|
|
138
|
+
const to = expandedSet2[i] || (expandedSet2.length > 0 ? expandedSet2[expandedSet2.length - 1] : '')
|
|
139
|
+
if (from !== undefined) {
|
|
140
|
+
map.set(from, to || '')
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
for (const char of content) {
|
|
145
|
+
result += map.get(char) || char
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (squeeze) {
|
|
150
|
+
let squeezed = ''
|
|
151
|
+
let lastChar = ''
|
|
152
|
+
for (const char of result) {
|
|
153
|
+
if (char !== lastChar || !expandedSet1.includes(char)) {
|
|
154
|
+
squeezed += char
|
|
155
|
+
lastChar = char
|
|
156
|
+
} else {
|
|
157
|
+
lastChar = char
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
result = squeezed
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
await writer.write(new TextEncoder().encode(result))
|
|
164
|
+
return 0
|
|
165
|
+
} finally {
|
|
166
|
+
writer.releaseLock()
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
2
|
+
import { TerminalCommand } from '../shared/terminal-command.js'
|
|
3
|
+
import { writelnStdout } from '../shared/helpers.js'
|
|
4
|
+
|
|
5
|
+
function printUsage(process: Process | undefined, terminal: Terminal): void {
|
|
6
|
+
const usage = `Usage: true
|
|
7
|
+
Return a successful exit status.
|
|
8
|
+
|
|
9
|
+
--help display this help and exit`
|
|
10
|
+
writelnStdout(process, terminal, usage)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
14
|
+
return new TerminalCommand({
|
|
15
|
+
command: 'true',
|
|
16
|
+
description: 'Return a successful exit status',
|
|
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 (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
|
|
24
|
+
printUsage(process, terminal)
|
|
25
|
+
return 0
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return 0
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
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: uniq [OPTION]... [INPUT [OUTPUT]]
|
|
9
|
+
Report or omit repeated lines.
|
|
10
|
+
|
|
11
|
+
-c, --count prefix lines by the number of occurrences
|
|
12
|
+
-d, --repeated only print duplicate lines
|
|
13
|
+
-u, --unique only print unique lines
|
|
14
|
+
--help display this help and exit`
|
|
15
|
+
writelnStderr(process, terminal, usage)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
19
|
+
return new TerminalCommand({
|
|
20
|
+
command: 'uniq',
|
|
21
|
+
description: 'Report or omit repeated lines',
|
|
22
|
+
kernel,
|
|
23
|
+
shell,
|
|
24
|
+
terminal,
|
|
25
|
+
run: async (pid: number, argv: string[]) => {
|
|
26
|
+
const process = kernel.processes.get(pid) as Process | undefined
|
|
27
|
+
|
|
28
|
+
if (!process) return 1
|
|
29
|
+
|
|
30
|
+
if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
|
|
31
|
+
printUsage(process, terminal)
|
|
32
|
+
return 0
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const files: string[] = []
|
|
36
|
+
let count = false
|
|
37
|
+
let repeated = false
|
|
38
|
+
let unique = false
|
|
39
|
+
|
|
40
|
+
for (const arg of argv) {
|
|
41
|
+
if (arg === '--help' || arg === '-h') {
|
|
42
|
+
printUsage(process, terminal)
|
|
43
|
+
return 0
|
|
44
|
+
} else if (arg === '-c' || arg === '--count') {
|
|
45
|
+
count = true
|
|
46
|
+
} else if (arg === '-d' || arg === '--repeated') {
|
|
47
|
+
repeated = true
|
|
48
|
+
} else if (arg === '-u' || arg === '--unique') {
|
|
49
|
+
unique = true
|
|
50
|
+
} else if (arg.startsWith('-')) {
|
|
51
|
+
const flags = arg.slice(1).split('')
|
|
52
|
+
if (flags.includes('c')) count = true
|
|
53
|
+
if (flags.includes('d')) repeated = true
|
|
54
|
+
if (flags.includes('u')) unique = true
|
|
55
|
+
const invalidFlags = flags.filter(f => !['c', 'd', 'u'].includes(f))
|
|
56
|
+
if (invalidFlags.length > 0) {
|
|
57
|
+
await writelnStderr(process, terminal, `uniq: invalid option -- '${invalidFlags[0]}'`)
|
|
58
|
+
return 1
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
files.push(arg)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const writer = process.stdout.getWriter()
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
let lines: string[] = []
|
|
69
|
+
|
|
70
|
+
if (files.length === 0) {
|
|
71
|
+
if (!process.stdin) {
|
|
72
|
+
return 0
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const reader = process.stdin.getReader()
|
|
76
|
+
const decoder = new TextDecoder()
|
|
77
|
+
let buffer = ''
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
while (true) {
|
|
81
|
+
const { done, value } = await reader.read()
|
|
82
|
+
if (done) break
|
|
83
|
+
if (value) {
|
|
84
|
+
buffer += decoder.decode(value, { stream: true })
|
|
85
|
+
const newLines = buffer.split('\n')
|
|
86
|
+
buffer = newLines.pop() || ''
|
|
87
|
+
lines.push(...newLines)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (buffer) {
|
|
91
|
+
lines.push(buffer)
|
|
92
|
+
}
|
|
93
|
+
} finally {
|
|
94
|
+
reader.releaseLock()
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
for (const file of files) {
|
|
98
|
+
const fullPath = path.resolve(shell.cwd, file)
|
|
99
|
+
|
|
100
|
+
let interrupted = false
|
|
101
|
+
const interruptHandler = () => { interrupted = true }
|
|
102
|
+
kernel.terminal.events.on(TerminalEvents.INTERRUPT, interruptHandler)
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
if (fullPath.startsWith('/dev')) {
|
|
106
|
+
await writelnStderr(process, terminal, `uniq: ${file}: cannot process device files`)
|
|
107
|
+
continue
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const handle = await shell.context.fs.promises.open(fullPath, 'r')
|
|
111
|
+
const stat = await shell.context.fs.promises.stat(fullPath)
|
|
112
|
+
|
|
113
|
+
const decoder = new TextDecoder()
|
|
114
|
+
let content = ''
|
|
115
|
+
let bytesRead = 0
|
|
116
|
+
const chunkSize = 1024
|
|
117
|
+
|
|
118
|
+
while (bytesRead < stat.size) {
|
|
119
|
+
if (interrupted) break
|
|
120
|
+
const data = new Uint8Array(chunkSize)
|
|
121
|
+
const readSize = Math.min(chunkSize, stat.size - bytesRead)
|
|
122
|
+
await handle.read(data, 0, readSize, bytesRead)
|
|
123
|
+
const chunk = data.subarray(0, readSize)
|
|
124
|
+
content += decoder.decode(chunk, { stream: true })
|
|
125
|
+
bytesRead += readSize
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const fileLines = content.split('\n')
|
|
129
|
+
if (fileLines[fileLines.length - 1] === '') {
|
|
130
|
+
fileLines.pop()
|
|
131
|
+
}
|
|
132
|
+
lines.push(...fileLines)
|
|
133
|
+
} catch (error) {
|
|
134
|
+
await writelnStderr(process, terminal, `uniq: ${file}: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
135
|
+
} finally {
|
|
136
|
+
kernel.terminal.events.off(TerminalEvents.INTERRUPT, interruptHandler)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (lines.length === 0) {
|
|
142
|
+
return 0
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let prevLine: string | null = null
|
|
146
|
+
let countValue = 1
|
|
147
|
+
|
|
148
|
+
for (let i = 0; i < lines.length; i++) {
|
|
149
|
+
const line = lines[i]
|
|
150
|
+
if (line === undefined) continue
|
|
151
|
+
|
|
152
|
+
if (prevLine === null) {
|
|
153
|
+
prevLine = line
|
|
154
|
+
countValue = 1
|
|
155
|
+
continue
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (line === prevLine) {
|
|
159
|
+
countValue++
|
|
160
|
+
} else {
|
|
161
|
+
if (repeated && countValue === 1) {
|
|
162
|
+
} else if (unique && countValue > 1) {
|
|
163
|
+
} else {
|
|
164
|
+
const output = count ? `${countValue.toString().padStart(7)} ${prevLine}` : prevLine
|
|
165
|
+
await writer.write(new TextEncoder().encode(output + '\n'))
|
|
166
|
+
}
|
|
167
|
+
if (line !== undefined) {
|
|
168
|
+
prevLine = line
|
|
169
|
+
}
|
|
170
|
+
countValue = 1
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (prevLine !== null) {
|
|
175
|
+
if (repeated && countValue === 1) {
|
|
176
|
+
} else if (unique && countValue > 1) {
|
|
177
|
+
} else {
|
|
178
|
+
const output = count ? `${countValue.toString().padStart(7)} ${prevLine}` : prevLine
|
|
179
|
+
await writer.write(new TextEncoder().encode(output + '\n'))
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return 0
|
|
184
|
+
} finally {
|
|
185
|
+
writer.releaseLock()
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
}
|