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
49 #include <stdlib.h> /* for alloca() */
50 #include <stdarg.h> /* for va_start() etc */
51 #include <sys/param.h>
58 #include <sys/ioctl.h>
67 #include "closestream.h"
72 # define NON_INTERACTIVE_MORE 1
79 #define VI "vi" /* found on the user's path */
81 #define Fopen(s,m) (Currline = 0,file_pos=0,fopen(s,m))
82 #define Ftell(f) file_pos
83 #define Fseek(f,off) (file_pos=off,fseek(f,off,0))
84 #define Getc(f) (++file_pos, getc(f))
85 #define Ungetc(c,f) (--file_pos, ungetc(c,f))
86 #define putcerr(c) fputc(c, stderr)
87 #define putserr(s) fputs(s, stderr)
88 #define putsout(s) fputs(s, stdout)
90 #define stty(fd,argp) tcsetattr(fd,TCSANOW,argp)
92 /* some function declarations */
99 void more_error(char *mess
);
100 void do_shell(char *filename
);
101 int colon(char *filename
, int cmd
, int nlines
);
102 int expand(char **outbuf
, char *inbuf
);
103 void argscan(char *s
);
104 void rdline(register FILE *f
);
105 void copy_file(register FILE *f
);
106 void search(char buf
[], FILE *file
, register int n
);
107 void skipf(register int nskip
);
108 void skiplns(register int n
, register FILE *f
);
109 void screen(register FILE *f
, register int num_lines
);
110 int command(char *filename
, register FILE *f
);
111 void erasep(register int col
);
112 void show(register char ch
);
114 void reset_tty(void);
115 void ttyin(char buf
[], register int nmax
, char pchar
);
116 int number(char *cmd
);
118 int get_line(register FILE *f
, int *length
);
119 void prbuf(register char *s
, register int n
);
120 void execute(char *filename
, char *cmd
, ...);
121 FILE *checkf(char *, int *);
122 void prepare_line_buffer(void);
125 #define LINSIZ 256 /* minimal Line buffer size */
126 #define ctrl(letter) (letter & 077)
127 #define RUBOUT '\177'
130 #define SCROLL_LEN 11
131 #define LINES_PER_PAGE 24
132 #define NUM_COLUMNS 80
133 #define TERMINAL_BUF 4096
135 #define SHELL_LINE 1000
136 #define COMMAND_BUF 200
137 #define REGERR_BUF NUM_COLUMNS
139 static struct termios otty
, savetty0
;
140 static long file_pos
, file_size
;
141 static int fnum
, no_intty
, no_tty
, slow_tty
;
142 static int dum_opt
, dlines
;
143 static void onquit(int), onsusp(int), chgwinsz(int), end_it(int);
144 static int nscroll
= SCROLL_LEN
; /* Number of lines scrolled by 'd' */
145 static int fold_opt
= 1; /* Fold long lines */
146 static int stop_opt
= 1; /* Stop after form feeds */
147 static int ssp_opt
= 0; /* Suppress white space */
148 static int ul_opt
= 1; /* Underline as best we can */
149 static int promptlen
;
150 static int Currline
; /* Line we are currently at */
151 static int startup
= 1;
152 static int firstf
= 1;
153 static int notell
= 1;
154 static int docrterase
= 0;
155 static int docrtkill
= 0;
156 static int bad_so
; /* True if overwriting does not turn
158 static int inwait
, Pause
, errors
;
159 static int within
; /* true if we are within a file,
160 false if we are between files */
161 static int hard
, dumb
, noscroll
, hardtabs
, clreol
, eatnl
;
162 static int catch_susp
; /* We should catch the SIGTSTP signal */
163 static char **fnames
; /* The list of file names */
164 static int nfiles
; /* Number of files left to process */
165 static char *shell
; /* The name of the shell to use */
166 static int shellp
; /* A previous shell command exists */
167 static sigjmp_buf restore
;
168 static char *Line
; /* Line buffer */
169 static size_t LineLen
; /* size of Line buffer */
170 static int Lpp
= LINES_PER_PAGE
; /* lines per page */
171 static char *Clear
; /* clear screen */
172 static char *eraseln
; /* erase line */
173 static char *Senter
, *Sexit
; /* enter and exit standout mode */
174 static char *ULenter
, *ULexit
; /* enter and exit underline mode */
175 static char *chUL
; /* underline character */
176 static char *chBS
; /* backspace character */
177 static char *Home
; /* go to home */
178 static char *cursorm
; /* cursor movement */
179 static char cursorhome
[40]; /* contains cursor movement to home */
180 static char *EodClr
; /* clear rest of screen */
181 static int Mcol
= NUM_COLUMNS
; /* number of columns */
182 static int Wrap
= 1; /* set if automargins */
183 static int soglitch
; /* terminal has standout mode glitch */
184 static int ulglitch
; /* terminal has underline mode glitch */
185 static int pstate
= 0; /* current UL state */
186 static int magic(FILE *, char *);
187 static char *previousre
; /* previous search() buf[] item */
190 } context
, screen_start
;
191 extern char PC
; /* pad character */
193 #if defined(HAVE_NCURSESW_TERM_H)
194 # include <ncursesw/term.h>
195 #elif defined(HAVE_NCURSES_TERM_H)
196 # include <ncurses/term.h>
197 #elif defined(HAVE_TERM_H)
201 #define TERM_AUTO_RIGHT_MARGIN "am"
202 #define TERM_CEOL "xhp"
203 #define TERM_CLEAR "clear"
204 #define TERM_CLEAR_TO_LINE_END "el"
205 #define TERM_CLEAR_TO_SCREEN_END "ed"
206 #define TERM_COLS "cols"
207 #define TERM_CURSOR_ADDRESS "cup"
208 #define TERM_EAT_NEW_LINE "xenl"
209 #define TERM_ENTER_UNDERLINE "smul"
210 #define TERM_EXIT_STANDARD_MODE "rmso"
211 #define TERM_EXIT_UNDERLINE "rmul"
212 #define TERM_HARD_COPY "hc"
213 #define TERM_HOME "home"
214 #define TERM_LINE_DOWN "cud1"
215 #define TERM_LINES "lines"
216 #define TERM_OVER_STRIKE "os"
217 #define TERM_PAD_CHAR "pad"
218 #define TERM_STANDARD_MODE "smso"
219 #define TERM_STD_MODE_GLITCH "xmc"
220 #define TERM_UNDERLINE_CHAR "uc"
221 #define TERM_UNDERLINE "ul"
223 static void putstring(char *s
)
225 tputs(s
, fileno(stdout
), putchar
); /* putp(s); */
228 static void __attribute__((__noreturn__
)) usage(FILE *out
)
230 fputs(USAGE_HEADER
, out
);
231 fprintf(out
, _(" %s [options] <file>...\n"), program_invocation_short_name
);
233 fputs(USAGE_SEPARATOR
, out
);
234 fputs(_("A file perusal filter for CRT viewing.\n"), out
);
236 fputs(USAGE_OPTIONS
, out
);
237 fputs(_(" -d display help instead of ringing bell\n"), out
);
238 fputs(_(" -f count logical rather than screen lines\n"), out
);
239 fputs(_(" -l suppress pause after form feed\n"), out
);
240 fputs(_(" -c do not scroll, display text and clean line ends\n"), out
);
241 fputs(_(" -p do not scroll, clean screen and display text\n"), out
);
242 fputs(_(" -s squeeze multiple blank lines into one\n"), out
);
243 fputs(_(" -u suppress underlining\n"), out
);
244 fputs(_(" -<number> the number of lines per screenful\n"), out
);
245 fputs(_(" +<number> display file beginning from line number\n"), out
);
246 fputs(_(" +/<string> display file beginning from search string match\n"), out
);
247 fputs(_(" -V display version information and exit\n"), out
);
248 fprintf(out
, USAGE_MAN_TAIL("more(1)"));
249 exit(out
== stderr
? EXIT_FAILURE
: EXIT_SUCCESS
);
252 int main(int argc
, char **argv
)
263 char *initbuf
= NULL
;
265 setlocale(LC_ALL
, "");
266 bindtextdomain(PACKAGE
, LOCALEDIR
);
268 atexit(close_stdout
);
272 setlocale(LC_ALL
, "");
275 /* Auto set no scroll on when binary is called page */
276 if (!(strcmp(program_invocation_short_name
, "page")))
279 prepare_line_buffer();
281 nscroll
= Lpp
/ 2 - 1;
285 if ((s
= getenv("MORE")) != NULL
)
288 while (--nfiles
> 0) {
289 if ((ch
= (*++fnames
)[0]) == '-') {
290 argscan(*fnames
+ 1);
291 } else if (ch
== '+') {
295 initbuf
= xstrdup(s
+ 1);
298 for (initline
= 0; *s
!= '\0'; s
++)
301 initline
* 10 + *s
- '0';
307 /* allow clreol only if Home and eraseln and EodClr strings are
308 * defined, and in that case, make sure we are in noscroll mode */
310 if ((Home
== NULL
) || (*Home
== '\0') ||
311 (eraseln
== NULL
) || (*eraseln
== '\0') ||
312 (EodClr
== NULL
) || (*EodClr
== '\0'))
318 dlines
= Lpp
- 1; /* was: Lpp - (noscroll ? 1 : 2) */
322 if (!no_intty
&& nfiles
== 0)
327 signal(SIGQUIT
, onquit
);
328 signal(SIGINT
, end_it
);
330 signal(SIGWINCH
, chgwinsz
);
332 if (signal(SIGTSTP
, SIG_IGN
) == SIG_DFL
) {
333 signal(SIGTSTP
, onsusp
);
336 stty(fileno(stderr
), &otty
);
342 if ((ch
= Getc(f
)) == '\f')
346 if (noscroll
&& (ch
!= EOF
)) {
355 previousre
= xstrdup(initbuf
);
356 search(initbuf
, stdin
, 1);
360 skiplns(initline
, stdin
);
368 while (fnum
< nfiles
) {
369 if ((f
= checkf(fnames
[fnum
], &clearit
)) != NULL
) {
370 context
.line
= context
.chrctr
= 0;
373 sigsetjmp(restore
, 1);
378 previousre
= xstrdup(initbuf
);
379 search(initbuf
, f
, 1);
383 skiplns(initline
, f
);
384 } else if (fnum
< nfiles
&& !no_tty
) {
385 sigsetjmp(restore
, 1);
386 left
= command(fnames
[fnum
], f
);
389 if ((noscroll
|| clearit
)
390 && (file_size
!= LONG_MAX
)) {
401 putsout("::::::::::::::");
410 puts("::::::::::::::");
422 sigsetjmp(restore
, 1);
425 screen_start
.line
= screen_start
.chrctr
= 0L;
426 context
.line
= context
.chrctr
= 0L;
438 void argscan(char *s
)
458 dlines
= dlines
* 10 + *s
- '0';
486 printf(UTIL_LINUX_VERSION
);
490 warnx(_("unknown option -%s"), s
);
498 /* Check whether the file named by fs is an ASCII file which the user may
499 * access. If it is, return the opened file. Otherwise return NULL. */
500 FILE *checkf(register char *fs
, int *clearfirst
)
506 if (stat(fs
, &stbuf
) == -1) {
510 warn(_("stat of %s failed"), fs
);
511 return ((FILE *)NULL
);
513 if ((stbuf
.st_mode
& S_IFMT
) == S_IFDIR
) {
514 printf(_("\n*** %s: directory ***\n\n"), fs
);
515 return ((FILE *)NULL
);
517 if ((f
= Fopen(fs
, "r")) == NULL
) {
519 warn(_("cannot open %s"), fs
);
520 return ((FILE *)NULL
);
524 return ((FILE *)NULL
);
526 fcntl(fileno(f
), F_SETFD
, FD_CLOEXEC
);
528 *clearfirst
= (c
== '\f');
530 if ((file_size
= stbuf
.st_size
) == 0)
531 file_size
= LONG_MAX
;
536 * check for file magic numbers. This code would best be shared
537 * with the file(1) program or, perhaps, more should not try to be
539 static int magic(FILE *f
, char *fs
)
541 signed char twobytes
[2];
543 /* don't try to look ahead if the input is unseekable */
544 if (fseek(f
, 0L, SEEK_SET
))
547 if (fread(twobytes
, 2, 1, f
) == 1) {
548 switch (twobytes
[0] + (twobytes
[1] << 8)) {
549 case 0407: /* a.out obj */
550 case 0410: /* a.out exec */
551 case 0413: /* a.out demand exec */
555 case 0x457f: /* simple ELF detection */
556 printf(_("\n******** %s: Not a text file ********\n\n"),
561 fseek(f
, 0L, SEEK_SET
); /* rewind() not necessary */
565 /* Print out the contents of the file f, one screenful at a time. */
567 void screen(register FILE *f
, register int num_lines
)
571 int length
; /* length of current line */
572 static int prev_len
= 1; /* length of previous line */
575 while (num_lines
> 0 && !Pause
) {
576 if ((nchars
= get_line(f
, &length
)) == EOF
) {
581 if (ssp_opt
&& length
== 0 && prev_len
== 0)
585 || ((Senter
&& *Senter
== ' ') && (promptlen
> 0)))
587 /* must clear before drawing line since tabs on
588 * some terminals do not erase what they tab
593 if (nchars
< promptlen
)
594 erasep(nchars
); /* erasep () sets promptlen to 0 */
599 * cleareol(); * must clear again in case we wrapped *
601 if (nchars
< Mcol
|| !fold_opt
)
602 prbuf("\n", 1); /* will turn off UL if necessary */
612 if ((c
= Getc(f
)) == EOF
) {
621 sigsetjmp(restore
, 1);
624 if ((num_lines
= command(NULL
, f
)) == 0)
626 if (hard
&& promptlen
> 0)
628 if (noscroll
&& num_lines
>= dlines
) {
634 screen_start
.line
= Currline
;
635 screen_start
.chrctr
= Ftell(f
);
639 /* Come here if a quit signal is received */
640 static void onquit(int dummy
__attribute__((__unused__
)))
642 signal(SIGQUIT
, SIG_IGN
);
646 signal(SIGQUIT
, onquit
);
647 siglongjmp(restore
, 1);
650 } else if (!dum_opt
&& notell
) {
651 promptlen
+= fprintf(stderr
, _("[Use q or Q to quit]"));
654 signal(SIGQUIT
, onquit
);
657 /* Come here if a signal for a window size change is received */
659 static void chgwinsz(int dummy
__attribute__((__unused__
)))
663 signal(SIGWINCH
, SIG_IGN
);
664 if (ioctl(fileno(stdout
), TIOCGWINSZ
, &win
) != -1) {
665 if (win
.ws_row
!= 0) {
667 nscroll
= Lpp
/ 2 - 1;
670 dlines
= Lpp
- 1; /* was: Lpp - (noscroll ? 1 : 2) */
675 signal(SIGWINCH
, chgwinsz
);
677 #endif /* SIGWINCH */
679 /* Clean up terminal state and exit. Also come here if interrupt signal received */
680 static void __attribute__((__noreturn__
)) end_it(int dummy
__attribute__((__unused__
)))
682 /* May be executed as a signal handler as well as by main process.
684 * The _exit() may wait for pending I/O for really long time, be sure
685 * that signal handler is not executed in this time to avoid double
686 * de-initialization (free() calls, etc.).
688 signal(SIGINT
, SIG_IGN
);
695 } else if (!clreol
&& (promptlen
> 0)) {
705 void copy_file(register FILE *f
)
710 while ((sz
= fread(&buf
, sizeof(char), sizeof(buf
), f
)) > 0)
711 fwrite(&buf
, sizeof(char), sz
, stdout
);
714 #define ringbell() putcerr('\007')
716 static void prompt(char *filename
)
720 else if (promptlen
> 0)
724 if (Senter
&& Sexit
) {
726 promptlen
+= (2 * soglitch
);
730 promptlen
+= printf(_("--More--"));
731 if (filename
!= NULL
) {
732 promptlen
+= printf(_("(Next file: %s)"), filename
);
733 } else if (!no_intty
) {
736 (int)((file_pos
* 100) / file_size
));
740 printf(_("[Press space to continue, 'q' to quit.]"));
752 void prepare_line_buffer(void)
755 size_t nsz
= Mcol
* 4;
763 /* alloc nsz and extra space for \n\0 */
764 nline
= xrealloc(Line
, nsz
+ 2);
769 /* Get a logical line */
770 int get_line(register FILE *f
, int *length
)
781 mbstate_t state
, state_bak
; /* Current status of the stream. */
782 char mbc
[MB_LEN_MAX
]; /* Buffer for one multibyte char. */
783 size_t mblength
; /* Byte length of multibyte char. */
784 size_t mbc_pos
= 0; /* Position of the MBC. */
785 int use_mbc_buffer_flag
= 0; /* If 1, mbc has data. */
786 int break_flag
= 0; /* If 1, exit while(). */
787 long file_pos_bak
= Ftell(f
);
789 memset(&state
, '\0', sizeof(mbstate_t));
792 prepare_line_buffer();
797 if (colflg
&& c
== '\n') {
801 while (p
< &Line
[LineLen
- 1]) {
803 if (fold_opt
&& use_mbc_buffer_flag
&& MB_CUR_MAX
> 1) {
804 use_mbc_buffer_flag
= 0;
808 mblength
= mbrtowc(&wc
, mbc
, mbc_pos
, &state
);
811 case (size_t)-2: /* Incomplete multibyte character. */
812 use_mbc_buffer_flag
= 1;
816 case (size_t)-1: /* Invalid as a multibyte character. */
822 if (column
>= Mcol
) {
823 Fseek(f
, file_pos_bak
);
825 memmove(mbc
, mbc
+ 1, --mbc_pos
);
834 wc_width
= wcwidth(wc
);
836 if (column
+ wc_width
> Mcol
) {
837 Fseek(f
, file_pos_bak
);
840 for (i
= 0; p
< &Line
[LineLen
- 1] &&
848 if (break_flag
|| column
>= Mcol
)
854 #endif /* HAVE_WIDECHAR */
871 if (c
== '\033') { /* ESC */
873 while (c
> ' ' && c
< '0' && p
< &Line
[LineLen
- 1]) {
877 if (c
>= '0' && c
< '\177' && p
< &Line
[LineLen
- 1]) {
885 if (!hardtabs
|| (column
< promptlen
&& !hard
)) {
886 if (hardtabs
&& eraseln
&& !dumb
) {
887 column
= 1 + (column
| 7);
891 for (--p
; p
< &Line
[LineLen
- 1];) {
893 if ((++column
& 7) == 0)
896 if (column
>= promptlen
)
900 column
= 1 + (column
| 7);
901 } else if (c
== '\b' && column
> 0) {
903 } else if (c
== '\r') {
912 } else if (c
== '\f' && stop_opt
) {
917 } else if (c
== EOF
) {
922 if (fold_opt
&& MB_CUR_MAX
> 1) {
923 memset(mbc
, '\0', MB_LEN_MAX
);
928 mblength
= mbrtowc(&wc
, mbc
, mbc_pos
, &state
);
929 /* The value of mblength is always less than 2 here. */
933 file_pos_bak
= Ftell(f
) - 1;
935 use_mbc_buffer_flag
= 1;
944 wc_width
= wcwidth(wc
);
949 #endif /* HAVE_WIDECHAR */
956 if (column
>= Mcol
&& fold_opt
)
959 if (use_mbc_buffer_flag
== 0 && p
>= &Line
[LineLen
- 1 - 4])
960 /* don't read another char if there is no space for
961 * whole multibyte sequence */
966 if (column
>= Mcol
&& Mcol
> 0) {
971 colflg
= column
== Mcol
&& fold_opt
;
972 if (colflg
&& eatnl
&& Wrap
) {
973 *p
++ = '\n'; /* simulate normal wrap */
980 /* Erase the rest of the prompt, assuming we are starting at column col. */
981 void erasep(register int col
)
991 if (!dumb
&& eraseln
)
994 printf("%*s", promptlen
- col
, "");
999 /* Erase the current line entirely */
1000 void kill_line(void)
1003 if (!eraseln
|| dumb
)
1007 /* force clear to end of line */
1019 #ifdef HAVE_WIDECHAR
1020 static UL_ASAN_BLACKLIST
size_t xmbrtowc(wchar_t *wc
, const char *s
, size_t n
,
1023 const size_t mblength
= mbrtowc(wc
, s
, n
, mbstate
);
1024 if (mblength
== (size_t)-2 || mblength
== (size_t)-1)
1030 /* Print a buffer of n characters */
1031 void prbuf(register char *s
, register int n
)
1033 register char c
; /* next output character */
1034 register int state
; /* next output char's UL state */
1035 #define wouldul(s,n) ((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_')))
1041 if (*s
== ' ' && pstate
== 0 && ulglitch
1042 && wouldul(s
+ 1, n
- 1)) {
1046 if ((state
= wouldul(s
, n
)) != 0) {
1047 c
= (*s
== '_') ? s
[2] : *s
;
1052 if (state
!= pstate
) {
1053 if (c
== ' ' && state
== 0 && ulglitch
1054 && wouldul(s
, n
- 1))
1057 putstring(state
? ULenter
: ULexit
);
1059 if (c
!= ' ' || pstate
== 0 || state
!= 0
1061 #ifdef HAVE_WIDECHAR
1066 memset(&mbstate
, '\0', sizeof(mbstate_t));
1069 mblength
= xmbrtowc(&wc
, s
, n
, &mbstate
);
1076 #endif /* HAVE_WIDECHAR */
1077 if (state
&& *chUL
) {
1085 /* Clear the screen */
1088 if (Clear
&& !hard
) {
1090 /* Put out carriage return so that system doesn't get
1091 * confused by escape sequences when expanding tabs */
1097 /* Go to home position */
1103 static int lastcmd
, lastarg
, lastp
;
1104 static int lastcolon
;
1105 static char shell_line
[SHELL_LINE
];
1107 /* Read a command and do it. A command consists of an optional integer
1108 * argument followed by the command character. Return the number of
1109 * lines to display in the next screenful. If there is nothing more to
1110 * display in the current file, zero is returned. */
1111 int command(char *filename
, register FILE *f
)
1113 register int nlines
;
1114 register int retval
= 0;
1118 char comchar
, cmdbuf
[INIT_BUF
];
1120 #define ret(val) retval=val;done++;break
1128 nlines
= number(&comchar
);
1129 lastp
= colonch
= 0;
1130 if (comchar
== '.') { /* Repeat last command */
1135 colonch
= lastcolon
;
1139 if ((cc_t
) comchar
== otty
.c_cc
[VERASE
]) {
1146 retval
= colon(filename
, colonch
, nlines
);
1153 register int initline
;
1168 printf(P_("...back %d page",
1169 "...back %d pages", nlines
),
1175 initline
= Currline
- dlines
* (nlines
+ 1);
1181 Currline
= 0; /* skiplns() will make Currline correct */
1182 skiplns(initline
, f
);
1193 else if (comchar
== 'z')
1216 printf(P_("...skipping %d line",
1217 "...skipping %d lines", nlines
),
1224 while (nlines
> 0) {
1225 while ((c
= Getc(f
)) != '\n')
1244 Fseek(f
, screen_start
.chrctr
);
1245 Currline
= screen_start
.line
;
1254 putsout(_("\n***Back***\n\n"));
1255 Fseek(f
, context
.chrctr
);
1256 Currline
= context
.line
;
1264 promptlen
= printf("%d", Currline
);
1269 more_error(_("No previous regular expression"));
1283 search(previousre
, f
, nlines
);
1285 ttyin(cmdbuf
, sizeof(cmdbuf
) - 2, '/');
1288 previousre
= xstrdup(cmdbuf
);
1289 search(cmdbuf
, f
, nlines
);
1300 "Most commands optionally preceded by integer argument k. "
1301 "Defaults in brackets.\n"
1302 "Star (*) indicates argument becomes new default.\n"));
1303 puts("---------------------------------------"
1304 "----------------------------------------");
1306 ("<space> Display next k lines of text [current screen size]\n"
1307 "z Display next k lines of text [current screen size]*\n"
1308 "<return> Display next k lines of text [1]*\n"
1309 "d or ctrl-D Scroll k lines [current scroll size, initially 11]*\n"
1310 "q or Q or <interrupt> Exit from more\n"
1311 "s Skip forward k lines of text [1]\n"
1312 "f Skip forward k screenfuls of text [1]\n"
1313 "b or ctrl-B Skip backwards k screenfuls of text [1]\n"
1314 "' Go to place where previous search started\n"
1315 "= Display current line number\n"
1316 "/<regular expression> Search for kth occurrence of regular expression [1]\n"
1317 "n Search for kth occurrence of last r.e [1]\n"
1318 "!<cmd> or :!<cmd> Execute <cmd> in a subshell\n"
1319 "v Start up /usr/bin/vi at current line\n"
1320 "ctrl-L Redraw screen\n"
1321 ":n Go to kth next file [1]\n"
1322 ":p Go to kth previous file [1]\n"
1323 ":f Display current file name and line number\n"
1324 ". Repeat previous command\n"));
1325 puts("---------------------------------------"
1326 "----------------------------------------");
1329 case 'v': /* This case should go right before default */
1331 /* Earlier: call vi +n file. This also
1332 * works for emacs. POSIX: call vi -c n
1333 * file (when editor is vi or ex). */
1335 int n
= (Currline
- dlines
<= 0 ? 1 :
1336 Currline
- (dlines
+ 1) / 2);
1339 editor
= getenv("VISUAL");
1340 if (editor
== NULL
|| *editor
== '\0')
1341 editor
= getenv("EDITOR");
1342 if (editor
== NULL
|| *editor
== '\0')
1345 p
= strrchr(editor
, '/');
1350 if (!strcmp(p
, "vi") || !strcmp(p
, "ex")) {
1351 sprintf(cmdbuf
, "-c %d", n
);
1354 sprintf(cmdbuf
, "+%d", n
);
1358 printf("%s %s %s", editor
, cmdbuf
,
1362 execute(filename
, editor
, editor
,
1364 fnames
[fnum
], (char *)0);
1366 execute(filename
, editor
, editor
,
1367 cmdbuf
, fnames
[fnum
],
1375 if (Senter
&& Sexit
) {
1379 ("[Press 'h' for instructions.]"))
1385 ("[Press 'h' for instructions.]"));
1402 /* Execute a colon-prefixed command. Returns <0 if not a command that
1403 * should cause more of the file to be printed. */
1404 int colon(char *filename
, int cmd
, int nlines
)
1416 printf(_("\"%s\" line %d"), fnames
[fnum
], Currline
);
1418 promptlen
= printf(_("[Not a file] line %d"), Currline
);
1423 if (fnum
>= nfiles
- 1)
1454 /* Read a decimal number from the terminal. Set cmd to the non-digit
1455 * which terminates the number. */
1456 int number(char *cmd
)
1461 ch
= otty
.c_cc
[VKILL
];
1465 i
= i
* 10 + ch
- '0';
1466 else if ((cc_t
) ch
== otty
.c_cc
[VKILL
])
1476 void do_shell(char *filename
)
1478 char cmdbuf
[COMMAND_BUF
];
1487 putsout(shell_line
);
1489 ttyin(cmdbuf
, sizeof(cmdbuf
) - 2, '!');
1491 rc
= expand(&expanded
, cmdbuf
);
1493 if (strlen(expanded
) < sizeof(shell_line
))
1494 strcpy(shell_line
, expanded
);
1500 putserr(_(" Overflow\n"));
1503 } else if (rc
> 0) {
1505 promptlen
= printf("!%s", shell_line
);
1512 execute(filename
, shell
, shell
, "-c", shell_line
, 0);
1515 /* Search for nth occurrence of regular expression contained in buf in
1517 void search(char buf
[], FILE *file
, register int n
)
1519 long startline
= Ftell(file
);
1520 register long line1
= startline
;
1521 register long line2
= startline
;
1522 register long line3
;
1523 register int lncount
;
1527 context
.line
= saveln
= Currline
;
1528 context
.chrctr
= startline
;
1532 if ((rc
= regcomp(&re
, buf
, REG_NOSUB
)) != 0) {
1534 regerror(rc
, &re
, s
, sizeof s
);
1537 while (!feof(file
)) {
1540 line1
= Ftell(file
);
1543 if (regexec(&re
, Line
, 0, NULL
, 0) == 0) {
1545 if (lncount
> 3 || (lncount
> 1 && no_intty
)) {
1549 putsout(_("...skipping\n"));
1553 (lncount
>= 3 ? 3 : lncount
);
1581 Fseek(file
, startline
);
1583 putsout(_("\nPattern not found\n"));
1589 more_error(_("Pattern not found"));
1593 void execute(char *filename
, char *cmd
, ...)
1604 for (n
= 10; (id
= fork()) < 0 && n
> 0; n
--)
1609 open("/dev/tty", 0);
1612 va_start(argp
, cmd
);
1613 arg
= va_arg(argp
, char *);
1617 arg
= va_arg(argp
, char *);
1621 args
= alloca(sizeof(char *) * (argcount
+ 1));
1622 args
[argcount
] = NULL
;
1624 va_start(argp
, cmd
);
1625 arg
= va_arg(argp
, char *);
1628 args
[argcount
] = arg
;
1630 arg
= va_arg(argp
, char *);
1635 putserr(_("exec failed\n"));
1639 signal(SIGINT
, SIG_IGN
);
1640 signal(SIGQUIT
, SIG_IGN
);
1642 signal(SIGTSTP
, SIG_DFL
);
1643 while (wait(NULL
) > 0) ;
1644 signal(SIGINT
, end_it
);
1645 signal(SIGQUIT
, onquit
);
1647 signal(SIGTSTP
, onsusp
);
1649 putserr(_("can't fork\n"));
1651 puts("------------------------");
1655 /* Skip n lines in the file f */
1656 void skiplns(register int n
, register FILE *f
)
1661 while ((c
= Getc(f
)) != '\n')
1669 /* Skip nskip files in the file list (from the command line). Nskip may
1671 void skipf(register int nskip
)
1676 if (fnum
+ nskip
> nfiles
- 1)
1677 nskip
= nfiles
- fnum
- 1;
1683 puts(_("\n...Skipping "));
1687 putsout(_("...Skipping to file "));
1689 putsout(_("...Skipping back to file "));
1697 /*----------------------------- Terminal I/O -------------------------------*/
1709 #ifndef NON_INTERACTIVE_MORE
1710 no_tty
= tcgetattr(fileno(stdout
), &otty
);
1713 docrterase
= (otty
.c_cc
[VERASE
] != 255);
1714 docrtkill
= (otty
.c_cc
[VKILL
] != 255);
1718 /* Wait until we're in the foreground before we
1719 * save the terminal modes. */
1720 if ((tgrp
= tcgetpgrp(fileno(stdout
))) < 0)
1721 err(EXIT_FAILURE
, "tcgetpgrp");
1722 if (tgrp
!= getpgrp(0)) {
1727 #endif /* do_SIGTTOU */
1728 if ((term
= getenv("TERM")) == NULL
) {
1732 setupterm(term
, 1, &ret
);
1738 if (ioctl(fileno(stdout
), TIOCGWINSZ
, &win
) < 0) {
1740 Lpp
= tigetnum(TERM_LINES
);
1741 Mcol
= tigetnum(TERM_COLS
);
1744 if ((Lpp
= win
.ws_row
) == 0)
1745 Lpp
= tigetnum(TERM_LINES
);
1746 if ((Mcol
= win
.ws_col
) == 0)
1747 Mcol
= tigetnum(TERM_COLS
);
1750 if ((Lpp
<= 0) || tigetflag(TERM_HARD_COPY
)) {
1751 hard
++; /* Hard copy terminal */
1752 Lpp
= LINES_PER_PAGE
;
1755 if (tigetflag(TERM_EAT_NEW_LINE
))
1756 /* Eat newline at last column + 1; dec, concept */
1761 Wrap
= tigetflag(TERM_AUTO_RIGHT_MARGIN
);
1762 bad_so
= tigetflag(TERM_CEOL
);
1763 eraseln
= tigetstr(TERM_CLEAR_TO_LINE_END
);
1764 Clear
= tigetstr(TERM_CLEAR
);
1765 Senter
= tigetstr(TERM_STANDARD_MODE
);
1766 Sexit
= tigetstr(TERM_EXIT_STANDARD_MODE
);
1767 if ((soglitch
= tigetnum(TERM_STD_MODE_GLITCH
)) < 0)
1770 /* Set up for underlining: some terminals don't
1771 * need it; others have start/stop sequences,
1772 * still others have an underline char sequence
1773 * which is assumed to move the cursor forward
1774 * one character. If underline sequence isn't
1775 * available, settle for standout sequence. */
1776 if (tigetflag(TERM_UNDERLINE
)
1777 || tigetflag(TERM_OVER_STRIKE
))
1779 if ((chUL
= tigetstr(TERM_UNDERLINE_CHAR
)) == NULL
)
1782 tigetstr(TERM_ENTER_UNDERLINE
)) == NULL
1784 tigetstr(TERM_EXIT_UNDERLINE
)) == NULL
)
1786 if ((ULenter
= Senter
) == NULL
1787 || (ULexit
= Sexit
) == NULL
) {
1791 ulglitch
= soglitch
;
1796 if ((padstr
= tigetstr(TERM_PAD_CHAR
)) != NULL
)
1798 Home
= tigetstr(TERM_HOME
);
1799 if (Home
== NULL
|| *Home
== '\0') {
1801 tigetstr(TERM_CURSOR_ADDRESS
)) != NULL
) {
1803 (const char *)tparm(cursorm
, 0,
1805 xstrncpy(cursorhome
, t
,
1806 sizeof(cursorhome
));
1810 EodClr
= tigetstr(TERM_CLEAR_TO_SCREEN_END
);
1811 if ((chBS
= tigetstr(TERM_LINE_DOWN
)) == NULL
)
1815 if ((shell
= getenv("SHELL")) == NULL
)
1818 no_intty
= tcgetattr(fileno(stdin
), &otty
);
1819 tcgetattr(fileno(stderr
), &otty
);
1821 slow_tty
= cfgetispeed(&otty
) < B1200
;
1822 hardtabs
= (otty
.c_oflag
& TABDLY
) != XTABS
;
1824 otty
.c_lflag
&= ~(ICANON
| ECHO
);
1825 otty
.c_cc
[VMIN
] = 1;
1826 otty
.c_cc
[VTIME
] = 0;
1835 if (read(fileno(stderr
), &c
, 1) <= 0) {
1839 c
= otty
.c_cc
[VKILL
];
1844 static char *BS
= "\b";
1845 static char *BSB
= "\b \b";
1846 static char *CARAT
= "^";
1847 #define ERASEONECOLUMN(x) \
1855 void ttyin(char buf
[], register int nmax
, char pchar
)
1864 while (sp
- buf
< nmax
) {
1865 if (promptlen
> maxlen
)
1870 } else if (((cc_t
) c
== otty
.c_cc
[VERASE
]) && !slash
) {
1872 #ifdef HAVE_WIDECHAR
1873 if (MB_CUR_MAX
> 1) {
1875 size_t pos
= 0, mblength
;
1876 mbstate_t state
, state_bak
;
1878 memset(&state
, '\0', sizeof(mbstate_t));
1883 mbrtowc(&wc
, buf
+ pos
,
1886 state
= (mblength
== (size_t)-2
1888 (size_t)-1) ? state_bak
1891 (mblength
== (size_t)-2
1892 || mblength
== (size_t)-1
1896 if (buf
+ pos
+ mblength
>= sp
)
1902 if (mblength
== 1) {
1903 ERASEONECOLUMN(docrterase
);
1906 wc_width
= wcwidth(wc
);
1910 while (wc_width
--) {
1911 ERASEONECOLUMN(docrterase
);
1915 while (mblength
--) {
1920 #endif /* HAVE_WIDECHAR */
1923 ERASEONECOLUMN(docrterase
);
1927 if ((*sp
< ' ' && *sp
!= '\n') || *sp
== RUBOUT
) {
1929 ERASEONECOLUMN(docrterase
);
1935 siglongjmp(restore
, 1);
1937 } else if (((cc_t
) c
== otty
.c_cc
[VKILL
]) && !slash
) {
1948 while (promptlen
-- > 1)
1956 if (slash
&& ((cc_t
) c
== otty
.c_cc
[VKILL
]
1957 || (cc_t
) c
== otty
.c_cc
[VERASE
])) {
1958 ERASEONECOLUMN(docrterase
);
1964 if ((c
< ' ' && c
!= '\n' && c
!= ESC
) || c
== RUBOUT
) {
1965 c
+= (c
== RUBOUT
) ? -0100 : 0100;
1969 if (c
!= '\n' && c
!= ESC
) {
1978 if (sp
- buf
>= nmax
- 1)
1979 more_error(_("Line too long"));
1982 /* return: 0 - unchanged, 1 - changed, -1 - overflow (unchanged) */
1983 int expand(char **outbuf
, char *inbuf
)
1990 int tempsz
, xtra
, offset
;
1992 xtra
= strlen(fnames
[fnum
]) + strlen(shell_line
) + 1;
1993 tempsz
= 200 + xtra
;
1994 temp
= xmalloc(tempsz
);
1997 while ((c
= *inpstr
++) != '\0') {
1998 offset
= outstr
- temp
;
1999 if (tempsz
- offset
- 1 < xtra
) {
2000 tempsz
+= 200 + xtra
;
2001 temp
= xrealloc(temp
, tempsz
);
2002 outstr
= temp
+ offset
;
2007 strcpy(outstr
, fnames
[fnum
]);
2008 outstr
+= strlen(fnames
[fnum
]);
2016 ("No previous command to substitute for"));
2017 strcpy(outstr
, shell_line
);
2018 outstr
+= strlen(shell_line
);
2022 if (*inpstr
== '%' || *inpstr
== '!') {
2023 *outstr
++ = *inpstr
++;
2037 if ((c
< ' ' && c
!= '\n' && c
!= ESC
) || c
== RUBOUT
) {
2038 c
+= (c
== RUBOUT
) ? -0100 : 0100;
2046 void more_error(char *mess
)
2052 promptlen
+= strlen(mess
);
2053 if (Senter
&& Sexit
) {
2061 siglongjmp(restore
, 1);
2066 otty
.c_lflag
&= ~(ICANON
| ECHO
);
2067 otty
.c_cc
[VMIN
] = 1; /* read at least 1 char */
2068 otty
.c_cc
[VTIME
] = 0; /* no timeout */
2069 stty(fileno(stderr
), &otty
);
2072 static int ourputch(int c
)
2074 return putc(c
, stdout
);
2077 void reset_tty(void)
2082 /* putchar - if that isn't a macro */
2083 tputs(ULexit
, fileno(stdout
), ourputch
);
2087 otty
.c_lflag
|= ICANON
| ECHO
;
2088 otty
.c_cc
[VMIN
] = savetty0
.c_cc
[VMIN
];
2089 otty
.c_cc
[VTIME
] = savetty0
.c_cc
[VTIME
];
2090 stty(fileno(stderr
), &savetty0
);
2093 void rdline(register FILE *f
)
2098 prepare_line_buffer();
2101 while ((c
= Getc(f
)) != '\n' && c
!= EOF
2102 && (size_t)(p
- Line
) < LineLen
- 1)
2109 /* Come here when we get a suspend signal from the terminal */
2110 void onsusp(int dummy
__attribute__((__unused__
)))
2112 sigset_t signals
, oldmask
;
2114 /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
2115 signal(SIGTTOU
, SIG_IGN
);
2118 signal(SIGTTOU
, SIG_DFL
);
2119 /* Send the TSTP signal to suspend our process group */
2120 signal(SIGTSTP
, SIG_DFL
);
2122 /* unblock SIGTSTP or we won't be able to suspend ourself */
2123 sigemptyset(&signals
);
2124 sigaddset(&signals
, SIGTSTP
);
2125 sigprocmask(SIG_UNBLOCK
, &signals
, &oldmask
);
2128 /* Pause for station break */
2130 sigprocmask(SIG_SETMASK
, &oldmask
, NULL
);
2133 signal(SIGTSTP
, onsusp
);
2136 siglongjmp(restore
, 1);