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)
85 #include "closestream.h"
89 # define NON_INTERACTIVE_MORE 1
92 #define BACKSPACE "\b"
95 #define ARROW_UP "\x1b\x5b\x41"
96 #define ARROW_DOWN "\x1b\x5b\x42"
97 #define PAGE_UP "\x1b\x5b\x35\x7e"
98 #define PAGE_DOWN "\x1b\x5b\x36\x7e"
100 #define MIN_LINE_SZ 256 /* minimal line_buf buffer size */
102 #define SCROLL_LEN 11
103 #define LINES_PER_PAGE 24
104 #define NUM_COLUMNS 80
105 #define TERMINAL_BUF 4096
107 #define COMMAND_BUF 200
108 #define REGERR_BUF NUM_COLUMNS
110 #define TERM_AUTO_RIGHT_MARGIN "am"
111 #define TERM_BACKSPACE "cub1"
112 #define TERM_CEOL "xhp"
113 #define TERM_CLEAR "clear"
114 #define TERM_CLEAR_TO_LINE_END "el"
115 #define TERM_CLEAR_TO_SCREEN_END "ed"
116 #define TERM_COLS "cols"
117 #define TERM_CURSOR_ADDRESS "cup"
118 #define TERM_EAT_NEW_LINE "xenl"
119 #define TERM_EXIT_STANDARD_MODE "rmso"
120 #define TERM_HARD_COPY "hc"
121 #define TERM_HOME "home"
122 #define TERM_LINE_DOWN "cud1"
123 #define TERM_LINES "lines"
124 #define TERM_OVER_STRIKE "os"
125 #define TERM_STANDARD_MODE "smso"
126 #define TERM_STD_MODE_GLITCH "xmc"
128 /* Used in read_command() */
130 more_kc_unknown_command
,
132 more_kc_repeat_previous
,
134 more_kc_jump_lines_per_screen
,
135 more_kc_set_lines_per_screen
,
136 more_kc_set_scroll_len
,
138 more_kc_skip_forward_screen
,
139 more_kc_skip_forward_line
,
141 more_kc_clear_screen
,
142 more_kc_previous_search_match
,
143 more_kc_display_line
,
144 more_kc_display_file_and_line
,
145 more_kc_repeat_search
,
150 more_kc_previous_file
,
153 struct number_command
{
155 more_key_commands key
;
158 struct more_control
{
159 struct termios output_tty
; /* output terminal */
160 struct termios original_tty
; /* original terminal settings */
161 FILE *current_file
; /* currently open input file */
162 off_t file_position
; /* file position */
163 off_t file_size
; /* file size */
164 int argv_position
; /* argv[] position */
165 int lines_per_screen
; /* screen size in lines */
166 int d_scroll_len
; /* number of lines scrolled by 'd' */
167 int prompt_len
; /* message prompt length */
168 int current_line
; /* line we are currently at */
169 int next_jump
; /* number of lines to skip ahead */
170 char **file_names
; /* The list of file names */
171 int num_files
; /* Number of files left to process */
172 char *shell
; /* name of the shell to use */
173 int sigfd
; /* signalfd() file descriptor */
174 sigset_t sigset
; /* signal operations */
175 char *line_buf
; /* line buffer */
176 size_t line_sz
; /* size of line_buf buffer */
177 int lines_per_page
; /* lines per page */
178 char *clear
; /* clear screen */
179 char *erase_line
; /* erase line */
180 char *enter_std
; /* enter standout mode */
181 char *exit_std
; /* exit standout mode */
182 char *backspace_ch
; /* backspace character */
183 char *go_home
; /* go to home */
184 char *move_line_down
; /* move line down */
185 char *clear_rest
; /* clear rest of screen */
186 int num_columns
; /* number of columns */
187 char *next_search
; /* file beginning search string */
188 char *previous_search
; /* previous search() buf[] item */
190 off_t row_num
; /* row file position */
191 long line_num
; /* line number */
194 unsigned int leading_number
; /* number in front of key command */
195 struct number_command previous_command
; /* previous key command */
196 char *shell_line
; /* line to execute in subshell */
198 magic_t magic
; /* libmagic database entries */
201 bad_stdout
:1, /* true if overwriting does not turn off standout */
202 catch_suspend
:1, /* we should catch the SIGTSTP signal */
203 clear_line_ends
:1, /* do not scroll, paint each screen from the top */
204 clear_first
:1, /* is first character in file \f */
205 dumb_tty
:1, /* is terminal type known */
206 eat_newline
:1, /* is newline ignored after 80 cols */
207 erase_input_ok
:1, /* is erase input supported */
208 erase_previous_ok
:1, /* is erase previous supported */
209 exit_on_eof
:1, /* exit on EOF */
210 first_file
:1, /* is the input file the first in list */
211 fold_long_lines
:1, /* fold long lines */
212 hard_tabs
:1, /* print spaces instead of '\t' */
213 hard_tty
:1, /* is this hard copy terminal (a printer or such) */
214 leading_colon
:1, /* key command has leading ':' character */
215 is_eof
:1, /* EOF detected */
216 is_paused
:1, /* is output paused */
217 no_quit_dialog
:1, /* suppress quit dialog */
218 no_scroll
:1, /* do not scroll, clear the screen and then display text */
219 no_tty_in
:1, /* is input in interactive mode */
220 no_tty_out
:1, /* is output in interactive mode */
221 no_tty_err
:1, /* is stderr terminal */
222 print_banner
:1, /* print file name banner */
223 reading_num
:1, /* are we reading leading_number */
224 report_errors
:1, /* is an error reported */
225 search_at_start
:1, /* search pattern defined at start up */
226 search_called
:1, /* previous more command was a search */
227 squeeze_spaces
:1, /* suppress white space */
228 stdout_glitch
:1, /* terminal has standout mode glitch */
229 stop_after_formfeed
:1, /* stop after form feeds */
230 suppress_bell
:1, /* suppress bell */
231 wrap_margin
:1; /* set if automargins */
234 static void __attribute__((__noreturn__
)) usage(void)
236 printf("%s", USAGE_HEADER
);
237 printf(_(" %s [options] <file>...\n"), program_invocation_short_name
);
239 printf("%s", USAGE_SEPARATOR
);
240 printf("%s\n", _("Display the contents of a file in a terminal."));
242 printf("%s", USAGE_OPTIONS
);
243 printf("%s\n", _(" -d, --silent display help instead of ringing bell"));
244 printf("%s\n", _(" -f, --logical count logical rather than screen lines"));
245 printf("%s\n", _(" -l, --no-pause suppress pause after form feed"));
246 printf("%s\n", _(" -c, --print-over do not scroll, display text and clean line ends"));
247 printf("%s\n", _(" -p, --clean-print do not scroll, clean screen and display text"));
248 printf("%s\n", _(" -e, --exit-on-eof exit on end-of-file"));
249 printf("%s\n", _(" -s, --squeeze squeeze multiple blank lines into one"));
250 printf("%s\n", _(" -u, --plain suppress underlining and bold"));
251 printf("%s\n", _(" -n, --lines <number> the number of lines per screenful"));
252 printf("%s\n", _(" -<number> same as --lines"));
253 printf("%s\n", _(" +<number> display file beginning from line number"));
254 printf("%s\n", _(" +/<pattern> display file beginning from pattern match"));
255 printf("%s", USAGE_SEPARATOR
);
256 printf(USAGE_HELP_OPTIONS(23));
257 printf(USAGE_MAN_TAIL("more(1)"));
261 static void argscan(struct more_control
*ctl
, int as_argc
, char **as_argv
)
264 static const struct option longopts
[] = {
265 { "silent", no_argument
, NULL
, 'd' },
266 { "logical", no_argument
, NULL
, 'f' },
267 { "no-pause", no_argument
, NULL
, 'l' },
268 { "print-over", no_argument
, NULL
, 'c' },
269 { "clean-print", no_argument
, NULL
, 'p' },
270 { "exit-on-eof", no_argument
, NULL
, 'e' },
271 { "squeeze", no_argument
, NULL
, 's' },
272 { "plain", no_argument
, NULL
, 'u' },
273 { "lines", required_argument
, NULL
, 'n' },
274 { "version", no_argument
, NULL
, 'V' },
275 { "help", no_argument
, NULL
, 'h' },
279 /* Take care of number option and +args. */
280 for (opt
= 0; opt
< as_argc
; opt
++) {
283 if (as_argv
[opt
][0] == '-' && isdigit_string(as_argv
[opt
] + 1)) {
284 ctl
->lines_per_screen
=
285 strtos16_or_err(as_argv
[opt
], _("failed to parse number"));
286 ctl
->lines_per_screen
= abs(ctl
->lines_per_screen
);
288 } else if (as_argv
[opt
][0] == '+') {
289 if (isdigit_string(as_argv
[opt
] + 1)) {
290 ctl
->next_jump
= strtos32_or_err(as_argv
[opt
],
291 _("failed to parse number")) - 1;
293 } else if (as_argv
[opt
][1] == '/') {
294 free(ctl
->next_search
);
295 ctl
->next_search
= xstrdup(as_argv
[opt
] + 2);
296 ctl
->search_at_start
= 1;
301 as_argc
= remove_entry(as_argv
, opt
, as_argc
);
306 while ((c
= getopt_long(as_argc
, as_argv
, "dflcpsun:eVh", longopts
, NULL
)) != -1) {
309 ctl
->suppress_bell
= 1;
312 ctl
->stop_after_formfeed
= 0;
315 ctl
->fold_long_lines
= 0;
321 ctl
->clear_line_ends
= 1;
324 ctl
->squeeze_spaces
= 1;
329 ctl
->lines_per_screen
= strtou16_or_err(optarg
, _("argument error"));
332 ctl
->exit_on_eof
= 1;
335 print_version(EXIT_SUCCESS
);
339 errtryhelp(EXIT_FAILURE
);
343 ctl
->num_files
= as_argc
- optind
;
344 ctl
->file_names
= as_argv
+ optind
;
347 static void env_argscan(struct more_control
*ctl
, const char *s
)
352 const char delim
[] = { ' ', '\n', '\t', '\0' };
353 char *str
= xstrdup(s
);
354 char *key
= NULL
, *tok
;
356 env_argv
= xreallocarray(NULL
, size
, sizeof(char *));
357 env_argv
[0] = _("MORE environment variable"); /* program name */
358 for (tok
= strtok_r(str
, delim
, &key
); tok
; tok
= strtok_r(NULL
, delim
, &key
)) {
359 if (size
== env_argc
) {
361 env_argv
= xreallocarray(env_argv
, size
, sizeof(char *));
363 env_argv
[env_argc
++] = tok
;
366 argscan(ctl
, env_argc
, env_argv
);
367 /* Reset optind, command line parsing needs this. */
373 static void more_fseek(struct more_control
*ctl
, off_t pos
)
375 ctl
->file_position
= pos
;
376 fseeko(ctl
->current_file
, pos
, SEEK_SET
);
379 static int more_getc(struct more_control
*ctl
)
381 int ret
= getc(ctl
->current_file
);
382 ctl
->file_position
= ftello(ctl
->current_file
);
386 static int more_ungetc(struct more_control
*ctl
, int c
)
388 int ret
= ungetc(c
, ctl
->current_file
);
389 ctl
->file_position
= ftello(ctl
->current_file
);
393 static void print_separator(const int c
, int n
)
400 /* check_magic -- check for file magic numbers. */
401 static int check_magic(struct more_control
*ctl
, char *fs
)
404 const int fd
= fileno(ctl
->current_file
);
405 const char *mime_encoding
= magic_descriptor(ctl
->magic
, fd
);
406 const char *magic_error_msg
= magic_error(ctl
->magic
);
408 if (magic_error_msg
) {
409 printf("%s: %s: %s\n", program_invocation_short_name
,
410 _("magic failed"), magic_error_msg
);
413 if (!mime_encoding
|| !(strcmp("binary", mime_encoding
))) {
414 printf(_("\n******** %s: Not a text file ********\n\n"), fs
);
418 signed char twobytes
[2];
420 /* don't try to look ahead if the input is unseekable */
421 if (fseek(ctl
->current_file
, 0L, SEEK_SET
))
424 if (fread(twobytes
, 2, 1, ctl
->current_file
) == 1) {
425 switch (twobytes
[0] + (twobytes
[1] << 8)) {
426 case 0407: /* a.out obj */
427 case 0410: /* a.out exec */
428 case 0413: /* a.out demand exec */
432 case 0x457f: /* simple ELF detection */
433 printf(_("\n******** %s: Not a text file ********\n\n"),
438 fseek(ctl
->current_file
, 0L, SEEK_SET
); /* rewind() not necessary */
443 /* Check whether the file named by fs is an ASCII file which the user may
444 * access. If it is, return the opened file. Otherwise return NULL. */
445 static void checkf(struct more_control
*ctl
, char *fs
)
450 ctl
->current_line
= 0;
451 ctl
->file_position
= 0;
455 ctl
->current_file
= fopen(fs
, "r");
456 if (ctl
->current_file
== NULL
) {
457 if (ctl
->clear_line_ends
)
458 putp(ctl
->erase_line
);
459 warn(_("cannot open %s"), fs
);
462 if (fstat(fileno(ctl
->current_file
), &st
) != 0) {
463 warn(_("stat of %s failed"), fs
);
466 if ((st
.st_mode
& S_IFMT
) == S_IFDIR
) {
467 printf(_("\n*** %s: directory ***\n\n"), fs
);
468 ctl
->current_file
= NULL
;
471 ctl
->file_size
= st
.st_size
;
472 if (0 < ctl
->file_size
&& check_magic(ctl
, fs
)) {
473 fclose(ctl
->current_file
);
474 ctl
->current_file
= NULL
;
477 fcntl(fileno(ctl
->current_file
), F_SETFD
, FD_CLOEXEC
);
479 ctl
->clear_first
= (c
== '\f');
483 static void prepare_line_buffer(struct more_control
*ctl
)
485 size_t sz
= ctl
->num_columns
* 4;
487 if (ctl
->line_sz
>= sz
)
490 if (sz
< MIN_LINE_SZ
)
493 /* alloc sz and extra space for \n\0 */
494 ctl
->line_buf
= xrealloc(ctl
->line_buf
, sz
+ 2);
498 /* Get a logical line */
499 static int get_line(struct more_control
*ctl
, int *length
)
504 static int column_wrap
;
510 mbstate_t state
, state_bak
; /* Current status of the stream. */
511 char mbc
[MB_LEN_MAX
]; /* Buffer for one multibyte char. */
512 size_t mblength
; /* Byte length of multibyte char. */
513 size_t mbc_pos
= 0; /* Position of the MBC. */
514 int use_mbc_buffer_flag
= 0; /* If 1, mbc has data. */
515 int break_flag
= 0; /* If 1, exit while(). */
516 off_t file_position_bak
= ctl
->file_position
;
518 memset(&state
, '\0', sizeof(mbstate_t));
524 if (column_wrap
&& c
== '\n') {
528 while (p
< &ctl
->line_buf
[ctl
->line_sz
- 1]) {
530 if (ctl
->fold_long_lines
&& use_mbc_buffer_flag
&& MB_CUR_MAX
> 1) {
531 use_mbc_buffer_flag
= 0;
535 mblength
= mbrtowc(&wc
, mbc
, mbc_pos
, &state
);
538 case (size_t)-2: /* Incomplete multibyte character. */
539 use_mbc_buffer_flag
= 1;
543 case (size_t)-1: /* Invalid as a multibyte character. */
548 if (column
>= ctl
->num_columns
) {
549 more_fseek(ctl
, file_position_bak
);
551 memmove(mbc
, mbc
+ 1, --mbc_pos
);
560 wc_width
= wcwidth(wc
);
561 if (column
+ wc_width
> ctl
->num_columns
) {
562 more_fseek(ctl
, file_position_bak
);
565 for (i
= 0; p
< &ctl
->line_buf
[ctl
->line_sz
- 1] &&
573 if (break_flag
|| column
>= ctl
->num_columns
)
579 #endif /* HAVE_WIDECHAR */
581 if (p
> ctl
->line_buf
) {
583 *length
= p
- ctl
->line_buf
;
586 *length
= p
- ctl
->line_buf
;
596 if (!ctl
->hard_tabs
|| (column
< ctl
->prompt_len
&& !ctl
->hard_tty
)) {
597 if (ctl
->hard_tabs
&& ctl
->erase_line
&& !ctl
->dumb_tty
) {
598 column
= 1 + (column
| 7);
599 putp(ctl
->erase_line
);
602 for (--p
; p
< &ctl
->line_buf
[ctl
->line_sz
- 1];) {
604 if ((++column
& 7) == 0)
607 if (column
>= ctl
->prompt_len
)
611 column
= 1 + (column
| 7);
612 } else if (c
== '\b' && column
> 0) {
614 } else if (c
== '\r') {
615 int next
= more_getc(ctl
);
623 } else if (c
== '\f' && ctl
->stop_after_formfeed
) {
628 } else if (c
== EOF
) {
629 *length
= p
- ctl
->line_buf
;
633 if (ctl
->fold_long_lines
&& MB_CUR_MAX
> 1) {
634 memset(mbc
, '\0', MB_LEN_MAX
);
639 mblength
= mbrtowc(&wc
, mbc
, mbc_pos
, &state
);
640 /* The value of mblength is always less than 2 here. */
644 file_position_bak
= ctl
->file_position
- 1;
646 use_mbc_buffer_flag
= 1;
655 wc_width
= wcwidth(wc
);
660 #endif /* HAVE_WIDECHAR */
667 if (column
>= ctl
->num_columns
&& ctl
->fold_long_lines
)
670 if (use_mbc_buffer_flag
== 0 && p
>= &ctl
->line_buf
[ctl
->line_sz
- 1 - 4])
671 /* don't read another char if there is no space for
672 * whole multibyte sequence */
677 if (column
>= ctl
->num_columns
&& ctl
->num_columns
> 0) {
678 if (!ctl
->wrap_margin
) {
682 column_wrap
= column
== ctl
->num_columns
&& ctl
->fold_long_lines
;
683 if (column_wrap
&& ctl
->eat_newline
&& ctl
->wrap_margin
) {
684 *p
++ = '\n'; /* simulate normal wrap */
686 *length
= p
- ctl
->line_buf
;
691 /* Erase the rest of the prompt, assuming we are starting at column col. */
692 static void erase_to_col(struct more_control
*ctl
, int col
)
695 if (ctl
->prompt_len
== 0)
697 if (col
== 0 && ctl
->clear_line_ends
)
698 puts(ctl
->erase_line
);
699 else if (ctl
->hard_tty
)
704 if (!ctl
->dumb_tty
&& ctl
->erase_line
)
705 putp(ctl
->erase_line
);
707 printf("%*s", ctl
->prompt_len
- col
, "");
712 ctl
->prompt_len
= col
;
715 static void output_prompt(struct more_control
*ctl
, char *filename
)
717 if (ctl
->clear_line_ends
)
718 putp(ctl
->erase_line
);
719 else if (ctl
->prompt_len
> 0)
720 erase_to_col(ctl
, 0);
721 if (!ctl
->hard_tty
) {
723 if (ctl
->enter_std
) {
724 putp(ctl
->enter_std
);
725 ctl
->prompt_len
+= (2 * ctl
->stdout_glitch
);
727 if (ctl
->clear_line_ends
)
728 putp(ctl
->erase_line
);
729 ctl
->prompt_len
+= printf(_("--More--"));
730 if (filename
!= NULL
) {
731 ctl
->prompt_len
+= printf(_("(Next file: %s)"), filename
);
732 } else if (!ctl
->no_tty_in
&& 0 < ctl
->file_size
) {
733 int position
= ((ctl
->file_position
* 100) / ctl
->file_size
);
734 if (position
== 100) {
735 erase_to_col(ctl
, 0);
736 ctl
->prompt_len
+= printf(_("(END)"));
738 ctl
->prompt_len
+= printf("(%d%%)", position
);
740 } else if (ctl
->is_eof
) {
741 erase_to_col(ctl
, 0);
742 ctl
->prompt_len
+= printf(_("(END)"));
745 if (ctl
->suppress_bell
) {
747 printf(_("[Press space to continue, 'q' to quit.]"));
751 if (ctl
->clear_line_ends
)
752 putp(ctl
->clear_rest
);
754 fprintf(stderr
, "\a");
758 static void reset_tty(struct more_control
*ctl
)
763 ctl
->output_tty
.c_lflag
|= ICANON
| ECHO
;
764 ctl
->output_tty
.c_cc
[VMIN
] = ctl
->original_tty
.c_cc
[VMIN
];
765 ctl
->output_tty
.c_cc
[VTIME
] = ctl
->original_tty
.c_cc
[VTIME
];
766 tcsetattr(STDERR_FILENO
, TCSANOW
, &ctl
->original_tty
);
769 /* Clean up terminal state and exit. Also come here if interrupt signal received */
770 static void __attribute__((__noreturn__
)) more_exit(struct more_control
*ctl
)
773 magic_close(ctl
->magic
);
776 if (ctl
->clear_line_ends
) {
778 putp(ctl
->erase_line
);
779 } else if (!ctl
->clear_line_ends
&& (ctl
->prompt_len
> 0))
780 erase_to_col(ctl
, 0);
782 free(ctl
->previous_search
);
783 free(ctl
->shell_line
);
786 if (ctl
->current_file
)
787 fclose(ctl
->current_file
);
788 del_curterm(cur_term
);
792 static cc_t
read_user_input(struct more_control
*ctl
)
798 * Key commands can be read() from either stderr or stdin. If they
799 * are read from stdin such as 'cat file.txt | more' then the pipe
800 * input is understood as series key commands - and that is not
801 * wanted. Keep the read() reading from stderr.
803 if (read(STDERR_FILENO
, &c
, 1) <= 0) {
807 c
= ctl
->output_tty
.c_cc
[VKILL
];
812 /* Read a number and command from the terminal. Set cmd to the non-digit
813 * which terminates the number. */
814 static struct number_command
read_command(struct more_control
*ctl
)
816 cc_t input
[8] = { 0 };
818 struct number_command cmd
= { .key
= more_kc_unknown_command
};
820 /* See stderr note in read_user_input() */
821 if ((ilen
= read(STDERR_FILENO
, &input
, sizeof(input
))) <= 0)
824 if (!memcmp(input
, ARROW_UP
, sizeof(ARROW_UP
))) {
825 cmd
.key
= more_kc_backwards
;
827 } else if (!memcmp(input
, ARROW_DOWN
, sizeof(ARROW_DOWN
))) {
828 cmd
.key
= more_kc_jump_lines_per_screen
;
830 } else if (!memcmp(input
, PAGE_UP
, sizeof(PAGE_UP
))) {
831 cmd
.key
= more_kc_backwards
;
833 } else if (!memcmp(input
, PAGE_DOWN
, sizeof(PAGE_DOWN
))) {
834 cmd
.key
= more_kc_jump_lines_per_screen
;
838 for (i
= 0; i
< ilen
; i
++) {
839 if (isdigit(input
[i
])) {
840 if (0 < ctl
->reading_num
) {
841 ctl
->leading_number
*= 10;
842 ctl
->leading_number
+= input
[i
] - '0';
844 ctl
->leading_number
= input
[i
] - '0';
845 ctl
->reading_num
= 1;
848 cmd
.number
= ctl
->leading_number
;
849 ctl
->reading_num
= 0;
850 ctl
->leading_number
= 0;
851 if (ctl
->leading_colon
) {
852 ctl
->leading_colon
= 0;
855 cmd
.key
= more_kc_display_file_and_line
;
858 cmd
.key
= more_kc_next_file
;
861 cmd
.key
= more_kc_previous_file
;
864 cmd
.key
= more_kc_unknown_command
;
868 /* command is a single char */
871 cmd
.key
= more_kc_repeat_previous
;
874 ctl
->leading_colon
= 1;
878 cmd
.key
= more_kc_backwards
;
881 cmd
.key
= more_kc_jump_lines_per_screen
;
884 cmd
.key
= more_kc_set_lines_per_screen
;
888 cmd
.key
= more_kc_set_scroll_len
;
892 cmd
.key
= more_kc_quit
;
897 cmd
.key
= more_kc_skip_forward_screen
;
900 cmd
.key
= more_kc_skip_forward_line
;
903 cmd
.key
= more_kc_next_line
;
906 cmd
.key
= more_kc_clear_screen
;
909 cmd
.key
= more_kc_previous_search_match
;
912 cmd
.key
= more_kc_display_line
;
915 cmd
.key
= more_kc_repeat_search
;
918 cmd
.key
= more_kc_search
;
921 cmd
.key
= more_kc_run_shell
;
925 cmd
.key
= more_kc_help
;
928 cmd
.key
= more_kc_run_editor
;
935 /* Change displayed file from command line list to next nskip, where nskip
936 * is relative position in argv and can be negative, that is a previous
938 static void change_file(struct more_control
*ctl
, int nskip
)
943 if (ctl
->argv_position
+ nskip
> ctl
->num_files
- 1)
944 nskip
= ctl
->num_files
- ctl
->argv_position
- 1;
946 ctl
->argv_position
+= nskip
;
947 if (ctl
->argv_position
< 0)
948 ctl
->argv_position
= 0;
949 puts(_("\n...Skipping "));
950 if (ctl
->clear_line_ends
)
951 putp(ctl
->erase_line
);
953 fputs(_("...Skipping to file "), stdout
);
955 fputs(_("...Skipping back to file "), stdout
);
956 puts(ctl
->file_names
[ctl
->argv_position
]);
957 if (ctl
->clear_line_ends
)
958 putp(ctl
->erase_line
);
960 ctl
->argv_position
--;
963 static void show(struct more_control
*ctl
, char c
)
965 if ((c
< ' ' && c
!= '\n' && c
!= ESC
) || c
== CERASE
) {
966 c
+= (c
== CERASE
) ? -0100 : 0100;
967 fputs(CARAT
, stderr
);
974 static void more_error(struct more_control
*ctl
, char *mess
)
976 if (ctl
->clear_line_ends
)
977 putp(ctl
->erase_line
);
979 erase_to_col(ctl
, 0);
980 ctl
->prompt_len
+= strlen(mess
);
982 putp(ctl
->enter_std
);
987 ctl
->report_errors
++;
990 static void erase_one_column(struct more_control
*ctl
)
992 if (ctl
->erase_previous_ok
)
993 fprintf(stderr
, "%s ", ctl
->backspace_ch
);
994 fputs(ctl
->backspace_ch
, stderr
);
997 static void ttyin(struct more_control
*ctl
, char buf
[], int nmax
, char pchar
)
1006 while (sp
- buf
< nmax
) {
1007 if (ctl
->prompt_len
> maxlen
)
1008 maxlen
= ctl
->prompt_len
;
1009 c
= read_user_input(ctl
);
1012 } else if (c
== ctl
->output_tty
.c_cc
[VERASE
] && !slash
) {
1014 #ifdef HAVE_WIDECHAR
1015 if (MB_CUR_MAX
> 1) {
1017 size_t pos
= 0, mblength
;
1018 mbstate_t state
, state_bak
;
1020 memset(&state
, '\0', sizeof(mbstate_t));
1025 mbrtowc(&wc
, buf
+ pos
,
1036 if (buf
+ pos
+ mblength
>= sp
)
1042 if (mblength
== 1) {
1043 erase_one_column(ctl
);
1046 wc_width
= wcwidth(wc
);
1050 while (wc_width
--) {
1051 erase_one_column(ctl
);
1055 while (mblength
--) {
1060 #endif /* HAVE_WIDECHAR */
1063 erase_one_column(ctl
);
1067 if ((*sp
< ' ' && *sp
!= '\n') || *sp
== CERASE
) {
1069 erase_one_column(ctl
);
1073 if (!ctl
->erase_line
)
1074 ctl
->prompt_len
= maxlen
;
1075 } else if (c
== ctl
->output_tty
.c_cc
[VKILL
] && !slash
) {
1076 if (ctl
->hard_tty
) {
1083 if (ctl
->erase_line
)
1084 erase_to_col(ctl
, 1);
1085 else if (ctl
->erase_input_ok
)
1086 while (ctl
->prompt_len
-- > 1)
1087 fprintf(stderr
, "%s %s", ctl
->backspace_ch
, ctl
->backspace_ch
);
1088 ctl
->prompt_len
= 1;
1094 if (slash
&& (c
== ctl
->output_tty
.c_cc
[VKILL
] ||
1095 c
== ctl
->output_tty
.c_cc
[VERASE
])) {
1096 erase_one_column(ctl
);
1102 if ((c
< ' ' && c
!= '\n' && c
!= ESC
) || c
== CERASE
) {
1103 c
+= (c
== CERASE
) ? -0100 : 0100;
1104 fputs(CARAT
, stderr
);
1107 if (c
!= '\n' && c
!= ESC
) {
1114 if (!ctl
->erase_line
)
1115 ctl
->prompt_len
= maxlen
;
1116 if (sp
- buf
>= nmax
- 1)
1117 more_error(ctl
, _("Line too long"));
1120 /* Expand shell command line. */
1121 static void expand(struct more_control
*ctl
, char *inbuf
)
1127 int tempsz
, xtra
= 0, offset
;
1129 if (!ctl
->no_tty_in
)
1130 xtra
+= strlen(ctl
->file_names
[ctl
->argv_position
]) + 1;
1131 if (ctl
->shell_line
)
1132 xtra
+= strlen(ctl
->shell_line
) + 1;
1134 tempsz
= COMMAND_BUF
+ xtra
;
1135 temp
= xmalloc(tempsz
);
1139 while ((c
= *inpstr
++) != '\0') {
1140 offset
= outstr
- temp
;
1141 if (tempsz
- offset
- 1 < xtra
) {
1142 tempsz
+= COMMAND_BUF
+ xtra
;
1143 temp
= xrealloc(temp
, tempsz
);
1144 outstr
= temp
+ offset
;
1148 if (!ctl
->no_tty_in
) {
1149 strcpy(outstr
, ctl
->file_names
[ctl
->argv_position
]);
1150 outstr
+= strlen(ctl
->file_names
[ctl
->argv_position
]);
1155 if (ctl
->shell_line
) {
1156 strcpy(outstr
, ctl
->shell_line
);
1157 outstr
+= strlen(ctl
->shell_line
);
1160 ("No previous command to substitute for"));
1163 if (*inpstr
== '%' || *inpstr
== '!') {
1164 *outstr
++ = *inpstr
++;
1173 free(ctl
->shell_line
);
1174 ctl
->shell_line
= temp
;
1177 static void set_tty(struct more_control
*ctl
)
1179 ctl
->output_tty
.c_lflag
&= ~(ICANON
| ECHO
);
1180 ctl
->output_tty
.c_cc
[VMIN
] = 1; /* read at least 1 char */
1181 ctl
->output_tty
.c_cc
[VTIME
] = 0; /* no timeout */
1182 tcsetattr(STDERR_FILENO
, TCSANOW
, &ctl
->output_tty
);
1185 /* Come here if a quit signal is received */
1186 static void sigquit_handler(struct more_control
*ctl
)
1188 if (!ctl
->dumb_tty
&& ctl
->no_quit_dialog
) {
1189 ctl
->prompt_len
+= fprintf(stderr
, _("[Use q or Q to quit]"));
1190 ctl
->no_quit_dialog
= 0;
1195 /* Come here when we get a suspend signal from the terminal */
1196 static void sigtstp_handler(struct more_control
*ctl
)
1200 kill(getpid(), SIGSTOP
);
1203 /* Come here when we get a continue signal from the terminal */
1204 static void sigcont_handler(struct more_control
*ctl
)
1209 /* Come here if a signal for a window size change is received */
1210 static void sigwinch_handler(struct more_control
*ctl
)
1214 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) != -1) {
1215 if (win
.ws_row
!= 0) {
1216 ctl
->lines_per_page
= win
.ws_row
;
1217 ctl
->d_scroll_len
= ctl
->lines_per_page
/ 2 - 1;
1218 if (ctl
->d_scroll_len
< 1)
1219 ctl
->d_scroll_len
= 1;
1220 ctl
->lines_per_screen
= ctl
->lines_per_page
- 1;
1222 if (win
.ws_col
!= 0)
1223 ctl
->num_columns
= win
.ws_col
;
1225 prepare_line_buffer(ctl
);
1228 static void __attribute__((__format__ (__printf__
, 3, 4)))
1229 execute(struct more_control
*ctl
, char *filename
, const char *cmd
, ...)
1241 if (!isatty(STDIN_FILENO
)) {
1242 close(STDIN_FILENO
);
1243 ignore_result( open("/dev/tty", 0) );
1247 va_start(argp
, cmd
);
1248 arg
= va_arg(argp
, char *);
1252 arg
= va_arg(argp
, char *);
1256 args
= xcalloc(argcount
+ 1, sizeof(char *));
1258 va_start(argp
, cmd
);
1259 arg
= va_arg(argp
, char *);
1262 args
[argcount
] = arg
;
1264 arg
= va_arg(argp
, char *);
1268 if ((geteuid() != getuid() || getegid() != getgid())
1269 && drop_permissions() != 0)
1270 err(EXIT_FAILURE
, _("drop permissions failed"));
1274 fputs(_("exec failed\n"), stderr
);
1275 exit(errsv
== ENOENT
? EX_EXEC_ENOENT
: EX_EXEC_FAILED
);
1279 while (wait(NULL
) > 0) {
1284 fputs(_("can't fork\n"), stderr
);
1286 print_separator('-', 24);
1287 output_prompt(ctl
, filename
);
1290 static void run_shell(struct more_control
*ctl
, char *filename
)
1292 char cmdbuf
[COMMAND_BUF
];
1294 erase_to_col(ctl
, 0);
1297 if (ctl
->previous_command
.key
== more_kc_run_shell
&& ctl
->shell_line
)
1298 fputs(ctl
->shell_line
, stderr
);
1300 ttyin(ctl
, cmdbuf
, sizeof(cmdbuf
) - 2, '!');
1301 if (strpbrk(cmdbuf
, "%!\\"))
1302 expand(ctl
, cmdbuf
);
1304 free(ctl
->shell_line
);
1305 ctl
->shell_line
= xstrdup(cmdbuf
);
1308 fputc('\n', stderr
);
1310 ctl
->prompt_len
= 0;
1311 execute(ctl
, filename
, ctl
->shell
, ctl
->shell
, "-c", ctl
->shell_line
, 0);
1314 /* Skip n lines in the file f */
1315 static void skip_lines(struct more_control
*ctl
)
1319 while (ctl
->next_jump
> 0) {
1320 while ((c
= more_getc(ctl
)) != '\n')
1324 ctl
->current_line
++;
1328 /* Clear the screen */
1329 static void more_clear_screen(struct more_control
*ctl
)
1331 if (ctl
->clear
&& !ctl
->hard_tty
) {
1333 /* Put out carriage return so that system doesn't get
1334 * confused by escape sequences when expanding tabs */
1336 ctl
->prompt_len
= 0;
1340 static void read_line(struct more_control
*ctl
)
1346 while ((c
= more_getc(ctl
)) != '\n' && c
!= EOF
1347 && (ptrdiff_t)p
!= (ptrdiff_t)(ctl
->line_buf
+ ctl
->line_sz
- 1))
1350 ctl
->current_line
++;
1354 static int more_poll(struct more_control
*ctl
, int timeout
)
1356 struct pollfd pfd
[2];
1358 pfd
[0].fd
= ctl
->sigfd
;
1359 pfd
[0].events
= POLLIN
| POLLERR
| POLLHUP
;
1360 pfd
[1].fd
= STDIN_FILENO
;
1361 pfd
[1].events
= POLLIN
;
1363 if (poll(pfd
, 2, timeout
) < 0) {
1364 if (errno
== EAGAIN
)
1366 more_error(ctl
, _("poll failed"));
1369 if (pfd
[0].revents
!= 0) {
1370 struct signalfd_siginfo info
;
1373 sz
= read(pfd
[0].fd
, &info
, sizeof(info
));
1374 assert(sz
== sizeof(info
));
1375 switch (info
.ssi_signo
) {
1380 sigquit_handler(ctl
);
1383 sigtstp_handler(ctl
);
1386 sigcont_handler(ctl
);
1389 sigwinch_handler(ctl
);
1395 if (pfd
[1].revents
== 0)
1400 /* Search for nth occurrence of regular expression contained in buf in
1402 static void search(struct more_control
*ctl
, char buf
[], int n
)
1404 off_t startline
= ctl
->file_position
;
1405 off_t line1
= startline
;
1406 off_t line2
= startline
;
1412 if (buf
!= ctl
->previous_search
) {
1413 free(ctl
->previous_search
);
1414 ctl
->previous_search
= buf
;
1417 ctl
->search_called
= 1;
1418 ctl
->context
.line_num
= saveln
= ctl
->current_line
;
1419 ctl
->context
.row_num
= startline
;
1423 if ((rc
= regcomp(&re
, buf
, REG_NOSUB
)) != 0) {
1425 regerror(rc
, &re
, s
, sizeof s
);
1429 while (!feof(ctl
->current_file
)) {
1432 line1
= ctl
->file_position
;
1435 if (regexec(&re
, ctl
->line_buf
, 0, NULL
, 0) == 0 && --n
== 0) {
1436 if ((1 < lncount
&& ctl
->no_tty_in
) || 3 < lncount
) {
1438 if (ctl
->clear_line_ends
)
1439 putp(ctl
->erase_line
);
1440 fputs(_("...skipping\n"), stdout
);
1442 if (!ctl
->no_tty_in
) {
1443 ctl
->current_line
-= (lncount
< 3 ? lncount
: 3);
1444 more_fseek(ctl
, line3
);
1445 if (ctl
->no_scroll
) {
1446 if (ctl
->clear_line_ends
) {
1448 putp(ctl
->erase_line
);
1450 more_clear_screen(ctl
);
1453 erase_to_col(ctl
, 0);
1454 if (ctl
->no_scroll
) {
1455 if (ctl
->clear_line_ends
) {
1457 putp(ctl
->erase_line
);
1459 more_clear_screen(ctl
);
1461 puts(ctl
->line_buf
);
1467 /* Move ctrl+c signal handling back to more_key_command(). */
1468 signal(SIGINT
, SIG_DFL
);
1469 sigaddset(&ctl
->sigset
, SIGINT
);
1470 sigprocmask(SIG_BLOCK
, &ctl
->sigset
, NULL
);
1472 if (feof(ctl
->current_file
)) {
1473 if (!ctl
->no_tty_in
) {
1474 ctl
->current_line
= saveln
;
1475 more_fseek(ctl
, startline
);
1477 fputs(_("\nPattern not found\n"), stdout
);
1481 more_error(ctl
, _("Pattern not found"));
1485 static char *find_editor(void)
1487 static char *editor
;
1489 editor
= getenv("VISUAL");
1490 if (editor
== NULL
|| *editor
== '\0')
1491 editor
= getenv("EDITOR");
1492 if (editor
== NULL
|| *editor
== '\0')
1497 static void runtime_usage(void)
1499 fputs(_("Most commands optionally preceded by integer argument k. "
1500 "Defaults in brackets.\n"
1501 "Star (*) indicates argument becomes new default.\n"), stdout
);
1502 print_separator('-', 79);
1505 ("<space> Display next k lines of text [current screen size]\n"
1506 "z Display next k lines of text [current screen size]*\n"
1507 "<return> Display next k lines of text [1]*\n"
1508 "d or ctrl-D Scroll k lines [current scroll size, initially 11]*\n"
1509 "q or Q or <interrupt> Exit from more\n"
1510 "s Skip forward k lines of text [1]\n"
1511 "f Skip forward k screenfuls of text [1]\n"
1512 "b or ctrl-B Skip backwards k screenfuls of text [1]\n"
1513 "' Go to place where previous search started\n"
1514 "= Display current line number\n"
1515 "/<regular expression> Search for kth occurrence of regular expression [1]\n"
1516 "n Search for kth occurrence of last r.e [1]\n"
1517 "!<cmd> or :!<cmd> Execute <cmd> in a subshell\n"
1518 "v Start up '%s' at current line\n"
1519 "ctrl-L Redraw screen\n"
1520 ":n Go to kth next file [1]\n"
1521 ":p Go to kth previous file [1]\n"
1522 ":f Display current file name and line number\n"
1523 ". Repeat previous command\n"),
1525 print_separator('-', 79);
1528 static void execute_editor(struct more_control
*ctl
, char *cmdbuf
, size_t buflen
, char *filename
)
1534 if ((ctl
->current_line
- ctl
->lines_per_screen
) < 1)
1537 n
= ctl
->current_line
- (ctl
->lines_per_screen
+ 1) / 2;
1538 editor
= find_editor();
1539 p
= strrchr(editor
, '/');
1545 * Earlier: call vi +n file. This also works for emacs.
1546 * POSIX: call vi -c n file (when editor is vi or ex).
1548 if (!strcmp(p
, "vi") || !strcmp(p
, "ex")) {
1549 snprintf(cmdbuf
, buflen
, "-c %d", n
);
1552 snprintf(cmdbuf
, buflen
, "+%d", n
);
1554 erase_to_col(ctl
, 0);
1555 printf("%s %s %s", editor
, cmdbuf
, ctl
->file_names
[ctl
->argv_position
]);
1558 execute(ctl
, filename
, editor
, editor
,
1560 ctl
->file_names
[ctl
->argv_position
], (char *)0);
1562 execute(ctl
, filename
, editor
, editor
,
1563 cmdbuf
, ctl
->file_names
[ctl
->argv_position
], (char *)0);
1566 static int skip_backwards(struct more_control
*ctl
, int nlines
)
1570 erase_to_col(ctl
, 0);
1571 printf(P_("...back %d page", "...back %d pages", nlines
), nlines
);
1573 ctl
->next_jump
= ctl
->current_line
- (ctl
->lines_per_screen
* (nlines
+ 1)) - 1;
1574 if (ctl
->next_jump
< 0)
1577 ctl
->current_line
= 0;
1579 return ctl
->lines_per_screen
;
1582 static int skip_forwards(struct more_control
*ctl
, int nlines
, cc_t comchar
)
1589 nlines
*= ctl
->lines_per_screen
;
1591 erase_to_col(ctl
, 0);
1593 if (ctl
->clear_line_ends
)
1594 putp(ctl
->erase_line
);
1595 printf(P_("...skipping %d line",
1596 "...skipping %d lines", nlines
), nlines
);
1598 if (ctl
->clear_line_ends
)
1599 putp(ctl
->erase_line
);
1602 while (nlines
> 0) {
1603 while ((c
= more_getc(ctl
)) != '\n')
1606 ctl
->current_line
++;
1612 /* Read a command and do it. A command consists of an optional integer
1613 * argument followed by the command character. Return the number of
1614 * lines to display in the next screenful. If there is nothing more to
1615 * display in the current file, zero is returned. */
1616 static int more_key_command(struct more_control
*ctl
, char *filename
)
1619 int done
= 0, search_again
= 0;
1620 char cmdbuf
[INIT_BUF
];
1621 struct number_command cmd
;
1623 if (!ctl
->report_errors
)
1624 output_prompt(ctl
, filename
);
1626 ctl
->report_errors
= 0;
1627 ctl
->search_called
= 0;
1629 if (more_poll(ctl
, -1) != 0)
1631 cmd
= read_command(ctl
);
1632 if (cmd
.key
== more_kc_unknown_command
)
1634 if (cmd
.key
== more_kc_repeat_previous
)
1635 cmd
= ctl
->previous_command
;
1637 case more_kc_backwards
:
1638 if (ctl
->no_tty_in
) {
1639 fprintf(stderr
, "\a");
1642 retval
= skip_backwards(ctl
, cmd
.number
);
1645 case more_kc_jump_lines_per_screen
:
1646 case more_kc_set_lines_per_screen
:
1647 if (cmd
.number
== 0)
1648 cmd
.number
= ctl
->lines_per_screen
;
1649 else if (cmd
.key
== more_kc_set_lines_per_screen
)
1650 ctl
->lines_per_screen
= cmd
.number
;
1651 retval
= cmd
.number
;
1654 case more_kc_set_scroll_len
:
1655 if (cmd
.number
!= 0)
1656 ctl
->d_scroll_len
= cmd
.number
;
1657 retval
= ctl
->d_scroll_len
;
1662 case more_kc_skip_forward_screen
:
1663 if (skip_forwards(ctl
, cmd
.number
, 'f'))
1664 retval
= ctl
->lines_per_screen
;
1667 case more_kc_skip_forward_line
:
1668 if (skip_forwards(ctl
, cmd
.number
, 's'))
1669 retval
= ctl
->lines_per_screen
;
1672 case more_kc_next_line
:
1673 if (cmd
.number
!= 0)
1674 ctl
->lines_per_screen
= cmd
.number
;
1677 retval
= cmd
.number
;
1680 case more_kc_clear_screen
:
1681 if (!ctl
->no_tty_in
) {
1682 more_clear_screen(ctl
);
1683 more_fseek(ctl
, ctl
->screen_start
.row_num
);
1684 ctl
->current_line
= ctl
->screen_start
.line_num
;
1685 retval
= ctl
->lines_per_screen
;
1689 fprintf(stderr
, "\a");
1692 case more_kc_previous_search_match
:
1693 if (!ctl
->no_tty_in
) {
1694 erase_to_col(ctl
, 0);
1695 fputs(_("\n***Back***\n\n"), stdout
);
1696 more_fseek(ctl
, ctl
->context
.row_num
);
1697 ctl
->current_line
= ctl
->context
.line_num
;
1698 retval
= ctl
->lines_per_screen
;
1702 fprintf(stderr
, "\a");
1705 case more_kc_display_line
:
1706 erase_to_col(ctl
, 0);
1707 ctl
->prompt_len
= printf("%d", ctl
->current_line
);
1710 case more_kc_display_file_and_line
:
1711 erase_to_col(ctl
, 0);
1712 if (!ctl
->no_tty_in
)
1714 printf(_("\"%s\" line %d"),
1715 ctl
->file_names
[ctl
->argv_position
], ctl
->current_line
);
1717 ctl
->prompt_len
= printf(_("[Not a file] line %d"),
1721 case more_kc_repeat_search
:
1722 if (!ctl
->previous_search
) {
1723 more_error(ctl
, _("No previous regular expression"));
1728 case more_kc_search
:
1729 if (cmd
.number
== 0)
1731 erase_to_col(ctl
, 0);
1733 ctl
->prompt_len
= 1;
1736 fputc('\r', stderr
);
1737 search(ctl
, ctl
->previous_search
, cmd
.number
);
1740 ttyin(ctl
, cmdbuf
, sizeof(cmdbuf
) - 2, '/');
1741 fputc('\r', stderr
);
1742 ctl
->next_search
= xstrdup(cmdbuf
);
1743 search(ctl
, ctl
->next_search
, cmd
.number
);
1745 retval
= ctl
->lines_per_screen
- 1;
1748 case more_kc_run_shell
:
1749 run_shell(ctl
, filename
);
1753 more_clear_screen(ctl
);
1754 erase_to_col(ctl
, 0);
1756 output_prompt(ctl
, filename
);
1758 case more_kc_next_file
:
1760 erase_to_col(ctl
, 0);
1761 if (cmd
.number
== 0)
1763 if (ctl
->argv_position
+ cmd
.number
>= (unsigned int)ctl
->num_files
)
1765 change_file(ctl
, cmd
.number
);
1768 case more_kc_previous_file
:
1769 if (ctl
->no_tty_in
) {
1770 fprintf(stderr
, "\a");
1774 erase_to_col(ctl
, 0);
1775 if (cmd
.number
== 0)
1777 change_file(ctl
, -cmd
.number
);
1780 case more_kc_run_editor
: /* This case should go right before default */
1781 if (!ctl
->no_tty_in
) {
1782 execute_editor(ctl
, cmdbuf
, sizeof(cmdbuf
), filename
);
1787 if (ctl
->suppress_bell
) {
1788 erase_to_col(ctl
, 0);
1790 putp(ctl
->enter_std
);
1792 printf(_("[Press 'h' for instructions.]"))
1793 + 2 * ctl
->stdout_glitch
;
1795 putp(ctl
->exit_std
);
1797 fprintf(stderr
, "\a");
1801 ctl
->previous_command
= cmd
;
1803 cmd
.key
= more_kc_unknown_command
;
1808 ctl
->no_quit_dialog
= 1;
1812 /* Print out the contents of the file f, one screenful at a time. */
1813 static void screen(struct more_control
*ctl
, int num_lines
)
1817 int length
; /* length of current line */
1818 static int prev_len
= 1; /* length of previous line */
1821 while (num_lines
> 0 && !ctl
->is_paused
) {
1822 nchars
= get_line(ctl
, &length
);
1823 ctl
->is_eof
= nchars
== EOF
;
1824 if (ctl
->is_eof
&& ctl
->exit_on_eof
) {
1825 if (ctl
->clear_line_ends
)
1826 putp(ctl
->clear_rest
);
1829 if (ctl
->squeeze_spaces
&& length
== 0 && prev_len
== 0 && !ctl
->is_eof
)
1833 || ((ctl
->enter_std
&& *ctl
->enter_std
== ' ') && (ctl
->prompt_len
> 0)))
1834 erase_to_col(ctl
, 0);
1835 /* must clear before drawing line since tabs on
1836 * some terminals do not erase what they tab
1838 if (ctl
->clear_line_ends
)
1839 putp(ctl
->erase_line
);
1840 fwrite(ctl
->line_buf
, length
, 1, stdout
);
1841 if (nchars
< ctl
->prompt_len
)
1842 erase_to_col(ctl
, nchars
);
1843 ctl
->prompt_len
= 0;
1844 if (nchars
< ctl
->num_columns
|| !ctl
->fold_long_lines
)
1851 ctl
->is_eof
= c
== EOF
;
1853 if (ctl
->is_eof
&& ctl
->exit_on_eof
) {
1854 if (ctl
->clear_line_ends
)
1855 putp(ctl
->clear_rest
);
1859 if (ctl
->is_paused
&& ctl
->clear_line_ends
)
1860 putp(ctl
->clear_rest
);
1861 more_ungetc(ctl
, c
);
1864 if ((num_lines
= more_key_command(ctl
, NULL
)) == 0)
1866 } while (ctl
->search_called
&& !ctl
->previous_search
);
1867 if (ctl
->hard_tty
&& ctl
->prompt_len
> 0)
1868 erase_to_col(ctl
, 0);
1869 if (ctl
->no_scroll
&& num_lines
>= ctl
->lines_per_screen
) {
1870 if (ctl
->clear_line_ends
)
1873 more_clear_screen(ctl
);
1875 ctl
->screen_start
.line_num
= ctl
->current_line
;
1876 ctl
->screen_start
.row_num
= ctl
->file_position
;
1880 static void copy_file(FILE *f
)
1885 while ((sz
= fread(&buf
, sizeof(char), sizeof(buf
), f
)) > 0)
1886 fwrite(&buf
, sizeof(char), sz
, stdout
);
1890 static void display_file(struct more_control
*ctl
, int left
)
1892 if (!ctl
->current_file
)
1894 ctl
->context
.line_num
= ctl
->context
.row_num
= 0;
1895 ctl
->current_line
= 0;
1896 if (ctl
->first_file
) {
1897 ctl
->first_file
= 0;
1900 if (ctl
->search_at_start
) {
1901 search(ctl
, ctl
->next_search
, 1);
1905 } else if (ctl
->argv_position
< ctl
->num_files
&& !ctl
->no_tty_out
)
1907 more_key_command(ctl
, ctl
->file_names
[ctl
->argv_position
]);
1909 if ((ctl
->no_scroll
|| ctl
->clear_first
)
1910 && 0 < ctl
->file_size
) {
1911 if (ctl
->clear_line_ends
)
1914 more_clear_screen(ctl
);
1916 if (ctl
->print_banner
) {
1917 if (ctl
->bad_stdout
)
1918 erase_to_col(ctl
, 0);
1919 if (ctl
->clear_line_ends
)
1920 putp(ctl
->erase_line
);
1921 if (ctl
->prompt_len
> 14)
1922 erase_to_col(ctl
, 14);
1923 if (ctl
->clear_line_ends
)
1924 putp(ctl
->erase_line
);
1925 print_separator(':', 14);
1926 if (ctl
->clear_line_ends
)
1927 putp(ctl
->erase_line
);
1928 puts(ctl
->file_names
[ctl
->argv_position
]);
1929 if (ctl
->clear_line_ends
)
1930 putp(ctl
->erase_line
);
1931 print_separator(':', 14);
1932 if (left
> ctl
->lines_per_page
- 4)
1933 left
= ctl
->lines_per_page
- 4;
1935 if (ctl
->no_tty_out
)
1936 copy_file(ctl
->current_file
);
1941 fclose(ctl
->current_file
);
1942 ctl
->current_file
= NULL
;
1943 ctl
->screen_start
.line_num
= ctl
->screen_start
.row_num
= 0;
1944 ctl
->context
.line_num
= ctl
->context
.row_num
= 0L;
1947 static void initterm(struct more_control
*ctl
)
1954 #ifndef NON_INTERACTIVE_MORE
1955 ctl
->no_tty_out
= tcgetattr(STDOUT_FILENO
, &ctl
->output_tty
);
1957 ctl
->no_tty_in
= tcgetattr(STDIN_FILENO
, &ctl
->output_tty
);
1958 ctl
->no_tty_err
= tcgetattr(STDERR_FILENO
, &ctl
->output_tty
);
1959 ctl
->original_tty
= ctl
->output_tty
;
1961 ctl
->hard_tabs
= (ctl
->output_tty
.c_oflag
& TABDLY
) != TAB3
;
1962 if (ctl
->no_tty_out
)
1965 ctl
->output_tty
.c_lflag
&= ~(ICANON
| ECHO
);
1966 ctl
->output_tty
.c_cc
[VMIN
] = 1;
1967 ctl
->output_tty
.c_cc
[VTIME
] = 0;
1968 ctl
->erase_previous_ok
= (ctl
->output_tty
.c_cc
[VERASE
] != 255);
1969 ctl
->erase_input_ok
= (ctl
->output_tty
.c_cc
[VKILL
] != 255);
1970 if ((term
= getenv("TERM")) == NULL
) {
1973 setupterm(term
, 1, &ret
);
1978 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0) {
1979 ctl
->lines_per_page
= tigetnum(TERM_LINES
);
1980 ctl
->num_columns
= tigetnum(TERM_COLS
);
1982 if ((ctl
->lines_per_page
= win
.ws_row
) == 0)
1983 ctl
->lines_per_page
= tigetnum(TERM_LINES
);
1984 if ((ctl
->num_columns
= win
.ws_col
) == 0)
1985 ctl
->num_columns
= tigetnum(TERM_COLS
);
1987 if ((ctl
->lines_per_page
<= 0) || tigetflag(TERM_HARD_COPY
)) {
1989 ctl
->lines_per_page
= LINES_PER_PAGE
;
1992 if (tigetflag(TERM_EAT_NEW_LINE
))
1993 /* Eat newline at last column + 1; dec, concept */
1995 if (ctl
->num_columns
<= 0)
1996 ctl
->num_columns
= NUM_COLUMNS
;
1998 ctl
->wrap_margin
= tigetflag(TERM_AUTO_RIGHT_MARGIN
);
1999 ctl
->bad_stdout
= tigetflag(TERM_CEOL
);
2000 ctl
->erase_line
= tigetstr(TERM_CLEAR_TO_LINE_END
);
2001 ctl
->clear
= tigetstr(TERM_CLEAR
);
2002 if ((ctl
->enter_std
= tigetstr(TERM_STANDARD_MODE
)) != NULL
) {
2003 ctl
->exit_std
= tigetstr(TERM_EXIT_STANDARD_MODE
);
2004 if (0 < tigetnum(TERM_STD_MODE_GLITCH
))
2005 ctl
->stdout_glitch
= 1;
2008 cursor_addr
= tigetstr(TERM_HOME
);
2009 if (cursor_addr
== NULL
|| *cursor_addr
== '\0') {
2010 cursor_addr
= tigetstr(TERM_CURSOR_ADDRESS
);
2012 cursor_addr
= tparm(cursor_addr
, 0, 0);
2015 ctl
->go_home
= xstrdup(cursor_addr
);
2017 if ((ctl
->move_line_down
= tigetstr(TERM_LINE_DOWN
)) == NULL
)
2018 ctl
->move_line_down
= BACKSPACE
;
2019 ctl
->clear_rest
= tigetstr(TERM_CLEAR_TO_SCREEN_END
);
2020 if ((ctl
->backspace_ch
= tigetstr(TERM_BACKSPACE
)) == NULL
)
2021 ctl
->backspace_ch
= BACKSPACE
;
2023 if ((ctl
->shell
= getenv("SHELL")) == NULL
)
2024 ctl
->shell
= _PATH_BSHELL
;
2027 int main(int argc
, char **argv
)
2031 struct more_control ctl
= {
2033 .fold_long_lines
= 1,
2034 .no_quit_dialog
= 1,
2035 .stop_after_formfeed
= 1,
2037 .lines_per_page
= LINES_PER_PAGE
,
2038 .num_columns
= NUM_COLUMNS
,
2039 .d_scroll_len
= SCROLL_LEN
,
2043 setlocale(LC_ALL
, "");
2044 bindtextdomain(PACKAGE
, LOCALEDIR
);
2045 textdomain(PACKAGE
);
2046 close_stdout_atexit();
2047 setlocale(LC_ALL
, "");
2049 /* Auto set no scroll on when binary is called page */
2050 if (!(strcmp(program_invocation_short_name
, "page")))
2053 ctl
.exit_on_eof
= getenv("POSIXLY_CORRECT") ? 0 : 1;
2055 if ((s
= getenv("MORE")) != NULL
)
2056 env_argscan(&ctl
, s
);
2058 argscan(&ctl
, argc
, argv
);
2060 /* clear any inherited settings */
2061 signal(SIGCHLD
, SIG_DFL
);
2066 /* exit when we cannot read user's input */
2067 ctl
.exit_on_eof
= 1;
2070 ctl
.magic
= magic_open(MAGIC_MIME_ENCODING
| MAGIC_SYMLINK
);
2071 magic_load(ctl
.magic
, NULL
);
2073 prepare_line_buffer(&ctl
);
2075 ctl
.d_scroll_len
= ctl
.lines_per_page
/ 2 - 1;
2076 if (ctl
.d_scroll_len
<= 0)
2077 ctl
.d_scroll_len
= 1;
2079 /* allow clear_line_ends only if go_home and erase_line and clear_rest strings are
2080 * defined, and in that case, make sure we are in no_scroll mode */
2081 if (ctl
.clear_line_ends
) {
2082 if ((ctl
.go_home
== NULL
) || (*ctl
.go_home
== '\0') ||
2083 (ctl
.erase_line
== NULL
) || (*ctl
.erase_line
== '\0') ||
2084 (ctl
.clear_rest
== NULL
) || (*ctl
.clear_rest
== '\0'))
2085 ctl
.clear_line_ends
= 0;
2089 if (ctl
.lines_per_screen
== 0)
2090 ctl
.lines_per_screen
= ctl
.lines_per_page
- 1;
2091 left
= ctl
.lines_per_screen
;
2092 if (ctl
.num_files
> 1)
2093 ctl
.print_banner
= 1;
2094 if (!ctl
.no_tty_in
&& ctl
.num_files
== 0) {
2095 warnx(_("bad usage"));
2096 errtryhelp(EXIT_FAILURE
);
2098 ctl
.current_file
= stdin
;
2099 if (!ctl
.no_tty_out
) {
2100 if (signal(SIGTSTP
, SIG_IGN
) == SIG_DFL
) {
2101 ctl
.catch_suspend
++;
2103 tcsetattr(STDERR_FILENO
, TCSANOW
, &ctl
.output_tty
);
2105 sigemptyset(&ctl
.sigset
);
2106 sigaddset(&ctl
.sigset
, SIGINT
);
2107 sigaddset(&ctl
.sigset
, SIGQUIT
);
2108 sigaddset(&ctl
.sigset
, SIGTSTP
);
2109 sigaddset(&ctl
.sigset
, SIGCONT
);
2110 sigaddset(&ctl
.sigset
, SIGWINCH
);
2111 sigprocmask(SIG_BLOCK
, &ctl
.sigset
, NULL
);
2112 ctl
.sigfd
= signalfd(-1, &ctl
.sigset
, SFD_CLOEXEC
);
2113 if (ctl
.no_tty_in
) {
2117 ctl
.current_file
= stdin
;
2118 display_file(&ctl
, left
);
2121 ctl
.print_banner
= 1;
2125 while (ctl
.argv_position
< ctl
.num_files
) {
2126 checkf(&ctl
, ctl
.file_names
[ctl
.argv_position
]);
2127 display_file(&ctl
, left
);
2129 ctl
.argv_position
++;
2131 ctl
.clear_line_ends
= 0;