]> git.ipfire.org Git - thirdparty/util-linux.git/blob - text-utils/more.c
more: avoid libmagic telling an empty file is binary
[thirdparty/util-linux.git] / text-utils / more.c
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
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
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
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
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
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
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
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. */
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. */
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
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 */
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. */
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
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
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 */
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
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. */
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. */
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
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
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
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
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. */
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
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 */
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 */
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 */
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 */
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
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
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 */
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 */
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
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
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 */
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
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
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
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
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
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. */
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. */
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
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
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
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
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 }