/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* Copyright (c) 1987, 1988 Microsoft Corporation */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ /* * @(#) more.c 1.1 88/03/29 more:more.c */ /* ** 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 ** modified by Hans Spiller, Microsoft to handle \r better July 23, 82 ** added ? help command, and -w ** ** vwh 11 Jan 83 M001 ** modified to handle x.out magic number and magic number ** byte ordering OTHER than the vax and pdp11. ** JJD 19 Jan 83 M002 ** - fix distributed on USENET ** From decvax!ucbvax!dist2 Sun Dec 6 02:58:31 1981 ** Subject: FIXED: bug in src/more/more.c ** - fixed bug on terminal with "magic cookie" standout ** sequences. ** JJD 14 Feb 83 M003 ** - fix exit status of more ** - Made first letter of "no more" message uppercase ** andyp 03 Aug 83 M004 3.0 upgrade ** - moved to cmd/include. ** - use UCB, rather than XENIX, stty(2). ** andyp 30 Nov 83 M005 ** - (thanks to reubenb). Changed frame variable to static, it is ** used as a global buffer. We never saw the bug before because ** of the depth of the stack. ** barrys 03 Jul 84 M006 ** - Updated the usage message to include the 's' and 'w' options ** and to make the 'n' option a separate entry (uncommented). ** ericc 26 Dec 84 M007 ** - Replaced the constant 0x7fffffffffffffffL with MAXLONG. ** ericc 25 Jul 85 M008 ** - made "-r" option display control characters as '^x', as documented. ** - fixed processing of '\b' so that more doesn't terminate when ** the sequence "\b\n" is encountered. ** - changed "Hit Rubout ..." to "Hit Del ...", for ibm keyboards. ** davidby 9 March 1988 Unmarked ** - replaced all locally defined functions with library equivalents, ** - changed from termcap to terminfo ** - included for MAXLONG value ** - removed most ifdef code for V6, V7, and BSD ** - added /etc/magic support for file type checking */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include eucwidth_t wp; int cw[4]; int scw[4]; #include /* Help file will eventually go in libpath(more.help) on all systems */ #ifdef INGRES #define VI "/usr/bin/vi" #define HELPFILE "/mntp/doucette/more/more.help" #define LOCAL_HELP "/usr/lib/locale/%s/LC_MESSAGES/more.help" #endif #ifndef INGRES #ifndef HELPFILE #define HELPFILE "/usr/lib/more.help" #define LOCAL_HELP "/usr/lib/locale/%s/LC_MESSAGES/more.help" #endif #define VI "vi" #endif #define Fopen(s,m) (Currline = 0,file_pos=0,fopen(s,m)) #define Ftell(f) file_pos #define Fseek(f,off) (file_pos=off,fseeko(f,off,0)) #define Getc(f) (++file_pos, getc(f)) #define Ungetc(c,f) (--file_pos, ungetc(c,f)) #define pr(s1) fputs(s1, stdout) #define clreos() putp(clr_eos) #define cleareol() putp(clr_eol) #define home() putp(cursor_home) #define LINSIZ 512 #define ctrl(letter) ((letter) & 077) #define RUBOUT '\177' #define ESC '\033' #define QUIT '\034' struct termio otty; /* old tty modes */ struct termio ntty; /* new tty modes */ off_t file_pos, file_size; int fnum, no_intty, no_tty; int dum_opt; off_t dlines; void end_it(int sig) __NORETURN; void onquit(int sig); void chgwinsz(int sig); #ifdef SIGTSTP void onsusp(int sig); #endif 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 cr_opt = 0; /* show ctrl characters as '^c' */ int wait_opt = 0; /* prompt for exit at eof */ int promptlen; off_t Currline; /* Line we are currently at */ int startup = 1; int firstf = 1; int notell = 1; 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; 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 ch; jmp_buf restore; char obuf[BUFSIZ]; /* stdout buffer */ char Line[LINSIZ]; /* Line buffer */ int Lpp = 24; /* lines per page */ char *ULenter, *ULexit; /* enter and exit underline mode */ int Mcol = 80; /* number of columns */ int Wrap = 1; /* set if automargins */ int fseeko(); struct { off_t chrctr, line; } context, screen_start; int exitstat = 0; /* status to use when exiting more */ /*M003*/ static void execute(char *filename, char *cmd, ...); static void error(char *mess); static void wait_eof(void); static void prompt(char *filename); static void argscan(char *s); static void copy_file(register FILE *f); static void initterm(void); static void do_shell(char *filename); static FILE *checkf(register char *fs, int *clearfirst); static void screen(register FILE *f, register off_t num_lines); static void skiplns(register off_t n, register FILE *f); static void skipf(register int nskip); static int readch(void); static void prmpt_erase(register int col); static void kill_line(void); static void prbuf(register char *s, register int n); static void search(char buf[], FILE *file, register off_t n); static void doclear(void); static void ttyin(char buf[], register int nmax, char pchar); static int expand(char *outbuf, char *inbuf); static void show(register char ch); static void set_tty(void); static void reset_tty(void); static void rdline(register FILE *f); static off_t command(char *filename, register FILE *f); static int getaline(register FILE *f, int *length); static int number(char *cmd); static int colon(char *filename, int cmd, off_t nlines); int main(int argc, char *argv[]) { register FILE *f; register char *s; register char *p; register int ch; register off_t left; int prnames = 0; int initopt = 0; int srchopt = 0; int clearit = 0; off_t initline; char initbuf[80]; setlocale( LC_ALL, "" ); getwidth(&wp); cw[0] = 1; cw[1] = wp._eucw1; cw[2] = wp._eucw2+1; cw[3] = wp._eucw3+1; scw[0] = 1; scw[1] = wp._scrw1; scw[2] = wp._scrw2; scw[3] = wp._scrw3; nfiles = argc; fnames = argv; (void) setlocale(LC_ALL,""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ #endif (void) textdomain(TEXT_DOMAIN); initterm (); if(s = getenv("MORE")) argscan(s); while (--nfiles > 0) { if ((ch = (*++fnames)[0]) == '-') { argscan(*fnames+1); } else if (ch == '+') { s = *fnames; if (*++s == '/') { srchopt++; for (++s, p = initbuf; p < initbuf + 79 && *s != '\0';) *p++ = *s++; *p = '\0'; } else { initopt++; for (initline = 0; *s != '\0'; s++) if (isdigit (*s)) initline = initline*10 + *s -'0'; --initline; } } else break; } /* allow clreol only if cursor_home and clr_eol and clr_eos strings are * defined, and in that case, make sure we are in noscroll mode */ if(clreol) { if (!cursor_home || !clr_eol || !clr_eos) { clreol = 0; } else noscroll = 1; } if (dlines == 0) dlines =(off_t) (Lpp - (noscroll ? 1 : 2)); left = dlines; if (nfiles > 1) prnames++; if (!no_intty && nfiles == 0) { fprintf(stderr, gettext("Usage: %s\ [-cdflrsuw] [-lines] [+linenumber] [+/pattern] [filename ...].\n") , argv[0]); exit(1); } else f = stdin; if (!no_tty) { signal(SIGQUIT, onquit); signal(SIGINT, end_it); signal(SIGWINCH, chgwinsz); #ifdef SIGTSTP if (signal (SIGTSTP, SIG_IGN) == SIG_DFL) { signal(SIGTSTP, onsusp); catch_susp++; } #endif set_tty(); } if (no_intty) { if (no_tty) copy_file (stdin); else { if ((ch = Getc (f)) == '\f') doclear(); else { Ungetc (ch, f); if (noscroll && (ch != EOF)) { if (clreol) home (); else doclear (); } } if (!setjmp(restore)) { if (srchopt) { search (initbuf, stdin,(off_t) 1); if (noscroll) left--; } else if (initopt) skiplns (initline, stdin); } else left = command(NULL, f); screen (stdin, left); } no_intty = 0; prnames++; firstf = 0; } while (fnum < nfiles) { if ((f = checkf (fnames[fnum], &clearit)) != NULL) { context.line = context.chrctr = 0; Currline = 0; if (firstf) setjmp (restore); if (firstf) { firstf = 0; if (srchopt) { search (initbuf, f,(off_t) 1); if (noscroll) left--; } else if (initopt) skiplns (initline, f); } else if (fnum < nfiles && !no_tty) { setjmp (restore); left = command (fnames[fnum], f); } if (left != 0) { if ((noscroll || clearit) && (file_size != LLONG_MAX)) if (clreol) home (); else doclear (); if (prnames) { if (ceol_standout_glitch) prmpt_erase (0); if (clreol) cleareol (); pr("::::::::::::::"); if (promptlen > 14) prmpt_erase (14); printf ("\n"); if(clreol) cleareol(); printf("%s\n", fnames[fnum]); if(clreol) cleareol(); pr("::::::::::::::\n"); if (left > (off_t)(Lpp - 4)) left =(off_t)(Lpp - 4); } if (no_tty) copy_file (f); else { within++; screen(f, left); within = 0; } } setjmp (restore); fflush(stdout); fclose(f); screen_start.line = screen_start.chrctr = 0LL; context.line = context.chrctr = 0LL; } else exitstat |= 1; /*M003*/ fnum++; firstf = 0; } if (wait_opt) wait_eof(); reset_tty (); return (exitstat); /*M003*/ } static void argscan(char *s) { for (dlines = 0; *s != '\0'; s++) if (isdigit(*s)) dlines = dlines*10 + *s - '0'; else if (*s == 'd') dum_opt = 1; else if (*s == 'l') stop_opt = 0; else if (*s == 'f') fold_opt = 0; else if (*s == 'p') noscroll++; else if (*s == 'c') clreol++; else if (*s == 's') ssp_opt = 1; else if (*s == 'u') ul_opt = 0; else if (*s == 'r') cr_opt = 1; else if (*s == 'w') wait_opt = 1; } /* ** Check whether the file named by fs is a file which the user may ** access. If it is, return the opened file. Otherwise return NULL. */ static FILE * checkf(register char *fs, int *clearfirst) { struct stat stbuf; register FILE *f; int c; if (stat (fs, &stbuf) == -1) { fflush(stdout); if (clreol) cleareol (); perror(fs); return (NULL); } if ((stbuf.st_mode & S_IFMT) == S_IFDIR) { printf(gettext("\n*** %s: directory ***\n\n"), fs); return (NULL); } if ((f=Fopen(fs, "r")) == NULL) { fflush(stdout); perror(fs); return (NULL); } if ((c = Getc(f)) == '\f') /* end M001 */ *clearfirst = 1; else { *clearfirst = 0; Ungetc (c, f); } if ((file_size = (off_t)stbuf.st_size) == 0) file_size = LLONG_MAX; return (f); } /* ** Print out the contents of the file f, one screenful at a time. */ #define STOP -10 static void screen(register FILE *f, register off_t num_lines) { register int c; register int nchars; int length; /* length of current line */ static int prev_len = 1; /* length of previous line */ for (;;) { while (num_lines > 0 && !Pause) { if ((nchars = getaline (f, &length)) == EOF) { if (clreol) clreos(); return; } if (ssp_opt && length == 0 && prev_len == 0) continue; prev_len = length; if (ceol_standout_glitch || (enter_standout_mode && *enter_standout_mode == ' ') && promptlen > 0) prmpt_erase (0); /* must clear before drawing line since tabs on some terminals * do not erase what they tab over. */ if (clreol) cleareol (); prbuf (Line, length); if (nchars < promptlen) prmpt_erase (nchars); /* prmpt_erase () sets promptlen to 0 */ else promptlen = 0; /* is this needed? * if (clreol) * cleareol(); */ /* must clear again in case we wrapped */ if (nchars < Mcol || !fold_opt) putchar('\n'); if (nchars == STOP) break; num_lines--; } fflush(stdout); if ((c = Getc(f)) == EOF) { if (clreol) clreos (); return; } if (Pause && clreol) clreos (); Ungetc (c, f); setjmp (restore); Pause = 0; startup = 0; if ((num_lines = command (NULL, f)) == 0) return; if (hard && promptlen > 0) prmpt_erase (0); if (noscroll && num_lines == dlines) { if (clreol) home(); else doclear (); } screen_start.line = Currline; screen_start.chrctr = Ftell (f); } } /* ** Come here if a quit signal is received */ /* * sig is put in as a dummy arg to have the compiler not to complain */ /* ARGSUSED */ void onquit(int sig) { signal(SIGQUIT, SIG_IGN); if (!inwait) { putchar ('\n'); if (!startup) { signal(SIGQUIT, onquit); longjmp (restore, 1); } else Pause++; } else if (!dum_opt && notell) { write (2, gettext("[Use q or Q to quit]"), 20); promptlen += 20; notell = 0; } signal(SIGQUIT, onquit); } /* ** Come here if a signal for a window size change is received */ /*ARGSUSED*/ void chgwinsz(int sig) { struct winsize win; (void) signal(SIGWINCH, SIG_IGN); if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) { if (win.ws_row != 0) { Lpp = win.ws_row; nscroll = Lpp/2 - 1; if (nscroll <= 0) nscroll = 1; dlines = (off_t)(Lpp - (noscroll ? 1 : 2)); } if (win.ws_col != 0) Mcol = win.ws_col; } (void) signal(SIGWINCH, chgwinsz); } /* ** Clean up terminal state and exit. Also come here if interrupt signal received */ /* * sig is put in as a dummy arg to have the compiler not to complain */ /* ARGSUSED */ void end_it(int sig) { reset_tty (); if (clreol) { putchar ('\r'); clreos (); fflush (stdout); } else if (!clreol && (promptlen > 0)) { kill_line (); fflush (stdout); } else write (2, "\n", 1); _exit(exitstat); /*M003*/ } static void copy_file(register FILE *f) { register int c; while ((c = getc(f)) != EOF) putchar(c); } static char Bell = ctrl('G'); /* See whether the last component of the path name "path" is equal to the ** string "string" */ int tailequ(char *path, char *string) { return (!strcmp(basename(path), string)); } static void prompt(char *filename) { if (clreol) cleareol (); else if (promptlen > 0) kill_line (); if (!hard) { promptlen = 8; if (enter_standout_mode && exit_standout_mode) putp (enter_standout_mode); if (clreol) cleareol (); pr(gettext("--More--")); if (filename != NULL) { promptlen += printf (gettext("(Next file: %s)"), filename); } else if (!no_intty) { promptlen += printf ("(%d%%)", (int)((file_pos * 100) / file_size)); } if (dum_opt) { promptlen += pr(gettext("[Hit space to continue, Del to abort]")); } if (enter_standout_mode && exit_standout_mode) putp (exit_standout_mode); if (clreol) clreos (); fflush(stdout); } else write (2, &Bell, 1); inwait++; } /* * when run from another program or a shell script, it is * sometimes useful to prevent the next program from scrolling * us off the screen before we get a chance to read this page. * -Hans, July 24, 1982 */ static void wait_eof(void) { if (enter_standout_mode && exit_standout_mode) putp (enter_standout_mode); promptlen = pr(gettext("--No more--")); /*M003*/ if (dum_opt) promptlen += pr(gettext("[Hit any key to continue]")); if (enter_standout_mode && exit_standout_mode) putp(exit_standout_mode); if (clreol) clreos(); fflush(stdout); readch(); prmpt_erase (0); fflush(stdout); } /* ** Get a logical line */ static int getaline(register FILE *f, int *length) { register int c; register char *p; register int column; static int colflg; register int oldcolumn; int csno; p = Line; column = 0; oldcolumn = 0; c = Getc (f); if (colflg && c == '\n') { Currline++; c = Getc (f); } while (p < &Line[LINSIZ - 1]) { csno = csetno(c); if (c == EOF) { if (p > Line) { *p = '\0'; *length = p - Line; return (column); } *length = p - Line; return (EOF); } if (!csno) { if (c == '\n') { /* detect \r\n. -Hans */ if (p>Line && p[-1] == '\r') { column = oldcolumn; p--; } Currline++; break; } *p++ = c; if (c == '\t') if (hardtabs && column < promptlen && !hard) { if (clr_eol && !dumb) { column = 1 + (column | 7); putp (clr_eol); promptlen = 0; } else { for (--p; column & 7 && p < &Line[LINSIZ - 1]; column++) { *p++ = ' '; } if (column >= promptlen) promptlen = 0; } } else column = 1 + (column | 7); else if ((c == '\b') && (ul_opt || !cr_opt) && (column > 0)) /* M008 */ column--; /* this is sort of strange. what was here before was that \r always set column to zero, and the hack above to detect \r\n didnt exist. the net effect is to make the current line be overwritten by the prompt if it had a \r at the end, and the line start after the \r otherwise. I suppose this is useful for overstriking on hard copy terminals, but not on anything glass -Hans */ else if ((c == '\r') && !cr_opt) { oldcolumn = column; column = 0; } else if (c == '\f' && stop_opt) { p[-1] = '^'; *p++ = 'L'; column += 2; Pause++; } else if (c == EOF) { *length = p - Line; return (column); } else if (c < ' ' && cr_opt){ /* M008 begin */ p[-1] = '^'; *p++ = c | ('A' - 1); column += 2; } /* M008 end */ else if (c >= ' ' && c != RUBOUT) column++; } /* end of code set 0 */ else { column += scw[csno]; if ( column > Mcol && fold_opt ) { column -= scw[csno]; while ( column < Mcol ) { column++; *p++ = ' '; } column = Mcol; Ungetc(c,f); } else { int i; *p++ = c; for(i=1; i= Mcol && fold_opt) break; c = Getc (f); } if (column >= Mcol && Mcol > 0) { if (!Wrap) { *p++ = '\n'; } } colflg = column == Mcol && fold_opt; if (colflg && eat_newline_glitch && Wrap) { *p++ = '\n'; /* simulate normal wrap */ } *length = p - Line; *p = 0; return (column); } /* ** Erase the rest of the prompt, assuming we are starting at column col. */ static void prmpt_erase(register int col) { if (promptlen == 0) return; if (hard) { putchar ('\n'); } else { if (col == 0) putchar ('\r'); if (!dumb && clr_eol) putp (clr_eol); else for (col = promptlen - col; col > 0; col--) putchar (' '); } promptlen = 0; } /* ** Erase the current line entirely */ static void kill_line(void) { prmpt_erase (0); if (!clr_eol || dumb) putchar ('\r'); } /* Print a buffer of n characters */ static void prbuf(register char *s, register int n) { char c; /* next ouput character */ register int state = 0; /* next output char's UL state */ static int pstate = 0; /* current terminal UL state (off) */ while (--n >= 0) if (!ul_opt) putchar (*s++); else { if (n >= 2 && s[0] == '_' && s[1] == '\b') { n -= 2; s += 2; c = *s++; state = 1; } else if (n >= 2 && s[1] == '\b' && s[2] == '_') { n -= 2; c = *s++; s += 2; state = 1; } else { c = *s++; state = 0; } if (state != pstate) putp(state ? ULenter : ULexit); pstate = state; putchar(c); if (state && underline_char) { putp(cursor_left); putp(underline_char); } } /* * M002 * You don't want to stay in standout mode at the end of the line; * on some terminals, this will leave all of the remaining blank * space on the line in standout mode. */ if (state && !underline_char) { /*M002*/ putp(ULexit); /*M002*/ pstate = 0; /*M002*/ } /*M002*/ } /* ** Clear the screen */ static void doclear(void) { if (clear_screen && !hard) { putp(clear_screen); /* Put out carriage return so that system doesn't ** get confused by escape sequences when expanding tabs */ putchar ('\r'); promptlen = 0; } } static int lastcmd, lastp; static off_t lastarg; static int lastcolon; char shell_line[PATH_MAX]; /* ** 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. */ static off_t command(char *filename, register FILE *f) { register off_t nlines; register off_t retval; register int c; char colonch; FILE *helpf; int done; char comchar, cmdbuf[80]; char filebuf[128]; char *loc; #define ret(val) retval=val;done++;break done = 0; if (!errors) prompt (filename); else errors = 0; for (;;) { nlines = number (&comchar); lastp = colonch = 0; if (comchar == '.') { /* Repeat last command */ lastp++; comchar = lastcmd; nlines = lastarg; if (lastcmd == ':') colonch = lastcolon; } lastcmd = comchar; lastarg = nlines; if((comchar != RUBOUT) && !dum_opt) { if (comchar == otty.c_cc[VERASE]) { kill_line (); prompt (filename); continue; } } switch (comchar) { case ':': retval = colon (filename, colonch, nlines); if (retval >= 0) done++; break; case 'b': case ctrl('B'): { register off_t initline; if (no_intty) { write(2, &bell, 1); return (-1); } if (nlines == 0) nlines++; putchar ('\r'); prmpt_erase (0); printf ("\n"); if (clreol) cleareol (); printf (gettext("...back %lld page"), nlines); if (nlines > 1) pr ("s\n"); else pr ("\n"); if (clreol) cleareol (); pr ("\n"); initline = Currline - dlines * (nlines + 1); if (! noscroll) --initline; if (initline < 0) initline = 0; Fseek(f, 0LL); Currline = 0; /* skiplns() will make Currline correct */ skiplns(initline, f); if (! noscroll) { ret(dlines + 1); } else { ret(dlines); } } case ' ': case 'z': if (nlines == 0) nlines = dlines; else if (comchar == 'z') dlines = nlines; ret (nlines); case 'd': case ctrl('D'): if (nlines != 0) nscroll = nlines; ret (nscroll); case RUBOUT: case 'q': case 'Q': end_it(0); case 's': case 'f': if (nlines == 0) nlines++; if (comchar == 'f') nlines *= dlines; putchar ('\r'); prmpt_erase (0); printf ("\n"); if (clreol) cleareol (); printf (gettext("...skipping %lld line"), nlines); if (nlines > 1) pr ("s\n"); else pr ("\n"); if (clreol) cleareol (); pr ("\n"); while (nlines > 0) { while ((c = Getc (f)) != '\n') if (c == EOF) { retval = 0; done++; goto endsw; } Currline++; nlines--; } ret (dlines); case '\n': if (nlines != 0) dlines = nlines; else nlines = 1; ret (nlines); case '\f': if (!no_intty) { doclear (); Fseek (f, screen_start.chrctr); Currline = screen_start.line; ret (dlines); } else { write (2, &Bell, 1); break; } case '\'': if (!no_intty) { kill_line (); pr (gettext("\n***Back***\n\n")); Fseek (f, context.chrctr); Currline = context.line; ret (dlines); } else { write (2, &Bell, 1); break; } case '=': kill_line (); promptlen = printf ("%lld", Currline); fflush (stdout); break; case 'n': lastp++; /* FALLTHROUGH */ case '/': if (nlines == 0) nlines++; kill_line (); pr ("/"); promptlen = 1; fflush (stdout); if (lastp) { write (2,"\r", 1); search (NULL, f, nlines); /* Use previous r.e. */ } else { ttyin (cmdbuf, 78, '/'); write (2, "\r", 1); search (cmdbuf, f, nlines); } ret (dlines-1); case '!': do_shell (filename); break; case 'h': case '?': /* * First get local help file. */ loc = setlocale(LC_MESSAGES, 0); sprintf(filebuf, LOCAL_HELP, loc); if ((strcmp(loc, "C") == 0) || (helpf = fopen (filebuf, "r")) == NULL) { if ((helpf = fopen (HELPFILE, "r")) == NULL) error (gettext("Can't open help file")); } if (noscroll) doclear (); copy_file (helpf); fclose (helpf); prompt (filename); break; case 'v': /* This case should go right before default */ if (!no_intty) { kill_line (); cmdbuf[0] = '+'; sprintf(&cmdbuf[1], "%lld", Currline); pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]); execute (filename, VI, "vi", cmdbuf, fnames[fnum], 0); break; } /* FALLTHROUGH */ default: if (dum_opt) { kill_line (); promptlen = pr(gettext("[Press 'h' for instructions.]")); fflush (stdout); } else write (2, &Bell, 1); break; } if (done) break; } putchar ('\r'); endsw: inwait = 0; notell++; return (retval); } char ch; /* * Execute a colon-prefixed command. * Returns <0 if not a command that should cause * more of the file to be printed. */ static int colon(char *filename, int cmd, off_t nlines) { if (cmd == 0) ch = readch (); else ch = cmd; lastcolon = ch; switch (ch) { case 'f': kill_line (); if (!no_intty) promptlen = printf (gettext("\"%s\" line %lld"), fnames[fnum], Currline); else promptlen = printf( gettext("[Not a file] line %lld"), Currline); fflush (stdout); return (-1); case 'n': if (nlines == 0) { if (fnum >= nfiles - 1) end_it(0); nlines++; } putchar ('\r'); prmpt_erase (0); skipf ((int)nlines); return (0); case 'p': if (no_intty) { write (2, &Bell, 1); return (-1); } putchar ('\r'); prmpt_erase (0); if (nlines == 0) nlines++; skipf ((int)-nlines); return (0); case '!': do_shell (filename); return (-1); case 'q': case 'Q': end_it(0); default: write (2, &Bell, 1); return (-1); } } /* ** Read a decimal number from the terminal. Set cmd to the non-digit which ** terminates the number. */ static int number(char *cmd) { register int i; i = 0; ch = otty.c_cc[VKILL]; for (;;) { ch = readch (); if (ch >= '0' && ch <= '9') { i = i*10 + ch - '0'; } else if (ch == RUBOUT) { i = 0; *cmd = ch; break; } else if (ch == otty.c_cc[VKILL]) { i = 0; } else { *cmd = ch; break; } } return (i); } static void do_shell(char *filename) { char cmdbuf[80]; kill_line (); pr ("!"); fflush (stdout); promptlen = 1; if (lastp) pr (shell_line); else { ttyin (cmdbuf, 78, '!'); if (expand (shell_line, cmdbuf)) { kill_line (); promptlen = printf ("!%s", shell_line); } } fflush (stdout); write (2, "\n", 1); promptlen = 0; shellp = 1; execute (filename, shell, shell, "-c", shell_line, 0); } /* ** Search for nth ocurrence of regular expression contained in buf in the file */ static void search(char buf[], FILE *file, register off_t n) { off_t startline = Ftell (file); register off_t line1 = startline; register off_t line2 = startline; register off_t line3 = startline; register off_t lncount; off_t saveln; static char *s = NULL; static char lastbuf[80]; if (buf != NULL) { if (s != NULL) free(s); if (*buf != '\0') { if ((s = regcmp(buf, (char *) NULL)) == NULL) error(gettext("Regular expression botch")); else strcpy(lastbuf, buf); } else { if ((s = regcmp(lastbuf, (char *) NULL)) == NULL) error(gettext("No previous regular expression")); } } else { if (s == NULL) error(gettext("No previous regular expression")); } context.line = saveln = Currline; context.chrctr = startline; lncount = 0; while (!feof (file)) { line3 = line2; line2 = line1; line1 = Ftell (file); rdline (file); lncount++; if (regex(s, Line) != NULL) if (--n == 0) { if (lncount > 3 || (lncount > 1 && no_intty)) { pr ("\n"); if (clreol) cleareol (); pr(gettext("...skipping\n")); } if (!no_intty) { Currline -= (lncount >= 3 ? 3 : lncount); Fseek (file, line3); if (noscroll) if (clreol) { home (); cleareol (); } else doclear (); } else { kill_line (); if (noscroll) if (clreol) { home (); cleareol (); } else doclear (); pr (Line); putchar ('\n'); } break; } } if (feof (file)) { if (!no_intty) { Currline = saveln; Fseek (file, startline); } else { pr (gettext("\nPattern not found\n")); end_it (0); } error (gettext("Pattern not found")); } } #define MAXARGS 10 /* enough for 9 args. We are only passed 4 now */ static void execute (char *filename, char *cmd, ...) { pid_t id; va_list ap; char *argp[MAXARGS]; int count; fflush (stdout); reset_tty (); while ((id = fork ()) < 0) sleep (5); if (id == 0) { if (no_intty) { /*M002*/ close(0); /*M002*/ dup(2); /*M002*/ } /*M002*/ va_start(ap, cmd); count = 0; do { #ifndef lint argp[count] = va_arg(ap, char *); #else ap = ap; #endif count++; if (count > MAXARGS) error (gettext("Too many arguments in execute()\n")); } while (argp[count - 1] != NULL); va_end(ap); execvp(cmd, argp); write (2, "exec failed\n", 12); exit (1); } signal (SIGINT, SIG_IGN); signal (SIGQUIT, SIG_IGN); signal (SIGWINCH, SIG_IGN); #ifdef SIGTSTP if (catch_susp) signal(SIGTSTP, SIG_DFL); #endif wait ((pid_t)0); signal (SIGINT, end_it); signal (SIGQUIT, onquit); signal (SIGWINCH, chgwinsz); #ifdef SIGTSTP if (catch_susp) signal(SIGTSTP, onsusp); #endif /* * Since we were ignoring window change signals while we executed * the command, we must assume the window changed. */ (void) chgwinsz(0); set_tty (); pr ("------------------------\n"); prompt (filename); } /* ** Skip n lines in the file f */ static void skiplns(register off_t n, register FILE *f) { register int c; while (n > 0) { while ((c = Getc (f)) != '\n') if (c == EOF) return; n--; Currline++; } } /* ** Skip nskip files in the file list (from the command line). Nskip may be ** negative. */ static void skipf(register int nskip) { if (nskip == 0) return; if (nskip > 0) { if (fnum + nskip > nfiles - 1) nskip = nfiles - fnum - 1; } else if (within) ++fnum; fnum += nskip; if (fnum < 0) fnum = 0; pr (gettext("\n...Skipping ")); pr ("\n"); if (clreol) cleareol (); if (nskip > 0) printf(gettext("...Skipping to file %s\n"), fnames[fnum]); else printf(gettext("...Skipping back to file %s\n"), fnames[fnum]); if (clreol) cleareol (); pr ("\n"); --fnum; } /*----------------------------- Terminal I/O -------------------------------*/ static void initterm(void) { int erret = 0; setbuf(stdout, obuf); if (!(no_tty = ioctl(1, TCGETA, &otty))) { if (setupterm(NULL, 1, &erret) != OK) { dumb++; ul_opt = 0; } else { int row, col; struct winsize w; char *s; reset_shell_mode(); row = col = -1; if (ioctl(fileno(stdout), TIOCGWINSZ, &w) == 0) { if (w.ws_row > 0) row = w.ws_row; if (w.ws_col > 0) col = w.ws_col; } if (row < 0) { if ((s = getenv("LINES")) != NULL) row = atoi(s); } if (col < 0) { if ((s = getenv("COLUMNS")) != NULL) col = atoi(s); } if (row > 0) Lpp = row; else if (((Lpp = lines) < 0) || hard_copy) { hard++; /* Hard copy terminal */ Lpp = 24; } if (col > 0) Mcol = col; else if ((Mcol = columns) < 0) Mcol = 80; if (tailequ(fnames[0], "page") || !hard && (scroll_forward == NULL)) noscroll++; Wrap = tigetflag("am"); /* * 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 (transparent_underline || over_strike) ul_opt = 0; if ((ULenter = tigetstr("smul")) == NULL && (!underline_char) && (ULenter = tigetstr("smso")) == NULL) ULenter = ""; if ((ULexit = tigetstr("rmul")) == NULL && (!underline_char) && (ULexit = tigetstr("rmso")) == NULL) ULexit = ""; } if ((shell = getenv("SHELL")) == NULL) shell = "/usr/bin/sh"; } no_intty = ioctl(0, TCGETA, &otty); ioctl(2, TCGETA, &otty); hardtabs = !(otty.c_oflag & TAB3); } static int readch(void) { char ch; extern int errno; if (read (2, &ch, 1) <= 0) if (errno != EINTR) end_it(0); /* clean up before exiting */ else ch = otty.c_cc[VKILL]; return (ch); } static char BS = '\b'; static char CARAT = '^'; static void ttyin(char buf[], register int nmax, char pchar) { register char *sptr; register unsigned char ch; int LengthBuffer[80]; int *BufferPointer; int csno; register int slash = 0; int maxlen; char cbuf; BufferPointer = LengthBuffer; sptr = buf; maxlen = 0; while (sptr - buf < nmax) { if (promptlen > maxlen) maxlen = promptlen; ch = readch (); csno = csetno(ch); if (!csno) { if (ch == '\\') { slash++; } else if ((ch == otty.c_cc[VERASE]) && !slash) { if (sptr > buf) { --promptlen; write (2, &BS, 1); sptr -= (*--BufferPointer); if ((*sptr < ' ' && *sptr != '\n') || *sptr == RUBOUT) { --promptlen; write (2, &BS, 1); } continue; } else { if (!clr_eol) promptlen = maxlen; longjmp (restore, 1); } } else if ((ch == otty.c_cc[VKILL]) && !slash) { if (hard) { show(ch); putchar ('\n'); putchar (pchar); } else { putchar ('\r'); putchar (pchar); if (clr_eol) prmpt_erase (1); promptlen = 1; } sptr = buf; fflush (stdout); continue; } if (slash && (ch == otty.c_cc[VKILL] || ch == otty.c_cc[VERASE])) { write (2, &BS, 1); sptr -= (*--BufferPointer); } if (ch != '\\') slash = 0; *BufferPointer++ = 1; *sptr++ = ch; if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) { ch += ch == RUBOUT ? -0100 : 0100; write (2, &CARAT, 1); promptlen++; } cbuf = ch; if (ch != '\n' && ch != ESC) { write (2, &cbuf, 1); promptlen++; } else break; /* end of code set 0 */ } else { int i; u_char buffer[5]; *BufferPointer++ = cw[csno]; buffer[0] = *sptr++ = ch; for(i=1; i= nmax - 1) error (gettext("Line too long")); } static int expand(char *outbuf, char *inbuf) { char *in_str; char *out_str; char ch; char temp[PATH_MAX]; int changed = 0; in_str = inbuf; out_str = temp; while ((ch = *in_str++) != '\0') switch (ch) { case '%': if (!no_intty) { if (strlcpy(out_str, fnames[fnum], sizeof (temp)) >= sizeof (temp)) error(gettext("Command too long")); out_str += strlen (fnames[fnum]); changed++; } else *out_str++ = ch; break; case '!': if (!shellp) error (gettext("No previous command to substitute for")); if (strlcpy(out_str, shell_line, sizeof (temp)) >= sizeof (temp)) error(gettext("Command too long")); out_str += strlen (shell_line); changed++; break; case '\\': if (*in_str == '%' || *in_str == '!') { *out_str++ = *in_str++; break; } /* FALLTHROUGH */ default: *out_str++ = ch; } *out_str++ = '\0'; if (strlcpy(outbuf, temp, sizeof (shell_line)) >= sizeof (shell_line)) error(gettext("Command too long")); return (changed); } static void show(register char ch) { char cbuf; if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) { ch += ch == RUBOUT ? -0100 : 0100; write (2, &CARAT, 1); promptlen++; } cbuf = ch; write (2, &cbuf, 1); promptlen++; } static void error (char *mess) { if (clreol) cleareol (); else kill_line (); promptlen += strlen (mess); if (enter_standout_mode && exit_standout_mode) { putp (enter_standout_mode); pr(mess); putp (exit_standout_mode); } else pr (mess); fflush(stdout); errors++; longjmp (restore, 1); } static void set_tty(void) { ioctl(2, TCGETA, &otty); /* save old tty modes */ ioctl(2, TCGETA, &ntty); ntty.c_lflag &= ~ECHO & ~ICANON; ntty.c_cc[VMIN] = (char)1; ntty.c_cc[VTIME] = (char)0; ioctl (2, TCSETAF, &ntty); /* set new tty modes */ } static void reset_tty(void) { ioctl (2, TCSETAF, &otty); /* reset tty modes */ } static void rdline(register FILE *f) { register int c; register char *p; p = Line; while ((c = Getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1) *p++ = c; if (c == '\n') Currline++; *p = '\0'; } /* Come here when we get a suspend signal from the terminal */ /* * sig is put in as a dummy arg to have the compiler not to complain */ #ifdef SIGTSTP /* ARGSUSED */ void onsusp(int sig) { /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */ signal(SIGTTOU, SIG_IGN); reset_tty (); fflush (stdout); signal(SIGTTOU, SIG_DFL); /* Send the TSTP signal to suspend our process group */ kill (0, SIGTSTP); /* Pause for station break */ /* We're back */ signal (SIGTSTP, onsusp); set_tty (); if (inwait) longjmp (restore, 1); } #endif