mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-18 13:43:49 +01:00
BUGFIX: Fix issues and edge-cases with rm (#1404)
This commit is contained in:
parent
4382f860db
commit
99b22a221c
@ -1,18 +1,19 @@
|
|||||||
import { Terminal } from "../../Terminal";
|
import { Terminal } from "../../Terminal";
|
||||||
import { BaseServer } from "../../Server/BaseServer";
|
import { BaseServer } from "../../Server/BaseServer";
|
||||||
import { PromptEvent } from "../../ui/React/PromptManager";
|
import { PromptEvent } from "../../ui/React/PromptManager";
|
||||||
import { hasScriptExtension } from "../../Paths/ScriptFilePath";
|
import { getAllDirectories, type Directory } from "../../Paths/Directory";
|
||||||
import { hasTextExtension } from "../../Paths/TextFilePath";
|
import type { ProgramFilePath } from "../../Paths/ProgramFilePath";
|
||||||
import type { Directory } from "../../Paths/Directory";
|
|
||||||
import type { IReturnStatus } from "../../types";
|
import type { IReturnStatus } from "../../types";
|
||||||
import type { FilePath } from "../../Paths/FilePath";
|
import type { FilePath } from "../../Paths/FilePath";
|
||||||
|
|
||||||
export function rm(args: (string | number | boolean)[], server: BaseServer): void {
|
export function rm(args: (string | number | boolean)[], server: BaseServer): void {
|
||||||
const errors = {
|
const errors = {
|
||||||
arg: (reason: string) => `Incorrect usage of rm command. ${reason}. Usage: rm [OPTION]... [FILE]...`,
|
arg: (reason: string) => `Incorrect usage of rm command. ${reason}. Usage: rm [OPTION]... [FILE]...`,
|
||||||
dirsProvided: () => "Incorrect usage of rm command. To delete directories, use the -r flag",
|
dirsProvided: (name: string) =>
|
||||||
invalidDir: (name: string) => `Invalid directory: ${name}`,
|
`Incorrect usage of rm command. To delete directories, use the -r flag. Failing directory: ${name}`,
|
||||||
invalidFile: (name: string) => `Invalid file: ${name}`,
|
invalidFile: (name: string) => `Invalid filename: ${name}`,
|
||||||
|
noSuchFile: (name: string) => `File does not exist: ${name}`,
|
||||||
|
noSuchDir: (name: string) => `Directory does not exist: ${name}`,
|
||||||
deleteFailed: (name: string, reason?: string) => `Failed to delete "${name}". ${reason ?? "Uncaught error"}`,
|
deleteFailed: (name: string, reason?: string) => `Failed to delete "${name}". ${reason ?? "Uncaught error"}`,
|
||||||
rootDeletion: () =>
|
rootDeletion: () =>
|
||||||
"You are trying to delete all files within the root directory. If this is intentional, use the --no-preserve-root flag",
|
"You are trying to delete all files within the root directory. If this is intentional, use the --no-preserve-root flag",
|
||||||
@ -37,25 +38,74 @@ export function rm(args: (string | number | boolean)[], server: BaseServer): voi
|
|||||||
|
|
||||||
const directories: Directory[] = [];
|
const directories: Directory[] = [];
|
||||||
const files: FilePath[] = [];
|
const files: FilePath[] = [];
|
||||||
|
const allDirs: Set<Directory> = getAllDirectories(server);
|
||||||
|
const allFiles: Set<FilePath> = new Set([
|
||||||
|
...server.scripts.keys(),
|
||||||
|
...server.textFiles.keys(),
|
||||||
|
...(server.programs as ProgramFilePath[]),
|
||||||
|
]);
|
||||||
|
|
||||||
for (const target of targets) {
|
for (const file of server.contracts) {
|
||||||
if (!hasTextExtension(target) && !hasScriptExtension(target)) {
|
allFiles.add(file.fn);
|
||||||
const dirPath = Terminal.getDirectory(target);
|
}
|
||||||
if (dirPath === null) return Terminal.error(errors["invalidDir"](target));
|
for (const file of server.messages) {
|
||||||
if (!recursive) return Terminal.error(errors["dirsProvided"]());
|
if (file.endsWith(".lit")) {
|
||||||
directories.push(dirPath);
|
allFiles.add(file as FilePath);
|
||||||
continue;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const target of targets) {
|
||||||
|
// Directories can be specified with or without a trailing slash. However,
|
||||||
|
// trying to remove a file with a trailing slash is an error.
|
||||||
|
const fileDir = Terminal.getDirectory(target + (target[target.length - 1] === "/" ? "" : "/"));
|
||||||
const file = Terminal.getFilepath(target);
|
const file = Terminal.getFilepath(target);
|
||||||
if (file === null) return Terminal.error(errors["invalidFile"](target));
|
|
||||||
|
|
||||||
|
const fileExists = file !== null && allFiles.has(file);
|
||||||
|
|
||||||
|
if (fileDir === null) return Terminal.error(errors.invalidFile(target));
|
||||||
|
const dirExists = allDirs.has(fileDir);
|
||||||
|
if (file === null || dirExists) {
|
||||||
|
// If file === null, it means we specified a trailing-slash directory/,
|
||||||
|
// or something that does not have an extension or otherwise isn't file-like.
|
||||||
|
if (fileExists) {
|
||||||
|
// We have this early case here specifically to handle situations where
|
||||||
|
// a file and a directory with the same name exist. That's right, you
|
||||||
|
// can have *both* /foo.txt *and* /foo.txt/bar.txt.
|
||||||
|
//
|
||||||
|
// In this case, we need to treat filenames preferrentially as files first.
|
||||||
|
// If we have -r, we will *also* delete the directory.
|
||||||
|
files.push(file);
|
||||||
|
}
|
||||||
|
if (!recursive) {
|
||||||
|
if (fileExists) {
|
||||||
|
// This is valid, but we shouldn't touch the directory.
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// Only exists as a directory (maybe).
|
||||||
|
return Terminal.error(errors.dirsProvided(target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!dirExists && !force) {
|
||||||
|
return Terminal.error(errors.noSuchDir(target));
|
||||||
|
}
|
||||||
|
// If we pass -f and pass a non-existing directory, we will add it
|
||||||
|
// here and then it will match no files, producing no errors. This
|
||||||
|
// aligns with Unix rm.
|
||||||
|
directories.push(fileDir);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!force && !allFiles.has(file)) {
|
||||||
|
// With -f, we ignore file-not-found and try to delete everything at the end.
|
||||||
|
return Terminal.error(errors.noSuchFile(target));
|
||||||
|
}
|
||||||
files.push(file);
|
files.push(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const file of allFiles) {
|
||||||
for (const dir of directories) {
|
for (const dir of directories) {
|
||||||
for (const file of server.scripts.keys()) {
|
if (file.startsWith(dir)) {
|
||||||
if (file.startsWith(dir)) files.push(file);
|
files.push(file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,16 +122,23 @@ export function rm(args: (string | number | boolean)[], server: BaseServer): voi
|
|||||||
if (report.result.res) {
|
if (report.result.res) {
|
||||||
Terminal.success(`Deleted: ${report.target}`);
|
Terminal.success(`Deleted: ${report.target}`);
|
||||||
} else {
|
} else {
|
||||||
Terminal.error(errors["deleteFailed"](report.target, report.result.msg));
|
Terminal.error(errors.deleteFailed(report.target, report.result.msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (force || files.length === 1) {
|
if (
|
||||||
|
force ||
|
||||||
|
(files.length === 1 && !files[0].endsWith(".exe") && !files[0].endsWith(".lit") && !files[0].endsWith(".cct"))
|
||||||
|
) {
|
||||||
deleteSelectedTargets();
|
deleteSelectedTargets();
|
||||||
} else {
|
} else {
|
||||||
|
const promptText = `Are you sure you want to delete ${
|
||||||
|
files.length === 1 ? files[0] : "these files"
|
||||||
|
}? This is irreversible.${files.length > 1 ? "\n\nDeleting:\n" + targetList : ""}`;
|
||||||
|
|
||||||
PromptEvent.emit({
|
PromptEvent.emit({
|
||||||
txt: "Are you sure you want to delete these files? This is irreversible.\n\nDeleting:\n" + targetList,
|
txt: promptText,
|
||||||
resolve: (value: string | boolean) => {
|
resolve: (value: string | boolean) => {
|
||||||
if (typeof value === "string") throw new Error("PromptEvent got a string, expected boolean");
|
if (typeof value === "string") throw new Error("PromptEvent got a string, expected boolean");
|
||||||
if (value) deleteSelectedTargets();
|
if (value) deleteSelectedTargets();
|
||||||
|
Loading…
Reference in New Issue
Block a user