diff --git a/CwdUtils.c b/CwdUtils.c new file mode 100644 index 000000000..adc87a7e5 --- /dev/null +++ b/CwdUtils.c @@ -0,0 +1,153 @@ +#include "CwdUtils.h" + +#include +#include +#include + +#include "XUtils.h" + +typedef struct ShortenCwdContext { + size_t maxLength; + size_t len; + wchar_t **parts; + size_t partsLen; + size_t *partLengths; +} ShortenCwdContext; + +static void shortenCwdParts(ShortenCwdContext *ctx) { + for (int i = ctx->partsLen - 2; i >= 0 && ctx->len > ctx->maxLength; i--) { + if (ctx->partLengths[i] < 3) + continue; + + size_t extraChars = ctx->len - ctx->maxLength; + size_t maxRemovableChars = ctx->partLengths[i] - 2; + size_t charsToRemove = extraChars < maxRemovableChars ? extraChars : maxRemovableChars; + + ctx->partLengths[i] -= charsToRemove; + ctx->len -= charsToRemove; + + Wstring_safeWcsncpy(ctx->parts[i] + (ctx->partLengths[i] - 1), L"~", 2); + } +} + +static size_t collapseCwdParts(ShortenCwdContext *ctx, bool doActualWork) { + if (ctx->len <= ctx->maxLength || ctx->partsLen <= 3) + return 0; + + size_t len = ctx->len; + + size_t i; + for (i = ctx->partsLen - 2; i > 1; i--) { + if (len + (3 - ctx->partLengths[i]) <= ctx->maxLength) + break; + + len -= ctx->partLengths[i] + 1; + + if (doActualWork) { + ctx->partLengths[i] = 0; + free(ctx->parts[i]); + ctx->parts[i] = NULL; + } + } + + len += 3 - ctx->partLengths[i]; + size_t diff = ctx->len - len; + + if (doActualWork) { + wchar_t newPart[] = L"~~~"; + newPart[0] = ctx->parts[i][0]; + free(ctx->parts[i]); + ctx->parts[i] = xWcsdup(newPart); + ctx->partLengths[i] = 3; + ctx->len = len; + } + + return diff; +} + +static size_t shortenCwdLastPart(ShortenCwdContext *ctx, bool doActualWork) { + if (ctx->len <= ctx->maxLength) + return 0; + + size_t lastPartLen = ctx->partLengths[ctx->partsLen - 1]; + if (lastPartLen <= 3) + return 0; + + wchar_t *lastPart = ctx->parts[ctx->partsLen - 1]; + size_t extraChars = ctx->len - ctx->maxLength + 1; + size_t maxRemovableChars = lastPartLen - 2; + size_t charsToRemove = extraChars < maxRemovableChars ? extraChars : maxRemovableChars; + + if (doActualWork) { + size_t charsAtBeginning = (lastPartLen - charsToRemove + 1) / 2; + size_t charsAtEnd = lastPartLen - charsToRemove - charsAtBeginning; + lastPart[charsAtBeginning] = '~'; + wmemmove(lastPart + charsAtBeginning + 1, lastPart + lastPartLen - charsAtEnd, charsAtEnd); + lastPart[charsAtBeginning + charsAtEnd + 1] = '\0'; + ctx->partLengths[ctx->partsLen - 1] = lastPartLen - charsToRemove + 1; + ctx->len -= charsToRemove - 1; + } + + return charsToRemove - 1; +} + +static wchar_t* buildCwdFromParts(ShortenCwdContext *ctx) { + size_t len = ctx->partsLen - 1; + for (size_t i = 0; i < ctx->partsLen; i++) + len += ctx->partLengths[i]; + + wchar_t *newCwd = xCalloc(len + 1, sizeof(wchar_t)); + + newCwd[0] = '\0'; + for (size_t i = 0, writeIndex = 0; i < ctx->partsLen; i++) { + if (!ctx->parts[i]) + continue; + + Wstring_safeWcsncpy(newCwd + writeIndex, ctx->parts[i], ctx->partLengths[i] + 1); + writeIndex += ctx->partLengths[i]; + if (i < ctx->partsLen - 1) + newCwd[writeIndex++] = L'/'; + } + + return newCwd; +} + +char* CwdUtils_shortenCwd(char *cwd, const size_t maxLength) { + wchar_t *wcwd = xMbstowcs(cwd); + size_t len = wcslen(wcwd); + if (len <= maxLength) { + free(wcwd); + return xStrdup(cwd); + } + + ShortenCwdContext ctx = { + .maxLength = maxLength, + .len = len, + }; + ctx.parts = Wstring_split(wcwd, L'/', &ctx.partsLen); + free(wcwd); + wcwd = NULL; + ctx.partLengths = xCalloc(ctx.partsLen, sizeof(size_t)); + for (size_t i = 0; i < ctx.partsLen; i++) + ctx.partLengths[i] = wcslen(ctx.parts[i]); + + shortenCwdParts(&ctx); + if (shortenCwdLastPart(&ctx, false) > collapseCwdParts(&ctx, false)) { + shortenCwdLastPart(&ctx, true); + collapseCwdParts(&ctx, true); + } else { + collapseCwdParts(&ctx, true); + shortenCwdLastPart(&ctx, true); + } + + wchar_t *newWcwd = buildCwdFromParts(&ctx); + char *newCwd = xWcstombs(newWcwd); + free(newWcwd); + + free(ctx.partLengths); + for (size_t i = 0; i < ctx.partsLen; i++) + free(ctx.parts[i]); + free(ctx.parts); + + return newCwd; +} diff --git a/CwdUtils.h b/CwdUtils.h new file mode 100644 index 000000000..800111641 --- /dev/null +++ b/CwdUtils.h @@ -0,0 +1,8 @@ +#ifndef HEADER_CwdUtils +#define HEADER_CwdUtils + +#include + +char* CwdUtils_shortenCwd(char* cwd, size_t maxLength); + +#endif diff --git a/Makefile.am b/Makefile.am index 7ed500ca3..bb4f9e4f8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -43,6 +43,7 @@ myhtopsources = \ Compat.c \ CPUMeter.c \ CRT.c \ + CwdUtils.c \ DateMeter.c \ DateTimeMeter.c \ DiskIOMeter.c \ @@ -101,6 +102,7 @@ myhtopheaders = \ CommandLine.h \ CommandScreen.h \ Compat.h \ + CwdUtils.h \ DateMeter.h \ DateTimeMeter.h \ DiskIOMeter.h \ diff --git a/Process.c b/Process.c index f0fc67fa6..89825cecc 100644 --- a/Process.c +++ b/Process.c @@ -30,6 +30,7 @@ in the source distribution for its full text. #include "RichString.h" #include "Settings.h" #include "XUtils.h" +#include "CwdUtils.h" #if defined(MAJOR_IN_MKDEV) #include @@ -825,7 +826,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field attr = CRT_colors[PROCESS_SHADOW]; cwd = "main thread terminated"; } else { - cwd = this->procCwd; + cwd = this->procCwdShort; } Process_printLeftAlignedField(str, attr, cwd, 25); return; @@ -955,6 +956,7 @@ void Process_done(Process* this) { free(this->procComm); free(this->procExe); free(this->procCwd); + free(this->procCwdShort); free(this->mergedCommand.str); free(this->tty_name); } @@ -1238,3 +1240,8 @@ void Process_updateExe(Process* this, const char* exe) { } this->mergedCommand.exeChanged = true; } + +void Process_updateShortCwd(Process* this) { + free(this->procCwdShort); + this->procCwdShort = this->procCwd ? CwdUtils_shortenCwd(this->procCwd, 25) : NULL; +} diff --git a/Process.h b/Process.h index 4080d98c9..710f7f665 100644 --- a/Process.h +++ b/Process.h @@ -157,6 +157,9 @@ typedef struct Process_ { /* The process/thread working directory */ char* procCwd; + /* Shortened process/thread working directory */ + char* procCwdShort; + /* Offset in procExe of the process basename */ int procExeBasenameOffset; @@ -388,6 +391,7 @@ const char* Process_getCommandStr(const Process* this); void Process_updateComm(Process* this, const char* comm); void Process_updateCmdline(Process* this, const char* cmdline, int basenameStart, int basenameEnd); void Process_updateExe(Process* this, const char* exe); +void Process_updateShortCwd(Process* this); /* This function constructs the string that is displayed by * Process_writeCommand and also returned by Process_getCommandStr */ diff --git a/ProcessList.c b/ProcessList.c index 1ce6b76de..b8180cdd1 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -629,6 +629,8 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) { for (int i = Vector_size(this->processes) - 1; i >= 0; i--) { Process* p = (Process*) Vector_get(this->processes, i); Process_makeCommandStr(p); + if (p->settings->flags & PROCESS_FLAG_CWD) + Process_updateShortCwd(p); if (p->tombStampMs > 0) { // remove tombed process diff --git a/XUtils.c b/XUtils.c index b6999f92d..1138f839b 100644 --- a/XUtils.c +++ b/XUtils.c @@ -323,3 +323,83 @@ ssize_t xReadfileat(openat_arg_t dirfd, const char* pathname, void* buffer, size return readfd_internal(fd, buffer, count); } + +wchar_t* xMbstowcs(const char *mbs) { + size_t len = strlen(mbs); + wchar_t *wcs = xCalloc(len + 1, sizeof(wchar_t)); + mbstate_t mbstate = {0}; + mbsrtowcs(wcs, &mbs, len + 1, &mbstate); + if (mbs) + fail(); + return wcs; +} + +char* xWcstombs(const wchar_t *wcs) { + size_t len = wcslen(wcs); + size_t mbsSize = len * sizeof(wchar_t) + 1; + char *mbs = xMalloc(mbsSize); + mbstate_t mbstate = {0}; + wcsrtombs(mbs, &wcs, mbsSize, &mbstate); + if (wcs) + fail(); + return mbs; +} + +wchar_t* xWcsdup(const wchar_t* str) { + wchar_t* data = wcsdup(str); + if (!data) + fail(); + return data; +} + +wchar_t* xWcsndup(const wchar_t* str, size_t len) { + wchar_t* data = xCalloc(len + 1, sizeof(wchar_t)); + if (!data) + fail(); + + for (size_t i = 0; i < len && str[i]; i++) + data[i] = str[i]; + + return data; +} + +wchar_t** Wstring_split(const wchar_t* s, wchar_t sep, size_t* n) { + const unsigned int rate = 10; + wchar_t** out = xCalloc(rate, sizeof(wchar_t*)); + size_t ctr = 0; + unsigned int blocks = rate; + const wchar_t* where; + while ((where = wcschr(s, sep)) != NULL) { + size_t size = (size_t)(where - s); + out[ctr] = xWcsndup(s, size); + ctr++; + if (ctr == blocks) { + blocks += rate; + out = (wchar_t**) xRealloc(out, sizeof(wchar_t*) * blocks); + } + s += size + 1; + } + if (s[0] != L'\0') { + out[ctr] = xWcsdup(s); + ctr++; + } + out = xRealloc(out, sizeof(wchar_t*) * (ctr + 1)); + out[ctr] = NULL; + + if (n) + *n = ctr; + + return out; +} + +size_t Wstring_safeWcsncpy(wchar_t* restrict dest, const wchar_t* restrict src, size_t size) { + assert(size > 0); + + size_t i = 0; + for (; i < size - 1 && src[i]; i++) + dest[i] = src[i]; + + dest[i] = L'\0'; + + return i; +} diff --git a/XUtils.h b/XUtils.h index eeb214d1f..22656db83 100644 --- a/XUtils.h +++ b/XUtils.h @@ -75,4 +75,11 @@ char* xStrndup(const char* str, size_t len) ATTR_NONNULL ATTR_MALLOC; ssize_t xReadfile(const char* pathname, void* buffer, size_t count); ssize_t xReadfileat(openat_arg_t dirfd, const char* pathname, void* buffer, size_t count); +wchar_t* xMbstowcs(const char *mbs); +char* xWcstombs(const wchar_t *wcs); +wchar_t* xWcsdup(const wchar_t* str); +wchar_t* xWcsndup(const wchar_t* str, size_t len); +wchar_t** Wstring_split(const wchar_t* s, wchar_t sep, size_t* n); +size_t Wstring_safeWcsncpy(wchar_t* restrict dest, const wchar_t* restrict src, size_t size); + #endif