Skip to content

Commit 4d439ae

Browse files
committed
Shorten CWD path
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.
1 parent 4e6a781 commit 4d439ae

8 files changed

Lines changed: 268 additions & 1 deletion

File tree

CwdUtils.c

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#include "config.h" // IWYU pragma: keep
2+
3+
#include "CwdUtils.h"
4+
5+
#include <stdbool.h>
6+
#include <stdlib.h>
7+
#include <wchar.h>
8+
9+
#include "XUtils.h"
10+
11+
typedef struct ShortenCwdContext {
12+
size_t maxLength;
13+
size_t len;
14+
wchar_t **parts;
15+
size_t partsLen;
16+
size_t *partLengths;
17+
} ShortenCwdContext;
18+
19+
static void shortenCwdParts(ShortenCwdContext *ctx) {
20+
for (int i = ctx->partsLen - 2; i >= 0 && ctx->len > ctx->maxLength; i--) {
21+
if (ctx->partLengths[i] < 3)
22+
continue;
23+
24+
size_t extraChars = ctx->len - ctx->maxLength;
25+
size_t maxRemovableChars = ctx->partLengths[i] - 2;
26+
size_t charsToRemove = extraChars < maxRemovableChars ? extraChars : maxRemovableChars;
27+
28+
ctx->partLengths[i] -= charsToRemove;
29+
ctx->len -= charsToRemove;
30+
31+
Wstring_safeWcsncpy(ctx->parts[i] + (ctx->partLengths[i] - 1), L"~", 2);
32+
}
33+
}
34+
35+
static size_t collapseCwdParts(ShortenCwdContext *ctx, bool doActualWork) {
36+
if (ctx->len <= ctx->maxLength || ctx->partsLen <= 3)
37+
return 0;
38+
39+
size_t len = ctx->len;
40+
41+
size_t i;
42+
for (i = ctx->partsLen - 2; i > 1; i--) {
43+
if (len + (3 - ctx->partLengths[i]) <= ctx->maxLength)
44+
break;
45+
46+
len -= ctx->partLengths[i] + 1;
47+
48+
if (doActualWork) {
49+
ctx->partLengths[i] = 0;
50+
free(ctx->parts[i]);
51+
ctx->parts[i] = NULL;
52+
}
53+
}
54+
55+
len += 3 - ctx->partLengths[i];
56+
size_t diff = ctx->len - len;
57+
58+
if (doActualWork) {
59+
wchar_t newPart[] = L"~~~";
60+
newPart[0] = ctx->parts[i][0];
61+
free(ctx->parts[i]);
62+
ctx->parts[i] = xWcsdup(newPart);
63+
ctx->partLengths[i] = 3;
64+
ctx->len = len;
65+
}
66+
67+
return diff;
68+
}
69+
70+
static size_t shortenCwdLastPart(ShortenCwdContext *ctx, bool doActualWork) {
71+
if (ctx->len <= ctx->maxLength)
72+
return 0;
73+
74+
size_t lastPartLen = ctx->partLengths[ctx->partsLen - 1];
75+
if (lastPartLen <= 3)
76+
return 0;
77+
78+
wchar_t *lastPart = ctx->parts[ctx->partsLen - 1];
79+
size_t extraChars = ctx->len - ctx->maxLength + 1;
80+
size_t maxRemovableChars = lastPartLen - 2;
81+
size_t charsToRemove = extraChars < maxRemovableChars ? extraChars : maxRemovableChars;
82+
83+
if (doActualWork) {
84+
size_t charsAtBeginning = (lastPartLen - charsToRemove + 1) / 2;
85+
size_t charsAtEnd = lastPartLen - charsToRemove - charsAtBeginning;
86+
lastPart[charsAtBeginning] = '~';
87+
wmemmove(lastPart + charsAtBeginning + 1, lastPart + lastPartLen - charsAtEnd, charsAtEnd);
88+
lastPart[charsAtBeginning + charsAtEnd + 1] = '\0';
89+
ctx->partLengths[ctx->partsLen - 1] = lastPartLen - charsToRemove + 1;
90+
ctx->len -= charsToRemove - 1;
91+
}
92+
93+
return charsToRemove - 1;
94+
}
95+
96+
static wchar_t* buildCwdFromParts(ShortenCwdContext *ctx) {
97+
size_t len = ctx->partsLen - 1;
98+
for (size_t i = 0; i < ctx->partsLen; i++)
99+
len += ctx->partLengths[i];
100+
101+
wchar_t *newCwd = xCalloc(len + 1, sizeof(wchar_t));
102+
103+
newCwd[0] = '\0';
104+
for (size_t i = 0, writeIndex = 0; i < ctx->partsLen; i++) {
105+
if (!ctx->parts[i])
106+
continue;
107+
108+
Wstring_safeWcsncpy(newCwd + writeIndex, ctx->parts[i], ctx->partLengths[i] + 1);
109+
writeIndex += ctx->partLengths[i];
110+
if (i < ctx->partsLen - 1)
111+
newCwd[writeIndex++] = L'/';
112+
}
113+
114+
return newCwd;
115+
}
116+
117+
char* CwdUtils_shortenCwd(char *cwd, const size_t maxLength) {
118+
wchar_t *wcwd = xMbstowcs(cwd);
119+
size_t len = wcslen(wcwd);
120+
if (len <= maxLength) {
121+
free(wcwd);
122+
return xStrdup(cwd);
123+
}
124+
125+
ShortenCwdContext ctx = {
126+
.maxLength = maxLength,
127+
.len = len,
128+
};
129+
ctx.parts = Wstring_split(wcwd, L'/', &ctx.partsLen);
130+
free(wcwd);
131+
wcwd = NULL;
132+
ctx.partLengths = xCalloc(ctx.partsLen, sizeof(size_t));
133+
for (size_t i = 0; i < ctx.partsLen; i++)
134+
ctx.partLengths[i] = wcslen(ctx.parts[i]);
135+
136+
shortenCwdParts(&ctx);
137+
if (shortenCwdLastPart(&ctx, false) > collapseCwdParts(&ctx, false)) {
138+
shortenCwdLastPart(&ctx, true);
139+
collapseCwdParts(&ctx, true);
140+
} else {
141+
collapseCwdParts(&ctx, true);
142+
shortenCwdLastPart(&ctx, true);
143+
}
144+
145+
wchar_t *newWcwd = buildCwdFromParts(&ctx);
146+
char *newCwd = xWcstombs(newWcwd);
147+
free(newWcwd);
148+
149+
free(ctx.partLengths);
150+
for (size_t i = 0; i < ctx.partsLen; i++)
151+
free(ctx.parts[i]);
152+
free(ctx.parts);
153+
154+
return newCwd;
155+
}

