2 * Copyright (C) 1980 The Regents of the University of California.
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18 /* more.c - General purpose tty output filter and file perusal program
20 * by Eric Shienbrood, UC Berkeley
22 * modified by Geoff Peck
23 * UCB to add underlining, single spacing
24 * modified by John Foderaro
25 * UCB to add -c and MORE environment variable
26 * modified by Erik Troan <ewt@redhat.com>
27 * to be more posix and so compile on linux/axp.
28 * modified by Kars de Jong <jongk@cs.utwente.nl>
29 * to use terminfo instead of termcap.
30 * 1999-02-22 Arkadiusz MiĆkiewicz <misiek@pld.ORG.PL>
31 * added Native Language Support
32 * 1999-03-19 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
33 * more nls translatable strings
35 * applied a RedHat patch (setjmp->sigsetjmp); without it a second
38 * undone Kars' work, so that more works without libcurses (and
39 * hence can be in /bin with libcurses being in
40 * /usr/lib which may not be mounted). However, when termcap is not
41 * present curses can still be used.
42 * 2010-10-21 Davidlohr Bueso <dave@gnu.org>
43 * modified mem allocation handling for util-linux
51 #include <sys/param.h>
57 #include <sys/ioctl.h>
60 #include <sys/ttydefaults.h>
65 #include <sys/signalfd.h>
69 #if defined(HAVE_NCURSESW_TERM_H)
70 # include <ncursesw/term.h>
71 #elif defined(HAVE_NCURSES_TERM_H)
72 # include <ncurses/term.h>
73 #elif defined(HAVE_TERM_H)
82 #include "closestream.h"
86 # define NON_INTERACTIVE_MORE 1
89 #define BACKSPACE "\b"
92 #define MIN_LINE_SZ 256 /* minimal line_buf buffer size */
95 #define LINES_PER_PAGE 24
96 #define NUM_COLUMNS 80
97 #define TERMINAL_BUF 4096
99 #define COMMAND_BUF 200
100 #define REGERR_BUF NUM_COLUMNS
102 #define TERM_AUTO_RIGHT_MARGIN "am"
103 #define TERM_BACKSPACE "cub1"
104 #define TERM_CEOL "xhp"
105 #define TERM_CLEAR "clear"
106 #define TERM_CLEAR_TO_LINE_END "el"
107 #define TERM_CLEAR_TO_SCREEN_END "ed"
108 #define TERM_COLS "cols"
109 #define TERM_CURSOR_ADDRESS "cup"
110 #define TERM_EAT_NEW_LINE "xenl"
111 #define TERM_EXIT_STANDARD_MODE "rmso"
112 #define TERM_HARD_COPY "hc"
113 #define TERM_HOME "home"
114 #define TERM_LINE_DOWN "cud1"
115 #define TERM_LINES "lines"
116 #define TERM_OVER_STRIKE "os"
117 #define TERM_STANDARD_MODE "smso"
118 #define TERM_STD_MODE_GLITCH "xmc"
120 struct more_control
{
121 struct termios output_tty
; /* output terminal */
122 struct termios original_tty
; /* original terminal settings */
123 FILE *current_file
; /* currently open input file */
124 off_t file_position
; /* file position */
125 off_t file_size
; /* file size */
126 int argv_position
; /* argv[] position */
127 int lines_per_screen
; /* screen size in lines */
128 int d_scroll_len
; /* number of lines scrolled by 'd' */
129 int prompt_len
; /* message prompt length */
130 int current_line
; /* line we are currently at */
131 int next_jump
; /* number of lines to skip ahead */
132 char **file_names
; /* The list of file names */
133 int num_files
; /* Number of files left to process */
134 char *shell
; /* name of the shell to use */
135 int sigfd
; /* signalfd() file descriptor */
136 sigset_t sigset
; /* signal operations */
137 char *line_buf
; /* line buffer */
138 size_t line_sz
; /* size of line_buf buffer */
139 int lines_per_page
; /* lines per page */
140 char *clear
; /* clear screen */
141 char *erase_line
; /* erase line */
142 char *enter_std
; /* enter standout mode */
143 char *exit_std
; /* exit standout mode */
144 char *backspace_ch
; /* backspace character */
145 char *go_home
; /* go to home */
146 char *move_line_down
; /* move line down */
147 char *clear_rest
; /* clear rest of screen */
148 int num_columns
; /* number of columns */
149 char *next_search
; /* file beginning search string */
150 char *previous_search
; /* previous search() buf[] item */
152 off_t row_num
; /* row file position */
153 long line_num
; /* line number */
156 cc_t last_key_command
; /* previous more key command */
157 int last_key_arg
; /* previous key command argument */
158 int last_colon_command
; /* is a colon-prefixed key command */
159 char *shell_line
; /* line to execute in subshell */
161 bad_stdout
:1, /* true if overwriting does not turn off standout */
162 catch_suspend
:1, /* we should catch the SIGTSTP signal */
163 clear_line_ends
:1, /* do not scroll, paint each screen from the top */
164 clear_first
:1, /* is first character in file \f */
165 dumb_tty
:1, /* is terminal type known */
166 eat_newline
:1, /* is newline ignored after 80 cols */
167 erase_input_ok
:1, /* is erase input supported */
168 erase_previous_ok
:1, /* is erase previous supported */
169 first_file
:1, /* is the input file the first in list */
170 fold_long_lines
:1, /* fold long lines */
171 hard_tabs
:1, /* print spaces instead of '\t' */
172 hard_tty
:1, /* is this hard copy terminal (a printer or such) */
173 is_paused
:1, /* is output paused */
174 no_quit_dialog
:1, /* suppress quit dialog */
175 no_scroll
:1, /* do not scroll, clear the screen and then display text */
176 no_tty_in
:1, /* is input in interactive mode */
177 no_tty_out
:1, /* is output in interactive mode */
178 print_banner
:1, /* print file name banner */
179 report_errors
:1, /* is an error reported */
180 run_previous_command
:1, /* run previous key command */
181 search_at_start
:1, /* search pattern defined at start up */
182 search_called
:1, /* previous more command was a search */
183 squeeze_spaces
:1, /* suppress white space */
184 stdout_glitch
:1, /* terminal has standout mode glitch */
185 stop_after_formfeed
:1, /* stop after form feeds */
186 suppress_bell
:1, /* suppress bell */
187 wrap_margin
:1; /* set if automargins */
190 static void __attribute__((__noreturn__
)) usage(void)
192 printf("%s", USAGE_HEADER
);
193 printf(_(" %s [options] <file>...\n"), program_invocation_short_name
);
195 printf("%s", USAGE_SEPARATOR
);
196 printf("%s\n", _("A file perusal filter for CRT viewing."));
198 printf("%s", USAGE_OPTIONS
);
199 printf("%s\n", _(" -d, --silent display help instead of ringing bell"));
200 printf("%s\n", _(" -f, --logical count logical rather than screen lines"));
201 printf("%s\n", _(" -l, --no-pause suppress pause after form feed"));
202 printf("%s\n", _(" -c, --print-over do not scroll, display text and clean line ends"));
203 printf("%s\n", _(" -p, --clean-print do not scroll, clean screen and display text"));
204 printf("%s\n", _(" -s, --squeeze squeeze multiple blank lines into one"));
205 printf("%s\n", _(" -u, --plain suppress underlining and bold"));
206 printf("%s\n", _(" -n, --lines <number> the number of lines per screenful"));
207 printf("%s\n", _(" -<number> same as --lines"));
208 printf("%s\n", _(" +<number> display file beginning from line number"));
209 printf("%s\n", _(" +/<pattern> display file beginning from pattern match"));
210 printf("%s", USAGE_SEPARATOR
);
211 printf(USAGE_HELP_OPTIONS(23));
212 printf(USAGE_MAN_TAIL("more(1)"));
216 static void argscan(struct more_control
*ctl
, int as_argc
, char **as_argv
)
219 static const struct option longopts
[] = {
220 { "silent", no_argument
, NULL
, 'd' },
221 { "logical", no_argument
, NULL
, 'f' },
222 { "no-pause", no_argument
, NULL
, 'l' },
223 { "print-over", no_argument
, NULL
, 'c' },
224 { "clean-print", no_argument
, NULL
, 'p' },
225 { "squeeze", no_argument
, NULL
, 's' },
226 { "plain", no_argument
, NULL
, 'u' },
227 { "lines", required_argument
, NULL
, 'n' },
228 { "version", no_argument
, NULL
, 'V' },
229 { "help", no_argument
, NULL
, 'h' },
233 /* Take care of number option and +args. */
234 for (opt
= 0; opt
< as_argc
; opt
++) {
237 if (as_argv
[opt
][0] == '-' && isdigit_string(as_argv
[opt
] + 1)) {
238 ctl
->lines_per_screen
=
239 strtos16_or_err(as_argv
[opt
], _("failed to parse number"));
240 ctl
->lines_per_screen
= abs(ctl
->lines_per_screen
);
242 } else if (as_argv
[opt
][0] == '+') {
243 if (isdigit_string(as_argv
[opt
] + 1)) {
244 ctl
->next_jump
= strtos32_or_err(as_argv
[opt
],
245 _("failed to parse number")) - 1;
247 } else if (as_argv
[opt
][1] == '/') {
248 free(ctl
->next_search
);
249 ctl
->next_search
= xstrdup(as_argv
[opt
] + 2);
250 ctl
->search_at_start
= 1;
255 as_argc
= remote_entry(as_argv
, opt
, as_argc
);
260 while ((c
= getopt_long(as_argc
, as_argv
, "dflcpsun:eVh", longopts
, NULL
)) != -1) {
263 ctl
->suppress_bell
= 1;
266 ctl
->stop_after_formfeed
= 0;
269 ctl
->fold_long_lines
= 0;
275 ctl
->clear_line_ends
= 1;
278 ctl
->squeeze_spaces
= 1;
283 ctl
->lines_per_screen
= strtou16_or_err(optarg
, _("argument error"));
285 case 'e': /* ignored silently to be posix compliant */
288 print_version(EXIT_SUCCESS
);
292 errtryhelp(EXIT_FAILURE
);
296 ctl
->num_files
= as_argc
- optind
;
297 ctl
->file_names
= as_argv
+ optind
;
300 static void env_argscan(struct more_control
*ctl
, const char *s
)
305 const char delim
[] = { ' ', '\n', '\t', '\0' };
306 char *str
= xstrdup(s
);
307 char *key
= NULL
, *tok
;
309 env_argv
= xmalloc(sizeof(char *) * size
);
310 env_argv
[0] = _("MORE environment variable"); /* program name */
311 for (tok
= strtok_r(str
, delim
, &key
); tok
; tok
= strtok_r(NULL
, delim
, &key
)) {
312 env_argv
[env_argc
++] = tok
;
313 if (size
< env_argc
) {
315 env_argv
= xrealloc(env_argv
, sizeof(char *) * size
);
319 argscan(ctl
, env_argc
, env_argv
);
320 /* Reset optind, command line parsing needs this. */
326 static void more_fseek(struct more_control
*ctl
, off_t pos
)
328 ctl
->file_position
= pos
;
329 fseeko(ctl
->current_file
, pos
, SEEK_SET
);
332 static int more_getc(struct more_control
*ctl
)
334 int ret
= getc(ctl
->current_file
);
335 ctl
->file_position
= ftello(ctl
->current_file
);
339 static int more_ungetc(struct more_control
*ctl
, int c
)
341 int ret
= ungetc(c
, ctl
->current_file
);
342 ctl
->file_position
= ftello(ctl
->current_file
);
346 static void print_separator(const int c
, int n
)
354 * check for file magic numbers. This code would best be shared
355 * with the file(1) program or, perhaps, more should not try to be
357 static int check_magic(FILE *f
, char *fs
)
359 signed char twobytes
[2];
361 /* don't try to look ahead if the input is unseekable */
362 if (fseek(f
, 0L, SEEK_SET
))
365 if (fread(twobytes
, 2, 1, f
) == 1) {
366 switch (twobytes
[0] + (twobytes
[1] << 8)) {
367 case 0407: /* a.out obj */
368 case 0410: /* a.out exec */
369 case 0413: /* a.out demand exec */
373 case 0x457f: /* simple ELF detection */
374 printf(_("\n******** %s: Not a text file ********\n\n"),
379 fseek(f
, 0L, SEEK_SET
); /* rewind() not necessary */
383 /* Check whether the file named by fs is an ASCII file which the user may
384 * access. If it is, return the opened file. Otherwise return NULL. */
385 static void checkf(struct more_control
*ctl
, char *fs
)
390 ctl
->current_line
= 0;
391 ctl
->file_position
= 0;
393 if (((ctl
->current_file
= fopen(fs
, "r")) == NULL
) ||
394 (fstat(fileno(ctl
->current_file
), &st
) != 0)) {
395 if (ctl
->clear_line_ends
)
396 putp(ctl
->erase_line
);
397 warn(_("stat of %s failed"), fs
);
398 ctl
->current_file
= NULL
;
401 if ((st
.st_mode
& S_IFMT
) == S_IFDIR
) {
402 printf(_("\n*** %s: directory ***\n\n"), fs
);
403 ctl
->current_file
= NULL
;
406 ctl
->current_line
= 0;
407 ctl
->file_position
= 0;
408 if ((ctl
->current_file
= fopen(fs
, "r")) == NULL
) {
410 warn(_("cannot open %s"), fs
);
413 if (check_magic(ctl
->current_file
, fs
)) {
414 fclose(ctl
->current_file
);
415 ctl
->current_file
= NULL
;
418 fcntl(fileno(ctl
->current_file
), F_SETFD
, FD_CLOEXEC
);
420 ctl
->clear_first
= (c
== '\f');
422 if ((ctl
->file_size
= st
.st_size
) == 0)
423 ctl
->file_size
= ~((off_t
)0);
426 static void prepare_line_buffer(struct more_control
*ctl
)
428 size_t sz
= ctl
->num_columns
* 4;
430 if (ctl
->line_sz
>= sz
)
433 if (sz
< MIN_LINE_SZ
)
436 /* alloc sz and extra space for \n\0 */
437 ctl
->line_buf
= xrealloc(ctl
->line_buf
, sz
+ 2);
441 /* Get a logical line */
442 static int get_line(struct more_control
*ctl
, int *length
)
447 static int column_wrap
;
453 mbstate_t state
, state_bak
; /* Current status of the stream. */
454 char mbc
[MB_LEN_MAX
]; /* Buffer for one multibyte char. */
455 size_t mblength
; /* Byte length of multibyte char. */
456 size_t mbc_pos
= 0; /* Position of the MBC. */
457 int use_mbc_buffer_flag
= 0; /* If 1, mbc has data. */
458 int break_flag
= 0; /* If 1, exit while(). */
459 off_t file_position_bak
= ctl
->file_position
;
461 memset(&state
, '\0', sizeof(mbstate_t));
467 if (column_wrap
&& c
== '\n') {
471 while (p
< &ctl
->line_buf
[ctl
->line_sz
- 1]) {
473 if (ctl
->fold_long_lines
&& use_mbc_buffer_flag
&& MB_CUR_MAX
> 1) {
474 use_mbc_buffer_flag
= 0;
478 mblength
= mbrtowc(&wc
, mbc
, mbc_pos
, &state
);
481 case (size_t)-2: /* Incomplete multibyte character. */
482 use_mbc_buffer_flag
= 1;
486 case (size_t)-1: /* Invalid as a multibyte character. */
491 if (column
>= ctl
->num_columns
) {
492 more_fseek(ctl
, file_position_bak
);
494 memmove(mbc
, mbc
+ 1, --mbc_pos
);
503 wc_width
= wcwidth(wc
);
504 if (column
+ wc_width
> ctl
->num_columns
) {
505 more_fseek(ctl
, file_position_bak
);
508 for (i
= 0; p
< &ctl
->line_buf
[ctl
->line_sz
- 1] &&
516 if (break_flag
|| column
>= ctl
->num_columns
)
522 #endif /* HAVE_WIDECHAR */
524 if (p
> ctl
->line_buf
) {
526 *length
= p
- ctl
->line_buf
;
529 *length
= p
- ctl
->line_buf
;
539 if (!ctl
->hard_tabs
|| (column
< ctl
->prompt_len
&& !ctl
->hard_tty
)) {
540 if (ctl
->hard_tabs
&& ctl
->erase_line
&& !ctl
->dumb_tty
) {
541 column
= 1 + (column
| 7);
542 putp(ctl
->erase_line
);
545 for (--p
; p
< &ctl
->line_buf
[ctl
->line_sz
- 1];) {
547 if ((++column
& 7) == 0)
550 if (column
>= ctl
->prompt_len
)
554 column
= 1 + (column
| 7);
555 } else if (c
== '\b' && column
> 0) {
557 } else if (c
== '\r') {
558 int next
= more_getc(ctl
);
566 } else if (c
== '\f' && ctl
->stop_after_formfeed
) {
571 } else if (c
== EOF
) {
572 *length
= p
- ctl
->line_buf
;
576 if (ctl
->fold_long_lines
&& MB_CUR_MAX
> 1) {
577 memset(mbc
, '\0', MB_LEN_MAX
);
582 mblength
= mbrtowc(&wc
, mbc
, mbc_pos
, &state
);
583 /* The value of mblength is always less than 2 here. */
587 file_position_bak
= ctl
->file_position
- 1;
589 use_mbc_buffer_flag
= 1;
598 wc_width
= wcwidth(wc
);
603 #endif /* HAVE_WIDECHAR */
610 if (column
>= ctl
->num_columns
&& ctl
->fold_long_lines
)
613 if (use_mbc_buffer_flag
== 0 && p
>= &ctl
->line_buf
[ctl
->line_sz
- 1 - 4])
614 /* don't read another char if there is no space for
615 * whole multibyte sequence */
620 if (column
>= ctl
->num_columns
&& ctl
->num_columns
> 0) {
621 if (!ctl
->wrap_margin
) {
625 column_wrap
= column
== ctl
->num_columns
&& ctl
->fold_long_lines
;
626 if (column_wrap
&& ctl
->eat_newline
&& ctl
->wrap_margin
) {
627 *p
++ = '\n'; /* simulate normal wrap */
629 *length
= p
- ctl
->line_buf
;
634 /* Erase the rest of the prompt, assuming we are starting at column col. */
635 static void erase_to_col(struct more_control
*ctl
, int col
)
638 if (ctl
->prompt_len
== 0)
640 if (col
== 0 && ctl
->clear_line_ends
)
641 puts(ctl
->erase_line
);
642 else if (ctl
->hard_tty
)
647 if (!ctl
->dumb_tty
&& ctl
->erase_line
)
648 putp(ctl
->erase_line
);
650 printf("%*s", ctl
->prompt_len
- col
, "");
655 ctl
->prompt_len
= col
;
658 static void output_prompt(struct more_control
*ctl
, char *filename
)
660 if (ctl
->clear_line_ends
)
661 putp(ctl
->erase_line
);
662 else if (ctl
->prompt_len
> 0)
663 erase_to_col(ctl
, 0);
664 if (!ctl
->hard_tty
) {
666 if (ctl
->enter_std
) {
667 putp(ctl
->enter_std
);
668 ctl
->prompt_len
+= (2 * ctl
->stdout_glitch
);
670 if (ctl
->clear_line_ends
)
671 putp(ctl
->erase_line
);
672 ctl
->prompt_len
+= printf(_("--More--"));
673 if (filename
!= NULL
) {
674 ctl
->prompt_len
+= printf(_("(Next file: %s)"), filename
);
675 } else if (!ctl
->no_tty_in
) {
678 (int)((ctl
->file_position
* 100) / ctl
->file_size
));
680 if (ctl
->suppress_bell
) {
682 printf(_("[Press space to continue, 'q' to quit.]"));
686 if (ctl
->clear_line_ends
)
687 putp(ctl
->clear_rest
);
689 fprintf(stderr
, "\a");
693 static void reset_tty(struct more_control
*ctl
)
698 ctl
->output_tty
.c_lflag
|= ICANON
| ECHO
;
699 ctl
->output_tty
.c_cc
[VMIN
] = ctl
->original_tty
.c_cc
[VMIN
];
700 ctl
->output_tty
.c_cc
[VTIME
] = ctl
->original_tty
.c_cc
[VTIME
];
701 tcsetattr(STDERR_FILENO
, TCSANOW
, &ctl
->original_tty
);
704 /* Clean up terminal state and exit. Also come here if interrupt signal received */
705 static void __attribute__((__noreturn__
)) more_exit(struct more_control
*ctl
)
708 if (ctl
->clear_line_ends
) {
710 putp(ctl
->erase_line
);
711 } else if (!ctl
->clear_line_ends
&& (ctl
->prompt_len
> 0))
712 erase_to_col(ctl
, 0);
714 free(ctl
->previous_search
);
715 free(ctl
->shell_line
);
718 if (ctl
->current_file
)
719 fclose(ctl
->current_file
);
720 del_curterm(cur_term
);
724 static cc_t
read_user_input(struct more_control
*ctl
)
729 if (read(STDERR_FILENO
, &c
, 1) <= 0) {
733 c
= ctl
->output_tty
.c_cc
[VKILL
];
738 /* Read a decimal number from the terminal. Set cmd to the non-digit
739 * which terminates the number. */
740 static int read_number(struct more_control
*ctl
, cc_t
*cmd
)
746 ch
= ctl
->output_tty
.c_cc
[VKILL
];
748 ch
= read_user_input(ctl
);
750 i
= i
* 10 + ch
- '0';
751 else if (ch
== ctl
->output_tty
.c_cc
[VKILL
])
761 /* Change displayed file from command line list to next nskip, where nskip
762 * is relative position in argv and can be negative, that is a previous
764 static void change_file(struct more_control
*ctl
, int nskip
)
769 if (ctl
->argv_position
+ nskip
> ctl
->num_files
- 1)
770 nskip
= ctl
->num_files
- ctl
->argv_position
- 1;
772 ctl
->argv_position
+= nskip
;
773 if (ctl
->argv_position
< 0)
774 ctl
->argv_position
= 0;
775 puts(_("\n...Skipping "));
776 if (ctl
->clear_line_ends
)
777 putp(ctl
->erase_line
);
779 fputs(_("...Skipping to file "), stdout
);
781 fputs(_("...Skipping back to file "), stdout
);
782 puts(ctl
->file_names
[ctl
->argv_position
]);
783 if (ctl
->clear_line_ends
)
784 putp(ctl
->erase_line
);
786 ctl
->argv_position
--;
789 static void show(struct more_control
*ctl
, char c
)
791 if ((c
< ' ' && c
!= '\n' && c
!= ESC
) || c
== CERASE
) {
792 c
+= (c
== CERASE
) ? -0100 : 0100;
793 fputs(CARAT
, stderr
);
800 static void more_error(struct more_control
*ctl
, char *mess
)
802 if (ctl
->clear_line_ends
)
803 putp(ctl
->erase_line
);
805 erase_to_col(ctl
, 0);
806 ctl
->prompt_len
+= strlen(mess
);
808 putp(ctl
->enter_std
);
813 ctl
->report_errors
++;
816 static void erase_one_column(struct more_control
*ctl
)
818 if (ctl
->erase_previous_ok
)
819 fprintf(stderr
, "%s ", ctl
->backspace_ch
);
820 fputs(ctl
->backspace_ch
, stderr
);
823 static void ttyin(struct more_control
*ctl
, char buf
[], int nmax
, char pchar
)
832 while (sp
- buf
< nmax
) {
833 if (ctl
->prompt_len
> maxlen
)
834 maxlen
= ctl
->prompt_len
;
835 c
= read_user_input(ctl
);
838 } else if (c
== ctl
->output_tty
.c_cc
[VERASE
] && !slash
) {
841 if (MB_CUR_MAX
> 1) {
843 size_t pos
= 0, mblength
;
844 mbstate_t state
, state_bak
;
846 memset(&state
, '\0', sizeof(mbstate_t));
851 mbrtowc(&wc
, buf
+ pos
,
862 if (buf
+ pos
+ mblength
>= sp
)
869 erase_one_column(ctl
);
872 wc_width
= wcwidth(wc
);
877 erase_one_column(ctl
);
886 #endif /* HAVE_WIDECHAR */
889 erase_one_column(ctl
);
893 if ((*sp
< ' ' && *sp
!= '\n') || *sp
== CERASE
) {
895 erase_one_column(ctl
);
899 if (!ctl
->erase_line
)
900 ctl
->prompt_len
= maxlen
;
902 } else if (c
== ctl
->output_tty
.c_cc
[VKILL
] && !slash
) {
911 erase_to_col(ctl
, 1);
912 else if (ctl
->erase_input_ok
)
913 while (ctl
->prompt_len
-- > 1)
914 fprintf(stderr
, "%s %s", ctl
->backspace_ch
, ctl
->backspace_ch
);
921 if (slash
&& (c
== ctl
->output_tty
.c_cc
[VKILL
] ||
922 c
== ctl
->output_tty
.c_cc
[VERASE
])) {
923 erase_one_column(ctl
);
929 if ((c
< ' ' && c
!= '\n' && c
!= ESC
) || c
== CERASE
) {
930 c
+= (c
== CERASE
) ? -0100 : 0100;
931 fputs(CARAT
, stderr
);
934 if (c
!= '\n' && c
!= ESC
) {
941 if (!ctl
->erase_line
)
942 ctl
->prompt_len
= maxlen
;
943 if (sp
- buf
>= nmax
- 1)
944 more_error(ctl
, _("Line too long"));
947 /* Expand shell command line. */
948 static void expand(struct more_control
*ctl
, char *inbuf
)
954 int tempsz
, xtra
, offset
;
956 xtra
= strlen(ctl
->file_names
[ctl
->argv_position
]) + strlen(ctl
->shell_line
) + 1;
957 tempsz
= COMMAND_BUF
+ xtra
;
958 temp
= xmalloc(tempsz
);
961 while ((c
= *inpstr
++) != '\0') {
962 offset
= outstr
- temp
;
963 if (tempsz
- offset
- 1 < xtra
) {
964 tempsz
+= COMMAND_BUF
+ xtra
;
965 temp
= xrealloc(temp
, tempsz
);
966 outstr
= temp
+ offset
;
970 if (!ctl
->no_tty_in
) {
971 strcpy(outstr
, ctl
->file_names
[ctl
->argv_position
]);
972 outstr
+= strlen(ctl
->file_names
[ctl
->argv_position
]);
977 if (ctl
->shell_line
) {
978 strcpy(outstr
, ctl
->shell_line
);
979 outstr
+= strlen(ctl
->shell_line
);
982 ("No previous command to substitute for"));
985 if (*inpstr
== '%' || *inpstr
== '!') {
986 *outstr
++ = *inpstr
++;
995 free(ctl
->shell_line
);
996 ctl
->shell_line
= temp
;
999 static void set_tty(struct more_control
*ctl
)
1001 ctl
->output_tty
.c_lflag
&= ~(ICANON
| ECHO
);
1002 ctl
->output_tty
.c_cc
[VMIN
] = 1; /* read at least 1 char */
1003 ctl
->output_tty
.c_cc
[VTIME
] = 0; /* no timeout */
1004 tcsetattr(STDERR_FILENO
, TCSANOW
, &ctl
->output_tty
);
1007 /* Come here if a quit signal is received */
1008 static void sigquit_handler(struct more_control
*ctl
)
1010 if (!ctl
->dumb_tty
&& ctl
->no_quit_dialog
) {
1011 ctl
->prompt_len
+= fprintf(stderr
, _("[Use q or Q to quit]"));
1012 ctl
->no_quit_dialog
= 0;
1017 /* Come here when we get a suspend signal from the terminal */
1018 static void sigtstp_handler(struct more_control
*ctl
)
1022 kill(getpid(), SIGSTOP
);
1025 /* Come here when we get a continue signal from the terminal */
1026 static void sigcont_handler(struct more_control
*ctl
)
1031 /* Come here if a signal for a window size change is received */
1032 static void sigwinch_handler(struct more_control
*ctl
)
1036 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) != -1) {
1037 if (win
.ws_row
!= 0) {
1038 ctl
->lines_per_page
= win
.ws_row
;
1039 ctl
->d_scroll_len
= ctl
->lines_per_page
/ 2 - 1;
1040 if (ctl
->d_scroll_len
< 1)
1041 ctl
->d_scroll_len
= 1;
1042 ctl
->lines_per_screen
= ctl
->lines_per_page
- 1;
1044 if (win
.ws_col
!= 0)
1045 ctl
->num_columns
= win
.ws_col
;
1047 prepare_line_buffer(ctl
);
1050 static void execute(struct more_control
*ctl
, char *filename
, char *cmd
, ...)
1062 if (!isatty(STDIN_FILENO
)) {
1063 close(STDIN_FILENO
);
1064 open("/dev/tty", 0);
1068 va_start(argp
, cmd
);
1069 arg
= va_arg(argp
, char *);
1073 arg
= va_arg(argp
, char *);
1077 args
= alloca(sizeof(char *) * (argcount
+ 1));
1078 args
[argcount
] = NULL
;
1080 va_start(argp
, cmd
);
1081 arg
= va_arg(argp
, char *);
1084 args
[argcount
] = arg
;
1086 arg
= va_arg(argp
, char *);
1090 if (geteuid() != getuid() || getegid() != getgid()) {
1091 if (setuid(getuid()) < 0)
1092 err(EXIT_FAILURE
, _("setuid failed"));
1093 if (setgid(getgid()) < 0)
1094 err(EXIT_FAILURE
, _("setgid failed"));
1099 fputs(_("exec failed\n"), stderr
);
1100 exit(errsv
== ENOENT
? EX_EXEC_ENOENT
: EX_EXEC_FAILED
);
1104 while (wait(NULL
) > 0) {
1109 fputs(_("can't fork\n"), stderr
);
1111 print_separator('-', 24);
1112 output_prompt(ctl
, filename
);
1115 static void run_shell(struct more_control
*ctl
, char *filename
)
1117 char cmdbuf
[COMMAND_BUF
];
1119 erase_to_col(ctl
, 0);
1122 if (ctl
->run_previous_command
&& ctl
->shell_line
)
1123 fputs(ctl
->shell_line
, stdout
);
1125 ttyin(ctl
, cmdbuf
, sizeof(cmdbuf
) - 2, '!');
1126 if (strpbrk(cmdbuf
, "%!\\"))
1127 expand(ctl
, cmdbuf
);
1129 free(ctl
->shell_line
);
1130 ctl
->shell_line
= xstrdup(cmdbuf
);
1133 fputc('\n', stderr
);
1135 ctl
->prompt_len
= 0;
1136 execute(ctl
, filename
, ctl
->shell
, ctl
->shell
, "-c", ctl
->shell_line
, 0);
1139 /* Execute a colon-prefixed command. Returns <0 if not a command that
1140 * should cause more of the file to be printed. */
1141 static int colon_command(struct more_control
*ctl
, char *filename
, int cmd
, int nlines
)
1146 ch
= read_user_input(ctl
);
1149 ctl
->last_colon_command
= ch
;
1152 erase_to_col(ctl
, 0);
1153 if (!ctl
->no_tty_in
)
1155 printf(_("\"%s\" line %d"), ctl
->file_names
[ctl
->argv_position
], ctl
->current_line
);
1157 ctl
->prompt_len
= printf(_("[Not a file] line %d"), ctl
->current_line
);
1162 if (ctl
->argv_position
>= ctl
->num_files
- 1)
1167 erase_to_col(ctl
, 0);
1168 change_file(ctl
, nlines
);
1171 if (ctl
->no_tty_in
) {
1172 fprintf(stderr
, "\a");
1176 erase_to_col(ctl
, 0);
1179 change_file(ctl
, -nlines
);
1182 run_shell(ctl
, filename
);
1188 fprintf(stderr
, "\a");
1193 /* Skip n lines in the file f */
1194 static void skip_lines(struct more_control
*ctl
)
1198 while (ctl
->next_jump
> 0) {
1199 while ((c
= more_getc(ctl
)) != '\n')
1203 ctl
->current_line
++;
1207 /* Clear the screen */
1208 static void more_clear_screen(struct more_control
*ctl
)
1210 if (ctl
->clear
&& !ctl
->hard_tty
) {
1212 /* Put out carriage return so that system doesn't get
1213 * confused by escape sequences when expanding tabs */
1215 ctl
->prompt_len
= 0;
1219 static void read_line(struct more_control
*ctl
)
1225 while ((c
= more_getc(ctl
)) != '\n' && c
!= EOF
1226 && (ptrdiff_t)p
!= (ptrdiff_t)(ctl
->line_buf
+ ctl
->line_sz
- 1))
1229 ctl
->current_line
++;
1233 static int more_poll(struct more_control
*ctl
, int timeout
)
1235 struct pollfd pfd
[2];
1237 pfd
[0].fd
= ctl
->sigfd
;
1238 pfd
[0].events
= POLLIN
| POLLERR
| POLLHUP
;
1239 pfd
[1].fd
= STDIN_FILENO
;
1240 pfd
[1].events
= POLLIN
;
1242 if (poll(pfd
, 2, timeout
) < 0) {
1243 if (errno
== EAGAIN
)
1245 more_error(ctl
, _("poll failed"));
1248 if (pfd
[0].revents
!= 0) {
1249 struct signalfd_siginfo info
;
1252 sz
= read(pfd
[0].fd
, &info
, sizeof(info
));
1253 assert(sz
== sizeof(info
));
1254 switch (info
.ssi_signo
) {
1259 sigquit_handler(ctl
);
1262 sigtstp_handler(ctl
);
1265 sigcont_handler(ctl
);
1268 sigwinch_handler(ctl
);
1274 if (pfd
[1].revents
== 0)
1279 /* Search for nth occurrence of regular expression contained in buf in
1281 static void search(struct more_control
*ctl
, char buf
[], int n
)
1283 off_t startline
= ctl
->file_position
;
1284 off_t line1
= startline
;
1285 off_t line2
= startline
;
1291 if (buf
!= ctl
->previous_search
) {
1292 free(ctl
->previous_search
);
1293 ctl
->previous_search
= buf
;
1296 ctl
->search_called
= 1;
1297 ctl
->context
.line_num
= saveln
= ctl
->current_line
;
1298 ctl
->context
.row_num
= startline
;
1302 if ((rc
= regcomp(&re
, buf
, REG_NOSUB
)) != 0) {
1304 regerror(rc
, &re
, s
, sizeof s
);
1308 while (!feof(ctl
->current_file
)) {
1311 line1
= ctl
->file_position
;
1314 if (regexec(&re
, ctl
->line_buf
, 0, NULL
, 0) == 0 && --n
== 0) {
1315 if ((1 < lncount
&& ctl
->no_tty_in
) || 3 < lncount
) {
1317 if (ctl
->clear_line_ends
)
1318 putp(ctl
->erase_line
);
1319 fputs(_("...skipping\n"), stdout
);
1321 if (!ctl
->no_tty_in
) {
1322 ctl
->current_line
-= (lncount
< 3 ? lncount
: 3);
1323 more_fseek(ctl
, line3
);
1324 if (ctl
->no_scroll
) {
1325 if (ctl
->clear_line_ends
) {
1327 putp(ctl
->erase_line
);
1329 more_clear_screen(ctl
);
1332 erase_to_col(ctl
, 0);
1333 if (ctl
->no_scroll
) {
1334 if (ctl
->clear_line_ends
) {
1336 putp(ctl
->erase_line
);
1338 more_clear_screen(ctl
);
1340 puts(ctl
->line_buf
);
1346 /* Move ctrl+c signal handling back to more_key_command(). */
1347 signal(SIGINT
, SIG_DFL
);
1348 sigaddset(&ctl
->sigset
, SIGINT
);
1349 sigprocmask(SIG_BLOCK
, &ctl
->sigset
, NULL
);
1351 if (feof(ctl
->current_file
)) {
1352 if (!ctl
->no_tty_in
) {
1353 ctl
->current_line
= saveln
;
1354 more_fseek(ctl
, startline
);
1356 fputs(_("\nPattern not found\n"), stdout
);
1360 more_error(ctl
, _("Pattern not found"));
1364 static char *find_editor(void)
1366 static char *editor
;
1368 editor
= getenv("VISUAL");
1369 if (editor
== NULL
|| *editor
== '\0')
1370 editor
= getenv("EDITOR");
1371 if (editor
== NULL
|| *editor
== '\0')
1376 static void runtime_usage(void)
1378 fputs(_("Most commands optionally preceded by integer argument k. "
1379 "Defaults in brackets.\n"
1380 "Star (*) indicates argument becomes new default.\n"), stdout
);
1381 print_separator('-', 79);
1384 ("<space> Display next k lines of text [current screen size]\n"
1385 "z Display next k lines of text [current screen size]*\n"
1386 "<return> Display next k lines of text [1]*\n"
1387 "d or ctrl-D Scroll k lines [current scroll size, initially 11]*\n"
1388 "q or Q or <interrupt> Exit from more\n"
1389 "s Skip forward k lines of text [1]\n"
1390 "f Skip forward k screenfuls of text [1]\n"
1391 "b or ctrl-B Skip backwards k screenfuls of text [1]\n"
1392 "' Go to place where previous search started\n"
1393 "= Display current line number\n"
1394 "/<regular expression> Search for kth occurrence of regular expression [1]\n"
1395 "n Search for kth occurrence of last r.e [1]\n"
1396 "!<cmd> or :!<cmd> Execute <cmd> in a subshell\n"
1397 "v Start up '%s' at current line\n"
1398 "ctrl-L Redraw screen\n"
1399 ":n Go to kth next file [1]\n"
1400 ":p Go to kth previous file [1]\n"
1401 ":f Display current file name and line number\n"
1402 ". Repeat previous command\n"),
1404 print_separator('-', 79);
1407 static void execute_editor(struct more_control
*ctl
, char *cmdbuf
, char *filename
)
1413 if ((ctl
->current_line
- ctl
->lines_per_screen
) < 1)
1416 n
= ctl
->current_line
- (ctl
->lines_per_screen
+ 1) / 2;
1417 editor
= find_editor();
1418 p
= strrchr(editor
, '/');
1424 * Earlier: call vi +n file. This also works for emacs.
1425 * POSIX: call vi -c n file (when editor is vi or ex).
1427 if (!strcmp(p
, "vi") || !strcmp(p
, "ex")) {
1428 sprintf(cmdbuf
, "-c %d", n
);
1431 sprintf(cmdbuf
, "+%d", n
);
1433 erase_to_col(ctl
, 0);
1434 printf("%s %s %s", editor
, cmdbuf
, ctl
->file_names
[ctl
->argv_position
]);
1437 execute(ctl
, filename
, editor
, editor
,
1439 ctl
->file_names
[ctl
->argv_position
], (char *)0);
1441 execute(ctl
, filename
, editor
, editor
,
1442 cmdbuf
, ctl
->file_names
[ctl
->argv_position
], (char *)0);
1445 static int skip_backwards(struct more_control
*ctl
, int nlines
)
1449 erase_to_col(ctl
, 0);
1450 printf(P_("...back %d page", "...back %d pages", nlines
), nlines
);
1452 ctl
->next_jump
= ctl
->current_line
- (ctl
->lines_per_screen
* (nlines
+ 1)) - 1;
1453 if (ctl
->next_jump
< 0)
1456 ctl
->current_line
= 0;
1458 return ctl
->lines_per_screen
;
1461 static int skip_forwards(struct more_control
*ctl
, int nlines
, cc_t comchar
)
1468 nlines
*= ctl
->lines_per_screen
;
1470 erase_to_col(ctl
, 0);
1472 if (ctl
->clear_line_ends
)
1473 putp(ctl
->erase_line
);
1474 printf(P_("...skipping %d line",
1475 "...skipping %d lines", nlines
), nlines
);
1477 if (ctl
->clear_line_ends
)
1478 putp(ctl
->erase_line
);
1481 while (nlines
> 0) {
1482 while ((c
= more_getc(ctl
)) != '\n')
1485 ctl
->current_line
++;
1491 /* Read a command and do it. A command consists of an optional integer
1492 * argument followed by the command character. Return the number of
1493 * lines to display in the next screenful. If there is nothing more to
1494 * display in the current file, zero is returned. */
1495 static int more_key_command(struct more_control
*ctl
, char *filename
)
1502 char cmdbuf
[INIT_BUF
];
1503 if (!ctl
->report_errors
)
1504 output_prompt(ctl
, filename
);
1506 ctl
->report_errors
= 0;
1507 ctl
->search_called
= 0;
1509 if (more_poll(ctl
, -1) != 0)
1511 nlines
= read_number(ctl
, &comchar
);
1512 ctl
->run_previous_command
= colonch
= 0;
1513 if (comchar
== '.') { /* Repeat last command */
1514 ctl
->run_previous_command
++;
1515 comchar
= ctl
->last_key_command
;
1516 nlines
= ctl
->last_key_arg
;
1517 if (ctl
->last_key_command
== ':')
1518 colonch
= ctl
->last_colon_command
;
1520 ctl
->last_key_command
= comchar
;
1521 ctl
->last_key_arg
= nlines
;
1522 if (comchar
== ctl
->output_tty
.c_cc
[VERASE
]) {
1523 erase_to_col(ctl
, 0);
1524 output_prompt(ctl
, filename
);
1529 retval
= colon_command(ctl
, filename
, colonch
, nlines
);
1535 if (ctl
->no_tty_in
) {
1536 fprintf(stderr
, "\a");
1539 retval
= skip_backwards(ctl
, nlines
);
1545 nlines
= ctl
->lines_per_screen
;
1546 else if (comchar
== 'z')
1547 ctl
->lines_per_screen
= nlines
;
1554 ctl
->d_scroll_len
= nlines
;
1555 retval
= ctl
->d_scroll_len
;
1564 if (skip_forwards(ctl
, nlines
, comchar
))
1565 retval
= ctl
->lines_per_screen
;
1570 ctl
->lines_per_screen
= nlines
;
1577 if (!ctl
->no_tty_in
) {
1578 more_clear_screen(ctl
);
1579 more_fseek(ctl
, ctl
->screen_start
.row_num
);
1580 ctl
->current_line
= ctl
->screen_start
.line_num
;
1581 retval
= ctl
->lines_per_screen
;
1585 fprintf(stderr
, "\a");
1589 if (!ctl
->no_tty_in
) {
1590 erase_to_col(ctl
, 0);
1591 fputs(_("\n***Back***\n\n"), stdout
);
1592 more_fseek(ctl
, ctl
->context
.row_num
);
1593 ctl
->current_line
= ctl
->context
.line_num
;
1594 retval
= ctl
->lines_per_screen
;
1598 fprintf(stderr
, "\a");
1602 erase_to_col(ctl
, 0);
1603 ctl
->prompt_len
= printf("%d", ctl
->current_line
);
1607 if (!ctl
->previous_search
) {
1608 more_error(ctl
, _("No previous regular expression"));
1611 ctl
->run_previous_command
= 1;
1616 erase_to_col(ctl
, 0);
1618 ctl
->prompt_len
= 1;
1620 if (ctl
->run_previous_command
) {
1621 fputc('\r', stderr
);
1622 search(ctl
, ctl
->previous_search
, nlines
);
1624 ttyin(ctl
, cmdbuf
, sizeof(cmdbuf
) - 2, '/');
1625 fputc('\r', stderr
);
1626 ctl
->next_search
= xstrdup(cmdbuf
);
1627 search(ctl
, ctl
->next_search
, nlines
);
1629 retval
= ctl
->lines_per_screen
- 1;
1633 run_shell(ctl
, filename
);
1638 more_clear_screen(ctl
);
1639 erase_to_col(ctl
, 0);
1641 output_prompt(ctl
, filename
);
1643 case 'v': /* This case should go right before default */
1644 if (!ctl
->no_tty_in
) {
1645 execute_editor(ctl
, cmdbuf
, filename
);
1650 if (ctl
->suppress_bell
) {
1651 erase_to_col(ctl
, 0);
1653 putp(ctl
->enter_std
);
1655 printf(_("[Press 'h' for instructions.]"))
1656 + 2 * ctl
->stdout_glitch
;
1658 putp(ctl
->exit_std
);
1660 fprintf(stderr
, "\a");
1668 ctl
->no_quit_dialog
= 1;
1672 /* Print out the contents of the file f, one screenful at a time. */
1673 static void screen(struct more_control
*ctl
, int num_lines
)
1677 int length
; /* length of current line */
1678 static int prev_len
= 1; /* length of previous line */
1681 while (num_lines
> 0 && !ctl
->is_paused
) {
1682 if ((nchars
= get_line(ctl
, &length
)) == EOF
) {
1683 if (ctl
->clear_line_ends
)
1684 putp(ctl
->clear_rest
);
1687 if (ctl
->squeeze_spaces
&& length
== 0 && prev_len
== 0)
1691 || ((ctl
->enter_std
&& *ctl
->enter_std
== ' ') && (ctl
->prompt_len
> 0)))
1692 erase_to_col(ctl
, 0);
1693 /* must clear before drawing line since tabs on
1694 * some terminals do not erase what they tab
1696 if (ctl
->clear_line_ends
)
1697 putp(ctl
->erase_line
);
1698 fwrite(ctl
->line_buf
, length
, 1, stdout
);
1699 if (nchars
< ctl
->prompt_len
)
1700 erase_to_col(ctl
, nchars
);
1701 ctl
->prompt_len
= 0;
1702 if (nchars
< ctl
->num_columns
|| !ctl
->fold_long_lines
)
1707 if ((c
= more_getc(ctl
)) == EOF
) {
1708 if (ctl
->clear_line_ends
)
1709 putp(ctl
->clear_rest
);
1713 if (ctl
->is_paused
&& ctl
->clear_line_ends
)
1714 putp(ctl
->clear_rest
);
1715 more_ungetc(ctl
, c
);
1718 if ((num_lines
= more_key_command(ctl
, NULL
)) == 0)
1720 } while (ctl
->search_called
&& !ctl
->previous_search
);
1721 if (ctl
->hard_tty
&& ctl
->prompt_len
> 0)
1722 erase_to_col(ctl
, 0);
1723 if (ctl
->no_scroll
&& num_lines
>= ctl
->lines_per_screen
) {
1724 if (ctl
->clear_line_ends
)
1727 more_clear_screen(ctl
);
1729 ctl
->screen_start
.line_num
= ctl
->current_line
;
1730 ctl
->screen_start
.row_num
= ctl
->file_position
;
1734 static void copy_file(FILE *f
)
1739 while ((sz
= fread(&buf
, sizeof(char), sizeof(buf
), f
)) > 0)
1740 fwrite(&buf
, sizeof(char), sz
, stdout
);
1744 static void display_file(struct more_control
*ctl
, int left
)
1746 if (!ctl
->current_file
)
1748 ctl
->context
.line_num
= ctl
->context
.row_num
= 0;
1749 ctl
->current_line
= 0;
1750 if (ctl
->first_file
) {
1751 ctl
->first_file
= 0;
1754 if (ctl
->search_at_start
) {
1755 search(ctl
, ctl
->next_search
, 1);
1759 } else if (ctl
->argv_position
< ctl
->num_files
&& !ctl
->no_tty_out
)
1761 more_key_command(ctl
, ctl
->file_names
[ctl
->argv_position
]);
1763 if ((ctl
->no_scroll
|| ctl
->clear_first
)
1764 && ctl
->file_size
!= ~((off_t
)0)) {
1765 if (ctl
->clear_line_ends
)
1768 more_clear_screen(ctl
);
1770 if (ctl
->print_banner
) {
1771 if (ctl
->bad_stdout
)
1772 erase_to_col(ctl
, 0);
1773 if (ctl
->clear_line_ends
)
1774 putp(ctl
->erase_line
);
1775 if (ctl
->prompt_len
> 14)
1776 erase_to_col(ctl
, 14);
1777 if (ctl
->clear_line_ends
)
1778 putp(ctl
->erase_line
);
1779 print_separator(':', 14);
1780 puts(ctl
->file_names
[ctl
->argv_position
]);
1781 if (ctl
->clear_line_ends
)
1782 putp(ctl
->erase_line
);
1783 print_separator(':', 14);
1784 if (left
> ctl
->lines_per_page
- 4)
1785 left
= ctl
->lines_per_page
- 4;
1787 if (ctl
->no_tty_out
)
1788 copy_file(ctl
->current_file
);
1793 fclose(ctl
->current_file
);
1794 ctl
->current_file
= NULL
;
1795 ctl
->screen_start
.line_num
= ctl
->screen_start
.row_num
= 0;
1796 ctl
->context
.line_num
= ctl
->context
.row_num
= 0L;
1799 static void initterm(struct more_control
*ctl
)
1806 #ifndef NON_INTERACTIVE_MORE
1807 ctl
->no_tty_out
= tcgetattr(STDOUT_FILENO
, &ctl
->output_tty
);
1809 ctl
->no_tty_in
= tcgetattr(STDIN_FILENO
, &ctl
->output_tty
);
1810 tcgetattr(STDERR_FILENO
, &ctl
->output_tty
);
1811 ctl
->original_tty
= ctl
->output_tty
;
1812 ctl
->hard_tabs
= (ctl
->output_tty
.c_oflag
& TABDLY
) != TAB3
;
1813 if (ctl
->no_tty_out
)
1816 ctl
->output_tty
.c_lflag
&= ~(ICANON
| ECHO
);
1817 ctl
->output_tty
.c_cc
[VMIN
] = 1;
1818 ctl
->output_tty
.c_cc
[VTIME
] = 0;
1819 ctl
->erase_previous_ok
= (ctl
->output_tty
.c_cc
[VERASE
] != 255);
1820 ctl
->erase_input_ok
= (ctl
->output_tty
.c_cc
[VKILL
] != 255);
1821 if ((term
= getenv("TERM")) == NULL
) {
1824 setupterm(term
, 1, &ret
);
1829 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0) {
1830 ctl
->lines_per_page
= tigetnum(TERM_LINES
);
1831 ctl
->num_columns
= tigetnum(TERM_COLS
);
1833 if ((ctl
->lines_per_page
= win
.ws_row
) == 0)
1834 ctl
->lines_per_page
= tigetnum(TERM_LINES
);
1835 if ((ctl
->num_columns
= win
.ws_col
) == 0)
1836 ctl
->num_columns
= tigetnum(TERM_COLS
);
1838 if ((ctl
->lines_per_page
<= 0) || tigetflag(TERM_HARD_COPY
)) {
1840 ctl
->lines_per_page
= LINES_PER_PAGE
;
1843 if (tigetflag(TERM_EAT_NEW_LINE
))
1844 /* Eat newline at last column + 1; dec, concept */
1846 if (ctl
->num_columns
<= 0)
1847 ctl
->num_columns
= NUM_COLUMNS
;
1849 ctl
->wrap_margin
= tigetflag(TERM_AUTO_RIGHT_MARGIN
);
1850 ctl
->bad_stdout
= tigetflag(TERM_CEOL
);
1851 ctl
->erase_line
= tigetstr(TERM_CLEAR_TO_LINE_END
);
1852 ctl
->clear
= tigetstr(TERM_CLEAR
);
1853 if ((ctl
->enter_std
= tigetstr(TERM_STANDARD_MODE
)) != NULL
) {
1854 ctl
->exit_std
= tigetstr(TERM_EXIT_STANDARD_MODE
);
1855 if (0 < tigetnum(TERM_STD_MODE_GLITCH
))
1856 ctl
->stdout_glitch
= 1;
1859 cursor_addr
= tigetstr(TERM_HOME
);
1860 if (cursor_addr
== NULL
|| *cursor_addr
== '\0') {
1861 cursor_addr
= tigetstr(TERM_CURSOR_ADDRESS
);
1863 cursor_addr
= tparm(cursor_addr
, 0, 0);
1866 ctl
->go_home
= xstrdup(cursor_addr
);
1868 if ((ctl
->move_line_down
= tigetstr(TERM_LINE_DOWN
)) == NULL
)
1869 ctl
->move_line_down
= BACKSPACE
;
1870 ctl
->clear_rest
= tigetstr(TERM_CLEAR_TO_SCREEN_END
);
1871 if ((ctl
->backspace_ch
= tigetstr(TERM_BACKSPACE
)) == NULL
)
1872 ctl
->backspace_ch
= BACKSPACE
;
1874 if ((ctl
->shell
= getenv("SHELL")) == NULL
)
1875 ctl
->shell
= _PATH_BSHELL
;
1878 int main(int argc
, char **argv
)
1882 struct more_control ctl
= {
1884 .fold_long_lines
= 1,
1885 .no_quit_dialog
= 1,
1886 .stop_after_formfeed
= 1,
1888 .lines_per_page
= LINES_PER_PAGE
,
1889 .num_columns
= NUM_COLUMNS
,
1890 .d_scroll_len
= SCROLL_LEN
,
1894 setlocale(LC_ALL
, "");
1895 bindtextdomain(PACKAGE
, LOCALEDIR
);
1896 textdomain(PACKAGE
);
1897 close_stdout_atexit();
1898 setlocale(LC_ALL
, "");
1900 /* Auto set no scroll on when binary is called page */
1901 if (!(strcmp(program_invocation_short_name
, "page")))
1904 if ((s
= getenv("MORE")) != NULL
)
1905 env_argscan(&ctl
, s
);
1906 argscan(&ctl
, argc
, argv
);
1910 prepare_line_buffer(&ctl
);
1912 ctl
.d_scroll_len
= ctl
.lines_per_page
/ 2 - 1;
1913 if (ctl
.d_scroll_len
<= 0)
1914 ctl
.d_scroll_len
= 1;
1916 /* allow clear_line_ends only if go_home and erase_line and clear_rest strings are
1917 * defined, and in that case, make sure we are in no_scroll mode */
1918 if (ctl
.clear_line_ends
) {
1919 if ((ctl
.go_home
== NULL
) || (*ctl
.go_home
== '\0') ||
1920 (ctl
.erase_line
== NULL
) || (*ctl
.erase_line
== '\0') ||
1921 (ctl
.clear_rest
== NULL
) || (*ctl
.clear_rest
== '\0'))
1922 ctl
.clear_line_ends
= 0;
1926 if (ctl
.lines_per_screen
== 0)
1927 ctl
.lines_per_screen
= ctl
.lines_per_page
- 1;
1928 left
= ctl
.lines_per_screen
;
1929 if (ctl
.num_files
> 1)
1930 ctl
.print_banner
= 1;
1931 if (!ctl
.no_tty_in
&& ctl
.num_files
== 0) {
1932 warnx(_("bad usage"));
1933 errtryhelp(EXIT_FAILURE
);
1935 ctl
.current_file
= stdin
;
1936 if (!ctl
.no_tty_out
) {
1937 if (signal(SIGTSTP
, SIG_IGN
) == SIG_DFL
) {
1938 ctl
.catch_suspend
++;
1940 tcsetattr(STDERR_FILENO
, TCSANOW
, &ctl
.output_tty
);
1942 sigemptyset(&ctl
.sigset
);
1943 sigaddset(&ctl
.sigset
, SIGINT
);
1944 sigaddset(&ctl
.sigset
, SIGQUIT
);
1945 sigaddset(&ctl
.sigset
, SIGTSTP
);
1946 sigaddset(&ctl
.sigset
, SIGCONT
);
1947 sigaddset(&ctl
.sigset
, SIGWINCH
);
1948 sigprocmask(SIG_BLOCK
, &ctl
.sigset
, NULL
);
1949 ctl
.sigfd
= signalfd(-1, &ctl
.sigset
, SFD_CLOEXEC
);
1950 if (ctl
.no_tty_in
) {
1954 ctl
.current_file
= stdin
;
1955 display_file(&ctl
, left
);
1958 ctl
.print_banner
= 1;
1962 while (ctl
.argv_position
< ctl
.num_files
) {
1963 checkf(&ctl
, ctl
.file_names
[ctl
.argv_position
]);
1964 display_file(&ctl
, left
);
1966 ctl
.argv_position
++;
1968 ctl
.clear_line_ends
= 0;