Skip to content

Commit

Permalink
Shorten CWD path
Browse files Browse the repository at this point in the history
For showing the CWD path in a more compact way, this function

    - shortens path parts before last from right to left, until first
      character of the path stays (directory -> direc~ or d~)
    - combines parts before last from right with left (a~/b~ -> a~~)
    - shortens the last part from middle, preferring to keep characters
      from left (longdirectoryname -> long~ame)

in order (of 2nd and 3rd, the one that removes more characters is
applied first) until given maximum length is reached.
  • Loading branch information
onlined committed Jan 19, 2025
1 parent 4e6a781 commit 01c686c
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 1 deletion.
155 changes: 155 additions & 0 deletions CwdUtils.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#include "config.h" // IWYU pragma: keep

#include "CwdUtils.h"

#include <stdbool.h>
#include <stdlib.h>
#include <wchar.h>

#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;
}
8 changes: 8 additions & 0 deletions CwdUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef HEADER_CwdUtils
#define HEADER_CwdUtils

#include <stddef.h>

char* CwdUtils_shortenCwd(char* cwd, size_t maxLength);

#endif
2 changes: 2 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ myhtopsources = \
Compat.c \
CPUMeter.c \
CRT.c \
CwdUtils.c \
DateMeter.c \
DateTimeMeter.c \
DiskIOMeter.c \
Expand Down Expand Up @@ -109,6 +110,7 @@ myhtopheaders = \
CommandLine.h \
CommandScreen.h \
Compat.h \
CwdUtils.h \
DateMeter.h \
DateTimeMeter.h \
DiskIOMeter.h \
Expand Down
9 changes: 8 additions & 1 deletion Process.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ in the source distribution for its full text.
#include "Settings.h"
#include "Table.h"
#include "XUtils.h"
#include "CwdUtils.h"

#if defined(MAJOR_IN_MKDEV)
#include <sys/mkdev.h>
Expand Down Expand Up @@ -630,7 +631,7 @@ void Process_writeField(const Process* this, RichString* str, RowField field) {
attr = CRT_colors[PROCESS_SHADOW];
cwd = "main thread terminated";
} else {
cwd = this->procCwd;
cwd = this->procCwdShort;
}
Row_printLeftAlignedField(str, attr, cwd, 25);
return;
Expand Down Expand Up @@ -770,6 +771,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);
}
Expand Down Expand Up @@ -1083,6 +1085,11 @@ void Process_updateCPUFieldWidths(float percentage) {
Row_updateFieldWidth(PERCENT_NORM_CPU, width);
}

void Process_updateShortCwd(Process* this) {
free(this->procCwdShort);
this->procCwdShort = this->procCwd ? CwdUtils_shortenCwd(this->procCwd, 25) : NULL;
}

const ProcessClass Process_class = {
.super = {
.super = {
Expand Down
4 changes: 4 additions & 0 deletions Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,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;

Expand Down Expand Up @@ -328,6 +331,7 @@ const char* Process_getCommand(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_getCommand */
Expand Down
2 changes: 2 additions & 0 deletions ProcessTable.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ static void ProcessTable_cleanupEntries(Table* super) {

// tidy up Process state after refreshing the ProcessTable table
Process_makeCommandStr(p, settings);
if (p->super.host->settings->ss->flags & PROCESS_FLAG_CWD)
Process_updateShortCwd(p);

// keep track of the highest UID for column scaling
if (p->st_uid > host->maxUserId)
Expand Down
80 changes: 80 additions & 0 deletions XUtils.c
Original file line number Diff line number Diff line change
Expand Up @@ -402,3 +402,83 @@ unsigned int countTrailingZeros(unsigned int x) {
return mod37BitPosition[(-x & x) % 37];
}
#endif

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

wcsncpy(data, str, len);
data[len] = L'\0';

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;
}
7 changes: 7 additions & 0 deletions XUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,11 @@ static inline int xDirfd(DIR* dirp) {
return r;
}

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

0 comments on commit 01c686c

Please sign in to comment.