/src/gnupg/common/ttyio.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* ttyio.c - tty i/O functions |
2 | | * Copyright (C) 1997-2019 Werner Koch |
3 | | * Copyright (C) 1998-2020 Free Software Foundation, Inc. |
4 | | * Copyright (C) 2015-2020 g10 Code GmbH |
5 | | * |
6 | | * This file is part of GnuPG. |
7 | | * |
8 | | * This file is free software; you can redistribute it and/or modify |
9 | | * it under the terms of either |
10 | | * |
11 | | * - the GNU Lesser General Public License as published by the Free |
12 | | * Software Foundation; either version 3 of the License, or (at |
13 | | * your option) any later version. |
14 | | * |
15 | | * or |
16 | | * |
17 | | * - the GNU General Public License as published by the Free |
18 | | * Software Foundation; either version 2 of the License, or (at |
19 | | * your option) any later version. |
20 | | * |
21 | | * or both in parallel, as here. |
22 | | * |
23 | | * This file is distributed in the hope that it will be useful, |
24 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
25 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
26 | | * GNU General Public License for more details. |
27 | | * |
28 | | * You should have received a copy of the GNU General Public License |
29 | | * along with this program; if not, see <https://www.gnu.org/licenses/>. |
30 | | * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later) |
31 | | */ |
32 | | |
33 | | #include <config.h> |
34 | | #include <stdio.h> |
35 | | #include <stdlib.h> |
36 | | #include <string.h> |
37 | | #include <stdarg.h> |
38 | | #include <unistd.h> |
39 | | |
40 | | #ifdef HAVE_TCGETATTR |
41 | | # include <termios.h> |
42 | | #else |
43 | | # ifdef HAVE_TERMIO_H |
44 | | /* simulate termios with termio */ |
45 | | # include <termio.h> |
46 | | # define termios termio |
47 | | # define tcsetattr ioctl |
48 | | # define TCSAFLUSH TCSETAF |
49 | | # define tcgetattr(A,B) ioctl(A,TCGETA,B) |
50 | | # define HAVE_TCGETATTR |
51 | | # endif |
52 | | #endif |
53 | | #ifdef HAVE_W32_SYSTEM |
54 | | # ifdef HAVE_WINSOCK2_H |
55 | | # include <winsock2.h> |
56 | | # endif |
57 | | # include <windows.h> |
58 | | # ifdef HAVE_TCGETATTR |
59 | | # error mingw32 and termios |
60 | | # endif |
61 | | #endif |
62 | | #include <errno.h> |
63 | | #include <ctype.h> |
64 | | |
65 | | #include "util.h" |
66 | | #include "ttyio.h" |
67 | | #include "i18n.h" |
68 | | #include "common-defs.h" |
69 | | |
70 | 0 | #define CONTROL_D ('D' - 'A' + 1) |
71 | | |
72 | | |
73 | | #ifdef HAVE_W32_SYSTEM |
74 | | static struct { |
75 | | HANDLE in, out; |
76 | | } con; |
77 | | #define DEF_INPMODE (ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT \ |
78 | | |ENABLE_PROCESSED_INPUT ) |
79 | | #define HID_INPMODE (ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT ) |
80 | | #define DEF_OUTMODE (ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_PROCESSED_OUTPUT) |
81 | | |
82 | | #else /* Unix */ |
83 | | static FILE *ttyfp = NULL; |
84 | | #endif /* Unix */ |
85 | | |
86 | | static int initialized; |
87 | | static int last_prompt_len; |
88 | | static int batchmode; |
89 | | static int no_terminal; |
90 | | |
91 | | #ifdef HAVE_TCGETATTR |
92 | | static struct termios termsave; |
93 | | static int restore_termios; |
94 | | #endif |
95 | | |
96 | | /* Hooks set by gpgrlhelp.c if required. */ |
97 | | static void (*my_rl_set_completer) (rl_completion_func_t *); |
98 | | static void (*my_rl_inhibit_completion) (int); |
99 | | static void (*my_rl_cleanup_after_signal) (void); |
100 | | static void (*my_rl_init_stream) (FILE *); |
101 | | static char *(*my_rl_readline) (const char*); |
102 | | static void (*my_rl_add_history) (const char*); |
103 | | static int (*my_rl_rw_history)(const char *, int, int); |
104 | | |
105 | | /* This is a wrapper around ttyname so that we can use it even when |
106 | | the standard streams are redirected. It figures the name out the |
107 | | first time and returns it in a statically allocated buffer. */ |
108 | | const char * |
109 | | tty_get_ttyname (void) |
110 | 0 | { |
111 | 0 | static char *name; |
112 | | |
113 | | /* On a GNU system ctermid() always return /dev/tty, so this does |
114 | | not make much sense - however if it is ever changed we do the |
115 | | Right Thing now. */ |
116 | 0 | #ifdef HAVE_CTERMID |
117 | 0 | static int got_name; |
118 | |
|
119 | 0 | if (!got_name) |
120 | 0 | { |
121 | 0 | const char *s; |
122 | | /* Note that despite our checks for these macros the function is |
123 | | not necessarily thread save. We mainly do this for |
124 | | portability reasons, in case L_ctermid is not defined. */ |
125 | 0 | # if defined(_POSIX_THREAD_SAFE_FUNCTIONS) || defined(_POSIX_TRHEADS) |
126 | 0 | char buffer[L_ctermid]; |
127 | 0 | s = ctermid (buffer); |
128 | | # else |
129 | | s = ctermid (NULL); |
130 | | # endif |
131 | 0 | if (s) |
132 | 0 | name = strdup (s); |
133 | 0 | got_name = 1; |
134 | 0 | } |
135 | 0 | #endif /*HAVE_CTERMID*/ |
136 | | /* Assume the standard tty on memory error or when there is no |
137 | | ctermid. */ |
138 | 0 | return name? name : "/dev/tty"; |
139 | 0 | } |
140 | | |
141 | | |
142 | | |
143 | | #ifdef HAVE_TCGETATTR |
144 | | static void |
145 | | cleanup(void) |
146 | 0 | { |
147 | 0 | if (restore_termios) |
148 | 0 | { |
149 | 0 | restore_termios = 0; /* do it prior in case it is interrupted again */ |
150 | 0 | if (tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave)) |
151 | 0 | log_error ("tcsetattr() failed: %s\n", strerror (errno)); |
152 | 0 | } |
153 | 0 | } |
154 | | #endif /*HAVE_TCGETATTR*/ |
155 | | |
156 | | |
157 | | static void |
158 | | init_ttyfp(void) |
159 | 0 | { |
160 | 0 | if (initialized) |
161 | 0 | return; |
162 | | |
163 | | #ifdef HAVE_W32_SYSTEM |
164 | | { |
165 | | SECURITY_ATTRIBUTES sa; |
166 | | |
167 | | memset (&sa, 0, sizeof(sa)); |
168 | | sa.nLength = sizeof(sa); |
169 | | sa.bInheritHandle = TRUE; |
170 | | con.out = CreateFileA ("CONOUT$", GENERIC_READ|GENERIC_WRITE, |
171 | | FILE_SHARE_READ|FILE_SHARE_WRITE, |
172 | | &sa, OPEN_EXISTING, 0, 0 ); |
173 | | if (con.out == INVALID_HANDLE_VALUE) |
174 | | log_fatal ("open(CONOUT$) failed: %s\n", w32_strerror (-1)); |
175 | | |
176 | | memset (&sa, 0, sizeof(sa)); |
177 | | sa.nLength = sizeof(sa); |
178 | | sa.bInheritHandle = TRUE; |
179 | | con.in = CreateFileA ("CONIN$", GENERIC_READ|GENERIC_WRITE, |
180 | | FILE_SHARE_READ|FILE_SHARE_WRITE, |
181 | | &sa, OPEN_EXISTING, 0, 0 ); |
182 | | if (con.in == INVALID_HANDLE_VALUE) |
183 | | log_fatal ("open(CONIN$) failed: %s\n", w32_strerror (-1)); |
184 | | } |
185 | | SetConsoleMode (con.in, DEF_INPMODE); |
186 | | SetConsoleMode (con.out, DEF_OUTMODE); |
187 | | |
188 | | #else /* Unix */ |
189 | 0 | ttyfp = batchmode? stderr : fopen (tty_get_ttyname (), "r+"); |
190 | 0 | if (!ttyfp) |
191 | 0 | { |
192 | 0 | log_error ("cannot open '%s': %s\n", tty_get_ttyname (), strerror(errno)); |
193 | 0 | exit (2); |
194 | 0 | } |
195 | 0 | if (my_rl_init_stream) |
196 | 0 | my_rl_init_stream (ttyfp); |
197 | 0 | #endif /* Unix */ |
198 | |
|
199 | 0 | #ifdef HAVE_TCGETATTR |
200 | 0 | atexit (cleanup); |
201 | 0 | #endif |
202 | |
|
203 | 0 | initialized = 1; |
204 | 0 | } |
205 | | |
206 | | |
207 | | int |
208 | | tty_batchmode( int onoff ) |
209 | 0 | { |
210 | 0 | int old = batchmode; |
211 | 0 | if (onoff != -1) |
212 | 0 | batchmode = onoff; |
213 | 0 | return old; |
214 | 0 | } |
215 | | |
216 | | int |
217 | | tty_no_terminal(int onoff) |
218 | 0 | { |
219 | 0 | int old = no_terminal; |
220 | 0 | no_terminal = onoff ? 1 : 0; |
221 | 0 | return old; |
222 | 0 | } |
223 | | |
224 | | |
225 | | #ifdef HAVE_W32_SYSTEM |
226 | | /* Write the UTF-8 encoded STRING to the console. */ |
227 | | static void |
228 | | w32_write_console (const char *string) |
229 | | { |
230 | | wchar_t *wstring; |
231 | | DWORD n, nwritten; |
232 | | |
233 | | wstring = utf8_to_wchar (string); |
234 | | if (!wstring) |
235 | | log_fatal ("w32_write_console failed: %s", strerror (errno)); |
236 | | n = wcslen (wstring); |
237 | | |
238 | | if (!WriteConsoleW (con.out, wstring, n, &nwritten, NULL)) |
239 | | { |
240 | | static int shown; |
241 | | if (!shown) |
242 | | { |
243 | | shown = 1; |
244 | | log_info ("WriteConsole failed: %s", w32_strerror (-1)); |
245 | | log_info ("Please configure a suitable font for the console\n"); |
246 | | } |
247 | | n = strlen (string); |
248 | | if (!WriteConsoleA (con.out, string, n , &nwritten, NULL)) |
249 | | log_fatal ("WriteConsole fallback failed: %s", w32_strerror (-1)); |
250 | | } |
251 | | else |
252 | | { |
253 | | if (n != nwritten) |
254 | | log_fatal ("WriteConsole failed: %lu != %lu\n", |
255 | | (unsigned long)n, (unsigned long)nwritten); |
256 | | } |
257 | | last_prompt_len += n; |
258 | | xfree (wstring); |
259 | | } |
260 | | #endif /*HAVE_W32_SYSTEM*/ |
261 | | |
262 | | |
263 | | void |
264 | | tty_printf (const char *fmt, ... ) |
265 | 0 | { |
266 | 0 | va_list arg_ptr; |
267 | |
|
268 | 0 | if (no_terminal) |
269 | 0 | return; |
270 | | |
271 | 0 | if (!initialized) |
272 | 0 | init_ttyfp (); |
273 | |
|
274 | 0 | va_start (arg_ptr, fmt); |
275 | |
|
276 | | #ifdef HAVE_W32_SYSTEM |
277 | | { |
278 | | char *buf = NULL; |
279 | | |
280 | | vasprintf(&buf, fmt, arg_ptr); |
281 | | if (!buf) |
282 | | log_bug ("vasprintf() failed\n"); |
283 | | w32_write_console (buf); |
284 | | xfree (buf); |
285 | | } |
286 | | #else /* Unix */ |
287 | 0 | last_prompt_len += vfprintf (ttyfp, fmt, arg_ptr) ; |
288 | 0 | fflush (ttyfp); |
289 | 0 | #endif /* Unix */ |
290 | 0 | va_end(arg_ptr); |
291 | 0 | } |
292 | | |
293 | | |
294 | | /* Same as tty_printf but if FP is not NULL, behave like a regular |
295 | | fprintf. */ |
296 | | void |
297 | | tty_fprintf (estream_t fp, const char *fmt, ... ) |
298 | 0 | { |
299 | 0 | va_list arg_ptr; |
300 | |
|
301 | 0 | if (fp) |
302 | 0 | { |
303 | 0 | va_start (arg_ptr, fmt) ; |
304 | 0 | es_vfprintf (fp, fmt, arg_ptr ); |
305 | 0 | va_end (arg_ptr); |
306 | 0 | return; |
307 | 0 | } |
308 | | |
309 | 0 | if (no_terminal) |
310 | 0 | return; |
311 | | |
312 | 0 | if (!initialized) |
313 | 0 | init_ttyfp (); |
314 | |
|
315 | 0 | va_start (arg_ptr, fmt); |
316 | |
|
317 | | #ifdef HAVE_W32_SYSTEM |
318 | | { |
319 | | char *buf = NULL; |
320 | | |
321 | | vasprintf (&buf, fmt, arg_ptr); |
322 | | if (!buf) |
323 | | log_bug ("vasprintf() failed\n"); |
324 | | w32_write_console (buf); |
325 | | xfree (buf); |
326 | | } |
327 | | #else /* Unix */ |
328 | 0 | last_prompt_len += vfprintf(ttyfp,fmt,arg_ptr) ; |
329 | 0 | fflush(ttyfp); |
330 | 0 | #endif /* Unix */ |
331 | |
|
332 | 0 | va_end(arg_ptr); |
333 | 0 | } |
334 | | |
335 | | |
336 | | /* Print a string, but filter all control characters out. If FP is |
337 | | * not NULL print to that stream instead to the tty. */ |
338 | | static void |
339 | | do_print_string (estream_t fp, const byte *p, size_t n ) |
340 | 0 | { |
341 | 0 | if (no_terminal && !fp) |
342 | 0 | return; |
343 | | |
344 | 0 | if (!initialized && !fp) |
345 | 0 | init_ttyfp(); |
346 | |
|
347 | 0 | if (fp) |
348 | 0 | { |
349 | 0 | print_utf8_buffer (fp, p, n); |
350 | 0 | return; |
351 | 0 | } |
352 | | |
353 | | #ifdef HAVE_W32_SYSTEM |
354 | | /* Not so effective, change it if you want */ |
355 | | for (; n; n--, p++) |
356 | | { |
357 | | if (iscntrl (*p)) |
358 | | { |
359 | | if( *p == '\n' ) |
360 | | tty_printf ("\\n"); |
361 | | else if( !*p ) |
362 | | tty_printf ("\\0"); |
363 | | else |
364 | | tty_printf ("\\x%02x", *p); |
365 | | } |
366 | | else |
367 | | tty_printf ("%c", *p); |
368 | | } |
369 | | #else /* Unix */ |
370 | 0 | for (; n; n--, p++) |
371 | 0 | { |
372 | 0 | if (iscntrl (*p)) |
373 | 0 | { |
374 | 0 | putc ('\\', ttyfp); |
375 | 0 | if ( *p == '\n' ) |
376 | 0 | putc ('n', ttyfp); |
377 | 0 | else if ( !*p ) |
378 | 0 | putc ('0', ttyfp); |
379 | 0 | else |
380 | 0 | fprintf (ttyfp, "x%02x", *p ); |
381 | 0 | } |
382 | 0 | else |
383 | 0 | putc (*p, ttyfp); |
384 | 0 | } |
385 | 0 | #endif /* Unix */ |
386 | 0 | } |
387 | | |
388 | | |
389 | | void |
390 | | tty_print_utf8_string2 (estream_t fp, const byte *p, size_t n, size_t max_n) |
391 | 0 | { |
392 | 0 | size_t i; |
393 | 0 | char *buf; |
394 | |
|
395 | 0 | if (no_terminal && !fp) |
396 | 0 | return; |
397 | | |
398 | | /* We can handle plain ascii simpler, so check for it first. */ |
399 | 0 | for(i=0; i < n; i++ ) |
400 | 0 | { |
401 | 0 | if (p[i] & 0x80) |
402 | 0 | break; |
403 | 0 | } |
404 | 0 | if (i < n) |
405 | 0 | { |
406 | 0 | buf = utf8_to_native ((const char *)p, n, 0); |
407 | 0 | if (max_n && (strlen (buf) > max_n)) |
408 | 0 | buf[max_n] = 0; |
409 | | /* (utf8_to_native already did the control character quoting) */ |
410 | 0 | tty_fprintf (fp, "%s", buf); |
411 | 0 | xfree (buf); |
412 | 0 | } |
413 | 0 | else |
414 | 0 | { |
415 | 0 | if (max_n && (n > max_n)) |
416 | 0 | n = max_n; |
417 | 0 | do_print_string (fp, p, n ); |
418 | 0 | } |
419 | 0 | } |
420 | | |
421 | | |
422 | | void |
423 | | tty_print_utf8_string (const byte *p, size_t n) |
424 | 0 | { |
425 | 0 | tty_print_utf8_string2 (NULL, p, n, 0); |
426 | 0 | } |
427 | | |
428 | | |
429 | | /* Read a string from the tty using PROMPT. If HIDDEN is set the |
430 | | * input is not echoed. */ |
431 | | static char * |
432 | | do_get (const char *prompt, int hidden) |
433 | 0 | { |
434 | 0 | char *buf; |
435 | 0 | int n; /* Allocated size of BUF. */ |
436 | 0 | int i; /* Number of bytes in BUF. */ |
437 | 0 | int c; |
438 | | #ifdef HAVE_W32_SYSTEM |
439 | | char *utf8buf; |
440 | | int errcount = 0; |
441 | | #else |
442 | 0 | byte cbuf[1]; |
443 | 0 | #endif |
444 | |
|
445 | 0 | if (batchmode) |
446 | 0 | { |
447 | 0 | log_error (_("Sorry, we are in batchmode - can't get input\n")); |
448 | 0 | exit (2); |
449 | 0 | } |
450 | | |
451 | 0 | if (no_terminal) |
452 | 0 | { |
453 | 0 | log_error (_("Sorry, no terminal at all requested - can't get input\n")); |
454 | 0 | exit (2); |
455 | 0 | } |
456 | | |
457 | 0 | if (!initialized) |
458 | 0 | init_ttyfp (); |
459 | |
|
460 | 0 | last_prompt_len = 0; |
461 | 0 | tty_printf ("%s", prompt); |
462 | 0 | buf = xmalloc ((n=50)); |
463 | 0 | i = 0; |
464 | |
|
465 | | #ifdef HAVE_W32_SYSTEM |
466 | | if (hidden) |
467 | | SetConsoleMode(con.in, HID_INPMODE ); |
468 | | |
469 | | utf8buf = NULL; |
470 | | for (;;) |
471 | | { |
472 | | DWORD nread; |
473 | | wchar_t wbuf[2]; |
474 | | const unsigned char *s; |
475 | | |
476 | | if (!ReadConsoleW (con.in, wbuf, 1, &nread, NULL)) |
477 | | log_fatal ("ReadConsole failed: %s", w32_strerror (-1)); |
478 | | if (!nread) |
479 | | continue; |
480 | | |
481 | | wbuf[1] = 0; |
482 | | xfree (utf8buf); |
483 | | utf8buf = wchar_to_utf8 (wbuf); |
484 | | if (!utf8buf) |
485 | | { |
486 | | log_info ("wchar_to_utf8 failed: %s\n", strerror (errno)); |
487 | | if (++errcount > 10) |
488 | | log_fatal (_("too many errors; giving up\n")); |
489 | | continue; |
490 | | } |
491 | | if (*utf8buf == '\n') |
492 | | { |
493 | | if (utf8buf[1]) |
494 | | { |
495 | | log_info ("ReadConsole returned more than requested" |
496 | | " (0x0a,0x%02x)\n", utf8buf[1]); |
497 | | if (++errcount > 10) |
498 | | log_fatal (_("too many errors; giving up\n")); |
499 | | } |
500 | | break; |
501 | | } |
502 | | if (!hidden) |
503 | | last_prompt_len++; |
504 | | |
505 | | for (s=utf8buf; *s; s++) |
506 | | { |
507 | | c = *s; |
508 | | if (c == '\t') |
509 | | c = ' '; /* Map tab to a space. */ |
510 | | else if ((c >= 0 && c <= 0x1f) || c == 0x7f) |
511 | | continue; /* Remove control characters. */ |
512 | | if (!(i < n-1)) |
513 | | { |
514 | | n += 50; |
515 | | buf = xrealloc (buf, n); |
516 | | } |
517 | | buf[i++] = c; |
518 | | } |
519 | | } |
520 | | xfree (utf8buf); |
521 | | |
522 | | if (hidden) |
523 | | SetConsoleMode(con.in, DEF_INPMODE ); |
524 | | |
525 | | #else /* Unix */ |
526 | |
|
527 | 0 | if (hidden) |
528 | 0 | { |
529 | 0 | #ifdef HAVE_TCGETATTR |
530 | 0 | struct termios term; |
531 | |
|
532 | 0 | if (tcgetattr(fileno(ttyfp), &termsave)) |
533 | 0 | log_fatal ("tcgetattr() failed: %s\n", strerror(errno)); |
534 | 0 | restore_termios = 1; |
535 | 0 | term = termsave; |
536 | 0 | term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); |
537 | 0 | if (tcsetattr( fileno(ttyfp), TCSAFLUSH, &term ) ) |
538 | 0 | log_fatal("tcsetattr() failed: %s\n", strerror(errno)); |
539 | 0 | #endif /*HAVE_TCGETATTR*/ |
540 | 0 | } |
541 | | |
542 | | /* fixme: How can we avoid that the \n is echoed w/o disabling |
543 | | * canonical mode - w/o this kill_prompt can't work */ |
544 | 0 | while (read(fileno(ttyfp), cbuf, 1) == 1 && *cbuf != '\n') |
545 | 0 | { |
546 | 0 | if (!hidden) |
547 | 0 | last_prompt_len++; |
548 | 0 | c = *cbuf; |
549 | 0 | if (c == CONTROL_D) |
550 | 0 | log_info (_("Control-D detected\n")); |
551 | |
|
552 | 0 | if (c == '\t') /* Map tab to a space. */ |
553 | 0 | c = ' '; |
554 | 0 | else if ( (c >= 0 && c <= 0x1f) || c == 0x7f) |
555 | 0 | continue; /* Skip all other ASCII control characters. */ |
556 | 0 | if (!(i < n-1)) |
557 | 0 | { |
558 | 0 | n += 50; |
559 | 0 | buf = xrealloc (buf, n); |
560 | 0 | } |
561 | 0 | buf[i++] = c; |
562 | 0 | } |
563 | 0 | if (*cbuf != '\n') |
564 | 0 | { |
565 | 0 | buf[0] = CONTROL_D; |
566 | 0 | i = 1; |
567 | 0 | } |
568 | |
|
569 | 0 | if (hidden) |
570 | 0 | { |
571 | 0 | #ifdef HAVE_TCGETATTR |
572 | 0 | if (tcsetattr (fileno(ttyfp), TCSAFLUSH, &termsave)) |
573 | 0 | log_error ("tcsetattr() failed: %s\n", strerror(errno)); |
574 | 0 | restore_termios = 0; |
575 | 0 | #endif /*HAVE_TCGETATTR*/ |
576 | 0 | } |
577 | 0 | #endif /* Unix */ |
578 | |
|
579 | 0 | buf[i] = 0; |
580 | 0 | return buf; |
581 | 0 | } |
582 | | |
583 | | |
584 | | |
585 | | /* Note: This function never returns NULL. */ |
586 | | char * |
587 | | tty_get( const char *prompt ) |
588 | 0 | { |
589 | 0 | if (!batchmode && !no_terminal && my_rl_readline && my_rl_add_history) |
590 | 0 | { |
591 | 0 | char *line; |
592 | 0 | char *buf; |
593 | |
|
594 | 0 | if (!initialized) |
595 | 0 | init_ttyfp(); |
596 | |
|
597 | 0 | last_prompt_len = 0; |
598 | |
|
599 | 0 | line = my_rl_readline (prompt?prompt:""); |
600 | | |
601 | | /* We need to copy it to memory controlled by our malloc |
602 | | implementations; further we need to convert an EOF to our |
603 | | convention. */ |
604 | 0 | buf = xmalloc(line? strlen(line)+1:2); |
605 | 0 | if (line) |
606 | 0 | { |
607 | 0 | strcpy (buf, line); |
608 | 0 | trim_spaces (buf); |
609 | 0 | if (strlen (buf) > 2 ) |
610 | 0 | my_rl_add_history (line); /* Note that we test BUF but add LINE. */ |
611 | 0 | free (line); |
612 | 0 | } |
613 | 0 | else |
614 | 0 | { |
615 | 0 | buf[0] = CONTROL_D; |
616 | 0 | buf[1] = 0; |
617 | 0 | } |
618 | 0 | return buf; |
619 | 0 | } |
620 | 0 | else |
621 | 0 | return do_get ( prompt, 0 ); |
622 | 0 | } |
623 | | |
624 | | |
625 | | /* Variable argument version of tty_get. The prompt is actually a |
626 | | * format string with arguments. */ |
627 | | char * |
628 | | tty_getf (const char *promptfmt, ... ) |
629 | 0 | { |
630 | 0 | va_list arg_ptr; |
631 | 0 | char *prompt; |
632 | 0 | char *answer; |
633 | |
|
634 | 0 | va_start (arg_ptr, promptfmt); |
635 | 0 | if (gpgrt_vasprintf (&prompt, promptfmt, arg_ptr) < 0) |
636 | 0 | log_fatal ("estream_vasprintf failed: %s\n", strerror (errno)); |
637 | 0 | va_end (arg_ptr); |
638 | 0 | answer = tty_get (prompt); |
639 | 0 | xfree (prompt); |
640 | 0 | return answer; |
641 | 0 | } |
642 | | |
643 | | |
644 | | char * |
645 | | tty_get_hidden( const char *prompt ) |
646 | 0 | { |
647 | 0 | return do_get (prompt, 1); |
648 | 0 | } |
649 | | |
650 | | |
651 | | void |
652 | | tty_kill_prompt (void) |
653 | 0 | { |
654 | 0 | if (no_terminal) |
655 | 0 | return; |
656 | | |
657 | 0 | if (!initialized) |
658 | 0 | init_ttyfp (); |
659 | |
|
660 | 0 | if (batchmode) |
661 | 0 | last_prompt_len = 0; |
662 | 0 | if (!last_prompt_len) |
663 | 0 | return; |
664 | | #ifdef HAVE_W32_SYSTEM |
665 | | tty_printf ("\r%*s\r", last_prompt_len, ""); |
666 | | #else /* Unix */ |
667 | 0 | { |
668 | 0 | int i; |
669 | 0 | putc ('\r', ttyfp); |
670 | 0 | for (i=0; i < last_prompt_len; i ++ ) |
671 | 0 | putc (' ', ttyfp); |
672 | 0 | putc ('\r', ttyfp); |
673 | 0 | fflush (ttyfp); |
674 | 0 | } |
675 | 0 | #endif /* Unix */ |
676 | 0 | last_prompt_len = 0; |
677 | 0 | } |
678 | | |
679 | | |
680 | | int |
681 | | tty_get_answer_is_yes( const char *prompt ) |
682 | 0 | { |
683 | 0 | int yes; |
684 | 0 | char *p; |
685 | |
|
686 | 0 | p = tty_get (prompt); |
687 | 0 | tty_kill_prompt (); |
688 | 0 | yes = answer_is_yes (p); |
689 | 0 | xfree (p); |
690 | |
|
691 | 0 | return yes; |
692 | 0 | } |
693 | | |
694 | | |
695 | | /* Called by gnupg_rl_initialize to setup the readline support. */ |
696 | | void |
697 | | tty_private_set_rl_hooks (void (*init_stream) (FILE *), |
698 | | void (*set_completer) (rl_completion_func_t*), |
699 | | void (*inhibit_completion) (int), |
700 | | void (*cleanup_after_signal) (void), |
701 | | char *(*readline_fun) (const char*), |
702 | | void (*add_history_fun) (const char*), |
703 | | int (*rw_history_fun)(const char *, int, int)) |
704 | 0 | { |
705 | 0 | my_rl_init_stream = init_stream; |
706 | 0 | my_rl_set_completer = set_completer; |
707 | 0 | my_rl_inhibit_completion = inhibit_completion; |
708 | 0 | my_rl_cleanup_after_signal = cleanup_after_signal; |
709 | 0 | my_rl_readline = readline_fun; |
710 | 0 | my_rl_add_history = add_history_fun; |
711 | 0 | my_rl_rw_history = rw_history_fun; |
712 | 0 | } |
713 | | |
714 | | |
715 | | /* Read the history from FILENAME or limit the size of the history. |
716 | | * If FILENAME is NULL and NLINES is zero the current history is |
717 | | * cleared. Returns 0 on success or -1 on error and sets ERRNO. No |
718 | | * error is return if readline support is not available. */ |
719 | | int |
720 | | tty_read_history (const char *filename, int nlines) |
721 | 0 | { |
722 | 0 | int rc; |
723 | |
|
724 | 0 | if (!my_rl_rw_history) |
725 | 0 | return 0; |
726 | | |
727 | 0 | rc = my_rl_rw_history (filename, 0, nlines); |
728 | 0 | if (rc && gpg_err_code_from_syserror () == GPG_ERR_ENOENT) |
729 | 0 | rc = 0; |
730 | |
|
731 | 0 | return rc; |
732 | 0 | } |
733 | | |
734 | | |
735 | | /* Write the current history to the file FILENAME. Returns 0 on |
736 | | * success or -1 on error and sets ERRNO. No error is return if |
737 | | * readline support is not available. */ |
738 | | int |
739 | | tty_write_history (const char *filename) |
740 | 0 | { |
741 | 0 | if (!my_rl_rw_history) |
742 | 0 | return 0; |
743 | | |
744 | 0 | return my_rl_rw_history (filename, 1, 0); |
745 | 0 | } |
746 | | |
747 | | |
748 | | #ifdef HAVE_LIBREADLINE |
749 | | void |
750 | | tty_enable_completion (rl_completion_func_t *completer) |
751 | | { |
752 | | if (no_terminal || !my_rl_set_completer ) |
753 | | return; |
754 | | |
755 | | if (!initialized) |
756 | | init_ttyfp(); |
757 | | |
758 | | my_rl_set_completer (completer); |
759 | | } |
760 | | |
761 | | void |
762 | | tty_disable_completion (void) |
763 | | { |
764 | | if (no_terminal || !my_rl_inhibit_completion) |
765 | | return; |
766 | | |
767 | | if (!initialized) |
768 | | init_ttyfp(); |
769 | | |
770 | | my_rl_inhibit_completion (1); |
771 | | } |
772 | | #endif /* HAVE_LIBREADLINE */ |
773 | | |
774 | | void |
775 | | tty_cleanup_after_signal (void) |
776 | 0 | { |
777 | 0 | #ifdef HAVE_TCGETATTR |
778 | 0 | cleanup (); |
779 | 0 | #endif |
780 | 0 | } |
781 | | |
782 | | void |
783 | | tty_cleanup_rl_after_signal (void) |
784 | 0 | { |
785 | 0 | if (my_rl_cleanup_after_signal) |
786 | 0 | my_rl_cleanup_after_signal (); |
787 | 0 | } |