2 * Copyright (C) 1984-2023 Mark Nudelman
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.
7 * For more information, see the README file.
25 extern char *every_first_cmd;
26 extern int force_open;
30 extern int want_filesize;
31 extern int consecutive_nulls;
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;
45 extern int force_logfile;
46 extern char *namelogfile;
50 public dev_t curr_dev;
51 public ino_t curr_ino;
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.
61 public void init_textlist(struct textlist *tlist, char *str)
64 #if SPACES_IN_FILENAMES
67 char *esc = get_meta_escape();
68 int esclen = (int) strlen(esc);
71 tlist->string = skipsp(str);
72 tlist->endstring = tlist->string + strlen(tlist->string);
73 for (s = str; s < tlist->endstring; s++)
75 #if SPACES_IN_FILENAMES
79 } else if (esclen > 0 && s + esclen < tlist->endstring &&
80 strncmp(s, esc, esclen) == 0)
84 } else if (delim_quoted)
88 } else /* (!delim_quoted) */
102 public char * forw_textlist(struct textlist *tlist, char *prev)
107 * prev == NULL means return the first word in the list.
108 * Otherwise, return the word after "prev".
113 s = prev + strlen(prev);
114 if (s >= tlist->endstring)
118 if (s >= tlist->endstring)
123 public char * back_textlist(struct textlist *tlist, char *prev)
128 * prev == NULL means return the last word in the list.
129 * Otherwise, return the word before "prev".
132 s = tlist->endstring;
133 else if (prev <= tlist->string)
139 if (s <= tlist->string)
141 while (s[-1] != '\0' && s > tlist->string)
147 * Parse a single option setting in a modeline.
149 static void modeline_option(char *str, int opt_len)
151 struct mloption { char *opt_name; void (*opt_func)(char*,int); };
152 struct mloption options[] = {
154 { "tabstop=", set_tabs },
157 struct mloption *opt;
158 for (opt = options; opt->opt_name != NULL; opt++)
160 int name_len = strlen(opt->opt_name);
161 if (opt_len > name_len && strncmp(str, opt->opt_name, name_len) == 0)
163 (*opt->opt_func)(str + name_len, opt_len - name_len);
170 * String length, terminated by option separator (space or colon).
171 * Space/colon can be escaped with backspace.
173 static int modeline_option_len(char *str)
177 for (s = str; *s != '\0'; s++)
183 else if (*s == ' ' || *s == ':') /* separator */
190 * Parse colon- or space-separated option settings in a modeline.
192 static void modeline_options(char *str, char end_char)
198 if (*str == '\0' || *str == end_char)
200 opt_len = modeline_option_len(str);
201 modeline_option(str, opt_len);
204 str += 1; /* skip past the separator */
209 * See if there is a modeline string in a line.
211 static void check_modeline(char *line)
214 static char *pgms[] = { "less:", "vim:", "vi:", "ex:", NULL };
216 for (pgm = pgms; *pgm != NULL; ++pgm)
222 pline = strstr(pline, *pgm);
223 if (pline == NULL) /* pgm is not in this line */
225 str = skipsp(pline + strlen(*pgm));
226 if (pline == line || pline[-1] == ' ')
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');
234 /* Continue searching the rest of the line. */
238 #endif /* HAVE_STRSTR */
242 * Read lines from start of file and check if any are modelines.
244 static void check_modelines(void)
246 POSITION pos = ch_zero();
248 for (i = 0; i < modelines; i++)
254 pos = forw_raw_line(pos, &line, &line_len);
255 if (pos == NULL_POSITION)
257 check_modeline(line);
262 * Close a pipe opened via popen.
264 static void close_pipe(FILE *pipefd)
273 * The pclose function of OS/2 emx sometimes fails.
274 * Send SIGINT to the piped process before closing it.
276 kill(pipefd->_pid, SIGINT);
278 status = pclose(pipefd);
281 /* An internal error in 'less', not a preprocessor error. */
282 parg.p_string = errno_message("pclose");
287 if (!show_preproc_error)
289 #if defined WIFEXITED && defined WEXITSTATUS
290 if (WIFEXITED(status))
292 int s = WEXITSTATUS(status);
296 error("Input preprocessor failed (status %d)", &parg);
301 #if defined WIFSIGNALED && defined WTERMSIG && HAVE_STRSIGNAL
302 if (WIFSIGNALED(status))
304 int sig = WTERMSIG(status);
305 if (sig != SIGPIPE || ch_length() != NULL_POSITION)
307 parg.p_string = signal_message(sig);
308 error("Input preprocessor terminated: %s", &parg);
316 error("Input preprocessor exited with status %x", &parg);
321 * Drain and close an input pipe if needed.
323 public void close_altpipe(IFILE ifile)
325 FILE *altpipe = get_altpipe(ifile);
326 if (altpipe != NULL && !(ch_getflags() & CH_KEEPOPEN))
329 set_altpipe(ifile, NULL);
334 * Check for error status from the current altpipe.
335 * May or may not close the pipe.
337 public void check_altpipe_error(void)
339 if (!show_preproc_error)
341 if (curr_ifile != NULL_IFILE && get_altfilename(curr_ifile) != NULL)
342 close_altpipe(curr_ifile);
346 * Close the current input file.
348 static void close_file(void)
350 struct scrpos scrpos;
353 if (curr_ifile == NULL_IFILE)
357 * Save the current position so that we can return to
358 * the same position if we edit this file again.
360 get_scrpos(&scrpos, TOP);
361 if (scrpos.pos != NULL_POSITION)
363 store_pos(curr_ifile, &scrpos);
367 * Close the file descriptor, unless it is a pipe.
371 * If we opened a file using an alternate name,
372 * do special stuff to close it.
374 altfilename = get_altfilename(curr_ifile);
375 if (altfilename != NULL)
377 close_altpipe(curr_ifile);
378 close_altfile(altfilename, get_filename(curr_ifile));
379 set_altfilename(curr_ifile, NULL);
381 curr_ifile = NULL_IFILE;
383 curr_ino = curr_dev = 0;
388 * Edit a new file (given its name).
389 * Filename == "-" means standard input.
390 * Filename == NULL means just close the current file.
392 public int edit(char *filename)
394 if (filename == NULL)
395 return (edit_ifile(NULL_IFILE));
396 return (edit_ifile(get_ifile(filename, curr_ifile)));
400 * Clean up what edit_ifile did before error return.
402 static int edit_error(char *filename, char *alt_filename, void *altpipe, IFILE ifile, IFILE was_curr_ifile)
404 if (alt_filename != NULL)
407 close_altfile(alt_filename, filename);
413 * Re-open the current file.
415 if (was_curr_ifile == ifile)
418 * Whoops. The "current" ifile is the one we just deleted.
423 reedit_ifile(was_curr_ifile);
428 * Edit a new file (given its IFILE).
429 * ifile == NULL means just close the current file.
431 public int edit_ifile(IFILE ifile)
440 IFILE was_curr_ifile;
443 if (ifile == curr_ifile)
446 * Already have the correct file open.
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); }}
461 was_curr_ifile = save_curr_ifile();
462 if (curr_ifile != NULL_IFILE)
464 chflags = ch_getflags();
466 if ((chflags & CH_HELPFILE) && held_ifile(was_curr_ifile) <= 1)
469 * Don't keep the help file in the ifile list.
471 del_ifile(was_curr_ifile);
472 was_curr_ifile = old_ifile;
476 if (ifile == NULL_IFILE)
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.)
484 unsave_ifile(was_curr_ifile);
488 filename = save(get_filename(ifile));
491 * See if LESSOPEN specifies an "alternate" file to open.
493 altpipe = get_altpipe(ifile);
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.
504 alt_filename = get_altfilename(ifile);
505 open_filename = (alt_filename != NULL) ? alt_filename : filename;
508 if (strcmp(filename, FAKE_HELPFILE) == 0 ||
509 strcmp(filename, FAKE_EMPTYFILE) == 0)
512 alt_filename = open_altfile(filename, &f, &altpipe);
514 open_filename = (alt_filename != NULL) ? alt_filename : filename;
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.
526 chflags |= CH_POPENED;
527 if (strcmp(filename, "-") == 0)
528 chflags |= CH_KEEPOPEN;
529 } else if (strcmp(filename, "-") == 0)
532 * Use standard input.
533 * Keep the file descriptor open because we can't reopen it.
536 chflags |= CH_KEEPOPEN;
538 * Must switch stdin to BINARY mode.
541 #if MSDOS_COMPILER==DJGPPC
543 * Setting stdin to binary by default causes
544 * Ctrl-C to not raise SIGINT. We must undo
547 __djgpp_set_ctrl_c(1);
549 } else if (strcmp(open_filename, FAKE_EMPTYFILE) == 0)
552 chflags |= CH_NODATA;
553 } else if (strcmp(open_filename, FAKE_HELPFILE) == 0)
556 chflags |= CH_HELPFILE;
557 } else if ((parg.p_string = bad_file(open_filename)) != NULL)
560 * It looks like a bad file. Don't try to open it.
564 return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile);
565 } else if ((f = open(open_filename, OPEN_READ)) < 0)
568 * Got an error trying to open it.
570 parg.p_string = errno_message(filename);
573 return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile);
576 chflags |= CH_CANSEEK;
577 if (!force_open && !opened(ifile) && bin_file(f))
580 * Looks like a binary file.
581 * Ask user if we should proceed.
583 parg.p_string = filename;
584 answer = query("\"%s\" may be a binary file. See it anyway? ",
586 if (answer != 'y' && answer != 'Y')
589 return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile);
594 if (!force_open && f >= 0 && isatty(f))
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);
604 * Get the saved position for the file.
606 if (was_curr_ifile != NULL_IFILE)
608 old_ifile = was_curr_ifile;
609 unsave_ifile(was_curr_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);
618 consecutive_nulls = 0;
621 if (!(chflags & CH_HELPFILE))
624 if (namelogfile != NULL && is_tty)
625 use_logfile(namelogfile);
628 /* Remember the i-number and device of the opened file. */
629 if (strcmp(open_filename, "-") != 0)
632 int r = stat(open_filename, &statbuf);
635 curr_ino = statbuf.st_ino;
636 curr_dev = statbuf.st_dev;
640 if (every_first_cmd != NULL)
642 ungetsc(every_first_cmd);
643 ungetcc_back(CHAR_END_COMMAND);
652 * Output is to a real tty.
656 * Indicate there is nothing displayed yet.
664 if (strcmp(filename, FAKE_HELPFILE) && strcmp(filename, FAKE_EMPTYFILE))
666 char *qfilename = shell_quote(filename);
667 cmd_addhist(ml_examine, qfilename, 1);
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.
682 public int edit_list(char *filelist)
690 struct textlist tl_files;
691 struct textlist tl_gfiles;
693 save_ifile = save_curr_ifile();
694 good_filename = NULL;
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.
702 init_textlist(&tl_files, filelist);
704 while ((filename = forw_textlist(&tl_files, filename)) != NULL)
706 gfilelist = lglob(filename);
707 init_textlist(&tl_gfiles, gfilelist);
709 while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL)
711 qfilename = shell_unquote(gfilename);
712 if (edit(qfilename) == 0 && good_filename == NULL)
713 good_filename = get_filename(curr_ifile);
719 * Edit the first valid filename in the list.
721 if (good_filename == NULL)
723 unsave_ifile(save_ifile);
726 if (get_ifile(good_filename, curr_ifile) == curr_ifile)
729 * Trying to edit the current file; don't reopen it.
731 unsave_ifile(save_ifile);
734 reedit_ifile(save_ifile);
735 return (edit(good_filename));
739 * Edit the first file in the command line (ifile) list.
741 public int edit_first(void)
744 return (edit_stdin());
745 curr_ifile = NULL_IFILE;
746 return (edit_next(1));
750 * Edit the last file in the command line (ifile) list.
752 public int edit_last(void)
754 curr_ifile = NULL_IFILE;
755 return (edit_prev(1));
760 * Edit the n-th next or previous file in the command line (ifile) list.
762 static int edit_istep(IFILE h, int n, int dir)
767 * Skip n filenames, then try to edit each filename.
771 next = (dir > 0) ? next_ifile(h) : prev_ifile(h);
774 if (edit_ifile(h) == 0)
777 if (next == NULL_IFILE)
780 * Reached end of the ifile list.
787 * Interrupt breaks out, if we're in a long
788 * list of files that can't be opened.
795 * Found a file that we can edit.
800 static int edit_inext(IFILE h, int n)
802 return (edit_istep(h, n, +1));
805 public int edit_next(int n)
807 return edit_istep(curr_ifile, n, +1);
810 static int edit_iprev(IFILE h, int n)
812 return (edit_istep(h, n, -1));
815 public int edit_prev(int n)
817 return edit_istep(curr_ifile, n, -1);
821 * Edit a specific file in the command line (ifile) list.
823 public int edit_index(int n)
830 if ((h = next_ifile(h)) == NULL_IFILE)
833 * Reached end of the list without finding it.
837 } while (get_index(h) != n);
839 return (edit_ifile(h));
842 public IFILE save_curr_ifile(void)
844 if (curr_ifile != NULL_IFILE)
845 hold_ifile(curr_ifile, 1);
849 public void unsave_ifile(IFILE save_ifile)
851 if (save_ifile != NULL_IFILE)
852 hold_ifile(save_ifile, -1);
856 * Reedit the ifile which was previously open.
858 public void reedit_ifile(IFILE save_ifile)
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.
869 unsave_ifile(save_ifile);
870 next = next_ifile(save_ifile);
871 prev = prev_ifile(save_ifile);
872 if (edit_ifile(save_ifile) == 0)
875 * If can't reopen it, open the next input file in the list.
877 if (next != NULL_IFILE && edit_inext(next, 0) == 0)
880 * If can't open THAT one, open the previous input file in the list.
882 if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0)
885 * If can't even open that, we're stuck. Just quit.
890 public void reopen_curr_ifile(void)
892 IFILE save_ifile = save_curr_ifile();
894 reedit_ifile(save_ifile);
898 * Edit standard input.
900 public int edit_stdin(void)
904 error("Missing filename (\"less --help\" for help)", NULL_PARG);
911 * Copy a file directly to standard output.
912 * Used if standard output is not a tty.
914 public void cat_file(void)
918 while ((c = ch_forw_get()) != EOI)
925 #define OVERWRITE_OPTIONS "Overwrite, Append, Don't log, or Quit?"
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.
932 public void use_logfile(char *filename)
938 if (ch_getflags() & CH_CANSEEK)
940 * Can't currently use a log file on a file that can seek.
945 * {{ We could use access() here. }}
947 exists = open(filename, OPEN_READ);
950 exists = (exists >= 0);
953 * Decide whether to overwrite the log file or append to it.
954 * If it doesn't exist we "overwrite" it.
956 if (!exists || force_logfile)
959 * Overwrite (or create) the log file.
965 * Ask user what to do.
967 parg.p_string = filename;
968 answer = query("Warning: \"%s\" exists; "OVERWRITE_OPTIONS" ", &parg);
976 * Overwrite: create the file.
978 logfile = creat(filename, CREAT_RW);
982 * Append: open the file and seek to the end.
984 logfile = open(filename, OPEN_APPEND);
985 if (lseek(logfile, (off_t)0, SEEK_END) == BAD_LSEEK)
1001 answer = query(OVERWRITE_OPTIONS" (Type \"O\", \"A\", \"D\" or \"Q\") ", NULL_PARG);
1008 * Error in opening logfile.
1010 parg.p_string = filename;
1011 error("Cannot write to \"%s\"", &parg);
1014 SET_BINARY(logfile);