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