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