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