New upstream version 5.2.5188
This commit is contained in:
24
3rdparty/tinydir/.travis.yml
vendored
Normal file
24
3rdparty/tinydir/.travis.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
sudo: false
|
||||
|
||||
language: c
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- gcc-4.8
|
||||
|
||||
before_script:
|
||||
# force newer gcc version
|
||||
- if [ "$CC" = "gcc" ]; then export CC="gcc-4.8"; fi
|
||||
# show which tests failed
|
||||
- export CTEST_OUTPUT_ON_FAILURE=1
|
||||
|
||||
script:
|
||||
- mkdir build && cd build && cmake ../samples && make -j2 && cd .. && rm -rf build
|
||||
- mkdir build && cd build && cmake ../tests && make -j2 && make test && cd .. && rm -rf build
|
||||
26
3rdparty/tinydir/COPYING
vendored
Normal file
26
3rdparty/tinydir/COPYING
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
Copyright (c) 2013-2016, tinydir authors:
|
||||
- Cong Xu
|
||||
- Lautis Sun
|
||||
- Baudouin Feildel
|
||||
- Andargor <andargor@yahoo.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
79
3rdparty/tinydir/README.md
vendored
Normal file
79
3rdparty/tinydir/README.md
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
TinyDir
|
||||
=======
|
||||
[](https://travis-ci.org/cxong/tinydir)
|
||||
[](https://github.com/cxong/tinydir/releases/latest)
|
||||
|
||||
Lightweight, portable and easy to integrate C directory and file reader. TinyDir wraps dirent for POSIX and FindFirstFile for Windows.
|
||||
|
||||
Windows unicode is supported by defining `UNICODE` and `_UNICODE` before including `tinydir.h`.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
There are two methods. Error checking omitted:
|
||||
|
||||
```C
|
||||
tinydir_dir dir;
|
||||
tinydir_open(&dir, "/path/to/dir");
|
||||
|
||||
while (dir.has_next)
|
||||
{
|
||||
tinydir_file file;
|
||||
tinydir_readfile(&dir, &file);
|
||||
|
||||
printf("%s", file.name);
|
||||
if (file.is_dir)
|
||||
{
|
||||
printf("/");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
tinydir_next(&dir);
|
||||
}
|
||||
|
||||
tinydir_close(&dir);
|
||||
```
|
||||
|
||||
```C
|
||||
tinydir_dir dir;
|
||||
int i;
|
||||
tinydir_open_sorted(&dir, "/path/to/dir");
|
||||
|
||||
for (i = 0; i < dir.n_files; i++)
|
||||
{
|
||||
tinydir_file file;
|
||||
tinydir_readfile_n(&dir, &file, i);
|
||||
|
||||
printf("%s", file.name);
|
||||
if (file.is_dir)
|
||||
{
|
||||
printf("/");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
tinydir_close(&dir);
|
||||
```
|
||||
|
||||
See the `/samples` folder for more examples, including an interactive command-line directory navigator.
|
||||
|
||||
Language
|
||||
========
|
||||
|
||||
ANSI C, or C90.
|
||||
|
||||
Platforms
|
||||
=========
|
||||
|
||||
POSIX and Windows supported. Open to the possibility of supporting other platforms.
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
Simplified BSD; if you use tinydir you can comply by including `tinydir.h` or `COPYING` somewhere in your package.
|
||||
|
||||
Known Limitations
|
||||
=================
|
||||
|
||||
- Limited path and filename sizes
|
||||
- [Possible race condition bug if folder being read has changing content](https://github.com/cxong/tinydir/issues/13)
|
||||
17
3rdparty/tinydir/package.json
vendored
Normal file
17
3rdparty/tinydir/package.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "tinydir",
|
||||
"description": "Lightweight, portable and easy to integrate C directory and file reader",
|
||||
"license": "BSD-2-Clause",
|
||||
"keywords": [
|
||||
"dir",
|
||||
"directory",
|
||||
"file",
|
||||
"reader",
|
||||
"filesystem"
|
||||
],
|
||||
"src": [
|
||||
"tinydir.h"
|
||||
],
|
||||
"version": "1.2.4",
|
||||
"repo": "cxong/tinydir"
|
||||
}
|
||||
45
3rdparty/tinydir/samples/.gitignore
vendored
Normal file
45
3rdparty/tinydir/samples/.gitignore
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# Object files
|
||||
*.o
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*_sample
|
||||
|
||||
# CMake
|
||||
CMakeFiles/
|
||||
CMakeCache.txt
|
||||
*.dir/
|
||||
cmake_install.cmake
|
||||
|
||||
# Visual Studio
|
||||
.vs/
|
||||
Debug/
|
||||
out/
|
||||
Win32/
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.suo
|
||||
*.vcxproj.user
|
||||
*.vcxproj
|
||||
*.vcxproj.filters
|
||||
*.VC.*
|
||||
*.sln
|
||||
|
||||
# Linux
|
||||
Makefile
|
||||
|
||||
# OS X
|
||||
CMakeScripts/
|
||||
*.xcodeproj
|
||||
26
3rdparty/tinydir/samples/CMakeLists.txt
vendored
Normal file
26
3rdparty/tinydir/samples/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
cmake_minimum_required(VERSION 2.6 FATAL_ERROR)
|
||||
cmake_policy(VERSION 2.6)
|
||||
|
||||
project(tinydir)
|
||||
|
||||
INCLUDE_DIRECTORIES(..)
|
||||
|
||||
################################
|
||||
# Add definitions
|
||||
|
||||
if(MSVC)
|
||||
add_definitions(-W4 -WX -wd"4996")
|
||||
else()
|
||||
add_definitions(-fsigned-char -Wall -W -Wshadow -Wpointer-arith -Wcast-qual -Winline -Werror)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-prototypes")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
|
||||
endif()
|
||||
|
||||
################################
|
||||
# Add targets
|
||||
add_executable(file_open_sample file_open_sample.c)
|
||||
add_executable(iterate_sample iterate_sample.c)
|
||||
add_executable(random_access_sample random_access_sample.c)
|
||||
add_executable(interactive_sample interactive_sample.c)
|
||||
add_executable(cpp_sample cpp_sample.cpp)
|
||||
add_executable(list_to_file list_to_file.c)
|
||||
46
3rdparty/tinydir/samples/cpp_sample.cpp
vendored
Normal file
46
3rdparty/tinydir/samples/cpp_sample.cpp
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
struct tinydir_dir;
|
||||
|
||||
class TinyDir {
|
||||
public:
|
||||
TinyDir(const std::string& path);
|
||||
~TinyDir();
|
||||
std::string BaseName() const;
|
||||
private:
|
||||
tinydir_dir* dir;
|
||||
};
|
||||
|
||||
#include <tinydir.h>
|
||||
|
||||
TinyDir::TinyDir(const std::string& path) : dir(new tinydir_dir) {
|
||||
if (tinydir_open(dir, path.c_str()) == -1) {
|
||||
throw std::invalid_argument{"path"};
|
||||
}
|
||||
}
|
||||
|
||||
TinyDir::~TinyDir() {
|
||||
delete dir;
|
||||
}
|
||||
|
||||
std::string TinyDir::BaseName() const {
|
||||
const std::string path{dir->path};
|
||||
auto lastSlash = path.find_last_of("/\\");
|
||||
if (lastSlash == std::string::npos) {
|
||||
return path;
|
||||
}
|
||||
return path.substr(lastSlash + 1);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 2) {
|
||||
std::cerr << "Usage: cpp_sample filename\n";
|
||||
return 1;
|
||||
}
|
||||
TinyDir td{argv[1]};
|
||||
std::cout << "Basename is " << td.BaseName() << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
21
3rdparty/tinydir/samples/file_open_sample.c
vendored
Normal file
21
3rdparty/tinydir/samples/file_open_sample.c
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
#include <stdio.h>
|
||||
#include <tinydir.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
tinydir_file file;
|
||||
if (argc != 2)
|
||||
{
|
||||
fprintf(stderr, "Usage: test filename\n");
|
||||
return 1;
|
||||
}
|
||||
if (tinydir_file_open(&file, argv[1]) == -1)
|
||||
{
|
||||
perror("Error opening file");
|
||||
return 1;
|
||||
}
|
||||
printf("Path: %s\nName: %s\nExtension: %s\nIs dir? %s\nIs regular file? %s\n",
|
||||
file.path, file.name, file.extension,
|
||||
file.is_dir?"yes":"no", file.is_reg?"yes":"no");
|
||||
return 0;
|
||||
}
|
||||
60
3rdparty/tinydir/samples/interactive_sample.c
vendored
Normal file
60
3rdparty/tinydir/samples/interactive_sample.c
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
#include <stdio.h>
|
||||
#include <tinydir.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
tinydir_dir dir;
|
||||
if (tinydir_open_sorted(&dir, ".") == -1)
|
||||
{
|
||||
perror("Error opening file");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
size_t i;
|
||||
char input[256];
|
||||
for (i = 0; i < dir.n_files; i++)
|
||||
{
|
||||
tinydir_file file;
|
||||
if (tinydir_readfile_n(&dir, &file, i) == -1)
|
||||
{
|
||||
perror("Error getting file");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (file.is_dir)
|
||||
{
|
||||
printf("[%u] ", (unsigned int)i);
|
||||
}
|
||||
printf("%s", file.name);
|
||||
if (file.is_dir)
|
||||
{
|
||||
printf("/");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf("?");
|
||||
|
||||
if (fgets(input, 256, stdin) == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
int choice = atoi(input);
|
||||
if (choice >= 0 && (size_t)choice < dir.n_files)
|
||||
{
|
||||
if (tinydir_open_subdir_n(&dir, choice) == -1)
|
||||
{
|
||||
perror("Error opening subdirectory");
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bail:
|
||||
tinydir_close(&dir);
|
||||
return 0;
|
||||
}
|
||||
39
3rdparty/tinydir/samples/iterate_sample.c
vendored
Normal file
39
3rdparty/tinydir/samples/iterate_sample.c
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <stdio.h>
|
||||
#include <tinydir.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
tinydir_dir dir;
|
||||
if (tinydir_open(&dir, ".") == -1)
|
||||
{
|
||||
perror("Error opening file");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
while (dir.has_next)
|
||||
{
|
||||
tinydir_file file;
|
||||
if (tinydir_readfile(&dir, &file) == -1)
|
||||
{
|
||||
perror("Error getting file");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
printf("%s", file.name);
|
||||
if (file.is_dir)
|
||||
{
|
||||
printf("/");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
if (tinydir_next(&dir) == -1)
|
||||
{
|
||||
perror("Error getting next file");
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
bail:
|
||||
tinydir_close(&dir);
|
||||
return 0;
|
||||
}
|
||||
47
3rdparty/tinydir/samples/list_to_file.c
vendored
Normal file
47
3rdparty/tinydir/samples/list_to_file.c
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#define _UNICODE
|
||||
#define UNICODE
|
||||
#include "tinydir.h"
|
||||
|
||||
char bom[] = "\xFF\xFE";
|
||||
|
||||
int main(void) {
|
||||
|
||||
FILE *fp;
|
||||
tinydir_dir dir;
|
||||
tinydir_open(&dir, TINYDIR_STRING("/path/to/dir"));
|
||||
|
||||
fp =
|
||||
#if ((defined _WIN32) && (defined _UNICODE))
|
||||
_wfopen(
|
||||
#else
|
||||
fopen(
|
||||
#endif
|
||||
TINYDIR_STRING("/file/to/output"), TINYDIR_STRING("wb"));
|
||||
|
||||
#if ((defined _WIN32) && (defined _UNICODE))
|
||||
fwrite(bom, 1, 2, fp);
|
||||
#endif
|
||||
|
||||
while (dir.has_next)
|
||||
{
|
||||
tinydir_file file;
|
||||
tinydir_readfile(&dir, &file);
|
||||
|
||||
fwrite(file.name, sizeof(_tinydir_char_t), _tinydir_strlen(file.name), fp);
|
||||
if (file.is_dir)
|
||||
{
|
||||
fwrite(TINYDIR_STRING("/"), sizeof(_tinydir_char_t), _tinydir_strlen(TINYDIR_STRING("/")), fp);
|
||||
}
|
||||
fwrite(TINYDIR_STRING("\n"), sizeof(_tinydir_char_t), _tinydir_strlen(TINYDIR_STRING("/")), fp);
|
||||
|
||||
tinydir_next(&dir);
|
||||
}
|
||||
|
||||
tinydir_close(&dir);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
34
3rdparty/tinydir/samples/random_access_sample.c
vendored
Normal file
34
3rdparty/tinydir/samples/random_access_sample.c
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
#include <stdio.h>
|
||||
#include <tinydir.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
tinydir_dir dir;
|
||||
size_t i;
|
||||
if (tinydir_open_sorted(&dir, argc >= 2 ? argv[1] : ".") == -1)
|
||||
{
|
||||
perror("Error opening file");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
for (i = 0; i < dir.n_files; i++)
|
||||
{
|
||||
tinydir_file file;
|
||||
if (tinydir_readfile_n(&dir, &file, i) == -1)
|
||||
{
|
||||
perror("Error getting file");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
printf("%s", file.name);
|
||||
if (file.is_dir)
|
||||
{
|
||||
printf("/");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
bail:
|
||||
tinydir_close(&dir);
|
||||
return 0;
|
||||
}
|
||||
47
3rdparty/tinydir/tests/.gitignore
vendored
Normal file
47
3rdparty/tinydir/tests/.gitignore
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
# Object files
|
||||
*.o
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*_sample
|
||||
|
||||
# CMake
|
||||
CMakeFiles/
|
||||
CMakeCache.txt
|
||||
*.dir/
|
||||
cmake_install.cmake
|
||||
CTestTestfile.cmake
|
||||
|
||||
# Visual Studio
|
||||
Debug/
|
||||
Win32/
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.suo
|
||||
*.vcxproj.user
|
||||
*.vcxproj
|
||||
*.vcxproj.filters
|
||||
*.VC.*
|
||||
*.sln
|
||||
|
||||
# Linux
|
||||
Makefile
|
||||
|
||||
# OS X
|
||||
CMakeScripts/
|
||||
*.xcodeproj
|
||||
|
||||
# Test files
|
||||
*.tmp
|
||||
38
3rdparty/tinydir/tests/CMakeLists.txt
vendored
Normal file
38
3rdparty/tinydir/tests/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
project(tinydir_tests C)
|
||||
|
||||
INCLUDE_DIRECTORIES(..)
|
||||
include_directories(cbehave)
|
||||
add_subdirectory(cbehave)
|
||||
|
||||
################################
|
||||
# Add definitions
|
||||
|
||||
if(MSVC)
|
||||
add_definitions(-W4 -WX -wd"4127" -wd"4102" -wd"4996")
|
||||
else()
|
||||
add_definitions(-fsigned-char -Wall -W -Wshadow -Wpointer-arith -Wcast-qual -Winline -Werror -Wno-unused-label)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-prototypes")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
|
||||
if("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
|
||||
else()
|
||||
add_definitions(-Wno-pointer-to-int-cast)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
################################
|
||||
# Add tests
|
||||
enable_testing()
|
||||
|
||||
add_library(util util.c util.h)
|
||||
|
||||
add_executable(file_open_test file_open_test.c)
|
||||
target_link_libraries(file_open_test util cbehave)
|
||||
add_test(NAME file_open_test COMMAND file_open_test)
|
||||
|
||||
if(MSVC)
|
||||
add_executable(windows_unicode_test windows_unicode_test.c)
|
||||
target_link_libraries(windows_unicode_test cbehave)
|
||||
add_test(NAME windows_unicode_test COMMAND windows_unicode_test)
|
||||
endif()
|
||||
11
3rdparty/tinydir/tests/cbehave/CMakeLists.txt
vendored
Normal file
11
3rdparty/tinydir/tests/cbehave/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
include_directories(.)
|
||||
|
||||
if(MSVC)
|
||||
add_definitions(-wd"4127" -wd"4102" -wd"4996")
|
||||
else()
|
||||
if(NOT BEOS AND NOT HAIKU)
|
||||
add_definitions(-Wno-unused-label)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(cbehave STATIC cbehave.h cbehave.c apr_ring.h rlutil/rlutil.h)
|
||||
13
3rdparty/tinydir/tests/cbehave/LICENSE
vendored
Normal file
13
3rdparty/tinydir/tests/cbehave/LICENSE
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
Copyright 2012 Tony Bai
|
||||
|
||||
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.
|
||||
78
3rdparty/tinydir/tests/cbehave/README.md
vendored
Normal file
78
3rdparty/tinydir/tests/cbehave/README.md
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
cbehave - A Behavior Driven Development Framework for C
|
||||
=======
|
||||
[](https://travis-ci.org/cxong/cbehave)
|
||||
|
||||
A demonstration using real C code:
|
||||
|
||||
#include "cbehave.h"
|
||||
|
||||
// Step 1: define your functions
|
||||
int add(int a, int b);
|
||||
|
||||
// Step 2: describe behaviour and the function calls
|
||||
FEATURE(addition, "Addition")
|
||||
SCENARIO("Add two numbers")
|
||||
GIVEN("we have two numbers 50 and 70")
|
||||
int a = 50;
|
||||
int b = 70;
|
||||
WHEN("we add them together")
|
||||
int r = add(a, b);
|
||||
THEN("the result should be 120")
|
||||
SHOULD_INT_EQUAL(r, 120);
|
||||
SCENARIO_END
|
||||
FEATURE_END
|
||||
|
||||
// Step 3: write empty implementations of functions
|
||||
int add(int a, int b)
|
||||
{
|
||||
// Step 5: write code to make the behaviour pass
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// Step 4: run tests and watch them fail (and succeed later)
|
||||
CBEHAVE_RUN("Calculator Features are as below:", TEST_FEATURE(addition))
|
||||
|
||||
Introduction
|
||||
-------------
|
||||
CBehave - A Behavior Driven Development Framework for C.
|
||||
|
||||
Main Features
|
||||
-------------
|
||||
|
||||
- use the "feature + scenario" structure (inspired by Cucumber)
|
||||
- use classical "given-when-then" template to describe behavior scenarios
|
||||
- support mock
|
||||
|
||||
Example Output
|
||||
-------------
|
||||
|
||||
*******************************************************************
|
||||
CBEHAVE -- A Behavior Driven Development Framework for C
|
||||
By Tony Bai
|
||||
*******************************************************************
|
||||
Strstr Features are as belows:
|
||||
Feature: strstr
|
||||
Scenario: The strstr finds the first occurrence of the substring in the source string
|
||||
Given A source string: Lionel Messi is a great football player
|
||||
When we use strstr to find the first occurrence of [football]
|
||||
Then We should get the string: [football player]
|
||||
Scenario: If strstr could not find the first occurrence of the substring, it will return NULL
|
||||
Given A source string: FC Barcelona is a great football club.
|
||||
When we use strstr to find the first occurrence of [AC Milan]
|
||||
Then We should get no string but a NULL
|
||||
Summary:
|
||||
features: [1/1]
|
||||
scenarios: [2/2]
|
||||
|
||||
Build
|
||||
------
|
||||
|
||||
To run the examples:
|
||||
|
||||
- Clone the project
|
||||
- cmake cbehave/examples
|
||||
|
||||
To use cbehave in your CMake project:
|
||||
|
||||
- include the cbehave directory
|
||||
- link against `cbehave`
|
||||
516
3rdparty/tinydir/tests/cbehave/apr_ring.h
vendored
Normal file
516
3rdparty/tinydir/tests/cbehave/apr_ring.h
vendored
Normal file
@@ -0,0 +1,516 @@
|
||||
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code draws heavily from the 4.4BSD <sys/queue.h> macros
|
||||
* and Dean Gaudet's "splim/ring.h".
|
||||
* <http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/sys/queue.h>
|
||||
* <http://www.arctic.org/~dean/splim/>
|
||||
*
|
||||
* We'd use Dean's code directly if we could guarantee the
|
||||
* availability of inline functions.
|
||||
*/
|
||||
|
||||
#ifndef APR_RING_H
|
||||
#define APR_RING_H
|
||||
|
||||
/**
|
||||
* @file apr_ring.h
|
||||
* @brief APR Rings
|
||||
*/
|
||||
|
||||
/*
|
||||
* for offsetof()
|
||||
*
|
||||
* !here the apr_general.h has been removed, Tony Bai
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#define APR_OFFSETOF(s_type,field) offsetof(s_type,field)
|
||||
|
||||
/**
|
||||
* @defgroup apr_ring Ring Macro Implementations
|
||||
* @ingroup APR
|
||||
* A ring is a kind of doubly-linked list that can be manipulated
|
||||
* without knowing where its head is.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Ring Element
|
||||
*
|
||||
* A ring element struct is linked to the other elements in the ring
|
||||
* through its ring entry field, e.g.
|
||||
* <pre>
|
||||
* struct my_element_t {
|
||||
* APR_RING_ENTRY(my_element_t) link;
|
||||
* int foo;
|
||||
* char *bar;
|
||||
* };
|
||||
* </pre>
|
||||
*
|
||||
* An element struct may be put on more than one ring if it has more
|
||||
* than one APR_RING_ENTRY field. Each APR_RING_ENTRY has a corresponding
|
||||
* APR_RING_HEAD declaration.
|
||||
*
|
||||
* @warning For strict C standards compliance you should put the APR_RING_ENTRY
|
||||
* first in the element struct unless the head is always part of a larger
|
||||
* object with enough earlier fields to accommodate the offsetof() used
|
||||
* to compute the ring sentinel below. You can usually ignore this caveat.
|
||||
*/
|
||||
#define APR_RING_ENTRY(elem) \
|
||||
struct { \
|
||||
struct elem * volatile next; \
|
||||
struct elem * volatile prev; \
|
||||
}
|
||||
|
||||
/**
|
||||
* The Ring Head
|
||||
*
|
||||
* Each ring is managed via its head, which is a struct declared like this:
|
||||
* <pre>
|
||||
* APR_RING_HEAD(my_ring_t, my_element_t);
|
||||
* struct my_ring_t ring, *ringp;
|
||||
* </pre>
|
||||
*
|
||||
* This struct looks just like the element link struct so that we can
|
||||
* be sure that the typecasting games will work as expected.
|
||||
*
|
||||
* The first element in the ring is next after the head, and the last
|
||||
* element is just before the head.
|
||||
*/
|
||||
#define APR_RING_HEAD(head, elem) \
|
||||
struct head { \
|
||||
struct elem *next; \
|
||||
struct elem *prev; \
|
||||
}
|
||||
|
||||
/**
|
||||
* The Ring Sentinel
|
||||
*
|
||||
* This is the magic pointer value that occurs before the first and
|
||||
* after the last elements in the ring, computed from the address of
|
||||
* the ring's head. The head itself isn't an element, but in order to
|
||||
* get rid of all the special cases when dealing with the ends of the
|
||||
* ring, we play typecasting games to make it look like one.
|
||||
*
|
||||
* Here is a diagram to illustrate the arrangements of the next and
|
||||
* prev pointers of each element in a single ring. Note that they point
|
||||
* to the start of each element, not to the APR_RING_ENTRY structure.
|
||||
*
|
||||
* <pre>
|
||||
* +->+------+<-+ +->+------+<-+ +->+------+<-+
|
||||
* | |struct| | | |struct| | | |struct| |
|
||||
* / | elem | \/ | elem | \/ | elem | \
|
||||
* ... | | /\ | | /\ | | ...
|
||||
* +------+ | | +------+ | | +------+
|
||||
* ...--|prev | | +--|ring | | +--|prev |
|
||||
* | next|--+ | entry|--+ | next|--...
|
||||
* +------+ +------+ +------+
|
||||
* | etc. | | etc. | | etc. |
|
||||
* : : : : : :
|
||||
* </pre>
|
||||
*
|
||||
* The APR_RING_HEAD is nothing but a bare APR_RING_ENTRY. The prev
|
||||
* and next pointers in the first and last elements don't actually
|
||||
* point to the head, they point to a phantom place called the
|
||||
* sentinel. Its value is such that last->next->next == first because
|
||||
* the offset from the sentinel to the head's next pointer is the same
|
||||
* as the offset from the start of an element to its next pointer.
|
||||
* This also works in the opposite direction.
|
||||
*
|
||||
* <pre>
|
||||
* last first
|
||||
* +->+------+<-+ +->sentinel<-+ +->+------+<-+
|
||||
* | |struct| | | | | |struct| |
|
||||
* / | elem | \/ \/ | elem | \
|
||||
* ... | | /\ /\ | | ...
|
||||
* +------+ | | +------+ | | +------+
|
||||
* ...--|prev | | +--|ring | | +--|prev |
|
||||
* | next|--+ | head|--+ | next|--...
|
||||
* +------+ +------+ +------+
|
||||
* | etc. | | etc. |
|
||||
* : : : :
|
||||
* </pre>
|
||||
*
|
||||
* Note that the offset mentioned above is different for each kind of
|
||||
* ring that the element may be on, and each kind of ring has a unique
|
||||
* name for its APR_RING_ENTRY in each element, and has its own type
|
||||
* for its APR_RING_HEAD.
|
||||
*
|
||||
* Note also that if the offset is non-zero (which is required if an
|
||||
* element has more than one APR_RING_ENTRY), the unreality of the
|
||||
* sentinel may have bad implications on very perverse implementations
|
||||
* of C -- see the warning in APR_RING_ENTRY.
|
||||
*
|
||||
* @param hp The head of the ring
|
||||
* @param elem The name of the element struct
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_SENTINEL(hp, elem, link) \
|
||||
(struct elem *)((char *)(&(hp)->next) - APR_OFFSETOF(struct elem, link))
|
||||
|
||||
/**
|
||||
* The first element of the ring
|
||||
* @param hp The head of the ring
|
||||
*/
|
||||
#define APR_RING_FIRST(hp) (hp)->next
|
||||
/**
|
||||
* The last element of the ring
|
||||
* @param hp The head of the ring
|
||||
*/
|
||||
#define APR_RING_LAST(hp) (hp)->prev
|
||||
/**
|
||||
* The next element in the ring
|
||||
* @param ep The current element
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_NEXT(ep, link) (ep)->link.next
|
||||
/**
|
||||
* The previous element in the ring
|
||||
* @param ep The current element
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_PREV(ep, link) (ep)->link.prev
|
||||
|
||||
|
||||
/**
|
||||
* Initialize a ring
|
||||
* @param hp The head of the ring
|
||||
* @param elem The name of the element struct
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_INIT(hp, elem, link) do { \
|
||||
APR_RING_FIRST((hp)) = APR_RING_SENTINEL((hp), elem, link); \
|
||||
APR_RING_LAST((hp)) = APR_RING_SENTINEL((hp), elem, link); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Determine if a ring is empty
|
||||
* @param hp The head of the ring
|
||||
* @param elem The name of the element struct
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
* @return true or false
|
||||
*/
|
||||
#define APR_RING_EMPTY(hp, elem, link) \
|
||||
(APR_RING_FIRST((hp)) == APR_RING_SENTINEL((hp), elem, link))
|
||||
|
||||
/**
|
||||
* Initialize a singleton element
|
||||
* @param ep The element
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_ELEM_INIT(ep, link) do { \
|
||||
APR_RING_NEXT((ep), link) = (ep); \
|
||||
APR_RING_PREV((ep), link) = (ep); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/**
|
||||
* Splice the sequence ep1..epN into the ring before element lep
|
||||
* (..lep.. becomes ..ep1..epN..lep..)
|
||||
* @warning This doesn't work for splicing before the first element or on
|
||||
* empty rings... see APR_RING_SPLICE_HEAD for one that does
|
||||
* @param lep Element in the ring to splice before
|
||||
* @param ep1 First element in the sequence to splice in
|
||||
* @param epN Last element in the sequence to splice in
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_SPLICE_BEFORE(lep, ep1, epN, link) do { \
|
||||
APR_RING_NEXT((epN), link) = (lep); \
|
||||
APR_RING_PREV((ep1), link) = APR_RING_PREV((lep), link); \
|
||||
APR_RING_NEXT(APR_RING_PREV((lep), link), link) = (ep1); \
|
||||
APR_RING_PREV((lep), link) = (epN); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Splice the sequence ep1..epN into the ring after element lep
|
||||
* (..lep.. becomes ..lep..ep1..epN..)
|
||||
* @warning This doesn't work for splicing after the last element or on
|
||||
* empty rings... see APR_RING_SPLICE_TAIL for one that does
|
||||
* @param lep Element in the ring to splice after
|
||||
* @param ep1 First element in the sequence to splice in
|
||||
* @param epN Last element in the sequence to splice in
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_SPLICE_AFTER(lep, ep1, epN, link) do { \
|
||||
APR_RING_PREV((ep1), link) = (lep); \
|
||||
APR_RING_NEXT((epN), link) = APR_RING_NEXT((lep), link); \
|
||||
APR_RING_PREV(APR_RING_NEXT((lep), link), link) = (epN); \
|
||||
APR_RING_NEXT((lep), link) = (ep1); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Insert the element nep into the ring before element lep
|
||||
* (..lep.. becomes ..nep..lep..)
|
||||
* @warning This doesn't work for inserting before the first element or on
|
||||
* empty rings... see APR_RING_INSERT_HEAD for one that does
|
||||
* @param lep Element in the ring to insert before
|
||||
* @param nep Element to insert
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_INSERT_BEFORE(lep, nep, link) \
|
||||
APR_RING_SPLICE_BEFORE((lep), (nep), (nep), link)
|
||||
|
||||
/**
|
||||
* Insert the element nep into the ring after element lep
|
||||
* (..lep.. becomes ..lep..nep..)
|
||||
* @warning This doesn't work for inserting after the last element or on
|
||||
* empty rings... see APR_RING_INSERT_TAIL for one that does
|
||||
* @param lep Element in the ring to insert after
|
||||
* @param nep Element to insert
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_INSERT_AFTER(lep, nep, link) \
|
||||
APR_RING_SPLICE_AFTER((lep), (nep), (nep), link)
|
||||
|
||||
|
||||
/**
|
||||
* Splice the sequence ep1..epN into the ring before the first element
|
||||
* (..hp.. becomes ..hp..ep1..epN..)
|
||||
* @param hp Head of the ring
|
||||
* @param ep1 First element in the sequence to splice in
|
||||
* @param epN Last element in the sequence to splice in
|
||||
* @param elem The name of the element struct
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_SPLICE_HEAD(hp, ep1, epN, elem, link) \
|
||||
APR_RING_SPLICE_AFTER(APR_RING_SENTINEL((hp), elem, link), \
|
||||
(ep1), (epN), link)
|
||||
|
||||
/**
|
||||
* Splice the sequence ep1..epN into the ring after the last element
|
||||
* (..hp.. becomes ..ep1..epN..hp..)
|
||||
* @param hp Head of the ring
|
||||
* @param ep1 First element in the sequence to splice in
|
||||
* @param epN Last element in the sequence to splice in
|
||||
* @param elem The name of the element struct
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_SPLICE_TAIL(hp, ep1, epN, elem, link) \
|
||||
APR_RING_SPLICE_BEFORE(APR_RING_SENTINEL((hp), elem, link), \
|
||||
(ep1), (epN), link)
|
||||
|
||||
/**
|
||||
* Insert the element nep into the ring before the first element
|
||||
* (..hp.. becomes ..hp..nep..)
|
||||
* @param hp Head of the ring
|
||||
* @param nep Element to insert
|
||||
* @param elem The name of the element struct
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_INSERT_HEAD(hp, nep, elem, link) \
|
||||
APR_RING_SPLICE_HEAD((hp), (nep), (nep), elem, link)
|
||||
|
||||
/**
|
||||
* Insert the element nep into the ring after the last element
|
||||
* (..hp.. becomes ..nep..hp..)
|
||||
* @param hp Head of the ring
|
||||
* @param nep Element to insert
|
||||
* @param elem The name of the element struct
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_INSERT_TAIL(hp, nep, elem, link) \
|
||||
APR_RING_SPLICE_TAIL((hp), (nep), (nep), elem, link)
|
||||
|
||||
/**
|
||||
* Concatenate ring h2 onto the end of ring h1, leaving h2 empty.
|
||||
* @param h1 Head of the ring to concatenate onto
|
||||
* @param h2 Head of the ring to concatenate
|
||||
* @param elem The name of the element struct
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_CONCAT(h1, h2, elem, link) do { \
|
||||
if (!APR_RING_EMPTY((h2), elem, link)) { \
|
||||
APR_RING_SPLICE_BEFORE(APR_RING_SENTINEL((h1), elem, link), \
|
||||
APR_RING_FIRST((h2)), \
|
||||
APR_RING_LAST((h2)), link); \
|
||||
APR_RING_INIT((h2), elem, link); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Prepend ring h2 onto the beginning of ring h1, leaving h2 empty.
|
||||
* @param h1 Head of the ring to prepend onto
|
||||
* @param h2 Head of the ring to prepend
|
||||
* @param elem The name of the element struct
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_PREPEND(h1, h2, elem, link) do { \
|
||||
if (!APR_RING_EMPTY((h2), elem, link)) { \
|
||||
APR_RING_SPLICE_AFTER(APR_RING_SENTINEL((h1), elem, link), \
|
||||
APR_RING_FIRST((h2)), \
|
||||
APR_RING_LAST((h2)), link); \
|
||||
APR_RING_INIT((h2), elem, link); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Unsplice a sequence of elements from a ring
|
||||
* @warning The unspliced sequence is left with dangling pointers at either end
|
||||
* @param ep1 First element in the sequence to unsplice
|
||||
* @param epN Last element in the sequence to unsplice
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_UNSPLICE(ep1, epN, link) do { \
|
||||
APR_RING_NEXT(APR_RING_PREV((ep1), link), link) = \
|
||||
APR_RING_NEXT((epN), link); \
|
||||
APR_RING_PREV(APR_RING_NEXT((epN), link), link) = \
|
||||
APR_RING_PREV((ep1), link); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Remove a single element from a ring
|
||||
* @warning The unspliced element is left with dangling pointers at either end
|
||||
* @param ep Element to remove
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_REMOVE(ep, link) \
|
||||
APR_RING_UNSPLICE((ep), (ep), link)
|
||||
|
||||
/**
|
||||
* Iterate over a ring
|
||||
* @param ep The current element
|
||||
* @param head The head of the ring
|
||||
* @param elem The name of the element struct
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_FOREACH(ep, head, elem, link) \
|
||||
for (ep = APR_RING_FIRST(head); \
|
||||
ep != APR_RING_SENTINEL(head, elem, link); \
|
||||
ep = APR_RING_NEXT(ep, link))
|
||||
|
||||
/**
|
||||
* Iterate over a ring safe against removal of the current element
|
||||
* @param ep1 The current element
|
||||
* @param ep2 Iteration cursor
|
||||
* @param head The head of the ring
|
||||
* @param elem The name of the element struct
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_FOREACH_SAFE(ep1, ep2, head, elem, link) \
|
||||
for (ep1 = APR_RING_FIRST(head), ep2 = APR_RING_NEXT(ep1, link); \
|
||||
ep1 != APR_RING_SENTINEL(head, elem, link); \
|
||||
ep1 = ep2, ep2 = APR_RING_NEXT(ep1, link))
|
||||
|
||||
/* Debugging tools: */
|
||||
|
||||
#ifdef APR_RING_DEBUG
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define APR_RING_CHECK_ONE(msg, ptr) \
|
||||
fprintf(stderr, "*** %s %p\n", msg, ptr)
|
||||
|
||||
#define APR_RING_CHECK(hp, elem, link, msg) \
|
||||
APR_RING_CHECK_ELEM(APR_RING_SENTINEL(hp, elem, link), elem, link, msg)
|
||||
|
||||
#define APR_RING_CHECK_ELEM(ep, elem, link, msg) do { \
|
||||
struct elem *start = (ep); \
|
||||
struct elem *here = start; \
|
||||
fprintf(stderr, "*** ring check start -- %s\n", msg); \
|
||||
do { \
|
||||
fprintf(stderr, "\telem %p\n", here); \
|
||||
fprintf(stderr, "\telem->next %p\n", \
|
||||
APR_RING_NEXT(here, link)); \
|
||||
fprintf(stderr, "\telem->prev %p\n", \
|
||||
APR_RING_PREV(here, link)); \
|
||||
fprintf(stderr, "\telem->next->prev %p\n", \
|
||||
APR_RING_PREV(APR_RING_NEXT(here, link), link)); \
|
||||
fprintf(stderr, "\telem->prev->next %p\n", \
|
||||
APR_RING_NEXT(APR_RING_PREV(here, link), link)); \
|
||||
if (APR_RING_PREV(APR_RING_NEXT(here, link), link) != here) { \
|
||||
fprintf(stderr, "\t*** elem->next->prev != elem\n"); \
|
||||
break; \
|
||||
} \
|
||||
if (APR_RING_NEXT(APR_RING_PREV(here, link), link) != here) { \
|
||||
fprintf(stderr, "\t*** elem->prev->next != elem\n"); \
|
||||
break; \
|
||||
} \
|
||||
here = APR_RING_NEXT(here, link); \
|
||||
} while (here != start); \
|
||||
fprintf(stderr, "*** ring check end\n"); \
|
||||
} while (0)
|
||||
|
||||
#define APR_RING_CHECK_CONSISTENCY(hp, elem, link) \
|
||||
APR_RING_CHECK_ELEM_CONSISTENCY(APR_RING_SENTINEL(hp, elem, link),\
|
||||
elem, link)
|
||||
|
||||
#define APR_RING_CHECK_ELEM_CONSISTENCY(ep, elem, link) do { \
|
||||
struct elem *start = (ep); \
|
||||
struct elem *here = start; \
|
||||
do { \
|
||||
assert(APR_RING_PREV(APR_RING_NEXT(here, link), link) == here); \
|
||||
assert(APR_RING_NEXT(APR_RING_PREV(here, link), link) == here); \
|
||||
here = APR_RING_NEXT(here, link); \
|
||||
} while (here != start); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
/**
|
||||
* Print a single pointer value to STDERR
|
||||
* (This is a no-op unless APR_RING_DEBUG is defined.)
|
||||
* @param msg Descriptive message
|
||||
* @param ptr Pointer value to print
|
||||
*/
|
||||
#define APR_RING_CHECK_ONE(msg, ptr)
|
||||
/**
|
||||
* Dump all ring pointers to STDERR, starting with the head and looping all
|
||||
* the way around the ring back to the head. Aborts if an inconsistency
|
||||
* is found.
|
||||
* (This is a no-op unless APR_RING_DEBUG is defined.)
|
||||
* @param hp Head of the ring
|
||||
* @param elem The name of the element struct
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
* @param msg Descriptive message
|
||||
*/
|
||||
#define APR_RING_CHECK(hp, elem, link, msg)
|
||||
/**
|
||||
* Loops around a ring and checks all the pointers for consistency. Pops
|
||||
* an assertion if any inconsistency is found. Same idea as APR_RING_CHECK()
|
||||
* except that it's silent if all is well.
|
||||
* (This is a no-op unless APR_RING_DEBUG is defined.)
|
||||
* @param hp Head of the ring
|
||||
* @param elem The name of the element struct
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_CHECK_CONSISTENCY(hp, elem, link)
|
||||
/**
|
||||
* Dump all ring pointers to STDERR, starting with the given element and
|
||||
* looping all the way around the ring back to that element. Aborts if
|
||||
* an inconsistency is found.
|
||||
* (This is a no-op unless APR_RING_DEBUG is defined.)
|
||||
* @param ep The element
|
||||
* @param elem The name of the element struct
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
* @param msg Descriptive message
|
||||
*/
|
||||
#define APR_RING_CHECK_ELEM(ep, elem, link, msg)
|
||||
/**
|
||||
* Loops around a ring, starting with the given element, and checks all
|
||||
* the pointers for consistency. Pops an assertion if any inconsistency
|
||||
* is found. Same idea as APR_RING_CHECK_ELEM() except that it's silent
|
||||
* if all is well.
|
||||
* (This is a no-op unless APR_RING_DEBUG is defined.)
|
||||
* @param ep The element
|
||||
* @param elem The name of the element struct
|
||||
* @param link The name of the APR_RING_ENTRY in the element struct
|
||||
*/
|
||||
#define APR_RING_CHECK_ELEM_CONSISTENCY(ep, elem, link)
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif /* !APR_RING_H */
|
||||
419
3rdparty/tinydir/tests/cbehave/cbehave.c
vendored
Normal file
419
3rdparty/tinydir/tests/cbehave/cbehave.c
vendored
Normal file
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Tony Bai <bigwhite.cn@gmail.com>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <rlutil/rlutil.h>
|
||||
|
||||
#include "cbehave.h"
|
||||
|
||||
|
||||
cbehave_scope_e cbehave_scope;
|
||||
|
||||
static cbehave_symbol_head_t _symbol_list;
|
||||
|
||||
static cbehave_symbol_t* lookup_symbol(const char *symbol_name, int obj_type);
|
||||
static void add_value(cbehave_symbol_t *s, void *value, int count);
|
||||
static cbehave_value_t* get_value(cbehave_symbol_t *s);
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define DEFAULT_COLOR BLACK
|
||||
#else
|
||||
#define DEFAULT_COLOR GREY
|
||||
#endif
|
||||
|
||||
void should_int_equal(int actual, int expected,
|
||||
void *state, const char *file,
|
||||
int line) {
|
||||
int *_scenario_state = (int*)state;
|
||||
if ((expected) != (actual)) {
|
||||
(*_scenario_state) = 1;
|
||||
setColor(RED);
|
||||
printf("\t\t\t%s:%d: Failed: expected[%d], but actual[%d].\n",
|
||||
file,
|
||||
line,
|
||||
expected,
|
||||
actual);
|
||||
setColor(DEFAULT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
void should_int_gt(int val1, int val2,
|
||||
void *state,
|
||||
const char *file, int line) {
|
||||
int *_scenario_state = (int*)state;
|
||||
if ((val1) <= (val2)) {
|
||||
(*_scenario_state) = 1;
|
||||
setColor(RED);
|
||||
printf("\t\t\t%s:%d: Failed: [%d] not greater than [%d].\n",
|
||||
file,
|
||||
line,
|
||||
val1,
|
||||
val2);
|
||||
setColor(DEFAULT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
void should_int_lt(int val1, int val2,
|
||||
void *state,
|
||||
const char *file, int line) {
|
||||
int *_scenario_state = (int*)state;
|
||||
if ((val1) >= (val2)) {
|
||||
(*_scenario_state) = 1;
|
||||
setColor(RED);
|
||||
printf("\t\t\t%s:%d: Failed: [%d] not less than [%d].\n",
|
||||
file,
|
||||
line,
|
||||
val1,
|
||||
val2);
|
||||
setColor(DEFAULT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
void should_int_ge(int val1, int val2,
|
||||
void *state,
|
||||
const char *file, int line) {
|
||||
int *_scenario_state = (int*)state;
|
||||
if ((val1) < (val2)) {
|
||||
(*_scenario_state) = 1;
|
||||
setColor(RED);
|
||||
printf("\t\t\t%s:%d: Failed: [%d] not greater than or equal to [%d].\n",
|
||||
file,
|
||||
line,
|
||||
val1,
|
||||
val2);
|
||||
setColor(DEFAULT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
void should_int_le(int val1, int val2,
|
||||
void *state,
|
||||
const char *file, int line) {
|
||||
int *_scenario_state = (int*)state;
|
||||
if ((val1) > (val2)) {
|
||||
(*_scenario_state) = 1;
|
||||
setColor(RED);
|
||||
printf("\t\t\t%s:%d: Failed: [%d] not less than or equal to [%d].\n",
|
||||
file,
|
||||
line,
|
||||
val1,
|
||||
val2);
|
||||
setColor(DEFAULT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
void should_str_equal(const char *actual, const char *expected, void *state,
|
||||
const char *file, int line) {
|
||||
|
||||
int *_scenario_state = (int*)state;
|
||||
/*
|
||||
* both pointers are NULL or pointing to the same memory
|
||||
*/
|
||||
if (expected == actual) return;
|
||||
|
||||
if (expected && actual) {
|
||||
if (!strcmp(expected, actual)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
(*_scenario_state) = 1;
|
||||
setColor(RED);
|
||||
printf("\t\t\t%s:%d: Failed: expected[%s], but actual[%s].\n",
|
||||
file, line,
|
||||
expected ? expected : "NULL",
|
||||
actual ? actual : "NULL");
|
||||
setColor(DEFAULT_COLOR);
|
||||
}
|
||||
|
||||
void should_mem_equal(const void *actual, const void *expected, size_t size, void *state,
|
||||
const char *file, int line) {
|
||||
|
||||
int *_scenario_state = (int*)state;
|
||||
/*
|
||||
* both pointers are NULL or pointing to the same memory
|
||||
*/
|
||||
if (expected == actual) return;
|
||||
|
||||
if (expected && actual) {
|
||||
if (!memcmp(expected, actual, size)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
(*_scenario_state) = 1;
|
||||
setColor(RED);
|
||||
printf("\t\t\t%s:%d: Failed: memory does not equal.\n", file, line);
|
||||
setColor(DEFAULT_COLOR);
|
||||
}
|
||||
|
||||
void should_be_bool(bool actual, bool expected, void *state, const char *file, int line) {
|
||||
int *_scenario_state = (int*)state;
|
||||
if (actual != expected) {
|
||||
(*_scenario_state) = 1;
|
||||
setColor(RED);
|
||||
printf("\t\t\t%s:%d: Failed: actual[%d] is not a %s value.\n",
|
||||
file,
|
||||
line,
|
||||
actual,
|
||||
expected ? "true" : "false");
|
||||
setColor(DEFAULT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
void cbehave_given_entry(const char *prompt, const char *str, void *state) {
|
||||
(void)(state);
|
||||
printf("\t\t%s %s\n", prompt, str);
|
||||
}
|
||||
|
||||
void cbehave_when_entry(const char *prompt, const char *str, void *state) {
|
||||
(void)(state);
|
||||
printf("\t\t%s %s\n", prompt, str);
|
||||
}
|
||||
|
||||
void cbehave_then_entry(const char *prompt, const char *str, void *state) {
|
||||
(void)(state);
|
||||
printf("\t\t%s %s\n", prompt, str);
|
||||
}
|
||||
|
||||
void cbehave_scenario_entry(const char *str, void *state) {
|
||||
cbehave_state *cur = (cbehave_state*)state;
|
||||
cur->total_scenarios++;
|
||||
|
||||
printf("\tScenario: %s\n", str);
|
||||
}
|
||||
|
||||
void cbehave_feature_entry(const char *str, void *old_state, void *state) {
|
||||
cbehave_state *cur = (cbehave_state*)state;
|
||||
cbehave_state *old = (cbehave_state*)old_state;
|
||||
|
||||
cur->total_features++;
|
||||
memcpy(old, state, sizeof(*cur));
|
||||
|
||||
printf("\nFeature: %s\n", str);
|
||||
}
|
||||
|
||||
void cbehave_given_exit(void *state) {
|
||||
(void)(state);
|
||||
}
|
||||
|
||||
void cbehave_when_exit(void *state) {
|
||||
(void)(state);
|
||||
}
|
||||
|
||||
void cbehave_then_exit(void *state) {
|
||||
(void)(state);
|
||||
}
|
||||
|
||||
void cbehave_scenario_exit(void *scenario_state, void *state) {
|
||||
int *_scenario_state = (int*)scenario_state;
|
||||
cbehave_state *cur = (cbehave_state*)state;
|
||||
|
||||
if ((*_scenario_state) == 1) {
|
||||
cur->failed_scenarios++;
|
||||
}
|
||||
}
|
||||
|
||||
void cbehave_feature_exit(void *old_state, void *state) {
|
||||
cbehave_state *cur = (cbehave_state*)state;
|
||||
cbehave_state *old = (cbehave_state*)old_state;
|
||||
|
||||
if (cur->failed_scenarios > old->failed_scenarios) {
|
||||
cur->failed_features++;
|
||||
}
|
||||
}
|
||||
|
||||
void cbehave_feature_return(const char *file, int line, int ret, void *state) {
|
||||
cbehave_state *cur = (cbehave_state*)state;
|
||||
|
||||
cur->failed_scenarios++;
|
||||
|
||||
setColor(RED);
|
||||
printf("\t\t\t%s:%d: Exception occurred, error code: %d.\n",
|
||||
file,
|
||||
line,
|
||||
ret);
|
||||
setColor(DEFAULT_COLOR);
|
||||
}
|
||||
|
||||
|
||||
int _cbehave_runner(const char *description, const cbehave_feature *features, int count) {
|
||||
cbehave_state *state = NULL;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
printf("%s\n", CBEHAVE_LOGO);
|
||||
printf("%s\n", description);
|
||||
|
||||
state = (cbehave_state*)malloc(sizeof(*state));
|
||||
if (!state) {
|
||||
setColor(RED);
|
||||
printf("\t%s:%d: Failed to alloc memory, error code: %d.\n",
|
||||
__FILE__, __LINE__, errno);
|
||||
setColor(DEFAULT_COLOR);
|
||||
return -1;
|
||||
}
|
||||
memset(state, 0, sizeof(*state));
|
||||
|
||||
APR_RING_INIT(&_symbol_list, cbehave_symbol_t, link);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
features[i].func(state);
|
||||
}
|
||||
|
||||
printf("\nSummary: \n");
|
||||
if (state->failed_features) {
|
||||
setColor(RED);
|
||||
} else {
|
||||
setColor(GREEN);
|
||||
}
|
||||
printf("\tfeatures: [%d/%d]\n",
|
||||
state->total_features - state->failed_features, state->total_features);
|
||||
setColor(DEFAULT_COLOR);
|
||||
|
||||
if (state->failed_scenarios) {
|
||||
setColor(RED);
|
||||
} else {
|
||||
setColor(GREEN);
|
||||
}
|
||||
printf("\tscenarios: [%d/%d]\n", state->total_scenarios - state->failed_scenarios, state->total_scenarios);
|
||||
setColor(DEFAULT_COLOR);
|
||||
|
||||
ret = (state->failed_features == 0) ? 0 : 1;
|
||||
|
||||
if (state) {
|
||||
free(state);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* cbehave_mock_obj(const char *fcname,
|
||||
int lineno,
|
||||
const char *fname,
|
||||
int obj_type) {
|
||||
cbehave_symbol_t *s = NULL;
|
||||
cbehave_value_t *v = NULL;
|
||||
void *p;
|
||||
|
||||
s = lookup_symbol(fcname, obj_type);
|
||||
if (!s) {
|
||||
printf("\t[CBEHAVE]: can't find the symbol: <%s> which is being mocked!, %d line in file %s\n",
|
||||
fcname, lineno, fname);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (s->always_return_flag) return s->value;
|
||||
|
||||
v = get_value(s);
|
||||
if (!v) {
|
||||
printf("\t[CBEHAVE]: you have not set the value of mock obj <%s>!, %d line in file %s\n",
|
||||
fcname, lineno, fname);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
p = v->value;
|
||||
|
||||
APR_RING_REMOVE(v, link);
|
||||
free(v);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
void cbehave_mock_obj_return(const char *symbol_name,
|
||||
void *value,
|
||||
const char *fcname,
|
||||
int lineno,
|
||||
const char *fname,
|
||||
int obj_type,
|
||||
int count) {
|
||||
|
||||
cbehave_symbol_t *s = lookup_symbol(symbol_name, obj_type);
|
||||
(void)(fcname);
|
||||
(void)(lineno);
|
||||
(void)(fname);
|
||||
if (!s) {
|
||||
errno = 0;
|
||||
s = (cbehave_symbol_t*)malloc(sizeof(*s));
|
||||
if (!s) {
|
||||
printf("\t[CBEHAVE]: malloc error!, errcode[%d]\n", errno);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
memset(s, 0, sizeof(*s));
|
||||
strcpy(s->desc, symbol_name);
|
||||
s->obj_type = obj_type;
|
||||
APR_RING_INIT(&(s->value_list), cbehave_value_t, link);
|
||||
APR_RING_INSERT_TAIL(&_symbol_list, s, cbehave_symbol_t, link);
|
||||
}
|
||||
|
||||
add_value(s, value, count);
|
||||
}
|
||||
|
||||
static cbehave_symbol_t* lookup_symbol(const char *symbol_name, int obj_type) {
|
||||
cbehave_symbol_t *s = NULL;
|
||||
|
||||
APR_RING_FOREACH(s, &_symbol_list, cbehave_symbol_t, link) {
|
||||
if (s != NULL) {
|
||||
if ((s->obj_type == obj_type)
|
||||
&& (!strcmp(s->desc, symbol_name))) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void add_value(cbehave_symbol_t *s, void *value, int count) {
|
||||
cbehave_value_t *v = NULL;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* make the obj always to return one same value
|
||||
* until another cbehave_mock_obj_return invoking
|
||||
*/
|
||||
if (count == -1) {
|
||||
s->always_return_flag = 1;
|
||||
s->value = value;
|
||||
return;
|
||||
}
|
||||
|
||||
s->always_return_flag = 0;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
errno = 0;
|
||||
v = (cbehave_value_t*)malloc(sizeof(*v));
|
||||
if (!v) {
|
||||
printf("\t[CBEHAVE]: malloc error!, errcode[%d]\n", errno);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
memset(v, 0, sizeof(*v));
|
||||
v->value = value;
|
||||
|
||||
APR_RING_INSERT_TAIL(&(s->value_list), v, cbehave_value_t, link);
|
||||
}
|
||||
}
|
||||
|
||||
static cbehave_value_t* get_value(cbehave_symbol_t *s) {
|
||||
cbehave_value_t *v = NULL;
|
||||
|
||||
v = APR_RING_FIRST(&(s->value_list));
|
||||
return v;
|
||||
}
|
||||
|
||||
270
3rdparty/tinydir/tests/cbehave/cbehave.h
vendored
Normal file
270
3rdparty/tinydir/tests/cbehave/cbehave.h
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Tony Bai <bigwhite.cn@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file cbehave.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _CBEHAVE_H
|
||||
#define _CBEHAVE_H
|
||||
|
||||
#ifdef _cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef _cplusplus
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#include "apr_ring.h"
|
||||
|
||||
#define CBEHAVE_LOGO \
|
||||
"*******************************************************************\n\
|
||||
\tCBEHAVE -- A Behavior Driven Development Framework for C\n\
|
||||
\t\t\t By Tony Bai\n\
|
||||
*******************************************************************"
|
||||
|
||||
#define CBEHAVE_MAX_NAME_LEN 128
|
||||
|
||||
typedef struct cbehave_state {
|
||||
int total_features;
|
||||
int failed_features;
|
||||
int total_scenarios;
|
||||
int failed_scenarios;
|
||||
} cbehave_state;
|
||||
|
||||
typedef enum {
|
||||
CBEHAVE_SCOPE_NONE,
|
||||
CBEHAVE_SCOPE_GIVEN,
|
||||
CBEHAVE_SCOPE_WHEN,
|
||||
CBEHAVE_SCOPE_THEN
|
||||
} cbehave_scope_e;
|
||||
extern cbehave_scope_e cbehave_scope;
|
||||
|
||||
#define END_SCOPE \
|
||||
if (cbehave_scope == CBEHAVE_SCOPE_GIVEN) { \
|
||||
cbehave_given_exit(_state); \
|
||||
} else if (cbehave_scope == CBEHAVE_SCOPE_WHEN) { \
|
||||
cbehave_when_exit(_state); \
|
||||
} else if (cbehave_scope == CBEHAVE_SCOPE_THEN) { \
|
||||
cbehave_then_exit(_state); \
|
||||
}
|
||||
#define GIVEN_IMPL(x, _prompt) \
|
||||
END_SCOPE \
|
||||
cbehave_scope = CBEHAVE_SCOPE_GIVEN; \
|
||||
cbehave_given_entry(_prompt, x, _state);
|
||||
#define GIVEN(x) GIVEN_IMPL(x, "Given")
|
||||
#define GIVEN_END
|
||||
|
||||
#define WHEN_IMPL(x, _prompt) \
|
||||
END_SCOPE \
|
||||
cbehave_scope = CBEHAVE_SCOPE_WHEN; \
|
||||
cbehave_when_entry(_prompt, x, _state);
|
||||
#define WHEN(x) WHEN_IMPL(x, "When")
|
||||
#define WHEN_END
|
||||
|
||||
#define THEN_IMPL(x, _prompt) \
|
||||
END_SCOPE \
|
||||
cbehave_scope = CBEHAVE_SCOPE_THEN; \
|
||||
cbehave_then_entry(_prompt, x, _state);
|
||||
#define THEN(x) THEN_IMPL(x, "Then")
|
||||
#define THEN_END
|
||||
|
||||
#define AND(x) \
|
||||
if (cbehave_scope == CBEHAVE_SCOPE_GIVEN) { \
|
||||
GIVEN_IMPL(x, "And") \
|
||||
} else if (cbehave_scope == CBEHAVE_SCOPE_WHEN) { \
|
||||
WHEN_IMPL(x, "And") \
|
||||
} else if (cbehave_scope == CBEHAVE_SCOPE_THEN) { \
|
||||
THEN_IMPL(x, "And") \
|
||||
}
|
||||
|
||||
#define SCENARIO(x) { \
|
||||
int _scenario_state = 0; \
|
||||
cbehave_scenario_entry(x, _state); \
|
||||
cbehave_scope = CBEHAVE_SCOPE_NONE;
|
||||
|
||||
#define SCENARIO_END \
|
||||
END_SCOPE \
|
||||
cbehave_scenario_exit(&_scenario_state, _state); \
|
||||
}
|
||||
|
||||
#define FEATURE(idx, x) static void _cbehave_feature_##idx(void *_state) { \
|
||||
cbehave_state _old_state; \
|
||||
cbehave_feature_entry(x, &_old_state, _state);
|
||||
|
||||
#define FEATURE_END \
|
||||
_feature_over: \
|
||||
cbehave_feature_exit(&_old_state, _state); \
|
||||
}
|
||||
|
||||
#define ASSERT(cond, ret) \
|
||||
if (!(cond)) {\
|
||||
cbehave_feature_return(__FILE__, __LINE__, ret, _state); \
|
||||
goto _feature_over; \
|
||||
}\
|
||||
|
||||
#define TEST_FEATURE(name) {_cbehave_feature_##name}
|
||||
|
||||
#define SHOULD_INT_EQUAL(actual, expected) do { \
|
||||
should_int_equal((actual), (expected), &_scenario_state, __FILE__, __LINE__); \
|
||||
} while(0)
|
||||
|
||||
#define SHOULD_INT_GT(val1, val2) do { \
|
||||
should_int_gt((val1), (val2), &_scenario_state, __FILE__, __LINE__); \
|
||||
} while(0)
|
||||
|
||||
#define SHOULD_INT_LT(val1, val2) do { \
|
||||
should_int_lt((val1), (val2), &_scenario_state, __FILE__, __LINE__); \
|
||||
} while(0)
|
||||
|
||||
#define SHOULD_INT_GE(val1, val2) do { \
|
||||
should_int_ge((val1), (val2), &_scenario_state, __FILE__, __LINE__); \
|
||||
} while(0)
|
||||
|
||||
#define SHOULD_INT_LE(val1, val2) do { \
|
||||
should_int_le((val1), (val2), &_scenario_state, __FILE__, __LINE__); \
|
||||
} while(0)
|
||||
|
||||
#define SHOULD_STR_EQUAL(actual, expected) do { \
|
||||
should_str_equal((actual), (expected), &_scenario_state, __FILE__, __LINE__); \
|
||||
} while(0)
|
||||
|
||||
#define SHOULD_MEM_EQUAL(actual, expected, size) do { \
|
||||
should_mem_equal((actual), (expected), (size), &_scenario_state, __FILE__, __LINE__); \
|
||||
} while(0)
|
||||
|
||||
#define SHOULD_BE_TRUE(actual) do { \
|
||||
should_be_bool((actual), true, &_scenario_state, __FILE__, __LINE__); \
|
||||
} while(0)
|
||||
|
||||
#define SHOULD_BE_FALSE(actual) do { \
|
||||
should_be_bool((actual), false, &_scenario_state, __FILE__, __LINE__); \
|
||||
} while(0)
|
||||
|
||||
#define CBEHAVE_RUN(_description, ...)\
|
||||
int main(void) {\
|
||||
cbehave_feature _cfeatures[] = {__VA_ARGS__};\
|
||||
return cbehave_runner(_description, _cfeatures);\
|
||||
}
|
||||
|
||||
#define cbehave_runner(str, features) \
|
||||
_cbehave_runner(str, features, sizeof(features)/sizeof(features[0]))
|
||||
|
||||
typedef struct cbehave_feature {
|
||||
void (*func)(void *state);
|
||||
} cbehave_feature;
|
||||
|
||||
int _cbehave_runner(const char *description, const cbehave_feature *features, int count);
|
||||
void should_int_equal(int actual, int expected,
|
||||
void *state,
|
||||
const char *file, int line);
|
||||
void should_int_gt(int val1, int val2,
|
||||
void *state,
|
||||
const char *file, int line);
|
||||
void should_int_lt(int val1, int val2,
|
||||
void *state,
|
||||
const char *file, int line);
|
||||
void should_int_ge(int val1, int val2,
|
||||
void *state,
|
||||
const char *file, int line);
|
||||
void should_int_le(int val1, int val2,
|
||||
void *state,
|
||||
const char *file, int line);
|
||||
void should_str_equal(const char *actual, const char *expected, void *state,
|
||||
const char *file, int line);
|
||||
void should_mem_equal(const void *actual, const void *expected, size_t size, void *state,
|
||||
const char *file, int line);
|
||||
void should_be_bool(bool actual, bool expected, void *state, const char *file, int line);
|
||||
|
||||
void cbehave_given_entry(const char *prompt, const char *str, void *state);
|
||||
void cbehave_when_entry(const char *prompt, const char *str, void *state);
|
||||
void cbehave_then_entry(const char *prompt, const char *str, void *state);
|
||||
void cbehave_scenario_entry(const char *str, void *state);
|
||||
void cbehave_feature_entry(const char *str, void *old_state, void *state);
|
||||
|
||||
void cbehave_given_exit(void *state);
|
||||
void cbehave_when_exit(void *state);
|
||||
void cbehave_then_exit(void *state);
|
||||
void cbehave_scenario_exit(void *scenario_state, void *state);
|
||||
void cbehave_feature_exit(void *old_state, void *state);
|
||||
void cbehave_feature_return(const char *file, int line, int ret, void *state);
|
||||
|
||||
/*
|
||||
* mock symbol list
|
||||
*
|
||||
* ------------
|
||||
* | symbol-#0|-> value_list
|
||||
* ------------
|
||||
* | symbol-#1|-> value_list
|
||||
* ------------
|
||||
* | symbol-#2|-> value_list
|
||||
* ------------
|
||||
* | ... ... |-> value_list
|
||||
* ------------
|
||||
* | symbol-#n|-> value_list
|
||||
* ------------
|
||||
*/
|
||||
|
||||
typedef struct cbehave_value_t {
|
||||
APR_RING_ENTRY(cbehave_value_t) link;
|
||||
void *value;
|
||||
} cbehave_value_t;
|
||||
typedef APR_RING_HEAD(cbehave_value_head_t, cbehave_value_t) cbehave_value_head_t;
|
||||
|
||||
typedef struct cbehave_symbol_t {
|
||||
APR_RING_ENTRY(cbehave_symbol_t) link;
|
||||
char desc[CBEHAVE_MAX_NAME_LEN];
|
||||
int obj_type;
|
||||
int always_return_flag; /* 1: always return the same value; 0(default) */
|
||||
void* value;
|
||||
cbehave_value_head_t value_list;
|
||||
} cbehave_symbol_t;
|
||||
typedef APR_RING_HEAD(cbehave_symbol_head_t, cbehave_symbol_t) cbehave_symbol_head_t;
|
||||
|
||||
void* cbehave_mock_obj(const char *fcname, int lineno, const char *fname, int obj_type);
|
||||
void cbehave_mock_obj_return(const char *symbol_name, void *value, const char *fcname,
|
||||
int lineno, const char *fname, int obj_type, int count);
|
||||
|
||||
#define MOCK_ARG 0x0
|
||||
#define MOCK_RETV 0x1
|
||||
|
||||
#define CBEHAVE_MOCK_ARG() cbehave_mock_obj(__FUNCTION__, __LINE__, __FILE__, MOCK_ARG)
|
||||
#define CBEHAVE_MOCK_RETV() cbehave_mock_obj(__FUNCTION__, __LINE__, __FILE__, MOCK_RETV)
|
||||
|
||||
#define CBEHAVE_ARG_RETURN(fcname, value) do { \
|
||||
cbehave_mock_obj_return(#fcname, (void*)value, __FUNCTION__, __LINE__, __FILE__, MOCK_ARG, 1); \
|
||||
} while(0);
|
||||
|
||||
#define CBEHAVE_ARG_RETURN_COUNT(fcname, value, count) do { \
|
||||
cbehave_mock_obj_return(#fcname, (void*)value, __FUNCTION__, __LINE__, __FILE__, MOCK_ARG, count); \
|
||||
} while(0);
|
||||
|
||||
#define CBEHAVE_RETV_RETURN(fcname, value) do { \
|
||||
cbehave_mock_obj_return(#fcname, (void*)value, __FUNCTION__, __LINE__, __FILE__, MOCK_RETV, 1); \
|
||||
} while(0);
|
||||
|
||||
#define CBEHAVE_RETV_RETURN_COUNT(fcname, value, count) do { \
|
||||
cbehave_mock_obj_return(#fcname, (void*)value, __FUNCTION__, __LINE__, __FILE__, MOCK_RETV, count); \
|
||||
} while(0);
|
||||
|
||||
|
||||
#ifdef _cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _CBEHAVE_H */
|
||||
728
3rdparty/tinydir/tests/cbehave/rlutil/rlutil.h
vendored
Normal file
728
3rdparty/tinydir/tests/cbehave/rlutil/rlutil.h
vendored
Normal file
@@ -0,0 +1,728 @@
|
||||
#pragma once
|
||||
/**
|
||||
* File: rlutil.h
|
||||
*
|
||||
* About: Description
|
||||
* This file provides some useful utilities for console mode
|
||||
* roguelike game development with C and C++. It is aimed to
|
||||
* be cross-platform (at least Windows and Linux).
|
||||
*
|
||||
* About: Copyright
|
||||
* (C) 2010 Tapio Vierros
|
||||
*
|
||||
* About: Licensing
|
||||
* See <License>
|
||||
*/
|
||||
|
||||
|
||||
/// Define: RLUTIL_USE_ANSI
|
||||
/// Define this to use ANSI escape sequences also on Windows
|
||||
/// (defaults to using WinAPI instead).
|
||||
#if 0
|
||||
#define RLUTIL_USE_ANSI
|
||||
#endif
|
||||
|
||||
/// Define: RLUTIL_STRING_T
|
||||
/// Define/typedef this to your preference to override rlutil's string type.
|
||||
///
|
||||
/// Defaults to std::string with C++ and char* with C.
|
||||
#if 0
|
||||
#define RLUTIL_STRING_T char*
|
||||
#endif
|
||||
|
||||
#ifndef RLUTIL_INLINE
|
||||
#ifdef _MSC_VER
|
||||
#define RLUTIL_INLINE __inline
|
||||
#else
|
||||
#define RLUTIL_INLINE static __inline__
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
/// Common C++ headers
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstdio> // for getch()
|
||||
/// Namespace forward declarations
|
||||
namespace rlutil {
|
||||
RLUTIL_INLINE void locate(int x, int y);
|
||||
}
|
||||
#else
|
||||
#include <stdio.h> // for getch() / printf()
|
||||
#include <string.h> // for strlen()
|
||||
RLUTIL_INLINE void locate(int x, int y); // Forward declare for C to avoid warnings
|
||||
#endif // __cplusplus
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h> // for WinAPI and Sleep()
|
||||
#define _NO_OLDNAMES // for MinGW compatibility
|
||||
#include <conio.h> // for getch() and kbhit()
|
||||
#define getch _getch
|
||||
#define kbhit _kbhit
|
||||
#else
|
||||
#include <termios.h> // for getch() and kbhit()
|
||||
#include <unistd.h> // for getch(), kbhit() and (u)sleep()
|
||||
#include <sys/ioctl.h> // for getkey()
|
||||
#include <sys/types.h> // for kbhit()
|
||||
#include <sys/time.h> // for kbhit()
|
||||
|
||||
/// Function: getch
|
||||
/// Get character without waiting for Return to be pressed.
|
||||
/// Windows has this in conio.h
|
||||
RLUTIL_INLINE int getch(void) {
|
||||
// Here be magic.
|
||||
struct termios oldt, newt;
|
||||
int ch;
|
||||
tcgetattr(STDIN_FILENO, &oldt);
|
||||
newt = oldt;
|
||||
newt.c_lflag &= ~(ICANON | ECHO);
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
||||
ch = getchar();
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
||||
return ch;
|
||||
}
|
||||
|
||||
/// Function: kbhit
|
||||
/// Determines if keyboard has been hit.
|
||||
/// Windows has this in conio.h
|
||||
RLUTIL_INLINE int kbhit(void) {
|
||||
// Here be dragons.
|
||||
static struct termios oldt, newt;
|
||||
int cnt = 0;
|
||||
tcgetattr(STDIN_FILENO, &oldt);
|
||||
newt = oldt;
|
||||
newt.c_lflag &= ~(ICANON | ECHO);
|
||||
newt.c_iflag = 0; // input mode
|
||||
newt.c_oflag = 0; // output mode
|
||||
newt.c_cc[VMIN] = 1; // minimum time to wait
|
||||
newt.c_cc[VTIME] = 1; // minimum characters to wait for
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
||||
ioctl(0, FIONREAD, &cnt); // Read count
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 100;
|
||||
select(STDIN_FILENO+1, NULL, NULL, NULL, &tv); // A small time delay
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
||||
return cnt; // Return number of characters
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
#ifndef gotoxy
|
||||
/// Function: gotoxy
|
||||
/// Same as <rlutil.locate>.
|
||||
RLUTIL_INLINE void gotoxy(int x, int y) {
|
||||
#ifdef __cplusplus
|
||||
rlutil::
|
||||
#endif
|
||||
locate(x,y);
|
||||
}
|
||||
#endif // gotoxy
|
||||
|
||||
#ifdef __cplusplus
|
||||
/// Namespace: rlutil
|
||||
/// In C++ all functions except <getch>, <kbhit> and <gotoxy> are arranged
|
||||
/// under namespace rlutil. That is because some platforms have them defined
|
||||
/// outside of rlutil.
|
||||
namespace rlutil {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Defs: Internal typedefs and macros
|
||||
* RLUTIL_STRING_T - String type depending on which one of C or C++ is used
|
||||
* RLUTIL_PRINT(str) - Printing macro independent of C/C++
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
#ifndef RLUTIL_STRING_T
|
||||
typedef std::string RLUTIL_STRING_T;
|
||||
#endif // RLUTIL_STRING_T
|
||||
|
||||
#define RLUTIL_PRINT(st) do { std::cout << st; } while(false)
|
||||
#else // __cplusplus
|
||||
#ifndef RLUTIL_STRING_T
|
||||
typedef const char* RLUTIL_STRING_T;
|
||||
#endif // RLUTIL_STRING_T
|
||||
|
||||
#define RLUTIL_PRINT(st) printf("%s", st)
|
||||
#endif // __cplusplus
|
||||
|
||||
/**
|
||||
* Enums: Color codes
|
||||
*
|
||||
* BLACK - Black
|
||||
* BLUE - Blue
|
||||
* GREEN - Green
|
||||
* CYAN - Cyan
|
||||
* RED - Red
|
||||
* MAGENTA - Magenta / purple
|
||||
* BROWN - Brown / dark yellow
|
||||
* GREY - Grey / dark white
|
||||
* DARKGREY - Dark grey / light black
|
||||
* LIGHTBLUE - Light blue
|
||||
* LIGHTGREEN - Light green
|
||||
* LIGHTCYAN - Light cyan
|
||||
* LIGHTRED - Light red
|
||||
* LIGHTMAGENTA - Light magenta / light purple
|
||||
* YELLOW - Yellow (bright)
|
||||
* WHITE - White (bright)
|
||||
*/
|
||||
enum {
|
||||
BLACK,
|
||||
BLUE,
|
||||
GREEN,
|
||||
CYAN,
|
||||
RED,
|
||||
MAGENTA,
|
||||
BROWN,
|
||||
GREY,
|
||||
DARKGREY,
|
||||
LIGHTBLUE,
|
||||
LIGHTGREEN,
|
||||
LIGHTCYAN,
|
||||
LIGHTRED,
|
||||
LIGHTMAGENTA,
|
||||
YELLOW,
|
||||
WHITE
|
||||
};
|
||||
|
||||
/**
|
||||
* Consts: ANSI escape strings
|
||||
*
|
||||
* ANSI_CLS - Clears screen
|
||||
* ANSI_ATTRIBUTE_RESET - Resets all attributes
|
||||
* ANSI_CURSOR_HIDE - Hides the cursor
|
||||
* ANSI_CURSOR_SHOW - Shows the cursor
|
||||
* ANSI_CURSOR_HOME - Moves the cursor home (0,0)
|
||||
* ANSI_BLACK - Black
|
||||
* ANSI_RED - Red
|
||||
* ANSI_GREEN - Green
|
||||
* ANSI_BROWN - Brown / dark yellow
|
||||
* ANSI_BLUE - Blue
|
||||
* ANSI_MAGENTA - Magenta / purple
|
||||
* ANSI_CYAN - Cyan
|
||||
* ANSI_GREY - Grey / dark white
|
||||
* ANSI_DARKGREY - Dark grey / light black
|
||||
* ANSI_LIGHTRED - Light red
|
||||
* ANSI_LIGHTGREEN - Light green
|
||||
* ANSI_YELLOW - Yellow (bright)
|
||||
* ANSI_LIGHTBLUE - Light blue
|
||||
* ANSI_LIGHTMAGENTA - Light magenta / light purple
|
||||
* ANSI_LIGHTCYAN - Light cyan
|
||||
* ANSI_WHITE - White (bright)
|
||||
* ANSI_BACKGROUND_BLACK - Black background
|
||||
* ANSI_BACKGROUND_RED - Red background
|
||||
* ANSI_BACKGROUND_GREEN - Green background
|
||||
* ANSI_BACKGROUND_YELLOW - Yellow background
|
||||
* ANSI_BACKGROUND_BLUE - Blue background
|
||||
* ANSI_BACKGROUND_MAGENTA - Magenta / purple background
|
||||
* ANSI_BACKGROUND_CYAN - Cyan background
|
||||
* ANSI_BACKGROUND_WHITE - White background
|
||||
*/
|
||||
const RLUTIL_STRING_T ANSI_CLS = "\033[2J\033[3J";
|
||||
const RLUTIL_STRING_T ANSI_ATTRIBUTE_RESET = "\033[0m";
|
||||
const RLUTIL_STRING_T ANSI_CURSOR_HIDE = "\033[?25l";
|
||||
const RLUTIL_STRING_T ANSI_CURSOR_SHOW = "\033[?25h";
|
||||
const RLUTIL_STRING_T ANSI_CURSOR_HOME = "\033[H";
|
||||
const RLUTIL_STRING_T ANSI_BLACK = "\033[22;30m";
|
||||
const RLUTIL_STRING_T ANSI_RED = "\033[22;31m";
|
||||
const RLUTIL_STRING_T ANSI_GREEN = "\033[22;32m";
|
||||
const RLUTIL_STRING_T ANSI_BROWN = "\033[22;33m";
|
||||
const RLUTIL_STRING_T ANSI_BLUE = "\033[22;34m";
|
||||
const RLUTIL_STRING_T ANSI_MAGENTA = "\033[22;35m";
|
||||
const RLUTIL_STRING_T ANSI_CYAN = "\033[22;36m";
|
||||
const RLUTIL_STRING_T ANSI_GREY = "\033[22;37m";
|
||||
const RLUTIL_STRING_T ANSI_DARKGREY = "\033[01;30m";
|
||||
const RLUTIL_STRING_T ANSI_LIGHTRED = "\033[01;31m";
|
||||
const RLUTIL_STRING_T ANSI_LIGHTGREEN = "\033[01;32m";
|
||||
const RLUTIL_STRING_T ANSI_YELLOW = "\033[01;33m";
|
||||
const RLUTIL_STRING_T ANSI_LIGHTBLUE = "\033[01;34m";
|
||||
const RLUTIL_STRING_T ANSI_LIGHTMAGENTA = "\033[01;35m";
|
||||
const RLUTIL_STRING_T ANSI_LIGHTCYAN = "\033[01;36m";
|
||||
const RLUTIL_STRING_T ANSI_WHITE = "\033[01;37m";
|
||||
const RLUTIL_STRING_T ANSI_BACKGROUND_BLACK = "\033[40m";
|
||||
const RLUTIL_STRING_T ANSI_BACKGROUND_RED = "\033[41m";
|
||||
const RLUTIL_STRING_T ANSI_BACKGROUND_GREEN = "\033[42m";
|
||||
const RLUTIL_STRING_T ANSI_BACKGROUND_YELLOW = "\033[43m";
|
||||
const RLUTIL_STRING_T ANSI_BACKGROUND_BLUE = "\033[44m";
|
||||
const RLUTIL_STRING_T ANSI_BACKGROUND_MAGENTA = "\033[45m";
|
||||
const RLUTIL_STRING_T ANSI_BACKGROUND_CYAN = "\033[46m";
|
||||
const RLUTIL_STRING_T ANSI_BACKGROUND_WHITE = "\033[47m";
|
||||
// Remaining colors not supported as background colors
|
||||
|
||||
/**
|
||||
* Enums: Key codes for keyhit()
|
||||
*
|
||||
* KEY_ESCAPE - Escape
|
||||
* KEY_ENTER - Enter
|
||||
* KEY_SPACE - Space
|
||||
* KEY_INSERT - Insert
|
||||
* KEY_HOME - Home
|
||||
* KEY_END - End
|
||||
* KEY_DELETE - Delete
|
||||
* KEY_PGUP - PageUp
|
||||
* KEY_PGDOWN - PageDown
|
||||
* KEY_UP - Up arrow
|
||||
* KEY_DOWN - Down arrow
|
||||
* KEY_LEFT - Left arrow
|
||||
* KEY_RIGHT - Right arrow
|
||||
* KEY_F1 - F1
|
||||
* KEY_F2 - F2
|
||||
* KEY_F3 - F3
|
||||
* KEY_F4 - F4
|
||||
* KEY_F5 - F5
|
||||
* KEY_F6 - F6
|
||||
* KEY_F7 - F7
|
||||
* KEY_F8 - F8
|
||||
* KEY_F9 - F9
|
||||
* KEY_F10 - F10
|
||||
* KEY_F11 - F11
|
||||
* KEY_F12 - F12
|
||||
* KEY_NUMDEL - Numpad del
|
||||
* KEY_NUMPAD0 - Numpad 0
|
||||
* KEY_NUMPAD1 - Numpad 1
|
||||
* KEY_NUMPAD2 - Numpad 2
|
||||
* KEY_NUMPAD3 - Numpad 3
|
||||
* KEY_NUMPAD4 - Numpad 4
|
||||
* KEY_NUMPAD5 - Numpad 5
|
||||
* KEY_NUMPAD6 - Numpad 6
|
||||
* KEY_NUMPAD7 - Numpad 7
|
||||
* KEY_NUMPAD8 - Numpad 8
|
||||
* KEY_NUMPAD9 - Numpad 9
|
||||
*/
|
||||
enum {
|
||||
KEY_ESCAPE = 0,
|
||||
KEY_ENTER = 1,
|
||||
KEY_SPACE = 32,
|
||||
|
||||
KEY_INSERT = 2,
|
||||
KEY_HOME = 3,
|
||||
KEY_PGUP = 4,
|
||||
KEY_DELETE = 5,
|
||||
KEY_END = 6,
|
||||
KEY_PGDOWN = 7,
|
||||
|
||||
KEY_UP = 14,
|
||||
KEY_DOWN = 15,
|
||||
KEY_LEFT = 16,
|
||||
KEY_RIGHT = 17,
|
||||
|
||||
KEY_F1 = 18,
|
||||
KEY_F2 = 19,
|
||||
KEY_F3 = 20,
|
||||
KEY_F4 = 21,
|
||||
KEY_F5 = 22,
|
||||
KEY_F6 = 23,
|
||||
KEY_F7 = 24,
|
||||
KEY_F8 = 25,
|
||||
KEY_F9 = 26,
|
||||
KEY_F10 = 27,
|
||||
KEY_F11 = 28,
|
||||
KEY_F12 = 29,
|
||||
|
||||
KEY_NUMDEL = 30,
|
||||
KEY_NUMPAD0 = 31,
|
||||
KEY_NUMPAD1 = 127,
|
||||
KEY_NUMPAD2 = 128,
|
||||
KEY_NUMPAD3 = 129,
|
||||
KEY_NUMPAD4 = 130,
|
||||
KEY_NUMPAD5 = 131,
|
||||
KEY_NUMPAD6 = 132,
|
||||
KEY_NUMPAD7 = 133,
|
||||
KEY_NUMPAD8 = 134,
|
||||
KEY_NUMPAD9 = 135
|
||||
};
|
||||
|
||||
/// Function: getkey
|
||||
/// Reads a key press (blocking) and returns a key code.
|
||||
///
|
||||
/// See <Key codes for keyhit()>
|
||||
///
|
||||
/// Note:
|
||||
/// Only Arrows, Esc, Enter and Space are currently working properly.
|
||||
RLUTIL_INLINE int getkey(void) {
|
||||
#ifndef _WIN32
|
||||
int cnt = kbhit(); // for ANSI escapes processing
|
||||
#endif
|
||||
int k = getch();
|
||||
switch(k) {
|
||||
case 0: {
|
||||
int kk;
|
||||
switch (kk = getch()) {
|
||||
case 71: return KEY_NUMPAD7;
|
||||
case 72: return KEY_NUMPAD8;
|
||||
case 73: return KEY_NUMPAD9;
|
||||
case 75: return KEY_NUMPAD4;
|
||||
case 77: return KEY_NUMPAD6;
|
||||
case 79: return KEY_NUMPAD1;
|
||||
case 80: return KEY_NUMPAD4;
|
||||
case 81: return KEY_NUMPAD3;
|
||||
case 82: return KEY_NUMPAD0;
|
||||
case 83: return KEY_NUMDEL;
|
||||
default: return kk-59+KEY_F1; // Function keys
|
||||
}}
|
||||
case 224: {
|
||||
int kk;
|
||||
switch (kk = getch()) {
|
||||
case 71: return KEY_HOME;
|
||||
case 72: return KEY_UP;
|
||||
case 73: return KEY_PGUP;
|
||||
case 75: return KEY_LEFT;
|
||||
case 77: return KEY_RIGHT;
|
||||
case 79: return KEY_END;
|
||||
case 80: return KEY_DOWN;
|
||||
case 81: return KEY_PGDOWN;
|
||||
case 82: return KEY_INSERT;
|
||||
case 83: return KEY_DELETE;
|
||||
default: return kk-123+KEY_F1; // Function keys
|
||||
}}
|
||||
case 13: return KEY_ENTER;
|
||||
#ifdef _WIN32
|
||||
case 27: return KEY_ESCAPE;
|
||||
#else // _WIN32
|
||||
case 155: // single-character CSI
|
||||
case 27: {
|
||||
// Process ANSI escape sequences
|
||||
if (cnt >= 3 && getch() == '[') {
|
||||
switch (k = getch()) {
|
||||
case 'A': return KEY_UP;
|
||||
case 'B': return KEY_DOWN;
|
||||
case 'C': return KEY_RIGHT;
|
||||
case 'D': return KEY_LEFT;
|
||||
}
|
||||
} else return KEY_ESCAPE;
|
||||
}
|
||||
#endif // _WIN32
|
||||
default: return k;
|
||||
}
|
||||
}
|
||||
|
||||
/// Function: nb_getch
|
||||
/// Non-blocking getch(). Returns 0 if no key was pressed.
|
||||
RLUTIL_INLINE int nb_getch(void) {
|
||||
if (kbhit()) return getch();
|
||||
else return 0;
|
||||
}
|
||||
|
||||
/// Function: getANSIColor
|
||||
/// Return ANSI color escape sequence for specified number 0-15.
|
||||
///
|
||||
/// See <Color Codes>
|
||||
RLUTIL_INLINE RLUTIL_STRING_T getANSIColor(const int c) {
|
||||
switch (c) {
|
||||
case BLACK : return ANSI_BLACK;
|
||||
case BLUE : return ANSI_BLUE; // non-ANSI
|
||||
case GREEN : return ANSI_GREEN;
|
||||
case CYAN : return ANSI_CYAN; // non-ANSI
|
||||
case RED : return ANSI_RED; // non-ANSI
|
||||
case MAGENTA : return ANSI_MAGENTA;
|
||||
case BROWN : return ANSI_BROWN;
|
||||
case GREY : return ANSI_GREY;
|
||||
case DARKGREY : return ANSI_DARKGREY;
|
||||
case LIGHTBLUE : return ANSI_LIGHTBLUE; // non-ANSI
|
||||
case LIGHTGREEN : return ANSI_LIGHTGREEN;
|
||||
case LIGHTCYAN : return ANSI_LIGHTCYAN; // non-ANSI;
|
||||
case LIGHTRED : return ANSI_LIGHTRED; // non-ANSI;
|
||||
case LIGHTMAGENTA: return ANSI_LIGHTMAGENTA;
|
||||
case YELLOW : return ANSI_YELLOW; // non-ANSI
|
||||
case WHITE : return ANSI_WHITE;
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
/// Function: getANSIBackgroundColor
|
||||
/// Return ANSI background color escape sequence for specified number 0-15.
|
||||
///
|
||||
/// See <Color Codes>
|
||||
RLUTIL_INLINE RLUTIL_STRING_T getANSIBackgroundColor(const int c) {
|
||||
switch (c) {
|
||||
case BLACK : return ANSI_BACKGROUND_BLACK;
|
||||
case BLUE : return ANSI_BACKGROUND_BLUE;
|
||||
case GREEN : return ANSI_BACKGROUND_GREEN;
|
||||
case CYAN : return ANSI_BACKGROUND_CYAN;
|
||||
case RED : return ANSI_BACKGROUND_RED;
|
||||
case MAGENTA: return ANSI_BACKGROUND_MAGENTA;
|
||||
case BROWN : return ANSI_BACKGROUND_YELLOW;
|
||||
case GREY : return ANSI_BACKGROUND_WHITE;
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
/// Function: setColor
|
||||
/// Change color specified by number (Windows / QBasic colors).
|
||||
/// Don't change the background color
|
||||
///
|
||||
/// See <Color Codes>
|
||||
RLUTIL_INLINE void setColor(int c) {
|
||||
#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
|
||||
SetConsoleTextAttribute(hConsole, (csbi.wAttributes & 0xFFF0) | (WORD)c); // Foreground colors take up the least significant byte
|
||||
#else
|
||||
RLUTIL_PRINT(getANSIColor(c));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Function: setBackgroundColor
|
||||
/// Change background color specified by number (Windows / QBasic colors).
|
||||
/// Don't change the foreground color
|
||||
///
|
||||
/// See <Color Codes>
|
||||
RLUTIL_INLINE void setBackgroundColor(int c) {
|
||||
#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
|
||||
SetConsoleTextAttribute(hConsole, (csbi.wAttributes & 0xFF0F) | (((WORD)c) << 4)); // Background colors take up the second-least significant byte
|
||||
#else
|
||||
RLUTIL_PRINT(getANSIBackgroundColor(c));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Function: saveDefaultColor
|
||||
/// Call once to preserve colors for use in resetColor()
|
||||
/// on Windows without ANSI, no-op otherwise
|
||||
///
|
||||
/// See <Color Codes>
|
||||
/// See <resetColor>
|
||||
RLUTIL_INLINE int saveDefaultColor() {
|
||||
#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
|
||||
static char initialized = 0; // bool
|
||||
static WORD attributes;
|
||||
|
||||
if (!initialized) {
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
|
||||
attributes = csbi.wAttributes;
|
||||
initialized = 1;
|
||||
}
|
||||
return (int)attributes;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Function: resetColor
|
||||
/// Reset color to default
|
||||
/// Requires a call to saveDefaultColor() to set the defaults
|
||||
///
|
||||
/// See <Color Codes>
|
||||
/// See <setColor>
|
||||
/// See <saveDefaultColor>
|
||||
RLUTIL_INLINE void resetColor() {
|
||||
#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), (WORD)saveDefaultColor());
|
||||
#else
|
||||
RLUTIL_PRINT(ANSI_ATTRIBUTE_RESET);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Function: cls
|
||||
/// Clears screen, resets all attributes and moves cursor home.
|
||||
RLUTIL_INLINE void cls(void) {
|
||||
#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
|
||||
// Based on https://msdn.microsoft.com/en-us/library/windows/desktop/ms682022%28v=vs.85%29.aspx
|
||||
const HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
const COORD coordScreen = {0, 0};
|
||||
DWORD cCharsWritten;
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
const DWORD dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
|
||||
FillConsoleOutputCharacter(hConsole, (TCHAR)' ', dwConSize, coordScreen, &cCharsWritten);
|
||||
|
||||
GetConsoleScreenBufferInfo(hConsole, &csbi);
|
||||
FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);
|
||||
|
||||
SetConsoleCursorPosition(hConsole, coordScreen);
|
||||
#else
|
||||
RLUTIL_PRINT(ANSI_CLS);
|
||||
RLUTIL_PRINT(ANSI_CURSOR_HOME);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Function: locate
|
||||
/// Sets the cursor position to 1-based x,y.
|
||||
RLUTIL_INLINE void locate(int x, int y) {
|
||||
#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
|
||||
COORD coord;
|
||||
// TODO: clamping/assert for x/y <= 0?
|
||||
coord.X = (SHORT)(x - 1);
|
||||
coord.Y = (SHORT)(y - 1); // Windows uses 0-based coordinates
|
||||
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
|
||||
#else // _WIN32 || USE_ANSI
|
||||
#ifdef __cplusplus
|
||||
RLUTIL_PRINT("\033[" << y << ";" << x << "H");
|
||||
#else // __cplusplus
|
||||
char buf[32];
|
||||
sprintf(buf, "\033[%d;%df", y, x);
|
||||
RLUTIL_PRINT(buf);
|
||||
#endif // __cplusplus
|
||||
#endif // _WIN32 || USE_ANSI
|
||||
}
|
||||
|
||||
/// Function: setString
|
||||
/// Prints the supplied string without advancing the cursor
|
||||
#ifdef __cplusplus
|
||||
RLUTIL_INLINE void setString(const RLUTIL_STRING_T & str_) {
|
||||
const char * const str = str_.data();
|
||||
unsigned int len = str_.size();
|
||||
#else // __cplusplus
|
||||
RLUTIL_INLINE void setString(RLUTIL_STRING_T str) {
|
||||
unsigned int len = strlen(str);
|
||||
#endif // __cplusplus
|
||||
#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
|
||||
HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD numberOfCharsWritten;
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
|
||||
GetConsoleScreenBufferInfo(hConsoleOutput, &csbi);
|
||||
WriteConsoleOutputCharacter(hConsoleOutput, str, len, csbi.dwCursorPosition, &numberOfCharsWritten);
|
||||
#else // _WIN32 || USE_ANSI
|
||||
RLUTIL_PRINT(str);
|
||||
#ifdef __cplusplus
|
||||
RLUTIL_PRINT("\033[" << len << 'D');
|
||||
#else // __cplusplus
|
||||
char buf[3 + 20 + 1]; // 20 = max length of 64-bit unsigned int when printed as dec
|
||||
sprintf(buf, "\033[%uD", len);
|
||||
RLUTIL_PRINT(buf);
|
||||
#endif // __cplusplus
|
||||
#endif // _WIN32 || USE_ANSI
|
||||
}
|
||||
|
||||
/// Function: setChar
|
||||
/// Sets the character at the cursor without advancing the cursor
|
||||
RLUTIL_INLINE void setChar(char ch) {
|
||||
const char buf[] = {ch, 0};
|
||||
setString(buf);
|
||||
}
|
||||
|
||||
/// Function: setCursorVisibility
|
||||
/// Shows/hides the cursor.
|
||||
RLUTIL_INLINE void setCursorVisibility(char visible) {
|
||||
#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
|
||||
HANDLE hConsoleOutput = GetStdHandle( STD_OUTPUT_HANDLE );
|
||||
CONSOLE_CURSOR_INFO structCursorInfo;
|
||||
GetConsoleCursorInfo( hConsoleOutput, &structCursorInfo ); // Get current cursor size
|
||||
structCursorInfo.bVisible = (visible ? TRUE : FALSE);
|
||||
SetConsoleCursorInfo( hConsoleOutput, &structCursorInfo );
|
||||
#else // _WIN32 || USE_ANSI
|
||||
RLUTIL_PRINT((visible ? ANSI_CURSOR_SHOW : ANSI_CURSOR_HIDE));
|
||||
#endif // _WIN32 || USE_ANSI
|
||||
}
|
||||
|
||||
/// Function: hidecursor
|
||||
/// Hides the cursor.
|
||||
RLUTIL_INLINE void hidecursor(void) {
|
||||
setCursorVisibility(0);
|
||||
}
|
||||
|
||||
/// Function: showcursor
|
||||
/// Shows the cursor.
|
||||
RLUTIL_INLINE void showcursor(void) {
|
||||
setCursorVisibility(1);
|
||||
}
|
||||
|
||||
/// Function: msleep
|
||||
/// Waits given number of milliseconds before continuing.
|
||||
RLUTIL_INLINE void msleep(unsigned int ms) {
|
||||
#ifdef _WIN32
|
||||
Sleep(ms);
|
||||
#else
|
||||
// usleep argument must be under 1 000 000
|
||||
if (ms > 1000) sleep(ms/1000000);
|
||||
usleep((ms % 1000000) * 1000);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Function: trows
|
||||
/// Get the number of rows in the terminal window or -1 on error.
|
||||
RLUTIL_INLINE int trows(void) {
|
||||
#ifdef _WIN32
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
|
||||
return -1;
|
||||
else
|
||||
return csbi.srWindow.Bottom - csbi.srWindow.Top + 1; // Window height
|
||||
// return csbi.dwSize.Y; // Buffer height
|
||||
#else
|
||||
#ifdef TIOCGSIZE
|
||||
struct ttysize ts;
|
||||
ioctl(STDIN_FILENO, TIOCGSIZE, &ts);
|
||||
return ts.ts_lines;
|
||||
#elif defined(TIOCGWINSZ)
|
||||
struct winsize ts;
|
||||
ioctl(STDIN_FILENO, TIOCGWINSZ, &ts);
|
||||
return ts.ws_row;
|
||||
#else // TIOCGSIZE
|
||||
return -1;
|
||||
#endif // TIOCGSIZE
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
/// Function: tcols
|
||||
/// Get the number of columns in the terminal window or -1 on error.
|
||||
RLUTIL_INLINE int tcols(void) {
|
||||
#ifdef _WIN32
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
|
||||
return -1;
|
||||
else
|
||||
return csbi.srWindow.Right - csbi.srWindow.Left + 1; // Window width
|
||||
// return csbi.dwSize.X; // Buffer width
|
||||
#else
|
||||
#ifdef TIOCGSIZE
|
||||
struct ttysize ts;
|
||||
ioctl(STDIN_FILENO, TIOCGSIZE, &ts);
|
||||
return ts.ts_cols;
|
||||
#elif defined(TIOCGWINSZ)
|
||||
struct winsize ts;
|
||||
ioctl(STDIN_FILENO, TIOCGWINSZ, &ts);
|
||||
return ts.ws_col;
|
||||
#else // TIOCGSIZE
|
||||
return -1;
|
||||
#endif // TIOCGSIZE
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
/// Function: anykey
|
||||
/// Waits until a key is pressed.
|
||||
/// In C++, it either takes no arguments
|
||||
/// or a template-type-argument-deduced
|
||||
/// argument.
|
||||
/// In C, it takes a const char* representing
|
||||
/// the message to be displayed, or NULL
|
||||
/// for no message.
|
||||
#ifdef __cplusplus
|
||||
RLUTIL_INLINE void anykey() {
|
||||
getch();
|
||||
}
|
||||
|
||||
template <class T> void anykey(const T& msg) {
|
||||
RLUTIL_PRINT(msg);
|
||||
#else
|
||||
RLUTIL_INLINE void anykey(RLUTIL_STRING_T msg) {
|
||||
if (msg)
|
||||
RLUTIL_PRINT(msg);
|
||||
#endif // __cplusplus
|
||||
getch();
|
||||
}
|
||||
|
||||
// Classes are here at the end so that documentation is pretty.
|
||||
|
||||
#ifdef __cplusplus
|
||||
/// Class: CursorHider
|
||||
/// RAII OOP wrapper for <rlutil.hidecursor>.
|
||||
/// Hides the cursor and shows it again
|
||||
/// when the object goes out of scope.
|
||||
struct CursorHider {
|
||||
CursorHider() { hidecursor(); }
|
||||
~CursorHider() { showcursor(); }
|
||||
};
|
||||
|
||||
} // namespace rlutil
|
||||
#endif
|
||||
22
3rdparty/tinydir/tests/file_open_test.c
vendored
Normal file
22
3rdparty/tinydir/tests/file_open_test.c
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <tinydir.h>
|
||||
#include "cbehave.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
FEATURE(file_open, "File open")
|
||||
SCENARIO("Open file in current directory")
|
||||
GIVEN("a file in the current directory")
|
||||
char name[4096];
|
||||
make_temp_file("temp_file_", name);
|
||||
WHEN("we open it")
|
||||
tinydir_file file;
|
||||
int r = tinydir_file_open(&file, name);
|
||||
THEN("the result should be successful")
|
||||
SHOULD_INT_EQUAL(r, 0);
|
||||
remove(name);
|
||||
SCENARIO_END
|
||||
FEATURE_END
|
||||
|
||||
CBEHAVE_RUN("File open:", TEST_FEATURE(file_open))
|
||||
29
3rdparty/tinydir/tests/util.c
vendored
Normal file
29
3rdparty/tinydir/tests/util.c
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
|
||||
void make_temp_file(const char *prefix, char *out)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
if (GetTempFileName(".", prefix, 0, out) != 0)
|
||||
{
|
||||
// Strip the ".\\" prefix
|
||||
if (strncmp(out, ".\\", 2) == 0)
|
||||
{
|
||||
memmove(out, out + 2, strlen(out));
|
||||
}
|
||||
// Create file
|
||||
fclose(fopen(out, "w"));
|
||||
}
|
||||
#else
|
||||
sprintf(out, "%sXXXXXX", prefix);
|
||||
close(mkstemp(out));
|
||||
#endif
|
||||
}
|
||||
3
3rdparty/tinydir/tests/util.h
vendored
Normal file
3
3rdparty/tinydir/tests/util.h
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void make_temp_file(const char *prefix, char *out);
|
||||
55
3rdparty/tinydir/tests/windows_unicode_test.c
vendored
Normal file
55
3rdparty/tinydir/tests/windows_unicode_test.c
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#define UNICODE
|
||||
#include <tinydir.h>
|
||||
#include "cbehave.h"
|
||||
|
||||
|
||||
#define TEST_PATH L"windows_unicode_test"
|
||||
#define TEST_PATH_A "windows_unicode_test"
|
||||
|
||||
void make_temp_file_utf16(const wchar_t *prefix, wchar_t *out)
|
||||
{
|
||||
if (GetTempFileNameW(TEST_PATH, prefix, 0, out) != 0)
|
||||
{
|
||||
// Create file
|
||||
fclose(_wfopen(out, L"w"));
|
||||
// Strip the ".\\" prefix
|
||||
if (wcsncmp(out, TEST_PATH L"\\", wcslen(TEST_PATH) + 1) == 0)
|
||||
{
|
||||
memmove(out, out + wcslen(TEST_PATH) + 1, wcslen(out));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FEATURE(windows_unicode, "Windows Unicode")
|
||||
SCENARIO("Open directory with unicode file names")
|
||||
GIVEN("a directory with unicode file names")
|
||||
CreateDirectoryW(TEST_PATH, NULL);
|
||||
wchar_t name[4096];
|
||||
make_temp_file_utf16(L"t📁", name);
|
||||
WHEN("we open the directory")
|
||||
tinydir_dir dir;
|
||||
int r = tinydir_open(&dir, TEST_PATH);
|
||||
THEN("the result should be successful")
|
||||
SHOULD_INT_EQUAL(r, 0);
|
||||
AND("iterating it, we should find the file")
|
||||
bool found = false;
|
||||
while (dir.has_next)
|
||||
{
|
||||
tinydir_file file;
|
||||
tinydir_readfile(&dir, &file);
|
||||
if (wcscmp((wchar_t *)file.name, name) == 0)
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
tinydir_next(&dir);
|
||||
}
|
||||
SHOULD_BE_TRUE(found);
|
||||
tinydir_close(&dir);
|
||||
_wremove(name);
|
||||
RemoveDirectoryW(TEST_PATH);
|
||||
SCENARIO_END
|
||||
FEATURE_END
|
||||
|
||||
CBEHAVE_RUN("Windows unicode:", TEST_FEATURE(windows_unicode))
|
||||
831
3rdparty/tinydir/tinydir.h
vendored
Normal file
831
3rdparty/tinydir/tinydir.h
vendored
Normal file
@@ -0,0 +1,831 @@
|
||||
/*
|
||||
Copyright (c) 2013-2019, tinydir authors:
|
||||
- Cong Xu
|
||||
- Lautis Sun
|
||||
- Baudouin Feildel
|
||||
- Andargor <andargor@yahoo.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef TINYDIR_H
|
||||
#define TINYDIR_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if ((defined _UNICODE) && !(defined UNICODE))
|
||||
#define UNICODE
|
||||
#endif
|
||||
|
||||
#if ((defined UNICODE) && !(defined _UNICODE))
|
||||
#define _UNICODE
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef _MSC_VER
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <windows.h>
|
||||
# include <tchar.h>
|
||||
# pragma warning(push)
|
||||
# pragma warning (disable : 4996)
|
||||
#else
|
||||
# include <dirent.h>
|
||||
# include <libgen.h>
|
||||
# include <sys/stat.h>
|
||||
# include <stddef.h>
|
||||
#endif
|
||||
#ifdef __MINGW32__
|
||||
# include <tchar.h>
|
||||
#endif
|
||||
|
||||
|
||||
/* types */
|
||||
|
||||
/* Windows UNICODE wide character support */
|
||||
#if defined _MSC_VER || defined __MINGW32__
|
||||
# define _tinydir_char_t TCHAR
|
||||
# define TINYDIR_STRING(s) _TEXT(s)
|
||||
# define _tinydir_strlen _tcslen
|
||||
# define _tinydir_strcpy _tcscpy
|
||||
# define _tinydir_strcat _tcscat
|
||||
# define _tinydir_strcmp _tcscmp
|
||||
# define _tinydir_strrchr _tcsrchr
|
||||
# define _tinydir_strncmp _tcsncmp
|
||||
#else
|
||||
# define _tinydir_char_t char
|
||||
# define TINYDIR_STRING(s) s
|
||||
# define _tinydir_strlen strlen
|
||||
# define _tinydir_strcpy strcpy
|
||||
# define _tinydir_strcat strcat
|
||||
# define _tinydir_strcmp strcmp
|
||||
# define _tinydir_strrchr strrchr
|
||||
# define _tinydir_strncmp strncmp
|
||||
#endif
|
||||
|
||||
#if (defined _MSC_VER || defined __MINGW32__)
|
||||
# include <windows.h>
|
||||
# define _TINYDIR_PATH_MAX MAX_PATH
|
||||
#elif defined __linux__
|
||||
# include <limits.h>
|
||||
# ifdef PATH_MAX
|
||||
# define _TINYDIR_PATH_MAX PATH_MAX
|
||||
# endif
|
||||
#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
||||
# include <sys/param.h>
|
||||
# if defined(BSD)
|
||||
# include <limits.h>
|
||||
# ifdef PATH_MAX
|
||||
# define _TINYDIR_PATH_MAX PATH_MAX
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef _TINYDIR_PATH_MAX
|
||||
#define _TINYDIR_PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/* extra chars for the "\\*" mask */
|
||||
# define _TINYDIR_PATH_EXTRA 2
|
||||
#else
|
||||
# define _TINYDIR_PATH_EXTRA 0
|
||||
#endif
|
||||
|
||||
#define _TINYDIR_FILENAME_MAX 256
|
||||
|
||||
#if (defined _MSC_VER || defined __MINGW32__)
|
||||
#define _TINYDIR_DRIVE_MAX 3
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define _TINYDIR_FUNC static __inline
|
||||
#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
|
||||
# define _TINYDIR_FUNC static __inline__
|
||||
#else
|
||||
# define _TINYDIR_FUNC static inline
|
||||
#endif
|
||||
|
||||
/* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */
|
||||
#ifdef TINYDIR_USE_READDIR_R
|
||||
|
||||
/* readdir_r is a POSIX-only function, and may not be available under various
|
||||
* environments/settings, e.g. MinGW. Use readdir fallback */
|
||||
#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\
|
||||
_POSIX_SOURCE
|
||||
# define _TINYDIR_HAS_READDIR_R
|
||||
#endif
|
||||
#if _POSIX_C_SOURCE >= 200112L
|
||||
# define _TINYDIR_HAS_FPATHCONF
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#if _BSD_SOURCE || _SVID_SOURCE || \
|
||||
(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
|
||||
# define _TINYDIR_HAS_DIRFD
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\
|
||||
defined _PC_NAME_MAX
|
||||
# define _TINYDIR_USE_FPATHCONF
|
||||
#endif
|
||||
#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\
|
||||
!(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX)
|
||||
# define _TINYDIR_USE_READDIR
|
||||
#endif
|
||||
|
||||
/* Use readdir by default */
|
||||
#else
|
||||
# define _TINYDIR_USE_READDIR
|
||||
#endif
|
||||
|
||||
/* MINGW32 has two versions of dirent, ASCII and UNICODE*/
|
||||
#ifndef _MSC_VER
|
||||
#if (defined __MINGW32__) && (defined _UNICODE)
|
||||
#define _TINYDIR_DIR _WDIR
|
||||
#define _tinydir_dirent _wdirent
|
||||
#define _tinydir_opendir _wopendir
|
||||
#define _tinydir_readdir _wreaddir
|
||||
#define _tinydir_closedir _wclosedir
|
||||
#else
|
||||
#define _TINYDIR_DIR DIR
|
||||
#define _tinydir_dirent dirent
|
||||
#define _tinydir_opendir opendir
|
||||
#define _tinydir_readdir readdir
|
||||
#define _tinydir_closedir closedir
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */
|
||||
#if defined(_TINYDIR_MALLOC) && defined(_TINYDIR_FREE)
|
||||
#elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE)
|
||||
#else
|
||||
#error "Either define both alloc and free or none of them!"
|
||||
#endif
|
||||
|
||||
#if !defined(_TINYDIR_MALLOC)
|
||||
#define _TINYDIR_MALLOC(_size) malloc(_size)
|
||||
#define _TINYDIR_FREE(_ptr) free(_ptr)
|
||||
#endif /* !defined(_TINYDIR_MALLOC) */
|
||||
|
||||
typedef struct tinydir_file
|
||||
{
|
||||
_tinydir_char_t path[_TINYDIR_PATH_MAX];
|
||||
_tinydir_char_t name[_TINYDIR_FILENAME_MAX];
|
||||
_tinydir_char_t *extension;
|
||||
int is_dir;
|
||||
int is_reg;
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#ifdef __MINGW32__
|
||||
struct _stat _s;
|
||||
#else
|
||||
struct stat _s;
|
||||
#endif
|
||||
#endif
|
||||
} tinydir_file;
|
||||
|
||||
typedef struct tinydir_dir
|
||||
{
|
||||
_tinydir_char_t path[_TINYDIR_PATH_MAX];
|
||||
int has_next;
|
||||
size_t n_files;
|
||||
|
||||
tinydir_file *_files;
|
||||
#ifdef _MSC_VER
|
||||
HANDLE _h;
|
||||
WIN32_FIND_DATA _f;
|
||||
#else
|
||||
_TINYDIR_DIR *_d;
|
||||
struct _tinydir_dirent *_e;
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
struct _tinydir_dirent *_ep;
|
||||
#endif
|
||||
#endif
|
||||
} tinydir_dir;
|
||||
|
||||
|
||||
/* declarations */
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path);
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path);
|
||||
_TINYDIR_FUNC
|
||||
void tinydir_close(tinydir_dir *dir);
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_next(tinydir_dir *dir);
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file);
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i);
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open_subdir_n(tinydir_dir *dir, size_t i);
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path);
|
||||
_TINYDIR_FUNC
|
||||
void _tinydir_get_ext(tinydir_file *file);
|
||||
_TINYDIR_FUNC
|
||||
int _tinydir_file_cmp(const void *a, const void *b);
|
||||
#ifndef _MSC_VER
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
_TINYDIR_FUNC
|
||||
size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* definitions*/
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
|
||||
{
|
||||
#ifndef _MSC_VER
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
int error;
|
||||
int size; /* using int size */
|
||||
#endif
|
||||
#else
|
||||
_tinydir_char_t path_buf[_TINYDIR_PATH_MAX];
|
||||
#endif
|
||||
_tinydir_char_t *pathp;
|
||||
|
||||
if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* initialise dir */
|
||||
dir->_files = NULL;
|
||||
#ifdef _MSC_VER
|
||||
dir->_h = INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
dir->_d = NULL;
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
dir->_ep = NULL;
|
||||
#endif
|
||||
#endif
|
||||
tinydir_close(dir);
|
||||
|
||||
_tinydir_strcpy(dir->path, path);
|
||||
/* Remove trailing slashes */
|
||||
pathp = &dir->path[_tinydir_strlen(dir->path) - 1];
|
||||
while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/')))
|
||||
{
|
||||
*pathp = TINYDIR_STRING('\0');
|
||||
pathp++;
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
_tinydir_strcpy(path_buf, dir->path);
|
||||
_tinydir_strcat(path_buf, TINYDIR_STRING("\\*"));
|
||||
#if (defined WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP)
|
||||
dir->_h = FindFirstFileEx(path_buf, FindExInfoStandard, &dir->_f, FindExSearchNameMatch, NULL, 0);
|
||||
#else
|
||||
dir->_h = FindFirstFile(path_buf, &dir->_f);
|
||||
#endif
|
||||
if (dir->_h == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
errno = ENOENT;
|
||||
#else
|
||||
dir->_d = _tinydir_opendir(path);
|
||||
if (dir->_d == NULL)
|
||||
{
|
||||
#endif
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* read first file */
|
||||
dir->has_next = 1;
|
||||
#ifndef _MSC_VER
|
||||
#ifdef _TINYDIR_USE_READDIR
|
||||
dir->_e = _tinydir_readdir(dir->_d);
|
||||
#else
|
||||
/* allocate dirent buffer for readdir_r */
|
||||
size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */
|
||||
if (size == -1) return -1;
|
||||
dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size);
|
||||
if (dir->_ep == NULL) return -1;
|
||||
|
||||
error = readdir_r(dir->_d, dir->_ep, &dir->_e);
|
||||
if (error != 0) return -1;
|
||||
#endif
|
||||
if (dir->_e == NULL)
|
||||
{
|
||||
dir->has_next = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
tinydir_close(dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path)
|
||||
{
|
||||
/* Count the number of files first, to pre-allocate the files array */
|
||||
size_t n_files = 0;
|
||||
if (tinydir_open(dir, path) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
while (dir->has_next)
|
||||
{
|
||||
n_files++;
|
||||
if (tinydir_next(dir) == -1)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
tinydir_close(dir);
|
||||
|
||||
if (n_files == 0 || tinydir_open(dir, path) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
dir->n_files = 0;
|
||||
dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files);
|
||||
if (dir->_files == NULL)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
while (dir->has_next)
|
||||
{
|
||||
tinydir_file *p_file;
|
||||
dir->n_files++;
|
||||
|
||||
p_file = &dir->_files[dir->n_files - 1];
|
||||
if (tinydir_readfile(dir, p_file) == -1)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (tinydir_next(dir) == -1)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Just in case the number of files has changed between the first and
|
||||
second reads, terminate without writing into unallocated memory */
|
||||
if (dir->n_files == n_files)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
tinydir_close(dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
void tinydir_close(tinydir_dir *dir)
|
||||
{
|
||||
if (dir == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
memset(dir->path, 0, sizeof(dir->path));
|
||||
dir->has_next = 0;
|
||||
dir->n_files = 0;
|
||||
_TINYDIR_FREE(dir->_files);
|
||||
dir->_files = NULL;
|
||||
#ifdef _MSC_VER
|
||||
if (dir->_h != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
FindClose(dir->_h);
|
||||
}
|
||||
dir->_h = INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
if (dir->_d)
|
||||
{
|
||||
_tinydir_closedir(dir->_d);
|
||||
}
|
||||
dir->_d = NULL;
|
||||
dir->_e = NULL;
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
_TINYDIR_FREE(dir->_ep);
|
||||
dir->_ep = NULL;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_next(tinydir_dir *dir)
|
||||
{
|
||||
if (dir == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (!dir->has_next)
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
if (FindNextFile(dir->_h, &dir->_f) == 0)
|
||||
#else
|
||||
#ifdef _TINYDIR_USE_READDIR
|
||||
dir->_e = _tinydir_readdir(dir->_d);
|
||||
#else
|
||||
if (dir->_ep == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
if (dir->_e == NULL)
|
||||
#endif
|
||||
{
|
||||
dir->has_next = 0;
|
||||
#ifdef _MSC_VER
|
||||
if (GetLastError() != ERROR_SUCCESS &&
|
||||
GetLastError() != ERROR_NO_MORE_FILES)
|
||||
{
|
||||
tinydir_close(dir);
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
|
||||
{
|
||||
const _tinydir_char_t *filename;
|
||||
if (dir == NULL || file == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
if (dir->_h == INVALID_HANDLE_VALUE)
|
||||
#else
|
||||
if (dir->_e == NULL)
|
||||
#endif
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
filename =
|
||||
#ifdef _MSC_VER
|
||||
dir->_f.cFileName;
|
||||
#else
|
||||
dir->_e->d_name;
|
||||
#endif
|
||||
if (_tinydir_strlen(dir->path) +
|
||||
_tinydir_strlen(filename) + 1 + _TINYDIR_PATH_EXTRA >=
|
||||
_TINYDIR_PATH_MAX)
|
||||
{
|
||||
/* the path for the file will be too long */
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
if (_tinydir_strlen(filename) >= _TINYDIR_FILENAME_MAX)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_tinydir_strcpy(file->path, dir->path);
|
||||
if (_tinydir_strcmp(dir->path, TINYDIR_STRING("/")) != 0)
|
||||
_tinydir_strcat(file->path, TINYDIR_STRING("/"));
|
||||
_tinydir_strcpy(file->name, filename);
|
||||
_tinydir_strcat(file->path, filename);
|
||||
#ifndef _MSC_VER
|
||||
#ifdef __MINGW32__
|
||||
if (_tstat(
|
||||
#elif (defined _BSD_SOURCE) || (defined _DEFAULT_SOURCE) \
|
||||
|| ((defined _XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)) \
|
||||
|| ((defined _POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L))
|
||||
if (lstat(
|
||||
#else
|
||||
if (stat(
|
||||
#endif
|
||||
file->path, &file->_s) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
_tinydir_get_ext(file);
|
||||
|
||||
file->is_dir =
|
||||
#ifdef _MSC_VER
|
||||
!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
||||
#else
|
||||
S_ISDIR(file->_s.st_mode);
|
||||
#endif
|
||||
file->is_reg =
|
||||
#ifdef _MSC_VER
|
||||
!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
|
||||
(
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
|
||||
#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
|
||||
#endif
|
||||
#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
|
||||
#endif
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
|
||||
#else
|
||||
S_ISREG(file->_s.st_mode);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
|
||||
{
|
||||
if (dir == NULL || file == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (i >= dir->n_files)
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(file, &dir->_files[i], sizeof(tinydir_file));
|
||||
_tinydir_get_ext(file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
|
||||
{
|
||||
_tinydir_char_t path[_TINYDIR_PATH_MAX];
|
||||
if (dir == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (i >= dir->n_files || !dir->_files[i].is_dir)
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_tinydir_strcpy(path, dir->_files[i].path);
|
||||
tinydir_close(dir);
|
||||
if (tinydir_open_sorted(dir, path) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Open a single file given its path */
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
|
||||
{
|
||||
tinydir_dir dir;
|
||||
int result = 0;
|
||||
int found = 0;
|
||||
_tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX];
|
||||
_tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX];
|
||||
_tinydir_char_t *dir_name;
|
||||
_tinydir_char_t *base_name;
|
||||
#if (defined _MSC_VER || defined __MINGW32__)
|
||||
_tinydir_char_t drive_buf[_TINYDIR_PATH_MAX];
|
||||
_tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX];
|
||||
#endif
|
||||
|
||||
if (file == NULL || path == NULL || _tinydir_strlen(path) == 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get the parent path */
|
||||
#if (defined _MSC_VER || defined __MINGW32__)
|
||||
#if ((defined _MSC_VER) && (_MSC_VER >= 1400))
|
||||
errno = _tsplitpath_s(
|
||||
path,
|
||||
drive_buf, _TINYDIR_DRIVE_MAX,
|
||||
dir_name_buf, _TINYDIR_FILENAME_MAX,
|
||||
file_name_buf, _TINYDIR_FILENAME_MAX,
|
||||
ext_buf, _TINYDIR_FILENAME_MAX);
|
||||
#else
|
||||
_tsplitpath(
|
||||
path,
|
||||
drive_buf,
|
||||
dir_name_buf,
|
||||
file_name_buf,
|
||||
ext_buf);
|
||||
#endif
|
||||
|
||||
if (errno)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* _splitpath_s not work fine with only filename and widechar support */
|
||||
#ifdef _UNICODE
|
||||
if (drive_buf[0] == L'\xFEFE')
|
||||
drive_buf[0] = '\0';
|
||||
if (dir_name_buf[0] == L'\xFEFE')
|
||||
dir_name_buf[0] = '\0';
|
||||
#endif
|
||||
|
||||
/* Emulate the behavior of dirname by returning "." for dir name if it's
|
||||
empty */
|
||||
if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0')
|
||||
{
|
||||
_tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
|
||||
}
|
||||
/* Concatenate the drive letter and dir name to form full dir name */
|
||||
_tinydir_strcat(drive_buf, dir_name_buf);
|
||||
dir_name = drive_buf;
|
||||
/* Concatenate the file name and extension to form base name */
|
||||
_tinydir_strcat(file_name_buf, ext_buf);
|
||||
base_name = file_name_buf;
|
||||
#else
|
||||
_tinydir_strcpy(dir_name_buf, path);
|
||||
dir_name = dirname(dir_name_buf);
|
||||
_tinydir_strcpy(file_name_buf, path);
|
||||
base_name = basename(file_name_buf);
|
||||
#endif
|
||||
|
||||
/* Special case: if the path is a root dir, open the parent dir as the file */
|
||||
#if (defined _MSC_VER || defined __MINGW32__)
|
||||
if (_tinydir_strlen(base_name) == 0)
|
||||
#else
|
||||
if ((_tinydir_strcmp(base_name, TINYDIR_STRING("/"))) == 0)
|
||||
#endif
|
||||
{
|
||||
memset(file, 0, sizeof * file);
|
||||
file->is_dir = 1;
|
||||
file->is_reg = 0;
|
||||
_tinydir_strcpy(file->path, dir_name);
|
||||
file->extension = file->path + _tinydir_strlen(file->path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Open the parent directory */
|
||||
if (tinydir_open(&dir, dir_name) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read through the parent directory and look for the file */
|
||||
while (dir.has_next)
|
||||
{
|
||||
if (tinydir_readfile(&dir, file) == -1)
|
||||
{
|
||||
result = -1;
|
||||
goto bail;
|
||||
}
|
||||
if (_tinydir_strcmp(file->name, base_name) == 0)
|
||||
{
|
||||
/* File found */
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
tinydir_next(&dir);
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
result = -1;
|
||||
errno = ENOENT;
|
||||
}
|
||||
|
||||
bail:
|
||||
tinydir_close(&dir);
|
||||
return result;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
void _tinydir_get_ext(tinydir_file *file)
|
||||
{
|
||||
_tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.'));
|
||||
if (period == NULL)
|
||||
{
|
||||
file->extension = &(file->name[_tinydir_strlen(file->name)]);
|
||||
}
|
||||
else
|
||||
{
|
||||
file->extension = period + 1;
|
||||
}
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int _tinydir_file_cmp(const void *a, const void *b)
|
||||
{
|
||||
const tinydir_file *fa = (const tinydir_file *)a;
|
||||
const tinydir_file *fb = (const tinydir_file *)b;
|
||||
if (fa->is_dir != fb->is_dir)
|
||||
{
|
||||
return -(fa->is_dir - fb->is_dir);
|
||||
}
|
||||
return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
|
||||
}
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
/*
|
||||
The following authored by Ben Hutchings <ben@decadent.org.uk>
|
||||
from https://womble.decadent.org.uk/readdir_r-advisory.html
|
||||
*/
|
||||
/* Calculate the required buffer size (in bytes) for directory *
|
||||
* entries read from the given directory handle. Return -1 if this *
|
||||
* this cannot be done. *
|
||||
* *
|
||||
* This code does not trust values of NAME_MAX that are less than *
|
||||
* 255, since some systems (including at least HP-UX) incorrectly *
|
||||
* define it to be a smaller value. */
|
||||
_TINYDIR_FUNC
|
||||
size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp)
|
||||
{
|
||||
long name_max;
|
||||
size_t name_end;
|
||||
/* parameter may be unused */
|
||||
(void)dirp;
|
||||
|
||||
#if defined _TINYDIR_USE_FPATHCONF
|
||||
name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
|
||||
if (name_max == -1)
|
||||
#if defined(NAME_MAX)
|
||||
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
|
||||
#else
|
||||
return (size_t)(-1);
|
||||
#endif
|
||||
#elif defined(NAME_MAX)
|
||||
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
|
||||
#else
|
||||
#error "buffer size for readdir_r cannot be determined"
|
||||
#endif
|
||||
name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1;
|
||||
return (name_end > sizeof(struct _tinydir_dirent) ?
|
||||
name_end : sizeof(struct _tinydir_dirent));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
# if defined (_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
# endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user