diff options
Diffstat (limited to 'contrib/less/edit.c')
-rw-r--r-- | contrib/less/edit.c | 361 |
1 files changed, 255 insertions, 106 deletions
diff --git a/contrib/less/edit.c b/contrib/less/edit.c index 529ed75b0afe..3ca5e4cc7d31 100644 --- a/contrib/less/edit.c +++ b/contrib/less/edit.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1984-2021 Mark Nudelman + * Copyright (C) 1984-2023 Mark Nudelman * * You may distribute under the terms of either the GNU General Public * License or the Less License, as specified in the README file. @@ -13,9 +13,10 @@ #if HAVE_STAT #include <sys/stat.h> #endif -#if OS2 -#include <signal.h> +#if HAVE_SYS_WAIT_H +#include <sys/wait.h> #endif +#include <signal.h> public int fd0 = 0; @@ -27,6 +28,9 @@ extern int is_tty; extern int sigs; extern int hshift; extern int want_filesize; +extern int consecutive_nulls; +extern int modelines; +extern int show_preproc_error; extern IFILE curr_ifile; extern IFILE old_ifile; extern struct scrpos initial_scrpos; @@ -54,10 +58,7 @@ public ino_t curr_ino; * words, returning each one as a standard null-terminated string. * back_textlist does the same, but runs thru the list backwards. */ - public void -init_textlist(tlist, str) - struct textlist *tlist; - char *str; +public void init_textlist(struct textlist *tlist, char *str) { char *s; #if SPACES_IN_FILENAMES @@ -98,10 +99,7 @@ init_textlist(tlist, str) } } - public char * -forw_textlist(tlist, prev) - struct textlist *tlist; - char *prev; +public char * forw_textlist(struct textlist *tlist, char *prev) { char *s; @@ -122,10 +120,7 @@ forw_textlist(tlist, prev) return (s); } - public char * -back_textlist(tlist, prev) - struct textlist *tlist; - char *prev; +public char * back_textlist(struct textlist *tlist, char *prev) { char *s; @@ -149,11 +144,128 @@ back_textlist(tlist, prev) } /* + * Parse a single option setting in a modeline. + */ +static void modeline_option(char *str, int opt_len) +{ + struct mloption { char *opt_name; void (*opt_func)(char*,int); }; + struct mloption options[] = { + { "ts=", set_tabs }, + { "tabstop=", set_tabs }, + { NULL, NULL } + }; + struct mloption *opt; + for (opt = options; opt->opt_name != NULL; opt++) + { + int name_len = strlen(opt->opt_name); + if (opt_len > name_len && strncmp(str, opt->opt_name, name_len) == 0) + { + (*opt->opt_func)(str + name_len, opt_len - name_len); + break; + } + } +} + +/* + * String length, terminated by option separator (space or colon). + * Space/colon can be escaped with backspace. + */ +static int modeline_option_len(char *str) +{ + int esc = FALSE; + char *s; + for (s = str; *s != '\0'; s++) + { + if (esc) + esc = FALSE; + else if (*s == '\\') + esc = TRUE; + else if (*s == ' ' || *s == ':') /* separator */ + break; + } + return (s - str); +} + +/* + * Parse colon- or space-separated option settings in a modeline. + */ +static void modeline_options(char *str, char end_char) +{ + for (;;) + { + int opt_len; + str = skipsp(str); + if (*str == '\0' || *str == end_char) + break; + opt_len = modeline_option_len(str); + modeline_option(str, opt_len); + str += opt_len; + if (*str != '\0') + str += 1; /* skip past the separator */ + } +} + +/* + * See if there is a modeline string in a line. + */ +static void check_modeline(char *line) +{ +#if HAVE_STRSTR + static char *pgms[] = { "less:", "vim:", "vi:", "ex:", NULL }; + char **pgm; + for (pgm = pgms; *pgm != NULL; ++pgm) + { + char *pline = line; + for (;;) + { + char *str; + pline = strstr(pline, *pgm); + if (pline == NULL) /* pgm is not in this line */ + break; + str = skipsp(pline + strlen(*pgm)); + if (pline == line || pline[-1] == ' ') + { + if (strncmp(str, "set ", 4) == 0) + modeline_options(str+4, ':'); + else if (pgm != &pgms[0]) /* "less:" requires "set" */ + modeline_options(str, '\0'); + break; + } + /* Continue searching the rest of the line. */ + pline = str; + } + } +#endif /* HAVE_STRSTR */ +} + +/* + * Read lines from start of file and check if any are modelines. + */ +static void check_modelines(void) +{ + POSITION pos = ch_zero(); + int i; + for (i = 0; i < modelines; i++) + { + char *line; + int line_len; + if (ABORT_SIGS()) + return; + pos = forw_raw_line(pos, &line, &line_len); + if (pos == NULL_POSITION) + break; + check_modeline(line); + } +} + +/* * Close a pipe opened via popen. */ - static void -close_pipe(FILE *pipefd) +static void close_pipe(FILE *pipefd) { + int status; + PARG parg; + if (pipefd == NULL) return; #if OS2 @@ -163,18 +275,79 @@ close_pipe(FILE *pipefd) */ kill(pipefd->_pid, SIGINT); #endif - pclose(pipefd); + status = pclose(pipefd); + if (status == -1) + { + /* An internal error in 'less', not a preprocessor error. */ + parg.p_string = errno_message("pclose"); + error("%s", &parg); + free(parg.p_string); + return; + } + if (!show_preproc_error) + return; +#if defined WIFEXITED && defined WEXITSTATUS + if (WIFEXITED(status)) + { + int s = WEXITSTATUS(status); + if (s != 0) + { + parg.p_int = s; + error("Input preprocessor failed (status %d)", &parg); + } + return; + } +#endif +#if defined WIFSIGNALED && defined WTERMSIG && HAVE_STRSIGNAL + if (WIFSIGNALED(status)) + { + int sig = WTERMSIG(status); + if (sig != SIGPIPE || ch_length() != NULL_POSITION) + { + parg.p_string = signal_message(sig); + error("Input preprocessor terminated: %s", &parg); + } + return; + } +#endif + if (status != 0) + { + parg.p_int = status; + error("Input preprocessor exited with status %x", &parg); + } +} + +/* + * Drain and close an input pipe if needed. + */ +public void close_altpipe(IFILE ifile) +{ + FILE *altpipe = get_altpipe(ifile); + if (altpipe != NULL && !(ch_getflags() & CH_KEEPOPEN)) + { + close_pipe(altpipe); + set_altpipe(ifile, NULL); + } +} + +/* + * Check for error status from the current altpipe. + * May or may not close the pipe. + */ +public void check_altpipe_error(void) +{ + if (!show_preproc_error) + return; + if (curr_ifile != NULL_IFILE && get_altfilename(curr_ifile) != NULL) + close_altpipe(curr_ifile); } /* * Close the current input file. */ - static void -close_file(VOID_PARAM) +static void close_file(void) { struct scrpos scrpos; - int chflags; - FILE *altpipe; char *altfilename; if (curr_ifile == NULL_IFILE) @@ -193,7 +366,6 @@ close_file(VOID_PARAM) /* * Close the file descriptor, unless it is a pipe. */ - chflags = ch_getflags(); ch_close(); /* * If we opened a file using an alternate name, @@ -202,12 +374,7 @@ close_file(VOID_PARAM) altfilename = get_altfilename(curr_ifile); if (altfilename != NULL) { - altpipe = get_altpipe(curr_ifile); - if (altpipe != NULL && !(chflags & CH_KEEPOPEN)) - { - close_pipe(altpipe); - set_altpipe(curr_ifile, NULL); - } + close_altpipe(curr_ifile); close_altfile(altfilename, get_filename(curr_ifile)); set_altfilename(curr_ifile, NULL); } @@ -222,9 +389,7 @@ close_file(VOID_PARAM) * Filename == "-" means standard input. * Filename == NULL means just close the current file. */ - public int -edit(filename) - char *filename; +public int edit(char *filename) { if (filename == NULL) return (edit_ifile(NULL_IFILE)); @@ -232,12 +397,38 @@ edit(filename) } /* + * Clean up what edit_ifile did before error return. + */ +static int edit_error(char *filename, char *alt_filename, void *altpipe, IFILE ifile, IFILE was_curr_ifile) +{ + if (alt_filename != NULL) + { + close_pipe(altpipe); + close_altfile(alt_filename, filename); + free(alt_filename); + } + del_ifile(ifile); + free(filename); + /* + * Re-open the current file. + */ + if (was_curr_ifile == ifile) + { + /* + * Whoops. The "current" ifile is the one we just deleted. + * Just give up. + */ + quit(QUIT_ERROR); + } + reedit_ifile(was_curr_ifile); + return (1); +} + +/* * Edit a new file (given its IFILE). * ifile == NULL means just close the current file. */ - public int -edit_ifile(ifile) - IFILE ifile; +public int edit_ifile(IFILE ifile) { int f; int answer; @@ -370,28 +561,7 @@ edit_ifile(ifile) */ error("%s", &parg); free(parg.p_string); - err1: - if (alt_filename != NULL) - { - close_pipe(altpipe); - close_altfile(alt_filename, filename); - free(alt_filename); - } - del_ifile(ifile); - free(filename); - /* - * Re-open the current file. - */ - if (was_curr_ifile == ifile) - { - /* - * Whoops. The "current" ifile is the one we just deleted. - * Just give up. - */ - quit(QUIT_ERROR); - } - reedit_ifile(was_curr_ifile); - return (1); + return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile); } else if ((f = open(open_filename, OPEN_READ)) < 0) { /* @@ -400,7 +570,7 @@ edit_ifile(ifile) parg.p_string = errno_message(filename); error("%s", &parg); free(parg.p_string); - goto err1; + return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile); } else { chflags |= CH_CANSEEK; @@ -416,11 +586,18 @@ edit_ifile(ifile) if (answer != 'y' && answer != 'Y') { close(f); - goto err1; + return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile); } } } } + if (!force_open && f >= 0 && isatty(f)) + { + PARG parg; + parg.p_string = filename; + error("%s is a terminal (use -f to open it)", &parg); + return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile); + } /* * Get the new ifile. @@ -438,6 +615,8 @@ edit_ifile(ifile) get_pos(curr_ifile, &initial_scrpos); new_file = TRUE; ch_init(f, chflags); + consecutive_nulls = 0; + check_modelines(); if (!(chflags & CH_HELPFILE)) { @@ -500,9 +679,7 @@ edit_ifile(ifile) * For each filename in the list, enter it into the ifile list. * Then edit the first one. */ - public int -edit_list(filelist) - char *filelist; +public int edit_list(char *filelist) { IFILE save_ifile; char *good_filename; @@ -561,8 +738,7 @@ edit_list(filelist) /* * Edit the first file in the command line (ifile) list. */ - public int -edit_first(VOID_PARAM) +public int edit_first(void) { if (nifile() == 0) return (edit_stdin()); @@ -573,8 +749,7 @@ edit_first(VOID_PARAM) /* * Edit the last file in the command line (ifile) list. */ - public int -edit_last(VOID_PARAM) +public int edit_last(void) { curr_ifile = NULL_IFILE; return (edit_prev(1)); @@ -584,11 +759,7 @@ edit_last(VOID_PARAM) /* * Edit the n-th next or previous file in the command line (ifile) list. */ - static int -edit_istep(h, n, dir) - IFILE h; - int n; - int dir; +static int edit_istep(IFILE h, int n, int dir) { IFILE next; @@ -626,32 +797,22 @@ edit_istep(h, n, dir) return (0); } - static int -edit_inext(h, n) - IFILE h; - int n; +static int edit_inext(IFILE h, int n) { return (edit_istep(h, n, +1)); } - public int -edit_next(n) - int n; +public int edit_next(int n) { return edit_istep(curr_ifile, n, +1); } - static int -edit_iprev(h, n) - IFILE h; - int n; +static int edit_iprev(IFILE h, int n) { return (edit_istep(h, n, -1)); } - public int -edit_prev(n) - int n; +public int edit_prev(int n) { return edit_istep(curr_ifile, n, -1); } @@ -659,9 +820,7 @@ edit_prev(n) /* * Edit a specific file in the command line (ifile) list. */ - public int -edit_index(n) - int n; +public int edit_index(int n) { IFILE h; @@ -680,17 +839,14 @@ edit_index(n) return (edit_ifile(h)); } - public IFILE -save_curr_ifile(VOID_PARAM) +public IFILE save_curr_ifile(void) { if (curr_ifile != NULL_IFILE) hold_ifile(curr_ifile, 1); return (curr_ifile); } - public void -unsave_ifile(save_ifile) - IFILE save_ifile; +public void unsave_ifile(IFILE save_ifile) { if (save_ifile != NULL_IFILE) hold_ifile(save_ifile, -1); @@ -699,9 +855,7 @@ unsave_ifile(save_ifile) /* * Reedit the ifile which was previously open. */ - public void -reedit_ifile(save_ifile) - IFILE save_ifile; +public void reedit_ifile(IFILE save_ifile) { IFILE next; IFILE prev; @@ -733,8 +887,7 @@ reedit_ifile(save_ifile) quit(QUIT_ERROR); } - public void -reopen_curr_ifile(VOID_PARAM) +public void reopen_curr_ifile(void) { IFILE save_ifile = save_curr_ifile(); close_file(); @@ -744,8 +897,7 @@ reopen_curr_ifile(VOID_PARAM) /* * Edit standard input. */ - public int -edit_stdin(VOID_PARAM) +public int edit_stdin(void) { if (isatty(fd0)) { @@ -759,8 +911,7 @@ edit_stdin(VOID_PARAM) * Copy a file directly to standard output. * Used if standard output is not a tty. */ - public void -cat_file(VOID_PARAM) +public void cat_file(void) { int c; @@ -778,9 +929,7 @@ cat_file(VOID_PARAM) * is standard input, create the log file. * We take care not to blindly overwrite an existing file. */ - public void -use_logfile(filename) - char *filename; +public void use_logfile(char *filename) { int exists; int answer; @@ -826,7 +975,7 @@ loop: /* * Overwrite: create the file. */ - logfile = creat(filename, 0644); + logfile = creat(filename, CREAT_RW); break; case 'A': case 'a': /* |