Merge changes I8bf8a347,Ifd81d7f8,I5f6555d8,Ia7fdbaae,I05e5b66a, ...

* changes:
  Merge remote-tracking branch 'toybox/master' into HEAD
  Bash Compatibility Patch
  modinfo: various small fixes.
  split.test: don't rely on bash process substitution.
  file, stat: various small improvements.
  touch.test: add missing `TZ=utc`s.
diff --git a/generated/globals.h b/generated/globals.h
index ddec5f6..9f54f13 100644
--- a/generated/globals.h
+++ b/generated/globals.h
@@ -344,6 +344,7 @@
   char *F, *k, *b;
 
   long mod;
+  int count;
 };
 
 // toys/other/nsenter.c
diff --git a/generated/help.h b/generated/help.h
index 9ad97af..7466a97 100644
--- a/generated/help.h
+++ b/generated/help.h
@@ -212,7 +212,7 @@
 
 #define HELP_mountpoint "usage: mountpoint [-qd] DIR\n       mountpoint [-qx] DEVICE\n\nCheck whether the directory or device is a mountpoint.\n\n-q	Be quiet, return zero if directory is a mountpoint\n-d	Print major/minor device number of the directory\n-x	Print major/minor device number of the block device"
 
-#define HELP_modinfo "usage: modinfo [-0] [-b basedir] [-k kernrelease] [-F field] [modulename...]\n\nDisplay module fields for all specified modules, looking in\n<basedir>/lib/modules/<kernrelease>/ (kernrelease defaults to uname -r)."
+#define HELP_modinfo "usage: modinfo [-0] [-b basedir] [-k kernel] [-F field] [module|file...]\n\nDisplay module fields for modules specified by name or .ko path.\n\n-F  Only show the given field\n-0  Separate fields with NUL rather than newline\n-b  Use <basedir> as root for /lib/modules/\n-k  Look in given directory under /lib/modules/"
 
 #define HELP_mkswap "usage: mkswap [-L LABEL] DEVICE\n\nSet up a Linux swap area on a device or file."
 
diff --git a/scripts/make.sh b/scripts/make.sh
index 6649999..2a78844 100755
--- a/scripts/make.sh
+++ b/scripts/make.sh
@@ -152,7 +152,7 @@
     $KCONFIG_CONFIG > generated/config.h || exit 1
 fi
 
-if [ generated/mkflags -ot scripts/mkflags.c ]
+if [ ! -f generated/mkflags ] || [ generated/mkflags -ot scripts/mkflags.c ]
 then
   do_loudly $HOSTCC scripts/mkflags.c -o generated/mkflags || exit 1
 fi
@@ -236,7 +236,7 @@
   ) > generated/globals.h
 fi
 
-if [ generated/mktags -ot scripts/mktags.c ]
+if [ ! -f generated/mktags ] || [ generated/mktags -ot scripts/mktags.c ]
 then
   do_loudly $HOSTCC scripts/mktags.c -o generated/mktags || exit 1
 fi
