diff --git a/implement-shell-tools/cat/cat.js b/implement-shell-tools/cat/cat.js new file mode 100644 index 000000000..e9dcd9147 --- /dev/null +++ b/implement-shell-tools/cat/cat.js @@ -0,0 +1,73 @@ +const fs = require("fs"); + +const arguments = process.argv; +const userArguments = arguments.slice(2); + +const index = arguments[1].lastIndexOf("/"); +const currentWorkingDirectory = arguments[1].slice(0, index + 1); + +const flags = userArguments + .filter((argument) => argument.startsWith("-")) + .map((argument) => argument.slice(1)) + .join(""); + +const flagHandlers = { + b: bFlag, + n: nFlag, +}; + +const fileNames = userArguments.filter((argument) => !argument.startsWith("-")); +let allFilesContents = []; + +readFiles(); +executeFlags(); +printLines(); + +function readFiles() { + fileNames.forEach((fileName) => { + fileContent = readFile(fileName); + allFilesContents.push(fileContent.split("\n")); + }); +} + +function readFile(fileName) { + const filePath = currentWorkingDirectory + fileName; + return fs.readFileSync(filePath, "utf8").trimEnd(); +} + +function executeFlags() { + for (const flag of flags) { + if (flagHandlers[flag]) { + allFilesContents = flagHandlers[flag](); + } else { + console.error(`cat: illegal option -- ${flag}\nusage: cat [-belnstuv] [file ...]`); + process.exit(1); + } + } +} + +function bFlag() { + return allFilesContents.map((fileContent) => { + let lineNumber = 1; + return fileContent.map((line, index) => { + if (line == "") { + return `${line}`; + } + return `${String(lineNumber++).padStart(6, " ")} ${line}`; + }); + }); +} + +function nFlag() { + return allFilesContents.map((fileContent) => { + return fileContent.map((line, index) => `${String(index + 1).padStart(6, " ")} ${line}`); + }); +} + +function printLines() { + allFilesContents.forEach((fileContent) => { + fileContent.forEach((line) => { + console.log(line); + }); + }); +} diff --git a/implement-shell-tools/ls/ls.js b/implement-shell-tools/ls/ls.js new file mode 100644 index 000000000..8646dfef6 --- /dev/null +++ b/implement-shell-tools/ls/ls.js @@ -0,0 +1,122 @@ +const fs = require("fs"); +const { allowedNodeEnvironmentFlags } = require("process"); + +const args = process.argv; +const userArgs = args.slice(2); + +const index = args[1].lastIndexOf("/"); +const currentWorkingDirectory = args[1].slice(0, index); + +const flags = userArgs + .filter((arg) => arg.startsWith("-")) + .map((arg) => arg.slice(1)) + .join(""); + +const flagHandlers = { + 1: Flag1, + a: Flaga, +}; + +let printInList = false; +let showAll = false; + +const fsItems = userArgs.filter((arg) => !arg.startsWith("-")); +let dirArgs; +let fileArgs; +let outputString = ""; + +checkArgsLength(); +executeFlags(); +filterFilesAndDirs(); +populateOutput(); +print(); + +function executeFlags() { + for (const flag of flags) { + if (flagHandlers[flag]) { + allFilesContents = flagHandlers[flag](); + } else { + console.error( + `ls: invalid option -- ${flag}\nusage: ls [-@ABCFGHILOPRSTUWXabcdefghiklmnopqrstuvwxy1%,] [--color=when] [-D format] [file ...]`, + ); + process.exit(1); + } + } +} + +function Flag1() { + if (printInList) { + outputString = outputString.replaceAll("\t", "\n"); + outputString = outputString.replaceAll("\n\n", "\n"); + } else { + printInList = true; + } +} + +function Flaga() { + showAll = true; +} + +function checkArgsLength() { + if (fsItems.length == 0) { + fsItems.push("."); + } +} + +function filterFilesAndDirs() { + dirArgs = fsItems.filter((p) => fs.statSync(currentWorkingDirectory + "/" + p).isDirectory()); + fileArgs = fsItems.filter((p) => !fs.statSync(currentWorkingDirectory + "/" + p).isDirectory()); + if (showAll == false) { + removeDotFiles(fileArgs); + } +} + +function populateOutput() { + if (fsItems.length == 1) { + fileArgs.forEach((file) => { + outputString += file + " "; + }); + dirArgs.forEach((dir) => { + outputString += dirOutput(dir); + }); + } else { + fileArgs.forEach((file) => { + outputString += file + "\t"; + }); + dirArgs.forEach((dir) => { + outputString += "\n\n" + dir + ":\n" + dirOutput(dir); + }); + } +} + +function dirOutput(dir) { + let contents = fs.readdirSync(currentWorkingDirectory + "/" + dir); + let outputStr = ""; + if (showAll) { + outputStr += ".\t..\t"; + } else { + for (let i = contents.length - 1; i >= 0; i--) { + contents = removeDotFiles(contents); + } + } + contents.forEach((item) => { + outputStr += item + "\t"; + }); + return outputStr; +} + +function removeDotFiles(fileList) { + for (let i = fileList.length - 1; i >= 0; i--) { + if (fileList[i][0] == ".") { + fileList.splice(i, 1); + } + } + return fileList; +} + +function print() { + if (printInList == true) { + Flag1(); + } + console.log(outputString.trimEnd()); +} diff --git a/implement-shell-tools/wc/wc.js b/implement-shell-tools/wc/wc.js new file mode 100644 index 000000000..9635e8e3f --- /dev/null +++ b/implement-shell-tools/wc/wc.js @@ -0,0 +1,141 @@ +const fs = require("fs"); +const { allowedNodeEnvironmentFlags } = require("process"); + +const arguments = process.argv; +const userArguments = arguments.slice(2); + +const index = arguments[1].lastIndexOf("/"); +const currentWorkingDirectory = arguments[1].slice(0, index + 1); + +const flags = userArguments + .filter((argument) => argument.startsWith("-")) + .map((argument) => argument.slice(1)) + .join(""); + +const flagHandlers = { + l: lFlag, + w: wFlag, + c: cFlag, +}; + +const metrics = ["lineCount", "wordCount", "byteSize"]; +const fileNames = userArguments.filter((argument) => !argument.startsWith("-")); +const allFilesData = []; + +extractFilesData(); +addTotals(); +const outputData = structuredClone(allFilesData); + +let deleted = false; +executeFlags(); +printOutput(); + +function extractFilesData() { + fileNames.forEach((fileName) => { + const fileData = {}; + fileData.name = fileName; + fileData.text = readFile(fileName); + fileData.lineCount = calculateLineCount(fileData.text); + fileData.wordCount = calculateWordCount(fileData.text); + fileData.byteSize = readByteSize(fileName); + + allFilesData.push(fileData); + }); +} + +function addTotals() { + if (allFilesData.length == 1) { + return; + } + const totalsData = { name: "total" }; + metrics.forEach((metric) => { + let sum = 0; + allFilesData.forEach((file) => { + sum += file[metric]; + }); + totalsData[metric] = sum; + }); + allFilesData.push(totalsData); +} + +function readFile(fileName) { + const filePath = currentWorkingDirectory + fileName; + return fs.readFileSync(filePath, "utf8").trimEnd(); +} + +function calculateLineCount(text) { + return text.split("\n").length; +} + +function calculateWordCount(text) { + return text.split(/\s+/).length; +} + +function readByteSize(fileName) { + return fs.statSync(currentWorkingDirectory + fileName).size; +} + +function executeFlags() { + for (const flag of flags) { + if (flagHandlers[flag]) { + allFilesContents = flagHandlers[flag](); + } else { + console.error(`wc: illegal option -- ${flag}\nusage: wc [-Lclmw] [file ...]`); + process.exit(1); + } + } +} + +function lFlag() { + deleteOutputs(); + allFilesData.forEach((sourceFile) => { + const targetFile = outputData.find((file) => file.name === sourceFile.name); + targetFile.lineCount = getIfTrue(sourceFile, "lineCount"); + }); +} + +function wFlag() { + deleteOutputs(); + allFilesData.forEach((sourceFile) => { + const targetFile = outputData.find((file) => file.name === sourceFile.name); + targetFile.wordCount = getIfTrue(sourceFile, "wordCount"); + }); +} + +function cFlag() { + deleteOutputs(); + allFilesData.forEach((sourceFile) => { + const targetFile = outputData.find((file) => file.name === sourceFile.name); + targetFile.byteSize = getIfTrue(sourceFile, "byteSize"); + }); +} + +function deleteOutputs() { + if (!deleted) { + outputData.forEach((file) => { + metrics.forEach((metric) => { + delete file[metric]; + }); + }); + } + deleted = true; +} + +function printOutput() { + outputs = []; + outputData.forEach((file) => { + let outputString = ""; + metrics.forEach((metric) => { + outputString += getIfTrue(file, metric); + }); + outputString += ` ${file.name}`; + console.log(outputString); + }); +} + +function getIfTrue(file, metric) { + if (file[metric]) { + return String(file[metric]).padStart(8, " "); + } + return ""; +}