1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 /*
7  * This code contains changes by
8  * Gunnar Ritter, Freiburg i. Br., Germany, October 2003. All rights reserved.
9  *
10  * Conditions 1, 2, and 4 and the no-warranty notice below apply
11  * to these changes.
12  *
13  *
14  * Copyright (c) 1991
15  * 	The Regents of the University of California.  All rights reserved.
16  *
17  * Redistribution and use in source and binary forms, with or without
18  * modification, are permitted provided that the following conditions
19  * are met:
20  * 1. Redistributions of source code must retain the above copyright
21  *    notice, this list of conditions and the following disclaimer.
22  * 2. Redistributions in binary form must reproduce the above copyright
23  *    notice, this list of conditions and the following disclaimer in the
24  *    documentation and/or other materials provided with the distribution.
25  * 3. All advertising materials mentioning features or use of this software
26  *    must display the following acknowledgement:
27  * 	This product includes software developed by the University of
28  * 	California, Berkeley and its contributors.
29  * 4. Neither the name of the University nor the names of its contributors
30  *    may be used to endorse or promote products derived from this software
31  *    without specific prior written permission.
32  *
33  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
34  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
37  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43  * SUCH DAMAGE.
44  */
45 
46 /*	from 4.3BSD more.c	5.4 (Berkeley) 4/3/86	*/
47 #if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4
48 #define	USED	__attribute__ ((used))
49 #elif defined __GNUC__
50 #define	USED	__attribute__ ((unused))
51 #else
52 #define	USED
53 #endif
54 static const char sccsid[] USED = "@(#)more.sl	1.33 (gritter) 5/29/05";
55 
56 /*
57 ** more.c - General purpose tty output filter and file perusal program
58 **
59 **	by Eric Shienbrood, UC Berkeley
60 **
61 **	modified by Geoff Peck, UCB to add underlining, single spacing
62 **	modified by John Foderaro, UCB to add -c and MORE environment variable
63 */
64 
65 #ifndef	USE_TERMCAP
66 #include <curses.h>
67 #include <term.h>
68 #endif	/* USE_TERMCAP */
69 #include <stdio.h>
70 #include <sys/types.h>
71 #include <ctype.h>
72 #include <signal.h>
73 #include "sigset.h"
74 #include <errno.h>
75 #include <termios.h>
76 #include <setjmp.h>
77 #include <sys/stat.h>
78 #include <locale.h>
79 #include <libgen.h>
80 #include <wchar.h>
81 #include <wctype.h>
82 #include <string.h>
83 #include <stdlib.h>
84 #include <unistd.h>
85 #include <pathconf.h>
86 #include <fcntl.h>
87 #include <signal.h>
88 #include <sys/wait.h>
89 #include <sys/ioctl.h>
90 
91 #ifdef	USE_TERMCAP
92 #include <termcap.h>
93 #endif
94 
95 #include <regex.h>
96 
97 #include <mbtowi.h>
98 
99 #ifdef	__GLIBC__
100 #ifdef	_IO_getc_unlocked
101 #undef	getc
102 #define	getc(f)	_IO_getc_unlocked(f)
103 #endif	/* _IO_getc_unlocked */
104 #ifdef	_IO_putc_unlocked
105 #undef	putchar
106 #define	putchar(c)	_IO_putc_unlocked(c, stdout)
107 #endif	/* _IO_putc_unlocked */
108 #endif	/* __GLIBC__ */
109 
110 #define	getline(f, l)	xxgetline(f, l)	/* avoid glibc _GNU_SOURCE collision */
111 
112 #define Fopen(s,m)	(Currline = 0,file_pos=0,fopen(s,m))
113 #define Ftell(f)	file_pos
114 #define Fseek(f,off)	(file_pos=off,fseeko(f,off,0))
115 #define Getc(f)		(++file_pos, getc(f))
116 #define Ungetc(c,f)	(--file_pos, ungetc(c,f))
117 
118 #define TBUFSIZ	2048
119 static int	LINSIZ;
120 #define ctrl(letter)	(letter & 077)
121 #define RUBOUT	'\177'
122 #define ESC	'\033'
123 #define QUIT	'\034'
124 
125 static struct termios	otty, savtty;
126 static off_t	file_pos, file_size;
127 static int	fnum, no_intty, no_tty;
128 static int	dum_opt, dlines;
129 static int	nscroll = 11;	/* Number of lines scrolled by 'd' */
130 static int	fold_opt = 1;	/* Fold long lines */
131 static int	stop_opt = 1;	/* Stop after form feeds */
132 static int	ssp_opt = 0;	/* Suppress white space */
133 static int	ul_opt = 1;	/* Underline as best we can */
134 static int	promptlen;
135 static int	Currline;	/* Line we are currently at */
136 static int	startup = 1;
137 static int	firstf = 1;
138 static int	notell = 1;
139 static int	docrterase = 0;
140 static int	docrtkill = 0;
141 static int	bad_so;	/* True if overwriting does not turn off standout */
142 static int	inwait, Pause, errors;
143 static int	within;	/* true if we are within a file,
144 			false if we are between files */
145 static int	hard, dumb, noscroll, hardtabs, clreol;
146 static int	catch_susp;	/* We should catch the SIGTSTP signal */
147 static char	**fnames;	/* The list of file names */
148 static int	nfiles;		/* Number of files left to process */
149 static char	*shell;		/* The name of the shell to use */
150 static int	shellp;		/* A previous shell command exists */
151 static char	ch;
152 static jmp_buf	restore;
153 static char	*Line;	/* Line buffer */
154 static int	Lpp = 24;	/* lines per page */
155 static char	*Clear;		/* clear screen */
156 static char	*eraseln;	/* erase line */
157 static char	*Senter, *Sexit;/* enter and exit standout mode */
158 static char	*ULenter, *ULexit;	/* enter and exit underline mode */
159 static char	*chUL;		/* underline character */
160 static char	*chBS;		/* backspace character */
161 static char	*Home;		/* go to home */
162 static char	*cursorm;	/* cursor movement */
163 static char	cursorhome[40];	/* contains cursor movement to home */
164 static char	*EodClr;	/* clear rest of screen */
165 static int	Mcol = 80;	/* number of columns */
166 static int	Wrap = 1;	/* set if automargins */
167 static int	soglitch;	/* terminal has standout mode glitch */
168 static int	ulglitch;	/* terminal has underline mode glitch */
169 static int	pstate = 0;	/* current UL state */
170 static int	mb_cur_max;
171 static int	rflag, wflag;
172 static const char	*progname;
173 static struct {
174     off_t chrctr, line;
175 } context, screen_start;
176 #ifndef	__hpux
177 extern char	PC;		/* pad character */
178 #endif
179 
180 static const char helptext[] ="\
181 Most commands optionally preceded by integer argument k.  Defaults in brackets.\n\
182 Star (*) indicates argument becomes new default.\n\
183 -------------------------------------------------------------------------------\n\
184 <space>			Display next k lines of text [current screen size]\n\
185 z			Display next k lines of text [current screen size]*\n\
186 <return>		Display next k lines of text [1]*\n\
187 d or ctrl-D		Scroll k lines [current scroll size, initially 11]*\n\
188 q or Q or <interrupt>	Exit from more\n\
189 s			Skip forward k lines of text [1]\n\
190 f			Skip forward k screenfuls of text [1]\n\
191 b or ctrl-B		Skip backwards k screenfuls of text [1]\n\
192 '			Go to place where previous search started\n\
193 =			Display current line number\n\
194 /<regular expression>	Search for kth occurrence of regular expression [1]\n\
195 n			Search for kth occurrence of last r.e [1]\n\
196 !<cmd> or :!<cmd>	Execute <cmd> in a subshell\n\
197 v			Start up /usr/ucb/vi at current line\n\
198 ctrl-L			Redraw screen\n\
199 :n			Go to kth next file [1]\n\
200 :p			Go to kth previous file [1]\n\
201 :f			Display current file name and line number\n\
202 .			Repeat previous command\n\
203 -------------------------------------------------------------------------------\n";
204 
205 static void	argscan(const char *);
206 static FILE	*checkf(register const char *, int *);
207 static int	putch(int);
208 static void	screen(register FILE *, register int);
209 static void	onquit(int);
210 static void	chgwinsz(int);
211 static void	end_it(int);
212 static void	copy_file(register FILE *);
213 static int	printd(int);
214 static void	scanstr(int, char *);
215 static void	Sprintf(int);
216 static void	prompt(const char *);
217 static int	getline(register FILE *, int *);
218 static void	eras(register int);
219 static void	kill_line(void);
220 static void	cleareol(void);
221 static void	clreos(void);
222 static int	pr(const char *);
223 static void	prbuf(register const char *, register int);
224 static void	doclear(void);
225 static void	home(void);
226 static int	command(const char *, register FILE *);
227 static int	colon(const char *, int, int);
228 static int	number(char *);
229 static void	do_shell(const char *);
230 static void	search(char *, FILE *, register int);
231 static void	execute(const char *, const char *, const char *, const char *,
232 			const char *, const char *);
233 static void	skiplns(register int, register FILE *);
234 static void	skipf(register int);
235 static void	initterm(void);
236 static char	readch(void);
237 static void	ttyin(char **, register int *, char);
238 static int	expand(char **, size_t *, const char *);
239 static void	show(register char);
240 static void	error(const char *);
241 static void	set_tty(void);
242 static void	reset_tty(void);
243 static void	rdline(register FILE *);
244 static void	onsusp(int);
245 static void	*srealloc(void *, size_t);
246 static void	*smalloc(size_t);
247 
248 int
main(int argc,char ** argv)249 main(int argc, char **argv)
250 {
251     FILE		*f;
252     char		*s;
253     int			ch;
254     int			left, i;
255     int			prnames = 0;
256     int			initopt = 0;
257     int			srchopt = 0;
258     int			clearit = 0;
259     int			initline = 0;
260     char		*initbuf = NULL;
261     int			initsize = 0;
262 
263 #ifdef	__GNUC__
264     (void)&f;
265     (void)&left;
266     (void)&prnames;
267     (void)&initopt;
268     (void)&srchopt;
269     (void)&initline;
270     (void)&initbuf;
271 #endif	/* __GNUC__ */
272     setlocale(LC_CTYPE, "");
273     mb_cur_max = MB_CUR_MAX;
274     progname = basename(argv[0]);
275     nfiles = argc;
276     fnames = argv;
277     initterm ();
278     nscroll = Lpp/2 - 1;
279     if (nscroll <= 0)
280 	nscroll = 1;
281     Line = smalloc(LINSIZ=256);
282     if(s = getenv("MORE")) argscan(s);
283     while (--nfiles > 0) {
284 	if ((ch = (*++fnames)[0]&0377) == '-') {
285 	    argscan(*fnames+1);
286 	}
287 	else if (ch == '+') {
288 	    s = *fnames;
289 	    if (*++s == '/') {
290 		srchopt++;
291 		for (++s, i = 0; *s != '\0';) {
292 		    if (i >= initsize)
293 			initbuf = srealloc(initbuf, initsize = i+1);
294 		    initbuf[i++] = *s++;
295 		}
296 		if (i >= initsize)
297 		    initbuf = srealloc(initbuf, initsize = i+1);
298 		initbuf[i] = '\0';
299 	    }
300 	    else {
301 		initopt++;
302 		for (initline = 0; *s != '\0'; s++)
303 		    if (isdigit (*s))
304 			initline = initline*10 + *s -'0';
305 		--initline;
306 	    }
307 	}
308 	else break;
309     }
310     /* allow clreol only if Home and eraseln and EodClr strings are
311      *  defined, and in that case, make sure we are in noscroll mode
312      */
313     if(clreol)
314     {
315         if((Home == NULL) || (*Home == '\0') ||
316 	   (eraseln == NULL) || (*eraseln == '\0') ||
317            (EodClr == NULL) || (*EodClr == '\0') )
318 	      clreol = 0;
319 	else noscroll = 1;
320     }
321     if (dlines == 0)
322 	dlines = Lpp - (noscroll ? 1 : 2);
323     left = dlines;
324     if (nfiles > 1)
325 	prnames++;
326     if (!no_intty && nfiles == 0) {
327 	fprintf(stderr, "Usage: %s [-dflsucrw] [-n] "
328 			"[+linenum | +/pattern] name1 name2 ...\n", progname);
329 	reset_tty();
330 	exit(1);
331     }
332     else
333 	f = stdin;
334     if (!no_tty) {
335 	sigset(SIGQUIT, onquit);
336 	sigset(SIGINT, end_it);
337 	sigset(SIGWINCH, chgwinsz);
338 	if (sigset (SIGTSTP, SIG_IGN) == SIG_DFL) {
339 	    sigset(SIGTSTP, onsusp);
340 	    catch_susp++;
341 	}
342 	tcgetattr(fileno(stderr), &otty);
343     }
344     if (no_intty) {
345 	if (no_tty)
346 	    copy_file (stdin);
347 	else {
348 	    if ((ch = Getc (f)) == '\f')
349 		doclear();
350 	    else {
351 		Ungetc (ch, f);
352 		if (noscroll && (ch != EOF)) {
353 		    if (clreol)
354 			home ();
355 		    else
356 			doclear ();
357 		}
358 	    }
359 	    if (srchopt)
360 	    {
361 		search (initbuf, stdin, 1);
362 		if (noscroll)
363 		    left--;
364 	    }
365 	    else if (initopt)
366 		skiplns (initline, stdin);
367 	    screen (stdin, left);
368 	}
369 	no_intty = 0;
370 	prnames++;
371 	firstf = 0;
372     }
373 
374     while (fnum < nfiles) {
375 	if ((f = checkf (fnames[fnum], &clearit)) != NULL) {
376 	    context.line = context.chrctr = 0;
377 	    Currline = 0;
378 	    if (firstf) setjmp (restore);
379 	    if (firstf) {
380 		firstf = 0;
381 		if (srchopt)
382 		{
383 		    search (initbuf, f, 1);
384 		    if (noscroll)
385 			left--;
386 		}
387 		else if (initopt)
388 		    skiplns (initline, f);
389 	    }
390 	    else if (fnum < nfiles && !no_tty) {
391 		setjmp (restore);
392 		left = command (fnames[fnum], f);
393 	    }
394 	    if (left != 0) {
395 		if ((noscroll || clearit) && (file_size!=0x7fffffffffffffffLL))
396 		    if (clreol)
397 			home ();
398 		    else
399 			doclear ();
400 		if (prnames) {
401 		    if (bad_so)
402 			eras (0);
403 		    if (clreol)
404 			cleareol ();
405 		    pr("::::::::::::::");
406 		    if (promptlen > 14)
407 			eras (14);
408 		    printf ("\n");
409 		    if(clreol) cleareol();
410 		    printf("%s\n", fnames[fnum]);
411 		    if(clreol) cleareol();
412 		    printf("::::::::::::::\n"/*, fnames[fnum]*/);
413 		    if (left > Lpp - 4)
414 			left = Lpp - 4;
415 		}
416 		if (no_tty)
417 		    copy_file (f);
418 		else {
419 		    within++;
420 		    screen(f, left);
421 		    within = 0;
422 		}
423 	    }
424 	    setjmp (restore);
425 	    fflush(stdout);
426 	    fclose(f);
427 	    screen_start.line = screen_start.chrctr = 0L;
428 	    context.line = context.chrctr = 0L;
429 	}
430 	fnum++;
431 	firstf = 0;
432     }
433     if (wflag && !hard) {
434 	if (Senter && Sexit)
435 	    tputs(Senter, 1, putch);
436 	if (clreol)
437 	    cleareol();
438 	pr("--No more--");
439 	if (clreol)
440 	    clreos();
441 	if (Senter && Sexit)
442 	    tputs(Sexit, 1, putch);
443 	fflush(stdout);
444 	readch();
445 	promptlen = 11;
446 	eras(0);
447 	fflush(stdout);
448     }
449     reset_tty ();
450     exit(0);
451 }
452 
453 static void
argscan(const char * s)454 argscan(const char *s)
455 {
456 	for (dlines = 0; *s != '\0'; s++)
457 	{
458 		switch (*s)
459 		{
460 		  case '0': case '1': case '2':
461 		  case '3': case '4': case '5':
462 		  case '6': case '7': case '8':
463 		  case '9':
464 			dlines = dlines*10 + *s - '0';
465 			break;
466 		  case 'd':
467 			dum_opt = 1;
468 			break;
469 		  case 'l':
470 			stop_opt = 0;
471 			break;
472 		  case 'f':
473 			fold_opt = 0;
474 			break;
475 		  case 'p':
476 			noscroll++;
477 			break;
478 		  case 'c':
479 			clreol++;
480 			break;
481 		  case 's':
482 			ssp_opt = 1;
483 			break;
484 		  case 'u':
485 			ul_opt = 0;
486 			break;
487 		  case 'r':
488 			rflag = 1;
489 			break;
490 		  case 'w':
491 			wflag = 1;
492 			break;
493 		}
494 	}
495 }
496 
497 
498 /*
499 ** Check whether the file named by fs is an ASCII file which the user may
500 ** access.  If it is, return the opened file. Otherwise return NULL.
501 */
502 
503 static FILE *
checkf(register const char * fs,int * clearfirst)504 checkf (register const char *fs, int *clearfirst)
505 {
506     struct stat stbuf;
507     register FILE *f;
508     char b[8];
509     int	n;
510 
511     if (stat (fs, &stbuf) == -1) {
512 	fflush(stdout);
513 	if (clreol)
514 	    cleareol ();
515 	perror(fs);
516 	return (NULL);
517     }
518     if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
519 	printf("\n*** %s: directory ***\n\n", fs);
520 	return (NULL);
521     }
522     if ((f=Fopen(fs, "r")) == NULL) {
523 	fflush(stdout);
524 	perror(fs);
525 	return (NULL);
526     }
527     n = fread(b, 1, sizeof b, f);
528 
529     /* Try to see whether it is an ASCII file */
530 
531     if (n > 1) switch (b[0]&0377) {
532     case 0405:
533     case 0407:
534     case 0410:
535     case 0411:
536     case 0413:
537     not:printf("\n******** %s: Not a text file ********\n\n", fs);
538 	fclose (f);
539 	return (NULL);
540     default:
541 	break;
542     }
543     if (n > 2) switch (((b[1]&0377)<<8) + (b[0]&0377)) {
544     case 0570:
545     case 0430:
546     case 0431:
547     case 0575:
548     case 0502:
549     case 0503:
550     case 0510:
551     case 0511:
552     case 0512:
553     case 0522:
554     case 0514:
555 	goto not;
556     }
557     if (n > 4 && b[0] == '\177' && b[1] == 'E' && b[2] == 'L' && b[3] == 'F')
558 	goto not;
559     if (n > 1 && b[0] == '\f') {
560 	*clearfirst = 1;
561 	fseeko(f, 1, SEEK_SET);
562     } else {
563 	*clearfirst = 0;
564 	fseeko(f, 0, SEEK_SET);
565     }
566     if ((file_size = stbuf.st_size) == 0)
567 	file_size = 0x7fffffffffffffffLL;
568     return (f);
569 }
570 
571 /*
572 ** A real function, for the tputs routine in termlib
573 */
574 
575 static int
putch(int ch)576 putch (int ch)
577 {
578     putchar (ch);
579     return 0;
580 }
581 
582 /*
583 ** Print out the contents of the file f, one screenful at a time.
584 */
585 
586 #define STOP -10
587 
588 static void
screen(register FILE * f,register int num_lines)589 screen (register FILE *f, register int num_lines)
590 {
591     register int c;
592     register int nchars;
593     int length;			/* length of current line */
594     static int prev_len = 1;	/* length of previous line */
595 
596     for (;;) {
597 	while (num_lines > 0 && !Pause) {
598 	    if ((nchars = getline (f, &length)) == EOF)
599 	    {
600 		if (clreol)
601 		    clreos();
602 		return;
603 	    }
604 	    if (ssp_opt && length == 0 && prev_len == 0)
605 		continue;
606 	    prev_len = length;
607 	    if (bad_so || (Senter && *Senter == ' ') && promptlen > 0)
608 		eras (0);
609 	    /* must clear before drawing line since tabs on some terminals
610 	     * do not erase what they tab over.
611 	     */
612 	    if (clreol)
613 		cleareol ();
614 	    prbuf (Line, length);
615 	    if (nchars < promptlen)
616 		eras (nchars);	/* eras () sets promptlen to 0 */
617 	    else promptlen = 0;
618 	    /* is this needed?
619 	     * if (clreol)
620 	     *	cleareol();	/\* must clear again in case we wrapped *
621 	     */
622 	    if (nchars < Mcol || !fold_opt)
623 		prbuf("\n", 1);	/* will turn off UL if necessary */
624 	    if (nchars == STOP)
625 		break;
626 	    num_lines--;
627 	}
628 	if (pstate) {
629 		tputs(ULexit, 1, putch);
630 		pstate = 0;
631 	}
632 	fflush(stdout);
633 	if ((c = Getc(f)) == EOF)
634 	{
635 	    if (clreol)
636 		clreos ();
637 	    return;
638 	}
639 
640 	if (Pause && clreol)
641 	    clreos ();
642 	Ungetc (c, f);
643 	setjmp (restore);
644 	Pause = 0; startup = 0;
645 	if ((num_lines = command (NULL, f)) == 0)
646 	    return;
647 	if (hard && promptlen > 0)
648 		eras (0);
649 	if (noscroll && num_lines >= dlines)
650 	{
651 	    if (clreol)
652 		home();
653 	    else
654 		doclear ();
655 	}
656 	screen_start.line = Currline;
657 	screen_start.chrctr = Ftell (f);
658     }
659 }
660 
661 /*
662 ** Come here if a quit signal is received
663 */
664 
665 static void
onquit(int signo)666 onquit(int signo)
667 {
668     sigset(signo, SIG_IGN);
669     if (!inwait) {
670 	putchar ('\n');
671 	if (!startup) {
672 	    sigset(signo, onquit);
673 	    sigrelse(signo);
674 	    longjmp (restore, 1);
675 	}
676 	else
677 	    Pause++;
678     }
679     else if (!dum_opt && notell) {
680 	write (2, "[Use q or Q to quit]", 20);
681 	promptlen += 20;
682 	notell = 0;
683     }
684     sigset(signo, onquit);
685 }
686 
687 /*
688 ** Come here if a signal for a window size change is received
689 */
690 
691 static void
chgwinsz(int signo)692 chgwinsz(int signo)
693 {
694     struct winsize win;
695 
696     sigset(SIGWINCH, SIG_IGN);
697     if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) {
698 	if (win.ws_row != 0) {
699 	    Lpp = win.ws_row;
700 	    nscroll = Lpp/2 - 1;
701 	    if (nscroll <= 0)
702 		nscroll = 1;
703 	    dlines = Lpp - (noscroll ? 1 : 2);
704 	}
705 	if (win.ws_col != 0)
706 	    Mcol = win.ws_col;
707     }
708     sigset(SIGWINCH, chgwinsz);
709 }
710 
711 /*
712 ** Clean up terminal state and exit. Also come here if interrupt signal received
713 */
714 
715 static void
end_it(int signo)716 end_it (int signo)
717 {
718 
719     reset_tty ();
720     if (clreol) {
721 	putchar ('\r');
722 	clreos ();
723 	fflush (stdout);
724     }
725     else if (!clreol && (promptlen > 0)) {
726 	kill_line ();
727 	fflush (stdout);
728     }
729     else
730 	write (2, "\n", 1);
731     _exit(0);
732 }
733 
734 static void
copy_file(register FILE * f)735 copy_file(register FILE *f)
736 {
737     register int c;
738 
739     while ((c = getc(f)) != EOF)
740 	putchar(c);
741 }
742 
743 /*
744 ** Print an integer as a string of decimal digits,
745 ** returning the length of the print representation.
746 */
747 
748 static int
printd(int n)749 printd (int n)
750 {
751     int a, nchars;
752 
753     if (a = n/10)
754 	nchars = 1 + printd(a);
755     else
756 	nchars = 1;
757     putchar (n % 10 + '0');
758     return (nchars);
759 }
760 
761 /* Put the print representation of an integer into a string */
762 static char *sptr;
763 
764 static void
scanstr(int n,char * str)765 scanstr (int n, char *str)
766 {
767     sptr = str;
768     Sprintf (n);
769     *sptr = '\0';
770 }
771 
772 static void
Sprintf(int n)773 Sprintf (int n)
774 {
775     int a;
776 
777     if (a = n/10)
778 	Sprintf (a);
779     *sptr++ = n % 10 + '0';
780 }
781 
782 static char bel = ctrl('G');
783 
784 static void
prompt(const char * filename)785 prompt (const char *filename)
786 {
787     if (clreol)
788 	cleareol ();
789     else if (promptlen > 0)
790 	kill_line ();
791     if (!hard) {
792 	promptlen = 8;
793 	if (Senter && Sexit) {
794 	    tputs (Senter, 1, putch);
795 	    promptlen += (2 * soglitch);
796 	}
797 	if (clreol)
798 	    cleareol ();
799 	pr("--More--");
800 	if (filename != NULL) {
801 	    promptlen += printf ("(Next file: %s)", filename);
802 	}
803 	else if (!no_intty) {
804 	    promptlen += printf ("(%d%%)", (int)((file_pos * 100) / file_size));
805 	}
806 	if (dum_opt) {
807 	    promptlen += pr("[Press space to continue, 'q' to quit.]");
808 	}
809 	if (Senter && Sexit)
810 	    tputs (Sexit, 1, putch);
811 	if (clreol)
812 	    clreos ();
813 	fflush(stdout);
814     }
815     else
816 	write (2, &bel, 1);
817     inwait++;
818 }
819 
820 /*
821 ** Get a logical line
822 */
823 
824 static int
getline(register FILE * f,int * length)825 getline(register FILE *f, int *length)
826 {
827     register int	c, i = 0;
828     register int	column;
829     static int		colflg;
830     mbstate_t state;
831 
832     memset(&state, 0, sizeof state);
833     column = 0;
834     c = Getc (f);
835     if (colflg && c == '\n') {
836 	Currline++;
837 	c = Getc (f);
838     }
839     for (;;) {
840 	if (i >= LINSIZ-1)
841 	    Line = srealloc(Line, LINSIZ+=256);
842 	if (c == EOF) {
843 	    if (i > 0) {
844 		Line[i] = '\0';
845 		*length = i;
846 		return (column);
847 	    }
848 	    *length = i;
849 	    return (EOF);
850 	}
851 	if (c == '\n') {
852 	    Currline++;
853 	    break;
854 	}
855 	Line[i++] = c;
856 	if (c == '\t')
857 	    if (hardtabs && column < promptlen && !hard) {
858 		if (eraseln && !dumb) {
859 		    column = 1 + (column | 7);
860 		    tputs (eraseln, 1, putch);
861 		    promptlen = 0;
862 		}
863 		else {
864 		    for (--i; column & 7; column++) {
865 			if (i >= LINSIZ-1)
866 			    Line = srealloc(Line, LINSIZ+=256);
867 			Line[i++] = ' ';
868 		    }
869 		    if (column >= promptlen) promptlen = 0;
870 		}
871 	    }
872 	    else
873 		column = 1 + (column | 7);
874 	else if (c == '\b' && column > 0)
875 	    column--;
876 	else if (c == '\r')
877 	    column = 0;
878 	else if (c == '\f' && stop_opt || rflag && !(c&~037)) {
879 		Line[i-1] = '^';
880 		if (i >= LINSIZ-1)
881 		    Line = srealloc(Line, LINSIZ+=256);
882 		Line[i++] = c|0100;
883 		column += 2;
884 		if (c == '\f' && stop_opt)
885 		    Pause++;
886 	}
887 	else if (c == EOF) {
888 	    *length = i;
889 	    return (column);
890 	}
891 	else if ((mb_cur_max == 1 || !(c&0200)) && isprint(c))
892 	    column++;
893 	else if (mb_cur_max > 1 && c&0200) {
894 	    int w, n;
895 	    wchar_t wc;
896 	    int i1;
897 	    i1 = i - 1;
898 	    while ((n = mbrtowc(&wc, &Line[i1], i - i1, &state)) == -2) {
899 		if ((c = Getc(f)) == EOF || c == '\n')
900 		    break;
901 		if (i >= LINSIZ-1)
902 		    Line = srealloc(Line, LINSIZ+=256);
903 		Line[i++] = c;
904 		memset(&state, 0, sizeof state);
905 	    }
906 	    if (n > 0 && (w = wcwidth(wc)) > 0)
907 		column += w;
908 	    else if (n < 0)
909 		memset(&state, 0, sizeof state);
910 	}
911 	if (column >= Mcol && fold_opt) break;
912 	c = Getc (f);
913     }
914     if (column >= Mcol && Mcol > 0) {
915 	if (!Wrap) {
916 	    if (i >= LINSIZ-1)
917 		Line = srealloc(Line, LINSIZ+=256);
918 	    Line[i++] = '\n';
919 	}
920     }
921     colflg = column == Mcol && fold_opt;
922     *length = i;
923     Line[i] = '\0';
924     return (column);
925 }
926 
927 /*
928 ** Erase the rest of the prompt, assuming we are starting at column col.
929 */
930 
931 static void
eras(register int col)932 eras (register int col)
933 {
934 
935     if (promptlen == 0)
936 	return;
937     if (hard) {
938 	putchar ('\n');
939     }
940     else {
941 	if (col == 0)
942 	    putchar ('\r');
943 	if (!dumb && eraseln)
944 	    tputs (eraseln, 1, putch);
945 	else
946 	    for (col = promptlen - col; col > 0; col--)
947 		putchar (' ');
948     }
949     promptlen = 0;
950 }
951 
952 /*
953 ** Erase the current line entirely
954 */
955 
956 static void
kill_line(void)957 kill_line (void)
958 {
959     eras (0);
960     if (!eraseln || dumb) putchar ('\r');
961 }
962 
963 /*
964  * force clear to end of line
965  */
966 static void
cleareol(void)967 cleareol(void)
968 {
969     tputs(eraseln, 1, putch);
970 }
971 
972 static void
clreos(void)973 clreos(void)
974 {
975     tputs(EodClr, 1, putch);
976 }
977 
978 /*
979 **  Print string and return number of characters
980 */
981 
982 static int
pr(const char * s1)983 pr(const char *s1)
984 {
985     register const char	*s;
986     register char	c;
987 
988     for (s = s1; c = *s++; )
989 	putchar(c);
990     return (s - s1 - 1);
991 }
992 
993 
994 /* Print a buffer of n characters */
995 
996 static void
prbuf(register const char * s,register int n)997 prbuf (register const char *s, register int n)
998 {
999     register char c;			/* next output character */
1000     register int state;			/* next output char's UL state */
1001 #define wouldul(s,n)	((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_')))
1002 
1003     while (--n >= 0)
1004 	if (!ul_opt)
1005 	    putchar (*s++);
1006 	else {
1007 	    if (*s == ' ' && pstate == 0 && ulglitch && wouldul(s+1, n-1)) {
1008 		s++;
1009 		continue;
1010 	    }
1011 	    if (state = wouldul(s, n)) {
1012 		c = (*s == '_')? s[2] : *s ;
1013 		n -= 2;
1014 		s += 3;
1015 	    } else
1016 		c = *s++;
1017 	    if (state != pstate) {
1018 		if (c == ' ' && state == 0 && ulglitch && wouldul(s, n-1))
1019 		    state = 1;
1020 		else
1021 		    tputs(state ? ULenter : ULexit, 1, putch);
1022 	    }
1023 	    if (c != ' ' || pstate == 0 || state != 0 || ulglitch == 0)
1024 	        putchar(c);
1025 	    if (mb_cur_max > 1 && c & 0200) {
1026 		    int	x = mblen(&s[-1], mb_cur_max);
1027 		    while (--x > 0) {
1028 			    putchar(*s & 0377);
1029 			    s++;
1030 			    n--;
1031 		    }
1032 	    }
1033 	    if (state && *chUL) {
1034 		pr(chBS);
1035 		tputs(chUL, 1, putch);
1036 	    }
1037 	    pstate = state;
1038 	}
1039 }
1040 
1041 /*
1042 **  Clear the screen
1043 */
1044 
1045 static void
doclear(void)1046 doclear(void)
1047 {
1048     if (Clear && !hard) {
1049 	tputs(Clear, 1, putch);
1050 
1051 	/* Put out carriage return so that system doesn't
1052 	** get confused by escape sequences when expanding tabs
1053 	*/
1054 	putchar ('\r');
1055 	promptlen = 0;
1056     }
1057 }
1058 
1059 /*
1060  * Go to home position
1061  */
1062 static void
home(void)1063 home(void)
1064 {
1065     tputs(Home,1,putch);
1066 }
1067 
1068 static int lastcmd, lastarg, lastp;
1069 static int lastcolon;
1070 static char *shell_line;
1071 static size_t shell_size;
1072 
1073 /*
1074 ** Read a command and do it. A command consists of an optional integer
1075 ** argument followed by the command character.  Return the number of lines
1076 ** to display in the next screenful.  If there is nothing more to display
1077 ** in the current file, zero is returned.
1078 */
1079 
1080 static int
command(const char * filename,register FILE * f)1081 command (const char *filename, register FILE *f)
1082 {
1083     register int nlines;
1084     register int retval = 0;
1085     register int c;
1086     char colonch;
1087     int done;
1088     char comchar;
1089     static char *cmdbuf;
1090     static int cmdsize;
1091 
1092 #define ret(val) retval=val;done++;break
1093 
1094     done = 0;
1095     if (!errors)
1096 	prompt (filename);
1097     else
1098 	errors = 0;
1099     for (;;) {
1100 	nlines = number (&comchar);
1101 	lastp = colonch = 0;
1102 	if (comchar == '.') {	/* Repeat last command */
1103 		lastp++;
1104 		comchar = lastcmd;
1105 		nlines = lastarg;
1106 		if (lastcmd == ':')
1107 			colonch = lastcolon;
1108 	}
1109 	lastcmd = comchar;
1110 	lastarg = nlines;
1111 	if (comchar == otty.c_cc[VERASE]) {
1112 	    kill_line ();
1113 	    prompt (filename);
1114 	    continue;
1115 	}
1116 	switch (comchar) {
1117 	case ':':
1118 	    retval = colon (filename, colonch, nlines);
1119 	    if (retval >= 0)
1120 		done++;
1121 	    break;
1122 	case 'b':
1123 	case ctrl('B'):
1124 	    {
1125 		register int initline;
1126 
1127 		if (no_intty) {
1128 		    write(2, &bel, 1);
1129 		    return (-1);
1130 		}
1131 
1132 		if (nlines == 0) nlines++;
1133 
1134 		putchar ('\r');
1135 		eras (0);
1136 		printf ("\n");
1137 		if (clreol)
1138 			cleareol ();
1139 		printf ("...back %d page", nlines);
1140 		if (nlines > 1)
1141 			pr ("s\n");
1142 		else
1143 			pr ("\n");
1144 
1145 		if (clreol)
1146 			cleareol ();
1147 		pr ("\n");
1148 
1149 		initline = Currline - dlines * (nlines + 1);
1150 		if (! noscroll)
1151 		    --initline;
1152 		if (initline < 0) initline = 0;
1153 		Fseek(f, 0L);
1154 		Currline = 0;	/* skiplns() will make Currline correct */
1155 		skiplns(initline, f);
1156 		if (! noscroll) {
1157 		    ret(dlines + 1);
1158 		}
1159 		else {
1160 		    ret(dlines);
1161 		}
1162 	    }
1163 	case ' ':
1164 	case 'z':
1165 	    if (nlines == 0) nlines = dlines;
1166 	    else if (comchar == 'z') dlines = nlines;
1167 	    ret (nlines);
1168 	case 'd':
1169 	case ctrl('D'):
1170 	    if (nlines != 0) nscroll = nlines;
1171 	    ret (nscroll);
1172 	case 'q':
1173 	case 'Q':
1174 	    end_it (0);
1175 	case 's':
1176 	case 'f':
1177 	    if (nlines == 0) nlines++;
1178 	    if (comchar == 'f')
1179 		nlines *= dlines;
1180 	    putchar ('\r');
1181 	    eras (0);
1182 	    printf ("\n");
1183 	    if (clreol)
1184 		cleareol ();
1185 	    printf ("...skipping %d line", nlines);
1186 	    if (nlines > 1)
1187 		pr ("s\n");
1188 	    else
1189 		pr ("\n");
1190 
1191 	    if (clreol)
1192 		cleareol ();
1193 	    pr ("\n");
1194 
1195 	    while (nlines > 0) {
1196 		while ((c = Getc (f)) != '\n')
1197 		    if (c == EOF) {
1198 			retval = 0;
1199 			done++;
1200 			goto endsw;
1201 		    }
1202 		    Currline++;
1203 		    nlines--;
1204 	    }
1205 	    ret (dlines);
1206 	case '\n':
1207 	    if (nlines != 0)
1208 		dlines = nlines;
1209 	    else
1210 		nlines = 1;
1211 	    ret (nlines);
1212 	case '\f':
1213 	    if (!no_intty) {
1214 		doclear ();
1215 		Fseek (f, screen_start.chrctr);
1216 		Currline = screen_start.line;
1217 		ret (dlines);
1218 	    }
1219 	    else {
1220 		write (2, &bel, 1);
1221 		break;
1222 	    }
1223 	case '\'':
1224 	    if (!no_intty) {
1225 		kill_line ();
1226 		pr ("\n***Back***\n\n");
1227 		Fseek (f, context.chrctr);
1228 		Currline = context.line;
1229 		ret (dlines);
1230 	    }
1231 	    else {
1232 		write (2, &bel, 1);
1233 		break;
1234 	    }
1235 	case '=':
1236 	    kill_line ();
1237 	    promptlen = printd (Currline);
1238 	    fflush (stdout);
1239 	    break;
1240 	case 'n':
1241 	    lastp++;
1242 	case '/':
1243 	    if (nlines == 0) nlines++;
1244 	    kill_line ();
1245 	    pr ("/");
1246 	    promptlen = 1;
1247 	    fflush (stdout);
1248 	    if (lastp) {
1249 		write (2,"\r", 1);
1250 		search (NULL, f, nlines);	/* Use previous r.e. */
1251 	    }
1252 	    else {
1253 		ttyin (&cmdbuf, &cmdsize, '/');
1254 		write (2, "\r", 1);
1255 		search (cmdbuf, f, nlines);
1256 	    }
1257 	    ret (dlines-1);
1258 	case '!':
1259 	    do_shell (filename);
1260 	    break;
1261 	case '?':
1262 	case 'h':
1263 	    if (noscroll) doclear ();
1264 	    fwrite(helptext, 1, sizeof helptext - 1, stdout);
1265 	    prompt (filename);
1266 	    break;
1267 	case 'v':	/* This case should go right before default */
1268 	    if (!no_intty) {
1269 		kill_line ();
1270 		if (cmdsize < 40)
1271 		    cmdbuf = srealloc(cmdbuf, cmdsize = 40);
1272 		cmdbuf[0] = '+';
1273 		scanstr (Currline - dlines < 0 ? 0
1274 				: Currline - (dlines + 1) / 2, &cmdbuf[1]);
1275 		pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]);
1276 		execute (filename, "vi", "vi", cmdbuf, fnames[fnum], 0);
1277 		break;
1278 	    }
1279 	default:
1280 	    if (dum_opt) {
1281    		kill_line ();
1282 		if (Senter && Sexit) {
1283 		    tputs (Senter, 1, putch);
1284 		    promptlen = pr ("[Press 'h' for instructions.]") + (2 * soglitch);
1285 		    tputs (Sexit, 1, putch);
1286 		}
1287 		else
1288 		    promptlen = pr ("[Press 'h' for instructions.]");
1289 		fflush (stdout);
1290 	    }
1291 	    else
1292 		write (2, &bel, 1);
1293 	    break;
1294 	}
1295 	if (done) break;
1296     }
1297     putchar ('\r');
1298 endsw:
1299     inwait = 0;
1300     notell++;
1301     return (retval);
1302 }
1303 
1304 static char ch;
1305 
1306 /*
1307  * Execute a colon-prefixed command.
1308  * Returns <0 if not a command that should cause
1309  * more of the file to be printed.
1310  */
1311 
1312 static int
colon(const char * filename,int cmd,int nlines)1313 colon (const char *filename, int cmd, int nlines)
1314 {
1315 	if (cmd == 0)
1316 		ch = readch ();
1317 	else
1318 		ch = cmd;
1319 	lastcolon = ch;
1320 	switch (ch) {
1321 	case 'f':
1322 		kill_line ();
1323 		if (!no_intty)
1324 			promptlen = printf ("\"%s\" line %d", fnames[fnum], Currline);
1325 		else
1326 			promptlen = printf ("[Not a file] line %d", Currline);
1327 		fflush (stdout);
1328 		return (-1);
1329 	case 'n':
1330 		if (nlines == 0) {
1331 			if (fnum >= nfiles - 1)
1332 				end_it (0);
1333 			nlines++;
1334 		}
1335 		putchar ('\r');
1336 		eras (0);
1337 		skipf (nlines);
1338 		return (0);
1339 	case 'p':
1340 		if (no_intty) {
1341 			write (2, &bel, 1);
1342 			return (-1);
1343 		}
1344 		putchar ('\r');
1345 		eras (0);
1346 		if (nlines == 0)
1347 			nlines++;
1348 		skipf (-nlines);
1349 		return (0);
1350 	case '!':
1351 		do_shell (filename);
1352 		return (-1);
1353 	case 'q':
1354 	case 'Q':
1355 		end_it (0);
1356 	default:
1357 		write (2, &bel, 1);
1358 		return (-1);
1359 	}
1360 }
1361 
1362 /*
1363 ** Read a decimal number from the terminal. Set cmd to the non-digit which
1364 ** terminates the number.
1365 */
1366 
1367 static int
number(char * cmd)1368 number(char *cmd)
1369 {
1370 	register int i;
1371 
1372 	i = 0; ch = otty.c_cc[VKILL];
1373 	for (;;) {
1374 		ch = readch ();
1375 		if (ch >= '0' && ch <= '9')
1376 			i = i*10 + ch - '0';
1377 		else if (ch == otty.c_cc[VKILL])
1378 			i = 0;
1379 		else {
1380 			*cmd = ch;
1381 			break;
1382 		}
1383 	}
1384 	return (i);
1385 }
1386 
1387 static void
do_shell(const char * filename)1388 do_shell (const char *filename)
1389 {
1390 	static char *cmdbuf;
1391 	static int cmdsize;
1392 
1393 	kill_line ();
1394 	pr ("!");
1395 	fflush (stdout);
1396 	promptlen = 1;
1397 	if (lastp)
1398 		pr (shell_line);
1399 	else {
1400 		ttyin (&cmdbuf, &cmdsize, '!');
1401 		if (expand (&shell_line, &shell_size, cmdbuf)) {
1402 			kill_line ();
1403 			promptlen = printf ("!%s", shell_line);
1404 		}
1405 	}
1406 	fflush (stdout);
1407 	write (2, "\n", 1);
1408 	promptlen = 0;
1409 	shellp = 1;
1410 	execute (filename, shell, shell, "-c", shell_line, 0);
1411 }
1412 
1413 /*
1414 ** Search for nth ocurrence of regular expression contained in buf in the file
1415 */
1416 
1417 static void
search(char * buf,FILE * file,register int n)1418 search (char *buf, FILE *file, register int n)
1419 {
1420     off_t startline = Ftell (file);
1421     register off_t line1 = startline;
1422     register off_t line2 = startline;
1423     register off_t line3 = startline;
1424     register int lncount;
1425     int saveln;
1426     static int used;
1427     static regex_t s;
1428 
1429     context.line = saveln = Currline;
1430     context.chrctr = startline;
1431     lncount = 0;
1432     if (buf == NULL && used == 0)
1433 	    error ("No previous regular expression");
1434     if (buf && used) {
1435 	regfree(&s);
1436 	used = 0;
1437     }
1438     /* emulate regcmp()/regex() */
1439     if (buf && regcomp(&s, buf, REG_PLUS|REG_BRACES|REG_PARENS|REG_NOBACKREF|
1440 			    REG_BADRANGE|REG_ESCNL|REG_NOI18N|REG_NOSUB) != 0)
1441 	error ("Regular expression botch");
1442 	/*error (s);*/
1443     used = 1;
1444     while (!feof (file)) {
1445 	line3 = line2;
1446 	line2 = line1;
1447 	line1 = Ftell (file);
1448 	rdline (file);
1449 	lncount++;
1450 	if (regexec(&s, Line, 0, 0, 0) == 0)
1451 		if (--n == 0) {
1452 		    if (lncount > 3 || (lncount > 1 && no_intty))
1453 		    {
1454 			pr ("\n");
1455 			if (clreol)
1456 			    cleareol ();
1457 			pr("...skipping\n");
1458 		    }
1459 		    if (!no_intty) {
1460 			Currline -= (lncount >= 3 ? 3 : lncount);
1461 			Fseek (file, line3);
1462 			if (noscroll)
1463 			    if (clreol) {
1464 				home ();
1465 				cleareol ();
1466 			    }
1467 			    else
1468 				doclear ();
1469 		    }
1470 		    else {
1471 			kill_line ();
1472 			if (noscroll)
1473 			    if (clreol) {
1474 			        home ();
1475 			        cleareol ();
1476 			    }
1477 			    else
1478 				doclear ();
1479 			pr (Line);
1480 			putchar ('\n');
1481 		    }
1482 		    break;
1483 		}
1484     }
1485     if (feof (file)) {
1486 	if (!no_intty) {
1487 	    Currline = saveln;
1488 	    Fseek (file, startline);
1489 	}
1490 	else {
1491 	    pr ("\nPattern not found\n");
1492 	    end_it (0);
1493 	}
1494 	error ("Pattern not found");
1495     }
1496 }
1497 
1498 static void
execute(const char * filename,const char * cmd,const char * arg0,const char * arg1,const char * arg2,const char * arg3)1499 execute (const char *filename, const char *cmd, const char *arg0,
1500 		const char *arg1, const char *arg2, const char *arg3)
1501 {
1502 	int id;
1503 	int n;
1504 
1505 	fflush (stdout);
1506 	reset_tty ();
1507 	for (n = 10; (id = fork ()) < 0 && n > 0; n--)
1508 	    sleep (5);
1509 	if (id == 0) {
1510 	    if (!isatty(0)) {
1511 		close(0);
1512 		open("/dev/tty", 0);
1513 	    }
1514 	    execlp (cmd, arg0, arg1, arg2, arg3, NULL);
1515 	    write (2, "exec failed\n", 12);
1516 	    exit (1);
1517 	}
1518 	if (id > 0) {
1519 	    sigset (SIGINT, SIG_IGN);
1520 	    sigset (SIGQUIT, SIG_IGN);
1521 	    if (catch_susp)
1522 		sigset(SIGTSTP, SIG_DFL);
1523 	    while (wait(0) > 0);
1524 	    sigset (SIGINT, end_it);
1525 	    sigset (SIGQUIT, onquit);
1526 	    if (catch_susp)
1527 		sigset(SIGTSTP, onsusp);
1528 	} else
1529 	    write(2, "can't fork\n", 11);
1530 	set_tty ();
1531 	pr ("------------------------\n");
1532 	prompt (filename);
1533 }
1534 /*
1535 ** Skip n lines in the file f
1536 */
1537 
1538 static void
skiplns(register int n,register FILE * f)1539 skiplns (register int n, register FILE *f)
1540 {
1541     register int c;
1542 
1543     while (n > 0) {
1544 	while ((c = Getc (f)) != '\n')
1545 	    if (c == EOF)
1546 		return;
1547 	    n--;
1548 	    Currline++;
1549     }
1550 }
1551 
1552 /*
1553 ** Skip nskip files in the file list (from the command line). Nskip may be
1554 ** negative.
1555 */
1556 
1557 static void
skipf(register int nskip)1558 skipf (register int nskip)
1559 {
1560     if (nskip == 0) return;
1561     if (nskip > 0) {
1562 	if (fnum + nskip > nfiles - 1)
1563 	    nskip = nfiles - fnum - 1;
1564     }
1565     else if (within)
1566 	++fnum;
1567     fnum += nskip;
1568     if (fnum < 0)
1569 	fnum = 0;
1570     pr ("\n...Skipping ");
1571     pr ("\n");
1572     if (clreol)
1573 	cleareol ();
1574     pr ("...Skipping ");
1575     pr (nskip > 0 ? "to file " : "back to file ");
1576     pr (fnames[fnum]);
1577     pr ("\n");
1578     if (clreol)
1579 	cleareol ();
1580     pr ("\n");
1581     --fnum;
1582 }
1583 
1584 /*----------------------------- Terminal I/O -------------------------------*/
1585 
1586 static void
initterm(void)1587 initterm (void)
1588 {
1589     char	buf[TBUFSIZ];
1590     static char	clearbuf[TBUFSIZ];
1591     char	*clearptr, *padstr;
1592     char	*term;
1593     pid_t	tgrp;
1594     struct winsize win;
1595     long	vdis;
1596 
1597     vdis = fpathconf(fileno(stdout), _PC_VDISABLE);
1598 retry:
1599     if (!(no_tty = tcgetattr(fileno(stdout), &otty))) {
1600 	docrterase = otty.c_cc[VERASE] != vdis;
1601 	docrtkill = otty.c_cc[VKILL] != vdis;
1602 	/*
1603 	 * Wait until we're in the foreground before we save the
1604 	 * the terminal modes.
1605 	 */
1606 	if ((tgrp = tcgetpgrp(fileno(stdout))) < 0) {
1607 	    perror("tcgetpgrp");
1608 	    exit(1);
1609 	}
1610 	if (tgrp != getpgid(0)) {
1611 	    kill(0, SIGTTOU);
1612 	    goto retry;
1613 	}
1614 	if ((term = getenv("TERM")) == 0 || tgetent(buf, term) <= 0) {
1615 	    dumb++; ul_opt = 0;
1616 	}
1617 	else {
1618 	    if (ioctl(fileno(stdout), TIOCGWINSZ, &win) < 0) {
1619 		Lpp = tgetnum("li");
1620 		Mcol = tgetnum("co");
1621 	    } else {
1622 		if ((Lpp = win.ws_row) == 0)
1623 		    Lpp = tgetnum("li");
1624 		if ((Mcol = win.ws_col) == 0)
1625 		    Mcol = tgetnum("co");
1626 	    }
1627 	    if ((Lpp <= 0) || tgetflag("hc")) {
1628 		hard++;	/* Hard copy terminal */
1629 		Lpp = 24;
1630 	    }
1631 	    if (Mcol <= 0)
1632 		Mcol = 80;
1633 
1634 	    if (strcmp (progname, "page") == 0 || !hard && tgetflag("ns"))
1635 		noscroll++;
1636 	    Wrap = tgetflag("am");
1637 	    bad_so = tgetflag ("xs");
1638 	    clearptr = clearbuf;
1639 	    eraseln = tgetstr("ce",&clearptr);
1640 	    Clear = tgetstr("cl", &clearptr);
1641 	    Senter = tgetstr("so", &clearptr);
1642 	    Sexit = tgetstr("se", &clearptr);
1643 	    if ((soglitch = tgetnum("sg")) < 0)
1644 		soglitch = 0;
1645 
1646 	    /*
1647 	     *  Set up for underlining:  some terminals don't need it;
1648 	     *  others have start/stop sequences, still others have an
1649 	     *  underline char sequence which is assumed to move the
1650 	     *  cursor forward one character.  If underline sequence
1651 	     *  isn't available, settle for standout sequence.
1652 	     */
1653 
1654 	    if (tgetflag("ul") || tgetflag("os"))
1655 		ul_opt = 0;
1656 	    if ((chUL = tgetstr("uc", &clearptr)) == NULL )
1657 		chUL = "";
1658 	    if (((ULenter = tgetstr("us", &clearptr)) == NULL ||
1659 	         (ULexit = tgetstr("ue", &clearptr)) == NULL) && !*chUL) {
1660 	        if ((ULenter = Senter) == NULL || (ULexit = Sexit) == NULL) {
1661 			ULenter = "";
1662 			ULexit = "";
1663 		} else
1664 			ulglitch = soglitch;
1665 	    } else {
1666 		if ((ulglitch = tgetnum("ug")) < 0)
1667 		    ulglitch = 0;
1668 	    }
1669 
1670 #ifndef	__hpux
1671 	    if (padstr = tgetstr("pc", &clearptr))
1672 		PC = *padstr;
1673 #endif
1674 	    Home = tgetstr("ho",&clearptr);
1675 	    if (Home == 0 || *Home == '\0')
1676 	    {
1677 		if ((cursorm = tgetstr("cm", &clearptr)) != NULL) {
1678 		    strcpy(cursorhome, tgoto(cursorm, 0, 0));
1679 		    Home = cursorhome;
1680 	       }
1681 	    }
1682 	    EodClr = tgetstr("cd", &clearptr);
1683 	    if ((chBS = tgetstr("bc", &clearptr)) == NULL)
1684 		chBS = "\b";
1685 
1686 	}
1687 	if ((shell = getenv("SHELL")) == NULL)
1688 	    shell = "/bin/sh";
1689     }
1690     no_intty = tcgetattr(fileno(stdin), &otty);
1691     tcgetattr(fileno(stdout), &otty);
1692     savtty = otty;
1693 #if defined (TABDLY) && defined (TAB3)
1694     hardtabs = (otty.c_oflag & TABDLY) != TAB3;
1695 #endif
1696     if (!no_tty) {
1697 	otty.c_lflag &= ~(tcflag_t)(ICANON|ECHO);
1698 	otty.c_cc[VMIN] = 1;
1699 	otty.c_cc[VTIME] = 0;
1700         tcsetattr(fileno(stderr), TCSADRAIN, &otty);
1701     }
1702 }
1703 
1704 static char
readch(void)1705 readch (void)
1706 {
1707 	char ch;
1708 
1709 	if (read (2, &ch, 1) <= 0)
1710 		if (errno != EINTR)
1711 			exit(0);
1712 		else
1713 			ch = otty.c_cc[VKILL];
1714 	return (ch);
1715 }
1716 
1717 static const char BS = '\b';
1718 static const char *BSB = "\b \b";
1719 static const char CARAT = '^';
1720 #define ERASEONECHAR \
1721     if (docrterase) \
1722 	write (2, BSB, sizeof(BSB)); \
1723     else \
1724 	write (2, &BS, sizeof(BS));
1725 
1726 static char *
previous(const char * mb,const char * bottom,wchar_t * wp)1727 previous(const char *mb, const char *bottom, wchar_t *wp)
1728 {
1729 	const char	*p, *lastp;
1730 	wint_t	wc, lastwc = WEOF;
1731 	int	len, max = 0;
1732 
1733 	if (mb <= bottom) {
1734 		*wp = WEOF;
1735 		return (char *)bottom;
1736 	}
1737 	if (mb_cur_max == 1 || !(mb[-1]&0200)) {
1738 		*wp = btowc(mb[-1] & 0377);
1739 		return (char *)&mb[-1];
1740 	}
1741 	p = mb;
1742 	lastp = &mb[-1];
1743 	while (p-- > bottom) {
1744 		mbtowc(NULL, NULL, 0);
1745 		if ((len = mbtowi(&wc, p, mb - p)) >= 0) {
1746 			if (len < max || len < mb - p)
1747 				break;
1748 			max = len;
1749 			lastp = p;
1750 			lastwc = wc;
1751 		} else if (len < 0 && max > 0)
1752 			break;
1753 	}
1754 	*wp = lastwc;
1755 	return (char *)lastp;
1756 }
1757 
1758 static void
ttyin(char ** buf,int * nmax,char pchar)1759 ttyin (char **buf, int *nmax, char pchar)
1760 {
1761     register char ch;
1762     register int slash = 0, i, n, w;
1763     int	maxlen;
1764     char cbuf;
1765     wchar_t wc;
1766 
1767     i = 0;
1768     maxlen = 0;
1769     for (;;) {
1770 	if (promptlen > maxlen) maxlen = promptlen;
1771 	ch = readch ();
1772 	if (ch == '\\') {
1773 	    slash++;
1774 	}
1775 	else if ((ch == otty.c_cc[VERASE]) && !slash) {
1776 	    if (i > 0) {
1777 		n = &(*buf)[i] - previous(&(*buf)[i], *buf, &wc);
1778 		i -= n;
1779 		w = wcwidth(wc);
1780 		if (w > 0)
1781 		    promptlen -= w;
1782 		while (w-- > 0) {
1783 		    ERASEONECHAR
1784 		}
1785 		if (iscntrl((*buf)[i]) && (*buf)[i] != '\n') {
1786 		    --promptlen;
1787 		    ERASEONECHAR
1788 		}
1789 		continue;
1790 	    }
1791 	    else {
1792 		if (!eraseln) promptlen = maxlen;
1793 		longjmp (restore, 1);
1794 	    }
1795 	}
1796 	else if ((ch == otty.c_cc[VKILL]) && !slash) {
1797 	    if (hard) {
1798 		show (ch);
1799 		putchar ('\n');
1800 		putchar (pchar);
1801 	    }
1802 	    else {
1803 		putchar ('\r');
1804 		putchar (pchar);
1805 		if (eraseln)
1806 		    eras (1);
1807 		else if (docrtkill)
1808 		    while (promptlen-- > 1)
1809 			write (2, BSB, sizeof(BSB));
1810 		promptlen = 1;
1811 	    }
1812 	    i = 0;
1813 	    fflush (stdout);
1814 	    continue;
1815 	}
1816 	if (slash && (ch == otty.c_cc[VKILL] || ch == otty.c_cc[VERASE])) {
1817 	    ERASEONECHAR
1818 	    --i;
1819 	}
1820 	if (ch != '\\')
1821 	    slash = 0;
1822 	if (i >= *nmax)
1823 	    *buf = srealloc(*buf, *nmax += 20);
1824 	(*buf)[i++] = ch;
1825 	if ((!(ch&~037) && ch != '\n' && ch != ESC) || ch == RUBOUT) {
1826 	    ch += ch == RUBOUT ? -0100 : 0100;
1827 	    write (2, &CARAT, 1);
1828 	    promptlen++;
1829 	}
1830 	cbuf = ch;
1831 	if (ch != '\n' && ch != ESC) {
1832 	    write (2, &cbuf, 1);
1833 	    promptlen++;
1834 	}
1835 	else
1836 	    break;
1837     }
1838     if (i >= *nmax)
1839 	*buf = srealloc(*buf, *nmax += 20);
1840     (*buf)[--i] = '\0';
1841     if (!eraseln) promptlen = maxlen;
1842 }
1843 
1844 static int
expand(char ** outbuf,size_t * outsize,const char * inbuf)1845 expand (char **outbuf, size_t *outsize, const char *inbuf)
1846 {
1847     register const char *instr;
1848     register int n, o;
1849     register char ch;
1850     char *temp = NULL;
1851     int size = 0;
1852     int changed = 0;
1853 
1854     instr = inbuf;
1855     o = 0;
1856     while ((ch = *instr++) != '\0')
1857 	switch (ch) {
1858 	case '%':
1859 	    if (!no_intty) {
1860 		n = strlen(fnames[fnum]);
1861 		if (o + n + 1 >= size)
1862 			temp = srealloc(temp, size = o + n + 1);
1863 		strcpy(&temp[o], fnames[fnum]);
1864 		o += n;
1865 		changed++;
1866 	    }
1867 	    else {
1868 		if (o >= size)
1869 		    temp = srealloc(temp, ++size);
1870 		temp[o++] = ch;
1871 	    }
1872 	    break;
1873 	case '!':
1874 	    if (!shellp) {
1875 		free(temp);
1876 		error ("No previous command to substitute for");
1877 	    }
1878 	    n = strlen(shell_line);
1879 	    if (o + n + 1 >= size)
1880 		temp = srealloc(temp, size = o + n + 1);
1881 	    strcpy(&temp[o], shell_line);
1882 	    o += n;
1883 	    changed++;
1884 	    break;
1885 	case '\\':
1886 	    if (*instr == '%' || *instr == '!') {
1887 		if (o >= size)
1888 		    temp = srealloc(temp, ++size);
1889 		temp[o++] = *instr++;
1890 		break;
1891 	    }
1892 	default:
1893 	    if (o >= size)
1894 		temp = srealloc(temp, ++size);
1895 	    temp[o++] = ch;
1896 	}
1897     if (o >= size)
1898 	temp = srealloc(temp, ++size);
1899     temp[o++] = '\0';
1900     if (o > *outsize)
1901 	    *outbuf = srealloc(*outbuf, *outsize = o);
1902     strcpy (*outbuf, temp);
1903     return (changed);
1904 }
1905 
1906 static void
show(register char ch)1907 show (register char ch)
1908 {
1909     char cbuf;
1910 
1911     if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
1912 	ch += ch == RUBOUT ? -0100 : 0100;
1913 	write (2, &CARAT, 1);
1914 	promptlen++;
1915     }
1916     cbuf = ch;
1917     write (2, &cbuf, 1);
1918     promptlen++;
1919 }
1920 
1921 static void
error(const char * mess)1922 error (const char *mess)
1923 {
1924     if (clreol)
1925 	cleareol ();
1926     else
1927 	kill_line ();
1928     promptlen += strlen (mess);
1929     if (Senter && Sexit) {
1930 	tputs (Senter, 1, putch);
1931 	pr(mess);
1932 	tputs (Sexit, 1, putch);
1933     }
1934     else
1935 	pr (mess);
1936     fflush(stdout);
1937     errors++;
1938     longjmp (restore, 1);
1939 }
1940 
1941 
1942 static void
set_tty(void)1943 set_tty (void)
1944 {
1945 	if (!no_tty) {
1946 	    otty.c_cc[VMIN] = 1;
1947 	    otty.c_cc[VTIME] = 0;
1948 	    otty.c_lflag &= ~(tcflag_t)(ICANON|ECHO);
1949 	    tcsetattr(fileno(stderr), TCSADRAIN, &otty);
1950 	}
1951 }
1952 
1953 static void
reset_tty(void)1954 reset_tty (void)
1955 {
1956     if (pstate) {
1957 	tputs(ULexit, 1, putch);
1958 	fflush(stdout);
1959 	pstate = 0;
1960     }
1961     if (!no_tty) {
1962 	otty.c_lflag |= ICANON|ECHO;
1963 	tcsetattr(fileno(stderr), TCSADRAIN, &savtty);
1964     }
1965 }
1966 
1967 /* Since this is used for searches only, filter backspace sequences */
1968 static void
rdline(register FILE * f)1969 rdline (register FILE *f)
1970 {
1971     register int c;
1972     wint_t wc, lastwc = '\b';
1973     wchar_t wb;
1974     register int i = 0, m, n, lastn = 0;
1975     mbstate_t state;
1976 
1977     memset(&state, 0, sizeof state);
1978     while ((c = Getc (f)) != '\n' && c != EOF) {
1979 	if (i >= LINSIZ-1)
1980 	    Line = srealloc(Line, LINSIZ+=256);
1981 	Line[i++] = c;
1982 	n = 1;
1983 	if (mb_cur_max > 1 && c&0200) {
1984 	    while ((m = mbrtowc(&wb, &Line[i-1], 1, &state)) == -2) {
1985 		if ((c = Getc(f)) == EOF || c == '\n')
1986 		    break;
1987 		if (i >= LINSIZ-1)
1988 		    Line = srealloc(Line, LINSIZ+=256);
1989 		Line[i++] = c;
1990 		n++;
1991 	    }
1992 	    wc = wb;
1993 	    if (m < 0) {
1994 		memset(&state, 0, sizeof state);
1995 		wc = WEOF;
1996 		n = 1;
1997 	    } else if (m == 0)
1998 		n = 1;
1999 	} else
2000 	    wc = c;
2001 	if (wc == '\b' && lastwc != '\b')
2002 	    i -= lastn + 1;
2003 	lastn = n;
2004 	lastwc = wc;
2005     }
2006     if (c == '\n')
2007 	Currline++;
2008     Line[i] = '\0';
2009 }
2010 
2011 /* Come here when we get a suspend signal from the terminal */
2012 
2013 static void
onsusp(int signo)2014 onsusp (int signo)
2015 {
2016     /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
2017     sigset(SIGTTOU, SIG_IGN);
2018     reset_tty ();
2019     fflush (stdout);
2020     sigset(SIGTTOU, SIG_DFL);
2021     /* Send the TSTP signal to suspend our process group */
2022     sigset(SIGTSTP, SIG_DFL);
2023     sigrelse(SIGTSTP);
2024     kill (0, SIGTSTP);
2025     /* Pause for station break */
2026 
2027     /* We're back */
2028     sigset (SIGTSTP, onsusp);
2029     set_tty ();
2030     if (inwait)
2031 	    longjmp (restore, 1);
2032 }
2033 
2034 static void *
srealloc(void * vp,size_t sz)2035 srealloc(void *vp, size_t sz)
2036 {
2037 	if ((vp = realloc(vp, sz)) == NULL) {
2038 		write(2, "no memory\n", 10);
2039 		reset_tty();
2040 		_exit(077);
2041 	}
2042 	return vp;
2043 }
2044 
2045 static void *
smalloc(size_t sz)2046 smalloc(size_t sz)
2047 {
2048 	return srealloc(0, sz);
2049 }
2050