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>
53 #include <sys/ioctl.h>
61 #define _REGEX_RE_COMP
65 #define VI "vi" /* found on the user's path */
67 #define Fopen(s,m) (Currline = 0,file_pos=0,fopen(s,m))
68 #define Ftell(f) file_pos
69 #define Fseek(f,off) (file_pos=off,fseek(f,off,0))
70 #define Getc(f) (++file_pos, getc(f))
71 #define Ungetc(c,f) (--file_pos, ungetc(c,f))
72 #define putcerr(c) fputc(c, stderr)
73 #define putserr(s) fputs(s, stderr)
74 #define putsout(s) fputs(s, stdout)
76 #define stty(fd,argp) tcsetattr(fd,TCSANOW,argp)
78 /* some function declarations */
85 void error (char *mess);
86 void do_shell (char *filename);
87 int colon (char *filename, int cmd, int nlines);
88 int expand (char **outbuf, char *inbuf);
89 void argscan(char *s,char *argv0);
90 void rdline (register FILE *f);
91 void copy_file(register FILE *f);
92 void search(char buf[], FILE *file, register int n);
93 void skipf (register int nskip);
94 void skiplns(register int n, register FILE *f);
95 void screen (register FILE *f, register int num_lines);
96 int command (char *filename, register FILE *f);
97 void erasep (register int col);
98 void show (register char ch);
100 void reset_tty(void);
101 void ttyin (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 void execute (char *filename, char *cmd, ...);
107 FILE *checkf (char *, int *);
111 #define ctrl(letter) (letter & 077)
112 #define RUBOUT '\177'
116 struct termios otty, savetty0;
117 long file_pos, file_size;
118 int fnum, no_intty, no_tty, slow_tty;
120 void onquit(int), onsusp(int), chgwinsz(int), end_it(int);
121 int nscroll = 11; /* Number of lines scrolled by 'd' */
122 int fold_opt = 1; /* Fold long lines */
123 int stop_opt = 1; /* Stop after form feeds */
124 int ssp_opt = 0; /* Suppress white space */
125 int ul_opt = 1; /* Underline as best we can */
127 int Currline; /* Line we are currently at */
133 int bad_so; /* True if overwriting does not turn off standout */
134 int inwait, Pause, errors;
135 int within; /* true if we are within a file,
136 false if we are between files */
137 int hard, dumb, noscroll, hardtabs, clreol, eatnl;
138 int catch_susp; /* We should catch the SIGTSTP signal */
139 char **fnames; /* The list of file names */
140 int nfiles; /* Number of files left to process */
141 char *shell; /* The name of the shell to use */
142 int shellp; /* A previous shell command exists */
144 char Line[LINSIZ+2]; /* Line buffer */
145 int Lpp = 24; /* lines per page */
146 char *Clear; /* clear screen */
147 char *eraseln; /* erase line */
148 char *Senter, *Sexit;/* enter and exit standout mode */
149 char *ULenter, *ULexit; /* enter and exit underline mode */
150 char *chUL; /* underline character */
151 char *chBS; /* backspace character */
152 char *Home; /* go to home */
153 char *cursorm; /* cursor movement */
154 char cursorhome[40]; /* contains cursor movement to home */
155 char *EodClr; /* clear rest of screen */
156 int Mcol = 80; /* number of columns */
157 int Wrap = 1; /* set if automargins */
158 int soglitch; /* terminal has standout mode glitch */
159 int ulglitch; /* terminal has underline mode glitch */
160 int pstate = 0; /* current UL state */
161 static int magic(FILE *, char *);
164 } context, screen_start;
165 extern char PC; /* pad character */
167 #ifdef HAVE_NCURSES_H
168 # include <ncurses.h>
169 #elif defined(HAVE_NCURSES_NCURSES_H)
170 # include <ncurses/ncurses.h>
173 #if defined(HAVE_NCURSES_H) || defined(HAVE_NCURSES_NCURSES_H)
174 # include <term.h> /* include after <curses.h> */
177 my_putstring(char *s) {
178 tputs (s, 1, putchar); /* putp(s); */
182 my_setupterm(char *term, int fildes, int *errret) {
183 setupterm(term, fildes, errret);
187 my_tgetnum(char *s, char *ss) {
192 my_tgetflag(char *s, char *ss) {
193 return tigetflag(ss);
197 my_tgetstr(char *s, char *ss) {
202 my_tgoto(char *cap, int col, int row) {
203 return tparm(cap, col, row);
206 #elif defined(HAVE_LIBTERMCAP) /* !ncurses */
210 char termbuffer[4096];
212 char *strbuf = termbuffer;
215 my_putstring(char *s) {
216 tputs (s, 1, putchar);
220 my_setupterm(char *term, int fildes, int *errret) {
221 *errret = tgetent(tcbuffer, term);
225 my_tgetnum(char *s, char *ss) {
230 my_tgetflag(char *s, char *ss) {
235 my_tgetstr(char *s, char *ss) {
236 return tgetstr(s, &strbuf);
240 my_tgoto(char *cap, int col, int row) {
241 return tgoto(cap, col, row);
244 #endif /* HAVE_LIBTERMCAP */
254 char *p = strrchr(s, '/');
256 _("usage: %s [-dflpcsu] [+linenum | +/pattern] name1 name2 ...\n"),
260 int main(int argc, char **argv) {
273 setlocale(LC_ALL, "");
274 bindtextdomain(PACKAGE, LOCALEDIR);
277 /* avoid gcc complaints about register variables that
278 may be clobbered by a longjmp, by forcing our variables here
279 to be non-register */
280 Fdummy(&f); idummy(&left); idummy(&prnames);
281 idummy(&initopt); idummy(&srchopt); idummy(&initline);
285 setlocale(LC_ALL, "");
290 if((s = getenv("MORE")) != NULL) argscan(s,argv[0]);
291 while (--nfiles > 0) {
292 if ((ch = (*++fnames)[0]) == '-') {
293 argscan(*fnames+1,argv[0]);
295 else if (ch == '+') {
299 for (++s, p = initbuf; p < initbuf + 79 && *s != '\0';)
305 for (initline = 0; *s != '\0'; s++)
307 initline = initline*10 + *s -'0';
313 /* allow clreol only if Home and eraseln and EodClr strings are
314 * defined, and in that case, make sure we are in noscroll mode
317 if((Home == NULL) || (*Home == '\0') ||
318 (eraseln == NULL) || (*eraseln == '\0') ||
319 (EodClr == NULL) || (*EodClr == '\0') )
324 dlines = Lpp - 1; /* was: Lpp - (noscroll ? 1 : 2) */
328 if (!no_intty && nfiles == 0) {
335 signal(SIGQUIT, onquit);
336 signal(SIGINT, end_it);
338 signal(SIGWINCH, chgwinsz);
340 if (signal (SIGTSTP, SIG_IGN) == SIG_DFL) {
341 signal(SIGTSTP, onsusp);
344 stty (fileno(stderr), &otty);
350 if ((ch = Getc (f)) == '\f')
354 if (noscroll && (ch != EOF)) {
363 search (initbuf, stdin, 1);
368 skiplns (initline, stdin);
369 screen (stdin, left);
376 while (fnum < nfiles) {
377 if ((f = checkf (fnames[fnum], &clearit)) != NULL) {
378 context.line = context.chrctr = 0;
380 if (firstf) sigsetjmp (restore, 1);
384 search (initbuf, f, 1);
389 skiplns (initline, f);
391 else if (fnum < nfiles && !no_tty) {
392 sigsetjmp (restore, 1);
393 left = command (fnames[fnum], f);
396 if ((noscroll || clearit) && (file_size != LONG_MAX)) {
407 putsout("::::::::::::::");
411 if(clreol) cleareol();
413 if(clreol) cleareol();
414 puts("::::::::::::::");
426 sigsetjmp (restore, 1);
429 screen_start.line = screen_start.chrctr = 0L;
430 context.line = context.chrctr = 0L;
439 void argscan(char *s, char *argv0) {
444 case '0': case '1': case '2':
445 case '3': case '4': case '5':
446 case '6': case '7': case '8':
452 dlines = dlines*10 + *s - '0';
475 case '-': case ' ': case '\t':
479 _("%s: unknown option \"-%c\"\n"), argv0, *s);
490 ** Check whether the file named by fs is an ASCII file which the user may
491 ** access. If it is, return the opened file. Otherwise return NULL.
495 checkf (fs, clearfirst)
503 if (stat (fs, &stbuf) == -1) {
504 (void)fflush(stdout);
508 return((FILE *)NULL);
510 if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
511 printf(_("\n*** %s: directory ***\n\n"), fs);
512 return((FILE *)NULL);
514 if ((f = Fopen(fs, "r")) == NULL) {
515 (void)fflush(stdout);
517 return((FILE *)NULL);
520 return((FILE *)NULL);
521 fcntl(fileno(f), F_SETFD, FD_CLOEXEC );
523 *clearfirst = (c == '\f');
525 if ((file_size = stbuf.st_size) == 0)
526 file_size = LONG_MAX;
532 * check for file magic numbers. This code would best be shared with
533 * the file(1) program or, perhaps, more should not try to be so smart.
540 signed char twobytes[2];
542 /* don't try to look ahead if the input is unseekable */
543 if (fseek(f, 0L, SEEK_SET))
546 if (fread(twobytes, 2, 1, f) == 1) {
547 switch(twobytes[0] + (twobytes[1]<<8)) {
548 case 0407: /* a.out obj */
549 case 0410: /* a.out exec */
550 case 0413: /* a.out demand exec */
554 case 0x457f: /* simple ELF detection */
555 printf(_("\n******** %s: Not a text file ********\n\n"), fs);
560 (void)fseek(f, 0L, SEEK_SET); /* rewind() not necessary */
565 ** Print out the contents of the file f, one screenful at a time.
570 void screen (register FILE *f, register int num_lines)
574 int length; /* length of current line */
575 static int prev_len = 1; /* length of previous line */
578 while (num_lines > 0 && !Pause) {
579 if ((nchars = get_line (f, &length)) == EOF)
585 if (ssp_opt && length == 0 && prev_len == 0)
588 if (bad_so || ((Senter && *Senter == ' ') && (promptlen > 0)))
590 /* must clear before drawing line since tabs on some terminals
591 * do not erase what they tab over.
595 prbuf (Line, length);
596 if (nchars < promptlen)
597 erasep (nchars); /* erasep () sets promptlen to 0 */
601 * cleareol(); * must clear again in case we wrapped *
603 if (nchars < Mcol || !fold_opt)
604 prbuf("\n", 1); /* will turn off UL if necessary */
610 my_putstring (ULexit);
614 if ((c = Getc(f)) == EOF)
624 sigsetjmp (restore, 1);
625 Pause = 0; startup = 0;
626 if ((num_lines = command (NULL, f)) == 0)
628 if (hard && promptlen > 0)
630 if (noscroll && num_lines >= dlines)
637 screen_start.line = Currline;
638 screen_start.chrctr = Ftell (f);
643 ** Come here if a quit signal is received
646 void onquit(int dummy) {
647 signal(SIGQUIT, SIG_IGN);
651 signal(SIGQUIT, onquit);
652 siglongjmp (restore, 1);
657 else if (!dum_opt && notell) {
658 promptlen += fprintf(stderr, _("[Use q or Q to quit]"));
661 signal(SIGQUIT, onquit);
665 ** Come here if a signal for a window size change is received
669 void chgwinsz(int dummy) {
672 (void) signal(SIGWINCH, SIG_IGN);
673 if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) {
674 if (win.ws_row != 0) {
679 dlines = Lpp - 1; /* was: Lpp - (noscroll ? 1 : 2) */
684 (void) signal(SIGWINCH, chgwinsz);
689 ** Clean up terminal state and exit. Also come here if interrupt signal received
692 void end_it (int dummy) {
699 else if (!clreol && (promptlen > 0)) {
708 void copy_file(register FILE *f) {
711 while ((c = getc(f)) != EOF)
715 #define ringbell() putcerr('\007')
717 /* See whether the last component of the path name "path" is equal to the
721 static int tailequ (char *path, register char *string)
725 tail = path + strlen(path);
726 while (--tail >= path)
730 while (*tail++ == *string++)
736 static void prompt (char *filename)
740 else if (promptlen > 0)
744 if (Senter && Sexit) {
745 my_putstring (Senter);
746 promptlen += (2 * soglitch);
750 promptlen += printf(_("--More--"));
751 if (filename != NULL) {
752 promptlen += printf(_("(Next file: %s)"), filename);
753 } else if (!no_intty) {
754 promptlen += printf("(%d%%)", (int) ((file_pos * 100) / file_size));
757 promptlen += printf(_("[Press space to continue, 'q' to quit.]"));
760 my_putstring (Sexit);
774 int get_line(register FILE *f, int *length)
785 mbstate_t state, state_bak; /* Current status of the stream. */
786 char mbc[MB_LEN_MAX]; /* Buffer for one multibyte char. */
787 size_t mblength; /* Byte length of multibyte char. */
788 size_t mbc_pos = 0; /* Postion of the MBC. */
789 int use_mbc_buffer_flag = 0; /* If 1, mbc has data. */
790 int break_flag = 0; /* If 1, exit while(). */
791 long file_pos_bak = Ftell (f);
793 memset (&state, '\0', sizeof (mbstate_t));
799 if (colflg && c == '\n') {
803 while (p < &Line[LINSIZ - 1]) {
805 if (fold_opt && use_mbc_buffer_flag && MB_CUR_MAX > 1) {
806 use_mbc_buffer_flag = 0;
810 mblength = mbrtowc (&wc, mbc, mbc_pos, &state);
813 case (size_t)-2: /* Incomplete multibyte character. */
814 use_mbc_buffer_flag = 1;
818 case (size_t)-1: /* Invalid as a multibyte character. */
824 if (column >= Mcol) {
825 Fseek (f, file_pos_bak);
827 memmove (mbc, mbc + 1, --mbc_pos);
836 wc_width = wcwidth (wc);
838 if (column + wc_width > Mcol) {
839 Fseek (f, file_pos_bak);
842 for (i = 0; i < mbc_pos; i++)
849 if (break_flag || column >= Mcol)
872 if (c == '\033') { /* ESC */
874 while (c > ' ' && c < '0' && p < &Line[LINSIZ - 1]) {
878 if (c >= '0' && c < '\177' && p < &Line[LINSIZ - 1]) {
886 if (!hardtabs || (column < promptlen && !hard)) {
887 if (hardtabs && eraseln && !dumb) {
888 column = 1 + (column | 7);
889 my_putstring (eraseln);
893 for (--p; p < &Line[LINSIZ - 1];) {
895 if ((++column & 7) == 0)
898 if (column >= promptlen) promptlen = 0;
901 column = 1 + (column | 7);
902 } else if (c == '\b' && column > 0) {
904 } else if (c == '\r') {
913 } else if (c == '\f' && stop_opt) {
918 } else if (c == EOF) {
923 if (fold_opt && MB_CUR_MAX > 1) {
924 memset (mbc, '\0', MB_LEN_MAX);
929 mblength = mbrtowc (&wc, mbc, mbc_pos, &state);
931 /* The value of mblength is always less than 2 here. */
935 file_pos_bak = Ftell (f) - 1;
937 use_mbc_buffer_flag = 1;
946 wc_width = wcwidth (wc);
958 if (column >= Mcol && fold_opt)
962 if (column >= Mcol && Mcol > 0) {
967 colflg = column == Mcol && fold_opt;
968 if (colflg && eatnl && Wrap) {
969 *p++ = '\n'; /* simulate normal wrap */
977 ** Erase the rest of the prompt, assuming we are starting at column col.
980 void erasep (register int col)
991 if (!dumb && eraseln)
992 my_putstring (eraseln);
994 for (col = promptlen - col; col > 0; col--)
1001 ** Erase the current line entirely
1007 if (!eraseln || dumb)
1012 * force clear to end of line
1016 my_putstring(eraseln);
1021 my_putstring(EodClr);
1024 /* Print a buffer of n characters */
1026 void prbuf (register char *s, register int n)
1028 register char c; /* next output character */
1029 register int state; /* next output char's UL state */
1030 #define wouldul(s,n) ((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_')))
1036 if (*s == ' ' && pstate == 0 && ulglitch && wouldul(s+1, n-1)) {
1040 if ((state = wouldul(s, n)) != 0) {
1041 c = (*s == '_')? s[2] : *s ;
1046 if (state != pstate) {
1047 if (c == ' ' && state == 0 && ulglitch && wouldul(s, n-1))
1050 my_putstring(state ? ULenter : ULexit);
1052 if (c != ' ' || pstate == 0 || state != 0 || ulglitch == 0)
1053 #ifdef HAVE_WIDECHAR
1058 memset (&state, '\0', sizeof (mbstate_t));
1060 mblength = mbrtowc (&wc, s, n, &state);
1061 if (mblength == (size_t) -2 || mblength == (size_t) -1)
1069 #endif /* HAVE_WIDECHAR */
1070 if (state && *chUL) {
1084 if (Clear && !hard) {
1085 my_putstring(Clear);
1087 /* Put out carriage return so that system doesn't
1088 ** get confused by escape sequences when expanding tabs
1096 * Go to home position
1104 static int lastcmd, lastarg, lastp;
1105 static int lastcolon;
1106 char shell_line[1000];
1109 ** Read a command and do it. A command consists of an optional integer
1110 ** argument followed by the command character. Return the number of lines
1111 ** to display in the next screenful. If there is nothing more to display
1112 ** in the current file, zero is returned.
1115 int command (char *filename, register FILE *f)
1117 register int nlines;
1118 register int retval = 0;
1122 char comchar, cmdbuf[80];
1124 #define ret(val) retval=val;done++;break
1132 nlines = number (&comchar);
1133 lastp = colonch = 0;
1134 if (comchar == '.') { /* Repeat last command */
1139 colonch = lastcolon;
1143 if ((cc_t) comchar == otty.c_cc[VERASE]) {
1150 retval = colon (filename, colonch, nlines);
1157 register int initline;
1164 if (nlines == 0) nlines++;
1172 printf(_("...back %d pages"), nlines);
1174 putsout(_("...back 1 page"));
1179 initline = Currline - dlines * (nlines + 1);
1182 if (initline < 0) initline = 0;
1184 Currline = 0; /* skiplns() will make Currline correct */
1185 skiplns(initline, f);
1195 if (nlines == 0) nlines = dlines;
1196 else if (comchar == 'z') dlines = nlines;
1200 if (nlines != 0) nscroll = nlines;
1208 if (nlines == 0) nlines++;
1217 putsout(_("...skipping one line"));
1219 printf(_("...skipping %d lines"), nlines);
1225 while (nlines > 0) {
1226 while ((c = Getc (f)) != '\n')
1245 Fseek (f, screen_start.chrctr);
1246 Currline = screen_start.line;
1256 putsout(_("\n***Back***\n\n"));
1257 Fseek (f, context.chrctr);
1258 Currline = context.line;
1267 promptlen = printf("%d", Currline);
1273 if (nlines == 0) nlines++;
1280 search (NULL, f, nlines); /* Use previous r.e. */
1283 ttyin (cmdbuf, sizeof(cmdbuf)-2, '/');
1285 search (cmdbuf, f, nlines);
1289 do_shell (filename);
1293 if (noscroll) doclear();
1295 "Most commands optionally preceded by integer argument k. "
1296 "Defaults in brackets.\n"
1297 "Star (*) indicates argument becomes new default.\n"));
1298 puts("---------------------------------------"
1299 "----------------------------------------");
1301 "<space> Display next k lines of text [current screen size]\n"
1302 "z Display next k lines of text [current screen size]*\n"
1303 "<return> Display next k lines of text [1]*\n"
1304 "d or ctrl-D Scroll k lines [current scroll size, initially 11]*\n"
1305 "q or Q or <interrupt> Exit from more\n"
1306 "s Skip forward k lines of text [1]\n"
1307 "f Skip forward k screenfuls of text [1]\n"
1308 "b or ctrl-B Skip backwards k screenfuls of text [1]\n"
1309 "' Go to place where previous search started\n"
1310 "= Display current line number\n"
1311 "/<regular expression> Search for kth occurrence of regular expression [1]\n"
1312 "n Search for kth occurrence of last r.e [1]\n"
1313 "!<cmd> or :!<cmd> Execute <cmd> in a subshell\n"
1314 "v Start up /usr/bin/vi at current line\n"
1315 "ctrl-L Redraw screen\n"
1316 ":n Go to kth next file [1]\n"
1317 ":p Go to kth previous file [1]\n"
1318 ":f Display current file name and line number\n"
1319 ". Repeat previous command\n"));
1320 puts("---------------------------------------"
1321 "----------------------------------------");
1324 case 'v': /* This case should go right before default */
1327 * Earlier: call vi +n file. This also works for emacs.
1328 * POSIX: call vi -c n file (when editor is vi or ex).
1331 int n = (Currline - dlines <= 0 ? 1 :
1332 Currline - (dlines + 1) / 2);
1335 editor = getenv("VISUAL");
1336 if (editor == NULL || *editor == '\0')
1337 editor = getenv("EDITOR");
1338 if (editor == NULL || *editor == '\0')
1341 p = strrchr(editor, '/');
1346 if (!strcmp(p, "vi") || !strcmp(p, "ex")) {
1347 sprintf(cmdbuf, "-c %d", n);
1350 sprintf(cmdbuf, "+%d", n);
1354 printf("%s %s %s", editor, cmdbuf, fnames[fnum]);
1357 execute(filename, editor, editor, cmdbuf,
1358 cmdbuf+3, fnames[fnum], (char *)0);
1360 execute(filename, editor, editor,
1361 cmdbuf, fnames[fnum], (char *)0);
1368 if (Senter && Sexit) {
1369 my_putstring (Senter);
1370 promptlen = printf(_("[Press 'h' for instructions.]"))
1372 my_putstring (Sexit);
1375 promptlen = printf(_("[Press 'h' for instructions.]"));
1394 * Execute a colon-prefixed command.
1395 * Returns <0 if not a command that should cause
1396 * more of the file to be printed.
1399 int colon (char *filename, int cmd, int nlines) {
1409 promptlen = printf(_("\"%s\" line %d"), fnames[fnum], Currline);
1411 promptlen = printf(_("[Not a file] line %d"), Currline);
1416 if (fnum >= nfiles - 1)
1436 do_shell (filename);
1448 ** Read a decimal number from the terminal. Set cmd to the non-digit which
1449 ** terminates the number.
1452 int number(char *cmd)
1456 i = 0; ch = otty.c_cc[VKILL];
1460 i = i*10 + ch - '0';
1461 else if ((cc_t) ch == otty.c_cc[VKILL])
1471 void do_shell (char *filename)
1482 putsout(shell_line);
1484 ttyin (cmdbuf, sizeof(cmdbuf)-2, '!');
1486 rc = expand (&expanded, cmdbuf);
1488 if (strlen(expanded) < sizeof(shell_line))
1489 strcpy(shell_line, expanded);
1495 putserr(_(" Overflow\n"));
1498 } else if (rc > 0) {
1500 promptlen = printf("!%s", shell_line);
1507 execute (filename, shell, shell, "-c", shell_line, 0);
1511 ** Search for nth ocurrence of regular expression contained in buf in the file
1514 void search(char buf[], FILE *file, register int n)
1516 long startline = Ftell (file);
1517 register long line1 = startline;
1518 register long line2 = startline;
1519 register long line3 = startline;
1520 register int lncount;
1524 context.line = saveln = Currline;
1525 context.chrctr = startline;
1527 if ((s = re_comp (buf)) != 0)
1529 while (!feof (file)) {
1532 line1 = Ftell (file);
1535 if ((rv = re_exec (Line)) == 1) {
1537 if (lncount > 3 || (lncount > 1 && no_intty))
1542 putsout(_("...skipping\n"));
1545 Currline -= (lncount >= 3 ? 3 : lncount);
1546 Fseek (file, line3);
1570 } else if (rv == -1)
1571 error (_("Regular expression botch"));
1576 Fseek (file, startline);
1579 putsout(_("\nPattern not found\n"));
1582 error (_("Pattern not found"));
1587 void execute (char *filename, char *cmd, ...)
1598 for (n = 10; (id = fork ()) < 0 && n > 0; n--)
1603 open("/dev/tty", 0);
1606 va_start(argp, cmd);
1607 arg = va_arg(argp, char *);
1611 arg = va_arg(argp, char *);
1615 args = alloca(sizeof(char *) * (argcount + 1));
1616 args[argcount] = NULL;
1618 va_start(argp, cmd);
1619 arg = va_arg(argp, char *);
1622 args[argcount] = arg;
1624 arg = va_arg(argp, char *);
1629 putserr(_("exec failed\n"));
1633 signal (SIGINT, SIG_IGN);
1634 signal (SIGQUIT, SIG_IGN);
1636 signal(SIGTSTP, SIG_DFL);
1637 while (wait(0) > 0);
1638 signal (SIGINT, end_it);
1639 signal (SIGQUIT, onquit);
1641 signal(SIGTSTP, onsusp);
1643 putserr(_("can't fork\n"));
1645 puts("------------------------");
1649 ** Skip n lines in the file f
1652 void skiplns (register int n, register FILE *f)
1657 while ((c = Getc (f)) != '\n')
1666 ** Skip nskip files in the file list (from the command line). Nskip may be
1670 void skipf (register int nskip)
1672 if (nskip == 0) return;
1674 if (fnum + nskip > nfiles - 1)
1675 nskip = nfiles - fnum - 1;
1682 puts(_("\n...Skipping "));
1686 putsout(_("...Skipping to file "));
1688 putsout(_("...Skipping back to file "));
1696 /*----------------------------- Terminal I/O -------------------------------*/
1708 no_tty = tcgetattr(fileno(stdout), &otty);
1710 docrterase = (otty.c_cc[VERASE] != 255);
1711 docrtkill = (otty.c_cc[VKILL] != 255);
1716 * Wait until we're in the foreground before we save the
1717 * the terminal modes.
1719 if ((tgrp = tcgetpgrp(fileno(stdout))) < 0) {
1720 perror("tcgetpgrp");
1723 if (tgrp != getpgrp(0)) {
1729 if ((term = getenv("TERM")) == 0) {
1732 my_setupterm(term, 1, &ret);
1738 if (ioctl(fileno(stdout), TIOCGWINSZ, &win) < 0) {
1740 Lpp = my_tgetnum("li","lines");
1741 Mcol = my_tgetnum("co","cols");
1744 if ((Lpp = win.ws_row) == 0)
1745 Lpp = my_tgetnum("li","lines");
1746 if ((Mcol = win.ws_col) == 0)
1747 Mcol = my_tgetnum("co","cols");
1750 if ((Lpp <= 0) || my_tgetflag("hc","hc")) {
1751 hard++; /* Hard copy terminal */
1755 if (my_tgetflag("xn","xenl"))
1756 eatnl++; /* Eat newline at last column + 1; dec, concept */
1760 if (tailequ (fnames[0], "page"))
1762 Wrap = my_tgetflag("am","am");
1763 bad_so = my_tgetflag ("xs","xhp");
1764 eraseln = my_tgetstr("ce","el");
1765 Clear = my_tgetstr("cl","clear");
1766 Senter = my_tgetstr("so","smso");
1767 Sexit = my_tgetstr("se","rmso");
1768 if ((soglitch = my_tgetnum("sg","xmc")) < 0)
1772 * Set up for underlining: some terminals don't need it;
1773 * others have start/stop sequences, still others have an
1774 * underline char sequence which is assumed to move the
1775 * cursor forward one character. If underline sequence
1776 * isn't available, settle for standout sequence.
1779 if (my_tgetflag("ul","ul") || my_tgetflag("os","os"))
1781 if ((chUL = my_tgetstr("uc","uc")) == NULL )
1783 if (((ULenter = my_tgetstr("us","smul")) == NULL ||
1784 (ULexit = my_tgetstr("ue","rmul")) == NULL) && !*chUL) {
1785 if ((ULenter = Senter) == NULL || (ULexit = Sexit) == NULL) {
1789 ulglitch = soglitch;
1794 if ((padstr = my_tgetstr("pc","pad")) != NULL)
1796 Home = my_tgetstr("ho","home");
1797 if (Home == 0 || *Home == '\0') {
1798 if ((cursorm = my_tgetstr("cm","cup")) != NULL) {
1799 const char *t = (const char *)my_tgoto(cursorm, 0, 0);
1800 xstrncpy(cursorhome, t, sizeof(cursorhome));
1804 EodClr = my_tgetstr("cd","ed");
1805 if ((chBS = my_tgetstr("le","cub1")) == NULL)
1809 if ((shell = getenv("SHELL")) == NULL)
1812 no_intty = tcgetattr(fileno(stdin), &otty);
1813 tcgetattr(fileno(stderr), &otty);
1815 slow_tty = cfgetispeed(&otty) < B1200;
1816 hardtabs = (otty.c_oflag & TABDLY) != XTABS;
1818 otty.c_lflag &= ~(ICANON|ECHO);
1819 otty.c_cc[VMIN] = 1;
1820 otty.c_cc[VTIME] = 0;
1828 if (read (fileno(stderr), &c, 1) <= 0) {
1832 c = otty.c_cc[VKILL];
1837 static char *BS = "\b";
1838 static char *BSB = "\b \b";
1839 static char *CARAT = "^";
1840 #define ERASEONECOLUMN \
1846 void ttyin (char buf[], register int nmax, char pchar) {
1854 while (sp - buf < nmax) {
1855 if (promptlen > maxlen) maxlen = promptlen;
1860 else if (((cc_t) c == otty.c_cc[VERASE]) && !slash) {
1862 #ifdef HAVE_WIDECHAR
1866 size_t pos = 0, mblength;
1867 mbstate_t state, state_bak;
1869 memset (&state, '\0', sizeof (mbstate_t));
1873 mblength = mbrtowc (&wc, buf + pos, sp - buf, &state);
1875 state = (mblength == (size_t)-2
1876 || mblength == (size_t)-1) ? state_bak : state;
1877 mblength = (mblength == (size_t)-2
1878 || mblength == (size_t)-1
1879 || mblength == 0) ? 1 : mblength;
1881 if (buf + pos + mblength >= sp)
1887 if (mblength == 1) {
1892 wc_width = wcwidth (wc);
1893 wc_width = (wc_width < 1) ? 1 : wc_width;
1894 while (wc_width--) {
1899 while (mblength--) {
1912 if ((*sp < ' ' && *sp != '\n') || *sp == RUBOUT) {
1919 if (!eraseln) promptlen = maxlen;
1920 siglongjmp (restore, 1);
1923 else if (((cc_t) c == otty.c_cc[VKILL]) && !slash) {
1935 while (promptlen-- > 1)
1943 if (slash && ((cc_t) c == otty.c_cc[VKILL]
1944 || (cc_t) c == otty.c_cc[VERASE])) {
1951 if ((c < ' ' && c != '\n' && c != ESC) || c == RUBOUT) {
1952 c += (c == RUBOUT) ? -0100 : 0100;
1956 if (c != '\n' && c != ESC) {
1964 if (!eraseln) promptlen = maxlen;
1965 if (sp - buf >= nmax - 1)
1966 error (_("Line too long"));
1969 /* return: 0 - unchanged, 1 - changed, -1 - overflow (unchanged) */
1970 int expand (char **outbuf, char *inbuf) {
1976 int tempsz, xtra, offset;
1978 xtra = strlen (fnames[fnum]) + strlen (shell_line) + 1;
1979 tempsz = 200 + xtra;
1980 temp = malloc(tempsz);
1982 error (_("Out of memory"));
1987 while ((c = *inpstr++) != '\0'){
1988 offset = outstr-temp;
1989 if (tempsz-offset-1 < xtra) {
1990 tempsz += 200 + xtra;
1991 temp = realloc(temp, tempsz);
1993 error (_("Out of memory"));
1996 outstr = temp + offset;
2001 strcpy (outstr, fnames[fnum]);
2002 outstr += strlen (fnames[fnum]);
2009 error (_("No previous command to substitute for"));
2010 strcpy (outstr, shell_line);
2011 outstr += strlen (shell_line);
2015 if (*inpstr == '%' || *inpstr == '!') {
2016 *outstr++ = *inpstr++;
2028 void show (char c) {
2029 if ((c < ' ' && c != '\n' && c != ESC) || c == RUBOUT) {
2030 c += (c == RUBOUT) ? -0100 : 0100;
2038 void error (char *mess)
2044 promptlen += strlen (mess);
2045 if (Senter && Sexit) {
2046 my_putstring (Senter);
2048 my_putstring (Sexit);
2054 siglongjmp (restore, 1);
2059 otty.c_lflag &= ~(ICANON|ECHO);
2060 otty.c_cc[VMIN] = 1; /* read at least 1 char */
2061 otty.c_cc[VTIME] = 0; /* no timeout */
2062 stty(fileno(stderr), &otty);
2067 return putc(c, stdout);
2075 tputs(ULexit, 1, ourputch); /* putchar - if that isnt a macro */
2079 otty.c_lflag |= ICANON|ECHO;
2080 otty.c_cc[VMIN] = savetty0.c_cc[VMIN];
2081 otty.c_cc[VTIME] = savetty0.c_cc[VTIME];
2082 stty(fileno(stderr), &savetty0);
2085 void rdline (register FILE *f)
2091 while ((c = Getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1)
2098 /* Come here when we get a suspend signal from the terminal */
2100 void onsusp (int dummy) {
2101 sigset_t signals, oldmask;
2103 /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
2104 signal(SIGTTOU, SIG_IGN);
2107 signal(SIGTTOU, SIG_DFL);
2108 /* Send the TSTP signal to suspend our process group */
2109 signal(SIGTSTP, SIG_DFL);
2111 /* unblock SIGTSTP or we won't be able to suspend ourself */
2112 sigemptyset(&signals);
2113 sigaddset(&signals, SIGTSTP);
2114 sigprocmask(SIG_UNBLOCK, &signals, &oldmask);
2117 /* Pause for station break */
2119 sigprocmask(SIG_SETMASK, &oldmask, NULL);
2122 signal (SIGTSTP, onsusp);
2125 siglongjmp (restore, 1);