Initial commit

This commit is contained in:
2026-01-29 22:37:14 +11:00
commit 2250577caf
2 changed files with 277 additions and 0 deletions

216
main.cpp Normal file
View File

@@ -0,0 +1,216 @@
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <unistd.h>
#include <sys/wait.h>
std::map<std::string, std::string> variables;
std::vector<std::string> containers;
std::vector<std::string> lexCommand(const std::string& command) {
std::vector<std::string> lexed;
std::string buf;
bool inString = false;
bool inVariable = false;
for (const char& c : command) {
bool addToBuf = inString && inVariable;
switch (c) {
case '"': {
inString = !inString;
if (!inString) {
lexed.push_back(buf);
buf.clear();
}
break;
}
case ' ': {
if (inString) {
buf += c;
} else if (inVariable) {
if (variables.find(buf) != variables.end()) {
if (!buf.empty()) lexed.push_back(variables[buf]);
buf.clear();
} else {
lexed.push_back("");
buf.clear();
}
inVariable = false;
} else {
if (!buf.empty()) lexed.push_back(buf);
buf.clear();
}
break;
}
case '$': {
if (addToBuf) {
buf += c;
} else {
inVariable = true;
}
break;
}
default: {
buf += c;
}
}
}
if (!buf.empty()) lexed.push_back(buf);
return lexed;
}
void safeExecute(const std::vector<std::string>& args) {
pid_t pid = fork();
if (pid == 0) {
std::vector<char*> c_args;
for (const auto& arg : args) {
c_args.push_back(const_cast<char*>(arg.c_str()));
}
c_args.push_back(nullptr);
execvp(c_args[0], c_args.data());
exit(1);
} else {
wait(NULL);
}
}
void syncContainers() {
containers.clear();
// Run podman command to get names of all containers
FILE* pipe = popen("podman ps -a --format '{{.Names}}'", "r");
if (!pipe) return;
char buffer[128];
while (fgets(buffer, sizeof(buffer), pipe) != NULL) {
std::string name = buffer;
// Remove trailing newline
if (!name.empty() && name.back() == '\n') name.pop_back();
containers.push_back(name);
}
pclose(pipe);
}
int main(int argc, char** argv) {
std::cout << "Welcome to Spinny! Type a command to get started..." << std::endl;
bool done = false;
std::string prompt;
while (!done) {
std::cout << "> ";
std::getline(std::cin, prompt);
auto command = lexCommand(prompt);
if (!command.empty()) {
syncContainers();
if (command[0] == "exit") {
done = true;
}
else if (command[0] == "set") {
if (command.size() < 3) {
std::cout << "Usage: set [key] [value]" << std::endl;
} else {
variables[command[1]] = command[2];
}
}
else if (command[0] == "create") {
if (command.size() < 2) {
std::cout << "Usage: create [name]" << std::endl;
} else {
if (std::find(containers.begin(), containers.end(), command[1]) != containers.end()) {
std::cout << "Container " + command[1] + " already exists (use 'delete " + command[1] + "' to delete the existing container)" << std::endl;
} else {
safeExecute({"podman", "run", "-d", "--name", command[1], "dev-container", "tail", "-f", "/dev/null"});
containers.push_back(command[1]);
std::cout << "Done! Enter your new container with 'enter " + command[1] + "'" << std::endl;
}
}
}
else if (command[0] == "start") {
if (command.size() < 2) {
std::cout << "Usage: start [name]" << std::endl;
} else {
if (std::find(containers.begin(), containers.end(), command[1]) != containers.end()) {
safeExecute({"podman", "start", command[1]});
} else {
std::cout << "Container " + command[1] + " not found (use 'create " + command[1] + "' to make it)" << std::endl;
}
}
}
else if (command[0] == "delete") {
if (command.size() < 2) {
std::cout << "Usage: start [name]" << std::endl;
} else {
if (std::find(containers.begin(), containers.end(), command[1]) != containers.end()) {
std::string buf;
std::cout << "This will delete the container, and all files within it. Are you sure? (yes/no)";
std::getline(std::cin, buf);
if (buf == "yes") {
containers.erase(std::remove(containers.begin(), containers.end(), command[1]), containers.end());
safeExecute({"podman", "rm", command[1]});
std::cout << "Deleted!" << std::endl;
} else {
std::cout << "*container breathes a sigh of relief*" << std::endl;
}
} else {
std::cout << "Container " + command[1] + " not found (use 'create " + command[1] + "' to make it)" << std::endl;
}
}
}
else if (command[0] == "stop") {
if (command.size() < 2) {
std::cout << "Usage: stop [name]" << std::endl;
} else {
if (std::find(containers.begin(), containers.end(), command[1]) != containers.end()) {
safeExecute({"podman", "stop", command[1]});
} else {
std::cout << "Container " + command[1] + " not found (use 'create " + command[1] + "' to make it)" << std::endl;
}
}
}
else if (command[0] == "enter") {
if (command.size() < 2) {
std::cout << "Usage: enter [name]" << std::endl;
} else {
if (std::find(containers.begin(), containers.end(), command[1]) != containers.end()) {
safeExecute({"podman", "exec", "-it", command[1], "fish"});
} else {
std::cout << "Container " + command[1] + " not found (use 'create " + command[1] + "' to make it)" << std::endl;
}
}
}
else if (command[0] == "clear") {
safeExecute({"clear"});
}
else if (command[0] == "help") {
std::cout << "Commands:" << std::endl;
std::cout << " create [name] - Creates an empty container" << std::endl;
std::cout << " enter [name] - Opens a shell in a container" << std::endl;
std::cout << " start [name] - Starts a container" << std::endl;
std::cout << " stop [name] - Stops a container" << std::endl;
std::cout << " delete [name] - Deletes a container" << std::endl;
std::cout << " tmp - Create a temporary container" << std::endl;
std::cout << " list - List containers" << std::endl;
std::cout << " exit - Exits Spinny" << std::endl;
}
else if (command[0] == "list" || command[0] == "ls") {
std::cout << "My containers:" << std::endl;
safeExecute({"podman", "ps", "-a"});
}
else if (command[0] == "tmp") {
std::cout << "Spawning temporary container..." << std::endl;
safeExecute({"podman", "run", "-it", "--rm", "dev-container", "fish"});
} else {
std::cout << "I don't know how to " + command[0] + " (run 'help' for commands)" << std::endl;
}
}
}
std::cout << "Goodbye!" << std::endl;
return 0;
}