Skip to content

Commit 21b5eca

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 21b5eca

8 files changed

Lines changed: 275 additions & 1 deletion

File tree

CwdUtils.c

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

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(const 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
@@ -21,6 +21,7 @@ in the source distribution for its full text.
2121
#include <sys/resource.h>
2222

2323
#include "CRT.h"
24+
#include "CwdUtils.h"
2425
#include "Hashtable.h"
2526
#include "Machine.h"
2627
#include "Macros.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: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,3 +402,84 @@ 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+
Wstring_safeWcsncpy(data, str, len + 1);
442+
443+
return data;
444+
}
445+
446+
wchar_t** Wstring_split(const wchar_t* s, wchar_t sep, size_t* n) {
447+
const unsigned int rate = 10;
448+
wchar_t** out = xCalloc(rate, sizeof(wchar_t*));
449+
size_t ctr = 0;
450+
unsigned int blocks = rate;
451+
const wchar_t* where;
452+
while ((where = wcschr(s, sep)) != NULL) {
453+
size_t size = (size_t)(where - s);
454+
out[ctr] = xWcsndup(s, size);
455+
ctr++;
456+
if (ctr == blocks) {
457+
blocks += rate;
458+
out = xReallocArray(out, blocks, sizeof(wchar_t*));
459+
}
460+
s += size + 1;
461+
}
462+
if (s[0] != L'\0') {
463+
out[ctr] = xWcsdup(s);
464+
ctr++;
465+
}
466+
out = xRealloc(out, sizeof(wchar_t*) * (ctr + 1));
467+
out[ctr] = NULL;
468+
469+
if (n)
470+
*n = ctr;
471+
472+
return out;
473+
}
474+
475+
size_t Wstring_safeWcsncpy(wchar_t* restrict dest, const wchar_t* restrict src, size_t size) {
476+
assert(size > 0);
477+
478+
size_t i = 0;
479+
for (; i < size - 1 && src[i]; i++)
480+
dest[i] = src[i];
481+
482+
dest[i] = L'\0';
483+
484+
return i;
485+
}

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)