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