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