Skip to content

Commit 7a18024

Browse files
NoCoderRandomclaude
andcommitted
Fix UTF-8 console output and add combined short flag support
- pstree: Set console to UTF-8 (CP_UTF8) so box-drawing characters render correctly instead of mojibake on Windows 11 - df: Fix memory leak in get_fs_type() by returning std::string instead of _strdup; add combined short flag parsing (-Th, -ahl, etc.) - free: Add combined short flag parsing (-ht, -gw, etc.) - ps: Move GlobalMemoryStatusEx call outside per-process loop - htop: Fix swap usage calculation; add UTF-8 console setup - top: Add UTF-8 console setup - All tools: Enable VT processing unconditionally at startup Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 4f1c348 commit 7a18024

6 files changed

Lines changed: 118 additions & 50 deletions

File tree

src/df.cpp

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ static unsigned long long to_blocks(unsigned long long bytes, Unit unit) {
5252
}
5353
}
5454

55-
static const char* get_fs_type(const char* root) {
55+
static std::string get_fs_type_str(const char* root) {
5656
char fs_name[256] = {};
5757
if (GetVolumeInformationA(root, NULL, 0, NULL, NULL, NULL, fs_name, sizeof(fs_name)))
58-
return _strdup(fs_name);
58+
return fs_name;
5959
return "unknown";
6060
}
6161

@@ -80,24 +80,54 @@ int main(int argc, char* argv[]) {
8080
for (int i = 1; i < argc; i++) {
8181
char* a = argv[i];
8282
if (strcmp(a, "--help") == 0) { usage(); return 0; }
83-
else if (strcmp(a, "-a") == 0 || strcmp(a, "--all") == 0) opt_all = true;
84-
else if (strcmp(a, "-h") == 0 || strcmp(a, "--human-readable") == 0) { opt_h = true; unit = HUMAN_1024; }
85-
else if (strcmp(a, "-H") == 0 || strcmp(a, "--si") == 0) { opt_H = true; unit = HUMAN_SI; }
86-
else if (strcmp(a, "-i") == 0 || strcmp(a, "--inodes") == 0) opt_i = true;
87-
else if (strcmp(a, "-k") == 0) { opt_k = true; unit = BLOCK_1K; }
88-
else if (strcmp(a, "-l") == 0 || strcmp(a, "--local") == 0) opt_l = true;
89-
else if (strcmp(a, "-T") == 0 || strcmp(a, "--print-type") == 0) opt_T = true;
90-
else if (strcmp(a, "-P") == 0 || strcmp(a, "--portability") == 0) opt_P = true;
91-
else if ((strcmp(a, "-t") == 0 || strcmp(a, "--type") == 0) && i+1 < argc)
83+
else if (strcmp(a, "--all") == 0) opt_all = true;
84+
else if (strcmp(a, "--human-readable") == 0) { opt_h = true; unit = HUMAN_1024; }
85+
else if (strcmp(a, "--si") == 0) { opt_H = true; unit = HUMAN_SI; }
86+
else if (strcmp(a, "--inodes") == 0) opt_i = true;
87+
else if (strcmp(a, "--local") == 0) opt_l = true;
88+
else if (strcmp(a, "--print-type") == 0) opt_T = true;
89+
else if (strcmp(a, "--portability") == 0) opt_P = true;
90+
else if (strcmp(a, "--type") == 0 && i+1 < argc)
9291
filter_types.push_back(argv[++i]);
9392
else if (strncmp(a, "--type=", 7) == 0) filter_types.push_back(a+7);
94-
else if ((strcmp(a, "-x") == 0 || strcmp(a, "--exclude-type") == 0) && i+1 < argc)
93+
else if (strcmp(a, "--exclude-type") == 0 && i+1 < argc)
9594
exclude_types.push_back(argv[++i]);
9695
else if (strncmp(a, "--exclude-type=", 15) == 0) exclude_types.push_back(a+15);
97-
else if (strncmp(a, "-B", 2) == 0 || strncmp(a, "--block-size=", 13) == 0) {
98-
const char* s = (a[1]=='B' && strlen(a)>2) ? a+2 : (strncmp(a,"--block-size=",13)==0 ? a+13 : NULL);
99-
if (!s && i+1 < argc) s = argv[++i];
100-
if (s) block_size = (unsigned long long)atoll(s);
96+
else if (strncmp(a, "--block-size=", 13) == 0) {
97+
block_size = (unsigned long long)atoll(a+13);
98+
}
99+
else if (a[0] == '-' && a[1] != '-') {
100+
// Handle combined short flags: -Th, -ahT, etc.
101+
bool err = false;
102+
for (int j = 1; a[j] && !err; j++) {
103+
switch (a[j]) {
104+
case 'a': opt_all = true; break;
105+
case 'h': opt_h = true; unit = HUMAN_1024; break;
106+
case 'H': opt_H = true; unit = HUMAN_SI; break;
107+
case 'i': opt_i = true; break;
108+
case 'k': opt_k = true; unit = BLOCK_1K; break;
109+
case 'l': opt_l = true; break;
110+
case 'T': opt_T = true; break;
111+
case 'P': opt_P = true; break;
112+
case 't':
113+
if (a[j+1]) { filter_types.push_back(a+j+1); j=(int)strlen(a)-1; }
114+
else if (i+1 < argc) filter_types.push_back(argv[++i]);
115+
break;
116+
case 'x':
117+
if (a[j+1]) { exclude_types.push_back(a+j+1); j=(int)strlen(a)-1; }
118+
else if (i+1 < argc) exclude_types.push_back(argv[++i]);
119+
break;
120+
case 'B':
121+
if (a[j+1]) { block_size=(unsigned long long)atoll(a+j+1); j=(int)strlen(a)-1; }
122+
else if (i+1 < argc) block_size=(unsigned long long)atoll(argv[++i]);
123+
break;
124+
default:
125+
fprintf(stderr, "df: invalid option -- '%c'\n", a[j]);
126+
fprintf(stderr, "Try 'df --help' for more information.\n");
127+
err = true;
128+
}
129+
}
130+
if (err) return 1;
101131
}
102132
else if (a[0] != '-') paths.push_back(a);
103133
else {
@@ -129,17 +159,17 @@ int main(int argc, char* argv[]) {
129159
UINT dtype = GetDriveTypeA(root);
130160
if (opt_l && dtype == DRIVE_REMOTE) return;
131161

132-
const char* fstype = get_fs_type(root);
162+
std::string fstype = get_fs_type_str(root);
133163

134164
// filter by type
135165
if (!filter_types.empty()) {
136166
bool found = false;
137167
for (auto& t : filter_types)
138-
if (_stricmp(t.c_str(), fstype) == 0) { found = true; break; }
168+
if (_stricmp(t.c_str(), fstype.c_str()) == 0) { found = true; break; }
139169
if (!found) return;
140170
}
141171
for (auto& t : exclude_types)
142-
if (_stricmp(t.c_str(), fstype) == 0) return;
172+
if (_stricmp(t.c_str(), fstype.c_str()) == 0) return;
143173

144174
unsigned long long total = totalBytes.QuadPart;
145175
unsigned long long avail = freeBytesAvail.QuadPart;
@@ -181,7 +211,7 @@ int main(int argc, char* argv[]) {
181211

182212
if (opt_T) {
183213
printf("%-20s %-10s %12s %12s %12s %5s %s\n",
184-
root_trimmed, fstype, size_str, used_str, avail_str, pct_str, mount);
214+
root_trimmed, fstype.c_str(), size_str, used_str, avail_str, pct_str, mount);
185215
} else {
186216
printf("%-20s %12s %12s %12s %5s %s\n",
187217
root_trimmed, size_str, used_str, avail_str, pct_str, mount);

src/free.cpp

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -163,26 +163,51 @@ int main(int argc, char* argv[]) {
163163
for (int i = 1; i < argc; i++) {
164164
char* a = argv[i];
165165
if (strcmp(a, "--help") == 0) { usage(); return 0; }
166-
else if (strcmp(a, "-b") == 0 || strcmp(a, "--bytes") == 0) unit = BYTES;
167-
else if (strcmp(a, "-k") == 0 || strcmp(a, "--kilo") == 0) unit = KILO;
168-
else if (strcmp(a, "-m") == 0 || strcmp(a, "--mega") == 0) unit = MEGA;
169-
else if (strcmp(a, "-g") == 0 || strcmp(a, "--giga") == 0) unit = GIGA;
170-
else if (strcmp(a, "--tera") == 0) unit = TERA;
171-
else if (strcmp(a, "--peta") == 0) unit = PETA;
172-
else if (strcmp(a, "-h") == 0 || strcmp(a, "--human") == 0) unit = HUMAN;
173-
else if (strcmp(a, "--si") == 0) si = true;
174-
else if (strcmp(a, "-t") == 0 || strcmp(a, "--total") == 0) show_total = true;
175-
else if (strcmp(a, "-w") == 0 || strcmp(a, "--wide") == 0) wide = true;
176-
else if (strcmp(a, "-v") == 0 || strcmp(a, "--committed") == 0) committed = true;
177-
else if (strcmp(a, "-l") == 0 || strcmp(a, "--lohi") == 0) {} // ignored on Windows
178-
else if ((strcmp(a, "-s") == 0 || strcmp(a, "--seconds") == 0) && i+1 < argc)
166+
else if (strcmp(a, "--bytes") == 0) unit = BYTES;
167+
else if (strcmp(a, "--kilo") == 0) unit = KILO;
168+
else if (strcmp(a, "--mega") == 0) unit = MEGA;
169+
else if (strcmp(a, "--giga") == 0) unit = GIGA;
170+
else if (strcmp(a, "--tera") == 0) unit = TERA;
171+
else if (strcmp(a, "--peta") == 0) unit = PETA;
172+
else if (strcmp(a, "--human") == 0) unit = HUMAN;
173+
else if (strcmp(a, "--si") == 0) si = true;
174+
else if (strcmp(a, "--total") == 0) show_total = true;
175+
else if (strcmp(a, "--wide") == 0) wide = true;
176+
else if (strcmp(a, "--committed") == 0) committed = true;
177+
else if (strcmp(a, "--lohi") == 0) {} // ignored on Windows
178+
else if (strcmp(a, "--seconds") == 0 && i+1 < argc)
179179
interval = atof(argv[++i]);
180-
else if ((strcmp(a, "-c") == 0 || strcmp(a, "--count") == 0) && i+1 < argc)
180+
else if (strcmp(a, "--count") == 0 && i+1 < argc)
181181
count = atoi(argv[++i]);
182-
else if (a[0] == '-' && (a[1] == 's' || a[1] == 'c') && strlen(a) > 2) {
183-
// -s3 or -c3 forms
184-
if (a[1] == 's') interval = atof(a+2);
185-
else count = atoi(a+2);
182+
else if (a[0] == '-' && a[1] != '-') {
183+
// Handle combined short flags: -ht, -gw, etc.
184+
bool err = false;
185+
for (int j = 1; a[j] && !err; j++) {
186+
switch (a[j]) {
187+
case 'b': unit = BYTES; break;
188+
case 'k': unit = KILO; break;
189+
case 'm': unit = MEGA; break;
190+
case 'g': unit = GIGA; break;
191+
case 'h': unit = HUMAN; break;
192+
case 't': show_total = true; break;
193+
case 'w': wide = true; break;
194+
case 'v': committed = true; break;
195+
case 'l': break; // ignored on Windows
196+
case 's':
197+
if (a[j+1]) { interval = atof(a+j+1); j=(int)strlen(a)-1; }
198+
else if (i+1 < argc) interval = atof(argv[++i]);
199+
break;
200+
case 'c':
201+
if (a[j+1]) { count = atoi(a+j+1); j=(int)strlen(a)-1; }
202+
else if (i+1 < argc) count = atoi(argv[++i]);
203+
break;
204+
default:
205+
fprintf(stderr, "free: invalid option -- '%c'\n", a[j]);
206+
fprintf(stderr, "Try 'free --help' for more information.\n");
207+
err = true;
208+
}
209+
}
210+
if (err) return 1;
186211
}
187212
else {
188213
fprintf(stderr, "free: invalid option '%s'\n", a);

src/htop.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,10 @@ int main(int argc, char* argv[]) {
233233
}
234234
}
235235

236+
// Set console to UTF-8
237+
SetConsoleOutputCP(CP_UTF8);
238+
SetConsoleCP(CP_UTF8);
239+
236240
set_vt_mode();
237241

238242
// Get terminal size
@@ -348,8 +352,10 @@ int main(int argc, char* argv[]) {
348352
unsigned long long used_phys = total_phys - free_phys;
349353
unsigned long long total_swap = ms.ullTotalPageFile > ms.ullTotalPhys ?
350354
ms.ullTotalPageFile - ms.ullTotalPhys : 0;
351-
unsigned long long used_swap = total_swap > ms.ullAvailPageFile ?
352-
total_swap - ms.ullAvailPageFile : 0;
355+
unsigned long long free_swap = ms.ullAvailPageFile > free_phys ?
356+
ms.ullAvailPageFile - free_phys : 0;
357+
if (free_swap > total_swap) free_swap = total_swap;
358+
unsigned long long used_swap = total_swap - free_swap;
353359

354360
// Draw
355361
printf("\033[H"); // cursor home

src/ps.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,10 +337,9 @@ int main(int argc, char* argv[]) {
337337
if (!no_header)
338338
printf("%-12s %6s %5s %5s %8s %8s %4s %4s %-8s %8s %s\n",
339339
"USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY", "STAT", "START", "TIME", "COMMAND");
340+
MEMORYSTATUSEX ms = {}; ms.dwLength = sizeof(ms);
341+
GlobalMemoryStatusEx(&ms);
340342
for (auto* p : shown) {
341-
// %MEM = RSS / total_phys * 100
342-
MEMORYSTATUSEX ms = {}; ms.dwLength = sizeof(ms);
343-
GlobalMemoryStatusEx(&ms);
344343
double mem_pct = ms.ullTotalPhys ? (double)(p->mem_kb * 1024) / ms.ullTotalPhys * 100.0 : 0.0;
345344
printf("%-12s %6lu %5.1f %5.1f %8llu %8llu %4s %4c %-8s %8s %s\n",
346345
p->user.c_str(), (unsigned long)p->pid,

src/pstree.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,16 @@ static std::string get_user(DWORD pid) {
6262
}
6363

6464
int main(int argc, char* argv[]) {
65+
// Set console to UTF-8 so box-drawing characters render correctly
66+
SetConsoleOutputCP(CP_UTF8);
67+
SetConsoleCP(CP_UTF8);
68+
69+
// Enable VT processing for proper rendering
70+
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
71+
DWORD mode = 0;
72+
if (GetConsoleMode(hOut, &mode))
73+
SetConsoleMode(hOut, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
74+
6575
bool show_pids = false;
6676
bool sort_by_pid = false;
6777
bool use_ascii = false;
@@ -156,13 +166,7 @@ int main(int argc, char* argv[]) {
156166
const char* branch_end = use_ascii ? "`-" : "\xe2\x94\x94\xe2\x94\x80";
157167
const char* vert_bar = use_ascii ? "|" : "\xe2\x94\x82";
158168

159-
// Enable VT output for color highlights
160-
if (highlight_pid) {
161-
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
162-
DWORD mode = 0;
163-
if (GetConsoleMode(hOut, &mode))
164-
SetConsoleMode(hOut, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
165-
}
169+
// VT processing already enabled at startup
166170

167171
// Recursive tree printer
168172
// own_prefix: prefix printed on the same line as this node's label

src/top.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ int main(int argc, char* argv[]) {
254254

255255
g_batch = batch;
256256

257+
// Set console to UTF-8
258+
SetConsoleOutputCP(CP_UTF8);
259+
SetConsoleCP(CP_UTF8);
260+
257261
// In batch mode use binary stdout to avoid CRLF on Windows
258262
if (batch) _setmode(_fileno(stdout), _O_BINARY);
259263

0 commit comments

Comments
 (0)