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