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>
68 #if defined(HAVE_NCURSESW_TERM_H)
69 # include <ncursesw/term.h>
70 #elif defined(HAVE_NCURSES_TERM_H)
71 # include <ncurses/term.h>
72 #elif defined(HAVE_TERM_H)
80 #include "closestream.h"
84 # define NON_INTERACTIVE_MORE 1
87 #define BACKSPACE "\b"
90 #define MIN_LINE_SZ 256 /* minimal line_buf buffer size */
93 #define LINES_PER_PAGE 24
94 #define NUM_COLUMNS 80
95 #define TERMINAL_BUF 4096
97 #define COMMAND_BUF 200
98 #define REGERR_BUF NUM_COLUMNS
100 #define TERM_AUTO_RIGHT_MARGIN "am"
101 #define TERM_BACKSPACE "cub1"
102 #define TERM_CEOL "xhp"
103 #define TERM_CLEAR "clear"
104 #define TERM_CLEAR_TO_LINE_END "el"
105 #define TERM_CLEAR_TO_SCREEN_END "ed"
106 #define TERM_COLS "cols"
107 #define TERM_CURSOR_ADDRESS "cup"
108 #define TERM_EAT_NEW_LINE "xenl"
109 #define TERM_ENTER_UNDERLINE "smul"
110 #define TERM_EXIT_STANDARD_MODE "rmso"
111 #define TERM_EXIT_UNDERLINE "rmul"
112 #define TERM_HARD_COPY "hc"
113 #define TERM_HOME "home"
114 #define TERM_LINE_DOWN "cud1"
115 #define TERM_LINES "lines"
116 #define TERM_OVER_STRIKE "os"
117 #define TERM_STANDARD_MODE "smso"
118 #define TERM_STD_MODE_GLITCH "xmc"
119 #define TERM_UNDERLINE_CHAR "uc"
120 #define TERM_UNDERLINE "ul"
122 struct more_control
{
123 struct termios output_tty
; /* output terminal */
124 struct termios original_tty
; /* original terminal settings */
125 FILE *current_file
; /* currently open input file */
126 off_t file_position
; /* file position */
127 off_t file_size
; /* file size */
128 int argv_position
; /* argv[] position */
129 int lines_per_screen
; /* screen size in lines */
130 int d_scroll_len
; /* number of lines scrolled by 'd' */
131 int prompt_len
; /* message prompt length */
132 int current_line
; /* line we are currently at */
133 char **file_names
; /* The list of file names */
134 int num_files
; /* Number of files left to process */
135 char *shell
; /* name of the shell to use */
136 int sigfd
; /* signalfd() file descriptor */
137 sigset_t sigset
; /* signal operations */
138 char *line_buf
; /* line buffer */
139 size_t line_sz
; /* size of line_buf buffer */
140 int lines_per_page
; /* lines per page */
141 char *clear
; /* clear screen */
142 char *erase_line
; /* erase line */
143 char *enter_std
; /* enter standout mode */
144 char *exit_std
; /* exit standout mode */
145 char *enter_underline
; /* enter underline mode */
146 char *exit_underline
; /* exit underline mode */
147 char *underline_ch
; /* underline character */
148 char *backspace_ch
; /* backspace character */
149 char *go_home
; /* go to home */
150 char *move_line_down
; /* move line down */
151 char *clear_rest
; /* clear rest of screen */
152 int num_columns
; /* number of columns */
153 char *previous_search
; /* previous search() buf[] item */
155 off_t row_num
; /* row file position */
156 long line_num
; /* line number */
159 cc_t last_key_command
; /* previous more key command */
160 int last_key_arg
; /* previous key command argument */
161 int last_colon_command
; /* is a colon-prefixed key command */
162 char *shell_line
; /* line to execute in subshell */
164 bad_stdout
:1, /* true if overwriting does not turn off standout */
165 catch_suspend
:1, /* we should catch the SIGTSTP signal */
166 clear_line_ends
:1, /* do not scroll, paint each screen from the top */
167 clear_first
:1, /* is first character in file \f */
168 dumb_tty
:1, /* is terminal type known */
169 eat_newline
:1, /* is newline ignored after 80 cols */
170 enable_underlining
:1, /* underline as best we can */
171 erase_input_ok
:1, /* is erase input supported */
172 erase_previous_ok
:1, /* is erase previous supported */
173 first_file
:1, /* is the input file the first in list */
174 fold_long_lines
:1, /* fold long lines */
175 hard_tabs
:1, /* print spaces instead of '\t' */
176 hard_tty
:1, /* is this hard copy terminal (a printer or such) */
177 jump_at_start
:1, /* jump to line N defined at start up */
178 is_paused
:1, /* is output paused */
179 no_quit_dialog
:1, /* suppress quit dialog */
180 no_scroll
:1, /* do not scroll, clear the screen and then display text */
181 no_tty_in
:1, /* is input in interactive mode */
182 no_tty_out
:1, /* is output in interactive mode */
183 print_banner
:1, /* print file name banner */
184 report_errors
:1, /* is an error reported */
185 run_previous_command
:1, /* run previous key command */
186 search_at_start
:1, /* search pattern defined at start up */
187 search_called
:1, /* previous more command was a search */
188 squeeze_spaces
:1, /* suppress white space */
189 stdout_glitch
:1, /* terminal has standout mode glitch */
190 stop_after_formfeed
:1, /* stop after form feeds */
191 suppress_bell
:1, /* suppress bell */
192 underline_glitch
:1, /* terminal has underline mode glitch */
193 underline_state
:1, /* current UL state */
194 wrap_margin
:1; /* set if automargins */
197 static void __attribute__((__noreturn__
)) usage(void)
200 fputs(USAGE_HEADER
, out
);
201 fprintf(out
, _(" %s [options] <file>...\n"), program_invocation_short_name
);
203 fputs(USAGE_SEPARATOR
, out
);
204 fputs(_("A file perusal filter for CRT viewing.\n"), out
);
206 fputs(USAGE_OPTIONS
, out
);
207 fputs(_(" -d display help instead of ringing bell\n"), out
);
208 fputs(_(" -f count logical rather than screen lines\n"), out
);
209 fputs(_(" -l suppress pause after form feed\n"), out
);
210 fputs(_(" -c do not scroll, display text and clean line ends\n"), out
);
211 fputs(_(" -p do not scroll, clean screen and display text\n"), out
);
212 fputs(_(" -s squeeze multiple blank lines into one\n"), out
);
213 fputs(_(" -u suppress underlining\n"), out
);
214 fputs(_(" -<number> the number of lines per screenful\n"), out
);
215 fputs(_(" +<number> display file beginning from line number\n"), out
);
216 fputs(_(" +/<string> display file beginning from search string match\n"), out
);
218 fputs(USAGE_SEPARATOR
, out
);
219 printf( " --help %s\n", USAGE_OPTSTR_HELP
);
220 printf( " -V, --version %s\n", USAGE_OPTSTR_VERSION
);
221 printf(USAGE_MAN_TAIL("more(1)"));
226 static void arg_parser(struct more_control
*ctl
, char *s
)
243 ctl
->lines_per_screen
= 0;
246 ctl
->lines_per_screen
= ctl
->lines_per_screen
* 10 + *s
- '0';
249 ctl
->suppress_bell
= 1;
252 ctl
->stop_after_formfeed
= 0;
255 ctl
->fold_long_lines
= 0;
261 ctl
->clear_line_ends
= 1;
264 ctl
->squeeze_spaces
= 1;
267 ctl
->enable_underlining
= 0;
274 print_version(EXIT_SUCCESS
);
278 warnx(_("unknown option -%s"), s
);
279 errtryhelp(EXIT_FAILURE
);
286 static void more_fseek(struct more_control
*ctl
, off_t pos
)
288 ctl
->file_position
= pos
;
289 fseeko(ctl
->current_file
, pos
, SEEK_SET
);
292 static int more_getc(struct more_control
*ctl
)
294 int ret
= getc(ctl
->current_file
);
295 ctl
->file_position
= ftello(ctl
->current_file
);
299 static int more_ungetc(struct more_control
*ctl
, int c
)
301 int ret
= ungetc(c
, ctl
->current_file
);
302 ctl
->file_position
= ftello(ctl
->current_file
);
306 static void print_separator(const int c
, int n
)
314 * check for file magic numbers. This code would best be shared
315 * with the file(1) program or, perhaps, more should not try to be
317 static int check_magic(FILE *f
, char *fs
)
319 signed char twobytes
[2];
321 /* don't try to look ahead if the input is unseekable */
322 if (fseek(f
, 0L, SEEK_SET
))
325 if (fread(twobytes
, 2, 1, f
) == 1) {
326 switch (twobytes
[0] + (twobytes
[1] << 8)) {
327 case 0407: /* a.out obj */
328 case 0410: /* a.out exec */
329 case 0413: /* a.out demand exec */
333 case 0x457f: /* simple ELF detection */
334 printf(_("\n******** %s: Not a text file ********\n\n"),
339 fseek(f
, 0L, SEEK_SET
); /* rewind() not necessary */
343 /* Check whether the file named by fs is an ASCII file which the user may
344 * access. If it is, return the opened file. Otherwise return NULL. */
345 static void checkf(struct more_control
*ctl
, char *fs
)
350 if (stat(fs
, &st
) == -1) {
352 if (ctl
->clear_line_ends
)
353 putp(ctl
->erase_line
);
354 warn(_("stat of %s failed"), fs
);
355 ctl
->current_file
= NULL
;
358 if ((st
.st_mode
& S_IFMT
) == S_IFDIR
) {
359 printf(_("\n*** %s: directory ***\n\n"), fs
);
360 ctl
->current_file
= NULL
;
363 ctl
->current_line
= 0;
364 ctl
->file_position
= 0;
365 if ((ctl
->current_file
= fopen(fs
, "r")) == NULL
) {
367 warn(_("cannot open %s"), fs
);
370 if (check_magic(ctl
->current_file
, fs
)) {
371 fclose(ctl
->current_file
);
372 ctl
->current_file
= NULL
;
375 fcntl(fileno(ctl
->current_file
), F_SETFD
, FD_CLOEXEC
);
377 ctl
->clear_first
= (c
== '\f');
379 if ((ctl
->file_size
= st
.st_size
) == 0)
380 ctl
->file_size
= ~((off_t
)0);
383 static void prepare_line_buffer(struct more_control
*ctl
)
385 size_t sz
= ctl
->num_columns
* 4;
387 if (ctl
->line_sz
>= sz
)
390 if (sz
< MIN_LINE_SZ
)
393 /* alloc sz and extra space for \n\0 */
394 ctl
->line_buf
= xrealloc(ctl
->line_buf
, sz
+ 2);
398 /* Get a logical line */
399 static int get_line(struct more_control
*ctl
, int *length
)
404 static int column_wrap
;
410 mbstate_t state
, state_bak
; /* Current status of the stream. */
411 char mbc
[MB_LEN_MAX
]; /* Buffer for one multibyte char. */
412 size_t mblength
; /* Byte length of multibyte char. */
413 size_t mbc_pos
= 0; /* Position of the MBC. */
414 int use_mbc_buffer_flag
= 0; /* If 1, mbc has data. */
415 int break_flag
= 0; /* If 1, exit while(). */
416 off_t file_position_bak
= ctl
->file_position
;
418 memset(&state
, '\0', sizeof(mbstate_t));
424 if (column_wrap
&& c
== '\n') {
428 while (p
< &ctl
->line_buf
[ctl
->line_sz
- 1]) {
430 if (ctl
->fold_long_lines
&& use_mbc_buffer_flag
&& MB_CUR_MAX
> 1) {
431 use_mbc_buffer_flag
= 0;
435 mblength
= mbrtowc(&wc
, mbc
, mbc_pos
, &state
);
438 case (size_t)-2: /* Incomplete multibyte character. */
439 use_mbc_buffer_flag
= 1;
443 case (size_t)-1: /* Invalid as a multibyte character. */
448 if (column
>= ctl
->num_columns
) {
449 more_fseek(ctl
, file_position_bak
);
451 memmove(mbc
, mbc
+ 1, --mbc_pos
);
460 wc_width
= wcwidth(wc
);
461 if (column
+ wc_width
> ctl
->num_columns
) {
462 more_fseek(ctl
, file_position_bak
);
465 for (i
= 0; p
< &ctl
->line_buf
[ctl
->line_sz
- 1] &&
473 if (break_flag
|| column
>= ctl
->num_columns
)
479 #endif /* HAVE_WIDECHAR */
481 if (p
> ctl
->line_buf
) {
483 *length
= p
- ctl
->line_buf
;
486 *length
= p
- ctl
->line_buf
;
496 if (!ctl
->hard_tabs
|| (column
< ctl
->prompt_len
&& !ctl
->hard_tty
)) {
497 if (ctl
->hard_tabs
&& ctl
->erase_line
&& !ctl
->dumb_tty
) {
498 column
= 1 + (column
| 7);
499 putp(ctl
->erase_line
);
502 for (--p
; p
< &ctl
->line_buf
[ctl
->line_sz
- 1];) {
504 if ((++column
& 7) == 0)
507 if (column
>= ctl
->prompt_len
)
511 column
= 1 + (column
| 7);
512 } else if (c
== '\b' && column
> 0) {
514 } else if (c
== '\r') {
515 int next
= more_getc(ctl
);
523 } else if (c
== '\f' && ctl
->stop_after_formfeed
) {
528 } else if (c
== EOF
) {
529 *length
= p
- ctl
->line_buf
;
533 if (ctl
->fold_long_lines
&& MB_CUR_MAX
> 1) {
534 memset(mbc
, '\0', MB_LEN_MAX
);
539 mblength
= mbrtowc(&wc
, mbc
, mbc_pos
, &state
);
540 /* The value of mblength is always less than 2 here. */
544 file_position_bak
= ctl
->file_position
- 1;
546 use_mbc_buffer_flag
= 1;
555 wc_width
= wcwidth(wc
);
560 #endif /* HAVE_WIDECHAR */
567 if (column
>= ctl
->num_columns
&& ctl
->fold_long_lines
)
570 if (use_mbc_buffer_flag
== 0 && p
>= &ctl
->line_buf
[ctl
->line_sz
- 1 - 4])
571 /* don't read another char if there is no space for
572 * whole multibyte sequence */
577 if (column
>= ctl
->num_columns
&& ctl
->num_columns
> 0) {
578 if (!ctl
->wrap_margin
) {
582 column_wrap
= column
== ctl
->num_columns
&& ctl
->fold_long_lines
;
583 if (column_wrap
&& ctl
->eat_newline
&& ctl
->wrap_margin
) {
584 *p
++ = '\n'; /* simulate normal wrap */
586 *length
= p
- ctl
->line_buf
;
591 /* Erase the rest of the prompt, assuming we are starting at column col. */
592 static void erase_to_col(struct more_control
*ctl
, int col
)
595 if (ctl
->prompt_len
== 0)
597 if (col
== 0 && ctl
->clear_line_ends
)
598 puts(ctl
->erase_line
);
599 else if (ctl
->hard_tty
)
604 if (!ctl
->dumb_tty
&& ctl
->erase_line
)
605 putp(ctl
->erase_line
);
607 printf("%*s", ctl
->prompt_len
- col
, "");
612 ctl
->prompt_len
= col
;
616 static UL_ASAN_BLACKLIST
size_t xmbrtowc(wchar_t *wc
, const char *s
, size_t n
,
619 const size_t mblength
= mbrtowc(wc
, s
, n
, mbstate
);
620 if (mblength
== (size_t)-2 || mblength
== (size_t)-1)
626 static int would_underline(char *s
, int n
)
630 if ((s
[0] == '_' && s
[1] == '\b') || (s
[1] == '\b' && s
[2] == '_'))
635 /* Print a buffer of n characters */
636 static void print_buf(struct more_control
*ctl
, char *s
, int n
)
638 char c
; /* next output character */
639 int state
; /* next output char's UL state */
642 if (!ctl
->enable_underlining
) {
646 if (*s
== ' ' && ctl
->underline_state
== 0 && ctl
->underline_glitch
647 && would_underline(s
+ 1, n
- 1)) {
651 if ((state
= would_underline(s
, n
)) != 0) {
652 c
= (*s
== '_') ? s
[2] : *s
;
657 if (state
!= ctl
->underline_state
) {
658 if (c
== ' ' && state
== 0 && ctl
->underline_glitch
659 && would_underline(s
, n
- 1))
662 putp(state
? ctl
->enter_underline
: ctl
->exit_underline
);
664 if (c
!= ' ' || ctl
->underline_state
== 0 || state
!= 0
665 || ctl
->underline_glitch
== 0) {
671 memset(&mbstate
, '\0', sizeof(mbstate_t));
674 mblength
= xmbrtowc(&wc
, s
, n
, &mbstate
);
682 if (state
&& *ctl
->underline_ch
) {
683 fputs(ctl
->move_line_down
, stdout
);
684 putp(ctl
->underline_ch
);
686 ctl
->underline_state
= state
;
690 static void output_prompt(struct more_control
*ctl
, char *filename
)
692 if (ctl
->clear_line_ends
)
693 putp(ctl
->erase_line
);
694 else if (ctl
->prompt_len
> 0)
695 erase_to_col(ctl
, 0);
696 if (!ctl
->hard_tty
) {
698 if (ctl
->enter_std
) {
699 putp(ctl
->enter_std
);
700 ctl
->prompt_len
+= (2 * ctl
->stdout_glitch
);
702 if (ctl
->clear_line_ends
)
703 putp(ctl
->erase_line
);
704 ctl
->prompt_len
+= printf(_("--More--"));
705 if (filename
!= NULL
) {
706 ctl
->prompt_len
+= printf(_("(Next file: %s)"), filename
);
707 } else if (!ctl
->no_tty_in
) {
710 (int)((ctl
->file_position
* 100) / ctl
->file_size
));
712 if (ctl
->suppress_bell
) {
714 printf(_("[Press space to continue, 'q' to quit.]"));
718 if (ctl
->clear_line_ends
)
719 putp(ctl
->clear_rest
);
722 fprintf(stderr
, "\a");
725 static void reset_tty(struct more_control
*ctl
)
729 if (ctl
->underline_state
) {
730 putp(ctl
->exit_underline
);
732 ctl
->underline_state
= 0;
734 ctl
->output_tty
.c_lflag
|= ICANON
| ECHO
;
735 ctl
->output_tty
.c_cc
[VMIN
] = ctl
->original_tty
.c_cc
[VMIN
];
736 ctl
->output_tty
.c_cc
[VTIME
] = ctl
->original_tty
.c_cc
[VTIME
];
737 tcsetattr(STDERR_FILENO
, TCSANOW
, &ctl
->original_tty
);
740 /* Clean up terminal state and exit. Also come here if interrupt signal received */
741 static void __attribute__((__noreturn__
)) more_exit(struct more_control
*ctl
)
744 if (ctl
->clear_line_ends
) {
746 putp(ctl
->erase_line
);
748 } else if (!ctl
->clear_line_ends
&& (ctl
->prompt_len
> 0)) {
749 erase_to_col(ctl
, 0);
752 free(ctl
->previous_search
);
753 free(ctl
->shell_line
);
756 if (ctl
->current_file
)
757 fclose(ctl
->current_file
);
758 del_curterm(cur_term
);
762 static cc_t
read_user_input(struct more_control
*ctl
)
767 if (read(STDERR_FILENO
, &c
, 1) <= 0) {
771 c
= ctl
->output_tty
.c_cc
[VKILL
];
776 /* Read a decimal number from the terminal. Set cmd to the non-digit
777 * which terminates the number. */
778 static int read_number(struct more_control
*ctl
, cc_t
*cmd
)
784 ch
= ctl
->output_tty
.c_cc
[VKILL
];
786 ch
= read_user_input(ctl
);
788 i
= i
* 10 + ch
- '0';
789 else if (ch
== ctl
->output_tty
.c_cc
[VKILL
])
799 /* Change displayed file from command line list to next nskip, where nskip
800 * is relative position in argv and can be negative, that is a previous
802 static void change_file(struct more_control
*ctl
, int nskip
)
807 if (ctl
->argv_position
+ nskip
> ctl
->num_files
- 1)
808 nskip
= ctl
->num_files
- ctl
->argv_position
- 1;
810 ctl
->argv_position
+= nskip
;
811 if (ctl
->argv_position
< 0)
812 ctl
->argv_position
= 0;
813 puts(_("\n...Skipping "));
814 if (ctl
->clear_line_ends
)
815 putp(ctl
->erase_line
);
817 fputs(_("...Skipping to file "), stdout
);
819 fputs(_("...Skipping back to file "), stdout
);
820 puts(ctl
->file_names
[ctl
->argv_position
]);
821 if (ctl
->clear_line_ends
)
822 putp(ctl
->erase_line
);
824 ctl
->argv_position
--;
827 static void show(struct more_control
*ctl
, char c
)
829 if ((c
< ' ' && c
!= '\n' && c
!= ESC
) || c
== CERASE
) {
830 c
+= (c
== CERASE
) ? -0100 : 0100;
831 fputs(CARAT
, stderr
);
838 static void more_error(struct more_control
*ctl
, char *mess
)
840 if (ctl
->clear_line_ends
)
841 putp(ctl
->erase_line
);
843 erase_to_col(ctl
, 0);
844 ctl
->prompt_len
+= strlen(mess
);
846 putp(ctl
->enter_std
);
851 ctl
->report_errors
++;
854 static void erase_one_column(struct more_control
*ctl
)
856 if (ctl
->erase_previous_ok
)
857 fprintf(stderr
, "%s ", ctl
->backspace_ch
);
858 fputs(ctl
->backspace_ch
, stderr
);
861 static void ttyin(struct more_control
*ctl
, char buf
[], int nmax
, char pchar
)
870 while (sp
- buf
< nmax
) {
871 if (ctl
->prompt_len
> maxlen
)
872 maxlen
= ctl
->prompt_len
;
873 c
= read_user_input(ctl
);
876 } else if (c
== ctl
->output_tty
.c_cc
[VERASE
] && !slash
) {
879 if (MB_CUR_MAX
> 1) {
881 size_t pos
= 0, mblength
;
882 mbstate_t state
, state_bak
;
884 memset(&state
, '\0', sizeof(mbstate_t));
889 mbrtowc(&wc
, buf
+ pos
,
900 if (buf
+ pos
+ mblength
>= sp
)
907 erase_one_column(ctl
);
910 wc_width
= wcwidth(wc
);
915 erase_one_column(ctl
);
924 #endif /* HAVE_WIDECHAR */
927 erase_one_column(ctl
);
931 if ((*sp
< ' ' && *sp
!= '\n') || *sp
== CERASE
) {
933 erase_one_column(ctl
);
937 if (!ctl
->erase_line
)
938 ctl
->prompt_len
= maxlen
;
940 } else if (c
== ctl
->output_tty
.c_cc
[VKILL
] && !slash
) {
949 erase_to_col(ctl
, 1);
950 else if (ctl
->erase_input_ok
)
951 while (ctl
->prompt_len
-- > 1)
952 fprintf(stderr
, "%s %s", ctl
->backspace_ch
, ctl
->backspace_ch
);
959 if (slash
&& (c
== ctl
->output_tty
.c_cc
[VKILL
] ||
960 c
== ctl
->output_tty
.c_cc
[VERASE
])) {
961 erase_one_column(ctl
);
967 if ((c
< ' ' && c
!= '\n' && c
!= ESC
) || c
== CERASE
) {
968 c
+= (c
== CERASE
) ? -0100 : 0100;
969 fputs(CARAT
, stderr
);
972 if (c
!= '\n' && c
!= ESC
) {
979 if (!ctl
->erase_line
)
980 ctl
->prompt_len
= maxlen
;
981 if (sp
- buf
>= nmax
- 1)
982 more_error(ctl
, _("Line too long"));
985 /* Expand shell command line. */
986 static void expand(struct more_control
*ctl
, char *inbuf
)
992 int tempsz
, xtra
, offset
;
994 xtra
= strlen(ctl
->file_names
[ctl
->argv_position
]) + strlen(ctl
->shell_line
) + 1;
995 tempsz
= COMMAND_BUF
+ xtra
;
996 temp
= xmalloc(tempsz
);
999 while ((c
= *inpstr
++) != '\0') {
1000 offset
= outstr
- temp
;
1001 if (tempsz
- offset
- 1 < xtra
) {
1002 tempsz
+= COMMAND_BUF
+ xtra
;
1003 temp
= xrealloc(temp
, tempsz
);
1004 outstr
= temp
+ offset
;
1008 if (!ctl
->no_tty_in
) {
1009 strcpy(outstr
, ctl
->file_names
[ctl
->argv_position
]);
1010 outstr
+= strlen(ctl
->file_names
[ctl
->argv_position
]);
1015 if (ctl
->shell_line
) {
1016 strcpy(outstr
, ctl
->shell_line
);
1017 outstr
+= strlen(ctl
->shell_line
);
1020 ("No previous command to substitute for"));
1023 if (*inpstr
== '%' || *inpstr
== '!') {
1024 *outstr
++ = *inpstr
++;
1033 free(ctl
->shell_line
);
1034 ctl
->shell_line
= temp
;
1037 static void set_tty(struct more_control
*ctl
)
1039 ctl
->output_tty
.c_lflag
&= ~(ICANON
| ECHO
);
1040 ctl
->output_tty
.c_cc
[VMIN
] = 1; /* read at least 1 char */
1041 ctl
->output_tty
.c_cc
[VTIME
] = 0; /* no timeout */
1042 tcsetattr(STDERR_FILENO
, TCSANOW
, &ctl
->output_tty
);
1045 /* Come here if a quit signal is received */
1046 static void sigquit_handler(struct more_control
*ctl
)
1048 if (!ctl
->dumb_tty
&& ctl
->no_quit_dialog
) {
1049 ctl
->prompt_len
+= fprintf(stderr
, _("[Use q or Q to quit]"));
1050 ctl
->no_quit_dialog
= 0;
1055 /* Come here when we get a suspend signal from the terminal */
1056 static void sigtstp_handler(struct more_control
*ctl
)
1065 /* Come here if a signal for a window size change is received */
1066 static void sigwinch_handler(struct more_control
*ctl
)
1070 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) != -1) {
1071 if (win
.ws_row
!= 0) {
1072 ctl
->lines_per_page
= win
.ws_row
;
1073 ctl
->d_scroll_len
= ctl
->lines_per_page
/ 2 - 1;
1074 if (ctl
->d_scroll_len
< 1)
1075 ctl
->d_scroll_len
= 1;
1076 ctl
->lines_per_screen
= ctl
->lines_per_page
- 1;
1078 if (win
.ws_col
!= 0)
1079 ctl
->num_columns
= win
.ws_col
;
1081 prepare_line_buffer(ctl
);
1084 static void execute(struct more_control
*ctl
, char *filename
, char *cmd
, ...)
1094 for (n
= 10; (id
= fork()) < 0 && n
> 0; n
--)
1098 if (!isatty(STDIN_FILENO
)) {
1099 close(STDIN_FILENO
);
1100 open("/dev/tty", 0);
1104 va_start(argp
, cmd
);
1105 arg
= va_arg(argp
, char *);
1109 arg
= va_arg(argp
, char *);
1113 args
= alloca(sizeof(char *) * (argcount
+ 1));
1114 args
[argcount
] = NULL
;
1116 va_start(argp
, cmd
);
1117 arg
= va_arg(argp
, char *);
1120 args
[argcount
] = arg
;
1122 arg
= va_arg(argp
, char *);
1126 if (geteuid() != getuid() || getegid() != getgid()) {
1127 if (setuid(getuid()) < 0)
1128 err(EXIT_FAILURE
, _("setuid failed"));
1129 if (setgid(getgid()) < 0)
1130 err(EXIT_FAILURE
, _("setgid failed"));
1135 fputs(_("exec failed\n"), stderr
);
1136 exit(errsv
== ENOENT
? EX_EXEC_ENOENT
: EX_EXEC_FAILED
);
1139 while (wait(NULL
) > 0)
1142 fputs(_("can't fork\n"), stderr
);
1144 print_separator('-', 24);
1145 output_prompt(ctl
, filename
);
1148 static void run_shell(struct more_control
*ctl
, char *filename
)
1150 char cmdbuf
[COMMAND_BUF
];
1152 erase_to_col(ctl
, 0);
1155 ctl
->prompt_len
= 1;
1156 if (ctl
->run_previous_command
&& ctl
->shell_line
)
1157 fputs(ctl
->shell_line
, stdout
);
1159 ttyin(ctl
, cmdbuf
, sizeof(cmdbuf
) - 2, '!');
1160 if (strpbrk(cmdbuf
, "%!\\"))
1161 expand(ctl
, cmdbuf
);
1163 free(ctl
->shell_line
);
1164 ctl
->shell_line
= xstrdup(cmdbuf
);
1166 erase_to_col(ctl
, 0);
1167 ctl
->prompt_len
= printf("!%s", ctl
->shell_line
);
1170 fputc('\n', stderr
);
1171 ctl
->prompt_len
= 0;
1172 execute(ctl
, filename
, ctl
->shell
, ctl
->shell
, "-c", ctl
->shell_line
, 0);
1175 /* Execute a colon-prefixed command. Returns <0 if not a command that
1176 * should cause more of the file to be printed. */
1177 static int colon_command(struct more_control
*ctl
, char *filename
, int cmd
, int nlines
)
1182 ch
= read_user_input(ctl
);
1185 ctl
->last_colon_command
= ch
;
1188 erase_to_col(ctl
, 0);
1189 if (!ctl
->no_tty_in
)
1191 printf(_("\"%s\" line %d"), ctl
->file_names
[ctl
->argv_position
], ctl
->current_line
);
1193 ctl
->prompt_len
= printf(_("[Not a file] line %d"), ctl
->current_line
);
1198 if (ctl
->argv_position
>= ctl
->num_files
- 1)
1203 erase_to_col(ctl
, 0);
1204 change_file(ctl
, nlines
);
1207 if (ctl
->no_tty_in
) {
1208 fprintf(stderr
, "\a");
1212 erase_to_col(ctl
, 0);
1215 change_file(ctl
, -nlines
);
1218 run_shell(ctl
, filename
);
1224 fprintf(stderr
, "\a");
1229 /* Skip n lines in the file f */
1230 static void skip_lines(struct more_control
*ctl
, int n
)
1235 while ((c
= more_getc(ctl
)) != '\n')
1239 ctl
->current_line
++;
1243 /* Clear the screen */
1244 static void more_clear_screen(struct more_control
*ctl
)
1246 if (ctl
->clear
&& !ctl
->hard_tty
) {
1248 /* Put out carriage return so that system doesn't get
1249 * confused by escape sequences when expanding tabs */
1251 ctl
->prompt_len
= 0;
1255 static void read_line(struct more_control
*ctl
)
1261 while ((c
= more_getc(ctl
)) != '\n' && c
!= EOF
1262 && (ptrdiff_t)p
!= (ptrdiff_t)(ctl
->line_buf
+ ctl
->line_sz
- 1))
1265 ctl
->current_line
++;
1269 static int more_poll(struct more_control
*ctl
, int timeout
)
1271 struct pollfd pfd
[2];
1273 pfd
[0].fd
= ctl
->sigfd
;
1274 pfd
[0].events
= POLLIN
| POLLERR
| POLLHUP
;
1275 pfd
[1].fd
= STDIN_FILENO
;
1276 pfd
[1].events
= POLLIN
;
1278 if (poll(pfd
, 2, timeout
) < 0) {
1279 if (errno
== EAGAIN
)
1281 more_error(ctl
, _("poll failed"));
1284 if (pfd
[0].revents
!= 0) {
1285 struct signalfd_siginfo info
;
1288 sz
= read(pfd
[0].fd
, &info
, sizeof(info
));
1289 assert(sz
== sizeof(info
));
1290 switch (info
.ssi_signo
) {
1295 sigquit_handler(ctl
);
1298 sigtstp_handler(ctl
);
1301 sigwinch_handler(ctl
);
1307 if (pfd
[1].revents
== 0)
1312 /* Search for nth occurrence of regular expression contained in buf in
1314 static void search(struct more_control
*ctl
, char buf
[], int n
)
1316 off_t startline
= ctl
->file_position
;
1317 off_t line1
= startline
;
1318 off_t line2
= startline
;
1324 ctl
->context
.line_num
= saveln
= ctl
->current_line
;
1325 ctl
->context
.row_num
= startline
;
1329 if ((rc
= regcomp(&re
, buf
, REG_NOSUB
)) != 0) {
1331 regerror(rc
, &re
, s
, sizeof s
);
1335 while (!feof(ctl
->current_file
)) {
1338 line1
= ctl
->file_position
;
1341 if (regexec(&re
, ctl
->line_buf
, 0, NULL
, 0) == 0 && --n
== 0) {
1342 if ((1 < lncount
&& ctl
->no_tty_in
) || 3 < lncount
) {
1344 if (ctl
->clear_line_ends
)
1345 putp(ctl
->erase_line
);
1346 fputs(_("...skipping\n"), stdout
);
1348 if (!ctl
->no_tty_in
) {
1349 ctl
->current_line
-= (lncount
< 3 ? lncount
: 3);
1350 more_fseek(ctl
, line3
);
1351 if (ctl
->no_scroll
) {
1352 if (ctl
->clear_line_ends
) {
1354 putp(ctl
->erase_line
);
1356 more_clear_screen(ctl
);
1359 erase_to_col(ctl
, 0);
1360 if (ctl
->no_scroll
) {
1361 if (ctl
->clear_line_ends
) {
1363 putp(ctl
->erase_line
);
1365 more_clear_screen(ctl
);
1367 puts(ctl
->line_buf
);
1373 /* Move ctrl+c signal handling back to more_key_command(). */
1374 signal(SIGINT
, SIG_DFL
);
1375 sigaddset(&ctl
->sigset
, SIGINT
);
1376 sigprocmask(SIG_BLOCK
, &ctl
->sigset
, NULL
);
1378 if (feof(ctl
->current_file
)) {
1379 if (!ctl
->no_tty_in
) {
1380 ctl
->current_line
= saveln
;
1381 more_fseek(ctl
, startline
);
1383 fputs(_("\nPattern not found\n"), stdout
);
1386 free(ctl
->previous_search
);
1387 ctl
->previous_search
= NULL
;
1389 more_error(ctl
, _("Pattern not found"));
1393 static char *find_editor(void)
1395 static char *editor
;
1397 editor
= getenv("VISUAL");
1398 if (editor
== NULL
|| *editor
== '\0')
1399 editor
= getenv("EDITOR");
1400 if (editor
== NULL
|| *editor
== '\0')
1405 static void runtime_usage(void)
1407 fputs(_("Most commands optionally preceded by integer argument k. "
1408 "Defaults in brackets.\n"
1409 "Star (*) indicates argument becomes new default.\n"), stdout
);
1410 print_separator('-', 79);
1413 ("<space> Display next k lines of text [current screen size]\n"
1414 "z Display next k lines of text [current screen size]*\n"
1415 "<return> Display next k lines of text [1]*\n"
1416 "d or ctrl-D Scroll k lines [current scroll size, initially 11]*\n"
1417 "q or Q or <interrupt> Exit from more\n"
1418 "s Skip forward k lines of text [1]\n"
1419 "f Skip forward k screenfuls of text [1]\n"
1420 "b or ctrl-B Skip backwards k screenfuls of text [1]\n"
1421 "' Go to place where previous search started\n"
1422 "= Display current line number\n"
1423 "/<regular expression> Search for kth occurrence of regular expression [1]\n"
1424 "n Search for kth occurrence of last r.e [1]\n"
1425 "!<cmd> or :!<cmd> Execute <cmd> in a subshell\n"
1426 "v Start up '%s' at current line\n"
1427 "ctrl-L Redraw screen\n"
1428 ":n Go to kth next file [1]\n"
1429 ":p Go to kth previous file [1]\n"
1430 ":f Display current file name and line number\n"
1431 ". Repeat previous command\n"),
1433 print_separator('-', 79);
1436 static void execute_editor(struct more_control
*ctl
, char *cmdbuf
, char *filename
)
1442 if ((ctl
->current_line
- ctl
->lines_per_screen
) < 1)
1445 n
= ctl
->current_line
- (ctl
->lines_per_screen
+ 1) / 2;
1446 editor
= find_editor();
1447 p
= strrchr(editor
, '/');
1453 * Earlier: call vi +n file. This also works for emacs.
1454 * POSIX: call vi -c n file (when editor is vi or ex).
1456 if (!strcmp(p
, "vi") || !strcmp(p
, "ex")) {
1457 sprintf(cmdbuf
, "-c %d", n
);
1460 sprintf(cmdbuf
, "+%d", n
);
1462 erase_to_col(ctl
, 0);
1463 printf("%s %s %s", editor
, cmdbuf
, ctl
->file_names
[ctl
->argv_position
]);
1466 execute(ctl
, filename
, editor
, editor
,
1468 ctl
->file_names
[ctl
->argv_position
], (char *)0);
1470 execute(ctl
, filename
, editor
, editor
,
1471 cmdbuf
, ctl
->file_names
[ctl
->argv_position
], (char *)0);
1474 static int skip_backwards(struct more_control
*ctl
, int nlines
)
1483 erase_to_col(ctl
, 0);
1485 if (ctl
->clear_line_ends
)
1486 putp(ctl
->erase_line
);
1487 printf(P_("...back %d page", "...back %d pages", nlines
), nlines
);
1488 if (ctl
->clear_line_ends
)
1489 putp(ctl
->erase_line
);
1492 initline
= ctl
->current_line
- ctl
->lines_per_screen
* (nlines
+ 1);
1493 if (!ctl
->no_scroll
)
1498 ctl
->current_line
= 0; /* skip_lines() will make current_line correct */
1499 skip_lines(ctl
, initline
);
1500 if (!ctl
->no_scroll
)
1501 retval
= ctl
->lines_per_screen
+ 1;
1503 retval
= ctl
->lines_per_screen
;
1507 static int skip_forwards(struct more_control
*ctl
, int nlines
, cc_t comchar
)
1514 nlines
*= ctl
->lines_per_screen
;
1516 erase_to_col(ctl
, 0);
1518 if (ctl
->clear_line_ends
)
1519 putp(ctl
->erase_line
);
1520 printf(P_("...skipping %d line",
1521 "...skipping %d lines", nlines
), nlines
);
1523 if (ctl
->clear_line_ends
)
1524 putp(ctl
->erase_line
);
1527 while (nlines
> 0) {
1528 while ((c
= more_getc(ctl
)) != '\n')
1531 ctl
->current_line
++;
1537 /* Read a command and do it. A command consists of an optional integer
1538 * argument followed by the command character. Return the number of
1539 * lines to display in the next screenful. If there is nothing more to
1540 * display in the current file, zero is returned. */
1541 static int more_key_command(struct more_control
*ctl
, char *filename
)
1548 char cmdbuf
[INIT_BUF
];
1549 if (!ctl
->report_errors
)
1550 output_prompt(ctl
, filename
);
1552 ctl
->report_errors
= 0;
1553 ctl
->search_called
= 0;
1555 if (more_poll(ctl
, -1) != 0)
1557 nlines
= read_number(ctl
, &comchar
);
1558 ctl
->run_previous_command
= colonch
= 0;
1559 if (comchar
== '.') { /* Repeat last command */
1560 ctl
->run_previous_command
++;
1561 comchar
= ctl
->last_key_command
;
1562 nlines
= ctl
->last_key_arg
;
1563 if (ctl
->last_key_command
== ':')
1564 colonch
= ctl
->last_colon_command
;
1566 ctl
->last_key_command
= comchar
;
1567 ctl
->last_key_arg
= nlines
;
1568 if (comchar
== ctl
->output_tty
.c_cc
[VERASE
]) {
1569 erase_to_col(ctl
, 0);
1570 output_prompt(ctl
, filename
);
1575 retval
= colon_command(ctl
, filename
, colonch
, nlines
);
1581 if (ctl
->no_tty_in
) {
1582 fprintf(stderr
, "\a");
1585 retval
= skip_backwards(ctl
, nlines
);
1591 nlines
= ctl
->lines_per_screen
;
1592 else if (comchar
== 'z')
1593 ctl
->lines_per_screen
= nlines
;
1600 ctl
->d_scroll_len
= nlines
;
1601 retval
= ctl
->d_scroll_len
;
1610 if (skip_forwards(ctl
, nlines
, comchar
))
1611 retval
= ctl
->lines_per_screen
;
1616 ctl
->lines_per_screen
= nlines
;
1623 if (!ctl
->no_tty_in
) {
1624 more_clear_screen(ctl
);
1625 more_fseek(ctl
, ctl
->screen_start
.row_num
);
1626 ctl
->current_line
= ctl
->screen_start
.line_num
;
1627 retval
= ctl
->lines_per_screen
;
1631 fprintf(stderr
, "\a");
1635 if (!ctl
->no_tty_in
) {
1636 erase_to_col(ctl
, 0);
1637 fputs(_("\n***Back***\n\n"), stdout
);
1638 more_fseek(ctl
, ctl
->context
.row_num
);
1639 ctl
->current_line
= ctl
->context
.line_num
;
1640 retval
= ctl
->lines_per_screen
;
1644 fprintf(stderr
, "\a");
1648 erase_to_col(ctl
, 0);
1649 ctl
->prompt_len
= printf("%d", ctl
->current_line
);
1653 if (!ctl
->previous_search
) {
1654 more_error(ctl
, _("No previous regular expression"));
1657 ctl
->run_previous_command
++;
1660 ctl
->search_called
= 1;
1663 erase_to_col(ctl
, 0);
1665 ctl
->prompt_len
= 1;
1667 if (ctl
->run_previous_command
) {
1668 fputc('\r', stderr
);
1669 search(ctl
, ctl
->previous_search
, nlines
);
1671 ttyin(ctl
, cmdbuf
, sizeof(cmdbuf
) - 2, '/');
1672 fputc('\r', stderr
);
1673 free(ctl
->previous_search
);
1674 ctl
->previous_search
= xstrdup(cmdbuf
);
1675 search(ctl
, cmdbuf
, nlines
);
1677 retval
= ctl
->lines_per_screen
- 1;
1681 run_shell(ctl
, filename
);
1686 more_clear_screen(ctl
);
1687 erase_to_col(ctl
, 0);
1689 output_prompt(ctl
, filename
);
1691 case 'v': /* This case should go right before default */
1692 if (!ctl
->no_tty_in
) {
1693 execute_editor(ctl
, cmdbuf
, filename
);
1698 if (ctl
->suppress_bell
) {
1699 erase_to_col(ctl
, 0);
1701 putp(ctl
->enter_std
);
1703 printf(_("[Press 'h' for instructions.]"))
1704 + 2 * ctl
->stdout_glitch
;
1706 putp(ctl
->exit_std
);
1709 fprintf(stderr
, "\a");
1716 ctl
->no_quit_dialog
= 1;
1720 /* Print out the contents of the file f, one screenful at a time. */
1721 static void screen(struct more_control
*ctl
, int num_lines
)
1725 int length
; /* length of current line */
1726 static int prev_len
= 1; /* length of previous line */
1729 while (num_lines
> 0 && !ctl
->is_paused
) {
1730 if ((nchars
= get_line(ctl
, &length
)) == EOF
) {
1731 if (ctl
->clear_line_ends
)
1732 putp(ctl
->clear_rest
);
1735 if (ctl
->squeeze_spaces
&& length
== 0 && prev_len
== 0)
1739 || ((ctl
->enter_std
&& *ctl
->enter_std
== ' ') && (ctl
->prompt_len
> 0)))
1740 erase_to_col(ctl
, 0);
1741 /* must clear before drawing line since tabs on
1742 * some terminals do not erase what they tab
1744 if (ctl
->clear_line_ends
)
1745 putp(ctl
->erase_line
);
1746 print_buf(ctl
, ctl
->line_buf
, length
);
1747 if (nchars
< ctl
->prompt_len
)
1748 erase_to_col(ctl
, nchars
);
1749 ctl
->prompt_len
= 0;
1750 if (nchars
< ctl
->num_columns
|| !ctl
->fold_long_lines
)
1751 print_buf(ctl
, "\n", 1); /* will turn off UL if necessary */
1754 if (ctl
->underline_state
) {
1755 putp(ctl
->exit_underline
);
1756 ctl
->underline_state
= 0;
1759 if ((c
= more_getc(ctl
)) == EOF
) {
1760 if (ctl
->clear_line_ends
)
1761 putp(ctl
->clear_rest
);
1765 if (ctl
->is_paused
&& ctl
->clear_line_ends
)
1766 putp(ctl
->clear_rest
);
1767 more_ungetc(ctl
, c
);
1770 if ((num_lines
= more_key_command(ctl
, NULL
)) == 0)
1772 } while (ctl
->search_called
&& !ctl
->previous_search
);
1773 if (ctl
->hard_tty
&& ctl
->prompt_len
> 0)
1774 erase_to_col(ctl
, 0);
1775 if (ctl
->no_scroll
&& num_lines
>= ctl
->lines_per_screen
) {
1776 if (ctl
->clear_line_ends
)
1779 more_clear_screen(ctl
);
1781 ctl
->screen_start
.line_num
= ctl
->current_line
;
1782 ctl
->screen_start
.row_num
= ctl
->file_position
;
1786 static void copy_file(FILE *f
)
1791 while ((sz
= fread(&buf
, sizeof(char), sizeof(buf
), f
)) > 0)
1792 fwrite(&buf
, sizeof(char), sz
, stdout
);
1796 static void display_file(struct more_control
*ctl
, char *initbuf
, int left
)
1798 if (!ctl
->current_file
)
1800 ctl
->context
.line_num
= ctl
->context
.row_num
= 0;
1801 ctl
->current_line
= 0;
1802 if (ctl
->first_file
) {
1803 ctl
->first_file
= 0;
1804 if (ctl
->search_at_start
) {
1805 free(ctl
->previous_search
);
1806 ctl
->previous_search
= xstrdup(initbuf
);
1807 search(ctl
, initbuf
, 1);
1810 } else if (ctl
->jump_at_start
)
1811 skip_lines(ctl
, ctl
->search_at_start
);
1812 } else if (ctl
->argv_position
< ctl
->num_files
&& !ctl
->no_tty_out
)
1814 more_key_command(ctl
, ctl
->file_names
[ctl
->argv_position
]);
1816 if ((ctl
->no_scroll
|| ctl
->clear_first
)
1817 && ctl
->file_size
!= ~((off_t
)0)) {
1818 if (ctl
->clear_line_ends
)
1821 more_clear_screen(ctl
);
1823 if (ctl
->print_banner
) {
1824 if (ctl
->bad_stdout
)
1825 erase_to_col(ctl
, 0);
1826 if (ctl
->clear_line_ends
)
1827 putp(ctl
->erase_line
);
1828 if (ctl
->prompt_len
> 14)
1829 erase_to_col(ctl
, 14);
1830 if (ctl
->clear_line_ends
)
1831 putp(ctl
->erase_line
);
1832 print_separator(':', 14);
1833 puts(ctl
->file_names
[ctl
->argv_position
]);
1834 if (ctl
->clear_line_ends
)
1835 putp(ctl
->erase_line
);
1836 print_separator(':', 14);
1837 if (left
> ctl
->lines_per_page
- 4)
1838 left
= ctl
->lines_per_page
- 4;
1840 if (ctl
->no_tty_out
)
1841 copy_file(ctl
->current_file
);
1846 fclose(ctl
->current_file
);
1847 ctl
->current_file
= NULL
;
1848 ctl
->screen_start
.line_num
= ctl
->screen_start
.row_num
= 0;
1849 ctl
->context
.line_num
= ctl
->context
.row_num
= 0L;
1852 static void initterm(struct more_control
*ctl
)
1859 #ifndef NON_INTERACTIVE_MORE
1860 ctl
->no_tty_out
= tcgetattr(STDOUT_FILENO
, &ctl
->output_tty
);
1862 ctl
->no_tty_in
= tcgetattr(STDIN_FILENO
, &ctl
->output_tty
);
1863 tcgetattr(STDERR_FILENO
, &ctl
->output_tty
);
1864 ctl
->original_tty
= ctl
->output_tty
;
1865 ctl
->hard_tabs
= (ctl
->output_tty
.c_oflag
& TABDLY
) != TAB3
;
1866 if (ctl
->no_tty_out
)
1869 ctl
->output_tty
.c_lflag
&= ~(ICANON
| ECHO
);
1870 ctl
->output_tty
.c_cc
[VMIN
] = 1;
1871 ctl
->output_tty
.c_cc
[VTIME
] = 0;
1872 ctl
->erase_previous_ok
= (ctl
->output_tty
.c_cc
[VERASE
] != 255);
1873 ctl
->erase_input_ok
= (ctl
->output_tty
.c_cc
[VKILL
] != 255);
1874 if ((term
= getenv("TERM")) == NULL
) {
1876 ctl
->enable_underlining
= 0;
1878 setupterm(term
, 1, &ret
);
1881 ctl
->enable_underlining
= 0;
1884 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0) {
1885 ctl
->lines_per_page
= tigetnum(TERM_LINES
);
1886 ctl
->num_columns
= tigetnum(TERM_COLS
);
1888 if ((ctl
->lines_per_page
= win
.ws_row
) == 0)
1889 ctl
->lines_per_page
= tigetnum(TERM_LINES
);
1890 if ((ctl
->num_columns
= win
.ws_col
) == 0)
1891 ctl
->num_columns
= tigetnum(TERM_COLS
);
1893 if ((ctl
->lines_per_page
<= 0) || tigetflag(TERM_HARD_COPY
)) {
1895 ctl
->lines_per_page
= LINES_PER_PAGE
;
1898 if (tigetflag(TERM_EAT_NEW_LINE
))
1899 /* Eat newline at last column + 1; dec, concept */
1901 if (ctl
->num_columns
<= 0)
1902 ctl
->num_columns
= NUM_COLUMNS
;
1904 ctl
->wrap_margin
= tigetflag(TERM_AUTO_RIGHT_MARGIN
);
1905 ctl
->bad_stdout
= tigetflag(TERM_CEOL
);
1906 ctl
->erase_line
= tigetstr(TERM_CLEAR_TO_LINE_END
);
1907 ctl
->clear
= tigetstr(TERM_CLEAR
);
1908 if ((ctl
->enter_std
= tigetstr(TERM_STANDARD_MODE
)) != NULL
) {
1909 ctl
->exit_std
= tigetstr(TERM_EXIT_STANDARD_MODE
);
1910 if (0 < tigetnum(TERM_STD_MODE_GLITCH
))
1911 ctl
->stdout_glitch
= 1;
1915 * Set up for underlining. Some terminals don't need it, others have
1916 * start/stop sequences, still others have an underline char sequence
1917 * which is assumed to move the cursor forward one character. If
1918 * underline sequence isn't available, settle for standout sequence.
1920 if (tigetflag(TERM_UNDERLINE
) || tigetflag(TERM_OVER_STRIKE
))
1921 ctl
->enable_underlining
= 0;
1922 ctl
->enter_underline
= tigetstr(TERM_ENTER_UNDERLINE
);
1923 ctl
->exit_underline
= tigetstr(TERM_EXIT_UNDERLINE
);
1924 ctl
->underline_ch
= tigetstr(TERM_UNDERLINE_CHAR
);
1925 if (!ctl
->underline_ch
) {
1926 ctl
->underline_ch
= "";
1927 if (!ctl
->enter_underline
|| !ctl
->exit_underline
) {
1928 if (!ctl
->enter_std
|| !ctl
->exit_std
) {
1929 ctl
->enter_underline
= "";
1930 ctl
->exit_underline
= "";
1932 ctl
->enter_underline
= ctl
->enter_std
;
1933 ctl
->exit_underline
= ctl
->exit_std
;
1934 ctl
->underline_glitch
= ctl
->stdout_glitch
;
1937 ctl
->underline_glitch
= 0;
1940 cursor_addr
= tigetstr(TERM_HOME
);
1941 if (cursor_addr
== NULL
|| *cursor_addr
== '\0') {
1942 cursor_addr
= tigetstr(TERM_CURSOR_ADDRESS
);
1944 cursor_addr
= tparm(cursor_addr
, 0, 0);
1947 ctl
->go_home
= xstrdup(cursor_addr
);
1949 if ((ctl
->move_line_down
= tigetstr(TERM_LINE_DOWN
)) == NULL
)
1950 ctl
->move_line_down
= BACKSPACE
;
1951 ctl
->clear_rest
= tigetstr(TERM_CLEAR_TO_SCREEN_END
);
1952 if ((ctl
->backspace_ch
= tigetstr(TERM_BACKSPACE
)) == NULL
)
1953 ctl
->backspace_ch
= BACKSPACE
;
1955 if ((ctl
->shell
= getenv("SHELL")) == NULL
)
1956 ctl
->shell
= _PATH_BSHELL
;
1959 int main(int argc
, char **argv
)
1964 int start_at_line
= 0;
1965 char *initbuf
= NULL
;
1966 struct more_control ctl
= {
1968 .fold_long_lines
= 1,
1969 .no_quit_dialog
= 1,
1970 .stop_after_formfeed
= 1,
1971 .enable_underlining
= 1,
1973 .lines_per_page
= LINES_PER_PAGE
,
1974 .num_columns
= NUM_COLUMNS
,
1975 .d_scroll_len
= SCROLL_LEN
,
1979 setlocale(LC_ALL
, "");
1980 bindtextdomain(PACKAGE
, LOCALEDIR
);
1981 textdomain(PACKAGE
);
1982 close_stdout_atexit();
1985 /* first arg may be one of our standard longopts */
1986 if (!strcmp(argv
[1], "--help"))
1988 if (!strcmp(argv
[1], "--version"))
1989 print_version(EXIT_SUCCESS
);
1992 ctl
.num_files
= argc
;
1993 ctl
.file_names
= argv
;
1994 setlocale(LC_ALL
, "");
1997 /* Auto set no scroll on when binary is called page */
1998 if (!(strcmp(program_invocation_short_name
, "page")))
2001 prepare_line_buffer(&ctl
);
2003 ctl
.d_scroll_len
= ctl
.lines_per_page
/ 2 - 1;
2004 if (ctl
.d_scroll_len
<= 0)
2005 ctl
.d_scroll_len
= 1;
2007 if ((s
= getenv("MORE")) != NULL
)
2008 arg_parser(&ctl
, s
);
2010 while (--ctl
.num_files
> 0) {
2011 if ((chr
= (*++ctl
.file_names
)[0]) == '-') {
2012 arg_parser(&ctl
, *ctl
.file_names
+ 1);
2013 } else if (chr
== '+') {
2014 s
= *ctl
.file_names
;
2016 ctl
.search_at_start
= 1;
2017 initbuf
= xstrdup(s
+ 1);
2019 ctl
.jump_at_start
= 1;
2020 for (start_at_line
= 0; *s
!= '\0'; s
++)
2023 start_at_line
* 10 + *s
- '0';
2029 /* allow clear_line_ends only if go_home and erase_line and clear_rest strings are
2030 * defined, and in that case, make sure we are in no_scroll mode */
2031 if (ctl
.clear_line_ends
) {
2032 if ((ctl
.go_home
== NULL
) || (*ctl
.go_home
== '\0') ||
2033 (ctl
.erase_line
== NULL
) || (*ctl
.erase_line
== '\0') ||
2034 (ctl
.clear_rest
== NULL
) || (*ctl
.clear_rest
== '\0'))
2035 ctl
.clear_line_ends
= 0;
2039 if (ctl
.lines_per_screen
== 0)
2040 ctl
.lines_per_screen
= ctl
.lines_per_page
- 1;
2041 left
= ctl
.lines_per_screen
;
2042 if (ctl
.num_files
> 1)
2043 ctl
.print_banner
= 1;
2044 if (!ctl
.no_tty_in
&& ctl
.num_files
== 0) {
2045 warnx(_("bad usage"));
2046 errtryhelp(EXIT_FAILURE
);
2048 ctl
.current_file
= stdin
;
2049 if (!ctl
.no_tty_out
) {
2050 if (signal(SIGTSTP
, SIG_IGN
) == SIG_DFL
) {
2051 ctl
.catch_suspend
++;
2053 tcsetattr(STDERR_FILENO
, TCSANOW
, &ctl
.output_tty
);
2055 sigemptyset(&ctl
.sigset
);
2056 sigaddset(&ctl
.sigset
, SIGINT
);
2057 sigaddset(&ctl
.sigset
, SIGQUIT
);
2058 sigaddset(&ctl
.sigset
, SIGTSTP
);
2059 sigaddset(&ctl
.sigset
, SIGWINCH
);
2060 sigprocmask(SIG_BLOCK
, &ctl
.sigset
, NULL
);
2061 ctl
.sigfd
= signalfd(-1, &ctl
.sigset
, SFD_CLOEXEC
);
2062 if (ctl
.no_tty_in
) {
2066 ctl
.current_file
= stdin
;
2067 display_file(&ctl
, initbuf
, left
);
2070 ctl
.print_banner
= 1;
2074 while (ctl
.argv_position
< ctl
.num_files
) {
2075 checkf(&ctl
, ctl
.file_names
[ctl
.argv_position
]);
2076 display_file(&ctl
, initbuf
, left
);
2078 ctl
.argv_position
++;
2080 ctl
.clear_line_ends
= 0;