GNU Octave  4.0.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-2015 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 ("diary");
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 {
237  || ! Vpage_screen_output)
238  {
239  char *buf = eback ();
240 
241  int len = pptr () - buf;
242 
243  bool bypass_pager = (! interactive || forced_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 
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 (__diaryfile__, , ,
593  "-*- texinfo -*-\n\
594 @deftypefn {Built-in Function} {@var{fname} =} __diaryfile__ ()\n\
595 Undocumented internal function\n\
596 @end deftypefn")
597 {
598  return ovl (diary_file);
599 }
600 
601 DEFUN (__diarystate__, , ,
602  "-*- texinfo -*-\n\
603 @deftypefn {Built-in Function} {@var{state} =} __diarystate__ ()\n\
604 Undocumented internal function\n\
605 @end deftypefn")
606 {
607  return ovl (write_to_diary_file);
608 }
609 
610 DEFUN (more, args, ,
611  "-*- texinfo -*-\n\
612 @deftypefn {Command} {} more\n\
613 @deftypefnx {Command} {} more on\n\
614 @deftypefnx {Command} {} more off\n\
615 Turn output pagination on or off.\n\
616 \n\
617 Without an argument, @code{more} toggles the current state.\n\
618 \n\
619 The current state can be determined via @code{page_screen_output}.\n\
620 @seealso{page_screen_output, page_output_immediately, PAGER, PAGER_FLAGS}\n\
621 @end deftypefn")
622 {
623  octave_value_list retval;
624 
625  int argc = args.length () + 1;
626 
627  string_vector argv = args.make_argv ("more");
628 
629  if (error_state)
630  return retval;
631 
632  if (argc == 2)
633  {
634  std::string arg = argv[1];
635 
636  if (arg == "on")
637  Vpage_screen_output = true;
638  else if (arg == "off")
639  Vpage_screen_output = false;
640  else
641  error ("more: unrecognized argument '%s'", arg.c_str ());
642  }
643  else if (argc == 1)
645  else
646  print_usage ();
647 
648  return retval;
649 }
650 
651 DEFUN (terminal_size, , ,
652  "-*- texinfo -*-\n\
653 @deftypefn {Built-in Function} {} terminal_size ()\n\
654 Return a two-element row vector containing the current size of the terminal\n\
655 window in characters (rows and columns).\n\
656 @seealso{list_in_columns}\n\
657 @end deftypefn")
658 {
659  RowVector size (2, 0.0);
660 
663 
664  return octave_value (size);
665 }
666 
667 DEFUN (page_output_immediately, args, nargout,
668  "-*- texinfo -*-\n\
669 @deftypefn {Built-in Function} {@var{val} =} page_output_immediately ()\n\
670 @deftypefnx {Built-in Function} {@var{old_val} =} page_output_immediately (@var{new_val})\n\
671 @deftypefnx {Built-in Function} {} page_output_immediately (@var{new_val}, \"local\")\n\
672 Query or set the internal variable that controls whether Octave sends\n\
673 output to the pager as soon as it is available.\n\
674 \n\
675 Otherwise, Octave buffers its output and waits until just before the prompt\n\
676 is printed to flush it to the pager.\n\
677 \n\
678 When called from inside a function with the @qcode{\"local\"} option, the\n\
679 variable is changed locally for the function and any subroutines it calls.\n\
680 The original variable value is restored when exiting the function.\n\
681 @seealso{page_screen_output, more, PAGER, PAGER_FLAGS}\n\
682 @end deftypefn")
683 {
684  return SET_INTERNAL_VARIABLE (page_output_immediately);
685 }
686 
687 DEFUN (page_screen_output, args, nargout,
688  "-*- texinfo -*-\n\
689 @deftypefn {Built-in Function} {@var{val} =} page_screen_output ()\n\
690 @deftypefnx {Built-in Function} {@var{old_val} =} page_screen_output (@var{new_val})\n\
691 @deftypefnx {Built-in Function} {} page_screen_output (@var{new_val}, \"local\")\n\
692 Query or set the internal variable that controls whether output intended\n\
693 for the terminal window that is longer than one page is sent through a\n\
694 pager.\n\
695 \n\
696 This allows you to view one screenful at a time. Some pagers\n\
697 (such as @code{less}---see @ref{Installation}) are also capable of moving\n\
698 backward on the output.\n\
699 \n\
700 When called from inside a function with the @qcode{\"local\"} option, the\n\
701 variable is changed locally for the function and any subroutines it calls.\n\
702 The original variable value is restored when exiting the function.\n\
703 @seealso{more, page_output_immediately, PAGER, PAGER_FLAGS}\n\
704 @end deftypefn")
705 {
706  return SET_INTERNAL_VARIABLE (page_screen_output);
707 }
708 
709 DEFUN (PAGER, args, nargout,
710  "-*- texinfo -*-\n\
711 @deftypefn {Built-in Function} {@var{val} =} PAGER ()\n\
712 @deftypefnx {Built-in Function} {@var{old_val} =} PAGER (@var{new_val})\n\
713 @deftypefnx {Built-in Function} {} PAGER (@var{new_val}, \"local\")\n\
714 Query or set the internal variable that specifies the program to use\n\
715 to display terminal output on your system.\n\
716 \n\
717 The default value is normally @qcode{\"less\"}, @qcode{\"more\"}, or\n\
718 @qcode{\"pg\"}, depending on what programs are installed on your system.\n\
719 @xref{Installation}.\n\
720 \n\
721 When called from inside a function with the @qcode{\"local\"} option, the\n\
722 variable is changed locally for the function and any subroutines it calls.\n\
723 The original variable value is restored when exiting the function.\n\
724 @seealso{PAGER_FLAGS, page_output_immediately, more, page_screen_output}\n\
725 @end deftypefn")
726 {
728 }
729 
730 DEFUN (PAGER_FLAGS, args, nargout,
731  "-*- texinfo -*-\n\
732 @deftypefn {Built-in Function} {@var{val} =} PAGER_FLAGS ()\n\
733 @deftypefnx {Built-in Function} {@var{old_val} =} PAGER_FLAGS (@var{new_val})\n\
734 @deftypefnx {Built-in Function} {} PAGER_FLAGS (@var{new_val}, \"local\")\n\
735 Query or set the internal variable that specifies the options to pass\n\
736 to the pager.\n\
737 \n\
738 When called from inside a function with the @qcode{\"local\"} option, the\n\
739 variable is changed locally for the function and any subroutines it calls.\n\
740 The original variable value is restored when exiting the function.\n\
741 @seealso{PAGER, more, page_screen_output, page_output_immediately}\n\
742 @end deftypefn")
743 {
744  return SET_NONEMPTY_INTERNAL_STRING_VARIABLE (PAGER_FLAGS);
745 }
static void close_diary_file(void)
Definition: pager.cc:477
void flush_octave_stdout(void)
Definition: pager.cc:458
static int terminal_cols(void)
Definition: cmd-edit.cc:1133
static hash_table_type db
Definition: kpse.cc:1727
void do_reset(void)
Definition: pager.cc:426
static void reset(void)
Definition: pager.cc:340
OCTINTERP_API void print_usage(void)
Definition: defun.cc:51
octave_idx_type length(void) const
Definition: oct-obj.h:89
static bool pager_event_handler(pid_t pid, int status)
Definition: pager.cc:105
static bool ifexited(int status)
Definition: lo-utils.h:145
static std::ostream & stream(void)
Definition: pager.cc:315
static bool instance_ok(void)
Definition: pager.cc:435
static bool really_flush_to_pager
Definition: pager.cc:88
void protect_var(T &var)
#define DEFUN(name, args_name, nargout_name, doc)
Definition: defun.h:44
void error(const char *fmt,...)
Definition: error.cc:476
static void clear_external_pager(void)
Definition: pager.cc:93
bool forced_interactive
Definition: input.cc:107
size_t diary_skip
Definition: pager.h:50
#define SET_INTERNAL_VARIABLE(NM)
Definition: variables.h:120
STL namespace.
int sync(void)
Definition: pager.cc:282
#define octave_diary
Definition: pager.h:146
static bool instance_ok(void)
Definition: pager.cc:370
static void cleanup_instance(void)
Definition: pager.h:133
static int terminal_rows(void)
Definition: cmd-edit.cc:1126
void set_diary_skip(void)
Definition: pager.cc:276
static void do_sync(const char *msg, int len, bool bypass_pager)
Definition: pager.cc:143
void do_set_diary_skip(void)
Definition: pager.cc:354
static octave_diary_stream * instance
Definition: pager.h:129
static std::string VPAGER
Definition: pager.cc:74
octave_pager_buf * pb
Definition: pager.h:87
#define OCTAVE_DEFAULT_PAGER
Definition: defaults.h:36
bool interactive
Definition: input.cc:103
~octave_diary_stream(void)
Definition: pager.cc:401
static void add(fptr f)
static void flush_current_contents_to_diary(void)
Definition: pager.cc:321
static bool Vpage_output_immediately
Definition: pager.cc:82
void do_flush_current_contents_to_diary(void)
Definition: pager.cc:347
static void reset(void)
Definition: pager.cc:419
int error_state
Definition: error.cc:101
static bool write_to_diary_file
Definition: pager.cc:52
static std::string getenv(const std::string &name)
Definition: oct-env.cc:238
static void insert(pid_t pid, octave_child::child_event_handler f)
Definition: sighandlers.cc:941
static void remove(pid_t pid)
Definition: sighandlers.cc:974
octave_pager_stream(void)
Definition: pager.cc:301
static octave_pager_stream * instance
Definition: pager.h:81
size_t size(T const (&)[z])
Definition: help.cc:103
static bool more_than_a_screenful(const char *s, int len)
Definition: pager.cc:201
#define SET_NONEMPTY_INTERNAL_STRING_VARIABLE(NM)
Definition: variables.h:123
static std::string VPAGER_FLAGS
Definition: pager.cc:77
double arg(double x)
Definition: lo-mappers.h:37
void flush_current_contents_to_diary(void)
Definition: pager.cc:264
static std::string pager_command(void)
Definition: pager.cc:132
pid_t pid(void) const
Definition: procstream.h:57
#define octave_stdout
Definition: pager.h:144
static void open_diary_file(void)
Definition: pager.cc:500
octave_value_list ovl(const octave_value &a0)
Definition: oct-obj.h:178
static std::string default_pager(void)
Definition: pager.cc:61
static std::string diary_file("diary")
~octave_pager_stream(void)
Definition: pager.cc:308
static void cleanup_instance(void)
Definition: pager.h:85
static bool ifsignaled(int status)
Definition: lo-utils.h:155
static void set_diary_skip(void)
Definition: pager.cc:328
octave_diary_buf * db
Definition: pager.h:135
void do_reset(void)
Definition: pager.cc:361
static std::ofstream external_diary_file
Definition: pager.cc:58
static bool flushing_output_to_pager
Definition: pager.cc:90
static bool Vpage_screen_output
Definition: pager.cc:86
static std::ostream & stream(void)
Definition: pager.cc:408
int sync(void)
Definition: pager.cc:232
return octave_value(v1.char_array_value().concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string())? '\'': '"'))
octave_diary_stream(void)
Definition: pager.cc:394
static oprocstream * external_pager
Definition: pager.cc:49