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