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 public 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(LM(Input_preprocessor_failed), &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(LM(Input_preprocessor_terminated_X), &parg); 329 } 330 return; 331 } 332 if (status != 0) 333 { 334 parg.p_int = status; 335 error(LM(Input_preprocessor_exited_with_status_X), &parg); 336 } 337 } 338 339 /* 340 * Drain and close an input pipe if needed. 341 */ 342 static 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, FILE *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 FILE *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(LM(X_may_be_a_binary_file), &parg); 574 if (answer == 'q') 575 quit(QUIT_OK); 576 if (answer != 'y' && answer != 'Y') 577 { 578 close(f); 579 return edit_error(filename, alt_filename, altpipe, ifile); 580 } 581 } 582 } 583 } 584 if (!force_open && f >= 0 && isatty(f)) 585 { 586 PARG parg; 587 parg.p_string = filename; 588 error(LM(X_is_a_terminal), &parg); 589 return edit_error(filename, alt_filename, altpipe, ifile); 590 } 591 } 592 593 #if LOGFILE 594 end_logfile(); 595 #endif 596 was_curr_ifile = save_curr_ifile(); 597 if (curr_ifile != NULL_IFILE) 598 { 599 int was_helpfile = (ch_getflags() & CH_HELPFILE); 600 close_file(); 601 if (was_helpfile && held_ifile(was_curr_ifile) <= 1) 602 { 603 /* 604 * Don't keep the help file in the ifile list. 605 */ 606 del_ifile(was_curr_ifile); 607 was_curr_ifile = NULL_IFILE; 608 } 609 } 610 unsave_ifile(was_curr_ifile); 611 612 if (ifile == NULL_IFILE) 613 { 614 /* 615 * No new file to open. 616 * (Don't set old_ifile, because if you call edit_ifile(NULL), 617 * you're supposed to have saved curr_ifile yourself, 618 * and you'll restore it if necessary.) 619 */ 620 return (0); 621 } 622 623 /* 624 * Set up the new ifile. 625 * Get the saved position for the file. 626 */ 627 curr_ifile = ifile; 628 soft_eof = NULL_POSITION; 629 set_altfilename(curr_ifile, alt_filename); 630 set_altpipe(curr_ifile, altpipe); 631 set_open(curr_ifile); /* File has been opened */ 632 get_pos(curr_ifile, &initial_scrpos); 633 ch_init(f, chflags, nread); 634 check_modelines(); 635 636 if (!(chflags & CH_HELPFILE)) 637 { 638 if (was_curr_ifile != NULL_IFILE) 639 old_ifile = was_curr_ifile; 640 #if LOGFILE 641 if (namelogfile != NULL && is_tty) 642 use_logfile(namelogfile); 643 #endif 644 #if HAVE_STAT_INO 645 /* Remember the i-number and device of the opened file. */ 646 curr_ino = curr_dev = 0; 647 if (!is_fake_pathname(open_filename)) 648 { 649 less_stat_t statbuf; 650 int r = less_stat(open_filename, &statbuf); 651 if (r == 0) 652 { 653 curr_ino = statbuf.st_ino; 654 curr_dev = statbuf.st_dev; 655 } 656 } 657 #endif 658 if (every_first_cmd != NULL) 659 { 660 ungetsc(every_first_cmd); 661 ungetcc_end_command(); 662 } 663 } 664 665 flush(); 666 667 if (is_tty) 668 { 669 /* 670 * Output is to a real tty. 671 */ 672 673 /* 674 * Indicate there is nothing displayed yet. 675 */ 676 pos_clear(); 677 clr_linenum(); 678 #if HILITE_SEARCH 679 clr_hilite(); 680 #endif 681 undo_osc8(); 682 hshift = 0; 683 read_error = FALSE; 684 if (strcmp(filename, FAKE_HELPFILE) && strcmp(filename, FAKE_EMPTYFILE)) 685 { 686 char *qfilename = shell_quote(filename); 687 if (qfilename == NULL) 688 cmd_addhist(ml_examine, filename, TRUE); 689 else 690 { 691 cmd_addhist(ml_examine, qfilename, TRUE); 692 free(qfilename); 693 } 694 } 695 if (want_filesize) 696 scan_eof(); 697 set_header(ch_zero()); 698 } 699 return (0); 700 } 701 702 /* 703 * Edit a space-separated list of files. 704 * For each filename in the list, enter it into the ifile list. 705 * Then edit the first one. 706 */ 707 public int edit_list(char *filelist) 708 { 709 IFILE save_ifile; 710 constant char *good_filename; 711 constant char *filename; 712 char *gfilelist; 713 constant char *gfilename; 714 char *qfilename; 715 struct textlist tl_files; 716 struct textlist tl_gfiles; 717 718 save_ifile = save_curr_ifile(); 719 good_filename = NULL; 720 721 /* 722 * Run thru each filename in the list. 723 * Try to glob the filename. 724 * If it doesn't expand, just try to open the filename. 725 * If it does expand, try to open each name in that list. 726 */ 727 init_textlist(&tl_files, filelist); 728 filename = NULL; 729 while ((filename = forw_textlist(&tl_files, filename)) != NULL) 730 { 731 gfilelist = lglob(filename); 732 init_textlist(&tl_gfiles, gfilelist); 733 gfilename = NULL; 734 while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL) 735 { 736 qfilename = shell_unquote(gfilename); 737 if (edit(qfilename) == 0 && good_filename == NULL) 738 good_filename = get_filename(curr_ifile); 739 free(qfilename); 740 } 741 free(gfilelist); 742 } 743 /* 744 * Edit the first valid filename in the list. 745 */ 746 if (good_filename == NULL) 747 { 748 unsave_ifile(save_ifile); 749 return (1); 750 } 751 if (get_ifile(good_filename, curr_ifile) == curr_ifile) 752 { 753 /* 754 * Trying to edit the current file; don't reopen it. 755 */ 756 unsave_ifile(save_ifile); 757 return (0); 758 } 759 reedit_ifile(save_ifile); 760 return (edit(good_filename)); 761 } 762 763 /* 764 * Edit the first file in the command line (ifile) list. 765 */ 766 public int edit_first(void) 767 { 768 if (nifile() == 0) 769 return (edit_stdin()); 770 curr_ifile = NULL_IFILE; 771 return (edit_next(1)); 772 } 773 774 /* 775 * Edit the last file in the command line (ifile) list. 776 */ 777 public int edit_last(void) 778 { 779 curr_ifile = NULL_IFILE; 780 return (edit_prev(1)); 781 } 782 783 784 /* 785 * Edit the n-th next or previous file in the command line (ifile) list. 786 */ 787 static int edit_istep(IFILE h, int n, int dir) 788 { 789 IFILE next; 790 791 /* 792 * Skip n filenames, then try to edit each filename. 793 */ 794 for (;;) 795 { 796 next = (dir > 0) ? next_ifile(h) : prev_ifile(h); 797 if (--n < 0) 798 { 799 if (edit_ifile(h) == 0) 800 break; 801 } 802 if (next == NULL_IFILE) 803 { 804 /* 805 * Reached end of the ifile list. 806 */ 807 return (1); 808 } 809 if (ABORT_SIGS()) 810 { 811 /* 812 * Interrupt breaks out, if we're in a long 813 * list of files that can't be opened. 814 */ 815 return (1); 816 } 817 h = next; 818 } 819 /* 820 * Found a file that we can edit. 821 */ 822 return (0); 823 } 824 825 static int edit_inext(IFILE h, int n) 826 { 827 return (edit_istep(h, n, +1)); 828 } 829 830 public int edit_next(int n) 831 { 832 return edit_istep(curr_ifile, n, +1); 833 } 834 835 static int edit_iprev(IFILE h, int n) 836 { 837 return (edit_istep(h, n, -1)); 838 } 839 840 public int edit_prev(int n) 841 { 842 return edit_istep(curr_ifile, n, -1); 843 } 844 845 /* 846 * Edit a specific file in the command line (ifile) list. 847 */ 848 public int edit_index(int n) 849 { 850 IFILE h; 851 852 h = NULL_IFILE; 853 do 854 { 855 if ((h = next_ifile(h)) == NULL_IFILE) 856 { 857 /* 858 * Reached end of the list without finding it. 859 */ 860 return (1); 861 } 862 } while (get_index(h) != n); 863 864 return (edit_ifile(h)); 865 } 866 867 public IFILE save_curr_ifile(void) 868 { 869 if (curr_ifile != NULL_IFILE) 870 hold_ifile(curr_ifile, 1); 871 return (curr_ifile); 872 } 873 874 public void unsave_ifile(IFILE save_ifile) 875 { 876 if (save_ifile != NULL_IFILE) 877 hold_ifile(save_ifile, -1); 878 } 879 880 /* 881 * Reedit the ifile which was previously open. 882 */ 883 public void reedit_ifile(IFILE save_ifile) 884 { 885 IFILE next; 886 IFILE prev; 887 888 /* 889 * Try to reopen the ifile. 890 * Note that opening it may fail (maybe the file was removed), 891 * in which case the ifile will be deleted from the list. 892 * So save the next and prev ifiles first. 893 */ 894 unsave_ifile(save_ifile); 895 next = next_ifile(save_ifile); 896 prev = prev_ifile(save_ifile); 897 if (edit_ifile(save_ifile) == 0) 898 return; 899 /* 900 * If can't reopen it, open the next input file in the list. 901 */ 902 if (next != NULL_IFILE && edit_inext(next, 0) == 0) 903 return; 904 /* 905 * If can't open THAT one, open the previous input file in the list. 906 */ 907 if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0) 908 return; 909 /* 910 * If can't even open that, we're stuck. Just quit. 911 */ 912 quit(QUIT_ERROR); 913 } 914 915 public void reopen_curr_ifile(void) 916 { 917 IFILE save_ifile = save_curr_ifile(); 918 close_file(); 919 reedit_ifile(save_ifile); 920 } 921 922 /* 923 * Edit standard input. 924 */ 925 public int edit_stdin(void) 926 { 927 if (isatty(fd0)) 928 { 929 error(LM(Missing_filename), NULL_PARG); 930 quit(QUIT_ERROR); 931 } 932 return (edit("-")); 933 } 934 935 /* 936 * Copy a file directly to standard output. 937 * Used if standard output is not a tty. 938 */ 939 public void cat_file(void) 940 { 941 int c; 942 943 while ((c = ch_forw_get()) != EOI) 944 putchr(c); 945 flush(); 946 } 947 948 #if LOGFILE 949 950 /* 951 * If the user asked for a log file and our input file 952 * is standard input, create the log file. 953 * We take care not to blindly overwrite an existing file. 954 */ 955 public void use_logfile(constant char *filename) 956 { 957 int exists; 958 int answer; 959 PARG parg; 960 961 if (ch_getflags() & CH_CANSEEK) 962 /* 963 * Can't currently use a log file on a file that can seek. 964 */ 965 return; 966 967 /* 968 * {{ We could use access() here. }} 969 */ 970 exists = open(filename, OPEN_READ); 971 if (exists >= 0) 972 close(exists); 973 exists = (exists >= 0); 974 975 /* 976 * Decide whether to overwrite the log file or append to it. 977 * If it doesn't exist we "overwrite" it. 978 */ 979 if (!exists || force_logfile) 980 { 981 /* 982 * Overwrite (or create) the log file. 983 */ 984 answer = 'O'; 985 } else 986 { 987 /* 988 * Ask user what to do. 989 */ 990 parg.p_string = filename; 991 answer = query(LM(X_exists), &parg); 992 } 993 994 loop: 995 switch (answer) 996 { 997 case 'O': case 'o': 998 /* 999 * Overwrite: create the file. 1000 */ 1001 logfile = creat(filename, CREAT_RW); 1002 break; 1003 case 'A': case 'a': 1004 /* 1005 * Append: open the file and seek to the end. 1006 */ 1007 logfile = open(filename, OPEN_APPEND); 1008 if (less_lseek(logfile, (less_off_t)0, SEEK_END) == BAD_LSEEK) 1009 { 1010 close(logfile); 1011 logfile = -1; 1012 } 1013 break; 1014 case 'D': case 'd': 1015 /* 1016 * Don't do anything. 1017 */ 1018 return; 1019 default: 1020 /* 1021 * Eh? 1022 */ 1023 1024 answer = query(LM(Overwrite), NULL_PARG); 1025 goto loop; 1026 } 1027 1028 if (logfile < 0) 1029 { 1030 /* 1031 * Error in opening logfile. 1032 */ 1033 parg.p_string = filename; 1034 error(LM(Cannot_write_to_X), &parg); 1035 return; 1036 } 1037 SET_BINARY(logfile); 1038 } 1039 1040 #endif 1041