pager.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 1993-2012 John W. Eaton
00004 
00005 This file is part of Octave.
00006 
00007 Octave is free software; you can redistribute it and/or modify it
00008 under the terms of the GNU General Public License as published by the
00009 Free Software Foundation; either version 3 of the License, or (at your
00010 option) any later version.
00011 
00012 Octave is distributed in the hope that it will be useful, but WITHOUT
00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00015 for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Octave; see the file COPYING.  If not, see
00019 <http://www.gnu.org/licenses/>.
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 // Our actual connection to the external pager.
00049 static oprocstream *external_pager = 0;
00050 
00051 // TRUE means we write to the diary file.
00052 static bool write_to_diary_file = false;
00053 
00054 // The name of the current diary file.
00055 static std::string diary_file;
00056 
00057 // The diary file.
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 // The shell command to run as the pager.
00074 static std::string VPAGER = default_pager ();
00075 
00076 // The options to pass to the pager.
00077 static std::string VPAGER_FLAGS;
00078 
00079 // TRUE means that if output is going to the pager, it is sent as soon
00080 // as it is available.  Otherwise, it is buffered and only sent to the
00081 // pager when it is time to print another prompt.
00082 static bool Vpage_output_immediately = false;
00083 
00084 // TRUE means all output intended for the screen should be passed
00085 // through the pager.
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           // Avoid warning() since that will put us back in the pager,
00114           // which would be bad news.
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           // Request removal of this PID from the list of child
00122           // processes.
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                   // FIXME -- omething is not right with the
00184                   // pager.  If it died then we should receive a
00185                   // signal for that.  If there is some other problem,
00186                   // then what?
00187                 }
00188             }
00189           else
00190             {
00191               std::cout.write (msg, len);
00192               std::cout.flush ();
00193             }
00194         }
00195     }
00196 }
00197 
00198 // Assume our terminal wraps long lines.
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 // Reinitialize the pager buffer to avoid hanging on to large internal
00335 // buffers when they might not be needed.  This function should only be
00336 // called when the pager is not in use.  For example, just before
00337 // getting command-line input.
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 // Reinitialize the diary buffer to avoid hanging on to large internal
00414 // buffers when they might not be needed.  This function should only be
00415 // called when the pager is not in use.  For example, just before
00416 // getting command-line input.
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   // Try to flush the current buffer to the diary now, so that things
00480   // like
00481   //
00482   // function foo ()
00483   //   diary on;
00484   //   ...
00485   //   diary off;
00486   // endfunction
00487   //
00488   // will do the right thing.
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   // If there is pending output in the pager buf, it should not go
00505   // into the diary file.
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 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines