Signed-off-by: Ilya Kuzmich <ilya.kuzm...@gmail.com>
---
 tests/pathchk.test   | 85 ++++++++++++++++++++++++++++++++++++++++++++++
 toys/posix/pathchk.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 180 insertions(+)
 create mode 100644 tests/pathchk.test
 create mode 100644 toys/posix/pathchk.c

diff --git a/tests/pathchk.test b/tests/pathchk.test
new file mode 100644
index 0000000..4632399
--- /dev/null
+++ b/tests/pathchk.test
@@ -0,0 +1,85 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "zero arguments" "pathchk 2>/dev/null || echo ok" "ok\n" "" "" 
+
+mkdir dir_a-rx
+chmod a-rx dir_a-rx
+testing "dir_a-rx/a" "pathchk dir_a-rx/a 2>/dev/null || echo ok" "ok\n" "" ""
+rmdir dir_a-rx
+
+touch file
+testing "file/a" "pathchk file/a 2>/dev/null || echo ok" "ok\n" "" ""
+rm file
+
+mkcomponent() {
+  head -c "$1" /dev/zero | tr '\0' 'a'
+}
+
+mkpath() {
+  to_fill=$1
+  component_len=$2
+
+  while [ "$to_fill" -gt 0 ]; do
+    if [ $component_len -ge $to_fill ]; then
+      component_len=$((to_fill))
+    fi
+    mkcomponent $component_len
+    to_fill=$((to_fill-component_len))
+    if [ "$to_fill" -gt 0 ]; then
+      printf "/"
+      to_fill=$((to_fill-1))
+    fi
+  done
+}
+
+NAME_MAX="$(getconf NAME_MAX .)"
+testing "NAME_MAX" "pathchk '$(mkcomponent $NAME_MAX )' \
+        && echo ok" "ok\n" "" ""
+testing "NAME_MAX+1" "pathchk '$(mkcomponent $((NAME_MAX+1)) )' \
+        2>/dev/null || echo ok" "ok\n" "" ""
+testing "NAME_MAX/1" "pathchk '$(mkcomponent $NAME_MAX)/a' \
+        2>/dev/null && echo ok" "ok\n" "" ""
+testing "NAME_MAX+1/1" "pathchk '$(mkcomponent $((NAME_MAX+1)))/a' \
+        2>/dev/null || echo ok" "ok\n" "" ""
+
+PATH_MAX="$(getconf PATH_MAX .)"
+testing "PATH_MAX-1" "pathchk '$(mkpath $((PATH_MAX-1)) $NAME_MAX)' \
+        && echo ok" "ok\n" "" ""
+testing "PATH_MAX" "pathchk '$(mkpath $PATH_MAX $NAME_MAX)' \
+        2>/dev/null || echo ok" "ok\n" "" ""
+
+POSIX_NAME_MAX="14"
+testing "-p _POSIX_NAME_MAX" "pathchk -p \
+        '$(mkcomponent $POSIX_NAME_MAX)' && echo ok" "ok\n" "" ""
+testing "-p _POSIX_NAME_MAX+1" "pathchk -p\
+        '$(mkcomponent $((POSIX_NAME_MAX+1)))' 2>/dev/null || echo ok" \
+        "ok\n" "" ""
+testing "-p _POSIX_NAME_MAX/1" "pathchk -p \
+        '$(mkcomponent $POSIX_NAME_MAX)/a' && echo ok" "ok\n" "" ""
+testing "-p _POSIX_NAME_MAX+1/1" "pathchk -p\
+        '$(mkcomponent $((POSIX_NAME_MAX+1)))/1' 2>/dev/null || echo ok" \
+        "ok\n" "" ""
+
+POSIX_PATH_MAX="256"
+testing "-p _POSIX_PATH_MAX-1" "pathchk -p \
+        '$(mkpath $((POSIX_PATH_MAX-1)) $POSIX_NAME_MAX)' && echo ok" \
+        "ok\n" "" ""
+testing "-p _POSIX_PATH_MAX" "pathchk -p \
+       '$(mkpath $POSIX_PATH_MAX $POSIX_NAME_MAX)' 2>/dev/null || \
+       echo ok" "ok\n" "" ""
+
+# portable filename character set
+testing "-p a=b" "pathchk -p a=b 2>/dev/null || echo ok" "ok\n" "" ""
+testing "-p 'a$'" "pathchk -p 'a$' 2>/dev/null || echo ok" "ok\n" "" ""
+testing "-p '+a'" "pathchk -p '+a' 2>/dev/null || echo ok" "ok\n" "" ""
+
+# empty path and leading '-'
+testing "-P ''" "pathchk -P '' 2>/dev/null || echo ok" "ok\n" "" ""
+testing "-P a/-b" "pathchk -P 'a/-b' 2>/dev/null || echo ok" "ok\n" "" ""
+
+# one error message per path
+testing "-P '' '-'" "pathchk -P '' '-' 2>&1 | wc -l" "2\n" "" ""
diff --git a/toys/posix/pathchk.c b/toys/posix/pathchk.c
new file mode 100644
index 0000000..25d76a7
--- /dev/null
+++ b/toys/posix/pathchk.c
@@ -0,0 +1,95 @@
+/* pathchk.c - check pathnames
+ *
+ * Copyright 2017 Ilya Kuzmich <ilya.kuzm...@gmail.com>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pathchk.html
+
+USE_PATHCHK(NEWTOY(pathchk, "<1pP", TOYFLAG_USR|TOYFLAG_BIN))
+
+config PATHCHK
+  bool "pathchk"
+  default y
+  help
+    usage: pathchk [-p] [-P] [NAME...]
+
+    Check that pathnames are portable and valid on the underlying file system
+
+    -p check instead for POSIX portability
+    -P check for empty names and components with leading "-"
+*/
+
+#define FOR_pathchk
+#include "toys.h"
+
+static long get_pathconf(char *path, int name, long fallback)
+{
+  long ret;
+
+  errno = 0;
+  ret = pathconf(path, name);
+  if (ret == -1 && errno)
+    ret = fallback;
+
+  return ret;
+}
+
+static void do_check_path(char *path)
+{
+  long pathmax, namemax;
+  size_t namelen;
+  char *p = path;
+
+  if (toys.optflags & FLAG_p) {
+    namemax = _POSIX_NAME_MAX;
+    pathmax = _POSIX_PATH_MAX;
+
+    p += strspn(p, "/"
+        "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+        "0123456789._-");
+
+    if (*p) {
+      error_msg("%s: nonportable character %c in path", path, *p);
+      return;
+    }
+  } else {
+    struct stat st;
+
+    namemax = get_pathconf(*path == '/' ? "/" : ".", _PC_NAME_MAX, NAME_MAX);
+    pathmax = get_pathconf(path, _PC_PATH_MAX, PATH_MAX);
+
+    errno = 0;
+    if (stat(path, &st) && errno != ENOENT)
+      perror_msg("%s", path);
+  }
+
+  for (namelen = 0, p = path; *p; p += namelen) {
+    p += strspn(p, "/");
+    namelen = strcspn(p, "/");
+
+    if (namemax != -1 && namelen > namemax) {
+      error_msg("%s: component too long (limit %ld)", path, namemax);
+      return;
+    }
+
+    if (toys.optflags & FLAG_P && *p == '-') {
+      error_msg("%s: leading '-' in a path component", path);
+      return;
+    }
+  }
+
+  if (pathmax != -1 && strlen(path) >= (size_t)pathmax) {
+    error_msg("%s: path too long (limit %ld)", path, pathmax);
+    return;
+  }
+
+  if (toys.optflags & FLAG_P && !*path) {
+    error_msg("path is empty");
+    return;
+  }
+}
+
+void pathchk_main(void)
+{
+  while (*toys.optargs) do_check_path(*toys.optargs++);
+}
-- 
2.7.4

_______________________________________________
Toybox mailing list
Toybox@lists.landley.net
http://lists.landley.net/listinfo.cgi/toybox-landley.net

Reply via email to