GNU Octave 7.1.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-2022 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
55OCTAVE_NAMESPACE_BEGIN
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 () + m_diary_skip;
150
151 std::size_t len = pptr () - buf;
152
153 octave_diary.write (buf, len);
154
155 m_diary_skip = 0;
156 }
157
158 void
160 {
161 m_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), m_pb (nullptr)
187 {
188 m_pb = new pager_buf ();
189 rdbuf (m_pb);
190 setf (unitbuf);
191 }
192
194 {
195 flush ();
196 delete m_pb;
197 }
198
199 std::ostream& pager_stream::stream (void)
200 {
201 return *this;
202 }
203
205 {
206 if (m_pb)
208 }
209
211 {
212 if (m_pb)
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 m_pb;
224 m_pb = new pager_buf ();
225 rdbuf (m_pb);
226 setf (unitbuf);
227 }
228
229 diary_stream::diary_stream (void) : std::ostream (nullptr), m_db (nullptr)
230 {
231 m_db = new diary_buf ();
232 rdbuf (m_db);
233 setf (unitbuf);
234 }
235
237 {
238 flush ();
239 delete m_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 m_db;
255 m_db = new diary_buf ();
256 rdbuf (m_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 {
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_var<bool> restore_var1 (m_really_flush_to_pager);
328 unwind_protect_var<bool> restore_var2 (m_flushing_output_to_pager);
329
332
333 std::ostream& pager_ostream = m_pager_stream.stream ();
334
335 pager_ostream.flush ();
336
338 }
339 }
340
342 {
343 // Try to flush the current buffer to the diary now, so that things
344 // like
345 //
346 // function foo ()
347 // diary on;
348 // ...
349 // diary off;
350 // endfunction
351 //
352 // will do the right thing.
353
355
356 if (m_external_diary_file.is_open ())
357 {
358 octave_diary.flush ();
359 m_external_diary_file.close ();
360 }
361 }
362
364 {
365 close_diary ();
366
367 // If there is pending output in the pager buf, it should not go
368 // into the diary file.
369
371
372 m_external_diary_file.open (m_diary_file_name.c_str (), std::ios::app);
373
375 error ("diary: can't open diary file '%s'", m_diary_file_name.c_str ());
376 }
377
378 bool output_system::sync (const char *buf, int len)
379 {
380 // FIXME: The following seems to be a bit of a mess.
381
388 {
389 bool bypass_pager = (m_interpreter.server_mode ()
396 && ! more_than_a_screenful (buf, len)));
397
398 if (len > 0)
399 {
400 do_sync (buf, len, bypass_pager);
401
402 return true;
403 }
404 }
405
406 return false;
407 }
408
410 {
412 {
413 child_list& kids = m_interpreter.get_child_list ();
414
415 kids.remove (m_external_pager->pid ());
416
417 delete m_external_pager;
418 m_external_pager = nullptr;
419 }
420 }
421
423 {
425 return;
426
427 std::string pgr = pager_command ();
428
429 if (! pgr.empty ())
430 {
431 m_external_pager = new oprocstream (pgr.c_str ());
432
433 child_list& kids = m_interpreter.get_child_list ();
434
435 kids.insert (m_external_pager->pid (),
437 }
438 }
439
440 void output_system::do_sync (const char *msg, int len, bool bypass_pager)
441 {
442 if (msg && len > 0)
443 {
444 if (bypass_pager)
445 {
447 {
449
450 evmgr.interpreter_output (std::string (msg, len));
451 }
452 else
453 {
454 std::cout.write (msg, len);
455 std::cout.flush ();
456 }
457 }
458 else
459 {
461
463 {
464 if (m_external_pager->good ())
465 {
466 m_external_pager->write (msg, len);
467
468 m_external_pager->flush ();
469
470#if defined (EPIPE)
471 if (errno == EPIPE)
472 m_external_pager->setstate (std::ios::failbit);
473#endif
474 }
475 else
476 {
477 // FIXME: something is not right with the
478 // pager. If it died then we should receive a
479 // signal for that. If there is some other problem,
480 // then what?
481 }
482 }
483 else
484 {
485 std::cout.write (msg, len);
486 std::cout.flush ();
487 }
488 }
489 }
490 }
491
492 std::ostream& __stdout__ (void)
493 {
494 output_system& output_sys = __get_output_system__ ("__stdout__");
495
496 return output_sys.__stdout__ ();
497 }
498
499 std::ostream& __diary__ (void)
500 {
501 output_system& output_sys = __get_output_system__ ("__diary__");
502
503 return output_sys.__diary__ ();
504 }
505
506DEFMETHOD (diary, interp, args, nargout,
507 doc: /* -*- texinfo -*-
508@deftypefn {} {} diary
509@deftypefnx {} {} diary on
510@deftypefnx {} {} diary off
511@deftypefnx {} {} diary @var{filename}
512@deftypefnx {} {[@var{status}, @var{diaryfile}] =} diary
513Record a list of all commands @emph{and} the output they produce, mixed
514together just as they appear on the terminal.
515
516Valid options are:
517
518@table @asis
519@item on
520Start recording a session in a file called @file{diary} in the current working
521directory.
522
523@item off
524Stop recording the session in the diary file.
525
526@item @var{filename}
527Record the session in the file named @var{filename}.
528@end table
529
530With no input or output arguments, @code{diary} toggles the current diary
531state.
532
533If output arguments are requested, @code{diary} ignores inputs and returns
534the current status. The boolean @var{status} indicates whether recording is on
535or off, and @var{diaryfile} is the name of the file where the session is
536stored.
537@seealso{history, evalc}
538@end deftypefn */)
539{
540 int nargin = args.length ();
541
542 if (nargin > 1)
543 print_usage ();
544
545 output_system& output_sys = interp.get_output_system ();
546
547 if (nargout > 0)
548 {
549 // Querying diary variables
550 if (nargout == 1)
551 return ovl (output_sys.write_to_diary_file ());
552 else
553 return ovl (output_sys.write_to_diary_file (),
554 output_sys.diary_file_name ());
555 }
556
557 if (nargin == 0)
558 {
559 output_sys.write_to_diary_file (! output_sys.write_to_diary_file ());
560 output_sys.open_diary ();
561 }
562 else
563 {
564 std::string arg = args(0).xstring_value ("diary: argument must be a string");
565
566 if (arg == "on")
567 {
568 output_sys.write_to_diary_file (true);
569 output_sys.open_diary ();
570 }
571 else if (arg == "off")
572 {
573 output_sys.close_diary ();
574 output_sys.write_to_diary_file (false);
575 }
576 else
577 {
578 output_sys.diary_file_name (arg);
579 output_sys.write_to_diary_file (true);
580 output_sys.open_diary ();
581 }
582 }
583
584 return ovl ();
585}
586
587DEFMETHOD (more, interp, args, ,
588 doc: /* -*- texinfo -*-
589@deftypefn {} {} more
590@deftypefnx {} {} more on
591@deftypefnx {} {} more off
592Turn output pagination on or off.
593
594Without an argument, @code{more} toggles the current state.
595
596The current state can be determined via @code{page_screen_output}.
597@seealso{page_screen_output, page_output_immediately, PAGER, PAGER_FLAGS}
598@end deftypefn */)
599{
600 int nargin = args.length ();
601
602 if (nargin > 1)
603 print_usage ();
604
605 output_system& output_sys = interp.get_output_system ();
606
607 if (nargin > 0)
608 {
609 std::string arg = args(0).xstring_value (R"(more: argument must be string "on" or "off")");
610
611 if (arg == "on")
612 output_sys.page_screen_output (true);
613 else if (arg == "off")
614 output_sys.page_screen_output (false);
615 else
616 error (R"(more: argument must be "on" or "off")");
617 }
618 else
619 output_sys.page_screen_output (! output_sys.page_screen_output ());
620
621 return ovl ();
622}
623
624DEFUN (terminal_size, args, ,
625 doc: /* -*- texinfo -*-
626@deftypefn {} {} terminal_size ()
627Query or set the size of the terminal window. If called with no
628arguments, return a two-element row vector containing the current size
629of the terminal window in characters (rows and columns). If called with
630a two-element vector of integer values, set the terminal size and return
631the previous setting. Setting the size manually should not be needed
632when using readline for command-line editing.
633@seealso{list_in_columns}
634@end deftypefn */)
635{
636 int nargin = args.length ();
637
638 if (nargin > 1)
639 print_usage ();
640
641 RowVector size (2, 0.0);
642
643 size(0) = command_editor::terminal_rows ();
644 size(1) = command_editor::terminal_cols ();
645
646 if (nargin == 1)
647 {
648 Matrix m = args(0).xmatrix_value ("argument must be a 2-element array");
649
650 if (m.numel () != 2)
651 error ("terminal_size: argument must be a 2-element array");
652
653 int rows = math::x_nint (m(0));
654 int cols = math::x_nint (m(1));
655
656 if (rows <= 0 || cols <= 0)
657 error ("terminal_size: rows and columns must be positive integers");
658
659 command_editor::set_screen_size (rows, cols);
660 }
661
662 return ovl (size);
663}
664
665DEFMETHOD (page_output_immediately, interp, args, nargout,
666 doc: /* -*- texinfo -*-
667@deftypefn {} {@var{val} =} page_output_immediately ()
668@deftypefnx {} {@var{old_val} =} page_output_immediately (@var{new_val})
669@deftypefnx {} {} page_output_immediately (@var{new_val}, "local")
670Query or set the internal variable that controls whether Octave sends
671output to the pager as soon as it is available.
672
673When the value is @code{false}, Octave buffers its output and waits until just
674before the prompt is printed to flush it to the pager. This is the default.
675
676When @code{page_screen_output} is @code{false}, this variable has no effect.
677
678When called from inside a function with the @qcode{"local"} option, the
679variable is changed locally for the function and any subroutines it calls.
680The original variable value is restored when exiting the function.
681@seealso{page_screen_output, more, PAGER, PAGER_FLAGS}
682@end deftypefn */)
683{
684 output_system& output_sys = interp.get_output_system ();
685
686 return output_sys.page_output_immediately (args, nargout);
687}
688
689DEFMETHOD (page_screen_output, interp, args, nargout,
690 doc: /* -*- texinfo -*-
691@deftypefn {} {@var{val} =} page_screen_output ()
692@deftypefnx {} {@var{old_val} =} page_screen_output (@var{new_val})
693@deftypefnx {} {} page_screen_output (@var{new_val}, "local")
694Query or set the internal variable that controls whether output intended
695for the terminal window that is longer than one page is sent through a
696pager.
697
698This allows you to view one screenful at a time. Some pagers
699(such as @code{less}---@pxref{Installation}) are also capable of moving
700backward on the output.
701
702When called from inside a function with the @qcode{"local"} option, the
703variable is changed locally for the function and any subroutines it calls.
704The original variable value is restored when exiting the function.
705@seealso{more, page_output_immediately, PAGER, PAGER_FLAGS}
706@end deftypefn */)
707{
708 output_system& output_sys = interp.get_output_system ();
709
710 return output_sys.page_screen_output (args, nargout);
711}
712
713DEFMETHOD (PAGER, interp, args, nargout,
714 doc: /* -*- texinfo -*-
715@deftypefn {} {@var{val} =} PAGER ()
716@deftypefnx {} {@var{old_val} =} PAGER (@var{new_val})
717@deftypefnx {} {} PAGER (@var{new_val}, "local")
718Query or set the internal variable that specifies the program to use
719to display terminal output on your system.
720
721The default value is normally @qcode{"less"}, @qcode{"more"}, or
722@qcode{"pg"}, depending on what programs are installed on your system.
723@xref{Installation}.
724
725When called from inside a function with the @qcode{"local"} option, the
726variable is changed locally for the function and any subroutines it calls.
727The original variable value is restored when exiting the function.
728@seealso{PAGER_FLAGS, page_output_immediately, more, page_screen_output}
729@end deftypefn */)
730{
731 output_system& output_sys = interp.get_output_system ();
732
733 return output_sys.PAGER (args, nargout);
734}
735
736DEFMETHOD (PAGER_FLAGS, interp, args, nargout,
737 doc: /* -*- texinfo -*-
738@deftypefn {} {@var{val} =} PAGER_FLAGS ()
739@deftypefnx {} {@var{old_val} =} PAGER_FLAGS (@var{new_val})
740@deftypefnx {} {} PAGER_FLAGS (@var{new_val}, "local")
741Query or set the internal variable that specifies the options to pass
742to the pager.
743
744When called from inside a function with the @qcode{"local"} option, the
745variable is changed locally for the function and any subroutines it calls.
746The original variable value is restored when exiting the function.
747@seealso{PAGER, more, page_screen_output, page_output_immediately}
748@end deftypefn */)
749{
750 output_system& output_sys = interp.get_output_system ();
751
752 return output_sys.PAGER_FLAGS (args, nargout);
753}
754
755OCTAVE_NAMESPACE_END
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:411
Definition: dMatrix.h:42
static bool forced_interactive(void)
Definition: octave.cc:325
int sync(void)
Definition: pager.cc:165
diary_stream(void)
Definition: pager.cc:229
void reset(void)
Definition: pager.cc:252
std::ostream & stream(void)
Definition: pager.cc:242
~diary_stream(void)
Definition: pager.cc:236
diary_buf * m_db
Definition: pager.h:129
Provides threadsafe access to octave.
bool interpreter_output(const std::string &msg)
bool server_mode(void) const
Definition: interpreter.h:169
event_manager & get_event_manager(void)
Definition: interpreter.h:328
bool interactive(void) const
Definition: interpreter.h:171
child_list & get_child_list(void)
Definition: interpreter.h:311
std::ofstream & external_diary_file(void)
Definition: pager.h:246
pager_stream m_pager_stream
Definition: pager.h:268
std::string PAGER_FLAGS(void) const
Definition: pager.h:172
std::ostream & __diary__(void)
Definition: pager.h:262
std::string PAGER(void) const
Definition: pager.h:161
diary_stream m_diary_stream
Definition: pager.h:270
void close_diary(void)
Definition: pager.cc:341
std::string m_PAGER
Definition: pager.h:282
bool page_screen_output(void) const
Definition: pager.h:199
void reset(void)
Definition: pager.cc:315
interpreter & m_interpreter
Definition: pager.h:266
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
octave_value PAGER_FLAGS(const octave_value_list &args, int nargout)
Definition: pager.cc:282
std::ostream & __stdout__(void)
Definition: pager.h:260
void open_diary(void)
Definition: pager.cc:363
bool m_flushing_output_to_pager
Definition: pager.h:301
oprocstream * m_external_pager
Definition: pager.h:273
bool sync(const char *msg, int len)
Definition: pager.cc:378
bool write_to_diary_file(void) const
Definition: pager.h:208
void start_external_pager(void)
Definition: pager.cc:422
void clear_external_pager(void)
Definition: pager.cc:409
std::string pager_command(void) const
Definition: pager.cc:305
bool m_page_screen_output
Definition: pager.h:294
void flush_stdout(void)
Definition: pager.cc:323
output_system(interpreter &interp)
Definition: pager.cc:267
std::string m_PAGER_FLAGS
Definition: pager.h:285
octave_value PAGER(const octave_value_list &args, int nargout)
Definition: pager.cc:276
bool page_output_immediately(void) const
Definition: pager.h:184
bool m_really_flush_to_pager
Definition: pager.h:299
std::string m_diary_file_name
Definition: pager.h:279
bool m_page_output_immediately
Definition: pager.h:290
void do_sync(const char *msg, int len, bool bypass_pager)
Definition: pager.cc:440
std::ofstream m_external_diary_file
Definition: pager.h:276
octave_value page_output_immediately(const octave_value_list &args, int nargout)
Definition: pager.cc:290
int sync(void)
Definition: pager.cc:128
void set_diary_skip(void)
Definition: pager.cc:159
std::size_t m_diary_skip
Definition: pager.h:62
void flush_current_contents_to_diary(void)
Definition: pager.cc:147
~pager_stream(void)
Definition: pager.cc:193
std::ostream & stream(void)
Definition: pager.cc:199
void flush_current_contents_to_diary(void)
Definition: pager.cc:204
pager_stream(void)
Definition: pager.cc:186
pager_buf * m_pb
Definition: pager.h:91
void set_diary_skip(void)
Definition: pager.cc:210
void reset(void)
Definition: pager.cc:221
pid_t pid(void) const
Definition: procstream.h:65
OCTINTERP_API void print_usage(void)
Definition: defun-int.h:72
#define DEFMETHOD(name, interp_name, args_name, nargout_name, doc)
Macro to define a builtin method.
Definition: defun.h:111
#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:980
std::string default_pager(void)
Definition: defaults.cc:150
T x_nint(T x)
Definition: lo-mappers.h:269
std::ofstream ofstream(const std::string &filename, const std::ios::openmode mode)
Definition: lo-sysdep.cc:414
bool wifsignaled(int status)
bool wifexited(int status)
output_system & __get_output_system__(const std::string &who)
STL namespace.
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211
static std::string default_pager(void)
Definition: pager.cc:117
static OCTAVE_NAMESPACE_BEGIN bool pager_event_handler(pid_t pid, int status)
Definition: pager.cc:58
std::ostream & __diary__(void)
Definition: pager.cc:499
std::ostream & __stdout__(void)
Definition: pager.cc:492
static bool more_than_a_screenful(const char *s, int len)
Definition: pager.cc:87
void flush_stdout(void)
Definition: pager.cc:260
#define octave_diary
Definition: pager.h:316
octave_value set_internal_variable(bool &var, const octave_value_list &args, int nargout, const char *nm)
Definition: variables.cc:587
F77_RET_T len
Definition: xerbla.cc:61