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 MERCHANTIBILITY 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>
58 #include <sys/ioctl.h>
64 #if defined(HAVE_NCURSESW_TERM_H)
65 # include <ncursesw/term.h>
66 #elif defined(HAVE_NCURSES_TERM_H)
67 # include <ncurses/term.h>
68 #elif defined(HAVE_TERM_H)
76 #include "closestream.h"
79 # define NON_INTERACTIVE_MORE 1
82 #define VI "vi" /* found on the user's path */
87 #define RINGBELL '\007'
89 #define MIN_LINE_SZ 256 /* minimal line_buf buffer size */
90 #define ctrl(letter) (letter & 077)
95 #define LINES_PER_PAGE 24
96 #define NUM_COLUMNS 80
97 #define TERMINAL_BUF 4096
99 #define SHELL_LINE 1000
100 #define COMMAND_BUF 200
101 #define REGERR_BUF NUM_COLUMNS
103 #define TERM_AUTO_RIGHT_MARGIN "am"
104 #define TERM_CEOL "xhp"
105 #define TERM_CLEAR "clear"
106 #define TERM_CLEAR_TO_LINE_END "el"
107 #define TERM_CLEAR_TO_SCREEN_END "ed"
108 #define TERM_COLS "cols"
109 #define TERM_CURSOR_ADDRESS "cup"
110 #define TERM_EAT_NEW_LINE "xenl"
111 #define TERM_ENTER_UNDERLINE "smul"
112 #define TERM_EXIT_STANDARD_MODE "rmso"
113 #define TERM_EXIT_UNDERLINE "rmul"
114 #define TERM_HARD_COPY "hc"
115 #define TERM_HOME "home"
116 #define TERM_LINE_DOWN "cud1"
117 #define TERM_LINES "lines"
118 #define TERM_OVER_STRIKE "os"
119 #define TERM_PAD_CHAR "pad"
120 #define TERM_STANDARD_MODE "smso"
121 #define TERM_STD_MODE_GLITCH "xmc"
122 #define TERM_UNDERLINE_CHAR "uc"
123 #define TERM_UNDERLINE "ul"
125 struct more_control
{
126 struct termios output_tty
; /* output terminal */
127 struct termios original_tty
; /* original terminal settings */
128 long file_position
; /* file position */
129 long file_size
; /* file size */
130 int argv_position
; /* argv[] position */
131 int lines_per_screen
; /* screen size in lines */
132 int d_scroll_len
; /* number of lines scrolled by 'd' */
133 int prompt_len
; /* message prompt length */
134 int current_line
; /* line we are currently at */
135 char **file_names
; /* The list of file names */
136 int num_files
; /* Number of files left to process */
137 char *shell
; /* name of the shell to use */
138 int previous_shell
; /* does previous shell command exist */
139 sigjmp_buf destination
; /* siglongjmp() destination */
140 char *line_buf
; /* line buffer */
141 size_t line_sz
; /* size of line_buf buffer */
142 int lines_per_page
; /* lines per page */
143 char *clear
; /* clear screen */
144 char *erase_line
; /* erase line */
145 char *enter_std
; /* enter standout mode */
146 char *exit_std
; /* exit standout mode */
147 char *enter_underline
; /* enter underline mode */
148 char *exit_underline
; /* exit underline mode */
149 char *underline_ch
; /* underline character */
150 char *backspace_ch
; /* backspace character */
151 char *go_home
; /* go to home */
152 char *cursor_addr
; /* cursor movement */
153 char home_position
[40]; /* contains cursor movement to home */
154 char *clear_rest
; /* clear rest of screen */
155 int num_columns
; /* number of columns */
156 char *previous_search
; /* previous search() buf[] item */
158 long row_num
; /* row number */
159 long line_num
; /* line number */
162 int last_key_command
; /* previous more key command */
163 int last_key_arg
; /* previous key command argument */
164 int last_colon_command
; /* is a colon-prefixed key command */
165 char shell_line
[SHELL_LINE
];
167 bad_stdout
:1, /* true if overwriting does not turn off standout */
168 catch_suspend
:1, /* we should catch the SIGTSTP signal */
169 clear_line_ends
:1, /* do not scroll, paint each screen from the top */
170 dumb_tty
:1, /* is terminal type known */
171 eat_newline
:1, /* is newline ignored after 80 cols */
172 enable_underlining
:1, /* underline as best we can */
173 erase_input_ok
:1, /* is erase input supported */
174 erase_previous_ok
:1, /* is erase previous supported */
175 first_file
:1, /* is the input file the first in list */
176 fold_long_lines
:1, /* fold long lines */
177 hard_tabs
:1, /* print spaces instead of '\t' */
178 hard_tty
:1, /* is this hard copy terminal (a printer or such) */
179 is_paused
:1, /* is output paused */
180 no_quit_dialog
:1, /* suppress quit dialog */
181 no_scroll
:1, /* do not scroll, clear the screen and then display text */
182 no_tty_in
:1, /* is input in interactive mode */
183 no_tty_out
:1, /* is output in interactive mode */
184 report_errors
:1, /* is an error reported */
185 run_previous_command
:1, /* run previous key command */
186 squeeze_spaces
:1, /* suppress white space */
187 starting_up
:1, /* is startup completed */
188 stdout_glitch
:1, /* terminal has standout mode glitch */
189 stop_after_formfeed
:1, /* stop after form feeds */
190 suppress_bell
:1, /* suppress bell */
191 underline_glitch
:1, /* terminal has underline mode glitch */
192 underline_state
:1, /* current UL state */
193 waiting_input
:1, /* is waiting user input */
194 within_file
:1, /* true if we are within a file, false if we are between files */
195 wrap_margin
:1; /* set if automargins */
198 /* FIXME: global_ctl is used in signal handlers. */
199 struct more_control
*global_ctl
;
201 static void __attribute__((__noreturn__
)) usage(void)
204 fputs(USAGE_HEADER
, out
);
205 fprintf(out
, _(" %s [options] <file>...\n"), program_invocation_short_name
);
207 fputs(USAGE_SEPARATOR
, out
);
208 fputs(_("A file perusal filter for CRT viewing.\n"), out
);
210 fputs(USAGE_OPTIONS
, out
);
211 fputs(_(" -d display help instead of ringing bell\n"), out
);
212 fputs(_(" -f count logical rather than screen lines\n"), out
);
213 fputs(_(" -l suppress pause after form feed\n"), out
);
214 fputs(_(" -c do not scroll, display text and clean line ends\n"), out
);
215 fputs(_(" -p do not scroll, clean screen and display text\n"), out
);
216 fputs(_(" -s squeeze multiple blank lines into one\n"), out
);
217 fputs(_(" -u suppress underlining\n"), out
);
218 fputs(_(" -<number> the number of lines per screenful\n"), out
);
219 fputs(_(" +<number> display file beginning from line number\n"), out
);
220 fputs(_(" +/<string> display file beginning from search string match\n"), out
);
222 fputs(USAGE_SEPARATOR
, out
);
223 printf( " --help %s\n", USAGE_OPTSTR_HELP
);
224 printf( " -V, --version %s\n", USAGE_OPTSTR_VERSION
);
225 printf(USAGE_MAN_TAIL("more(1)"));
230 static void arg_parser(struct more_control
*ctl
, char *s
)
247 ctl
->lines_per_screen
= 0;
250 ctl
->lines_per_screen
= ctl
->lines_per_screen
* 10 + *s
- '0';
253 ctl
->suppress_bell
= 1;
256 ctl
->stop_after_formfeed
= 0;
259 ctl
->fold_long_lines
= 0;
265 ctl
->clear_line_ends
= 1;
268 ctl
->squeeze_spaces
= 1;
271 ctl
->enable_underlining
= 0;
278 printf(UTIL_LINUX_VERSION
);
282 warnx(_("unknown option -%s"), s
);
283 errtryhelp(EXIT_FAILURE
);
290 static void more_fseek(struct more_control
*ctl
, FILE *stream
, long pos
)
292 ctl
->file_position
= pos
;
293 fseek(stream
, pos
, 0);
296 static int more_getc(struct more_control
*ctl
, FILE *stream
)
298 ctl
->file_position
++;
302 static int more_ungetc(struct more_control
*ctl
, int c
, FILE *stream
)
304 ctl
->file_position
--;
305 return ungetc(c
, stream
);
309 * check for file magic numbers. This code would best be shared
310 * with the file(1) program or, perhaps, more should not try to be
312 static int check_magic(FILE *f
, char *fs
)
314 signed char twobytes
[2];
316 /* don't try to look ahead if the input is unseekable */
317 if (fseek(f
, 0L, SEEK_SET
))
320 if (fread(twobytes
, 2, 1, f
) == 1) {
321 switch (twobytes
[0] + (twobytes
[1] << 8)) {
322 case 0407: /* a.out obj */
323 case 0410: /* a.out exec */
324 case 0413: /* a.out demand exec */
328 case 0x457f: /* simple ELF detection */
329 printf(_("\n******** %s: Not a text file ********\n\n"),
334 fseek(f
, 0L, SEEK_SET
); /* rewind() not necessary */
338 /* Check whether the file named by fs is an ASCII file which the user may
339 * access. If it is, return the opened file. Otherwise return NULL. */
340 static FILE *checkf(struct more_control
*ctl
, char *fs
, int *clearfirst
)
346 if (stat(fs
, &st
) == -1) {
348 if (ctl
->clear_line_ends
)
349 putp(ctl
->erase_line
);
350 warn(_("stat of %s failed"), fs
);
353 if ((st
.st_mode
& S_IFMT
) == S_IFDIR
) {
354 printf(_("\n*** %s: directory ***\n\n"), fs
);
357 ctl
->current_line
= 0;
358 ctl
->file_position
= 0;
359 if ((f
= fopen(fs
, "r")) == NULL
) {
361 warn(_("cannot open %s"), fs
);
364 if (check_magic(f
, fs
)) {
368 fcntl(fileno(f
), F_SETFD
, FD_CLOEXEC
);
369 c
= more_getc(ctl
, f
);
370 *clearfirst
= (c
== '\f');
371 more_ungetc(ctl
, c
, f
);
372 if ((ctl
->file_size
= st
.st_size
) == 0)
373 ctl
->file_size
= LONG_MAX
;
377 static void prepare_line_buffer(struct more_control
*ctl
)
379 size_t sz
= ctl
->num_columns
* 4;
381 if (ctl
->line_sz
>= sz
)
384 if (sz
< MIN_LINE_SZ
)
387 /* alloc sz and extra space for \n\0 */
388 ctl
->line_buf
= xrealloc(ctl
->line_buf
, sz
+ 2);
392 /* Get a logical line */
393 static int get_line(struct more_control
*ctl
, FILE *f
, int *length
)
398 static int column_wrap
;
404 mbstate_t state
, state_bak
; /* Current status of the stream. */
405 char mbc
[MB_LEN_MAX
]; /* Buffer for one multibyte char. */
406 size_t mblength
; /* Byte length of multibyte char. */
407 size_t mbc_pos
= 0; /* Position of the MBC. */
408 int use_mbc_buffer_flag
= 0; /* If 1, mbc has data. */
409 int break_flag
= 0; /* If 1, exit while(). */
410 long file_position_bak
= ctl
->file_position
;
412 memset(&state
, '\0', sizeof(mbstate_t));
415 prepare_line_buffer(ctl
);
419 c
= more_getc(ctl
, f
);
420 if (column_wrap
&& c
== '\n') {
422 c
= more_getc(ctl
, f
);
424 while (p
< &ctl
->line_buf
[ctl
->line_sz
- 1]) {
426 if (ctl
->fold_long_lines
&& use_mbc_buffer_flag
&& MB_CUR_MAX
> 1) {
427 use_mbc_buffer_flag
= 0;
431 mblength
= mbrtowc(&wc
, mbc
, mbc_pos
, &state
);
434 case (size_t)-2: /* Incomplete multibyte character. */
435 use_mbc_buffer_flag
= 1;
439 case (size_t)-1: /* Invalid as a multibyte character. */
444 if (column
>= ctl
->num_columns
) {
445 more_fseek(ctl
, f
, file_position_bak
);
447 memmove(mbc
, mbc
+ 1, --mbc_pos
);
456 wc_width
= wcwidth(wc
);
457 if (column
+ wc_width
> ctl
->num_columns
) {
458 more_fseek(ctl
, f
, file_position_bak
);
461 for (i
= 0; p
< &ctl
->line_buf
[ctl
->line_sz
- 1] &&
469 if (break_flag
|| column
>= ctl
->num_columns
)
472 c
= more_getc(ctl
, f
);
475 #endif /* HAVE_WIDECHAR */
477 if (p
> ctl
->line_buf
) {
479 *length
= p
- ctl
->line_buf
;
482 *length
= p
- ctl
->line_buf
;
492 if (!ctl
->hard_tabs
|| (column
< ctl
->prompt_len
&& !ctl
->hard_tty
)) {
493 if (ctl
->hard_tabs
&& ctl
->erase_line
&& !ctl
->dumb_tty
) {
494 column
= 1 + (column
| 7);
495 putp(ctl
->erase_line
);
498 for (--p
; p
< &ctl
->line_buf
[ctl
->line_sz
- 1];) {
500 if ((++column
& 7) == 0)
503 if (column
>= ctl
->prompt_len
)
507 column
= 1 + (column
| 7);
508 } else if (c
== '\b' && column
> 0) {
510 } else if (c
== '\r') {
511 int next
= more_getc(ctl
, f
);
517 more_ungetc(ctl
, c
, f
);
519 } else if (c
== '\f' && ctl
->stop_after_formfeed
) {
524 } else if (c
== EOF
) {
525 *length
= p
- ctl
->line_buf
;
529 if (ctl
->fold_long_lines
&& MB_CUR_MAX
> 1) {
530 memset(mbc
, '\0', MB_LEN_MAX
);
535 mblength
= mbrtowc(&wc
, mbc
, mbc_pos
, &state
);
536 /* The value of mblength is always less than 2 here. */
540 file_position_bak
= ctl
->file_position
- 1;
542 use_mbc_buffer_flag
= 1;
551 wc_width
= wcwidth(wc
);
556 #endif /* HAVE_WIDECHAR */
563 if (column
>= ctl
->num_columns
&& ctl
->fold_long_lines
)
566 if (use_mbc_buffer_flag
== 0 && p
>= &ctl
->line_buf
[ctl
->line_sz
- 1 - 4])
567 /* don't read another char if there is no space for
568 * whole multibyte sequence */
571 c
= more_getc(ctl
, f
);
573 if (column
>= ctl
->num_columns
&& ctl
->num_columns
> 0) {
574 if (!ctl
->wrap_margin
) {
578 column_wrap
= column
== ctl
->num_columns
&& ctl
->fold_long_lines
;
579 if (column_wrap
&& ctl
->eat_newline
&& ctl
->wrap_margin
) {
580 *p
++ = '\n'; /* simulate normal wrap */
582 *length
= p
- ctl
->line_buf
;
587 /* Erase the rest of the prompt, assuming we are starting at column col. */
588 static void erase_prompt(struct more_control
*ctl
, int col
)
591 if (ctl
->prompt_len
== 0)
598 if (!ctl
->dumb_tty
&& ctl
->erase_line
)
599 putp(ctl
->erase_line
);
601 printf("%*s", ctl
->prompt_len
- col
, "");
607 static UL_ASAN_BLACKLIST
size_t xmbrtowc(wchar_t *wc
, const char *s
, size_t n
,
610 const size_t mblength
= mbrtowc(wc
, s
, n
, mbstate
);
611 if (mblength
== (size_t)-2 || mblength
== (size_t)-1)
617 static int would_underline(char *s
, int n
)
621 if ((s
[0] == '_' && s
[1] == '\b') || (s
[1] == '\b' && s
[2] == '_'))
626 /* Print a buffer of n characters */
627 static void print_buf(struct more_control
*ctl
, char *s
, int n
)
629 char c
; /* next output character */
630 int state
; /* next output char's UL state */
633 if (!ctl
->enable_underlining
)
636 if (*s
== ' ' && ctl
->underline_state
== 0 && ctl
->underline_glitch
637 && would_underline(s
+ 1, n
- 1)) {
641 if ((state
= would_underline(s
, n
)) != 0) {
642 c
= (*s
== '_') ? s
[2] : *s
;
647 if (state
!= ctl
->underline_state
) {
648 if (c
== ' ' && state
== 0 && ctl
->underline_glitch
649 && would_underline(s
, n
- 1))
652 putp(state
? ctl
->enter_underline
: ctl
->exit_underline
);
654 if (c
!= ' ' || ctl
->underline_state
== 0 || state
!= 0
655 || ctl
->underline_glitch
== 0)
661 memset(&mbstate
, '\0', sizeof(mbstate_t));
664 mblength
= xmbrtowc(&wc
, s
, n
, &mbstate
);
671 #endif /* HAVE_WIDECHAR */
672 if (state
&& *ctl
->underline_ch
) {
673 fputs(ctl
->backspace_ch
, stdout
);
674 putp(ctl
->underline_ch
);
676 ctl
->underline_state
= state
;
680 /* Erase the current line entirely */
681 static void kill_line(struct more_control
*ctl
)
683 erase_prompt(ctl
, 0);
684 if (!ctl
->erase_line
|| ctl
->dumb_tty
)
688 static void output_prompt(struct more_control
*ctl
, char *filename
)
690 if (ctl
->clear_line_ends
)
691 putp(ctl
->erase_line
);
692 else if (ctl
->prompt_len
> 0)
694 if (!ctl
->hard_tty
) {
696 if (ctl
->enter_std
&& ctl
->exit_std
) {
697 putp(ctl
->enter_std
);
698 ctl
->prompt_len
+= (2 * ctl
->stdout_glitch
);
700 if (ctl
->clear_line_ends
)
701 putp(ctl
->erase_line
);
702 ctl
->prompt_len
+= printf(_("--More--"));
703 if (filename
!= NULL
) {
704 ctl
->prompt_len
+= printf(_("(Next file: %s)"), filename
);
705 } else if (!ctl
->no_tty_in
) {
708 (int)((ctl
->file_position
* 100) / ctl
->file_size
));
710 if (ctl
->suppress_bell
) {
712 printf(_("[Press space to continue, 'q' to quit.]"));
714 if (ctl
->enter_std
&& ctl
->exit_std
)
716 if (ctl
->clear_line_ends
)
717 putp(ctl
->clear_rest
);
720 fputc(RINGBELL
, stderr
);
721 ctl
->waiting_input
++;
724 static void reset_tty(void)
726 if (global_ctl
->no_tty_out
)
728 if (global_ctl
->underline_state
) {
729 putp(global_ctl
->exit_underline
);
731 global_ctl
->underline_state
= 0;
733 global_ctl
->output_tty
.c_lflag
|= ICANON
| ECHO
;
734 global_ctl
->output_tty
.c_cc
[VMIN
] = global_ctl
->original_tty
.c_cc
[VMIN
];
735 global_ctl
->output_tty
.c_cc
[VTIME
] = global_ctl
->original_tty
.c_cc
[VTIME
];
736 tcsetattr(STDERR_FILENO
, TCSANOW
, &global_ctl
->original_tty
);
739 /* Clean up terminal state and exit. Also come here if interrupt signal received */
740 static void __attribute__((__noreturn__
)) more_exit(int dummy
__attribute__((__unused__
)))
742 /* May be executed as a signal handler as well as by main process.
744 * The _exit() may wait for pending I/O for really long time, be sure
745 * that signal handler is not executed in this time to avoid double
746 * de-initialization (free() calls, etc.).
748 signal(SIGINT
, SIG_IGN
);
751 if (global_ctl
->clear_line_ends
) {
753 putp(global_ctl
->erase_line
);
755 } else if (!global_ctl
->clear_line_ends
&& (global_ctl
->prompt_len
> 0)) {
756 kill_line(global_ctl
);
760 free(global_ctl
->previous_search
);
761 free(global_ctl
->line_buf
);
765 static int read_user_input(struct more_control
*ctl
)
770 if (read(STDERR_FILENO
, &c
, 1) <= 0) {
774 c
= ctl
->output_tty
.c_cc
[VKILL
];
779 /* Read a decimal number from the terminal. Set cmd to the non-digit
780 * which terminates the number. */
781 static int read_number(struct more_control
*ctl
, char *cmd
)
787 ch
= ctl
->output_tty
.c_cc
[VKILL
];
789 ch
= read_user_input(ctl
);
791 i
= i
* 10 + ch
- '0';
792 else if ((cc_t
) ch
== ctl
->output_tty
.c_cc
[VKILL
])
802 /* Change displayed file from command line list to next nskip, where nskip
803 * is relative position in argv and can be negative, that is a previous
805 static void change_file(struct more_control
*ctl
, int nskip
)
810 if (ctl
->argv_position
+ nskip
> ctl
->num_files
- 1)
811 nskip
= ctl
->num_files
- ctl
->argv_position
- 1;
812 } else if (ctl
->within_file
)
813 ctl
->argv_position
++;
814 ctl
->argv_position
+= nskip
;
815 if (ctl
->argv_position
< 0)
816 ctl
->argv_position
= 0;
817 puts(_("\n...Skipping "));
818 if (ctl
->clear_line_ends
)
819 putp(ctl
->erase_line
);
821 fputs(_("...Skipping to file "), stdout
);
823 fputs(_("...Skipping back to file "), stdout
);
824 puts(ctl
->file_names
[ctl
->argv_position
]);
825 if (ctl
->clear_line_ends
)
826 putp(ctl
->erase_line
);
828 ctl
->argv_position
--;
831 static void show(struct more_control
*ctl
, char c
)
833 if ((c
< ' ' && c
!= '\n' && c
!= ESC
) || c
== RUBOUT
) {
834 c
+= (c
== RUBOUT
) ? -0100 : 0100;
835 fputs(CARAT
, stderr
);
842 static void more_error(struct more_control
*ctl
, char *mess
)
844 if (ctl
->clear_line_ends
)
845 putp(ctl
->erase_line
);
848 ctl
->prompt_len
+= strlen(mess
);
849 if (ctl
->enter_std
&& ctl
->exit_std
) {
850 putp(ctl
->enter_std
);
856 ctl
->report_errors
++;
857 siglongjmp(ctl
->destination
, 1);
860 static void erase_one_column(struct more_control
*ctl
)
862 if (ctl
->erase_previous_ok
)
868 static void ttyin(struct more_control
*ctl
, char buf
[], int nmax
, char pchar
)
877 while (sp
- buf
< nmax
) {
878 if (ctl
->prompt_len
> maxlen
)
879 maxlen
= ctl
->prompt_len
;
880 c
= read_user_input(ctl
);
883 } else if (((cc_t
) c
== ctl
->output_tty
.c_cc
[VERASE
]) && !slash
) {
886 if (MB_CUR_MAX
> 1) {
888 size_t pos
= 0, mblength
;
889 mbstate_t state
, state_bak
;
891 memset(&state
, '\0', sizeof(mbstate_t));
896 mbrtowc(&wc
, buf
+ pos
,
899 state
= (mblength
== (size_t)-2
901 (size_t)-1) ? state_bak
904 (mblength
== (size_t)-2
905 || mblength
== (size_t)-1
909 if (buf
+ pos
+ mblength
>= sp
)
916 erase_one_column(ctl
);
919 wc_width
= wcwidth(wc
);
924 erase_one_column(ctl
);
933 #endif /* HAVE_WIDECHAR */
936 erase_one_column(ctl
);
940 if ((*sp
< ' ' && *sp
!= '\n') || *sp
== RUBOUT
) {
942 erase_one_column(ctl
);
946 if (!ctl
->erase_line
)
947 ctl
->prompt_len
= maxlen
;
948 siglongjmp(ctl
->destination
, 1);
950 } else if (((cc_t
) c
== ctl
->output_tty
.c_cc
[VKILL
]) && !slash
) {
959 erase_prompt(ctl
, 1);
960 else if (ctl
->erase_input_ok
)
961 while (ctl
->prompt_len
-- > 1)
969 if (slash
&& ((cc_t
) c
== ctl
->output_tty
.c_cc
[VKILL
]
970 || (cc_t
) c
== ctl
->output_tty
.c_cc
[VERASE
])) {
971 erase_one_column(ctl
);
977 if ((c
< ' ' && c
!= '\n' && c
!= ESC
) || c
== RUBOUT
) {
978 c
+= (c
== RUBOUT
) ? -0100 : 0100;
979 fputs(CARAT
, stderr
);
982 if (c
!= '\n' && c
!= ESC
) {
989 if (!ctl
->erase_line
)
990 ctl
->prompt_len
= maxlen
;
991 if (sp
- buf
>= nmax
- 1)
992 more_error(ctl
, _("Line too long"));
995 static int expand(struct more_control
*ctl
, char **outbuf
, char *inbuf
)
1002 int tempsz
, xtra
, offset
;
1004 xtra
= strlen(ctl
->file_names
[ctl
->argv_position
]) + strlen(ctl
->shell_line
) + 1;
1005 tempsz
= 200 + xtra
;
1006 temp
= xmalloc(tempsz
);
1009 while ((c
= *inpstr
++) != '\0') {
1010 offset
= outstr
- temp
;
1011 if (tempsz
- offset
- 1 < xtra
) {
1012 tempsz
+= 200 + xtra
;
1013 temp
= xrealloc(temp
, tempsz
);
1014 outstr
= temp
+ offset
;
1018 if (!ctl
->no_tty_in
) {
1019 strcpy(outstr
, ctl
->file_names
[ctl
->argv_position
]);
1020 outstr
+= strlen(ctl
->file_names
[ctl
->argv_position
]);
1026 if (!ctl
->previous_shell
)
1028 ("No previous command to substitute for"));
1029 strcpy(outstr
, ctl
->shell_line
);
1030 outstr
+= strlen(ctl
->shell_line
);
1034 if (*inpstr
== '%' || *inpstr
== '!') {
1035 *outstr
++ = *inpstr
++;
1048 static void set_tty(struct more_control
*ctl
)
1050 ctl
->output_tty
.c_lflag
&= ~(ICANON
| ECHO
);
1051 ctl
->output_tty
.c_cc
[VMIN
] = 1; /* read at least 1 char */
1052 ctl
->output_tty
.c_cc
[VTIME
] = 0; /* no timeout */
1053 tcsetattr(STDERR_FILENO
, TCSANOW
, &ctl
->output_tty
);
1056 /* Come here if a quit signal is received */
1057 static void sigquit_handler(int dummy
__attribute__((__unused__
)))
1059 signal(SIGQUIT
, SIG_IGN
);
1060 if (!global_ctl
->waiting_input
) {
1062 if (!global_ctl
->starting_up
) {
1063 signal(SIGQUIT
, sigquit_handler
);
1064 siglongjmp(global_ctl
->destination
, 1);
1066 global_ctl
->is_paused
= 1;
1067 } else if (!global_ctl
->suppress_bell
&& global_ctl
->no_quit_dialog
) {
1068 global_ctl
->prompt_len
+= fprintf(stderr
, _("[Use q or Q to quit]"));
1069 global_ctl
->no_quit_dialog
= 0;
1071 signal(SIGQUIT
, sigquit_handler
);
1074 /* Come here when we get a suspend signal from the terminal */
1075 static void sigtstp_handler(int dummy
__attribute__((__unused__
)))
1077 sigset_t signals
, oldmask
;
1079 /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
1080 signal(SIGTTOU
, SIG_IGN
);
1083 signal(SIGTTOU
, SIG_DFL
);
1084 /* Send the TSTP signal to suspend our process group */
1085 signal(SIGTSTP
, SIG_DFL
);
1087 /* unblock SIGTSTP or we won't be able to suspend ourself */
1088 sigemptyset(&signals
);
1089 sigaddset(&signals
, SIGTSTP
);
1090 sigprocmask(SIG_UNBLOCK
, &signals
, &oldmask
);
1093 /* Pause for station break */
1095 sigprocmask(SIG_SETMASK
, &oldmask
, NULL
);
1098 signal(SIGTSTP
, sigtstp_handler
);
1099 set_tty(global_ctl
);
1100 if (global_ctl
->waiting_input
)
1101 siglongjmp(global_ctl
->destination
, 1);
1104 static void execute(struct more_control
*ctl
, char *filename
, char *cmd
, ...)
1115 for (n
= 10; (id
= fork()) < 0 && n
> 0; n
--)
1119 if (!isatty(STDIN_FILENO
)) {
1120 close(STDIN_FILENO
);
1121 open("/dev/tty", 0);
1124 va_start(argp
, cmd
);
1125 arg
= va_arg(argp
, char *);
1129 arg
= va_arg(argp
, char *);
1133 args
= alloca(sizeof(char *) * (argcount
+ 1));
1134 args
[argcount
] = NULL
;
1136 va_start(argp
, cmd
);
1137 arg
= va_arg(argp
, char *);
1140 args
[argcount
] = arg
;
1142 arg
= va_arg(argp
, char *);
1148 fputs(_("exec failed\n"), stderr
);
1149 exit(errsv
== ENOENT
? EX_EXEC_ENOENT
: EX_EXEC_FAILED
);
1152 signal(SIGINT
, SIG_IGN
);
1153 signal(SIGQUIT
, SIG_IGN
);
1154 if (ctl
->catch_suspend
)
1155 signal(SIGTSTP
, SIG_DFL
);
1156 while (wait(NULL
) > 0)
1158 signal(SIGINT
, more_exit
);
1159 signal(SIGQUIT
, sigquit_handler
);
1160 if (ctl
->catch_suspend
)
1161 signal(SIGTSTP
, sigtstp_handler
);
1163 fputs(_("can't fork\n"), stderr
);
1165 puts("------------------------");
1166 output_prompt(ctl
, filename
);
1169 static void run_shell(struct more_control
*ctl
, char *filename
)
1171 char cmdbuf
[COMMAND_BUF
];
1178 ctl
->prompt_len
= 1;
1179 if (ctl
->run_previous_command
)
1180 fputs(ctl
->shell_line
, stdout
);
1182 ttyin(ctl
, cmdbuf
, sizeof(cmdbuf
) - 2, '!');
1184 rc
= expand(ctl
, &expanded
, cmdbuf
);
1186 if (strlen(expanded
) < sizeof(ctl
->shell_line
))
1187 strcpy(ctl
->shell_line
, expanded
);
1193 fputs(_(" Overflow\n"), stderr
);
1194 output_prompt(ctl
, filename
);
1196 } else if (rc
> 0) {
1198 ctl
->prompt_len
= printf("!%s", ctl
->shell_line
);
1202 fputc('\n', stderr
);
1203 ctl
->prompt_len
= 0;
1204 ctl
->previous_shell
= 1;
1205 execute(ctl
, filename
, ctl
->shell
, ctl
->shell
, "-c", ctl
->shell_line
, 0);
1208 /* Execute a colon-prefixed command. Returns <0 if not a command that
1209 * should cause more of the file to be printed. */
1210 static int colon_command(struct more_control
*ctl
, char *filename
, int cmd
, int nlines
)
1215 ch
= read_user_input(ctl
);
1218 ctl
->last_colon_command
= ch
;
1222 if (!ctl
->no_tty_in
)
1224 printf(_("\"%s\" line %d"), ctl
->file_names
[ctl
->argv_position
], ctl
->current_line
);
1226 ctl
->prompt_len
= printf(_("[Not a file] line %d"), ctl
->current_line
);
1231 if (ctl
->argv_position
>= ctl
->num_files
- 1)
1236 erase_prompt(ctl
, 0);
1237 change_file(ctl
, nlines
);
1240 if (ctl
->no_tty_in
) {
1241 fputc(RINGBELL
, stderr
);
1245 erase_prompt(ctl
, 0);
1248 change_file(ctl
, -nlines
);
1251 run_shell(ctl
, filename
);
1257 fputc(RINGBELL
, stderr
);
1262 /* Skip n lines in the file f */
1263 static void skip_lines(struct more_control
*ctl
, int n
, FILE *f
)
1268 while ((c
= more_getc(ctl
, f
)) != '\n')
1272 ctl
->current_line
++;
1276 /* Clear the screen */
1277 static void more_clear_screen(struct more_control
*ctl
)
1279 if (ctl
->clear
&& !ctl
->hard_tty
) {
1281 /* Put out carriage return so that system doesn't get
1282 * confused by escape sequences when expanding tabs */
1284 ctl
->prompt_len
= 0;
1288 static void read_line(struct more_control
*ctl
, FILE *f
)
1293 prepare_line_buffer(ctl
);
1296 while ((c
= more_getc(ctl
, f
)) != '\n' && c
!= EOF
1297 && (size_t)(p
- ctl
->line_buf
) < ctl
->line_sz
- 1)
1300 ctl
->current_line
++;
1304 /* Search for nth occurrence of regular expression contained in buf in
1306 static void search(struct more_control
*ctl
, char buf
[], FILE *file
, int n
)
1308 long startline
= ctl
->file_position
;
1309 long line1
= startline
;
1310 long line2
= startline
;
1316 ctl
->context
.line_num
= saveln
= ctl
->current_line
;
1317 ctl
->context
.row_num
= startline
;
1321 if ((rc
= regcomp(&re
, buf
, REG_NOSUB
)) != 0) {
1323 regerror(rc
, &re
, s
, sizeof s
);
1326 while (!feof(file
)) {
1329 line1
= ctl
->file_position
;
1330 read_line(ctl
, file
);
1332 if (regexec(&re
, ctl
->line_buf
, 0, NULL
, 0) == 0) {
1334 if (lncount
> 3 || (lncount
> 1 && ctl
->no_tty_in
)) {
1336 if (ctl
->clear_line_ends
)
1337 putp(ctl
->erase_line
);
1338 fputs(_("...skipping\n"), stdout
);
1340 if (!ctl
->no_tty_in
) {
1341 ctl
->current_line
-=
1342 (lncount
>= 3 ? 3 : lncount
);
1343 more_fseek(ctl
, file
, line3
);
1344 if (ctl
->no_scroll
) {
1345 if (ctl
->clear_line_ends
) {
1347 putp(ctl
->erase_line
);
1349 more_clear_screen(ctl
);
1353 if (ctl
->no_scroll
) {
1354 if (ctl
->clear_line_ends
) {
1356 putp(ctl
->erase_line
);
1358 more_clear_screen(ctl
);
1360 puts(ctl
->line_buf
);
1368 if (!ctl
->no_tty_in
) {
1369 ctl
->current_line
= saveln
;
1370 more_fseek(ctl
, file
, startline
);
1372 fputs(_("\nPattern not found\n"), stdout
);
1375 free(ctl
->previous_search
);
1376 ctl
->previous_search
= NULL
;
1378 more_error(ctl
, _("Pattern not found"));
1382 /* Read a command and do it. A command consists of an optional integer
1383 * argument followed by the command character. Return the number of
1384 * lines to display in the next screenful. If there is nothing more to
1385 * display in the current file, zero is returned. */
1386 static int more_key_command(struct more_control
*ctl
, char *filename
, FILE *f
)
1393 char comchar
, cmdbuf
[INIT_BUF
];
1396 if (!ctl
->report_errors
)
1397 output_prompt(ctl
, filename
);
1399 ctl
->report_errors
= 0;
1401 nlines
= read_number(ctl
, &comchar
);
1402 ctl
->run_previous_command
= colonch
= 0;
1403 if (comchar
== '.') { /* Repeat last command */
1404 ctl
->run_previous_command
++;
1405 comchar
= ctl
->last_key_command
;
1406 nlines
= ctl
->last_key_arg
;
1407 if (ctl
->last_key_command
== ':')
1408 colonch
= ctl
->last_colon_command
;
1410 ctl
->last_key_command
= comchar
;
1411 ctl
->last_key_arg
= nlines
;
1412 if ((cc_t
) comchar
== ctl
->output_tty
.c_cc
[VERASE
]) {
1414 output_prompt(ctl
, filename
);
1419 retval
= colon_command(ctl
, filename
, colonch
, nlines
);
1428 if (ctl
->no_tty_in
) {
1429 fputc(RINGBELL
, stderr
);
1437 erase_prompt(ctl
, 0);
1439 if (ctl
->clear_line_ends
)
1440 putp(ctl
->erase_line
);
1441 printf(P_("...back %d page",
1442 "...back %d pages", nlines
),
1444 if (ctl
->clear_line_ends
)
1445 putp(ctl
->erase_line
);
1448 initline
= ctl
->current_line
- ctl
->lines_per_screen
* (nlines
+ 1);
1449 if (!ctl
->no_scroll
)
1453 more_fseek(ctl
, f
, 0L);
1454 ctl
->current_line
= 0; /* skip_lines() will make current_line correct */
1455 skip_lines(ctl
, initline
, f
);
1456 if (!ctl
->no_scroll
) {
1457 retval
= ctl
->lines_per_screen
+ 1;
1459 retval
= ctl
->lines_per_screen
;
1467 nlines
= ctl
->lines_per_screen
;
1468 else if (comchar
== 'z')
1469 ctl
->lines_per_screen
= nlines
;
1476 ctl
->d_scroll_len
= nlines
;
1477 retval
= ctl
->d_scroll_len
;
1489 nlines
*= ctl
->lines_per_screen
;
1491 erase_prompt(ctl
, 0);
1493 if (ctl
->clear_line_ends
)
1494 putp(ctl
->erase_line
);
1495 printf(P_("...skipping %d line",
1496 "...skipping %d lines", nlines
),
1499 if (ctl
->clear_line_ends
)
1500 putp(ctl
->erase_line
);
1503 while (nlines
> 0) {
1504 while ((c
= more_getc(ctl
, f
)) != '\n')
1510 ctl
->current_line
++;
1513 retval
= ctl
->lines_per_screen
;
1518 ctl
->lines_per_screen
= nlines
;
1525 if (!ctl
->no_tty_in
) {
1526 more_clear_screen(ctl
);
1527 more_fseek(ctl
, f
, ctl
->screen_start
.row_num
);
1528 ctl
->current_line
= ctl
->screen_start
.line_num
;
1529 retval
= ctl
->lines_per_screen
;
1533 fputc(RINGBELL
, stderr
);
1537 if (!ctl
->no_tty_in
) {
1539 fputs(_("\n***Back***\n\n"), stdout
);
1540 more_fseek(ctl
, f
, ctl
->context
.row_num
);
1541 ctl
->current_line
= ctl
->context
.line_num
;
1542 retval
= ctl
->lines_per_screen
;
1546 fputc(RINGBELL
, stderr
);
1551 ctl
->prompt_len
= printf("%d", ctl
->current_line
);
1555 if (!ctl
->previous_search
) {
1556 more_error(ctl
, _("No previous regular expression"));
1559 ctl
->run_previous_command
++;
1566 ctl
->prompt_len
= 1;
1568 if (ctl
->run_previous_command
) {
1569 fputc('\r', stderr
);
1570 search(ctl
, ctl
->previous_search
, f
, nlines
);
1572 ttyin(ctl
, cmdbuf
, sizeof(cmdbuf
) - 2, '/');
1573 fputc('\r', stderr
);
1574 free(ctl
->previous_search
);
1575 ctl
->previous_search
= xstrdup(cmdbuf
);
1576 search(ctl
, cmdbuf
, f
, nlines
);
1578 retval
= ctl
->lines_per_screen
- 1;
1582 run_shell(ctl
, filename
);
1587 more_clear_screen(ctl
);
1589 "Most commands optionally preceded by integer argument k. "
1590 "Defaults in brackets.\n"
1591 "Star (*) indicates argument becomes new default.\n"), stdout
);
1592 puts("---------------------------------------"
1593 "----------------------------------------");
1595 ("<space> Display next k lines of text [current screen size]\n"
1596 "z Display next k lines of text [current screen size]*\n"
1597 "<return> Display next k lines of text [1]*\n"
1598 "d or ctrl-D Scroll k lines [current scroll size, initially 11]*\n"
1599 "q or Q or <interrupt> Exit from more\n"
1600 "s Skip forward k lines of text [1]\n"
1601 "f Skip forward k screenfuls of text [1]\n"
1602 "b or ctrl-B Skip backwards k screenfuls of text [1]\n"
1603 "' Go to place where previous search started\n"
1604 "= Display current line number\n"
1605 "/<regular expression> Search for kth occurrence of regular expression [1]\n"
1606 "n Search for kth occurrence of last r.e [1]\n"
1607 "!<cmd> or :!<cmd> Execute <cmd> in a subshell\n"
1608 "v Start up /usr/bin/vi at current line\n"
1609 "ctrl-L Redraw screen\n"
1610 ":n Go to kth next file [1]\n"
1611 ":p Go to kth previous file [1]\n"
1612 ":f Display current file name and line number\n"
1613 ". Repeat previous command\n"), stdout
);
1614 puts("---------------------------------------"
1615 "----------------------------------------");
1616 output_prompt(ctl
, filename
);
1618 case 'v': /* This case should go right before default */
1619 if (!ctl
->no_tty_in
) {
1620 /* Earlier: call vi +n file. This also
1621 * works for emacs. POSIX: call vi -c n
1622 * file (when editor is vi or ex). */
1624 int n
= (ctl
->current_line
- ctl
->lines_per_screen
<= 0 ? 1 :
1625 ctl
->current_line
- (ctl
->lines_per_screen
+ 1) / 2);
1628 editor
= getenv("VISUAL");
1629 if (editor
== NULL
|| *editor
== '\0')
1630 editor
= getenv("EDITOR");
1631 if (editor
== NULL
|| *editor
== '\0')
1634 p
= strrchr(editor
, '/');
1639 if (!strcmp(p
, "vi") || !strcmp(p
, "ex")) {
1640 sprintf(cmdbuf
, "-c %d", n
);
1643 sprintf(cmdbuf
, "+%d", n
);
1647 printf("%s %s %s", editor
, cmdbuf
,
1648 ctl
->file_names
[ctl
->argv_position
]);
1651 execute(ctl
, filename
, editor
, editor
,
1653 ctl
->file_names
[ctl
->argv_position
], (char *)0);
1655 execute(ctl
, filename
, editor
, editor
,
1656 cmdbuf
, ctl
->file_names
[ctl
->argv_position
],
1662 if (ctl
->suppress_bell
) {
1664 if (ctl
->enter_std
&& ctl
->exit_std
) {
1665 putp(ctl
->enter_std
);
1668 ("[Press 'h' for instructions.]"))
1669 + 2 * ctl
->stdout_glitch
;
1670 putp(ctl
->exit_std
);
1674 ("[Press 'h' for instructions.]"));
1677 fputc(RINGBELL
, stderr
);
1685 ctl
->waiting_input
= 0;
1686 ctl
->no_quit_dialog
= 1;
1690 /* Print out the contents of the file f, one screenful at a time. */
1691 static void screen(struct more_control
*ctl
, FILE *f
, int num_lines
)
1695 int length
; /* length of current line */
1696 static int prev_len
= 1; /* length of previous line */
1699 while (num_lines
> 0 && !ctl
->is_paused
) {
1700 if ((nchars
= get_line(ctl
, f
, &length
)) == EOF
) {
1701 if (ctl
->clear_line_ends
)
1702 putp(ctl
->clear_rest
);
1705 if (ctl
->squeeze_spaces
&& length
== 0 && prev_len
== 0)
1709 || ((ctl
->enter_std
&& *ctl
->enter_std
== ' ') && (ctl
->prompt_len
> 0)))
1710 erase_prompt(ctl
, 0);
1711 /* must clear before drawing line since tabs on
1712 * some terminals do not erase what they tab
1714 if (ctl
->clear_line_ends
)
1715 putp(ctl
->erase_line
);
1716 print_buf(ctl
, ctl
->line_buf
, length
);
1717 if (nchars
< ctl
->prompt_len
)
1718 erase_prompt(ctl
, nchars
); /* erase_prompt () sets prompt_len to 0 */
1720 ctl
->prompt_len
= 0;
1721 if (nchars
< ctl
->num_columns
|| !ctl
->fold_long_lines
)
1722 print_buf(ctl
, "\n", 1); /* will turn off UL if necessary */
1725 if (ctl
->underline_state
) {
1726 putp(ctl
->exit_underline
);
1727 ctl
->underline_state
= 0;
1730 if ((c
= more_getc(ctl
, f
)) == EOF
) {
1731 if (ctl
->clear_line_ends
)
1732 putp(ctl
->clear_rest
);
1736 if (ctl
->is_paused
&& ctl
->clear_line_ends
)
1737 putp(ctl
->clear_rest
);
1738 more_ungetc(ctl
, c
, f
);
1739 sigsetjmp(ctl
->destination
, 1);
1741 ctl
->starting_up
= 0;
1742 if ((num_lines
= more_key_command(ctl
, NULL
, f
)) == 0)
1744 if (ctl
->hard_tty
&& ctl
->prompt_len
> 0)
1745 erase_prompt(ctl
, 0);
1746 if (ctl
->no_scroll
&& num_lines
>= ctl
->lines_per_screen
) {
1747 if (ctl
->clear_line_ends
)
1750 more_clear_screen(ctl
);
1752 ctl
->screen_start
.line_num
= ctl
->current_line
;
1753 ctl
->screen_start
.row_num
= ctl
->file_position
;
1757 /* Come here if a signal for a window size change is received */
1758 static void sigwinch_handler(int dummy
__attribute__((__unused__
)))
1762 signal(SIGWINCH
, SIG_IGN
);
1763 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) != -1) {
1764 if (win
.ws_row
!= 0) {
1765 global_ctl
->lines_per_page
= win
.ws_row
;
1766 global_ctl
->d_scroll_len
= global_ctl
->lines_per_page
/ 2 - 1;
1767 if (global_ctl
->d_scroll_len
<= 0)
1768 global_ctl
->d_scroll_len
= 1;
1769 global_ctl
->lines_per_screen
= global_ctl
->lines_per_page
- 1;
1771 if (win
.ws_col
!= 0)
1772 global_ctl
->num_columns
= win
.ws_col
;
1774 signal(SIGWINCH
, sigwinch_handler
);
1777 static void copy_file(FILE *f
)
1782 while ((sz
= fread(&buf
, sizeof(char), sizeof(buf
), f
)) > 0)
1783 fwrite(&buf
, sizeof(char), sz
, stdout
);
1787 static void initterm(struct more_control
*ctl
)
1793 #ifndef NON_INTERACTIVE_MORE
1794 ctl
->no_tty_out
= tcgetattr(STDOUT_FILENO
, &ctl
->output_tty
);
1796 if (!ctl
->no_tty_out
) {
1797 ctl
->erase_previous_ok
= (ctl
->output_tty
.c_cc
[VERASE
] != 255);
1798 ctl
->erase_input_ok
= (ctl
->output_tty
.c_cc
[VKILL
] != 255);
1799 if ((term
= getenv("TERM")) == NULL
) {
1801 ctl
->enable_underlining
= 0;
1803 setupterm(term
, 1, &ret
);
1806 ctl
->enable_underlining
= 0;
1808 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0) {
1809 ctl
->lines_per_page
= tigetnum(TERM_LINES
);
1810 ctl
->num_columns
= tigetnum(TERM_COLS
);
1812 if ((ctl
->lines_per_page
= win
.ws_row
) == 0)
1813 ctl
->lines_per_page
= tigetnum(TERM_LINES
);
1814 if ((ctl
->num_columns
= win
.ws_col
) == 0)
1815 ctl
->num_columns
= tigetnum(TERM_COLS
);
1817 if ((ctl
->lines_per_page
<= 0) || tigetflag(TERM_HARD_COPY
)) {
1819 ctl
->lines_per_page
= LINES_PER_PAGE
;
1822 if (tigetflag(TERM_EAT_NEW_LINE
))
1823 /* Eat newline at last column + 1; dec, concept */
1825 if (ctl
->num_columns
<= 0)
1826 ctl
->num_columns
= NUM_COLUMNS
;
1828 ctl
->wrap_margin
= tigetflag(TERM_AUTO_RIGHT_MARGIN
);
1829 ctl
->bad_stdout
= tigetflag(TERM_CEOL
);
1830 ctl
->erase_line
= tigetstr(TERM_CLEAR_TO_LINE_END
);
1831 ctl
->clear
= tigetstr(TERM_CLEAR
);
1832 ctl
->enter_std
= tigetstr(TERM_STANDARD_MODE
);
1833 ctl
->exit_std
= tigetstr(TERM_EXIT_STANDARD_MODE
);
1834 if (0 < tigetnum(TERM_STD_MODE_GLITCH
))
1835 ctl
->stdout_glitch
= 1;
1837 /* Set up for underlining: some terminals don't
1838 * need it; others have start/stop sequences,
1839 * still others have an underline char sequence
1840 * which is assumed to move the cursor forward
1841 * one character. If underline sequence isn't
1842 * available, settle for standout sequence. */
1843 if (tigetflag(TERM_UNDERLINE
)
1844 || tigetflag(TERM_OVER_STRIKE
))
1845 ctl
->enable_underlining
= 0;
1846 if ((ctl
->underline_ch
= tigetstr(TERM_UNDERLINE_CHAR
)) == NULL
)
1847 ctl
->underline_ch
= "";
1848 if (((ctl
->enter_underline
=
1849 tigetstr(TERM_ENTER_UNDERLINE
)) == NULL
1850 || (ctl
->exit_underline
=
1851 tigetstr(TERM_EXIT_UNDERLINE
)) == NULL
)
1852 && !*ctl
->underline_ch
) {
1853 if ((ctl
->enter_underline
= ctl
->enter_std
) == NULL
1854 || (ctl
->exit_underline
= ctl
->exit_std
) == NULL
) {
1855 ctl
->enter_underline
= "";
1856 ctl
->exit_underline
= "";
1858 ctl
->underline_glitch
= ctl
->stdout_glitch
;
1860 ctl
->underline_glitch
= 0;
1862 ctl
->go_home
= tigetstr(TERM_HOME
);
1863 if (ctl
->go_home
== NULL
|| *ctl
->go_home
== '\0') {
1864 if ((ctl
->cursor_addr
=
1865 tigetstr(TERM_CURSOR_ADDRESS
)) != NULL
) {
1867 (const char *)tparm(ctl
->cursor_addr
, 0,
1869 xstrncpy(ctl
->home_position
, t
,
1870 sizeof(ctl
->home_position
));
1871 ctl
->go_home
= ctl
->home_position
;
1874 ctl
->clear_rest
= tigetstr(TERM_CLEAR_TO_SCREEN_END
);
1875 if ((ctl
->backspace_ch
= tigetstr(TERM_LINE_DOWN
)) == NULL
)
1876 ctl
->backspace_ch
= BS
;
1879 if ((ctl
->shell
= getenv("SHELL")) == NULL
)
1880 ctl
->shell
= "/bin/sh";
1882 ctl
->no_tty_in
= tcgetattr(STDIN_FILENO
, &ctl
->output_tty
);
1883 tcgetattr(STDERR_FILENO
, &ctl
->output_tty
);
1884 ctl
->original_tty
= ctl
->output_tty
;
1885 ctl
->hard_tabs
= (ctl
->output_tty
.c_oflag
& TABDLY
) != TAB3
;
1886 if (!ctl
->no_tty_out
) {
1887 ctl
->output_tty
.c_lflag
&= ~(ICANON
| ECHO
);
1888 ctl
->output_tty
.c_cc
[VMIN
] = 1;
1889 ctl
->output_tty
.c_cc
[VTIME
] = 0;
1893 int main(int argc
, char **argv
)
1899 int print_names
= 0;
1901 int search_at_start
= 0;
1903 int start_at_line
= 0;
1904 char *initbuf
= NULL
;
1905 struct more_control ctl
= {
1907 .fold_long_lines
= 1,
1908 .no_quit_dialog
= 1,
1910 .stop_after_formfeed
= 1,
1911 .enable_underlining
= 1,
1913 .lines_per_page
= LINES_PER_PAGE
,
1914 .num_columns
= NUM_COLUMNS
,
1915 .d_scroll_len
= SCROLL_LEN
,
1920 setlocale(LC_ALL
, "");
1921 bindtextdomain(PACKAGE
, LOCALEDIR
);
1922 textdomain(PACKAGE
);
1923 atexit(close_stdout
);
1926 /* first arg may be one of our standard longopts */
1927 if (!strcmp(argv
[1], "--help"))
1929 if (!strcmp(argv
[1], "--version")) {
1930 printf(UTIL_LINUX_VERSION
);
1935 ctl
.num_files
= argc
;
1936 ctl
.file_names
= argv
;
1937 setlocale(LC_ALL
, "");
1940 /* Auto set no scroll on when binary is called page */
1941 if (!(strcmp(program_invocation_short_name
, "page")))
1944 prepare_line_buffer(&ctl
);
1946 ctl
.d_scroll_len
= ctl
.lines_per_page
/ 2 - 1;
1947 if (ctl
.d_scroll_len
<= 0)
1948 ctl
.d_scroll_len
= 1;
1950 if ((s
= getenv("MORE")) != NULL
)
1951 arg_parser(&ctl
, s
);
1953 while (--ctl
.num_files
> 0) {
1954 if ((chr
= (*++ctl
.file_names
)[0]) == '-') {
1955 arg_parser(&ctl
, *ctl
.file_names
+ 1);
1956 } else if (chr
== '+') {
1957 s
= *ctl
.file_names
;
1960 initbuf
= xstrdup(s
+ 1);
1963 for (start_at_line
= 0; *s
!= '\0'; s
++)
1966 start_at_line
* 10 + *s
- '0';
1972 /* allow clear_line_ends only if go_home and erase_line and clear_rest strings are
1973 * defined, and in that case, make sure we are in no_scroll mode */
1974 if (ctl
.clear_line_ends
) {
1975 if ((ctl
.go_home
== NULL
) || (*ctl
.go_home
== '\0') ||
1976 (ctl
.erase_line
== NULL
) || (*ctl
.erase_line
== '\0') ||
1977 (ctl
.clear_rest
== NULL
) || (*ctl
.clear_rest
== '\0'))
1978 ctl
.clear_line_ends
= 0;
1982 if (ctl
.lines_per_screen
== 0)
1983 ctl
.lines_per_screen
= ctl
.lines_per_page
- 1;
1984 left
= ctl
.lines_per_screen
;
1985 if (ctl
.num_files
> 1)
1987 if (!ctl
.no_tty_in
&& ctl
.num_files
== 0) {
1988 warnx(_("bad usage"));
1989 errtryhelp(EXIT_FAILURE
);
1992 if (!ctl
.no_tty_out
) {
1993 signal(SIGQUIT
, sigquit_handler
);
1994 signal(SIGINT
, more_exit
);
1995 signal(SIGWINCH
, sigwinch_handler
);
1996 if (signal(SIGTSTP
, SIG_IGN
) == SIG_DFL
) {
1997 signal(SIGTSTP
, sigtstp_handler
);
1998 ctl
.catch_suspend
++;
2000 tcsetattr(STDERR_FILENO
, TCSANOW
, &ctl
.output_tty
);
2002 if (ctl
.no_tty_in
) {
2006 if ((chr
= getc(f
)) == '\f')
2007 more_clear_screen(&ctl
);
2010 if (ctl
.no_scroll
&& (chr
!= EOF
)) {
2011 if (ctl
.clear_line_ends
)
2014 more_clear_screen(&ctl
);
2017 if (search_at_start
) {
2018 free(ctl
.previous_search
);
2019 ctl
.previous_search
= xstrdup(initbuf
);
2020 search(&ctl
, initbuf
, stdin
, 1);
2024 skip_lines(&ctl
, start_at_line
, stdin
);
2025 screen(&ctl
, stdin
, left
);
2032 while (ctl
.argv_position
< ctl
.num_files
) {
2033 if ((f
= checkf(&ctl
, ctl
.file_names
[ctl
.argv_position
], &skip_file
)) != NULL
) {
2034 ctl
.context
.line_num
= ctl
.context
.row_num
= 0;
2035 ctl
.current_line
= 0;
2037 sigsetjmp(ctl
.destination
, 1);
2038 if (ctl
.first_file
) {
2040 if (search_at_start
) {
2041 free(ctl
.previous_search
);
2042 ctl
.previous_search
= xstrdup(initbuf
);
2043 search(&ctl
, initbuf
, f
, 1);
2047 skip_lines(&ctl
, start_at_line
, f
);
2048 } else if (ctl
.argv_position
< ctl
.num_files
&& !ctl
.no_tty_out
) {
2049 sigsetjmp(ctl
.destination
, 1);
2050 left
= more_key_command(&ctl
, ctl
.file_names
[ctl
.argv_position
], f
);
2053 if ((ctl
.no_scroll
|| skip_file
)
2054 && (ctl
.file_size
!= LONG_MAX
)) {
2055 if (ctl
.clear_line_ends
)
2058 more_clear_screen(&ctl
);
2062 erase_prompt(&ctl
, 0);
2063 if (ctl
.clear_line_ends
)
2064 putp(ctl
.erase_line
);
2065 fputs("::::::::::::::", stdout
);
2066 if (ctl
.prompt_len
> 14)
2067 erase_prompt(&ctl
, 14);
2069 if (ctl
.clear_line_ends
)
2070 putp(ctl
.erase_line
);
2071 puts(ctl
.file_names
[ctl
.argv_position
]);
2072 if (ctl
.clear_line_ends
)
2073 putp(ctl
.erase_line
);
2074 fputs("::::::::::::::\n", stdout
);
2075 if (left
> ctl
.lines_per_page
- 4)
2076 left
= ctl
.lines_per_page
- 4;
2081 ctl
.within_file
= 1;
2082 screen(&ctl
, f
, left
);
2083 ctl
.within_file
= 0;
2086 sigsetjmp(ctl
.destination
, 1);
2089 ctl
.screen_start
.line_num
= ctl
.screen_start
.row_num
= 0L;
2090 ctl
.context
.line_num
= ctl
.context
.row_num
= 0L;
2092 ctl
.argv_position
++;
2095 free(ctl
.previous_search
);