shell: do not read user database for every prompt - only for those which need it
function old new delta
get_user_strings - 52 +52
get_homedir_or_NULL - 23 +23
parse_and_put_prompt 823 838 +15
null_str 1 - -1
complete_cmd_dir_file 814 812 -2
deinit_S 51 42 -9
read_line_input 3059 3015 -44
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 1/3 up/down: 90/-56) Total: 34 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index 9960448..e14c787 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -135,10 +135,6 @@
: 0x7ff0
};
-#if ENABLE_USERNAME_OR_HOMEDIR
-static const char null_str[] ALIGN1 = "";
-#endif
-
/* We try to minimize both static and stack usage. */
struct lineedit_statics {
line_input_t *state;
@@ -161,12 +157,13 @@
#if ENABLE_USERNAME_OR_HOMEDIR
char *user_buf;
- char *home_pwd_buf; /* = (char*)null_str; */
+ char *home_pwd_buf;
+ smallint got_user_strings;
#endif
#if ENABLE_FEATURE_TAB_COMPLETION
- char **matches;
unsigned num_matches;
+ char **matches;
#endif
#if ENABLE_FEATURE_EDITING_WINCH
@@ -207,8 +204,9 @@
#define prompt_last_line (S.prompt_last_line)
#define user_buf (S.user_buf )
#define home_pwd_buf (S.home_pwd_buf )
-#define matches (S.matches )
+#define got_user_strings (S.got_user_strings)
#define num_matches (S.num_matches )
+#define matches (S.matches )
#define delptr (S.delptr )
#define newdelflag (S.newdelflag )
#define delbuf (S.delbuf )
@@ -226,14 +224,47 @@
#endif
#if ENABLE_USERNAME_OR_HOMEDIR
free(user_buf);
- if (home_pwd_buf != null_str)
- free(home_pwd_buf);
+ free(home_pwd_buf);
#endif
free(lineedit_ptr_to_statics);
}
#define DEINIT_S() deinit_S()
+#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)
+{
+ if (!got_user_strings)
+ get_user_strings();
+ return home_pwd_buf;
+}
+#endif
+
#if ENABLE_UNICODE_SUPPORT
static size_t load_string(const char *src)
{
@@ -691,11 +722,11 @@
{
struct passwd *entry;
char *tilde_name = ud;
- char *home = NULL;
+ const char *home = NULL;
ud++; /* skip ~ */
if (*ud == '/') { /* "~/..." */
- home = home_pwd_buf;
+ home = get_homedir_or_NULL();
} else {
/* "~user/..." */
ud = strchr(ud, '/');
@@ -1971,7 +2002,7 @@
switch (c) {
case 'u':
- pbuf = user_buf ? user_buf : (char*)"";
+ pbuf = (char*)get_username_str();
break;
case 'H':
case 'h':
@@ -1993,14 +2024,15 @@
case 'w': /* current dir */
case 'W': /* basename of cur dir */
if (!cwd_buf) {
+ const char *home;
cwd_buf = xrealloc_getcwd_or_warn(NULL);
if (!cwd_buf)
cwd_buf = (char *)bb_msg_unknown;
- else if (home_pwd_buf[0]) {
+ else if ((home = get_homedir_or_NULL()) != NULL && home[0]) {
char *after_home_user;
/* /home/user[/something] -> ~[/something] */
- after_home_user = is_prefixed_with(cwd_buf, home_pwd_buf);
+ after_home_user = is_prefixed_with(cwd_buf, home);
if (after_home_user
&& (*after_home_user == '/' || *after_home_user == '\0')
) {
@@ -2399,7 +2431,6 @@
//command_len = 0; - done by INIT_S()
//cmdedit_y = 0; /* quasireal y, not true if line > xt*yt */
cmdedit_termw = 80;
- IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;)
IF_FEATURE_EDITING_VI(delptr = delbuf;)
n = get_termios_and_make_raw(STDIN_FILENO, &new_settings, &initial_settings, 0
@@ -2459,18 +2490,6 @@
tcsetattr_stdin_TCSANOW(&new_settings);
-#if ENABLE_USERNAME_OR_HOMEDIR
- {
- struct passwd *entry;
-
- entry = getpwuid(geteuid());
- if (entry) {
- user_buf = xstrdup(entry->pw_name);
- home_pwd_buf = xstrdup(entry->pw_dir);
- }
- }
-#endif
-
#if 0
for (i = 0; i <= state->max_history; i++)
bb_error_msg("history[%d]:'%s'", i, state->history[i]);