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