@@ -249,7 +249,7 @@
     toys/*/*.c lib/*.c | generated/mktags > generated/tags.h
 fi
 
-if [ generated/config2help -ot scripts/config2help.c ]
+if [ ! -f generated/config2help ] || [ generated/config2help -ot scripts/config2help.c ]
 then
   do_loudly $HOSTCC scripts/config2help.c -o generated/config2help || exit 1
 fi
diff --git a/scripts/portability.sh b/scripts/portability.sh
index abeb31f..618022c 100644
--- a/scripts/portability.sh
+++ b/scripts/portability.sh
@@ -10,5 +10,5 @@
 
 if [ -z "$SED" ]
 then
-  [ ! -z "$(which gsed 2>/dev/null)" ] && SED=gsed || SED=sed
+  [ ! -z "$(command -v gsed 2>/dev/null)" ] && SED=gsed || SED=sed
 fi
diff --git a/tests/file.test b/tests/file.test
index fa8ea45..57204e6 100755
--- a/tests/file.test
+++ b/tests/file.test
@@ -11,6 +11,9 @@
 echo "Hello, world!" > ascii
 echo "6465780a3033350038ca8f6ce910f94e" | xxd -r -p > android.dex
 ln -s $FILES/java.class symlink
+LINK=$(readlink symlink)
+ln -s $FILES/java.klass dangler
+BROKEN=$(readlink dangler)
 
 testing "directory" "file ." ".: directory\n" "" ""
 testing "empty" "file empty" "empty: empty\n" "" ""
@@ -43,14 +46,15 @@
     "file $FILES/elf/ndk-elf-note-short | sed 's/^.*: //'" \
     "ELF shared object, 32-bit LSB arm, dynamic (/system/bin/linker), for Android 28, BuildID=da6a5f4ca8da163b9339326e626d8a3c, stripped\n" "" ""
 
-testing "symlink" "file symlink" "symlink: symbolic link\n" "" ""
-testing "symlink -h" "file -h symlink" "symlink: symbolic link\n" "" ""
+testing "broken symlink" "file dangler" "dangler: broken symbolic link to $BROKEN\n" "" ""
+testing "symlink" "file symlink" "symlink: symbolic link to $LINK\n" "" ""
+testing "symlink -h" "file -h symlink" "symlink: symbolic link to $LINK\n" "" ""
 testing "symlink -L" "file -L symlink" "symlink: Java class file, version 53.0 (Java 1.9)\n" "" ""
 
 testing "- pipe" "cat $FILES/java.class | file -" "-: Java class file, version 53.0 (Java 1.9)\n" "" ""
 testing "- redirect" "file - <$FILES/java.class" "-: Java class file, version 53.0 (Java 1.9)\n" "" ""
 
-testing "/dev/zero" "file /dev/zero" "/dev/zero: character special\n" "" ""
+testing "/dev/zero" "file /dev/zero" "/dev/zero: character special (1/5)\n" "" ""
 testing "- </dev/zero" "file - </dev/zero" "-: data\n" "" ""
 
 rm empty bash.script bash.script2 env.python.script ascii android.dex
diff --git a/tests/modinfo.test b/tests/modinfo.test
index 0a8c2be..f39dc6f 100644
--- a/tests/modinfo.test
+++ b/tests/modinfo.test
@@ -4,7 +4,23 @@
 
 #testing "name" "command" "result" "infile" "stdin"
 
-[ -e /proc/modules ] || { echo "Skipping test because modules are not supported"; exit 1; }
+testcmd "missing" "missing 2>&1" "modinfo: missing: not found\n" "" ""
+
+[ -e /proc/modules ] || { echo "Skipping: no /proc/modules"; exit 1; }
+
+# Android keeps its modules on the vendor partition.
+MODULE_ROOT=""
+[ -d /vendor/lib/modules ] && MODULE_ROOT="/vendor"
+
+# Find some modules to work with.
+MODULE_PATH1=$(find $MODULE_ROOT/lib/modules -name *.ko | head -1 2>/dev/null)
+MODULE1=$(basename -s .ko $MODULE_PATH1)
+MODULE_PATH2=$(find $MODULE_ROOT/lib/modules -name *.ko | tail -1 2>/dev/null)
+MODULE2=$(basename -s .ko $MODULE_PATH2)
+DASH_MODULE=$(basename -s .ko \
+  $(find $MODULE_ROOT/lib/modules -name *-*.ko | tail -1 2>/dev/null))
+BAR_MODULE=$(basename -s .ko \
+  $(find $MODULE_ROOT/lib/modules -name *_*.ko | tail -1 2>/dev/null))
 
 # modinfo does not need to output fields in a specified order.
 # Instead, there are labelled fields.  We can use sort to make up for this.
@@ -12,19 +28,19 @@
 # which change from kernel to kernel and can be disabled. 
 # We grep to remove these.
 
-#We expect they have ne2k-pci as a module.
-
-testing "gets right number of fields" "modinfo ne2k-pci |cut -d: -f1 |grep -v ver|sort" "alias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nauthor\ndepends\ndescription\nfilename\nlicense\nparm\nparm\nparm\n" "" ""
-testing "treats - and _ as equivalent" "modinfo ne2k_pci |cut -d: -f1 |grep -v ver|sort" "alias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nauthor\ndepends\ndescription\nfilename\nlicense\nparm\nparm\nparm\n" "" ""
+skipnot [ -n "$DASH_MODULE" ]
+testing "treats - and _ as equivalent" "modinfo $DASH_MODULE > dash-dash &&
+  modinfo ${DASH_MODULE/-/_} > dash-bar && diff -u dash-dash dash-bar" "" "" ""
+skipnot [ -n "$BAR_MODULE" ]
+testing "treats _ and - as equivalent" "modinfo $BAR_MODULE > bar-bar &&
+  modinfo ${BAR_MODULE/_/-} > bar-dash && diff -u bar-bar bar-dash" "" "" ""
 
 # Output of -F filename should be an absolute path to the module.
 # Otherwise, initrd generating scripts will break.
 
-testing "-F filename gets absolute path" "[ -e `modinfo -F filename ne2k-pci` ] && echo ne2k-pci " "ne2k-pci\n" "" ""
+testing "-F filename gets absolute path" "modinfo -F filename $MODULE1" \
+  "$MODULE_PATH1\n" "" ""
 
-testing "supports multiple modules" "modinfo -F filename ne2k-pci 8390 | wc -l" "2\n" "" ""
-
-testing "does not output filename for bad module" "modinfo -F filename zxcvbnm__9753" "" "" ""
-
-
-
+skipnot [ "$MODULE1" != "$MODULE2" ]
+testing "supports multiple modules" "modinfo -F filename $MODULE1 $MODULE2" \
+  "$MODULE_PATH1\n$MODULE_PATH2\n" "" ""
diff --git a/tests/split.test b/tests/split.test
index 533cd4f..4a52243 100755
--- a/tests/split.test
+++ b/tests/split.test
@@ -25,7 +25,7 @@
   "seq 1 20000 | split -b 100 -a 3 - whang && ls whang* | wc -l && wc -c whangbpw" "1089\n94 whangbpw\n" "" ""
 
 testing "reassembly" \
-  'diff -u <(ls whang* | sort | xargs cat) <(seq 1 20000) && echo yes' \
+  'ls whang* | sort | xargs cat > reassembled && seq 1 20000 | diff -u reassembled - && echo yes' \
   "yes\n" "" ""
 
-rm file whang*
+rm file whang* reassembled
diff --git a/tests/touch.test b/tests/touch.test
index b010f7d..8be231b 100644
--- a/tests/touch.test
+++ b/tests/touch.test
@@ -64,10 +64,10 @@
   "touch -t 2101231234 input && date +%Y-%m-%d:%H-%M-%S -r input" \
   "$(date +%C)21-01-23:12-34-00\n" "" ""
 
-testing "-a" "touch -t 197101020304 walrus &&
-    touch -t 197203040506 -a walrus && stat -c '%X %Y' walrus" \
-    "68555160 31655040\n" "" ""
-testing "-m" "TZ=utc touch -t 197101020304  walrus &&
+testing "-a" "TZ=utc touch -t 197101020304 walrus &&
+    TZ=utc touch -t 197203040506 -a walrus && TZ=utc stat -c '%X %Y' walrus" \
+    "68533560 31633440\n" "" ""
+testing "-m" "TZ=utc touch -t 197101020304 walrus &&
     TZ=utc touch -t 197203040506 -m walrus && TZ=utc stat -c '%X %Y' walrus" \
     "31633440 68533560\n" "" ""
 testing "-am" "TZ=utc touch -t 197101020304 walrus &&
diff --git a/toys/other/modinfo.c b/toys/other/modinfo.c
index eaf6cb9..286570f 100644
--- a/toys/other/modinfo.c
+++ b/toys/other/modinfo.c
@@ -10,10 +10,14 @@
   bool "modinfo"
   default y
   help
-    usage: modinfo [-0] [-b basedir] [-k kernrelease] [-F field] [modulename...]
+    usage: modinfo [-0] [-b basedir] [-k kernel] [-F field] [module|file...]
 
-    Display module fields for all specified modules, looking in
-    <basedir>/lib/modules/<kernrelease>/ (kernrelease defaults to uname -r).
+    Display module fields for modules specified by name or .ko path.
+
+    -F  Only show the given field
+    -0  Separate fields with NUL rather than newline
+    -b  Use <basedir> as root for /lib/modules/
+    -k  Look in given directory under /lib/modules/
 */
 
 #define FOR_modinfo
