From 62151fe1378974421385f0bf15571d18114c8968 Mon Sep 17 00:00:00 2001 From: Dmitry Kostikov Date: Mon, 26 Jun 2023 14:43:05 +0400 Subject: [PATCH 01/13] Implemented main loop, exit and up commands --- package.json | 19 ++++++ src/commands/exit.js | 7 +++ src/commands/up.js | 14 +++++ src/file_manager.js | 134 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 174 insertions(+) create mode 100644 package.json create mode 100644 src/commands/exit.js create mode 100644 src/commands/up.js create mode 100644 src/file_manager.js diff --git a/package.json b/package.json new file mode 100644 index 0000000..94add39 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "node-nodejs-basics", + "version": "1.0.0", + "description": "This repository is the part of nodejs-assignments https://github.com/AlreadyBored/nodejs-assignments", + "type": "module", + "scripts": { + "start": "node src/file_manager.js", + "test": "node src/file_manager.js -- --username=docroot" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/docroot/nodejs_file_manager.git" + }, + "author": "docroot", + "bugs": { + "url": "https://github.com/docroot/nodejs_file_manager/issues" + }, + "homepage": "https://github.com/docroot/nodejs_file_manager.git#readme" +} diff --git a/src/commands/exit.js b/src/commands/exit.js new file mode 100644 index 0000000..620d144 --- /dev/null +++ b/src/commands/exit.js @@ -0,0 +1,7 @@ +const cmd_exit = (ctx, args) => { + process.exit(0); +} + +export { + cmd_exit +} diff --git a/src/commands/up.js b/src/commands/up.js new file mode 100644 index 0000000..2b475b9 --- /dev/null +++ b/src/commands/up.js @@ -0,0 +1,14 @@ +import path from 'path'; + +const cmd_up = (ctx, args) => { + if (ctx.cwd !== ctx.rootDir) { + const upDir = path.join(ctx.cwd, '..'); + process.chdir(upDir); + ctx['cwd'] = process.cwd(); + } +} + +export { + cmd_up +} + diff --git a/src/file_manager.js b/src/file_manager.js new file mode 100644 index 0000000..e7aeeb1 --- /dev/null +++ b/src/file_manager.js @@ -0,0 +1,134 @@ +import os from 'os'; +import path from 'path'; + +import { cmd_exit } from "./commands/exit.js"; +import { cmd_up } from "./commands/up.js"; + +const sigint = process.platform === 'win32' ? 'SIGBREAK' : 'SIGINT'; + +const invInputStr = 'Invalid input'; + +const commands = { + '.exit': { 'cmd': 'cmd_exit', 'min_args': 0 }, + 'up': { 'cmd': 'cmd_up', 'min_args': 0 }, +}; + + +const printPromt = (ctx) => { + process.stdout.write(`You are currently in ${ctx['cwd']}\n`); + process.stdout.write(`Input a command $ `); +} + + +const getUsernameFromArgs = () => { + const re = /^--username=.+/; + const args = process.argv; + for (let i = 0; i < args.length; i++) { + if (re.test(args[i])) { + return args[i].split(/=/)[1]; + } + } + return 'Anonymous'; +}; + + +const homedir = os.homedir(); +const rootDir = path.parse(process.cwd()).root; +const pathDelimiter = path.delimiter; +const username = getUsernameFromArgs(); + + +const context = { + 'cwd': homedir, + 'homedir': homedir, + 'username': username, + 'rootDir': rootDir, + 'pathDelimiter': pathDelimiter, +} + + +const parseCommand = (command) => { + const args = []; + let curArg = ''; + let i = 0; + let p = ''; + let s = ''; + while (i < command.length) { + const char = command[i]; + if (s == "'") { + if (char !== "'") { + curArg = curArg + char; + } + else { + if (curArg.length > 0) args.push(curArg); + curArg = ''; + s = ''; + } + } + else if (char !== ' ') { + if (char === "'" && (p === '' || p === ' ')) { + s = char; + } + else { + curArg = curArg + char; + } + } + else { + if (curArg.length > 0) args.push(curArg); + curArg = ''; + s = ''; + } + p = char; + i++; + continue; + } + if (curArg.length > 0) { + if (s != "'") { + args.push(curArg); + } + else { + throw new Error(invInputStr); + } + } + return args; +} + + +const processUserInput = (chunk) => { + const cmdString = chunk.toString().replace(/[\r\n]+$/g, '').replace(/^\s+/, '').replace(/\s+$/, ''); + if (cmdString.length === 0) return; + try { + const args = parseCommand(cmdString); + if (args.length === 0 || args[0].length === 0) throw new Error(); + + let cmd = args[0]; + // console.debug('command: [' + cmd + ']'); + + if (!commands.hasOwnProperty(cmd)) throw new Error(); + + if (commands[cmd]['min_args'] > args.length - 1) throw new Error(); + + eval(commands[cmd]['cmd'])(context, args.slice(1)); + } catch (error) { + // console.log(error); + console.log(invInputStr); + } + printPromt(context); +}; + + +process.chdir(homedir); +console.log(`Welcome to the File Manager, ${username}!`); +printPromt(context); + +process.on('exit', (code) => { + console.log(`Thank you for using File Manager, ${username}, goodbye!`); +}); + +process.on('SIGINT', () => { + console.log(''); + process.exit(0); +}); + +process.stdin.on('data', processUserInput); + From ad52356ce4f81d0d23a0a203d0e7f84677f4edb2 Mon Sep 17 00:00:00 2001 From: Dmitry Kostikov Date: Mon, 26 Jun 2023 15:16:32 +0400 Subject: [PATCH 02/13] Implemented cd command --- src/commands/cd.js | 18 ++++++++++++++++++ src/commands/up.js | 4 ++-- src/file_manager.js | 5 ++++- 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 src/commands/cd.js diff --git a/src/commands/cd.js b/src/commands/cd.js new file mode 100644 index 0000000..a5f56c9 --- /dev/null +++ b/src/commands/cd.js @@ -0,0 +1,18 @@ +import path from 'path'; + +const cmd_cd = (ctx, args) => { + if (args.length > 1) throw new Error(); + let newDir; + if (path.isAbsolute(args[0])) { + newDir = args[0]; + } + else { + newDir = path.join(ctx.cwd, args[0]); + } + process.chdir(newDir); + ctx.cwd = process.cwd(); +} + +export { + cmd_cd +} \ No newline at end of file diff --git a/src/commands/up.js b/src/commands/up.js index 2b475b9..819a9b0 100644 --- a/src/commands/up.js +++ b/src/commands/up.js @@ -2,9 +2,9 @@ import path from 'path'; const cmd_up = (ctx, args) => { if (ctx.cwd !== ctx.rootDir) { - const upDir = path.join(ctx.cwd, '..'); + const upDir = path.dirname(ctx.cwd); process.chdir(upDir); - ctx['cwd'] = process.cwd(); + ctx.cwd = process.cwd(); } } diff --git a/src/file_manager.js b/src/file_manager.js index e7aeeb1..9fc05b7 100644 --- a/src/file_manager.js +++ b/src/file_manager.js @@ -3,6 +3,7 @@ import path from 'path'; import { cmd_exit } from "./commands/exit.js"; import { cmd_up } from "./commands/up.js"; +import { cmd_cd } from "./commands/cd.js"; const sigint = process.platform === 'win32' ? 'SIGBREAK' : 'SIGINT'; @@ -11,6 +12,7 @@ const invInputStr = 'Invalid input'; const commands = { '.exit': { 'cmd': 'cmd_exit', 'min_args': 0 }, 'up': { 'cmd': 'cmd_up', 'min_args': 0 }, + 'cd': { 'cmd': 'cmd_cd', 'min_args': 1 }, }; @@ -35,15 +37,16 @@ const getUsernameFromArgs = () => { const homedir = os.homedir(); const rootDir = path.parse(process.cwd()).root; const pathDelimiter = path.delimiter; +const dirSeparator = path.sep; const username = getUsernameFromArgs(); - const context = { 'cwd': homedir, 'homedir': homedir, 'username': username, 'rootDir': rootDir, 'pathDelimiter': pathDelimiter, + 'dirSeparator': dirSeparator, } From 1c9f71417904e2acbc6db40455656e11d1fcc019 Mon Sep 17 00:00:00 2001 From: Dmitry Kostikov Date: Mon, 26 Jun 2023 17:16:47 +0400 Subject: [PATCH 03/13] Main loop was refactored. ls command was added. --- src/commands/ls.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/file_manager.js | 37 ++++++++++++++++++++++++++----------- 2 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 src/commands/ls.js diff --git a/src/commands/ls.js b/src/commands/ls.js new file mode 100644 index 0000000..94288ac --- /dev/null +++ b/src/commands/ls.js @@ -0,0 +1,44 @@ +import fs from 'fs/promises'; +import path from 'path'; + +const cmd_ls = async (ctx, args) => { + if (args.length > 1) throw new Error(); + let dir = ctx.cwd; + if (args.length === 1) { + if (path.isAbsolute(args[0])) { + dir = args[0]; + } + else { + dir = path.join(ctx.cwd, args[0]); + } + } + try { + const files = await fs.readdir(dir); + const filesInfo = []; + for (let i = 0; i < files.length; i++) { + const file = files[i]; + const filePath = path.join(dir, file); + try { + const fileStats = await fs.stat(filePath); + filesInfo.push({ 'Name': file, 'Type': (fileStats.isDirectory() ? 'DIR' : 'File'), 'size': fileStats.size, }); + } catch (error) { + } + } + filesInfo.sort((a, b) => { + if (a.Type === b.Type) { + return a.Name.localeCompare(b.Name); + } + if (a.Type === 'DIR') { + return -1; + } + return 1; + }); + console.table(filesInfo); + } catch (error) { + throw error; + } +} + +export { + cmd_ls +} diff --git a/src/file_manager.js b/src/file_manager.js index 9fc05b7..39e40d5 100644 --- a/src/file_manager.js +++ b/src/file_manager.js @@ -1,9 +1,11 @@ import os from 'os'; import path from 'path'; +import * as readline from 'readline/promises'; import { cmd_exit } from "./commands/exit.js"; import { cmd_up } from "./commands/up.js"; import { cmd_cd } from "./commands/cd.js"; +import { cmd_ls } from "./commands/ls.js"; const sigint = process.platform === 'win32' ? 'SIGBREAK' : 'SIGINT'; @@ -13,12 +15,12 @@ const commands = { '.exit': { 'cmd': 'cmd_exit', 'min_args': 0 }, 'up': { 'cmd': 'cmd_up', 'min_args': 0 }, 'cd': { 'cmd': 'cmd_cd', 'min_args': 1 }, + 'ls': { 'cmd': 'cmd_ls', 'min_args': 0 }, }; const printPromt = (ctx) => { - process.stdout.write(`You are currently in ${ctx['cwd']}\n`); - process.stdout.write(`Input a command $ `); + process.stdout.write(`\x1b[37mYou are currently in \x1b[1m${ctx['cwd']}\x1b[0m\n`); } @@ -96,8 +98,12 @@ const parseCommand = (command) => { return args; } +const execCmd = async (cmd, context, args) => { + const cmdFunc = eval(cmd); + await cmdFunc(context, args); +} -const processUserInput = (chunk) => { +const processUserInput = async (chunk) => { const cmdString = chunk.toString().replace(/[\r\n]+$/g, '').replace(/^\s+/, '').replace(/\s+$/, ''); if (cmdString.length === 0) return; try { @@ -111,9 +117,8 @@ const processUserInput = (chunk) => { if (commands[cmd]['min_args'] > args.length - 1) throw new Error(); - eval(commands[cmd]['cmd'])(context, args.slice(1)); + await execCmd(commands[cmd]['cmd'], context, args.slice(1)); } catch (error) { - // console.log(error); console.log(invInputStr); } printPromt(context); @@ -124,14 +129,24 @@ process.chdir(homedir); console.log(`Welcome to the File Manager, ${username}!`); printPromt(context); -process.on('exit', (code) => { - console.log(`Thank you for using File Manager, ${username}, goodbye!`); +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout }); -process.on('SIGINT', () => { - console.log(''); - process.exit(0); +process.on('exit', (code) => { + console.log(`\nThank you for using File Manager, ${username}, goodbye!`); + rl.close(); }); -process.stdin.on('data', processUserInput); +// process.on('SIGINT', () => { +// console.log('\n'); +// process.exit(0); +// }); + + +do { + const answer = await rl.question('Input a command > '); + await processUserInput(answer); +} while (1); From c34071afd94ebd5a991123745135f4d770f2dacc Mon Sep 17 00:00:00 2001 From: Dmitry Kostikov Date: Mon, 26 Jun 2023 18:56:57 +0400 Subject: [PATCH 04/13] cat command was implemented. --- src/file_manager.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/file_manager.js b/src/file_manager.js index 39e40d5..5f863cd 100644 --- a/src/file_manager.js +++ b/src/file_manager.js @@ -1,21 +1,24 @@ import os from 'os'; import path from 'path'; -import * as readline from 'readline/promises'; +import readline from 'readline/promises'; import { cmd_exit } from "./commands/exit.js"; import { cmd_up } from "./commands/up.js"; import { cmd_cd } from "./commands/cd.js"; import { cmd_ls } from "./commands/ls.js"; +import { cmd_cat } from "./commands/cat.js"; const sigint = process.platform === 'win32' ? 'SIGBREAK' : 'SIGINT'; -const invInputStr = 'Invalid input'; +const invInputStr = '\x1b[1m\x1b[33mInvalid input\x1b[0m'; +const operationFailed = '\x1b[1m\x1b[31mOperation failed\x1b[0m'; const commands = { '.exit': { 'cmd': 'cmd_exit', 'min_args': 0 }, 'up': { 'cmd': 'cmd_up', 'min_args': 0 }, 'cd': { 'cmd': 'cmd_cd', 'min_args': 1 }, 'ls': { 'cmd': 'cmd_ls', 'min_args': 0 }, + 'cat': { 'cmd': 'cmd_cat', 'min_args': 1 }, }; @@ -117,7 +120,11 @@ const processUserInput = async (chunk) => { if (commands[cmd]['min_args'] > args.length - 1) throw new Error(); - await execCmd(commands[cmd]['cmd'], context, args.slice(1)); + try { + await execCmd(commands[cmd]['cmd'], context, args.slice(1)); + } catch (error) { + console.log(operationFailed); + } } catch (error) { console.log(invInputStr); } From 4b5e3096534526276a4df37ea7cc920d101e29eb Mon Sep 17 00:00:00 2001 From: Dmitry Kostikov Date: Mon, 26 Jun 2023 19:28:42 +0400 Subject: [PATCH 05/13] add command was implemented. --- src/commands/add.js | 19 +++++++++++++++++++ src/commands/cat.js | 30 ++++++++++++++++++++++++++++++ src/file_manager.js | 2 ++ 3 files changed, 51 insertions(+) create mode 100644 src/commands/add.js create mode 100644 src/commands/cat.js diff --git a/src/commands/add.js b/src/commands/add.js new file mode 100644 index 0000000..642d0eb --- /dev/null +++ b/src/commands/add.js @@ -0,0 +1,19 @@ +import fs from 'fs/promises'; +import path from 'path'; + +const cmd_add = async (ctx, args) => { + if (args.length !== 1) throw new Error(); + let filePath; + if (path.isAbsolute(args[0])) { + filePath = args[0]; + } + else { + filePath = path.join(ctx.cwd, args[0]); + } + + await fs.writeFile(filePath, '', { flag: 'wx' }); +} + +export { + cmd_add +} diff --git a/src/commands/cat.js b/src/commands/cat.js new file mode 100644 index 0000000..5ab9e64 --- /dev/null +++ b/src/commands/cat.js @@ -0,0 +1,30 @@ +import fs from 'fs'; +import path from 'path'; + +const cmd_cat = async (ctx, args) => { + if (args.length !== 1) throw new Error(); + let filePath; + if (path.isAbsolute(args[0])) { + filePath = args[0]; + } + else { + filePath = path.join(ctx.cwd, args[0]); + } + + await new Promise((resolve, reject) => { + const inputStream = fs.createReadStream(filePath, 'utf8'); + inputStream.pipe(process.stdout); + inputStream.on('end', () => { + inputStream.close(); + resolve(); + }) + inputStream.on('error', (error) => { + reject(error); + }) + } + ).catch((error) => { throw error; }); +} + +export { + cmd_cat +} diff --git a/src/file_manager.js b/src/file_manager.js index 5f863cd..6b88895 100644 --- a/src/file_manager.js +++ b/src/file_manager.js @@ -7,6 +7,7 @@ import { cmd_up } from "./commands/up.js"; import { cmd_cd } from "./commands/cd.js"; import { cmd_ls } from "./commands/ls.js"; import { cmd_cat } from "./commands/cat.js"; +import { cmd_add } from "./commands/add.js"; const sigint = process.platform === 'win32' ? 'SIGBREAK' : 'SIGINT'; @@ -19,6 +20,7 @@ const commands = { 'cd': { 'cmd': 'cmd_cd', 'min_args': 1 }, 'ls': { 'cmd': 'cmd_ls', 'min_args': 0 }, 'cat': { 'cmd': 'cmd_cat', 'min_args': 1 }, + 'add': { 'cmd': 'cmd_add', 'min_args': 1 }, }; From 2b8b5dcfcad1f23f679e80ffcb73e719d61e6451 Mon Sep 17 00:00:00 2001 From: Dmitry Kostikov Date: Mon, 26 Jun 2023 20:05:02 +0400 Subject: [PATCH 06/13] rn command was implemented. --- src/commands/rn.js | 25 +++++++++++++++++++++++++ src/file_manager.js | 3 +++ 2 files changed, 28 insertions(+) create mode 100644 src/commands/rn.js diff --git a/src/commands/rn.js b/src/commands/rn.js new file mode 100644 index 0000000..a62a06e --- /dev/null +++ b/src/commands/rn.js @@ -0,0 +1,25 @@ +import fs from 'fs/promises'; +import path from 'path'; + +const cmd_rn = async (ctx, args) => { + if (args.length !== 2) throw new Error(); + let filePaths = []; + args.forEach(file => { + if (path.isAbsolute(file)) { + filePaths.push(file); + } + else { + filePaths.push(path.join(ctx.cwd, file)); + } + }); + console.log(filePaths); + await fs.access(filePaths[1], fs.constants.F_OK) + .then(() => { + throw new Error(); + }); + await fs.rename(filePaths[0], filePaths[1]); +} + +export { + cmd_rn +} diff --git a/src/file_manager.js b/src/file_manager.js index 6b88895..ea1a1d0 100644 --- a/src/file_manager.js +++ b/src/file_manager.js @@ -8,6 +8,8 @@ import { cmd_cd } from "./commands/cd.js"; import { cmd_ls } from "./commands/ls.js"; import { cmd_cat } from "./commands/cat.js"; import { cmd_add } from "./commands/add.js"; +import { cmd_rn } from "./commands/rn.js"; + const sigint = process.platform === 'win32' ? 'SIGBREAK' : 'SIGINT'; @@ -21,6 +23,7 @@ const commands = { 'ls': { 'cmd': 'cmd_ls', 'min_args': 0 }, 'cat': { 'cmd': 'cmd_cat', 'min_args': 1 }, 'add': { 'cmd': 'cmd_add', 'min_args': 1 }, + 'rn': { 'cmd': 'cmd_rn', 'min_args': 2 }, }; From dfd4e29bf3e28e6a4f3148829f481e48e3057633 Mon Sep 17 00:00:00 2001 From: Dmitry Kostikov Date: Mon, 26 Jun 2023 21:32:08 +0400 Subject: [PATCH 07/13] cp command was imlemented. --- src/commands/cp.js | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/commands/cp.js diff --git a/src/commands/cp.js b/src/commands/cp.js new file mode 100644 index 0000000..ffd164a --- /dev/null +++ b/src/commands/cp.js @@ -0,0 +1,53 @@ +import fs from 'fs'; +import fsP from 'fs/promises'; +import path from 'path'; + +const cmd_cp = async (ctx, args) => { + if (args.length !== 2) throw new Error(); + let filePaths = []; + const fileName = path.basename(args[0]); + + if (path.isAbsolute(args[0])) { + filePaths.push(args[0]); + } + else { + filePaths.push(path.join(ctx.cwd, args[0])); + } + + if (path.isAbsolute(args[1])) { + filePaths.push(path.join(args[1], fileName)); + } + else { + filePaths.push(path.join(ctx.cwd, args[1], fileName)); + } + + await fsP.access(filePaths[0], fsP.constants.F_OK) + .catch((error) => { throw error; }); + + await fsP.access(filePaths[1], fsP.constants.F_OK) + .then(() => { + throw new Error(); + }).catch((error) => { if (error.code !== 'ENOENT') throw error; }); + + await new Promise((resolve, reject) => { + const srcStream = fs.createReadStream(filePaths[0], { flag: 'rx' }); + const dstStream = fs.createWriteStream(filePaths[1], { flag: 'wx' }); + dstStream.on('finish', () => { + dstStream.close(); + srcStream.close(); + resolve(); + }); + srcStream.on('error', (error) => { + reject(error); + }); + dstStream.on('error', (error) => { + reject(error); + }); + srcStream.pipe(dstStream); + } + ).catch((error) => { srcStream.close(); dstStream.close(); throw error; }); +} + +export { + cmd_cp +} From be3e2fb03d6d05498db51e4dc2a09bd0706f3f16 Mon Sep 17 00:00:00 2001 From: Dmitry Kostikov Date: Mon, 26 Jun 2023 23:04:14 +0400 Subject: [PATCH 08/13] rm command was implemented. --- src/commands/rm.js | 21 +++++++++++++++++++++ src/file_manager.js | 5 +++++ 2 files changed, 26 insertions(+) create mode 100644 src/commands/rm.js diff --git a/src/commands/rm.js b/src/commands/rm.js new file mode 100644 index 0000000..1076ddd --- /dev/null +++ b/src/commands/rm.js @@ -0,0 +1,21 @@ +import fs from 'fs/promises'; +import path from 'path'; + +const cmd_rm = async (ctx, args) => { + if (args.length !== 1) throw new Error(); + let filePath; + if (path.isAbsolute(args[0])) { + filePath = args[0]; + } + else { + filePath = path.join(ctx.cwd, args[0]); + } + + const fileInfo = await fs.stat(filePath); + if (fileInfo.isDirectory()) throw new Error(); + await fs.unlink(filePath); +} + +export { + cmd_rm +} diff --git a/src/file_manager.js b/src/file_manager.js index ea1a1d0..599593d 100644 --- a/src/file_manager.js +++ b/src/file_manager.js @@ -9,6 +9,8 @@ import { cmd_ls } from "./commands/ls.js"; import { cmd_cat } from "./commands/cat.js"; import { cmd_add } from "./commands/add.js"; import { cmd_rn } from "./commands/rn.js"; +import { cmd_cp } from "./commands/cp.js"; +import { cmd_rm } from "./commands/rm.js"; const sigint = process.platform === 'win32' ? 'SIGBREAK' : 'SIGINT'; @@ -24,6 +26,8 @@ const commands = { 'cat': { 'cmd': 'cmd_cat', 'min_args': 1 }, 'add': { 'cmd': 'cmd_add', 'min_args': 1 }, 'rn': { 'cmd': 'cmd_rn', 'min_args': 2 }, + 'cp': { 'cmd': 'cmd_cp', 'min_args': 2 }, + 'rm': { 'cmd': 'cmd_rm', 'min_args': 1 }, }; @@ -128,6 +132,7 @@ const processUserInput = async (chunk) => { try { await execCmd(commands[cmd]['cmd'], context, args.slice(1)); } catch (error) { + // console.log(error); console.log(operationFailed); } } catch (error) { From 8f255ee65308919ec559b90165832a773b4e9d85 Mon Sep 17 00:00:00 2001 From: Dmitry Kostikov Date: Mon, 26 Jun 2023 23:22:48 +0400 Subject: [PATCH 09/13] mv command was implemented. --- src/commands/mv.js | 12 ++++++++++++ src/file_manager.js | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 src/commands/mv.js diff --git a/src/commands/mv.js b/src/commands/mv.js new file mode 100644 index 0000000..bfea544 --- /dev/null +++ b/src/commands/mv.js @@ -0,0 +1,12 @@ +import { cmd_cp } from './cp.js'; +import { cmd_rm } from './rm.js'; + +const cmd_mv = async (ctx, args) => { + if (args.length !== 2) throw new Error(); + await cmd_cp(ctx, args); + await cmd_rm(ctx, [args[0]]); +} + +export { + cmd_mv +} diff --git a/src/file_manager.js b/src/file_manager.js index 599593d..6898abe 100644 --- a/src/file_manager.js +++ b/src/file_manager.js @@ -11,6 +11,7 @@ import { cmd_add } from "./commands/add.js"; import { cmd_rn } from "./commands/rn.js"; import { cmd_cp } from "./commands/cp.js"; import { cmd_rm } from "./commands/rm.js"; +import { cmd_mv } from "./commands/mv.js"; const sigint = process.platform === 'win32' ? 'SIGBREAK' : 'SIGINT'; @@ -27,7 +28,7 @@ const commands = { 'add': { 'cmd': 'cmd_add', 'min_args': 1 }, 'rn': { 'cmd': 'cmd_rn', 'min_args': 2 }, 'cp': { 'cmd': 'cmd_cp', 'min_args': 2 }, - 'rm': { 'cmd': 'cmd_rm', 'min_args': 1 }, + 'mv': { 'cmd': 'cmd_mv', 'min_args': 1 }, }; From 8a4e0b3c83c8293426d579a33009bb5e86698fe2 Mon Sep 17 00:00:00 2001 From: Dmitry Kostikov Date: Tue, 27 Jun 2023 00:13:54 +0400 Subject: [PATCH 10/13] os command was implemented. --- src/commands/os.js | 43 +++++++++++++++++++++++++++++++++++++++++++ src/commands/rn.js | 1 - src/file_manager.js | 2 ++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/commands/os.js diff --git a/src/commands/os.js b/src/commands/os.js new file mode 100644 index 0000000..cff9145 --- /dev/null +++ b/src/commands/os.js @@ -0,0 +1,43 @@ +import os from 'os'; + +const cmd_os = async (ctx, args) => { + if (args.length !== 1) throw new Error(); + + const re = /^--.+/; + if (!re.test(args[0])) throw new Error(); + + const opt = args[0].substring(2); + + switch (opt) { + case 'EOL': + const eol = JSON.stringify(os.EOL); + console.log(`Default system End-Of-Line is \x1b[1m\x1b[32m${eol}\x1b[0m`); + break; + case 'cpus': + const cpus = os.cpus(); + console.log(`Number of CPUs is [\x1b[1m\x1b[32m${cpus.length}\x1b[0m]`); + const cpuInfo = []; + cpus.forEach(cpu => { + cpuInfo.push({ 'Model': cpu.model, 'Clock rate (GHz)': (cpu.speed / 1000).toFixed(2) }); + }); + console.table(cpuInfo); + break; + case 'homedir': + console.log(`The HOMEDIR is [\x1b[1m\x1b[32m${ctx.homedir}\x1b[0m]`); + break; + case 'username': + const username = os.userInfo().username; + console.log(`The USERNAME is [\x1b[1m\x1b[32m${username}\x1b[0m]`); + break; + case 'architecture': + console.log(`The ARCHITECTURE is [\x1b[1m\x1b[32m${process.arch}\x1b[0m]`); + break; + + default: + throw new Error(); + } +} + +export { + cmd_os +} diff --git a/src/commands/rn.js b/src/commands/rn.js index a62a06e..ad99b6f 100644 --- a/src/commands/rn.js +++ b/src/commands/rn.js @@ -12,7 +12,6 @@ const cmd_rn = async (ctx, args) => { filePaths.push(path.join(ctx.cwd, file)); } }); - console.log(filePaths); await fs.access(filePaths[1], fs.constants.F_OK) .then(() => { throw new Error(); diff --git a/src/file_manager.js b/src/file_manager.js index 6898abe..9855e34 100644 --- a/src/file_manager.js +++ b/src/file_manager.js @@ -12,6 +12,7 @@ import { cmd_rn } from "./commands/rn.js"; import { cmd_cp } from "./commands/cp.js"; import { cmd_rm } from "./commands/rm.js"; import { cmd_mv } from "./commands/mv.js"; +import { cmd_os } from "./commands/os.js"; const sigint = process.platform === 'win32' ? 'SIGBREAK' : 'SIGINT'; @@ -29,6 +30,7 @@ const commands = { 'rn': { 'cmd': 'cmd_rn', 'min_args': 2 }, 'cp': { 'cmd': 'cmd_cp', 'min_args': 2 }, 'mv': { 'cmd': 'cmd_mv', 'min_args': 1 }, + 'os': { 'cmd': 'cmd_os', 'min_args': 1 }, }; From 488e66ef30c1fa45b9e8e492a860ec16ff69e6b2 Mon Sep 17 00:00:00 2001 From: Dmitry Kostikov Date: Tue, 27 Jun 2023 00:44:31 +0400 Subject: [PATCH 11/13] hash command was implemented. --- src/commands/hash.js | 37 +++++++++++++++++++++++++++++++++++++ src/file_manager.js | 2 ++ 2 files changed, 39 insertions(+) create mode 100644 src/commands/hash.js diff --git a/src/commands/hash.js b/src/commands/hash.js new file mode 100644 index 0000000..67f55c9 --- /dev/null +++ b/src/commands/hash.js @@ -0,0 +1,37 @@ +import fs from 'fs'; +import path from 'path'; +import crypto from 'crypto'; + +const cmd_hash = async (ctx, args) => { + if (args.length !== 1) throw new Error(); + let filePath; + if (path.isAbsolute(args[0])) { + filePath = args[0]; + } + else { + filePath = path.join(ctx.cwd, args[0]); + } + + const hash = crypto.createHash('sha256'); + const inputStream = fs.createReadStream(filePath); + + await new Promise((resolve, reject) => { + inputStream.on('end', () => { + inputStream.close(); + }) + inputStream.on('error', (error) => { + reject(error); + }) + hash.on('finish', () => { + resolve(); + }); + inputStream.pipe(hash); + } + ).catch((error) => { throw error; }); + + console.log(hash.digest('hex')); +} + +export { + cmd_hash +} diff --git a/src/file_manager.js b/src/file_manager.js index 9855e34..1076e71 100644 --- a/src/file_manager.js +++ b/src/file_manager.js @@ -13,6 +13,7 @@ import { cmd_cp } from "./commands/cp.js"; import { cmd_rm } from "./commands/rm.js"; import { cmd_mv } from "./commands/mv.js"; import { cmd_os } from "./commands/os.js"; +import { cmd_hash } from "./commands/hash.js"; const sigint = process.platform === 'win32' ? 'SIGBREAK' : 'SIGINT'; @@ -31,6 +32,7 @@ const commands = { 'cp': { 'cmd': 'cmd_cp', 'min_args': 2 }, 'mv': { 'cmd': 'cmd_mv', 'min_args': 1 }, 'os': { 'cmd': 'cmd_os', 'min_args': 1 }, + 'hash': { 'cmd': 'cmd_hash', 'min_args': 1 }, }; From 36cb154edaee38c2bf1f615a31a8ab3c752a73f7 Mon Sep 17 00:00:00 2001 From: Dmitry Kostikov Date: Tue, 27 Jun 2023 01:57:06 +0400 Subject: [PATCH 12/13] compress and decompress commands were implemented. --- src/commands/arch.js | 93 ++++++++++++++++++++++++++++++++++++++++++++ src/commands/cp.js | 11 +----- src/file_manager.js | 4 ++ 3 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 src/commands/arch.js diff --git a/src/commands/arch.js b/src/commands/arch.js new file mode 100644 index 0000000..60210ca --- /dev/null +++ b/src/commands/arch.js @@ -0,0 +1,93 @@ +import { error } from 'console'; +import fs, { lstat } from 'fs'; +import path from 'path'; +import zlib from 'zlib'; + +const cmd_compress = async (ctx, args) => { + if (args.length !== 1) throw new Error(); + let filePath; + if (path.isAbsolute(args[0])) { + filePath = args[0]; + } + else { + filePath = path.join(ctx.cwd, args[0]); + } + + const archPath = filePath + '.br'; + + const inputStream = fs.createReadStream(filePath); + const writeStream = fs.createWriteStream(archPath, { flags: 'wx' }); + + const brotliOptions = { + chunkSize: 64 * 1024, + params: { + [zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_GENERIC, + [zlib.constants.BROTLI_PARAM_QUALITY]: zlib.constants.BROTLI_MAX_QUALITY, + }, + }; + const brotliStream = zlib.createBrotliCompress(brotliOptions); + + await new Promise((resolve, reject) => { + inputStream.on('error', (error) => { + reject(error); + }) + brotliStream.on('error', (error) => { + reject(error); + }) + writeStream.on('error', (error) => { + reject(error); + }); + writeStream.on('finish', () => { + resolve(); + }); + inputStream.pipe(brotliStream).pipe(writeStream); + } + ).catch((error) => { throw error; }); +} + +const cmd_decompress = async (ctx, args) => { + if (args.length !== 1) throw new Error(); + let archPath; + if (path.isAbsolute(args[0])) { + archPath = args[0]; + } + else { + archPath = path.join(ctx.cwd, args[0]); + } + + const re = /\.br$/; + let filePath = archPath; + if (re.test(filePath)) { + filePath = filePath.slice(0, -3); + } + else { + filePath = filePath + '.unpkd'; + } + + const inputStream = fs.createReadStream(archPath); + const writeStream = fs.createWriteStream(filePath, { flags: 'wx' }); + + const brotliStream = zlib.createBrotliDecompress(); + + await new Promise((resolve, reject) => { + inputStream.on('error', (error) => { + reject(error); + }) + brotliStream.on('error', (error) => { + reject(error); + }) + writeStream.on('error', (error) => { + reject(error); + }); + writeStream.on('finish', () => { + resolve(); + }); + inputStream.pipe(brotliStream).pipe(writeStream); + } + ).catch((error) => { throw error; }); +} + +export { + cmd_compress, + cmd_decompress +} diff --git a/src/commands/cp.js b/src/commands/cp.js index ffd164a..69a70e4 100644 --- a/src/commands/cp.js +++ b/src/commands/cp.js @@ -21,17 +21,10 @@ const cmd_cp = async (ctx, args) => { filePaths.push(path.join(ctx.cwd, args[1], fileName)); } - await fsP.access(filePaths[0], fsP.constants.F_OK) - .catch((error) => { throw error; }); - - await fsP.access(filePaths[1], fsP.constants.F_OK) - .then(() => { - throw new Error(); - }).catch((error) => { if (error.code !== 'ENOENT') throw error; }); + const srcStream = fs.createReadStream(filePaths[0], { flags: 'r' }); + const dstStream = fs.createWriteStream(filePaths[1], { flags: 'wx' }); await new Promise((resolve, reject) => { - const srcStream = fs.createReadStream(filePaths[0], { flag: 'rx' }); - const dstStream = fs.createWriteStream(filePaths[1], { flag: 'wx' }); dstStream.on('finish', () => { dstStream.close(); srcStream.close(); diff --git a/src/file_manager.js b/src/file_manager.js index 1076e71..4acf90b 100644 --- a/src/file_manager.js +++ b/src/file_manager.js @@ -14,6 +14,7 @@ import { cmd_rm } from "./commands/rm.js"; import { cmd_mv } from "./commands/mv.js"; import { cmd_os } from "./commands/os.js"; import { cmd_hash } from "./commands/hash.js"; +import { cmd_compress, cmd_decompress } from "./commands/arch.js"; const sigint = process.platform === 'win32' ? 'SIGBREAK' : 'SIGINT'; @@ -30,9 +31,12 @@ const commands = { 'add': { 'cmd': 'cmd_add', 'min_args': 1 }, 'rn': { 'cmd': 'cmd_rn', 'min_args': 2 }, 'cp': { 'cmd': 'cmd_cp', 'min_args': 2 }, + 'rm': { 'cmd': 'cmd_rm', 'min_args': 1 }, 'mv': { 'cmd': 'cmd_mv', 'min_args': 1 }, 'os': { 'cmd': 'cmd_os', 'min_args': 1 }, 'hash': { 'cmd': 'cmd_hash', 'min_args': 1 }, + 'compress': { 'cmd': 'cmd_compress', 'min_args': 1 }, + 'decompress': { 'cmd': 'cmd_decompress', 'min_args': 1 }, }; From e7d8a9ed66614d7f917c6c8ca2960a674ad2efc8 Mon Sep 17 00:00:00 2001 From: Dmitry Kostikov Date: Tue, 27 Jun 2023 01:59:57 +0400 Subject: [PATCH 13/13] fix: Unused import removed. --- src/commands/cp.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/commands/cp.js b/src/commands/cp.js index 69a70e4..bf4c200 100644 --- a/src/commands/cp.js +++ b/src/commands/cp.js @@ -1,5 +1,4 @@ import fs from 'fs'; -import fsP from 'fs/promises'; import path from 'path'; const cmd_cp = async (ctx, args) => {