aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/common-defs.h3
-rw-r--r--common/gpgrlhelp.c71
-rw-r--r--common/ttyio.c39
-rw-r--r--common/ttyio.h2
-rw-r--r--doc/gpg-card.texi6
-rw-r--r--doc/tools.texi9
-rw-r--r--tools/gpg-card.c56
-rw-r--r--tools/gpg-card.h2
-rw-r--r--tools/gpg-connect-agent.c35
9 files changed, 216 insertions, 7 deletions
diff --git a/common/common-defs.h b/common/common-defs.h
index b1928e611..cad5405d0 100644
--- a/common/common-defs.h
+++ b/common/common-defs.h
@@ -47,7 +47,8 @@ void tty_private_set_rl_hooks (void (*init_stream) (FILE *),
void (*inhibit_completion) (int),
void (*cleanup_after_signal) (void),
char *(*readline_fun) (const char*),
- void (*add_history_fun) (const char*));
+ void (*add_history_fun) (const char*),
+ int (*rw_history_fun)(const char *, int, int));
diff --git a/common/gpgrlhelp.c b/common/gpgrlhelp.c
index 680d9998b..fd3a48f8a 100644
--- a/common/gpgrlhelp.c
+++ b/common/gpgrlhelp.c
@@ -77,11 +77,77 @@ init_stream (FILE *fp)
rl_inhibit_completion = 1;
}
+
+/* Read or write the history to or from the file FILENAME. The
+ * behaviour depends on the flag WRITE_MODE:
+ *
+ * In read mode (WRITE_MODE is false) these semantics are used:
+ *
+ * If NLINES is positive only this number of lines are read from the
+ * history and the history is always limited to that number of
+ * lines. A negative value for NLINES is undefined.
+ *
+ * If FILENAME is NULL the current history is cleared. If NLINES is
+ * positive the number of lines stored in the history is limited to
+ * that number. A negative value for NLINES is undefined.
+ *
+ * If WRITE_MODE is true these semantics are used:
+ *
+ * If NLINES is negative the history and the history file are
+ * cleared; if it is zero the entire history is written to the file;
+ * if it is positive the history is written to the file and the file
+ * is truncated to this number of lines.
+ *
+ * If FILENAME is NULL no file operations are done but if NLINES is
+ * negative the entire history is cleared.
+ *
+ * On success 0 is returned; on error -1 is returned and ERRNO is set.
+ */
+static int
+read_write_history (const char *filename, int write_mode, int nlines)
+{
+ int rc;
+
+ if (write_mode)
+ {
+ if (nlines < 0)
+ clear_history ();
+ rc = filename? write_history (filename) : 0;
+ if (!rc && filename && nlines > 0)
+ rc = history_truncate_file (filename, nlines);
+ if (rc)
+ {
+ gpg_err_set_errno (rc);
+ return -1;
+ }
+ }
+ else
+ {
+ clear_history ();
+ if (filename)
+ {
+ if (nlines)
+ rc = read_history_range (filename, 0, nlines);
+ else
+ rc = read_history (filename);
+ if (rc)
+ {
+ gpg_err_set_errno (rc);
+ return -1;
+ }
+ }
+ if (nlines > 0)
+ stifle_history (nlines);
+ }
+
+ return 0;
+}
+
#endif /*HAVE_LIBREADLINE*/
/* Initialize our readline code. This should be called as early as
- possible as it is actually a constructur. */
+ * possible as it is actually a constructor. */
void
gnupg_rl_initialize (void)
{
@@ -91,7 +157,8 @@ gnupg_rl_initialize (void)
inhibit_completion,
cleanup_after_signal,
readline,
- add_history);
+ add_history,
+ read_write_history);
rl_readline_name = GNUPG_NAME;
#endif
}
diff --git a/common/ttyio.c b/common/ttyio.c
index 4c095bc03..a27c095cf 100644
--- a/common/ttyio.c
+++ b/common/ttyio.c
@@ -101,7 +101,7 @@ static void (*my_rl_cleanup_after_signal) (void);
static void (*my_rl_init_stream) (FILE *);
static char *(*my_rl_readline) (const char*);
static void (*my_rl_add_history) (const char*);
-
+static int (*my_rl_rw_history)(const char *, int, int);
/* This is a wrapper around ttyname so that we can use it even when
the standard streams are redirected. It figures the name out the
@@ -703,7 +703,8 @@ tty_private_set_rl_hooks (void (*init_stream) (FILE *),
void (*inhibit_completion) (int),
void (*cleanup_after_signal) (void),
char *(*readline_fun) (const char*),
- void (*add_history_fun) (const char*))
+ void (*add_history_fun) (const char*),
+ int (*rw_history_fun)(const char *, int, int))
{
my_rl_init_stream = init_stream;
my_rl_set_completer = set_completer;
@@ -711,6 +712,40 @@ tty_private_set_rl_hooks (void (*init_stream) (FILE *),
my_rl_cleanup_after_signal = cleanup_after_signal;
my_rl_readline = readline_fun;
my_rl_add_history = add_history_fun;
+ my_rl_rw_history = rw_history_fun;
+}
+
+
+/* Read the history from FILENAME or limit the size of the history.
+ * If FILENAME is NULL and NLINES is zero the current history is
+ * cleared. Returns 0 on success or -1 on error and sets ERRNO. No
+ * error is return if readline support is not available. */
+int
+tty_read_history (const char *filename, int nlines)
+{
+ int rc;
+
+ if (!my_rl_rw_history)
+ return 0;
+
+ rc = my_rl_rw_history (filename, 0, nlines);
+ if (rc && gpg_err_code_from_syserror () == GPG_ERR_ENOENT)
+ rc = 0;
+
+ return rc;
+}
+
+
+/* Write the current history to the file FILENAME. Returns 0 on
+ * success or -1 on error and sets ERRNO. No error is return if
+ * readline support is not available. */
+int
+tty_write_history (const char *filename)
+{
+ if (!my_rl_rw_history)
+ return 0;
+
+ return my_rl_rw_history (filename, 1, 0);
}
diff --git a/common/ttyio.h b/common/ttyio.h
index 5bff82fbb..46bcc2ffc 100644
--- a/common/ttyio.h
+++ b/common/ttyio.h
@@ -66,6 +66,8 @@ void tty_disable_completion (void);
#define tty_enable_completion(x)
#define tty_disable_completion()
#endif
+int tty_read_history (const char *filename, int nlines);
+int tty_write_history (const char *filename);
void tty_cleanup_after_signal (void);
void tty_cleanup_rl_after_signal (void);
diff --git a/doc/gpg-card.texi b/doc/gpg-card.texi
index 0865798d2..be19704cc 100644
--- a/doc/gpg-card.texi
+++ b/doc/gpg-card.texi
@@ -105,6 +105,12 @@ Do not start the gpg-agent if it has not yet been started and its
service is required. This option is mostly useful on machines where
the connection to gpg-agent has been redirected to another machines.
+@item --no-history
+@opindex --no-history
+In interactive mode the command line history is usually saved and
+restored to and from a file below the GnuPG home directory. This
+option inhibits the use of that file.
+
@item --agent-program @var{file}
@opindex agent-program
Specify the agent program to be started if none is running. The
diff --git a/doc/tools.texi b/doc/tools.texi
index 985c0a75c..537c3c72a 100644
--- a/doc/tools.texi
+++ b/doc/tools.texi
@@ -1391,6 +1391,12 @@ passing. This option makes it use the old mode.
Do not start the gpg-agent or the dirmngr if it has not yet been
started.
+@item --no-history
+@opindex --no-history
+In interactive mode the command line history is usually saved and
+restored to and from a file below the GnuPG home directory. This
+option inhibits the use of that file.
+
@item -r @var{file}
@itemx --run @var{file}
@opindex run
@@ -1611,6 +1617,9 @@ string @code{true} or @code{yes}. The evaluation is done by passing
@item /run @var{file}
Run commands from @var{file}.
+@item /history --clear
+Clear the command history.
+
@item /bye
Terminate the connection and the program.
diff --git a/tools/gpg-card.c b/tools/gpg-card.c
index 96b2bcf44..2fcc120fb 100644
--- a/tools/gpg-card.c
+++ b/tools/gpg-card.c
@@ -48,6 +48,8 @@
#define CONTROL_D ('D' - 'A' + 1)
+#define HISTORYNAME ".gpg-card_history"
+
/* Constants to identify the commands and options. */
enum opt_values
{
@@ -73,6 +75,7 @@ enum opt_values
oLCmessages,
oNoKeyLookup,
+ oNoHistory,
oDummy
};
@@ -99,6 +102,8 @@ static gpgrt_opt_t opts[] = {
ARGPARSE_s_s (oLCmessages, "lc-messages","@"),
ARGPARSE_s_n (oNoKeyLookup,"no-key-lookup",
"use --no-key-lookup for \"list\""),
+ ARGPARSE_s_n (oNoHistory,"no-history",
+ "do not use the command history file"),
ARGPARSE_end ()
};
@@ -228,6 +233,7 @@ parse_arguments (gpgrt_argparse_t *pargs, gpgrt_opt_t *popts)
case oLCmessages: opt.lc_messages = pargs->r.ret_str; break;
case oNoKeyLookup: opt.no_key_lookup = 1; break;
+ case oNoHistory: opt.no_history = 1; break;
default: pargs->err = 2; break;
}
@@ -315,6 +321,9 @@ main (int argc, char **argv)
}
opt.interactive = !cmdidx;
+ if (!opt.interactive)
+ opt.no_history = 1;
+
if (opt.interactive)
{
interactive_loop ();
@@ -3498,6 +3507,32 @@ cmd_apdu (card_info_t info, char *argstr)
}
+static gpg_error_t
+cmd_history (card_info_t info, char *argstr)
+{
+ int opt_list, opt_clear;
+
+ opt_list = has_option (argstr, "--list");
+ opt_clear = has_option (argstr, "--clear");
+
+ if (!info || !(opt_list || opt_clear))
+ return print_help
+ ("HISTORY --list\n"
+ " List the command history\n"
+ "HISTORY --clear\n"
+ " Clear the command history",
+ 0);
+
+ if (opt_list)
+ tty_printf ("Sorry, history listing not yet possible\n");
+
+ if (opt_clear)
+ tty_read_history (NULL, 0);
+
+ return 0;
+}
+
+
/* Data used by the command parser. This needs to be outside of the
@@ -3509,7 +3544,7 @@ enum cmdids
cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSALUT, cmdCAFPR,
cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
cmdREADCERT, cmdWRITEKEY, cmdUNBLOCK, cmdFACTRST, cmdKDFSETUP,
- cmdUIF, cmdAUTH, cmdYUBIKEY, cmdAPDU,
+ cmdUIF, cmdAUTH, cmdYUBIKEY, cmdAPDU, cmdHISTORY,
cmdINVCMD
};
@@ -3551,6 +3586,7 @@ static struct
{ "writekey", cmdWRITEKEY, N_("store a private key to a data object")},
{ "yubikey", cmdYUBIKEY, N_("Yubikey management commands")},
{ "apdu", cmdAPDU, NULL},
+ { "history", cmdHISTORY, N_("manage the command history")},
{ NULL, cmdINVCMD, NULL }
};
@@ -3679,6 +3715,7 @@ dispatch_command (card_info_t info, const char *orig_command)
case cmdUIF: err = cmd_uif (info, argstr); break;
case cmdYUBIKEY: err = cmd_yubikey (info, argstr); break;
case cmdAPDU: err = cmd_apdu (info, argstr); break;
+ case cmdHISTORY: err = 0; break; /* Only used in interactive mode. */
case cmdINVCMD:
default:
@@ -3735,10 +3772,19 @@ interactive_loop (void)
card_info_t info = &info_buffer;
char *p;
int i;
+ const char *historyname = NULL;
/* In the interactive mode we do not want to print the program prefix. */
log_set_prefix (NULL, 0);
+ if (!opt.no_history)
+ {
+ historyname = make_filename (gnupg_homedir (), HISTORYNAME, NULL);
+ if (tty_read_history (historyname, 500))
+ log_info ("error reading '%s': %s\n",
+ historyname, gpg_strerror (gpg_error_from_syserror ()));
+ }
+
for (;;)
{
if (help_arg)
@@ -3818,7 +3864,7 @@ interactive_loop (void)
argstr = answer + strlen (answer);
if (!(cmd == cmdNOP || cmd == cmdQUIT || cmd == cmdHELP
- || cmd == cmdINVCMD))
+ || cmd == cmdHISTORY || cmd == cmdINVCMD))
{
/* If redisplay is set we know that there was an error reading
* the card. In this case we force a LIST command to retry. */
@@ -3926,6 +3972,7 @@ interactive_loop (void)
case cmdUIF: err = cmd_uif (info, argstr); break;
case cmdYUBIKEY: err = cmd_yubikey (info, argstr); break;
case cmdAPDU: err = cmd_apdu (info, argstr); break;
+ case cmdHISTORY: err = cmd_history (info, argstr); break;
case cmdINVCMD:
default:
@@ -3962,7 +4009,12 @@ interactive_loop (void)
} /* End of main menu loop. */
leave:
+ if (historyname && tty_write_history (historyname))
+ log_info ("error writing '%s': %s\n",
+ historyname, gpg_strerror (gpg_error_from_syserror ()));
+
release_card_info (info);
+ xfree (historyname);
xfree (answer);
}
diff --git a/tools/gpg-card.h b/tools/gpg-card.h
index 56e117bf3..067720d6c 100644
--- a/tools/gpg-card.h
+++ b/tools/gpg-card.h
@@ -40,6 +40,8 @@ struct
int no_key_lookup; /* Assume --no-key-lookup for "list". */
+ int no_history; /* Do not use the command line history. */
+
/* Options passed to the gpg-agent: */
session_env_t session_env;
char *lc_ctype;
diff --git a/tools/gpg-connect-agent.c b/tools/gpg-connect-agent.c
index 746ac7daf..cde086770 100644
--- a/tools/gpg-connect-agent.c
+++ b/tools/gpg-connect-agent.c
@@ -44,6 +44,9 @@
#define CONTROL_D ('D' - 'A' + 1)
#define octdigitp(p) (*(p) >= '0' && *(p) <= '7')
+#define HISTORYNAME ".gpg-connect_history"
+
+
/* Constants to identify the commands and options. */
enum cmd_and_opt_values
{
@@ -67,6 +70,7 @@ enum cmd_and_opt_values
oDirmngr,
oKeyboxd,
oUIServer,
+ oNoHistory,
oNoAutostart
};
@@ -97,6 +101,8 @@ static gpgrt_opt_t opts[] = {
ARGPARSE_s_n (oNoAutostart, "no-autostart", "@"),
ARGPARSE_s_n (oNoVerbose, "no-verbose", "@"),
+ ARGPARSE_s_n (oNoHistory,"no-history",
+ "do not use the command history file"),
ARGPARSE_s_s (oHomedir, "homedir", "@" ),
ARGPARSE_s_s (oAgentProgram, "agent-program", "@"),
ARGPARSE_s_s (oDirmngrProgram, "dirmngr-program", "@"),
@@ -127,6 +133,7 @@ struct
unsigned int connect_flags; /* Flags used for connecting. */
int enable_varsubst; /* Set if variable substitution is enabled. */
int trim_leading_spaces;
+ int no_history;
} opt;
@@ -1178,6 +1185,7 @@ main (int argc, char **argv)
} loopstack[20];
int loopidx;
char **cmdline_commands = NULL;
+ char *historyname = NULL;
early_system_init ();
gnupg_rl_initialize ();
@@ -1210,6 +1218,7 @@ main (int argc, char **argv)
case oDirmngrProgram: opt.dirmngr_program = pargs.r.ret_str; break;
case oKeyboxdProgram: opt.keyboxd_program = pargs.r.ret_str; break;
case oNoAutostart: opt.autostart = 0; break;
+ case oNoHistory: opt.no_history = 1; break;
case oHex: opt.hex = 1; break;
case oDecode: opt.decode = 1; break;
case oDirmngr: opt.use_dirmngr = 1; break;
@@ -1422,6 +1431,15 @@ main (int argc, char **argv)
{
keep_line = 0;
xfree (line);
+ if (!historyname && !opt.no_history)
+ {
+ historyname = make_filename (gnupg_homedir (), HISTORYNAME, NULL);
+ if (tty_read_history (historyname, 500))
+ log_info ("error reading '%s': %s\n",
+ historyname,
+ gpg_strerror (gpg_error_from_syserror ()));
+ }
+
line = tty_get ("> ");
n = strlen (line);
if (n==1 && *line == CONTROL_D)
@@ -1817,6 +1835,15 @@ main (int argc, char **argv)
{
gnupg_sleep (1);
}
+ else if (!strcmp (cmd, "history"))
+ {
+ if (!strcmp (p, "--clear"))
+ {
+ tty_read_history (NULL, 0);
+ }
+ else
+ log_error ("Only \"/history --clear\" is supported\n");
+ }
else if (!strcmp (cmd, "help"))
{
puts (
@@ -1843,6 +1870,7 @@ main (int argc, char **argv)
"/if VAR Begin conditional block controlled by VAR.\n"
"/while VAR Begin loop controlled by VAR.\n"
"/end End loop or condition\n"
+"/history Manage the history\n"
"/bye Terminate gpg-connect-agent.\n"
"/help Print this help.");
}
@@ -1895,6 +1923,12 @@ main (int argc, char **argv)
opt.use_keyboxd? "keyboxd" :
"agent");
+
+ if (historyname && tty_write_history (historyname))
+ log_info ("error writing '%s': %s\n",
+ historyname, gpg_strerror (gpg_error_from_syserror ()));
+
+
/* XXX: We would like to release the context here, but libassuan
nicely says good bye to the server, which results in a SIGPIPE if
the server died. Unfortunately, libassuan does not ignore
@@ -1904,6 +1938,7 @@ main (int argc, char **argv)
assuan_release (ctx);
else
gpgrt_annotate_leaked_object (ctx);
+ xfree (historyname);
xfree (line);
return 0;
}