changeset 11486:a1deab9a6e71

bash-like history control
author Pascal Dupuis <Pascal.Dupuis@worldonline.be> and John W. Eaton <jwe@octave.org>
date Wed, 12 Jan 2011 03:40:19 -0500
parents 571bfa4fc295
children 0f14c093476e
files doc/ChangeLog doc/interpreter/basics.txi liboctave/ChangeLog liboctave/cmd-hist.cc liboctave/cmd-hist.h liboctave/oct-rl-hist.c liboctave/oct-rl-hist.h src/ChangeLog src/oct-hist.cc
diffstat 9 files changed, 283 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -1,3 +1,8 @@
+2011-01-12  John W. Eaton  <jwe@octave.org>
+
+	* interpreter/basics.txi (Commands For History):
+	Document history_control.
+
 2011-01-10  John W. Eaton  <jwe@octave.org>
 
 	* interpreter/contributors.in: Add Konstantinos Poulios to the list.
--- a/doc/interpreter/basics.txi
+++ b/doc/interpreter/basics.txi
@@ -660,6 +660,8 @@
 
 @DOCSTRING(saving_history)
 
+@DOCSTRING(history_control)
+
 @DOCSTRING(history_file)
 
 @DOCSTRING(history_size)
--- a/liboctave/ChangeLog
+++ b/liboctave/ChangeLog
@@ -1,3 +1,25 @@
+2011-01-12  Pascal Dupuis  <Pascal.Dupuis@worldonline.be>
+	    John W. Eaton  <jwe@octave.org>
+
+	* cmd-hist.cc (command_history::process_histcontrol,
+	command_history::hist_control, command_history::process_histcontrol,
+	command_history::hist_control): New functions.
+	(command_history::initialize, command_history::do_initialize):
+	New arg, control_arg.
+	* cmd-hist.cc (gnu_history::do_add): Pass history_control to
+	octave_add_history.
+	* cmd-hist.h (command_history::process_histcontrol,
+	command_history::hist_control, command_history::process_histcontrol,
+	command_history::hist_control): Provide decls.
+	(command_history::history_control): New data member.
+
+	* oct-rl-hist.c (octave_add_history): New arg, history_control.
+	Return int, not void.
+	(check_history_control, hc_erasedups): New static functions
+	borrowed from Bash.
+	* oct-rl-hist.h (octave_add_history): Fix decl.
+	(HC_IGNSPACE, HC_IGNDUPS, HC_ERASEDUPS): New #defined constants.
+
 2011-01-06  John W. Eaton  <jwe@octave.org>
 
 	* oct-md5.cc (oct_md5_file): Tag call to fclose with gnulib::.
--- a/liboctave/cmd-hist.cc
+++ b/liboctave/cmd-hist.cc
@@ -117,9 +117,7 @@
           || (s.length () == 1 && (s[0] == '\r' || s[0] == '\n')))
         return;
 
-      ::octave_add_history (s.c_str ());
-
-      lines_this_session++;
+      lines_this_session += ::octave_add_history (s.c_str (), history_control);
     }
 }
 
@@ -421,10 +419,11 @@
 
 void
 command_history::initialize (bool read_history_file,
-                             const std::string& f_arg, int sz)
+                             const std::string& f_arg, int sz,
+                             const std::string & control_arg)
 {
   if (instance_ok ())
-    instance->do_initialize (read_history_file, f_arg, sz);
+    instance->do_initialize (read_history_file, f_arg, sz, control_arg);
 }
 
 bool
@@ -454,6 +453,20 @@
 }
 
 void
+command_history::process_histcontrol (const std::string& control_arg)
+{
+  if (instance_ok ())
+    instance->do_process_histcontrol(control_arg);
+}
+
+std::string
+command_history::histcontrol (void)
+{
+  return (instance_ok ())
+    ? instance->do_histcontrol () : std::string ();
+}
+
+void
 command_history::set_size (int n)
 {
   if (instance_ok ())
@@ -643,10 +656,12 @@
 
 void
 command_history::do_initialize (bool read_history_file,
-                                const std::string& f_arg, int sz)
+                                const std::string& f_arg, int sz,
+                                const std::string & control_arg)
 {
   command_history::set_file (f_arg);
   command_history::set_size (sz);
+  command_history::process_histcontrol (control_arg);
 
   if (read_history_file)
     command_history::read (false);
@@ -666,6 +681,77 @@
   xfile = f;
 }
 
