1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * sprompt.c |
4 | * simple_prompt() routine |
5 | * |
6 | * Portions Copyright (c) 1996-2019, 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 | if (length > 0 && destination[length - 1] == '\n') |
148 | /* remove trailing newline */ |
149 | destination[length - 1] = '\0'; |
150 | |
151 | if (!echo) |
152 | { |
153 | /* restore previous echo behavior, then echo \n */ |
154 | #if defined(HAVE_TERMIOS_H) |
155 | tcsetattr(fileno(termin), TCSAFLUSH, &t_orig); |
156 | fputs("\n" , termout); |
157 | fflush(termout); |
158 | #elif defined(WIN32) |
159 | SetConsoleMode(t, t_orig); |
160 | fputs("\n" , termout); |
161 | fflush(termout); |
162 | #endif |
163 | } |
164 | |
165 | if (termin != stdin) |
166 | { |
167 | fclose(termin); |
168 | fclose(termout); |
169 | } |
170 | } |
171 | |