From 4e5215cb6185108ade86af49f123b13674fe9587 Mon Sep 17 00:00:00 2001 From: "C. McEnroe" Date: Wed, 23 Dec 2020 14:19:58 -0500 Subject: Cache the expanded prompt for editline Previously, the prompt would be expanded every time editline called the getprompt callback. I think the code may have been written assuming that editline only calls getprompt once per prompt, but it may actually call it many times, for instance every time you type backspace. This results not only in slower editing from expanding complex prompts repeatedly, it also consumes more and more stack memory each time getprompt is called. This can be seen by setting PS1 to some command substitution, typing many characters at the prompt, then holding backspace and observing memory usage. Thankfully all this stack memory is freed between prompts by the stackmark calls around el_gets. This change causes prompt expansion to always happen in the setprompt call, as it would when editline is disabled, and a cached copy of the prompt is saved for getprompt to return every time editline calls it. Since getprompt is no longer doing expansion, the stackmark calls surrounding el_gets can be removed. --- bin/dash/src/input.c | 6 +---- bin/dash/src/parser.c | 66 ++++++++++++++++++++++++++++----------------------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/bin/dash/src/input.c b/bin/dash/src/input.c index 17544e78..4167bd18 100644 --- a/bin/dash/src/input.c +++ b/bin/dash/src/input.c @@ -152,12 +152,8 @@ retry: static const char *rl_cp; static int el_len; - if (rl_cp == NULL) { - struct stackmark smark; - pushstackmark(&smark, stackblocksize()); + if (rl_cp == NULL) rl_cp = el_gets(el, &el_len); - popstackmark(&smark); - } if (rl_cp == NULL) nr = 0; else { diff --git a/bin/dash/src/parser.c b/bin/dash/src/parser.c index a47022e0..270a01de 100644 --- a/bin/dash/src/parser.c +++ b/bin/dash/src/parser.c @@ -107,6 +107,9 @@ struct heredoc *heredoc; int quoteflag; /* set if (part of) last token was quoted */ +static char *promptcache; + + STATIC union node *list(int); STATIC union node *andor(void); STATIC union node *pipeline(void); @@ -1541,27 +1544,6 @@ synerror(const char *msg) /* NOTREACHED */ } -STATIC void -setprompt(int which) -{ - struct stackmark smark; - int show; - - needprompt = 0; - whichprompt = which; - -#ifdef SMALL - show = 1; -#else - show = !el; -#endif - if (show) { - pushstackmark(&smark, stackblocksize()); - out2str(getprompt(NULL)); - popstackmark(&smark); - } -} - const char * expandstr(const char *ps) { @@ -1611,22 +1593,25 @@ out: return result; } -/* - * called by editline -- any expansions to the prompt - * should be added here. - */ -const char * -getprompt(void *unused) +STATIC void +setprompt(int which) { + struct stackmark smark; const char *prompt; + int show; + + needprompt = 0; + whichprompt = which; switch (whichprompt) { default: #ifdef DEBUG - return ""; + prompt = ""; + break; #endif case 0: - return nullstr; + prompt = nullstr; + break; case 1: prompt = ps1val(); break; @@ -1635,7 +1620,28 @@ getprompt(void *unused) break; } - return expandstr(prompt); +#ifdef SMALL + show = 1; +#else + show = !el; +#endif + pushstackmark(&smark, stackblocksize()); + if (show) { + out2str(expandstr(prompt)); + } else { + free(promptcache); + promptcache = savestr(expandstr(prompt)); + } + popstackmark(&smark); +} + +/* + * called by editline -- return the cached prompt expansion. + */ +const char * +getprompt(void *unused) +{ + return promptcache; } const char *const * -- cgit 1.4.1 11dc723e8496c8c8ab1048dbd2181170c&follow=1'>Add concept of passive clientsJune McEnroe 2019-11-14Save and load full struct timevalJune McEnroe 2019-11-14Use struct timeval for sub-second precisionJune McEnroe 2019-11-14Remove server-time filter TODOJune McEnroe 2019-11-14Tweak filter regexesJune McEnroe 2019-11-14Filter multi-prefixJune McEnroe 2019-11-14Replace filters with regex replacesJune McEnroe 2019-11-13Refactor filterUserhostInNamesJune McEnroe 2019-11-13Factor out wordcpy for filtersJune McEnroe 2019-11-11Revert "Test getopt_config"June McEnroe 2019-11-11Test getopt_configJune McEnroe 2019-11-11Add userhost-in-names to manualJune McEnroe 2019-11-11Skip initial NAMES parametersJune McEnroe 2019-11-11Filter userhost-in-namesJune McEnroe 2019-11-10Compare words without copying in filtersJune McEnroe 2019-11-10Separate tags from all targetJune McEnroe 2019-11-10Filter invite-notifyJune McEnroe 2019-11-10Add capsicum note to READMEJune McEnroe 2019-11-10Filter extended-joinJune McEnroe 2019-11-10Expand client configuration documentation and list capabilitiesJune McEnroe 2019-11-10Request all supported caps from serverJune McEnroe 2019-11-10Filter ACCOUNT, AWAY, CHGHOST for incapable clientsJune McEnroe 2019-11-10Rename listen to localJune McEnroe 2019-11-09Remove extended-join and invite-notifyJune McEnroe 2019-11-09Maintain stateCaps and offer them to clientsJune McEnroe 2019-11-09Parse capabilitiesJune McEnroe 2019-11-09Avoid the reserved _A names with BIT macroJune McEnroe 2019-11-09Define macro for bit flag enumsJune McEnroe 2019-11-08Check that password is hashedJune McEnroe 2019-11-08Avoid calling getopt_long again after it returns -1June McEnroe 2019-11-08Only change AWAY status for registered clientsJune McEnroe 2019-11-07Just write the example normallyJune McEnroe 2019-11-07Include path in readlinkat errorJune McEnroe 2019-11-07Call clientConsume before clientRecvJune McEnroe 2019-11-06Use -l:filename in Linux.mkJune McEnroe 2019-11-06Fix compat.h for #defined strlcpyJune McEnroe 2019-11-06Allow unsetting LIBRESSL_PREFIXJune McEnroe 2019-11-06Document calico service configurationJune McEnroe 2019-11-06Document SASL EXTERNAL configuration in more detailJune McEnroe 2019-11-06Document pounce service configurationJune McEnroe 2019-11-06Mention Darwin and GNU/Linux in READMEJune McEnroe 2019-11-06Assume LibreSSL from brew on DarwinJune McEnroe 2019-11-06Remove -DNO_EXPLICIT_BZERO from Darwin.mkJune McEnroe 2019-11-06Don't install rc scripts or dirs on LinuxJune McEnroe