GNU Octave  3.8.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
pager.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 1993-2013 John W. Eaton
4 
5 This file is part of Octave.
6 
7 Octave is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <http://www.gnu.org/licenses/>.
20 
21 */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <fstream>
28 #include <iostream>
29 #include <string>
30 
31 #include "cmd-edit.h"
32 #include "oct-env.h"
33 #include "singleton-cleanup.h"
34 
35 #include "defaults.h"
36 #include "defun.h"
37 #include "error.h"
38 #include "gripes.h"
39 #include "input.h"
40 #include "oct-obj.h"
41 #include "pager.h"
42 #include "procstream.h"
43 #include "sighandlers.h"
44 #include "unwind-prot.h"
45 #include "utils.h"
46 #include "variables.h"
47 
48 // Our actual connection to the external pager.
50 
51 // TRUE means we write to the diary file.
52 static bool write_to_diary_file = false;
53 
54 // The name of the current diary file.
55 static std::string diary_file;
56 
57 // The diary file.
58 static std::ofstream external_diary_file;
59 
60 static std::string
62 {
63  std::string pager_binary = octave_env::getenv ("PAGER");
64 
65 #ifdef OCTAVE_DEFAULT_PAGER
66  if (pager_binary.empty ())
67  pager_binary = OCTAVE_DEFAULT_PAGER;
68 #endif
69 
70  return pager_binary;
71 }
72 
73 // The shell command to run as the pager.
74 static std::string VPAGER = default_pager ();
75 
76 // The options to pass to the pager.
77 static std::string VPAGER_FLAGS;
78 
79 // TRUE means that if output is going to the pager, it is sent as soon
80 // as it is available. Otherwise, it is buffered and only sent to the
81 // pager when it is time to print another prompt.
82 static bool Vpage_output_immediately = false;
83 
84 // TRUE means all output intended for the screen should be passed
85 // through the pager.
86 static bool Vpage_screen_output = true;
87 
88 static bool really_flush_to_pager = false;
89 
90 static bool flushing_output_to_pager = false;
91 
92 static void
94 {
95  if (external_pager)
96  {
97  octave_child_list::remove (external_pager->pid ());
98 
99  delete external_pager;
100  external_pager = 0;
101  }
102 }
103 
104 static bool
105 pager_event_handler (pid_t pid, int status)
106 {
107  bool retval = false;
108 
109  if (pid > 0)
110  {
111  if (octave_wait::ifexited (status) || octave_wait::ifsignaled (status))
112  {
113  // Avoid warning() since that will put us back in the pager,
114  // which would be bad news.
115 
116  std::cerr << "warning: connection to external pager lost (pid = "
117  << pid << ")" << std::endl;
118  std::cerr << "warning: flushing pending output (please wait)"
119  << std::endl;
120 
121  // Request removal of this PID from the list of child
122  // processes.
123 
124  retval = true;
125  }
126  }
127 
128  return retval;
129 }
130 
131 static std::string
133 {
134  std::string cmd = VPAGER;
135 
136  if (! (cmd.empty () || VPAGER_FLAGS.empty ()))
137  cmd += " " + VPAGER_FLAGS;
138 
139  return cmd;
140 }
141 
142 static void
143 do_sync (const char *msg, int len, bool bypass_pager)
144 {
145  if (msg && len > 0)
146  {
147  if (bypass_pager)
148  {
149  std::cout.write (msg, len);
150  std::cout.flush ();
151  }
152  else
153  {
154  if (! external_pager)
155  {
156  std::string pgr = pager_command ();
157 
158  if (! pgr.empty ())
159  {
160  external_pager = new oprocstream (pgr.c_str ());
161 
162  if (external_pager)
163  octave_child_list::insert (external_pager->pid (),
165  }
166  }
167 
168  if (external_pager)
169  {
170  if (external_pager->good ())
171  {
172  external_pager->write (msg, len);
173 
174  external_pager->flush ();
175 
176 #if defined (EPIPE)
177  if (errno == EPIPE)
178  external_pager->setstate (std::ios::failbit);
179 #endif
180  }
181  else
182  {
183  // FIXME: omething is not right with the
184  // pager. If it died then we should receive a
185  // signal for that. If there is some other problem,
186  // then what?
187  }
188  }
189  else
190  {
191  std::cout.write (msg, len);
192  std::cout.flush ();
193  }
194  }
195  }
196 }
197 
198 // Assume our terminal wraps long lines.
199 
200 static bool
201 more_than_a_screenful (const char *s, int len)
202 {
203  if (s)
204  {
205  int available_rows = command_editor::terminal_rows () - 2;
206 
207  int cols = command_editor::terminal_cols ();
208 
209  int count = 0;
210 
211  int chars_this_line = 0;
212 
213  for (int i = 0; i < len; i++)
214  {
215  if (*s++ == '\n')
216  {
217  count += chars_this_line / cols + 1;
218  chars_this_line = 0;
219  }
220  else
221  chars_this_line++;
222  }
223 
224  if (count > available_rows)
225  return true;
226  }
227 
228  return false;
229 }
230 
231 int
233 {
234  if (! interactive
237  || ! Vpage_screen_output)
238  {
239  char *buf = eback ();
240 
241  int len = pptr () - buf;
242 
243  bool bypass_pager = (! interactive
248  && ! more_than_a_screenful (buf, len)));
249 
250  if (len > 0)
251  {
252  do_sync (buf, len, bypass_pager);
253 
255 
256  seekoff (0, std::ios::beg);
257  }
258  }
259 
260  return 0;
261 }
262 
263 void
265 {
266  char *buf = eback () + diary_skip;
267 
268  size_t len = pptr () - buf;
269 
270  octave_diary.write (buf, len);
271 
272  diary_skip = 0;
273 }
274 
275 void
277 {
278  diary_skip = pptr () - eback ();
279 }
280 
281 int
283 {
285  {
286  char *buf = eback ();
287 
288  int len = pptr () - buf;
289 
290  if (len > 0)
291  external_diary_file.write (buf, len);
292  }
293 
294  seekoff (0, std::ios::beg);
295 
296  return 0;
297 }
298 
300 
301 octave_pager_stream::octave_pager_stream (void) : std::ostream (0), pb (0)
302 {
303  pb = new octave_pager_buf ();
304  rdbuf (pb);
305  setf (unitbuf);
306 }
307 
309 {
310  flush ();
311  delete pb;
312 }
313 
314 std::ostream&
316 {
317  return instance_ok () ? *instance : std::cout;
318 }
319 
320 void
322 {
323  if (instance_ok ())
325 }
326 
327 void
329 {
330  if (instance_ok ())
332 }
333 
334 // Reinitialize the pager buffer to avoid hanging on to large internal
335 // buffers when they might not be needed. This function should only be
336 // called when the pager is not in use. For example, just before
337 // getting command-line input.
338 
339 void
341 {
342  if (instance_ok ())
343  instance->do_reset ();
344 }
345 
346 void
348 {
349  if (pb)
351 }
352 
353 void
355 {
356  if (pb)
357  pb->set_diary_skip ();
358 }
359 
360 void
362 {
363  delete pb;
364  pb = new octave_pager_buf ();
365  rdbuf (pb);
366  setf (unitbuf);
367 }
368 
369 bool
371 {
372  bool retval = true;
373 
374  if (! instance)
375  {
376  instance = new octave_pager_stream ();
377 
378  if (instance)
380  }
381 
382  if (! instance)
383  {
384  ::error ("unable to create pager_stream object!");
385 
386  retval = false;
387  }
388 
389  return retval;
390 }
391 
393 
394 octave_diary_stream::octave_diary_stream (void) : std::ostream (0), db (0)
395 {
396  db = new octave_diary_buf ();
397  rdbuf (db);
398  setf (unitbuf);
399 }
400 
402 {
403  flush ();
404  delete db;
405 }
406 
407 std::ostream&
409 {
410  return instance_ok () ? *instance : std::cout;
411 }
412 
413 // Reinitialize the diary buffer to avoid hanging on to large internal
414 // buffers when they might not be needed. This function should only be
415 // called when the pager is not in use. For example, just before
416 // getting command-line input.
417 
418 void
420 {
421  if (instance_ok ())
422  instance->do_reset ();
423 }
424 
425 void
427 {
428  delete db;
429  db = new octave_diary_buf ();
430  rdbuf (db);
431  setf (unitbuf);
432 }
433 
434 bool
436 {
437  bool retval = true;
438 
439  if (! instance)
440  {
441  instance = new octave_diary_stream ();
442 
443  if (instance)
445  }
446 
447  if (! instance)
448  {
449  ::error ("unable to create diary_stream object!");
450 
451  retval = false;
452  }
453 
454  return retval;
455 }
456 
457 void
459 {
461  {
462  unwind_protect frame;
463 
466 
467  really_flush_to_pager = true;
469 
470  octave_stdout.flush ();
471 
473  }
474 }
475 
476 static void
478 {
479  // Try to flush the current buffer to the diary now, so that things
480  // like
481  //
482  // function foo ()
483  // diary on;
484  // ...
485  // diary off;
486  // endfunction
487  //
488  // will do the right thing.
489 
491 
492  if (external_diary_file.is_open ())
493  {
494  octave_diary.flush ();
495  external_diary_file.close ();
496  }
497 }
498 
499 static void
501 {
502  close_diary_file ();
503 
504  // If there is pending output in the pager buf, it should not go
505  // into the diary file.
506 
508 
509  external_diary_file.open (diary_file.c_str (), std::ios::app);
510 
511  if (! external_diary_file)
512  error ("diary: can't open diary file '%s'", diary_file.c_str ());
513 }
514 
515 DEFUN (diary, args, ,
516  "-*- texinfo -*-\n\
517 @deftypefn {Command} {} diary\n\
518 @deftypefnx {Command} {} diary on\n\
519 @deftypefnx {Command} {} diary off\n\
520 @deftypefnx {Command} {} diary @var{filename}\n\
521 Record a list of all commands @emph{and} the output they produce, mixed\n\
522 together just as they appear on the terminal.\n\
523 \n\
524 Valid options are:\n\
525 \n\
526 @table @asis\n\
527 @item on\n\
528 Start recording a session in a file called @file{diary} in the\n\
529 current working directory.\n\
530 \n\
531 @item off\n\
532 Stop recording the session in the diary file.\n\
533 \n\
534 @item @var{filename}\n\
535 Record the session in the file named @var{filename}.\n\
536 @end table\n\
537 \n\
538 With no arguments, @code{diary} toggles the current diary state.\n\
539 @seealso{history}\n\
540 @end deftypefn")
541 {
542  octave_value_list retval;
543 
544  int argc = args.length () + 1;
545 
546  string_vector argv = args.make_argv ("diary");
547 
548  if (error_state)
549  return retval;
550 
551  if (diary_file.empty ())
552  diary_file = "diary";
553 
554  switch (argc)
555  {
556  case 1:
558  open_diary_file ();
559  break;
560 
561  case 2:
562  {
563  std::string arg = argv[1];
564 
565  if (arg == "on")
566  {
567  write_to_diary_file = true;
568  open_diary_file ();
569  }
570  else if (arg == "off")
571  {
572  close_diary_file ();
573  write_to_diary_file = false;
574  }
575  else
576  {
577  diary_file = arg;
578  write_to_diary_file = true;
579  open_diary_file ();
580  }
581  }
582  break;
583 
584  default:
585  print_usage ();
586  break;
587  }
588 
589  return retval;
590 }
591 
592 DEFUN (more, args, ,
593  "-*- texinfo -*-\n\
594 @deftypefn {Command} {} more\n\
595 @deftypefnx {Command} {} more on\n\
596 @deftypefnx {Command} {} more off\n\
597 Turn output pagination on or off. Without an argument, @code{more}\n\
598 toggles the current state.\n\
599 The current state can be determined via @code{page_screen_output}.\n\
600 @seealso{page_screen_output, page_output_immediately, PAGER, PAGER_FLAGS}\n\
601 @end deftypefn")
602 {
603  octave_value_list retval;
604 
605  int argc = args.length () + 1;
606 
607  string_vector argv = args.make_argv ("more");
608 
609  if (error_state)
610  return retval;
611 
612  if (argc == 2)
613  {
614  std::string arg = argv[1];
615 
616  if (arg == "on")
617  Vpage_screen_output = true;
618  else if (arg == "off")
619  Vpage_screen_output = false;
620  else
621  error ("more: unrecognized argument '%s'", arg.c_str ());
622  }
623  else if (argc == 1)
625  else
626  print_usage ();
627 
628  return retval;
629 }
630 
631 DEFUN (terminal_size, , ,
632  "-*- texinfo -*-\n\
633 @deftypefn {Built-in Function} {} terminal_size ()\n\
634 Return a two-element row vector containing the current size of the\n\
635 terminal window in characters (rows and columns).\n\
636 @seealso{list_in_columns}\n\
637 @end deftypefn")
638 {
639  RowVector size (2, 0.0);
640 
643 
644  return octave_value (size);
645 }
646 
647 DEFUN (page_output_immediately, args, nargout,
648  "-*- texinfo -*-\n\
649 @deftypefn {Built-in Function} {@var{val} =} page_output_immediately ()\n\
650 @deftypefnx {Built-in Function} {@var{old_val} =} page_output_immediately (@var{new_val})\n\
651 @deftypefnx {Built-in Function} {} page_output_immediately (@var{new_val}, \"local\")\n\
652 Query or set the internal variable that controls whether Octave sends\n\
653 output to the pager as soon as it is available. Otherwise, Octave\n\
654 buffers its output and waits until just before the prompt is printed to\n\
655 flush it to the pager.\n\
656 \n\
657 When called from inside a function with the @qcode{\"local\"} option, the\n\
658 variable is changed locally for the function and any subroutines it calls. \n\
659 The original variable value is restored when exiting the function.\n\
660 @seealso{page_screen_output, more, PAGER, PAGER_FLAGS}\n\
661 @end deftypefn")
662 {
663  return SET_INTERNAL_VARIABLE (page_output_immediately);
664 }
665 
666 DEFUN (page_screen_output, args, nargout,
667  "-*- texinfo -*-\n\
668 @deftypefn {Built-in Function} {@var{val} =} page_screen_output ()\n\
669 @deftypefnx {Built-in Function} {@var{old_val} =} page_screen_output (@var{new_val})\n\
670 @deftypefnx {Built-in Function} {} page_screen_output (@var{new_val}, \"local\")\n\
671 Query or set the internal variable that controls whether output intended\n\
672 for the terminal window that is longer than one page is sent through a\n\
673 pager. This allows you to view one screenful at a time. Some pagers\n\
674 (such as @code{less}---see @ref{Installation}) are also capable of moving\n\
675 backward on the output.\n\
676 \n\
677 When called from inside a function with the @qcode{\"local\"} option, the\n\
678 variable is changed locally for the function and any subroutines it calls. \n\
679 The original variable value is restored when exiting the function.\n\
680 @seealso{more, page_output_immediately, PAGER, PAGER_FLAGS}\n\
681 @end deftypefn")
682 {
683  return SET_INTERNAL_VARIABLE (page_screen_output);
684 }
685 
686 DEFUN (PAGER, args, nargout,
687  "-*- texinfo -*-\n\
688 @deftypefn {Built-in Function} {@var{val} =} PAGER ()\n\
689 @deftypefnx {Built-in Function} {@var{old_val} =} PAGER (@var{new_val})\n\
690 @deftypefnx {Built-in Function} {} PAGER (@var{new_val}, \"local\")\n\
691 Query or set the internal variable that specifies the program to use\n\
692 to display terminal output on your system. The default value is\n\
693 normally @qcode{\"less\"}, @qcode{\"more\"}, or\n\
694 @qcode{\"pg\"}, depending on what programs are installed on your system.\n\
695 @xref{Installation}.\n\
696 \n\
697 When called from inside a function with the @qcode{\"local\"} option, the\n\
698 variable is changed locally for the function and any subroutines it calls. \n\
699 The original variable value is restored when exiting the function.\n\
700 @seealso{PAGER_FLAGS, page_output_immediately, more, page_screen_output}\n\
701 @end deftypefn")
702 {
704 }
705 
706 DEFUN (PAGER_FLAGS, args, nargout,
707  "-*- texinfo -*-\n\
708 @deftypefn {Built-in Function} {@var{val} =} PAGER_FLAGS ()\n\
709 @deftypefnx {Built-in Function} {@var{old_val} =} PAGER_FLAGS (@var{new_val})\n\
710 @deftypefnx {Built-in Function} {} PAGER_FLAGS (@var{new_val}, \"local\")\n\
711 Query or set the internal variable that specifies the options to pass\n\
712 to the pager.\n\
713 \n\
714 When called from inside a function with the @qcode{\"local\"} option, the\n\
715 variable is changed locally for the function and any subroutines it calls. \n\
716 The original variable value is restored when exiting the function.\n\
717 @seealso{PAGER, more, page_screen_output, page_output_immediately}\n\
718 @end deftypefn")
719 {
720  return SET_NONEMPTY_INTERNAL_STRING_VARIABLE (PAGER_FLAGS);
721 }