CwdUtils.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef HEADER_CwdUtils
2+
#define HEADER_CwdUtils
3+
4+
#include <stddef.h>
5+
6+
char* CwdUtils_shortenCwd(char* cwd, size_t maxLength);
7+
8+
#endif

Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ myhtopsources = \
4343
Compat.c \
4444
CPUMeter.c \
4545
CRT.c \
46+
CwdUtils.c \
4647
DateMeter.c \
4748
DateTimeMeter.c \
4849
DiskIOMeter.c \
@@ -109,6 +110,7 @@ myhtopheaders = \
109110
CommandLine.h \
110111
CommandScreen.h \
111112
Compat.h \
113+
CwdUtils.h \
112114
DateMeter.h \
113115
DateTimeMeter.h \
114116
DiskIOMeter.h \

Process.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ in the source distribution for its full text.
3131
#include "Settings.h"
3232
#include "Table.h"
3333
#include "XUtils.h"
34+
#include "CwdUtils.h"
3435

3536
#if defined(MAJOR_IN_MKDEV)
3637
#include <sys/mkdev.h>
@@ -630,7 +631,7 @@ void Process_writeField(const Process* this, RichString* str, RowField field) {
630631
attr = CRT_colors[PROCESS_SHADOW];
631632
cwd = "main thread terminated";
632633
} else {
633-
cwd = this->procCwd;
634+
cwd = this->procCwdShort;
634635
}
635636
Row_printLeftAlignedField(str, attr, cwd, 25);
636637
return;
@@ -770,6 +771,7 @@ void Process_done(Process* this) {
770771
free(this->procComm);
771772
free(this->procExe);
772773
free(this->procCwd);
774+
free(this->procCwdShort);
773775
free(this->mergedCommand.str);
774776
free(this->tty_name);
775777
}
@@ -1083,6 +1085,11 @@ void Process_updateCPUFieldWidths(float percentage) {
10831085
Row_updateFieldWidth(PERCENT_NORM_CPU, width);
10841086
}
10851087

