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