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