home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / octave-1.1.1p1-src.tgz / tar.out / fsf / octave / src / octave-hist.cc < prev    next >
C/C++ Source or Header  |  1996-09-28  |  14KB  |  686 lines

  1. // octave-hist.cc                                        -*- C++ -*-
  2. /*
  3.  
  4. Copyright (C) 1992, 1993, 1994, 1995 John W. Eaton
  5.  
  6. This file is part of Octave.
  7.  
  8. Octave is free software; you can redistribute it and/or modify it
  9. under the terms of the GNU General Public License as published by the
  10. Free Software Foundation; either version 2, or (at your option) any
  11. later version.
  12.  
  13. Octave is distributed in the hope that it will be useful, but WITHOUT
  14. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16. for more details.
  17.  
  18. You should have received a copy of the GNU General Public License
  19. along with Octave; see the file COPYING.  If not, write to the Free
  20. Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  21.  
  22. The functions listed below were adapted from similar functions from
  23. GNU Bash, the Bourne Again SHell, copyright (C) 1987, 1989, 1991 Free
  24. Software Foundation, Inc.
  25.  
  26.   do_history         edit_history_readline
  27.   do_edit_history    edit_history_add_hist
  28.  
  29. */
  30.  
  31. #ifdef HAVE_CONFIG_H
  32. #include "config.h"
  33. #endif
  34.  
  35. #include <sys/types.h>
  36. #ifdef HAVE_UNISTD_H
  37. #include <unistd.h>
  38. #endif
  39. #include <fcntl.h>
  40. #include <stdlib.h>
  41. #include <string.h>
  42. #include <signal.h>
  43. #include <fstream.h>
  44. #include <strstream.h>
  45.  
  46. #include "statdefs.h"
  47. #include "utils.h"
  48. #include "error.h"
  49. #include "input.h"
  50. #include "pager.h"
  51. #include "octave.h"
  52. #include "oct-obj.h"
  53. #include "user-prefs.h"
  54. #include "unwind-prot.h"
  55. #include "octave-hist.h"
  56. #include "sighandlers.h"
  57. #include "defun.h"
  58.  
  59. extern "C"
  60. {
  61. #include <readline/history.h>
  62. }
  63.  
  64. // Nonzero means input is coming from temporary history file.
  65. int input_from_tmp_history_file = 0;
  66.  
  67. // Nonzero means we are saving history lines.
  68. int saving_history = 1;
  69.  
  70. // The number of lines to save in the history file.
  71. static int octave_hist_size = 1024;
  72.  
  73. // The name of the history file.
  74. static char *octave_hist_file;
  75.  
  76. // The number of hisory lines we read from the history file.
  77. static int history_lines_in_file = 0;
  78.  
  79. // The number of history lines we've saved so far.
  80. static int history_lines_this_session = 0;
  81.  
  82. // Get some default values, possibly reading them from the
  83. // environment.
  84.  
  85. static int
  86. default_history_size (void)
  87. {
  88.   int size = 1024;
  89.   char *env_size = getenv ("OCTAVE_HISTSIZE");
  90.   if (env_size)
  91.     {
  92.       int val;
  93.       if (sscanf (env_size, "%d", &val) == 1)
  94.     size = val > 0 ? val : 0;
  95.     }
  96.   return size;
  97. }
  98.  
  99. static char *
  100. default_history_file (void)
  101. {
  102.   char *file = 0;
  103.  
  104.   char *env_file = getenv ("OCTAVE_HISTFILE");
  105.   if (env_file)
  106.     {
  107.       fstream f (env_file, (ios::in | ios::out));
  108.       if (f)
  109.     {
  110.       file = strsave (env_file);
  111.       f.close ();
  112.     }
  113.     }
  114.  
  115.   if (! file && home_directory)
  116.     file = strconcat (home_directory, "/.octave_hist");
  117.  
  118.   return file;
  119. }
  120.  
  121. // Prime the history list.
  122.  
  123. void
  124. initialize_history (void)
  125. {
  126.   octave_hist_file = default_history_file ();
  127.   octave_hist_size = default_history_size ();
  128.  
  129.   read_history (octave_hist_file);
  130.   using_history ();
  131.   history_lines_in_file = where_history ();
  132. }
  133.  
  134. void
  135. clean_up_history (void)
  136. {
  137.   stifle_history (octave_hist_size);
  138.   write_history (octave_hist_file);
  139. }
  140.  
  141. void
  142. maybe_save_history (char *s)
  143. {
  144.   if (saving_history)
  145.     {
  146.       add_history (s);
  147.       history_lines_this_session++;
  148.     }
  149. }
  150.  
  151. // Display, save, or load history.  Stolen and modified from bash.
  152. //
  153. // Arg of -w FILENAME means write file, arg of -r FILENAME
  154. // means read file, arg of -q means don't number lines.  Arg of N
  155. // means only display that many items. 
  156.  
  157. void
  158. do_history (int argc, char **argv)
  159. {
  160.   HIST_ENTRY **hlist;
  161.  
  162.   int numbered_output = 1;
  163.  
  164.   while (--argc > 0)
  165.     {
  166.       argv++;
  167.  
  168.       if (*argv[0] == '-' && strlen (*argv) == 2
  169.       && ((*argv)[1] == 'r' || (*argv)[1] == 'w'
  170.           || (*argv)[1] == 'a' || (*argv)[1] == 'n'))
  171.     {
  172.       char *file;
  173.       int result = 0;
  174.  
  175.       if (argc > 1)
  176.         file = *(argv+1);
  177.       else
  178.         file = octave_hist_file;
  179.  
  180.       switch ((*argv)[1])
  181.         {
  182.         case 'a':        // Append `new' lines to file.
  183.           {
  184.         if (history_lines_this_session)
  185.           {
  186.             if (history_lines_this_session < where_history ())
  187.               {
  188. // If the filename was supplied, then create it if it doesn't already
  189. // exist.
  190.             if (file)
  191.               {
  192.                 struct stat buf;
  193.  
  194.                 if (stat (file, &buf) == -1)
  195.                   {
  196.                 int tem;
  197.  
  198.                 tem = open (file, O_CREAT, 0666);
  199.                 close (tem);
  200.                   }
  201.               }
  202.  
  203.             result =
  204.               append_history (history_lines_this_session, file);
  205.             history_lines_in_file += history_lines_this_session;
  206.             history_lines_this_session = 0;
  207.               }
  208.           }
  209.           }
  210.           break;
  211.  
  212.         case 'w':        // Write entire history.
  213.           result = write_history (file);
  214.           break;
  215.  
  216.         case 'r':        // Read entire file.
  217.           result = read_history (file);
  218.           break;
  219.  
  220.         case 'n':        // Read `new' history from file.
  221. // Read all of the lines in the file that we haven't already read.
  222.           using_history ();
  223.           result = read_history_range (file, history_lines_in_file, -1);
  224.           using_history ();
  225.           history_lines_in_file = where_history ();
  226.           break;
  227.         }
  228.       return;
  229.     }
  230.       else if (strcmp (*argv, "-q") == 0)
  231.     numbered_output = 0;
  232.       else if (strcmp (*argv, "--") == 0)
  233.     {
  234.       argc--;
  235.       argv++;
  236.       break;
  237.     }
  238.       else
  239.     break;
  240.     }
  241.  
  242.   int limited = 0;
  243.   int limit = 0;
  244.  
  245.   if (argc > 0)
  246.     {
  247.       limited = 1;
  248.       if (sscanf (*argv, "%d", &limit) != 1)
  249.         {
  250.       if (*argv[0] == '-')
  251.         error ("history: unrecognized option `%s'", *argv);
  252.       else
  253.         error ("history: bad non-numeric arg `%s'", *argv);
  254.       return;
  255.         }
  256.     }
  257.  
  258.   hlist = history_list ();
  259.  
  260.   if (hlist)
  261.     {
  262.       for (int i = 0; hlist[i]; i++)
  263.     ; // Do nothing.
  264.  
  265.       if (limit < 0)
  266.     limit = -limit;
  267.  
  268.       if (!limited)
  269.     i = 0;
  270.       else
  271.     if ((i -= limit) < 0)
  272.       i = 0;
  273.  
  274.       ostrstream output_buf;
  275.  
  276.       while (hlist[i])
  277.     {
  278. //      QUIT;  // in bash: (interrupt_state) throw_to_top_level ();
  279.  
  280.       if (numbered_output)
  281.         output_buf.form ("%5d%c", i + history_base,
  282.                  hlist[i]->data ? '*' : ' '); 
  283.       output_buf << hlist[i]->line << "\n";
  284.       i++;
  285.     }
  286.  
  287.       output_buf << ends;
  288.       maybe_page_output (output_buf);
  289.     }
  290. }
  291.  
  292. // Read the edited history lines from STREAM and return them
  293. // one at a time.  This can read unlimited length lines.  The
  294. //  caller should free the storage.
  295.  
  296. static char *
  297. edit_history_readline (fstream& stream)
  298. {
  299.   char c;
  300.   int line_len = 128;
  301.   int lindex = 0;
  302.   char *line = new char [line_len];
  303.   line[0] = '\0';
  304.  
  305.   while (stream.get (c))
  306.     {
  307.       if (lindex + 2 >= line_len)
  308.     {
  309.       char *tmp_line = new char [line_len += 128];
  310.       strcpy (tmp_line, line);
  311.       delete [] line;
  312.       line = tmp_line;
  313.     }
  314.  
  315.       if (c == '\n')
  316.     {
  317.       line[lindex++] = '\n';
  318.       line[lindex++] = '\0';
  319.       return line;
  320.     }
  321.       else
  322.     line[lindex++] = c;
  323.     }
  324.  
  325.   if (! lindex)
  326.     {
  327.       delete [] line;
  328.       return 0;
  329.     }
  330.  
  331.   if (lindex + 2 >= line_len)
  332.     {
  333.       char *tmp_line = new char [lindex+3];
  334.       strcpy (tmp_line, line);
  335.       delete [] line;
  336.       line = tmp_line;
  337.     }
  338.  
  339. // Finish with newline if none in file.
  340.  
  341.   line[lindex++] = '\n';
  342.   line[lindex++] = '\0';
  343.   return line;
  344. }
  345.  
  346. extern "C"
  347. {
  348.   HIST_ENTRY *history_get ();
  349. }
  350.  
  351. // Use `command' to replace the last entry in the history list, which,
  352. // by this time, is `run_history blah...'.  The intent is that the
  353. // new command become the history entry, and that `fc' should never
  354. // appear in the history list.  This way you can do `run_history' to
  355. // your heart's content.
  356.  
  357. static void
  358. edit_history_repl_hist (char *command)
  359. {
  360.   if (! command || ! *command)
  361.     return;
  362.  
  363.   HIST_ENTRY **hlist = history_list ();
  364.  
  365.   if (! hlist)
  366.     return;
  367.  
  368.   for (int i = 0; hlist[i]; i++)
  369.     ; // Count 'em.
  370.   i--;
  371.  
  372. // History_get () takes a parameter that should be offset by history_base.
  373.  
  374. // Don't free this.
  375.   HIST_ENTRY *histent = history_get (history_base + i);
  376.   if (! histent)
  377.     return;
  378.  
  379.   char *data = 0;
  380.   if (histent->data)
  381.     {
  382.       int len = strlen (histent->data);
  383.       data = (char *) malloc (len);
  384.       strcpy (data, histent->data);
  385.     }
  386.  
  387.   int n = strlen (command);
  388.  
  389.   if (command[n - 1] == '\n')
  390.     command[n - 1] = '\0';
  391.  
  392.   if (command && *command)
  393.     {
  394.       HIST_ENTRY *discard = replace_history_entry (i, command, data);
  395.       if (discard)
  396.     {
  397.       if (discard->line)
  398.         free (discard->line);
  399.  
  400.       free ((char *) discard);
  401.     }
  402.     }
  403. }
  404.  
  405. static void
  406. edit_history_add_hist (char *line)
  407. {
  408.   if (line)
  409.     {
  410.       int len = strlen (line);
  411.       if (len > 0 && line[len-1] == '\n')
  412.     line[len-1] = '\0';
  413.  
  414.       if (line[0] != '\0')
  415.     add_history (line);
  416.     }
  417. }
  418.  
  419. #define histline(i) (hlist[(i)]->line)
  420.  
  421. static char *
  422. mk_tmp_hist_file (int argc, char **argv, int insert_curr, char *warn_for)
  423. {
  424.   HIST_ENTRY **hlist;
  425.  
  426.   hlist = history_list ();
  427.  
  428.   int hist_count = 0;
  429.  
  430.   while (hlist[hist_count++])
  431.     ; // Find the number of items in the history list.
  432.  
  433. // The current command line is already part of the history list by the
  434. // time we get to this point.  Delete it from the list.
  435.  
  436.   hist_count -= 2;
  437.   if (! insert_curr)
  438.     remove_history (hist_count);
  439.   hist_count--;
  440.  
  441. // If no numbers have been specified, the default is to edit the last
  442. // command in the history list.
  443.  
  444.   int hist_end = hist_count;
  445.   int hist_beg = hist_count;
  446.   int reverse = 0;
  447.  
  448. // Process options
  449.  
  450.   int usage_error = 0;
  451.   if (argc == 3)
  452.     {
  453.       argv++;
  454.       if (sscanf (*argv++, "%d", &hist_beg) != 1
  455.       || sscanf (*argv, "%d", &hist_end) != 1)
  456.     usage_error = 1;
  457.       else
  458.     {
  459.       hist_beg--;
  460.       hist_end--;
  461.     }
  462.     }
  463.   else if (argc == 2)
  464.     {
  465.       argv++;
  466.       if (sscanf (*argv++, "%d", &hist_beg) != 1)
  467.     usage_error = 1;
  468.       else
  469.     {
  470.       hist_beg--;
  471.       hist_end = hist_beg;
  472.     }
  473.     }
  474.  
  475.   if (hist_beg < 0 || hist_end < 0 || hist_beg > hist_count
  476.       || hist_end > hist_count)
  477.     {
  478.       error ("%s: history specification out of range", warn_for);
  479.       return 0;
  480.     }
  481.  
  482.   if (usage_error)
  483.     {
  484.       usage ("%s [first] [last]", warn_for);
  485.       return 0;
  486.     }
  487.  
  488.   if (hist_end < hist_beg)
  489.     {
  490.       int t = hist_end;
  491.       hist_end = hist_beg;
  492.       hist_beg = t;
  493.       reverse = 1;
  494.     }
  495.  
  496.   char *name = octave_tmp_file_name ();
  497.  
  498.   fstream file (name, ios::out);
  499.  
  500.   if (! file)
  501.     {
  502.       error ("%s: couldn't open temporary file `%s'", warn_for, name);
  503.       return 0;
  504.     }
  505.  
  506.   if (reverse)
  507.     {
  508.       for (int i = hist_end; i >= hist_beg; i--)
  509.     file << histline (i) << "\n";
  510.     }
  511.   else
  512.     {
  513.       for (int i = hist_beg; i <= hist_end; i++)
  514.     file << histline (i) << "\n";
  515.     }
  516.  
  517.   file.close ();
  518.  
  519.   return strsave (name);
  520. }
  521.  
  522. void
  523. do_edit_history (int argc, char **argv)
  524. {
  525.   char *name = mk_tmp_hist_file (argc, argv, 0, "edit_history");
  526.  
  527.   if (! name)
  528.     return;
  529.  
  530. // Call up our favorite editor on the file of commands.
  531.  
  532.   ostrstream buf;
  533.   buf << user_pref.editor << " " << name << ends;
  534.   char *cmd = buf.str ();
  535.  
  536. // Ignore interrupts while we are off editing commands.  Should we
  537. // maybe avoid using system()?
  538.  
  539.   volatile sig_handler *saved_sigint_handler = signal (SIGINT, SIG_IGN);
  540.   system (cmd);
  541.   signal (SIGINT, saved_sigint_handler);
  542.  
  543. // Write the commands to the history file since parse_and_execute
  544. // disables command line history while it executes.
  545.  
  546.   fstream file (name, ios::in);
  547.  
  548.   char *line;
  549.   int first = 1;
  550.   while ((line = edit_history_readline (file)) != 0)
  551.     {
  552.  
  553. // Skip blank lines
  554.  
  555.       if (line[0] == '\n')
  556.     {
  557.       delete [] line;
  558.       continue;
  559.     }
  560.  
  561.       if (first)
  562.     {
  563.       first = 0;
  564.       edit_history_repl_hist (line);
  565.     }
  566.       else
  567.     edit_history_add_hist (line);
  568.     }
  569.  
  570.   file.close ();
  571.  
  572. // Turn on command echo, so the output from this will make better sense.
  573.  
  574.   begin_unwind_frame ("do_edit_history");
  575.   unwind_protect_int (echo_input);
  576.   unwind_protect_int (input_from_tmp_history_file);
  577.   echo_input = 1;
  578.   input_from_tmp_history_file = 1;
  579.  
  580.   parse_and_execute (name, 1);
  581.  
  582.   run_unwind_frame ("do_edit_history");
  583.  
  584. // Delete the temporary file.  Should probably be done with an
  585. // unwind_protect.
  586.  
  587.   unlink (name);
  588.  
  589.   delete [] name;
  590. }
  591.  
  592. void
  593. do_run_history (int argc, char **argv)
  594. {
  595.   char *name = mk_tmp_hist_file (argc, argv, 1, "run_history");
  596.  
  597.   if (! name)
  598.     return;
  599.  
  600. // Turn on command echo, so the output from this will make better sense.
  601.  
  602.   begin_unwind_frame ("do_run_history");
  603.   unwind_protect_int (echo_input);
  604.   unwind_protect_int (input_from_tmp_history_file);
  605.   echo_input = 1;
  606.   input_from_tmp_history_file = 1;
  607.  
  608.   parse_and_execute (name, 1);
  609.  
  610.   run_unwind_frame ("do_run_history");
  611.  
  612. // Delete the temporary file.  Should probably be done with an
  613. // unwind_protect.
  614.  
  615.   unlink (name);
  616.  
  617.   delete [] name;
  618. }
  619.  
  620. int
  621. current_history_number (void)
  622. {
  623.   using_history ();
  624.  
  625.   if (octave_hist_size > 0)
  626.     return history_base + where_history ();
  627.   else
  628.     return -1;
  629.  
  630. }
  631.  
  632. DEFUN_TEXT ("edit_history", Fedit_history, Sedit_history, -1, 1,
  633.   "edit_history [first] [last]\n\
  634. \n\
  635. edit commands from the history list")
  636. {
  637.   Octave_object retval;
  638.  
  639.   DEFINE_ARGV("edit_history");
  640.  
  641.   do_edit_history (argc, argv);
  642.  
  643.   DELETE_ARGV;
  644.  
  645.   return retval;
  646. }
  647.  
  648. DEFUN_TEXT ("history", Fhistory, Shistory, -1, 1,
  649.   "history [N] [-w file] [-r file] [-q]\n\
  650. \n\
  651. display, save, or load command history")
  652. {
  653.   Octave_object retval;
  654.  
  655.   DEFINE_ARGV("history");
  656.  
  657.   do_history (argc, argv);
  658.  
  659.   DELETE_ARGV;
  660.  
  661.   return retval;
  662. }
  663.  
  664. DEFUN_TEXT ("run_history", Frun_history, Srun_history, -1, 1,
  665.   "run_history [first] [last]\n\
  666. \n\
  667. run commands from the history list")
  668. {
  669.   Octave_object retval;
  670.  
  671.   DEFINE_ARGV("run_history");
  672.  
  673.   do_run_history (argc, argv);
  674.  
  675.   DELETE_ARGV;
  676.  
  677.   return retval;
  678. }
  679.  
  680. /*
  681. ;;; Local Variables: ***
  682. ;;; mode: C++ ***
  683. ;;; page-delimiter: "^/\\*" ***
  684. ;;; End: ***
  685. */
  686.