@@ -23,6 +27,7 @@
   char *F, *k, *b;
 
   long mod;
+  int count;
 )
 
 static void output_field(char *field, char *value)
@@ -30,7 +35,7 @@
   if (!TT.F) xprintf("%s:%*c", field, 15-(int)strlen(field), ' ');
   else if (strcmp(TT.F, field)) return;
   xprintf("%s", value);
-  xputc((toys.optflags & FLAG_0) ? 0 : '\n');
+  xputc(FLAG(0) ? 0 : '\n');
 }
 
 static void modinfo_file(char *full_name)
@@ -53,6 +58,7 @@
     return;
   }
 
+  TT.count++;
   output_field("filename", full_name);
 
   for (pos = buf; pos < buf+len; pos++) {
@@ -100,19 +106,30 @@
 
 void modinfo_main(void)
 {
-  for(TT.mod = 0; TT.mod<toys.optc; TT.mod++) {
+  struct utsname uts;
+
+  // Android (as shipped by Google) currently only has modules on /vendor.
+  // Android does not support multiple sets of modules for different kernels.
+  if (CFG_TOYBOX_ON_ANDROID) {
+   if (!TT.b) TT.b = "/vendor";
+   if (!TT.k) TT.k = "";
+  } else {
+   uname(&uts);
+   if (!TT.b) TT.b = "";
+   if (!TT.k) TT.k = uts.release;
+  }
+
+  for (TT.mod = 0; TT.mod<toys.optc; TT.mod++) {
     char *s = strstr(toys.optargs[TT.mod], ".ko");
 
     if (s && !s[3]) modinfo_file(toys.optargs[TT.mod]);
     else {
-      struct utsname uts;
+      char *path = xmprintf("%s/lib/modules/%s", TT.b, TT.k);
 
-      if (uname(&uts) < 0) perror_exit("bad uname");
-      if (snprintf(toybuf, sizeof(toybuf), "%s/lib/modules/%s",
-          (toys.optflags & FLAG_b) ? TT.b : "",
-          (toys.optflags & FLAG_k) ? TT.k : uts.release) >= sizeof(toybuf))
-            perror_exit("basedir/kernrelease too long");
-      dirtree_read(toybuf, check_module);
+      TT.count = 0;
+      dirtree_read(path, check_module);
+      if (!TT.count) error_msg("%s: not found", toys.optargs[TT.mod]);
+      free(path);
     }
   }
 }
diff --git a/toys/other/stat.c b/toys/other/stat.c
index c4c1bf4..66c5dc3 100644
--- a/toys/other/stat.c
+++ b/toys/other/stat.c
@@ -191,8 +191,8 @@
       "Block Size: %s    Fundamental block size: %S\n"
       "Blocks: Total: %b\tFree: %f\tAvailable: %a\n"
       "Inodes: Total: %c\tFree: %d"
-    : "  File: %N\n  Size: %s\t Blocks: %b\t IO Blocks: %B\t%F\n"
-      "Device: %Dh/%dd\t Inode: %i\t Links: %h\n"
+    : "  File: %N\n  Size: %s\t Blocks: %b\t IO Blocks: %B\t %F\n"
+      "Device: %Dh/%dd\t Inode: %i\t Links: %h\t Device type: %t,%T\n"
       "Access: (0%a/%A)\tUid: (%5u/%8U)\tGid: (%5g/%8G)\n"
       "Access: %x\nModify: %y\nChange: %z";
 
diff --git a/toys/posix/file.c b/toys/posix/file.c
index 6b8c677..2370be7 100644
--- a/toys/posix/file.c
+++ b/toys/posix/file.c
@@ -421,7 +421,7 @@
 
   // Can't use loopfiles here because it doesn't call function when can't open
   for (arg = toys.optargs; *arg; arg++) {
-    char *name = *arg, *what = "cannot open";
+    char *name = *arg, *what = "unknown";
     struct stat sb;
     int fd = !strcmp(name, "-");
 
@@ -439,14 +439,20 @@
           continue;
         }
       } else if (S_ISFIFO(sb.st_mode)) what = "fifo";
-      else if (S_ISBLK(sb.st_mode)) what = "block special";
-      else if (S_ISCHR(sb.st_mode)) what = "character special";
+      else if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode))
+        sprintf(what = toybuf, "%s special (%u/%u)",
+            S_ISBLK(sb.st_mode) ? "block" : "character",
+            dev_major(sb.st_rdev), dev_minor(sb.st_rdev));
       else if (S_ISDIR(sb.st_mode)) what = "directory";
       else if (S_ISSOCK(sb.st_mode)) what = "socket";
-      else if (S_ISLNK(sb.st_mode)) what = "symbolic link";
-      else what = "unknown";
-    }
+      else if (S_ISLNK(sb.st_mode)) {
+        char *lnk = xreadlink(name);
 
-    xputs(what);
+        sprintf(what = toybuf, "%ssymbolic link to %s",
+            stat(lnk, &sb) ? "broken " : "", lnk);
+        free(lnk);
+      }
+      xputs(what);
+    } else xprintf("cannot open: %s\n", strerror(errno));
   }
 }