New upstream version 5.2.5188

This commit is contained in:
geos_one
2025-08-08 12:40:03 +02:00
parent b1e9cf955b
commit e8b2c75644
5730 changed files with 1430965 additions and 238433 deletions

24
3rdparty/tinydir/.travis.yml vendored Normal file
View 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
View 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
View File

@@ -0,0 +1,79 @@
TinyDir
=======
[![Build Status](https://travis-ci.org/cxong/tinydir.svg?branch=master)](https://travis-ci.org/cxong/tinydir)
[![Release](http://img.shields.io/github/release/cxong/tinydir.svg)](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
View 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
View 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
View 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
View 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;
}

View 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;
}

View 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;
}

View 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
View 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;
}

View 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
View 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
View 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()

View 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
View 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.

View File

@@ -0,0 +1,78 @@
cbehave - A Behavior Driven Development Framework for C
=======
[![Build Status](https://travis-ci.org/cxong/cbehave.svg?branch=master)](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`

View 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
View 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
View 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 */

View 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
View 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
View 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
View File

@@ -0,0 +1,3 @@
#pragma once
void make_temp_file(const char *prefix, char *out);

View 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
View 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