forked from tools/josh
1
0
Fork 0
This commit is contained in:
Florine W. Dekker 2019-11-10 18:20:46 +01:00
parent cd0d2cb16d
commit d158b4f970
Signed by: FWDekker
GPG Key ID: B1B567AF58D6EE0F
6 changed files with 44 additions and 32 deletions

View File

@ -1,6 +1,6 @@
{
"name": "fwdekker.com",
"version": "1.8.9",
"version": "1.9.0",
"description": "The source code of [my personal website](https://fwdekker.com/).",
"author": "Felix W. Dekker",
"repository": {

View File

@ -2,7 +2,7 @@ import "./Extensions"
import {Environment} from "./Environment";
import {Directory, File, FileSystem, Path} from "./FileSystem"
import {Persistence} from "./Persistence";
import {IllegalArgumentError, IllegalStateError} from "./Shared";
import {escapeHtml, IllegalArgumentError, IllegalStateError} from "./Shared";
import {InputArgs} from "./Shell";
import {EscapeCharacters} from "./Terminal";
import {UserList} from "./UserList";
@ -46,8 +46,12 @@ export class Commands {
"cat": new Command(
this.cat,
`concatenate and print files`,
`cat <u>file</u> <u>...</u>`,
`Reads files sequentially, writing them to the standard output.`,
`cat [<b>-e</b> | <b>--escape-html</b>] <u>file</u> <u>...</u>`,
`Reads files sequentially, writing them to the standard output.
If the file contains valid HTML, it will be displayed as such by default. If the <b>--html</b> \\\
option is given, special HTML characters are escaped and the raw text contents can be inspected.\\\
`.trimLines(),
new InputValidator({minArgs: 1})
),
"clear": new Command(
@ -122,7 +126,7 @@ export class Commands {
`display manual documentation pages`,
`man <u>page</u> <u>...</u>`,
`Displays the manual pages with names <u>page</u>. Equivalent to using <b>help</b> if at least one \\\
<u>page</u> is given.`.trimLines(),
<u>page</u> is given.`.trimMultiLines(),
new InputValidator()
),
"mkdir": new Command(
@ -175,7 +179,7 @@ export class Commands {
this.rm,
`remove file`,
`rm [<b>-f</b> | <b>--force</b>] [<b>-r</b> | <b>-R</b> | <b>--recursive</b>] \\\
[<b>--no-preserve-root</b>] <u>file</u> <u>...</u>`.trimLines(),
[<b>--no-preserve-root</b>] <u>file</u> <u>...</u>`.trimMultiLines(),
`Removes each given <u>file</u>. If more than one <u>file</u> is given, they are removed in the \\\
order they are given in.
@ -285,10 +289,11 @@ export class Commands {
return -1;
}
if (node.contents.endsWith("\n"))
streams.out.write(node.contents);
const contents = input.hasAnyOption("e", "--escape-html") ? escapeHtml(node.contents) : node.contents;
if (contents.endsWith("\n"))
streams.out.write(contents);
else
streams.out.writeLine(node.contents);
streams.out.writeLine(contents);
return 0;
})
.reduce((acc, exitCode) => exitCode === 0 ? acc : exitCode);
@ -637,7 +642,7 @@ export class Commands {
this.fileSystem.add(path, new File(), false);
return 0;
} catch (error) {
streams.err.writeLine(`touche: ${error.message}`);
streams.err.writeLine(`touch: ${error.message}`);
return -1;
}
})

View File

@ -162,7 +162,7 @@ export class FileSystem {
const targetNode = this.get(target);
if (!(targetNode instanceof File))
throw new IllegalArgumentError("Cannot open stream to directory.");
throw new IllegalArgumentError(`Cannot open stream to directory '${target}'.`);
if (options === "write")
targetNode.contents = "";

View File

@ -18,8 +18,7 @@ export const asciiHeaderHtml =
/**
* A function that does nothing.
*/
export const emptyFunction = () => {
};
export const emptyFunction = () => {};
/**
@ -38,6 +37,19 @@ export function addOnLoad(fun: () => void): void {
};
}
/**
* Replaces all special HTML characters with escaped variants.
*
* @param string the string to escape special HTML characters in
*/
export function escapeHtml(string: string): string {
return string
.replaceAll(/</, "&lt;")
.replaceAll(/>/, "&gt;")
.replaceAll(/"/, "&quot;")
.replaceAll(/'/, "&#039;");
}
/**
* Returns the extension of the given filename, or `""` if it doesn't have one.
*
@ -98,17 +110,6 @@ export function q(query: string): HTMLElement {
return element;
}
/**
* Returns this string with all HTML tags stripped.
*
* @param string the string to strip HTML tags from
*/
export function stripHtmlTags(string: string): string {
const div = document.createElement("div");
div.innerHTML = string;
return div.textContent || div.innerText || "";
}
/**
* Indicates that an argument is given to a function that should not have been given.

View File

@ -3,7 +3,7 @@ import {Environment} from "./Environment";
import {Directory, FileSystem, Path} from "./FileSystem";
import {InputParser} from "./InputParser";
import {Persistence} from "./Persistence";
import {asciiHeaderHtml, stripHtmlTags} from "./Shared";
import {asciiHeaderHtml} from "./Shared";
import {EscapeCharacters, InputHistory} from "./Terminal";
import {UserList} from "./UserList";
import {StreamSet} from "./Stream";
@ -144,11 +144,11 @@ export class Shell {
return 0;
}
this.inputHistory.addEntry(inputString.trimStart());
this.inputHistory.addEntry(inputString);
let input;
try {
input = InputParser.create(this.environment, this.fileSystem).parse(stripHtmlTags(inputString));
input = InputParser.create(this.environment, this.fileSystem).parse(inputString);
} catch (error) {
streams.err.writeLine(`Could not parse input: ${error.message}`);
this.environment.set("status", "-1");
@ -157,7 +157,13 @@ export class Shell {
if (input.redirectTarget[0] !== "default") {
const target = Path.interpret(this.environment.get("cwd"), input.redirectTarget[1]);
streams.out = this.fileSystem.open(target, input.redirectTarget[0]);
try {
streams.out = this.fileSystem.open(target, input.redirectTarget[0]);
} catch(error) {
streams.err.writeLine(`open: ${error.message}`);
this.environment.set("status", "-1");
return -1;
}
}
const output = this.commands.execute(input, streams);

View File

@ -1,4 +1,4 @@
import {moveCaretToEndOf, parseCssPixels} from "./Shared";
import {escapeHtml, moveCaretToEndOf, parseCssPixels} from "./Shared";
import {Shell} from "./Shell";
import {Buffer, StreamSet} from "./Stream";
@ -96,7 +96,7 @@ export class Terminal {
* Returns the input the user has entered in the HTML element.
*/
private get inputText(): string {
return this.input.innerHTML.replaceAll(/<br>/, "");
return this.input.innerText.replaceAll(/<br>/, "");
}
/**
@ -105,7 +105,7 @@ export class Terminal {
* @param inputText the text to set as the text of the input HTML element
*/
private set inputText(inputText: string) {
this.input.innerHTML = inputText;
this.input.innerText = inputText;
}
/**
@ -207,7 +207,7 @@ export class Terminal {
*/
processInput(input: string): void {
this.inputText = "";
this.outputText += `${this.prefixText}${this.isInputHidden ? "" : input.trim()}\n`;
this.outputText += `${this.prefixText}${this.isInputHidden ? "" : escapeHtml(input)}\n`;
this.standardInput.writeLine(input);
this.shell.execute(new StreamSet(this.standardInput, this.standardOutput, this.standardOutput));