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