Line | Count | Source |
1 | | /* status.c - Status message and command-fd interface |
2 | | * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, |
3 | | * 2004, 2005, 2006, 2010 Free Software Foundation, Inc. |
4 | | * |
5 | | * This file is part of GnuPG. |
6 | | * |
7 | | * GnuPG is free software; you can redistribute it and/or modify |
8 | | * it under the terms of the GNU General Public License as published by |
9 | | * the Free Software Foundation; either version 3 of the License, or |
10 | | * (at your option) any later version. |
11 | | * |
12 | | * GnuPG is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public License |
18 | | * along with this program; if not, see <https://www.gnu.org/licenses/>. |
19 | | */ |
20 | | |
21 | | #include <config.h> |
22 | | #include <stdio.h> |
23 | | #include <stdlib.h> |
24 | | #include <string.h> |
25 | | #include <errno.h> |
26 | | #include <unistd.h> |
27 | | #ifdef HAVE_SIGNAL_H |
28 | | # include <signal.h> |
29 | | #endif |
30 | | |
31 | | #include "gpg.h" |
32 | | #include "../common/util.h" |
33 | | #include "../common/status.h" |
34 | | #include "../common/ttyio.h" |
35 | | #include "options.h" |
36 | | #include "main.h" |
37 | | #include "../common/i18n.h" |
38 | | |
39 | 0 | #define CONTROL_D ('D' - 'A' + 1) |
40 | | |
41 | | |
42 | | /* The stream to output the status information. Output is disabled if |
43 | | this is NULL. */ |
44 | | static estream_t statusfp; |
45 | | |
46 | | |
47 | | static void |
48 | | progress_cb (void *ctx, const char *what, int printchar, |
49 | | int current, int total) |
50 | 0 | { |
51 | 0 | char buf[50]; |
52 | |
|
53 | 0 | (void)ctx; |
54 | |
|
55 | 0 | if ( printchar == '\n' && !strcmp (what, "primegen") ) |
56 | 0 | snprintf (buf, sizeof buf, "%.20s X 100 100", what ); |
57 | 0 | else |
58 | 0 | snprintf (buf, sizeof buf, "%.20s %c %d %d", |
59 | 0 | what, printchar=='\n'?'X':printchar, current, total ); |
60 | 0 | write_status_text (STATUS_PROGRESS, buf); |
61 | 0 | } |
62 | | |
63 | | |
64 | | /* Return true if the status message NO may currently be issued. We |
65 | | need this to avoid synchronization problem while auto retrieving a |
66 | | key. There it may happen that a status NODATA is issued for a non |
67 | | available key and the user may falsely interpret this has a missing |
68 | | signature. */ |
69 | | static int |
70 | | status_currently_allowed (int no) |
71 | 0 | { |
72 | 0 | if (!glo_ctrl.in_auto_key_retrieve) |
73 | 0 | return 1; /* Yes. */ |
74 | | |
75 | | /* We allow some statis anyway, so that import statistics are |
76 | | correct and to avoid problems if the retrieval subsystem will |
77 | | prompt the user. */ |
78 | 0 | switch (no) |
79 | 0 | { |
80 | 0 | case STATUS_GET_BOOL: |
81 | 0 | case STATUS_GET_LINE: |
82 | 0 | case STATUS_GET_HIDDEN: |
83 | 0 | case STATUS_GOT_IT: |
84 | 0 | case STATUS_IMPORTED: |
85 | 0 | case STATUS_IMPORT_OK: |
86 | 0 | case STATUS_IMPORT_CHECK: |
87 | 0 | case STATUS_IMPORT_RES: |
88 | 0 | return 1; /* Yes. */ |
89 | 0 | default: |
90 | 0 | break; |
91 | 0 | } |
92 | 0 | return 0; /* No. */ |
93 | 0 | } |
94 | | |
95 | | |
96 | | void |
97 | | set_status_fd (int fd) |
98 | 0 | { |
99 | 0 | static int last_fd = -1; |
100 | |
|
101 | 0 | if (fd != -1 && last_fd == fd) |
102 | 0 | return; |
103 | | |
104 | 0 | if (statusfp && statusfp != es_stdout && statusfp != es_stderr ) |
105 | 0 | es_fclose (statusfp); |
106 | 0 | statusfp = NULL; |
107 | 0 | if (fd == -1) |
108 | 0 | return; |
109 | | |
110 | 0 | if (! gnupg_fd_valid (fd)) |
111 | 0 | log_fatal ("status-fd is invalid: %s\n", strerror (errno)); |
112 | | |
113 | 0 | if (fd == 1) |
114 | 0 | statusfp = es_stdout; |
115 | 0 | else if (fd == 2) |
116 | 0 | statusfp = es_stderr; |
117 | 0 | else |
118 | 0 | statusfp = es_fdopen (fd, "w"); |
119 | 0 | if (!statusfp) |
120 | 0 | { |
121 | 0 | log_fatal ("can't open fd %d for status output: %s\n", |
122 | 0 | fd, strerror (errno)); |
123 | 0 | } |
124 | 0 | last_fd = fd; |
125 | |
|
126 | 0 | gcry_set_progress_handler (progress_cb, NULL); |
127 | 0 | } |
128 | | |
129 | | |
130 | | int |
131 | | is_status_enabled (void) |
132 | 0 | { |
133 | 0 | return !!statusfp; |
134 | 0 | } |
135 | | |
136 | | |
137 | | void |
138 | | write_status ( int no ) |
139 | 8.40k | { |
140 | 8.40k | write_status_text( no, NULL ); |
141 | 8.40k | } |
142 | | |
143 | | |
144 | | /* Write a status line with code NO followed by the string TEXT and |
145 | | * directly followed by the remaining strings up to a NULL. Embedded |
146 | | * CR and LFs in the strings (but not in TEXT) are C-style escaped.*/ |
147 | | void |
148 | | write_status_strings (int no, const char *text, ...) |
149 | 748k | { |
150 | 748k | va_list arg_ptr; |
151 | 748k | const char *s; |
152 | | |
153 | 748k | if (!statusfp || !status_currently_allowed (no) ) |
154 | 748k | return; /* Not enabled or allowed. */ |
155 | | |
156 | 748k | es_fputs ("[GNUPG:] ", statusfp); |
157 | 0 | es_fputs (get_status_string (no), statusfp); |
158 | 0 | if ( text ) |
159 | 0 | { |
160 | 0 | es_putc ( ' ', statusfp); |
161 | 0 | va_start (arg_ptr, text); |
162 | 0 | s = text; |
163 | 0 | do |
164 | 0 | { |
165 | 0 | for (; *s; s++) |
166 | 0 | { |
167 | 0 | if (*s == '\n') |
168 | 0 | es_fputs ("\\n", statusfp); |
169 | 0 | else if (*s == '\r') |
170 | 0 | es_fputs ("\\r", statusfp); |
171 | 0 | else |
172 | 0 | es_fputc (*(const byte *)s, statusfp); |
173 | 0 | } |
174 | 0 | } |
175 | 0 | while ((s = va_arg (arg_ptr, const char*))); |
176 | 0 | va_end (arg_ptr); |
177 | 0 | } |
178 | 0 | es_putc ('\n', statusfp); |
179 | 0 | if (es_fflush (statusfp) && opt.exit_on_status_write_error) |
180 | 0 | g10_exit (0); |
181 | 0 | } |
182 | | |
183 | | |
184 | | /* Write a status line with code NO followed by the remaining |
185 | | * arguments which must be a list of strings terminated by a NULL. |
186 | | * Embedded CR and LFs in the strings are C-style escaped. All |
187 | | * strings are printed with a space as delimiter. */ |
188 | | gpg_error_t |
189 | | write_status_strings2 (ctrl_t dummy, int no, ...) |
190 | 0 | { |
191 | 0 | va_list arg_ptr; |
192 | 0 | const char *s; |
193 | |
|
194 | 0 | (void)dummy; |
195 | |
|
196 | 0 | if (!statusfp || !status_currently_allowed (no) ) |
197 | 0 | return 0; /* Not enabled or allowed. */ |
198 | | |
199 | 0 | va_start (arg_ptr, no); |
200 | |
|
201 | 0 | es_fputs ("[GNUPG:] ", statusfp); |
202 | 0 | es_fputs (get_status_string (no), statusfp); |
203 | 0 | while ((s = va_arg (arg_ptr, const char*))) |
204 | 0 | { |
205 | 0 | if (*s) |
206 | 0 | es_putc (' ', statusfp); |
207 | 0 | for (; *s; s++) |
208 | 0 | { |
209 | 0 | if (*s == '\n') |
210 | 0 | es_fputs ("\\n", statusfp); |
211 | 0 | else if (*s == '\r') |
212 | 0 | es_fputs ("\\r", statusfp); |
213 | 0 | else |
214 | 0 | es_fputc (*(const byte *)s, statusfp); |
215 | 0 | } |
216 | 0 | } |
217 | 0 | es_putc ('\n', statusfp); |
218 | |
|
219 | 0 | va_end (arg_ptr); |
220 | |
|
221 | 0 | if (es_fflush (statusfp) && opt.exit_on_status_write_error) |
222 | 0 | g10_exit (0); |
223 | | |
224 | 0 | return 0; |
225 | 0 | } |
226 | | |
227 | | |
228 | | void |
229 | | write_status_text (int no, const char *text) |
230 | 748k | { |
231 | 748k | write_status_strings (no, text, NULL); |
232 | 748k | } |
233 | | |
234 | | |
235 | | /* Write a status line with code NO followed by the output of the |
236 | | * printf style FORMAT. Embedded CR and LFs are C-style escaped. */ |
237 | | void |
238 | | write_status_printf (int no, const char *format, ...) |
239 | 127k | { |
240 | 127k | va_list arg_ptr; |
241 | 127k | char *buf; |
242 | | |
243 | 127k | if (!statusfp || !status_currently_allowed (no) ) |
244 | 127k | return; /* Not enabled or allowed. */ |
245 | | |
246 | 127k | es_fputs ("[GNUPG:] ", statusfp); |
247 | 0 | es_fputs (get_status_string (no), statusfp); |
248 | 0 | if (format) |
249 | 0 | { |
250 | 0 | es_putc ( ' ', statusfp); |
251 | 0 | va_start (arg_ptr, format); |
252 | 0 | buf = gpgrt_vbsprintf (format, arg_ptr); |
253 | 0 | if (!buf) |
254 | 0 | log_error ("error printing status line: %s\n", |
255 | 0 | gpg_strerror (gpg_err_code_from_syserror ())); |
256 | 0 | else |
257 | 0 | { |
258 | 0 | if (strpbrk (buf, "\r\n")) |
259 | 0 | { |
260 | 0 | const byte *s; |
261 | 0 | for (s=buf; *s; s++) |
262 | 0 | { |
263 | 0 | if (*s == '\n') |
264 | 0 | es_fputs ("\\n", statusfp); |
265 | 0 | else if (*s == '\r') |
266 | 0 | es_fputs ("\\r", statusfp); |
267 | 0 | else |
268 | 0 | es_fputc (*s, statusfp); |
269 | 0 | } |
270 | 0 | } |
271 | 0 | else |
272 | 0 | es_fputs (buf, statusfp); |
273 | 0 | gpgrt_free (buf); |
274 | 0 | } |
275 | |
|
276 | 0 | va_end (arg_ptr); |
277 | 0 | } |
278 | 0 | es_putc ('\n', statusfp); |
279 | 0 | if (es_fflush (statusfp) && opt.exit_on_status_write_error) |
280 | 0 | g10_exit (0); |
281 | 0 | } |
282 | | |
283 | | /* Write a WARNING status line using a full gpg-error error value. */ |
284 | | void |
285 | | write_status_warning (const char *where, gpg_error_t err) |
286 | 0 | { |
287 | 0 | if (!statusfp || !status_currently_allowed (STATUS_WARNING)) |
288 | 0 | return; /* Not enabled or allowed. */ |
289 | | |
290 | 0 | es_fprintf (statusfp, "[GNUPG:] %s %s %u\n", |
291 | 0 | get_status_string (STATUS_WARNING), where, err); |
292 | 0 | if (es_fflush (statusfp) && opt.exit_on_status_write_error) |
293 | 0 | g10_exit (0); |
294 | 0 | } |
295 | | |
296 | | |
297 | | /* Write an ERROR status line using a full gpg-error error value. */ |
298 | | void |
299 | | write_status_error (const char *where, gpg_error_t err) |
300 | 0 | { |
301 | 0 | if (!statusfp || !status_currently_allowed (STATUS_ERROR)) |
302 | 0 | return; /* Not enabled or allowed. */ |
303 | | |
304 | 0 | es_fprintf (statusfp, "[GNUPG:] %s %s %u\n", |
305 | 0 | get_status_string (STATUS_ERROR), where, err); |
306 | 0 | if (es_fflush (statusfp) && opt.exit_on_status_write_error) |
307 | 0 | g10_exit (0); |
308 | 0 | } |
309 | | |
310 | | |
311 | | /* Same as above but outputs the error code only. */ |
312 | | void |
313 | | write_status_errcode (const char *where, int errcode) |
314 | 0 | { |
315 | 0 | if (!statusfp || !status_currently_allowed (STATUS_ERROR)) |
316 | 0 | return; /* Not enabled or allowed. */ |
317 | | |
318 | 0 | es_fprintf (statusfp, "[GNUPG:] %s %s %u\n", |
319 | 0 | get_status_string (STATUS_ERROR), where, gpg_err_code (errcode)); |
320 | 0 | if (es_fflush (statusfp) && opt.exit_on_status_write_error) |
321 | 0 | g10_exit (0); |
322 | 0 | } |
323 | | |
324 | | |
325 | | /* Write a FAILURE status line. */ |
326 | | void |
327 | | write_status_failure (const char *where, gpg_error_t err) |
328 | 0 | { |
329 | 0 | static int any_failure_printed; |
330 | |
|
331 | 0 | if (!statusfp || !status_currently_allowed (STATUS_FAILURE)) |
332 | 0 | return; /* Not enabled or allowed. */ |
333 | 0 | if (any_failure_printed) |
334 | 0 | return; |
335 | 0 | any_failure_printed = 1; |
336 | 0 | es_fprintf (statusfp, "[GNUPG:] %s %s %u\n", |
337 | 0 | get_status_string (STATUS_FAILURE), where, err); |
338 | 0 | if (es_fflush (statusfp) && opt.exit_on_status_write_error) |
339 | 0 | g10_exit (0); |
340 | 0 | } |
341 | | |
342 | | |
343 | | /* |
344 | | * Write a status line with a buffer using %XX escapes. If WRAP is > |
345 | | * 0 wrap the line after this length. If STRING is not NULL it will |
346 | | * be prepended to the buffer, no escaping is done for string. |
347 | | * A wrap of -1 forces spaces not to be encoded as %20. |
348 | | */ |
349 | | void |
350 | | write_status_text_and_buffer (int no, const char *string, |
351 | | const char *buffer, size_t len, int wrap) |
352 | 10.5k | { |
353 | 10.5k | const char *s, *text; |
354 | 10.5k | int esc, first; |
355 | 10.5k | int lower_limit = ' '; |
356 | 10.5k | int escape_more; |
357 | 10.5k | size_t n, count, dowrap; |
358 | | |
359 | 10.5k | if (!statusfp || !status_currently_allowed (no)) |
360 | 10.5k | return; /* Not enabled or allowed. */ |
361 | | |
362 | 0 | if (wrap == -1) |
363 | 0 | { |
364 | 0 | lower_limit--; |
365 | 0 | wrap = 0; |
366 | 0 | } |
367 | |
|
368 | 0 | escape_more = (no == STATUS_NOTATION_NAME || no == STATUS_NOTATION_DATA); |
369 | |
|
370 | 0 | text = get_status_string (no); |
371 | 0 | count = dowrap = first = 1; |
372 | 0 | do |
373 | 0 | { |
374 | 0 | if (dowrap) |
375 | 0 | { |
376 | 0 | es_fprintf (statusfp, "[GNUPG:] %s ", text); |
377 | 0 | count = dowrap = 0; |
378 | 0 | if (first && string) |
379 | 0 | { |
380 | 0 | es_fputs (string, statusfp); |
381 | 0 | count += strlen (string); |
382 | | /* Make sure that there is a space after the string. */ |
383 | 0 | if (*string && string[strlen (string)-1] != ' ') |
384 | 0 | { |
385 | 0 | es_putc (' ', statusfp); |
386 | 0 | count++; |
387 | 0 | } |
388 | 0 | } |
389 | 0 | first = 0; |
390 | 0 | } |
391 | 0 | for (esc=0, s=buffer, n=len; n; s++, n--) |
392 | 0 | { |
393 | 0 | if (*s == '%' || *(const byte*)s <= lower_limit |
394 | 0 | || *(const byte*)s == 127 || (escape_more && (*s & 0x80))) |
395 | 0 | esc = 1; |
396 | 0 | if (wrap && ++count > wrap) |
397 | 0 | dowrap=1; |
398 | 0 | if (esc || dowrap) |
399 | 0 | break; |
400 | 0 | } |
401 | 0 | if (s != buffer) |
402 | 0 | es_fwrite (buffer, s-buffer, 1, statusfp); |
403 | 0 | if ( esc ) |
404 | 0 | { |
405 | 0 | es_fprintf (statusfp, "%%%02X", *(const byte*)s ); |
406 | 0 | s++; n--; |
407 | 0 | } |
408 | 0 | buffer = s; |
409 | 0 | len = n; |
410 | 0 | if (dowrap && len) |
411 | 0 | es_putc ('\n', statusfp); |
412 | 0 | } |
413 | 0 | while (len); |
414 | |
|
415 | 0 | es_putc ('\n',statusfp); |
416 | 0 | if (es_fflush (statusfp) && opt.exit_on_status_write_error) |
417 | 0 | g10_exit (0); |
418 | 0 | } |
419 | | |
420 | | |
421 | | void |
422 | | write_status_buffer (int no, const char *buffer, size_t len, int wrap) |
423 | 10.5k | { |
424 | 10.5k | write_status_text_and_buffer (no, NULL, buffer, len, wrap); |
425 | 10.5k | } |
426 | | |
427 | | |
428 | | /* Print the BEGIN_SIGNING status message. If MD is not NULL it is |
429 | | used to retrieve the hash algorithms used for the message. */ |
430 | | void |
431 | | write_status_begin_signing (gcry_md_hd_t md) |
432 | 0 | { |
433 | 0 | if (md) |
434 | 0 | { |
435 | 0 | char buf[100]; |
436 | 0 | size_t buflen; |
437 | 0 | int i, ga; |
438 | |
|
439 | 0 | buflen = 0; |
440 | 0 | for (i=1; i <= 110; i++) |
441 | 0 | { |
442 | 0 | ga = map_md_openpgp_to_gcry (i); |
443 | 0 | if (ga && gcry_md_is_enabled (md, ga) && buflen+10 < DIM(buf)) |
444 | 0 | { |
445 | 0 | snprintf (buf+buflen, DIM(buf) - buflen, |
446 | 0 | "%sH%d", buflen? " ":"",i); |
447 | 0 | buflen += strlen (buf+buflen); |
448 | 0 | } |
449 | 0 | } |
450 | 0 | write_status_text (STATUS_BEGIN_SIGNING, buf); |
451 | 0 | } |
452 | 0 | else |
453 | 0 | write_status ( STATUS_BEGIN_SIGNING ); |
454 | 0 | } |
455 | | |
456 | | |
457 | | static int |
458 | | myread(int fd, void *buf, size_t count) |
459 | 0 | { |
460 | 0 | int rc; |
461 | 0 | do |
462 | 0 | { |
463 | 0 | rc = read( fd, buf, count ); |
464 | 0 | } |
465 | 0 | while (rc == -1 && errno == EINTR); |
466 | |
|
467 | 0 | if (!rc && count) |
468 | 0 | { |
469 | 0 | static int eof_emmited=0; |
470 | 0 | if ( eof_emmited < 3 ) |
471 | 0 | { |
472 | 0 | *(char*)buf = CONTROL_D; |
473 | 0 | rc = 1; |
474 | 0 | eof_emmited++; |
475 | 0 | } |
476 | 0 | else /* Ctrl-D not caught - do something reasonable */ |
477 | 0 | { |
478 | | #ifdef HAVE_DOSISH_SYSTEM |
479 | | raise (SIGINT); /* Nothing to hangup under DOS. */ |
480 | | #else |
481 | 0 | raise (SIGHUP); /* No more input data. */ |
482 | 0 | #endif |
483 | 0 | } |
484 | 0 | } |
485 | 0 | return rc; |
486 | 0 | } |
487 | | |
488 | | |
489 | | |
490 | | /* Request a string from the client over the command-fd. If GETBOOL |
491 | | is set the function returns a static string (do not free) if the |
492 | | entered value was true or NULL if the entered value was false. */ |
493 | | static char * |
494 | | do_get_from_fd ( const char *keyword, int hidden, int getbool ) |
495 | 0 | { |
496 | 0 | int i, len; |
497 | 0 | char *string; |
498 | |
|
499 | 0 | if (statusfp != es_stdout) |
500 | 0 | es_fflush (es_stdout); |
501 | |
|
502 | 0 | write_status_text (getbool? STATUS_GET_BOOL : |
503 | 0 | hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword); |
504 | |
|
505 | 0 | for (string = NULL, i = len = 200; ; i++ ) |
506 | 0 | { |
507 | 0 | if (i >= len-1 ) |
508 | 0 | { |
509 | | /* On the first iteration allocate a new buffer. If that |
510 | | * buffer is too short at further iterations do a poor man's |
511 | | * realloc. */ |
512 | 0 | char *save = string; |
513 | 0 | len += 100; |
514 | 0 | string = hidden? xmalloc_secure ( len ) : xmalloc ( len ); |
515 | 0 | if (save) |
516 | 0 | { |
517 | 0 | memcpy (string, save, i); |
518 | 0 | xfree (save); |
519 | 0 | } |
520 | 0 | else |
521 | 0 | i = 0; |
522 | 0 | } |
523 | | /* Fixme: why not use our read_line function here? */ |
524 | 0 | if ( myread( opt.command_fd, string+i, 1) != 1 || string[i] == '\n' ) |
525 | 0 | break; |
526 | 0 | else if ( string[i] == CONTROL_D ) |
527 | 0 | { |
528 | | /* Found ETX - Cancel the line and return a sole ETX. */ |
529 | 0 | string[0] = CONTROL_D; |
530 | 0 | i = 1; |
531 | 0 | break; |
532 | 0 | } |
533 | 0 | } |
534 | 0 | string[i] = 0; |
535 | |
|
536 | 0 | write_status (STATUS_GOT_IT); |
537 | |
|
538 | 0 | if (getbool) /* Fixme: is this correct??? */ |
539 | 0 | { |
540 | 0 | char *rv = (string[0] == 'Y' || string[0] == 'y') ? "" : NULL; |
541 | 0 | xfree (string); |
542 | 0 | return rv; |
543 | 0 | } |
544 | | |
545 | 0 | return string; |
546 | 0 | } |
547 | | |
548 | | |
549 | | |
550 | | int |
551 | | cpr_enabled(void) |
552 | 0 | { |
553 | 0 | if( opt.command_fd != -1 ) |
554 | 0 | return 1; |
555 | 0 | return 0; |
556 | 0 | } |
557 | | |
558 | | char * |
559 | | cpr_get_no_help( const char *keyword, const char *prompt ) |
560 | 0 | { |
561 | 0 | char *p; |
562 | |
|
563 | 0 | if( opt.command_fd != -1 ) |
564 | 0 | return do_get_from_fd ( keyword, 0, 0 ); |
565 | 0 | for(;;) { |
566 | 0 | p = tty_get( prompt ); |
567 | 0 | return p; |
568 | 0 | } |
569 | 0 | } |
570 | | |
571 | | char * |
572 | | cpr_get( const char *keyword, const char *prompt ) |
573 | 0 | { |
574 | 0 | char *p; |
575 | |
|
576 | 0 | if( opt.command_fd != -1 ) |
577 | 0 | return do_get_from_fd ( keyword, 0, 0 ); |
578 | 0 | for(;;) { |
579 | 0 | p = tty_get( prompt ); |
580 | 0 | if( *p=='?' && !p[1] && !(keyword && !*keyword)) { |
581 | 0 | xfree(p); |
582 | 0 | display_online_help( keyword ); |
583 | 0 | } |
584 | 0 | else |
585 | 0 | return p; |
586 | 0 | } |
587 | 0 | } |
588 | | |
589 | | |
590 | | char * |
591 | | cpr_get_utf8( const char *keyword, const char *prompt ) |
592 | 0 | { |
593 | 0 | char *p; |
594 | 0 | p = cpr_get( keyword, prompt ); |
595 | 0 | if( p ) { |
596 | 0 | char *utf8 = native_to_utf8( p ); |
597 | 0 | xfree( p ); |
598 | 0 | p = utf8; |
599 | 0 | } |
600 | 0 | return p; |
601 | 0 | } |
602 | | |
603 | | char * |
604 | | cpr_get_hidden( const char *keyword, const char *prompt ) |
605 | 0 | { |
606 | 0 | char *p; |
607 | |
|
608 | 0 | if( opt.command_fd != -1 ) |
609 | 0 | return do_get_from_fd ( keyword, 1, 0 ); |
610 | 0 | for(;;) { |
611 | 0 | p = tty_get_hidden( prompt ); |
612 | 0 | if( *p == '?' && !p[1] ) { |
613 | 0 | xfree(p); |
614 | 0 | display_online_help( keyword ); |
615 | 0 | } |
616 | 0 | else |
617 | 0 | return p; |
618 | 0 | } |
619 | 0 | } |
620 | | |
621 | | void |
622 | | cpr_kill_prompt(void) |
623 | 0 | { |
624 | 0 | if( opt.command_fd != -1 ) |
625 | 0 | return; |
626 | 0 | tty_kill_prompt(); |
627 | 0 | return; |
628 | 0 | } |
629 | | |
630 | | int |
631 | | cpr_get_answer_is_yes_def (const char *keyword, const char *prompt, int def_yes) |
632 | 0 | { |
633 | 0 | int yes; |
634 | 0 | char *p; |
635 | |
|
636 | 0 | if( opt.command_fd != -1 ) |
637 | 0 | return !!do_get_from_fd ( keyword, 0, 1 ); |
638 | 0 | for(;;) { |
639 | 0 | p = tty_get( prompt ); |
640 | 0 | trim_spaces(p); /* it is okay to do this here */ |
641 | 0 | if( *p == '?' && !p[1] ) { |
642 | 0 | xfree(p); |
643 | 0 | display_online_help( keyword ); |
644 | 0 | } |
645 | 0 | else { |
646 | 0 | tty_kill_prompt(); |
647 | 0 | yes = answer_is_yes_no_default (p, def_yes); |
648 | 0 | xfree(p); |
649 | 0 | return yes; |
650 | 0 | } |
651 | 0 | } |
652 | 0 | } |
653 | | |
654 | | int |
655 | | cpr_get_answer_is_yes (const char *keyword, const char *prompt) |
656 | 0 | { |
657 | 0 | return cpr_get_answer_is_yes_def (keyword, prompt, 0); |
658 | 0 | } |
659 | | |
660 | | int |
661 | | cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt ) |
662 | 0 | { |
663 | 0 | int yes; |
664 | 0 | char *p; |
665 | |
|
666 | 0 | if( opt.command_fd != -1 ) |
667 | 0 | return !!do_get_from_fd ( keyword, 0, 1 ); |
668 | 0 | for(;;) { |
669 | 0 | p = tty_get( prompt ); |
670 | 0 | trim_spaces(p); /* it is okay to do this here */ |
671 | 0 | if( *p == '?' && !p[1] ) { |
672 | 0 | xfree(p); |
673 | 0 | display_online_help( keyword ); |
674 | 0 | } |
675 | 0 | else { |
676 | 0 | tty_kill_prompt(); |
677 | 0 | yes = answer_is_yes_no_quit(p); |
678 | 0 | xfree(p); |
679 | 0 | return yes; |
680 | 0 | } |
681 | 0 | } |
682 | 0 | } |
683 | | |
684 | | |
685 | | int |
686 | | cpr_get_answer_okay_cancel (const char *keyword, |
687 | | const char *prompt, |
688 | | int def_answer) |
689 | 0 | { |
690 | 0 | int yes; |
691 | 0 | char *answer = NULL; |
692 | 0 | char *p; |
693 | |
|
694 | 0 | if( opt.command_fd != -1 ) |
695 | 0 | answer = do_get_from_fd ( keyword, 0, 0 ); |
696 | |
|
697 | 0 | if (answer) |
698 | 0 | { |
699 | 0 | yes = answer_is_okay_cancel (answer, def_answer); |
700 | 0 | xfree (answer); |
701 | 0 | return yes; |
702 | 0 | } |
703 | | |
704 | 0 | for(;;) |
705 | 0 | { |
706 | 0 | p = tty_get( prompt ); |
707 | 0 | trim_spaces(p); /* it is okay to do this here */ |
708 | 0 | if (*p == '?' && !p[1]) |
709 | 0 | { |
710 | 0 | xfree(p); |
711 | 0 | display_online_help (keyword); |
712 | 0 | } |
713 | 0 | else |
714 | 0 | { |
715 | 0 | tty_kill_prompt(); |
716 | 0 | yes = answer_is_okay_cancel (p, def_answer); |
717 | 0 | xfree(p); |
718 | 0 | return yes; |
719 | 0 | } |
720 | 0 | } |
721 | 0 | } |