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