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