00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #ifdef HAVE_CONFIG_H
00035 #include <config.h>
00036 #endif
00037
00038 #include <cstdlib>
00039 #include <cstring>
00040
00041 #include <string>
00042
00043 #include <fstream>
00044
00045 #include <sys/types.h>
00046 #include <unistd.h>
00047
00048 #include "cmd-hist.h"
00049 #include "file-ops.h"
00050 #include "lo-mappers.h"
00051 #include "oct-env.h"
00052 #include "oct-time.h"
00053 #include "str-vec.h"
00054
00055 #include <defaults.h>
00056 #include "defun.h"
00057 #include "error.h"
00058 #include "gripes.h"
00059 #include "input.h"
00060 #include "oct-hist.h"
00061 #include "oct-obj.h"
00062 #include "pager.h"
00063 #include "parse.h"
00064 #include "sighandlers.h"
00065 #include "sysdep.h"
00066 #include "toplev.h"
00067 #include "unwind-prot.h"
00068 #include "utils.h"
00069 #include "variables.h"
00070
00071
00072 bool input_from_tmp_history_file = false;
00073
00074 static std::string
00075 default_history_file (void)
00076 {
00077 std::string file;
00078
00079 std::string env_file = octave_env::getenv ("OCTAVE_HISTFILE");
00080
00081 if (! env_file.empty ())
00082 file = env_file;
00083
00084 if (file.empty ())
00085 file = file_ops::concat (octave_env::get_home_directory (),
00086 ".octave_hist");
00087
00088 return file;
00089 }
00090
00091 static int
00092 default_history_size (void)
00093 {
00094 int size = 1024;
00095
00096 std::string env_size = octave_env::getenv ("OCTAVE_HISTSIZE");
00097
00098 if (! env_size.empty ())
00099 {
00100 int val;
00101
00102 if (sscanf (env_size.c_str (), "%d", &val) == 1)
00103 size = val > 0 ? val : 0;
00104 }
00105
00106 return size;
00107 }
00108
00109 static std::string
00110 default_history_timestamp_format (void)
00111 {
00112 return
00113 std::string ("# Octave " OCTAVE_VERSION ", %a %b %d %H:%M:%S %Y %Z <")
00114 + octave_env::get_user_name ()
00115 + std::string ("@")
00116 + octave_env::get_host_name ()
00117 + std::string (">");
00118 }
00119
00120
00121
00122 static std::string Vhistory_timestamp_format_string
00123 = default_history_timestamp_format ();
00124
00125
00126
00127
00128
00129
00130
00131 static void
00132 do_history (int argc, const string_vector& argv)
00133 {
00134 int numbered_output = 1;
00135
00136 unwind_protect frame;
00137
00138 frame.add_fcn (command_history::set_file, command_history::file ());
00139
00140 int i;
00141 for (i = 1; i < argc; i++)
00142 {
00143 std::string option = argv[i];
00144
00145 if (option == "-r" || option == "-w" || option == "-a"
00146 || option == "-n")
00147 {
00148 if (i < argc - 1)
00149 command_history::set_file (argv[i+1]);
00150
00151 if (option == "-a")
00152
00153 command_history::append ();
00154
00155 else if (option == "-w")
00156
00157 command_history::write ();
00158
00159 else if (option == "-r")
00160
00161 command_history::read ();
00162
00163 else if (option == "-n")
00164
00165 command_history::read_range ();
00166
00167 else
00168 panic_impossible ();
00169
00170 return;
00171 }
00172 else if (argv[i] == "-q")
00173 numbered_output = 0;
00174 else if (argv[i] == "--")
00175 {
00176 i++;
00177 break;
00178 }
00179 else
00180 break;
00181 }
00182
00183 int limit = -1;
00184
00185 if (i < argc)
00186 {
00187 if (sscanf (argv[i].c_str (), "%d", &limit) != 1)
00188 {
00189 if (argv[i][0] == '-')
00190 error ("history: unrecognized option '%s'", argv[i].c_str ());
00191 else
00192 error ("history: bad non-numeric arg '%s'", argv[i].c_str ());
00193
00194 return;
00195 }
00196
00197 if (limit < 0)
00198 limit = -limit;
00199 }
00200
00201 string_vector hlist = command_history::list (limit, numbered_output);
00202
00203 int len = hlist.length ();
00204
00205 for (i = 0; i < len; i++)
00206 octave_stdout << hlist[i] << "\n";
00207 }
00208
00209
00210
00211
00212
00213 static char *
00214 edit_history_readline (std::fstream& stream)
00215 {
00216 char c;
00217 int line_len = 128;
00218 int lindex = 0;
00219 char *line = new char [line_len];
00220 line[0] = '\0';
00221
00222 while (stream.get (c))
00223 {
00224 if (lindex + 2 >= line_len)
00225 {
00226 char *tmp_line = new char [line_len += 128];
00227 strcpy (tmp_line, line);
00228 delete [] line;
00229 line = tmp_line;
00230 }
00231
00232 if (c == '\n')
00233 {
00234 line[lindex++] = '\n';
00235 line[lindex++] = '\0';
00236 return line;
00237 }
00238 else
00239 line[lindex++] = c;
00240 }
00241
00242 if (! lindex)
00243 {
00244 delete [] line;
00245 return 0;
00246 }
00247
00248 if (lindex + 2 >= line_len)
00249 {
00250 char *tmp_line = new char [lindex+3];
00251 strcpy (tmp_line, line);
00252 delete [] line;
00253 line = tmp_line;
00254 }
00255
00256
00257
00258 line[lindex++] = '\n';
00259 line[lindex++] = '\0';
00260 return line;
00261 }
00262
00263
00264
00265
00266
00267
00268
00269 static void
00270 edit_history_repl_hist (const std::string& command)
00271 {
00272 if (! command.empty ())
00273 {
00274 string_vector hlist = command_history::list ();
00275
00276 int len = hlist.length ();
00277
00278 if (len > 0)
00279 {
00280 int i = len - 1;
00281
00282 std::string histent = command_history::get_entry (i);
00283
00284 if (! histent.empty ())
00285 {
00286 std::string cmd = command;
00287
00288 int cmd_len = cmd.length ();
00289
00290 if (cmd[cmd_len - 1] == '\n')
00291 cmd.resize (cmd_len - 1);
00292
00293 if (! cmd.empty ())
00294 command_history::replace_entry (i, cmd);
00295 }
00296 }
00297 }
00298 }
00299
00300 static void
00301 edit_history_add_hist (const std::string& line)
00302 {
00303 if (! line.empty ())
00304 {
00305 std::string tmp = line;
00306
00307 int len = tmp.length ();
00308
00309 if (len > 0 && tmp[len-1] == '\n')
00310 tmp.resize (len - 1);
00311
00312 if (! tmp.empty ())
00313 command_history::add (tmp);
00314 }
00315 }
00316
00317 static std::string
00318 mk_tmp_hist_file (int argc, const string_vector& argv,
00319 int insert_curr, const char *warn_for)
00320 {
00321 std::string retval;
00322
00323 string_vector hlist = command_history::list ();
00324
00325 int hist_count = hlist.length ();
00326
00327
00328
00329
00330 hist_count -= 2;
00331
00332 if (! insert_curr)
00333 command_history::remove (hist_count);
00334
00335 hist_count--;
00336
00337
00338
00339
00340 int hist_end = hist_count;
00341 int hist_beg = hist_count;
00342 int reverse = 0;
00343
00344
00345
00346 int usage_error = 0;
00347 if (argc == 3)
00348 {
00349 if (sscanf (argv[1].c_str (), "%d", &hist_beg) != 1
00350 || sscanf (argv[2].c_str (), "%d", &hist_end) != 1)
00351 usage_error = 1;
00352 else
00353 {
00354 hist_beg--;
00355 hist_end--;
00356 }
00357 }
00358 else if (argc == 2)
00359 {
00360 if (sscanf (argv[1].c_str (), "%d", &hist_beg) != 1)
00361 usage_error = 1;
00362 else
00363 {
00364 hist_beg--;
00365 hist_end = hist_beg;
00366 }
00367 }
00368
00369 if (hist_beg < 0 || hist_end < 0 || hist_beg > hist_count
00370 || hist_end > hist_count)
00371 {
00372 error ("%s: history specification out of range", warn_for);
00373 return retval;
00374 }
00375
00376 if (usage_error)
00377 {
00378 usage ("%s [first] [last]", warn_for);
00379 return retval;
00380 }
00381
00382 if (hist_end < hist_beg)
00383 {
00384 int t = hist_end;
00385 hist_end = hist_beg;
00386 hist_beg = t;
00387 reverse = 1;
00388 }
00389
00390 std::string name = octave_tempnam ("", "oct-");
00391
00392 std::fstream file (name.c_str (), std::ios::out);
00393
00394 if (! file)
00395 {
00396 error ("%s: couldn't open temporary file '%s'", warn_for,
00397 name.c_str ());
00398 return retval;
00399 }
00400
00401 if (reverse)
00402 {
00403 for (int i = hist_end; i >= hist_beg; i--)
00404 file << hlist[i] << "\n";
00405 }
00406 else
00407 {
00408 for (int i = hist_beg; i <= hist_end; i++)
00409 file << hlist[i] << "\n";
00410 }
00411
00412 file.close ();
00413
00414 return name;
00415 }
00416
00417 static void
00418 unlink_cleanup (const char *file)
00419 {
00420 gnulib::unlink (file);
00421 }
00422
00423 static void
00424 do_edit_history (int argc, const string_vector& argv)
00425 {
00426 std::string name = mk_tmp_hist_file (argc, argv, 0, "edit_history");
00427
00428 if (name.empty ())
00429 return;
00430
00431
00432
00433 std::string cmd = VEDITOR;
00434 cmd.append (" \"");
00435 cmd.append (name);
00436 cmd.append ("\"");
00437
00438
00439
00440
00441 volatile octave_interrupt_handler old_interrupt_handler
00442 = octave_ignore_interrupts ();
00443
00444 system (cmd.c_str ());
00445
00446 octave_set_interrupt_handler (old_interrupt_handler);
00447
00448
00449
00450
00451 std::fstream file (name.c_str (), std::ios::in);
00452
00453 char *line;
00454 int first = 1;
00455 while ((line = edit_history_readline (file)) != 0)
00456 {
00457
00458
00459 if (line[0] == '\n')
00460 {
00461 delete [] line;
00462 continue;
00463 }
00464
00465 if (first)
00466 {
00467 first = 0;
00468 edit_history_repl_hist (line);
00469 }
00470 else
00471 edit_history_add_hist (line);
00472 }
00473
00474 file.close ();
00475
00476
00477
00478
00479 unwind_protect frame;
00480
00481 frame.add_fcn (unlink_cleanup, name.c_str ());
00482 frame.protect_var (Vecho_executing_commands);
00483 frame.protect_var (input_from_tmp_history_file);
00484
00485 Vecho_executing_commands = ECHO_CMD_LINE;
00486 input_from_tmp_history_file = true;
00487
00488 source_file (name);
00489 }
00490
00491 static void
00492 do_run_history (int argc, const string_vector& argv)
00493 {
00494 std::string name = mk_tmp_hist_file (argc, argv, 1, "run_history");
00495
00496 if (name.empty ())
00497 return;
00498
00499
00500
00501
00502 unwind_protect frame;
00503
00504 frame.add_fcn (unlink_cleanup, name.c_str ());
00505 frame.protect_var (Vecho_executing_commands);
00506 frame.protect_var (input_from_tmp_history_file);
00507
00508 Vecho_executing_commands = ECHO_CMD_LINE;
00509 input_from_tmp_history_file = true;
00510
00511 source_file (name);
00512 }
00513
00514 void
00515 initialize_history (bool read_history_file)
00516 {
00517 command_history::initialize (read_history_file,
00518 default_history_file (),
00519 default_history_size (),
00520 octave_env::getenv ("OCTAVE_HISTCONTROL"));
00521 }
00522
00523 void
00524 octave_history_write_timestamp (void)
00525 {
00526 octave_localtime now;
00527
00528 std::string timestamp = now.strftime (Vhistory_timestamp_format_string);
00529
00530 if (! timestamp.empty ())
00531 command_history::add (timestamp);
00532 }
00533
00534 DEFUN (edit_history, args, ,
00535 "-*- texinfo -*-\n\
00536 @deftypefn {Command} {} edit_history [@var{first}] [@var{last}]\n\
00537 If invoked with no arguments, @code{edit_history} allows you to edit the\n\
00538 history list using the editor named by the variable @w{@env{EDITOR}}. The\n\
00539 commands to be edited are first copied to a temporary file. When you\n\
00540 exit the editor, Octave executes the commands that remain in the file.\n\
00541 It is often more convenient to use @code{edit_history} to define functions\n\
00542 rather than attempting to enter them directly on the command line.\n\
00543 By default, the block of commands is executed as soon as you exit the\n\
00544 editor. To avoid executing any commands, simply delete all the lines\n\
00545 from the buffer before exiting the editor.\n\
00546 \n\
00547 The @code{edit_history} command takes two optional arguments specifying\n\
00548 the history numbers of first and last commands to edit. For example,\n\
00549 the command\n\
00550 \n\
00551 @example\n\
00552 edit_history 13\n\
00553 @end example\n\
00554 \n\
00555 @noindent\n\
00556 extracts all the commands from the 13th through the last in the history\n\
00557 list. The command\n\
00558 \n\
00559 @example\n\
00560 edit_history 13 169\n\
00561 @end example\n\
00562 \n\
00563 @noindent\n\
00564 only extracts commands 13 through 169. Specifying a larger number for\n\
00565 the first command than the last command reverses the list of commands\n\
00566 before placing them in the buffer to be edited. If both arguments are\n\
00567 omitted, the previous command in the history list is used.\n\
00568 @seealso{run_history}\n\
00569 @end deftypefn")
00570 {
00571 octave_value_list retval;
00572
00573 int argc = args.length () + 1;
00574
00575 string_vector argv = args.make_argv ("edit_history");
00576
00577 if (error_state)
00578 return retval;
00579
00580 do_edit_history (argc, argv);
00581
00582 return retval;
00583 }
00584
00585 DEFUN (history, args, ,
00586 "-*- texinfo -*-\n\
00587 @deftypefn {Command} {} history options\n\
00588 If invoked with no arguments, @code{history} displays a list of commands\n\
00589 that you have executed. Valid options are:\n\
00590 \n\
00591 @table @code\n\
00592 @item -w @var{file}\n\
00593 Write the current history to the file @var{file}. If the name is\n\
00594 omitted, use the default history file (normally @file{~/.octave_hist}).\n\
00595 \n\
00596 @item -r @var{file}\n\
00597 Read the file @var{file}, appending its contents to the current\n\
00598 history list. If the name is omitted, use the default history file\n\
00599 (normally @file{~/.octave_hist}).\n\
00600 \n\
00601 @item @var{n}\n\
00602 Display only the most recent @var{n} lines of history.\n\
00603 \n\
00604 @item -q\n\
00605 Don't number the displayed lines of history. This is useful for cutting\n\
00606 and pasting commands using the X Window System.\n\
00607 @end table\n\
00608 \n\
00609 For example, to display the five most recent commands that you have\n\
00610 typed without displaying line numbers, use the command\n\
00611 @kbd{history -q 5}.\n\
00612 @end deftypefn")
00613 {
00614 octave_value_list retval;
00615
00616 int argc = args.length () + 1;
00617
00618 string_vector argv = args.make_argv ("history");
00619
00620 if (error_state)
00621 return retval;
00622
00623 do_history (argc, argv);
00624
00625 return retval;
00626 }
00627
00628 DEFUN (run_history, args, ,
00629 "-*- texinfo -*-\n\
00630 @deftypefn {Command} {} run_history [@var{first}] [@var{last}]\n\
00631 Similar to @code{edit_history}, except that the editor is not invoked,\n\
00632 and the commands are simply executed as they appear in the history list.\n\
00633 @seealso{edit_history}\n\
00634 @end deftypefn")
00635 {
00636 octave_value_list retval;
00637
00638 int argc = args.length () + 1;
00639
00640 string_vector argv = args.make_argv ("run_history");
00641
00642 if (error_state)
00643 return retval;
00644
00645 do_run_history (argc, argv);
00646
00647 return retval;
00648 }
00649
00650 DEFUN (history_control, args, nargout,
00651 "-*- texinfo -*-\n\
00652 @deftypefn {Built-in Function} {@var{val} =} history_control ()\n\
00653 @deftypefnx {Built-in Function} {@var{old_val} =} history_control (@var{new_val})\n\
00654 Query or set the internal variable that specifies how commands are saved\n\
00655 to the history list. The default value is an empty character string,\n\
00656 but may be overridden by the environment variable\n\
00657 @w{@env{OCTAVE_HISTCONTROL}}.\n\
00658 \n\
00659 The value of @code{history_control} is a colon-separated list of values\n\
00660 controlling how commands are saved on the history list. If the list\n\
00661 of values includes @code{ignorespace}, lines which begin with a space\n\
00662 character are not saved in the history list. A value of @code{ignoredups}\n\
00663 causes lines matching the previous history entry to not be saved.\n\
00664 A value of @code{ignoreboth} is shorthand for @code{ignorespace} and\n\
00665 @code{ignoredups}. A value of @code{erasedups} causes all previous lines\n\
00666 matching the current line to be removed from the history list before that\n\
00667 line is saved. Any value not in the above list is ignored. If\n\
00668 @code{history_control} is the empty string, all commands are saved on\n\
00669 the history list, subject to the value of @code{saving_history}.\n\
00670 @seealso{history_file, history_size, history_timestamp_format_string, saving_history}\n\
00671 @end deftypefn")
00672 {
00673 std::string old_history_control = command_history::histcontrol ();
00674
00675 std::string tmp = old_history_control;
00676
00677 octave_value retval = set_internal_variable (tmp, args, nargout,
00678 "history_control");
00679
00680 if (tmp != old_history_control)
00681 command_history::process_histcontrol (tmp);
00682
00683 return retval;
00684 }
00685
00686 DEFUN (history_size, args, nargout,
00687 "-*- texinfo -*-\n\
00688 @deftypefn {Built-in Function} {@var{val} =} history_size ()\n\
00689 @deftypefnx {Built-in Function} {@var{old_val} =} history_size (@var{new_val})\n\
00690 Query or set the internal variable that specifies how many entries\n\
00691 to store in the history file. The default value is @code{1024},\n\
00692 but may be overridden by the environment variable @w{@env{OCTAVE_HISTSIZE}}.\n\
00693 @seealso{history_file, history_timestamp_format_string, saving_history}\n\
00694 @end deftypefn")
00695 {
00696 int old_history_size = command_history::size ();
00697
00698 int tmp = old_history_size;
00699
00700 octave_value retval = set_internal_variable (tmp, args, nargout,
00701 "history_size", -1, INT_MAX);
00702
00703 if (tmp != old_history_size)
00704 command_history::set_size (tmp);
00705
00706 return retval;
00707 }
00708
00709 DEFUN (history_file, args, nargout,
00710 "-*- texinfo -*-\n\
00711 @deftypefn {Built-in Function} {@var{val} =} history_file ()\n\
00712 @deftypefnx {Built-in Function} {@var{old_val} =} history_file (@var{new_val})\n\
00713 Query or set the internal variable that specifies the name of the\n\
00714 file used to store command history. The default value is\n\
00715 @file{~/.octave_hist}, but may be overridden by the environment\n\
00716 variable @w{@env{OCTAVE_HISTFILE}}.\n\
00717 @seealso{history_size, saving_history, history_timestamp_format_string}\n\
00718 @end deftypefn")
00719 {
00720 std::string old_history_file = command_history::file ();
00721
00722 std::string tmp = old_history_file;
00723
00724 octave_value retval = set_internal_variable (tmp, args, nargout,
00725 "history_file");
00726
00727 if (tmp != old_history_file)
00728 command_history::set_file (tmp);
00729
00730 return retval;
00731 }
00732
00733 DEFUN (history_timestamp_format_string, args, nargout,
00734 "-*- texinfo -*-\n\
00735 @deftypefn {Built-in Function} {@var{val} =} history_timestamp_format_string ()\n\
00736 @deftypefnx {Built-in Function} {@var{old_val} =} history_timestamp_format_string (@var{new_val})\n\
00737 @deftypefnx {Built-in Function} {} history_timestamp_format_string (@var{new_val}, \"local\")\n\
00738 Query or set the internal variable that specifies the format string\n\
00739 for the comment line that is written to the history file when Octave\n\
00740 exits. The format string is passed to @code{strftime}. The default\n\
00741 value is\n\
00742 \n\
00743 @example\n\
00744 \"# Octave VERSION, %a %b %d %H:%M:%S %Y %Z <USER@@HOST>\"\n\
00745 @end example\n\
00746 \n\
00747 When called from inside a function with the \"local\" option, the variable is\n\
00748 changed locally for the function and any subroutines it calls. The original\n\
00749 variable value is restored when exiting the function.\n\
00750 @seealso{strftime, history_file, history_size, saving_history}\n\
00751 @end deftypefn")
00752 {
00753 return SET_INTERNAL_VARIABLE (history_timestamp_format_string);
00754 }
00755
00756 DEFUN (saving_history, args, nargout,
00757 "-*- texinfo -*-\n\
00758 @deftypefn {Built-in Function} {@var{val} =} saving_history ()\n\
00759 @deftypefnx {Built-in Function} {@var{old_val} =} saving_history (@var{new_val})\n\
00760 @deftypefnx {Built-in Function} {} saving_history (@var{new_val}, \"local\")\n\
00761 Query or set the internal variable that controls whether commands entered\n\
00762 on the command line are saved in the history file.\n\
00763 \n\
00764 When called from inside a function with the \"local\" option, the variable is\n\
00765 changed locally for the function and any subroutines it calls. The original\n\
00766 variable value is restored when exiting the function.\n\
00767 @seealso{history_control, history_file, history_size, history_timestamp_format_string}\n\
00768 @end deftypefn")
00769 {
00770 bool old_saving_history = ! command_history::ignoring_entries ();
00771
00772 bool tmp = old_saving_history;
00773
00774 octave_value retval = set_internal_variable (tmp, args, nargout,
00775 "saving_history");
00776
00777 if (tmp != old_saving_history)
00778 command_history::ignore_entries (! tmp);
00779
00780 return retval;
00781 }