#include "shellio.h" #include "ansi.h" #include "utf8.h" #include #include #include #include #include namespace detz::shell { namespace { // getpass.getpass(prompt): prompt on the tty, no echo, newline after std::optional GetPass(const std::string& prompt) { int fd = ::open("/dev/tty", O_RDWR | O_NOCTTY); bool own = fd >= 0; if (fd < 0) fd = 0; struct termios old, quiet; bool changed = false; if (::tcgetattr(fd, &old) == 0) { quiet = old; quiet.c_lflag &= ~(tcflag_t)ECHO; changed = ::tcsetattr(fd, TCSAFLUSH, &quiet) == 0; } int out = own ? fd : 2; (void)!::write(out, prompt.data(), prompt.size()); std::string line; bool got = false, eof = false; char c; while (true) { ssize_t n = ::read(fd, &c, 1); if (n <= 0) { eof = true; break; } got = true; if (c == '\n') break; line.push_back(c); } if (changed) ::tcsetattr(fd, TCSAFLUSH, &old); (void)!::write(out, "\n", 1); if (own) ::close(fd); if (eof && !got) return std::nullopt; // EOFError return line; } std::optional Input(const std::string& prompt) { std::fputs(prompt.c_str(), stdout); std::fflush(stdout); std::string line; if (!std::getline(std::cin, line)) return std::nullopt; // EOFError return line; } } // namespace std::unique_ptr ShellIo::Stream(ShellApp& app, const std::string& command, bool live) { if (app.HasStreamFactory()) return app.StreamFactory(app.Redact(command)); if (live && ::isatty(1)) return std::make_unique(); return nullptr; } AskFn ShellIo::Ask(ShellApp& app, const std::string& command) { if (app.Auto() || !app.Persist() || app.Depth() > 0) return nullptr; ShellApp* a = &app; auto answered = [a](std::optional value) { if (value) a->MarkUserSteppedIn(); return value; }; if (app.HasPrompter()) { std::string head = Utf8::Head(command, 60); return [a, head, answered](const std::string& text, bool) { return answered(a->Prompt("ask", head + " asks:\n" + text, {})); }; } if (!(::isatty(0) && ::isatty(1))) return nullptr; return [answered](const std::string& text, bool secret) -> std::optional { std::string prompt = Paint("\xc2\xbb " + text + " ", Ansi::kYellow); return answered(secret ? GetPass(prompt) : Input(prompt)); }; } } // namespace detz::shell