From nobody Mon Nov 10 08:56:03 2025 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4d4k6D07r7z6GQSB; Mon, 10 Nov 2025 08:56:04 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R12" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4d4k6C5Xcgz3C07; Mon, 10 Nov 2025 08:56:03 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1762764963; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Oqt/9UQXsdqKgkdOMzk07BJZU79ypDgRHGOSOsYzHig=; b=dKy/sb/iyqthUYcWoveA7GshVBK+AM36qd/eNezvuR5AKnC2743ehxmSaSqcWtxPOijMxC +qJwHMa0LV5u1mw8ccumegX/RbX9i+xhezo5FNrX5G8fLPrO3VIiFayLYhn4TgCIeqdRdE 8N0MYFSpGWw+MI5uI92pkUiJiQ2hkE6rFl1j3k0rwqY9jyBAAqYA0b2IrDjGeQE40SQNhW 1PIoy9BnT6zOn7IuvGZGC3jM9CYZmiQ1G6bM0uq2vBnQupfmOTsX1FDtLjwFPcwmGcIeB8 Hvy903H3hpQ+2pxT7SZJk5VMn/MZ28BD55+BhUx9SZrrlhIjubMgIOaPyEydeA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1762764963; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Oqt/9UQXsdqKgkdOMzk07BJZU79ypDgRHGOSOsYzHig=; b=b+nGf95d4O53bEmHrN4yha2s6k5rIggvwUOp/xySvaK8RsQ0KBFaveEF4xUzZb+EPHsRoU TRYhOHfsxBaLz2QiwObgUJvDo6/V+O/aosElGktqLKL9TjQjdzieeZtFmc1FABHr/KnZbQ LdEiTiyImFvkHXXxI6KS/cZl7Yw4OZdVxnSYq4VQswwovRPmAkQojvnawhCN68eO6W3B2u +8u6Wuas31S/80bpkKhjZ1nkIFKSFjFkjOHgMeoBSVrgHf5dn2ljKTReUCs1Pu4CbLID48 aZjXyUjNBpE1IHGoKmMIvCr6qpfwC4RPCvqbqJOZtsbivrLm7tQHPCjiXMPGTw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1762764963; a=rsa-sha256; cv=none; b=x6pHuODHwuyS8gI2qxaxTMizMZHAsb+Z3oOaQov9jV9g2VLworhhtxKar7uxuHWzfSppiX 0LMiPsCm6OLfowd3okRPYXLqFiCVycyFLIUV7z7B/djOh5LJ+YqQOoQnNm7FQR7CvWxHSQ BoSJFJRF3W4LmyBh9rU4jGFo9GFGI89OfmcNh/4OWOq1KLEkgD4TtH/irVy7CH+CrYDvmL 0rUXTnxhUKVx3qPbWutAoIgoeumCQGwpRze0QoS9cK58EEBB/iKaU07MH2tbr6jMt/ZVH8 4kPptA+JpzwZIDeLWUfO4QK6A9lnKdfYcJf16BNhCz2HGFVnFVKYbpqy4hP9kw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4d4k6C55vCz196Q; Mon, 10 Nov 2025 08:56:03 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 5AA8u3YY066520; Mon, 10 Nov 2025 08:56:03 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 5AA8u3oQ066517; Mon, 10 Nov 2025 08:56:03 GMT (envelope-from git) Date: Mon, 10 Nov 2025 08:56:03 GMT Message-Id: <202511100856.5AA8u3oQ066517@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Xin LI Subject: git: d5cb458b4b58 - main - MFV: less v685. List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: delphij X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: d5cb458b4b58b0f0b3c058a32439f232fd5455ca Auto-Submitted: auto-generated The branch main has been updated by delphij: URL: https://cgit.FreeBSD.org/src/commit/?id=d5cb458b4b58b0f0b3c058a32439f232fd5455ca commit d5cb458b4b58b0f0b3c058a32439f232fd5455ca Merge: dbb34d496708 a1320769a9f7 Author: Xin LI AuthorDate: 2025-11-10 08:55:06 +0000 Commit: Xin LI CommitDate: 2025-11-10 08:55:06 +0000 MFV: less v685. MFC after: 3 days contrib/less/NEWS | 39 ++++++++++++++++ contrib/less/ch.c | 4 -- contrib/less/charset.c | 48 +++++++++++++++++--- contrib/less/command.c | 40 +++++++++++++++++ contrib/less/compose.uni | 4 +- contrib/less/decode.c | 11 +++-- contrib/less/edit.c | 10 +++-- contrib/less/fmt.uni | 6 +-- contrib/less/forwback.c | 2 +- contrib/less/funcs.h | 12 ++++- contrib/less/help.c | 2 +- contrib/less/input.c | 72 +++++++++++++++++------------- contrib/less/less.h | 5 ++- contrib/less/less.nro | 110 ++++++++++++++++++++++++++++++++++++---------- contrib/less/lessecho.nro | 6 +-- contrib/less/lesskey.nro | 18 +++++--- contrib/less/lglob.h | 2 +- contrib/less/line.c | 106 +++++++++++++++++++++++++++++++++++++++----- contrib/less/lsystem.c | 2 +- contrib/less/main.c | 41 ++++++++++++----- contrib/less/mkutable | 21 ++++++++- contrib/less/omit.uni | 7 +++ contrib/less/optfunc.c | 18 ++++++++ contrib/less/opttbl.c | 6 +++ contrib/less/os.c | 51 ++++++++++++++++++--- contrib/less/pattern.c | 7 ++- contrib/less/screen.c | 30 +++++++++++++ contrib/less/search.c | 12 +++-- contrib/less/ubin.uni | 2 +- contrib/less/version.c | 14 +++++- contrib/less/wide.uni | 8 ++-- contrib/less/xbuf.c | 2 +- usr.bin/less/defines.h | 18 ++++++-- 33 files changed, 596 insertions(+), 140 deletions(-) diff --cc contrib/less/command.c index 3ec1f9b48358,000000000000..390385547385 mode 100644,000000..100644 --- a/contrib/less/command.c +++ b/contrib/less/command.c @@@ -1,2320 -1,0 +1,2360 @@@ +/* + * Copyright (C) 1984-2025 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. + * + * For more information, see the README file. + */ + + +/* + * User-level command processor. + */ + +#include "less.h" +#if MSDOS_COMPILER==WIN32C +#include +#endif +#include "position.h" +#include "option.h" +#include "cmd.h" + +extern int erase_char, erase2_char, kill_char; +extern int sigs; +extern int quit_if_one_screen; +extern int one_screen; +extern int sc_width; +extern int sc_height; +extern char *kent; +extern int swindow; +extern int jump_sline; +extern lbool quitting; +extern int wscroll; +extern int top_scroll; +extern int ignore_eoi; +extern int hshift; +extern int bs_mode; +extern int proc_backspace; +extern int show_attn; +extern int less_is_more; +extern int chopline; +extern POSITION highest_hilite; +extern char *every_first_cmd; +extern char version[]; +extern struct scrpos initial_scrpos; +extern IFILE curr_ifile; +extern void *ml_search; +extern void *ml_examine; +extern int wheel_lines; +extern int def_search_type; +extern lbool search_wrapped; ++extern lbool no_poll; +extern int no_paste; +extern lbool pasting; +extern int no_edit_warn; +extern POSITION soft_eof; ++extern POSITION search_incr_start; ++extern char *first_cmd_at_prompt; +#if SHELL_ESCAPE || PIPEC +extern void *ml_shell; +#endif +#if EDITOR +extern constant char *editproto; +#endif +#if OSC8_LINK +extern char *osc8_uri; +#endif +extern int shift_count; +extern int forw_prompt; +extern int incr_search; +extern int full_screen; +#if MSDOS_COMPILER==WIN32C +extern int utf_mode; +extern unsigned less_acp; +#endif + +#if SHELL_ESCAPE +static char *shellcmd = NULL; /* For holding last shell command for "!!" */ +#endif +static int mca; /* The multicharacter command (action) */ +static int search_type; /* The previous type of search */ +static int last_search_type; /* Type of last executed search */ +static LINENUM number; /* The number typed by the user */ +static long fraction; /* The fractional part of the number */ +static struct loption *curropt; +static lbool opt_lower; +static int optflag; +static lbool optgetname; +static POSITION bottompos; +static int save_hshift; +static int save_bs_mode; +static int save_proc_backspace; +static int screen_trashed_value = 0; +static lbool literal_char = FALSE; +static lbool ignoring_input = FALSE; ++static struct scrpos search_incr_pos = { NULL_POSITION, 0 }; ++static int search_incr_hshift; +#if HAVE_TIME +static time_type ignoring_input_time; +#endif +#if PIPEC +static char pipec; +#endif + +/* Stack of ungotten chars (via ungetcc) */ +struct ungot { + struct ungot *ug_next; + char ug_char; + lbool ug_end_command; +}; +static struct ungot* ungot = NULL; + +static void multi_search(constant char *pattern, int n, int silent); + +/* + * Move the cursor to start of prompt line before executing a command. + * This looks nicer if the command takes a long time before + * updating the screen. + */ +public void cmd_exec(void) +{ + clear_attn(); + clear_bot(); + flush(); +} + +/* + * Indicate we are reading a multi-character command. + */ +static void set_mca(int action) +{ + mca = action; + clear_bot(); + clear_cmd(); +} + +/* + * Indicate we are not reading a multi-character command. + */ +static void clear_mca(void) +{ + if (mca == 0) + return; + mca = 0; +} + +/* + * Set up the display to start a new multi-character command. + */ +static void start_mca(int action, constant char *prompt, void *mlist, int cmdflags) +{ + set_mca(action); + cmd_putstr(prompt); + set_mlist(mlist, cmdflags); +} + +public int in_mca(void) +{ + return (mca != 0 && mca != A_PREFIX); +} + +/* + * Set up the display to start a new search command. + */ +static void mca_search1(void) +{ + int i; + +#if HILITE_SEARCH + if (search_type & SRCH_FILTER) + set_mca(A_FILTER); + else +#endif + if (search_type & SRCH_FORW) + set_mca(A_F_SEARCH); + else + set_mca(A_B_SEARCH); + + if (search_type & SRCH_NO_MATCH) + cmd_putstr("Non-match "); + if (search_type & SRCH_FIRST_FILE) + cmd_putstr("First-file "); + if (search_type & SRCH_PAST_EOF) + cmd_putstr("EOF-ignore "); + if (search_type & SRCH_NO_MOVE) + cmd_putstr("Keep-pos "); + if (search_type & SRCH_NO_REGEX) + cmd_putstr("Regex-off "); + if (search_type & SRCH_WRAP) + cmd_putstr("Wrap "); + for (i = 1; i <= NUM_SEARCH_COLORS; i++) + { + if (search_type & SRCH_SUBSEARCH(i)) + { + char buf[INT_STRLEN_BOUND(int)+8]; + SNPRINTF1(buf, sizeof(buf), "Sub-%d ", i); + cmd_putstr(buf); + } + } + if (literal_char) + cmd_putstr("Lit "); + +#if HILITE_SEARCH + if (search_type & SRCH_FILTER) + cmd_putstr("&/"); + else +#endif + if (search_type & SRCH_FORW) + cmd_putstr("/"); + else + cmd_putstr("?"); + forw_prompt = 0; +} + +static void mca_search(void) +{ ++ if (incr_search) ++ { ++ /* Remember where the incremental search started. */ ++ get_scrpos(&search_incr_pos, TOP); ++ search_incr_start = search_pos(search_type); ++ search_incr_hshift = hshift; ++ } + mca_search1(); + set_mlist(ml_search, 0); +} + +/* + * Set up the display to start a new toggle-option command. + */ +static void mca_opt_toggle(void) +{ + int no_prompt = (optflag & OPT_NO_PROMPT); + int flag = (optflag & ~OPT_NO_PROMPT); + constant char *dash = (flag == OPT_NO_TOGGLE) ? "_" : "-"; + + set_mca(A_OPT_TOGGLE); + cmd_putstr(dash); + if (optgetname) + cmd_putstr(dash); + if (no_prompt) + cmd_putstr("(P)"); + switch (flag) + { + case OPT_UNSET: + cmd_putstr("+"); + break; + case OPT_SET: + cmd_putstr("!"); + break; + } + forw_prompt = 0; + set_mlist(NULL, CF_OPTION); +} + +/* + * Execute a multicharacter command. + */ +static void exec_mca(void) +{ + constant char *cbuf; + + cmd_exec(); + cbuf = get_cmdbuf(); + if (cbuf == NULL) + return; + + switch (mca) + { + case A_F_SEARCH: + case A_B_SEARCH: + multi_search(cbuf, (int) number, 0); + break; +#if HILITE_SEARCH + case A_FILTER: + search_type ^= SRCH_NO_MATCH; + set_filter_pattern(cbuf, search_type); + soft_eof = NULL_POSITION; + break; +#endif + case A_FIRSTCMD: + /* + * Skip leading spaces or + signs in the string. + */ + while (*cbuf == '+' || *cbuf == ' ') + cbuf++; + if (every_first_cmd != NULL) + free(every_first_cmd); + if (*cbuf == '\0') + every_first_cmd = NULL; + else + every_first_cmd = save(cbuf); + break; + case A_OPT_TOGGLE: + toggle_option(curropt, opt_lower, cbuf, optflag); + curropt = NULL; + break; + case A_F_BRACKET: + match_brac(cbuf[0], cbuf[1], 1, (int) number); + break; + case A_B_BRACKET: + match_brac(cbuf[1], cbuf[0], 0, (int) number); + break; +#if EXAMINE + case A_EXAMINE: { + char *p; + if (!secure_allow(SF_EXAMINE)) + break; + p = save(cbuf); + edit_list(p); + free(p); +#if TAGS + /* If tag structure is loaded then clean it up. */ + cleantags(); +#endif + break; } +#endif +#if SHELL_ESCAPE + case A_SHELL: { + /* + * !! just uses whatever is in shellcmd. + * Otherwise, copy cmdbuf to shellcmd, + * expanding any special characters ("%" or "#"). + */ + constant char *done_msg = (*cbuf == CONTROL('P')) ? NULL : "!done"; + if (done_msg == NULL) + ++cbuf; + if (*cbuf != '!') + { + if (shellcmd != NULL) + free(shellcmd); + shellcmd = fexpand(cbuf); + } + if (!secure_allow(SF_SHELL)) + break; + if (shellcmd == NULL) + shellcmd = ""; + lsystem(shellcmd, done_msg); + break; } + case A_PSHELL: { + constant char *done_msg = (*cbuf == CONTROL('P')) ? NULL : "#done"; + if (done_msg == NULL) + ++cbuf; + if (!secure_allow(SF_SHELL)) + break; + lsystem(pr_expand(cbuf), done_msg); + break; } +#endif +#if PIPEC + case A_PIPE: { + constant char *done_msg = (*cbuf == CONTROL('P')) ? NULL : "|done"; + if (done_msg == NULL) + ++cbuf; + if (!secure_allow(SF_PIPE)) + break; + (void) pipe_mark(pipec, cbuf); + if (done_msg != NULL) + error(done_msg, NULL_PARG); + break; } +#endif + } +} + +/* + * Is a character an erase or kill char? + */ +static lbool is_erase_char(char c) +{ + return (c == erase_char || c == erase2_char || c == kill_char); +} + +/* + * Is a character a carriage return or newline? + */ +static lbool is_newline_char(char c) +{ + return (c == '\n' || c == '\r'); +} + +/* + * Handle the first char of an option (after the initial dash). + */ +static int mca_opt_first_char(char c) +{ + int no_prompt = (optflag & OPT_NO_PROMPT); + int flag = (optflag & ~OPT_NO_PROMPT); + if (flag == OPT_NO_TOGGLE) + { + switch (c) + { + case '_': + /* "__" = long option name. */ + optgetname = TRUE; + mca_opt_toggle(); + return (MCA_MORE); + } + } else + { + switch (c) + { + case '+': + /* "-+" = UNSET. */ + optflag = no_prompt | ((flag == OPT_UNSET) ? + OPT_TOGGLE : OPT_UNSET); + mca_opt_toggle(); + return (MCA_MORE); + case '!': + /* "-!" = SET */ + optflag = no_prompt | ((flag == OPT_SET) ? + OPT_TOGGLE : OPT_SET); + mca_opt_toggle(); + return (MCA_MORE); + case CONTROL('P'): + optflag ^= OPT_NO_PROMPT; + mca_opt_toggle(); + return (MCA_MORE); + case '-': + /* "--" = long option name. */ + optgetname = TRUE; + mca_opt_toggle(); + return (MCA_MORE); + } + } + /* Char was not handled here. */ + return (NO_MCA); +} + +/* + * Add a char to a long option name. + * See if we've got a match for an option name yet. + * If so, display the complete name and stop + * accepting chars until user hits RETURN. + */ +static int mca_opt_nonfirst_char(char c) +{ + constant char *p; + constant char *oname; + lbool ambig; + struct loption *was_curropt; + + if (curropt != NULL) + { + /* Already have a match for the name. */ + if (is_erase_char(c)) + return (MCA_DONE); + /* {{ Checking for TAB here is ugly. + * Also doesn't extend well -- can't do BACKTAB this way + * because it's a multichar sequence. }} */ + if (c != '\t') + return (MCA_MORE); + } + /* + * Add char to cmd buffer and try to match + * the option name. + */ + if (cmd_char(c) == CC_QUIT) + return (MCA_DONE); + p = get_cmdbuf(); + if (p == NULL || p[0] == '\0') + return (MCA_MORE); + opt_lower = ASCII_IS_LOWER(p[0]); + was_curropt = curropt; + curropt = findopt_name(&p, &oname, &ambig); + if (curropt != NULL) + { + if (was_curropt == NULL) + { + /* + * Got a match. + * Remember the option and + * display the full option name. + */ + cmd_reset(); + mca_opt_toggle(); + cmd_setstring(oname, !opt_lower); + } + } else if (!ambig) + { + bell(); + } + return (MCA_MORE); +} + +/* + * Handle a char of an option toggle command. + */ +static int mca_opt_char(char c) +{ + PARG parg; + + /* + * This may be a short option (single char), + * or one char of a long option name, + * or one char of the option parameter. + */ + if (curropt == NULL && cmdbuf_empty()) + { + int ret = mca_opt_first_char(c); + if (ret != NO_MCA) + return (ret); + } + if (optgetname) + { + /* We're getting a long option name. */ + if (!is_newline_char(c) && c != '=') + return (mca_opt_nonfirst_char(c)); + if (curropt == NULL) + { + parg.p_string = get_cmdbuf(); + if (parg.p_string == NULL) + return (MCA_MORE); + error("There is no --%s option", &parg); + return (MCA_DONE); + } + optgetname = FALSE; + cmd_reset(); + } else + { + if (is_erase_char(c)) + return (NO_MCA); + if (curropt != NULL) + /* We're getting the option parameter. */ + return (NO_MCA); + curropt = findopt(c); + if (curropt == NULL) + { + parg.p_string = propt(c); + error("There is no %s option", &parg); + return (MCA_DONE); + } + opt_lower = ASCII_IS_LOWER(c); + } + /* + * If the option which was entered does not take a + * parameter, toggle the option immediately, + * so user doesn't have to hit RETURN. + */ + if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE || + !opt_has_param(curropt)) + { + toggle_option(curropt, opt_lower, "", optflag); + return (MCA_DONE); + } + /* + * Display a prompt appropriate for the option parameter. + */ + start_mca(A_OPT_TOGGLE, opt_prompt(curropt), NULL, CF_OPTION); + return (MCA_MORE); +} + +/* + * Normalize search type. + */ +public int norm_search_type(int st) +{ + /* WRAP and PAST_EOF are mutually exclusive. */ + if ((st & (SRCH_PAST_EOF|SRCH_WRAP)) == (SRCH_PAST_EOF|SRCH_WRAP)) + st ^= SRCH_PAST_EOF; + return st; +} + +/* + * Handle a char of a search command. + */ +static int mca_search_char(char c) +{ + int flag = 0; + + /* + * Certain characters as the first char of + * the pattern have special meaning: + * ! Toggle the NO_MATCH flag + * * Toggle the PAST_EOF flag + * @ Toggle the FIRST_FILE flag + */ + if (!cmdbuf_empty() || literal_char) + { + literal_char = FALSE; + return (NO_MCA); + } + + switch (c) + { + case '*': + if (less_is_more) + break; + case CONTROL('E'): /* ignore END of file */ + if (mca != A_FILTER) + flag = SRCH_PAST_EOF; + search_type &= ~SRCH_WRAP; + break; + case '@': + if (less_is_more) + break; + case CONTROL('F'): /* FIRST file */ + if (mca != A_FILTER) + flag = SRCH_FIRST_FILE; + break; + case CONTROL('K'): /* KEEP position */ + if (mca != A_FILTER) + flag = SRCH_NO_MOVE; + break; + case CONTROL('S'): { /* SUBSEARCH */ + char buf[INT_STRLEN_BOUND(int)+24]; + SNPRINTF1(buf, sizeof(buf), "Sub-pattern (1-%d):", NUM_SEARCH_COLORS); + clear_bot(); + cmd_putstr(buf); + flush(); + c = getcc(); + if (c >= '1' && c <= '0'+NUM_SEARCH_COLORS) + flag = SRCH_SUBSEARCH(c-'0'); + else + flag = -1; /* calls mca_search() below to repaint */ + break; } + case CONTROL('W'): /* WRAP around */ + if (mca != A_FILTER) + flag = SRCH_WRAP; + break; + case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */ + flag = SRCH_NO_REGEX; + break; + case CONTROL('N'): /* NOT match */ + case '!': + flag = SRCH_NO_MATCH; + break; + case CONTROL('L'): + literal_char = TRUE; + flag = -1; + break; + } + + if (flag != 0) + { + if (flag != -1) + search_type = norm_search_type(search_type ^ flag); + mca_search(); + return (MCA_MORE); + } + return (NO_MCA); +} + +/* + * Handle a character of a multi-character command. + */ +static int mca_char(char c) +{ + int ret; + + switch (mca) + { + case 0: + /* + * We're not in a multicharacter command. + */ + return (NO_MCA); + + case A_PREFIX: + /* + * In the prefix of a command. + * This not considered a multichar command + * (even tho it uses cmdbuf, etc.). + * It is handled in the commands() switch. + */ + return (NO_MCA); + + case A_DIGIT: + /* + * Entering digits of a number. + * Terminated by a non-digit. + */ + if ((c >= '0' && c <= '9') || c == '.') + break; + switch (editchar(c, ECF_PEEK|ECF_NOHISTORY|ECF_NOCOMPLETE|ECF_NORIGHTLEFT)) + { + case A_NOACTION: + /* + * Ignore this char and get another one. + */ + return (MCA_MORE); + case A_INVALID: + /* + * Not part of the number. + * End the number and treat this char + * as a normal command character. + */ + number = cmd_int(&fraction); + clear_mca(); + cmd_accept(); + return (NO_MCA); + } + break; + + case A_OPT_TOGGLE: + ret = mca_opt_char(c); + if (ret != NO_MCA) + return (ret); + break; + + case A_F_SEARCH: + case A_B_SEARCH: + case A_FILTER: + ret = mca_search_char(c); + if (ret != NO_MCA) + return (ret); + break; + + default: + /* Other multicharacter command. */ + break; + } + + /* + * The multichar command is terminated by a newline. + */ + if (is_newline_char(c)) + { + if (pasting && no_paste) + { + /* Ignore pasted input after (and including) the first newline */ + start_ignoring_input(); + return (MCA_MORE); + } + /* Execute the command. */ + exec_mca(); + return (MCA_DONE); + } + + /* + * Append the char to the command buffer. + */ + if (cmd_char(c) == CC_QUIT) + /* + * Abort the multi-char command. + */ + return (MCA_DONE); + + switch (mca) + { + case A_F_BRACKET: + case A_B_BRACKET: + if (len_cmdbuf() >= 2) + { + /* + * Special case for the bracket-matching commands. + * Execute the command after getting exactly two + * characters from the user. + */ + exec_mca(); + return (MCA_DONE); + } + break; + case A_F_SEARCH: + case A_B_SEARCH: + if (incr_search) + { + /* Incremental search: do a search after every input char. */ + int st = (search_type & (SRCH_FORW|SRCH_BACK|SRCH_NO_MATCH|SRCH_NO_REGEX|SRCH_NO_MOVE|SRCH_WRAP|SRCH_SUBSEARCH_ALL)); + ssize_t save_updown; + constant char *pattern = get_cmdbuf(); + if (pattern == NULL) + return (MCA_MORE); ++ /* Defer searching if more chars of the pattern are available. */ ++ if (ttyin_ready()) ++ return (MCA_MORE); + /* + * Must save updown_match because mca_search + * reinits it. That breaks history scrolling. + * {{ This is ugly. mca_search probably shouldn't call set_mlist. }} + */ + save_updown = save_updown_match(); + cmd_exec(); + if (*pattern == '\0') + { + /* User has backspaced to an empty pattern. */ + undo_search(1); ++ hshift = search_incr_hshift; ++ jump_loc(search_incr_pos.pos, search_incr_pos.ln); + } else + { ++ /* ++ * Suppress tty polling while searching. ++ * This avoids a problem where tty input ++ * can cause the search to be interrupted. ++ */ ++ no_poll = TRUE; + if (search(st | SRCH_INCR, pattern, 1) != 0) ++ { + /* No match, invalid pattern, etc. */ + undo_search(1); ++ hshift = search_incr_hshift; ++ jump_loc(search_incr_pos.pos, search_incr_pos.ln); ++ } ++ no_poll = FALSE; + } + /* Redraw the search prompt and search string. */ + if (is_screen_trashed() || !full_screen) + { + clear(); + repaint(); + } + mca_search1(); + restore_updown_match(save_updown); + cmd_repaint(NULL); + } + break; + } + + /* + * Need another character. + */ + return (MCA_MORE); +} + +/* + * Discard any buffered file data. + */ +static void clear_buffers(void) +{ + if (!(ch_getflags() & CH_CANSEEK)) + return; + ch_flush(); + clr_linenum(); +#if HILITE_SEARCH + clr_hilite(); +#endif ++ set_line_contig_pos(NULL_POSITION); +} + +public void screen_trashed_num(int trashed) +{ + screen_trashed_value = trashed; +} + +public void screen_trashed(void) +{ + screen_trashed_num(1); +} + +public int is_screen_trashed(void) +{ + return screen_trashed_value; +} + +/* + * Make sure the screen is displayed. + */ +static void make_display(void) +{ + /* + * If not full_screen, we can't rely on scrolling to fill the screen. + * We need to clear and repaint screen before any change. + */ + if (!full_screen && !(quit_if_one_screen && one_screen)) + clear(); + /* + * If nothing is displayed yet, display starting from initial_scrpos. + */ + if (empty_screen()) + { + if (initial_scrpos.pos == NULL_POSITION) + jump_loc(ch_zero(), 1); + else + jump_loc(initial_scrpos.pos, initial_scrpos.ln); + } else if (is_screen_trashed() || !full_screen) + { + int save_top_scroll = top_scroll; + int save_ignore_eoi = ignore_eoi; + top_scroll = 1; + ignore_eoi = 0; + if (is_screen_trashed() == 2) + { + /* Special case used by ignore_eoi: re-open the input file + * and jump to the end of the file. */ + reopen_curr_ifile(); + jump_forw(); + } + repaint(); + top_scroll = save_top_scroll; + ignore_eoi = save_ignore_eoi; + } +} + +/* + * Display the appropriate prompt. + */ +static void prompt(void) +{ + constant char *p; + + if (ungot != NULL && !ungot->ug_end_command) + { + /* + * No prompt necessary if commands are from + * ungotten chars rather than from the user. + */ + return; + } + + /* + * Make sure the screen is displayed. + */ + make_display(); + bottompos = position(BOTTOM_PLUS_ONE); + + /* + * If we've hit EOF on the last file and the -E flag is set, quit. + */ + if (get_quit_at_eof() == OPT_ONPLUS && + eof_displayed(FALSE) && !(ch_getflags() & CH_HELPFILE) && + next_ifile(curr_ifile) == NULL_IFILE) + quit(QUIT_OK); + + /* + * If the entire file is displayed and the -F flag is set, quit. + */ + if (quit_if_one_screen && + entire_file_displayed() && !(ch_getflags() & CH_HELPFILE) && + next_ifile(curr_ifile) == NULL_IFILE) + quit(QUIT_OK); + quit_if_one_screen = FALSE; /* only get one chance at this */ ++ if (first_cmd_at_prompt != NULL) ++ { ++ ungetsc(first_cmd_at_prompt); ++ first_cmd_at_prompt = NULL; ++ return; ++ } + +#if MSDOS_COMPILER==WIN32C + /* + * In Win32, display the file name in the window title. + */ *** 2619 LINES SKIPPED ***