1 /*
2  * Copyright (C) 1980 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 /* more.c - General purpose tty output filter and file perusal program
19  *
20  * by Eric Shienbrood, UC Berkeley
21  *
22  * modified by Geoff Peck
23  *	UCB to add underlining, single spacing
24  * modified by John Foderaro
25  *	UCB to add -c and MORE environment variable
26  * modified by Erik Troan <ewt@redhat.com>
27  *	to be more posix and so compile on linux/axp.
28  * modified by Kars de Jong <jongk@cs.utwente.nl>
29  *	to use terminfo instead of termcap.
30  * 1999-02-22 Arkadiusz Miśkiewicz <misiek@pld.ORG.PL>
31  *	added Native Language Support
32  * 1999-03-19 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
33  *	more nls translatable strings
34  * 1999-05-09 aeb
35  *	applied a RedHat patch (setjmp->sigsetjmp); without it a second
36  *	^Z would fail.
37  * 1999-05-09 aeb
38  *	undone Kars' work, so that more works without libcurses (and
39  *	hence can be in /bin with libcurses being in
40  *	/usr/lib which may not be mounted).  However, when termcap is not
41  *	present curses can still be used.
42  * 2010-10-21 Davidlohr Bueso <dave@gnu.org>
43  *	modified mem allocation handling for util-linux
44  */
45 
46 #include <stdio.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <stdlib.h>		/* for alloca() */
50 #include <stdarg.h>		/* for va_start() etc */
51 #include <sys/param.h>
52 #include <ctype.h>
53 #include <signal.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <termios.h>
57 #include <setjmp.h>
58 #include <sys/ioctl.h>
59 #include <sys/stat.h>
60 #include <sys/file.h>
61 #include <sys/wait.h>
62 
63 #include "strutils.h"
64 #include "nls.h"
65 #include "xalloc.h"
66 #include "widechar.h"
67 #include "closestream.h"
68 
69 #include <regex.h>
70 
71 #ifdef TEST_PROGRAM
72 # define NON_INTERACTIVE_MORE 1
73 #endif
74 
75 #ifndef XTABS
76 # define XTABS	TAB3
77 #endif
78 
79 #define VI	"vi"	/* found on the user's path */
80 
81 #define Fopen(s,m)	(Currline = 0,file_pos=0,fopen(s,m))
82 #define Ftell(f)	file_pos
83 #define Fseek(f,off)	(file_pos=off,fseek(f,off,0))
84 #define Getc(f)		(++file_pos, getc(f))
85 #define Ungetc(c,f)	(--file_pos, ungetc(c,f))
86 #define putcerr(c)	fputc(c, stderr)
87 #define putserr(s)	fputs(s, stderr)
88 #define putsout(s)	fputs(s, stdout)
89 
90 #define stty(fd,argp)  tcsetattr(fd,TCSANOW,argp)
91 
92 /* some function declarations */
93 void initterm(void);
94 void kill_line(void);
95 void doclear(void);
96 void cleareol(void);
97 void clreos(void);
98 void home(void);
99 void more_error(char *mess);
100 void do_shell(char *filename);
101 int colon(char *filename, int cmd, int nlines);
102 int expand(char **outbuf, char *inbuf);
103 void argscan(char *s);
104 void rdline(register FILE *f);
105 void copy_file(register FILE *f);
106 void search(char buf[], FILE *file, register int n);
107 void skipf(register int nskip);
108 void skiplns(register int n, register FILE *f);
109 void screen(register FILE *f, register int num_lines);
110 int command(char *filename, register FILE *f);
111 void erasep(register int col);
112 void show(register char ch);
113 void set_tty(void);
114 void reset_tty(void);
115 void ttyin(char buf[], register int nmax, char pchar);
116 int number(char *cmd);
117 int readch(void);
118 int get_line(register FILE *f, int *length);
119 void prbuf(register char *s, register int n);
120 void execute(char *filename, char *cmd, ...);
121 FILE *checkf(char *, int *);
122 void prepare_line_buffer(void);
123 
124 #define TBUFSIZ		1024
125 #define LINSIZ		256	/* minimal Line buffer size */
126 #define ctrl(letter)	(letter & 077)
127 #define RUBOUT		'\177'
128 #define ESC		'\033'
129 #define QUIT		'\034'
130 #define SCROLL_LEN	11
131 #define LINES_PER_PAGE	24
132 #define NUM_COLUMNS	80
133 #define TERMINAL_BUF	4096
134 #define INIT_BUF	80
135 #define SHELL_LINE	1000
136 #define COMMAND_BUF	200
137 #define REGERR_BUF	NUM_COLUMNS
138 
139 struct termios otty, savetty0;
140 long file_pos, file_size;
141 int fnum, no_intty, no_tty, slow_tty;
142 int dum_opt, dlines;
143 void onquit(int), onsusp(int), chgwinsz(int), end_it(int);
144 int nscroll = SCROLL_LEN;	/* Number of lines scrolled by 'd' */
145 int fold_opt = 1;		/* Fold long lines */
146 int stop_opt = 1;		/* Stop after form feeds */
147 int ssp_opt = 0;		/* Suppress white space */
148 int ul_opt = 1;			/* Underline as best we can */
149 int promptlen;
150 int Currline;			/* Line we are currently at */
151 int startup = 1;
152 int firstf = 1;
153 int notell = 1;
154 int docrterase = 0;
155 int docrtkill = 0;
156 int bad_so;			/* True if overwriting does not turn
157 				   off standout */
158 int inwait, Pause, errors;
159 int within;			/* true if we are within a file,
160 				   false if we are between files */
161 int hard, dumb, noscroll, hardtabs, clreol, eatnl;
162 int catch_susp;			/* We should catch the SIGTSTP signal */
163 char **fnames;			/* The list of file names */
164 int nfiles;			/* Number of files left to process */
165 char *shell;			/* The name of the shell to use */
166 int shellp;			/* A previous shell command exists */
167 sigjmp_buf restore;
168 char *Line;			/* Line buffer */
169 size_t LineLen;			/* size of Line buffer */
170 int Lpp = LINES_PER_PAGE;	/* lines per page */
171 char *Clear;			/* clear screen */
172 char *eraseln;			/* erase line */
173 char *Senter, *Sexit;		/* enter and exit standout mode */
174 char *ULenter, *ULexit;		/* enter and exit underline mode */
175 char *chUL;			/* underline character */
176 char *chBS;			/* backspace character */
177 char *Home;			/* go to home */
178 char *cursorm;			/* cursor movement */
179 char cursorhome[40];		/* contains cursor movement to home */
180 char *EodClr;			/* clear rest of screen */
181 int Mcol = NUM_COLUMNS;		/* number of columns */
182 int Wrap = 1;			/* set if automargins */
183 int soglitch;			/* terminal has standout mode glitch */
184 int ulglitch;			/* terminal has underline mode glitch */
185 int pstate = 0;			/* current UL state */
186 static int magic(FILE *, char *);
187 char *previousre;		/* previous search() buf[] item */
188 struct {
189 	long chrctr, line;
190 } context, screen_start;
191 extern char PC;			/* pad character */
192 
193 #ifdef HAVE_NCURSES_H
194 # include <ncurses.h>
195 #elif defined(HAVE_NCURSES_NCURSES_H)
196 # include <ncurses/ncurses.h>
197 #endif
198 
199 #if defined(HAVE_NCURSES_H) || defined(HAVE_NCURSES_NCURSES_H)
200 # include <term.h>		/* include after <curses.h> */
201 
202 # define TERM_AUTO_RIGHT_MARGIN    "am"
203 # define TERM_CEOL                 "xhp"
204 # define TERM_CLEAR                "clear"
205 # define TERM_CLEAR_TO_LINE_END    "el"
206 # define TERM_CLEAR_TO_SCREEN_END  "ed"
207 # define TERM_COLS                 "cols"
208 # define TERM_CURSOR_ADDRESS       "cup"
209 # define TERM_EAT_NEW_LINE         "xenl"
210 # define TERM_ENTER_UNDERLINE      "smul"
211 # define TERM_EXIT_STANDARD_MODE   "rmso"
212 # define TERM_EXIT_UNDERLINE       "rmul"
213 # define TERM_HARD_COPY            "hc"
214 # define TERM_HOME                 "home"
215 # define TERM_LINE_DOWN            "cud1"
216 # define TERM_LINES                "lines"
217 # define TERM_OVER_STRIKE          "os"
218 # define TERM_PAD_CHAR             "pad"
219 # define TERM_STANDARD_MODE        "smso"
220 # define TERM_STD_MODE_GLITCH      "xmc"
221 # define TERM_UNDERLINE_CHAR       "uc"
222 # define TERM_UNDERLINE            "ul"
223 
my_putstring(char * s)224 static void my_putstring(char *s)
225 {
226 	tputs(s, fileno(stdout), putchar);	/* putp(s); */
227 }
228 
my_setupterm(char * term,int fildes,int * errret)229 static void my_setupterm(char *term, int fildes, int *errret)
230 {
231 	setupterm(term, fildes, errret);
232 }
233 
my_tgetnum(char * s)234 static int my_tgetnum(char *s)
235 {
236 	return tigetnum(s);
237 }
238 
my_tgetflag(char * s)239 static int my_tgetflag(char *s)
240 {
241 	return tigetflag(s);
242 }
243 
my_tgetstr(char * s)244 static char *my_tgetstr(char *s)
245 {
246 	return tigetstr(s);
247 }
248 
my_tgoto(char * cap,int col,int row)249 static char *my_tgoto(char *cap, int col, int row)
250 {
251 	return tparm(cap, col, row);
252 }
253 
254 #elif defined(HAVE_LIBTERMCAP)	/* ncurses not found */
255 
256 # include <termcap.h>
257 
258 # define TERM_AUTO_RIGHT_MARGIN    "am"
259 # define TERM_CEOL                 "xs"
260 # define TERM_CLEAR                "cl"
261 # define TERM_CLEAR_TO_LINE_END    "ce"
262 # define TERM_CLEAR_TO_SCREEN_END  "cd"
263 # define TERM_COLS                 "co"
264 # define TERM_CURSOR_ADDRESS       "cm"
265 # define TERM_EAT_NEW_LINE         "xn"
266 # define TERM_ENTER_UNDERLINE      "us"
267 # define TERM_EXIT_STANDARD_MODE   "se"
268 # define TERM_EXIT_UNDERLINE       "ue"
269 # define TERM_HARD_COPY            "hc"
270 # define TERM_HOME                 "ho"
271 # define TERM_LINE_DOWN            "le"
272 # define TERM_LINES                "li"
273 # define TERM_OVER_STRIKE          "os"
274 # define TERM_PAD_CHAR             "pc"
275 # define TERM_STANDARD_MODE        "so"
276 # define TERM_STD_MODE_GLITCH      "sg"
277 # define TERM_UNDERLINE_CHAR       "uc"
278 # define TERM_UNDERLINE            "ul"
279 
280 char termbuffer[TERMINAL_BUF];
281 char tcbuffer[TERMINAL_BUF];
282 char *strbuf = termbuffer;
283 
my_putstring(char * s)284 static void my_putstring(char *s)
285 {
286 	tputs(s, fileno(stdout), putchar);
287 }
288 
my_setupterm(char * term,int fildes,int * errret)289 static void my_setupterm(char *term, int fildes __attribute__((__unused__)), int *errret)
290 {
291 	*errret = tgetent(tcbuffer, term);
292 }
293 
my_tgetnum(char * s)294 static int my_tgetnum(char *s)
295 {
296 	return tgetnum(s);
297 }
298 
my_tgetflag(char * s)299 static int my_tgetflag(char *s)
300 {
301 	return tgetflag(s);
302 }
303 
my_tgetstr(char * s)304 static char *my_tgetstr(char *s)
305 {
306 	return tgetstr(s, &strbuf);
307 }
308 
my_tgoto(char * cap,int col,int row)309 static char *my_tgoto(char *cap, int col, int row)
310 {
311 	return tgoto(cap, col, row);
312 }
313 
314 #endif	/* HAVE_LIBTERMCAP */
315 
usage(FILE * out)316 static void __attribute__((__noreturn__)) usage(FILE *out)
317 {
318 	fputs(USAGE_HEADER, out);
319 	fprintf(out, _(" %s [options] <file>...\n"), program_invocation_short_name);
320 	fputs(USAGE_OPTIONS, out);
321 	fputs(_(" -d          display help instead of ringing bell\n"), out);
322 	fputs(_(" -f          count logical rather than screen lines\n"), out);
323 	fputs(_(" -l          suppress pause after form feed\n"), out);
324 	fputs(_(" -c          do not scroll, display text and clean line ends\n"), out);
325 	fputs(_(" -p          do not scroll, clean screen and display text\n"), out);
326 	fputs(_(" -s          squeeze multiple blank lines into one\n"), out);
327 	fputs(_(" -u          suppress underlining\n"), out);
328 	fputs(_(" -<number>   the number of lines per screenful\n"), out);
329 	fputs(_(" +<number>   display file beginning from line number\n"), out);
330 	fputs(_(" +/<string>  display file beginning from search string match\n"), out);
331 	fputs(_(" -V          display version information and exit\n"), out);
332 	fprintf(out, USAGE_MAN_TAIL("more(1)"));
333 	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
334 }
335 
main(int argc,char ** argv)336 int main(int argc, char **argv)
337 {
338 	FILE *f;
339 	char *s;
340 	int ch;
341 	int left;
342 	int prnames = 0;
343 	int initopt = 0;
344 	int srchopt = 0;
345 	int clearit = 0;
346 	int initline = 0;
347 	char *initbuf = NULL;
348 
349 	setlocale(LC_ALL, "");
350 	bindtextdomain(PACKAGE, LOCALEDIR);
351 	textdomain(PACKAGE);
352 	atexit(close_stdout);
353 
354 	nfiles = argc;
355 	fnames = argv;
356 	setlocale(LC_ALL, "");
357 	initterm();
358 
359 	/* Auto set no scroll on when binary is called page */
360 	if (!(strcmp(program_invocation_short_name, "page")))
361 		noscroll++;
362 
363 	prepare_line_buffer();
364 
365 	nscroll = Lpp / 2 - 1;
366 	if (nscroll <= 0)
367 		nscroll = 1;
368 
369 	if ((s = getenv("MORE")) != NULL)
370 		argscan(s);
371 
372 	while (--nfiles > 0) {
373 		if ((ch = (*++fnames)[0]) == '-') {
374 			argscan(*fnames + 1);
375 		} else if (ch == '+') {
376 			s = *fnames;
377 			if (*++s == '/') {
378 				srchopt++;
379 				initbuf = xstrdup(s + 1);
380 			} else {
381 				initopt++;
382 				for (initline = 0; *s != '\0'; s++)
383 					if (isdigit(*s))
384 						initline =
385 						    initline * 10 + *s - '0';
386 				--initline;
387 			}
388 		} else
389 			break;
390 	}
391 	/* allow clreol only if Home and eraseln and EodClr strings are
392 	 * defined, and in that case, make sure we are in noscroll mode */
393 	if (clreol) {
394 		if ((Home == NULL) || (*Home == '\0') ||
395 		    (eraseln == NULL) || (*eraseln == '\0') ||
396 		    (EodClr == NULL) || (*EodClr == '\0'))
397 			clreol = 0;
398 		else
399 			noscroll = 1;
400 	}
401 	if (dlines == 0)
402 		dlines = Lpp - 1;	/* was: Lpp - (noscroll ? 1 : 2) */
403 	left = dlines;
404 	if (nfiles > 1)
405 		prnames++;
406 	if (!no_intty && nfiles == 0)
407 		usage(stderr);
408 	else
409 		f = stdin;
410 	if (!no_tty) {
411 		signal(SIGQUIT, onquit);
412 		signal(SIGINT, end_it);
413 #ifdef SIGWINCH
414 		signal(SIGWINCH, chgwinsz);
415 #endif
416 		if (signal(SIGTSTP, SIG_IGN) == SIG_DFL) {
417 			signal(SIGTSTP, onsusp);
418 			catch_susp++;
419 		}
420 		stty(fileno(stderr), &otty);
421 	}
422 	if (no_intty) {
423 		if (no_tty)
424 			copy_file(stdin);
425 		else {
426 			if ((ch = Getc(f)) == '\f')
427 				doclear();
428 			else {
429 				Ungetc(ch, f);
430 				if (noscroll && (ch != EOF)) {
431 					if (clreol)
432 						home();
433 					else
434 						doclear();
435 				}
436 			}
437 			if (srchopt) {
438 				free(previousre);
439 				previousre = xstrdup(initbuf);
440 				search(initbuf, stdin, 1);
441 				if (noscroll)
442 					left--;
443 			} else if (initopt)
444 				skiplns(initline, stdin);
445 			screen(stdin, left);
446 		}
447 		no_intty = 0;
448 		prnames++;
449 		firstf = 0;
450 	}
451 
452 	while (fnum < nfiles) {
453 		if ((f = checkf(fnames[fnum], &clearit)) != NULL) {
454 			context.line = context.chrctr = 0;
455 			Currline = 0;
456 			if (firstf)
457 				sigsetjmp(restore, 1);
458 			if (firstf) {
459 				firstf = 0;
460 				if (srchopt) {
461 					free(previousre);
462 					previousre = xstrdup(initbuf);
463 					search(initbuf, f, 1);
464 					if (noscroll)
465 						left--;
466 				} else if (initopt)
467 					skiplns(initline, f);
468 			} else if (fnum < nfiles && !no_tty) {
469 				sigsetjmp(restore, 1);
470 				left = command(fnames[fnum], f);
471 			}
472 			if (left != 0) {
473 				if ((noscroll || clearit)
474 				    && (file_size != LONG_MAX)) {
475 					if (clreol)
476 						home();
477 					else
478 						doclear();
479 				}
480 				if (prnames) {
481 					if (bad_so)
482 						erasep(0);
483 					if (clreol)
484 						cleareol();
485 					putsout("::::::::::::::");
486 					if (promptlen > 14)
487 						erasep(14);
488 					putchar('\n');
489 					if (clreol)
490 						cleareol();
491 					puts(fnames[fnum]);
492 					if (clreol)
493 						cleareol();
494 					puts("::::::::::::::");
495 					if (left > Lpp - 4)
496 						left = Lpp - 4;
497 				}
498 				if (no_tty)
499 					copy_file(f);
500 				else {
501 					within++;
502 					screen(f, left);
503 					within = 0;
504 				}
505 			}
506 			sigsetjmp(restore, 1);
507 			fflush(stdout);
508 			fclose(f);
509 			screen_start.line = screen_start.chrctr = 0L;
510 			context.line = context.chrctr = 0L;
511 		}
512 		fnum++;
513 		firstf = 0;
514 	}
515 	free(previousre);
516 	free(initbuf);
517 	free(Line);
518 	reset_tty();
519 	exit(EXIT_SUCCESS);
520 }
521 
argscan(char * s)522 void argscan(char *s)
523 {
524 	int seen_num = 0;
525 
526 	while (*s != '\0') {
527 		switch (*s) {
528 		case '0':
529 		case '1':
530 		case '2':
531 		case '3':
532 		case '4':
533 		case '5':
534 		case '6':
535 		case '7':
536 		case '8':
537 		case '9':
538 			if (!seen_num) {
539 				dlines = 0;
540 				seen_num = 1;
541 			}
542 			dlines = dlines * 10 + *s - '0';
543 			break;
544 		case 'd':
545 			dum_opt = 1;
546 			break;
547 		case 'l':
548 			stop_opt = 0;
549 			break;
550 		case 'f':
551 			fold_opt = 0;
552 			break;
553 		case 'p':
554 			noscroll++;
555 			break;
556 		case 'c':
557 			clreol++;
558 			break;
559 		case 's':
560 			ssp_opt = 1;
561 			break;
562 		case 'u':
563 			ul_opt = 0;
564 			break;
565 		case '-':
566 		case ' ':
567 		case '\t':
568 			break;
569 		case 'V':
570 			printf(UTIL_LINUX_VERSION);
571 			exit(EXIT_SUCCESS);
572 			break;
573 		default:
574 			warnx(_("unknown option -%s"), s);
575 			usage(stderr);
576 			break;
577 		}
578 		s++;
579 	}
580 }
581 
582 /* Check whether the file named by fs is an ASCII file which the user may
583  * access.  If it is, return the opened file.  Otherwise return NULL. */
checkf(register char * fs,int * clearfirst)584 FILE *checkf(register char *fs, int *clearfirst)
585 {
586 	struct stat stbuf;
587 	register FILE *f;
588 	int c;
589 
590 	if (stat(fs, &stbuf) == -1) {
591 		fflush(stdout);
592 		if (clreol)
593 			cleareol();
594 		perror(fs);
595 		return ((FILE *)NULL);
596 	}
597 	if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
598 		printf(_("\n*** %s: directory ***\n\n"), fs);
599 		return ((FILE *)NULL);
600 	}
601 	if ((f = Fopen(fs, "r")) == NULL) {
602 		fflush(stdout);
603 		perror(fs);
604 		return ((FILE *)NULL);
605 	}
606 	if (magic(f, fs)) {
607 		fclose(f);
608 		return ((FILE *)NULL);
609 	}
610 	fcntl(fileno(f), F_SETFD, FD_CLOEXEC);
611 	c = Getc(f);
612 	*clearfirst = (c == '\f');
613 	Ungetc(c, f);
614 	if ((file_size = stbuf.st_size) == 0)
615 		file_size = LONG_MAX;
616 	return (f);
617 }
618 
619 /* magic --
620  *	check for file magic numbers.  This code would best be shared
621  *	with the file(1) program or, perhaps, more should not try to be
622  *	so smart. */
magic(FILE * f,char * fs)623 static int magic(FILE *f, char *fs)
624 {
625 	signed char twobytes[2];
626 
627 	/* don't try to look ahead if the input is unseekable */
628 	if (fseek(f, 0L, SEEK_SET))
629 		return 0;
630 
631 	if (fread(twobytes, 2, 1, f) == 1) {
632 		switch (twobytes[0] + (twobytes[1] << 8)) {
633 		case 0407:	/* a.out obj */
634 		case 0410:	/* a.out exec */
635 		case 0413:	/* a.out demand exec */
636 		case 0405:
637 		case 0411:
638 		case 0177545:
639 		case 0x457f:	/* simple ELF detection */
640 			printf(_("\n******** %s: Not a text file ********\n\n"),
641 			       fs);
642 			return 1;
643 		}
644 	}
645 	fseek(f, 0L, SEEK_SET);	/* rewind() not necessary */
646 	return 0;
647 }
648 
649 /* Print out the contents of the file f, one screenful at a time. */
650 #define STOP -10
screen(register FILE * f,register int num_lines)651 void screen(register FILE *f, register int num_lines)
652 {
653 	register int c;
654 	register int nchars;
655 	int length;			/* length of current line */
656 	static int prev_len = 1;	/* length of previous line */
657 
658 	for (;;) {
659 		while (num_lines > 0 && !Pause) {
660 			if ((nchars = get_line(f, &length)) == EOF) {
661 				if (clreol)
662 					clreos();
663 				return;
664 			}
665 			if (ssp_opt && length == 0 && prev_len == 0)
666 				continue;
667 			prev_len = length;
668 			if (bad_so
669 			    || ((Senter && *Senter == ' ') && (promptlen > 0)))
670 				erasep(0);
671 			/* must clear before drawing line since tabs on
672 			 * some terminals do not erase what they tab
673 			 * over. */
674 			if (clreol)
675 				cleareol();
676 			prbuf(Line, length);
677 			if (nchars < promptlen)
678 				erasep(nchars);	/* erasep () sets promptlen to 0 */
679 			else
680 				promptlen = 0;
681 			/* is this needed?
682 			 * if (clreol)
683 			 *	cleareol();     * must clear again in case we wrapped *
684 			 */
685 			if (nchars < Mcol || !fold_opt)
686 				prbuf("\n", 1);	/* will turn off UL if necessary */
687 			if (nchars == STOP)
688 				break;
689 			num_lines--;
690 		}
691 		if (pstate) {
692 			my_putstring(ULexit);
693 			pstate = 0;
694 		}
695 		fflush(stdout);
696 		if ((c = Getc(f)) == EOF) {
697 			if (clreol)
698 				clreos();
699 			return;
700 		}
701 
702 		if (Pause && clreol)
703 			clreos();
704 		Ungetc(c, f);
705 		sigsetjmp(restore, 1);
706 		Pause = 0;
707 		startup = 0;
708 		if ((num_lines = command(NULL, f)) == 0)
709 			return;
710 		if (hard && promptlen > 0)
711 			erasep(0);
712 		if (noscroll && num_lines >= dlines) {
713 			if (clreol)
714 				home();
715 			else
716 				doclear();
717 		}
718 		screen_start.line = Currline;
719 		screen_start.chrctr = Ftell(f);
720 	}
721 }
722 
723 /* Come here if a quit signal is received */
onquit(int dummy)724 void onquit(int dummy __attribute__((__unused__)))
725 {
726 	signal(SIGQUIT, SIG_IGN);
727 	if (!inwait) {
728 		putchar('\n');
729 		if (!startup) {
730 			signal(SIGQUIT, onquit);
731 			siglongjmp(restore, 1);
732 		} else
733 			Pause++;
734 	} else if (!dum_opt && notell) {
735 		promptlen += fprintf(stderr, _("[Use q or Q to quit]"));
736 		notell = 0;
737 	}
738 	signal(SIGQUIT, onquit);
739 }
740 
741 /* Come here if a signal for a window size change is received */
742 #ifdef SIGWINCH
chgwinsz(int dummy)743 void chgwinsz(int dummy __attribute__((__unused__)))
744 {
745 	struct winsize win;
746 
747 	signal(SIGWINCH, SIG_IGN);
748 	if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) {
749 		if (win.ws_row != 0) {
750 			Lpp = win.ws_row;
751 			nscroll = Lpp / 2 - 1;
752 			if (nscroll <= 0)
753 				nscroll = 1;
754 			dlines = Lpp - 1;	/* was: Lpp - (noscroll ? 1 : 2) */
755 		}
756 		if (win.ws_col != 0)
757 			Mcol = win.ws_col;
758 	}
759 	signal(SIGWINCH, chgwinsz);
760 }
761 #endif				/* SIGWINCH */
762 
763 /* Clean up terminal state and exit. Also come here if interrupt signal received */
end_it(int dummy)764 void __attribute__((__noreturn__)) end_it(int dummy __attribute__((__unused__)))
765 {
766 	reset_tty();
767 	if (clreol) {
768 		putchar('\r');
769 		clreos();
770 		fflush(stdout);
771 	} else if (!clreol && (promptlen > 0)) {
772 		kill_line();
773 		fflush(stdout);
774 	} else
775 		putcerr('\n');
776 	free(previousre);
777 	free(Line);
778 	_exit(EXIT_SUCCESS);
779 }
780 
copy_file(register FILE * f)781 void copy_file(register FILE *f)
782 {
783 	char buf[BUFSIZ];
784 	size_t sz;
785 
786 	while ((sz = fread(&buf, sizeof(char), sizeof(buf), f)) > 0)
787 		fwrite(&buf, sizeof(char), sz, stdout);
788 }
789 
790 #define ringbell()	putcerr('\007')
791 
prompt(char * filename)792 static void prompt(char *filename)
793 {
794 	if (clreol)
795 		cleareol();
796 	else if (promptlen > 0)
797 		kill_line();
798 	if (!hard) {
799 		promptlen = 0;
800 		if (Senter && Sexit) {
801 			my_putstring(Senter);
802 			promptlen += (2 * soglitch);
803 		}
804 		if (clreol)
805 			cleareol();
806 		promptlen += printf(_("--More--"));
807 		if (filename != NULL) {
808 			promptlen += printf(_("(Next file: %s)"), filename);
809 		} else if (!no_intty) {
810 			promptlen +=
811 			    printf("(%d%%)",
812 				   (int)((file_pos * 100) / file_size));
813 		}
814 		if (dum_opt) {
815 			promptlen +=
816 			    printf(_("[Press space to continue, 'q' to quit.]"));
817 		}
818 		if (Senter && Sexit)
819 			my_putstring(Sexit);
820 		if (clreol)
821 			clreos();
822 		fflush(stdout);
823 	} else
824 		ringbell();
825 	inwait++;
826 }
827 
prepare_line_buffer(void)828 void prepare_line_buffer(void)
829 {
830 	char *nline;
831 	size_t nsz = Mcol * 4;
832 
833 	if (LineLen >= nsz)
834 		return;
835 
836 	if (nsz < LINSIZ)
837 		nsz = LINSIZ;
838 
839 	/* alloc nsz and extra space for \n\0 */
840 	nline = xrealloc(Line, nsz + 2);
841 	Line = nline;
842 	LineLen = nsz;
843 }
844 
845 /* Get a logical line */
get_line(register FILE * f,int * length)846 int get_line(register FILE *f, int *length)
847 {
848 	int c;
849 	char *p;
850 	int column;
851 	static int colflg;
852 
853 #ifdef HAVE_WIDECHAR
854 	size_t i;
855 	wchar_t wc;
856 	int wc_width;
857 	mbstate_t state, state_bak;	/* Current status of the stream. */
858 	char mbc[MB_LEN_MAX];		/* Buffer for one multibyte char. */
859 	size_t mblength;		/* Byte length of multibyte char. */
860 	size_t mbc_pos = 0;		/* Position of the MBC. */
861 	int use_mbc_buffer_flag = 0;	/* If 1, mbc has data. */
862 	int break_flag = 0;		/* If 1, exit while(). */
863 	long file_pos_bak = Ftell(f);
864 
865 	memset(&state, '\0', sizeof(mbstate_t));
866 #endif
867 
868 	prepare_line_buffer();
869 
870 	p = Line;
871 	column = 0;
872 	c = Getc(f);
873 	if (colflg && c == '\n') {
874 		Currline++;
875 		c = Getc(f);
876 	}
877 	while (p < &Line[LineLen - 1]) {
878 #ifdef HAVE_WIDECHAR
879 		if (fold_opt && use_mbc_buffer_flag && MB_CUR_MAX > 1) {
880 			use_mbc_buffer_flag = 0;
881 			state_bak = state;
882 			mbc[mbc_pos++] = c;
883  process_mbc:
884 			mblength = mbrtowc(&wc, mbc, mbc_pos, &state);
885 
886 			switch (mblength) {
887 			case (size_t)-2:	/* Incomplete multibyte character. */
888 				use_mbc_buffer_flag = 1;
889 				state = state_bak;
890 				break;
891 
892 			case (size_t)-1:	/* Invalid as a multibyte character. */
893 				*p++ = mbc[0];
894 				state = state_bak;
895 				column++;
896 				file_pos_bak++;
897 
898 				if (column >= Mcol) {
899 					Fseek(f, file_pos_bak);
900 				} else {
901 					memmove(mbc, mbc + 1, --mbc_pos);
902 					if (mbc_pos > 0) {
903 						mbc[mbc_pos] = '\0';
904 						goto process_mbc;
905 					}
906 				}
907 				break;
908 
909 			default:
910 				wc_width = wcwidth(wc);
911 
912 				if (column + wc_width > Mcol) {
913 					Fseek(f, file_pos_bak);
914 					break_flag = 1;
915 				} else {
916 					for (i = 0; p < &Line[LineLen - 1] &&
917 						    i < mbc_pos; i++)
918 						*p++ = mbc[i];
919 					if (wc_width > 0)
920 						column += wc_width;
921 				}
922 			}
923 
924 			if (break_flag || column >= Mcol)
925 				break;
926 
927 			c = Getc(f);
928 			continue;
929 		}
930 #endif	/* HAVE_WIDECHAR */
931 		if (c == EOF) {
932 			if (p > Line) {
933 				*p = '\0';
934 				*length = p - Line;
935 				return (column);
936 			}
937 			*length = p - Line;
938 			return (EOF);
939 		}
940 		if (c == '\n') {
941 			Currline++;
942 			break;
943 		}
944 
945 		*p++ = c;
946 #if 0
947 		if (c == '\033') {	/* ESC */
948 			c = Getc(f);
949 			while (c > ' ' && c < '0' && p < &Line[LineLen - 1]) {
950 				*p++ = c;
951 				c = Getc(f);
952 			}
953 			if (c >= '0' && c < '\177' && p < &Line[LineLen - 1]) {
954 				*p++ = c;
955 				c = Getc(f);
956 				continue;
957 			}
958 		}
959 #endif	/* 0 */
960 		if (c == '\t') {
961 			if (!hardtabs || (column < promptlen && !hard)) {
962 				if (hardtabs && eraseln && !dumb) {
963 					column = 1 + (column | 7);
964 					my_putstring(eraseln);
965 					promptlen = 0;
966 				} else {
967 					for (--p; p < &Line[LineLen - 1];) {
968 						*p++ = ' ';
969 						if ((++column & 7) == 0)
970 							break;
971 					}
972 					if (column >= promptlen)
973 						promptlen = 0;
974 				}
975 			} else
976 				column = 1 + (column | 7);
977 		} else if (c == '\b' && column > 0) {
978 			column--;
979 		} else if (c == '\r') {
980 			int next = Getc(f);
981 			if (next == '\n') {
982 				p--;
983 				Currline++;
984 				break;
985 			}
986 			Ungetc(next, f);
987 			column = 0;
988 		} else if (c == '\f' && stop_opt) {
989 			p[-1] = '^';
990 			*p++ = 'L';
991 			column += 2;
992 			Pause++;
993 		} else if (c == EOF) {
994 			*length = p - Line;
995 			return (column);
996 		} else {
997 #ifdef HAVE_WIDECHAR
998 			if (fold_opt && MB_CUR_MAX > 1) {
999 				memset(mbc, '\0', MB_LEN_MAX);
1000 				mbc_pos = 0;
1001 				mbc[mbc_pos++] = c;
1002 				state_bak = state;
1003 
1004 				mblength = mbrtowc(&wc, mbc, mbc_pos, &state);
1005 				/* The value of mblength is always less than 2 here. */
1006 				switch (mblength) {
1007 				case (size_t)-2:
1008 					p--;
1009 					file_pos_bak = Ftell(f) - 1;
1010 					state = state_bak;
1011 					use_mbc_buffer_flag = 1;
1012 					break;
1013 
1014 				case (size_t)-1:
1015 					state = state_bak;
1016 					column++;
1017 					break;
1018 
1019 				default:
1020 					wc_width = wcwidth(wc);
1021 					if (wc_width > 0)
1022 						column += wc_width;
1023 				}
1024 			} else
1025 #endif	/* HAVE_WIDECHAR */
1026 			{
1027 				if (isprint(c))
1028 					column++;
1029 			}
1030 		}
1031 
1032 		if (column >= Mcol && fold_opt)
1033 			break;
1034 #ifdef HAVE_WIDECHAR
1035 		if (use_mbc_buffer_flag == 0 && p >= &Line[LineLen - 1 - 4])
1036 			/* don't read another char if there is no space for
1037 			 * whole multibyte sequence */
1038 			break;
1039 #endif
1040 		c = Getc(f);
1041 	}
1042 	if (column >= Mcol && Mcol > 0) {
1043 		if (!Wrap) {
1044 			*p++ = '\n';
1045 		}
1046 	}
1047 	colflg = column == Mcol && fold_opt;
1048 	if (colflg && eatnl && Wrap) {
1049 		*p++ = '\n';	/* simulate normal wrap */
1050 	}
1051 	*length = p - Line;
1052 	*p = 0;
1053 	return (column);
1054 }
1055 
1056 /* Erase the rest of the prompt, assuming we are starting at column col. */
erasep(register int col)1057 void erasep(register int col)
1058 {
1059 
1060 	if (promptlen == 0)
1061 		return;
1062 	if (hard) {
1063 		putchar('\n');
1064 	} else {
1065 		if (col == 0)
1066 			putchar('\r');
1067 		if (!dumb && eraseln)
1068 			my_putstring(eraseln);
1069 		else
1070 			printf("%*s", promptlen - col, "");
1071 	}
1072 	promptlen = 0;
1073 }
1074 
1075 /* Erase the current line entirely */
kill_line(void)1076 void kill_line(void)
1077 {
1078 	erasep(0);
1079 	if (!eraseln || dumb)
1080 		putchar('\r');
1081 }
1082 
1083 /* force clear to end of line */
cleareol(void)1084 void cleareol(void)
1085 {
1086 	my_putstring(eraseln);
1087 }
1088 
clreos(void)1089 void clreos(void)
1090 {
1091 	my_putstring(EodClr);
1092 }
1093 
1094 /* Print a buffer of n characters */
prbuf(register char * s,register int n)1095 void prbuf(register char *s, register int n)
1096 {
1097 	register char c;	/* next output character */
1098 	register int state;	/* next output char's UL state */
1099 #define wouldul(s,n)	((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_')))
1100 
1101 	while (--n >= 0)
1102 		if (!ul_opt)
1103 			putchar(*s++);
1104 		else {
1105 			if (*s == ' ' && pstate == 0 && ulglitch
1106 			    && wouldul(s + 1, n - 1)) {
1107 				s++;
1108 				continue;
1109 			}
1110 			if ((state = wouldul(s, n)) != 0) {
1111 				c = (*s == '_') ? s[2] : *s;
1112 				n -= 2;
1113 				s += 3;
1114 			} else
1115 				c = *s++;
1116 			if (state != pstate) {
1117 				if (c == ' ' && state == 0 && ulglitch
1118 				    && wouldul(s, n - 1))
1119 					state = 1;
1120 				else
1121 					my_putstring(state ? ULenter : ULexit);
1122 			}
1123 			if (c != ' ' || pstate == 0 || state != 0
1124 			    || ulglitch == 0)
1125 #ifdef HAVE_WIDECHAR
1126 			{
1127 				wchar_t wc;
1128 				size_t mblength;
1129 				mbstate_t mbstate;
1130 				memset(&mbstate, '\0', sizeof(mbstate_t));
1131 				s--;
1132 				n++;
1133 				mblength = mbrtowc(&wc, s, n, &mbstate);
1134 				if (mblength == (size_t)-2
1135 				    || mblength == (size_t)-1)
1136 					mblength = 1;
1137 				while (mblength--)
1138 					putchar(*s++);
1139 				n += mblength;
1140 			}
1141 #else
1142 				putchar(c);
1143 #endif				/* HAVE_WIDECHAR */
1144 			if (state && *chUL) {
1145 				putsout(chBS);
1146 				my_putstring(chUL);
1147 			}
1148 			pstate = state;
1149 		}
1150 }
1151 
1152 /*  Clear the screen */
doclear(void)1153 void doclear(void)
1154 {
1155 	if (Clear && !hard) {
1156 		my_putstring(Clear);
1157 		/* Put out carriage return so that system doesn't get
1158 		 * confused by escape sequences when expanding tabs */
1159 		putchar('\r');
1160 		promptlen = 0;
1161 	}
1162 }
1163 
1164 /* Go to home position */
home(void)1165 void home(void)
1166 {
1167 	my_putstring(Home);
1168 }
1169 
1170 static int lastcmd, lastarg, lastp;
1171 static int lastcolon;
1172 char shell_line[SHELL_LINE];
1173 
1174 /* Read a command and do it.  A command consists of an optional integer
1175  * argument followed by the command character.  Return the number of
1176  * lines to display in the next screenful.  If there is nothing more to
1177  * display in the current file, zero is returned. */
command(char * filename,register FILE * f)1178 int command(char *filename, register FILE *f)
1179 {
1180 	register int nlines;
1181 	register int retval = 0;
1182 	register int c;
1183 	char colonch;
1184 	int done;
1185 	char comchar, cmdbuf[INIT_BUF];
1186 
1187 #define ret(val) retval=val;done++;break
1188 
1189 	done = 0;
1190 	if (!errors)
1191 		prompt(filename);
1192 	else
1193 		errors = 0;
1194 	for (;;) {
1195 		nlines = number(&comchar);
1196 		lastp = colonch = 0;
1197 		if (comchar == '.') {	/* Repeat last command */
1198 			lastp++;
1199 			comchar = lastcmd;
1200 			nlines = lastarg;
1201 			if (lastcmd == ':')
1202 				colonch = lastcolon;
1203 		}
1204 		lastcmd = comchar;
1205 		lastarg = nlines;
1206 		if ((cc_t) comchar == otty.c_cc[VERASE]) {
1207 			kill_line();
1208 			prompt(filename);
1209 			continue;
1210 		}
1211 		switch (comchar) {
1212 		case ':':
1213 			retval = colon(filename, colonch, nlines);
1214 			if (retval >= 0)
1215 				done++;
1216 			break;
1217 		case 'b':
1218 		case ctrl('B'):
1219 			{
1220 				register int initline;
1221 
1222 				if (no_intty) {
1223 					ringbell();
1224 					return (-1);
1225 				}
1226 
1227 				if (nlines == 0)
1228 					nlines++;
1229 
1230 				putchar('\r');
1231 				erasep(0);
1232 				putchar('\n');
1233 				if (clreol)
1234 					cleareol();
1235 				printf(P_("...back %d page",
1236 					"...back %d pages", nlines),
1237 					nlines);
1238 				if (clreol)
1239 					cleareol();
1240 				putchar('\n');
1241 
1242 				initline = Currline - dlines * (nlines + 1);
1243 				if (!noscroll)
1244 					--initline;
1245 				if (initline < 0)
1246 					initline = 0;
1247 				Fseek(f, 0L);
1248 				Currline = 0;	/* skiplns() will make Currline correct */
1249 				skiplns(initline, f);
1250 				if (!noscroll) {
1251 					ret(dlines + 1);
1252 				} else {
1253 					ret(dlines);
1254 				}
1255 			}
1256 		case ' ':
1257 		case 'z':
1258 			if (nlines == 0)
1259 				nlines = dlines;
1260 			else if (comchar == 'z')
1261 				dlines = nlines;
1262 			ret(nlines);
1263 		case 'd':
1264 		case ctrl('D'):
1265 			if (nlines != 0)
1266 				nscroll = nlines;
1267 			ret(nscroll);
1268 		case 'q':
1269 		case 'Q':
1270 			end_it(0);
1271 		case 's':
1272 		case 'f':
1273 		case ctrl('F'):
1274 			if (nlines == 0)
1275 				nlines++;
1276 			if (comchar == 'f')
1277 				nlines *= dlines;
1278 			putchar('\r');
1279 			erasep(0);
1280 			putchar('\n');
1281 			if (clreol)
1282 				cleareol();
1283 			printf(P_("...skipping %d line",
1284 				"...skipping %d lines", nlines),
1285 				nlines);
1286 
1287 			if (clreol)
1288 				cleareol();
1289 			putchar('\n');
1290 
1291 			while (nlines > 0) {
1292 				while ((c = Getc(f)) != '\n')
1293 					if (c == EOF) {
1294 						retval = 0;
1295 						done++;
1296 						goto endsw;
1297 					}
1298 				Currline++;
1299 				nlines--;
1300 			}
1301 			ret(dlines);
1302 		case '\n':
1303 			if (nlines != 0)
1304 				dlines = nlines;
1305 			else
1306 				nlines = 1;
1307 			ret(nlines);
1308 		case '\f':
1309 			if (!no_intty) {
1310 				doclear();
1311 				Fseek(f, screen_start.chrctr);
1312 				Currline = screen_start.line;
1313 				ret(dlines);
1314 			} else {
1315 				ringbell();
1316 				break;
1317 			}
1318 		case '\'':
1319 			if (!no_intty) {
1320 				kill_line();
1321 				putsout(_("\n***Back***\n\n"));
1322 				Fseek(f, context.chrctr);
1323 				Currline = context.line;
1324 				ret(dlines);
1325 			} else {
1326 				ringbell();
1327 				break;
1328 			}
1329 		case '=':
1330 			kill_line();
1331 			promptlen = printf("%d", Currline);
1332 			fflush(stdout);
1333 			break;
1334 		case 'n':
1335 			if (!previousre) {
1336 				more_error(_("No previous regular expression"));
1337 				break;
1338 			}
1339 			lastp++;
1340 			/* fall through */
1341 		case '/':
1342 			if (nlines == 0)
1343 				nlines++;
1344 			kill_line();
1345 			putchar('/');
1346 			promptlen = 1;
1347 			fflush(stdout);
1348 			if (lastp) {
1349 				putcerr('\r');
1350 				search(previousre, f, nlines);
1351 			} else {
1352 				ttyin(cmdbuf, sizeof(cmdbuf) - 2, '/');
1353 				putcerr('\r');
1354 				free(previousre);
1355 				previousre = xstrdup(cmdbuf);
1356 				search(cmdbuf, f, nlines);
1357 			}
1358 			ret(dlines - 1);
1359 		case '!':
1360 			do_shell(filename);
1361 			break;
1362 		case '?':
1363 		case 'h':
1364 			if (noscroll)
1365 				doclear();
1366 			putsout(_("\n"
1367 				  "Most commands optionally preceded by integer argument k.  "
1368 				  "Defaults in brackets.\n"
1369 				  "Star (*) indicates argument becomes new default.\n"));
1370 			puts("---------------------------------------"
1371 			     "----------------------------------------");
1372 			putsout(_
1373 				("<space>                 Display next k lines of text [current screen size]\n"
1374 				 "z                       Display next k lines of text [current screen size]*\n"
1375 				 "<return>                Display next k lines of text [1]*\n"
1376 				 "d or ctrl-D             Scroll k lines [current scroll size, initially 11]*\n"
1377 				 "q or Q or <interrupt>   Exit from more\n"
1378 				 "s                       Skip forward k lines of text [1]\n"
1379 				 "f                       Skip forward k screenfuls of text [1]\n"
1380 				 "b or ctrl-B             Skip backwards k screenfuls of text [1]\n"
1381 				 "'                       Go to place where previous search started\n"
1382 				 "=                       Display current line number\n"
1383 				 "/<regular expression>   Search for kth occurrence of regular expression [1]\n"
1384 				 "n                       Search for kth occurrence of last r.e [1]\n"
1385 				 "!<cmd> or :!<cmd>       Execute <cmd> in a subshell\n"
1386 				 "v                       Start up /usr/bin/vi at current line\n"
1387 				 "ctrl-L                  Redraw screen\n"
1388 				 ":n                      Go to kth next file [1]\n"
1389 				 ":p                      Go to kth previous file [1]\n"
1390 				 ":f                      Display current file name and line number\n"
1391 				 ".                       Repeat previous command\n"));
1392 			puts("---------------------------------------"
1393 			     "----------------------------------------");
1394 			prompt(filename);
1395 			break;
1396 		case 'v':	/* This case should go right before default */
1397 			if (!no_intty) {
1398 				/* Earlier: call vi +n file. This also
1399 				 * works for emacs.  POSIX: call vi -c n
1400 				 * file (when editor is vi or ex). */
1401 				char *editor, *p;
1402 				int n = (Currline - dlines <= 0 ? 1 :
1403 					 Currline - (dlines + 1) / 2);
1404 				int split = 0;
1405 
1406 				editor = getenv("VISUAL");
1407 				if (editor == NULL || *editor == '\0')
1408 					editor = getenv("EDITOR");
1409 				if (editor == NULL || *editor == '\0')
1410 					editor = VI;
1411 
1412 				p = strrchr(editor, '/');
1413 				if (p)
1414 					p++;
1415 				else
1416 					p = editor;
1417 				if (!strcmp(p, "vi") || !strcmp(p, "ex")) {
1418 					sprintf(cmdbuf, "-c %d", n);
1419 					split = 1;
1420 				} else {
1421 					sprintf(cmdbuf, "+%d", n);
1422 				}
1423 
1424 				kill_line();
1425 				printf("%s %s %s", editor, cmdbuf,
1426 				       fnames[fnum]);
1427 				if (split) {
1428 					cmdbuf[2] = 0;
1429 					execute(filename, editor, editor,
1430 						cmdbuf, cmdbuf + 3,
1431 						fnames[fnum], (char *)0);
1432 				} else
1433 					execute(filename, editor, editor,
1434 						cmdbuf, fnames[fnum],
1435 						(char *)0);
1436 				break;
1437 			}
1438 			/* fall through */
1439 		default:
1440 			if (dum_opt) {
1441 				kill_line();
1442 				if (Senter && Sexit) {
1443 					my_putstring(Senter);
1444 					promptlen =
1445 					    printf(_
1446 						   ("[Press 'h' for instructions.]"))
1447 					    + 2 * soglitch;
1448 					my_putstring(Sexit);
1449 				} else
1450 					promptlen =
1451 					    printf(_
1452 						   ("[Press 'h' for instructions.]"));
1453 				fflush(stdout);
1454 			} else
1455 				ringbell();
1456 			break;
1457 		}
1458 		if (done)
1459 			break;
1460 	}
1461 	putchar('\r');
1462  endsw:
1463 	inwait = 0;
1464 	notell++;
1465 	return (retval);
1466 }
1467 
1468 static char ch;
1469 /* Execute a colon-prefixed command.  Returns <0 if not a command that
1470  * should cause more of the file to be printed. */
colon(char * filename,int cmd,int nlines)1471 int colon(char *filename, int cmd, int nlines)
1472 {
1473 	if (cmd == 0)
1474 		ch = readch();
1475 	else
1476 		ch = cmd;
1477 	lastcolon = ch;
1478 	switch (ch) {
1479 	case 'f':
1480 		kill_line();
1481 		if (!no_intty)
1482 			promptlen =
1483 			    printf(_("\"%s\" line %d"), fnames[fnum], Currline);
1484 		else
1485 			promptlen = printf(_("[Not a file] line %d"), Currline);
1486 		fflush(stdout);
1487 		return (-1);
1488 	case 'n':
1489 		if (nlines == 0) {
1490 			if (fnum >= nfiles - 1)
1491 				end_it(0);
1492 			nlines++;
1493 		}
1494 		putchar('\r');
1495 		erasep(0);
1496 		skipf(nlines);
1497 		return (0);
1498 	case 'p':
1499 		if (no_intty) {
1500 			ringbell();
1501 			return (-1);
1502 		}
1503 		putchar('\r');
1504 		erasep(0);
1505 		if (nlines == 0)
1506 			nlines++;
1507 		skipf(-nlines);
1508 		return (0);
1509 	case '!':
1510 		do_shell(filename);
1511 		return (-1);
1512 	case 'q':
1513 	case 'Q':
1514 		end_it(0);
1515 	default:
1516 		ringbell();
1517 		return (-1);
1518 	}
1519 }
1520 
1521 /* Read a decimal number from the terminal.  Set cmd to the non-digit
1522  * which terminates the number. */
number(char * cmd)1523 int number(char *cmd)
1524 {
1525 	register int i;
1526 
1527 	i = 0;
1528 	ch = otty.c_cc[VKILL];
1529 	for (;;) {
1530 		ch = readch();
1531 		if (isdigit(ch))
1532 			i = i * 10 + ch - '0';
1533 		else if ((cc_t) ch == otty.c_cc[VKILL])
1534 			i = 0;
1535 		else {
1536 			*cmd = ch;
1537 			break;
1538 		}
1539 	}
1540 	return (i);
1541 }
1542 
do_shell(char * filename)1543 void do_shell(char *filename)
1544 {
1545 	char cmdbuf[COMMAND_BUF];
1546 	int rc;
1547 	char *expanded;
1548 
1549 	kill_line();
1550 	putchar('!');
1551 	fflush(stdout);
1552 	promptlen = 1;
1553 	if (lastp)
1554 		putsout(shell_line);
1555 	else {
1556 		ttyin(cmdbuf, sizeof(cmdbuf) - 2, '!');
1557 		expanded = 0;
1558 		rc = expand(&expanded, cmdbuf);
1559 		if (expanded) {
1560 			if (strlen(expanded) < sizeof(shell_line))
1561 				strcpy(shell_line, expanded);
1562 			else
1563 				rc = -1;
1564 			free(expanded);
1565 		}
1566 		if (rc < 0) {
1567 			putserr(_("  Overflow\n"));
1568 			prompt(filename);
1569 			return;
1570 		} else if (rc > 0) {
1571 			kill_line();
1572 			promptlen = printf("!%s", shell_line);
1573 		}
1574 	}
1575 	fflush(stdout);
1576 	putcerr('\n');
1577 	promptlen = 0;
1578 	shellp = 1;
1579 	execute(filename, shell, shell, "-c", shell_line, 0);
1580 }
1581 
1582 /* Search for nth occurrence of regular expression contained in buf in
1583  * the file */
search(char buf[],FILE * file,register int n)1584 void search(char buf[], FILE *file, register int n)
1585 {
1586 	long startline = Ftell(file);
1587 	register long line1 = startline;
1588 	register long line2 = startline;
1589 	register long line3;
1590 	register int lncount;
1591 	int saveln, rc;
1592 	regex_t re;
1593 
1594 	context.line = saveln = Currline;
1595 	context.chrctr = startline;
1596 	lncount = 0;
1597 	if ((rc = regcomp(&re, buf, REG_NOSUB)) != 0) {
1598 		char s[REGERR_BUF];
1599 		regerror(rc, &re, s, sizeof s);
1600 		more_error(s);
1601 	}
1602 	while (!feof(file)) {
1603 		line3 = line2;
1604 		line2 = line1;
1605 		line1 = Ftell(file);
1606 		rdline(file);
1607 		lncount++;
1608 		if (regexec(&re, Line, 0, NULL, 0) == 0) {
1609 			if (--n == 0) {
1610 				if (lncount > 3 || (lncount > 1 && no_intty)) {
1611 					putchar('\n');
1612 					if (clreol)
1613 						cleareol();
1614 					putsout(_("...skipping\n"));
1615 				}
1616 				if (!no_intty) {
1617 					Currline -=
1618 					    (lncount >= 3 ? 3 : lncount);
1619 					Fseek(file, line3);
1620 					if (noscroll) {
1621 						if (clreol) {
1622 							home();
1623 							cleareol();
1624 						} else
1625 							doclear();
1626 					}
1627 				} else {
1628 					kill_line();
1629 					if (noscroll) {
1630 						if (clreol) {
1631 							home();
1632 							cleareol();
1633 						} else
1634 							doclear();
1635 					}
1636 					puts(Line);
1637 				}
1638 				break;
1639 			}
1640 		}
1641 	}
1642 	regfree(&re);
1643 	if (feof(file)) {
1644 		if (!no_intty) {
1645 			Currline = saveln;
1646 			Fseek(file, startline);
1647 		} else {
1648 			putsout(_("\nPattern not found\n"));
1649 			end_it(0);
1650 		}
1651 		free(previousre);
1652 		previousre = NULL;
1653 		more_error(_("Pattern not found"));
1654 	}
1655 }
1656 
execute(char * filename,char * cmd,...)1657 void execute(char *filename, char *cmd, ...)
1658 {
1659 	int id;
1660 	int n;
1661 	va_list argp;
1662 	char *arg;
1663 	char **args;
1664 	int argcount;
1665 
1666 	fflush(stdout);
1667 	reset_tty();
1668 	for (n = 10; (id = fork()) < 0 && n > 0; n--)
1669 		sleep(5);
1670 	if (id == 0) {
1671 		if (!isatty(0)) {
1672 			close(0);
1673 			open("/dev/tty", 0);
1674 		}
1675 
1676 		va_start(argp, cmd);
1677 		arg = va_arg(argp, char *);
1678 		argcount = 0;
1679 		while (arg) {
1680 			argcount++;
1681 			arg = va_arg(argp, char *);
1682 		}
1683 		va_end(argp);
1684 
1685 		args = alloca(sizeof(char *) * (argcount + 1));
1686 		args[argcount] = NULL;
1687 
1688 		va_start(argp, cmd);
1689 		arg = va_arg(argp, char *);
1690 		argcount = 0;
1691 		while (arg) {
1692 			args[argcount] = arg;
1693 			argcount++;
1694 			arg = va_arg(argp, char *);
1695 		}
1696 		va_end(argp);
1697 
1698 		execvp(cmd, args);
1699 		putserr(_("exec failed\n"));
1700 		exit(EXIT_FAILURE);
1701 	}
1702 	if (id > 0) {
1703 		signal(SIGINT, SIG_IGN);
1704 		signal(SIGQUIT, SIG_IGN);
1705 		if (catch_susp)
1706 			signal(SIGTSTP, SIG_DFL);
1707 		while (wait(0) > 0) ;
1708 		signal(SIGINT, end_it);
1709 		signal(SIGQUIT, onquit);
1710 		if (catch_susp)
1711 			signal(SIGTSTP, onsusp);
1712 	} else
1713 		putserr(_("can't fork\n"));
1714 	set_tty();
1715 	puts("------------------------");
1716 	prompt(filename);
1717 }
1718 
1719 /* Skip n lines in the file f */
skiplns(register int n,register FILE * f)1720 void skiplns(register int n, register FILE *f)
1721 {
1722 	register int c;
1723 
1724 	while (n > 0) {
1725 		while ((c = Getc(f)) != '\n')
1726 			if (c == EOF)
1727 				return;
1728 		n--;
1729 		Currline++;
1730 	}
1731 }
1732 
1733 /* Skip nskip files in the file list (from the command line).  Nskip may
1734  * be negative. */
skipf(register int nskip)1735 void skipf(register int nskip)
1736 {
1737 	if (nskip == 0)
1738 		return;
1739 	if (nskip > 0) {
1740 		if (fnum + nskip > nfiles - 1)
1741 			nskip = nfiles - fnum - 1;
1742 	} else if (within)
1743 		++fnum;
1744 	fnum += nskip;
1745 	if (fnum < 0)
1746 		fnum = 0;
1747 	puts(_("\n...Skipping "));
1748 	if (clreol)
1749 		cleareol();
1750 	if (nskip > 0)
1751 		putsout(_("...Skipping to file "));
1752 	else
1753 		putsout(_("...Skipping back to file "));
1754 	puts(fnames[fnum]);
1755 	if (clreol)
1756 		cleareol();
1757 	putchar('\n');
1758 	--fnum;
1759 }
1760 
1761 /*----------------------------- Terminal I/O -------------------------------*/
initterm(void)1762 void initterm(void)
1763 {
1764 	int ret;
1765 	char *padstr;
1766 	char *term;
1767 	struct winsize win;
1768 
1769 #ifdef do_SIGTTOU
1770  retry:
1771 #endif
1772 
1773 #ifndef NON_INTERACTIVE_MORE
1774 	no_tty = tcgetattr(fileno(stdout), &otty);
1775 #endif
1776 	if (!no_tty) {
1777 		docrterase = (otty.c_cc[VERASE] != 255);
1778 		docrtkill = (otty.c_cc[VKILL] != 255);
1779 #ifdef do_SIGTTOU
1780 		{
1781 			int tgrp;
1782 			/* Wait until we're in the foreground before we
1783 			 * save the terminal modes. */
1784 			if ((tgrp = tcgetpgrp(fileno(stdout))) < 0) {
1785 				perror("tcgetpgrp");
1786 				exit(EXIT_FAILURE);
1787 			}
1788 			if (tgrp != getpgrp(0)) {
1789 				kill(0, SIGTTOU);
1790 				goto retry;
1791 			}
1792 		}
1793 #endif	/* do_SIGTTOU */
1794 		if ((term = getenv("TERM")) == NULL) {
1795 			dumb++;
1796 			ul_opt = 0;
1797 		}
1798 		my_setupterm(term, 1, &ret);
1799 		if (ret <= 0) {
1800 			dumb++;
1801 			ul_opt = 0;
1802 		} else {
1803 #ifdef TIOCGWINSZ
1804 			if (ioctl(fileno(stdout), TIOCGWINSZ, &win) < 0) {
1805 #endif
1806 				Lpp = my_tgetnum(TERM_LINES);
1807 				Mcol = my_tgetnum(TERM_COLS);
1808 #ifdef TIOCGWINSZ
1809 			} else {
1810 				if ((Lpp = win.ws_row) == 0)
1811 					Lpp = my_tgetnum(TERM_LINES);
1812 				if ((Mcol = win.ws_col) == 0)
1813 					Mcol = my_tgetnum(TERM_COLS);
1814 			}
1815 #endif
1816 			if ((Lpp <= 0) || my_tgetflag(TERM_HARD_COPY)) {
1817 				hard++;	/* Hard copy terminal */
1818 				Lpp = LINES_PER_PAGE;
1819 			}
1820 
1821 			if (my_tgetflag(TERM_EAT_NEW_LINE))
1822 				/* Eat newline at last column + 1; dec, concept */
1823 				eatnl++;
1824 			if (Mcol <= 0)
1825 				Mcol = NUM_COLUMNS;
1826 
1827 			Wrap = my_tgetflag(TERM_AUTO_RIGHT_MARGIN);
1828 			bad_so = my_tgetflag(TERM_CEOL);
1829 			eraseln = my_tgetstr(TERM_CLEAR_TO_LINE_END);
1830 			Clear = my_tgetstr(TERM_CLEAR);
1831 			Senter = my_tgetstr(TERM_STANDARD_MODE);
1832 			Sexit = my_tgetstr(TERM_EXIT_STANDARD_MODE);
1833 			if ((soglitch = my_tgetnum(TERM_STD_MODE_GLITCH)) < 0)
1834 				soglitch = 0;
1835 
1836 			/* Set up for underlining:  some terminals don't
1837 			 * need it; others have start/stop sequences,
1838 			 * still others have an underline char sequence
1839 			 * which is assumed to move the cursor forward
1840 			 * one character.  If underline sequence isn't
1841 			 * available, settle for standout sequence. */
1842 			if (my_tgetflag(TERM_UNDERLINE)
1843 			    || my_tgetflag(TERM_OVER_STRIKE))
1844 				ul_opt = 0;
1845 			if ((chUL = my_tgetstr(TERM_UNDERLINE_CHAR)) == NULL)
1846 				chUL = "";
1847 			if (((ULenter =
1848 			      my_tgetstr(TERM_ENTER_UNDERLINE)) == NULL
1849 			     || (ULexit =
1850 				 my_tgetstr(TERM_EXIT_UNDERLINE)) == NULL)
1851 			    && !*chUL) {
1852 				if ((ULenter = Senter) == NULL
1853 				    || (ULexit = Sexit) == NULL) {
1854 					ULenter = "";
1855 					ULexit = "";
1856 				} else
1857 					ulglitch = soglitch;
1858 			} else {
1859 				ulglitch = 0;
1860 			}
1861 
1862 			if ((padstr = my_tgetstr(TERM_PAD_CHAR)) != NULL)
1863 				PC = *padstr;
1864 			Home = my_tgetstr(TERM_HOME);
1865 			if (Home == NULL || *Home == '\0') {
1866 				if ((cursorm =
1867 				     my_tgetstr(TERM_CURSOR_ADDRESS)) != NULL) {
1868 					const char *t =
1869 					    (const char *)my_tgoto(cursorm, 0,
1870 								   0);
1871 					xstrncpy(cursorhome, t,
1872 						 sizeof(cursorhome));
1873 					Home = cursorhome;
1874 				}
1875 			}
1876 			EodClr = my_tgetstr(TERM_CLEAR_TO_SCREEN_END);
1877 			if ((chBS = my_tgetstr(TERM_LINE_DOWN)) == NULL)
1878 				chBS = "\b";
1879 
1880 		}
1881 		if ((shell = getenv("SHELL")) == NULL)
1882 			shell = "/bin/sh";
1883 	}
1884 	no_intty = tcgetattr(fileno(stdin), &otty);
1885 	tcgetattr(fileno(stderr), &otty);
1886 	savetty0 = otty;
1887 	slow_tty = cfgetispeed(&otty) < B1200;
1888 	hardtabs = (otty.c_oflag & TABDLY) != XTABS;
1889 	if (!no_tty) {
1890 		otty.c_lflag &= ~(ICANON | ECHO);
1891 		otty.c_cc[VMIN] = 1;
1892 		otty.c_cc[VTIME] = 0;
1893 	}
1894 }
1895 
readch(void)1896 int readch(void)
1897 {
1898 	unsigned char c;
1899 
1900 	errno = 0;
1901 	if (read(fileno(stderr), &c, 1) <= 0) {
1902 		if (errno != EINTR)
1903 			end_it(0);
1904 		else
1905 			c = otty.c_cc[VKILL];
1906 	}
1907 	return (c);
1908 }
1909 
1910 static char *BS = "\b";
1911 static char *BSB = "\b \b";
1912 static char *CARAT = "^";
1913 #define ERASEONECOLUMN \
1914     if (docrterase) \
1915 	putserr(BSB); \
1916     else \
1917 	putserr(BS);
1918 
ttyin(char buf[],register int nmax,char pchar)1919 void ttyin(char buf[], register int nmax, char pchar)
1920 {
1921 	char *sp;
1922 	int c;
1923 	int slash = 0;
1924 	int maxlen;
1925 
1926 	sp = buf;
1927 	maxlen = 0;
1928 	while (sp - buf < nmax) {
1929 		if (promptlen > maxlen)
1930 			maxlen = promptlen;
1931 		c = readch();
1932 		if (c == '\\') {
1933 			slash++;
1934 		} else if (((cc_t) c == otty.c_cc[VERASE]) && !slash) {
1935 			if (sp > buf) {
1936 #ifdef HAVE_WIDECHAR
1937 				if (MB_CUR_MAX > 1) {
1938 					wchar_t wc;
1939 					size_t pos = 0, mblength;
1940 					mbstate_t state, state_bak;
1941 
1942 					memset(&state, '\0', sizeof(mbstate_t));
1943 
1944 					while (1) {
1945 						state_bak = state;
1946 						mblength =
1947 						    mbrtowc(&wc, buf + pos,
1948 							    sp - buf, &state);
1949 
1950 						state = (mblength == (size_t)-2
1951 							 || mblength ==
1952 							 (size_t)-1) ? state_bak
1953 						    : state;
1954 						mblength =
1955 						    (mblength == (size_t)-2
1956 						     || mblength == (size_t)-1
1957 						     || mblength ==
1958 						     0) ? 1 : mblength;
1959 
1960 						if (buf + pos + mblength >= sp)
1961 							break;
1962 
1963 						pos += mblength;
1964 					}
1965 
1966 					if (mblength == 1) {
1967 					ERASEONECOLUMN} else {
1968 						int wc_width;
1969 						wc_width = wcwidth(wc);
1970 						wc_width =
1971 						    (wc_width <
1972 						     1) ? 1 : wc_width;
1973 						while (wc_width--) {
1974 						ERASEONECOLUMN}
1975 					}
1976 
1977 					while (mblength--) {
1978 						--promptlen;
1979 						--sp;
1980 					}
1981 				} else
1982 #endif	/* HAVE_WIDECHAR */
1983 				{
1984 					--promptlen;
1985 					ERASEONECOLUMN-- sp;
1986 				}
1987 
1988 				if ((*sp < ' ' && *sp != '\n') || *sp == RUBOUT) {
1989 					--promptlen;
1990 				ERASEONECOLUMN}
1991 				continue;
1992 			} else {
1993 				if (!eraseln)
1994 					promptlen = maxlen;
1995 				siglongjmp(restore, 1);
1996 			}
1997 		} else if (((cc_t) c == otty.c_cc[VKILL]) && !slash) {
1998 			if (hard) {
1999 				show(c);
2000 				putchar('\n');
2001 				putchar(pchar);
2002 			} else {
2003 				putchar('\r');
2004 				putchar(pchar);
2005 				if (eraseln)
2006 					erasep(1);
2007 				else if (docrtkill)
2008 					while (promptlen-- > 1)
2009 						putserr(BSB);
2010 				promptlen = 1;
2011 			}
2012 			sp = buf;
2013 			fflush(stdout);
2014 			continue;
2015 		}
2016 		if (slash && ((cc_t) c == otty.c_cc[VKILL]
2017 			      || (cc_t) c == otty.c_cc[VERASE])) {
2018 			ERASEONECOLUMN-- sp;
2019 		}
2020 		if (c != '\\')
2021 			slash = 0;
2022 		*sp++ = c;
2023 		if ((c < ' ' && c != '\n' && c != ESC) || c == RUBOUT) {
2024 			c += (c == RUBOUT) ? -0100 : 0100;
2025 			putserr(CARAT);
2026 			promptlen++;
2027 		}
2028 		if (c != '\n' && c != ESC) {
2029 			putcerr(c);
2030 			promptlen++;
2031 		} else
2032 			break;
2033 	}
2034 	*--sp = '\0';
2035 	if (!eraseln)
2036 		promptlen = maxlen;
2037 	if (sp - buf >= nmax - 1)
2038 		more_error(_("Line too long"));
2039 }
2040 
2041 /* return: 0 - unchanged, 1 - changed, -1 - overflow (unchanged) */
expand(char ** outbuf,char * inbuf)2042 int expand(char **outbuf, char *inbuf)
2043 {
2044 	char *inpstr;
2045 	char *outstr;
2046 	char c;
2047 	char *temp;
2048 	int changed = 0;
2049 	int tempsz, xtra, offset;
2050 
2051 	xtra = strlen(fnames[fnum]) + strlen(shell_line) + 1;
2052 	tempsz = 200 + xtra;
2053 	temp = xmalloc(tempsz);
2054 	inpstr = inbuf;
2055 	outstr = temp;
2056 	while ((c = *inpstr++) != '\0') {
2057 		offset = outstr - temp;
2058 		if (tempsz - offset - 1 < xtra) {
2059 			tempsz += 200 + xtra;
2060 			temp = xrealloc(temp, tempsz);
2061 			outstr = temp + offset;
2062 		}
2063 		switch (c) {
2064 		case '%':
2065 			if (!no_intty) {
2066 				strcpy(outstr, fnames[fnum]);
2067 				outstr += strlen(fnames[fnum]);
2068 				changed++;
2069 			} else
2070 				*outstr++ = c;
2071 			break;
2072 		case '!':
2073 			if (!shellp)
2074 				more_error(_
2075 					   ("No previous command to substitute for"));
2076 			strcpy(outstr, shell_line);
2077 			outstr += strlen(shell_line);
2078 			changed++;
2079 			break;
2080 		case '\\':
2081 			if (*inpstr == '%' || *inpstr == '!') {
2082 				*outstr++ = *inpstr++;
2083 				break;
2084 			}
2085 		default:
2086 			*outstr++ = c;
2087 		}
2088 	}
2089 	*outstr++ = '\0';
2090 	*outbuf = temp;
2091 	return (changed);
2092 }
2093 
show(char c)2094 void show(char c)
2095 {
2096 	if ((c < ' ' && c != '\n' && c != ESC) || c == RUBOUT) {
2097 		c += (c == RUBOUT) ? -0100 : 0100;
2098 		putserr(CARAT);
2099 		promptlen++;
2100 	}
2101 	putcerr(c);
2102 	promptlen++;
2103 }
2104 
more_error(char * mess)2105 void more_error(char *mess)
2106 {
2107 	if (clreol)
2108 		cleareol();
2109 	else
2110 		kill_line();
2111 	promptlen += strlen(mess);
2112 	if (Senter && Sexit) {
2113 		my_putstring(Senter);
2114 		putsout(mess);
2115 		my_putstring(Sexit);
2116 	} else
2117 		putsout(mess);
2118 	fflush(stdout);
2119 	errors++;
2120 	siglongjmp(restore, 1);
2121 }
2122 
set_tty(void)2123 void set_tty(void)
2124 {
2125 	otty.c_lflag &= ~(ICANON | ECHO);
2126 	otty.c_cc[VMIN] = 1;	/* read at least 1 char */
2127 	otty.c_cc[VTIME] = 0;	/* no timeout */
2128 	stty(fileno(stderr), &otty);
2129 }
2130 
ourputch(int c)2131 static int ourputch(int c)
2132 {
2133 	return putc(c, stdout);
2134 }
2135 
reset_tty(void)2136 void reset_tty(void)
2137 {
2138 	if (no_tty)
2139 		return;
2140 	if (pstate) {
2141 		/* putchar - if that isn't a macro */
2142 		tputs(ULexit, fileno(stdout), ourputch);
2143 		fflush(stdout);
2144 		pstate = 0;
2145 	}
2146 	otty.c_lflag |= ICANON | ECHO;
2147 	otty.c_cc[VMIN] = savetty0.c_cc[VMIN];
2148 	otty.c_cc[VTIME] = savetty0.c_cc[VTIME];
2149 	stty(fileno(stderr), &savetty0);
2150 }
2151 
rdline(register FILE * f)2152 void rdline(register FILE *f)
2153 {
2154 	register int c;
2155 	register char *p;
2156 
2157 	prepare_line_buffer();
2158 
2159 	p = Line;
2160 	while ((c = Getc(f)) != '\n' && c != EOF
2161 	       && (size_t)(p - Line) < LineLen - 1)
2162 		*p++ = c;
2163 	if (c == '\n')
2164 		Currline++;
2165 	*p = '\0';
2166 }
2167 
2168 /* Come here when we get a suspend signal from the terminal */
onsusp(int dummy)2169 void onsusp(int dummy __attribute__((__unused__)))
2170 {
2171 	sigset_t signals, oldmask;
2172 
2173 	/* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
2174 	signal(SIGTTOU, SIG_IGN);
2175 	reset_tty();
2176 	fflush(stdout);
2177 	signal(SIGTTOU, SIG_DFL);
2178 	/* Send the TSTP signal to suspend our process group */
2179 	signal(SIGTSTP, SIG_DFL);
2180 
2181 	/* unblock SIGTSTP or we won't be able to suspend ourself */
2182 	sigemptyset(&signals);
2183 	sigaddset(&signals, SIGTSTP);
2184 	sigprocmask(SIG_UNBLOCK, &signals, &oldmask);
2185 
2186 	kill(0, SIGTSTP);
2187 	/* Pause for station break */
2188 
2189 	sigprocmask(SIG_SETMASK, &oldmask, NULL);
2190 
2191 	/* We're back */
2192 	signal(SIGTSTP, onsusp);
2193 	set_tty();
2194 	if (inwait)
2195 		siglongjmp(restore, 1);
2196 }
2197