[PATCH] libbb/lineedit: tab completion pager

Fryderyk Wrobel frd1996 at gmail.com
Mon Jul 15 13:30:57 UTC 2019


Hi there,

This patch adds a TAB-completion pager to the lineedit feature of libbb.
Below is the bloat-o-meter output and the patch for 1_31_stable branch.

Kind Regards,
Fryderyk


$ ./scripts/bloat-o-meter busybox_unstripped.1_31_stable
busybox_unstripped.1_31_completion_pager
function                                             old     new   delta
input_tab                                           1126    1391    +265
.rodata                                           158112  158127     +15
read_line_input                                     2741    2753     +12
cmdedit_setwidth                                      56      55      -1
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/1 up/down: 292/-1)            Total: 291 bytes


---
 libbb/Config.src |  5 ++++
 libbb/lineedit.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 76 insertions(+), 2 deletions(-)

diff --git a/libbb/Config.src b/libbb/Config.src
index 312aa1831..af4b4b846 100644
--- a/libbb/Config.src
+++ b/libbb/Config.src
@@ -153,6 +153,11 @@ config FEATURE_USERNAME_COMPLETION
 	default y
 	depends on FEATURE_TAB_COMPLETION

+config FEATURE_TAB_COMPLETION_PAGER
+	bool "Use pager for large amount of completion results"
+	default n
+	depends on FEATURE_TAB_COMPLETION
+
 config FEATURE_EDITING_FANCY_PROMPT
 	bool "Fancy shell prompts"
 	default y
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index fbabc6c12..6eb51f964 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -50,6 +50,7 @@
 # define ENABLE_FEATURE_EDITING 0
 # define ENABLE_FEATURE_TAB_COMPLETION 0
 # define ENABLE_FEATURE_USERNAME_COMPLETION 0
+# define ENABLE_FEATURE_TAB_COMPLETION_PAGER 0
 #endif


@@ -115,6 +116,7 @@ static bool BB_isalnum_or_underscore(CHAR_T c)
 #define ESC "\033"

 #define SEQ_CLEAR_TILL_END_OF_SCREEN  ESC"[J"
+#define SEQ_CLEAR_TILL_BEGIN_OF_LINE  ESC"[1K"
 //#define SEQ_CLEAR_TILL_END_OF_LINE  ESC"[K"


@@ -133,6 +135,7 @@ struct lineedit_statics {
 	line_input_t *state;

 	unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */
+	unsigned cmdedit_termh; /* = 25; */ /* actual terminal height (lines) */

 	unsigned cmdedit_x;        /* real x (col) terminal position */
 	unsigned cmdedit_y;        /* pseudoreal y (row) terminal position */
@@ -186,6 +189,7 @@ extern struct lineedit_statics *const
lineedit_ptr_to_statics;
 #define S (*lineedit_ptr_to_statics)
 #define state            (S.state           )
 #define cmdedit_termw    (S.cmdedit_termw   )
+#define cmdedit_termh    (S.cmdedit_termh   )
 #define cmdedit_x        (S.cmdedit_x       )
 #define cmdedit_y        (S.cmdedit_y       )
 #define cmdedit_prmt_len (S.cmdedit_prmt_len)
@@ -206,6 +210,7 @@ extern struct lineedit_statics *const
lineedit_ptr_to_statics;
 	(*(struct lineedit_statics**)&lineedit_ptr_to_statics) = xzalloc(sizeof(S)); \
 	barrier(); \
 	cmdedit_termw = 80; \
+	cmdedit_termh = 25; \
 	IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) \
 	IF_FEATURE_EDITING_VI(delptr = delbuf;) \
 } while (0)
@@ -1055,6 +1060,43 @@ static NOINLINE int build_match_prefix(char *match_buf)
 	return command_mode;
 }

+
+# if ENABLE_FEATURE_TAB_COMPLETION_PAGER
+
+static int lineedit_read_key(char *read_key_buffer, int timeout);
+
+/* Display the pager line and wait for the user */
+static int pager_wait(int nlines)
+{
+	char read_key_buffer[KEYCODE_BUFFER_SIZE];
+	int ic;
+
+	read_key_buffer[0] = 0;
+	while (1) {
+		printf("--More--");
+		ic = lineedit_read_key(read_key_buffer, -1); /* Calls fflush_all() */
+		printf(SEQ_CLEAR_TILL_BEGIN_OF_LINE "\r");
+
+		switch (ic) {
+		case -1:  /* Error */
+		case 0:   /* Timeout? */
+		case 3:   /* ETX - result of CTRL+C */
+		case 'Q':
+		case 'q':
+			return 0;            /* Don't print more  */
+		case '\n':
+			return 1;            /* Print one line */
+		case ' ':
+			return nlines;       /* Print one page */
+		case 'R':
+		case 'r':
+			return -1;           /* Print the rest */
+		}
+	}
+}
+
+# endif
+
 /*
  * Display by column (original idea from ls applet,
  * very optimized by me [Vladimir] :)
@@ -1066,6 +1108,13 @@ static void showfiles(void)
 	int nfiles = num_matches;
 	int nrows = nfiles;
 	int l;
+# if ENABLE_FEATURE_TAB_COMPLETION_PAGER
+	int nlines, sl;
+
+	nlines = cmdedit_termh - 2;
+	if (nlines < 2)
+		nlines = -1; /* Disable paging for a really small terminals */
+# endif

 	/* find the longest file name - use that as the column width */
 	for (row = 0; row < nrows; row++) {
@@ -1083,7 +1132,12 @@ static void showfiles(void)
 	} else {
 		ncols = 1;
 	}
+
+# if ENABLE_FEATURE_TAB_COMPLETION_PAGER
+	for (row = 0, sl = nlines; row < nrows; row++, sl--) {
+# else
 	for (row = 0; row < nrows; row++) {
+# endif
 		int n = row;
 		int nc;

@@ -1096,6 +1150,20 @@ static void showfiles(void)
 			puts(printable_string(matches[n]));
 		else
 			puts(matches[n]);
+
+# if ENABLE_FEATURE_TAB_COMPLETION_PAGER
+		if (sl == 0) {
+			sl = pager_wait(nlines);
+			if (sl == 0)
+				break;
+			/*
+			 * NB: When sl bcomes -1 then it is getting decreased
+			 *     every time the loop iterates. It will eventually
+			 *     overflow. Tht means the pager will kick in again
+			 *     after full range of 'int' type.
+			 */
+		}
+# endif
 	}
 }

@@ -2049,7 +2117,8 @@ static void cmdedit_setwidth(void)
 {
 	int new_y;

-	cmdedit_termw = get_terminal_width(STDIN_FILENO);
+	get_terminal_width_height(STDIN_FILENO, &cmdedit_termw, &cmdedit_termh);
+
 	/* new y for current cursor */
 	new_y = (cursor + cmdedit_prmt_len) / cmdedit_termw;
 	/* redraw */
@@ -2432,7 +2501,7 @@ int FAST_FUNC read_line_input(line_input_t *st,
const char *prompt, char *comman
 #endif

 	/* Get width (before printing prompt) */
-	cmdedit_termw = get_terminal_width(STDIN_FILENO);
+	get_terminal_width_height(STDIN_FILENO, &cmdedit_termw, &cmdedit_termh);
 	/* Print out the command prompt, optionally ask where cursor is */
 	parse_and_put_prompt(prompt);
 	ask_terminal();
-- 
2.16.4


More information about the busybox mailing list