Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions implement-shell-tools/cat/cat.js
Original file line number Diff line number Diff line change
@@ -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) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A map is typically used to change one array into another using a function that doesn't have side effects. Here, you are changing a lineNumber variable each time the map runs. This isn't wrong, but just be careful of side effects, especially if you ever want to run things asynchronously.

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);
});
});
}
122 changes: 122 additions & 0 deletions implement-shell-tools/ls/ls.js
Original file line number Diff line number Diff line change
@@ -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");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that you are using lots of task specific functions.

But, is using global variables in this way a safe way of programming this? Is there a way that would have the data flow through your app in a more semantic way?

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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think about how if conditions work, and check this line again. Are you writing code in the most optimal way?

Flag1();
}
console.log(outputString.trimEnd());
}
141 changes: 141 additions & 0 deletions implement-shell-tools/wc/wc.js
Original file line number Diff line number Diff line change
@@ -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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain your thinking that led to a design where you need to manually handle your resources like this?

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 "";
}
Loading