@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.
Files changed (231) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +45 -0
  3. package/dist/commands/basename.d.ts +4 -0
  4. package/dist/commands/basename.d.ts.map +1 -0
  5. package/dist/commands/basename.js +78 -0
  6. package/dist/commands/basename.js.map +1 -0
  7. package/dist/commands/cal.d.ts +4 -0
  8. package/dist/commands/cal.d.ts.map +1 -0
  9. package/dist/commands/cal.js +105 -0
  10. package/dist/commands/cal.js.map +1 -0
  11. package/dist/commands/cat.d.ts.map +1 -1
  12. package/dist/commands/cat.js +33 -25
  13. package/dist/commands/cat.js.map +1 -1
  14. package/dist/commands/cd.d.ts.map +1 -1
  15. package/dist/commands/cd.js +38 -10
  16. package/dist/commands/cd.js.map +1 -1
  17. package/dist/commands/chmod.d.ts.map +1 -1
  18. package/dist/commands/chmod.js +31 -9
  19. package/dist/commands/chmod.js.map +1 -1
  20. package/dist/commands/comm.d.ts +4 -0
  21. package/dist/commands/comm.d.ts.map +1 -0
  22. package/dist/commands/comm.js +162 -0
  23. package/dist/commands/comm.js.map +1 -0
  24. package/dist/commands/cp.d.ts.map +1 -1
  25. package/dist/commands/cp.js +61 -12
  26. package/dist/commands/cp.js.map +1 -1
  27. package/dist/commands/cut.d.ts +4 -0
  28. package/dist/commands/cut.d.ts.map +1 -0
  29. package/dist/commands/cut.js +208 -0
  30. package/dist/commands/cut.js.map +1 -0
  31. package/dist/commands/date.d.ts +4 -0
  32. package/dist/commands/date.d.ts.map +1 -0
  33. package/dist/commands/date.js +100 -0
  34. package/dist/commands/date.js.map +1 -0
  35. package/dist/commands/diff.d.ts +4 -0
  36. package/dist/commands/diff.d.ts.map +1 -0
  37. package/dist/commands/diff.js +194 -0
  38. package/dist/commands/diff.js.map +1 -0
  39. package/dist/commands/dirname.d.ts +4 -0
  40. package/dist/commands/dirname.d.ts.map +1 -0
  41. package/dist/commands/dirname.js +50 -0
  42. package/dist/commands/dirname.js.map +1 -0
  43. package/dist/commands/echo.d.ts.map +1 -1
  44. package/dist/commands/echo.js +49 -10
  45. package/dist/commands/echo.js.map +1 -1
  46. package/dist/commands/false.d.ts +4 -0
  47. package/dist/commands/false.d.ts.map +1 -0
  48. package/dist/commands/false.js +27 -0
  49. package/dist/commands/false.js.map +1 -0
  50. package/dist/commands/find.d.ts +4 -0
  51. package/dist/commands/find.d.ts.map +1 -0
  52. package/dist/commands/find.js +181 -0
  53. package/dist/commands/find.js.map +1 -0
  54. package/dist/commands/grep.d.ts.map +1 -1
  55. package/dist/commands/grep.js +45 -10
  56. package/dist/commands/grep.js.map +1 -1
  57. package/dist/commands/head.d.ts.map +1 -1
  58. package/dist/commands/head.js +46 -9
  59. package/dist/commands/head.js.map +1 -1
  60. package/dist/commands/hex.d.ts.map +1 -1
  61. package/dist/commands/hex.js +16 -6
  62. package/dist/commands/hex.js.map +1 -1
  63. package/dist/commands/id.d.ts +4 -0
  64. package/dist/commands/id.d.ts.map +1 -0
  65. package/dist/commands/id.js +97 -0
  66. package/dist/commands/id.js.map +1 -0
  67. package/dist/commands/join.d.ts +4 -0
  68. package/dist/commands/join.d.ts.map +1 -0
  69. package/dist/commands/join.js +152 -0
  70. package/dist/commands/join.js.map +1 -0
  71. package/dist/commands/less.d.ts.map +1 -1
  72. package/dist/commands/less.js +16 -7
  73. package/dist/commands/less.js.map +1 -1
  74. package/dist/commands/ln.d.ts.map +1 -1
  75. package/dist/commands/ln.js +54 -12
  76. package/dist/commands/ln.js.map +1 -1
  77. package/dist/commands/ls.d.ts.map +1 -1
  78. package/dist/commands/ls.js +96 -91
  79. package/dist/commands/ls.js.map +1 -1
  80. package/dist/commands/mkdir.d.ts.map +1 -1
  81. package/dist/commands/mkdir.js +34 -9
  82. package/dist/commands/mkdir.js.map +1 -1
  83. package/dist/commands/mv.d.ts.map +1 -1
  84. package/dist/commands/mv.js +25 -7
  85. package/dist/commands/mv.js.map +1 -1
  86. package/dist/commands/nc.d.ts +4 -0
  87. package/dist/commands/nc.d.ts.map +1 -0
  88. package/dist/commands/nc.js +451 -0
  89. package/dist/commands/nc.js.map +1 -0
  90. package/dist/commands/nl.d.ts +4 -0
  91. package/dist/commands/nl.d.ts.map +1 -0
  92. package/dist/commands/nl.js +208 -0
  93. package/dist/commands/nl.js.map +1 -0
  94. package/dist/commands/passkey.d.ts.map +1 -1
  95. package/dist/commands/passkey.js +44 -18
  96. package/dist/commands/passkey.js.map +1 -1
  97. package/dist/commands/paste.d.ts +4 -0
  98. package/dist/commands/paste.d.ts.map +1 -0
  99. package/dist/commands/paste.js +161 -0
  100. package/dist/commands/paste.js.map +1 -0
  101. package/dist/commands/pwd.d.ts.map +1 -1
  102. package/dist/commands/pwd.js +13 -4
  103. package/dist/commands/pwd.js.map +1 -1
  104. package/dist/commands/rm.d.ts.map +1 -1
  105. package/dist/commands/rm.js +105 -12
  106. package/dist/commands/rm.js.map +1 -1
  107. package/dist/commands/rmdir.d.ts.map +1 -1
  108. package/dist/commands/rmdir.js +34 -9
  109. package/dist/commands/rmdir.js.map +1 -1
  110. package/dist/commands/sed.d.ts.map +1 -1
  111. package/dist/commands/sed.js +73 -28
  112. package/dist/commands/sed.js.map +1 -1
  113. package/dist/commands/seq.d.ts +4 -0
  114. package/dist/commands/seq.d.ts.map +1 -0
  115. package/dist/commands/seq.js +148 -0
  116. package/dist/commands/seq.js.map +1 -0
  117. package/dist/commands/sockets.d.ts +4 -0
  118. package/dist/commands/sockets.d.ts.map +1 -0
  119. package/dist/commands/sockets.js +239 -0
  120. package/dist/commands/sockets.js.map +1 -0
  121. package/dist/commands/sort.d.ts +4 -0
  122. package/dist/commands/sort.d.ts.map +1 -0
  123. package/dist/commands/sort.js +175 -0
  124. package/dist/commands/sort.js.map +1 -0
  125. package/dist/commands/split.d.ts +4 -0
  126. package/dist/commands/split.d.ts.map +1 -0
  127. package/dist/commands/split.js +147 -0
  128. package/dist/commands/split.js.map +1 -0
  129. package/dist/commands/stat.d.ts.map +1 -1
  130. package/dist/commands/stat.js +14 -6
  131. package/dist/commands/stat.js.map +1 -1
  132. package/dist/commands/tail.d.ts.map +1 -1
  133. package/dist/commands/tail.js +46 -9
  134. package/dist/commands/tail.js.map +1 -1
  135. package/dist/commands/tee.d.ts.map +1 -1
  136. package/dist/commands/tee.js +45 -10
  137. package/dist/commands/tee.js.map +1 -1
  138. package/dist/commands/test.d.ts +4 -0
  139. package/dist/commands/test.d.ts.map +1 -0
  140. package/dist/commands/test.js +99 -0
  141. package/dist/commands/test.js.map +1 -0
  142. package/dist/commands/touch.d.ts.map +1 -1
  143. package/dist/commands/touch.js +34 -9
  144. package/dist/commands/touch.js.map +1 -1
  145. package/dist/commands/tr.d.ts +4 -0
  146. package/dist/commands/tr.d.ts.map +1 -0
  147. package/dist/commands/tr.js +162 -0
  148. package/dist/commands/tr.js.map +1 -0
  149. package/dist/commands/true.d.ts +4 -0
  150. package/dist/commands/true.d.ts.map +1 -0
  151. package/dist/commands/true.js +27 -0
  152. package/dist/commands/true.js.map +1 -0
  153. package/dist/commands/uniq.d.ts +4 -0
  154. package/dist/commands/uniq.d.ts.map +1 -0
  155. package/dist/commands/uniq.js +187 -0
  156. package/dist/commands/uniq.js.map +1 -0
  157. package/dist/commands/user.d.ts +4 -0
  158. package/dist/commands/user.d.ts.map +1 -0
  159. package/dist/commands/user.js +427 -0
  160. package/dist/commands/user.js.map +1 -0
  161. package/dist/commands/wc.d.ts +4 -0
  162. package/dist/commands/wc.d.ts.map +1 -0
  163. package/dist/commands/wc.js +178 -0
  164. package/dist/commands/wc.js.map +1 -0
  165. package/dist/commands/which.d.ts +4 -0
  166. package/dist/commands/which.d.ts.map +1 -0
  167. package/dist/commands/which.js +78 -0
  168. package/dist/commands/which.js.map +1 -0
  169. package/dist/commands/whoami.d.ts +4 -0
  170. package/dist/commands/whoami.d.ts.map +1 -0
  171. package/dist/commands/whoami.js +28 -0
  172. package/dist/commands/whoami.js.map +1 -0
  173. package/dist/index.d.ts +14 -0
  174. package/dist/index.d.ts.map +1 -1
  175. package/dist/index.js +67 -1
  176. package/dist/index.js.map +1 -1
  177. package/dist/shared/terminal-command.d.ts +8 -2
  178. package/dist/shared/terminal-command.d.ts.map +1 -1
  179. package/dist/shared/terminal-command.js +42 -17
  180. package/dist/shared/terminal-command.js.map +1 -1
  181. package/package.json +4 -2
  182. package/src/commands/basename.ts +84 -0
  183. package/src/commands/cal.ts +111 -0
  184. package/src/commands/cat.ts +37 -27
  185. package/src/commands/cd.ts +40 -12
  186. package/src/commands/chmod.ts +37 -11
  187. package/src/commands/comm.ts +169 -0
  188. package/src/commands/cp.ts +73 -15
  189. package/src/commands/cut.ts +214 -0
  190. package/src/commands/date.ts +97 -0
  191. package/src/commands/diff.ts +204 -0
  192. package/src/commands/dirname.ts +57 -0
  193. package/src/commands/echo.ts +51 -11
  194. package/src/commands/false.ts +31 -0
  195. package/src/commands/find.ts +184 -0
  196. package/src/commands/grep.ts +44 -11
  197. package/src/commands/head.ts +46 -10
  198. package/src/commands/hex.ts +19 -7
  199. package/src/commands/id.ts +94 -0
  200. package/src/commands/join.ts +162 -0
  201. package/src/commands/less.ts +20 -8
  202. package/src/commands/ln.ts +50 -13
  203. package/src/commands/ls.ts +110 -87
  204. package/src/commands/mkdir.ts +41 -12
  205. package/src/commands/mv.ts +31 -9
  206. package/src/commands/nc.ts +499 -0
  207. package/src/commands/nl.ts +201 -0
  208. package/src/commands/passkey.ts +46 -19
  209. package/src/commands/paste.ts +172 -0
  210. package/src/commands/pwd.ts +16 -4
  211. package/src/commands/rm.ts +118 -13
  212. package/src/commands/rmdir.ts +41 -12
  213. package/src/commands/sed.ts +64 -30
  214. package/src/commands/seq.ts +147 -0
  215. package/src/commands/sockets.ts +283 -0
  216. package/src/commands/sort.ts +175 -0
  217. package/src/commands/split.ts +154 -0
  218. package/src/commands/stat.ts +17 -8
  219. package/src/commands/tail.ts +46 -10
  220. package/src/commands/tee.ts +43 -11
  221. package/src/commands/test.ts +116 -0
  222. package/src/commands/touch.ts +41 -12
  223. package/src/commands/tr.ts +170 -0
  224. package/src/commands/true.ts +31 -0
  225. package/src/commands/uniq.ts +189 -0
  226. package/src/commands/user.ts +436 -0
  227. package/src/commands/wc.ts +181 -0
  228. package/src/commands/which.ts +89 -0
  229. package/src/commands/whoami.ts +32 -0
  230. package/src/index.ts +67 -1
  231. 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
+ }