1088+
void Process_updateShortCwd(Process* this) {
1089+
free(this->procCwdShort);
1090+
this->procCwdShort = this->procCwd ? CwdUtils_shortenCwd(this->procCwd, 25) : NULL;
1091+
}
1092+
10861093
const ProcessClass Process_class = {
10871094
.super = {
10881095
.super = {

Process.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ typedef struct Process_ {
143143
/* The process/thread working directory */
144144
char* procCwd;
145145

146+
/* Shortened process/thread working directory */
147+
char* procCwdShort;
148+
146149
/* Offset in procExe of the process basename */
147150
int procExeBasenameOffset;
148151

@@ -328,6 +331,7 @@ const char* Process_getCommand(const Process* this);
328331
void Process_updateComm(Process* this, const char* comm);
329332
void Process_updateCmdline(Process* this, const char* cmdline, int basenameStart, int basenameEnd);
330333
void Process_updateExe(Process* this, const char* exe);
334+
void Process_updateShortCwd(Process* this);
331335

332336
/* This function constructs the string that is displayed by
333337
* Process_writeCommand and also returned by Process_getCommand */

ProcessTable.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ static void ProcessTable_cleanupEntries(Table* super) {
6969

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

7375
// keep track of the highest UID for column scaling
7476
if (p->st_uid > host->maxUserId)

XUtils.c

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,3 +402,85 @@ unsigned int countTrailingZeros(unsigned int x) {
402402
return mod37BitPosition[(-x & x) % 37];
403403
}
404404
#endif
405+
406+
wchar_t* xMbstowcs(const char *mbs) {
407+
size_t len = strlen(mbs);
408+
wchar_t *wcs = xCalloc(len + 1, sizeof(wchar_t));
409+
mbstate_t mbstate = {0};
410+
mbsrtowcs(wcs, &mbs, len + 1, &mbstate);
411+
if (mbs)
412+
fail();
413+
return wcs;
414+
}
415+
416+
char* xWcstombs(const wchar_t *wcs) {
417+
size_t len = wcslen(wcs);
418+
if (SIZE_MAX / MB_CUR_MAX <= len)
419+
fail();
420+
size_t mbsSize = len * MB_CUR_MAX + 1;
421+
char *mbs = xMalloc(mbsSize);
422+
mbstate_t mbstate = {0};
423+
wcsrtombs(mbs, &wcs, mbsSize, &mbstate);
424+
if (wcs)
425+
fail();
426+
return mbs;
427+
}
428+
429+
wchar_t* xWcsdup(const wchar_t* str) {
430+
wchar_t* data = wcsdup(str);
431+
if (!data)
432+
fail();
433+
return data;
434+
}
435+
436+
wchar_t* xWcsndup(const wchar_t* str, size_t len) {
437+
wchar_t* data = xCalloc(len + 1, sizeof(wchar_t));
438+
if (!data)
439+
fail();
440+
441+
wcsncpy(data, str, len);
442+
data[len] = L'\0';
443+
444+
return data;
445+
}
446+
447+
wchar_t** Wstring_split(const wchar_t* s, wchar_t sep, size_t* n) {
448+
const unsigned int rate = 10;
449+
wchar_t** out = xCalloc(rate, sizeof(wchar_t*));
450+
size_t ctr = 0;
451+
unsigned int blocks = rate;
452+
const wchar_t* where;
453+
while ((where = wcschr(s, sep)) != NULL) {
454+
size_t size = (size_t)(where - s);
455+
out[ctr] = xWcsndup(s, size);
456+
ctr++;
457+
if (ctr == blocks) {
458+
blocks += rate;
459+
out = xReallocArray(out, blocks, sizeof(wchar_t*));
460+
}
461+
s += size + 1;
462+
}
463+
if (s[0] != L'\0') {
464+
out[ctr] = xWcsdup(s);
465+
ctr++;
466+
}
467+
out = xRealloc(out, sizeof(wchar_t*) * (ctr + 1));
468+
out[ctr] = NULL;
469+
470+
if (n)
471+
*n = ctr;
472+
473+
return out;
474+
}
475+
476+
size_t Wstring_safeWcsncpy(wchar_t* restrict dest, const wchar_t* restrict src, size_t size) {
477+
assert(size > 0);
478+
479+
size_t i = 0;
480+
for (; i < size - 1 && src[i]; i++)
481+
dest[i] = src[i];
482+
483+
dest[i] = L'\0';
484+
485+
return i;
486+
}

XUtils.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,4 +174,11 @@ static inline int xDirfd(DIR* dirp) {
174174
return r;
175175
}
176176

177+
wchar_t* xMbstowcs(const char *mbs);
178+
char* xWcstombs(const wchar_t *wcs);
179+
wchar_t* xWcsdup(const wchar_t* str);
180+
wchar_t* xWcsndup(const wchar_t* str, size_t len);
181+
wchar_t** Wstring_split(const wchar_t* s, wchar_t sep, size_t* n);
182+
size_t Wstring_safeWcsncpy(wchar_t* restrict dest, const wchar_t* restrict src, size_t size);
183+
177184
#endif

0 commit comments

Comments
 (0)