#include #include #include #include #include #include #include #include std::map variables; std::vector containers; std::vector lexCommand(const std::string& command) { std::vector 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& args) { pid_t pid = fork(); if (pid == 0) { std::vector c_args; for (const auto& arg : args) { c_args.push_back(const_cast(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 << "\033]0;Spinny\007" << std::flush; 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; }