xref: /illumos-gate/usr/src/cmd/more/more.c (revision 99ea293e719ac006d413e4fde6ac0d5cd4dd6c59)
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) __NORETURN;
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         case 's':
1065         case 'f':
1066             if (nlines == 0) nlines++;
1067             if (comchar == 'f')
1068                 nlines *= dlines;
1069             putchar ('\r');
1070             prmpt_erase (0);
1071             printf ("\n");
1072             if (clreol)
1073                 cleareol ();
1074             printf (gettext("...skipping %lld line"), nlines);
1075             if (nlines > 1)
1076                 pr ("s\n");
1077             else
1078                 pr ("\n");
1079 
1080             if (clreol)
1081                 cleareol ();
1082             pr ("\n");
1083 
1084             while (nlines > 0) {
1085                 while ((c = Getc (f)) != '\n')
1086                     if (c == EOF) {
1087                         retval = 0;
1088                         done++;
1089                         goto endsw;
1090                     }
1091                 Currline++;
1092                 nlines--;
1093             }
1094             ret (dlines);
1095         case '\n':
1096 	    if (nlines != 0)
1097                 dlines = nlines;
1098             else
1099                 nlines = 1;
1100             ret (nlines);
1101         case '\f':
1102             if (!no_intty) {
1103                 doclear ();
1104                 Fseek (f, screen_start.chrctr);
1105                 Currline = screen_start.line;
1106                 ret (dlines);
1107             }
1108             else {
1109                 write (2, &Bell, 1);
1110                 break;
1111             }
1112         case '\'':
1113             if (!no_intty) {
1114                 kill_line ();
1115                 pr (gettext("\n***Back***\n\n"));
1116                 Fseek (f, context.chrctr);
1117                 Currline = context.line;
1118                 ret (dlines);
1119             }
1120             else {
1121                 write (2, &Bell, 1);
1122                 break;
1123             }
1124         case '=':
1125             kill_line ();
1126             promptlen = printf ("%lld", Currline);
1127             fflush (stdout);
1128             break;
1129         case 'n':
1130             lastp++;
1131 	    /* FALLTHROUGH */
1132         case '/':
1133             if (nlines == 0) nlines++;
1134             kill_line ();
1135             pr ("/");
1136             promptlen = 1;
1137             fflush (stdout);
1138             if (lastp) {
1139                 write (2,"\r", 1);
1140                 search (NULL, f, nlines);       /* Use previous r.e. */
1141             }
1142             else {
1143                 ttyin (cmdbuf, 78, '/');
1144                 write (2, "\r", 1);
1145                 search (cmdbuf, f, nlines);
1146             }
1147             ret (dlines-1);
1148         case '!':
1149             do_shell (filename);
1150             break;
1151         case 'h':
1152         case '?':
1153 	    /*
1154 	     * First get local help file.
1155 	     */
1156 	    loc = setlocale(LC_MESSAGES, 0);
1157 	    sprintf(filebuf, LOCAL_HELP, loc);
1158 
1159             if  ((strcmp(loc, "C") == 0) || (helpf = fopen (filebuf, "r")) == NULL) {
1160 		    if  ((helpf = fopen (HELPFILE, "r")) == NULL)
1161 			error (gettext("Can't open help file"));
1162 	    }
1163             if (noscroll) doclear ();
1164             copy_file (helpf);
1165             fclose (helpf);
1166             prompt (filename);
1167             break;
1168         case 'v':       /* This case should go right before default */
1169             if (!no_intty) {
1170                 kill_line ();
1171                 cmdbuf[0] = '+';
1172                 sprintf(&cmdbuf[1], "%lld", Currline);
1173                 pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]);
1174                 execute (filename, VI, "vi", cmdbuf, fnames[fnum], 0);
1175                 break;
1176             }
1177 	    /* FALLTHROUGH */
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 	    int row, col;
1524 	    struct winsize w;
1525 	    char *s;
1526 
1527 	    reset_shell_mode();
1528 	    row = col = -1;
1529 	    if (ioctl(fileno(stdout), TIOCGWINSZ, &w) == 0) {
1530 		if (w.ws_row > 0)
1531 		    row = w.ws_row;
1532 		if (w.ws_col > 0)
1533 		    col = w.ws_col;
1534 	    }
1535 	    if (row < 0) {
1536 		if ((s = getenv("LINES")) != NULL)
1537 		    row = atoi(s);
1538 	    }
1539 	    if (col < 0) {
1540 		if ((s = getenv("COLUMNS")) != NULL)
1541 		    col = atoi(s);
1542 	    }
1543 
1544 	    if (row > 0)
1545 		Lpp = row;
1546 	    else if (((Lpp = lines) < 0) || hard_copy) {
1547                 hard++; /* Hard copy terminal */
1548                 Lpp = 24;
1549             }
1550 	    if (col > 0)
1551 		Mcol = col;
1552 	    else if ((Mcol = columns) < 0)
1553                 Mcol = 80;
1554 
1555             if (tailequ(fnames[0], "page") || !hard && (scroll_forward == NULL))
1556                 noscroll++;
1557             Wrap = tigetflag("am");
1558             /*
1559              *  Set up for underlining:  some terminals don't need it;
1560              *  others have start/stop sequences, still others have an
1561              *  underline char sequence which is assumed to move the
1562              *  cursor forward one character.  If underline sequence
1563              *  isn't available, settle for standout sequence.
1564              */
1565 
1566             if (transparent_underline || over_strike)
1567                 ul_opt = 0;
1568             if ((ULenter = tigetstr("smul")) == NULL &&
1569                 (!underline_char) && (ULenter = tigetstr("smso")) == NULL)
1570                 ULenter = "";
1571             if ((ULexit = tigetstr("rmul")) == NULL &&
1572                 (!underline_char) && (ULexit = tigetstr("rmso")) == NULL)
1573                 ULexit = "";
1574         }
1575         if ((shell = getenv("SHELL")) == NULL)
1576             shell = "/usr/bin/sh";
1577     }
1578     no_intty = ioctl(0, TCGETA, &otty);
1579     ioctl(2, TCGETA, &otty);
1580     hardtabs = !(otty.c_oflag & TAB3);
1581 }
1582 
1583 static int
1584 readch(void)
1585 {
1586         char ch;
1587         extern int errno;
1588 
1589         if (read (2, &ch, 1) <= 0)
1590                 if (errno != EINTR)
1591                         end_it(0);		/* clean up before exiting */
1592                 else
1593                         ch = otty.c_cc[VKILL];
1594         return (ch);
1595 }
1596 
1597 static char BS = '\b';
1598 static char CARAT = '^';
1599 
1600 static void
1601 ttyin(char buf[], register int nmax, char pchar)
1602 {
1603     register char *sptr;
1604     register unsigned char ch;
1605     int LengthBuffer[80];
1606     int *BufferPointer;
1607     int csno;
1608     register int slash = 0;
1609     int maxlen;
1610     char cbuf;
1611 
1612     BufferPointer = LengthBuffer;
1613     sptr = buf;
1614     maxlen = 0;
1615     while (sptr - buf < nmax) {
1616         if (promptlen > maxlen)
1617 	    maxlen = promptlen;
1618         ch = readch ();
1619         csno = csetno(ch);
1620         if (!csno) {
1621             if (ch == '\\') {
1622                 slash++;
1623             } else if ((ch == otty.c_cc[VERASE]) && !slash) {
1624                 if (sptr > buf) {
1625                     --promptlen;
1626                     write (2, &BS, 1);
1627 		    sptr -= (*--BufferPointer);
1628                     if ((*sptr < ' ' && *sptr != '\n') || *sptr == RUBOUT) {
1629                         --promptlen;
1630                         write (2, &BS, 1);
1631                     }
1632                     continue;
1633                 } else {
1634                     if (!clr_eol)
1635 			promptlen = maxlen;
1636                     longjmp (restore, 1);
1637                 }
1638             } else if ((ch == otty.c_cc[VKILL]) && !slash) {
1639                 if (hard) {
1640                     show(ch);
1641                     putchar ('\n');
1642                     putchar (pchar);
1643                 } else {
1644                     putchar ('\r');
1645 		    putchar (pchar);
1646                     if (clr_eol)
1647                         prmpt_erase (1);
1648                     promptlen = 1;
1649                 }
1650                 sptr = buf;
1651                 fflush (stdout);
1652                 continue;
1653             }
1654             if (slash && (ch == otty.c_cc[VKILL] || ch == otty.c_cc[VERASE])) {
1655                 write (2, &BS, 1);
1656 	        sptr -= (*--BufferPointer);
1657             }
1658             if (ch != '\\')
1659                 slash = 0;
1660 	    *BufferPointer++ = 1;
1661             *sptr++ = ch;
1662             if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
1663                 ch += ch == RUBOUT ? -0100 : 0100;
1664                 write (2, &CARAT, 1);
1665                 promptlen++;
1666             }
1667             cbuf = ch;
1668             if (ch != '\n' && ch != ESC) {
1669                 write (2, &cbuf, 1);
1670                 promptlen++;
1671             } else
1672                 break;
1673             /* end of code set 0 */
1674         } else {
1675 	    int i;
1676 	    u_char buffer[5];
1677 
1678 	    *BufferPointer++ = cw[csno];
1679 	    buffer[0] = *sptr++ = ch;
1680 	    for(i=1; i<cw[csno]; i++) {
1681 	        buffer[i] = *sptr++ = readch();
1682 	    }
1683 	    buffer[i]='\0';
1684 	    write(2, buffer, strlen((char *)buffer));
1685 	}
1686     }
1687     *--sptr = '\0';
1688     if (!clr_eol) promptlen = maxlen;
1689     if (sptr - buf >= nmax - 1)
1690         error (gettext("Line too long"));
1691 }
1692 
1693 static int
1694 expand(char *outbuf, char *inbuf)
1695 {
1696 	char *in_str;
1697 	char *out_str;
1698 	char ch;
1699 	char temp[PATH_MAX];
1700 	int changed = 0;
1701 
1702     in_str = inbuf;
1703     out_str = temp;
1704     while ((ch = *in_str++) != '\0')
1705         switch (ch) {
1706         case '%':
1707             if (!no_intty) {
1708 		if (strlcpy(out_str, fnames[fnum], sizeof (temp))
1709 		    >= sizeof (temp))
1710 			error(gettext("Command too long"));
1711                 out_str += strlen (fnames[fnum]);
1712                 changed++;
1713             }
1714             else
1715                 *out_str++ = ch;
1716             break;
1717         case '!':
1718             if (!shellp)
1719                 error (gettext("No previous command to substitute for"));
1720 	    if (strlcpy(out_str, shell_line, sizeof (temp)) >= sizeof (temp))
1721 		error(gettext("Command too long"));
1722             out_str += strlen (shell_line);
1723             changed++;
1724             break;
1725         case '\\':
1726             if (*in_str == '%' || *in_str == '!') {
1727                 *out_str++ = *in_str++;
1728                 break;
1729             }
1730 	    /* FALLTHROUGH */
1731         default:
1732             *out_str++ = ch;
1733         }
1734     *out_str++ = '\0';
1735 	if (strlcpy(outbuf, temp, sizeof (shell_line)) >= sizeof (shell_line))
1736 		error(gettext("Command too long"));
1737     return (changed);
1738 }
1739 
1740 static void
1741 show(register char ch)
1742 {
1743     char cbuf;
1744 
1745     if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
1746         ch += ch == RUBOUT ? -0100 : 0100;
1747         write (2, &CARAT, 1);
1748         promptlen++;
1749     }
1750     cbuf = ch;
1751     write (2, &cbuf, 1);
1752     promptlen++;
1753 }
1754 
1755 static void
1756 error (char *mess)
1757 {
1758     if (clreol)
1759         cleareol ();
1760     else
1761         kill_line ();
1762     promptlen += strlen (mess);
1763     if (enter_standout_mode && exit_standout_mode) {
1764         putp (enter_standout_mode);
1765         pr(mess);
1766         putp (exit_standout_mode);
1767     }
1768     else
1769         pr (mess);
1770     fflush(stdout);
1771     errors++;
1772     longjmp (restore, 1);
1773 }
1774 
1775 
1776 static void
1777 set_tty(void)
1778 {
1779         ioctl(2, TCGETA, &otty);     /* save old tty modes */
1780         ioctl(2, TCGETA, &ntty);
1781         ntty.c_lflag &= ~ECHO & ~ICANON;
1782         ntty.c_cc[VMIN] = (char)1;
1783         ntty.c_cc[VTIME] = (char)0;
1784         ioctl (2, TCSETAF, &ntty);        /* set new tty modes */
1785 }
1786 
1787 static void
1788 reset_tty(void)
1789 {
1790         ioctl (2, TCSETAF, &otty);        /* reset tty modes */
1791 }
1792 
1793 static void
1794 rdline(register FILE *f)
1795 {
1796     register int c;
1797     register char *p;
1798 
1799     p = Line;
1800     while ((c = Getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1)
1801         *p++ = c;
1802     if (c == '\n')
1803         Currline++;
1804     *p = '\0';
1805 }
1806 
1807 /* Come here when we get a suspend signal from the terminal */
1808 
1809 /*
1810  * sig is put in as a dummy arg to have the compiler not to complain
1811  */
1812 #ifdef SIGTSTP
1813 /* ARGSUSED */
1814 void
1815 onsusp(int sig)
1816 {
1817     /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
1818     signal(SIGTTOU, SIG_IGN);
1819     reset_tty ();
1820     fflush (stdout);
1821     signal(SIGTTOU, SIG_DFL);
1822 
1823     /* Send the TSTP signal to suspend our process group */
1824     kill (0, SIGTSTP);
1825     /* Pause for station break */
1826 
1827     /* We're back */
1828     signal (SIGTSTP, onsusp);
1829     set_tty ();
1830     if (inwait)
1831             longjmp (restore, 1);
1832 }
1833 #endif
1834