@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,111 @@
|
|
|
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: cal [MONTH] [YEAR]
|
|
7
|
+
Display a calendar.
|
|
8
|
+
|
|
9
|
+
MONTH month (1-12)
|
|
10
|
+
YEAR year
|
|
11
|
+
--help display this help and exit`
|
|
12
|
+
writelnStdout(process, terminal, usage)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
16
|
+
return new TerminalCommand({
|
|
17
|
+
command: 'cal',
|
|
18
|
+
description: 'Display a calendar',
|
|
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 (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
|
|
26
|
+
printUsage(process, terminal)
|
|
27
|
+
return 0
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const now = new Date()
|
|
31
|
+
let month: number | undefined
|
|
32
|
+
let year: number | undefined
|
|
33
|
+
|
|
34
|
+
if (argv.length === 1) {
|
|
35
|
+
const argStr = argv[0]
|
|
36
|
+
if (!argStr) {
|
|
37
|
+
month = now.getMonth() + 1
|
|
38
|
+
year = now.getFullYear()
|
|
39
|
+
} else {
|
|
40
|
+
const arg = parseInt(argStr, 10)
|
|
41
|
+
if (isNaN(arg)) {
|
|
42
|
+
await writelnStdout(process, terminal, 'cal: invalid argument')
|
|
43
|
+
return 1
|
|
44
|
+
}
|
|
45
|
+
if (arg >= 1 && arg <= 12) {
|
|
46
|
+
month = arg
|
|
47
|
+
year = now.getFullYear()
|
|
48
|
+
} else {
|
|
49
|
+
year = arg
|
|
50
|
+
month = now.getMonth() + 1
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} else if (argv.length === 2) {
|
|
54
|
+
const monthStr = argv[0]
|
|
55
|
+
const yearStr = argv[1]
|
|
56
|
+
if (!monthStr || !yearStr) {
|
|
57
|
+
await writelnStdout(process, terminal, 'cal: invalid arguments')
|
|
58
|
+
return 1
|
|
59
|
+
}
|
|
60
|
+
month = parseInt(monthStr, 10)
|
|
61
|
+
year = parseInt(yearStr, 10)
|
|
62
|
+
if (isNaN(month) || isNaN(year)) {
|
|
63
|
+
await writelnStdout(process, terminal, 'cal: invalid arguments')
|
|
64
|
+
return 1
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
month = now.getMonth() + 1
|
|
68
|
+
year = now.getFullYear()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (month < 1 || month > 12) {
|
|
72
|
+
await writelnStdout(process, terminal, 'cal: invalid month')
|
|
73
|
+
return 1
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const monthNames = ['January', 'February', 'March', 'April', 'May', 'June',
|
|
77
|
+
'July', 'August', 'September', 'October', 'November', 'December']
|
|
78
|
+
const dayNames = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']
|
|
79
|
+
|
|
80
|
+
const firstDay = new Date(year, month - 1, 1)
|
|
81
|
+
const lastDay = new Date(year, month, 0)
|
|
82
|
+
const daysInMonth = lastDay.getDate()
|
|
83
|
+
const startDayOfWeek = firstDay.getDay()
|
|
84
|
+
|
|
85
|
+
let output = ` ${monthNames[month - 1]} ${year}\n`
|
|
86
|
+
output += dayNames.join(' ') + '\n'
|
|
87
|
+
|
|
88
|
+
let day = 1
|
|
89
|
+
let isFirstWeek = true
|
|
90
|
+
|
|
91
|
+
while (day <= daysInMonth) {
|
|
92
|
+
let line = ''
|
|
93
|
+
for (let i = 0; i < 7; i++) {
|
|
94
|
+
if (isFirstWeek && i < startDayOfWeek) {
|
|
95
|
+
line += ' '
|
|
96
|
+
} else if (day <= daysInMonth) {
|
|
97
|
+
line += day.toString().padStart(2) + ' '
|
|
98
|
+
day++
|
|
99
|
+
} else {
|
|
100
|
+
line += ' '
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
output += line.trimEnd() + '\n'
|
|
104
|
+
isFirstWeek = false
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
await writelnStdout(process, terminal, output)
|
|
108
|
+
return 0
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
}
|
package/src/commands/cat.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
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
|
-
import {
|
|
5
|
+
import { writelnStdout } from '../shared/helpers.js'
|
|
6
|
+
|
|
7
|
+
function printUsage(process: Process | undefined, terminal: Terminal): void {
|
|
8
|
+
const usage = `Usage: cat [OPTION]... [FILE]...
|
|
9
|
+
Concatenate files and print on the standard output.
|
|
10
|
+
|
|
11
|
+
--help display this help and exit`
|
|
12
|
+
writelnStdout(process, terminal, usage)
|
|
13
|
+
}
|
|
7
14
|
|
|
8
15
|
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
9
16
|
return new TerminalCommand({
|
|
@@ -12,38 +19,58 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
12
19
|
kernel,
|
|
13
20
|
shell,
|
|
14
21
|
terminal,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
{ name: 'bytes', type: Number, description: 'The number of bytes to read from the file' }
|
|
19
|
-
],
|
|
20
|
-
run: async (argv: CommandLineOptions, process?: Process) => {
|
|
22
|
+
run: async (pid: number, argv: string[]) => {
|
|
23
|
+
const process = kernel.processes.get(pid) as Process | undefined
|
|
24
|
+
|
|
21
25
|
if (!process) return 1
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
|
|
28
|
+
printUsage(process, terminal)
|
|
29
|
+
return 0
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const files: string[] = []
|
|
33
|
+
|
|
34
|
+
for (let i = 0; i < argv.length; i++) {
|
|
35
|
+
const arg = argv[i]
|
|
36
|
+
if (arg === undefined) continue
|
|
37
|
+
|
|
38
|
+
if (arg === '--help' || arg === '-h') {
|
|
39
|
+
printUsage(process, terminal)
|
|
40
|
+
return 0
|
|
41
|
+
} else if (!arg.startsWith('-')) {
|
|
42
|
+
files.push(arg)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
24
46
|
const writer = process.stdout.getWriter()
|
|
47
|
+
const isTTY = process.stdoutIsTTY ?? false
|
|
48
|
+
let lastByte: number | undefined
|
|
25
49
|
|
|
26
50
|
try {
|
|
27
|
-
|
|
28
|
-
if (!argv.path || !(argv.path as string[])[0]) {
|
|
51
|
+
if (files.length === 0) {
|
|
29
52
|
const reader = process.stdin!.getReader()
|
|
30
53
|
|
|
31
54
|
try {
|
|
32
55
|
while (true) {
|
|
33
56
|
const { done, value } = await reader.read()
|
|
34
57
|
if (done) break
|
|
58
|
+
if (value.length > 0) {
|
|
59
|
+
lastByte = value[value.length - 1]
|
|
60
|
+
}
|
|
35
61
|
await writer.write(value)
|
|
36
62
|
}
|
|
37
63
|
} finally {
|
|
38
64
|
reader.releaseLock()
|
|
39
65
|
}
|
|
40
66
|
|
|
67
|
+
if (isTTY && lastByte !== undefined && lastByte !== 0x0A) {
|
|
68
|
+
await writer.write(new Uint8Array([0x0A]))
|
|
69
|
+
}
|
|
70
|
+
|
|
41
71
|
return 0
|
|
42
72
|
}
|
|
43
73
|
|
|
44
|
-
// Otherwise process files
|
|
45
|
-
const files = (argv.path as string[]) || []
|
|
46
|
-
const bytes = argv.bytes as string | undefined
|
|
47
74
|
for (const file of files) {
|
|
48
75
|
const fullPath = path.resolve(shell.cwd, file)
|
|
49
76
|
|
|
@@ -64,13 +91,15 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
64
91
|
const data = new Uint8Array(chunkSize)
|
|
65
92
|
const readSize = Math.min(chunkSize, stat.size - bytesRead)
|
|
66
93
|
await handle.read(data, 0, readSize, bytesRead)
|
|
67
|
-
|
|
94
|
+
const chunk = data.subarray(0, readSize)
|
|
95
|
+
if (chunk.length > 0) {
|
|
96
|
+
lastByte = chunk[chunk.length - 1]
|
|
97
|
+
}
|
|
98
|
+
await writer.write(chunk)
|
|
68
99
|
bytesRead += readSize
|
|
69
100
|
}
|
|
70
101
|
} else {
|
|
71
102
|
const device = await shell.context.fs.promises.open(fullPath)
|
|
72
|
-
const maxBytes = bytes ? parseInt(bytes) : undefined
|
|
73
|
-
let totalBytesRead = 0
|
|
74
103
|
const chunkSize = 1024
|
|
75
104
|
const data = new Uint8Array(chunkSize)
|
|
76
105
|
let bytesRead = 0
|
|
@@ -80,25 +109,27 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
80
109
|
const result = await device.read(data)
|
|
81
110
|
bytesRead = result.bytesRead
|
|
82
111
|
if (bytesRead > 0) {
|
|
83
|
-
const
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
totalBytesRead += bytesToWrite
|
|
112
|
+
const chunk = data.subarray(0, bytesRead)
|
|
113
|
+
if (chunk.length > 0) {
|
|
114
|
+
lastByte = chunk[chunk.length - 1]
|
|
87
115
|
}
|
|
116
|
+
await writer.write(chunk)
|
|
88
117
|
}
|
|
89
|
-
} while (bytesRead > 0
|
|
118
|
+
} while (bytesRead > 0)
|
|
90
119
|
}
|
|
91
120
|
} finally {
|
|
92
121
|
kernel.terminal.events.off(TerminalEvents.INTERRUPT, interruptHandler)
|
|
93
122
|
}
|
|
94
123
|
}
|
|
95
124
|
|
|
125
|
+
if (isTTY && lastByte !== undefined && lastByte !== 0x0A) {
|
|
126
|
+
await writer.write(new Uint8Array([0x0A]))
|
|
127
|
+
}
|
|
128
|
+
|
|
96
129
|
return 0
|
|
97
130
|
} finally {
|
|
98
131
|
writer.releaseLock()
|
|
99
|
-
await writeStdout(process, terminal, '\n')
|
|
100
132
|
}
|
|
101
133
|
}
|
|
102
134
|
})
|
|
103
135
|
}
|
|
104
|
-
|
package/src/commands/cd.ts
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
|
-
import type { Kernel, Shell, Terminal } from '@ecmaos/types'
|
|
2
|
+
import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
3
3
|
import { TerminalCommand } from '../shared/terminal-command.js'
|
|
4
|
+
import { writelnStdout } from '../shared/helpers.js'
|
|
5
|
+
|
|
6
|
+
function printUsage(process: Process | undefined, terminal: Terminal): void {
|
|
7
|
+
const usage = `Usage: cd [DIRECTORY]
|
|
8
|
+
Change the shell working directory.
|
|
9
|
+
|
|
10
|
+
DIRECTORY the directory to change to (default: $HOME)
|
|
11
|
+
--help display this help and exit`
|
|
12
|
+
writelnStdout(process, terminal, usage)
|
|
13
|
+
}
|
|
4
14
|
|
|
5
15
|
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
6
16
|
return new TerminalCommand({
|
|
@@ -9,18 +19,36 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
9
19
|
kernel,
|
|
10
20
|
shell,
|
|
11
21
|
terminal,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
22
|
+
run: async (pid: number, argv: string[]) => {
|
|
23
|
+
const process = kernel.processes.get(pid) as Process | undefined
|
|
24
|
+
|
|
25
|
+
if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
|
|
26
|
+
printUsage(process, terminal)
|
|
27
|
+
return 0
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const destination = argv.length > 0 && argv[0] && !argv[0].startsWith('-') ? argv[0] : shell.cwd
|
|
18
31
|
const fullPath = destination ? path.resolve(shell.cwd, destination) : shell.cwd
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
await shell.context.fs.promises.access(fullPath)
|
|
35
|
+
shell.cwd = fullPath
|
|
36
|
+
localStorage.setItem(`cwd:${shell.credentials.uid}`, fullPath)
|
|
37
|
+
return 0
|
|
38
|
+
} catch (error) {
|
|
39
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
40
|
+
if (process) {
|
|
41
|
+
const writer = process.stderr.getWriter()
|
|
42
|
+
try {
|
|
43
|
+
await writer.write(new TextEncoder().encode(`cd: ${destination}: ${errorMessage}\n`))
|
|
44
|
+
} finally {
|
|
45
|
+
writer.releaseLock()
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
terminal.write(`cd: ${destination}: ${errorMessage}\n`)
|
|
49
|
+
}
|
|
50
|
+
return 1
|
|
51
|
+
}
|
|
23
52
|
}
|
|
24
53
|
})
|
|
25
54
|
}
|
|
26
|
-
|
package/src/commands/chmod.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
import chalk from 'chalk'
|
|
3
|
-
import type { CommandLineOptions } from 'command-line-args'
|
|
4
3
|
import type { Kernel, Process, Shell, Terminal } 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: chmod [OPTION]... MODE[,MODE]... FILE...
|
|
9
|
+
Change the mode of each FILE to MODE.
|
|
10
|
+
|
|
11
|
+
--help display this help and exit`
|
|
12
|
+
writelnStderr(process, terminal, usage)
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
9
16
|
return new TerminalCommand({
|
|
10
17
|
command: 'chmod',
|
|
@@ -12,12 +19,21 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
12
19
|
kernel,
|
|
13
20
|
shell,
|
|
14
21
|
terminal,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
run: async (pid: number, argv: string[]) => {
|
|
23
|
+
const process = kernel.processes.get(pid) as Process | undefined
|
|
24
|
+
|
|
25
|
+
if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
|
|
26
|
+
printUsage(process, terminal)
|
|
27
|
+
return 0
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const args: string[] = []
|
|
31
|
+
for (const arg of argv) {
|
|
32
|
+
if (arg && !arg.startsWith('-')) {
|
|
33
|
+
args.push(arg)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
21
37
|
if (args.length === 0) {
|
|
22
38
|
await writelnStderr(process, terminal, chalk.red('chmod: missing operand'))
|
|
23
39
|
await writelnStderr(process, terminal, 'Try \'chmod --help\' for more information.')
|
|
@@ -25,11 +41,21 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
25
41
|
}
|
|
26
42
|
|
|
27
43
|
const [mode, target] = args
|
|
28
|
-
if (!mode || !target)
|
|
44
|
+
if (!mode || !target) {
|
|
45
|
+
await writelnStderr(process, terminal, chalk.red('chmod: missing operand'))
|
|
46
|
+
return 1
|
|
47
|
+
}
|
|
48
|
+
|
|
29
49
|
const fullPath = path.resolve(shell.cwd, target)
|
|
30
|
-
|
|
31
|
-
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
await shell.context.fs.promises.chmod(fullPath, mode)
|
|
53
|
+
return 0
|
|
54
|
+
} catch (error) {
|
|
55
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
56
|
+
await writelnStderr(process, terminal, `chmod: ${target}: ${errorMessage}`)
|
|
57
|
+
return 1
|
|
58
|
+
}
|
|
32
59
|
}
|
|
33
60
|
})
|
|
34
61
|
}
|
|
35
|
-
|
|
@@ -0,0 +1,169 @@
|
|
|
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: comm [OPTION]... FILE1 FILE2
|
|
9
|
+
Compare two sorted files line by line.
|
|
10
|
+
|
|
11
|
+
-1 suppress lines unique to FILE1
|
|
12
|
+
-2 suppress lines unique to FILE2
|
|
13
|
+
-3 suppress lines that appear in both files
|
|
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: 'comm',
|
|
21
|
+
description: 'Compare two sorted files line by line',
|
|
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 suppress1 = false
|
|
37
|
+
let suppress2 = false
|
|
38
|
+
let suppress3 = 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 === '-1') {
|
|
45
|
+
suppress1 = true
|
|
46
|
+
} else if (arg === '-2') {
|
|
47
|
+
suppress2 = true
|
|
48
|
+
} else if (arg === '-3') {
|
|
49
|
+
suppress3 = true
|
|
50
|
+
} else if (!arg.startsWith('-')) {
|
|
51
|
+
if (files.length < 2) {
|
|
52
|
+
files.push(arg)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (files.length !== 2) {
|
|
58
|
+
await writelnStderr(process, terminal, 'comm: exactly two files must be specified')
|
|
59
|
+
return 1
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const file1 = files[0]
|
|
63
|
+
const file2 = files[1]
|
|
64
|
+
if (!file1 || !file2) {
|
|
65
|
+
await writelnStderr(process, terminal, 'comm: exactly two files must be specified')
|
|
66
|
+
return 1
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const writer = process.stdout.getWriter()
|
|
70
|
+
|
|
71
|
+
const readFileLines = async (filePath: string): Promise<string[]> => {
|
|
72
|
+
if (filePath.startsWith('/dev')) {
|
|
73
|
+
throw new Error('cannot comm device files')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let interrupted = false
|
|
77
|
+
const interruptHandler = () => { interrupted = true }
|
|
78
|
+
kernel.terminal.events.on(TerminalEvents.INTERRUPT, interruptHandler)
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const handle = await shell.context.fs.promises.open(filePath, 'r')
|
|
82
|
+
const stat = await shell.context.fs.promises.stat(filePath)
|
|
83
|
+
|
|
84
|
+
const decoder = new TextDecoder()
|
|
85
|
+
let content = ''
|
|
86
|
+
let bytesRead = 0
|
|
87
|
+
const chunkSize = 1024
|
|
88
|
+
|
|
89
|
+
while (bytesRead < stat.size) {
|
|
90
|
+
if (interrupted) break
|
|
91
|
+
const data = new Uint8Array(chunkSize)
|
|
92
|
+
const readSize = Math.min(chunkSize, stat.size - bytesRead)
|
|
93
|
+
await handle.read(data, 0, readSize, bytesRead)
|
|
94
|
+
const chunk = data.subarray(0, readSize)
|
|
95
|
+
content += decoder.decode(chunk, { stream: true })
|
|
96
|
+
bytesRead += readSize
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const lines = content.split('\n')
|
|
100
|
+
if (lines[lines.length - 1] === '') {
|
|
101
|
+
lines.pop()
|
|
102
|
+
}
|
|
103
|
+
return lines
|
|
104
|
+
} finally {
|
|
105
|
+
kernel.terminal.events.off(TerminalEvents.INTERRUPT, interruptHandler)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const fullPath1 = path.resolve(shell.cwd || '/', file1)
|
|
111
|
+
const fullPath2 = path.resolve(shell.cwd || '/', file2)
|
|
112
|
+
|
|
113
|
+
const lines1 = await readFileLines(fullPath1)
|
|
114
|
+
const lines2 = await readFileLines(fullPath2)
|
|
115
|
+
|
|
116
|
+
let i = 0
|
|
117
|
+
let j = 0
|
|
118
|
+
|
|
119
|
+
while (i < lines1.length || j < lines2.length) {
|
|
120
|
+
if (i >= lines1.length) {
|
|
121
|
+
if (!suppress2) {
|
|
122
|
+
const prefix = suppress1 ? '' : '\t'
|
|
123
|
+
await writer.write(new TextEncoder().encode(prefix + lines2[j] + '\n'))
|
|
124
|
+
}
|
|
125
|
+
j++
|
|
126
|
+
} else if (j >= lines2.length) {
|
|
127
|
+
if (!suppress1) {
|
|
128
|
+
await writer.write(new TextEncoder().encode(lines1[i] + '\n'))
|
|
129
|
+
}
|
|
130
|
+
i++
|
|
131
|
+
} else {
|
|
132
|
+
const line1 = lines1[i]
|
|
133
|
+
const line2 = lines2[j]
|
|
134
|
+
if (!line1 || !line2) {
|
|
135
|
+
break
|
|
136
|
+
}
|
|
137
|
+
const cmp = line1.localeCompare(line2)
|
|
138
|
+
if (cmp < 0) {
|
|
139
|
+
if (!suppress1) {
|
|
140
|
+
await writer.write(new TextEncoder().encode(lines1[i] + '\n'))
|
|
141
|
+
}
|
|
142
|
+
i++
|
|
143
|
+
} else if (cmp > 0) {
|
|
144
|
+
if (!suppress2) {
|
|
145
|
+
const prefix = suppress1 ? '' : '\t'
|
|
146
|
+
await writer.write(new TextEncoder().encode(prefix + lines2[j] + '\n'))
|
|
147
|
+
}
|
|
148
|
+
j++
|
|
149
|
+
} else {
|
|
150
|
+
if (!suppress3) {
|
|
151
|
+
const prefix = suppress1 && suppress2 ? '' : suppress1 ? '\t' : suppress2 ? '' : '\t\t'
|
|
152
|
+
await writer.write(new TextEncoder().encode(prefix + lines1[i] + '\n'))
|
|
153
|
+
}
|
|
154
|
+
i++
|
|
155
|
+
j++
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return 0
|
|
161
|
+
} catch (error) {
|
|
162
|
+
await writelnStderr(process, terminal, `comm: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
163
|
+
return 1
|
|
164
|
+
} finally {
|
|
165
|
+
writer.releaseLock()
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
}
|
package/src/commands/cp.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: cp [OPTION]... SOURCE... DEST
|
|
8
|
+
Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.
|
|
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,19 +18,69 @@ 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
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
+
const args: string[] = []
|
|
30
|
+
for (const arg of argv) {
|
|
31
|
+
if (arg && !arg.startsWith('-')) {
|
|
32
|
+
args.push(arg)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (args.length < 2) {
|
|
37
|
+
await writelnStderr(process, terminal, 'cp: missing file operand')
|
|
38
|
+
await writelnStderr(process, terminal, "Try 'cp --help' for more information.")
|
|
39
|
+
return 1
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const sources = args.slice(0, -1)
|
|
43
|
+
const destination = args[args.length - 1]
|
|
44
|
+
|
|
45
|
+
if (!destination) {
|
|
46
|
+
await writelnStderr(process, terminal, 'cp: missing destination file operand')
|
|
47
|
+
return 1
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let hasError = false
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const destinationStats = await shell.context.fs.promises.stat(path.resolve(shell.cwd, destination)).catch(() => null)
|
|
54
|
+
const isDestinationDir = destinationStats?.isDirectory()
|
|
55
|
+
|
|
56
|
+
if (sources.length > 1 && !isDestinationDir) {
|
|
57
|
+
await writelnStderr(process, terminal, `cp: target '${destination}' is not a directory`)
|
|
58
|
+
return 1
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
for (const source of sources) {
|
|
62
|
+
if (!source) continue
|
|
63
|
+
|
|
64
|
+
const sourcePath = path.resolve(shell.cwd, source)
|
|
65
|
+
const finalDestination = isDestinationDir
|
|
66
|
+
? path.join(path.resolve(shell.cwd, destination), path.basename(source))
|
|
67
|
+
: path.resolve(shell.cwd, destination)
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
await shell.context.fs.promises.copyFile(sourcePath, finalDestination)
|
|
71
|
+
} catch (error) {
|
|
72
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
73
|
+
await writelnStderr(process, terminal, `cp: ${source}: ${errorMessage}`)
|
|
74
|
+
hasError = true
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
79
|
+
await writelnStderr(process, terminal, `cp: ${errorMessage}`)
|
|
80
|
+
hasError = true
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return hasError ? 1 : 0
|
|
25
84
|
}
|
|
26
85
|
})
|
|
27
86
|
}
|
|
28
|
-
|