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