mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-27 10:03:48 +01:00
321 lines
8.8 KiB
TypeScript
321 lines
8.8 KiB
TypeScript
/**
|
|
* Helper functions that implement "directory" functionality in the Terminal.
|
|
* These aren't "real" directories, it's more of a pseudo-directory implementation
|
|
* that uses mainly string manipulation.
|
|
*
|
|
* This file contains functions that deal only with that string manipulation.
|
|
* Functions that need to access/process Server-related things can be
|
|
* found in ./DirectoryServerHelpers.ts
|
|
*/
|
|
|
|
/**
|
|
* Removes leading forward slash ("/") from a string.
|
|
*/
|
|
export function removeLeadingSlash(s: string): string {
|
|
if (s.startsWith("/")) {
|
|
return s.slice(1);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Removes trailing forward slash ("/") from a string.
|
|
* Note that this will also remove the slash if it is the leading slash (i.e. if s = "/")
|
|
*/
|
|
export function removeTrailingSlash(s: string): string {
|
|
if (s.endsWith("/")) {
|
|
return s.slice(0, -1);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Checks whether a string is a valid filename. Only used for the filename itself,
|
|
* not the entire filepath
|
|
*/
|
|
export function isValidFilename(filename: string): boolean {
|
|
// Allows alphanumerics, hyphens, underscores, and percentage signs
|
|
// Must have a file extension
|
|
const regex = /^[.a-zA-Z0-9_-]+[.][a-zA-Z0-9]+(?:-\d+(?:\.\d*)?%-INC)?$/;
|
|
|
|
// match() returns null if no match is found
|
|
return filename.match(regex) != null;
|
|
}
|
|
|
|
/**
|
|
* Checks whether a string is a valid directory name. Only used for the directory itself,
|
|
* not an entire path
|
|
*/
|
|
export function isValidDirectoryName(name: string): boolean {
|
|
// A valid directory name:
|
|
// Must be at least 1 character long
|
|
// Can only include characters in the character set [-.%a-zA-Z0-9_]
|
|
// Cannot end with a '.'
|
|
const regex = /^.?[a-zA-Z0-9_-]+$/;
|
|
|
|
// match() returns null if no match is found
|
|
return name.match(regex) != null;
|
|
}
|
|
|
|
/**
|
|
* Checks whether a string is a valid directory path.
|
|
* This only checks if it has the proper formatting. It does NOT check
|
|
* if the directories actually exist on Terminal
|
|
*/
|
|
export function isValidDirectoryPath(path: string): boolean {
|
|
let t_path: string = path;
|
|
|
|
if (t_path.length === 0) {
|
|
return false;
|
|
}
|
|
if (t_path.length === 1) {
|
|
return t_path === "/";
|
|
}
|
|
|
|
// A full path must have a leading slash, but we'll ignore it for the checks
|
|
if (t_path.startsWith("/")) {
|
|
t_path = t_path.slice(1);
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
// Trailing slash does not matter
|
|
t_path = removeTrailingSlash(t_path);
|
|
|
|
// Check that every section of the path is a valid directory name
|
|
const dirs = t_path.split("/");
|
|
for (const dir of dirs) {
|
|
// Special case, "." and ".." are valid for path
|
|
if (dir === "." || dir === "..") {
|
|
continue;
|
|
}
|
|
if (!isValidDirectoryName(dir)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Checks whether a string is a valid file path. This only checks if it has the
|
|
* proper formatting. It dose NOT check if the file actually exists on Terminal
|
|
*/
|
|
export function isValidFilePath(path: string): boolean {
|
|
if (path == null || typeof path !== "string") {
|
|
return false;
|
|
}
|
|
const t_path = path;
|
|
|
|
// Impossible for filename to have less than length of 3
|
|
if (t_path.length < 3) {
|
|
return false;
|
|
}
|
|
|
|
// Full filepath can't end with trailing slash because it must be a file
|
|
if (t_path.endsWith("/")) {
|
|
return false;
|
|
}
|
|
|
|
// Everything after the last forward slash is the filename. Everything before
|
|
// it is the file path
|
|
const fnSeparator = t_path.lastIndexOf("/");
|
|
if (fnSeparator === -1) {
|
|
return isValidFilename(t_path);
|
|
}
|
|
|
|
const fn = t_path.slice(fnSeparator + 1);
|
|
const dirPath = t_path.slice(0, fnSeparator + 1);
|
|
|
|
return isValidDirectoryPath(dirPath) && isValidFilename(fn);
|
|
}
|
|
|
|
/**
|
|
* Returns a formatted string for the first parent directory in a filepath. For example:
|
|
* /home/var/test/ -> home/
|
|
* If there is no first parent directory, then it returns "/" for root
|
|
*/
|
|
export function getFirstParentDirectory(path: string): string {
|
|
let t_path = path;
|
|
t_path = removeLeadingSlash(t_path);
|
|
t_path = removeTrailingSlash(t_path);
|
|
|
|
if (t_path.lastIndexOf("/") === -1) {
|
|
return "/";
|
|
}
|
|
|
|
const dirs = t_path.split("/");
|
|
if (dirs.length === 0) {
|
|
return "/";
|
|
}
|
|
|
|
return dirs[0] + "/";
|
|
}
|
|
|
|
/**
|
|
* Given a filepath, returns a formatted string for all of the parent directories
|
|
* in that filepath. For example:
|
|
* /home/var/tes -> home/var/
|
|
* /home/var/test/ -> home/var/test/
|
|
* If there are no parent directories, it returns the empty string
|
|
*/
|
|
export function getAllParentDirectories(path: string): string {
|
|
const t_path = path;
|
|
const lastSlash = t_path.lastIndexOf("/");
|
|
if (lastSlash === -1) {
|
|
return "";
|
|
}
|
|
|
|
return t_path.slice(0, lastSlash + 1);
|
|
}
|
|
|
|
/**
|
|
* Given a destination that only contains a directory part, returns the
|
|
* path to the source filename inside the new destination directory.
|
|
* Otherwise, returns the path to the destination file.
|
|
* @param destination The destination path or file name
|
|
* @param source The source path
|
|
* @param cwd The current working directory
|
|
* @returns A file path which may be absolute or relative
|
|
*/
|
|
export function getDestinationFilepath(destination: string, source: string, cwd: string): string {
|
|
const dstDir = evaluateDirectoryPath(destination, cwd);
|
|
// If evaluating the directory for this destination fails, we have a filename or full path.
|
|
if (dstDir === null) {
|
|
return destination;
|
|
} else {
|
|
// Append the filename to the directory provided.
|
|
const t_path = removeTrailingSlash(dstDir);
|
|
const fileName = getFileName(source);
|
|
return t_path + "/" + fileName;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Given a filepath, returns the file name (e.g. without directory parts)
|
|
* For example:
|
|
* /home/var/test.js -> test.js
|
|
* ./var/test.js -> test.js
|
|
* test.js -> test.js
|
|
*/
|
|
export function getFileName(path: string): string {
|
|
const t_path = path;
|
|
const lastSlash = t_path.lastIndexOf("/");
|
|
if (lastSlash === -1) {
|
|
return t_path;
|
|
}
|
|
|
|
return t_path.slice(lastSlash + 1);
|
|
}
|
|
|
|
/**
|
|
* Checks if a file path refers to a file in the root directory.
|
|
*/
|
|
export function isInRootDirectory(path: string): boolean {
|
|
if (!isValidFilePath(path)) {
|
|
return false;
|
|
}
|
|
if (path == null || path.length === 0) {
|
|
return false;
|
|
}
|
|
|
|
return path.lastIndexOf("/") <= 0;
|
|
}
|
|
|
|
/**
|
|
* Evaluates a directory path, including the processing of linux dots.
|
|
* Returns the full, proper path, or null if an invalid path is passed in
|
|
*/
|
|
export function evaluateDirectoryPath(path: string, currPath?: string): string | null {
|
|
let t_path = path;
|
|
|
|
// If the path begins with a slash, then its an absolute path. Otherwise its relative
|
|
// For relative paths, we need to prepend the current directory
|
|
if (!t_path.startsWith("/") && currPath) {
|
|
t_path = currPath + (currPath.endsWith("/") ? "" : "/") + t_path;
|
|
}
|
|
|
|
if (!isValidDirectoryPath(t_path)) {
|
|
return null;
|
|
}
|
|
|
|
// Trim leading/trailing slashes
|
|
t_path = removeLeadingSlash(t_path);
|
|
t_path = removeTrailingSlash(t_path);
|
|
|
|
const dirs = t_path.split("/");
|
|
const reconstructedPath: string[] = [];
|
|
|
|
for (const dir of dirs) {
|
|
if (dir === ".") {
|
|
// Current directory, do nothing
|
|
continue;
|
|
} else if (dir === "..") {
|
|
// Parent directory
|
|
const res = reconstructedPath.pop();
|
|
if (res == null) {
|
|
return null; // Array was empty, invalid path
|
|
}
|
|
} else {
|
|
reconstructedPath.push(dir);
|
|
}
|
|
}
|
|
|
|
return "/" + reconstructedPath.join("/");
|
|
}
|
|
|
|
/**
|
|
* Evaluates a file path, including the processing of linux dots.
|
|
* Returns the full, proper path, or null if an invalid path is passed in
|
|
*/
|
|
export function evaluateFilePath(path: string, currPath?: string): string | null {
|
|
let t_path = path;
|
|
|
|
// If the path begins with a slash, then its an absolute path. Otherwise its relative
|
|
// For relative paths, we need to prepend the current directory
|
|
if (!t_path.startsWith("/") && currPath != null) {
|
|
t_path = currPath + (currPath.endsWith("/") ? "" : "/") + t_path;
|
|
}
|
|
|
|
if (!isValidFilePath(t_path)) {
|
|
return null;
|
|
}
|
|
|
|
// Trim leading/trailing slashes
|
|
t_path = removeLeadingSlash(t_path);
|
|
|
|
const dirs = t_path.split("/");
|
|
const reconstructedPath: string[] = [];
|
|
|
|
for (const dir of dirs) {
|
|
if (dir === ".") {
|
|
// Current directory, do nothing
|
|
continue;
|
|
} else if (dir === "..") {
|
|
// Parent directory
|
|
const res = reconstructedPath.pop();
|
|
if (res == null) {
|
|
return null; // Array was empty, invalid path
|
|
}
|
|
} else {
|
|
reconstructedPath.push(dir);
|
|
}
|
|
}
|
|
|
|
return "/" + reconstructedPath.join("/");
|
|
}
|
|
|
|
export function areFilesEqual(f0: string, f1: string): boolean {
|
|
if (!f0.startsWith("/")) f0 = "/" + f0;
|
|
if (!f1.startsWith("/")) f1 = "/" + f1;
|
|
return f0 === f1;
|
|
}
|
|
|
|
export function areImportsEquals(f0: string, f1: string): boolean {
|
|
if (!f0.endsWith(".js")) f0 = f0 + ".js";
|
|
if (!f1.endsWith(".js")) f1 = f1 + ".js";
|
|
return areFilesEqual(f0, f1);
|
|
}
|