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