]> git.ipfire.org Git - thirdparty/util-linux.git/blob - text-utils/more.c
libfdisk: (dos) accept start for log.partitions on template
[thirdparty/util-linux.git] / text-utils / more.c
1 /*
2 * Copyright (C) 1980 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 */
17
18 /* more.c - General purpose tty output filter and file perusal program
19 *
20 * by Eric Shienbrood, UC Berkeley
21 *
22 * modified by Geoff Peck
23 * UCB to add underlining, single spacing
24 * modified by John Foderaro
25 * UCB to add -c and MORE environment variable
26 * modified by Erik Troan <ewt@redhat.com>
27 * to be more posix and so compile on linux/axp.
28 * modified by Kars de Jong <jongk@cs.utwente.nl>
29 * to use terminfo instead of termcap.
30 * 1999-02-22 Arkadiusz Miƛkiewicz <misiek@pld.ORG.PL>
31 * added Native Language Support
32 * 1999-03-19 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
33 * more nls translatable strings
34 * 1999-05-09 aeb
35 * applied a RedHat patch (setjmp->sigsetjmp); without it a second
36 * ^Z would fail.
37 * 1999-05-09 aeb
38 * undone Kars' work, so that more works without libcurses (and
39 * hence can be in /bin with libcurses being in
40 * /usr/lib which may not be mounted). However, when termcap is not
41 * present curses can still be used.
42 * 2010-10-21 Davidlohr Bueso <dave@gnu.org>
43 * modified mem allocation handling for util-linux
44 */
45
46 #include <stdio.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <stdlib.h> /* for alloca() */
50 #include <stdarg.h> /* for va_start() etc */
51 #include <sys/param.h>
52 #include <ctype.h>
53 #include <signal.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <termios.h>
57 #include <setjmp.h>
58 #include <sys/ioctl.h>
59 #include <sys/stat.h>
60 #include <sys/file.h>
61 #include <sys/wait.h>
62
63 #include "strutils.h"
64 #include "nls.h"
65 #include "xalloc.h"
66 #include "widechar.h"
67 #include "closestream.h"
68
69 #include <regex.h>
70
71 #ifdef TEST_PROGRAM
72 # define NON_INTERACTIVE_MORE 1
73 #endif
74
75 #ifndef XTABS
76 # define XTABS TAB3
77 #endif
78
79 #define VI "vi" /* found on the user's path */
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))
86 #define putcerr(c) fputc(c, stderr)
87 #define putserr(s) fputs(s, stderr)
88 #define putsout(s) fputs(s, stdout)
89
90 #define stty(fd,argp) tcsetattr(fd,TCSANOW,argp)
91
92 /* some function declarations */
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);
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);
103 void argscan(char *s);
104 void rdline(register FILE *f);
105 void copy_file(register FILE *f);
106 void search(char buf[], FILE *file, register int n);
107 void skipf(register int nskip);
108 void skiplns(register int n, register FILE *f);
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);
113 void set_tty(void);
114 void reset_tty(void);
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 *);
122 void prepare_line_buffer(void);
123
124 #define TBUFSIZ 1024
125 #define LINSIZ 256 /* minimal Line buffer size */
126 #define ctrl(letter) (letter & 077)
127 #define RUBOUT '\177'
128 #define ESC '\033'
129 #define QUIT '\034'
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
137 #define REGERR_BUF NUM_COLUMNS
138
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 */
186 static int magic(FILE *, char *);
187 static char *previousre; /* previous search() buf[] item */
188 static struct {
189 long chrctr, line;
190 } context, screen_start;
191 extern char PC; /* pad character */
192
193 #if defined(HAVE_NCURSESW_TERM_H)
194 # include <ncursesw/term.h>
195 #elif defined(HAVE_NCURSES_TERM_H)
196 # include <ncurses/term.h>
197 #elif defined(HAVE_TERM_H)
198 # include <term.h>
199 #endif
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"
222
223 static void putstring(char *s)
224 {
225 tputs(s, fileno(stdout), putchar); /* putp(s); */
226 }
227
228 static void __attribute__((__noreturn__)) usage(FILE *out)
229 {
230 fputs(USAGE_HEADER, out);
231 fprintf(out, _(" %s [options] <file>...\n"), program_invocation_short_name);
232
233 fputs(USAGE_SEPARATOR, out);
234 fputs(_("A file perusal filter for CRT viewing.\n"), out);
235
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)"));
249 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
250 }
251
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;
306 }
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;
316 }
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);
329 #ifdef SIGWINCH
330 signal(SIGWINCH, chgwinsz);
331 #endif
332 if (signal(SIGTSTP, SIG_IGN) == SIG_DFL) {
333 signal(SIGTSTP, onsusp);
334 catch_susp++;
335 }
336 stty(fileno(stderr), &otty);
337 }
338 if (no_intty) {
339 if (no_tty)
340 copy_file(stdin);
341 else {
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) {
354 free(previousre);
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) {
377 free(previousre);
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;
427 }
428 fnum++;
429 firstf = 0;
430 }
431 free(previousre);
432 free(initbuf);
433 free(Line);
434 reset_tty();
435 exit(EXIT_SUCCESS);
436 }
437
438 void argscan(char *s)
439 {
440 int seen_num = 0;
441
442 while (*s != '\0') {
443 switch (*s) {
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':
454 if (!seen_num) {
455 dlines = 0;
456 seen_num = 1;
457 }
458 dlines = dlines * 10 + *s - '0';
459 break;
460 case 'd':
461 dum_opt = 1;
462 break;
463 case 'l':
464 stop_opt = 0;
465 break;
466 case 'f':
467 fold_opt = 0;
468 break;
469 case 'p':
470 noscroll++;
471 break;
472 case 'c':
473 clreol++;
474 break;
475 case 's':
476 ssp_opt = 1;
477 break;
478 case 'u':
479 ul_opt = 0;
480 break;
481 case '-':
482 case ' ':
483 case '\t':
484 break;
485 case 'V':
486 printf(UTIL_LINUX_VERSION);
487 exit(EXIT_SUCCESS);
488 break;
489 default:
490 warnx(_("unknown option -%s"), s);
491 usage(stderr);
492 break;
493 }
494 s++;
495 }
496 }
497
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)
501 {
502 struct stat stbuf;
503 register FILE *f;
504 int c;
505
506 if (stat(fs, &stbuf) == -1) {
507 fflush(stdout);
508 if (clreol)
509 cleareol();
510 warn(_("stat of %s failed"), fs);
511 return ((FILE *)NULL);
512 }
513 if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
514 printf(_("\n*** %s: directory ***\n\n"), fs);
515 return ((FILE *)NULL);
516 }
517 if ((f = Fopen(fs, "r")) == NULL) {
518 fflush(stdout);
519 warn(_("cannot open %s"), fs);
520 return ((FILE *)NULL);
521 }
522 if (magic(f, fs)) {
523 fclose(f);
524 return ((FILE *)NULL);
525 }
526 fcntl(fileno(f), F_SETFD, FD_CLOEXEC);
527 c = Getc(f);
528 *clearfirst = (c == '\f');
529 Ungetc(c, f);
530 if ((file_size = stbuf.st_size) == 0)
531 file_size = LONG_MAX;
532 return (f);
533 }
534
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)
540 {
541 signed char twobytes[2];
542
543 /* don't try to look ahead if the input is unseekable */
544 if (fseek(f, 0L, SEEK_SET))
545 return 0;
546
547 if (fread(twobytes, 2, 1, f) == 1) {
548 switch (twobytes[0] + (twobytes[1] << 8)) {
549 case 0407: /* a.out obj */
550 case 0410: /* a.out exec */
551 case 0413: /* a.out demand exec */
552 case 0405:
553 case 0411:
554 case 0177545:
555 case 0x457f: /* simple ELF detection */
556 printf(_("\n******** %s: Not a text file ********\n\n"),
557 fs);
558 return 1;
559 }
560 }
561 fseek(f, 0L, SEEK_SET); /* rewind() not necessary */
562 return 0;
563 }
564
565 /* Print out the contents of the file f, one screenful at a time. */
566 #define STOP -10
567 void screen(register FILE *f, register int num_lines)
568 {
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 */
573
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) {
608 putstring(ULexit);
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);
636 }
637 }
638
639 /* Come here if a quit signal is received */
640 static void onquit(int dummy __attribute__((__unused__)))
641 {
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;
653 }
654 signal(SIGQUIT, onquit);
655 }
656
657 /* Come here if a signal for a window size change is received */
658 #ifdef SIGWINCH
659 static void chgwinsz(int dummy __attribute__((__unused__)))
660 {
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;
674 }
675 signal(SIGWINCH, chgwinsz);
676 }
677 #endif /* SIGWINCH */
678
679 /* Clean up terminal state and exit. Also come here if interrupt signal received */
680 static void __attribute__((__noreturn__)) end_it(int dummy __attribute__((__unused__)))
681 {
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
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');
700 free(previousre);
701 free(Line);
702 _exit(EXIT_SUCCESS);
703 }
704
705 void copy_file(register FILE *f)
706 {
707 char buf[BUFSIZ];
708 size_t sz;
709
710 while ((sz = fread(&buf, sizeof(char), sizeof(buf), f)) > 0)
711 fwrite(&buf, sizeof(char), sz, stdout);
712 }
713
714 #define ringbell() putcerr('\007')
715
716 static void prompt(char *filename)
717 {
718 if (clreol)
719 cleareol();
720 else if (promptlen > 0)
721 kill_line();
722 if (!hard) {
723 promptlen = 0;
724 if (Senter && Sexit) {
725 putstring(Senter);
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)
743 putstring(Sexit);
744 if (clreol)
745 clreos();
746 fflush(stdout);
747 } else
748 ringbell();
749 inwait++;
750 }
751
752 void prepare_line_buffer(void)
753 {
754 char *nline;
755 size_t nsz = Mcol * 4;
756
757 if (LineLen >= nsz)
758 return;
759
760 if (nsz < LINSIZ)
761 nsz = LINSIZ;
762
763 /* alloc nsz and extra space for \n\0 */
764 nline = xrealloc(Line, nsz + 2);
765 Line = nline;
766 LineLen = nsz;
767 }
768
769 /* Get a logical line */
770 int get_line(register FILE *f, int *length)
771 {
772 int c;
773 char *p;
774 int column;
775 static int colflg;
776
777 #ifdef HAVE_WIDECHAR
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]) {
802 #ifdef HAVE_WIDECHAR
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 {
840 for (i = 0; p < &Line[LineLen - 1] &&
841 i < mbc_pos; i++)
842 *p++ = mbc[i];
843 if (wc_width > 0)
844 column += wc_width;
845 }
846 }
847
848 if (break_flag || column >= Mcol)
849 break;
850
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;
867 }
868
869 *p++ = c;
870 #if 0
871 if (c == '\033') { /* ESC */
872 c = Getc(f);
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 }
882 }
883 #endif /* 0 */
884 if (c == '\t') {
885 if (!hardtabs || (column < promptlen && !hard)) {
886 if (hardtabs && eraseln && !dumb) {
887 column = 1 + (column | 7);
888 putstring(eraseln);
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 }
954 }
955
956 if (column >= Mcol && fold_opt)
957 break;
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
964 c = Getc(f);
965 }
966 if (column >= Mcol && Mcol > 0) {
967 if (!Wrap) {
968 *p++ = '\n';
969 }
970 }
971 colflg = column == Mcol && fold_opt;
972 if (colflg && eatnl && Wrap) {
973 *p++ = '\n'; /* simulate normal wrap */
974 }
975 *length = p - Line;
976 *p = 0;
977 return (column);
978 }
979
980 /* Erase the rest of the prompt, assuming we are starting at column col. */
981 void erasep(register int col)
982 {
983
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)
992 putstring(eraseln);
993 else
994 printf("%*s", promptlen - col, "");
995 }
996 promptlen = 0;
997 }
998
999 /* Erase the current line entirely */
1000 void kill_line(void)
1001 {
1002 erasep(0);
1003 if (!eraseln || dumb)
1004 putchar('\r');
1005 }
1006
1007 /* force clear to end of line */
1008 void cleareol(void)
1009 {
1010 putstring(eraseln);
1011 }
1012
1013 void clreos(void)
1014 {
1015 putstring(EodClr);
1016 }
1017
1018
1019 #ifdef HAVE_WIDECHAR
1020 static UL_ASAN_BLACKLIST size_t xmbrtowc(wchar_t *wc, const char *s, size_t n,
1021 mbstate_t *mbstate)
1022 {
1023 const size_t mblength = mbrtowc(wc, s, n, mbstate);
1024 if (mblength == (size_t)-2 || mblength == (size_t)-1)
1025 return 1;
1026 return mblength;
1027 }
1028 #endif
1029
1030 /* Print a buffer of n characters */
1031 void prbuf(register char *s, register int n)
1032 {
1033 register char c; /* next output character */
1034 register int state; /* next output char's UL state */
1035 #define wouldul(s,n) ((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_')))
1036
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
1057 putstring(state ? ULenter : ULexit);
1058 }
1059 if (c != ' ' || pstate == 0 || state != 0
1060 || ulglitch == 0)
1061 #ifdef HAVE_WIDECHAR
1062 {
1063 wchar_t wc;
1064 size_t mblength;
1065 mbstate_t mbstate;
1066 memset(&mbstate, '\0', sizeof(mbstate_t));
1067 s--;
1068 n++;
1069 mblength = xmbrtowc(&wc, s, n, &mbstate);
1070 while (mblength--)
1071 putchar(*s++);
1072 n += mblength;
1073 }
1074 #else
1075 putchar(c);
1076 #endif /* HAVE_WIDECHAR */
1077 if (state && *chUL) {
1078 putsout(chBS);
1079 putstring(chUL);
1080 }
1081 pstate = state;
1082 }
1083 }
1084
1085 /* Clear the screen */
1086 void doclear(void)
1087 {
1088 if (Clear && !hard) {
1089 putstring(Clear);
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 }
1095 }
1096
1097 /* Go to home position */
1098 void home(void)
1099 {
1100 putstring(Home);
1101 }
1102
1103 static int lastcmd, lastarg, lastp;
1104 static int lastcolon;
1105 static char shell_line[SHELL_LINE];
1106
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)
1112 {
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];
1119
1120 #define ret(val) retval=val;done++;break
1121
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;
1136 }
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();
1168 printf(P_("...back %d page",
1169 "...back %d pages", nlines),
1170 nlines);
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();
1216 printf(P_("...skipping %d line",
1217 "...skipping %d lines", nlines),
1218 nlines);
1219
1220 if (clreol)
1221 cleareol();
1222 putchar('\n');
1223
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) {
1376 putstring(Senter);
1377 promptlen =
1378 printf(_
1379 ("[Press 'h' for instructions.]"))
1380 + 2 * soglitch;
1381 putstring(Sexit);
1382 } else
1383 promptlen =
1384 printf(_
1385 ("[Press 'h' for instructions.]"));
1386 fflush(stdout);
1387 } else
1388 ringbell();
1389 break;
1390 }
1391 if (done)
1392 break;
1393 }
1394 putchar('\r');
1395 endsw:
1396 inwait = 0;
1397 notell++;
1398 return (retval);
1399 }
1400
1401 static char ch;
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 {
1406 if (cmd == 0)
1407 ch = readch();
1408 else
1409 ch = cmd;
1410 lastcolon = ch;
1411 switch (ch) {
1412 case 'f':
1413 kill_line();
1414 if (!no_intty)
1415 promptlen =
1416 printf(_("\"%s\" line %d"), fnames[fnum], Currline);
1417 else
1418 promptlen = printf(_("[Not a file] line %d"), Currline);
1419 fflush(stdout);
1420 return (-1);
1421 case 'n':
1422 if (nlines == 0) {
1423 if (fnum >= nfiles - 1)
1424 end_it(0);
1425 nlines++;
1426 }
1427 putchar('\r');
1428 erasep(0);
1429 skipf(nlines);
1430 return (0);
1431 case 'p':
1432 if (no_intty) {
1433 ringbell();
1434 return (-1);
1435 }
1436 putchar('\r');
1437 erasep(0);
1438 if (nlines == 0)
1439 nlines++;
1440 skipf(-nlines);
1441 return (0);
1442 case '!':
1443 do_shell(filename);
1444 return (-1);
1445 case 'q':
1446 case 'Q':
1447 end_it(0);
1448 default:
1449 ringbell();
1450 return (-1);
1451 }
1452 }
1453
1454 /* Read a decimal number from the terminal. Set cmd to the non-digit
1455 * which terminates the number. */
1456 int number(char *cmd)
1457 {
1458 register int i;
1459
1460 i = 0;
1461 ch = otty.c_cc[VKILL];
1462 for (;;) {
1463 ch = readch();
1464 if (isdigit(ch))
1465 i = i * 10 + ch - '0';
1466 else if ((cc_t) ch == otty.c_cc[VKILL])
1467 i = 0;
1468 else {
1469 *cmd = ch;
1470 break;
1471 }
1472 }
1473 return (i);
1474 }
1475
1476 void do_shell(char *filename)
1477 {
1478 char cmdbuf[COMMAND_BUF];
1479 int rc;
1480 char *expanded;
1481
1482 kill_line();
1483 putchar('!');
1484 fflush(stdout);
1485 promptlen = 1;
1486 if (lastp)
1487 putsout(shell_line);
1488 else {
1489 ttyin(cmdbuf, sizeof(cmdbuf) - 2, '!');
1490 expanded = NULL;
1491 rc = expand(&expanded, cmdbuf);
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) {
1500 putserr(_(" Overflow\n"));
1501 prompt(filename);
1502 return;
1503 } else if (rc > 0) {
1504 kill_line();
1505 promptlen = printf("!%s", shell_line);
1506 }
1507 }
1508 fflush(stdout);
1509 putcerr('\n');
1510 promptlen = 0;
1511 shellp = 1;
1512 execute(filename, shell, shell, "-c", shell_line, 0);
1513 }
1514
1515 /* Search for nth occurrence of regular expression contained in buf in
1516 * the file */
1517 void search(char buf[], FILE *file, register int n)
1518 {
1519 long startline = Ftell(file);
1520 register long line1 = startline;
1521 register long line2 = startline;
1522 register long line3;
1523 register int lncount;
1524 int saveln, rc;
1525 regex_t re;
1526
1527 context.line = saveln = Currline;
1528 context.chrctr = startline;
1529 lncount = 0;
1530 if (!buf)
1531 goto notfound;
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++;
1543 if (regexec(&re, Line, 0, NULL, 0) == 0) {
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;
1574 }
1575 }
1576 }
1577 regfree(&re);
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 }
1586 free(previousre);
1587 previousre = NULL;
1588 notfound:
1589 more_error(_("Pattern not found"));
1590 }
1591 }
1592
1593 void execute(char *filename, char *cmd, ...)
1594 {
1595 int id;
1596 int n;
1597 va_list argp;
1598 char *arg;
1599 char **args;
1600 int argcount;
1601
1602 fflush(stdout);
1603 reset_tty();
1604 for (n = 10; (id = fork()) < 0 && n > 0; n--)
1605 sleep(5);
1606 if (id == 0) {
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);
1637 }
1638 if (id > 0) {
1639 signal(SIGINT, SIG_IGN);
1640 signal(SIGQUIT, SIG_IGN);
1641 if (catch_susp)
1642 signal(SIGTSTP, SIG_DFL);
1643 while (wait(NULL) > 0) ;
1644 signal(SIGINT, end_it);
1645 signal(SIGQUIT, onquit);
1646 if (catch_susp)
1647 signal(SIGTSTP, onsusp);
1648 } else
1649 putserr(_("can't fork\n"));
1650 set_tty();
1651 puts("------------------------");
1652 prompt(filename);
1653 }
1654
1655 /* Skip n lines in the file f */
1656 void skiplns(register int n, register FILE *f)
1657 {
1658 register int c;
1659
1660 while (n > 0) {
1661 while ((c = Getc(f)) != '\n')
1662 if (c == EOF)
1663 return;
1664 n--;
1665 Currline++;
1666 }
1667 }
1668
1669 /* Skip nskip files in the file list (from the command line). Nskip may
1670 * be negative. */
1671 void skipf(register int nskip)
1672 {
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;
1695 }
1696
1697 /*----------------------------- Terminal I/O -------------------------------*/
1698 void initterm(void)
1699 {
1700 int ret;
1701 char *padstr;
1702 char *term;
1703 struct winsize win;
1704
1705 #ifdef do_SIGTTOU
1706 retry:
1707 #endif
1708
1709 #ifndef NON_INTERACTIVE_MORE
1710 no_tty = tcgetattr(fileno(stdout), &otty);
1711 #endif
1712 if (!no_tty) {
1713 docrterase = (otty.c_cc[VERASE] != 255);
1714 docrtkill = (otty.c_cc[VKILL] != 255);
1715 #ifdef do_SIGTTOU
1716 {
1717 int tgrp;
1718 /* Wait until we're in the foreground before we
1719 * save the terminal modes. */
1720 if ((tgrp = tcgetpgrp(fileno(stdout))) < 0)
1721 err(EXIT_FAILURE, "tcgetpgrp");
1722 if (tgrp != getpgrp(0)) {
1723 kill(0, SIGTTOU);
1724 goto retry;
1725 }
1726 }
1727 #endif /* do_SIGTTOU */
1728 if ((term = getenv("TERM")) == NULL) {
1729 dumb++;
1730 ul_opt = 0;
1731 }
1732 setupterm(term, 1, &ret);
1733 if (ret <= 0) {
1734 dumb++;
1735 ul_opt = 0;
1736 } else {
1737 #ifdef TIOCGWINSZ
1738 if (ioctl(fileno(stdout), TIOCGWINSZ, &win) < 0) {
1739 #endif
1740 Lpp = tigetnum(TERM_LINES);
1741 Mcol = tigetnum(TERM_COLS);
1742 #ifdef TIOCGWINSZ
1743 } else {
1744 if ((Lpp = win.ws_row) == 0)
1745 Lpp = tigetnum(TERM_LINES);
1746 if ((Mcol = win.ws_col) == 0)
1747 Mcol = tigetnum(TERM_COLS);
1748 }
1749 #endif
1750 if ((Lpp <= 0) || tigetflag(TERM_HARD_COPY)) {
1751 hard++; /* Hard copy terminal */
1752 Lpp = LINES_PER_PAGE;
1753 }
1754
1755 if (tigetflag(TERM_EAT_NEW_LINE))
1756 /* Eat newline at last column + 1; dec, concept */
1757 eatnl++;
1758 if (Mcol <= 0)
1759 Mcol = NUM_COLUMNS;
1760
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)
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. */
1776 if (tigetflag(TERM_UNDERLINE)
1777 || tigetflag(TERM_OVER_STRIKE))
1778 ul_opt = 0;
1779 if ((chUL = tigetstr(TERM_UNDERLINE_CHAR)) == NULL)
1780 chUL = "";
1781 if (((ULenter =
1782 tigetstr(TERM_ENTER_UNDERLINE)) == NULL
1783 || (ULexit =
1784 tigetstr(TERM_EXIT_UNDERLINE)) == NULL)
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 }
1795
1796 if ((padstr = tigetstr(TERM_PAD_CHAR)) != NULL)
1797 PC = *padstr;
1798 Home = tigetstr(TERM_HOME);
1799 if (Home == NULL || *Home == '\0') {
1800 if ((cursorm =
1801 tigetstr(TERM_CURSOR_ADDRESS)) != NULL) {
1802 const char *t =
1803 (const char *)tparm(cursorm, 0,
1804 0);
1805 xstrncpy(cursorhome, t,
1806 sizeof(cursorhome));
1807 Home = cursorhome;
1808 }
1809 }
1810 EodClr = tigetstr(TERM_CLEAR_TO_SCREEN_END);
1811 if ((chBS = tigetstr(TERM_LINE_DOWN)) == NULL)
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;
1827 }
1828 }
1829
1830 int readch(void)
1831 {
1832 unsigned char c;
1833
1834 errno = 0;
1835 if (read(fileno(stderr), &c, 1) <= 0) {
1836 if (errno != EINTR)
1837 end_it(0);
1838 else
1839 c = otty.c_cc[VKILL];
1840 }
1841 return (c);
1842 }
1843
1844 static char *BS = "\b";
1845 static char *BSB = "\b \b";
1846 static char *CARAT = "^";
1847 #define ERASEONECOLUMN(x) \
1848 do { \
1849 if (x) \
1850 putserr(BSB); \
1851 else \
1852 putserr(BS); \
1853 } while(0)
1854
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) {
1872 #ifdef HAVE_WIDECHAR
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) {
1903 ERASEONECOLUMN(docrterase);
1904 } else {
1905 int wc_width;
1906 wc_width = wcwidth(wc);
1907 wc_width =
1908 (wc_width <
1909 1) ? 1 : wc_width;
1910 while (wc_width--) {
1911 ERASEONECOLUMN(docrterase);
1912 }
1913 }
1914
1915 while (mblength--) {
1916 --promptlen;
1917 --sp;
1918 }
1919 } else
1920 #endif /* HAVE_WIDECHAR */
1921 {
1922 --promptlen;
1923 ERASEONECOLUMN(docrterase);
1924 --sp;
1925 }
1926
1927 if ((*sp < ' ' && *sp != '\n') || *sp == RUBOUT) {
1928 --promptlen;
1929 ERASEONECOLUMN(docrterase);
1930 }
1931 continue;
1932 } else {
1933 if (!eraseln)
1934 promptlen = maxlen;
1935 siglongjmp(restore, 1);
1936 }
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;
1955 }
1956 if (slash && ((cc_t) c == otty.c_cc[VKILL]
1957 || (cc_t) c == otty.c_cc[VERASE])) {
1958 ERASEONECOLUMN(docrterase);
1959 --sp;
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;
1974 }
1975 *--sp = '\0';
1976 if (!eraseln)
1977 promptlen = maxlen;
1978 if (sp - buf >= nmax - 1)
1979 more_error(_("Line too long"));
1980 }
1981
1982 /* return: 0 - unchanged, 1 - changed, -1 - overflow (unchanged) */
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 }
2029 }
2030 *outstr++ = '\0';
2031 *outbuf = temp;
2032 return (changed);
2033 }
2034
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);
2043 promptlen++;
2044 }
2045
2046 void more_error(char *mess)
2047 {
2048 if (clreol)
2049 cleareol();
2050 else
2051 kill_line();
2052 promptlen += strlen(mess);
2053 if (Senter && Sexit) {
2054 putstring(Senter);
2055 putsout(mess);
2056 putstring(Sexit);
2057 } else
2058 putsout(mess);
2059 fflush(stdout);
2060 errors++;
2061 siglongjmp(restore, 1);
2062 }
2063
2064 void set_tty(void)
2065 {
2066 otty.c_lflag &= ~(ICANON | ECHO);
2067 otty.c_cc[VMIN] = 1; /* read at least 1 char */
2068 otty.c_cc[VTIME] = 0; /* no timeout */
2069 stty(fileno(stderr), &otty);
2070 }
2071
2072 static int ourputch(int c)
2073 {
2074 return putc(c, stdout);
2075 }
2076
2077 void reset_tty(void)
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);
2091 }
2092
2093 void rdline(register FILE *f)
2094 {
2095 register int c;
2096 register char *p;
2097
2098 prepare_line_buffer();
2099
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';
2107 }
2108
2109 /* Come here when we get a suspend signal from the terminal */
2110 void onsusp(int dummy __attribute__((__unused__)))
2111 {
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);
2137 }