00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #ifdef HAVE_CONFIG_H
00024 #include <config.h>
00025 #endif
00026
00027 #include <fstream>
00028 #include <iostream>
00029 #include <string>
00030
00031 #include "cmd-edit.h"
00032 #include "oct-env.h"
00033 #include "singleton-cleanup.h"
00034
00035 #include "defaults.h"
00036 #include "defun.h"
00037 #include "error.h"
00038 #include "gripes.h"
00039 #include "input.h"
00040 #include "oct-obj.h"
00041 #include "pager.h"
00042 #include "procstream.h"
00043 #include "sighandlers.h"
00044 #include "unwind-prot.h"
00045 #include "utils.h"
00046 #include "variables.h"
00047
00048
00049 static oprocstream *external_pager = 0;
00050
00051
00052 static bool write_to_diary_file = false;
00053
00054
00055 static std::string diary_file;
00056
00057
00058 static std::ofstream external_diary_file;
00059
00060 static std::string
00061 default_pager (void)
00062 {
00063 std::string pager_binary = octave_env::getenv ("PAGER");
00064
00065 #ifdef OCTAVE_DEFAULT_PAGER
00066 if (pager_binary.empty ())
00067 pager_binary = OCTAVE_DEFAULT_PAGER;
00068 #endif
00069
00070 return pager_binary;
00071 }
00072
00073
00074 static std::string VPAGER = default_pager ();
00075
00076
00077 static std::string VPAGER_FLAGS;
00078
00079
00080
00081
00082 static bool Vpage_output_immediately = false;
00083
00084
00085
00086 static bool Vpage_screen_output = true;
00087
00088 static bool really_flush_to_pager = false;
00089
00090 static bool flushing_output_to_pager = false;
00091
00092 static void
00093 clear_external_pager (void)
00094 {
00095 if (external_pager)
00096 {
00097 octave_child_list::remove (external_pager->pid ());
00098
00099 delete external_pager;
00100 external_pager = 0;
00101 }
00102 }
00103
00104 static bool
00105 pager_event_handler (pid_t pid, int status)
00106 {
00107 bool retval = false;
00108
00109 if (pid > 0)
00110 {
00111 if (WIFEXITED (status) || WIFSIGNALLED (status))
00112 {
00113
00114
00115
00116 std::cerr << "warning: connection to external pager lost (pid = "
00117 << pid << ")" << std::endl;
00118 std::cerr << "warning: flushing pending output (please wait)"
00119 << std::endl;
00120
00121
00122
00123
00124 retval = true;
00125 }
00126 }
00127
00128 return retval;
00129 }
00130
00131 static std::string
00132 pager_command (void)
00133 {
00134 std::string cmd = VPAGER;
00135
00136 if (! (cmd.empty () || VPAGER_FLAGS.empty ()))
00137 cmd += " " + VPAGER_FLAGS;
00138
00139 return cmd;
00140 }
00141
00142 static void
00143 do_sync (const char *msg, int len, bool bypass_pager)
00144 {
00145 if (msg && len > 0)
00146 {
00147 if (bypass_pager)
00148 {
00149 std::cout.write (msg, len);
00150 std::cout.flush ();
00151 }
00152 else
00153 {
00154 if (! external_pager)
00155 {
00156 std::string pgr = pager_command ();
00157
00158 if (! pgr.empty ())
00159 {
00160 external_pager = new oprocstream (pgr.c_str ());
00161
00162 if (external_pager)
00163 octave_child_list::insert (external_pager->pid (),
00164 pager_event_handler);
00165 }
00166 }
00167
00168 if (external_pager)
00169 {
00170 if (external_pager->good ())
00171 {
00172 external_pager->write (msg, len);
00173
00174 external_pager->flush ();
00175
00176 #if defined (EPIPE)
00177 if (errno == EPIPE)
00178 external_pager->setstate (std::ios::failbit);
00179 #endif
00180 }
00181 else
00182 {
00183
00184
00185
00186
00187 }
00188 }
00189 else
00190 {
00191 std::cout.write (msg, len);
00192 std::cout.flush ();
00193 }
00194 }
00195 }
00196 }
00197
00198
00199
00200 static bool
00201 more_than_a_screenful (const char *s, int len)
00202 {
00203 if (s)
00204 {
00205 int available_rows = command_editor::terminal_rows () - 2;
00206
00207 int cols = command_editor::terminal_cols ();
00208
00209 int count = 0;
00210
00211 int chars_this_line = 0;
00212
00213 for (int i = 0; i < len; i++)
00214 {
00215 if (*s++ == '\n')
00216 {
00217 count += chars_this_line / cols + 1;
00218 chars_this_line = 0;
00219 }
00220 else
00221 chars_this_line++;
00222 }
00223
00224 if (count > available_rows)
00225 return true;
00226 }
00227
00228 return false;
00229 }
00230
00231 int
00232 octave_pager_buf::sync (void)
00233 {
00234 if (! interactive
00235 || really_flush_to_pager
00236 || (Vpage_screen_output && Vpage_output_immediately)
00237 || ! Vpage_screen_output)
00238 {
00239 char *buf = eback ();
00240
00241 int len = pptr () - buf;
00242
00243 bool bypass_pager = (! interactive
00244 || ! Vpage_screen_output
00245 || (really_flush_to_pager
00246 && Vpage_screen_output
00247 && ! Vpage_output_immediately
00248 && ! more_than_a_screenful (buf, len)));
00249
00250 if (len > 0)
00251 {
00252 do_sync (buf, len, bypass_pager);
00253
00254 flush_current_contents_to_diary ();
00255
00256 seekoff (0, std::ios::beg);
00257 }
00258 }
00259
00260 return 0;
00261 }
00262
00263 void
00264 octave_pager_buf::flush_current_contents_to_diary (void)
00265 {
00266 char *buf = eback () + diary_skip;
00267
00268 size_t len = pptr () - buf;
00269
00270 octave_diary.write (buf, len);
00271
00272 diary_skip = 0;
00273 }
00274
00275 void
00276 octave_pager_buf::set_diary_skip (void)
00277 {
00278 diary_skip = pptr () - eback ();
00279 }
00280
00281 int
00282 octave_diary_buf::sync (void)
00283 {
00284 if (write_to_diary_file && external_diary_file)
00285 {
00286 char *buf = eback ();
00287
00288 int len = pptr () - buf;
00289
00290 if (len > 0)
00291 external_diary_file.write (buf, len);
00292 }
00293
00294 seekoff (0, std::ios::beg);
00295
00296 return 0;
00297 }
00298
00299 octave_pager_stream *octave_pager_stream::instance = 0;
00300
00301 octave_pager_stream::octave_pager_stream (void) : std::ostream (0), pb (0)
00302 {
00303 pb = new octave_pager_buf ();
00304 rdbuf (pb);
00305 setf (unitbuf);
00306 }
00307
00308 octave_pager_stream::~octave_pager_stream (void)
00309 {
00310 flush ();
00311 delete pb;
00312 }
00313
00314 std::ostream&
00315 octave_pager_stream::stream (void)
00316 {
00317 return instance_ok () ? *instance : std::cout;
00318 }
00319
00320 void
00321 octave_pager_stream::flush_current_contents_to_diary (void)
00322 {
00323 if (instance_ok ())
00324 instance->do_flush_current_contents_to_diary ();
00325 }
00326
00327 void
00328 octave_pager_stream::set_diary_skip (void)
00329 {
00330 if (instance_ok ())
00331 instance->do_set_diary_skip ();
00332 }
00333
00334
00335
00336
00337
00338
00339 void
00340 octave_pager_stream::reset (void)
00341 {
00342 if (instance_ok ())
00343 instance->do_reset ();
00344 }
00345
00346 void
00347 octave_pager_stream::do_flush_current_contents_to_diary (void)
00348 {
00349 if (pb)
00350 pb->flush_current_contents_to_diary ();
00351 }
00352
00353 void
00354 octave_pager_stream::do_set_diary_skip (void)
00355 {
00356 if (pb)
00357 pb->set_diary_skip ();
00358 }
00359
00360 void
00361 octave_pager_stream::do_reset (void)
00362 {
00363 delete pb;
00364 pb = new octave_pager_buf ();
00365 rdbuf (pb);
00366 setf (unitbuf);
00367 }
00368
00369 bool
00370 octave_pager_stream::instance_ok (void)
00371 {
00372 bool retval = true;
00373
00374 if (! instance)
00375 {
00376 instance = new octave_pager_stream ();
00377
00378 if (instance)
00379 singleton_cleanup_list::add (cleanup_instance);
00380 }
00381
00382 if (! instance)
00383 {
00384 ::error ("unable to create pager_stream object!");
00385
00386 retval = false;
00387 }
00388
00389 return retval;
00390 }
00391
00392 octave_diary_stream *octave_diary_stream::instance = 0;
00393
00394 octave_diary_stream::octave_diary_stream (void) : std::ostream (0), db (0)
00395 {
00396 db = new octave_diary_buf ();
00397 rdbuf (db);
00398 setf (unitbuf);
00399 }
00400
00401 octave_diary_stream::~octave_diary_stream (void)
00402 {
00403 flush ();
00404 delete db;
00405 }
00406
00407 std::ostream&
00408 octave_diary_stream::stream (void)
00409 {
00410 return instance_ok () ? *instance : std::cout;
00411 }
00412
00413
00414
00415
00416
00417
00418 void
00419 octave_diary_stream::reset (void)
00420 {
00421 if (instance_ok ())
00422 instance->do_reset ();
00423 }
00424
00425 void
00426 octave_diary_stream::do_reset (void)
00427 {
00428 delete db;
00429 db = new octave_diary_buf ();
00430 rdbuf (db);
00431 setf (unitbuf);
00432 }
00433
00434 bool
00435 octave_diary_stream::instance_ok (void)
00436 {
00437 bool retval = true;
00438
00439 if (! instance)
00440 {
00441 instance = new octave_diary_stream ();
00442
00443 if (instance)
00444 singleton_cleanup_list::add (cleanup_instance);
00445 }
00446
00447 if (! instance)
00448 {
00449 ::error ("unable to create diary_stream object!");
00450
00451 retval = false;
00452 }
00453
00454 return retval;
00455 }
00456
00457 void
00458 flush_octave_stdout (void)
00459 {
00460 if (! flushing_output_to_pager)
00461 {
00462 unwind_protect frame;
00463
00464 frame.protect_var (really_flush_to_pager);
00465 frame.protect_var (flushing_output_to_pager);
00466
00467 really_flush_to_pager = true;
00468 flushing_output_to_pager = true;
00469
00470 octave_stdout.flush ();
00471
00472 clear_external_pager ();
00473 }
00474 }
00475
00476 static void
00477 close_diary_file (void)
00478 {
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490 octave_pager_stream::flush_current_contents_to_diary ();
00491
00492 if (external_diary_file.is_open ())
00493 {
00494 octave_diary.flush ();
00495 external_diary_file.close ();
00496 }
00497 }
00498
00499 static void
00500 open_diary_file (void)
00501 {
00502 close_diary_file ();
00503
00504
00505
00506
00507 octave_pager_stream::set_diary_skip ();
00508
00509 external_diary_file.open (diary_file.c_str (), std::ios::app);
00510
00511 if (! external_diary_file)
00512 error ("diary: can't open diary file '%s'", diary_file.c_str ());
00513 }
00514
00515 DEFUN (diary, args, ,
00516 "-*- texinfo -*-\n\
00517 @deftypefn {Command} {} diary options\n\
00518 Record a list of all commands @emph{and} the output they produce, mixed\n\
00519 together just as you see them on your terminal. Valid options are:\n\
00520 \n\
00521 @table @code\n\
00522 @item on\n\
00523 Start recording your session in a file called @file{diary} in your\n\
00524 current working directory.\n\
00525 \n\
00526 @item off\n\
00527 Stop recording your session in the diary file.\n\
00528 \n\
00529 @item @var{file}\n\
00530 Record your session in the file named @var{file}.\n\
00531 @end table\n\
00532 \n\
00533 With no arguments, @code{diary} toggles the current diary state.\n\
00534 @end deftypefn")
00535 {
00536 octave_value_list retval;
00537
00538 int argc = args.length () + 1;
00539
00540 string_vector argv = args.make_argv ("diary");
00541
00542 if (error_state)
00543 return retval;
00544
00545 if (diary_file.empty ())
00546 diary_file = "diary";
00547
00548 switch (argc)
00549 {
00550 case 1:
00551 write_to_diary_file = ! write_to_diary_file;
00552 open_diary_file ();
00553 break;
00554
00555 case 2:
00556 {
00557 std::string arg = argv[1];
00558
00559 if (arg == "on")
00560 {
00561 write_to_diary_file = true;
00562 open_diary_file ();
00563 }
00564 else if (arg == "off")
00565 {
00566 close_diary_file ();
00567 write_to_diary_file = false;
00568 }
00569 else
00570 {
00571 diary_file = arg;
00572 write_to_diary_file = true;
00573 open_diary_file ();
00574 }
00575 }
00576 break;
00577
00578 default:
00579 print_usage ();
00580 break;
00581 }
00582
00583 return retval;
00584 }
00585
00586 DEFUN (more, args, ,
00587 "-*- texinfo -*-\n\
00588 @deftypefn {Command} {} more\n\
00589 @deftypefnx {Command} {} more on\n\
00590 @deftypefnx {Command} {} more off\n\
00591 Turn output pagination on or off. Without an argument, @code{more}\n\
00592 toggles the current state.\n\
00593 The current state can be determined via @code{page_screen_output}.\n\
00594 @end deftypefn")
00595 {
00596 octave_value_list retval;
00597
00598 int argc = args.length () + 1;
00599
00600 string_vector argv = args.make_argv ("more");
00601
00602 if (error_state)
00603 return retval;
00604
00605 if (argc == 2)
00606 {
00607 std::string arg = argv[1];
00608
00609 if (arg == "on")
00610 Vpage_screen_output = true;
00611 else if (arg == "off")
00612 Vpage_screen_output = false;
00613 else
00614 error ("more: unrecognized argument '%s'", arg.c_str ());
00615 }
00616 else if (argc == 1)
00617 Vpage_screen_output = ! Vpage_screen_output;
00618 else
00619 print_usage ();
00620
00621 return retval;
00622 }
00623
00624 DEFUN (terminal_size, , ,
00625 "-*- texinfo -*-\n\
00626 @deftypefn {Built-in Function} {} terminal_size ()\n\
00627 Return a two-element row vector containing the current size of the\n\
00628 terminal window in characters (rows and columns).\n\
00629 @seealso{list_in_columns}\n\
00630 @end deftypefn")
00631 {
00632 RowVector size (2, 0.0);
00633
00634 size(0) = command_editor::terminal_rows ();
00635 size(1) = command_editor::terminal_cols ();
00636
00637 return octave_value (size);
00638 }
00639
00640 DEFUN (page_output_immediately, args, nargout,
00641 "-*- texinfo -*-\n\
00642 @deftypefn {Built-in Function} {@var{val} =} page_output_immediately ()\n\
00643 @deftypefnx {Built-in Function} {@var{old_val} =} page_output_immediately (@var{new_val})\n\
00644 @deftypefnx {Built-in Function} {} page_output_immediately (@var{new_val}, \"local\")\n\
00645 Query or set the internal variable that controls whether Octave sends\n\
00646 output to the pager as soon as it is available. Otherwise, Octave\n\
00647 buffers its output and waits until just before the prompt is printed to\n\
00648 flush it to the pager.\n\
00649 \n\
00650 When called from inside a function with the \"local\" option, the variable is\n\
00651 changed locally for the function and any subroutines it calls. The original\n\
00652 variable value is restored when exiting the function.\n\
00653 @end deftypefn")
00654 {
00655 return SET_INTERNAL_VARIABLE (page_output_immediately);
00656 }
00657
00658 DEFUN (page_screen_output, args, nargout,
00659 "-*- texinfo -*-\n\
00660 @deftypefn {Built-in Function} {@var{val} =} page_screen_output ()\n\
00661 @deftypefnx {Built-in Function} {@var{old_val} =} page_screen_output (@var{new_val})\n\
00662 @deftypefnx {Built-in Function} {} page_screen_output (@var{new_val}, \"local\")\n\
00663 Query or set the internal variable that controls whether output intended\n\
00664 for the terminal window that is longer than one page is sent through a\n\
00665 pager. This allows you to view one screenful at a time. Some pagers\n\
00666 (such as @code{less}---see @ref{Installation}) are also capable of moving\n\
00667 backward on the output.\n\
00668 \n\
00669 When called from inside a function with the \"local\" option, the variable is\n\
00670 changed locally for the function and any subroutines it calls. The original\n\
00671 variable value is restored when exiting the function.\n\
00672 @end deftypefn")
00673 {
00674 return SET_INTERNAL_VARIABLE (page_screen_output);
00675 }
00676
00677 DEFUN (PAGER, args, nargout,
00678 "-*- texinfo -*-\n\
00679 @deftypefn {Built-in Function} {@var{val} =} PAGER ()\n\
00680 @deftypefnx {Built-in Function} {@var{old_val} =} PAGER (@var{new_val})\n\
00681 @deftypefnx {Built-in Function} {} PAGER (@var{new_val}, \"local\")\n\
00682 Query or set the internal variable that specifies the program to use\n\
00683 to display terminal output on your system. The default value is\n\
00684 normally @code{\"less\"}, @code{\"more\"}, or\n\
00685 @code{\"pg\"}, depending on what programs are installed on your system.\n\
00686 @xref{Installation}.\n\
00687 \n\
00688 When called from inside a function with the \"local\" option, the variable is\n\
00689 changed locally for the function and any subroutines it calls. The original\n\
00690 variable value is restored when exiting the function.\n\
00691 @seealso{more, page_screen_output, page_output_immediately, PAGER_FLAGS}\n\
00692 @end deftypefn")
00693 {
00694 return SET_NONEMPTY_INTERNAL_STRING_VARIABLE (PAGER);
00695 }
00696
00697 DEFUN (PAGER_FLAGS, args, nargout,
00698 "-*- texinfo -*-\n\
00699 @deftypefn {Built-in Function} {@var{val} =} PAGER_FLAGS ()\n\
00700 @deftypefnx {Built-in Function} {@var{old_val} =} PAGER_FLAGS (@var{new_val})\n\
00701 @deftypefnx {Built-in Function} {} PAGER_FLAGS (@var{new_val}, \"local\")\n\
00702 Query or set the internal variable that specifies the options to pass\n\
00703 to the pager.\n\
00704 \n\
00705 When called from inside a function with the \"local\" option, the variable is\n\
00706 changed locally for the function and any subroutines it calls. The original\n\
00707 variable value is restored when exiting the function.\n\
00708 @seealso{PAGER}\n\
00709 @end deftypefn")
00710 {
00711 return SET_NONEMPTY_INTERNAL_STRING_VARIABLE (PAGER_FLAGS);
00712 }