summaryrefslogtreecommitdiff
path: root/decode.c
diff options
context:
space:
mode:
Diffstat (limited to 'decode.c')
-rw-r--r--decode.c315
1 files changed, 180 insertions, 135 deletions
diff --git a/decode.c b/decode.c
index 71b711839ce7..2942a30863cb 100644
--- a/decode.c
+++ b/decode.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 1984-2024 Mark Nudelman
+ * 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.
@@ -37,6 +37,8 @@ extern int erase_char, erase2_char, kill_char;
extern int mousecap;
extern int sc_height;
+static constant lbool allow_drag = TRUE;
+
#if USERFILE
/* "content" is lesskey source, never binary. */
static void add_content_table(int (*call_lesskey)(constant char *, lbool), constant char *envname, lbool sysvar);
@@ -86,6 +88,9 @@ static unsigned char cmdtable[] =
'z',0, A_F_WINDOW,
'w',0, A_B_WINDOW,
ESC,' ',0, A_FF_SCREEN,
+ ESC,'b',0, A_BF_SCREEN,
+ ESC,'j',0, A_F_NEWLINE,
+ ESC,'k',0, A_B_NEWLINE,
'F',0, A_F_FOREVER,
ESC,'F',0, A_F_UNTIL_HILITE,
'R',0, A_FREPAINT,
@@ -178,6 +183,8 @@ static unsigned char cmdtable[] =
'!',0, A_SHELL,
'#',0, A_PSHELL,
'+',0, A_FIRSTCMD,
+ ESC,'[','2','0','0','~',0, A_START_PASTE,
+ ESC,'[','2','0','1','~',0, A_END_PASTE,
'H',0, A_HELP,
'h',0, A_HELP,
@@ -229,6 +236,8 @@ static unsigned char edittable[] =
CONTROL('G'),0, EC_ABORT, /* CTRL-G */
ESC,'[','M',0, EC_X11MOUSE, /* X11 mouse report */
ESC,'[','<',0, EC_X116MOUSE, /* X11 1006 mouse report */
+ ESC,'[','2','0','0','~',0, A_START_PASTE, /* open paste bracket */
+ ESC,'[','2','0','1','~',0, A_END_PASTE, /* close paste bracket */
};
static unsigned char dflt_vartable[] =
@@ -514,22 +523,42 @@ static int mouse_wheel_up(void)
/*
* Return action for the left mouse button trigger.
*/
-static int mouse_button_left(int x, int y)
+static int mouse_button_left(int x, int y, lbool down, lbool drag)
{
- /*
- * {{ It would be better to return an action and then do this
- * in commands() but it's nontrivial to pass y to it. }}
- */
+ static int last_drag_y = -1;
+ static int last_click_y = -1;
+
+ if (down && !drag)
+ {
+ last_drag_y = last_click_y = y;
+ }
+ if (allow_drag && drag && last_drag_y >= 0)
+ {
+ /* Drag text up/down */
+ if (y > last_drag_y)
+ {
+ cmd_exec();
+ backward(y - last_drag_y, FALSE, FALSE, FALSE);
+ last_drag_y = y;
+ } else if (y < last_drag_y)
+ {
+ cmd_exec();
+ forward(last_drag_y - y, FALSE, FALSE, FALSE);
+ last_drag_y = y;
+ }
+ } else if (!down)
+ {
#if OSC8_LINK
- if (osc8_click(y, x))
- return (A_NOACTION);
+ if (osc8_click(y, x))
+ return (A_NOACTION);
#else
- (void) x;
+ (void) x;
#endif /* OSC8_LINK */
- if (y < sc_height-1)
- {
- setmark('#', y);
- screen_trashed();
+ if (y < sc_height-1 && y == last_click_y)
+ {
+ setmark('#', y);
+ screen_trashed();
+ }
}
return (A_NOACTION);
}
@@ -537,14 +566,14 @@ static int mouse_button_left(int x, int y)
/*
* Return action for the right mouse button trigger.
*/
-static int mouse_button_right(int x, int y)
+static int mouse_button_right(int x, int y, lbool down, lbool drag)
{
- (void) x;
+ (void) x; (void) drag;
/*
* {{ unlike mouse_button_left, we could return an action,
* but keep it near mouse_button_left for readability. }}
*/
- if (y < sc_height-1)
+ if (!down && y < sc_height-1)
{
gomark('#');
screen_trashed();
@@ -575,49 +604,61 @@ static int getcc_int(char *pterm)
}
}
+static int x11mouse_button(int btn, int x, int y, lbool down, lbool drag)
+{
+ switch (btn) {
+ case X11MOUSE_BUTTON1:
+ return mouse_button_left(x, y, down, drag);
+ /* is BUTTON2 the rightmost with 2-buttons mouse? */
+ case X11MOUSE_BUTTON2:
+ case X11MOUSE_BUTTON3:
+ return mouse_button_right(x, y, down, drag);
+ }
+ return (A_NOACTION);
+}
+
/*
* Read suffix of mouse input and return the action to take.
* The prefix ("\e[M") has already been read.
*/
-static int x11mouse_action(int skip)
+static int x11mouse_action(lbool skip)
{
static int prev_b = X11MOUSE_BUTTON_REL;
+ int x, y;
int b = getcc() - X11MOUSE_OFFSET;
- int x = getcc() - X11MOUSE_OFFSET-1;
- int y = getcc() - X11MOUSE_OFFSET-1;
+ lbool drag = ((b & X11MOUSE_DRAG) != 0);
+ b &= ~X11MOUSE_DRAG;
+ x = getcc() - X11MOUSE_OFFSET-1;
+ y = getcc() - X11MOUSE_OFFSET-1;
if (skip)
return (A_NOACTION);
switch (b) {
- default:
- prev_b = b;
- return (A_NOACTION);
case X11MOUSE_WHEEL_DOWN:
return mouse_wheel_down();
case X11MOUSE_WHEEL_UP:
return mouse_wheel_up();
- case X11MOUSE_BUTTON_REL:
- /* to trigger on button-up, we check the last button-down */
- switch (prev_b) {
- case X11MOUSE_BUTTON1:
- return mouse_button_left(x, y);
- /* is BUTTON2 the rightmost with 2-buttons mouse? */
- case X11MOUSE_BUTTON2:
- case X11MOUSE_BUTTON3:
- return mouse_button_right(x, y);
- }
- return (A_NOACTION);
+ case X11MOUSE_BUTTON1:
+ case X11MOUSE_BUTTON2:
+ case X11MOUSE_BUTTON3:
+ prev_b = b;
+ return x11mouse_button(b, x, y, TRUE, drag);
+ case X11MOUSE_BUTTON_REL: /* button up */
+ return x11mouse_button(prev_b, x, y, FALSE, drag);
}
+ return (A_NOACTION);
}
/*
* Read suffix of mouse input and return the action to take.
* The prefix ("\e[<") has already been read.
*/
-static int x116mouse_action(int skip)
+static int x116mouse_action(lbool skip)
{
char ch;
int x, y;
int b = getcc_int(&ch);
+ lbool drag = ((b & X11MOUSE_DRAG) != 0);
+ b &= ~X11MOUSE_DRAG;
if (b < 0 || ch != ';') return (A_NOACTION);
x = getcc_int(&ch) - 1;
if (x < 0 || ch != ';') return (A_NOACTION);
@@ -631,106 +672,94 @@ static int x116mouse_action(int skip)
case X11MOUSE_WHEEL_UP:
return mouse_wheel_up();
case X11MOUSE_BUTTON1:
- if (ch != 'm') return (A_NOACTION);
- return mouse_button_left(x, y);
- default:
- if (ch != 'm') return (A_NOACTION);
- /* any other button release */
- return mouse_button_right(x, y);
+ case X11MOUSE_BUTTON2:
+ case X11MOUSE_BUTTON3: {
+ lbool down = (ch == 'M');
+ lbool up = (ch == 'm');
+ if (up || down)
+ return x11mouse_button(b, x, y, down, drag);
+ break; }
}
+ return (A_NOACTION);
}
/*
- * Search a single command table for the command string in cmd.
+ * Return the largest N such that the first N chars of goal
+ * are equal to the last N chars of str.
*/
-static int cmd_search(constant char *cmd, constant char *table, constant char *endtable, constant char **sp)
+static size_t cmd_match(constant char *goal, constant char *str)
{
- constant char *p;
- constant char *q;
- int a = A_INVALID;
+ size_t slen = strlen(str);
+ size_t len;
+ for (len = slen; len > 0; len--)
+ if (strncmp(str + slen - len, goal, len) == 0)
+ break;
+ return len;
+}
- *sp = NULL;
- for (p = table, q = cmd; p < endtable; p++, q++)
+/*
+ * Return pointer to next command table entry.
+ * Also return the action and the extra string from the entry.
+ */
+static constant unsigned char * cmd_next_entry(constant unsigned char *entry, mutable int *action, mutable constant unsigned char **extra, mutable size_t *cmdlen)
+{
+ int a;
+ constant unsigned char *oentry = entry;
+ while (*entry != '\0') /* skip cmd */
+ ++entry;
+ if (cmdlen != NULL)
+ *cmdlen = ptr_diff(entry, oentry);
+ do
+ a = *++entry; /* get action */
+ while (a == A_SKIP);
+ ++entry; /* skip action */
+ if (extra != NULL)
+ *extra = (a & A_EXTRA) ? entry : NULL;
+ if (a & A_EXTRA)
{
- if (*p == *q)
+ while (*entry++ != '\0') /* skip extra string */
+ continue;
+ a &= ~A_EXTRA;
+ }
+ if (action != NULL)
+ *action = a;
+ return entry;
+}
+
+/*
+ * Search a single command table for the command string in cmd.
+ */
+static int cmd_search(constant char *cmd, constant unsigned char *table, constant unsigned char *endtable, constant unsigned char **extra, size_t *mlen)
+{
+ int action = A_INVALID;
+ size_t match_len = 0;
+ if (extra != NULL)
+ *extra = NULL;
+ while (table < endtable)
+ {
+ int taction;
+ const unsigned char *textra;
+ size_t cmdlen;
+ size_t match = cmd_match((constant char *) table, cmd);
+ table = cmd_next_entry(table, &taction, &textra, &cmdlen);
+ if (taction == A_END_LIST)
+ return (-action);
+ if (match >= match_len)
{
- /*
- * Current characters match.
- * If we're at the end of the string, we've found it.
- * Return the action code, which is the character
- * after the null at the end of the string
- * in the command table.
- */
- if (*p == '\0')
+ if (match == cmdlen) /* (last chars of) cmd matches this table entry */
{
- a = *++p & 0377;
- while (a == A_SKIP)
- a = *++p & 0377;
- if (a == A_END_LIST)
- {
- /*
- * We get here only if the original
- * cmd string passed in was empty ("").
- * I don't think that can happen,
- * but just in case ...
- */
- return (A_UINVALID);
- }
- /*
- * Check for an "extra" string.
- */
- if (a & A_EXTRA)
- {
- *sp = ++p;
- while (*p != '\0')
- ++p;
- a &= ~A_EXTRA;
- }
- if (a == A_X11MOUSE_IN)
- a = x11mouse_action(0);
- else if (a == A_X116MOUSE_IN)
- a = x116mouse_action(0);
- q = cmd-1;
- }
- } else if (*q == '\0')
- {
- /*
- * Hit the end of the user's command,
- * but not the end of the string in the command table.
- * The user's command is incomplete.
- */
- if (a == A_INVALID)
- a = A_PREFIX;
- q = cmd-1;
- } else
- {
- /*
- * Not a match.
- * Skip ahead to the next command in the
- * command table, and reset the pointer
- * to the beginning of the user's command.
- */
- if (*p == '\0' && p[1] == A_END_LIST)
+ action = taction;
+ *extra = textra;
+ } else if (match > 0) /* cmd is a prefix of this table entry */
{
- /*
- * A_END_LIST is a special marker that tells
- * us to abort the cmd search.
- * Negative action means accept this action
- * without searching any more cmd tables.
- */
- return -a;
+ action = A_PREFIX;
}
- while (*p++ != '\0')
- continue;
- while (*p == A_SKIP)
- p++;
- if (*p & A_EXTRA)
- while (*++p != '\0')
- continue;
- q = cmd-1;
+ match_len = match;
}
}
- return (a);
+ if (mlen != NULL)
+ *mlen = match_len;
+ return (action);
}
/*
@@ -741,6 +770,7 @@ static int cmd_decode(struct tablelist *tlist, constant char *cmd, constant char
{
struct tablelist *t;
int action = A_INVALID;
+ size_t match_len = 0;
/*
* Search for the cmd thru all the command tables.
@@ -749,18 +779,30 @@ static int cmd_decode(struct tablelist *tlist, constant char *cmd, constant char
*sp = NULL;
for (t = tlist; t != NULL; t = t->t_next)
{
- constant char *tsp;
- int taction = cmd_search(cmd, (char *) t->t_start, (char *) t->t_end, &tsp);
- if (taction == A_UINVALID)
- taction = A_INVALID;
- if (taction != A_INVALID)
+ constant unsigned char *tsp;
+ size_t mlen;
+ int taction = cmd_search(cmd, t->t_start, t->t_end, &tsp, &mlen);
+ if (mlen >= match_len)
{
- *sp = tsp;
- if (taction < 0)
- return (-taction);
- action = taction;
+ match_len = mlen;
+ if (taction == A_UINVALID)
+ taction = A_INVALID;
+ if (taction != A_INVALID)
+ {
+ *sp = (constant char *) tsp;
+ if (taction < 0)
+ {
+ action = -taction;
+ break;
+ }
+ action = taction;
+ }
}
}
+ if (action == A_X11MOUSE_IN)
+ action = x11mouse_action(FALSE);
+ else if (action == A_X116MOUSE_IN)
+ action = x116mouse_action(FALSE);
return (action);
}
@@ -780,6 +822,7 @@ public int ecmd_decode(constant char *cmd, constant char **sp)
return (cmd_decode(list_ecmd_tables, cmd, sp));
}
+
/*
* Get the value of an environment variable.
* Looks first in the lesskey file, then in the real environment.
@@ -1003,10 +1046,12 @@ public int lesskey(constant char *filename, lbool sysvar)
#if HAVE_LESSKEYSRC
static int lesskey_text(constant char *filename, lbool sysvar, lbool content)
{
+ int r;
static struct lesskey_tables tables;
+
if (!secure_allow(SF_LESSKEY))
return (1);
- int r = content ? parse_lesskey_content(filename, &tables) : parse_lesskey(filename, &tables);
+ r = content ? parse_lesskey_content(filename, &tables) : parse_lesskey(filename, &tables);
if (r != 0)
return (r);
add_fcmd_table(tables.cmdtable.buf.data, tables.cmdtable.buf.end);
@@ -1080,8 +1125,10 @@ static int add_hometable(int (*call_lesskey)(constant char *, lbool), constant c
*/
static void add_content_table(int (*call_lesskey)(constant char *, lbool), constant char *envname, lbool sysvar)
{
+ constant char *content;
+
(void) call_lesskey; /* not used */
- constant char *content = lgetenv(envname);
+ content = lgetenv(envname);
if (isnullenv(content))
return;
lesskey_content(content, sysvar);
@@ -1130,9 +1177,9 @@ public int editchar(char c, int flags)
} while (action == A_PREFIX && nch < MAX_CMDLEN);
if (action == EC_X11MOUSE)
- return (x11mouse_action(1));
+ return (x11mouse_action(TRUE));
if (action == EC_X116MOUSE)
- return (x116mouse_action(1));
+ return (x116mouse_action(TRUE));
if (flags & ECF_NORIGHTLEFT)
{
@@ -1160,7 +1207,6 @@ public int editchar(char c, int flags)
}
}
#endif
-#if TAB_COMPLETE_FILENAME
if (flags & ECF_NOCOMPLETE)
{
/*
@@ -1176,7 +1222,6 @@ public int editchar(char c, int flags)
break;
}
}
-#endif
if ((flags & ECF_PEEK) || action == A_INVALID)
{
/*