@ecmaos/coreutils 0.1.5 → 0.2.1
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 +45 -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 +33 -25
- 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 +49 -10
- 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.map +1 -1
- package/dist/commands/grep.js +45 -10
- package/dist/commands/grep.js.map +1 -1
- package/dist/commands/head.d.ts.map +1 -1
- package/dist/commands/head.js +46 -9
- package/dist/commands/head.js.map +1 -1
- package/dist/commands/hex.d.ts.map +1 -1
- package/dist/commands/hex.js +16 -6
- 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 +96 -91
- 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.map +1 -1
- package/dist/commands/tail.js +46 -9
- package/dist/commands/tail.js.map +1 -1
- 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/user.d.ts +4 -0
- package/dist/commands/user.d.ts.map +1 -0
- package/dist/commands/user.js +427 -0
- package/dist/commands/user.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 +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +67 -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 +37 -27
- 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 +51 -11
- package/src/commands/false.ts +31 -0
- package/src/commands/find.ts +184 -0
- package/src/commands/grep.ts +44 -11
- package/src/commands/head.ts +46 -10
- package/src/commands/hex.ts +19 -7
- 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 +110 -87
- 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 +46 -10
- 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/user.ts +436 -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 +67 -1
- package/src/shared/terminal-command.ts +43 -16
|
@@ -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
|
+
}
|