]> git.ipfire.org Git - thirdparty/util-linux.git/blame - text-utils/more.c
Merge branch 'logger-wrong-typo' of https://github.com/chentooerl/util-linux
[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>
508b2d79 60#include <sys/ttydefaults.h>
fd6b7a7f 61#include <sys/wait.h>
a8f98304 62#include <regex.h>
823ef319
SK
63#include <assert.h>
64#include <poll.h>
65#include <sys/signalfd.h>
f8492042 66#include <paths.h>
e3cb27f5 67#include <getopt.h>
a8f98304
SK
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
0d4e5f8e 76
09070e1a
SK
77#ifdef HAVE_MAGIC
78# include <magic.h>
79#endif
80
b87cbe84 81#include "strutils.h"
7eda085c 82#include "nls.h"
0d4e5f8e 83#include "xalloc.h"
1d4ad1de 84#include "widechar.h"
b87cbe84 85#include "closestream.h"
09070e1a 86#include "env.h"
6dbe3af9 87
951666e9
KZ
88#ifdef TEST_PROGRAM
89# define NON_INTERACTIVE_MORE 1
90#endif
91
5cd45502 92#define BACKSPACE "\b"
7cc31c18 93#define CARAT "^"
a8f98304 94
1f861935
SK
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
d910f311 100#define MIN_LINE_SZ 256 /* minimal line_buf buffer size */
4eaab145 101#define ESC '\033'
bf6db55a
SK
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
bf6db55a 107#define COMMAND_BUF 200
eecbb2cf 108#define REGERR_BUF NUM_COLUMNS
a8f98304
SK
109
110#define TERM_AUTO_RIGHT_MARGIN "am"
5cd45502 111#define TERM_BACKSPACE "cub1"
a8f98304
SK
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"
a8f98304 119#define TERM_EXIT_STANDARD_MODE "rmso"
a8f98304
SK
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"
a8f98304
SK
125#define TERM_STANDARD_MODE "smso"
126#define TERM_STD_MODE_GLITCH "xmc"
6dbe3af9 127
1f861935
SK
128/* Used in read_command() */
129typedef 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,
9f303a2b
HM
138 more_kc_skip_forward_screen,
139 more_kc_skip_forward_line,
1f861935
SK
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;
153struct number_command {
154 unsigned int number;
155 more_key_commands key;
156};
157
7cc31c18 158struct more_control {
d910f311
SK
159 struct termios output_tty; /* output terminal */
160 struct termios original_tty; /* original terminal settings */
82129a6f 161 FILE *current_file; /* currently open input file */
ba105bb5
SK
162 off_t file_position; /* file position */
163 off_t file_size; /* file size */
d910f311
SK
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 */
e3cb27f5 169 int next_jump; /* number of lines to skip ahead */
d910f311
SK
170 char **file_names; /* The list of file names */
171 int num_files; /* Number of files left to process */
7cc31c18 172 char *shell; /* name of the shell to use */
823ef319
SK
173 int sigfd; /* signalfd() file descriptor */
174 sigset_t sigset; /* signal operations */
d910f311
SK
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 */
d910f311
SK
182 char *backspace_ch; /* backspace character */
183 char *go_home; /* go to home */
5cd45502 184 char *move_line_down; /* move line down */
d910f311
SK
185 char *clear_rest; /* clear rest of screen */
186 int num_columns; /* number of columns */
e3cb27f5 187 char *next_search; /* file beginning search string */
d910f311 188 char *previous_search; /* previous search() buf[] item */
7cc31c18 189 struct {
ba105bb5 190 off_t row_num; /* row file position */
d910f311 191 long line_num; /* line number */
7cc31c18
SK
192 } context,
193 screen_start;
1f861935
SK
194 unsigned int leading_number; /* number in front of key command */
195 struct number_command previous_command; /* previous key command */
474c61c0 196 char *shell_line; /* line to execute in subshell */
09070e1a
SK
197#ifdef HAVE_MAGIC
198 magic_t magic; /* libmagic database entries */
199#endif
7cc31c18 200 unsigned int
d910f311
SK
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 */
63f96f68 204 clear_first:1, /* is first character in file \f */
d910f311
SK
205 dumb_tty:1, /* is terminal type known */
206 eat_newline:1, /* is newline ignored after 80 cols */
d910f311
SK
207 erase_input_ok:1, /* is erase input supported */
208 erase_previous_ok:1, /* is erase previous supported */
df6b29d3 209 exit_on_eof:1, /* exit on EOF */
d910f311
SK
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) */
1f861935 214 leading_colon:1, /* key command has leading ':' character */
df6b29d3 215 is_eof:1, /* EOF detected */
d910f311
SK
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 */
2ae8b757 221 no_tty_err:1, /* is stderr terminal */
63f96f68 222 print_banner:1, /* print file name banner */
1f861935 223 reading_num:1, /* are we reading leading_number */
d910f311 224 report_errors:1, /* is an error reported */
63f96f68 225 search_at_start:1, /* search pattern defined at start up */
823ef319 226 search_called:1, /* previous more command was a search */
d910f311 227 squeeze_spaces:1, /* suppress white space */
d910f311
SK
228 stdout_glitch:1, /* terminal has standout mode glitch */
229 stop_after_formfeed:1, /* stop after form feeds */
230 suppress_bell:1, /* suppress bell */
d910f311 231 wrap_margin:1; /* set if automargins */
7cc31c18
SK
232};
233
c1da6d1e 234static void __attribute__((__noreturn__)) usage(void)
bf6db55a 235{
e3cb27f5
SK
236 printf("%s", USAGE_HEADER);
237 printf(_(" %s [options] <file>...\n"), program_invocation_short_name);
238
239 printf("%s", USAGE_SEPARATOR);
ed07532a 240 printf("%s\n", _("Display the contents of a file in a terminal."));
e3cb27f5
SK
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"));
df6b29d3 248 printf("%s\n", _(" -e, --exit-on-eof exit on end-of-file"));
e3cb27f5
SK
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));
f45f3ec3 257 printf(USAGE_MAN_TAIL("more(1)"));
c1da6d1e 258 exit(EXIT_SUCCESS);
66ee8158
KZ
259}
260
e3cb27f5 261static void argscan(struct more_control *ctl, int as_argc, char **as_argv)
4eaab145 262{
e3cb27f5
SK
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' },
df6b29d3 270 { "exit-on-eof", no_argument, NULL, 'e' },
e3cb27f5
SK
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;
6dbe3af9 298 }
e3cb27f5
SK
299 }
300 if (move) {
5e2bc9d6 301 as_argc = remove_entry(as_argv, opt, as_argc);
e3cb27f5
SK
302 opt--;
303 }
304 }
305
306 while ((c = getopt_long(as_argc, as_argv, "dflcpsun:eVh", longopts, NULL)) != -1) {
307 switch (c) {
4eaab145 308 case 'd':
d910f311 309 ctl->suppress_bell = 1;
6dbe3af9 310 break;
4eaab145 311 case 'l':
d910f311 312 ctl->stop_after_formfeed = 0;
6dbe3af9 313 break;
4eaab145 314 case 'f':
d910f311 315 ctl->fold_long_lines = 0;
6dbe3af9 316 break;
4eaab145 317 case 'p':
d910f311 318 ctl->no_scroll = 1;
6dbe3af9 319 break;
4eaab145 320 case 'c':
d910f311 321 ctl->clear_line_ends = 1;
6dbe3af9 322 break;
4eaab145 323 case 's':
d910f311 324 ctl->squeeze_spaces = 1;
6dbe3af9 325 break;
4eaab145 326 case 'u':
6dbe3af9 327 break;
e3cb27f5
SK
328 case 'n':
329 ctl->lines_per_screen = strtou16_or_err(optarg, _("argument error"));
330 break;
df6b29d3
IJ
331 case 'e':
332 ctl->exit_on_eof = 1;
4eaab145
SK
333 break;
334 case 'V':
2c308875 335 print_version(EXIT_SUCCESS);
e3cb27f5
SK
336 case 'h':
337 usage();
4eaab145 338 default:
c1da6d1e 339 errtryhelp(EXIT_FAILURE);
66ee8158 340 break;
6dbe3af9 341 }
6dbe3af9 342 }
e3cb27f5
SK
343 ctl->num_files = as_argc - optind;
344 ctl->file_names = as_argv + optind;
345}
346
347static 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
343b2e4b 356 env_argv = xreallocarray(NULL, size, sizeof(char *));
e3cb27f5
SK
357 env_argv[0] = _("MORE environment variable"); /* program name */
358 for (tok = strtok_r(str, delim, &key); tok; tok = strtok_r(NULL, delim, &key)) {
ecdfc9aa 359 if (size == env_argc) {
e3cb27f5 360 size *= 2;
64d6d400 361 env_argv = xreallocarray(env_argv, size, sizeof(char *));
e3cb27f5 362 }
ecdfc9aa 363 env_argv[env_argc++] = tok;
e3cb27f5
SK
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);
6dbe3af9
KZ
371}
372
ba105bb5 373static void more_fseek(struct more_control *ctl, off_t pos)
bd6a253f 374{
d910f311 375 ctl->file_position = pos;
ba105bb5 376 fseeko(ctl->current_file, pos, SEEK_SET);
bd6a253f
SK
377}
378
82129a6f 379static int more_getc(struct more_control *ctl)
bd6a253f 380{
ba105bb5
SK
381 int ret = getc(ctl->current_file);
382 ctl->file_position = ftello(ctl->current_file);
383 return ret;
bd6a253f
SK
384}
385
82129a6f 386static int more_ungetc(struct more_control *ctl, int c)
bd6a253f 387{
ba105bb5
SK
388 int ret = ungetc(c, ctl->current_file);
389 ctl->file_position = ftello(ctl->current_file);
390 return ret;
bd6a253f
SK
391}
392
5cd45502
SK
393static void print_separator(const int c, int n)
394{
395 while (n--)
396 putchar(c);
397 putchar('\n');
398}
399
09070e1a
SK
400/* check_magic -- check for file magic numbers. */
401static int check_magic(struct more_control *ctl, char *fs)
82cfa6f2 402{
09070e1a 403#ifdef HAVE_MAGIC
2eb52772
SK
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);
2eb52772
SK
407
408 if (magic_error_msg) {
32a4efc5
SK
409 printf("%s: %s: %s\n", program_invocation_short_name,
410 _("magic failed"), magic_error_msg);
2eb52772
SK
411 return 0;
412 }
09070e1a
SK
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
82cfa6f2
SK
418 signed char twobytes[2];
419
420 /* don't try to look ahead if the input is unseekable */
09070e1a 421 if (fseek(ctl->current_file, 0L, SEEK_SET))
82cfa6f2
SK
422 return 0;
423
09070e1a 424 if (fread(twobytes, 2, 1, ctl->current_file) == 1) {
82cfa6f2
SK
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 }
09070e1a
SK
438 fseek(ctl->current_file, 0L, SEEK_SET); /* rewind() not necessary */
439#endif
82cfa6f2
SK
440 return 0;
441}
442
4eaab145
SK
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. */
63f96f68 445static void checkf(struct more_control *ctl, char *fs)
6dbe3af9 446{
d910f311 447 struct stat st;
e8f26419 448 int c;
6dbe3af9 449
f3c044ee
SK
450 ctl->current_line = 0;
451 ctl->file_position = 0;
8fd1e766 452 ctl->file_size = 0;
f3c044ee 453 fflush(NULL);
32a4efc5
SK
454
455 ctl->current_file = fopen(fs, "r");
456 if (ctl->current_file == NULL) {
d910f311
SK
457 if (ctl->clear_line_ends)
458 putp(ctl->erase_line);
09070e1a 459 warn(_("cannot open %s"), fs);
82129a6f 460 return;
6dbe3af9 461 }
32a4efc5 462 if (fstat(fileno(ctl->current_file), &st) != 0) {
1293b0f6 463 warn(_("stat of %s failed"), fs);
32a4efc5
SK
464 return;
465 }
d910f311 466 if ((st.st_mode & S_IFMT) == S_IFDIR) {
d162fcb5 467 printf(_("\n*** %s: directory ***\n\n"), fs);
82129a6f
SK
468 ctl->current_file = NULL;
469 return;
6dbe3af9 470 }
8fd1e766
TS
471 ctl->file_size = st.st_size;
472 if (0 < ctl->file_size && check_magic(ctl, fs)) {
82129a6f
SK
473 fclose(ctl->current_file);
474 ctl->current_file = NULL;
475 return;
b9579f1f 476 }
82129a6f
SK
477 fcntl(fileno(ctl->current_file), F_SETFD, FD_CLOEXEC);
478 c = more_getc(ctl);
63f96f68 479 ctl->clear_first = (c == '\f');
82129a6f 480 more_ungetc(ctl, c);
6dbe3af9
KZ
481}
482
7cc31c18 483static void prepare_line_buffer(struct more_control *ctl)
6dbe3af9 484{
d910f311 485 size_t sz = ctl->num_columns * 4;
6dbe3af9 486
d910f311 487 if (ctl->line_sz >= sz)
f16ca88d 488 return;
1ac30093 489
d910f311
SK
490 if (sz < MIN_LINE_SZ)
491 sz = MIN_LINE_SZ;
1ac30093 492
d910f311
SK
493 /* alloc sz and extra space for \n\0 */
494 ctl->line_buf = xrealloc(ctl->line_buf, sz + 2);
495 ctl->line_sz = sz;
1ac30093
KZ
496}
497
4eaab145 498/* Get a logical line */
82129a6f 499static int get_line(struct more_control *ctl, int *length)
6dbe3af9 500{
4eaab145
SK
501 int c;
502 char *p;
503 int column;
d910f311 504 static int column_wrap;
1d4ad1de 505
06b04b23 506#ifdef HAVE_WIDECHAR
4eaab145
SK
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(). */
ba105bb5 516 off_t file_position_bak = ctl->file_position;
4eaab145
SK
517
518 memset(&state, '\0', sizeof(mbstate_t));
519#endif
520
d910f311 521 p = ctl->line_buf;
4eaab145 522 column = 0;
82129a6f 523 c = more_getc(ctl);
d910f311
SK
524 if (column_wrap && c == '\n') {
525 ctl->current_line++;
82129a6f 526 c = more_getc(ctl);
4eaab145 527 }
d910f311 528 while (p < &ctl->line_buf[ctl->line_sz - 1]) {
06b04b23 529#ifdef HAVE_WIDECHAR
d910f311 530 if (ctl->fold_long_lines && use_mbc_buffer_flag && MB_CUR_MAX > 1) {
4eaab145
SK
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++;
d910f311
SK
547 file_position_bak++;
548 if (column >= ctl->num_columns) {
82129a6f 549 more_fseek(ctl, file_position_bak);
4eaab145
SK
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);
d910f311 561 if (column + wc_width > ctl->num_columns) {
82129a6f 562 more_fseek(ctl, file_position_bak);
4eaab145
SK
563 break_flag = 1;
564 } else {
d910f311 565 for (i = 0; p < &ctl->line_buf[ctl->line_sz - 1] &&
c3640729 566 i < mbc_pos; i++)
4eaab145
SK
567 *p++ = mbc[i];
568 if (wc_width > 0)
569 column += wc_width;
570 }
571 }
1d4ad1de 572
d910f311 573 if (break_flag || column >= ctl->num_columns)
4eaab145 574 break;
1d4ad1de 575
82129a6f 576 c = more_getc(ctl);
4eaab145
SK
577 continue;
578 }
579#endif /* HAVE_WIDECHAR */
580 if (c == EOF) {
d910f311 581 if (p > ctl->line_buf) {
4eaab145 582 *p = '\0';
d910f311 583 *length = p - ctl->line_buf;
f728d8ba 584 return column;
4eaab145 585 }
d910f311 586 *length = p - ctl->line_buf;
f728d8ba 587 return EOF;
4eaab145
SK
588 }
589 if (c == '\n') {
d910f311 590 ctl->current_line++;
4eaab145 591 break;
1d4ad1de 592 }
1d4ad1de 593
4eaab145 594 *p++ = c;
4eaab145 595 if (c == '\t') {
d910f311
SK
596 if (!ctl->hard_tabs || (column < ctl->prompt_len && !ctl->hard_tty)) {
597 if (ctl->hard_tabs && ctl->erase_line && !ctl->dumb_tty) {
4eaab145 598 column = 1 + (column | 7);
d910f311
SK
599 putp(ctl->erase_line);
600 ctl->prompt_len = 0;
4eaab145 601 } else {
d910f311 602 for (--p; p < &ctl->line_buf[ctl->line_sz - 1];) {
4eaab145
SK
603 *p++ = ' ';
604 if ((++column & 7) == 0)
605 break;
606 }
d910f311
SK
607 if (column >= ctl->prompt_len)
608 ctl->prompt_len = 0;
4eaab145
SK
609 }
610 } else
611 column = 1 + (column | 7);
612 } else if (c == '\b' && column > 0) {
613 column--;
614 } else if (c == '\r') {
82129a6f 615 int next = more_getc(ctl);
4eaab145
SK
616 if (next == '\n') {
617 p--;
d910f311 618 ctl->current_line++;
4eaab145
SK
619 break;
620 }
82129a6f 621 more_ungetc(ctl, c);
4eaab145 622 column = 0;
d910f311 623 } else if (c == '\f' && ctl->stop_after_formfeed) {
4eaab145
SK
624 p[-1] = '^';
625 *p++ = 'L';
626 column += 2;
d910f311 627 ctl->is_paused = 1;
4eaab145 628 } else if (c == EOF) {
d910f311 629 *length = p - ctl->line_buf;
f728d8ba 630 return column;
4eaab145
SK
631 } else {
632#ifdef HAVE_WIDECHAR
d910f311 633 if (ctl->fold_long_lines && MB_CUR_MAX > 1) {
4eaab145
SK
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--;
d910f311 644 file_position_bak = ctl->file_position - 1;
4eaab145
SK
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 }
66ee8158 665 }
4eaab145 666
d910f311 667 if (column >= ctl->num_columns && ctl->fold_long_lines)
4eaab145 668 break;
418cb4b3 669#ifdef HAVE_WIDECHAR
d910f311 670 if (use_mbc_buffer_flag == 0 && p >= &ctl->line_buf[ctl->line_sz - 1 - 4])
418cb4b3
KZ
671 /* don't read another char if there is no space for
672 * whole multibyte sequence */
673 break;
674#endif
82129a6f 675 c = more_getc(ctl);
66ee8158 676 }
d910f311
SK
677 if (column >= ctl->num_columns && ctl->num_columns > 0) {
678 if (!ctl->wrap_margin) {
4eaab145 679 *p++ = '\n';
6dbe3af9 680 }
1d4ad1de 681 }
d910f311
SK
682 column_wrap = column == ctl->num_columns && ctl->fold_long_lines;
683 if (column_wrap && ctl->eat_newline && ctl->wrap_margin) {
4eaab145 684 *p++ = '\n'; /* simulate normal wrap */
6dbe3af9 685 }
d910f311 686 *length = p - ctl->line_buf;
4eaab145 687 *p = 0;
f728d8ba 688 return column;
6dbe3af9
KZ
689}
690
4eaab145 691/* Erase the rest of the prompt, assuming we are starting at column col. */
21d09623 692static void erase_to_col(struct more_control *ctl, int col)
6dbe3af9
KZ
693{
694
d910f311 695 if (ctl->prompt_len == 0)
4eaab145 696 return;
21d09623
SK
697 if (col == 0 && ctl->clear_line_ends)
698 puts(ctl->erase_line);
699 else if (ctl->hard_tty)
4eaab145 700 putchar('\n');
21d09623 701 else {
4eaab145
SK
702 if (col == 0)
703 putchar('\r');
d910f311
SK
704 if (!ctl->dumb_tty && ctl->erase_line)
705 putp(ctl->erase_line);
21d09623 706 else {
d910f311 707 printf("%*s", ctl->prompt_len - col, "");
21d09623
SK
708 if (col == 0)
709 putchar('\r');
710 }
4eaab145 711 }
21d09623 712 ctl->prompt_len = col;
6dbe3af9
KZ
713}
714
275d47c9 715static void output_prompt(struct more_control *ctl, char *filename)
f16ca88d 716{
d910f311
SK
717 if (ctl->clear_line_ends)
718 putp(ctl->erase_line);
719 else if (ctl->prompt_len > 0)
21d09623 720 erase_to_col(ctl, 0);
d910f311
SK
721 if (!ctl->hard_tty) {
722 ctl->prompt_len = 0;
5cd45502 723 if (ctl->enter_std) {
d910f311
SK
724 putp(ctl->enter_std);
725 ctl->prompt_len += (2 * ctl->stdout_glitch);
f16ca88d 726 }
d910f311
SK
727 if (ctl->clear_line_ends)
728 putp(ctl->erase_line);
729 ctl->prompt_len += printf(_("--More--"));
f16ca88d 730 if (filename != NULL) {
d910f311 731 ctl->prompt_len += printf(_("(Next file: %s)"), filename);
ec4153cc 732 } else if (!ctl->no_tty_in && 0 < ctl->file_size) {
df6b29d3
IJ
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)"));
f16ca88d 743 }
df6b29d3 744
d910f311
SK
745 if (ctl->suppress_bell) {
746 ctl->prompt_len +=
f16ca88d
SK
747 printf(_("[Press space to continue, 'q' to quit.]"));
748 }
5cd45502 749 if (ctl->exit_std)
d910f311
SK
750 putp(ctl->exit_std);
751 if (ctl->clear_line_ends)
752 putp(ctl->clear_rest);
f16ca88d 753 } else
508b2d79 754 fprintf(stderr, "\a");
f3c044ee 755 fflush(NULL);
6dbe3af9
KZ
756}
757
823ef319 758static void reset_tty(struct more_control *ctl)
82cfa6f2 759{
823ef319 760 if (ctl->no_tty_out)
82cfa6f2 761 return;
f3c044ee 762 fflush(NULL);
823ef319
SK
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);
82cfa6f2
SK
767}
768
769/* Clean up terminal state and exit. Also come here if interrupt signal received */
823ef319 770static void __attribute__((__noreturn__)) more_exit(struct more_control *ctl)
82cfa6f2 771{
09070e1a
SK
772#ifdef HAVE_MAGIC
773 magic_close(ctl->magic);
774#endif
823ef319
SK
775 reset_tty(ctl);
776 if (ctl->clear_line_ends) {
82cfa6f2 777 putchar('\r');
823ef319 778 putp(ctl->erase_line);
f3c044ee 779 } else if (!ctl->clear_line_ends && (ctl->prompt_len > 0))
21d09623 780 erase_to_col(ctl, 0);
f3c044ee 781 fflush(NULL);
823ef319 782 free(ctl->previous_search);
474c61c0 783 free(ctl->shell_line);
823ef319 784 free(ctl->line_buf);
22ff9a38 785 free(ctl->go_home);
73ff5a95
SK
786 if (ctl->current_file)
787 fclose(ctl->current_file);
788 del_curterm(cur_term);
82cfa6f2
SK
789 _exit(EXIT_SUCCESS);
790}
791
ba105bb5 792static cc_t read_user_input(struct more_control *ctl)
6dbe3af9 793{
ba105bb5 794 cc_t c;
f16ca88d
SK
795
796 errno = 0;
1f861935
SK
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 */
d2b54002 803 if (read(STDERR_FILENO, &c, 1) <= 0) {
f16ca88d 804 if (errno != EINTR)
823ef319 805 more_exit(ctl);
f16ca88d 806 else
d910f311 807 c = ctl->output_tty.c_cc[VKILL];
f16ca88d 808 }
f728d8ba 809 return c;
6dbe3af9
KZ
810}
811
1f861935 812/* Read a number and command from the terminal. Set cmd to the non-digit
f16ca88d 813 * which terminates the number. */
1f861935 814static struct number_command read_command(struct more_control *ctl)
6dbe3af9 815{
1f861935
SK
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))) {
e899168a 828 cmd.key = more_kc_jump_lines_per_screen;
1f861935
SK
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))) {
e899168a 834 cmd.key = more_kc_jump_lines_per_screen;
1f861935
SK
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;
bf3e1c78 893 return cmd;
1f861935
SK
894 break;
895 case 'f':
1f861935 896 case CTRL('F'):
9f303a2b
HM
897 cmd.key = more_kc_skip_forward_screen;
898 break;
899 case 's':
900 cmd.key = more_kc_skip_forward_line;
1f861935
SK
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;
f16ca88d 929 break;
6dbe3af9 930 }
f16ca88d 931 }
1f861935 932 return cmd;
f16ca88d 933}
4eaab145 934
275d47c9
SK
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. */
938static void change_file(struct more_control *ctl, int nskip)
f16ca88d
SK
939{
940 if (nskip == 0)
941 return;
942 if (nskip > 0) {
d910f311
SK
943 if (ctl->argv_position + nskip > ctl->num_files - 1)
944 nskip = ctl->num_files - ctl->argv_position - 1;
823ef319 945 }
d910f311
SK
946 ctl->argv_position += nskip;
947 if (ctl->argv_position < 0)
948 ctl->argv_position = 0;
f16ca88d 949 puts(_("\n...Skipping "));
d910f311
SK
950 if (ctl->clear_line_ends)
951 putp(ctl->erase_line);
f16ca88d 952 if (nskip > 0)
bd6a253f 953 fputs(_("...Skipping to file "), stdout);
f16ca88d 954 else
bd6a253f 955 fputs(_("...Skipping back to file "), stdout);
d910f311
SK
956 puts(ctl->file_names[ctl->argv_position]);
957 if (ctl->clear_line_ends)
958 putp(ctl->erase_line);
f16ca88d 959 putchar('\n');
d910f311 960 ctl->argv_position--;
f16ca88d 961}
4eaab145 962
7cc31c18 963static void show(struct more_control *ctl, char c)
f16ca88d 964{
508b2d79
SK
965 if ((c < ' ' && c != '\n' && c != ESC) || c == CERASE) {
966 c += (c == CERASE) ? -0100 : 0100;
bd6a253f 967 fputs(CARAT, stderr);
d910f311 968 ctl->prompt_len++;
f16ca88d 969 }
bd6a253f 970 fputc(c, stderr);
d910f311 971 ctl->prompt_len++;
f16ca88d 972}
4eaab145 973
7cc31c18 974static void more_error(struct more_control *ctl, char *mess)
f16ca88d 975{
d910f311
SK
976 if (ctl->clear_line_ends)
977 putp(ctl->erase_line);
f16ca88d 978 else
21d09623 979 erase_to_col(ctl, 0);
d910f311 980 ctl->prompt_len += strlen(mess);
5cd45502 981 if (ctl->enter_std)
d910f311 982 putp(ctl->enter_std);
5cd45502
SK
983 fputs(mess, stdout);
984 if (ctl->exit_std)
d910f311 985 putp(ctl->exit_std);
f3c044ee 986 fflush(NULL);
d910f311 987 ctl->report_errors++;
f16ca88d 988}
6dbe3af9 989
bd6a253f
SK
990static void erase_one_column(struct more_control *ctl)
991{
d910f311 992 if (ctl->erase_previous_ok)
5cd45502
SK
993 fprintf(stderr, "%s ", ctl->backspace_ch);
994 fputs(ctl->backspace_ch, stderr);
bd6a253f
SK
995}
996
0b735ea4 997static void ttyin(struct more_control *ctl, char buf[], int nmax, char pchar)
f16ca88d
SK
998{
999 char *sp;
ba105bb5 1000 cc_t c;
f16ca88d
SK
1001 int slash = 0;
1002 int maxlen;
6dbe3af9 1003
f16ca88d
SK
1004 sp = buf;
1005 maxlen = 0;
1006 while (sp - buf < nmax) {
d910f311
SK
1007 if (ctl->prompt_len > maxlen)
1008 maxlen = ctl->prompt_len;
275d47c9 1009 c = read_user_input(ctl);
f16ca88d
SK
1010 if (c == '\\') {
1011 slash++;
ba105bb5 1012 } else if (c == ctl->output_tty.c_cc[VERASE] && !slash) {
f16ca88d
SK
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
5cd45502
SK
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 }
f16ca88d
SK
1036 if (buf + pos + mblength >= sp)
1037 break;
1038
1039 pos += mblength;
4eaab145 1040 }
f16ca88d
SK
1041
1042 if (mblength == 1) {
bd6a253f 1043 erase_one_column(ctl);
f16ca88d
SK
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--) {
bd6a253f 1051 erase_one_column(ctl);
f16ca88d
SK
1052 }
1053 }
1054
1055 while (mblength--) {
d910f311 1056 --ctl->prompt_len;
f16ca88d
SK
1057 --sp;
1058 }
1059 } else
1060#endif /* HAVE_WIDECHAR */
1061 {
d910f311 1062 --ctl->prompt_len;
bd6a253f 1063 erase_one_column(ctl);
f16ca88d
SK
1064 --sp;
1065 }
1066
508b2d79 1067 if ((*sp < ' ' && *sp != '\n') || *sp == CERASE) {
d910f311 1068 --ctl->prompt_len;
bd6a253f 1069 erase_one_column(ctl);
f16ca88d
SK
1070 }
1071 continue;
4eaab145 1072 }
042f62df
RP
1073 if (!ctl->erase_line)
1074 ctl->prompt_len = maxlen;
ba105bb5 1075 } else if (c == ctl->output_tty.c_cc[VKILL] && !slash) {
d910f311 1076 if (ctl->hard_tty) {
7cc31c18 1077 show(ctl, c);
f16ca88d
SK
1078 putchar('\n');
1079 putchar(pchar);
4eaab145 1080 } else {
f16ca88d
SK
1081 putchar('\r');
1082 putchar(pchar);
d910f311 1083 if (ctl->erase_line)
21d09623 1084 erase_to_col(ctl, 1);
d910f311
SK
1085 else if (ctl->erase_input_ok)
1086 while (ctl->prompt_len-- > 1)
5cd45502 1087 fprintf(stderr, "%s %s", ctl->backspace_ch, ctl->backspace_ch);
d910f311 1088 ctl->prompt_len = 1;
4eaab145 1089 }
f16ca88d 1090 sp = buf;
f3c044ee 1091 fflush(NULL);
f16ca88d
SK
1092 continue;
1093 }
ba105bb5
SK
1094 if (slash && (c == ctl->output_tty.c_cc[VKILL] ||
1095 c == ctl->output_tty.c_cc[VERASE])) {
bd6a253f 1096 erase_one_column(ctl);
f16ca88d
SK
1097 --sp;
1098 }
1099 if (c != '\\')
1100 slash = 0;
1101 *sp++ = c;
508b2d79
SK
1102 if ((c < ' ' && c != '\n' && c != ESC) || c == CERASE) {
1103 c += (c == CERASE) ? -0100 : 0100;
bd6a253f 1104 fputs(CARAT, stderr);
d910f311 1105 ctl->prompt_len++;
f16ca88d
SK
1106 }
1107 if (c != '\n' && c != ESC) {
bd6a253f 1108 fputc(c, stderr);
d910f311 1109 ctl->prompt_len++;
f16ca88d 1110 } else
4eaab145 1111 break;
f16ca88d
SK
1112 }
1113 *--sp = '\0';
d910f311
SK
1114 if (!ctl->erase_line)
1115 ctl->prompt_len = maxlen;
f16ca88d 1116 if (sp - buf >= nmax - 1)
7cc31c18 1117 more_error(ctl, _("Line too long"));
f16ca88d 1118}
4eaab145 1119
474c61c0
SK
1120/* Expand shell command line. */
1121static void expand(struct more_control *ctl, char *inbuf)
f16ca88d
SK
1122{
1123 char *inpstr;
1124 char *outstr;
1125 char c;
1126 char *temp;
86f05d39
KZ
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;
4eaab145 1133
474c61c0 1134 tempsz = COMMAND_BUF + xtra;
f16ca88d
SK
1135 temp = xmalloc(tempsz);
1136 inpstr = inbuf;
1137 outstr = temp;
86f05d39 1138
f16ca88d
SK
1139 while ((c = *inpstr++) != '\0') {
1140 offset = outstr - temp;
1141 if (tempsz - offset - 1 < xtra) {
474c61c0 1142 tempsz += COMMAND_BUF + xtra;
f16ca88d
SK
1143 temp = xrealloc(temp, tempsz);
1144 outstr = temp + offset;
1145 }
1146 switch (c) {
1147 case '%':
d910f311
SK
1148 if (!ctl->no_tty_in) {
1149 strcpy(outstr, ctl->file_names[ctl->argv_position]);
1150 outstr += strlen(ctl->file_names[ctl->argv_position]);
f16ca88d
SK
1151 } else
1152 *outstr++ = c;
1153 break;
1154 case '!':
474c61c0
SK
1155 if (ctl->shell_line) {
1156 strcpy(outstr, ctl->shell_line);
1157 outstr += strlen(ctl->shell_line);
1158 } else
7cc31c18 1159 more_error(ctl, _
f16ca88d 1160 ("No previous command to substitute for"));
f16ca88d
SK
1161 break;
1162 case '\\':
1163 if (*inpstr == '%' || *inpstr == '!') {
1164 *outstr++ = *inpstr++;
4eaab145
SK
1165 break;
1166 }
b1557fe9 1167 /* fallthrough */
4eaab145 1168 default:
f16ca88d 1169 *outstr++ = c;
6dbe3af9 1170 }
6dbe3af9 1171 }
f16ca88d 1172 *outstr++ = '\0';
474c61c0
SK
1173 free(ctl->shell_line);
1174 ctl->shell_line = temp;
6dbe3af9
KZ
1175}
1176
7cc31c18 1177static void set_tty(struct more_control *ctl)
4eaab145 1178{
d910f311
SK
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);
f16ca88d
SK
1183}
1184
82cfa6f2 1185/* Come here if a quit signal is received */
823ef319 1186static void sigquit_handler(struct more_control *ctl)
f16ca88d 1187{
823ef319
SK
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);
6dbe3af9
KZ
1193}
1194
82cfa6f2 1195/* Come here when we get a suspend signal from the terminal */
823ef319 1196static void sigtstp_handler(struct more_control *ctl)
6dbe3af9 1197{
823ef319 1198 reset_tty(ctl);
f3c044ee 1199 fflush(NULL);
6dcaaa9e
SK
1200 kill(getpid(), SIGSTOP);
1201}
1202
1203/* Come here when we get a continue signal from the terminal */
1204static void sigcont_handler(struct more_control *ctl)
1205{
823ef319
SK
1206 set_tty(ctl);
1207}
82cfa6f2 1208
823ef319
SK
1209/* Come here if a signal for a window size change is received */
1210static void sigwinch_handler(struct more_control *ctl)
1211{
1212 struct winsize win;
82cfa6f2 1213
823ef319
SK
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 }
5cd45502 1225 prepare_line_buffer(ctl);
f16ca88d 1226}
6dbe3af9 1227
2f38ae59
KZ
1228static void __attribute__((__format__ (__printf__, 3, 4)))
1229 execute(struct more_control *ctl, char *filename, const char *cmd, ...)
f16ca88d 1230{
c366ebc7 1231 pid_t id;
f16ca88d
SK
1232 va_list argp;
1233 char *arg;
1234 char **args;
1235 int argcount;
1236
f3c044ee 1237 fflush(NULL);
c366ebc7 1238 id = fork();
f16ca88d
SK
1239 if (id == 0) {
1240 int errsv;
c8b1fa60
SK
1241 if (!isatty(STDIN_FILENO)) {
1242 close(STDIN_FILENO);
c67497ce 1243 ignore_result( open("/dev/tty", 0) );
f16ca88d 1244 }
437cec38 1245 reset_tty(ctl);
f16ca88d
SK
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
c988ccdc 1256 args = xcalloc(argcount + 1, sizeof(char *));
f16ca88d
SK
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 *);
6dbe3af9 1265 }
f16ca88d
SK
1266 va_end(argp);
1267
17fc8693
KZ
1268 if ((geteuid() != getuid() || getegid() != getgid())
1269 && drop_permissions() != 0)
1270 err(EXIT_FAILURE, _("drop permissions failed"));
8fb5fe30 1271
f16ca88d
SK
1272 execvp(cmd, args);
1273 errsv = errno;
bd6a253f 1274 fputs(_("exec failed\n"), stderr);
f16ca88d 1275 exit(errsv == ENOENT ? EX_EXEC_ENOENT : EX_EXEC_FAILED);
6dbe3af9 1276 }
f16ca88d 1277 if (id > 0) {
c366ebc7
SK
1278 errno = 0;
1279 while (wait(NULL) > 0) {
1280 if (errno == EINTR)
1281 continue;
1282 }
f16ca88d 1283 } else
bd6a253f 1284 fputs(_("can't fork\n"), stderr);
7cc31c18 1285 set_tty(ctl);
5cd45502 1286 print_separator('-', 24);
275d47c9 1287 output_prompt(ctl, filename);
6dbe3af9
KZ
1288}
1289
275d47c9 1290static void run_shell(struct more_control *ctl, char *filename)
6dbe3af9 1291{
bf6db55a 1292 char cmdbuf[COMMAND_BUF];
6dbe3af9 1293
21d09623 1294 erase_to_col(ctl, 0);
d162fcb5 1295 putchar('!');
f3c044ee 1296 fflush(NULL);
1f861935 1297 if (ctl->previous_command.key == more_kc_run_shell && ctl->shell_line)
e5e0eac4 1298 fputs(ctl->shell_line, stderr);
6dbe3af9 1299 else {
7cc31c18 1300 ttyin(ctl, cmdbuf, sizeof(cmdbuf) - 2, '!');
474c61c0
SK
1301 if (strpbrk(cmdbuf, "%!\\"))
1302 expand(ctl, cmdbuf);
1303 else {
1304 free(ctl->shell_line);
1305 ctl->shell_line = xstrdup(cmdbuf);
6dbe3af9
KZ
1306 }
1307 }
bd6a253f 1308 fputc('\n', stderr);
f3c044ee 1309 fflush(NULL);
d910f311 1310 ctl->prompt_len = 0;
7cc31c18 1311 execute(ctl, filename, ctl->shell, ctl->shell, "-c", ctl->shell_line, 0);
6dbe3af9
KZ
1312}
1313
f16ca88d 1314/* Skip n lines in the file f */
e3cb27f5 1315static void skip_lines(struct more_control *ctl)
f16ca88d 1316{
0b735ea4 1317 int c;
f16ca88d 1318
e3cb27f5 1319 while (ctl->next_jump > 0) {
82129a6f 1320 while ((c = more_getc(ctl)) != '\n')
f16ca88d
SK
1321 if (c == EOF)
1322 return;
e3cb27f5 1323 ctl->next_jump--;
d910f311 1324 ctl->current_line++;
f16ca88d
SK
1325 }
1326}
1327
1328/* Clear the screen */
275d47c9 1329static void more_clear_screen(struct more_control *ctl)
f16ca88d 1330{
d910f311
SK
1331 if (ctl->clear && !ctl->hard_tty) {
1332 putp(ctl->clear);
f16ca88d
SK
1333 /* Put out carriage return so that system doesn't get
1334 * confused by escape sequences when expanding tabs */
1335 putchar('\r');
d910f311 1336 ctl->prompt_len = 0;
f16ca88d
SK
1337 }
1338}
1339
82129a6f 1340static void read_line(struct more_control *ctl)
f16ca88d 1341{
0b735ea4
SK
1342 int c;
1343 char *p;
f16ca88d 1344
d910f311 1345 p = ctl->line_buf;
82129a6f 1346 while ((c = more_getc(ctl)) != '\n' && c != EOF
ba105bb5 1347 && (ptrdiff_t)p != (ptrdiff_t)(ctl->line_buf + ctl->line_sz - 1))
f16ca88d
SK
1348 *p++ = c;
1349 if (c == '\n')
d910f311 1350 ctl->current_line++;
f16ca88d
SK
1351 *p = '\0';
1352}
1353
823ef319
SK
1354static 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;
6dcaaa9e
SK
1385 case SIGCONT:
1386 sigcont_handler(ctl);
1387 break;
823ef319
SK
1388 case SIGWINCH:
1389 sigwinch_handler(ctl);
1390 break;
1391 default:
1392 abort();
1393 }
1394 }
68e14d3d
GR
1395
1396 /* Check for POLLERR and POLLHUP in stdin revents */
1397 if ((pfd[1].revents & POLLERR) && (pfd[1].revents & POLLHUP))
1398 more_exit(ctl);
1399
823ef319
SK
1400 if (pfd[1].revents == 0)
1401 return 1;
1402 return 0;
1403}
1404
4eaab145
SK
1405/* Search for nth occurrence of regular expression contained in buf in
1406 * the file */
82129a6f 1407static void search(struct more_control *ctl, char buf[], int n)
6dbe3af9 1408{
ba105bb5
SK
1409 off_t startline = ctl->file_position;
1410 off_t line1 = startline;
1411 off_t line2 = startline;
1412 off_t line3;
0b735ea4 1413 int lncount;
cdd2cf46 1414 int saveln, rc;
4eaab145
SK
1415 regex_t re;
1416
e3cb27f5
SK
1417 if (buf != ctl->previous_search) {
1418 free(ctl->previous_search);
1419 ctl->previous_search = buf;
1420 }
1421
1422 ctl->search_called = 1;
d910f311
SK
1423 ctl->context.line_num = saveln = ctl->current_line;
1424 ctl->context.row_num = startline;
4eaab145 1425 lncount = 0;
bc1ed338
SK
1426 if (!buf)
1427 goto notfound;
4eaab145
SK
1428 if ((rc = regcomp(&re, buf, REG_NOSUB)) != 0) {
1429 char s[REGERR_BUF];
1430 regerror(rc, &re, s, sizeof s);
7cc31c18 1431 more_error(ctl, s);
823ef319 1432 return;
4eaab145 1433 }
82129a6f 1434 while (!feof(ctl->current_file)) {
4eaab145
SK
1435 line3 = line2;
1436 line2 = line1;
d910f311 1437 line1 = ctl->file_position;
82129a6f 1438 read_line(ctl);
4eaab145 1439 lncount++;
5cd45502
SK
1440 if (regexec(&re, ctl->line_buf, 0, NULL, 0) == 0 && --n == 0) {
1441 if ((1 < lncount && ctl->no_tty_in) || 3 < lncount) {
1442 putchar('\n');
1443 if (ctl->clear_line_ends)
1444 putp(ctl->erase_line);
1445 fputs(_("...skipping\n"), stdout);
1446 }
1447 if (!ctl->no_tty_in) {
1448 ctl->current_line -= (lncount < 3 ? lncount : 3);
1449 more_fseek(ctl, line3);
1450 if (ctl->no_scroll) {
1451 if (ctl->clear_line_ends) {
1452 putp(ctl->go_home);
d910f311 1453 putp(ctl->erase_line);
5cd45502
SK
1454 } else
1455 more_clear_screen(ctl);
4eaab145 1456 }
5cd45502 1457 } else {
21d09623 1458 erase_to_col(ctl, 0);
5cd45502
SK
1459 if (ctl->no_scroll) {
1460 if (ctl->clear_line_ends) {
1461 putp(ctl->go_home);
1462 putp(ctl->erase_line);
1463 } else
1464 more_clear_screen(ctl);
4eaab145 1465 }
5cd45502 1466 puts(ctl->line_buf);
5c36a0eb 1467 }
5cd45502 1468 break;
042f62df
RP
1469 }
1470 more_poll(ctl, 1);
7f84e444 1471 }
823ef319
SK
1472 /* Move ctrl+c signal handling back to more_key_command(). */
1473 signal(SIGINT, SIG_DFL);
1474 sigaddset(&ctl->sigset, SIGINT);
1475 sigprocmask(SIG_BLOCK, &ctl->sigset, NULL);
53078e08 1476 regfree(&re);
82129a6f 1477 if (feof(ctl->current_file)) {
d910f311
SK
1478 if (!ctl->no_tty_in) {
1479 ctl->current_line = saveln;
82129a6f 1480 more_fseek(ctl, startline);
4eaab145 1481 } else {
bd6a253f 1482 fputs(_("\nPattern not found\n"), stdout);
823ef319 1483 more_exit(ctl);
4eaab145 1484 }
bc1ed338 1485notfound:
7cc31c18 1486 more_error(ctl, _("Pattern not found"));
6dbe3af9 1487 }
6dbe3af9
KZ
1488}
1489
f8492042
SK
1490static char *find_editor(void)
1491{
1492 static char *editor;
1493
1494 editor = getenv("VISUAL");
1495 if (editor == NULL || *editor == '\0')
1496 editor = getenv("EDITOR");
1497 if (editor == NULL || *editor == '\0')
1498 editor = _PATH_VI;
1499 return editor;
1500}
1501
b6818219
SK
1502static void runtime_usage(void)
1503{
1504 fputs(_("Most commands optionally preceded by integer argument k. "
1505 "Defaults in brackets.\n"
1506 "Star (*) indicates argument becomes new default.\n"), stdout);
5cd45502 1507 print_separator('-', 79);
b6818219
SK
1508 fprintf(stdout,
1509 _
1510 ("<space> Display next k lines of text [current screen size]\n"
1511 "z Display next k lines of text [current screen size]*\n"
1512 "<return> Display next k lines of text [1]*\n"
1513 "d or ctrl-D Scroll k lines [current scroll size, initially 11]*\n"
1514 "q or Q or <interrupt> Exit from more\n"
1515 "s Skip forward k lines of text [1]\n"
1516 "f Skip forward k screenfuls of text [1]\n"
1517 "b or ctrl-B Skip backwards k screenfuls of text [1]\n"
1518 "' Go to place where previous search started\n"
1519 "= Display current line number\n"
1520 "/<regular expression> Search for kth occurrence of regular expression [1]\n"
1521 "n Search for kth occurrence of last r.e [1]\n"
1522 "!<cmd> or :!<cmd> Execute <cmd> in a subshell\n"
1523 "v Start up '%s' at current line\n"
1524 "ctrl-L Redraw screen\n"
1525 ":n Go to kth next file [1]\n"
1526 ":p Go to kth previous file [1]\n"
1527 ":f Display current file name and line number\n"
1528 ". Repeat previous command\n"),
1529 find_editor());
5cd45502 1530 print_separator('-', 79);
b6818219
SK
1531}
1532
042fbf37 1533static void execute_editor(struct more_control *ctl, char *cmdbuf, size_t buflen, char *filename)
1f5d21ec
SK
1534{
1535 char *editor, *p;
1f5d21ec 1536 int split = 0;
5cd45502 1537 int n;
1f5d21ec 1538
5cd45502
SK
1539 if ((ctl->current_line - ctl->lines_per_screen) < 1)
1540 n = 1;
1541 else
1542 n = ctl->current_line - (ctl->lines_per_screen + 1) / 2;
1f5d21ec
SK
1543 editor = find_editor();
1544 p = strrchr(editor, '/');
1545 if (p)
1546 p++;
1547 else
1548 p = editor;
1549 /*
1550 * Earlier: call vi +n file. This also works for emacs.
1551 * POSIX: call vi -c n file (when editor is vi or ex).
1552 */
1553 if (!strcmp(p, "vi") || !strcmp(p, "ex")) {
042fbf37 1554 snprintf(cmdbuf, buflen, "-c %d", n);
1f5d21ec
SK
1555 split = 1;
1556 } else
042fbf37 1557 snprintf(cmdbuf, buflen, "+%d", n);
1f5d21ec 1558
21d09623 1559 erase_to_col(ctl, 0);
1f5d21ec
SK
1560 printf("%s %s %s", editor, cmdbuf, ctl->file_names[ctl->argv_position]);
1561 if (split) {
1562 cmdbuf[2] = 0;
1563 execute(ctl, filename, editor, editor,
1564 cmdbuf, cmdbuf + 3,
1565 ctl->file_names[ctl->argv_position], (char *)0);
1566 } else
1567 execute(ctl, filename, editor, editor,
1568 cmdbuf, ctl->file_names[ctl->argv_position], (char *)0);
1569}
1570
82129a6f 1571static int skip_backwards(struct more_control *ctl, int nlines)
1f5d21ec 1572{
1f5d21ec
SK
1573 if (nlines == 0)
1574 nlines++;
21d09623 1575 erase_to_col(ctl, 0);
1f5d21ec 1576 printf(P_("...back %d page", "...back %d pages", nlines), nlines);
1f5d21ec 1577 putchar('\n');
837f49c6 1578 ctl->next_jump = ctl->current_line - (ctl->lines_per_screen * (nlines + 1)) - 1;
e3cb27f5
SK
1579 if (ctl->next_jump < 0)
1580 ctl->next_jump = 0;
ba105bb5 1581 more_fseek(ctl, 0);
837f49c6 1582 ctl->current_line = 0;
e3cb27f5 1583 skip_lines(ctl);
837f49c6 1584 return ctl->lines_per_screen;
1f5d21ec
SK
1585}
1586
ba105bb5 1587static int skip_forwards(struct more_control *ctl, int nlines, cc_t comchar)
1f5d21ec
SK
1588{
1589 int c;
1590
1591 if (nlines == 0)
1592 nlines++;
1593 if (comchar == 'f')
1594 nlines *= ctl->lines_per_screen;
1595 putchar('\r');
21d09623 1596 erase_to_col(ctl, 0);
1f5d21ec
SK
1597 putchar('\n');
1598 if (ctl->clear_line_ends)
1599 putp(ctl->erase_line);
1600 printf(P_("...skipping %d line",
1601 "...skipping %d lines", nlines), nlines);
1602
1603 if (ctl->clear_line_ends)
1604 putp(ctl->erase_line);
1605 putchar('\n');
1606
1607 while (nlines > 0) {
82129a6f 1608 while ((c = more_getc(ctl)) != '\n')
1f5d21ec
SK
1609 if (c == EOF)
1610 return 0;
1611 ctl->current_line++;
1612 nlines--;
1613 }
1614 return 1;
1615}
1616
f16ca88d
SK
1617/* Read a command and do it. A command consists of an optional integer
1618 * argument followed by the command character. Return the number of
1619 * lines to display in the next screenful. If there is nothing more to
1620 * display in the current file, zero is returned. */
82129a6f 1621static int more_key_command(struct more_control *ctl, char *filename)
6dbe3af9 1622{
0b735ea4 1623 int retval = 0;
1f861935 1624 int done = 0, search_again = 0;
ba105bb5 1625 char cmdbuf[INIT_BUF];
1f861935
SK
1626 struct number_command cmd;
1627
d910f311 1628 if (!ctl->report_errors)
275d47c9 1629 output_prompt(ctl, filename);
f16ca88d 1630 else
d910f311 1631 ctl->report_errors = 0;
823ef319 1632 ctl->search_called = 0;
f16ca88d 1633 for (;;) {
823ef319
SK
1634 if (more_poll(ctl, -1) != 0)
1635 continue;
1f861935
SK
1636 cmd = read_command(ctl);
1637 if (cmd.key == more_kc_unknown_command)
f16ca88d 1638 continue;
1f861935
SK
1639 if (cmd.key == more_kc_repeat_previous)
1640 cmd = ctl->previous_command;
1641 switch (cmd.key) {
1642 case more_kc_backwards:
1f5d21ec 1643 if (ctl->no_tty_in) {
508b2d79 1644 fprintf(stderr, "\a");
1f5d21ec 1645 return -1;
f16ca88d 1646 }
1f861935 1647 retval = skip_backwards(ctl, cmd.number);
1f5d21ec
SK
1648 done = 1;
1649 break;
1f861935
SK
1650 case more_kc_jump_lines_per_screen:
1651 case more_kc_set_lines_per_screen:
1652 if (cmd.number == 0)
1653 cmd.number = ctl->lines_per_screen;
1654 else if (cmd.key == more_kc_set_lines_per_screen)
1655 ctl->lines_per_screen = cmd.number;
1656 retval = cmd.number;
bd6a253f
SK
1657 done = 1;
1658 break;
1f861935
SK
1659 case more_kc_set_scroll_len:
1660 if (cmd.number != 0)
1661 ctl->d_scroll_len = cmd.number;
d910f311 1662 retval = ctl->d_scroll_len;
bd6a253f
SK
1663 done = 1;
1664 break;
1f861935 1665 case more_kc_quit:
823ef319 1666 more_exit(ctl);
9f303a2b
HM
1667 case more_kc_skip_forward_screen:
1668 if (skip_forwards(ctl, cmd.number, 'f'))
1669 retval = ctl->lines_per_screen;
1670 done = 1;
1671 break;
1672 case more_kc_skip_forward_line:
1673 if (skip_forwards(ctl, cmd.number, 's'))
1f5d21ec 1674 retval = ctl->lines_per_screen;
bd6a253f
SK
1675 done = 1;
1676 break;
1f861935
SK
1677 case more_kc_next_line:
1678 if (cmd.number != 0)
1679 ctl->lines_per_screen = cmd.number;
f16ca88d 1680 else
1f861935
SK
1681 cmd.number = 1;
1682 retval = cmd.number;
bd6a253f
SK
1683 done = 1;
1684 break;
1f861935 1685 case more_kc_clear_screen:
d910f311 1686 if (!ctl->no_tty_in) {
275d47c9 1687 more_clear_screen(ctl);
82129a6f 1688 more_fseek(ctl, ctl->screen_start.row_num);
d910f311
SK
1689 ctl->current_line = ctl->screen_start.line_num;
1690 retval = ctl->lines_per_screen;
bd6a253f
SK
1691 done = 1;
1692 break;
4eaab145 1693 } else {
508b2d79 1694 fprintf(stderr, "\a");
f16ca88d 1695 break;
4eaab145 1696 }
1f861935 1697 case more_kc_previous_search_match:
d910f311 1698 if (!ctl->no_tty_in) {
21d09623 1699 erase_to_col(ctl, 0);
bd6a253f 1700 fputs(_("\n***Back***\n\n"), stdout);
82129a6f 1701 more_fseek(ctl, ctl->context.row_num);
d910f311
SK
1702 ctl->current_line = ctl->context.line_num;
1703 retval = ctl->lines_per_screen;
bd6a253f
SK
1704 done = 1;
1705 break;
f16ca88d 1706 } else {
508b2d79 1707 fprintf(stderr, "\a");
f16ca88d 1708 break;
4eaab145 1709 }
1f861935 1710 case more_kc_display_line:
21d09623 1711 erase_to_col(ctl, 0);
d910f311 1712 ctl->prompt_len = printf("%d", ctl->current_line);
f3c044ee 1713 fflush(NULL);
f16ca88d 1714 break;
1f861935
SK
1715 case more_kc_display_file_and_line:
1716 erase_to_col(ctl, 0);
1717 if (!ctl->no_tty_in)
1718 ctl->prompt_len =
1719 printf(_("\"%s\" line %d"),
1720 ctl->file_names[ctl->argv_position], ctl->current_line);
1721 else
1722 ctl->prompt_len = printf(_("[Not a file] line %d"),
1723 ctl->current_line);
1724 fflush(NULL);
1725 break;
1726 case more_kc_repeat_search:
d910f311 1727 if (!ctl->previous_search) {
7cc31c18 1728 more_error(ctl, _("No previous regular expression"));
f16ca88d
SK
1729 break;
1730 }
1f861935 1731 search_again = 1;
f16ca88d 1732 /* fallthrough */
1f861935
SK
1733 case more_kc_search:
1734 if (cmd.number == 0)
1735 cmd.number++;
21d09623 1736 erase_to_col(ctl, 0);
f16ca88d 1737 putchar('/');
d910f311 1738 ctl->prompt_len = 1;
f3c044ee 1739 fflush(NULL);
1f861935 1740 if (search_again) {
bd6a253f 1741 fputc('\r', stderr);
1f861935
SK
1742 search(ctl, ctl->previous_search, cmd.number);
1743 search_again = 0;
4eaab145 1744 } else {
7cc31c18 1745 ttyin(ctl, cmdbuf, sizeof(cmdbuf) - 2, '/');
bd6a253f 1746 fputc('\r', stderr);
e3cb27f5 1747 ctl->next_search = xstrdup(cmdbuf);
1f861935 1748 search(ctl, ctl->next_search, cmd.number);
4eaab145 1749 }
d910f311 1750 retval = ctl->lines_per_screen - 1;
bd6a253f
SK
1751 done = 1;
1752 break;
1f861935 1753 case more_kc_run_shell:
275d47c9 1754 run_shell(ctl, filename);
f16ca88d 1755 break;
1f861935 1756 case more_kc_help:
d910f311 1757 if (ctl->no_scroll)
275d47c9 1758 more_clear_screen(ctl);
21d09623 1759 erase_to_col(ctl, 0);
b6818219 1760 runtime_usage();
275d47c9 1761 output_prompt(ctl, filename);
f16ca88d 1762 break;
1f861935
SK
1763 case more_kc_next_file:
1764 putchar('\r');
1765 erase_to_col(ctl, 0);
1766 if (cmd.number == 0)
1767 cmd.number = 1;
1768 if (ctl->argv_position + cmd.number >= (unsigned int)ctl->num_files)
1769 more_exit(ctl);
1770 change_file(ctl, cmd.number);
1771 done = 1;
1772 break;
1773 case more_kc_previous_file:
1774 if (ctl->no_tty_in) {
1775 fprintf(stderr, "\a");
1776 break;
1777 }
1778 putchar('\r');
1779 erase_to_col(ctl, 0);
1780 if (cmd.number == 0)
1781 cmd.number = 1;
1782 change_file(ctl, -cmd.number);
1783 done = 1;
1784 break;
1785 case more_kc_run_editor: /* This case should go right before default */
d910f311 1786 if (!ctl->no_tty_in) {
042fbf37 1787 execute_editor(ctl, cmdbuf, sizeof(cmdbuf), filename);
f16ca88d
SK
1788 break;
1789 }
1790 /* fallthrough */
1791 default:
d910f311 1792 if (ctl->suppress_bell) {
21d09623 1793 erase_to_col(ctl, 0);
5cd45502 1794 if (ctl->enter_std)
d910f311 1795 putp(ctl->enter_std);
5cd45502
SK
1796 ctl->prompt_len =
1797 printf(_("[Press 'h' for instructions.]"))
d910f311 1798 + 2 * ctl->stdout_glitch;
5cd45502 1799 if (ctl->exit_std)
d910f311 1800 putp(ctl->exit_std);
f16ca88d 1801 } else
508b2d79 1802 fprintf(stderr, "\a");
f3c044ee 1803 fflush(NULL);
f16ca88d 1804 break;
4eaab145 1805 }
1f861935
SK
1806 ctl->previous_command = cmd;
1807 if (done) {
1808 cmd.key = more_kc_unknown_command;
f16ca88d 1809 break;
1f861935 1810 }
5c36a0eb 1811 }
f16ca88d 1812 putchar('\r');
d910f311 1813 ctl->no_quit_dialog = 1;
f728d8ba 1814 return retval;
6dbe3af9
KZ
1815}
1816
f16ca88d 1817/* Print out the contents of the file f, one screenful at a time. */
82129a6f 1818static void screen(struct more_control *ctl, int num_lines)
4eaab145 1819{
0b735ea4
SK
1820 int c;
1821 int nchars;
f16ca88d
SK
1822 int length; /* length of current line */
1823 static int prev_len = 1; /* length of previous line */
4eaab145 1824
f16ca88d 1825 for (;;) {
d910f311 1826 while (num_lines > 0 && !ctl->is_paused) {
df6b29d3
IJ
1827 nchars = get_line(ctl, &length);
1828 ctl->is_eof = nchars == EOF;
1829 if (ctl->is_eof && ctl->exit_on_eof) {
d910f311
SK
1830 if (ctl->clear_line_ends)
1831 putp(ctl->clear_rest);
f16ca88d 1832 return;
4eaab145 1833 }
5c8f5f17 1834 if (ctl->squeeze_spaces && length == 0 && prev_len == 0 && !ctl->is_eof)
f16ca88d
SK
1835 continue;
1836 prev_len = length;
d910f311
SK
1837 if (ctl->bad_stdout
1838 || ((ctl->enter_std && *ctl->enter_std == ' ') && (ctl->prompt_len > 0)))
21d09623 1839 erase_to_col(ctl, 0);
f16ca88d
SK
1840 /* must clear before drawing line since tabs on
1841 * some terminals do not erase what they tab
1842 * over. */
d910f311
SK
1843 if (ctl->clear_line_ends)
1844 putp(ctl->erase_line);
94bece3c 1845 fwrite(ctl->line_buf, length, 1, stdout);
d910f311 1846 if (nchars < ctl->prompt_len)
21d09623
SK
1847 erase_to_col(ctl, nchars);
1848 ctl->prompt_len = 0;
d910f311 1849 if (nchars < ctl->num_columns || !ctl->fold_long_lines)
94bece3c 1850 putchar('\n');
f16ca88d 1851 num_lines--;
6dbe3af9 1852 }
f3c044ee 1853 fflush(NULL);
df6b29d3
IJ
1854
1855 c = more_getc(ctl);
1856 ctl->is_eof = c == EOF;
1857
1858 if (ctl->is_eof && ctl->exit_on_eof) {
d910f311
SK
1859 if (ctl->clear_line_ends)
1860 putp(ctl->clear_rest);
f16ca88d 1861 return;
4eaab145 1862 }
f16ca88d 1863
d910f311
SK
1864 if (ctl->is_paused && ctl->clear_line_ends)
1865 putp(ctl->clear_rest);
82129a6f 1866 more_ungetc(ctl, c);
d910f311 1867 ctl->is_paused = 0;
823ef319 1868 do {
82129a6f 1869 if ((num_lines = more_key_command(ctl, NULL)) == 0)
823ef319
SK
1870 return;
1871 } while (ctl->search_called && !ctl->previous_search);
d910f311 1872 if (ctl->hard_tty && ctl->prompt_len > 0)
21d09623 1873 erase_to_col(ctl, 0);
d910f311
SK
1874 if (ctl->no_scroll && num_lines >= ctl->lines_per_screen) {
1875 if (ctl->clear_line_ends)
1876 putp(ctl->go_home);
f16ca88d 1877 else
275d47c9 1878 more_clear_screen(ctl);
f16ca88d 1879 }
d910f311
SK
1880 ctl->screen_start.line_num = ctl->current_line;
1881 ctl->screen_start.row_num = ctl->file_position;
6dbe3af9 1882 }
6dbe3af9
KZ
1883}
1884
0b735ea4 1885static void copy_file(FILE *f)
4eaab145 1886{
f16ca88d
SK
1887 char buf[BUFSIZ];
1888 size_t sz;
1889
1890 while ((sz = fread(&buf, sizeof(char), sizeof(buf), f)) > 0)
1891 fwrite(&buf, sizeof(char), sz, stdout);
6dbe3af9
KZ
1892}
1893
63f96f68 1894
e3cb27f5 1895static void display_file(struct more_control *ctl, int left)
63f96f68
SK
1896{
1897 if (!ctl->current_file)
1898 return;
1899 ctl->context.line_num = ctl->context.row_num = 0;
1900 ctl->current_line = 0;
1901 if (ctl->first_file) {
1902 ctl->first_file = 0;
e3cb27f5
SK
1903 if (ctl->next_jump)
1904 skip_lines(ctl);
63f96f68 1905 if (ctl->search_at_start) {
e3cb27f5 1906 search(ctl, ctl->next_search, 1);
63f96f68
SK
1907 if (ctl->no_scroll)
1908 left--;
e3cb27f5 1909 }
63f96f68
SK
1910 } else if (ctl->argv_position < ctl->num_files && !ctl->no_tty_out)
1911 left =
1912 more_key_command(ctl, ctl->file_names[ctl->argv_position]);
1913 if (left != 0) {
1914 if ((ctl->no_scroll || ctl->clear_first)
8fd1e766 1915 && 0 < ctl->file_size) {
63f96f68
SK
1916 if (ctl->clear_line_ends)
1917 putp(ctl->go_home);
1918 else
1919 more_clear_screen(ctl);
1920 }
1921 if (ctl->print_banner) {
1922 if (ctl->bad_stdout)
21d09623 1923 erase_to_col(ctl, 0);
63f96f68
SK
1924 if (ctl->clear_line_ends)
1925 putp(ctl->erase_line);
63f96f68 1926 if (ctl->prompt_len > 14)
21d09623 1927 erase_to_col(ctl, 14);
63f96f68
SK
1928 if (ctl->clear_line_ends)
1929 putp(ctl->erase_line);
5cd45502 1930 print_separator(':', 14);
9c59b604
VS
1931 if (ctl->clear_line_ends)
1932 putp(ctl->erase_line);
63f96f68
SK
1933 puts(ctl->file_names[ctl->argv_position]);
1934 if (ctl->clear_line_ends)
1935 putp(ctl->erase_line);
5cd45502 1936 print_separator(':', 14);
63f96f68
SK
1937 if (left > ctl->lines_per_page - 4)
1938 left = ctl->lines_per_page - 4;
1939 }
1940 if (ctl->no_tty_out)
1941 copy_file(ctl->current_file);
1942 else
1943 screen(ctl, left);
1944 }
f3c044ee 1945 fflush(NULL);
63f96f68
SK
1946 fclose(ctl->current_file);
1947 ctl->current_file = NULL;
ba105bb5 1948 ctl->screen_start.line_num = ctl->screen_start.row_num = 0;
63f96f68
SK
1949 ctl->context.line_num = ctl->context.row_num = 0L;
1950}
1951
7cc31c18 1952static void initterm(struct more_control *ctl)
f16ca88d
SK
1953{
1954 int ret;
f16ca88d
SK
1955 char *term;
1956 struct winsize win;
22ff9a38 1957 char *cursor_addr;
f16ca88d 1958
f16ca88d 1959#ifndef NON_INTERACTIVE_MORE
d910f311 1960 ctl->no_tty_out = tcgetattr(STDOUT_FILENO, &ctl->output_tty);
f16ca88d 1961#endif
d910f311 1962 ctl->no_tty_in = tcgetattr(STDIN_FILENO, &ctl->output_tty);
2ae8b757 1963 ctl->no_tty_err = tcgetattr(STDERR_FILENO, &ctl->output_tty);
d910f311 1964 ctl->original_tty = ctl->output_tty;
2ae8b757 1965
d910f311 1966 ctl->hard_tabs = (ctl->output_tty.c_oflag & TABDLY) != TAB3;
22ff9a38
SK
1967 if (ctl->no_tty_out)
1968 return;
1969
1970 ctl->output_tty.c_lflag &= ~(ICANON | ECHO);
1971 ctl->output_tty.c_cc[VMIN] = 1;
1972 ctl->output_tty.c_cc[VTIME] = 0;
1973 ctl->erase_previous_ok = (ctl->output_tty.c_cc[VERASE] != 255);
1974 ctl->erase_input_ok = (ctl->output_tty.c_cc[VKILL] != 255);
1975 if ((term = getenv("TERM")) == NULL) {
1976 ctl->dumb_tty = 1;
22ff9a38
SK
1977 }
1978 setupterm(term, 1, &ret);
1979 if (ret <= 0) {
1980 ctl->dumb_tty = 1;
22ff9a38
SK
1981 return;
1982 }
1983 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) < 0) {
1984 ctl->lines_per_page = tigetnum(TERM_LINES);
1985 ctl->num_columns = tigetnum(TERM_COLS);
1986 } else {
1987 if ((ctl->lines_per_page = win.ws_row) == 0)
1988 ctl->lines_per_page = tigetnum(TERM_LINES);
1989 if ((ctl->num_columns = win.ws_col) == 0)
1990 ctl->num_columns = tigetnum(TERM_COLS);
1991 }
1992 if ((ctl->lines_per_page <= 0) || tigetflag(TERM_HARD_COPY)) {
1993 ctl->hard_tty = 1;
1994 ctl->lines_per_page = LINES_PER_PAGE;
4eaab145 1995 }
22ff9a38
SK
1996
1997 if (tigetflag(TERM_EAT_NEW_LINE))
1998 /* Eat newline at last column + 1; dec, concept */
1999 ctl->eat_newline++;
2000 if (ctl->num_columns <= 0)
2001 ctl->num_columns = NUM_COLUMNS;
2002
2003 ctl->wrap_margin = tigetflag(TERM_AUTO_RIGHT_MARGIN);
2004 ctl->bad_stdout = tigetflag(TERM_CEOL);
2005 ctl->erase_line = tigetstr(TERM_CLEAR_TO_LINE_END);
2006 ctl->clear = tigetstr(TERM_CLEAR);
5cd45502
SK
2007 if ((ctl->enter_std = tigetstr(TERM_STANDARD_MODE)) != NULL) {
2008 ctl->exit_std = tigetstr(TERM_EXIT_STANDARD_MODE);
2009 if (0 < tigetnum(TERM_STD_MODE_GLITCH))
2010 ctl->stdout_glitch = 1;
2011 }
22ff9a38 2012
22ff9a38
SK
2013 cursor_addr = tigetstr(TERM_HOME);
2014 if (cursor_addr == NULL || *cursor_addr == '\0') {
2015 cursor_addr = tigetstr(TERM_CURSOR_ADDRESS);
2016 if (cursor_addr)
2017 cursor_addr = tparm(cursor_addr, 0, 0);
2018 }
2019 if (cursor_addr)
2020 ctl->go_home = xstrdup(cursor_addr);
2021
5cd45502
SK
2022 if ((ctl->move_line_down = tigetstr(TERM_LINE_DOWN)) == NULL)
2023 ctl->move_line_down = BACKSPACE;
22ff9a38 2024 ctl->clear_rest = tigetstr(TERM_CLEAR_TO_SCREEN_END);
5cd45502
SK
2025 if ((ctl->backspace_ch = tigetstr(TERM_BACKSPACE)) == NULL)
2026 ctl->backspace_ch = BACKSPACE;
22ff9a38
SK
2027
2028 if ((ctl->shell = getenv("SHELL")) == NULL)
2029 ctl->shell = _PATH_BSHELL;
6dbe3af9
KZ
2030}
2031
f16ca88d
SK
2032int main(int argc, char **argv)
2033{
f16ca88d 2034 char *s;
f16ca88d 2035 int left;
7cc31c18 2036 struct more_control ctl = {
d910f311
SK
2037 .first_file = 1,
2038 .fold_long_lines = 1,
2039 .no_quit_dialog = 1,
d910f311 2040 .stop_after_formfeed = 1,
d910f311
SK
2041 .wrap_margin = 1,
2042 .lines_per_page = LINES_PER_PAGE,
2043 .num_columns = NUM_COLUMNS,
2044 .d_scroll_len = SCROLL_LEN,
7cc31c18
SK
2045 0
2046 };
f16ca88d
SK
2047
2048 setlocale(LC_ALL, "");
2049 bindtextdomain(PACKAGE, LOCALEDIR);
2050 textdomain(PACKAGE);
2c308875 2051 close_stdout_atexit();
f16ca88d 2052 setlocale(LC_ALL, "");
f16ca88d
SK
2053
2054 /* Auto set no scroll on when binary is called page */
2055 if (!(strcmp(program_invocation_short_name, "page")))
d910f311 2056 ctl.no_scroll++;
f16ca88d 2057
28b391ce
KZ
2058 ctl.exit_on_eof = getenv("POSIXLY_CORRECT") ? 0 : 1;
2059
e3cb27f5
SK
2060 if ((s = getenv("MORE")) != NULL)
2061 env_argscan(&ctl, s);
28b391ce 2062
e3cb27f5
SK
2063 argscan(&ctl, argc, argv);
2064
e18f896e
KZ
2065 /* clear any inherited settings */
2066 signal(SIGCHLD, SIG_DFL);
2067
e3cb27f5
SK
2068 initterm(&ctl);
2069
2ae8b757
KZ
2070 if (ctl.no_tty_err)
2071 /* exit when we cannot read user's input */
2072 ctl.exit_on_eof = 1;
2073
09070e1a
SK
2074#ifdef HAVE_MAGIC
2075 ctl.magic = magic_open(MAGIC_MIME_ENCODING | MAGIC_SYMLINK);
2076 magic_load(ctl.magic, NULL);
2077#endif
7cc31c18 2078 prepare_line_buffer(&ctl);
f16ca88d 2079
d910f311
SK
2080 ctl.d_scroll_len = ctl.lines_per_page / 2 - 1;
2081 if (ctl.d_scroll_len <= 0)
2082 ctl.d_scroll_len = 1;
f16ca88d 2083
d910f311
SK
2084 /* allow clear_line_ends only if go_home and erase_line and clear_rest strings are
2085 * defined, and in that case, make sure we are in no_scroll mode */
2086 if (ctl.clear_line_ends) {
2087 if ((ctl.go_home == NULL) || (*ctl.go_home == '\0') ||
2088 (ctl.erase_line == NULL) || (*ctl.erase_line == '\0') ||
2089 (ctl.clear_rest == NULL) || (*ctl.clear_rest == '\0'))
2090 ctl.clear_line_ends = 0;
f16ca88d 2091 else
d910f311 2092 ctl.no_scroll = 1;
f16ca88d 2093 }
d910f311
SK
2094 if (ctl.lines_per_screen == 0)
2095 ctl.lines_per_screen = ctl.lines_per_page - 1;
2096 left = ctl.lines_per_screen;
2097 if (ctl.num_files > 1)
63f96f68 2098 ctl.print_banner = 1;
d910f311 2099 if (!ctl.no_tty_in && ctl.num_files == 0) {
f16ca88d
SK
2100 warnx(_("bad usage"));
2101 errtryhelp(EXIT_FAILURE);
2102 } else
82129a6f 2103 ctl.current_file = stdin;
d910f311 2104 if (!ctl.no_tty_out) {
f16ca88d 2105 if (signal(SIGTSTP, SIG_IGN) == SIG_DFL) {
d910f311 2106 ctl.catch_suspend++;
f16ca88d 2107 }
d910f311 2108 tcsetattr(STDERR_FILENO, TCSANOW, &ctl.output_tty);
f16ca88d 2109 }
823ef319
SK
2110 sigemptyset(&ctl.sigset);
2111 sigaddset(&ctl.sigset, SIGINT);
2112 sigaddset(&ctl.sigset, SIGQUIT);
2113 sigaddset(&ctl.sigset, SIGTSTP);
6dcaaa9e 2114 sigaddset(&ctl.sigset, SIGCONT);
823ef319
SK
2115 sigaddset(&ctl.sigset, SIGWINCH);
2116 sigprocmask(SIG_BLOCK, &ctl.sigset, NULL);
2117 ctl.sigfd = signalfd(-1, &ctl.sigset, SFD_CLOEXEC);
d910f311
SK
2118 if (ctl.no_tty_in) {
2119 if (ctl.no_tty_out)
f16ca88d
SK
2120 copy_file(stdin);
2121 else {
82129a6f 2122 ctl.current_file = stdin;
e3cb27f5 2123 display_file(&ctl, left);
f16ca88d 2124 }
d910f311 2125 ctl.no_tty_in = 0;
63f96f68 2126 ctl.print_banner = 1;
d910f311 2127 ctl.first_file = 0;
f16ca88d
SK
2128 }
2129
d910f311 2130 while (ctl.argv_position < ctl.num_files) {
63f96f68 2131 checkf(&ctl, ctl.file_names[ctl.argv_position]);
e3cb27f5 2132 display_file(&ctl, left);
d910f311 2133 ctl.first_file = 0;
63f96f68 2134 ctl.argv_position++;
f16ca88d 2135 }
73ff5a95
SK
2136 ctl.clear_line_ends = 0;
2137 ctl.prompt_len = 0;
2138 more_exit(&ctl);
f16ca88d 2139}