Skip to content

Commit 25d449d

Browse files
author
Ekin Dursun
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 3e70de6 commit 25d449d

8 files changed

Lines changed: 266 additions & 1 deletion

File tree

CwdUtils.c

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

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 \
@@ -101,6 +102,7 @@ myhtopheaders = \
101102
CommandLine.h \
102103
CommandScreen.h \
103104
Compat.h \
105+
CwdUtils.h \
104106
DateMeter.h \
105107
DateTimeMeter.h \
106108
DiskIOMeter.h \

Process.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ in the source distribution for its full text.
3030
#include "RichString.h"
3131
#include "Settings.h"
3232
#include "XUtils.h"
33+
#include "CwdUtils.h"
3334

3435
#if defined(MAJOR_IN_MKDEV)
3536
#include <sys/mkdev.h>
@@ -733,6 +734,8 @@ void Process_printPercentage(float val, char* buffer, int n, int* attr) {
733734
}
734735
}
735736

737+
738+
736739
void Process_writeField(const Process* this, RichString* str, ProcessField field) {
737740
char buffer[256];
738741
size_t n = sizeof(buffer);
@@ -825,7 +828,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
825828
attr = CRT_colors[PROCESS_SHADOW];
826829
cwd = "main thread terminated";
827830
} else {
828-
cwd = this->procCwd;
831+
cwd = this->procCwdShort;
829832
}
830833
Process_printLeftAlignedField(str, attr, cwd, 25);
831834
return;
@@ -955,6 +958,7 @@ void Process_done(Process* this) {
955958
free(this->procComm);
956959
free(this->procExe);
957960
free(this->procCwd);
961+
free(this->procCwdShort);
958962
free(this->mergedCommand.str);
959963
free(this->tty_name);
960964
}
@@ -1238,3 +1242,8 @@ void Process_updateExe(Process* this, const char* exe) {
12381242
}
12391243
this->mergedCommand.exeChanged = true;
12401244
}
1245+
1246+
void Process_updateShortCwd(Process* this) {
1247+
free(this->procCwdShort);
1248+
this->procCwdShort = this->procCwd ? CwdUtils_shortenCwd(this->procCwd, 25) : NULL;
1249+
}

Process.h

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

160+
/* Shortened process/thread working directory */
161+
char* procCwdShort;
162+
160163
/* Offset in procExe of the process basename */
161164
int procExeBasenameOffset;
162165

@@ -388,6 +391,7 @@ const char* Process_getCommandStr(const Process* this);
388391
void Process_updateComm(Process* this, const char* comm);
389392
void Process_updateCmdline(Process* this, const char* cmdline, int basenameStart, int basenameEnd);
390393
void Process_updateExe(Process* this, const char* exe);
394+
void Process_updateShortCwd(Process* this);
391395

392396
/* This function constructs the string that is displayed by
393397
* Process_writeCommand and also returned by Process_getCommandStr */

ProcessList.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,8 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
629629
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
630630
Process* p = (Process*) Vector_get(this->processes, i);
631631
Process_makeCommandStr(p);
632+
if (p->settings->flags & PROCESS_FLAG_CWD)
633+
Process_updateShortCwd(p);
632634

633635
if (p->tombStampMs > 0) {
634636
// remove tombed process

XUtils.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,3 +323,83 @@ ssize_t xReadfileat(openat_arg_t dirfd, const char* pathname, void* buffer, size
323323

324324
return readfd_internal(fd, buffer, count);
325325
}
326+
327+
wchar_t* xMbstowcs(const char *mbs) {
328+
size_t len = strlen(mbs);
329+
wchar_t *wcs = xCalloc(len + 1, sizeof(wchar_t));
330+
mbstate_t mbstate = {0};
331+
mbsrtowcs(wcs, &mbs, len + 1, &mbstate);
332+
if (mbs)
333+
fail();
334+
return wcs;
335+
}
336+
337+
char* xWcstombs(const wchar_t *wcs) {
338+
size_t len = wcslen(wcs);
339+
size_t mbsSize = len * sizeof(wchar_t) + 1;
340+
char *mbs = xMalloc(mbsSize);
341+
mbstate_t mbstate = {0};
342+
wcsrtombs(mbs, &wcs, mbsSize, &mbstate);
343+
if (wcs)
344+
fail();
345+
return mbs;
346+
}
347+
348+
wchar_t* xWcsdup(const wchar_t* str) {
349+
wchar_t* data = wcsdup(str);
350+
if (!data)
351+
fail();
352+
return data;
353+
}
354+
355+
wchar_t* xWcsndup(const wchar_t* str, size_t len) {
356+
wchar_t* data = xCalloc(len + 1, sizeof(wchar_t));
357+
if (!data)
358+
fail();
359+
360+
for (size_t i = 0; i < len && str[i]; i++)
361+
data[i] = str[i];
362+
363+
return data;
364+
}
365+
366+
wchar_t** Wstring_split(const wchar_t* s, wchar_t sep, size_t* n) {
367+
const unsigned int rate = 10;
368+
wchar_t** out = xCalloc(rate, sizeof(wchar_t*));
369+
size_t ctr = 0;
370+
unsigned int blocks = rate;
371+
const wchar_t* where;
372+
while ((where = wcschr(s, sep)) != NULL) {
373+
size_t size = (size_t)(where - s);
374+
out[ctr] = xWcsndup(s, size);
375+
ctr++;
376+
if (ctr == blocks) {
377+
blocks += rate;
378+
out = (wchar_t**) xRealloc(out, sizeof(wchar_t*) * blocks);
379+
}
380+
s += size + 1;
381+
}
382+
if (s[0] != L'\0') {
383+
out[ctr] = xWcsdup(s);
384+
ctr++;
385+
}
386+
out = xRealloc(out, sizeof(wchar_t*) * (ctr + 1));
387+
out[ctr] = NULL;
388+
389+
if (n)
390+
*n = ctr;
391+
392+
return out;
393+
}
394+
395+
size_t Wstring_safeWcsncpy(wchar_t* restrict dest, const wchar_t* restrict src, size_t size) {
396+
assert(size > 0);
397+
398+
size_t i = 0;
399+
for (; i < size - 1 && src[i]; i++)
400+
dest[i] = src[i];
401+
402+
dest[i] = L'\0';
403+
404+
return i;
405+
}

XUtils.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,11 @@ char* xStrndup(const char* str, size_t len) ATTR_NONNULL ATTR_MALLOC;
7575
ssize_t xReadfile(const char* pathname, void* buffer, size_t count);
7676
ssize_t xReadfileat(openat_arg_t dirfd, const char* pathname, void* buffer, size_t count);
7777

78+
wchar_t* xMbstowcs(const char *mbs);
79+
char* xWcstombs(const wchar_t *wcs);
80+
wchar_t* xWcsdup(const wchar_t* str);
81+
wchar_t* xWcsndup(const wchar_t* str, size_t len);
82+
wchar_t** Wstring_split(const wchar_t* s, wchar_t sep, size_t* n);
83+
size_t Wstring_safeWcsncpy(wchar_t* restrict dest, const wchar_t* restrict src, size_t size);
84+
7885
#endif

0 commit comments

Comments
 (0)