]>
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" |
823ef319 | 86 | #include "rpmatch.h" |
09070e1a | 87 | #include "env.h" |
6dbe3af9 | 88 | |
951666e9 KZ |
89 | #ifdef TEST_PROGRAM |
90 | # define NON_INTERACTIVE_MORE 1 | |
91 | #endif | |
92 | ||
5cd45502 | 93 | #define BACKSPACE "\b" |
7cc31c18 | 94 | #define CARAT "^" |
a8f98304 | 95 | |
1f861935 SK |
96 | #define ARROW_UP "\x1b\x5b\x41" |
97 | #define ARROW_DOWN "\x1b\x5b\x42" | |
98 | #define PAGE_UP "\x1b\x5b\x35\x7e" | |
99 | #define PAGE_DOWN "\x1b\x5b\x36\x7e" | |
100 | ||
d910f311 | 101 | #define MIN_LINE_SZ 256 /* minimal line_buf buffer size */ |
4eaab145 | 102 | #define ESC '\033' |
bf6db55a SK |
103 | #define SCROLL_LEN 11 |
104 | #define LINES_PER_PAGE 24 | |
105 | #define NUM_COLUMNS 80 | |
106 | #define TERMINAL_BUF 4096 | |
107 | #define INIT_BUF 80 | |
bf6db55a | 108 | #define COMMAND_BUF 200 |
eecbb2cf | 109 | #define REGERR_BUF NUM_COLUMNS |
a8f98304 SK |
110 | |
111 | #define TERM_AUTO_RIGHT_MARGIN "am" | |
5cd45502 | 112 | #define TERM_BACKSPACE "cub1" |
a8f98304 SK |
113 | #define TERM_CEOL "xhp" |
114 | #define TERM_CLEAR "clear" | |
115 | #define TERM_CLEAR_TO_LINE_END "el" | |
116 | #define TERM_CLEAR_TO_SCREEN_END "ed" | |
117 | #define TERM_COLS "cols" | |
118 | #define TERM_CURSOR_ADDRESS "cup" | |
119 | #define TERM_EAT_NEW_LINE "xenl" | |
a8f98304 | 120 | #define TERM_EXIT_STANDARD_MODE "rmso" |
a8f98304 SK |
121 | #define TERM_HARD_COPY "hc" |
122 | #define TERM_HOME "home" | |
123 | #define TERM_LINE_DOWN "cud1" | |
124 | #define TERM_LINES "lines" | |
125 | #define TERM_OVER_STRIKE "os" | |
a8f98304 SK |
126 | #define TERM_STANDARD_MODE "smso" |
127 | #define TERM_STD_MODE_GLITCH "xmc" | |
6dbe3af9 | 128 | |
1f861935 SK |
129 | /* Used in read_command() */ |
130 | typedef enum { | |
131 | more_kc_unknown_command, | |
132 | more_kc_colon, | |
133 | more_kc_repeat_previous, | |
134 | more_kc_backwards, | |
135 | more_kc_jump_lines_per_screen, | |
136 | more_kc_set_lines_per_screen, | |
137 | more_kc_set_scroll_len, | |
138 | more_kc_quit, | |
139 | more_kc_skip_forward, | |
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 */ | |
209 | first_file:1, /* is the input file the first in list */ | |
210 | fold_long_lines:1, /* fold long lines */ | |
211 | hard_tabs:1, /* print spaces instead of '\t' */ | |
212 | hard_tty:1, /* is this hard copy terminal (a printer or such) */ | |
1f861935 | 213 | leading_colon:1, /* key command has leading ':' character */ |
d910f311 SK |
214 | is_paused:1, /* is output paused */ |
215 | no_quit_dialog:1, /* suppress quit dialog */ | |
216 | no_scroll:1, /* do not scroll, clear the screen and then display text */ | |
217 | no_tty_in:1, /* is input in interactive mode */ | |
218 | no_tty_out:1, /* is output in interactive mode */ | |
63f96f68 | 219 | print_banner:1, /* print file name banner */ |
1f861935 | 220 | reading_num:1, /* are we reading leading_number */ |
d910f311 | 221 | report_errors:1, /* is an error reported */ |
63f96f68 | 222 | search_at_start:1, /* search pattern defined at start up */ |
823ef319 | 223 | search_called:1, /* previous more command was a search */ |
d910f311 | 224 | squeeze_spaces:1, /* suppress white space */ |
d910f311 SK |
225 | stdout_glitch:1, /* terminal has standout mode glitch */ |
226 | stop_after_formfeed:1, /* stop after form feeds */ | |
227 | suppress_bell:1, /* suppress bell */ | |
d910f311 | 228 | wrap_margin:1; /* set if automargins */ |
7cc31c18 SK |
229 | }; |
230 | ||
c1da6d1e | 231 | static void __attribute__((__noreturn__)) usage(void) |
bf6db55a | 232 | { |
e3cb27f5 SK |
233 | printf("%s", USAGE_HEADER); |
234 | printf(_(" %s [options] <file>...\n"), program_invocation_short_name); | |
235 | ||
236 | printf("%s", USAGE_SEPARATOR); | |
237 | printf("%s\n", _("A file perusal filter for CRT viewing.")); | |
238 | ||
239 | printf("%s", USAGE_OPTIONS); | |
240 | printf("%s\n", _(" -d, --silent display help instead of ringing bell")); | |
241 | printf("%s\n", _(" -f, --logical count logical rather than screen lines")); | |
242 | printf("%s\n", _(" -l, --no-pause suppress pause after form feed")); | |
243 | printf("%s\n", _(" -c, --print-over do not scroll, display text and clean line ends")); | |
244 | printf("%s\n", _(" -p, --clean-print do not scroll, clean screen and display text")); | |
245 | printf("%s\n", _(" -s, --squeeze squeeze multiple blank lines into one")); | |
246 | printf("%s\n", _(" -u, --plain suppress underlining and bold")); | |
247 | printf("%s\n", _(" -n, --lines <number> the number of lines per screenful")); | |
248 | printf("%s\n", _(" -<number> same as --lines")); | |
249 | printf("%s\n", _(" +<number> display file beginning from line number")); | |
250 | printf("%s\n", _(" +/<pattern> display file beginning from pattern match")); | |
251 | printf("%s", USAGE_SEPARATOR); | |
252 | printf(USAGE_HELP_OPTIONS(23)); | |
f45f3ec3 | 253 | printf(USAGE_MAN_TAIL("more(1)")); |
c1da6d1e | 254 | exit(EXIT_SUCCESS); |
66ee8158 KZ |
255 | } |
256 | ||
e3cb27f5 | 257 | static void argscan(struct more_control *ctl, int as_argc, char **as_argv) |
4eaab145 | 258 | { |
e3cb27f5 SK |
259 | int c, opt; |
260 | static const struct option longopts[] = { | |
261 | { "silent", no_argument, NULL, 'd' }, | |
262 | { "logical", no_argument, NULL, 'f' }, | |
263 | { "no-pause", no_argument, NULL, 'l' }, | |
264 | { "print-over", no_argument, NULL, 'c' }, | |
265 | { "clean-print", no_argument, NULL, 'p' }, | |
266 | { "squeeze", no_argument, NULL, 's' }, | |
267 | { "plain", no_argument, NULL, 'u' }, | |
268 | { "lines", required_argument, NULL, 'n' }, | |
269 | { "version", no_argument, NULL, 'V' }, | |
270 | { "help", no_argument, NULL, 'h' }, | |
271 | { NULL, 0, NULL, 0 } | |
272 | }; | |
273 | ||
274 | /* Take care of number option and +args. */ | |
275 | for (opt = 0; opt < as_argc; opt++) { | |
276 | int move = 0; | |
277 | ||
278 | if (as_argv[opt][0] == '-' && isdigit_string(as_argv[opt] + 1)) { | |
279 | ctl->lines_per_screen = | |
280 | strtos16_or_err(as_argv[opt], _("failed to parse number")); | |
281 | ctl->lines_per_screen = abs(ctl->lines_per_screen); | |
282 | move = 1; | |
283 | } else if (as_argv[opt][0] == '+') { | |
284 | if (isdigit_string(as_argv[opt] + 1)) { | |
285 | ctl->next_jump = strtos32_or_err(as_argv[opt], | |
286 | _("failed to parse number")) - 1; | |
287 | move = 1; | |
288 | } else if (as_argv[opt][1] == '/') { | |
289 | free(ctl->next_search); | |
290 | ctl->next_search = xstrdup(as_argv[opt] + 2); | |
291 | ctl->search_at_start = 1; | |
292 | move = 1; | |
6dbe3af9 | 293 | } |
e3cb27f5 SK |
294 | } |
295 | if (move) { | |
ed292a08 | 296 | as_argc = remote_entry(as_argv, opt, as_argc); |
e3cb27f5 SK |
297 | opt--; |
298 | } | |
299 | } | |
300 | ||
301 | while ((c = getopt_long(as_argc, as_argv, "dflcpsun:eVh", longopts, NULL)) != -1) { | |
302 | switch (c) { | |
4eaab145 | 303 | case 'd': |
d910f311 | 304 | ctl->suppress_bell = 1; |
6dbe3af9 | 305 | break; |
4eaab145 | 306 | case 'l': |
d910f311 | 307 | ctl->stop_after_formfeed = 0; |
6dbe3af9 | 308 | break; |
4eaab145 | 309 | case 'f': |
d910f311 | 310 | ctl->fold_long_lines = 0; |
6dbe3af9 | 311 | break; |
4eaab145 | 312 | case 'p': |
d910f311 | 313 | ctl->no_scroll = 1; |
6dbe3af9 | 314 | break; |
4eaab145 | 315 | case 'c': |
d910f311 | 316 | ctl->clear_line_ends = 1; |
6dbe3af9 | 317 | break; |
4eaab145 | 318 | case 's': |
d910f311 | 319 | ctl->squeeze_spaces = 1; |
6dbe3af9 | 320 | break; |
4eaab145 | 321 | case 'u': |
6dbe3af9 | 322 | break; |
e3cb27f5 SK |
323 | case 'n': |
324 | ctl->lines_per_screen = strtou16_or_err(optarg, _("argument error")); | |
325 | break; | |
326 | case 'e': /* ignored silently to be posix compliant */ | |
4eaab145 SK |
327 | break; |
328 | case 'V': | |
2c308875 | 329 | print_version(EXIT_SUCCESS); |
e3cb27f5 SK |
330 | case 'h': |
331 | usage(); | |
4eaab145 | 332 | default: |
c1da6d1e | 333 | errtryhelp(EXIT_FAILURE); |
66ee8158 | 334 | break; |
6dbe3af9 | 335 | } |
6dbe3af9 | 336 | } |
e3cb27f5 SK |
337 | ctl->num_files = as_argc - optind; |
338 | ctl->file_names = as_argv + optind; | |
339 | } | |
340 | ||
341 | static void env_argscan(struct more_control *ctl, const char *s) | |
342 | { | |
343 | char **env_argv; | |
344 | int env_argc = 1; | |
345 | int size = 8; | |
346 | const char delim[] = { ' ', '\n', '\t', '\0' }; | |
347 | char *str = xstrdup(s); | |
348 | char *key = NULL, *tok; | |
349 | ||
350 | env_argv = xmalloc(sizeof(char *) * size); | |
351 | env_argv[0] = _("MORE environment variable"); /* program name */ | |
352 | for (tok = strtok_r(str, delim, &key); tok; tok = strtok_r(NULL, delim, &key)) { | |
353 | env_argv[env_argc++] = tok; | |
354 | if (size < env_argc) { | |
355 | size *= 2; | |
356 | env_argv = xrealloc(env_argv, sizeof(char *) * size); | |
357 | } | |
358 | } | |
359 | ||
360 | argscan(ctl, env_argc, env_argv); | |
361 | /* Reset optind, command line parsing needs this. */ | |
362 | optind = 0; | |
363 | free(str); | |
364 | free(env_argv); | |
6dbe3af9 KZ |
365 | } |
366 | ||
ba105bb5 | 367 | static void more_fseek(struct more_control *ctl, off_t pos) |
bd6a253f | 368 | { |
d910f311 | 369 | ctl->file_position = pos; |
ba105bb5 | 370 | fseeko(ctl->current_file, pos, SEEK_SET); |
bd6a253f SK |
371 | } |
372 | ||
82129a6f | 373 | static int more_getc(struct more_control *ctl) |
bd6a253f | 374 | { |
ba105bb5 SK |
375 | int ret = getc(ctl->current_file); |
376 | ctl->file_position = ftello(ctl->current_file); | |
377 | return ret; | |
bd6a253f SK |
378 | } |
379 | ||
82129a6f | 380 | static int more_ungetc(struct more_control *ctl, int c) |
bd6a253f | 381 | { |
ba105bb5 SK |
382 | int ret = ungetc(c, ctl->current_file); |
383 | ctl->file_position = ftello(ctl->current_file); | |
384 | return ret; | |
bd6a253f SK |
385 | } |
386 | ||
5cd45502 SK |
387 | static void print_separator(const int c, int n) |
388 | { | |
389 | while (n--) | |
390 | putchar(c); | |
391 | putchar('\n'); | |
392 | } | |
393 | ||
09070e1a SK |
394 | /* check_magic -- check for file magic numbers. */ |
395 | static int check_magic(struct more_control *ctl, char *fs) | |
82cfa6f2 | 396 | { |
09070e1a | 397 | #ifdef HAVE_MAGIC |
2eb52772 SK |
398 | const int fd = fileno(ctl->current_file); |
399 | const char *mime_encoding = magic_descriptor(ctl->magic, fd); | |
400 | const char *magic_error_msg = magic_error(ctl->magic); | |
401 | struct stat st; | |
402 | ||
403 | if (magic_error_msg) { | |
404 | printf(_("magic failed: %s\n"), magic_error_msg); | |
405 | return 0; | |
406 | } | |
407 | if (fstat(fd, &st)) { | |
408 | warn(_("cannot stat %s"), fs); | |
409 | return 1; | |
410 | } | |
411 | if (st.st_size == 0) { | |
412 | /* libmagic tells an empty file has binary encoding */ | |
413 | return 0; | |
414 | } | |
09070e1a SK |
415 | |
416 | if (!mime_encoding || !(strcmp("binary", mime_encoding))) { | |
417 | printf(_("\n******** %s: Not a text file ********\n\n"), fs); | |
418 | return 1; | |
419 | } | |
420 | #else | |
82cfa6f2 SK |
421 | signed char twobytes[2]; |
422 | ||
423 | /* don't try to look ahead if the input is unseekable */ | |
09070e1a | 424 | if (fseek(ctl->current_file, 0L, SEEK_SET)) |
82cfa6f2 SK |
425 | return 0; |
426 | ||
09070e1a | 427 | if (fread(twobytes, 2, 1, ctl->current_file) == 1) { |
82cfa6f2 SK |
428 | switch (twobytes[0] + (twobytes[1] << 8)) { |
429 | case 0407: /* a.out obj */ | |
430 | case 0410: /* a.out exec */ | |
431 | case 0413: /* a.out demand exec */ | |
432 | case 0405: | |
433 | case 0411: | |
434 | case 0177545: | |
435 | case 0x457f: /* simple ELF detection */ | |
436 | printf(_("\n******** %s: Not a text file ********\n\n"), | |
437 | fs); | |
438 | return 1; | |
439 | } | |
440 | } | |
09070e1a SK |
441 | fseek(ctl->current_file, 0L, SEEK_SET); /* rewind() not necessary */ |
442 | #endif | |
82cfa6f2 SK |
443 | return 0; |
444 | } | |
445 | ||
4eaab145 SK |
446 | /* Check whether the file named by fs is an ASCII file which the user may |
447 | * access. If it is, return the opened file. Otherwise return NULL. */ | |
63f96f68 | 448 | static void checkf(struct more_control *ctl, char *fs) |
6dbe3af9 | 449 | { |
d910f311 | 450 | struct stat st; |
e8f26419 | 451 | int c; |
6dbe3af9 | 452 | |
f3c044ee SK |
453 | ctl->current_line = 0; |
454 | ctl->file_position = 0; | |
455 | fflush(NULL); | |
456 | if (((ctl->current_file = fopen(fs, "r")) == NULL) || | |
457 | (fstat(fileno(ctl->current_file), &st) != 0)) { | |
d910f311 SK |
458 | if (ctl->clear_line_ends) |
459 | putp(ctl->erase_line); | |
09070e1a | 460 | warn(_("cannot open %s"), fs); |
82129a6f | 461 | return; |
6dbe3af9 | 462 | } |
09070e1a | 463 | #ifndef HAVE_MAGIC |
d910f311 | 464 | if ((st.st_mode & S_IFMT) == S_IFDIR) { |
d162fcb5 | 465 | printf(_("\n*** %s: directory ***\n\n"), fs); |
82129a6f SK |
466 | ctl->current_file = NULL; |
467 | return; | |
6dbe3af9 | 468 | } |
09070e1a SK |
469 | #endif |
470 | if (check_magic(ctl, fs)) { | |
82129a6f SK |
471 | fclose(ctl->current_file); |
472 | ctl->current_file = NULL; | |
473 | return; | |
b9579f1f | 474 | } |
82129a6f SK |
475 | fcntl(fileno(ctl->current_file), F_SETFD, FD_CLOEXEC); |
476 | c = more_getc(ctl); | |
63f96f68 | 477 | ctl->clear_first = (c == '\f'); |
82129a6f | 478 | more_ungetc(ctl, c); |
d910f311 | 479 | if ((ctl->file_size = st.st_size) == 0) |
ba105bb5 | 480 | ctl->file_size = ~((off_t)0); |
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 SK |
731 | ctl->prompt_len += printf(_("(Next file: %s)"), filename); |
732 | } else if (!ctl->no_tty_in) { | |
733 | ctl->prompt_len += | |
f16ca88d | 734 | printf("(%d%%)", |
d910f311 | 735 | (int)((ctl->file_position * 100) / ctl->file_size)); |
f16ca88d | 736 | } |
d910f311 SK |
737 | if (ctl->suppress_bell) { |
738 | ctl->prompt_len += | |
f16ca88d SK |
739 | printf(_("[Press space to continue, 'q' to quit.]")); |
740 | } | |
5cd45502 | 741 | if (ctl->exit_std) |
d910f311 SK |
742 | putp(ctl->exit_std); |
743 | if (ctl->clear_line_ends) | |
744 | putp(ctl->clear_rest); | |
f16ca88d | 745 | } else |
508b2d79 | 746 | fprintf(stderr, "\a"); |
f3c044ee | 747 | fflush(NULL); |
6dbe3af9 KZ |
748 | } |
749 | ||
823ef319 | 750 | static void reset_tty(struct more_control *ctl) |
82cfa6f2 | 751 | { |
823ef319 | 752 | if (ctl->no_tty_out) |
82cfa6f2 | 753 | return; |
f3c044ee | 754 | fflush(NULL); |
823ef319 SK |
755 | ctl->output_tty.c_lflag |= ICANON | ECHO; |
756 | ctl->output_tty.c_cc[VMIN] = ctl->original_tty.c_cc[VMIN]; | |
757 | ctl->output_tty.c_cc[VTIME] = ctl->original_tty.c_cc[VTIME]; | |
758 | tcsetattr(STDERR_FILENO, TCSANOW, &ctl->original_tty); | |
82cfa6f2 SK |
759 | } |
760 | ||
761 | /* Clean up terminal state and exit. Also come here if interrupt signal received */ | |
823ef319 | 762 | static void __attribute__((__noreturn__)) more_exit(struct more_control *ctl) |
82cfa6f2 | 763 | { |
09070e1a SK |
764 | #ifdef HAVE_MAGIC |
765 | magic_close(ctl->magic); | |
766 | #endif | |
823ef319 SK |
767 | reset_tty(ctl); |
768 | if (ctl->clear_line_ends) { | |
82cfa6f2 | 769 | putchar('\r'); |
823ef319 | 770 | putp(ctl->erase_line); |
f3c044ee | 771 | } else if (!ctl->clear_line_ends && (ctl->prompt_len > 0)) |
21d09623 | 772 | erase_to_col(ctl, 0); |
f3c044ee | 773 | fflush(NULL); |
823ef319 | 774 | free(ctl->previous_search); |
474c61c0 | 775 | free(ctl->shell_line); |
823ef319 | 776 | free(ctl->line_buf); |
22ff9a38 | 777 | free(ctl->go_home); |
73ff5a95 SK |
778 | if (ctl->current_file) |
779 | fclose(ctl->current_file); | |
780 | del_curterm(cur_term); | |
82cfa6f2 SK |
781 | _exit(EXIT_SUCCESS); |
782 | } | |
783 | ||
ba105bb5 | 784 | static cc_t read_user_input(struct more_control *ctl) |
6dbe3af9 | 785 | { |
ba105bb5 | 786 | cc_t c; |
f16ca88d SK |
787 | |
788 | errno = 0; | |
1f861935 SK |
789 | /* |
790 | * Key commands can be read() from either stderr or stdin. If they | |
791 | * are read from stdin such as 'cat file.txt | more' then the pipe | |
792 | * input is understood as series key commands - and that is not | |
793 | * wanted. Keep the read() reading from stderr. | |
794 | */ | |
d2b54002 | 795 | if (read(STDERR_FILENO, &c, 1) <= 0) { |
f16ca88d | 796 | if (errno != EINTR) |
823ef319 | 797 | more_exit(ctl); |
f16ca88d | 798 | else |
d910f311 | 799 | c = ctl->output_tty.c_cc[VKILL]; |
f16ca88d | 800 | } |
f728d8ba | 801 | return c; |
6dbe3af9 KZ |
802 | } |
803 | ||
1f861935 | 804 | /* Read a number and command from the terminal. Set cmd to the non-digit |
f16ca88d | 805 | * which terminates the number. */ |
1f861935 | 806 | static struct number_command read_command(struct more_control *ctl) |
6dbe3af9 | 807 | { |
1f861935 SK |
808 | cc_t input[8] = { 0 }; |
809 | ssize_t i, ilen; | |
810 | struct number_command cmd = { .key = more_kc_unknown_command }; | |
811 | ||
812 | /* See stderr note in read_user_input() */ | |
813 | if ((ilen = read(STDERR_FILENO, &input, sizeof(input))) <= 0) | |
814 | return cmd; | |
815 | if (2 < ilen) { | |
816 | if (!memcmp(input, ARROW_UP, sizeof(ARROW_UP))) { | |
817 | cmd.key = more_kc_backwards; | |
818 | return cmd; | |
819 | } else if (!memcmp(input, ARROW_DOWN, sizeof(ARROW_DOWN))) { | |
820 | cmd.key = more_kc_skip_forward; | |
821 | return cmd; | |
822 | } else if (!memcmp(input, PAGE_UP, sizeof(PAGE_UP))) { | |
823 | cmd.key = more_kc_backwards; | |
824 | return cmd; | |
825 | } else if (!memcmp(input, PAGE_DOWN, sizeof(PAGE_DOWN))) { | |
826 | cmd.key = more_kc_skip_forward; | |
827 | return cmd; | |
828 | } | |
829 | } | |
830 | for (i = 0; i < ilen; i++) { | |
831 | if (isdigit(input[i])) { | |
832 | if (0 < ctl->reading_num) { | |
833 | ctl->leading_number *= 10; | |
834 | ctl->leading_number += input[i] - '0'; | |
835 | } else | |
836 | ctl->leading_number = input[i] - '0'; | |
837 | ctl->reading_num = 1; | |
838 | continue; | |
839 | } | |
840 | cmd.number = ctl->leading_number; | |
841 | ctl->reading_num = 0; | |
842 | ctl->leading_number = 0; | |
843 | if (ctl->leading_colon) { | |
844 | ctl->leading_colon = 0; | |
845 | switch (input[i]) { | |
846 | case 'f': | |
847 | cmd.key = more_kc_display_file_and_line; | |
848 | return cmd; | |
849 | case 'n': | |
850 | cmd.key = more_kc_next_file; | |
851 | return cmd; | |
852 | case 'p': | |
853 | cmd.key = more_kc_previous_file; | |
854 | return cmd; | |
855 | default: | |
856 | cmd.key = more_kc_unknown_command; | |
857 | return cmd; | |
858 | } | |
859 | } | |
860 | /* command is a single char */ | |
861 | switch (input[i]) { | |
862 | case '.': | |
863 | cmd.key = more_kc_repeat_previous; | |
864 | break; | |
865 | case ':': | |
866 | ctl->leading_colon = 1; | |
867 | break; | |
868 | case 'b': | |
869 | case CTRL('B'): | |
870 | cmd.key = more_kc_backwards; | |
871 | break; | |
872 | case ' ': | |
873 | cmd.key = more_kc_jump_lines_per_screen; | |
874 | break; | |
875 | case 'z': | |
876 | cmd.key = more_kc_set_lines_per_screen; | |
877 | break; | |
878 | case 'd': | |
879 | case CTRL('D'): | |
880 | cmd.key = more_kc_set_scroll_len; | |
881 | break; | |
882 | case 'q': | |
883 | case 'Q': | |
884 | cmd.key = more_kc_quit; | |
885 | break; | |
886 | case 'f': | |
887 | case 's': | |
888 | case CTRL('F'): | |
889 | cmd.key = more_kc_skip_forward; | |
890 | break; | |
891 | case '\n': | |
892 | cmd.key = more_kc_next_line; | |
893 | break; | |
894 | case '\f': | |
895 | cmd.key = more_kc_clear_screen; | |
896 | break; | |
897 | case '\'': | |
898 | cmd.key = more_kc_previous_search_match; | |
899 | break; | |
900 | case '=': | |
901 | cmd.key = more_kc_display_line; | |
902 | break; | |
903 | case 'n': | |
904 | cmd.key = more_kc_repeat_search; | |
905 | break; | |
906 | case '/': | |
907 | cmd.key = more_kc_search; | |
908 | break; | |
909 | case '!': | |
910 | cmd.key = more_kc_run_shell; | |
911 | break; | |
912 | case '?': | |
913 | case 'h': | |
914 | cmd.key = more_kc_help; | |
915 | break; | |
916 | case 'v': | |
917 | cmd.key = more_kc_run_editor; | |
f16ca88d | 918 | break; |
6dbe3af9 | 919 | } |
f16ca88d | 920 | } |
1f861935 | 921 | return cmd; |
f16ca88d | 922 | } |
4eaab145 | 923 | |
275d47c9 SK |
924 | /* Change displayed file from command line list to next nskip, where nskip |
925 | * is relative position in argv and can be negative, that is a previous | |
926 | * file. */ | |
927 | static void change_file(struct more_control *ctl, int nskip) | |
f16ca88d SK |
928 | { |
929 | if (nskip == 0) | |
930 | return; | |
931 | if (nskip > 0) { | |
d910f311 SK |
932 | if (ctl->argv_position + nskip > ctl->num_files - 1) |
933 | nskip = ctl->num_files - ctl->argv_position - 1; | |
823ef319 | 934 | } |
d910f311 SK |
935 | ctl->argv_position += nskip; |
936 | if (ctl->argv_position < 0) | |
937 | ctl->argv_position = 0; | |
f16ca88d | 938 | puts(_("\n...Skipping ")); |
d910f311 SK |
939 | if (ctl->clear_line_ends) |
940 | putp(ctl->erase_line); | |
f16ca88d | 941 | if (nskip > 0) |
bd6a253f | 942 | fputs(_("...Skipping to file "), stdout); |
f16ca88d | 943 | else |
bd6a253f | 944 | fputs(_("...Skipping back to file "), stdout); |
d910f311 SK |
945 | puts(ctl->file_names[ctl->argv_position]); |
946 | if (ctl->clear_line_ends) | |
947 | putp(ctl->erase_line); | |
f16ca88d | 948 | putchar('\n'); |
d910f311 | 949 | ctl->argv_position--; |
f16ca88d | 950 | } |
4eaab145 | 951 | |
7cc31c18 | 952 | static void show(struct more_control *ctl, char c) |
f16ca88d | 953 | { |
508b2d79 SK |
954 | if ((c < ' ' && c != '\n' && c != ESC) || c == CERASE) { |
955 | c += (c == CERASE) ? -0100 : 0100; | |
bd6a253f | 956 | fputs(CARAT, stderr); |
d910f311 | 957 | ctl->prompt_len++; |
f16ca88d | 958 | } |
bd6a253f | 959 | fputc(c, stderr); |
d910f311 | 960 | ctl->prompt_len++; |
f16ca88d | 961 | } |
4eaab145 | 962 | |
7cc31c18 | 963 | static void more_error(struct more_control *ctl, char *mess) |
f16ca88d | 964 | { |
d910f311 SK |
965 | if (ctl->clear_line_ends) |
966 | putp(ctl->erase_line); | |
f16ca88d | 967 | else |
21d09623 | 968 | erase_to_col(ctl, 0); |
d910f311 | 969 | ctl->prompt_len += strlen(mess); |
5cd45502 | 970 | if (ctl->enter_std) |
d910f311 | 971 | putp(ctl->enter_std); |
5cd45502 SK |
972 | fputs(mess, stdout); |
973 | if (ctl->exit_std) | |
d910f311 | 974 | putp(ctl->exit_std); |
f3c044ee | 975 | fflush(NULL); |
d910f311 | 976 | ctl->report_errors++; |
f16ca88d | 977 | } |
6dbe3af9 | 978 | |
bd6a253f SK |
979 | static void erase_one_column(struct more_control *ctl) |
980 | { | |
d910f311 | 981 | if (ctl->erase_previous_ok) |
5cd45502 SK |
982 | fprintf(stderr, "%s ", ctl->backspace_ch); |
983 | fputs(ctl->backspace_ch, stderr); | |
bd6a253f SK |
984 | } |
985 | ||
0b735ea4 | 986 | static void ttyin(struct more_control *ctl, char buf[], int nmax, char pchar) |
f16ca88d SK |
987 | { |
988 | char *sp; | |
ba105bb5 | 989 | cc_t c; |
f16ca88d SK |
990 | int slash = 0; |
991 | int maxlen; | |
6dbe3af9 | 992 | |
f16ca88d SK |
993 | sp = buf; |
994 | maxlen = 0; | |
995 | while (sp - buf < nmax) { | |
d910f311 SK |
996 | if (ctl->prompt_len > maxlen) |
997 | maxlen = ctl->prompt_len; | |
275d47c9 | 998 | c = read_user_input(ctl); |
f16ca88d SK |
999 | if (c == '\\') { |
1000 | slash++; | |
ba105bb5 | 1001 | } else if (c == ctl->output_tty.c_cc[VERASE] && !slash) { |
f16ca88d SK |
1002 | if (sp > buf) { |
1003 | #ifdef HAVE_WIDECHAR | |
1004 | if (MB_CUR_MAX > 1) { | |
1005 | wchar_t wc; | |
1006 | size_t pos = 0, mblength; | |
1007 | mbstate_t state, state_bak; | |
1008 | ||
1009 | memset(&state, '\0', sizeof(mbstate_t)); | |
1010 | ||
1011 | while (1) { | |
1012 | state_bak = state; | |
1013 | mblength = | |
1014 | mbrtowc(&wc, buf + pos, | |
1015 | sp - buf, &state); | |
1016 | ||
5cd45502 SK |
1017 | switch (mblength) { |
1018 | case (size_t)-2: | |
1019 | case (size_t)-1: | |
1020 | state = state_bak; | |
1021 | /* fallthrough */ | |
1022 | case 0: | |
1023 | mblength = 1; | |
1024 | } | |
f16ca88d SK |
1025 | if (buf + pos + mblength >= sp) |
1026 | break; | |
1027 | ||
1028 | pos += mblength; | |
4eaab145 | 1029 | } |
f16ca88d SK |
1030 | |
1031 | if (mblength == 1) { | |
bd6a253f | 1032 | erase_one_column(ctl); |
f16ca88d SK |
1033 | } else { |
1034 | int wc_width; | |
1035 | wc_width = wcwidth(wc); | |
1036 | wc_width = | |
1037 | (wc_width < | |
1038 | 1) ? 1 : wc_width; | |
1039 | while (wc_width--) { | |
bd6a253f | 1040 | erase_one_column(ctl); |
f16ca88d SK |
1041 | } |
1042 | } | |
1043 | ||
1044 | while (mblength--) { | |
d910f311 | 1045 | --ctl->prompt_len; |
f16ca88d SK |
1046 | --sp; |
1047 | } | |
1048 | } else | |
1049 | #endif /* HAVE_WIDECHAR */ | |
1050 | { | |
d910f311 | 1051 | --ctl->prompt_len; |
bd6a253f | 1052 | erase_one_column(ctl); |
f16ca88d SK |
1053 | --sp; |
1054 | } | |
1055 | ||
508b2d79 | 1056 | if ((*sp < ' ' && *sp != '\n') || *sp == CERASE) { |
d910f311 | 1057 | --ctl->prompt_len; |
bd6a253f | 1058 | erase_one_column(ctl); |
f16ca88d SK |
1059 | } |
1060 | continue; | |
4eaab145 | 1061 | } |
042f62df RP |
1062 | if (!ctl->erase_line) |
1063 | ctl->prompt_len = maxlen; | |
ba105bb5 | 1064 | } else if (c == ctl->output_tty.c_cc[VKILL] && !slash) { |
d910f311 | 1065 | if (ctl->hard_tty) { |
7cc31c18 | 1066 | show(ctl, c); |
f16ca88d SK |
1067 | putchar('\n'); |
1068 | putchar(pchar); | |
4eaab145 | 1069 | } else { |
f16ca88d SK |
1070 | putchar('\r'); |
1071 | putchar(pchar); | |
d910f311 | 1072 | if (ctl->erase_line) |
21d09623 | 1073 | erase_to_col(ctl, 1); |
d910f311 SK |
1074 | else if (ctl->erase_input_ok) |
1075 | while (ctl->prompt_len-- > 1) | |
5cd45502 | 1076 | fprintf(stderr, "%s %s", ctl->backspace_ch, ctl->backspace_ch); |
d910f311 | 1077 | ctl->prompt_len = 1; |
4eaab145 | 1078 | } |
f16ca88d | 1079 | sp = buf; |
f3c044ee | 1080 | fflush(NULL); |
f16ca88d SK |
1081 | continue; |
1082 | } | |
ba105bb5 SK |
1083 | if (slash && (c == ctl->output_tty.c_cc[VKILL] || |
1084 | c == ctl->output_tty.c_cc[VERASE])) { | |
bd6a253f | 1085 | erase_one_column(ctl); |
f16ca88d SK |
1086 | --sp; |
1087 | } | |
1088 | if (c != '\\') | |
1089 | slash = 0; | |
1090 | *sp++ = c; | |
508b2d79 SK |
1091 | if ((c < ' ' && c != '\n' && c != ESC) || c == CERASE) { |
1092 | c += (c == CERASE) ? -0100 : 0100; | |
bd6a253f | 1093 | fputs(CARAT, stderr); |
d910f311 | 1094 | ctl->prompt_len++; |
f16ca88d SK |
1095 | } |
1096 | if (c != '\n' && c != ESC) { | |
bd6a253f | 1097 | fputc(c, stderr); |
d910f311 | 1098 | ctl->prompt_len++; |
f16ca88d | 1099 | } else |
4eaab145 | 1100 | break; |
f16ca88d SK |
1101 | } |
1102 | *--sp = '\0'; | |
d910f311 SK |
1103 | if (!ctl->erase_line) |
1104 | ctl->prompt_len = maxlen; | |
f16ca88d | 1105 | if (sp - buf >= nmax - 1) |
7cc31c18 | 1106 | more_error(ctl, _("Line too long")); |
f16ca88d | 1107 | } |
4eaab145 | 1108 | |
474c61c0 SK |
1109 | /* Expand shell command line. */ |
1110 | static void expand(struct more_control *ctl, char *inbuf) | |
f16ca88d SK |
1111 | { |
1112 | char *inpstr; | |
1113 | char *outstr; | |
1114 | char c; | |
1115 | char *temp; | |
f16ca88d | 1116 | int tempsz, xtra, offset; |
4eaab145 | 1117 | |
d910f311 | 1118 | xtra = strlen(ctl->file_names[ctl->argv_position]) + strlen(ctl->shell_line) + 1; |
474c61c0 | 1119 | tempsz = COMMAND_BUF + xtra; |
f16ca88d SK |
1120 | temp = xmalloc(tempsz); |
1121 | inpstr = inbuf; | |
1122 | outstr = temp; | |
1123 | while ((c = *inpstr++) != '\0') { | |
1124 | offset = outstr - temp; | |
1125 | if (tempsz - offset - 1 < xtra) { | |
474c61c0 | 1126 | tempsz += COMMAND_BUF + xtra; |
f16ca88d SK |
1127 | temp = xrealloc(temp, tempsz); |
1128 | outstr = temp + offset; | |
1129 | } | |
1130 | switch (c) { | |
1131 | case '%': | |
d910f311 SK |
1132 | if (!ctl->no_tty_in) { |
1133 | strcpy(outstr, ctl->file_names[ctl->argv_position]); | |
1134 | outstr += strlen(ctl->file_names[ctl->argv_position]); | |
f16ca88d SK |
1135 | } else |
1136 | *outstr++ = c; | |
1137 | break; | |
1138 | case '!': | |
474c61c0 SK |
1139 | if (ctl->shell_line) { |
1140 | strcpy(outstr, ctl->shell_line); | |
1141 | outstr += strlen(ctl->shell_line); | |
1142 | } else | |
7cc31c18 | 1143 | more_error(ctl, _ |
f16ca88d | 1144 | ("No previous command to substitute for")); |
f16ca88d SK |
1145 | break; |
1146 | case '\\': | |
1147 | if (*inpstr == '%' || *inpstr == '!') { | |
1148 | *outstr++ = *inpstr++; | |
4eaab145 SK |
1149 | break; |
1150 | } | |
b1557fe9 | 1151 | /* fallthrough */ |
4eaab145 | 1152 | default: |
f16ca88d | 1153 | *outstr++ = c; |
6dbe3af9 | 1154 | } |
6dbe3af9 | 1155 | } |
f16ca88d | 1156 | *outstr++ = '\0'; |
474c61c0 SK |
1157 | free(ctl->shell_line); |
1158 | ctl->shell_line = temp; | |
6dbe3af9 KZ |
1159 | } |
1160 | ||
7cc31c18 | 1161 | static void set_tty(struct more_control *ctl) |
4eaab145 | 1162 | { |
d910f311 SK |
1163 | ctl->output_tty.c_lflag &= ~(ICANON | ECHO); |
1164 | ctl->output_tty.c_cc[VMIN] = 1; /* read at least 1 char */ | |
1165 | ctl->output_tty.c_cc[VTIME] = 0; /* no timeout */ | |
1166 | tcsetattr(STDERR_FILENO, TCSANOW, &ctl->output_tty); | |
f16ca88d SK |
1167 | } |
1168 | ||
82cfa6f2 | 1169 | /* Come here if a quit signal is received */ |
823ef319 | 1170 | static void sigquit_handler(struct more_control *ctl) |
f16ca88d | 1171 | { |
823ef319 SK |
1172 | if (!ctl->dumb_tty && ctl->no_quit_dialog) { |
1173 | ctl->prompt_len += fprintf(stderr, _("[Use q or Q to quit]")); | |
1174 | ctl->no_quit_dialog = 0; | |
1175 | } else | |
1176 | more_exit(ctl); | |
6dbe3af9 KZ |
1177 | } |
1178 | ||
82cfa6f2 | 1179 | /* Come here when we get a suspend signal from the terminal */ |
823ef319 | 1180 | static void sigtstp_handler(struct more_control *ctl) |
6dbe3af9 | 1181 | { |
823ef319 | 1182 | reset_tty(ctl); |
f3c044ee | 1183 | fflush(NULL); |
6dcaaa9e SK |
1184 | kill(getpid(), SIGSTOP); |
1185 | } | |
1186 | ||
1187 | /* Come here when we get a continue signal from the terminal */ | |
1188 | static void sigcont_handler(struct more_control *ctl) | |
1189 | { | |
823ef319 SK |
1190 | set_tty(ctl); |
1191 | } | |
82cfa6f2 | 1192 | |
823ef319 SK |
1193 | /* Come here if a signal for a window size change is received */ |
1194 | static void sigwinch_handler(struct more_control *ctl) | |
1195 | { | |
1196 | struct winsize win; | |
82cfa6f2 | 1197 | |
823ef319 SK |
1198 | if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1) { |
1199 | if (win.ws_row != 0) { | |
1200 | ctl->lines_per_page = win.ws_row; | |
1201 | ctl->d_scroll_len = ctl->lines_per_page / 2 - 1; | |
1202 | if (ctl->d_scroll_len < 1) | |
1203 | ctl->d_scroll_len = 1; | |
1204 | ctl->lines_per_screen = ctl->lines_per_page - 1; | |
1205 | } | |
1206 | if (win.ws_col != 0) | |
1207 | ctl->num_columns = win.ws_col; | |
1208 | } | |
5cd45502 | 1209 | prepare_line_buffer(ctl); |
f16ca88d | 1210 | } |
6dbe3af9 | 1211 | |
7cc31c18 | 1212 | static void execute(struct more_control *ctl, char *filename, char *cmd, ...) |
f16ca88d | 1213 | { |
c366ebc7 | 1214 | pid_t id; |
f16ca88d SK |
1215 | va_list argp; |
1216 | char *arg; | |
1217 | char **args; | |
1218 | int argcount; | |
1219 | ||
f3c044ee | 1220 | fflush(NULL); |
c366ebc7 | 1221 | id = fork(); |
f16ca88d SK |
1222 | if (id == 0) { |
1223 | int errsv; | |
c8b1fa60 SK |
1224 | if (!isatty(STDIN_FILENO)) { |
1225 | close(STDIN_FILENO); | |
f16ca88d SK |
1226 | open("/dev/tty", 0); |
1227 | } | |
437cec38 | 1228 | reset_tty(ctl); |
f16ca88d SK |
1229 | |
1230 | va_start(argp, cmd); | |
1231 | arg = va_arg(argp, char *); | |
1232 | argcount = 0; | |
1233 | while (arg) { | |
1234 | argcount++; | |
1235 | arg = va_arg(argp, char *); | |
1236 | } | |
1237 | va_end(argp); | |
1238 | ||
1239 | args = alloca(sizeof(char *) * (argcount + 1)); | |
1240 | args[argcount] = NULL; | |
1241 | ||
1242 | va_start(argp, cmd); | |
1243 | arg = va_arg(argp, char *); | |
1244 | argcount = 0; | |
1245 | while (arg) { | |
1246 | args[argcount] = arg; | |
1247 | argcount++; | |
1248 | arg = va_arg(argp, char *); | |
6dbe3af9 | 1249 | } |
f16ca88d SK |
1250 | va_end(argp); |
1251 | ||
8fb5fe30 SK |
1252 | if (geteuid() != getuid() || getegid() != getgid()) { |
1253 | if (setuid(getuid()) < 0) | |
1254 | err(EXIT_FAILURE, _("setuid failed")); | |
1255 | if (setgid(getgid()) < 0) | |
1256 | err(EXIT_FAILURE, _("setgid failed")); | |
1257 | } | |
1258 | ||
f16ca88d SK |
1259 | execvp(cmd, args); |
1260 | errsv = errno; | |
bd6a253f | 1261 | fputs(_("exec failed\n"), stderr); |
f16ca88d | 1262 | exit(errsv == ENOENT ? EX_EXEC_ENOENT : EX_EXEC_FAILED); |
6dbe3af9 | 1263 | } |
f16ca88d | 1264 | if (id > 0) { |
c366ebc7 SK |
1265 | errno = 0; |
1266 | while (wait(NULL) > 0) { | |
1267 | if (errno == EINTR) | |
1268 | continue; | |
1269 | } | |
f16ca88d | 1270 | } else |
bd6a253f | 1271 | fputs(_("can't fork\n"), stderr); |
7cc31c18 | 1272 | set_tty(ctl); |
5cd45502 | 1273 | print_separator('-', 24); |
275d47c9 | 1274 | output_prompt(ctl, filename); |
6dbe3af9 KZ |
1275 | } |
1276 | ||
275d47c9 | 1277 | static void run_shell(struct more_control *ctl, char *filename) |
6dbe3af9 | 1278 | { |
bf6db55a | 1279 | char cmdbuf[COMMAND_BUF]; |
6dbe3af9 | 1280 | |
21d09623 | 1281 | erase_to_col(ctl, 0); |
d162fcb5 | 1282 | putchar('!'); |
f3c044ee | 1283 | fflush(NULL); |
1f861935 | 1284 | if (ctl->previous_command.key == more_kc_run_shell && ctl->shell_line) |
bd6a253f | 1285 | fputs(ctl->shell_line, stdout); |
6dbe3af9 | 1286 | else { |
7cc31c18 | 1287 | ttyin(ctl, cmdbuf, sizeof(cmdbuf) - 2, '!'); |
474c61c0 SK |
1288 | if (strpbrk(cmdbuf, "%!\\")) |
1289 | expand(ctl, cmdbuf); | |
1290 | else { | |
1291 | free(ctl->shell_line); | |
1292 | ctl->shell_line = xstrdup(cmdbuf); | |
6dbe3af9 KZ |
1293 | } |
1294 | } | |
bd6a253f | 1295 | fputc('\n', stderr); |
f3c044ee | 1296 | fflush(NULL); |
d910f311 | 1297 | ctl->prompt_len = 0; |
7cc31c18 | 1298 | execute(ctl, filename, ctl->shell, ctl->shell, "-c", ctl->shell_line, 0); |
6dbe3af9 KZ |
1299 | } |
1300 | ||
f16ca88d | 1301 | /* Skip n lines in the file f */ |
e3cb27f5 | 1302 | static void skip_lines(struct more_control *ctl) |
f16ca88d | 1303 | { |
0b735ea4 | 1304 | int c; |
f16ca88d | 1305 | |
e3cb27f5 | 1306 | while (ctl->next_jump > 0) { |
82129a6f | 1307 | while ((c = more_getc(ctl)) != '\n') |
f16ca88d SK |
1308 | if (c == EOF) |
1309 | return; | |
e3cb27f5 | 1310 | ctl->next_jump--; |
d910f311 | 1311 | ctl->current_line++; |
f16ca88d SK |
1312 | } |
1313 | } | |
1314 | ||
1315 | /* Clear the screen */ | |
275d47c9 | 1316 | static void more_clear_screen(struct more_control *ctl) |
f16ca88d | 1317 | { |
d910f311 SK |
1318 | if (ctl->clear && !ctl->hard_tty) { |
1319 | putp(ctl->clear); | |
f16ca88d SK |
1320 | /* Put out carriage return so that system doesn't get |
1321 | * confused by escape sequences when expanding tabs */ | |
1322 | putchar('\r'); | |
d910f311 | 1323 | ctl->prompt_len = 0; |
f16ca88d SK |
1324 | } |
1325 | } | |
1326 | ||
82129a6f | 1327 | static void read_line(struct more_control *ctl) |
f16ca88d | 1328 | { |
0b735ea4 SK |
1329 | int c; |
1330 | char *p; | |
f16ca88d | 1331 | |
d910f311 | 1332 | p = ctl->line_buf; |
82129a6f | 1333 | while ((c = more_getc(ctl)) != '\n' && c != EOF |
ba105bb5 | 1334 | && (ptrdiff_t)p != (ptrdiff_t)(ctl->line_buf + ctl->line_sz - 1)) |
f16ca88d SK |
1335 | *p++ = c; |
1336 | if (c == '\n') | |
d910f311 | 1337 | ctl->current_line++; |
f16ca88d SK |
1338 | *p = '\0'; |
1339 | } | |
1340 | ||
823ef319 SK |
1341 | static int more_poll(struct more_control *ctl, int timeout) |
1342 | { | |
1343 | struct pollfd pfd[2]; | |
1344 | ||
1345 | pfd[0].fd = ctl->sigfd; | |
1346 | pfd[0].events = POLLIN | POLLERR | POLLHUP; | |
1347 | pfd[1].fd = STDIN_FILENO; | |
1348 | pfd[1].events = POLLIN; | |
1349 | ||
1350 | if (poll(pfd, 2, timeout) < 0) { | |
1351 | if (errno == EAGAIN) | |
1352 | return 1; | |
1353 | more_error(ctl, _("poll failed")); | |
1354 | return 1; | |
1355 | } | |
1356 | if (pfd[0].revents != 0) { | |
1357 | struct signalfd_siginfo info; | |
1358 | ssize_t sz; | |
1359 | ||
1360 | sz = read(pfd[0].fd, &info, sizeof(info)); | |
1361 | assert(sz == sizeof(info)); | |
1362 | switch (info.ssi_signo) { | |
1363 | case SIGINT: | |
1364 | more_exit(ctl); | |
1365 | break; | |
1366 | case SIGQUIT: | |
1367 | sigquit_handler(ctl); | |
1368 | break; | |
1369 | case SIGTSTP: | |
1370 | sigtstp_handler(ctl); | |
1371 | break; | |
6dcaaa9e SK |
1372 | case SIGCONT: |
1373 | sigcont_handler(ctl); | |
1374 | break; | |
823ef319 SK |
1375 | case SIGWINCH: |
1376 | sigwinch_handler(ctl); | |
1377 | break; | |
1378 | default: | |
1379 | abort(); | |
1380 | } | |
1381 | } | |
1382 | if (pfd[1].revents == 0) | |
1383 | return 1; | |
1384 | return 0; | |
1385 | } | |
1386 | ||
4eaab145 SK |
1387 | /* Search for nth occurrence of regular expression contained in buf in |
1388 | * the file */ | |
82129a6f | 1389 | static void search(struct more_control *ctl, char buf[], int n) |
6dbe3af9 | 1390 | { |
ba105bb5 SK |
1391 | off_t startline = ctl->file_position; |
1392 | off_t line1 = startline; | |
1393 | off_t line2 = startline; | |
1394 | off_t line3; | |
0b735ea4 | 1395 | int lncount; |
cdd2cf46 | 1396 | int saveln, rc; |
4eaab145 SK |
1397 | regex_t re; |
1398 | ||
e3cb27f5 SK |
1399 | if (buf != ctl->previous_search) { |
1400 | free(ctl->previous_search); | |
1401 | ctl->previous_search = buf; | |
1402 | } | |
1403 | ||
1404 | ctl->search_called = 1; | |
d910f311 SK |
1405 | ctl->context.line_num = saveln = ctl->current_line; |
1406 | ctl->context.row_num = startline; | |
4eaab145 | 1407 | lncount = 0; |
bc1ed338 SK |
1408 | if (!buf) |
1409 | goto notfound; | |
4eaab145 SK |
1410 | if ((rc = regcomp(&re, buf, REG_NOSUB)) != 0) { |
1411 | char s[REGERR_BUF]; | |
1412 | regerror(rc, &re, s, sizeof s); | |
7cc31c18 | 1413 | more_error(ctl, s); |
823ef319 | 1414 | return; |
4eaab145 | 1415 | } |
82129a6f | 1416 | while (!feof(ctl->current_file)) { |
4eaab145 SK |
1417 | line3 = line2; |
1418 | line2 = line1; | |
d910f311 | 1419 | line1 = ctl->file_position; |
82129a6f | 1420 | read_line(ctl); |
4eaab145 | 1421 | lncount++; |
5cd45502 SK |
1422 | if (regexec(&re, ctl->line_buf, 0, NULL, 0) == 0 && --n == 0) { |
1423 | if ((1 < lncount && ctl->no_tty_in) || 3 < lncount) { | |
1424 | putchar('\n'); | |
1425 | if (ctl->clear_line_ends) | |
1426 | putp(ctl->erase_line); | |
1427 | fputs(_("...skipping\n"), stdout); | |
1428 | } | |
1429 | if (!ctl->no_tty_in) { | |
1430 | ctl->current_line -= (lncount < 3 ? lncount : 3); | |
1431 | more_fseek(ctl, line3); | |
1432 | if (ctl->no_scroll) { | |
1433 | if (ctl->clear_line_ends) { | |
1434 | putp(ctl->go_home); | |
d910f311 | 1435 | putp(ctl->erase_line); |
5cd45502 SK |
1436 | } else |
1437 | more_clear_screen(ctl); | |
4eaab145 | 1438 | } |
5cd45502 | 1439 | } else { |
21d09623 | 1440 | erase_to_col(ctl, 0); |
5cd45502 SK |
1441 | if (ctl->no_scroll) { |
1442 | if (ctl->clear_line_ends) { | |
1443 | putp(ctl->go_home); | |
1444 | putp(ctl->erase_line); | |
1445 | } else | |
1446 | more_clear_screen(ctl); | |
4eaab145 | 1447 | } |
5cd45502 | 1448 | puts(ctl->line_buf); |
5c36a0eb | 1449 | } |
5cd45502 | 1450 | break; |
042f62df RP |
1451 | } |
1452 | more_poll(ctl, 1); | |
7f84e444 | 1453 | } |
823ef319 SK |
1454 | /* Move ctrl+c signal handling back to more_key_command(). */ |
1455 | signal(SIGINT, SIG_DFL); | |
1456 | sigaddset(&ctl->sigset, SIGINT); | |
1457 | sigprocmask(SIG_BLOCK, &ctl->sigset, NULL); | |
53078e08 | 1458 | regfree(&re); |
82129a6f | 1459 | if (feof(ctl->current_file)) { |
d910f311 SK |
1460 | if (!ctl->no_tty_in) { |
1461 | ctl->current_line = saveln; | |
82129a6f | 1462 | more_fseek(ctl, startline); |
4eaab145 | 1463 | } else { |
bd6a253f | 1464 | fputs(_("\nPattern not found\n"), stdout); |
823ef319 | 1465 | more_exit(ctl); |
4eaab145 | 1466 | } |
bc1ed338 | 1467 | notfound: |
7cc31c18 | 1468 | more_error(ctl, _("Pattern not found")); |
6dbe3af9 | 1469 | } |
6dbe3af9 KZ |
1470 | } |
1471 | ||
f8492042 SK |
1472 | static char *find_editor(void) |
1473 | { | |
1474 | static char *editor; | |
1475 | ||
1476 | editor = getenv("VISUAL"); | |
1477 | if (editor == NULL || *editor == '\0') | |
1478 | editor = getenv("EDITOR"); | |
1479 | if (editor == NULL || *editor == '\0') | |
1480 | editor = _PATH_VI; | |
1481 | return editor; | |
1482 | } | |
1483 | ||
b6818219 SK |
1484 | static void runtime_usage(void) |
1485 | { | |
1486 | fputs(_("Most commands optionally preceded by integer argument k. " | |
1487 | "Defaults in brackets.\n" | |
1488 | "Star (*) indicates argument becomes new default.\n"), stdout); | |
5cd45502 | 1489 | print_separator('-', 79); |
b6818219 SK |
1490 | fprintf(stdout, |
1491 | _ | |
1492 | ("<space> Display next k lines of text [current screen size]\n" | |
1493 | "z Display next k lines of text [current screen size]*\n" | |
1494 | "<return> Display next k lines of text [1]*\n" | |
1495 | "d or ctrl-D Scroll k lines [current scroll size, initially 11]*\n" | |
1496 | "q or Q or <interrupt> Exit from more\n" | |
1497 | "s Skip forward k lines of text [1]\n" | |
1498 | "f Skip forward k screenfuls of text [1]\n" | |
1499 | "b or ctrl-B Skip backwards k screenfuls of text [1]\n" | |
1500 | "' Go to place where previous search started\n" | |
1501 | "= Display current line number\n" | |
1502 | "/<regular expression> Search for kth occurrence of regular expression [1]\n" | |
1503 | "n Search for kth occurrence of last r.e [1]\n" | |
1504 | "!<cmd> or :!<cmd> Execute <cmd> in a subshell\n" | |
1505 | "v Start up '%s' at current line\n" | |
1506 | "ctrl-L Redraw screen\n" | |
1507 | ":n Go to kth next file [1]\n" | |
1508 | ":p Go to kth previous file [1]\n" | |
1509 | ":f Display current file name and line number\n" | |
1510 | ". Repeat previous command\n"), | |
1511 | find_editor()); | |
5cd45502 | 1512 | print_separator('-', 79); |
b6818219 SK |
1513 | } |
1514 | ||
1f5d21ec SK |
1515 | static void execute_editor(struct more_control *ctl, char *cmdbuf, char *filename) |
1516 | { | |
1517 | char *editor, *p; | |
1f5d21ec | 1518 | int split = 0; |
5cd45502 | 1519 | int n; |
1f5d21ec | 1520 | |
5cd45502 SK |
1521 | if ((ctl->current_line - ctl->lines_per_screen) < 1) |
1522 | n = 1; | |
1523 | else | |
1524 | n = ctl->current_line - (ctl->lines_per_screen + 1) / 2; | |
1f5d21ec SK |
1525 | editor = find_editor(); |
1526 | p = strrchr(editor, '/'); | |
1527 | if (p) | |
1528 | p++; | |
1529 | else | |
1530 | p = editor; | |
1531 | /* | |
1532 | * Earlier: call vi +n file. This also works for emacs. | |
1533 | * POSIX: call vi -c n file (when editor is vi or ex). | |
1534 | */ | |
1535 | if (!strcmp(p, "vi") || !strcmp(p, "ex")) { | |
1536 | sprintf(cmdbuf, "-c %d", n); | |
1537 | split = 1; | |
1538 | } else | |
1539 | sprintf(cmdbuf, "+%d", n); | |
1540 | ||
21d09623 | 1541 | erase_to_col(ctl, 0); |
1f5d21ec SK |
1542 | printf("%s %s %s", editor, cmdbuf, ctl->file_names[ctl->argv_position]); |
1543 | if (split) { | |
1544 | cmdbuf[2] = 0; | |
1545 | execute(ctl, filename, editor, editor, | |
1546 | cmdbuf, cmdbuf + 3, | |
1547 | ctl->file_names[ctl->argv_position], (char *)0); | |
1548 | } else | |
1549 | execute(ctl, filename, editor, editor, | |
1550 | cmdbuf, ctl->file_names[ctl->argv_position], (char *)0); | |
1551 | } | |
1552 | ||
82129a6f | 1553 | static int skip_backwards(struct more_control *ctl, int nlines) |
1f5d21ec | 1554 | { |
1f5d21ec SK |
1555 | if (nlines == 0) |
1556 | nlines++; | |
21d09623 | 1557 | erase_to_col(ctl, 0); |
1f5d21ec | 1558 | printf(P_("...back %d page", "...back %d pages", nlines), nlines); |
1f5d21ec | 1559 | putchar('\n'); |
837f49c6 | 1560 | ctl->next_jump = ctl->current_line - (ctl->lines_per_screen * (nlines + 1)) - 1; |
e3cb27f5 SK |
1561 | if (ctl->next_jump < 0) |
1562 | ctl->next_jump = 0; | |
ba105bb5 | 1563 | more_fseek(ctl, 0); |
837f49c6 | 1564 | ctl->current_line = 0; |
e3cb27f5 | 1565 | skip_lines(ctl); |
837f49c6 | 1566 | return ctl->lines_per_screen; |
1f5d21ec SK |
1567 | } |
1568 | ||
ba105bb5 | 1569 | static int skip_forwards(struct more_control *ctl, int nlines, cc_t comchar) |
1f5d21ec SK |
1570 | { |
1571 | int c; | |
1572 | ||
1573 | if (nlines == 0) | |
1574 | nlines++; | |
1575 | if (comchar == 'f') | |
1576 | nlines *= ctl->lines_per_screen; | |
1577 | putchar('\r'); | |
21d09623 | 1578 | erase_to_col(ctl, 0); |
1f5d21ec SK |
1579 | putchar('\n'); |
1580 | if (ctl->clear_line_ends) | |
1581 | putp(ctl->erase_line); | |
1582 | printf(P_("...skipping %d line", | |
1583 | "...skipping %d lines", nlines), nlines); | |
1584 | ||
1585 | if (ctl->clear_line_ends) | |
1586 | putp(ctl->erase_line); | |
1587 | putchar('\n'); | |
1588 | ||
1589 | while (nlines > 0) { | |
82129a6f | 1590 | while ((c = more_getc(ctl)) != '\n') |
1f5d21ec SK |
1591 | if (c == EOF) |
1592 | return 0; | |
1593 | ctl->current_line++; | |
1594 | nlines--; | |
1595 | } | |
1596 | return 1; | |
1597 | } | |
1598 | ||
f16ca88d SK |
1599 | /* Read a command and do it. A command consists of an optional integer |
1600 | * argument followed by the command character. Return the number of | |
1601 | * lines to display in the next screenful. If there is nothing more to | |
1602 | * display in the current file, zero is returned. */ | |
82129a6f | 1603 | static int more_key_command(struct more_control *ctl, char *filename) |
6dbe3af9 | 1604 | { |
0b735ea4 | 1605 | int retval = 0; |
1f861935 | 1606 | int done = 0, search_again = 0; |
ba105bb5 | 1607 | char cmdbuf[INIT_BUF]; |
1f861935 SK |
1608 | struct number_command cmd; |
1609 | ||
d910f311 | 1610 | if (!ctl->report_errors) |
275d47c9 | 1611 | output_prompt(ctl, filename); |
f16ca88d | 1612 | else |
d910f311 | 1613 | ctl->report_errors = 0; |
823ef319 | 1614 | ctl->search_called = 0; |
f16ca88d | 1615 | for (;;) { |
823ef319 SK |
1616 | if (more_poll(ctl, -1) != 0) |
1617 | continue; | |
1f861935 SK |
1618 | cmd = read_command(ctl); |
1619 | if (cmd.key == more_kc_unknown_command) | |
f16ca88d | 1620 | continue; |
1f861935 SK |
1621 | if (cmd.key == more_kc_repeat_previous) |
1622 | cmd = ctl->previous_command; | |
1623 | switch (cmd.key) { | |
1624 | case more_kc_backwards: | |
1f5d21ec | 1625 | if (ctl->no_tty_in) { |
508b2d79 | 1626 | fprintf(stderr, "\a"); |
1f5d21ec | 1627 | return -1; |
f16ca88d | 1628 | } |
1f861935 | 1629 | retval = skip_backwards(ctl, cmd.number); |
1f5d21ec SK |
1630 | done = 1; |
1631 | break; | |
1f861935 SK |
1632 | case more_kc_jump_lines_per_screen: |
1633 | case more_kc_set_lines_per_screen: | |
1634 | if (cmd.number == 0) | |
1635 | cmd.number = ctl->lines_per_screen; | |
1636 | else if (cmd.key == more_kc_set_lines_per_screen) | |
1637 | ctl->lines_per_screen = cmd.number; | |
1638 | retval = cmd.number; | |
bd6a253f SK |
1639 | done = 1; |
1640 | break; | |
1f861935 SK |
1641 | case more_kc_set_scroll_len: |
1642 | if (cmd.number != 0) | |
1643 | ctl->d_scroll_len = cmd.number; | |
d910f311 | 1644 | retval = ctl->d_scroll_len; |
bd6a253f SK |
1645 | done = 1; |
1646 | break; | |
1f861935 | 1647 | case more_kc_quit: |
823ef319 | 1648 | more_exit(ctl); |
1f861935 SK |
1649 | case more_kc_skip_forward: |
1650 | if (skip_forwards(ctl, cmd.number, cmd.number)) | |
1f5d21ec | 1651 | retval = ctl->lines_per_screen; |
bd6a253f SK |
1652 | done = 1; |
1653 | break; | |
1f861935 SK |
1654 | case more_kc_next_line: |
1655 | if (cmd.number != 0) | |
1656 | ctl->lines_per_screen = cmd.number; | |
f16ca88d | 1657 | else |
1f861935 SK |
1658 | cmd.number = 1; |
1659 | retval = cmd.number; | |
bd6a253f SK |
1660 | done = 1; |
1661 | break; | |
1f861935 | 1662 | case more_kc_clear_screen: |
d910f311 | 1663 | if (!ctl->no_tty_in) { |
275d47c9 | 1664 | more_clear_screen(ctl); |
82129a6f | 1665 | more_fseek(ctl, ctl->screen_start.row_num); |
d910f311 SK |
1666 | ctl->current_line = ctl->screen_start.line_num; |
1667 | retval = ctl->lines_per_screen; | |
bd6a253f SK |
1668 | done = 1; |
1669 | break; | |
4eaab145 | 1670 | } else { |
508b2d79 | 1671 | fprintf(stderr, "\a"); |
f16ca88d | 1672 | break; |
4eaab145 | 1673 | } |
1f861935 | 1674 | case more_kc_previous_search_match: |
d910f311 | 1675 | if (!ctl->no_tty_in) { |
21d09623 | 1676 | erase_to_col(ctl, 0); |
bd6a253f | 1677 | fputs(_("\n***Back***\n\n"), stdout); |
82129a6f | 1678 | more_fseek(ctl, ctl->context.row_num); |
d910f311 SK |
1679 | ctl->current_line = ctl->context.line_num; |
1680 | retval = ctl->lines_per_screen; | |
bd6a253f SK |
1681 | done = 1; |
1682 | break; | |
f16ca88d | 1683 | } else { |
508b2d79 | 1684 | fprintf(stderr, "\a"); |
f16ca88d | 1685 | break; |
4eaab145 | 1686 | } |
1f861935 | 1687 | case more_kc_display_line: |
21d09623 | 1688 | erase_to_col(ctl, 0); |
d910f311 | 1689 | ctl->prompt_len = printf("%d", ctl->current_line); |
f3c044ee | 1690 | fflush(NULL); |
f16ca88d | 1691 | break; |
1f861935 SK |
1692 | case more_kc_display_file_and_line: |
1693 | erase_to_col(ctl, 0); | |
1694 | if (!ctl->no_tty_in) | |
1695 | ctl->prompt_len = | |
1696 | printf(_("\"%s\" line %d"), | |
1697 | ctl->file_names[ctl->argv_position], ctl->current_line); | |
1698 | else | |
1699 | ctl->prompt_len = printf(_("[Not a file] line %d"), | |
1700 | ctl->current_line); | |
1701 | fflush(NULL); | |
1702 | break; | |
1703 | case more_kc_repeat_search: | |
d910f311 | 1704 | if (!ctl->previous_search) { |
7cc31c18 | 1705 | more_error(ctl, _("No previous regular expression")); |
f16ca88d SK |
1706 | break; |
1707 | } | |
1f861935 | 1708 | search_again = 1; |
f16ca88d | 1709 | /* fallthrough */ |
1f861935 SK |
1710 | case more_kc_search: |
1711 | if (cmd.number == 0) | |
1712 | cmd.number++; | |
21d09623 | 1713 | erase_to_col(ctl, 0); |
f16ca88d | 1714 | putchar('/'); |
d910f311 | 1715 | ctl->prompt_len = 1; |
f3c044ee | 1716 | fflush(NULL); |
1f861935 | 1717 | if (search_again) { |
bd6a253f | 1718 | fputc('\r', stderr); |
1f861935 SK |
1719 | search(ctl, ctl->previous_search, cmd.number); |
1720 | search_again = 0; | |
4eaab145 | 1721 | } else { |
7cc31c18 | 1722 | ttyin(ctl, cmdbuf, sizeof(cmdbuf) - 2, '/'); |
bd6a253f | 1723 | fputc('\r', stderr); |
e3cb27f5 | 1724 | ctl->next_search = xstrdup(cmdbuf); |
1f861935 | 1725 | search(ctl, ctl->next_search, cmd.number); |
4eaab145 | 1726 | } |
d910f311 | 1727 | retval = ctl->lines_per_screen - 1; |
bd6a253f SK |
1728 | done = 1; |
1729 | break; | |
1f861935 | 1730 | case more_kc_run_shell: |
275d47c9 | 1731 | run_shell(ctl, filename); |
f16ca88d | 1732 | break; |
1f861935 | 1733 | case more_kc_help: |
d910f311 | 1734 | if (ctl->no_scroll) |
275d47c9 | 1735 | more_clear_screen(ctl); |
21d09623 | 1736 | erase_to_col(ctl, 0); |
b6818219 | 1737 | runtime_usage(); |
275d47c9 | 1738 | output_prompt(ctl, filename); |
f16ca88d | 1739 | break; |
1f861935 SK |
1740 | case more_kc_next_file: |
1741 | putchar('\r'); | |
1742 | erase_to_col(ctl, 0); | |
1743 | if (cmd.number == 0) | |
1744 | cmd.number = 1; | |
1745 | if (ctl->argv_position + cmd.number >= (unsigned int)ctl->num_files) | |
1746 | more_exit(ctl); | |
1747 | change_file(ctl, cmd.number); | |
1748 | done = 1; | |
1749 | break; | |
1750 | case more_kc_previous_file: | |
1751 | if (ctl->no_tty_in) { | |
1752 | fprintf(stderr, "\a"); | |
1753 | break; | |
1754 | } | |
1755 | putchar('\r'); | |
1756 | erase_to_col(ctl, 0); | |
1757 | if (cmd.number == 0) | |
1758 | cmd.number = 1; | |
1759 | change_file(ctl, -cmd.number); | |
1760 | done = 1; | |
1761 | break; | |
1762 | case more_kc_run_editor: /* This case should go right before default */ | |
d910f311 | 1763 | if (!ctl->no_tty_in) { |
1f5d21ec | 1764 | execute_editor(ctl, cmdbuf, filename); |
f16ca88d SK |
1765 | break; |
1766 | } | |
1767 | /* fallthrough */ | |
1768 | default: | |
d910f311 | 1769 | if (ctl->suppress_bell) { |
21d09623 | 1770 | erase_to_col(ctl, 0); |
5cd45502 | 1771 | if (ctl->enter_std) |
d910f311 | 1772 | putp(ctl->enter_std); |
5cd45502 SK |
1773 | ctl->prompt_len = |
1774 | printf(_("[Press 'h' for instructions.]")) | |
d910f311 | 1775 | + 2 * ctl->stdout_glitch; |
5cd45502 | 1776 | if (ctl->exit_std) |
d910f311 | 1777 | putp(ctl->exit_std); |
f16ca88d | 1778 | } else |
508b2d79 | 1779 | fprintf(stderr, "\a"); |
f3c044ee | 1780 | fflush(NULL); |
f16ca88d | 1781 | break; |
4eaab145 | 1782 | } |
1f861935 SK |
1783 | ctl->previous_command = cmd; |
1784 | if (done) { | |
1785 | cmd.key = more_kc_unknown_command; | |
f16ca88d | 1786 | break; |
1f861935 | 1787 | } |
5c36a0eb | 1788 | } |
f16ca88d | 1789 | putchar('\r'); |
d910f311 | 1790 | ctl->no_quit_dialog = 1; |
f728d8ba | 1791 | return retval; |
6dbe3af9 KZ |
1792 | } |
1793 | ||
f16ca88d | 1794 | /* Print out the contents of the file f, one screenful at a time. */ |
82129a6f | 1795 | static void screen(struct more_control *ctl, int num_lines) |
4eaab145 | 1796 | { |
0b735ea4 SK |
1797 | int c; |
1798 | int nchars; | |
f16ca88d SK |
1799 | int length; /* length of current line */ |
1800 | static int prev_len = 1; /* length of previous line */ | |
4eaab145 | 1801 | |
f16ca88d | 1802 | for (;;) { |
d910f311 | 1803 | while (num_lines > 0 && !ctl->is_paused) { |
82129a6f | 1804 | if ((nchars = get_line(ctl, &length)) == EOF) { |
d910f311 SK |
1805 | if (ctl->clear_line_ends) |
1806 | putp(ctl->clear_rest); | |
f16ca88d | 1807 | return; |
4eaab145 | 1808 | } |
d910f311 | 1809 | if (ctl->squeeze_spaces && length == 0 && prev_len == 0) |
f16ca88d SK |
1810 | continue; |
1811 | prev_len = length; | |
d910f311 SK |
1812 | if (ctl->bad_stdout |
1813 | || ((ctl->enter_std && *ctl->enter_std == ' ') && (ctl->prompt_len > 0))) | |
21d09623 | 1814 | erase_to_col(ctl, 0); |
f16ca88d SK |
1815 | /* must clear before drawing line since tabs on |
1816 | * some terminals do not erase what they tab | |
1817 | * over. */ | |
d910f311 SK |
1818 | if (ctl->clear_line_ends) |
1819 | putp(ctl->erase_line); | |
94bece3c | 1820 | fwrite(ctl->line_buf, length, 1, stdout); |
d910f311 | 1821 | if (nchars < ctl->prompt_len) |
21d09623 SK |
1822 | erase_to_col(ctl, nchars); |
1823 | ctl->prompt_len = 0; | |
d910f311 | 1824 | if (nchars < ctl->num_columns || !ctl->fold_long_lines) |
94bece3c | 1825 | putchar('\n'); |
f16ca88d | 1826 | num_lines--; |
6dbe3af9 | 1827 | } |
f3c044ee | 1828 | fflush(NULL); |
82129a6f | 1829 | if ((c = more_getc(ctl)) == EOF) { |
d910f311 SK |
1830 | if (ctl->clear_line_ends) |
1831 | putp(ctl->clear_rest); | |
f16ca88d | 1832 | return; |
4eaab145 | 1833 | } |
f16ca88d | 1834 | |
d910f311 SK |
1835 | if (ctl->is_paused && ctl->clear_line_ends) |
1836 | putp(ctl->clear_rest); | |
82129a6f | 1837 | more_ungetc(ctl, c); |
d910f311 | 1838 | ctl->is_paused = 0; |
823ef319 | 1839 | do { |
82129a6f | 1840 | if ((num_lines = more_key_command(ctl, NULL)) == 0) |
823ef319 SK |
1841 | return; |
1842 | } while (ctl->search_called && !ctl->previous_search); | |
d910f311 | 1843 | if (ctl->hard_tty && ctl->prompt_len > 0) |
21d09623 | 1844 | erase_to_col(ctl, 0); |
d910f311 SK |
1845 | if (ctl->no_scroll && num_lines >= ctl->lines_per_screen) { |
1846 | if (ctl->clear_line_ends) | |
1847 | putp(ctl->go_home); | |
f16ca88d | 1848 | else |
275d47c9 | 1849 | more_clear_screen(ctl); |
f16ca88d | 1850 | } |
d910f311 SK |
1851 | ctl->screen_start.line_num = ctl->current_line; |
1852 | ctl->screen_start.row_num = ctl->file_position; | |
6dbe3af9 | 1853 | } |
6dbe3af9 KZ |
1854 | } |
1855 | ||
0b735ea4 | 1856 | static void copy_file(FILE *f) |
4eaab145 | 1857 | { |
f16ca88d SK |
1858 | char buf[BUFSIZ]; |
1859 | size_t sz; | |
1860 | ||
1861 | while ((sz = fread(&buf, sizeof(char), sizeof(buf), f)) > 0) | |
1862 | fwrite(&buf, sizeof(char), sz, stdout); | |
6dbe3af9 KZ |
1863 | } |
1864 | ||
63f96f68 | 1865 | |
e3cb27f5 | 1866 | static void display_file(struct more_control *ctl, int left) |
63f96f68 SK |
1867 | { |
1868 | if (!ctl->current_file) | |
1869 | return; | |
1870 | ctl->context.line_num = ctl->context.row_num = 0; | |
1871 | ctl->current_line = 0; | |
1872 | if (ctl->first_file) { | |
1873 | ctl->first_file = 0; | |
e3cb27f5 SK |
1874 | if (ctl->next_jump) |
1875 | skip_lines(ctl); | |
63f96f68 | 1876 | if (ctl->search_at_start) { |
e3cb27f5 | 1877 | search(ctl, ctl->next_search, 1); |
63f96f68 SK |
1878 | if (ctl->no_scroll) |
1879 | left--; | |
e3cb27f5 | 1880 | } |
63f96f68 SK |
1881 | } else if (ctl->argv_position < ctl->num_files && !ctl->no_tty_out) |
1882 | left = | |
1883 | more_key_command(ctl, ctl->file_names[ctl->argv_position]); | |
1884 | if (left != 0) { | |
1885 | if ((ctl->no_scroll || ctl->clear_first) | |
ba105bb5 | 1886 | && ctl->file_size != ~((off_t)0)) { |
63f96f68 SK |
1887 | if (ctl->clear_line_ends) |
1888 | putp(ctl->go_home); | |
1889 | else | |
1890 | more_clear_screen(ctl); | |
1891 | } | |
1892 | if (ctl->print_banner) { | |
1893 | if (ctl->bad_stdout) | |
21d09623 | 1894 | erase_to_col(ctl, 0); |
63f96f68 SK |
1895 | if (ctl->clear_line_ends) |
1896 | putp(ctl->erase_line); | |
63f96f68 | 1897 | if (ctl->prompt_len > 14) |
21d09623 | 1898 | erase_to_col(ctl, 14); |
63f96f68 SK |
1899 | if (ctl->clear_line_ends) |
1900 | putp(ctl->erase_line); | |
5cd45502 | 1901 | print_separator(':', 14); |
63f96f68 SK |
1902 | puts(ctl->file_names[ctl->argv_position]); |
1903 | if (ctl->clear_line_ends) | |
1904 | putp(ctl->erase_line); | |
5cd45502 | 1905 | print_separator(':', 14); |
63f96f68 SK |
1906 | if (left > ctl->lines_per_page - 4) |
1907 | left = ctl->lines_per_page - 4; | |
1908 | } | |
1909 | if (ctl->no_tty_out) | |
1910 | copy_file(ctl->current_file); | |
1911 | else | |
1912 | screen(ctl, left); | |
1913 | } | |
f3c044ee | 1914 | fflush(NULL); |
63f96f68 SK |
1915 | fclose(ctl->current_file); |
1916 | ctl->current_file = NULL; | |
ba105bb5 | 1917 | ctl->screen_start.line_num = ctl->screen_start.row_num = 0; |
63f96f68 SK |
1918 | ctl->context.line_num = ctl->context.row_num = 0L; |
1919 | } | |
1920 | ||
7cc31c18 | 1921 | static void initterm(struct more_control *ctl) |
f16ca88d SK |
1922 | { |
1923 | int ret; | |
f16ca88d SK |
1924 | char *term; |
1925 | struct winsize win; | |
22ff9a38 | 1926 | char *cursor_addr; |
f16ca88d | 1927 | |
f16ca88d | 1928 | #ifndef NON_INTERACTIVE_MORE |
d910f311 | 1929 | ctl->no_tty_out = tcgetattr(STDOUT_FILENO, &ctl->output_tty); |
f16ca88d | 1930 | #endif |
d910f311 SK |
1931 | ctl->no_tty_in = tcgetattr(STDIN_FILENO, &ctl->output_tty); |
1932 | tcgetattr(STDERR_FILENO, &ctl->output_tty); | |
1933 | ctl->original_tty = ctl->output_tty; | |
1934 | ctl->hard_tabs = (ctl->output_tty.c_oflag & TABDLY) != TAB3; | |
22ff9a38 SK |
1935 | if (ctl->no_tty_out) |
1936 | return; | |
1937 | ||
1938 | ctl->output_tty.c_lflag &= ~(ICANON | ECHO); | |
1939 | ctl->output_tty.c_cc[VMIN] = 1; | |
1940 | ctl->output_tty.c_cc[VTIME] = 0; | |
1941 | ctl->erase_previous_ok = (ctl->output_tty.c_cc[VERASE] != 255); | |
1942 | ctl->erase_input_ok = (ctl->output_tty.c_cc[VKILL] != 255); | |
1943 | if ((term = getenv("TERM")) == NULL) { | |
1944 | ctl->dumb_tty = 1; | |
22ff9a38 SK |
1945 | } |
1946 | setupterm(term, 1, &ret); | |
1947 | if (ret <= 0) { | |
1948 | ctl->dumb_tty = 1; | |
22ff9a38 SK |
1949 | return; |
1950 | } | |
1951 | if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) < 0) { | |
1952 | ctl->lines_per_page = tigetnum(TERM_LINES); | |
1953 | ctl->num_columns = tigetnum(TERM_COLS); | |
1954 | } else { | |
1955 | if ((ctl->lines_per_page = win.ws_row) == 0) | |
1956 | ctl->lines_per_page = tigetnum(TERM_LINES); | |
1957 | if ((ctl->num_columns = win.ws_col) == 0) | |
1958 | ctl->num_columns = tigetnum(TERM_COLS); | |
1959 | } | |
1960 | if ((ctl->lines_per_page <= 0) || tigetflag(TERM_HARD_COPY)) { | |
1961 | ctl->hard_tty = 1; | |
1962 | ctl->lines_per_page = LINES_PER_PAGE; | |
4eaab145 | 1963 | } |
22ff9a38 SK |
1964 | |
1965 | if (tigetflag(TERM_EAT_NEW_LINE)) | |
1966 | /* Eat newline at last column + 1; dec, concept */ | |
1967 | ctl->eat_newline++; | |
1968 | if (ctl->num_columns <= 0) | |
1969 | ctl->num_columns = NUM_COLUMNS; | |
1970 | ||
1971 | ctl->wrap_margin = tigetflag(TERM_AUTO_RIGHT_MARGIN); | |
1972 | ctl->bad_stdout = tigetflag(TERM_CEOL); | |
1973 | ctl->erase_line = tigetstr(TERM_CLEAR_TO_LINE_END); | |
1974 | ctl->clear = tigetstr(TERM_CLEAR); | |
5cd45502 SK |
1975 | if ((ctl->enter_std = tigetstr(TERM_STANDARD_MODE)) != NULL) { |
1976 | ctl->exit_std = tigetstr(TERM_EXIT_STANDARD_MODE); | |
1977 | if (0 < tigetnum(TERM_STD_MODE_GLITCH)) | |
1978 | ctl->stdout_glitch = 1; | |
1979 | } | |
22ff9a38 | 1980 | |
22ff9a38 SK |
1981 | cursor_addr = tigetstr(TERM_HOME); |
1982 | if (cursor_addr == NULL || *cursor_addr == '\0') { | |
1983 | cursor_addr = tigetstr(TERM_CURSOR_ADDRESS); | |
1984 | if (cursor_addr) | |
1985 | cursor_addr = tparm(cursor_addr, 0, 0); | |
1986 | } | |
1987 | if (cursor_addr) | |
1988 | ctl->go_home = xstrdup(cursor_addr); | |
1989 | ||
5cd45502 SK |
1990 | if ((ctl->move_line_down = tigetstr(TERM_LINE_DOWN)) == NULL) |
1991 | ctl->move_line_down = BACKSPACE; | |
22ff9a38 | 1992 | ctl->clear_rest = tigetstr(TERM_CLEAR_TO_SCREEN_END); |
5cd45502 SK |
1993 | if ((ctl->backspace_ch = tigetstr(TERM_BACKSPACE)) == NULL) |
1994 | ctl->backspace_ch = BACKSPACE; | |
22ff9a38 SK |
1995 | |
1996 | if ((ctl->shell = getenv("SHELL")) == NULL) | |
1997 | ctl->shell = _PATH_BSHELL; | |
6dbe3af9 KZ |
1998 | } |
1999 | ||
f16ca88d SK |
2000 | int main(int argc, char **argv) |
2001 | { | |
f16ca88d | 2002 | char *s; |
f16ca88d | 2003 | int left; |
7cc31c18 | 2004 | struct more_control ctl = { |
d910f311 SK |
2005 | .first_file = 1, |
2006 | .fold_long_lines = 1, | |
2007 | .no_quit_dialog = 1, | |
d910f311 | 2008 | .stop_after_formfeed = 1, |
d910f311 SK |
2009 | .wrap_margin = 1, |
2010 | .lines_per_page = LINES_PER_PAGE, | |
2011 | .num_columns = NUM_COLUMNS, | |
2012 | .d_scroll_len = SCROLL_LEN, | |
7cc31c18 SK |
2013 | 0 |
2014 | }; | |
f16ca88d SK |
2015 | |
2016 | setlocale(LC_ALL, ""); | |
2017 | bindtextdomain(PACKAGE, LOCALEDIR); | |
2018 | textdomain(PACKAGE); | |
2c308875 | 2019 | close_stdout_atexit(); |
f16ca88d | 2020 | setlocale(LC_ALL, ""); |
f16ca88d SK |
2021 | |
2022 | /* Auto set no scroll on when binary is called page */ | |
2023 | if (!(strcmp(program_invocation_short_name, "page"))) | |
d910f311 | 2024 | ctl.no_scroll++; |
f16ca88d | 2025 | |
e3cb27f5 SK |
2026 | if ((s = getenv("MORE")) != NULL) |
2027 | env_argscan(&ctl, s); | |
2028 | argscan(&ctl, argc, argv); | |
2029 | ||
2030 | initterm(&ctl); | |
2031 | ||
09070e1a SK |
2032 | #ifdef HAVE_MAGIC |
2033 | ctl.magic = magic_open(MAGIC_MIME_ENCODING | MAGIC_SYMLINK); | |
2034 | magic_load(ctl.magic, NULL); | |
2035 | #endif | |
7cc31c18 | 2036 | prepare_line_buffer(&ctl); |
f16ca88d | 2037 | |
d910f311 SK |
2038 | ctl.d_scroll_len = ctl.lines_per_page / 2 - 1; |
2039 | if (ctl.d_scroll_len <= 0) | |
2040 | ctl.d_scroll_len = 1; | |
f16ca88d | 2041 | |
d910f311 SK |
2042 | /* allow clear_line_ends only if go_home and erase_line and clear_rest strings are |
2043 | * defined, and in that case, make sure we are in no_scroll mode */ | |
2044 | if (ctl.clear_line_ends) { | |
2045 | if ((ctl.go_home == NULL) || (*ctl.go_home == '\0') || | |
2046 | (ctl.erase_line == NULL) || (*ctl.erase_line == '\0') || | |
2047 | (ctl.clear_rest == NULL) || (*ctl.clear_rest == '\0')) | |
2048 | ctl.clear_line_ends = 0; | |
f16ca88d | 2049 | else |
d910f311 | 2050 | ctl.no_scroll = 1; |
f16ca88d | 2051 | } |
d910f311 SK |
2052 | if (ctl.lines_per_screen == 0) |
2053 | ctl.lines_per_screen = ctl.lines_per_page - 1; | |
2054 | left = ctl.lines_per_screen; | |
2055 | if (ctl.num_files > 1) | |
63f96f68 | 2056 | ctl.print_banner = 1; |
d910f311 | 2057 | if (!ctl.no_tty_in && ctl.num_files == 0) { |
f16ca88d SK |
2058 | warnx(_("bad usage")); |
2059 | errtryhelp(EXIT_FAILURE); | |
2060 | } else | |
82129a6f | 2061 | ctl.current_file = stdin; |
d910f311 | 2062 | if (!ctl.no_tty_out) { |
f16ca88d | 2063 | if (signal(SIGTSTP, SIG_IGN) == SIG_DFL) { |
d910f311 | 2064 | ctl.catch_suspend++; |
f16ca88d | 2065 | } |
d910f311 | 2066 | tcsetattr(STDERR_FILENO, TCSANOW, &ctl.output_tty); |
f16ca88d | 2067 | } |
823ef319 SK |
2068 | sigemptyset(&ctl.sigset); |
2069 | sigaddset(&ctl.sigset, SIGINT); | |
2070 | sigaddset(&ctl.sigset, SIGQUIT); | |
2071 | sigaddset(&ctl.sigset, SIGTSTP); | |
6dcaaa9e | 2072 | sigaddset(&ctl.sigset, SIGCONT); |
823ef319 SK |
2073 | sigaddset(&ctl.sigset, SIGWINCH); |
2074 | sigprocmask(SIG_BLOCK, &ctl.sigset, NULL); | |
2075 | ctl.sigfd = signalfd(-1, &ctl.sigset, SFD_CLOEXEC); | |
d910f311 SK |
2076 | if (ctl.no_tty_in) { |
2077 | if (ctl.no_tty_out) | |
f16ca88d SK |
2078 | copy_file(stdin); |
2079 | else { | |
82129a6f | 2080 | ctl.current_file = stdin; |
e3cb27f5 | 2081 | display_file(&ctl, left); |
f16ca88d | 2082 | } |
d910f311 | 2083 | ctl.no_tty_in = 0; |
63f96f68 | 2084 | ctl.print_banner = 1; |
d910f311 | 2085 | ctl.first_file = 0; |
f16ca88d SK |
2086 | } |
2087 | ||
d910f311 | 2088 | while (ctl.argv_position < ctl.num_files) { |
63f96f68 | 2089 | checkf(&ctl, ctl.file_names[ctl.argv_position]); |
e3cb27f5 | 2090 | display_file(&ctl, left); |
d910f311 | 2091 | ctl.first_file = 0; |
63f96f68 | 2092 | ctl.argv_position++; |
f16ca88d | 2093 | } |
73ff5a95 SK |
2094 | ctl.clear_line_ends = 0; |
2095 | ctl.prompt_len = 0; | |
2096 | more_exit(&ctl); | |
f16ca88d | 2097 | } |