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