@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,154 @@
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: split [OPTION]... [INPUT [PREFIX]]
9
+ Split INPUT into fixed-size pieces.
10
+
11
+ -l, -lNUMBER put NUMBER lines per output file
12
+ -b, -bSIZE put SIZE bytes per output file
13
+ --help display this help and exit`
14
+ writelnStderr(process, terminal, usage)
15
+ }
16
+
17
+ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
18
+ return new TerminalCommand({
19
+ command: 'split',
20
+ description: 'Split a file into pieces',
21
+ kernel,
22
+ shell,
23
+ terminal,
24
+ run: async (pid: number, argv: string[]) => {
25
+ const process = kernel.processes.get(pid) as Process | undefined
26
+
27
+ if (!process) return 1
28
+
29
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
30
+ printUsage(process, terminal)
31
+ return 0
32
+ }
33
+
34
+ let file = ''
35
+ let lines: number | undefined
36
+ let bytes: number | undefined
37
+ let prefix = 'x'
38
+
39
+ for (let i = 0; i < argv.length; i++) {
40
+ const arg = argv[i]
41
+ if (!arg) continue
42
+
43
+ if (arg === '--help' || arg === '-h') {
44
+ printUsage(process, terminal)
45
+ return 0
46
+ } else if (arg === '-l' || arg.startsWith('-l')) {
47
+ if (arg === '-l' && i + 1 < argv.length) {
48
+ i++
49
+ const nextArg = argv[i]
50
+ if (nextArg !== undefined) {
51
+ lines = parseInt(nextArg, 10)
52
+ }
53
+ } else if (arg.startsWith('-l') && arg.length > 2) {
54
+ lines = parseInt(arg.slice(2), 10)
55
+ }
56
+ } else if (arg === '-b' || arg.startsWith('-b')) {
57
+ if (arg === '-b' && i + 1 < argv.length) {
58
+ i++
59
+ const nextArg = argv[i]
60
+ if (nextArg !== undefined) {
61
+ bytes = parseInt(nextArg, 10)
62
+ }
63
+ } else if (arg.startsWith('-b') && arg.length > 2) {
64
+ bytes = parseInt(arg.slice(2), 10)
65
+ }
66
+ } else if (!arg.startsWith('-')) {
67
+ if (!file) {
68
+ file = arg
69
+ } else if (prefix === 'x') {
70
+ prefix = arg
71
+ }
72
+ }
73
+ }
74
+
75
+ if (!file) {
76
+ await writelnStderr(process, terminal, 'split: missing file operand')
77
+ return 1
78
+ }
79
+
80
+ if (!lines && !bytes) {
81
+ await writelnStderr(process, terminal, 'split: you must specify -l or -b')
82
+ return 1
83
+ }
84
+
85
+ const fullPath = path.resolve(shell.cwd, file)
86
+
87
+ let interrupted = false
88
+ const interruptHandler = () => { interrupted = true }
89
+ kernel.terminal.events.on(TerminalEvents.INTERRUPT, interruptHandler)
90
+
91
+ try {
92
+ if (fullPath.startsWith('/dev')) {
93
+ await writelnStderr(process, terminal, `split: ${file}: cannot split device files`)
94
+ return 1
95
+ }
96
+
97
+ const handle = await shell.context.fs.promises.open(fullPath, 'r')
98
+ const stat = await shell.context.fs.promises.stat(fullPath)
99
+
100
+ const decoder = new TextDecoder()
101
+ let content = ''
102
+ let bytesRead = 0
103
+ const chunkSize = 1024
104
+
105
+ while (bytesRead < stat.size) {
106
+ if (interrupted) break
107
+ const data = new Uint8Array(chunkSize)
108
+ const readSize = Math.min(chunkSize, stat.size - bytesRead)
109
+ await handle.read(data, 0, readSize, bytesRead)
110
+ const chunk = data.subarray(0, readSize)
111
+ content += decoder.decode(chunk, { stream: true })
112
+ bytesRead += readSize
113
+ }
114
+
115
+ const dir = path.dirname(fullPath)
116
+ let fileIndex = 0
117
+
118
+ const getSuffix = (index: number): string => {
119
+ const first = Math.floor(index / 26)
120
+ const second = index % 26
121
+ return String.fromCharCode(97 + first) + String.fromCharCode(97 + second)
122
+ }
123
+
124
+ if (lines) {
125
+ const allLines = content.split('\n')
126
+ for (let i = 0; i < allLines.length; i += lines) {
127
+ const chunk = allLines.slice(i, i + lines).join('\n')
128
+ const suffix = getSuffix(fileIndex)
129
+ const outputPath = path.join(dir, `${prefix}${suffix}`)
130
+ await shell.context.fs.promises.writeFile(outputPath, chunk, 'utf-8')
131
+ fileIndex++
132
+ }
133
+ } else if (bytes) {
134
+ const encoder = new TextEncoder()
135
+ const contentBytes = encoder.encode(content)
136
+ for (let i = 0; i < contentBytes.length; i += bytes) {
137
+ const chunk = contentBytes.slice(i, i + bytes)
138
+ const suffix = getSuffix(fileIndex)
139
+ const outputPath = path.join(dir, `${prefix}${suffix}`)
140
+ await shell.context.fs.promises.writeFile(outputPath, chunk)
141
+ fileIndex++
142
+ }
143
+ }
144
+
145
+ return 0
146
+ } catch (error) {
147
+ await writelnStderr(process, terminal, `split: ${file}: ${error instanceof Error ? error.message : 'Unknown error'}`)
148
+ return 1
149
+ } finally {
150
+ kernel.terminal.events.off(TerminalEvents.INTERRUPT, interruptHandler)
151
+ }
152
+ }
153
+ })
154
+ }
@@ -1,11 +1,18 @@
1
1
  import path from 'path'
2
2
  import chalk from 'chalk'
3
3
  import * as zipjs from '@zip.js/zip.js'
4
- import type { CommandLineOptions } from 'command-line-args'
5
4
  import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
6
5
  import { TerminalCommand } from '../shared/terminal-command.js'
7
6
  import { writelnStdout } from '../shared/helpers.js'
8
7
 
8
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
9
+ const usage = `Usage: stat [OPTION]... FILE...
10
+ Display file or file system status.
11
+
12
+ --help display this help and exit`
13
+ writelnStdout(process, terminal, usage)
14
+ }
15
+
9
16
  export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
10
17
  return new TerminalCommand({
11
18
  command: 'stat',
@@ -13,12 +20,15 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
13
20
  kernel,
14
21
  shell,
15
22
  terminal,
16
- options: [
17
- { name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
18
- { name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, description: 'The path to the file or directory to display' }
19
- ],
20
- run: async (argv: CommandLineOptions, process?: Process) => {
21
- const argPath = (argv.path as string) || shell.cwd
23
+ run: async (pid: number, argv: string[]) => {
24
+ const process = kernel.processes.get(pid) as Process | undefined
25
+
26
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
27
+ printUsage(process, terminal)
28
+ return 0
29
+ }
30
+
31
+ const argPath = argv.length > 0 && argv[0] && !argv[0].startsWith('-') ? argv[0] : shell.cwd
22
32
  const fullPath = argPath ? path.resolve(shell.cwd, argPath) : shell.cwd
23
33
  const stats = await shell.context.fs.promises.stat(fullPath)
24
34
  await writelnStdout(process, terminal, JSON.stringify(stats, null, 2))
@@ -37,4 +47,3 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
37
47
  }
38
48
  })
39
49
  }
40
-
@@ -1,8 +1,17 @@
1
1
  import path from 'path'
2
- import type { CommandLineOptions } from 'command-line-args'
3
2
  import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
4
3
  import { TerminalEvents } from '@ecmaos/types'
5
4
  import { TerminalCommand } from '../shared/terminal-command.js'
5
+ import { writelnStdout } from '../shared/helpers.js'
6
+
7
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
8
+ const usage = `Usage: tail [OPTION]... [FILE]...
9
+ Print the last 10 lines of each FILE to standard output.
10
+
11
+ -n, -nNUMBER print the last NUMBER lines instead of 10
12
+ --help display this help and exit`
13
+ writelnStdout(process, terminal, usage)
14
+ }
6
15
 
7
16
  export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
8
17
  return new TerminalCommand({
@@ -11,19 +20,47 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
11
20
  kernel,
12
21
  shell,
13
22
  terminal,
14
- options: [
15
- { name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
16
- { name: 'lines', type: Number, alias: 'n', description: 'Print the last NUM lines instead of the last 10' },
17
- { name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, multiple: true, description: 'The path(s) to the file(s) to read' }
18
- ],
19
- run: async (argv: CommandLineOptions, process?: Process) => {
23
+ run: async (pid: number, argv: string[]) => {
24
+ const process = kernel.processes.get(pid) as Process | undefined
25
+
20
26
  if (!process) return 1
21
27
 
28
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
29
+ printUsage(process, terminal)
30
+ return 0
31
+ }
32
+
33
+ let numLines = 10
34
+ const files: string[] = []
35
+
36
+ for (let i = 0; i < argv.length; i++) {
37
+ const arg = argv[i]
38
+ if (!arg) continue
39
+
40
+ if (arg === '--help' || arg === '-h') {
41
+ printUsage(process, terminal)
42
+ return 0
43
+ } else if (arg === '-n' || arg.startsWith('-n')) {
44
+ if (arg === '-n' && i + 1 < argv.length) {
45
+ i++
46
+ const nextArg = argv[i]
47
+ if (nextArg !== undefined) {
48
+ const num = parseInt(nextArg, 10)
49
+ if (!isNaN(num)) numLines = num
50
+ }
51
+ } else if (arg.startsWith('-n') && arg.length > 2) {
52
+ const num = parseInt(arg.slice(2), 10)
53
+ if (!isNaN(num)) numLines = num
54
+ }
55
+ } else if (!arg.startsWith('-')) {
56
+ files.push(arg)
57
+ }
58
+ }
59
+
22
60
  const writer = process.stdout.getWriter()
23
- const numLines = (argv.lines as number) ?? 10
24
61
 
25
62
  try {
26
- if (!argv.path || !(argv.path as string[])[0]) {
63
+ if (files.length === 0) {
27
64
  if (!process.stdin) {
28
65
  return 0
29
66
  }
@@ -75,7 +112,6 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
75
112
  return 0
76
113
  }
77
114
 
78
- const files = (argv.path as string[]) || []
79
115
  const isMultipleFiles = files.length > 1
80
116
 
81
117
  for (let i = 0; i < files.length; i++) {
@@ -1,10 +1,19 @@
1
1
  import path from 'path'
2
- import type { CommandLineOptions } from 'command-line-args'
3
2
  import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
4
3
  import { TerminalEvents } from '@ecmaos/types'
5
4
  import { TerminalCommand } from '../shared/terminal-command.js'
6
5
  import { writelnStderr } from '../shared/helpers.js'
7
6
 
7
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
8
+ const usage = `Usage: tee [OPTION]... [FILE]...
9
+ Read from standard input and write to standard output and files.
10
+
11
+ -a, --append append to the given files, do not overwrite
12
+ -i, --ignore-interrupts ignore interrupt signals
13
+ --help display this help and exit`
14
+ writelnStderr(process, terminal, usage)
15
+ }
16
+
8
17
  export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
9
18
  return new TerminalCommand({
10
19
  command: 'tee',
@@ -12,23 +21,46 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
12
21
  kernel,
13
22
  shell,
14
23
  terminal,
15
- options: [
16
- { name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
17
- { name: 'append', type: Boolean, alias: 'a', description: 'Append to the given files, do not overwrite' },
18
- { name: 'ignore-interrupts', type: Boolean, alias: 'i', description: 'Ignore interrupt signals' },
19
- { name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, multiple: true, description: 'File(s) to write to' }
20
- ],
21
- run: async (argv: CommandLineOptions, process?: Process) => {
24
+ run: async (pid: number, argv: string[]) => {
25
+ const process = kernel.processes.get(pid) as Process | undefined
26
+
22
27
  if (!process) return 1
23
28
 
29
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
30
+ printUsage(process, terminal)
31
+ return 0
32
+ }
33
+
24
34
  if (!process.stdin) {
25
35
  await writelnStderr(process, terminal, 'tee: No input provided')
26
36
  return 1
27
37
  }
28
38
 
29
- const files = (argv.path as string[]) || []
30
- const append = (argv.append as boolean) || false
31
- const ignoreInterrupts = (argv['ignore-interrupts'] as boolean) || false
39
+ const files: string[] = []
40
+ let append = false
41
+ let ignoreInterrupts = false
42
+
43
+ for (const arg of argv) {
44
+ if (arg === '--help' || arg === '-h') {
45
+ printUsage(process, terminal)
46
+ return 0
47
+ } else if (arg === '-a' || arg === '--append') {
48
+ append = true
49
+ } else if (arg === '-i' || arg === '--ignore-interrupts') {
50
+ ignoreInterrupts = true
51
+ } else if (arg.startsWith('-')) {
52
+ const flags = arg.slice(1).split('')
53
+ if (flags.includes('a')) append = true
54
+ if (flags.includes('i')) ignoreInterrupts = true
55
+ const invalidFlags = flags.filter(f => !['a', 'i'].includes(f))
56
+ if (invalidFlags.length > 0) {
57
+ await writelnStderr(process, terminal, `tee: invalid option -- '${invalidFlags[0]}'`)
58
+ return 1
59
+ }
60
+ } else {
61
+ files.push(arg)
62
+ }
63
+ }
32
64
 
33
65
  const writer = process.stdout.getWriter()
34
66
 
@@ -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
+ }
@@ -1,7 +1,15 @@
1
1
  import path from 'path'
2
- import type { CommandLineOptions } from 'command-line-args'
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
- options: [
14
- { name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
15
- { name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, description: 'The path to the file to create' }
16
- ],
17
- run: async (argv: CommandLineOptions) => {
18
- const target = (argv.path as string) || shell.cwd
19
- const fullPath = target ? path.resolve(shell.cwd, target) : shell.cwd
20
- await shell.context.fs.promises.appendFile(fullPath, '')
21
- return 0
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
-