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"
90 # define NON_INTERACTIVE_MORE 1
93 #define BACKSPACE "\b"
96 #define ARROW_UP "\x1b\x5b\x41"
97 #define ARROW_DOWN "\x1b\x5b\x42"
98 #define PAGE_UP "\x1b\x5b\x35\x7e"
99 #define PAGE_DOWN "\x1b\x5b\x36\x7e"
101 #define MIN_LINE_SZ 256 /* minimal line_buf buffer size */
103 #define SCROLL_LEN 11
104 #define LINES_PER_PAGE 24
105 #define NUM_COLUMNS 80
106 #define TERMINAL_BUF 4096
108 #define COMMAND_BUF 200
109 #define REGERR_BUF NUM_COLUMNS
111 #define TERM_AUTO_RIGHT_MARGIN "am"
112 #define TERM_BACKSPACE "cub1"
113 #define TERM_CEOL "xhp"
114 #define TERM_CLEAR "clear"
115 #define TERM_CLEAR_TO_LINE_END "el"
116 #define TERM_CLEAR_TO_SCREEN_END "ed"
117 #define TERM_COLS "cols"
118 #define TERM_CURSOR_ADDRESS "cup"
119 #define TERM_EAT_NEW_LINE "xenl"
120 #define TERM_EXIT_STANDARD_MODE "rmso"
121 #define TERM_HARD_COPY "hc"
122 #define TERM_HOME "home"
123 #define TERM_LINE_DOWN "cud1"
124 #define TERM_LINES "lines"
125 #define TERM_OVER_STRIKE "os"
126 #define TERM_STANDARD_MODE "smso"
127 #define TERM_STD_MODE_GLITCH "xmc"
129 /* Used in read_command() */
131 more_kc_unknown_command
,
133 more_kc_repeat_previous
,
135 more_kc_jump_lines_per_screen
,
136 more_kc_set_lines_per_screen
,
137 more_kc_set_scroll_len
,
139 more_kc_skip_forward
,
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 first_file
:1, /* is the input file the first in list */
210 fold_long_lines
:1, /* fold long lines */
211 hard_tabs
:1, /* print spaces instead of '\t' */
212 hard_tty
:1, /* is this hard copy terminal (a printer or such) */
213 leading_colon
:1, /* key command has leading ':' character */
214 is_paused
:1, /* is output paused */
215 no_quit_dialog
:1, /* suppress quit dialog */
216 no_scroll
:1, /* do not scroll, clear the screen and then display text */
217 no_tty_in
:1, /* is input in interactive mode */
218 no_tty_out
:1, /* is output in interactive mode */
219 print_banner
:1, /* print file name banner */
220 reading_num
:1, /* are we reading leading_number */
221 report_errors
:1, /* is an error reported */
222 search_at_start
:1, /* search pattern defined at start up */
223 search_called
:1, /* previous more command was a search */
224 squeeze_spaces
:1, /* suppress white space */
225 stdout_glitch
:1, /* terminal has standout mode glitch */
226 stop_after_formfeed
:1, /* stop after form feeds */
227 suppress_bell
:1, /* suppress bell */
228 wrap_margin
:1; /* set if automargins */
231 static void __attribute__((__noreturn__
)) usage(void)
233 printf("%s", USAGE_HEADER
);
234 printf(_(" %s [options] <file>...\n"), program_invocation_short_name
);
236 printf("%s", USAGE_SEPARATOR
);
237 printf("%s\n", _("A file perusal filter for CRT viewing."));
239 printf("%s", USAGE_OPTIONS
);
240 printf("%s\n", _(" -d, --silent display help instead of ringing bell"));
241 printf("%s\n", _(" -f, --logical count logical rather than screen lines"));
242 printf("%s\n", _(" -l, --no-pause suppress pause after form feed"));
243 printf("%s\n", _(" -c, --print-over do not scroll, display text and clean line ends"));
244 printf("%s\n", _(" -p, --clean-print do not scroll, clean screen and display text"));
245 printf("%s\n", _(" -s, --squeeze squeeze multiple blank lines into one"));
246 printf("%s\n", _(" -u, --plain suppress underlining and bold"));
247 printf("%s\n", _(" -n, --lines <number> the number of lines per screenful"));
248 printf("%s\n", _(" -<number> same as --lines"));
249 printf("%s\n", _(" +<number> display file beginning from line number"));
250 printf("%s\n", _(" +/<pattern> display file beginning from pattern match"));
251 printf("%s", USAGE_SEPARATOR
);
252 printf(USAGE_HELP_OPTIONS(23));
253 printf(USAGE_MAN_TAIL("more(1)"));
257 static void argscan(struct more_control
*ctl
, int as_argc
, char **as_argv
)
260 static const struct option longopts
[] = {
261 { "silent", no_argument
, NULL
, 'd' },
262 { "logical", no_argument
, NULL
, 'f' },
263 { "no-pause", no_argument
, NULL
, 'l' },
264 { "print-over", no_argument
, NULL
, 'c' },
265 { "clean-print", no_argument
, NULL
, 'p' },
266 { "squeeze", no_argument
, NULL
, 's' },
267 { "plain", no_argument
, NULL
, 'u' },
268 { "lines", required_argument
, NULL
, 'n' },
269 { "version", no_argument
, NULL
, 'V' },
270 { "help", no_argument
, NULL
, 'h' },
274 /* Take care of number option and +args. */
275 for (opt
= 0; opt
< as_argc
; opt
++) {
278 if (as_argv
[opt
][0] == '-' && isdigit_string(as_argv
[opt
] + 1)) {
279 ctl
->lines_per_screen
=
280 strtos16_or_err(as_argv
[opt
], _("failed to parse number"));
281 ctl
->lines_per_screen
= abs(ctl
->lines_per_screen
);
283 } else if (as_argv
[opt
][0] == '+') {
284 if (isdigit_string(as_argv
[opt
] + 1)) {
285 ctl
->next_jump
= strtos32_or_err(as_argv
[opt
],
286 _("failed to parse number")) - 1;
288 } else if (as_argv
[opt
][1] == '/') {
289 free(ctl
->next_search
);
290 ctl
->next_search
= xstrdup(as_argv
[opt
] + 2);
291 ctl
->search_at_start
= 1;
296 as_argc
= remote_entry(as_argv
, opt
, as_argc
);
301 while ((c
= getopt_long(as_argc
, as_argv
, "dflcpsun:eVh", longopts
, NULL
)) != -1) {
304 ctl
->suppress_bell
= 1;
307 ctl
->stop_after_formfeed
= 0;
310 ctl
->fold_long_lines
= 0;
316 ctl
->clear_line_ends
= 1;
319 ctl
->squeeze_spaces
= 1;
324 ctl
->lines_per_screen
= strtou16_or_err(optarg
, _("argument error"));
326 case 'e': /* ignored silently to be posix compliant */
329 print_version(EXIT_SUCCESS
);
333 errtryhelp(EXIT_FAILURE
);
337 ctl
->num_files
= as_argc
- optind
;
338 ctl
->file_names
= as_argv
+ optind
;
341 static void env_argscan(struct more_control
*ctl
, const char *s
)
346 const char delim
[] = { ' ', '\n', '\t', '\0' };
347 char *str
= xstrdup(s
);
348 char *key
= NULL
, *tok
;
350 env_argv
= xmalloc(sizeof(char *) * size
);
351 env_argv
[0] = _("MORE environment variable"); /* program name */
352 for (tok
= strtok_r(str
, delim
, &key
); tok
; tok
= strtok_r(NULL
, delim
, &key
)) {
353 env_argv
[env_argc
++] = tok
;
354 if (size
< env_argc
) {
356 env_argv
= xrealloc(env_argv
, sizeof(char *) * size
);
360 argscan(ctl
, env_argc
, env_argv
);
361 /* Reset optind, command line parsing needs this. */
367 static void more_fseek(struct more_control
*ctl
, off_t pos
)
369 ctl
->file_position
= pos
;
370 fseeko(ctl
->current_file
, pos
, SEEK_SET
);
373 static int more_getc(struct more_control
*ctl
)
375 int ret
= getc(ctl
->current_file
);
376 ctl
->file_position
= ftello(ctl
->current_file
);
380 static int more_ungetc(struct more_control
*ctl
, int c
)
382 int ret
= ungetc(c
, ctl
->current_file
);
383 ctl
->file_position
= ftello(ctl
->current_file
);
387 static void print_separator(const int c
, int n
)
394 /* check_magic -- check for file magic numbers. */
395 static int check_magic(struct more_control
*ctl
, char *fs
)
398 const int fd
= fileno(ctl
->current_file
);
399 const char *mime_encoding
= magic_descriptor(ctl
->magic
, fd
);
400 const char *magic_error_msg
= magic_error(ctl
->magic
);
403 if (magic_error_msg
) {
404 printf(_("magic failed: %s\n"), magic_error_msg
);
407 if (fstat(fd
, &st
)) {
408 warn(_("cannot stat %s"), fs
);
411 if (st
.st_size
== 0) {
412 /* libmagic tells an empty file has binary encoding */
416 if (!mime_encoding
|| !(strcmp("binary", mime_encoding
))) {
417 printf(_("\n******** %s: Not a text file ********\n\n"), fs
);
421 signed char twobytes
[2];
423 /* don't try to look ahead if the input is unseekable */
424 if (fseek(ctl
->current_file
, 0L, SEEK_SET
))
427 if (fread(twobytes
, 2, 1, ctl
->current_file
) == 1) {
428 switch (twobytes
[0] + (twobytes
[1] << 8)) {
429 case 0407: /* a.out obj */
430 case 0410: /* a.out exec */
431 case 0413: /* a.out demand exec */
435 case 0x457f: /* simple ELF detection */
436 printf(_("\n******** %s: Not a text file ********\n\n"),
441 fseek(ctl
->current_file
, 0L, SEEK_SET
); /* rewind() not necessary */
446 /* Check whether the file named by fs is an ASCII file which the user may
447 * access. If it is, return the opened file. Otherwise return NULL. */
448 static void checkf(struct more_control
*ctl
, char *fs
)
453 ctl
->current_line
= 0;
454 ctl
->file_position
= 0;
456 if (((ctl
->current_file
= fopen(fs
, "r")) == NULL
) ||
457 (fstat(fileno(ctl
->current_file
), &st
) != 0)) {
458 if (ctl
->clear_line_ends
)
459 putp(ctl
->erase_line
);
460 warn(_("cannot open %s"), fs
);
464 if ((st
.st_mode
& S_IFMT
) == S_IFDIR
) {
465 printf(_("\n*** %s: directory ***\n\n"), fs
);
466 ctl
->current_file
= NULL
;
470 if (check_magic(ctl
, fs
)) {
471 fclose(ctl
->current_file
);
472 ctl
->current_file
= NULL
;
475 fcntl(fileno(ctl
->current_file
), F_SETFD
, FD_CLOEXEC
);
477 ctl
->clear_first
= (c
== '\f');
479 if ((ctl
->file_size
= st
.st_size
) == 0)
480 ctl
->file_size
= ~((off_t
)0);
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
) {
735 (int)((ctl
->file_position
* 100) / ctl
->file_size
));
737 if (ctl
->suppress_bell
) {
739 printf(_("[Press space to continue, 'q' to quit.]"));
743 if (ctl
->clear_line_ends
)
744 putp(ctl
->clear_rest
);
746 fprintf(stderr
, "\a");
750 static void reset_tty(struct more_control
*ctl
)
755 ctl
->output_tty
.c_lflag
|= ICANON
| ECHO
;
756 ctl
->output_tty
.c_cc
[VMIN
] = ctl
->original_tty
.c_cc
[VMIN
];
757 ctl
->output_tty
.c_cc
[VTIME
] = ctl
->original_tty
.c_cc
[VTIME
];
758 tcsetattr(STDERR_FILENO
, TCSANOW
, &ctl
->original_tty
);
761 /* Clean up terminal state and exit. Also come here if interrupt signal received */
762 static void __attribute__((__noreturn__
)) more_exit(struct more_control
*ctl
)
765 magic_close(ctl
->magic
);
768 if (ctl
->clear_line_ends
) {
770 putp(ctl
->erase_line
);
771 } else if (!ctl
->clear_line_ends
&& (ctl
->prompt_len
> 0))
772 erase_to_col(ctl
, 0);
774 free(ctl
->previous_search
);
775 free(ctl
->shell_line
);
778 if (ctl
->current_file
)
779 fclose(ctl
->current_file
);
780 del_curterm(cur_term
);
784 static cc_t
read_user_input(struct more_control
*ctl
)
790 * Key commands can be read() from either stderr or stdin. If they
791 * are read from stdin such as 'cat file.txt | more' then the pipe
792 * input is understood as series key commands - and that is not
793 * wanted. Keep the read() reading from stderr.
795 if (read(STDERR_FILENO
, &c
, 1) <= 0) {
799 c
= ctl
->output_tty
.c_cc
[VKILL
];
804 /* Read a number and command from the terminal. Set cmd to the non-digit
805 * which terminates the number. */
806 static struct number_command
read_command(struct more_control
*ctl
)
808 cc_t input
[8] = { 0 };
810 struct number_command cmd
= { .key
= more_kc_unknown_command
};
812 /* See stderr note in read_user_input() */
813 if ((ilen
= read(STDERR_FILENO
, &input
, sizeof(input
))) <= 0)
816 if (!memcmp(input
, ARROW_UP
, sizeof(ARROW_UP
))) {
817 cmd
.key
= more_kc_backwards
;
819 } else if (!memcmp(input
, ARROW_DOWN
, sizeof(ARROW_DOWN
))) {
820 cmd
.key
= more_kc_skip_forward
;
822 } else if (!memcmp(input
, PAGE_UP
, sizeof(PAGE_UP
))) {
823 cmd
.key
= more_kc_backwards
;
825 } else if (!memcmp(input
, PAGE_DOWN
, sizeof(PAGE_DOWN
))) {
826 cmd
.key
= more_kc_skip_forward
;
830 for (i
= 0; i
< ilen
; i
++) {
831 if (isdigit(input
[i
])) {
832 if (0 < ctl
->reading_num
) {
833 ctl
->leading_number
*= 10;
834 ctl
->leading_number
+= input
[i
] - '0';
836 ctl
->leading_number
= input
[i
] - '0';
837 ctl
->reading_num
= 1;
840 cmd
.number
= ctl
->leading_number
;
841 ctl
->reading_num
= 0;
842 ctl
->leading_number
= 0;
843 if (ctl
->leading_colon
) {
844 ctl
->leading_colon
= 0;
847 cmd
.key
= more_kc_display_file_and_line
;
850 cmd
.key
= more_kc_next_file
;
853 cmd
.key
= more_kc_previous_file
;
856 cmd
.key
= more_kc_unknown_command
;
860 /* command is a single char */
863 cmd
.key
= more_kc_repeat_previous
;
866 ctl
->leading_colon
= 1;
870 cmd
.key
= more_kc_backwards
;
873 cmd
.key
= more_kc_jump_lines_per_screen
;
876 cmd
.key
= more_kc_set_lines_per_screen
;
880 cmd
.key
= more_kc_set_scroll_len
;
884 cmd
.key
= more_kc_quit
;
889 cmd
.key
= more_kc_skip_forward
;
892 cmd
.key
= more_kc_next_line
;
895 cmd
.key
= more_kc_clear_screen
;
898 cmd
.key
= more_kc_previous_search_match
;
901 cmd
.key
= more_kc_display_line
;
904 cmd
.key
= more_kc_repeat_search
;
907 cmd
.key
= more_kc_search
;
910 cmd
.key
= more_kc_run_shell
;
914 cmd
.key
= more_kc_help
;
917 cmd
.key
= more_kc_run_editor
;
924 /* Change displayed file from command line list to next nskip, where nskip
925 * is relative position in argv and can be negative, that is a previous
927 static void change_file(struct more_control
*ctl
, int nskip
)
932 if (ctl
->argv_position
+ nskip
> ctl
->num_files
- 1)
933 nskip
= ctl
->num_files
- ctl
->argv_position
- 1;
935 ctl
->argv_position
+= nskip
;
936 if (ctl
->argv_position
< 0)
937 ctl
->argv_position
= 0;
938 puts(_("\n...Skipping "));
939 if (ctl
->clear_line_ends
)
940 putp(ctl
->erase_line
);
942 fputs(_("...Skipping to file "), stdout
);
944 fputs(_("...Skipping back to file "), stdout
);
945 puts(ctl
->file_names
[ctl
->argv_position
]);
946 if (ctl
->clear_line_ends
)
947 putp(ctl
->erase_line
);
949 ctl
->argv_position
--;
952 static void show(struct more_control
*ctl
, char c
)
954 if ((c
< ' ' && c
!= '\n' && c
!= ESC
) || c
== CERASE
) {
955 c
+= (c
== CERASE
) ? -0100 : 0100;
956 fputs(CARAT
, stderr
);
963 static void more_error(struct more_control
*ctl
, char *mess
)
965 if (ctl
->clear_line_ends
)
966 putp(ctl
->erase_line
);
968 erase_to_col(ctl
, 0);
969 ctl
->prompt_len
+= strlen(mess
);
971 putp(ctl
->enter_std
);
976 ctl
->report_errors
++;
979 static void erase_one_column(struct more_control
*ctl
)
981 if (ctl
->erase_previous_ok
)
982 fprintf(stderr
, "%s ", ctl
->backspace_ch
);
983 fputs(ctl
->backspace_ch
, stderr
);
986 static void ttyin(struct more_control
*ctl
, char buf
[], int nmax
, char pchar
)
995 while (sp
- buf
< nmax
) {
996 if (ctl
->prompt_len
> maxlen
)
997 maxlen
= ctl
->prompt_len
;
998 c
= read_user_input(ctl
);
1001 } else if (c
== ctl
->output_tty
.c_cc
[VERASE
] && !slash
) {
1003 #ifdef HAVE_WIDECHAR
1004 if (MB_CUR_MAX
> 1) {
1006 size_t pos
= 0, mblength
;
1007 mbstate_t state
, state_bak
;
1009 memset(&state
, '\0', sizeof(mbstate_t));
1014 mbrtowc(&wc
, buf
+ pos
,
1025 if (buf
+ pos
+ mblength
>= sp
)
1031 if (mblength
== 1) {
1032 erase_one_column(ctl
);
1035 wc_width
= wcwidth(wc
);
1039 while (wc_width
--) {
1040 erase_one_column(ctl
);
1044 while (mblength
--) {
1049 #endif /* HAVE_WIDECHAR */
1052 erase_one_column(ctl
);
1056 if ((*sp
< ' ' && *sp
!= '\n') || *sp
== CERASE
) {
1058 erase_one_column(ctl
);
1062 if (!ctl
->erase_line
)
1063 ctl
->prompt_len
= maxlen
;
1064 } else if (c
== ctl
->output_tty
.c_cc
[VKILL
] && !slash
) {
1065 if (ctl
->hard_tty
) {
1072 if (ctl
->erase_line
)
1073 erase_to_col(ctl
, 1);
1074 else if (ctl
->erase_input_ok
)
1075 while (ctl
->prompt_len
-- > 1)
1076 fprintf(stderr
, "%s %s", ctl
->backspace_ch
, ctl
->backspace_ch
);
1077 ctl
->prompt_len
= 1;
1083 if (slash
&& (c
== ctl
->output_tty
.c_cc
[VKILL
] ||
1084 c
== ctl
->output_tty
.c_cc
[VERASE
])) {
1085 erase_one_column(ctl
);
1091 if ((c
< ' ' && c
!= '\n' && c
!= ESC
) || c
== CERASE
) {
1092 c
+= (c
== CERASE
) ? -0100 : 0100;
1093 fputs(CARAT
, stderr
);
1096 if (c
!= '\n' && c
!= ESC
) {
1103 if (!ctl
->erase_line
)
1104 ctl
->prompt_len
= maxlen
;
1105 if (sp
- buf
>= nmax
- 1)
1106 more_error(ctl
, _("Line too long"));
1109 /* Expand shell command line. */
1110 static void expand(struct more_control
*ctl
, char *inbuf
)
1116 int tempsz
, xtra
, offset
;
1118 xtra
= strlen(ctl
->file_names
[ctl
->argv_position
]) + strlen(ctl
->shell_line
) + 1;
1119 tempsz
= COMMAND_BUF
+ xtra
;
1120 temp
= xmalloc(tempsz
);
1123 while ((c
= *inpstr
++) != '\0') {
1124 offset
= outstr
- temp
;
1125 if (tempsz
- offset
- 1 < xtra
) {
1126 tempsz
+= COMMAND_BUF
+ xtra
;
1127 temp
= xrealloc(temp
, tempsz
);
1128 outstr
= temp
+ offset
;
1132 if (!ctl
->no_tty_in
) {
1133 strcpy(outstr
, ctl
->file_names
[ctl
->argv_position
]);
1134 outstr
+= strlen(ctl
->file_names
[ctl
->argv_position
]);
1139 if (ctl
->shell_line
) {
1140 strcpy(outstr
, ctl
->shell_line
);
1141 outstr
+= strlen(ctl
->shell_line
);
1144 ("No previous command to substitute for"));
1147 if (*inpstr
== '%' || *inpstr
== '!') {
1148 *outstr
++ = *inpstr
++;
1157 free(ctl
->shell_line
);
1158 ctl
->shell_line
= temp
;
1161 static void set_tty(struct more_control
*ctl
)
1163 ctl
->output_tty
.c_lflag
&= ~(ICANON
| ECHO
);
1164 ctl
->output_tty
.c_cc
[VMIN
] = 1; /* read at least 1 char */
1165 ctl
->output_tty
.c_cc
[VTIME
] = 0; /* no timeout */
1166 tcsetattr(STDERR_FILENO
, TCSANOW
, &ctl
->output_tty
);
1169 /* Come here if a quit signal is received */
1170 static void sigquit_handler(struct more_control
*ctl
)
1172 if (!ctl
->dumb_tty
&& ctl
->no_quit_dialog
) {
1173 ctl
->prompt_len
+= fprintf(stderr
, _("[Use q or Q to quit]"));
1174 ctl
->no_quit_dialog
= 0;
1179 /* Come here when we get a suspend signal from the terminal */
1180 static void sigtstp_handler(struct more_control
*ctl
)
1184 kill(getpid(), SIGSTOP
);
1187 /* Come here when we get a continue signal from the terminal */
1188 static void sigcont_handler(struct more_control
*ctl
)
1193 /* Come here if a signal for a window size change is received */
1194 static void sigwinch_handler(struct more_control
*ctl
)
1198 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) != -1) {
1199 if (win
.ws_row
!= 0) {
1200 ctl
->lines_per_page
= win
.ws_row
;
1201 ctl
->d_scroll_len
= ctl
->lines_per_page
/ 2 - 1;
1202 if (ctl
->d_scroll_len
< 1)
1203 ctl
->d_scroll_len
= 1;
1204 ctl
->lines_per_screen
= ctl
->lines_per_page
- 1;
1206 if (win
.ws_col
!= 0)
1207 ctl
->num_columns
= win
.ws_col
;
1209 prepare_line_buffer(ctl
);
1212 static void execute(struct more_control
*ctl
, char *filename
, char *cmd
, ...)
1224 if (!isatty(STDIN_FILENO
)) {
1225 close(STDIN_FILENO
);
1226 open("/dev/tty", 0);
1230 va_start(argp
, cmd
);
1231 arg
= va_arg(argp
, char *);
1235 arg
= va_arg(argp
, char *);
1239 args
= alloca(sizeof(char *) * (argcount
+ 1));
1240 args
[argcount
] = NULL
;
1242 va_start(argp
, cmd
);
1243 arg
= va_arg(argp
, char *);
1246 args
[argcount
] = arg
;
1248 arg
= va_arg(argp
, char *);
1252 if (geteuid() != getuid() || getegid() != getgid()) {
1253 if (setuid(getuid()) < 0)
1254 err(EXIT_FAILURE
, _("setuid failed"));
1255 if (setgid(getgid()) < 0)
1256 err(EXIT_FAILURE
, _("setgid failed"));
1261 fputs(_("exec failed\n"), stderr
);
1262 exit(errsv
== ENOENT
? EX_EXEC_ENOENT
: EX_EXEC_FAILED
);
1266 while (wait(NULL
) > 0) {
1271 fputs(_("can't fork\n"), stderr
);
1273 print_separator('-', 24);
1274 output_prompt(ctl
, filename
);
1277 static void run_shell(struct more_control
*ctl
, char *filename
)
1279 char cmdbuf
[COMMAND_BUF
];
1281 erase_to_col(ctl
, 0);
1284 if (ctl
->previous_command
.key
== more_kc_run_shell
&& ctl
->shell_line
)
1285 fputs(ctl
->shell_line
, stdout
);
1287 ttyin(ctl
, cmdbuf
, sizeof(cmdbuf
) - 2, '!');
1288 if (strpbrk(cmdbuf
, "%!\\"))
1289 expand(ctl
, cmdbuf
);
1291 free(ctl
->shell_line
);
1292 ctl
->shell_line
= xstrdup(cmdbuf
);
1295 fputc('\n', stderr
);
1297 ctl
->prompt_len
= 0;
1298 execute(ctl
, filename
, ctl
->shell
, ctl
->shell
, "-c", ctl
->shell_line
, 0);
1301 /* Skip n lines in the file f */
1302 static void skip_lines(struct more_control
*ctl
)
1306 while (ctl
->next_jump
> 0) {
1307 while ((c
= more_getc(ctl
)) != '\n')
1311 ctl
->current_line
++;
1315 /* Clear the screen */
1316 static void more_clear_screen(struct more_control
*ctl
)
1318 if (ctl
->clear
&& !ctl
->hard_tty
) {
1320 /* Put out carriage return so that system doesn't get
1321 * confused by escape sequences when expanding tabs */
1323 ctl
->prompt_len
= 0;
1327 static void read_line(struct more_control
*ctl
)
1333 while ((c
= more_getc(ctl
)) != '\n' && c
!= EOF
1334 && (ptrdiff_t)p
!= (ptrdiff_t)(ctl
->line_buf
+ ctl
->line_sz
- 1))
1337 ctl
->current_line
++;
1341 static int more_poll(struct more_control
*ctl
, int timeout
)
1343 struct pollfd pfd
[2];
1345 pfd
[0].fd
= ctl
->sigfd
;
1346 pfd
[0].events
= POLLIN
| POLLERR
| POLLHUP
;
1347 pfd
[1].fd
= STDIN_FILENO
;
1348 pfd
[1].events
= POLLIN
;
1350 if (poll(pfd
, 2, timeout
) < 0) {
1351 if (errno
== EAGAIN
)
1353 more_error(ctl
, _("poll failed"));
1356 if (pfd
[0].revents
!= 0) {
1357 struct signalfd_siginfo info
;
1360 sz
= read(pfd
[0].fd
, &info
, sizeof(info
));
1361 assert(sz
== sizeof(info
));
1362 switch (info
.ssi_signo
) {
1367 sigquit_handler(ctl
);
1370 sigtstp_handler(ctl
);
1373 sigcont_handler(ctl
);
1376 sigwinch_handler(ctl
);
1382 if (pfd
[1].revents
== 0)
1387 /* Search for nth occurrence of regular expression contained in buf in
1389 static void search(struct more_control
*ctl
, char buf
[], int n
)
1391 off_t startline
= ctl
->file_position
;
1392 off_t line1
= startline
;
1393 off_t line2
= startline
;
1399 if (buf
!= ctl
->previous_search
) {
1400 free(ctl
->previous_search
);
1401 ctl
->previous_search
= buf
;
1404 ctl
->search_called
= 1;
1405 ctl
->context
.line_num
= saveln
= ctl
->current_line
;
1406 ctl
->context
.row_num
= startline
;
1410 if ((rc
= regcomp(&re
, buf
, REG_NOSUB
)) != 0) {
1412 regerror(rc
, &re
, s
, sizeof s
);
1416 while (!feof(ctl
->current_file
)) {
1419 line1
= ctl
->file_position
;
1422 if (regexec(&re
, ctl
->line_buf
, 0, NULL
, 0) == 0 && --n
== 0) {
1423 if ((1 < lncount
&& ctl
->no_tty_in
) || 3 < lncount
) {
1425 if (ctl
->clear_line_ends
)
1426 putp(ctl
->erase_line
);
1427 fputs(_("...skipping\n"), stdout
);
1429 if (!ctl
->no_tty_in
) {
1430 ctl
->current_line
-= (lncount
< 3 ? lncount
: 3);
1431 more_fseek(ctl
, line3
);
1432 if (ctl
->no_scroll
) {
1433 if (ctl
->clear_line_ends
) {
1435 putp(ctl
->erase_line
);
1437 more_clear_screen(ctl
);
1440 erase_to_col(ctl
, 0);
1441 if (ctl
->no_scroll
) {
1442 if (ctl
->clear_line_ends
) {
1444 putp(ctl
->erase_line
);
1446 more_clear_screen(ctl
);
1448 puts(ctl
->line_buf
);
1454 /* Move ctrl+c signal handling back to more_key_command(). */
1455 signal(SIGINT
, SIG_DFL
);
1456 sigaddset(&ctl
->sigset
, SIGINT
);
1457 sigprocmask(SIG_BLOCK
, &ctl
->sigset
, NULL
);
1459 if (feof(ctl
->current_file
)) {
1460 if (!ctl
->no_tty_in
) {
1461 ctl
->current_line
= saveln
;
1462 more_fseek(ctl
, startline
);
1464 fputs(_("\nPattern not found\n"), stdout
);
1468 more_error(ctl
, _("Pattern not found"));
1472 static char *find_editor(void)
1474 static char *editor
;
1476 editor
= getenv("VISUAL");
1477 if (editor
== NULL
|| *editor
== '\0')
1478 editor
= getenv("EDITOR");
1479 if (editor
== NULL
|| *editor
== '\0')
1484 static void runtime_usage(void)
1486 fputs(_("Most commands optionally preceded by integer argument k. "
1487 "Defaults in brackets.\n"
1488 "Star (*) indicates argument becomes new default.\n"), stdout
);
1489 print_separator('-', 79);
1492 ("<space> Display next k lines of text [current screen size]\n"
1493 "z Display next k lines of text [current screen size]*\n"
1494 "<return> Display next k lines of text [1]*\n"
1495 "d or ctrl-D Scroll k lines [current scroll size, initially 11]*\n"
1496 "q or Q or <interrupt> Exit from more\n"
1497 "s Skip forward k lines of text [1]\n"
1498 "f Skip forward k screenfuls of text [1]\n"
1499 "b or ctrl-B Skip backwards k screenfuls of text [1]\n"
1500 "' Go to place where previous search started\n"
1501 "= Display current line number\n"
1502 "/<regular expression> Search for kth occurrence of regular expression [1]\n"
1503 "n Search for kth occurrence of last r.e [1]\n"
1504 "!<cmd> or :!<cmd> Execute <cmd> in a subshell\n"
1505 "v Start up '%s' at current line\n"
1506 "ctrl-L Redraw screen\n"
1507 ":n Go to kth next file [1]\n"
1508 ":p Go to kth previous file [1]\n"
1509 ":f Display current file name and line number\n"
1510 ". Repeat previous command\n"),
1512 print_separator('-', 79);
1515 static void execute_editor(struct more_control
*ctl
, char *cmdbuf
, char *filename
)
1521 if ((ctl
->current_line
- ctl
->lines_per_screen
) < 1)
1524 n
= ctl
->current_line
- (ctl
->lines_per_screen
+ 1) / 2;
1525 editor
= find_editor();
1526 p
= strrchr(editor
, '/');
1532 * Earlier: call vi +n file. This also works for emacs.
1533 * POSIX: call vi -c n file (when editor is vi or ex).
1535 if (!strcmp(p
, "vi") || !strcmp(p
, "ex")) {
1536 sprintf(cmdbuf
, "-c %d", n
);
1539 sprintf(cmdbuf
, "+%d", n
);
1541 erase_to_col(ctl
, 0);
1542 printf("%s %s %s", editor
, cmdbuf
, ctl
->file_names
[ctl
->argv_position
]);
1545 execute(ctl
, filename
, editor
, editor
,
1547 ctl
->file_names
[ctl
->argv_position
], (char *)0);
1549 execute(ctl
, filename
, editor
, editor
,
1550 cmdbuf
, ctl
->file_names
[ctl
->argv_position
], (char *)0);
1553 static int skip_backwards(struct more_control
*ctl
, int nlines
)
1557 erase_to_col(ctl
, 0);
1558 printf(P_("...back %d page", "...back %d pages", nlines
), nlines
);
1560 ctl
->next_jump
= ctl
->current_line
- (ctl
->lines_per_screen
* (nlines
+ 1)) - 1;
1561 if (ctl
->next_jump
< 0)
1564 ctl
->current_line
= 0;
1566 return ctl
->lines_per_screen
;
1569 static int skip_forwards(struct more_control
*ctl
, int nlines
, cc_t comchar
)
1576 nlines
*= ctl
->lines_per_screen
;
1578 erase_to_col(ctl
, 0);
1580 if (ctl
->clear_line_ends
)
1581 putp(ctl
->erase_line
);
1582 printf(P_("...skipping %d line",
1583 "...skipping %d lines", nlines
), nlines
);
1585 if (ctl
->clear_line_ends
)
1586 putp(ctl
->erase_line
);
1589 while (nlines
> 0) {
1590 while ((c
= more_getc(ctl
)) != '\n')
1593 ctl
->current_line
++;
1599 /* Read a command and do it. A command consists of an optional integer
1600 * argument followed by the command character. Return the number of
1601 * lines to display in the next screenful. If there is nothing more to
1602 * display in the current file, zero is returned. */
1603 static int more_key_command(struct more_control
*ctl
, char *filename
)
1606 int done
= 0, search_again
= 0;
1607 char cmdbuf
[INIT_BUF
];
1608 struct number_command cmd
;
1610 if (!ctl
->report_errors
)
1611 output_prompt(ctl
, filename
);
1613 ctl
->report_errors
= 0;
1614 ctl
->search_called
= 0;
1616 if (more_poll(ctl
, -1) != 0)
1618 cmd
= read_command(ctl
);
1619 if (cmd
.key
== more_kc_unknown_command
)
1621 if (cmd
.key
== more_kc_repeat_previous
)
1622 cmd
= ctl
->previous_command
;
1624 case more_kc_backwards
:
1625 if (ctl
->no_tty_in
) {
1626 fprintf(stderr
, "\a");
1629 retval
= skip_backwards(ctl
, cmd
.number
);
1632 case more_kc_jump_lines_per_screen
:
1633 case more_kc_set_lines_per_screen
:
1634 if (cmd
.number
== 0)
1635 cmd
.number
= ctl
->lines_per_screen
;
1636 else if (cmd
.key
== more_kc_set_lines_per_screen
)
1637 ctl
->lines_per_screen
= cmd
.number
;
1638 retval
= cmd
.number
;
1641 case more_kc_set_scroll_len
:
1642 if (cmd
.number
!= 0)
1643 ctl
->d_scroll_len
= cmd
.number
;
1644 retval
= ctl
->d_scroll_len
;
1649 case more_kc_skip_forward
:
1650 if (skip_forwards(ctl
, cmd
.number
, cmd
.number
))
1651 retval
= ctl
->lines_per_screen
;
1654 case more_kc_next_line
:
1655 if (cmd
.number
!= 0)
1656 ctl
->lines_per_screen
= cmd
.number
;
1659 retval
= cmd
.number
;
1662 case more_kc_clear_screen
:
1663 if (!ctl
->no_tty_in
) {
1664 more_clear_screen(ctl
);
1665 more_fseek(ctl
, ctl
->screen_start
.row_num
);
1666 ctl
->current_line
= ctl
->screen_start
.line_num
;
1667 retval
= ctl
->lines_per_screen
;
1671 fprintf(stderr
, "\a");
1674 case more_kc_previous_search_match
:
1675 if (!ctl
->no_tty_in
) {
1676 erase_to_col(ctl
, 0);
1677 fputs(_("\n***Back***\n\n"), stdout
);
1678 more_fseek(ctl
, ctl
->context
.row_num
);
1679 ctl
->current_line
= ctl
->context
.line_num
;
1680 retval
= ctl
->lines_per_screen
;
1684 fprintf(stderr
, "\a");
1687 case more_kc_display_line
:
1688 erase_to_col(ctl
, 0);
1689 ctl
->prompt_len
= printf("%d", ctl
->current_line
);
1692 case more_kc_display_file_and_line
:
1693 erase_to_col(ctl
, 0);
1694 if (!ctl
->no_tty_in
)
1696 printf(_("\"%s\" line %d"),
1697 ctl
->file_names
[ctl
->argv_position
], ctl
->current_line
);
1699 ctl
->prompt_len
= printf(_("[Not a file] line %d"),
1703 case more_kc_repeat_search
:
1704 if (!ctl
->previous_search
) {
1705 more_error(ctl
, _("No previous regular expression"));
1710 case more_kc_search
:
1711 if (cmd
.number
== 0)
1713 erase_to_col(ctl
, 0);
1715 ctl
->prompt_len
= 1;
1718 fputc('\r', stderr
);
1719 search(ctl
, ctl
->previous_search
, cmd
.number
);
1722 ttyin(ctl
, cmdbuf
, sizeof(cmdbuf
) - 2, '/');
1723 fputc('\r', stderr
);
1724 ctl
->next_search
= xstrdup(cmdbuf
);
1725 search(ctl
, ctl
->next_search
, cmd
.number
);
1727 retval
= ctl
->lines_per_screen
- 1;
1730 case more_kc_run_shell
:
1731 run_shell(ctl
, filename
);
1735 more_clear_screen(ctl
);
1736 erase_to_col(ctl
, 0);
1738 output_prompt(ctl
, filename
);
1740 case more_kc_next_file
:
1742 erase_to_col(ctl
, 0);
1743 if (cmd
.number
== 0)
1745 if (ctl
->argv_position
+ cmd
.number
>= (unsigned int)ctl
->num_files
)
1747 change_file(ctl
, cmd
.number
);
1750 case more_kc_previous_file
:
1751 if (ctl
->no_tty_in
) {
1752 fprintf(stderr
, "\a");
1756 erase_to_col(ctl
, 0);
1757 if (cmd
.number
== 0)
1759 change_file(ctl
, -cmd
.number
);
1762 case more_kc_run_editor
: /* This case should go right before default */
1763 if (!ctl
->no_tty_in
) {
1764 execute_editor(ctl
, cmdbuf
, filename
);
1769 if (ctl
->suppress_bell
) {
1770 erase_to_col(ctl
, 0);
1772 putp(ctl
->enter_std
);
1774 printf(_("[Press 'h' for instructions.]"))
1775 + 2 * ctl
->stdout_glitch
;
1777 putp(ctl
->exit_std
);
1779 fprintf(stderr
, "\a");
1783 ctl
->previous_command
= cmd
;
1785 cmd
.key
= more_kc_unknown_command
;
1790 ctl
->no_quit_dialog
= 1;
1794 /* Print out the contents of the file f, one screenful at a time. */
1795 static void screen(struct more_control
*ctl
, int num_lines
)
1799 int length
; /* length of current line */
1800 static int prev_len
= 1; /* length of previous line */
1803 while (num_lines
> 0 && !ctl
->is_paused
) {
1804 if ((nchars
= get_line(ctl
, &length
)) == EOF
) {
1805 if (ctl
->clear_line_ends
)
1806 putp(ctl
->clear_rest
);
1809 if (ctl
->squeeze_spaces
&& length
== 0 && prev_len
== 0)
1813 || ((ctl
->enter_std
&& *ctl
->enter_std
== ' ') && (ctl
->prompt_len
> 0)))
1814 erase_to_col(ctl
, 0);
1815 /* must clear before drawing line since tabs on
1816 * some terminals do not erase what they tab
1818 if (ctl
->clear_line_ends
)
1819 putp(ctl
->erase_line
);
1820 fwrite(ctl
->line_buf
, length
, 1, stdout
);
1821 if (nchars
< ctl
->prompt_len
)
1822 erase_to_col(ctl
, nchars
);
1823 ctl
->prompt_len
= 0;
1824 if (nchars
< ctl
->num_columns
|| !ctl
->fold_long_lines
)
1829 if ((c
= more_getc(ctl
)) == EOF
) {
1830 if (ctl
->clear_line_ends
)
1831 putp(ctl
->clear_rest
);
1835 if (ctl
->is_paused
&& ctl
->clear_line_ends
)
1836 putp(ctl
->clear_rest
);
1837 more_ungetc(ctl
, c
);
1840 if ((num_lines
= more_key_command(ctl
, NULL
)) == 0)
1842 } while (ctl
->search_called
&& !ctl
->previous_search
);
1843 if (ctl
->hard_tty
&& ctl
->prompt_len
> 0)
1844 erase_to_col(ctl
, 0);
1845 if (ctl
->no_scroll
&& num_lines
>= ctl
->lines_per_screen
) {
1846 if (ctl
->clear_line_ends
)
1849 more_clear_screen(ctl
);
1851 ctl
->screen_start
.line_num
= ctl
->current_line
;
1852 ctl
->screen_start
.row_num
= ctl
->file_position
;
1856 static void copy_file(FILE *f
)
1861 while ((sz
= fread(&buf
, sizeof(char), sizeof(buf
), f
)) > 0)
1862 fwrite(&buf
, sizeof(char), sz
, stdout
);
1866 static void display_file(struct more_control
*ctl
, int left
)
1868 if (!ctl
->current_file
)
1870 ctl
->context
.line_num
= ctl
->context
.row_num
= 0;
1871 ctl
->current_line
= 0;
1872 if (ctl
->first_file
) {
1873 ctl
->first_file
= 0;
1876 if (ctl
->search_at_start
) {
1877 search(ctl
, ctl
->next_search
, 1);
1881 } else if (ctl
->argv_position
< ctl
->num_files
&& !ctl
->no_tty_out
)
1883 more_key_command(ctl
, ctl
->file_names
[ctl
->argv_position
]);
1885 if ((ctl
->no_scroll
|| ctl
->clear_first
)
1886 && ctl
->file_size
!= ~((off_t
)0)) {
1887 if (ctl
->clear_line_ends
)
1890 more_clear_screen(ctl
);
1892 if (ctl
->print_banner
) {
1893 if (ctl
->bad_stdout
)
1894 erase_to_col(ctl
, 0);
1895 if (ctl
->clear_line_ends
)
1896 putp(ctl
->erase_line
);
1897 if (ctl
->prompt_len
> 14)
1898 erase_to_col(ctl
, 14);
1899 if (ctl
->clear_line_ends
)
1900 putp(ctl
->erase_line
);
1901 print_separator(':', 14);
1902 puts(ctl
->file_names
[ctl
->argv_position
]);
1903 if (ctl
->clear_line_ends
)
1904 putp(ctl
->erase_line
);
1905 print_separator(':', 14);
1906 if (left
> ctl
->lines_per_page
- 4)
1907 left
= ctl
->lines_per_page
- 4;
1909 if (ctl
->no_tty_out
)
1910 copy_file(ctl
->current_file
);
1915 fclose(ctl
->current_file
);
1916 ctl
->current_file
= NULL
;
1917 ctl
->screen_start
.line_num
= ctl
->screen_start
.row_num
= 0;
1918 ctl
->context
.line_num
= ctl
->context
.row_num
= 0L;
1921 static void initterm(struct more_control
*ctl
)
1928 #ifndef NON_INTERACTIVE_MORE
1929 ctl
->no_tty_out
= tcgetattr(STDOUT_FILENO
, &ctl
->output_tty
);
1931 ctl
->no_tty_in
= tcgetattr(STDIN_FILENO
, &ctl
->output_tty
);
1932 tcgetattr(STDERR_FILENO
, &ctl
->output_tty
);
1933 ctl
->original_tty
= ctl
->output_tty
;
1934 ctl
->hard_tabs
= (ctl
->output_tty
.c_oflag
& TABDLY
) != TAB3
;
1935 if (ctl
->no_tty_out
)
1938 ctl
->output_tty
.c_lflag
&= ~(ICANON
| ECHO
);
1939 ctl
->output_tty
.c_cc
[VMIN
] = 1;
1940 ctl
->output_tty
.c_cc
[VTIME
] = 0;
1941 ctl
->erase_previous_ok
= (ctl
->output_tty
.c_cc
[VERASE
] != 255);
1942 ctl
->erase_input_ok
= (ctl
->output_tty
.c_cc
[VKILL
] != 255);
1943 if ((term
= getenv("TERM")) == NULL
) {
1946 setupterm(term
, 1, &ret
);
1951 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0) {
1952 ctl
->lines_per_page
= tigetnum(TERM_LINES
);
1953 ctl
->num_columns
= tigetnum(TERM_COLS
);
1955 if ((ctl
->lines_per_page
= win
.ws_row
) == 0)
1956 ctl
->lines_per_page
= tigetnum(TERM_LINES
);
1957 if ((ctl
->num_columns
= win
.ws_col
) == 0)
1958 ctl
->num_columns
= tigetnum(TERM_COLS
);
1960 if ((ctl
->lines_per_page
<= 0) || tigetflag(TERM_HARD_COPY
)) {
1962 ctl
->lines_per_page
= LINES_PER_PAGE
;
1965 if (tigetflag(TERM_EAT_NEW_LINE
))
1966 /* Eat newline at last column + 1; dec, concept */
1968 if (ctl
->num_columns
<= 0)
1969 ctl
->num_columns
= NUM_COLUMNS
;
1971 ctl
->wrap_margin
= tigetflag(TERM_AUTO_RIGHT_MARGIN
);
1972 ctl
->bad_stdout
= tigetflag(TERM_CEOL
);
1973 ctl
->erase_line
= tigetstr(TERM_CLEAR_TO_LINE_END
);
1974 ctl
->clear
= tigetstr(TERM_CLEAR
);
1975 if ((ctl
->enter_std
= tigetstr(TERM_STANDARD_MODE
)) != NULL
) {
1976 ctl
->exit_std
= tigetstr(TERM_EXIT_STANDARD_MODE
);
1977 if (0 < tigetnum(TERM_STD_MODE_GLITCH
))
1978 ctl
->stdout_glitch
= 1;
1981 cursor_addr
= tigetstr(TERM_HOME
);
1982 if (cursor_addr
== NULL
|| *cursor_addr
== '\0') {
1983 cursor_addr
= tigetstr(TERM_CURSOR_ADDRESS
);
1985 cursor_addr
= tparm(cursor_addr
, 0, 0);
1988 ctl
->go_home
= xstrdup(cursor_addr
);
1990 if ((ctl
->move_line_down
= tigetstr(TERM_LINE_DOWN
)) == NULL
)
1991 ctl
->move_line_down
= BACKSPACE
;
1992 ctl
->clear_rest
= tigetstr(TERM_CLEAR_TO_SCREEN_END
);
1993 if ((ctl
->backspace_ch
= tigetstr(TERM_BACKSPACE
)) == NULL
)
1994 ctl
->backspace_ch
= BACKSPACE
;
1996 if ((ctl
->shell
= getenv("SHELL")) == NULL
)
1997 ctl
->shell
= _PATH_BSHELL
;
2000 int main(int argc
, char **argv
)
2004 struct more_control ctl
= {
2006 .fold_long_lines
= 1,
2007 .no_quit_dialog
= 1,
2008 .stop_after_formfeed
= 1,
2010 .lines_per_page
= LINES_PER_PAGE
,
2011 .num_columns
= NUM_COLUMNS
,
2012 .d_scroll_len
= SCROLL_LEN
,
2016 setlocale(LC_ALL
, "");
2017 bindtextdomain(PACKAGE
, LOCALEDIR
);
2018 textdomain(PACKAGE
);
2019 close_stdout_atexit();
2020 setlocale(LC_ALL
, "");
2022 /* Auto set no scroll on when binary is called page */
2023 if (!(strcmp(program_invocation_short_name
, "page")))
2026 if ((s
= getenv("MORE")) != NULL
)
2027 env_argscan(&ctl
, s
);
2028 argscan(&ctl
, argc
, argv
);
2033 ctl
.magic
= magic_open(MAGIC_MIME_ENCODING
| MAGIC_SYMLINK
);
2034 magic_load(ctl
.magic
, NULL
);
2036 prepare_line_buffer(&ctl
);
2038 ctl
.d_scroll_len
= ctl
.lines_per_page
/ 2 - 1;
2039 if (ctl
.d_scroll_len
<= 0)
2040 ctl
.d_scroll_len
= 1;
2042 /* allow clear_line_ends only if go_home and erase_line and clear_rest strings are
2043 * defined, and in that case, make sure we are in no_scroll mode */
2044 if (ctl
.clear_line_ends
) {
2045 if ((ctl
.go_home
== NULL
) || (*ctl
.go_home
== '\0') ||
2046 (ctl
.erase_line
== NULL
) || (*ctl
.erase_line
== '\0') ||
2047 (ctl
.clear_rest
== NULL
) || (*ctl
.clear_rest
== '\0'))
2048 ctl
.clear_line_ends
= 0;
2052 if (ctl
.lines_per_screen
== 0)
2053 ctl
.lines_per_screen
= ctl
.lines_per_page
- 1;
2054 left
= ctl
.lines_per_screen
;
2055 if (ctl
.num_files
> 1)
2056 ctl
.print_banner
= 1;
2057 if (!ctl
.no_tty_in
&& ctl
.num_files
== 0) {
2058 warnx(_("bad usage"));
2059 errtryhelp(EXIT_FAILURE
);
2061 ctl
.current_file
= stdin
;
2062 if (!ctl
.no_tty_out
) {
2063 if (signal(SIGTSTP
, SIG_IGN
) == SIG_DFL
) {
2064 ctl
.catch_suspend
++;
2066 tcsetattr(STDERR_FILENO
, TCSANOW
, &ctl
.output_tty
);
2068 sigemptyset(&ctl
.sigset
);
2069 sigaddset(&ctl
.sigset
, SIGINT
);
2070 sigaddset(&ctl
.sigset
, SIGQUIT
);
2071 sigaddset(&ctl
.sigset
, SIGTSTP
);
2072 sigaddset(&ctl
.sigset
, SIGCONT
);
2073 sigaddset(&ctl
.sigset
, SIGWINCH
);
2074 sigprocmask(SIG_BLOCK
, &ctl
.sigset
, NULL
);
2075 ctl
.sigfd
= signalfd(-1, &ctl
.sigset
, SFD_CLOEXEC
);
2076 if (ctl
.no_tty_in
) {
2080 ctl
.current_file
= stdin
;
2081 display_file(&ctl
, left
);
2084 ctl
.print_banner
= 1;
2088 while (ctl
.argv_position
< ctl
.num_files
) {
2089 checkf(&ctl
, ctl
.file_names
[ctl
.argv_position
]);
2090 display_file(&ctl
, left
);
2092 ctl
.argv_position
++;
2094 ctl
.clear_line_ends
= 0;