comparison libinterp/interpfcn/pager.cc @ 15195:2fc554ffbc28

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