1 /* 2 * Copyright (C) 1984-2012 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 = 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 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 /* 454 * Edit a space-separated list of files. 455 * For each filename in the list, enter it into the ifile list. 456 * Then edit the first one. 457 */ 458 public int 459 edit_list(filelist) 460 char *filelist; 461 { 462 IFILE save_ifile; 463 char *good_filename; 464 char *filename; 465 char *gfilelist; 466 char *gfilename; 467 struct textlist tl_files; 468 struct textlist tl_gfiles; 469 470 save_ifile = save_curr_ifile(); 471 good_filename = NULL; 472 473 /* 474 * Run thru each filename in the list. 475 * Try to glob the filename. 476 * If it doesn't expand, just try to open the filename. 477 * If it does expand, try to open each name in that list. 478 */ 479 init_textlist(&tl_files, filelist); 480 filename = NULL; 481 while ((filename = forw_textlist(&tl_files, filename)) != NULL) 482 { 483 gfilelist = lglob(filename); 484 init_textlist(&tl_gfiles, gfilelist); 485 gfilename = NULL; 486 while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL) 487 { 488 if (edit(gfilename) == 0 && good_filename == NULL) 489 good_filename = get_filename(curr_ifile); 490 } 491 free(gfilelist); 492 } 493 /* 494 * Edit the first valid filename in the list. 495 */ 496 if (good_filename == NULL) 497 { 498 unsave_ifile(save_ifile); 499 return (1); 500 } 501 if (get_ifile(good_filename, curr_ifile) == curr_ifile) 502 { 503 /* 504 * Trying to edit the current file; don't reopen it. 505 */ 506 unsave_ifile(save_ifile); 507 return (0); 508 } 509 reedit_ifile(save_ifile); 510 return (edit(good_filename)); 511 } 512 513 /* 514 * Edit the first file in the command line (ifile) list. 515 */ 516 public int 517 edit_first() 518 { 519 curr_ifile = NULL_IFILE; 520 return (edit_next(1)); 521 } 522 523 /* 524 * Edit the last file in the command line (ifile) list. 525 */ 526 public int 527 edit_last() 528 { 529 curr_ifile = NULL_IFILE; 530 return (edit_prev(1)); 531 } 532 533 534 /* 535 * Edit the n-th next or previous file in the command line (ifile) list. 536 */ 537 static int 538 edit_istep(h, n, dir) 539 IFILE h; 540 int n; 541 int dir; 542 { 543 IFILE next; 544 545 /* 546 * Skip n filenames, then try to edit each filename. 547 */ 548 for (;;) 549 { 550 next = (dir > 0) ? next_ifile(h) : prev_ifile(h); 551 if (--n < 0) 552 { 553 if (edit_ifile(h) == 0) 554 break; 555 } 556 if (next == NULL_IFILE) 557 { 558 /* 559 * Reached end of the ifile list. 560 */ 561 return (1); 562 } 563 if (ABORT_SIGS()) 564 { 565 /* 566 * Interrupt breaks out, if we're in a long 567 * list of files that can't be opened. 568 */ 569 return (1); 570 } 571 h = next; 572 } 573 /* 574 * Found a file that we can edit. 575 */ 576 return (0); 577 } 578 579 static int 580 edit_inext(h, n) 581 IFILE h; 582 int n; 583 { 584 return (edit_istep(h, n, +1)); 585 } 586 587 public int 588 edit_next(n) 589 int n; 590 { 591 return edit_istep(curr_ifile, n, +1); 592 } 593 594 static int 595 edit_iprev(h, n) 596 IFILE h; 597 int n; 598 { 599 return (edit_istep(h, n, -1)); 600 } 601 602 public int 603 edit_prev(n) 604 int n; 605 { 606 return edit_istep(curr_ifile, n, -1); 607 } 608 609 /* 610 * Edit a specific file in the command line (ifile) list. 611 */ 612 public int 613 edit_index(n) 614 int n; 615 { 616 IFILE h; 617 618 h = NULL_IFILE; 619 do 620 { 621 if ((h = next_ifile(h)) == NULL_IFILE) 622 { 623 /* 624 * Reached end of the list without finding it. 625 */ 626 return (1); 627 } 628 } while (get_index(h) != n); 629 630 return (edit_ifile(h)); 631 } 632 633 public IFILE 634 save_curr_ifile() 635 { 636 if (curr_ifile != NULL_IFILE) 637 hold_ifile(curr_ifile, 1); 638 return (curr_ifile); 639 } 640 641 public void 642 unsave_ifile(save_ifile) 643 IFILE save_ifile; 644 { 645 if (save_ifile != NULL_IFILE) 646 hold_ifile(save_ifile, -1); 647 } 648 649 /* 650 * Reedit the ifile which was previously open. 651 */ 652 public void 653 reedit_ifile(save_ifile) 654 IFILE save_ifile; 655 { 656 IFILE next; 657 IFILE prev; 658 659 /* 660 * Try to reopen the ifile. 661 * Note that opening it may fail (maybe the file was removed), 662 * in which case the ifile will be deleted from the list. 663 * So save the next and prev ifiles first. 664 */ 665 unsave_ifile(save_ifile); 666 next = next_ifile(save_ifile); 667 prev = prev_ifile(save_ifile); 668 if (edit_ifile(save_ifile) == 0) 669 return; 670 /* 671 * If can't reopen it, open the next input file in the list. 672 */ 673 if (next != NULL_IFILE && edit_inext(next, 0) == 0) 674 return; 675 /* 676 * If can't open THAT one, open the previous input file in the list. 677 */ 678 if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0) 679 return; 680 /* 681 * If can't even open that, we're stuck. Just quit. 682 */ 683 quit(QUIT_ERROR); 684 } 685 686 public void 687 reopen_curr_ifile() 688 { 689 IFILE save_ifile = save_curr_ifile(); 690 close_file(); 691 reedit_ifile(save_ifile); 692 } 693 694 /* 695 * Edit standard input. 696 */ 697 public int 698 edit_stdin() 699 { 700 if (isatty(fd0)) 701 { 702 error("Missing filename (\"less --help\" for help)", NULL_PARG); 703 quit(QUIT_OK); 704 } 705 return (edit("-")); 706 } 707 708 /* 709 * Copy a file directly to standard output. 710 * Used if standard output is not a tty. 711 */ 712 public void 713 cat_file() 714 { 715 register int c; 716 717 while ((c = ch_forw_get()) != EOI) 718 putchr(c); 719 flush(); 720 } 721 722 #if LOGFILE 723 724 /* 725 * If the user asked for a log file and our input file 726 * is standard input, create the log file. 727 * We take care not to blindly overwrite an existing file. 728 */ 729 public void 730 use_logfile(filename) 731 char *filename; 732 { 733 register int exists; 734 register int answer; 735 PARG parg; 736 737 if (ch_getflags() & CH_CANSEEK) 738 /* 739 * Can't currently use a log file on a file that can seek. 740 */ 741 return; 742 743 /* 744 * {{ We could use access() here. }} 745 */ 746 filename = shell_unquote(filename); 747 exists = open(filename, OPEN_READ); 748 close(exists); 749 exists = (exists >= 0); 750 751 /* 752 * Decide whether to overwrite the log file or append to it. 753 * If it doesn't exist we "overwrite" it. 754 */ 755 if (!exists || force_logfile) 756 { 757 /* 758 * Overwrite (or create) the log file. 759 */ 760 answer = 'O'; 761 } else 762 { 763 /* 764 * Ask user what to do. 765 */ 766 parg.p_string = filename; 767 answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg); 768 } 769 770 loop: 771 switch (answer) 772 { 773 case 'O': case 'o': 774 /* 775 * Overwrite: create the file. 776 */ 777 logfile = creat(filename, 0644); 778 break; 779 case 'A': case 'a': 780 /* 781 * Append: open the file and seek to the end. 782 */ 783 logfile = open(filename, OPEN_APPEND); 784 if (lseek(logfile, (off_t)0, SEEK_END) == BAD_LSEEK) 785 { 786 close(logfile); 787 logfile = -1; 788 } 789 break; 790 case 'D': case 'd': 791 /* 792 * Don't do anything. 793 */ 794 free(filename); 795 return; 796 case 'q': 797 quit(QUIT_OK); 798 /*NOTREACHED*/ 799 default: 800 /* 801 * Eh? 802 */ 803 answer = query("Overwrite, Append, or Don't log? (Type \"O\", \"A\", \"D\" or \"q\") ", NULL_PARG); 804 goto loop; 805 } 806 807 if (logfile < 0) 808 { 809 /* 810 * Error in opening logfile. 811 */ 812 parg.p_string = filename; 813 error("Cannot write to \"%s\"", &parg); 814 free(filename); 815 return; 816 } 817 free(filename); 818 SET_BINARY(logfile); 819 } 820 821 #endif 822