GNU Octave 10.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-2025 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 recording a session in a file called @file{diary} in the current working
542directory.
543
544@item off
545Stop recording the session in the diary file.
546
547@item @var{filename}
548Record the session in the file named @var{filename}.
549@end table
550
551With no input or output arguments, @code{diary} toggles the current diary
552state.
553
554If output arguments are requested, @code{diary} ignores inputs and returns
555the current status. The boolean @var{status} indicates whether recording is on
556or off, and @var{diaryfile} is the name of the file where the session is
557stored.
558@seealso{history, evalc}
559@end deftypefn */)
560{
561 int nargin = args.length ();
562
563 if (nargin > 1)
564 print_usage ();
565
566 output_system& output_sys = interp.get_output_system ();
567
568 if (nargout > 0)
569 {
570 // Querying diary variables
571 if (nargout == 1)
572 return ovl (output_sys.write_to_diary_file ());
573 else
574 return ovl (output_sys.write_to_diary_file (),
575 output_sys.diary_file_name ());
576 }
577
578 if (nargin == 0)
579 {
580 output_sys.write_to_diary_file (! output_sys.write_to_diary_file ());
581 output_sys.open_diary ();
582 }
583 else
584 {
585 std::string arg = args(0).xstring_value ("diary: argument must be a string");
586
587 if (arg == "on")
588 {
589 output_sys.write_to_diary_file (true);
590 output_sys.open_diary ();
591 }
592 else if (arg == "off")
593 {
594 output_sys.close_diary ();
595 output_sys.write_to_diary_file (false);
596 }
597 else
598 {
599 output_sys.diary_file_name (arg);
600 output_sys.write_to_diary_file (true);
601 output_sys.open_diary ();
602 }
603 }
604
605 return ovl ();
606}
607
608DEFMETHOD (more, interp, args, ,
609 doc: /* -*- texinfo -*-
610@deftypefn {} {} more
611@deftypefnx {} {} more on
612@deftypefnx {} {} more off
613Turn output pagination on or off.
614
615Without an argument, @code{more} toggles the current state.
616
617The current state can be determined via @code{page_screen_output}.
618@seealso{page_screen_output, page_output_immediately, PAGER, PAGER_FLAGS}
619@end deftypefn */)
620{
621 int nargin = args.length ();
622
623 if (nargin > 1)
624 print_usage ();
625
626 output_system& output_sys = interp.get_output_system ();
627
628 if (nargin > 0)
629 {
630 std::string arg = args(0).xstring_value (R"(more: argument must be string "on" or "off")");
631
632 if (arg == "on")
633 output_sys.page_screen_output (true);
634 else if (arg == "off")
635 output_sys.page_screen_output (false);
636 else
637 error (R"(more: argument must be "on" or "off")");
638 }
639 else
640 output_sys.page_screen_output (! output_sys.page_screen_output ());
641
642 return ovl ();
643}
644
645DEFUN (terminal_size, args, ,
646 doc: /* -*- texinfo -*-
647@deftypefn {} {[@var{rows}, @var{cols}] =} terminal_size ()
648@deftypefnx {} {} terminal_size ([@var{rows}, @var{cols}])
649Query or set the size of the terminal window. If called with no arguments,
650return a two-element row vector containing the current size of the terminal
651window in characters (rows and columns). If called with a two-element vector
652of integer values, set the terminal size and return the previous setting.
653Setting the size manually should not be needed when using readline for
654command-line editing.
655@seealso{list_in_columns}
656@end deftypefn */)
657{
658 int nargin = args.length ();
659
660 if (nargin > 1)
661 print_usage ();
662
663 RowVector size (2, 0.0);
664
667
668 if (nargin == 1)
669 {
670 Matrix m = args(0).xmatrix_value ("argument must be a 2-element array");
671
672 if (m.numel () != 2)
673 error ("terminal_size: argument must be a 2-element array");
674
675 int rows = math::x_nint (m(0));
676 int cols = math::x_nint (m(1));
677
678 if (rows <= 0 || cols <= 0)
679 error ("terminal_size: rows and columns must be positive integers");
680
682 }
683
684 return ovl (size);
685}
686
687DEFMETHOD (page_output_immediately, interp, args, nargout,
688 doc: /* -*- texinfo -*-
689@deftypefn {} {@var{val} =} page_output_immediately ()
690@deftypefnx {} {@var{old_val} =} page_output_immediately (@var{new_val})
691@deftypefnx {} {@var{old_val} =} page_output_immediately (@var{new_val}, "local")
692Query or set the internal variable that controls whether Octave sends
693output to the pager as soon as it is available.
694
695When the value is @code{false}, Octave buffers its output and waits until just
696before the prompt is printed to flush it to the pager. This is the default.
697
698When @code{page_screen_output} is @code{false}, this variable has no effect.
699
700When called from inside a function with the @qcode{"local"} option, the
701variable is changed locally for the function and any subroutines it calls.
702The original variable value is restored when exiting the function.
703@seealso{page_screen_output, more, PAGER, PAGER_FLAGS}
704@end deftypefn */)
705{
706 output_system& output_sys = interp.get_output_system ();
707
708 return output_sys.page_output_immediately (args, nargout);
709}
710
711DEFMETHOD (page_screen_output, interp, args, nargout,
712 doc: /* -*- texinfo -*-
713@deftypefn {} {@var{val} =} page_screen_output ()
714@deftypefnx {} {@var{old_val} =} page_screen_output (@var{new_val})
715@deftypefnx {} {@var{old_val} =} page_screen_output (@var{new_val}, "local")
716Query or set the internal variable that controls whether output intended
717for the terminal window that is longer than one page is sent through a
718pager.
719
720This allows you to view one screenful at a time. Some pagers
721(such as @code{less}---@pxref{Installation}) are also capable of moving
722backward on the output.
723
724When called from inside a function with the @qcode{"local"} option, the
725variable is changed locally for the function and any subroutines it calls.
726The original variable value is restored when exiting the function.
727@seealso{more, page_output_immediately, PAGER, PAGER_FLAGS}
728@end deftypefn */)
729{
730 output_system& output_sys = interp.get_output_system ();
731
732 return output_sys.page_screen_output (args, nargout);
733}
734
735DEFMETHOD (PAGER, interp, args, nargout,
736 doc: /* -*- texinfo -*-
737@deftypefn {} {@var{val} =} PAGER ()
738@deftypefnx {} {@var{old_val} =} PAGER (@var{new_val})
739@deftypefnx {} {@var{old_val} =} PAGER (@var{new_val}, "local")
740Query or set the internal variable that specifies the program to use
741to display terminal output on your system.
742
743The default value is normally @qcode{"less"}, @qcode{"more"}, or
744@qcode{"pg"}, depending on what programs are installed on your system.
745@xref{Installation}.
746
747When called from inside a function with the @qcode{"local"} option, the
748variable is changed locally for the function and any subroutines it calls.
749The original variable value is restored when exiting the function.
750@seealso{PAGER_FLAGS, page_output_immediately, more, page_screen_output}
751@end deftypefn */)
752{
753 output_system& output_sys = interp.get_output_system ();
754
755 return output_sys.PAGER (args, nargout);
756}
757
758DEFMETHOD (PAGER_FLAGS, interp, args, nargout,
759 doc: /* -*- texinfo -*-
760@deftypefn {} {@var{val} =} PAGER_FLAGS ()
761@deftypefnx {} {@var{old_val} =} PAGER_FLAGS (@var{new_val})
762@deftypefnx {} {@var{old_val} =} PAGER_FLAGS (@var{new_val}, "local")
763Query or set the internal variable that specifies the options to pass
764to the pager.
765
766When called from inside a function with the @qcode{"local"} option, the
767variable is changed locally for the function and any subroutines it calls.
768The original variable value is restored when exiting the function.
769@seealso{PAGER, more, page_screen_output, page_output_immediately}
770@end deftypefn */)
771{
772 output_system& output_sys = interp.get_output_system ();
773
774 return output_sys.PAGER_FLAGS (args, nargout);
775}
776
777OCTAVE_END_NAMESPACE(octave)
octave_idx_type numel() const
Number of elements in the array.
Definition Array.h:418
static bool forced_interactive()
Definition octave.cc:335
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:159
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:1003
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