diff --git a/utils/.gitignore b/utils/.gitignore
index 9295e7dc..0d18f08a 100644
--- a/utils/.gitignore
+++ b/utils/.gitignore
@@ -28,3 +28,4 @@ rmdir
which
ln
mv
+find
diff --git a/utils/Makefile b/utils/Makefile
index 0b4ab99a..ef1332d7 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -23,6 +23,7 @@ column \
cp \
echo \
editor \
+find \
head \
help \
init \
diff --git a/utils/find.cpp b/utils/find.cpp
new file mode 100644
index 00000000..a8d6c02f
--- /dev/null
+++ b/utils/find.cpp
@@ -0,0 +1,125 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2013.
+
+ This program is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program. If not, see .
+
+ find.cpp
+ Locate files and directories.
+
+*******************************************************************************/
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+char* AddElemToPath(const char* path, const char* elem)
+{
+ size_t pathlen = strlen(path);
+ size_t elemlen = strlen(elem);
+ if ( pathlen && path[pathlen-1] == '/' )
+ {
+ char* ret = (char*) malloc(sizeof(char) * (pathlen + elemlen + 1));
+ stpcpy(stpcpy(ret, path), elem);
+ return ret;
+ }
+ char* ret = (char*) malloc(sizeof(char) * (pathlen + 1 + elemlen + 1));
+ stpcpy(stpcpy(stpcpy(ret, path), "/"), elem);
+ return ret;
+}
+
+const int TYPE_FILE = 1 << 0;
+const int TYPE_DIR = 1 << 1;
+
+bool Find(int dirfd, const char* relpath, const char* path, int types)
+{
+ bool ret = true;
+ int fd = openat(dirfd, relpath, O_RDONLY);
+ if ( fd < 0 ) { error(0, errno, "%s", path); return false; }
+ struct stat st;
+ if ( fstat(fd, &st) ) { error(0, errno, "stat: %s", path); return false; }
+ if ( S_ISDIR(st.st_mode) )
+ {
+ if ( types & TYPE_DIR )
+ printf("%s\n", path);
+ DIR* dir = fdopendir(fd);
+ if ( !dir ) { perror("fdopendir"); close(fd); return false; }
+ struct dirent* entry;
+ while ( (entry = readdir(dir)) )
+ {
+ const char* name = entry->d_name;
+ if ( !strcmp(name, ".") || !strcmp(name, "..") )
+ continue;
+ char* newpath = AddElemToPath(path, name);
+ if ( !Find(fd, name, newpath, types) )
+ {
+ ret = false;
+ break;
+ }
+ }
+ closedir(dir);
+ }
+ else if ( S_ISREG(st.st_mode) )
+ {
+ if ( types & TYPE_FILE )
+ printf("%s\n", path);
+ }
+ return ret;
+}
+
+int main(int argc, char* argv[])
+{
+ const char* path = NULL;
+ bool found_options = false;
+ int types = 0;
+ for ( int i = 1; i < argc; i++ )
+ {
+ const char* arg = argv[i];
+ if ( arg[0] != '-' )
+ {
+ if ( found_options )
+ error(1, 0, "path `%s' must come before options");
+ if ( path )
+ error(1, 0, "multiple paths are not supported");
+ path = arg;
+ }
+ else if ( !strcmp(arg, "-type") )
+ {
+ if ( i + 1 == argc )
+ error(1, 0, "-type expects an argument");
+ arg = argv[++i];
+ if ( !strcmp(arg, "f") )
+ types |= TYPE_FILE;
+ else if ( !strcmp(arg, "d") )
+ types |= TYPE_DIR;
+ else
+ error(1, 0, "unknown `-type %s'", arg);
+ }
+ else
+ error(1, 0, "unknown option `%s'", arg);
+ }
+ if ( !path )
+ path = ".";
+ if ( !types )
+ types = TYPE_FILE | TYPE_DIR;
+ return Find(AT_FDCWD, path, path, types) ? 0 : 1;
+}