xref: /titanic_44/usr/src/cmd/more/more.c (revision b7f45089ccbe01bab3d7c7377b49d80d2ae18a69)
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 2000 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 tailequ (path, string)
664 char *path;
665 register char *string;
666 {
667 	return (!strcmp(basename(path), string));
668 }
669 
670 static void
671 prompt(char *filename)
672 {
673     if (clreol)
674         cleareol ();
675     else if (promptlen > 0)
676         kill_line ();
677     if (!hard) {
678         promptlen = 8;
679         if (enter_standout_mode && exit_standout_mode)
680             putp (enter_standout_mode);
681         if (clreol)
682             cleareol ();
683         pr(gettext("--More--"));
684         if (filename != NULL) {
685             promptlen += printf (gettext("(Next file: %s)"), filename);
686         }
687         else if (!no_intty) {
688             promptlen += printf ("(%d%%)", (int)((file_pos * 100) / file_size));
689         }
690         if (dum_opt) {
691             promptlen += pr(gettext("[Hit space to continue, Del to abort]"));
692         }
693         if (enter_standout_mode && exit_standout_mode)
694             putp (exit_standout_mode);
695         if (clreol) clreos ();
696         fflush(stdout);
697     }
698     else
699         write (2, &Bell, 1);
700     inwait++;
701 }
702 
703 /*
704  * when run from another program or a shell script, it is
705  * sometimes useful to prevent the next program from scrolling
706  * us off the screen before we get a chance to read this page.
707  *                      -Hans, July 24, 1982
708  */
709 static void
710 wait_eof(void)
711 {
712         if (enter_standout_mode && exit_standout_mode)
713                 putp (enter_standout_mode);
714         promptlen = pr(gettext("--No more--"));          /*M003*/
715         if (dum_opt)
716                 promptlen += pr(gettext("[Hit any key to continue]"));
717         if (enter_standout_mode && exit_standout_mode)
718                 putp(exit_standout_mode);
719         if (clreol) clreos();
720         fflush(stdout);
721         readch();
722         prmpt_erase (0);
723         fflush(stdout);
724 }
725 
726 /*
727 ** Get a logical line
728 */
729 
730 static int
731 getline(register FILE *f, int *length)
732 {
733     register int        c;
734     register char       *p;
735     register int        column;
736     static int          colflg;
737     register int        oldcolumn;
738     int			csno;
739 
740     p = Line;
741     column = 0;
742     oldcolumn = 0;
743     c = Getc (f);
744     if (colflg && c == '\n') {
745         Currline++;
746         c = Getc (f);
747     }
748     while (p < &Line[LINSIZ - 1]) {
749 	csno = csetno(c);
750         if (c == EOF) {
751             if (p > Line) {
752                 *p = '\0';
753                 *length = p - Line;
754                 return (column);
755             }
756             *length = p - Line;
757             return (EOF);
758         }
759 	if (!csno) {
760 	        if (c == '\n') {
761 	            /* detect \r\n.  -Hans */
762 	            if (p>Line && p[-1] == '\r') {
763 	                column = oldcolumn;
764 	                p--;
765 	            }
766 	            Currline++;
767 	            break;
768 	        }
769 	        *p++ = c;
770 	        if (c == '\t')
771 	            if (hardtabs && column < promptlen && !hard) {
772 	                if (clr_eol && !dumb) {
773 	                    column = 1 + (column | 7);
774 	                    putp (clr_eol);
775 	                    promptlen = 0;
776 	                }
777 	                else {
778 	                    for (--p; column & 7 && p < &Line[LINSIZ - 1]; column++) {
779 	                        *p++ = ' ';
780 	                    }
781 	                    if (column >= promptlen) promptlen = 0;
782 	                }
783 	            }
784 	            else
785 	                column = 1 + (column | 7);
786 	        else if ((c == '\b') && (ul_opt || !cr_opt) && (column > 0))  /* M008 */
787 	                column--;
788 
789 	        /* this is sort of strange.  what was here before was that
790 	           \r always set column to zero, and the hack above to
791 	           detect \r\n didnt exist.  the net effect is to make
792 	           the current line be overwritten by the prompt if it
793 	           had a \r at the end, and the line start after the \r
794 	           otherwise.  I suppose this is useful for overstriking
795 	           on hard copy terminals, but not on anything glass
796 	           -Hans */
797 
798 	        else if ((c == '\r') && !cr_opt) {
799 	                oldcolumn = column;
800 	                column = 0;
801 	        }
802 	        else if (c == '\f' && stop_opt) {
803 	                p[-1] = '^';
804 	                *p++ = 'L';
805 	                column += 2;
806 	                Pause++;
807 	        }
808 	        else if (c == EOF) {
809 	            *length = p - Line;
810 	            return (column);
811 	        }
812 	        else if (c < ' ' && cr_opt){                    /* M008 begin */
813 	                p[-1] = '^';
814 	                *p++ = c | ('A' - 1);
815 	                column += 2;
816 	        }                                               /* M008 end */
817 	        else if (c >= ' ' && c != RUBOUT)
818 	            column++;
819 	} /* end of code set 0 */
820 	else {
821 		column += scw[csno];
822 		if ( column > Mcol && fold_opt ) {
823 		    column -= scw[csno];
824 		    while ( column < Mcol ) {
825 		        column++;
826 		        *p++ = ' ';
827 		    }
828 		    column = Mcol;
829 		    Ungetc(c,f);
830 		} else {
831 		    int i;
832 		    *p++ = c;
833 		    for(i=1; i<cw[csno];i++)
834 			*p++ = Getc(f);
835 		}
836 	} /* end of codeset 1 ~ 3 */
837         if (column >= Mcol && fold_opt) break;
838         c = Getc (f);
839     }
840     if (column >= Mcol && Mcol > 0) {
841         if (!Wrap) {
842             *p++ = '\n';
843         }
844     }
845     colflg = column == Mcol && fold_opt;
846     if (colflg && eat_newline_glitch && Wrap) {
847 	*p++ = '\n'; /* simulate normal wrap */
848     }
849     *length = p - Line;
850     *p = 0;
851     return (column);
852 }
853 
854 /*
855 ** Erase the rest of the prompt, assuming we are starting at column col.
856 */
857 
858 static void
859 prmpt_erase(register int col)
860 {
861 
862     if (promptlen == 0)
863         return;
864     if (hard) {
865         putchar ('\n');
866     }
867     else {
868         if (col == 0)
869             putchar ('\r');
870         if (!dumb && clr_eol)
871             putp (clr_eol);
872         else
873             for (col = promptlen - col; col > 0; col--)
874                 putchar (' ');
875     }
876     promptlen = 0;
877 }
878 
879 /*
880 ** Erase the current line entirely
881 */
882 
883 static void
884 kill_line(void)
885 {
886     prmpt_erase (0);
887     if (!clr_eol || dumb) putchar ('\r');
888 }
889 
890 /* Print a buffer of n characters */
891 
892 static void
893 prbuf(register char *s, register int n)
894 {
895     char c;                             /* next ouput character */
896     register int state = 0;             /* next output char's UL state */
897     static int pstate = 0;              /* current terminal UL state (off) */
898 
899     while (--n >= 0)
900         if (!ul_opt)
901             putchar (*s++);
902         else {
903             if (n >= 2 && s[0] == '_' && s[1] == '\b') {
904                 n -= 2;
905                 s += 2;
906                 c = *s++;
907                 state = 1;
908             } else if (n >= 2 && s[1] == '\b' && s[2] == '_') {
909                 n -= 2;
910                 c = *s++;
911                 s += 2;
912                 state = 1;
913             } else {
914                 c = *s++;
915                 state = 0;
916             }
917             if (state != pstate)
918                 putp(state ? ULenter : ULexit);
919             pstate = state;
920             putchar(c);
921             if (state && underline_char) {
922                 putp(cursor_left);
923                 putp(underline_char);
924             }
925         }
926     /*
927      * M002
928      * You don't want to stay in standout mode at the end of the line;
929      * on some terminals, this will leave all of the remaining blank
930      * space on the line in standout mode.
931      */
932     if (state && !underline_char) {                       /*M002*/
933             putp(ULexit);                    /*M002*/
934             pstate = 0;                                 /*M002*/
935     }                                                   /*M002*/
936 }
937 
938 /*
939 **  Clear the screen
940 */
941 
942 static void
943 doclear(void)
944 {
945     if (clear_screen && !hard) {
946         putp(clear_screen);
947 
948         /* Put out carriage return so that system doesn't
949         ** get confused by escape sequences when expanding tabs
950         */
951         putchar ('\r');
952         promptlen = 0;
953     }
954 }
955 
956 
957 static int lastcmd, lastp;
958 static off_t lastarg;
959 static int lastcolon;
960 char shell_line[132];
961 
962 /*
963 ** Read a command and do it. A command consists of an optional integer
964 ** argument followed by the command character.  Return the number of lines
965 ** to display in the next screenful.  If there is nothing more to display
966 ** in the current file, zero is returned.
967 */
968 
969 static off_t
970 command(char *filename, register FILE *f)
971 {
972     register off_t nlines;
973     register off_t retval;
974     register int c;
975     char colonch;
976     FILE *helpf;
977     int done;
978     char comchar, cmdbuf[80];
979     char filebuf[128];
980     char *loc;
981 
982 #define ret(val) retval=val;done++;break
983 
984     done = 0;
985     if (!errors)
986         prompt (filename);
987     else
988         errors = 0;
989     for (;;) {
990         nlines = number (&comchar);
991         lastp = colonch = 0;
992         if (comchar == '.') {   /* Repeat last command */
993                 lastp++;
994                 comchar = lastcmd;
995                 nlines = lastarg;
996                 if (lastcmd == ':')
997                         colonch = lastcolon;
998         }
999         lastcmd = comchar;
1000         lastarg = nlines;
1001 	if((comchar != RUBOUT) && !dum_opt) {
1002         	if (comchar == otty.c_cc[VERASE]) {
1003            		 kill_line ();
1004             		prompt (filename);
1005             		continue;
1006         	}
1007 	}
1008         switch (comchar) {
1009         case ':':
1010             retval = colon (filename, colonch, nlines);
1011             if (retval >= 0)
1012                 done++;
1013             break;
1014 	case 'b':
1015 	case ctrl('B'):
1016 	    {
1017 		register off_t initline;
1018 
1019 		if (no_intty) {
1020 		    write(2, &bell, 1);
1021 		    return (-1);
1022 		}
1023 
1024 		if (nlines == 0) nlines++;
1025 
1026 		putchar ('\r');
1027 		prmpt_erase (0);
1028 		printf ("\n");
1029 		if (clreol)
1030 			cleareol ();
1031 		printf (gettext("...back %lld page"), nlines);
1032 		if (nlines > 1)
1033 			pr ("s\n");
1034 		else
1035 			pr ("\n");
1036 
1037 		if (clreol)
1038 			cleareol ();
1039 		pr ("\n");
1040 
1041 		initline = Currline - dlines * (nlines + 1);
1042 		if (! noscroll)
1043 		    --initline;
1044 		if (initline < 0) initline = 0;
1045 		Fseek(f, 0LL);
1046 		Currline = 0;	/* skiplns() will make Currline correct */
1047 		skiplns(initline, f);
1048 		if (! noscroll) {
1049 		    ret(dlines + 1);
1050 		}
1051 		else {
1052 		    ret(dlines);
1053 		}
1054 	    }
1055         case ' ':
1056         case 'z':
1057             if (nlines == 0) nlines = dlines;
1058             else if (comchar == 'z') dlines = nlines;
1059             ret (nlines);
1060         case 'd':
1061         case ctrl('D'):
1062             if (nlines != 0) nscroll = nlines;
1063             ret (nscroll);
1064         case RUBOUT:
1065         case 'q':
1066         case 'Q':
1067             end_it(0);
1068 	    /*NOTREACHED*/
1069         case 's':
1070         case 'f':
1071             if (nlines == 0) nlines++;
1072             if (comchar == 'f')
1073                 nlines *= dlines;
1074             putchar ('\r');
1075             prmpt_erase (0);
1076             printf ("\n");
1077             if (clreol)
1078                 cleareol ();
1079             printf (gettext("...skipping %lld line"), nlines);
1080             if (nlines > 1)
1081                 pr ("s\n");
1082             else
1083                 pr ("\n");
1084 
1085             if (clreol)
1086                 cleareol ();
1087             pr ("\n");
1088 
1089             while (nlines > 0) {
1090                 while ((c = Getc (f)) != '\n')
1091                     if (c == EOF) {
1092                         retval = 0;
1093                         done++;
1094                         goto endsw;
1095                     }
1096                     Currline++;
1097                     nlines--;
1098             }
1099             ret (dlines);
1100         case '\n':
1101 	    if (nlines != 0)
1102                 dlines = nlines;
1103             else
1104                 nlines = 1;
1105             ret (nlines);
1106         case '\f':
1107             if (!no_intty) {
1108                 doclear ();
1109                 Fseek (f, screen_start.chrctr);
1110                 Currline = screen_start.line;
1111                 ret (dlines);
1112             }
1113             else {
1114                 write (2, &Bell, 1);
1115                 break;
1116             }
1117         case '\'':
1118             if (!no_intty) {
1119                 kill_line ();
1120                 pr (gettext("\n***Back***\n\n"));
1121                 Fseek (f, context.chrctr);
1122                 Currline = context.line;
1123                 ret (dlines);
1124             }
1125             else {
1126                 write (2, &Bell, 1);
1127                 break;
1128             }
1129         case '=':
1130             kill_line ();
1131             promptlen = printf ("%lld", Currline);
1132             fflush (stdout);
1133             break;
1134         case 'n':
1135             lastp++;
1136 	    /*FALLTHROUGH*/
1137         case '/':
1138             if (nlines == 0) nlines++;
1139             kill_line ();
1140             pr ("/");
1141             promptlen = 1;
1142             fflush (stdout);
1143             if (lastp) {
1144                 write (2,"\r", 1);
1145                 search (NULL, f, nlines);       /* Use previous r.e. */
1146             }
1147             else {
1148                 ttyin (cmdbuf, 78, '/');
1149                 write (2, "\r", 1);
1150                 search (cmdbuf, f, nlines);
1151             }
1152             ret (dlines-1);
1153         case '!':
1154             do_shell (filename);
1155             break;
1156         case 'h':
1157         case '?':
1158 	    /*
1159 	     * First get local help file.
1160 	     */
1161 	    loc = setlocale(LC_MESSAGES, 0);
1162 	    sprintf(filebuf, LOCAL_HELP, loc);
1163 
1164             if  ((strcmp(loc, "C") == 0) || (helpf = fopen (filebuf, "r")) == NULL) {
1165 		    if  ((helpf = fopen (HELPFILE, "r")) == NULL)
1166 			error (gettext("Can't open help file"));
1167 	    }
1168             if (noscroll) doclear ();
1169             copy_file (helpf);
1170             fclose (helpf);
1171             prompt (filename);
1172             break;
1173         case 'v':       /* This case should go right before default */
1174             if (!no_intty) {
1175                 kill_line ();
1176                 cmdbuf[0] = '+';
1177                 sprintf(&cmdbuf[1], "%lld", Currline);
1178                 pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]);
1179                 execute (filename, VI, "vi", cmdbuf, fnames[fnum], 0);
1180                 break;
1181             }
1182         default:
1183 		if (dum_opt) {
1184 			kill_line ();
1185 		    	promptlen = pr(gettext("[Press 'h' for instructions.]"));
1186 			fflush (stdout);
1187 	    	}
1188 	    	else
1189             		write (2, &Bell, 1);
1190             break;
1191         }
1192         if (done) break;
1193     }
1194     putchar ('\r');
1195 endsw:
1196     inwait = 0;
1197     notell++;
1198     return (retval);
1199 }
1200 
1201 char ch;
1202 
1203 /*
1204  * Execute a colon-prefixed command.
1205  * Returns <0 if not a command that should cause
1206  * more of the file to be printed.
1207  */
1208 
1209 static int
1210 colon(char *filename, int cmd, off_t nlines)
1211 {
1212         if (cmd == 0)
1213                 ch = readch ();
1214         else
1215                 ch = cmd;
1216         lastcolon = ch;
1217         switch (ch) {
1218         case 'f':
1219                 kill_line ();
1220                 if (!no_intty)
1221                         promptlen = printf (gettext("\"%s\" line %lld"),
1222 					    fnames[fnum], Currline);
1223                 else
1224                         promptlen = printf(
1225 			 gettext("[Not a file] line %lld"), Currline);
1226                 fflush (stdout);
1227                 return (-1);
1228         case 'n':
1229                 if (nlines == 0) {
1230                         if (fnum >= nfiles - 1)
1231                                 end_it(0);
1232                         nlines++;
1233                 }
1234                 putchar ('\r');
1235                 prmpt_erase (0);
1236                 skipf ((int)nlines);
1237                 return (0);
1238         case 'p':
1239                 if (no_intty) {
1240                         write (2, &Bell, 1);
1241                         return (-1);
1242                 }
1243                 putchar ('\r');
1244                 prmpt_erase (0);
1245                 if (nlines == 0)
1246                         nlines++;
1247                 skipf ((int)-nlines);
1248                 return (0);
1249         case '!':
1250                 do_shell (filename);
1251                 return (-1);
1252         case 'q':
1253         case 'Q':
1254                 end_it(0);
1255         default:
1256                 write (2, &Bell, 1);
1257                 return (-1);
1258         }
1259 }
1260 
1261 /*
1262 ** Read a decimal number from the terminal. Set cmd to the non-digit which
1263 ** terminates the number.
1264 */
1265 
1266 static int
1267 number(char *cmd)
1268 {
1269         register int i;
1270 
1271         i = 0; ch = otty.c_cc[VKILL];
1272         for (;;) {
1273                 ch = readch ();
1274                 if (ch >= '0' && ch <= '9') {
1275                         i = i*10 + ch - '0';
1276                 } else if (ch == RUBOUT) {
1277                         i = 0;
1278                         *cmd = ch;
1279                         break;
1280                 } else if (ch == otty.c_cc[VKILL]) {
1281                         i = 0;
1282                 } else {
1283                         *cmd = ch;
1284                         break;
1285                 }
1286         }
1287         return (i);
1288 }
1289 
1290 static void
1291 do_shell(char *filename)
1292 {
1293         char cmdbuf[80];
1294 
1295         kill_line ();
1296         pr ("!");
1297         fflush (stdout);
1298         promptlen = 1;
1299         if (lastp)
1300                 pr (shell_line);
1301         else {
1302                 ttyin (cmdbuf, 78, '!');
1303                 if (expand (shell_line, cmdbuf)) {
1304                         kill_line ();
1305                         promptlen = printf ("!%s", shell_line);
1306                 }
1307         }
1308         fflush (stdout);
1309         write (2, "\n", 1);
1310         promptlen = 0;
1311         shellp = 1;
1312         execute (filename, shell, shell, "-c", shell_line, 0);
1313 }
1314 
1315 /*
1316 ** Search for nth ocurrence of regular expression contained in buf in the file
1317 */
1318 
1319 static void
1320 search(char buf[], FILE *file, register off_t n)
1321 {
1322     off_t startline = Ftell (file);
1323     register off_t line1 = startline;
1324     register off_t line2 = startline;
1325     register off_t line3 = startline;
1326     register off_t lncount;
1327     off_t saveln;
1328     static char *s = NULL;
1329     static char lastbuf[80];
1330 
1331     if (buf != NULL) {
1332 	if (s != NULL)
1333 		free(s);
1334 	if (*buf != '\0') {
1335 		if ((s = regcmp(buf, (char *) NULL)) == NULL)
1336 			error(gettext("Regular expression botch"));
1337 		else
1338 			strcpy(lastbuf, buf);
1339 	} else {
1340 		if ((s = regcmp(lastbuf, (char *) NULL)) == NULL)
1341 			error(gettext("No previous regular expression"));
1342 	}
1343     } else {
1344 	if (s == NULL)
1345 	    error(gettext("No previous regular expression"));
1346     }
1347     context.line = saveln = Currline;
1348     context.chrctr = startline;
1349     lncount = 0;
1350     while (!feof (file)) {
1351         line3 = line2;
1352         line2 = line1;
1353         line1 = Ftell (file);
1354         rdline (file);
1355         lncount++;
1356         if (regex(s, Line) != NULL)
1357                 if (--n == 0) {
1358                     if (lncount > 3 || (lncount > 1 && no_intty))
1359                     {
1360                         pr ("\n");
1361                         if (clreol)
1362                             cleareol ();
1363                         pr(gettext("...skipping\n"));
1364                     }
1365                     if (!no_intty) {
1366                         Currline -= (lncount >= 3 ? 3 : lncount);
1367                         Fseek (file, line3);
1368                         if (noscroll)
1369                             if (clreol) {
1370                                 home ();
1371                                 cleareol ();
1372                             }
1373                             else
1374                                 doclear ();
1375                     }
1376                     else {
1377                         kill_line ();
1378                         if (noscroll)
1379                             if (clreol) {
1380                                 home ();
1381                                 cleareol ();
1382                             } else
1383                                 doclear ();
1384                         pr (Line);
1385                         putchar ('\n');
1386                     }
1387                     break;
1388                 }
1389     }
1390     if (feof (file)) {
1391         if (!no_intty) {
1392             Currline = saveln;
1393             Fseek (file, startline);
1394         }
1395         else {
1396             pr (gettext("\nPattern not found\n"));
1397             end_it (0);
1398         }
1399         error (gettext("Pattern not found"));
1400     }
1401 }
1402 
1403 #define MAXARGS 10 /* enough for 9 args. We are only passed 4 now */
1404 
1405 static void
1406 execute (char *filename, char *cmd, ...)
1407 {
1408         pid_t id;
1409 	va_list ap;
1410 	char *argp[MAXARGS];
1411 	int  count;
1412 
1413         fflush (stdout);
1414         reset_tty ();
1415         while ((id = fork ()) < 0)
1416             sleep (5);
1417         if (id == 0) {
1418             if (no_intty) {     /*M002*/
1419                 close(0);       /*M002*/
1420                 dup(2);         /*M002*/
1421             }                   /*M002*/
1422 	    va_start(ap, cmd);
1423 	    count = 0;
1424 	    do {
1425 #ifndef lint
1426 		argp[count] = va_arg(ap, char *);
1427 #else
1428 		ap = ap;
1429 #endif
1430 		count++;
1431 		if (count > MAXARGS)
1432 			error (gettext("Too many arguments in execute()\n"));
1433 	    } while (argp[count - 1] != NULL);
1434 	    va_end(ap);
1435 	    execvp(cmd, argp);
1436             write (2, "exec failed\n", 12);
1437             exit (1);
1438         }
1439         signal (SIGINT, SIG_IGN);
1440         signal (SIGQUIT, SIG_IGN);
1441 	signal (SIGWINCH, SIG_IGN);
1442 #ifdef SIGTSTP
1443         if (catch_susp)
1444             signal(SIGTSTP, SIG_DFL);
1445 #endif
1446         wait ((pid_t)0);
1447         signal (SIGINT, end_it);
1448         signal (SIGQUIT, onquit);
1449 	signal (SIGWINCH, chgwinsz);
1450 #ifdef SIGTSTP
1451         if (catch_susp)
1452             signal(SIGTSTP, onsusp);
1453 #endif
1454 	/*
1455 	 * Since we were ignoring window change signals while we executed
1456 	 * the command, we must assume the window changed.
1457 	 */
1458 	(void) chgwinsz(0);
1459 	set_tty ();
1460 
1461         pr ("------------------------\n");
1462         prompt (filename);
1463 }
1464 /*
1465 ** Skip n lines in the file f
1466 */
1467 
1468 static void
1469 skiplns(register off_t n, register FILE *f)
1470 {
1471     register int c;
1472 
1473     while (n > 0) {
1474         while ((c = Getc (f)) != '\n')
1475             if (c == EOF)
1476                 return;
1477             n--;
1478             Currline++;
1479     }
1480 }
1481 
1482 /*
1483 ** Skip nskip files in the file list (from the command line). Nskip may be
1484 ** negative.
1485 */
1486 
1487 static void
1488 skipf(register int nskip)
1489 {
1490     if (nskip == 0) return;
1491     if (nskip > 0) {
1492         if (fnum + nskip > nfiles - 1)
1493             nskip = nfiles - fnum - 1;
1494     }
1495     else if (within)
1496         ++fnum;
1497     fnum += nskip;
1498     if (fnum < 0)
1499         fnum = 0;
1500     pr (gettext("\n...Skipping "));
1501     pr ("\n");
1502     if (clreol)
1503         cleareol ();
1504     if (nskip > 0)
1505 	printf(gettext("...Skipping to file %s\n"), fnames[fnum]);
1506     else
1507 	printf(gettext("...Skipping back to file %s\n"), fnames[fnum]);
1508     if (clreol)
1509         cleareol ();
1510     pr ("\n");
1511     --fnum;
1512 }
1513 
1514 /*----------------------------- Terminal I/O -------------------------------*/
1515 
1516 static void
1517 initterm(void)
1518 {
1519     int         erret = 0;
1520 
1521     setbuf(stdout, obuf);
1522     if (!(no_tty = ioctl(1, TCGETA, &otty))) {
1523 	if (setupterm(NULL, 1, &erret) != OK) {
1524             dumb++; ul_opt = 0;
1525         }
1526         else {
1527 	    reset_shell_mode();
1528             if (((Lpp = lines) < 0) || hard_copy) {
1529                 hard++; /* Hard copy terminal */
1530                 Lpp = 24;
1531             }
1532             if (tailequ(fnames[0], "page") || !hard && (scroll_forward == NULL))
1533                 noscroll++;
1534             if ((Mcol = columns) < 0)
1535                 Mcol = 80;
1536             Wrap = tigetflag("am");
1537             /*
1538              *  Set up for underlining:  some terminals don't need it;
1539              *  others have start/stop sequences, still others have an
1540              *  underline char sequence which is assumed to move the
1541              *  cursor forward one character.  If underline sequence
1542              *  isn't available, settle for standout sequence.
1543              */
1544 
1545             if (transparent_underline || over_strike)
1546                 ul_opt = 0;
1547             if ((ULenter = tigetstr("smul")) == NULL &&
1548                 (!underline_char) && (ULenter = tigetstr("smso")) == NULL)
1549                 ULenter = "";
1550             if ((ULexit = tigetstr("rmul")) == NULL &&
1551                 (!underline_char) && (ULexit = tigetstr("rmso")) == NULL)
1552                 ULexit = "";
1553         }
1554         if ((shell = getenv("SHELL")) == NULL)
1555             shell = "/usr/bin/sh";
1556     }
1557     no_intty = ioctl(0, TCGETA, &otty);
1558     ioctl(2, TCGETA, &otty);
1559     hardtabs = !(otty.c_oflag & TAB3);
1560 }
1561 
1562 static int
1563 readch(void)
1564 {
1565         char ch;
1566         extern int errno;
1567 
1568         if (read (2, &ch, 1) <= 0)
1569                 if (errno != EINTR)
1570                         end_it(0);		/* clean up before exiting */
1571                 else
1572                         ch = otty.c_cc[VKILL];
1573         return (ch);
1574 }
1575 
1576 static char BS = '\b';
1577 static char CARAT = '^';
1578 
1579 static void
1580 ttyin(char buf[], register int nmax, char pchar)
1581 {
1582     register char *sptr;
1583     register unsigned char ch;
1584     int LengthBuffer[80];
1585     int *BufferPointer;
1586     int csno;
1587     register int slash = 0;
1588     int maxlen;
1589     char cbuf;
1590 
1591     BufferPointer = LengthBuffer;
1592     sptr = buf;
1593     maxlen = 0;
1594     while (sptr - buf < nmax) {
1595         if (promptlen > maxlen)
1596 	    maxlen = promptlen;
1597         ch = readch ();
1598         csno = csetno(ch);
1599         if (!csno) {
1600             if (ch == '\\') {
1601                 slash++;
1602             } else if ((ch == otty.c_cc[VERASE]) && !slash) {
1603                 if (sptr > buf) {
1604                     --promptlen;
1605                     write (2, &BS, 1);
1606 		    sptr -= (*--BufferPointer);
1607                     if ((*sptr < ' ' && *sptr != '\n') || *sptr == RUBOUT) {
1608                         --promptlen;
1609                         write (2, &BS, 1);
1610                     }
1611                     continue;
1612                 } else {
1613                     if (!clr_eol)
1614 			promptlen = maxlen;
1615                     longjmp (restore, 1);
1616                 }
1617             } else if ((ch == otty.c_cc[VKILL]) && !slash) {
1618                 if (hard) {
1619                     show(ch);
1620                     putchar ('\n');
1621                     putchar (pchar);
1622                 } else {
1623                     putchar ('\r');
1624 		    putchar (pchar);
1625                     if (clr_eol)
1626                         prmpt_erase (1);
1627                     promptlen = 1;
1628                 }
1629                 sptr = buf;
1630                 fflush (stdout);
1631                 continue;
1632             }
1633             if (slash && (ch == otty.c_cc[VKILL] || ch == otty.c_cc[VERASE])) {
1634                 write (2, &BS, 1);
1635 	        sptr -= (*--BufferPointer);
1636             }
1637             if (ch != '\\')
1638                 slash = 0;
1639 	    *BufferPointer++ = 1;
1640             *sptr++ = ch;
1641             if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
1642                 ch += ch == RUBOUT ? -0100 : 0100;
1643                 write (2, &CARAT, 1);
1644                 promptlen++;
1645             }
1646             cbuf = ch;
1647             if (ch != '\n' && ch != ESC) {
1648                 write (2, &cbuf, 1);
1649                 promptlen++;
1650             } else
1651                 break;
1652             /* end of code set 0 */
1653         } else {
1654 	    int i;
1655 	    u_char buffer[5];
1656 
1657 	    *BufferPointer++ = cw[csno];
1658 	    buffer[0] = *sptr++ = ch;
1659 	    for(i=1; i<cw[csno]; i++) {
1660 	        buffer[i] = *sptr++ = readch();
1661 	    }
1662 	    buffer[i]='\0';
1663 	    write(2, buffer, strlen((char *)buffer));
1664 	}
1665     }
1666     *--sptr = '\0';
1667     if (!clr_eol) promptlen = maxlen;
1668     if (sptr - buf >= nmax - 1)
1669         error (gettext("Line too long"));
1670 }
1671 
1672 static int
1673 expand(char *outbuf, char *inbuf)
1674 {
1675     register char *in_str;
1676     register char *out_str;
1677     register char ch;
1678     char temp[200];
1679     int changed = 0;
1680 
1681     in_str = inbuf;
1682     out_str = temp;
1683     while ((ch = *in_str++) != '\0')
1684         switch (ch) {
1685         case '%':
1686             if (!no_intty) {
1687                 strcpy (out_str, fnames[fnum]);
1688                 out_str += strlen (fnames[fnum]);
1689                 changed++;
1690             }
1691             else
1692                 *out_str++ = ch;
1693             break;
1694         case '!':
1695             if (!shellp)
1696                 error (gettext("No previous command to substitute for"));
1697             strcpy (out_str, shell_line);
1698             out_str += strlen (shell_line);
1699             changed++;
1700             break;
1701         case '\\':
1702             if (*in_str == '%' || *in_str == '!') {
1703                 *out_str++ = *in_str++;
1704                 break;
1705             }
1706         default:
1707             *out_str++ = ch;
1708         }
1709     *out_str++ = '\0';
1710     strcpy (outbuf, temp);
1711     return (changed);
1712 }
1713 
1714 static void
1715 show(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 
1729 static void
1730 error (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 
1750 static void
1751 set_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 
1761 static void
1762 reset_tty(void)
1763 {
1764         ioctl (2, TCSETAF, &otty);        /* reset tty modes */
1765 }
1766 
1767 static void
1768 rdline(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 */
1788 void
1789 onsusp(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