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