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
;
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(void)
231 fputs(USAGE_HEADER
, out
);
232 fprintf(out
, _(" %s [options] <file>...\n"), program_invocation_short_name
);
234 fputs(USAGE_SEPARATOR
, out
);
235 fputs(_("A file perusal filter for CRT viewing.\n"), out
);
237 fputs(USAGE_OPTIONS
, out
);
238 fputs(_(" -d display help instead of ringing bell\n"), out
);
239 fputs(_(" -f count logical rather than screen lines\n"), out
);
240 fputs(_(" -l suppress pause after form feed\n"), out
);
241 fputs(_(" -c do not scroll, display text and clean line ends\n"), out
);
242 fputs(_(" -p do not scroll, clean screen and display text\n"), out
);
243 fputs(_(" -s squeeze multiple blank lines into one\n"), out
);
244 fputs(_(" -u suppress underlining\n"), out
);
245 fputs(_(" -<number> the number of lines per screenful\n"), out
);
246 fputs(_(" +<number> display file beginning from line number\n"), out
);
247 fputs(_(" +/<string> display file beginning from search string match\n"), out
);
249 fputs(USAGE_SEPARATOR
, out
);
250 printf( " --help %s\n", USAGE_OPTSTR_HELP
);
251 printf( " -V, --version %s\n", USAGE_OPTSTR_VERSION
);
252 printf(USAGE_MAN_TAIL("more(1)"));
256 int main(int argc
, char **argv
)
267 char *initbuf
= NULL
;
269 setlocale(LC_ALL
, "");
270 bindtextdomain(PACKAGE
, LOCALEDIR
);
272 atexit(close_stdout
);
275 /* first arg may be one of our standard longopts */
276 if (!strcmp(argv
[1], "--help"))
278 if (!strcmp(argv
[1], "--version")) {
279 printf(UTIL_LINUX_VERSION
);
286 setlocale(LC_ALL
, "");
289 /* Auto set no scroll on when binary is called page */
290 if (!(strcmp(program_invocation_short_name
, "page")))
293 prepare_line_buffer();
295 nscroll
= Lpp
/ 2 - 1;
299 if ((s
= getenv("MORE")) != NULL
)
302 while (--nfiles
> 0) {
303 if ((ch
= (*++fnames
)[0]) == '-') {
304 argscan(*fnames
+ 1);
305 } else if (ch
== '+') {
309 initbuf
= xstrdup(s
+ 1);
312 for (initline
= 0; *s
!= '\0'; s
++)
315 initline
* 10 + *s
- '0';
321 /* allow clreol only if Home and eraseln and EodClr strings are
322 * defined, and in that case, make sure we are in noscroll mode */
324 if ((Home
== NULL
) || (*Home
== '\0') ||
325 (eraseln
== NULL
) || (*eraseln
== '\0') ||
326 (EodClr
== NULL
) || (*EodClr
== '\0'))
332 dlines
= Lpp
- 1; /* was: Lpp - (noscroll ? 1 : 2) */
336 if (!no_intty
&& nfiles
== 0) {
337 warnx(_("bad usage"));
338 errtryhelp(EXIT_FAILURE
);
342 signal(SIGQUIT
, onquit
);
343 signal(SIGINT
, end_it
);
345 signal(SIGWINCH
, chgwinsz
);
347 if (signal(SIGTSTP
, SIG_IGN
) == SIG_DFL
) {
348 signal(SIGTSTP
, onsusp
);
351 stty(fileno(stderr
), &otty
);
357 if ((ch
= Getc(f
)) == '\f')
361 if (noscroll
&& (ch
!= EOF
)) {
370 previousre
= xstrdup(initbuf
);
371 search(initbuf
, stdin
, 1);
375 skiplns(initline
, stdin
);
383 while (fnum
< nfiles
) {
384 if ((f
= checkf(fnames
[fnum
], &clearit
)) != NULL
) {
385 context
.line
= context
.chrctr
= 0;
388 sigsetjmp(restore
, 1);
393 previousre
= xstrdup(initbuf
);
394 search(initbuf
, f
, 1);
398 skiplns(initline
, f
);
399 } else if (fnum
< nfiles
&& !no_tty
) {
400 sigsetjmp(restore
, 1);
401 left
= command(fnames
[fnum
], f
);
404 if ((noscroll
|| clearit
)
405 && (file_size
!= LONG_MAX
)) {
416 putsout("::::::::::::::");
425 puts("::::::::::::::");
437 sigsetjmp(restore
, 1);
440 screen_start
.line
= screen_start
.chrctr
= 0L;
441 context
.line
= context
.chrctr
= 0L;
453 void argscan(char *s
)
473 dlines
= dlines
* 10 + *s
- '0';
501 printf(UTIL_LINUX_VERSION
);
505 warnx(_("unknown option -%s"), s
);
506 errtryhelp(EXIT_FAILURE
);
513 /* Check whether the file named by fs is an ASCII file which the user may
514 * access. If it is, return the opened file. Otherwise return NULL. */
515 FILE *checkf(register char *fs
, int *clearfirst
)
521 if (stat(fs
, &stbuf
) == -1) {
525 warn(_("stat of %s failed"), fs
);
526 return ((FILE *)NULL
);
528 if ((stbuf
.st_mode
& S_IFMT
) == S_IFDIR
) {
529 printf(_("\n*** %s: directory ***\n\n"), fs
);
530 return ((FILE *)NULL
);
532 if ((f
= Fopen(fs
, "r")) == NULL
) {
534 warn(_("cannot open %s"), fs
);
535 return ((FILE *)NULL
);
539 return ((FILE *)NULL
);
541 fcntl(fileno(f
), F_SETFD
, FD_CLOEXEC
);
543 *clearfirst
= (c
== '\f');
545 if ((file_size
= stbuf
.st_size
) == 0)
546 file_size
= LONG_MAX
;
551 * check for file magic numbers. This code would best be shared
552 * with the file(1) program or, perhaps, more should not try to be
554 static int magic(FILE *f
, char *fs
)
556 signed char twobytes
[2];
558 /* don't try to look ahead if the input is unseekable */
559 if (fseek(f
, 0L, SEEK_SET
))
562 if (fread(twobytes
, 2, 1, f
) == 1) {
563 switch (twobytes
[0] + (twobytes
[1] << 8)) {
564 case 0407: /* a.out obj */
565 case 0410: /* a.out exec */
566 case 0413: /* a.out demand exec */
570 case 0x457f: /* simple ELF detection */
571 printf(_("\n******** %s: Not a text file ********\n\n"),
576 fseek(f
, 0L, SEEK_SET
); /* rewind() not necessary */
580 /* Print out the contents of the file f, one screenful at a time. */
582 void screen(register FILE *f
, register int num_lines
)
586 int length
; /* length of current line */
587 static int prev_len
= 1; /* length of previous line */
590 while (num_lines
> 0 && !Pause
) {
591 if ((nchars
= get_line(f
, &length
)) == EOF
) {
596 if (ssp_opt
&& length
== 0 && prev_len
== 0)
600 || ((Senter
&& *Senter
== ' ') && (promptlen
> 0)))
602 /* must clear before drawing line since tabs on
603 * some terminals do not erase what they tab
608 if (nchars
< promptlen
)
609 erasep(nchars
); /* erasep () sets promptlen to 0 */
614 * cleareol(); * must clear again in case we wrapped *
616 if (nchars
< Mcol
|| !fold_opt
)
617 prbuf("\n", 1); /* will turn off UL if necessary */
627 if ((c
= Getc(f
)) == EOF
) {
636 sigsetjmp(restore
, 1);
639 if ((num_lines
= command(NULL
, f
)) == 0)
641 if (hard
&& promptlen
> 0)
643 if (noscroll
&& num_lines
>= dlines
) {
649 screen_start
.line
= Currline
;
650 screen_start
.chrctr
= Ftell(f
);
654 /* Come here if a quit signal is received */
655 static void onquit(int dummy
__attribute__((__unused__
)))
657 signal(SIGQUIT
, SIG_IGN
);
661 signal(SIGQUIT
, onquit
);
662 siglongjmp(restore
, 1);
665 } else if (!dum_opt
&& notell
) {
666 promptlen
+= fprintf(stderr
, _("[Use q or Q to quit]"));
669 signal(SIGQUIT
, onquit
);
672 /* Come here if a signal for a window size change is received */
674 static void chgwinsz(int dummy
__attribute__((__unused__
)))
678 signal(SIGWINCH
, SIG_IGN
);
679 if (ioctl(fileno(stdout
), TIOCGWINSZ
, &win
) != -1) {
680 if (win
.ws_row
!= 0) {
682 nscroll
= Lpp
/ 2 - 1;
685 dlines
= Lpp
- 1; /* was: Lpp - (noscroll ? 1 : 2) */
690 signal(SIGWINCH
, chgwinsz
);
692 #endif /* SIGWINCH */
694 /* Clean up terminal state and exit. Also come here if interrupt signal received */
695 static void __attribute__((__noreturn__
)) end_it(int dummy
__attribute__((__unused__
)))
697 /* May be executed as a signal handler as well as by main process.
699 * The _exit() may wait for pending I/O for really long time, be sure
700 * that signal handler is not executed in this time to avoid double
701 * de-initialization (free() calls, etc.).
703 signal(SIGINT
, SIG_IGN
);
710 } else if (!clreol
&& (promptlen
> 0)) {
720 void copy_file(register FILE *f
)
725 while ((sz
= fread(&buf
, sizeof(char), sizeof(buf
), f
)) > 0)
726 fwrite(&buf
, sizeof(char), sz
, stdout
);
729 #define ringbell() putcerr('\007')
731 static void prompt(char *filename
)
735 else if (promptlen
> 0)
739 if (Senter
&& Sexit
) {
741 promptlen
+= (2 * soglitch
);
745 promptlen
+= printf(_("--More--"));
746 if (filename
!= NULL
) {
747 promptlen
+= printf(_("(Next file: %s)"), filename
);
748 } else if (!no_intty
) {
751 (int)((file_pos
* 100) / file_size
));
755 printf(_("[Press space to continue, 'q' to quit.]"));
767 void prepare_line_buffer(void)
770 size_t nsz
= Mcol
* 4;
778 /* alloc nsz and extra space for \n\0 */
779 nline
= xrealloc(Line
, nsz
+ 2);
784 /* Get a logical line */
785 int get_line(register FILE *f
, int *length
)
796 mbstate_t state
, state_bak
; /* Current status of the stream. */
797 char mbc
[MB_LEN_MAX
]; /* Buffer for one multibyte char. */
798 size_t mblength
; /* Byte length of multibyte char. */
799 size_t mbc_pos
= 0; /* Position of the MBC. */
800 int use_mbc_buffer_flag
= 0; /* If 1, mbc has data. */
801 int break_flag
= 0; /* If 1, exit while(). */
802 long file_pos_bak
= Ftell(f
);
804 memset(&state
, '\0', sizeof(mbstate_t));
807 prepare_line_buffer();
812 if (colflg
&& c
== '\n') {
816 while (p
< &Line
[LineLen
- 1]) {
818 if (fold_opt
&& use_mbc_buffer_flag
&& MB_CUR_MAX
> 1) {
819 use_mbc_buffer_flag
= 0;
823 mblength
= mbrtowc(&wc
, mbc
, mbc_pos
, &state
);
826 case (size_t)-2: /* Incomplete multibyte character. */
827 use_mbc_buffer_flag
= 1;
831 case (size_t)-1: /* Invalid as a multibyte character. */
837 if (column
>= Mcol
) {
838 Fseek(f
, file_pos_bak
);
840 memmove(mbc
, mbc
+ 1, --mbc_pos
);
849 wc_width
= wcwidth(wc
);
851 if (column
+ wc_width
> Mcol
) {
852 Fseek(f
, file_pos_bak
);
855 for (i
= 0; p
< &Line
[LineLen
- 1] &&
863 if (break_flag
|| column
>= Mcol
)
869 #endif /* HAVE_WIDECHAR */
886 if (c
== '\033') { /* ESC */
888 while (c
> ' ' && c
< '0' && p
< &Line
[LineLen
- 1]) {
892 if (c
>= '0' && c
< '\177' && p
< &Line
[LineLen
- 1]) {
900 if (!hardtabs
|| (column
< promptlen
&& !hard
)) {
901 if (hardtabs
&& eraseln
&& !dumb
) {
902 column
= 1 + (column
| 7);
906 for (--p
; p
< &Line
[LineLen
- 1];) {
908 if ((++column
& 7) == 0)
911 if (column
>= promptlen
)
915 column
= 1 + (column
| 7);
916 } else if (c
== '\b' && column
> 0) {
918 } else if (c
== '\r') {
927 } else if (c
== '\f' && stop_opt
) {
932 } else if (c
== EOF
) {
937 if (fold_opt
&& MB_CUR_MAX
> 1) {
938 memset(mbc
, '\0', MB_LEN_MAX
);
943 mblength
= mbrtowc(&wc
, mbc
, mbc_pos
, &state
);
944 /* The value of mblength is always less than 2 here. */
948 file_pos_bak
= Ftell(f
) - 1;
950 use_mbc_buffer_flag
= 1;
959 wc_width
= wcwidth(wc
);
964 #endif /* HAVE_WIDECHAR */
971 if (column
>= Mcol
&& fold_opt
)
974 if (use_mbc_buffer_flag
== 0 && p
>= &Line
[LineLen
- 1 - 4])
975 /* don't read another char if there is no space for
976 * whole multibyte sequence */
981 if (column
>= Mcol
&& Mcol
> 0) {
986 colflg
= column
== Mcol
&& fold_opt
;
987 if (colflg
&& eatnl
&& Wrap
) {
988 *p
++ = '\n'; /* simulate normal wrap */
995 /* Erase the rest of the prompt, assuming we are starting at column col. */
996 void erasep(register int col
)
1006 if (!dumb
&& eraseln
)
1009 printf("%*s", promptlen
- col
, "");
1014 /* Erase the current line entirely */
1015 void kill_line(void)
1018 if (!eraseln
|| dumb
)
1022 /* force clear to end of line */
1034 #ifdef HAVE_WIDECHAR
1035 static UL_ASAN_BLACKLIST
size_t xmbrtowc(wchar_t *wc
, const char *s
, size_t n
,
1038 const size_t mblength
= mbrtowc(wc
, s
, n
, mbstate
);
1039 if (mblength
== (size_t)-2 || mblength
== (size_t)-1)
1045 /* Print a buffer of n characters */
1046 void prbuf(register char *s
, register int n
)
1048 register char c
; /* next output character */
1049 register int state
; /* next output char's UL state */
1050 #define wouldul(s,n) ((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_')))
1056 if (*s
== ' ' && pstate
== 0 && ulglitch
1057 && wouldul(s
+ 1, n
- 1)) {
1061 if ((state
= wouldul(s
, n
)) != 0) {
1062 c
= (*s
== '_') ? s
[2] : *s
;
1067 if (state
!= pstate
) {
1068 if (c
== ' ' && state
== 0 && ulglitch
1069 && wouldul(s
, n
- 1))
1072 putstring(state
? ULenter
: ULexit
);
1074 if (c
!= ' ' || pstate
== 0 || state
!= 0
1076 #ifdef HAVE_WIDECHAR
1081 memset(&mbstate
, '\0', sizeof(mbstate_t));
1084 mblength
= xmbrtowc(&wc
, s
, n
, &mbstate
);
1091 #endif /* HAVE_WIDECHAR */
1092 if (state
&& *chUL
) {
1100 /* Clear the screen */
1103 if (Clear
&& !hard
) {
1105 /* Put out carriage return so that system doesn't get
1106 * confused by escape sequences when expanding tabs */
1112 /* Go to home position */
1118 static int lastcmd
, lastarg
, lastp
;
1119 static int lastcolon
;
1120 static char shell_line
[SHELL_LINE
];
1122 /* Read a command and do it. A command consists of an optional integer
1123 * argument followed by the command character. Return the number of
1124 * lines to display in the next screenful. If there is nothing more to
1125 * display in the current file, zero is returned. */
1126 int command(char *filename
, register FILE *f
)
1128 register int nlines
;
1129 register int retval
= 0;
1133 char comchar
, cmdbuf
[INIT_BUF
];
1135 #define ret(val) retval=val;done++;break
1143 nlines
= number(&comchar
);
1144 lastp
= colonch
= 0;
1145 if (comchar
== '.') { /* Repeat last command */
1150 colonch
= lastcolon
;
1154 if ((cc_t
) comchar
== otty
.c_cc
[VERASE
]) {
1161 retval
= colon(filename
, colonch
, nlines
);
1168 register int initline
;
1183 printf(P_("...back %d page",
1184 "...back %d pages", nlines
),
1190 initline
= Currline
- dlines
* (nlines
+ 1);
1196 Currline
= 0; /* skiplns() will make Currline correct */
1197 skiplns(initline
, f
);
1208 else if (comchar
== 'z')
1231 printf(P_("...skipping %d line",
1232 "...skipping %d lines", nlines
),
1239 while (nlines
> 0) {
1240 while ((c
= Getc(f
)) != '\n')
1259 Fseek(f
, screen_start
.chrctr
);
1260 Currline
= screen_start
.line
;
1269 putsout(_("\n***Back***\n\n"));
1270 Fseek(f
, context
.chrctr
);
1271 Currline
= context
.line
;
1279 promptlen
= printf("%d", Currline
);
1284 more_error(_("No previous regular expression"));
1298 search(previousre
, f
, nlines
);
1300 ttyin(cmdbuf
, sizeof(cmdbuf
) - 2, '/');
1303 previousre
= xstrdup(cmdbuf
);
1304 search(cmdbuf
, f
, nlines
);
1315 "Most commands optionally preceded by integer argument k. "
1316 "Defaults in brackets.\n"
1317 "Star (*) indicates argument becomes new default.\n"));
1318 puts("---------------------------------------"
1319 "----------------------------------------");
1321 ("<space> Display next k lines of text [current screen size]\n"
1322 "z Display next k lines of text [current screen size]*\n"
1323 "<return> Display next k lines of text [1]*\n"
1324 "d or ctrl-D Scroll k lines [current scroll size, initially 11]*\n"
1325 "q or Q or <interrupt> Exit from more\n"
1326 "s Skip forward k lines of text [1]\n"
1327 "f Skip forward k screenfuls of text [1]\n"
1328 "b or ctrl-B Skip backwards k screenfuls of text [1]\n"
1329 "' Go to place where previous search started\n"
1330 "= Display current line number\n"
1331 "/<regular expression> Search for kth occurrence of regular expression [1]\n"
1332 "n Search for kth occurrence of last r.e [1]\n"
1333 "!<cmd> or :!<cmd> Execute <cmd> in a subshell\n"
1334 "v Start up /usr/bin/vi at current line\n"
1335 "ctrl-L Redraw screen\n"
1336 ":n Go to kth next file [1]\n"
1337 ":p Go to kth previous file [1]\n"
1338 ":f Display current file name and line number\n"
1339 ". Repeat previous command\n"));
1340 puts("---------------------------------------"
1341 "----------------------------------------");
1344 case 'v': /* This case should go right before default */
1346 /* Earlier: call vi +n file. This also
1347 * works for emacs. POSIX: call vi -c n
1348 * file (when editor is vi or ex). */
1350 int n
= (Currline
- dlines
<= 0 ? 1 :
1351 Currline
- (dlines
+ 1) / 2);
1354 editor
= getenv("VISUAL");
1355 if (editor
== NULL
|| *editor
== '\0')
1356 editor
= getenv("EDITOR");
1357 if (editor
== NULL
|| *editor
== '\0')
1360 p
= strrchr(editor
, '/');
1365 if (!strcmp(p
, "vi") || !strcmp(p
, "ex")) {
1366 sprintf(cmdbuf
, "-c %d", n
);
1369 sprintf(cmdbuf
, "+%d", n
);
1373 printf("%s %s %s", editor
, cmdbuf
,
1377 execute(filename
, editor
, editor
,
1379 fnames
[fnum
], (char *)0);
1381 execute(filename
, editor
, editor
,
1382 cmdbuf
, fnames
[fnum
],
1390 if (Senter
&& Sexit
) {
1394 ("[Press 'h' for instructions.]"))
1400 ("[Press 'h' for instructions.]"));
1417 /* Execute a colon-prefixed command. Returns <0 if not a command that
1418 * should cause more of the file to be printed. */
1419 int colon(char *filename
, int cmd
, int nlines
)
1431 printf(_("\"%s\" line %d"), fnames
[fnum
], Currline
);
1433 promptlen
= printf(_("[Not a file] line %d"), Currline
);
1438 if (fnum
>= nfiles
- 1)
1469 /* Read a decimal number from the terminal. Set cmd to the non-digit
1470 * which terminates the number. */
1471 int number(char *cmd
)
1476 ch
= otty
.c_cc
[VKILL
];
1480 i
= i
* 10 + ch
- '0';
1481 else if ((cc_t
) ch
== otty
.c_cc
[VKILL
])
1491 void do_shell(char *filename
)
1493 char cmdbuf
[COMMAND_BUF
];
1502 putsout(shell_line
);
1504 ttyin(cmdbuf
, sizeof(cmdbuf
) - 2, '!');
1506 rc
= expand(&expanded
, cmdbuf
);
1508 if (strlen(expanded
) < sizeof(shell_line
))
1509 strcpy(shell_line
, expanded
);
1515 putserr(_(" Overflow\n"));
1518 } else if (rc
> 0) {
1520 promptlen
= printf("!%s", shell_line
);
1527 execute(filename
, shell
, shell
, "-c", shell_line
, 0);
1530 /* Search for nth occurrence of regular expression contained in buf in
1532 void search(char buf
[], FILE *file
, register int n
)
1534 long startline
= Ftell(file
);
1535 register long line1
= startline
;
1536 register long line2
= startline
;
1537 register long line3
;
1538 register int lncount
;
1542 context
.line
= saveln
= Currline
;
1543 context
.chrctr
= startline
;
1547 if ((rc
= regcomp(&re
, buf
, REG_NOSUB
)) != 0) {
1549 regerror(rc
, &re
, s
, sizeof s
);
1552 while (!feof(file
)) {
1555 line1
= Ftell(file
);
1558 if (regexec(&re
, Line
, 0, NULL
, 0) == 0) {
1560 if (lncount
> 3 || (lncount
> 1 && no_intty
)) {
1564 putsout(_("...skipping\n"));
1568 (lncount
>= 3 ? 3 : lncount
);
1596 Fseek(file
, startline
);
1598 putsout(_("\nPattern not found\n"));
1604 more_error(_("Pattern not found"));
1608 void execute(char *filename
, char *cmd
, ...)
1619 for (n
= 10; (id
= fork()) < 0 && n
> 0; n
--)
1624 open("/dev/tty", 0);
1627 va_start(argp
, cmd
);
1628 arg
= va_arg(argp
, char *);
1632 arg
= va_arg(argp
, char *);
1636 args
= alloca(sizeof(char *) * (argcount
+ 1));
1637 args
[argcount
] = NULL
;
1639 va_start(argp
, cmd
);
1640 arg
= va_arg(argp
, char *);
1643 args
[argcount
] = arg
;
1645 arg
= va_arg(argp
, char *);
1650 putserr(_("exec failed\n"));
1654 signal(SIGINT
, SIG_IGN
);
1655 signal(SIGQUIT
, SIG_IGN
);
1657 signal(SIGTSTP
, SIG_DFL
);
1658 while (wait(NULL
) > 0) ;
1659 signal(SIGINT
, end_it
);
1660 signal(SIGQUIT
, onquit
);
1662 signal(SIGTSTP
, onsusp
);
1664 putserr(_("can't fork\n"));
1666 puts("------------------------");
1670 /* Skip n lines in the file f */
1671 void skiplns(register int n
, register FILE *f
)
1676 while ((c
= Getc(f
)) != '\n')
1684 /* Skip nskip files in the file list (from the command line). Nskip may
1686 void skipf(register int nskip
)
1691 if (fnum
+ nskip
> nfiles
- 1)
1692 nskip
= nfiles
- fnum
- 1;
1698 puts(_("\n...Skipping "));
1702 putsout(_("...Skipping to file "));
1704 putsout(_("...Skipping back to file "));
1712 /*----------------------------- Terminal I/O -------------------------------*/
1724 #ifndef NON_INTERACTIVE_MORE
1725 no_tty
= tcgetattr(fileno(stdout
), &otty
);
1728 docrterase
= (otty
.c_cc
[VERASE
] != 255);
1729 docrtkill
= (otty
.c_cc
[VKILL
] != 255);
1733 /* Wait until we're in the foreground before we
1734 * save the terminal modes. */
1735 if ((tgrp
= tcgetpgrp(fileno(stdout
))) < 0)
1736 err(EXIT_FAILURE
, "tcgetpgrp");
1737 if (tgrp
!= getpgrp(0)) {
1742 #endif /* do_SIGTTOU */
1743 if ((term
= getenv("TERM")) == NULL
) {
1747 setupterm(term
, 1, &ret
);
1753 if (ioctl(fileno(stdout
), TIOCGWINSZ
, &win
) < 0) {
1755 Lpp
= tigetnum(TERM_LINES
);
1756 Mcol
= tigetnum(TERM_COLS
);
1759 if ((Lpp
= win
.ws_row
) == 0)
1760 Lpp
= tigetnum(TERM_LINES
);
1761 if ((Mcol
= win
.ws_col
) == 0)
1762 Mcol
= tigetnum(TERM_COLS
);
1765 if ((Lpp
<= 0) || tigetflag(TERM_HARD_COPY
)) {
1766 hard
++; /* Hard copy terminal */
1767 Lpp
= LINES_PER_PAGE
;
1770 if (tigetflag(TERM_EAT_NEW_LINE
))
1771 /* Eat newline at last column + 1; dec, concept */
1776 Wrap
= tigetflag(TERM_AUTO_RIGHT_MARGIN
);
1777 bad_so
= tigetflag(TERM_CEOL
);
1778 eraseln
= tigetstr(TERM_CLEAR_TO_LINE_END
);
1779 Clear
= tigetstr(TERM_CLEAR
);
1780 Senter
= tigetstr(TERM_STANDARD_MODE
);
1781 Sexit
= tigetstr(TERM_EXIT_STANDARD_MODE
);
1782 if ((soglitch
= tigetnum(TERM_STD_MODE_GLITCH
)) < 0)
1785 /* Set up for underlining: some terminals don't
1786 * need it; others have start/stop sequences,
1787 * still others have an underline char sequence
1788 * which is assumed to move the cursor forward
1789 * one character. If underline sequence isn't
1790 * available, settle for standout sequence. */
1791 if (tigetflag(TERM_UNDERLINE
)
1792 || tigetflag(TERM_OVER_STRIKE
))
1794 if ((chUL
= tigetstr(TERM_UNDERLINE_CHAR
)) == NULL
)
1797 tigetstr(TERM_ENTER_UNDERLINE
)) == NULL
1799 tigetstr(TERM_EXIT_UNDERLINE
)) == NULL
)
1801 if ((ULenter
= Senter
) == NULL
1802 || (ULexit
= Sexit
) == NULL
) {
1806 ulglitch
= soglitch
;
1811 if ((padstr
= tigetstr(TERM_PAD_CHAR
)) != NULL
)
1813 Home
= tigetstr(TERM_HOME
);
1814 if (Home
== NULL
|| *Home
== '\0') {
1816 tigetstr(TERM_CURSOR_ADDRESS
)) != NULL
) {
1818 (const char *)tparm(cursorm
, 0,
1820 xstrncpy(cursorhome
, t
,
1821 sizeof(cursorhome
));
1825 EodClr
= tigetstr(TERM_CLEAR_TO_SCREEN_END
);
1826 if ((chBS
= tigetstr(TERM_LINE_DOWN
)) == NULL
)
1830 if ((shell
= getenv("SHELL")) == NULL
)
1833 no_intty
= tcgetattr(fileno(stdin
), &otty
);
1834 tcgetattr(fileno(stderr
), &otty
);
1836 hardtabs
= (otty
.c_oflag
& TABDLY
) != XTABS
;
1838 otty
.c_lflag
&= ~(ICANON
| ECHO
);
1839 otty
.c_cc
[VMIN
] = 1;
1840 otty
.c_cc
[VTIME
] = 0;
1849 if (read(fileno(stderr
), &c
, 1) <= 0) {
1853 c
= otty
.c_cc
[VKILL
];
1858 static char *BS
= "\b";
1859 static char *BSB
= "\b \b";
1860 static char *CARAT
= "^";
1861 #define ERASEONECOLUMN(x) \
1869 void ttyin(char buf
[], register int nmax
, char pchar
)
1878 while (sp
- buf
< nmax
) {
1879 if (promptlen
> maxlen
)
1884 } else if (((cc_t
) c
== otty
.c_cc
[VERASE
]) && !slash
) {
1886 #ifdef HAVE_WIDECHAR
1887 if (MB_CUR_MAX
> 1) {
1889 size_t pos
= 0, mblength
;
1890 mbstate_t state
, state_bak
;
1892 memset(&state
, '\0', sizeof(mbstate_t));
1897 mbrtowc(&wc
, buf
+ pos
,
1900 state
= (mblength
== (size_t)-2
1902 (size_t)-1) ? state_bak
1905 (mblength
== (size_t)-2
1906 || mblength
== (size_t)-1
1910 if (buf
+ pos
+ mblength
>= sp
)
1916 if (mblength
== 1) {
1917 ERASEONECOLUMN(docrterase
);
1920 wc_width
= wcwidth(wc
);
1924 while (wc_width
--) {
1925 ERASEONECOLUMN(docrterase
);
1929 while (mblength
--) {
1934 #endif /* HAVE_WIDECHAR */
1937 ERASEONECOLUMN(docrterase
);
1941 if ((*sp
< ' ' && *sp
!= '\n') || *sp
== RUBOUT
) {
1943 ERASEONECOLUMN(docrterase
);
1949 siglongjmp(restore
, 1);
1951 } else if (((cc_t
) c
== otty
.c_cc
[VKILL
]) && !slash
) {
1962 while (promptlen
-- > 1)
1970 if (slash
&& ((cc_t
) c
== otty
.c_cc
[VKILL
]
1971 || (cc_t
) c
== otty
.c_cc
[VERASE
])) {
1972 ERASEONECOLUMN(docrterase
);
1978 if ((c
< ' ' && c
!= '\n' && c
!= ESC
) || c
== RUBOUT
) {
1979 c
+= (c
== RUBOUT
) ? -0100 : 0100;
1983 if (c
!= '\n' && c
!= ESC
) {
1992 if (sp
- buf
>= nmax
- 1)
1993 more_error(_("Line too long"));
1996 /* return: 0 - unchanged, 1 - changed, -1 - overflow (unchanged) */
1997 int expand(char **outbuf
, char *inbuf
)
2004 int tempsz
, xtra
, offset
;
2006 xtra
= strlen(fnames
[fnum
]) + strlen(shell_line
) + 1;
2007 tempsz
= 200 + xtra
;
2008 temp
= xmalloc(tempsz
);
2011 while ((c
= *inpstr
++) != '\0') {
2012 offset
= outstr
- temp
;
2013 if (tempsz
- offset
- 1 < xtra
) {
2014 tempsz
+= 200 + xtra
;
2015 temp
= xrealloc(temp
, tempsz
);
2016 outstr
= temp
+ offset
;
2021 strcpy(outstr
, fnames
[fnum
]);
2022 outstr
+= strlen(fnames
[fnum
]);
2030 ("No previous command to substitute for"));
2031 strcpy(outstr
, shell_line
);
2032 outstr
+= strlen(shell_line
);
2036 if (*inpstr
== '%' || *inpstr
== '!') {
2037 *outstr
++ = *inpstr
++;
2052 if ((c
< ' ' && c
!= '\n' && c
!= ESC
) || c
== RUBOUT
) {
2053 c
+= (c
== RUBOUT
) ? -0100 : 0100;
2061 void more_error(char *mess
)
2067 promptlen
+= strlen(mess
);
2068 if (Senter
&& Sexit
) {
2076 siglongjmp(restore
, 1);
2081 otty
.c_lflag
&= ~(ICANON
| ECHO
);
2082 otty
.c_cc
[VMIN
] = 1; /* read at least 1 char */
2083 otty
.c_cc
[VTIME
] = 0; /* no timeout */
2084 stty(fileno(stderr
), &otty
);
2087 static int ourputch(int c
)
2089 return putc(c
, stdout
);
2092 void reset_tty(void)
2097 /* putchar - if that isn't a macro */
2098 tputs(ULexit
, fileno(stdout
), ourputch
);
2102 otty
.c_lflag
|= ICANON
| ECHO
;
2103 otty
.c_cc
[VMIN
] = savetty0
.c_cc
[VMIN
];
2104 otty
.c_cc
[VTIME
] = savetty0
.c_cc
[VTIME
];
2105 stty(fileno(stderr
), &savetty0
);
2108 void rdline(register FILE *f
)
2113 prepare_line_buffer();
2116 while ((c
= Getc(f
)) != '\n' && c
!= EOF
2117 && (size_t)(p
- Line
) < LineLen
- 1)
2124 /* Come here when we get a suspend signal from the terminal */
2125 void onsusp(int dummy
__attribute__((__unused__
)))
2127 sigset_t signals
, oldmask
;
2129 /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
2130 signal(SIGTTOU
, SIG_IGN
);
2133 signal(SIGTTOU
, SIG_DFL
);
2134 /* Send the TSTP signal to suspend our process group */
2135 signal(SIGTSTP
, SIG_DFL
);
2137 /* unblock SIGTSTP or we won't be able to suspend ourself */
2138 sigemptyset(&signals
);
2139 sigaddset(&signals
, SIGTSTP
);
2140 sigprocmask(SIG_UNBLOCK
, &signals
, &oldmask
);
2143 /* Pause for station break */
2145 sigprocmask(SIG_SETMASK
, &oldmask
, NULL
);
2148 signal(SIGTSTP
, onsusp
);
2151 siglongjmp(restore
, 1);