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