summaryrefslogtreecommitdiff
path: root/text-utils/more.c
diff options
context:
space:
mode:
Diffstat (limited to 'text-utils/more.c')
-rw-r--r--text-utils/more.c2197
1 files changed, 2197 insertions, 0 deletions
diff --git a/text-utils/more.c b/text-utils/more.c
new file mode 100644
index 0000000..58c99cc
--- /dev/null
+++ b/text-utils/more.c
@@ -0,0 +1,2197 @@
+/*
+ * Copyright (C) 1980 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+** 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 Erik Troan <ewt@redhat.com> to be more posix and so compile
+** on linux/axp.
+** modified by Kars de Jong <jongk@cs.utwente.nl> to use terminfo instead
+** of termcap.
+ 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ - added Native Language Support
+ 1999-03-19 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ - more nls translatable strings
+ 1999-05-09 aeb - applied a RedHat patch (setjmp->sigsetjmp); without it
+ a second ^Z would fail.
+ 1999-05-09 aeb - undone Kars' work, so that more works without
+ libcurses (and hence can be in /bin with libcurses being in /usr/lib
+ which may not be mounted). However, when termcap is not present curses
+ can still be used.
+ 2010-10-21 Davidlohr Bueso <dave@gnu.org>
+ - modified mem allocation handling for util-linux
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h> /* for alloca() */
+#include <stdarg.h> /* for va_start() etc */
+#include <sys/param.h>
+#include <ctype.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <setjmp.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+
+#include "strutils.h"
+#include "nls.h"
+#include "xalloc.h"
+#include "widechar.h"
+#include "closestream.h"
+
+#include <regex.h>
+
+#ifndef XTABS
+#define XTABS TAB3
+#endif /* XTABS */
+
+#define VI "vi" /* found on the user's path */
+
+#define Fopen(s,m) (Currline = 0,file_pos=0,fopen(s,m))
+#define Ftell(f) file_pos
+#define Fseek(f,off) (file_pos=off,fseek(f,off,0))
+#define Getc(f) (++file_pos, getc(f))
+#define Ungetc(c,f) (--file_pos, ungetc(c,f))
+#define putcerr(c) fputc(c, stderr)
+#define putserr(s) fputs(s, stderr)
+#define putsout(s) fputs(s, stdout)
+
+#define stty(fd,argp) tcsetattr(fd,TCSANOW,argp)
+
+/* some function declarations */
+void initterm(void);
+void kill_line(void);
+void doclear(void);
+void cleareol(void);
+void clreos(void);
+void home(void);
+void more_error (char *mess);
+void do_shell (char *filename);
+int colon (char *filename, int cmd, int nlines);
+int expand (char **outbuf, char *inbuf);
+void argscan(char *s);
+void rdline (register FILE *f);
+void copy_file(register FILE *f);
+void search(char buf[], FILE *file, register int n);
+void skipf (register int nskip);
+void skiplns(register int n, register FILE *f);
+void screen (register FILE *f, register int num_lines);
+int command (char *filename, register FILE *f);
+void erasep (register int col);
+void show (register char ch);
+void set_tty(void);
+void reset_tty(void);
+void ttyin (char buf[], register int nmax, char pchar);
+int number(char *cmd);
+int readch (void);
+int get_line(register FILE *f, int *length);
+void prbuf (register char *s, register int n);
+void execute (char *filename, char *cmd, ...);
+FILE *checkf (char *, int *);
+void prepare_line_buffer(void);
+
+#define TBUFSIZ 1024
+#define LINSIZ 256 /* minimal Line buffer size */
+#define ctrl(letter) (letter & 077)
+#define RUBOUT '\177'
+#define ESC '\033'
+#define QUIT '\034'
+#define SCROLL_LEN 11
+#define LINES_PER_PAGE 24
+#define NUM_COLUMNS 80
+#define TERMINAL_BUF 4096
+#define INIT_BUF 80
+#define SHELL_LINE 1000
+#define COMMAND_BUF 200
+#define REGERR_BUF NUM_COLUMNS
+
+struct termios otty, savetty0;
+long file_pos, file_size;
+int fnum, no_intty, no_tty, slow_tty;
+int dum_opt, dlines;
+void onquit(int), onsusp(int), chgwinsz(int), end_it(int);
+int nscroll = SCROLL_LEN; /* 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 promptlen;
+int Currline; /* Line we are currently at */
+int startup = 1;
+int firstf = 1;
+int notell = 1;
+int docrterase = 0;
+int docrtkill = 0;
+int bad_so; /* True if overwriting does not turn
+ off standout */
+int inwait, Pause, errors;
+int within; /* true if we are within a file,
+ false if we are between files */
+int hard, dumb, noscroll, hardtabs, clreol, eatnl;
+int catch_susp; /* We should catch the SIGTSTP signal */
+char **fnames; /* The list of file names */
+int nfiles; /* Number of files left to process */
+char *shell; /* The name of the shell to use */
+int shellp; /* A previous shell command exists */
+sigjmp_buf restore;
+char *Line; /* Line buffer */
+size_t LineLen; /* size of Line buffer */
+int Lpp = LINES_PER_PAGE; /* lines per page */
+char *Clear; /* clear screen */
+char *eraseln; /* erase line */
+char *Senter, *Sexit; /* enter and exit standout mode */
+char *ULenter, *ULexit; /* enter and exit underline mode */
+char *chUL; /* underline character */
+char *chBS; /* backspace character */
+char *Home; /* go to home */
+char *cursorm; /* cursor movement */
+char cursorhome[40]; /* contains cursor movement to home */
+char *EodClr; /* clear rest of screen */
+int Mcol = NUM_COLUMNS; /* number of columns */
+int Wrap = 1; /* set if automargins */
+int soglitch; /* terminal has standout mode glitch */
+int ulglitch; /* terminal has underline mode glitch */
+int pstate = 0; /* current UL state */
+static int magic(FILE *, char *);
+char *previousre; /* previous search() buf[] item */
+struct {
+ long chrctr, line;
+} context, screen_start;
+extern char PC; /* pad character */
+
+#ifdef HAVE_NCURSES_H
+# include <ncurses.h>
+#elif defined(HAVE_NCURSES_NCURSES_H)
+# include <ncurses/ncurses.h>
+#endif /* HAVE_NCURSES_H */
+
+#if defined(HAVE_NCURSES_H) || defined(HAVE_NCURSES_NCURSES_H)
+# include <term.h> /* include after <curses.h> */
+
+#define TERM_AUTO_RIGHT_MARGIN "am"
+#define TERM_CEOL "xhp"
+#define TERM_CLEAR "clear"
+#define TERM_CLEAR_TO_LINE_END "el"
+#define TERM_CLEAR_TO_SCREEN_END "ed"
+#define TERM_COLS "cols"
+#define TERM_CURSOR_ADDRESS "cup"
+#define TERM_EAT_NEW_LINE "xenl"
+#define TERM_ENTER_UNDERLINE "smul"
+#define TERM_EXIT_STANDARD_MODE "rmso"
+#define TERM_EXIT_UNDERLINE "rmul"
+#define TERM_HARD_COPY "hc"
+#define TERM_HOME "home"
+#define TERM_LINE_DOWN "cud1"
+#define TERM_LINES "lines"
+#define TERM_OVER_STRIKE "os"
+#define TERM_PAD_CHAR "pad"
+#define TERM_STANDARD_MODE "smso"
+#define TERM_STD_MODE_GLITCH "xmc"
+#define TERM_UNDERLINE_CHAR "uc"
+#define TERM_UNDERLINE "ul"
+
+static void my_putstring(char *s) {
+ tputs (s, fileno(stdout), putchar); /* putp(s); */
+}
+
+static void my_setupterm(char *term, int fildes, int *errret) {
+ setupterm(term, fildes, errret);
+}
+
+static int my_tgetnum(char *s) {
+ return tigetnum(s);
+}
+
+static int my_tgetflag(char *s) {
+ return tigetflag(s);
+}
+
+static char *my_tgetstr(char *s) {
+ return tigetstr(s);
+}
+
+static char *my_tgoto(char *cap, int col, int row) {
+ return tparm(cap, col, row);
+}
+
+#elif defined(HAVE_LIBTERMCAP) /* !ncurses */
+
+#include <termcap.h>
+
+#define TERM_AUTO_RIGHT_MARGIN "am"
+#define TERM_CEOL "xs"
+#define TERM_CLEAR "cl"
+#define TERM_CLEAR_TO_LINE_END "ce"
+#define TERM_CLEAR_TO_SCREEN_END "cd"
+#define TERM_COLS "co"
+#define TERM_CURSOR_ADDRESS "cm"
+#define TERM_EAT_NEW_LINE "xn"
+#define TERM_ENTER_UNDERLINE "us"
+#define TERM_EXIT_STANDARD_MODE "se"
+#define TERM_EXIT_UNDERLINE "ue"
+#define TERM_HARD_COPY "hc"
+#define TERM_HOME "ho"
+#define TERM_LINE_DOWN "le"
+#define TERM_LINES "li"
+#define TERM_OVER_STRIKE "os"
+#define TERM_PAD_CHAR "pc"
+#define TERM_STANDARD_MODE "so"
+#define TERM_STD_MODE_GLITCH "sg"
+#define TERM_UNDERLINE_CHAR "uc"
+#define TERM_UNDERLINE "ul"
+
+char termbuffer[TERMINAL_BUF];
+char tcbuffer[TERMINAL_BUF];
+char *strbuf = termbuffer;
+
+static void my_putstring(char *s) {
+ tputs (s, fileno(stdout), putchar);
+}
+
+static void my_setupterm(char *term, int fildes, int *errret) {
+ *errret = tgetent(tcbuffer, term);
+}
+
+static int my_tgetnum(char *s) {
+ return tgetnum(s);
+}
+
+static int my_tgetflag(char *s) {
+ return tgetflag(s);
+}
+
+static char *my_tgetstr(char *s) {
+ return tgetstr(s, &strbuf);
+}
+
+static char *my_tgoto(char *cap, int col, int row) {
+ return tgoto(cap, col, row);
+}
+
+#endif /* HAVE_LIBTERMCAP */
+
+static void __attribute__ ((__noreturn__)) usage(FILE *out)
+{
+ fprintf(out,
+ _("Usage: %s [options] file...\n\n"),
+ program_invocation_short_name);
+ fprintf(out,
+ _("Options:\n"
+ " -d display help instead of ring bell\n"
+ " -f count logical, rather than screen lines\n"
+ " -l suppress pause after form feed\n"
+ " -p suppress scroll, clean screen and display text\n"
+ " -c suppress scroll, display text and clean line ends\n"
+ " -u suppress underlining\n"
+ " -s squeeze multiple blank lines into one\n"
+ " -NUM specify the number of lines per screenful\n"
+ " +NUM display file beginning from line number NUM\n"
+ " +/STRING display file beginning from search string match\n"
+ " -V output version information and exit\n"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv) {
+ FILE *f;
+ char *s;
+ int ch;
+ int left;
+ int prnames = 0;
+ int initopt = 0;
+ int srchopt = 0;
+ int clearit = 0;
+ int initline = 0;
+ char *initbuf = NULL;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ nfiles = argc;
+ fnames = argv;
+ setlocale(LC_ALL, "");
+ initterm ();
+
+ /* Auto set no scroll on when binary is called page */
+ if (!(strcmp(program_invocation_short_name, "page")))
+ noscroll++;
+
+ prepare_line_buffer();
+
+ nscroll = Lpp/2 - 1;
+ if (nscroll <= 0)
+ nscroll = 1;
+
+ if ((s = getenv("MORE")) != NULL)
+ argscan(s);
+
+ while (--nfiles > 0) {
+ if ((ch = (*++fnames)[0]) == '-') {
+ argscan(*fnames+1);
+ }
+ else if (ch == '+') {
+ s = *fnames;
+ if (*++s == '/') {
+ srchopt++;
+ initbuf = xstrdup(s + 1);
+ }
+ else {
+ initopt++;
+ for (initline = 0; *s != '\0'; s++)
+ if (isdigit (*s))
+ initline = initline*10 + *s -'0';
+ --initline;
+ }
+ }
+ else break;
+ }
+ /* allow clreol only if Home and eraseln and EodClr strings are
+ * defined, and in that case, make sure we are in noscroll mode
+ */
+ if (clreol) {
+ if((Home == NULL) || (*Home == '\0') ||
+ (eraseln == NULL) || (*eraseln == '\0') ||
+ (EodClr == NULL) || (*EodClr == '\0') )
+ clreol = 0;
+ else noscroll = 1;
+ }
+ if (dlines == 0)
+ dlines = Lpp - 1; /* was: Lpp - (noscroll ? 1 : 2) */
+ left = dlines;
+ if (nfiles > 1)
+ prnames++;
+ if (!no_intty && nfiles == 0)
+ usage(stderr);
+ else
+ f = stdin;
+ if (!no_tty) {
+ signal(SIGQUIT, onquit);
+ signal(SIGINT, end_it);
+#ifdef SIGWINCH
+ signal(SIGWINCH, chgwinsz);
+#endif /* SIGWINCH */
+ if (signal (SIGTSTP, SIG_IGN) == SIG_DFL) {
+ signal(SIGTSTP, onsusp);
+ catch_susp++;
+ }
+ stty (fileno(stderr), &otty);
+ }
+ 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 (srchopt)
+ {
+ previousre = xstrdup(initbuf);
+ search (initbuf, stdin, 1);
+ if (noscroll)
+ left--;
+ }
+ else if (initopt)
+ skiplns (initline, stdin);
+ 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) sigsetjmp (restore, 1);
+ if (firstf) {
+ firstf = 0;
+ if (srchopt) {
+ previousre = xstrdup(initbuf);
+ search (initbuf, f, 1);
+ if (noscroll)
+ left--;
+ }
+ else if (initopt)
+ skiplns (initline, f);
+ }
+ else if (fnum < nfiles && !no_tty) {
+ sigsetjmp (restore, 1);
+ left = command (fnames[fnum], f);
+ }
+ if (left != 0) {
+ if ((noscroll || clearit) && (file_size != LONG_MAX)) {
+ if (clreol)
+ home ();
+ else
+ doclear ();
+ }
+ if (prnames) {
+ if (bad_so)
+ erasep (0);
+ if (clreol)
+ cleareol ();
+ putsout("::::::::::::::");
+ if (promptlen > 14)
+ erasep (14);
+ putchar('\n');
+ if(clreol) cleareol();
+ puts(fnames[fnum]);
+ if(clreol) cleareol();
+ puts("::::::::::::::");
+ if (left > Lpp - 4)
+ left = Lpp - 4;
+ }
+ if (no_tty)
+ copy_file (f);
+ else {
+ within++;
+ screen(f, left);
+ within = 0;
+ }
+ }
+ sigsetjmp (restore, 1);
+ fflush(stdout);
+ fclose(f);
+ screen_start.line = screen_start.chrctr = 0L;
+ context.line = context.chrctr = 0L;
+ }
+ fnum++;
+ firstf = 0;
+ }
+ free (previousre);
+ free (initbuf);
+ reset_tty ();
+ exit(EXIT_SUCCESS);
+}
+
+void argscan(char *s) {
+ int seen_num = 0;
+
+ while (*s != '\0') {
+ switch (*s) {
+ case '0': case '1': case '2':
+ case '3': case '4': case '5':
+ case '6': case '7': case '8':
+ case '9':
+ if (!seen_num) {
+ dlines = 0;
+ seen_num = 1;
+ }
+ dlines = dlines*10 + *s - '0';
+ break;
+ case 'd':
+ dum_opt = 1;
+ break;
+ case 'l':
+ stop_opt = 0;
+ break;
+ case 'f':
+ fold_opt = 0;
+ break;
+ case 'p':
+ noscroll++;
+ break;
+ case 'c':
+ clreol++;
+ break;
+ case 's':
+ ssp_opt = 1;
+ break;
+ case 'u':
+ ul_opt = 0;
+ break;
+ case '-': case ' ': case '\t':
+ break;
+ case 'V':
+ printf(_("more (%s)\n"), PACKAGE_STRING);
+ exit(EXIT_SUCCESS);
+ break;
+ default:
+ warnx(_("unknown option -%s"), s);
+ usage(stderr);
+ break;
+ }
+ s++;
+ }
+}
+
+
+/*
+** Check whether the file named by fs is an ASCII file which the user may
+** access. If it is, return the opened file. Otherwise return NULL.
+*/
+
+FILE *
+checkf (fs, clearfirst)
+ register char *fs;
+ int *clearfirst;
+{
+ struct stat stbuf;
+ register FILE *f;
+ int c;
+
+ if (stat (fs, &stbuf) == -1) {
+ (void)fflush(stdout);
+ if (clreol)
+ cleareol ();
+ perror(fs);
+ return((FILE *)NULL);
+ }
+ if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
+ printf(_("\n*** %s: directory ***\n\n"), fs);
+ return((FILE *)NULL);
+ }
+ if ((f = Fopen(fs, "r")) == NULL) {
+ (void)fflush(stdout);
+ perror(fs);
+ return((FILE *)NULL);
+ }
+ if (magic(f, fs))
+ return((FILE *)NULL);
+ fcntl(fileno(f), F_SETFD, FD_CLOEXEC );
+ c = Getc(f);
+ *clearfirst = (c == '\f');
+ Ungetc (c, f);
+ if ((file_size = stbuf.st_size) == 0)
+ file_size = LONG_MAX;
+ return(f);
+}
+
+/*
+ * magic --
+ * check for file magic numbers. This code would best be shared with
+ * the file(1) program or, perhaps, more should not try to be so smart.
+ */
+static int
+magic(f, fs)
+ FILE *f;
+ char *fs;
+{
+ signed char twobytes[2];
+
+ /* don't try to look ahead if the input is unseekable */
+ if (fseek(f, 0L, SEEK_SET))
+ return 0;
+
+ if (fread(twobytes, 2, 1, f) == 1) {
+ switch(twobytes[0] + (twobytes[1]<<8)) {
+ case 0407: /* a.out obj */
+ case 0410: /* a.out exec */
+ case 0413: /* a.out demand exec */
+ case 0405:
+ case 0411:
+ case 0177545:
+ case 0x457f: /* simple ELF detection */
+ printf(_("\n******** %s: Not a text file ********\n\n"), fs);
+ (void)fclose(f);
+ return 1;
+ }
+ }
+ (void)fseek(f, 0L, SEEK_SET); /* rewind() not necessary */
+ return 0;
+}
+
+/*
+** Print out the contents of the file f, one screenful at a time.
+*/
+
+#define STOP -10
+
+void screen (register FILE *f, register int 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 = get_line (f, &length)) == EOF)
+ {
+ if (clreol)
+ clreos();
+ return;
+ }
+ if (ssp_opt && length == 0 && prev_len == 0)
+ continue;
+ prev_len = length;
+ if (bad_so || ((Senter && *Senter == ' ') && (promptlen > 0)))
+ erasep (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)
+ erasep (nchars); /* erasep () 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)
+ prbuf("\n", 1); /* will turn off UL if necessary */
+ if (nchars == STOP)
+ break;
+ num_lines--;
+ }
+ if (pstate) {
+ my_putstring (ULexit);
+ pstate = 0;
+ }
+ fflush(stdout);
+ if ((c = Getc(f)) == EOF)
+ {
+ if (clreol)
+ clreos ();
+ return;
+ }
+
+ if (Pause && clreol)
+ clreos ();
+ Ungetc (c, f);
+ sigsetjmp (restore, 1);
+ Pause = 0; startup = 0;
+ if ((num_lines = command (NULL, f)) == 0)
+ return;
+ if (hard && promptlen > 0)
+ erasep (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
+*/
+
+void onquit(int dummy __attribute__ ((__unused__)))
+{
+ signal(SIGQUIT, SIG_IGN);
+ if (!inwait) {
+ putchar ('\n');
+ if (!startup) {
+ signal(SIGQUIT, onquit);
+ siglongjmp (restore, 1);
+ }
+ else
+ Pause++;
+ }
+ else if (!dum_opt && notell) {
+ promptlen += fprintf(stderr, _("[Use q or Q to quit]"));
+ notell = 0;
+ }
+ signal(SIGQUIT, onquit);
+}
+
+/*
+** Come here if a signal for a window size change is received
+*/
+
+#ifdef SIGWINCH
+void chgwinsz(int dummy __attribute__ ((__unused__)))
+{
+ struct winsize win;
+
+ 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 = Lpp - 1; /* was: Lpp - (noscroll ? 1 : 2) */
+ }
+ if (win.ws_col != 0)
+ Mcol = win.ws_col;
+ }
+ (void) signal(SIGWINCH, chgwinsz);
+}
+#endif /* SIGWINCH */
+
+/*
+** Clean up terminal state and exit. Also come here if interrupt signal received
+*/
+
+void __attribute__((__noreturn__))
+end_it (int dummy __attribute__ ((__unused__)))
+{
+ reset_tty ();
+ if (clreol) {
+ putchar ('\r');
+ clreos ();
+ fflush (stdout);
+ }
+ else if (!clreol && (promptlen > 0)) {
+ kill_line ();
+ fflush (stdout);
+ }
+ else
+ putcerr('\n');
+ _exit(EXIT_SUCCESS);
+}
+
+void copy_file(register FILE *f) {
+ register int c;
+
+ while ((c = getc(f)) != EOF)
+ putchar(c);
+}
+
+#define ringbell() putcerr('\007')
+
+static void prompt (char *filename)
+{
+ if (clreol)
+ cleareol ();
+ else if (promptlen > 0)
+ kill_line ();
+ if (!hard) {
+ promptlen = 0;
+ if (Senter && Sexit) {
+ my_putstring (Senter);
+ promptlen += (2 * soglitch);
+ }
+ if (clreol)
+ cleareol ();
+ promptlen += printf(_("--More--"));
+ if (filename != NULL) {
+ promptlen += printf(_("(Next file: %s)"), filename);
+ } else if (!no_intty) {
+ promptlen += printf("(%d%%)", (int) ((file_pos * 100) / file_size));
+ }
+ if (dum_opt) {
+ promptlen += printf(_("[Press space to continue, 'q' to quit.]"));
+ }
+ if (Senter && Sexit)
+ my_putstring (Sexit);
+ if (clreol)
+ clreos ();
+ fflush(stdout);
+ }
+ else
+ ringbell();
+ inwait++;
+}
+
+void prepare_line_buffer(void)
+{
+ char *nline;
+ size_t nsz = Mcol * 4;
+
+ if (LineLen >= nsz)
+ return;
+
+ if (nsz < LINSIZ)
+ nsz = LINSIZ;
+
+ nline = xrealloc(Line, nsz);
+ Line = nline;
+ LineLen = nsz;
+}
+
+/*
+ * Get a logical line
+ */
+
+int get_line(register FILE *f, int *length)
+{
+ int c;
+ char *p;
+ int column;
+ static int colflg;
+
+#ifdef HAVE_WIDECHAR
+ size_t i;
+ wchar_t wc;
+ int wc_width;
+ mbstate_t state, state_bak; /* Current status of the stream. */
+ char mbc[MB_LEN_MAX]; /* Buffer for one multibyte char. */
+ size_t mblength; /* Byte length of multibyte char. */
+ size_t mbc_pos = 0; /* Position of the MBC. */
+ int use_mbc_buffer_flag = 0; /* If 1, mbc has data. */
+ int break_flag = 0; /* If 1, exit while(). */
+ long file_pos_bak = Ftell (f);
+
+ memset (&state, '\0', sizeof (mbstate_t));
+#endif /* HAVE_WIDECHAR */
+
+ prepare_line_buffer();
+
+ p = Line;
+ column = 0;
+ c = Getc (f);
+ if (colflg && c == '\n') {
+ Currline++;
+ c = Getc (f);
+ }
+ while (p < &Line[LineLen - 1]) {
+#ifdef HAVE_WIDECHAR
+ if (fold_opt && use_mbc_buffer_flag && MB_CUR_MAX > 1) {
+ use_mbc_buffer_flag = 0;
+ state_bak = state;
+ mbc[mbc_pos++] = c;
+process_mbc:
+ mblength = mbrtowc (&wc, mbc, mbc_pos, &state);
+
+ switch (mblength) {
+ case (size_t)-2: /* Incomplete multibyte character. */
+ use_mbc_buffer_flag = 1;
+ state = state_bak;
+ break;
+
+ case (size_t)-1: /* Invalid as a multibyte character. */
+ *p++ = mbc[0];
+ state = state_bak;
+ column++;
+ file_pos_bak++;
+
+ if (column >= Mcol) {
+ Fseek (f, file_pos_bak);
+ } else {
+ memmove (mbc, mbc + 1, --mbc_pos);
+ if (mbc_pos > 0) {
+ mbc[mbc_pos] = '\0';
+ goto process_mbc;
+ }
+ }
+ break;
+
+ default:
+ wc_width = wcwidth (wc);
+
+ if (column + wc_width > Mcol) {
+ Fseek (f, file_pos_bak);
+ break_flag = 1;
+ } else {
+ for (i = 0; i < mbc_pos; i++)
+ *p++ = mbc[i];
+ if (wc_width > 0)
+ column += wc_width;
+ }
+ }
+
+ if (break_flag || column >= Mcol)
+ break;
+
+ c = Getc (f);
+ continue;
+ }
+#endif /* HAVE_WIDECHAR */
+ if (c == EOF) {
+ if (p > Line) {
+ *p = '\0';
+ *length = p - Line;
+ return (column);
+ }
+ *length = p - Line;
+ return (EOF);
+ }
+ if (c == '\n') {
+ Currline++;
+ break;
+ }
+
+ *p++ = c;
+#if 0
+ if (c == '\033') { /* ESC */
+ c = Getc(f);
+ while (c > ' ' && c < '0' && p < &Line[LineLen - 1]) {
+ *p++ = c;
+ c = Getc(f);
+ }
+ if (c >= '0' && c < '\177' && p < &Line[LineLen - 1]) {
+ *p++ = c;
+ c = Getc(f);
+ continue;
+ }
+ }
+#endif /* 0 */
+ if (c == '\t') {
+ if (!hardtabs || (column < promptlen && !hard)) {
+ if (hardtabs && eraseln && !dumb) {
+ column = 1 + (column | 7);
+ my_putstring (eraseln);
+ promptlen = 0;
+ }
+ else {
+ for (--p; p < &Line[LineLen - 1];) {
+ *p++ = ' ';
+ if ((++column & 7) == 0)
+ break;
+ }
+ if (column >= promptlen) promptlen = 0;
+ }
+ } else
+ column = 1 + (column | 7);
+ } else if (c == '\b' && column > 0) {
+ column--;
+ } else if (c == '\r') {
+ int next = Getc(f);
+ if (next == '\n') {
+ p--;
+ Currline++;
+ break;
+ }
+ Ungetc(next,f);
+ 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 {
+#ifdef HAVE_WIDECHAR
+ if (fold_opt && MB_CUR_MAX > 1) {
+ memset (mbc, '\0', MB_LEN_MAX);
+ mbc_pos = 0;
+ mbc[mbc_pos++] = c;
+ state_bak = state;
+
+ mblength = mbrtowc (&wc, mbc, mbc_pos, &state);
+
+ /* The value of mblength is always less than 2 here. */
+ switch (mblength) {
+ case (size_t)-2:
+ p--;
+ file_pos_bak = Ftell (f) - 1;
+ state = state_bak;
+ use_mbc_buffer_flag = 1;
+ break;
+
+ case (size_t)-1:
+ state = state_bak;
+ column++;
+ break;
+
+ default:
+ wc_width = wcwidth (wc);
+ if (wc_width > 0)
+ column += wc_width;
+ }
+ } else
+#endif /* HAVE_WIDECHAR */
+ {
+ if (isprint(c))
+ column++;
+ }
+ }
+
+ if (column >= Mcol && fold_opt)
+ break;
+ c = Getc (f);
+ }
+ if (column >= Mcol && Mcol > 0) {
+ if (!Wrap) {
+ *p++ = '\n';
+ }
+ }
+ colflg = column == Mcol && fold_opt;
+ if (colflg && eatnl && 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.
+*/
+
+void erasep (register int col)
+{
+
+ if (promptlen == 0)
+ return;
+ if (hard) {
+ putchar ('\n');
+ }
+ else {
+ if (col == 0)
+ putchar ('\r');
+ if (!dumb && eraseln)
+ my_putstring (eraseln);
+ else
+ for (col = promptlen - col; col > 0; col--)
+ putchar (' ');
+ }
+ promptlen = 0;
+}
+
+/*
+** Erase the current line entirely
+*/
+
+void kill_line()
+{
+ erasep(0);
+ if (!eraseln || dumb)
+ putchar('\r');
+}
+
+/*
+ * force clear to end of line
+ */
+void cleareol()
+{
+ my_putstring(eraseln);
+}
+
+void clreos()
+{
+ my_putstring(EodClr);
+}
+
+/* Print a buffer of n characters */
+
+void prbuf (register char *s, register int n)
+{
+ register char c; /* next output character */
+ register int state; /* next output char's UL state */
+#define wouldul(s,n) ((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_')))
+
+ while (--n >= 0)
+ if (!ul_opt)
+ putchar (*s++);
+ else {
+ if (*s == ' ' && pstate == 0 && ulglitch && wouldul(s+1, n-1)) {
+ s++;
+ continue;
+ }
+ if ((state = wouldul(s, n)) != 0) {
+ c = (*s == '_')? s[2] : *s ;
+ n -= 2;
+ s += 3;
+ } else
+ c = *s++;
+ if (state != pstate) {
+ if (c == ' ' && state == 0 && ulglitch && wouldul(s, n-1))
+ state = 1;
+ else
+ my_putstring(state ? ULenter : ULexit);
+ }
+ if (c != ' ' || pstate == 0 || state != 0 || ulglitch == 0)
+#ifdef HAVE_WIDECHAR
+ {
+ wchar_t wc;
+ size_t mblength;
+ mbstate_t mbstate;
+ memset (&mbstate, '\0', sizeof (mbstate_t));
+ s--; n++;
+ mblength = mbrtowc (&wc, s, n, &mbstate);
+ if (mblength == (size_t) -2 || mblength == (size_t) -1)
+ mblength = 1;
+ while (mblength--)
+ putchar (*s++);
+ n += mblength;
+ }
+#else
+ putchar(c);
+#endif /* HAVE_WIDECHAR */
+ if (state && *chUL) {
+ putsout(chBS);
+ my_putstring(chUL);
+ }
+ pstate = state;
+ }
+}
+
+/*
+** Clear the screen
+*/
+void
+doclear()
+{
+ if (Clear && !hard) {
+ my_putstring(Clear);
+
+ /* Put out carriage return so that system doesn't
+ ** get confused by escape sequences when expanding tabs
+ */
+ putchar ('\r');
+ promptlen = 0;
+ }
+}
+
+/*
+ * Go to home position
+ */
+void
+home()
+{
+ my_putstring(Home);
+}
+
+static int lastcmd, lastarg, lastp;
+static int lastcolon;
+char shell_line[SHELL_LINE];
+
+/*
+** 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.
+*/
+
+int command (char *filename, register FILE *f)
+{
+ register int nlines;
+ register int retval = 0;
+ register int c;
+ char colonch;
+ int done;
+ char comchar, cmdbuf[INIT_BUF];
+
+#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 ((cc_t) 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 int initline;
+
+ if (no_intty) {
+ ringbell();
+ return (-1);
+ }
+
+ if (nlines == 0) nlines++;
+
+ putchar ('\r');
+ erasep (0);
+ putchar('\n');
+ if (clreol)
+ cleareol ();
+ if (nlines != 1)
+ printf(_("...back %d pages"), nlines);
+ else
+ putsout(_("...back 1 page"));
+ if (clreol)
+ cleareol ();
+ putchar('\n');
+
+ initline = Currline - dlines * (nlines + 1);
+ if (! noscroll)
+ --initline;
+ if (initline < 0) initline = 0;
+ Fseek(f, 0L);
+ 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 'q':
+ case 'Q':
+ end_it (0);
+ case 's':
+ case 'f':
+ case ctrl('F'):
+ if (nlines == 0) nlines++;
+ if (comchar == 'f')
+ nlines *= dlines;
+ putchar ('\r');
+ erasep (0);
+ putchar('\n');
+ if (clreol)
+ cleareol ();
+ if (nlines == 1)
+ putsout(_("...skipping one line"));
+ else
+ printf(_("...skipping %d lines"), nlines);
+
+ if (clreol)
+ cleareol ();
+ putchar('\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 {
+ ringbell();
+ break;
+ }
+ case '\'':
+ if (!no_intty) {
+ kill_line ();
+ putsout(_("\n***Back***\n\n"));
+ Fseek (f, context.chrctr);
+ Currline = context.line;
+ ret (dlines);
+ }
+ else {
+ ringbell();
+ break;
+ }
+ case '=':
+ kill_line ();
+ promptlen = printf("%d", Currline);
+ fflush (stdout);
+ break;
+ case 'n':
+ if (!previousre) {
+ more_error (_("No previous regular expression"));
+ break;
+ }
+ lastp++;
+ /* fallthrough */
+ case '/':
+ if (nlines == 0) nlines++;
+ kill_line ();
+ putchar('/');
+ promptlen = 1;
+ fflush (stdout);
+ if (lastp) {
+ putcerr('\r');
+ search (previousre, f, nlines);
+ }
+ else {
+ ttyin (cmdbuf, sizeof(cmdbuf)-2, '/');
+ putcerr('\r');
+ free (previousre);
+ previousre = xstrdup(cmdbuf);
+ search (cmdbuf, f, nlines);
+ }
+ ret (dlines-1);
+ case '!':
+ do_shell (filename);
+ break;
+ case '?':
+ case 'h':
+ if (noscroll) doclear();
+ putsout(_("\n"
+"Most commands optionally preceded by integer argument k. "
+"Defaults in brackets.\n"
+"Star (*) indicates argument becomes new default.\n"));
+ puts("---------------------------------------"
+ "----------------------------------------");
+ putsout(_(
+"<space> Display next k lines of text [current screen size]\n"
+"z Display next k lines of text [current screen size]*\n"
+"<return> Display next k lines of text [1]*\n"
+"d or ctrl-D Scroll k lines [current scroll size, initially 11]*\n"
+"q or Q or <interrupt> Exit from more\n"
+"s Skip forward k lines of text [1]\n"
+"f Skip forward k screenfuls of text [1]\n"
+"b or ctrl-B Skip backwards k screenfuls of text [1]\n"
+"' Go to place where previous search started\n"
+"= Display current line number\n"
+"/<regular expression> Search for kth occurrence of regular expression [1]\n"
+"n Search for kth occurrence of last r.e [1]\n"
+"!<cmd> or :!<cmd> Execute <cmd> in a subshell\n"
+"v Start up /usr/bin/vi at current line\n"
+"ctrl-L Redraw screen\n"
+":n Go to kth next file [1]\n"
+":p Go to kth previous file [1]\n"
+":f Display current file name and line number\n"
+". Repeat previous command\n"));
+ puts("---------------------------------------"
+ "----------------------------------------");
+ prompt(filename);
+ break;
+ case 'v': /* This case should go right before default */
+ if (!no_intty) {
+ /*
+ * Earlier: call vi +n file. This also works for emacs.
+ * POSIX: call vi -c n file (when editor is vi or ex).
+ */
+ char *editor, *p;
+ int n = (Currline - dlines <= 0 ? 1 :
+ Currline - (dlines + 1) / 2);
+ int split = 0;
+
+ editor = getenv("VISUAL");
+ if (editor == NULL || *editor == '\0')
+ editor = getenv("EDITOR");
+ if (editor == NULL || *editor == '\0')
+ editor = VI;
+
+ p = strrchr(editor, '/');
+ if (p)
+ p++;
+ else
+ p = editor;
+ if (!strcmp(p, "vi") || !strcmp(p, "ex")) {
+ sprintf(cmdbuf, "-c %d", n);
+ split = 1;
+ } else {
+ sprintf(cmdbuf, "+%d", n);
+ }
+
+ kill_line();
+ printf("%s %s %s", editor, cmdbuf, fnames[fnum]);
+ if (split) {
+ cmdbuf[2] = 0;
+ execute(filename, editor, editor, cmdbuf,
+ cmdbuf+3, fnames[fnum], (char *)0);
+ } else
+ execute(filename, editor, editor,
+ cmdbuf, fnames[fnum], (char *)0);
+ break;
+ }
+ /* fall through */
+ default:
+ if (dum_opt) {
+ kill_line ();
+ if (Senter && Sexit) {
+ my_putstring (Senter);
+ promptlen = printf(_("[Press 'h' for instructions.]"))
+ + 2 * soglitch;
+ my_putstring (Sexit);
+ }
+ else
+ promptlen = printf(_("[Press 'h' for instructions.]"));
+ fflush (stdout);
+ }
+ else
+ ringbell();
+ break;
+ }
+ if (done) break;
+ }
+ putchar ('\r');
+endsw:
+ inwait = 0;
+ notell++;
+ return (retval);
+}
+
+static char ch;
+
+/*
+ * Execute a colon-prefixed command.
+ * Returns <0 if not a command that should cause
+ * more of the file to be printed.
+ */
+
+int colon (char *filename, int cmd, int nlines) {
+ if (cmd == 0)
+ ch = readch ();
+ else
+ ch = cmd;
+ lastcolon = ch;
+ switch (ch) {
+ case 'f':
+ kill_line ();
+ if (!no_intty)
+ promptlen = printf(_("\"%s\" line %d"), fnames[fnum], Currline);
+ else
+ promptlen = printf(_("[Not a file] line %d"), Currline);
+ fflush (stdout);
+ return (-1);
+ case 'n':
+ if (nlines == 0) {
+ if (fnum >= nfiles - 1)
+ end_it (0);
+ nlines++;
+ }
+ putchar ('\r');
+ erasep (0);
+ skipf (nlines);
+ return (0);
+ case 'p':
+ if (no_intty) {
+ ringbell();
+ return (-1);
+ }
+ putchar ('\r');
+ erasep (0);
+ if (nlines == 0)
+ nlines++;
+ skipf (-nlines);
+ return (0);
+ case '!':
+ do_shell (filename);
+ return (-1);
+ case 'q':
+ case 'Q':
+ end_it (0);
+ default:
+ ringbell();
+ return (-1);
+ }
+}
+
+/*
+** Read a decimal number from the terminal. Set cmd to the non-digit which
+** terminates the number.
+*/
+
+int number(char *cmd)
+{
+ register int i;
+
+ i = 0; ch = otty.c_cc[VKILL];
+ for (;;) {
+ ch = readch ();
+ if (isdigit(ch))
+ i = i*10 + ch - '0';
+ else if ((cc_t) ch == otty.c_cc[VKILL])
+ i = 0;
+ else {
+ *cmd = ch;
+ break;
+ }
+ }
+ return (i);
+}
+
+void do_shell (char *filename)
+{
+ char cmdbuf[COMMAND_BUF];
+ int rc;
+ char *expanded;
+
+ kill_line ();
+ putchar('!');
+ fflush (stdout);
+ promptlen = 1;
+ if (lastp)
+ putsout(shell_line);
+ else {
+ ttyin (cmdbuf, sizeof(cmdbuf)-2, '!');
+ expanded = 0;
+ rc = expand (&expanded, cmdbuf);
+ if (expanded) {
+ if (strlen(expanded) < sizeof(shell_line))
+ strcpy(shell_line, expanded);
+ else
+ rc = -1;
+ free(expanded);
+ }
+ if (rc < 0) {
+ putserr(_(" Overflow\n"));
+ prompt (filename);
+ return;
+ } else if (rc > 0) {
+ kill_line ();
+ promptlen = printf("!%s", shell_line);
+ }
+ }
+ fflush (stdout);
+ putcerr('\n');
+ promptlen = 0;
+ shellp = 1;
+ execute (filename, shell, shell, "-c", shell_line, 0);
+}
+
+/*
+** Search for nth occurrence of regular expression contained in buf in the file
+*/
+
+void search(char buf[], FILE *file, register int n)
+{
+ long startline = Ftell (file);
+ register long line1 = startline;
+ register long line2 = startline;
+ register long line3 = startline;
+ register int lncount;
+ int saveln, rv, rc;
+ regex_t re;
+
+ context.line = saveln = Currline;
+ context.chrctr = startline;
+ lncount = 0;
+ if ((rc = regcomp (&re, buf, REG_NOSUB)) != 0) {
+ char s[REGERR_BUF];
+ regerror (rc, &re, s, sizeof s);
+ more_error (s);
+ }
+ while (!feof (file)) {
+ line3 = line2;
+ line2 = line1;
+ line1 = Ftell (file);
+ rdline (file);
+ lncount++;
+ if ((rv = regexec (&re, Line, 0, NULL, 0)) == 0) {
+ if (--n == 0) {
+ if (lncount > 3 || (lncount > 1 && no_intty))
+ {
+ putchar('\n');
+ if (clreol)
+ cleareol ();
+ putsout(_("...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 ();
+ }
+ puts(Line);
+ }
+ break;
+ }
+ }
+ }
+ if (feof (file)) {
+ if (!no_intty) {
+ Currline = saveln;
+ Fseek (file, startline);
+ }
+ else {
+ putsout(_("\nPattern not found\n"));
+ end_it (0);
+ }
+ more_error (_("Pattern not found"));
+ free (previousre);
+ previousre = NULL;
+ }
+}
+
+/*VARARGS2*/
+void execute (char *filename, char *cmd, ...)
+{
+ int id;
+ int n;
+ va_list argp;
+ char * arg;
+ char ** args;
+ int argcount;
+
+ fflush (stdout);
+ reset_tty ();
+ for (n = 10; (id = fork ()) < 0 && n > 0; n--)
+ sleep (5);
+ if (id == 0) {
+ if (!isatty(0)) {
+ close(0);
+ open("/dev/tty", 0);
+ }
+
+ va_start(argp, cmd);
+ arg = va_arg(argp, char *);
+ argcount = 0;
+ while (arg) {
+ argcount++;
+ arg = va_arg(argp, char *);
+ }
+ va_end(argp);
+
+ args = alloca(sizeof(char *) * (argcount + 1));
+ args[argcount] = NULL;
+
+ va_start(argp, cmd);
+ arg = va_arg(argp, char *);
+ argcount = 0;
+ while (arg) {
+ args[argcount] = arg;
+ argcount++;
+ arg = va_arg(argp, char *);
+ }
+ va_end(argp);
+
+ execvp (cmd, args);
+ putserr(_("exec failed\n"));
+ exit (EXIT_FAILURE);
+ }
+ if (id > 0) {
+ signal (SIGINT, SIG_IGN);
+ signal (SIGQUIT, SIG_IGN);
+ if (catch_susp)
+ signal(SIGTSTP, SIG_DFL);
+ while (wait(0) > 0);
+ signal (SIGINT, end_it);
+ signal (SIGQUIT, onquit);
+ if (catch_susp)
+ signal(SIGTSTP, onsusp);
+ } else
+ putserr(_("can't fork\n"));
+ set_tty ();
+ puts("------------------------");
+ prompt (filename);
+}
+/*
+** Skip n lines in the file f
+*/
+
+void skiplns (register int 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.
+*/
+
+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;
+ puts(_("\n...Skipping "));
+ if (clreol)
+ cleareol ();
+ if (nskip > 0)
+ putsout(_("...Skipping to file "));
+ else
+ putsout(_("...Skipping back to file "));
+ puts(fnames[fnum]);
+ if (clreol)
+ cleareol ();
+ putchar('\n');
+ --fnum;
+}
+
+/*----------------------------- Terminal I/O -------------------------------*/
+
+void initterm()
+{
+ int ret;
+ char *padstr;
+ char *term;
+ struct winsize win;
+
+#ifdef do_SIGTTOU
+retry:
+#endif /* do_SIGTTOU */
+ no_tty = tcgetattr(fileno(stdout), &otty);
+ if (!no_tty) {
+ docrterase = (otty.c_cc[VERASE] != 255);
+ docrtkill = (otty.c_cc[VKILL] != 255);
+#ifdef do_SIGTTOU
+ {
+ int tgrp;
+ /*
+ * Wait until we're in the foreground before we save the
+ * the terminal modes.
+ */
+ if ((tgrp = tcgetpgrp(fileno(stdout))) < 0) {
+ perror("tcgetpgrp");
+ exit(EXIT_FAILURE);
+ }
+ if (tgrp != getpgrp(0)) {
+ kill(0, SIGTTOU);
+ goto retry;
+ }
+ }
+#endif /* do_SIGTTOU */
+ if ((term = getenv("TERM")) == 0) {
+ dumb++; ul_opt = 0;
+ }
+ my_setupterm(term, 1, &ret);
+ if (ret <= 0) {
+ dumb++; ul_opt = 0;
+ }
+ else {
+#ifdef TIOCGWINSZ
+ if (ioctl(fileno(stdout), TIOCGWINSZ, &win) < 0) {
+#endif /* TIOCGWINSZ */
+ Lpp = my_tgetnum(TERM_LINES);
+ Mcol = my_tgetnum(TERM_COLS);
+#ifdef TIOCGWINSZ
+ } else {
+ if ((Lpp = win.ws_row) == 0)
+ Lpp = my_tgetnum(TERM_LINES);
+ if ((Mcol = win.ws_col) == 0)
+ Mcol = my_tgetnum(TERM_COLS);
+ }
+#endif /* TIOCGWINSZ */
+ if ((Lpp <= 0) || my_tgetflag(TERM_HARD_COPY)) {
+ hard++; /* Hard copy terminal */
+ Lpp = LINES_PER_PAGE;
+ }
+
+ if (my_tgetflag(TERM_EAT_NEW_LINE))
+ eatnl++; /* Eat newline at last column + 1; dec, concept */
+ if (Mcol <= 0)
+ Mcol = NUM_COLUMNS;
+
+ Wrap = my_tgetflag(TERM_AUTO_RIGHT_MARGIN);
+ bad_so = my_tgetflag (TERM_CEOL);
+ eraseln = my_tgetstr(TERM_CLEAR_TO_LINE_END);
+ Clear = my_tgetstr(TERM_CLEAR);
+ Senter = my_tgetstr(TERM_STANDARD_MODE);
+ Sexit = my_tgetstr(TERM_EXIT_STANDARD_MODE);
+ if ((soglitch = my_tgetnum(TERM_STD_MODE_GLITCH)) < 0)
+ soglitch = 0;
+
+ /*
+ * Set up for underlining: some terminals don't need it;
+ * others have start/stop sequences, still others have an
+ * underline char sequence which is assumed to move the
+ * cursor forward one character. If underline sequence
+ * isn't available, settle for standout sequence.
+ */
+
+ if (my_tgetflag(TERM_UNDERLINE) || my_tgetflag(TERM_OVER_STRIKE))
+ ul_opt = 0;
+ if ((chUL = my_tgetstr(TERM_UNDERLINE_CHAR)) == NULL )
+ chUL = "";
+ if (((ULenter = my_tgetstr(TERM_ENTER_UNDERLINE)) == NULL ||
+ (ULexit = my_tgetstr(TERM_EXIT_UNDERLINE)) == NULL) && !*chUL) {
+ if ((ULenter = Senter) == NULL || (ULexit = Sexit) == NULL) {
+ ULenter = "";
+ ULexit = "";
+ } else
+ ulglitch = soglitch;
+ } else {
+ ulglitch = 0;
+ }
+
+ if ((padstr = my_tgetstr(TERM_PAD_CHAR)) != NULL)
+ PC = *padstr;
+ Home = my_tgetstr(TERM_HOME);
+ if (Home == 0 || *Home == '\0') {
+ if ((cursorm = my_tgetstr(TERM_CURSOR_ADDRESS)) != NULL) {
+ const char *t = (const char *)my_tgoto(cursorm, 0, 0);
+ xstrncpy(cursorhome, t, sizeof(cursorhome));
+ Home = cursorhome;
+ }
+ }
+ EodClr = my_tgetstr(TERM_CLEAR_TO_SCREEN_END);
+ if ((chBS = my_tgetstr(TERM_LINE_DOWN)) == NULL)
+ chBS = "\b";
+
+ }
+ if ((shell = getenv("SHELL")) == NULL)
+ shell = "/bin/sh";
+ }
+ no_intty = tcgetattr(fileno(stdin), &otty);
+ tcgetattr(fileno(stderr), &otty);
+ savetty0 = otty;
+ slow_tty = cfgetispeed(&otty) < B1200;
+ hardtabs = (otty.c_oflag & TABDLY) != XTABS;
+ if (!no_tty) {
+ otty.c_lflag &= ~(ICANON|ECHO);
+ otty.c_cc[VMIN] = 1;
+ otty.c_cc[VTIME] = 0;
+ }
+}
+
+int readch () {
+ unsigned char c;
+
+ errno = 0;
+ if (read (fileno(stderr), &c, 1) <= 0) {
+ if (errno != EINTR)
+ end_it(0);
+ else
+ c = otty.c_cc[VKILL];
+ }
+ return (c);
+}
+
+static char *BS = "\b";
+static char *BSB = "\b \b";
+static char *CARAT = "^";
+#define ERASEONECOLUMN \
+ if (docrterase) \
+ putserr(BSB); \
+ else \
+ putserr(BS);
+
+void ttyin (char buf[], register int nmax, char pchar) {
+ char *sp;
+ int c;
+ int slash = 0;
+ int maxlen;
+
+ sp = buf;
+ maxlen = 0;
+ while (sp - buf < nmax) {
+ if (promptlen > maxlen) maxlen = promptlen;
+ c = readch ();
+ if (c == '\\') {
+ slash++;
+ }
+ else if (((cc_t) c == otty.c_cc[VERASE]) && !slash) {
+ if (sp > buf) {
+#ifdef HAVE_WIDECHAR
+ if (MB_CUR_MAX > 1)
+ {
+ wchar_t wc;
+ size_t pos = 0, mblength;
+ mbstate_t state, state_bak;
+
+ memset (&state, '\0', sizeof (mbstate_t));
+
+ while (1) {
+ state_bak = state;
+ mblength = mbrtowc (&wc, buf + pos, sp - buf, &state);
+
+ state = (mblength == (size_t)-2
+ || mblength == (size_t)-1) ? state_bak : state;
+ mblength = (mblength == (size_t)-2
+ || mblength == (size_t)-1
+ || mblength == 0) ? 1 : mblength;
+
+ if (buf + pos + mblength >= sp)
+ break;
+
+ pos += mblength;
+ }
+
+ if (mblength == 1) {
+ ERASEONECOLUMN
+ }
+ else {
+ int wc_width;
+ wc_width = wcwidth (wc);
+ wc_width = (wc_width < 1) ? 1 : wc_width;
+ while (wc_width--) {
+ ERASEONECOLUMN
+ }
+ }
+
+ while (mblength--) {
+ --promptlen;
+ --sp;
+ }
+ }
+ else
+#endif /* HAVE_WIDECHAR */
+ {
+ --promptlen;
+ ERASEONECOLUMN
+ --sp;
+ }
+
+ if ((*sp < ' ' && *sp != '\n') || *sp == RUBOUT) {
+ --promptlen;
+ ERASEONECOLUMN
+ }
+ continue;
+ }
+ else {
+ if (!eraseln) promptlen = maxlen;
+ siglongjmp (restore, 1);
+ }
+ }
+ else if (((cc_t) c == otty.c_cc[VKILL]) && !slash) {
+ if (hard) {
+ show (c);
+ putchar ('\n');
+ putchar (pchar);
+ }
+ else {
+ putchar ('\r');
+ putchar (pchar);
+ if (eraseln)
+ erasep (1);
+ else if (docrtkill)
+ while (promptlen-- > 1)
+ putserr(BSB);
+ promptlen = 1;
+ }
+ sp = buf;
+ fflush (stdout);
+ continue;
+ }
+ if (slash && ((cc_t) c == otty.c_cc[VKILL]
+ || (cc_t) c == otty.c_cc[VERASE])) {
+ ERASEONECOLUMN
+ --sp;
+ }
+ if (c != '\\')
+ slash = 0;
+ *sp++ = c;
+ if ((c < ' ' && c != '\n' && c != ESC) || c == RUBOUT) {
+ c += (c == RUBOUT) ? -0100 : 0100;
+ putserr(CARAT);
+ promptlen++;
+ }
+ if (c != '\n' && c != ESC) {
+ putcerr(c);
+ promptlen++;
+ }
+ else
+ break;
+ }
+ *--sp = '\0';
+ if (!eraseln) promptlen = maxlen;
+ if (sp - buf >= nmax - 1)
+ more_error (_("Line too long"));
+}
+
+/* return: 0 - unchanged, 1 - changed, -1 - overflow (unchanged) */
+int expand (char **outbuf, char *inbuf) {
+ char *inpstr;
+ char *outstr;
+ char c;
+ char *temp;
+ int changed = 0;
+ int tempsz, xtra, offset;
+
+ xtra = strlen (fnames[fnum]) + strlen (shell_line) + 1;
+ tempsz = 200 + xtra;
+ temp = xmalloc(tempsz);
+ inpstr = inbuf;
+ outstr = temp;
+ while ((c = *inpstr++) != '\0'){
+ offset = outstr-temp;
+ if (tempsz-offset-1 < xtra) {
+ tempsz += 200 + xtra;
+ temp = xrealloc(temp, tempsz);
+ outstr = temp + offset;
+ }
+ switch (c) {
+ case '%':
+ if (!no_intty) {
+ strcpy (outstr, fnames[fnum]);
+ outstr += strlen (fnames[fnum]);
+ changed++;
+ } else
+ *outstr++ = c;
+ break;
+ case '!':
+ if (!shellp)
+ more_error (_("No previous command to substitute for"));
+ strcpy (outstr, shell_line);
+ outstr += strlen (shell_line);
+ changed++;
+ break;
+ case '\\':
+ if (*inpstr == '%' || *inpstr == '!') {
+ *outstr++ = *inpstr++;
+ break;
+ }
+ default:
+ *outstr++ = c;
+ }
+ }
+ *outstr++ = '\0';
+ *outbuf = temp;
+ return (changed);
+}
+
+void show (char c) {
+ if ((c < ' ' && c != '\n' && c != ESC) || c == RUBOUT) {
+ c += (c == RUBOUT) ? -0100 : 0100;
+ putserr(CARAT);
+ promptlen++;
+ }
+ putcerr(c);
+ promptlen++;
+}
+
+void more_error (char *mess)
+{
+ if (clreol)
+ cleareol ();
+ else
+ kill_line ();
+ promptlen += strlen (mess);
+ if (Senter && Sexit) {
+ my_putstring (Senter);
+ putsout(mess);
+ my_putstring (Sexit);
+ }
+ else
+ putsout(mess);
+ fflush(stdout);
+ errors++;
+ siglongjmp (restore, 1);
+}
+
+
+void set_tty () {
+ otty.c_lflag &= ~(ICANON|ECHO);
+ otty.c_cc[VMIN] = 1; /* read at least 1 char */
+ otty.c_cc[VTIME] = 0; /* no timeout */
+ stty(fileno(stderr), &otty);
+}
+
+static int
+ourputch(int c) {
+ return putc(c, stdout);
+}
+
+void
+reset_tty () {
+ if (no_tty)
+ return;
+ if (pstate) {
+ tputs(ULexit, fileno(stdout), ourputch); /* putchar - if that isn't a macro */
+ fflush(stdout);
+ pstate = 0;
+ }
+ otty.c_lflag |= ICANON|ECHO;
+ otty.c_cc[VMIN] = savetty0.c_cc[VMIN];
+ otty.c_cc[VTIME] = savetty0.c_cc[VTIME];
+ stty(fileno(stderr), &savetty0);
+}
+
+void rdline (register FILE *f)
+{
+ register int c;
+ register char *p;
+
+ prepare_line_buffer();
+
+ p = Line;
+ while ((c = Getc (f)) != '\n' && c != EOF && (size_t) (p - Line) < LineLen - 1)
+ *p++ = c;
+ if (c == '\n')
+ Currline++;
+ *p = '\0';
+}
+
+/* Come here when we get a suspend signal from the terminal */
+
+void onsusp (int dummy __attribute__ ((__unused__)))
+{
+ sigset_t signals, oldmask;
+
+ /* 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 */
+ signal(SIGTSTP, SIG_DFL);
+
+ /* unblock SIGTSTP or we won't be able to suspend ourself */
+ sigemptyset(&signals);
+ sigaddset(&signals, SIGTSTP);
+ sigprocmask(SIG_UNBLOCK, &signals, &oldmask);
+
+ kill (0, SIGTSTP);
+ /* Pause for station break */
+
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+
+ /* We're back */
+ signal (SIGTSTP, onsusp);
+ set_tty ();
+ if (inwait)
+ siglongjmp (restore, 1);
+}