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