39
41
#include "private/svn_cmdline_private.h"
40
42
#include "svn_private_config.h"
46
#elif defined(HAVE_TERMIOS_H)
44
/* Wait for input on @a *f. Doing all allocations
45
* in @a pool. This functions is based on apr_wait_for_io_or_timeout().
46
* Note that this will return an EINTR on a signal.
48
* ### FIX: When APR gives us a better way of doing this use it. */
49
static apr_status_t wait_for_input(apr_file_t *f,
56
pollset.desc_type = APR_POLL_FILE;
59
pollset.reqevents = APR_POLLIN;
61
srv = apr_poll(&pollset, 1, &n, -1);
63
if (n == 1 && pollset.rtnevents & APR_POLLIN)
68
/* APR specs say things that are unimplemented are supposed to return
69
* APR_ENOTIMPL. But when trying to use APR_POLL_FILE with apr_poll
70
* on Windows it returns APR_EBADF instead. So just return APR_ENOTIMPL
53
/* Descriptor of an open terminal */
54
typedef struct terminal_handle_t terminal_handle_t;
55
struct terminal_handle_t
57
apr_file_t *infd; /* input file handle */
58
apr_file_t *outfd; /* output file handle */
59
svn_boolean_t noecho; /* terminal echo was turned off */
60
svn_boolean_t close_handles; /* close handles when closing the terminal */
61
apr_pool_t *pool; /* pool associated with the file handles */
64
svn_boolean_t restore_state; /* terminal state was changed */
65
apr_os_file_t osinfd; /* OS-specific handle for infd */
66
struct termios attr; /* saved terminal attributes */
70
/* Initialize safe state of terminal_handle_t. */
72
terminal_handle_init(terminal_handle_t *terminal,
73
apr_file_t *infd, apr_file_t *outfd,
74
svn_boolean_t noecho, svn_boolean_t close_handles,
77
memset(terminal, 0, sizeof(*terminal));
78
terminal->infd = infd;
79
terminal->outfd = outfd;
80
terminal->noecho = noecho;
81
terminal->close_handles = close_handles;
82
terminal->pool = pool;
86
* Common pool cleanup handler for terminal_handle_t. Closes TERMINAL.
87
* If CLOSE_HANDLES is TRUE, close the terminal file handles.
88
* If RESTORE_STATE is TRUE, restores the TERMIOS flags of the terminal.
91
terminal_cleanup_handler(terminal_handle_t *terminal,
92
svn_boolean_t close_handles,
93
svn_boolean_t restore_state)
95
apr_status_t status = APR_SUCCESS;
98
/* Restore terminal state flags. */
99
if (restore_state && terminal->restore_state)
100
tcsetattr(terminal->osinfd, TCSANOW, &terminal->attr);
103
/* Close terminal handles. */
104
if (close_handles && terminal->close_handles)
106
apr_file_t *const infd = terminal->infd;
107
apr_file_t *const outfd = terminal->outfd;
111
terminal->infd = NULL;
112
status = apr_file_close(infd);
115
if (!status && outfd && outfd != infd)
117
terminal->outfd = NULL;
118
status = apr_file_close(terminal->outfd);
124
/* Normal pool cleanup for a terminal. */
125
static apr_status_t terminal_plain_cleanup(void *baton)
127
return terminal_cleanup_handler(baton, FALSE, TRUE);
130
/* Child pool cleanup for a terminal -- does not restore echo state. */
131
static apr_status_t terminal_child_cleanup(void *baton)
133
return terminal_cleanup_handler(baton, FALSE, FALSE);
136
/* Explicitly close the terminal, removing its cleanup handlers. */
138
terminal_close(terminal_handle_t *terminal)
142
/* apr_pool_cleanup_kill() removes both normal and child cleanup */
143
apr_pool_cleanup_kill(terminal->pool, terminal, terminal_plain_cleanup);
145
status = terminal_cleanup_handler(terminal, TRUE, TRUE);
147
return svn_error_create(status, NULL, _("Can't close terminal"));
151
/* Allocate and open *TERMINAL. If NOECHO is TRUE, try to turn off
152
terminal echo. Use POOL for all allocations.*/
154
terminal_open(terminal_handle_t **terminal, svn_boolean_t noecho,
160
/* On Windows, we'll use the console API directly if the process has
161
a console attached; otherwise we'll just use stdin and stderr. */
162
const HANDLE conin = CreateFileW(L"CONIN$", GENERIC_READ,
163
FILE_SHARE_READ | FILE_SHARE_WRITE,
165
FILE_ATTRIBUTE_NORMAL, NULL);
166
*terminal = apr_palloc(pool, sizeof(terminal_handle_t));
167
if (conin != INVALID_HANDLE_VALUE)
169
/* The process has a console. */
171
terminal_handle_init(*terminal, NULL, NULL, noecho, FALSE, NULL);
175
/* Without evidence to the contrary, we'll assume this is *nix and
176
try to open /dev/tty. If that fails, we'll use stdin for input
177
and stderr for prompting. */
179
status = apr_file_open(&tmpfd, "/dev/tty",
180
APR_FOPEN_READ | APR_FOPEN_WRITE,
181
APR_OS_DEFAULT, pool);
182
*terminal = apr_palloc(pool, sizeof(terminal_handle_t));
185
/* We have a terminal handle that we can use for input and output. */
186
terminal_handle_init(*terminal, tmpfd, tmpfd, FALSE, TRUE, pool);
191
/* There is no terminal. Sigh. */
195
status = apr_file_open_stdin(&infd, pool);
197
return svn_error_wrap_apr(status, _("Can't open stdin"));
198
status = apr_file_open_stderr(&outfd, pool);
200
return svn_error_wrap_apr(status, _("Can't open stderr"));
201
terminal_handle_init(*terminal, infd, outfd, FALSE, FALSE, pool);
204
#ifdef HAVE_TERMIOS_H
205
/* Set terminal state */
206
if (0 == apr_os_file_get(&(*terminal)->osinfd, (*terminal)->infd))
208
if (0 == tcgetattr((*terminal)->osinfd, &(*terminal)->attr))
210
struct termios attr = (*terminal)->attr;
211
/* Turn off signal handling and canonical input mode */
212
attr.c_lflag &= ~(ISIG | ICANON);
213
attr.c_cc[VMIN] = 1; /* Read one byte at a time */
214
attr.c_cc[VTIME] = 0; /* No timeout, wait indefinitely */
215
attr.c_lflag &= ~(ECHO); /* Turn off echo */
216
if (0 == tcsetattr((*terminal)->osinfd, TCSAFLUSH, &attr))
218
(*terminal)->noecho = noecho;
219
(*terminal)->restore_state = TRUE;
223
#endif /* HAVE_TERMIOS_H */
225
/* Register pool cleanup to close handles and restore echo state. */
226
apr_pool_cleanup_register((*terminal)->pool, *terminal,
227
terminal_plain_cleanup,
228
terminal_child_cleanup);
232
/* Write a null-terminated STRING to TERMINAL.
233
Use POOL for allocations related to converting STRING from UTF-8. */
235
terminal_puts(const char *string, terminal_handle_t *terminal,
240
const char *converted;
242
err = svn_cmdline_cstring_from_utf8(&converted, string, pool);
245
svn_error_clear(err);
246
converted = svn_cmdline_cstring_from_utf8_fuzzy(string, pool);
250
if (!terminal->outfd)
252
/* See terminal_open; we're using Console I/O. */
258
status = apr_file_write_full(terminal->outfd, converted,
259
strlen(converted), NULL);
261
status = apr_file_flush(terminal->outfd);
263
return svn_error_wrap_apr(status, _("Can't write to terminal"));
267
/* These codes can be returned from terminal_getc instead of a character. */
268
#define TERMINAL_NONE 0x80000 /* no character read, retry */
269
#define TERMINAL_DEL (TERMINAL_NONE + 1) /* the input was a deleteion */
270
#define TERMINAL_EOL (TERMINAL_NONE + 2) /* end of input/end of line */
271
#define TERMINAL_EOF (TERMINAL_NONE + 3) /* end of file during input */
273
/* Helper for terminal_getc: writes CH to OUTFD as a control char. */
276
echo_control_char(char ch, apr_file_t *outfd)
278
if (svn_ctype_iscntrl(ch))
280
const char substitute = (ch < 32? '@' + ch : '?');
281
apr_file_putc('^', outfd);
282
apr_file_putc(substitute, outfd);
284
else if (svn_ctype_isprint(ch))
286
/* Pass printable characters unchanged. */
287
apr_file_putc(ch, outfd);
291
/* Everything else is strange. */
292
apr_file_putc('^', outfd);
293
apr_file_putc('!', outfd);
298
/* Read one character or control code from TERMINAL, returning it in CODE.
299
if CAN_ERASE and the input was a deletion, emit codes to erase the
300
last character displayed on the terminal.
301
Use POOL for all allocations. */
303
terminal_getc(int *code, terminal_handle_t *terminal,
304
svn_boolean_t can_erase, apr_pool_t *pool)
306
const svn_boolean_t echo = !terminal->noecho;
307
apr_status_t status = APR_SUCCESS;
313
/* See terminal_open; we're using Console I/O. */
315
/* The following was hoisted from APR's getpass for Windows. */
316
int concode = _getch();
319
case '\r': /* end-of-line */
320
*code = TERMINAL_EOL;
325
case EOF: /* end-of-file */
326
case 26: /* Ctrl+Z */
327
*code = TERMINAL_EOF;
329
_cputs((concode == EOF ? "[EOF]\r\n" : "^Z\r\n"));
332
case 3: /* Ctrl+C, Ctrl+Break */
333
/* _getch() bypasses Ctrl+C but not Ctrl+Break detection! */
336
return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL);
338
case 0: /* Function code prefix */
340
concode = (concode << 4) | _getch();
341
/* Catch {DELETE}, {<--}, Num{DEL} and Num{<--} */
342
if (concode == 0xE53 || concode == 0xE4B
343
|| concode == 0x053 || concode == 0x04B)
345
*code = TERMINAL_DEL;
351
*code = TERMINAL_NONE;
358
*code = TERMINAL_DEL;
364
if (!apr_iscntrl(concode))
366
*code = (int)(unsigned char)concode;
367
_putch(echo ? concode : '*');
371
*code = TERMINAL_NONE;
377
#elif defined(HAVE_TERMIOS_H)
378
if (terminal->restore_state)
380
/* We're using a bytewise-immediate termios input */
381
const struct termios *const attr = &terminal->attr;
383
status = apr_file_getc(&ch, terminal->infd);
385
return svn_error_wrap_apr(status, _("Can't read from terminal"));
387
if (ch == attr->c_cc[VINTR] || ch == attr->c_cc[VQUIT])
390
echo_control_char(ch, terminal->outfd);
391
return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL);
393
else if (ch == '\r' || ch == '\n' || ch == attr->c_cc[VEOL])
396
*code = TERMINAL_EOL;
397
apr_file_putc('\n', terminal->outfd);
399
else if (ch == '\b' || ch == attr->c_cc[VERASE])
402
*code = TERMINAL_DEL;
405
apr_file_putc('\b', terminal->outfd);
406
apr_file_putc(' ', terminal->outfd);
407
apr_file_putc('\b', terminal->outfd);
410
else if (ch == attr->c_cc[VEOF])
413
*code = TERMINAL_EOF;
414
echo_control_char(ch, terminal->outfd);
416
else if (ch == attr->c_cc[VSUSP])
419
*code = TERMINAL_NONE;
422
else if (!apr_iscntrl(ch))
424
/* Normal character */
425
*code = (int)(unsigned char)ch;
426
apr_file_putc((echo ? ch : '*'), terminal->outfd);
430
/* Ignored character */
431
*code = TERMINAL_NONE;
432
apr_file_putc('\a', terminal->outfd);
436
#endif /* HAVE_TERMIOS_H */
438
/* Fall back to plain stream-based I/O. */
440
/* Wait for input on termin. This code is based on
441
apr_wait_for_io_or_timeout().
442
Note that this will return an EINTR on a signal. */
444
apr_pollfd_t pollset;
447
pollset.desc_type = APR_POLL_FILE;
448
pollset.desc.f = terminal->infd;
450
pollset.reqevents = APR_POLLIN;
452
status = apr_poll(&pollset, 1, &n, -1);
454
if (n == 1 && pollset.rtnevents & APR_POLLIN)
455
status = APR_SUCCESS;
460
status = apr_file_getc(&ch, terminal->infd);
461
if (APR_STATUS_IS_EINTR(status))
463
*code = TERMINAL_NONE;
466
else if (APR_STATUS_IS_EOF(status))
468
*code = TERMINAL_EOF;
472
return svn_error_wrap_apr(status, _("Can't read from terminal"));
474
*code = (int)(unsigned char)ch;
77
479
/* Set @a *result to the result of prompting the user with @a
78
480
* prompt_msg. Use @ *pb to get the cancel_func and cancel_baton.
91
493
/* XXX: If this functions ever starts using members of *pb
92
494
* which were not included in svn_cmdline_prompt_baton_t,
93
495
* we need to update svn_cmdline_prompt_user2 and its callers. */
497
svn_boolean_t saw_first_half_of_eol = FALSE;
498
svn_stringbuf_t *strbuf = svn_stringbuf_create_empty(pool);
499
terminal_handle_t *terminal;
98
svn_stringbuf_t *strbuf = svn_stringbuf_create("", pool);
100
status = apr_file_open_stdin(&fp, pool);
102
return svn_error_wrap_apr(status, _("Can't open stdin"));
106
svn_boolean_t saw_first_half_of_eol = FALSE;
107
SVN_ERR(svn_cmdline_fputs(prompt_msg, stderr, pool));
112
/* Hack to allow us to not block for io on the prompt, so
115
SVN_ERR(pb->cancel_func(pb->cancel_baton));
116
status = wait_for_input(fp, pool);
117
if (APR_STATUS_IS_EINTR(status))
119
else if (status && status != APR_ENOTIMPL)
120
return svn_error_wrap_apr(status, _("Can't read stdin"));
122
status = apr_file_getc(&c, fp);
124
return svn_error_wrap_apr(status, _("Can't read stdin"));
126
if (saw_first_half_of_eol)
128
if (c == APR_EOL_STR[1])
131
saw_first_half_of_eol = FALSE;
133
else if (c == APR_EOL_STR[0])
135
/* GCC might complain here: "warning: will never be executed"
136
* That's fine. This is a compile-time check for "\r\n\0" */
137
if (sizeof(APR_EOL_STR) == 3)
139
saw_first_half_of_eol = TRUE;
142
else if (sizeof(APR_EOL_STR) == 2)
145
/* ### APR_EOL_STR holds more than two chars? Who
146
ever heard of such a thing? */
147
SVN_ERR_MALFUNCTION();
150
svn_stringbuf_appendbyte(strbuf, c);
155
const char *prompt_stdout;
156
size_t bufsize = 300;
157
SVN_ERR(svn_cmdline_cstring_from_utf8(&prompt_stdout, prompt_msg,
159
svn_stringbuf_ensure(strbuf, bufsize);
161
status = apr_password_get(prompt_stdout, strbuf->data, &bufsize);
163
return svn_error_wrap_apr(status, _("Can't get password"));
503
SVN_ERR(terminal_open(&terminal, hide, pool));
504
SVN_ERR(terminal_puts(prompt_msg, terminal, pool));
508
SVN_ERR(terminal_getc(&code, terminal, (strbuf->len > 0), pool));
510
/* Check for cancellation after a character has been read, some
511
input processing modes may eat ^C and we'll only notice a
512
cancellation signal after characters have been read --
513
sometimes even after a newline. */
515
SVN_ERR(pb->cancel_func(pb->cancel_baton));
520
/* Nothing useful happened; retry. */
524
/* Delete the last input character. terminal_getc takes care
525
of erasing the feedback from the terminal, if applicable. */
526
svn_stringbuf_chop(strbuf, 1);
530
/* End-of-line means end of input. Trick the EOL-detection code
531
below to stop reading. */
532
saw_first_half_of_eol = TRUE;
533
c = APR_EOL_STR[1]; /* Could be \0 but still stops reading. */
537
return svn_error_create(
539
terminal_close(terminal),
540
_("End of file while reading from terminal"));
543
/* Convert the returned code back to the character. */
547
if (saw_first_half_of_eol)
549
if (c == APR_EOL_STR[1])
552
saw_first_half_of_eol = FALSE;
554
else if (c == APR_EOL_STR[0])
556
/* GCC might complain here: "warning: will never be executed"
557
* That's fine. This is a compile-time check for "\r\n\0" */
558
if (sizeof(APR_EOL_STR) == 3)
560
saw_first_half_of_eol = TRUE;
563
else if (sizeof(APR_EOL_STR) == 2)
566
/* ### APR_EOL_STR holds more than two chars? Who
567
ever heard of such a thing? */
568
SVN_ERR_MALFUNCTION();
571
svn_stringbuf_appendbyte(strbuf, c);
574
if (terminal->noecho)
576
/* If terminal echo was turned off, make sure future output
577
to the terminal starts on a new line, as expected. */
578
SVN_ERR(terminal_puts(APR_EOL_STR, terminal, pool));
580
SVN_ERR(terminal_close(terminal));
166
582
return svn_cmdline_cstring_to_utf8(result, strbuf->data, pool);