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