Create the dirent.h portability header
authorChristopher Dykes <cdykes@fb.com>
Fri, 1 Apr 2016 18:18:42 +0000 (11:18 -0700)
committerFacebook Github Bot 8 <facebook-github-bot-8-bot@fb.com>
Fri, 1 Apr 2016 18:20:30 +0000 (11:20 -0700)
Summary: We don't have dirent.h on Windows, but we can emulate its behavior.

Reviewed By: yfeldblum

Differential Revision: D2978570

fb-gh-sync-id: af5ade0ea64ba22900440250e7125aa039a77f62
fbshipit-source-id: af5ade0ea64ba22900440250e7125aa039a77f62

folly/Makefile.am
folly/portability/Dirent.cpp [new file with mode: 0755]
folly/portability/Dirent.h [new file with mode: 0755]

index 3ea8c30ab3291fde15c76404960987aae0ac6312..96ff566526df8905cc61dfde8d61050fc5b53937 100644 (file)
@@ -270,6 +270,7 @@ nobase_follyinclude_HEADERS = \
        portability/Asm.h \
        portability/Config.h \
        portability/Constexpr.h \
+       portability/Dirent.h \
        portability/Environment.h \
        portability/GFlags.h \
        portability/IOVec.h \
@@ -416,6 +417,7 @@ libfolly_la_SOURCES = \
        detail/MemoryIdler.cpp \
        MacAddress.cpp \
        MemoryMapping.cpp \
+       portability/Dirent.cpp \
        portability/Environment.cpp \
        portability/Malloc.cpp \
        portability/String.cpp \
diff --git a/folly/portability/Dirent.cpp b/folly/portability/Dirent.cpp
new file mode 100755 (executable)
index 0000000..5c75a20
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/portability/Dirent.h>
+
+#ifdef _WIN32
+#include <stdlib.h>
+#include <string>
+
+#include <folly/portability/Windows.h>
+
+struct DIR {
+  dirent dir{};
+  HANDLE searchHandle{INVALID_HANDLE_VALUE};
+  int entriesRead{0};
+  char currentName[MAX_PATH * 3];
+  std::string pattern;
+
+  int close() {
+    return FindClose(searchHandle) ? 0 : -1;
+  }
+
+  DIR* open() {
+    wchar_t patternBuf[MAX_PATH + 2];
+    size_t len;
+
+    if (mbstowcs_s(&len, patternBuf, MAX_PATH, pattern.c_str(), MAX_PATH - 2)) {
+      return nullptr;
+    }
+
+    if (len && patternBuf[len - 1] != '/' && patternBuf[len - 1] != '\\') {
+      patternBuf[len++] = '\\';
+    }
+    patternBuf[len++] = '*';
+    patternBuf[len] = 0;
+
+    WIN32_FIND_DATAW fdata;
+    HANDLE h = FindFirstFileW(patternBuf, &fdata);
+    if (h == INVALID_HANDLE_VALUE) {
+      return nullptr;
+    }
+
+    searchHandle = h;
+    dir.d_name = currentName;
+    if (wcstombs(currentName, fdata.cFileName, MAX_PATH * 3) == (size_t)-1) {
+      return nullptr;
+    }
+
+    setEntryType(fdata.dwFileAttributes);
+    return this;
+  }
+
+  dirent* nextDir() {
+    if (entriesRead) {
+      WIN32_FIND_DATAW fdata;
+      if (!FindNextFileW(searchHandle, &fdata)) {
+        return nullptr;
+      }
+
+      if (wcstombs(currentName, fdata.cFileName, MAX_PATH * 3) == (size_t)-1) {
+        errno = EBADF;
+        return nullptr;
+      }
+      setEntryType(fdata.dwFileAttributes);
+    }
+
+    entriesRead++;
+    return &dir;
+  }
+
+ private:
+  void setEntryType(DWORD attr) {
+    if (attr & FILE_ATTRIBUTE_DIRECTORY) {
+      dir.d_type = DT_DIR;
+    } else {
+      dir.d_type = DT_REG;
+    }
+  }
+};
+
+extern "C" {
+int closedir(DIR* dir) {
+  auto ret = dir->close();
+  delete dir;
+  return ret;
+}
+
+DIR* opendir(const char* name) {
+  auto dir = new DIR();
+  dir->pattern = name;
+  if (!dir->open()) {
+    delete dir;
+    return nullptr;
+  }
+  return dir;
+}
+
+dirent* readdir(DIR* dir) {
+  return dir->nextDir();
+}
+
+int readdir_r(DIR* dir, dirent* buf, dirent** ent) {
+  if (!dir || !buf || !ent) {
+    return EBADF;
+  }
+  *ent = dir->nextDir();
+  // Our normal readdir implementation is actually
+  // already reentrant, but we need to do this copy
+  // in case the caller expects buf to have the value.
+  if (*ent) {
+    *buf = dir->dir;
+  }
+  return 0;
+}
+
+void rewinddir(DIR* dir) {
+  dir->close();
+  dir->open();
+}
+}
+#endif
diff --git a/folly/portability/Dirent.h b/folly/portability/Dirent.h
new file mode 100755 (executable)
index 0000000..f14e0c9
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifndef _WIN32
+#include <dirent.h>
+#else
+
+#define DT_UNKNOWN 0
+#define DT_DIR 1
+#define DT_REG 2
+#define DT_LNK 3
+struct dirent {
+  unsigned char d_type;
+  char* d_name;
+};
+
+struct DIR;
+
+extern "C" {
+int closedir(DIR* dir);
+DIR* opendir(const char* name);
+dirent* readdir(DIR* dir);
+int readdir_r(DIR* dir, dirent* buf, dirent** ent);
+void rewinddir(DIR* dir);
+}
+#endif