]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - text-utils/more.c
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.
19 ** more.c - General purpose tty output filter and file perusal program
21 ** by Eric Shienbrood, UC Berkeley
23 ** modified by Geoff Peck, UCB to add underlining, single spacing
24 ** modified by John Foderaro, UCB to add -c and MORE environment variable
25 ** modified by Erik Troan <ewt@redhat.com> to be more posix and so compile
27 ** modified by Kars de Jong <jongk@cs.utwente.nl> to use terminfo instead
29 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
30 - added Native Language Support
31 1999-03-19 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
32 - more nls translatable strings
33 1999-05-09 aeb - applied a RedHat patch (setjmp->sigsetjmp); without it
34 a second ^Z would fail.
35 1999-05-09 aeb - undone Kars' work, so that more works without
36 libcurses (and hence can be in /bin with libcurses being in /usr/lib
37 which may not be mounted). However, when termcap is not present curses
44 #include <stdlib.h> /* for alloca() */
45 #include <stdarg.h> /* for va_start() etc */
46 #include <sys/param.h>
52 #include <sys/ioctl.h>
62 #define _REGEX_RE_COMP
66 #define VI "vi" /* found on the user's path */
68 #define Fopen(s,m) (Currline = 0,file_pos=0,fopen(s,m))
69 #define Ftell(f) file_pos
70 #define Fseek(f,off) (file_pos=off,fseek(f,off,0))
71 #define Getc(f) (++file_pos, getc(f))
72 #define Ungetc(c,f) (--file_pos, ungetc(c,f))
74 #define stty(fd,argp) tcsetattr(fd,TCSANOW,argp)
76 /* some function declarations */
83 void error (char *mess
);
84 void do_shell (char *filename
);
85 int colon (char *filename
, int cmd
, int nlines
);
86 int expand (char **outbuf
, char *inbuf
);
87 void argscan(char *s
,char *argv0
);
88 void rdline (register FILE *f
);
89 void copy_file(register FILE *f
);
90 void search(char buf
[], FILE *file
, register int n
);
91 void skipf (register int nskip
);
92 void skiplns(register int n
, register FILE *f
);
93 void screen (register FILE *f
, register int num_lines
);
94 int command (char *filename
, register FILE *f
);
95 void erasep (register int col
);
96 void show (register char ch
);
100 void reset_tty(void);
101 void ttyin (unsigned char buf
[], register int nmax
, char pchar
);
102 int number(char *cmd
);
104 int get_line(register FILE *f
, int *length
);
105 void prbuf (register char *s
, register int n
);
106 int xprintf (char *fmt
, ...);
107 void execute (char *filename
, char *cmd
, ...);
108 void errwrite (char *txt
);
109 void errwrite1 (char *sym
);
110 FILE *checkf (char *, int *);
114 #define ctrl(letter) (letter & 077)
115 #define RUBOUT '\177'
119 struct termios otty
, savetty0
;
120 long file_pos
, file_size
;
121 int fnum
, no_intty
, no_tty
, slow_tty
;
123 void onquit(int), onsusp(int), chgwinsz(int), end_it(int);
124 int nscroll
= 11; /* Number of lines scrolled by 'd' */
125 int fold_opt
= 1; /* Fold long lines */
126 int stop_opt
= 1; /* Stop after form feeds */
127 int ssp_opt
= 0; /* Suppress white space */
128 int ul_opt
= 1; /* Underline as best we can */
130 int Currline
; /* Line we are currently at */
136 int bad_so
; /* True if overwriting does not turn off standout */
137 int inwait
, Pause
, errors
;
138 int within
; /* true if we are within a file,
139 false if we are between files */
140 int hard
, dumb
, noscroll
, hardtabs
, clreol
, eatnl
;
141 int catch_susp
; /* We should catch the SIGTSTP signal */
142 char **fnames
; /* The list of file names */
143 int nfiles
; /* Number of files left to process */
144 char *shell
; /* The name of the shell to use */
145 int shellp
; /* A previous shell command exists */
147 char Line
[LINSIZ
+2]; /* Line buffer */
148 int Lpp
= 24; /* lines per page */
149 char *Clear
; /* clear screen */
150 char *eraseln
; /* erase line */
151 char *Senter
, *Sexit
;/* enter and exit standout mode */
152 char *ULenter
, *ULexit
; /* enter and exit underline mode */
153 char *chUL
; /* underline character */
154 char *chBS
; /* backspace character */
155 char *Home
; /* go to home */
156 char *cursorm
; /* cursor movement */
157 char cursorhome
[40]; /* contains cursor movement to home */
158 char *EodClr
; /* clear rest of screen */
159 int Mcol
= 80; /* number of columns */
160 int Wrap
= 1; /* set if automargins */
161 int soglitch
; /* terminal has standout mode glitch */
162 int ulglitch
; /* terminal has underline mode glitch */
163 int pstate
= 0; /* current UL state */
164 static int magic(FILE *, char *);
167 } context
, screen_start
;
168 extern char PC
; /* pad character */
181 #include <term.h> /* include after <curses.h> */
184 my_putstring(char *s
) {
189 my_setupterm(const char *term
, int fildes
, int *errret
) {
190 setupterm(term
, fildes
, errret
);
194 my_tgetnum(char *s
, char *ss
) {
199 my_tgetflag(char *s
, char *ss
) {
200 return tigetflag(ss
);
204 my_tgetstr(char *s
, char *ss
) {
209 my_tgoto(const char *cap
, int col
, int row
) {
210 return tparm(cap
, col
, row
);
213 #else /* no CURSES */
216 char termbuffer
[4096];
218 char *strbuf
= termbuffer
;
221 my_putstring(char *s
) {
222 tputs (s
, 1, putchar
);
226 my_setupterm(const char *term
, int fildes
, int *errret
) {
227 *errret
= tgetent(tcbuffer
, term
);
231 my_tgetnum(char *s
, char *ss
) {
236 my_tgetflag(char *s
, char *ss
) {
241 my_tgetstr(char *s
, char *ss
) {
242 return tgetstr(s
, &strbuf
);
246 my_tgoto(const char *cap
, int col
, int row
) {
247 return tgoto(cap
, col
, row
);
251 #endif /* USE_CURSES */
261 char *p
= strrchr(s
, '/');
263 _("usage: %s [-dflpcsu] [+linenum | +/pattern] name1 name2 ...\n"),
267 int main(int argc
, char **argv
) {
280 setlocale(LC_ALL
, "");
281 bindtextdomain(PACKAGE
, LOCALEDIR
);
284 /* avoid gcc complaints about register variables that
285 may be clobbered by a longjmp, by forcing our variables here
286 to be non-register */
287 Fdummy(&f
); idummy(&left
); idummy(&prnames
);
288 idummy(&initopt
); idummy(&srchopt
); idummy(&initline
);
292 setlocale(LC_ALL
, "");
297 if((s
= getenv("MORE")) != NULL
) argscan(s
,argv
[0]);
298 while (--nfiles
> 0) {
299 if ((ch
= (*++fnames
)[0]) == '-') {
300 argscan(*fnames
+1,argv
[0]);
302 else if (ch
== '+') {
306 for (++s
, p
= initbuf
; p
< initbuf
+ 79 && *s
!= '\0';)
312 for (initline
= 0; *s
!= '\0'; s
++)
314 initline
= initline
*10 + *s
-'0';
320 /* allow clreol only if Home and eraseln and EodClr strings are
321 * defined, and in that case, make sure we are in noscroll mode
325 if((Home
== NULL
) || (*Home
== '\0') ||
326 (eraseln
== NULL
) || (*eraseln
== '\0') ||
327 (EodClr
== NULL
) || (*EodClr
== '\0') )
332 dlines
= Lpp
- (noscroll
? 1 : 2);
336 if (!no_intty
&& nfiles
== 0) {
343 signal(SIGQUIT
, onquit
);
344 signal(SIGINT
, end_it
);
346 signal(SIGWINCH
, chgwinsz
);
348 if (signal (SIGTSTP
, SIG_IGN
) == SIG_DFL
) {
349 signal(SIGTSTP
, onsusp
);
352 stty (fileno(stderr
), &otty
);
358 if ((ch
= Getc (f
)) == '\f')
362 if (noscroll
&& (ch
!= EOF
)) {
371 search (initbuf
, stdin
, 1);
376 skiplns (initline
, stdin
);
377 screen (stdin
, left
);
384 while (fnum
< nfiles
) {
385 if ((f
= checkf (fnames
[fnum
], &clearit
)) != NULL
) {
386 context
.line
= context
.chrctr
= 0;
388 if (firstf
) sigsetjmp (restore
, 1);
392 search (initbuf
, f
, 1);
397 skiplns (initline
, f
);
399 else if (fnum
< nfiles
&& !no_tty
) {
400 sigsetjmp (restore
, 1);
401 left
= command (fnames
[fnum
], f
);
404 if ((noscroll
|| clearit
) && (file_size
!= LONG_MAX
)) {
415 pr("::::::::::::::");
419 if(clreol
) cleareol();
420 xprintf("%s\n", fnames
[fnum
]);
421 if(clreol
) cleareol();
422 xprintf("::::::::::::::\n");
434 sigsetjmp (restore
, 1);
437 screen_start
.line
= screen_start
.chrctr
= 0L;
438 context
.line
= context
.chrctr
= 0L;
447 void argscan(char *s
, char *argv0
) {
452 case '0': case '1': case '2':
453 case '3': case '4': case '5':
454 case '6': case '7': case '8':
460 dlines
= dlines
*10 + *s
- '0';
483 case '-': case ' ': case '\t':
487 fputs(": unknown option \"-",stderr
);
489 fputs("\"\n",stderr
);
500 ** Check whether the file named by fs is an ASCII file which the user may
501 ** access. If it is, return the opened file. Otherwise return NULL.
505 checkf (fs
, clearfirst
)
513 if (stat (fs
, &stbuf
) == -1) {
514 (void)fflush(stdout
);
518 return((FILE *)NULL
);
520 if ((stbuf
.st_mode
& S_IFMT
) == S_IFDIR
) {
521 xprintf(_("\n*** %s: directory ***\n\n"), fs
);
522 return((FILE *)NULL
);
524 if ((f
= Fopen(fs
, "r")) == NULL
) {
525 (void)fflush(stdout
);
527 return((FILE *)NULL
);
530 return((FILE *)NULL
);
532 *clearfirst
= (c
== '\f');
534 if ((file_size
= stbuf
.st_size
) == 0)
535 file_size
= LONG_MAX
;
541 * check for file magic numbers. This code would best be shared with
542 * the file(1) program or, perhaps, more should not try and be so smart?
551 /* don't try to look ahead if the input is unseekable */
552 if (fseek(f
, 0L, SEEK_SET
))
555 if (fread(twobytes
, 2, 1, f
) == 1) {
556 switch(twobytes
[0] + (twobytes
[1]<<8)) {
557 case OMAGIC
: /* 0407 */
558 case NMAGIC
: /* 0410 */
559 case ZMAGIC
: /* 0413 */
563 case 0x457f: /* simple ELF detection */
564 xprintf(_("\n******** %s: Not a text file ********\n\n"), fs
);
569 (void)fseek(f
, 0L, SEEK_SET
); /* rewind() not necessary */
574 ** Print out the contents of the file f, one screenful at a time.
579 void screen (register FILE *f
, register int num_lines
)
583 int length
; /* length of current line */
584 static int prev_len
= 1; /* length of previous line */
587 while (num_lines
> 0 && !Pause
) {
588 if ((nchars
= get_line (f
, &length
)) == EOF
)
594 if (ssp_opt
&& length
== 0 && prev_len
== 0)
597 if (bad_so
|| ((Senter
&& *Senter
== ' ') && (promptlen
> 0)))
599 /* must clear before drawing line since tabs on some terminals
600 * do not erase what they tab over.
604 prbuf (Line
, length
);
605 if (nchars
< promptlen
)
606 erasep (nchars
); /* erasep () sets promptlen to 0 */
610 * cleareol(); * must clear again in case we wrapped *
612 if (nchars
< Mcol
|| !fold_opt
)
613 prbuf("\n", 1); /* will turn off UL if necessary */
619 my_putstring (ULexit
);
623 if ((c
= Getc(f
)) == EOF
)
633 sigsetjmp (restore
, 1);
634 Pause
= 0; startup
= 0;
635 if ((num_lines
= command (NULL
, f
)) == 0)
637 if (hard
&& promptlen
> 0)
639 if (noscroll
&& num_lines
>= dlines
)
646 screen_start
.line
= Currline
;
647 screen_start
.chrctr
= Ftell (f
);
652 ** Come here if a quit signal is received
655 void onquit(int dummy
) {
656 signal(SIGQUIT
, SIG_IGN
);
660 signal(SIGQUIT
, onquit
);
661 siglongjmp (restore
, 1);
666 else if (!dum_opt
&& notell
) {
667 char *s
= _("[Use q or Q to quit]");
669 promptlen
+= strlen(s
);
672 signal(SIGQUIT
, onquit
);
676 ** Come here if a signal for a window size change is received
680 void chgwinsz(int dummy
) {
683 (void) signal(SIGWINCH
, SIG_IGN
);
684 if (ioctl(fileno(stdout
), TIOCGWINSZ
, &win
) != -1) {
685 if (win
.ws_row
!= 0) {
690 dlines
= Lpp
- (noscroll
? 1 : 2);
695 (void) signal(SIGWINCH
, chgwinsz
);
700 ** Clean up terminal state and exit. Also come here if interrupt signal received
703 void end_it (int dummy
) {
710 else if (!clreol
&& (promptlen
> 0)) {
719 void copy_file(register FILE *f
) {
722 while ((c
= getc(f
)) != EOF
)
726 /* Simplified printf function */
728 int xprintf (char *fmt
, ...) {
736 while ((ch
= *fmt
++) != '%') {
744 ccount
+= printd (va_arg(ap
, int));
747 ccount
+= pr (va_arg(ap
, char *));
765 ** Print an integer as a string of decimal digits,
766 ** returning the length of the print representation.
774 nchars
= 1 + printd(a
);
777 putchar (n
% 10 + '0');
781 /* Put the print representation of an integer into a string */
784 static void Sprintf (int n
) {
789 *sptr
++ = n
% 10 + '0';
792 static void scanstr (int n
, char *str
)
799 #define ringbell() errwrite("\007");
814 /* See whether the last component of the path name "path" is equal to the
818 static int tailequ (char *path
, register char *string
)
822 tail
= path
+ strlen(path
);
823 while (--tail
>= path
)
827 while (*tail
++ == *string
++)
833 static void prompt (char *filename
)
837 else if (promptlen
> 0)
841 if (Senter
&& Sexit
) {
842 my_putstring (Senter
);
843 promptlen
+= (2 * soglitch
);
847 promptlen
+= pr(_("--More--"));
848 if (filename
!= NULL
) {
849 promptlen
+= xprintf (_("(Next file: %s)"), filename
);
850 } else if (!no_intty
) {
851 promptlen
+= xprintf ("(%d%%)",
852 (int)((file_pos
* 100) / file_size
));
855 promptlen
+= pr(_("[Press space to continue, 'q' to quit.]"));
858 my_putstring (Sexit
);
872 int get_line(register FILE *f
, int *length
)
879 #ifdef ENABLE_WIDECHAR
883 mbstate_t state
, state_bak
; /* Current status of the stream. */
884 unsigned char mbc
[MB_LEN_MAX
]; /* Buffer for one multibyte char. */
885 size_t mblength
; /* Byte length of multibyte char. */
886 size_t mbc_pos
= 0; /* Postion of the MBC. */
887 int use_mbc_buffer_flag
= 0; /* If 1, mbc has data. */
888 int break_flag
= 0; /* If 1, exit while(). */
889 long file_pos_bak
= Ftell (f
);
891 memset (&state
, '\0', sizeof (mbstate_t));
897 if (colflg
&& c
== '\n') {
901 while (p
< &Line
[LINSIZ
- 1]) {
902 #ifdef ENABLE_WIDECHAR
903 if (fold_opt
&& use_mbc_buffer_flag
&& MB_CUR_MAX
> 1) {
904 use_mbc_buffer_flag
= 0;
908 mblength
= mbrtowc (&wc
, mbc
, mbc_pos
, &state
);
911 case (size_t)-2: /* Incomplete multibyte character. */
912 use_mbc_buffer_flag
= 1;
916 case (size_t)-1: /* Invalid as a multibyte character. */
922 if (column
>= Mcol
) {
923 Fseek (f
, file_pos_bak
);
925 memmove (mbc
, mbc
+ 1, --mbc_pos
);
934 wc_width
= wcwidth (wc
);
936 if (column
+ wc_width
> Mcol
) {
937 Fseek (f
, file_pos_bak
);
940 for (i
= 0; i
< mbc_pos
; i
++)
947 if (break_flag
|| column
>= Mcol
)
970 if (c
== '\033') { /* ESC */
972 while (c
> ' ' && c
< '0' && p
< &Line
[LINSIZ
- 1]) {
976 if (c
>= '0' && c
< '\177' && p
< &Line
[LINSIZ
- 1]) {
984 if (!hardtabs
|| (column
< promptlen
&& !hard
)) {
985 if (hardtabs
&& eraseln
&& !dumb
) {
986 column
= 1 + (column
| 7);
987 my_putstring (eraseln
);
991 for (--p
; p
< &Line
[LINSIZ
- 1];) {
993 if ((++column
& 7) == 0)
996 if (column
>= promptlen
) promptlen
= 0;
999 column
= 1 + (column
| 7);
1000 } else if (c
== '\b' && column
> 0) {
1002 } else if (c
== '\r') {
1011 } else if (c
== '\f' && stop_opt
) {
1016 } else if (c
== EOF
) {
1020 #ifdef ENABLE_WIDECHAR
1021 if (fold_opt
&& MB_CUR_MAX
> 1) {
1022 memset (mbc
, '\0', MB_LEN_MAX
);
1027 mblength
= mbrtowc (&wc
, mbc
, mbc_pos
, &state
);
1029 /* The value of mblength is always less than 2 here. */
1033 file_pos_bak
= Ftell (f
) - 1;
1035 use_mbc_buffer_flag
= 1;
1044 wc_width
= wcwidth (wc
);
1056 if (column
>= Mcol
&& fold_opt
)
1060 if (column
>= Mcol
&& Mcol
> 0) {
1065 colflg
= column
== Mcol
&& fold_opt
;
1066 if (colflg
&& eatnl
&& Wrap
) {
1067 *p
++ = '\n'; /* simulate normal wrap */
1075 ** Erase the rest of the prompt, assuming we are starting at column col.
1078 void erasep (register int col
)
1089 if (!dumb
&& eraseln
)
1090 my_putstring (eraseln
);
1092 for (col
= promptlen
- col
; col
> 0; col
--)
1099 ** Erase the current line entirely
1105 if (!eraseln
|| dumb
)
1110 * force clear to end of line
1114 my_putstring(eraseln
);
1119 my_putstring(EodClr
);
1123 ** Print string and return number of characters
1131 for (s
= s1
; (c
= *s
++) != 0; )
1133 return (int) (s
- s1
- 1);
1137 /* Print a buffer of n characters */
1139 void prbuf (register char *s
, register int n
)
1141 register char c
; /* next output character */
1142 register int state
; /* next output char's UL state */
1143 #define wouldul(s,n) ((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_')))
1149 if (*s
== ' ' && pstate
== 0 && ulglitch
&& wouldul(s
+1, n
-1)) {
1153 if ((state
= wouldul(s
, n
)) != 0) {
1154 c
= (*s
== '_')? s
[2] : *s
;
1159 if (state
!= pstate
) {
1160 if (c
== ' ' && state
== 0 && ulglitch
&& wouldul(s
, n
-1))
1163 my_putstring(state
? ULenter
: ULexit
);
1165 if (c
!= ' ' || pstate
== 0 || state
!= 0 || ulglitch
== 0)
1167 if (state
&& *chUL
) {
1181 if (Clear
&& !hard
) {
1182 my_putstring(Clear
);
1184 /* Put out carriage return so that system doesn't
1185 ** get confused by escape sequences when expanding tabs
1193 * Go to home position
1201 static int lastcmd
, lastarg
, lastp
;
1202 static int lastcolon
;
1203 char shell_line
[1000];
1206 ** Read a command and do it. A command consists of an optional integer
1207 ** argument followed by the command character. Return the number of lines
1208 ** to display in the next screenful. If there is nothing more to display
1209 ** in the current file, zero is returned.
1212 int command (char *filename
, register FILE *f
)
1214 register int nlines
;
1215 register int retval
= 0;
1219 char comchar
, cmdbuf
[80];
1221 #define ret(val) retval=val;done++;break
1229 nlines
= number (&comchar
);
1230 lastp
= colonch
= 0;
1231 if (comchar
== '.') { /* Repeat last command */
1236 colonch
= lastcolon
;
1240 if ((cc_t
) comchar
== otty
.c_cc
[VERASE
]) {
1247 retval
= colon (filename
, colonch
, nlines
);
1254 register int initline
;
1261 if (nlines
== 0) nlines
++;
1269 xprintf (_("...back %d pages"), nlines
);
1271 xprintf (_("...back 1 page"));
1276 initline
= Currline
- dlines
* (nlines
+ 1);
1279 if (initline
< 0) initline
= 0;
1281 Currline
= 0; /* skiplns() will make Currline correct */
1282 skiplns(initline
, f
);
1292 if (nlines
== 0) nlines
= dlines
;
1293 else if (comchar
== 'z') dlines
= nlines
;
1297 if (nlines
!= 0) nscroll
= nlines
;
1304 if (nlines
== 0) nlines
++;
1313 xprintf (_("...skipping one line"));
1315 xprintf (_("...skipping %d lines"), nlines
);
1321 while (nlines
> 0) {
1322 while ((c
= Getc (f
)) != '\n')
1341 Fseek (f
, screen_start
.chrctr
);
1342 Currline
= screen_start
.line
;
1352 pr (_("\n***Back***\n\n"));
1353 Fseek (f
, context
.chrctr
);
1354 Currline
= context
.line
;
1363 promptlen
= printd (Currline
);
1369 if (nlines
== 0) nlines
++;
1376 search (NULL
, f
, nlines
); /* Use previous r.e. */
1379 ttyin (cmdbuf
, sizeof(cmdbuf
)-2, '/');
1381 search (cmdbuf
, f
, nlines
);
1385 do_shell (filename
);
1389 if (noscroll
) doclear();
1390 xprintf(_("\nMost commands optionally preceded by integer argument"
1391 " k. Defaults in brackets.\n"
1392 "Star (*) indicates argument becomes new default.\n"));
1393 xprintf("---------------------------------------"
1394 "----------------------------------------\n");
1395 xprintf(_("<space>\t\t\t"
1396 "Display next k lines of text [current screen size]\n"
1397 "z\t\t\tDisplay next k lines of text [current screen size]*\n"
1398 "<return>\t\tDisplay next k lines of text [1]*\n"
1400 "Scroll k lines [current scroll size, initially 11]*\n"
1401 "q or Q or <interrupt>\tExit from more\n"
1402 "s\t\t\tSkip forward k lines of text [1]\n"
1403 "f\t\t\tSkip forward k screenfuls of text [1]\n"
1404 "b or ctrl-B\t\tSkip backwards k screenfuls of text [1]\n"
1405 "'\t\t\tGo to place where previous search started\n"
1406 "=\t\t\tDisplay current line number\n"
1407 "/<regular expression>\t"
1408 "Search for kth occurrence of regular expression [1]\n"
1409 "n\t\t\tSearch for kth occurrence of last r.e [1]\n"
1410 "!<cmd> or :!<cmd>\tExecute <cmd> in a subshell\n"
1411 "v\t\t\tStart up /usr/bin/vi at current line\n"
1412 "ctrl-L\t\t\tRedraw screen\n"
1413 ":n\t\t\tGo to kth next file [1]\n"
1414 ":p\t\t\tGo to kth previous file [1]\n"
1415 ":f\t\t\tDisplay current file name and line number\n"
1416 ".\t\t\tRepeat previous command\n"));
1417 xprintf("---------------------------------------"
1418 "----------------------------------------\n");
1421 case 'v': /* This case should go right before default */
1425 editor
= getenv("VISUAL");
1426 if (editor
== NULL
|| *editor
== '\0')
1427 editor
= getenv("EDITOR");
1428 if (editor
== NULL
|| *editor
== '\0')
1433 scanstr (Currline
- dlines
<= 0 ? 1
1434 : Currline
- (dlines
+ 1) / 2, &cmdbuf
[1]);
1435 pr (editor
); putchar (' ');
1436 pr (cmdbuf
); putchar (' '); pr (fnames
[fnum
]);
1437 execute (filename
, editor
, editor
, cmdbuf
, fnames
[fnum
], 0);
1443 if (Senter
&& Sexit
) {
1444 my_putstring (Senter
);
1445 promptlen
= pr (_("[Press 'h' for instructions.]"))
1447 my_putstring (Sexit
);
1450 promptlen
= pr (_("[Press 'h' for instructions.]"));
1469 * Execute a colon-prefixed command.
1470 * Returns <0 if not a command that should cause
1471 * more of the file to be printed.
1474 int colon (char *filename
, int cmd
, int nlines
) {
1484 promptlen
= xprintf (_("\"%s\" line %d"), fnames
[fnum
], Currline
);
1486 promptlen
= xprintf (_("[Not a file] line %d"), Currline
);
1491 if (fnum
>= nfiles
- 1)
1511 do_shell (filename
);
1523 ** Read a decimal number from the terminal. Set cmd to the non-digit which
1524 ** terminates the number.
1527 int number(char *cmd
)
1531 i
= 0; ch
= otty
.c_cc
[VKILL
];
1535 i
= i
*10 + ch
- '0';
1536 else if ((cc_t
) ch
== otty
.c_cc
[VKILL
])
1546 void do_shell (char *filename
)
1559 ttyin (cmdbuf
, sizeof(cmdbuf
)-2, '!');
1561 rc
= expand (&expanded
, cmdbuf
);
1563 if (strlen(expanded
) < sizeof(shell_line
))
1564 strcpy(shell_line
, expanded
);
1570 errwrite(_(" Overflow\n"));
1573 } else if (rc
> 0) {
1575 promptlen
= xprintf ("!%s", shell_line
);
1582 execute (filename
, shell
, shell
, "-c", shell_line
, 0);
1586 ** Search for nth ocurrence of regular expression contained in buf in the file
1589 void search(char buf
[], FILE *file
, register int n
)
1591 long startline
= Ftell (file
);
1592 register long line1
= startline
;
1593 register long line2
= startline
;
1594 register long line3
= startline
;
1595 register int lncount
;
1599 context
.line
= saveln
= Currline
;
1600 context
.chrctr
= startline
;
1602 if ((s
= re_comp (buf
)) != 0)
1604 while (!feof (file
)) {
1607 line1
= Ftell (file
);
1610 if ((rv
= re_exec (Line
)) == 1) {
1612 if (lncount
> 3 || (lncount
> 1 && no_intty
))
1617 pr(_("...skipping\n"));
1620 Currline
-= (lncount
>= 3 ? 3 : lncount
);
1621 Fseek (file
, line3
);
1646 } else if (rv
== -1)
1647 error (_("Regular expression botch"));
1652 /* No longer in libc 4.5.8. . . */
1653 file
->_flags
&= ~STDIO_S_EOF_SEEN
; /* why doesn't fseek do this ??!!??! */
1656 Fseek (file
, startline
);
1659 pr (_("\nPattern not found\n"));
1662 error (_("Pattern not found"));
1667 void execute (char * filename
, char * cmd
, ...)
1678 for (n
= 10; (id
= fork ()) < 0 && n
> 0; n
--)
1683 open("/dev/tty", 0);
1686 va_start(argp
, cmd
);
1687 arg
= va_arg(argp
, char *);
1691 arg
= va_arg(argp
, char *);
1695 args
= alloca(sizeof(char *) * (argcount
+ 1));
1696 args
[argcount
] = NULL
;
1698 va_start(argp
, cmd
);
1699 arg
= va_arg(argp
, char *);
1702 args
[argcount
] = arg
;
1704 arg
= va_arg(argp
, char *);
1709 errwrite(_("exec failed\n"));
1713 signal (SIGINT
, SIG_IGN
);
1714 signal (SIGQUIT
, SIG_IGN
);
1716 signal(SIGTSTP
, SIG_DFL
);
1717 while (wait(0) > 0);
1718 signal (SIGINT
, end_it
);
1719 signal (SIGQUIT
, onquit
);
1721 signal(SIGTSTP
, onsusp
);
1723 errwrite(_("can't fork\n"));
1725 pr ("------------------------\n");
1729 ** Skip n lines in the file f
1732 void skiplns (register int n
, register FILE *f
)
1737 while ((c
= Getc (f
)) != '\n')
1746 ** Skip nskip files in the file list (from the command line). Nskip may be
1750 void skipf (register int nskip
)
1752 if (nskip
== 0) return;
1754 if (fnum
+ nskip
> nfiles
- 1)
1755 nskip
= nfiles
- fnum
- 1;
1762 pr (_("\n...Skipping "));
1767 pr (_("...Skipping to file "));
1769 pr (_("...Skipping back to file "));
1778 /*----------------------------- Terminal I/O -------------------------------*/
1790 no_tty
= tcgetattr(fileno(stdout
), &otty
);
1792 docrterase
= (otty
.c_cc
[VERASE
] != 255);
1793 docrtkill
= (otty
.c_cc
[VKILL
] != 255);
1798 * Wait until we're in the foreground before we save the
1799 * the terminal modes.
1801 if ((tgrp
= tcgetpgrp(fileno(stdout
))) < 0) {
1802 perror("tcgetpgrp");
1805 if (tgrp
!= getpgrp(0)) {
1811 if ((term
= getenv("TERM")) == 0) {
1814 my_setupterm(term
, 1, &ret
);
1820 if (ioctl(fileno(stdout
), TIOCGWINSZ
, &win
) < 0) {
1822 Lpp
= my_tgetnum("li","lines");
1823 Mcol
= my_tgetnum("co","cols");
1826 if ((Lpp
= win
.ws_row
) == 0)
1827 Lpp
= my_tgetnum("li","lines");
1828 if ((Mcol
= win
.ws_col
) == 0)
1829 Mcol
= my_tgetnum("co","cols");
1832 if ((Lpp
<= 0) || my_tgetflag("hc","hc")) {
1833 hard
++; /* Hard copy terminal */
1837 if (my_tgetflag("xn","xenl"))
1838 eatnl
++; /* Eat newline at last column + 1; dec, concept */
1842 if (tailequ (fnames
[0], "page"))
1844 Wrap
= my_tgetflag("am","am");
1845 bad_so
= my_tgetflag ("xs","xhp");
1846 eraseln
= my_tgetstr("ce","el");
1847 Clear
= my_tgetstr("cl","clear");
1848 Senter
= my_tgetstr("so","smso");
1849 Sexit
= my_tgetstr("se","rmso");
1850 if ((soglitch
= my_tgetnum("sg","xmc")) < 0)
1854 * Set up for underlining: some terminals don't need it;
1855 * others have start/stop sequences, still others have an
1856 * underline char sequence which is assumed to move the
1857 * cursor forward one character. If underline sequence
1858 * isn't available, settle for standout sequence.
1861 if (my_tgetflag("ul","ul") || my_tgetflag("os","os"))
1863 if ((chUL
= my_tgetstr("uc","uc")) == NULL
)
1865 if (((ULenter
= my_tgetstr("us","smul")) == NULL
||
1866 (ULexit
= my_tgetstr("ue","rmul")) == NULL
) && !*chUL
) {
1867 if ((ULenter
= Senter
) == NULL
|| (ULexit
= Sexit
) == NULL
) {
1871 ulglitch
= soglitch
;
1876 if ((padstr
= my_tgetstr("pc","pad")) != NULL
)
1878 Home
= my_tgetstr("ho","home");
1879 if (Home
== 0 || *Home
== '\0') {
1880 if ((cursorm
= my_tgetstr("cm","cup")) != NULL
) {
1881 const char *t
= (const char *)my_tgoto(cursorm
, 0, 0);
1882 xstrncpy(cursorhome
, t
, sizeof(cursorhome
));
1886 EodClr
= my_tgetstr("cd","ed");
1887 if ((chBS
= my_tgetstr("le","cub1")) == NULL
)
1891 if ((shell
= getenv("SHELL")) == NULL
)
1894 no_intty
= tcgetattr(fileno(stdin
), &otty
);
1895 tcgetattr(fileno(stderr
), &otty
);
1897 slow_tty
= (otty
.c_cflag
& CBAUD
) < B1200
;
1898 hardtabs
= (otty
.c_oflag
& TABDLY
) != XTABS
;
1900 otty
.c_lflag
&= ~(ICANON
|ECHO
);
1901 otty
.c_cc
[VMIN
] = 1;
1902 otty
.c_cc
[VTIME
] = 0;
1910 if (read (fileno(stderr
), &c
, 1) <= 0) {
1914 c
= otty
.c_cc
[VKILL
];
1919 static char *BS
= "\b";
1920 static char *BSB
= "\b \b";
1921 static char *CARAT
= "^";
1922 #define ERASEONECOLUMN \
1928 void ttyin (unsigned char buf
[], register int nmax
, char pchar
) {
1936 while (sp
- buf
< nmax
) {
1937 if (promptlen
> maxlen
) maxlen
= promptlen
;
1942 else if (((cc_t
) c
== otty
.c_cc
[VERASE
]) && !slash
) {
1944 #ifdef ENABLE_WIDECHAR
1948 size_t pos
= 0, mblength
;
1949 mbstate_t state
, state_bak
;
1951 memset (&state
, '\0', sizeof (mbstate_t));
1955 mblength
= mbrtowc (&wc
, buf
+ pos
, sp
- buf
, &state
);
1957 state
= (mblength
== (size_t)-2
1958 || mblength
== (size_t)-1) ? state_bak
: state
;
1959 mblength
= (mblength
== (size_t)-2
1960 || mblength
== (size_t)-1
1961 || mblength
== 0) ? 1 : mblength
;
1963 if (buf
+ pos
+ mblength
>= sp
)
1969 if (mblength
== 1) {
1974 wc_width
= wcwidth (wc
);
1975 wc_width
= (wc_width
< 1) ? 1 : wc_width
;
1976 while (wc_width
--) {
1981 while (mblength
--) {
1994 if ((*sp
< ' ' && *sp
!= '\n') || *sp
== RUBOUT
) {
2001 if (!eraseln
) promptlen
= maxlen
;
2002 siglongjmp (restore
, 1);
2005 else if (((cc_t
) c
== otty
.c_cc
[VKILL
]) && !slash
) {
2017 while (promptlen
-- > 1)
2025 if (slash
&& ((cc_t
) c
== otty
.c_cc
[VKILL
]
2026 || (cc_t
) c
== otty
.c_cc
[VERASE
])) {
2033 if ((c
< ' ' && c
!= '\n' && c
!= ESC
) || c
== RUBOUT
) {
2034 c
+= (c
== RUBOUT
) ? -0100 : 0100;
2038 if (c
!= '\n' && c
!= ESC
) {
2047 if (!eraseln
) promptlen
= maxlen
;
2048 if (sp
- buf
>= nmax
- 1)
2049 error (_("Line too long"));
2052 /* return: 0 - unchanged, 1 - changed, -1 - overflow (unchanged) */
2053 int expand (char **outbuf
, char *inbuf
) {
2059 int tempsz
, xtra
, offset
;
2061 xtra
= strlen (fnames
[fnum
]) + strlen (shell_line
) + 1;
2062 tempsz
= 200 + xtra
;
2063 temp
= malloc(tempsz
);
2065 error (_("Out of memory"));
2070 while ((c
= *inpstr
++) != '\0'){
2071 offset
= outstr
-temp
;
2072 if (tempsz
-offset
-1 < xtra
) {
2073 tempsz
+= 200 + xtra
;
2074 temp
= realloc(temp
, tempsz
);
2076 error (_("Out of memory"));
2079 outstr
= temp
+ offset
;
2084 strcpy (outstr
, fnames
[fnum
]);
2085 outstr
+= strlen (fnames
[fnum
]);
2092 error (_("No previous command to substitute for"));
2093 strcpy (outstr
, shell_line
);
2094 outstr
+= strlen (shell_line
);
2098 if (*inpstr
== '%' || *inpstr
== '!') {
2099 *outstr
++ = *inpstr
++;
2111 void show (char c
) {
2114 if ((c
< ' ' && c
!= '\n' && c
!= ESC
) || c
== RUBOUT
) {
2115 c
+= (c
== RUBOUT
) ? -0100 : 0100;
2124 void errwrite (char *txt
)
2126 write (fileno(stderr
), txt
, strlen(txt
));
2129 void errwrite1 (char *sym
)
2131 write (fileno(stderr
), sym
, 1);
2134 void error (char *mess
)
2140 promptlen
+= strlen (mess
);
2141 if (Senter
&& Sexit
) {
2142 my_putstring (Senter
);
2144 my_putstring (Sexit
);
2150 siglongjmp (restore
, 1);
2155 otty
.c_lflag
&= ~(ICANON
|ECHO
);
2156 otty
.c_cc
[VMIN
] = 1; /* read at least 1 char */
2157 otty
.c_cc
[VTIME
] = 0; /* no timeout */
2158 stty(fileno(stderr
), &otty
);
2163 return putc(c
, stdout
);
2171 tputs(ULexit
, 1, ourputch
);
2175 otty
.c_lflag
|= ICANON
|ECHO
;
2176 otty
.c_cc
[VMIN
] = savetty0
.c_cc
[VMIN
];
2177 otty
.c_cc
[VTIME
] = savetty0
.c_cc
[VTIME
];
2178 stty(fileno(stderr
), &savetty0
);
2181 void rdline (register FILE *f
)
2187 while ((c
= Getc (f
)) != '\n' && c
!= EOF
&& p
- Line
< LINSIZ
- 1)
2194 /* Come here when we get a suspend signal from the terminal */
2196 void onsusp (int dummy
) {
2197 sigset_t signals
, oldmask
;
2199 /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
2200 signal(SIGTTOU
, SIG_IGN
);
2203 signal(SIGTTOU
, SIG_DFL
);
2204 /* Send the TSTP signal to suspend our process group */
2205 signal(SIGTSTP
, SIG_DFL
);
2207 /* unblock SIGTSTP or we won't be able to suspend ourself */
2208 sigemptyset(&signals
);
2209 sigaddset(&signals
, SIGTSTP
);
2210 sigprocmask(SIG_UNBLOCK
, &signals
, &oldmask
);
2213 /* Pause for station break */
2215 sigprocmask(SIG_SETMASK
, &oldmask
, NULL
);
2218 signal (SIGTSTP
, onsusp
);
2221 siglongjmp (restore
, 1);