2021-09-09 05:47:34 +02:00
|
|
|
import { evaluateDirectoryPath, getAllParentDirectories } from "./DirectoryHelpers";
|
2019-05-12 04:20:20 +02:00
|
|
|
import { getSubdirectories } from "./DirectoryServerHelpers";
|
2019-04-20 07:27:33 +02:00
|
|
|
|
2021-11-16 05:49:33 +01:00
|
|
|
import { Aliases, GlobalAliases, substituteAliases } from "../Alias";
|
2019-04-10 08:07:12 +02:00
|
|
|
import { DarkWebItems } from "../DarkWeb/DarkWebItems";
|
2021-09-05 01:09:30 +02:00
|
|
|
import { IPlayer } from "../PersonObjects/IPlayer";
|
2022-04-12 15:03:45 +02:00
|
|
|
import { GetAllServers } from "../Server/AllServers";
|
|
|
|
import { Server } from "../Server/Server";
|
2021-10-15 04:36:28 +02:00
|
|
|
import { ParseCommand, ParseCommands } from "./Parser";
|
2022-01-14 22:25:30 +01:00
|
|
|
import { HelpTexts } from "./HelpText";
|
2021-10-15 04:36:28 +02:00
|
|
|
import { isScriptFilename } from "../Script/isScriptFilename";
|
2021-10-15 18:47:43 +02:00
|
|
|
import { compile } from "../NetscriptJSEvaluator";
|
|
|
|
import { Flags } from "../NetscriptFunctions/Flags";
|
2022-02-01 05:43:49 +01:00
|
|
|
import { AutocompleteData } from "../ScriptEditor/NetscriptDefinitions";
|
2021-10-15 18:47:43 +02:00
|
|
|
import * as libarg from "arg";
|
2019-04-10 08:07:12 +02:00
|
|
|
|
|
|
|
// An array of all Terminal commands
|
|
|
|
const commands = [
|
2021-09-05 01:09:30 +02:00
|
|
|
"alias",
|
|
|
|
"analyze",
|
|
|
|
"backdoor",
|
|
|
|
"cat",
|
|
|
|
"cd",
|
|
|
|
"check",
|
|
|
|
"clear",
|
|
|
|
"cls",
|
|
|
|
"connect",
|
2021-10-12 04:35:00 +02:00
|
|
|
"cp",
|
2021-09-05 01:09:30 +02:00
|
|
|
"download",
|
|
|
|
"expr",
|
|
|
|
"free",
|
2021-10-05 01:58:34 +02:00
|
|
|
"grow",
|
2021-09-05 01:09:30 +02:00
|
|
|
"hack",
|
|
|
|
"help",
|
|
|
|
"home",
|
|
|
|
"hostname",
|
|
|
|
"ifconfig",
|
|
|
|
"kill",
|
|
|
|
"killall",
|
|
|
|
"ls",
|
|
|
|
"lscpu",
|
|
|
|
"mem",
|
|
|
|
"mv",
|
|
|
|
"nano",
|
|
|
|
"ps",
|
|
|
|
"rm",
|
|
|
|
"run",
|
|
|
|
"scan-analyze",
|
2021-10-05 01:58:34 +02:00
|
|
|
"scan",
|
2021-09-05 01:09:30 +02:00
|
|
|
"scp",
|
|
|
|
"sudov",
|
|
|
|
"tail",
|
|
|
|
"theme",
|
|
|
|
"top",
|
2021-12-17 18:48:34 +01:00
|
|
|
"vim",
|
2021-10-05 01:58:34 +02:00
|
|
|
"weaken",
|
2019-04-10 08:07:12 +02:00
|
|
|
];
|
2019-04-05 11:08:41 +02:00
|
|
|
|
2021-10-15 04:36:28 +02:00
|
|
|
export async function determineAllPossibilitiesForTabCompletion(
|
2021-09-05 01:09:30 +02:00
|
|
|
p: IPlayer,
|
|
|
|
input: string,
|
|
|
|
index: number,
|
|
|
|
currPath = "",
|
2021-10-15 04:36:28 +02:00
|
|
|
): Promise<string[]> {
|
2021-11-16 05:49:33 +01:00
|
|
|
input = substituteAliases(input);
|
2021-09-05 01:09:30 +02:00
|
|
|
let allPos: string[] = [];
|
|
|
|
allPos = allPos.concat(Object.keys(GlobalAliases));
|
|
|
|
const currServ = p.getCurrentServer();
|
|
|
|
const homeComputer = p.getHomeComputer();
|
|
|
|
|
|
|
|
let parentDirPath = "";
|
|
|
|
let evaledParentDirPath: string | null = null;
|
|
|
|
// Helper functions
|
|
|
|
function addAllCodingContracts(): void {
|
|
|
|
for (const cct of currServ.contracts) {
|
|
|
|
allPos.push(cct.fn);
|
2019-04-10 08:07:12 +02:00
|
|
|
}
|
2021-09-05 01:09:30 +02:00
|
|
|
}
|
2019-04-10 08:07:12 +02:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
function addAllLitFiles(): void {
|
|
|
|
for (const file of currServ.messages) {
|
2021-10-14 08:07:05 +02:00
|
|
|
if (!file.endsWith(".msg")) {
|
2021-09-05 01:09:30 +02:00
|
|
|
allPos.push(file);
|
|
|
|
}
|
2019-04-10 08:07:12 +02:00
|
|
|
}
|
2021-09-05 01:09:30 +02:00
|
|
|
}
|
2019-04-10 08:07:12 +02:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
function addAllMessages(): void {
|
|
|
|
for (const file of currServ.messages) {
|
2021-10-14 08:07:05 +02:00
|
|
|
if (file.endsWith(".msg")) {
|
|
|
|
allPos.push(file);
|
2021-09-05 01:09:30 +02:00
|
|
|
}
|
2019-04-10 08:07:12 +02:00
|
|
|
}
|
2021-09-05 01:09:30 +02:00
|
|
|
}
|
2019-04-10 08:07:12 +02:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
function addAllPrograms(): void {
|
|
|
|
for (const program of homeComputer.programs) {
|
|
|
|
allPos.push(program);
|
2019-04-10 08:07:12 +02:00
|
|
|
}
|
2021-09-05 01:09:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function addAllScripts(): void {
|
|
|
|
for (const script of currServ.scripts) {
|
|
|
|
const res = processFilepath(script.filename);
|
|
|
|
if (res) {
|
|
|
|
allPos.push(res);
|
|
|
|
}
|
2019-04-10 08:07:12 +02:00
|
|
|
}
|
2021-09-05 01:09:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function addAllTextFiles(): void {
|
|
|
|
for (const txt of currServ.textFiles) {
|
|
|
|
const res = processFilepath(txt.fn);
|
|
|
|
if (res) {
|
|
|
|
allPos.push(res);
|
|
|
|
}
|
2019-04-20 07:27:33 +02:00
|
|
|
}
|
2021-09-05 01:09:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function addAllDirectories(): void {
|
|
|
|
// Directories are based on the currently evaluated path
|
2021-09-09 05:47:34 +02:00
|
|
|
const subdirs = getSubdirectories(currServ, evaledParentDirPath == null ? "/" : evaledParentDirPath);
|
2021-09-05 01:09:30 +02:00
|
|
|
|
|
|
|
for (let i = 0; i < subdirs.length; ++i) {
|
2021-09-09 05:47:34 +02:00
|
|
|
const assembledDirPath = evaledParentDirPath == null ? subdirs[i] : evaledParentDirPath + subdirs[i];
|
2021-09-05 01:09:30 +02:00
|
|
|
const res = processFilepath(assembledDirPath);
|
|
|
|
if (res != null) {
|
|
|
|
subdirs[i] = res;
|
|
|
|
}
|
2019-04-20 07:27:33 +02:00
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
allPos = allPos.concat(subdirs);
|
|
|
|
}
|
2019-04-20 07:27:33 +02:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
// Convert from the real absolute path back to the original path used in the input
|
|
|
|
function convertParentPath(filepath: string): string {
|
|
|
|
if (parentDirPath == null || evaledParentDirPath == null) {
|
|
|
|
console.warn(`convertParentPath() called when paths are null`);
|
|
|
|
return filepath;
|
2019-04-20 07:27:33 +02:00
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
if (!filepath.startsWith(evaledParentDirPath)) {
|
|
|
|
console.warn(
|
|
|
|
`convertParentPath() called for invalid path. (filepath=${filepath}) (evaledParentDirPath=${evaledParentDirPath})`,
|
|
|
|
);
|
|
|
|
return filepath;
|
2019-04-10 08:07:12 +02:00
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
return parentDirPath + filepath.slice(evaledParentDirPath.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Given an a full, absolute filepath, converts it to the proper value
|
|
|
|
// for autocompletion purposes
|
|
|
|
function processFilepath(filepath: string): string | null {
|
|
|
|
if (evaledParentDirPath) {
|
|
|
|
if (filepath.startsWith(evaledParentDirPath)) {
|
|
|
|
return convertParentPath(filepath);
|
|
|
|
}
|
|
|
|
} else if (parentDirPath !== "") {
|
|
|
|
// If the parent directory is the root directory, but we're not searching
|
|
|
|
// it from the root directory, we have to add the original path
|
|
|
|
let t_parentDirPath = parentDirPath;
|
|
|
|
if (!t_parentDirPath.endsWith("/")) {
|
|
|
|
t_parentDirPath += "/";
|
|
|
|
}
|
|
|
|
return parentDirPath + filepath;
|
|
|
|
} else {
|
|
|
|
return filepath;
|
2019-04-10 08:07:12 +02:00
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isCommand(cmd: string): boolean {
|
|
|
|
let t_cmd = cmd;
|
|
|
|
if (!t_cmd.endsWith(" ")) {
|
|
|
|
t_cmd += " ";
|
2019-04-05 11:08:41 +02:00
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
return input.startsWith(t_cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Autocomplete the command
|
2022-04-07 01:30:08 +02:00
|
|
|
if (index === -1 && !input.startsWith("./")) {
|
2021-09-09 05:47:34 +02:00
|
|
|
return commands.concat(Object.keys(Aliases)).concat(Object.keys(GlobalAliases));
|
2021-09-05 01:09:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Since we're autocompleting an argument and not a command, the argument might
|
|
|
|
// be a file/directory path. We have to account for that when autocompleting
|
|
|
|
const commandArray = input.split(" ");
|
|
|
|
if (commandArray.length === 0) {
|
|
|
|
console.warn(`Tab autocompletion logic reached invalid branch`);
|
|
|
|
return allPos;
|
|
|
|
}
|
|
|
|
const arg = commandArray[commandArray.length - 1];
|
|
|
|
parentDirPath = getAllParentDirectories(arg);
|
|
|
|
evaledParentDirPath = evaluateDirectoryPath(parentDirPath, currPath);
|
|
|
|
if (evaledParentDirPath === "/") {
|
|
|
|
evaledParentDirPath = null;
|
|
|
|
} else if (evaledParentDirPath == null) {
|
2022-05-25 13:03:58 +02:00
|
|
|
// do nothing for some reason tests dont like this?
|
|
|
|
// return allPos; // Invalid path
|
2021-09-05 01:09:30 +02:00
|
|
|
} else {
|
|
|
|
evaledParentDirPath += "/";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isCommand("buy")) {
|
|
|
|
const options = [];
|
2022-01-16 01:45:03 +01:00
|
|
|
for (const i of Object.keys(DarkWebItems)) {
|
2021-09-05 01:09:30 +02:00
|
|
|
const item = DarkWebItems[i];
|
|
|
|
options.push(item.program);
|
2019-04-20 07:27:33 +02:00
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
return options.concat(Object.keys(GlobalAliases));
|
|
|
|
}
|
2019-04-10 08:07:12 +02:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
if (isCommand("scp") && index === 1) {
|
2021-10-07 22:04:04 +02:00
|
|
|
for (const server of GetAllServers()) {
|
|
|
|
allPos.push(server.hostname);
|
2019-04-05 11:08:41 +02:00
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
return allPos;
|
|
|
|
}
|
2019-04-05 11:08:41 +02:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
if (isCommand("scp") && index === 0) {
|
|
|
|
addAllScripts();
|
|
|
|
addAllLitFiles();
|
|
|
|
addAllTextFiles();
|
|
|
|
addAllDirectories();
|
2019-04-10 08:07:12 +02:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
return allPos;
|
|
|
|
}
|
|
|
|
|
2021-10-12 04:35:00 +02:00
|
|
|
if (isCommand("cp") && index === 0) {
|
|
|
|
addAllScripts();
|
|
|
|
addAllTextFiles();
|
|
|
|
addAllDirectories();
|
|
|
|
return allPos;
|
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
if (isCommand("connect")) {
|
2022-04-12 15:03:45 +02:00
|
|
|
// All directly connected and backdoored servers are reachable
|
|
|
|
return GetAllServers()
|
2022-04-12 15:11:38 +02:00
|
|
|
.filter(
|
|
|
|
(server) =>
|
|
|
|
currServ.serversOnNetwork.includes(server.hostname) || (server instanceof Server && server.backdoorInstalled),
|
|
|
|
)
|
|
|
|
.map((server) => server.hostname);
|
2021-09-05 01:09:30 +02:00
|
|
|
}
|
2019-04-05 11:08:41 +02:00
|
|
|
|
2021-12-17 18:48:34 +01:00
|
|
|
if (isCommand("nano") || isCommand("vim")) {
|
2021-09-05 01:09:30 +02:00
|
|
|
addAllScripts();
|
|
|
|
addAllTextFiles();
|
|
|
|
addAllDirectories();
|
2019-04-10 08:07:12 +02:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
return allPos;
|
|
|
|
}
|
2019-04-05 11:08:41 +02:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
if (isCommand("rm")) {
|
|
|
|
addAllScripts();
|
|
|
|
addAllPrograms();
|
|
|
|
addAllLitFiles();
|
|
|
|
addAllTextFiles();
|
|
|
|
addAllCodingContracts();
|
|
|
|
addAllDirectories();
|
2019-04-10 08:07:12 +02:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
return allPos;
|
|
|
|
}
|
2019-04-05 11:08:41 +02:00
|
|
|
|
2021-10-15 04:36:28 +02:00
|
|
|
async function scriptAutocomplete(): Promise<string[] | undefined> {
|
2022-01-10 23:32:50 +01:00
|
|
|
if (!isCommand("run") && !isCommand("tail") && !isCommand("kill") && !input.startsWith("./")) return;
|
2022-01-12 00:04:14 +01:00
|
|
|
let copy = input;
|
|
|
|
if (input.startsWith("./")) copy = "run " + input.slice(2);
|
|
|
|
const commands = ParseCommands(copy);
|
2021-10-15 04:36:28 +02:00
|
|
|
if (commands.length === 0) return;
|
|
|
|
const command = ParseCommand(commands[commands.length - 1]);
|
|
|
|
const filename = command[1] + "";
|
|
|
|
if (!isScriptFilename(filename)) return; // Not a script.
|
|
|
|
if (filename.endsWith(".script")) return; // Doesn't work with ns1.
|
2022-01-12 00:04:14 +01:00
|
|
|
// Use regex to remove any leading './', and then check if it matches against
|
|
|
|
// the output of processFilepath or if it matches with a '/' prepended,
|
|
|
|
// this way autocomplete works inside of directories
|
|
|
|
const script = currServ.scripts.find((script) => {
|
2022-04-07 01:30:08 +02:00
|
|
|
const fn = filename.replace(/^\.\//g, "");
|
|
|
|
return processFilepath(script.filename) === fn || script.filename === "/" + fn;
|
|
|
|
});
|
2021-10-15 04:36:28 +02:00
|
|
|
if (!script) return; // Doesn't exist.
|
2021-10-15 18:47:43 +02:00
|
|
|
if (!script.module) {
|
2022-01-05 01:09:34 +01:00
|
|
|
await compile(p, script, currServ.scripts);
|
2021-10-15 18:47:43 +02:00
|
|
|
}
|
2021-10-15 04:36:28 +02:00
|
|
|
const loadedModule = await script.module;
|
2022-07-20 04:44:45 +02:00
|
|
|
if (!loadedModule || !loadedModule.autocomplete) return; // Doesn't have an autocomplete function.
|
2021-10-15 18:47:43 +02:00
|
|
|
|
|
|
|
const runArgs = { "--tail": Boolean, "-t": Number };
|
|
|
|
const flags = libarg(runArgs, {
|
|
|
|
permissive: true,
|
|
|
|
argv: command.slice(2),
|
|
|
|
});
|
2021-10-15 19:12:18 +02:00
|
|
|
const flagFunc = Flags(flags._);
|
2022-02-01 05:43:49 +01:00
|
|
|
const autocompleteData: AutocompleteData = {
|
|
|
|
servers: GetAllServers().map((server) => server.hostname),
|
|
|
|
scripts: currServ.scripts.map((script) => script.filename),
|
|
|
|
txts: currServ.textFiles.map((txt) => txt.fn),
|
2022-07-18 09:19:10 +02:00
|
|
|
flags: (schema: unknown) => {
|
|
|
|
if (!Array.isArray(schema)) throw new Error("flags require an array of array");
|
|
|
|
pos2 = schema.map((f: unknown) => {
|
|
|
|
if (!Array.isArray(f)) throw new Error("flags require an array of array");
|
2022-02-01 05:43:49 +01:00
|
|
|
if (f[0].length === 1) return "-" + f[0];
|
|
|
|
return "--" + f[0];
|
|
|
|
});
|
|
|
|
try {
|
2022-08-09 21:41:47 +02:00
|
|
|
return flagFunc(schema);
|
2022-02-01 05:43:49 +01:00
|
|
|
} catch (err) {
|
2022-07-20 05:26:21 +02:00
|
|
|
return {};
|
2022-02-01 05:43:49 +01:00
|
|
|
}
|
|
|
|
},
|
2022-04-07 01:30:08 +02:00
|
|
|
};
|
2021-10-15 19:12:18 +02:00
|
|
|
let pos: string[] = [];
|
|
|
|
let pos2: string[] = [];
|
2022-07-20 04:44:45 +02:00
|
|
|
const options = loadedModule.autocomplete(autocompleteData, flags._);
|
|
|
|
if (!Array.isArray(options)) throw new Error("autocomplete did not return list of strings");
|
|
|
|
pos = pos.concat(options.map((x) => String(x)));
|
2021-10-15 19:12:18 +02:00
|
|
|
return pos.concat(pos2);
|
2021-10-15 04:36:28 +02:00
|
|
|
}
|
|
|
|
const pos = await scriptAutocomplete();
|
|
|
|
if (pos) return pos;
|
|
|
|
|
2022-01-12 00:04:14 +01:00
|
|
|
// If input starts with './', essentially treat it as a slimmer
|
|
|
|
// invocation of `run`.
|
|
|
|
if (input.startsWith("./")) {
|
|
|
|
// All programs and scripts
|
|
|
|
for (const script of currServ.scripts) {
|
|
|
|
const res = processFilepath(script.filename);
|
|
|
|
if (res) {
|
|
|
|
allPos.push(res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const program of currServ.programs) {
|
|
|
|
const res = processFilepath(program);
|
|
|
|
if (res) {
|
|
|
|
allPos.push(res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// All coding contracts
|
|
|
|
for (const cct of currServ.contracts) {
|
|
|
|
const res = processFilepath(cct.fn);
|
|
|
|
if (res) {
|
|
|
|
allPos.push(res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return allPos;
|
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
if (isCommand("run")) {
|
|
|
|
addAllScripts();
|
|
|
|
addAllPrograms();
|
|
|
|
addAllCodingContracts();
|
|
|
|
addAllDirectories();
|
|
|
|
}
|
2019-04-05 11:08:41 +02:00
|
|
|
|
2021-12-03 20:44:32 +01:00
|
|
|
if (isCommand("kill") || isCommand("tail") || isCommand("mem") || isCommand("check")) {
|
|
|
|
addAllScripts();
|
|
|
|
addAllDirectories();
|
|
|
|
|
|
|
|
return allPos;
|
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
if (isCommand("cat")) {
|
|
|
|
addAllMessages();
|
|
|
|
addAllLitFiles();
|
|
|
|
addAllTextFiles();
|
|
|
|
addAllDirectories();
|
2021-12-20 11:11:58 +01:00
|
|
|
addAllScripts();
|
2019-04-10 08:07:12 +02:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
return allPos;
|
|
|
|
}
|
2019-04-05 11:08:41 +02:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
if (isCommand("download") || isCommand("mv")) {
|
|
|
|
addAllScripts();
|
|
|
|
addAllTextFiles();
|
|
|
|
addAllDirectories();
|
2019-04-20 07:27:33 +02:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
return allPos;
|
|
|
|
}
|
2019-04-20 07:27:33 +02:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
if (isCommand("cd")) {
|
|
|
|
addAllDirectories();
|
2019-04-05 11:08:41 +02:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
return allPos;
|
|
|
|
}
|
2019-04-10 08:07:12 +02:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
if (isCommand("ls") && index === 0) {
|
|
|
|
addAllDirectories();
|
|
|
|
}
|
2019-04-20 07:27:33 +02:00
|
|
|
|
2022-01-14 22:25:30 +01:00
|
|
|
if (isCommand("help")) {
|
|
|
|
// Get names from here instead of commands array because some
|
|
|
|
// undocumented/nonexistent commands are in the array
|
|
|
|
return Object.keys(HelpTexts);
|
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
return allPos;
|
2019-04-05 11:08:41 +02:00
|
|
|
}
|