├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── examples └── ls.c └── minirent.h /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build-linux-gcc: 6 | runs-on: ubuntu-18.04 7 | steps: 8 | - uses: actions/checkout@v1 9 | - name: build all and examples 10 | run: | 11 | $CC -I . -o ls ./examples/ls.c 12 | ./ls 13 | env: 14 | CC: gcc 15 | CXX: g++ 16 | build-linux-clang: 17 | runs-on: ubuntu-18.04 18 | steps: 19 | - uses: actions/checkout@v1 20 | - name: build all and examples 21 | run: | 22 | $CC -I . -o ls ./examples/ls.c 23 | ./ls 24 | env: 25 | CC: clang 26 | CXX: clang++ 27 | build-macos: 28 | runs-on: macOS-latest 29 | steps: 30 | - uses: actions/checkout@v1 31 | - name: build all and examples 32 | run: | 33 | $CC -I . -o ls ./examples/ls.c 34 | ./ls 35 | env: 36 | CC: clang 37 | CXX: clang++ 38 | build-windows-msvc: 39 | runs-on: windows-2019 40 | steps: 41 | - uses: actions/checkout@v1 42 | # this runs vcvarsall for us, so we get the MSVC toolchain in PATH. 43 | - uses: seanmiddleditch/gha-setup-vsdevenv@master 44 | - name: build all and examples 45 | shell: cmd 46 | # this replaces default PowerShell, which can't fail the build 47 | run: | 48 | cl.exe /I. examples\ls.c 49 | .\ls.exe 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ls 2 | *.exe 3 | *.obj -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Alexey Kutepov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://github.com/tsoding/minirent/workflows/CI/badge.svg)](https://github.com/tsoding/minirent/actions) 2 | 3 | # minirent 4 | 5 | A subset of [dirent](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/dirent.h.html) interface for Windows. 6 | 7 | The code that works with minirent must work with the dirent. 8 | 9 | Use minirent as a cross-platform replacement of dirent if you only need the subset interface. 10 | 11 | ## Usage 12 | 13 | [minirent.h](./minirent.h) is an [stb-style](https://github.com/nothings/stb/blob/master/docs/stb_howto.txt) header-only library. That means that when you just include it it does not include the implementations of the functions. You have to define `MINIRENT_IMPLEMENTATION` macro: 14 | 15 | ```c 16 | #include 17 | #include 18 | #include 19 | 20 | #define MINIRENT_IMPLEMENTATION 21 | #include 22 | 23 | int main(void) 24 | { 25 | DIR *dir = opendir("."); 26 | assert(dir); 27 | 28 | errno = 0; 29 | struct dirent *dp = NULL; 30 | while ((dp = readdir(dir))) { 31 | printf("%s\n", dp->d_name); 32 | } 33 | assert(errno == 0); 34 | 35 | int err = closedir(dir); 36 | assert(err == 0); 37 | 38 | return 0; 39 | } 40 | ``` 41 | 42 | For more information see [./examples/](./examples/) folder. 43 | -------------------------------------------------------------------------------- /examples/ls.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define MINIRENT_IMPLEMENTATION 8 | #include 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | const char *dir_path = "."; 13 | 14 | if (argc >= 2) { 15 | dir_path = argv[1]; 16 | } 17 | 18 | DIR *dir = opendir(dir_path); 19 | if (dir == NULL) { 20 | fprintf(stderr, "ERROR: could not open directory %s: %s\n", 21 | dir_path, strerror(errno)); 22 | exit(1); 23 | } 24 | 25 | errno = 0; 26 | struct dirent *dp = NULL; 27 | while ((dp = readdir(dir))) { 28 | // TODO: the output of ls is not sorted 29 | printf("%s\n", dp->d_name); 30 | } 31 | 32 | if (errno) { 33 | fprintf(stderr, "ERROR: could not read directory %s: %s\n", 34 | dir_path, strerror(errno)); 35 | exit(1); 36 | } 37 | 38 | if (closedir(dir) < 0) { 39 | fprintf(stderr, "ERROR: could not close directory %s: %s\n", 40 | dir_path, strerror(errno)); 41 | exit(1); 42 | } 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /minirent.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Alexey Kutepov 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining 4 | // a copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so, subject to 9 | // the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be 12 | // included in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | // 22 | // ============================================================ 23 | // 24 | // minirent — 0.0.1 — A subset of dirent interface for Windows. 25 | // 26 | // https://github.com/tsoding/minirent 27 | // 28 | // ============================================================ 29 | // 30 | // ChangeLog (https://semver.org/ is implied) 31 | // 32 | // 0.0.2 Automatically include dirent.h on non-Windows 33 | // platforms 34 | // 0.0.1 First Official Release 35 | 36 | #ifndef _WIN32 37 | #include 38 | #else // _WIN32 39 | 40 | #ifndef MINIRENT_H_ 41 | #define MINIRENT_H_ 42 | 43 | #define WIN32_LEAN_AND_MEAN 44 | #include "windows.h" 45 | 46 | struct dirent 47 | { 48 | char d_name[MAX_PATH+1]; 49 | }; 50 | 51 | typedef struct DIR DIR; 52 | 53 | DIR *opendir(const char *dirpath); 54 | struct dirent *readdir(DIR *dirp); 55 | int closedir(DIR *dirp); 56 | 57 | #endif // MINIRENT_H_ 58 | 59 | #ifdef MINIRENT_IMPLEMENTATION 60 | 61 | struct DIR 62 | { 63 | HANDLE hFind; 64 | WIN32_FIND_DATA data; 65 | struct dirent *dirent; 66 | }; 67 | 68 | DIR *opendir(const char *dirpath) 69 | { 70 | assert(dirpath); 71 | 72 | char buffer[MAX_PATH]; 73 | snprintf(buffer, MAX_PATH, "%s\\*", dirpath); 74 | 75 | DIR *dir = (DIR*)calloc(1, sizeof(DIR)); 76 | 77 | dir->hFind = FindFirstFile(buffer, &dir->data); 78 | if (dir->hFind == INVALID_HANDLE_VALUE) { 79 | // TODO: opendir should set errno accordingly on FindFirstFile fail 80 | // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror 81 | errno = ENOSYS; 82 | goto fail; 83 | } 84 | 85 | return dir; 86 | 87 | fail: 88 | if (dir) { 89 | free(dir); 90 | } 91 | 92 | return NULL; 93 | } 94 | 95 | struct dirent *readdir(DIR *dirp) 96 | { 97 | assert(dirp); 98 | 99 | if (dirp->dirent == NULL) { 100 | dirp->dirent = (struct dirent*)calloc(1, sizeof(struct dirent)); 101 | } else { 102 | if(!FindNextFile(dirp->hFind, &dirp->data)) { 103 | if (GetLastError() != ERROR_NO_MORE_FILES) { 104 | // TODO: readdir should set errno accordingly on FindNextFile fail 105 | // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror 106 | errno = ENOSYS; 107 | } 108 | 109 | return NULL; 110 | } 111 | } 112 | 113 | memset(dirp->dirent->d_name, 0, sizeof(dirp->dirent->d_name)); 114 | 115 | strncpy( 116 | dirp->dirent->d_name, 117 | dirp->data.cFileName, 118 | sizeof(dirp->dirent->d_name) - 1); 119 | 120 | return dirp->dirent; 121 | } 122 | 123 | int closedir(DIR *dirp) 124 | { 125 | assert(dirp); 126 | 127 | if(!FindClose(dirp->hFind)) { 128 | // TODO: closedir should set errno accordingly on FindClose fail 129 | // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror 130 | errno = ENOSYS; 131 | return -1; 132 | } 133 | 134 | if (dirp->dirent) { 135 | free(dirp->dirent); 136 | } 137 | free(dirp); 138 | 139 | return 0; 140 | } 141 | 142 | #endif // MINIRENT_IMPLEMENTATION 143 | #endif // _WIN32 144 | --------------------------------------------------------------------------------