1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1993-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING.  If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 /*
27 
28 The functions listed below were adapted from similar functions from
29 GNU Bash, the Bourne Again SHell, copyright (C) 1987, 1989, 1991 Free
30 Software Foundation, Inc.
31 
32   do_history         edit_history_readline
33   do_edit_history    edit_history_add_hist
34 
35 */
36 
37 #if defined (HAVE_CONFIG_H)
38 #  include "config.h"
39 #endif
40 
41 #include <cstdlib>
42 #include <cstring>
43 
44 #include <fstream>
45 #include <string>
46 
47 #include "cmd-hist.h"
48 #include "file-ops.h"
49 #include "lo-mappers.h"
50 #include "lo-sysdep.h"
51 #include "oct-env.h"
52 #include "oct-time.h"
53 #include "str-vec.h"
54 #include "unistd-wrappers.h"
55 
56 #include "defun.h"
57 #include "error.h"
58 #include "errwarn.h"
59 #include "event-manager.h"
60 #include "input.h"
61 #include "oct-hist.h"
62 #include "ovl.h"
63 #include "pager.h"
64 #include "parse.h"
65 #include "sighandlers.h"
66 #include "sysdep.h"
67 #include "interpreter.h"
68 #include "interpreter-private.h"
69 #include "unwind-prot.h"
70 #include "utils.h"
71 #include "variables.h"
72 
73 namespace octave
74 {
75   // Read the edited history lines from STREAM and return them
76   // one at a time.  This can read unlimited length lines.  The
77   // caller should free the storage.
78 
79   static char *
edit_history_readline(std::fstream & stream)80   edit_history_readline (std::fstream& stream)
81   {
82     char c;
83     int line_len = 128;
84     int lindex = 0;
85     char *line = new char [line_len];
86     line[0] = '\0';
87 
88     while (stream.get (c))
89       {
90         if (lindex + 2 >= line_len)
91           {
92             char *tmp_line = new char [line_len += 128];
93             strcpy (tmp_line, line);
94             delete [] line;
95             line = tmp_line;
96           }
97 
98         if (c == '\n')
99           {
100             line[lindex++] = '\n';
101             line[lindex++] = '\0';
102             return line;
103           }
104         else
105           line[lindex++] = c;
106       }
107 
108     if (! lindex)
109       {
110         delete [] line;
111         return nullptr;
112       }
113 
114     if (lindex + 2 >= line_len)
115       {
116         char *tmp_line = new char [lindex+3];
117         strcpy (tmp_line, line);
118         delete [] line;
119         line = tmp_line;
120       }
121 
122     // Finish with newline if none in file.
123 
124     line[lindex++] = '\n';
125     line[lindex++] = '\0';
126     return line;
127   }
128 
129   static void
edit_history_add_hist(const std::string & line)130   edit_history_add_hist (const std::string& line)
131   {
132     if (! line.empty ())
133       {
134         std::string tmp = line;
135 
136         int len = tmp.length ();
137 
138         if (len > 0 && tmp[len-1] == '\n')
139           tmp.resize (len - 1);
140 
141         if (! tmp.empty ())
142           {
143             if (command_history::add (tmp))
144               {
145                 event_manager& evmgr
146                   = __get_event_manager__ ("edit_history_add_hist");
147 
148                 evmgr.append_history (tmp);
149               }
150           }
151       }
152   }
153 
154   static bool
get_int_arg(const octave_value & arg,int & val)155   get_int_arg (const octave_value& arg, int& val)
156   {
157     bool ok = true;
158 
159     if (arg.is_string ())
160       {
161         std::string tmp = arg.string_value ();
162 
163         ok = sscanf (tmp.c_str (), "%d", &val) == 1;
164       }
165     else if (arg.isnumeric ())
166       val = arg.int_value ();
167     else
168       ok = false;
169 
170     return ok;
171   }
172 
173   static std::string
mk_tmp_hist_file(const octave_value_list & args,bool insert_curr,const char * warn_for)174   mk_tmp_hist_file (const octave_value_list& args,
175                     bool insert_curr, const char *warn_for)
176   {
177     string_vector hlist = command_history::list ();
178 
179     int hist_count = hlist.numel () - 1;  // switch to zero-based indexing
180 
181     // The current command line is already part of the history list by
182     // the time we get to this point.  Delete the cmd from the list when
183     // executing 'edit_history' so that it doesn't show up in the history
184     // but the actual commands performed will.
185 
186     if (! insert_curr)
187       command_history::remove (hist_count);
188 
189     hist_count--;  // skip last entry in history list
190 
191     // If no numbers have been specified, the default is to edit the
192     // last command in the history list.
193 
194     int hist_beg = hist_count;
195     int hist_end = hist_count;
196 
197     bool reverse = false;
198 
199     // Process options.
200 
201     int nargin = args.length ();
202 
203     if (nargin == 2)
204       {
205         if (! get_int_arg (args(0), hist_beg)
206             || ! get_int_arg (args(1), hist_end))
207           error ("%s: arguments must be integers", warn_for);
208 
209         if (hist_beg < 0)
210           hist_beg += (hist_count + 1);
211         else
212           hist_beg--;
213         if (hist_end < 0)
214           hist_end += (hist_count + 1);
215         else
216           hist_end--;
217       }
218     else if (nargin == 1)
219       {
220         if (! get_int_arg (args(0), hist_beg))
221           error ("%s: argument must be an integer", warn_for);
222 
223         if (hist_beg < 0)
224           hist_beg += (hist_count + 1);
225         else
226           hist_beg--;
227 
228         hist_end = hist_beg;
229       }
230 
231     if (hist_beg > hist_count || hist_end > hist_count)
232       error ("%s: history specification out of range", warn_for);
233 
234     if (hist_end < hist_beg)
235       {
236         std::swap (hist_end, hist_beg);
237         reverse = true;
238       }
239 
240     std::string name = sys::tempnam ("", "oct-");
241 
242     std::ofstream file = sys::ofstream (name.c_str (), std::ios::out);
243 
244     if (! file)
245       error ("%s: couldn't open temporary file '%s'", warn_for,
246              name.c_str ());
247 
248     if (reverse)
249       {
250         for (int i = hist_end; i >= hist_beg; i--)
251           file << hlist[i] << "\n";
252       }
253     else
254       {
255         for (int i = hist_beg; i <= hist_end; i++)
256           file << hlist[i] << "\n";
257       }
258 
259     file.close ();
260 
261     return name;
262   }
263 
264   static void
unlink_cleanup(const char * file)265   unlink_cleanup (const char *file)
266   {
267     octave_unlink_wrapper (file);
268   }
269 
history_system(interpreter & interp)270   history_system::history_system (interpreter& interp)
271     : m_interpreter (interp), m_input_from_tmp_file (false),
272       m_timestamp_format_string (default_timestamp_format ())
273   { }
274 
initialize(bool read_history_file)275   void history_system::initialize (bool read_history_file)
276   {
277     command_history::initialize (read_history_file, default_file (),
278                                  default_size (),
279                                  sys::env::getenv ("OCTAVE_HISTCONTROL"));
280 
281     event_manager& evmgr = m_interpreter.get_event_manager ();
282 
283     evmgr.set_history (command_history::list ());
284   }
285 
write_timestamp(void)286   void history_system::write_timestamp (void)
287   {
288     sys::localtime now;
289 
290     std::string timestamp = now.strftime (m_timestamp_format_string);
291 
292     if (! timestamp.empty ())
293       {
294         if (command_history::add (timestamp))
295           {
296             event_manager& evmgr = m_interpreter.get_event_manager ();
297 
298             evmgr.append_history (timestamp);
299           }
300       }
301   }
302 
303   octave_value
input_from_tmp_file(const octave_value_list & args,int nargout)304   history_system::input_from_tmp_file (const octave_value_list& args,
305                                        int nargout)
306   {
307     return set_internal_variable (m_input_from_tmp_file, args, nargout,
308                                   "input_from_tmp_file");
309   }
310 
311   octave_value
timestamp_format_string(const octave_value_list & args,int nargout)312   history_system::timestamp_format_string (const octave_value_list& args,
313                                            int nargout)
314   {
315     return set_internal_variable (m_timestamp_format_string, args, nargout,
316                                   "timestamp_format_string");
317   }
318 
319   // Display, save, or load history.  Stolen and modified from bash.
320   //
321   // Arg of -w FILENAME means write file, arg of -r FILENAME
322   // means read file, arg of -q means don't number lines.  Arg of N
323   // means only display that many items.
324 
do_history(const octave_value_list & args,int nargout)325   string_vector history_system::do_history (const octave_value_list& args,
326                                             int nargout)
327   {
328     bool numbered_output = nargout == 0;
329 
330     unwind_protect frame;
331 
332     string_vector hlist;
333 
334     frame.add_fcn (command_history::set_file, command_history::file ());
335 
336     int nargin = args.length ();
337 
338     // Number of history lines to show (-1 = all)
339     int limit = -1;
340 
341     for (octave_idx_type i = 0; i < nargin; i++)
342       {
343         octave_value arg = args(i);
344 
345         std::string option;
346 
347         if (arg.is_string ())
348           option = arg.string_value ();
349         else if (arg.isnumeric ())
350           {
351             limit = arg.int_value ();
352             if (limit < 0)
353               limit = -limit;
354             continue;
355           }
356         else
357           err_wrong_type_arg ("history", arg);
358 
359         event_manager& evmgr = m_interpreter.get_event_manager ();
360 
361         if (option == "-r" || option == "-w" || option == "-a"
362             || option == "-n")
363           {
364             if (i < nargin - 1)
365               {
366                 std::string fname
367                   = args(++i).xstring_value ("history: filename must be a string for %s option",
368                                              option.c_str ());
369 
370                 command_history::set_file (fname);
371               }
372             else
373               command_history::set_file (default_file ());
374 
375             if (option == "-a")
376               // Append 'new' lines to file.
377               command_history::append ();
378 
379             else if (option == "-w")
380               // Write entire history.
381               command_history::write ();
382 
383             else if (option == "-r")
384               {
385                 // Read entire file.
386                 command_history::read ();
387                 evmgr.set_history (command_history::list ());
388               }
389 
390             else if (option == "-n")
391               {
392                 // Read 'new' history from file.
393                 command_history::read_range ();
394                 evmgr.set_history (command_history::list ());
395               }
396 
397             else
398               panic_impossible ();
399 
400             return hlist;
401           }
402         else if (option == "-c")
403           {
404             command_history::clear ();
405             evmgr.clear_history ();
406           }
407         else if (option == "-q")
408           numbered_output = false;
409         else if (option == "--")
410           {
411             i++;
412             break;
413           }
414         else
415           {
416             // The last argument found in the command list that looks like
417             // an integer will be used
418             int tmp;
419 
420             if (sscanf (option.c_str (), "%d", &tmp) == 1)
421               {
422                 if (tmp > 0)
423                   limit = tmp;
424                 else
425                   limit = -tmp;
426               }
427 
428             else
429               {
430                 if (option.length () > 0 && option[0] == '-')
431                   error ("history: unrecognized option '%s'", option.c_str ());
432                 else
433                   error ("history: bad non-numeric arg '%s'", option.c_str ());
434               }
435           }
436       }
437 
438     hlist = command_history::list (limit, numbered_output);
439 
440     int len = hlist.numel ();
441 
442     if (nargout == 0)
443       {
444         for (octave_idx_type i = 0; i < len; i++)
445           octave_stdout << hlist[i] << "\n";
446       }
447 
448     return hlist;
449   }
450 
do_edit_history(const octave_value_list & args)451   void history_system::do_edit_history (const octave_value_list& args)
452   {
453     std::string name = mk_tmp_hist_file (args, false, "edit_history");
454 
455     if (name.empty ())
456       return;
457 
458     // Call up our favorite editor on the file of commands.
459 
460     environment& env = m_interpreter.get_environment ();
461     std::string cmd = env.editor ();
462     cmd.append (R"( ")" + name + '"');
463 
464     // Ignore interrupts while we are off editing commands.  Should we
465     // maybe avoid using system()?
466 
467     volatile interrupt_handler old_interrupt_handler
468       = ignore_interrupts ();
469 
470     int status = system (cmd.c_str ());
471 
472     set_interrupt_handler (old_interrupt_handler);
473 
474     // Check if text edition was successful.  Abort the operation
475     // in case of failure.
476     if (status != EXIT_SUCCESS)
477       error ("edit_history: text editor command failed");
478 
479     // Write the commands to the history file since source_file
480     // disables command line history while it executes.
481 
482     std::fstream file = sys::fstream (name.c_str (), std::ios::in);
483 
484     char *line;
485     //int first = 1;
486     while ((line = edit_history_readline (file)) != nullptr)
487       {
488         // Skip blank lines.
489 
490         if (line[0] == '\n')
491           {
492             delete [] line;
493             continue;
494           }
495 
496         edit_history_add_hist (line);
497 
498         delete [] line;
499       }
500 
501     file.close ();
502 
503     unwind_protect frame;
504 
505     frame.add_fcn (unlink_cleanup, name.c_str ());
506     frame.protect_var (m_input_from_tmp_file);
507 
508     m_input_from_tmp_file = true;
509 
510     // FIXME: instead of sourcing a file, we should just iterate through
511     // the list of commands, parsing and executing them one at a time as
512     // if they were entered interactively.
513 
514     source_file (name);
515   }
516 
517   void history_system::do_run_history (const octave_value_list& args)
518   {
519     std::string name = mk_tmp_hist_file (args, false, "run_history");
520 
521     if (name.empty ())
522       return;
523 
524     unwind_protect frame;
525 
526     frame.add_fcn (unlink_cleanup, name.c_str ());
527     frame.protect_var (m_input_from_tmp_file);
528 
529     m_input_from_tmp_file = true;
530 
531     // FIXME: instead of sourcing a file, we should just iterate through
532     // the list of commands, parsing and executing them one at a time as
533     // if they were entered interactively.
534 
535     source_file (name);
536   }
537 
538   std::string history_system::default_file (void)
539   {
540     std::string file;
541 
542     std::string env_file = sys::env::getenv ("OCTAVE_HISTFILE");
543 
544     if (! env_file.empty ())
545       file = env_file;
546 
547     if (file.empty ())
548       file = sys::file_ops::concat (sys::env::get_home_directory (),
549                                     ".octave_hist");
550 
551     return file;
552   }
553 
554   int history_system::default_size (void)
555   {
556     int size = 1000;
557 
558     std::string env_size = sys::env::getenv ("OCTAVE_HISTSIZE");
559 
560     if (! env_size.empty ())
561       {
562         int val;
563 
564         if (sscanf (env_size.c_str (), "%d", &val) == 1)
565           size = (val > 0 ? val : 0);
566       }
567 
568     return size;
569   }
570 
571   std::string history_system::default_timestamp_format (void)
572   {
573     return
574       "# Octave " OCTAVE_VERSION ", %a %b %d %H:%M:%S %Y %Z <"
575       + sys::env::get_user_name ()
576       + '@'
577       + sys::env::get_host_name ()
578       + '>';
579   }
580 }
581 
582 DEFMETHOD (edit_history, interp, args, ,
583            doc: /* -*- texinfo -*-
584 @deftypefn  {} {} edit_history
585 @deftypefnx {} {} edit_history @var{cmd_number}
586 @deftypefnx {} {} edit_history @var{first} @var{last}
587 Edit the history list using the editor named by the variable @env{EDITOR}.
588 
589 The commands to be edited are first copied to a temporary file.  When you
590 exit the editor, Octave executes the commands that remain in the file.  It
591 is often more convenient to use @code{edit_history} to define functions
592 rather than attempting to enter them directly on the command line.
593 The block of commands is executed as soon as you exit the editor.
594 To avoid executing any commands, simply delete all the lines from the buffer
595 before leaving the editor.
596 
597 When invoked with no arguments, edit the previously executed command;
598 With one argument, edit the specified command @var{cmd_number};
599 With two arguments, edit the list of commands between @var{first} and
600 @var{last}.  Command number specifiers may also be negative where -1
601 refers to the most recently executed command.
602 The following are equivalent and edit the most recently executed command.
603 
604 @example
605 @group
606 edit_history
607 edit_history -1
608 @end group
609 @end example
610 
611 When using ranges, specifying a larger number for the first command than the
612 last command reverses the list of commands before they are placed in the
613 buffer to be edited.
614 @seealso{run_history, history}
615 @end deftypefn */)
616 {
617   // FIXME: should this be limited to the top-level context?
618 
619   if (args.length () > 2)
620     print_usage ();
621 
622   octave::history_system& history_sys = interp.get_history_system ();
623 
624   history_sys.do_edit_history (args);
625 
626   return ovl ();
627 }
628 
629 DEFMETHOD (history, interp, args, nargout,
630            doc: /* -*- texinfo -*-
631 @deftypefn  {} {} history
632 @deftypefnx {} {} history @var{opt1} @dots{}
633 @deftypefnx {} {@var{h} =} history ()
634 @deftypefnx {} {@var{h} =} history (@var{opt1}, @dots{})
635 If invoked with no arguments, @code{history} displays a list of commands
636 that you have executed.
637 
638 Valid options are:
639 
640 @table @code
641 @item   @var{n}
642 @itemx -@var{n}
643 Display only the most recent @var{n} lines of history.
644 
645 @item -c
646 Clear the history list.
647 
648 @item -q
649 Don't number the displayed lines of history.  This is useful for cutting
650 and pasting commands using the X Window System.
651 
652 @item -r @var{file}
653 Read the file @var{file}, appending its contents to the current
654 history list.  If the name is omitted, use the default history file
655 (normally @file{~/.octave_hist}).
656 
657 @item -w @var{file}
658 Write the current history to the file @var{file}.  If the name is
659 omitted, use the default history file (normally @file{~/.octave_hist}).
660 @end table
661 
662 For example, to display the five most recent commands that you have
663 typed without displaying line numbers, use the command
664 @kbd{history -q 5}.
665 
666 If invoked with a single output argument, the history will be saved to that
667 argument as a cell string and will not be output to screen.
668 @seealso{edit_history, run_history}
669 @end deftypefn */)
670 {
671   // FIXME: should this be limited to the top-level context?
672 
673   octave::history_system& history_sys = interp.get_history_system ();
674 
675   // Call do_history even if nargout is zero to display history list.
676 
677   string_vector hlist = history_sys.do_history (args, nargout);
678 
679   return nargout > 0 ? ovl (Cell (hlist)) : ovl ();
680 }
681 
682 DEFMETHOD (run_history, interp, args, ,
683            doc: /* -*- texinfo -*-
684 @deftypefn  {} {} run_history
685 @deftypefnx {} {} run_history @var{cmd_number}
686 @deftypefnx {} {} run_history @var{first} @var{last}
687 Run commands from the history list.
688 
689 When invoked with no arguments, run the previously executed command;
690 
691 With one argument, run the specified command @var{cmd_number};
692 
693 With two arguments, run the list of commands between @var{first} and
694 @var{last}.  Command number specifiers may also be negative where -1
695 refers to the most recently executed command.  For example, the command
696 
697 @example
698 @group
699 run_history
700      OR
701 run_history -1
702 @end group
703 @end example
704 
705 @noindent
706 executes the most recent command again.
707 The command
708 
709 @example
710 run_history 13 169
711 @end example
712 
713 @noindent
714 executes commands 13 through 169.
715 
716 Specifying a larger number for the first command than the last command
717 reverses the list of commands before executing them.
718 For example:
719 
720 @example
721 @group
722 disp (1)
723 disp (2)
724 run_history -1 -2
725 @result{}
726  2
727  1
728 @end group
729 @end example
730 
731 @seealso{edit_history, history}
732 @end deftypefn */)
733 {
734   // FIXME: should this be limited to the top-level context?
735 
736   octave::history_system& history_sys = interp.get_history_system ();
737 
738   if (args.length () > 2)
739     print_usage ();
740 
741   history_sys.do_run_history (args);
742 
743   return ovl ();
744 }
745 
746 DEFUN (history_control, args, nargout,
747        doc: /* -*- texinfo -*-
748 @deftypefn  {} {@var{val} =} history_control ()
749 @deftypefnx {} {@var{old_val} =} history_control (@var{new_val})
750 Query or set the internal variable that specifies how commands are saved
751 to the history list.
752 
753 The default value is an empty character string, but may be overridden by the
754 environment variable @w{@env{OCTAVE_HISTCONTROL}}.
755 
756 The value of @code{history_control} is a colon-separated list of values
757 controlling how commands are saved on the history list.  If the list
758 of values includes @code{ignorespace}, lines which begin with a space
759 character are not saved in the history list.  A value of @code{ignoredups}
760 causes lines matching the previous history entry to not be saved.
761 A value of @code{ignoreboth} is shorthand for @code{ignorespace} and
762 @code{ignoredups}.  A value of @code{erasedups} causes all previous lines
763 matching the current line to be removed from the history list before that
764 line is saved.  Any value not in the above list is ignored.  If
765 @code{history_control} is the empty string, all commands are saved on
766 the history list, subject to the value of @code{history_save}.
767 @seealso{history_file, history_size, history_timestamp_format_string, history_save}
768 @end deftypefn */)
769 {
770   octave_value retval;
771 
772   std::string old_history_control = octave::command_history::histcontrol ();
773 
774   std::string tmp = old_history_control;
775 
776   retval = set_internal_variable (tmp, args, nargout, "history_control");
777 
778   if (tmp != old_history_control)
779     octave::command_history::process_histcontrol (tmp);
780 
781   return retval;
782 }
783 
784 DEFUN (history_size, args, nargout,
785        doc: /* -*- texinfo -*-
786 @deftypefn  {} {@var{val} =} history_size ()
787 @deftypefnx {} {@var{old_val} =} history_size (@var{new_val})
788 Query or set the internal variable that specifies how many entries
789 to store in the history file.
790 
791 The default value is @code{1000}, but may be overridden by the environment
792 variable @w{@env{OCTAVE_HISTSIZE}}.
793 @seealso{history_file, history_timestamp_format_string, history_save}
794 @end deftypefn */)
795 {
796   octave_value retval;
797 
798   int old_history_size = octave::command_history::size ();
799 
800   int tmp = old_history_size;
801 
802   retval = set_internal_variable (tmp, args, nargout,
803                                   "history_size", -1,
804                                   std::numeric_limits<int>::max ());
805 
806   if (tmp != old_history_size)
807     octave::command_history::set_size (tmp);
808 
809   return retval;
810 }
811 
812 DEFUN (history_file, args, nargout,
813        doc: /* -*- texinfo -*-
814 @deftypefn  {} {@var{val} =} history_file ()
815 @deftypefnx {} {@var{old_val} =} history_file (@var{new_val})
816 Query or set the internal variable that specifies the name of the file used to
817 store command history.
818 
819 All future commands issued during the current Octave session will be written to
820 this new file (if the current setting of @code{history_save} allows for this).
821 
822 The default value is @file{~/.octave_hist}, but may be overridden by the
823 environment variable @w{@env{OCTAVE_HISTFILE}}.
824 
825 Programming Notes:
826 
827 If you want to permanently change the location of Octave's history file you
828 need to issue the @code{history_file} command in every new Octave session.
829 This can be achieved by using Octave's @file{.octaverc} startup file.
830 
831 If you also want to read the saved history commands of past Octave sessions
832 from this different history file, then you need to use the additional command
833 @code{history -r} after setting the new value of the history file.  Example
834 code in Octave's startup file to do this might look like this:
835 
836 @example
837 @group
838 history_file ("~/new/.octave_hist");
839 if (exist (history_file ()))
840   history ("-r", history_file());
841 endif
842 @end group
843 @end example
844 
845 @seealso{history, history_control, history_save, history_size, history_timestamp_format_string}
846 @end deftypefn */)
847 {
848   octave_value retval;
849 
850   std::string old_history_file = octave::command_history::file ();
851 
852   std::string tmp = old_history_file;
853 
854   retval = set_internal_variable (tmp, args, nargout, "history_file");
855 
856   if (tmp != old_history_file)
857     octave::command_history::set_file (tmp);
858 
859   return retval;
860 }
861 
862 DEFMETHOD (history_timestamp_format_string, interp, args, nargout,
863            doc: /* -*- texinfo -*-
864 @deftypefn  {} {@var{val} =} history_timestamp_format_string ()
865 @deftypefnx {} {@var{old_val} =} history_timestamp_format_string (@var{new_val})
866 @deftypefnx {} {} history_timestamp_format_string (@var{new_val}, "local")
867 Query or set the internal variable that specifies the format string
868 for the comment line that is written to the history file when Octave
869 exits.
870 
871 The format string is passed to @code{strftime}.  The default value is
872 
873 @example
874 "# Octave VERSION, %a %b %d %H:%M:%S %Y %Z <USER@@HOST>"
875 @end example
876 
877 When called from inside a function with the @qcode{"local"} option, the
878 variable is changed locally for the function and any subroutines it calls.
879 The original variable value is restored when exiting the function.
880 @seealso{strftime, history_file, history_size, history_save}
881 @end deftypefn */)
882 {
883   octave::history_system& history_sys = interp.get_history_system ();
884 
885   return history_sys.timestamp_format_string (args, nargout);
886 }
887 
888 /*
889 %!test
890 %! history_timestamp_format_string ("# Example history marker", "local");
891 %! assert (history_timestamp_format_string (), "# Example history marker")
892 %!test <*57843>
893 %! history_timestamp_format_string ("", "local");
894 %! assert (history_timestamp_format_string (), "")
895 */
896 
897 DEFUN (history_save, args, nargout,
898        doc: /* -*- texinfo -*-
899 @deftypefn  {} {@var{val} =} history_save ()
900 @deftypefnx {} {@var{old_val} =} history_save (@var{new_val})
901 @deftypefnx {} {} history_save (@var{new_val}, "local")
902 Query or set the internal variable that controls whether commands entered
903 on the command line are saved in the history file.
904 
905 When called from inside a function with the @qcode{"local"} option, the
906 variable is changed locally for the function and any subroutines it calls.
907 The original variable value is restored when exiting the function.
908 @seealso{history_control, history_file, history_size, history_timestamp_format_string}
909 @end deftypefn */)
910 {
911   octave_value retval;
912 
913   bool old_history_save = ! octave::command_history::ignoring_entries ();
914 
915   bool tmp = old_history_save;
916 
917   retval = set_internal_variable (tmp, args, nargout, "history_save");
918 
919   if (tmp != old_history_save)
920     octave::command_history::ignore_entries (! tmp);
921 
922   return retval;
923 }
924