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