+void
+command_history::do_process_histcontrol (const std::string& control_arg)
+{
+  history_control = 0;
+
+  size_t len = control_arg.length ();
+  size_t beg = 0;
+
+  while (beg < len)
+    {
+      if (control_arg[beg] == ':')
+        beg++;
+      else
+        {
+          size_t end = control_arg.find (":", beg);
+
+          if (end == std::string::npos)
+            end = len;
+
+          std::string tmp = control_arg.substr (beg, end-beg);
+
+          if (tmp == "erasedups")
+            history_control |= HC_ERASEDUPS;
+          else if (tmp == "ignoreboth")
+            history_control |= HC_IGNDUPS|HC_IGNSPACE;
+          else if (tmp == "ignoredups")
+            history_control |= HC_IGNDUPS;
+          else if (tmp == "ignorespace")
+            history_control |= HC_IGNSPACE;
+          else
+            (*current_liboctave_warning_handler)
+              ("unknown histcontrol directive %s", tmp.c_str ());
+
+          if (end != std::string::npos)
+            beg = end + 1;
+        }
+    }
+}
+
+std::string
+command_history::do_histcontrol (void) const
+{
+  // FIXME -- instead of reconstructing this value, should we just save
+  // the string we were given when constructing the command_history
+  // object?
+
+  std::string retval;
+
+  if (history_control & HC_IGNSPACE)
+    retval.append ("ignorespace");
+
+  if (history_control & HC_IGNDUPS)
+    {
+      if (retval.length() > 0)
+        retval.append (":");
+
+      retval.append ("ignoredups");
+    }
+
+  if (history_control & HC_ERASEDUPS)
+    {
+      if (retval.length() > 0)
+        retval.append (":");
+
+      retval.append ("erasedups");
+    }
+
+  return retval;
+}
+
+
 std::string
 command_history::do_file (void)
 {
--- a/liboctave/cmd-hist.h
+++ b/liboctave/cmd-hist.h
@@ -34,14 +34,14 @@
 protected:
 
   command_history (void)
-    : initialized (false), ignoring_additions (false), lines_in_file (0),
-      lines_this_session (0), xfile (), xsize (-1) { }
+    : initialized (false), ignoring_additions (false), history_control (0),
+      lines_in_file (0), lines_this_session (0), xfile (), xsize (-1) { }
 
 public:
 
   virtual ~command_history (void) { }
 
-  static void initialize (bool, const std::string&, int);
+  static void initialize (bool, const std::string&, int, const std::string&);
 
   static bool is_initialized (void);
 
@@ -49,6 +49,10 @@
 
   static std::string file (void);
 
+  static void process_histcontrol (const std::string&);
+
+  static std::string histcontrol (void);
+
   static void set_size (int);
 
   static int size (void);
@@ -132,7 +136,11 @@
 
   virtual std::string do_file (void);
 
-  virtual void do_initialize (bool, const std::string&, int);
+  virtual void do_process_histcontrol (const std::string&);
+
+  virtual std::string do_histcontrol (void) const;
+
+  virtual void do_initialize (bool, const std::string&, int, const std::string&);
 
   virtual bool do_is_initialized (void) const;
 
@@ -197,7 +205,10 @@
   // TRUE means we are ignoring new additions.
   bool ignoring_additions;
 
-  // The number of hisory lines we read from the history file.
+  // Bitmask for history control options.  See oct-rl-hist.h.
+  int history_control;
+
+  // The number of history lines we read from the history file.
   int lines_in_file;
 
   // The number of history lines we've saved so far.
--- a/liboctave/oct-rl-hist.c
+++ b/liboctave/oct-rl-hist.c
@@ -24,6 +24,8 @@
 #include <config.h>
 #endif
 
+#include "oct-rl-hist.h"
+
 #if defined (USE_READLINE)
 
 #include <stdio.h>
@@ -32,10 +34,82 @@
 
 #include <readline/history.h>
 
-void
-octave_add_history (const char *line)
+/* check_history_control, hc_erasedup, and the core of
+   octave_add_history were borrowed from Bash.  */
+
+/* Check LINE against what HISTCONTROL says to do.  Returns 1 if the line
+   should be saved; 0 if it should be discarded.  */
+static int
+check_history_control (const char *line, int history_control)
 {
-  add_history (line);
+  HIST_ENTRY *temp;
+  int r;
+
+  if (history_control == 0)
+    return 1;
+
+  /* ignorespace or ignoreboth */
+  if ((history_control & HC_IGNSPACE) && *line == ' ')
+    return 0;
+
+  /* ignoredups or ignoreboth */
+  if (history_control & HC_IGNDUPS)
+    {
+      using_history ();
+      temp = previous_history ();
+
+      r = (temp == 0 || strcmp (temp->line, line));
+
+      using_history ();
+
+      if (r == 0)
+        return r;
+    }
+
+  return 1;
+}
+
+/* Remove all entries matching LINE from the history list.  Triggered when
+   HISTCONTROL includes `erasedups'.  */
+
+static void
+hc_erasedups (const char *line)
+{
+  HIST_ENTRY *temp;
+  int r;
+
+  using_history ();
+  while (temp = previous_history ())
+    {
+      if (! strcmp (temp->line, line))
+        {
+          r = where_history ();
+          remove_history (r);
+        }
+    }
+  using_history ();
+}
+
+/* Check LINE against HISTCONTROL and add it to the history if it's OK.
+   Returns 1 if the line was saved in the history, 0 otherwise.  */
+
+int
+octave_add_history (const char *line, int history_control)
+{
+  if (check_history_control (line, history_control))
+    {
+      /* We're committed to saving the line.  If the user has requested it,
+         remove other matching lines from the history.  */
+
+      if (history_control & HC_ERASEDUPS)
+        hc_erasedups (line);
+        
+      add_history (line);
+
+      return 1;
+    }
+
+  return 0;
 }
 
 int
--- a/liboctave/oct-rl-hist.h
+++ b/liboctave/oct-rl-hist.h
@@ -28,7 +28,11 @@
 {
 #endif
 
-extern void octave_add_history (const char *);
+#define HC_IGNSPACE 0x01
+#define HC_IGNDUPS 0x02
+#define HC_ERASEDUPS 0x04
+
+extern int octave_add_history (const char *, int);
 
 extern int octave_where_history (void);
 
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,13 @@
+2011-01-12  John W. Eaton  <jwe@octave.org>
+
+	*oct-hist.cc (Fhistory_control): New function.
+
+2011-01-12  Pascal Dupuis  <Pascal.Dupuis@worldonline.be>
+
+	* oct-hist.cc (default_history_control): New function.
+	(initialize_history): Pass Vhistory control to
+	command_history::initialize.
+
 2011-01-11  Konstantinos Poulios  <logari81@googlemail.com>
 
 	* gl-render.cc (opengl_renderer::draw_axes): Improve positioning
--- a/src/oct-hist.cc
+++ b/src/oct-hist.cc
@@ -114,6 +114,24 @@
 static int Vhistory_size = default_history_size ();
 
 static std::string
+default_history_control (void)
+{
+  std::string retval;
+
+  std::string env_histcontrol = octave_env::getenv ("OCTAVE_HISTCONTROL");
+
+  if (! env_histcontrol.empty ())
+    {
+      return env_histcontrol;
+    }
+
+  return retval;
+}
+
+// The number of lines to keep in the history file.
+static std::string Vhistory_control = default_history_control ();
+
+static std::string
 default_history_timestamp_format (void)
 {
   return
@@ -520,7 +538,8 @@
 void
 initialize_history (bool read_history_file)
 {
-  command_history::initialize (read_history_file, Vhistory_file, Vhistory_size);
+  command_history::initialize (read_history_file, Vhistory_file, Vhistory_size,
+                               Vhistory_control);
 }
 
 void
@@ -650,6 +669,39 @@
   return retval;
 }
 
+DEFUN (history_control, args, nargout,
+  "-*- texinfo -*-\n\
+@deftypefn  {Built-in Function} {@var{val} =} history_control ()\n\
+@deftypefnx {Built-in Function} {@var{old_val} =} history_control (@var{new_val})\n\
+Query or set the internal variable that specifies how commands are saved\n\
+to the history list.  The default value is an empty character string,\n\
+but may be overridden by the environment variable\n\
+@w{@env{OCTAVE_HISTCONTROL}}.\n\
+\n\
+The value of @code{history_control} is a colon-separated list of values\n\
+controlling how commands are saved on the history list.   If the list\n\
+of values includes @code{ignorespace},  lines which begin with a space\n\
+character are not saved in the history list.  A value of @code{ignoredups}\n\
+causes lines matching the previous history entry to not be saved.\n\
+A value of @code{ignoreboth} is shorthand for @code{ignorespace} and\n\
+@code{ignoredups}.  A value of @code{erasedups} causes all previous lines\n\
+matching the current line to be removed from the history list before that\n\
+line is saved.  Any value not in the above list is ignored.  If\n\
+@code{history_control} is the empty string, all commands are saved on\n\
+the history list, subject to the value of @code{saving_history}.\n\
+@seealso{history_file, history_size, history_timestamp_format_string, saving_history}\n\
+@end deftypefn")
+{
+  std::string saved_history_control = Vhistory_control;
+
+  octave_value retval = SET_INTERNAL_VARIABLE (history_control);
+
+  if (Vhistory_control != saved_history_control)
+    command_history::process_histcontrol (Vhistory_control);
+
+  return retval;
+}
+
 DEFUN (history_size, args, nargout,
   "-*- texinfo -*-\n\
 @deftypefn  {Built-in Function} {@var{val} =} history_size ()\n\
@@ -716,7 +768,7 @@
 @deftypefnx {Built-in Function} {@var{old_val} =} saving_history (@var{new_val})\n\
 Query or set the internal variable that controls whether commands entered\n\
 on the command line are saved in the history file.\n\
-@seealso{history_file, history_size, history_timestamp_format_string}\n\
+@seealso{history_control, history_file, history_size, history_timestamp_format_string}\n\
 @end deftypefn")
 {
   octave_value retval = SET_INTERNAL_VARIABLE (saving_history);