* Copyright (c) 1980 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
static char sccsid
[] = "@(#)more.c 5.19 (Berkeley) 6/29/88";
** more.c - General purpose tty output filter and file perusal program
** by Eric Shienbrood, UC Berkeley
** modified by Geoff Peck, UCB to add underlining, single spacing
** modified by John Foderaro, UCB to add -c and MORE environment variable
#define HELPFILE "/usr/lib/more.help"
#define Fopen(s,m) (Currline = 0,file_pos=0,fopen(s,m))
#define Ftell(f) file_pos
#define Fseek(f,off) (file_pos=off,fseek(f,off,0))
#define Getc(f) (++file_pos, getc(f))
#define Ungetc(c,f) (--file_pos, ungetc(c,f))
#define stty(fd,argp) ioctl(fd,TIOCSETN,argp)
#define ctrl(letter) (letter & 077)
struct sgttyb otty
, savetty
;
long file_pos
, file_size
;
int fnum
, no_intty
, no_tty
, slow_tty
;
int dum_opt
, dlines
, onquit(), end_it(), chgwinsz();
int nscroll
= 11; /* Number of lines scrolled by 'd' */
int fold_opt
= 1; /* Fold long lines */
int stop_opt
= 1; /* Stop after form feeds */
int ssp_opt
= 0; /* Suppress white space */
int ul_opt
= 1; /* Underline as best we can */
int Currline
; /* Line we are currently at */
int bad_so
; /* True if overwriting does not turn off standout */
int inwait
, Pause
, errors
;
int within
; /* true if we are within a file,
false if we are between files */
int hard
, dumb
, noscroll
, hardtabs
, clreol
, eatnl
;
int catch_susp
; /* We should catch the SIGTSTP signal */
char **fnames
; /* The list of file names */
int nfiles
; /* Number of files left to process */
char *shell
; /* The name of the shell to use */
int shellp
; /* A previous shell command exists */
char Line
[LINSIZ
]; /* Line buffer */
int Lpp
= 24; /* lines per page */
char *Clear
; /* clear screen */
char *eraseln
; /* erase line */
char *Senter
, *Sexit
;/* enter and exit standout mode */
char *ULenter
, *ULexit
; /* enter and exit underline mode */
char *chUL
; /* underline character */
char *chBS
; /* backspace character */
char *Home
; /* go to home */
char *cursorm
; /* cursor movement */
char cursorhome
[40]; /* contains cursor movement to home */
char *EodClr
; /* clear rest of screen */
int Mcol
= 80; /* number of columns */
int Wrap
= 1; /* set if automargins */
int soglitch
; /* terminal has standout mode glitch */
int ulglitch
; /* terminal has underline mode glitch */
int pstate
= 0; /* current UL state */
extern char PC
; /* pad character */
if(s
= getenv("MORE")) argscan(s
);
if ((ch
= (*++fnames
)[0]) == '-') {
for (++s
, p
= initbuf
; p
< initbuf
+ 79 && *s
!= '\0';)
for (initline
= 0; *s
!= '\0'; s
++)
initline
= initline
*10 + *s
-'0';
/* allow clreol only if Home and eraseln and EodClr strings are
* defined, and in that case, make sure we are in noscroll mode
if((Home
== NULL
) || (*Home
== '\0') ||
(eraseln
== NULL
) || (*eraseln
== '\0') ||
(EodClr
== NULL
) || (*EodClr
== '\0') )
dlines
= Lpp
- (noscroll
? 1 : 2);
if (!no_intty
&& nfiles
== 0) {
p
= rindex(argv
[0], '/');
fputs(p
? p
+ 1 : argv
[0],stderr
);
fputs(" [-dfln] [+linenum | +/pattern] name1 name2 ...\n",stderr
);
signal(SIGWINCH
, chgwinsz
);
if (signal (SIGTSTP
, SIG_IGN
) == SIG_DFL
) {
stty (fileno(stderr
), &otty
);
if ((ch
= Getc (f
)) == '\f')
if (noscroll
&& (ch
!= EOF
)) {
search (initbuf
, stdin
, 1);
skiplns (initline
, stdin
);
if ((f
= checkf (fnames
[fnum
], &clearit
)) != NULL
) {
context
.line
= context
.chrctr
= 0;
if (firstf
) setjmp (restore
);
else if (fnum
< nfiles
&& !no_tty
) {
left
= command (fnames
[fnum
], f
);
if ((noscroll
|| clearit
) && (file_size
!= LONG_MAX
))
printf("%s\n", fnames
[fnum
]);
printf("::::::::::::::\n");
screen_start
.line
= screen_start
.chrctr
= 0L;
context
.line
= context
.chrctr
= 0L;
case '0': case '1': case '2':
case '3': case '4': case '5':
case '6': case '7': case '8':
dlines
= dlines
*10 + *s
- '0';
** Check whether the file named by fs is an ASCII file which the user may
** access. If it is, return the opened file. Otherwise return NULL.
if (stat (fs
, &stbuf
) == -1) {
if ((stbuf
.st_mode
& S_IFMT
) == S_IFDIR
) {
printf("\n*** %s: directory ***\n\n", fs
);
if ((f
= Fopen(fs
, "r")) == NULL
) {
if ((file_size
= stbuf
.st_size
) == 0)
* check for file magic numbers. This code would best be shared with
* the file(1) program or, perhaps, more should not try and be so smart?
if (fread(&ex
, sizeof(ex
), 1, f
) == 1)
printf("\n******** %s: Not a text file ********\n\n", fs
);
(void)fseek(f
, 0L, L_SET
); /* rewind() not necessary */
** A real function, for the tputs routine in termlib
** Print out the contents of the file f, one screenful at a time.
int length
; /* length of current line */
static int prev_len
= 1; /* length of previous line */
while (num_lines
> 0 && !Pause
) {
if ((nchars
= getline (f
, &length
)) == EOF
)
if (ssp_opt
&& length
== 0 && prev_len
== 0)
if (bad_so
|| (Senter
&& *Senter
== ' ') && promptlen
> 0)
/* must clear before drawing line since tabs on some terminals
* do not erase what they tab over.
erase (nchars
); /* erase () sets promptlen to 0 */
* cleareol(); /* must clear again in case we wrapped *
if (nchars
< Mcol
|| !fold_opt
)
prbuf("\n", 1); /* will turn off UL if necessary */
if ((c
= Getc(f
)) == EOF
)
if ((num_lines
= command (NULL
, f
)) == 0)
if (hard
&& promptlen
> 0)
if (noscroll
&& num_lines
>= dlines
)
screen_start
.line
= Currline
;
screen_start
.chrctr
= Ftell (f
);
** Come here if a quit signal is received
signal(SIGQUIT
, SIG_IGN
);
else if (!dum_opt
&& notell
) {
write (2, "[Use q or Q to quit]", 20);
** Come here if a signal for a window size change is received
(void) signal(SIGWINCH
, SIG_IGN
);
if (ioctl(fileno(stdout
), TIOCGWINSZ
, &win
) != -1) {
dlines
= Lpp
- (noscroll
? 1 : 2);
(void) signal(SIGWINCH
, chgwinsz
);
** Clean up terminal state and exit. Also come here if interrupt signal received
else if (!clreol
&& (promptlen
> 0)) {
while ((c
= getc(f
)) != EOF
)
/* Simplified printf function */
while ((ch
= *fmt
++) != '%') {
ccount
+= printd (va_arg(ap
, int));
ccount
+= pr (va_arg(ap
, char *));
** Print an integer as a string of decimal digits,
** returning the length of the print representation.
/* Put the print representation of an integer into a string */
static char bell
= ctrl('G');
/* See whether the last component of the path name "path" is equal to the
tail
= path
+ strlen(path
);
while (*tail
++ == *string
++)
tputs (Senter
, 1, putch
);
promptlen
+= (2 * soglitch
);
promptlen
+= printf ("(Next file: %s)", filename
);
promptlen
+= printf ("(%d%%)", (int)((file_pos
* 100) / file_size
));
promptlen
+= pr("[Press space to continue, 'q' to quit.]");
if (colflg
&& c
== '\n') {
while (p
< &Line
[LINSIZ
- 1]) {
if (!hardtabs
|| column
< promptlen
&& !hard
) {
if (hardtabs
&& eraseln
&& !dumb
) {
column
= 1 + (column
| 7);
tputs (eraseln
, 1, putch
);
for (--p
; p
< &Line
[LINSIZ
- 1];) {
if (column
>= promptlen
) promptlen
= 0;
column
= 1 + (column
| 7);
else if (c
== '\b' && column
> 0)
else if (c
== '\f' && stop_opt
) {
else if (c
>= ' ' && c
!= RUBOUT
)
if (column
>= Mcol
&& fold_opt
) break;
if (column
>= Mcol
&& Mcol
> 0) {
colflg
= column
== Mcol
&& fold_opt
;
if (colflg
&& eatnl
&& Wrap
) {
*p
++ = '\n'; /* simulate normal wrap */
** Erase the rest of the prompt, assuming we are starting at column col.
tputs (eraseln
, 1, putch
);
for (col
= promptlen
- col
; col
> 0; col
--)
** Erase the current line entirely
if (!eraseln
|| dumb
) putchar ('\r');
* force clear to end of line
tputs(eraseln
, 1, putch
);
** Print string and return number of characters
/* Print a buffer of n characters */
register char c
; /* next output character */
register int state
; /* next output char's UL state */
#define wouldul(s,n) ((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_')))
if (*s
== ' ' && pstate
== 0 && ulglitch
&& wouldul(s
+1, n
-1)) {
if (state
= wouldul(s
, n
)) {
c
= (*s
== '_')? s
[2] : *s
;
if (c
== ' ' && state
== 0 && ulglitch
&& wouldul(s
, n
-1))
tputs(state
? ULenter
: ULexit
, 1, putch
);
if (c
!= ' ' || pstate
== 0 || state
!= 0 || ulglitch
== 0)
/* Put out carriage return so that system doesn't
** get confused by escape sequences when expanding tabs
static int lastcmd
, lastarg
, lastp
;
** Read a command and do it. A command consists of an optional integer
** argument followed by the command character. Return the number of lines
** to display in the next screenful. If there is nothing more to display
** in the current file, zero is returned.
char comchar
, cmdbuf
[80], *p
;
#define ret(val) retval=val;done++;break
if (MBIT
== RAW
&& slow_tty
) {
stty(fileno(stderr
), &otty
);
nlines
= number (&comchar
);
if (comchar
== '.') { /* Repeat last command */
if (comchar
== otty
.sg_erase
) {
retval
= colon (filename
, colonch
, nlines
);
if (nlines
== 0) nlines
++;
printf ("...back %d page", nlines
);
initline
= Currline
- dlines
* (nlines
+ 1);
if (initline
< 0) initline
= 0;
Currline
= 0; /* skiplns() will make Currline correct */
if (nlines
== 0) nlines
= dlines
;
else if (comchar
== 'z') dlines
= nlines
;
if (nlines
!= 0) nscroll
= nlines
;
if (nlines
== 0) nlines
++;
printf ("...skipping %d line", nlines
);
while ((c
= Getc (f
)) != '\n')
Fseek (f
, screen_start
.chrctr
);
Currline
= screen_start
.line
;
Fseek (f
, context
.chrctr
);
promptlen
= printd (Currline
);
if (nlines
== 0) nlines
++;
search (NULL
, f
, nlines
); /* Use previous r.e. */
search (cmdbuf
, f
, nlines
);
if ((helpf
= fopen (HELPFILE
, "r")) == NULL
)
error ("Can't open help file");
if (noscroll
) doclear ();
case 'v': /* This case should go right before default */
scanstr (Currline
- dlines
< 0 ? 0
: Currline
- (dlines
+ 1) / 2, &cmdbuf
[1]);
pr ("vi "); pr (cmdbuf
); putchar (' '); pr (fnames
[fnum
]);
execute (filename
, VI
, "vi", cmdbuf
, fnames
[fnum
], 0);
tputs (Senter
, 1, putch
);
promptlen
= pr ("[Press 'h' for instructions.]") + (2 * soglitch
);
promptlen
= pr ("[Press 'h' for instructions.]");
if (MBIT
== RAW
&& slow_tty
) {
stty(fileno(stderr
), &otty
);
* Execute a colon-prefixed command.
* Returns <0 if not a command that should cause
* more of the file to be printed.
colon (filename
, cmd
, nlines
)
promptlen
= printf ("\"%s\" line %d", fnames
[fnum
], Currline
);
promptlen
= printf ("[Not a file] line %d", Currline
);
** Read a decimal number from the terminal. Set cmd to the non-digit which
** terminates the number.
i
= 0; ch
= otty
.sg_kill
;
if (ch
>= '0' && ch
<= '9')
else if (ch
== otty
.sg_kill
)
if (expand (shell_line
, cmdbuf
)) {
promptlen
= printf ("!%s", shell_line
);
execute (filename
, shell
, shell
, "-c", shell_line
, 0);
** Search for nth ocurrence of regular expression contained in buf in the file
long startline
= Ftell (file
);
register long line1
= startline
;
register long line2
= startline
;
register long line3
= startline
;
int saveln
, rv
, re_exec();
context
.line
= saveln
= Currline
;
context
.chrctr
= startline
;
if ((s
= re_comp (buf
)) != 0)
if ((rv
= re_exec (Line
)) == 1)
if (lncount
> 3 || (lncount
> 1 && no_intty
))
Currline
-= (lncount
>= 3 ? 3 : lncount
);
error ("Regular expression botch");
file
->_flag
&= ~_IOEOF
; /* why doesn't fseek do this ??!!??! */
pr ("\nPattern not found\n");
error ("Pattern not found");
execute (filename
, cmd
, va_alist
)
for (n
= 10; (id
= fork ()) < 0 && n
> 0; n
--)
write (2, "exec failed\n", 12);
va_end(argp
); /* balance {}'s for some UNIX's */
signal (SIGINT
, SIG_IGN
);
signal (SIGQUIT
, SIG_IGN
);
signal(SIGTSTP
, SIG_DFL
);
signal (SIGQUIT
, onquit
);
write(2, "can't fork\n", 11);
pr ("------------------------\n");
** Skip n lines in the file f
while ((c
= Getc (f
)) != '\n')
** Skip nskip files in the file list (from the command line). Nskip may be
if (fnum
+ nskip
> nfiles
- 1)
nskip
= nfiles
- fnum
- 1;
pr (nskip
> 0 ? "to file " : "back to file ");
/*----------------------------- Terminal I/O -------------------------------*/
static char clearbuf
[TBUFSIZ
];
if (!(no_tty
= gtty(fileno(stdout
), &otty
))) {
if (ioctl(fileno(stdout
), TIOCLGET
, &lmode
) < 0) {
docrterase
= ((lmode
& LCRTERA
) != 0);
docrtkill
= ((lmode
& LCRTKIL
) != 0);
* Wait until we're in the foreground before we save the
if (ioctl(fileno(stdout
), TIOCGPGRP
, &tgrp
) < 0) {
if (tgrp
!= getpgrp(0)) {
if ((term
= getenv("TERM")) == 0 || tgetent(buf
, term
) <= 0) {
if (ioctl(fileno(stdout
), TIOCGWINSZ
, &win
) < 0) {
if ((Lpp
= win
.ws_row
) == 0)
if ((Mcol
= win
.ws_col
) == 0)
if ((Lpp
<= 0) || tgetflag("hc")) {
hard
++; /* Hard copy terminal */
eatnl
++; /* Eat newline at last column + 1; dec, concept */
if (tailequ (fnames
[0], "page") || !hard
&& tgetflag("ns"))
bad_so
= tgetflag ("xs");
eraseln
= tgetstr("ce",&clearptr
);
Clear
= tgetstr("cl", &clearptr
);
Senter
= tgetstr("so", &clearptr
);
Sexit
= tgetstr("se", &clearptr
);
if ((soglitch
= tgetnum("sg")) < 0)
* Set up for underlining: some terminals don't need it;
* others have start/stop sequences, still others have an
* underline char sequence which is assumed to move the
* cursor forward one character. If underline sequence
* isn't available, settle for standout sequence.
if (tgetflag("ul") || tgetflag("os"))
if ((chUL
= tgetstr("uc", &clearptr
)) == NULL
)
if (((ULenter
= tgetstr("us", &clearptr
)) == NULL
||
(ULexit
= tgetstr("ue", &clearptr
)) == NULL
) && !*chUL
) {
if ((ULenter
= Senter
) == NULL
|| (ULexit
= Sexit
) == NULL
) {
if ((ulglitch
= tgetnum("ug")) < 0)
if (padstr
= tgetstr("pc", &clearptr
))
Home
= tgetstr("ho",&clearptr
);
if (Home
== 0 || *Home
== '\0')
if ((cursorm
= tgetstr("cm", &clearptr
)) != NULL
) {
strcpy(cursorhome
, tgoto(cursorm
, 0, 0));
EodClr
= tgetstr("cd", &clearptr
);
if ((chBS
= tgetstr("bc", &clearptr
)) == NULL
)
if ((shell
= getenv("SHELL")) == NULL
)
no_intty
= gtty(fileno(stdin
), &otty
);
gtty(fileno(stderr
), &otty
);
slow_tty
= ospeed
< B1200
;
hardtabs
= (otty
.sg_flags
& TBDELAY
) != XTABS
;
if (MBIT
== CBREAK
|| !slow_tty
)
if (read (2, &ch
, 1) <= 0)
static char *BSB
= "\b \b";
write (2, BSB, sizeof(BSB)); \
write (2, &BS, sizeof(BS));
while (sptr
- buf
< nmax
) {
if (promptlen
> maxlen
) maxlen
= promptlen
;
else if ((ch
== otty
.sg_erase
) && !slash
) {
if ((*sptr
< ' ' && *sptr
!= '\n') || *sptr
== RUBOUT
) {
if (!eraseln
) promptlen
= maxlen
;
else if ((ch
== otty
.sg_kill
) && !slash
) {
write (2, BSB
, sizeof(BSB
));
if (slash
&& (ch
== otty
.sg_kill
|| ch
== otty
.sg_erase
)) {
if ((ch
< ' ' && ch
!= '\n' && ch
!= ESC
) || ch
== RUBOUT
) {
ch
+= ch
== RUBOUT
? -0100 : 0100;
if (ch
!= '\n' && ch
!= ESC
) {
if (!eraseln
) promptlen
= maxlen
;
if (sptr
- buf
>= nmax
- 1)
while ((ch
= *instr
++) != '\0')
strcpy (outstr
, fnames
[fnum
]);
outstr
+= strlen (fnames
[fnum
]);
error ("No previous command to substitute for");
strcpy (outstr
, shell_line
);
outstr
+= strlen (shell_line
);
if (*instr
== '%' || *instr
== '!') {
if ((ch
< ' ' && ch
!= '\n' && ch
!= ESC
) || ch
== RUBOUT
) {
ch
+= ch
== RUBOUT
? -0100 : 0100;
promptlen
+= strlen (mess
);
tputs (Senter
, 1, putch
);
stty(fileno(stderr
), &otty
);
stty(fileno(stderr
), &savetty
);
while ((c
= Getc (f
)) != '\n' && c
!= EOF
&& p
- Line
< LINSIZ
- 1)
/* Come here when we get a suspend signal from the terminal */
/* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
signal(SIGTTOU
, SIG_IGN
);
signal(SIGTTOU
, SIG_DFL
);
/* Send the TSTP signal to suspend our process group */
signal(SIGTSTP
, SIG_DFL
);
/* Pause for station break */
signal (SIGTSTP
, onsusp
);