aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2012-03-22 15:21:20 +0000
committerRon Yorston <rmy@pobox.com>2012-03-22 15:21:20 +0000
commit0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2 (patch)
tree6709ddd6071a9c238ba69233540bbcfe560c6a44
parent67758035a4fe040c6ac69b39d61bcd6bddd7b827 (diff)
parent56a3b82e9692a25ef9c9269e88feac0d579ce8e8 (diff)
downloadbusybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.tar.gz
busybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.tar.bz2
busybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.zip
Merge commit '56a3b82e9692a25ef9c9269e88feac0d579ce8e8' into merge
Conflicts: coreutils/ls.c include/platform.h libbb/bb_basename.c
-rw-r--r--.gitignore1
-rw-r--r--Config.in22
-rw-r--r--Makefile.flags4
-rw-r--r--archival/dpkg.c7
-rw-r--r--archival/libarchive/bz/compress.c7
-rw-r--r--archival/tar.c36
-rw-r--r--console-tools/setconsole.c2
-rw-r--r--coreutils/ls.c853
-rw-r--r--coreutils/od.c7
-rw-r--r--coreutils/od_bloaty.c443
-rw-r--r--coreutils/tail.c36
-rw-r--r--docs/style-guide.txt11
-rw-r--r--e2fsprogs/fsck.c3
-rw-r--r--e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c6
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/getsize.c6
-rw-r--r--editors/diff.c4
-rw-r--r--editors/patch.c2
-rw-r--r--editors/sed.c157
-rw-r--r--editors/vi.c6
-rw-r--r--examples/udhcp/udhcpd.conf23
-rw-r--r--findutils/find.c42
-rw-r--r--include/archive.h2
-rw-r--r--include/libbb.h38
-rw-r--r--include/mingw.h2
-rw-r--r--include/platform.h158
-rw-r--r--libbb/Kbuild.src1
-rw-r--r--libbb/appletlib.c213
-rw-r--r--libbb/bb_basename.c23
-rw-r--r--libbb/dump.c7
-rw-r--r--libbb/get_last_path_component.c15
-rw-r--r--libbb/getopt32.c4
-rw-r--r--libbb/lineedit.c24
-rw-r--r--libbb/llist.c2
-rw-r--r--libbb/pw_encrypt.c25
-rw-r--r--libbb/read_printf.c17
-rw-r--r--libpwdgrp/uidgid_get.c3
-rw-r--r--loginutils/adduser.c36
-rw-r--r--loginutils/chpasswd.c11
-rw-r--r--loginutils/cryptpw.c31
-rw-r--r--loginutils/passwd.c83
-rw-r--r--mailutils/Kbuild.src4
-rw-r--r--mailutils/mail.c4
-rw-r--r--mailutils/makemime.c (renamed from mailutils/mime.c)337
-rw-r--r--mailutils/popmaildir.c2
-rw-r--r--mailutils/reformime.c280
-rw-r--r--mailutils/sendmail.c2
-rw-r--r--miscutils/dc.c2
-rw-r--r--miscutils/last_fancy.c3
-rw-r--r--miscutils/makedevs.c2
-rw-r--r--miscutils/setserial.c763
-rw-r--r--miscutils/ubi_tools.c (renamed from miscutils/ubi_attach_detach.c)96
-rw-r--r--networking/ftpgetput.c7
-rw-r--r--networking/httpd.c2
-rw-r--r--networking/inetd.c5
-rw-r--r--networking/ntpd.c4
-rw-r--r--networking/traceroute.c61
-rw-r--r--networking/udhcp/Config.src8
-rw-r--r--networking/udhcp/common.c8
-rw-r--r--networking/udhcp/common.h2
-rw-r--r--networking/udhcp/dhcpc.c57
-rw-r--r--networking/udhcp/files.c6
-rw-r--r--printutils/lpd.c2
-rw-r--r--procps/fuser.c386
-rw-r--r--procps/iostat.c335
-rw-r--r--procps/mpstat.c3
-rw-r--r--procps/nmeter.c26
-rw-r--r--procps/powertop.c2
-rw-r--r--procps/top.c282
-rw-r--r--runit/sv.c3
-rw-r--r--scripts/defconfig.tig5
-rw-r--r--scripts/kconfig/conf.c11
-rw-r--r--scripts/kconfig/lxdialog/textbox.c8
-rwxr-xr-xscripts/trylink1
-rw-r--r--selinux/chcon.c1
-rw-r--r--selinux/runcon.c1
-rw-r--r--shell/ash.c8
-rw-r--r--shell/hush.c717
-rw-r--r--shell/hush_test/hush-misc/assignment4.right1
-rwxr-xr-xshell/hush_test/hush-misc/assignment4.tests3
-rw-r--r--shell/hush_test/hush-misc/while3.right1
-rwxr-xr-xshell/hush_test/hush-misc/while3.tests4
-rw-r--r--shell/hush_test/hush-trap/signal_read1.right1
-rwxr-xr-xshell/hush_test/hush-trap/signal_read1.tests5
-rw-r--r--shell/hush_test/hush-trap/signal_read2.right2
-rwxr-xr-xshell/hush_test/hush-trap/signal_read2.tests7
-rwxr-xr-xshell/hush_test/run-all10
-rw-r--r--shell/shell_common.c43
-rw-r--r--sysklogd/klogd.c2
-rw-r--r--sysklogd/syslogd.c31
-rwxr-xr-xtestsuite/od.tests22
-rwxr-xr-xtestsuite/sed.tests3
-rw-r--r--util-linux/acpid.c2
-rw-r--r--util-linux/fbset.c14
-rw-r--r--util-linux/fdisk.c35
-rw-r--r--util-linux/getopt.c6
-rw-r--r--util-linux/mdev.c2
-rw-r--r--util-linux/mount.c2
97 files changed, 3706 insertions, 2309 deletions
diff --git a/.gitignore b/.gitignore
index 7d2cca67c..0a0c65bc3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,6 +18,7 @@ Config.in
18# Normal output 18# Normal output
19# 19#
20/busybox 20/busybox
21/busybox_old
21/busybox_unstripped* 22/busybox_unstripped*
22 23
23# 24#
diff --git a/Config.in b/Config.in
index 63d9f5ad7..597ceb02b 100644
--- a/Config.in
+++ b/Config.in
@@ -271,8 +271,9 @@ config UNICODE_PRESERVE_BROKEN
271 default n 271 default n
272 depends on UNICODE_SUPPORT 272 depends on UNICODE_SUPPORT
273 help 273 help
274 With this option on, invalid UTF-8 bytes are not substituted 274 With this option on, on line-editing input (such as used by shells)
275 with the selected substitution character. 275 invalid UTF-8 bytes are not substituted with the selected
276 substitution character.
276 For example, this means that entering 'l', 's', ' ', 0xff, [Enter] 277 For example, this means that entering 'l', 's', ' ', 0xff, [Enter]
277 at shell prompt will list file named 0xff (single char name 278 at shell prompt will list file named 0xff (single char name
278 with char value 255), not file named '?'. 279 with char value 255), not file named '?'.
@@ -365,14 +366,24 @@ config FEATURE_SUID
365 366
366config FEATURE_SUID_CONFIG 367config FEATURE_SUID_CONFIG
367 bool "Runtime SUID/SGID configuration via /etc/busybox.conf" 368 bool "Runtime SUID/SGID configuration via /etc/busybox.conf"
368 default y if FEATURE_SUID 369 default y
369 depends on FEATURE_SUID 370 depends on FEATURE_SUID
370 help 371 help
371 Allow the SUID / SGID state of an applet to be determined at runtime 372 Allow the SUID / SGID state of an applet to be determined at runtime
372 by checking /etc/busybox.conf. (This is sort of a poor man's sudo.) 373 by checking /etc/busybox.conf. (This is sort of a poor man's sudo.)
373 The format of this file is as follows: 374 The format of this file is as follows:
374 375
375 <applet> = [Ssx-][Ssx-][x-] (<username>|<uid>).(<groupname>|<gid>) 376 APPLET = [Ssx-][Ssx-][x-] [USER.GROUP]
377
378 s: USER or GROUP is allowed to execute APPLET.
379 APPLET will run under USER or GROUP
380 (reagardless of who's running it).
381 S: USER or GROUP is NOT allowed to execute APPLET.
382 APPLET will run under USER or GROUP.
383 This option is not very sensical.
384 x: USER/GROUP/others are allowed to execute APPLET.
385 No UID/GID change will be done when it is run.
386 -: USER/GROUP/others are not allowed to execute APPLET.
376 387
377 An example might help: 388 An example might help:
378 389
@@ -382,7 +393,8 @@ config FEATURE_SUID_CONFIG
382 su = ssx # exactly the same 393 su = ssx # exactly the same
383 394
384 mount = sx- root.disk # applet mount can be run by root and members 395 mount = sx- root.disk # applet mount can be run by root and members
385 # of group disk and runs with euid=0 396 # of group disk (but not anyone else)
397 # and runs with euid=0 (egid is not changed)
386 398
387 cp = --- # disable applet cp for everyone 399 cp = --- # disable applet cp for everyone
388 400
diff --git a/Makefile.flags b/Makefile.flags
index c7a805877..513b6ebb2 100644
--- a/Makefile.flags
+++ b/Makefile.flags
@@ -58,14 +58,14 @@ CFLAGS += $(call cc-option,-falign-functions=1 -falign-jumps=1 -falign-labels=1
58#CFLAGS += $(call cc-option,-Wconversion,) 58#CFLAGS += $(call cc-option,-Wconversion,)
59 59
60ifneq ($(CONFIG_DEBUG),y) 60ifneq ($(CONFIG_DEBUG),y)
61CFLAGS += $(call cc-option,-Os,) 61CFLAGS += $(call cc-option,-Os,$(call cc-option,-O2,))
62else 62else
63CFLAGS += $(call cc-option,-g,) 63CFLAGS += $(call cc-option,-g,)
64#CFLAGS += "-D_FORTIFY_SOURCE=2" 64#CFLAGS += "-D_FORTIFY_SOURCE=2"
65ifeq ($(CONFIG_DEBUG_PESSIMIZE),y) 65ifeq ($(CONFIG_DEBUG_PESSIMIZE),y)
66CFLAGS += $(call cc-option,-O0,) 66CFLAGS += $(call cc-option,-O0,)
67else 67else
68CFLAGS += $(call cc-option,-Os,) 68CFLAGS += $(call cc-option,-Os,$(call cc-option,-O2,))
69endif 69endif
70endif 70endif
71 71
diff --git a/archival/dpkg.c b/archival/dpkg.c
index f8e349d09..8899d1719 100644
--- a/archival/dpkg.c
+++ b/archival/dpkg.c
@@ -702,28 +702,21 @@ static unsigned get_status(const unsigned status_node, const int num)
702 702
703static void set_status(const unsigned status_node_num, const char *new_value, const int position) 703static void set_status(const unsigned status_node_num, const char *new_value, const int position)
704{ 704{
705 const unsigned new_value_len = strlen(new_value);
706 const unsigned new_value_num = search_name_hashtable(new_value); 705 const unsigned new_value_num = search_name_hashtable(new_value);
707 unsigned want = get_status(status_node_num, 1); 706 unsigned want = get_status(status_node_num, 1);
708 unsigned flag = get_status(status_node_num, 2); 707 unsigned flag = get_status(status_node_num, 2);
709 unsigned status = get_status(status_node_num, 3); 708 unsigned status = get_status(status_node_num, 3);
710 int want_len = strlen(name_hashtable[want]);
711 int flag_len = strlen(name_hashtable[flag]);
712 int status_len = strlen(name_hashtable[status]);
713 char *new_status; 709 char *new_status;
714 710
715 switch (position) { 711 switch (position) {
716 case 1: 712 case 1:
717 want = new_value_num; 713 want = new_value_num;
718 want_len = new_value_len;
719 break; 714 break;
720 case 2: 715 case 2:
721 flag = new_value_num; 716 flag = new_value_num;
722 flag_len = new_value_len;
723 break; 717 break;
724 case 3: 718 case 3:
725 status = new_value_num; 719 status = new_value_num;
726 status_len = new_value_len;
727 break; 720 break;
728 default: 721 default:
729 bb_error_msg_and_die("DEBUG ONLY: this shouldnt happen"); 722 bb_error_msg_and_die("DEBUG ONLY: this shouldnt happen");
diff --git a/archival/libarchive/bz/compress.c b/archival/libarchive/bz/compress.c
index 6f1c70a08..f93671742 100644
--- a/archival/libarchive/bz/compress.c
+++ b/archival/libarchive/bz/compress.c
@@ -251,7 +251,7 @@ void sendMTFValues(EState* s)
251{ 251{
252 int32_t v, t, i, j, gs, ge, totc, bt, bc, iter; 252 int32_t v, t, i, j, gs, ge, totc, bt, bc, iter;
253 int32_t nSelectors, alphaSize, minLen, maxLen, selCtr; 253 int32_t nSelectors, alphaSize, minLen, maxLen, selCtr;
254 int32_t nGroups, nBytes; 254 int32_t nGroups;
255 255
256 /* 256 /*
257 * uint8_t len[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; 257 * uint8_t len[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
@@ -512,7 +512,6 @@ void sendMTFValues(EState* s)
512 } 512 }
513 } 513 }
514 514
515 nBytes = s->numZ;
516 bsW(s, 16, inUse16); 515 bsW(s, 16, inUse16);
517 516
518 inUse16 <<= (sizeof(int)*8 - 16); /* move 15th bit into sign bit */ 517 inUse16 <<= (sizeof(int)*8 - 16); /* move 15th bit into sign bit */
@@ -528,7 +527,6 @@ void sendMTFValues(EState* s)
528 } 527 }
529 528
530 /*--- Now the selectors. ---*/ 529 /*--- Now the selectors. ---*/
531 nBytes = s->numZ;
532 bsW(s, 3, nGroups); 530 bsW(s, 3, nGroups);
533 bsW(s, 15, nSelectors); 531 bsW(s, 15, nSelectors);
534 for (i = 0; i < nSelectors; i++) { 532 for (i = 0; i < nSelectors; i++) {
@@ -538,8 +536,6 @@ void sendMTFValues(EState* s)
538 } 536 }
539 537
540 /*--- Now the coding tables. ---*/ 538 /*--- Now the coding tables. ---*/
541 nBytes = s->numZ;
542
543 for (t = 0; t < nGroups; t++) { 539 for (t = 0; t < nGroups; t++) {
544 int32_t curr = s->len[t][0]; 540 int32_t curr = s->len[t][0];
545 bsW(s, 5, curr); 541 bsW(s, 5, curr);
@@ -551,7 +547,6 @@ void sendMTFValues(EState* s)
551 } 547 }
552 548
553 /*--- And finally, the block data proper ---*/ 549 /*--- And finally, the block data proper ---*/
554 nBytes = s->numZ;
555 selCtr = 0; 550 selCtr = 0;
556 gs = 0; 551 gs = 0;
557 while (1) { 552 while (1) {
diff --git a/archival/tar.c b/archival/tar.c
index 52f3c364c..fca2d068b 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -264,7 +264,8 @@ static int writeTarHeader(struct TarBallInfo *tbInfo,
264 PUT_OCTAL(header.uid, statbuf->st_uid); 264 PUT_OCTAL(header.uid, statbuf->st_uid);
265 PUT_OCTAL(header.gid, statbuf->st_gid); 265 PUT_OCTAL(header.gid, statbuf->st_gid);
266 memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */ 266 memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */
267 PUT_OCTAL(header.mtime, statbuf->st_mtime); 267 /* users report that files with negative st_mtime cause trouble, so: */
268 PUT_OCTAL(header.mtime, statbuf->st_mtime >= 0 ? statbuf->st_mtime : 0);
268 269
269 /* Enter the user and group names */ 270 /* Enter the user and group names */
270 safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname)); 271 safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname));
@@ -316,15 +317,42 @@ static int writeTarHeader(struct TarBallInfo *tbInfo,
316 } else if (S_ISFIFO(statbuf->st_mode)) { 317 } else if (S_ISFIFO(statbuf->st_mode)) {
317 header.typeflag = FIFOTYPE; 318 header.typeflag = FIFOTYPE;
318 } else if (S_ISREG(statbuf->st_mode)) { 319 } else if (S_ISREG(statbuf->st_mode)) {
319 if (sizeof(statbuf->st_size) > 4 320 /* header.size field is 12 bytes long */
320 && statbuf->st_size > (off_t)0777777777777LL 321 /* Does octal-encoded size fit? */
322 uoff_t filesize = statbuf->st_size;
323 if (sizeof(filesize) <= 4
324 || filesize <= (uoff_t)0777777777777LL
321 ) { 325 ) {
326 PUT_OCTAL(header.size, filesize);
327 }
328 /* Does base256-encoded size fit?
329 * It always does unless off_t is wider than 64 bits.
330 */
331 else if (ENABLE_FEATURE_TAR_GNU_EXTENSIONS
332#if ULLONG_MAX > 0xffffffffffffffffLL /* 2^64-1 */
333 && (filesize <= 0x3fffffffffffffffffffffffLL)
334#endif
335 ) {
336 /* GNU tar uses "base-256 encoding" for very large numbers.
337 * Encoding is binary, with highest bit always set as a marker
338 * and sign in next-highest bit:
339 * 80 00 .. 00 - zero
340 * bf ff .. ff - largest positive number
341 * ff ff .. ff - minus 1
342 * c0 00 .. 00 - smallest negative number
343 */
344 char *p8 = header.size + sizeof(header.size);
345 do {
346 *--p8 = (uint8_t)filesize;
347 filesize >>= 8;
348 } while (p8 != header.size);
349 *p8 |= 0x80;
350 } else {
322 bb_error_msg_and_die("can't store file '%s' " 351 bb_error_msg_and_die("can't store file '%s' "
323 "of size %"OFF_FMT"u, aborting", 352 "of size %"OFF_FMT"u, aborting",
324 fileName, statbuf->st_size); 353 fileName, statbuf->st_size);
325 } 354 }
326 header.typeflag = REGTYPE; 355 header.typeflag = REGTYPE;
327 PUT_OCTAL(header.size, statbuf->st_size);
328 } else { 356 } else {
329 bb_error_msg("%s: unknown file type", fileName); 357 bb_error_msg("%s: unknown file type", fileName);
330 return FALSE; 358 return FALSE;
diff --git a/console-tools/setconsole.c b/console-tools/setconsole.c
index 59c83361c..771974ae3 100644
--- a/console-tools/setconsole.c
+++ b/console-tools/setconsole.c
@@ -41,6 +41,6 @@ int setconsole_main(int argc UNUSED_PARAM, char **argv)
41 device = DEV_CONSOLE; 41 device = DEV_CONSOLE;
42 } 42 }
43 43
44 xioctl(xopen(device, O_RDONLY), TIOCCONS, NULL); 44 xioctl(xopen(device, O_WRONLY), TIOCCONS, NULL);
45 return EXIT_SUCCESS; 45 return EXIT_SUCCESS;
46} 46}
diff --git a/coreutils/ls.c b/coreutils/ls.c
index 09b9f101a..c967fd462 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -39,7 +39,7 @@
39//usage: IF_FEATURE_LS_SORTFILES("rSXv") 39//usage: IF_FEATURE_LS_SORTFILES("rSXv")
40//usage: IF_FEATURE_LS_TIMESTAMPS("ctu") 40//usage: IF_FEATURE_LS_TIMESTAMPS("ctu")
41//usage: IF_SELINUX("kKZ") "]" 41//usage: IF_SELINUX("kKZ") "]"
42//usage: IF_FEATURE_AUTOWIDTH(" -w WIDTH") " [FILE]..." 42//usage: IF_FEATURE_AUTOWIDTH(" [-w WIDTH]") " [FILE]..."
43//usage:#define ls_full_usage "\n\n" 43//usage:#define ls_full_usage "\n\n"
44//usage: "List directory contents\n" 44//usage: "List directory contents\n"
45//usage: "\nOptions:" 45//usage: "\nOptions:"
@@ -51,14 +51,14 @@
51//usage: "\n -d List directory entries instead of contents" 51//usage: "\n -d List directory entries instead of contents"
52//usage: IF_FEATURE_LS_FOLLOWLINKS( 52//usage: IF_FEATURE_LS_FOLLOWLINKS(
53//usage: "\n -L Follow symlinks" 53//usage: "\n -L Follow symlinks"
54//usage: "\n -H Follow symlinks on command line only" 54//usage: "\n -H Follow symlinks on command line"
55//usage: ) 55//usage: )
56//usage: IF_FEATURE_LS_RECURSIVE( 56//usage: IF_FEATURE_LS_RECURSIVE(
57//usage: "\n -R Recurse" 57//usage: "\n -R Recurse"
58//usage: ) 58//usage: )
59//usage: IF_FEATURE_LS_FILETYPES( 59//usage: IF_FEATURE_LS_FILETYPES(
60//usage: "\n -p Append / to dir entries"
60//usage: "\n -F Append indicator (one of */=@|) to entries" 61//usage: "\n -F Append indicator (one of */=@|) to entries"
61//usage: "\n -p Append indicator (one of /=@|) to entries"
62//usage: ) 62//usage: )
63//usage: "\n -l Long listing format" 63//usage: "\n -l Long listing format"
64//usage: "\n -i List inode numbers" 64//usage: "\n -i List inode numbers"
@@ -118,11 +118,11 @@
118enum { 118enum {
119TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ 119TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
120 120
121SPLIT_DIR = 1,
122SPLIT_FILE = 0, 121SPLIT_FILE = 0,
122SPLIT_DIR = 1,
123SPLIT_SUBDIR = 2, 123SPLIT_SUBDIR = 2,
124 124
125/* Bits in all_fmt: */ 125/* Bits in G.all_fmt: */
126 126
127/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */ 127/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
128/* what file information will be listed */ 128/* what file information will be listed */
@@ -137,9 +137,9 @@ LIST_SIZE = 1 << 7,
137LIST_DATE_TIME = 1 << 8, 137LIST_DATE_TIME = 1 << 8,
138LIST_FULLTIME = 1 << 9, 138LIST_FULLTIME = 1 << 9,
139LIST_SYMLINK = 1 << 10, 139LIST_SYMLINK = 1 << 10,
140LIST_FILETYPE = 1 << 11, 140LIST_FILETYPE = 1 << 11, /* show / suffix for dirs */
141LIST_EXEC = 1 << 12, 141LIST_CLASSIFY = 1 << 12, /* requires LIST_FILETYPE, also show *,|,@,= suffixes */
142LIST_MASK = (LIST_EXEC << 1) - 1, 142LIST_MASK = (LIST_CLASSIFY << 1) - 1,
143 143
144/* what files will be displayed */ 144/* what files will be displayed */
145DISP_DIRNAME = 1 << 13, /* 2 or more items? label directories */ 145DISP_DIRNAME = 1 << 13, /* 2 or more items? label directories */
@@ -288,7 +288,7 @@ static const uint32_t opt_flags[] = {
288 SORT_VERSION, /* v */ 288 SORT_VERSION, /* v */
289#endif 289#endif
290#if ENABLE_FEATURE_LS_FILETYPES 290#if ENABLE_FEATURE_LS_FILETYPES
291 LIST_FILETYPE | LIST_EXEC, /* F */ 291 LIST_FILETYPE | LIST_CLASSIFY, /* F */
292 LIST_FILETYPE, /* p */ 292 LIST_FILETYPE, /* p */
293#endif 293#endif
294#if ENABLE_FEATURE_LS_RECURSIVE 294#if ENABLE_FEATURE_LS_RECURSIVE
@@ -304,25 +304,65 @@ static const uint32_t opt_flags[] = {
304 304
305 305
306/* 306/*
307 * a directory entry and its stat info are stored here 307 * a directory entry and its stat info
308 */ 308 */
309struct dnode { 309struct dnode {
310 const char *name; /* the dir entry name */ 310 const char *name; /* usually basename, but think "ls -l dir/file" */
311 const char *fullname; /* the dir entry name */ 311 const char *fullname; /* full name (usable for stat etc) */
312 struct dnode *next; /* point at the next node */ 312 struct dnode *dn_next; /* for linked list */
313 smallint fname_allocated;
314 struct stat dstat; /* the file stat info */
315 IF_SELINUX(security_context_t sid;) 313 IF_SELINUX(security_context_t sid;)
314 smallint fname_allocated;
315
316 /* Used to avoid re-doing [l]stat at printout stage
317 * if we already collected needed data in scan stage:
318 */
319 mode_t dn_mode_lstat; /* obtained with lstat, or 0 */
320 mode_t dn_mode_stat; /* obtained with stat, or 0 */
321
322// struct stat dstat;
323// struct stat is huge. We don't need it in full.
324// At least we don't need st_dev and st_blksize,
325// but there are invisible fields as well
326// (such as nanosecond-resolution timespamps)
327// and padding, which we also don't want to store.
328// We also can pre-parse dev_t dn_rdev (in glibc, it's huge).
329// On 32-bit uclibc: dnode size went from 112 to 84 bytes.
330//
331 /* Same names as in struct stat, but with dn_ instead of st_ pfx: */
332 mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */
333 off_t dn_size;
334#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
335 time_t dn_atime;
336 time_t dn_mtime;
337 time_t dn_ctime;
338#endif
339 ino_t dn_ino;
340#if !ENABLE_PLATFORM_MINGW32
341 blkcnt_t dn_blocks;
342#endif
343 nlink_t dn_nlink;
344 uid_t dn_uid;
345 gid_t dn_gid;
346 int dn_rdev_maj;
347 int dn_rdev_min;
348// dev_t dn_dev;
349// blksize_t dn_blksize;
316}; 350};
317 351
318struct globals { 352struct globals {
319#if ENABLE_FEATURE_LS_COLOR 353#if ENABLE_FEATURE_LS_COLOR
320 smallint show_color; 354 smallint show_color;
355# define G_show_color (G.show_color)
356#else
357# define G_show_color 0
321#endif 358#endif
322 smallint exit_code; 359 smallint exit_code;
323 unsigned all_fmt; 360 unsigned all_fmt;
324#if ENABLE_FEATURE_AUTOWIDTH 361#if ENABLE_FEATURE_AUTOWIDTH
325 unsigned terminal_width; // = TERMINAL_WIDTH; 362 unsigned terminal_width;
363# define G_terminal_width (G.terminal_width)
364#else
365# define G_terminal_width TERMINAL_WIDTH
326#endif 366#endif
327#if ENABLE_FEATURE_LS_TIMESTAMPS 367#if ENABLE_FEATURE_LS_TIMESTAMPS
328 /* Do time() just once. Saves one syscall per file for "ls -l" */ 368 /* Do time() just once. Saves one syscall per file for "ls -l" */
@@ -330,74 +370,24 @@ struct globals {
330#endif 370#endif
331} FIX_ALIASING; 371} FIX_ALIASING;
332#define G (*(struct globals*)&bb_common_bufsiz1) 372#define G (*(struct globals*)&bb_common_bufsiz1)
333#if ENABLE_FEATURE_LS_COLOR
334# define show_color (G.show_color )
335#else
336enum { show_color = 0 };
337#endif
338#define exit_code (G.exit_code )
339#define all_fmt (G.all_fmt )
340#if ENABLE_FEATURE_AUTOWIDTH
341# define terminal_width (G.terminal_width)
342#else
343enum {
344 terminal_width = TERMINAL_WIDTH,
345};
346#endif
347#define current_time_t (G.current_time_t)
348#define INIT_G() do { \ 373#define INIT_G() do { \
349 /* we have to zero it out because of NOEXEC */ \ 374 /* we have to zero it out because of NOEXEC */ \
350 memset(&G, 0, sizeof(G)); \ 375 memset(&G, 0, sizeof(G)); \
351 IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \ 376 IF_FEATURE_AUTOWIDTH(G_terminal_width = TERMINAL_WIDTH;) \
352 IF_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \ 377 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
353} while (0) 378} while (0)
354 379
355 380
356static struct dnode *my_stat(const char *fullname, const char *name, int force_follow) 381/*** Output code ***/
357{
358 struct stat dstat;
359 struct dnode *cur;
360 IF_SELINUX(security_context_t sid = NULL;)
361 382
362 if ((option_mask32 & OPT_L) || force_follow) {
363#if ENABLE_SELINUX
364 if (is_selinux_enabled()) {
365 getfilecon(fullname, &sid);
366 }
367#endif
368 if (stat(fullname, &dstat)) {
369 bb_simple_perror_msg(fullname);
370 exit_code = EXIT_FAILURE;
371 return 0;
372 }
373 } else {
374#if ENABLE_SELINUX
375 if (is_selinux_enabled()) {
376 lgetfilecon(fullname, &sid);
377 }
378#endif
379 if (lstat(fullname, &dstat)) {
380 bb_simple_perror_msg(fullname);
381 exit_code = EXIT_FAILURE;
382 return 0;
383 }
384 }
385
386 cur = xmalloc(sizeof(*cur));
387 cur->fullname = fullname;
388 cur->name = name;
389 cur->dstat = dstat;
390 IF_SELINUX(cur->sid = sid;)
391 return cur;
392}
393 383
394/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket 384/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
395 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file 385 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
396 * 3/7:multiplexed char/block device) 386 * 3/7:multiplexed char/block device)
397 * and we use 0 for unknown and 15 for executables (see below) */ 387 * and we use 0 for unknown and 15 for executables (see below) */
398#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) 388#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
399#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) 389/* un fi chr - dir - blk - file - link - sock - - exe */
400#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)]) 390#define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)])
401/* 036 black foreground 050 black background 391/* 036 black foreground 050 black background
402 037 red foreground 051 red background 392 037 red foreground 051 red background
403 040 green foreground 052 green background 393 040 green foreground 052 green background
@@ -408,7 +398,7 @@ static struct dnode *my_stat(const char *fullname, const char *name, int force_f
408 045 gray foreground 057 white background 398 045 gray foreground 057 white background
409*/ 399*/
410#define COLOR(mode) ( \ 400#define COLOR(mode) ( \
411 /*un fi chr dir blk file link sock exe */ \ 401 /*un fi chr - dir - blk - file - link - sock - - exe */ \
412 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \ 402 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
413 [TYPEINDEX(mode)]) 403 [TYPEINDEX(mode)])
414/* Select normal (0) [actually "reset all"] or bold (1) 404/* Select normal (0) [actually "reset all"] or bold (1)
@@ -417,7 +407,7 @@ static struct dnode *my_stat(const char *fullname, const char *name, int force_f
417 * Note: coreutils 6.9 uses inverted red for setuid binaries. 407 * Note: coreutils 6.9 uses inverted red for setuid binaries.
418 */ 408 */
419#define ATTR(mode) ( \ 409#define ATTR(mode) ( \
420 /*un fi chr dir blk file link sock exe */ \ 410 /*un fi chr - dir - blk - file- link- sock- - exe */ \
421 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \ 411 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
422 [TYPEINDEX(mode)]) 412 [TYPEINDEX(mode)])
423 413
@@ -437,14 +427,14 @@ static char bold(mode_t mode)
437} 427}
438#endif 428#endif
439 429
440#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR 430#if ENABLE_FEATURE_LS_FILETYPES
441static char append_char(mode_t mode) 431static char append_char(mode_t mode)
442{ 432{
443 if (!(all_fmt & LIST_FILETYPE)) 433 if (!(G.all_fmt & LIST_FILETYPE))
444 return '\0'; 434 return '\0';
445 if (S_ISDIR(mode)) 435 if (S_ISDIR(mode))
446 return '/'; 436 return '/';
447 if (!(all_fmt & LIST_EXEC)) 437 if (!(G.all_fmt & LIST_CLASSIFY))
448 return '\0'; 438 return '\0';
449 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) 439 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
450 return '*'; 440 return '*';
@@ -452,149 +442,6 @@ static char append_char(mode_t mode)
452} 442}
453#endif 443#endif
454 444
455static unsigned count_dirs(struct dnode **dn, int which)
456{
457 unsigned dirs, all;
458
459 if (!dn)
460 return 0;
461
462 dirs = all = 0;
463 for (; *dn; dn++) {
464 const char *name;
465
466 all++;
467 if (!S_ISDIR((*dn)->dstat.st_mode))
468 continue;
469 name = (*dn)->name;
470 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
471 /* or if it's not . or .. */
472 || name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))
473 ) {
474 dirs++;
475 }
476 }
477 return which != SPLIT_FILE ? dirs : all - dirs;
478}
479
480/* get memory to hold an array of pointers */
481static struct dnode **dnalloc(unsigned num)
482{
483 if (num < 1)
484 return NULL;
485
486 num++; /* so that we have terminating NULL */
487 return xzalloc(num * sizeof(struct dnode *));
488}
489
490#if ENABLE_FEATURE_LS_RECURSIVE
491static void dfree(struct dnode **dnp)
492{
493 unsigned i;
494
495 if (dnp == NULL)
496 return;
497
498 for (i = 0; dnp[i]; i++) {
499 struct dnode *cur = dnp[i];
500 if (cur->fname_allocated)
501 free((char*)cur->fullname);
502 free(cur);
503 }
504 free(dnp);
505}
506#else
507#define dfree(...) ((void)0)
508#endif
509
510/* Returns NULL-terminated malloced vector of pointers (or NULL) */
511static struct dnode **splitdnarray(struct dnode **dn, int which)
512{
513 unsigned dncnt, d;
514 struct dnode **dnp;
515
516 if (dn == NULL)
517 return NULL;
518
519 /* count how many dirs or files there are */
520 dncnt = count_dirs(dn, which);
521
522 /* allocate a file array and a dir array */
523 dnp = dnalloc(dncnt);
524
525 /* copy the entrys into the file or dir array */
526 for (d = 0; *dn; dn++) {
527 if (S_ISDIR((*dn)->dstat.st_mode)) {
528 const char *name;
529
530 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
531 continue;
532 name = (*dn)->name;
533 if ((which & SPLIT_DIR)
534 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
535 ) {
536 dnp[d++] = *dn;
537 }
538 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
539 dnp[d++] = *dn;
540 }
541 }
542 return dnp;
543}
544
545#if ENABLE_FEATURE_LS_SORTFILES
546static int sortcmp(const void *a, const void *b)
547{
548 struct dnode *d1 = *(struct dnode **)a;
549 struct dnode *d2 = *(struct dnode **)b;
550 unsigned sort_opts = all_fmt & SORT_MASK;
551 off_t dif;
552
553 dif = 0; /* assume SORT_NAME */
554 // TODO: use pre-initialized function pointer
555 // instead of branch forest
556 if (sort_opts == SORT_SIZE) {
557 dif = (d2->dstat.st_size - d1->dstat.st_size);
558 } else if (sort_opts == SORT_ATIME) {
559 dif = (d2->dstat.st_atime - d1->dstat.st_atime);
560 } else if (sort_opts == SORT_CTIME) {
561 dif = (d2->dstat.st_ctime - d1->dstat.st_ctime);
562 } else if (sort_opts == SORT_MTIME) {
563 dif = (d2->dstat.st_mtime - d1->dstat.st_mtime);
564 } else if (sort_opts == SORT_DIR) {
565 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
566 /* } else if (sort_opts == SORT_VERSION) { */
567 /* } else if (sort_opts == SORT_EXT) { */
568 }
569 if (dif == 0) {
570 /* sort by name, or tie_breaker for other sorts */
571 if (ENABLE_LOCALE_SUPPORT)
572 dif = strcoll(d1->name, d2->name);
573 else
574 dif = strcmp(d1->name, d2->name);
575 }
576
577 /* Make dif fit into an int */
578 if (sizeof(dif) > sizeof(int)) {
579 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
580 /* shift leaving only "int" worth of bits */
581 if (dif != 0) {
582 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
583 }
584 }
585
586 return (all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
587}
588
589static void dnsort(struct dnode **dn, int size)
590{
591 qsort(dn, size, sizeof(*dn), sortcmp);
592}
593#else
594#define dnsort(dn, size) ((void)0)
595#endif
596
597
598static unsigned calc_name_len(const char *name) 445static unsigned calc_name_len(const char *name)
599{ 446{
600 unsigned len; 447 unsigned len;
@@ -617,7 +464,6 @@ static unsigned calc_name_len(const char *name)
617 return len; 464 return len;
618} 465}
619 466
620
621/* Return the number of used columns. 467/* Return the number of used columns.
622 * Note that only STYLE_COLUMNAR uses return value. 468 * Note that only STYLE_COLUMNAR uses return value.
623 * STYLE_SINGLE and STYLE_LONG don't care. 469 * STYLE_SINGLE and STYLE_LONG don't care.
@@ -656,98 +502,94 @@ static unsigned print_name(const char *name)
656 * Note that only STYLE_COLUMNAR uses return value, 502 * Note that only STYLE_COLUMNAR uses return value,
657 * STYLE_SINGLE and STYLE_LONG don't care. 503 * STYLE_SINGLE and STYLE_LONG don't care.
658 */ 504 */
659static NOINLINE unsigned list_single(const struct dnode *dn) 505static NOINLINE unsigned display_single(const struct dnode *dn)
660{ 506{
661 unsigned column = 0; 507 unsigned column = 0;
662 char *lpath = lpath; /* for compiler */ 508 char *lpath;
663#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR 509#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
664 struct stat info; 510 struct stat statbuf;
665 char append; 511 char append;
666#endif 512#endif
667 513
668 /* Never happens:
669 if (dn->fullname == NULL)
670 return 0;
671 */
672
673#if ENABLE_FEATURE_LS_FILETYPES 514#if ENABLE_FEATURE_LS_FILETYPES
674 append = append_char(dn->dstat.st_mode); 515 append = append_char(dn->dn_mode);
675#endif 516#endif
676 517
677 /* Do readlink early, so that if it fails, error message 518 /* Do readlink early, so that if it fails, error message
678 * does not appear *inside* the "ls -l" line */ 519 * does not appear *inside* the "ls -l" line */
679 if (all_fmt & LIST_SYMLINK) 520 lpath = NULL;
680 if (S_ISLNK(dn->dstat.st_mode)) 521 if (G.all_fmt & LIST_SYMLINK)
522 if (S_ISLNK(dn->dn_mode))
681 lpath = xmalloc_readlink_or_warn(dn->fullname); 523 lpath = xmalloc_readlink_or_warn(dn->fullname);
682 524
683 if (all_fmt & LIST_INO) 525 if (G.all_fmt & LIST_INO)
684 column += printf("%7llu ", (long long) dn->dstat.st_ino); 526 column += printf("%7llu ", (long long) dn->dn_ino);
685//TODO: -h should affect -s too: 527//TODO: -h should affect -s too:
686 if (all_fmt & LIST_BLOCKS) 528 if (G.all_fmt & LIST_BLOCKS)
687#if ENABLE_PLATFORM_MINGW32 529#if ENABLE_PLATFORM_MINGW32
688 /* MinGW does not have st_blocks */ 530 /* MinGW does not have st_blocks */
689 column += printf("%6"OFF_FMT"u ", (off_t)0); 531 column += printf("%6"OFF_FMT"u ", (off_t)0);
690#else 532#else
691 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1)); 533 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
692#endif 534#endif
693 if (all_fmt & LIST_MODEBITS) 535 if (G.all_fmt & LIST_MODEBITS)
694 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode)); 536 column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode));
695 if (all_fmt & LIST_NLINKS) 537 if (G.all_fmt & LIST_NLINKS)
696 column += printf("%4lu ", (long) dn->dstat.st_nlink); 538 column += printf("%4lu ", (long) dn->dn_nlink);
697 if (all_fmt & LIST_ID_NUMERIC) { 539 if (G.all_fmt & LIST_ID_NUMERIC) {
698 if (option_mask32 & OPT_g) 540 if (option_mask32 & OPT_g)
699 column += printf("%-8u ", (int) dn->dstat.st_gid); 541 column += printf("%-8u ", (int) dn->dn_gid);
700 else 542 else
701 column += printf("%-8u %-8u ", 543 column += printf("%-8u %-8u ",
702 (int) dn->dstat.st_uid, 544 (int) dn->dn_uid,
703 (int) dn->dstat.st_gid); 545 (int) dn->dn_gid);
704 } 546 }
705#if ENABLE_FEATURE_LS_USERNAME 547#if ENABLE_FEATURE_LS_USERNAME
706 else if (all_fmt & LIST_ID_NAME) { 548 else if (G.all_fmt & LIST_ID_NAME) {
707 if (option_mask32 & OPT_g) { 549 if (option_mask32 & OPT_g) {
708 column += printf("%-8.8s ", 550 column += printf("%-8.8s ",
709 get_cached_groupname(dn->dstat.st_gid)); 551 get_cached_groupname(dn->dn_gid));
710 } else { 552 } else {
711 column += printf("%-8.8s %-8.8s ", 553 column += printf("%-8.8s %-8.8s ",
712 get_cached_username(dn->dstat.st_uid), 554 get_cached_username(dn->dn_uid),
713 get_cached_groupname(dn->dstat.st_gid)); 555 get_cached_groupname(dn->dn_gid));
714 } 556 }
715 } 557 }
716#endif 558#endif
717 if (all_fmt & LIST_SIZE) { 559 if (G.all_fmt & LIST_SIZE) {
718 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) { 560 if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
719 column += printf("%4u, %3u ", 561 column += printf("%4u, %3u ",
720 (int) major(dn->dstat.st_rdev), 562 dn->dn_rdev_maj,
721 (int) minor(dn->dstat.st_rdev)); 563 dn->dn_rdev_min);
722 } else { 564 } else {
723 if (option_mask32 & OPT_h) { 565 if (option_mask32 & OPT_h) {
724 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ", 566 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
725 /* print st_size, show one fractional, use suffixes */ 567 /* print size, show one fractional, use suffixes */
726 make_human_readable_str(dn->dstat.st_size, 1, 0) 568 make_human_readable_str(dn->dn_size, 1, 0)
727 ); 569 );
728 } else { 570 } else {
729 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size); 571 column += printf("%9"OFF_FMT"u ", dn->dn_size);
730 } 572 }
731 } 573 }
732 } 574 }
733#if ENABLE_FEATURE_LS_TIMESTAMPS 575#if ENABLE_FEATURE_LS_TIMESTAMPS
734 if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) { 576 if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
735 char *filetime; 577 char *filetime;
736 time_t ttime = dn->dstat.st_mtime; 578 time_t ttime = dn->dn_mtime;
737 if (all_fmt & TIME_ACCESS) 579 if (G.all_fmt & TIME_ACCESS)
738 ttime = dn->dstat.st_atime; 580 ttime = dn->dn_atime;
739 if (all_fmt & TIME_CHANGE) 581 if (G.all_fmt & TIME_CHANGE)
740 ttime = dn->dstat.st_ctime; 582 ttime = dn->dn_ctime;
741 filetime = ctime(&ttime); 583 filetime = ctime(&ttime);
742 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */ 584 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
743 if (all_fmt & LIST_FULLTIME) { /* -e */ 585 if (G.all_fmt & LIST_FULLTIME) { /* -e */
744 /* Note: coreutils 8.4 ls --full-time prints: 586 /* Note: coreutils 8.4 ls --full-time prints:
745 * 2009-07-13 17:49:27.000000000 +0200 587 * 2009-07-13 17:49:27.000000000 +0200
746 */ 588 */
747 column += printf("%.24s ", filetime); 589 column += printf("%.24s ", filetime);
748 } else { /* LIST_DATE_TIME */ 590 } else { /* LIST_DATE_TIME */
749 /* current_time_t ~== time(NULL) */ 591 /* G.current_time_t ~== time(NULL) */
750 time_t age = current_time_t - ttime; 592 time_t age = G.current_time_t - ttime;
751 printf("%.6s ", filetime + 4); /* "Jun 30" */ 593 printf("%.6s ", filetime + 4); /* "Jun 30" */
752 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { 594 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
753 /* hh:mm if less than 6 months old */ 595 /* hh:mm if less than 6 months old */
@@ -760,51 +602,52 @@ static NOINLINE unsigned list_single(const struct dnode *dn)
760 } 602 }
761#endif 603#endif
762#if ENABLE_SELINUX 604#if ENABLE_SELINUX
763 if (all_fmt & LIST_CONTEXT) { 605 if (G.all_fmt & LIST_CONTEXT) {
764 column += printf("%-32s ", dn->sid ? dn->sid : "unknown"); 606 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
765 freecon(dn->sid); 607 freecon(dn->sid);
766 } 608 }
767#endif 609#endif
768 610
769#if ENABLE_FEATURE_LS_COLOR 611#if ENABLE_FEATURE_LS_COLOR
770 if (show_color) { 612 if (G_show_color) {
771 info.st_mode = 0; /* for fgcolor() */ 613 mode_t mode = dn->dn_mode_lstat;
772 lstat(dn->fullname, &info); 614 if (!mode)
773 printf("\033[%u;%um", bold(info.st_mode), 615 if (lstat(dn->fullname, &statbuf) == 0)
774 fgcolor(info.st_mode)); 616 mode = statbuf.st_mode;
617 printf("\033[%u;%um", bold(mode), fgcolor(mode));
775 } 618 }
776#endif 619#endif
777 column += print_name(dn->name); 620 column += print_name(dn->name);
778 if (show_color) { 621 if (G_show_color) {
779 printf("\033[0m"); 622 printf("\033[0m");
780 } 623 }
781 624
782 if (all_fmt & LIST_SYMLINK) { 625 if (lpath) {
783 if (S_ISLNK(dn->dstat.st_mode) && lpath) { 626 printf(" -> ");
784 printf(" -> ");
785#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR 627#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
786#if ENABLE_FEATURE_LS_COLOR 628 if ((G.all_fmt & LIST_FILETYPE) || G_show_color) {
787 info.st_mode = 0; /* for fgcolor() */ 629 mode_t mode = dn->dn_mode_stat;
788#endif 630 if (!mode)
789 if (stat(dn->fullname, &info) == 0) { 631 if (stat(dn->fullname, &statbuf) == 0)
790 append = append_char(info.st_mode); 632 mode = statbuf.st_mode;
791 } 633# if ENABLE_FEATURE_LS_FILETYPES
792#endif 634 append = append_char(mode);
793#if ENABLE_FEATURE_LS_COLOR 635# endif
794 if (show_color) { 636# if ENABLE_FEATURE_LS_COLOR
795 printf("\033[%u;%um", bold(info.st_mode), 637 if (G_show_color) {
796 fgcolor(info.st_mode)); 638 printf("\033[%u;%um", bold(mode), fgcolor(mode));
797 } 639 }
640# endif
641 }
798#endif 642#endif
799 column += print_name(lpath) + 4; 643 column += print_name(lpath) + 4;
800 if (show_color) { 644 free(lpath);
801 printf("\033[0m"); 645 if (G_show_color) {
802 } 646 printf("\033[0m");
803 free(lpath);
804 } 647 }
805 } 648 }
806#if ENABLE_FEATURE_LS_FILETYPES 649#if ENABLE_FEATURE_LS_FILETYPES
807 if (all_fmt & LIST_FILETYPE) { 650 if (G.all_fmt & LIST_FILETYPE) {
808 if (append) { 651 if (append) {
809 putchar(append); 652 putchar(append);
810 column++; 653 column++;
@@ -815,14 +658,14 @@ static NOINLINE unsigned list_single(const struct dnode *dn)
815 return column; 658 return column;
816} 659}
817 660
818static void showfiles(struct dnode **dn, unsigned nfiles) 661static void display_files(struct dnode **dn, unsigned nfiles)
819{ 662{
820 unsigned i, ncols, nrows, row, nc; 663 unsigned i, ncols, nrows, row, nc;
821 unsigned column; 664 unsigned column;
822 unsigned nexttab; 665 unsigned nexttab;
823 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */ 666 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
824 667
825 if (all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */ 668 if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
826 ncols = 1; 669 ncols = 1;
827 } else { 670 } else {
828 /* find the longest file name, use that as the column width */ 671 /* find the longest file name, use that as the column width */
@@ -832,10 +675,10 @@ static void showfiles(struct dnode **dn, unsigned nfiles)
832 column_width = len; 675 column_width = len;
833 } 676 }
834 column_width += 1 + 677 column_width += 1 +
835 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + ) 678 IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + )
836 ((all_fmt & LIST_INO) ? 8 : 0) + 679 ((G.all_fmt & LIST_INO) ? 8 : 0) +
837 ((all_fmt & LIST_BLOCKS) ? 5 : 0); 680 ((G.all_fmt & LIST_BLOCKS) ? 5 : 0);
838 ncols = (int) (terminal_width / column_width); 681 ncols = (unsigned)G_terminal_width / column_width;
839 } 682 }
840 683
841 if (ncols > 1) { 684 if (ncols > 1) {
@@ -852,7 +695,7 @@ static void showfiles(struct dnode **dn, unsigned nfiles)
852 for (row = 0; row < nrows; row++) { 695 for (row = 0; row < nrows; row++) {
853 for (nc = 0; nc < ncols; nc++) { 696 for (nc = 0; nc < ncols; nc++) {
854 /* reach into the array based on the column and row */ 697 /* reach into the array based on the column and row */
855 if (all_fmt & DISP_ROWS) 698 if (G.all_fmt & DISP_ROWS)
856 i = (row * ncols) + nc; /* display across row */ 699 i = (row * ncols) + nc; /* display across row */
857 else 700 else
858 i = (nc * nrows) + row; /* display by column */ 701 i = (nc * nrows) + row; /* display by column */
@@ -863,7 +706,7 @@ static void showfiles(struct dnode **dn, unsigned nfiles)
863 column += nexttab + 1; 706 column += nexttab + 1;
864 } 707 }
865 nexttab = column + column_width; 708 nexttab = column + column_width;
866 column += list_single(dn[i]); 709 column += display_single(dn[i]);
867 } 710 }
868 } 711 }
869 putchar('\n'); 712 putchar('\n');
@@ -872,102 +715,233 @@ static void showfiles(struct dnode **dn, unsigned nfiles)
872} 715}
873 716
874 717
875#if !ENABLE_PLATFORM_MINGW32 718/*** Dir scanning code ***/
876#if ENABLE_DESKTOP 719
877/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html 720static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
878 * If any of the -l, -n, -s options is specified, each list
879 * of files within the directory shall be preceded by a
880 * status line indicating the number of file system blocks
881 * occupied by files in the directory in 512-byte units if
882 * the -k option is not specified, or 1024-byte units if the
883 * -k option is specified, rounded up to the next integral
884 * number of units.
885 */
886/* by Jorgen Overgaard (jorgen AT antistaten.se) */
887static off_t calculate_blocks(struct dnode **dn)
888{ 721{
889 uoff_t blocks = 1; 722 struct stat statbuf;
890 if (dn) { 723 struct dnode *cur;
891 while (*dn) { 724
892 /* st_blocks is in 512 byte blocks */ 725 cur = xzalloc(sizeof(*cur));
893 blocks += (*dn)->dstat.st_blocks; 726 cur->fullname = fullname;
894 dn++; 727 cur->name = name;
728
729 if ((option_mask32 & OPT_L) || force_follow) {
730#if ENABLE_SELINUX
731 if (is_selinux_enabled()) {
732 getfilecon(fullname, &cur->sid);
733 }
734#endif
735 if (stat(fullname, &statbuf)) {
736 bb_simple_perror_msg(fullname);
737 G.exit_code = EXIT_FAILURE;
738 free(cur);
739 return NULL;
740 }
741 cur->dn_mode_stat = statbuf.st_mode;
742 } else {
743#if ENABLE_SELINUX
744 if (is_selinux_enabled()) {
745 lgetfilecon(fullname, &cur->sid);
746 }
747#endif
748 if (lstat(fullname, &statbuf)) {
749 bb_simple_perror_msg(fullname);
750 G.exit_code = EXIT_FAILURE;
751 free(cur);
752 return NULL;
895 } 753 }
754 cur->dn_mode_lstat = statbuf.st_mode;
896 } 755 }
897 756
898 /* Even though standard says use 512 byte blocks, coreutils use 1k */ 757 /* cur->dstat = statbuf: */
899 /* Actually, we round up by calculating (blocks + 1) / 2, 758 cur->dn_mode = statbuf.st_mode ;
900 * "+ 1" was done when we initialized blocks to 1 */ 759 cur->dn_size = statbuf.st_size ;
901 return blocks >> 1; 760#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
902} 761 cur->dn_atime = statbuf.st_atime ;
762 cur->dn_mtime = statbuf.st_mtime ;
763 cur->dn_ctime = statbuf.st_ctime ;
903#endif 764#endif
765 cur->dn_ino = statbuf.st_ino ;
766#if !ENABLE_PLATFORM_MINGW32
767 cur->dn_blocks = statbuf.st_blocks;
904#endif 768#endif
769 cur->dn_nlink = statbuf.st_nlink ;
770 cur->dn_uid = statbuf.st_uid ;
771 cur->dn_gid = statbuf.st_gid ;
772 cur->dn_rdev_maj = major(statbuf.st_rdev);
773 cur->dn_rdev_min = minor(statbuf.st_rdev);
905 774
775 return cur;
776}
906 777
907static struct dnode **list_dir(const char *, unsigned *); 778static unsigned count_dirs(struct dnode **dn, int which)
908
909static void showdirs(struct dnode **dn, int first)
910{ 779{
911 unsigned nfiles; 780 unsigned dirs, all;
912 unsigned dndirs;
913 struct dnode **subdnp;
914 struct dnode **dnd;
915 781
782 if (!dn)
783 return 0;
784
785 dirs = all = 0;
916 for (; *dn; dn++) { 786 for (; *dn; dn++) {
917 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) { 787 const char *name;
918 if (!first) 788
919 bb_putchar('\n'); 789 all++;
920 first = 0; 790 if (!S_ISDIR((*dn)->dn_mode))
921 printf("%s:\n", (*dn)->fullname); 791 continue;
792
793 name = (*dn)->name;
794 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
795 /* or if it's not . or .. */
796 || name[0] != '.'
797 || (name[1] && (name[1] != '.' || name[2]))
798 ) {
799 dirs++;
922 } 800 }
923 subdnp = list_dir((*dn)->fullname, &nfiles); 801 }
924#if !ENABLE_PLATFORM_MINGW32 802 return which != SPLIT_FILE ? dirs : all - dirs;
925#if ENABLE_DESKTOP 803}
926 if ((all_fmt & STYLE_MASK) == STYLE_LONG) 804
927 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp)); 805/* get memory to hold an array of pointers */
928#endif 806static struct dnode **dnalloc(unsigned num)
807{
808 if (num < 1)
809 return NULL;
810
811 num++; /* so that we have terminating NULL */
812 return xzalloc(num * sizeof(struct dnode *));
813}
814
815#if ENABLE_FEATURE_LS_RECURSIVE
816static void dfree(struct dnode **dnp)
817{
818 unsigned i;
819
820 if (dnp == NULL)
821 return;
822
823 for (i = 0; dnp[i]; i++) {
824 struct dnode *cur = dnp[i];
825 if (cur->fname_allocated)
826 free((char*)cur->fullname);
827 free(cur);
828 }
829 free(dnp);
830}
831#else
832#define dfree(...) ((void)0)
929#endif 833#endif
930 if (nfiles > 0) { 834
931 /* list all files at this level */ 835/* Returns NULL-terminated malloced vector of pointers (or NULL) */
932 dnsort(subdnp, nfiles); 836static struct dnode **splitdnarray(struct dnode **dn, int which)
933 showfiles(subdnp, nfiles); 837{
934 if (ENABLE_FEATURE_LS_RECURSIVE 838 unsigned dncnt, d;
935 && (all_fmt & DISP_RECURSIVE) 839 struct dnode **dnp;
840
841 if (dn == NULL)
842 return NULL;
843
844 /* count how many dirs or files there are */
845 dncnt = count_dirs(dn, which);
846
847 /* allocate a file array and a dir array */
848 dnp = dnalloc(dncnt);
849
850 /* copy the entrys into the file or dir array */
851 for (d = 0; *dn; dn++) {
852 if (S_ISDIR((*dn)->dn_mode)) {
853 const char *name;
854
855 if (which == SPLIT_FILE)
856 continue;
857
858 name = (*dn)->name;
859 if ((which & SPLIT_DIR) /* any dir... */
860 /* ... or not . or .. */
861 || name[0] != '.'
862 || (name[1] && (name[1] != '.' || name[2]))
936 ) { 863 ) {
937 /* recursive - list the sub-dirs */ 864 dnp[d++] = *dn;
938 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
939 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
940 if (dndirs > 0) {
941 dnsort(dnd, dndirs);
942 showdirs(dnd, 0);
943 /* free the array of dnode pointers to the dirs */
944 free(dnd);
945 }
946 } 865 }
947 /* free the dnodes and the fullname mem */ 866 } else
948 dfree(subdnp); 867 if (which == SPLIT_FILE) {
868 dnp[d++] = *dn;
949 } 869 }
950 } 870 }
871 return dnp;
951} 872}
952 873
874#if ENABLE_FEATURE_LS_SORTFILES
875static int sortcmp(const void *a, const void *b)
876{
877 struct dnode *d1 = *(struct dnode **)a;
878 struct dnode *d2 = *(struct dnode **)b;
879 unsigned sort_opts = G.all_fmt & SORT_MASK;
880 off_t dif;
881
882 dif = 0; /* assume SORT_NAME */
883 // TODO: use pre-initialized function pointer
884 // instead of branch forest
885 if (sort_opts == SORT_SIZE) {
886 dif = (d2->dn_size - d1->dn_size);
887 } else if (sort_opts == SORT_ATIME) {
888 dif = (d2->dn_atime - d1->dn_atime);
889 } else if (sort_opts == SORT_CTIME) {
890 dif = (d2->dn_ctime - d1->dn_ctime);
891 } else if (sort_opts == SORT_MTIME) {
892 dif = (d2->dn_mtime - d1->dn_mtime);
893 } else if (sort_opts == SORT_DIR) {
894 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
895 /* } else if (sort_opts == SORT_VERSION) { */
896 /* } else if (sort_opts == SORT_EXT) { */
897 }
898 if (dif == 0) {
899 /* sort by name, or tie_breaker for other sorts */
900 if (ENABLE_LOCALE_SUPPORT)
901 dif = strcoll(d1->name, d2->name);
902 else
903 dif = strcmp(d1->name, d2->name);
904 }
905
906 /* Make dif fit into an int */
907 if (sizeof(dif) > sizeof(int)) {
908 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
909 /* shift leaving only "int" worth of bits */
910 if (dif != 0) {
911 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
912 }
913 }
914
915 return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
916}
917
918static void dnsort(struct dnode **dn, int size)
919{
920 qsort(dn, size, sizeof(*dn), sortcmp);
921}
922
923static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
924{
925 dnsort(dn, nfiles);
926 display_files(dn, nfiles);
927}
928#else
929# define dnsort(dn, size) ((void)0)
930# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
931#endif
953 932
954/* Returns NULL-terminated malloced vector of pointers (or NULL) */ 933/* Returns NULL-terminated malloced vector of pointers (or NULL) */
955static struct dnode **list_dir(const char *path, unsigned *nfiles_p) 934static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
956{ 935{
957 struct dnode *dn, *cur, **dnp; 936 struct dnode *dn, *cur, **dnp;
958 struct dirent *entry; 937 struct dirent *entry;
959 DIR *dir; 938 DIR *dir;
960 unsigned i, nfiles; 939 unsigned i, nfiles;
961 940
962 /* Never happens:
963 if (path == NULL)
964 return NULL;
965 */
966
967 *nfiles_p = 0; 941 *nfiles_p = 0;
968 dir = warn_opendir(path); 942 dir = warn_opendir(path);
969 if (dir == NULL) { 943 if (dir == NULL) {
970 exit_code = EXIT_FAILURE; 944 G.exit_code = EXIT_FAILURE;
971 return NULL; /* could not open the dir */ 945 return NULL; /* could not open the dir */
972 } 946 }
973 dn = NULL; 947 dn = NULL;
@@ -978,11 +952,11 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
978 /* are we going to list the file- it may be . or .. or a hidden file */ 952 /* are we going to list the file- it may be . or .. or a hidden file */
979 if (entry->d_name[0] == '.') { 953 if (entry->d_name[0] == '.') {
980 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2])) 954 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
981 && !(all_fmt & DISP_DOT) 955 && !(G.all_fmt & DISP_DOT)
982 ) { 956 ) {
983 continue; 957 continue;
984 } 958 }
985 if (!(all_fmt & DISP_HIDDEN)) 959 if (!(G.all_fmt & DISP_HIDDEN))
986 continue; 960 continue;
987 } 961 }
988 fullname = concat_path_file(path, entry->d_name); 962 fullname = concat_path_file(path, entry->d_name);
@@ -992,7 +966,7 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
992 continue; 966 continue;
993 } 967 }
994 cur->fname_allocated = 1; 968 cur->fname_allocated = 1;
995 cur->next = dn; 969 cur->dn_next = dn;
996 dn = cur; 970 dn = cur;
997 nfiles++; 971 nfiles++;
998 } 972 }
@@ -1008,7 +982,7 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
1008 dnp = dnalloc(nfiles); 982 dnp = dnalloc(nfiles);
1009 for (i = 0; /* i < nfiles - detected via !dn below */; i++) { 983 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1010 dnp[i] = dn; /* save pointer to node in array */ 984 dnp[i] = dn; /* save pointer to node in array */
1011 dn = dn->next; 985 dn = dn->dn_next;
1012 if (!dn) 986 if (!dn)
1013 break; 987 break;
1014 } 988 }
@@ -1016,6 +990,77 @@ static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
1016 return dnp; 990 return dnp;
1017} 991}
1018 992
993#if ENABLE_DESKTOP && !ENABLE_PLATFORM_MINGW32
994/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
995 * If any of the -l, -n, -s options is specified, each list
996 * of files within the directory shall be preceded by a
997 * status line indicating the number of file system blocks
998 * occupied by files in the directory in 512-byte units if
999 * the -k option is not specified, or 1024-byte units if the
1000 * -k option is specified, rounded up to the next integral
1001 * number of units.
1002 */
1003/* by Jorgen Overgaard (jorgen AT antistaten.se) */
1004static off_t calculate_blocks(struct dnode **dn)
1005{
1006 uoff_t blocks = 1;
1007 if (dn) {
1008 while (*dn) {
1009 /* st_blocks is in 512 byte blocks */
1010 blocks += (*dn)->dn_blocks;
1011 dn++;
1012 }
1013 }
1014
1015 /* Even though standard says use 512 byte blocks, coreutils use 1k */
1016 /* Actually, we round up by calculating (blocks + 1) / 2,
1017 * "+ 1" was done when we initialized blocks to 1 */
1018 return blocks >> 1;
1019}
1020#endif
1021
1022static void scan_and_display_dirs_recur(struct dnode **dn, int first)
1023{
1024 unsigned nfiles;
1025 struct dnode **subdnp;
1026
1027 for (; *dn; dn++) {
1028 if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
1029 if (!first)
1030 bb_putchar('\n');
1031 first = 0;
1032 printf("%s:\n", (*dn)->fullname);
1033 }
1034 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
1035#if ENABLE_DESKTOP && !ENABLE_PLATFORM_MINGW32
1036 if ((G.all_fmt & STYLE_MASK) == STYLE_LONG)
1037 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
1038#endif
1039 if (nfiles > 0) {
1040 /* list all files at this level */
1041 sort_and_display_files(subdnp, nfiles);
1042
1043 if (ENABLE_FEATURE_LS_RECURSIVE
1044 && (G.all_fmt & DISP_RECURSIVE)
1045 ) {
1046 struct dnode **dnd;
1047 unsigned dndirs;
1048 /* recursive - list the sub-dirs */
1049 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1050 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1051 if (dndirs > 0) {
1052 dnsort(dnd, dndirs);
1053 scan_and_display_dirs_recur(dnd, 0);
1054 /* free the array of dnode pointers to the dirs */
1055 free(dnd);
1056 }
1057 }
1058 /* free the dnodes and the fullname mem */
1059 dfree(subdnp);
1060 }
1061 }
1062}
1063
1019 1064
1020int ls_main(int argc UNUSED_PARAM, char **argv) 1065int ls_main(int argc UNUSED_PARAM, char **argv)
1021{ 1066{
@@ -1054,13 +1099,13 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1054 init_unicode(); 1099 init_unicode();
1055 1100
1056 if (ENABLE_FEATURE_LS_SORTFILES) 1101 if (ENABLE_FEATURE_LS_SORTFILES)
1057 all_fmt = SORT_NAME; 1102 G.all_fmt = SORT_NAME;
1058 1103
1059#if ENABLE_FEATURE_AUTOWIDTH 1104#if ENABLE_FEATURE_AUTOWIDTH
1060 /* obtain the terminal width */ 1105 /* obtain the terminal width */
1061 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL); 1106 get_terminal_width_height(STDIN_FILENO, &G_terminal_width, NULL);
1062 /* go one less... */ 1107 /* go one less... */
1063 terminal_width--; 1108 G_terminal_width--;
1064#endif 1109#endif
1065 1110
1066 /* process options */ 1111 /* process options */
@@ -1081,7 +1126,7 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1081 /* -w NUM: */ 1126 /* -w NUM: */
1082 IF_FEATURE_AUTOWIDTH(":w+"); 1127 IF_FEATURE_AUTOWIDTH(":w+");
1083 opt = getopt32(argv, ls_options 1128 opt = getopt32(argv, ls_options
1084 IF_FEATURE_AUTOWIDTH(, NULL, &terminal_width) 1129 IF_FEATURE_AUTOWIDTH(, NULL, &G_terminal_width)
1085 IF_FEATURE_LS_COLOR(, &color_opt) 1130 IF_FEATURE_LS_COLOR(, &color_opt)
1086 ); 1131 );
1087 for (i = 0; opt_flags[i] != (1U << 31); i++) { 1132 for (i = 0; opt_flags[i] != (1U << 31); i++) {
@@ -1089,27 +1134,27 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1089 uint32_t flags = opt_flags[i]; 1134 uint32_t flags = opt_flags[i];
1090 1135
1091 if (flags & STYLE_MASK) 1136 if (flags & STYLE_MASK)
1092 all_fmt &= ~STYLE_MASK; 1137 G.all_fmt &= ~STYLE_MASK;
1093 if (flags & SORT_MASK) 1138 if (flags & SORT_MASK)
1094 all_fmt &= ~SORT_MASK; 1139 G.all_fmt &= ~SORT_MASK;
1095 if (flags & TIME_MASK) 1140 if (flags & TIME_MASK)
1096 all_fmt &= ~TIME_MASK; 1141 G.all_fmt &= ~TIME_MASK;
1097 1142
1098 all_fmt |= flags; 1143 G.all_fmt |= flags;
1099 } 1144 }
1100 } 1145 }
1101 1146
1102#if ENABLE_FEATURE_LS_COLOR 1147#if ENABLE_FEATURE_LS_COLOR
1103 /* set show_color = 1/0 */ 1148 /* set G_show_color = 1/0 */
1104 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) { 1149 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1105 char *p = getenv("LS_COLORS"); 1150 char *p = getenv("LS_COLORS");
1106 /* LS_COLORS is unset, or (not empty && not "none") ? */ 1151 /* LS_COLORS is unset, or (not empty && not "none") ? */
1107 if (!p || (p[0] && strcmp(p, "none") != 0)) 1152 if (!p || (p[0] && strcmp(p, "none") != 0))
1108 show_color = 1; 1153 G_show_color = 1;
1109 } 1154 }
1110 if (opt & OPT_color) { 1155 if (opt & OPT_color) {
1111 if (color_opt[0] == 'n') 1156 if (color_opt[0] == 'n')
1112 show_color = 0; 1157 G_show_color = 0;
1113 else switch (index_in_substrings(color_str, color_opt)) { 1158 else switch (index_in_substrings(color_str, color_opt)) {
1114 case 3: 1159 case 3:
1115 case 4: 1160 case 4:
@@ -1118,34 +1163,34 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1118 case 0: 1163 case 0:
1119 case 1: 1164 case 1:
1120 case 2: 1165 case 2:
1121 show_color = 1; 1166 G_show_color = 1;
1122 } 1167 }
1123 } 1168 }
1124 } 1169 }
1125#endif 1170#endif
1126 1171
1127 /* sort out which command line options take precedence */ 1172 /* sort out which command line options take precedence */
1128 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST)) 1173 if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST))
1129 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */ 1174 G.all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
1130 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { 1175 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1131 if (all_fmt & TIME_CHANGE) 1176 if (G.all_fmt & TIME_CHANGE)
1132 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME; 1177 G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME;
1133 if (all_fmt & TIME_ACCESS) 1178 if (G.all_fmt & TIME_ACCESS)
1134 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME; 1179 G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME;
1135 } 1180 }
1136 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */ 1181 if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */
1137 all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME); 1182 G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME);
1138 1183
1139 /* choose a display format if one was not already specified by an option */ 1184 /* choose a display format if one was not already specified by an option */
1140 if (!(all_fmt & STYLE_MASK)) 1185 if (!(G.all_fmt & STYLE_MASK))
1141 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE); 1186 G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
1142 1187
1143 argv += optind; 1188 argv += optind;
1144 if (!argv[0]) 1189 if (!argv[0])
1145 *--argv = (char*)"."; 1190 *--argv = (char*)".";
1146 1191
1147 if (argv[1]) 1192 if (argv[1])
1148 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */ 1193 G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
1149 1194
1150 /* stuff the command line file names into a dnode array */ 1195 /* stuff the command line file names into a dnode array */
1151 dn = NULL; 1196 dn = NULL;
@@ -1153,25 +1198,26 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1153 do { 1198 do {
1154 cur = my_stat(*argv, *argv, 1199 cur = my_stat(*argv, *argv,
1155 /* follow links on command line unless -l, -s or -F: */ 1200 /* follow links on command line unless -l, -s or -F: */
1156 !((all_fmt & STYLE_MASK) == STYLE_LONG 1201 !((G.all_fmt & STYLE_MASK) == STYLE_LONG
1157 || (all_fmt & LIST_BLOCKS) 1202 || (G.all_fmt & LIST_BLOCKS)
1158 || (option_mask32 & OPT_F) 1203 || (option_mask32 & OPT_F)
1159 ) 1204 )
1160 /* ... or if -H: */ 1205 /* ... or if -H: */
1161 || (option_mask32 & OPT_H) 1206 || (option_mask32 & OPT_H)
1207 /* ... or if -L, but my_stat always follows links if -L */
1162 ); 1208 );
1163 argv++; 1209 argv++;
1164 if (!cur) 1210 if (!cur)
1165 continue; 1211 continue;
1166 cur->fname_allocated = 0; 1212 /*cur->fname_allocated = 0; - already is */
1167 cur->next = dn; 1213 cur->dn_next = dn;
1168 dn = cur; 1214 dn = cur;
1169 nfiles++; 1215 nfiles++;
1170 } while (*argv); 1216 } while (*argv);
1171 1217
1172 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */ 1218 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1173 if (nfiles == 0) 1219 if (nfiles == 0)
1174 return exit_code; 1220 return G.exit_code;
1175 1221
1176 /* now that we know how many files there are 1222 /* now that we know how many files there are
1177 * allocate memory for an array to hold dnode pointers 1223 * allocate memory for an array to hold dnode pointers
@@ -1179,33 +1225,32 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1179 dnp = dnalloc(nfiles); 1225 dnp = dnalloc(nfiles);
1180 for (i = 0; /* i < nfiles - detected via !dn below */; i++) { 1226 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1181 dnp[i] = dn; /* save pointer to node in array */ 1227 dnp[i] = dn; /* save pointer to node in array */
1182 dn = dn->next; 1228 dn = dn->dn_next;
1183 if (!dn) 1229 if (!dn)
1184 break; 1230 break;
1185 } 1231 }
1186 1232
1187 if (all_fmt & DISP_NOLIST) { 1233 if (G.all_fmt & DISP_NOLIST) {
1188 dnsort(dnp, nfiles); 1234 sort_and_display_files(dnp, nfiles);
1189 showfiles(dnp, nfiles);
1190 } else { 1235 } else {
1191 dnd = splitdnarray(dnp, SPLIT_DIR); 1236 dnd = splitdnarray(dnp, SPLIT_DIR);
1192 dnf = splitdnarray(dnp, SPLIT_FILE); 1237 dnf = splitdnarray(dnp, SPLIT_FILE);
1193 dndirs = count_dirs(dnp, SPLIT_DIR); 1238 dndirs = count_dirs(dnp, SPLIT_DIR);
1194 dnfiles = nfiles - dndirs; 1239 dnfiles = nfiles - dndirs;
1195 if (dnfiles > 0) { 1240 if (dnfiles > 0) {
1196 dnsort(dnf, dnfiles); 1241 sort_and_display_files(dnf, dnfiles);
1197 showfiles(dnf, dnfiles);
1198 if (ENABLE_FEATURE_CLEAN_UP) 1242 if (ENABLE_FEATURE_CLEAN_UP)
1199 free(dnf); 1243 free(dnf);
1200 } 1244 }
1201 if (dndirs > 0) { 1245 if (dndirs > 0) {
1202 dnsort(dnd, dndirs); 1246 dnsort(dnd, dndirs);
1203 showdirs(dnd, dnfiles == 0); 1247 scan_and_display_dirs_recur(dnd, dnfiles == 0);
1204 if (ENABLE_FEATURE_CLEAN_UP) 1248 if (ENABLE_FEATURE_CLEAN_UP)
1205 free(dnd); 1249 free(dnd);
1206 } 1250 }
1207 } 1251 }
1252
1208 if (ENABLE_FEATURE_CLEAN_UP) 1253 if (ENABLE_FEATURE_CLEAN_UP)
1209 dfree(dnp); 1254 dfree(dnp);
1210 return exit_code; 1255 return G.exit_code;
1211} 1256}
diff --git a/coreutils/od.c b/coreutils/od.c
index 31ebde210..fb11fcfe3 100644
--- a/coreutils/od.c
+++ b/coreutils/od.c
@@ -11,11 +11,12 @@
11 * Original copyright notice is retained at the end of this file. 11 * Original copyright notice is retained at the end of this file.
12 */ 12 */
13 13
14//usage:#if !ENABLE_DESKTOP
14//usage:#define od_trivial_usage 15//usage:#define od_trivial_usage
15//usage: "[-aBbcDdeFfHhIiLlOovXx] " IF_DESKTOP("[-t TYPE] ") "[FILE]" 16//usage: "[-aBbcDdeFfHhIiLlOovXx] [FILE]"
16//usage:#define od_full_usage "\n\n" 17//usage:#define od_full_usage "\n\n"
17//usage: "Write an unambiguous representation, octal bytes by default, of FILE\n" 18//usage: "Print FILE (or stdin) unambiguously, as octal bytes by default"
18//usage: "(or stdin) to stdout" 19//usage:#endif
19 20
20#include "libbb.h" 21#include "libbb.h"
21#if ENABLE_DESKTOP 22#if ENABLE_DESKTOP
diff --git a/coreutils/od_bloaty.c b/coreutils/od_bloaty.c
index 4c6b64d5e..8013f483c 100644
--- a/coreutils/od_bloaty.c
+++ b/coreutils/od_bloaty.c
@@ -13,48 +13,64 @@
13 13
14 You should have received a copy of the GNU General Public License 14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation, 15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 17 */
18/* Written by Jim Meyering. */ 18/* Written by Jim Meyering. */
19/* Busyboxed by Denys Vlasenko, based on od.c from coreutils-5.2.1 */
19 20
20/* Busyboxed by Denys Vlasenko
21
22Based on od.c from coreutils-5.2.1
23Top bloat sources:
24 21
2500000073 t parse_old_offset 22/* #include "libbb.h" - done in od.c */
260000007b t get_lcm 23#define assert(a) ((void)0)
2700000090 r long_options
2800000092 t print_named_ascii
29000000bf t print_ascii
3000000168 t write_block
3100000366 t decode_format_string
3200000a71 T od_main
33
34Tested for compat with coreutils 6.3
35using this script. Minor differences fixed.
36 24
37#!/bin/sh
38echo STD
39time /path/to/coreutils/od \
40...params... \
41>std
42echo Exit code $?
43echo BBOX
44time ./busybox od \
45...params... \
46>bbox
47echo Exit code $?
48diff -u -a std bbox >bbox.diff || { echo Different!; sleep 1; }
49 25
50*/ 26//usage:#if ENABLE_DESKTOP
27//usage:#define od_trivial_usage
28//usage: "[-abcdfhilovxs] [-t TYPE] [-A RADIX] [-N SIZE] [-j SKIP] [-S MINSTR] [-w WIDTH] [FILE...]"
29// We don't support:
30// ... [FILE] [[+]OFFSET[.][b]]
31// Support is buggy for:
32// od --traditional [OPTION]... [FILE] [[+]OFFSET[.][b] [+][LABEL][.][b]]
33
34//usage:#define od_full_usage "\n\n"
35//usage: "Print FILEs (or stdin) unambiguously, as octal bytes by default"
36//usage:#endif
37
38enum {
39 OPT_A = 1 << 0,
40 OPT_N = 1 << 1,
41 OPT_a = 1 << 2,
42 OPT_b = 1 << 3,
43 OPT_c = 1 << 4,
44 OPT_d = 1 << 5,
45 OPT_f = 1 << 6,
46 OPT_h = 1 << 7,
47 OPT_i = 1 << 8,
48 OPT_j = 1 << 9,
49 OPT_l = 1 << 10,
50 OPT_o = 1 << 11,
51 OPT_t = 1 << 12,
52 /* When zero and two or more consecutive blocks are equal, format
53 only the first block and output an asterisk alone on the following
54 line to indicate that identical blocks have been elided: */
55 OPT_v = 1 << 13,
56 OPT_x = 1 << 14,
57 OPT_s = 1 << 15,
58 OPT_S = 1 << 16,
59 OPT_w = 1 << 17,
60 OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS,
61};
51 62
52#include "libbb.h" 63#define OD_GETOPT32() getopt32(argv, \
64 "A:N:abcdfhij:lot:vxsS:w::", \
65 /* -w with optional param */ \
66 /* -S was -s and also had optional parameter */ \
67 /* but in coreutils 6.3 it was renamed and now has */ \
68 /* _mandatory_ parameter */ \
69 &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block)
53 70
54#define assert(a) ((void)0)
55 71
56/* Check for 0x7f is a coreutils 6.3 addition */ 72/* Check for 0x7f is a coreutils 6.3 addition */
57#define ISPRINT(c) (((c)>=' ') && (c) != 0x7f) 73#define ISPRINT(c) (((c) >= ' ') && (c) < 0x7f)
58 74
59typedef long double longdouble_t; 75typedef long double longdouble_t;
60typedef unsigned long long ulonglong_t; 76typedef unsigned long long ulonglong_t;
@@ -165,17 +181,9 @@ struct ERR_width_bytes_has_bad_size {
165 char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1]; 181 char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1];
166}; 182};
167 183
168static smallint flag_dump_strings; 184static smallint exit_code;
169/* Non-zero if an old-style 'pseudo-address' was specified. */
170static smallint flag_pseudo_start;
171static smallint limit_bytes_to_format;
172/* When zero and two or more consecutive blocks are equal, format
173 only the first block and output an asterisk alone on the following
174 line to indicate that identical blocks have been elided. */
175static smallint verbose;
176static smallint ioerror;
177 185
178static size_t string_min; 186static unsigned string_min;
179 187
180/* An array of specs describing how to format each input block. */ 188/* An array of specs describing how to format each input block. */
181static size_t n_specs; 189static size_t n_specs;
@@ -186,7 +194,11 @@ static struct tspec *spec;
186static void (*format_address)(off_t, char); 194static void (*format_address)(off_t, char);
187/* The difference between the old-style pseudo starting address and 195/* The difference between the old-style pseudo starting address and
188 the number of bytes to skip. */ 196 the number of bytes to skip. */
197#if ENABLE_LONG_OPTS
189static off_t pseudo_offset; 198static off_t pseudo_offset;
199#else
200enum { pseudo_offset = 0 };
201#endif
190/* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all 202/* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all
191 input is formatted. */ 203 input is formatted. */
192 204
@@ -221,7 +233,7 @@ static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1
221 233
222#define MAX_FP_TYPE_SIZE sizeof(longdouble_t) 234#define MAX_FP_TYPE_SIZE sizeof(longdouble_t)
223static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = { 235static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = {
224 /* gcc seems to allow repeated indexes. Last one stays */ 236 /* gcc seems to allow repeated indexes. Last one wins */
225 [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE, 237 [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE,
226 [sizeof(double)] = FLOAT_DOUBLE, 238 [sizeof(double)] = FLOAT_DOUBLE,
227 [sizeof(float)] = FLOAT_SINGLE 239 [sizeof(float)] = FLOAT_SINGLE
@@ -383,7 +395,7 @@ print_named_ascii(size_t n_bytes, const char *block,
383 }; 395 };
384 // buf[N] pos: 01234 56789 396 // buf[N] pos: 01234 56789
385 char buf[12] = " x\0 0xx\0"; 397 char buf[12] = " x\0 0xx\0";
386 // actually " x\0 xxx\0", but I want to share the string with below. 398 // actually " x\0 xxx\0", but want to share string with print_ascii.
387 // [12] because we take three 32bit stack slots anyway, and 399 // [12] because we take three 32bit stack slots anyway, and
388 // gcc is too dumb to initialize with constant stores, 400 // gcc is too dumb to initialize with constant stores,
389 // it copies initializer from rodata. Oh well. 401 // it copies initializer from rodata. Oh well.
@@ -479,10 +491,10 @@ open_next_file(void)
479 if (in_stream) { 491 if (in_stream) {
480 break; 492 break;
481 } 493 }
482 ioerror = 1; 494 exit_code = 1;
483 } 495 }
484 496
485 if (limit_bytes_to_format && !flag_dump_strings) 497 if ((option_mask32 & (OPT_N|OPT_S)) == OPT_N)
486 setbuf(in_stream, NULL); 498 setbuf(in_stream, NULL);
487} 499}
488 500
@@ -502,15 +514,14 @@ check_and_close(void)
502 ? bb_msg_standard_input 514 ? bb_msg_standard_input
503 : file_list[-1] 515 : file_list[-1]
504 ); 516 );
505 ioerror = 1; 517 exit_code = 1;
506 } 518 }
507 fclose_if_not_stdin(in_stream); 519 fclose_if_not_stdin(in_stream);
508 in_stream = NULL; 520 in_stream = NULL;
509 } 521 }
510 522
511 if (ferror(stdout)) { 523 if (ferror(stdout)) {
512 bb_error_msg(bb_msg_write_error); 524 bb_error_msg_and_die(bb_msg_write_error);
513 ioerror = 1;
514 } 525 }
515} 526}
516 527
@@ -542,7 +553,6 @@ decode_one_format(const char *s_orig, const char *s, struct tspec *tspec)
542 unsigned field_width = 0; 553 unsigned field_width = 0;
543 int pos; 554 int pos;
544 555
545
546 switch (*s) { 556 switch (*s) {
547 case 'd': 557 case 'd':
548 case 'o': 558 case 'o':
@@ -788,7 +798,7 @@ skip(off_t n_skip)
788 /* take "check & close / open_next" route */ 798 /* take "check & close / open_next" route */
789 } else { 799 } else {
790 if (fseeko(in_stream, n_skip, SEEK_CUR) != 0) 800 if (fseeko(in_stream, n_skip, SEEK_CUR) != 0)
791 ioerror = 1; 801 exit_code = 1;
792 return; 802 return;
793 } 803 }
794 } else { 804 } else {
@@ -889,7 +899,8 @@ write_block(off_t current_offset, size_t n_bytes,
889 static char prev_pair_equal = 0; 899 static char prev_pair_equal = 0;
890 size_t i; 900 size_t i;
891 901
892 if (!verbose && !first 902 if (!(option_mask32 & OPT_v)
903 && !first
893 && n_bytes == bytes_per_block 904 && n_bytes == bytes_per_block
894 && memcmp(prev_block, curr_block, bytes_per_block) == 0 905 && memcmp(prev_block, curr_block, bytes_per_block) == 0
895 ) { 906 ) {
@@ -911,9 +922,9 @@ write_block(off_t current_offset, size_t n_bytes,
911 (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string); 922 (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
912 if (spec[i].hexl_mode_trailer) { 923 if (spec[i].hexl_mode_trailer) {
913 /* space-pad out to full line width, then dump the trailer */ 924 /* space-pad out to full line width, then dump the trailer */
914 int datum_width = width_bytes[spec[i].size]; 925 unsigned datum_width = width_bytes[spec[i].size];
915 int blank_fields = (bytes_per_block - n_bytes) / datum_width; 926 unsigned blank_fields = (bytes_per_block - n_bytes) / datum_width;
916 int field_width = spec[i].field_width + 1; 927 unsigned field_width = spec[i].field_width + 1;
917 printf("%*s", blank_fields * field_width, ""); 928 printf("%*s", blank_fields * field_width, "");
918 dump_hexl_mode_trailer(n_bytes, curr_block); 929 dump_hexl_mode_trailer(n_bytes, curr_block);
919 } 930 }
@@ -961,42 +972,6 @@ get_lcm(void)
961 return l_c_m; 972 return l_c_m;
962} 973}
963 974
964#if ENABLE_LONG_OPTS
965/* If S is a valid traditional offset specification with an optional
966 leading '+' return nonzero and set *OFFSET to the offset it denotes. */
967
968static int
969parse_old_offset(const char *s, off_t *offset)
970{
971 static const struct suffix_mult Bb[] = {
972 { "B", 1024 },
973 { "b", 512 },
974 { "", 0 }
975 };
976 char *p;
977 int radix;
978
979 /* Skip over any leading '+'. */
980 if (s[0] == '+') ++s;
981
982 /* Determine the radix we'll use to interpret S. If there is a '.',
983 * it's decimal, otherwise, if the string begins with '0X'or '0x',
984 * it's hexadecimal, else octal. */
985 p = strchr(s, '.');
986 radix = 8;
987 if (p) {
988 p[0] = '\0'; /* cheating */
989 radix = 10;
990 } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
991 radix = 16;
992
993 *offset = xstrtooff_sfx(s, radix, Bb);
994 if (p) p[0] = '.';
995
996 return (*offset >= 0);
997}
998#endif
999
1000/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the 975/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
1001 formatted block to standard output, and repeat until the specified 976 formatted block to standard output, and repeat until the specified
1002 maximum number of bytes has been read or until all input has been 977 maximum number of bytes has been read or until all input has been
@@ -1014,27 +989,25 @@ dump(off_t current_offset, off_t end_offset)
1014 int idx; 989 int idx;
1015 size_t n_bytes_read; 990 size_t n_bytes_read;
1016 991
1017 block[0] = xmalloc(2*bytes_per_block); 992 block[0] = xmalloc(2 * bytes_per_block);
1018 block[1] = block[0] + bytes_per_block; 993 block[1] = block[0] + bytes_per_block;
1019 994
1020 idx = 0; 995 idx = 0;
1021 if (limit_bytes_to_format) { 996 if (option_mask32 & OPT_N) {
1022 while (1) { 997 while (1) {
1023 size_t n_needed; 998 size_t n_needed;
1024 if (current_offset >= end_offset) { 999 if (current_offset >= end_offset) {
1025 n_bytes_read = 0; 1000 n_bytes_read = 0;
1026 break; 1001 break;
1027 } 1002 }
1028 n_needed = MIN(end_offset - current_offset, 1003 n_needed = MIN(end_offset - current_offset, (off_t) bytes_per_block);
1029 (off_t) bytes_per_block);
1030 read_block(n_needed, block[idx], &n_bytes_read); 1004 read_block(n_needed, block[idx], &n_bytes_read);
1031 if (n_bytes_read < bytes_per_block) 1005 if (n_bytes_read < bytes_per_block)
1032 break; 1006 break;
1033 assert(n_bytes_read == bytes_per_block); 1007 assert(n_bytes_read == bytes_per_block);
1034 write_block(current_offset, n_bytes_read, 1008 write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]);
1035 block[!idx], block[idx]);
1036 current_offset += n_bytes_read; 1009 current_offset += n_bytes_read;
1037 idx = !idx; 1010 idx ^= 1;
1038 } 1011 }
1039 } else { 1012 } else {
1040 while (1) { 1013 while (1) {
@@ -1042,10 +1015,9 @@ dump(off_t current_offset, off_t end_offset)
1042 if (n_bytes_read < bytes_per_block) 1015 if (n_bytes_read < bytes_per_block)
1043 break; 1016 break;
1044 assert(n_bytes_read == bytes_per_block); 1017 assert(n_bytes_read == bytes_per_block);
1045 write_block(current_offset, n_bytes_read, 1018 write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]);
1046 block[!idx], block[idx]);
1047 current_offset += n_bytes_read; 1019 current_offset += n_bytes_read;
1048 idx = !idx; 1020 idx ^= 1;
1049 } 1021 }
1050 } 1022 }
1051 1023
@@ -1061,41 +1033,18 @@ dump(off_t current_offset, off_t end_offset)
1061 1033
1062 memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read); 1034 memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
1063 write_block(current_offset, bytes_to_write, 1035 write_block(current_offset, bytes_to_write,
1064 block[!idx], block[idx]); 1036 block[idx ^ 1], block[idx]);
1065 current_offset += n_bytes_read; 1037 current_offset += n_bytes_read;
1066 } 1038 }
1067 1039
1068 format_address(current_offset, '\n'); 1040 format_address(current_offset, '\n');
1069 1041
1070 if (limit_bytes_to_format && current_offset >= end_offset) 1042 if ((option_mask32 & OPT_N) && current_offset >= end_offset)
1071 check_and_close(); 1043 check_and_close();
1072 1044
1073 free(block[0]); 1045 free(block[0]);
1074} 1046}
1075 1047
1076/* Read a single byte into *C from the concatenation of the input files
1077 named in the global array FILE_LIST. On the first call to this
1078 function, the global variable IN_STREAM is expected to be an open
1079 stream associated with the input file INPUT_FILENAME. If IN_STREAM
1080 is at end-of-file, close it and update the global variables IN_STREAM
1081 and INPUT_FILENAME so they correspond to the next file in the list.
1082 Then try to read a byte from the newly opened file. Repeat if
1083 necessary until EOF is reached for the last file in FILE_LIST, then
1084 set *C to EOF and return. Subsequent calls do likewise. */
1085
1086static void
1087read_char(int *c)
1088{
1089 while (in_stream) { /* !EOF */
1090 *c = fgetc(in_stream);
1091 if (*c != EOF)
1092 return;
1093 check_and_close();
1094 open_next_file();
1095 }
1096 *c = EOF;
1097}
1098
1099/* Read N bytes into BLOCK from the concatenation of the input files 1048/* Read N bytes into BLOCK from the concatenation of the input files
1100 named in the global array FILE_LIST. On the first call to this 1049 named in the global array FILE_LIST. On the first call to this
1101 function, the global variable IN_STREAM is expected to be an open 1050 function, the global variable IN_STREAM is expected to be an open
@@ -1119,8 +1068,8 @@ read_char(int *c)
1119static void 1068static void
1120dump_strings(off_t address, off_t end_offset) 1069dump_strings(off_t address, off_t end_offset)
1121{ 1070{
1122 size_t bufsize = MAX(100, string_min); 1071 unsigned bufsize = MAX(100, string_min);
1123 char *buf = xmalloc(bufsize); 1072 unsigned char *buf = xmalloc(bufsize);
1124 1073
1125 while (1) { 1074 while (1) {
1126 size_t i; 1075 size_t i;
@@ -1128,19 +1077,25 @@ dump_strings(off_t address, off_t end_offset)
1128 1077
1129 /* See if the next 'string_min' chars are all printing chars. */ 1078 /* See if the next 'string_min' chars are all printing chars. */
1130 tryline: 1079 tryline:
1131 if (limit_bytes_to_format && (end_offset - string_min <= address)) 1080 if ((option_mask32 & OPT_N) && (end_offset - string_min <= address))
1132 break; 1081 break;
1133 i = 0; 1082 i = 0;
1134 while (!limit_bytes_to_format || address < end_offset) { 1083 while (!(option_mask32 & OPT_N) || address < end_offset) {
1135 if (i == bufsize) { 1084 if (i == bufsize) {
1136 bufsize += bufsize/8; 1085 bufsize += bufsize/8;
1137 buf = xrealloc(buf, bufsize); 1086 buf = xrealloc(buf, bufsize);
1138 } 1087 }
1139 read_char(&c); 1088
1140 if (c < 0) { /* EOF */ 1089 while (in_stream) { /* !EOF */
1141 free(buf); 1090 c = fgetc(in_stream);
1142 return; 1091 if (c != EOF)
1092 goto got_char;
1093 check_and_close();
1094 open_next_file();
1143 } 1095 }
1096 /* EOF */
1097 goto ret;
1098 got_char:
1144 address++; 1099 address++;
1145 if (!c) 1100 if (!c)
1146 break; 1101 break;
@@ -1152,8 +1107,7 @@ dump_strings(off_t address, off_t end_offset)
1152 if (i < string_min) /* Too short! */ 1107 if (i < string_min) /* Too short! */
1153 goto tryline; 1108 goto tryline;
1154 1109
1155 /* If we get here, the string is all printable and NUL-terminated, 1110 /* If we get here, the string is all printable and NUL-terminated */
1156 * so print it. It is all in 'buf' and 'i' is its length. */
1157 buf[i] = 0; 1111 buf[i] = 0;
1158 format_address(address - i - 1, ' '); 1112 format_address(address - i - 1, ' ');
1159 1113
@@ -1174,13 +1128,50 @@ dump_strings(off_t address, off_t end_offset)
1174 1128
1175 /* We reach this point only if we search through 1129 /* We reach this point only if we search through
1176 (max_bytes_to_format - string_min) bytes before reaching EOF. */ 1130 (max_bytes_to_format - string_min) bytes before reaching EOF. */
1131 check_and_close();
1132 ret:
1177 free(buf); 1133 free(buf);
1134}
1178 1135
1179 check_and_close(); 1136#if ENABLE_LONG_OPTS
1137/* If S is a valid traditional offset specification with an optional
1138 leading '+' return nonzero and set *OFFSET to the offset it denotes. */
1139
1140static int
1141parse_old_offset(const char *s, off_t *offset)
1142{
1143 static const struct suffix_mult Bb[] = {
1144 { "B", 1024 },
1145 { "b", 512 },
1146 { "", 0 }
1147 };
1148 char *p;
1149 int radix;
1150
1151 /* Skip over any leading '+'. */
1152 if (s[0] == '+') ++s;
1153 if (!isdigit(s[0])) return 0; /* not a number */
1154
1155 /* Determine the radix we'll use to interpret S. If there is a '.',
1156 * it's decimal, otherwise, if the string begins with '0X'or '0x',
1157 * it's hexadecimal, else octal. */
1158 p = strchr(s, '.');
1159 radix = 8;
1160 if (p) {
1161 p[0] = '\0'; /* cheating */
1162 radix = 10;
1163 } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
1164 radix = 16;
1165
1166 *offset = xstrtooff_sfx(s, radix, Bb);
1167 if (p) p[0] = '.';
1168
1169 return (*offset >= 0);
1180} 1170}
1171#endif
1181 1172
1182int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1173int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1183int od_main(int argc, char **argv) 1174int od_main(int argc UNUSED_PARAM, char **argv)
1184{ 1175{
1185 static const struct suffix_mult bkm[] = { 1176 static const struct suffix_mult bkm[] = {
1186 { "b", 512 }, 1177 { "b", 512 },
@@ -1188,27 +1179,6 @@ int od_main(int argc, char **argv)
1188 { "m", 1024*1024 }, 1179 { "m", 1024*1024 },
1189 { "", 0 } 1180 { "", 0 }
1190 }; 1181 };
1191 enum {
1192 OPT_A = 1 << 0,
1193 OPT_N = 1 << 1,
1194 OPT_a = 1 << 2,
1195 OPT_b = 1 << 3,
1196 OPT_c = 1 << 4,
1197 OPT_d = 1 << 5,
1198 OPT_f = 1 << 6,
1199 OPT_h = 1 << 7,
1200 OPT_i = 1 << 8,
1201 OPT_j = 1 << 9,
1202 OPT_l = 1 << 10,
1203 OPT_o = 1 << 11,
1204 OPT_t = 1 << 12,
1205 OPT_v = 1 << 13,
1206 OPT_x = 1 << 14,
1207 OPT_s = 1 << 15,
1208 OPT_S = 1 << 16,
1209 OPT_w = 1 << 17,
1210 OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS,
1211 };
1212#if ENABLE_LONG_OPTS 1182#if ENABLE_LONG_OPTS
1213 static const char od_longopts[] ALIGN1 = 1183 static const char od_longopts[] ALIGN1 =
1214 "skip-bytes\0" Required_argument "j" 1184 "skip-bytes\0" Required_argument "j"
@@ -1216,18 +1186,18 @@ int od_main(int argc, char **argv)
1216 "read-bytes\0" Required_argument "N" 1186 "read-bytes\0" Required_argument "N"
1217 "format\0" Required_argument "t" 1187 "format\0" Required_argument "t"
1218 "output-duplicates\0" No_argument "v" 1188 "output-duplicates\0" No_argument "v"
1189 /* Yes, it's true: -S NUM, but --strings[=NUM]!
1190 * that is, NUM is mandatory for -S but optional for --strings!
1191 */
1219 "strings\0" Optional_argument "S" 1192 "strings\0" Optional_argument "S"
1220 "width\0" Optional_argument "w" 1193 "width\0" Optional_argument "w"
1221 "traditional\0" No_argument "\xff" 1194 "traditional\0" No_argument "\xff"
1222 ; 1195 ;
1223#endif 1196#endif
1224 char *str_A, *str_N, *str_j, *str_S; 1197 const char *str_A, *str_N, *str_j, *str_S = "3";
1225 llist_t *lst_t = NULL; 1198 llist_t *lst_t = NULL;
1226 unsigned opt; 1199 unsigned opt;
1227 int l_c_m; 1200 int l_c_m;
1228 /* The old-style 'pseudo starting address' to be printed in parentheses
1229 after any true address. */
1230 off_t pseudo_start = pseudo_start; // for gcc
1231 /* The number of input bytes to skip before formatting and writing. */ 1201 /* The number of input bytes to skip before formatting and writing. */
1232 off_t n_bytes_to_skip = 0; 1202 off_t n_bytes_to_skip = 0;
1233 /* The offset of the first byte after the last byte to be formatted. */ 1203 /* The offset of the first byte after the last byte to be formatted. */
@@ -1239,20 +1209,13 @@ int od_main(int argc, char **argv)
1239 format_address = format_address_std; 1209 format_address = format_address_std;
1240 address_base_char = 'o'; 1210 address_base_char = 'o';
1241 address_pad_len_char = '7'; 1211 address_pad_len_char = '7';
1242 /* flag_dump_strings = 0; - already is */
1243 1212
1244 /* Parse command line */ 1213 /* Parse command line */
1245 opt_complementary = "w+:t::"; /* -w N, -t is a list */ 1214 opt_complementary = "w+:t::"; /* -w N, -t is a list */
1246#if ENABLE_LONG_OPTS 1215#if ENABLE_LONG_OPTS
1247 applet_long_options = od_longopts; 1216 applet_long_options = od_longopts;
1248#endif 1217#endif
1249 opt = getopt32(argv, "A:N:abcdfhij:lot:vxsS:" 1218 opt = OD_GETOPT32();
1250 "w::", // -w with optional param
1251 // -S was -s and also had optional parameter
1252 // but in coreutils 6.3 it was renamed and now has
1253 // _mandatory_ parameter
1254 &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block);
1255 argc -= optind;
1256 argv += optind; 1219 argv += optind;
1257 if (opt & OPT_A) { 1220 if (opt & OPT_A) {
1258 static const char doxn[] ALIGN1 = "doxn"; 1221 static const char doxn[] ALIGN1 = "doxn";
@@ -1274,7 +1237,6 @@ int od_main(int argc, char **argv)
1274 address_pad_len_char = doxn_address_pad_len_char[pos]; 1237 address_pad_len_char = doxn_address_pad_len_char[pos];
1275 } 1238 }
1276 if (opt & OPT_N) { 1239 if (opt & OPT_N) {
1277 limit_bytes_to_format = 1;
1278 max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm); 1240 max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm);
1279 } 1241 }
1280 if (opt & OPT_a) decode_format_string("a"); 1242 if (opt & OPT_a) decode_format_string("a");
@@ -1287,28 +1249,23 @@ int od_main(int argc, char **argv)
1287 if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm); 1249 if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm);
1288 if (opt & OPT_l) decode_format_string("d4"); 1250 if (opt & OPT_l) decode_format_string("d4");
1289 if (opt & OPT_o) decode_format_string("o2"); 1251 if (opt & OPT_o) decode_format_string("o2");
1290 //if (opt & OPT_t)...
1291 while (lst_t) { 1252 while (lst_t) {
1292 decode_format_string(llist_pop(&lst_t)); 1253 decode_format_string(llist_pop(&lst_t));
1293 } 1254 }
1294 if (opt & OPT_v) verbose = 1;
1295 if (opt & OPT_x) decode_format_string("x2"); 1255 if (opt & OPT_x) decode_format_string("x2");
1296 if (opt & OPT_s) decode_format_string("d2"); 1256 if (opt & OPT_s) decode_format_string("d2");
1297 if (opt & OPT_S) { 1257 if (opt & OPT_S) {
1298 string_min = 3;
1299 string_min = xstrtou_sfx(str_S, 0, bkm); 1258 string_min = xstrtou_sfx(str_S, 0, bkm);
1300 flag_dump_strings = 1;
1301 } 1259 }
1302 //if (opt & OPT_w)...
1303 //if (opt & OPT_traditional)...
1304 1260
1305 if (flag_dump_strings && n_specs > 0) 1261 // Bloat:
1306 bb_error_msg_and_die("no type may be specified when dumping strings"); 1262 //if ((option_mask32 & OPT_S) && n_specs > 0)
1263 // bb_error_msg_and_die("no type may be specified when dumping strings");
1307 1264
1308 /* If the --traditional option is used, there may be from 1265 /* If the --traditional option is used, there may be from
1309 * 0 to 3 remaining command line arguments; handle each case 1266 * 0 to 3 remaining command line arguments; handle each case
1310 * separately. 1267 * separately.
1311 * od [file] [[+]offset[.][b] [[+]label[.][b]]] 1268 * od [FILE] [[+]OFFSET[.][b] [[+]LABEL[.][b]]]
1312 * The offset and pseudo_start have the same syntax. 1269 * The offset and pseudo_start have the same syntax.
1313 * 1270 *
1314 * FIXME: POSIX 1003.1-2001 with XSI requires support for the 1271 * FIXME: POSIX 1003.1-2001 with XSI requires support for the
@@ -1316,93 +1273,91 @@ int od_main(int argc, char **argv)
1316 1273
1317#if ENABLE_LONG_OPTS 1274#if ENABLE_LONG_OPTS
1318 if (opt & OPT_traditional) { 1275 if (opt & OPT_traditional) {
1319 off_t o1, o2; 1276 if (argv[0]) {
1320 1277 off_t pseudo_start = -1;
1321 if (argc == 1) { 1278 off_t o1, o2;
1322 if (parse_old_offset(argv[0], &o1)) { 1279
1323 n_bytes_to_skip = o1; 1280 if (!argv[1]) { /* one arg */
1324 --argc; 1281 if (parse_old_offset(argv[0], &o1)) {
1325 ++argv; 1282 /* od --traditional OFFSET */
1326 } 1283 n_bytes_to_skip = o1;
1327 } else if (argc == 2) { 1284 argv++;
1328 if (parse_old_offset(argv[0], &o1) 1285 }
1329 && parse_old_offset(argv[1], &o2) 1286 /* od --traditional FILE */
1330 ) { 1287 } else if (!argv[2]) { /* two args */
1331 n_bytes_to_skip = o1; 1288 if (parse_old_offset(argv[0], &o1)
1332 flag_pseudo_start = 1; 1289 && parse_old_offset(argv[1], &o2)
1333 pseudo_start = o2; 1290 ) {
1334 argv += 2; 1291 /* od --traditional OFFSET LABEL */
1335 argc -= 2; 1292 n_bytes_to_skip = o1;
1336 } else if (parse_old_offset(argv[1], &o2)) { 1293 pseudo_start = o2;
1337 n_bytes_to_skip = o2; 1294 argv += 2;
1338 --argc; 1295 } else if (parse_old_offset(argv[1], &o2)) {
1339 argv[1] = argv[0]; 1296 /* od --traditional FILE OFFSET */
1340 ++argv; 1297 n_bytes_to_skip = o2;
1341 } else { 1298 argv[1] = NULL;
1342 bb_error_msg_and_die("invalid second operand " 1299 } else {
1343 "in compatibility mode '%s'", argv[1]); 1300 bb_error_msg_and_die("invalid second argument '%s'", argv[1]);
1344 } 1301 }
1345 } else if (argc == 3) { 1302 } else if (!argv[3]) { /* three args */
1346 if (parse_old_offset(argv[1], &o1) 1303 if (parse_old_offset(argv[1], &o1)
1347 && parse_old_offset(argv[2], &o2) 1304 && parse_old_offset(argv[2], &o2)
1348 ) { 1305 ) {
1349 n_bytes_to_skip = o1; 1306 /* od --traditional FILE OFFSET LABEL */
1350 flag_pseudo_start = 1; 1307 n_bytes_to_skip = o1;
1351 pseudo_start = o2; 1308 pseudo_start = o2;
1352 argv[2] = argv[0]; 1309 argv[1] = NULL;
1353 argv += 2; 1310 } else {
1354 argc -= 2; 1311 bb_error_msg_and_die("the last two arguments must be offsets");
1355 } else { 1312 }
1356 bb_error_msg_and_die("in compatibility mode " 1313 } else { /* >3 args */
1357 "the last two arguments must be offsets"); 1314 bb_error_msg_and_die("too many arguments");
1358 } 1315 }
1359 } else if (argc > 3) {
1360 bb_error_msg_and_die("compatibility mode supports "
1361 "at most three arguments");
1362 }
1363 1316
1364 if (flag_pseudo_start) { 1317 if (pseudo_start >= 0) {
1365 if (format_address == format_address_none) { 1318 if (format_address == format_address_none) {
1366 address_base_char = 'o'; 1319 address_base_char = 'o';
1367 address_pad_len_char = '7'; 1320 address_pad_len_char = '7';
1368 format_address = format_address_paren; 1321 format_address = format_address_paren;
1369 } else 1322 } else {
1370 format_address = format_address_label; 1323 format_address = format_address_label;
1324 }
1325 pseudo_offset = pseudo_start - n_bytes_to_skip;
1326 }
1371 } 1327 }
1328 /* else: od --traditional (without args) */
1372 } 1329 }
1373#endif 1330#endif
1374 1331
1375 if (limit_bytes_to_format) { 1332 if (option_mask32 & OPT_N) {
1376 end_offset = n_bytes_to_skip + max_bytes_to_format; 1333 end_offset = n_bytes_to_skip + max_bytes_to_format;
1377 if (end_offset < n_bytes_to_skip) 1334 if (end_offset < n_bytes_to_skip)
1378 bb_error_msg_and_die("skip-bytes + read-bytes is too large"); 1335 bb_error_msg_and_die("SKIP + SIZE is too large");
1379 } 1336 }
1380 1337
1381 if (n_specs == 0) { 1338 if (n_specs == 0) {
1382 decode_format_string("o2"); 1339 decode_format_string("o2");
1383 n_specs = 1; 1340 /*n_specs = 1; - done by decode_format_string */
1384 } 1341 }
1385 1342
1386 /* If no files were listed on the command line, 1343 /* If no files were listed on the command line,
1387 set the global pointer FILE_LIST so that it 1344 set the global pointer FILE_LIST so that it
1388 references the null-terminated list of one name: "-". */ 1345 references the null-terminated list of one name: "-". */
1389 file_list = bb_argv_dash; 1346 file_list = bb_argv_dash;
1390 if (argc > 0) { 1347 if (argv[0]) {
1391 /* Set the global pointer FILE_LIST so that it 1348 /* Set the global pointer FILE_LIST so that it
1392 references the first file-argument on the command-line. */ 1349 references the first file-argument on the command-line. */
1393 file_list = (char const *const *) argv; 1350 file_list = (char const *const *) argv;
1394 } 1351 }
1395 1352
1396 /* open the first input file */ 1353 /* Open the first input file */
1397 open_next_file(); 1354 open_next_file();
1398 /* skip over any unwanted header bytes */ 1355 /* Skip over any unwanted header bytes */
1399 skip(n_bytes_to_skip); 1356 skip(n_bytes_to_skip);
1400 if (!in_stream) 1357 if (!in_stream)
1401 return EXIT_FAILURE; 1358 return EXIT_FAILURE;
1402 1359
1403 pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0); 1360 /* Compute output block length */
1404
1405 /* Compute output block length. */
1406 l_c_m = get_lcm(); 1361 l_c_m = get_lcm();
1407 1362
1408 if (opt & OPT_w) { /* -w: width */ 1363 if (opt & OPT_w) { /* -w: width */
@@ -1424,13 +1379,13 @@ int od_main(int argc, char **argv)
1424 } 1379 }
1425#endif 1380#endif
1426 1381
1427 if (flag_dump_strings) 1382 if (option_mask32 & OPT_S)
1428 dump_strings(n_bytes_to_skip, end_offset); 1383 dump_strings(n_bytes_to_skip, end_offset);
1429 else 1384 else
1430 dump(n_bytes_to_skip, end_offset); 1385 dump(n_bytes_to_skip, end_offset);
1431 1386
1432 if (fclose(stdin) == EOF) 1387 if (fclose(stdin))
1433 bb_perror_msg_and_die(bb_msg_standard_input); 1388 bb_perror_msg_and_die(bb_msg_standard_input);
1434 1389
1435 return ioerror; 1390 return exit_code;
1436} 1391}
diff --git a/coreutils/tail.c b/coreutils/tail.c
index eac982735..4b42ebc52 100644
--- a/coreutils/tail.c
+++ b/coreutils/tail.c
@@ -59,7 +59,8 @@ static const struct suffix_mult tail_suffixes[] = {
59}; 59};
60 60
61struct globals { 61struct globals {
62 bool status; 62 bool from_top;
63 bool exitcode;
63} FIX_ALIASING; 64} FIX_ALIASING;
64#define G (*(struct globals*)&bb_common_bufsiz1) 65#define G (*(struct globals*)&bb_common_bufsiz1)
65 66
@@ -85,7 +86,7 @@ static ssize_t tail_read(int fd, char *buf, size_t count)
85 r = full_read(fd, buf, count); 86 r = full_read(fd, buf, count);
86 if (r < 0) { 87 if (r < 0) {
87 bb_perror_msg(bb_msg_read_error); 88 bb_perror_msg(bb_msg_read_error);
88 G.status = EXIT_FAILURE; 89 G.exitcode = EXIT_FAILURE;
89 } 90 }
90 91
91 return r; 92 return r;
@@ -99,7 +100,7 @@ static unsigned eat_num(const char *p)
99 p++; 100 p++;
100 else if (*p == '+') { 101 else if (*p == '+') {
101 p++; 102 p++;
102 G.status = 1; /* mark that we saw "+" */ 103 G.from_top = 1;
103 } 104 }
104 return xatou_sfx(p, tail_suffixes); 105 return xatou_sfx(p, tail_suffixes);
105} 106}
@@ -109,7 +110,6 @@ int tail_main(int argc, char **argv)
109{ 110{
110 unsigned count = 10; 111 unsigned count = 10;
111 unsigned sleep_period = 1; 112 unsigned sleep_period = 1;
112 bool from_top;
113 const char *str_c, *str_n; 113 const char *str_c, *str_n;
114 114
115 char *tailbuf; 115 char *tailbuf;
@@ -152,8 +152,6 @@ int tail_main(int argc, char **argv)
152#endif 152#endif
153 argc -= optind; 153 argc -= optind;
154 argv += optind; 154 argv += optind;
155 from_top = G.status; /* 1 if there was "-c +N" or "-n +N" */
156 G.status = EXIT_SUCCESS;
157 155
158 /* open all the files */ 156 /* open all the files */
159 fds = xmalloc(sizeof(fds[0]) * (argc + 1)); 157 fds = xmalloc(sizeof(fds[0]) * (argc + 1));
@@ -171,7 +169,7 @@ int tail_main(int argc, char **argv)
171 do { 169 do {
172 int fd = open_or_warn_stdin(argv[i]); 170 int fd = open_or_warn_stdin(argv[i]);
173 if (fd < 0 && !FOLLOW_RETRY) { 171 if (fd < 0 && !FOLLOW_RETRY) {
174 G.status = EXIT_FAILURE; 172 G.exitcode = EXIT_FAILURE;
175 continue; 173 continue;
176 } 174 }
177 fds[nfiles] = fd; 175 fds[nfiles] = fd;
@@ -183,15 +181,19 @@ int tail_main(int argc, char **argv)
183 181
184 /* prepare the buffer */ 182 /* prepare the buffer */
185 tailbufsize = BUFSIZ; 183 tailbufsize = BUFSIZ;
186 if (!from_top && COUNT_BYTES) { 184 if (!G.from_top && COUNT_BYTES) {
187 if (tailbufsize < count + BUFSIZ) { 185 if (tailbufsize < count + BUFSIZ) {
188 tailbufsize = count + BUFSIZ; 186 tailbufsize = count + BUFSIZ;
189 } 187 }
190 } 188 }
191 tailbuf = xmalloc(tailbufsize); 189 /* tail -c1024m REGULAR_FILE doesn't really need 1G mem block.
190 * (In fact, it doesn't need ANY memory). So delay allocation.
191 */
192 tailbuf = NULL;
192 193
193 /* tail the files */ 194 /* tail the files */
194 fmt = header_fmt_str + 1; /* skip header leading newline on first output */ 195
196 fmt = header_fmt_str + 1; /* skip leading newline in the header on the first output */
195 i = 0; 197 i = 0;
196 do { 198 do {
197 char *buf; 199 char *buf;
@@ -209,7 +211,7 @@ int tail_main(int argc, char **argv)
209 fmt = header_fmt_str; 211 fmt = header_fmt_str;
210 } 212 }
211 213
212 if (!from_top) { 214 if (!G.from_top) {
213 off_t current = lseek(fd, 0, SEEK_END); 215 off_t current = lseek(fd, 0, SEEK_END);
214 if (current > 0) { 216 if (current > 0) {
215 unsigned off; 217 unsigned off;
@@ -242,6 +244,9 @@ int tail_main(int argc, char **argv)
242 } 244 }
243 } 245 }
244 246
247 if (!tailbuf)
248 tailbuf = xmalloc(tailbufsize);
249
245 buf = tailbuf; 250 buf = tailbuf;
246 taillen = 0; 251 taillen = 0;
247 /* "We saw 1st line/byte". 252 /* "We saw 1st line/byte".
@@ -249,7 +254,7 @@ int tail_main(int argc, char **argv)
249 seen = 1; 254 seen = 1;
250 newlines_seen = 0; 255 newlines_seen = 0;
251 while ((nread = tail_read(fd, buf, tailbufsize-taillen)) > 0) { 256 while ((nread = tail_read(fd, buf, tailbufsize-taillen)) > 0) {
252 if (from_top) { 257 if (G.from_top) {
253 int nwrite = nread; 258 int nwrite = nread;
254 if (seen < count) { 259 if (seen < count) {
255 /* We need to skip a few more bytes/lines */ 260 /* We need to skip a few more bytes/lines */
@@ -313,7 +318,7 @@ int tail_main(int argc, char **argv)
313 buf = tailbuf + taillen; 318 buf = tailbuf + taillen;
314 } 319 }
315 } /* while (tail_read() > 0) */ 320 } /* while (tail_read() > 0) */
316 if (!from_top) { 321 if (!G.from_top) {
317 xwrite(STDOUT_FILENO, tailbuf, taillen); 322 xwrite(STDOUT_FILENO, tailbuf, taillen);
318 } 323 }
319 } while (++i < nfiles); 324 } while (++i < nfiles);
@@ -368,10 +373,11 @@ int tail_main(int argc, char **argv)
368 xwrite(STDOUT_FILENO, tailbuf, nread); 373 xwrite(STDOUT_FILENO, tailbuf, nread);
369 } 374 }
370 } while (++i < nfiles); 375 } while (++i < nfiles);
371 } 376 } /* while (1) */
377
372 if (ENABLE_FEATURE_CLEAN_UP) { 378 if (ENABLE_FEATURE_CLEAN_UP) {
373 free(fds); 379 free(fds);
374 free(tailbuf); 380 free(tailbuf);
375 } 381 }
376 return G.status; 382 return G.exitcode;
377} 383}
diff --git a/docs/style-guide.txt b/docs/style-guide.txt
index fdf6cfe4d..10ed893dc 100644
--- a/docs/style-guide.txt
+++ b/docs/style-guide.txt
@@ -679,11 +679,10 @@ line in the midst of your #includes, if you need to parse long options:
679 679
680Then have long options defined: 680Then have long options defined:
681 681
682 static const struct option <applet>_long_options[] = { 682 static const char <applet>_longopts[] ALIGN1 =
683 { "list", 0, NULL, 't' }, 683 "list\0" No_argument "t"
684 { "extract", 0, NULL, 'x' }, 684 "extract\0" No_argument "x"
685 { NULL, 0, NULL, 0 } 685 ;
686 };
687 686
688And a code block similar to the following near the top of your applet_main() 687And a code block similar to the following near the top of your applet_main()
689routine: 688routine:
@@ -691,7 +690,7 @@ routine:
691 char *str_b; 690 char *str_b;
692 691
693 opt_complementary = "cryptic_string"; 692 opt_complementary = "cryptic_string";
694 applet_long_options = <applet>_long_options; /* if you have them */ 693 applet_long_options = <applet>_longopts; /* if you have them */
695 opt = getopt32(argc, argv, "ab:c", &str_b); 694 opt = getopt32(argc, argv, "ab:c", &str_b);
696 if (opt & 1) { 695 if (opt & 1) {
697 handle_option_a(); 696 handle_option_a();
diff --git a/e2fsprogs/fsck.c b/e2fsprogs/fsck.c
index b4257a2ad..fcbdb5989 100644
--- a/e2fsprogs/fsck.c
+++ b/e2fsprogs/fsck.c
@@ -317,7 +317,6 @@ static void load_fs_info(const char *filename)
317{ 317{
318 FILE *fstab; 318 FILE *fstab;
319 struct mntent mte; 319 struct mntent mte;
320 struct fs_info *fs;
321 320
322 fstab = setmntent(filename, "r"); 321 fstab = setmntent(filename, "r");
323 if (!fstab) { 322 if (!fstab) {
@@ -330,7 +329,7 @@ static void load_fs_info(const char *filename)
330 //bb_info_msg("CREATE[%s][%s][%s][%s][%d]", mte.mnt_fsname, mte.mnt_dir, 329 //bb_info_msg("CREATE[%s][%s][%s][%s][%d]", mte.mnt_fsname, mte.mnt_dir,
331 // mte.mnt_type, mte.mnt_opts, 330 // mte.mnt_type, mte.mnt_opts,
332 // mte.mnt_passno); 331 // mte.mnt_passno);
333 fs = create_fs_device(mte.mnt_fsname, mte.mnt_dir, 332 create_fs_device(mte.mnt_fsname, mte.mnt_dir,
334 mte.mnt_type, mte.mnt_opts, 333 mte.mnt_type, mte.mnt_opts,
335 mte.mnt_passno); 334 mte.mnt_passno);
336 } 335 }
diff --git a/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c b/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c
index 941efa42c..e1f6ba6d2 100644
--- a/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c
+++ b/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c
@@ -96,9 +96,9 @@ blkid_loff_t blkid_get_dev_size(int fd)
96 96
97#ifdef BLKGETSIZE64 97#ifdef BLKGETSIZE64
98#ifdef __linux__ 98#ifdef __linux__
99 if ((uname(&ut) == 0) && 99 uname(&ut);
100 ((ut.release[0] == '2') && (ut.release[1] == '.') && 100 if ((ut.release[0] == '2') && (ut.release[1] == '.') &&
101 (ut.release[2] < '6') && (ut.release[3] == '.'))) 101 (ut.release[2] < '6') && (ut.release[3] == '.'))
102 valid_blkgetsize64 = 0; 102 valid_blkgetsize64 = 0;
103#endif 103#endif
104 if (valid_blkgetsize64 && 104 if (valid_blkgetsize64 &&
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c b/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c
index ff11fe98c..ee4bbb7b0 100644
--- a/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c
+++ b/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c
@@ -174,9 +174,9 @@ errcode_t ext2fs_get_device_size(const char *file, int blocksize,
174 174
175#ifdef BLKGETSIZE64 175#ifdef BLKGETSIZE64
176#ifdef __linux__ 176#ifdef __linux__
177 if ((uname(&ut) == 0) && 177 uname(&ut);
178 ((ut.release[0] == '2') && (ut.release[1] == '.') && 178 if ((ut.release[0] == '2') && (ut.release[1] == '.') &&
179 (ut.release[2] < '6') && (ut.release[3] == '.'))) 179 (ut.release[2] < '6') && (ut.release[3] == '.'))
180 valid_blkgetsize64 = 0; 180 valid_blkgetsize64 = 0;
181#endif 181#endif
182 if (valid_blkgetsize64 && 182 if (valid_blkgetsize64 &&
diff --git a/editors/diff.c b/editors/diff.c
index daa58af9b..8b1e92783 100644
--- a/editors/diff.c
+++ b/editors/diff.c
@@ -101,9 +101,9 @@
101#include "libbb.h" 101#include "libbb.h"
102 102
103#if 0 103#if 0
104//#define dbg_error_msg(...) bb_error_msg(__VA_ARGS__) 104# define dbg_error_msg(...) bb_error_msg(__VA_ARGS__)
105#else 105#else
106#define dbg_error_msg(...) ((void)0) 106# define dbg_error_msg(...) ((void)0)
107#endif 107#endif
108 108
109enum { /* print_status() and diffreg() return values */ 109enum { /* print_status() and diffreg() return values */
diff --git a/editors/patch.c b/editors/patch.c
index a90252a03..6d3f319b0 100644
--- a/editors/patch.c
+++ b/editors/patch.c
@@ -239,7 +239,7 @@ static int apply_one_hunk(void)
239 plist = TT.current_hunk; 239 plist = TT.current_hunk;
240 buf = NULL; 240 buf = NULL;
241 if (reverse ? TT.oldlen : TT.newlen) for (;;) { 241 if (reverse ? TT.oldlen : TT.newlen) for (;;) {
242 char *data = xmalloc_reads(TT.filein, NULL, NULL); 242 char *data = xmalloc_reads(TT.filein, NULL);
243 243
244 TT.linenum++; 244 TT.linenum++;
245 245
diff --git a/editors/sed.c b/editors/sed.c
index 9ab758bd7..99e56ff52 100644
--- a/editors/sed.c
+++ b/editors/sed.c
@@ -14,49 +14,47 @@
14 */ 14 */
15 15
16/* Code overview. 16/* Code overview.
17 *
18 * Files are laid out to avoid unnecessary function declarations. So for
19 * example, every function add_cmd calls occurs before add_cmd in this file.
20 *
21 * add_cmd() is called on each line of sed command text (from a file or from
22 * the command line). It calls get_address() and parse_cmd_args(). The
23 * resulting sed_cmd_t structures are appended to a linked list
24 * (G.sed_cmd_head/G.sed_cmd_tail).
25 *
26 * add_input_file() adds a FILE* to the list of input files. We need to
27 * know all input sources ahead of time to find the last line for the $ match.
28 *
29 * process_files() does actual sedding, reading data lines from each input FILE *
30 * (which could be stdin) and applying the sed command list (sed_cmd_head) to
31 * each of the resulting lines.
32 *
33 * sed_main() is where external code calls into this, with a command line.
34 */
17 35
18 Files are laid out to avoid unnecessary function declarations. So for 36/* Supported features and commands in this version of sed:
19 example, every function add_cmd calls occurs before add_cmd in this file. 37 *
20 38 * - comments ('#')
21 add_cmd() is called on each line of sed command text (from a file or from 39 * - address matching: num|/matchstr/[,num|/matchstr/|$]command
22 the command line). It calls get_address() and parse_cmd_args(). The 40 * - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags)
23 resulting sed_cmd_t structures are appended to a linked list 41 * - edit commands: (a)ppend, (i)nsert, (c)hange
24 (G.sed_cmd_head/G.sed_cmd_tail). 42 * - file commands: (r)ead
25 43 * - backreferences in substitution expressions (\0, \1, \2...\9)
26 add_input_file() adds a FILE* to the list of input files. We need to 44 * - grouped commands: {cmd1;cmd2}
27 know all input sources ahead of time to find the last line for the $ match. 45 * - transliteration (y/source-chars/dest-chars/)
28 46 * - pattern space hold space storing / swapping (g, h, x)
29 process_files() does actual sedding, reading data lines from each input FILE * 47 * - labels / branching (: label, b, t, T)
30 (which could be stdin) and applying the sed command list (sed_cmd_head) to 48 *
31 each of the resulting lines. 49 * (Note: Specifying an address (range) to match is *optional*; commands
32 50 * default to the whole pattern space if no specific address match was
33 sed_main() is where external code calls into this, with a command line. 51 * requested.)
34*/ 52 *
35 53 * Todo:
36 54 * - Create a wrapper around regex to make libc's regex conform with sed
37/* 55 *
38 Supported features and commands in this version of sed: 56 * Reference http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html
39 57 */
40 - comments ('#')
41 - address matching: num|/matchstr/[,num|/matchstr/|$]command
42 - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags)
43 - edit commands: (a)ppend, (i)nsert, (c)hange
44 - file commands: (r)ead
45 - backreferences in substitution expressions (\0, \1, \2...\9)
46 - grouped commands: {cmd1;cmd2}
47 - transliteration (y/source-chars/dest-chars/)
48 - pattern space hold space storing / swapping (g, h, x)
49 - labels / branching (: label, b, t, T)
50
51 (Note: Specifying an address (range) to match is *optional*; commands
52 default to the whole pattern space if no specific address match was
53 requested.)
54
55 Todo:
56 - Create a wrapper around regex to make libc's regex conform with sed
57
58 Reference http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html
59*/
60 58
61//usage:#define sed_trivial_usage 59//usage:#define sed_trivial_usage
62//usage: "[-efinr] SED_CMD [FILE]..." 60//usage: "[-efinr] SED_CMD [FILE]..."
@@ -217,11 +215,16 @@ static void parse_escapes(char *dest, const char *string, int len, char from, ch
217 215
218static char *copy_parsing_escapes(const char *string, int len) 216static char *copy_parsing_escapes(const char *string, int len)
219{ 217{
218 const char *s;
220 char *dest = xmalloc(len + 1); 219 char *dest = xmalloc(len + 1);
221 220
222 parse_escapes(dest, string, len, 'n', '\n'); 221 /* sed recognizes \n */
223 /* GNU sed also recognizes \t */ 222 /* GNU sed also recognizes \t and \r */
224 parse_escapes(dest, dest, strlen(dest), 't', '\t'); 223 for (s = "\nn\tt\rr"; *s; s += 2) {
224 parse_escapes(dest, string, len, s[1], s[0]);
225 string = dest;
226 len = strlen(dest);
227 }
225 return dest; 228 return dest;
226} 229}
227 230
@@ -244,11 +247,13 @@ static int index_of_next_unescaped_regexp_delim(int delimiter, const char *str)
244 delimiter = -delimiter; 247 delimiter = -delimiter;
245 } 248 }
246 249
247 for (; (ch = str[idx]); idx++) { 250 for (; (ch = str[idx]) != '\0'; idx++) {
248 if (bracket >= 0) { 251 if (bracket >= 0) {
249 if (ch == ']' && !(bracket == idx - 1 || (bracket == idx - 2 252 if (ch == ']'
250 && str[idx - 1] == '^'))) 253 && !(bracket == idx - 1 || (bracket == idx - 2 && str[idx - 1] == '^'))
254 ) {
251 bracket = -1; 255 bracket = -1;
256 }
252 } else if (escaped) 257 } else if (escaped)
253 escaped = 0; 258 escaped = 0;
254 else if (ch == '\\') 259 else if (ch == '\\')
@@ -434,11 +439,47 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
434 */ 439 */
435static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) 440static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
436{ 441{
442 static const char cmd_letters[] = "saicrw:btTydDgGhHlnNpPqx={}";
443 enum {
444 IDX_s = 0,
445 IDX_a,
446 IDX_i,
447 IDX_c,
448 IDX_r,
449 IDX_w,
450 IDX_colon,
451 IDX_b,
452 IDX_t,
453 IDX_T,
454 IDX_y,
455 IDX_d,
456 IDX_D,
457 IDX_g,
458 IDX_G,
459 IDX_h,
460 IDX_H,
461 IDX_l,
462 IDX_n,
463 IDX_N,
464 IDX_p,
465 IDX_P,
466 IDX_q,
467 IDX_x,
468 IDX_equal,
469 IDX_lbrace,
470 IDX_rbrace,
471 IDX_nul
472 };
473 struct chk { char chk[sizeof(cmd_letters)-1 == IDX_nul ? 1 : -1]; };
474
475 unsigned idx = strchrnul(cmd_letters, sed_cmd->cmd) - cmd_letters;
476
437 /* handle (s)ubstitution command */ 477 /* handle (s)ubstitution command */
438 if (sed_cmd->cmd == 's') 478 if (idx == IDX_s) {
439 cmdstr += parse_subst_cmd(sed_cmd, cmdstr); 479 cmdstr += parse_subst_cmd(sed_cmd, cmdstr);
480 }
440 /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */ 481 /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */
441 else if (strchr("aic", sed_cmd->cmd)) { 482 else if (idx <= IDX_c) { /* a,i,c */
442 if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c') 483 if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c')
443 bb_error_msg_and_die("only a beginning address can be specified for edit commands"); 484 bb_error_msg_and_die("only a beginning address can be specified for edit commands");
444 for (;;) { 485 for (;;) {
@@ -454,8 +495,9 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
454 /* "\anychar" -> "anychar" */ 495 /* "\anychar" -> "anychar" */
455 parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), '\0', '\0'); 496 parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), '\0', '\0');
456 cmdstr += strlen(cmdstr); 497 cmdstr += strlen(cmdstr);
498 }
457 /* handle file cmds: (r)ead */ 499 /* handle file cmds: (r)ead */
458 } else if (strchr("rw", sed_cmd->cmd)) { 500 else if (idx <= IDX_w) { /* r,w */
459 if (sed_cmd->end_line || sed_cmd->end_match) 501 if (sed_cmd->end_line || sed_cmd->end_match)
460 bb_error_msg_and_die("command only uses one address"); 502 bb_error_msg_and_die("command only uses one address");
461 cmdstr += parse_file_cmd(/*sed_cmd,*/ cmdstr, &sed_cmd->string); 503 cmdstr += parse_file_cmd(/*sed_cmd,*/ cmdstr, &sed_cmd->string);
@@ -463,8 +505,9 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
463 sed_cmd->sw_file = xfopen_for_write(sed_cmd->string); 505 sed_cmd->sw_file = xfopen_for_write(sed_cmd->string);
464 sed_cmd->sw_last_char = '\n'; 506 sed_cmd->sw_last_char = '\n';
465 } 507 }
508 }
466 /* handle branch commands */ 509 /* handle branch commands */
467 } else if (strchr(":btT", sed_cmd->cmd)) { 510 else if (idx <= IDX_T) { /* :,b,t,T */
468 int length; 511 int length;
469 512
470 cmdstr = skip_whitespace(cmdstr); 513 cmdstr = skip_whitespace(cmdstr);
@@ -475,7 +518,7 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
475 } 518 }
476 } 519 }
477 /* translation command */ 520 /* translation command */
478 else if (sed_cmd->cmd == 'y') { 521 else if (idx == IDX_y) {
479 char *match, *replace; 522 char *match, *replace;
480 int i = cmdstr[0]; 523 int i = cmdstr[0];
481 524
@@ -495,7 +538,7 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
495 /* if it wasnt a single-letter command that takes no arguments 538 /* if it wasnt a single-letter command that takes no arguments
496 * then it must be an invalid command. 539 * then it must be an invalid command.
497 */ 540 */
498 else if (strchr("dDgGhHlnNpPqx={}", sed_cmd->cmd) == 0) { 541 else if (idx >= IDX_nul) { /* not d,D,g,G,h,H,l,n,N,p,P,q,x,=,{,} */
499 bb_error_msg_and_die("unsupported command %c", sed_cmd->cmd); 542 bb_error_msg_and_die("unsupported command %c", sed_cmd->cmd);
500 } 543 }
501 544
@@ -966,9 +1009,9 @@ static void process_files(void)
966 } 1009 }
967 sed_cmd->in_match = !( 1010 sed_cmd->in_match = !(
968 /* has the ending line come, or is this a single address command? */ 1011 /* has the ending line come, or is this a single address command? */
969 (sed_cmd->end_line ? 1012 (sed_cmd->end_line
970 sed_cmd->end_line == -1 ? 1013 ? sed_cmd->end_line == -1
971 !next_line 1014 ? !next_line
972 : (sed_cmd->end_line <= linenum) 1015 : (sed_cmd->end_line <= linenum)
973 : !sed_cmd->end_match 1016 : !sed_cmd->end_match
974 ) 1017 )
diff --git a/editors/vi.c b/editors/vi.c
index 712af0326..9d74acc91 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -536,7 +536,6 @@ static void edit_file(char *fn)
536#define cur_line edit_file__cur_line 536#define cur_line edit_file__cur_line
537#endif 537#endif
538 int c; 538 int c;
539 int size;
540#if ENABLE_FEATURE_VI_USE_SIGNALS 539#if ENABLE_FEATURE_VI_USE_SIGNALS
541 int sig; 540 int sig;
542#endif 541#endif
@@ -545,7 +544,6 @@ static void edit_file(char *fn)
545 rawmode(); 544 rawmode();
546 rows = 24; 545 rows = 24;
547 columns = 80; 546 columns = 80;
548 size = 0;
549 IF_FEATURE_VI_ASK_TERMINAL(G.get_rowcol_error =) query_screen_dimensions(); 547 IF_FEATURE_VI_ASK_TERMINAL(G.get_rowcol_error =) query_screen_dimensions();
550#if ENABLE_FEATURE_VI_ASK_TERMINAL 548#if ENABLE_FEATURE_VI_ASK_TERMINAL
551 if (G.get_rowcol_error /* TODO? && no input on stdin */) { 549 if (G.get_rowcol_error /* TODO? && no input on stdin */) {
@@ -1690,12 +1688,16 @@ static char *char_insert(char *p, char c) // insert the char c at 'p'
1690 p = text_hole_delete(p, p); // shrink buffer 1 char 1688 p = text_hole_delete(p, p); // shrink buffer 1 char
1691 } 1689 }
1692 } else { 1690 } else {
1691#if ENABLE_FEATURE_VI_SETOPTS
1693 // insert a char into text[] 1692 // insert a char into text[]
1694 char *sp; // "save p" 1693 char *sp; // "save p"
1694#endif
1695 1695
1696 if (c == 13) 1696 if (c == 13)
1697 c = '\n'; // translate \r to \n 1697 c = '\n'; // translate \r to \n
1698#if ENABLE_FEATURE_VI_SETOPTS
1698 sp = p; // remember addr of insert 1699 sp = p; // remember addr of insert
1700#endif
1699 p += 1 + stupid_insert(p, c); // insert the char 1701 p += 1 + stupid_insert(p, c); // insert the char
1700#if ENABLE_FEATURE_VI_SETOPTS 1702#if ENABLE_FEATURE_VI_SETOPTS
1701 if (showmatch && strchr(")]}", *sp) != NULL) { 1703 if (showmatch && strchr(")]}", *sp) != NULL) {
diff --git a/examples/udhcp/udhcpd.conf b/examples/udhcp/udhcpd.conf
index 23fc834b7..cd2957ccc 100644
--- a/examples/udhcp/udhcpd.conf
+++ b/examples/udhcp/udhcpd.conf
@@ -14,11 +14,6 @@ interface eth0
14# smaller than lease block. 14# smaller than lease block.
15#max_leases 254 15#max_leases 254
16 16
17# The time period at which udhcpd will write out a dhcpd.leases
18# file. If this is 0, udhcpd will never automatically write a
19# lease file. Specified in seconds.
20#auto_time 7200
21
22# The amount of time that an IP will be reserved (leased to nobody) 17# The amount of time that an IP will be reserved (leased to nobody)
23# if a DHCP decline message is received (seconds) 18# if a DHCP decline message is received (seconds)
24#decline_time 3600 19#decline_time 3600
@@ -34,11 +29,16 @@ interface eth0
34# to this value (seconds) 29# to this value (seconds)
35#min_lease 60 30#min_lease 60
36 31
32# The location of the pid file
33#pidfile /var/run/udhcpd.pid
34
37# The location of the leases file 35# The location of the leases file
38#lease_file /var/lib/misc/udhcpd.leases 36#lease_file /var/lib/misc/udhcpd.leases
39 37
40# The location of the pid file 38# The time period at which udhcpd will write out leases file.
41#pidfile /var/run/udhcpd.pid 39# If this is 0, udhcpd will never automatically write leases file.
40# Specified in seconds.
41#auto_time 7200
42 42
43# Every time udhcpd writes a leases file, the below script will be called 43# Every time udhcpd writes a leases file, the below script will be called
44#notify_file # default: no script 44#notify_file # default: no script
@@ -90,6 +90,8 @@ option 0x08 01020304 # option 8: "cookie server IP addr: 1.2.3.4"
90#opt wpad STRING 90#opt wpad STRING
91#opt serverid IP # default: server's IP 91#opt serverid IP # default: server's IP
92#opt message STRING # error message (udhcpd sends it on success too) 92#opt message STRING # error message (udhcpd sends it on success too)
93#opt vlanid NUM # 802.1P VLAN ID
94#opt vlanpriority NUM # 802.1Q VLAN priority
93# Options specifying server(s) 95# Options specifying server(s)
94#opt dns IP_LIST 96#opt dns IP_LIST
95#opt wins IP_LIST 97#opt wins IP_LIST
@@ -97,8 +99,15 @@ option 0x08 01020304 # option 8: "cookie server IP addr: 1.2.3.4"
97#opt ntpsrv IP_LIST 99#opt ntpsrv IP_LIST
98#opt lprsrv IP_LIST 100#opt lprsrv IP_LIST
99#opt swapsrv IP 101#opt swapsrv IP
102# Options specifying routes
103#opt routes IP_PAIR_LIST
100# Obsolete options, no longer supported 104# Obsolete options, no longer supported
101#opt logsrv IP_LIST # 704/UDP log server (not syslog!) 105#opt logsrv IP_LIST # 704/UDP log server (not syslog!)
102#opt namesrv IP_LIST # IEN 116 name server, obsolete (August 1979!!!) 106#opt namesrv IP_LIST # IEN 116 name server, obsolete (August 1979!!!)
103#opt cookiesrv IP_LIST # RFC 865 "quote of the day" server, rarely (never?) used 107#opt cookiesrv IP_LIST # RFC 865 "quote of the day" server, rarely (never?) used
104#opt timesrv IP_LIST # RFC 868 time server, rarely (never?) used 108#opt timesrv IP_LIST # RFC 868 time server, rarely (never?) used
109# TODO: in development
110#opt userclass STRING # RFC 3004. set of LASCII strings. "I am a printer" etc
111#opt sipserv STRING LIST # RFC 3361. flag byte, then: 0: domain names, 1: IP addrs
112#opt staticroutes STATIC_ROUTES
113#opt msstaticroutes STATIC_ROUTES
diff --git a/findutils/find.c b/findutils/find.c
index 9ae84fa0d..050d6373e 100644
--- a/findutils/find.c
+++ b/findutils/find.c
@@ -264,6 +264,7 @@
264//usage: "\n -iname PATTERN Case insensitive -name" 264//usage: "\n -iname PATTERN Case insensitive -name"
265//usage: IF_FEATURE_FIND_PATH( 265//usage: IF_FEATURE_FIND_PATH(
266//usage: "\n -path PATTERN Match path to PATTERN" 266//usage: "\n -path PATTERN Match path to PATTERN"
267//usage: "\n -ipath PATTERN Case insensitive -path"
267//usage: ) 268//usage: )
268//usage: IF_FEATURE_FIND_REGEX( 269//usage: IF_FEATURE_FIND_REGEX(
269//usage: "\n -regex PATTERN Match path to regex PATTERN" 270//usage: "\n -regex PATTERN Match path to regex PATTERN"
@@ -329,7 +330,11 @@
329#include <fnmatch.h> 330#include <fnmatch.h>
330#include "libbb.h" 331#include "libbb.h"
331#if ENABLE_FEATURE_FIND_REGEX 332#if ENABLE_FEATURE_FIND_REGEX
332#include "xregex.h" 333# include "xregex.h"
334#endif
335/* GNUism: */
336#ifndef FNM_CASEFOLD
337# define FNM_CASEFOLD 0
333#endif 338#endif
334 339
335/* This is a NOEXEC applet. Be very careful! */ 340/* This is a NOEXEC applet. Be very careful! */
@@ -352,7 +357,7 @@ typedef struct {
352 357
353 ACTS(print) 358 ACTS(print)
354 ACTS(name, const char *pattern; bool iname;) 359 ACTS(name, const char *pattern; bool iname;)
355IF_FEATURE_FIND_PATH( ACTS(path, const char *pattern;)) 360IF_FEATURE_FIND_PATH( ACTS(path, const char *pattern; bool ipath;))
356IF_FEATURE_FIND_REGEX( ACTS(regex, regex_t compiled_pattern;)) 361IF_FEATURE_FIND_REGEX( ACTS(regex, regex_t compiled_pattern;))
357IF_FEATURE_FIND_PRINT0( ACTS(print0)) 362IF_FEATURE_FIND_PRINT0( ACTS(print0))
358IF_FEATURE_FIND_TYPE( ACTS(type, int type_mask;)) 363IF_FEATURE_FIND_TYPE( ACTS(type, int type_mask;))
@@ -473,6 +478,22 @@ static int exec_actions(action ***appp, const char *fileName, const struct stat
473} 478}
474 479
475 480
481#if !FNM_CASEFOLD
482static char *strcpy_upcase(char *dst, const char *src)
483{
484 char *d = dst;
485 while (1) {
486 unsigned char ch = *src++;
487 if (ch >= 'a' && ch <= 'z')
488 ch -= ('a' - 'A');
489 *d++ = ch;
490 if (ch == '\0')
491 break;
492 }
493 return dst;
494}
495#endif
496
476ACTF(name) 497ACTF(name)
477{ 498{
478 const char *tmp = bb_basename(fileName); 499 const char *tmp = bb_basename(fileName);
@@ -488,13 +509,25 @@ ACTF(name)
488 * but somewhere between 4.1.20 and 4.4.0 GNU find stopped using it. 509 * but somewhere between 4.1.20 and 4.4.0 GNU find stopped using it.
489 * find -name '*foo' should match .foo too: 510 * find -name '*foo' should match .foo too:
490 */ 511 */
512#if FNM_CASEFOLD
491 return fnmatch(ap->pattern, tmp, (ap->iname ? FNM_CASEFOLD : 0)) == 0; 513 return fnmatch(ap->pattern, tmp, (ap->iname ? FNM_CASEFOLD : 0)) == 0;
514#else
515 if (ap->iname)
516 tmp = strcpy_upcase(alloca(strlen(tmp) + 1), tmp);
517 return fnmatch(ap->pattern, tmp, 0) == 0;
518#endif
492} 519}
493 520
494#if ENABLE_FEATURE_FIND_PATH 521#if ENABLE_FEATURE_FIND_PATH
495ACTF(path) 522ACTF(path)
496{ 523{
524# if FNM_CASEFOLD
525 return fnmatch(ap->pattern, fileName, (ap->ipath ? FNM_CASEFOLD : 0)) == 0;
526# else
527 if (ap->ipath)
528 fileName = strcpy_upcase(alloca(strlen(fileName) + 1), fileName);
497 return fnmatch(ap->pattern, fileName, 0) == 0; 529 return fnmatch(ap->pattern, fileName, 0) == 0;
530# endif
498} 531}
499#endif 532#endif
500#if ENABLE_FEATURE_FIND_REGEX 533#if ENABLE_FEATURE_FIND_REGEX
@@ -794,6 +827,7 @@ static action*** parse_params(char **argv)
794 PARM_name , 827 PARM_name ,
795 PARM_iname , 828 PARM_iname ,
796 IF_FEATURE_FIND_PATH( PARM_path ,) 829 IF_FEATURE_FIND_PATH( PARM_path ,)
830 IF_FEATURE_FIND_PATH( PARM_ipath ,)
797 IF_FEATURE_FIND_REGEX( PARM_regex ,) 831 IF_FEATURE_FIND_REGEX( PARM_regex ,)
798 IF_FEATURE_FIND_TYPE( PARM_type ,) 832 IF_FEATURE_FIND_TYPE( PARM_type ,)
799 IF_FEATURE_FIND_PERM( PARM_perm ,) 833 IF_FEATURE_FIND_PERM( PARM_perm ,)
@@ -831,6 +865,7 @@ static action*** parse_params(char **argv)
831 "-name\0" 865 "-name\0"
832 "-iname\0" 866 "-iname\0"
833 IF_FEATURE_FIND_PATH( "-path\0" ) 867 IF_FEATURE_FIND_PATH( "-path\0" )
868 IF_FEATURE_FIND_PATH( "-ipath\0" )
834 IF_FEATURE_FIND_REGEX( "-regex\0" ) 869 IF_FEATURE_FIND_REGEX( "-regex\0" )
835 IF_FEATURE_FIND_TYPE( "-type\0" ) 870 IF_FEATURE_FIND_TYPE( "-type\0" )
836 IF_FEATURE_FIND_PERM( "-perm\0" ) 871 IF_FEATURE_FIND_PERM( "-perm\0" )
@@ -1018,10 +1053,11 @@ static action*** parse_params(char **argv)
1018 ap->iname = (parm == PARM_iname); 1053 ap->iname = (parm == PARM_iname);
1019 } 1054 }
1020#if ENABLE_FEATURE_FIND_PATH 1055#if ENABLE_FEATURE_FIND_PATH
1021 else if (parm == PARM_path) { 1056 else if (parm == PARM_path || parm == PARM_ipath) {
1022 action_path *ap; 1057 action_path *ap;
1023 ap = ALLOC_ACTION(path); 1058 ap = ALLOC_ACTION(path);
1024 ap->pattern = arg1; 1059 ap->pattern = arg1;
1060 ap->ipath = (parm == PARM_ipath);
1025 } 1061 }
1026#endif 1062#endif
1027#if ENABLE_FEATURE_FIND_REGEX 1063#if ENABLE_FEATURE_FIND_REGEX
diff --git a/include/archive.h b/include/archive.h
index b139dc5be..9e176d335 100644
--- a/include/archive.h
+++ b/include/archive.h
@@ -125,7 +125,7 @@ typedef struct archive_handle_t {
125#define TAR_BLOCK_SIZE 512 125#define TAR_BLOCK_SIZE 512
126#define NAME_SIZE 100 126#define NAME_SIZE 100
127#define NAME_SIZE_STR "100" 127#define NAME_SIZE_STR "100"
128typedef struct tar_header_t { /* byte offset */ 128typedef struct tar_header_t { /* byte offset */
129 char name[NAME_SIZE]; /* 0-99 */ 129 char name[NAME_SIZE]; /* 0-99 */
130 char mode[8]; /* 100-107 */ 130 char mode[8]; /* 100-107 */
131 char uid[8]; /* 108-115 */ 131 char uid[8]; /* 108-115 */
diff --git a/include/libbb.h b/include/libbb.h
index 4f46cf8c5..ac5722c89 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -20,6 +20,12 @@
20#include <netdb.h> 20#include <netdb.h>
21#include <setjmp.h> 21#include <setjmp.h>
22#include <signal.h> 22#include <signal.h>
23#if defined __UCLIBC__ /* TODO: and glibc? */
24/* use inlined versions of these: */
25# define sigfillset(s) __sigfillset(s)
26# define sigemptyset(s) __sigemptyset(s)
27# define sigisemptyset(s) __sigisemptyset(s)
28#endif
23#include <stdint.h> 29#include <stdint.h>
24#include <stdio.h> 30#include <stdio.h>
25#include <stdlib.h> 31#include <stdlib.h>
@@ -363,6 +369,7 @@ extern void bb_copyfd_exact_size(int fd1, int fd2, off_t size) FAST_FUNC;
363/* "short" copy can be detected by return value < size */ 369/* "short" copy can be detected by return value < size */
364/* this helper yells "short read!" if param is not -1 */ 370/* this helper yells "short read!" if param is not -1 */
365extern void complain_copyfd_and_die(off_t sz) NORETURN FAST_FUNC; 371extern void complain_copyfd_and_die(off_t sz) NORETURN FAST_FUNC;
372
366extern char bb_process_escape_sequence(const char **ptr) FAST_FUNC; 373extern char bb_process_escape_sequence(const char **ptr) FAST_FUNC;
367char* strcpy_and_process_escape_sequences(char *dst, const char *src) FAST_FUNC; 374char* strcpy_and_process_escape_sequences(char *dst, const char *src) FAST_FUNC;
368/* xxxx_strip version can modify its parameter: 375/* xxxx_strip version can modify its parameter:
@@ -371,9 +378,13 @@ char* strcpy_and_process_escape_sequences(char *dst, const char *src) FAST_FUNC;
371 * "abc/def" -> "def" 378 * "abc/def" -> "def"
372 * "abc/def/" -> "def" !! 379 * "abc/def/" -> "def" !!
373 */ 380 */
374extern char *bb_get_last_path_component_strip(char *path) FAST_FUNC; 381char *bb_get_last_path_component_strip(char *path) FAST_FUNC;
375/* "abc/def/" -> "" and it never modifies 'path' */ 382/* "abc/def/" -> "" and it never modifies 'path' */
376extern char *bb_get_last_path_component_nostrip(const char *path) FAST_FUNC; 383char *bb_get_last_path_component_nostrip(const char *path) FAST_FUNC;
384/* Simpler version: does not special case "/" string */
385const char *bb_basename(const char *name) FAST_FUNC;
386/* NB: can violate const-ness (similarly to strchr) */
387char *last_char_is(const char *s, int c) FAST_FUNC;
377 388
378void ndelay_on(int fd) FAST_FUNC; 389void ndelay_on(int fd) FAST_FUNC;
379void ndelay_off(int fd) FAST_FUNC; 390void ndelay_off(int fd) FAST_FUNC;
@@ -685,7 +696,7 @@ void* xrealloc_vector_helper(void *vector, unsigned sizeof_and_shift, int idx) F
685 696
686 697
687extern ssize_t safe_read(int fd, void *buf, size_t count) FAST_FUNC; 698extern ssize_t safe_read(int fd, void *buf, size_t count) FAST_FUNC;
688extern ssize_t nonblock_safe_read(int fd, void *buf, size_t count) FAST_FUNC; 699extern ssize_t nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR) FAST_FUNC;
689// NB: will return short read on error, not -1, 700// NB: will return short read on error, not -1,
690// if some data was read before error occurred 701// if some data was read before error occurred
691extern ssize_t full_read(int fd, void *buf, size_t count) FAST_FUNC; 702extern ssize_t full_read(int fd, void *buf, size_t count) FAST_FUNC;
@@ -696,7 +707,7 @@ extern ssize_t open_read_close(const char *filename, void *buf, size_t maxsz) FA
696// Reads one line a-la fgets (but doesn't save terminating '\n'). 707// Reads one line a-la fgets (but doesn't save terminating '\n').
697// Reads byte-by-byte. Useful when it is important to not read ahead. 708// Reads byte-by-byte. Useful when it is important to not read ahead.
698// Bytes are appended to pfx (which must be malloced, or NULL). 709// Bytes are appended to pfx (which must be malloced, or NULL).
699extern char *xmalloc_reads(int fd, char *pfx, size_t *maxsz_p) FAST_FUNC; 710extern char *xmalloc_reads(int fd, size_t *maxsz_p) FAST_FUNC;
700/* Reads block up to *maxsz_p (default: INT_MAX - 4095) */ 711/* Reads block up to *maxsz_p (default: INT_MAX - 4095) */
701extern void *xmalloc_read(int fd, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC; 712extern void *xmalloc_read(int fd, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC;
702/* Returns NULL if file can't be opened (default max size: INT_MAX - 4095) */ 713/* Returns NULL if file can't be opened (default max size: INT_MAX - 4095) */
@@ -997,9 +1008,13 @@ extern uint32_t option_mask32;
997extern uint32_t getopt32(char **argv, const char *applet_opts, ...) FAST_FUNC; 1008extern uint32_t getopt32(char **argv, const char *applet_opts, ...) FAST_FUNC;
998 1009
999 1010
1011/* Having next pointer as a first member allows easy creation
1012 * of "llist-compatible" structs, and using llist_FOO functions
1013 * on them.
1014 */
1000typedef struct llist_t { 1015typedef struct llist_t {
1001 char *data;
1002 struct llist_t *link; 1016 struct llist_t *link;
1017 char *data;
1003} llist_t; 1018} llist_t;
1004void llist_add_to(llist_t **old_head, void *data) FAST_FUNC; 1019void llist_add_to(llist_t **old_head, void *data) FAST_FUNC;
1005void llist_add_to_end(llist_t **list_head, void *data) FAST_FUNC; 1020void llist_add_to_end(llist_t **list_head, void *data) FAST_FUNC;
@@ -1198,10 +1213,8 @@ void config_close(parser_t *parser) FAST_FUNC;
1198 * If path is NULL, it is assumed to be "/". 1213 * If path is NULL, it is assumed to be "/".
1199 * filename should not be NULL. */ 1214 * filename should not be NULL. */
1200char *concat_path_file(const char *path, const char *filename) FAST_FUNC; 1215char *concat_path_file(const char *path, const char *filename) FAST_FUNC;
1216/* Returns NULL on . and .. */
1201char *concat_subpath_file(const char *path, const char *filename) FAST_FUNC; 1217char *concat_subpath_file(const char *path, const char *filename) FAST_FUNC;
1202const char *bb_basename(const char *name) FAST_FUNC;
1203/* NB: can violate const-ness (similarly to strchr) */
1204char *last_char_is(const char *s, int c) FAST_FUNC;
1205 1218
1206 1219
1207int bb_make_directory(char *path, long mode, int flags) FAST_FUNC; 1220int bb_make_directory(char *path, long mode, int flags) FAST_FUNC;
@@ -1269,14 +1282,19 @@ extern int correct_password(const struct passwd *pw) FAST_FUNC;
1269#endif 1282#endif
1270extern char *pw_encrypt(const char *clear, const char *salt, int cleanup) FAST_FUNC; 1283extern char *pw_encrypt(const char *clear, const char *salt, int cleanup) FAST_FUNC;
1271extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) FAST_FUNC; 1284extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) FAST_FUNC;
1272/* rnd is additional random input. New one is returned. 1285/*
1286 * rnd is additional random input. New one is returned.
1273 * Useful if you call crypt_make_salt many times in a row: 1287 * Useful if you call crypt_make_salt many times in a row:
1274 * rnd = crypt_make_salt(buf1, 4, 0); 1288 * rnd = crypt_make_salt(buf1, 4, 0);
1275 * rnd = crypt_make_salt(buf2, 4, rnd); 1289 * rnd = crypt_make_salt(buf2, 4, rnd);
1276 * rnd = crypt_make_salt(buf3, 4, rnd); 1290 * rnd = crypt_make_salt(buf3, 4, rnd);
1277 * (otherwise we risk having same salt generated) 1291 * (otherwise we risk having same salt generated)
1278 */ 1292 */
1279extern int crypt_make_salt(char *p, int cnt, int rnd) FAST_FUNC; 1293extern int crypt_make_salt(char *p, int cnt /*, int rnd*/) FAST_FUNC;
1294/* "$N$" + sha_salt_16_bytes + NUL */
1295#define MAX_PW_SALT_LEN (3 + 16 + 1)
1296extern char* crypt_make_pw_salt(char p[MAX_PW_SALT_LEN], const char *algo) FAST_FUNC;
1297
1280 1298
1281/* Returns number of lines changed, or -1 on error */ 1299/* Returns number of lines changed, or -1 on error */
1282#if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP) 1300#if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP)
diff --git a/include/mingw.h b/include/mingw.h
index 49de6cb5e..69bf05574 100644
--- a/include/mingw.h
+++ b/include/mingw.h
@@ -209,6 +209,8 @@ NOIMPL(mingw_bind,SOCKET s UNUSED_PARAM,const struct sockaddr* sa UNUSED_PARAM,i
209/* 209/*
210 * sys/stat.h 210 * sys/stat.h
211 */ 211 */
212typedef int nlink_t;
213
212#define S_ISUID 04000 214#define S_ISUID 04000
213#define S_ISGID 02000 215#define S_ISGID 02000
214#define S_ISVTX 01000 216#define S_ISVTX 01000
diff --git a/include/platform.h b/include/platform.h
index eb9f8032b..48e256aa0 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -17,24 +17,6 @@
17# endif 17# endif
18#endif 18#endif
19 19
20/* Assume all these functions exist by default. Platforms where it is not
21 * true will #undef them below.
22 */
23#define HAVE_CLEARENV 1
24#define HAVE_FDATASYNC 1
25#define HAVE_DPRINTF 1
26#define HAVE_MEMRCHR 1
27#define HAVE_MKDTEMP 1
28#define HAVE_PTSNAME_R 1
29#define HAVE_SETBIT 1
30#define HAVE_SIGHANDLER_T 1
31#define HAVE_STPCPY 1
32#define HAVE_STRCASESTR 1
33#define HAVE_STRCHRNUL 1
34#define HAVE_STRSEP 1
35#define HAVE_STRSIGNAL 1
36#define HAVE_VASPRINTF 1
37
38/* Convenience macros to test the version of gcc. */ 20/* Convenience macros to test the version of gcc. */
39#undef __GNUC_PREREQ 21#undef __GNUC_PREREQ
40#if defined __GNUC__ && defined __GNUC_MINOR__ 22#if defined __GNUC__ && defined __GNUC_MINOR__
@@ -51,10 +33,6 @@
51# endif 33# endif
52#endif 34#endif
53 35
54/* Define macros for some gcc attributes. This permits us to use the
55 macros freely, and know that they will come into play for the
56 version of gcc in which they are supported. */
57
58#if !__GNUC_PREREQ(2,7) 36#if !__GNUC_PREREQ(2,7)
59# ifndef __attribute__ 37# ifndef __attribute__
60# define __attribute__(x) 38# define __attribute__(x)
@@ -108,7 +86,7 @@
108#endif 86#endif
109 87
110/* -fwhole-program makes all symbols local. The attribute externally_visible 88/* -fwhole-program makes all symbols local. The attribute externally_visible
111 forces a symbol global. */ 89 * forces a symbol global. */
112#if __GNUC_PREREQ(4,1) 90#if __GNUC_PREREQ(4,1)
113# define EXTERNALLY_VISIBLE __attribute__(( visibility("default") )) 91# define EXTERNALLY_VISIBLE __attribute__(( visibility("default") ))
114//__attribute__ ((__externally_visible__)) 92//__attribute__ ((__externally_visible__))
@@ -124,22 +102,14 @@
124#endif 102#endif
125 103
126/* We use __extension__ in some places to suppress -pedantic warnings 104/* We use __extension__ in some places to suppress -pedantic warnings
127 about GCC extensions. This feature didn't work properly before 105 * about GCC extensions. This feature didn't work properly before
128 gcc 2.8. */ 106 * gcc 2.8. */
129#if !__GNUC_PREREQ(2,8) 107#if !__GNUC_PREREQ(2,8)
130# ifndef __extension__ 108# ifndef __extension__
131# define __extension__ 109# define __extension__
132# endif 110# endif
133#endif 111#endif
134 112
135/* gcc-2.95 had no va_copy but only __va_copy. */
136#if !__GNUC_PREREQ(3,0)
137# include <stdarg.h>
138# if !defined va_copy && defined __va_copy
139# define va_copy(d,s) __va_copy((d),(s))
140# endif
141#endif
142
143/* FAST_FUNC is a qualifier which (possibly) makes function call faster 113/* FAST_FUNC is a qualifier which (possibly) makes function call faster
144 * and/or smaller by using modified ABI. It is usually only needed 114 * and/or smaller by using modified ABI. It is usually only needed
145 * on non-static, busybox internal functions. Recent versions of gcc 115 * on non-static, busybox internal functions. Recent versions of gcc
@@ -163,6 +133,15 @@
163# define POP_SAVED_FUNCTION_VISIBILITY 133# define POP_SAVED_FUNCTION_VISIBILITY
164#endif 134#endif
165 135
136/* gcc-2.95 had no va_copy but only __va_copy. */
137#if !__GNUC_PREREQ(3,0)
138# include <stdarg.h>
139# if !defined va_copy && defined __va_copy
140# define va_copy(d,s) __va_copy((d),(s))
141# endif
142#endif
143
144
166/* ---- Endian Detection ------------------------------------ */ 145/* ---- Endian Detection ------------------------------------ */
167 146
168#include <limits.h> 147#include <limits.h>
@@ -238,6 +217,7 @@
238# define IF_LITTLE_ENDIAN(...) __VA_ARGS__ 217# define IF_LITTLE_ENDIAN(...) __VA_ARGS__
239#endif 218#endif
240 219
220
241/* ---- Unaligned access ------------------------------------ */ 221/* ---- Unaligned access ------------------------------------ */
242 222
243#include <stdint.h> 223#include <stdint.h>
@@ -270,37 +250,9 @@ typedef uint32_t bb__aliased_uint32_t FIX_ALIASING;
270} while (0) 250} while (0)
271#endif 251#endif
272 252
273/* ---- Compiler dependent settings ------------------------- */
274 253
275#if (defined __digital__ && defined __unix__) \ 254/* ---- Size-saving "small" ints (arch-dependent) ----------- */
276 || defined __APPLE__ \
277 || defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__
278# undef HAVE_CLEARENV
279# undef HAVE_FDATASYNC
280# undef HAVE_MNTENT_H
281# undef HAVE_PTSNAME_R
282# undef HAVE_SYS_STATFS_H
283# undef HAVE_SIGHANDLER_T
284# undef HAVE_XTABS
285# undef HAVE_DPRINTF
286#else
287# define HAVE_MNTENT_H 1
288# define HAVE_SYS_STATFS_H 1
289# define HAVE_XTABS 1
290#endif
291
292/*----- Kernel versioning ------------------------------------*/
293
294#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
295
296/* ---- Miscellaneous --------------------------------------- */
297 255
298#if defined __GLIBC__ || defined __UCLIBC__ \
299 || defined __dietlibc__ || defined _NEWLIB_VERSION
300# include <features.h>
301#endif
302
303/* Size-saving "small" ints (arch-dependent) */
304#if defined(i386) || defined(__x86_64__) || defined(__mips__) || defined(__cris__) 256#if defined(i386) || defined(__x86_64__) || defined(__mips__) || defined(__cris__)
305/* add other arches which benefit from this... */ 257/* add other arches which benefit from this... */
306typedef signed char smallint; 258typedef signed char smallint;
@@ -320,7 +272,34 @@ typedef unsigned smalluint;
320# include <stdbool.h> 272# include <stdbool.h>
321#endif 273#endif
322 274
323/* Try to defeat gcc's alignment of "char message[]"-like data */ 275
276/*----- Kernel versioning ------------------------------------*/
277
278#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
279
280
281/* ---- Miscellaneous --------------------------------------- */
282
283#if defined __GLIBC__ \
284 || defined __UCLIBC__ \
285 || defined __dietlibc__ \
286 || defined _NEWLIB_VERSION
287# include <features.h>
288#endif
289
290/* Define bb_setpgrp */
291#if defined(__digital__) && defined(__unix__)
292/* use legacy setpgrp(pid_t, pid_t) for now. move to platform.c */
293# define bb_setpgrp() do { pid_t __me = getpid(); setpgrp(__me, __me); } while (0)
294#else
295# define bb_setpgrp() setpgrp()
296#endif
297
298/* fdprintf is more readable, we used it before dprintf was standardized */
299#include <unistd.h>
300#define fdprintf dprintf
301
302/* Useful for defeating gcc's alignment of "char message[]"-like data */
324#if 1 /* if needed: !defined(arch1) && !defined(arch2) */ 303#if 1 /* if needed: !defined(arch1) && !defined(arch2) */
325# define ALIGN1 __attribute__((aligned(1))) 304# define ALIGN1 __attribute__((aligned(1)))
326# define ALIGN2 __attribute__((aligned(2))) 305# define ALIGN2 __attribute__((aligned(2)))
@@ -332,8 +311,7 @@ typedef unsigned smalluint;
332# define ALIGN4 311# define ALIGN4
333#endif 312#endif
334 313
335 314/*
336/* uclibc does not implement daemon() for no-mmu systems.
337 * For 0.9.29 and svn, __ARCH_USE_MMU__ indicates no-mmu reliably. 315 * For 0.9.29 and svn, __ARCH_USE_MMU__ indicates no-mmu reliably.
338 * For earlier versions there is no reliable way to check if we are building 316 * For earlier versions there is no reliable way to check if we are building
339 * for a mmu-less system. 317 * for a mmu-less system.
@@ -351,12 +329,9 @@ typedef unsigned smalluint;
351#endif 329#endif
352 330
353#if defined(__digital__) && defined(__unix__) 331#if defined(__digital__) && defined(__unix__)
354
355# include <standards.h> 332# include <standards.h>
356# include <inttypes.h> 333# include <inttypes.h>
357# define PRIu32 "u" 334# define PRIu32 "u"
358/* use legacy setpgrp(pid_t,pid_t) for now. move to platform.c */
359# define bb_setpgrp() do { pid_t __me = getpid(); setpgrp(__me, __me); } while (0)
360# if !defined ADJ_OFFSET_SINGLESHOT && defined MOD_CLKA && defined MOD_OFFSET 335# if !defined ADJ_OFFSET_SINGLESHOT && defined MOD_CLKA && defined MOD_OFFSET
361# define ADJ_OFFSET_SINGLESHOT (MOD_CLKA | MOD_OFFSET) 336# define ADJ_OFFSET_SINGLESHOT (MOD_CLKA | MOD_OFFSET)
362# endif 337# endif
@@ -369,17 +344,31 @@ typedef unsigned smalluint;
369# if !defined ADJ_TICK && defined MOD_CLKB 344# if !defined ADJ_TICK && defined MOD_CLKB
370# define ADJ_TICK MOD_CLKB 345# define ADJ_TICK MOD_CLKB
371# endif 346# endif
347#endif
372 348
373# undef HAVE_STPCPY
374
375#else
376
377# define bb_setpgrp() setpgrp()
378 349
379#endif 350/* ---- Who misses what? ------------------------------------ */
380 351
381#include <unistd.h> 352/* Assume all these functions exist by default. Platforms where it is not
382#define fdprintf dprintf 353 * true will #undef them below.
354 */
355#define HAVE_CLEARENV 1
356#define HAVE_FDATASYNC 1
357#define HAVE_DPRINTF 1
358#define HAVE_MEMRCHR 1
359#define HAVE_MKDTEMP 1
360#define HAVE_PTSNAME_R 1
361#define HAVE_SETBIT 1
362#define HAVE_SIGHANDLER_T 1
363#define HAVE_STPCPY 1
364#define HAVE_STRCASESTR 1
365#define HAVE_STRCHRNUL 1
366#define HAVE_STRSEP 1
367#define HAVE_STRSIGNAL 1
368#define HAVE_VASPRINTF 1
369#define HAVE_MNTENT_H 1
370#define HAVE_SYS_STATFS_H 1
371#define HAVE_XTABS 1
383 372
384#if defined(__dietlibc__) 373#if defined(__dietlibc__)
385# undef HAVE_STRCHRNUL 374# undef HAVE_STRCHRNUL
@@ -416,6 +405,23 @@ typedef unsigned smalluint;
416# undef HAVE_STRCHRNUL 405# undef HAVE_STRCHRNUL
417#endif 406#endif
418 407
408#if (defined __digital__ && defined __unix__) \
409 || defined __APPLE__ \
410 || defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__
411# undef HAVE_CLEARENV
412# undef HAVE_FDATASYNC
413# undef HAVE_MNTENT_H
414# undef HAVE_PTSNAME_R
415# undef HAVE_SYS_STATFS_H
416# undef HAVE_SIGHANDLER_T
417# undef HAVE_XTABS
418# undef HAVE_DPRINTF
419#endif
420
421#if defined(__digital__) && defined(__unix__)
422# undef HAVE_STPCPY
423#endif
424
419/* 425/*
420 * Now, define prototypes for all the functions defined in platform.c 426 * Now, define prototypes for all the functions defined in platform.c
421 * These must come after all the HAVE_* macros are defined (or not) 427 * These must come after all the HAVE_* macros are defined (or not)
diff --git a/libbb/Kbuild.src b/libbb/Kbuild.src
index 3c9493f7e..d110a3265 100644
--- a/libbb/Kbuild.src
+++ b/libbb/Kbuild.src
@@ -13,7 +13,6 @@ INSERT
13lib-y += appletlib.o 13lib-y += appletlib.o
14lib-y += ask_confirmation.o 14lib-y += ask_confirmation.o
15lib-y += bb_askpass.o 15lib-y += bb_askpass.o
16lib-y += bb_basename.o
17lib-y += bb_bswap_64.o 16lib-y += bb_bswap_64.o
18lib-y += bb_do_delay.o 17lib-y += bb_do_delay.o
19lib-y += bb_pwd.o 18lib-y += bb_pwd.o
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index a934f11bb..d4025b9c7 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -27,12 +27,13 @@
27 * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :( 27 * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :(
28 */ 28 */
29#include "busybox.h" 29#include "busybox.h"
30#include <assert.h> 30
31#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ 31#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
32 || defined(__APPLE__) \ 32 || defined(__APPLE__) \
33 ) 33 )
34# include <malloc.h> /* for mallopt */ 34# include <malloc.h> /* for mallopt */
35#endif 35#endif
36
36/* Try to pull in PAGE_SIZE */ 37/* Try to pull in PAGE_SIZE */
37#ifdef __linux__ 38#ifdef __linux__
38# include <sys/user.h> 39# include <sys/user.h>
@@ -40,6 +41,9 @@
40#ifdef __GNU__ /* Hurd */ 41#ifdef __GNU__ /* Hurd */
41# include <mach/vm_param.h> 42# include <mach/vm_param.h>
42#endif 43#endif
44#ifndef PAGE_SIZE
45# define PAGE_SIZE (4*1024) /* guess */
46#endif
43 47
44 48
45/* Declare <applet>_main() */ 49/* Declare <applet>_main() */
@@ -47,7 +51,6 @@
47#include "applets.h" 51#include "applets.h"
48#undef PROTOTYPES 52#undef PROTOTYPES
49 53
50
51/* Include generated applet names, pointers to <applet>_main, etc */ 54/* Include generated applet names, pointers to <applet>_main, etc */
52#include "applet_tables.h" 55#include "applet_tables.h"
53/* ...and if applet_tables generator says we have only one applet... */ 56/* ...and if applet_tables generator says we have only one applet... */
@@ -58,9 +61,9 @@
58# define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__ 61# define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__
59#endif 62#endif
60 63
61
62#include "usage_compressed.h" 64#include "usage_compressed.h"
63 65
66
64#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE 67#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE
65static const char usage_messages[] ALIGN1 = UNPACKED_USAGE; 68static const char usage_messages[] ALIGN1 = UNPACKED_USAGE;
66#else 69#else
@@ -233,13 +236,12 @@ IF_FEATURE_SUID(static uid_t ruid;) /* real uid */
233 236
234# if ENABLE_FEATURE_SUID_CONFIG 237# if ENABLE_FEATURE_SUID_CONFIG
235 238
236/* applets[] is const, so we have to define this "override" structure */ 239static struct suid_config_t {
237static struct BB_suid_config { 240 /* next ptr must be first: this struct needs to be llist-compatible */
241 struct suid_config_t *m_next;
242 struct bb_uidgid_t m_ugid;
238 int m_applet; 243 int m_applet;
239 uid_t m_uid;
240 gid_t m_gid;
241 mode_t m_mode; 244 mode_t m_mode;
242 struct BB_suid_config *m_next;
243} *suid_config; 245} *suid_config;
244 246
245static bool suid_cfg_readable; 247static bool suid_cfg_readable;
@@ -248,13 +250,10 @@ static bool suid_cfg_readable;
248static int ingroup(uid_t u, gid_t g) 250static int ingroup(uid_t u, gid_t g)
249{ 251{
250 struct group *grp = getgrgid(g); 252 struct group *grp = getgrgid(g);
251
252 if (grp) { 253 if (grp) {
253 char **mem; 254 char **mem;
254
255 for (mem = grp->gr_mem; *mem; mem++) { 255 for (mem = grp->gr_mem; *mem; mem++) {
256 struct passwd *pwd = getpwnam(*mem); 256 struct passwd *pwd = getpwnam(*mem);
257
258 if (pwd && (pwd->pw_uid == u)) 257 if (pwd && (pwd->pw_uid == u))
259 return 1; 258 return 1;
260 } 259 }
@@ -262,9 +261,7 @@ static int ingroup(uid_t u, gid_t g)
262 return 0; 261 return 0;
263} 262}
264 263
265/* This should probably be a libbb routine. In that case, 264/* libbb candidate */
266 * I'd probably rename it to something like bb_trimmed_slice.
267 */
268static char *get_trimmed_slice(char *s, char *e) 265static char *get_trimmed_slice(char *s, char *e)
269{ 266{
270 /* First, consider the value at e to be nul and back up until we 267 /* First, consider the value at e to be nul and back up until we
@@ -282,38 +279,19 @@ static char *get_trimmed_slice(char *s, char *e)
282 return skip_whitespace(s); 279 return skip_whitespace(s);
283} 280}
284 281
285/* Don't depend on the tools to combine strings. */
286static const char config_file[] ALIGN1 = "/etc/busybox.conf";
287
288/* We don't supply a value for the nul, so an index adjustment is
289 * necessary below. Also, we use unsigned short here to save some
290 * space even though these are really mode_t values. */
291static const unsigned short mode_mask[] ALIGN2 = {
292 /* SST sst xxx --- */
293 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */
294 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */
295 0, S_IXOTH, S_IXOTH, 0 /* other */
296};
297
298#define parse_error(x) do { errmsg = x; goto pe_label; } while (0)
299
300static void parse_config_file(void) 282static void parse_config_file(void)
301{ 283{
302 struct BB_suid_config *sct_head; 284 /* Don't depend on the tools to combine strings. */
303 struct BB_suid_config *sct; 285 static const char config_file[] ALIGN1 = "/etc/busybox.conf";
286
287 struct suid_config_t *sct_head;
304 int applet_no; 288 int applet_no;
305 FILE *f; 289 FILE *f;
306 const char *errmsg; 290 const char *errmsg;
307 char *s;
308 char *e;
309 int i;
310 unsigned lc; 291 unsigned lc;
311 smallint section; 292 smallint section;
312 char buffer[256];
313 struct stat st; 293 struct stat st;
314 294
315 assert(!suid_config); /* Should be set to NULL by bss init. */
316
317 ruid = getuid(); 295 ruid = getuid();
318 if (ruid == 0) /* run by root - don't need to even read config file */ 296 if (ruid == 0) /* run by root - don't need to even read config file */
319 return; 297 return;
@@ -322,7 +300,7 @@ static void parse_config_file(void)
322 || !S_ISREG(st.st_mode) /* Not a regular file? */ 300 || !S_ISREG(st.st_mode) /* Not a regular file? */
323 || (st.st_uid != 0) /* Not owned by root? */ 301 || (st.st_uid != 0) /* Not owned by root? */
324 || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */ 302 || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */
325 || !(f = fopen_for_read(config_file)) /* Cannot open? */ 303 || !(f = fopen_for_read(config_file)) /* Cannot open? */
326 ) { 304 ) {
327 return; 305 return;
328 } 306 }
@@ -332,18 +310,21 @@ static void parse_config_file(void)
332 section = lc = 0; 310 section = lc = 0;
333 311
334 while (1) { 312 while (1) {
335 s = buffer; 313 char buffer[256];
336 314 char *s;
337 if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */ 315
338// why? 316 if (!fgets(buffer, sizeof(buffer), f)) { /* Are we done? */
339 if (ferror(f)) { /* Make sure it wasn't a read error. */ 317 // Looks like bloat
340 parse_error("reading"); 318 //if (ferror(f)) { /* Make sure it wasn't a read error. */
341 } 319 // errmsg = "reading";
320 // goto pe_label;
321 //}
342 fclose(f); 322 fclose(f);
343 suid_config = sct_head; /* Success, so set the pointer. */ 323 suid_config = sct_head; /* Success, so set the pointer. */
344 return; 324 return;
345 } 325 }
346 326
327 s = buffer;
347 lc++; /* Got a (partial) line. */ 328 lc++; /* Got a (partial) line. */
348 329
349 /* If a line is too long for our buffer, we consider it an error. 330 /* If a line is too long for our buffer, we consider it an error.
@@ -355,7 +336,8 @@ static void parse_config_file(void)
355 * we do err on the side of caution. Besides, the line would be 336 * we do err on the side of caution. Besides, the line would be
356 * too long if it did end with a newline. */ 337 * too long if it did end with a newline. */
357 if (!strchr(s, '\n') && !feof(f)) { 338 if (!strchr(s, '\n') && !feof(f)) {
358 parse_error("line too long"); 339 errmsg = "line too long";
340 goto pe_label;
359 } 341 }
360 342
361 /* Trim leading and trailing whitespace, ignoring comments, and 343 /* Trim leading and trailing whitespace, ignoring comments, and
@@ -371,12 +353,13 @@ static void parse_config_file(void)
371 /* Unlike the old code, we ignore leading and trailing 353 /* Unlike the old code, we ignore leading and trailing
372 * whitespace for the section name. We also require that 354 * whitespace for the section name. We also require that
373 * there are no stray characters after the closing bracket. */ 355 * there are no stray characters after the closing bracket. */
374 e = strchr(s, ']'); 356 char *e = strchr(s, ']');
375 if (!e /* Missing right bracket? */ 357 if (!e /* Missing right bracket? */
376 || e[1] /* Trailing characters? */ 358 || e[1] /* Trailing characters? */
377 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */ 359 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
378 ) { 360 ) {
379 parse_error("section header"); 361 errmsg = "section header";
362 goto pe_label;
380 } 363 }
381 /* Right now we only have one section so just check it. 364 /* Right now we only have one section so just check it.
382 * If more sections are added in the future, please don't 365 * If more sections are added in the future, please don't
@@ -401,12 +384,13 @@ static void parse_config_file(void)
401 * where both key and value could contain inner whitespace. */ 384 * where both key and value could contain inner whitespace. */
402 385
403 /* First get the key (an applet name in our case). */ 386 /* First get the key (an applet name in our case). */
404 e = strchr(s, '='); 387 char *e = strchr(s, '=');
405 if (e) { 388 if (e) {
406 s = get_trimmed_slice(s, e); 389 s = get_trimmed_slice(s, e);
407 } 390 }
408 if (!e || !*s) { /* Missing '=' or empty key. */ 391 if (!e || !*s) { /* Missing '=' or empty key. */
409 parse_error("keyword"); 392 errmsg = "keyword";
393 goto pe_label;
410 } 394 }
411 395
412 /* Ok, we have an applet name. Process the rhs if this 396 /* Ok, we have an applet name. Process the rhs if this
@@ -415,13 +399,16 @@ static void parse_config_file(void)
415 * up when the busybox configuration is changed. */ 399 * up when the busybox configuration is changed. */
416 applet_no = find_applet_by_name(s); 400 applet_no = find_applet_by_name(s);
417 if (applet_no >= 0) { 401 if (applet_no >= 0) {
402 unsigned i;
403 struct suid_config_t *sct;
404
418 /* Note: We currently don't check for duplicates! 405 /* Note: We currently don't check for duplicates!
419 * The last config line for each applet will be the 406 * The last config line for each applet will be the
420 * one used since we insert at the head of the list. 407 * one used since we insert at the head of the list.
421 * I suppose this could be considered a feature. */ 408 * I suppose this could be considered a feature. */
422 sct = xmalloc(sizeof(struct BB_suid_config)); 409 sct = xzalloc(sizeof(*sct));
423 sct->m_applet = applet_no; 410 sct->m_applet = applet_no;
424 sct->m_mode = 0; 411 /*sct->m_mode = 0;*/
425 sct->m_next = sct_head; 412 sct->m_next = sct_head;
426 sct_head = sct; 413 sct_head = sct;
427 414
@@ -430,48 +417,41 @@ static void parse_config_file(void)
430 e = skip_whitespace(e+1); 417 e = skip_whitespace(e+1);
431 418
432 for (i = 0; i < 3; i++) { 419 for (i = 0; i < 3; i++) {
433 /* There are 4 chars + 1 nul for each of user/group/other. */ 420 /* There are 4 chars for each of user/group/other.
434 static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-"; 421 * "x-xx" instead of "x-" are to make
435 422 * "idx > 3" check catch invalid chars.
436 const char *q; 423 */
437 q = strchrnul(mode_chars + 5*i, *e++); 424 static const char mode_chars[] ALIGN1 = "Ssx-" "Ssx-" "x-xx";
438 if (!*q) { 425 static const unsigned short mode_mask[] ALIGN2 = {
439 parse_error("mode"); 426 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* Ssx- */
427 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* Ssx- */
428 S_IXOTH, 0 /* x- */
429 };
430 const char *q = strchrnul(mode_chars + 4*i, *e);
431 unsigned idx = q - (mode_chars + 4*i);
432 if (idx > 3) {
433 errmsg = "mode";
434 goto pe_label;
440 } 435 }
441 /* Adjust by -i to account for nul. */ 436 sct->m_mode |= mode_mask[q - mode_chars];
442 sct->m_mode |= mode_mask[(q - mode_chars) - i]; 437 e++;
443 } 438 }
444 439
445 /* Now get the user/group info. */ 440 /* Now get the user/group info. */
446 441
447 s = skip_whitespace(e); 442 s = skip_whitespace(e);
448 443 /* Default is 0.0, else parse USER.GROUP: */
449 /* Note: we require whitespace between the mode and the 444 if (*s) {
450 * user/group info. */ 445 /* We require whitespace between mode and USER.GROUP */
451 if ((s == e) || !(e = strchr(s, '.'))) { 446 if ((s == e) || !(e = strchr(s, '.'))) {
452 parse_error("<uid>.<gid>"); 447 errmsg = "uid.gid";
453 } 448 goto pe_label;
454 *e++ = '\0';
455
456 /* We can't use get_ug_id here since it would exit()
457 * if a uid or gid was not found. Oh well... */
458 sct->m_uid = bb_strtoul(s, NULL, 10);
459 if (errno) {
460 struct passwd *pwd = getpwnam(s);
461 if (!pwd) {
462 parse_error("user");
463 } 449 }
464 sct->m_uid = pwd->pw_uid; 450 *e = ':'; /* get_uidgid needs USER:GROUP syntax */
465 } 451 if (get_uidgid(&sct->m_ugid, s, /*allow_numeric:*/ 1) == 0) {
466 452 errmsg = "unknown user/group";
467 sct->m_gid = bb_strtoul(e, NULL, 10); 453 goto pe_label;
468 if (errno) {
469 struct group *grp;
470 grp = getgrnam(e);
471 if (!grp) {
472 parse_error("group");
473 } 454 }
474 sct->m_gid = grp->gr_gid;
475 } 455 }
476 } 456 }
477 continue; 457 continue;
@@ -485,22 +465,18 @@ static void parse_config_file(void)
485 * We may want to simply ignore such lines in case they 465 * We may want to simply ignore such lines in case they
486 * are used in some future version of busybox. */ 466 * are used in some future version of busybox. */
487 if (!section) { 467 if (!section) {
488 parse_error("keyword outside section"); 468 errmsg = "keyword outside section";
469 goto pe_label;
489 } 470 }
490 471
491 } /* while (1) */ 472 } /* while (1) */
492 473
493 pe_label: 474 pe_label:
494 fprintf(stderr, "Parse error in %s, line %d: %s\n",
495 config_file, lc, errmsg);
496
497 fclose(f); 475 fclose(f);
476 bb_error_msg("parse error in %s, line %u: %s", config_file, lc, errmsg);
477
498 /* Release any allocated memory before returning. */ 478 /* Release any allocated memory before returning. */
499 while (sct_head) { 479 llist_free((llist_t*)sct_head, NULL);
500 sct = sct_head->m_next;
501 free(sct_head);
502 sct_head = sct;
503 }
504} 480}
505# else 481# else
506static inline void parse_config_file(void) 482static inline void parse_config_file(void)
@@ -522,7 +498,7 @@ static void check_suid(int applet_no)
522# if ENABLE_FEATURE_SUID_CONFIG 498# if ENABLE_FEATURE_SUID_CONFIG
523 if (suid_cfg_readable) { 499 if (suid_cfg_readable) {
524 uid_t uid; 500 uid_t uid;
525 struct BB_suid_config *sct; 501 struct suid_config_t *sct;
526 mode_t m; 502 mode_t m;
527 503
528 for (sct = suid_config; sct; sct = sct->m_next) { 504 for (sct = suid_config; sct; sct = sct->m_next) {
@@ -531,36 +507,40 @@ static void check_suid(int applet_no)
531 } 507 }
532 goto check_need_suid; 508 goto check_need_suid;
533 found: 509 found:
510 /* Is this user allowed to run this applet? */
534 m = sct->m_mode; 511 m = sct->m_mode;
535 if (sct->m_uid == ruid) 512 if (sct->m_ugid.uid == ruid)
536 /* same uid */ 513 /* same uid */
537 m >>= 6; 514 m >>= 6;
538 else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid)) 515 else if ((sct->m_ugid.gid == rgid) || ingroup(ruid, sct->m_ugid.gid))
539 /* same group / in group */ 516 /* same group / in group */
540 m >>= 3; 517 m >>= 3;
541 518 if (!(m & S_IXOTH)) /* is x bit not set? */
542 if (!(m & S_IXOTH)) /* is x bit not set ? */ 519 bb_error_msg_and_die("you have no permission to run this applet");
543 bb_error_msg_and_die("you have no permission to run this applet!");
544
545 /* _both_ sgid and group_exec have to be set for setegid */
546 if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
547 rgid = sct->m_gid;
548 /* else (no setegid) we will set egid = rgid */
549 520
550 /* We set effective AND saved ids. If saved-id is not set 521 /* We set effective AND saved ids. If saved-id is not set
551 * like we do below, seteiud(0) can still later succeed! */ 522 * like we do below, seteuid(0) can still later succeed! */
523
524 /* Are we directed to change gid
525 * (APPLET = *s* USER.GROUP or APPLET = *S* USER.GROUP)?
526 */
527 if (sct->m_mode & S_ISGID)
528 rgid = sct->m_ugid.gid;
529 /* else: we will set egid = rgid, thus dropping sgid effect */
552 if (setresgid(-1, rgid, rgid)) 530 if (setresgid(-1, rgid, rgid))
553 bb_perror_msg_and_die("setresgid"); 531 bb_perror_msg_and_die("setresgid");
554 532
555 /* do we have to set effective uid? */ 533 /* Are we directed to change uid
534 * (APPLET = s** USER.GROUP or APPLET = S** USER.GROUP)?
535 */
556 uid = ruid; 536 uid = ruid;
557 if (sct->m_mode & S_ISUID) 537 if (sct->m_mode & S_ISUID)
558 uid = sct->m_uid; 538 uid = sct->m_ugid.uid;
559 /* else (no seteuid) we will set euid = ruid */ 539 /* else: we will set euid = ruid, thus dropping suid effect */
560
561 if (setresuid(-1, uid, uid)) 540 if (setresuid(-1, uid, uid))
562 bb_perror_msg_and_die("setresuid"); 541 bb_perror_msg_and_die("setresuid");
563 return; 542
543 goto ret;
564 } 544 }
565# if !ENABLE_FEATURE_SUID_CONFIG_QUIET 545# if !ENABLE_FEATURE_SUID_CONFIG_QUIET
566 { 546 {
@@ -568,7 +548,7 @@ static void check_suid(int applet_no)
568 548
569 if (!onetime) { 549 if (!onetime) {
570 onetime = 1; 550 onetime = 1;
571 fprintf(stderr, "Using fallback suid method\n"); 551 bb_error_msg("using fallback suid method");
572 } 552 }
573 } 553 }
574# endif 554# endif
@@ -583,6 +563,10 @@ static void check_suid(int applet_no)
583 xsetgid(rgid); /* drop all privileges */ 563 xsetgid(rgid); /* drop all privileges */
584 xsetuid(ruid); 564 xsetuid(ruid);
585 } 565 }
566# if ENABLE_FEATURE_SUID_CONFIG
567 ret: ;
568 llist_free((llist_t*)suid_config, NULL);
569# endif
586} 570}
587# else 571# else
588# define check_suid(x) ((void)0) 572# define check_suid(x) ((void)0)
@@ -799,9 +783,6 @@ int main(int argc UNUSED_PARAM, char **argv)
799#endif 783#endif
800{ 784{
801 /* Tweak malloc for reduced memory consumption */ 785 /* Tweak malloc for reduced memory consumption */
802#ifndef PAGE_SIZE
803# define PAGE_SIZE (4*1024) /* guess */
804#endif
805#ifdef M_TRIM_THRESHOLD 786#ifdef M_TRIM_THRESHOLD
806 /* M_TRIM_THRESHOLD is the maximum amount of freed top-most memory 787 /* M_TRIM_THRESHOLD is the maximum amount of freed top-most memory
807 * to keep before releasing to the OS 788 * to keep before releasing to the OS
diff --git a/libbb/bb_basename.c b/libbb/bb_basename.c
deleted file mode 100644
index 92fdb6545..000000000
--- a/libbb/bb_basename.c
+++ /dev/null
@@ -1,23 +0,0 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 2007 Denys Vlasenko
6 *
7 * Licensed under GPLv2, see file LICENSE in this source tree.
8 */
9
10#include "libbb.h"
11
12const char* FAST_FUNC bb_basename(const char *name)
13{
14 const char *cp = strrchr(name, '/');
15 if (cp)
16 return cp + 1;
17 if (ENABLE_PLATFORM_MINGW32) {
18 cp = strrchr(name, '\\');
19 if (cp)
20 return cp + 1;
21 }
22 return name;
23}
diff --git a/libbb/dump.c b/libbb/dump.c
index 1b1d03a66..919fe135c 100644
--- a/libbb/dump.c
+++ b/libbb/dump.c
@@ -99,7 +99,7 @@ static NOINLINE int bb_dump_size(FS *fs)
99static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs) 99static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
100{ 100{
101 enum { NOTOKAY, USEBCNT, USEPREC } sokay; 101 enum { NOTOKAY, USEBCNT, USEPREC } sokay;
102 PR *pr, **nextpr = NULL; 102 PR *pr;
103 FU *fu; 103 FU *fu;
104 char *p1, *p2, *p3; 104 char *p1, *p2, *p3;
105 char savech, *fmtp; 105 char savech, *fmtp;
@@ -111,15 +111,12 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
111 * break each format unit into print units; each 111 * break each format unit into print units; each
112 * conversion character gets its own. 112 * conversion character gets its own.
113 */ 113 */
114 for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) { 114 for (nconv = 0, fmtp = fu->fmt; *fmtp; ) {
115 /* NOSTRICT */ 115 /* NOSTRICT */
116 /* DBU:[dvae@cray.com] zalloc so that forward ptrs start out NULL*/ 116 /* DBU:[dvae@cray.com] zalloc so that forward ptrs start out NULL*/
117 pr = xzalloc(sizeof(PR)); 117 pr = xzalloc(sizeof(PR));
118 if (!fu->nextpr) 118 if (!fu->nextpr)
119 fu->nextpr = pr; 119 fu->nextpr = pr;
120 /* ignore nextpr -- its unused inside the loop and is
121 * uninitialized 1st time through.
122 */
123 120
124 /* skip preceding text and up to the next % sign */ 121 /* skip preceding text and up to the next % sign */
125 for (p1 = fmtp; *p1 && *p1 != '%'; ++p1) 122 for (p1 = fmtp; *p1 && *p1 != '%'; ++p1)
diff --git a/libbb/get_last_path_component.c b/libbb/get_last_path_component.c
index 72598d22e..db4be68ad 100644
--- a/libbb/get_last_path_component.c
+++ b/libbb/get_last_path_component.c
@@ -6,8 +6,21 @@
6 * 6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */ 8 */
9
10#include "libbb.h" 9#include "libbb.h"
10
11const char* FAST_FUNC bb_basename(const char *name)
12{
13 const char *cp = strrchr(name, '/');
14 if (cp)
15 return cp + 1;
16#if ENABLE_PLATFORM_MINGW32
17 cp = strrchr(name, '\\');
18 if (cp)
19 return cp + 1;
20#endif
21 return name;
22}
23
11/* 24/*
12 * "/" -> "/" 25 * "/" -> "/"
13 * "abc" -> "abc" 26 * "abc" -> "abc"
diff --git a/libbb/getopt32.c b/libbb/getopt32.c
index 8087b3ba6..02cbefec9 100644
--- a/libbb/getopt32.c
+++ b/libbb/getopt32.c
@@ -7,7 +7,9 @@
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */ 8 */
9 9
10#include <getopt.h> 10#if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG
11# include <getopt.h>
12#endif
11#include "libbb.h" 13#include "libbb.h"
12 14
13/* Documentation 15/* Documentation
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index 67c5e0baa..e443c7a3e 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -41,6 +41,10 @@
41 */ 41 */
42#include "libbb.h" 42#include "libbb.h"
43#include "unicode.h" 43#include "unicode.h"
44#ifndef _POSIX_VDISABLE
45# define _POSIX_VDISABLE '\0'
46#endif
47
44 48
45#ifdef TEST 49#ifdef TEST
46# define ENABLE_FEATURE_EDITING 0 50# define ENABLE_FEATURE_EDITING 0
@@ -184,6 +188,7 @@ extern struct lineedit_statics *const lineedit_ptr_to_statics;
184 IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines = 1;) \ 188 IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines = 1;) \
185 IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) \ 189 IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) \
186} while (0) 190} while (0)
191
187static void deinit_S(void) 192static void deinit_S(void)
188{ 193{
189#if ENABLE_FEATURE_EDITING_FANCY_PROMPT 194#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
@@ -1684,7 +1689,7 @@ static void ask_terminal(void)
1684 * write(1, "~/srcdevel/bbox/fix/busybox.t4 # ", 33) = 33 1689 * write(1, "~/srcdevel/bbox/fix/busybox.t4 # ", 33) = 33
1685 * poll([{fd=0, events=POLLIN}], 1, 0) = 0 (Timeout) <-- no input exists 1690 * poll([{fd=0, events=POLLIN}], 1, 0) = 0 (Timeout) <-- no input exists
1686 * write(1, "\33[6n", 4) = 4 <-- send the ESC sequence, quick! 1691 * write(1, "\33[6n", 4) = 4 <-- send the ESC sequence, quick!
1687 * poll([{fd=0, events=POLLIN}], 1, 4294967295) = 1 ([{fd=0, revents=POLLIN}]) 1692 * poll([{fd=0, events=POLLIN}], 1, -1) = 1 ([{fd=0, revents=POLLIN}])
1688 * read(0, "\n", 1) = 1 <-- oh crap, user's input got in first 1693 * read(0, "\n", 1) = 1 <-- oh crap, user's input got in first
1689 */ 1694 */
1690 struct pollfd pfd; 1695 struct pollfd pfd;
@@ -1842,10 +1847,11 @@ static void win_changed(int nsig)
1842{ 1847{
1843 int sv_errno = errno; 1848 int sv_errno = errno;
1844 unsigned width; 1849 unsigned width;
1850
1845 get_terminal_width_height(0, &width, NULL); 1851 get_terminal_width_height(0, &width, NULL);
1846 cmdedit_setwidth(width, nsig /* - just a yes/no flag */); 1852//FIXME: cmdedit_setwidth() -> redraw() -> printf() -> KABOOM! (we are in signal handler!)
1847 if (nsig == SIGWINCH) 1853 cmdedit_setwidth(width, /*redraw_flg:*/ nsig);
1848 signal(SIGWINCH, win_changed); /* rearm ourself */ 1854
1849 errno = sv_errno; 1855 errno = sv_errno;
1850} 1856}
1851 1857
@@ -2031,14 +2037,9 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2031 new_settings.c_cc[VMIN] = 1; 2037 new_settings.c_cc[VMIN] = 1;
2032 new_settings.c_cc[VTIME] = 0; 2038 new_settings.c_cc[VTIME] = 0;
2033 /* Turn off CTRL-C, so we can trap it */ 2039 /* Turn off CTRL-C, so we can trap it */
2034#ifndef _POSIX_VDISABLE
2035# define _POSIX_VDISABLE '\0'
2036#endif
2037 new_settings.c_cc[VINTR] = _POSIX_VDISABLE; 2040 new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
2038 tcsetattr_stdin_TCSANOW(&new_settings); 2041 tcsetattr_stdin_TCSANOW(&new_settings);
2039 2042
2040 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
2041 win_changed(0); /* do initial resizing */
2042#if ENABLE_USERNAME_OR_HOMEDIR 2043#if ENABLE_USERNAME_OR_HOMEDIR
2043 { 2044 {
2044 struct passwd *entry; 2045 struct passwd *entry;
@@ -2061,6 +2062,11 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2061 parse_and_put_prompt(prompt); 2062 parse_and_put_prompt(prompt);
2062 ask_terminal(); 2063 ask_terminal();
2063 2064
2065 /* Install window resize handler (NB: after *all* init is complete) */
2066//FIXME: save entire sigaction!
2067 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
2068 win_changed(0); /* get initial window size */
2069
2064 read_key_buffer[0] = 0; 2070 read_key_buffer[0] = 0;
2065 while (1) { 2071 while (1) {
2066 /* 2072 /*
diff --git a/libbb/llist.c b/libbb/llist.c
index ed84e6472..032e9fac8 100644
--- a/libbb/llist.c
+++ b/libbb/llist.c
@@ -62,7 +62,7 @@ void FAST_FUNC llist_unlink(llist_t **head, llist_t *elm)
62 62
63/* Recursively free all elements in the linked list. If freeit != NULL 63/* Recursively free all elements in the linked list. If freeit != NULL
64 * call it on each datum in the list */ 64 * call it on each datum in the list */
65void FAST_FUNC llist_free(llist_t *elm, void (*freeit) (void *data)) 65void FAST_FUNC llist_free(llist_t *elm, void (*freeit)(void *data))
66{ 66{
67 while (elm) { 67 while (elm) {
68 void *data = llist_pop(&elm); 68 void *data = llist_pop(&elm);
diff --git a/libbb/pw_encrypt.c b/libbb/pw_encrypt.c
index c6c04d44a..39ffa084f 100644
--- a/libbb/pw_encrypt.c
+++ b/libbb/pw_encrypt.c
@@ -27,9 +27,10 @@ static int i64c(int i)
27 return ('a' - 38 + i); 27 return ('a' - 38 + i);
28} 28}
29 29
30int FAST_FUNC crypt_make_salt(char *p, int cnt, int x) 30int FAST_FUNC crypt_make_salt(char *p, int cnt /*, int x */)
31{ 31{
32 x += getpid() + time(NULL); 32 /* was: x += ... */
33 int x = getpid() + monotonic_us();
33 do { 34 do {
34 /* x = (x*1664525 + 1013904223) % 2^32 generator is lame 35 /* x = (x*1664525 + 1013904223) % 2^32 generator is lame
35 * (low-order bit is not "random", etc...), 36 * (low-order bit is not "random", etc...),
@@ -47,6 +48,26 @@ int FAST_FUNC crypt_make_salt(char *p, int cnt, int x)
47 return x; 48 return x;
48} 49}
49 50
51char* FAST_FUNC crypt_make_pw_salt(char salt[MAX_PW_SALT_LEN], const char *algo)
52{
53 int len = 2/2;
54 char *salt_ptr = salt;
55 if (algo[0] != 'd') { /* not des */
56 len = 8/2; /* so far assuming md5 */
57 *salt_ptr++ = '$';
58 *salt_ptr++ = '1';
59 *salt_ptr++ = '$';
60#if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA
61 if (algo[0] == 's') { /* sha */
62 salt[1] = '5' + (strcmp(algo, "sha512") == 0);
63 len = 16/2;
64 }
65#endif
66 }
67 crypt_make_salt(salt_ptr, len);
68 return salt_ptr;
69}
70
50#if ENABLE_USE_BB_CRYPT 71#if ENABLE_USE_BB_CRYPT
51 72
52static char* 73static char*
diff --git a/libbb/read_printf.c b/libbb/read_printf.c
index 8664bc625..192f83d6e 100644
--- a/libbb/read_printf.c
+++ b/libbb/read_printf.c
@@ -55,32 +55,35 @@
55 * which detects EAGAIN and uses poll() to wait on the fd. 55 * which detects EAGAIN and uses poll() to wait on the fd.
56 * Thankfully, poll() doesn't care about O_NONBLOCK flag. 56 * Thankfully, poll() doesn't care about O_NONBLOCK flag.
57 */ 57 */
58ssize_t FAST_FUNC nonblock_safe_read(int fd, void *buf, size_t count) 58ssize_t FAST_FUNC nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR)
59{ 59{
60 struct pollfd pfd[1]; 60 struct pollfd pfd[1];
61 ssize_t n; 61 ssize_t n;
62 62
63 while (1) { 63 while (1) {
64 n = safe_read(fd, buf, count); 64 n = loop_on_EINTR ? safe_read(fd, buf, count) : read(fd, buf, count);
65 if (n >= 0 || errno != EAGAIN) 65 if (n >= 0 || errno != EAGAIN)
66 return n; 66 return n;
67 /* fd is in O_NONBLOCK mode. Wait using poll and repeat */ 67 /* fd is in O_NONBLOCK mode. Wait using poll and repeat */
68 pfd[0].fd = fd; 68 pfd[0].fd = fd;
69 pfd[0].events = POLLIN; 69 pfd[0].events = POLLIN;
70 safe_poll(pfd, 1, -1); /* note: this pulls in printf */ 70 /* note: safe_poll pulls in printf */
71 loop_on_EINTR ? safe_poll(pfd, 1, -1) : poll(pfd, 1, -1);
71 } 72 }
72} 73}
73 74
74// Reads one line a-la fgets (but doesn't save terminating '\n'). 75// Reads one line a-la fgets (but doesn't save terminating '\n').
75// Reads byte-by-byte. Useful when it is important to not read ahead. 76// Reads byte-by-byte. Useful when it is important to not read ahead.
76// Bytes are appended to pfx (which must be malloced, or NULL). 77// Bytes are appended to pfx (which must be malloced, or NULL).
77char* FAST_FUNC xmalloc_reads(int fd, char *buf, size_t *maxsz_p) 78char* FAST_FUNC xmalloc_reads(int fd, size_t *maxsz_p)
78{ 79{
79 char *p; 80 char *p;
80 size_t sz = buf ? strlen(buf) : 0; 81 char *buf = NULL;
82 size_t sz = 0;
81 size_t maxsz = maxsz_p ? *maxsz_p : (INT_MAX - 4095); 83 size_t maxsz = maxsz_p ? *maxsz_p : (INT_MAX - 4095);
82 84
83 goto jump_in; 85 goto jump_in;
86
84 while (sz < maxsz) { 87 while (sz < maxsz) {
85 if ((size_t)(p - buf) == sz) { 88 if ((size_t)(p - buf) == sz) {
86 jump_in: 89 jump_in:
@@ -88,8 +91,8 @@ char* FAST_FUNC xmalloc_reads(int fd, char *buf, size_t *maxsz_p)
88 p = buf + sz; 91 p = buf + sz;
89 sz += 128; 92 sz += 128;
90 } 93 }
91 /* nonblock_safe_read() because we are used by e.g. shells */ 94 if (nonblock_immune_read(fd, p, 1, /*loop_on_EINTR:*/ 1) != 1) {
92 if (nonblock_safe_read(fd, p, 1) != 1) { /* EOF/error */ 95 /* EOF/error */
93 if (p == buf) { /* we read nothing */ 96 if (p == buf) { /* we read nothing */
94 free(buf); 97 free(buf);
95 return NULL; 98 return NULL;
diff --git a/libpwdgrp/uidgid_get.c b/libpwdgrp/uidgid_get.c
index 92290bfdb..8388be0da 100644
--- a/libpwdgrp/uidgid_get.c
+++ b/libpwdgrp/uidgid_get.c
@@ -71,7 +71,8 @@ int FAST_FUNC get_uidgid(struct bb_uidgid_t *u, const char *ug, int numeric_ok)
71 } 71 }
72 } 72 }
73 gr = getgrnam(group); 73 gr = getgrnam(group);
74 if (!gr) return 0; 74 if (!gr)
75 return 0;
75 u->gid = gr->gr_gid; 76 u->gid = gr->gr_gid;
76 } 77 }
77 return 1; 78 return 1;
diff --git a/loginutils/adduser.c b/loginutils/adduser.c
index 1944d9d56..47c674bf4 100644
--- a/loginutils/adduser.c
+++ b/loginutils/adduser.c
@@ -67,6 +67,7 @@ static void passwd_study(struct passwd *p)
67 } 67 }
68 if (p->pw_uid == max) { 68 if (p->pw_uid == max) {
69 bb_error_msg_and_die("no %cids left", 'u'); 69 bb_error_msg_and_die("no %cids left", 'u');
70 /* this format string is reused in adduser and addgroup */
70 } 71 }
71 p->pw_uid++; 72 p->pw_uid++;
72 } 73 }
@@ -82,21 +83,32 @@ static void passwd_study(struct passwd *p)
82 83
83static void addgroup_wrapper(struct passwd *p, const char *group_name) 84static void addgroup_wrapper(struct passwd *p, const char *group_name)
84{ 85{
85 char *argv[5]; 86 char *argv[6];
86 87
87 argv[0] = (char*)"addgroup"; 88 argv[0] = (char*)"addgroup";
88 if (group_name) { 89 if (group_name) {
89 /* Add user to existing group */ 90 /* Add user to existing group */
90 argv[1] = p->pw_name; 91 argv[1] = (char*)"--";
91 argv[2] = (char*)group_name; 92 argv[2] = p->pw_name;
92 argv[3] = NULL; 93 argv[3] = (char*)group_name;
94 argv[4] = NULL;
93 } else { 95 } else {
94 /* Add user to his own group with the first free gid found in passwd_study */ 96 /* Add user to his own group with the first free gid
95//TODO: to be compatible with external addgroup programs we should use --gid instead... 97 * found in passwd_study.
98 */
99#if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS || !ENABLE_ADDGROUP
100 /* We try to use --gid, not -g, because "standard" addgroup
101 * has no short option -g, it has only long --gid.
102 */
103 argv[1] = (char*)"--gid";
104#else
105 /* Breaks if system in fact does NOT use busybox addgroup */
96 argv[1] = (char*)"-g"; 106 argv[1] = (char*)"-g";
107#endif
97 argv[2] = utoa(p->pw_gid); 108 argv[2] = utoa(p->pw_gid);
98 argv[3] = p->pw_name; 109 argv[3] = (char*)"--";
99 argv[4] = NULL; 110 argv[4] = p->pw_name;
111 argv[5] = NULL;
100 } 112 }
101 113
102 spawn_and_wait(argv); 114 spawn_and_wait(argv);
@@ -106,7 +118,7 @@ static void passwd_wrapper(const char *login_name) NORETURN;
106 118
107static void passwd_wrapper(const char *login_name) 119static void passwd_wrapper(const char *login_name)
108{ 120{
109 BB_EXECLP("passwd", "passwd", login_name, NULL); 121 BB_EXECLP("passwd", "passwd", "--", login_name, NULL);
110 bb_error_msg_and_die("can't execute passwd, you must set password manually"); 122 bb_error_msg_and_die("can't execute passwd, you must set password manually");
111} 123}
112 124
@@ -228,9 +240,11 @@ int adduser_main(int argc UNUSED_PARAM, char **argv)
228 if (mkdir_err == 0) { 240 if (mkdir_err == 0) {
229 /* New home. Copy /etc/skel to it */ 241 /* New home. Copy /etc/skel to it */
230 const char *args[] = { 242 const char *args[] = {
231 "chown", "-R", 243 "chown",
244 "-R",
232 xasprintf("%u:%u", (int)pw.pw_uid, (int)pw.pw_gid), 245 xasprintf("%u:%u", (int)pw.pw_uid, (int)pw.pw_gid),
233 pw.pw_dir, NULL 246 pw.pw_dir,
247 NULL
234 }; 248 };
235 /* Be silent on any errors (like: no /etc/skel) */ 249 /* Be silent on any errors (like: no /etc/skel) */
236 logmode = LOGMODE_NONE; 250 logmode = LOGMODE_NONE;
diff --git a/loginutils/chpasswd.c b/loginutils/chpasswd.c
index 6c4296faa..f4718c829 100644
--- a/loginutils/chpasswd.c
+++ b/loginutils/chpasswd.c
@@ -37,9 +37,8 @@ int chpasswd_main(int argc UNUSED_PARAM, char **argv)
37 char *name, *pass; 37 char *name, *pass;
38 char salt[sizeof("$N$XXXXXXXX")]; 38 char salt[sizeof("$N$XXXXXXXX")];
39 int opt, rc; 39 int opt, rc;
40 int rnd = rnd; /* we *want* it to be non-initialized! */
41 40
42 if (getuid()) 41 if (getuid() != 0)
43 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); 42 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
44 43
45 opt_complementary = "m--e:e--m"; 44 opt_complementary = "m--e:e--m";
@@ -55,10 +54,12 @@ int chpasswd_main(int argc UNUSED_PARAM, char **argv)
55 xuname2uid(name); /* dies if there is no such user */ 54 xuname2uid(name); /* dies if there is no such user */
56 55
57 if (!(opt & OPT_ENC)) { 56 if (!(opt & OPT_ENC)) {
58 rnd = crypt_make_salt(salt, 1, rnd); 57 crypt_make_salt(salt, 1);
59 if (opt & OPT_MD5) { 58 if (opt & OPT_MD5) {
60 strcpy(salt, "$1$"); 59 salt[0] = '$';
61 rnd = crypt_make_salt(salt + 3, 4, rnd); 60 salt[1] = '1';
61 salt[2] = '$';
62 crypt_make_salt(salt + 3, 4);
62 } 63 }
63 pass = pw_encrypt(pass, salt, 0); 64 pass = pw_encrypt(pass, salt, 0);
64 } 65 }
diff --git a/loginutils/cryptpw.c b/loginutils/cryptpw.c
index 72388c492..b25a39ac9 100644
--- a/loginutils/cryptpw.c
+++ b/loginutils/cryptpw.c
@@ -14,12 +14,12 @@
14//usage: "[OPTIONS] [PASSWORD] [SALT]" 14//usage: "[OPTIONS] [PASSWORD] [SALT]"
15/* We do support -s, we just don't mention it */ 15/* We do support -s, we just don't mention it */
16//usage:#define cryptpw_full_usage "\n\n" 16//usage:#define cryptpw_full_usage "\n\n"
17//usage: "Crypt the PASSWORD using crypt(3)\n" 17//usage: "Crypt PASSWORD using crypt(3)\n"
18//usage: "\nOptions:" 18//usage: "\nOptions:"
19//usage: IF_LONG_OPTS( 19//usage: IF_LONG_OPTS(
20//usage: "\n -P,--password-fd=N Read password from fd N" 20//usage: "\n -P,--password-fd=N Read password from fd N"
21/* //usage: "\n -s,--stdin Use stdin; like -P0" */ 21/* //usage: "\n -s,--stdin Use stdin; like -P0" */
22//usage: "\n -m,--method=TYPE Encryption method TYPE" 22//usage: "\n -m,--method=TYPE Encryption method"
23//usage: "\n -S,--salt=SALT" 23//usage: "\n -S,--salt=SALT"
24//usage: ) 24//usage: )
25//usage: IF_NOT_LONG_OPTS( 25//usage: IF_NOT_LONG_OPTS(
@@ -34,12 +34,12 @@
34//usage: "[OPTIONS] [PASSWORD] [SALT]" 34//usage: "[OPTIONS] [PASSWORD] [SALT]"
35/* We do support -s, we just don't mention it */ 35/* We do support -s, we just don't mention it */
36//usage:#define mkpasswd_full_usage "\n\n" 36//usage:#define mkpasswd_full_usage "\n\n"
37//usage: "Crypt the PASSWORD using crypt(3)\n" 37//usage: "Crypt PASSWORD using crypt(3)\n"
38//usage: "\nOptions:" 38//usage: "\nOptions:"
39//usage: IF_LONG_OPTS( 39//usage: IF_LONG_OPTS(
40//usage: "\n -P,--password-fd=N Read password from fd N" 40//usage: "\n -P,--password-fd=N Read password from fd N"
41/* //usage: "\n -s,--stdin Use stdin; like -P0" */ 41/* //usage: "\n -s,--stdin Use stdin; like -P0" */
42//usage: "\n -m,--method=TYPE Encryption method TYPE" 42//usage: "\n -m,--method=TYPE Encryption method"
43//usage: "\n -S,--salt=SALT" 43//usage: "\n -S,--salt=SALT"
44//usage: ) 44//usage: )
45//usage: IF_NOT_LONG_OPTS( 45//usage: IF_NOT_LONG_OPTS(
@@ -92,11 +92,9 @@ to cryptpw. -a option (alias for -m) came from cryptpw.
92int cryptpw_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 92int cryptpw_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
93int cryptpw_main(int argc UNUSED_PARAM, char **argv) 93int cryptpw_main(int argc UNUSED_PARAM, char **argv)
94{ 94{
95 /* $N$ + sha_salt_16_bytes + NUL */ 95 char salt[MAX_PW_SALT_LEN];
96 char salt[3 + 16 + 1];
97 char *salt_ptr; 96 char *salt_ptr;
98 const char *opt_m, *opt_S; 97 const char *opt_m, *opt_S;
99 int len;
100 int fd; 98 int fd;
101 99
102#if ENABLE_LONG_OPTS 100#if ENABLE_LONG_OPTS
@@ -121,24 +119,9 @@ int cryptpw_main(int argc UNUSED_PARAM, char **argv)
121 if (argv[0] && !opt_S) 119 if (argv[0] && !opt_S)
122 opt_S = argv[1]; 120 opt_S = argv[1];
123 121
124 len = 2/2; 122 salt_ptr = crypt_make_pw_salt(salt, opt_m);
125 salt_ptr = salt;
126 if (opt_m[0] != 'd') { /* not des */
127 len = 8/2; /* so far assuming md5 */
128 *salt_ptr++ = '$';
129 *salt_ptr++ = '1';
130 *salt_ptr++ = '$';
131#if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA
132 if (opt_m[0] == 's') { /* sha */
133 salt[1] = '5' + (strcmp(opt_m, "sha512") == 0);
134 len = 16/2;
135 }
136#endif
137 }
138 if (opt_S) 123 if (opt_S)
139 safe_strncpy(salt_ptr, opt_S, sizeof(salt) - 3); 124 safe_strncpy(salt_ptr, opt_S, sizeof(salt) - (sizeof("$N$")-1));
140 else
141 crypt_make_salt(salt_ptr, len, 0);
142 125
143 xmove_fd(fd, STDIN_FILENO); 126 xmove_fd(fd, STDIN_FILENO);
144 127
diff --git a/loginutils/passwd.c b/loginutils/passwd.c
index 810644e61..b3ce646f1 100644
--- a/loginutils/passwd.c
+++ b/loginutils/passwd.c
@@ -6,13 +6,13 @@
6//usage:#define passwd_trivial_usage 6//usage:#define passwd_trivial_usage
7//usage: "[OPTIONS] [USER]" 7//usage: "[OPTIONS] [USER]"
8//usage:#define passwd_full_usage "\n\n" 8//usage:#define passwd_full_usage "\n\n"
9//usage: "Change USER's password. If no USER is specified,\n" 9//usage: "Change USER's password (default: current user)"
10//usage: "changes the password for the current user.\n" 10//usage: "\n"
11//usage: "\nOptions:" 11//usage: "\nOptions:"
12//usage: "\n -a ALG Algorithm to use for password (des, md5)" /* ", sha1)" */ 12//usage: "\n -a ALG Encryption method"
13//usage: "\n -d Delete password for the account" 13//usage: "\n -d Set password to ''"
14//usage: "\n -l Lock (disable) account" 14//usage: "\n -l Lock (disable) account"
15//usage: "\n -u Unlock (re-enable) account" 15//usage: "\n -u Unlock (enable) account"
16 16
17#include "libbb.h" 17#include "libbb.h"
18#include <syslog.h> 18#include <syslog.h>
@@ -22,15 +22,15 @@ static void nuke_str(char *str)
22 if (str) memset(str, 0, strlen(str)); 22 if (str) memset(str, 0, strlen(str));
23} 23}
24 24
25static char* new_password(const struct passwd *pw, uid_t myuid, int algo) 25static char* new_password(const struct passwd *pw, uid_t myuid, const char *algo)
26{ 26{
27 char salt[sizeof("$N$XXXXXXXX")]; /* "$N$XXXXXXXX" or "XX" */ 27 char salt[MAX_PW_SALT_LEN];
28 char *orig = (char*)""; 28 char *orig = (char*)"";
29 char *newp = NULL; 29 char *newp = NULL;
30 char *cp = NULL; 30 char *cp = NULL;
31 char *ret = NULL; /* failure so far */ 31 char *ret = NULL; /* failure so far */
32 32
33 if (myuid && pw->pw_passwd[0]) { 33 if (myuid != 0 && pw->pw_passwd[0]) {
34 char *encrypted; 34 char *encrypted;
35 35
36 orig = bb_ask_stdin("Old password: "); /* returns ptr to static */ 36 orig = bb_ask_stdin("Old password: "); /* returns ptr to static */
@@ -38,13 +38,13 @@ static char* new_password(const struct passwd *pw, uid_t myuid, int algo)
38 goto err_ret; 38 goto err_ret;
39 encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */ 39 encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */
40 if (strcmp(encrypted, pw->pw_passwd) != 0) { 40 if (strcmp(encrypted, pw->pw_passwd) != 0) {
41 syslog(LOG_WARNING, "incorrect password for %s", 41 syslog(LOG_WARNING, "incorrect password for %s", pw->pw_name);
42 pw->pw_name);
43 bb_do_delay(LOGIN_FAIL_DELAY); 42 bb_do_delay(LOGIN_FAIL_DELAY);
44 puts("Incorrect password"); 43 puts("Incorrect password");
45 goto err_ret; 44 goto err_ret;
46 } 45 }
47 if (ENABLE_FEATURE_CLEAN_UP) free(encrypted); 46 if (ENABLE_FEATURE_CLEAN_UP)
47 free(encrypted);
48 } 48 }
49 orig = xstrdup(orig); /* or else bb_ask_stdin() will destroy it */ 49 orig = xstrdup(orig); /* or else bb_ask_stdin() will destroy it */
50 newp = bb_ask_stdin("New password: "); /* returns ptr to static */ 50 newp = bb_ask_stdin("New password: "); /* returns ptr to static */
@@ -52,22 +52,22 @@ static char* new_password(const struct passwd *pw, uid_t myuid, int algo)
52 goto err_ret; 52 goto err_ret;
53 newp = xstrdup(newp); /* we are going to bb_ask_stdin() again, so save it */ 53 newp = xstrdup(newp); /* we are going to bb_ask_stdin() again, so save it */
54 if (ENABLE_FEATURE_PASSWD_WEAK_CHECK 54 if (ENABLE_FEATURE_PASSWD_WEAK_CHECK
55 && obscure(orig, newp, pw) && myuid) 55 && obscure(orig, newp, pw)
56 && myuid != 0
57 ) {
56 goto err_ret; /* non-root is not allowed to have weak passwd */ 58 goto err_ret; /* non-root is not allowed to have weak passwd */
59 }
57 60
58 cp = bb_ask_stdin("Retype password: "); 61 cp = bb_ask_stdin("Retype password: ");
59 if (!cp) 62 if (!cp)
60 goto err_ret; 63 goto err_ret;
61 if (strcmp(cp, newp)) { 64 if (strcmp(cp, newp) != 0) {
62 puts("Passwords don't match"); 65 puts("Passwords don't match");
63 goto err_ret; 66 goto err_ret;
64 } 67 }
65 68
66 crypt_make_salt(salt, 1, 0); /* des */ 69 crypt_make_pw_salt(salt, algo);
67 if (algo) { /* MD5 */ 70
68 strcpy(salt, "$1$");
69 crypt_make_salt(salt + 3, 4, 0);
70 }
71 /* pw_encrypt returns malloced str */ 71 /* pw_encrypt returns malloced str */
72 ret = pw_encrypt(newp, salt, 1); 72 ret = pw_encrypt(newp, salt, 1);
73 /* whee, success! */ 73 /* whee, success! */
@@ -75,8 +75,10 @@ static char* new_password(const struct passwd *pw, uid_t myuid, int algo)
75 err_ret: 75 err_ret:
76 nuke_str(orig); 76 nuke_str(orig);
77 if (ENABLE_FEATURE_CLEAN_UP) free(orig); 77 if (ENABLE_FEATURE_CLEAN_UP) free(orig);
78
78 nuke_str(newp); 79 nuke_str(newp);
79 if (ENABLE_FEATURE_CLEAN_UP) free(newp); 80 if (ENABLE_FEATURE_CLEAN_UP) free(newp);
81
80 nuke_str(cp); 82 nuke_str(cp);
81 return ret; 83 return ret;
82} 84}
@@ -85,17 +87,15 @@ int passwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
85int passwd_main(int argc UNUSED_PARAM, char **argv) 87int passwd_main(int argc UNUSED_PARAM, char **argv)
86{ 88{
87 enum { 89 enum {
88 OPT_algo = 0x1, /* -a - password algorithm */ 90 OPT_algo = (1 << 0), /* -a - password algorithm */
89 OPT_lock = 0x2, /* -l - lock account */ 91 OPT_lock = (1 << 1), /* -l - lock account */
90 OPT_unlock = 0x4, /* -u - unlock account */ 92 OPT_unlock = (1 << 2), /* -u - unlock account */
91 OPT_delete = 0x8, /* -d - delete password */ 93 OPT_delete = (1 << 3), /* -d - delete password */
92 OPT_lud = 0xe, 94 OPT_lud = OPT_lock | OPT_unlock | OPT_delete,
93 STATE_ALGO_md5 = 0x10,
94 //STATE_ALGO_des = 0x20, not needed yet
95 }; 95 };
96 unsigned opt; 96 unsigned opt;
97 int rc; 97 int rc;
98 const char *opt_a = ""; 98 const char *opt_a = "d"; /* des */
99 const char *filename; 99 const char *filename;
100 char *myname; 100 char *myname;
101 char *name; 101 char *name;
@@ -116,13 +116,9 @@ int passwd_main(int argc UNUSED_PARAM, char **argv)
116 //argc -= optind; 116 //argc -= optind;
117 argv += optind; 117 argv += optind;
118 118
119 if (strcasecmp(opt_a, "des") != 0) /* -a */
120 opt |= STATE_ALGO_md5;
121 //else
122 // opt |= STATE_ALGO_des;
123 myuid = getuid(); 119 myuid = getuid();
124 /* -l, -u, -d require root priv and username argument */ 120 /* -l, -u, -d require root priv and username argument */
125 if ((opt & OPT_lud) && (myuid || !argv[0])) 121 if ((opt & OPT_lud) && (myuid != 0 || !argv[0]))
126 bb_show_usage(); 122 bb_show_usage();
127 123
128 /* Will complain and die if username not found */ 124 /* Will complain and die if username not found */
@@ -130,7 +126,7 @@ int passwd_main(int argc UNUSED_PARAM, char **argv)
130 name = argv[0] ? argv[0] : myname; 126 name = argv[0] ? argv[0] : myname;
131 127
132 pw = xgetpwnam(name); 128 pw = xgetpwnam(name);
133 if (myuid && pw->pw_uid != myuid) { 129 if (myuid != 0 && pw->pw_uid != myuid) {
134 /* LOGMODE_BOTH */ 130 /* LOGMODE_BOTH */
135 bb_error_msg_and_die("%s can't change password for %s", myname, name); 131 bb_error_msg_and_die("%s can't change password for %s", myname, name);
136 } 132 }
@@ -164,27 +160,29 @@ int passwd_main(int argc UNUSED_PARAM, char **argv)
164 newp = NULL; 160 newp = NULL;
165 c = pw->pw_passwd[0] - '!'; 161 c = pw->pw_passwd[0] - '!';
166 if (!(opt & OPT_lud)) { 162 if (!(opt & OPT_lud)) {
167 if (myuid && !c) { /* passwd starts with '!' */ 163 if (myuid != 0 && !c) { /* passwd starts with '!' */
168 /* LOGMODE_BOTH */ 164 /* LOGMODE_BOTH */
169 bb_error_msg_and_die("can't change " 165 bb_error_msg_and_die("can't change "
170 "locked password for %s", name); 166 "locked password for %s", name);
171 } 167 }
172 printf("Changing password for %s\n", name); 168 printf("Changing password for %s\n", name);
173 newp = new_password(pw, myuid, opt & STATE_ALGO_md5); 169 newp = new_password(pw, myuid, opt_a);
174 if (!newp) { 170 if (!newp) {
175 logmode = LOGMODE_STDIO; 171 logmode = LOGMODE_STDIO;
176 bb_error_msg_and_die("password for %s is unchanged", name); 172 bb_error_msg_and_die("password for %s is unchanged", name);
177 } 173 }
178 } else if (opt & OPT_lock) { 174 } else if (opt & OPT_lock) {
179 if (!c) goto skip; /* passwd starts with '!' */ 175 if (!c)
176 goto skip; /* passwd starts with '!' */
180 newp = xasprintf("!%s", pw->pw_passwd); 177 newp = xasprintf("!%s", pw->pw_passwd);
181 } else if (opt & OPT_unlock) { 178 } else if (opt & OPT_unlock) {
182 if (c) goto skip; /* not '!' */ 179 if (c)
180 goto skip; /* not '!' */
183 /* pw->pw_passwd points to static storage, 181 /* pw->pw_passwd points to static storage,
184 * strdup'ing to avoid nasty surprizes */ 182 * strdup'ing to avoid nasty surprizes */
185 newp = xstrdup(&pw->pw_passwd[1]); 183 newp = xstrdup(&pw->pw_passwd[1]);
186 } else if (opt & OPT_delete) { 184 } else if (opt & OPT_delete) {
187 newp = (char*)""; //xstrdup(""); 185 newp = (char*)"";
188 } 186 }
189 187
190 rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000; 188 rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000;
@@ -202,7 +200,7 @@ int passwd_main(int argc UNUSED_PARAM, char **argv)
202 rc = update_passwd(bb_path_shadow_file, name, newp, NULL); 200 rc = update_passwd(bb_path_shadow_file, name, newp, NULL);
203 if (rc > 0) 201 if (rc > 0)
204 /* password in /etc/shadow was updated */ 202 /* password in /etc/shadow was updated */
205 newp = (char*) "x"; //xstrdup("x"); 203 newp = (char*) "x";
206 if (rc >= 0) 204 if (rc >= 0)
207 /* 0 = /etc/shadow missing (not an error), >0 = passwd changed in /etc/shadow */ 205 /* 0 = /etc/shadow missing (not an error), >0 = passwd changed in /etc/shadow */
208#endif 206#endif
@@ -212,16 +210,17 @@ int passwd_main(int argc UNUSED_PARAM, char **argv)
212 } 210 }
213 /* LOGMODE_BOTH */ 211 /* LOGMODE_BOTH */
214 if (rc < 0) 212 if (rc < 0)
215 bb_error_msg_and_die("can't update password file %s", 213 bb_error_msg_and_die("can't update password file %s", filename);
216 filename);
217 bb_info_msg("Password for %s changed by %s", name, myname); 214 bb_info_msg("Password for %s changed by %s", name, myname);
218 215
219 //if (ENABLE_FEATURE_CLEAN_UP) free(newp); 216 /*if (ENABLE_FEATURE_CLEAN_UP) free(newp); - can't, it may be non-malloced */
220 skip: 217 skip:
221 if (!newp) { 218 if (!newp) {
222 bb_error_msg_and_die("password for %s is already %slocked", 219 bb_error_msg_and_die("password for %s is already %slocked",
223 name, (opt & OPT_unlock) ? "un" : ""); 220 name, (opt & OPT_unlock) ? "un" : "");
224 } 221 }
225 if (ENABLE_FEATURE_CLEAN_UP) free(myname); 222
223 if (ENABLE_FEATURE_CLEAN_UP)
224 free(myname);
226 return 0; 225 return 0;
227} 226}
diff --git a/mailutils/Kbuild.src b/mailutils/Kbuild.src
index 20220dac8..6b4fb7470 100644
--- a/mailutils/Kbuild.src
+++ b/mailutils/Kbuild.src
@@ -7,7 +7,3 @@
7lib-y:= 7lib-y:=
8 8
9INSERT 9INSERT
10lib-$(CONFIG_MAKEMIME) += mime.o mail.o
11lib-$(CONFIG_POPMAILDIR) += popmaildir.o mail.o
12lib-$(CONFIG_REFORMIME) += mime.o mail.o
13lib-$(CONFIG_SENDMAIL) += sendmail.o mail.o
diff --git a/mailutils/mail.c b/mailutils/mail.c
index 44957016f..66c79471f 100644
--- a/mailutils/mail.c
+++ b/mailutils/mail.c
@@ -172,8 +172,8 @@ void FAST_FUNC get_cred_or_die(int fd)
172 G.user = xstrdup(bb_ask(fd, /* timeout: */ 0, "User: ")); 172 G.user = xstrdup(bb_ask(fd, /* timeout: */ 0, "User: "));
173 G.pass = xstrdup(bb_ask(fd, /* timeout: */ 0, "Password: ")); 173 G.pass = xstrdup(bb_ask(fd, /* timeout: */ 0, "Password: "));
174 } else { 174 } else {
175 G.user = xmalloc_reads(fd, /* pfx: */ NULL, /* maxsize: */ NULL); 175 G.user = xmalloc_reads(fd, /* maxsize: */ NULL);
176 G.pass = xmalloc_reads(fd, /* pfx: */ NULL, /* maxsize: */ NULL); 176 G.pass = xmalloc_reads(fd, /* maxsize: */ NULL);
177 } 177 }
178 if (!G.user || !*G.user || !G.pass) 178 if (!G.user || !*G.user || !G.pass)
179 bb_error_msg_and_die("no username or password"); 179 bb_error_msg_and_die("no username or password");
diff --git a/mailutils/mime.c b/mailutils/makemime.c
index 0aff8b1d7..628619bb8 100644
--- a/mailutils/mime.c
+++ b/mailutils/makemime.c
@@ -8,35 +8,17 @@
8 * Licensed under GPLv2, see file LICENSE in this source tree. 8 * Licensed under GPLv2, see file LICENSE in this source tree.
9 */ 9 */
10 10
11//usage:#define makemime_trivial_usage 11//kbuild:lib-$(CONFIG_MAKEMIME) += makemime.o mail.o
12//usage: "[OPTIONS] [FILE]..."
13//usage:#define makemime_full_usage "\n\n"
14//usage: "Create multipart MIME-encoded message from FILEs\n"
15/* //usage: "Transfer encoding is base64, disposition is inline (not attachment)\n" */
16//usage: "\nOptions:"
17//usage: "\n -o FILE Output. Default: stdout"
18//usage: "\n -a HDR Add header. Examples:"
19//usage: "\n \"From: user@host.org\", \"Date: `date -R`\""
20//usage: "\n -c CT Content type. Default: text/plain"
21//usage: "\n -C CS Charset. Default: " CONFIG_FEATURE_MIME_CHARSET
22/* //usage: "\n -e ENC Transfer encoding. Ignored. base64 is assumed" */
23//usage: "\n"
24//usage: "\nOther options are silently ignored"
25
26//usage:#define reformime_trivial_usage
27//usage: "[OPTIONS] [FILE]..."
28//usage:#define reformime_full_usage "\n\n"
29//usage: "Parse MIME-encoded message\n"
30//usage: "\nOptions:"
31//usage: "\n -x PREFIX Extract content of MIME sections to files"
32//usage: "\n -X PROG ARGS Filter content of MIME sections through PROG"
33//usage: "\n Must be the last option"
34//usage: "\n"
35//usage: "\nOther options are silently ignored"
36 12
37#include "libbb.h" 13#include "libbb.h"
38#include "mail.h" 14#include "mail.h"
39 15
16#if 0
17# define dbg_error_msg(...) bb_error_msg(__VA_ARGS__)
18#else
19# define dbg_error_msg(...) ((void)0)
20#endif
21
40/* 22/*
41 makemime -c type [-o file] [-e encoding] [-C charset] [-N name] \ 23 makemime -c type [-o file] [-e encoding] [-C charset] [-N name] \
42 [-a "Header: Contents"] file 24 [-a "Header: Contents"] file
@@ -50,7 +32,6 @@
50 \( opts \) - read from child process, that generates [ opts ] 32 \( opts \) - read from child process, that generates [ opts ]
51 33
52Options: 34Options:
53
54 -c type - create a new MIME section from "file" with this 35 -c type - create a new MIME section from "file" with this
55 Content-Type: (default is application/octet-stream). 36 Content-Type: (default is application/octet-stream).
56 -C charset - MIME charset of a new text/plain section. 37 -C charset - MIME charset of a new text/plain section.
@@ -70,6 +51,28 @@ Options:
70 value on each line. 51 value on each line.
71 {which version of makemime is this? What do we support?} 52 {which version of makemime is this? What do we support?}
72*/ 53*/
54/* man makemime:
55
56 * -c TYPE: create a (non-multipart) MIME section with Content-Type: TYPE
57 * makemime -c TYPE [-e ENCODING] [-o OUTFILE] [-C CHARSET] [-N NAME] [-a HEADER...] FILE
58 * The -C option sets the MIME charset attribute for text/plain content.
59 * The -N option sets the name attribute for Content-Type:
60 * Encoding must be one of the following: 7bit, 8bit, quoted-printable, or base64.
61
62 * -m multipart/TYPE: create a multipart MIME collection with Content-Type: multipart/TYPE
63 * makemime -m multipart/TYPE [-e ENCODING] [-o OUTFILE] [-a HEADER...] FILE
64 * Type must be either "multipart/mixed", "multipart/alternative", or some other MIME multipart content type.
65 * Additionally, encoding can only be "7bit" or "8bit", and will default to "8bit" if not specified.
66 * Finally, filename must be a MIME-formatted section, NOT a regular file.
67 * The -m option creates an initial multipart MIME collection, that contains only one MIME section, taken from filename.
68 * The collection is written to standard output, or the pipe or to outputfile.
69
70 * -j FILE1: add a section to a multipart MIME collection
71 * makemime -j FILE1 [-o OUTFILE] FILE2
72 * FILE1 must be a MIME collection that was previously created by the -m option.
73 * FILE2 must be a MIME section that was previously created by the -c option.
74 * The -j options adds the MIME section in FILE2 to the MIME collection in FILE1.
75 */
73 76
74 77
75/* In busybox 1.15.0.svn, makemime generates output like this 78/* In busybox 1.15.0.svn, makemime generates output like this
@@ -92,10 +95,8 @@ Content-Transfer-Encoding: base64
92...file B contents... 95...file B contents...
93--24269534-2145583448-1655890676-- 96--24269534-2145583448-1655890676--
94 97
95*/ 98 *
96 99 * For reference: here is an example email to LKML which has
97
98/* For reference: here is an example email to LKML which has
99 * 1st unnamed part (so it serves as an email body) 100 * 1st unnamed part (so it serves as an email body)
100 * and one attached file: 101 * and one attached file:
101...other headers... 102...other headers...
@@ -126,28 +127,21 @@ Content-Transfer-Encoding: 7bit
126...random junk added by mailing list robots and such... 127...random junk added by mailing list robots and such...
127*/ 128*/
128 129
129/* man makemime: 130//usage:#define makemime_trivial_usage
130 131//usage: "[OPTIONS] [FILE]..."
131 * -c TYPE: create a (non-multipart) MIME section with Content-Type: TYPE 132//usage:#define makemime_full_usage "\n\n"
132 * makemime -c TYPE [-e ENCODING] [-o OUTFILE] [-C CHARSET] [-N NAME] [-a HEADER...] FILE 133//usage: "Create multipart MIME-encoded message from FILEs\n"
133 * The -C option sets the MIME charset attribute for text/plain content. 134/* //usage: "Transfer encoding is base64, disposition is inline (not attachment)\n" */
134 * The -N option sets the name attribute for Content-Type: 135//usage: "\nOptions:"
135 * Encoding must be one of the following: 7bit, 8bit, quoted-printable, or base64. 136//usage: "\n -o FILE Output. Default: stdout"
136 137//usage: "\n -a HDR Add header. Examples:"
137 * -m multipart/TYPE: create a multipart MIME collection with Content-Type: multipart/TYPE 138//usage: "\n \"From: user@host.org\", \"Date: `date -R`\""
138 * makemime -m multipart/TYPE [-e ENCODING] [-o OUTFILE] [-a HEADER...] FILE 139//usage: "\n -c CT Content type. Default: text/plain"
139 * Type must be either "multipart/mixed", "multipart/alternative", or some other MIME multipart content type. 140//usage: "\n -C CS Charset. Default: " CONFIG_FEATURE_MIME_CHARSET
140 * Additionally, encoding can only be "7bit" or "8bit", and will default to "8bit" if not specified. 141/* //usage: "\n -e ENC Transfer encoding. Ignored. base64 is assumed" */
141 * Finally, filename must be a MIME-formatted section, NOT a regular file. 142//usage: "\n"
142 * The -m option creates an initial multipart MIME collection, that contains only one MIME section, taken from filename. 143//usage: "\nOther options are silently ignored"
143 * The collection is written to standard output, or the pipe or to outputfile.
144 144
145 * -j FILE1: add a section to a multipart MIME collection
146 * makemime -j FILE1 [-o OUTFILE] FILE2
147 * FILE1 must be a MIME collection that was previously created by the -m option.
148 * FILE2 must be a MIME section that was previously created by the -c option.
149 * The -j options adds the MIME section in FILE2 to the MIME collection in FILE1.
150 */
151int makemime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 145int makemime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
152int makemime_main(int argc UNUSED_PARAM, char **argv) 146int makemime_main(int argc UNUSED_PARAM, char **argv)
153{ 147{
@@ -222,244 +216,3 @@ int makemime_main(int argc UNUSED_PARAM, char **argv)
222 return EXIT_SUCCESS; 216 return EXIT_SUCCESS;
223#undef boundary 217#undef boundary
224} 218}
225
226static const char *find_token(const char *const string_array[], const char *key, const char *defvalue)
227{
228 const char *r = NULL;
229 int i;
230 for (i = 0; string_array[i] != NULL; i++) {
231 if (strcasecmp(string_array[i], key) == 0) {
232 r = (char *)string_array[i+1];
233 break;
234 }
235 }
236 return (r) ? r : defvalue;
237}
238
239static const char *xfind_token(const char *const string_array[], const char *key)
240{
241 const char *r = find_token(string_array, key, NULL);
242 if (r)
243 return r;
244 bb_error_msg_and_die("header: %s", key);
245}
246
247enum {
248 OPT_x = 1 << 0,
249 OPT_X = 1 << 1,
250#if ENABLE_FEATURE_REFORMIME_COMPAT
251 OPT_d = 1 << 2,
252 OPT_e = 1 << 3,
253 OPT_i = 1 << 4,
254 OPT_s = 1 << 5,
255 OPT_r = 1 << 6,
256 OPT_c = 1 << 7,
257 OPT_m = 1 << 8,
258 OPT_h = 1 << 9,
259 OPT_o = 1 << 10,
260 OPT_O = 1 << 11,
261#endif
262};
263
264static int parse(const char *boundary, char **argv)
265{
266 char *line, *s, *p;
267 const char *type;
268 int boundary_len = strlen(boundary);
269 const char *delims = " ;\"\t\r\n";
270 const char *uniq;
271 int ntokens;
272 const char *tokens[32]; // 32 is enough
273
274 // prepare unique string pattern
275 uniq = xasprintf("%%llu.%u.%s", (unsigned)getpid(), safe_gethostname());
276
277//bb_info_msg("PARSE[%s]", uniq);
278
279 while ((line = xmalloc_fgets_str(stdin, "\r\n\r\n")) != NULL) {
280
281 // seek to start of MIME section
282 // N.B. to avoid false positives let us seek to the _last_ occurance
283 p = NULL;
284 s = line;
285 while ((s = strcasestr(s, "Content-Type:")) != NULL)
286 p = s++;
287 if (!p)
288 goto next;
289//bb_info_msg("L[%s]", p);
290
291 // split to tokens
292 // TODO: strip of comments which are of form: (comment-text)
293 ntokens = 0;
294 tokens[ntokens] = NULL;
295 for (s = strtok(p, delims); s; s = strtok(NULL, delims)) {
296 tokens[ntokens] = s;
297 if (ntokens < ARRAY_SIZE(tokens) - 1)
298 ntokens++;
299//bb_info_msg("L[%d][%s]", ntokens, s);
300 }
301 tokens[ntokens] = NULL;
302//bb_info_msg("N[%d]", ntokens);
303
304 // analyse tokens
305 type = find_token(tokens, "Content-Type:", "text/plain");
306//bb_info_msg("T[%s]", type);
307 if (0 == strncasecmp(type, "multipart/", 10)) {
308 if (0 == strcasecmp(type+10, "mixed")) {
309 parse(xfind_token(tokens, "boundary="), argv);
310 } else
311 bb_error_msg_and_die("no support of content type '%s'", type);
312 } else {
313 pid_t pid = pid;
314 int rc;
315 FILE *fp;
316 // fetch charset
317 const char *charset = find_token(tokens, "charset=", CONFIG_FEATURE_MIME_CHARSET);
318 // fetch encoding
319 const char *encoding = find_token(tokens, "Content-Transfer-Encoding:", "7bit");
320 // compose target filename
321 char *filename = (char *)find_token(tokens, "filename=", NULL);
322 if (!filename)
323 filename = xasprintf(uniq, monotonic_us());
324 else
325 filename = bb_get_last_path_component_strip(xstrdup(filename));
326
327 // start external helper, if any
328 if (opts & OPT_X) {
329 int fd[2];
330 xpipe(fd);
331 pid = vfork();
332 if (0 == pid) {
333 // child reads from fd[0]
334 close(fd[1]);
335 xmove_fd(fd[0], STDIN_FILENO);
336 xsetenv("CONTENT_TYPE", type);
337 xsetenv("CHARSET", charset);
338 xsetenv("ENCODING", encoding);
339 xsetenv("FILENAME", filename);
340 BB_EXECVP_or_die(argv);
341 }
342 // parent dumps to fd[1]
343 close(fd[0]);
344 fp = xfdopen_for_write(fd[1]);
345 signal(SIGPIPE, SIG_IGN); // ignore EPIPE
346 // or create a file for dump
347 } else {
348 char *fname = xasprintf("%s%s", *argv, filename);
349 fp = xfopen_for_write(fname);
350 free(fname);
351 }
352
353 // housekeeping
354 free(filename);
355
356 // dump to fp
357 if (0 == strcasecmp(encoding, "base64")) {
358 read_base64(stdin, fp, '-');
359 } else if (0 != strcasecmp(encoding, "7bit")
360 && 0 != strcasecmp(encoding, "8bit")
361 ) {
362 // quoted-printable, binary, user-defined are unsupported so far
363 bb_error_msg_and_die("no support of encoding '%s'", encoding);
364 } else {
365 // N.B. we have written redundant \n. so truncate the file
366 // The following weird 2-tacts reading technique is due to
367 // we have to not write extra \n at the end of the file
368 // In case of -x option we could truncate the resulting file as
369 // fseek(fp, -1, SEEK_END);
370 // if (ftruncate(fileno(fp), ftell(fp)))
371 // bb_perror_msg("ftruncate");
372 // But in case of -X we have to be much more careful. There is
373 // no means to truncate what we already have sent to the helper.
374 p = xmalloc_fgets_str(stdin, "\r\n");
375 while (p) {
376 s = xmalloc_fgets_str(stdin, "\r\n");
377 if (s == NULL)
378 break;
379 if ('-' == s[0]
380 && '-' == s[1]
381 && 0 == strncmp(s+2, boundary, boundary_len)
382 ) {
383 break;
384 }
385 fputs(p, fp);
386 p = s;
387 }
388
389/*
390 while ((s = xmalloc_fgetline_str(stdin, "\r\n")) != NULL) {
391 if ('-' == s[0] && '-' == s[1]
392 && 0 == strncmp(s+2, boundary, boundary_len))
393 break;
394 fprintf(fp, "%s\n", s);
395 }
396 // N.B. we have written redundant \n. so truncate the file
397 fseek(fp, -1, SEEK_END);
398 if (ftruncate(fileno(fp), ftell(fp)))
399 bb_perror_msg("ftruncate");
400*/
401 }
402 fclose(fp);
403
404 // finalize helper
405 if (opts & OPT_X) {
406 signal(SIGPIPE, SIG_DFL);
407 // exit if helper exited >0
408 rc = (wait4pid(pid) & 0xff);
409 if (rc)
410 return rc+20;
411 }
412
413 // check multipart finalized
414 if (s && '-' == s[2+boundary_len] && '-' == s[2+boundary_len+1]) {
415 free(line);
416 break;
417 }
418 }
419 next:
420 free(line);
421 }
422
423//bb_info_msg("ENDPARSE[%s]", boundary);
424
425 return EXIT_SUCCESS;
426}
427
428/*
429Usage: reformime [options]
430 -d - parse a delivery status notification.
431 -e - extract contents of MIME section.
432 -x - extract MIME section to a file.
433 -X - pipe MIME section to a program.
434 -i - show MIME info.
435 -s n.n.n.n - specify MIME section.
436 -r - rewrite message, filling in missing MIME headers.
437 -r7 - also convert 8bit/raw encoding to quoted-printable, if possible.
438 -r8 - also convert quoted-printable encoding to 8bit, if possible.
439 -c charset - default charset for rewriting, -o, and -O.
440 -m [file] [file]... - create a MIME message digest.
441 -h "header" - decode RFC 2047-encoded header.
442 -o "header" - encode unstructured header using RFC 2047.
443 -O "header" - encode address list header using RFC 2047.
444*/
445
446int reformime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
447int reformime_main(int argc UNUSED_PARAM, char **argv)
448{
449 const char *opt_prefix = "";
450
451 INIT_G();
452
453 // parse options
454 // N.B. only -x and -X are supported so far
455 opt_complementary = "x--X:X--x" IF_FEATURE_REFORMIME_COMPAT(":m::");
456 opts = getopt32(argv,
457 "x:X" IF_FEATURE_REFORMIME_COMPAT("deis:r:c:m:h:o:O:"),
458 &opt_prefix
459 IF_FEATURE_REFORMIME_COMPAT(, NULL, NULL, &G.opt_charset, NULL, NULL, NULL, NULL)
460 );
461 //argc -= optind;
462 argv += optind;
463
464 return parse("", (opts & OPT_X) ? argv : (char **)&opt_prefix);
465}
diff --git a/mailutils/popmaildir.c b/mailutils/popmaildir.c
index 642657919..c9d22aa28 100644
--- a/mailutils/popmaildir.c
+++ b/mailutils/popmaildir.c
@@ -10,6 +10,8 @@
10 * Licensed under GPLv2, see file LICENSE in this source tree. 10 * Licensed under GPLv2, see file LICENSE in this source tree.
11 */ 11 */
12 12
13//kbuild:lib-$(CONFIG_POPMAILDIR) += popmaildir.o mail.o
14
13//usage:#define popmaildir_trivial_usage 15//usage:#define popmaildir_trivial_usage
14//usage: "[OPTIONS] MAILDIR [CONN_HELPER ARGS]" 16//usage: "[OPTIONS] MAILDIR [CONN_HELPER ARGS]"
15//usage:#define popmaildir_full_usage "\n\n" 17//usage:#define popmaildir_full_usage "\n\n"
diff --git a/mailutils/reformime.c b/mailutils/reformime.c
new file mode 100644
index 000000000..aa5e3b1c0
--- /dev/null
+++ b/mailutils/reformime.c
@@ -0,0 +1,280 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * makemime: create MIME-encoded message
4 * reformime: parse MIME-encoded message
5 *
6 * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
7 *
8 * Licensed under GPLv2, see file LICENSE in this source tree.
9 */
10
11//kbuild:lib-$(CONFIG_REFORMIME) += reformime.o mail.o
12
13#include "libbb.h"
14#include "mail.h"
15
16#if 0
17# define dbg_error_msg(...) bb_error_msg(__VA_ARGS__)
18#else
19# define dbg_error_msg(...) ((void)0)
20#endif
21
22static const char *find_token(const char *const string_array[], const char *key, const char *defvalue)
23{
24 const char *r = NULL;
25 int i;
26 for (i = 0; string_array[i] != NULL; i++) {
27 if (strcasecmp(string_array[i], key) == 0) {
28 r = (char *)string_array[i+1];
29 break;
30 }
31 }
32 return (r) ? r : defvalue;
33}
34
35static const char *xfind_token(const char *const string_array[], const char *key)
36{
37 const char *r = find_token(string_array, key, NULL);
38 if (r)
39 return r;
40 bb_error_msg_and_die("not found: '%s'", key);
41}
42
43enum {
44 OPT_x = 1 << 0,
45 OPT_X = 1 << 1,
46#if ENABLE_FEATURE_REFORMIME_COMPAT
47 OPT_d = 1 << 2,
48 OPT_e = 1 << 3,
49 OPT_i = 1 << 4,
50 OPT_s = 1 << 5,
51 OPT_r = 1 << 6,
52 OPT_c = 1 << 7,
53 OPT_m = 1 << 8,
54 OPT_h = 1 << 9,
55 OPT_o = 1 << 10,
56 OPT_O = 1 << 11,
57#endif
58};
59
60static int parse(const char *boundary, char **argv)
61{
62 int boundary_len = strlen(boundary);
63 char uniq[sizeof("%%llu.%u") + sizeof(int)*3];
64
65 dbg_error_msg("BOUNDARY[%s]", boundary);
66
67 // prepare unique string pattern
68 sprintf(uniq, "%%llu.%u", (unsigned)getpid());
69 dbg_error_msg("UNIQ[%s]", uniq);
70
71 while (1) {
72 char *header;
73 const char *tokens[32]; /* 32 is enough */
74 const char *type;
75
76 /* Read the header (everything up to two \n) */
77 {
78 unsigned header_idx = 0;
79 int last_ch = 0;
80 header = NULL;
81 while (1) {
82 int ch = fgetc(stdin);
83 if (ch == '\r') /* Support both line endings */
84 continue;
85 if (ch == EOF)
86 break;
87 if (ch == '\n' && last_ch == ch)
88 break;
89 if (!(header_idx & 0xff))
90 header = xrealloc(header, header_idx + 0x101);
91 header[header_idx++] = last_ch = ch;
92 }
93 if (!header) {
94 dbg_error_msg("EOF");
95 break;
96 }
97 header[header_idx] = '\0';
98 dbg_error_msg("H:'%s'", p);
99 }
100
101 /* Split to tokens */
102 {
103 char *s, *p;
104 unsigned ntokens;
105 const char *delims = ";=\" \t\n";
106
107 /* Skip to last Content-Type: */
108 s = p = header;
109 while ((p = strchr(p, '\n')) != NULL) {
110 p++;
111 if (strncasecmp(p, "Content-Type:", sizeof("Content-Type:")-1) == 0)
112 s = p;
113 }
114 dbg_error_msg("L:'%s'", p);
115 ntokens = 0;
116 s = strtok(s, delims);
117 while (s) {
118 tokens[ntokens] = s;
119 if (ntokens < ARRAY_SIZE(tokens) - 1)
120 ntokens++;
121 dbg_error_msg("L[%d]='%s'", ntokens, s);
122 s = strtok(NULL, delims);
123 }
124 tokens[ntokens] = NULL;
125 dbg_error_msg("EMPTYLINE, ntokens:%d", ntokens);
126 if (ntokens == 0)
127 break;
128 }
129
130 /* Is it multipart? */
131 type = find_token(tokens, "Content-Type:", "text/plain");
132 dbg_error_msg("TYPE:'%s'", type);
133 if (0 == strncasecmp(type, "multipart/", 10)) {
134 /* Yes, recurse */
135 if (strcasecmp(type + 10, "mixed") != 0)
136 bb_error_msg_and_die("no support of content type '%s'", type);
137 parse(xfind_token(tokens, "boundary"), argv);
138
139 } else {
140 /* No, process one non-multipart section */
141 char *end;
142 pid_t pid = pid;
143 FILE *fp;
144
145 const char *charset = find_token(tokens, "charset", CONFIG_FEATURE_MIME_CHARSET);
146 const char *encoding = find_token(tokens, "Content-Transfer-Encoding:", "7bit");
147
148 /* Compose target filename */
149 char *filename = (char *)find_token(tokens, "filename", NULL);
150 if (!filename)
151 filename = xasprintf(uniq, monotonic_us());
152 else
153 filename = bb_get_last_path_component_strip(xstrdup(filename));
154
155 if (opts & OPT_X) {
156 int fd[2];
157
158 /* start external helper */
159 xpipe(fd);
160 pid = vfork();
161 if (0 == pid) {
162 /* child reads from fd[0] */
163 close(fd[1]);
164 xmove_fd(fd[0], STDIN_FILENO);
165 xsetenv("CONTENT_TYPE", type);
166 xsetenv("CHARSET", charset);
167 xsetenv("ENCODING", encoding);
168 xsetenv("FILENAME", filename);
169 BB_EXECVP_or_die(argv);
170 }
171 /* parent will write to fd[1] */
172 close(fd[0]);
173 fp = xfdopen_for_write(fd[1]);
174 signal(SIGPIPE, SIG_IGN);
175 } else {
176 /* write to file */
177 char *fname = xasprintf("%s%s", *argv, filename);
178 fp = xfopen_for_write(fname);
179 free(fname);
180 }
181 free(filename);
182
183 /* write to fp */
184 end = NULL;
185 if (0 == strcasecmp(encoding, "base64")) {
186 read_base64(stdin, fp, '-');
187 } else
188 if (0 != strcasecmp(encoding, "7bit")
189 && 0 != strcasecmp(encoding, "8bit")
190 ) {
191 /* quoted-printable, binary, user-defined are unsupported so far */
192 bb_error_msg_and_die("encoding '%s' not supported", encoding);
193 } else {
194 /* plain 7bit or 8bit */
195 while ((end = xmalloc_fgets(stdin)) != NULL) {
196 if ('-' == end[0]
197 && '-' == end[1]
198 && strncmp(end + 2, boundary, boundary_len) == 0
199 ) {
200 break;
201 }
202 fputs(end, fp);
203 }
204 }
205 fclose(fp);
206
207 /* Wait for child */
208 if (opts & OPT_X) {
209 int rc;
210 signal(SIGPIPE, SIG_DFL);
211 rc = (wait4pid(pid) & 0xff);
212 if (rc != 0)
213 return rc + 20;
214 }
215
216 /* Multipart ended? */
217 if (end && '-' == end[2 + boundary_len] && '-' == end[2 + boundary_len + 1]) {
218 dbg_error_msg("FINISHED MPART:'%s'", end);
219 break;
220 }
221 dbg_error_msg("FINISHED:'%s'", end);
222 free(end);
223 } /* end of "handle one non-multipart block" */
224
225 free(header);
226 } /* while (1) */
227
228 dbg_error_msg("ENDPARSE[%s]", boundary);
229
230 return EXIT_SUCCESS;
231}
232
233//usage:#define reformime_trivial_usage
234//usage: "[OPTIONS]"
235//usage:#define reformime_full_usage "\n\n"
236//usage: "Parse MIME-encoded message on stdin\n"
237//usage: "\nOptions:"
238//usage: "\n -x PREFIX Extract content of MIME sections to files"
239//usage: "\n -X PROG ARGS Filter content of MIME sections through PROG"
240//usage: "\n Must be the last option"
241//usage: "\n"
242//usage: "\nOther options are silently ignored"
243
244/*
245Usage: reformime [options]
246 -d - parse a delivery status notification.
247 -e - extract contents of MIME section.
248 -x - extract MIME section to a file.
249 -X - pipe MIME section to a program.
250 -i - show MIME info.
251 -s n.n.n.n - specify MIME section.
252 -r - rewrite message, filling in missing MIME headers.
253 -r7 - also convert 8bit/raw encoding to quoted-printable, if possible.
254 -r8 - also convert quoted-printable encoding to 8bit, if possible.
255 -c charset - default charset for rewriting, -o, and -O.
256 -m [file] [file]... - create a MIME message digest.
257 -h "header" - decode RFC 2047-encoded header.
258 -o "header" - encode unstructured header using RFC 2047.
259 -O "header" - encode address list header using RFC 2047.
260*/
261
262int reformime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
263int reformime_main(int argc UNUSED_PARAM, char **argv)
264{
265 const char *opt_prefix = "";
266
267 INIT_G();
268
269 // parse options
270 // N.B. only -x and -X are supported so far
271 opt_complementary = "x--X:X--x" IF_FEATURE_REFORMIME_COMPAT(":m::");
272 opts = getopt32(argv,
273 "x:X" IF_FEATURE_REFORMIME_COMPAT("deis:r:c:m:h:o:O:"),
274 &opt_prefix
275 IF_FEATURE_REFORMIME_COMPAT(, NULL, NULL, &G.opt_charset, NULL, NULL, NULL, NULL)
276 );
277 argv += optind;
278
279 return parse("", (opts & OPT_X) ? argv : (char **)&opt_prefix);
280}
diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c
index 8096288ef..e0aff20fb 100644
--- a/mailutils/sendmail.c
+++ b/mailutils/sendmail.c
@@ -7,6 +7,8 @@
7 * Licensed under GPLv2, see file LICENSE in this source tree. 7 * Licensed under GPLv2, see file LICENSE in this source tree.
8 */ 8 */
9 9
10//kbuild:lib-$(CONFIG_SENDMAIL) += sendmail.o mail.o
11
10//usage:#define sendmail_trivial_usage 12//usage:#define sendmail_trivial_usage
11//usage: "[OPTIONS] [RECIPIENT_EMAIL]..." 13//usage: "[OPTIONS] [RECIPIENT_EMAIL]..."
12//usage:#define sendmail_full_usage "\n\n" 14//usage:#define sendmail_full_usage "\n\n"
diff --git a/miscutils/dc.c b/miscutils/dc.c
index 777ec1654..b080ba133 100644
--- a/miscutils/dc.c
+++ b/miscutils/dc.c
@@ -11,7 +11,7 @@
11//usage: 11//usage:
12//usage:#define dc_full_usage "\n\n" 12//usage:#define dc_full_usage "\n\n"
13//usage: "Tiny RPN calculator. Operations:\n" 13//usage: "Tiny RPN calculator. Operations:\n"
14//usage: "+, add, -, sub, *, mul, /, div, %, mod, **, exp, and, or, not, eor,\n" 14//usage: "+, add, -, sub, *, mul, /, div, %, mod, "IF_FEATURE_DC_LIBM("**, exp, ")"and, or, not, eor,\n"
15//usage: "p - print top of the stack (without popping),\n" 15//usage: "p - print top of the stack (without popping),\n"
16//usage: "f - print entire stack,\n" 16//usage: "f - print entire stack,\n"
17//usage: "o - pop the value and set output radix (must be 10, 16, 8 or 2).\n" 17//usage: "o - pop the value and set output radix (must be 10, 16, 8 or 2).\n"
diff --git a/miscutils/last_fancy.c b/miscutils/last_fancy.c
index 7e61b7691..dc09b65fb 100644
--- a/miscutils/last_fancy.c
+++ b/miscutils/last_fancy.c
@@ -161,11 +161,10 @@ int last_main(int argc UNUSED_PARAM, char **argv)
161 time_t boot_time; 161 time_t boot_time;
162 time_t down_time; 162 time_t down_time;
163 int file; 163 int file;
164 unsigned opt;
165 smallint going_down; 164 smallint going_down;
166 smallint boot_down; 165 smallint boot_down;
167 166
168 opt = getopt32(argv, "Wf:" /* "H" */, &filename); 167 /*opt =*/ getopt32(argv, "Wf:" /* "H" */, &filename);
169#ifdef BUT_UTIL_LINUX_LAST_HAS_NO_SUCH_OPT 168#ifdef BUT_UTIL_LINUX_LAST_HAS_NO_SUCH_OPT
170 if (opt & LAST_OPT_H) { 169 if (opt & LAST_OPT_H) {
171 /* Print header line */ 170 /* Print header line */
diff --git a/miscutils/makedevs.c b/miscutils/makedevs.c
index 8cca83882..c945a1352 100644
--- a/miscutils/makedevs.c
+++ b/miscutils/makedevs.c
@@ -38,7 +38,7 @@
38//usage:#define makedevs_full_usage "\n\n" 38//usage:#define makedevs_full_usage "\n\n"
39//usage: "Create a range of special files as specified in a device table.\n" 39//usage: "Create a range of special files as specified in a device table.\n"
40//usage: "Device table entries take the form of:\n" 40//usage: "Device table entries take the form of:\n"
41//usage: "<type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>\n" 41//usage: "<name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>\n"
42//usage: "Where name is the file name, type can be one of:\n" 42//usage: "Where name is the file name, type can be one of:\n"
43//usage: " f Regular file\n" 43//usage: " f Regular file\n"
44//usage: " d Directory\n" 44//usage: " d Directory\n"
diff --git a/miscutils/setserial.c b/miscutils/setserial.c
new file mode 100644
index 000000000..2951b987d
--- /dev/null
+++ b/miscutils/setserial.c
@@ -0,0 +1,763 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * setserial implementation for busybox
4 *
5 *
6 * Copyright (C) 2011 Marek Bečka <yuen@klacno.sk>
7 *
8 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
9 */
10
11//config:config SETSERIAL
12//config: bool "setserial"
13//config: default y
14//config: depends on PLATFORM_LINUX
15//config: help
16//config: Retrieve or set Linux serial port.
17
18//applet:IF_SETSERIAL(APPLET(setserial, BB_DIR_BIN, BB_SUID_DROP))
19
20//kbuild:lib-$(CONFIG_SETSERIAL) += setserial.o
21
22#include "libbb.h"
23#include <assert.h>
24
25#ifndef PORT_UNKNOWN
26# define PORT_UNKNOWN 0
27#endif
28#ifndef PORT_8250
29# define PORT_8250 1
30#endif
31#ifndef PORT_16450
32# define PORT_16450 2
33#endif
34#ifndef PORT_16550
35# define PORT_16550 3
36#endif
37#ifndef PORT_16550A
38# define PORT_16550A 4
39#endif
40#ifndef PORT_CIRRUS
41# define PORT_CIRRUS 5
42#endif
43#ifndef PORT_16650
44# define PORT_16650 6
45#endif
46#ifndef PORT_16650V2
47# define PORT_16650V2 7
48#endif
49#ifndef PORT_16750
50# define PORT_16750 8
51#endif
52#ifndef PORT_STARTECH
53# define PORT_STARTECH 9
54#endif
55#ifndef PORT_16C950
56# define PORT_16C950 10
57#endif
58#ifndef PORT_16654
59# define PORT_16654 11
60#endif
61#ifndef PORT_16850
62# define PORT_16850 12
63#endif
64#ifndef PORT_RSA
65# define PORT_RSA 13
66#endif
67#ifndef PORT_NS16550A
68# define PORT_NS16550A 14
69#endif
70#ifndef PORT_XSCALE
71# define PORT_XSCALE 15
72#endif
73#ifndef PORT_RM9000
74# define PORT_RM9000 16
75#endif
76#ifndef PORT_OCTEON
77# define PORT_OCTEON 17
78#endif
79#ifndef PORT_AR7
80# define PORT_AR7 18
81#endif
82#ifndef PORT_U6_16550A
83# define PORT_U6_16550A 19
84#endif
85
86#ifndef ASYNCB_HUP_NOTIFY
87# define ASYNCB_HUP_NOTIFY 0
88#endif
89#ifndef ASYNCB_FOURPORT
90# define ASYNCB_FOURPORT 1
91#endif
92#ifndef ASYNCB_SAK
93# define ASYNCB_SAK 2
94#endif
95#ifndef ASYNCB_SPLIT_TERMIOS
96# define ASYNCB_SPLIT_TERMIOS 3
97#endif
98#ifndef ASYNCB_SPD_HI
99# define ASYNCB_SPD_HI 4
100#endif
101#ifndef ASYNCB_SPD_VHI
102# define ASYNCB_SPD_VHI 5
103#endif
104#ifndef ASYNCB_SKIP_TEST
105# define ASYNCB_SKIP_TEST 6
106#endif
107#ifndef ASYNCB_AUTO_IRQ
108# define ASYNCB_AUTO_IRQ 7
109#endif
110#ifndef ASYNCB_SESSION_LOCKOUT
111# define ASYNCB_SESSION_LOCKOUT 8
112#endif
113#ifndef ASYNCB_PGRP_LOCKOUT
114# define ASYNCB_PGRP_LOCKOUT 9
115#endif
116#ifndef ASYNCB_CALLOUT_NOHUP
117# define ASYNCB_CALLOUT_NOHUP 10
118#endif
119#ifndef ASYNCB_SPD_SHI
120# define ASYNCB_SPD_SHI 12
121#endif
122#ifndef ASYNCB_LOW_LATENCY
123# define ASYNCB_LOW_LATENCY 13
124#endif
125#ifndef ASYNCB_BUGGY_UART
126# define ASYNCB_BUGGY_UART 14
127#endif
128
129#ifndef ASYNC_HUP_NOTIFY
130# define ASYNC_HUP_NOTIFY (1U << ASYNCB_HUP_NOTIFY)
131#endif
132#ifndef ASYNC_FOURPORT
133# define ASYNC_FOURPORT (1U << ASYNCB_FOURPORT)
134#endif
135#ifndef ASYNC_SAK
136# define ASYNC_SAK (1U << ASYNCB_SAK)
137#endif
138#ifndef ASYNC_SPLIT_TERMIOS
139# define ASYNC_SPLIT_TERMIOS (1U << ASYNCB_SPLIT_TERMIOS)
140#endif
141#ifndef ASYNC_SPD_HI
142# define ASYNC_SPD_HI (1U << ASYNCB_SPD_HI)
143#endif
144#ifndef ASYNC_SPD_VHI
145# define ASYNC_SPD_VHI (1U << ASYNCB_SPD_VHI)
146#endif
147#ifndef ASYNC_SKIP_TEST
148# define ASYNC_SKIP_TEST (1U << ASYNCB_SKIP_TEST)
149#endif
150#ifndef ASYNC_AUTO_IRQ
151# define ASYNC_AUTO_IRQ (1U << ASYNCB_AUTO_IRQ)
152#endif
153#ifndef ASYNC_SESSION_LOCKOUT
154# define ASYNC_SESSION_LOCKOUT (1U << ASYNCB_SESSION_LOCKOUT)
155#endif
156#ifndef ASYNC_PGRP_LOCKOUT
157# define ASYNC_PGRP_LOCKOUT (1U << ASYNCB_PGRP_LOCKOUT)
158#endif
159#ifndef ASYNC_CALLOUT_NOHUP
160# define ASYNC_CALLOUT_NOHUP (1U << ASYNCB_CALLOUT_NOHUP)
161#endif
162#ifndef ASYNC_SPD_SHI
163# define ASYNC_SPD_SHI (1U << ASYNCB_SPD_SHI)
164#endif
165#ifndef ASYNC_LOW_LATENCY
166# define ASYNC_LOW_LATENCY (1U << ASYNCB_LOW_LATENCY)
167#endif
168#ifndef ASYNC_BUGGY_UART
169# define ASYNC_BUGGY_UART (1U << ASYNCB_BUGGY_UART)
170#endif
171
172#ifndef ASYNC_SPD_CUST
173# define ASYNC_SPD_CUST (ASYNC_SPD_HI|ASYNC_SPD_VHI)
174#endif
175#ifndef ASYNC_SPD_WARP
176# define ASYNC_SPD_WARP (ASYNC_SPD_HI|ASYNC_SPD_SHI)
177#endif
178#ifndef ASYNC_SPD_MASK
179# define ASYNC_SPD_MASK (ASYNC_SPD_HI|ASYNC_SPD_VHI|ASYNC_SPD_SHI)
180#endif
181
182#ifndef ASYNC_CLOSING_WAIT_INF
183# define ASYNC_CLOSING_WAIT_INF 0
184#endif
185#ifndef ASYNC_CLOSING_WAIT_NONE
186# define ASYNC_CLOSING_WAIT_NONE 65535
187#endif
188
189#ifndef _LINUX_SERIAL_H
190struct serial_struct {
191 int type;
192 int line;
193 unsigned int port;
194 int irq;
195 int flags;
196 int xmit_fifo_size;
197 int custom_divisor;
198 int baud_base;
199 unsigned short close_delay;
200 char io_type;
201 char reserved_char[1];
202 int hub6;
203 unsigned short closing_wait; /* time to wait before closing */
204 unsigned short closing_wait2; /* no longer used... */
205 unsigned char *iomem_base;
206 unsigned short iomem_reg_shift;
207 unsigned int port_high;
208 unsigned long iomap_base; /* cookie passed into ioremap */
209};
210#endif
211
212//usage:#define setserial_trivial_usage
213//usage: "[-gabGvzV] DEVICE [PARAMETER [ARG]]..."
214//usage:#define setserial_full_usage "\n\n"
215//usage: "Request or set Linux serial port information\n\n"
216//usage: "Options:\n"
217//usage: " -g Interpret parameters as list of devices for reporting"
218//usage: " -a Print all available information\n"
219//usage: " -b Print summary information\n"
220//usage: " -G Print in form which can be fed back\n"
221//usage: " to setserial as command line parameters\n"
222//usage: " -z Zero out serial flags before setting\n"
223//usage: " -v Verbose\n"
224//usage: "\n"
225//usage: "Parameters: (* = takes an argument, ^ = can be turned off by preceding ^)\n"
226//usage: " *port, *irq, *divisor, *uart, *baund_base, *close_delay, *closing_wait,\n"
227//usage: " ^fourport, ^auto_irq, ^skip_test, ^sak, ^session_lockout, ^pgrp_lockout,\n"
228//usage: " ^callout_nohup, ^split_termios, ^hup_notify, ^low_latency, autoconfig,\n"
229//usage: " spd_normal, spd_hi, spd_vhi, spd_shi, spd_warp, spd_cust\n"
230//usage: "\n"
231//usage: "UART types:\n"
232//usage: " unknown, 8250, 16450, 16550, 16550A, Cirrus, 16650, 16650V2, 16750,\n"
233//usage: " 16950, 16954, 16654, 16850, RSA, NS16550A, XSCALE, RM9000, OCTEON, AR7,\n"
234//usage: " U6_16550A"
235
236#define OPT_PRINT_SUMMARY (1 << 0)
237#define OPT_PRINT_FEDBACK (1 << 1)
238#define OPT_PRINT_ALL (1 << 2)
239#define OPT_VERBOSE (1 << 3)
240#define OPT_ZERO (1 << 4)
241#define OPT_GET (1 << 5)
242
243#define OPT_MODE_MASK \
244 (OPT_PRINT_ALL | OPT_PRINT_SUMMARY | OPT_PRINT_FEDBACK)
245
246enum print_mode
247{
248 PRINT_NORMAL = 0,
249 PRINT_SUMMARY = (1 << 0),
250 PRINT_FEDBACK = (1 << 1),
251 PRINT_ALL = (1 << 2),
252};
253
254#define CTL_SET (1 << 0)
255#define CTL_CONFIG (1 << 1)
256#define CTL_GET (1 << 2)
257#define CTL_CLOSE (1 << 3)
258#define CTL_NODIE (1 << 4)
259
260static const char serial_types[] =
261 "unknown\0" /* 0 */
262 "8250\0" /* 1 */
263 "16450\0" /* 2 */
264 "16550\0" /* 3 */
265 "16550A\0" /* 4 */
266 "Cirrus\0" /* 5 */
267 "16650\0" /* 6 */
268 "16650V2\0" /* 7 */
269 "16750\0" /* 8 */
270 "16950\0" /* 9 UNIMPLEMENTED: also know as "16950/954" */
271 "16954\0" /* 10 */
272 "16654\0" /* 11 */
273 "16850\0" /* 12 */
274 "RSA\0" /* 13 */
275#ifndef SETSERIAL_BASE
276 "NS16550A\0" /* 14 */
277 "XSCALE\0" /* 15 */
278 "RM9000\0" /* 16 */
279 "OCTEON\0" /* 17 */
280 "AR7\0" /* 18 */
281 "U6_16550A\0" /* 19 */
282#endif
283;
284
285#ifndef SETSERIAL_BASE
286# define MAX_SERIAL_TYPE 19
287#else
288# define MAX_SERIAL_TYPE 13
289#endif
290
291static const char commands[] =
292 "spd_normal\0"
293 "spd_hi\0"
294 "spd_vhi\0"
295 "spd_shi\0"
296 "spd_warp\0"
297 "spd_cust\0"
298
299 "sak\0"
300 "fourport\0"
301 "hup_notify\0"
302 "skip_test\0"
303 "auto_irq\0"
304 "split_termios\0"
305 "session_lockout\0"
306 "pgrp_lockout\0"
307 "callout_nohup\0"
308 "low_latency\0"
309
310 "port\0"
311 "irq\0"
312 "divisor\0"
313 "uart\0"
314 "baund_base\0"
315 "close_delay\0"
316 "closing_wait\0"
317
318 "autoconfig\0"
319;
320
321enum
322{
323 CMD_SPD_NORMAL = 0,
324 CMD_SPD_HI,
325 CMD_SPD_VHI,
326 CMD_SPD_SHI,
327 CMD_SPD_WARP,
328 CMD_SPD_CUST,
329
330 CMD_FLAG_SAK,
331 CMD_FLAG_FOURPORT,
332 CMD_FLAG_NUP_NOTIFY,
333 CMD_FLAG_SKIP_TEST,
334 CMD_FLAG_AUTO_IRQ,
335 CMD_FLAG_SPLIT_TERMIOS,
336 CMD_FLAG_SESSION_LOCKOUT,
337 CMD_FLAG_PGRP_LOCKOUT,
338 CMD_FLAG_CALLOUT_NOHUP,
339 CMD_FLAG_LOW_LATENCY,
340
341 CMD_PORT,
342 CMD_IRQ,
343 CMD_DIVISOR,
344 CMD_UART,
345 CMD_BASE,
346 CMD_DELAY,
347 CMD_WAIT,
348
349 CMD_AUTOCONFIG,
350
351 CMD_FLAG_FIRST = CMD_FLAG_SAK,
352 CMD_FLAG_LAST = CMD_FLAG_LOW_LATENCY,
353};
354
355static bool cmd_noprint(int cmd)
356{
357 return (cmd >= CMD_FLAG_SKIP_TEST && cmd <= CMD_FLAG_CALLOUT_NOHUP);
358}
359
360static bool cmd_is_flag(int cmd)
361{
362 return (cmd >= CMD_FLAG_FIRST && cmd <= CMD_FLAG_LAST);
363}
364
365static bool cmd_need_arg(int cmd)
366{
367 return (cmd >= CMD_PORT && cmd <= CMD_WAIT);
368}
369
370#define ALL_SPD ( \
371 ASYNC_SPD_HI | ASYNC_SPD_VHI | ASYNC_SPD_SHI | \
372 ASYNC_SPD_WARP | ASYNC_SPD_CUST \
373 )
374
375#define ALL_FLAGS ( \
376 ASYNC_SAK | ASYNC_FOURPORT | ASYNC_HUP_NOTIFY | \
377 ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ | ASYNC_SPLIT_TERMIOS | \
378 ASYNC_SESSION_LOCKOUT | ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP | \
379 ASYNC_LOW_LATENCY \
380 )
381
382#if (ALL_SPD | ALL_FLAGS) > 0xffff
383# error "Unexpected flags size"
384#endif
385
386static const uint16_t setbits[CMD_FLAG_LAST + 1] =
387{
388 0,
389 ASYNC_SPD_HI,
390 ASYNC_SPD_VHI,
391 ASYNC_SPD_SHI,
392 ASYNC_SPD_WARP,
393 ASYNC_SPD_CUST,
394
395 ASYNC_SAK,
396 ASYNC_FOURPORT,
397 ASYNC_HUP_NOTIFY,
398 ASYNC_SKIP_TEST,
399 ASYNC_AUTO_IRQ,
400 ASYNC_SPLIT_TERMIOS,
401 ASYNC_SESSION_LOCKOUT,
402 ASYNC_PGRP_LOCKOUT,
403 ASYNC_CALLOUT_NOHUP,
404 ASYNC_LOW_LATENCY
405};
406
407static const char STR_INFINITE[] = "infinite";
408static const char STR_NONE[] = "none";
409
410static const char *uart_type(int type)
411{
412 if (type > MAX_SERIAL_TYPE)
413 return "undefined";
414
415 return nth_string(serial_types, type);
416}
417
418/* libbb candidate */
419static int index_in_strings_case_insensitive(const char *strings, const char *key)
420{
421 int idx = 0;
422
423 while (*strings) {
424 if (strcasecmp(strings, key) == 0) {
425 return idx;
426 }
427 strings += strlen(strings) + 1; /* skip NUL */
428 idx++;
429 }
430 return -1;
431}
432
433static int uart_id(const char *name)
434{
435 return index_in_strings_case_insensitive(serial_types, name);
436}
437
438static const char *get_spd(int flags, enum print_mode mode)
439{
440 int idx;
441
442 switch (flags & ASYNC_SPD_MASK) {
443 case ASYNC_SPD_HI:
444 idx = CMD_SPD_HI;
445 break;
446 case ASYNC_SPD_VHI:
447 idx = CMD_SPD_VHI;
448 break;
449 case ASYNC_SPD_SHI:
450 idx = CMD_SPD_SHI;
451 break;
452 case ASYNC_SPD_WARP:
453 idx = CMD_SPD_WARP;
454 break;
455 case ASYNC_SPD_CUST:
456 idx = CMD_SPD_CUST;
457 break;
458 default:
459 if (mode < PRINT_FEDBACK)
460 return NULL;
461 idx = CMD_SPD_NORMAL;
462 }
463
464 return nth_string(commands, idx);
465}
466
467static int get_numeric(const char *arg)
468{
469 return bb_strtol(arg, NULL, 0);
470}
471
472static int get_wait(const char *arg)
473{
474 if (strcasecmp(arg, STR_NONE) == 0)
475 return ASYNC_CLOSING_WAIT_NONE;
476
477 if (strcasecmp(arg, STR_INFINITE) == 0)
478 return ASYNC_CLOSING_WAIT_INF;
479
480 return get_numeric(arg);
481}
482
483static int get_uart(const char *arg)
484{
485 int uart = uart_id(arg);
486
487 if (uart < 0)
488 bb_error_msg_and_die("illegal UART type: %s", arg);
489
490 return uart;
491}
492
493static int serial_open(const char *dev, bool quiet)
494{
495 int fd;
496
497 fd = device_open(dev, O_RDWR | O_NONBLOCK);
498 if (fd < 0 && !quiet)
499 bb_simple_perror_msg(dev);
500
501 return fd;
502}
503
504static int serial_ctl(int fd, int ops, struct serial_struct *serinfo)
505{
506 int ret = 0;
507 const char *err;
508
509 if (ops & CTL_SET) {
510 ret = ioctl(fd, TIOCSSERIAL, serinfo);
511 if (ret < 0) {
512 err = "can't set serial info";
513 goto fail;
514 }
515 }
516
517 if (ops & CTL_CONFIG) {
518 ret = ioctl(fd, TIOCSERCONFIG);
519 if (ret < 0) {
520 err = "can't autoconfigure port";
521 goto fail;
522 }
523 }
524
525 if (ops & CTL_GET) {
526 ret = ioctl(fd, TIOCGSERIAL, serinfo);
527 if (ret < 0) {
528 err = "can't get serial info";
529 goto fail;
530 }
531 }
532 nodie:
533 if (ops & CTL_CLOSE)
534 close(fd);
535
536 return ret;
537 fail:
538 bb_simple_perror_msg(err);
539 if (ops & CTL_NODIE)
540 goto nodie;
541 exit(EXIT_FAILURE);
542}
543
544static void print_flag(const char **prefix, const char *flag)
545{
546 printf("%s%s", *prefix, flag);
547 *prefix = " ";
548}
549
550static void print_serial_flags(int serial_flags, enum print_mode mode,
551 const char *prefix, const char *postfix)
552{
553 int i;
554 const char *spd, *pr;
555
556 pr = prefix;
557
558 spd = get_spd(serial_flags, mode);
559 if (spd)
560 print_flag(&pr, spd);
561
562 for (i = CMD_FLAG_FIRST; i <= CMD_FLAG_LAST; i++) {
563 if ((serial_flags & setbits[i])
564 && (mode > PRINT_SUMMARY || !cmd_noprint(i))
565 ) {
566 print_flag(&pr, nth_string(commands, i));
567 }
568 }
569
570 puts(pr == prefix ? "" : postfix);
571}
572
573static void print_closing_wait(unsigned int closing_wait)
574{
575 switch (closing_wait) {
576 case ASYNC_CLOSING_WAIT_NONE:
577 puts(STR_NONE);
578 break;
579 case ASYNC_CLOSING_WAIT_INF:
580 puts(STR_INFINITE);
581 break;
582 default:
583 printf("%u\n", closing_wait);
584 }
585}
586
587static void serial_get(const char *device, enum print_mode mode)
588{
589 int fd, ret;
590 const char *uart, *prefix, *postfix;
591 struct serial_struct serinfo;
592
593 fd = serial_open(device, /*quiet:*/ mode == PRINT_SUMMARY);
594 if (fd < 0)
595 return;
596
597 ret = serial_ctl(fd, CTL_GET | CTL_CLOSE | CTL_NODIE, &serinfo);
598 if (ret < 0)
599 return;
600
601 uart = uart_type(serinfo.type);
602 prefix = ", Flags: ";
603 postfix = "";
604
605 switch (mode) {
606 case PRINT_NORMAL:
607 printf("%s, UART: %s, Port: 0x%.4x, IRQ: %d",
608 device, uart, serinfo.port, serinfo.irq);
609 break;
610 case PRINT_SUMMARY:
611 if (!serinfo.type)
612 return;
613 printf("%s at 0x%.4x (irq = %d) is a %s",
614 device, serinfo.port, serinfo.irq, uart);
615 prefix = " (";
616 postfix = ")";
617 break;
618 case PRINT_FEDBACK:
619 printf("%s uart %s port 0x%.4x irq %d baud_base %d", device,
620 uart, serinfo.port, serinfo.irq, serinfo.baud_base);
621 prefix = " ";
622 break;
623 case PRINT_ALL:
624 printf("%s, Line %d, UART: %s, Port: 0x%.4x, IRQ: %d\n",
625 device, serinfo.line, uart, serinfo.port, serinfo.irq);
626 printf("\tBaud_base: %d, close_delay: %u, divisor: %d\n",
627 serinfo.baud_base, serinfo.close_delay,
628 serinfo.custom_divisor);
629 printf("\tclosing_wait: ");
630 print_closing_wait(serinfo.closing_wait);
631 prefix = "\tFlags: ";
632 postfix = "\n";
633 break;
634 default:
635 assert(0);
636 }
637
638 print_serial_flags(serinfo.flags, mode, prefix, postfix);
639}
640
641static int find_cmd(const char *cmd)
642{
643 int idx;
644
645 idx = index_in_strings_case_insensitive(commands, cmd);
646 if (idx < 0)
647 bb_error_msg_and_die("invalid flag: %s", cmd);
648
649 return idx;
650}
651
652static void serial_set(char **arg, int opts)
653{
654 struct serial_struct serinfo;
655 int cmd;
656 const char *word;
657 int fd;
658
659 fd = serial_open(*arg++, /*quiet:*/ false);
660 if (fd < 0)
661 exit(201);
662
663 serial_ctl(fd, CTL_GET, &serinfo);
664
665 if (opts & OPT_ZERO)
666 serinfo.flags = 0;
667
668 while (*arg) {
669 int invert;
670
671 word = *arg++;
672 invert = (*word == '^');
673 word += invert;
674
675 cmd = find_cmd(word);
676
677 if (*arg == NULL && cmd_need_arg(cmd))
678 bb_error_msg_and_die(bb_msg_requires_arg, word);
679
680 if (invert && !cmd_is_flag(cmd))
681 bb_error_msg_and_die("can't invert %s", word);
682
683 switch (cmd) {
684 case CMD_SPD_NORMAL:
685 case CMD_SPD_HI:
686 case CMD_SPD_VHI:
687 case CMD_SPD_SHI:
688 case CMD_SPD_WARP:
689 case CMD_SPD_CUST:
690 serinfo.flags &= ~ASYNC_SPD_MASK;
691 /* fallthrough */
692 case CMD_FLAG_SAK:
693 case CMD_FLAG_FOURPORT:
694 case CMD_FLAG_NUP_NOTIFY:
695 case CMD_FLAG_SKIP_TEST:
696 case CMD_FLAG_AUTO_IRQ:
697 case CMD_FLAG_SPLIT_TERMIOS:
698 case CMD_FLAG_SESSION_LOCKOUT:
699 case CMD_FLAG_PGRP_LOCKOUT:
700 case CMD_FLAG_CALLOUT_NOHUP:
701 case CMD_FLAG_LOW_LATENCY:
702 if (invert)
703 serinfo.flags &= ~setbits[cmd];
704 else
705 serinfo.flags |= setbits[cmd];
706 break;
707 case CMD_PORT:
708 serinfo.port = get_numeric(*arg++);
709 break;
710 case CMD_IRQ:
711 serinfo.irq = get_numeric(*arg++);
712 break;
713 case CMD_DIVISOR:
714 serinfo.custom_divisor = get_numeric(*arg++);
715 break;
716 case CMD_UART:
717 serinfo.type = get_uart(*arg++);
718 break;
719 case CMD_BASE:
720 serinfo.baud_base = get_numeric(*arg++);
721 break;
722 case CMD_DELAY:
723 serinfo.close_delay = get_numeric(*arg++);
724 break;
725 case CMD_WAIT:
726 serinfo.closing_wait = get_wait(*arg++);
727 break;
728 case CMD_AUTOCONFIG:
729 serial_ctl(fd, CTL_SET | CTL_CONFIG | CTL_GET, &serinfo);
730 break;
731 default:
732 assert(0);
733 }
734 }
735
736 serial_ctl(fd, CTL_SET | CTL_CLOSE, &serinfo);
737}
738
739int setserial_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
740int setserial_main(int argc UNUSED_PARAM, char **argv)
741{
742 int opts;
743
744 opt_complementary = "-1:b-aG:G-ab:a-bG";
745 opts = getopt32(argv, "bGavzg");
746 argv += optind;
747
748 if (!argv[1]) /* one arg only? */
749 opts |= OPT_GET;
750
751 if (!(opts & OPT_GET)) {
752 serial_set(argv, opts);
753 argv[1] = NULL;
754 }
755
756 if (opts & (OPT_VERBOSE | OPT_GET)) {
757 do {
758 serial_get(*argv++, opts & OPT_MODE_MASK);
759 } while (*argv);
760 }
761
762 return EXIT_SUCCESS;
763}
diff --git a/miscutils/ubi_attach_detach.c b/miscutils/ubi_tools.c
index 9007f8c3f..fc7f38c5d 100644
--- a/miscutils/ubi_attach_detach.c
+++ b/miscutils/ubi_tools.c
@@ -37,18 +37,27 @@
37//config: select PLATFORM_LINUX 37//config: select PLATFORM_LINUX
38//config: help 38//config: help
39//config: Resize a UBI volume. 39//config: Resize a UBI volume.
40//config:
41//config:config UBIUPDATEVOL
42//config: bool "ubiupdatevol"
43//config: default y
44//config: select PLATFORM_LINUX
45//config: help
46//config: Update a UBI volume.
40 47
41//applet:IF_UBIATTACH(APPLET_ODDNAME(ubiattach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiattach)) 48//applet:IF_UBIATTACH(APPLET_ODDNAME(ubiattach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiattach))
42//applet:IF_UBIDETACH(APPLET_ODDNAME(ubidetach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubidetach)) 49//applet:IF_UBIDETACH(APPLET_ODDNAME(ubidetach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubidetach))
43//applet:IF_UBIMKVOL(APPLET_ODDNAME(ubimkvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubimkvol)) 50//applet:IF_UBIMKVOL(APPLET_ODDNAME(ubimkvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubimkvol))
44//applet:IF_UBIRMVOL(APPLET_ODDNAME(ubirmvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirmvol)) 51//applet:IF_UBIRMVOL(APPLET_ODDNAME(ubirmvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirmvol))
45//applet:IF_UBIRSVOL(APPLET_ODDNAME(ubirsvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirsvol)) 52//applet:IF_UBIRSVOL(APPLET_ODDNAME(ubirsvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirsvol))
53//applet:IF_UBIUPDATEVOL(APPLET_ODDNAME(ubiupdatevol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiupdatevol))
46 54
47//kbuild:lib-$(CONFIG_UBIATTACH) += ubi_attach_detach.o 55//kbuild:lib-$(CONFIG_UBIATTACH) += ubi_tools.o
48//kbuild:lib-$(CONFIG_UBIDETACH) += ubi_attach_detach.o 56//kbuild:lib-$(CONFIG_UBIDETACH) += ubi_tools.o
49//kbuild:lib-$(CONFIG_UBIMKVOL) += ubi_attach_detach.o 57//kbuild:lib-$(CONFIG_UBIMKVOL) += ubi_tools.o
50//kbuild:lib-$(CONFIG_UBIRMVOL) += ubi_attach_detach.o 58//kbuild:lib-$(CONFIG_UBIRMVOL) += ubi_tools.o
51//kbuild:lib-$(CONFIG_UBIRSVOL) += ubi_attach_detach.o 59//kbuild:lib-$(CONFIG_UBIRSVOL) += ubi_tools.o
60//kbuild:lib-$(CONFIG_UBIUPDATEVOL) += ubi_tools.o
52 61
53#include "libbb.h" 62#include "libbb.h"
54#include <mtd/ubi-user.h> 63#include <mtd/ubi-user.h>
@@ -66,6 +75,7 @@
66#define do_mkvol (ENABLE_UBIMKVOL && applet_name[3] == 'm') 75#define do_mkvol (ENABLE_UBIMKVOL && applet_name[3] == 'm')
67#define do_rmvol (ENABLE_UBIRMVOL && applet_name[4] == 'm') 76#define do_rmvol (ENABLE_UBIRMVOL && applet_name[4] == 'm')
68#define do_rsvol (ENABLE_UBIRSVOL && applet_name[4] == 's') 77#define do_rsvol (ENABLE_UBIRSVOL && applet_name[4] == 's')
78#define do_update (ENABLE_UBIUPDATEVOL && applet_name[3] == 'u')
69 79
70//usage:#define ubiattach_trivial_usage 80//usage:#define ubiattach_trivial_usage
71//usage: "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV" 81//usage: "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV"
@@ -85,7 +95,7 @@
85//usage:#define ubimkvol_trivial_usage 95//usage:#define ubimkvol_trivial_usage
86//usage: "UBI_DEVICE -N NAME -s SIZE" 96//usage: "UBI_DEVICE -N NAME -s SIZE"
87//usage:#define ubimkvol_full_usage "\n\n" 97//usage:#define ubimkvol_full_usage "\n\n"
88//usage: "Create UBI Volume\n" 98//usage: "Create UBI volume\n"
89//usage: "\nOptions:" 99//usage: "\nOptions:"
90//usage: "\n -a ALIGNMENT Volume alignment (default 1)" 100//usage: "\n -a ALIGNMENT Volume alignment (default 1)"
91//usage: "\n -n VOLID Volume ID, if not specified, it" 101//usage: "\n -n VOLID Volume ID, if not specified, it"
@@ -97,17 +107,26 @@
97//usage:#define ubirmvol_trivial_usage 107//usage:#define ubirmvol_trivial_usage
98//usage: "UBI_DEVICE -n VOLID" 108//usage: "UBI_DEVICE -n VOLID"
99//usage:#define ubirmvol_full_usage "\n\n" 109//usage:#define ubirmvol_full_usage "\n\n"
100//usage: "Remove UBI Volume\n" 110//usage: "Remove UBI volume\n"
101//usage: "\nOptions:" 111//usage: "\nOptions:"
102//usage: "\n -n VOLID Volume ID" 112//usage: "\n -n VOLID Volume ID"
103//usage: 113//usage:
104//usage:#define ubirsvol_trivial_usage 114//usage:#define ubirsvol_trivial_usage
105//usage: "UBI_DEVICE -N NAME -s SIZE" 115//usage: "UBI_DEVICE -n VOLID -s SIZE"
106//usage:#define ubirsvol_full_usage "\n\n" 116//usage:#define ubirsvol_full_usage "\n\n"
107//usage: "Resize UBI Volume\n" 117//usage: "Resize UBI volume\n"
108//usage: "\nOptions:" 118//usage: "\nOptions:"
109//usage: "\n -N NAME Volume name" 119//usage: "\n -n VOLID Volume ID to resize"
110//usage: "\n -s SIZE Size in bytes" 120//usage: "\n -s SIZE Size in bytes"
121//usage:
122//usage:#define ubiupdatevol_trivial_usage
123//usage: "UBI_DEVICE [IMG_FILE]"
124//usage:#define ubiupdatevol_full_usage "\n\n"
125//usage: "Update UBI volume\n"
126//usage: "\nOptions:"
127//usage: "\n -t Truncate UBI volume"
128//usage: "\n -s SIZE Bytes in input (if reading stdin)"
129
111 130
112int ubi_tools_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 131int ubi_tools_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
113int ubi_tools_main(int argc UNUSED_PARAM, char **argv) 132int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
@@ -124,8 +143,8 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
124 int alignment = 1; 143 int alignment = 1;
125 char *type = NULL; 144 char *type = NULL;
126 145
127 opt_complementary = "=1:m+:d+:n+:s+:a+"; 146 opt_complementary = "-1:m+:d+:n+:s+:a+";
128 opts = getopt32(argv, "m:d:n:N:s:a:t:", 147 opts = getopt32(argv, "m:d:n:N:s:a:t::",
129 &mtd_num, &dev_num, &vol_id, 148 &mtd_num, &dev_num, &vol_id,
130 &vol_name, &size_bytes, &alignment, &type 149 &vol_name, &size_bytes, &alignment, &type
131 ); 150 );
@@ -166,7 +185,7 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
166 185
167 memset(&req, 0, sizeof(req)); 186 memset(&req, 0, sizeof(req));
168 req.vol_id = vol_id; 187 req.vol_id = vol_id;
169 if (opts & OPTION_t) { 188 if ((opts & OPTION_t) && type) {
170 if (type[0] == 's') 189 if (type[0] == 's')
171 req.vol_type = UBI_STATIC_VOLUME; 190 req.vol_type = UBI_STATIC_VOLUME;
172 else 191 else
@@ -199,6 +218,57 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
199 req.vol_id = vol_id; 218 req.vol_id = vol_id;
200 219
201 xioctl(fd, UBI_IOCRSVOL, &req); 220 xioctl(fd, UBI_IOCRSVOL, &req);
221 } else
222 if (do_update) {
223 long long bytes;
224
225 if (opts & OPTION_t) {
226 // truncate the volume by starting an update for size 0
227 bytes = 0;
228 xioctl(fd, UBI_IOCVOLUP, &bytes);
229 }
230 else {
231 struct stat st;
232 char buf[sizeof("/sys/class/ubi/ubi%d_%d/usable_eb_size") + 2 * sizeof(int)*3];
233 int input_fd;
234 unsigned ubinum, volnum;
235 unsigned leb_size;
236 ssize_t len;
237 char *input_data;
238
239 // Make assumption that device not is in normal format.
240 // Removes need for scanning sysfs tree as full libubi does
241 if (sscanf(ubi_ctrl, "/dev/ubi%u_%u", &ubinum, &volnum) != 2)
242 bb_error_msg_and_die("%s volume node not in correct format", "UBI");
243
244 sprintf(buf, "/sys/class/ubi/ubi%u_%u/usable_eb_size", ubinum, volnum);
245 if (open_read_close(buf, buf, sizeof(buf)) <= 0)
246 bb_error_msg_and_die("%s could not get LEB size", "UBI");
247 if (sscanf(buf, "%u", &leb_size) != 1)
248 bb_error_msg_and_die("%s could not get LEB size", "UBI");
249
250 if (opts & OPTION_s) {
251 input_fd = 0;
252 } else {
253 if (!argv[optind+1])
254 bb_show_usage();
255 xstat(argv[optind+1], &st);
256 size_bytes = st.st_size;
257 input_fd = xopen(argv[optind+1], O_RDONLY);
258 }
259
260 bytes = size_bytes;
261 xioctl(fd, UBI_IOCVOLUP, &bytes);
262
263 input_data = xmalloc(leb_size);
264 while ((len = full_read(input_fd, input_data, leb_size)) > 0) {
265 xwrite(fd, input_data, len);
266 }
267 if (len < 0)
268 bb_error_msg_and_die("%s volume update failed", "UBI");
269 if (ENABLE_FEATURE_CLEAN_UP)
270 close(input_fd);
271 }
202 } 272 }
203 273
204 if (ENABLE_FEATURE_CLEAN_UP) 274 if (ENABLE_FEATURE_CLEAN_UP)
diff --git a/networking/ftpgetput.c b/networking/ftpgetput.c
index 66316e21f..f63df55f4 100644
--- a/networking/ftpgetput.c
+++ b/networking/ftpgetput.c
@@ -60,7 +60,7 @@ struct globals {
60 FILE *control_stream; 60 FILE *control_stream;
61 int verbose_flag; 61 int verbose_flag;
62 int do_continue; 62 int do_continue;
63 char buf[1]; /* actually [BUFSZ] */ 63 char buf[4]; /* actually [BUFSZ] */
64} FIX_ALIASING; 64} FIX_ALIASING;
65#define G (*(struct globals*)&bb_common_bufsiz1) 65#define G (*(struct globals*)&bb_common_bufsiz1)
66enum { BUFSZ = COMMON_BUFSIZE - offsetof(struct globals, buf) }; 66enum { BUFSZ = COMMON_BUFSIZE - offsetof(struct globals, buf) };
@@ -105,7 +105,7 @@ static int ftpcmd(const char *s1, const char *s2)
105 } 105 }
106 106
107 do { 107 do {
108 strcpy(buf, "EOF"); 108 strcpy(buf, "EOF"); /* for ftp_die */
109 if (fgets(buf, BUFSZ - 2, control_stream) == NULL) { 109 if (fgets(buf, BUFSZ - 2, control_stream) == NULL) {
110 ftp_die(NULL); 110 ftp_die(NULL);
111 } 111 }
@@ -316,7 +316,6 @@ static const char ftpgetput_longopts[] ALIGN1 =
316int ftpgetput_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 316int ftpgetput_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
317int ftpgetput_main(int argc UNUSED_PARAM, char **argv) 317int ftpgetput_main(int argc UNUSED_PARAM, char **argv)
318{ 318{
319 unsigned opt;
320 const char *port = "ftp"; 319 const char *port = "ftp";
321 /* socket to ftp server */ 320 /* socket to ftp server */
322 321
@@ -345,7 +344,7 @@ int ftpgetput_main(int argc UNUSED_PARAM, char **argv)
345 applet_long_options = ftpgetput_longopts; 344 applet_long_options = ftpgetput_longopts;
346#endif 345#endif
347 opt_complementary = "-2:vv:cc"; /* must have 2 to 3 params; -v and -c count */ 346 opt_complementary = "-2:vv:cc"; /* must have 2 to 3 params; -v and -c count */
348 opt = getopt32(argv, "cvu:p:P:", &user, &password, &port, 347 getopt32(argv, "cvu:p:P:", &user, &password, &port,
349 &verbose_flag, &do_continue); 348 &verbose_flag, &do_continue);
350 argv += optind; 349 argv += optind;
351 350
diff --git a/networking/httpd.c b/networking/httpd.c
index d6157aca2..d77342a2a 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -2424,7 +2424,7 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2424 salt[0] = '$'; 2424 salt[0] = '$';
2425 salt[1] = '1'; 2425 salt[1] = '1';
2426 salt[2] = '$'; 2426 salt[2] = '$';
2427 crypt_make_salt(salt + 3, 4, 0); 2427 crypt_make_salt(salt + 3, 4);
2428 puts(pw_encrypt(pass, salt, 1)); 2428 puts(pw_encrypt(pass, salt, 1));
2429 return 0; 2429 return 0;
2430 } 2430 }
diff --git a/networking/inetd.c b/networking/inetd.c
index 226a6491c..58ae089d1 100644
--- a/networking/inetd.c
+++ b/networking/inetd.c
@@ -1164,12 +1164,17 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
1164 sigaddset(&sa.sa_mask, SIGALRM); 1164 sigaddset(&sa.sa_mask, SIGALRM);
1165 sigaddset(&sa.sa_mask, SIGCHLD); 1165 sigaddset(&sa.sa_mask, SIGCHLD);
1166 sigaddset(&sa.sa_mask, SIGHUP); 1166 sigaddset(&sa.sa_mask, SIGHUP);
1167//FIXME: explain why no SA_RESTART
1168//FIXME: retry_network_setup is unsafe to run in signal handler (many reasons)!
1167 sa.sa_handler = retry_network_setup; 1169 sa.sa_handler = retry_network_setup;
1168 sigaction_set(SIGALRM, &sa); 1170 sigaction_set(SIGALRM, &sa);
1171//FIXME: reread_config_file is unsafe to run in signal handler(many reasons)!
1169 sa.sa_handler = reread_config_file; 1172 sa.sa_handler = reread_config_file;
1170 sigaction_set(SIGHUP, &sa); 1173 sigaction_set(SIGHUP, &sa);
1174//FIXME: reap_child is unsafe to run in signal handler (uses stdio)!
1171 sa.sa_handler = reap_child; 1175 sa.sa_handler = reap_child;
1172 sigaction_set(SIGCHLD, &sa); 1176 sigaction_set(SIGCHLD, &sa);
1177//FIXME: clean_up_and_exit is unsafe to run in signal handler (uses stdio)!
1173 sa.sa_handler = clean_up_and_exit; 1178 sa.sa_handler = clean_up_and_exit;
1174 sigaction_set(SIGTERM, &sa); 1179 sigaction_set(SIGTERM, &sa);
1175 sa.sa_handler = clean_up_and_exit; 1180 sa.sa_handler = clean_up_and_exit;
diff --git a/networking/ntpd.c b/networking/ntpd.c
index e27dbaa6b..165673a1e 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -1745,7 +1745,7 @@ static NOINLINE void
1745recv_and_process_client_pkt(void /*int fd*/) 1745recv_and_process_client_pkt(void /*int fd*/)
1746{ 1746{
1747 ssize_t size; 1747 ssize_t size;
1748 uint8_t version; 1748 //uint8_t version;
1749 len_and_sockaddr *to; 1749 len_and_sockaddr *to;
1750 struct sockaddr *from; 1750 struct sockaddr *from;
1751 msg_t msg; 1751 msg_t msg;
@@ -1793,7 +1793,7 @@ recv_and_process_client_pkt(void /*int fd*/)
1793 msg.m_rootdelay = d_to_sfp(G.rootdelay); 1793 msg.m_rootdelay = d_to_sfp(G.rootdelay);
1794//simple code does not do this, fix simple code! 1794//simple code does not do this, fix simple code!
1795 msg.m_rootdisp = d_to_sfp(G.rootdisp); 1795 msg.m_rootdisp = d_to_sfp(G.rootdisp);
1796 version = (query_status & VERSION_MASK); /* ... >> VERSION_SHIFT - done below instead */ 1796 //version = (query_status & VERSION_MASK); /* ... >> VERSION_SHIFT - done below instead */
1797 msg.m_refid = G.refid; // (version > (3 << VERSION_SHIFT)) ? G.refid : G.refid3; 1797 msg.m_refid = G.refid; // (version > (3 << VERSION_SHIFT)) ? G.refid : G.refid3;
1798 1798
1799 /* We reply from the local address packet was sent to, 1799 /* We reply from the local address packet was sent to,
diff --git a/networking/traceroute.c b/networking/traceroute.c
index 96f9d3472..85181ab8d 100644
--- a/networking/traceroute.c
+++ b/networking/traceroute.c
@@ -398,18 +398,23 @@ static len_and_sockaddr* dup_sockaddr(const len_and_sockaddr *lsa)
398 398
399 399
400static int 400static int
401wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to) 401wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to, unsigned *timestamp_us, int *left_ms)
402{ 402{
403 struct pollfd pfd[1]; 403 struct pollfd pfd[1];
404 int read_len = 0; 404 int read_len = 0;
405 405
406 pfd[0].fd = rcvsock; 406 pfd[0].fd = rcvsock;
407 pfd[0].events = POLLIN; 407 pfd[0].events = POLLIN;
408 if (safe_poll(pfd, 1, waittime * 1000) > 0) { 408 if (*left_ms >= 0 && safe_poll(pfd, 1, *left_ms) > 0) {
409 unsigned t;
410
409 read_len = recv_from_to(rcvsock, 411 read_len = recv_from_to(rcvsock,
410 recv_pkt, sizeof(recv_pkt), 412 recv_pkt, sizeof(recv_pkt),
411 /*flags:*/ 0, 413 /*flags:*/ MSG_DONTWAIT,
412 &from_lsa->u.sa, to, from_lsa->len); 414 &from_lsa->u.sa, to, from_lsa->len);
415 t = monotonic_us();
416 *left_ms -= (t - *timestamp_us) / 1000;
417 *timestamp_us = t;
413 } 418 }
414 419
415 return read_len; 420 return read_len;
@@ -730,7 +735,7 @@ packet_ok(int read_len, len_and_sockaddr *from_lsa,
730 type, pr_type(type), icp->icmp6_code); 735 type, pr_type(type), icp->icmp6_code);
731 736
732 read_len -= sizeof(struct icmp6_hdr); 737 read_len -= sizeof(struct icmp6_hdr);
733 for (i = 0; i < read_len ; i++) { 738 for (i = 0; i < read_len; i++) {
734 if (i % 16 == 0) 739 if (i % 16 == 0)
735 printf("%04x:", i); 740 printf("%04x:", i);
736 if (i % 4 == 0) 741 if (i % 4 == 0)
@@ -819,7 +824,6 @@ print_delta_ms(unsigned t1p, unsigned t2p)
819static int 824static int
820common_traceroute_main(int op, char **argv) 825common_traceroute_main(int op, char **argv)
821{ 826{
822 int i;
823 int minpacket; 827 int minpacket;
824 int tos = 0; 828 int tos = 0;
825 int max_ttl = 30; 829 int max_ttl = 30;
@@ -973,6 +977,7 @@ common_traceroute_main(int op, char **argv)
973#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE && defined IP_OPTIONS 977#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE && defined IP_OPTIONS
974 if (lsrr > 0) { 978 if (lsrr > 0) {
975 unsigned char optlist[MAX_IPOPTLEN]; 979 unsigned char optlist[MAX_IPOPTLEN];
980 unsigned size;
976 981
977 /* final hop */ 982 /* final hop */
978 gwlist[lsrr] = dest_lsa->u.sin.sin_addr.s_addr; 983 gwlist[lsrr] = dest_lsa->u.sin.sin_addr.s_addr;
@@ -982,14 +987,14 @@ common_traceroute_main(int op, char **argv)
982 optlist[0] = IPOPT_NOP; 987 optlist[0] = IPOPT_NOP;
983 /* loose source route option */ 988 /* loose source route option */
984 optlist[1] = IPOPT_LSRR; 989 optlist[1] = IPOPT_LSRR;
985 i = lsrr * sizeof(gwlist[0]); 990 size = lsrr * sizeof(gwlist[0]);
986 optlist[2] = i + 3; 991 optlist[2] = size + 3;
987 /* pointer to LSRR addresses */ 992 /* pointer to LSRR addresses */
988 optlist[3] = IPOPT_MINOFF; 993 optlist[3] = IPOPT_MINOFF;
989 memcpy(optlist + 4, gwlist, i); 994 memcpy(optlist + 4, gwlist, size);
990 995
991 if (setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS, 996 if (setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS,
992 (char *)optlist, i + sizeof(gwlist[0])) < 0) { 997 (char *)optlist, size + sizeof(gwlist[0])) < 0) {
993 bb_perror_msg_and_die("IP_OPTIONS"); 998 bb_perror_msg_and_die("IP_OPTIONS");
994 } 999 }
995 } 1000 }
@@ -1103,28 +1108,34 @@ common_traceroute_main(int op, char **argv)
1103 int unreachable = 0; /* counter */ 1108 int unreachable = 0; /* counter */
1104 int gotlastaddr = 0; /* flags */ 1109 int gotlastaddr = 0; /* flags */
1105 int got_there = 0; 1110 int got_there = 0;
1106 int first = 1;
1107 1111
1108 printf("%2d", ttl); 1112 printf("%2d", ttl);
1109 for (probe = 0; probe < nprobes; ++probe) { 1113 for (probe = 0; probe < nprobes; ++probe) {
1110 int read_len; 1114 int read_len;
1111 unsigned t1; 1115 unsigned t1;
1112 unsigned t2; 1116 unsigned t2;
1117 int left_ms;
1113 struct ip *ip; 1118 struct ip *ip;
1114 1119
1115 if (!first && pausemsecs > 0)
1116 usleep(pausemsecs * 1000);
1117 fflush_all(); 1120 fflush_all();
1121 if (probe != 0 && pausemsecs > 0)
1122 usleep(pausemsecs * 1000);
1118 1123
1119 t1 = monotonic_us();
1120 send_probe(++seq, ttl); 1124 send_probe(++seq, ttl);
1125 t2 = t1 = monotonic_us();
1126
1127 left_ms = waittime * 1000;
1128 while ((read_len = wait_for_reply(from_lsa, to, &t2, &left_ms)) != 0) {
1129 int icmp_code;
1130
1131 /* Recv'ed a packet, or read error */
1132 /* t2 = monotonic_us() - set by wait_for_reply */
1121 1133
1122 first = 0; 1134 if (read_len < 0)
1123 while ((read_len = wait_for_reply(from_lsa, to)) != 0) { 1135 continue;
1124 t2 = monotonic_us(); 1136 icmp_code = packet_ok(read_len, from_lsa, to, seq);
1125 i = packet_ok(read_len, from_lsa, to, seq);
1126 /* Skip short packet */ 1137 /* Skip short packet */
1127 if (i == 0) 1138 if (icmp_code == 0)
1128 continue; 1139 continue;
1129 1140
1130 if (!gotlastaddr 1141 if (!gotlastaddr
@@ -1143,10 +1154,10 @@ common_traceroute_main(int op, char **argv)
1143 printf(" (%d)", ip->ip_ttl); 1154 printf(" (%d)", ip->ip_ttl);
1144 1155
1145 /* time exceeded in transit */ 1156 /* time exceeded in transit */
1146 if (i == -1) 1157 if (icmp_code == -1)
1147 break; 1158 break;
1148 i--; 1159 icmp_code--;
1149 switch (i) { 1160 switch (icmp_code) {
1150#if ENABLE_TRACEROUTE6 1161#if ENABLE_TRACEROUTE6
1151 case ICMP6_DST_UNREACH_NOPORT << 8: 1162 case ICMP6_DST_UNREACH_NOPORT << 8:
1152 got_there = 1; 1163 got_there = 1;
@@ -1219,16 +1230,18 @@ common_traceroute_main(int op, char **argv)
1219 ++unreachable; 1230 ++unreachable;
1220 break; 1231 break;
1221 default: 1232 default:
1222 printf(" !<%d>", i); 1233 printf(" !<%d>", icmp_code);
1223 ++unreachable; 1234 ++unreachable;
1224 break; 1235 break;
1225 } 1236 }
1226 break; 1237 break;
1227 } 1238 } /* while (wait and read a packet) */
1239
1228 /* there was no packet at all? */ 1240 /* there was no packet at all? */
1229 if (read_len == 0) 1241 if (read_len == 0)
1230 printf(" *"); 1242 printf(" *");
1231 } 1243 } /* for (nprobes) */
1244
1232 bb_putchar('\n'); 1245 bb_putchar('\n');
1233 if (got_there 1246 if (got_there
1234 || (unreachable > 0 && unreachable >= nprobes - 1) 1247 || (unreachable > 0 && unreachable >= nprobes - 1)
diff --git a/networking/udhcp/Config.src b/networking/udhcp/Config.src
index 9cd8cbbae..6bfa398ea 100644
--- a/networking/udhcp/Config.src
+++ b/networking/udhcp/Config.src
@@ -113,6 +113,14 @@ config FEATURE_UDHCP_RFC3397
113 search lists via option 119, specified in RFC 3397, 113 search lists via option 119, specified in RFC 3397,
114 and SIP servers option 120, specified in RFC 3361. 114 and SIP servers option 120, specified in RFC 3361.
115 115
116config FEATURE_UDHCP_8021Q
117 bool "Support for 802.1Q VLAN parameters"
118 default y
119 depends on UDHCPD || UDHCPC
120 help
121 If selected, both client and server will support passing of VLAN
122 ID and priority via options 132 and 133 as per 802.1Q.
123
116config UDHCPC_DEFAULT_SCRIPT 124config UDHCPC_DEFAULT_SCRIPT
117 string "Absolute path to config script" 125 string "Absolute path to config script"
118 default "/usr/share/udhcpc/default.script" 126 default "/usr/share/udhcpc/default.script"
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c
index 0a60261ab..70e34614c 100644
--- a/networking/udhcp/common.c
+++ b/networking/udhcp/common.c
@@ -55,6 +55,10 @@ const struct dhcp_optflag dhcp_optflags[] = {
55 { OPTION_SIP_SERVERS , 0x78 }, /* DHCP_SIP_SERVERS */ 55 { OPTION_SIP_SERVERS , 0x78 }, /* DHCP_SIP_SERVERS */
56#endif 56#endif
57 { OPTION_STATIC_ROUTES , 0x79 }, /* DHCP_STATIC_ROUTES */ 57 { OPTION_STATIC_ROUTES , 0x79 }, /* DHCP_STATIC_ROUTES */
58#if ENABLE_FEATURE_UDHCP_8021Q
59 { OPTION_U16 , 0x84 }, /* DHCP_VLAN_ID */
60 { OPTION_U8 , 0x85 }, /* DHCP_VLAN_PRIORITY */
61#endif
58 { OPTION_STATIC_ROUTES , 0xf9 }, /* DHCP_MS_STATIC_ROUTES */ 62 { OPTION_STATIC_ROUTES , 0xf9 }, /* DHCP_MS_STATIC_ROUTES */
59 { OPTION_STRING , 0xfc }, /* DHCP_WPAD */ 63 { OPTION_STRING , 0xfc }, /* DHCP_WPAD */
60 64
@@ -118,6 +122,10 @@ const char dhcp_option_strings[] ALIGN1 =
118// doesn't work in udhcpd.conf since OPTION_STATIC_ROUTES 122// doesn't work in udhcpd.conf since OPTION_STATIC_ROUTES
119// is not handled yet by "string->option" conversion code: 123// is not handled yet by "string->option" conversion code:
120 "staticroutes" "\0"/* DHCP_STATIC_ROUTES */ 124 "staticroutes" "\0"/* DHCP_STATIC_ROUTES */
125#if ENABLE_FEATURE_UDHCP_8021Q
126 "vlanid" "\0" /* DHCP_VLAN_ID */
127 "vlanpriority" "\0"/* DHCP_VLAN_PRIORITY */
128#endif
121 "msstaticroutes""\0"/* DHCP_MS_STATIC_ROUTES */ 129 "msstaticroutes""\0"/* DHCP_MS_STATIC_ROUTES */
122 "wpad" "\0" /* DHCP_WPAD */ 130 "wpad" "\0" /* DHCP_WPAD */
123 ; 131 ;
diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h
index f8f18ff01..ad6991c94 100644
--- a/networking/udhcp/common.h
+++ b/networking/udhcp/common.h
@@ -145,6 +145,8 @@ enum {
145//#define DHCP_DOMAIN_SEARCH 0x77 /* RFC 3397. set of ASCIZ string, DNS-style compressed */ 145//#define DHCP_DOMAIN_SEARCH 0x77 /* RFC 3397. set of ASCIZ string, DNS-style compressed */
146//#define DHCP_SIP_SERVERS 0x78 /* RFC 3361. flag byte, then: 0: domain names, 1: IP addrs */ 146//#define DHCP_SIP_SERVERS 0x78 /* RFC 3361. flag byte, then: 0: domain names, 1: IP addrs */
147//#define DHCP_STATIC_ROUTES 0x79 /* RFC 3442. (mask,ip,router) tuples */ 147//#define DHCP_STATIC_ROUTES 0x79 /* RFC 3442. (mask,ip,router) tuples */
148#define DHCP_VLAN_ID 0x84 /* 802.1P VLAN ID */
149#define DHCP_VLAN_PRIORITY 0x85 /* 802.1Q VLAN priority */
148//#define DHCP_MS_STATIC_ROUTES 0xf9 /* Microsoft's pre-RFC 3442 code for 0x79? */ 150//#define DHCP_MS_STATIC_ROUTES 0xf9 /* Microsoft's pre-RFC 3442 code for 0x79? */
149//#define DHCP_WPAD 0xfc /* MSIE's Web Proxy Autodiscovery Protocol */ 151//#define DHCP_WPAD 0xfc /* MSIE's Web Proxy Autodiscovery Protocol */
150#define DHCP_END 0xff 152#define DHCP_END 0xff
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index ca82d37e6..510c3a1d0 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -300,6 +300,14 @@ static char **fill_envp(struct dhcp_packet *packet)
300 uint8_t *temp; 300 uint8_t *temp;
301 uint8_t overload = 0; 301 uint8_t overload = 0;
302 302
303#define BITMAP unsigned
304#define BBITS (sizeof(BITMAP) * 8)
305#define BMASK(i) (1 << (i & (sizeof(BITMAP) * 8 - 1)))
306#define FOUND_OPTS(i) (found_opts[(unsigned)i / BBITS])
307 BITMAP found_opts[256 / BBITS];
308
309 memset(found_opts, 0, sizeof(found_opts));
310
303 /* We need 6 elements for: 311 /* We need 6 elements for:
304 * "interface=IFACE" 312 * "interface=IFACE"
305 * "ip=N.N.N.N" from packet->yiaddr 313 * "ip=N.N.N.N" from packet->yiaddr
@@ -311,18 +319,21 @@ static char **fill_envp(struct dhcp_packet *packet)
311 envc = 6; 319 envc = 6;
312 /* +1 element for each option, +2 for subnet option: */ 320 /* +1 element for each option, +2 for subnet option: */
313 if (packet) { 321 if (packet) {
314 for (i = 0; dhcp_optflags[i].code; i++) { 322 /* note: do not search for "pad" (0) and "end" (255) options */
315 if (udhcp_get_option(packet, dhcp_optflags[i].code)) { 323 for (i = 1; i < 255; i++) {
316 if (dhcp_optflags[i].code == DHCP_SUBNET) 324 temp = udhcp_get_option(packet, i);
325 if (temp) {
326 if (i == DHCP_OPTION_OVERLOAD)
327 overload = *temp;
328 else if (i == DHCP_SUBNET)
317 envc++; /* for mton */ 329 envc++; /* for mton */
318 envc++; 330 envc++;
331 /*if (i != DHCP_MESSAGE_TYPE)*/
332 FOUND_OPTS(i) |= BMASK(i);
319 } 333 }
320 } 334 }
321 temp = udhcp_get_option(packet, DHCP_OPTION_OVERLOAD);
322 if (temp)
323 overload = *temp;
324 } 335 }
325 curr = envp = xzalloc(sizeof(char *) * envc); 336 curr = envp = xzalloc(sizeof(envp[0]) * envc);
326 337
327 *curr = xasprintf("interface=%s", client_config.interface); 338 *curr = xasprintf("interface=%s", client_config.interface);
328 putenv(*curr++); 339 putenv(*curr++);
@@ -337,12 +348,16 @@ static char **fill_envp(struct dhcp_packet *packet)
337 opt_name = dhcp_option_strings; 348 opt_name = dhcp_option_strings;
338 i = 0; 349 i = 0;
339 while (*opt_name) { 350 while (*opt_name) {
340 temp = udhcp_get_option(packet, dhcp_optflags[i].code); 351 uint8_t code = dhcp_optflags[i].code;
341 if (!temp) 352 BITMAP *found_ptr = &FOUND_OPTS(code);
353 BITMAP found_mask = BMASK(code);
354 if (!(*found_ptr & found_mask))
342 goto next; 355 goto next;
356 *found_ptr &= ~found_mask; /* leave only unknown options */
357 temp = udhcp_get_option(packet, code);
343 *curr = xmalloc_optname_optval(temp, &dhcp_optflags[i], opt_name); 358 *curr = xmalloc_optname_optval(temp, &dhcp_optflags[i], opt_name);
344 putenv(*curr++); 359 putenv(*curr++);
345 if (dhcp_optflags[i].code == DHCP_SUBNET) { 360 if (code == DHCP_SUBNET) {
346 /* Subnet option: make things like "$ip/$mask" possible */ 361 /* Subnet option: make things like "$ip/$mask" possible */
347 uint32_t subnet; 362 uint32_t subnet;
348 move_from_unaligned32(subnet, temp); 363 move_from_unaligned32(subnet, temp);
@@ -368,6 +383,28 @@ static char **fill_envp(struct dhcp_packet *packet)
368 *curr = xasprintf("sname=%."DHCP_PKT_SNAME_LEN_STR"s", packet->sname); 383 *curr = xasprintf("sname=%."DHCP_PKT_SNAME_LEN_STR"s", packet->sname);
369 putenv(*curr++); 384 putenv(*curr++);
370 } 385 }
386 /* Handle unknown options */
387 for (i = 0; i < 256;) {
388 BITMAP bitmap = FOUND_OPTS(i);
389 if (!bitmap) {
390 i += BBITS;
391 continue;
392 }
393 if (bitmap & BMASK(i)) {
394 unsigned len, ofs;
395
396 temp = udhcp_get_option(packet, i);
397 /* udhcp_get_option returns ptr to data portion,
398 * need to go back to get len
399 */
400 len = temp[-OPT_DATA + OPT_LEN];
401 *curr = xmalloc(sizeof("optNNN=") + 1 + len*2);
402 ofs = sprintf(*curr, "opt%u=", i);
403 bin2hex(*curr + ofs, (void*) temp, len)[0] = '\0';
404 putenv(*curr++);
405 }
406 i++;
407 }
371 return envp; 408 return envp;
372} 409}
373 410
diff --git a/networking/udhcp/files.c b/networking/udhcp/files.c
index 49bcafb9c..6840f3c25 100644
--- a/networking/udhcp/files.c
+++ b/networking/udhcp/files.c
@@ -80,9 +80,9 @@ static const struct config_keyword keywords[] = {
80 /* keywords with no defaults must be last! */ 80 /* keywords with no defaults must be last! */
81 {"option" , udhcp_str2optset, &server_config.options , ""}, 81 {"option" , udhcp_str2optset, &server_config.options , ""},
82 {"opt" , udhcp_str2optset, &server_config.options , ""}, 82 {"opt" , udhcp_str2optset, &server_config.options , ""},
83 {"notify_file" , read_str , &server_config.notify_file , ""}, 83 {"notify_file" , read_str , &server_config.notify_file , NULL},
84 {"sname" , read_str , &server_config.sname , ""}, 84 {"sname" , read_str , &server_config.sname , NULL},
85 {"boot_file" , read_str , &server_config.boot_file , ""}, 85 {"boot_file" , read_str , &server_config.boot_file , NULL},
86 {"static_lease" , read_staticlease, &server_config.static_leases, ""}, 86 {"static_lease" , read_staticlease, &server_config.static_leases, ""},
87}; 87};
88enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 }; 88enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 };
diff --git a/printutils/lpd.c b/printutils/lpd.c
index 115552e0b..642e8a89e 100644
--- a/printutils/lpd.c
+++ b/printutils/lpd.c
@@ -102,7 +102,7 @@ static char *xmalloc_read_stdin(void)
102{ 102{
103 // SECURITY: 103 // SECURITY:
104 size_t max = 4 * 1024; // more than enough for commands! 104 size_t max = 4 * 1024; // more than enough for commands!
105 return xmalloc_reads(STDIN_FILENO, NULL, &max); 105 return xmalloc_reads(STDIN_FILENO, &max);
106} 106}
107 107
108int lpd_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; 108int lpd_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE;
diff --git a/procps/fuser.c b/procps/fuser.c
index 7837ff883..8d63a7313 100644
--- a/procps/fuser.c
+++ b/procps/fuser.c
@@ -37,33 +37,18 @@ typedef struct inode_list {
37 dev_t dev; 37 dev_t dev;
38} inode_list; 38} inode_list;
39 39
40typedef struct pid_list {
41 struct pid_list *next;
42 pid_t pid;
43} pid_list;
44
45
46struct globals { 40struct globals {
47 pid_list *pid_list_head; 41 int recursion_depth;
42 pid_t mypid;
48 inode_list *inode_list_head; 43 inode_list *inode_list_head;
44 smallint kill_failed;
45 int killsig;
49} FIX_ALIASING; 46} FIX_ALIASING;
50#define G (*(struct globals*)&bb_common_bufsiz1) 47#define G (*(struct globals*)&bb_common_bufsiz1)
51#define INIT_G() do { } while (0) 48#define INIT_G() do { \
52 49 G.mypid = getpid(); \
53 50 G.killsig = SIGKILL; \
54static void add_pid(const pid_t pid) 51} while (0)
55{
56 pid_list **curr = &G.pid_list_head;
57
58 while (*curr) {
59 if ((*curr)->pid == pid)
60 return;
61 curr = &(*curr)->next;
62 }
63
64 *curr = xzalloc(sizeof(pid_list));
65 (*curr)->pid = pid;
66}
67 52
68static void add_inode(const struct stat *st) 53static void add_inode(const struct stat *st)
69{ 54{
@@ -83,48 +68,7 @@ static void add_inode(const struct stat *st)
83 (*curr)->inode = st->st_ino; 68 (*curr)->inode = st->st_ino;
84} 69}
85 70
86static void scan_proc_net(const char *path, unsigned port) 71static smallint search_dev_inode(const struct stat *st)
87{
88 char line[MAX_LINE + 1];
89 long long uint64_inode;
90 unsigned tmp_port;
91 FILE *f;
92 struct stat st;
93 int fd;
94
95 /* find socket dev */
96 st.st_dev = 0;
97 fd = socket(AF_INET, SOCK_DGRAM, 0);
98 if (fd >= 0) {
99 fstat(fd, &st);
100 close(fd);
101 }
102
103 f = fopen_for_read(path);
104 if (!f)
105 return;
106
107 while (fgets(line, MAX_LINE, f)) {
108 char addr[68];
109 if (sscanf(line, "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x "
110 "%*x:%*x %*x %*d %*d %llu",
111 addr, &tmp_port, &uint64_inode) == 3
112 ) {
113 int len = strlen(addr);
114 if (len == 8 && (option_mask32 & OPT_IP6))
115 continue;
116 if (len > 8 && (option_mask32 & OPT_IP4))
117 continue;
118 if (tmp_port == port) {
119 st.st_ino = uint64_inode;
120 add_inode(&st);
121 }
122 }
123 }
124 fclose(f);
125}
126
127static int search_dev_inode(const struct stat *st)
128{ 72{
129 inode_list *ilist = G.inode_list_head; 73 inode_list *ilist = G.inode_list_head;
130 74
@@ -140,130 +84,202 @@ static int search_dev_inode(const struct stat *st)
140 return 0; 84 return 0;
141} 85}
142 86
143static void scan_pid_maps(const char *fname, pid_t pid) 87enum {
88 PROC_NET = 0,
89 PROC_DIR,
90 PROC_DIR_LINKS,
91 PROC_SUBDIR_LINKS,
92};
93
94static smallint scan_proc_net_or_maps(const char *path, unsigned port)
144{ 95{
145 FILE *file; 96 FILE *f;
146 char line[MAX_LINE + 1]; 97 char line[MAX_LINE + 1], addr[68];
147 int major, minor; 98 int major, minor, r;
148 long long uint64_inode; 99 long long uint64_inode;
149 struct stat st; 100 unsigned tmp_port;
101 smallint retval;
102 struct stat statbuf;
103 const char *fmt;
104 void *fag, *sag;
150 105
151 file = fopen_for_read(fname); 106 f = fopen_for_read(path);
152 if (!file) 107 if (!f)
153 return; 108 return 0;
154 109
155 while (fgets(line, MAX_LINE, file)) { 110 if (G.recursion_depth == PROC_NET) {
156 if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3) 111 int fd;
157 continue;
158 st.st_ino = uint64_inode;
159 if (major == 0 && minor == 0 && st.st_ino == 0)
160 continue;
161 st.st_dev = makedev(major, minor);
162 if (search_dev_inode(&st))
163 add_pid(pid);
164 }
165 fclose(file);
166}
167 112
168static void scan_link(const char *lname, pid_t pid) 113 /* find socket dev */
169{ 114 statbuf.st_dev = 0;
170 struct stat st; 115 fd = socket(AF_INET, SOCK_DGRAM, 0);
116 if (fd >= 0) {
117 fstat(fd, &statbuf);
118 close(fd);
119 }
171 120
172 if (stat(lname, &st) >= 0) { 121 fmt = "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x "
173 if (search_dev_inode(&st)) 122 "%*x:%*x %*x:%*x %*x %*d %*d %llu";
174 add_pid(pid); 123 fag = addr;
124 sag = &tmp_port;
125 } else {
126 fmt = "%*s %*s %*s %x:%x %llu";
127 fag = &major;
128 sag = &minor;
175 } 129 }
176}
177
178static void scan_dir_links(const char *dname, pid_t pid)
179{
180 DIR *d;
181 struct dirent *de;
182 char *lname;
183
184 d = opendir(dname);
185 if (!d)
186 return;
187 130
188 while ((de = readdir(d)) != NULL) { 131 retval = 0;
189 lname = concat_subpath_file(dname, de->d_name); 132 while (fgets(line, MAX_LINE, f)) {
190 if (lname == NULL) 133 r = sscanf(line, fmt, fag, sag, &uint64_inode);
134 if (r != 3)
191 continue; 135 continue;
192 scan_link(lname, pid); 136
193 free(lname); 137 statbuf.st_ino = uint64_inode;
138 if (G.recursion_depth == PROC_NET) {
139 r = strlen(addr);
140 if (r == 8 && (option_mask32 & OPT_IP6))
141 continue;
142 if (r > 8 && (option_mask32 & OPT_IP4))
143 continue;
144 if (tmp_port == port)
145 add_inode(&statbuf);
146 } else {
147 if (major != 0 && minor != 0 && statbuf.st_ino != 0) {
148 statbuf.st_dev = makedev(major, minor);
149 retval = search_dev_inode(&statbuf);
150 if (retval)
151 break;
152 }
153 }
194 } 154 }
195 closedir(d); 155 fclose(f);
156
157 return retval;
196} 158}
197 159
198/* NB: does chdir internally */ 160static smallint scan_recursive(const char *path)
199static void scan_proc_pids(void)
200{ 161{
201 DIR *d; 162 DIR *d;
202 struct dirent *de; 163 struct dirent *d_ent;
203 pid_t pid; 164 smallint stop_scan;
204 165 smallint retval;
205 xchdir("/proc"); 166
206 d = opendir("/proc"); 167 d = opendir(path);
207 if (!d) 168 if (d == NULL)
208 return; 169 return 0;
209 170
210 while ((de = readdir(d)) != NULL) { 171 G.recursion_depth++;
211 pid = (pid_t)bb_strtou(de->d_name, NULL, 10); 172 retval = 0;
212 if (errno) 173 stop_scan = 0;
213 continue; 174 while (!stop_scan && (d_ent = readdir(d)) != NULL) {
214 if (chdir(de->d_name) < 0) 175 struct stat statbuf;
215 continue; 176 pid_t pid;
216 scan_link("cwd", pid); 177 char *subpath;
217 scan_link("exe", pid); 178
218 scan_link("root", pid); 179 subpath = concat_subpath_file(path, d_ent->d_name);
219 180 if (subpath == NULL)
220 scan_dir_links("fd", pid); 181 continue; /* . or .. */
221 scan_dir_links("lib", pid); 182
222 scan_dir_links("mmap", pid); 183 switch (G.recursion_depth) {
184 case PROC_DIR:
185 pid = (pid_t)bb_strtou(d_ent->d_name, NULL, 10);
186 if (errno != 0
187 || pid == G.mypid
188 /* "this PID doesn't use specified FILEs or PORT/PROTO": */
189 || scan_recursive(subpath) == 0
190 ) {
191 break;
192 }
193 if (option_mask32 & OPT_KILL) {
194 if (kill(pid, G.killsig) != 0) {
195 bb_perror_msg("kill pid %s", d_ent->d_name);
196 G.kill_failed = 1;
197 }
198 }
199 if (!(option_mask32 & OPT_SILENT))
200 printf("%s ", d_ent->d_name);
201 retval = 1;
202 break;
223 203
224 scan_pid_maps("maps", pid); 204 case PROC_DIR_LINKS:
225 xchdir("/proc"); 205 switch (
206 index_in_substrings(
207 "cwd" "\0" "exe" "\0"
208 "root" "\0" "fd" "\0"
209 "lib" "\0" "mmap" "\0"
210 "maps" "\0",
211 d_ent->d_name
212 )
213 ) {
214 enum {
215 CWD_LINK,
216 EXE_LINK,
217 ROOT_LINK,
218 FD_DIR_LINKS,
219 LIB_DIR_LINKS,
220 MMAP_DIR_LINKS,
221 MAPS,
222 };
223 case CWD_LINK:
224 case EXE_LINK:
225 case ROOT_LINK:
226 goto scan_link;
227 case FD_DIR_LINKS:
228 case LIB_DIR_LINKS:
229 case MMAP_DIR_LINKS:
230 stop_scan = scan_recursive(subpath);
231 if (stop_scan)
232 retval = stop_scan;
233 break;
234 case MAPS:
235 stop_scan = scan_proc_net_or_maps(subpath, 0);
236 if (stop_scan)
237 retval = stop_scan;
238 default:
239 break;
240 }
241 break;
242 case PROC_SUBDIR_LINKS:
243 scan_link:
244 if (stat(subpath, &statbuf) < 0)
245 break;
246 stop_scan = search_dev_inode(&statbuf);
247 if (stop_scan)
248 retval = stop_scan;
249 default:
250 break;
251 }
252 free(subpath);
226 } 253 }
227 closedir(d); 254 closedir(d);
255 G.recursion_depth--;
256 return retval;
228} 257}
229 258
230int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 259int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
231int fuser_main(int argc UNUSED_PARAM, char **argv) 260int fuser_main(int argc UNUSED_PARAM, char **argv)
232{ 261{
233 pid_list *plist;
234 pid_t mypid;
235 char **pp; 262 char **pp;
236 struct stat st; 263
237 unsigned port; 264 INIT_G();
238 int opt; 265
239 int exitcode;
240 int killsig;
241/*
242fuser [OPTIONS] FILE or PORT/PROTO
243Find processes which use FILEs or PORTs
244 -m Find processes which use same fs as FILEs
245 -4 Search only IPv4 space
246 -6 Search only IPv6 space
247 -s Don't display PIDs
248 -k Kill found processes
249 -SIGNAL Signal to send (default: KILL)
250*/
251 /* Handle -SIGNAL. Oh my... */ 266 /* Handle -SIGNAL. Oh my... */
252 killsig = SIGKILL; /* yes, the default is not SIGTERM */
253 pp = argv; 267 pp = argv;
254 while (*++pp) { 268 while (*++pp) {
269 int sig;
255 char *arg = *pp; 270 char *arg = *pp;
271
256 if (arg[0] != '-') 272 if (arg[0] != '-')
257 continue; 273 continue;
258 if (arg[1] == '-' && arg[2] == '\0') /* "--" */ 274 if (arg[1] == '-' && arg[2] == '\0') /* "--" */
259 break; 275 break;
260 if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0') 276 if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0')
261 continue; /* it's "-4" or "-6" */ 277 continue; /* it's "-4" or "-6" */
262 opt = get_signum(&arg[1]); 278 sig = get_signum(&arg[1]);
263 if (opt < 0) 279 if (sig < 0)
264 continue; 280 continue;
265 /* "-SIGNAL" option found. Remove it and bail out */ 281 /* "-SIGNAL" option found. Remove it and bail out */
266 killsig = opt; 282 G.killsig = sig;
267 do { 283 do {
268 pp[0] = arg = pp[1]; 284 pp[0] = arg = pp[1];
269 pp++; 285 pp++;
@@ -272,57 +288,35 @@ Find processes which use FILEs or PORTs
272 } 288 }
273 289
274 opt_complementary = "-1"; /* at least one param */ 290 opt_complementary = "-1"; /* at least one param */
275 opt = getopt32(argv, OPTION_STRING); 291 getopt32(argv, OPTION_STRING);
276 argv += optind; 292 argv += optind;
277 293
278 pp = argv; 294 pp = argv;
279 while (*pp) { 295 while (*pp) {
280 /* parse net arg */ 296 /* parse net arg */
281 char path[20], tproto[5]; 297 unsigned port;
282 if (sscanf(*pp, "%u/%4s", &port, tproto) != 2) 298 char path[sizeof("/proc/net/TCP6")];
283 goto file; 299
284 sprintf(path, "/proc/net/%s", tproto); 300 strcpy(path, "/proc/net/");
285 if (access(path, R_OK) != 0) { /* PORT/PROTO */ 301 if (sscanf(*pp, "%u/%4s", &port, path + sizeof("/proc/net/")-1) == 2
286 scan_proc_net(path, port); 302 && access(path, R_OK) == 0
287 } else { /* FILE */ 303 ) {
288 file: 304 /* PORT/PROTO */
289 xstat(*pp, &st); 305 scan_proc_net_or_maps(path, port);
290 add_inode(&st); 306 } else {
307 /* FILE */
308 struct stat statbuf;
309 xstat(*pp, &statbuf);
310 add_inode(&statbuf);
291 } 311 }
292 pp++; 312 pp++;
293 } 313 }
294 314
295 scan_proc_pids(); /* changes dir to "/proc" */ 315 if (scan_recursive("/proc")) {
296 316 if (!(option_mask32 & OPT_SILENT))
297 mypid = getpid(); 317 bb_putchar('\n');
298 plist = G.pid_list_head; 318 return G.kill_failed;
299 while (1) {
300 if (!plist)
301 return EXIT_FAILURE;
302 if (plist->pid != mypid)
303 break;
304 plist = plist->next;
305 }
306
307 exitcode = EXIT_SUCCESS;
308 do {
309 if (plist->pid != mypid) {
310 if (opt & OPT_KILL) {
311 if (kill(plist->pid, killsig) != 0) {
312 bb_perror_msg("kill pid %u", (unsigned)plist->pid);
313 exitcode = EXIT_FAILURE;
314 }
315 }
316 if (!(opt & OPT_SILENT)) {
317 printf("%u ", (unsigned)plist->pid);
318 }
319 }
320 plist = plist->next;
321 } while (plist);
322
323 if (!(opt & (OPT_SILENT))) {
324 bb_putchar('\n');
325 } 319 }
326 320
327 return exitcode; 321 return EXIT_FAILURE;
328} 322}
diff --git a/procps/iostat.c b/procps/iostat.c
index 06a33eb1d..cd233c72f 100644
--- a/procps/iostat.c
+++ b/procps/iostat.c
@@ -24,8 +24,6 @@
24#define debug(fmt, ...) ((void)0) 24#define debug(fmt, ...) ((void)0)
25 25
26#define MAX_DEVICE_NAME 12 26#define MAX_DEVICE_NAME 12
27#define CURRENT 0
28#define LAST 1
29 27
30#if 1 28#if 1
31typedef unsigned long long cputime_t; 29typedef unsigned long long cputime_t;
@@ -39,18 +37,33 @@ typedef long icputime_t;
39# define CPUTIME_MAX (~0UL) 37# define CPUTIME_MAX (~0UL)
40#endif 38#endif
41 39
42struct stats_cpu { 40enum {
43 cputime_t cpu_user; 41 STATS_CPU_USER,
44 cputime_t cpu_nice; 42 STATS_CPU_NICE,
45 cputime_t cpu_system; 43 STATS_CPU_SYSTEM,
46 cputime_t cpu_idle; 44 STATS_CPU_IDLE,
47 cputime_t cpu_iowait; 45 STATS_CPU_IOWAIT,
48 cputime_t cpu_steal; 46 STATS_CPU_IRQ,
49 cputime_t cpu_irq; 47 STATS_CPU_SOFTIRQ,
50 cputime_t cpu_softirq; 48 STATS_CPU_STEAL,
51 cputime_t cpu_guest; 49 STATS_CPU_GUEST,
50
51 GLOBAL_UPTIME,
52 SMP_UPTIME,
53
54 N_STATS_CPU,
52}; 55};
53 56
57typedef struct {
58 cputime_t vector[N_STATS_CPU];
59} stats_cpu_t;
60
61typedef struct {
62 stats_cpu_t *prev;
63 stats_cpu_t *curr;
64 cputime_t itv;
65} stats_cpu_pair_t;
66
54struct stats_dev { 67struct stats_dev {
55 char dname[MAX_DEVICE_NAME]; 68 char dname[MAX_DEVICE_NAME];
56 unsigned long long rd_sectors; 69 unsigned long long rd_sectors;
@@ -59,24 +72,24 @@ struct stats_dev {
59 unsigned long wr_ops; 72 unsigned long wr_ops;
60}; 73};
61 74
62/* List of devices entered on the command line */
63struct device_list {
64 char dname[MAX_DEVICE_NAME];
65};
66
67/* Globals. Sort by size and access frequency. */ 75/* Globals. Sort by size and access frequency. */
68struct globals { 76struct globals {
69 smallint show_all; 77 smallint show_all;
70 unsigned devlist_i; /* Index to the list of devices */
71 unsigned total_cpus; /* Number of CPUs */ 78 unsigned total_cpus; /* Number of CPUs */
72 unsigned clk_tck; /* Number of clock ticks per second */ 79 unsigned clk_tck; /* Number of clock ticks per second */
73 struct device_list *dlist; 80 llist_t *dev_list; /* List of devices entered on the command line */
74 struct stats_dev *saved_stats_dev; 81 struct stats_dev *saved_stats_dev;
75 struct tm tmtime; 82 struct tm tmtime;
83 struct {
84 const char *str;
85 unsigned div;
86 } unit;
76}; 87};
77#define G (*ptr_to_globals) 88#define G (*ptr_to_globals)
78#define INIT_G() do { \ 89#define INIT_G() do { \
79 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 90 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
91 G.unit.str = "Blk"; \
92 G.unit.div = 1; \
80} while (0) 93} while (0)
81 94
82/* Must match option string! */ 95/* Must match option string! */
@@ -104,12 +117,12 @@ static void print_header(void)
104 char buf[16]; 117 char buf[16];
105 struct utsname uts; 118 struct utsname uts;
106 119
107 if (uname(&uts) < 0) 120 uname(&uts); /* never fails */
108 bb_perror_msg_and_die("uname");
109 121
122 /* Date representation for the current locale */
110 strftime(buf, sizeof(buf), "%x", &G.tmtime); 123 strftime(buf, sizeof(buf), "%x", &G.tmtime);
111 124
112 printf("%s %s (%s) \t%s \t_%s_\t(%d CPU)\n\n", 125 printf("%s %s (%s) \t%s \t_%s_\t(%u CPU)\n\n",
113 uts.sysname, uts.release, uts.nodename, 126 uts.sysname, uts.release, uts.nodename,
114 buf, uts.machine, G.total_cpus); 127 buf, uts.machine, G.total_cpus);
115} 128}
@@ -124,61 +137,58 @@ static void get_localtime(struct tm *ptm)
124static void print_timestamp(void) 137static void print_timestamp(void)
125{ 138{
126 char buf[20]; 139 char buf[20];
140 /* %x: date representation for the current locale */
141 /* %X: time representation for the current locale */
127 strftime(buf, sizeof(buf), "%x %X", &G.tmtime); 142 strftime(buf, sizeof(buf), "%x %X", &G.tmtime);
128 printf("%s\n", buf); 143 printf("%s\n", buf);
129} 144}
130 145
131/* Fetch CPU statistics from /proc/stat */ 146static cputime_t get_smp_uptime(void)
132static void get_cpu_statistics(struct stats_cpu *sc)
133{ 147{
134 FILE *fp; 148 FILE *fp;
135 char buf[1024]; 149 unsigned long sec, dec;
136
137 fp = xfopen_for_read("/proc/stat");
138 150
139 memset(sc, 0, sizeof(*sc)); 151 fp = xfopen_for_read("/proc/uptime");
140 152
141 while (fgets(buf, sizeof(buf), fp)) { 153 if (fscanf(fp, "%lu.%lu", &sec, &dec) != 2)
142 /* Does the line starts with "cpu "? */ 154 bb_error_msg_and_die("can't read '%s'", "/proc/uptime");
143 if (starts_with_cpu(buf) && buf[3] == ' ') {
144 sscanf(buf + 4 + 1,
145 "%"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"
146 FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u",
147 &sc->cpu_user, &sc->cpu_nice, &sc->cpu_system,
148 &sc->cpu_idle, &sc->cpu_iowait, &sc->cpu_irq,
149 &sc->cpu_softirq, &sc->cpu_steal, &sc->cpu_guest);
150 }
151 }
152 155
153 fclose(fp); 156 fclose(fp);
157
158 return (cputime_t)sec * G.clk_tck + dec * G.clk_tck / 100;
154} 159}
155 160
156static cputime_t get_smp_uptime(void) 161/* Fetch CPU statistics from /proc/stat */
162static void get_cpu_statistics(stats_cpu_t *sc)
157{ 163{
158 FILE *fp; 164 FILE *fp;
159 char buf[sizeof(long)*3 * 2 + 4]; 165 char buf[1024], *ibuf = buf + 4;
160 unsigned long sec, dec;
161 166
162 fp = xfopen_for_read("/proc/uptime"); 167 fp = xfopen_for_read("/proc/stat");
163 168
164 if (fgets(buf, sizeof(buf), fp)) 169 memset(sc, 0, sizeof(*sc));
165 if (sscanf(buf, "%lu.%lu", &sec, &dec) != 2)
166 bb_error_msg_and_die("can't read /proc/uptime");
167 170
168 fclose(fp); 171 while (fgets(buf, sizeof(buf), fp)) {
172 /* Does the line starts with "cpu "? */
173 if (!starts_with_cpu(buf) || buf[3] != ' ') {
174 continue;
175 }
176 for (int i = STATS_CPU_USER; i <= STATS_CPU_GUEST; i++) {
177 ibuf = skip_whitespace(ibuf);
178 sscanf(ibuf, "%"FMT_DATA"u", &sc->vector[i]);
179 if (i != STATS_CPU_GUEST) {
180 sc->vector[GLOBAL_UPTIME] += sc->vector[i];
181 }
182 ibuf = skip_non_whitespace(ibuf);
183 }
184 break;
185 }
169 186
170 return (cputime_t)sec * G.clk_tck + dec * G.clk_tck / 100; 187 if (this_is_smp()) {
171} 188 sc->vector[SMP_UPTIME] = get_smp_uptime();
189 }
172 190
173/* 191 fclose(fp);
174 * Obtain current uptime in jiffies.
175 * Uptime is sum of individual CPUs' uptimes.
176 */
177static cputime_t get_uptime(const struct stats_cpu *sc)
178{
179 /* NB: Don't include cpu_guest, it is already in cpu_user */
180 return sc->cpu_user + sc->cpu_nice + sc->cpu_system + sc->cpu_idle +
181 + sc->cpu_iowait + sc->cpu_irq + sc->cpu_steal + sc->cpu_softirq;
182} 192}
183 193
184static ALWAYS_INLINE cputime_t get_interval(cputime_t old, cputime_t new) 194static ALWAYS_INLINE cputime_t get_interval(cputime_t old, cputime_t new)
@@ -219,65 +229,51 @@ static double percent_value(cputime_t prev, cputime_t curr, cputime_t itv)
219 return ((double)overflow_safe_sub(prev, curr)) / itv * 100; 229 return ((double)overflow_safe_sub(prev, curr)) / itv * 100;
220} 230}
221 231
222static void print_stats_cpu_struct(const struct stats_cpu *p, 232static void print_stats_cpu_struct(stats_cpu_pair_t *stats)
223 const struct stats_cpu *c, cputime_t itv)
224{ 233{
234 cputime_t *p = stats->prev->vector;
235 cputime_t *c = stats->curr->vector;
225 printf(" %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f\n", 236 printf(" %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f\n",
226 percent_value(p->cpu_user , c->cpu_user , itv), 237 percent_value(p[STATS_CPU_USER] , c[STATS_CPU_USER] , stats->itv),
227 percent_value(p->cpu_nice , c->cpu_nice , itv), 238 percent_value(p[STATS_CPU_NICE] , c[STATS_CPU_NICE] , stats->itv),
228 percent_value(p->cpu_system + p->cpu_softirq + p->cpu_irq, 239 percent_value(p[STATS_CPU_SYSTEM] + p[STATS_CPU_SOFTIRQ] + p[STATS_CPU_IRQ],
229 c->cpu_system + c->cpu_softirq + c->cpu_irq, itv), 240 c[STATS_CPU_SYSTEM] + c[STATS_CPU_SOFTIRQ] + c[STATS_CPU_IRQ], stats->itv),
230 percent_value(p->cpu_iowait , c->cpu_iowait , itv), 241 percent_value(p[STATS_CPU_IOWAIT], c[STATS_CPU_IOWAIT], stats->itv),
231 percent_value(p->cpu_steal , c->cpu_steal , itv), 242 percent_value(p[STATS_CPU_STEAL] , c[STATS_CPU_STEAL] , stats->itv),
232 percent_value(p->cpu_idle , c->cpu_idle , itv) 243 percent_value(p[STATS_CPU_IDLE] , c[STATS_CPU_IDLE] , stats->itv)
233 ); 244 );
234} 245}
235 246
236static void print_stats_dev_struct(const struct stats_dev *p, 247static void print_stats_dev_struct(const struct stats_dev *p,
237 const struct stats_dev *c, cputime_t itv) 248 const struct stats_dev *c, cputime_t itv)
238{ 249{
239 int unit = 1;
240
241 if (option_mask32 & OPT_k)
242 unit = 2;
243 else if (option_mask32 & OPT_m)
244 unit = 2048;
245
246 if (option_mask32 & OPT_z) 250 if (option_mask32 & OPT_z)
247 if (p->rd_ops == c->rd_ops && p->wr_ops == c->wr_ops) 251 if (p->rd_ops == c->rd_ops && p->wr_ops == c->wr_ops)
248 return; 252 return;
249 253
250 printf("%-13s", c->dname); 254 printf("%-13s %8.2f %12.2f %12.2f %10llu %10llu \n", c->dname,
251 printf(" %8.2f %12.2f %12.2f %10llu %10llu \n",
252 percent_value(p->rd_ops + p->wr_ops , 255 percent_value(p->rd_ops + p->wr_ops ,
253 /**/ c->rd_ops + c->wr_ops , itv), 256 /**/ c->rd_ops + c->wr_ops , itv),
254 percent_value(p->rd_sectors, c->rd_sectors, itv) / unit, 257 percent_value(p->rd_sectors, c->rd_sectors, itv) / G.unit.div,
255 percent_value(p->wr_sectors, c->wr_sectors, itv) / unit, 258 percent_value(p->wr_sectors, c->wr_sectors, itv) / G.unit.div,
256 (c->rd_sectors - p->rd_sectors) / unit, 259 (c->rd_sectors - p->rd_sectors) / G.unit.div,
257 (c->wr_sectors - p->wr_sectors) / unit); 260 (c->wr_sectors - p->wr_sectors) / G.unit.div);
258} 261}
259 262
260static void cpu_report(const struct stats_cpu *last, 263static void cpu_report(stats_cpu_pair_t *stats)
261 const struct stats_cpu *cur,
262 cputime_t itv)
263{ 264{
264 /* Always print a header */ 265 /* Always print a header */
265 puts("avg-cpu: %user %nice %system %iowait %steal %idle"); 266 puts("avg-cpu: %user %nice %system %iowait %steal %idle");
266 267
267 /* Print current statistics */ 268 /* Print current statistics */
268 print_stats_cpu_struct(last, cur, itv); 269 print_stats_cpu_struct(stats);
269} 270}
270 271
271static void print_devstat_header(void) 272static void print_devstat_header(void)
272{ 273{
273 printf("Device: tps"); 274 printf("Device:%15s%6s%s/s%6s%s/s%6s%s%6s%s\n", "tps",
274 275 G.unit.str, "_read", G.unit.str, "_wrtn",
275 if (option_mask32 & OPT_m) 276 G.unit.str, "_read", G.unit.str, "_wrtn");
276 puts(" MB_read/s MB_wrtn/s MB_read MB_wrtn");
277 else if (option_mask32 & OPT_k)
278 puts(" kB_read/s kB_wrtn/s kB_read kB_wrtn");
279 else
280 puts(" Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn");
281} 277}
282 278
283/* 279/*
@@ -289,37 +285,6 @@ static int is_partition(const char *dev)
289 return ((dev[0] - 's') | (dev[1] - 'd') | (dev[2] - 'a')) == 0 && isdigit(dev[3]); 285 return ((dev[0] - 's') | (dev[1] - 'd') | (dev[2] - 'a')) == 0 && isdigit(dev[3]);
290} 286}
291 287
292/*
293 * Return number of numbers on cmdline.
294 * Reasonable values are only 0 (no interval/count specified),
295 * 1 (interval specified) and 2 (both interval and count specified)
296 */
297static int numbers_on_cmdline(int argc, char *argv[])
298{
299 int sum = 0;
300
301 if (isdigit(argv[argc-1][0]))
302 sum++;
303 if (argc > 2 && isdigit(argv[argc-2][0]))
304 sum++;
305
306 return sum;
307}
308
309static int is_dev_in_dlist(const char *dev)
310{
311 int i;
312
313 /* Go through the device list */
314 for (i = 0; i < G.devlist_i; i++)
315 if (strcmp(G.dlist[i].dname, dev) == 0)
316 /* Found a match */
317 return 1;
318
319 /* No match found */
320 return 0;
321}
322
323static void do_disk_statistics(cputime_t itv) 288static void do_disk_statistics(cputime_t itv)
324{ 289{
325 FILE *fp; 290 FILE *fp;
@@ -356,7 +321,7 @@ static void do_disk_statistics(cputime_t itv)
356 break; 321 break;
357 } 322 }
358 323
359 if (!G.devlist_i && !is_partition(sd.dname)) { 324 if (!G.dev_list && !is_partition(sd.dname)) {
360 /* User didn't specify device */ 325 /* User didn't specify device */
361 if (!G.show_all && !sd.rd_ops && !sd.wr_ops) { 326 if (!G.show_all && !sd.rd_ops && !sd.wr_ops) {
362 /* Don't print unused device */ 327 /* Don't print unused device */
@@ -367,7 +332,7 @@ static void do_disk_statistics(cputime_t itv)
367 i++; 332 i++;
368 } else { 333 } else {
369 /* Is device in device list? */ 334 /* Is device in device list? */
370 if (is_dev_in_dlist(sd.dname)) { 335 if (llist_find_str(G.dev_list, sd.dname)) {
371 /* Print current statistics */ 336 /* Print current statistics */
372 print_stats_dev_struct(&G.saved_stats_dev[i], &sd, itv); 337 print_stats_dev_struct(&G.saved_stats_dev[i], &sd, itv);
373 G.saved_stats_dev[i] = sd; 338 G.saved_stats_dev[i] = sd;
@@ -389,28 +354,6 @@ static void dev_report(cputime_t itv)
389 do_disk_statistics(itv); 354 do_disk_statistics(itv);
390} 355}
391 356
392static void save_to_devlist(const char *dname)
393{
394 int i;
395 struct device_list *tmp = G.dlist;
396
397 if (strncmp(dname, "/dev/", 5) == 0)
398 /* We'll ignore prefix '/dev/' */
399 dname += 5;
400
401 /* Go through the list */
402 for (i = 0; i < G.devlist_i; i++, tmp++)
403 if (strcmp(tmp->dname, dname) == 0)
404 /* Already in the list */
405 return;
406
407 /* Add device name to the list */
408 strncpy(tmp->dname, dname, MAX_DEVICE_NAME - 1);
409
410 /* Update device list index */
411 G.devlist_i++;
412}
413
414static unsigned get_number_of_devices(void) 357static unsigned get_number_of_devices(void)
415{ 358{
416 FILE *fp; 359 FILE *fp;
@@ -440,18 +383,6 @@ static unsigned get_number_of_devices(void)
440 return n; 383 return n;
441} 384}
442 385
443static int number_of_ALL_on_cmdline(char **argv)
444{
445 int alls = 0;
446
447 /* Iterate over cmd line arguments, count "ALL" */
448 while (*argv)
449 if (strcmp(*argv++, "ALL") == 0)
450 alls++;
451
452 return alls;
453}
454
455//usage:#define iostat_trivial_usage 386//usage:#define iostat_trivial_usage
456//usage: "[-c] [-d] [-t] [-z] [-k|-m] [ALL|BLOCKDEV...] [INTERVAL [COUNT]]" 387//usage: "[-c] [-d] [-t] [-z] [-k|-m] [ALL|BLOCKDEV...] [INTERVAL [COUNT]]"
457//usage:#define iostat_full_usage "\n\n" 388//usage:#define iostat_full_usage "\n\n"
@@ -465,19 +396,17 @@ static int number_of_ALL_on_cmdline(char **argv)
465//usage: "\n -m Use Mb/s" 396//usage: "\n -m Use Mb/s"
466 397
467int iostat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 398int iostat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
468int iostat_main(int argc, char **argv) 399int iostat_main(int argc UNUSED_PARAM, char **argv)
469{ 400{
470 int opt, dev_num; 401 int opt, dev_num;
471 unsigned interval = 0; 402 unsigned interval;
472 int count; 403 int count;
473 cputime_t global_uptime[2] = { 0 }; 404 stats_cpu_t stats_data[2];
474 cputime_t smp_uptime[2] = { 0 }; 405 smallint current_stats;
475 cputime_t itv;
476 struct stats_cpu stats_cur, stats_last;
477 406
478 INIT_G(); 407 INIT_G();
479 408
480 memset(&stats_last, 0, sizeof(stats_last)); 409 memset(&stats_data, 0, sizeof(stats_data));
481 410
482 /* Get number of clock ticks per sec */ 411 /* Get number of clock ticks per sec */
483 G.clk_tck = get_user_hz(); 412 G.clk_tck = get_user_hz();
@@ -496,25 +425,24 @@ int iostat_main(int argc, char **argv)
496 opt |= OPT_c + OPT_d; 425 opt |= OPT_c + OPT_d;
497 426
498 argv += optind; 427 argv += optind;
499 argc -= optind;
500
501 dev_num = argc - numbers_on_cmdline(argc, argv);
502 /* We don't want to allocate space for 'ALL' */
503 dev_num -= number_of_ALL_on_cmdline(argv);
504 if (dev_num > 0)
505 /* Make space for device list */
506 G.dlist = xzalloc(sizeof(G.dlist[0]) * dev_num);
507 428
508 /* Store device names into device list */ 429 /* Store device names into device list */
430 dev_num = 0;
509 while (*argv && !isdigit(*argv[0])) { 431 while (*argv && !isdigit(*argv[0])) {
510 if (strcmp(*argv, "ALL") != 0) 432 if (strcmp(*argv, "ALL") != 0) {
511 /* If not ALL, save device name */ 433 /* If not ALL, save device name */
512 save_to_devlist(*argv); 434 char *dev_name = skip_dev_pfx(*argv);
513 else 435 if (!llist_find_str(G.dev_list, dev_name)) {
436 llist_add_to(&G.dev_list, dev_name);
437 dev_num++;
438 }
439 } else {
514 G.show_all = 1; 440 G.show_all = 1;
441 }
515 argv++; 442 argv++;
516 } 443 }
517 444
445 interval = 0;
518 count = 1; 446 count = 1;
519 if (*argv) { 447 if (*argv) {
520 /* Get interval */ 448 /* Get interval */
@@ -533,28 +461,46 @@ int iostat_main(int argc, char **argv)
533 ); 461 );
534 } 462 }
535 463
464 if (opt & OPT_m) {
465 G.unit.str = " MB";
466 G.unit.div = 2048;
467 }
468
469 if (opt & OPT_k) {
470 G.unit.str = " kB";
471 G.unit.div = 2;
472 }
473
474 get_localtime(&G.tmtime);
475
536 /* Display header */ 476 /* Display header */
537 print_header(); 477 print_header();
538 478
479 current_stats = 0;
539 /* Main loop */ 480 /* Main loop */
540 for (;;) { 481 for (;;) {
482 stats_cpu_pair_t stats;
483
484 stats.prev = &stats_data[current_stats ^ 1];
485 stats.curr = &stats_data[current_stats];
486
541 /* Fill the time structure */ 487 /* Fill the time structure */
542 get_localtime(&G.tmtime); 488 get_localtime(&G.tmtime);
543 489
544 /* Fetch current CPU statistics */ 490 /* Fetch current CPU statistics */
545 get_cpu_statistics(&stats_cur); 491 get_cpu_statistics(stats.curr);
546
547 /* Fetch current uptime */
548 global_uptime[CURRENT] = get_uptime(&stats_cur);
549 492
550 /* Get interval */ 493 /* Get interval */
551 itv = get_interval(global_uptime[LAST], global_uptime[CURRENT]); 494 stats.itv = get_interval(
495 stats.prev->vector[GLOBAL_UPTIME],
496 stats.curr->vector[GLOBAL_UPTIME]
497 );
552 498
553 if (opt & OPT_t) 499 if (opt & OPT_t)
554 print_timestamp(); 500 print_timestamp();
555 501
556 if (opt & OPT_c) { 502 if (opt & OPT_c) {
557 cpu_report(&stats_last, &stats_cur, itv); 503 cpu_report(&stats);
558 if (opt & OPT_d) 504 if (opt & OPT_d)
559 /* Separate outputs by a newline */ 505 /* Separate outputs by a newline */
560 bb_putchar('\n'); 506 bb_putchar('\n');
@@ -562,32 +508,31 @@ int iostat_main(int argc, char **argv)
562 508
563 if (opt & OPT_d) { 509 if (opt & OPT_d) {
564 if (this_is_smp()) { 510 if (this_is_smp()) {
565 smp_uptime[CURRENT] = get_smp_uptime(); 511 stats.itv = get_interval(
566 itv = get_interval(smp_uptime[LAST], smp_uptime[CURRENT]); 512 stats.prev->vector[SMP_UPTIME],
567 smp_uptime[LAST] = smp_uptime[CURRENT]; 513 stats.curr->vector[SMP_UPTIME]
514 );
568 } 515 }
569 dev_report(itv); 516 dev_report(stats.itv);
570 } 517 }
571 518
519 bb_putchar('\n');
520
572 if (count > 0) { 521 if (count > 0) {
573 if (--count == 0) 522 if (--count == 0)
574 break; 523 break;
575 } 524 }
576 525
577 /* Backup current stats */ 526 /* Swap stats */
578 global_uptime[LAST] = global_uptime[CURRENT]; 527 current_stats ^= 1;
579 stats_last = stats_cur;
580 528
581 bb_putchar('\n');
582 sleep(interval); 529 sleep(interval);
583 } 530 }
584 531
585 bb_putchar('\n');
586
587 if (ENABLE_FEATURE_CLEAN_UP) { 532 if (ENABLE_FEATURE_CLEAN_UP) {
588 free(&G); 533 llist_free(G.dev_list, NULL);
589 free(G.dlist);
590 free(G.saved_stats_dev); 534 free(G.saved_stats_dev);
535 free(&G);
591 } 536 }
592 537
593 return EXIT_SUCCESS; 538 return EXIT_SUCCESS;
diff --git a/procps/mpstat.c b/procps/mpstat.c
index d643c999f..da8f34dab 100644
--- a/procps/mpstat.c
+++ b/procps/mpstat.c
@@ -36,11 +36,10 @@
36 * We are printing headers in the " IRQNAME/s" form, experimentally 36 * We are printing headers in the " IRQNAME/s" form, experimentally
37 * anything smaller than 10 chars looks ugly for /proc/softirqs stats. 37 * anything smaller than 10 chars looks ugly for /proc/softirqs stats.
38 */ 38 */
39#define INTRATE_SCRWIDTH 10 39#define INTRATE_SCRWIDTH 10
40#define INTRATE_SCRWIDTH_STR "10" 40#define INTRATE_SCRWIDTH_STR "10"
41 41
42/* System files */ 42/* System files */
43#define SYSFS_DEVCPU "/sys/devices/system/cpu"
44#define PROCFS_STAT "/proc/stat" 43#define PROCFS_STAT "/proc/stat"
45#define PROCFS_INTERRUPTS "/proc/interrupts" 44#define PROCFS_INTERRUPTS "/proc/interrupts"
46#define PROCFS_SOFTIRQS "/proc/softirqs" 45#define PROCFS_SOFTIRQS "/proc/softirqs"
diff --git a/procps/nmeter.c b/procps/nmeter.c
index 48b56a399..999955982 100644
--- a/procps/nmeter.c
+++ b/procps/nmeter.c
@@ -24,20 +24,20 @@
24//usage: "\n -d MSEC Milliseconds between updates (default:1000)" 24//usage: "\n -d MSEC Milliseconds between updates (default:1000)"
25//usage: "\n" 25//usage: "\n"
26//usage: "\nFormat specifiers:" 26//usage: "\nFormat specifiers:"
27//usage: "\n %Nc or %[cN] Monitor CPU. N - bar size (default:10)" 27//usage: "\n %Nc or %[cN] CPU. N - bar size (default:10)"
28//usage: "\n (displays: S:system U:user N:niced D:iowait I:irq i:softirq)" 28//usage: "\n (displays: S:system U:user N:niced D:iowait I:irq i:softirq)"
29//usage: "\n %[niface] Monitor network interface 'iface'" 29//usage: "\n %[nINTERFACE] Network INTERFACE"
30//usage: "\n %m Monitor allocated memory" 30//usage: "\n %m Allocated memory"
31//usage: "\n %[mf] Monitor free memory" 31//usage: "\n %[mf] Free memory"
32//usage: "\n %[mt] Monitor total memory" 32//usage: "\n %[mt] Total memory"
33//usage: "\n %s Monitor allocated swap" 33//usage: "\n %s Allocated swap"
34//usage: "\n %f Monitor number of used file descriptors" 34//usage: "\n %f Number of used file descriptors"
35//usage: "\n %Ni Monitor total/specific IRQ rate" 35//usage: "\n %Ni Total/specific IRQ rate"
36//usage: "\n %x Monitor context switch rate" 36//usage: "\n %x Context switch rate"
37//usage: "\n %p Monitor forks" 37//usage: "\n %p Forks"
38//usage: "\n %[pn] Monitor # of processes" 38//usage: "\n %[pn] # of processes"
39//usage: "\n %b Monitor block io" 39//usage: "\n %b Block io"
40//usage: "\n %Nt Show time (with N decimal points)" 40//usage: "\n %Nt Time (with N decimal points)"
41//usage: "\n %r Print <cr> instead of <lf> at EOL" 41//usage: "\n %r Print <cr> instead of <lf> at EOL"
42 42
43//TODO: 43//TODO:
diff --git a/procps/powertop.c b/procps/powertop.c
index bfe5a9568..008cdfca4 100644
--- a/procps/powertop.c
+++ b/procps/powertop.c
@@ -393,11 +393,9 @@ static NOINLINE int process_timer_stats(void)
393 char buf[128]; 393 char buf[128];
394 char line[15 + 3 + 128]; 394 char line[15 + 3 + 128];
395 int n; 395 int n;
396 ullong totalticks;
397 FILE *fp; 396 FILE *fp;
398 397
399 buf[0] = '\0'; 398 buf[0] = '\0';
400 totalticks = 0;
401 399
402 n = 0; 400 n = 0;
403 fp = NULL; 401 fp = NULL;
diff --git a/procps/top.c b/procps/top.c
index ee6555188..011bbf183 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -92,9 +92,9 @@ enum { SORT_DEPTH = 3 };
92struct globals { 92struct globals {
93 top_status_t *top; 93 top_status_t *top;
94 int ntop; 94 int ntop;
95 smallint inverted;
95#if ENABLE_FEATURE_TOPMEM 96#if ENABLE_FEATURE_TOPMEM
96 smallint sort_field; 97 smallint sort_field;
97 smallint inverted;
98#endif 98#endif
99#if ENABLE_FEATURE_TOP_SMP_CPU 99#if ENABLE_FEATURE_TOP_SMP_CPU
100 smallint smp_cpu_info; /* one/many cpu info lines? */ 100 smallint smp_cpu_info; /* one/many cpu info lines? */
@@ -194,9 +194,9 @@ static int mult_lvl_cmp(void* a, void* b)
194 for (i = 0; i < SORT_DEPTH; i++) { 194 for (i = 0; i < SORT_DEPTH; i++) {
195 cmp_val = (*sort_function[i])(a, b); 195 cmp_val = (*sort_function[i])(a, b);
196 if (cmp_val != 0) 196 if (cmp_val != 0)
197 return cmp_val; 197 break;
198 } 198 }
199 return 0; 199 return inverted ? -cmp_val : cmp_val;
200} 200}
201 201
202static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif) 202static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif)
@@ -797,7 +797,7 @@ static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width)
797 797
798 display_topmem_header(scr_width, &lines_rem); 798 display_topmem_header(scr_width, &lines_rem);
799 strcpy(line_buf, HDR_STR " COMMAND"); 799 strcpy(line_buf, HDR_STR " COMMAND");
800 line_buf[5 + sort_field * 6] = '*'; 800 line_buf[11 + sort_field * 6] = "^_"[inverted];
801 printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf); 801 printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf);
802 lines_rem--; 802 lines_rem--;
803 803
@@ -850,8 +850,114 @@ enum {
850 | PSSCAN_PID 850 | PSSCAN_PID
851 | PSSCAN_SMAPS 851 | PSSCAN_SMAPS
852 | PSSCAN_COMM, 852 | PSSCAN_COMM,
853 EXIT_MASK = (unsigned)-1,
853}; 854};
854 855
856#if ENABLE_FEATURE_USE_TERMIOS
857static unsigned handle_input(unsigned scan_mask, unsigned interval)
858{
859 unsigned char c;
860 struct pollfd pfd[1];
861
862 pfd[0].fd = 0;
863 pfd[0].events = POLLIN;
864
865 while (1) {
866 if (safe_poll(pfd, 1, interval * 1000) <= 0)
867 return scan_mask;
868 interval = 0;
869
870 if (safe_read(STDIN_FILENO, &c, 1) != 1) { /* error/EOF? */
871 option_mask32 |= OPT_EOF;
872 return scan_mask;
873 }
874
875 if (c == initial_settings.c_cc[VINTR])
876 return EXIT_MASK;
877 if (c == initial_settings.c_cc[VEOF])
878 return EXIT_MASK;
879 c |= 0x20; /* lowercase */
880 if (c == 'q')
881 return EXIT_MASK;
882
883 if (c == 'n') {
884 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
885 sort_function[0] = pid_sort;
886 continue;
887 }
888 if (c == 'm') {
889 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
890 sort_function[0] = mem_sort;
891# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
892 sort_function[1] = pcpu_sort;
893 sort_function[2] = time_sort;
894# endif
895 continue;
896 }
897# if ENABLE_FEATURE_SHOW_THREADS
898 if (c == 'h'
899 IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK)
900 ) {
901 scan_mask ^= PSSCAN_TASKS;
902 continue;
903 }
904# endif
905# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
906 if (c == 'p') {
907 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
908 sort_function[0] = pcpu_sort;
909 sort_function[1] = mem_sort;
910 sort_function[2] = time_sort;
911 continue;
912 }
913 if (c == 't') {
914 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
915 sort_function[0] = time_sort;
916 sort_function[1] = mem_sort;
917 sort_function[2] = pcpu_sort;
918 continue;
919 }
920# if ENABLE_FEATURE_TOPMEM
921 if (c == 's') {
922 scan_mask = TOPMEM_MASK;
923 free(prev_hist);
924 prev_hist = NULL;
925 prev_hist_count = 0;
926 sort_field = (sort_field + 1) % NUM_SORT_FIELD;
927 continue;
928 }
929# endif
930 if (c == 'r') {
931 inverted ^= 1;
932 continue;
933 }
934# if ENABLE_FEATURE_TOP_SMP_CPU
935 /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
936 if (c == 'c' || c == '1') {
937 /* User wants to toggle per cpu <> aggregate */
938 if (smp_cpu_info) {
939 free(cpu_prev_jif);
940 free(cpu_jif);
941 cpu_jif = &cur_jif;
942 cpu_prev_jif = &prev_jif;
943 } else {
944 /* Prepare for xrealloc() */
945 cpu_jif = cpu_prev_jif = NULL;
946 }
947 num_cpus = 0;
948 smp_cpu_info = !smp_cpu_info;
949 get_jiffy_counts();
950 continue;
951 }
952# endif
953# endif
954 break; /* unknown key -> force refresh */
955 }
956
957 return scan_mask;
958}
959#endif
960
855//usage:#if ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_TOP_SMP_CPU 961//usage:#if ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_TOP_SMP_CPU
856//usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...) __VA_ARGS__ 962//usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...) __VA_ARGS__
857//usage:#else 963//usage:#else
@@ -871,8 +977,9 @@ enum {
871//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/cpu") 977//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/cpu")
872//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/time") 978//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/time")
873//usage: IF_FEATURE_TOPMEM( 979//usage: IF_FEATURE_TOPMEM(
874//usage: "\n"" S: show memory, R: reverse memory sort" 980//usage: "\n"" S: show memory"
875//usage: ) 981//usage: )
982//usage: "\n"" R: reverse sort"
876//usage: IF_SHOW_THREADS_OR_TOP_SMP( 983//usage: IF_SHOW_THREADS_OR_TOP_SMP(
877//usage: "\n"" " 984//usage: "\n"" "
878//usage: IF_FEATURE_SHOW_THREADS("H: toggle threads") 985//usage: IF_FEATURE_SHOW_THREADS("H: toggle threads")
@@ -880,23 +987,36 @@ enum {
880//usage: IF_FEATURE_TOP_SMP_CPU("1: toggle SMP") 987//usage: IF_FEATURE_TOP_SMP_CPU("1: toggle SMP")
881//usage: ) 988//usage: )
882//usage: "\n"" Q,^C: exit" 989//usage: "\n"" Q,^C: exit"
990//usage: "\n"
991//usage: "\n""Options:"
992//usage: "\n"" -b Batch mode"
993//usage: "\n"" -n N Exit after N iterations"
994//usage: "\n"" -d N Delay between updates"
995//usage: IF_FEATURE_TOPMEM(
996//usage: "\n"" -m Same as 's' key"
997//usage: )
998
999/* Interactive testing:
1000 * echo sss | ./busybox top
1001 * - shows memory screen
1002 * echo sss | ./busybox top -bn1 >mem
1003 * - saves memory screen - the *whole* list, not first NROWS processes!
1004 * echo .m.s.s.s.s.s.s.q | ./busybox top -b >z
1005 * - saves several different screens, and exits
1006 *
1007 * TODO: -i STRING param as a better alternative?
1008 */
883 1009
884int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1010int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
885int top_main(int argc UNUSED_PARAM, char **argv) 1011int top_main(int argc UNUSED_PARAM, char **argv)
886{ 1012{
887 int iterations; 1013 int iterations;
888 unsigned lines, col; 1014 unsigned lines, col;
889 int lines_rem;
890 unsigned interval; 1015 unsigned interval;
891 char *str_interval, *str_iterations; 1016 char *str_interval, *str_iterations;
892 unsigned scan_mask = TOP_MASK; 1017 unsigned scan_mask = TOP_MASK;
893#if ENABLE_FEATURE_USE_TERMIOS 1018#if ENABLE_FEATURE_USE_TERMIOS
894 struct termios new_settings; 1019 struct termios new_settings;
895 struct pollfd pfd[1];
896 unsigned char c;
897
898 pfd[0].fd = 0;
899 pfd[0].events = POLLIN;
900#endif 1020#endif
901 1021
902 INIT_G(); 1022 INIT_G();
@@ -933,15 +1053,6 @@ int top_main(int argc UNUSED_PARAM, char **argv)
933 1053
934 /* change to /proc */ 1054 /* change to /proc */
935 xchdir("/proc"); 1055 xchdir("/proc");
936#if ENABLE_FEATURE_USE_TERMIOS
937 tcgetattr(0, (void *) &initial_settings);
938 memcpy(&new_settings, &initial_settings, sizeof(new_settings));
939 /* unbuffered input, turn off echo */
940 new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
941
942 bb_signals(BB_FATAL_SIGS, sig_catcher);
943 tcsetattr_stdin_TCSANOW(&new_settings);
944#endif
945 1056
946#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 1057#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
947 sort_function[0] = pcpu_sort; 1058 sort_function[0] = pcpu_sort;
@@ -951,21 +1062,41 @@ int top_main(int argc UNUSED_PARAM, char **argv)
951 sort_function[0] = mem_sort; 1062 sort_function[0] = mem_sort;
952#endif 1063#endif
953 1064
954 while (1) { 1065#if ENABLE_FEATURE_USE_TERMIOS
1066 tcgetattr(0, (void *) &initial_settings);
1067 memcpy(&new_settings, &initial_settings, sizeof(new_settings));
1068 if (!OPT_BATCH_MODE) {
1069 /* unbuffered input, turn off echo */
1070 new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
1071 tcsetattr_stdin_TCSANOW(&new_settings);
1072 }
1073
1074 bb_signals(BB_FATAL_SIGS, sig_catcher);
1075
1076 /* Eat initial input, if any */
1077 scan_mask = handle_input(scan_mask, 0);
1078#endif
1079
1080 while (scan_mask != EXIT_MASK) {
955 procps_status_t *p = NULL; 1081 procps_status_t *p = NULL;
956 1082
957 lines = 24; /* default */ 1083 if (OPT_BATCH_MODE) {
958 col = 79; 1084 lines = INT_MAX;
1085 col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */
1086 } else {
1087 lines = 24; /* default */
1088 col = 79;
959#if ENABLE_FEATURE_USE_TERMIOS 1089#if ENABLE_FEATURE_USE_TERMIOS
960 /* We output to stdout, we need size of stdout (not stdin)! */ 1090 /* We output to stdout, we need size of stdout (not stdin)! */
961 get_terminal_width_height(STDOUT_FILENO, &col, &lines); 1091 get_terminal_width_height(STDOUT_FILENO, &col, &lines);
962 if (lines < 5 || col < 10) { 1092 if (lines < 5 || col < 10) {
963 sleep(interval); 1093 sleep(interval);
964 continue; 1094 continue;
965 } 1095 }
966#endif 1096#endif
967 if (col > LINE_BUF_SIZE-2) /* +2 bytes for '\n', NUL, */ 1097 if (col > LINE_BUF_SIZE - 2)
968 col = LINE_BUF_SIZE-2; 1098 col = LINE_BUF_SIZE - 2;
1099 }
969 1100
970 /* read process IDs & status for all the processes */ 1101 /* read process IDs & status for all the processes */
971 while ((p = procps_scan(p, scan_mask)) != NULL) { 1102 while ((p = procps_scan(p, scan_mask)) != NULL) {
@@ -1033,15 +1164,11 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1033 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); 1164 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
1034 } 1165 }
1035#endif 1166#endif
1036 lines_rem = lines;
1037 if (OPT_BATCH_MODE) {
1038 lines_rem = INT_MAX;
1039 }
1040 if (scan_mask != TOPMEM_MASK) 1167 if (scan_mask != TOPMEM_MASK)
1041 display_process_list(lines_rem, col); 1168 display_process_list(lines, col);
1042#if ENABLE_FEATURE_TOPMEM 1169#if ENABLE_FEATURE_TOPMEM
1043 else 1170 else
1044 display_topmem_process_list(lines_rem, col); 1171 display_topmem_process_list(lines, col);
1045#endif 1172#endif
1046 clearmems(); 1173 clearmems();
1047 if (iterations >= 0 && !--iterations) 1174 if (iterations >= 0 && !--iterations)
@@ -1049,84 +1176,13 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1049#if !ENABLE_FEATURE_USE_TERMIOS 1176#if !ENABLE_FEATURE_USE_TERMIOS
1050 sleep(interval); 1177 sleep(interval);
1051#else 1178#else
1052 if (option_mask32 & (OPT_b|OPT_EOF)) 1179 if (option_mask32 & OPT_EOF)
1053 /* batch mode, or EOF on stdin ("top </dev/null") */ 1180 /* EOF on stdin ("top </dev/null") */
1054 sleep(interval); 1181 sleep(interval);
1055 else if (safe_poll(pfd, 1, interval * 1000) > 0) { 1182 else
1056 if (safe_read(STDIN_FILENO, &c, 1) != 1) { /* error/EOF? */ 1183 scan_mask = handle_input(scan_mask, interval);
1057 option_mask32 |= OPT_EOF;
1058 continue;
1059 }
1060 if (c == initial_settings.c_cc[VINTR])
1061 break;
1062 c |= 0x20; /* lowercase */
1063 if (c == 'q')
1064 break;
1065 if (c == 'n') {
1066 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1067 sort_function[0] = pid_sort;
1068 }
1069 if (c == 'm') {
1070 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1071 sort_function[0] = mem_sort;
1072# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1073 sort_function[1] = pcpu_sort;
1074 sort_function[2] = time_sort;
1075# endif
1076 }
1077# if ENABLE_FEATURE_SHOW_THREADS
1078 if (c == 'h'
1079 IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK)
1080 ) {
1081 scan_mask ^= PSSCAN_TASKS;
1082 }
1083# endif
1084# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1085 if (c == 'p') {
1086 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1087 sort_function[0] = pcpu_sort;
1088 sort_function[1] = mem_sort;
1089 sort_function[2] = time_sort;
1090 }
1091 if (c == 't') {
1092 IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1093 sort_function[0] = time_sort;
1094 sort_function[1] = mem_sort;
1095 sort_function[2] = pcpu_sort;
1096 }
1097# if ENABLE_FEATURE_TOPMEM
1098 if (c == 's') {
1099 scan_mask = TOPMEM_MASK;
1100 free(prev_hist);
1101 prev_hist = NULL;
1102 prev_hist_count = 0;
1103 sort_field = (sort_field + 1) % NUM_SORT_FIELD;
1104 }
1105 if (c == 'r')
1106 inverted ^= 1;
1107# endif
1108# if ENABLE_FEATURE_TOP_SMP_CPU
1109 /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
1110 if (c == 'c' || c == '1') {
1111 /* User wants to toggle per cpu <> aggregate */
1112 if (smp_cpu_info) {
1113 free(cpu_prev_jif);
1114 free(cpu_jif);
1115 cpu_jif = &cur_jif;
1116 cpu_prev_jif = &prev_jif;
1117 } else {
1118 /* Prepare for xrealloc() */
1119 cpu_jif = cpu_prev_jif = NULL;
1120 }
1121 num_cpus = 0;
1122 smp_cpu_info = !smp_cpu_info;
1123 get_jiffy_counts();
1124 }
1125# endif
1126# endif
1127 }
1128#endif /* FEATURE_USE_TERMIOS */ 1184#endif /* FEATURE_USE_TERMIOS */
1129 } /* end of "while (1)" */ 1185 } /* end of "while (not Q)" */
1130 1186
1131 bb_putchar('\n'); 1187 bb_putchar('\n');
1132#if ENABLE_FEATURE_USE_TERMIOS 1188#if ENABLE_FEATURE_USE_TERMIOS
diff --git a/runit/sv.c b/runit/sv.c
index 322688a36..5b01c875c 100644
--- a/runit/sv.c
+++ b/runit/sv.c
@@ -437,7 +437,6 @@ static int control(const char *a)
437int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 437int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
438int sv_main(int argc UNUSED_PARAM, char **argv) 438int sv_main(int argc UNUSED_PARAM, char **argv)
439{ 439{
440 unsigned opt;
441 char *x; 440 char *x;
442 char *action; 441 char *action;
443 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR; 442 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
@@ -458,7 +457,7 @@ int sv_main(int argc UNUSED_PARAM, char **argv)
458 if (x) waitsec = xatou(x); 457 if (x) waitsec = xatou(x);
459 458
460 opt_complementary = "w+:vv"; /* -w N, -v is a counter */ 459 opt_complementary = "w+:vv"; /* -w N, -v is a counter */
461 opt = getopt32(argv, "w:v", &waitsec, &verbose); 460 getopt32(argv, "w:v", &waitsec, &verbose);
462 argv += optind; 461 argv += optind;
463 action = *argv++; 462 action = *argv++;
464 if (!action || !*argv) bb_show_usage(); 463 if (!action || !*argv) bb_show_usage();
diff --git a/scripts/defconfig.tig b/scripts/defconfig.tig
index ab9dc4728..e19217f08 100644
--- a/scripts/defconfig.tig
+++ b/scripts/defconfig.tig
@@ -1,7 +1,7 @@
1# 1#
2# Automatically generated make config: don't edit 2# Automatically generated make config: don't edit
3# Busybox version: 1.19.0.git 3# Busybox version: 1.19.0.git
4# Thu Mar 22 14:00:24 2012 4# Thu Mar 22 15:00:34 2012
5# 5#
6CONFIG_HAVE_DOT_CONFIG=y 6CONFIG_HAVE_DOT_CONFIG=y
7# CONFIG_PLATFORM_POSIX is not set 7# CONFIG_PLATFORM_POSIX is not set
@@ -615,11 +615,13 @@ CONFIG_HD=y
615# CONFIG_CONSPY is not set 615# CONFIG_CONSPY is not set
616# CONFIG_NANDWRITE is not set 616# CONFIG_NANDWRITE is not set
617# CONFIG_NANDDUMP is not set 617# CONFIG_NANDDUMP is not set
618# CONFIG_SETSERIAL is not set
618# CONFIG_UBIATTACH is not set 619# CONFIG_UBIATTACH is not set
619# CONFIG_UBIDETACH is not set 620# CONFIG_UBIDETACH is not set
620# CONFIG_UBIMKVOL is not set 621# CONFIG_UBIMKVOL is not set
621# CONFIG_UBIRMVOL is not set 622# CONFIG_UBIRMVOL is not set
622# CONFIG_UBIRSVOL is not set 623# CONFIG_UBIRSVOL is not set
624# CONFIG_UBIUPDATEVOL is not set
623# CONFIG_ADJTIMEX is not set 625# CONFIG_ADJTIMEX is not set
624CONFIG_BBCONFIG=y 626CONFIG_BBCONFIG=y
625CONFIG_FEATURE_COMPRESS_BBCONFIG=y 627CONFIG_FEATURE_COMPRESS_BBCONFIG=y
@@ -826,6 +828,7 @@ CONFIG_DHCPD_LEASES_FILE=""
826# CONFIG_FEATURE_UDHCP_PORT is not set 828# CONFIG_FEATURE_UDHCP_PORT is not set
827CONFIG_UDHCP_DEBUG=0 829CONFIG_UDHCP_DEBUG=0
828# CONFIG_FEATURE_UDHCP_RFC3397 is not set 830# CONFIG_FEATURE_UDHCP_RFC3397 is not set
831# CONFIG_FEATURE_UDHCP_8021Q is not set
829CONFIG_UDHCPC_DEFAULT_SCRIPT="" 832CONFIG_UDHCPC_DEFAULT_SCRIPT=""
830CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0 833CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0
831CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="" 834CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS=""
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index 6e097889f..ea2446a89 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -173,7 +173,7 @@ static void conf_askvalue(struct symbol *sym, const char *def)
173int conf_string(struct menu *menu) 173int conf_string(struct menu *menu)
174{ 174{
175 struct symbol *sym = menu->sym; 175 struct symbol *sym = menu->sym;
176 const char *def, *help; 176 const char *def;
177 177
178 while (1) { 178 while (1) {
179 printf("%*s%s ", indent - 1, "", menu->prompt->text); 179 printf("%*s%s ", indent - 1, "", menu->prompt->text);
@@ -188,10 +188,7 @@ int conf_string(struct menu *menu)
188 case '?': 188 case '?':
189 /* print help */ 189 /* print help */
190 if (line[1] == '\n') { 190 if (line[1] == '\n') {
191 help = nohelp_text; 191 printf("\n%s\n", menu->sym->help ? menu->sym->help : nohelp_text);
192 if (menu->sym->help)
193 help = menu->sym->help;
194 printf("\n%s\n", menu->sym->help);
195 def = NULL; 192 def = NULL;
196 break; 193 break;
197 } 194 }
@@ -207,7 +204,6 @@ int conf_string(struct menu *menu)
207static int conf_sym(struct menu *menu) 204static int conf_sym(struct menu *menu)
208{ 205{
209 struct symbol *sym = menu->sym; 206 struct symbol *sym = menu->sym;
210 int type;
211 tristate oldval, newval; 207 tristate oldval, newval;
212 const char *help; 208 const char *help;
213 209
@@ -215,7 +211,6 @@ static int conf_sym(struct menu *menu)
215 printf("%*s%s ", indent - 1, "", menu->prompt->text); 211 printf("%*s%s ", indent - 1, "", menu->prompt->text);
216 if (sym->name) 212 if (sym->name)
217 printf("(%s) ", sym->name); 213 printf("(%s) ", sym->name);
218 type = sym_get_type(sym);
219 putchar('['); 214 putchar('[');
220 oldval = sym_get_tristate_value(sym); 215 oldval = sym_get_tristate_value(sym);
221 switch (oldval) { 216 switch (oldval) {
@@ -282,11 +277,9 @@ static int conf_choice(struct menu *menu)
282{ 277{
283 struct symbol *sym, *def_sym; 278 struct symbol *sym, *def_sym;
284 struct menu *child; 279 struct menu *child;
285 int type;
286 bool is_new; 280 bool is_new;
287 281
288 sym = menu->sym; 282 sym = menu->sym;
289 type = sym_get_type(sym);
290 is_new = !sym_has_value(sym); 283 is_new = !sym_has_value(sym);
291 if (sym_is_changable(sym)) { 284 if (sym_is_changable(sym)) {
292 conf_sym(menu); 285 conf_sym(menu);
diff --git a/scripts/kconfig/lxdialog/textbox.c b/scripts/kconfig/lxdialog/textbox.c
index 77848bb8e..de4ae41d7 100644
--- a/scripts/kconfig/lxdialog/textbox.c
+++ b/scripts/kconfig/lxdialog/textbox.c
@@ -38,11 +38,8 @@ int dialog_textbox(const char *title, const char *file, int height, int width)
38{ 38{
39 int i, x, y, cur_x, cur_y, fpos, key = 0; 39 int i, x, y, cur_x, cur_y, fpos, key = 0;
40 int passed_end; 40 int passed_end;
41 char search_term[MAX_LEN + 1];
42 WINDOW *dialog, *text; 41 WINDOW *dialog, *text;
43 42
44 search_term[0] = '\0'; /* no search term entered yet */
45
46 /* Open input file for reading */ 43 /* Open input file for reading */
47 if ((fd = open(file, O_RDONLY)) == -1) { 44 if ((fd = open(file, O_RDONLY)) == -1) {
48 endwin(); 45 endwin();
@@ -437,7 +434,6 @@ static void print_page(WINDOW * win, int height, int width)
437 */ 434 */
438static void print_line(WINDOW * win, int row, int width) 435static void print_line(WINDOW * win, int row, int width)
439{ 436{
440 int y, x;
441 char *line; 437 char *line;
442 438
443 line = get_line(); 439 line = get_line();
@@ -446,11 +442,13 @@ static void print_line(WINDOW * win, int row, int width)
446 waddch(win, ' '); 442 waddch(win, ' ');
447 waddnstr(win, line, MIN(strlen(line), width - 2)); 443 waddnstr(win, line, MIN(strlen(line), width - 2));
448 444
449 getyx(win, y, x);
450 /* Clear 'residue' of previous line */ 445 /* Clear 'residue' of previous line */
451#if OLD_NCURSES 446#if OLD_NCURSES
452 { 447 {
453 int i; 448 int i;
449 int y, x;
450
451 getyx(win, y, x);
454 for (i = 0; i < width - x; i++) 452 for (i = 0; i < width - x; i++)
455 waddch(win, ' '); 453 waddch(win, ' ');
456 } 454 }
diff --git a/scripts/trylink b/scripts/trylink
index 5994a757b..a8b0b2e03 100755
--- a/scripts/trylink
+++ b/scripts/trylink
@@ -255,6 +255,7 @@ if test "$CONFIG_FEATURE_SHARED_BUSYBOX" = y; then
255 $GC_SECTIONS \ 255 $GC_SECTIONS \
256 $START_GROUP $O_FILES $END_GROUP \ 256 $START_GROUP $O_FILES $END_GROUP \
257 -L"$sharedlib_dir" -lbusybox \ 257 -L"$sharedlib_dir" -lbusybox \
258 $l_list \
258 $INFO_OPTS \ 259 $INFO_OPTS \
259 || { 260 || {
260 echo "Linking $EXE failed" 261 echo "Linking $EXE failed"
diff --git a/selinux/chcon.c b/selinux/chcon.c
index 8644502b5..88d0cfec6 100644
--- a/selinux/chcon.c
+++ b/selinux/chcon.c
@@ -40,7 +40,6 @@
40//usage: "\n -R Recurse" 40//usage: "\n -R Recurse"
41//usage: ) 41//usage: )
42 42
43#include <getopt.h>
44#include <selinux/context.h> 43#include <selinux/context.h>
45 44
46#include "libbb.h" 45#include "libbb.h"
diff --git a/selinux/runcon.c b/selinux/runcon.c
index f0b21269f..3183a2274 100644
--- a/selinux/runcon.c
+++ b/selinux/runcon.c
@@ -50,7 +50,6 @@
50//usage: "\n -l RNG Levelrange" 50//usage: "\n -l RNG Levelrange"
51//usage: ) 51//usage: )
52 52
53#include <getopt.h>
54#include <selinux/context.h> 53#include <selinux/context.h>
55#include <selinux/flask.h> 54#include <selinux/flask.h>
56 55
diff --git a/shell/ash.c b/shell/ash.c
index eaaa71967..a809bf181 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -6276,7 +6276,7 @@ expbackq(union node *cmd, int quoted, int quotes)
6276 read: 6276 read:
6277 if (in.fd < 0) 6277 if (in.fd < 0)
6278 break; 6278 break;
6279 i = nonblock_safe_read(in.fd, buf, sizeof(buf)); 6279 i = nonblock_immune_read(in.fd, buf, sizeof(buf), /*loop_on_EINTR:*/ 1);
6280 TRACE(("expbackq: read returns %d\n", i)); 6280 TRACE(("expbackq: read returns %d\n", i));
6281 if (i <= 0) 6281 if (i <= 0)
6282 break; 6282 break;
@@ -10120,7 +10120,7 @@ preadfd(void)
10120#if ENABLE_FEATURE_EDITING 10120#if ENABLE_FEATURE_EDITING
10121 retry: 10121 retry:
10122 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) 10122 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
10123 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 10123 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
10124 else { 10124 else {
10125 int timeout = -1; 10125 int timeout = -1;
10126# if ENABLE_ASH_IDLE_TIMEOUT 10126# if ENABLE_ASH_IDLE_TIMEOUT
@@ -10166,10 +10166,10 @@ preadfd(void)
10166 } 10166 }
10167 } 10167 }
10168#else 10168#else
10169 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 10169 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
10170#endif 10170#endif
10171 10171
10172#if 0 /* disabled: nonblock_safe_read() handles this problem */ 10172#if 0 /* disabled: nonblock_immune_read() handles this problem */
10173 if (nr < 0) { 10173 if (nr < 0) {
10174 if (parsefile->fd == 0 && errno == EWOULDBLOCK) { 10174 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
10175 int flags = fcntl(0, F_GETFL); 10175 int flags = fcntl(0, F_GETFL);
diff --git a/shell/hush.c b/shell/hush.c
index d3e957c2f..1082738a2 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -106,6 +106,10 @@
106# define PIPE_BUF 4096 /* amount of buffering in a pipe */ 106# define PIPE_BUF 4096 /* amount of buffering in a pipe */
107#endif 107#endif
108 108
109/* Not every libc has sighandler_t. Fix it */
110typedef void (*hush_sighandler_t)(int);
111#define sighandler_t hush_sighandler_t
112
109//config:config HUSH 113//config:config HUSH
110//config: bool "hush" 114//config: bool "hush"
111//config: default y 115//config: default y
@@ -256,7 +260,7 @@
256 * therefore we don't show them either. 260 * therefore we don't show them either.
257 */ 261 */
258//usage:#define hush_trivial_usage 262//usage:#define hush_trivial_usage
259//usage: "[-nx] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" 263//usage: "[-nxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
260//usage:#define hush_full_usage "\n\n" 264//usage:#define hush_full_usage "\n\n"
261//usage: "Unix shell interpreter" 265//usage: "Unix shell interpreter"
262 266
@@ -445,6 +449,15 @@ enum {
445/* Used for initialization: o_string foo = NULL_O_STRING; */ 449/* Used for initialization: o_string foo = NULL_O_STRING; */
446#define NULL_O_STRING { NULL } 450#define NULL_O_STRING { NULL }
447 451
452#ifndef debug_printf_parse
453static const char *const assignment_flag[] = {
454 "MAYBE_ASSIGNMENT",
455 "DEFINITELY_ASSIGNMENT",
456 "NOT_ASSIGNMENT",
457 "WORD_IS_KEYWORD",
458};
459#endif
460
448typedef struct in_str { 461typedef struct in_str {
449 const char *p; 462 const char *p;
450 /* eof_flag=1: last char in ->p is really an EOF */ 463 /* eof_flag=1: last char in ->p is really an EOF */
@@ -764,7 +777,6 @@ struct globals {
764 smalluint last_exitcode; 777 smalluint last_exitcode;
765 /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */ 778 /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
766 smalluint global_args_malloced; 779 smalluint global_args_malloced;
767 smalluint inherited_set_is_saved;
768 /* how many non-NULL argv's we have. NB: $# + 1 */ 780 /* how many non-NULL argv's we have. NB: $# + 1 */
769 int global_argc; 781 int global_argc;
770 char **global_argv; 782 char **global_argv;
@@ -792,15 +804,27 @@ struct globals {
792 unsigned handled_SIGCHLD; 804 unsigned handled_SIGCHLD;
793 smallint we_have_children; 805 smallint we_have_children;
794#endif 806#endif
795 /* which signals have non-DFL handler (even with no traps set)? */ 807 /* Which signals have non-DFL handler (even with no traps set)?
796 unsigned non_DFL_mask; 808 * Set at the start to:
809 * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS)
810 * SPECIAL_INTERACTIVE_SIGS are cleared after fork.
811 * The rest is cleared right before execv syscalls.
812 * Other than these two times, never modified.
813 */
814 unsigned special_sig_mask;
815#if ENABLE_HUSH_JOB
816 unsigned fatal_sig_mask;
817# define G_fatal_sig_mask G.fatal_sig_mask
818#else
819# define G_fatal_sig_mask 0
820#endif
797 char **traps; /* char *traps[NSIG] */ 821 char **traps; /* char *traps[NSIG] */
798 sigset_t blocked_set; 822 sigset_t pending_set;
799 sigset_t inherited_set;
800#if HUSH_DEBUG 823#if HUSH_DEBUG
801 unsigned long memleak_value; 824 unsigned long memleak_value;
802 int debug_indent; 825 int debug_indent;
803#endif 826#endif
827 struct sigaction sa;
804 char user_input_buf[ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 2]; 828 char user_input_buf[ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 2];
805}; 829};
806#define G (*ptr_to_globals) 830#define G (*ptr_to_globals)
@@ -809,6 +833,9 @@ struct globals {
809 * is global, thus "G." prefix is a useful hint */ 833 * is global, thus "G." prefix is a useful hint */
810#define INIT_G() do { \ 834#define INIT_G() do { \
811 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 835 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
836 /* memset(&G.sa, 0, sizeof(G.sa)); */ \
837 sigfillset(&G.sa.sa_mask); \
838 G.sa.sa_flags = SA_RESTART; \
812} while (0) 839} while (0)
813 840
814 841
@@ -1319,12 +1346,14 @@ static void restore_G_args(save_arg_t *sv, char **argv)
1319 * "echo $$; sleep 5 & wait; ls -l" + "kill -INT <pid>" 1346 * "echo $$; sleep 5 & wait; ls -l" + "kill -INT <pid>"
1320 * Example 3: this does not wait 5 sec, but executes ls: 1347 * Example 3: this does not wait 5 sec, but executes ls:
1321 * "sleep 5; ls -l" + press ^C 1348 * "sleep 5; ls -l" + press ^C
1349 * Example 4: this does not wait and does not execute ls:
1350 * "sleep 5 & wait; ls -l" + press ^C
1322 * 1351 *
1323 * (What happens to signals which are IGN on shell start?) 1352 * (What happens to signals which are IGN on shell start?)
1324 * (What happens with signal mask on shell start?) 1353 * (What happens with signal mask on shell start?)
1325 * 1354 *
1326 * Implementation in hush 1355 * Old implementation
1327 * ====================== 1356 * ==================
1328 * We use in-kernel pending signal mask to determine which signals were sent. 1357 * We use in-kernel pending signal mask to determine which signals were sent.
1329 * We block all signals which we don't want to take action immediately, 1358 * We block all signals which we don't want to take action immediately,
1330 * i.e. we block all signals which need to have special handling as described 1359 * i.e. we block all signals which need to have special handling as described
@@ -1332,11 +1361,11 @@ static void restore_G_args(save_arg_t *sv, char **argv)
1332 * After each pipe execution, we extract any pending signals via sigtimedwait() 1361 * After each pipe execution, we extract any pending signals via sigtimedwait()
1333 * and act on them. 1362 * and act on them.
1334 * 1363 *
1335 * unsigned non_DFL_mask: a mask of such "special" signals 1364 * unsigned special_sig_mask: a mask of such "special" signals
1336 * sigset_t blocked_set: current blocked signal set 1365 * sigset_t blocked_set: current blocked signal set
1337 * 1366 *
1338 * "trap - SIGxxx": 1367 * "trap - SIGxxx":
1339 * clear bit in blocked_set unless it is also in non_DFL_mask 1368 * clear bit in blocked_set unless it is also in special_sig_mask
1340 * "trap 'cmd' SIGxxx": 1369 * "trap 'cmd' SIGxxx":
1341 * set bit in blocked_set (even if 'cmd' is '') 1370 * set bit in blocked_set (even if 'cmd' is '')
1342 * after [v]fork, if we plan to be a shell: 1371 * after [v]fork, if we plan to be a shell:
@@ -1355,6 +1384,49 @@ static void restore_G_args(save_arg_t *sv, char **argv)
1355 * Standard says "When a subshell is entered, traps that are not being ignored 1384 * Standard says "When a subshell is entered, traps that are not being ignored
1356 * are set to the default actions". bash interprets it so that traps which 1385 * are set to the default actions". bash interprets it so that traps which
1357 * are set to '' (ignore) are NOT reset to defaults. We do the same. 1386 * are set to '' (ignore) are NOT reset to defaults. We do the same.
1387 *
1388 * Problem: the above approach makes it unwieldy to catch signals while
1389 * we are in read builtin, of while we read commands from stdin:
1390 * masked signals are not visible!
1391 *
1392 * New implementation
1393 * ==================
1394 * We record each signal we are interested in by installing signal handler
1395 * for them - a bit like emulating kernel pending signal mask in userspace.
1396 * We are interested in: signals which need to have special handling
1397 * as described above, and all signals which have traps set.
1398 * Signals are rocorded in pending_set.
1399 * After each pipe execution, we extract any pending signals
1400 * and act on them.
1401 *
1402 * unsigned special_sig_mask: a mask of shell-special signals.
1403 * unsigned fatal_sig_mask: a mask of signals on which we restore tty pgrp.
1404 * char *traps[sig] if trap for sig is set (even if it's '').
1405 * sigset_t pending_set: set of sigs we received.
1406 *
1407 * "trap - SIGxxx":
1408 * if sig is in special_sig_mask, set handler back to:
1409 * record_pending_signo, or to IGN if it's a tty stop signal
1410 * if sig is in fatal_sig_mask, set handler back to sigexit.
1411 * else: set handler back to SIG_DFL
1412 * "trap 'cmd' SIGxxx":
1413 * set handler to record_pending_signo.
1414 * "trap '' SIGxxx":
1415 * set handler to SIG_IGN.
1416 * after [v]fork, if we plan to be a shell:
1417 * set signals with special interactive handling to SIG_DFL
1418 * (because child shell is not interactive),
1419 * unset all traps except '' (note: regardless of child shell's type - {}, (), etc)
1420 * after [v]fork, if we plan to exec:
1421 * POSIX says fork clears pending signal mask in child - no need to clear it.
1422 *
1423 * To make wait builtin interruptible, we handle SIGCHLD as special signal,
1424 * otherwise (if we leave it SIG_DFL) sigsuspend in wait builtin will not wake up on it.
1425 *
1426 * Note (compat):
1427 * Standard says "When a subshell is entered, traps that are not being ignored
1428 * are set to the default actions". bash interprets it so that traps which
1429 * are set to '' (ignore) are NOT reset to defaults. We do the same.
1358 */ 1430 */
1359enum { 1431enum {
1360 SPECIAL_INTERACTIVE_SIGS = 0 1432 SPECIAL_INTERACTIVE_SIGS = 0
@@ -1362,21 +1434,43 @@ enum {
1362 | (1 << SIGINT) 1434 | (1 << SIGINT)
1363 | (1 << SIGHUP) 1435 | (1 << SIGHUP)
1364 , 1436 ,
1365 SPECIAL_JOB_SIGS = 0 1437 SPECIAL_JOBSTOP_SIGS = 0
1366#if ENABLE_HUSH_JOB 1438#if ENABLE_HUSH_JOB
1367 | (1 << SIGTTIN) 1439 | (1 << SIGTTIN)
1368 | (1 << SIGTTOU) 1440 | (1 << SIGTTOU)
1369 | (1 << SIGTSTP) 1441 | (1 << SIGTSTP)
1370#endif 1442#endif
1443 ,
1371}; 1444};
1372 1445
1373#if ENABLE_HUSH_FAST 1446static void record_pending_signo(int sig)
1374static void SIGCHLD_handler(int sig UNUSED_PARAM)
1375{ 1447{
1376 G.count_SIGCHLD++; 1448 sigaddset(&G.pending_set, sig);
1449#if ENABLE_HUSH_FAST
1450 if (sig == SIGCHLD) {
1451 G.count_SIGCHLD++;
1377//bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); 1452//bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
1378} 1453 }
1379#endif 1454#endif
1455}
1456
1457static sighandler_t install_sighandler(int sig, sighandler_t handler)
1458{
1459 struct sigaction old_sa;
1460
1461 /* We could use signal() to install handlers... almost:
1462 * except that we need to mask ALL signals while handlers run.
1463 * I saw signal nesting in strace, race window isn't small.
1464 * SA_RESTART is also needed, but in Linux, signal()
1465 * sets SA_RESTART too.
1466 */
1467 /* memset(&G.sa, 0, sizeof(G.sa)); - already done */
1468 /* sigfillset(&G.sa.sa_mask); - already done */
1469 /* G.sa.sa_flags = SA_RESTART; - already done */
1470 G.sa.sa_handler = handler;
1471 sigaction(sig, &G.sa, &old_sa);
1472 return old_sa.sa_handler;
1473}
1380 1474
1381#if ENABLE_HUSH_JOB 1475#if ENABLE_HUSH_JOB
1382 1476
@@ -1393,13 +1487,15 @@ static void SIGCHLD_handler(int sig UNUSED_PARAM)
1393static void sigexit(int sig) NORETURN; 1487static void sigexit(int sig) NORETURN;
1394static void sigexit(int sig) 1488static void sigexit(int sig)
1395{ 1489{
1396 /* Disable all signals: job control, SIGPIPE, etc. */
1397 sigprocmask_allsigs(SIG_BLOCK);
1398
1399 /* Careful: we can end up here after [v]fork. Do not restore 1490 /* Careful: we can end up here after [v]fork. Do not restore
1400 * tty pgrp then, only top-level shell process does that */ 1491 * tty pgrp then, only top-level shell process does that */
1401 if (G_saved_tty_pgrp && getpid() == G.root_pid) 1492 if (G_saved_tty_pgrp && getpid() == G.root_pid) {
1493 /* Disable all signals: job control, SIGPIPE, etc.
1494 * Mostly paranoid measure, to prevent infinite SIGTTOU.
1495 */
1496 sigprocmask_allsigs(SIG_BLOCK);
1402 tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); 1497 tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
1498 }
1403 1499
1404 /* Not a signal, just exit */ 1500 /* Not a signal, just exit */
1405 if (sig <= 0) 1501 if (sig <= 0)
@@ -1414,15 +1510,39 @@ static void sigexit(int sig)
1414 1510
1415#endif 1511#endif
1416 1512
1513static sighandler_t pick_sighandler(unsigned sig)
1514{
1515 sighandler_t handler = SIG_DFL;
1516 if (sig < sizeof(unsigned)*8) {
1517 unsigned sigmask = (1 << sig);
1518
1519#if ENABLE_HUSH_JOB
1520 /* is sig fatal? */
1521 if (G_fatal_sig_mask & sigmask)
1522 handler = sigexit;
1523 else
1524#endif
1525 /* sig has special handling? */
1526 if (G.special_sig_mask & sigmask) {
1527 handler = record_pending_signo;
1528 /* TTIN/TTOU/TSTP can't be set to record_pending_signo
1529 * in order to ignore them: they will be raised
1530 * in an endless loop when we try to do some
1531 * terminal ioctls! We do have to _ignore_ these.
1532 */
1533 if (SPECIAL_JOBSTOP_SIGS & sigmask)
1534 handler = SIG_IGN;
1535 }
1536 }
1537 return handler;
1538}
1539
1417/* Restores tty foreground process group, and exits. */ 1540/* Restores tty foreground process group, and exits. */
1418static void hush_exit(int exitcode) NORETURN; 1541static void hush_exit(int exitcode) NORETURN;
1419static void hush_exit(int exitcode) 1542static void hush_exit(int exitcode)
1420{ 1543{
1421 fflush_all(); 1544 fflush_all();
1422 if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) { 1545 if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) {
1423 /* Prevent recursion:
1424 * trap "echo Hi; exit" EXIT; exit
1425 */
1426 char *argv[3]; 1546 char *argv[3];
1427 /* argv[0] is unused */ 1547 /* argv[0] is unused */
1428 argv[1] = G.traps[0]; 1548 argv[1] = G.traps[0];
@@ -1459,28 +1579,30 @@ static void hush_exit(int exitcode)
1459} 1579}
1460 1580
1461 1581
1462static int check_and_run_traps(int sig) 1582//TODO: return a mask of ALL handled sigs?
1583static int check_and_run_traps(void)
1463{ 1584{
1464 /* I want it in rodata, not in bss.
1465 * gcc 4.2.1 puts it in rodata only if it has { 0, 0 }
1466 * initializer. But other compilers may still use bss.
1467 * TODO: find more portable solution.
1468 */
1469 static const struct timespec zero_timespec = { 0, 0 };
1470 smalluint save_rcode;
1471 int last_sig = 0; 1585 int last_sig = 0;
1472 1586
1473 if (sig)
1474 goto jump_in;
1475 while (1) { 1587 while (1) {
1476 sig = sigtimedwait(&G.blocked_set, NULL, &zero_timespec); 1588 int sig;
1477 if (sig <= 0) 1589
1590 if (sigisemptyset(&G.pending_set))
1478 break; 1591 break;
1479 jump_in: 1592 sig = 0;
1480 last_sig = sig; 1593 do {
1594 sig++;
1595 if (sigismember(&G.pending_set, sig)) {
1596 sigdelset(&G.pending_set, sig);
1597 goto got_sig;
1598 }
1599 } while (sig < NSIG);
1600 break;
1601 got_sig:
1481 if (G.traps && G.traps[sig]) { 1602 if (G.traps && G.traps[sig]) {
1482 if (G.traps[sig][0]) { 1603 if (G.traps[sig][0]) {
1483 /* We have user-defined handler */ 1604 /* We have user-defined handler */
1605 smalluint save_rcode;
1484 char *argv[3]; 1606 char *argv[3];
1485 /* argv[0] is unused */ 1607 /* argv[0] is unused */
1486 argv[1] = G.traps[sig]; 1608 argv[1] = G.traps[sig];
@@ -1488,21 +1610,17 @@ static int check_and_run_traps(int sig)
1488 save_rcode = G.last_exitcode; 1610 save_rcode = G.last_exitcode;
1489 builtin_eval(argv); 1611 builtin_eval(argv);
1490 G.last_exitcode = save_rcode; 1612 G.last_exitcode = save_rcode;
1613 last_sig = sig;
1491 } /* else: "" trap, ignoring signal */ 1614 } /* else: "" trap, ignoring signal */
1492 continue; 1615 continue;
1493 } 1616 }
1494 /* not a trap: special action */ 1617 /* not a trap: special action */
1495 switch (sig) { 1618 switch (sig) {
1496#if ENABLE_HUSH_FAST
1497 case SIGCHLD:
1498 G.count_SIGCHLD++;
1499//bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
1500 break;
1501#endif
1502 case SIGINT: 1619 case SIGINT:
1503 /* Builtin was ^C'ed, make it look prettier: */ 1620 /* Builtin was ^C'ed, make it look prettier: */
1504 bb_putchar('\n'); 1621 bb_putchar('\n');
1505 G.flag_SIGINT = 1; 1622 G.flag_SIGINT = 1;
1623 last_sig = sig;
1506 break; 1624 break;
1507#if ENABLE_HUSH_JOB 1625#if ENABLE_HUSH_JOB
1508 case SIGHUP: { 1626 case SIGHUP: {
@@ -1519,8 +1637,23 @@ static int check_and_run_traps(int sig)
1519 sigexit(SIGHUP); 1637 sigexit(SIGHUP);
1520 } 1638 }
1521#endif 1639#endif
1640#if ENABLE_HUSH_FAST
1641 case SIGCHLD:
1642 G.count_SIGCHLD++;
1643//bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
1644 /* Note:
1645 * We dont do 'last_sig = sig' here -> NOT returning this sig.
1646 * This simplifies wait builtin a bit.
1647 */
1648 break;
1649#endif
1522 default: /* ignored: */ 1650 default: /* ignored: */
1523 /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ 1651 /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */
1652 /* Note:
1653 * We dont do 'last_sig = sig' here -> NOT returning this sig.
1654 * Example: wait is not interrupted by TERM
1655 * in interactive shell, because TERM is ignored.
1656 */
1524 break; 1657 break;
1525 } 1658 }
1526 } 1659 }
@@ -1911,7 +2044,7 @@ static void get_user_input(struct in_str *i)
1911 * only after <Enter>. (^C will work) */ 2044 * only after <Enter>. (^C will work) */
1912 r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1); 2045 r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1);
1913 /* catch *SIGINT* etc (^C is handled by read_line_input) */ 2046 /* catch *SIGINT* etc (^C is handled by read_line_input) */
1914 check_and_run_traps(0); 2047 check_and_run_traps();
1915 } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ 2048 } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */
1916 i->eof_flag = (r < 0); 2049 i->eof_flag = (r < 0);
1917 if (i->eof_flag) { /* EOF/error detected */ 2050 if (i->eof_flag) { /* EOF/error detected */
@@ -1921,11 +2054,18 @@ static void get_user_input(struct in_str *i)
1921# else 2054# else
1922 do { 2055 do {
1923 G.flag_SIGINT = 0; 2056 G.flag_SIGINT = 0;
1924 fputs(prompt_str, stdout); 2057 if (i->last_char == '\0' || i->last_char == '\n') {
2058 /* Why check_and_run_traps here? Try this interactively:
2059 * $ trap 'echo INT' INT; (sleep 2; kill -INT $$) &
2060 * $ <[enter], repeatedly...>
2061 * Without check_and_run_traps, handler never runs.
2062 */
2063 check_and_run_traps();
2064 fputs(prompt_str, stdout);
2065 }
1925 fflush_all(); 2066 fflush_all();
1926 G.user_input_buf[0] = r = fgetc(i->file); 2067 G.user_input_buf[0] = r = fgetc(i->file);
1927 /*G.user_input_buf[1] = '\0'; - already is and never changed */ 2068 /*G.user_input_buf[1] = '\0'; - already is and never changed */
1928//do we need check_and_run_traps(0)? (maybe only if stdin)
1929 } while (G.flag_SIGINT); 2069 } while (G.flag_SIGINT);
1930 i->eof_flag = (r == EOF); 2070 i->eof_flag = (r == EOF);
1931# endif 2071# endif
@@ -2902,24 +3042,24 @@ static const struct reserved_combo* match_reserved_word(o_string *word)
2902 */ 3042 */
2903 static const struct reserved_combo reserved_list[] = { 3043 static const struct reserved_combo reserved_list[] = {
2904# if ENABLE_HUSH_IF 3044# if ENABLE_HUSH_IF
2905 { "!", RES_NONE, NOT_ASSIGNMENT , 0 }, 3045 { "!", RES_NONE, NOT_ASSIGNMENT , 0 },
2906 { "if", RES_IF, WORD_IS_KEYWORD, FLAG_THEN | FLAG_START }, 3046 { "if", RES_IF, MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START },
2907 { "then", RES_THEN, WORD_IS_KEYWORD, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, 3047 { "then", RES_THEN, MAYBE_ASSIGNMENT, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
2908 { "elif", RES_ELIF, WORD_IS_KEYWORD, FLAG_THEN }, 3048 { "elif", RES_ELIF, MAYBE_ASSIGNMENT, FLAG_THEN },
2909 { "else", RES_ELSE, WORD_IS_KEYWORD, FLAG_FI }, 3049 { "else", RES_ELSE, MAYBE_ASSIGNMENT, FLAG_FI },
2910 { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END }, 3050 { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END },
2911# endif 3051# endif
2912# if ENABLE_HUSH_LOOPS 3052# if ENABLE_HUSH_LOOPS
2913 { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START }, 3053 { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START },
2914 { "while", RES_WHILE, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, 3054 { "while", RES_WHILE, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
2915 { "until", RES_UNTIL, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, 3055 { "until", RES_UNTIL, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
2916 { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO }, 3056 { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO },
2917 { "do", RES_DO, WORD_IS_KEYWORD, FLAG_DONE }, 3057 { "do", RES_DO, MAYBE_ASSIGNMENT, FLAG_DONE },
2918 { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END }, 3058 { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END },
2919# endif 3059# endif
2920# if ENABLE_HUSH_CASE 3060# if ENABLE_HUSH_CASE
2921 { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START }, 3061 { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START },
2922 { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END }, 3062 { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END },
2923# endif 3063# endif
2924 }; 3064 };
2925 const struct reserved_combo *r; 3065 const struct reserved_combo *r;
@@ -2985,6 +3125,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx)
2985 ctx->ctx_res_w = r->res; 3125 ctx->ctx_res_w = r->res;
2986 ctx->old_flag = r->flag; 3126 ctx->old_flag = r->flag;
2987 word->o_assignment = r->assignment_flag; 3127 word->o_assignment = r->assignment_flag;
3128 debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]);
2988 3129
2989 if (ctx->old_flag & FLAG_END) { 3130 if (ctx->old_flag & FLAG_END) {
2990 struct parse_context *old; 3131 struct parse_context *old;
@@ -3051,18 +3192,6 @@ static int done_word(o_string *word, struct parse_context *ctx)
3051 debug_printf_parse("word stored in rd_filename: '%s'\n", word->data); 3192 debug_printf_parse("word stored in rd_filename: '%s'\n", word->data);
3052 ctx->pending_redirect = NULL; 3193 ctx->pending_redirect = NULL;
3053 } else { 3194 } else {
3054 /* If this word wasn't an assignment, next ones definitely
3055 * can't be assignments. Even if they look like ones. */
3056 if (word->o_assignment != DEFINITELY_ASSIGNMENT
3057 && word->o_assignment != WORD_IS_KEYWORD
3058 ) {
3059 word->o_assignment = NOT_ASSIGNMENT;
3060 } else {
3061 if (word->o_assignment == DEFINITELY_ASSIGNMENT)
3062 command->assignment_cnt++;
3063 word->o_assignment = MAYBE_ASSIGNMENT;
3064 }
3065
3066#if HAS_KEYWORDS 3195#if HAS_KEYWORDS
3067# if ENABLE_HUSH_CASE 3196# if ENABLE_HUSH_CASE
3068 if (ctx->ctx_dsemicolon 3197 if (ctx->ctx_dsemicolon
@@ -3082,8 +3211,9 @@ static int done_word(o_string *word, struct parse_context *ctx)
3082 && ctx->ctx_res_w != RES_CASE 3211 && ctx->ctx_res_w != RES_CASE
3083# endif 3212# endif
3084 ) { 3213 ) {
3085 debug_printf_parse("checking '%s' for reserved-ness\n", word->data); 3214 int reserved = reserved_word(word, ctx);
3086 if (reserved_word(word, ctx)) { 3215 debug_printf_parse("checking for reserved-ness: %d\n", reserved);
3216 if (reserved) {
3087 o_reset_to_empty_unquoted(word); 3217 o_reset_to_empty_unquoted(word);
3088 debug_printf_parse("done_word return %d\n", 3218 debug_printf_parse("done_word return %d\n",
3089 (ctx->ctx_res_w == RES_SNTX)); 3219 (ctx->ctx_res_w == RES_SNTX));
@@ -3104,6 +3234,23 @@ static int done_word(o_string *word, struct parse_context *ctx)
3104 "groups and arglists don't mix\n"); 3234 "groups and arglists don't mix\n");
3105 return 1; 3235 return 1;
3106 } 3236 }
3237
3238 /* If this word wasn't an assignment, next ones definitely
3239 * can't be assignments. Even if they look like ones. */
3240 if (word->o_assignment != DEFINITELY_ASSIGNMENT
3241 && word->o_assignment != WORD_IS_KEYWORD
3242 ) {
3243 word->o_assignment = NOT_ASSIGNMENT;
3244 } else {
3245 if (word->o_assignment == DEFINITELY_ASSIGNMENT) {
3246 command->assignment_cnt++;
3247 debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt);
3248 }
3249 debug_printf_parse("word->o_assignment was:'%s'\n", assignment_flag[word->o_assignment]);
3250 word->o_assignment = MAYBE_ASSIGNMENT;
3251 }
3252 debug_printf_parse("word->o_assignment='%s'\n", assignment_flag[word->o_assignment]);
3253
3107 if (word->has_quoted_part 3254 if (word->has_quoted_part
3108 /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ 3255 /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */
3109 && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) 3256 && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL)
@@ -3322,6 +3469,7 @@ static char *fetch_till_str(o_string *as_string,
3322 int ch; 3469 int ch;
3323 3470
3324 goto jump_in; 3471 goto jump_in;
3472
3325 while (1) { 3473 while (1) {
3326 ch = i_getch(input); 3474 ch = i_getch(input);
3327 if (ch != EOF) 3475 if (ch != EOF)
@@ -4127,6 +4275,7 @@ static struct pipe *parse_stream(char **pstring,
4127 && is_well_formed_var_name(dest.data, '=') 4275 && is_well_formed_var_name(dest.data, '=')
4128 ) { 4276 ) {
4129 dest.o_assignment = DEFINITELY_ASSIGNMENT; 4277 dest.o_assignment = DEFINITELY_ASSIGNMENT;
4278 debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
4130 } 4279 }
4131 continue; 4280 continue;
4132 } 4281 }
@@ -4176,6 +4325,7 @@ static struct pipe *parse_stream(char **pstring,
4176 heredoc_cnt = 0; 4325 heredoc_cnt = 0;
4177 } 4326 }
4178 dest.o_assignment = MAYBE_ASSIGNMENT; 4327 dest.o_assignment = MAYBE_ASSIGNMENT;
4328 debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
4179 ch = ';'; 4329 ch = ';';
4180 /* note: if (is_blank) continue; 4330 /* note: if (is_blank) continue;
4181 * will still trigger for us */ 4331 * will still trigger for us */
@@ -4225,6 +4375,7 @@ static struct pipe *parse_stream(char **pstring,
4225 } 4375 }
4226 done_pipe(&ctx, PIPE_SEQ); 4376 done_pipe(&ctx, PIPE_SEQ);
4227 dest.o_assignment = MAYBE_ASSIGNMENT; 4377 dest.o_assignment = MAYBE_ASSIGNMENT;
4378 debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
4228 /* Do we sit outside of any if's, loops or case's? */ 4379 /* Do we sit outside of any if's, loops or case's? */
4229 if (!HAS_KEYWORDS 4380 if (!HAS_KEYWORDS
4230 IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0)) 4381 IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
@@ -4331,6 +4482,7 @@ static struct pipe *parse_stream(char **pstring,
4331 /* ch is a special char and thus this word 4482 /* ch is a special char and thus this word
4332 * cannot be an assignment */ 4483 * cannot be an assignment */
4333 dest.o_assignment = NOT_ASSIGNMENT; 4484 dest.o_assignment = NOT_ASSIGNMENT;
4485 debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
4334 } 4486 }
4335 4487
4336 /* Note: nommu_addchr(&ctx.as_string, ch) is already done */ 4488 /* Note: nommu_addchr(&ctx.as_string, ch) is already done */
@@ -4385,11 +4537,11 @@ static struct pipe *parse_stream(char **pstring,
4385 break; 4537 break;
4386#if ENABLE_HUSH_TICK 4538#if ENABLE_HUSH_TICK
4387 case '`': { 4539 case '`': {
4388 unsigned pos; 4540 USE_FOR_NOMMU(unsigned pos;)
4389 4541
4390 o_addchr(&dest, SPECIAL_VAR_SYMBOL); 4542 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
4391 o_addchr(&dest, '`'); 4543 o_addchr(&dest, '`');
4392 pos = dest.length; 4544 USE_FOR_NOMMU(pos = dest.length;)
4393 if (!add_till_backquote(&dest, input, /*in_dquote:*/ 0)) 4545 if (!add_till_backquote(&dest, input, /*in_dquote:*/ 0))
4394 goto parse_error; 4546 goto parse_error;
4395# if !BB_MMU 4547# if !BB_MMU
@@ -4429,6 +4581,7 @@ static struct pipe *parse_stream(char **pstring,
4429 /* We just finished a cmd. New one may start 4581 /* We just finished a cmd. New one may start
4430 * with an assignment */ 4582 * with an assignment */
4431 dest.o_assignment = MAYBE_ASSIGNMENT; 4583 dest.o_assignment = MAYBE_ASSIGNMENT;
4584 debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
4432 break; 4585 break;
4433 case '&': 4586 case '&':
4434 if (done_word(&dest, &ctx)) { 4587 if (done_word(&dest, &ctx)) {
@@ -5323,6 +5476,25 @@ void re_execute_shell(char ***to_free, const char *s,
5323 char *g_argv0, char **g_argv, 5476 char *g_argv0, char **g_argv,
5324 char **builtin_argv) NORETURN; 5477 char **builtin_argv) NORETURN;
5325 5478
5479static void switch_off_special_sigs(unsigned mask)
5480{
5481 unsigned sig = 0;
5482 while ((mask >>= 1) != 0) {
5483 sig++;
5484 if (!(mask & 1))
5485 continue;
5486 if (G.traps) {
5487 if (G.traps[sig] && !G.traps[sig][0])
5488 /* trap is '', has to remain SIG_IGN */
5489 continue;
5490 free(G.traps[sig]);
5491 G.traps[sig] = NULL;
5492 }
5493 /* We are here only if no trap or trap was not '' */
5494 install_sighandler(sig, SIG_DFL);
5495 }
5496}
5497
5326static void reset_traps_to_defaults(void) 5498static void reset_traps_to_defaults(void)
5327{ 5499{
5328 /* This function is always called in a child shell 5500 /* This function is always called in a child shell
@@ -5336,47 +5508,36 @@ static void reset_traps_to_defaults(void)
5336 * Testcase: (while :; do :; done) + ^Z should background. 5508 * Testcase: (while :; do :; done) + ^Z should background.
5337 * Same goes for SIGTERM, SIGHUP, SIGINT. 5509 * Same goes for SIGTERM, SIGHUP, SIGINT.
5338 */ 5510 */
5339 if (!G.traps && !(G.non_DFL_mask & SPECIAL_INTERACTIVE_SIGS)) 5511 mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask;
5340 return; /* already no traps and no SPECIAL_INTERACTIVE_SIGS */ 5512 if (!G.traps && !mask)
5341 5513 return; /* already no traps and no special sigs */
5342 /* Switching off SPECIAL_INTERACTIVE_SIGS.
5343 * Stupid. It can be done with *single* &= op, but we can't use
5344 * the fact that G.blocked_set is implemented as a bitmask
5345 * in libc... */
5346 mask = (SPECIAL_INTERACTIVE_SIGS >> 1);
5347 sig = 1;
5348 while (1) {
5349 if (mask & 1) {
5350 /* Careful. Only if no trap or trap is not "" */
5351 if (!G.traps || !G.traps[sig] || G.traps[sig][0])
5352 sigdelset(&G.blocked_set, sig);
5353 }
5354 mask >>= 1;
5355 if (!mask)
5356 break;
5357 sig++;
5358 }
5359 /* Our homegrown sig mask is saner to work with :) */
5360 G.non_DFL_mask &= ~SPECIAL_INTERACTIVE_SIGS;
5361 5514
5362 /* Resetting all traps to default except empty ones */ 5515 /* Switch off special sigs */
5363 mask = G.non_DFL_mask; 5516 switch_off_special_sigs(mask);
5364 if (G.traps) for (sig = 0; sig < NSIG; sig++, mask >>= 1) { 5517#if ENABLE_HUSH_JOB
5365 if (!G.traps[sig] || !G.traps[sig][0]) 5518 G_fatal_sig_mask = 0;
5366 continue; 5519#endif
5520 G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS;
5521 /* SIGQUIT,SIGCHLD and maybe SPECIAL_JOBSTOP_SIGS
5522 * remain set in G.special_sig_mask */
5523
5524 if (!G.traps)
5525 return;
5526
5527 /* Reset all sigs to default except ones with empty traps */
5528 for (sig = 0; sig < NSIG; sig++) {
5529 if (!G.traps[sig])
5530 continue; /* no trap: nothing to do */
5531 if (!G.traps[sig][0])
5532 continue; /* empty trap: has to remain SIG_IGN */
5533 /* sig has non-empty trap, reset it: */
5367 free(G.traps[sig]); 5534 free(G.traps[sig]);
5368 G.traps[sig] = NULL; 5535 G.traps[sig] = NULL;
5369 /* There is no signal for 0 (EXIT) */ 5536 /* There is no signal for trap 0 (EXIT) */
5370 if (sig == 0) 5537 if (sig == 0)
5371 continue; 5538 continue;
5372 /* There was a trap handler, we just removed it. 5539 install_sighandler(sig, pick_sighandler(sig));
5373 * But if sig still has non-DFL handling,
5374 * we should not unblock the sig. */
5375 if (mask & 1)
5376 continue;
5377 sigdelset(&G.blocked_set, sig);
5378 } 5540 }
5379 sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
5380} 5541}
5381 5542
5382#else /* !BB_MMU */ 5543#else /* !BB_MMU */
@@ -5487,9 +5648,6 @@ static void re_execute_shell(char ***to_free, const char *s,
5487 * _inside_ group (just before echo 1), it works. 5648 * _inside_ group (just before echo 1), it works.
5488 * 5649 *
5489 * I conclude it means we don't need to pass active traps here. 5650 * I conclude it means we don't need to pass active traps here.
5490 * Even if we would use signal handlers instead of signal masking
5491 * in order to implement trap handling,
5492 * exec syscall below resets signals to SIG_DFL for us.
5493 */ 5651 */
5494 *pp++ = (char *) "-c"; 5652 *pp++ = (char *) "-c";
5495 *pp++ = (char *) s; 5653 *pp++ = (char *) s;
@@ -5506,7 +5664,9 @@ static void re_execute_shell(char ***to_free, const char *s,
5506 5664
5507 do_exec: 5665 do_exec:
5508 debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s); 5666 debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s);
5509 sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); 5667 /* Don't propagate SIG_IGN to the child */
5668 if (SPECIAL_JOBSTOP_SIGS != 0)
5669 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
5510 execve(bb_busybox_exec_path, argv, pp); 5670 execve(bb_busybox_exec_path, argv, pp);
5511 /* Fallback. Useful for init=/bin/hush usage etc */ 5671 /* Fallback. Useful for init=/bin/hush usage etc */
5512 if (argv[0][0] == '/') 5672 if (argv[0][0] == '/')
@@ -6160,7 +6320,9 @@ static void execvp_or_die(char **argv) NORETURN;
6160static void execvp_or_die(char **argv) 6320static void execvp_or_die(char **argv)
6161{ 6321{
6162 debug_printf_exec("execing '%s'\n", argv[0]); 6322 debug_printf_exec("execing '%s'\n", argv[0]);
6163 sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); 6323 /* Don't propagate SIG_IGN to the child */
6324 if (SPECIAL_JOBSTOP_SIGS != 0)
6325 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
6164 execvp(argv[0], argv); 6326 execvp(argv[0], argv);
6165 bb_perror_msg("can't execute '%s'", argv[0]); 6327 bb_perror_msg("can't execute '%s'", argv[0]);
6166 _exit(127); /* bash compat */ 6328 _exit(127); /* bash compat */
@@ -6292,7 +6454,9 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
6292# endif 6454# endif
6293 /* Re-exec ourselves */ 6455 /* Re-exec ourselves */
6294 debug_printf_exec("re-execing applet '%s'\n", argv[0]); 6456 debug_printf_exec("re-execing applet '%s'\n", argv[0]);
6295 sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); 6457 /* Don't propagate SIG_IGN to the child */
6458 if (SPECIAL_JOBSTOP_SIGS != 0)
6459 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
6296 execv(bb_busybox_exec_path, argv); 6460 execv(bb_busybox_exec_path, argv);
6297 /* If they called chroot or otherwise made the binary no longer 6461 /* If they called chroot or otherwise made the binary no longer
6298 * executable, fall through */ 6462 * executable, fall through */
@@ -6991,9 +7155,6 @@ static NOINLINE int run_pipe(struct pipe *pi)
6991 if (setup_redirects(command, NULL)) 7155 if (setup_redirects(command, NULL))
6992 _exit(1); 7156 _exit(1);
6993 7157
6994 /* Restore default handlers just prior to exec */
6995 /*signal(SIGCHLD, SIG_DFL); - so far we don't have any handlers */
6996
6997 /* Stores to nommu_save list of env vars putenv'ed 7158 /* Stores to nommu_save list of env vars putenv'ed
6998 * (NOMMU, on MMU we don't need that) */ 7159 * (NOMMU, on MMU we don't need that) */
6999 /* cast away volatility... */ 7160 /* cast away volatility... */
@@ -7271,7 +7432,7 @@ static int run_list(struct pipe *pi)
7271 * and we don't need to wait for anything. */ 7432 * and we don't need to wait for anything. */
7272 G.last_exitcode = rcode; 7433 G.last_exitcode = rcode;
7273 debug_printf_exec(": builtin/func exitcode %d\n", rcode); 7434 debug_printf_exec(": builtin/func exitcode %d\n", rcode);
7274 check_and_run_traps(0); 7435 check_and_run_traps();
7275#if ENABLE_HUSH_LOOPS 7436#if ENABLE_HUSH_LOOPS
7276 /* Was it "break" or "continue"? */ 7437 /* Was it "break" or "continue"? */
7277 if (G.flag_break_continue) { 7438 if (G.flag_break_continue) {
@@ -7303,7 +7464,7 @@ static int run_list(struct pipe *pi)
7303 /* even bash 3.2 doesn't do that well with nested bg: 7464 /* even bash 3.2 doesn't do that well with nested bg:
7304 * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". 7465 * try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
7305 * I'm NOT treating inner &'s as jobs */ 7466 * I'm NOT treating inner &'s as jobs */
7306 check_and_run_traps(0); 7467 check_and_run_traps();
7307#if ENABLE_HUSH_JOB 7468#if ENABLE_HUSH_JOB
7308 if (G.run_list_level == 1) 7469 if (G.run_list_level == 1)
7309 insert_bg_job(pi); 7470 insert_bg_job(pi);
@@ -7318,13 +7479,13 @@ static int run_list(struct pipe *pi)
7318 /* Waits for completion, then fg's main shell */ 7479 /* Waits for completion, then fg's main shell */
7319 rcode = checkjobs_and_fg_shell(pi); 7480 rcode = checkjobs_and_fg_shell(pi);
7320 debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode); 7481 debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode);
7321 check_and_run_traps(0); 7482 check_and_run_traps();
7322 } else 7483 } else
7323#endif 7484#endif
7324 { /* This one just waits for completion */ 7485 { /* This one just waits for completion */
7325 rcode = checkjobs(pi); 7486 rcode = checkjobs(pi);
7326 debug_printf_exec(": checkjobs exitcode %d\n", rcode); 7487 debug_printf_exec(": checkjobs exitcode %d\n", rcode);
7327 check_and_run_traps(0); 7488 check_and_run_traps();
7328 } 7489 }
7329 G.last_exitcode = rcode; 7490 G.last_exitcode = rcode;
7330 } 7491 }
@@ -7337,7 +7498,10 @@ static int run_list(struct pipe *pi)
7337#endif 7498#endif
7338#if ENABLE_HUSH_LOOPS 7499#if ENABLE_HUSH_LOOPS
7339 /* Beware of "while false; true; do ..."! */ 7500 /* Beware of "while false; true; do ..."! */
7340 if (pi->next && pi->next->res_word == RES_DO) { 7501 if (pi->next
7502 && (pi->next->res_word == RES_DO || pi->next->res_word == RES_DONE)
7503 /* check for RES_DONE is needed for "while ...; do \n done" case */
7504 ) {
7341 if (rword == RES_WHILE) { 7505 if (rword == RES_WHILE) {
7342 if (rcode) { 7506 if (rcode) {
7343 /* "while false; do...done" - exitcode 0 */ 7507 /* "while false; do...done" - exitcode 0 */
@@ -7395,88 +7559,81 @@ static int run_and_free_list(struct pipe *pi)
7395} 7559}
7396 7560
7397 7561
7562static void install_sighandlers(unsigned mask)
7563{
7564 sighandler_t old_handler;
7565 unsigned sig = 0;
7566 while ((mask >>= 1) != 0) {
7567 sig++;
7568 if (!(mask & 1))
7569 continue;
7570 old_handler = install_sighandler(sig, pick_sighandler(sig));
7571 /* POSIX allows shell to re-enable SIGCHLD
7572 * even if it was SIG_IGN on entry.
7573 * Therefore we skip IGN check for it:
7574 */
7575 if (sig == SIGCHLD)
7576 continue;
7577 if (old_handler == SIG_IGN) {
7578 /* oops... restore back to IGN, and record this fact */
7579 install_sighandler(sig, old_handler);
7580 if (!G.traps)
7581 G.traps = xzalloc(sizeof(G.traps[0]) * NSIG);
7582 free(G.traps[sig]);
7583 G.traps[sig] = xzalloc(1); /* == xstrdup(""); */
7584 }
7585 }
7586}
7587
7398/* Called a few times only (or even once if "sh -c") */ 7588/* Called a few times only (or even once if "sh -c") */
7399static void init_sigmasks(void) 7589static void install_special_sighandlers(void)
7400{ 7590{
7401 unsigned sig;
7402 unsigned mask; 7591 unsigned mask;
7403 sigset_t old_blocked_set;
7404
7405 if (!G.inherited_set_is_saved) {
7406 sigprocmask(SIG_SETMASK, NULL, &G.blocked_set);
7407 G.inherited_set = G.blocked_set;
7408 }
7409 old_blocked_set = G.blocked_set;
7410 7592
7411 mask = (1 << SIGQUIT); 7593 /* Which signals are shell-special? */
7594 mask = (1 << SIGQUIT) | (1 << SIGCHLD);
7412 if (G_interactive_fd) { 7595 if (G_interactive_fd) {
7413 mask = (1 << SIGQUIT) | SPECIAL_INTERACTIVE_SIGS; 7596 mask |= SPECIAL_INTERACTIVE_SIGS;
7414 if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */ 7597 if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */
7415 mask |= SPECIAL_JOB_SIGS; 7598 mask |= SPECIAL_JOBSTOP_SIGS;
7416 } 7599 }
7417 G.non_DFL_mask = mask; 7600 /* Careful, do not re-install handlers we already installed */
7418 7601 if (G.special_sig_mask != mask) {
7419 sig = 0; 7602 unsigned diff = mask & ~G.special_sig_mask;
7420 while (mask) { 7603 G.special_sig_mask = mask;
7421 if (mask & 1) 7604 install_sighandlers(diff);
7422 sigaddset(&G.blocked_set, sig);
7423 mask >>= 1;
7424 sig++;
7425 } 7605 }
7426 sigdelset(&G.blocked_set, SIGCHLD);
7427
7428 if (memcmp(&old_blocked_set, &G.blocked_set, sizeof(old_blocked_set)) != 0)
7429 sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
7430
7431 /* POSIX allows shell to re-enable SIGCHLD
7432 * even if it was SIG_IGN on entry */
7433#if ENABLE_HUSH_FAST
7434 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
7435 if (!G.inherited_set_is_saved)
7436 signal(SIGCHLD, SIGCHLD_handler);
7437#else
7438 if (!G.inherited_set_is_saved)
7439 signal(SIGCHLD, SIG_DFL);
7440#endif
7441
7442 G.inherited_set_is_saved = 1;
7443} 7606}
7444 7607
7445#if ENABLE_HUSH_JOB 7608#if ENABLE_HUSH_JOB
7446/* helper */ 7609/* helper */
7447static void maybe_set_to_sigexit(int sig)
7448{
7449 void (*handler)(int);
7450 /* non_DFL_mask'ed signals are, well, masked,
7451 * no need to set handler for them.
7452 */
7453 if (!((G.non_DFL_mask >> sig) & 1)) {
7454 handler = signal(sig, sigexit);
7455 if (handler == SIG_IGN) /* oops... restore back to IGN! */
7456 signal(sig, handler);
7457 }
7458}
7459/* Set handlers to restore tty pgrp and exit */ 7610/* Set handlers to restore tty pgrp and exit */
7460static void set_fatal_handlers(void) 7611static void install_fatal_sighandlers(void)
7461{ 7612{
7462 /* We _must_ restore tty pgrp on fatal signals */ 7613 unsigned mask;
7463 if (HUSH_DEBUG) { 7614
7464 maybe_set_to_sigexit(SIGILL ); 7615 /* We will restore tty pgrp on these signals */
7465 maybe_set_to_sigexit(SIGFPE ); 7616 mask = 0
7466 maybe_set_to_sigexit(SIGBUS ); 7617 + (1 << SIGILL ) * HUSH_DEBUG
7467 maybe_set_to_sigexit(SIGSEGV); 7618 + (1 << SIGFPE ) * HUSH_DEBUG
7468 maybe_set_to_sigexit(SIGTRAP); 7619 + (1 << SIGBUS ) * HUSH_DEBUG
7469 } /* else: hush is perfect. what SEGV? */ 7620 + (1 << SIGSEGV) * HUSH_DEBUG
7470 maybe_set_to_sigexit(SIGABRT); 7621 + (1 << SIGTRAP) * HUSH_DEBUG
7622 + (1 << SIGABRT)
7471 /* bash 3.2 seems to handle these just like 'fatal' ones */ 7623 /* bash 3.2 seems to handle these just like 'fatal' ones */
7472 maybe_set_to_sigexit(SIGPIPE); 7624 + (1 << SIGPIPE)
7473 maybe_set_to_sigexit(SIGALRM); 7625 + (1 << SIGALRM)
7474 /* if we are interactive, SIGHUP, SIGTERM and SIGINT are masked. 7626 /* if we are interactive, SIGHUP, SIGTERM and SIGINT are special sigs.
7475 * if we aren't interactive... but in this case 7627 * if we aren't interactive... but in this case
7476 * we never want to restore pgrp on exit, and this fn is not called */ 7628 * we never want to restore pgrp on exit, and this fn is not called
7477 /*maybe_set_to_sigexit(SIGHUP );*/ 7629 */
7478 /*maybe_set_to_sigexit(SIGTERM);*/ 7630 /*+ (1 << SIGHUP )*/
7479 /*maybe_set_to_sigexit(SIGINT );*/ 7631 /*+ (1 << SIGTERM)*/
7632 /*+ (1 << SIGINT )*/
7633 ;
7634 G_fatal_sig_mask = mask;
7635
7636 install_sighandlers(mask);
7480} 7637}
7481#endif 7638#endif
7482 7639
@@ -7522,6 +7679,10 @@ static int set_mode(int state, char mode, const char *o_opt)
7522int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 7679int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
7523int hush_main(int argc, char **argv) 7680int hush_main(int argc, char **argv)
7524{ 7681{
7682 enum {
7683 OPT_login = (1 << 0),
7684 };
7685 unsigned flags;
7525 int opt; 7686 int opt;
7526 unsigned builtin_argc; 7687 unsigned builtin_argc;
7527 char **e; 7688 char **e;
@@ -7529,8 +7690,11 @@ int hush_main(int argc, char **argv)
7529 struct variable *shell_ver; 7690 struct variable *shell_ver;
7530 7691
7531 INIT_G(); 7692 INIT_G();
7532 if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, it is already done */ 7693 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
7533 G.last_exitcode = EXIT_SUCCESS; 7694 G.last_exitcode = EXIT_SUCCESS;
7695#if ENABLE_HUSH_FAST
7696 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
7697#endif
7534#if !BB_MMU 7698#if !BB_MMU
7535 G.argv0_for_re_execing = argv[0]; 7699 G.argv0_for_re_execing = argv[0];
7536#endif 7700#endif
@@ -7622,8 +7786,6 @@ int hush_main(int argc, char **argv)
7622# endif 7786# endif
7623#endif 7787#endif
7624 7788
7625 G.global_argc = argc;
7626 G.global_argv = argv;
7627 /* Initialize some more globals to non-zero values */ 7789 /* Initialize some more globals to non-zero values */
7628 cmdedit_update_prompt(); 7790 cmdedit_update_prompt();
7629 7791
@@ -7635,17 +7797,18 @@ int hush_main(int argc, char **argv)
7635 } 7797 }
7636 7798
7637 /* Shell is non-interactive at first. We need to call 7799 /* Shell is non-interactive at first. We need to call
7638 * init_sigmasks() if we are going to execute "sh <script>", 7800 * install_special_sighandlers() if we are going to execute "sh <script>",
7639 * "sh -c <cmds>" or login shell's /etc/profile and friends. 7801 * "sh -c <cmds>" or login shell's /etc/profile and friends.
7640 * If we later decide that we are interactive, we run init_sigmasks() 7802 * If we later decide that we are interactive, we run install_special_sighandlers()
7641 * in order to intercept (more) signals. 7803 * in order to intercept (more) signals.
7642 */ 7804 */
7643 7805
7644 /* Parse options */ 7806 /* Parse options */
7645 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ 7807 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
7808 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
7646 builtin_argc = 0; 7809 builtin_argc = 0;
7647 while (1) { 7810 while (1) {
7648 opt = getopt(argc, argv, "+c:xins" 7811 opt = getopt(argc, argv, "+c:xinsl"
7649#if !BB_MMU 7812#if !BB_MMU
7650 "<:$:R:V:" 7813 "<:$:R:V:"
7651# if ENABLE_HUSH_FUNCTIONS 7814# if ENABLE_HUSH_FUNCTIONS
@@ -7677,7 +7840,7 @@ int hush_main(int argc, char **argv)
7677 /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */ 7840 /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */
7678 const struct built_in_command *x; 7841 const struct built_in_command *x;
7679 7842
7680 init_sigmasks(); 7843 install_special_sighandlers();
7681 x = find_builtin(optarg); 7844 x = find_builtin(optarg);
7682 if (x) { /* paranoia */ 7845 if (x) { /* paranoia */
7683 G.global_argc -= builtin_argc; /* skip [BARGV...] "" */ 7846 G.global_argc -= builtin_argc; /* skip [BARGV...] "" */
@@ -7694,7 +7857,7 @@ int hush_main(int argc, char **argv)
7694 G.global_argv[0] = argv[0]; 7857 G.global_argv[0] = argv[0];
7695 G.global_argc++; 7858 G.global_argc++;
7696 } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */ 7859 } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */
7697 init_sigmasks(); 7860 install_special_sighandlers();
7698 parse_and_run_string(optarg); 7861 parse_and_run_string(optarg);
7699 goto final_return; 7862 goto final_return;
7700 case 'i': 7863 case 'i':
@@ -7706,6 +7869,9 @@ int hush_main(int argc, char **argv)
7706 /* "-s" means "read from stdin", but this is how we always 7869 /* "-s" means "read from stdin", but this is how we always
7707 * operate, so simply do nothing here. */ 7870 * operate, so simply do nothing here. */
7708 break; 7871 break;
7872 case 'l':
7873 flags |= OPT_login;
7874 break;
7709#if !BB_MMU 7875#if !BB_MMU
7710 case '<': /* "big heredoc" support */ 7876 case '<': /* "big heredoc" support */
7711 full_write1_str(optarg); 7877 full_write1_str(optarg);
@@ -7726,15 +7892,14 @@ int hush_main(int argc, char **argv)
7726 empty_trap_mask = bb_strtoull(optarg, &optarg, 16); 7892 empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
7727 if (empty_trap_mask != 0) { 7893 if (empty_trap_mask != 0) {
7728 int sig; 7894 int sig;
7729 init_sigmasks(); 7895 install_special_sighandlers();
7730 G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); 7896 G.traps = xzalloc(sizeof(G.traps[0]) * NSIG);
7731 for (sig = 1; sig < NSIG; sig++) { 7897 for (sig = 1; sig < NSIG; sig++) {
7732 if (empty_trap_mask & (1LL << sig)) { 7898 if (empty_trap_mask & (1LL << sig)) {
7733 G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ 7899 G.traps[sig] = xzalloc(1); /* == xstrdup(""); */
7734 sigaddset(&G.blocked_set, sig); 7900 install_sighandler(sig, SIG_IGN);
7735 } 7901 }
7736 } 7902 }
7737 sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
7738 } 7903 }
7739# if ENABLE_HUSH_LOOPS 7904# if ENABLE_HUSH_LOOPS
7740 optarg++; 7905 optarg++;
@@ -7772,19 +7937,24 @@ int hush_main(int argc, char **argv)
7772 } 7937 }
7773 } /* option parsing loop */ 7938 } /* option parsing loop */
7774 7939
7940 /* Skip options. Try "hush -l": $1 should not be "-l"! */
7941 G.global_argc = argc - (optind - 1);
7942 G.global_argv = argv + (optind - 1);
7943 G.global_argv[0] = argv[0];
7944
7775 if (!G.root_pid) { 7945 if (!G.root_pid) {
7776 G.root_pid = getpid(); 7946 G.root_pid = getpid();
7777 G.root_ppid = getppid(); 7947 G.root_ppid = getppid();
7778 } 7948 }
7779 7949
7780 /* If we are login shell... */ 7950 /* If we are login shell... */
7781 if (argv[0] && argv[0][0] == '-') { 7951 if (flags & OPT_login) {
7782 FILE *input; 7952 FILE *input;
7783 debug_printf("sourcing /etc/profile\n"); 7953 debug_printf("sourcing /etc/profile\n");
7784 input = fopen_for_read("/etc/profile"); 7954 input = fopen_for_read("/etc/profile");
7785 if (input != NULL) { 7955 if (input != NULL) {
7786 close_on_exec_on(fileno(input)); 7956 close_on_exec_on(fileno(input));
7787 init_sigmasks(); 7957 install_special_sighandlers();
7788 parse_and_run_file(input); 7958 parse_and_run_file(input);
7789 fclose(input); 7959 fclose(input);
7790 } 7960 }
@@ -7797,19 +7967,19 @@ int hush_main(int argc, char **argv)
7797 */ 7967 */
7798 } 7968 }
7799 7969
7800 if (argv[optind]) { 7970 if (G.global_argv[1]) {
7801 FILE *input; 7971 FILE *input;
7802 /* 7972 /*
7803 * "bash <script>" (which is never interactive (unless -i?)) 7973 * "bash <script>" (which is never interactive (unless -i?))
7804 * sources $BASH_ENV here (without scanning $PATH). 7974 * sources $BASH_ENV here (without scanning $PATH).
7805 * If called as sh, does the same but with $ENV. 7975 * If called as sh, does the same but with $ENV.
7806 */ 7976 */
7807 debug_printf("running script '%s'\n", argv[optind]); 7977 G.global_argc--;
7808 G.global_argv = argv + optind; 7978 G.global_argv++;
7809 G.global_argc = argc - optind; 7979 debug_printf("running script '%s'\n", G.global_argv[0]);
7810 input = xfopen_for_read(argv[optind]); 7980 input = xfopen_for_read(G.global_argv[0]);
7811 close_on_exec_on(fileno(input)); 7981 close_on_exec_on(fileno(input));
7812 init_sigmasks(); 7982 install_special_sighandlers();
7813 parse_and_run_file(input); 7983 parse_and_run_file(input);
7814#if ENABLE_FEATURE_CLEAN_UP 7984#if ENABLE_FEATURE_CLEAN_UP
7815 fclose(input); 7985 fclose(input);
@@ -7818,7 +7988,7 @@ int hush_main(int argc, char **argv)
7818 } 7988 }
7819 7989
7820 /* Up to here, shell was non-interactive. Now it may become one. 7990 /* Up to here, shell was non-interactive. Now it may become one.
7821 * NB: don't forget to (re)run init_sigmasks() as needed. 7991 * NB: don't forget to (re)run install_special_sighandlers() as needed.
7822 */ 7992 */
7823 7993
7824 /* A shell is interactive if the '-i' flag was given, 7994 /* A shell is interactive if the '-i' flag was given,
@@ -7870,12 +8040,12 @@ int hush_main(int argc, char **argv)
7870 } 8040 }
7871 } 8041 }
7872 8042
7873 /* Block some signals */ 8043 /* Install more signal handlers */
7874 init_sigmasks(); 8044 install_special_sighandlers();
7875 8045
7876 if (G_saved_tty_pgrp) { 8046 if (G_saved_tty_pgrp) {
7877 /* Set other signals to restore saved_tty_pgrp */ 8047 /* Set other signals to restore saved_tty_pgrp */
7878 set_fatal_handlers(); 8048 install_fatal_sighandlers();
7879 /* Put ourselves in our own process group 8049 /* Put ourselves in our own process group
7880 * (bash, too, does this only if ctty is available) */ 8050 * (bash, too, does this only if ctty is available) */
7881 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ 8051 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
@@ -7886,7 +8056,7 @@ int hush_main(int argc, char **argv)
7886 * (we reset die_sleep = 0 whereever we [v]fork) */ 8056 * (we reset die_sleep = 0 whereever we [v]fork) */
7887 enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */ 8057 enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */
7888 } else { 8058 } else {
7889 init_sigmasks(); 8059 install_special_sighandlers();
7890 } 8060 }
7891#elif ENABLE_HUSH_INTERACTIVE 8061#elif ENABLE_HUSH_INTERACTIVE
7892 /* No job control compiled in, only prompt/line editing */ 8062 /* No job control compiled in, only prompt/line editing */
@@ -7903,10 +8073,10 @@ int hush_main(int argc, char **argv)
7903 if (G_interactive_fd) { 8073 if (G_interactive_fd) {
7904 close_on_exec_on(G_interactive_fd); 8074 close_on_exec_on(G_interactive_fd);
7905 } 8075 }
7906 init_sigmasks(); 8076 install_special_sighandlers();
7907#else 8077#else
7908 /* We have interactiveness code disabled */ 8078 /* We have interactiveness code disabled */
7909 init_sigmasks(); 8079 install_special_sighandlers();
7910#endif 8080#endif
7911 /* bash: 8081 /* bash:
7912 * if interactive but not a login shell, sources ~/.bashrc 8082 * if interactive but not a login shell, sources ~/.bashrc
@@ -8040,7 +8210,7 @@ static int FAST_FUNC builtin_exec(char **argv)
8040 tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); 8210 tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
8041 8211
8042 /* TODO: if exec fails, bash does NOT exit! We do. 8212 /* TODO: if exec fails, bash does NOT exit! We do.
8043 * We'll need to undo sigprocmask (it's inside execvp_or_die) 8213 * We'll need to undo trap cleanup (it's inside execvp_or_die)
8044 * and tcsetpgrp, and this is inherently racy. 8214 * and tcsetpgrp, and this is inherently racy.
8045 */ 8215 */
8046 execvp_or_die(argv); 8216 execvp_or_die(argv);
@@ -8237,6 +8407,8 @@ static int FAST_FUNC builtin_trap(char **argv)
8237 process_sig_list: 8407 process_sig_list:
8238 ret = EXIT_SUCCESS; 8408 ret = EXIT_SUCCESS;
8239 while (*argv) { 8409 while (*argv) {
8410 sighandler_t handler;
8411
8240 sig = get_signum(*argv++); 8412 sig = get_signum(*argv++);
8241 if (sig < 0 || sig >= NSIG) { 8413 if (sig < 0 || sig >= NSIG) {
8242 ret = EXIT_FAILURE; 8414 ret = EXIT_FAILURE;
@@ -8255,18 +8427,13 @@ static int FAST_FUNC builtin_trap(char **argv)
8255 if (sig == 0) 8427 if (sig == 0)
8256 continue; 8428 continue;
8257 8429
8258 if (new_cmd) { 8430 if (new_cmd)
8259 sigaddset(&G.blocked_set, sig); 8431 handler = (new_cmd[0] ? record_pending_signo : SIG_IGN);
8260 } else { 8432 else
8261 /* There was a trap handler, we are removing it 8433 /* We are removing trap handler */
8262 * (if sig has non-DFL handling, 8434 handler = pick_sighandler(sig);
8263 * we don't need to do anything) */ 8435 install_sighandler(sig, handler);
8264 if (sig < 32 && (G.non_DFL_mask & (1 << sig)))
8265 continue;
8266 sigdelset(&G.blocked_set, sig);
8267 }
8268 } 8436 }
8269 sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
8270 return ret; 8437 return ret;
8271 } 8438 }
8272 8439
@@ -8467,6 +8634,27 @@ static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
8467 return EXIT_SUCCESS; 8634 return EXIT_SUCCESS;
8468} 8635}
8469 8636
8637/* Interruptibility of read builtin in bash
8638 * (tested on bash-4.2.8 by sending signals (not by ^C)):
8639 *
8640 * Empty trap makes read ignore corresponding signal, for any signal.
8641 *
8642 * SIGINT:
8643 * - terminates non-interactive shell;
8644 * - interrupts read in interactive shell;
8645 * if it has non-empty trap:
8646 * - executes trap and returns to command prompt in interactive shell;
8647 * - executes trap and returns to read in non-interactive shell;
8648 * SIGTERM:
8649 * - is ignored (does not interrupt) read in interactive shell;
8650 * - terminates non-interactive shell;
8651 * if it has non-empty trap:
8652 * - executes trap and returns to read;
8653 * SIGHUP:
8654 * - terminates shell (regardless of interactivity);
8655 * if it has non-empty trap:
8656 * - executes trap and returns to read;
8657 */
8470static int FAST_FUNC builtin_read(char **argv) 8658static int FAST_FUNC builtin_read(char **argv)
8471{ 8659{
8472 const char *r; 8660 const char *r;
@@ -8474,6 +8662,7 @@ static int FAST_FUNC builtin_read(char **argv)
8474 char *opt_p = NULL; 8662 char *opt_p = NULL;
8475 char *opt_t = NULL; 8663 char *opt_t = NULL;
8476 char *opt_u = NULL; 8664 char *opt_u = NULL;
8665 const char *ifs;
8477 int read_flags; 8666 int read_flags;
8478 8667
8479 /* "!": do not abort on errors. 8668 /* "!": do not abort on errors.
@@ -8483,10 +8672,12 @@ static int FAST_FUNC builtin_read(char **argv)
8483 if (read_flags == (uint32_t)-1) 8672 if (read_flags == (uint32_t)-1)
8484 return EXIT_FAILURE; 8673 return EXIT_FAILURE;
8485 argv += optind; 8674 argv += optind;
8675 ifs = get_local_var_value("IFS"); /* can be NULL */
8486 8676
8677 again:
8487 r = shell_builtin_read(set_local_var_from_halves, 8678 r = shell_builtin_read(set_local_var_from_halves,
8488 argv, 8679 argv,
8489 get_local_var_value("IFS"), /* can be NULL */ 8680 ifs,
8490 read_flags, 8681 read_flags,
8491 opt_n, 8682 opt_n,
8492 opt_p, 8683 opt_p,
@@ -8494,6 +8685,12 @@ static int FAST_FUNC builtin_read(char **argv)
8494 opt_u 8685 opt_u
8495 ); 8686 );
8496 8687
8688 if ((uintptr_t)r == 1 && errno == EINTR) {
8689 unsigned sig = check_and_run_traps();
8690 if (sig && sig != SIGINT)
8691 goto again;
8692 }
8693
8497 if ((uintptr_t)r > 1) { 8694 if ((uintptr_t)r > 1) {
8498 bb_error_msg("%s", r); 8695 bb_error_msg("%s", r);
8499 r = (char*)(uintptr_t)1; 8696 r = (char*)(uintptr_t)1;
@@ -8726,7 +8923,7 @@ static int FAST_FUNC builtin_unset(char **argv)
8726static int FAST_FUNC builtin_wait(char **argv) 8923static int FAST_FUNC builtin_wait(char **argv)
8727{ 8924{
8728 int ret = EXIT_SUCCESS; 8925 int ret = EXIT_SUCCESS;
8729 int status, sig; 8926 int status;
8730 8927
8731 argv = skip_dash_dash(argv); 8928 argv = skip_dash_dash(argv);
8732 if (argv[0] == NULL) { 8929 if (argv[0] == NULL) {
@@ -8746,25 +8943,53 @@ static int FAST_FUNC builtin_wait(char **argv)
8746 * ^C <-- after ~4 sec from keyboard 8943 * ^C <-- after ~4 sec from keyboard
8747 * $ 8944 * $
8748 */ 8945 */
8749 sigaddset(&G.blocked_set, SIGCHLD);
8750 sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
8751 while (1) { 8946 while (1) {
8752 checkjobs(NULL); 8947 int sig;
8753 if (errno == ECHILD) 8948 sigset_t oldset, allsigs;
8949
8950 /* waitpid is not interruptible by SA_RESTARTed
8951 * signals which we use. Thus, this ugly dance:
8952 */
8953
8954 /* Make sure possible SIGCHLD is stored in kernel's
8955 * pending signal mask before we call waitpid.
8956 * Or else we may race with SIGCHLD, lose it,
8957 * and get stuck in sigwaitinfo...
8958 */
8959 sigfillset(&allsigs);
8960 sigprocmask(SIG_SETMASK, &allsigs, &oldset);
8961
8962 if (!sigisemptyset(&G.pending_set)) {
8963 /* Crap! we raced with some signal! */
8964 // sig = 0;
8965 goto restore;
8966 }
8967
8968 checkjobs(NULL); /* waitpid(WNOHANG) inside */
8969 if (errno == ECHILD) {
8970 sigprocmask(SIG_SETMASK, &oldset, NULL);
8971 break;
8972 }
8973
8974 /* Wait for SIGCHLD or any other signal */
8975 //sig = sigwaitinfo(&allsigs, NULL);
8976 /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */
8977 /* Note: sigsuspend invokes signal handler */
8978 sigsuspend(&oldset);
8979 restore:
8980 sigprocmask(SIG_SETMASK, &oldset, NULL);
8981
8982 /* So, did we get a signal? */
8983 //if (sig > 0)
8984 // raise(sig); /* run handler */
8985 sig = check_and_run_traps();
8986 if (sig /*&& sig != SIGCHLD - always true */) {
8987 /* see note 2 */
8988 ret = 128 + sig;
8754 break; 8989 break;
8755 /* Wait for SIGCHLD or any other signal of interest */
8756 /* sigtimedwait with infinite timeout: */
8757 sig = sigwaitinfo(&G.blocked_set, NULL);
8758 if (sig > 0) {
8759 sig = check_and_run_traps(sig);
8760 if (sig && sig != SIGCHLD) { /* see note 2 */
8761 ret = 128 + sig;
8762 break;
8763 }
8764 } 8990 }
8991 /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */
8765 } 8992 }
8766 sigdelset(&G.blocked_set, SIGCHLD);
8767 sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
8768 return ret; 8993 return ret;
8769 } 8994 }
8770 8995
diff --git a/shell/hush_test/hush-misc/assignment4.right b/shell/hush_test/hush-misc/assignment4.right
new file mode 100644
index 000000000..31c896f62
--- /dev/null
+++ b/shell/hush_test/hush-misc/assignment4.right
@@ -0,0 +1 @@
Done:0
diff --git a/shell/hush_test/hush-misc/assignment4.tests b/shell/hush_test/hush-misc/assignment4.tests
new file mode 100755
index 000000000..6f46d0a33
--- /dev/null
+++ b/shell/hush_test/hush-misc/assignment4.tests
@@ -0,0 +1,3 @@
1# There was a bug where we misinterpreted assignments after 'do':
2for i in 1; do eval b=; done
3echo Done:$?
diff --git a/shell/hush_test/hush-misc/while3.right b/shell/hush_test/hush-misc/while3.right
new file mode 100644
index 000000000..7c4d7beb0
--- /dev/null
+++ b/shell/hush_test/hush-misc/while3.right
@@ -0,0 +1 @@
OK:0
diff --git a/shell/hush_test/hush-misc/while3.tests b/shell/hush_test/hush-misc/while3.tests
new file mode 100755
index 000000000..9132b5f4d
--- /dev/null
+++ b/shell/hush_test/hush-misc/while3.tests
@@ -0,0 +1,4 @@
1while false; do
2 # bash will require at least ":" here...
3done
4echo OK:$?
diff --git a/shell/hush_test/hush-trap/signal_read1.right b/shell/hush_test/hush-trap/signal_read1.right
new file mode 100644
index 000000000..2870a8e70
--- /dev/null
+++ b/shell/hush_test/hush-trap/signal_read1.right
@@ -0,0 +1 @@
Got HUP:0
diff --git a/shell/hush_test/hush-trap/signal_read1.tests b/shell/hush_test/hush-trap/signal_read1.tests
new file mode 100755
index 000000000..1105479a3
--- /dev/null
+++ b/shell/hush_test/hush-trap/signal_read1.tests
@@ -0,0 +1,5 @@
1(sleep 1; kill -HUP $$) &
2trap 'echo "Got HUP:$?"; exit' HUP
3while true; do
4 read ignored
5done
diff --git a/shell/hush_test/hush-trap/signal_read2.right b/shell/hush_test/hush-trap/signal_read2.right
new file mode 100644
index 000000000..71a6bc16d
--- /dev/null
+++ b/shell/hush_test/hush-trap/signal_read2.right
@@ -0,0 +1,2 @@
1HUP
2Done:129
diff --git a/shell/hush_test/hush-trap/signal_read2.tests b/shell/hush_test/hush-trap/signal_read2.tests
new file mode 100755
index 000000000..eab5b9b5b
--- /dev/null
+++ b/shell/hush_test/hush-trap/signal_read2.tests
@@ -0,0 +1,7 @@
1$THIS_SH -c '
2(sleep 1; kill -HUP $$) &
3while true; do
4 read ignored
5done
6'
7echo "Done:$?"
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all
index 256f152dc..64a7abc47 100755
--- a/shell/hush_test/run-all
+++ b/shell/hush_test/run-all
@@ -48,8 +48,9 @@ do_test()
48 *.orig|*~) ;; 48 *.orig|*~) ;;
49 #*) echo $x ; sh $x ;; 49 #*) echo $x ; sh $x ;;
50 *) 50 *)
51 echo -n "$1/$x:"
51 sh "$x" >"../$1-$x.fail" 2>&1 && \ 52 sh "$x" >"../$1-$x.fail" 2>&1 && \
52 { echo "$1/$x: ok"; rm "../$1-$x.fail"; } || echo "$1/$x: fail"; 53 { { echo " ok"; rm "../$1-$x.fail"; } || echo " fail"; }
53 ;; 54 ;;
54 esac 55 esac
55 done 56 done
@@ -60,6 +61,7 @@ do_test()
60 name="${x%%.tests}" 61 name="${x%%.tests}"
61 test -f "$name.right" || continue 62 test -f "$name.right" || continue
62# echo Running test: "$x" 63# echo Running test: "$x"
64 echo -n "$1/$x:"
63 ( 65 (
64 "$THIS_SH" "./$x" >"$name.xx" 2>&1 66 "$THIS_SH" "./$x" >"$name.xx" 2>&1
65 # filter C library differences 67 # filter C library differences
@@ -70,9 +72,9 @@ do_test()
70 diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail" 72 diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail"
71 ) 73 )
72 case $? in 74 case $? in
73 0) echo "$1/$x: ok";; 75 0) echo " ok";;
74 77) echo "$1/$x: skip (feature disabled)";; 76 77) echo " skip (feature disabled)";;
75 *) echo "$1/$x: fail"; tret=1;; 77 *) echo " fail"; tret=1;;
76 esac 78 esac
77 done 79 done
78 exit ${tret} 80 exit ${tret}
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 75f4b3e54..4329ca05c 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -36,6 +36,10 @@ int FAST_FUNC is_well_formed_var_name(const char *s, char terminator)
36 36
37/* read builtin */ 37/* read builtin */
38 38
39/* Needs to be interruptible: shell mush handle traps and shell-special signals
40 * while inside read. To implement this, be sure to not loop on EINTR
41 * and return errno == EINTR reliably.
42 */
39//TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL" 43//TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL"
40//string. hush naturally has it, and ash has setvareq(). 44//string. hush naturally has it, and ash has setvareq().
41//Here we can simply store "VAR=" at buffer start and store read data directly 45//Here we can simply store "VAR=" at buffer start and store read data directly
@@ -51,6 +55,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
51 const char *opt_u 55 const char *opt_u
52) 56)
53{ 57{
58 unsigned err;
54 unsigned end_ms; /* -t TIMEOUT */ 59 unsigned end_ms; /* -t TIMEOUT */
55 int fd; /* -u FD */ 60 int fd; /* -u FD */
56 int nchars; /* -n NUM */ 61 int nchars; /* -n NUM */
@@ -62,6 +67,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
62 int startword; 67 int startword;
63 smallint backslash; 68 smallint backslash;
64 69
70 errno = err = 0;
71
65 pp = argv; 72 pp = argv;
66 while (*pp) { 73 while (*pp) {
67 if (!is_well_formed_var_name(*pp, '\0')) { 74 if (!is_well_formed_var_name(*pp, '\0')) {
@@ -152,28 +159,40 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
152 bufpos = 0; 159 bufpos = 0;
153 do { 160 do {
154 char c; 161 char c;
162 struct pollfd pfd[1];
163 int timeout;
155 164
156 if (end_ms) { 165 if ((bufpos & 0xff) == 0)
157 int timeout; 166 buffer = xrealloc(buffer, bufpos + 0x100);
158 struct pollfd pfd[1];
159 167
160 pfd[0].fd = fd; 168 timeout = -1;
161 pfd[0].events = POLLIN; 169 if (end_ms) {
162 timeout = end_ms - (unsigned)monotonic_ms(); 170 timeout = end_ms - (unsigned)monotonic_ms();
163 if (timeout <= 0 /* already late? */ 171 if (timeout <= 0) { /* already late? */
164 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
165 ) { /* timed out! */
166 retval = (const char *)(uintptr_t)1; 172 retval = (const char *)(uintptr_t)1;
167 goto ret; 173 goto ret;
168 } 174 }
169 } 175 }
170 176
171 if ((bufpos & 0xff) == 0) 177 /* We must poll even if timeout is -1:
172 buffer = xrealloc(buffer, bufpos + 0x100); 178 * we want to be interrupted if signal arrives,
173 if (nonblock_safe_read(fd, &buffer[bufpos], 1) != 1) { 179 * regardless of SA_RESTART-ness of that signal!
180 */
181 errno = 0;
182 pfd[0].fd = fd;
183 pfd[0].events = POLLIN;
184 if (poll(pfd, 1, timeout) != 1) {
185 /* timed out, or EINTR */
186 err = errno;
187 retval = (const char *)(uintptr_t)1;
188 goto ret;
189 }
190 if (read(fd, &buffer[bufpos], 1) != 1) {
191 err = errno;
174 retval = (const char *)(uintptr_t)1; 192 retval = (const char *)(uintptr_t)1;
175 break; 193 break;
176 } 194 }
195
177 c = buffer[bufpos]; 196 c = buffer[bufpos];
178 if (c == '\0' || (ENABLE_PLATFORM_MINGW32 && c == '\r')) 197 if (c == '\0' || (ENABLE_PLATFORM_MINGW32 && c == '\r'))
179 continue; 198 continue;
@@ -240,6 +259,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
240 free(buffer); 259 free(buffer);
241 if (read_flags & BUILTIN_READ_SILENT) 260 if (read_flags & BUILTIN_READ_SILENT)
242 tcsetattr(fd, TCSANOW, &old_tty); 261 tcsetattr(fd, TCSANOW, &old_tty);
262
263 errno = err;
243 return retval; 264 return retval;
244} 265}
245 266
diff --git a/sysklogd/klogd.c b/sysklogd/klogd.c
index 3992081ca..27995e570 100644
--- a/sysklogd/klogd.c
+++ b/sysklogd/klogd.c
@@ -22,7 +22,7 @@
22//usage:#define klogd_full_usage "\n\n" 22//usage:#define klogd_full_usage "\n\n"
23//usage: "Kernel logger\n" 23//usage: "Kernel logger\n"
24//usage: "\nOptions:" 24//usage: "\nOptions:"
25//usage: "\n -c N Only messages with level < N are printed to console" 25//usage: "\n -c N Print to console messages more urgent than prio N (1-8)"
26//usage: "\n -n Run in foreground" 26//usage: "\n -n Run in foreground"
27 27
28#include "libbb.h" 28#include "libbb.h"
diff --git a/sysklogd/syslogd.c b/sysklogd/syslogd.c
index b6f409f41..c5c2a2e86 100644
--- a/sysklogd/syslogd.c
+++ b/sysklogd/syslogd.c
@@ -16,26 +16,33 @@
16//usage:#define syslogd_trivial_usage 16//usage:#define syslogd_trivial_usage
17//usage: "[OPTIONS]" 17//usage: "[OPTIONS]"
18//usage:#define syslogd_full_usage "\n\n" 18//usage:#define syslogd_full_usage "\n\n"
19//usage: "System logging utility.\n" 19//usage: "System logging utility\n"
20//usage: "This version of syslogd ignores /etc/syslog.conf\n" 20//usage: IF_NOT_FEATURE_SYSLOGD_CFG(
21//usage: "(this version of syslogd ignores /etc/syslog.conf)\n"
22//usage: )
21//usage: "\nOptions:" 23//usage: "\nOptions:"
22//usage: "\n -n Run in foreground" 24//usage: "\n -n Run in foreground"
23//usage: "\n -O FILE Log to given file (default:/var/log/messages)" 25//usage: "\n -O FILE Log to FILE (default:/var/log/messages)"
24//usage: "\n -l N Set local log level" 26//usage: "\n -l N Log only messages more urgent than prio N (1-8)"
25//usage: "\n -S Smaller logging output" 27//usage: "\n -S Smaller output"
26//usage: IF_FEATURE_ROTATE_LOGFILE( 28//usage: IF_FEATURE_ROTATE_LOGFILE(
27//usage: "\n -s SIZE Max size (KB) before rotate (default:200KB, 0=off)" 29//usage: "\n -s SIZE Max size (KB) before rotation (default:200KB, 0=off)"
28//usage: "\n -b N N rotated logs to keep (default:1, max=99, 0=purge)") 30//usage: "\n -b N N rotated logs to keep (default:1, max=99, 0=purge)"
31//usage: )
29//usage: IF_FEATURE_REMOTE_LOG( 32//usage: IF_FEATURE_REMOTE_LOG(
30//usage: "\n -R HOST[:PORT] Log to IP or hostname on PORT (default PORT=514/UDP)" 33//usage: "\n -R HOST[:PORT] Log to IP or hostname on PORT (default PORT=514/UDP)"
31//usage: "\n -L Log locally and via network (default is network only if -R)") 34//usage: "\n -L Log locally and via network (default is network only if -R)"
35//usage: )
32//usage: IF_FEATURE_SYSLOGD_DUP( 36//usage: IF_FEATURE_SYSLOGD_DUP(
33//usage: "\n -D Drop duplicates") 37//usage: "\n -D Drop duplicates"
38//usage: )
34//usage: IF_FEATURE_IPC_SYSLOG( 39//usage: IF_FEATURE_IPC_SYSLOG(
35//usage: "\n -C[size(KiB)] Log to shared mem buffer (read it using logread)")
36//usage: IF_FEATURE_SYSLOGD_CFG(
37//usage: "\n -f FILE Use FILE as config (default is /etc/syslog.conf)")
38/* NB: -Csize shouldn't have space (because size is optional) */ 40/* NB: -Csize shouldn't have space (because size is optional) */
41//usage: "\n -C[size_kb] Log to shared mem buffer (use logread to read it)"
42//usage: )
43//usage: IF_FEATURE_SYSLOGD_CFG(
44//usage: "\n -f FILE Use FILE as config (default:/etc/syslog.conf)"
45//usage: )
39/* //usage: "\n -m MIN Minutes between MARK lines (default:20, 0=off)" */ 46/* //usage: "\n -m MIN Minutes between MARK lines (default:20, 0=off)" */
40//usage: 47//usage:
41//usage:#define syslogd_example_usage 48//usage:#define syslogd_example_usage
diff --git a/testsuite/od.tests b/testsuite/od.tests
index 138efedea..fa47b4790 100755
--- a/testsuite/od.tests
+++ b/testsuite/od.tests
@@ -4,7 +4,7 @@
4 4
5. ./testing.sh 5. ./testing.sh
6 6
7# testing "test name" "options" "expected result" "file input" "stdin" 7# testing "test name" "commands" "expected result" "file input" "stdin"
8 8
9optional DESKTOP 9optional DESKTOP
10testing "od -b" \ 10testing "od -b" \
@@ -16,4 +16,24 @@ testing "od -b" \
16 "" "HELLO" 16 "" "HELLO"
17SKIP= 17SKIP=
18 18
19optional DESKTOP
20testing "od -b --traditional" \
21 "od -b --traditional" \
22"\
230000000 110 105 114 114 117
240000005
25" \
26 "" "HELLO"
27SKIP=
28
29optional DESKTOP
30testing "od -b --traditional FILE" \
31 "od -b --traditional input" \
32"\
330000000 110 105 114 114 117
340000005
35" \
36 "HELLO" ""
37SKIP=
38
19exit $FAILCOUNT 39exit $FAILCOUNT
diff --git a/testsuite/sed.tests b/testsuite/sed.tests
index 395372ae6..e9d0ed601 100755
--- a/testsuite/sed.tests
+++ b/testsuite/sed.tests
@@ -287,6 +287,9 @@ testing "sed -i with address modifies all files, not only first" \
287 "cp input input2; sed -i -e '1s/foo/bar/' input input2 && cat input input2; rm input2" \ 287 "cp input input2; sed -i -e '1s/foo/bar/' input input2 && cat input input2; rm input2" \
288 "bar\nbar\n" "foo\n" "" 288 "bar\nbar\n" "foo\n" ""
289 289
290testing "sed understands \r" \
291 "sed 's/r/\r/'" \
292 "\rrr\n" "" "rrr\n"
290 293
291# testing "description" "arguments" "result" "infile" "stdin" 294# testing "description" "arguments" "result" "infile" "stdin"
292 295
diff --git a/util-linux/acpid.c b/util-linux/acpid.c
index c9eed2a7f..4b7e5cacb 100644
--- a/util-linux/acpid.c
+++ b/util-linux/acpid.c
@@ -283,7 +283,7 @@ int acpid_main(int argc UNUSED_PARAM, char **argv)
283 char *buf; 283 char *buf;
284 int len; 284 int len;
285 285
286 buf = xmalloc_reads(pfd[i].fd, NULL, NULL); 286 buf = xmalloc_reads(pfd[i].fd, NULL);
287 /* buf = "button/power PWRB 00000080 00000000" */ 287 /* buf = "button/power PWRB 00000080 00000000" */
288 len = strlen(buf) - 9; 288 len = strlen(buf) - 9;
289 if (len >= 0) 289 if (len >= 0)
diff --git a/util-linux/fbset.c b/util-linux/fbset.c
index 75d41b882..3be342481 100644
--- a/util-linux/fbset.c
+++ b/util-linux/fbset.c
@@ -402,7 +402,14 @@ int fbset_main(int argc, char **argv)
402 argv++; 402 argv++;
403 argc--; 403 argc--;
404 for (; argc > 0 && (thisarg = *argv) != NULL; argc--, argv++) { 404 for (; argc > 0 && (thisarg = *argv) != NULL; argc--, argv++) {
405 if (thisarg[0] == '-') for (i = 0; i < ARRAY_SIZE(g_cmdoptions); i++) { 405 if (thisarg[0] != '-') {
406 if (!ENABLE_FEATURE_FBSET_READMODE || argc != 1)
407 bb_show_usage();
408 mode = thisarg;
409 options |= OPT_READMODE;
410 goto contin;
411 }
412 for (i = 0; i < ARRAY_SIZE(g_cmdoptions); i++) {
406 if (strcmp(thisarg + 1, g_cmdoptions[i].name) != 0) 413 if (strcmp(thisarg + 1, g_cmdoptions[i].name) != 0)
407 continue; 414 continue;
408 if (argc <= g_cmdoptions[i].param_count) 415 if (argc <= g_cmdoptions[i].param_count)
@@ -471,10 +478,7 @@ int fbset_main(int argc, char **argv)
471 argv += g_cmdoptions[i].param_count; 478 argv += g_cmdoptions[i].param_count;
472 goto contin; 479 goto contin;
473 } 480 }
474 if (!ENABLE_FEATURE_FBSET_READMODE || argc != 1) 481 bb_show_usage();
475 bb_show_usage();
476 mode = *argv;
477 options |= OPT_READMODE;
478 contin: ; 482 contin: ;
479 } 483 }
480 484
diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c
index da03e683e..f4fd4d31d 100644
--- a/util-linux/fdisk.c
+++ b/util-linux/fdisk.c
@@ -2846,13 +2846,37 @@ open_list_and_close(const char *device, int user_specified)
2846 close_dev_fd(); 2846 close_dev_fd();
2847} 2847}
2848 2848
2849/* Is it a whole disk? The digit check is still useful
2850 for Xen devices for example. */
2851static int is_whole_disk(const char *disk)
2852{
2853 unsigned len;
2854 int fd = open(disk, O_RDONLY);
2855
2856 if (fd != -1) {
2857 struct hd_geometry geometry;
2858 int err = ioctl(fd, HDIO_GETGEO, &geometry);
2859 close(fd);
2860 if (!err)
2861 return (geometry.start == 0);
2862 }
2863
2864 /* Treat "nameN" as a partition name, not whole disk */
2865 /* note: mmcblk0 should work from the geometry check above */
2866 len = strlen(disk);
2867 if (len != 0 && isdigit(disk[len - 1]))
2868 return 0;
2869
2870 return 1;
2871}
2872
2849/* for fdisk -l: try all things in /proc/partitions 2873/* for fdisk -l: try all things in /proc/partitions
2850 that look like a partition name (do not end in a digit) */ 2874 that look like a partition name (do not end in a digit) */
2851static void 2875static void
2852list_devs_in_proc_partititons(void) 2876list_devs_in_proc_partititons(void)
2853{ 2877{
2854 FILE *procpt; 2878 FILE *procpt;
2855 char line[100], ptname[100], devname[120], *s; 2879 char line[100], ptname[100], devname[120];
2856 int ma, mi, sz; 2880 int ma, mi, sz;
2857 2881
2858 procpt = fopen_or_warn("/proc/partitions", "r"); 2882 procpt = fopen_or_warn("/proc/partitions", "r");
@@ -2861,13 +2885,10 @@ list_devs_in_proc_partititons(void)
2861 if (sscanf(line, " %u %u %u %[^\n ]", 2885 if (sscanf(line, " %u %u %u %[^\n ]",
2862 &ma, &mi, &sz, ptname) != 4) 2886 &ma, &mi, &sz, ptname) != 4)
2863 continue; 2887 continue;
2864 for (s = ptname; *s; s++) 2888
2865 continue;
2866 /* note: excluding '0': e.g. mmcblk0 is not a partition name! */
2867 if (s[-1] >= '1' && s[-1] <= '9')
2868 continue;
2869 sprintf(devname, "/dev/%s", ptname); 2889 sprintf(devname, "/dev/%s", ptname);
2870 open_list_and_close(devname, 0); 2890 if (is_whole_disk(devname))
2891 open_list_and_close(devname, 0);
2871 } 2892 }
2872#if ENABLE_FEATURE_CLEAN_UP 2893#if ENABLE_FEATURE_CLEAN_UP
2873 fclose(procpt); 2894 fclose(procpt);
diff --git a/util-linux/getopt.c b/util-linux/getopt.c
index 10e1dc49b..85ff76189 100644
--- a/util-linux/getopt.c
+++ b/util-linux/getopt.c
@@ -25,7 +25,7 @@
25 * Added NLS support (partly written by Arkadiusz Mickiewicz 25 * Added NLS support (partly written by Arkadiusz Mickiewicz
26 * <misiek@misiek.eu.org>) 26 * <misiek@misiek.eu.org>)
27 * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org> 27 * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org>
28 * Removed --version/-V and --help/-h in 28 * Removed --version/-V and --help/-h
29 * Removed parse_error(), using bb_error_msg() from Busybox instead 29 * Removed parse_error(), using bb_error_msg() from Busybox instead
30 * Replaced our_malloc with xmalloc and our_realloc with xrealloc 30 * Replaced our_malloc with xmalloc and our_realloc with xrealloc
31 * 31 *
@@ -79,7 +79,9 @@
79//usage: " esac\n" 79//usage: " esac\n"
80//usage: "done\n" 80//usage: "done\n"
81 81
82#include <getopt.h> 82#if ENABLE_FEATURE_GETOPT_LONG
83# include <getopt.h>
84#endif
83#include "libbb.h" 85#include "libbb.h"
84 86
85/* NON_OPT is the code that is returned when a non-option is found in '+' 87/* NON_OPT is the code that is returned when a non-option is found in '+'
diff --git a/util-linux/mdev.c b/util-linux/mdev.c
index 2f225ac0b..7cabb1df6 100644
--- a/util-linux/mdev.c
+++ b/util-linux/mdev.c
@@ -292,7 +292,7 @@ static void make_device(char *path, int delete)
292 * the rest the line unless keep_matching == 1 */ 292 * the rest the line unless keep_matching == 1 */
293 293
294 /* 2nd field: uid:gid - device ownership */ 294 /* 2nd field: uid:gid - device ownership */
295 if (get_uidgid(&ugid, tokens[1], 1) == 0) 295 if (get_uidgid(&ugid, tokens[1], /*allow_numeric:*/ 1) == 0)
296 bb_error_msg("unknown user/group %s on line %d", tokens[1], parser->lineno); 296 bb_error_msg("unknown user/group %s on line %d", tokens[1], parser->lineno);
297 297
298 /* 3rd field: mode - device permissions */ 298 /* 3rd field: mode - device permissions */
diff --git a/util-linux/mount.c b/util-linux/mount.c
index 3e2ba1fab..6a154e2b2 100644
--- a/util-linux/mount.c
+++ b/util-linux/mount.c
@@ -1195,7 +1195,7 @@ static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
1195 noac = 0; 1195 noac = 0;
1196 nordirplus = 0; 1196 nordirplus = 0;
1197 retry = 10000; /* 10000 minutes ~ 1 week */ 1197 retry = 10000; /* 10000 minutes ~ 1 week */
1198 tcp = 0; 1198 tcp = 1; /* nfs-utils uses tcp per default */
1199 1199
1200 mountprog = MOUNTPROG; 1200 mountprog = MOUNTPROG;
1201 mountvers = 0; 1201 mountvers = 0;