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