aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.custom4
-rw-r--r--NOFORK_NOEXEC.lst1
-rw-r--r--applets/usage_pod.c17
-rw-r--r--configs/mingw64a_defconfig2
-rw-r--r--coreutils/date.c2
-rw-r--r--coreutils/ls.c246
-rw-r--r--coreutils/md5_sha1_sum.c58
-rw-r--r--e2fsprogs/fsck.c4
-rw-r--r--include/libbb.h68
-rw-r--r--include/platform.h2
-rw-r--r--init/init.c2
-rw-r--r--libbb/c_escape.c20
-rw-r--r--libbb/concat_path_file.c75
-rw-r--r--libbb/dump.c55
-rw-r--r--libbb/getopt32.c13
-rw-r--r--libbb/hash_md5_sha.c72
-rw-r--r--libbb/lineedit.c2
-rw-r--r--libbb/procps.c174
-rw-r--r--libbb/replace.c14
-rw-r--r--libbb/yescrypt/alg-sha256.c13
-rw-r--r--miscutils/crond.c10
-rw-r--r--modutils/modprobe-small.c21
-rw-r--r--modutils/modprobe.c2
-rw-r--r--modutils/modutils.c9
-rw-r--r--modutils/modutils.h1
-rw-r--r--networking/ftpd.c39
-rw-r--r--procps/pmap.c126
-rw-r--r--procps/top.c351
-rw-r--r--shell/ash.c15
-rw-r--r--shell/hush.c463
-rwxr-xr-xshell/hush_leaktool.sh18
-rwxr-xr-xshell/hush_test/hush-misc/sig_exitcode.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait1.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait2.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait3.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait4.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait5.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait6.tests3
-rwxr-xr-xshell/hush_test/hush-signals/catch.tests3
-rwxr-xr-xshell/hush_test/hush-signals/signal1.tests3
-rwxr-xr-xshell/hush_test/hush-signals/signal8.tests3
-rwxr-xr-xshell/hush_test/hush-signals/signal_read2.tests3
-rwxr-xr-xshell/hush_test/hush-signals/subshell.tests3
-rwxr-xr-xshell/hush_test/run-all7
-rwxr-xr-xtestsuite/hexdump.tests10
-rwxr-xr-xtestsuite/ls.tests26
-rwxr-xr-xtestsuite/md5sum.tests7
-rwxr-xr-xtestsuite/sha384sum.tests3
-rwxr-xr-xtestsuite/sha3sum.tests6
-rw-r--r--util-linux/lspci.c2
-rw-r--r--util-linux/lsusb.c2
-rw-r--r--win32/process.c3
52 files changed, 1265 insertions, 736 deletions
diff --git a/Makefile.custom b/Makefile.custom
index e2f6d1c2f..36170de8a 100644
--- a/Makefile.custom
+++ b/Makefile.custom
@@ -156,12 +156,12 @@ docs/busybox.pod: $(srctree)/docs/busybox_header.pod \
156docs/BusyBox.txt: docs/busybox.pod 156docs/BusyBox.txt: docs/busybox.pod
157 $(disp_doc) 157 $(disp_doc)
158 $(Q)-mkdir -p docs 158 $(Q)-mkdir -p docs
159 $(Q)-pod2text $< > $@ 159 $(Q)-pod2text --quotes=none $< > $@
160 160
161docs/busybox.1: docs/busybox.pod 161docs/busybox.1: docs/busybox.pod
162 $(disp_doc) 162 $(disp_doc)
163 $(Q)-mkdir -p docs 163 $(Q)-mkdir -p docs
164 $(Q)-pod2man --center=busybox --release="version $(KERNELVERSION)" $< > $@ 164 $(Q)-pod2man --quotes=none --center=busybox --release="version $(KERNELVERSION)" $< > $@
165 165
166docs/BusyBox.html: docs/busybox.net/BusyBox.html 166docs/BusyBox.html: docs/busybox.net/BusyBox.html
167 $(disp_doc) 167 $(disp_doc)
diff --git a/NOFORK_NOEXEC.lst b/NOFORK_NOEXEC.lst
index 055f9fb24..a000de45b 100644
--- a/NOFORK_NOEXEC.lst
+++ b/NOFORK_NOEXEC.lst
@@ -336,6 +336,7 @@ setuidgid - noexec. spawner
336sha1sum - noexec. runner 336sha1sum - noexec. runner
337sha256sum - noexec. runner 337sha256sum - noexec. runner
338sha3sum - noexec. runner 338sha3sum - noexec. runner
339sha384sum - noexec. runner
339sha512sum - noexec. runner 340sha512sum - noexec. runner
340showkey - interactive, longterm 341showkey - interactive, longterm
341shred - runner 342shred - runner
diff --git a/applets/usage_pod.c b/applets/usage_pod.c
index 9e6d3f0ee..2c177be90 100644
--- a/applets/usage_pod.c
+++ b/applets/usage_pod.c
@@ -67,30 +67,37 @@ int main(void)
67 } 67 }
68 if (col == 0) { 68 if (col == 0) {
69 col = 6; 69 col = 6;
70 printf("\t");
71 } else { 70 } else {
72 printf(", "); 71 printf(", ");
73 } 72 }
74 printf("%s", usage_array[i].aname); 73 if (usage_array[i].usage[0] != NOUSAGE_STR[0]) {
74 /*
75 * If the applet usage string will be included in the final document
76 * optimistically link to its header (which is just the applet name).
77 */
78 printf("L<C<%1$s>|/\"%1$s\">", usage_array[i].aname);
79 } else {
80 /* Without a usage string, just output the applet name with no link. */
81 printf("C<%s>", usage_array[i].aname);
82 }
75 col += len2; 83 col += len2;
76 } 84 }
77 printf("\n\n"); 85 printf("\n\n");
78 86
79 printf("=head1 COMMAND DESCRIPTIONS\n\n"); 87 printf("=head1 COMMAND DESCRIPTIONS\n\n");
80 printf("=over 4\n\n");
81 88
82 for (i = 0; i < num_messages; i++) { 89 for (i = 0; i < num_messages; i++) {
83 if (usage_array[i].aname[0] >= 'a' && usage_array[i].aname[0] <= 'z' 90 if (usage_array[i].aname[0] >= 'a' && usage_array[i].aname[0] <= 'z'
84 && usage_array[i].usage[0] != NOUSAGE_STR[0] 91 && usage_array[i].usage[0] != NOUSAGE_STR[0]
85 ) { 92 ) {
86 printf("=item B<%s>\n\n", usage_array[i].aname); 93 /* This is the heading that will be linked from the command list. */
94 printf("=head2 %s\n\n", usage_array[i].aname);
87 if (usage_array[i].usage[0]) 95 if (usage_array[i].usage[0])
88 printf("%s %s\n\n", usage_array[i].aname, usage_array[i].usage); 96 printf("%s %s\n\n", usage_array[i].aname, usage_array[i].usage);
89 else 97 else
90 printf("%s\n\n", usage_array[i].aname); 98 printf("%s\n\n", usage_array[i].aname);
91 } 99 }
92 } 100 }
93 printf("=back\n\n");
94 101
95 return 0; 102 return 0;
96} 103}
diff --git a/configs/mingw64a_defconfig b/configs/mingw64a_defconfig
index f23dab438..d17737721 100644
--- a/configs/mingw64a_defconfig
+++ b/configs/mingw64a_defconfig
@@ -121,7 +121,7 @@ CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
121# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set 121# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
122# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set 122# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
123CONFIG_PASSWORD_MINLEN=6 123CONFIG_PASSWORD_MINLEN=6
124# CONFIG_FEATURE_USE_CNG_API is not set 124CONFIG_FEATURE_USE_CNG_API=y
125CONFIG_MD5_SMALL=1 125CONFIG_MD5_SMALL=1
126CONFIG_SHA1_SMALL=3 126CONFIG_SHA1_SMALL=3
127# CONFIG_SHA1_HWACCEL is not set 127# CONFIG_SHA1_HWACCEL is not set
diff --git a/coreutils/date.c b/coreutils/date.c
index 3a89b6caf..ef482af1b 100644
--- a/coreutils/date.c
+++ b/coreutils/date.c
@@ -289,7 +289,7 @@ int date_main(int argc UNUSED_PARAM, char **argv)
289 289
290 /* if setting time, set it */ 290 /* if setting time, set it */
291 if ((opt & OPT_SET) && clock_settime(CLOCK_REALTIME, &ts) < 0) { 291 if ((opt & OPT_SET) && clock_settime(CLOCK_REALTIME, &ts) < 0) {
292 bb_simple_perror_msg("can't set date"); 292 bb_simple_perror_msg_and_die("can't set date");
293 } 293 }
294 } 294 }
295 295
diff --git a/coreutils/ls.c b/coreutils/ls.c
index 5d2e96a1e..2153554e8 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -129,6 +129,8 @@
129//usage: "\n -F Append indicator (one of */=@|) to names" 129//usage: "\n -F Append indicator (one of */=@|) to names"
130//usage: ) 130//usage: )
131//usage: "\n -l Long format" 131//usage: "\n -l Long format"
132////usage: "\n -g Long format without group column"
133////TODO: support -G too ("suppress owner column", GNUism)
132//usage: "\n -i List inode numbers" 134//usage: "\n -i List inode numbers"
133//usage: "\n -n List numeric UIDs and GIDs instead of names" 135//usage: "\n -n List numeric UIDs and GIDs instead of names"
134//usage: "\n -s List allocated blocks" 136//usage: "\n -s List allocated blocks"
@@ -162,6 +164,8 @@
162//usage: IF_FEATURE_LS_WIDTH( 164//usage: IF_FEATURE_LS_WIDTH(
163//usage: "\n -w N Format N columns wide" 165//usage: "\n -w N Format N columns wide"
164//usage: ) 166//usage: )
167////usage: "\n -Q Double-quote names"
168////usage: "\n -q Replace unprintable chars with '?'"
165//usage: IF_FEATURE_LS_COLOR( 169//usage: IF_FEATURE_LS_COLOR(
166//usage: "\n --color[={always,never,auto}]" 170//usage: "\n --color[={always,never,auto}]"
167//usage: ) 171//usage: )
@@ -199,27 +203,47 @@ SPLIT_SUBDIR = 2,
199 203
200/* -Cadi1l Std options, busybox always supports */ 204/* -Cadi1l Std options, busybox always supports */
201/* -gnsxA Std options, busybox always supports */ 205/* -gnsxA Std options, busybox always supports */
202/* -Q GNU option, busybox always supports */ 206/* -Q GNU option, busybox always supports: */
203/* -k Std option, busybox always supports (by ignoring) */ 207/* -Q, --quote-name */
204/* It means "for -s, show sizes in kbytes" */ 208/* enclose entry names in double quotes */
205/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
206/* since otherwise -s shows kbytes anyway */
207/* -LHRctur Std options, busybox optionally supports */ 209/* -LHRctur Std options, busybox optionally supports */
208/* -Fp Std options, busybox optionally supports */ 210/* -Fp Std options, busybox optionally supports */
209/* -SXvhTw GNU options, busybox optionally supports */ 211/* -SXvhTw GNU options, busybox optionally supports */
210/* -T WIDTH Ignored (we don't use tabs on output) */ 212/* -T WIDTH Ignored (we don't use tabs on output) */
211/* -Z SELinux mandated option, busybox optionally supports */ 213/* -Z SELinux mandated option, busybox optionally supports */
214/* -q Std option, busybox always supports: */
215/* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html: */
216/* Force each instance of non-printable filename characters and */
217/* <tab> characters to be written as the <question-mark> ('?') */
218/* character. Implementations may provide this option by default */
219/* if the output is to a terminal device. */
220/* -k Std option, busybox always supports (by ignoring) */
221/* It means "for -s, show sizes in kbytes" */
222/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
223/* since otherwise -s shows kbytes anyway */
212#define ls_options \ 224#define ls_options \
213 "Cadi1lgnsxAk" /* 12 opts, total 12 */ \ 225 "Cadi1lgnsxA" /* 11 opts, total 11 */ \
214 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 14 */ \ 226 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 13 */ \
215 IF_FEATURE_LS_RECURSIVE("R") /* 1, 15 */ \ 227 IF_FEATURE_LS_RECURSIVE("R") /* 1, 14 */ \
216 IF_SELINUX("Z") /* 1, 16 */ \ 228 IF_SELINUX("Z") /* 1, 15 */ \
217 "Q" /* 1, 17 */ \ 229 "Q" /* 1, 16 */ \
218 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 20 */ \ 230 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 19 */ \
219 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 24 */ \ 231 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 23 */ \
220 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 26 */ \ 232 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 25 */ \
221 IF_FEATURE_HUMAN_READABLE("h") /* 1, 27 */ \ 233 IF_FEATURE_HUMAN_READABLE("h") /* 1, 26 */ \
222 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 29 */ 234 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 28 */ \
235 IF_LONG_OPTS("\xff") /* 1, 29 */ \
236 IF_LONG_OPTS("\xfe") /* 1, 30 */ \
237 IF_LONG_OPTS("\xfd") /* 1, 31 */ \
238 "qk" /* 2, 33 */
239
240#if ENABLE_LONG_OPTS
241static const char ls_longopts[] ALIGN1 =
242 "full-time\0" No_argument "\xff"
243 "group-directories-first\0" No_argument "\xfe"
244 IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd")
245;
246#endif
223 247
224enum { 248enum {
225 OPT_C = (1 << 0), 249 OPT_C = (1 << 0),
@@ -233,29 +257,31 @@ enum {
233 OPT_s = (1 << 8), 257 OPT_s = (1 << 8),
234 OPT_x = (1 << 9), 258 OPT_x = (1 << 9),
235 OPT_A = (1 << 10), 259 OPT_A = (1 << 10),
236 //OPT_k = (1 << 11),
237 260
238 OPTBIT_F = 12, 261 OPTBIT_F = 11,
239 OPTBIT_p, /* 13 */ 262 OPTBIT_p, /* 12 */
240 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES, 263 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
241 OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE, 264 OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
242 OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX, 265 OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX,
243 OPTBIT_c, /* 17 */ 266 OPTBIT_c, /* 16 */
244 OPTBIT_t, /* 18 */ 267 OPTBIT_t, /* 17 */
245 OPTBIT_u, /* 19 */ 268 OPTBIT_u, /* 18 */
246 OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS, 269 OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS,
247 OPTBIT_X, /* 21 */ 270 OPTBIT_X, /* 20 */
248 OPTBIT_r, /* 22 */ 271 OPTBIT_r, /* 21 */
249 OPTBIT_v, /* 23 */ 272 OPTBIT_v, /* 22 */
250 OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES, 273 OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
251 OPTBIT_H, /* 25 */ 274 OPTBIT_H, /* 24 */
252 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS, 275 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
253 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE, 276 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
254 OPTBIT_w, /* 28 */ 277 OPTBIT_w, /* 27 */
255 OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH, 278 OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH,
256 OPTBIT_dirs_first, 279 OPTBIT_dirs_first,
257 OPTBIT_color, /* 31 */ 280 OPTBIT_color, /* 30 */
258 /* with long opts, we use all 32 bits */ 281 OPTBIT_q = OPTBIT_color + 1, /* 31 */
282 OPTBIT_k = OPTBIT_q + 1, /* 32 */
283 /* with all options enabled, we use all 32 bits and even one extra bit! */
284 /* this works because -k is ignored, and getopt32 allows such "ignore" options past 31th bit */
259 285
260 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES, 286 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
261 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES, 287 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
@@ -277,6 +303,8 @@ enum {
277 OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS, 303 OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS,
278 OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS, 304 OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS,
279 OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR, 305 OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR,
306 OPT_q = (1 << OPTBIT_q),
307 //-k is ignored: OPT_k = (1 << OPTBIT_k),
280}; 308};
281 309
282/* 310/*
@@ -333,6 +361,7 @@ struct globals {
333#endif 361#endif
334 smallint exit_code; 362 smallint exit_code;
335 smallint show_dirname; 363 smallint show_dirname;
364 smallint tty_out;
336#if ENABLE_FEATURE_LS_WIDTH 365#if ENABLE_FEATURE_LS_WIDTH
337 unsigned terminal_width; 366 unsigned terminal_width;
338# define G_terminal_width (G.terminal_width) 367# define G_terminal_width (G.terminal_width)
@@ -353,16 +382,21 @@ struct globals {
353 setup_common_bufsiz(); \ 382 setup_common_bufsiz(); \
354 /* we have to zero it out because of NOEXEC */ \ 383 /* we have to zero it out because of NOEXEC */ \
355 memset(&G, 0, sizeof(G)); \ 384 memset(&G, 0, sizeof(G)); \
356 IF_FEATURE_LS_WIDTH(G_terminal_width = TERMINAL_WIDTH;) \ 385 IF_FEATURE_LS_WIDTH(G_terminal_width = ~0U;) \
357 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \ 386 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
358} while (0) 387} while (0)
359 388
360#define ESC "\033" 389#define ESC "\033"
361 390
391static int G_isatty(void)
392{
393 if (!G.tty_out) /* not known yet? */
394 G.tty_out = isatty(STDOUT_FILENO) + 1;
395 return (G.tty_out == 2);
396}
362 397
363/*** Output code ***/ 398/*** Output code ***/
364 399
365
366/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket 400/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
367 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file 401 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
368 * 3/7:multiplexed char/block device) 402 * 3/7:multiplexed char/block device)
@@ -425,56 +459,93 @@ static char append_char(mode_t mode)
425} 459}
426#endif 460#endif
427 461
462/* Return the number of used columns.
463 * Note that only columnar output uses return value.
464 * -l and -1 modes don't care.
465 * coreutils 7.2 also supports:
466 * ls -b (--escape) = octal escapes (although it doesn't look like working)
467 * ls -N (--literal) = not escape at all
468 */
428static unsigned calc_name_len(const char *name) 469static unsigned calc_name_len(const char *name)
429{ 470{
430 unsigned len; 471 unsigned len;
431 uni_stat_t uni_stat; 472 uni_stat_t uni_stat;
432 473
433 // TODO: quote tab as \t, etc, if -Q 474 if (!(option_mask32 & (OPT_q|OPT_Q)))
434 name = printable_string2(&uni_stat, name); 475 return strlen(name);
435 476
436 if (!(option_mask32 & OPT_Q)) { 477 if (!(option_mask32 & OPT_Q)) {
478 /* the most likely branch: "ls" to tty (it auto-enables -q behavior) */
479 printable_string2(&uni_stat, name);
437 return uni_stat.unicode_width; 480 return uni_stat.unicode_width;
438 } 481 }
439 482
440 len = 2 + uni_stat.unicode_width; 483 len = 2 + strlen(name);
441 while (*name) { 484 while (*name) {
485 unsigned char ch = (unsigned char)*name;
486 if (ch < ' ' || ch > 0x7e) {
487 ch -= 7;
488 if (ch <= 6) {
489 /* quote chars 7..13 as \a,b,t,n,v,f,r */
490 goto two;
491 }
492 /* other chars <32 or >126 as \ooo octal */
493 len += 3;
494 goto next;
495 }
442 if (*name == '"' || *name == '\\') { 496 if (*name == '"' || *name == '\\') {
497 two:
443 len++; 498 len++;
444 } 499 }
500 next:
445 name++; 501 name++;
446 } 502 }
447 return len; 503 return len;
448} 504}
449
450/* Return the number of used columns.
451 * Note that only columnar output uses return value.
452 * -l and -1 modes don't care.
453 * coreutils 7.2 also supports:
454 * ls -b (--escape) = octal escapes (although it doesn't look like working)
455 * ls -N (--literal) = not escape at all
456 */
457static unsigned print_name(const char *name) 505static unsigned print_name(const char *name)
458{ 506{
459 unsigned len; 507 unsigned len;
460 uni_stat_t uni_stat; 508 uni_stat_t uni_stat;
461 509
462 // TODO: quote tab as \t, etc, if -Q 510 if (!(option_mask32 & (OPT_q|OPT_Q))) {
463 name = printable_string2(&uni_stat, name); 511 fputs_stdout(name);
512 return strlen(name);
513 }
464 514
465 if (!(option_mask32 & OPT_Q)) { 515 if (!(option_mask32 & OPT_Q)) {
516 /* the most likely branch: "ls" to tty (it auto-enables -q behavior) */
517 name = printable_string2(&uni_stat, name);
466 fputs_stdout(name); 518 fputs_stdout(name);
467 return uni_stat.unicode_width; 519 return uni_stat.unicode_width;
468 } 520 }
469 521
470 len = 2 + uni_stat.unicode_width; 522 len = 2 + strlen(name);
471 putchar('"'); 523 putchar('"');
472 while (*name) { 524 while (*name) {
473 if (*name == '"' || *name == '\\') { 525 unsigned char ch = (unsigned char)*name;
526 if (ch < ' ' || ch > 0x7e) {
474 putchar('\\'); 527 putchar('\\');
528 ch -= 7;
529 if (ch <= 6) {
530 /* quote chars 7..13 as \a,b,t,n,v,f,r */
531 ch = c_escape_conv_str07[1 + 3 * ch];
532 goto two;
533 }
534 /* other chars <32 or >126 as \ooo octal */
535 ch = (unsigned char)*name;
536 putchar('0' + (ch>>6));
537 putchar('0' + ((ch>>3) & 7));
538 ch = '0' + (ch & 7);
539 len += 3;
540 goto put_ch;
541 }
542 if (ch == '"' || ch == '\\') {
543 putchar('\\');
544 two:
475 len++; 545 len++;
476 } 546 }
477 putchar(*name); 547 put_ch:
548 putchar(ch);
478 name++; 549 name++;
479 } 550 }
480 putchar('"'); 551 putchar('"');
@@ -660,7 +731,7 @@ static void display_files(struct dnode **dn, unsigned nfiles)
660 unsigned i, ncols, nrows, row, nc; 731 unsigned i, ncols, nrows, row, nc;
661 unsigned column; 732 unsigned column;
662 unsigned nexttab; 733 unsigned nexttab;
663 unsigned column_width = 0; /* used only by coulmnal output */ 734 unsigned column_width = 0; /* used only by columnar output */
664 735
665 if (option_mask32 & (OPT_l|OPT_1)) { 736 if (option_mask32 & (OPT_l|OPT_1)) {
666 ncols = 1; 737 ncols = 1;
@@ -709,6 +780,11 @@ static void display_files(struct dnode **dn, unsigned nfiles)
709 } 780 }
710 nexttab = column + column_width; 781 nexttab = column + column_width;
711 column += display_single(dn[i]); 782 column += display_single(dn[i]);
783 } else {
784 /* if -w999999999, ncols can be very large */
785 //bb_error_msg(" col:%u ncol:%u i:%i", nc, ncols, i); sleep1();
786 /* without "break", we loop millions of times here */
787 break;
712 } 788 }
713 } 789 }
714 putchar('\n'); 790 putchar('\n');
@@ -1155,25 +1231,11 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1155 /* need to initialize since --color has _an optional_ argument */ 1231 /* need to initialize since --color has _an optional_ argument */
1156 const char *color_opt = color_str; /* "always" */ 1232 const char *color_opt = color_str; /* "always" */
1157#endif 1233#endif
1158#if ENABLE_LONG_OPTS
1159 static const char ls_longopts[] ALIGN1 =
1160 "full-time\0" No_argument "\xff"
1161 "group-directories-first\0" No_argument "\xfe"
1162 IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd")
1163 ;
1164#endif
1165 1234
1166 INIT_G(); 1235 INIT_G();
1167 1236
1168 init_unicode(); 1237 init_unicode();
1169 1238
1170#if ENABLE_FEATURE_LS_WIDTH
1171 /* obtain the terminal width */
1172 G_terminal_width = get_terminal_width(STDIN_FILENO);
1173 /* go one less... */
1174 G_terminal_width--;
1175#endif
1176
1177 /* process options */ 1239 /* process options */
1178 opt = getopt32long(argv, "^" 1240 opt = getopt32long(argv, "^"
1179 ls_options 1241 ls_options
@@ -1211,6 +1273,29 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1211 exit(0); 1273 exit(0);
1212#endif 1274#endif
1213 1275
1276 /* ftpd secret backdoor? */
1277 if (ENABLE_FTPD && applet_name[0] == 'f') {
1278 /* dirs first are much nicer */
1279 opt = option_mask32 |= OPT_dirs_first;
1280 /* don't show SEcontext */
1281 IF_SELINUX(opt = option_mask32 &= ~OPT_Z;)
1282 /* do not query stdout about size and tty-ness */
1283 IF_FEATURE_LS_WIDTH(G_terminal_width = INT_MAX;)
1284 G.tty_out = 1; /* not a tty */
1285 goto skip_if_ftpd;
1286 }
1287
1288#if ENABLE_FEATURE_LS_WIDTH
1289 if ((int)G_terminal_width < 0) {
1290 /* obtain the terminal width */
1291 G_terminal_width = get_terminal_width(STDIN_FILENO);
1292 /* go one less... */
1293 G_terminal_width--;
1294 }
1295 if (G_terminal_width == 0) /* -w0 */
1296 G_terminal_width = INT_MAX; /* "infinite" */
1297#endif
1298
1214#if ENABLE_SELINUX 1299#if ENABLE_SELINUX
1215 if (opt & OPT_Z) { 1300 if (opt & OPT_Z) {
1216 if (!is_selinux_enabled()) 1301 if (!is_selinux_enabled())
@@ -1229,7 +1314,7 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1229# endif 1314# endif
1230 /* LS_COLORS is unset, or (not empty && not "none") ? */ 1315 /* LS_COLORS is unset, or (not empty && not "none") ? */
1231 if (!p || (p[0] && strcmp(p, "none") != 0)) { 1316 if (!p || (p[0] && strcmp(p, "none") != 0)) {
1232 if (isatty(STDOUT_FILENO)) { 1317 if (G_isatty()) {
1233 /* check isatty() last because it's expensive (syscall) */ 1318 /* check isatty() last because it's expensive (syscall) */
1234 G_show_color = 1; 1319 G_show_color = 1;
1235 } 1320 }
@@ -1238,23 +1323,28 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1238 if (opt & OPT_color) { 1323 if (opt & OPT_color) {
1239 if (color_opt[0] == 'n') 1324 if (color_opt[0] == 'n')
1240 G_show_color = 0; 1325 G_show_color = 0;
1241 else switch (index_in_substrings(color_str, color_opt)) { 1326 else if (!G_show_color) {
1242 case 3: 1327 /* if() is not needed, but avoids extra isatty() if G_show_color is already set */
1243 case 4: 1328 /* Check --color=COLOR_OPT and maybe set show_color=1 */
1244 case 5: 1329 switch (index_in_substrings(color_str, color_opt)) {
1245 if (!is_TERM_dumb() && isatty(STDOUT_FILENO)) { 1330 case 3: // auto
1246 case 0: 1331 case 4: // tty
1247 case 1: 1332 case 5: // if-tty
1248 case 2: 1333 if (!is_TERM_dumb() && G_isatty()) {
1249 G_show_color = 1; 1334 case 0: // always
1335 case 1: // yes
1336 case 2: // force
1337 G_show_color = 1;
1338 }
1250 } 1339 }
1251 } 1340 }
1252 } 1341 }
1253#endif 1342#endif
1343 skip_if_ftpd:
1254 1344
1255 /* sort out which command line options take precedence */ 1345 /* sort out which command line options take precedence */
1256 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d)) 1346 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d))
1257 option_mask32 &= ~OPT_R; /* no recurse if listing only dir */ 1347 opt = option_mask32 &= ~OPT_R; /* no recurse if listing only dir */
1258 if (!(opt & OPT_l)) { /* not -l? */ 1348 if (!(opt & OPT_l)) { /* not -l? */
1259 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { 1349 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1260 /* when to sort by time? -t[cu] sorts by time even with -l */ 1350 /* when to sort by time? -t[cu] sorts by time even with -l */
@@ -1262,19 +1352,17 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1262 /* without -l, bare -c or -u enable sort too */ 1352 /* without -l, bare -c or -u enable sort too */
1263 /* (with -l, bare -c or -u just select which time to show) */ 1353 /* (with -l, bare -c or -u just select which time to show) */
1264 if (opt & (OPT_c|OPT_u)) { 1354 if (opt & (OPT_c|OPT_u)) {
1265 option_mask32 |= OPT_t; 1355 opt = option_mask32 |= OPT_t;
1266 } 1356 }
1267 } 1357 }
1268 } 1358 }
1269 1359
1270 /* choose a display format if one was not already specified by an option */ 1360 /* choose a display format if one was not already specified by an option */
1271 if (!(option_mask32 & (OPT_l|OPT_1|OPT_x|OPT_C))) 1361 if (!(opt & (OPT_l|OPT_1|OPT_x|OPT_C)))
1272 option_mask32 |= (isatty(STDOUT_FILENO) ? OPT_C : OPT_1); 1362 opt = option_mask32 |= (G_isatty() ? OPT_C : OPT_1);
1273 1363
1274 if (ENABLE_FTPD && applet_name[0] == 'f') { 1364 if (!(opt & OPT_q) && G_isatty())
1275 /* ftpd secret backdoor. dirs first are much nicer */ 1365 opt = option_mask32 |= OPT_q;
1276 option_mask32 |= OPT_dirs_first;
1277 }
1278 1366
1279#if ENABLE_FEATURE_EXTRA_FILE_DATA 1367#if ENABLE_FEATURE_EXTRA_FILE_DATA
1280 /* Enable accurate link counts for directories */ 1368 /* Enable accurate link counts for directories */
diff --git a/coreutils/md5_sha1_sum.c b/coreutils/md5_sha1_sum.c
index 978d328f1..4506aeb56 100644
--- a/coreutils/md5_sha1_sum.c
+++ b/coreutils/md5_sha1_sum.c
@@ -23,6 +23,12 @@
23//config: help 23//config: help
24//config: Compute and check SHA256 message digest 24//config: Compute and check SHA256 message digest
25//config: 25//config:
26//config:config SHA384SUM
27//config: bool "sha384sum (7.3 kb)"
28//config: default y
29//config: help
30//config: Compute and check SHA384 message digest
31//config:
26//config:config SHA512SUM 32//config:config SHA512SUM
27//config: bool "sha512sum (7.3 kb)" 33//config: bool "sha512sum (7.3 kb)"
28//config: default y 34//config: default y
@@ -35,13 +41,13 @@
35//config: help 41//config: help
36//config: Compute and check SHA3 message digest 42//config: Compute and check SHA3 message digest
37//config: 43//config:
38//config:comment "Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum" 44//config:comment "Common options for md5sum, sha1sum, sha256sum, ..., sha3sum"
39//config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM 45//config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA384SUM || SHA512SUM || SHA3SUM
40//config: 46//config:
41//config:config FEATURE_MD5_SHA1_SUM_CHECK 47//config:config FEATURE_MD5_SHA1_SUM_CHECK
42//config: bool "Enable -c, -s and -w options" 48//config: bool "Enable -c, -s and -w options"
43//config: default y 49//config: default y
44//config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM 50//config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA384SUM || SHA512SUM || SHA3SUM
45//config: help 51//config: help
46//config: Enabling the -c options allows files to be checked 52//config: Enabling the -c options allows files to be checked
47//config: against pre-calculated hash values. 53//config: against pre-calculated hash values.
@@ -51,11 +57,13 @@
51//applet:IF_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum)) 57//applet:IF_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum))
52//applet:IF_SHA3SUM(APPLET_NOEXEC(sha3sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha3sum)) 58//applet:IF_SHA3SUM(APPLET_NOEXEC(sha3sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha3sum))
53//applet:IF_SHA256SUM(APPLET_NOEXEC(sha256sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha256sum)) 59//applet:IF_SHA256SUM(APPLET_NOEXEC(sha256sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha256sum))
60//applet:IF_SHA384SUM(APPLET_NOEXEC(sha384sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha384sum))
54//applet:IF_SHA512SUM(APPLET_NOEXEC(sha512sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha512sum)) 61//applet:IF_SHA512SUM(APPLET_NOEXEC(sha512sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha512sum))
55 62
56//kbuild:lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o 63//kbuild:lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o
57//kbuild:lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o 64//kbuild:lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o
58//kbuild:lib-$(CONFIG_SHA256SUM) += md5_sha1_sum.o 65//kbuild:lib-$(CONFIG_SHA256SUM) += md5_sha1_sum.o
66//kbuild:lib-$(CONFIG_SHA384SUM) += md5_sha1_sum.o
59//kbuild:lib-$(CONFIG_SHA512SUM) += md5_sha1_sum.o 67//kbuild:lib-$(CONFIG_SHA512SUM) += md5_sha1_sum.o
60//kbuild:lib-$(CONFIG_SHA3SUM) += md5_sha1_sum.o 68//kbuild:lib-$(CONFIG_SHA3SUM) += md5_sha1_sum.o
61 69
@@ -99,6 +107,16 @@
99//usage: "\n -w Warn about improperly formatted checksum lines" 107//usage: "\n -w Warn about improperly formatted checksum lines"
100//usage: ) 108//usage: )
101//usage: 109//usage:
110//usage:#define sha384sum_trivial_usage
111//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
112//usage:#define sha384sum_full_usage "\n\n"
113//usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA384 checksums"
114//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n"
115//usage: "\n -c Check sums against list in FILEs"
116//usage: "\n -s Don't output anything, status code shows success"
117//usage: "\n -w Warn about improperly formatted checksum lines"
118//usage: )
119//usage:
102//usage:#define sha512sum_trivial_usage 120//usage:#define sha512sum_trivial_usage
103//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." 121//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
104//usage:#define sha512sum_full_usage "\n\n" 122//usage:#define sha512sum_full_usage "\n\n"
@@ -130,11 +148,12 @@
130 148
131enum { 149enum {
132 /* 4th letter of applet_name is... */ 150 /* 4th letter of applet_name is... */
133 HASH_MD5 = 's', /* "md5>s<um" */ 151 HASH_MD5 = 's', /* "md5>s<um" */
134 HASH_SHA1 = '1', 152 HASH_SHA1 = '1',
135 HASH_SHA256 = '2', 153 HASH_SHA256 = '2',
136 HASH_SHA3 = '3', 154 HASH_SHA3 = '3',
137 HASH_SHA512 = '5', 155 HASH_SHA512 = '5',
156 /* unfortunately, sha384sum has the same '3' as sha3 */
138}; 157};
139 158
140#define FLAG_SILENT 1 159#define FLAG_SILENT 1
@@ -158,10 +177,11 @@ static unsigned char *hash_bin_to_hex(unsigned char *hash_value,
158#endif 177#endif
159static uint8_t *hash_file(unsigned char *in_buf, const char *filename, unsigned sha3_width) 178static uint8_t *hash_file(unsigned char *in_buf, const char *filename, unsigned sha3_width)
160{ 179{
161 int src_fd, hash_len, count; 180 int src_fd, count;
162 union _ctx_ { 181 union _ctx_ {
163 sha3_ctx_t sha3; 182 sha3_ctx_t sha3;
164 sha512_ctx_t sha512; 183 sha512_ctx_t sha512;
184 sha384_ctx_t sha384;
165 sha256_ctx_t sha256; 185 sha256_ctx_t sha256;
166 sha1_ctx_t sha1; 186 sha1_ctx_t sha1;
167 md5_ctx_t md5; 187 md5_ctx_t md5;
@@ -183,25 +203,31 @@ static uint8_t *hash_file(unsigned char *in_buf, const char *filename, unsigned
183 md5_begin(&context.md5); 203 md5_begin(&context.md5);
184 update = (void*)md5_hash; 204 update = (void*)md5_hash;
185 final = (void*)md5_end; 205 final = (void*)md5_end;
186 hash_len = 16;
187 } 206 }
188 else if (ENABLE_SHA1SUM && hash_algo == HASH_SHA1) { 207 else if (ENABLE_SHA1SUM && hash_algo == HASH_SHA1) {
189 sha1_begin(&context.sha1); 208 sha1_begin(&context.sha1);
190 update = (void*)sha1_hash; 209 update = (void*)sha1_hash;
191 final = (void*)sha1_end; 210 final = (void*)sha1_end;
192 hash_len = 20;
193 } 211 }
194 else if (ENABLE_SHA256SUM && hash_algo == HASH_SHA256) { 212 else if (ENABLE_SHA256SUM && hash_algo == HASH_SHA256) {
195 sha256_begin(&context.sha256); 213 sha256_begin(&context.sha256);
196 update = (void*)sha256_hash; 214 update = (void*)sha256_hash;
197 final = (void*)sha256_end; 215 final = (void*)sha256_end;
198 hash_len = 32; 216 }
217 else if (ENABLE_SHA384SUM
218 && (ENABLE_SHA3SUM
219 ? (applet_name[4] == '8') /* check for "sha384", but do not match "sha3" */
220 : (hash_algo == '3') /* applet_name = "sha3sum" is not possible */
221 )
222 ) {
223 sha384_begin(&context.sha384);
224 update = (void*)sha384_hash;
225 final = (void*)sha384_end;
199 } 226 }
200 else if (ENABLE_SHA512SUM && hash_algo == HASH_SHA512) { 227 else if (ENABLE_SHA512SUM && hash_algo == HASH_SHA512) {
201 sha512_begin(&context.sha512); 228 sha512_begin(&context.sha512);
202 update = (void*)sha512_hash; 229 update = (void*)sha512_hash;
203 final = (void*)sha512_end; 230 final = (void*)sha512_end;
204 hash_len = 64;
205 } 231 }
206#if ENABLE_SHA3SUM 232#if ENABLE_SHA3SUM
207 else if (ENABLE_SHA3SUM && hash_algo == HASH_SHA3) { 233 else if (ENABLE_SHA3SUM && hash_algo == HASH_SHA3) {
@@ -219,9 +245,7 @@ static uint8_t *hash_file(unsigned char *in_buf, const char *filename, unsigned
219 ) { 245 ) {
220 bb_error_msg_and_die("bad -a%u", sha3_width); 246 bb_error_msg_and_die("bad -a%u", sha3_width);
221 } 247 }
222 sha3_width /= 4; 248 context.sha3.input_block_bytes = 1600/8 - sha3_width/4;
223 context.sha3.input_block_bytes = 1600/8 - sha3_width;
224 hash_len = sha3_width/2;
225 } 249 }
226#endif 250#endif
227 else { 251 else {
@@ -236,7 +260,7 @@ static uint8_t *hash_file(unsigned char *in_buf, const char *filename, unsigned
236 if (count < 0) 260 if (count < 0)
237 bb_perror_msg("can't read '%s'", filename); 261 bb_perror_msg("can't read '%s'", filename);
238 else /* count == 0 */ { 262 else /* count == 0 */ {
239 final(&context, in_buf); 263 unsigned hash_len = final(&context, in_buf);
240 hash_value = hash_bin_to_hex(in_buf, hash_len); 264 hash_value = hash_bin_to_hex(in_buf, hash_len);
241 } 265 }
242 } 266 }
@@ -262,14 +286,14 @@ int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv)
262 /* -b "binary", -t "text" are ignored (shaNNNsum compat) */ 286 /* -b "binary", -t "text" are ignored (shaNNNsum compat) */
263 /* -s and -w require -c */ 287 /* -s and -w require -c */
264#if ENABLE_SHA3SUM 288#if ENABLE_SHA3SUM
265 if (applet_name[3] == HASH_SHA3) 289 if (applet_name[3] == HASH_SHA3 && (!ENABLE_SHA384SUM || applet_name[4] != '8'))
266 flags = getopt32(argv, "^" "scwbta:+" "\0" "s?c:w?c", &sha3_width); 290 flags = getopt32(argv, "^" "scwbta:+" "\0" "s?c:w?c", &sha3_width);
267 else 291 else
268#endif 292#endif
269 flags = getopt32(argv, "^" "scwbt" "\0" "s?c:w?c"); 293 flags = getopt32(argv, "^" "scwbt" "\0" "s?c:w?c");
270 } else { 294 } else {
271#if ENABLE_SHA3SUM 295#if ENABLE_SHA3SUM
272 if (applet_name[3] == HASH_SHA3) 296 if (applet_name[3] == HASH_SHA3 && (!ENABLE_SHA384SUM || applet_name[4] != '8'))
273 getopt32(argv, "a:+", &sha3_width); 297 getopt32(argv, "a:+", &sha3_width);
274 else 298 else
275#endif 299#endif
diff --git a/e2fsprogs/fsck.c b/e2fsprogs/fsck.c
index fd4ea737c..f7e93497d 100644
--- a/e2fsprogs/fsck.c
+++ b/e2fsprogs/fsck.c
@@ -423,13 +423,11 @@ static int wait_one(int flags)
423 /* if (G.noexecute) { already returned -1; } */ 423 /* if (G.noexecute) { already returned -1; } */
424 424
425 while (1) { 425 while (1) {
426 pid = waitpid(-1, &status, flags); 426 pid = safe_waitpid(-1, &status, flags);
427 kill_all_if_got_signal(); 427 kill_all_if_got_signal();
428 if (pid == 0) /* flags == WNOHANG and no children exited */ 428 if (pid == 0) /* flags == WNOHANG and no children exited */
429 return -1; 429 return -1;
430 if (pid < 0) { 430 if (pid < 0) {
431 if (errno == EINTR)
432 continue;
433 if (errno == ECHILD) { /* paranoia */ 431 if (errno == ECHILD) { /* paranoia */
434 bb_simple_error_msg("wait: no more children"); 432 bb_simple_error_msg("wait: no more children");
435 return -1; 433 return -1;
diff --git a/include/libbb.h b/include/libbb.h
index 8dc4e4992..60037ed3d 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1224,6 +1224,22 @@ char *bin2hex(char *dst, const char *src, int count) FAST_FUNC;
1224/* Reverse */ 1224/* Reverse */
1225char* hex2bin(char *dst, const char *src, int count) FAST_FUNC; 1225char* hex2bin(char *dst, const char *src, int count) FAST_FUNC;
1226 1226
1227/* Returns strlen as a bonus */
1228//size_t replace_char(char *s, char what, char with) FAST_FUNC;
1229static inline size_t replace_char(char *str, char from, char to)
1230{
1231 char *p = str;
1232 while (*p) {
1233 if (*p == from)
1234 *p = to;
1235 p++;
1236 }
1237 return p - str;
1238}
1239
1240extern const char c_escape_conv_str00[];
1241#define c_escape_conv_str07 (c_escape_conv_str00+3)
1242
1227void FAST_FUNC xorbuf_3(void *dst, const void *src1, const void *src2, unsigned count); 1243void FAST_FUNC xorbuf_3(void *dst, const void *src1, const void *src2, unsigned count);
1228void FAST_FUNC xorbuf(void* buf, const void* mask, unsigned count); 1244void FAST_FUNC xorbuf(void* buf, const void* mask, unsigned count);
1229void FAST_FUNC xorbuf16_aligned_long(void* buf, const void* mask); 1245void FAST_FUNC xorbuf16_aligned_long(void* buf, const void* mask);
@@ -2225,33 +2241,6 @@ enum { COMM_LEN = 16 };
2225# endif 2241# endif
2226#endif 2242#endif
2227 2243
2228struct smaprec {
2229 unsigned long mapped_rw;
2230 unsigned long mapped_ro;
2231 unsigned long shared_clean;
2232 unsigned long shared_dirty;
2233 unsigned long private_clean;
2234 unsigned long private_dirty;
2235 unsigned long stack;
2236 unsigned long smap_pss, smap_swap;
2237 unsigned long smap_size;
2238 // For mixed 32/64 userspace, 32-bit pmap still needs
2239 // 64-bit field here to correctly show 64-bit processes:
2240 unsigned long long smap_start;
2241 // (strictly speaking, other fields need to be wider too,
2242 // but they are in kbytes, not bytes, and they hold sizes,
2243 // not start addresses, sizes tend to be less than 4 terabytes)
2244 char smap_mode[5];
2245 char *smap_name;
2246};
2247
2248#if !ENABLE_PMAP
2249#define procps_read_smaps(pid, total, cb, data) \
2250 procps_read_smaps(pid, total)
2251#endif
2252int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
2253 void (*cb)(struct smaprec *, void *), void *data);
2254
2255typedef struct procps_status_t { 2244typedef struct procps_status_t {
2256#if !ENABLE_PLATFORM_MINGW32 2245#if !ENABLE_PLATFORM_MINGW32
2257 DIR *dir; 2246 DIR *dir;
@@ -2287,7 +2276,13 @@ typedef struct procps_status_t {
2287#endif 2276#endif
2288 unsigned tty_major,tty_minor; 2277 unsigned tty_major,tty_minor;
2289#if ENABLE_FEATURE_TOPMEM 2278#if ENABLE_FEATURE_TOPMEM
2290 struct smaprec smaps; 2279 unsigned long mapped_rw;
2280 unsigned long mapped_ro;
2281 unsigned long shared_clean;
2282 unsigned long shared_dirty;
2283 unsigned long private_clean;
2284 unsigned long private_dirty;
2285 unsigned long stack;
2291#endif 2286#endif
2292 char state[4]; 2287 char state[4];
2293 /* basename of executable in exec(2), read from /proc/N/stat 2288 /* basename of executable in exec(2), read from /proc/N/stat
@@ -2336,11 +2331,15 @@ void free_procps_scan(procps_status_t* sp) FAST_FUNC;
2336procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC; 2331procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC;
2337/* Format cmdline (up to col chars) into char buf[size] */ 2332/* Format cmdline (up to col chars) into char buf[size] */
2338/* Puts [comm] if cmdline is empty (-> process is a kernel thread) */ 2333/* Puts [comm] if cmdline is empty (-> process is a kernel thread) */
2339void read_cmdline(char *buf, int size, unsigned pid, const char *comm) FAST_FUNC; 2334int read_cmdline(char *buf, int size, unsigned pid, const char *comm) FAST_FUNC;
2340pid_t *find_pid_by_name(const char* procName) FAST_FUNC; 2335pid_t *find_pid_by_name(const char* procName) FAST_FUNC;
2341pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC; 2336pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC;
2342int starts_with_cpu(const char *str) FAST_FUNC; 2337int starts_with_cpu(const char *str) FAST_FUNC;
2343unsigned get_cpu_count(void) FAST_FUNC; 2338unsigned get_cpu_count(void) FAST_FUNC;
2339/* Some internals reused by pmap: */
2340unsigned long FAST_FUNC fast_strtoul_10(char **endptr);
2341unsigned long long FAST_FUNC fast_strtoull_16(char **endptr);
2342char* FAST_FUNC skip_fields(char *str, int count);
2344 2343
2345 2344
2346/* Use strict=1 if you process input from untrusted source: 2345/* Use strict=1 if you process input from untrusted source:
@@ -2375,8 +2374,9 @@ enum {
2375 MD5_OUTSIZE = 16, 2374 MD5_OUTSIZE = 16,
2376 SHA1_OUTSIZE = 20, 2375 SHA1_OUTSIZE = 20,
2377 SHA256_OUTSIZE = 32, 2376 SHA256_OUTSIZE = 32,
2377 SHA384_OUTSIZE = 48,
2378 SHA512_OUTSIZE = 64, 2378 SHA512_OUTSIZE = 64,
2379 SHA3_OUTSIZE = 28, 2379 //SHA3-224_OUTSIZE = 28,
2380 /* size of input block */ 2380 /* size of input block */
2381 SHA2_INSIZE = 64, 2381 SHA2_INSIZE = 64,
2382}; 2382};
@@ -2390,6 +2390,7 @@ struct bcrypt_hash_ctx_t {
2390typedef struct bcrypt_hash_ctx_t md5_ctx_t; 2390typedef struct bcrypt_hash_ctx_t md5_ctx_t;
2391typedef struct bcrypt_hash_ctx_t sha1_ctx_t; 2391typedef struct bcrypt_hash_ctx_t sha1_ctx_t;
2392typedef struct bcrypt_hash_ctx_t sha256_ctx_t; 2392typedef struct bcrypt_hash_ctx_t sha256_ctx_t;
2393typedef struct bcrypt_hash_ctx_t sha384_ctx_t;
2393typedef struct bcrypt_hash_ctx_t sha512_ctx_t; 2394typedef struct bcrypt_hash_ctx_t sha512_ctx_t;
2394typedef struct sha3_ctx_t { 2395typedef struct sha3_ctx_t {
2395 uint64_t state[25]; 2396 uint64_t state[25];
@@ -2399,16 +2400,19 @@ typedef struct sha3_ctx_t {
2399void md5_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; 2400void md5_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2400void sha1_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; 2401void sha1_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2401void sha256_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; 2402void sha256_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2403void sha384_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2402void sha512_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; 2404void sha512_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2403void generic_hash(struct bcrypt_hash_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; 2405void generic_hash(struct bcrypt_hash_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
2404unsigned generic_end(struct bcrypt_hash_ctx_t *ctx, void *resbuf) FAST_FUNC; 2406unsigned generic_end(struct bcrypt_hash_ctx_t *ctx, void *resbuf) FAST_FUNC;
2405# define md5_hash generic_hash 2407# define md5_hash generic_hash
2406# define sha1_hash generic_hash 2408# define sha1_hash generic_hash
2407# define sha256_hash generic_hash 2409# define sha256_hash generic_hash
2410# define sha384_hash generic_hash
2408# define sha512_hash generic_hash 2411# define sha512_hash generic_hash
2409# define md5_end generic_end 2412# define md5_end generic_end
2410# define sha1_end generic_end 2413# define sha1_end generic_end
2411# define sha256_end generic_end 2414# define sha256_end generic_end
2415# define sha384_end generic_end
2412# define sha512_end generic_end 2416# define sha512_end generic_end
2413#else 2417#else
2414typedef struct md5_ctx_t { 2418typedef struct md5_ctx_t {
@@ -2424,6 +2428,7 @@ typedef struct sha512_ctx_t {
2424 uint64_t hash[8]; 2428 uint64_t hash[8];
2425 uint8_t wbuffer[128]; /* always correctly aligned for uint64_t */ 2429 uint8_t wbuffer[128]; /* always correctly aligned for uint64_t */
2426} sha512_ctx_t; 2430} sha512_ctx_t;
2431typedef struct sha512_ctx_t sha384_ctx_t;
2427typedef struct sha3_ctx_t { 2432typedef struct sha3_ctx_t {
2428 uint64_t state[25]; 2433 uint64_t state[25];
2429 unsigned bytes_queued; 2434 unsigned bytes_queued;
@@ -2441,6 +2446,9 @@ void sha256_begin(sha256_ctx_t *ctx) FAST_FUNC;
2441void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC; 2446void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC;
2442void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; 2447void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
2443unsigned sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC; 2448unsigned sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC;
2449void sha384_begin(sha384_ctx_t *ctx) FAST_FUNC;
2450#define sha384_hash sha512_hash
2451unsigned sha384_end(sha384_ctx_t *ctx, void *resbuf) FAST_FUNC;
2444#endif 2452#endif
2445void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC; 2453void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC;
2446void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; 2454void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
diff --git a/include/platform.h b/include/platform.h
index 5795a0cf3..0b88f990b 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -208,7 +208,7 @@
208#elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN 208#elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN
209# define BB_BIG_ENDIAN 0 209# define BB_BIG_ENDIAN 0
210# define BB_LITTLE_ENDIAN 1 210# define BB_LITTLE_ENDIAN 1
211#elif defined(__386__) 211#elif defined(__i386__)
212# define BB_BIG_ENDIAN 0 212# define BB_BIG_ENDIAN 0
213# define BB_LITTLE_ENDIAN 1 213# define BB_LITTLE_ENDIAN 1
214#else 214#else
diff --git a/init/init.c b/init/init.c
index 797e0a0eb..294be9952 100644
--- a/init/init.c
+++ b/init/init.c
@@ -1201,7 +1201,7 @@ int init_main(int argc UNUSED_PARAM, char **argv)
1201 int status; 1201 int status;
1202 struct init_action *a; 1202 struct init_action *a;
1203 1203
1204 wpid = waitpid(-1, &status, WNOHANG); 1204 wpid = wait_any_nohang(&status);
1205 if (wpid <= 0) 1205 if (wpid <= 0)
1206 break; 1206 break;
1207 1207
diff --git a/libbb/c_escape.c b/libbb/c_escape.c
new file mode 100644
index 000000000..6c109f2e0
--- /dev/null
+++ b/libbb/c_escape.c
@@ -0,0 +1,20 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright (C) 2025 by Denys Vlasenko <vda.linux@googlemail.com>
4 *
5 * Licensed under GPLv2, see file LICENSE in this source tree.
6 */
7//kbuild:lib-y += c_escape.o
8
9#include "libbb.h"
10
11const char c_escape_conv_str00[] ALIGN1 =
12 "\\""0""\0" // [0]:00
13 "\\""a""\0" // [1]:07
14 "\\""b""\0" // [2]:08
15 "\\""t""\0" // [3]:09
16 "\\""n""\0" // [4]:0a
17 "\\""v""\0" // [5]:0b
18 "\\""f""\0" // [6]:0c
19 "\\""r" // [7]:0d
20 ;
diff --git a/libbb/concat_path_file.c b/libbb/concat_path_file.c
index 3afb0e3a4..96fcd4a1d 100644
--- a/libbb/concat_path_file.c
+++ b/libbb/concat_path_file.c
@@ -17,6 +17,7 @@
17 17
18char* FAST_FUNC concat_path_file(const char *path, const char *filename) 18char* FAST_FUNC concat_path_file(const char *path, const char *filename)
19{ 19{
20#if 0
20 char *lc; 21 char *lc;
21 22
22 if (!path) 23 if (!path)
@@ -31,4 +32,78 @@ char* FAST_FUNC concat_path_file(const char *path, const char *filename)
31 filename++; 32 filename++;
32#endif 33#endif
33 return xasprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename); 34 return xasprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename);
35#else
36/* ^^^^^^^^^^^ timing of xasprintf-based code above:
37 * real 7.074s
38 * user 0.156s <<<
39 * sys 6.394s
40 * "rm -rf" of a Linux kernel tree from tmpfs (run time still dominated by in-kernel work, though)
41 * real 6.989s
42 * user 0.055s <<< 3 times less CPU used
43 * sys 6.450s
44 * vvvvvvvvvvv timing of open-coded malloc+memcpy code below (+59 bytes):
45 */
46 char *buf, *p;
47 size_t n1, n2, n3;
48
49 while (*filename == '/')
50 filename++;
51
52 if (!path || !path[0])
53 return xstrdup(filename);
54
55 n1 = strlen(path);
56 n2 = (path[n1 - 1] != '/'); /* 1: "path has no trailing slash" */
57 n3 = strlen(filename) + 1;
58
59 buf = xmalloc(n1 + n2 + n3);
60 p = mempcpy(buf, path, n1);
61 if (n2)
62 *p++ = '/';
63 memcpy(p, filename, n3);
64 return buf;
65#endif
34} 66}
67
68/* If second component comes from struct dirent,
69 * it's possible to eliminate one strlen() by using name length
70 * provided by kernel in struct dirent. See below.
71 * However, the win seems to be insignificant.
72 */
73
74#if 0
75
76/* Extract d_namlen from struct dirent */
77static size_t get_d_namlen(const struct dirent *de)
78{
79#if defined(_DIRENT_HAVE_D_NAMLEN)
80 return de->d_namlen;
81#elif defined(_DIRENT_HAVE_D_RECLEN)
82 const size_t prefix_sz = offsetof(struct dirent, d_name);
83 return de->d_reclen - prefix_sz;
84#else
85 return strlen(de->d_name);
86#endif
87}
88
89char* FAST_FUNC concat_path_dirent(const char *path, const struct dirent *de)
90{
91 char *buf, *p;
92 size_t n1, n2, n3;
93
94 if (!path || !path[0])
95 return xstrdup(de->d_name);
96
97 n1 = strlen(path);
98 n2 = (path[n1 - 1] != '/');
99 n3 = get_d_namlen(de) + 1;
100
101 buf = xmalloc(n1 + n2 + n3);
102 p = mempcpy(buf, path, n1);
103 if (n2)
104 *p++ = '/';
105 memcpy(p, de->d_name, n3);
106 return buf;
107}
108
109#endif
diff --git a/libbb/dump.c b/libbb/dump.c
index b2abe85af..3dc53d55f 100644
--- a/libbb/dump.c
+++ b/libbb/dump.c
@@ -514,37 +514,52 @@ static void bpad(PR *pr)
514 continue; 514 continue;
515} 515}
516 516
517static const char conv_str[] ALIGN1 =
518 "\0" "\\""0""\0"
519 "\007""\\""a""\0"
520 "\b" "\\""b""\0"
521 "\f" "\\""f""\0"
522 "\n" "\\""n""\0"
523 "\r" "\\""r""\0"
524 "\t" "\\""t""\0"
525 "\v" "\\""v""\0"
526 ;
527
528static void conv_c(PR *pr, unsigned char *p) 517static void conv_c(PR *pr, unsigned char *p)
529{ 518{
530 const char *str = conv_str; 519 const char *str;
531 520 unsigned char ch;
532 do { 521
533 if (*p == *str) { 522 ch = *p;
534 ++str; 523 if (ch == 0 || (ch -= 6, (signed char)ch > 0 && ch <= 7)) {
535 goto strpr; /* map e.g. '\n' to "\\n" */ 524 /* map chars 0,7..13 to "\0","\{a,b,t,n,v,f,r}" */
536 } 525 str = c_escape_conv_str00 + 3 * ch;
537 str += 4; 526 goto strpr;
538 } while (*str); 527 }
539 528
540 if (isprint_asciionly(*p)) { 529 if (isprint_asciionly(*p)) {
541 *pr->cchar = 'c'; 530 *pr->cchar = 'c';
542 printf(pr->fmt, *p); 531 printf(pr->fmt, *p);
543 } else { 532 } else {
533#if defined(__i386__) || defined(__x86_64__)
534 /* Abuse partial register operations */
535 uint32_t buf;
536 unsigned n = *p;
537 asm ( //00000000 00000000 00000000 aabbbccc
538"\n shll $10,%%eax" //00000000 000000aa bbbccc00 00000000
539"\n shrw $5,%%ax" //00000000 000000aa 00000bbb ccc00000
540"\n shrb $5,%%al" //00000000 000000aa 00000bbb 00000ccc
541"\n shll $8,%%eax" //000000aa 00000bbb 00000ccc 00000000
542"\n bswapl %%eax" //00000000 00000ccc 00000bbb 000000aa
543"\n addl $0x303030,%%eax"
544"\n" : "=a" (n)
545 : "0" (n)
546 );
547 buf = n;
548 str = (void*)&buf;
549#elif 1
544 char buf[4]; 550 char buf[4];
545 /* gcc-8.0.1 needs lots of casts to shut up */ 551 /* gcc-8.0.1 needs lots of casts to shut up */
546 sprintf(buf, "%03o", (unsigned)(uint8_t)*p); 552 sprintf(buf, "%03o", (unsigned)(uint8_t)*p);
547 str = buf; 553 str = buf;
554#else // use faster version? +20 bytes of code relative to sprintf() method
555 char buf[4];
556 buf[3] = '\0';
557 ch = *p;
558 buf[2] = '0' + (ch & 7); ch >>= 3;
559 buf[1] = '0' + (ch & 7); ch >>= 3;
560 buf[0] = '0' + ch;
561 str = buf;
562#endif
548 strpr: 563 strpr:
549 *pr->cchar = 's'; 564 *pr->cchar = 's';
550 printf(pr->fmt, str); 565 printf(pr->fmt, str);
diff --git a/libbb/getopt32.c b/libbb/getopt32.c
index 76d29d5eb..9247588d9 100644
--- a/libbb/getopt32.c
+++ b/libbb/getopt32.c
@@ -530,6 +530,7 @@ vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options,
530 * "fake" short options, like this one: 530 * "fake" short options, like this one:
531 * wget $'-\203' "Test: test" http://kernel.org/ 531 * wget $'-\203' "Test: test" http://kernel.org/
532 * (supposed to act as --header, but doesn't) */ 532 * (supposed to act as --header, but doesn't) */
533 next_opt:
533#if ENABLE_LONG_OPTS 534#if ENABLE_LONG_OPTS
534 while ((c = getopt_long(argc, argv, applet_opts, 535 while ((c = getopt_long(argc, argv, applet_opts,
535 long_options, NULL)) != -1) { 536 long_options, NULL)) != -1) {
@@ -544,8 +545,16 @@ vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options,
544 * but we construct long opts so that flag 545 * but we construct long opts so that flag
545 * is always NULL (see above) */ 546 * is always NULL (see above) */
546 if (on_off->opt_char == '\0' /* && c != '\0' */) { 547 if (on_off->opt_char == '\0' /* && c != '\0' */) {
547 /* c is probably '?' - "bad option" */ 548 /* We reached the end of complementary[] and did not find -c */
548 goto error; 549 if (c == '?') /* getopt says: "bad option, or option has no required argument" */
550 goto error;
551 /* if there were options beyond 32 bits (example: ls),
552 * they got no complementary[] slot, and no result bit.
553 * IOW: they must be "accept but ignore" options.
554 * For them, we end up here.
555 */
556 //bb_error_msg("ignored option '%c', skipping", c);
557 goto next_opt;
549 } 558 }
550 } 559 }
551 if (flags & on_off->incongruously) 560 if (flags & on_off->incongruously)
diff --git a/libbb/hash_md5_sha.c b/libbb/hash_md5_sha.c
index 22dd890bf..fd56d831b 100644
--- a/libbb/hash_md5_sha.c
+++ b/libbb/hash_md5_sha.c
@@ -11,7 +11,7 @@
11#define STR1(s) #s 11#define STR1(s) #s
12#define STR(s) STR1(s) 12#define STR(s) STR1(s)
13 13
14#define NEED_SHA512 (ENABLE_SHA512SUM || ENABLE_USE_BB_CRYPT_SHA) 14#define NEED_SHA512 (ENABLE_SHA512SUM || ENABLE_SHA384SUM || ENABLE_USE_BB_CRYPT_SHA)
15 15
16#if ENABLE_FEATURE_USE_CNG_API 16#if ENABLE_FEATURE_USE_CNG_API
17# include <windows.h> 17# include <windows.h>
@@ -21,6 +21,7 @@
21# define BCRYPT_MD5_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000021) 21# define BCRYPT_MD5_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000021)
22# define BCRYPT_SHA1_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000031) 22# define BCRYPT_SHA1_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000031)
23# define BCRYPT_SHA256_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000041) 23# define BCRYPT_SHA256_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000041)
24# define BCRYPT_SHA384_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000051)
24# define BCRYPT_SHA512_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000061) 25# define BCRYPT_SHA512_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000061)
25 26
26/* Initialize structure containing state of computation. 27/* Initialize structure containing state of computation.
@@ -61,9 +62,18 @@ void FAST_FUNC sha256_begin(sha256_ctx_t *ctx)
61 generic_init(ctx, BCRYPT_SHA256_ALG_HANDLE); 62 generic_init(ctx, BCRYPT_SHA256_ALG_HANDLE);
62} 63}
63 64
64#if NEED_SHA512 65#if ENABLE_SHA384SUM
65/* Initialize structure containing state of computation. 66/* Initialize structure containing state of computation.
66 (FIPS 180-2:5.3.3) */ 67 (FIPS 180-2:5.3.3) */
68void FAST_FUNC sha384_begin(sha384_ctx_t *ctx)
69{
70 generic_init(ctx, BCRYPT_SHA384_ALG_HANDLE);
71}
72#endif /* ENABLE_SHA384SUM */
73
74#if NEED_SHA512
75/* Initialize structure containing state of computation.
76 (FIPS 180-2:5.3.4) */
67void FAST_FUNC sha512_begin(sha512_ctx_t *ctx) 77void FAST_FUNC sha512_begin(sha512_ctx_t *ctx)
68{ 78{
69 generic_init(ctx, BCRYPT_SHA512_ALG_HANDLE); 79 generic_init(ctx, BCRYPT_SHA512_ALG_HANDLE);
@@ -1109,7 +1119,7 @@ static const sha_K_int sha_K[] ALIGN8 = {
1109 K(0x84c87814a1f0ab72ULL), K(0x8cc702081a6439ecULL), 1119 K(0x84c87814a1f0ab72ULL), K(0x8cc702081a6439ecULL),
1110 K(0x90befffa23631e28ULL), K(0xa4506cebde82bde9ULL), 1120 K(0x90befffa23631e28ULL), K(0xa4506cebde82bde9ULL),
1111 K(0xbef9a3f7b2c67915ULL), K(0xc67178f2e372532bULL), 1121 K(0xbef9a3f7b2c67915ULL), K(0xc67178f2e372532bULL),
1112#if NEED_SHA512 /* [64]+ are used for sha512 only */ 1122#if NEED_SHA512 /* [64]+ are used for sha384 and sha512 only */
1113 K(0xca273eceea26619cULL), K(0xd186b8c721c0c207ULL), 1123 K(0xca273eceea26619cULL), K(0xd186b8c721c0c207ULL),
1114 K(0xeada7dd6cde0eb1eULL), K(0xf57d4f7fee6ed178ULL), 1124 K(0xeada7dd6cde0eb1eULL), K(0xf57d4f7fee6ed178ULL),
1115 K(0x06f067aa72176fbaULL), K(0x0a637dc5a2c898a6ULL), 1125 K(0x06f067aa72176fbaULL), K(0x0a637dc5a2c898a6ULL),
@@ -1306,11 +1316,20 @@ static const uint32_t init512_lo[] ALIGN4 = {
1306 0x137e2179, 1316 0x137e2179,
1307}; 1317};
1308#endif /* NEED_SHA512 */ 1318#endif /* NEED_SHA512 */
1309 1319#if ENABLE_SHA384SUM
1310// Note: SHA-384 is identical to SHA-512, except that initial hash values are 1320static const uint64_t init384[] ALIGN8 = {
1311// 0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939, 1321 0,
1312// 0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4, 1322 0,
1313// and the output is constructed by omitting last two 64-bit words of it. 1323 0xcbbb9d5dc1059ed8,
1324 0x629a292a367cd507,
1325 0x9159015a3070dd17,
1326 0x152fecd8f70e5939,
1327 0x67332667ffc00b31,
1328 0x8eb44a8768581511,
1329 0xdb0c2e0d64f98fa7,
1330 0x47b5481dbefa4fa4,
1331};
1332#endif
1314 1333
1315/* Initialize structure containing state of computation. 1334/* Initialize structure containing state of computation.
1316 (FIPS 180-2:5.3.2) */ 1335 (FIPS 180-2:5.3.2) */
@@ -1332,9 +1351,19 @@ void FAST_FUNC sha256_begin(sha256_ctx_t *ctx)
1332#endif 1351#endif
1333} 1352}
1334 1353
1335#if NEED_SHA512 1354#if ENABLE_SHA384SUM
1336/* Initialize structure containing state of computation. 1355/* Initialize structure containing state of computation.
1337 (FIPS 180-2:5.3.3) */ 1356 (FIPS 180-2:5.3.3) */
1357void FAST_FUNC sha384_begin(sha512_ctx_t *ctx)
1358{
1359 memcpy(&ctx->total64, init384, sizeof(init384));
1360 /*ctx->total64[0] = ctx->total64[1] = 0; - already done */
1361}
1362#endif
1363
1364#if NEED_SHA512
1365/* Initialize structure containing state of computation.
1366 (FIPS 180-2:5.3.4) */
1338void FAST_FUNC sha512_begin(sha512_ctx_t *ctx) 1367void FAST_FUNC sha512_begin(sha512_ctx_t *ctx)
1339{ 1368{
1340 int i; 1369 int i;
@@ -1409,7 +1438,7 @@ unsigned FAST_FUNC sha1_end(sha1_ctx_t *ctx, void *resbuf)
1409} 1438}
1410 1439
1411#if NEED_SHA512 1440#if NEED_SHA512
1412unsigned FAST_FUNC sha512_end(sha512_ctx_t *ctx, void *resbuf) 1441static unsigned FAST_FUNC sha512384_end(sha512_ctx_t *ctx, void *resbuf, unsigned outsize)
1413{ 1442{
1414 unsigned bufpos = ctx->total64[0] & 127; 1443 unsigned bufpos = ctx->total64[0] & 127;
1415 1444
@@ -1440,12 +1469,22 @@ unsigned FAST_FUNC sha512_end(sha512_ctx_t *ctx, void *resbuf)
1440 for (i = 0; i < ARRAY_SIZE(ctx->hash); ++i) 1469 for (i = 0; i < ARRAY_SIZE(ctx->hash); ++i)
1441 ctx->hash[i] = SWAP_BE64(ctx->hash[i]); 1470 ctx->hash[i] = SWAP_BE64(ctx->hash[i]);
1442 } 1471 }
1443 memcpy(resbuf, ctx->hash, sizeof(ctx->hash)); 1472 memcpy(resbuf, ctx->hash, outsize);
1444 return sizeof(ctx->hash); 1473 return outsize;
1474}
1475unsigned FAST_FUNC sha512_end(sha384_ctx_t *ctx, void *resbuf)
1476{
1477 return sha512384_end(ctx, resbuf, SHA512_OUTSIZE);
1445} 1478}
1446#endif /* NEED_SHA512 */ 1479#endif /* NEED_SHA512 */
1447#endif /* !ENABLE_FEATURE_USE_CNG_API */
1448 1480
1481#if ENABLE_SHA384SUM
1482unsigned FAST_FUNC sha384_end(sha384_ctx_t *ctx, void *resbuf)
1483{
1484 return sha512384_end(ctx, resbuf, SHA384_OUTSIZE);
1485}
1486#endif
1487#endif /* !ENABLE_FEATURE_USE_CNG_API */
1449 1488
1450/* 1489/*
1451 * The Keccak sponge function, designed by Guido Bertoni, Joan Daemen, 1490 * The Keccak sponge function, designed by Guido Bertoni, Joan Daemen,
@@ -1982,6 +2021,8 @@ void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len)
1982 2021
1983unsigned FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf) 2022unsigned FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf)
1984{ 2023{
2024 unsigned hash_len;
2025
1985 /* Padding */ 2026 /* Padding */
1986 uint8_t *buf = (uint8_t*)ctx->state; 2027 uint8_t *buf = (uint8_t*)ctx->state;
1987 /* 2028 /*
@@ -2004,6 +2045,7 @@ unsigned FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf)
2004 sha3_process_block72(ctx->state); 2045 sha3_process_block72(ctx->state);
2005 2046
2006 /* Output */ 2047 /* Output */
2007 memcpy(resbuf, ctx->state, 64); 2048 hash_len = (1600/8 - ctx->input_block_bytes) / 2;
2008 return 64; 2049 memcpy(resbuf, ctx->state, hash_len);
2050 return hash_len;
2009} 2051}
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index c8a0f37fe..77207a427 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -1754,7 +1754,7 @@ void FAST_FUNC save_history(line_input_t *st)
1754 FILE *fp; 1754 FILE *fp;
1755 1755
1756 /* bash compat: HISTFILE="" disables history saving */ 1756 /* bash compat: HISTFILE="" disables history saving */
1757 if (!st || !st->hist_file || !state->hist_file[0]) 1757 if (!st || !st->hist_file || !st->hist_file[0])
1758 return; 1758 return;
1759 if (st->cnt_history <= st->cnt_history_in_file) 1759 if (st->cnt_history <= st->cnt_history_in_file)
1760 return; /* no new entries were added */ 1760 return; /* no new entries were added */
diff --git a/libbb/procps.c b/libbb/procps.c
index 8c9cac125..c751100bc 100644
--- a/libbb/procps.c
+++ b/libbb/procps.c
@@ -110,7 +110,7 @@ void FAST_FUNC free_procps_scan(procps_status_t* sp)
110} 110}
111 111
112#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP 112#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
113static unsigned long long fast_strtoull_16(char **endptr) 113unsigned long long FAST_FUNC fast_strtoull_16(char **endptr)
114{ 114{
115 unsigned char c; 115 unsigned char c;
116 char *str = *endptr; 116 char *str = *endptr;
@@ -131,7 +131,7 @@ static unsigned long long fast_strtoull_16(char **endptr)
131 131
132#if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP 132#if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
133/* We cut a lot of corners here for speed */ 133/* We cut a lot of corners here for speed */
134static unsigned long fast_strtoul_10(char **endptr) 134unsigned long FAST_FUNC fast_strtoul_10(char **endptr)
135{ 135{
136 unsigned char c; 136 unsigned char c;
137 char *str = *endptr; 137 char *str = *endptr;
@@ -144,6 +144,24 @@ static unsigned long fast_strtoul_10(char **endptr)
144 *endptr = str + 1; /* We skip trailing space! */ 144 *endptr = str + 1; /* We skip trailing space! */
145 return n; 145 return n;
146} 146}
147# if LONG_MAX < LLONG_MAX
148/* For VSZ, which can be very large */
149static unsigned long long fast_strtoull_10(char **endptr)
150{
151 unsigned char c;
152 char *str = *endptr;
153 unsigned long long n = *str - '0';
154
155 /* Need to stop on both ' ' and '\n' */
156 while ((c = *++str) > ' ')
157 n = n*10 + (c - '0');
158
159 *endptr = str + 1; /* We skip trailing space! */
160 return n;
161}
162# else
163# define fast_strtoull_10(endptr) fast_strtoul_10(endptr)
164# endif
147 165
148# if ENABLE_FEATURE_FAST_TOP 166# if ENABLE_FEATURE_FAST_TOP
149static long fast_strtol_10(char **endptr) 167static long fast_strtol_10(char **endptr)
@@ -156,7 +174,7 @@ static long fast_strtol_10(char **endptr)
156} 174}
157# endif 175# endif
158 176
159static char *skip_fields(char *str, int count) 177char* FAST_FUNC skip_fields(char *str, int count)
160{ 178{
161 do { 179 do {
162 while (*str++ != ' ') 180 while (*str++ != ' ')
@@ -167,35 +185,25 @@ static char *skip_fields(char *str, int count)
167} 185}
168#endif 186#endif
169 187
170#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP 188#if ENABLE_FEATURE_TOPMEM
171static char* skip_whitespace_if_prefixed_with(char *buf, const char *prefix) 189static NOINLINE void procps_read_smaps(pid_t pid, procps_status_t *sp)
172{ 190{
173 char *tp = is_prefixed_with(buf, prefix); 191 // There is A LOT of /proc/PID/smaps data on a big system.
174 if (tp) { 192 // Optimize this for speed, makes "top -m" faster.
175 tp = skip_whitespace(tp); 193//TODO large speedup:
176 } 194//read /proc/PID/smaps_rollup (cumulative stats of all mappings, much faster)
177 return tp; 195//and /proc/PID/maps to get mapped_ro and mapped_rw (IOW: VSZ,VSZRW)
178}
179 196
180int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
181 void (*cb)(struct smaprec *, void *), void *data)
182{
183 FILE *file; 197 FILE *file;
184 struct smaprec currec;
185 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; 198 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3];
186 char buf[PROCPS_BUFSIZE]; 199 char buf[PROCPS_BUFSIZE];
187#if !ENABLE_PMAP
188 void (*cb)(struct smaprec *, void *) = NULL;
189 void *data = NULL;
190#endif
191 200
192 sprintf(filename, "/proc/%u/smaps", (int)pid); 201 sprintf(filename, "/proc/%u/smaps", (int)pid);
193 202
194 file = fopen_for_read(filename); 203 file = fopen_for_read(filename);
195 if (!file) 204 if (!file)
196 return 1; 205 return;
197 206
198 memset(&currec, 0, sizeof(currec));
199 while (fgets(buf, PROCPS_BUFSIZE, file)) { 207 while (fgets(buf, PROCPS_BUFSIZE, file)) {
200 // Each mapping datum has this form: 208 // Each mapping datum has this form:
201 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME 209 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
@@ -203,80 +211,53 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
203 // Rss: nnn kB 211 // Rss: nnn kB
204 // ..... 212 // .....
205 213
206 char *tp, *p; 214 char *tp;
207 215
216 if (buf[0] == 'S' || buf[0] == 'P') {
208#define SCAN(S, X) \ 217#define SCAN(S, X) \
209 if ((tp = skip_whitespace_if_prefixed_with(buf, S)) != NULL) { \ 218 if (memcmp(buf, S, sizeof(S)-1) == 0) { \
210 total->X += currec.X = fast_strtoul_10(&tp); \ 219 tp = skip_whitespace(buf + sizeof(S)-1); \
211 continue; \ 220 sp->X += fast_strtoul_10(&tp); \
212 } 221 continue; \
213 if (cb) { 222 }
214 SCAN("Pss:" , smap_pss ); 223 SCAN("Private_Dirty:", private_dirty)
215 SCAN("Swap:" , smap_swap ); 224 SCAN("Private_Clean:", private_clean)
216 } 225 SCAN("Shared_Dirty:" , shared_dirty )
217 SCAN("Private_Dirty:", private_dirty); 226 SCAN("Shared_Clean:" , shared_clean )
218 SCAN("Private_Clean:", private_clean);
219 SCAN("Shared_Dirty:" , shared_dirty );
220 SCAN("Shared_Clean:" , shared_clean );
221#undef SCAN 227#undef SCAN
228 }
222 tp = strchr(buf, '-'); 229 tp = strchr(buf, '-');
223 if (tp) { 230 if (tp) {
224 // We reached next mapping - the line of this form: 231 // We reached next mapping - the line of this form:
225 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME 232 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
226 233
227 if (cb) { 234 char *rwx;
228 /* If we have a previous record, there's nothing more 235 unsigned long sz;
229 * for it, call the callback and clear currec
230 */
231 if (currec.smap_size)
232 cb(&currec, data);
233 free(currec.smap_name);
234 }
235 memset(&currec, 0, sizeof(currec));
236 236
237 *tp = ' '; 237 *tp = ' ';
238 tp = buf; 238 tp = buf;
239 currec.smap_start = fast_strtoull_16(&tp); 239 sz = fast_strtoull_16(&tp); // start
240 currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10; 240 sz = (fast_strtoull_16(&tp) - sz) >> 10; // end - start
241 241 // tp -> "rw-s" string
242 strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1); 242 rwx = tp;
243
244 // skipping "rw-s FILEOFS M:m INODE " 243 // skipping "rw-s FILEOFS M:m INODE "
245 tp = skip_whitespace(skip_fields(tp, 4)); 244 tp = skip_whitespace(skip_fields(tp, 4));
246 // filter out /dev/something (something != zero) 245 // if not a device memory mapped...
247 if (!is_prefixed_with(tp, "/dev/") || strcmp(tp, "/dev/zero\n") == 0) { 246 if (memcmp(tp, "/dev/", 5) != 0 // not "/dev/something"
248 if (currec.smap_mode[1] == 'w') { 247 || strcmp(tp + 5, "zero\n") == 0 // or is "/dev/zero" (which isn't a device)
249 currec.mapped_rw = currec.smap_size; 248 ) {
250 total->mapped_rw += currec.smap_size; 249 if (rwx[1] == 'w')
251 } else if (currec.smap_mode[1] == '-') { 250 sp->mapped_rw += sz;
252 currec.mapped_ro = currec.smap_size; 251 else if (rwx[0] == 'r' || rwx[2] == 'x')
253 total->mapped_ro += currec.smap_size; 252 sp->mapped_ro += sz;
254 } 253 // else: seen "---p" mappings (mmap guard gaps?),
254 // do NOT account these as VSZ, they aren't really
255 } 255 }
256
257 if (strcmp(tp, "[stack]\n") == 0) 256 if (strcmp(tp, "[stack]\n") == 0)
258 total->stack += currec.smap_size; 257 sp->stack += sz;
259 if (cb) {
260 p = skip_non_whitespace(tp);
261 if (p == tp) {
262 currec.smap_name = xstrdup(" [ anon ]");
263 } else {
264 *p = '\0';
265 currec.smap_name = xstrdup(tp);
266 }
267 }
268 total->smap_size += currec.smap_size;
269 } 258 }
270 } 259 }
271 fclose(file); 260 fclose(file);
272
273 if (cb) {
274 if (currec.smap_size)
275 cb(&currec, data);
276 free(currec.smap_name);
277 }
278
279 return 0;
280} 261}
281#endif 262#endif
282 263
@@ -371,7 +352,8 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
371 char *cp, *comm1; 352 char *cp, *comm1;
372 int tty; 353 int tty;
373#if !ENABLE_FEATURE_FAST_TOP 354#if !ENABLE_FEATURE_FAST_TOP
374 unsigned long vsz, rss; 355 unsigned long long vsz;
356 unsigned long rss;
375#endif 357#endif
376 /* see proc(5) for some details on this */ 358 /* see proc(5) for some details on this */
377 strcpy(filename_tail, "stat"); 359 strcpy(filename_tail, "stat");
@@ -397,7 +379,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
397 "%ld " /* nice */ 379 "%ld " /* nice */
398 "%*s %*s " /* timeout, it_real_value */ 380 "%*s %*s " /* timeout, it_real_value */
399 "%lu " /* start_time */ 381 "%lu " /* start_time */
400 "%lu " /* vsize */ 382 "%llu " /* vsize - can be very large */
401 "%lu " /* rss */ 383 "%lu " /* rss */
402# if ENABLE_FEATURE_TOP_SMP_PROCESS 384# if ENABLE_FEATURE_TOP_SMP_PROCESS
403 "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ 385 "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */
@@ -450,7 +432,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
450 cp = skip_fields(cp, 2); /* timeout, it_real_value */ 432 cp = skip_fields(cp, 2); /* timeout, it_real_value */
451 sp->start_time = fast_strtoul_10(&cp); 433 sp->start_time = fast_strtoul_10(&cp);
452 /* vsz is in bytes and we want kb */ 434 /* vsz is in bytes and we want kb */
453 sp->vsz = fast_strtoul_10(&cp) >> 10; 435 sp->vsz = fast_strtoull_10(&cp) >> 10;
454 /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ 436 /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */
455 sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb; 437 sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb;
456# if ENABLE_FEATURE_TOP_SMP_PROCESS 438# if ENABLE_FEATURE_TOP_SMP_PROCESS
@@ -484,7 +466,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
484 466
485#if ENABLE_FEATURE_TOPMEM 467#if ENABLE_FEATURE_TOPMEM
486 if (flags & PSSCAN_SMAPS) 468 if (flags & PSSCAN_SMAPS)
487 procps_read_smaps(pid, &sp->smaps, NULL, NULL); 469 procps_read_smaps(pid, sp);
488#endif /* TOPMEM */ 470#endif /* TOPMEM */
489#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS 471#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
490 if (flags & PSSCAN_RUIDGID) { 472 if (flags & PSSCAN_RUIDGID) {
@@ -567,36 +549,45 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
567 return sp; 549 return sp;
568} 550}
569 551
570void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) 552int FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
571{ 553{
572 int sz; 554 int sz;
573 char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; 555 char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
574 556
575 sprintf(filename, "/proc/%u/cmdline", pid); 557 sprintf(filename, "/proc/%u/cmdline", pid);
576 sz = open_read_close(filename, buf, col - 1); 558 sz = open_read_close(filename, buf, col - 1);
559 if (sz < 0)
560 return sz;
577 if (sz > 0) { 561 if (sz > 0) {
578 const char *base; 562 const char *program_basename;
579 int comm_len; 563 int comm_len;
580 564
581 buf[sz] = '\0'; 565 buf[sz] = '\0';
582 while (--sz >= 0 && buf[sz] == '\0') 566 while (--sz >= 0 && buf[sz] == '\0')
583 continue; 567 continue;
584 /* Prevent basename("process foo/bar") = "bar" */ 568
585 strchrnul(buf, ' ')[0] = '\0'; 569 /* Find "program" in "[-][/PATH/TO/]program" */
586 base = bb_basename(buf); /* before we replace argv0's NUL with space */ 570 strchrnul(buf, ' ')[0] = '\0'; /* prevent basename("program foo/bar") = "bar" */
571 program_basename = bb_basename(buf[0] == '-' ? buf + 1 : buf);
572 /* ^^^ note: must do it *before* replacing argv0's NUL with space */
573
574 /* Prevent stuff like this:
575 * echo 'sleep 999; exit' >`printf '\ec'`; sh ?c
576 * messing up top and ps output (or worse).
577 * This also replaces NULs with spaces, converting
578 * list of NUL-strings into one string.
579 */
587 while (sz >= 0) { 580 while (sz >= 0) {
588 if ((unsigned char)(buf[sz]) < ' ') 581 if ((unsigned char)(buf[sz]) < ' ')
589 buf[sz] = ' '; 582 buf[sz] = ' ';
590 sz--; 583 sz--;
591 } 584 }
592 if (base[0] == '-') /* "-sh" (login shell)? */
593 base++;
594 585
595 /* If comm differs from argv0, prepend "{comm} ". 586 /* If comm differs from argv0, prepend "{comm} ".
596 * It allows to see thread names set by prctl(PR_SET_NAME). 587 * It allows to see thread names set by prctl(PR_SET_NAME).
597 */ 588 */
598 if (!comm) 589 if (!comm)
599 return; 590 return 0;
600 comm_len = strlen(comm); 591 comm_len = strlen(comm);
601 /* Why compare up to comm_len, not COMM_LEN-1? 592 /* Why compare up to comm_len, not COMM_LEN-1?
602 * Well, some processes rewrite argv, and use _spaces_ there 593 * Well, some processes rewrite argv, and use _spaces_ there
@@ -604,19 +595,20 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
604 * I prefer to still treat argv0 "process foo bar" 595 * I prefer to still treat argv0 "process foo bar"
605 * as 'equal' to comm "process". 596 * as 'equal' to comm "process".
606 */ 597 */
607 if (strncmp(base, comm, comm_len) != 0) { 598 if (strncmp(program_basename, comm, comm_len) != 0) {
608 comm_len += 3; 599 comm_len += 3;
609 if (col > comm_len) 600 if (col > comm_len)
610 memmove(buf + comm_len, buf, col - comm_len); 601 memmove(buf + comm_len, buf, col - comm_len);
611 snprintf(buf, col, "{%s}", comm); 602 snprintf(buf, col, "{%s}", comm);
612 if (col <= comm_len) 603 if (col <= comm_len)
613 return; 604 return 0;
614 buf[comm_len - 1] = ' '; 605 buf[comm_len - 1] = ' ';
615 buf[col - 1] = '\0'; 606 buf[col - 1] = '\0';
616 } 607 }
617 } else { 608 } else {
618 snprintf(buf, col, "[%s]", comm ? comm : "?"); 609 snprintf(buf, col, "[%s]", comm ? comm : "?");
619 } 610 }
611 return 0;
620} 612}
621 613
622#endif /* ENABLE_PLATFORM_MINGW32 */ 614#endif /* ENABLE_PLATFORM_MINGW32 */
diff --git a/libbb/replace.c b/libbb/replace.c
index 6183d3e6f..bc26b04cc 100644
--- a/libbb/replace.c
+++ b/libbb/replace.c
@@ -46,3 +46,17 @@ char* FAST_FUNC xmalloc_substitute_string(const char *src, int count, const char
46 //dbg_msg("subst9:'%s'", buf); 46 //dbg_msg("subst9:'%s'", buf);
47 return buf; 47 return buf;
48} 48}
49
50#if 0 /* inlined in libbb.h */
51/* Returns strlen as a bonus */
52size_t FAST_FUNC replace_char(char *str, char from, char to)
53{
54 char *p = str;
55 while (*p) {
56 if (*p == from)
57 *p = to;
58 p++;
59 }
60 return p - str;
61}
62#endif
diff --git a/libbb/yescrypt/alg-sha256.c b/libbb/yescrypt/alg-sha256.c
index 20e8d1ee4..dc748c968 100644
--- a/libbb/yescrypt/alg-sha256.c
+++ b/libbb/yescrypt/alg-sha256.c
@@ -47,9 +47,12 @@ PBKDF2_SHA256(const uint8_t *passwd, size_t passwdlen,
47 47
48 /* Iterate through the blocks. */ 48 /* Iterate through the blocks. */
49 for (i = 0; dkLen != 0; ) { 49 for (i = 0; dkLen != 0; ) {
50 uint64_t U[32 / 8]; 50 long U[32 / sizeof(long)];
51 uint64_t T[32 / 8]; 51 long T[32 / sizeof(long)];
52 uint64_t j; 52// Do not make these ^^ uint64_t[]. Keep them long[].
53// Even though the XORing loop below is optimized out,
54// gcc is not smart enough to realize that 64-bit alignment of the stack
55// is no longer useful, and generates ~50 more bytes of code on i386...
53 uint32_t ivec; 56 uint32_t ivec;
54 size_t clen; 57 size_t clen;
55 int k; 58 int k;
@@ -64,13 +67,15 @@ PBKDF2_SHA256(const uint8_t *passwd, size_t passwdlen,
64//does libbb need a non-vararg version with just one (buf,len)? 67//does libbb need a non-vararg version with just one (buf,len)?
65 68
66 if (c > 1) { 69 if (c > 1) {
70//in yescrypt, c is always 1, so this if() branch is optimized out
71 uint64_t j;
67 /* T_i = U_1 ... */ 72 /* T_i = U_1 ... */
68 memcpy(U, T, 32); 73 memcpy(U, T, 32);
69 for (j = 2; j <= c; j++) { 74 for (j = 2; j <= c; j++) {
70 /* Compute U_j. */ 75 /* Compute U_j. */
71 hmac_peek_hash(&Phctx, (void*)U, U, 32, NULL); 76 hmac_peek_hash(&Phctx, (void*)U, U, 32, NULL);
72 /* ... xor U_j ... */ 77 /* ... xor U_j ... */
73 for (k = 0; k < 32 / 8; k++) 78 for (k = 0; k < 32 / sizeof(long); k++)
74 T[k] ^= U[k]; 79 T[k] ^= U[k];
75 //TODO: xorbuf32_aligned_long(T, U); 80 //TODO: xorbuf32_aligned_long(T, U);
76 } 81 }
diff --git a/miscutils/crond.c b/miscutils/crond.c
index 96131cae4..6a384fdfb 100644
--- a/miscutils/crond.c
+++ b/miscutils/crond.c
@@ -989,7 +989,7 @@ static int check_completions(void)
989 if (line->cl_pid <= 0) 989 if (line->cl_pid <= 0)
990 continue; 990 continue;
991 991
992 r = waitpid(line->cl_pid, NULL, WNOHANG); 992 r = safe_waitpid(line->cl_pid, NULL, WNOHANG);
993 if (r < 0 || r == line->cl_pid) { 993 if (r < 0 || r == line->cl_pid) {
994 process_finished_job(file->cf_username, line); 994 process_finished_job(file->cf_username, line);
995 if (line->cl_pid == 0) { 995 if (line->cl_pid == 0) {
@@ -1001,6 +1001,14 @@ static int check_completions(void)
1001 /* else: r == 0: "process is still running" */ 1001 /* else: r == 0: "process is still running" */
1002 file->cf_has_running = 1; 1002 file->cf_has_running = 1;
1003 } 1003 }
1004
1005 /* Reap any other children we don't actively track.
1006 * Reportedly, some people run crond as init process!
1007 * Thus, we need to reap orphans, like init does.
1008 */
1009 while (wait_any_nohang(NULL) > 0)
1010 continue;
1011
1004//FIXME: if !file->cf_has_running && file->deleted: delete it! 1012//FIXME: if !file->cf_has_running && file->deleted: delete it!
1005//otherwise deleted entries will stay forever, right? 1013//otherwise deleted entries will stay forever, right?
1006 num_still_running += file->cf_has_running; 1014 num_still_running += file->cf_has_running;
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c
index 77e42e3fb..31a215a29 100644
--- a/modutils/modprobe-small.c
+++ b/modutils/modprobe-small.c
@@ -186,15 +186,6 @@ static char* find_keyword(char *ptr, size_t len, const char *word)
186 return NULL; 186 return NULL;
187} 187}
188 188
189static void replace(char *s, char what, char with)
190{
191 while (*s) {
192 if (what == *s)
193 *s = with;
194 ++s;
195 }
196}
197
198static char *filename2modname(const char *filename, char *modname) 189static char *filename2modname(const char *filename, char *modname)
199{ 190{
200 int i; 191 int i;
@@ -230,7 +221,7 @@ static char* str_2_list(const char *str)
230 dst[len] = '\0'; 221 dst[len] = '\0';
231 memcpy(dst, str, len); 222 memcpy(dst, str, len);
232//TODO: protect against 2+ spaces: "word word" 223//TODO: protect against 2+ spaces: "word word"
233 replace(dst, ' ', '\0'); 224 replace_char(dst, ' ', '\0');
234 return dst; 225 return dst;
235} 226}
236 227
@@ -369,14 +360,14 @@ static int parse_module(module_info *info, const char *pathname)
369 } 360 }
370 bksp(); /* remove last ' ' */ 361 bksp(); /* remove last ' ' */
371 info->aliases = copy_stringbuf(); 362 info->aliases = copy_stringbuf();
372 replace(info->aliases, '-', '_'); 363 replace_char(info->aliases, '-', '_');
373 364
374 /* "dependency1 depandency2" */ 365 /* "dependency1 depandency2" */
375 reset_stringbuf(); 366 reset_stringbuf();
376 ptr = find_keyword(module_image, len, "depends="); 367 ptr = find_keyword(module_image, len, "depends=");
377 if (ptr && *ptr) { 368 if (ptr && *ptr) {
378 replace(ptr, ',', ' '); 369 replace_char(ptr, ',', ' ');
379 replace(ptr, '-', '_'); 370 replace_char(ptr, '-', '_');
380 dbg2_error_msg("dep:'%s'", ptr); 371 dbg2_error_msg("dep:'%s'", ptr);
381 append(ptr); 372 append(ptr);
382 } 373 }
@@ -707,7 +698,7 @@ static int process_module(char *name, const char *cmdline_options)
707 698
708 dbg1_error_msg("process_module('%s','%s')", name, cmdline_options); 699 dbg1_error_msg("process_module('%s','%s')", name, cmdline_options);
709 700
710 replace(name, '-', '_'); 701 replace_char(name, '-', '_');
711 702
712 dbg1_error_msg("already_loaded:%d is_remove:%d", already_loaded(name), is_remove); 703 dbg1_error_msg("already_loaded:%d is_remove:%d", already_loaded(name), is_remove);
713 704
@@ -735,7 +726,7 @@ static int process_module(char *name, const char *cmdline_options)
735 char *opt_filename = xasprintf("/etc/modules/%s", name); 726 char *opt_filename = xasprintf("/etc/modules/%s", name);
736 options = xmalloc_open_read_close(opt_filename, NULL); 727 options = xmalloc_open_read_close(opt_filename, NULL);
737 if (options) 728 if (options)
738 replace(options, '\n', ' '); 729 replace_char(options, '\n', ' ');
739#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS 730#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS
740 if (cmdline_options) { 731 if (cmdline_options) {
741 /* NB: cmdline_options always have one leading ' ' 732 /* NB: cmdline_options always have one leading ' '
diff --git a/modutils/modprobe.c b/modutils/modprobe.c
index 543f53e99..f890abe53 100644
--- a/modutils/modprobe.c
+++ b/modutils/modprobe.c
@@ -579,7 +579,7 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
579 parser_t *p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read); 579 parser_t *p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read);
580 580
581 for (i = 0; argv[i]; i++) 581 for (i = 0; argv[i]; i++)
582 replace(argv[i], '-', '_'); 582 replace_char(argv[i], '-', '_');
583 583
584 while (config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)) { 584 while (config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)) {
585 colon = last_char_is(tokens[0], ':'); 585 colon = last_char_is(tokens[0], ':');
diff --git a/modutils/modutils.c b/modutils/modutils.c
index cbff20961..862f71f57 100644
--- a/modutils/modutils.c
+++ b/modutils/modutils.c
@@ -69,15 +69,6 @@ void FAST_FUNC moddb_free(module_db *db)
69 } 69 }
70} 70}
71 71
72void FAST_FUNC replace(char *s, char what, char with)
73{
74 while (*s) {
75 if (what == *s)
76 *s = with;
77 ++s;
78 }
79}
80
81int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim) 72int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim)
82{ 73{
83 char *tok; 74 char *tok;
diff --git a/modutils/modutils.h b/modutils/modutils.h
index 4a702e97c..9b05116d1 100644
--- a/modutils/modutils.h
+++ b/modutils/modutils.h
@@ -47,7 +47,6 @@ module_entry *moddb_get(module_db *db, const char *s) FAST_FUNC;
47module_entry *moddb_get_or_create(module_db *db, const char *s) FAST_FUNC; 47module_entry *moddb_get_or_create(module_db *db, const char *s) FAST_FUNC;
48void moddb_free(module_db *db) FAST_FUNC; 48void moddb_free(module_db *db) FAST_FUNC;
49 49
50void replace(char *s, char what, char with) FAST_FUNC;
51int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC; 50int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC;
52char *filename2modname(const char *filename, char *modname) FAST_FUNC; 51char *filename2modname(const char *filename, char *modname) FAST_FUNC;
53#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS 52#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS
diff --git a/networking/ftpd.c b/networking/ftpd.c
index 0d6a289c7..c3125410e 100644
--- a/networking/ftpd.c
+++ b/networking/ftpd.c
@@ -190,54 +190,39 @@ struct globals {
190} while (0) 190} while (0)
191 191
192 192
193/* escape_text("pfx:", str, (0xff << 8) + '\r')
194 * Duplicate 0xff, append \r ^^^^^^^^^^^^^^^^^^
195 */
193static char * 196static char *
194escape_text(const char *prepend, const char *str, unsigned escapee) 197escape_text(const char *prepend, const char *str, unsigned escapee)
195{ 198{
196 unsigned retlen, remainlen, chunklen; 199 char *ret, *p;
197 char *ret, *found;
198 char append; 200 char append;
199 201
200 append = (char)escapee; 202 append = (char)escapee;
201 escapee >>= 8; 203 escapee >>= 8;
202 204
203 remainlen = strlen(str); 205 ret = xmalloc(strlen(prepend) + strlen(str) * 2 + 1 + 1);
204 retlen = strlen(prepend); 206 p = stpcpy(ret, prepend);
205 ret = xmalloc(retlen + remainlen * 2 + 1 + 1);
206 strcpy(ret, prepend);
207 207
208 for (;;) { 208 for (;;) {
209 found = strchrnul(str, escapee); 209 char *found = strchrnul(str, escapee);
210 chunklen = found - str + 1;
211 210
212 /* Copy chunk up to and including escapee (or NUL) to ret */ 211 /* Copy up to and including escapee (or NUL) */
213 memcpy(ret + retlen, str, chunklen); 212 p = mempcpy(p, str, found - str + 1);
214 retlen += chunklen;
215 213
216 if (*found == '\0') { 214 if (*found == '\0') {
217 /* It wasn't escapee, it was NUL! */ 215 /* It wasn't escapee, it was NUL! */
218 ret[retlen - 1] = append; /* replace NUL */
219 ret[retlen] = '\0'; /* add NUL */
220 break; 216 break;
221 } 217 }
222 ret[retlen++] = escapee; /* duplicate escapee */
223 str = found + 1; 218 str = found + 1;
219 *p++ = escapee; /* duplicate escapee */
224 } 220 }
221 p[-1] = append; /* replace NUL */
222 *p = '\0'; /* add NUL */
225 return ret; 223 return ret;
226} 224}
227 225
228/* Returns strlen as a bonus */
229static unsigned
230replace_char(char *str, char from, char to)
231{
232 char *p = str;
233 while (*p) {
234 if (*p == from)
235 *p = to;
236 p++;
237 }
238 return p - str;
239}
240
241static void 226static void
242verbose_log(const char *str) 227verbose_log(const char *str)
243{ 228{
diff --git a/procps/pmap.c b/procps/pmap.c
index 49f7688d9..3069856a4 100644
--- a/procps/pmap.c
+++ b/procps/pmap.c
@@ -29,10 +29,14 @@
29 29
30#if ULLONG_MAX == 0xffffffff 30#if ULLONG_MAX == 0xffffffff
31# define TABS "\t" 31# define TABS "\t"
32# define SIZEWIDTHx "7"
33# define SIZEWIDTH "9"
32# define AFMTLL "8" 34# define AFMTLL "8"
33# define DASHES "" 35# define DASHES ""
34#else 36#else
35# define TABS "\t\t" 37# define TABS "\t\t"
38# define SIZEWIDTHx "15"
39# define SIZEWIDTH "17"
36# define AFMTLL "16" 40# define AFMTLL "16"
37# define DASHES "--------" 41# define DASHES "--------"
38#endif 42#endif
@@ -42,49 +46,145 @@ enum {
42 OPT_q = 1 << 1, 46 OPT_q = 1 << 1,
43}; 47};
44 48
45static void print_smaprec(struct smaprec *currec, void *data) 49struct smaprec {
46{ 50 // For mixed 32/64 userspace, 32-bit pmap still needs
47 unsigned opt = (uintptr_t)data; 51 // 64-bit field here to correctly show 64-bit processes:
52 unsigned long long smap_start;
53 // Make size wider too:
54 // I've seen 1203765248 kb large "---p" mapping in a browser,
55 // this cuts close to 4 terabytes.
56 unsigned long long smap_size;
57 // (strictly speaking, other fields need to be wider too,
58 // but they are in kbytes, not bytes, and they hold sizes,
59 // not start addresses, sizes tend to be less than 4 terabytes)
60 unsigned long private_dirty;
61 unsigned long smap_pss, smap_swap;
62 char smap_mode[5];
63 char *smap_name;
64};
48 65
66// How long the filenames and command lines we want to handle?
67#define PMAP_BUFSZ 4096
68
69static void print_smaprec(struct smaprec *currec)
70{
49 printf("%0" AFMTLL "llx ", currec->smap_start); 71 printf("%0" AFMTLL "llx ", currec->smap_start);
50 72
51 if (opt & OPT_x) 73 if (option_mask32 & OPT_x)
52 printf("%7lu %7lu %7lu %7lu ", 74 printf("%7llu %7lu %7lu %7lu ",
53 currec->smap_size, 75 currec->smap_size,
54 currec->smap_pss, 76 currec->smap_pss,
55 currec->private_dirty, 77 currec->private_dirty,
56 currec->smap_swap); 78 currec->smap_swap);
57 else 79 else
58 printf("%7luK", currec->smap_size); 80 printf("%7lluK", currec->smap_size);
59 81
60 printf(" %.4s %s\n", currec->smap_mode, currec->smap_name); 82 printf(" %.4s %s\n", currec->smap_mode, currec->smap_name ? : " [ anon ]");
83}
84
85/* libbb's procps_read_smaps() looks somewhat similar,
86 * but the collected information is sufficiently different
87 * that merging them into one function is not a good idea
88 * (unless you feel masochistic today).
89 */
90static int read_smaps(pid_t pid, char buf[PMAP_BUFSZ], struct smaprec *total)
91{
92 FILE *file;
93 struct smaprec currec;
94 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3];
95
96 sprintf(filename, "/proc/%u/smaps", (int)pid);
97
98 file = fopen_for_read(filename);
99 if (!file)
100 return 1;
101
102 memset(&currec, 0, sizeof(currec));
103 while (fgets(buf, PMAP_BUFSZ, file)) {
104 // Each mapping datum has this form:
105 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
106 // Size: nnn kB
107 // Rss: nnn kB
108 // .....
109
110 char *tp, *p;
111
112 if (buf[0] == 'S' || buf[0] == 'P') {
113#define SCAN(S, X) \
114 if (memcmp(buf, S, sizeof(S)-1) == 0) { \
115 tp = skip_whitespace(buf + sizeof(S)-1); \
116 total->X += currec.X = fast_strtoul_10(&tp); \
117 continue; \
118 }
119 SCAN("Pss:" , smap_pss );
120 SCAN("Swap:" , smap_swap );
121 SCAN("Private_Dirty:", private_dirty);
122#undef SCAN
123 }
124 tp = strchr(buf, '-');
125 if (tp) {
126 // We reached next mapping - the line of this form:
127 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
128
129 // If we have a previous record, there's nothing more
130 // for it, print and clear currec
131 if (currec.smap_size)
132 print_smaprec(&currec);
133 free(currec.smap_name);
134 memset(&currec, 0, sizeof(currec));
135
136 *tp = ' ';
137 tp = buf;
138 currec.smap_start = fast_strtoull_16(&tp);
139 currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10;
140 strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1);
141
142 // skipping "rw-s FILEOFS M:m INODE "
143 tp = skip_fields(tp, 4);
144 tp = skip_whitespace(tp); // there may be many spaces, can't just "tp++"
145 p = strchrnul(tp, '\n');
146 if (p != tp) {
147 currec.smap_name = xstrndup(tp, p - tp);
148 }
149 total->smap_size += currec.smap_size;
150 }
151 } // while (got line)
152 fclose(file);
153
154 if (currec.smap_size)
155 print_smaprec(&currec);
156 free(currec.smap_name);
157
158 return 0;
61} 159}
62 160
63static int procps_get_maps(pid_t pid, unsigned opt) 161static int procps_get_maps(pid_t pid, unsigned opt)
64{ 162{
65 struct smaprec total; 163 struct smaprec total;
66 int ret; 164 int ret;
67 char buf[256]; 165 char buf[PMAP_BUFSZ];
166
167 ret = read_cmdline(buf, sizeof(buf), pid, NULL);
168 if (ret < 0)
169 return ret;
68 170
69 read_cmdline(buf, sizeof(buf), pid, NULL);
70 printf("%u: %s\n", (int)pid, buf); 171 printf("%u: %s\n", (int)pid, buf);
71 172
72 if (!(opt & OPT_q) && (opt & OPT_x)) 173 if (!(opt & OPT_q) && (opt & OPT_x))
73 puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping"); 174 puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping");
74 175
75 memset(&total, 0, sizeof(total)); 176 memset(&total, 0, sizeof(total));
76 177 ret = read_smaps(pid, buf, &total);
77 ret = procps_read_smaps(pid, &total, print_smaprec, (void*)(uintptr_t)opt);
78 if (ret) 178 if (ret)
79 return ret; 179 return ret;
80 180
81 if (!(opt & OPT_q)) { 181 if (!(opt & OPT_q)) {
82 if (opt & OPT_x) 182 if (opt & OPT_x)
83 printf("--------" DASHES " ------ ------ ------ ------\n" 183 printf("--------" DASHES " ------ ------ ------ ------\n"
84 "total" TABS " %7lu %7lu %7lu %7lu\n", 184 "total kB %"SIZEWIDTHx"llu %7lu %7lu %7lu\n",
85 total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap); 185 total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap);
86 else 186 else
87 printf("mapped: %luK\n", total.smap_size); 187 printf(" total %"SIZEWIDTH"lluK\n", total.smap_size);
88 } 188 }
89 189
90 return 0; 190 return 0;
diff --git a/procps/top.c b/procps/top.c
index 09d31c673..96b3e2d4e 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -117,10 +117,15 @@
117 117
118#include "libbb.h" 118#include "libbb.h"
119 119
120#define ESC "\033" 120#define ESC "\033"
121#define HOME ESC"[H"
122#define CLREOS ESC"[J"
123#define CLREOL ESC"[K"
124#define REVERSE ESC"[7m"
125#define NORMAL ESC"[m"
121 126
122typedef struct top_status_t { 127typedef struct top_status_t {
123 unsigned long vsz; 128 unsigned long memsize;
124#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 129#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
125 unsigned long ticks; 130 unsigned long ticks;
126 unsigned pcpu; /* delta of ticks */ 131 unsigned pcpu; /* delta of ticks */
@@ -161,13 +166,16 @@ struct globals {
161 top_status_t *top; 166 top_status_t *top;
162 int ntop; 167 int ntop;
163 smallint inverted; 168 smallint inverted;
169 smallint not_first_line;
164#if ENABLE_FEATURE_TOPMEM 170#if ENABLE_FEATURE_TOPMEM
165 smallint sort_field; 171 smallint sort_field;
166#endif 172#endif
167#if ENABLE_FEATURE_TOP_SMP_CPU 173#if ENABLE_FEATURE_TOP_SMP_CPU
168 smallint smp_cpu_info; /* one/many cpu info lines? */ 174 smallint smp_cpu_info; /* one/many cpu info lines? */
169#endif 175#endif
170 unsigned lines; /* screen height */ 176 int lines_remaining;
177 unsigned lines; /* screen height */
178 unsigned scr_width; /* width, clamped <= LINE_BUF_SIZE-2 */
171#if ENABLE_FEATURE_TOP_INTERACTIVE 179#if ENABLE_FEATURE_TOP_INTERACTIVE
172 struct termios initial_settings; 180 struct termios initial_settings;
173 int scroll_ofs; 181 int scroll_ofs;
@@ -212,7 +220,6 @@ struct globals {
212#define cpu_prev_jif (G.cpu_prev_jif ) 220#define cpu_prev_jif (G.cpu_prev_jif )
213#define num_cpus (G.num_cpus ) 221#define num_cpus (G.num_cpus )
214#define total_pcpu (G.total_pcpu ) 222#define total_pcpu (G.total_pcpu )
215#define line_buf (G.line_buf )
216#define INIT_G() do { \ 223#define INIT_G() do { \
217 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 224 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
218 BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \ 225 BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \
@@ -241,8 +248,8 @@ static int pid_sort(top_status_t *P, top_status_t *Q)
241static int mem_sort(top_status_t *P, top_status_t *Q) 248static int mem_sort(top_status_t *P, top_status_t *Q)
242{ 249{
243 /* We want to avoid unsigned->signed and truncation errors */ 250 /* We want to avoid unsigned->signed and truncation errors */
244 if (Q->vsz < P->vsz) return -1; 251 if (Q->memsize < P->memsize) return -1;
245 return Q->vsz != P->vsz; /* 0 if ==, 1 if > */ 252 return Q->memsize != P->memsize; /* 0 if ==, 1 if > */
246} 253}
247 254
248 255
@@ -283,9 +290,9 @@ static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif)
283#endif 290#endif
284 int ret; 291 int ret;
285 292
286 if (!fgets(line_buf, LINE_BUF_SIZE, fp) || line_buf[0] != 'c' /* not "cpu" */) 293 if (!fgets(G.line_buf, LINE_BUF_SIZE, fp) || G.line_buf[0] != 'c' /* not "cpu" */)
287 return 0; 294 return 0;
288 ret = sscanf(line_buf, fmt, 295 ret = sscanf(G.line_buf, fmt,
289 &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle, 296 &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle,
290 &p_jif->iowait, &p_jif->irq, &p_jif->softirq, 297 &p_jif->iowait, &p_jif->irq, &p_jif->softirq,
291 &p_jif->steal); 298 &p_jif->steal);
@@ -362,7 +369,7 @@ static void do_stats(void)
362 369
363 get_jiffy_counts(); 370 get_jiffy_counts();
364 total_pcpu = 0; 371 total_pcpu = 0;
365 /* total_vsz = 0; */ 372 /* total_memsize = 0; */
366 new_hist = xmalloc(sizeof(new_hist[0]) * ntop); 373 new_hist = xmalloc(sizeof(new_hist[0]) * ntop);
367 /* 374 /*
368 * Make a pass through the data to get stats. 375 * Make a pass through the data to get stats.
@@ -394,7 +401,7 @@ static void do_stats(void)
394 i = (i+1) % prev_hist_count; 401 i = (i+1) % prev_hist_count;
395 /* hist_iterations++; */ 402 /* hist_iterations++; */
396 } while (i != last_i); 403 } while (i != last_i);
397 /* total_vsz += cur->vsz; */ 404 /* total_memsize += cur->memsize; */
398 } 405 }
399 406
400 /* 407 /*
@@ -407,6 +414,38 @@ static void do_stats(void)
407 414
408#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */ 415#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
409 416
417static void print_line_buf(void)
418{
419 const char *fmt;
420
421 G.lines_remaining--;
422 fmt = OPT_BATCH_MODE ? "\n""%.*s" : "\n""%.*s"CLREOL;
423 if (!G.not_first_line) {
424 G.not_first_line = 1;
425 /* Go to top */
426 fmt = OPT_BATCH_MODE ? "%.*s" : HOME"%.*s"CLREOL;
427 }
428 printf(fmt, G.scr_width - 1, G.line_buf);
429}
430
431static void print_line_bold(void)
432{
433 G.lines_remaining--;
434//we never print first line in bold
435// if (!G.not_first_line) {
436// printf(OPT_BATCH_MODE ? "%.*s" : HOME"%.*s"CLREOL, G.scr_width - 1, G.line_buf);
437// G.not_first_line = 1;
438// } else {
439 printf(OPT_BATCH_MODE ? "\n""%.*s" : "\n"REVERSE"%.*s"NORMAL CLREOL, G.scr_width - 1, G.line_buf);
440// }
441}
442
443static void print_end(void)
444{
445 fputs_stdout(OPT_BATCH_MODE ? "\n" : CLREOS"\r");
446 G.not_first_line = 0; /* next print will be "first line" (will clear the screen) */
447}
448
410#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS 449#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS
411/* formats 7 char string (8 with terminating NUL) */ 450/* formats 7 char string (8 with terminating NUL) */
412static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total) 451static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
@@ -433,7 +472,7 @@ static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
433#endif 472#endif
434 473
435#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS 474#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
436static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) 475static void display_cpus(void)
437{ 476{
438 /* 477 /*
439 * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100% 478 * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100%
@@ -469,8 +508,8 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
469# else 508# else
470 /* Loop thru CPU(s) */ 509 /* Loop thru CPU(s) */
471 n_cpu_lines = smp_cpu_info ? num_cpus : 1; 510 n_cpu_lines = smp_cpu_info ? num_cpus : 1;
472 if (n_cpu_lines > *lines_rem_p) 511 if (n_cpu_lines > G.lines_remaining)
473 n_cpu_lines = *lines_rem_p; 512 n_cpu_lines = G.lines_remaining;
474 513
475 for (i = 0; i < n_cpu_lines; i++) { 514 for (i = 0; i < n_cpu_lines; i++) {
476 p_jif = &cpu_jif[i]; 515 p_jif = &cpu_jif[i];
@@ -488,7 +527,7 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
488 CALC_STAT(softirq); 527 CALC_STAT(softirq);
489 /*CALC_STAT(steal);*/ 528 /*CALC_STAT(steal);*/
490 529
491 snprintf(scrbuf, scr_width, 530 sprintf(G.line_buf,
492 /* Barely fits in 79 chars when in "decimals" mode. */ 531 /* Barely fits in 79 chars when in "decimals" mode. */
493# if ENABLE_FEATURE_TOP_SMP_CPU 532# if ENABLE_FEATURE_TOP_SMP_CPU
494 "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq", 533 "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
@@ -501,16 +540,15 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
501 /*, SHOW_STAT(steal) - what is this 'steal' thing? */ 540 /*, SHOW_STAT(steal) - what is this 'steal' thing? */
502 /* I doubt anyone wants to know it */ 541 /* I doubt anyone wants to know it */
503 ); 542 );
504 puts(scrbuf); 543 print_line_buf();
505 } 544 }
506 } 545 }
507# undef SHOW_STAT 546# undef SHOW_STAT
508# undef CALC_STAT 547# undef CALC_STAT
509# undef FMT 548# undef FMT
510 *lines_rem_p -= i;
511} 549}
512#else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */ 550#else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */
513# define display_cpus(scr_width, scrbuf, lines_rem) ((void)0) 551# define display_cpus() ((void)0)
514#endif 552#endif
515 553
516enum { 554enum {
@@ -564,52 +602,55 @@ static void parse_meminfo(unsigned long meminfo[MI_MAX])
564 fclose(f); 602 fclose(f);
565} 603}
566 604
567static unsigned long display_header(int scr_width, int *lines_rem_p) 605static void cmdline_to_line_buf_and_print(unsigned offset, unsigned pid, const char *comm)
606{
607 int width = G.scr_width - offset;
608 if (width > 1) /* wider than to fit just the NUL? */
609 read_cmdline(G.line_buf + offset, width, pid, comm);
610//TODO: read_cmdline() sanitizes control chars, but not chars above 0x7e
611 print_line_buf();
612}
613
614static unsigned long display_header(void)
568{ 615{
569 char scrbuf[100]; /* [80] was a bit too low on 8Gb ram box */
570 char *buf; 616 char *buf;
571 unsigned long meminfo[MI_MAX]; 617 unsigned long meminfo[MI_MAX];
572 618
573 parse_meminfo(meminfo); 619 parse_meminfo(meminfo);
574 620
575 /* Output memory info */ 621 /* Output memory info */
576 if (scr_width > (int)sizeof(scrbuf)) 622 sprintf(G.line_buf,
577 scr_width = sizeof(scrbuf);
578 snprintf(scrbuf, scr_width,
579 "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached", 623 "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached",
580 meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE], 624 meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE],
581 meminfo[MI_MEMFREE], 625 meminfo[MI_MEMFREE],
582 meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM], 626 meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM],
583 meminfo[MI_BUFFERS], 627 meminfo[MI_BUFFERS],
584 meminfo[MI_CACHED]); 628 meminfo[MI_CACHED]);
585 /* Go to top & clear to the end of screen */ 629 print_line_buf();
586 printf(OPT_BATCH_MODE ? "%s\n" : ESC"[H" ESC"[J" "%s\n", scrbuf);
587 (*lines_rem_p)--;
588 630
589 /* Display CPU time split as percentage of total time. 631 /* Display CPU time split as percentage of total time.
590 * This displays either a cumulative line or one line per CPU. 632 * This displays either a cumulative line or one line per CPU.
591 */ 633 */
592 display_cpus(scr_width, scrbuf, lines_rem_p); 634 display_cpus();
593 635
594 /* Read load average as a string */ 636 /* Read load average as a string */
595 buf = stpcpy(scrbuf, "Load average: "); 637 buf = stpcpy(G.line_buf, "Load average: ");
596 open_read_close("loadavg", buf, sizeof(scrbuf) - sizeof("Load average: ")); 638 open_read_close("loadavg", buf, sizeof(G.line_buf) - sizeof("Load average: "));
597 scrbuf[scr_width - 1] = '\0'; 639 G.line_buf[sizeof(G.line_buf) - 1] = '\0'; /* paranoia */
598 strchrnul(buf, '\n')[0] = '\0'; 640 strchrnul(buf, '\n')[0] = '\0';
599 puts(scrbuf); 641 print_line_buf();
600 (*lines_rem_p)--;
601 642
602 return meminfo[MI_MEMTOTAL]; 643 return meminfo[MI_MEMTOTAL];
603} 644}
604 645
605static NOINLINE void display_process_list(int lines_rem, int scr_width) 646static NOINLINE void display_process_list(void)
606{ 647{
607 enum { 648 enum {
608 BITS_PER_INT = sizeof(int) * 8 649 BITS_PER_INT = sizeof(int) * 8
609 }; 650 };
610 651
611 top_status_t *s; 652 top_status_t *s;
612 unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */ 653 unsigned long total_memory = display_header();
613 /* xxx_shift and xxx_scale variables allow us to replace 654 /* xxx_shift and xxx_scale variables allow us to replace
614 * expensive divides with multiply and shift */ 655 * expensive divides with multiply and shift */
615 unsigned pmem_shift, pmem_scale, pmem_half; 656 unsigned pmem_shift, pmem_scale, pmem_half;
@@ -621,7 +662,7 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width)
621 662
622#if ENABLE_FEATURE_TOP_DECIMALS 663#if ENABLE_FEATURE_TOP_DECIMALS
623# define UPSCALE 1000 664# define UPSCALE 1000
624typedef struct { unsigned quot, rem; } bb_div_t; 665 typedef struct { unsigned quot, rem; } bb_div_t;
625/* Used to have "div_t name = div((val), 10)" here 666/* Used to have "div_t name = div((val), 10)" here
626 * (IOW: intended to use libc-compatible way to divide and use 667 * (IOW: intended to use libc-compatible way to divide and use
627 * both result and remainder, but musl does not inline div()...) 668 * both result and remainder, but musl does not inline div()...)
@@ -629,28 +670,34 @@ typedef struct { unsigned quot, rem; } bb_div_t;
629 */ 670 */
630# define CALC_STAT(name, val) bb_div_t name = { (val) / 10, (val) % 10 } 671# define CALC_STAT(name, val) bb_div_t name = { (val) / 10, (val) % 10 }
631# define SHOW_STAT(name) name.quot, '0'+name.rem 672# define SHOW_STAT(name) name.quot, '0'+name.rem
673# define SANITIZE(name) if (name.quot > 99) name.quot = 99, name.rem = (unsigned char)('+' - '0')
632# define FMT "%3u.%c" 674# define FMT "%3u.%c"
633#else 675#else
634# define UPSCALE 100 676# define UPSCALE 100
635# define CALC_STAT(name, val) unsigned name = (val) 677# define CALC_STAT(name, val) unsigned name = (val)
678# define SANITIZE(name) if (name > 99) name = 99
636# define SHOW_STAT(name) name 679# define SHOW_STAT(name) name
637# define FMT "%4u%%" 680# define FMT "%4u%%"
638#endif 681#endif
639 682
640 /* what info of the processes is shown */ 683 strcpy(G.line_buf, " PID PPID USER STAT RSS %RSS"
641 printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width,
642 " PID PPID USER STAT VSZ %VSZ"
643 IF_FEATURE_TOP_SMP_PROCESS(" CPU") 684 IF_FEATURE_TOP_SMP_PROCESS(" CPU")
644 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU") 685 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU")
645 " COMMAND"); 686 " COMMAND");
646 lines_rem--; 687 print_line_bold();
647 688
648 /* 689 /* %RSS = s->memsize / MemTotal * 100%
649 * %VSZ = s->vsz/MemTotal 690 * Calculate this with multiply and shift. Example:
691 * shift = 12
692 * scale = 100 * 0x1000 / total_memory
693 * percent_mem = (size_mem * scale) >> shift
694 * ~= (size_mem >> shift) * scale
695 * ~= (size_mem >> shift) * 100 * (1 << shift) / total_memory
696 * ~= size_mem * 100 / total_memory
650 */ 697 */
651 pmem_shift = BITS_PER_INT-11; 698 pmem_shift = BITS_PER_INT-11;
652 pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory; 699 pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory;
653 /* s->vsz is in kb. we want (s->vsz * pmem_scale) to never overflow */ 700 /* s->memsize is in kb. we want (s->memsize * pmem_scale) to never overflow */
654 while (pmem_scale >= 512) { 701 while (pmem_scale >= 512) {
655 pmem_scale /= 4; 702 pmem_scale /= 4;
656 pmem_shift -= 2; 703 pmem_shift -= 2;
@@ -689,25 +736,29 @@ typedef struct { unsigned quot, rem; } bb_div_t;
689 pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2); 736 pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
690 /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */ 737 /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
691#endif 738#endif
739 if (G.lines_remaining > ntop - G_scroll_ofs)
740 G.lines_remaining = ntop - G_scroll_ofs;
692 741
693 /* Ok, all preliminary data is ready, go through the list */ 742 /* Ok, all preliminary data is ready, go through the list */
694 scr_width += 2; /* account for leading '\n' and trailing NUL */
695 if (lines_rem > ntop - G_scroll_ofs)
696 lines_rem = ntop - G_scroll_ofs;
697 s = top + G_scroll_ofs; 743 s = top + G_scroll_ofs;
698 while (--lines_rem >= 0) { 744 while (G.lines_remaining > 0) {
699 int n; 745 int n;
700 char *ppu; 746 char *ppu;
701 char ppubuf[sizeof(int)*3 * 2 + 12]; 747 char ppubuf[sizeof(int)*3 * 2 + 12];
702 char vsz_str_buf[8]; 748 char memsize_str_buf[8];
703 unsigned col; 749 unsigned col;
704 750
705 CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift); 751 CALC_STAT(pmem, (s->memsize*pmem_scale + pmem_half) >> pmem_shift);
706#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 752#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
707 CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift); 753 CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
708#endif 754#endif
755 /* VSZ can be much larger than total memory
756 * (seen values close to 2Tbyte), don't try to display
757 * "uses 12345.6% of MemTotal" (won't fit the column)
758 */
759 SANITIZE(pmem);
709 760
710 smart_ulltoa5(s->vsz, vsz_str_buf, " mgtpezy"); 761 smart_ulltoa5(s->memsize, memsize_str_buf, " mgtpezy");
711 /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */ 762 /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */
712 n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid)); 763 n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid));
713 ppu = ppubuf; 764 ppu = ppubuf;
@@ -736,27 +787,23 @@ typedef struct { unsigned quot, rem; } bb_div_t;
736 ppu[6+6+8] = '\0'; /* truncate USER */ 787 ppu[6+6+8] = '\0'; /* truncate USER */
737 } 788 }
738 shortened: 789 shortened:
739 col = snprintf(line_buf, scr_width, 790 col = sprintf(G.line_buf,
740 "\n" "%s %s %.5s" FMT 791 "%s %s %.5s" FMT
741 IF_FEATURE_TOP_SMP_PROCESS(" %3d") 792 IF_FEATURE_TOP_SMP_PROCESS(" %3d")
742 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT) 793 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT)
743 " ", 794 " ",
744 ppu, 795 ppu,
745 s->state, vsz_str_buf, 796 s->state, memsize_str_buf,
746 SHOW_STAT(pmem) 797 SHOW_STAT(pmem)
747 IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu) 798 IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu)
748 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu)) 799 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu))
749 ); 800 );
750 if ((int)(scr_width - col) > 1) 801 cmdline_to_line_buf_and_print(col, s->pid, s->comm);
751 read_cmdline(line_buf + col, scr_width - col, s->pid, s->comm);
752 fputs_stdout(line_buf);
753 /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu, 802 /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
754 cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */ 803 cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */
755 s++; 804 s++;
756 } 805 }
757 /* printf(" %d", hist_iterations); */ 806 /* printf(" %d", hist_iterations); */
758 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
759 fflush_all();
760} 807}
761#undef UPSCALE 808#undef UPSCALE
762#undef SHOW_STAT 809#undef SHOW_STAT
@@ -828,36 +875,34 @@ static int topmem_sort(char *a, char *b)
828} 875}
829 876
830/* display header info (meminfo / loadavg) */ 877/* display header info (meminfo / loadavg) */
831static void display_topmem_header(int scr_width, int *lines_rem_p) 878static void display_topmem_header(void)
832{ 879{
833 unsigned long meminfo[MI_MAX]; 880 unsigned long meminfo[MI_MAX];
834 881
835 parse_meminfo(meminfo); 882 parse_meminfo(meminfo);
836 883
837 snprintf(line_buf, LINE_BUF_SIZE, 884 sprintf(G.line_buf,
838 "Mem total:%lu anon:%lu map:%lu free:%lu", 885 "Mem total:%lu anon:%lu map:%lu free:%lu",
839 meminfo[MI_MEMTOTAL], 886 meminfo[MI_MEMTOTAL],
840 meminfo[MI_ANONPAGES], 887 meminfo[MI_ANONPAGES],
841 meminfo[MI_MAPPED], 888 meminfo[MI_MAPPED],
842 meminfo[MI_MEMFREE]); 889 meminfo[MI_MEMFREE]);
843 printf(OPT_BATCH_MODE ? "%.*s\n" : ESC"[H" ESC"[J" "%.*s\n", scr_width, line_buf); 890 print_line_buf();
844 891
845 snprintf(line_buf, LINE_BUF_SIZE, 892 sprintf(G.line_buf,
846 " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu", 893 " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu",
847 meminfo[MI_SLAB], 894 meminfo[MI_SLAB],
848 meminfo[MI_BUFFERS], 895 meminfo[MI_BUFFERS],
849 meminfo[MI_CACHED], 896 meminfo[MI_CACHED],
850 meminfo[MI_DIRTY], 897 meminfo[MI_DIRTY],
851 meminfo[MI_WRITEBACK]); 898 meminfo[MI_WRITEBACK]);
852 printf("%.*s\n", scr_width, line_buf); 899 print_line_buf();
853 900
854 snprintf(line_buf, LINE_BUF_SIZE, 901 sprintf(G.line_buf,
855 "Swap total:%lu free:%lu", // TODO: % used? 902 "Swap total:%lu free:%lu", // TODO: % used?
856 meminfo[MI_SWAPTOTAL], 903 meminfo[MI_SWAPTOTAL],
857 meminfo[MI_SWAPFREE]); 904 meminfo[MI_SWAPFREE]);
858 printf("%.*s\n", scr_width, line_buf); 905 print_line_buf();
859
860 (*lines_rem_p) -= 3;
861} 906}
862 907
863/* see http://en.wikipedia.org/wiki/Tera */ 908/* see http://en.wikipedia.org/wiki/Tera */
@@ -870,75 +915,57 @@ static void ulltoa4_and_space(unsigned long long ul, char buf[5])
870 smart_ulltoa4(ul, buf, " mgtpezy")[0] = ' '; 915 smart_ulltoa4(ul, buf, " mgtpezy")[0] = ' ';
871} 916}
872 917
873static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width) 918static NOINLINE void display_topmem_process_list(void)
874{ 919{
875#define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK"
876#define MIN_WIDTH sizeof(HDR_STR)
877 const topmem_status_t *s = topmem + G_scroll_ofs; 920 const topmem_status_t *s = topmem + G_scroll_ofs;
878 char *cp, ch; 921 char *cp, ch;
879 922
880 display_topmem_header(scr_width, &lines_rem); 923 display_topmem_header();
881 924
882 strcpy(line_buf, HDR_STR " COMMAND"); 925 strcpy(G.line_buf, " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK COMMAND");
883 /* Mark the ^FIELD^ we sort by */ 926 /* Mark the ^FIELD^ we sort by */
884 cp = &line_buf[5 + sort_field * 6]; 927 cp = &G.line_buf[5 + sort_field * 6];
885 ch = "^_"[inverted]; 928 ch = "^_"[inverted];
886 cp[6] = ch; 929 cp[6] = ch;
887 do *cp++ = ch; while (*cp == ' '); 930 do *cp++ = ch; while (*cp == ' ');
931 print_line_bold();
888 932
889 printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width, line_buf); 933 if (G.lines_remaining > ntop - G_scroll_ofs)
890 lines_rem--; 934 G.lines_remaining = ntop - G_scroll_ofs;
891 935 while (G.lines_remaining > 0) {
892 if (lines_rem > ntop - G_scroll_ofs)
893 lines_rem = ntop - G_scroll_ofs;
894 while (--lines_rem >= 0) {
895 /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */ 936 /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */
896 int n = sprintf(line_buf, "%5u ", s->pid); 937 int n = sprintf(G.line_buf, "%5u ", s->pid);
897 if (n > 7) { 938 if (n > 7) {
898 /* PID is 7 chars long (up to 4194304) */ 939 /* PID is 7 chars long (up to 4194304) */
899 ulltoa4_and_space(s->vsz , &line_buf[8]); 940 ulltoa4_and_space(s->vsz , &G.line_buf[8]);
900 ulltoa4_and_space(s->vszrw, &line_buf[8+5]); 941 ulltoa4_and_space(s->vszrw, &G.line_buf[8+5]);
901 /* the next field (RSS) starts at 8+10 = 3*6 */ 942 /* the next field (RSS) starts at 8+10 = 3*6 */
902 } else { 943 } else {
903 if (n == 7) /* PID is 6 chars long */ 944 if (n == 7) /* PID is 6 chars long */
904 ulltoa4_and_space(s->vsz, &line_buf[7]); 945 ulltoa4_and_space(s->vsz, &G.line_buf[7]);
905 /* the next field (VSZRW) starts at 7+5 = 2*6 */ 946 /* the next field (VSZRW) starts at 7+5 = 2*6 */
906 else /* PID is 5 chars or less */ 947 else /* PID is 5 chars or less */
907 ulltoa5_and_space(s->vsz, &line_buf[6]); 948 ulltoa5_and_space(s->vsz, &G.line_buf[6]);
908 ulltoa5_and_space(s->vszrw, &line_buf[2*6]); 949 ulltoa5_and_space(s->vszrw, &G.line_buf[2*6]);
909 }
910 ulltoa5_and_space(s->rss , &line_buf[3*6]);
911 ulltoa5_and_space(s->rss_sh , &line_buf[4*6]);
912 ulltoa5_and_space(s->dirty , &line_buf[5*6]);
913 ulltoa5_and_space(s->dirty_sh, &line_buf[6*6]);
914 ulltoa5_and_space(s->stack , &line_buf[7*6]);
915 line_buf[8*6] = '\0';
916 if (scr_width > (int)MIN_WIDTH) {
917 read_cmdline(&line_buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm);
918 } 950 }
919 printf("\n""%.*s", scr_width, line_buf); 951 ulltoa5_and_space(s->rss , &G.line_buf[3*6]);
952 ulltoa5_and_space(s->rss_sh , &G.line_buf[4*6]);
953 ulltoa5_and_space(s->dirty , &G.line_buf[5*6]);
954 ulltoa5_and_space(s->dirty_sh, &G.line_buf[6*6]);
955 ulltoa5_and_space(s->stack , &G.line_buf[7*6]);
956 G.line_buf[8*6] = '\0';
957 cmdline_to_line_buf_and_print(8*6, s->pid, s->comm);
920 s++; 958 s++;
921 } 959 }
922 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
923 fflush_all();
924#undef HDR_STR
925#undef MIN_WIDTH
926} 960}
927 961
928#else 962#endif /* end TOPMEM support */
929void display_topmem_process_list(int lines_rem, int scr_width);
930int topmem_sort(char *a, char *b);
931#endif /* TOPMEM */
932
933/*
934 * end TOPMEM support
935 */
936 963
937enum { 964enum {
938 TOP_MASK = 0 965 TOP_MASK = 0
939 | PSSCAN_PID 966 | PSSCAN_PID
940 | PSSCAN_PPID 967 | PSSCAN_PPID
941 | PSSCAN_VSZ 968 | PSSCAN_RSS
942 | PSSCAN_STIME 969 | PSSCAN_STIME
943 | PSSCAN_UTIME 970 | PSSCAN_UTIME
944 | PSSCAN_STATE 971 | PSSCAN_STATE
@@ -950,7 +977,7 @@ enum {
950 | PSSCAN_SMAPS 977 | PSSCAN_SMAPS
951 | PSSCAN_COMM, 978 | PSSCAN_COMM,
952 EXIT_MASK = 0, 979 EXIT_MASK = 0,
953 NO_RESCAN_MASK = (unsigned)-1, 980 ONLY_REDRAW = (unsigned)-1,
954}; 981};
955 982
956#if ENABLE_FEATURE_TOP_INTERACTIVE 983#if ENABLE_FEATURE_TOP_INTERACTIVE
@@ -963,15 +990,22 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
963 } 990 }
964 991
965 while (1) { 992 while (1) {
966 int32_t c; 993 int32_t c, cc;
967 994
968 c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000); 995 c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000);
969 if (c == -1 && errno != EAGAIN) { 996 if (c == -1) {
970 /* error/EOF */ 997 if (errno != EAGAIN)
971 option_mask32 |= OPT_EOF; 998 /* error/EOF */
999 option_mask32 |= OPT_EOF;
1000 /* else: timeout - rescan and refresh */
972 break; 1001 break;
973 } 1002 }
974 interval = 0; 1003 interval = 0;
1004 /* "continue" statements below return to do one additional
1005 * quick attempt to read a key. This prevents
1006 * long sequence of e.g. "nnnnnnnnnnnnnnnnnnnnnnnnnn"
1007 * to cause lots of rescans.
1008 */
975 1009
976 if (c == initial_settings.c_cc[VINTR]) 1010 if (c == initial_settings.c_cc[VINTR])
977 return EXIT_MASK; 1011 return EXIT_MASK;
@@ -1005,9 +1039,10 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1005 G_scroll_ofs = ntop - 1; 1039 G_scroll_ofs = ntop - 1;
1006 if (G_scroll_ofs < 0) 1040 if (G_scroll_ofs < 0)
1007 G_scroll_ofs = 0; 1041 G_scroll_ofs = 0;
1008 return NO_RESCAN_MASK; 1042 return ONLY_REDRAW;
1009 } 1043 }
1010 1044
1045 cc = c;
1011 c |= 0x20; /* lowercase */ 1046 c |= 0x20; /* lowercase */
1012 if (c == 'q') 1047 if (c == 'q')
1013 return EXIT_MASK; 1048 return EXIT_MASK;
@@ -1055,9 +1090,17 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1055 continue; 1090 continue;
1056 } 1091 }
1057# if ENABLE_FEATURE_TOPMEM 1092# if ENABLE_FEATURE_TOPMEM
1093 if (cc == 'S') {
1094 if (--sort_field < 0)
1095 sort_field = NUM_SORT_FIELD - 1;
1096 if (--sort_field < 0)
1097 sort_field = NUM_SORT_FIELD - 1;
1098 }
1058 if (c == 's') { 1099 if (c == 's') {
1059 scan_mask = TOPMEM_MASK;
1060 sort_field = (sort_field + 1) % NUM_SORT_FIELD; 1100 sort_field = (sort_field + 1) % NUM_SORT_FIELD;
1101 if (scan_mask == TOPMEM_MASK)
1102 return ONLY_REDRAW;
1103 scan_mask = TOPMEM_MASK;
1061 free(prev_hist); 1104 free(prev_hist);
1062 prev_hist = NULL; 1105 prev_hist = NULL;
1063 prev_hist_count = 0; 1106 prev_hist_count = 0;
@@ -1066,7 +1109,7 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1066# endif 1109# endif
1067 if (c == 'r') { 1110 if (c == 'r') {
1068 inverted ^= 1; 1111 inverted ^= 1;
1069 continue; 1112 return ONLY_REDRAW;
1070 } 1113 }
1071# if ENABLE_FEATURE_TOP_SMP_CPU 1114# if ENABLE_FEATURE_TOP_SMP_CPU
1072 /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ 1115 /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
@@ -1088,8 +1131,8 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1088 } 1131 }
1089# endif 1132# endif
1090# endif 1133# endif
1091 break; /* unknown key -> force refresh */ 1134 /* Unknown key. Eat remaining buffered input (if any) */
1092 } 1135 } /* while (1) */
1093 1136
1094 return scan_mask; 1137 return scan_mask;
1095} 1138}
@@ -1155,7 +1198,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1155{ 1198{
1156 duration_t interval; 1199 duration_t interval;
1157 int iterations; 1200 int iterations;
1158 unsigned col; 1201 unsigned opt;
1159 char *str_interval, *str_iterations; 1202 char *str_interval, *str_iterations;
1160 unsigned scan_mask = TOP_MASK; 1203 unsigned scan_mask = TOP_MASK;
1161 1204
@@ -1172,13 +1215,13 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1172 1215
1173 /* all args are options; -n NUM */ 1216 /* all args are options; -n NUM */
1174 make_all_argv_opts(argv); /* options can be specified w/o dash */ 1217 make_all_argv_opts(argv); /* options can be specified w/o dash */
1175 col = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations); 1218 opt = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations);
1176 /* NB: -m and -H are accepted even if not configured */ 1219 /* NB: -m and -H are accepted even if not configured */
1177#if ENABLE_FEATURE_TOPMEM 1220#if ENABLE_FEATURE_TOPMEM
1178 if (col & OPT_m) /* -m (busybox specific) */ 1221 if (opt & OPT_m) /* -m (busybox specific) */
1179 scan_mask = TOPMEM_MASK; 1222 scan_mask = TOPMEM_MASK;
1180#endif 1223#endif
1181 if (col & OPT_d) { 1224 if (opt & OPT_d) {
1182 /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */ 1225 /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */
1183 if (str_interval[0] == '-') 1226 if (str_interval[0] == '-')
1184 str_interval++; 1227 str_interval++;
@@ -1187,18 +1230,17 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1187 if (interval > INT_MAX / 1000) 1230 if (interval > INT_MAX / 1000)
1188 interval = INT_MAX / 1000; 1231 interval = INT_MAX / 1000;
1189 } 1232 }
1190 if (col & OPT_n) { 1233 if (opt & OPT_n) {
1191 if (str_iterations[0] == '-') 1234 if (str_iterations[0] == '-')
1192 str_iterations++; 1235 str_iterations++;
1193 iterations = xatou(str_iterations); 1236 iterations = xatou(str_iterations);
1194 } 1237 }
1195#if ENABLE_FEATURE_SHOW_THREADS 1238#if ENABLE_FEATURE_SHOW_THREADS
1196 if (col & OPT_H) { 1239 if (opt & OPT_H) {
1197 scan_mask |= PSSCAN_TASKS; 1240 scan_mask |= PSSCAN_TASKS;
1198 } 1241 }
1199#endif 1242#endif
1200 1243
1201 /* change to /proc */
1202 xchdir("/proc"); 1244 xchdir("/proc");
1203 1245
1204#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 1246#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
@@ -1226,23 +1268,22 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1226#endif 1268#endif
1227 1269
1228 while (scan_mask != EXIT_MASK) { 1270 while (scan_mask != EXIT_MASK) {
1229 IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask;) 1271 IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask = scan_mask;)
1230 procps_status_t *p = NULL; 1272 procps_status_t *p = NULL;
1231 1273
1232 if (OPT_BATCH_MODE) { 1274 G.lines = INT_MAX;
1233 G.lines = INT_MAX; 1275 G.scr_width = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */
1234 col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */ 1276 if (!OPT_BATCH_MODE) {
1235 } else {
1236 G.lines = 24; /* default */ 1277 G.lines = 24; /* default */
1237 col = 79; 1278 G.scr_width = 80;
1238 /* We output to stdout, we need size of stdout (not stdin)! */ 1279 /* We output to stdout, we need size of stdout (not stdin)! */
1239 get_terminal_width_height(STDOUT_FILENO, &col, &G.lines); 1280 get_terminal_width_height(STDOUT_FILENO, &G.scr_width, &G.lines);
1240 if (G.lines < 5 || col < 10) { 1281 if (G.lines < 5 || G.scr_width < 10) {
1241 sleep_for_duration(interval); 1282 sleep_for_duration(interval);
1242 continue; 1283 continue;
1243 } 1284 }
1244 if (col > LINE_BUF_SIZE - 2) 1285 if (G.scr_width > LINE_BUF_SIZE - 2)
1245 col = LINE_BUF_SIZE - 2; 1286 G.scr_width = LINE_BUF_SIZE - 2;
1246 } 1287 }
1247 1288
1248 /* read process IDs & status for all the processes */ 1289 /* read process IDs & status for all the processes */
@@ -1255,7 +1296,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1255 top = xrealloc_vector(top, 6, ntop++); 1296 top = xrealloc_vector(top, 6, ntop++);
1256 top[n].pid = p->pid; 1297 top[n].pid = p->pid;
1257 top[n].ppid = p->ppid; 1298 top[n].ppid = p->ppid;
1258 top[n].vsz = p->vsz; 1299 top[n].memsize = p->rss;
1259#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 1300#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1260 top[n].ticks = p->stime + p->utime; 1301 top[n].ticks = p->stime + p->utime;
1261#endif 1302#endif
@@ -1268,20 +1309,20 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1268 } 1309 }
1269#if ENABLE_FEATURE_TOPMEM 1310#if ENABLE_FEATURE_TOPMEM
1270 else { /* TOPMEM */ 1311 else { /* TOPMEM */
1271 if (!(p->smaps.mapped_ro | p->smaps.mapped_rw)) 1312 if (!(p->mapped_ro | p->mapped_rw))
1272 continue; /* kernel threads are ignored */ 1313 continue; /* kernel threads are ignored */
1273 n = ntop; 1314 n = ntop;
1274 /* No bug here - top and topmem are the same */ 1315 /* No bug here - top and topmem are the same */
1275 top = xrealloc_vector(topmem, 6, ntop++); 1316 top = xrealloc_vector(topmem, 6, ntop++);
1276 strcpy(topmem[n].comm, p->comm); 1317 strcpy(topmem[n].comm, p->comm);
1277 topmem[n].pid = p->pid; 1318 topmem[n].pid = p->pid;
1278 topmem[n].vsz = p->smaps.mapped_rw + p->smaps.mapped_ro; 1319 topmem[n].vsz = p->mapped_rw + p->mapped_ro;
1279 topmem[n].vszrw = p->smaps.mapped_rw; 1320 topmem[n].vszrw = p->mapped_rw;
1280 topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty; 1321 topmem[n].rss_sh = p->shared_clean + p->shared_dirty;
1281 topmem[n].rss = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh; 1322 topmem[n].rss = p->private_clean + p->private_dirty + topmem[n].rss_sh;
1282 topmem[n].dirty = p->smaps.private_dirty + p->smaps.shared_dirty; 1323 topmem[n].dirty = p->private_dirty + p->shared_dirty;
1283 topmem[n].dirty_sh = p->smaps.shared_dirty; 1324 topmem[n].dirty_sh = p->shared_dirty;
1284 topmem[n].stack = p->smaps.stack; 1325 topmem[n].stack = p->stack;
1285 } 1326 }
1286#endif 1327#endif
1287 } /* end of "while we read /proc" */ 1328 } /* end of "while we read /proc" */
@@ -1310,30 +1351,40 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1310 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); 1351 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
1311 } 1352 }
1312#endif 1353#endif
1313 IF_FEATURE_TOP_INTERACTIVE(display:) 1354 IF_FEATURE_TOP_INTERACTIVE(redraw:)
1355 G.lines_remaining = G.lines;
1314 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) { 1356 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1315 display_process_list(G.lines, col); 1357 display_process_list();
1316 } 1358 }
1317#if ENABLE_FEATURE_TOPMEM 1359#if ENABLE_FEATURE_TOPMEM
1318 else { /* TOPMEM */ 1360 else { /* TOPMEM */
1319 display_topmem_process_list(G.lines, col); 1361 display_topmem_process_list();
1320 } 1362 }
1321#endif 1363#endif
1364 print_end();
1365 fflush_all();
1322 if (iterations >= 0 && !--iterations) 1366 if (iterations >= 0 && !--iterations)
1323 break; 1367 break;
1324#if !ENABLE_FEATURE_TOP_INTERACTIVE 1368#if !ENABLE_FEATURE_TOP_INTERACTIVE
1325 clearmems(); 1369 clearmems();
1326 sleep_for_duration(interval); 1370 sleep_for_duration(interval);
1327#else 1371#else
1328 new_mask = handle_input(scan_mask, interval); 1372 new_mask = handle_input(scan_mask,
1329 if (new_mask == NO_RESCAN_MASK) 1373 /* After "redraw with no rescan", have one
1330 goto display; 1374 * key timeout shorter that normal
1375 * (IOW: rescan sooner):
1376 */
1377 (new_mask == ONLY_REDRAW ? 1 : interval)
1378 );
1379 if (new_mask == ONLY_REDRAW)
1380 goto redraw;
1331 scan_mask = new_mask; 1381 scan_mask = new_mask;
1332 clearmems(); 1382 clearmems();
1333#endif 1383#endif
1334 } /* end of "while (not Q)" */ 1384 } /* end of "while (not Q)" */
1335 1385
1336 bb_putchar('\n'); 1386 if (!OPT_BATCH_MODE)
1387 bb_putchar('\n');
1337#if ENABLE_FEATURE_TOP_INTERACTIVE 1388#if ENABLE_FEATURE_TOP_INTERACTIVE
1338 reset_term(); 1389 reset_term();
1339#endif 1390#endif
diff --git a/shell/ash.c b/shell/ash.c
index 0038aa1e9..605215e41 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -461,7 +461,7 @@ static void forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes)
461 461
462/* ============ Shell options */ 462/* ============ Shell options */
463 463
464/* If you add/change options hare, update --help text too */ 464/* If you add/change options here, update --help text too */
465static const char *const optletters_optnames[] ALIGN_PTR = { 465static const char *const optletters_optnames[] ALIGN_PTR = {
466 "e" "errexit", 466 "e" "errexit",
467 "f" "noglob", 467 "f" "noglob",
@@ -1822,7 +1822,6 @@ struct stackmark {
1822 size_t stacknleft; 1822 size_t stacknleft;
1823}; 1823};
1824 1824
1825
1826struct globals_memstack { 1825struct globals_memstack {
1827 struct stack_block *g_stackp; // = &stackbase; 1826 struct stack_block *g_stackp; // = &stackbase;
1828 char *g_stacknxt; // = stackbase.space; 1827 char *g_stacknxt; // = stackbase.space;
@@ -1845,7 +1844,6 @@ extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack;
1845 sstrend = stackbase.space + MINSIZE; \ 1844 sstrend = stackbase.space + MINSIZE; \
1846} while (0) 1845} while (0)
1847 1846
1848
1849#define stackblock() ((void *)g_stacknxt) 1847#define stackblock() ((void *)g_stacknxt)
1850#define stackblocksize() g_stacknleft 1848#define stackblocksize() g_stacknleft
1851 1849
@@ -2362,7 +2360,6 @@ struct localvar {
2362# define VIMPORT 0x400 /* variable was imported from environment */ 2360# define VIMPORT 0x400 /* variable was imported from environment */
2363#endif 2361#endif
2364 2362
2365
2366/* Need to be before varinit_data[] */ 2363/* Need to be before varinit_data[] */
2367#if ENABLE_LOCALE_SUPPORT 2364#if ENABLE_LOCALE_SUPPORT
2368static void FAST_FUNC 2365static void FAST_FUNC
@@ -3582,7 +3579,6 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3582 3579
3583/* ============ ... */ 3580/* ============ ... */
3584 3581
3585
3586#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024) 3582#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
3587 3583
3588/* Syntax classes */ 3584/* Syntax classes */
@@ -3989,13 +3985,11 @@ struct alias {
3989 int flag; 3985 int flag;
3990}; 3986};
3991 3987
3992
3993static struct alias **atab; // [ATABSIZE]; 3988static struct alias **atab; // [ATABSIZE];
3994#define INIT_G_alias() do { \ 3989#define INIT_G_alias() do { \
3995 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \ 3990 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3996} while (0) 3991} while (0)
3997 3992
3998
3999static struct alias ** 3993static struct alias **
4000__lookupalias(const char *name) 3994__lookupalias(const char *name)
4001{ 3995{
@@ -4177,7 +4171,6 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
4177 4171
4178#endif /* ASH_ALIAS */ 4172#endif /* ASH_ALIAS */
4179 4173
4180
4181/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ 4174/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
4182#define FORK_FG 0 4175#define FORK_FG 0
4183#define FORK_BG 1 4176#define FORK_BG 1
@@ -6194,7 +6187,6 @@ stoppedjobs(void)
6194} 6187}
6195#endif 6188#endif
6196 6189
6197
6198/* 6190/*
6199 * Code for dealing with input/output redirection. 6191 * Code for dealing with input/output redirection.
6200 */ 6192 */
@@ -9904,7 +9896,6 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
9904} 9896}
9905#endif 9897#endif
9906 9898
9907
9908/*static int funcblocksize; // size of structures in function */ 9899/*static int funcblocksize; // size of structures in function */
9909/*static int funcstringsize; // size of strings in node */ 9900/*static int funcstringsize; // size of strings in node */
9910static void *funcblock; /* block to allocate function from */ 9901static void *funcblock; /* block to allocate function from */
@@ -11920,7 +11911,6 @@ goodname(const char *p)
11920 return endofname(p)[0] == '\0'; 11911 return endofname(p)[0] == '\0';
11921} 11912}
11922 11913
11923
11924/* 11914/*
11925 * Search for a command. This is called before we fork so that the 11915 * Search for a command. This is called before we fork so that the
11926 * location of the command will be available in the parent as well as 11916 * location of the command will be available in the parent as well as
@@ -14773,7 +14763,6 @@ parseheredoc(void)
14773 } 14763 }
14774} 14764}
14775 14765
14776
14777static const char * 14766static const char *
14778expandstr(const char *ps, int syntax_type) 14767expandstr(const char *ps, int syntax_type)
14779{ 14768{
@@ -15403,7 +15392,6 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
15403 entry->u = cmdp->param; 15392 entry->u = cmdp->param;
15404} 15393}
15405 15394
15406
15407/* 15395/*
15408 * The trap builtin. 15396 * The trap builtin.
15409 */ 15397 */
@@ -16170,7 +16158,6 @@ init(void)
16170 } 16158 }
16171} 16159}
16172 16160
16173
16174//usage:#define ash_trivial_usage 16161//usage:#define ash_trivial_usage
16175//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]" 16162//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]"
16176//////// comes from ^^^^^^^^^^optletters 16163//////// comes from ^^^^^^^^^^optletters
diff --git a/shell/hush.c b/shell/hush.c
index d1f687f9d..09ab6ebc0 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -392,6 +392,8 @@
392 392
393/* Build knobs */ 393/* Build knobs */
394#define LEAK_HUNTING 0 394#define LEAK_HUNTING 0
395#define LEAK_PRINTF(...) fdprintf(__VA_ARGS__)
396//#define LEAK_PRINTF(...) do { if (ptr_to_globals && G.root_pid == getpid()) fdprintf(__VA_ARGS__); } while (0)
395#define BUILD_AS_NOMMU 0 397#define BUILD_AS_NOMMU 0
396/* Enable/disable sanity checks. Ok to enable in production, 398/* Enable/disable sanity checks. Ok to enable in production,
397 * only adds a bit of bloat. Set to >1 to get non-production level verbosity. 399 * only adds a bit of bloat. Set to >1 to get non-production level verbosity.
@@ -930,6 +932,12 @@ struct globals {
930# define G_flag_return_in_progress 0 932# define G_flag_return_in_progress 0
931#endif 933#endif
932 smallint exiting; /* used to prevent EXIT trap recursion */ 934 smallint exiting; /* used to prevent EXIT trap recursion */
935#if !BB_MMU
936 smallint reexeced_on_NOMMU;
937# define G_reexeced_on_NOMMU (G.reexeced_on_NOMMU)
938#else
939# define G_reexeced_on_NOMMU 0
940#endif
933 /* These support $? */ 941 /* These support $? */
934 smalluint last_exitcode; 942 smalluint last_exitcode;
935 smalluint expand_exitcode; 943 smalluint expand_exitcode;
@@ -1352,30 +1360,67 @@ static void debug_print_strings(const char *prefix, char **vv)
1352static void *xxmalloc(int lineno, size_t size) 1360static void *xxmalloc(int lineno, size_t size)
1353{ 1361{
1354 void *ptr = xmalloc((size + 0xff) & ~0xff); 1362 void *ptr = xmalloc((size + 0xff) & ~0xff);
1355 fdprintf(2, "line %d: malloc %p\n", lineno, ptr); 1363 LEAK_PRINTF(2, "line %d: malloc %p\n", lineno, ptr);
1364 return ptr;
1365}
1366static void *xxzalloc(int lineno, size_t size)
1367{
1368 void *ptr = xzalloc((size + 0xff) & ~0xff);
1369 LEAK_PRINTF(2, "line %d: zalloc %p\n", lineno, ptr);
1356 return ptr; 1370 return ptr;
1357} 1371}
1358static void *xxrealloc(int lineno, void *ptr, size_t size) 1372static void *xxrealloc(int lineno, void *ptr, size_t size)
1359{ 1373{
1374 char *p = ptr;
1360 ptr = xrealloc(ptr, (size + 0xff) & ~0xff); 1375 ptr = xrealloc(ptr, (size + 0xff) & ~0xff);
1361 fdprintf(2, "line %d: realloc %p\n", lineno, ptr); 1376 if (p != ptr)
1377 LEAK_PRINTF(2, "line %d: realloc %p\n", lineno, ptr);
1378 return ptr;
1379}
1380static void *xxrealloc_getcwd_or_warn(int lineno, char *ptr)
1381{
1382 char *p = ptr;
1383 ptr = xrealloc_getcwd_or_warn(ptr);
1384 if (p != ptr)
1385 LEAK_PRINTF(2, "line %d: xrealloc_getcwd_or_warn %p\n", lineno, ptr);
1362 return ptr; 1386 return ptr;
1363} 1387}
1364static char *xxstrdup(int lineno, const char *str) 1388static char *xxstrdup(int lineno, const char *str)
1365{ 1389{
1366 char *ptr = xstrdup(str); 1390 char *ptr = xstrdup(str);
1367 fdprintf(2, "line %d: strdup %p\n", lineno, ptr); 1391 LEAK_PRINTF(2, "line %d: strdup %p\n", lineno, ptr);
1392 return ptr;
1393}
1394static char *xxstrndup(int lineno, const char *str, size_t n)
1395{
1396 char *ptr = xstrndup(str, n);
1397 LEAK_PRINTF(2, "line %d: strndup %p\n", lineno, ptr);
1398 return ptr;
1399}
1400static char *xxasprintf(int lineno, const char *f, ...)
1401{
1402 char *ptr;
1403 va_list args;
1404 va_start(args, f);
1405 if (vasprintf(&ptr, f, args) < 0)
1406 bb_die_memory_exhausted();
1407 va_end(args);
1408 LEAK_PRINTF(2, "line %d: xasprintf %p\n", lineno, ptr);
1368 return ptr; 1409 return ptr;
1369} 1410}
1370static void xxfree(void *ptr) 1411static void xxfree(void *ptr)
1371{ 1412{
1372 fdprintf(2, "free %p\n", ptr); 1413 LEAK_PRINTF(2, "free %p\n", ptr);
1373 free(ptr); 1414 free(ptr);
1374} 1415}
1375# define xmalloc(s) xxmalloc(__LINE__, s) 1416# define xmalloc(s) xxmalloc(__LINE__, s)
1376# define xrealloc(p, s) xxrealloc(__LINE__, p, s) 1417# define xzalloc(s) xxzalloc(__LINE__, s)
1377# define xstrdup(s) xxstrdup(__LINE__, s) 1418# define xrealloc(p, s) xxrealloc(__LINE__, p, s)
1378# define free(p) xxfree(p) 1419# define xrealloc_getcwd_or_warn(p) xxrealloc_getcwd_or_warn(__LINE__, p)
1420# define xstrdup(s) xxstrdup(__LINE__, s)
1421# define xstrndup(s, n) xxstrndup(__LINE__, s, n)
1422# define xasprintf(f, ...) xxasprintf(__LINE__, f, __VA_ARGS__)
1423# define free(p) xxfree(p)
1379#endif 1424#endif
1380 1425
1381/* 1426/*
@@ -1929,7 +1974,7 @@ static void restore_G_args(save_arg_t *sv, char **argv)
1929 * "trap - SIGxxx": 1974 * "trap - SIGxxx":
1930 * if sig is in special_sig_mask, set handler back to: 1975 * if sig is in special_sig_mask, set handler back to:
1931 * record_pending_signo, or to IGN if it's a tty stop signal 1976 * record_pending_signo, or to IGN if it's a tty stop signal
1932 * if sig is in fatal_sig_mask, set handler back to sigexit. 1977 * if sig is in fatal_sig_mask, set handler back to restore_ttypgrp_and_killsig_or__exit.
1933 * else: set handler back to SIG_DFL 1978 * else: set handler back to SIG_DFL
1934 * "trap 'cmd' SIGxxx": 1979 * "trap 'cmd' SIGxxx":
1935 * set handler to record_pending_signo. 1980 * set handler to record_pending_signo.
@@ -2002,19 +2047,6 @@ static sighandler_t install_sighandler(int sig, sighandler_t handler)
2002 return old_sa.sa_handler; 2047 return old_sa.sa_handler;
2003} 2048}
2004 2049
2005static void hush_exit(int exitcode) NORETURN;
2006
2007static void restore_ttypgrp_and__exit(void) NORETURN;
2008static void restore_ttypgrp_and__exit(void)
2009{
2010 /* xfunc has failed! die die die */
2011 /* no EXIT traps, this is an escape hatch! */
2012 G.exiting = 1;
2013 hush_exit(xfunc_error_retval);
2014}
2015
2016#if ENABLE_HUSH_JOB
2017
2018/* Needed only on some libc: 2050/* Needed only on some libc:
2019 * It was observed that on exit(), fgetc'ed buffered data 2051 * It was observed that on exit(), fgetc'ed buffered data
2020 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR). 2052 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR).
@@ -2028,26 +2060,20 @@ static void restore_ttypgrp_and__exit(void)
2028 * and in `cmd` handling. 2060 * and in `cmd` handling.
2029 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): 2061 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit():
2030 */ 2062 */
2031static void fflush_and__exit(void) NORETURN; 2063static NORETURN void fflush_and__exit(void)
2032static void fflush_and__exit(void)
2033{ 2064{
2034 fflush_all(); 2065 fflush_all();
2035 _exit(xfunc_error_retval); 2066 _exit(xfunc_error_retval);
2036} 2067}
2037 2068
2038/* After [v]fork, in child: do not restore tty pgrp on xfunc death */ 2069#if ENABLE_HUSH_JOB
2039# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
2040/* After [v]fork, in parent: restore tty pgrp on xfunc death */
2041# define enable_restore_tty_pgrp_on_exit() (die_func = restore_ttypgrp_and__exit)
2042
2043/* Restores tty foreground process group, and exits. 2070/* Restores tty foreground process group, and exits.
2044 * May be called as signal handler for fatal signal 2071 * May be called as signal handler for fatal signal
2045 * (will resend signal to itself, producing correct exit state) 2072 * (will resend signal to itself, producing correct exit state)
2046 * or called directly with -EXITCODE. 2073 * or called directly with -EXITCODE.
2047 * We also call it if xfunc is exiting. 2074 * We also call it if xfunc is exiting.
2048 */ 2075 */
2049static void sigexit(int sig) NORETURN; 2076static NORETURN void restore_ttypgrp_and_killsig_or__exit(int sig)
2050static void sigexit(int sig)
2051{ 2077{
2052 /* Careful: we can end up here after [v]fork. Do not restore 2078 /* Careful: we can end up here after [v]fork. Do not restore
2053 * tty pgrp then, only top-level shell process does that */ 2079 * tty pgrp then, only top-level shell process does that */
@@ -2065,6 +2091,19 @@ static void sigexit(int sig)
2065 2091
2066 kill_myself_with_sig(sig); /* does not return */ 2092 kill_myself_with_sig(sig); /* does not return */
2067} 2093}
2094
2095static NORETURN void fflush_restore_ttypgrp_and__exit(void)
2096{
2097 /* xfunc has failed! die die die */
2098 fflush_all();
2099 restore_ttypgrp_and_killsig_or__exit(- xfunc_error_retval);
2100}
2101
2102/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
2103# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
2104/* After [v]fork, in parent: restore tty pgrp on xfunc death */
2105# define enable_restore_tty_pgrp_on_exit() (die_func = fflush_restore_ttypgrp_and__exit)
2106
2068#else 2107#else
2069 2108
2070# define disable_restore_tty_pgrp_on_exit() ((void)0) 2109# define disable_restore_tty_pgrp_on_exit() ((void)0)
@@ -2081,7 +2120,7 @@ static sighandler_t pick_sighandler(unsigned sig)
2081#if ENABLE_HUSH_JOB 2120#if ENABLE_HUSH_JOB
2082 /* is sig fatal? */ 2121 /* is sig fatal? */
2083 if (G_fatal_sig_mask & sigmask) 2122 if (G_fatal_sig_mask & sigmask)
2084 handler = sigexit; 2123 handler = restore_ttypgrp_and_killsig_or__exit;
2085 else 2124 else
2086#endif 2125#endif
2087 /* sig has special handling? */ 2126 /* sig has special handling? */
@@ -2101,11 +2140,15 @@ static sighandler_t pick_sighandler(unsigned sig)
2101 2140
2102static const char* FAST_FUNC get_local_var_value(const char *name); 2141static const char* FAST_FUNC get_local_var_value(const char *name);
2103 2142
2104/* Restores tty foreground process group, and exits. */ 2143/* Self-explanatory.
2105static void hush_exit(int exitcode) 2144 * Restores tty foreground process group too.
2145 */
2146static NORETURN void save_history_run_exit_trap_and_exit(int exitcode)
2106{ 2147{
2107#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 2148#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
2108 if (G.line_input_state) { 2149 if (G.line_input_state
2150 && getpid() == G.root_pid /* exits in subshells do not save history */
2151 ) {
2109 const char *hp; 2152 const char *hp;
2110# if ENABLE_FEATURE_SH_HISTFILESIZE 2153# if ENABLE_FEATURE_SH_HISTFILESIZE
2111// in bash: 2154// in bash:
@@ -2155,7 +2198,7 @@ static void hush_exit(int exitcode)
2155 2198
2156 fflush_all(); 2199 fflush_all();
2157#if ENABLE_HUSH_JOB 2200#if ENABLE_HUSH_JOB
2158 sigexit(- (exitcode & 0xff)); 2201 restore_ttypgrp_and_killsig_or__exit(- (exitcode & 0xff));
2159#else 2202#else
2160 _exit(exitcode); 2203 _exit(exitcode);
2161#endif 2204#endif
@@ -2240,7 +2283,7 @@ static int check_and_run_traps(void)
2240 } 2283 }
2241 } 2284 }
2242 /* this restores tty pgrp, then kills us with SIGHUP */ 2285 /* this restores tty pgrp, then kills us with SIGHUP */
2243 sigexit(SIGHUP); 2286 restore_ttypgrp_and_killsig_or__exit(SIGHUP);
2244 } 2287 }
2245#endif 2288#endif
2246#if ENABLE_HUSH_FAST 2289#if ENABLE_HUSH_FAST
@@ -7389,11 +7432,6 @@ static void switch_off_special_sigs(unsigned mask)
7389} 7432}
7390 7433
7391#if BB_MMU 7434#if BB_MMU
7392/* never called */
7393void re_execute_shell(char ***to_free, const char *s,
7394 char *g_argv0, char **g_argv,
7395 char **builtin_argv) NORETURN;
7396
7397static void reset_traps_to_defaults(void) 7435static void reset_traps_to_defaults(void)
7398{ 7436{
7399 /* This function is always called in a child shell 7437 /* This function is always called in a child shell
@@ -7443,10 +7481,8 @@ static void reset_traps_to_defaults(void)
7443 7481
7444#else /* !BB_MMU */ 7482#else /* !BB_MMU */
7445 7483
7446static void re_execute_shell(char ***to_free, const char *s, 7484static NORETURN void re_execute_shell(
7447 char *g_argv0, char **g_argv, 7485 char * *volatile * to_free, const char *s,
7448 char **builtin_argv) NORETURN;
7449static void re_execute_shell(char ***to_free, const char *s,
7450 char *g_argv0, char **g_argv, 7486 char *g_argv0, char **g_argv,
7451 char **builtin_argv) 7487 char **builtin_argv)
7452{ 7488{
@@ -7676,7 +7712,13 @@ static int generate_stream_from_string(const char *s, pid_t *pid_p)
7676 pid_t pid; 7712 pid_t pid;
7677 int channel[2]; 7713 int channel[2];
7678# if !BB_MMU 7714# if !BB_MMU
7679 char **to_free = NULL; 7715 /* _volatile_ pointer to "char*".
7716 * Or else compiler can peek from inside re_execute_shell()
7717 * and see that this pointer is a local var (i.e. not globally visible),
7718 * and decide to optimize out the store to it. Yes,
7719 * it was seen in the wild.
7720 */
7721 char * *volatile to_free = NULL;
7680# endif 7722# endif
7681 7723
7682 xpipe(channel); 7724 xpipe(channel);
@@ -7823,7 +7865,7 @@ static void setup_heredoc(struct redir_struct *redir)
7823 const char *heredoc = redir->rd_filename; 7865 const char *heredoc = redir->rd_filename;
7824 char *expanded; 7866 char *expanded;
7825#if !BB_MMU 7867#if !BB_MMU
7826 char **to_free; 7868 char * *volatile to_free;
7827#endif 7869#endif
7828 7870
7829 expanded = NULL; 7871 expanded = NULL;
@@ -8298,7 +8340,7 @@ static const struct built_in_command *find_builtin(const char *name)
8298 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]); 8340 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]);
8299} 8341}
8300 8342
8301#if ENABLE_HUSH_JOB && ENABLE_FEATURE_TAB_COMPLETION 8343#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_TAB_COMPLETION
8302static const char * FAST_FUNC hush_command_name(int i) 8344static const char * FAST_FUNC hush_command_name(int i)
8303{ 8345{
8304 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) { 8346 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) {
@@ -8464,10 +8506,8 @@ static void unset_func(const char *name)
8464#define exec_function(to_free, funcp, argv) \ 8506#define exec_function(to_free, funcp, argv) \
8465 exec_function(funcp, argv) 8507 exec_function(funcp, argv)
8466# endif 8508# endif
8467static void exec_function(char ***to_free, 8509static NORETURN void exec_function(
8468 const struct function *funcp, 8510 char * *volatile *to_free,
8469 char **argv) NORETURN;
8470static void exec_function(char ***to_free,
8471 const struct function *funcp, 8511 const struct function *funcp,
8472 char **argv) 8512 char **argv)
8473{ 8513{
@@ -8563,10 +8603,8 @@ static int run_function(const struct function *funcp, char **argv)
8563#define exec_builtin(to_free, x, argv) \ 8603#define exec_builtin(to_free, x, argv) \
8564 exec_builtin(to_free, argv) 8604 exec_builtin(to_free, argv)
8565#endif 8605#endif
8566static void exec_builtin(char ***to_free, 8606static NORETURN void exec_builtin(
8567 const struct built_in_command *x, 8607 char * *volatile *to_free,
8568 char **argv) NORETURN;
8569static void exec_builtin(char ***to_free,
8570 const struct built_in_command *x, 8608 const struct built_in_command *x,
8571 char **argv) 8609 char **argv)
8572{ 8610{
@@ -8589,8 +8627,7 @@ static void exec_builtin(char ***to_free,
8589#endif 8627#endif
8590} 8628}
8591 8629
8592static void execvp_or_die(char **argv) NORETURN; 8630static NORETURN void execvp_or_die(char **argv)
8593static void execvp_or_die(char **argv)
8594{ 8631{
8595 int e; 8632 int e;
8596 debug_printf_exec("execing '%s'\n", argv[0]); 8633 debug_printf_exec("execing '%s'\n", argv[0]);
@@ -8711,10 +8748,8 @@ static void if_command_vV_print_and_exit(char opt_vV, char *cmd, const char *exp
8711 * The at_exit handlers apparently confuse the calling process, 8748 * The at_exit handlers apparently confuse the calling process,
8712 * in particular stdin handling. Not sure why? -- because of vfork! (vda) 8749 * in particular stdin handling. Not sure why? -- because of vfork! (vda)
8713 */ 8750 */
8714static void pseudo_exec_argv(nommu_save_t *nommu_save, 8751static NORETURN NOINLINE void pseudo_exec_argv(
8715 char **argv, int assignment_cnt, 8752 volatile nommu_save_t *nommu_save,
8716 char **argv_expanded) NORETURN;
8717static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8718 char **argv, int assignment_cnt, 8753 char **argv, int assignment_cnt,
8719 char **argv_expanded) 8754 char **argv_expanded)
8720{ 8755{
@@ -8740,7 +8775,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8740#if BB_MMU 8775#if BB_MMU
8741 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */ 8776 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */
8742#else 8777#else
8743 G.shadowed_vars_pp = &nommu_save->old_vars; 8778 /* cast away volatility */
8779 G.shadowed_vars_pp = (struct variable **)&nommu_save->old_vars;
8744 G.var_nest_level++; 8780 G.var_nest_level++;
8745#endif 8781#endif
8746 set_vars_and_save_old(new_env); 8782 set_vars_and_save_old(new_env);
@@ -8867,10 +8903,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8867 8903
8868/* Called after [v]fork() in run_pipe 8904/* Called after [v]fork() in run_pipe
8869 */ 8905 */
8870static void pseudo_exec(nommu_save_t *nommu_save, 8906static NORETURN void pseudo_exec(
8871 struct command *command, 8907 volatile nommu_save_t *nommu_save,
8872 char **argv_expanded) NORETURN;
8873static void pseudo_exec(nommu_save_t *nommu_save,
8874 struct command *command, 8908 struct command *command,
8875 char **argv_expanded) 8909 char **argv_expanded)
8876{ 8910{
@@ -9755,8 +9789,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
9755 9789
9756 /* Stores to nommu_save list of env vars putenv'ed 9790 /* Stores to nommu_save list of env vars putenv'ed
9757 * (NOMMU, on MMU we don't need that) */ 9791 * (NOMMU, on MMU we don't need that) */
9758 /* cast away volatility... */ 9792 pseudo_exec(&nommu_save, command, argv_expanded);
9759 pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded);
9760 /* pseudo_exec() does not return */ 9793 /* pseudo_exec() does not return */
9761 } 9794 }
9762 9795
@@ -10144,7 +10177,7 @@ static int run_list(struct pipe *pi)
10144 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) { 10177 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) {
10145 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth); 10178 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth);
10146 if (G.errexit_depth == 0) 10179 if (G.errexit_depth == 0)
10147 hush_exit(rcode); 10180 save_history_run_exit_trap_and_exit(rcode);
10148 } 10181 }
10149 G.errexit_depth = sv_errexit_depth; 10182 G.errexit_depth = sv_errexit_depth;
10150 10183
@@ -10218,6 +10251,53 @@ static int run_and_free_list(struct pipe *pi)
10218/* 10251/*
10219 * Initialization and main 10252 * Initialization and main
10220 */ 10253 */
10254#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING
10255static void init_line_editing(void)
10256{
10257 G.line_input_state = new_line_input_t(FOR_SHELL);
10258# if ENABLE_FEATURE_TAB_COMPLETION
10259 G.line_input_state->get_exe_name = hush_command_name;
10260# endif
10261# if EDITING_HAS_sh_get_var
10262 G.line_input_state->sh_get_var = get_local_var_value;
10263# endif
10264# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0
10265 {
10266 const char *hp = get_local_var_value("HISTFILE");
10267 if (!hp) {
10268 hp = get_local_var_value("HOME");
10269 if (hp) {
10270 hp = concat_path_file(hp, ".hush_history");
10271 /* Make HISTFILE set on exit (else history won't be saved) */
10272 set_local_var_from_halves("HISTFILE", hp);
10273 }
10274 } else {
10275 hp = xstrdup(hp);
10276 }
10277 if (hp) {
10278 G.line_input_state->hist_file = hp;
10279 }
10280# if ENABLE_FEATURE_SH_HISTFILESIZE
10281 hp = get_local_var_value("HISTSIZE");
10282 /* Using HISTFILESIZE above to limit max_history would be WRONG:
10283 * users may set HISTFILESIZE=0 in their profile scripts
10284 * to prevent _saving_ of history files, but still want to have
10285 * non-zero history limit for in-memory list.
10286 */
10287// in bash, runtime history size is controlled by HISTSIZE (0=no history),
10288// HISTFILESIZE controls on-disk history file size (in lines, 0=no history):
10289 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
10290// HISTFILESIZE: "The shell sets the default value to the value of HISTSIZE after reading any startup files."
10291// HISTSIZE: "The shell sets the default value to 500 after reading any startup files."
10292// (meaning: if the value wasn't set after startup files, the default value is set as described above)
10293# endif
10294 }
10295# endif
10296}
10297#else
10298# define init_line_editing() ((void)0)
10299#endif
10300
10221static void install_sighandlers(unsigned mask) 10301static void install_sighandlers(unsigned mask)
10222{ 10302{
10223 sighandler_t old_handler; 10303 sighandler_t old_handler;
@@ -10356,7 +10436,6 @@ static int set_mode(int state, char mode, const char *o_opt)
10356int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 10436int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
10357int hush_main(int argc, char **argv) 10437int hush_main(int argc, char **argv)
10358{ 10438{
10359 pid_t cached_getpid;
10360 enum { 10439 enum {
10361 OPT_login = (1 << 0), 10440 OPT_login = (1 << 0),
10362 }; 10441 };
@@ -10369,6 +10448,11 @@ int hush_main(int argc, char **argv)
10369 struct variable *shell_ver; 10448 struct variable *shell_ver;
10370 10449
10371 INIT_G(); 10450 INIT_G();
10451#if ENABLE_HUSH_JOB
10452 die_func = fflush_restore_ttypgrp_and__exit;
10453#else
10454 die_func = fflush_and__exit;
10455#endif
10372 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ 10456 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
10373 G.last_exitcode = EXIT_SUCCESS; 10457 G.last_exitcode = EXIT_SUCCESS;
10374#if !BB_MMU 10458#if !BB_MMU
@@ -10384,9 +10468,6 @@ int hush_main(int argc, char **argv)
10384 _exit(0); 10468 _exit(0);
10385 } 10469 }
10386 G.argv0_for_re_execing = argv[0]; 10470 G.argv0_for_re_execing = argv[0];
10387 if (G.argv0_for_re_execing[0] == '-')
10388 /* reexeced hush should never be a login shell */
10389 G.argv0_for_re_execing++;
10390#endif 10471#endif
10391#if ENABLE_HUSH_TRAP 10472#if ENABLE_HUSH_TRAP
10392# if ENABLE_HUSH_FUNCTIONS 10473# if ENABLE_HUSH_FUNCTIONS
@@ -10399,9 +10480,8 @@ int hush_main(int argc, char **argv)
10399 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ 10480 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
10400#endif 10481#endif
10401 10482
10402 cached_getpid = getpid(); /* for tcsetpgrp() during init */ 10483 G.root_pid = getpid(); /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */
10403 G.root_pid = cached_getpid; /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */ 10484 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10404 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10405 10485
10406 /* Deal with HUSH_VERSION */ 10486 /* Deal with HUSH_VERSION */
10407 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); 10487 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
@@ -10484,27 +10564,24 @@ int hush_main(int argc, char **argv)
10484 * PS4='+ ' 10564 * PS4='+ '
10485 */ 10565 */
10486 10566
10567 /* Shell is non-interactive at first. We need to call
10568 * install_special_sighandlers() if we are going to execute "sh <script>",
10569 * "sh -c <cmds>" or login shell's /etc/profile and friends.
10570 * If we later decide that we are interactive, we run
10571 * install_special_sighandlers() in order to intercept more signals.
10572 */
10573 install_special_sighandlers();
10574
10487#if NUM_SCRIPTS > 0 10575#if NUM_SCRIPTS > 0
10488 if (argc < 0) { 10576 if (argc < 0) {
10489 char *script = get_script_content(-argc - 1); 10577 char *script = get_script_content(-argc - 1);
10490 G.global_argv = argv; 10578 G.global_argv = argv;
10491 G.global_argc = string_array_len(argv); 10579 G.global_argc = string_array_len(argv);
10492 //install_special_sighandlers(); - needed?
10493 parse_and_run_string(script); 10580 parse_and_run_string(script);
10494 goto final_return; 10581 goto final_return;
10495 } 10582 }
10496#endif 10583#endif
10497 10584
10498 /* Initialize some more globals to non-zero values */
10499 die_func = restore_ttypgrp_and__exit;
10500
10501 /* Shell is non-interactive at first. We need to call
10502 * install_special_sighandlers() if we are going to execute "sh <script>",
10503 * "sh -c <cmds>" or login shell's /etc/profile and friends.
10504 * If we later decide that we are interactive, we run install_special_sighandlers()
10505 * in order to intercept (more) signals.
10506 */
10507
10508 /* Parse options */ 10585 /* Parse options */
10509 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ 10586 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
10510 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; 10587 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
@@ -10568,6 +10645,7 @@ int hush_main(int argc, char **argv)
10568 case '$': { 10645 case '$': {
10569 unsigned long long empty_trap_mask; 10646 unsigned long long empty_trap_mask;
10570 10647
10648 G.reexeced_on_NOMMU = 1;
10571 G.root_pid = bb_strtou(optarg, &optarg, 16); 10649 G.root_pid = bb_strtou(optarg, &optarg, 16);
10572 optarg++; 10650 optarg++;
10573 G.root_ppid = bb_strtou(optarg, &optarg, 16); 10651 G.root_ppid = bb_strtou(optarg, &optarg, 16);
@@ -10581,7 +10659,6 @@ int hush_main(int argc, char **argv)
10581 empty_trap_mask = bb_strtoull(optarg, &optarg, 16); 10659 empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
10582 if (empty_trap_mask != 0) { 10660 if (empty_trap_mask != 0) {
10583 IF_HUSH_TRAP(int sig;) 10661 IF_HUSH_TRAP(int sig;)
10584 install_special_sighandlers();
10585# if ENABLE_HUSH_TRAP 10662# if ENABLE_HUSH_TRAP
10586 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG); 10663 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
10587 for (sig = 1; sig < NSIG; sig++) { 10664 for (sig = 1; sig < NSIG; sig++) {
@@ -10647,7 +10724,9 @@ int hush_main(int argc, char **argv)
10647 G.global_argv[0] = argv[0]; 10724 G.global_argv[0] = argv[0];
10648 10725
10649 /* If we are login shell... */ 10726 /* If we are login shell... */
10650 if (flags & OPT_login) { 10727 if (!G_reexeced_on_NOMMU /* reexeced hush should never be a login shell */
10728 && (flags & OPT_login)
10729 ) {
10651 const char *hp = NULL; 10730 const char *hp = NULL;
10652 HFILE *input; 10731 HFILE *input;
10653 10732
@@ -10655,7 +10734,6 @@ int hush_main(int argc, char **argv)
10655 input = hfopen("/etc/profile"); 10734 input = hfopen("/etc/profile");
10656 run_profile: 10735 run_profile:
10657 if (input != NULL) { 10736 if (input != NULL) {
10658 install_special_sighandlers();
10659 parse_and_run_file(input); 10737 parse_and_run_file(input);
10660 hfclose(input); 10738 hfclose(input);
10661 } 10739 }
@@ -10692,8 +10770,6 @@ int hush_main(int argc, char **argv)
10692 */ 10770 */
10693 char *script; 10771 char *script;
10694 10772
10695 install_special_sighandlers();
10696
10697 G.global_argc--; 10773 G.global_argc--;
10698 G.global_argv++; 10774 G.global_argv++;
10699#if !BB_MMU 10775#if !BB_MMU
@@ -10744,7 +10820,6 @@ int hush_main(int argc, char **argv)
10744 bb_simple_perror_msg_and_die(G.global_argv[0]); 10820 bb_simple_perror_msg_and_die(G.global_argv[0]);
10745 } 10821 }
10746 xfunc_error_retval = 1; 10822 xfunc_error_retval = 1;
10747 install_special_sighandlers();
10748 parse_and_run_file(input); 10823 parse_and_run_file(input);
10749#if ENABLE_FEATURE_CLEAN_UP 10824#if ENABLE_FEATURE_CLEAN_UP
10750 hfclose(input); 10825 hfclose(input);
@@ -10760,138 +10835,86 @@ int hush_main(int argc, char **argv)
10760 10835
10761 /* A shell is interactive if the '-i' flag was given, 10836 /* A shell is interactive if the '-i' flag was given,
10762 * or if all of the following conditions are met: 10837 * or if all of the following conditions are met:
10763 * no -c command 10838 * not -c 'CMD'
10764 * no arguments remaining or the -s flag given 10839 * not running a script (no arguments remaining, or -s flag given)
10765 * standard input is a terminal 10840 * standard input is a terminal
10766 * standard output is a terminal 10841 * standard output is a terminal
10767 * Refer to Posix.2, the description of the 'sh' utility. 10842 * Refer to Posix.2, the description of the 'sh' utility.
10768 */ 10843 */
10769#if ENABLE_HUSH_JOB 10844#if ENABLE_HUSH_INTERACTIVE
10770 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { 10845 if (!G_reexeced_on_NOMMU
10771 G_saved_tty_pgrp = tcgetpgrp(STDIN_FILENO); 10846 && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
10772 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp); 10847 ) {
10773 if (G_saved_tty_pgrp < 0) 10848 /* Try to dup stdin to high fd#, >= 255 */
10774 G_saved_tty_pgrp = 0;
10775
10776 /* try to dup stdin to high fd#, >= 255 */
10777 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); 10849 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
10778 if (G_interactive_fd < 0) { 10850 if (G_interactive_fd < 0) {
10779 /* try to dup to any fd */ 10851 /* Try to dup to any fd */
10780 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1); 10852 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
10781 if (G_interactive_fd < 0) { 10853 if (G_interactive_fd < 0)
10782 /* give up */ 10854 /* Give up */
10783 G_interactive_fd = 0; 10855 G_interactive_fd = 0;
10784 G_saved_tty_pgrp = 0;
10785 }
10786 } 10856 }
10787 } 10857 debug_printf("interactive_fd:%d\n", G_interactive_fd);
10788 debug_printf("interactive_fd:%d\n", G_interactive_fd); 10858 if (G_interactive_fd) {
10789 if (G_interactive_fd) { 10859// TODO? bash:
10790 if (G_saved_tty_pgrp) { 10860// if interactive but not a login shell, sources ~/.bashrc
10791 /* If we were run as 'hush &', sleep until we are 10861// (--norc turns this off, --rcfile <file> overrides)
10792 * in the foreground (tty pgrp == our pgrp). 10862# if ENABLE_HUSH_JOB
10793 * If we get started under a job aware app (like bash), 10863 /* Can we do job control? */
10794 * make sure we are now in charge so we don't fight over 10864 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
10795 * who gets the foreground */ 10865 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp);
10796 while (1) { 10866 if (G_saved_tty_pgrp < 0)
10797 pid_t shell_pgrp = getpgrp(); 10867 G_saved_tty_pgrp = 0; /* no */
10798 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd); 10868 if (G_saved_tty_pgrp) {
10799 if (G_saved_tty_pgrp == shell_pgrp) 10869 /* If we were run as 'hush &', sleep until we are
10800 break; 10870 * in the foreground (tty pgrp == our pgrp).
10801 /* send TTIN to ourself (should stop us) */ 10871 * If we get started under a job aware app (like bash),
10802 kill(- shell_pgrp, SIGTTIN); 10872 * make sure we are now in charge so we don't fight over
10873 * who gets the foreground */
10874 while (1) {
10875 pid_t shell_pgrp = getpgrp();
10876 if (G_saved_tty_pgrp == shell_pgrp) {
10877/* Often both pgrps here are set to our pid - but not always!
10878 * Example: sh -c 'echo $$; hush; echo FIN'
10879 * Here, the parent shell is not interactive, so it does NOT set up
10880 * a separate process group for its children, and we (hush) initially
10881 * run in parent's process group (until we set up our own a few lines down).
10882 */
10883 //bb_error_msg("process groups tty:%d hush:%d", G_saved_tty_pgrp, shell_pgrp);
10884 break;
10885 }
10886 /* Send TTIN to ourself (should stop us) */
10887 kill(- shell_pgrp, SIGTTIN);
10888 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
10889 }
10803 } 10890 }
10804 }
10805
10806 /* Install more signal handlers */
10807 install_special_sighandlers();
10808
10809 if (G_saved_tty_pgrp) {
10810 /* Set other signals to restore saved_tty_pgrp */
10811 install_fatal_sighandlers();
10812 /* Put ourselves in our own process group
10813 * (bash, too, does this only if ctty is available) */
10814 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
10815 /* Grab control of the terminal */
10816 tcsetpgrp(G_interactive_fd, cached_getpid);
10817 }
10818 enable_restore_tty_pgrp_on_exit();
10819
10820# if ENABLE_FEATURE_EDITING
10821 G.line_input_state = new_line_input_t(FOR_SHELL);
10822# if ENABLE_FEATURE_TAB_COMPLETION
10823 G.line_input_state->get_exe_name = hush_command_name;
10824# endif
10825# if EDITING_HAS_sh_get_var
10826 G.line_input_state->sh_get_var = get_local_var_value;
10827# endif
10828# endif 10891# endif
10829# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0 10892 /* Install more signal handlers */
10830 { 10893 install_special_sighandlers();
10831 const char *hp = get_local_var_value("HISTFILE"); 10894# if ENABLE_HUSH_JOB
10832 if (!hp) { 10895 if (G_saved_tty_pgrp) {
10833 hp = get_local_var_value("HOME"); 10896 /* Set fatal signals to restore saved_tty_pgrp */
10834 if (hp) { 10897 install_fatal_sighandlers();
10835 hp = concat_path_file(hp, ".hush_history"); 10898 /* (The if() is an optimization: can avoid two redundant syscalls) */
10836 /* Make HISTFILE set on exit (else history won't be saved) */ 10899 if (G_saved_tty_pgrp != G.root_pid) {
10837 set_local_var_from_halves("HISTFILE", hp); 10900 /* Put ourselves in our own process group
10901 * (bash, too, does this only if ctty is available) */
10902 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
10903 /* Grab control of the terminal */
10904 tcsetpgrp(G_interactive_fd, G.root_pid);
10838 } 10905 }
10839 } else {
10840 hp = xstrdup(hp);
10841 } 10906 }
10842 if (hp) {
10843 G.line_input_state->hist_file = hp;
10844 }
10845# if ENABLE_FEATURE_SH_HISTFILESIZE
10846 hp = get_local_var_value("HISTSIZE");
10847 /* Using HISTFILESIZE above to limit max_history would be WRONG:
10848 * users may set HISTFILESIZE=0 in their profile scripts
10849 * to prevent _saving_ of history files, but still want to have
10850 * non-zero history limit for in-memory list.
10851 */
10852// in bash, runtime history size is controlled by HISTSIZE (0=no history),
10853// HISTFILESIZE controls on-disk history file size (in lines, 0=no history):
10854 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
10855// HISTFILESIZE: "The shell sets the default value to the value of HISTSIZE after reading any startup files."
10856// HISTSIZE: "The shell sets the default value to 500 after reading any startup files."
10857// (meaning: if the value wasn't set after startup files, the default value is set as described above)
10858# endif
10859 }
10860# endif 10907# endif
10861 } else { 10908# if ENABLE_FEATURE_EDITING_FANCY_PROMPT
10862 install_special_sighandlers(); 10909 /* Set (but not export) PS1/2 unless already set */
10863 } 10910 if (!get_local_var_value("PS1"))
10864#elif ENABLE_HUSH_INTERACTIVE 10911 set_local_var_from_halves("PS1", "\\w \\$ ");
10865 /* No job control compiled in, only prompt/line editing */ 10912 if (!get_local_var_value("PS2"))
10866 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { 10913 set_local_var_from_halves("PS2", "> ");
10867 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); 10914# endif
10868 if (G_interactive_fd < 0) { 10915 init_line_editing();
10869 /* try to dup to any fd */
10870 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
10871 if (G_interactive_fd < 0)
10872 /* give up */
10873 G_interactive_fd = 0;
10874 }
10875 }
10876 install_special_sighandlers();
10877#else
10878 /* We have interactiveness code disabled */
10879 install_special_sighandlers();
10880#endif
10881 /* bash:
10882 * if interactive but not a login shell, sources ~/.bashrc
10883 * (--norc turns this off, --rcfile <file> overrides)
10884 */
10885 10916
10886 if (G_interactive_fd) { 10917# if !ENABLE_FEATURE_SH_EXTRA_QUIET
10887#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
10888 /* Set (but not export) PS1/2 unless already set */
10889 if (!get_local_var_value("PS1"))
10890 set_local_var_from_halves("PS1", "\\w \\$ ");
10891 if (!get_local_var_value("PS2"))
10892 set_local_var_from_halves("PS2", "> ");
10893#endif
10894 if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
10895 /* note: ash and hush share this string */ 10918 /* note: ash and hush share this string */
10896 printf("\n\n%s %s\n" 10919 printf("\n\n%s %s\n"
10897 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n") 10920 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n")
@@ -10899,13 +10922,15 @@ int hush_main(int argc, char **argv)
10899 bb_banner, 10922 bb_banner,
10900 "hush - the humble shell" 10923 "hush - the humble shell"
10901 ); 10924 );
10902 } 10925# endif
10903 } 10926 } /* if become interactive */
10927 } /* if on tty */
10928#endif /* if INTERACTIVE is allowed by build config */
10904 10929
10905 parse_and_run_file(hfopen(NULL)); /* stdin */ 10930 parse_and_run_file(hfopen(NULL)); /* stdin */
10906 10931
10907 final_return: 10932 final_return:
10908 hush_exit(G.last_exitcode); 10933 save_history_run_exit_trap_and_exit(G.last_exitcode);
10909} 10934}
10910 10935
10911/* 10936/*
@@ -11091,19 +11116,19 @@ static int FAST_FUNC builtin_exit(char **argv)
11091 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit" 11116 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit"
11092 */ 11117 */
11093 11118
11094 /* note: EXIT trap is run by hush_exit */ 11119 /* note: EXIT trap is run by save_history_run_exit_trap_and_exit */
11095 argv = skip_dash_dash(argv); 11120 argv = skip_dash_dash(argv);
11096 if (argv[0] == NULL) { 11121 if (argv[0] == NULL) {
11097#if ENABLE_HUSH_TRAP 11122#if ENABLE_HUSH_TRAP
11098 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */ 11123 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */
11099 hush_exit(G.pre_trap_exitcode); 11124 save_history_run_exit_trap_and_exit(G.pre_trap_exitcode);
11100#endif 11125#endif
11101 hush_exit(G.last_exitcode); 11126 save_history_run_exit_trap_and_exit(G.last_exitcode);
11102 } 11127 }
11103 /* mimic bash: exit 123abc == exit 255 + error msg */ 11128 /* mimic bash: exit 123abc == exit 255 + error msg */
11104 xfunc_error_retval = 255; 11129 xfunc_error_retval = 255;
11105 /* bash: exit -2 == exit 254, no error msg */ 11130 /* bash: exit -2 == exit 254, no error msg */
11106 hush_exit(xatoi(argv[0]) & 0xff); 11131 save_history_run_exit_trap_and_exit(xatoi(argv[0]) & 0xff);
11107} 11132}
11108 11133
11109#if ENABLE_HUSH_TYPE 11134#if ENABLE_HUSH_TYPE
diff --git a/shell/hush_leaktool.sh b/shell/hush_leaktool.sh
index ca35ec144..3edd3df61 100755
--- a/shell/hush_leaktool.sh
+++ b/shell/hush_leaktool.sh
@@ -7,7 +7,7 @@ freelist=`grep 'free 0x' "$output" | cut -d' ' -f2 | sort | uniq | xargs`
7 7
8grep -v free "$output" >"$output.leaked" 8grep -v free "$output" >"$output.leaked"
9 9
10i=8 10i=16
11list= 11list=
12for freed in $freelist; do 12for freed in $freelist; do
13 list="$list -e $freed" 13 list="$list -e $freed"
@@ -15,7 +15,7 @@ for freed in $freelist; do
15 echo Dropping $list 15 echo Dropping $list
16 grep -F -v $list <"$output.leaked" >"$output.temp" 16 grep -F -v $list <"$output.leaked" >"$output.temp"
17 mv "$output.temp" "$output.leaked" 17 mv "$output.temp" "$output.leaked"
18 i=8 18 i=16
19 list= 19 list=
20done 20done
21if test "$list"; then 21if test "$list"; then
@@ -23,3 +23,17 @@ if test "$list"; then
23 grep -F -v $list <"$output.leaked" >"$output.temp" 23 grep -F -v $list <"$output.leaked" >"$output.temp"
24 mv "$output.temp" "$output.leaked" 24 mv "$output.temp" "$output.leaked"
25fi 25fi
26
27# All remaining allocations are on addresses which were never freed.
28# * Sort them by line, grouping together allocations which allocated the same address.
29# A leaky allocation will give many different addresses (because it's never freed,
30# the address can not be reused).
31# * Remove the address (field #4).
32# * Count the allocations per every unique source line and alloc type.
33# * Show largest counts on top.
34cat output.leaked \
35 | sort -u \
36 | cut -d' ' -f1-3 \
37 | uniq -c \
38 | sort -rn \
39>output.leaked.counted_uniq_alloc_address
diff --git a/shell/hush_test/hush-misc/sig_exitcode.tests b/shell/hush_test/hush-misc/sig_exitcode.tests
index 7879dc854..67b4500f4 100755
--- a/shell/hush_test/hush-misc/sig_exitcode.tests
+++ b/shell/hush_test/hush-misc/sig_exitcode.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1exec 2>&1 4exec 2>&1
2 5
3$THIS_SH -c 'kill -9 $$' 6$THIS_SH -c 'kill -9 $$'
diff --git a/shell/hush_test/hush-misc/wait1.tests b/shell/hush_test/hush-misc/wait1.tests
index f9cf6d48c..54120319b 100755
--- a/shell/hush_test/hush-misc/wait1.tests
+++ b/shell/hush_test/hush-misc/wait1.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 2 & sleep 1 & wait $! 4sleep 2 & sleep 1 & wait $!
2echo $? 5echo $?
3jobs 6jobs
diff --git a/shell/hush_test/hush-misc/wait2.tests b/shell/hush_test/hush-misc/wait2.tests
index be20f95a5..60f382c9f 100755
--- a/shell/hush_test/hush-misc/wait2.tests
+++ b/shell/hush_test/hush-misc/wait2.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 3 & sleep 2 & sleep 1 4sleep 3 & sleep 2 & sleep 1
2wait $! 5wait $!
3echo $? 6echo $?
diff --git a/shell/hush_test/hush-misc/wait3.tests b/shell/hush_test/hush-misc/wait3.tests
index ac541c3fc..aceed1126 100755
--- a/shell/hush_test/hush-misc/wait3.tests
+++ b/shell/hush_test/hush-misc/wait3.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 2 & (sleep 1;exit 3) & wait $! 4sleep 2 & (sleep 1;exit 3) & wait $!
2echo $? 5echo $?
3jobs 6jobs
diff --git a/shell/hush_test/hush-misc/wait4.tests b/shell/hush_test/hush-misc/wait4.tests
index cc34059ac..c979a38b6 100755
--- a/shell/hush_test/hush-misc/wait4.tests
+++ b/shell/hush_test/hush-misc/wait4.tests
@@ -1,2 +1,5 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 1 | (sleep 1;exit 3) & wait %1 4sleep 1 | (sleep 1;exit 3) & wait %1
2echo Three:$? 5echo Three:$?
diff --git a/shell/hush_test/hush-misc/wait5.tests b/shell/hush_test/hush-misc/wait5.tests
index 1b4762d89..e0ac8c251 100755
--- a/shell/hush_test/hush-misc/wait5.tests
+++ b/shell/hush_test/hush-misc/wait5.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 0 | (sleep 0;exit 3) & 4sleep 0 | (sleep 0;exit 3) &
2sleep 1 5sleep 1
3echo Zero:$? 6echo Zero:$?
diff --git a/shell/hush_test/hush-misc/wait6.tests b/shell/hush_test/hush-misc/wait6.tests
index c23713199..c09002ab0 100755
--- a/shell/hush_test/hush-misc/wait6.tests
+++ b/shell/hush_test/hush-misc/wait6.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1# In bash, "wait $!" extracts correct exitcode even if bg task has already exited 4# In bash, "wait $!" extracts correct exitcode even if bg task has already exited
2# It prints 0, then 3: 5# It prints 0, then 3:
3(sleep 0; exit 3) & sleep 1 6(sleep 0; exit 3) & sleep 1
diff --git a/shell/hush_test/hush-signals/catch.tests b/shell/hush_test/hush-signals/catch.tests
index d2a21d17e..4b1a08e8f 100755
--- a/shell/hush_test/hush-signals/catch.tests
+++ b/shell/hush_test/hush-signals/catch.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test ("User defined signal 2" text not emitted)
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1# avoid ugly warnings about signals not being caught 4# avoid ugly warnings about signals not being caught
2trap ":" USR1 USR2 5trap ":" USR1 USR2
3 6
diff --git a/shell/hush_test/hush-signals/signal1.tests b/shell/hush_test/hush-signals/signal1.tests
index 61943467a..c83fa1254 100755
--- a/shell/hush_test/hush-signals/signal1.tests
+++ b/shell/hush_test/hush-signals/signal1.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1trap "echo got signal" USR1 4trap "echo got signal" USR1
2 5
3for try in 1 2 3 4 5; do 6for try in 1 2 3 4 5; do
diff --git a/shell/hush_test/hush-signals/signal8.tests b/shell/hush_test/hush-signals/signal8.tests
index 731af7477..cd5790164 100755
--- a/shell/hush_test/hush-signals/signal8.tests
+++ b/shell/hush_test/hush-signals/signal8.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1"$THIS_SH" -c ' 4"$THIS_SH" -c '
2exit_func() { 5exit_func() {
3 echo "Removing traps" 6 echo "Removing traps"
diff --git a/shell/hush_test/hush-signals/signal_read2.tests b/shell/hush_test/hush-signals/signal_read2.tests
index eab5b9b5b..11accd5ab 100755
--- a/shell/hush_test/hush-signals/signal_read2.tests
+++ b/shell/hush_test/hush-signals/signal_read2.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test ("Hangup" not emitted)
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1$THIS_SH -c ' 4$THIS_SH -c '
2(sleep 1; kill -HUP $$) & 5(sleep 1; kill -HUP $$) &
3while true; do 6while true; do
diff --git a/shell/hush_test/hush-signals/subshell.tests b/shell/hush_test/hush-signals/subshell.tests
index d877f2b82..856c922d3 100755
--- a/shell/hush_test/hush-signals/subshell.tests
+++ b/shell/hush_test/hush-signals/subshell.tests
@@ -1,5 +1,8 @@
1# Non-empty traps should be reset in subshell 1# Non-empty traps should be reset in subshell
2 2
3# If job control is disabled, skip the test ("Terminated" text not emitted)
4test "`type jobs`" = "jobs is a shell builtin" || exit 77
5
3# HUP is special in interactive shells 6# HUP is special in interactive shells
4trap '' HUP 7trap '' HUP
5# QUIT is always special 8# QUIT is always special
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all
index 7345fee43..ff29ca0b1 100755
--- a/shell/hush_test/run-all
+++ b/shell/hush_test/run-all
@@ -67,9 +67,12 @@ do_test()
67# echo Running test: "$x" 67# echo Running test: "$x"
68 echo -n "$1/$x:" 68 echo -n "$1/$x:"
69 ( 69 (
70 "$THIS_SH" "./$x" 2>&1 | \ 70 "$THIS_SH" "./$x" >"$name.xx" 2>&1
71 grep -va "^hush: using fallback suid method$" >"$name.xx"
72 r=$? 71 r=$?
72 # filter !FEATURE_SUID_CONFIG_QUIET message
73 sed -i \
74 -e "/^hush: using fallback suid method$/d" \
75 "$name.xx"
73 # filter C library differences 76 # filter C library differences
74 sed -i \ 77 sed -i \
75 -e "/: invalid option /s:'::g" \ 78 -e "/: invalid option /s:'::g" \
diff --git a/testsuite/hexdump.tests b/testsuite/hexdump.tests
index b2f6a2201..d2c0a5dc8 100755
--- a/testsuite/hexdump.tests
+++ b/testsuite/hexdump.tests
@@ -56,6 +56,16 @@ testing "hexdump -e %3_u" \
56" \ 56" \
57 "" "$input" 57 "" "$input"
58 58
59testing "hexdump -e %3_c" \
60 "hexdump -e '16/1 \" %3_c\" \"\n\"'" \
61' \\0 001 002 003 004 005 006 \\a \\b \\t \\n \\v \\f \\r 016 017
62 020 021 022 023 024 025 026 027 030 031 032 033 034 035 036 037
63 p q r s t u v w x y z { | } ~ 177
64 200 201 202 203 204 205 206 207 210 211 212 213 214 215 216 217
65 360 361 362 363 364 365 366 367 370 371 372 373 374 375 376 377
66' \
67 "" "$input"
68
59testing "hexdump -e /1 %d" \ 69testing "hexdump -e /1 %d" \
60 "hexdump -e '16/1 \" %4d\" \"\n\"'" \ 70 "hexdump -e '16/1 \" %4d\" \"\n\"'" \
61 "\ 71 "\
diff --git a/testsuite/ls.tests b/testsuite/ls.tests
index 9309d366b..a95911034 100755
--- a/testsuite/ls.tests
+++ b/testsuite/ls.tests
@@ -19,7 +19,7 @@ test x"$CONFIG_UNICODE_SUPPORT" = x"y" \
19&& test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"767" \ 19&& test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"767" \
20&& test x"$CONFIG_FEATURE_LS_SORTFILES" = x"y" \ 20&& test x"$CONFIG_FEATURE_LS_SORTFILES" = x"y" \
21&& testing "ls unicode test with codepoints limited to 767" \ 21&& testing "ls unicode test with codepoints limited to 767" \
22"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1 ls.testdir" \ 22"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1q ls.testdir" \
23'0001_1__Some_correct_UTF-8_text___________________________________________| 23'0001_1__Some_correct_UTF-8_text___________________________________________|
240002_2__Boundary_condition_test_cases_____________________________________| 240002_2__Boundary_condition_test_cases_____________________________________|
250003_2.1__First_possible_sequence_of_a_certain_length_____________________| 250003_2.1__First_possible_sequence_of_a_certain_length_____________________|
@@ -138,7 +138,7 @@ test x"$CONFIG_UNICODE_SUPPORT" = x"y" \
138&& test x"$CONFIG_SUBST_WCHAR" = x"63" \ 138&& test x"$CONFIG_SUBST_WCHAR" = x"63" \
139&& test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"0" \ 139&& test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"0" \
140&& testing "ls unicode test with unlimited codepoints" \ 140&& testing "ls unicode test with unlimited codepoints" \
141"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1 ls.testdir" \ 141"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1q ls.testdir" \
142'0001_1__Some_correct_UTF-8_text___________________________________________| 142'0001_1__Some_correct_UTF-8_text___________________________________________|
1430002_2__Boundary_condition_test_cases_____________________________________| 1430002_2__Boundary_condition_test_cases_____________________________________|
1440003_2.1__First_possible_sequence_of_a_certain_length_____________________| 1440003_2.1__First_possible_sequence_of_a_certain_length_____________________|
@@ -262,6 +262,28 @@ test x"$CONFIG_FEATURE_LS_SORTFILES" = x"y" \
262"A\nB\nA\nB\nA\nB\n" \ 262"A\nB\nA\nB\nA\nB\n" \
263"" "" 263"" ""
264 264
265rm -rf ls.testdir 2>/dev/null
266mkdir ls.testdir || exit 1
267touch "`printf "ls.testdir/\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f_\x7f\x80\xfe\xff_\x22_\x27_\x5c"`"
268
269sq="'"
270
271# testing "test name" "command" "expected result" "file input" "stdin"
272testing "ls -q" \
273'ls -q ls.testdir' \
274'???????????????????????????????_????_"_'$sq'_\\''\n' \
275"" ""
276
277testing "ls -Q" \
278'ls -Q ls.testdir' \
279'"\\001\\002\\003\\004\\005\\006\\a\\b\\t\\n\\v\\f\\r\\016\\017\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\033\\034\\035\\036\\037_\\177\\200\\376\\377_\\"_'$sq'_\\\\"\n' \
280"" ""
281
282testing "ls -qQ" \
283'ls -qQ ls.testdir' \
284'"\\001\\002\\003\\004\\005\\006\\a\\b\\t\\n\\v\\f\\r\\016\\017\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\033\\034\\035\\036\\037_\\177\\200\\376\\377_\\"_'$sq'_\\\\"\n' \
285"" ""
286
265# Clean up 287# Clean up
266rm -rf ls.testdir 2>/dev/null 288rm -rf ls.testdir 2>/dev/null
267 289
diff --git a/testsuite/md5sum.tests b/testsuite/md5sum.tests
index cca26dc64..a6d2b7ffb 100755
--- a/testsuite/md5sum.tests
+++ b/testsuite/md5sum.tests
@@ -9,6 +9,7 @@
9# efe30c482e0b687e0cca0612f42ca29b 9# efe30c482e0b687e0cca0612f42ca29b
10# d41337e834377140ae7f98460d71d908598ef04f 10# d41337e834377140ae7f98460d71d908598ef04f
11# 8e1d3ed57ebc130f0f72508446559eeae06451ae6d61b1e8ce46370cfb8963c3 11# 8e1d3ed57ebc130f0f72508446559eeae06451ae6d61b1e8ce46370cfb8963c3
12# c01420bb6613d4bd00396a82033e59e81486cbb045ae0dd2b4c3332e581b3ce09fb1946d6e283acec685778ff205d485
12# fe413e0f177324d1353893ca0772ceba83fd41512ba63895a0eebb703ef9feac2fb4e92b2cb430b3bda41b46b0cb4ea8307190a5cc795157cfb680a9cd635d0f 13# fe413e0f177324d1353893ca0772ceba83fd41512ba63895a0eebb703ef9feac2fb4e92b2cb430b3bda41b46b0cb4ea8307190a5cc795157cfb680a9cd635d0f
13 14
14if ! test "$1"; then 15if ! test "$1"; then
@@ -31,9 +32,9 @@ text=`yes "$text" | head -c 9999`
31result=`( 32result=`(
32n=0 33n=0
33while test $n -le 999; do 34while test $n -le 999; do
34 echo "$text" | head -c $n | "$sum" 35 echo "$text" | head -c $n | $sum
35 n=$(($n+1)) 36 n=$(($n+1))
36done | "$sum" 37done | $sum
37)` 38)`
38if test x"$result" != x"$expected -"; then 39if test x"$result" != x"$expected -"; then
39 echo "FAIL: $sum (r:$result exp:$expected)" 40 echo "FAIL: $sum (r:$result exp:$expected)"
@@ -44,7 +45,7 @@ fi
44 45
45# GNU compat: -c EMPTY must fail (exitcode 1)! 46# GNU compat: -c EMPTY must fail (exitcode 1)!
46>EMPTY 47>EMPTY
47if "$sum" -c EMPTY 2>/dev/null; then 48if $sum -c EMPTY 2>/dev/null; then
48 echo "FAIL: $sum -c EMPTY" 49 echo "FAIL: $sum -c EMPTY"
49 : $((FAILCOUNT++)) 50 : $((FAILCOUNT++))
50else 51else
diff --git a/testsuite/sha384sum.tests b/testsuite/sha384sum.tests
new file mode 100755
index 000000000..f1449d195
--- /dev/null
+++ b/testsuite/sha384sum.tests
@@ -0,0 +1,3 @@
1#!/bin/sh
2
3. ./md5sum.tests sha384sum c01420bb6613d4bd00396a82033e59e81486cbb045ae0dd2b4c3332e581b3ce09fb1946d6e283acec685778ff205d485
diff --git a/testsuite/sha3sum.tests b/testsuite/sha3sum.tests
index 2cd8e3bf2..631141735 100755
--- a/testsuite/sha3sum.tests
+++ b/testsuite/sha3sum.tests
@@ -1,3 +1,7 @@
1#!/bin/sh 1#!/bin/sh
2 2
3. ./md5sum.tests sha3sum 11659f09370139f8ef384f4a6260947fafa6e4fcd87a1ef3f35410e9 3 (. ./md5sum.tests sha3sum 11659f09370139f8ef384f4a6260947fafa6e4fcd87a1ef3f35410e9) \
4&& (. ./md5sum.tests "sha3sum -a224" 11659f09370139f8ef384f4a6260947fafa6e4fcd87a1ef3f35410e9) \
5&& (. ./md5sum.tests "sha3sum -a256" 6f69c8d36a9a579a943d878dab38c179d2a9dde12b244aa8840002c0f3d5bb73) \
6&& (. ./md5sum.tests "sha3sum -a384" 303913449042257996a869e0378323193b4f58d90eea801b12186a3d65640bd3403d3404c63527424ec43dff842c0cd0) \
7&& (. ./md5sum.tests "sha3sum -a512" e14814dccc2fef967af74eb6710885b35dfe660a362c0609b642404987d24a13dac66ad037e6affa5c42631110231655fcf4c972b1457ac49fb83af8113fc51f)
diff --git a/util-linux/lspci.c b/util-linux/lspci.c
index b38b46be3..25be23a01 100644
--- a/util-linux/lspci.c
+++ b/util-linux/lspci.c
@@ -47,7 +47,7 @@ static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM,
47 int pci_class = 0, pci_vid = 0, pci_did = 0; 47 int pci_class = 0, pci_vid = 0, pci_did = 0;
48 int pci_subsys_vid = 0, pci_subsys_did = 0; 48 int pci_subsys_vid = 0, pci_subsys_did = 0;
49 49
50 char *uevent_filename = concat_path_file(fileName, "/uevent"); 50 char *uevent_filename = concat_path_file(fileName, "uevent");
51 parser = config_open2(uevent_filename, fopen_for_read); 51 parser = config_open2(uevent_filename, fopen_for_read);
52 free(uevent_filename); 52 free(uevent_filename);
53 53
diff --git a/util-linux/lsusb.c b/util-linux/lsusb.c
index 0a9e505f4..f7d0de32d 100644
--- a/util-linux/lsusb.c
+++ b/util-linux/lsusb.c
@@ -50,7 +50,7 @@ static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM,
50 char *tokens[4]; 50 char *tokens[4];
51 char *busnum = NULL, *devnum = NULL; 51 char *busnum = NULL, *devnum = NULL;
52 int product_vid = 0, product_did = 0; 52 int product_vid = 0, product_did = 0;
53 char *uevent_filename = concat_path_file(fileName, "/uevent"); 53 char *uevent_filename = concat_path_file(fileName, "uevent");
54 54
55 parser = config_open2(uevent_filename, fopen_for_read); 55 parser = config_open2(uevent_filename, fopen_for_read);
56 free(uevent_filename); 56 free(uevent_filename);
diff --git a/win32/process.c b/win32/process.c
index 33f45ee42..0d120936e 100644
--- a/win32/process.c
+++ b/win32/process.c
@@ -795,7 +795,7 @@ UNUSED_PARAM
795 return sp; 795 return sp;
796} 796}
797 797
798void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) 798int FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
799{ 799{
800 const char *str, *cmdline; 800 const char *str, *cmdline;
801 801
@@ -807,6 +807,7 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
807 else 807 else
808 cmdline = comm; 808 cmdline = comm;
809 safe_strncpy(buf, cmdline, col); 809 safe_strncpy(buf, cmdline, col);
810 return 0;
810} 811}
811 812
812/** 813/**