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