diff src/oct-hist.cc @ 1:78fd87e624cb

[project @ 1993-08-08 01:13:40 by jwe] Initial revision
author jwe
date Sun, 08 Aug 1993 01:13:40 +0000
parents
children bd04d91a7a4a
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/src/oct-hist.cc
@@ -0,0 +1,521 @@
+// octave-hist.cc                                        -*- C++ -*-
+/*
+
+Copyright (C) 1992, 1993 John W. Eaton
+
+This file is part of Octave.
+
+Octave is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+Octave is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, write to the Free
+Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+The functions listed below were adapted from similar functions from
+GNU Bash, the Bourne Again SHell, copyright (C) 1987, 1989, 1991 Free
+Software Foundation, Inc.
+
+  do_history         edit_history_readline
+  do_edit_history    edit_history_add_hist
+
+*/
+
+#ifdef __GNUG__
+#pragma implementation
+#endif
+
+#include <sys/types.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <fstream.h>
+#include <strstream.h>
+
+#include "statdefs.h"
+#include "utils.h"
+#include "error.h"
+#include "input.h"
+#include "octave.h"
+#include "unwind-prot.h"
+#include "octave-hist.h"
+#include "sighandlers.h"
+
+extern "C"
+{
+#include <readline/history.h>
+}
+
+// Nonzero means we are saving history lines.
+int saving_history = 1;
+
+// The number of lines to save in the history file.
+static int octave_hist_size = 1024;
+
+// The name of the history file.
+static char *octave_hist_file;
+
+// The number of hisory lines we read from the history file.
+static int history_lines_in_file = 0;
+
+// The number of history lines we've saved so far.
+static int history_lines_this_session = 0;
+
+/*
+ * Get some default values, possibly reading them from the
+ * environment.
+ */
+static int
+default_history_size (void)
+{
+  int size = 1024;
+  char *env_size = getenv ("OCTAVE_HISTSIZE");
+  if (env_size != (char *) NULL)
+    {
+      int val;
+      if (sscanf (env_size, "%d", &val) == 1)
+	size = val > 0 ? val : 0;
+    }
+  return size;
+}
+
+static char *
+default_history_file (void)
+{
+  char *file = (char *) NULL;;
+
+  char *env_file = getenv ("OCTAVE_HISTFILE");
+  if (env_file != (char *) NULL)
+    {
+      fstream f (env_file, (ios::in | ios::out));
+      if (f != 0)
+	{
+	  file = strsave (env_file);
+	  f.close ();
+	}
+    }
+
+  if (file == (char *) NULL)
+    {
+      if (home_directory != NULL)
+	file = strconcat (home_directory, "/.octave_hist");
+    }
+
+  return file;
+}
+
+/*
+ * Prime the history list.
+ */
+void
+initialize_history (void)
+{
+  octave_hist_file = default_history_file ();
+  octave_hist_size = default_history_size ();
+
+  read_history (octave_hist_file);
+  using_history ();
+  history_lines_in_file = where_history ();
+}
+
+void
+clean_up_history (void)
+{
+  stifle_history (octave_hist_size);
+  write_history (octave_hist_file);
+}
+
+void
+maybe_save_history (char *s)
+{
+  if (saving_history)
+    {
+      add_history (s);
+      history_lines_this_session++;
+    }
+}
+
+/*
+ * Display, save, or load history.  Stolen and modified from bash.
+ *
+ * Arg of -w FILENAME means write file, arg of -r FILENAME
+ * means read file, arg of -q means don't number lines.  Arg of N
+ * means only display that many items. 
+ */
+void
+do_history (int argc, char **argv)
+{
+  HIST_ENTRY **hlist;
+
+  int numbered_output = 1;
+
+  while (--argc > 0)
+    {
+      argv++;
+
+      if (*argv[0] == '-' && strlen (*argv) == 2
+	  && ((*argv)[1] == 'r' || (*argv)[1] == 'w'
+	      || (*argv)[1] == 'a' || (*argv)[1] == 'n'))
+	{
+	  char *file;
+	  int result = 0;
+
+	  if (argc > 1)
+	    file = *(argv+1);
+	  else
+	    file = octave_hist_file;
+
+	  switch ((*argv)[1])
+	    {
+	    case 'a':		// Append `new' lines to file.
+	      {
+		if (history_lines_this_session)
+		  {
+		    if (history_lines_this_session < where_history ())
+		      {
+// If the filename was supplied, then create it if it doesn't already
+// exist.
+			if (file)
+			  {
+			    struct stat buf;
+
+			    if (stat (file, &buf) == -1)
+			      {
+				int tem;
+
+				tem = open (file, O_CREAT, 0666);
+				close (tem);
+			      }
+			  }
+
+			result =
+			  append_history (history_lines_this_session, file);
+			history_lines_in_file += history_lines_this_session;
+			history_lines_this_session = 0;
+		      }
+		  }
+	      }
+	      break;
+	    case 'w':		// Write entire history.
+	      result = write_history (file);
+	      break;
+	    case 'r':		// Read entire file.
+	      result = read_history (file);
+	      break;
+	    case 'n':		// Read `new' history from file.
+// Read all of the lines in the file that we haven't already read.
+	      using_history ();
+	      result = read_history_range (file, history_lines_in_file, -1);
+	      using_history ();
+	      history_lines_in_file = where_history ();
+	      break;
+	    }
+	  return;
+	}
+      else if (strcmp (*argv, "-q") == 0)
+	numbered_output = 0;
+      else if (strcmp (*argv, "--") == 0)
+	{
+	  argc--;
+	  argv++;
+	  break;
+	}
+      else
+	break;
+    }
+
+  int limited = 0;
+  int limit = 0;
+
+  if (argc > 0)
+    {
+      limited = 1;
+      if (sscanf (*argv, "%d", &limit) != 1)
+        {
+	  if (*argv[0] == '-')
+	    message ("history", "unrecognized option `%s'", *argv);
+	  else
+	    message ("history", "bad non-numeric arg `%s'", *argv);
+	  return;
+        }
+    }
+
+  hlist = history_list ();
+
+  if (hlist)
+    {
+      for (int i = 0; hlist[i] != (HIST_ENTRY *) NULL; i++)
+	; // Do nothing.
+
+      if (limit < 0)
+	limit = -limit;
+
+      if (!limited)
+	i = 0;
+      else
+	if ((i -= limit) < 0)
+	  i = 0;
+
+      while (hlist[i])
+	{
+//	  QUIT;  // in bash: (interrupt_state) throw_to_top_level ();
+
+	  if (numbered_output)
+	    cerr.form ("%5d%c", i + history_base, hlist[i]->data ? '*' : ' ');
+	  cerr << hlist[i]->line << "\n";
+	  i++;
+	}
+    }
+}
+
+/*
+ * Read the edited history lines from STREAM and return them
+ * one at a time.  This can read unlimited length lines.  The
+ *  caller should free the storage.
+ */
+static char *
+edit_history_readline (fstream& stream)
+{
+  char c;
+  int line_len = 128;
+  int lindex = 0;
+  char *line = new char [line_len];
+  line[0] = '\0';
+
+  while (stream.get (c))
+    {
+      if (lindex + 2 >= line_len)
+	{
+	  char *tmp_line = new char [line_len += 128];
+	  strcpy (tmp_line, line);
+	  delete [] line;
+	  line = tmp_line;
+	}
+
+      if (c == '\n')
+	{
+	  line[lindex++] = '\n';
+	  line[lindex++] = '\0';
+	  return line;
+	}
+      else
+	line[lindex++] = c;
+    }
+
+  if (! lindex)
+    {
+      delete [] line;
+      return (char *) NULL;
+    }
+
+  if (lindex + 2 >= line_len)
+    {
+      char *tmp_line = new char [lindex+3];
+      strcpy (tmp_line, line);
+      delete [] line;
+      line = tmp_line;
+    }
+
+// Finish with newline if none in file.
+
+  line[lindex++] = '\n';
+  line[lindex++] = '\0';
+  return line;
+}
+
+static void
+edit_history_add_hist (char *line)
+{
+  if (line != (char *) NULL)
+    {
+      int len = strlen (line);
+      if (len > 0 && line[len-1] == '\n')
+	line[len-1] = '\0';
+
+      if (line[0] != '\0')
+	add_history (line);
+    }
+}
+
+#define histline(i) (hlist[(i)]->line)
+
+#define EDIT_COMMAND "${EDITOR:-vi}"
+
+void
+do_edit_history (int argc, char **argv)
+{
+  HIST_ENTRY **hlist;
+
+  hlist = history_list ();
+
+  int hist_count = 0;
+
+  while (hlist[hist_count++] != (HIST_ENTRY *) NULL)
+    ; // Find the number of items in the history list.
+
+// The current command line is already part of the history list by the
+// time we get to this point.  Delete it from the list.
+
+  hist_count -= 2;
+  remove_history (hist_count);
+  hist_count--;
+
+// If no numbers have been specified, the default is to edit the last
+// command in the history list.
+
+  int hist_end = hist_count;
+  int hist_beg = hist_count;
+  int reverse = 0;
+
+// Process options
+
+  int usage_error = 0;
+  if (argc == 3)
+    {
+      argv++;
+      if (sscanf (*argv++, "%d", &hist_beg) != 1
+	  || sscanf (*argv, "%d", &hist_end) != 1)
+	usage_error = 1;
+      else
+	{
+	  hist_beg--;
+	  hist_end--;
+	}
+    }
+  else if (argc == 2)
+    {
+      argv++;
+      if (sscanf (*argv++, "%d", &hist_beg) != 1)
+	usage_error = 1;
+      else
+	{
+	  hist_beg--;
+	  hist_end = hist_beg;
+	}
+    }
+
+  if (hist_beg < 0 || hist_end < 0 || hist_beg > hist_count
+      || hist_end > hist_count)
+    {
+      error ("history specification out of range");
+      return;
+    }
+
+  if (usage_error)
+    {
+      usage ("edit_history [first] [last]");
+      return;
+    }
+
+  if (hist_end < hist_beg)
+    {
+      int t = hist_end;
+      hist_end = hist_beg;
+      hist_beg = t;
+      reverse = 1;
+    }
+
+  char *name = tmpnam ((char *) NULL);
+
+  fstream file (name, ios::out);
+  if (! file)
+    {
+      error ("edit_history: couldn't open temporary file `%s'", name);
+      return;
+    }
+
+  if (reverse)
+    {
+      for (int i = hist_end; i >= hist_beg; i--)
+	file << histline (i) << "\n";
+    }
+  else
+    {
+      for (int i = hist_beg; i <= hist_end; i++)
+	file << histline (i) << "\n";
+    }
+
+  file.close ();
+
+// Call up our favorite editor on the file of commands.
+
+  ostrstream buf;
+  buf << EDIT_COMMAND << " " << name << ends;
+  char *cmd = buf.str ();
+
+// Ignore interrupts while we are off editing commands.  Should we
+// maybe avoid using system()?  There still seems to be a problem with
+// properly waiting for emacsclient.
+
+  volatile sig_handler *saved_sigint_handler = signal (SIGINT, SIG_IGN);
+  system (cmd);
+  signal (SIGINT, saved_sigint_handler);
+
+// Write the commands to the history file since parse_and_execute
+// disables command line history while it executes.
+
+  file.open (name, ios::in);
+
+  char *line;
+  while ((line = edit_history_readline (file)) != NULL)
+    {
+
+// Skip blank lines
+
+      if (line[0] == '\n')
+	{
+	  delete [] line;
+	  continue;
+	}
+
+      edit_history_add_hist (line);
+    }
+
+  file.close ();
+
+// Turn on command echo, so the output from this will make better sense.
+
+  begin_unwind_frame ("do_edit_history");
+  unwind_protect_int (echo_input);
+  echo_input = 1;
+
+  parse_and_execute (name, 1);
+
+  run_unwind_frame ("do_edit_history");
+
+// Delete the temporary file.  Should probably be done with an
+// unwind_protect.
+
+  unlink (name);
+}
+
+int
+current_history_number (void)
+{
+  using_history ();
+
+  if (octave_hist_size > 0)
+    return history_base + where_history ();
+  else
+    return -1;
+
+}
+
+/*
+;;; Local Variables: ***
+;;; mode: C++ ***
+;;; page-delimiter: "^/\\*" ***
+;;; End: ***
+*/