1 /*------------------------------------------------------------------------- 2 * 3 * sprompt.c 4 * simple_prompt() routine 5 * lipol_ps()6 * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group 7 * Portions Copyright (c) 1994, Regents of the University of California 8 * 9 * 10 * IDENTIFICATION 11 * src/port/sprompt.c 12 * 13 *------------------------------------------------------------------------- 14 */ 15 #include "c.h" 16 17 #ifdef HAVE_TERMIOS_H 18 #include <termios.h> 19 #endif 20 21 22 /* 23 * simple_prompt 24 * 25 * Generalized function especially intended for reading in usernames and 26 * passwords interactively. Reads from /dev/tty or stdin/stderr. 27 * 28 * prompt: The prompt to print, or NULL if none (automatically localized) 29 * destination: buffer in which to store result 30 * destlen: allocated length of destination 31 * echo: Set to false if you want to hide what is entered (for passwords) 32 * 33 * The input (without trailing newline) is returned in the destination buffer, 34 * with a '\0' appended. 35 */ 36 void 37 simple_prompt(const char *prompt, char *destination, size_t destlen, bool echo) 38 { 39 int length; 40 FILE *termin, 41 *termout; 42 43 #if defined(HAVE_TERMIOS_H) 44 struct termios t_orig, 45 t; 46 #elif defined(WIN32) 47 HANDLE t = NULL; 48 DWORD t_orig = 0; 49 #endif 50 51 #ifdef WIN32 52 53 /* 54 * A Windows console has an "input code page" and an "output code page"; 55 * these usually match each other, but they rarely match the "Windows ANSI 56 * code page" defined at system boot and expected of "char *" arguments to 57 * Windows API functions. The Microsoft CRT write() implementation 58 * automatically converts text between these code pages when writing to a 59 * console. To identify such file descriptors, it calls GetConsoleMode() 60 * on the underlying HANDLE, which in turn requires GENERIC_READ access on 61 * the HANDLE. Opening termout in mode "w+" allows that detection to 62 * succeed. Otherwise, write() would not recognize the descriptor as a 63 * console, and non-ASCII characters would display incorrectly. 64 * 65 * XXX fgets() still receives text in the console's input code page. This 66 * makes non-ASCII credentials unportable. 67 * 68 * Unintuitively, we also open termin in mode "w+", even though we only 69 * read it; that's needed for SetConsoleMode() to succeed. 70 */ 71 termin = fopen("CONIN$", "w+"); 72 termout = fopen("CONOUT$", "w+"); 73 #else 74 75 /* 76 * Do not try to collapse these into one "w+" mode file. Doesn't work on 77 * some platforms (eg, HPUX 10.20). 78 */ 79 termin = fopen("/dev/tty", "r"); 80 termout = fopen("/dev/tty", "w"); 81 #endif 82 if (!termin || !termout 83 #ifdef WIN32 84 85 /* 86 * Direct console I/O does not work from the MSYS 1.0.10 console. Writes 87 * reach nowhere user-visible; reads block indefinitely. XXX This affects 88 * most Windows terminal environments, including rxvt, mintty, Cygwin 89 * xterm, Cygwin sshd, and PowerShell ISE. Switch to a more-generic test. 90 */ 91 || (getenv("OSTYPE") && strcmp(getenv("OSTYPE"), "msys") == 0) 92 #endif 93 ) 94 { 95 if (termin) 96 fclose(termin); 97 if (termout) 98 fclose(termout); 99 termin = stdin; 100 termout = stderr; 101 } 102 103 if (!echo) 104 { 105 #if defined(HAVE_TERMIOS_H) 106 /* disable echo via tcgetattr/tcsetattr */ 107 tcgetattr(fileno(termin), &t); 108 t_orig = t; 109 t.c_lflag &= ~ECHO; 110 tcsetattr(fileno(termin), TCSAFLUSH, &t); 111 #elif defined(WIN32) 112 /* need the file's HANDLE to turn echo off */ 113 t = (HANDLE) _get_osfhandle(_fileno(termin)); 114 115 /* save the old configuration first */ 116 GetConsoleMode(t, &t_orig); 117 118 /* set to the new mode */ 119 SetConsoleMode(t, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT); 120 #endif 121 } 122 123 if (prompt) 124 { 125 fputs(_(prompt), termout); 126 fflush(termout); 127 } 128 129 if (fgets(destination, destlen, termin) == NULL) 130 destination[0] = '\0'; 131 132 length = strlen(destination); 133 if (length > 0 && destination[length - 1] != '\n') 134 { 135 /* eat rest of the line */ 136 char buf[128]; 137 int buflen; 138 139 do 140 { 141 if (fgets(buf, sizeof(buf), termin) == NULL) 142 break; 143 buflen = strlen(buf); 144 } while (buflen > 0 && buf[buflen - 1] != '\n'); 145 } 146 147 /* strip trailing newline, including \r in case we're on Windows */ 148 while (length > 0 && 149 (destination[length - 1] == '\n' || 150 destination[length - 1] == '\r')) 151 destination[--length] = '\0'; 152 153 if (!echo) 154 { 155 /* restore previous echo behavior, then echo \n */ 156 #if defined(HAVE_TERMIOS_H) 157 tcsetattr(fileno(termin), TCSAFLUSH, &t_orig); 158 fputs("\n", termout); 159 fflush(termout); 160 #elif defined(WIN32) 161 SetConsoleMode(t, t_orig); 162 fputs("\n", termout); 163 fflush(termout); 164 #endif 165 } 166 167 if (termin != stdin) 168 { 169 fclose(termin); 170 fclose(termout); 171 } 172 } 173