@ecmaos/coreutils 0.1.1 → 0.1.3
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 +182 -0
- package/dist/commands/cd.d.ts.map +1 -1
- package/dist/commands/cd.js +1 -7
- package/dist/commands/cd.js.map +1 -1
- package/dist/commands/hex.d.ts +4 -0
- package/dist/commands/hex.d.ts.map +1 -0
- package/dist/commands/hex.js +82 -0
- package/dist/commands/hex.js.map +1 -0
- package/dist/commands/less.d.ts +4 -0
- package/dist/commands/less.d.ts.map +1 -0
- package/dist/commands/less.js +173 -0
- package/dist/commands/less.js.map +1 -0
- package/dist/commands/ln.d.ts +4 -0
- package/dist/commands/ln.d.ts.map +1 -0
- package/dist/commands/ln.js +104 -0
- package/dist/commands/ln.js.map +1 -0
- package/dist/commands/ls.d.ts.map +1 -1
- package/dist/commands/ls.js +91 -11
- package/dist/commands/ls.js.map +1 -1
- package/dist/commands/sed.d.ts +4 -0
- package/dist/commands/sed.d.ts.map +1 -0
- package/dist/commands/sed.js +381 -0
- package/dist/commands/sed.js.map +1 -0
- package/dist/commands/tee.d.ts +4 -0
- package/dist/commands/tee.d.ts.map +1 -0
- package/dist/commands/tee.js +87 -0
- package/dist/commands/tee.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
- package/src/commands/cd.ts +1 -8
- package/src/commands/hex.ts +92 -0
- package/src/commands/less.ts +192 -0
- package/src/commands/ln.ts +108 -0
- package/src/commands/ls.ts +85 -11
- package/src/commands/sed.ts +436 -0
- package/src/commands/tee.ts +93 -0
- package/src/index.ts +16 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { TerminalEvents } from '@ecmaos/types';
|
|
3
|
+
import { TerminalCommand } from '../shared/terminal-command.js';
|
|
4
|
+
import { writelnStderr } from '../shared/helpers.js';
|
|
5
|
+
export function createCommand(kernel, shell, terminal) {
|
|
6
|
+
return new TerminalCommand({
|
|
7
|
+
command: 'tee',
|
|
8
|
+
description: 'Read from standard input and write to standard output and files',
|
|
9
|
+
kernel,
|
|
10
|
+
shell,
|
|
11
|
+
terminal,
|
|
12
|
+
options: [
|
|
13
|
+
{ name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
|
|
14
|
+
{ name: 'append', type: Boolean, alias: 'a', description: 'Append to the given files, do not overwrite' },
|
|
15
|
+
{ name: 'ignore-interrupts', type: Boolean, alias: 'i', description: 'Ignore interrupt signals' },
|
|
16
|
+
{ name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, multiple: true, description: 'File(s) to write to' }
|
|
17
|
+
],
|
|
18
|
+
run: async (argv, process) => {
|
|
19
|
+
if (!process)
|
|
20
|
+
return 1;
|
|
21
|
+
if (!process.stdin) {
|
|
22
|
+
await writelnStderr(process, terminal, 'tee: No input provided');
|
|
23
|
+
return 1;
|
|
24
|
+
}
|
|
25
|
+
const files = argv.path || [];
|
|
26
|
+
const append = argv.append || false;
|
|
27
|
+
const ignoreInterrupts = argv['ignore-interrupts'] || false;
|
|
28
|
+
const writer = process.stdout.getWriter();
|
|
29
|
+
try {
|
|
30
|
+
const filePaths = [];
|
|
31
|
+
for (const file of files) {
|
|
32
|
+
const expandedPath = shell.expandTilde(file);
|
|
33
|
+
const fullPath = path.resolve(shell.cwd, expandedPath);
|
|
34
|
+
if (!append) {
|
|
35
|
+
try {
|
|
36
|
+
await shell.context.fs.promises.writeFile(fullPath, '');
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
await writelnStderr(process, terminal, `tee: ${file}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
40
|
+
return 1;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
filePaths.push({ path: file, fullPath });
|
|
44
|
+
}
|
|
45
|
+
const reader = process.stdin.getReader();
|
|
46
|
+
let interrupted = false;
|
|
47
|
+
const interruptHandler = () => { interrupted = true; };
|
|
48
|
+
if (!ignoreInterrupts) {
|
|
49
|
+
kernel.terminal.events.on(TerminalEvents.INTERRUPT, interruptHandler);
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
while (true) {
|
|
53
|
+
if (interrupted)
|
|
54
|
+
break;
|
|
55
|
+
const { done, value } = await reader.read();
|
|
56
|
+
if (done)
|
|
57
|
+
break;
|
|
58
|
+
await writer.write(value);
|
|
59
|
+
for (const fileInfo of filePaths) {
|
|
60
|
+
try {
|
|
61
|
+
await shell.context.fs.promises.appendFile(fileInfo.fullPath, value);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
await writelnStderr(process, terminal, `tee: ${fileInfo.path}: ${error instanceof Error ? error.message : 'Write error'}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
reader.releaseLock();
|
|
71
|
+
if (!ignoreInterrupts) {
|
|
72
|
+
kernel.terminal.events.off(TerminalEvents.INTERRUPT, interruptHandler);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
await writelnStderr(process, terminal, `tee: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
writer.releaseLock();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=tee.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tee.js","sourceRoot":"","sources":["../../src/commands/tee.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export { createCommand as createCd } from './commands/cd.js';
|
|
|
8
8
|
export { createCommand as createChmod } from './commands/chmod.js';
|
|
9
9
|
export { createCommand as createCp } from './commands/cp.js';
|
|
10
10
|
export { createCommand as createEcho } from './commands/echo.js';
|
|
11
|
+
export { createCommand as createLn } from './commands/ln.js';
|
|
11
12
|
export { createCommand as createLs } from './commands/ls.js';
|
|
12
13
|
export { createCommand as createMkdir } from './commands/mkdir.js';
|
|
13
14
|
export { createCommand as createMv } from './commands/mv.js';
|
|
@@ -16,6 +17,10 @@ export { createCommand as createRm } from './commands/rm.js';
|
|
|
16
17
|
export { createCommand as createRmdir } from './commands/rmdir.js';
|
|
17
18
|
export { createCommand as createStat } from './commands/stat.js';
|
|
18
19
|
export { createCommand as createTouch } from './commands/touch.js';
|
|
20
|
+
export { createCommand as createHex } from './commands/hex.js';
|
|
21
|
+
export { createCommand as createLess } from './commands/less.js';
|
|
22
|
+
export { createCommand as createSed } from './commands/sed.js';
|
|
23
|
+
export { createCommand as createTee } from './commands/tee.js';
|
|
19
24
|
/**
|
|
20
25
|
* Creates all coreutils commands.
|
|
21
26
|
* This function replaces the TerminalCommands function from the kernel.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAG9D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9D,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAG9D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9D,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAuB5F,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,aAAa,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAE9D;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAA;CAAE,CAqBtH;AAGD,OAAO,EAAE,iBAAiB,IAAI,gBAAgB,EAAE,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { createCommand as createCd } from './commands/cd.js';
|
|
|
7
7
|
import { createCommand as createChmod } from './commands/chmod.js';
|
|
8
8
|
import { createCommand as createCp } from './commands/cp.js';
|
|
9
9
|
import { createCommand as createEcho } from './commands/echo.js';
|
|
10
|
+
import { createCommand as createLn } from './commands/ln.js';
|
|
10
11
|
import { createCommand as createLs } from './commands/ls.js';
|
|
11
12
|
import { createCommand as createMkdir } from './commands/mkdir.js';
|
|
12
13
|
import { createCommand as createMv } from './commands/mv.js';
|
|
@@ -15,12 +16,17 @@ import { createCommand as createRm } from './commands/rm.js';
|
|
|
15
16
|
import { createCommand as createRmdir } from './commands/rmdir.js';
|
|
16
17
|
import { createCommand as createStat } from './commands/stat.js';
|
|
17
18
|
import { createCommand as createTouch } from './commands/touch.js';
|
|
19
|
+
import { createCommand as createHex } from './commands/hex.js';
|
|
20
|
+
import { createCommand as createLess } from './commands/less.js';
|
|
21
|
+
import { createCommand as createSed } from './commands/sed.js';
|
|
22
|
+
import { createCommand as createTee } from './commands/tee.js';
|
|
18
23
|
// Export individual command factories
|
|
19
24
|
export { createCommand as createCat } from './commands/cat.js';
|
|
20
25
|
export { createCommand as createCd } from './commands/cd.js';
|
|
21
26
|
export { createCommand as createChmod } from './commands/chmod.js';
|
|
22
27
|
export { createCommand as createCp } from './commands/cp.js';
|
|
23
28
|
export { createCommand as createEcho } from './commands/echo.js';
|
|
29
|
+
export { createCommand as createLn } from './commands/ln.js';
|
|
24
30
|
export { createCommand as createLs } from './commands/ls.js';
|
|
25
31
|
export { createCommand as createMkdir } from './commands/mkdir.js';
|
|
26
32
|
export { createCommand as createMv } from './commands/mv.js';
|
|
@@ -29,6 +35,10 @@ export { createCommand as createRm } from './commands/rm.js';
|
|
|
29
35
|
export { createCommand as createRmdir } from './commands/rmdir.js';
|
|
30
36
|
export { createCommand as createStat } from './commands/stat.js';
|
|
31
37
|
export { createCommand as createTouch } from './commands/touch.js';
|
|
38
|
+
export { createCommand as createHex } from './commands/hex.js';
|
|
39
|
+
export { createCommand as createLess } from './commands/less.js';
|
|
40
|
+
export { createCommand as createSed } from './commands/sed.js';
|
|
41
|
+
export { createCommand as createTee } from './commands/tee.js';
|
|
32
42
|
/**
|
|
33
43
|
* Creates all coreutils commands.
|
|
34
44
|
* This function replaces the TerminalCommands function from the kernel.
|
|
@@ -40,6 +50,7 @@ export function createAllCommands(kernel, shell, terminal) {
|
|
|
40
50
|
chmod: createChmod(kernel, shell, terminal),
|
|
41
51
|
cp: createCp(kernel, shell, terminal),
|
|
42
52
|
echo: createEcho(kernel, shell, terminal),
|
|
53
|
+
ln: createLn(kernel, shell, terminal),
|
|
43
54
|
ls: createLs(kernel, shell, terminal),
|
|
44
55
|
mkdir: createMkdir(kernel, shell, terminal),
|
|
45
56
|
mv: createMv(kernel, shell, terminal),
|
|
@@ -47,7 +58,11 @@ export function createAllCommands(kernel, shell, terminal) {
|
|
|
47
58
|
rm: createRm(kernel, shell, terminal),
|
|
48
59
|
rmdir: createRmdir(kernel, shell, terminal),
|
|
49
60
|
stat: createStat(kernel, shell, terminal),
|
|
50
|
-
touch: createTouch(kernel, shell, terminal)
|
|
61
|
+
touch: createTouch(kernel, shell, terminal),
|
|
62
|
+
hex: createHex(kernel, shell, terminal),
|
|
63
|
+
less: createLess(kernel, shell, terminal),
|
|
64
|
+
sed: createSed(kernel, shell, terminal),
|
|
65
|
+
tee: createTee(kernel, shell, terminal)
|
|
51
66
|
};
|
|
52
67
|
}
|
|
53
68
|
// For backward compatibility, export as TerminalCommands
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,+BAA+B;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAE9D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAE5F,2BAA2B;AAC3B,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecmaos/coreutils",
|
|
3
3
|
"description": "ecmaOS: Core utilities for the ecmaOS framework",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"license": "MIT+Apache-2.0",
|
|
6
6
|
"homepage": "https://ecmaos.sh",
|
|
7
7
|
"repository": "https://github.com/ecmaos/ecmaos",
|
|
@@ -24,13 +24,15 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@zenfs/core": "^2.4.2",
|
|
26
26
|
"@zip.js/zip.js": "^2.7.52",
|
|
27
|
+
"ansi-escape-sequences": "^6.2.4",
|
|
27
28
|
"chalk": "^5.3.0",
|
|
28
29
|
"command-line-args": "^6.0.0",
|
|
29
30
|
"command-line-usage": "^7.0.3",
|
|
30
31
|
"human-format": "^1.2.1",
|
|
31
|
-
"@ecmaos/types": "^0.4.
|
|
32
|
+
"@ecmaos/types": "^0.4.2"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
35
|
+
"@types/ansi-escape-sequences": "^4.0.4",
|
|
34
36
|
"@types/command-line-args": "^5.2.3",
|
|
35
37
|
"@types/command-line-usage": "^5.0.4"
|
|
36
38
|
},
|
package/src/commands/cd.ts
CHANGED
|
@@ -14,14 +14,7 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
14
14
|
{ name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, description: 'The path to the directory to change to' }
|
|
15
15
|
],
|
|
16
16
|
run: async (argv) => {
|
|
17
|
-
|
|
18
|
-
if (destination && destination.startsWith('~')) {
|
|
19
|
-
const home = shell.env.get('HOME')
|
|
20
|
-
if (home) {
|
|
21
|
-
destination = destination.replace(/^~(?=$|\/)/, home)
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
17
|
+
const destination = (argv.path as string) || shell.cwd
|
|
25
18
|
const fullPath = destination ? path.resolve(shell.cwd, destination) : shell.cwd
|
|
26
19
|
await shell.context.fs.promises.access(fullPath)
|
|
27
20
|
shell.cwd = fullPath
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import type { CommandLineOptions } from 'command-line-args'
|
|
3
|
+
import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
4
|
+
import { TerminalCommand } from '../shared/terminal-command.js'
|
|
5
|
+
import { writelnStdout, writelnStderr } from '../shared/helpers.js'
|
|
6
|
+
|
|
7
|
+
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
8
|
+
return new TerminalCommand({
|
|
9
|
+
command: 'hex',
|
|
10
|
+
description: 'Display file contents in hexadecimal format',
|
|
11
|
+
kernel,
|
|
12
|
+
shell,
|
|
13
|
+
terminal,
|
|
14
|
+
options: [
|
|
15
|
+
{ name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
|
|
16
|
+
{ name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, description: 'The path to the file to display' }
|
|
17
|
+
],
|
|
18
|
+
run: async (argv: CommandLineOptions, process?: Process) => {
|
|
19
|
+
const filePath = argv.path as string | undefined
|
|
20
|
+
|
|
21
|
+
if (!filePath) {
|
|
22
|
+
await writelnStderr(process, terminal, 'Usage: hex <file>')
|
|
23
|
+
return 1
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const fullPath = path.resolve(shell.cwd, filePath)
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const exists = await shell.context.fs.promises.exists(fullPath)
|
|
30
|
+
if (!exists) {
|
|
31
|
+
await writelnStderr(process, terminal, `hex: ${filePath}: No such file or directory`)
|
|
32
|
+
return 1
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const stats = await shell.context.fs.promises.stat(fullPath)
|
|
36
|
+
if (stats.isDirectory()) {
|
|
37
|
+
await writelnStderr(process, terminal, `hex: ${filePath}: Is a directory`)
|
|
38
|
+
return 1
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const data = await shell.context.fs.promises.readFile(fullPath)
|
|
42
|
+
const bytesPerLine = 16
|
|
43
|
+
|
|
44
|
+
for (let offset = 0; offset < data.length; offset += bytesPerLine) {
|
|
45
|
+
const lineBytes = data.slice(offset, offset + bytesPerLine)
|
|
46
|
+
const offsetHex = offset.toString(16).padStart(8, '0')
|
|
47
|
+
|
|
48
|
+
const hexGroups: string[] = []
|
|
49
|
+
const asciiChars: string[] = []
|
|
50
|
+
|
|
51
|
+
for (let i = 0; i < bytesPerLine; i++) {
|
|
52
|
+
if (i < lineBytes.length) {
|
|
53
|
+
const byte = lineBytes[i]
|
|
54
|
+
if (byte === undefined) continue
|
|
55
|
+
|
|
56
|
+
const hex = byte.toString(16).padStart(2, '0')
|
|
57
|
+
|
|
58
|
+
if (i % 2 === 0) {
|
|
59
|
+
hexGroups.push(hex)
|
|
60
|
+
} else {
|
|
61
|
+
hexGroups[hexGroups.length - 1] += hex
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (byte >= 32 && byte <= 126) {
|
|
65
|
+
asciiChars.push(String.fromCharCode(byte))
|
|
66
|
+
} else {
|
|
67
|
+
asciiChars.push('.')
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
if (i % 2 === 0) {
|
|
71
|
+
hexGroups.push(' ')
|
|
72
|
+
} else {
|
|
73
|
+
hexGroups[hexGroups.length - 1] += ' '
|
|
74
|
+
}
|
|
75
|
+
asciiChars.push(' ')
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const hexString = hexGroups.join(' ').padEnd(47, ' ')
|
|
80
|
+
const asciiString = asciiChars.join('')
|
|
81
|
+
|
|
82
|
+
await writelnStdout(process, terminal, `${offsetHex}: ${hexString} ${asciiString}`)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return 0
|
|
86
|
+
} catch (error) {
|
|
87
|
+
await writelnStderr(process, terminal, `hex: ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
88
|
+
return 1
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import ansi from 'ansi-escape-sequences'
|
|
3
|
+
import type { CommandLineOptions } from 'command-line-args'
|
|
4
|
+
import type { IDisposable } from '@xterm/xterm'
|
|
5
|
+
import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
6
|
+
import { TerminalCommand } from '../shared/terminal-command.js'
|
|
7
|
+
import { writelnStderr } from '../shared/helpers.js'
|
|
8
|
+
|
|
9
|
+
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
10
|
+
return new TerminalCommand({
|
|
11
|
+
command: 'less',
|
|
12
|
+
description: 'View file contents interactively',
|
|
13
|
+
kernel,
|
|
14
|
+
shell,
|
|
15
|
+
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 to view' }
|
|
19
|
+
],
|
|
20
|
+
run: async (argv: CommandLineOptions, process?: Process) => {
|
|
21
|
+
if (!process) return 1
|
|
22
|
+
|
|
23
|
+
let lines: string[] = []
|
|
24
|
+
let currentLine = 0
|
|
25
|
+
let keyListener: IDisposable | null = null
|
|
26
|
+
let linesRendered = 0
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
if (argv.path) {
|
|
30
|
+
const filePath = argv.path as string
|
|
31
|
+
const expandedPath = shell.expandTilde(filePath)
|
|
32
|
+
const fullPath = path.resolve(shell.cwd, expandedPath)
|
|
33
|
+
|
|
34
|
+
const exists = await shell.context.fs.promises.exists(fullPath)
|
|
35
|
+
if (!exists) {
|
|
36
|
+
await writelnStderr(process, terminal, `less: ${filePath}: No such file or directory`)
|
|
37
|
+
return 1
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const stats = await shell.context.fs.promises.stat(fullPath)
|
|
41
|
+
if (stats.isDirectory()) {
|
|
42
|
+
await writelnStderr(process, terminal, `less: ${filePath}: Is a directory`)
|
|
43
|
+
return 1
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const content = await shell.context.fs.promises.readFile(fullPath, 'utf-8')
|
|
47
|
+
lines = content.split('\n')
|
|
48
|
+
} else {
|
|
49
|
+
if (!process.stdin) {
|
|
50
|
+
await writelnStderr(process, terminal, 'less: No input provided')
|
|
51
|
+
return 1
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const reader = process.stdin.getReader()
|
|
55
|
+
const decoder = new TextDecoder()
|
|
56
|
+
const chunks: string[] = []
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
while (true) {
|
|
60
|
+
const { done, value } = await reader.read()
|
|
61
|
+
if (done) break
|
|
62
|
+
chunks.push(decoder.decode(value, { stream: true }))
|
|
63
|
+
}
|
|
64
|
+
chunks.push(decoder.decode(new Uint8Array(), { stream: false }))
|
|
65
|
+
} finally {
|
|
66
|
+
reader.releaseLock()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const content = chunks.join('')
|
|
70
|
+
lines = content.split('\n')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (lines.length === 0) {
|
|
74
|
+
return 0
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
terminal.unlisten()
|
|
78
|
+
terminal.write('\n')
|
|
79
|
+
terminal.write(ansi.cursor.hide)
|
|
80
|
+
|
|
81
|
+
const rows = terminal.rows
|
|
82
|
+
const displayRows = rows - 1
|
|
83
|
+
|
|
84
|
+
const render = () => {
|
|
85
|
+
const maxLine = Math.max(0, lines.length - displayRows)
|
|
86
|
+
if (currentLine > maxLine) {
|
|
87
|
+
currentLine = maxLine
|
|
88
|
+
}
|
|
89
|
+
if (currentLine < 0) {
|
|
90
|
+
currentLine = 0
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (linesRendered > 0) {
|
|
94
|
+
terminal.write(ansi.cursor.up(linesRendered))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const endLine = Math.min(currentLine + displayRows, lines.length)
|
|
98
|
+
linesRendered = 0
|
|
99
|
+
|
|
100
|
+
for (let i = currentLine; i < endLine; i++) {
|
|
101
|
+
terminal.write(ansi.erase.inLine(2))
|
|
102
|
+
const line = lines[i] || ''
|
|
103
|
+
terminal.write(line)
|
|
104
|
+
linesRendered++
|
|
105
|
+
if (i < endLine - 1) {
|
|
106
|
+
terminal.write('\n')
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
for (let i = endLine - currentLine; i < displayRows; i++) {
|
|
111
|
+
terminal.write('\n')
|
|
112
|
+
terminal.write(ansi.erase.inLine(2))
|
|
113
|
+
linesRendered++
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const percentage = lines.length > 0 ? Math.round(((endLine / lines.length) * 100)) : 100
|
|
117
|
+
const statusLine = `-- ${currentLine + 1}-${endLine} / ${lines.length} (${percentage}%)`
|
|
118
|
+
terminal.write('\n')
|
|
119
|
+
terminal.write(ansi.erase.inLine(2))
|
|
120
|
+
terminal.write(statusLine)
|
|
121
|
+
linesRendered++
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
render()
|
|
125
|
+
|
|
126
|
+
await new Promise<void>((resolve) => {
|
|
127
|
+
keyListener = terminal.onKey(async ({ domEvent }) => {
|
|
128
|
+
const keyName = domEvent.key
|
|
129
|
+
|
|
130
|
+
switch (keyName) {
|
|
131
|
+
case 'q':
|
|
132
|
+
case 'Q':
|
|
133
|
+
case 'Escape':
|
|
134
|
+
if (keyListener) {
|
|
135
|
+
keyListener.dispose()
|
|
136
|
+
keyListener = null
|
|
137
|
+
}
|
|
138
|
+
terminal.write(ansi.cursor.show)
|
|
139
|
+
terminal.write('\n')
|
|
140
|
+
terminal.listen()
|
|
141
|
+
resolve()
|
|
142
|
+
return
|
|
143
|
+
case 'ArrowUp':
|
|
144
|
+
if (currentLine > 0) {
|
|
145
|
+
currentLine--
|
|
146
|
+
render()
|
|
147
|
+
}
|
|
148
|
+
break
|
|
149
|
+
case 'ArrowDown':
|
|
150
|
+
case 'Enter':
|
|
151
|
+
currentLine++
|
|
152
|
+
render()
|
|
153
|
+
break
|
|
154
|
+
case 'PageDown':
|
|
155
|
+
case ' ':
|
|
156
|
+
currentLine = Math.min(currentLine + displayRows, Math.max(0, lines.length - displayRows))
|
|
157
|
+
render()
|
|
158
|
+
break
|
|
159
|
+
case 'PageUp':
|
|
160
|
+
case 'b':
|
|
161
|
+
case 'B':
|
|
162
|
+
currentLine = Math.max(0, currentLine - displayRows)
|
|
163
|
+
render()
|
|
164
|
+
break
|
|
165
|
+
case 'Home':
|
|
166
|
+
case 'g':
|
|
167
|
+
currentLine = 0
|
|
168
|
+
render()
|
|
169
|
+
break
|
|
170
|
+
case 'End':
|
|
171
|
+
case 'G':
|
|
172
|
+
currentLine = Math.max(0, lines.length - displayRows)
|
|
173
|
+
render()
|
|
174
|
+
break
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
return 0
|
|
180
|
+
} catch (error) {
|
|
181
|
+
if (keyListener) {
|
|
182
|
+
(keyListener as IDisposable).dispose()
|
|
183
|
+
}
|
|
184
|
+
terminal.write(ansi.cursor.show)
|
|
185
|
+
terminal.write('\n')
|
|
186
|
+
terminal.listen()
|
|
187
|
+
await writelnStderr(process, terminal, `less: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
188
|
+
return 1
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import chalk from 'chalk'
|
|
3
|
+
import type { CommandLineOptions } from 'command-line-args'
|
|
4
|
+
import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
5
|
+
import { TerminalCommand } from '../shared/terminal-command.js'
|
|
6
|
+
import { writelnStdout, writelnStderr } from '../shared/helpers.js'
|
|
7
|
+
|
|
8
|
+
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
9
|
+
return new TerminalCommand({
|
|
10
|
+
command: 'ln',
|
|
11
|
+
description: 'Create links between files',
|
|
12
|
+
kernel,
|
|
13
|
+
shell,
|
|
14
|
+
terminal,
|
|
15
|
+
options: [
|
|
16
|
+
{ name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
|
|
17
|
+
{ name: 'symbolic', type: Boolean, alias: 's', description: 'Create symbolic links instead of hard links' },
|
|
18
|
+
{ name: 'force', type: Boolean, alias: 'f', description: 'Remove existing destination files' },
|
|
19
|
+
{ name: 'verbose', type: Boolean, alias: 'v', description: 'Print name of each linked file' },
|
|
20
|
+
{ name: 'args', type: String, multiple: true, defaultOption: true, description: 'The target and optional link name' }
|
|
21
|
+
],
|
|
22
|
+
run: async (argv: CommandLineOptions, process?: Process) => {
|
|
23
|
+
const args = (argv.args as string[]) || []
|
|
24
|
+
const symbolic = argv.symbolic as boolean || false
|
|
25
|
+
const force = argv.force as boolean || false
|
|
26
|
+
const verbose = argv.verbose as boolean || false
|
|
27
|
+
|
|
28
|
+
if (args.length === 0) {
|
|
29
|
+
await writelnStderr(process, terminal, chalk.red('ln: missing file operand'))
|
|
30
|
+
await writelnStderr(process, terminal, 'Try \'ln --help\' for more information.')
|
|
31
|
+
return 1
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const target = args[0]
|
|
35
|
+
if (!target) {
|
|
36
|
+
await writelnStderr(process, terminal, chalk.red('ln: missing file operand'))
|
|
37
|
+
return 1
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const targetPath = path.resolve(shell.cwd, target)
|
|
41
|
+
|
|
42
|
+
let targetStats
|
|
43
|
+
try {
|
|
44
|
+
targetStats = await shell.context.fs.promises.stat(targetPath)
|
|
45
|
+
if (!targetStats.isFile() && !targetStats.isDirectory()) {
|
|
46
|
+
await writelnStderr(process, terminal, chalk.red(`ln: ${target}: invalid target`))
|
|
47
|
+
return 1
|
|
48
|
+
}
|
|
49
|
+
if (!symbolic && targetStats.isDirectory()) {
|
|
50
|
+
await writelnStderr(process, terminal, chalk.red(`ln: ${target}: hard link not allowed for directory`))
|
|
51
|
+
return 1
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
await writelnStderr(process, terminal, chalk.red(`ln: ${target}: No such file or directory`))
|
|
55
|
+
return 1
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let linkName: string
|
|
59
|
+
if (args.length === 1) {
|
|
60
|
+
linkName = path.join(shell.cwd, path.basename(target))
|
|
61
|
+
} else {
|
|
62
|
+
const linkNameInput = args[1]
|
|
63
|
+
if (!linkNameInput) {
|
|
64
|
+
await writelnStderr(process, terminal, chalk.red('ln: missing link name'))
|
|
65
|
+
return 1
|
|
66
|
+
}
|
|
67
|
+
const linkNamePath = path.resolve(shell.cwd, linkNameInput)
|
|
68
|
+
const linkNameStats = await shell.context.fs.promises.stat(linkNamePath).catch(() => null)
|
|
69
|
+
if (linkNameStats?.isDirectory()) {
|
|
70
|
+
linkName = path.join(linkNamePath, path.basename(target))
|
|
71
|
+
} else {
|
|
72
|
+
linkName = linkNamePath
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const linkExists = await shell.context.fs.promises.stat(linkName).catch(() => null)
|
|
78
|
+
if (linkExists) {
|
|
79
|
+
if (force) {
|
|
80
|
+
if (linkExists.isDirectory()) {
|
|
81
|
+
await shell.context.fs.promises.rmdir(linkName)
|
|
82
|
+
} else {
|
|
83
|
+
await shell.context.fs.promises.unlink(linkName)
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
await writelnStderr(process, terminal, chalk.red(`ln: ${linkName}: File exists`))
|
|
87
|
+
return 1
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (symbolic) {
|
|
92
|
+
await shell.context.fs.promises.symlink(targetPath, linkName)
|
|
93
|
+
} else {
|
|
94
|
+
await shell.context.fs.promises.link(targetPath, linkName)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (verbose) {
|
|
98
|
+
await writelnStdout(process, terminal, linkName)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return 0
|
|
102
|
+
} catch (error) {
|
|
103
|
+
await writelnStderr(process, terminal, chalk.red(`ln: ${(error as Error).message}`))
|
|
104
|
+
return 1
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
}
|