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