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]);