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