1 /* 2 * Copyright (C) 1984-2011 Mark Nudelman 3 * 4 * You may distribute under the terms of either the GNU General Public 5 * License or the Less License, as specified in the README file. 6 * 7 * For more information about less, or for information on how to 8 * contact the author, see the README file. 9 */ 10 11 12 #include "less.h" 13 #if HAVE_STAT 14 #include <sys/stat.h> 15 #endif 16 17 public int fd0 = 0; 18 19 extern int new_file; 20 extern int errmsgs; 21 extern int cbufs; 22 extern char *every_first_cmd; 23 extern int any_display; 24 extern int force_open; 25 extern int is_tty; 26 extern volatile sig_atomic_t sigs; 27 extern IFILE curr_ifile; 28 extern IFILE old_ifile; 29 extern struct scrpos initial_scrpos; 30 extern void constant *ml_examine; 31 #if SPACES_IN_FILENAMES 32 extern char openquote; 33 extern char closequote; 34 #endif 35 36 #if LOGFILE 37 extern int logfile; 38 extern int force_logfile; 39 extern char *namelogfile; 40 #endif 41 42 #if HAVE_STAT_INO 43 public dev_t curr_dev; 44 public ino_t curr_ino; 45 #endif 46 47 char *curr_altfilename = NULL; 48 static void *curr_altpipe; 49 50 #if EXAMINE || TAB_COMPLETE_FILENAME 51 /* 52 * Textlist functions deal with a list of words separated by spaces. 53 * init_textlist sets up a textlist structure. 54 * forw_textlist uses that structure to iterate thru the list of 55 * words, returning each one as a standard null-terminated string. 56 * back_textlist does the same, but runs thru the list backwards. 57 */ 58 public void 59 init_textlist(tlist, str) 60 struct textlist *tlist; 61 char *str; 62 { 63 char *s; 64 #if SPACES_IN_FILENAMES 65 int meta_quoted = 0; 66 int delim_quoted = 0; 67 char *esc = get_meta_escape(); 68 int esclen = strlen(esc); 69 #endif 70 71 tlist->string = skipsp(str); 72 tlist->endstring = tlist->string + strlen(tlist->string); 73 for (s = str; s < tlist->endstring; s++) 74 { 75 #if SPACES_IN_FILENAMES 76 if (meta_quoted) 77 { 78 meta_quoted = 0; 79 } else if (esclen > 0 && s + esclen < tlist->endstring && 80 strncmp(s, esc, esclen) == 0) 81 { 82 meta_quoted = 1; 83 s += esclen - 1; 84 } else if (delim_quoted) 85 { 86 if (*s == closequote) 87 delim_quoted = 0; 88 } else /* (!delim_quoted) */ 89 { 90 if (*s == openquote) 91 delim_quoted = 1; 92 else if (*s == ' ') 93 *s = '\0'; 94 } 95 #else 96 if (*s == ' ') 97 *s = '\0'; 98 #endif 99 } 100 } 101 102 public char * 103 forw_textlist(tlist, prev) 104 struct textlist *tlist; 105 char *prev; 106 { 107 char *s; 108 109 /* 110 * prev == NULL means return the first word in the list. 111 * Otherwise, return the word after "prev". 112 */ 113 if (prev == NULL) 114 s = tlist->string; 115 else 116 s = prev + strlen(prev); 117 if (s >= tlist->endstring) 118 return (NULL); 119 while (*s == '\0') 120 s++; 121 if (s >= tlist->endstring) 122 return (NULL); 123 return (s); 124 } 125 126 public char * 127 back_textlist(tlist, prev) 128 struct textlist *tlist; 129 char *prev; 130 { 131 char *s; 132 133 /* 134 * prev == NULL means return the last word in the list. 135 * Otherwise, return the word before "prev". 136 */ 137 if (prev == NULL) 138 s = tlist->endstring; 139 else if (prev <= tlist->string) 140 return (NULL); 141 else 142 s = prev - 1; 143 while (*s == '\0') 144 s--; 145 if (s <= tlist->string) 146 return (NULL); 147 while (s[-1] != '\0' && s > tlist->string) 148 s--; 149 return (s); 150 } 151 #endif /* EXAMINE || TAB_COMPLETE_FILENAME */ 152 153 /* 154 * Close the current input file. 155 */ 156 static void 157 close_file() 158 { 159 struct scrpos scrpos; 160 161 if (curr_ifile == NULL_IFILE) 162 return; 163 164 /* 165 * Save the current position so that we can return to 166 * the same position if we edit this file again. 167 */ 168 get_scrpos(&scrpos); 169 if (scrpos.pos != NULL_POSITION) 170 { 171 store_pos(curr_ifile, &scrpos); 172 lastmark(); 173 } 174 /* 175 * Close the file descriptor, unless it is a pipe. 176 */ 177 ch_close(); 178 /* 179 * If we opened a file using an alternate name, 180 * do special stuff to close it. 181 */ 182 if (curr_altfilename != NULL) 183 { 184 close_altfile(curr_altfilename, get_filename(curr_ifile), 185 curr_altpipe); 186 free(curr_altfilename); 187 curr_altfilename = NULL; 188 } 189 curr_ifile = NULL_IFILE; 190 #if HAVE_STAT_INO 191 curr_ino = curr_dev = 0; 192 #endif 193 } 194 195 /* 196 * Edit a new file (given its name). 197 * Filename == "-" means standard input. 198 * Filename == NULL means just close the current file. 199 */ 200 public int 201 edit(filename) 202 char *filename; 203 { 204 if (filename == NULL) 205 return (edit_ifile(NULL_IFILE)); 206 return (edit_ifile(get_ifile(filename, curr_ifile))); 207 } 208 209 /* 210 * Edit a new file (given its IFILE). 211 * ifile == NULL means just close the current file. 212 */ 213 public int 214 edit_ifile(ifile) 215 IFILE ifile; 216 { 217 int f; 218 int answer; 219 int no_display; 220 int chflags; 221 char *filename; 222 char *open_filename; 223 char *qopen_filename; 224 char *alt_filename; 225 void *alt_pipe; 226 IFILE was_curr_ifile; 227 PARG parg; 228 229 if (ifile == curr_ifile) 230 { 231 /* 232 * Already have the correct file open. 233 */ 234 return (0); 235 } 236 237 /* 238 * We must close the currently open file now. 239 * This is necessary to make the open_altfile/close_altfile pairs 240 * nest properly (or rather to avoid nesting at all). 241 * {{ Some stupid implementations of popen() mess up if you do: 242 * fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }} 243 */ 244 #if LOGFILE 245 end_logfile(); 246 #endif 247 was_curr_ifile = save_curr_ifile(); 248 if (curr_ifile != NULL_IFILE) 249 { 250 chflags = ch_getflags(); 251 close_file(); 252 #if !SMALL 253 if ((chflags & CH_HELPFILE) && held_ifile(was_curr_ifile) <= 1) 254 { 255 /* 256 * Don't keep the help file in the ifile list. 257 */ 258 del_ifile(was_curr_ifile); 259 was_curr_ifile = old_ifile; 260 } 261 #endif /* !SMALL */ 262 } 263 264 if (ifile == NULL_IFILE) 265 { 266 /* 267 * No new file to open. 268 * (Don't set old_ifile, because if you call edit_ifile(NULL), 269 * you're supposed to have saved curr_ifile yourself, 270 * and you'll restore it if necessary.) 271 */ 272 unsave_ifile(was_curr_ifile); 273 return (0); 274 } 275 276 filename = save(get_filename(ifile)); 277 /* 278 * See if LESSOPEN specifies an "alternate" file to open. 279 */ 280 alt_pipe = NULL; 281 alt_filename = open_altfile(filename, &f, &alt_pipe); 282 open_filename = (alt_filename != NULL) ? alt_filename : filename; 283 qopen_filename = shell_unquote(open_filename); 284 285 chflags = 0; 286 #if !SMALL 287 if (strcmp(open_filename, HELPFILE) == 0) 288 chflags |= CH_HELPFILE; 289 #endif /* !SMALL */ 290 if (alt_pipe != NULL) 291 { 292 /* 293 * The alternate "file" is actually a pipe. 294 * f has already been set to the file descriptor of the pipe 295 * in the call to open_altfile above. 296 * Keep the file descriptor open because it was opened 297 * via popen(), and pclose() wants to close it. 298 */ 299 chflags |= CH_POPENED; 300 } else if (strcmp(open_filename, "-") == 0) 301 { 302 /* 303 * Use standard input. 304 * Keep the file descriptor open because we can't reopen it. 305 */ 306 f = fd0; 307 chflags |= CH_KEEPOPEN; 308 /* 309 * Must switch stdin to BINARY mode. 310 */ 311 SET_BINARY(f); 312 #if MSDOS_COMPILER==DJGPPC 313 /* 314 * Setting stdin to binary by default causes 315 * Ctrl-C to not raise SIGINT. We must undo 316 * that side-effect. 317 */ 318 __djgpp_set_ctrl_c(1); 319 #endif 320 } else if ((parg.p_string = bad_file(open_filename)) != NULL) 321 { 322 /* 323 * It looks like a bad file. Don't try to open it. 324 */ 325 error("%s", &parg); 326 free(parg.p_string); 327 err1: 328 if (alt_filename != NULL) 329 { 330 close_altfile(alt_filename, filename, alt_pipe); 331 free(alt_filename); 332 } 333 del_ifile(ifile); 334 free(qopen_filename); 335 free(filename); 336 /* 337 * Re-open the current file. 338 */ 339 if (was_curr_ifile == ifile) 340 { 341 /* 342 * Whoops. The "current" ifile is the one we just deleted. 343 * Just give up. 344 */ 345 quit(QUIT_ERROR); 346 } 347 reedit_ifile(was_curr_ifile); 348 return (1); 349 } else if ((f = open(qopen_filename, OPEN_READ)) < 0) 350 { 351 /* 352 * Got an error trying to open it. 353 */ 354 parg.p_string = errno_message(filename); 355 error("%s", &parg); 356 free(parg.p_string); 357 goto err1; 358 } else 359 { 360 chflags |= CH_CANSEEK; 361 if (!force_open && !opened(ifile) && bin_file(f)) 362 { 363 /* 364 * Looks like a binary file. 365 * Ask user if we should proceed. 366 */ 367 parg.p_string = filename; 368 answer = query("\"%s\" may be a binary file. See it anyway? ", 369 &parg); 370 if (answer != 'y' && answer != 'Y') 371 { 372 close(f); 373 goto err1; 374 } 375 } 376 } 377 378 /* 379 * Get the new ifile. 380 * Get the saved position for the file. 381 */ 382 if (was_curr_ifile != NULL_IFILE) 383 { 384 old_ifile = was_curr_ifile; 385 unsave_ifile(was_curr_ifile); 386 } 387 curr_ifile = ifile; 388 curr_altfilename = alt_filename; 389 curr_altpipe = alt_pipe; 390 set_open(curr_ifile); /* File has been opened */ 391 get_pos(curr_ifile, &initial_scrpos); 392 new_file = TRUE; 393 ch_init(f, chflags); 394 395 if (!(chflags & CH_HELPFILE)) 396 { 397 #if LOGFILE 398 if (namelogfile != NULL && is_tty) 399 use_logfile(namelogfile); 400 #endif 401 #if HAVE_STAT_INO 402 /* Remember the i-number and device of the opened file. */ 403 { 404 struct stat statbuf; 405 int r = stat(qopen_filename, &statbuf); 406 if (r == 0) 407 { 408 curr_ino = statbuf.st_ino; 409 curr_dev = statbuf.st_dev; 410 } 411 } 412 #endif 413 if (every_first_cmd != NULL) 414 ungetsc(every_first_cmd); 415 } 416 417 free(qopen_filename); 418 no_display = !any_display; 419 flush(); 420 any_display = TRUE; 421 422 if (is_tty) 423 { 424 /* 425 * Output is to a real tty. 426 */ 427 428 /* 429 * Indicate there is nothing displayed yet. 430 */ 431 pos_clear(); 432 clr_linenum(); 433 #if HILITE_SEARCH 434 clr_hilite(); 435 #endif 436 cmd_addhist(ml_examine, filename); 437 if (no_display && errmsgs > 0) 438 { 439 /* 440 * We displayed some messages on error output 441 * (file descriptor 2; see error() function). 442 * Before erasing the screen contents, 443 * display the file name and wait for a keystroke. 444 */ 445 parg.p_string = filename; 446 error("%s", &parg); 447 } 448 } 449 free(filename); 450 return (0); 451 } 452 453 #if EXAMINE 454 /* 455 * Edit a space-separated list of files. 456 * For each filename in the list, enter it into the ifile list. 457 * Then edit the first one. 458 */ 459 public int 460 edit_list(filelist) 461 char *filelist; 462 { 463 IFILE save_ifile; 464 char *good_filename; 465 char *filename; 466 char *gfilelist; 467 char *gfilename; 468 struct textlist tl_files; 469 struct textlist tl_gfiles; 470 471 save_ifile = save_curr_ifile(); 472 good_filename = NULL; 473 474 /* 475 * Run thru each filename in the list. 476 * Try to glob the filename. 477 * If it doesn't expand, just try to open the filename. 478 * If it does expand, try to open each name in that list. 479 */ 480 init_textlist(&tl_files, filelist); 481 filename = NULL; 482 while ((filename = forw_textlist(&tl_files, filename)) != NULL) 483 { 484 gfilelist = lglob(filename); 485 init_textlist(&tl_gfiles, gfilelist); 486 gfilename = NULL; 487 while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL) 488 { 489 if (edit(gfilename) == 0 && good_filename == NULL) 490 good_filename = get_filename(curr_ifile); 491 } 492 free(gfilelist); 493 } 494 /* 495 * Edit the first valid filename in the list. 496 */ 497 if (good_filename == NULL) 498 { 499 unsave_ifile(save_ifile); 500 return (1); 501 } 502 if (get_ifile(good_filename, curr_ifile) == curr_ifile) 503 { 504 /* 505 * Trying to edit the current file; don't reopen it. 506 */ 507 unsave_ifile(save_ifile); 508 return (0); 509 } 510 reedit_ifile(save_ifile); 511 return (edit(good_filename)); 512 } 513 #endif /* EXAMINE */ 514 515 /* 516 * Edit the first file in the command line (ifile) list. 517 */ 518 public int 519 edit_first() 520 { 521 curr_ifile = NULL_IFILE; 522 return (edit_next(1)); 523 } 524 525 /* 526 * Edit the last file in the command line (ifile) list. 527 */ 528 public int 529 edit_last() 530 { 531 curr_ifile = NULL_IFILE; 532 return (edit_prev(1)); 533 } 534 535 536 /* 537 * Edit the n-th next or previous file in the command line (ifile) list. 538 */ 539 static int 540 edit_istep(h, n, dir) 541 IFILE h; 542 int n; 543 int dir; 544 { 545 IFILE next; 546 547 /* 548 * Skip n filenames, then try to edit each filename. 549 */ 550 for (;;) 551 { 552 next = (dir > 0) ? next_ifile(h) : prev_ifile(h); 553 if (--n < 0) 554 { 555 if (edit_ifile(h) == 0) 556 break; 557 } 558 if (next == NULL_IFILE) 559 { 560 /* 561 * Reached end of the ifile list. 562 */ 563 return (1); 564 } 565 if (ABORT_SIGS()) 566 { 567 /* 568 * Interrupt breaks out, if we're in a long 569 * list of files that can't be opened. 570 */ 571 return (1); 572 } 573 h = next; 574 } 575 /* 576 * Found a file that we can edit. 577 */ 578 return (0); 579 } 580 581 static int 582 edit_inext(h, n) 583 IFILE h; 584 int n; 585 { 586 return (edit_istep(h, n, +1)); 587 } 588 589 public int 590 edit_next(n) 591 int n; 592 { 593 return edit_istep(curr_ifile, n, +1); 594 } 595 596 static int 597 edit_iprev(h, n) 598 IFILE h; 599 int n; 600 { 601 return (edit_istep(h, n, -1)); 602 } 603 604 public int 605 edit_prev(n) 606 int n; 607 { 608 return edit_istep(curr_ifile, n, -1); 609 } 610 611 /* 612 * Edit a specific file in the command line (ifile) list. 613 */ 614 public int 615 edit_index(n) 616 int n; 617 { 618 IFILE h; 619 620 h = NULL_IFILE; 621 do 622 { 623 if ((h = next_ifile(h)) == NULL_IFILE) 624 { 625 /* 626 * Reached end of the list without finding it. 627 */ 628 return (1); 629 } 630 } while (get_index(h) != n); 631 632 return (edit_ifile(h)); 633 } 634 635 public IFILE 636 save_curr_ifile() 637 { 638 if (curr_ifile != NULL_IFILE) 639 hold_ifile(curr_ifile, 1); 640 return (curr_ifile); 641 } 642 643 public void 644 unsave_ifile(save_ifile) 645 IFILE save_ifile; 646 { 647 if (save_ifile != NULL_IFILE) 648 hold_ifile(save_ifile, -1); 649 } 650 651 /* 652 * Reedit the ifile which was previously open. 653 */ 654 public void 655 reedit_ifile(save_ifile) 656 IFILE save_ifile; 657 { 658 IFILE next; 659 IFILE prev; 660 661 /* 662 * Try to reopen the ifile. 663 * Note that opening it may fail (maybe the file was removed), 664 * in which case the ifile will be deleted from the list. 665 * So save the next and prev ifiles first. 666 */ 667 unsave_ifile(save_ifile); 668 next = next_ifile(save_ifile); 669 prev = prev_ifile(save_ifile); 670 if (edit_ifile(save_ifile) == 0) 671 return; 672 /* 673 * If can't reopen it, open the next input file in the list. 674 */ 675 if (next != NULL_IFILE && edit_inext(next, 0) == 0) 676 return; 677 /* 678 * If can't open THAT one, open the previous input file in the list. 679 */ 680 if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0) 681 return; 682 /* 683 * If can't even open that, we're stuck. Just quit. 684 */ 685 quit(QUIT_ERROR); 686 } 687 688 public void 689 reopen_curr_ifile() 690 { 691 IFILE save_ifile = save_curr_ifile(); 692 close_file(); 693 reedit_ifile(save_ifile); 694 } 695 696 /* 697 * Edit standard input. 698 */ 699 public int 700 edit_stdin() 701 { 702 if (isatty(fd0)) 703 { 704 error("Missing filename (\"less --help\" for help)", NULL_PARG); 705 quit(QUIT_OK); 706 } 707 return (edit("-")); 708 } 709 710 /* 711 * Copy a file directly to standard output. 712 * Used if standard output is not a tty. 713 */ 714 public void 715 cat_file() 716 { 717 register int c; 718 719 while ((c = ch_forw_get()) != EOI) 720 putchr(c); 721 flush(); 722 } 723 724 #if LOGFILE 725 726 /* 727 * If the user asked for a log file and our input file 728 * is standard input, create the log file. 729 * We take care not to blindly overwrite an existing file. 730 */ 731 public void 732 use_logfile(filename) 733 char *filename; 734 { 735 register int exists; 736 register int answer; 737 PARG parg; 738 739 if (ch_getflags() & CH_CANSEEK) 740 /* 741 * Can't currently use a log file on a file that can seek. 742 */ 743 return; 744 745 /* 746 * {{ We could use access() here. }} 747 */ 748 filename = shell_unquote(filename); 749 exists = open(filename, OPEN_READ); 750 close(exists); 751 exists = (exists >= 0); 752 753 /* 754 * Decide whether to overwrite the log file or append to it. 755 * If it doesn't exist we "overwrite" it. 756 */ 757 if (!exists || force_logfile) 758 { 759 /* 760 * Overwrite (or create) the log file. 761 */ 762 answer = 'O'; 763 } else 764 { 765 /* 766 * Ask user what to do. 767 */ 768 parg.p_string = filename; 769 answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg); 770 } 771 772 loop: 773 switch (answer) 774 { 775 case 'O': case 'o': 776 /* 777 * Overwrite: create the file. 778 */ 779 logfile = creat(filename, 0644); 780 break; 781 case 'A': case 'a': 782 /* 783 * Append: open the file and seek to the end. 784 */ 785 logfile = open(filename, OPEN_APPEND); 786 if (lseek(logfile, (off_t)0, SEEK_END) == BAD_LSEEK) 787 { 788 close(logfile); 789 logfile = -1; 790 } 791 break; 792 case 'D': case 'd': 793 /* 794 * Don't do anything. 795 */ 796 free(filename); 797 return; 798 case 'q': 799 quit(QUIT_OK); 800 /*NOTREACHED*/ 801 default: 802 /* 803 * Eh? 804 */ 805 answer = query("Overwrite, Append, or Don't log? (Type \"O\", \"A\", \"D\" or \"q\") ", NULL_PARG); 806 goto loop; 807 } 808 809 if (logfile < 0) 810 { 811 /* 812 * Error in opening logfile. 813 */ 814 parg.p_string = filename; 815 error("Cannot write to \"%s\"", &parg); 816 free(filename); 817 return; 818 } 819 free(filename); 820 SET_BINARY(logfile); 821 } 822 823 #endif 824