2000-03-19 10:58:55 +05:30
|
|
|
/* vi: set sw=4 ts=4: */
|
2000-03-16 13:39:57 +05:30
|
|
|
/*
|
2010-09-03 16:21:36 +05:30
|
|
|
* Command line editing.
|
2000-03-16 13:39:57 +05:30
|
|
|
*
|
2003-07-29 12:08:40 +05:30
|
|
|
* Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license.
|
2001-07-13 01:56:32 +05:30
|
|
|
* Written by: Vladimir Oleynik <dzo@simtreas.ru>
|
2001-02-17 00:06:04 +05:30
|
|
|
*
|
|
|
|
* Used ideas:
|
|
|
|
* Adam Rogoyski <rogoyski@cs.utexas.edu>
|
|
|
|
* Dave Cinege <dcinege@psychosis.com>
|
|
|
|
* Jakub Jelinek (c) 1995
|
2003-07-29 12:08:40 +05:30
|
|
|
* Erik Andersen <andersen@codepoet.org> (Majorly adjusted for busybox)
|
2000-03-16 13:39:57 +05:30
|
|
|
*
|
2001-02-17 00:06:04 +05:30
|
|
|
* This code is 'as is' with no warranty.
|
2000-03-16 13:39:57 +05:30
|
|
|
*/
|
|
|
|
/*
|
2008-06-29 02:33:43 +05:30
|
|
|
* Usage and known bugs:
|
2009-10-26 04:20:56 +05:30
|
|
|
* Terminal key codes are not extensive, more needs to be added.
|
|
|
|
* This version was created on Debian GNU/Linux 2.x.
|
2008-06-29 02:33:43 +05:30
|
|
|
* Delete, Backspace, Home, End, and the arrow keys were tested
|
|
|
|
* to work in an Xterm and console. Ctrl-A also works as Home.
|
|
|
|
* Ctrl-E also works as End.
|
|
|
|
*
|
2009-10-26 04:20:56 +05:30
|
|
|
* The following readline-like commands are not implemented:
|
|
|
|
* CTL-t -- Transpose two characters
|
|
|
|
*
|
2008-06-29 02:33:43 +05:30
|
|
|
* lineedit does not know that the terminal escape sequences do not
|
|
|
|
* take up space on the screen. The redisplay code assumes, unless
|
|
|
|
* told otherwise, that each character in the prompt is a printable
|
|
|
|
* character that takes up one character position on the screen.
|
|
|
|
* You need to tell lineedit that some sequences of characters
|
|
|
|
* in the prompt take up no screen space. Compatibly with readline,
|
|
|
|
* use the \[ escape to begin a sequence of non-printing characters,
|
|
|
|
* and the \] escape to signal the end of such a sequence. Example:
|
|
|
|
*
|
|
|
|
* PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '
|
2013-08-19 20:15:04 +05:30
|
|
|
*
|
|
|
|
* Unicode in PS1 is not fully supported: prompt length calulation is wrong,
|
|
|
|
* resulting in line wrap problems with long (multi-line) input.
|
2001-02-17 00:06:04 +05:30
|
|
|
*/
|
2015-05-29 16:01:40 +05:30
|
|
|
#include "busybox.h"
|
|
|
|
#include "NUM_APPLETS.h"
|
2009-07-12 01:06:13 +05:30
|
|
|
#include "unicode.h"
|
2011-05-20 07:56:29 +05:30
|
|
|
#ifndef _POSIX_VDISABLE
|
|
|
|
# define _POSIX_VDISABLE '\0'
|
|
|
|
#endif
|
|
|
|
|
2004-01-22 18:12:23 +05:30
|
|
|
|
2006-12-19 06:40:25 +05:30
|
|
|
#ifdef TEST
|
2009-07-10 22:10:49 +05:30
|
|
|
# define ENABLE_FEATURE_EDITING 0
|
|
|
|
# define ENABLE_FEATURE_TAB_COMPLETION 0
|
|
|
|
# define ENABLE_FEATURE_USERNAME_COMPLETION 0
|
|
|
|
#endif
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2001-02-20 12:12:29 +05:30
|
|
|
|
2007-01-22 00:48:19 +05:30
|
|
|
/* Entire file (except TESTing part) sits inside this #if */
|
2007-01-22 14:33:07 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING
|
2000-03-16 13:39:57 +05:30
|
|
|
|
2007-01-22 00:48:19 +05:30
|
|
|
|
2020-12-16 15:44:08 +05:30
|
|
|
#if !ENABLE_SHELL_ASH && !ENABLE_SHELL_HUSH
|
|
|
|
/* so far only shells use these features */
|
|
|
|
# undef ENABLE_FEATURE_EDITING_FANCY_PROMPT
|
|
|
|
# undef ENABLE_FEATURE_TAB_COMPLETION
|
|
|
|
# undef ENABLE_FEATURE_USERNAME_COMPLETION
|
|
|
|
# define ENABLE_FEATURE_EDITING_FANCY_PROMPT 0
|
|
|
|
# define ENABLE_FEATURE_TAB_COMPLETION 0
|
|
|
|
# define ENABLE_FEATURE_USERNAME_COMPLETION 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2010-07-19 01:51:24 +05:30
|
|
|
#define ENABLE_USERNAME_OR_HOMEDIR \
|
2007-11-10 07:05:47 +05:30
|
|
|
(ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT)
|
2010-07-19 01:51:24 +05:30
|
|
|
#if ENABLE_USERNAME_OR_HOMEDIR
|
|
|
|
# define IF_USERNAME_OR_HOMEDIR(...) __VA_ARGS__
|
2020-12-16 15:44:08 +05:30
|
|
|
#else
|
|
|
|
# define IF_USERNAME_OR_HOMEDIR(...) /*nothing*/
|
2007-11-10 07:05:47 +05:30
|
|
|
#endif
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2009-07-10 22:10:49 +05:30
|
|
|
|
|
|
|
#undef CHAR_T
|
2010-03-26 18:36:56 +05:30
|
|
|
#if ENABLE_UNICODE_SUPPORT
|
2010-04-29 17:13:39 +05:30
|
|
|
# define BB_NUL ((wchar_t)0)
|
2009-07-10 22:10:49 +05:30
|
|
|
# define CHAR_T wchar_t
|
2019-05-14 20:56:47 +05:30
|
|
|
static bool BB_isspace(CHAR_T c)
|
|
|
|
{
|
|
|
|
return ((unsigned)c < 256 && isspace(c));
|
|
|
|
}
|
2009-12-12 07:12:35 +05:30
|
|
|
# if ENABLE_FEATURE_EDITING_VI
|
2019-05-14 20:56:47 +05:30
|
|
|
static bool BB_isalnum_or_underscore(CHAR_T c)
|
|
|
|
{
|
2016-08-15 03:00:29 +05:30
|
|
|
return ((unsigned)c < 256 && isalnum(c)) || c == '_';
|
|
|
|
}
|
2009-12-12 07:12:35 +05:30
|
|
|
# endif
|
2019-05-14 20:56:47 +05:30
|
|
|
static bool BB_ispunct(CHAR_T c)
|
|
|
|
{
|
|
|
|
return ((unsigned)c < 256 && ispunct(c));
|
|
|
|
}
|
2009-07-10 22:10:49 +05:30
|
|
|
# undef isspace
|
|
|
|
# undef isalnum
|
|
|
|
# undef ispunct
|
|
|
|
# undef isprint
|
|
|
|
# define isspace isspace_must_not_be_used
|
|
|
|
# define isalnum isalnum_must_not_be_used
|
|
|
|
# define ispunct ispunct_must_not_be_used
|
|
|
|
# define isprint isprint_must_not_be_used
|
|
|
|
#else
|
|
|
|
# define BB_NUL '\0'
|
|
|
|
# define CHAR_T char
|
|
|
|
# define BB_isspace(c) isspace(c)
|
2016-08-15 03:00:29 +05:30
|
|
|
# if ENABLE_FEATURE_EDITING_VI
|
2017-12-31 22:00:02 +05:30
|
|
|
static bool BB_isalnum_or_underscore(CHAR_T c)
|
|
|
|
{
|
2020-06-24 13:01:30 +05:30
|
|
|
return isalnum(c) || c == '_';
|
2016-08-15 03:00:29 +05:30
|
|
|
}
|
|
|
|
# endif
|
2009-07-10 22:10:49 +05:30
|
|
|
# define BB_ispunct(c) ispunct(c)
|
|
|
|
#endif
|
2010-07-19 01:51:24 +05:30
|
|
|
#if ENABLE_UNICODE_PRESERVE_BROKEN
|
|
|
|
# define unicode_mark_raw_byte(wc) ((wc) | 0x20000000)
|
|
|
|
# define unicode_is_raw_byte(wc) ((wc) & 0x20000000)
|
|
|
|
#else
|
|
|
|
# define unicode_is_raw_byte(wc) 0
|
|
|
|
#endif
|
2009-07-10 22:10:49 +05:30
|
|
|
|
|
|
|
|
2010-10-29 01:04:56 +05:30
|
|
|
#define ESC "\033"
|
|
|
|
|
|
|
|
#define SEQ_CLEAR_TILL_END_OF_SCREEN ESC"[J"
|
|
|
|
//#define SEQ_CLEAR_TILL_END_OF_LINE ESC"[K"
|
2010-04-29 17:13:39 +05:30
|
|
|
|
|
|
|
|
2007-11-10 07:05:47 +05:30
|
|
|
enum {
|
|
|
|
MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0
|
2008-01-07 21:43:14 +05:30
|
|
|
? CONFIG_FEATURE_EDITING_MAX_LEN
|
2007-11-10 07:05:47 +05:30
|
|
|
: 0x7ff0
|
|
|
|
};
|
|
|
|
|
|
|
|
/* We try to minimize both static and stack usage. */
|
2008-04-22 05:38:27 +05:30
|
|
|
struct lineedit_statics {
|
2007-11-10 07:05:47 +05:30
|
|
|
line_input_t *state;
|
2000-04-21 06:56:49 +05:30
|
|
|
|
2016-11-28 02:55:07 +05:30
|
|
|
unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */
|
2000-05-20 06:10:08 +05:30
|
|
|
|
2009-05-17 20:14:54 +05:30
|
|
|
unsigned cmdedit_x; /* real x (col) terminal position */
|
|
|
|
unsigned cmdedit_y; /* pseudoreal y (row) terminal position */
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
unsigned cmdedit_prmt_len; /* on-screen length of last/sole prompt line */
|
2000-12-19 01:55:50 +05:30
|
|
|
|
2007-11-10 07:05:47 +05:30
|
|
|
unsigned cursor;
|
2009-09-29 03:30:12 +05:30
|
|
|
int command_len; /* must be signed */
|
|
|
|
/* signed maxsize: we want x in "if (x > S.maxsize)"
|
2009-07-12 06:20:35 +05:30
|
|
|
* to _not_ be promoted to unsigned */
|
|
|
|
int maxsize;
|
2009-07-10 22:10:49 +05:30
|
|
|
CHAR_T *command_ps;
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2007-11-10 07:05:47 +05:30
|
|
|
const char *cmdedit_prompt;
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
const char *prompt_last_line; /* last/sole prompt line */
|
2007-11-10 07:05:47 +05:30
|
|
|
|
2010-07-19 01:51:24 +05:30
|
|
|
#if ENABLE_USERNAME_OR_HOMEDIR
|
2007-11-10 07:05:47 +05:30
|
|
|
char *user_buf;
|
2021-10-12 02:38:31 +05:30
|
|
|
char *home_pwd_buf;
|
|
|
|
smallint got_user_strings;
|
2007-11-10 07:05:47 +05:30
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLE_FEATURE_TAB_COMPLETION
|
|
|
|
unsigned num_matches;
|
2021-10-12 02:38:31 +05:30
|
|
|
char **matches;
|
2001-02-17 00:06:04 +05:30
|
|
|
#endif
|
|
|
|
|
2018-02-26 00:39:54 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_WINCH
|
2016-11-28 02:55:07 +05:30
|
|
|
unsigned SIGWINCH_saved;
|
|
|
|
volatile unsigned SIGWINCH_count;
|
|
|
|
volatile smallint ok_to_redraw;
|
2018-02-26 00:39:54 +05:30
|
|
|
#endif
|
2016-11-28 02:55:07 +05:30
|
|
|
|
2007-11-10 07:05:47 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_VI
|
2010-07-19 01:51:24 +05:30
|
|
|
# define DELBUFSIZ 128
|
2007-11-10 07:05:47 +05:30
|
|
|
smallint newdelflag; /* whether delbuf should be reused yet */
|
2016-11-28 02:55:07 +05:30
|
|
|
CHAR_T *delptr;
|
2009-07-10 22:10:49 +05:30
|
|
|
CHAR_T delbuf[DELBUFSIZ]; /* a place to store deleted characters */
|
2007-11-10 07:05:47 +05:30
|
|
|
#endif
|
2009-10-27 15:04:06 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
|
2009-10-27 15:17:49 +05:30
|
|
|
smallint sent_ESC_br6n;
|
2009-10-27 15:04:06 +05:30
|
|
|
#endif
|
2016-11-28 02:55:07 +05:30
|
|
|
|
2018-02-26 00:39:54 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_WINCH
|
2016-11-28 02:55:07 +05:30
|
|
|
/* Largish struct, keeping it last results in smaller code */
|
|
|
|
struct sigaction SIGWINCH_handler;
|
2018-02-26 00:39:54 +05:30
|
|
|
#endif
|
2007-11-10 07:05:47 +05:30
|
|
|
};
|
|
|
|
|
2008-04-22 05:38:27 +05:30
|
|
|
/* See lineedit_ptr_hack.c */
|
2021-09-29 15:07:26 +05:30
|
|
|
extern struct lineedit_statics *BB_GLOBAL_CONST lineedit_ptr_to_statics;
|
2007-11-10 07:05:47 +05:30
|
|
|
|
2008-04-22 05:38:27 +05:30
|
|
|
#define S (*lineedit_ptr_to_statics)
|
2007-11-10 07:05:47 +05:30
|
|
|
#define state (S.state )
|
|
|
|
#define cmdedit_termw (S.cmdedit_termw )
|
|
|
|
#define cmdedit_x (S.cmdedit_x )
|
|
|
|
#define cmdedit_y (S.cmdedit_y )
|
|
|
|
#define cmdedit_prmt_len (S.cmdedit_prmt_len)
|
|
|
|
#define cursor (S.cursor )
|
|
|
|
#define command_len (S.command_len )
|
|
|
|
#define command_ps (S.command_ps )
|
|
|
|
#define cmdedit_prompt (S.cmdedit_prompt )
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
#define prompt_last_line (S.prompt_last_line)
|
2007-12-24 19:39:19 +05:30
|
|
|
#define user_buf (S.user_buf )
|
2007-11-10 07:05:47 +05:30
|
|
|
#define home_pwd_buf (S.home_pwd_buf )
|
2021-10-12 02:38:31 +05:30
|
|
|
#define got_user_strings (S.got_user_strings)
|
2007-11-10 07:05:47 +05:30
|
|
|
#define num_matches (S.num_matches )
|
2021-10-12 02:38:31 +05:30
|
|
|
#define matches (S.matches )
|
2007-11-10 07:05:47 +05:30
|
|
|
#define delptr (S.delptr )
|
|
|
|
#define newdelflag (S.newdelflag )
|
|
|
|
#define delbuf (S.delbuf )
|
|
|
|
|
|
|
|
#define INIT_S() do { \
|
2021-10-09 23:49:51 +05:30
|
|
|
XZALLOC_CONST_PTR(&lineedit_ptr_to_statics, sizeof(S)); \
|
2007-11-10 07:05:47 +05:30
|
|
|
} while (0)
|
2011-05-20 07:56:29 +05:30
|
|
|
|
2007-11-10 07:05:47 +05:30
|
|
|
static void deinit_S(void)
|
|
|
|
{
|
|
|
|
#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
|
|
|
|
/* This one is allocated only if FANCY_PROMPT is on
|
2010-09-03 16:21:36 +05:30
|
|
|
* (otherwise it points to verbatim prompt (NOT malloced)) */
|
2007-11-10 07:05:47 +05:30
|
|
|
free((char*)cmdedit_prompt);
|
|
|
|
#endif
|
2010-07-19 01:51:24 +05:30
|
|
|
#if ENABLE_USERNAME_OR_HOMEDIR
|
2007-11-10 07:05:47 +05:30
|
|
|
free(user_buf);
|
2021-10-12 02:38:31 +05:30
|
|
|
free(home_pwd_buf);
|
2007-01-22 00:48:19 +05:30
|
|
|
#endif
|
2008-04-22 05:38:27 +05:30
|
|
|
free(lineedit_ptr_to_statics);
|
2007-11-10 07:05:47 +05:30
|
|
|
}
|
|
|
|
#define DEINIT_S() deinit_S()
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2008-04-26 00:14:35 +05:30
|
|
|
|
2021-10-12 02:38:31 +05:30
|
|
|
#if ENABLE_USERNAME_OR_HOMEDIR
|
|
|
|
/* Call getpwuid() only if necessary.
|
|
|
|
* E.g. if PS1=':', no user database reading is needed to generate prompt.
|
|
|
|
* (Unfortunately, default PS1='\w \$' needs it, \w abbreviates homedir
|
|
|
|
* as ~/... - for that it needs to *know* the homedir...)
|
|
|
|
*/
|
|
|
|
static void get_user_strings(void)
|
|
|
|
{
|
|
|
|
struct passwd *entry;
|
|
|
|
|
|
|
|
got_user_strings = 1;
|
|
|
|
entry = getpwuid(geteuid());
|
|
|
|
if (entry) {
|
|
|
|
user_buf = xstrdup(entry->pw_name);
|
|
|
|
home_pwd_buf = xstrdup(entry->pw_dir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *get_username_str(void)
|
|
|
|
{
|
|
|
|
if (!got_user_strings)
|
|
|
|
get_user_strings();
|
|
|
|
return user_buf ? user_buf : "";
|
|
|
|
/* btw, bash uses "I have no name!" string if uid has no entry */
|
|
|
|
}
|
|
|
|
|
|
|
|
static NOINLINE const char *get_homedir_or_NULL(void)
|
|
|
|
{
|
2022-03-24 17:47:25 +05:30
|
|
|
const char *home;
|
|
|
|
|
|
|
|
# if ENABLE_SHELL_ASH || ENABLE_SHELL_HUSH
|
|
|
|
home = state->sh_get_var ? state->sh_get_var("HOME") : getenv("HOME");
|
|
|
|
# else
|
|
|
|
home = getenv("HOME");
|
|
|
|
# endif
|
|
|
|
if (home != NULL && home[0] != '\0')
|
|
|
|
return home;
|
|
|
|
|
2021-10-12 02:38:31 +05:30
|
|
|
if (!got_user_strings)
|
|
|
|
get_user_strings();
|
|
|
|
return home_pwd_buf;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-03-26 18:36:56 +05:30
|
|
|
#if ENABLE_UNICODE_SUPPORT
|
2011-07-11 11:06:59 +05:30
|
|
|
static size_t load_string(const char *src)
|
2009-07-10 22:10:49 +05:30
|
|
|
{
|
2011-03-27 05:48:07 +05:30
|
|
|
if (unicode_status == UNICODE_ON) {
|
2011-07-11 11:06:59 +05:30
|
|
|
ssize_t len = mbstowcs(command_ps, src, S.maxsize - 1);
|
2011-03-27 05:48:07 +05:30
|
|
|
if (len < 0)
|
|
|
|
len = 0;
|
|
|
|
command_ps[len] = BB_NUL;
|
|
|
|
return len;
|
|
|
|
} else {
|
|
|
|
unsigned i = 0;
|
2011-07-11 11:06:59 +05:30
|
|
|
while (src[i] && i < S.maxsize - 1) {
|
|
|
|
command_ps[i] = src[i];
|
2011-03-27 05:48:07 +05:30
|
|
|
i++;
|
2011-07-11 11:06:59 +05:30
|
|
|
}
|
|
|
|
command_ps[i] = BB_NUL;
|
2011-03-27 05:48:07 +05:30
|
|
|
return i;
|
|
|
|
}
|
2009-07-10 22:10:49 +05:30
|
|
|
}
|
2010-04-29 17:13:39 +05:30
|
|
|
static unsigned save_string(char *dst, unsigned maxsize)
|
2009-07-10 22:10:49 +05:30
|
|
|
{
|
2011-03-27 05:48:07 +05:30
|
|
|
if (unicode_status == UNICODE_ON) {
|
2010-05-11 18:19:13 +05:30
|
|
|
# if !ENABLE_UNICODE_PRESERVE_BROKEN
|
2011-03-27 05:48:07 +05:30
|
|
|
ssize_t len = wcstombs(dst, command_ps, maxsize - 1);
|
|
|
|
if (len < 0)
|
|
|
|
len = 0;
|
|
|
|
dst[len] = '\0';
|
|
|
|
return len;
|
2010-05-11 18:19:13 +05:30
|
|
|
# else
|
2011-03-27 05:48:07 +05:30
|
|
|
unsigned dstpos = 0;
|
|
|
|
unsigned srcpos = 0;
|
|
|
|
|
|
|
|
maxsize--;
|
|
|
|
while (dstpos < maxsize) {
|
|
|
|
wchar_t wc;
|
|
|
|
int n = srcpos;
|
2010-04-29 17:13:39 +05:30
|
|
|
|
2011-03-27 05:48:07 +05:30
|
|
|
/* Convert up to 1st invalid byte (or up to end) */
|
|
|
|
while ((wc = command_ps[srcpos]) != BB_NUL
|
|
|
|
&& !unicode_is_raw_byte(wc)
|
|
|
|
) {
|
|
|
|
srcpos++;
|
|
|
|
}
|
|
|
|
command_ps[srcpos] = BB_NUL;
|
|
|
|
n = wcstombs(dst + dstpos, command_ps + n, maxsize - dstpos);
|
|
|
|
if (n < 0) /* should not happen */
|
|
|
|
break;
|
|
|
|
dstpos += n;
|
|
|
|
if (wc == BB_NUL) /* usually is */
|
|
|
|
break;
|
2010-09-02 15:31:11 +05:30
|
|
|
|
2011-03-27 05:48:07 +05:30
|
|
|
/* We do have invalid byte here! */
|
|
|
|
command_ps[srcpos] = wc; /* restore it */
|
2010-04-29 17:13:39 +05:30
|
|
|
srcpos++;
|
2011-03-27 05:48:07 +05:30
|
|
|
if (dstpos == maxsize)
|
|
|
|
break;
|
|
|
|
dst[dstpos++] = (char) wc;
|
2010-04-29 17:13:39 +05:30
|
|
|
}
|
2011-03-27 05:48:07 +05:30
|
|
|
dst[dstpos] = '\0';
|
|
|
|
return dstpos;
|
2010-05-11 18:19:13 +05:30
|
|
|
# endif
|
2011-03-27 05:48:07 +05:30
|
|
|
} else {
|
|
|
|
unsigned i = 0;
|
|
|
|
while ((dst[i] = command_ps[i]) != 0)
|
|
|
|
i++;
|
|
|
|
return i;
|
|
|
|
}
|
2009-07-10 22:10:49 +05:30
|
|
|
}
|
|
|
|
/* I thought just fputwc(c, stdout) would work. But no... */
|
|
|
|
static void BB_PUTCHAR(wchar_t c)
|
|
|
|
{
|
2011-03-27 05:48:07 +05:30
|
|
|
if (unicode_status == UNICODE_ON) {
|
|
|
|
char buf[MB_CUR_MAX + 1];
|
|
|
|
mbstate_t mbst = { 0 };
|
|
|
|
ssize_t len = wcrtomb(buf, c, &mbst);
|
|
|
|
if (len > 0) {
|
|
|
|
buf[len] = '\0';
|
2021-02-04 01:17:14 +05:30
|
|
|
fputs_stdout(buf);
|
2011-03-27 05:48:07 +05:30
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* In this case, c is always one byte */
|
|
|
|
putchar(c);
|
2009-07-10 22:10:49 +05:30
|
|
|
}
|
|
|
|
}
|
2010-05-17 00:16:53 +05:30
|
|
|
# if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS
|
|
|
|
static wchar_t adjust_width_and_validate_wc(unsigned *width_adj, wchar_t wc)
|
|
|
|
# else
|
|
|
|
static wchar_t adjust_width_and_validate_wc(wchar_t wc)
|
|
|
|
# define adjust_width_and_validate_wc(width_adj, wc) \
|
|
|
|
((*(width_adj))++, adjust_width_and_validate_wc(wc))
|
|
|
|
# endif
|
|
|
|
{
|
|
|
|
int w = 1;
|
|
|
|
|
|
|
|
if (unicode_status == UNICODE_ON) {
|
2010-05-17 00:45:03 +05:30
|
|
|
if (wc > CONFIG_LAST_SUPPORTED_WCHAR) {
|
|
|
|
/* note: also true for unicode_is_raw_byte(wc) */
|
2010-05-17 00:16:53 +05:30
|
|
|
goto subst;
|
|
|
|
}
|
|
|
|
w = wcwidth(wc);
|
|
|
|
if ((ENABLE_UNICODE_COMBINING_WCHARS && w < 0)
|
|
|
|
|| (!ENABLE_UNICODE_COMBINING_WCHARS && w <= 0)
|
|
|
|
|| (!ENABLE_UNICODE_WIDE_WCHARS && w > 1)
|
|
|
|
) {
|
|
|
|
subst:
|
|
|
|
w = 1;
|
|
|
|
wc = CONFIG_SUBST_WCHAR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS
|
|
|
|
*width_adj += w;
|
|
|
|
#endif
|
|
|
|
return wc;
|
|
|
|
}
|
|
|
|
#else /* !UNICODE */
|
2011-07-11 11:06:59 +05:30
|
|
|
static size_t load_string(const char *src)
|
2009-07-10 22:10:49 +05:30
|
|
|
{
|
2011-07-11 11:06:59 +05:30
|
|
|
safe_strncpy(command_ps, src, S.maxsize);
|
2009-07-10 22:10:49 +05:30
|
|
|
return strlen(command_ps);
|
|
|
|
}
|
2009-07-15 23:32:19 +05:30
|
|
|
# if ENABLE_FEATURE_TAB_COMPLETION
|
2010-04-29 17:13:39 +05:30
|
|
|
static void save_string(char *dst, unsigned maxsize)
|
2009-07-10 22:10:49 +05:30
|
|
|
{
|
|
|
|
safe_strncpy(dst, command_ps, maxsize);
|
|
|
|
}
|
2009-07-15 21:57:47 +05:30
|
|
|
# endif
|
|
|
|
# define BB_PUTCHAR(c) bb_putchar(c)
|
2010-05-17 00:16:53 +05:30
|
|
|
/* Should never be called: */
|
|
|
|
int adjust_width_and_validate_wc(unsigned *width_adj, int wc);
|
2009-07-10 22:10:49 +05:30
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2007-01-22 00:48:19 +05:30
|
|
|
/* Put 'command_ps[cursor]', cursor++.
|
|
|
|
* Advance cursor on screen. If we reached right margin, scroll text up
|
|
|
|
* and remove terminal margin effect by printing 'next_char' */
|
2008-04-26 00:14:35 +05:30
|
|
|
#define HACK_FOR_WRONG_WIDTH 1
|
2010-05-11 18:19:13 +05:30
|
|
|
static void put_cur_glyph_and_inc_cursor(void)
|
2001-02-17 00:06:04 +05:30
|
|
|
{
|
2009-07-10 22:10:49 +05:30
|
|
|
CHAR_T c = command_ps[cursor];
|
2010-05-17 00:16:53 +05:30
|
|
|
unsigned width = 0;
|
|
|
|
int ofs_to_right;
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2009-07-10 22:10:49 +05:30
|
|
|
if (c == BB_NUL) {
|
2007-01-22 00:48:19 +05:30
|
|
|
/* erase character after end of input string */
|
|
|
|
c = ' ';
|
2010-05-11 18:19:13 +05:30
|
|
|
} else {
|
|
|
|
/* advance cursor only if we aren't at the end yet */
|
|
|
|
cursor++;
|
2010-05-17 00:16:53 +05:30
|
|
|
if (unicode_status == UNICODE_ON) {
|
|
|
|
IF_UNICODE_WIDE_WCHARS(width = cmdedit_x;)
|
|
|
|
c = adjust_width_and_validate_wc(&cmdedit_x, c);
|
|
|
|
IF_UNICODE_WIDE_WCHARS(width = cmdedit_x - width;)
|
|
|
|
} else {
|
|
|
|
cmdedit_x++;
|
|
|
|
}
|
2007-01-22 00:48:19 +05:30
|
|
|
}
|
2010-05-11 18:19:13 +05:30
|
|
|
|
2010-05-17 00:16:53 +05:30
|
|
|
ofs_to_right = cmdedit_x - cmdedit_termw;
|
|
|
|
if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right <= 0) {
|
|
|
|
/* c fits on this line */
|
2009-07-10 22:10:49 +05:30
|
|
|
BB_PUTCHAR(c);
|
2006-12-22 03:57:10 +05:30
|
|
|
}
|
2010-05-17 00:16:53 +05:30
|
|
|
|
|
|
|
if (ofs_to_right >= 0) {
|
|
|
|
/* we go to the next line */
|
2008-04-26 00:14:35 +05:30
|
|
|
#if HACK_FOR_WRONG_WIDTH
|
|
|
|
/* This works better if our idea of term width is wrong
|
|
|
|
* and it is actually wider (often happens on serial lines).
|
|
|
|
* Printing CR,LF *forces* cursor to next line.
|
|
|
|
* OTOH if terminal width is correct AND terminal does NOT
|
|
|
|
* have automargin (IOW: it is moving cursor to next line
|
|
|
|
* by itself (which is wrong for VT-10x terminals)),
|
|
|
|
* this will break things: there will be one extra empty line */
|
|
|
|
puts("\r"); /* + implicit '\n' */
|
|
|
|
#else
|
2010-05-11 18:19:13 +05:30
|
|
|
/* VT-10x terminals don't wrap cursor to next line when last char
|
|
|
|
* on the line is printed - cursor stays "over" this char.
|
|
|
|
* Need to print _next_ char too (first one to appear on next line)
|
|
|
|
* to make cursor move down to next line.
|
|
|
|
*/
|
|
|
|
/* Works ok only if cmdedit_termw is correct. */
|
|
|
|
c = command_ps[cursor];
|
|
|
|
if (c == BB_NUL)
|
|
|
|
c = ' ';
|
|
|
|
BB_PUTCHAR(c);
|
2007-09-27 15:50:47 +05:30
|
|
|
bb_putchar('\b');
|
2008-04-26 00:14:35 +05:30
|
|
|
#endif
|
2010-05-17 00:16:53 +05:30
|
|
|
cmdedit_y++;
|
|
|
|
if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) {
|
|
|
|
width = 0;
|
|
|
|
} else { /* ofs_to_right > 0 */
|
|
|
|
/* wide char c didn't fit on prev line */
|
|
|
|
BB_PUTCHAR(c);
|
|
|
|
}
|
|
|
|
cmdedit_x = width;
|
2000-03-19 10:58:55 +05:30
|
|
|
}
|
2000-03-16 13:39:57 +05:30
|
|
|
}
|
|
|
|
|
2007-01-22 00:48:19 +05:30
|
|
|
/* Move to end of line (by printing all chars till the end) */
|
2010-05-11 18:19:13 +05:30
|
|
|
static void put_till_end_and_adv_cursor(void)
|
2001-02-17 00:06:04 +05:30
|
|
|
{
|
2007-01-22 12:51:38 +05:30
|
|
|
while (cursor < command_len)
|
2010-05-11 18:19:13 +05:30
|
|
|
put_cur_glyph_and_inc_cursor();
|
2001-01-27 02:12:23 +05:30
|
|
|
}
|
2000-07-04 11:52:18 +05:30
|
|
|
|
2001-01-27 02:12:23 +05:30
|
|
|
/* Go to the next line */
|
2001-02-17 00:06:04 +05:30
|
|
|
static void goto_new_line(void)
|
|
|
|
{
|
2010-05-11 18:19:13 +05:30
|
|
|
put_till_end_and_adv_cursor();
|
2018-04-06 21:28:21 +05:30
|
|
|
/* "cursor == 0" is only if prompt is "" and user input is empty */
|
|
|
|
if (cursor == 0 || cmdedit_x != 0)
|
2007-09-27 15:50:47 +05:30
|
|
|
bb_putchar('\n');
|
2001-01-27 02:12:23 +05:30
|
|
|
}
|
2000-07-04 11:52:18 +05:30
|
|
|
|
2006-08-30 01:11:06 +05:30
|
|
|
static void beep(void)
|
2001-02-17 00:06:04 +05:30
|
|
|
{
|
2007-09-27 15:50:47 +05:30
|
|
|
bb_putchar('\007');
|
2001-02-17 00:06:04 +05:30
|
|
|
}
|
2000-03-16 13:39:57 +05:30
|
|
|
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
/* Full or last/sole prompt line, reset edit cursor, calculate terminal cursor.
|
|
|
|
* cmdedit_y is always calculated for the last/sole prompt line.
|
|
|
|
*/
|
|
|
|
static void put_prompt_custom(bool is_full)
|
2010-05-17 04:15:44 +05:30
|
|
|
{
|
2021-02-04 01:17:14 +05:30
|
|
|
fputs_stdout((is_full ? cmdedit_prompt : prompt_last_line));
|
2010-05-17 04:15:44 +05:30
|
|
|
cursor = 0;
|
2016-11-28 02:55:07 +05:30
|
|
|
cmdedit_y = cmdedit_prmt_len / cmdedit_termw; /* new quasireal y */
|
|
|
|
cmdedit_x = cmdedit_prmt_len % cmdedit_termw;
|
2010-05-17 04:15:44 +05:30
|
|
|
}
|
|
|
|
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
#define put_prompt_last_line() put_prompt_custom(0)
|
|
|
|
#define put_prompt() put_prompt_custom(1)
|
|
|
|
|
2004-04-14 23:21:38 +05:30
|
|
|
/* Move back one character */
|
2007-01-22 00:48:59 +05:30
|
|
|
/* (optimized for slow terminals) */
|
|
|
|
static void input_backward(unsigned num)
|
2001-01-27 02:12:23 +05:30
|
|
|
{
|
2001-02-17 00:06:04 +05:30
|
|
|
if (num > cursor)
|
|
|
|
num = cursor;
|
2010-05-17 00:16:53 +05:30
|
|
|
if (num == 0)
|
2007-01-22 00:48:59 +05:30
|
|
|
return;
|
|
|
|
cursor -= num;
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2010-05-17 00:16:53 +05:30
|
|
|
if ((ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS)
|
|
|
|
&& unicode_status == UNICODE_ON
|
|
|
|
) {
|
|
|
|
/* correct NUM to be equal to _screen_ width */
|
|
|
|
int n = num;
|
|
|
|
num = 0;
|
|
|
|
while (--n >= 0)
|
|
|
|
adjust_width_and_validate_wc(&num, command_ps[cursor + n]);
|
|
|
|
if (num == 0)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-05-26 03:22:03 +05:30
|
|
|
if (cmdedit_x >= num) {
|
2001-02-17 00:06:04 +05:30
|
|
|
cmdedit_x -= num;
|
2007-01-22 00:48:59 +05:30
|
|
|
if (num <= 4) {
|
2008-02-19 03:58:03 +05:30
|
|
|
/* This is longer by 5 bytes on x86.
|
2008-05-26 03:22:03 +05:30
|
|
|
* Also gets miscompiled for ARM users
|
|
|
|
* (busybox.net/bugs/view.php?id=2274).
|
2008-02-19 03:58:03 +05:30
|
|
|
* printf(("\b\b\b\b" + 4) - num);
|
|
|
|
* return;
|
|
|
|
*/
|
|
|
|
do {
|
|
|
|
bb_putchar('\b');
|
|
|
|
} while (--num);
|
2007-01-22 00:48:59 +05:30
|
|
|
return;
|
|
|
|
}
|
2010-10-29 01:04:56 +05:30
|
|
|
printf(ESC"[%uD", num);
|
2007-01-22 00:48:59 +05:30
|
|
|
return;
|
2001-01-27 02:12:23 +05:30
|
|
|
}
|
2007-01-22 00:48:59 +05:30
|
|
|
|
|
|
|
/* Need to go one or more lines up */
|
2010-05-17 04:15:44 +05:30
|
|
|
if (ENABLE_UNICODE_WIDE_WCHARS) {
|
|
|
|
/* With wide chars, it is hard to "backtrack"
|
|
|
|
* and reliably figure out where to put cursor.
|
|
|
|
* Example (<> is a wide char; # is an ordinary char, _ cursor):
|
|
|
|
* |prompt: <><> |
|
|
|
|
* |<><><><><><> |
|
|
|
|
* |_ |
|
|
|
|
* and user presses left arrow. num = 1, cmdedit_x = 0,
|
|
|
|
* We need to go up one line, and then - how do we know that
|
|
|
|
* we need to go *10* positions to the right? Because
|
|
|
|
* |prompt: <>#<>|
|
|
|
|
* |<><><>#<><><>|
|
|
|
|
* |_ |
|
|
|
|
* in this situation we need to go *11* positions to the right.
|
|
|
|
*
|
|
|
|
* A simpler thing to do is to redraw everything from the start
|
|
|
|
* up to new cursor position (which is already known):
|
|
|
|
*/
|
|
|
|
unsigned sv_cursor;
|
2010-05-17 07:35:53 +05:30
|
|
|
/* go to 1st column; go up to first line */
|
2010-10-29 01:04:56 +05:30
|
|
|
printf("\r" ESC"[%uA", cmdedit_y);
|
2010-05-17 04:15:44 +05:30
|
|
|
cmdedit_y = 0;
|
|
|
|
sv_cursor = cursor;
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
put_prompt_last_line(); /* sets cursor to 0 */
|
2010-05-17 04:15:44 +05:30
|
|
|
while (cursor < sv_cursor)
|
|
|
|
put_cur_glyph_and_inc_cursor();
|
|
|
|
} else {
|
2010-05-17 16:00:44 +05:30
|
|
|
int lines_up;
|
|
|
|
/* num = chars to go back from the beginning of current line: */
|
2010-05-17 04:15:44 +05:30
|
|
|
num -= cmdedit_x;
|
2010-05-17 16:00:44 +05:30
|
|
|
/* num=1...w: one line up, w+1...2w: two, etc: */
|
2016-11-28 02:55:07 +05:30
|
|
|
lines_up = 1 + (num - 1) / cmdedit_termw;
|
|
|
|
cmdedit_x = (cmdedit_termw * cmdedit_y - num) % cmdedit_termw;
|
2010-05-17 16:00:44 +05:30
|
|
|
cmdedit_y -= lines_up;
|
|
|
|
/* go to 1st column; go up */
|
2010-10-29 01:04:56 +05:30
|
|
|
printf("\r" ESC"[%uA", lines_up);
|
2010-05-17 16:00:44 +05:30
|
|
|
/* go to correct column.
|
2010-05-17 16:03:13 +05:30
|
|
|
* xterm, konsole, Linux VT interpret 0 as 1 below! wow.
|
|
|
|
* need to *make sure* we skip it if cmdedit_x == 0 */
|
2010-05-17 16:00:44 +05:30
|
|
|
if (cmdedit_x)
|
2010-10-29 01:04:56 +05:30
|
|
|
printf(ESC"[%uC", cmdedit_x);
|
2008-05-26 03:22:03 +05:30
|
|
|
}
|
2001-02-17 00:06:04 +05:30
|
|
|
}
|
2000-03-16 13:39:57 +05:30
|
|
|
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
/* See redraw and draw_full below */
|
|
|
|
static void draw_custom(int y, int back_cursor, bool is_full)
|
2001-02-17 00:06:04 +05:30
|
|
|
{
|
2010-05-17 07:35:53 +05:30
|
|
|
if (y > 0) /* up y lines */
|
2010-10-29 01:04:56 +05:30
|
|
|
printf(ESC"[%uA", y);
|
2007-09-27 15:50:47 +05:30
|
|
|
bb_putchar('\r');
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
put_prompt_custom(is_full);
|
2010-05-11 18:19:13 +05:30
|
|
|
put_till_end_and_adv_cursor();
|
|
|
|
printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
|
2001-02-17 00:06:04 +05:30
|
|
|
input_backward(back_cursor);
|
2000-03-16 13:39:57 +05:30
|
|
|
}
|
|
|
|
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
/* Move y lines up, draw last/sole prompt line, editor line[s], and clear tail.
|
|
|
|
* goal: redraw the prompt+input+cursor in-place, overwriting the previous */
|
|
|
|
#define redraw(y, back_cursor) draw_custom((y), (back_cursor), 0)
|
|
|
|
|
|
|
|
/* Like above, but without moving up, and while using all the prompt lines.
|
|
|
|
* goal: draw a full prompt+input+cursor unrelated to a previous position.
|
|
|
|
* note: cmdedit_y always ends up relating to the last/sole prompt line */
|
|
|
|
#define draw_full(back_cursor) draw_custom(0, (back_cursor), 1)
|
|
|
|
|
2005-08-05 00:34:46 +05:30
|
|
|
/* Delete the char in front of the cursor, optionally saving it
|
|
|
|
* for later putback */
|
2008-03-17 14:34:04 +05:30
|
|
|
#if !ENABLE_FEATURE_EDITING_VI
|
|
|
|
static void input_delete(void)
|
|
|
|
#define input_delete(save) input_delete()
|
|
|
|
#else
|
2005-08-05 00:34:46 +05:30
|
|
|
static void input_delete(int save)
|
2008-03-17 14:34:04 +05:30
|
|
|
#endif
|
2000-04-12 23:19:52 +05:30
|
|
|
{
|
2001-01-27 02:12:23 +05:30
|
|
|
int j = cursor;
|
2000-04-09 23:57:46 +05:30
|
|
|
|
2008-05-13 07:57:31 +05:30
|
|
|
if (j == (int)command_len)
|
2000-04-12 23:19:52 +05:30
|
|
|
return;
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2007-01-22 14:33:07 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_VI
|
2005-08-05 00:34:46 +05:30
|
|
|
if (save) {
|
|
|
|
if (newdelflag) {
|
2007-11-10 07:05:47 +05:30
|
|
|
delptr = delbuf;
|
2005-08-05 00:34:46 +05:30
|
|
|
newdelflag = 0;
|
|
|
|
}
|
2007-11-10 07:05:47 +05:30
|
|
|
if ((delptr - delbuf) < DELBUFSIZ)
|
|
|
|
*delptr++ = command_ps[j];
|
2005-08-05 00:34:46 +05:30
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-07-10 22:10:49 +05:30
|
|
|
memmove(command_ps + j, command_ps + j + 1,
|
2009-07-16 18:03:16 +05:30
|
|
|
/* (command_len + 1 [because of NUL]) - (j + 1)
|
|
|
|
* simplified into (command_len - j) */
|
|
|
|
(command_len - j) * sizeof(command_ps[0]));
|
2007-01-22 12:51:38 +05:30
|
|
|
command_len--;
|
2010-05-11 18:19:13 +05:30
|
|
|
put_till_end_and_adv_cursor();
|
|
|
|
/* Last char is still visible, erase it (and more) */
|
|
|
|
printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
|
2003-07-28 15:26:35 +05:30
|
|
|
input_backward(cursor - j); /* back to old pos cursor */
|
2001-01-27 02:12:23 +05:30
|
|
|
}
|
|
|
|
|
2007-01-22 14:33:07 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_VI
|
2005-08-05 00:34:46 +05:30
|
|
|
static void put(void)
|
|
|
|
{
|
2007-01-22 00:48:19 +05:30
|
|
|
int ocursor;
|
2007-11-10 07:05:47 +05:30
|
|
|
int j = delptr - delbuf;
|
2007-01-22 00:48:19 +05:30
|
|
|
|
2005-08-05 00:34:46 +05:30
|
|
|
if (j == 0)
|
|
|
|
return;
|
|
|
|
ocursor = cursor;
|
|
|
|
/* open hole and then fill it */
|
2009-07-10 22:10:49 +05:30
|
|
|
memmove(command_ps + cursor + j, command_ps + cursor,
|
|
|
|
(command_len - cursor + 1) * sizeof(command_ps[0]));
|
|
|
|
memcpy(command_ps + cursor, delbuf, j * sizeof(command_ps[0]));
|
2007-01-22 14:04:44 +05:30
|
|
|
command_len += j;
|
2010-05-11 18:19:13 +05:30
|
|
|
put_till_end_and_adv_cursor();
|
2006-12-22 03:57:10 +05:30
|
|
|
input_backward(cursor - ocursor - j + 1); /* at end of new text */
|
2005-08-05 00:34:46 +05:30
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2001-01-27 02:12:23 +05:30
|
|
|
/* Delete the char in back of the cursor */
|
|
|
|
static void input_backspace(void)
|
|
|
|
{
|
|
|
|
if (cursor > 0) {
|
2001-02-17 00:06:04 +05:30
|
|
|
input_backward(1);
|
2005-08-05 00:34:46 +05:30
|
|
|
input_delete(0);
|
2000-04-12 23:19:52 +05:30
|
|
|
}
|
2001-01-27 02:12:23 +05:30
|
|
|
}
|
2000-04-12 23:19:52 +05:30
|
|
|
|
2004-04-14 23:21:38 +05:30
|
|
|
/* Move forward one character */
|
2001-01-27 02:12:23 +05:30
|
|
|
static void input_forward(void)
|
|
|
|
{
|
2007-01-22 12:51:38 +05:30
|
|
|
if (cursor < command_len)
|
2010-05-11 18:19:13 +05:30
|
|
|
put_cur_glyph_and_inc_cursor();
|
2000-04-12 23:19:52 +05:30
|
|
|
}
|
|
|
|
|
2007-01-22 14:33:07 +05:30
|
|
|
#if ENABLE_FEATURE_TAB_COMPLETION
|
2001-01-27 02:12:23 +05:30
|
|
|
|
2010-11-22 08:19:18 +05:30
|
|
|
//FIXME:
|
|
|
|
//needs to be more clever: currently it thinks that "foo\ b<TAB>
|
|
|
|
//matches the file named "foo bar", which is untrue.
|
|
|
|
//Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>,
|
|
|
|
//not "foo bar <cursor>...
|
|
|
|
|
2007-01-22 00:49:46 +05:30
|
|
|
static void free_tab_completion_data(void)
|
|
|
|
{
|
|
|
|
if (matches) {
|
|
|
|
while (num_matches)
|
|
|
|
free(matches[--num_matches]);
|
|
|
|
free(matches);
|
|
|
|
matches = NULL;
|
|
|
|
}
|
|
|
|
}
|
2006-01-25 17:23:47 +05:30
|
|
|
|
2006-12-22 03:54:46 +05:30
|
|
|
static void add_match(char *matched)
|
2006-01-25 17:23:47 +05:30
|
|
|
{
|
2017-11-07 22:39:29 +05:30
|
|
|
unsigned char *p = (unsigned char*)matched;
|
|
|
|
while (*p) {
|
|
|
|
/* ESC attack fix: drop any string with control chars */
|
|
|
|
if (*p < ' '
|
|
|
|
|| (!ENABLE_UNICODE_SUPPORT && *p >= 0x7f)
|
|
|
|
|| (ENABLE_UNICODE_SUPPORT && *p == 0x7f)
|
|
|
|
) {
|
|
|
|
free(matched);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
2008-07-08 10:44:36 +05:30
|
|
|
matches = xrealloc_vector(matches, 4, num_matches);
|
|
|
|
matches[num_matches] = matched;
|
2006-01-25 17:23:47 +05:30
|
|
|
num_matches++;
|
|
|
|
}
|
|
|
|
|
2010-11-22 08:19:18 +05:30
|
|
|
# if ENABLE_FEATURE_USERNAME_COMPLETION
|
2010-09-02 15:31:11 +05:30
|
|
|
/* Replace "~user/..." with "/homedir/...".
|
|
|
|
* The parameter is malloced, free it or return it
|
|
|
|
* unchanged if no user is matched.
|
|
|
|
*/
|
|
|
|
static char *username_path_completion(char *ud)
|
2000-03-17 06:42:41 +05:30
|
|
|
{
|
2001-02-17 00:06:04 +05:30
|
|
|
struct passwd *entry;
|
2010-09-02 15:31:11 +05:30
|
|
|
char *tilde_name = ud;
|
2021-10-12 02:38:31 +05:30
|
|
|
const char *home = NULL;
|
2010-09-02 15:31:11 +05:30
|
|
|
|
|
|
|
ud++; /* skip ~ */
|
|
|
|
if (*ud == '/') { /* "~/..." */
|
2021-10-12 02:38:31 +05:30
|
|
|
home = get_homedir_or_NULL();
|
2010-09-02 15:31:11 +05:30
|
|
|
} else {
|
|
|
|
/* "~user/..." */
|
|
|
|
ud = strchr(ud, '/');
|
|
|
|
*ud = '\0'; /* "~user" */
|
|
|
|
entry = getpwnam(tilde_name + 1);
|
|
|
|
*ud = '/'; /* restore "~user/..." */
|
|
|
|
if (entry)
|
|
|
|
home = entry->pw_dir;
|
|
|
|
}
|
|
|
|
if (home) {
|
|
|
|
ud = concat_path_file(home, ud);
|
|
|
|
free(tilde_name);
|
|
|
|
tilde_name = ud;
|
|
|
|
}
|
|
|
|
return tilde_name;
|
|
|
|
}
|
|
|
|
|
2010-09-03 16:26:36 +05:30
|
|
|
/* ~use<tab> - find all users with this prefix.
|
|
|
|
* Return the length of the prefix used for matching.
|
|
|
|
*/
|
|
|
|
static NOINLINE unsigned complete_username(const char *ud)
|
2010-09-02 15:31:11 +05:30
|
|
|
{
|
2015-02-08 01:51:02 +05:30
|
|
|
struct passwd *pw;
|
2010-09-03 16:26:36 +05:30
|
|
|
unsigned userlen;
|
2001-01-27 02:12:23 +05:30
|
|
|
|
2010-09-02 15:31:11 +05:30
|
|
|
ud++; /* skip ~ */
|
2001-02-17 00:06:04 +05:30
|
|
|
userlen = strlen(ud);
|
2001-01-27 02:12:23 +05:30
|
|
|
|
2010-09-02 15:31:11 +05:30
|
|
|
setpwent();
|
2015-02-08 01:51:02 +05:30
|
|
|
while ((pw = getpwent()) != NULL) {
|
2010-09-02 15:31:11 +05:30
|
|
|
/* Null usernames should result in all users as possible completions. */
|
2015-03-12 22:18:34 +05:30
|
|
|
if (/* !ud[0] || */ is_prefixed_with(pw->pw_name, ud)) {
|
2015-02-08 01:51:02 +05:30
|
|
|
add_match(xasprintf("~%s/", pw->pw_name));
|
2001-02-17 00:06:04 +05:30
|
|
|
}
|
|
|
|
}
|
2015-02-08 01:51:02 +05:30
|
|
|
endpwent(); /* don't keep password file open */
|
2010-09-03 16:26:36 +05:30
|
|
|
|
|
|
|
return 1 + userlen;
|
2000-03-17 06:42:41 +05:30
|
|
|
}
|
2010-11-22 08:19:18 +05:30
|
|
|
# endif /* FEATURE_USERNAME_COMPLETION */
|
2001-01-27 02:12:23 +05:30
|
|
|
|
|
|
|
enum {
|
2001-02-17 00:06:04 +05:30
|
|
|
FIND_EXE_ONLY = 0,
|
|
|
|
FIND_DIR_ONLY = 1,
|
2001-01-27 02:12:23 +05:30
|
|
|
FIND_FILE_ONLY = 2,
|
|
|
|
};
|
2000-03-19 16:16:06 +05:30
|
|
|
|
2020-12-16 15:29:20 +05:30
|
|
|
static unsigned path_parse(char ***p)
|
2001-01-27 02:12:23 +05:30
|
|
|
{
|
2020-12-16 15:29:20 +05:30
|
|
|
unsigned npth;
|
2007-01-22 12:51:38 +05:30
|
|
|
const char *pth;
|
2007-01-22 14:04:44 +05:30
|
|
|
char *tmp;
|
2007-01-22 12:51:38 +05:30
|
|
|
char **res;
|
2001-01-27 02:12:23 +05:30
|
|
|
|
2020-12-16 15:44:08 +05:30
|
|
|
# if EDITING_HAS_path_lookup
|
2007-01-22 12:51:38 +05:30
|
|
|
if (state->flags & WITH_PATH_LOOKUP)
|
|
|
|
pth = state->path_lookup;
|
|
|
|
else
|
2020-12-16 15:44:08 +05:30
|
|
|
# endif
|
2007-01-22 12:51:38 +05:30
|
|
|
pth = getenv("PATH");
|
2010-09-02 15:31:11 +05:30
|
|
|
|
|
|
|
/* PATH="" or PATH=":"? */
|
2006-12-22 03:57:10 +05:30
|
|
|
if (!pth || !pth[0] || LONE_CHAR(pth, ':'))
|
2001-01-27 02:12:23 +05:30
|
|
|
return 1;
|
|
|
|
|
2007-01-22 14:04:44 +05:30
|
|
|
tmp = (char*)pth;
|
2020-12-16 15:29:20 +05:30
|
|
|
npth = 1; /* path component count */
|
2006-12-22 03:57:10 +05:30
|
|
|
while (1) {
|
2001-01-27 02:12:23 +05:30
|
|
|
tmp = strchr(tmp, ':');
|
2006-12-22 03:57:10 +05:30
|
|
|
if (!tmp)
|
2001-01-27 02:12:23 +05:30
|
|
|
break;
|
2010-09-02 15:31:11 +05:30
|
|
|
tmp++;
|
2007-01-22 12:51:38 +05:30
|
|
|
npth++;
|
2001-01-27 02:12:23 +05:30
|
|
|
}
|
|
|
|
|
2020-12-16 15:29:20 +05:30
|
|
|
*p = res = xzalloc((npth + 1) * sizeof(res[0]));
|
2007-01-22 14:04:44 +05:30
|
|
|
res[0] = tmp = xstrdup(pth);
|
2007-01-22 12:51:38 +05:30
|
|
|
npth = 1;
|
2006-12-22 03:57:10 +05:30
|
|
|
while (1) {
|
2001-01-27 02:12:23 +05:30
|
|
|
tmp = strchr(tmp, ':');
|
2006-12-22 03:57:10 +05:30
|
|
|
if (!tmp)
|
2001-01-27 02:12:23 +05:30
|
|
|
break;
|
2007-01-22 12:51:38 +05:30
|
|
|
*tmp++ = '\0'; /* ':' -> '\0' */
|
|
|
|
res[npth++] = tmp;
|
2001-01-27 02:12:23 +05:30
|
|
|
}
|
2020-12-16 15:29:20 +05:30
|
|
|
/* special case: "match subdirectories of the current directory" */
|
|
|
|
/*res[npth++] = NULL; - filled by xzalloc() */
|
2001-01-27 02:12:23 +05:30
|
|
|
return npth;
|
|
|
|
}
|
|
|
|
|
2010-09-03 16:26:36 +05:30
|
|
|
/* Complete command, directory or file name.
|
|
|
|
* Return the length of the prefix used for matching.
|
|
|
|
*/
|
|
|
|
static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
|
2001-02-17 00:06:04 +05:30
|
|
|
{
|
|
|
|
char *path1[1];
|
|
|
|
char **paths = path1;
|
2020-12-16 15:29:20 +05:30
|
|
|
unsigned npaths;
|
|
|
|
unsigned i;
|
|
|
|
unsigned baselen;
|
|
|
|
const char *basecmd;
|
2010-09-02 15:31:11 +05:30
|
|
|
char *dirbuf = NULL;
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2007-01-22 00:48:19 +05:30
|
|
|
npaths = 1;
|
2007-01-30 04:21:58 +05:30
|
|
|
path1[0] = (char*)".";
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2020-12-16 15:29:20 +05:30
|
|
|
basecmd = strrchr(command, '/');
|
|
|
|
if (!basecmd) {
|
2010-09-02 15:31:11 +05:30
|
|
|
if (type == FIND_EXE_ONLY)
|
|
|
|
npaths = path_parse(&paths);
|
2020-12-16 15:29:20 +05:30
|
|
|
basecmd = command;
|
2001-01-27 02:12:23 +05:30
|
|
|
} else {
|
2010-09-02 15:31:11 +05:30
|
|
|
/* point to 'l' in "..../last_component" */
|
2020-12-16 15:29:20 +05:30
|
|
|
basecmd++;
|
2007-01-22 00:48:19 +05:30
|
|
|
/* dirbuf = ".../.../.../" */
|
2020-12-16 15:29:20 +05:30
|
|
|
dirbuf = xstrndup(command, basecmd - command);
|
2010-11-22 08:19:18 +05:30
|
|
|
# if ENABLE_FEATURE_USERNAME_COMPLETION
|
2003-07-28 15:26:35 +05:30
|
|
|
if (dirbuf[0] == '~') /* ~/... or ~user/... */
|
2010-09-02 15:31:11 +05:30
|
|
|
dirbuf = username_path_completion(dirbuf);
|
2010-11-22 08:19:18 +05:30
|
|
|
# endif
|
2010-09-02 16:14:39 +05:30
|
|
|
path1[0] = dirbuf;
|
2001-01-27 02:12:23 +05:30
|
|
|
}
|
2020-12-16 15:29:20 +05:30
|
|
|
baselen = strlen(basecmd);
|
2000-03-19 16:16:06 +05:30
|
|
|
|
2016-10-24 04:55:05 +05:30
|
|
|
if (type == FIND_EXE_ONLY && !dirbuf) {
|
2020-01-21 21:31:58 +05:30
|
|
|
# if ENABLE_FEATURE_SH_STANDALONE && NUM_APPLETS != 1
|
2018-11-17 23:18:14 +05:30
|
|
|
const char *p = applet_names;
|
2016-04-08 16:27:20 +05:30
|
|
|
while (*p) {
|
2020-12-16 15:29:20 +05:30
|
|
|
if (strncmp(basecmd, p, baselen) == 0)
|
2015-05-29 16:01:40 +05:30
|
|
|
add_match(xstrdup(p));
|
2018-11-17 23:18:14 +05:30
|
|
|
while (*p++ != '\0')
|
2016-04-08 16:27:20 +05:30
|
|
|
continue;
|
2015-05-29 16:01:40 +05:30
|
|
|
}
|
2017-07-29 04:29:24 +05:30
|
|
|
# endif
|
2022-03-24 17:47:25 +05:30
|
|
|
# if ENABLE_SHELL_ASH || ENABLE_SHELL_HUSH
|
2020-01-21 21:31:58 +05:30
|
|
|
if (state->get_exe_name) {
|
|
|
|
i = 0;
|
|
|
|
for (;;) {
|
|
|
|
const char *b = state->get_exe_name(i++);
|
|
|
|
if (!b)
|
|
|
|
break;
|
2020-12-16 15:29:20 +05:30
|
|
|
if (strncmp(basecmd, b, baselen) == 0)
|
2020-01-21 21:31:58 +05:30
|
|
|
add_match(xstrdup(b));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
# endif
|
|
|
|
}
|
2015-05-29 16:01:40 +05:30
|
|
|
|
2001-02-17 00:06:04 +05:30
|
|
|
for (i = 0; i < npaths; i++) {
|
2010-09-02 15:31:11 +05:30
|
|
|
DIR *dir;
|
|
|
|
struct dirent *next;
|
|
|
|
struct stat st;
|
|
|
|
char *found;
|
2021-02-18 15:20:29 +05:30
|
|
|
const char *lpath;
|
2010-09-02 15:31:11 +05:30
|
|
|
|
2020-12-16 15:29:20 +05:30
|
|
|
if (paths[i] == NULL) { /* path_parse()'s last component? */
|
|
|
|
/* in PATH completion, current dir's subdir names
|
|
|
|
* can be completions (but only subdirs, not files).
|
|
|
|
*/
|
2020-12-11 18:04:21 +05:30
|
|
|
type = FIND_DIR_ONLY;
|
|
|
|
paths[i] = (char *)".";
|
|
|
|
}
|
|
|
|
|
2021-02-18 15:20:29 +05:30
|
|
|
lpath = *paths[i] ? paths[i] : ".";
|
|
|
|
dir = opendir(lpath);
|
2008-04-24 10:12:52 +05:30
|
|
|
if (!dir)
|
|
|
|
continue; /* don't print an error */
|
2001-02-17 00:06:04 +05:30
|
|
|
|
|
|
|
while ((next = readdir(dir)) != NULL) {
|
2010-09-03 17:41:08 +05:30
|
|
|
unsigned len;
|
2010-09-02 15:31:11 +05:30
|
|
|
const char *name_found = next->d_name;
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2010-09-02 15:31:11 +05:30
|
|
|
/* .../<tab>: bash 3.2.0 shows dotfiles, but not . and .. */
|
2020-12-16 15:29:20 +05:30
|
|
|
if (!basecmd[0] && DOT_OR_DOTDOT(name_found))
|
2001-01-27 02:12:23 +05:30
|
|
|
continue;
|
2010-09-02 15:31:11 +05:30
|
|
|
/* match? */
|
2020-12-16 15:29:20 +05:30
|
|
|
if (strncmp(basecmd, name_found, baselen) != 0)
|
2010-09-02 15:31:11 +05:30
|
|
|
continue; /* no */
|
|
|
|
|
2021-02-18 15:20:29 +05:30
|
|
|
found = concat_path_file(lpath, name_found);
|
2008-04-24 10:12:52 +05:30
|
|
|
/* NB: stat() first so that we see is it a directory;
|
|
|
|
* but if that fails, use lstat() so that
|
|
|
|
* we still match dangling links */
|
|
|
|
if (stat(found, &st) && lstat(found, &st))
|
2010-09-02 15:31:11 +05:30
|
|
|
goto cont; /* hmm, remove in progress? */
|
2006-12-22 03:54:46 +05:30
|
|
|
|
2010-09-03 17:41:08 +05:30
|
|
|
/* Save only name */
|
|
|
|
len = strlen(name_found);
|
|
|
|
found = xrealloc(found, len + 2); /* +2: for slash and NUL */
|
|
|
|
strcpy(found, name_found);
|
2006-12-22 03:54:46 +05:30
|
|
|
|
2001-01-27 02:12:23 +05:30
|
|
|
if (S_ISDIR(st.st_mode)) {
|
2020-12-10 20:14:57 +05:30
|
|
|
/* skip directories if searching PATH */
|
|
|
|
if (type == FIND_EXE_ONLY && !dirbuf)
|
|
|
|
goto cont;
|
2010-09-03 17:41:08 +05:30
|
|
|
/* name is a directory, add slash */
|
|
|
|
found[len] = '/';
|
|
|
|
found[len + 1] = '\0';
|
2001-01-27 02:12:23 +05:30
|
|
|
} else {
|
2010-09-02 15:31:11 +05:30
|
|
|
/* skip files if looking for dirs only (example: cd) */
|
2003-07-28 15:26:35 +05:30
|
|
|
if (type == FIND_DIR_ONLY)
|
2001-04-10 04:18:12 +05:30
|
|
|
goto cont;
|
2001-02-17 00:06:04 +05:30
|
|
|
}
|
2010-09-02 15:31:11 +05:30
|
|
|
/* add it to the list */
|
2006-12-22 03:54:46 +05:30
|
|
|
add_match(found);
|
2006-01-25 17:23:47 +05:30
|
|
|
continue;
|
2006-12-22 03:57:10 +05:30
|
|
|
cont:
|
2001-04-10 04:18:12 +05:30
|
|
|
free(found);
|
2000-03-19 16:16:06 +05:30
|
|
|
}
|
2001-02-17 00:06:04 +05:30
|
|
|
closedir(dir);
|
2010-09-02 15:31:11 +05:30
|
|
|
} /* for every path */
|
|
|
|
|
2001-02-17 00:06:04 +05:30
|
|
|
if (paths != path1) {
|
2008-04-24 10:12:52 +05:30
|
|
|
free(paths[0]); /* allocated memory is only in first member */
|
2001-02-17 00:06:04 +05:30
|
|
|
free(paths);
|
2000-03-19 16:16:06 +05:30
|
|
|
}
|
2010-09-03 17:41:08 +05:30
|
|
|
free(dirbuf);
|
2010-09-03 16:26:36 +05:30
|
|
|
|
2020-12-16 15:29:20 +05:30
|
|
|
return baselen;
|
2001-02-17 00:06:04 +05:30
|
|
|
}
|
2001-01-27 02:12:23 +05:30
|
|
|
|
2010-09-03 16:21:36 +05:30
|
|
|
/* build_match_prefix:
|
2010-09-03 17:39:24 +05:30
|
|
|
* On entry, match_buf contains everything up to cursor at the moment <tab>
|
2010-09-03 16:21:36 +05:30
|
|
|
* was pressed. This function looks at it, figures out what part of it
|
|
|
|
* constitutes the command/file/directory prefix to use for completion,
|
2010-09-03 17:39:24 +05:30
|
|
|
* and rewrites match_buf to contain only that part.
|
2010-09-03 16:21:36 +05:30
|
|
|
*/
|
2010-09-03 17:39:24 +05:30
|
|
|
#define dbg_bmp 0
|
2010-09-03 16:21:36 +05:30
|
|
|
/* Helpers: */
|
2009-07-16 05:49:39 +05:30
|
|
|
/* QUOT is used on elements of int_buf[], which are bytes,
|
|
|
|
* not Unicode chars. Therefore it works correctly even in Unicode mode.
|
|
|
|
*/
|
2006-12-22 03:57:10 +05:30
|
|
|
#define QUOT (UCHAR_MAX+1)
|
2010-09-03 16:32:47 +05:30
|
|
|
static void remove_chunk(int16_t *int_buf, int beg, int end)
|
2009-07-16 05:49:39 +05:30
|
|
|
{
|
2010-09-03 16:21:36 +05:30
|
|
|
/* beg must be <= end */
|
2010-09-03 16:23:15 +05:30
|
|
|
if (beg == end)
|
|
|
|
return;
|
2010-09-03 16:29:15 +05:30
|
|
|
|
|
|
|
while ((int_buf[beg] = int_buf[end]) != 0)
|
|
|
|
beg++, end++;
|
|
|
|
|
2010-09-03 16:23:15 +05:30
|
|
|
if (dbg_bmp) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; int_buf[i]; i++)
|
|
|
|
bb_putchar((unsigned char)int_buf[i]);
|
|
|
|
bb_putchar('\n');
|
|
|
|
}
|
2009-07-16 05:49:39 +05:30
|
|
|
}
|
2010-09-03 17:39:24 +05:30
|
|
|
/* Caller ensures that match_buf points to a malloced buffer
|
|
|
|
* big enough to hold strlen(match_buf)*2 + 2
|
|
|
|
*/
|
|
|
|
static NOINLINE int build_match_prefix(char *match_buf)
|
2001-02-17 00:06:04 +05:30
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
int command_mode;
|
2010-09-03 17:39:24 +05:30
|
|
|
int16_t *int_buf = (int16_t*)match_buf;
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2010-09-03 17:39:24 +05:30
|
|
|
if (dbg_bmp) printf("\n%s\n", match_buf);
|
2010-09-03 16:23:15 +05:30
|
|
|
|
2010-09-03 17:39:24 +05:30
|
|
|
/* Copy in reverse order, since they overlap */
|
|
|
|
i = strlen(match_buf);
|
|
|
|
do {
|
|
|
|
int_buf[i] = (unsigned char)match_buf[i];
|
|
|
|
i--;
|
|
|
|
} while (i >= 0);
|
2000-04-12 23:19:52 +05:30
|
|
|
|
2010-09-03 16:21:36 +05:30
|
|
|
/* Mark every \c as "quoted c" */
|
2010-09-03 17:39:24 +05:30
|
|
|
for (i = 0; int_buf[i]; i++) {
|
|
|
|
if (int_buf[i] == '\\') {
|
|
|
|
remove_chunk(int_buf, i, i + 1);
|
|
|
|
int_buf[i] |= QUOT;
|
2000-04-12 23:19:52 +05:30
|
|
|
}
|
2010-05-17 00:16:53 +05:30
|
|
|
}
|
2010-09-03 16:29:15 +05:30
|
|
|
/* Quote-mark "chars" and 'chars', drop delimiters */
|
2010-09-03 16:23:15 +05:30
|
|
|
{
|
|
|
|
int in_quote = 0;
|
2010-09-03 16:29:15 +05:30
|
|
|
i = 0;
|
|
|
|
while (int_buf[i]) {
|
2010-09-03 16:23:15 +05:30
|
|
|
int cur = int_buf[i];
|
2010-09-03 16:29:15 +05:30
|
|
|
if (!cur)
|
|
|
|
break;
|
2010-09-03 16:23:15 +05:30
|
|
|
if (cur == '\'' || cur == '"') {
|
2010-09-03 16:29:15 +05:30
|
|
|
if (!in_quote || (cur == in_quote)) {
|
|
|
|
in_quote ^= cur;
|
2010-09-03 16:32:47 +05:30
|
|
|
remove_chunk(int_buf, i, i + 1);
|
2010-09-03 16:29:15 +05:30
|
|
|
continue;
|
|
|
|
}
|
2001-02-17 00:06:04 +05:30
|
|
|
}
|
2010-09-03 16:29:15 +05:30
|
|
|
if (in_quote)
|
|
|
|
int_buf[i] = cur | QUOT;
|
|
|
|
i++;
|
2010-09-03 16:23:15 +05:30
|
|
|
}
|
2001-02-17 00:06:04 +05:30
|
|
|
}
|
2001-01-27 02:12:23 +05:30
|
|
|
|
2010-09-03 16:21:36 +05:30
|
|
|
/* Remove everything up to command delimiters:
|
|
|
|
* ';' ';;' '&' '|' '&&' '||',
|
|
|
|
* but careful with '>&' '<&' '>|'
|
|
|
|
*/
|
2001-02-17 00:06:04 +05:30
|
|
|
for (i = 0; int_buf[i]; i++) {
|
2010-09-03 16:23:15 +05:30
|
|
|
int cur = int_buf[i];
|
|
|
|
if (cur == ';' || cur == '&' || cur == '|') {
|
|
|
|
int prev = i ? int_buf[i - 1] : 0;
|
|
|
|
if (cur == '&' && (prev == '>' || prev == '<')) {
|
|
|
|
continue;
|
|
|
|
} else if (cur == '|' && prev == '>') {
|
|
|
|
continue;
|
|
|
|
}
|
2010-09-03 16:32:47 +05:30
|
|
|
remove_chunk(int_buf, 0, i + 1 + (cur == int_buf[i + 1]));
|
2010-09-03 16:23:15 +05:30
|
|
|
i = -1; /* back to square 1 */
|
2001-02-17 00:06:04 +05:30
|
|
|
}
|
|
|
|
}
|
2010-09-03 16:21:36 +05:30
|
|
|
/* Remove all `cmd` */
|
2009-07-16 05:49:39 +05:30
|
|
|
for (i = 0; int_buf[i]; i++) {
|
2001-02-17 00:06:04 +05:30
|
|
|
if (int_buf[i] == '`') {
|
2010-09-03 16:21:36 +05:30
|
|
|
for (j = i + 1; int_buf[j]; j++) {
|
2001-02-17 00:06:04 +05:30
|
|
|
if (int_buf[j] == '`') {
|
2010-09-03 16:29:15 +05:30
|
|
|
/* `cmd` should count as a word:
|
|
|
|
* `cmd` c<tab> should search for files c*,
|
|
|
|
* not commands c*. Therefore we don't drop
|
|
|
|
* `cmd` entirely, we replace it with single `.
|
|
|
|
*/
|
2010-09-03 16:32:47 +05:30
|
|
|
remove_chunk(int_buf, i, j);
|
2010-09-03 16:23:15 +05:30
|
|
|
goto next;
|
2001-02-17 00:06:04 +05:30
|
|
|
}
|
2010-09-03 16:21:36 +05:30
|
|
|
}
|
2010-09-03 16:23:15 +05:30
|
|
|
/* No closing ` - command mode, remove all up to ` */
|
2010-09-03 16:32:47 +05:30
|
|
|
remove_chunk(int_buf, 0, i + 1);
|
2010-09-03 16:23:15 +05:30
|
|
|
break;
|
2010-09-03 16:29:15 +05:30
|
|
|
next: ;
|
2001-02-17 00:06:04 +05:30
|
|
|
}
|
2009-07-16 05:49:39 +05:30
|
|
|
}
|
2001-01-27 02:12:23 +05:30
|
|
|
|
2010-09-03 16:29:15 +05:30
|
|
|
/* Remove "cmd (" and "cmd {"
|
|
|
|
* Example: "if { c<tab>"
|
|
|
|
* In this example, c should be matched as command pfx.
|
|
|
|
*/
|
|
|
|
for (i = 0; int_buf[i]; i++) {
|
|
|
|
if (int_buf[i] == '(' || int_buf[i] == '{') {
|
2010-09-03 16:32:47 +05:30
|
|
|
remove_chunk(int_buf, 0, i + 1);
|
2010-09-03 17:38:24 +05:30
|
|
|
i = -1; /* back to square 1 */
|
2001-01-27 02:12:23 +05:30
|
|
|
}
|
2009-07-16 05:49:39 +05:30
|
|
|
}
|
2000-04-12 23:19:52 +05:30
|
|
|
|
2010-09-03 16:21:36 +05:30
|
|
|
/* Remove leading unquoted spaces */
|
2001-02-17 00:06:04 +05:30
|
|
|
for (i = 0; int_buf[i]; i++)
|
|
|
|
if (int_buf[i] != ' ')
|
|
|
|
break;
|
2010-09-03 16:32:47 +05:30
|
|
|
remove_chunk(int_buf, 0, i);
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2010-09-03 16:21:36 +05:30
|
|
|
/* Determine completion mode */
|
2001-02-17 00:06:04 +05:30
|
|
|
command_mode = FIND_EXE_ONLY;
|
2009-07-16 05:49:39 +05:30
|
|
|
for (i = 0; int_buf[i]; i++) {
|
2001-02-17 00:06:04 +05:30
|
|
|
if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
|
2010-09-03 16:21:36 +05:30
|
|
|
if (int_buf[i] == ' '
|
|
|
|
&& command_mode == FIND_EXE_ONLY
|
2010-09-03 16:29:15 +05:30
|
|
|
&& (char)int_buf[0] == 'c'
|
|
|
|
&& (char)int_buf[1] == 'd'
|
|
|
|
&& i == 2 /* -> int_buf[2] == ' ' */
|
2006-12-22 03:57:10 +05:30
|
|
|
) {
|
2001-02-17 00:06:04 +05:30
|
|
|
command_mode = FIND_DIR_ONLY;
|
2006-12-22 03:57:10 +05:30
|
|
|
} else {
|
2001-02-17 00:06:04 +05:30
|
|
|
command_mode = FIND_FILE_ONLY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-07-16 05:49:39 +05:30
|
|
|
}
|
2010-09-03 16:23:15 +05:30
|
|
|
if (dbg_bmp) printf("command_mode(0:exe/1:dir/2:file):%d\n", command_mode);
|
2010-09-03 16:21:36 +05:30
|
|
|
|
|
|
|
/* Remove everything except last word */
|
2010-09-02 15:31:11 +05:30
|
|
|
for (i = 0; int_buf[i]; i++) /* quasi-strlen(int_buf) */
|
|
|
|
continue;
|
2001-02-17 00:06:04 +05:30
|
|
|
for (--i; i >= 0; i--) {
|
2010-09-03 16:23:15 +05:30
|
|
|
int cur = int_buf[i];
|
2021-04-09 20:32:00 +05:30
|
|
|
if (cur == ' ' || cur == '<' || cur == '>' || cur == '|' || cur == '&' || cur == '=') {
|
2010-09-03 16:32:47 +05:30
|
|
|
remove_chunk(int_buf, 0, i + 1);
|
2001-02-17 00:06:04 +05:30
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-03 17:39:24 +05:30
|
|
|
/* Convert back to string of _chars_ */
|
2010-09-03 16:29:15 +05:30
|
|
|
i = 0;
|
2010-09-03 17:39:24 +05:30
|
|
|
while ((match_buf[i] = int_buf[i]) != '\0')
|
2010-09-03 16:29:15 +05:30
|
|
|
i++;
|
2010-09-03 16:32:47 +05:30
|
|
|
|
2010-09-03 17:39:24 +05:30
|
|
|
if (dbg_bmp) printf("final match_buf:'%s'\n", match_buf);
|
2001-02-17 00:06:04 +05:30
|
|
|
|
|
|
|
return command_mode;
|
2009-07-16 17:44:34 +05:30
|
|
|
}
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2003-01-06 06:41:50 +05:30
|
|
|
/*
|
2010-09-03 16:21:36 +05:30
|
|
|
* Display by column (original idea from ls applet,
|
|
|
|
* very optimized by me [Vladimir] :)
|
2007-01-22 00:49:46 +05:30
|
|
|
*/
|
2006-01-25 17:23:47 +05:30
|
|
|
static void showfiles(void)
|
2003-01-06 06:41:50 +05:30
|
|
|
{
|
|
|
|
int ncols, row;
|
|
|
|
int column_width = 0;
|
2006-01-25 17:23:47 +05:30
|
|
|
int nfiles = num_matches;
|
2003-01-06 06:41:50 +05:30
|
|
|
int nrows = nfiles;
|
2006-01-25 17:23:47 +05:30
|
|
|
int l;
|
2003-01-06 06:41:50 +05:30
|
|
|
|
2009-07-16 05:49:39 +05:30
|
|
|
/* find the longest file name - use that as the column width */
|
2003-01-06 06:41:50 +05:30
|
|
|
for (row = 0; row < nrows; row++) {
|
2010-06-01 12:03:18 +05:30
|
|
|
l = unicode_strwidth(matches[row]);
|
2003-01-06 06:41:50 +05:30
|
|
|
if (column_width < l)
|
|
|
|
column_width = l;
|
|
|
|
}
|
|
|
|
column_width += 2; /* min space for columns */
|
|
|
|
ncols = cmdedit_termw / column_width;
|
|
|
|
|
|
|
|
if (ncols > 1) {
|
|
|
|
nrows /= ncols;
|
2006-12-19 06:40:25 +05:30
|
|
|
if (nfiles % ncols)
|
2003-01-06 06:41:50 +05:30
|
|
|
nrows++; /* round up fractionals */
|
|
|
|
} else {
|
|
|
|
ncols = 1;
|
|
|
|
}
|
|
|
|
for (row = 0; row < nrows; row++) {
|
|
|
|
int n = row;
|
|
|
|
int nc;
|
2006-01-25 17:23:47 +05:30
|
|
|
|
2006-12-22 03:57:10 +05:30
|
|
|
for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
|
2006-12-22 03:54:46 +05:30
|
|
|
printf("%s%-*s", matches[n],
|
2010-06-01 12:03:18 +05:30
|
|
|
(int)(column_width - unicode_strwidth(matches[n])), ""
|
2009-07-16 05:49:39 +05:30
|
|
|
);
|
2006-01-25 17:23:47 +05:30
|
|
|
}
|
2010-06-01 12:03:18 +05:30
|
|
|
if (ENABLE_UNICODE_SUPPORT)
|
2018-09-30 20:26:56 +05:30
|
|
|
puts(printable_string(matches[n]));
|
2010-06-01 12:03:18 +05:30
|
|
|
else
|
|
|
|
puts(matches[n]);
|
2003-01-06 06:41:50 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-22 08:19:18 +05:30
|
|
|
static const char *is_special_char(char c)
|
|
|
|
{
|
2021-09-17 21:02:30 +05:30
|
|
|
// {: It's mandatory to escape { only if entire name is "{"
|
|
|
|
// (otherwise it's not special. Example: file named "{ "
|
|
|
|
// can be escaped simply as "{\ "; "{a" or "a{" need no escaping),
|
|
|
|
// or if shell supports brace expansion
|
|
|
|
// (ash doesn't, hush optionally does).
|
|
|
|
// (): unlike {, shell treats () specially even in contexts
|
|
|
|
// where they clearly are not valid (e.g. "echo )" is an error).
|
|
|
|
// #: needs escaping to not start a shell comment.
|
|
|
|
return strchr(" `'\"\\#$~?*[{()&;|<>", c);
|
|
|
|
// Used to also have %^=+}]: but not necessary to escape?
|
2010-11-22 08:19:18 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
static char *quote_special_chars(char *found)
|
2007-01-22 00:48:19 +05:30
|
|
|
{
|
|
|
|
int l = 0;
|
2009-07-16 05:49:39 +05:30
|
|
|
char *s = xzalloc((strlen(found) + 1) * 2);
|
2007-01-22 00:48:19 +05:30
|
|
|
|
|
|
|
while (*found) {
|
2010-11-22 08:19:18 +05:30
|
|
|
if (is_special_char(*found))
|
2007-01-22 00:48:19 +05:30
|
|
|
s[l++] = '\\';
|
|
|
|
s[l++] = *found++;
|
|
|
|
}
|
2009-07-16 05:49:39 +05:30
|
|
|
/* s[l] = '\0'; - already is */
|
2007-01-22 00:48:19 +05:30
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2007-01-22 00:49:46 +05:30
|
|
|
/* Do TAB completion */
|
2010-09-03 16:21:36 +05:30
|
|
|
static NOINLINE void input_tab(smallint *lastWasTab)
|
2001-02-17 00:06:04 +05:30
|
|
|
{
|
2010-09-03 17:38:24 +05:30
|
|
|
char *chosen_match;
|
2010-09-03 17:39:24 +05:30
|
|
|
char *match_buf;
|
2010-09-03 17:38:24 +05:30
|
|
|
size_t len_found;
|
|
|
|
/* Length of string used for matching */
|
|
|
|
unsigned match_pfx_len = match_pfx_len;
|
|
|
|
int find_type;
|
2010-11-22 08:19:18 +05:30
|
|
|
# if ENABLE_UNICODE_SUPPORT
|
2010-09-03 17:38:24 +05:30
|
|
|
/* cursor pos in command converted to multibyte form */
|
|
|
|
int cursor_mb;
|
2010-11-22 08:19:18 +05:30
|
|
|
# endif
|
2010-09-03 17:38:24 +05:30
|
|
|
if (!(state->flags & TAB_COMPLETION))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (*lastWasTab) {
|
|
|
|
/* The last char was a TAB too.
|
|
|
|
* Print a list of all the available choices.
|
|
|
|
*/
|
|
|
|
if (num_matches > 0) {
|
|
|
|
/* cursor will be changed by goto_new_line() */
|
|
|
|
int sav_cursor = cursor;
|
|
|
|
goto_new_line();
|
|
|
|
showfiles();
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
draw_full(command_len - sav_cursor);
|
2010-09-03 17:38:24 +05:30
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2010-09-03 17:38:24 +05:30
|
|
|
*lastWasTab = 1;
|
2010-09-03 17:39:24 +05:30
|
|
|
chosen_match = NULL;
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2010-09-03 17:39:24 +05:30
|
|
|
/* Make a local copy of the string up to the position of the cursor.
|
|
|
|
* build_match_prefix will expand it into int16_t's, need to allocate
|
|
|
|
* twice as much as the string_len+1.
|
|
|
|
* (we then also (ab)use this extra space later - see (**))
|
|
|
|
*/
|
|
|
|
match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t));
|
2010-11-22 08:19:18 +05:30
|
|
|
# if !ENABLE_UNICODE_SUPPORT
|
2010-09-03 17:39:24 +05:30
|
|
|
save_string(match_buf, cursor + 1); /* +1 for NUL */
|
2010-11-22 08:19:18 +05:30
|
|
|
# else
|
2010-09-03 17:38:24 +05:30
|
|
|
{
|
|
|
|
CHAR_T wc = command_ps[cursor];
|
|
|
|
command_ps[cursor] = BB_NUL;
|
2010-09-03 17:39:24 +05:30
|
|
|
save_string(match_buf, MAX_LINELEN);
|
2010-09-03 17:38:24 +05:30
|
|
|
command_ps[cursor] = wc;
|
2010-09-03 17:39:24 +05:30
|
|
|
cursor_mb = strlen(match_buf);
|
2010-09-03 17:38:24 +05:30
|
|
|
}
|
2010-11-22 08:19:18 +05:30
|
|
|
# endif
|
2010-09-03 17:39:24 +05:30
|
|
|
find_type = build_match_prefix(match_buf);
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2010-09-03 17:38:24 +05:30
|
|
|
/* Free up any memory already allocated */
|
|
|
|
free_tab_completion_data();
|
2000-04-12 23:19:52 +05:30
|
|
|
|
2010-11-22 08:19:18 +05:30
|
|
|
# if ENABLE_FEATURE_USERNAME_COMPLETION
|
|
|
|
/* If the word starts with ~ and there is no slash in the word,
|
2010-09-03 17:38:24 +05:30
|
|
|
* then try completing this word as a username. */
|
|
|
|
if (state->flags & USERNAME_COMPLETION)
|
2010-09-03 17:39:24 +05:30
|
|
|
if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL)
|
|
|
|
match_pfx_len = complete_username(match_buf);
|
2010-11-22 08:19:18 +05:30
|
|
|
# endif
|
|
|
|
/* If complete_username() did not match,
|
|
|
|
* try to match a command in $PATH, or a directory, or a file */
|
2010-09-03 17:38:24 +05:30
|
|
|
if (!matches)
|
2010-09-03 17:39:24 +05:30
|
|
|
match_pfx_len = complete_cmd_dir_file(match_buf, find_type);
|
2010-11-22 08:19:18 +05:30
|
|
|
|
|
|
|
/* Account for backslashes which will be inserted
|
|
|
|
* by quote_special_chars() later */
|
|
|
|
{
|
|
|
|
const char *e = match_buf + strlen(match_buf);
|
|
|
|
const char *s = e - match_pfx_len;
|
|
|
|
while (s < e)
|
|
|
|
if (is_special_char(*s++))
|
|
|
|
match_pfx_len++;
|
|
|
|
}
|
|
|
|
|
2010-09-03 17:38:24 +05:30
|
|
|
/* Remove duplicates */
|
|
|
|
if (matches) {
|
2010-11-22 08:19:18 +05:30
|
|
|
unsigned i, n = 0;
|
2010-09-03 17:38:24 +05:30
|
|
|
qsort_string_vector(matches, num_matches);
|
|
|
|
for (i = 0; i < num_matches - 1; ++i) {
|
|
|
|
//if (matches[i] && matches[i+1]) { /* paranoia */
|
|
|
|
if (strcmp(matches[i], matches[i+1]) == 0) {
|
|
|
|
free(matches[i]);
|
|
|
|
//matches[i] = NULL; /* paranoia */
|
|
|
|
} else {
|
|
|
|
matches[n++] = matches[i];
|
|
|
|
}
|
|
|
|
//}
|
2001-06-26 07:36:08 +05:30
|
|
|
}
|
2010-09-03 17:38:24 +05:30
|
|
|
matches[n++] = matches[i];
|
|
|
|
num_matches = n;
|
|
|
|
}
|
2010-11-22 08:19:18 +05:30
|
|
|
|
2010-09-03 17:38:24 +05:30
|
|
|
/* Did we find exactly one match? */
|
|
|
|
if (num_matches != 1) { /* no */
|
|
|
|
char *cp;
|
|
|
|
beep();
|
|
|
|
if (!matches)
|
2010-09-03 17:39:24 +05:30
|
|
|
goto ret; /* no matches at all */
|
2010-09-03 17:38:24 +05:30
|
|
|
/* Find common prefix */
|
|
|
|
chosen_match = xstrdup(matches[0]);
|
|
|
|
for (cp = chosen_match; *cp; cp++) {
|
|
|
|
unsigned n;
|
|
|
|
for (n = 1; n < num_matches; n++) {
|
|
|
|
if (matches[n][cp - chosen_match] != *cp) {
|
|
|
|
goto stop;
|
2009-07-12 06:20:35 +05:30
|
|
|
}
|
|
|
|
}
|
2010-09-03 17:38:24 +05:30
|
|
|
}
|
2010-09-03 16:26:36 +05:30
|
|
|
stop:
|
2010-09-03 17:38:24 +05:30
|
|
|
if (cp == chosen_match) { /* have unique prefix? */
|
2010-09-03 17:39:24 +05:30
|
|
|
goto ret; /* no */
|
2010-09-03 17:38:24 +05:30
|
|
|
}
|
|
|
|
*cp = '\0';
|
2010-11-22 08:19:18 +05:30
|
|
|
cp = quote_special_chars(chosen_match);
|
2010-09-03 17:38:24 +05:30
|
|
|
free(chosen_match);
|
|
|
|
chosen_match = cp;
|
|
|
|
len_found = strlen(chosen_match);
|
|
|
|
} else { /* exactly one match */
|
|
|
|
/* Next <tab> is not a double-tab */
|
|
|
|
*lastWasTab = 0;
|
|
|
|
|
2010-11-22 08:19:18 +05:30
|
|
|
chosen_match = quote_special_chars(matches[0]);
|
2010-09-03 17:38:24 +05:30
|
|
|
len_found = strlen(chosen_match);
|
|
|
|
if (chosen_match[len_found-1] != '/') {
|
|
|
|
chosen_match[len_found] = ' ';
|
|
|
|
chosen_match[++len_found] = '\0';
|
2001-01-27 02:12:23 +05:30
|
|
|
}
|
2010-09-03 17:38:24 +05:30
|
|
|
}
|
2009-07-12 06:20:35 +05:30
|
|
|
|
2010-11-22 08:19:18 +05:30
|
|
|
# if !ENABLE_UNICODE_SUPPORT
|
2010-09-03 17:38:24 +05:30
|
|
|
/* Have space to place the match? */
|
|
|
|
/* The result consists of three parts with these lengths: */
|
|
|
|
/* cursor + (len_found - match_pfx_len) + (command_len - cursor) */
|
|
|
|
/* it simplifies into: */
|
|
|
|
if ((int)(len_found - match_pfx_len + command_len) < S.maxsize) {
|
|
|
|
int pos;
|
|
|
|
/* save tail */
|
2010-09-03 17:39:24 +05:30
|
|
|
strcpy(match_buf, &command_ps[cursor]);
|
2010-09-03 17:38:24 +05:30
|
|
|
/* add match and tail */
|
2010-09-03 17:39:24 +05:30
|
|
|
sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf);
|
2010-09-03 17:38:24 +05:30
|
|
|
command_len = strlen(command_ps);
|
|
|
|
/* new pos */
|
|
|
|
pos = cursor + len_found - match_pfx_len;
|
|
|
|
/* write out the matched command */
|
|
|
|
redraw(cmdedit_y, command_len - pos);
|
|
|
|
}
|
2010-11-22 08:19:18 +05:30
|
|
|
# else
|
2010-09-03 17:38:24 +05:30
|
|
|
{
|
2010-09-03 17:39:24 +05:30
|
|
|
/* Use 2nd half of match_buf as scratch space - see (**) */
|
|
|
|
char *command = match_buf + MAX_LINELEN;
|
|
|
|
int len = save_string(command, MAX_LINELEN);
|
2010-09-03 16:26:36 +05:30
|
|
|
/* Have space to place the match? */
|
2010-09-03 17:38:24 +05:30
|
|
|
/* cursor_mb + (len_found - match_pfx_len) + (len - cursor_mb) */
|
|
|
|
if ((int)(len_found - match_pfx_len + len) < MAX_LINELEN) {
|
2010-09-03 16:26:36 +05:30
|
|
|
int pos;
|
2009-07-12 06:20:35 +05:30
|
|
|
/* save tail */
|
2010-09-03 17:39:24 +05:30
|
|
|
strcpy(match_buf, &command[cursor_mb]);
|
2010-09-03 17:38:24 +05:30
|
|
|
/* where do we want to have cursor after all? */
|
|
|
|
strcpy(&command[cursor_mb], chosen_match + match_pfx_len);
|
2011-07-11 11:06:59 +05:30
|
|
|
len = load_string(command);
|
2009-07-12 06:20:35 +05:30
|
|
|
/* add match and tail */
|
2021-04-14 21:22:18 +05:30
|
|
|
stpcpy(stpcpy(&command[cursor_mb], chosen_match + match_pfx_len), match_buf);
|
2011-07-11 11:06:59 +05:30
|
|
|
command_len = load_string(command);
|
2009-07-12 06:20:35 +05:30
|
|
|
/* write out the matched command */
|
2010-09-03 17:38:24 +05:30
|
|
|
/* paranoia: load_string can return 0 on conv error,
|
|
|
|
* prevent passing pos = (0 - 12) to redraw */
|
|
|
|
pos = command_len - len;
|
|
|
|
redraw(cmdedit_y, pos >= 0 ? pos : 0);
|
2009-07-12 06:20:35 +05:30
|
|
|
}
|
2010-09-03 17:38:24 +05:30
|
|
|
}
|
2010-11-22 08:19:18 +05:30
|
|
|
# endif
|
2010-09-03 17:39:24 +05:30
|
|
|
ret:
|
2010-09-03 17:38:24 +05:30
|
|
|
free(chosen_match);
|
2010-09-03 17:39:24 +05:30
|
|
|
free(match_buf);
|
2000-04-12 23:19:52 +05:30
|
|
|
}
|
2007-01-22 00:49:46 +05:30
|
|
|
|
2010-09-03 16:21:36 +05:30
|
|
|
#endif /* FEATURE_TAB_COMPLETION */
|
2000-04-12 23:19:52 +05:30
|
|
|
|
2007-01-22 00:48:19 +05:30
|
|
|
|
2009-03-23 12:03:37 +05:30
|
|
|
line_input_t* FAST_FUNC new_line_input_t(int flags)
|
|
|
|
{
|
|
|
|
line_input_t *n = xzalloc(sizeof(*n));
|
|
|
|
n->flags = flags;
|
2017-08-02 20:57:28 +05:30
|
|
|
n->timeout = -1;
|
2014-01-10 19:08:26 +05:30
|
|
|
#if MAX_HISTORY > 0
|
2011-03-31 16:46:52 +05:30
|
|
|
n->max_history = MAX_HISTORY;
|
2014-01-10 19:08:26 +05:30
|
|
|
#endif
|
2009-03-23 12:03:37 +05:30
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-11-03 03:39:37 +05:30
|
|
|
#if MAX_HISTORY > 0
|
2007-01-22 00:48:19 +05:30
|
|
|
|
2013-04-07 22:17:24 +05:30
|
|
|
unsigned FAST_FUNC size_from_HISTFILESIZE(const char *hp)
|
2011-03-31 16:46:52 +05:30
|
|
|
{
|
|
|
|
int size = MAX_HISTORY;
|
|
|
|
if (hp) {
|
|
|
|
size = atoi(hp);
|
|
|
|
if (size <= 0)
|
|
|
|
return 1;
|
|
|
|
if (size > MAX_HISTORY)
|
|
|
|
return MAX_HISTORY;
|
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2008-09-27 06:58:56 +05:30
|
|
|
static void save_command_ps_at_cur_history(void)
|
|
|
|
{
|
2009-07-10 22:10:49 +05:30
|
|
|
if (command_ps[0] != BB_NUL) {
|
2008-09-27 06:58:56 +05:30
|
|
|
int cur = state->cur_history;
|
|
|
|
free(state->history[cur]);
|
2009-07-10 22:10:49 +05:30
|
|
|
|
2010-03-26 18:36:56 +05:30
|
|
|
# if ENABLE_UNICODE_SUPPORT
|
2009-07-10 22:10:49 +05:30
|
|
|
{
|
|
|
|
char tbuf[MAX_LINELEN];
|
|
|
|
save_string(tbuf, sizeof(tbuf));
|
|
|
|
state->history[cur] = xstrdup(tbuf);
|
|
|
|
}
|
2009-09-27 06:18:53 +05:30
|
|
|
# else
|
2008-09-27 06:58:56 +05:30
|
|
|
state->history[cur] = xstrdup(command_ps);
|
2009-09-27 06:18:53 +05:30
|
|
|
# endif
|
2008-09-27 06:58:56 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-01-22 12:51:38 +05:30
|
|
|
/* state->flags is already checked to be nonzero */
|
2008-09-27 06:58:56 +05:30
|
|
|
static int get_previous_history(void)
|
2000-04-12 23:19:52 +05:30
|
|
|
{
|
2008-09-27 06:58:56 +05:30
|
|
|
if ((state->flags & DO_HISTORY) && state->cur_history) {
|
|
|
|
save_command_ps_at_cur_history();
|
|
|
|
state->cur_history--;
|
|
|
|
return 1;
|
2002-11-27 14:59:49 +05:30
|
|
|
}
|
2008-09-27 06:58:56 +05:30
|
|
|
beep();
|
|
|
|
return 0;
|
2000-04-12 23:19:52 +05:30
|
|
|
}
|
|
|
|
|
2002-11-27 14:59:49 +05:30
|
|
|
static int get_next_history(void)
|
2000-04-12 23:19:52 +05:30
|
|
|
{
|
2007-01-22 12:51:38 +05:30
|
|
|
if (state->flags & DO_HISTORY) {
|
2008-09-27 06:58:56 +05:30
|
|
|
if (state->cur_history < state->cnt_history) {
|
|
|
|
save_command_ps_at_cur_history(); /* save the current history line */
|
|
|
|
return ++state->cur_history;
|
2007-01-22 12:51:38 +05:30
|
|
|
}
|
2002-11-27 14:59:49 +05:30
|
|
|
}
|
2007-01-22 12:51:38 +05:30
|
|
|
beep();
|
|
|
|
return 0;
|
2000-04-12 23:19:52 +05:30
|
|
|
}
|
2002-12-04 04:15:46 +05:30
|
|
|
|
2013-04-07 22:17:24 +05:30
|
|
|
/* Lists command history. Used by shell 'history' builtins */
|
|
|
|
void FAST_FUNC show_history(const line_input_t *st)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!st)
|
|
|
|
return;
|
|
|
|
for (i = 0; i < st->cnt_history; i++)
|
|
|
|
printf("%4d %s\n", i, st->history[i]);
|
|
|
|
}
|
|
|
|
|
2020-12-22 02:06:58 +05:30
|
|
|
# if ENABLE_FEATURE_EDITING_SAVEHISTORY
|
2018-12-27 22:33:20 +05:30
|
|
|
void FAST_FUNC free_line_input_t(line_input_t *n)
|
|
|
|
{
|
2020-12-22 02:06:58 +05:30
|
|
|
if (n) {
|
|
|
|
int i = n->cnt_history;
|
|
|
|
while (i > 0)
|
|
|
|
free(n->history[--i]);
|
|
|
|
free(n);
|
|
|
|
}
|
2018-12-27 22:33:20 +05:30
|
|
|
}
|
2020-12-22 02:06:58 +05:30
|
|
|
# else
|
|
|
|
/* #defined to free() in libbb.h */
|
|
|
|
# endif
|
2018-12-27 22:33:20 +05:30
|
|
|
|
2009-09-27 06:18:53 +05:30
|
|
|
# if ENABLE_FEATURE_EDITING_SAVEHISTORY
|
2009-03-23 00:30:05 +05:30
|
|
|
/* We try to ensure that concurrent additions to the history
|
2009-03-23 12:03:37 +05:30
|
|
|
* do not overwrite each other.
|
2009-03-23 00:30:05 +05:30
|
|
|
* Otherwise shell users get unhappy.
|
|
|
|
*
|
|
|
|
* History file is trimmed lazily, when it grows several times longer
|
|
|
|
* than configured MAX_HISTORY lines.
|
|
|
|
*/
|
|
|
|
|
2007-01-22 12:51:38 +05:30
|
|
|
/* state->flags is already checked to be nonzero */
|
2009-03-23 12:03:37 +05:30
|
|
|
static void load_history(line_input_t *st_parm)
|
2002-12-09 16:40:40 +05:30
|
|
|
{
|
2009-03-23 00:30:05 +05:30
|
|
|
char *temp_h[MAX_HISTORY];
|
|
|
|
char *line;
|
2002-12-04 04:15:46 +05:30
|
|
|
FILE *fp;
|
2009-03-23 00:30:05 +05:30
|
|
|
unsigned idx, i, line_len;
|
2002-12-04 04:15:46 +05:30
|
|
|
|
2008-03-26 19:02:30 +05:30
|
|
|
/* NB: do not trash old history if file can't be opened */
|
2002-12-04 04:15:46 +05:30
|
|
|
|
2009-03-23 12:03:37 +05:30
|
|
|
fp = fopen_for_read(st_parm->hist_file);
|
2006-12-22 03:57:10 +05:30
|
|
|
if (fp) {
|
2008-03-26 19:02:30 +05:30
|
|
|
/* clean up old history */
|
2009-03-23 12:03:37 +05:30
|
|
|
for (idx = st_parm->cnt_history; idx > 0;) {
|
2009-03-23 00:30:05 +05:30
|
|
|
idx--;
|
2009-03-23 12:03:37 +05:30
|
|
|
free(st_parm->history[idx]);
|
|
|
|
st_parm->history[idx] = NULL;
|
2008-03-26 19:02:30 +05:30
|
|
|
}
|
|
|
|
|
2009-03-23 00:30:05 +05:30
|
|
|
/* fill temp_h[], retaining only last MAX_HISTORY lines */
|
|
|
|
memset(temp_h, 0, sizeof(temp_h));
|
2011-09-04 19:42:33 +05:30
|
|
|
idx = 0;
|
2012-04-25 02:10:58 +05:30
|
|
|
st_parm->cnt_history_in_file = 0;
|
2009-03-23 00:30:05 +05:30
|
|
|
while ((line = xmalloc_fgetline(fp)) != NULL) {
|
|
|
|
if (line[0] == '\0') {
|
|
|
|
free(line);
|
2002-12-09 16:40:40 +05:30
|
|
|
continue;
|
|
|
|
}
|
2009-03-23 00:30:05 +05:30
|
|
|
free(temp_h[idx]);
|
|
|
|
temp_h[idx] = line;
|
2012-04-25 02:10:58 +05:30
|
|
|
st_parm->cnt_history_in_file++;
|
2009-03-23 00:30:05 +05:30
|
|
|
idx++;
|
2011-03-31 16:46:52 +05:30
|
|
|
if (idx == st_parm->max_history)
|
2009-03-23 00:30:05 +05:30
|
|
|
idx = 0;
|
2002-12-04 04:15:46 +05:30
|
|
|
}
|
2006-12-22 03:57:10 +05:30
|
|
|
fclose(fp);
|
2009-03-23 00:30:05 +05:30
|
|
|
|
|
|
|
/* find first non-NULL temp_h[], if any */
|
2009-03-23 12:03:37 +05:30
|
|
|
if (st_parm->cnt_history_in_file) {
|
2009-03-23 00:30:05 +05:30
|
|
|
while (temp_h[idx] == NULL) {
|
|
|
|
idx++;
|
2011-03-31 16:46:52 +05:30
|
|
|
if (idx == st_parm->max_history)
|
2009-03-23 00:30:05 +05:30
|
|
|
idx = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-23 12:03:37 +05:30
|
|
|
/* copy temp_h[] to st_parm->history[] */
|
2011-03-31 16:46:52 +05:30
|
|
|
for (i = 0; i < st_parm->max_history;) {
|
2009-03-23 00:30:05 +05:30
|
|
|
line = temp_h[idx];
|
|
|
|
if (!line)
|
|
|
|
break;
|
|
|
|
idx++;
|
2011-03-31 16:46:52 +05:30
|
|
|
if (idx == st_parm->max_history)
|
2009-03-23 00:30:05 +05:30
|
|
|
idx = 0;
|
|
|
|
line_len = strlen(line);
|
|
|
|
if (line_len >= MAX_LINELEN)
|
|
|
|
line[MAX_LINELEN-1] = '\0';
|
2009-03-23 12:03:37 +05:30
|
|
|
st_parm->history[i++] = line;
|
2009-03-23 00:30:05 +05:30
|
|
|
}
|
2009-03-23 12:03:37 +05:30
|
|
|
st_parm->cnt_history = i;
|
2011-09-04 19:42:33 +05:30
|
|
|
if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT)
|
|
|
|
st_parm->cnt_history_in_file = i;
|
2002-12-04 04:15:46 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-04 19:42:33 +05:30
|
|
|
# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
|
|
|
|
void save_history(line_input_t *st)
|
|
|
|
{
|
|
|
|
FILE *fp;
|
|
|
|
|
2020-12-22 02:06:58 +05:30
|
|
|
if (!st || !st->hist_file)
|
2011-09-04 19:42:33 +05:30
|
|
|
return;
|
|
|
|
if (st->cnt_history <= st->cnt_history_in_file)
|
|
|
|
return;
|
|
|
|
|
|
|
|
fp = fopen(st->hist_file, "a");
|
|
|
|
if (fp) {
|
|
|
|
int i, fd;
|
|
|
|
char *new_name;
|
|
|
|
line_input_t *st_temp;
|
|
|
|
|
|
|
|
for (i = st->cnt_history_in_file; i < st->cnt_history; i++)
|
|
|
|
fprintf(fp, "%s\n", st->history[i]);
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
/* we may have concurrently written entries from others.
|
|
|
|
* load them */
|
|
|
|
st_temp = new_line_input_t(st->flags);
|
|
|
|
st_temp->hist_file = st->hist_file;
|
|
|
|
st_temp->max_history = st->max_history;
|
|
|
|
load_history(st_temp);
|
|
|
|
|
|
|
|
/* write out temp file and replace hist_file atomically */
|
|
|
|
new_name = xasprintf("%s.%u.new", st->hist_file, (int) getpid());
|
|
|
|
fd = open(new_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
|
|
|
if (fd >= 0) {
|
|
|
|
fp = xfdopen_for_write(fd);
|
|
|
|
for (i = 0; i < st_temp->cnt_history; i++)
|
|
|
|
fprintf(fp, "%s\n", st_temp->history[i]);
|
|
|
|
fclose(fp);
|
|
|
|
if (rename(new_name, st->hist_file) == 0)
|
|
|
|
st->cnt_history_in_file = st_temp->cnt_history;
|
|
|
|
}
|
|
|
|
free(new_name);
|
|
|
|
free_line_input_t(st_temp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
# else
|
2009-03-23 00:30:05 +05:30
|
|
|
static void save_history(char *str)
|
2002-12-04 04:15:46 +05:30
|
|
|
{
|
2009-03-23 00:30:05 +05:30
|
|
|
int fd;
|
2009-03-23 12:03:37 +05:30
|
|
|
int len, len2;
|
2003-07-28 15:26:35 +05:30
|
|
|
|
2011-09-04 19:42:33 +05:30
|
|
|
if (!state->hist_file)
|
|
|
|
return;
|
|
|
|
|
2010-11-15 07:28:28 +05:30
|
|
|
fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600);
|
2009-03-23 00:30:05 +05:30
|
|
|
if (fd < 0)
|
|
|
|
return;
|
2009-03-23 12:03:37 +05:30
|
|
|
xlseek(fd, 0, SEEK_END); /* paranoia */
|
|
|
|
len = strlen(str);
|
|
|
|
str[len] = '\n'; /* we (try to) do atomic write */
|
|
|
|
len2 = full_write(fd, str, len + 1);
|
|
|
|
str[len] = '\0';
|
2009-03-23 00:30:05 +05:30
|
|
|
close(fd);
|
2009-03-23 12:03:37 +05:30
|
|
|
if (len2 != len + 1)
|
|
|
|
return; /* "wtf?" */
|
2009-03-23 00:30:05 +05:30
|
|
|
|
|
|
|
/* did we write so much that history file needs trimming? */
|
2009-03-23 12:03:37 +05:30
|
|
|
state->cnt_history_in_file++;
|
2011-03-31 16:46:52 +05:30
|
|
|
if (state->cnt_history_in_file > state->max_history * 4) {
|
2009-03-23 00:30:05 +05:30
|
|
|
char *new_name;
|
2009-03-23 12:03:37 +05:30
|
|
|
line_input_t *st_temp;
|
|
|
|
|
|
|
|
/* we may have concurrently written entries from others.
|
|
|
|
* load them */
|
|
|
|
st_temp = new_line_input_t(state->flags);
|
|
|
|
st_temp->hist_file = state->hist_file;
|
2011-03-31 18:09:38 +05:30
|
|
|
st_temp->max_history = state->max_history;
|
2009-03-23 12:03:37 +05:30
|
|
|
load_history(st_temp);
|
2009-03-23 00:30:05 +05:30
|
|
|
|
2009-03-23 12:03:37 +05:30
|
|
|
/* write out temp file and replace hist_file atomically */
|
|
|
|
new_name = xasprintf("%s.%u.new", state->hist_file, (int) getpid());
|
2011-09-04 18:58:03 +05:30
|
|
|
fd = open(new_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
2010-11-15 07:28:28 +05:30
|
|
|
if (fd >= 0) {
|
|
|
|
FILE *fp;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
fp = xfdopen_for_write(fd);
|
2009-03-23 12:03:37 +05:30
|
|
|
for (i = 0; i < st_temp->cnt_history; i++)
|
|
|
|
fprintf(fp, "%s\n", st_temp->history[i]);
|
2009-03-23 00:30:05 +05:30
|
|
|
fclose(fp);
|
2009-03-23 12:03:37 +05:30
|
|
|
if (rename(new_name, state->hist_file) == 0)
|
|
|
|
state->cnt_history_in_file = st_temp->cnt_history;
|
2009-03-23 00:30:05 +05:30
|
|
|
}
|
|
|
|
free(new_name);
|
2009-03-23 12:03:37 +05:30
|
|
|
free_line_input_t(st_temp);
|
2002-12-04 04:15:46 +05:30
|
|
|
}
|
|
|
|
}
|
2011-09-04 19:42:33 +05:30
|
|
|
# endif
|
2009-09-27 06:18:53 +05:30
|
|
|
# else
|
|
|
|
# define load_history(a) ((void)0)
|
|
|
|
# define save_history(a) ((void)0)
|
|
|
|
# endif /* FEATURE_COMMAND_SAVEHISTORY */
|
2002-12-04 04:15:46 +05:30
|
|
|
|
2009-03-23 00:30:05 +05:30
|
|
|
static void remember_in_history(char *str)
|
2007-01-22 12:51:38 +05:30
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!(state->flags & DO_HISTORY))
|
|
|
|
return;
|
2008-09-27 06:58:56 +05:30
|
|
|
if (str[0] == '\0')
|
|
|
|
return;
|
2007-01-22 12:51:38 +05:30
|
|
|
i = state->cnt_history;
|
2008-09-27 06:58:56 +05:30
|
|
|
/* Don't save dupes */
|
|
|
|
if (i && strcmp(state->history[i-1], str) == 0)
|
|
|
|
return;
|
|
|
|
|
2011-03-31 16:46:52 +05:30
|
|
|
free(state->history[state->max_history]); /* redundant, paranoia */
|
|
|
|
state->history[state->max_history] = NULL; /* redundant, paranoia */
|
2008-09-27 06:58:56 +05:30
|
|
|
|
|
|
|
/* If history[] is full, remove the oldest command */
|
2011-03-31 16:46:52 +05:30
|
|
|
/* we need to keep history[state->max_history] empty, hence >=, not > */
|
|
|
|
if (i >= state->max_history) {
|
2007-01-22 12:51:38 +05:30
|
|
|
free(state->history[0]);
|
2011-03-31 16:46:52 +05:30
|
|
|
for (i = 0; i < state->max_history-1; i++)
|
2007-01-22 12:51:38 +05:30
|
|
|
state->history[i] = state->history[i+1];
|
2011-03-31 16:46:52 +05:30
|
|
|
/* i == state->max_history-1 */
|
2011-11-04 05:39:09 +05:30
|
|
|
# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
|
|
|
|
if (state->cnt_history_in_file)
|
2011-09-04 19:42:33 +05:30
|
|
|
state->cnt_history_in_file--;
|
2011-11-04 05:39:09 +05:30
|
|
|
# endif
|
2007-01-22 12:51:38 +05:30
|
|
|
}
|
2011-03-31 16:46:52 +05:30
|
|
|
/* i <= state->max_history-1 */
|
2007-01-22 12:51:38 +05:30
|
|
|
state->history[i++] = xstrdup(str);
|
2011-03-31 16:46:52 +05:30
|
|
|
/* i <= state->max_history */
|
2007-01-22 12:51:38 +05:30
|
|
|
state->cur_history = i;
|
|
|
|
state->cnt_history = i;
|
2011-09-04 19:42:33 +05:30
|
|
|
# if ENABLE_FEATURE_EDITING_SAVEHISTORY && !ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
|
|
|
|
save_history(str);
|
2009-09-27 06:18:53 +05:30
|
|
|
# endif
|
2007-01-22 12:51:38 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
#else /* MAX_HISTORY == 0 */
|
2009-09-27 06:18:53 +05:30
|
|
|
# define remember_in_history(a) ((void)0)
|
2007-01-22 12:51:38 +05:30
|
|
|
#endif /* MAX_HISTORY */
|
2001-02-17 00:06:04 +05:30
|
|
|
|
|
|
|
|
2009-10-26 04:20:56 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_VI
|
2000-03-17 06:42:41 +05:30
|
|
|
/*
|
2007-01-22 00:48:19 +05:30
|
|
|
* vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us>
|
2000-03-17 06:42:41 +05:30
|
|
|
*/
|
2005-09-22 18:29:26 +05:30
|
|
|
static void
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_Word_motion(int eat)
|
2005-08-05 00:34:46 +05:30
|
|
|
{
|
2009-07-10 22:10:49 +05:30
|
|
|
CHAR_T *command = command_ps;
|
|
|
|
|
|
|
|
while (cursor < command_len && !BB_isspace(command[cursor]))
|
2005-08-05 00:34:46 +05:30
|
|
|
input_forward();
|
2009-07-10 22:10:49 +05:30
|
|
|
if (eat) while (cursor < command_len && BB_isspace(command[cursor]))
|
2005-08-05 00:34:46 +05:30
|
|
|
input_forward();
|
|
|
|
}
|
|
|
|
|
2005-09-22 18:29:26 +05:30
|
|
|
static void
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_word_motion(int eat)
|
2005-08-05 00:34:46 +05:30
|
|
|
{
|
2009-07-10 22:10:49 +05:30
|
|
|
CHAR_T *command = command_ps;
|
|
|
|
|
2016-08-15 03:00:29 +05:30
|
|
|
if (BB_isalnum_or_underscore(command[cursor])) {
|
2007-01-22 14:04:44 +05:30
|
|
|
while (cursor < command_len
|
2016-08-15 03:00:29 +05:30
|
|
|
&& (BB_isalnum_or_underscore(command[cursor+1]))
|
2009-07-12 04:21:15 +05:30
|
|
|
) {
|
2005-08-05 00:34:46 +05:30
|
|
|
input_forward();
|
2009-07-12 04:21:15 +05:30
|
|
|
}
|
2009-07-10 22:10:49 +05:30
|
|
|
} else if (BB_ispunct(command[cursor])) {
|
|
|
|
while (cursor < command_len && BB_ispunct(command[cursor+1]))
|
2005-08-05 00:34:46 +05:30
|
|
|
input_forward();
|
|
|
|
}
|
|
|
|
|
2007-01-22 14:04:44 +05:30
|
|
|
if (cursor < command_len)
|
2005-08-05 00:34:46 +05:30
|
|
|
input_forward();
|
|
|
|
|
2009-07-12 04:21:15 +05:30
|
|
|
if (eat) {
|
2009-07-10 22:10:49 +05:30
|
|
|
while (cursor < command_len && BB_isspace(command[cursor]))
|
2005-08-05 00:34:46 +05:30
|
|
|
input_forward();
|
2009-07-12 04:21:15 +05:30
|
|
|
}
|
2005-08-05 00:34:46 +05:30
|
|
|
}
|
|
|
|
|
2005-09-22 18:29:26 +05:30
|
|
|
static void
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_End_motion(void)
|
2005-08-05 00:34:46 +05:30
|
|
|
{
|
2009-07-10 22:10:49 +05:30
|
|
|
CHAR_T *command = command_ps;
|
|
|
|
|
2005-08-05 00:34:46 +05:30
|
|
|
input_forward();
|
2009-07-10 22:10:49 +05:30
|
|
|
while (cursor < command_len && BB_isspace(command[cursor]))
|
2005-08-05 00:34:46 +05:30
|
|
|
input_forward();
|
2009-07-10 22:10:49 +05:30
|
|
|
while (cursor < command_len-1 && !BB_isspace(command[cursor+1]))
|
2005-08-05 00:34:46 +05:30
|
|
|
input_forward();
|
|
|
|
}
|
|
|
|
|
2005-09-22 18:29:26 +05:30
|
|
|
static void
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_end_motion(void)
|
2005-08-05 00:34:46 +05:30
|
|
|
{
|
2009-07-10 22:10:49 +05:30
|
|
|
CHAR_T *command = command_ps;
|
|
|
|
|
2007-01-22 14:04:44 +05:30
|
|
|
if (cursor >= command_len-1)
|
2005-08-05 00:34:46 +05:30
|
|
|
return;
|
|
|
|
input_forward();
|
2009-07-10 22:10:49 +05:30
|
|
|
while (cursor < command_len-1 && BB_isspace(command[cursor]))
|
2005-08-05 00:34:46 +05:30
|
|
|
input_forward();
|
2007-01-22 14:04:44 +05:30
|
|
|
if (cursor >= command_len-1)
|
2005-08-05 00:34:46 +05:30
|
|
|
return;
|
2016-08-15 03:00:29 +05:30
|
|
|
if (BB_isalnum_or_underscore(command[cursor])) {
|
2007-01-22 14:04:44 +05:30
|
|
|
while (cursor < command_len-1
|
2016-08-15 03:00:29 +05:30
|
|
|
&& (BB_isalnum_or_underscore(command[cursor+1]))
|
2006-12-22 03:57:10 +05:30
|
|
|
) {
|
2005-08-05 00:34:46 +05:30
|
|
|
input_forward();
|
2006-12-22 03:57:10 +05:30
|
|
|
}
|
2009-07-10 22:10:49 +05:30
|
|
|
} else if (BB_ispunct(command[cursor])) {
|
|
|
|
while (cursor < command_len-1 && BB_ispunct(command[cursor+1]))
|
2005-08-05 00:34:46 +05:30
|
|
|
input_forward();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-09-22 18:29:26 +05:30
|
|
|
static void
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_Back_motion(void)
|
2005-08-05 00:34:46 +05:30
|
|
|
{
|
2009-07-10 22:10:49 +05:30
|
|
|
CHAR_T *command = command_ps;
|
|
|
|
|
|
|
|
while (cursor > 0 && BB_isspace(command[cursor-1]))
|
2005-08-05 00:34:46 +05:30
|
|
|
input_backward(1);
|
2009-07-10 22:10:49 +05:30
|
|
|
while (cursor > 0 && !BB_isspace(command[cursor-1]))
|
2005-08-05 00:34:46 +05:30
|
|
|
input_backward(1);
|
|
|
|
}
|
|
|
|
|
2005-09-22 18:29:26 +05:30
|
|
|
static void
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_back_motion(void)
|
2005-08-05 00:34:46 +05:30
|
|
|
{
|
2009-07-10 22:10:49 +05:30
|
|
|
CHAR_T *command = command_ps;
|
|
|
|
|
2005-08-05 00:34:46 +05:30
|
|
|
if (cursor <= 0)
|
|
|
|
return;
|
|
|
|
input_backward(1);
|
2009-07-10 22:10:49 +05:30
|
|
|
while (cursor > 0 && BB_isspace(command[cursor]))
|
2005-08-05 00:34:46 +05:30
|
|
|
input_backward(1);
|
|
|
|
if (cursor <= 0)
|
|
|
|
return;
|
2016-08-15 03:00:29 +05:30
|
|
|
if (BB_isalnum_or_underscore(command[cursor])) {
|
2006-12-22 03:57:10 +05:30
|
|
|
while (cursor > 0
|
2016-08-15 03:00:29 +05:30
|
|
|
&& (BB_isalnum_or_underscore(command[cursor-1]))
|
2006-12-22 03:57:10 +05:30
|
|
|
) {
|
2005-08-05 00:34:46 +05:30
|
|
|
input_backward(1);
|
2006-12-22 03:57:10 +05:30
|
|
|
}
|
2009-07-10 22:10:49 +05:30
|
|
|
} else if (BB_ispunct(command[cursor])) {
|
|
|
|
while (cursor > 0 && BB_ispunct(command[cursor-1]))
|
2005-08-05 00:34:46 +05:30
|
|
|
input_backward(1);
|
|
|
|
}
|
|
|
|
}
|
2021-09-25 23:06:35 +05:30
|
|
|
#endif /* ENABLE_FEATURE_EDITING_VI */
|
2007-01-22 00:48:19 +05:30
|
|
|
|
2009-10-26 04:20:56 +05:30
|
|
|
/* Modelled after bash 4.0 behavior of Ctrl-<arrow> */
|
|
|
|
static void ctrl_left(void)
|
|
|
|
{
|
|
|
|
CHAR_T *command = command_ps;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
CHAR_T c;
|
|
|
|
|
|
|
|
input_backward(1);
|
|
|
|
if (cursor == 0)
|
|
|
|
break;
|
|
|
|
c = command[cursor];
|
|
|
|
if (c != ' ' && !BB_ispunct(c)) {
|
|
|
|
/* we reached a "word" delimited by spaces/punct.
|
|
|
|
* go to its beginning */
|
|
|
|
while (1) {
|
|
|
|
c = command[cursor - 1];
|
|
|
|
if (c == ' ' || BB_ispunct(c))
|
|
|
|
break;
|
|
|
|
input_backward(1);
|
|
|
|
if (cursor == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void ctrl_right(void)
|
|
|
|
{
|
|
|
|
CHAR_T *command = command_ps;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
CHAR_T c;
|
|
|
|
|
|
|
|
c = command[cursor];
|
|
|
|
if (c == BB_NUL)
|
|
|
|
break;
|
|
|
|
if (c != ' ' && !BB_ispunct(c)) {
|
|
|
|
/* we reached a "word" delimited by spaces/punct.
|
|
|
|
* go to its end + 1 */
|
|
|
|
while (1) {
|
|
|
|
input_forward();
|
|
|
|
c = command[cursor];
|
|
|
|
if (c == BB_NUL || c == ' ' || BB_ispunct(c))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
input_forward();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-01-22 00:48:19 +05:30
|
|
|
|
|
|
|
/*
|
2007-01-22 12:51:38 +05:30
|
|
|
* read_line_input and its helpers
|
2007-01-22 00:48:19 +05:30
|
|
|
*/
|
|
|
|
|
2009-11-11 07:49:30 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
|
|
|
|
static void ask_terminal(void)
|
|
|
|
{
|
|
|
|
/* Ask terminal where is the cursor now.
|
|
|
|
* lineedit_read_key handles response and corrects
|
|
|
|
* our idea of current cursor position.
|
|
|
|
* Testcase: run "echo -n long_line_long_line_long_line",
|
|
|
|
* then type in a long, wrapping command and try to
|
|
|
|
* delete it using backspace key.
|
|
|
|
* Note: we print it _after_ prompt, because
|
|
|
|
* prompt may contain CR. Example: PS1='\[\r\n\]\w '
|
|
|
|
*/
|
|
|
|
/* Problem: if there is buffered input on stdin,
|
|
|
|
* the response will be delivered later,
|
|
|
|
* possibly to an unsuspecting application.
|
|
|
|
* Testcase: "sleep 1; busybox ash" + press and hold [Enter].
|
|
|
|
* Result:
|
|
|
|
* ~/srcdevel/bbox/fix/busybox.t4 #
|
|
|
|
* ~/srcdevel/bbox/fix/busybox.t4 #
|
|
|
|
* ^[[59;34~/srcdevel/bbox/fix/busybox.t4 # <-- garbage
|
|
|
|
* ~/srcdevel/bbox/fix/busybox.t4 #
|
|
|
|
*
|
|
|
|
* Checking for input with poll only makes the race narrower,
|
|
|
|
* I still can trigger it. Strace:
|
|
|
|
*
|
|
|
|
* write(1, "~/srcdevel/bbox/fix/busybox.t4 # ", 33) = 33
|
|
|
|
* poll([{fd=0, events=POLLIN}], 1, 0) = 0 (Timeout) <-- no input exists
|
|
|
|
* write(1, "\33[6n", 4) = 4 <-- send the ESC sequence, quick!
|
2011-05-20 07:56:29 +05:30
|
|
|
* poll([{fd=0, events=POLLIN}], 1, -1) = 1 ([{fd=0, revents=POLLIN}])
|
2009-11-11 07:49:30 +05:30
|
|
|
* read(0, "\n", 1) = 1 <-- oh crap, user's input got in first
|
|
|
|
*/
|
2010-07-25 03:36:41 +05:30
|
|
|
struct pollfd pfd;
|
|
|
|
|
|
|
|
pfd.fd = STDIN_FILENO;
|
|
|
|
pfd.events = POLLIN;
|
|
|
|
if (safe_poll(&pfd, 1, 0) == 0) {
|
|
|
|
S.sent_ESC_br6n = 1;
|
2021-02-04 01:17:14 +05:30
|
|
|
fputs_stdout(ESC"[6n");
|
2010-07-25 03:36:41 +05:30
|
|
|
fflush_all(); /* make terminal see it ASAP! */
|
2009-11-11 07:49:30 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
2021-09-25 23:06:35 +05:30
|
|
|
# define ask_terminal() ((void)0)
|
2009-11-11 07:49:30 +05:30
|
|
|
#endif
|
|
|
|
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
/* Note about multi-line PS1 (e.g. "\n\w \u@\h\n> ") and prompt redrawing:
|
|
|
|
*
|
|
|
|
* If the prompt has any newlines, after we print it once we use only its last
|
|
|
|
* line to redraw in-place, which makes it simpler to calculate how many lines
|
|
|
|
* we should move the cursor up to align the redraw (cmdedit_y). The earlier
|
|
|
|
* prompt lines just stay on screen and we redraw below them.
|
|
|
|
*
|
|
|
|
* Use cases for all prompt lines beyond the initial draw:
|
|
|
|
* - After clear-screen (^L) or after displaying tab-completion choices, we
|
|
|
|
* print the full prompt, as it isn't redrawn in-place.
|
|
|
|
* - During terminal resize we could try to redraw all lines, but we don't,
|
|
|
|
* because it requires delicate alignment, it's good enough with only the
|
|
|
|
* last line, and doing it wrong is arguably worse than not doing it at all.
|
|
|
|
*
|
|
|
|
* Terminology wise, if it doesn't mention "full", then it means the last/sole
|
|
|
|
* prompt line. We use the prompt (last/sole line) while redrawing in-place,
|
|
|
|
* and the full where we need a fresh one unrelated to an earlier position.
|
|
|
|
*
|
|
|
|
* If PS1 is not multiline, the last/sole line and the full are the same string.
|
|
|
|
*/
|
|
|
|
|
2013-08-19 20:15:04 +05:30
|
|
|
/* Called just once at read_line_input() init time */
|
2007-01-22 14:33:07 +05:30
|
|
|
#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
|
2007-11-10 07:05:47 +05:30
|
|
|
static void parse_and_put_prompt(const char *prmt_ptr)
|
2007-01-22 00:48:19 +05:30
|
|
|
{
|
2013-08-19 20:15:04 +05:30
|
|
|
const char *p;
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
cmdedit_prompt = prompt_last_line = prmt_ptr;
|
2013-08-19 20:15:04 +05:30
|
|
|
p = strrchr(prmt_ptr, '\n');
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
if (p)
|
|
|
|
prompt_last_line = p + 1;
|
|
|
|
cmdedit_prmt_len = unicode_strwidth(prompt_last_line);
|
2007-01-22 00:48:19 +05:30
|
|
|
put_prompt();
|
|
|
|
}
|
|
|
|
#else
|
2007-11-10 07:05:47 +05:30
|
|
|
static void parse_and_put_prompt(const char *prmt_ptr)
|
2007-01-22 00:48:19 +05:30
|
|
|
{
|
2013-08-19 20:15:04 +05:30
|
|
|
int prmt_size = 0;
|
2007-01-22 00:48:19 +05:30
|
|
|
char *prmt_mem_ptr = xzalloc(1);
|
2013-03-29 17:39:05 +05:30
|
|
|
char *cwd_buf = NULL;
|
2013-08-19 20:15:04 +05:30
|
|
|
char flg_not_length = '[';
|
2007-12-03 16:15:14 +05:30
|
|
|
char cbuf[2];
|
2007-01-22 00:48:19 +05:30
|
|
|
|
2013-08-19 20:14:05 +05:30
|
|
|
/*cmdedit_prmt_len = 0; - already is */
|
2007-01-22 13:00:26 +05:30
|
|
|
|
2007-12-03 16:15:14 +05:30
|
|
|
cbuf[1] = '\0'; /* never changes */
|
|
|
|
|
2007-01-22 00:48:19 +05:30
|
|
|
while (*prmt_ptr) {
|
2013-08-19 20:15:04 +05:30
|
|
|
char timebuf[sizeof("HH:MM:SS")];
|
2007-12-03 16:15:14 +05:30
|
|
|
char *free_me = NULL;
|
2013-08-19 20:15:04 +05:30
|
|
|
char *pbuf;
|
|
|
|
char c;
|
2007-12-03 16:15:14 +05:30
|
|
|
|
|
|
|
pbuf = cbuf;
|
2007-01-22 00:48:19 +05:30
|
|
|
c = *prmt_ptr++;
|
|
|
|
if (c == '\\') {
|
2013-03-29 17:39:05 +05:30
|
|
|
const char *cp;
|
2007-01-22 00:48:19 +05:30
|
|
|
int l;
|
2013-03-28 17:50:12 +05:30
|
|
|
/*
|
|
|
|
* Supported via bb_process_escape_sequence:
|
|
|
|
* \a ASCII bell character (07)
|
|
|
|
* \e ASCII escape character (033)
|
|
|
|
* \n newline
|
|
|
|
* \r carriage return
|
|
|
|
* \\ backslash
|
|
|
|
* \nnn char with octal code nnn
|
|
|
|
* Supported:
|
|
|
|
* \$ if the effective UID is 0, a #, otherwise a $
|
|
|
|
* \w current working directory, with $HOME abbreviated with a tilde
|
|
|
|
* Note: we do not support $PROMPT_DIRTRIM=n feature
|
2013-03-29 17:39:05 +05:30
|
|
|
* \W basename of the current working directory, with $HOME abbreviated with a tilde
|
2013-03-28 17:50:12 +05:30
|
|
|
* \h hostname up to the first '.'
|
|
|
|
* \H hostname
|
|
|
|
* \u username
|
|
|
|
* \[ begin a sequence of non-printing characters
|
|
|
|
* \] end a sequence of non-printing characters
|
2013-03-29 17:39:05 +05:30
|
|
|
* \T current time in 12-hour HH:MM:SS format
|
|
|
|
* \@ current time in 12-hour am/pm format
|
|
|
|
* \A current time in 24-hour HH:MM format
|
|
|
|
* \t current time in 24-hour HH:MM:SS format
|
|
|
|
* (all of the above work as \A)
|
2013-03-28 17:50:12 +05:30
|
|
|
* Not supported:
|
2013-03-29 17:39:05 +05:30
|
|
|
* \! history number of this command
|
2013-03-28 17:50:12 +05:30
|
|
|
* \# command number of this command
|
|
|
|
* \j number of jobs currently managed by the shell
|
|
|
|
* \l basename of the shell's terminal device name
|
|
|
|
* \s name of the shell, the basename of $0 (the portion following the final slash)
|
|
|
|
* \V release of bash, version + patch level (e.g., 2.00.0)
|
|
|
|
* \d date in "Weekday Month Date" format (e.g., "Tue May 26")
|
|
|
|
* \D{format}
|
|
|
|
* format is passed to strftime(3).
|
|
|
|
* An empty format results in a locale-specific time representation.
|
|
|
|
* The braces are required.
|
|
|
|
* Mishandled by bb_process_escape_sequence:
|
|
|
|
* \v version of bash (e.g., 2.00)
|
|
|
|
*/
|
2013-03-29 17:39:05 +05:30
|
|
|
cp = prmt_ptr;
|
|
|
|
c = *cp;
|
|
|
|
if (c != 't') /* don't treat \t as tab */
|
|
|
|
c = bb_process_escape_sequence(&prmt_ptr);
|
2007-01-22 00:48:19 +05:30
|
|
|
if (prmt_ptr == cp) {
|
2007-11-10 07:05:47 +05:30
|
|
|
if (*cp == '\0')
|
2007-01-22 00:48:19 +05:30
|
|
|
break;
|
|
|
|
c = *prmt_ptr++;
|
2007-12-03 16:15:14 +05:30
|
|
|
|
2007-01-22 00:48:19 +05:30
|
|
|
switch (c) {
|
|
|
|
case 'u':
|
2021-10-12 02:38:31 +05:30
|
|
|
pbuf = (char*)get_username_str();
|
2007-01-22 00:48:19 +05:30
|
|
|
break;
|
2013-03-28 17:50:12 +05:30
|
|
|
case 'H':
|
2007-01-22 00:48:19 +05:30
|
|
|
case 'h':
|
2008-02-26 04:53:58 +05:30
|
|
|
pbuf = free_me = safe_gethostname();
|
2013-03-28 17:50:12 +05:30
|
|
|
if (c == 'h')
|
|
|
|
strchrnul(pbuf, '.')[0] = '\0';
|
2007-01-22 00:48:19 +05:30
|
|
|
break;
|
|
|
|
case '$':
|
2007-01-22 00:49:46 +05:30
|
|
|
c = (geteuid() == 0 ? '#' : '$');
|
2007-01-22 00:48:19 +05:30
|
|
|
break;
|
2013-03-29 17:39:05 +05:30
|
|
|
case 'T': /* 12-hour HH:MM:SS format */
|
|
|
|
case '@': /* 12-hour am/pm format */
|
|
|
|
case 'A': /* 24-hour HH:MM format */
|
|
|
|
case 't': /* 24-hour HH:MM:SS format */
|
|
|
|
/* We show all of them as 24-hour HH:MM */
|
|
|
|
strftime_HHMMSS(timebuf, sizeof(timebuf), NULL)[-3] = '\0';
|
|
|
|
pbuf = timebuf;
|
|
|
|
break;
|
|
|
|
case 'w': /* current dir */
|
|
|
|
case 'W': /* basename of cur dir */
|
|
|
|
if (!cwd_buf) {
|
2021-10-12 02:38:31 +05:30
|
|
|
const char *home;
|
2022-06-28 18:10:49 +05:30
|
|
|
#if ENABLE_SHELL_ASH
|
|
|
|
cwd_buf = state->sh_get_var
|
|
|
|
? xstrdup(state->sh_get_var("PWD"))
|
|
|
|
: xrealloc_getcwd_or_warn(NULL);
|
|
|
|
#else
|
2013-03-29 17:39:05 +05:30
|
|
|
cwd_buf = xrealloc_getcwd_or_warn(NULL);
|
2022-06-28 18:10:49 +05:30
|
|
|
#endif
|
2013-03-29 17:39:05 +05:30
|
|
|
if (!cwd_buf)
|
|
|
|
cwd_buf = (char *)bb_msg_unknown;
|
2021-10-12 02:38:31 +05:30
|
|
|
else if ((home = get_homedir_or_NULL()) != NULL && home[0]) {
|
2015-03-12 22:18:34 +05:30
|
|
|
char *after_home_user;
|
|
|
|
|
2013-03-29 17:39:05 +05:30
|
|
|
/* /home/user[/something] -> ~[/something] */
|
2021-10-12 02:38:31 +05:30
|
|
|
after_home_user = is_prefixed_with(cwd_buf, home);
|
2015-03-12 22:18:34 +05:30
|
|
|
if (after_home_user
|
|
|
|
&& (*after_home_user == '/' || *after_home_user == '\0')
|
2013-03-29 17:39:05 +05:30
|
|
|
) {
|
|
|
|
cwd_buf[0] = '~';
|
2015-03-12 22:18:34 +05:30
|
|
|
overlapping_strcpy(cwd_buf + 1, after_home_user);
|
2013-03-29 17:39:05 +05:30
|
|
|
}
|
|
|
|
}
|
2007-01-22 00:48:19 +05:30
|
|
|
}
|
2007-12-03 16:15:14 +05:30
|
|
|
pbuf = cwd_buf;
|
2013-03-29 17:39:05 +05:30
|
|
|
if (c == 'w')
|
|
|
|
break;
|
2007-06-30 13:34:05 +05:30
|
|
|
cp = strrchr(pbuf, '/');
|
2013-03-29 17:51:53 +05:30
|
|
|
if (cp)
|
2013-03-29 17:39:05 +05:30
|
|
|
pbuf = (char*)cp + 1;
|
2007-01-22 00:48:19 +05:30
|
|
|
break;
|
2013-03-28 17:50:12 +05:30
|
|
|
// bb_process_escape_sequence does this now:
|
|
|
|
// case 'e': case 'E': /* \e \E = \033 */
|
|
|
|
// c = '\033';
|
|
|
|
// break;
|
2007-12-03 16:15:14 +05:30
|
|
|
case 'x': case 'X': {
|
|
|
|
char buf2[4];
|
2007-01-22 00:48:19 +05:30
|
|
|
for (l = 0; l < 3;) {
|
2007-12-03 16:15:14 +05:30
|
|
|
unsigned h;
|
2007-01-22 00:48:19 +05:30
|
|
|
buf2[l++] = *prmt_ptr;
|
2007-12-03 16:15:14 +05:30
|
|
|
buf2[l] = '\0';
|
|
|
|
h = strtoul(buf2, &pbuf, 16);
|
2007-01-22 00:48:19 +05:30
|
|
|
if (h > UCHAR_MAX || (pbuf - buf2) < l) {
|
2007-12-03 16:15:14 +05:30
|
|
|
buf2[--l] = '\0';
|
2007-01-22 00:48:19 +05:30
|
|
|
break;
|
|
|
|
}
|
|
|
|
prmt_ptr++;
|
|
|
|
}
|
2007-12-03 16:15:14 +05:30
|
|
|
c = (char)strtoul(buf2, NULL, 16);
|
2007-01-22 00:48:19 +05:30
|
|
|
if (c == 0)
|
|
|
|
c = '?';
|
2007-12-03 16:15:14 +05:30
|
|
|
pbuf = cbuf;
|
2007-01-22 00:48:19 +05:30
|
|
|
break;
|
2007-12-03 16:15:14 +05:30
|
|
|
}
|
2007-01-22 00:48:19 +05:30
|
|
|
case '[': case ']':
|
|
|
|
if (c == flg_not_length) {
|
2013-08-19 20:14:05 +05:30
|
|
|
/* Toggle '['/']' hex 5b/5d */
|
|
|
|
flg_not_length ^= 6;
|
2007-01-22 00:48:19 +05:30
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
2007-12-03 16:15:14 +05:30
|
|
|
} /* switch */
|
|
|
|
} /* if */
|
|
|
|
} /* if */
|
|
|
|
cbuf[0] = c;
|
2013-08-19 20:15:04 +05:30
|
|
|
{
|
|
|
|
int n = strlen(pbuf);
|
|
|
|
prmt_size += n;
|
|
|
|
if (c == '\n')
|
|
|
|
cmdedit_prmt_len = 0;
|
|
|
|
else if (flg_not_length != ']') {
|
2021-09-25 23:06:35 +05:30
|
|
|
# if ENABLE_UNICODE_SUPPORT
|
2020-11-21 16:56:39 +05:30
|
|
|
if (n == 1) {
|
|
|
|
/* Only count single-byte characters and the first of multi-byte characters */
|
|
|
|
if ((unsigned char)*pbuf < 0x80 /* single byte character */
|
|
|
|
|| (unsigned char)*pbuf >= 0xc0 /* first of multi-byte characters */
|
|
|
|
) {
|
|
|
|
cmdedit_prmt_len += n;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cmdedit_prmt_len += unicode_strwidth(pbuf);
|
|
|
|
}
|
2021-09-25 23:06:35 +05:30
|
|
|
# else
|
2013-08-19 20:15:04 +05:30
|
|
|
cmdedit_prmt_len += n;
|
2021-09-25 23:06:35 +05:30
|
|
|
# endif
|
2013-08-19 20:15:04 +05:30
|
|
|
}
|
2013-08-19 20:14:05 +05:30
|
|
|
}
|
2013-08-19 20:15:04 +05:30
|
|
|
prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_size+1), pbuf);
|
2007-12-03 16:15:14 +05:30
|
|
|
free(free_me);
|
|
|
|
} /* while */
|
|
|
|
|
|
|
|
if (cwd_buf != (char *)bb_msg_unknown)
|
|
|
|
free(cwd_buf);
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
/* see comment (above this function) about multiline prompt redrawing */
|
|
|
|
cmdedit_prompt = prompt_last_line = prmt_mem_ptr;
|
|
|
|
prmt_ptr = strrchr(cmdedit_prompt, '\n');
|
|
|
|
if (prmt_ptr)
|
|
|
|
prompt_last_line = prmt_ptr + 1;
|
2007-01-22 00:48:19 +05:30
|
|
|
put_prompt();
|
|
|
|
}
|
2020-12-16 15:44:08 +05:30
|
|
|
#endif /* FEATURE_EDITING_FANCY_PROMPT */
|
2005-08-05 00:34:46 +05:30
|
|
|
|
2018-02-26 00:39:54 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_WINCH
|
2016-11-28 05:40:16 +05:30
|
|
|
static void cmdedit_setwidth(void)
|
2007-01-22 00:49:46 +05:30
|
|
|
{
|
2016-11-28 05:40:16 +05:30
|
|
|
int new_y;
|
|
|
|
|
|
|
|
cmdedit_termw = get_terminal_width(STDIN_FILENO);
|
|
|
|
/* new y for current cursor */
|
|
|
|
new_y = (cursor + cmdedit_prmt_len) / cmdedit_termw;
|
|
|
|
/* redraw */
|
|
|
|
redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor);
|
2007-01-22 00:49:46 +05:30
|
|
|
}
|
|
|
|
|
2016-11-28 02:55:07 +05:30
|
|
|
static void win_changed(int nsig UNUSED_PARAM)
|
2007-01-22 00:49:46 +05:30
|
|
|
{
|
2016-11-28 02:55:07 +05:30
|
|
|
if (S.ok_to_redraw) {
|
|
|
|
/* We are in read_key(), safe to redraw immediately */
|
|
|
|
int sv_errno = errno;
|
2016-11-28 05:40:16 +05:30
|
|
|
cmdedit_setwidth();
|
|
|
|
fflush_all();
|
2016-11-28 02:55:07 +05:30
|
|
|
errno = sv_errno;
|
|
|
|
} else {
|
|
|
|
/* Signal main loop that redraw is necessary */
|
|
|
|
S.SIGWINCH_count++;
|
|
|
|
}
|
2007-01-22 00:49:46 +05:30
|
|
|
}
|
2018-02-26 00:39:54 +05:30
|
|
|
#endif
|
2007-01-22 00:49:46 +05:30
|
|
|
|
2011-02-08 09:37:02 +05:30
|
|
|
static int lineedit_read_key(char *read_key_buffer, int timeout)
|
2009-05-15 06:57:53 +05:30
|
|
|
{
|
2009-05-17 20:14:54 +05:30
|
|
|
int64_t ic;
|
2010-03-26 18:36:56 +05:30
|
|
|
#if ENABLE_UNICODE_SUPPORT
|
2009-07-10 22:10:49 +05:30
|
|
|
char unicode_buf[MB_CUR_MAX + 1];
|
|
|
|
int unicode_idx = 0;
|
|
|
|
#endif
|
2009-05-17 20:14:54 +05:30
|
|
|
|
2016-11-28 05:40:16 +05:30
|
|
|
fflush_all();
|
2022-01-18 05:01:27 +05:30
|
|
|
for (;;) {
|
2010-03-12 01:47:55 +05:30
|
|
|
/* Wait for input. TIMEOUT = -1 makes read_key wait even
|
|
|
|
* on nonblocking stdin, TIMEOUT = 50 makes sure we won't
|
|
|
|
* insist on full MB_CUR_MAX buffer to declare input like
|
|
|
|
* "\xff\n",pause,"ls\n" invalid and thus won't lose "ls".
|
|
|
|
*
|
2022-01-17 07:32:40 +05:30
|
|
|
* If LI_INTERRUPTIBLE, return -1 if got EINTR in poll()
|
|
|
|
* inside read_key, or if bb_got_signal != 0 (IOW: if signal
|
|
|
|
* arrived before poll() is reached).
|
|
|
|
*
|
2010-03-12 01:47:55 +05:30
|
|
|
* Note: read_key sets errno to 0 on success.
|
|
|
|
*/
|
2022-01-18 05:01:27 +05:30
|
|
|
for (;;) {
|
2022-01-17 07:32:40 +05:30
|
|
|
if ((state->flags & LI_INTERRUPTIBLE) && bb_got_signal) {
|
|
|
|
errno = EINTR;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
//FIXME: still races here with signals, but small window to poll() inside read_key
|
|
|
|
IF_FEATURE_EDITING_WINCH(S.ok_to_redraw = 1;)
|
2022-01-18 05:01:27 +05:30
|
|
|
/* errno = 0; - read_key does this itself */
|
2022-01-17 07:32:40 +05:30
|
|
|
ic = read_key(STDIN_FILENO, read_key_buffer, timeout);
|
|
|
|
IF_FEATURE_EDITING_WINCH(S.ok_to_redraw = 0;)
|
2022-01-18 05:01:27 +05:30
|
|
|
if (errno != EINTR)
|
|
|
|
break;
|
|
|
|
if (state->flags & LI_INTERRUPTIBLE) {
|
|
|
|
/* LI_INTERRUPTIBLE bails out on EINTR,
|
|
|
|
* but nothing really guarantees that bb_got_signal
|
|
|
|
* is nonzero. Follow the least surprise principle:
|
|
|
|
*/
|
|
|
|
if (bb_got_signal == 0)
|
|
|
|
bb_got_signal = 255;
|
|
|
|
goto ret;
|
|
|
|
}
|
|
|
|
}
|
2022-01-17 07:32:40 +05:30
|
|
|
|
2010-03-12 01:47:55 +05:30
|
|
|
if (errno) {
|
2010-03-26 18:36:56 +05:30
|
|
|
#if ENABLE_UNICODE_SUPPORT
|
2010-03-12 01:47:55 +05:30
|
|
|
if (errno == EAGAIN && unicode_idx != 0)
|
|
|
|
goto pushback;
|
2009-10-29 08:15:26 +05:30
|
|
|
#endif
|
2010-03-12 01:47:55 +05:30
|
|
|
break;
|
2009-05-29 14:09:06 +05:30
|
|
|
}
|
2009-10-14 16:23:04 +05:30
|
|
|
|
2009-10-27 15:04:06 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
|
|
|
|
if ((int32_t)ic == KEYCODE_CURSOR_POS
|
2009-10-27 15:17:49 +05:30
|
|
|
&& S.sent_ESC_br6n
|
2009-05-17 20:14:54 +05:30
|
|
|
) {
|
2009-10-27 15:17:49 +05:30
|
|
|
S.sent_ESC_br6n = 0;
|
2009-10-27 15:04:06 +05:30
|
|
|
if (cursor == 0) { /* otherwise it may be bogus */
|
|
|
|
int col = ((ic >> 32) & 0x7fff) - 1;
|
2013-08-19 20:14:05 +05:30
|
|
|
/*
|
|
|
|
* Is col > cmdedit_prmt_len?
|
|
|
|
* If yes (terminal says cursor is farther to the right
|
|
|
|
* of where we think it should be),
|
|
|
|
* the prompt wasn't printed starting at col 1,
|
|
|
|
* there was additional text before it.
|
|
|
|
*/
|
|
|
|
if ((int)(col - cmdedit_prmt_len) > 0) {
|
|
|
|
/* Fix our understanding of current x position */
|
2009-10-27 15:04:06 +05:30
|
|
|
cmdedit_x += (col - cmdedit_prmt_len);
|
|
|
|
while (cmdedit_x >= cmdedit_termw) {
|
|
|
|
cmdedit_x -= cmdedit_termw;
|
|
|
|
cmdedit_y++;
|
|
|
|
}
|
2009-05-17 20:14:54 +05:30
|
|
|
}
|
|
|
|
}
|
2010-03-12 01:47:55 +05:30
|
|
|
continue;
|
2009-05-17 20:14:54 +05:30
|
|
|
}
|
2009-10-27 15:04:06 +05:30
|
|
|
#endif
|
2009-07-10 22:10:49 +05:30
|
|
|
|
2010-03-26 18:36:56 +05:30
|
|
|
#if ENABLE_UNICODE_SUPPORT
|
2010-03-09 18:39:24 +05:30
|
|
|
if (unicode_status == UNICODE_ON) {
|
2009-07-10 22:10:49 +05:30
|
|
|
wchar_t wc;
|
|
|
|
|
|
|
|
if ((int32_t)ic < 0) /* KEYCODE_xxx */
|
2010-03-12 01:47:55 +05:30
|
|
|
break;
|
|
|
|
// TODO: imagine sequence like: 0xff,<left-arrow>: we are currently losing 0xff...
|
2010-03-09 18:39:24 +05:30
|
|
|
|
2009-07-10 22:10:49 +05:30
|
|
|
unicode_buf[unicode_idx++] = ic;
|
|
|
|
unicode_buf[unicode_idx] = '\0';
|
2010-03-09 18:39:24 +05:30
|
|
|
if (mbstowcs(&wc, unicode_buf, 1) != 1) {
|
|
|
|
/* Not (yet?) a valid unicode char */
|
|
|
|
if (unicode_idx < MB_CUR_MAX) {
|
2010-03-12 01:47:55 +05:30
|
|
|
timeout = 50;
|
|
|
|
continue;
|
2010-03-09 18:39:24 +05:30
|
|
|
}
|
2010-03-12 01:47:55 +05:30
|
|
|
pushback:
|
2010-03-09 18:39:24 +05:30
|
|
|
/* Invalid sequence. Save all "bad bytes" except first */
|
2010-03-12 01:47:55 +05:30
|
|
|
read_key_ungets(read_key_buffer, unicode_buf + 1, unicode_idx - 1);
|
2010-04-29 17:13:39 +05:30
|
|
|
# if !ENABLE_UNICODE_PRESERVE_BROKEN
|
2010-03-09 18:39:24 +05:30
|
|
|
ic = CONFIG_SUBST_WCHAR;
|
2010-04-29 17:13:39 +05:30
|
|
|
# else
|
2010-05-17 00:16:53 +05:30
|
|
|
ic = unicode_mark_raw_byte(unicode_buf[0]);
|
2010-04-29 17:13:39 +05:30
|
|
|
# endif
|
2010-03-09 18:39:24 +05:30
|
|
|
} else {
|
|
|
|
/* Valid unicode char, return its code */
|
|
|
|
ic = wc;
|
2009-07-10 22:10:49 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2010-03-12 01:47:55 +05:30
|
|
|
break;
|
|
|
|
}
|
2022-01-18 05:01:27 +05:30
|
|
|
ret:
|
2009-05-15 06:57:53 +05:30
|
|
|
return ic;
|
|
|
|
}
|
|
|
|
|
2010-03-18 23:05:37 +05:30
|
|
|
#if ENABLE_UNICODE_BIDI_SUPPORT
|
|
|
|
static int isrtl_str(void)
|
|
|
|
{
|
|
|
|
int idx = cursor;
|
2010-03-26 17:43:24 +05:30
|
|
|
|
|
|
|
while (idx < command_len && unicode_bidi_is_neutral_wchar(command_ps[idx]))
|
2010-03-18 23:05:37 +05:30
|
|
|
idx++;
|
2010-03-26 17:43:24 +05:30
|
|
|
return unicode_bidi_isrtl(command_ps[idx]);
|
2010-03-18 23:05:37 +05:30
|
|
|
}
|
|
|
|
#else
|
|
|
|
# define isrtl_str() 0
|
|
|
|
#endif
|
|
|
|
|
2006-03-08 01:56:11 +05:30
|
|
|
/* leave out the "vi-mode"-only case labels if vi editing isn't
|
|
|
|
* configured. */
|
2009-07-12 04:21:15 +05:30
|
|
|
#define vi_case(caselabel) IF_FEATURE_EDITING_VI(case caselabel)
|
2005-08-05 00:34:46 +05:30
|
|
|
|
|
|
|
/* convert uppercase ascii to equivalent control char, for readability */
|
2007-01-22 00:48:19 +05:30
|
|
|
#undef CTRL
|
|
|
|
#define CTRL(a) ((a) & ~0x40)
|
2005-08-05 00:34:46 +05:30
|
|
|
|
2011-07-11 11:06:59 +05:30
|
|
|
enum {
|
|
|
|
VI_CMDMODE_BIT = 0x40000000,
|
|
|
|
/* 0x80000000 bit flags KEYCODE_xxx */
|
|
|
|
};
|
|
|
|
|
|
|
|
#if ENABLE_FEATURE_REVERSE_SEARCH
|
|
|
|
/* Mimic readline Ctrl-R reverse history search.
|
|
|
|
* When invoked, it shows the following prompt:
|
|
|
|
* (reverse-i-search)'': user_input [cursor pos unchanged by Ctrl-R]
|
|
|
|
* and typing results in search being performed:
|
|
|
|
* (reverse-i-search)'tmp': cd /tmp [cursor under t in /tmp]
|
|
|
|
* Search is performed by looking at progressively older lines in history.
|
|
|
|
* Ctrl-R again searches for the next match in history.
|
|
|
|
* Backspace deletes last matched char.
|
|
|
|
* Control keys exit search and return to normal editing (at current history line).
|
|
|
|
*/
|
2017-08-02 20:57:28 +05:30
|
|
|
static int32_t reverse_i_search(int timeout)
|
2011-07-11 11:06:59 +05:30
|
|
|
{
|
|
|
|
char match_buf[128]; /* for user input */
|
|
|
|
char read_key_buffer[KEYCODE_BUFFER_SIZE];
|
|
|
|
const char *matched_history_line;
|
|
|
|
const char *saved_prompt;
|
2013-08-19 20:14:05 +05:30
|
|
|
unsigned saved_prmt_len;
|
2011-07-11 11:06:59 +05:30
|
|
|
int32_t ic;
|
|
|
|
|
|
|
|
matched_history_line = NULL;
|
|
|
|
read_key_buffer[0] = 0;
|
|
|
|
match_buf[0] = '\0';
|
|
|
|
|
|
|
|
/* Save and replace the prompt */
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
saved_prompt = prompt_last_line;
|
2013-08-19 20:14:05 +05:30
|
|
|
saved_prmt_len = cmdedit_prmt_len;
|
2011-07-11 11:06:59 +05:30
|
|
|
goto set_prompt;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
int h;
|
|
|
|
unsigned match_buf_len = strlen(match_buf);
|
|
|
|
|
2017-08-02 20:57:28 +05:30
|
|
|
//FIXME: correct timeout? (i.e. count it down?)
|
|
|
|
ic = lineedit_read_key(read_key_buffer, timeout);
|
2011-07-11 11:06:59 +05:30
|
|
|
|
|
|
|
switch (ic) {
|
|
|
|
case CTRL('R'): /* searching for the next match */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\b':
|
|
|
|
case '\x7f':
|
|
|
|
/* Backspace */
|
|
|
|
if (unicode_status == UNICODE_ON) {
|
|
|
|
while (match_buf_len != 0) {
|
|
|
|
uint8_t c = match_buf[--match_buf_len];
|
|
|
|
if ((c & 0xc0) != 0x80) /* start of UTF-8 char? */
|
|
|
|
break; /* yes */
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (match_buf_len != 0)
|
|
|
|
match_buf_len--;
|
|
|
|
}
|
|
|
|
match_buf[match_buf_len] = '\0';
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (ic < ' '
|
|
|
|
|| (!ENABLE_UNICODE_SUPPORT && ic >= 256)
|
|
|
|
|| (ENABLE_UNICODE_SUPPORT && ic >= VI_CMDMODE_BIT)
|
|
|
|
) {
|
|
|
|
goto ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Append this char */
|
2021-09-25 23:06:35 +05:30
|
|
|
# if ENABLE_UNICODE_SUPPORT
|
2011-07-11 11:06:59 +05:30
|
|
|
if (unicode_status == UNICODE_ON) {
|
|
|
|
mbstate_t mbstate = { 0 };
|
|
|
|
char buf[MB_CUR_MAX + 1];
|
|
|
|
int len = wcrtomb(buf, ic, &mbstate);
|
|
|
|
if (len > 0) {
|
|
|
|
buf[len] = '\0';
|
|
|
|
if (match_buf_len + len < sizeof(match_buf))
|
|
|
|
strcpy(match_buf + match_buf_len, buf);
|
|
|
|
}
|
|
|
|
} else
|
2021-09-25 23:06:35 +05:30
|
|
|
# endif
|
2011-07-11 11:06:59 +05:30
|
|
|
if (match_buf_len < sizeof(match_buf) - 1) {
|
|
|
|
match_buf[match_buf_len] = ic;
|
|
|
|
match_buf[match_buf_len + 1] = '\0';
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
} /* switch (ic) */
|
|
|
|
|
|
|
|
/* Search in history for match_buf */
|
|
|
|
h = state->cur_history;
|
|
|
|
if (ic == CTRL('R'))
|
|
|
|
h--;
|
|
|
|
while (h >= 0) {
|
|
|
|
if (state->history[h]) {
|
|
|
|
char *match = strstr(state->history[h], match_buf);
|
|
|
|
if (match) {
|
|
|
|
state->cur_history = h;
|
|
|
|
matched_history_line = state->history[h];
|
|
|
|
command_len = load_string(matched_history_line);
|
|
|
|
cursor = match - matched_history_line;
|
|
|
|
//FIXME: cursor position for Unicode case
|
|
|
|
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
free((char*)prompt_last_line);
|
2011-07-11 11:06:59 +05:30
|
|
|
set_prompt:
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
prompt_last_line = xasprintf("(reverse-i-search)'%s': ", match_buf);
|
|
|
|
cmdedit_prmt_len = unicode_strwidth(prompt_last_line);
|
2011-07-11 11:06:59 +05:30
|
|
|
goto do_redraw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
h--;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Not found */
|
|
|
|
match_buf[match_buf_len] = '\0';
|
|
|
|
beep();
|
|
|
|
continue;
|
|
|
|
|
|
|
|
do_redraw:
|
|
|
|
redraw(cmdedit_y, command_len - cursor);
|
|
|
|
} /* while (1) */
|
|
|
|
|
|
|
|
ret:
|
|
|
|
if (matched_history_line)
|
|
|
|
command_len = load_string(matched_history_line);
|
|
|
|
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
free((char*)prompt_last_line);
|
|
|
|
prompt_last_line = saved_prompt;
|
2013-08-19 20:14:05 +05:30
|
|
|
cmdedit_prmt_len = saved_prmt_len;
|
2011-07-11 11:06:59 +05:30
|
|
|
redraw(cmdedit_y, command_len - cursor);
|
|
|
|
|
|
|
|
return ic;
|
|
|
|
}
|
2021-09-25 23:06:35 +05:30
|
|
|
#endif /* ENABLE_FEATURE_REVERSE_SEARCH */
|
2011-07-11 11:06:59 +05:30
|
|
|
|
2018-12-08 20:15:46 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_WINCH
|
2018-12-08 18:19:15 +05:30
|
|
|
static void sigaction2(int sig, struct sigaction *act)
|
|
|
|
{
|
|
|
|
// Grr... gcc 8.1.1:
|
|
|
|
// "passing argument 3 to restrict-qualified parameter aliases with argument 2"
|
|
|
|
// dance around that...
|
|
|
|
struct sigaction *oact FIX_ALIASING;
|
|
|
|
oact = act;
|
|
|
|
sigaction(sig, act, oact);
|
|
|
|
}
|
2018-12-08 20:15:46 +05:30
|
|
|
#endif
|
2018-12-08 18:19:15 +05:30
|
|
|
|
2009-07-16 17:44:34 +05:30
|
|
|
/* maxsize must be >= 2.
|
|
|
|
* Returns:
|
2008-02-03 00:05:55 +05:30
|
|
|
* -1 on read errors or EOF, or on bare Ctrl-D,
|
|
|
|
* 0 on ctrl-C (the line entered is still returned in 'command'),
|
2016-11-25 08:11:03 +05:30
|
|
|
* (in both cases the cursor remains on the input line, '\n' is not printed)
|
2007-09-26 00:05:28 +05:30
|
|
|
* >0 length of input string, including terminating '\n'
|
|
|
|
*/
|
2017-08-02 20:57:28 +05:30
|
|
|
int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize)
|
2000-03-16 13:39:57 +05:30
|
|
|
{
|
2017-09-15 20:44:01 +05:30
|
|
|
int len, n;
|
2017-08-02 20:57:28 +05:30
|
|
|
int timeout;
|
2007-11-10 07:05:47 +05:30
|
|
|
#if ENABLE_FEATURE_TAB_COMPLETION
|
2010-09-03 16:21:36 +05:30
|
|
|
smallint lastWasTab = 0;
|
2007-11-10 07:05:47 +05:30
|
|
|
#endif
|
2007-01-22 00:48:19 +05:30
|
|
|
smallint break_out = 0;
|
2007-01-22 14:33:07 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_VI
|
2007-01-22 00:48:19 +05:30
|
|
|
smallint vi_cmdmode = 0;
|
2005-08-05 00:34:46 +05:30
|
|
|
#endif
|
2007-11-10 07:05:47 +05:30
|
|
|
struct termios initial_settings;
|
|
|
|
struct termios new_settings;
|
2009-05-15 06:57:53 +05:30
|
|
|
char read_key_buffer[KEYCODE_BUFFER_SIZE];
|
2007-01-22 12:51:38 +05:30
|
|
|
|
2007-11-10 07:05:47 +05:30
|
|
|
INIT_S();
|
2020-12-22 03:20:23 +05:30
|
|
|
//command_len = 0; - done by INIT_S()
|
|
|
|
//cmdedit_y = 0; /* quasireal y, not true if line > xt*yt */
|
|
|
|
cmdedit_termw = 80;
|
|
|
|
IF_FEATURE_EDITING_VI(delptr = delbuf;)
|
2007-11-10 07:05:47 +05:30
|
|
|
|
2017-09-15 20:44:01 +05:30
|
|
|
n = get_termios_and_make_raw(STDIN_FILENO, &new_settings, &initial_settings, 0
|
|
|
|
| TERMIOS_CLEAR_ISIG /* turn off INTR (ctrl-C), QUIT, SUSP */
|
|
|
|
);
|
|
|
|
if (n != 0 || (initial_settings.c_lflag & (ECHO|ICANON)) == ICANON) {
|
2014-12-10 21:52:13 +05:30
|
|
|
/* Happens when e.g. stty -echo was run before.
|
|
|
|
* But if ICANON is not set, we don't come here.
|
|
|
|
* (example: interactive python ^Z-backgrounded,
|
|
|
|
* tty is still in "raw mode").
|
|
|
|
*/
|
2007-11-10 07:05:47 +05:30
|
|
|
parse_and_put_prompt(prompt);
|
2016-11-28 05:40:16 +05:30
|
|
|
fflush_all();
|
2007-12-09 15:33:28 +05:30
|
|
|
if (fgets(command, maxsize, stdin) == NULL)
|
|
|
|
len = -1; /* EOF or error */
|
|
|
|
else
|
|
|
|
len = strlen(command);
|
2007-11-10 07:05:47 +05:30
|
|
|
DEINIT_S();
|
|
|
|
return len;
|
2007-10-21 00:00:38 +05:30
|
|
|
}
|
|
|
|
|
2010-01-05 01:19:58 +05:30
|
|
|
init_unicode();
|
2009-07-12 01:06:13 +05:30
|
|
|
|
2007-01-22 12:51:38 +05:30
|
|
|
// FIXME: audit & improve this
|
2007-06-10 20:38:44 +05:30
|
|
|
if (maxsize > MAX_LINELEN)
|
|
|
|
maxsize = MAX_LINELEN;
|
2009-07-12 06:20:35 +05:30
|
|
|
S.maxsize = maxsize;
|
2007-01-22 12:51:38 +05:30
|
|
|
|
2017-08-02 20:57:28 +05:30
|
|
|
timeout = -1;
|
|
|
|
/* Make state->flags == 0 if st is NULL.
|
|
|
|
* With zeroed flags, no other fields are ever referenced.
|
|
|
|
*/
|
|
|
|
state = (line_input_t*) &const_int_0;
|
|
|
|
if (st) {
|
|
|
|
state = st;
|
|
|
|
timeout = st->timeout;
|
|
|
|
}
|
2009-09-27 06:18:53 +05:30
|
|
|
#if MAX_HISTORY > 0
|
2019-02-04 20:46:30 +05:30
|
|
|
if (state->flags & DO_HISTORY) {
|
2009-09-27 06:18:53 +05:30
|
|
|
# if ENABLE_FEATURE_EDITING_SAVEHISTORY
|
2019-02-04 20:46:30 +05:30
|
|
|
if (state->hist_file)
|
|
|
|
if (state->cnt_history == 0)
|
|
|
|
load_history(state);
|
2009-09-27 06:18:53 +05:30
|
|
|
# endif
|
2008-11-02 06:11:05 +05:30
|
|
|
state->cur_history = state->cnt_history;
|
2019-02-04 20:46:30 +05:30
|
|
|
}
|
2009-09-27 06:18:53 +05:30
|
|
|
#endif
|
2007-01-22 12:51:38 +05:30
|
|
|
|
2001-02-17 00:06:04 +05:30
|
|
|
/* prepare before init handlers */
|
2010-03-26 18:36:56 +05:30
|
|
|
#if ENABLE_UNICODE_SUPPORT
|
2009-07-10 22:10:49 +05:30
|
|
|
command_ps = xzalloc(maxsize * sizeof(command_ps[0]));
|
|
|
|
#else
|
2001-01-27 02:12:23 +05:30
|
|
|
command_ps = command;
|
2007-01-22 00:48:59 +05:30
|
|
|
command[0] = '\0';
|
2009-07-10 22:10:49 +05:30
|
|
|
#endif
|
|
|
|
#define command command_must_not_be_used
|
2001-01-27 02:12:23 +05:30
|
|
|
|
2008-11-05 18:50:58 +05:30
|
|
|
tcsetattr_stdin_TCSANOW(&new_settings);
|
2001-01-27 02:12:23 +05:30
|
|
|
|
2008-09-27 06:58:56 +05:30
|
|
|
#if 0
|
2011-03-31 16:46:52 +05:30
|
|
|
for (i = 0; i <= state->max_history; i++)
|
2009-07-10 22:10:49 +05:30
|
|
|
bb_error_msg("history[%d]:'%s'", i, state->history[i]);
|
2008-09-27 06:58:56 +05:30
|
|
|
bb_error_msg("cur_history:%d cnt_history:%d", state->cur_history, state->cnt_history);
|
|
|
|
#endif
|
|
|
|
|
2017-11-08 18:08:12 +05:30
|
|
|
/* Get width (before printing prompt) */
|
|
|
|
cmdedit_termw = get_terminal_width(STDIN_FILENO);
|
2009-11-11 07:49:30 +05:30
|
|
|
/* Print out the command prompt, optionally ask where cursor is */
|
2007-11-10 07:05:47 +05:30
|
|
|
parse_and_put_prompt(prompt);
|
2009-11-11 07:49:30 +05:30
|
|
|
ask_terminal();
|
2001-01-04 16:38:45 +05:30
|
|
|
|
2018-02-26 00:39:54 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_WINCH
|
2011-05-20 07:56:29 +05:30
|
|
|
/* Install window resize handler (NB: after *all* init is complete) */
|
2016-11-28 02:55:07 +05:30
|
|
|
S.SIGWINCH_handler.sa_handler = win_changed;
|
|
|
|
S.SIGWINCH_handler.sa_flags = SA_RESTART;
|
2018-12-08 18:19:15 +05:30
|
|
|
sigaction2(SIGWINCH, &S.SIGWINCH_handler);
|
2018-02-26 00:39:54 +05:30
|
|
|
#endif
|
2009-05-17 22:58:14 +05:30
|
|
|
read_key_buffer[0] = 0;
|
2000-03-19 10:58:55 +05:30
|
|
|
while (1) {
|
2009-07-10 22:10:49 +05:30
|
|
|
/*
|
|
|
|
* The emacs and vi modes share much of the code in the big
|
|
|
|
* command loop. Commands entered when in vi's command mode
|
|
|
|
* (aka "escape mode") get an extra bit added to distinguish
|
|
|
|
* them - this keeps them from being self-inserted. This
|
|
|
|
* clutters the big switch a bit, but keeps all the code
|
|
|
|
* in one place.
|
|
|
|
*/
|
2009-10-14 16:23:04 +05:30
|
|
|
int32_t ic, ic_raw;
|
2018-02-26 00:39:54 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_WINCH
|
2016-11-28 02:55:07 +05:30
|
|
|
unsigned count;
|
|
|
|
|
|
|
|
count = S.SIGWINCH_count;
|
|
|
|
if (S.SIGWINCH_saved != count) {
|
|
|
|
S.SIGWINCH_saved = count;
|
2016-11-28 05:40:16 +05:30
|
|
|
cmdedit_setwidth();
|
2016-11-28 02:55:07 +05:30
|
|
|
}
|
2018-02-26 00:39:54 +05:30
|
|
|
#endif
|
2011-02-08 09:37:02 +05:30
|
|
|
ic = ic_raw = lineedit_read_key(read_key_buffer, timeout);
|
2006-03-08 01:56:11 +05:30
|
|
|
|
2011-07-11 11:06:59 +05:30
|
|
|
#if ENABLE_FEATURE_REVERSE_SEARCH
|
|
|
|
again:
|
|
|
|
#endif
|
2007-01-22 14:33:07 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_VI
|
2005-08-05 00:34:46 +05:30
|
|
|
newdelflag = 1;
|
2009-05-15 06:57:53 +05:30
|
|
|
if (vi_cmdmode) {
|
|
|
|
/* btw, since KEYCODE_xxx are all < 0, this doesn't
|
|
|
|
* change ic if it contains one of them: */
|
|
|
|
ic |= VI_CMDMODE_BIT;
|
|
|
|
}
|
2005-08-05 00:34:46 +05:30
|
|
|
#endif
|
2009-05-15 06:57:53 +05:30
|
|
|
|
2006-12-22 03:57:10 +05:30
|
|
|
switch (ic) {
|
2000-04-12 23:19:52 +05:30
|
|
|
case '\n':
|
|
|
|
case '\r':
|
2009-05-15 06:57:53 +05:30
|
|
|
vi_case('\n'|VI_CMDMODE_BIT:)
|
|
|
|
vi_case('\r'|VI_CMDMODE_BIT:)
|
2000-04-12 23:19:52 +05:30
|
|
|
/* Enter */
|
2001-02-17 00:06:04 +05:30
|
|
|
goto_new_line();
|
2000-04-12 23:19:52 +05:30
|
|
|
break_out = 1;
|
|
|
|
break;
|
2007-01-22 00:48:19 +05:30
|
|
|
case CTRL('A'):
|
2009-05-15 06:57:53 +05:30
|
|
|
vi_case('0'|VI_CMDMODE_BIT:)
|
2000-03-19 10:58:55 +05:30
|
|
|
/* Control-a -- Beginning of line */
|
2001-02-17 00:06:04 +05:30
|
|
|
input_backward(cursor);
|
2001-01-27 02:12:23 +05:30
|
|
|
break;
|
2007-01-22 00:48:19 +05:30
|
|
|
case CTRL('B'):
|
2009-05-15 06:57:53 +05:30
|
|
|
vi_case('h'|VI_CMDMODE_BIT:)
|
2010-03-18 23:05:37 +05:30
|
|
|
vi_case('\b'|VI_CMDMODE_BIT:) /* ^H */
|
2009-05-15 06:57:53 +05:30
|
|
|
vi_case('\x7f'|VI_CMDMODE_BIT:) /* DEL */
|
2010-03-18 23:05:37 +05:30
|
|
|
input_backward(1); /* Move back one character */
|
2000-04-21 06:56:49 +05:30
|
|
|
break;
|
2007-01-22 00:48:19 +05:30
|
|
|
case CTRL('E'):
|
2009-05-15 06:57:53 +05:30
|
|
|
vi_case('$'|VI_CMDMODE_BIT:)
|
2000-04-12 23:19:52 +05:30
|
|
|
/* Control-e -- End of line */
|
2010-05-11 18:19:13 +05:30
|
|
|
put_till_end_and_adv_cursor();
|
2000-04-12 23:19:52 +05:30
|
|
|
break;
|
2007-01-22 00:48:19 +05:30
|
|
|
case CTRL('F'):
|
2009-05-15 06:57:53 +05:30
|
|
|
vi_case('l'|VI_CMDMODE_BIT:)
|
|
|
|
vi_case(' '|VI_CMDMODE_BIT:)
|
2010-03-18 23:05:37 +05:30
|
|
|
input_forward(); /* Move forward one character */
|
2000-03-19 10:58:55 +05:30
|
|
|
break;
|
2010-03-18 23:05:37 +05:30
|
|
|
case '\b': /* ^H */
|
2007-01-22 00:48:19 +05:30
|
|
|
case '\x7f': /* DEL */
|
2010-03-18 23:05:37 +05:30
|
|
|
if (!isrtl_str())
|
|
|
|
input_backspace();
|
|
|
|
else
|
|
|
|
input_delete(0);
|
|
|
|
break;
|
|
|
|
case KEYCODE_DELETE:
|
|
|
|
if (!isrtl_str())
|
|
|
|
input_delete(0);
|
|
|
|
else
|
|
|
|
input_backspace();
|
2000-04-12 23:19:52 +05:30
|
|
|
break;
|
2007-11-10 07:05:47 +05:30
|
|
|
#if ENABLE_FEATURE_TAB_COMPLETION
|
2000-04-12 23:19:52 +05:30
|
|
|
case '\t':
|
2001-02-17 00:06:04 +05:30
|
|
|
input_tab(&lastWasTab);
|
2000-03-19 10:58:55 +05:30
|
|
|
break;
|
2007-11-10 07:05:47 +05:30
|
|
|
#endif
|
2007-01-22 00:48:19 +05:30
|
|
|
case CTRL('K'):
|
2003-07-28 15:26:35 +05:30
|
|
|
/* Control-k -- clear to end of line */
|
2009-07-10 22:10:49 +05:30
|
|
|
command_ps[cursor] = BB_NUL;
|
2007-01-22 12:51:38 +05:30
|
|
|
command_len = cursor;
|
2010-05-11 18:19:13 +05:30
|
|
|
printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
|
2002-04-13 18:56:49 +05:30
|
|
|
break;
|
2007-01-22 00:48:19 +05:30
|
|
|
case CTRL('L'):
|
2009-05-15 06:57:53 +05:30
|
|
|
vi_case(CTRL('L')|VI_CMDMODE_BIT:)
|
2003-12-24 01:54:51 +05:30
|
|
|
/* Control-l -- clear screen */
|
lineedit: improve multiline PS1 - redraw using last PS1 line. Closes 10381
This patch only affects prompts with newlines.
We redraw the prompt [+ input] occasionally, e.g. during tab completion,
history browsing or search, etc, and we expect it to align with prior
redraws, such that the visible effect is that only the input changes.
With multi-line PS1, redraw always printed the prompt some lines below
the old one, which resulted in terminal scroll during every redraw.
Now we only redraw the last PS1 line, so vertical alignment is easier to
manage (we already calculated it using only the last line, but re-drew
all lines - that was the culprit), which fixes those extra scrolls.
Notes:
- We now use the full prompt for the initial draw, after clear-screen (^L),
and after tab-completion choices are displayed. Everything else now
redraws using the last/sole prompt line.
- During terminal resize we now only redraw the last[/sole] prompt line,
which is arguably better because it's hard to do right (and we never did).
- Good side effect for reverse-i-search: its prompt now replaces only the
last line of the original prompt - like other shells do.
function old new delta
put_prompt_custom - 66 +66
draw_custom - 66 +66
parse_and_put_prompt 766 806 +40
read_line_input 3867 3884 +17
input_tab 1069 1076 +7
cmdedit_setwidth 61 63 +2
redraw 59 47 -12
put_prompt 46 - -46
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 4/1 up/down: 198/-58) Total: 140 bytes
Signed-off-by: Avi Halachmi <avihpit@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-10-12 20:08:35 +05:30
|
|
|
/* cursor to top,left; clear to the end of screen */
|
|
|
|
printf(ESC"[H" ESC"[J");
|
|
|
|
draw_full(command_len - cursor);
|
2001-12-21 16:50:15 +05:30
|
|
|
break;
|
2006-11-03 03:39:37 +05:30
|
|
|
#if MAX_HISTORY > 0
|
2007-01-22 00:48:19 +05:30
|
|
|
case CTRL('N'):
|
2009-05-15 06:57:53 +05:30
|
|
|
vi_case(CTRL('N')|VI_CMDMODE_BIT:)
|
|
|
|
vi_case('j'|VI_CMDMODE_BIT:)
|
2000-04-12 23:19:52 +05:30
|
|
|
/* Control-n -- Get next command in history */
|
2002-11-27 14:59:49 +05:30
|
|
|
if (get_next_history())
|
2000-04-12 23:19:52 +05:30
|
|
|
goto rewrite_line;
|
2000-03-19 10:58:55 +05:30
|
|
|
break;
|
2007-01-22 00:48:19 +05:30
|
|
|
case CTRL('P'):
|
2009-05-15 06:57:53 +05:30
|
|
|
vi_case(CTRL('P')|VI_CMDMODE_BIT:)
|
|
|
|
vi_case('k'|VI_CMDMODE_BIT:)
|
2000-04-12 23:19:52 +05:30
|
|
|
/* Control-p -- Get previous command from history */
|
2008-09-27 06:58:56 +05:30
|
|
|
if (get_previous_history())
|
2000-04-12 23:19:52 +05:30
|
|
|
goto rewrite_line;
|
2000-03-19 10:58:55 +05:30
|
|
|
break;
|
2002-11-27 14:59:49 +05:30
|
|
|
#endif
|
2007-01-22 00:48:19 +05:30
|
|
|
case CTRL('U'):
|
2009-05-15 06:57:53 +05:30
|
|
|
vi_case(CTRL('U')|VI_CMDMODE_BIT:)
|
2001-02-17 00:06:04 +05:30
|
|
|
/* Control-U -- Clear line before cursor */
|
|
|
|
if (cursor) {
|
2007-01-22 12:51:38 +05:30
|
|
|
command_len -= cursor;
|
2009-07-10 22:10:49 +05:30
|
|
|
memmove(command_ps, command_ps + cursor,
|
|
|
|
(command_len + 1) * sizeof(command_ps[0]));
|
2007-01-22 12:51:38 +05:30
|
|
|
redraw(cmdedit_y, command_len);
|
2001-02-17 00:06:04 +05:30
|
|
|
}
|
|
|
|
break;
|
2007-01-22 00:48:19 +05:30
|
|
|
case CTRL('W'):
|
2009-05-15 06:57:53 +05:30
|
|
|
vi_case(CTRL('W')|VI_CMDMODE_BIT:)
|
2003-12-24 01:54:51 +05:30
|
|
|
/* Control-W -- Remove the last word */
|
2009-07-10 22:10:49 +05:30
|
|
|
while (cursor > 0 && BB_isspace(command_ps[cursor-1]))
|
2003-12-24 01:54:51 +05:30
|
|
|
input_backspace();
|
2009-07-10 22:10:49 +05:30
|
|
|
while (cursor > 0 && !BB_isspace(command_ps[cursor-1]))
|
2003-12-24 01:54:51 +05:30
|
|
|
input_backspace();
|
|
|
|
break;
|
2016-11-24 19:34:00 +05:30
|
|
|
case KEYCODE_ALT_D: {
|
|
|
|
/* Delete word forward */
|
|
|
|
int nc, sc = cursor;
|
|
|
|
ctrl_right();
|
|
|
|
nc = cursor - sc;
|
|
|
|
input_backward(nc);
|
|
|
|
while (--nc >= 0)
|
|
|
|
input_delete(1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case KEYCODE_ALT_BACKSPACE: {
|
|
|
|
/* Delete word backward */
|
|
|
|
int sc = cursor;
|
|
|
|
ctrl_left();
|
|
|
|
while (sc-- > cursor)
|
|
|
|
input_delete(1);
|
|
|
|
break;
|
|
|
|
}
|
2011-07-11 11:06:59 +05:30
|
|
|
#if ENABLE_FEATURE_REVERSE_SEARCH
|
|
|
|
case CTRL('R'):
|
2017-08-02 20:57:28 +05:30
|
|
|
ic = ic_raw = reverse_i_search(timeout);
|
2011-07-11 11:06:59 +05:30
|
|
|
goto again;
|
|
|
|
#endif
|
2007-01-22 00:51:21 +05:30
|
|
|
|
2007-01-22 14:33:07 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_VI
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'i'|VI_CMDMODE_BIT:
|
2005-08-05 00:34:46 +05:30
|
|
|
vi_cmdmode = 0;
|
|
|
|
break;
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'I'|VI_CMDMODE_BIT:
|
2005-08-05 00:34:46 +05:30
|
|
|
input_backward(cursor);
|
|
|
|
vi_cmdmode = 0;
|
|
|
|
break;
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'a'|VI_CMDMODE_BIT:
|
2005-08-05 00:34:46 +05:30
|
|
|
input_forward();
|
|
|
|
vi_cmdmode = 0;
|
|
|
|
break;
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'A'|VI_CMDMODE_BIT:
|
2010-05-11 18:19:13 +05:30
|
|
|
put_till_end_and_adv_cursor();
|
2005-08-05 00:34:46 +05:30
|
|
|
vi_cmdmode = 0;
|
|
|
|
break;
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'x'|VI_CMDMODE_BIT:
|
2005-08-05 00:34:46 +05:30
|
|
|
input_delete(1);
|
|
|
|
break;
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'X'|VI_CMDMODE_BIT:
|
2005-08-05 00:34:46 +05:30
|
|
|
if (cursor > 0) {
|
|
|
|
input_backward(1);
|
|
|
|
input_delete(1);
|
|
|
|
}
|
|
|
|
break;
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'W'|VI_CMDMODE_BIT:
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_Word_motion(1);
|
2005-08-05 00:34:46 +05:30
|
|
|
break;
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'w'|VI_CMDMODE_BIT:
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_word_motion(1);
|
2005-08-05 00:34:46 +05:30
|
|
|
break;
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'E'|VI_CMDMODE_BIT:
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_End_motion();
|
2005-08-05 00:34:46 +05:30
|
|
|
break;
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'e'|VI_CMDMODE_BIT:
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_end_motion();
|
2005-08-05 00:34:46 +05:30
|
|
|
break;
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'B'|VI_CMDMODE_BIT:
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_Back_motion();
|
2005-08-05 00:34:46 +05:30
|
|
|
break;
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'b'|VI_CMDMODE_BIT:
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_back_motion();
|
2005-08-05 00:34:46 +05:30
|
|
|
break;
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'C'|VI_CMDMODE_BIT:
|
2005-08-05 00:34:46 +05:30
|
|
|
vi_cmdmode = 0;
|
|
|
|
/* fall through */
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'D'|VI_CMDMODE_BIT:
|
2005-08-05 00:34:46 +05:30
|
|
|
goto clear_to_eol;
|
|
|
|
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'c'|VI_CMDMODE_BIT:
|
2005-08-05 00:34:46 +05:30
|
|
|
vi_cmdmode = 0;
|
|
|
|
/* fall through */
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'd'|VI_CMDMODE_BIT: {
|
2006-12-22 03:57:10 +05:30
|
|
|
int nc, sc;
|
2009-05-15 06:57:53 +05:30
|
|
|
|
2011-02-08 09:37:02 +05:30
|
|
|
ic = lineedit_read_key(read_key_buffer, timeout);
|
2009-05-15 06:57:53 +05:30
|
|
|
if (errno) /* error */
|
2010-09-07 22:10:53 +05:30
|
|
|
goto return_error_indicator;
|
2009-10-14 16:23:04 +05:30
|
|
|
if (ic == ic_raw) { /* "cc", "dd" */
|
2006-12-22 03:57:10 +05:30
|
|
|
input_backward(cursor);
|
|
|
|
goto clear_to_eol;
|
|
|
|
break;
|
|
|
|
}
|
2009-10-14 16:23:04 +05:30
|
|
|
|
|
|
|
sc = cursor;
|
2009-05-15 06:57:53 +05:30
|
|
|
switch (ic) {
|
2006-12-22 03:57:10 +05:30
|
|
|
case 'w':
|
|
|
|
case 'W':
|
|
|
|
case 'e':
|
|
|
|
case 'E':
|
2009-05-15 06:57:53 +05:30
|
|
|
switch (ic) {
|
2006-12-22 03:57:10 +05:30
|
|
|
case 'w': /* "dw", "cw" */
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_word_motion(vi_cmdmode);
|
2006-10-04 01:26:34 +05:30
|
|
|
break;
|
2006-12-22 03:57:10 +05:30
|
|
|
case 'W': /* 'dW', 'cW' */
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_Word_motion(vi_cmdmode);
|
2006-10-04 01:26:34 +05:30
|
|
|
break;
|
2006-12-22 03:57:10 +05:30
|
|
|
case 'e': /* 'de', 'ce' */
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_end_motion();
|
2006-12-22 03:57:10 +05:30
|
|
|
input_forward();
|
2006-10-04 01:26:34 +05:30
|
|
|
break;
|
2006-12-22 03:57:10 +05:30
|
|
|
case 'E': /* 'dE', 'cE' */
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_End_motion();
|
2006-12-22 03:57:10 +05:30
|
|
|
input_forward();
|
2006-10-04 01:26:34 +05:30
|
|
|
break;
|
|
|
|
}
|
2006-12-22 03:57:10 +05:30
|
|
|
nc = cursor;
|
|
|
|
input_backward(cursor - sc);
|
|
|
|
while (nc-- > cursor)
|
|
|
|
input_delete(1);
|
|
|
|
break;
|
|
|
|
case 'b': /* "db", "cb" */
|
|
|
|
case 'B': /* implemented as B */
|
2009-05-15 06:57:53 +05:30
|
|
|
if (ic == 'b')
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_back_motion();
|
2006-12-22 03:57:10 +05:30
|
|
|
else
|
2009-07-10 22:10:49 +05:30
|
|
|
vi_Back_motion();
|
2006-12-22 03:57:10 +05:30
|
|
|
while (sc-- > cursor)
|
|
|
|
input_delete(1);
|
|
|
|
break;
|
|
|
|
case ' ': /* "d ", "c " */
|
|
|
|
input_delete(1);
|
|
|
|
break;
|
|
|
|
case '$': /* "d$", "c$" */
|
2009-05-15 06:57:53 +05:30
|
|
|
clear_to_eol:
|
2007-01-22 12:51:38 +05:30
|
|
|
while (cursor < command_len)
|
2006-12-22 03:57:10 +05:30
|
|
|
input_delete(1);
|
|
|
|
break;
|
2005-08-05 00:34:46 +05:30
|
|
|
}
|
|
|
|
break;
|
2006-12-22 03:57:10 +05:30
|
|
|
}
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'p'|VI_CMDMODE_BIT:
|
2005-08-05 00:34:46 +05:30
|
|
|
input_forward();
|
|
|
|
/* fallthrough */
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'P'|VI_CMDMODE_BIT:
|
2005-08-05 00:34:46 +05:30
|
|
|
put();
|
|
|
|
break;
|
2009-05-15 06:57:53 +05:30
|
|
|
case 'r'|VI_CMDMODE_BIT:
|
2009-10-14 16:23:04 +05:30
|
|
|
//FIXME: unicode case?
|
2011-02-08 09:37:02 +05:30
|
|
|
ic = lineedit_read_key(read_key_buffer, timeout);
|
2009-05-15 06:57:53 +05:30
|
|
|
if (errno) /* error */
|
2010-09-07 22:10:53 +05:30
|
|
|
goto return_error_indicator;
|
2009-05-15 06:57:53 +05:30
|
|
|
if (ic < ' ' || ic > 255) {
|
2005-08-05 00:34:46 +05:30
|
|
|
beep();
|
2009-05-15 06:57:53 +05:30
|
|
|
} else {
|
2009-07-10 22:10:49 +05:30
|
|
|
command_ps[cursor] = ic;
|
2009-05-15 06:57:53 +05:30
|
|
|
bb_putchar(ic);
|
2007-09-27 15:50:47 +05:30
|
|
|
bb_putchar('\b');
|
2005-08-05 00:34:46 +05:30
|
|
|
}
|
|
|
|
break;
|
2007-01-22 00:48:19 +05:30
|
|
|
case '\x1b': /* ESC */
|
2007-01-22 12:51:38 +05:30
|
|
|
if (state->flags & VI_MODE) {
|
2009-05-15 06:57:53 +05:30
|
|
|
/* insert mode --> command mode */
|
2005-08-05 00:34:46 +05:30
|
|
|
vi_cmdmode = 1;
|
|
|
|
input_backward(1);
|
2004-01-22 18:12:23 +05:30
|
|
|
}
|
2009-05-15 06:57:53 +05:30
|
|
|
break;
|
|
|
|
#endif /* FEATURE_COMMAND_EDITING_VI */
|
2007-01-22 00:51:21 +05:30
|
|
|
|
2006-11-03 03:39:37 +05:30
|
|
|
#if MAX_HISTORY > 0
|
2009-05-15 06:57:53 +05:30
|
|
|
case KEYCODE_UP:
|
|
|
|
if (get_previous_history())
|
|
|
|
goto rewrite_line;
|
|
|
|
beep();
|
|
|
|
break;
|
|
|
|
case KEYCODE_DOWN:
|
|
|
|
if (!get_next_history())
|
2001-02-17 00:06:04 +05:30
|
|
|
break;
|
2007-01-22 00:48:19 +05:30
|
|
|
rewrite_line:
|
2009-05-15 06:57:53 +05:30
|
|
|
/* Rewrite the line with the selected history item */
|
|
|
|
/* change command */
|
2009-09-06 06:06:23 +05:30
|
|
|
command_len = load_string(state->history[state->cur_history] ?
|
2011-07-11 11:06:59 +05:30
|
|
|
state->history[state->cur_history] : "");
|
2009-05-15 06:57:53 +05:30
|
|
|
/* redraw and go to eol (bol, in vi) */
|
|
|
|
redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
|
|
|
|
break;
|
2002-11-27 14:59:49 +05:30
|
|
|
#endif
|
2009-05-15 06:57:53 +05:30
|
|
|
case KEYCODE_RIGHT:
|
|
|
|
input_forward();
|
|
|
|
break;
|
|
|
|
case KEYCODE_LEFT:
|
|
|
|
input_backward(1);
|
|
|
|
break;
|
2009-10-26 04:20:56 +05:30
|
|
|
case KEYCODE_CTRL_LEFT:
|
2011-11-03 17:58:22 +05:30
|
|
|
case KEYCODE_ALT_LEFT: /* bash doesn't do it */
|
2009-10-26 04:20:56 +05:30
|
|
|
ctrl_left();
|
|
|
|
break;
|
|
|
|
case KEYCODE_CTRL_RIGHT:
|
2011-11-03 17:58:22 +05:30
|
|
|
case KEYCODE_ALT_RIGHT: /* bash doesn't do it */
|
2009-10-26 04:20:56 +05:30
|
|
|
ctrl_right();
|
|
|
|
break;
|
2009-05-15 06:57:53 +05:30
|
|
|
case KEYCODE_HOME:
|
|
|
|
input_backward(cursor);
|
|
|
|
break;
|
|
|
|
case KEYCODE_END:
|
2010-05-11 18:19:13 +05:30
|
|
|
put_till_end_and_adv_cursor();
|
2001-02-17 00:06:04 +05:30
|
|
|
break;
|
2000-03-19 10:58:55 +05:30
|
|
|
|
2009-05-15 06:57:53 +05:30
|
|
|
default:
|
2009-10-14 16:23:04 +05:30
|
|
|
if (initial_settings.c_cc[VINTR] != 0
|
|
|
|
&& ic_raw == initial_settings.c_cc[VINTR]
|
|
|
|
) {
|
|
|
|
/* Ctrl-C (usually) - stop gathering input */
|
|
|
|
command_len = 0;
|
|
|
|
break_out = -1; /* "do not append '\n'" */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (initial_settings.c_cc[VEOF] != 0
|
|
|
|
&& ic_raw == initial_settings.c_cc[VEOF]
|
|
|
|
) {
|
|
|
|
/* Ctrl-D (usually) - delete one character,
|
|
|
|
* or exit if len=0 and no chars to delete */
|
|
|
|
if (command_len == 0) {
|
|
|
|
errno = 0;
|
2010-09-07 22:10:53 +05:30
|
|
|
|
|
|
|
case -1: /* error (e.g. EIO when tty is destroyed) */
|
|
|
|
IF_FEATURE_EDITING_VI(return_error_indicator:)
|
2009-10-14 16:23:04 +05:30
|
|
|
break_out = command_len = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
input_delete(0);
|
|
|
|
break;
|
|
|
|
}
|
2009-05-15 06:57:53 +05:30
|
|
|
// /* Control-V -- force insert of next char */
|
|
|
|
// if (c == CTRL('V')) {
|
|
|
|
// if (safe_read(STDIN_FILENO, &c, 1) < 1)
|
2010-09-07 22:10:53 +05:30
|
|
|
// goto return_error_indicator;
|
2009-05-15 06:57:53 +05:30
|
|
|
// if (c == 0) {
|
|
|
|
// beep();
|
|
|
|
// break;
|
|
|
|
// }
|
|
|
|
// }
|
2009-07-10 22:10:49 +05:30
|
|
|
if (ic < ' '
|
2010-03-26 18:36:56 +05:30
|
|
|
|| (!ENABLE_UNICODE_SUPPORT && ic >= 256)
|
|
|
|
|| (ENABLE_UNICODE_SUPPORT && ic >= VI_CMDMODE_BIT)
|
2009-07-10 22:10:49 +05:30
|
|
|
) {
|
2009-05-15 06:57:53 +05:30
|
|
|
/* If VI_CMDMODE_BIT is set, ic is >= 256
|
2009-10-14 16:23:04 +05:30
|
|
|
* and vi mode ignores unexpected chars.
|
2009-05-15 06:57:53 +05:30
|
|
|
* Otherwise, we are here if ic is a
|
|
|
|
* control char or an unhandled ESC sequence,
|
|
|
|
* which is also ignored.
|
|
|
|
*/
|
2007-01-22 00:51:21 +05:30
|
|
|
break;
|
2009-05-15 06:57:53 +05:30
|
|
|
}
|
|
|
|
if ((int)command_len >= (maxsize - 2)) {
|
|
|
|
/* Not enough space for the char and EOL */
|
2000-03-19 10:58:55 +05:30
|
|
|
break;
|
2009-05-15 06:57:53 +05:30
|
|
|
}
|
2000-03-19 10:58:55 +05:30
|
|
|
|
2007-01-22 12:51:38 +05:30
|
|
|
command_len++;
|
2009-05-15 06:57:53 +05:30
|
|
|
if (cursor == (command_len - 1)) {
|
|
|
|
/* We are at the end, append */
|
2009-07-10 22:10:49 +05:30
|
|
|
command_ps[cursor] = ic;
|
|
|
|
command_ps[cursor + 1] = BB_NUL;
|
2010-05-11 18:19:13 +05:30
|
|
|
put_cur_glyph_and_inc_cursor();
|
2010-03-26 17:43:24 +05:30
|
|
|
if (unicode_bidi_isrtl(ic))
|
2010-03-18 23:05:37 +05:30
|
|
|
input_backward(1);
|
2009-05-15 06:57:53 +05:30
|
|
|
} else {
|
|
|
|
/* In the middle, insert */
|
2001-02-17 00:06:04 +05:30
|
|
|
int sc = cursor;
|
2000-03-19 10:58:55 +05:30
|
|
|
|
2009-07-10 22:10:49 +05:30
|
|
|
memmove(command_ps + sc + 1, command_ps + sc,
|
|
|
|
(command_len - sc) * sizeof(command_ps[0]));
|
|
|
|
command_ps[sc] = ic;
|
2010-03-26 17:43:24 +05:30
|
|
|
/* is right-to-left char, or neutral one (e.g. comma) was just added to rtl text? */
|
|
|
|
if (!isrtl_str())
|
|
|
|
sc++; /* no */
|
2010-05-11 18:19:13 +05:30
|
|
|
put_till_end_and_adv_cursor();
|
2001-01-27 02:12:23 +05:30
|
|
|
/* to prev x pos + 1 */
|
2001-02-17 00:06:04 +05:30
|
|
|
input_backward(cursor - sc);
|
2000-03-19 10:58:55 +05:30
|
|
|
}
|
|
|
|
break;
|
2009-10-14 16:23:04 +05:30
|
|
|
} /* switch (ic) */
|
2009-05-15 06:57:53 +05:30
|
|
|
|
|
|
|
if (break_out)
|
2000-03-19 10:58:55 +05:30
|
|
|
break;
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2007-11-10 07:05:47 +05:30
|
|
|
#if ENABLE_FEATURE_TAB_COMPLETION
|
2009-10-14 16:23:04 +05:30
|
|
|
if (ic_raw != '\t')
|
2010-09-03 16:21:36 +05:30
|
|
|
lastWasTab = 0;
|
2007-11-10 07:05:47 +05:30
|
|
|
#endif
|
2009-05-15 06:57:53 +05:30
|
|
|
} /* while (1) */
|
2000-03-16 13:39:57 +05:30
|
|
|
|
2009-10-27 15:04:06 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
|
2009-10-27 15:17:49 +05:30
|
|
|
if (S.sent_ESC_br6n) {
|
2009-10-27 15:04:06 +05:30
|
|
|
/* "sleep 1; busybox ash" + hold [Enter] to trigger.
|
|
|
|
* We sent "ESC [ 6 n", but got '\n' first, and
|
|
|
|
* KEYCODE_CURSOR_POS response is now buffered from terminal.
|
|
|
|
* It's bad already and not much can be done with it
|
|
|
|
* (it _will_ be visible for the next process to read stdin),
|
|
|
|
* but without this delay it even shows up on the screen
|
|
|
|
* as garbage because we restore echo settings with tcsetattr
|
|
|
|
* before it comes in. UGLY!
|
|
|
|
*/
|
|
|
|
usleep(20*1000);
|
2020-12-22 02:25:03 +05:30
|
|
|
// MAYBE? tcflush(STDIN_FILENO, TCIFLUSH); /* flushes data received but not read */
|
2009-10-27 15:04:06 +05:30
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-07-19 01:51:24 +05:30
|
|
|
/* End of bug-catching "command_must_not_be_used" trick */
|
2009-07-10 22:10:49 +05:30
|
|
|
#undef command
|
|
|
|
|
2010-03-26 18:36:56 +05:30
|
|
|
#if ENABLE_UNICODE_SUPPORT
|
2009-09-29 03:30:12 +05:30
|
|
|
command[0] = '\0';
|
|
|
|
if (command_len > 0)
|
|
|
|
command_len = save_string(command, maxsize - 1);
|
2009-07-10 22:10:49 +05:30
|
|
|
free(command_ps);
|
|
|
|
#endif
|
|
|
|
|
2013-04-07 22:17:24 +05:30
|
|
|
if (command_len > 0) {
|
2007-01-22 12:51:38 +05:30
|
|
|
remember_in_history(command);
|
2013-04-07 22:17:24 +05:30
|
|
|
}
|
2007-01-22 00:48:19 +05:30
|
|
|
|
2003-12-24 01:54:51 +05:30
|
|
|
if (break_out > 0) {
|
2007-01-22 12:51:38 +05:30
|
|
|
command[command_len++] = '\n';
|
|
|
|
command[command_len] = '\0';
|
2001-07-17 06:42:36 +05:30
|
|
|
}
|
2007-01-22 00:48:19 +05:30
|
|
|
|
2007-11-10 07:05:47 +05:30
|
|
|
#if ENABLE_FEATURE_TAB_COMPLETION
|
2007-01-22 00:49:46 +05:30
|
|
|
free_tab_completion_data();
|
2001-02-17 00:06:04 +05:30
|
|
|
#endif
|
2007-01-22 00:48:19 +05:30
|
|
|
|
2007-01-22 13:00:26 +05:30
|
|
|
/* restore initial_settings */
|
2008-11-05 18:50:58 +05:30
|
|
|
tcsetattr_stdin_TCSANOW(&initial_settings);
|
2018-02-26 00:39:54 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_WINCH
|
2007-01-22 13:00:26 +05:30
|
|
|
/* restore SIGWINCH handler */
|
2016-11-28 02:55:07 +05:30
|
|
|
sigaction_set(SIGWINCH, &S.SIGWINCH_handler);
|
2018-02-26 00:39:54 +05:30
|
|
|
#endif
|
2009-11-02 18:49:51 +05:30
|
|
|
fflush_all();
|
2007-11-10 07:05:47 +05:30
|
|
|
|
2008-08-20 06:16:32 +05:30
|
|
|
len = command_len;
|
2007-11-10 07:05:47 +05:30
|
|
|
DEINIT_S();
|
|
|
|
|
2008-08-20 06:16:32 +05:30
|
|
|
return len; /* can't return command_len, DEINIT_S() destroys it */
|
2007-01-22 12:51:38 +05:30
|
|
|
}
|
|
|
|
|
2010-07-19 01:51:24 +05:30
|
|
|
#else /* !FEATURE_EDITING */
|
2007-01-22 12:51:38 +05:30
|
|
|
|
|
|
|
#undef read_line_input
|
2008-06-27 08:22:20 +05:30
|
|
|
int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize)
|
2007-01-22 12:51:38 +05:30
|
|
|
{
|
2021-02-04 01:17:14 +05:30
|
|
|
fputs_stdout(prompt);
|
2009-11-02 18:49:51 +05:30
|
|
|
fflush_all();
|
2012-09-27 19:33:49 +05:30
|
|
|
if (!fgets(command, maxsize, stdin))
|
|
|
|
return -1;
|
2007-01-22 12:51:38 +05:30
|
|
|
return strlen(command);
|
2000-03-16 13:39:57 +05:30
|
|
|
}
|
|
|
|
|
2010-07-19 01:51:24 +05:30
|
|
|
#endif /* !FEATURE_EDITING */
|
2001-02-17 00:06:04 +05:30
|
|
|
|
|
|
|
|
2007-01-22 00:48:19 +05:30
|
|
|
/*
|
|
|
|
* Testing
|
|
|
|
*/
|
2001-02-17 00:06:04 +05:30
|
|
|
|
2007-01-22 00:48:19 +05:30
|
|
|
#ifdef TEST
|
2001-04-10 04:18:12 +05:30
|
|
|
|
2001-03-16 02:21:09 +05:30
|
|
|
#include <locale.h>
|
2007-01-22 00:48:19 +05:30
|
|
|
|
|
|
|
const char *applet_name = "debug stuff usage";
|
2001-03-16 02:21:09 +05:30
|
|
|
|
2001-02-17 00:06:04 +05:30
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
2007-06-10 20:38:44 +05:30
|
|
|
char buff[MAX_LINELEN];
|
2001-02-17 00:06:04 +05:30
|
|
|
char *prompt =
|
2007-01-22 14:33:07 +05:30
|
|
|
#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
|
2006-12-22 03:57:10 +05:30
|
|
|
"\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:"
|
|
|
|
"\\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] "
|
2017-09-14 02:18:30 +05:30
|
|
|
"\\!\\[\\e[36;1m\\]\\$ \\[\\E[m\\]";
|
2001-02-17 00:06:04 +05:30
|
|
|
#else
|
|
|
|
"% ";
|
|
|
|
#endif
|
|
|
|
|
2006-12-19 06:40:25 +05:30
|
|
|
while (1) {
|
2001-03-14 04:27:56 +05:30
|
|
|
int l;
|
2007-01-22 12:51:38 +05:30
|
|
|
l = read_line_input(prompt, buff);
|
2006-12-22 03:57:10 +05:30
|
|
|
if (l <= 0 || buff[l-1] != '\n')
|
2003-12-24 01:54:51 +05:30
|
|
|
break;
|
2010-09-03 16:21:36 +05:30
|
|
|
buff[l-1] = '\0';
|
2007-01-22 12:51:38 +05:30
|
|
|
printf("*** read_line_input() returned line =%s=\n", buff);
|
2001-07-13 01:56:32 +05:30
|
|
|
}
|
2007-01-22 12:51:38 +05:30
|
|
|
printf("*** read_line_input() detect ^D\n");
|
2001-02-17 00:06:04 +05:30
|
|
|
return 0;
|
|
|
|
}
|
2000-07-28 20:44:45 +05:30
|
|
|
|
2003-07-28 15:26:35 +05:30
|
|
|
#endif /* TEST */
|