diff options
Diffstat (limited to 'src/common/sprompt.c')
-rw-r--r-- | src/common/sprompt.c | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/src/common/sprompt.c b/src/common/sprompt.c new file mode 100644 index 0000000..201c831 --- /dev/null +++ b/src/common/sprompt.c @@ -0,0 +1,181 @@ +/*------------------------------------------------------------------------- + * + * sprompt.c + * simple_prompt() routine + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/common/sprompt.c + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +#include "common/fe_memutils.h" +#include "common/string.h" + +#ifdef HAVE_TERMIOS_H +#include <termios.h> +#endif + + +/* + * simple_prompt + * + * Generalized function especially intended for reading in usernames and + * passwords interactively. Reads from /dev/tty or stdin/stderr. + * + * prompt: The prompt to print, or NULL if none (automatically localized) + * echo: Set to false if you want to hide what is entered (for passwords) + * + * The input (without trailing newline) is returned as a malloc'd string. + * Caller is responsible for freeing it when done. + */ +char * +simple_prompt(const char *prompt, bool echo) +{ + return simple_prompt_extended(prompt, echo, NULL); +} + +/* + * simple_prompt_extended + * + * This is the same as simple_prompt(), except that prompt_ctx can + * optionally be provided to allow this function to be canceled via an + * existing SIGINT signal handler that will longjmp to the specified place + * only when *(prompt_ctx->enabled) is true. If canceled, this function + * returns an empty string, and prompt_ctx->canceled is set to true. + */ +char * +simple_prompt_extended(const char *prompt, bool echo, + PromptInterruptContext *prompt_ctx) +{ + char *result; + FILE *termin, + *termout; +#if defined(HAVE_TERMIOS_H) + struct termios t_orig, + t; +#elif defined(WIN32) + HANDLE t = NULL; + DWORD t_orig = 0; +#endif + +#ifdef WIN32 + + /* + * A Windows console has an "input code page" and an "output code page"; + * these usually match each other, but they rarely match the "Windows ANSI + * code page" defined at system boot and expected of "char *" arguments to + * Windows API functions. The Microsoft CRT write() implementation + * automatically converts text between these code pages when writing to a + * console. To identify such file descriptors, it calls GetConsoleMode() + * on the underlying HANDLE, which in turn requires GENERIC_READ access on + * the HANDLE. Opening termout in mode "w+" allows that detection to + * succeed. Otherwise, write() would not recognize the descriptor as a + * console, and non-ASCII characters would display incorrectly. + * + * XXX fgets() still receives text in the console's input code page. This + * makes non-ASCII credentials unportable. + * + * Unintuitively, we also open termin in mode "w+", even though we only + * read it; that's needed for SetConsoleMode() to succeed. + */ + termin = fopen("CONIN$", "w+"); + termout = fopen("CONOUT$", "w+"); +#else + + /* + * Do not try to collapse these into one "w+" mode file. Doesn't work on + * some platforms (eg, HPUX 10.20). + */ + termin = fopen("/dev/tty", "r"); + termout = fopen("/dev/tty", "w"); +#endif + if (!termin || !termout +#ifdef WIN32 + + /* + * Direct console I/O does not work from the MSYS 1.0.10 console. Writes + * reach nowhere user-visible; reads block indefinitely. XXX This affects + * most Windows terminal environments, including rxvt, mintty, Cygwin + * xterm, Cygwin sshd, and PowerShell ISE. Switch to a more-generic test. + */ + || (getenv("OSTYPE") && strcmp(getenv("OSTYPE"), "msys") == 0) +#endif + ) + { + if (termin) + fclose(termin); + if (termout) + fclose(termout); + termin = stdin; + termout = stderr; + } + + if (!echo) + { +#if defined(HAVE_TERMIOS_H) + /* disable echo via tcgetattr/tcsetattr */ + tcgetattr(fileno(termin), &t); + t_orig = t; + t.c_lflag &= ~ECHO; + tcsetattr(fileno(termin), TCSAFLUSH, &t); +#elif defined(WIN32) + /* need the file's HANDLE to turn echo off */ + t = (HANDLE) _get_osfhandle(_fileno(termin)); + + /* save the old configuration first */ + GetConsoleMode(t, &t_orig); + + /* set to the new mode */ + SetConsoleMode(t, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT); +#endif + } + + if (prompt) + { + fputs(_(prompt), termout); + fflush(termout); + } + + result = pg_get_line(termin, prompt_ctx); + + /* If we failed to read anything, just return an empty string */ + if (result == NULL) + result = pg_strdup(""); + + /* strip trailing newline, including \r in case we're on Windows */ + (void) pg_strip_crlf(result); + + if (!echo) + { + /* restore previous echo behavior, then echo \n */ +#if defined(HAVE_TERMIOS_H) + tcsetattr(fileno(termin), TCSAFLUSH, &t_orig); + fputs("\n", termout); + fflush(termout); +#elif defined(WIN32) + SetConsoleMode(t, t_orig); + fputs("\n", termout); + fflush(termout); +#endif + } + else if (prompt_ctx && prompt_ctx->canceled) + { + /* also echo \n if prompt was canceled */ + fputs("\n", termout); + fflush(termout); + } + + if (termin != stdin) + { + fclose(termin); + fclose(termout); + } + + return result; +} |