GNU Octave 11.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
pager.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 1993-2026 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
56
57static bool
58pager_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
86static bool
87more_than_a_screenful (const char *s, int len)
88{
89 if (s)
90 {
91 int available_rows = command_editor::terminal_rows () - 2;
92
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
117static std::string
119{
120 std::string pager_binary = sys::env::getenv ("PAGER");
121
122 if (pager_binary.empty ())
123 pager_binary = config::default_pager ();
124
125 return pager_binary;
126}
127
128int
130{
131 output_system& output_sys = __get_output_system__ ();
132
133 char *buf = pbase ();
134
135 int len = pptr () - buf;
136
137 if (output_sys.sync (buf, len))
138 {
140
141 seekoff (0, std::ios::beg);
142 }
143
144 return 0;
145}
146
147void
149{
150 char *buf = pbase () + m_diary_skip;
151
152 std::size_t len = pptr () - buf;
153
154 octave_diary.write (buf, len);
155
156 m_diary_skip = 0;
157}
158
159void
161{
162 m_diary_skip = pptr () - pbase ();
163}
164
165int
167{
168 output_system& output_sys = __get_output_system__ ();
169
170 std::ofstream& external_diary_file = output_sys.external_diary_file ();
171
172 if (output_sys.write_to_diary_file () && external_diary_file)
173 {
174 char *buf = pbase ();
175
176 int len = pptr () - buf;
177
178 if (len > 0)
179 external_diary_file.write (buf, len);
180 }
181
182 seekoff (0, std::ios::beg);
183
184 return 0;
185}
186
187pager_stream::pager_stream () : std::ostream (nullptr), m_pb (nullptr)
188{
189 m_pb = new pager_buf ();
190 rdbuf (m_pb);
191 setf (unitbuf);
192}
193
195{
196 flush ();
197 delete m_pb;
198}
199
200std::ostream&
202{
203 return *this;
204}
205
206void
212
213void
215{
216 if (m_pb)
217 m_pb->set_diary_skip ();
218}
219
220// Reinitialize the pager buffer to avoid hanging on to large internal
221// buffers when they might not be needed. This function should only be
222// called when the pager is not in use. For example, just before
223// getting command-line input.
224
225void
227{
228 delete m_pb;
229 m_pb = new pager_buf ();
230 rdbuf (m_pb);
231 setf (unitbuf);
232}
233
234diary_stream::diary_stream () : std::ostream (nullptr), m_db (nullptr)
235{
236 m_db = new diary_buf ();
237 rdbuf (m_db);
238 setf (unitbuf);
239}
240
242{
243 flush ();
244 delete m_db;
245}
246
247std::ostream&
249{
250 return *this;
251}
252
253// Reinitialize the diary buffer to avoid hanging on to large internal
254// buffers when they might not be needed. This function should only be
255// called when the pager is not in use. For example, just before
256// getting command-line input.
257
258void
260{
261 delete m_db;
262 m_db = new diary_buf ();
263 rdbuf (m_db);
264 setf (unitbuf);
265}
266
267void
269{
270 output_system& output_sys = __get_output_system__ ();
271
272 output_sys.flush_stdout ();
273}
274
276 : m_interpreter (interp), m_pager_stream (), m_diary_stream (),
277 m_external_pager (nullptr), m_external_diary_file (),
278 m_diary_file_name ("diary"), m_PAGER (default_pager ()),
279 m_PAGER_FLAGS (), m_page_output_immediately (false),
280 m_page_screen_output (false), m_write_to_diary_file (false),
281 m_really_flush_to_pager (false), m_flushing_output_to_pager (false)
282{ }
283
286 int nargout)
287{
288 return set_internal_variable (m_PAGER, args, nargout, "PAGER", false);
289}
290
293 int nargout)
294{
295 return set_internal_variable (m_PAGER_FLAGS, args, nargout,
296 "PAGER_FLAGS", false);
297}
298
301 int nargout)
302{
303 return set_internal_variable (m_page_output_immediately, args, nargout,
304 "page_output_immediately");
305}
306
309 int nargout)
310{
311 return set_internal_variable (m_page_screen_output, args, nargout,
312 "page_screen_output");
313}
314
315std::string
317{
318 std::string cmd = m_PAGER;
319
320 if (! (cmd.empty () || m_PAGER_FLAGS.empty ()))
321 cmd += ' ' + m_PAGER_FLAGS;
322
323 return cmd;
324}
325
326void
328{
329 flush_stdout ();
330
331 m_pager_stream.reset ();
332 m_diary_stream.reset ();
333}
334
335void
337{
338 if (! m_flushing_output_to_pager)
339 {
340 unwind_protect_var<bool> restore_var1 (m_really_flush_to_pager);
341 unwind_protect_var<bool> restore_var2 (m_flushing_output_to_pager);
342
343 m_really_flush_to_pager = true;
344 m_flushing_output_to_pager = true;
345
346 std::ostream& pager_ostream = m_pager_stream.stream ();
347
348 pager_ostream.flush ();
349
351 }
352}
353
354void
356{
357 // Try to flush the current buffer to the diary now, so that things
358 // like
359 //
360 // function foo ()
361 // diary on;
362 // ...
363 // diary off;
364 // endfunction
365 //
366 // will do the right thing.
367
368 m_pager_stream.flush_current_contents_to_diary ();
369
370 if (m_external_diary_file.is_open ())
371 {
372 octave_diary.flush ();
373 m_external_diary_file.close ();
374 }
375}
376
377void
379{
380 close_diary ();
381
382 // If there is pending output in the pager buf, it should not go
383 // into the diary file.
384
385 m_pager_stream.set_diary_skip ();
386
387 m_external_diary_file.open (m_diary_file_name.c_str (), std::ios::app);
388
389 if (! m_external_diary_file)
390 error ("diary: can't open diary file '%s'", m_diary_file_name.c_str ());
391}
392
393bool
394output_system::sync (const char *buf, int len)
395{
396 // FIXME: The following seems to be a bit of a mess.
397
398 if (m_interpreter.server_mode ()
399 || ! m_interpreter.interactive ()
401 || m_really_flush_to_pager
402 || (m_page_screen_output && m_page_output_immediately)
403 || ! m_page_screen_output)
404 {
405 bool bypass_pager = (m_interpreter.server_mode ()
406 || ! m_interpreter.interactive ()
408 || ! m_page_screen_output
409 || (m_really_flush_to_pager
410 && m_page_screen_output
411 && ! m_page_output_immediately
412 && ! more_than_a_screenful (buf, len)));
413
414 if (len > 0)
415 {
416 do_sync (buf, len, bypass_pager);
417
418 return true;
419 }
420 }
421
422 return false;
423}
424
425void
427{
428 if (m_external_pager)
429 {
430 child_list& kids = m_interpreter.get_child_list ();
431
432 kids.remove (m_external_pager->pid ());
433
434 delete m_external_pager;
435 m_external_pager = nullptr;
436 }
437}
438
439void
440output_system::start_external_pager ()
441{
442 if (m_external_pager)
443 return;
444
445 std::string pgr = pager_command ();
446
447 if (! pgr.empty ())
448 {
449 m_external_pager = new oprocstream (pgr.c_str ());
450
451 child_list& kids = m_interpreter.get_child_list ();
452
453 kids.insert (m_external_pager->pid (),
454 pager_event_handler);
455 }
456}
457
458void
459output_system::do_sync (const char *msg, int len, bool bypass_pager)
460{
461 if (msg && len > 0)
462 {
463 if (bypass_pager)
464 {
465 if (m_interpreter.server_mode ())
466 {
467 event_manager& evmgr = m_interpreter.get_event_manager ();
468
469 evmgr.interpreter_output (std::string (msg, len));
470 }
471 else
472 {
473 std::cout.write (msg, len);
474 std::cout.flush ();
475 }
476 }
477 else
478 {
479 start_external_pager ();
480
481 if (m_external_pager)
482 {
483 if (m_external_pager->good ())
484 {
485 m_external_pager->write (msg, len);
486
487 m_external_pager->flush ();
488
489#if defined (EPIPE)
490 if (errno == EPIPE)
491 m_external_pager->setstate (std::ios::failbit);
492#endif
493 }
494 else
495 {
496 // FIXME: something is not right with the
497 // pager. If it died then we should receive a
498 // signal for that. If there is some other problem,
499 // then what?
500 }
501 }
502 else
503 {
504 std::cout.write (msg, len);
505 std::cout.flush ();
506 }
507 }
508 }
509}
510
511std::ostream&
513{
514 output_system& output_sys = __get_output_system__ ();
515
516 return output_sys.__stdout__ ();
517}
518
519std::ostream&
521{
522 output_system& output_sys = __get_output_system__ ();
523
524 return output_sys.__diary__ ();
525}
526
527DEFMETHOD (diary, interp, args, nargout,
528 doc: /* -*- texinfo -*-
529@deftypefn {} {} diary
530@deftypefnx {} {} diary on
531@deftypefnx {} {} diary off
532@deftypefnx {} {} diary @var{filename}
533@deftypefnx {} {[@var{status}, @var{diaryfile}] =} diary
534Record a list of all commands @emph{and} the output they produce, mixed
535together just as they appear on the terminal.
536
537Valid options are:
538
539@table @asis
540@item on
541Start or resume recording a session. If no @var{filename} has previously been
542specified recording will take place in a file called @file{diary} in the
543current working directory.
544
545@item off
546Stop recording the session in the diary file.
547
548@item @var{filename}
549Start or resume recording the session in the file named @var{filename}.
550@end table
551
552With no input or output arguments, @code{diary} toggles the current diary
553state.
554
555If output arguments are requested, @code{diary} ignores inputs and returns
556the current status. The boolean @var{status} indicates whether recording is on
557or off, and @var{diaryfile} is the name of the file where the session is
558stored.
559@seealso{history, evalc}
560@end deftypefn */)
561{
562 int nargin = args.length ();
563
564 if (nargin > 1)
565 print_usage ();
566
567 output_system& output_sys = interp.get_output_system ();
568
569 if (nargout > 0)
570 {
571 // Querying diary variables
572 if (nargout == 1)
573 return ovl (output_sys.write_to_diary_file ());
574 else
575 return ovl (output_sys.write_to_diary_file (),
576 output_sys.diary_file_name ());
577 }
578
579 if (nargin == 0)
580 {
581 output_sys.write_to_diary_file (! output_sys.write_to_diary_file ());
582 output_sys.open_diary ();
583 }
584 else
585 {
586 std::string arg = args(0).xstring_value ("diary: argument must be a string");
587
588 if (arg == "on")
589 {
590 output_sys.write_to_diary_file (true);
591 output_sys.open_diary ();
592 }
593 else if (arg == "off")
594 {
595 output_sys.close_diary ();
596 output_sys.write_to_diary_file (false);
597 }
598 else
599 {
600 output_sys.diary_file_name (arg);
601 output_sys.write_to_diary_file (true);
602 output_sys.open_diary ();
603 }
604 }
605
606 return ovl ();
607}
608
609DEFMETHOD (more, interp, args, ,
610 doc: /* -*- texinfo -*-
611@deftypefn {} {} more
612@deftypefnx {} {} more on
613@deftypefnx {} {} more off
614Turn output pagination on or off.
615
616Without an argument, @code{more} toggles the current state.
617
618The current state can be determined via @code{page_screen_output}.
619@seealso{page_screen_output, page_output_immediately, PAGER, PAGER_FLAGS}
620@end deftypefn */)
621{
622 int nargin = args.length ();
623
624 if (nargin > 1)
625 print_usage ();
626
627 output_system& output_sys = interp.get_output_system ();
628
629 if (nargin > 0)
630 {
631 std::string arg = args(0).xstring_value (R"(more: argument must be string "on" or "off")");
632
633 if (arg == "on")
634 output_sys.page_screen_output (true);
635 else if (arg == "off")
636 output_sys.page_screen_output (false);
637 else
638 error (R"(more: argument must be "on" or "off")");
639 }
640 else
641 output_sys.page_screen_output (! output_sys.page_screen_output ());
642
643 return ovl ();
644}
645
646DEFUN (terminal_size, args, ,
647 doc: /* -*- texinfo -*-
648@deftypefn {} {[@var{rows}, @var{cols}] =} terminal_size ()
649@deftypefnx {} {} terminal_size ([@var{rows}, @var{cols}])
650Query or set the size of the terminal window. If called with no arguments,
651return a two-element row vector containing the current size of the terminal
652window in characters (rows and columns). If called with a two-element vector
653of integer values, set the terminal size and return the previous setting.
654Setting the size manually should not be needed when using readline for
655command-line editing.
656@seealso{list_in_columns}
657@end deftypefn */)
658{
659 int nargin = args.length ();
660
661 if (nargin > 1)
662 print_usage ();
663
664 RowVector size (2, 0.0);
665
668
669 if (nargin == 1)
670 {
671 Matrix m = args(0).xmatrix_value ("argument must be a 2-element array");
672
673 if (m.numel () != 2)
674 error ("terminal_size: argument must be a 2-element array");
675
676 int rows = math::round (m(0));
677 int cols = math::round (m(1));
678
679 if (rows <= 0 || cols <= 0)
680 error ("terminal_size: rows and columns must be positive integers");
681
683 }
684
685 return ovl (size);
686}
687
688DEFMETHOD (page_output_immediately, interp, args, nargout,
689 doc: /* -*- texinfo -*-
690@deftypefn {} {@var{val} =} page_output_immediately ()
691@deftypefnx {} {@var{old_val} =} page_output_immediately (@var{new_val})
692@deftypefnx {} {@var{old_val} =} page_output_immediately (@var{new_val}, "local")
693Query or set the internal variable that controls whether Octave sends
694output to the pager as soon as it is available.
695
696When the value is @code{false}, Octave buffers its output and waits until just
697before the prompt is printed to flush it to the pager. This is the default.
698
699When @code{page_screen_output} is @code{false}, this variable has no effect.
700
701When called from inside a function with the @qcode{"local"} option, the
702variable is changed locally for the function and any subroutines it calls.
703The original variable value is restored when exiting the function.
704@seealso{page_screen_output, more, PAGER, PAGER_FLAGS}
705@end deftypefn */)
706{
707 output_system& output_sys = interp.get_output_system ();
708
709 return output_sys.page_output_immediately (args, nargout);
710}
711
712DEFMETHOD (page_screen_output, interp, args, nargout,
713 doc: /* -*- texinfo -*-
714@deftypefn {} {@var{val} =} page_screen_output ()
715@deftypefnx {} {@var{old_val} =} page_screen_output (@var{new_val})
716@deftypefnx {} {@var{old_val} =} page_screen_output (@var{new_val}, "local")
717Query or set the internal variable that controls whether output intended
718for the terminal window that is longer than one page is sent through a
719pager.
720
721This allows you to view one screenful at a time. Some pagers
722(such as @code{less}---@pxref{Installation}) are also capable of moving
723backward on the output.
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{more, page_output_immediately, PAGER, PAGER_FLAGS}
729@end deftypefn */)
730{
731 output_system& output_sys = interp.get_output_system ();
732
733 return output_sys.page_screen_output (args, nargout);
734}
735
736DEFMETHOD (PAGER, interp, args, nargout,
737 doc: /* -*- texinfo -*-
738@deftypefn {} {@var{val} =} PAGER ()
739@deftypefnx {} {@var{old_val} =} PAGER (@var{new_val})
740@deftypefnx {} {@var{old_val} =} PAGER (@var{new_val}, "local")
741Query or set the internal variable that specifies the program to use
742to display terminal output on your system.
743
744The default value is normally @qcode{"less"}, @qcode{"more"}, or
745@qcode{"pg"}, depending on what programs are installed on your system.
746@xref{Installation}.
747
748When called from inside a function with the @qcode{"local"} option, the
749variable is changed locally for the function and any subroutines it calls.
750The original variable value is restored when exiting the function.
751@seealso{PAGER_FLAGS, page_output_immediately, more, page_screen_output}
752@end deftypefn */)
753{
754 output_system& output_sys = interp.get_output_system ();
755
756 return output_sys.PAGER (args, nargout);
757}
758
759DEFMETHOD (PAGER_FLAGS, interp, args, nargout,
760 doc: /* -*- texinfo -*-
761@deftypefn {} {@var{val} =} PAGER_FLAGS ()
762@deftypefnx {} {@var{old_val} =} PAGER_FLAGS (@var{new_val})
763@deftypefnx {} {@var{old_val} =} PAGER_FLAGS (@var{new_val}, "local")
764Query or set the internal variable that specifies the options to pass
765to the pager.
766
767When called from inside a function with the @qcode{"local"} option, the
768variable is changed locally for the function and any subroutines it calls.
769The original variable value is restored when exiting the function.
770@seealso{PAGER, more, page_screen_output, page_output_immediately}
771@end deftypefn */)
772{
773 output_system& output_sys = interp.get_output_system ();
774
775 return output_sys.PAGER_FLAGS (args, nargout);
776}
777
778OCTAVE_END_NAMESPACE(octave)
octave_idx_type numel() const
Number of elements in the array.
Definition Array-base.h:440
static bool forced_interactive()
Definition octave.cc:342
void insert(pid_t pid, child::child_event_handler f)
void remove(pid_t pid)
Definition child-list.cc:35
static int terminal_rows()
Definition cmd-edit.cc:1240
static void set_screen_size(int ht, int wd)
Definition cmd-edit.cc:1266
static int terminal_cols()
Definition cmd-edit.cc:1246
int sync()
Definition pager.cc:166
void reset()
Definition pager.cc:259
~diary_stream()
Definition pager.cc:241
std::ostream & stream()
Definition pager.cc:248
Provides threadsafe access to octave.
bool interpreter_output(const std::string &msg)
bool interactive() const
child_list & get_child_list()
bool server_mode() const
event_manager & get_event_manager()
void close_diary()
Definition pager.cc:355
std::string PAGER_FLAGS() const
Definition pager.h:159
void open_diary()
Definition pager.cc:378
bool page_screen_output() const
Definition pager.h:186
bool page_output_immediately() const
Definition pager.h:171
void flush_stdout()
Definition pager.cc:336
std::string diary_file_name() const
Definition pager.h:137
octave_value page_screen_output(const octave_value_list &args, int nargout)
Definition pager.cc:308
std::ofstream & external_diary_file()
Definition pager.h:233
octave_value PAGER_FLAGS(const octave_value_list &args, int nargout)
Definition pager.cc:292
std::ostream & __stdout__()
Definition pager.h:247
bool sync(const char *msg, int len)
Definition pager.cc:394
void reset()
Definition pager.cc:327
void clear_external_pager()
Definition pager.cc:426
output_system(interpreter &interp)
Definition pager.cc:275
octave_value PAGER(const octave_value_list &args, int nargout)
Definition pager.cc:285
std::ostream & __diary__()
Definition pager.h:249
bool write_to_diary_file() const
Definition pager.h:195
std::string pager_command() const
Definition pager.cc:316
std::string PAGER() const
Definition pager.h:148
octave_value page_output_immediately(const octave_value_list &args, int nargout)
Definition pager.cc:300
void flush_current_contents_to_diary()
Definition pager.cc:148
int sync()
Definition pager.cc:129
void set_diary_skip()
Definition pager.cc:160
~pager_stream()
Definition pager.cc:194
void set_diary_skip()
Definition pager.cc:214
void reset()
Definition pager.cc:226
std::ostream & stream()
Definition pager.cc:201
void flush_current_contents_to_diary()
Definition pager.cc:207
pid_t pid() const
Definition procstream.h:65
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
std::string default_pager()
Definition defaults.cc:158
void print_usage()
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:1008
output_system & __get_output_system__()
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition ovl.h:217
std::ostream & __stdout__()
Definition pager.cc:512
void flush_stdout()
Definition pager.cc:268
std::ostream & __diary__()
Definition pager.cc:520
#define octave_diary
Definition pager.h:303
octave_value set_internal_variable(bool &var, const octave_value_list &args, int nargout, const char *nm)
Definition variables.cc:583
F77_RET_T len
Definition xerbla.cc:61