1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27/*	  All Rights Reserved  	*/
28
29/*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
30/*	  All Rights Reserved	*/
31
32/*
33 * University Copyright- Copyright (c) 1982, 1986, 1988
34 * The Regents of the University of California
35 * All Rights Reserved
36 *
37 * University Acknowledgment- Portions of this document are derived from
38 * software developed by the University of California, Berkeley, and its
39 * contributors.
40 */
41
42/*
43 *      @(#) more.c 1.1 88/03/29 more:more.c
44 */
45
46/*
47** more.c - General purpose tty output filter and file perusal program
48**
49**      by Eric Shienbrood, UC Berkeley
50**
51**      modified by Geoff Peck, UCB to add underlining, single spacing
52**      modified by John Foderaro, UCB to add -c and MORE environment variable
53**      modified by Hans Spiller, Microsoft to handle \r better July 23, 82
54**              added ? help command, and -w
55**
56**      vwh     11 Jan 83       M001
57**              modified to handle x.out magic number and magic number
58**              byte ordering OTHER than the vax and pdp11.
59**      JJD     19 Jan 83       M002
60**              - fix distributed on USENET
61**                From decvax!ucbvax!dist2 Sun Dec  6 02:58:31 1981
62**                Subject: FIXED:  bug in src/more/more.c
63**              - fixed bug on terminal with "magic cookie" standout
64**                sequences.
65**      JJD     14 Feb 83       M003
66**              - fix exit status of more
67**              - Made first letter of "no more" message uppercase
68**      andyp   03 Aug 83       M004    3.0 upgrade
69**      - moved <local/uparm.h> to cmd/include.
70**      - use UCB, rather than XENIX, stty(2).
71**      andyp   30 Nov 83       M005
72**      - (thanks to reubenb).  Changed frame variable to static, it is
73**        used as a global buffer.  We never saw the bug before because
74**        of the depth of the stack.
75**      barrys  03 Jul 84       M006
76**      - Updated the usage message to include the 's' and 'w' options
77**        and to make the 'n' option a separate entry (uncommented).
78**      ericc   26 Dec 84       M007
79**      - Replaced the constant 0x7fffffffffffffffL with MAXLONG.
80**      ericc   25 Jul 85       M008
81**      - made "-r" option display control characters as '^x', as documented.
82**      - fixed processing of '\b' so that more doesn't terminate when
83**        the sequence "\b\n" is encountered.
84**      - changed "Hit Rubout ..." to "Hit Del ...", for ibm keyboards.
85**	davidby 9 March 1988	Unmarked
86**	- replaced all locally defined functions with library equivalents,
87**	- changed from termcap to terminfo
88**	- included <values.h> for MAXLONG value
89**	- removed most ifdef code for V6, V7, and BSD
90**	- added /etc/magic support for file type checking
91*/
92
93#include <ctype.h>
94#include <signal.h>
95#include <errno.h>
96#include <sys/types.h>
97#include <sys/wait.h>
98#include <curses.h>
99#include <term.h>
100#include <sys/ioctl.h>
101#include <setjmp.h>
102#include <sys/stat.h>
103#include <values.h>
104#include <stdlib.h>
105#include <stdarg.h>
106#include <string.h>
107#include <unistd.h>
108#include <libgen.h>
109#include <euc.h>
110#include <getwidth.h>
111#include <locale.h>
112#include <widec.h>
113#include <wctype.h>
114#include <limits.h>
115eucwidth_t wp;
116int     cw[4];
117int     scw[4];
118#include <locale.h>
119
120/* Help file will eventually go in libpath(more.help) on all systems */
121
122#ifdef INGRES
123#define VI              "/usr/bin/vi"
124#define HELPFILE        "/mntp/doucette/more/more.help"
125#define LOCAL_HELP	"/usr/lib/locale/%s/LC_MESSAGES/more.help"
126#endif
127
128#ifndef INGRES
129#ifndef HELPFILE
130#define HELPFILE        "/usr/lib/more.help"
131#define LOCAL_HELP	"/usr/lib/locale/%s/LC_MESSAGES/more.help"
132#endif
133#define VI              "vi"
134#endif
135
136#define Fopen(s,m)      (Currline = 0,file_pos=0,fopen(s,m))
137#define Ftell(f)        file_pos
138#define Fseek(f,off)    (file_pos=off,fseeko(f,off,0))
139#define Getc(f)         (++file_pos, getc(f))
140#define Ungetc(c,f)     (--file_pos, ungetc(c,f))
141
142#define pr(s1)		fputs(s1, stdout)
143#define clreos()	putp(clr_eos)
144#define cleareol()	putp(clr_eol)
145#define home()		putp(cursor_home)
146
147#define LINSIZ  512
148#define ctrl(letter)    ((letter) & 077)
149#define RUBOUT  '\177'
150#define ESC     '\033'
151#define QUIT    '\034'
152
153struct termio   otty;           /* old tty modes */
154struct termio   ntty;           /* new tty modes */
155off_t           file_pos, file_size;
156int             fnum, no_intty, no_tty;
157int             dum_opt;
158off_t           dlines;
159void end_it(int sig);
160void onquit(int sig);
161void chgwinsz(int sig);
162#ifdef SIGTSTP
163void             onsusp(int sig);
164#endif
165int             nscroll = 11;   /* Number of lines scrolled by 'd' */
166int             fold_opt = 1;   /* Fold long lines */
167int             stop_opt = 1;   /* Stop after form feeds */
168int             ssp_opt = 0;    /* Suppress white space */
169int             ul_opt = 1;     /* Underline as best we can */
170int             cr_opt = 0;     /* show ctrl characters as '^c' */
171int             wait_opt = 0;   /* prompt for exit at eof */
172int             promptlen;
173off_t		Currline;       /* Line we are currently at */
174int             startup = 1;
175int             firstf = 1;
176int             notell = 1;
177int             inwait, Pause, errors;
178int             within; /* true if we are within a file,
179                        false if we are between files */
180int             hard, dumb, noscroll, hardtabs, clreol;
181int             catch_susp;     /* We should catch the SIGTSTP signal */
182char            **fnames;       /* The list of file names */
183int             nfiles;         /* Number of files left to process */
184char            *shell;         /* The name of the shell to use */
185int             shellp;         /* A previous shell command exists */
186char            ch;
187jmp_buf         restore;
188char            obuf[BUFSIZ];   /* stdout buffer */
189char            Line[LINSIZ];   /* Line buffer */
190int             Lpp = 24;       /* lines per page */
191char            *ULenter, *ULexit;      /* enter and exit underline mode */
192int             Mcol = 80;      /* number of columns */
193int             Wrap = 1;       /* set if automargins */
194int		fseeko();
195struct {
196    off_t chrctr, line;
197} context, screen_start;
198int             exitstat = 0;   /* status to use when exiting more */   /*M003*/
199
200static void execute(char *filename, char *cmd, ...);
201static void error(char *mess);
202static void wait_eof(void);
203static void prompt(char *filename);
204static void argscan(char *s);
205static void copy_file(register FILE *f);
206static void initterm(void);
207static void do_shell(char *filename);
208static FILE *checkf(register char *fs, int *clearfirst);
209static void screen(register FILE *f, register off_t num_lines);
210static void skiplns(register off_t n, register FILE *f);
211static void skipf(register int nskip);
212static int readch(void);
213static void prmpt_erase(register int col);
214static void kill_line(void);
215static void prbuf(register char *s, register int n);
216static void search(char buf[], FILE *file, register off_t n);
217static void doclear(void);
218static void ttyin(char buf[], register int nmax, char pchar);
219static int expand(char *outbuf, char *inbuf);
220static void show(register char ch);
221static void set_tty(void);
222static void reset_tty(void);
223static void rdline(register FILE *f);
224static off_t command(char *filename, register FILE *f);
225static int getaline(register FILE *f, int *length);
226static int number(char *cmd);
227static int colon(char *filename, int cmd, off_t nlines);
228
229int
230main(int argc, char *argv[])
231{
232    register FILE       *f;
233    register char       *s;
234    register char       *p;
235    register int	ch;
236    register off_t      left;
237    int                 prnames = 0;
238    int                 initopt = 0;
239    int                 srchopt = 0;
240    int                 clearit = 0;
241    off_t               initline;
242    char                initbuf[80];
243
244    setlocale( LC_ALL, "" );
245    getwidth(&wp);
246    cw[0] = 1;
247    cw[1] = wp._eucw1;
248    cw[2] = wp._eucw2+1;
249    cw[3] = wp._eucw3+1;
250    scw[0] = 1;
251    scw[1] = wp._scrw1;
252    scw[2] = wp._scrw2;
253    scw[3] = wp._scrw3;
254
255    nfiles = argc;
256    fnames = argv;
257
258    (void) setlocale(LC_ALL,"");
259#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
260#define TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
261#endif
262	(void) textdomain(TEXT_DOMAIN);
263
264    initterm ();
265    if(s = getenv("MORE")) argscan(s);
266    while (--nfiles > 0) {
267        if ((ch = (*++fnames)[0]) == '-') {
268            argscan(*fnames+1);
269        }
270        else if (ch == '+') {
271            s = *fnames;
272            if (*++s == '/') {
273                srchopt++;
274                for (++s, p = initbuf; p < initbuf + 79 && *s != '\0';)
275                    *p++ = *s++;
276                *p = '\0';
277            }
278            else {
279                initopt++;
280                for (initline = 0; *s != '\0'; s++)
281                    if (isdigit (*s))
282                        initline = initline*10 + *s -'0';
283                --initline;
284            }
285        }
286        else break;
287    }
288    /* allow clreol only if cursor_home and clr_eol and clr_eos strings are
289     *  defined, and in that case, make sure we are in noscroll mode
290     */
291    if(clreol)
292    {
293        if (!cursor_home || !clr_eol || !clr_eos) {
294           	clreol = 0;
295	}
296        else noscroll = 1;
297    }
298
299    if (dlines == 0)
300        dlines =(off_t) (Lpp - (noscroll ? 1 : 2));
301    left = dlines;
302    if (nfiles > 1)
303        prnames++;
304    if (!no_intty && nfiles == 0) {
305	fprintf(stderr, gettext("Usage: %s\
306 [-cdflrsuw] [-lines] [+linenumber] [+/pattern] [filename ...].\n")
307	, argv[0]);
308        exit(1);
309    }
310    else
311        f = stdin;
312    if (!no_tty) {
313        signal(SIGQUIT, onquit);
314        signal(SIGINT, end_it);
315        signal(SIGWINCH, chgwinsz);
316#ifdef SIGTSTP
317        if (signal (SIGTSTP, SIG_IGN) == SIG_DFL) {
318            signal(SIGTSTP, onsusp);
319            catch_susp++;
320        }
321#endif
322        set_tty();
323    }
324    if (no_intty) {
325        if (no_tty)
326            copy_file (stdin);
327        else {
328            if ((ch = Getc (f)) == '\f')
329                doclear();
330            else {
331                Ungetc (ch, f);
332                if (noscroll && (ch != EOF)) {
333                    if (clreol)
334                        home ();
335                    else
336                        doclear ();
337                }
338            }
339            if (!setjmp(restore)) {
340                if (srchopt) {
341                    search (initbuf, stdin,(off_t) 1);
342                    if (noscroll)
343                        left--;
344                    }
345                else if (initopt)
346                    skiplns (initline, stdin);
347                }
348            else
349                left = command(NULL, f);
350            screen (stdin, left);
351        }
352        no_intty = 0;
353        prnames++;
354        firstf = 0;
355    }
356
357    while (fnum < nfiles) {
358        if ((f = checkf (fnames[fnum], &clearit)) != NULL) {
359            context.line = context.chrctr = 0;
360            Currline = 0;
361            if (firstf) setjmp (restore);
362            if (firstf) {
363                firstf = 0;
364                if (srchopt)
365                {
366                    search (initbuf, f,(off_t) 1);
367                    if (noscroll)
368                        left--;
369                }
370                else if (initopt)
371                    skiplns (initline, f);
372            }
373            else if (fnum < nfiles && !no_tty) {
374                setjmp (restore);
375                left = command (fnames[fnum], f);
376            }
377            if (left != 0) {
378                if ((noscroll || clearit) && (file_size != LLONG_MAX))
379                    if (clreol)
380                        home ();
381                    else
382                        doclear ();
383                if (prnames) {
384                    if (ceol_standout_glitch)
385                        prmpt_erase (0);
386                    if (clreol)
387                        cleareol ();
388                    pr("::::::::::::::");
389                    if (promptlen > 14)
390                        prmpt_erase (14);
391                    printf ("\n");
392                    if(clreol) cleareol();
393                    printf("%s\n", fnames[fnum]);
394                    if(clreol) cleareol();
395                    pr("::::::::::::::\n");
396                    if (left > (off_t)(Lpp - 4))
397                        left =(off_t)(Lpp - 4);
398                }
399                if (no_tty)
400                    copy_file (f);
401                else {
402                    within++;
403                    screen(f, left);
404                    within = 0;
405                }
406            }
407            setjmp (restore);
408            fflush(stdout);
409            fclose(f);
410            screen_start.line = screen_start.chrctr = 0LL;
411            context.line = context.chrctr = 0LL;
412        } else
413            exitstat |= 1;                      /*M003*/
414        fnum++;
415        firstf = 0;
416    }
417    if (wait_opt) wait_eof();
418    reset_tty ();
419    return (exitstat);                             /*M003*/
420}
421
422static void
423argscan(char *s)
424{
425            for (dlines = 0; *s != '\0'; s++)
426                if (isdigit(*s))
427                    dlines = dlines*10 + *s - '0';
428                else if (*s == 'd')
429                    dum_opt = 1;
430                else if (*s == 'l')
431                    stop_opt = 0;
432                else if (*s == 'f')
433                    fold_opt = 0;
434                else if (*s == 'p')
435                    noscroll++;
436                else if (*s == 'c')
437                    clreol++;
438                else if (*s == 's')
439                    ssp_opt = 1;
440                else if (*s == 'u')
441                    ul_opt = 0;
442                else if (*s == 'r')
443                    cr_opt = 1;
444                else if (*s == 'w')
445                    wait_opt = 1;
446}
447
448
449/*
450** Check whether the file named by fs is a file which the user may
451** access.  If it is, return the opened file. Otherwise return NULL.
452*/
453
454static FILE *
455checkf(register char *fs, int *clearfirst)
456{
457    struct stat stbuf;
458    register FILE *f;
459    int c;
460
461    if (stat (fs, &stbuf) == -1) {
462        fflush(stdout);
463        if (clreol)
464            cleareol ();
465        perror(fs);
466        return (NULL);
467    }
468    if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
469        printf(gettext("\n*** %s: directory ***\n\n"), fs);
470        return (NULL);
471    }
472    if ((f=Fopen(fs, "r")) == NULL) {
473        fflush(stdout);
474        perror(fs);
475        return (NULL);
476    }
477
478    if ((c = Getc(f)) == '\f')                  /* end M001 */
479        *clearfirst = 1;
480    else {
481        *clearfirst = 0;
482        Ungetc (c, f);
483    }
484    if ((file_size = (off_t)stbuf.st_size) == 0)
485        file_size = LLONG_MAX;
486    return (f);
487}
488
489/*
490** Print out the contents of the file f, one screenful at a time.
491*/
492
493#define STOP -10
494
495static void
496screen(register FILE *f, register off_t num_lines)
497{
498    register int c;
499    register int nchars;
500    int length;                 /* length of current line */
501    static int prev_len = 1;    /* length of previous line */
502
503    for (;;) {
504        while (num_lines > 0 && !Pause) {
505            if ((nchars = getaline (f, &length)) == EOF)
506            {
507                if (clreol) clreos();
508                return;
509            }
510            if (ssp_opt && length == 0 && prev_len == 0)
511                continue;
512            prev_len = length;
513            if (ceol_standout_glitch ||
514		(enter_standout_mode && *enter_standout_mode == ' ')
515		&& promptlen > 0)
516                prmpt_erase (0);
517            /* must clear before drawing line since tabs on some terminals
518             * do not erase what they tab over.
519             */
520            if (clreol)
521                cleareol ();
522            prbuf (Line, length);
523            if (nchars < promptlen)
524                prmpt_erase (nchars); /* prmpt_erase () sets promptlen to 0 */
525            else promptlen = 0;
526            /* is this needed?
527             * if (clreol)
528             *  cleareol(); */    /* must clear again in case we wrapped */
529
530            if (nchars < Mcol || !fold_opt)
531                putchar('\n');
532            if (nchars == STOP)
533                break;
534            num_lines--;
535        }
536        fflush(stdout);
537        if ((c = Getc(f)) == EOF)
538        {
539            if (clreol) clreos ();
540            return;
541        }
542
543        if (Pause && clreol)
544            clreos ();
545        Ungetc (c, f);
546        setjmp (restore);
547        Pause = 0; startup = 0;
548        if ((num_lines = command (NULL, f)) == 0)
549            return;
550        if (hard && promptlen > 0)
551                prmpt_erase (0);
552        if (noscroll && num_lines == dlines) {
553            if (clreol)
554                home();
555            else
556                doclear ();
557        }
558        screen_start.line = Currline;
559        screen_start.chrctr = Ftell (f);
560    }
561}
562
563/*
564** Come here if a quit signal is received
565*/
566/*
567 * sig is put in as a dummy arg to have the compiler not to complain
568 */
569
570/* ARGSUSED */
571void
572onquit(int sig)
573{
574    signal(SIGQUIT, SIG_IGN);
575    if (!inwait) {
576        putchar ('\n');
577        if (!startup) {
578            signal(SIGQUIT, onquit);
579            longjmp (restore, 1);
580        }
581        else
582            Pause++;
583    }
584    else if (!dum_opt && notell) {
585        write (2, gettext("[Use q or Q to quit]"), 20);
586        promptlen += 20;
587        notell = 0;
588    }
589    signal(SIGQUIT, onquit);
590}
591
592/*
593** Come here if a signal for a window size change is received
594*/
595/*ARGSUSED*/
596void
597chgwinsz(int sig)
598{
599    struct winsize win;
600
601    (void) signal(SIGWINCH, SIG_IGN);
602    if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) {
603	if (win.ws_row != 0) {
604	    Lpp = win.ws_row;
605	    nscroll = Lpp/2 - 1;
606	    if (nscroll <= 0)
607		nscroll = 1;
608	    dlines = (off_t)(Lpp - (noscroll ? 1 : 2));
609	}
610	if (win.ws_col != 0)
611	    Mcol = win.ws_col;
612    }
613    (void) signal(SIGWINCH, chgwinsz);
614}
615
616/*
617** Clean up terminal state and exit. Also come here if interrupt signal received
618*/
619
620/*
621 * sig is put in as a dummy arg to have the compiler not to complain
622 */
623
624/* ARGSUSED */
625void
626end_it(int sig)
627{
628
629    reset_tty ();
630    if (clreol) {
631        putchar ('\r');
632        clreos ();
633        fflush (stdout);
634    }
635    else if (!clreol && (promptlen > 0)) {
636        kill_line ();
637        fflush (stdout);
638    }
639    else
640        write (2, "\n", 1);
641    _exit(exitstat);                    /*M003*/
642}
643
644static void
645copy_file(register FILE *f)
646{
647    register int c;
648
649    while ((c = getc(f)) != EOF)
650        putchar(c);
651}
652
653static char Bell = ctrl('G');
654
655
656/* See whether the last component of the path name "path" is equal to the
657** string "string"
658*/
659
660int
661tailequ(char *path, char *string)
662{
663	return (!strcmp(basename(path), string));
664}
665
666static void
667prompt(char *filename)
668{
669    if (clreol)
670        cleareol ();
671    else if (promptlen > 0)
672        kill_line ();
673    if (!hard) {
674        promptlen = 8;
675        if (enter_standout_mode && exit_standout_mode)
676            putp (enter_standout_mode);
677        if (clreol)
678            cleareol ();
679        pr(gettext("--More--"));
680        if (filename != NULL) {
681            promptlen += printf (gettext("(Next file: %s)"), filename);
682        }
683        else if (!no_intty) {
684            promptlen += printf ("(%d%%)", (int)((file_pos * 100) / file_size));
685        }
686        if (dum_opt) {
687            promptlen += pr(gettext("[Hit space to continue, Del to abort]"));
688        }
689        if (enter_standout_mode && exit_standout_mode)
690            putp (exit_standout_mode);
691        if (clreol) clreos ();
692        fflush(stdout);
693    }
694    else
695        write (2, &Bell, 1);
696    inwait++;
697}
698
699/*
700 * when run from another program or a shell script, it is
701 * sometimes useful to prevent the next program from scrolling
702 * us off the screen before we get a chance to read this page.
703 *                      -Hans, July 24, 1982
704 */
705static void
706wait_eof(void)
707{
708        if (enter_standout_mode && exit_standout_mode)
709                putp (enter_standout_mode);
710        promptlen = pr(gettext("--No more--"));          /*M003*/
711        if (dum_opt)
712                promptlen += pr(gettext("[Hit any key to continue]"));
713        if (enter_standout_mode && exit_standout_mode)
714                putp(exit_standout_mode);
715        if (clreol) clreos();
716        fflush(stdout);
717        readch();
718        prmpt_erase (0);
719        fflush(stdout);
720}
721
722/*
723** Get a logical line
724*/
725
726static int
727getaline(register FILE *f, int *length)
728{
729    register int        c;
730    register char       *p;
731    register int        column;
732    static int          colflg;
733    register int        oldcolumn;
734    int			csno;
735
736    p = Line;
737    column = 0;
738    oldcolumn = 0;
739    c = Getc (f);
740    if (colflg && c == '\n') {
741        Currline++;
742        c = Getc (f);
743    }
744    while (p < &Line[LINSIZ - 1]) {
745	csno = csetno(c);
746        if (c == EOF) {
747            if (p > Line) {
748                *p = '\0';
749                *length = p - Line;
750                return (column);
751            }
752            *length = p - Line;
753            return (EOF);
754        }
755	if (!csno) {
756	        if (c == '\n') {
757	            /* detect \r\n.  -Hans */
758	            if (p>Line && p[-1] == '\r') {
759	                column = oldcolumn;
760	                p--;
761	            }
762	            Currline++;
763	            break;
764	        }
765	        *p++ = c;
766	        if (c == '\t')
767	            if (hardtabs && column < promptlen && !hard) {
768	                if (clr_eol && !dumb) {
769	                    column = 1 + (column | 7);
770	                    putp (clr_eol);
771	                    promptlen = 0;
772	                }
773	                else {
774	                    for (--p; column & 7 && p < &Line[LINSIZ - 1]; column++) {
775	                        *p++ = ' ';
776	                    }
777	                    if (column >= promptlen) promptlen = 0;
778	                }
779	            }
780	            else
781	                column = 1 + (column | 7);
782	        else if ((c == '\b') && (ul_opt || !cr_opt) && (column > 0))  /* M008 */
783	                column--;
784
785	        /* this is sort of strange.  what was here before was that
786	           \r always set column to zero, and the hack above to
787	           detect \r\n didnt exist.  the net effect is to make
788	           the current line be overwritten by the prompt if it
789	           had a \r at the end, and the line start after the \r
790	           otherwise.  I suppose this is useful for overstriking
791	           on hard copy terminals, but not on anything glass
792	           -Hans */
793
794	        else if ((c == '\r') && !cr_opt) {
795	                oldcolumn = column;
796	                column = 0;
797	        }
798	        else if (c == '\f' && stop_opt) {
799	                p[-1] = '^';
800	                *p++ = 'L';
801	                column += 2;
802	                Pause++;
803	        }
804	        else if (c == EOF) {
805	            *length = p - Line;
806	            return (column);
807	        }
808	        else if (c < ' ' && cr_opt){                    /* M008 begin */
809	                p[-1] = '^';
810	                *p++ = c | ('A' - 1);
811	                column += 2;
812	        }                                               /* M008 end */
813	        else if (c >= ' ' && c != RUBOUT)
814	            column++;
815	} /* end of code set 0 */
816	else {
817		column += scw[csno];
818		if ( column > Mcol && fold_opt ) {
819		    column -= scw[csno];
820		    while ( column < Mcol ) {
821		        column++;
822		        *p++ = ' ';
823		    }
824		    column = Mcol;
825		    Ungetc(c,f);
826		} else {
827		    int i;
828		    *p++ = c;
829		    for(i=1; i<cw[csno];i++)
830			*p++ = Getc(f);
831		}
832	} /* end of codeset 1 ~ 3 */
833        if (column >= Mcol && fold_opt) break;
834        c = Getc (f);
835    }
836    if (column >= Mcol && Mcol > 0) {
837        if (!Wrap) {
838            *p++ = '\n';
839        }
840    }
841    colflg = column == Mcol && fold_opt;
842    if (colflg && eat_newline_glitch && Wrap) {
843	*p++ = '\n'; /* simulate normal wrap */
844    }
845    *length = p - Line;
846    *p = 0;
847    return (column);
848}
849
850/*
851** Erase the rest of the prompt, assuming we are starting at column col.
852*/
853
854static void
855prmpt_erase(register int col)
856{
857
858    if (promptlen == 0)
859        return;
860    if (hard) {
861        putchar ('\n');
862    }
863    else {
864        if (col == 0)
865            putchar ('\r');
866        if (!dumb && clr_eol)
867            putp (clr_eol);
868        else
869            for (col = promptlen - col; col > 0; col--)
870                putchar (' ');
871    }
872    promptlen = 0;
873}
874
875/*
876** Erase the current line entirely
877*/
878
879static void
880kill_line(void)
881{
882    prmpt_erase (0);
883    if (!clr_eol || dumb) putchar ('\r');
884}
885
886/* Print a buffer of n characters */
887
888static void
889prbuf(register char *s, register int n)
890{
891    char c;                             /* next ouput character */
892    register int state = 0;             /* next output char's UL state */
893    static int pstate = 0;              /* current terminal UL state (off) */
894
895    while (--n >= 0)
896        if (!ul_opt)
897            putchar (*s++);
898        else {
899            if (n >= 2 && s[0] == '_' && s[1] == '\b') {
900                n -= 2;
901                s += 2;
902                c = *s++;
903                state = 1;
904            } else if (n >= 2 && s[1] == '\b' && s[2] == '_') {
905                n -= 2;
906                c = *s++;
907                s += 2;
908                state = 1;
909            } else {
910                c = *s++;
911                state = 0;
912            }
913            if (state != pstate)
914                putp(state ? ULenter : ULexit);
915            pstate = state;
916            putchar(c);
917            if (state && underline_char) {
918                putp(cursor_left);
919                putp(underline_char);
920            }
921        }
922    /*
923     * M002
924     * You don't want to stay in standout mode at the end of the line;
925     * on some terminals, this will leave all of the remaining blank
926     * space on the line in standout mode.
927     */
928    if (state && !underline_char) {                       /*M002*/
929            putp(ULexit);                    /*M002*/
930            pstate = 0;                                 /*M002*/
931    }                                                   /*M002*/
932}
933
934/*
935**  Clear the screen
936*/
937
938static void
939doclear(void)
940{
941    if (clear_screen && !hard) {
942        putp(clear_screen);
943
944        /* Put out carriage return so that system doesn't
945        ** get confused by escape sequences when expanding tabs
946        */
947        putchar ('\r');
948        promptlen = 0;
949    }
950}
951
952
953static int lastcmd, lastp;
954static off_t lastarg;
955static int lastcolon;
956char shell_line[PATH_MAX];
957
958/*
959** Read a command and do it. A command consists of an optional integer
960** argument followed by the command character.  Return the number of lines
961** to display in the next screenful.  If there is nothing more to display
962** in the current file, zero is returned.
963*/
964
965static off_t
966command(char *filename, register FILE *f)
967{
968    register off_t nlines;
969    register off_t retval;
970    register int c;
971    char colonch;
972    FILE *helpf;
973    int done;
974    char comchar, cmdbuf[80];
975    char filebuf[128];
976    char *loc;
977
978#define ret(val) retval=val;done++;break
979
980    done = 0;
981    if (!errors)
982        prompt (filename);
983    else
984        errors = 0;
985    for (;;) {
986        nlines = number (&comchar);
987        lastp = colonch = 0;
988        if (comchar == '.') {   /* Repeat last command */
989                lastp++;
990                comchar = lastcmd;
991                nlines = lastarg;
992                if (lastcmd == ':')
993                        colonch = lastcolon;
994        }
995        lastcmd = comchar;
996        lastarg = nlines;
997	if((comchar != RUBOUT) && !dum_opt) {
998        	if (comchar == otty.c_cc[VERASE]) {
999           		 kill_line ();
1000            		prompt (filename);
1001            		continue;
1002        	}
1003	}
1004        switch (comchar) {
1005        case ':':
1006            retval = colon (filename, colonch, nlines);
1007            if (retval >= 0)
1008                done++;
1009            break;
1010	case 'b':
1011	case ctrl('B'):
1012	    {
1013		register off_t initline;
1014
1015		if (no_intty) {
1016		    write(2, &bell, 1);
1017		    return (-1);
1018		}
1019
1020		if (nlines == 0) nlines++;
1021
1022		putchar ('\r');
1023		prmpt_erase (0);
1024		printf ("\n");
1025		if (clreol)
1026			cleareol ();
1027		printf (gettext("...back %lld page"), nlines);
1028		if (nlines > 1)
1029			pr ("s\n");
1030		else
1031			pr ("\n");
1032
1033		if (clreol)
1034			cleareol ();
1035		pr ("\n");
1036
1037		initline = Currline - dlines * (nlines + 1);
1038		if (! noscroll)
1039		    --initline;
1040		if (initline < 0) initline = 0;
1041		Fseek(f, 0LL);
1042		Currline = 0;	/* skiplns() will make Currline correct */
1043		skiplns(initline, f);
1044		if (! noscroll) {
1045		    ret(dlines + 1);
1046		}
1047		else {
1048		    ret(dlines);
1049		}
1050	    }
1051        case ' ':
1052        case 'z':
1053            if (nlines == 0) nlines = dlines;
1054            else if (comchar == 'z') dlines = nlines;
1055            ret (nlines);
1056        case 'd':
1057        case ctrl('D'):
1058            if (nlines != 0) nscroll = nlines;
1059            ret (nscroll);
1060        case RUBOUT:
1061        case 'q':
1062        case 'Q':
1063            end_it(0);
1064	    /*NOTREACHED*/
1065        case 's':
1066        case 'f':
1067            if (nlines == 0) nlines++;
1068            if (comchar == 'f')
1069                nlines *= dlines;
1070            putchar ('\r');
1071            prmpt_erase (0);
1072            printf ("\n");
1073            if (clreol)
1074                cleareol ();
1075            printf (gettext("...skipping %lld line"), nlines);
1076            if (nlines > 1)
1077                pr ("s\n");
1078            else
1079                pr ("\n");
1080
1081            if (clreol)
1082                cleareol ();
1083            pr ("\n");
1084
1085            while (nlines > 0) {
1086                while ((c = Getc (f)) != '\n')
1087                    if (c == EOF) {
1088                        retval = 0;
1089                        done++;
1090                        goto endsw;
1091                    }
1092                Currline++;
1093                nlines--;
1094            }
1095            ret (dlines);
1096        case '\n':
1097	    if (nlines != 0)
1098                dlines = nlines;
1099            else
1100                nlines = 1;
1101            ret (nlines);
1102        case '\f':
1103            if (!no_intty) {
1104                doclear ();
1105                Fseek (f, screen_start.chrctr);
1106                Currline = screen_start.line;
1107                ret (dlines);
1108            }
1109            else {
1110                write (2, &Bell, 1);
1111                break;
1112            }
1113        case '\'':
1114            if (!no_intty) {
1115                kill_line ();
1116                pr (gettext("\n***Back***\n\n"));
1117                Fseek (f, context.chrctr);
1118                Currline = context.line;
1119                ret (dlines);
1120            }
1121            else {
1122                write (2, &Bell, 1);
1123                break;
1124            }
1125        case '=':
1126            kill_line ();
1127            promptlen = printf ("%lld", Currline);
1128            fflush (stdout);
1129            break;
1130        case 'n':
1131            lastp++;
1132	    /*FALLTHROUGH*/
1133        case '/':
1134            if (nlines == 0) nlines++;
1135            kill_line ();
1136            pr ("/");
1137            promptlen = 1;
1138            fflush (stdout);
1139            if (lastp) {
1140                write (2,"\r", 1);
1141                search (NULL, f, nlines);       /* Use previous r.e. */
1142            }
1143            else {
1144                ttyin (cmdbuf, 78, '/');
1145                write (2, "\r", 1);
1146                search (cmdbuf, f, nlines);
1147            }
1148            ret (dlines-1);
1149        case '!':
1150            do_shell (filename);
1151            break;
1152        case 'h':
1153        case '?':
1154	    /*
1155	     * First get local help file.
1156	     */
1157	    loc = setlocale(LC_MESSAGES, 0);
1158	    sprintf(filebuf, LOCAL_HELP, loc);
1159
1160            if  ((strcmp(loc, "C") == 0) || (helpf = fopen (filebuf, "r")) == NULL) {
1161		    if  ((helpf = fopen (HELPFILE, "r")) == NULL)
1162			error (gettext("Can't open help file"));
1163	    }
1164            if (noscroll) doclear ();
1165            copy_file (helpf);
1166            fclose (helpf);
1167            prompt (filename);
1168            break;
1169        case 'v':       /* This case should go right before default */
1170            if (!no_intty) {
1171                kill_line ();
1172                cmdbuf[0] = '+';
1173                sprintf(&cmdbuf[1], "%lld", Currline);
1174                pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]);
1175                execute (filename, VI, "vi", cmdbuf, fnames[fnum], 0);
1176                break;
1177            }
1178        default:
1179		if (dum_opt) {
1180			kill_line ();
1181		    	promptlen = pr(gettext("[Press 'h' for instructions.]"));
1182			fflush (stdout);
1183	    	}
1184	    	else
1185            		write (2, &Bell, 1);
1186            break;
1187        }
1188        if (done) break;
1189    }
1190    putchar ('\r');
1191endsw:
1192    inwait = 0;
1193    notell++;
1194    return (retval);
1195}
1196
1197char ch;
1198
1199/*
1200 * Execute a colon-prefixed command.
1201 * Returns <0 if not a command that should cause
1202 * more of the file to be printed.
1203 */
1204
1205static int
1206colon(char *filename, int cmd, off_t nlines)
1207{
1208        if (cmd == 0)
1209                ch = readch ();
1210        else
1211                ch = cmd;
1212        lastcolon = ch;
1213        switch (ch) {
1214        case 'f':
1215                kill_line ();
1216                if (!no_intty)
1217                        promptlen = printf (gettext("\"%s\" line %lld"),
1218					    fnames[fnum], Currline);
1219                else
1220                        promptlen = printf(
1221			 gettext("[Not a file] line %lld"), Currline);
1222                fflush (stdout);
1223                return (-1);
1224        case 'n':
1225                if (nlines == 0) {
1226                        if (fnum >= nfiles - 1)
1227                                end_it(0);
1228                        nlines++;
1229                }
1230                putchar ('\r');
1231                prmpt_erase (0);
1232                skipf ((int)nlines);
1233                return (0);
1234        case 'p':
1235                if (no_intty) {
1236                        write (2, &Bell, 1);
1237                        return (-1);
1238                }
1239                putchar ('\r');
1240                prmpt_erase (0);
1241                if (nlines == 0)
1242                        nlines++;
1243                skipf ((int)-nlines);
1244                return (0);
1245        case '!':
1246                do_shell (filename);
1247                return (-1);
1248        case 'q':
1249        case 'Q':
1250                end_it(0);
1251        default:
1252                write (2, &Bell, 1);
1253                return (-1);
1254        }
1255}
1256
1257/*
1258** Read a decimal number from the terminal. Set cmd to the non-digit which
1259** terminates the number.
1260*/
1261
1262static int
1263number(char *cmd)
1264{
1265        register int i;
1266
1267        i = 0; ch = otty.c_cc[VKILL];
1268        for (;;) {
1269                ch = readch ();
1270                if (ch >= '0' && ch <= '9') {
1271                        i = i*10 + ch - '0';
1272                } else if (ch == RUBOUT) {
1273                        i = 0;
1274                        *cmd = ch;
1275                        break;
1276                } else if (ch == otty.c_cc[VKILL]) {
1277                        i = 0;
1278                } else {
1279                        *cmd = ch;
1280                        break;
1281                }
1282        }
1283        return (i);
1284}
1285
1286static void
1287do_shell(char *filename)
1288{
1289        char cmdbuf[80];
1290
1291        kill_line ();
1292        pr ("!");
1293        fflush (stdout);
1294        promptlen = 1;
1295        if (lastp)
1296                pr (shell_line);
1297        else {
1298                ttyin (cmdbuf, 78, '!');
1299                if (expand (shell_line, cmdbuf)) {
1300                        kill_line ();
1301                        promptlen = printf ("!%s", shell_line);
1302                }
1303        }
1304        fflush (stdout);
1305        write (2, "\n", 1);
1306        promptlen = 0;
1307        shellp = 1;
1308        execute (filename, shell, shell, "-c", shell_line, 0);
1309}
1310
1311/*
1312** Search for nth ocurrence of regular expression contained in buf in the file
1313*/
1314
1315static void
1316search(char buf[], FILE *file, register off_t n)
1317{
1318    off_t startline = Ftell (file);
1319    register off_t line1 = startline;
1320    register off_t line2 = startline;
1321    register off_t line3 = startline;
1322    register off_t lncount;
1323    off_t saveln;
1324    static char *s = NULL;
1325    static char lastbuf[80];
1326
1327    if (buf != NULL) {
1328	if (s != NULL)
1329		free(s);
1330	if (*buf != '\0') {
1331		if ((s = regcmp(buf, (char *) NULL)) == NULL)
1332			error(gettext("Regular expression botch"));
1333		else
1334			strcpy(lastbuf, buf);
1335	} else {
1336		if ((s = regcmp(lastbuf, (char *) NULL)) == NULL)
1337			error(gettext("No previous regular expression"));
1338	}
1339    } else {
1340	if (s == NULL)
1341	    error(gettext("No previous regular expression"));
1342    }
1343    context.line = saveln = Currline;
1344    context.chrctr = startline;
1345    lncount = 0;
1346    while (!feof (file)) {
1347        line3 = line2;
1348        line2 = line1;
1349        line1 = Ftell (file);
1350        rdline (file);
1351        lncount++;
1352        if (regex(s, Line) != NULL)
1353                if (--n == 0) {
1354                    if (lncount > 3 || (lncount > 1 && no_intty))
1355                    {
1356                        pr ("\n");
1357                        if (clreol)
1358                            cleareol ();
1359                        pr(gettext("...skipping\n"));
1360                    }
1361                    if (!no_intty) {
1362                        Currline -= (lncount >= 3 ? 3 : lncount);
1363                        Fseek (file, line3);
1364                        if (noscroll)
1365                            if (clreol) {
1366                                home ();
1367                                cleareol ();
1368                            }
1369                            else
1370                                doclear ();
1371                    }
1372                    else {
1373                        kill_line ();
1374                        if (noscroll)
1375                            if (clreol) {
1376                                home ();
1377                                cleareol ();
1378                            } else
1379                                doclear ();
1380                        pr (Line);
1381                        putchar ('\n');
1382                    }
1383                    break;
1384                }
1385    }
1386    if (feof (file)) {
1387        if (!no_intty) {
1388            Currline = saveln;
1389            Fseek (file, startline);
1390        }
1391        else {
1392            pr (gettext("\nPattern not found\n"));
1393            end_it (0);
1394        }
1395        error (gettext("Pattern not found"));
1396    }
1397}
1398
1399#define MAXARGS 10 /* enough for 9 args. We are only passed 4 now */
1400
1401static void
1402execute (char *filename, char *cmd, ...)
1403{
1404        pid_t id;
1405	va_list ap;
1406	char *argp[MAXARGS];
1407	int  count;
1408
1409        fflush (stdout);
1410        reset_tty ();
1411        while ((id = fork ()) < 0)
1412            sleep (5);
1413        if (id == 0) {
1414            if (no_intty) {     /*M002*/
1415                close(0);       /*M002*/
1416                dup(2);         /*M002*/
1417            }                   /*M002*/
1418	    va_start(ap, cmd);
1419	    count = 0;
1420	    do {
1421#ifndef lint
1422		argp[count] = va_arg(ap, char *);
1423#else
1424		ap = ap;
1425#endif
1426		count++;
1427		if (count > MAXARGS)
1428			error (gettext("Too many arguments in execute()\n"));
1429	    } while (argp[count - 1] != NULL);
1430	    va_end(ap);
1431	    execvp(cmd, argp);
1432            write (2, "exec failed\n", 12);
1433            exit (1);
1434        }
1435        signal (SIGINT, SIG_IGN);
1436        signal (SIGQUIT, SIG_IGN);
1437	signal (SIGWINCH, SIG_IGN);
1438#ifdef SIGTSTP
1439        if (catch_susp)
1440            signal(SIGTSTP, SIG_DFL);
1441#endif
1442        wait ((pid_t)0);
1443        signal (SIGINT, end_it);
1444        signal (SIGQUIT, onquit);
1445	signal (SIGWINCH, chgwinsz);
1446#ifdef SIGTSTP
1447        if (catch_susp)
1448            signal(SIGTSTP, onsusp);
1449#endif
1450	/*
1451	 * Since we were ignoring window change signals while we executed
1452	 * the command, we must assume the window changed.
1453	 */
1454	(void) chgwinsz(0);
1455	set_tty ();
1456
1457        pr ("------------------------\n");
1458        prompt (filename);
1459}
1460/*
1461** Skip n lines in the file f
1462*/
1463
1464static void
1465skiplns(register off_t n, register FILE *f)
1466{
1467    register int c;
1468
1469    while (n > 0) {
1470        while ((c = Getc (f)) != '\n')
1471            if (c == EOF)
1472                return;
1473        n--;
1474        Currline++;
1475    }
1476}
1477
1478/*
1479** Skip nskip files in the file list (from the command line). Nskip may be
1480** negative.
1481*/
1482
1483static void
1484skipf(register int nskip)
1485{
1486    if (nskip == 0) return;
1487    if (nskip > 0) {
1488        if (fnum + nskip > nfiles - 1)
1489            nskip = nfiles - fnum - 1;
1490    }
1491    else if (within)
1492        ++fnum;
1493    fnum += nskip;
1494    if (fnum < 0)
1495        fnum = 0;
1496    pr (gettext("\n...Skipping "));
1497    pr ("\n");
1498    if (clreol)
1499        cleareol ();
1500    if (nskip > 0)
1501	printf(gettext("...Skipping to file %s\n"), fnames[fnum]);
1502    else
1503	printf(gettext("...Skipping back to file %s\n"), fnames[fnum]);
1504    if (clreol)
1505        cleareol ();
1506    pr ("\n");
1507    --fnum;
1508}
1509
1510/*----------------------------- Terminal I/O -------------------------------*/
1511
1512static void
1513initterm(void)
1514{
1515    int         erret = 0;
1516
1517    setbuf(stdout, obuf);
1518    if (!(no_tty = ioctl(1, TCGETA, &otty))) {
1519	if (setupterm(NULL, 1, &erret) != OK) {
1520            dumb++; ul_opt = 0;
1521        }
1522        else {
1523	    reset_shell_mode();
1524            if (((Lpp = lines) < 0) || hard_copy) {
1525                hard++; /* Hard copy terminal */
1526                Lpp = 24;
1527            }
1528            if (tailequ(fnames[0], "page") || !hard && (scroll_forward == NULL))
1529                noscroll++;
1530            if ((Mcol = columns) < 0)
1531                Mcol = 80;
1532            Wrap = tigetflag("am");
1533            /*
1534             *  Set up for underlining:  some terminals don't need it;
1535             *  others have start/stop sequences, still others have an
1536             *  underline char sequence which is assumed to move the
1537             *  cursor forward one character.  If underline sequence
1538             *  isn't available, settle for standout sequence.
1539             */
1540
1541            if (transparent_underline || over_strike)
1542                ul_opt = 0;
1543            if ((ULenter = tigetstr("smul")) == NULL &&
1544                (!underline_char) && (ULenter = tigetstr("smso")) == NULL)
1545                ULenter = "";
1546            if ((ULexit = tigetstr("rmul")) == NULL &&
1547                (!underline_char) && (ULexit = tigetstr("rmso")) == NULL)
1548                ULexit = "";
1549        }
1550        if ((shell = getenv("SHELL")) == NULL)
1551            shell = "/usr/bin/sh";
1552    }
1553    no_intty = ioctl(0, TCGETA, &otty);
1554    ioctl(2, TCGETA, &otty);
1555    hardtabs = !(otty.c_oflag & TAB3);
1556}
1557
1558static int
1559readch(void)
1560{
1561        char ch;
1562        extern int errno;
1563
1564        if (read (2, &ch, 1) <= 0)
1565                if (errno != EINTR)
1566                        end_it(0);		/* clean up before exiting */
1567                else
1568                        ch = otty.c_cc[VKILL];
1569        return (ch);
1570}
1571
1572static char BS = '\b';
1573static char CARAT = '^';
1574
1575static void
1576ttyin(char buf[], register int nmax, char pchar)
1577{
1578    register char *sptr;
1579    register unsigned char ch;
1580    int LengthBuffer[80];
1581    int *BufferPointer;
1582    int csno;
1583    register int slash = 0;
1584    int maxlen;
1585    char cbuf;
1586
1587    BufferPointer = LengthBuffer;
1588    sptr = buf;
1589    maxlen = 0;
1590    while (sptr - buf < nmax) {
1591        if (promptlen > maxlen)
1592	    maxlen = promptlen;
1593        ch = readch ();
1594        csno = csetno(ch);
1595        if (!csno) {
1596            if (ch == '\\') {
1597                slash++;
1598            } else if ((ch == otty.c_cc[VERASE]) && !slash) {
1599                if (sptr > buf) {
1600                    --promptlen;
1601                    write (2, &BS, 1);
1602		    sptr -= (*--BufferPointer);
1603                    if ((*sptr < ' ' && *sptr != '\n') || *sptr == RUBOUT) {
1604                        --promptlen;
1605                        write (2, &BS, 1);
1606                    }
1607                    continue;
1608                } else {
1609                    if (!clr_eol)
1610			promptlen = maxlen;
1611                    longjmp (restore, 1);
1612                }
1613            } else if ((ch == otty.c_cc[VKILL]) && !slash) {
1614                if (hard) {
1615                    show(ch);
1616                    putchar ('\n');
1617                    putchar (pchar);
1618                } else {
1619                    putchar ('\r');
1620		    putchar (pchar);
1621                    if (clr_eol)
1622                        prmpt_erase (1);
1623                    promptlen = 1;
1624                }
1625                sptr = buf;
1626                fflush (stdout);
1627                continue;
1628            }
1629            if (slash && (ch == otty.c_cc[VKILL] || ch == otty.c_cc[VERASE])) {
1630                write (2, &BS, 1);
1631	        sptr -= (*--BufferPointer);
1632            }
1633            if (ch != '\\')
1634                slash = 0;
1635	    *BufferPointer++ = 1;
1636            *sptr++ = ch;
1637            if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
1638                ch += ch == RUBOUT ? -0100 : 0100;
1639                write (2, &CARAT, 1);
1640                promptlen++;
1641            }
1642            cbuf = ch;
1643            if (ch != '\n' && ch != ESC) {
1644                write (2, &cbuf, 1);
1645                promptlen++;
1646            } else
1647                break;
1648            /* end of code set 0 */
1649        } else {
1650	    int i;
1651	    u_char buffer[5];
1652
1653	    *BufferPointer++ = cw[csno];
1654	    buffer[0] = *sptr++ = ch;
1655	    for(i=1; i<cw[csno]; i++) {
1656	        buffer[i] = *sptr++ = readch();
1657	    }
1658	    buffer[i]='\0';
1659	    write(2, buffer, strlen((char *)buffer));
1660	}
1661    }
1662    *--sptr = '\0';
1663    if (!clr_eol) promptlen = maxlen;
1664    if (sptr - buf >= nmax - 1)
1665        error (gettext("Line too long"));
1666}
1667
1668static int
1669expand(char *outbuf, char *inbuf)
1670{
1671	char *in_str;
1672	char *out_str;
1673	char ch;
1674	char temp[PATH_MAX];
1675	int changed = 0;
1676
1677    in_str = inbuf;
1678    out_str = temp;
1679    while ((ch = *in_str++) != '\0')
1680        switch (ch) {
1681        case '%':
1682            if (!no_intty) {
1683		if (strlcpy(out_str, fnames[fnum], sizeof (temp))
1684		    >= sizeof (temp))
1685			error(gettext("Command too long"));
1686                out_str += strlen (fnames[fnum]);
1687                changed++;
1688            }
1689            else
1690                *out_str++ = ch;
1691            break;
1692        case '!':
1693            if (!shellp)
1694                error (gettext("No previous command to substitute for"));
1695	    if (strlcpy(out_str, shell_line, sizeof (temp)) >= sizeof (temp))
1696		error(gettext("Command too long"));
1697            out_str += strlen (shell_line);
1698            changed++;
1699            break;
1700        case '\\':
1701            if (*in_str == '%' || *in_str == '!') {
1702                *out_str++ = *in_str++;
1703                break;
1704            }
1705        default:
1706            *out_str++ = ch;
1707        }
1708    *out_str++ = '\0';
1709	if (strlcpy(outbuf, temp, sizeof (shell_line)) >= sizeof (shell_line))
1710		error(gettext("Command too long"));
1711    return (changed);
1712}
1713
1714static void
1715show(register char ch)
1716{
1717    char cbuf;
1718
1719    if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
1720        ch += ch == RUBOUT ? -0100 : 0100;
1721        write (2, &CARAT, 1);
1722        promptlen++;
1723    }
1724    cbuf = ch;
1725    write (2, &cbuf, 1);
1726    promptlen++;
1727}
1728
1729static void
1730error (char *mess)
1731{
1732    if (clreol)
1733        cleareol ();
1734    else
1735        kill_line ();
1736    promptlen += strlen (mess);
1737    if (enter_standout_mode && exit_standout_mode) {
1738        putp (enter_standout_mode);
1739        pr(mess);
1740        putp (exit_standout_mode);
1741    }
1742    else
1743        pr (mess);
1744    fflush(stdout);
1745    errors++;
1746    longjmp (restore, 1);
1747}
1748
1749
1750static void
1751set_tty(void)
1752{
1753        ioctl(2, TCGETA, &otty);     /* save old tty modes */
1754        ioctl(2, TCGETA, &ntty);
1755        ntty.c_lflag &= ~ECHO & ~ICANON;
1756        ntty.c_cc[VMIN] = (char)1;
1757        ntty.c_cc[VTIME] = (char)0;
1758        ioctl (2, TCSETAF, &ntty);        /* set new tty modes */
1759}
1760
1761static void
1762reset_tty(void)
1763{
1764        ioctl (2, TCSETAF, &otty);        /* reset tty modes */
1765}
1766
1767static void
1768rdline(register FILE *f)
1769{
1770    register int c;
1771    register char *p;
1772
1773    p = Line;
1774    while ((c = Getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1)
1775        *p++ = c;
1776    if (c == '\n')
1777        Currline++;
1778    *p = '\0';
1779}
1780
1781/* Come here when we get a suspend signal from the terminal */
1782
1783/*
1784 * sig is put in as a dummy arg to have the compiler not to complain
1785 */
1786#ifdef SIGTSTP
1787/* ARGSUSED */
1788void
1789onsusp(int sig)
1790{
1791    /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
1792    signal(SIGTTOU, SIG_IGN);
1793    reset_tty ();
1794    fflush (stdout);
1795    signal(SIGTTOU, SIG_DFL);
1796
1797    /* Send the TSTP signal to suspend our process group */
1798    kill (0, SIGTSTP);
1799    /* Pause for station break */
1800
1801    /* We're back */
1802    signal (SIGTSTP, onsusp);
1803    set_tty ();
1804    if (inwait)
1805            longjmp (restore, 1);
1806}
1807#endif
1808