rmyorston / busybox-w32

WIN32 native port of BusyBox.
https://frippery.org/busybox
Other
674 stars 124 forks source link

Fancy prompt experiments with starship #351

Closed naksyl closed 1 year ago

naksyl commented 1 year ago

I would like to make posible to put command duration and job count on PS1. Bash for this purpose using trap DEBUG but busybox lacks such functionality. After couple of hours digging in ash source code jungle I manage to code quick and dirty solution (which suprisingly works - mostly). The main idea is to expose three variables to shell: CMDREALTIME (this may stay hidden - but its nicely paired with EPOCHREALTIME)- command start time, CMDDURATION integral value in usec and JOBSNUM. I admit that I have no expierience in C (doing stuff in Java and C++/Qt) so don't be rude.

ash.diff ```diff diff --git a/shell/ash.c b/shell/ash.c index d0ecd501a..f1a6b376a 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -2413,8 +2413,11 @@ static void change_random(const char *) FAST_FUNC; #if BASH_EPOCH_VARS static void change_seconds(const char *) FAST_FUNC; static void change_realtime(const char *) FAST_FUNC; +static void change_duration(void) FAST_FUNC; +#endif +#if JOBS_WIN32 +static void change_jobs(void) FAST_FUNC; #endif - #if ENABLE_PLATFORM_MINGW32 static void FAST_FUNC change_terminal_mode(const char *newval UNUSED_PARAM) @@ -2462,6 +2465,11 @@ static const struct { #if BASH_EPOCH_VARS { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "EPOCHSECONDS", change_seconds }, { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "EPOCHREALTIME", change_realtime }, + { VSTRFIXED|VTEXTFIXED|VUNSET, "CMDDURATION", NULL }, + { VSTRFIXED|VTEXTFIXED|VUNSET, "CMDREALTIME", NULL }, +#endif +#if JOBS_WIN32 + { VSTRFIXED|VTEXTFIXED|VUNSET, "JOBSNUM", NULL }, #endif #if ENABLE_LOCALE_SUPPORT { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all }, @@ -2528,6 +2536,12 @@ extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var; #if BASH_EPOCH_VARS # define vepochs varinit[VAR_OFFSET3 + 7] # define vepochr varinit[VAR_OFFSET3 + 8] +# define vduration varinit[VAR_OFFSET3 + 9] +# define vcmdr varinit[VAR_OFFSET3 + 10] +#endif +#define VAR_OFFSET4 (VAR_OFFSET3 + (BASH_EPOCH_VARS*4)) +#if JOBS_WIN32 +# define vjobs varinit[VAR_OFFSET4 + 7] #endif #define INIT_G_var() do { \ unsigned i; \ @@ -2562,7 +2576,9 @@ extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var; #if ENABLE_ASH_GETOPTS # define optindval() (voptind.var_text + 7) #endif - +#if BASH_EPOCH_VARS +# define cmdrval() (vcmdr.var_text + 12) +#endif #if ENABLE_ASH_GETOPTS static void FAST_FUNC getoptsreset(const char *value) @@ -12831,7 +12847,7 @@ change_random(const char *value) #endif #if BASH_EPOCH_VARS -static void FAST_FUNC +static struct timeval FAST_FUNC change_epoch(struct var *vepoch, const char *fmt) { struct timeval tv; @@ -12841,6 +12857,7 @@ change_epoch(struct var *vepoch, const char *fmt) sprintf(buffer, fmt, (unsigned long long)tv.tv_sec, (unsigned)tv.tv_usec); setvar(vepoch->var_text, buffer, VNOFUNC); vepoch->flags &= ~VNOFUNC; + return tv; } static void FAST_FUNC @@ -12854,8 +12871,40 @@ change_realtime(const char *value UNUSED_PARAM) { change_epoch(&vepochr, "%llu.%06u"); } -#endif +static void FAST_FUNC +change_duration(void) +{ + struct timeval start; + struct timeval stop; + char buff[32]; + char *ptr = strchr(cmdrval(), '.'); + unsigned long long duration = 0; + + start.tv_sec = strtoul(cmdrval(), NULL, 10); + start.tv_usec = strtoul(++ptr, NULL, 10); + stop = change_epoch(&vepochr, "%llu.%06u"); + + // calculate duration as integer microsecs + duration = (stop.tv_sec - start.tv_sec)*1000000 - (stop.tv_usec - start.tv_usec); + sprintf(buff, "%llu", duration); + setvar(vduration.var_text, buff, VNOFUNC); + vduration.flags &= ~VNOFUNC; +} +#endif +#if JOBS_WIN32 +static void FAST_FUNC +change_jobs(void) +{ + unsigned count = 0; + struct job *jp = curjob; + while (jp) { + count++; + jp = jp->prev_job; + } + setvar(vjobs.var_text, utoa(count), VNOFUNC); +} +#endif #if ENABLE_ASH_GETOPTS static int getopts(char *optstr, char *optvar, char **optfirst) @@ -14799,6 +14848,7 @@ cmdloop(int top) #if JOBS || JOBS_WIN32 if (doing_jobctl) showjobs(SHOW_CHANGED|SHOW_STDERR); + change_jobs(); #endif inter = 0; if (iflag && top) { @@ -14829,13 +14879,18 @@ cmdloop(int top) numeof++; } else { int i; - #if !ENABLE_PLATFORM_MINGW32 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */ job_warning >>= 1; #endif numeof = 0; +#if BASH_EPOCH_VARS + change_epoch(&vcmdr, "%llu.%.06u"); +#endif i = evaltree(n, 0); +#if BASH_EPOCH_VARS + change_duration(); +#endif if (n) status = i; } ```

Problems encountered:

Just notice warnings from Windows defender - after frequently killing jobs - maybe this has something to do with negative CMDDURATION.

It seem that best place for my change_epoch and change_duration is right around call to evaltree at ash.c:14890 Can somebody help me find the right spot to update those variables?

PS1='$(starship.exe prompt -s $? -j ${JOBSNUM:-0} -d $(( ${CMDDURATION:-0} / 1000 )) )'
PS2='$(starship.exe prompt --continuation)'
Screenshots ![Zrzut ekranu 2023-08-10 224641](https://github.com/rmyorston/busybox-w32/assets/17428477/04ec588c-e035-4d0a-afdf-340b1f3fe7e0) ![Zrzut ekranu 2023-08-11 002010](https://github.com/rmyorston/busybox-w32/assets/17428477/312dda09-095f-4b81-9188-28689e2b3a50)
naksyl commented 1 year ago

OMG my formula duration = (stop.tv_sec - start.tv_sec)*1000000 - (stop.tv_usec - start.tv_usec); is wrong, should be:

duration = (stop.tv_sec - start.tv_sec)*1000000 + (stop.tv_usec - start.tv_usec);

rmyorston commented 1 year ago

The shell features required by starship are also lacking in upstream BusyBox. This isn't a Windows-specific problem.

In such cases I prefer to try to get support upstream first:

The problem then becomes "What will upstream accept?".

Upstream is unlikely to see support for starship as a priority (and I have a similar view relative to busybox-w32) so there would need to be other benefits to sell the proposition.

naksyl commented 1 year ago

Ok, thanks for reply and Your work on windows port.