1 /* 2 * Copyright (C) 1984-2026 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 HAVE_SYS_WAIT_H 17 #include <sys/wait.h> 18 #endif 19 #if OS2 || __MVS__ || (defined WIFSIGNALED && defined WTERMSIG) 20 #include <signal.h> 21 #endif 22 23 public int fd0 = 0; 24 25 extern lbool new_file; 26 extern char *every_first_cmd; 27 extern int force_open; 28 extern lbool is_tty; 29 extern int sigs; 30 extern int hshift; 31 extern int want_filesize; 32 extern int modelines; 33 extern int show_preproc_error; 34 extern lbool read_error; 35 extern IFILE curr_ifile; 36 extern IFILE old_ifile; 37 extern struct scrpos initial_scrpos; 38 extern void *ml_examine; 39 extern POSITION soft_eof; 40 #if SPACES_IN_FILENAMES 41 extern char openquote; 42 extern char closequote; 43 #endif 44 45 #if LOGFILE 46 extern int logfile; 47 extern int force_logfile; 48 extern char *namelogfile; 49 #endif 50 51 #if HAVE_STAT_INO 52 public dev_t curr_dev; 53 public ino_t curr_ino; 54 #endif 55 56 /* 57 * Textlist functions deal with a list of words separated by spaces. 58 * init_textlist sets up a textlist structure. 59 * forw_textlist uses that structure to iterate thru the list of 60 * words, returning each one as a standard null-terminated string. 61 * back_textlist does the same, but runs thru the list backwards. 62 */ 63 public void init_textlist(struct textlist *tlist, mutable char *str) 64 { 65 char *s; 66 #if SPACES_IN_FILENAMES 67 lbool meta_quoted = FALSE; 68 lbool delim_quoted = FALSE; 69 constant char *esc = get_meta_escape(); 70 size_t esclen = strlen(esc); 71 #endif 72 73 tlist->string = skipsp(str); 74 tlist->endstring = tlist->string + strlen(tlist->string); 75 for (s = str; s < tlist->endstring; s++) 76 { 77 #if SPACES_IN_FILENAMES 78 if (meta_quoted) 79 { 80 meta_quoted = FALSE; 81 } else if (esclen > 0 && s + esclen < tlist->endstring && 82 strncmp(s, esc, esclen) == 0) 83 { 84 meta_quoted = TRUE; 85 s += esclen - 1; 86 } else if (delim_quoted) 87 { 88 if (*s == closequote) 89 delim_quoted = FALSE; 90 } else /* (!delim_quoted) */ 91 { 92 if (*s == openquote) 93 delim_quoted = TRUE; 94 else if (*s == ' ') 95 *s = '\0'; 96 } 97 #else 98 if (*s == ' ') 99 *s = '\0'; 100 #endif 101 } 102 } 103 104 public constant char * forw_textlist(constant struct textlist *tlist, constant char *prev) 105 { 106 constant 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 while (s < tlist->endstring && *s == '\0') 117 s++; 118 if (s >= tlist->endstring) 119 return (NULL); 120 return (s); 121 } 122 123 public constant char * back_textlist(constant struct textlist *tlist, constant char *prev) 124 { 125 constant char *s; 126 127 /* 128 * prev == NULL means return the last word in the list. 129 * Otherwise, return the word before "prev". 130 */ 131 if (prev == NULL) 132 s = tlist->endstring; 133 else if (prev <= tlist->string) 134 return (NULL); 135 else 136 s = prev - 1; 137 while (*s == '\0') 138 s--; 139 if (s <= tlist->string) 140 return (NULL); 141 while (s[-1] != '\0' && s > tlist->string) 142 s--; 143 return (s); 144 } 145 146 /* 147 * Parse a single option setting in a modeline. 148 */ 149 static void modeline_option(constant char *str, size_t opt_len) 150 { 151 struct mloption { constant char *opt_name; void (*opt_func)(constant char*,size_t); }; 152 struct mloption options[] = { 153 { "ts=", set_tabs }, 154 { "tabstop=", set_tabs }, 155 { NULL, NULL } 156 }; 157 struct mloption *opt; 158 for (opt = options; opt->opt_name != NULL; opt++) 159 { 160 size_t name_len = strlen(opt->opt_name); 161 if (opt_len > name_len && strncmp(str, opt->opt_name, name_len) == 0) 162 { 163 (*opt->opt_func)(str + name_len, opt_len - name_len); 164 break; 165 } 166 } 167 } 168 169 /* 170 * String length, terminated by option separator (space or colon). 171 * Space/colon can be escaped with backspace. 172 */ 173 static size_t modeline_option_len(constant char *str) 174 { 175 lbool esc = FALSE; 176 constant char *s; 177 for (s = str; *s != '\0'; s++) 178 { 179 if (esc) 180 esc = FALSE; 181 else if (*s == '\\') 182 esc = TRUE; 183 else if (*s == ' ' || *s == ':') /* separator */ 184 break; 185 } 186 return ptr_diff(s, str); 187 } 188 189 /* 190 * Parse colon- or space-separated option settings in a modeline. 191 */ 192 static void modeline_options(constant char *str, char end_char) 193 { 194 for (;;) 195 { 196 size_t opt_len; 197 str = skipspc(str); 198 if (*str == '\0' || *str == end_char) 199 break; 200 opt_len = modeline_option_len(str); 201 modeline_option(str, opt_len); 202 str += opt_len; 203 if (*str != '\0') 204 str += 1; /* skip past the separator */ 205 } 206 } 207 208 /* 209 * See if there is a modeline string in a line. 210 */ 211 static void check_modeline(constant char *line) 212 { 213 static constant char *pgms[] = { "less:", "vim:", "vi:", "ex:", NULL }; 214 constant char **pgm; 215 for (pgm = pgms; *pgm != NULL; ++pgm) 216 { 217 constant char *pline = line; 218 for (;;) 219 { 220 constant char *str; 221 pline = strstr(pline, *pgm); 222 if (pline == NULL) /* pgm is not in this line */ 223 break; 224 str = skipspc(pline + strlen(*pgm)); 225 if (pline == line || pline[-1] == ' ') 226 { 227 if (strncmp(str, "set ", 4) == 0) 228 modeline_options(str+4, ':'); 229 else if (pgm != &pgms[0]) /* "less:" requires "set" */ 230 modeline_options(str, '\0'); 231 break; 232 } 233 /* Continue searching the rest of the line. */ 234 pline = str; 235 } 236 } 237 } 238 239 /* 240 * Read lines from start of file and check if any are modelines. 241 */ 242 static void check_modelines(void) 243 { 244 POSITION pos = ch_zero(); 245 int i; 246 for (i = 0; i < modelines; i++) 247 { 248 constant char *line; 249 size_t line_len; 250 if (ABORT_SIGS()) 251 return; 252 pos = forw_raw_line(pos, &line, &line_len); 253 if (pos == NULL_POSITION) 254 break; 255 check_modeline(line); 256 } 257 } 258 259 /* 260 * Close a pipe opened via popen. 261 */ 262 static void close_pipe(FILE *pipefd) 263 { 264 int status; 265 char *p; 266 PARG parg; 267 int sig = 0; 268 269 if (pipefd == NULL) 270 return; 271 #if OS2 272 /* 273 * The pclose function of OS/2 emx sometimes fails. 274 * Send SIGINT to the piped process before closing it. 275 */ 276 kill(pipefd->_pid, SIGINT); 277 #endif 278 status = pclose(pipefd); 279 if (status == -1) 280 { 281 /* An internal error in 'less', not a preprocessor error. */ 282 p = errno_message("pclose"); 283 parg.p_string = p; 284 error("%s", &parg); 285 free(p); 286 return; 287 } 288 if (!show_preproc_error) 289 return; 290 #if defined WIFEXITED && defined WEXITSTATUS 291 if (WIFEXITED(status)) 292 { 293 int s = WEXITSTATUS(status); 294 if (s == 0) 295 return; 296 if (s <= 128) 297 { 298 parg.p_int = s; 299 error("Input preprocessor failed (status %d)", &parg); 300 return; 301 } 302 /* 303 * popen invoked the shell, which likely last ran a 304 * program that terminated due to a signal. 305 * Assume the longstanding tradition (allowed but not 306 * required by POSIX) of adding 128 to the signal. 307 * Many shells use last command optimization, i.e., 308 * they exec the last command instead of forking and 309 * waiting for it, and in that case the more-reliable 310 * WIFSIGNALED code below will be used. 311 */ 312 sig = s - 128; 313 } 314 #endif 315 #if defined WIFSIGNALED && defined WTERMSIG 316 if (WIFSIGNALED(status)) 317 sig = WTERMSIG(status); 318 #endif 319 if (sig != 0) 320 { 321 if ( 322 #ifdef SIGPIPE 323 sig != SIGPIPE || 324 #endif 325 ch_length() != NULL_POSITION) 326 { 327 parg.p_string = signal_message(sig); 328 error("Input preprocessor terminated: %s", &parg); 329 } 330 return; 331 } 332 if (status != 0) 333 { 334 parg.p_int = status; 335 error("Input preprocessor exited with status %x", &parg); 336 } 337 } 338 339 /* 340 * Drain and close an input pipe if needed. 341 */ 342 public void close_altpipe(IFILE ifile) 343 { 344 FILE *altpipe = get_altpipe(ifile); 345 if (altpipe != NULL && !(ch_getflags() & CH_KEEPOPEN)) 346 { 347 close_pipe(altpipe); 348 set_altpipe(ifile, NULL); 349 } 350 } 351 352 /* 353 * Check for error status from the current altpipe. 354 * May or may not close the pipe. 355 */ 356 public void check_altpipe_error(void) 357 { 358 if (!show_preproc_error) 359 return; 360 if (curr_ifile != NULL_IFILE && get_altfilename(curr_ifile) != NULL) 361 close_altpipe(curr_ifile); 362 } 363 364 /* 365 * Close the current input file. 366 */ 367 static void close_file(void) 368 { 369 struct scrpos scrpos; 370 constant char *altfilename; 371 372 if (curr_ifile == NULL_IFILE) 373 return; 374 375 /* 376 * Save the current position so that we can return to 377 * the same position if we edit this file again. 378 */ 379 if (is_tty) 380 { 381 get_scrpos(&scrpos, TOP); 382 if (scrpos.pos != NULL_POSITION) 383 { 384 store_pos(curr_ifile, &scrpos); 385 lastmark(); 386 } 387 } 388 /* 389 * Close the file descriptor, unless it is a pipe. 390 */ 391 ch_close(); 392 /* 393 * If we opened a file using an alternate name, 394 * do special stuff to close it. 395 */ 396 altfilename = get_altfilename(curr_ifile); 397 if (altfilename != NULL) 398 { 399 close_altpipe(curr_ifile); 400 close_altfile(altfilename, get_filename(curr_ifile)); 401 set_altfilename(curr_ifile, NULL); 402 } 403 curr_ifile = NULL_IFILE; 404 #if HAVE_STAT_INO 405 curr_ino = curr_dev = 0; 406 #endif 407 } 408 409 /* 410 * Edit a new file (given its name). 411 * Filename == "-" means standard input. 412 * Filename == NULL means just close the current file. 413 */ 414 public int edit(constant char *filename) 415 { 416 if (filename == NULL) 417 return (edit_ifile(NULL_IFILE)); 418 return (edit_ifile(get_ifile(filename, curr_ifile))); 419 } 420 421 /* 422 * Clean up what edit_ifile did before error return. 423 */ 424 static int edit_error(constant char *filename, constant char *alt_filename, void *altpipe, IFILE ifile) 425 { 426 if (alt_filename != NULL) 427 { 428 close_pipe(altpipe); 429 close_altfile(alt_filename, filename); 430 free((char*)alt_filename); /* FIXME: WTF? */ 431 } 432 del_ifile(ifile); 433 /* 434 * Re-open the current file. 435 */ 436 if (curr_ifile == ifile) 437 { 438 /* 439 * Whoops. The "current" ifile is the one we just deleted. 440 * Just give up. 441 */ 442 quit(QUIT_ERROR); 443 } 444 return (1); 445 } 446 447 /* 448 * Edit a new file (given its IFILE). 449 * ifile == NULL means just close the current file. 450 */ 451 public int edit_ifile(IFILE ifile) 452 { 453 int f; 454 int answer; 455 int chflags; 456 constant char *filename; 457 constant char *open_filename; 458 char *alt_filename; 459 void *altpipe; 460 IFILE was_curr_ifile; 461 char *p; 462 PARG parg; 463 ssize_t nread = 0; 464 465 if (ifile == curr_ifile) 466 { 467 /* 468 * Already have the correct file open. 469 */ 470 return (0); 471 } 472 new_file = TRUE; 473 474 if (ifile != NULL_IFILE) 475 { 476 /* 477 * See if LESSOPEN specifies an "alternate" file to open. 478 */ 479 filename = get_filename(ifile); 480 altpipe = get_altpipe(ifile); 481 if (altpipe != NULL) 482 { 483 /* 484 * File is already open. 485 * chflags and f are not used by ch_init if ifile has 486 * filestate which should be the case if we're here. 487 * Set them here to avoid uninitialized variable warnings. 488 */ 489 chflags = 0; 490 f = -1; 491 alt_filename = get_altfilename(ifile); 492 open_filename = (alt_filename != NULL) ? alt_filename : filename; 493 } else 494 { 495 if (strcmp(filename, FAKE_HELPFILE) == 0 || 496 strcmp(filename, FAKE_EMPTYFILE) == 0) 497 alt_filename = NULL; 498 else 499 alt_filename = open_altfile(filename, &f, &altpipe); 500 501 open_filename = (alt_filename != NULL) ? alt_filename : filename; 502 503 chflags = 0; 504 if (altpipe != NULL) 505 { 506 /* 507 * The alternate "file" is actually a pipe. 508 * f has already been set to the file descriptor of the pipe 509 * in the call to open_altfile above. 510 * Keep the file descriptor open because it was opened 511 * via popen(), and pclose() wants to close it. 512 */ 513 chflags |= CH_POPENED; 514 if (strcmp(filename, "-") == 0) 515 chflags |= CH_KEEPOPEN; 516 } else if (strcmp(filename, "-") == 0) 517 { 518 /* 519 * Use standard input. 520 * Keep the file descriptor open because we can't reopen it. 521 */ 522 f = fd0; 523 chflags |= CH_KEEPOPEN; 524 /* 525 * Must switch stdin to BINARY mode. 526 */ 527 SET_BINARY(f); 528 #if MSDOS_COMPILER==DJGPPC 529 /* 530 * Setting stdin to binary by default causes 531 * Ctrl-C to not raise SIGINT. We must undo 532 * that side-effect. 533 */ 534 __djgpp_set_ctrl_c(1); 535 #endif 536 } else if (strcmp(open_filename, FAKE_EMPTYFILE) == 0) 537 { 538 f = -1; 539 chflags |= CH_NODATA; 540 } else if (strcmp(open_filename, FAKE_HELPFILE) == 0) 541 { 542 f = -1; 543 chflags |= CH_HELPFILE; 544 } else if ((p = bad_file(open_filename)) != NULL) 545 { 546 /* 547 * It looks like a bad file. Don't try to open it. 548 */ 549 parg.p_string = p; 550 error("%s", &parg); 551 free(p); 552 return edit_error(filename, alt_filename, altpipe, ifile); 553 } else if ((f = iopen(open_filename, OPEN_READ)) < 0) 554 { 555 /* 556 * Got an error trying to open it. 557 */ 558 char *p = errno_message(filename); 559 parg.p_string = p; 560 error("%s", &parg); 561 free(p); 562 return edit_error(filename, alt_filename, altpipe, ifile); 563 } else 564 { 565 chflags |= CH_CANSEEK; 566 if (bin_file(f, &nread) && is_tty && !force_open && !opened(ifile)) 567 { 568 /* 569 * Looks like a binary file. 570 * Ask user if we should proceed. 571 */ 572 parg.p_string = filename; 573 answer = query("\"%s\" may be a binary file. See it anyway? ", &parg); 574 if (answer != 'y' && answer != 'Y') 575 { 576 close(f); 577 return edit_error(filename, alt_filename, altpipe, ifile); 578 } 579 } 580 } 581 } 582 if (!force_open && f >= 0 && isatty(f)) 583 { 584 PARG parg; 585 parg.p_string = filename; 586 error("%s is a terminal (use -f to open it)", &parg); 587 return edit_error(filename, alt_filename, altpipe, ifile); 588 } 589 } 590 591 #if LOGFILE 592 end_logfile(); 593 #endif 594 was_curr_ifile = save_curr_ifile(); 595 if (curr_ifile != NULL_IFILE) 596 { 597 int was_helpfile = (ch_getflags() & CH_HELPFILE); 598 close_file(); 599 if (was_helpfile && held_ifile(was_curr_ifile) <= 1) 600 { 601 /* 602 * Don't keep the help file in the ifile list. 603 */ 604 del_ifile(was_curr_ifile); 605 was_curr_ifile = NULL_IFILE; 606 } 607 } 608 unsave_ifile(was_curr_ifile); 609 610 if (ifile == NULL_IFILE) 611 { 612 /* 613 * No new file to open. 614 * (Don't set old_ifile, because if you call edit_ifile(NULL), 615 * you're supposed to have saved curr_ifile yourself, 616 * and you'll restore it if necessary.) 617 */ 618 return (0); 619 } 620 621 /* 622 * Set up the new ifile. 623 * Get the saved position for the file. 624 */ 625 curr_ifile = ifile; 626 soft_eof = NULL_POSITION; 627 set_altfilename(curr_ifile, alt_filename); 628 set_altpipe(curr_ifile, altpipe); 629 set_open(curr_ifile); /* File has been opened */ 630 get_pos(curr_ifile, &initial_scrpos); 631 ch_init(f, chflags, nread); 632 check_modelines(); 633 634 if (!(chflags & CH_HELPFILE)) 635 { 636 if (was_curr_ifile != NULL_IFILE) 637 old_ifile = was_curr_ifile; 638 #if LOGFILE 639 if (namelogfile != NULL && is_tty) 640 use_logfile(namelogfile); 641 #endif 642 #if HAVE_STAT_INO 643 /* Remember the i-number and device of the opened file. */ 644 if (strcmp(open_filename, "-") != 0) 645 { 646 struct stat statbuf; 647 int r = stat(open_filename, &statbuf); 648 if (r == 0) 649 { 650 curr_ino = statbuf.st_ino; 651 curr_dev = statbuf.st_dev; 652 } 653 } 654 #endif 655 if (every_first_cmd != NULL) 656 { 657 ungetsc(every_first_cmd); 658 ungetcc_end_command(); 659 } 660 } 661 662 flush(); 663 664 if (is_tty) 665 { 666 /* 667 * Output is to a real tty. 668 */ 669 670 /* 671 * Indicate there is nothing displayed yet. 672 */ 673 pos_clear(); 674 clr_linenum(); 675 #if HILITE_SEARCH 676 clr_hilite(); 677 #endif 678 undo_osc8(); 679 hshift = 0; 680 read_error = FALSE; 681 if (strcmp(filename, FAKE_HELPFILE) && strcmp(filename, FAKE_EMPTYFILE)) 682 { 683 char *qfilename = shell_quote(filename); 684 cmd_addhist(ml_examine, qfilename, 1); 685 free(qfilename); 686 } 687 if (want_filesize) 688 scan_eof(); 689 set_header(ch_zero()); 690 } 691 return (0); 692 } 693 694 /* 695 * Edit a space-separated list of files. 696 * For each filename in the list, enter it into the ifile list. 697 * Then edit the first one. 698 */ 699 public int edit_list(char *filelist) 700 { 701 IFILE save_ifile; 702 constant char *good_filename; 703 constant char *filename; 704 char *gfilelist; 705 constant char *gfilename; 706 char *qfilename; 707 struct textlist tl_files; 708 struct textlist tl_gfiles; 709 710 save_ifile = save_curr_ifile(); 711 good_filename = NULL; 712 713 /* 714 * Run thru each filename in the list. 715 * Try to glob the filename. 716 * If it doesn't expand, just try to open the filename. 717 * If it does expand, try to open each name in that list. 718 */ 719 init_textlist(&tl_files, filelist); 720 filename = NULL; 721 while ((filename = forw_textlist(&tl_files, filename)) != NULL) 722 { 723 gfilelist = lglob(filename); 724 init_textlist(&tl_gfiles, gfilelist); 725 gfilename = NULL; 726 while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL) 727 { 728 qfilename = shell_unquote(gfilename); 729 if (edit(qfilename) == 0 && good_filename == NULL) 730 good_filename = get_filename(curr_ifile); 731 free(qfilename); 732 } 733 free(gfilelist); 734 } 735 /* 736 * Edit the first valid filename in the list. 737 */ 738 if (good_filename == NULL) 739 { 740 unsave_ifile(save_ifile); 741 return (1); 742 } 743 if (get_ifile(good_filename, curr_ifile) == curr_ifile) 744 { 745 /* 746 * Trying to edit the current file; don't reopen it. 747 */ 748 unsave_ifile(save_ifile); 749 return (0); 750 } 751 reedit_ifile(save_ifile); 752 return (edit(good_filename)); 753 } 754 755 /* 756 * Edit the first file in the command line (ifile) list. 757 */ 758 public int edit_first(void) 759 { 760 if (nifile() == 0) 761 return (edit_stdin()); 762 curr_ifile = NULL_IFILE; 763 return (edit_next(1)); 764 } 765 766 /* 767 * Edit the last file in the command line (ifile) list. 768 */ 769 public int edit_last(void) 770 { 771 curr_ifile = NULL_IFILE; 772 return (edit_prev(1)); 773 } 774 775 776 /* 777 * Edit the n-th next or previous file in the command line (ifile) list. 778 */ 779 static int edit_istep(IFILE h, int n, int dir) 780 { 781 IFILE next; 782 783 /* 784 * Skip n filenames, then try to edit each filename. 785 */ 786 for (;;) 787 { 788 next = (dir > 0) ? next_ifile(h) : prev_ifile(h); 789 if (--n < 0) 790 { 791 if (edit_ifile(h) == 0) 792 break; 793 } 794 if (next == NULL_IFILE) 795 { 796 /* 797 * Reached end of the ifile list. 798 */ 799 return (1); 800 } 801 if (ABORT_SIGS()) 802 { 803 /* 804 * Interrupt breaks out, if we're in a long 805 * list of files that can't be opened. 806 */ 807 return (1); 808 } 809 h = next; 810 } 811 /* 812 * Found a file that we can edit. 813 */ 814 return (0); 815 } 816 817 static int edit_inext(IFILE h, int n) 818 { 819 return (edit_istep(h, n, +1)); 820 } 821 822 public int edit_next(int n) 823 { 824 return edit_istep(curr_ifile, n, +1); 825 } 826 827 static int edit_iprev(IFILE h, int n) 828 { 829 return (edit_istep(h, n, -1)); 830 } 831 832 public int edit_prev(int n) 833 { 834 return edit_istep(curr_ifile, n, -1); 835 } 836 837 /* 838 * Edit a specific file in the command line (ifile) list. 839 */ 840 public int edit_index(int n) 841 { 842 IFILE h; 843 844 h = NULL_IFILE; 845 do 846 { 847 if ((h = next_ifile(h)) == NULL_IFILE) 848 { 849 /* 850 * Reached end of the list without finding it. 851 */ 852 return (1); 853 } 854 } while (get_index(h) != n); 855 856 return (edit_ifile(h)); 857 } 858 859 public IFILE save_curr_ifile(void) 860 { 861 if (curr_ifile != NULL_IFILE) 862 hold_ifile(curr_ifile, 1); 863 return (curr_ifile); 864 } 865 866 public void unsave_ifile(IFILE save_ifile) 867 { 868 if (save_ifile != NULL_IFILE) 869 hold_ifile(save_ifile, -1); 870 } 871 872 /* 873 * Reedit the ifile which was previously open. 874 */ 875 public void reedit_ifile(IFILE save_ifile) 876 { 877 IFILE next; 878 IFILE prev; 879 880 /* 881 * Try to reopen the ifile. 882 * Note that opening it may fail (maybe the file was removed), 883 * in which case the ifile will be deleted from the list. 884 * So save the next and prev ifiles first. 885 */ 886 unsave_ifile(save_ifile); 887 next = next_ifile(save_ifile); 888 prev = prev_ifile(save_ifile); 889 if (edit_ifile(save_ifile) == 0) 890 return; 891 /* 892 * If can't reopen it, open the next input file in the list. 893 */ 894 if (next != NULL_IFILE && edit_inext(next, 0) == 0) 895 return; 896 /* 897 * If can't open THAT one, open the previous input file in the list. 898 */ 899 if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0) 900 return; 901 /* 902 * If can't even open that, we're stuck. Just quit. 903 */ 904 quit(QUIT_ERROR); 905 } 906 907 public void reopen_curr_ifile(void) 908 { 909 IFILE save_ifile = save_curr_ifile(); 910 close_file(); 911 reedit_ifile(save_ifile); 912 } 913 914 /* 915 * Edit standard input. 916 */ 917 public int edit_stdin(void) 918 { 919 if (isatty(fd0)) 920 { 921 error("Missing filename (\"less --help\" for help)", NULL_PARG); 922 quit(QUIT_OK); 923 } 924 return (edit("-")); 925 } 926 927 /* 928 * Copy a file directly to standard output. 929 * Used if standard output is not a tty. 930 */ 931 public void cat_file(void) 932 { 933 int c; 934 935 while ((c = ch_forw_get()) != EOI) 936 putchr(c); 937 flush(); 938 } 939 940 #if LOGFILE 941 942 #define OVERWRITE_OPTIONS "Overwrite, Append, Don't log, or Quit?" 943 944 /* 945 * If the user asked for a log file and our input file 946 * is standard input, create the log file. 947 * We take care not to blindly overwrite an existing file. 948 */ 949 public void use_logfile(constant char *filename) 950 { 951 int exists; 952 int answer; 953 PARG parg; 954 955 if (ch_getflags() & CH_CANSEEK) 956 /* 957 * Can't currently use a log file on a file that can seek. 958 */ 959 return; 960 961 /* 962 * {{ We could use access() here. }} 963 */ 964 exists = open(filename, OPEN_READ); 965 if (exists >= 0) 966 close(exists); 967 exists = (exists >= 0); 968 969 /* 970 * Decide whether to overwrite the log file or append to it. 971 * If it doesn't exist we "overwrite" it. 972 */ 973 if (!exists || force_logfile) 974 { 975 /* 976 * Overwrite (or create) the log file. 977 */ 978 answer = 'O'; 979 } else 980 { 981 /* 982 * Ask user what to do. 983 */ 984 parg.p_string = filename; 985 answer = query("Warning: \"%s\" exists; "OVERWRITE_OPTIONS" ", &parg); 986 } 987 988 loop: 989 switch (answer) 990 { 991 case 'O': case 'o': 992 /* 993 * Overwrite: create the file. 994 */ 995 logfile = creat(filename, CREAT_RW); 996 break; 997 case 'A': case 'a': 998 /* 999 * Append: open the file and seek to the end. 1000 */ 1001 logfile = open(filename, OPEN_APPEND); 1002 if (less_lseek(logfile, (less_off_t)0, SEEK_END) == BAD_LSEEK) 1003 { 1004 close(logfile); 1005 logfile = -1; 1006 } 1007 break; 1008 case 'D': case 'd': 1009 /* 1010 * Don't do anything. 1011 */ 1012 return; 1013 default: 1014 /* 1015 * Eh? 1016 */ 1017 1018 answer = query(OVERWRITE_OPTIONS" (Type \"O\", \"A\", \"D\" or \"Q\") ", NULL_PARG); 1019 goto loop; 1020 } 1021 1022 if (logfile < 0) 1023 { 1024 /* 1025 * Error in opening logfile. 1026 */ 1027 parg.p_string = filename; 1028 error("Cannot write to \"%s\"", &parg); 1029 return; 1030 } 1031 SET_BINARY(logfile); 1032 } 1033 1034 #endif 1035