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
Ekin Dursun committed Oct 25, 2021
1 parent 3e70de6 commit e2b9b21
Show file tree
Hide file tree
Showing 8 changed files with 264 additions and 1 deletion.
153 changes: 153 additions & 0 deletions CwdUtils.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#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 @@ -101,6 +102,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 @@ -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 <sys/mkdev.h>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
}
4 changes: 4 additions & 0 deletions Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 */
Expand Down
2 changes: 2 additions & 0 deletions ProcessList.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
80 changes: 80 additions & 0 deletions XUtils.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
7 changes: 7 additions & 0 deletions XUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit e2b9b21

Please sign in to comment.