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 MERCHANTABILITY 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>
50 #include <stdarg.h>
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 <sys/ioctl.h>
58 #include <sys/stat.h>
59 #include <sys/file.h>
60 #include <sys/ttydefaults.h>
61 #include <sys/wait.h>
62 #include <regex.h>
63 #include <assert.h>
64 #include <poll.h>
65 #include <sys/signalfd.h>
66 #include <paths.h>
67 #include <getopt.h>
68 
69 #if defined(HAVE_NCURSESW_TERM_H)
70 # include <ncursesw/term.h>
71 #elif defined(HAVE_NCURSES_TERM_H)
72 # include <ncurses/term.h>
73 #elif defined(HAVE_TERM_H)
74 # include <term.h>
75 #endif
76 
77 #ifdef HAVE_MAGIC
78 # include <magic.h>
79 #endif
80 
81 #include "strutils.h"
82 #include "nls.h"
83 #include "xalloc.h"
84 #include "widechar.h"
85 #include "closestream.h"
86 #include "rpmatch.h"
87 #include "env.h"
88 
89 #ifdef TEST_PROGRAM
90 # define NON_INTERACTIVE_MORE 1
91 #endif
92 
93 #define BACKSPACE	"\b"
94 #define CARAT		"^"
95 
96 #define ARROW_UP	"\x1b\x5b\x41"
97 #define ARROW_DOWN	"\x1b\x5b\x42"
98 #define PAGE_UP		"\x1b\x5b\x35\x7e"
99 #define PAGE_DOWN	"\x1b\x5b\x36\x7e"
100 
101 #define MIN_LINE_SZ	256	/* minimal line_buf buffer size */
102 #define ESC		'\033'
103 #define SCROLL_LEN	11
104 #define LINES_PER_PAGE	24
105 #define NUM_COLUMNS	80
106 #define TERMINAL_BUF	4096
107 #define INIT_BUF	80
108 #define COMMAND_BUF	200
109 #define REGERR_BUF	NUM_COLUMNS
110 
111 #define TERM_AUTO_RIGHT_MARGIN    "am"
112 #define TERM_BACKSPACE            "cub1"
113 #define TERM_CEOL                 "xhp"
114 #define TERM_CLEAR                "clear"
115 #define TERM_CLEAR_TO_LINE_END    "el"
116 #define TERM_CLEAR_TO_SCREEN_END  "ed"
117 #define TERM_COLS                 "cols"
118 #define TERM_CURSOR_ADDRESS       "cup"
119 #define TERM_EAT_NEW_LINE         "xenl"
120 #define TERM_EXIT_STANDARD_MODE   "rmso"
121 #define TERM_HARD_COPY            "hc"
122 #define TERM_HOME                 "home"
123 #define TERM_LINE_DOWN            "cud1"
124 #define TERM_LINES                "lines"
125 #define TERM_OVER_STRIKE          "os"
126 #define TERM_STANDARD_MODE        "smso"
127 #define TERM_STD_MODE_GLITCH      "xmc"
128 
129 /* Used in read_command() */
130 typedef enum {
131 	more_kc_unknown_command,
132 	more_kc_colon,
133 	more_kc_repeat_previous,
134 	more_kc_backwards,
135 	more_kc_jump_lines_per_screen,
136 	more_kc_set_lines_per_screen,
137 	more_kc_set_scroll_len,
138 	more_kc_quit,
139 	more_kc_skip_forward,
140 	more_kc_next_line,
141 	more_kc_clear_screen,
142 	more_kc_previous_search_match,
143 	more_kc_display_line,
144 	more_kc_display_file_and_line,
145 	more_kc_repeat_search,
146 	more_kc_search,
147 	more_kc_run_shell,
148 	more_kc_help,
149 	more_kc_next_file,
150 	more_kc_previous_file,
151 	more_kc_run_editor
152 } more_key_commands;
153 struct number_command {
154 	unsigned int number;
155 	more_key_commands key;
156 };
157 
158 struct more_control {
159 	struct termios output_tty;	/* output terminal */
160 	struct termios original_tty;	/* original terminal settings */
161 	FILE *current_file;		/* currently open input file */
162 	off_t file_position;		/* file position */
163 	off_t file_size;		/* file size */
164 	int argv_position;		/* argv[] position */
165 	int lines_per_screen;		/* screen size in lines */
166 	int d_scroll_len;		/* number of lines scrolled by 'd' */
167 	int prompt_len;			/* message prompt length */
168 	int current_line;		/* line we are currently at */
169 	int next_jump;			/* number of lines to skip ahead */
170 	char **file_names;		/* The list of file names */
171 	int num_files;			/* Number of files left to process */
172 	char *shell;			/* name of the shell to use */
173 	int sigfd;			/* signalfd() file descriptor */
174 	sigset_t sigset;		/* signal operations */
175 	char *line_buf;			/* line buffer */
176 	size_t line_sz;			/* size of line_buf buffer */
177 	int lines_per_page;		/* lines per page */
178 	char *clear;			/* clear screen */
179 	char *erase_line;		/* erase line */
180 	char *enter_std;		/* enter standout mode */
181 	char *exit_std;			/* exit standout mode */
182 	char *backspace_ch;		/* backspace character */
183 	char *go_home;			/* go to home */
184 	char *move_line_down;		/* move line down */
185 	char *clear_rest;		/* clear rest of screen */
186 	int num_columns;		/* number of columns */
187 	char *next_search;		/* file beginning search string */
188 	char *previous_search;		/* previous search() buf[] item */
189 	struct {
190 		off_t row_num;		/* row file position */
191 		long line_num;		/* line number */
192 	} context,
193 	  screen_start;
194 	unsigned int leading_number;	/* number in front of key command */
195 	struct number_command previous_command;	/* previous key command */
196 	char *shell_line;		/* line to execute in subshell */
197 #ifdef HAVE_MAGIC
198 	magic_t magic;			/* libmagic database entries */
199 #endif
200 	unsigned int
201 		bad_stdout:1,		/* true if overwriting does not turn off standout */
202 		catch_suspend:1,	/* we should catch the SIGTSTP signal */
203 		clear_line_ends:1,	/* do not scroll, paint each screen from the top */
204 		clear_first:1,		/* is first character in file \f */
205 		dumb_tty:1,		/* is terminal type known */
206 		eat_newline:1,		/* is newline ignored after 80 cols */
207 		erase_input_ok:1,	/* is erase input supported */
208 		erase_previous_ok:1,	/* is erase previous supported */
209 		first_file:1,		/* is the input file the first in list */
210 		fold_long_lines:1,	/* fold long lines */
211 		hard_tabs:1,		/* print spaces instead of '\t' */
212 		hard_tty:1,		/* is this hard copy terminal (a printer or such) */
213 		leading_colon:1,	/* key command has leading ':' character */
214 		is_paused:1,		/* is output paused */
215 		no_quit_dialog:1,	/* suppress quit dialog */
216 		no_scroll:1,		/* do not scroll, clear the screen and then display text */
217 		no_tty_in:1,		/* is input in interactive mode */
218 		no_tty_out:1,		/* is output in interactive mode */
219 		print_banner:1,		/* print file name banner */
220 		reading_num:1,		/* are we reading leading_number */
221 		report_errors:1,	/* is an error reported */
222 		search_at_start:1,	/* search pattern defined at start up */
223 		search_called:1,	/* previous more command was a search */
224 		squeeze_spaces:1,	/* suppress white space */
225 		stdout_glitch:1,	/* terminal has standout mode glitch */
226 		stop_after_formfeed:1,	/* stop after form feeds */
227 		suppress_bell:1,	/* suppress bell */
228 		wrap_margin:1;		/* set if automargins */
229 };
230 
usage(void)231 static void __attribute__((__noreturn__)) usage(void)
232 {
233 	printf("%s", USAGE_HEADER);
234 	printf(_(" %s [options] <file>...\n"), program_invocation_short_name);
235 
236 	printf("%s", USAGE_SEPARATOR);
237 	printf("%s\n", _("A file perusal filter for CRT viewing."));
238 
239 	printf("%s", USAGE_OPTIONS);
240 	printf("%s\n", _(" -d, --silent          display help instead of ringing bell"));
241 	printf("%s\n", _(" -f, --logical         count logical rather than screen lines"));
242 	printf("%s\n", _(" -l, --no-pause        suppress pause after form feed"));
243 	printf("%s\n", _(" -c, --print-over      do not scroll, display text and clean line ends"));
244 	printf("%s\n", _(" -p, --clean-print     do not scroll, clean screen and display text"));
245 	printf("%s\n", _(" -s, --squeeze         squeeze multiple blank lines into one"));
246 	printf("%s\n", _(" -u, --plain           suppress underlining and bold"));
247 	printf("%s\n", _(" -n, --lines <number>  the number of lines per screenful"));
248 	printf("%s\n", _(" -<number>             same as --lines"));
249 	printf("%s\n", _(" +<number>             display file beginning from line number"));
250 	printf("%s\n", _(" +/<pattern>           display file beginning from pattern match"));
251 	printf("%s", USAGE_SEPARATOR);
252 	printf(USAGE_HELP_OPTIONS(23));
253 	printf(USAGE_MAN_TAIL("more(1)"));
254 	exit(EXIT_SUCCESS);
255 }
256 
argscan(struct more_control * ctl,int as_argc,char ** as_argv)257 static void argscan(struct more_control *ctl, int as_argc, char **as_argv)
258 {
259 	int c, opt;
260 	static const struct option longopts[] = {
261 		{ "silent",      no_argument,       NULL, 'd' },
262 		{ "logical",     no_argument,       NULL, 'f' },
263 		{ "no-pause",    no_argument,       NULL, 'l' },
264 		{ "print-over",  no_argument,       NULL, 'c' },
265 		{ "clean-print", no_argument,       NULL, 'p' },
266 		{ "squeeze",     no_argument,       NULL, 's' },
267 		{ "plain",       no_argument,       NULL, 'u' },
268 		{ "lines",       required_argument, NULL, 'n' },
269 		{ "version",     no_argument,       NULL, 'V' },
270 		{ "help",        no_argument,       NULL, 'h' },
271 		{ NULL, 0, NULL, 0 }
272 	};
273 
274 	/* Take care of number option and +args. */
275 	for (opt = 0; opt < as_argc; opt++) {
276 		int move = 0;
277 
278 		if (as_argv[opt][0] == '-' && isdigit_string(as_argv[opt] + 1)) {
279 			ctl->lines_per_screen =
280 			    strtos16_or_err(as_argv[opt], _("failed to parse number"));
281 			ctl->lines_per_screen = abs(ctl->lines_per_screen);
282 			move = 1;
283 		} else if (as_argv[opt][0] == '+') {
284 			if (isdigit_string(as_argv[opt] + 1)) {
285 				ctl->next_jump = strtos32_or_err(as_argv[opt],
286 				    _("failed to parse number")) - 1;
287 				move = 1;
288 			} else if (as_argv[opt][1] == '/') {
289 				free(ctl->next_search);
290 				ctl->next_search = xstrdup(as_argv[opt] + 2);
291 				ctl->search_at_start = 1;
292 				move = 1;
293 			}
294 		}
295 		if (move) {
296 			as_argc = remote_entry(as_argv, opt, as_argc);
297 			opt--;
298 		}
299 	}
300 
301 	while ((c = getopt_long(as_argc, as_argv, "dflcpsun:eVh", longopts, NULL)) != -1) {
302 		switch (c) {
303 		case 'd':
304 			ctl->suppress_bell = 1;
305 			break;
306 		case 'l':
307 			ctl->stop_after_formfeed = 0;
308 			break;
309 		case 'f':
310 			ctl->fold_long_lines = 0;
311 			break;
312 		case 'p':
313 			ctl->no_scroll = 1;
314 			break;
315 		case 'c':
316 			ctl->clear_line_ends = 1;
317 			break;
318 		case 's':
319 			ctl->squeeze_spaces = 1;
320 			break;
321 		case 'u':
322 			break;
323 		case 'n':
324 			ctl->lines_per_screen = strtou16_or_err(optarg, _("argument error"));
325 			break;
326 		case 'e':	/* ignored silently to be posix compliant */
327 			break;
328 		case 'V':
329 			print_version(EXIT_SUCCESS);
330 		case 'h':
331 			usage();
332 		default:
333 			errtryhelp(EXIT_FAILURE);
334 			break;
335 		}
336 	}
337 	ctl->num_files = as_argc - optind;
338 	ctl->file_names = as_argv + optind;
339 }
340 
env_argscan(struct more_control * ctl,const char * s)341 static void env_argscan(struct more_control *ctl, const char *s)
342 {
343 	char **env_argv;
344 	int env_argc = 1;
345 	int size = 8;
346 	const char delim[] = { ' ', '\n', '\t', '\0' };
347 	char *str = xstrdup(s);
348 	char *key = NULL, *tok;
349 
350 	env_argv = xmalloc(sizeof(char *) * size);
351 	env_argv[0] = _("MORE environment variable");	/* program name */
352 	for (tok = strtok_r(str, delim, &key); tok; tok = strtok_r(NULL, delim, &key)) {
353 		env_argv[env_argc++] = tok;
354 		if (size < env_argc) {
355 			size *= 2;
356 			env_argv = xrealloc(env_argv, sizeof(char *) * size);
357 		}
358 	}
359 
360 	argscan(ctl, env_argc, env_argv);
361 	/* Reset optind, command line parsing needs this.  */
362 	optind = 0;
363 	free(str);
364 	free(env_argv);
365 }
366 
more_fseek(struct more_control * ctl,off_t pos)367 static void more_fseek(struct more_control *ctl, off_t pos)
368 {
369 	ctl->file_position = pos;
370 	fseeko(ctl->current_file, pos, SEEK_SET);
371 }
372 
more_getc(struct more_control * ctl)373 static int more_getc(struct more_control *ctl)
374 {
375 	int ret = getc(ctl->current_file);
376 	ctl->file_position = ftello(ctl->current_file);
377 	return ret;
378 }
379 
more_ungetc(struct more_control * ctl,int c)380 static int more_ungetc(struct more_control *ctl, int c)
381 {
382 	int ret = ungetc(c, ctl->current_file);
383 	ctl->file_position = ftello(ctl->current_file);
384 	return ret;
385 }
386 
print_separator(const int c,int n)387 static void print_separator(const int c, int n)
388 {
389 	while (n--)
390 		putchar(c);
391 	putchar('\n');
392 }
393 
394 /* check_magic -- check for file magic numbers. */
check_magic(struct more_control * ctl,char * fs)395 static int check_magic(struct more_control *ctl, char *fs)
396 {
397 #ifdef HAVE_MAGIC
398 	const int fd = fileno(ctl->current_file);
399 	const char *mime_encoding = magic_descriptor(ctl->magic, fd);
400 	const char *magic_error_msg = magic_error(ctl->magic);
401 	struct stat st;
402 
403 	if (magic_error_msg) {
404 		printf(_("magic failed: %s\n"), magic_error_msg);
405 		return 0;
406 	}
407 	if (fstat(fd, &st)) {
408 		warn(_("cannot stat %s"), fs);
409 		return 1;
410 	}
411 	if (st.st_size == 0) {
412 		/* libmagic tells an empty file has binary encoding */
413 		return 0;
414 	}
415 
416 	if (!mime_encoding || !(strcmp("binary", mime_encoding))) {
417 		printf(_("\n******** %s: Not a text file ********\n\n"), fs);
418 		return 1;
419 	}
420 #else
421 	signed char twobytes[2];
422 
423 	/* don't try to look ahead if the input is unseekable */
424 	if (fseek(ctl->current_file, 0L, SEEK_SET))
425 		return 0;
426 
427 	if (fread(twobytes, 2, 1, ctl->current_file) == 1) {
428 		switch (twobytes[0] + (twobytes[1] << 8)) {
429 		case 0407:	/* a.out obj */
430 		case 0410:	/* a.out exec */
431 		case 0413:	/* a.out demand exec */
432 		case 0405:
433 		case 0411:
434 		case 0177545:
435 		case 0x457f:	/* simple ELF detection */
436 			printf(_("\n******** %s: Not a text file ********\n\n"),
437 			       fs);
438 			return 1;
439 		}
440 	}
441 	fseek(ctl->current_file, 0L, SEEK_SET);	/* rewind() not necessary */
442 #endif
443 	return 0;
444 }
445 
446 /* Check whether the file named by fs is an ASCII file which the user may
447  * access.  If it is, return the opened file.  Otherwise return NULL. */
checkf(struct more_control * ctl,char * fs)448 static void checkf(struct more_control *ctl, char *fs)
449 {
450 	struct stat st;
451 	int c;
452 
453 	ctl->current_line = 0;
454 	ctl->file_position = 0;
455 	fflush(NULL);
456 	if (((ctl->current_file = fopen(fs, "r")) == NULL) ||
457 	    (fstat(fileno(ctl->current_file), &st) != 0)) {
458 		if (ctl->clear_line_ends)
459 			putp(ctl->erase_line);
460 		warn(_("cannot open %s"), fs);
461 		return;
462 	}
463 #ifndef HAVE_MAGIC
464 	if ((st.st_mode & S_IFMT) == S_IFDIR) {
465 		printf(_("\n*** %s: directory ***\n\n"), fs);
466 		ctl->current_file = NULL;
467 		return;
468 	}
469 #endif
470 	if (check_magic(ctl, fs)) {
471 		fclose(ctl->current_file);
472 		ctl->current_file = NULL;
473 		return;
474 	}
475 	fcntl(fileno(ctl->current_file), F_SETFD, FD_CLOEXEC);
476 	c = more_getc(ctl);
477 	ctl->clear_first = (c == '\f');
478 	more_ungetc(ctl, c);
479 	if ((ctl->file_size = st.st_size) == 0)
480 		ctl->file_size = ~((off_t)0);
481 }
482 
prepare_line_buffer(struct more_control * ctl)483 static void prepare_line_buffer(struct more_control *ctl)
484 {
485 	size_t sz = ctl->num_columns * 4;
486 
487 	if (ctl->line_sz >= sz)
488 		return;
489 
490 	if (sz < MIN_LINE_SZ)
491 		sz = MIN_LINE_SZ;
492 
493 	/* alloc sz and extra space for \n\0 */
494 	ctl->line_buf = xrealloc(ctl->line_buf, sz + 2);
495 	ctl->line_sz = sz;
496 }
497 
498 /* Get a logical line */
get_line(struct more_control * ctl,int * length)499 static int get_line(struct more_control *ctl, int *length)
500 {
501 	int c;
502 	char *p;
503 	int column;
504 	static int column_wrap;
505 
506 #ifdef HAVE_WIDECHAR
507 	size_t i;
508 	wchar_t wc;
509 	int wc_width;
510 	mbstate_t state, state_bak;	/* Current status of the stream. */
511 	char mbc[MB_LEN_MAX];		/* Buffer for one multibyte char. */
512 	size_t mblength;		/* Byte length of multibyte char. */
513 	size_t mbc_pos = 0;		/* Position of the MBC. */
514 	int use_mbc_buffer_flag = 0;	/* If 1, mbc has data. */
515 	int break_flag = 0;		/* If 1, exit while(). */
516 	off_t file_position_bak = ctl->file_position;
517 
518 	memset(&state, '\0', sizeof(mbstate_t));
519 #endif
520 
521 	p = ctl->line_buf;
522 	column = 0;
523 	c = more_getc(ctl);
524 	if (column_wrap && c == '\n') {
525 		ctl->current_line++;
526 		c = more_getc(ctl);
527 	}
528 	while (p < &ctl->line_buf[ctl->line_sz - 1]) {
529 #ifdef HAVE_WIDECHAR
530 		if (ctl->fold_long_lines && use_mbc_buffer_flag && MB_CUR_MAX > 1) {
531 			use_mbc_buffer_flag = 0;
532 			state_bak = state;
533 			mbc[mbc_pos++] = c;
534  process_mbc:
535 			mblength = mbrtowc(&wc, mbc, mbc_pos, &state);
536 
537 			switch (mblength) {
538 			case (size_t)-2:	/* Incomplete multibyte character. */
539 				use_mbc_buffer_flag = 1;
540 				state = state_bak;
541 				break;
542 
543 			case (size_t)-1:	/* Invalid as a multibyte character. */
544 				*p++ = mbc[0];
545 				state = state_bak;
546 				column++;
547 				file_position_bak++;
548 				if (column >= ctl->num_columns) {
549 					more_fseek(ctl, file_position_bak);
550 				} else {
551 					memmove(mbc, mbc + 1, --mbc_pos);
552 					if (mbc_pos > 0) {
553 						mbc[mbc_pos] = '\0';
554 						goto process_mbc;
555 					}
556 				}
557 				break;
558 
559 			default:
560 				wc_width = wcwidth(wc);
561 				if (column + wc_width > ctl->num_columns) {
562 					more_fseek(ctl, file_position_bak);
563 					break_flag = 1;
564 				} else {
565 					for (i = 0; p < &ctl->line_buf[ctl->line_sz - 1] &&
566 						    i < mbc_pos; i++)
567 						*p++ = mbc[i];
568 					if (wc_width > 0)
569 						column += wc_width;
570 				}
571 			}
572 
573 			if (break_flag || column >= ctl->num_columns)
574 				break;
575 
576 			c = more_getc(ctl);
577 			continue;
578 		}
579 #endif	/* HAVE_WIDECHAR */
580 		if (c == EOF) {
581 			if (p > ctl->line_buf) {
582 				*p = '\0';
583 				*length = p - ctl->line_buf;
584 				return column;
585 			}
586 			*length = p - ctl->line_buf;
587 			return EOF;
588 		}
589 		if (c == '\n') {
590 			ctl->current_line++;
591 			break;
592 		}
593 
594 		*p++ = c;
595 		if (c == '\t') {
596 			if (!ctl->hard_tabs || (column < ctl->prompt_len && !ctl->hard_tty)) {
597 				if (ctl->hard_tabs && ctl->erase_line && !ctl->dumb_tty) {
598 					column = 1 + (column | 7);
599 					putp(ctl->erase_line);
600 					ctl->prompt_len = 0;
601 				} else {
602 					for (--p; p < &ctl->line_buf[ctl->line_sz - 1];) {
603 						*p++ = ' ';
604 						if ((++column & 7) == 0)
605 							break;
606 					}
607 					if (column >= ctl->prompt_len)
608 						ctl->prompt_len = 0;
609 				}
610 			} else
611 				column = 1 + (column | 7);
612 		} else if (c == '\b' && column > 0) {
613 			column--;
614 		} else if (c == '\r') {
615 			int next = more_getc(ctl);
616 			if (next == '\n') {
617 				p--;
618 				ctl->current_line++;
619 				break;
620 			}
621 			more_ungetc(ctl, c);
622 			column = 0;
623 		} else if (c == '\f' && ctl->stop_after_formfeed) {
624 			p[-1] = '^';
625 			*p++ = 'L';
626 			column += 2;
627 			ctl->is_paused = 1;
628 		} else if (c == EOF) {
629 			*length = p - ctl->line_buf;
630 			return column;
631 		} else {
632 #ifdef HAVE_WIDECHAR
633 			if (ctl->fold_long_lines && MB_CUR_MAX > 1) {
634 				memset(mbc, '\0', MB_LEN_MAX);
635 				mbc_pos = 0;
636 				mbc[mbc_pos++] = c;
637 				state_bak = state;
638 
639 				mblength = mbrtowc(&wc, mbc, mbc_pos, &state);
640 				/* The value of mblength is always less than 2 here. */
641 				switch (mblength) {
642 				case (size_t)-2:
643 					p--;
644 					file_position_bak = ctl->file_position - 1;
645 					state = state_bak;
646 					use_mbc_buffer_flag = 1;
647 					break;
648 
649 				case (size_t)-1:
650 					state = state_bak;
651 					column++;
652 					break;
653 
654 				default:
655 					wc_width = wcwidth(wc);
656 					if (wc_width > 0)
657 						column += wc_width;
658 				}
659 			} else
660 #endif	/* HAVE_WIDECHAR */
661 			{
662 				if (isprint(c))
663 					column++;
664 			}
665 		}
666 
667 		if (column >= ctl->num_columns && ctl->fold_long_lines)
668 			break;
669 #ifdef HAVE_WIDECHAR
670 		if (use_mbc_buffer_flag == 0 && p >= &ctl->line_buf[ctl->line_sz - 1 - 4])
671 			/* don't read another char if there is no space for
672 			 * whole multibyte sequence */
673 			break;
674 #endif
675 		c = more_getc(ctl);
676 	}
677 	if (column >= ctl->num_columns && ctl->num_columns > 0) {
678 		if (!ctl->wrap_margin) {
679 			*p++ = '\n';
680 		}
681 	}
682 	column_wrap = column == ctl->num_columns && ctl->fold_long_lines;
683 	if (column_wrap && ctl->eat_newline && ctl->wrap_margin) {
684 		*p++ = '\n';	/* simulate normal wrap */
685 	}
686 	*length = p - ctl->line_buf;
687 	*p = 0;
688 	return column;
689 }
690 
691 /* Erase the rest of the prompt, assuming we are starting at column col. */
erase_to_col(struct more_control * ctl,int col)692 static void erase_to_col(struct more_control *ctl, int col)
693 {
694 
695 	if (ctl->prompt_len == 0)
696 		return;
697 	if (col == 0 && ctl->clear_line_ends)
698 		puts(ctl->erase_line);
699 	else if (ctl->hard_tty)
700 		putchar('\n');
701 	else {
702 		if (col == 0)
703 			putchar('\r');
704 		if (!ctl->dumb_tty && ctl->erase_line)
705 			putp(ctl->erase_line);
706 		else {
707 			printf("%*s", ctl->prompt_len - col, "");
708 			if (col == 0)
709 				putchar('\r');
710 		}
711 	}
712 	ctl->prompt_len = col;
713 }
714 
output_prompt(struct more_control * ctl,char * filename)715 static void output_prompt(struct more_control *ctl, char *filename)
716 {
717 	if (ctl->clear_line_ends)
718 		putp(ctl->erase_line);
719 	else if (ctl->prompt_len > 0)
720 		erase_to_col(ctl, 0);
721 	if (!ctl->hard_tty) {
722 		ctl->prompt_len = 0;
723 		if (ctl->enter_std) {
724 			putp(ctl->enter_std);
725 			ctl->prompt_len += (2 * ctl->stdout_glitch);
726 		}
727 		if (ctl->clear_line_ends)
728 			putp(ctl->erase_line);
729 		ctl->prompt_len += printf(_("--More--"));
730 		if (filename != NULL) {
731 			ctl->prompt_len += printf(_("(Next file: %s)"), filename);
732 		} else if (!ctl->no_tty_in) {
733 			ctl->prompt_len +=
734 			    printf("(%d%%)",
735 				   (int)((ctl->file_position * 100) / ctl->file_size));
736 		}
737 		if (ctl->suppress_bell) {
738 			ctl->prompt_len +=
739 			    printf(_("[Press space to continue, 'q' to quit.]"));
740 		}
741 		if (ctl->exit_std)
742 			putp(ctl->exit_std);
743 		if (ctl->clear_line_ends)
744 			putp(ctl->clear_rest);
745 	} else
746 		fprintf(stderr, "\a");
747 	fflush(NULL);
748 }
749 
reset_tty(struct more_control * ctl)750 static void reset_tty(struct more_control *ctl)
751 {
752 	if (ctl->no_tty_out)
753 		return;
754 	fflush(NULL);
755 	ctl->output_tty.c_lflag |= ICANON | ECHO;
756 	ctl->output_tty.c_cc[VMIN] = ctl->original_tty.c_cc[VMIN];
757 	ctl->output_tty.c_cc[VTIME] = ctl->original_tty.c_cc[VTIME];
758 	tcsetattr(STDERR_FILENO, TCSANOW, &ctl->original_tty);
759 }
760 
761 /* Clean up terminal state and exit. Also come here if interrupt signal received */
more_exit(struct more_control * ctl)762 static void __attribute__((__noreturn__)) more_exit(struct more_control *ctl)
763 {
764 #ifdef HAVE_MAGIC
765 	magic_close(ctl->magic);
766 #endif
767 	reset_tty(ctl);
768 	if (ctl->clear_line_ends) {
769 		putchar('\r');
770 		putp(ctl->erase_line);
771 	} else if (!ctl->clear_line_ends && (ctl->prompt_len > 0))
772 		erase_to_col(ctl, 0);
773 	fflush(NULL);
774 	free(ctl->previous_search);
775 	free(ctl->shell_line);
776 	free(ctl->line_buf);
777 	free(ctl->go_home);
778 	if (ctl->current_file)
779 		fclose(ctl->current_file);
780 	del_curterm(cur_term);
781 	_exit(EXIT_SUCCESS);
782 }
783 
read_user_input(struct more_control * ctl)784 static cc_t read_user_input(struct more_control *ctl)
785 {
786 	cc_t c;
787 
788 	errno = 0;
789 	/*
790 	 * Key commands can be read() from either stderr or stdin.  If they
791 	 * are read from stdin such as 'cat file.txt | more' then the pipe
792 	 * input is understood as series key commands - and that is not
793 	 * wanted.  Keep the read() reading from stderr.
794 	 */
795 	if (read(STDERR_FILENO, &c, 1) <= 0) {
796 		if (errno != EINTR)
797 			more_exit(ctl);
798 		else
799 			c = ctl->output_tty.c_cc[VKILL];
800 	}
801 	return c;
802 }
803 
804 /* Read a number and command from the terminal.  Set cmd to the non-digit
805  * which terminates the number. */
read_command(struct more_control * ctl)806 static struct number_command read_command(struct more_control *ctl)
807 {
808 	cc_t input[8] = { 0 };
809 	ssize_t i, ilen;
810 	struct number_command cmd = { .key = more_kc_unknown_command };
811 
812 	/* See stderr note in read_user_input() */
813 	if ((ilen = read(STDERR_FILENO, &input, sizeof(input))) <= 0)
814 		return cmd;
815 	if (2 < ilen) {
816 		if (!memcmp(input, ARROW_UP, sizeof(ARROW_UP))) {
817 			cmd.key = more_kc_backwards;
818 			return cmd;
819 		} else if (!memcmp(input, ARROW_DOWN, sizeof(ARROW_DOWN))) {
820 			cmd.key = more_kc_skip_forward;
821 			return cmd;
822 		} else if (!memcmp(input, PAGE_UP, sizeof(PAGE_UP))) {
823 			cmd.key = more_kc_backwards;
824 			return cmd;
825 		} else if (!memcmp(input, PAGE_DOWN, sizeof(PAGE_DOWN))) {
826 			cmd.key = more_kc_skip_forward;
827 			return cmd;
828 		}
829 	}
830 	for (i = 0; i < ilen; i++) {
831 		if (isdigit(input[i])) {
832 			if (0 < ctl->reading_num) {
833 				ctl->leading_number *= 10;
834 				ctl->leading_number += input[i] - '0';
835 			} else
836 				ctl->leading_number = input[i] - '0';
837 			ctl->reading_num = 1;
838 			continue;
839 		}
840 		cmd.number = ctl->leading_number;
841 		ctl->reading_num = 0;
842 		ctl->leading_number = 0;
843 		if (ctl->leading_colon) {
844 			ctl->leading_colon = 0;
845 			switch (input[i]) {
846 			case 'f':
847 				cmd.key = more_kc_display_file_and_line;
848 				return cmd;
849 			case 'n':
850 				cmd.key = more_kc_next_file;
851 				return cmd;
852 			case 'p':
853 				cmd.key = more_kc_previous_file;
854 				return cmd;
855 			default:
856 				cmd.key = more_kc_unknown_command;
857 				return cmd;
858 			}
859 		}
860 		/* command is a single char */
861 		switch (input[i]) {
862 		case '.':
863 			cmd.key = more_kc_repeat_previous;
864 			break;
865 		case ':':
866 			ctl->leading_colon = 1;
867 			break;
868 		case 'b':
869 		case CTRL('B'):
870 			cmd.key = more_kc_backwards;
871 			break;
872 		case ' ':
873 			cmd.key = more_kc_jump_lines_per_screen;
874 			break;
875 		case 'z':
876 			cmd.key = more_kc_set_lines_per_screen;
877 			break;
878 		case 'd':
879 		case CTRL('D'):
880 			cmd.key = more_kc_set_scroll_len;
881 			break;
882 		case 'q':
883 		case 'Q':
884 			cmd.key = more_kc_quit;
885 			break;
886 		case 'f':
887 		case 's':
888 		case CTRL('F'):
889 			cmd.key = more_kc_skip_forward;
890 			break;
891 		case '\n':
892 			cmd.key = more_kc_next_line;
893 			break;
894 		case '\f':
895 			cmd.key = more_kc_clear_screen;
896 			break;
897 		case '\'':
898 			cmd.key = more_kc_previous_search_match;
899 			break;
900 		case '=':
901 			cmd.key = more_kc_display_line;
902 			break;
903 		case 'n':
904 			cmd.key = more_kc_repeat_search;
905 			break;
906 		case '/':
907 			cmd.key = more_kc_search;
908 			break;
909 		case '!':
910 			cmd.key = more_kc_run_shell;
911 			break;
912 		case '?':
913 		case 'h':
914 			cmd.key = more_kc_help;
915 			break;
916 		case 'v':
917 			cmd.key = more_kc_run_editor;
918 			break;
919 		}
920 	}
921 	return cmd;
922 }
923 
924 /* Change displayed file from command line list to next nskip, where nskip
925  * is relative position in argv and can be negative, that is a previous
926  * file.  */
change_file(struct more_control * ctl,int nskip)927 static void change_file(struct more_control *ctl, int nskip)
928 {
929 	if (nskip == 0)
930 		return;
931 	if (nskip > 0) {
932 		if (ctl->argv_position + nskip > ctl->num_files - 1)
933 			nskip = ctl->num_files - ctl->argv_position - 1;
934 	}
935 	ctl->argv_position += nskip;
936 	if (ctl->argv_position < 0)
937 		ctl->argv_position = 0;
938 	puts(_("\n...Skipping "));
939 	if (ctl->clear_line_ends)
940 		putp(ctl->erase_line);
941 	if (nskip > 0)
942 		fputs(_("...Skipping to file "), stdout);
943 	else
944 		fputs(_("...Skipping back to file "), stdout);
945 	puts(ctl->file_names[ctl->argv_position]);
946 	if (ctl->clear_line_ends)
947 		putp(ctl->erase_line);
948 	putchar('\n');
949 	ctl->argv_position--;
950 }
951 
show(struct more_control * ctl,char c)952 static void show(struct more_control *ctl, char c)
953 {
954 	if ((c < ' ' && c != '\n' && c != ESC) || c == CERASE) {
955 		c += (c == CERASE) ? -0100 : 0100;
956 		fputs(CARAT, stderr);
957 		ctl->prompt_len++;
958 	}
959 	fputc(c, stderr);
960 	ctl->prompt_len++;
961 }
962 
more_error(struct more_control * ctl,char * mess)963 static void more_error(struct more_control *ctl, char *mess)
964 {
965 	if (ctl->clear_line_ends)
966 		putp(ctl->erase_line);
967 	else
968 		erase_to_col(ctl, 0);
969 	ctl->prompt_len += strlen(mess);
970 	if (ctl->enter_std)
971 		putp(ctl->enter_std);
972 	fputs(mess, stdout);
973 	if (ctl->exit_std)
974 		putp(ctl->exit_std);
975 	fflush(NULL);
976 	ctl->report_errors++;
977 }
978 
erase_one_column(struct more_control * ctl)979 static void erase_one_column(struct more_control *ctl)
980 {
981 	if (ctl->erase_previous_ok)
982 		fprintf(stderr, "%s ", ctl->backspace_ch);
983 	fputs(ctl->backspace_ch, stderr);
984 }
985 
ttyin(struct more_control * ctl,char buf[],int nmax,char pchar)986 static void ttyin(struct more_control *ctl, char buf[], int nmax, char pchar)
987 {
988 	char *sp;
989 	cc_t c;
990 	int slash = 0;
991 	int maxlen;
992 
993 	sp = buf;
994 	maxlen = 0;
995 	while (sp - buf < nmax) {
996 		if (ctl->prompt_len > maxlen)
997 			maxlen = ctl->prompt_len;
998 		c = read_user_input(ctl);
999 		if (c == '\\') {
1000 			slash++;
1001 		} else if (c == ctl->output_tty.c_cc[VERASE] && !slash) {
1002 			if (sp > buf) {
1003 #ifdef HAVE_WIDECHAR
1004 				if (MB_CUR_MAX > 1) {
1005 					wchar_t wc;
1006 					size_t pos = 0, mblength;
1007 					mbstate_t state, state_bak;
1008 
1009 					memset(&state, '\0', sizeof(mbstate_t));
1010 
1011 					while (1) {
1012 						state_bak = state;
1013 						mblength =
1014 						    mbrtowc(&wc, buf + pos,
1015 							    sp - buf, &state);
1016 
1017 						switch (mblength) {
1018 						case (size_t)-2:
1019 						case (size_t)-1:
1020 							state = state_bak;
1021 							/* fallthrough */
1022 						case 0:
1023 							mblength = 1;
1024 						}
1025 						if (buf + pos + mblength >= sp)
1026 							break;
1027 
1028 						pos += mblength;
1029 					}
1030 
1031 					if (mblength == 1) {
1032 						erase_one_column(ctl);
1033 					} else {
1034 						int wc_width;
1035 						wc_width = wcwidth(wc);
1036 						wc_width =
1037 						    (wc_width <
1038 						     1) ? 1 : wc_width;
1039 						while (wc_width--) {
1040 							erase_one_column(ctl);
1041 						}
1042 					}
1043 
1044 					while (mblength--) {
1045 						--ctl->prompt_len;
1046 						--sp;
1047 					}
1048 				} else
1049 #endif	/* HAVE_WIDECHAR */
1050 				{
1051 					--ctl->prompt_len;
1052 					erase_one_column(ctl);
1053 					--sp;
1054 				}
1055 
1056 				if ((*sp < ' ' && *sp != '\n') || *sp == CERASE) {
1057 					--ctl->prompt_len;
1058 					erase_one_column(ctl);
1059 				}
1060 				continue;
1061 			}
1062 			if (!ctl->erase_line)
1063 				ctl->prompt_len = maxlen;
1064 		} else if (c == ctl->output_tty.c_cc[VKILL] && !slash) {
1065 			if (ctl->hard_tty) {
1066 				show(ctl, c);
1067 				putchar('\n');
1068 				putchar(pchar);
1069 			} else {
1070 				putchar('\r');
1071 				putchar(pchar);
1072 				if (ctl->erase_line)
1073 					erase_to_col(ctl, 1);
1074 				else if (ctl->erase_input_ok)
1075 					while (ctl->prompt_len-- > 1)
1076 						fprintf(stderr, "%s %s", ctl->backspace_ch, ctl->backspace_ch);
1077 				ctl->prompt_len = 1;
1078 			}
1079 			sp = buf;
1080 			fflush(NULL);
1081 			continue;
1082 		}
1083 		if (slash && (c == ctl->output_tty.c_cc[VKILL] ||
1084 			      c == ctl->output_tty.c_cc[VERASE])) {
1085 			erase_one_column(ctl);
1086 			--sp;
1087 		}
1088 		if (c != '\\')
1089 			slash = 0;
1090 		*sp++ = c;
1091 		if ((c < ' ' && c != '\n' && c != ESC) || c == CERASE) {
1092 			c += (c == CERASE) ? -0100 : 0100;
1093 			fputs(CARAT, stderr);
1094 			ctl->prompt_len++;
1095 		}
1096 		if (c != '\n' && c != ESC) {
1097 			fputc(c, stderr);
1098 			ctl->prompt_len++;
1099 		} else
1100 			break;
1101 	}
1102 	*--sp = '\0';
1103 	if (!ctl->erase_line)
1104 		ctl->prompt_len = maxlen;
1105 	if (sp - buf >= nmax - 1)
1106 		more_error(ctl, _("Line too long"));
1107 }
1108 
1109 /* Expand shell command line. */
expand(struct more_control * ctl,char * inbuf)1110 static void expand(struct more_control *ctl, char *inbuf)
1111 {
1112 	char *inpstr;
1113 	char *outstr;
1114 	char c;
1115 	char *temp;
1116 	int tempsz, xtra, offset;
1117 
1118 	xtra = strlen(ctl->file_names[ctl->argv_position]) + strlen(ctl->shell_line) + 1;
1119 	tempsz = COMMAND_BUF + xtra;
1120 	temp = xmalloc(tempsz);
1121 	inpstr = inbuf;
1122 	outstr = temp;
1123 	while ((c = *inpstr++) != '\0') {
1124 		offset = outstr - temp;
1125 		if (tempsz - offset - 1 < xtra) {
1126 			tempsz += COMMAND_BUF + xtra;
1127 			temp = xrealloc(temp, tempsz);
1128 			outstr = temp + offset;
1129 		}
1130 		switch (c) {
1131 		case '%':
1132 			if (!ctl->no_tty_in) {
1133 				strcpy(outstr, ctl->file_names[ctl->argv_position]);
1134 				outstr += strlen(ctl->file_names[ctl->argv_position]);
1135 			} else
1136 				*outstr++ = c;
1137 			break;
1138 		case '!':
1139 			if (ctl->shell_line) {
1140 				strcpy(outstr, ctl->shell_line);
1141 				outstr += strlen(ctl->shell_line);
1142 			} else
1143 				more_error(ctl, _
1144 					   ("No previous command to substitute for"));
1145 			break;
1146 		case '\\':
1147 			if (*inpstr == '%' || *inpstr == '!') {
1148 				*outstr++ = *inpstr++;
1149 				break;
1150 			}
1151 			/* fallthrough */
1152 		default:
1153 			*outstr++ = c;
1154 		}
1155 	}
1156 	*outstr++ = '\0';
1157 	free(ctl->shell_line);
1158 	ctl->shell_line = temp;
1159 }
1160 
set_tty(struct more_control * ctl)1161 static void set_tty(struct more_control *ctl)
1162 {
1163 	ctl->output_tty.c_lflag &= ~(ICANON | ECHO);
1164 	ctl->output_tty.c_cc[VMIN] = 1;	/* read at least 1 char */
1165 	ctl->output_tty.c_cc[VTIME] = 0;	/* no timeout */
1166 	tcsetattr(STDERR_FILENO, TCSANOW, &ctl->output_tty);
1167 }
1168 
1169 /* Come here if a quit signal is received */
sigquit_handler(struct more_control * ctl)1170 static void sigquit_handler(struct more_control *ctl)
1171 {
1172 	if (!ctl->dumb_tty && ctl->no_quit_dialog) {
1173 		ctl->prompt_len += fprintf(stderr, _("[Use q or Q to quit]"));
1174 		ctl->no_quit_dialog = 0;
1175 	} else
1176 		more_exit(ctl);
1177 }
1178 
1179 /* Come here when we get a suspend signal from the terminal */
sigtstp_handler(struct more_control * ctl)1180 static void sigtstp_handler(struct more_control *ctl)
1181 {
1182 	reset_tty(ctl);
1183 	fflush(NULL);
1184 	kill(getpid(), SIGSTOP);
1185 }
1186 
1187 /* Come here when we get a continue signal from the terminal */
sigcont_handler(struct more_control * ctl)1188 static void sigcont_handler(struct more_control *ctl)
1189 {
1190 	set_tty(ctl);
1191 }
1192 
1193 /* Come here if a signal for a window size change is received */
sigwinch_handler(struct more_control * ctl)1194 static void sigwinch_handler(struct more_control *ctl)
1195 {
1196 	struct winsize win;
1197 
1198 	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1) {
1199 		if (win.ws_row != 0) {
1200 			ctl->lines_per_page = win.ws_row;
1201 			ctl->d_scroll_len = ctl->lines_per_page / 2 - 1;
1202 			if (ctl->d_scroll_len < 1)
1203 				ctl->d_scroll_len = 1;
1204 			ctl->lines_per_screen = ctl->lines_per_page - 1;
1205 		}
1206 		if (win.ws_col != 0)
1207 			ctl->num_columns = win.ws_col;
1208 	}
1209 	prepare_line_buffer(ctl);
1210 }
1211 
execute(struct more_control * ctl,char * filename,char * cmd,...)1212 static void execute(struct more_control *ctl, char *filename, char *cmd, ...)
1213 {
1214 	pid_t id;
1215 	va_list argp;
1216 	char *arg;
1217 	char **args;
1218 	int argcount;
1219 
1220 	fflush(NULL);
1221 	id = fork();
1222 	if (id == 0) {
1223 		int errsv;
1224 		if (!isatty(STDIN_FILENO)) {
1225 			close(STDIN_FILENO);
1226 			open("/dev/tty", 0);
1227 		}
1228 		reset_tty(ctl);
1229 
1230 		va_start(argp, cmd);
1231 		arg = va_arg(argp, char *);
1232 		argcount = 0;
1233 		while (arg) {
1234 			argcount++;
1235 			arg = va_arg(argp, char *);
1236 		}
1237 		va_end(argp);
1238 
1239 		args = alloca(sizeof(char *) * (argcount + 1));
1240 		args[argcount] = NULL;
1241 
1242 		va_start(argp, cmd);
1243 		arg = va_arg(argp, char *);
1244 		argcount = 0;
1245 		while (arg) {
1246 			args[argcount] = arg;
1247 			argcount++;
1248 			arg = va_arg(argp, char *);
1249 		}
1250 		va_end(argp);
1251 
1252 		if (geteuid() != getuid() || getegid() != getgid()) {
1253 			if (setuid(getuid()) < 0)
1254 				err(EXIT_FAILURE, _("setuid failed"));
1255 			if (setgid(getgid()) < 0)
1256 				err(EXIT_FAILURE, _("setgid failed"));
1257 		}
1258 
1259 		execvp(cmd, args);
1260 		errsv = errno;
1261 		fputs(_("exec failed\n"), stderr);
1262 		exit(errsv == ENOENT ? EX_EXEC_ENOENT : EX_EXEC_FAILED);
1263 	}
1264 	if (id > 0) {
1265 		errno = 0;
1266 		while (wait(NULL) > 0) {
1267 			if (errno == EINTR)
1268 				continue;
1269 		}
1270 	} else
1271 		fputs(_("can't fork\n"), stderr);
1272 	set_tty(ctl);
1273 	print_separator('-', 24);
1274 	output_prompt(ctl, filename);
1275 }
1276 
run_shell(struct more_control * ctl,char * filename)1277 static void run_shell(struct more_control *ctl, char *filename)
1278 {
1279 	char cmdbuf[COMMAND_BUF];
1280 
1281 	erase_to_col(ctl, 0);
1282 	putchar('!');
1283 	fflush(NULL);
1284 	if (ctl->previous_command.key == more_kc_run_shell && ctl->shell_line)
1285 		fputs(ctl->shell_line, stdout);
1286 	else {
1287 		ttyin(ctl, cmdbuf, sizeof(cmdbuf) - 2, '!');
1288 		if (strpbrk(cmdbuf, "%!\\"))
1289 			expand(ctl, cmdbuf);
1290 		else {
1291 			free(ctl->shell_line);
1292 			ctl->shell_line = xstrdup(cmdbuf);
1293 		}
1294 	}
1295 	fputc('\n', stderr);
1296 	fflush(NULL);
1297 	ctl->prompt_len = 0;
1298 	execute(ctl, filename, ctl->shell, ctl->shell, "-c", ctl->shell_line, 0);
1299 }
1300 
1301 /* Skip n lines in the file f */
skip_lines(struct more_control * ctl)1302 static void skip_lines(struct more_control *ctl)
1303 {
1304 	int c;
1305 
1306 	while (ctl->next_jump > 0) {
1307 		while ((c = more_getc(ctl)) != '\n')
1308 			if (c == EOF)
1309 				return;
1310 		ctl->next_jump--;
1311 		ctl->current_line++;
1312 	}
1313 }
1314 
1315 /*  Clear the screen */
more_clear_screen(struct more_control * ctl)1316 static void more_clear_screen(struct more_control *ctl)
1317 {
1318 	if (ctl->clear && !ctl->hard_tty) {
1319 		putp(ctl->clear);
1320 		/* Put out carriage return so that system doesn't get
1321 		 * confused by escape sequences when expanding tabs */
1322 		putchar('\r');
1323 		ctl->prompt_len = 0;
1324 	}
1325 }
1326 
read_line(struct more_control * ctl)1327 static void read_line(struct more_control *ctl)
1328 {
1329 	int c;
1330 	char *p;
1331 
1332 	p = ctl->line_buf;
1333 	while ((c = more_getc(ctl)) != '\n' && c != EOF
1334 	       && (ptrdiff_t)p != (ptrdiff_t)(ctl->line_buf + ctl->line_sz - 1))
1335 		*p++ = c;
1336 	if (c == '\n')
1337 		ctl->current_line++;
1338 	*p = '\0';
1339 }
1340 
more_poll(struct more_control * ctl,int timeout)1341 static int more_poll(struct more_control *ctl, int timeout)
1342 {
1343 	struct pollfd pfd[2];
1344 
1345 	pfd[0].fd = ctl->sigfd;
1346 	pfd[0].events = POLLIN | POLLERR | POLLHUP;
1347 	pfd[1].fd = STDIN_FILENO;
1348 	pfd[1].events = POLLIN;
1349 
1350 	if (poll(pfd, 2, timeout) < 0) {
1351 		if (errno == EAGAIN)
1352 			return 1;
1353 		more_error(ctl, _("poll failed"));
1354 		return 1;
1355 	}
1356 	if (pfd[0].revents != 0) {
1357 		struct signalfd_siginfo info;
1358 		ssize_t sz;
1359 
1360 		sz = read(pfd[0].fd, &info, sizeof(info));
1361 		assert(sz == sizeof(info));
1362 		switch (info.ssi_signo) {
1363 		case SIGINT:
1364 			more_exit(ctl);
1365 			break;
1366 		case SIGQUIT:
1367 			sigquit_handler(ctl);
1368 			break;
1369 		case SIGTSTP:
1370 			sigtstp_handler(ctl);
1371 			break;
1372 		case SIGCONT:
1373 			sigcont_handler(ctl);
1374 			break;
1375 		case SIGWINCH:
1376 			sigwinch_handler(ctl);
1377 			break;
1378 		default:
1379 			abort();
1380 		}
1381 	}
1382 	if (pfd[1].revents == 0)
1383 		return 1;
1384 	return 0;
1385 }
1386 
1387 /* Search for nth occurrence of regular expression contained in buf in
1388  * the file */
search(struct more_control * ctl,char buf[],int n)1389 static void search(struct more_control *ctl, char buf[], int n)
1390 {
1391 	off_t startline = ctl->file_position;
1392 	off_t line1 = startline;
1393 	off_t line2 = startline;
1394 	off_t line3;
1395 	int lncount;
1396 	int saveln, rc;
1397 	regex_t re;
1398 
1399 	if (buf != ctl->previous_search) {
1400 		free(ctl->previous_search);
1401 		ctl->previous_search = buf;
1402 	}
1403 
1404 	ctl->search_called = 1;
1405 	ctl->context.line_num = saveln = ctl->current_line;
1406 	ctl->context.row_num = startline;
1407 	lncount = 0;
1408 	if (!buf)
1409 		goto notfound;
1410 	if ((rc = regcomp(&re, buf, REG_NOSUB)) != 0) {
1411 		char s[REGERR_BUF];
1412 		regerror(rc, &re, s, sizeof s);
1413 		more_error(ctl, s);
1414 		return;
1415 	}
1416 	while (!feof(ctl->current_file)) {
1417 		line3 = line2;
1418 		line2 = line1;
1419 		line1 = ctl->file_position;
1420 		read_line(ctl);
1421 		lncount++;
1422 		if (regexec(&re, ctl->line_buf, 0, NULL, 0) == 0 && --n == 0) {
1423 			if ((1 < lncount && ctl->no_tty_in) || 3 < lncount) {
1424 				putchar('\n');
1425 				if (ctl->clear_line_ends)
1426 					putp(ctl->erase_line);
1427 				fputs(_("...skipping\n"), stdout);
1428 			}
1429 			if (!ctl->no_tty_in) {
1430 				ctl->current_line -= (lncount < 3 ? lncount : 3);
1431 				more_fseek(ctl, line3);
1432 				if (ctl->no_scroll) {
1433 					if (ctl->clear_line_ends) {
1434 						putp(ctl->go_home);
1435 						putp(ctl->erase_line);
1436 					} else
1437 						more_clear_screen(ctl);
1438 				}
1439 			} else {
1440 				erase_to_col(ctl, 0);
1441 				if (ctl->no_scroll) {
1442 					if (ctl->clear_line_ends) {
1443 						putp(ctl->go_home);
1444 						putp(ctl->erase_line);
1445 					} else
1446 						more_clear_screen(ctl);
1447 				}
1448 				puts(ctl->line_buf);
1449 			}
1450 			break;
1451 		}
1452 		more_poll(ctl, 1);
1453 	}
1454 	/* Move ctrl+c signal handling back to more_key_command(). */
1455 	signal(SIGINT, SIG_DFL);
1456 	sigaddset(&ctl->sigset, SIGINT);
1457 	sigprocmask(SIG_BLOCK, &ctl->sigset, NULL);
1458 	regfree(&re);
1459 	if (feof(ctl->current_file)) {
1460 		if (!ctl->no_tty_in) {
1461 			ctl->current_line = saveln;
1462 			more_fseek(ctl, startline);
1463 		} else {
1464 			fputs(_("\nPattern not found\n"), stdout);
1465 			more_exit(ctl);
1466 		}
1467 notfound:
1468 		more_error(ctl, _("Pattern not found"));
1469 	}
1470 }
1471 
find_editor(void)1472 static char *find_editor(void)
1473 {
1474 	static char *editor;
1475 
1476 	editor = getenv("VISUAL");
1477 	if (editor == NULL || *editor == '\0')
1478 		editor = getenv("EDITOR");
1479 	if (editor == NULL || *editor == '\0')
1480 		editor = _PATH_VI;
1481 	return editor;
1482 }
1483 
runtime_usage(void)1484 static void runtime_usage(void)
1485 {
1486 	fputs(_("Most commands optionally preceded by integer argument k.  "
1487 		"Defaults in brackets.\n"
1488 		"Star (*) indicates argument becomes new default.\n"), stdout);
1489 	print_separator('-', 79);
1490 	fprintf(stdout,
1491 		_
1492 		("<space>                 Display next k lines of text [current screen size]\n"
1493 		 "z                       Display next k lines of text [current screen size]*\n"
1494 		 "<return>                Display next k lines of text [1]*\n"
1495 		 "d or ctrl-D             Scroll k lines [current scroll size, initially 11]*\n"
1496 		 "q or Q or <interrupt>   Exit from more\n"
1497 		 "s                       Skip forward k lines of text [1]\n"
1498 		 "f                       Skip forward k screenfuls of text [1]\n"
1499 		 "b or ctrl-B             Skip backwards k screenfuls of text [1]\n"
1500 		 "'                       Go to place where previous search started\n"
1501 		 "=                       Display current line number\n"
1502 		 "/<regular expression>   Search for kth occurrence of regular expression [1]\n"
1503 		 "n                       Search for kth occurrence of last r.e [1]\n"
1504 		 "!<cmd> or :!<cmd>       Execute <cmd> in a subshell\n"
1505 		 "v                       Start up '%s' at current line\n"
1506 		 "ctrl-L                  Redraw screen\n"
1507 		 ":n                      Go to kth next file [1]\n"
1508 		 ":p                      Go to kth previous file [1]\n"
1509 		 ":f                      Display current file name and line number\n"
1510 		 ".                       Repeat previous command\n"),
1511 		find_editor());
1512 	print_separator('-', 79);
1513 }
1514 
execute_editor(struct more_control * ctl,char * cmdbuf,char * filename)1515 static void execute_editor(struct more_control *ctl, char *cmdbuf, char *filename)
1516 {
1517 	char *editor, *p;
1518 	int split = 0;
1519 	int n;
1520 
1521 	if ((ctl->current_line - ctl->lines_per_screen) < 1)
1522 		n = 1;
1523 	else
1524 		n = ctl->current_line - (ctl->lines_per_screen + 1) / 2;
1525 	editor = find_editor();
1526 	p = strrchr(editor, '/');
1527 	if (p)
1528 		p++;
1529 	else
1530 		p = editor;
1531 	/*
1532 	 * Earlier: call vi +n file. This also works for emacs.
1533 	 * POSIX: call vi -c n file (when editor is vi or ex).
1534 	 */
1535 	if (!strcmp(p, "vi") || !strcmp(p, "ex")) {
1536 		sprintf(cmdbuf, "-c %d", n);
1537 		split = 1;
1538 	} else
1539 		sprintf(cmdbuf, "+%d", n);
1540 
1541 	erase_to_col(ctl, 0);
1542 	printf("%s %s %s", editor, cmdbuf, ctl->file_names[ctl->argv_position]);
1543 	if (split) {
1544 		cmdbuf[2] = 0;
1545 		execute(ctl, filename, editor, editor,
1546 			cmdbuf, cmdbuf + 3,
1547 			ctl->file_names[ctl->argv_position], (char *)0);
1548 	} else
1549 		execute(ctl, filename, editor, editor,
1550 			cmdbuf, ctl->file_names[ctl->argv_position], (char *)0);
1551 }
1552 
skip_backwards(struct more_control * ctl,int nlines)1553 static int skip_backwards(struct more_control *ctl, int nlines)
1554 {
1555 	if (nlines == 0)
1556 		nlines++;
1557 	erase_to_col(ctl, 0);
1558 	printf(P_("...back %d page", "...back %d pages", nlines), nlines);
1559 	putchar('\n');
1560 	ctl->next_jump = ctl->current_line - (ctl->lines_per_screen * (nlines + 1)) - 1;
1561 	if (ctl->next_jump < 0)
1562 		ctl->next_jump = 0;
1563 	more_fseek(ctl, 0);
1564 	ctl->current_line = 0;
1565 	skip_lines(ctl);
1566 	return ctl->lines_per_screen;
1567 }
1568 
skip_forwards(struct more_control * ctl,int nlines,cc_t comchar)1569 static int skip_forwards(struct more_control *ctl, int nlines, cc_t comchar)
1570 {
1571 	int c;
1572 
1573 	if (nlines == 0)
1574 		nlines++;
1575 	if (comchar == 'f')
1576 		nlines *= ctl->lines_per_screen;
1577 	putchar('\r');
1578 	erase_to_col(ctl, 0);
1579 	putchar('\n');
1580 	if (ctl->clear_line_ends)
1581 		putp(ctl->erase_line);
1582 	printf(P_("...skipping %d line",
1583 		  "...skipping %d lines", nlines), nlines);
1584 
1585 	if (ctl->clear_line_ends)
1586 		putp(ctl->erase_line);
1587 	putchar('\n');
1588 
1589 	while (nlines > 0) {
1590 		while ((c = more_getc(ctl)) != '\n')
1591 			if (c == EOF)
1592 				return 0;
1593 		ctl->current_line++;
1594 		nlines--;
1595 	}
1596 	return 1;
1597 }
1598 
1599 /* Read a command and do it.  A command consists of an optional integer
1600  * argument followed by the command character.  Return the number of
1601  * lines to display in the next screenful.  If there is nothing more to
1602  * display in the current file, zero is returned. */
more_key_command(struct more_control * ctl,char * filename)1603 static int more_key_command(struct more_control *ctl, char *filename)
1604 {
1605 	int retval = 0;
1606 	int done = 0, search_again = 0;
1607 	char cmdbuf[INIT_BUF];
1608 	struct number_command cmd;
1609 
1610 	if (!ctl->report_errors)
1611 		output_prompt(ctl, filename);
1612 	else
1613 		ctl->report_errors = 0;
1614 	ctl->search_called = 0;
1615 	for (;;) {
1616 		if (more_poll(ctl, -1) != 0)
1617 			continue;
1618 		cmd = read_command(ctl);
1619 		if (cmd.key == more_kc_unknown_command)
1620 			continue;
1621 		if (cmd.key == more_kc_repeat_previous)
1622 			cmd = ctl->previous_command;
1623 		switch (cmd.key) {
1624 		case more_kc_backwards:
1625 			if (ctl->no_tty_in) {
1626 				fprintf(stderr, "\a");
1627 				return -1;
1628 			}
1629 			retval = skip_backwards(ctl, cmd.number);
1630 			done = 1;
1631 			break;
1632 		case more_kc_jump_lines_per_screen:
1633 		case more_kc_set_lines_per_screen:
1634 			if (cmd.number == 0)
1635 				cmd.number = ctl->lines_per_screen;
1636 			else if (cmd.key == more_kc_set_lines_per_screen)
1637 				ctl->lines_per_screen = cmd.number;
1638 			retval = cmd.number;
1639 			done = 1;
1640 			break;
1641 		case more_kc_set_scroll_len:
1642 			if (cmd.number != 0)
1643 				ctl->d_scroll_len = cmd.number;
1644 			retval = ctl->d_scroll_len;
1645 			done = 1;
1646 			break;
1647 		case more_kc_quit:
1648 			more_exit(ctl);
1649 		case more_kc_skip_forward:
1650 			if (skip_forwards(ctl, cmd.number, cmd.number))
1651 				retval = ctl->lines_per_screen;
1652 			done = 1;
1653 			break;
1654 		case more_kc_next_line:
1655 			if (cmd.number != 0)
1656 				ctl->lines_per_screen = cmd.number;
1657 			else
1658 				cmd.number = 1;
1659 			retval = cmd.number;
1660 			done = 1;
1661 			break;
1662 		case more_kc_clear_screen:
1663 			if (!ctl->no_tty_in) {
1664 				more_clear_screen(ctl);
1665 				more_fseek(ctl, ctl->screen_start.row_num);
1666 				ctl->current_line = ctl->screen_start.line_num;
1667 				retval = ctl->lines_per_screen;
1668 				done = 1;
1669 				break;
1670 			} else {
1671 				fprintf(stderr, "\a");
1672 				break;
1673 			}
1674 		case more_kc_previous_search_match:
1675 			if (!ctl->no_tty_in) {
1676 				erase_to_col(ctl, 0);
1677 				fputs(_("\n***Back***\n\n"), stdout);
1678 				more_fseek(ctl, ctl->context.row_num);
1679 				ctl->current_line = ctl->context.line_num;
1680 				retval = ctl->lines_per_screen;
1681 				done = 1;
1682 				break;
1683 			} else {
1684 				fprintf(stderr, "\a");
1685 				break;
1686 			}
1687 		case more_kc_display_line:
1688 			erase_to_col(ctl, 0);
1689 			ctl->prompt_len = printf("%d", ctl->current_line);
1690 			fflush(NULL);
1691 			break;
1692 		case more_kc_display_file_and_line:
1693 			erase_to_col(ctl, 0);
1694 			if (!ctl->no_tty_in)
1695 				ctl->prompt_len =
1696 				    printf(_("\"%s\" line %d"),
1697 				           ctl->file_names[ctl->argv_position], ctl->current_line);
1698 			else
1699 				ctl->prompt_len = printf(_("[Not a file] line %d"),
1700 							 ctl->current_line);
1701 			fflush(NULL);
1702 			break;
1703 		case more_kc_repeat_search:
1704 			if (!ctl->previous_search) {
1705 				more_error(ctl, _("No previous regular expression"));
1706 				break;
1707 			}
1708 			search_again = 1;
1709 			/* fallthrough */
1710 		case more_kc_search:
1711 			if (cmd.number == 0)
1712 				cmd.number++;
1713 			erase_to_col(ctl, 0);
1714 			putchar('/');
1715 			ctl->prompt_len = 1;
1716 			fflush(NULL);
1717 			if (search_again) {
1718 				fputc('\r', stderr);
1719 				search(ctl, ctl->previous_search, cmd.number);
1720 				search_again = 0;
1721 			} else {
1722 				ttyin(ctl, cmdbuf, sizeof(cmdbuf) - 2, '/');
1723 				fputc('\r', stderr);
1724 				ctl->next_search = xstrdup(cmdbuf);
1725 				search(ctl, ctl->next_search, cmd.number);
1726 			}
1727 			retval = ctl->lines_per_screen - 1;
1728 			done = 1;
1729 			break;
1730 		case more_kc_run_shell:
1731 			run_shell(ctl, filename);
1732 			break;
1733 		case more_kc_help:
1734 			if (ctl->no_scroll)
1735 				more_clear_screen(ctl);
1736 			erase_to_col(ctl, 0);
1737 			runtime_usage();
1738 			output_prompt(ctl, filename);
1739 			break;
1740 		case more_kc_next_file:
1741 			putchar('\r');
1742 			erase_to_col(ctl, 0);
1743 			if (cmd.number == 0)
1744 				cmd.number = 1;
1745 			if (ctl->argv_position + cmd.number >= (unsigned int)ctl->num_files)
1746 				more_exit(ctl);
1747 			change_file(ctl, cmd.number);
1748 			done = 1;
1749 			break;
1750 		case more_kc_previous_file:
1751 			if (ctl->no_tty_in) {
1752 				fprintf(stderr, "\a");
1753 				break;
1754 			}
1755 			putchar('\r');
1756 			erase_to_col(ctl, 0);
1757 			if (cmd.number == 0)
1758 				cmd.number = 1;
1759 			change_file(ctl, -cmd.number);
1760 			done = 1;
1761 			break;
1762 		case more_kc_run_editor:	/* This case should go right before default */
1763 			if (!ctl->no_tty_in) {
1764 				execute_editor(ctl, cmdbuf, filename);
1765 				break;
1766 			}
1767 			/* fallthrough */
1768 		default:
1769 			if (ctl->suppress_bell) {
1770 				erase_to_col(ctl, 0);
1771 				if (ctl->enter_std)
1772 					putp(ctl->enter_std);
1773 				ctl->prompt_len =
1774 				    printf(_("[Press 'h' for instructions.]"))
1775 					    + 2 * ctl->stdout_glitch;
1776 				if (ctl->exit_std)
1777 					putp(ctl->exit_std);
1778 			} else
1779 				fprintf(stderr, "\a");
1780 			fflush(NULL);
1781 			break;
1782 		}
1783 		ctl->previous_command = cmd;
1784 		if (done) {
1785 			cmd.key = more_kc_unknown_command;
1786 			break;
1787 		}
1788 	}
1789 	putchar('\r');
1790 	ctl->no_quit_dialog = 1;
1791 	return retval;
1792 }
1793 
1794 /* Print out the contents of the file f, one screenful at a time. */
screen(struct more_control * ctl,int num_lines)1795 static void screen(struct more_control *ctl, int num_lines)
1796 {
1797 	int c;
1798 	int nchars;
1799 	int length;			/* length of current line */
1800 	static int prev_len = 1;	/* length of previous line */
1801 
1802 	for (;;) {
1803 		while (num_lines > 0 && !ctl->is_paused) {
1804 			if ((nchars = get_line(ctl, &length)) == EOF) {
1805 				if (ctl->clear_line_ends)
1806 					putp(ctl->clear_rest);
1807 				return;
1808 			}
1809 			if (ctl->squeeze_spaces && length == 0 && prev_len == 0)
1810 				continue;
1811 			prev_len = length;
1812 			if (ctl->bad_stdout
1813 			    || ((ctl->enter_std && *ctl->enter_std == ' ') && (ctl->prompt_len > 0)))
1814 				erase_to_col(ctl, 0);
1815 			/* must clear before drawing line since tabs on
1816 			 * some terminals do not erase what they tab
1817 			 * over. */
1818 			if (ctl->clear_line_ends)
1819 				putp(ctl->erase_line);
1820 			fwrite(ctl->line_buf, length, 1, stdout);
1821 			if (nchars < ctl->prompt_len)
1822 				erase_to_col(ctl, nchars);
1823 			ctl->prompt_len = 0;
1824 			if (nchars < ctl->num_columns || !ctl->fold_long_lines)
1825 				putchar('\n');
1826 			num_lines--;
1827 		}
1828 		fflush(NULL);
1829 		if ((c = more_getc(ctl)) == EOF) {
1830 			if (ctl->clear_line_ends)
1831 				putp(ctl->clear_rest);
1832 			return;
1833 		}
1834 
1835 		if (ctl->is_paused && ctl->clear_line_ends)
1836 			putp(ctl->clear_rest);
1837 		more_ungetc(ctl, c);
1838 		ctl->is_paused = 0;
1839 		do {
1840 			if ((num_lines = more_key_command(ctl, NULL)) == 0)
1841 				return;
1842 		} while (ctl->search_called && !ctl->previous_search);
1843 		if (ctl->hard_tty && ctl->prompt_len > 0)
1844 			erase_to_col(ctl, 0);
1845 		if (ctl->no_scroll && num_lines >= ctl->lines_per_screen) {
1846 			if (ctl->clear_line_ends)
1847 				putp(ctl->go_home);
1848 			else
1849 				more_clear_screen(ctl);
1850 		}
1851 		ctl->screen_start.line_num = ctl->current_line;
1852 		ctl->screen_start.row_num = ctl->file_position;
1853 	}
1854 }
1855 
copy_file(FILE * f)1856 static void copy_file(FILE *f)
1857 {
1858 	char buf[BUFSIZ];
1859 	size_t sz;
1860 
1861 	while ((sz = fread(&buf, sizeof(char), sizeof(buf), f)) > 0)
1862 		fwrite(&buf, sizeof(char), sz, stdout);
1863 }
1864 
1865 
display_file(struct more_control * ctl,int left)1866 static void display_file(struct more_control *ctl, int left)
1867 {
1868 	if (!ctl->current_file)
1869 		return;
1870 	ctl->context.line_num = ctl->context.row_num = 0;
1871 	ctl->current_line = 0;
1872 	if (ctl->first_file) {
1873 		ctl->first_file = 0;
1874 		if (ctl->next_jump)
1875 			skip_lines(ctl);
1876 		if (ctl->search_at_start) {
1877 			search(ctl, ctl->next_search, 1);
1878 			if (ctl->no_scroll)
1879 				left--;
1880 		}
1881 	} else if (ctl->argv_position < ctl->num_files && !ctl->no_tty_out)
1882 		left =
1883 		    more_key_command(ctl, ctl->file_names[ctl->argv_position]);
1884 	if (left != 0) {
1885 		if ((ctl->no_scroll || ctl->clear_first)
1886 		    && ctl->file_size != ~((off_t)0)) {
1887 			if (ctl->clear_line_ends)
1888 				putp(ctl->go_home);
1889 			else
1890 				more_clear_screen(ctl);
1891 		}
1892 		if (ctl->print_banner) {
1893 			if (ctl->bad_stdout)
1894 				erase_to_col(ctl, 0);
1895 			if (ctl->clear_line_ends)
1896 				putp(ctl->erase_line);
1897 			if (ctl->prompt_len > 14)
1898 				erase_to_col(ctl, 14);
1899 			if (ctl->clear_line_ends)
1900 				putp(ctl->erase_line);
1901 			print_separator(':', 14);
1902 			puts(ctl->file_names[ctl->argv_position]);
1903 			if (ctl->clear_line_ends)
1904 				putp(ctl->erase_line);
1905 			print_separator(':', 14);
1906 			if (left > ctl->lines_per_page - 4)
1907 				left = ctl->lines_per_page - 4;
1908 		}
1909 		if (ctl->no_tty_out)
1910 			copy_file(ctl->current_file);
1911 		else
1912 			screen(ctl, left);
1913 	}
1914 	fflush(NULL);
1915 	fclose(ctl->current_file);
1916 	ctl->current_file = NULL;
1917 	ctl->screen_start.line_num = ctl->screen_start.row_num = 0;
1918 	ctl->context.line_num = ctl->context.row_num = 0L;
1919 }
1920 
initterm(struct more_control * ctl)1921 static void initterm(struct more_control *ctl)
1922 {
1923 	int ret;
1924 	char *term;
1925 	struct winsize win;
1926 	char *cursor_addr;
1927 
1928 #ifndef NON_INTERACTIVE_MORE
1929 	ctl->no_tty_out = tcgetattr(STDOUT_FILENO, &ctl->output_tty);
1930 #endif
1931 	ctl->no_tty_in = tcgetattr(STDIN_FILENO, &ctl->output_tty);
1932 	tcgetattr(STDERR_FILENO, &ctl->output_tty);
1933 	ctl->original_tty = ctl->output_tty;
1934 	ctl->hard_tabs = (ctl->output_tty.c_oflag & TABDLY) != TAB3;
1935 	if (ctl->no_tty_out)
1936 		return;
1937 
1938 	ctl->output_tty.c_lflag &= ~(ICANON | ECHO);
1939 	ctl->output_tty.c_cc[VMIN] = 1;
1940 	ctl->output_tty.c_cc[VTIME] = 0;
1941 	ctl->erase_previous_ok = (ctl->output_tty.c_cc[VERASE] != 255);
1942 	ctl->erase_input_ok = (ctl->output_tty.c_cc[VKILL] != 255);
1943 	if ((term = getenv("TERM")) == NULL) {
1944 		ctl->dumb_tty = 1;
1945 	}
1946 	setupterm(term, 1, &ret);
1947 	if (ret <= 0) {
1948 		ctl->dumb_tty = 1;
1949 		return;
1950 	}
1951 	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) < 0) {
1952 		ctl->lines_per_page = tigetnum(TERM_LINES);
1953 		ctl->num_columns = tigetnum(TERM_COLS);
1954 	} else {
1955 		if ((ctl->lines_per_page = win.ws_row) == 0)
1956 			ctl->lines_per_page = tigetnum(TERM_LINES);
1957 		if ((ctl->num_columns = win.ws_col) == 0)
1958 			ctl->num_columns = tigetnum(TERM_COLS);
1959 	}
1960 	if ((ctl->lines_per_page <= 0) || tigetflag(TERM_HARD_COPY)) {
1961 		ctl->hard_tty = 1;
1962 		ctl->lines_per_page = LINES_PER_PAGE;
1963 	}
1964 
1965 	if (tigetflag(TERM_EAT_NEW_LINE))
1966 		/* Eat newline at last column + 1; dec, concept */
1967 		ctl->eat_newline++;
1968 	if (ctl->num_columns <= 0)
1969 		ctl->num_columns = NUM_COLUMNS;
1970 
1971 	ctl->wrap_margin = tigetflag(TERM_AUTO_RIGHT_MARGIN);
1972 	ctl->bad_stdout = tigetflag(TERM_CEOL);
1973 	ctl->erase_line = tigetstr(TERM_CLEAR_TO_LINE_END);
1974 	ctl->clear = tigetstr(TERM_CLEAR);
1975 	if ((ctl->enter_std = tigetstr(TERM_STANDARD_MODE)) != NULL) {
1976 		ctl->exit_std = tigetstr(TERM_EXIT_STANDARD_MODE);
1977 		if (0 < tigetnum(TERM_STD_MODE_GLITCH))
1978 			ctl->stdout_glitch = 1;
1979 	}
1980 
1981 	cursor_addr = tigetstr(TERM_HOME);
1982 	if (cursor_addr == NULL || *cursor_addr == '\0') {
1983 		cursor_addr = tigetstr(TERM_CURSOR_ADDRESS);
1984 		if (cursor_addr)
1985 			cursor_addr = tparm(cursor_addr, 0, 0);
1986 	}
1987 	if (cursor_addr)
1988 		ctl->go_home = xstrdup(cursor_addr);
1989 
1990 	if ((ctl->move_line_down = tigetstr(TERM_LINE_DOWN)) == NULL)
1991 		ctl->move_line_down = BACKSPACE;
1992 	ctl->clear_rest = tigetstr(TERM_CLEAR_TO_SCREEN_END);
1993 	if ((ctl->backspace_ch = tigetstr(TERM_BACKSPACE)) == NULL)
1994 		ctl->backspace_ch = BACKSPACE;
1995 
1996 	if ((ctl->shell = getenv("SHELL")) == NULL)
1997 		ctl->shell = _PATH_BSHELL;
1998 }
1999 
main(int argc,char ** argv)2000 int main(int argc, char **argv)
2001 {
2002 	char *s;
2003 	int left;
2004 	struct more_control ctl = {
2005 		.first_file = 1,
2006 		.fold_long_lines = 1,
2007 		.no_quit_dialog = 1,
2008 		.stop_after_formfeed = 1,
2009 		.wrap_margin = 1,
2010 		.lines_per_page = LINES_PER_PAGE,
2011 		.num_columns = NUM_COLUMNS,
2012 		.d_scroll_len = SCROLL_LEN,
2013 		0
2014 	};
2015 
2016 	setlocale(LC_ALL, "");
2017 	bindtextdomain(PACKAGE, LOCALEDIR);
2018 	textdomain(PACKAGE);
2019 	close_stdout_atexit();
2020 	setlocale(LC_ALL, "");
2021 
2022 	/* Auto set no scroll on when binary is called page */
2023 	if (!(strcmp(program_invocation_short_name, "page")))
2024 		ctl.no_scroll++;
2025 
2026 	if ((s = getenv("MORE")) != NULL)
2027 		env_argscan(&ctl, s);
2028 	argscan(&ctl, argc, argv);
2029 
2030 	initterm(&ctl);
2031 
2032 #ifdef HAVE_MAGIC
2033 	ctl.magic = magic_open(MAGIC_MIME_ENCODING | MAGIC_SYMLINK);
2034 	magic_load(ctl.magic, NULL);
2035 #endif
2036 	prepare_line_buffer(&ctl);
2037 
2038 	ctl.d_scroll_len = ctl.lines_per_page / 2 - 1;
2039 	if (ctl.d_scroll_len <= 0)
2040 		ctl.d_scroll_len = 1;
2041 
2042 	/* allow clear_line_ends only if go_home and erase_line and clear_rest strings are
2043 	 * defined, and in that case, make sure we are in no_scroll mode */
2044 	if (ctl.clear_line_ends) {
2045 		if ((ctl.go_home == NULL) || (*ctl.go_home == '\0') ||
2046 		    (ctl.erase_line == NULL) || (*ctl.erase_line == '\0') ||
2047 		    (ctl.clear_rest == NULL) || (*ctl.clear_rest == '\0'))
2048 			ctl.clear_line_ends = 0;
2049 		else
2050 			ctl.no_scroll = 1;
2051 	}
2052 	if (ctl.lines_per_screen == 0)
2053 		ctl.lines_per_screen = ctl.lines_per_page - 1;
2054 	left = ctl.lines_per_screen;
2055 	if (ctl.num_files > 1)
2056 		ctl.print_banner = 1;
2057 	if (!ctl.no_tty_in && ctl.num_files == 0) {
2058 		warnx(_("bad usage"));
2059 		errtryhelp(EXIT_FAILURE);
2060 	} else
2061 		ctl.current_file = stdin;
2062 	if (!ctl.no_tty_out) {
2063 		if (signal(SIGTSTP, SIG_IGN) == SIG_DFL) {
2064 			ctl.catch_suspend++;
2065 		}
2066 		tcsetattr(STDERR_FILENO, TCSANOW, &ctl.output_tty);
2067 	}
2068 	sigemptyset(&ctl.sigset);
2069 	sigaddset(&ctl.sigset, SIGINT);
2070 	sigaddset(&ctl.sigset, SIGQUIT);
2071 	sigaddset(&ctl.sigset, SIGTSTP);
2072 	sigaddset(&ctl.sigset, SIGCONT);
2073 	sigaddset(&ctl.sigset, SIGWINCH);
2074 	sigprocmask(SIG_BLOCK, &ctl.sigset, NULL);
2075 	ctl.sigfd = signalfd(-1, &ctl.sigset, SFD_CLOEXEC);
2076 	if (ctl.no_tty_in) {
2077 		if (ctl.no_tty_out)
2078 			copy_file(stdin);
2079 		else {
2080 			ctl.current_file = stdin;
2081 			display_file(&ctl, left);
2082 		}
2083 		ctl.no_tty_in = 0;
2084 		ctl.print_banner = 1;
2085 		ctl.first_file = 0;
2086 	}
2087 
2088 	while (ctl.argv_position < ctl.num_files) {
2089 		checkf(&ctl, ctl.file_names[ctl.argv_position]);
2090 		display_file(&ctl, left);
2091 		ctl.first_file = 0;
2092 		ctl.argv_position++;
2093 	}
2094 	ctl.clear_line_ends = 0;
2095 	ctl.prompt_len = 0;
2096 	more_exit(&ctl);
2097 }
2098