Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
spdlog
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
spdlog
Commits
a453bccf
Unverified
Commit
a453bccf
authored
Jan 06, 2021
by
Charles Milette
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Better support for / separators on Windows, improve wchar filename coverage
parent
aa2053a5
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
163 additions
and
112 deletions
+163
-112
appveyor.yml
appveyor.yml
+13
-1
include/spdlog/common.h
include/spdlog/common.h
+4
-1
include/spdlog/details/file_helper-inl.h
include/spdlog/details/file_helper-inl.h
+1
-1
include/spdlog/details/os-inl.h
include/spdlog/details/os-inl.h
+2
-11
include/spdlog/details/os.h
include/spdlog/details/os.h
+7
-2
include/spdlog/pattern_formatter-inl.h
include/spdlog/pattern_formatter-inl.h
+24
-2
include/spdlog/tweakme.h
include/spdlog/tweakme.h
+8
-0
tests/test_create_dir.cpp
tests/test_create_dir.cpp
+31
-31
tests/test_daily_logger.cpp
tests/test_daily_logger.cpp
+28
-16
tests/test_eventlog.cpp
tests/test_eventlog.cpp
+3
-3
tests/test_file_helper.cpp
tests/test_file_helper.cpp
+23
-27
tests/test_file_logging.cpp
tests/test_file_logging.cpp
+16
-14
tests/test_pattern_formatter.cpp
tests/test_pattern_formatter.cpp
+1
-1
tests/utils.cpp
tests/utils.cpp
+2
-2
No files found.
appveyor.yml
View file @
a453bccf
...
...
@@ -5,30 +5,42 @@ environment:
-
GENERATOR
:
'
"Visual
Studio
14
2015"'
BUILD_TYPE
:
Debug
WCHAR
:
'
OFF'
WCHAR_FILES
:
'
OFF'
BUILD_SHARED
:
'
OFF'
-
GENERATOR
:
'
"Visual
Studio
14
2015"'
BUILD_TYPE
:
Release
WCHAR
:
'
ON'
WCHAR_FILES
:
'
OFF'
BUILD_SHARED
:
'
OFF'
-
GENERATOR
:
'
"Visual
Studio
14
2015
Win64"'
BUILD_TYPE
:
Debug
WCHAR
:
'
ON'
WCHAR_FILES
:
'
OFF'
BUILD_SHARED
:
'
OFF'
-
GENERATOR
:
'
"Visual
Studio
14
2015
Win64"'
BUILD_TYPE
:
Release
WCHAR
:
'
ON'
WCHAR_FILES
:
'
OFF'
BUILD_SHARED
:
'
OFF'
-
GENERATOR
:
'
"Visual
Studio
15
2017
Win64"'
BUILD_TYPE
:
Debug
WCHAR
:
'
ON'
WCHAR_FILES
:
'
OFF'
BUILD_SHARED
:
'
OFF'
-
GENERATOR
:
'
"Visual
Studio
15
2017
Win64"'
BUILD_TYPE
:
Release
WCHAR
:
'
OFF'
WCHAR_FILES
:
'
OFF'
BUILD_SHARED
:
'
OFF'
-
GENERATOR
:
'
"Visual
Studio
15
2017
Win64"'
BUILD_TYPE
:
Release
WCHAR
:
'
OFF'
WCHAR_FILES
:
'
OFF'
BUILD_SHARED
:
'
ON'
-
GENERATOR
:
'
"Visual
Studio
15
2017
Win64"'
BUILD_TYPE
:
Release
WCHAR
:
'
ON'
WCHAR_FILES
:
'
ON'
BUILD_SHARED
:
'
ON'
build_script
:
-
cmd
:
>-
...
...
@@ -40,7 +52,7 @@ build_script:
set PATH=%PATH%;C:\Program Files\Git\usr\bin
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DSPDLOG_WCHAR_SUPPORT=%WCHAR% -DSPDLOG_BUILD_SHARED=%BUILD_SHARED% -DSPDLOG_BUILD_EXAMPLE=ON -DSPDLOG_BUILD_EXAMPLE_HO=ON -DSPDLOG_BUILD_TESTS=ON -DSPDLOG_BUILD_TESTS_HO=OFF -DSPDLOG_BUILD_WARNINGS=ON
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DSPDLOG_WCHAR_SUPPORT=%WCHAR% -DSPDLOG_
WCHAR_FILENAMES=%WCHAR_FILES% -DSPDLOG_
BUILD_SHARED=%BUILD_SHARED% -DSPDLOG_BUILD_EXAMPLE=ON -DSPDLOG_BUILD_EXAMPLE_HO=ON -DSPDLOG_BUILD_TESTS=ON -DSPDLOG_BUILD_TESTS_HO=OFF -DSPDLOG_BUILD_WARNINGS=ON
cmake --build . --config %BUILD_TYPE%
...
...
include/spdlog/common.h
View file @
a453bccf
...
...
@@ -88,7 +88,9 @@ class sink;
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
using
filename_t
=
std
::
wstring
;
#define SPDLOG_FILENAME_T(s) L##s
// allow macro expansion to occur in SPDLOG_FILENAME_T
#define SPDLOG_FILENAME_T_INNER(s) L##s
#define SPDLOG_FILENAME_T(s) SPDLOG_FILENAME_T_INNER(s)
#else
using
filename_t
=
std
::
string
;
#define SPDLOG_FILENAME_T(s) s
...
...
@@ -101,6 +103,7 @@ using err_handler = std::function<void(const std::string &err_msg)>;
using
string_view_t
=
fmt
::
basic_string_view
<
char
>
;
using
wstring_view_t
=
fmt
::
basic_string_view
<
wchar_t
>
;
using
memory_buf_t
=
fmt
::
basic_memory_buffer
<
char
,
250
>
;
using
filename_memory_buf_t
=
fmt
::
basic_memory_buffer
<
filename_t
::
value_type
,
250
>
;
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
#ifndef _WIN32
...
...
include/spdlog/details/file_helper-inl.h
View file @
a453bccf
...
...
@@ -118,7 +118,7 @@ SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension
}
// treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
auto
folder_index
=
fname
.
rfind
(
details
::
os
::
folder_sep
);
auto
folder_index
=
fname
.
find_last_of
(
details
::
os
::
folder_seps_filename
);
if
(
folder_index
!=
filename_t
::
npos
&&
folder_index
>=
ext_index
-
1
)
{
return
std
::
make_tuple
(
fname
,
filename_t
());
...
...
include/spdlog/details/os-inl.h
View file @
a453bccf
...
...
@@ -533,15 +533,10 @@ SPDLOG_INLINE bool create_dir(filename_t path)
return
false
;
}
#ifdef _WIN32
// support forward slash in windows
std
::
replace
(
path
.
begin
(),
path
.
end
(),
'/'
,
folder_sep
);
#endif
size_t
search_offset
=
0
;
do
{
auto
token_pos
=
path
.
find
(
folder_sep
,
search_offset
);
auto
token_pos
=
path
.
find
_first_of
(
folder_seps_filename
,
search_offset
);
// treat the entire path as a folder if no folder separator not found
if
(
token_pos
==
filename_t
::
npos
)
{
...
...
@@ -567,11 +562,7 @@ SPDLOG_INLINE bool create_dir(filename_t path)
// "abc///" => "abc//"
SPDLOG_INLINE
filename_t
dir_name
(
filename_t
path
)
{
#ifdef _WIN32
// support forward slash in windows
std
::
replace
(
path
.
begin
(),
path
.
end
(),
'/'
,
folder_sep
);
#endif
auto
pos
=
path
.
find_last_of
(
folder_sep
);
auto
pos
=
path
.
find_last_of
(
folder_seps_filename
);
return
pos
!=
filename_t
::
npos
?
path
.
substr
(
0
,
pos
)
:
filename_t
{};
}
...
...
include/spdlog/details/os.h
View file @
a453bccf
...
...
@@ -32,11 +32,16 @@ SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
SPDLOG_CONSTEXPR
static
const
char
*
default_eol
=
SPDLOG_EOL
;
// folder separator
#if !defined(SPDLOG_FOLDER_SEPS)
#ifdef _WIN32
static
const
char
folder_sep
=
'\\'
;
#define SPDLOG_FOLDER_SEPS "\\/"
#else
SPDLOG_CONSTEXPR
static
const
char
folder_sep
=
'/'
;
#define SPDLOG_FOLDER_SEPS "/"
#endif
#endif
SPDLOG_CONSTEXPR
static
const
char
folder_seps
[]
=
SPDLOG_FOLDER_SEPS
;
SPDLOG_CONSTEXPR
static
const
filename_t
::
value_type
folder_seps_filename
[]
=
SPDLOG_FILENAME_T
(
SPDLOG_FOLDER_SEPS
);
// fopen_s on non windows for writing
SPDLOG_API
bool
fopen_s
(
FILE
**
fp
,
const
filename_t
&
filename
,
const
filename_t
&
mode
);
...
...
include/spdlog/pattern_formatter-inl.h
View file @
a453bccf
...
...
@@ -13,11 +13,13 @@
#include <spdlog/fmt/fmt.h>
#include <spdlog/formatter.h>
#include <algorithm>
#include <array>
#include <chrono>
#include <ctime>
#include <cctype>
#include <cstring>
#include <iterator>
#include <memory>
#include <mutex>
#include <string>
...
...
@@ -814,11 +816,31 @@ public:
:
flag_formatter
(
padinfo
)
{}
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4127) // consider using 'if constexpr' instead
#endif // _MSC_VER
static
const
char
*
basename
(
const
char
*
filename
)
{
const
char
*
rv
=
std
::
strrchr
(
filename
,
os
::
folder_sep
);
return
rv
!=
nullptr
?
rv
+
1
:
filename
;
// if the size is 2 (1 character + null terminator) we can use the more efficient strrchr
// the branch will be elided by optimizations
if
(
sizeof
(
os
::
folder_seps
)
==
2
)
{
const
char
*
rv
=
std
::
strrchr
(
filename
,
os
::
folder_seps
[
0
]);
return
rv
!=
nullptr
?
rv
+
1
:
filename
;
}
else
{
const
std
::
reverse_iterator
<
const
char
*>
begin
(
filename
+
std
::
strlen
(
filename
));
const
std
::
reverse_iterator
<
const
char
*>
end
(
filename
);
const
auto
it
=
std
::
find_first_of
(
begin
,
end
,
std
::
begin
(
os
::
folder_seps
),
std
::
end
(
os
::
folder_seps
)
-
1
);
return
it
!=
end
?
it
.
base
()
:
filename
;
}
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER
void
format
(
const
details
::
log_msg
&
msg
,
const
std
::
tm
&
,
memory_buf_t
&
dest
)
override
{
...
...
include/spdlog/tweakme.h
View file @
a453bccf
...
...
@@ -58,6 +58,14 @@
// #define SPDLOG_EOL ";-)\n"
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to override default folder separators ("/" or "\\/" under
// Linux/Windows). Each character in the string is treated as a different
// separator.
//
// #define SPDLOG_FOLDER_SEPS "\\"
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to use your own copy of the fmt library instead of spdlog's copy.
// In this case spdlog will try to include <fmt/format.h> so set your -I flag
...
...
tests/test_create_dir.cpp
View file @
a453bccf
...
...
@@ -6,7 +6,7 @@
using
spdlog
::
details
::
os
::
create_dir
;
using
spdlog
::
details
::
os
::
path_exists
;
bool
try_create_dir
(
const
char
*
path
,
const
char
*
normalized_path
)
bool
try_create_dir
(
const
spdlog
::
filename_t
&
path
,
const
spdlog
::
filename_t
&
normalized_path
)
{
auto
rv
=
create_dir
(
path
);
REQUIRE
(
rv
==
true
);
...
...
@@ -17,24 +17,24 @@ TEST_CASE("create_dir", "[create_dir]")
{
prepare_logdir
();
REQUIRE
(
try_create_dir
(
"test_logs/dir1/dir1"
,
"test_logs/dir1/dir1"
));
REQUIRE
(
try_create_dir
(
"test_logs/dir1/dir1"
,
"test_logs/dir1/dir1"
));
// test existing
REQUIRE
(
try_create_dir
(
"test_logs/dir1///dir2//"
,
"test_logs/dir1/dir2"
));
REQUIRE
(
try_create_dir
(
"./test_logs/dir1/dir3"
,
"test_logs/dir1/dir3"
));
REQUIRE
(
try_create_dir
(
"test_logs/../test_logs/dir1/dir4"
,
"test_logs/dir1/dir4"
));
REQUIRE
(
try_create_dir
(
SPDLOG_FILENAME_T
(
"test_logs/dir1/dir1"
),
SPDLOG_FILENAME_T
(
"test_logs/dir1/dir1"
)
));
REQUIRE
(
try_create_dir
(
SPDLOG_FILENAME_T
(
"test_logs/dir1/dir1"
),
SPDLOG_FILENAME_T
(
"test_logs/dir1/dir1"
)
));
// test existing
REQUIRE
(
try_create_dir
(
SPDLOG_FILENAME_T
(
"test_logs/dir1///dir2//"
),
SPDLOG_FILENAME_T
(
"test_logs/dir1/dir2"
)
));
REQUIRE
(
try_create_dir
(
SPDLOG_FILENAME_T
(
"./test_logs/dir1/dir3"
),
SPDLOG_FILENAME_T
(
"test_logs/dir1/dir3"
)
));
REQUIRE
(
try_create_dir
(
SPDLOG_FILENAME_T
(
"test_logs/../test_logs/dir1/dir4"
),
SPDLOG_FILENAME_T
(
"test_logs/dir1/dir4"
)
));
#ifdef WIN32
// test backslash folder separator
REQUIRE
(
try_create_dir
(
"test_logs
\\
dir1
\\
dir222"
,
"test_logs
\\
dir1
\\
dir222"
));
REQUIRE
(
try_create_dir
(
"test_logs
\\
dir1
\\
dir223
\\
"
,
"test_logs
\\
dir1
\\
dir223
\\
"
));
REQUIRE
(
try_create_dir
(
".
\\
test_logs
\\
dir1
\\
dir2
\\
dir99
\\
..
\\
dir23"
,
"test_logs
\\
dir1
\\
dir2
\\
dir23"
));
REQUIRE
(
try_create_dir
(
"test_logs
\\
..
\\
test_logs
\\
dir1
\\
dir5"
,
"test_logs
\\
dir1
\\
dir5"
));
REQUIRE
(
try_create_dir
(
SPDLOG_FILENAME_T
(
"test_logs
\\
dir1
\\
dir222"
),
SPDLOG_FILENAME_T
(
"test_logs
\\
dir1
\\
dir222"
)
));
REQUIRE
(
try_create_dir
(
SPDLOG_FILENAME_T
(
"test_logs
\\
dir1
\\
dir223
\\
"
),
SPDLOG_FILENAME_T
(
"test_logs
\\
dir1
\\
dir223
\\
"
)
));
REQUIRE
(
try_create_dir
(
SPDLOG_FILENAME_T
(
".
\\
test_logs
\\
dir1
\\
dir2
\\
dir99
\\
..
\\
dir23"
),
SPDLOG_FILENAME_T
(
"test_logs
\\
dir1
\\
dir2
\\
dir23"
)
));
REQUIRE
(
try_create_dir
(
SPDLOG_FILENAME_T
(
"test_logs
\\
..
\\
test_logs
\\
dir1
\\
dir5"
),
SPDLOG_FILENAME_T
(
"test_logs
\\
dir1
\\
dir5"
)
));
#endif
}
TEST_CASE
(
"create_invalid_dir"
,
"[create_dir]"
)
{
REQUIRE
(
create_dir
(
""
)
==
false
);
REQUIRE
(
create_dir
(
SPDLOG_FILENAME_T
(
""
)
)
==
false
);
REQUIRE
(
create_dir
(
spdlog
::
filename_t
{})
==
false
);
#ifdef __linux__
REQUIRE
(
create_dir
(
"/proc/spdlog-utest"
)
==
false
);
...
...
@@ -44,28 +44,28 @@ TEST_CASE("create_invalid_dir", "[create_dir]")
TEST_CASE
(
"dir_name"
,
"[create_dir]"
)
{
using
spdlog
::
details
::
os
::
dir_name
;
REQUIRE
(
dir_name
(
""
).
empty
());
REQUIRE
(
dir_name
(
"dir"
).
empty
());
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
""
)
).
empty
());
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
"dir"
)
).
empty
());
#ifdef WIN32
REQUIRE
(
dir_name
(
R"(dir\)"
)
==
"dir"
);
REQUIRE
(
dir_name
(
R"(dir\\\)"
)
==
R"(dir\\)"
);
REQUIRE
(
dir_name
(
R"(dir\file)"
)
==
"dir"
);
REQUIRE
(
dir_name
(
R"(dir/file)"
)
==
"dir"
);
REQUIRE
(
dir_name
(
R"(dir\file.txt)"
)
==
"dir"
);
REQUIRE
(
dir_name
(
R"(dir/file)"
)
==
"dir"
);
REQUIRE
(
dir_name
(
R"(dir\file.txt\)"
)
==
R"(dir\file.txt)"
);
REQUIRE
(
dir_name
(
R"(dir/file.txt/)"
)
==
R"(dir\file.txt)"
);
REQUIRE
(
dir_name
(
R"(\dir\file.txt)"
)
==
R"(\dir)"
);
REQUIRE
(
dir_name
(
R"(/dir/file.txt)"
)
==
R"(\dir)"
);
REQUIRE
(
dir_name
(
R"(\\dir\file.txt)"
)
==
R"(\\dir)"
);
REQUIRE
(
dir_name
(
R"(//dir/file.txt)"
)
==
R"(\\dir)"
);
REQUIRE
(
dir_name
(
R"(..\file.txt)"
)
==
".."
);
REQUIRE
(
dir_name
(
R"(../file.txt)"
)
==
".."
);
REQUIRE
(
dir_name
(
R"(.\file.txt)"
)
==
"."
);
REQUIRE
(
dir_name
(
R"(./file.txt)"
)
==
"."
);
REQUIRE
(
dir_name
(
R"(c:\\a\b\c\d\file.txt)"
)
==
R"(c:\\a\b\c\d)"
);
REQUIRE
(
dir_name
(
R"(c://a/b/c/d/file.txt)"
)
==
R"(c:\\a\b\c\d)"
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(dir\)"
))
==
SPDLOG_FILENAME_T
(
"dir"
)
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(dir\\\)"
))
==
SPDLOG_FILENAME_T
(
R"(dir\\)"
)
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(dir\file)"
))
==
SPDLOG_FILENAME_T
(
"dir"
)
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(dir/file)"
))
==
SPDLOG_FILENAME_T
(
"dir"
)
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(dir\file.txt)"
))
==
SPDLOG_FILENAME_T
(
"dir"
)
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(dir/file)"
))
==
SPDLOG_FILENAME_T
(
"dir"
)
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(dir\file.txt\)"
))
==
SPDLOG_FILENAME_T
(
R"(dir\file.txt)"
)
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(dir/file.txt/)"
))
==
SPDLOG_FILENAME_T
(
R"(dir\file.txt)"
)
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(\dir\file.txt)"
))
==
SPDLOG_FILENAME_T
(
R"(\dir)"
)
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(/dir/file.txt)"
))
==
SPDLOG_FILENAME_T
(
R"(\dir)"
)
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(\\dir\file.txt)"
))
==
SPDLOG_FILENAME_T
(
R"(\\dir)"
)
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(//dir/file.txt)"
))
==
SPDLOG_FILENAME_T
(
R"(\\dir)"
)
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(..\file.txt)"
))
==
SPDLOG_FILENAME_T
(
".."
)
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(../file.txt)"
))
==
SPDLOG_FILENAME_T
(
".."
)
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(.\file.txt)"
))
==
SPDLOG_FILENAME_T
(
"."
)
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(./file.txt)"
))
==
SPDLOG_FILENAME_T
(
"."
)
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(c:\\a\b\c\d\file.txt)"
))
==
SPDLOG_FILENAME_T
(
R"(c:\\a\b\c\d)"
)
);
REQUIRE
(
dir_name
(
SPDLOG_FILENAME_T
(
R"(c://a/b/c/d/file.txt)"
))
==
SPDLOG_FILENAME_T
(
R"(c:\\a\b\c\d)"
)
);
#else
REQUIRE
(
dir_name
(
"dir/"
)
==
"dir"
);
REQUIRE
(
dir_name
(
"dir///"
)
==
"dir//"
);
...
...
tests/test_daily_logger.cpp
View file @
a453bccf
...
...
@@ -10,10 +10,10 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]")
prepare_logdir
();
// calculate filename (time based)
s
td
::
string
basename
=
"test_logs/daily_dateonly"
;
s
pdlog
::
filename_t
basename
=
SPDLOG_FILENAME_T
(
"test_logs/daily_dateonly"
)
;
std
::
tm
tm
=
spdlog
::
details
::
os
::
localtime
();
spdlog
::
memory_buf_t
w
;
fmt
::
format_to
(
w
,
"{}_{:04d}-{:02d}-{:02d}"
,
basename
,
tm
.
tm_year
+
1900
,
tm
.
tm_mon
+
1
,
tm
.
tm_mday
);
spdlog
::
filename_
memory_buf_t
w
;
fmt
::
format_to
(
w
,
SPDLOG_FILENAME_T
(
"{}_{:04d}-{:02d}-{:02d}"
)
,
basename
,
tm
.
tm_year
+
1900
,
tm
.
tm_mon
+
1
,
tm
.
tm_mday
);
auto
logger
=
spdlog
::
create
<
sink_type
>
(
"logger"
,
basename
,
0
,
0
);
for
(
int
i
=
0
;
i
<
10
;
++
i
)
...
...
@@ -23,7 +23,13 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]")
}
logger
->
flush
();
#ifdef SPDLOG_WCHAR_FILENAMES
spdlog
::
memory_buf_t
buf
;
spdlog
::
details
::
os
::
wstr_to_utf8buf
(
fmt
::
to_string
(
w
));
auto
filename
=
fmt
::
to_string
(
buf
);
#else
auto
filename
=
fmt
::
to_string
(
w
);
#endif
require_message_count
(
filename
,
10
);
}
...
...
@@ -31,8 +37,8 @@ struct custom_daily_file_name_calculator
{
static
spdlog
::
filename_t
calc_filename
(
const
spdlog
::
filename_t
&
basename
,
const
tm
&
now_tm
)
{
spdlog
::
memory_buf_t
w
;
fmt
::
format_to
(
w
,
"{}{:04d}{:02d}{:02d}"
,
basename
,
now_tm
.
tm_year
+
1900
,
now_tm
.
tm_mon
+
1
,
now_tm
.
tm_mday
);
spdlog
::
filename_
memory_buf_t
w
;
fmt
::
format_to
(
w
,
SPDLOG_FILENAME_T
(
"{}{:04d}{:02d}{:02d}"
)
,
basename
,
now_tm
.
tm_year
+
1900
,
now_tm
.
tm_mon
+
1
,
now_tm
.
tm_mday
);
return
fmt
::
to_string
(
w
);
}
};
...
...
@@ -44,7 +50,7 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger]")
prepare_logdir
();
// calculate filename (time based)
s
td
::
string
basename
=
"test_logs/daily_dateonly"
;
s
pdlog
::
filename_t
basename
=
SPDLOG_FILENAME_T
(
"test_logs/daily_dateonly"
)
;
std
::
tm
tm
=
spdlog
::
details
::
os
::
localtime
();
spdlog
::
memory_buf_t
w
;
fmt
::
format_to
(
w
,
"{}{:04d}{:02d}{:02d}"
,
basename
,
tm
.
tm_year
+
1900
,
tm
.
tm_mon
+
1
,
tm
.
tm_mday
);
...
...
@@ -57,7 +63,13 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger]")
logger
->
flush
();
#ifdef SPDLOG_WCHAR_FILENAMES
spdlog
::
memory_buf_t
buf
;
spdlog
::
details
::
os
::
wstr_to_utf8buf
(
fmt
::
to_string
(
w
));
auto
filename
=
fmt
::
to_string
(
buf
);
#else
auto
filename
=
fmt
::
to_string
(
w
);
#endif
require_message_count
(
filename
,
10
);
}
...
...
@@ -67,20 +79,20 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger]")
TEST_CASE
(
"rotating_file_sink::calc_filename1"
,
"[rotating_file_sink]]"
)
{
auto
filename
=
spdlog
::
sinks
::
rotating_file_sink_st
::
calc_filename
(
"rotated.txt"
,
3
);
REQUIRE
(
filename
==
"rotated.3.txt"
);
auto
filename
=
spdlog
::
sinks
::
rotating_file_sink_st
::
calc_filename
(
SPDLOG_FILENAME_T
(
"rotated.txt"
)
,
3
);
REQUIRE
(
filename
==
SPDLOG_FILENAME_T
(
"rotated.3.txt"
)
);
}
TEST_CASE
(
"rotating_file_sink::calc_filename2"
,
"[rotating_file_sink]]"
)
{
auto
filename
=
spdlog
::
sinks
::
rotating_file_sink_st
::
calc_filename
(
"rotated"
,
3
);
REQUIRE
(
filename
==
"rotated.3"
);
auto
filename
=
spdlog
::
sinks
::
rotating_file_sink_st
::
calc_filename
(
SPDLOG_FILENAME_T
(
"rotated"
)
,
3
);
REQUIRE
(
filename
==
SPDLOG_FILENAME_T
(
"rotated.3"
)
);
}
TEST_CASE
(
"rotating_file_sink::calc_filename3"
,
"[rotating_file_sink]]"
)
{
auto
filename
=
spdlog
::
sinks
::
rotating_file_sink_st
::
calc_filename
(
"rotated.txt"
,
0
);
REQUIRE
(
filename
==
"rotated.txt"
);
auto
filename
=
spdlog
::
sinks
::
rotating_file_sink_st
::
calc_filename
(
SPDLOG_FILENAME_T
(
"rotated.txt"
)
,
0
);
REQUIRE
(
filename
==
SPDLOG_FILENAME_T
(
"rotated.txt"
)
);
}
// regex supported only from gcc 4.9 and above
...
...
@@ -91,10 +103,10 @@ TEST_CASE("rotating_file_sink::calc_filename3", "[rotating_file_sink]]")
TEST_CASE
(
"daily_file_sink::daily_filename_calculator"
,
"[daily_file_sink]]"
)
{
// daily_YYYY-MM-DD_hh-mm.txt
auto
filename
=
spdlog
::
sinks
::
daily_filename_calculator
::
calc_filename
(
"daily.txt"
,
spdlog
::
details
::
os
::
localtime
());
auto
filename
=
spdlog
::
sinks
::
daily_filename_calculator
::
calc_filename
(
SPDLOG_FILENAME_T
(
"daily.txt"
)
,
spdlog
::
details
::
os
::
localtime
());
// date regex based on https://www.regular-expressions.info/dates.html
std
::
regex
re
(
R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])\.txt$)"
);
std
::
smatch
match
;
std
::
basic_regex
<
spdlog
::
filename_t
::
value_type
>
re
(
SPDLOG_FILENAME_T
(
R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])\.txt$)"
)
);
std
::
match_results
<
spdlog
::
filename_t
::
const_iterator
>
match
;
REQUIRE
(
std
::
regex_match
(
filename
,
match
,
re
));
}
#endif
...
...
@@ -116,7 +128,7 @@ static void test_rotate(int days_to_run, uint16_t max_days, uint16_t expected_n_
prepare_logdir
();
s
td
::
string
basename
=
"test_logs/daily_rotate.txt"
;
s
pdlog
::
filename_t
basename
=
SPDLOG_FILENAME_T
(
"test_logs/daily_rotate.txt"
)
;
daily_file_sink_st
sink
{
basename
,
2
,
30
,
true
,
max_days
};
// simulate messages with 24 intervals
...
...
tests/test_eventlog.cpp
View file @
a453bccf
...
...
@@ -24,20 +24,20 @@ static void test_single_print(std::function<void(std::string const &)> do_log, s
REQUIRE
(
CloseEventLog
(
handle_
));
}
}
}
event_log
{
::
OpenEventLog
(
nullptr
,
TEST_SOURCE
)};
}
event_log
{
::
OpenEventLog
A
(
nullptr
,
TEST_SOURCE
)};
REQUIRE
(
event_log
.
handle_
);
DWORD
read_bytes
{},
size_needed
{};
auto
ok
=
::
ReadEventLog
(
event_log
.
handle_
,
EVENTLOG_SEQUENTIAL_READ
|
EVENTLOG_BACKWARDS_READ
,
0
,
&
read_bytes
,
0
,
&
read_bytes
,
&
size_needed
);
::
ReadEventLog
A
(
event_log
.
handle_
,
EVENTLOG_SEQUENTIAL_READ
|
EVENTLOG_BACKWARDS_READ
,
0
,
&
read_bytes
,
0
,
&
read_bytes
,
&
size_needed
);
REQUIRE
(
!
ok
);
REQUIRE
(
::
GetLastError
()
==
ERROR_INSUFFICIENT_BUFFER
);
std
::
vector
<
char
>
record_buffer
(
size_needed
);
PEVENTLOGRECORD
record
=
(
PEVENTLOGRECORD
)
record_buffer
.
data
();
ok
=
::
ReadEventLog
(
ok
=
::
ReadEventLog
A
(
event_log
.
handle_
,
EVENTLOG_SEQUENTIAL_READ
|
EVENTLOG_BACKWARDS_READ
,
0
,
record
,
size_needed
,
&
read_bytes
,
&
size_needed
);
REQUIRE
(
ok
);
...
...
tests/test_file_helper.cpp
View file @
a453bccf
...
...
@@ -18,7 +18,7 @@ TEST_CASE("file_helper_filename", "[file_helper::filename()]]")
prepare_logdir
();
file_helper
helper
;
s
td
::
string
target_filename
=
"test_logs/file_helper_test.txt"
;
s
pdlog
::
filename_t
target_filename
=
SPDLOG_FILENAME_T
(
"test_logs/file_helper_test.txt"
)
;
helper
.
open
(
target_filename
);
REQUIRE
(
helper
.
filename
()
==
target_filename
);
}
...
...
@@ -26,7 +26,7 @@ TEST_CASE("file_helper_filename", "[file_helper::filename()]]")
TEST_CASE
(
"file_helper_size"
,
"[file_helper::size()]]"
)
{
prepare_logdir
();
s
td
::
string
target_filename
=
"test_logs/file_helper_test.txt"
;
s
pdlog
::
filename_t
target_filename
=
SPDLOG_FILENAME_T
(
"test_logs/file_helper_test.txt"
)
;
size_t
expected_size
=
123
;
{
file_helper
helper
;
...
...
@@ -40,7 +40,7 @@ TEST_CASE("file_helper_size", "[file_helper::size()]]")
TEST_CASE
(
"file_helper_reopen"
,
"[file_helper::reopen()]]"
)
{
prepare_logdir
();
s
td
::
string
target_filename
=
"test_logs/file_helper_test.txt"
;
s
pdlog
::
filename_t
target_filename
=
SPDLOG_FILENAME_T
(
"test_logs/file_helper_test.txt"
)
;
file_helper
helper
;
helper
.
open
(
target_filename
);
write_with_helper
(
helper
,
12
);
...
...
@@ -52,7 +52,7 @@ TEST_CASE("file_helper_reopen", "[file_helper::reopen()]]")
TEST_CASE
(
"file_helper_reopen2"
,
"[file_helper::reopen(false)]]"
)
{
prepare_logdir
();
s
td
::
string
target_filename
=
"test_logs/file_helper_test.txt"
;
s
pdlog
::
filename_t
target_filename
=
SPDLOG_FILENAME_T
(
"test_logs/file_helper_test.txt"
)
;
size_t
expected_size
=
14
;
file_helper
helper
;
helper
.
open
(
target_filename
);
...
...
@@ -62,16 +62,12 @@ TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
REQUIRE
(
helper
.
size
()
==
expected_size
);
}
static
void
test_split_ext
(
const
char
*
fname
,
const
char
*
expect_base
,
const
char
*
expect_ext
)
static
void
test_split_ext
(
const
spdlog
::
filename_t
::
value_type
*
fname
,
const
spdlog
::
filename_t
::
value_type
*
expect_base
,
const
spdlog
::
filename_t
::
value_type
*
expect_ext
)
{
spdlog
::
filename_t
filename
(
fname
);
spdlog
::
filename_t
expected_base
(
expect_base
);
spdlog
::
filename_t
expected_ext
(
expect_ext
);
#ifdef _WIN32 // replace folder sep
std
::
replace
(
filename
.
begin
(),
filename
.
end
(),
'/'
,
'\\'
);
std
::
replace
(
expected_base
.
begin
(),
expected_base
.
end
(),
'/'
,
'\\'
);
#endif
spdlog
::
filename_t
basename
;
spdlog
::
filename_t
ext
;
std
::
tie
(
basename
,
ext
)
=
file_helper
::
split_by_extension
(
filename
);
...
...
@@ -81,22 +77,22 @@ static void test_split_ext(const char *fname, const char *expect_base, const cha
TEST_CASE
(
"file_helper_split_by_extension"
,
"[file_helper::split_by_extension()]]"
)
{
test_split_ext
(
"mylog.txt"
,
"mylog"
,
".txt"
);
test_split_ext
(
".mylog.txt"
,
".mylog"
,
".txt"
);
test_split_ext
(
".mylog"
,
".mylog"
,
""
);
test_split_ext
(
"/aaa/bb.d/mylog"
,
"/aaa/bb.d/mylog"
,
""
);
test_split_ext
(
"/aaa/bb.d/mylog.txt"
,
"/aaa/bb.d/mylog"
,
".txt"
);
test_split_ext
(
"aaa/bbb/ccc/mylog.txt"
,
"aaa/bbb/ccc/mylog"
,
".txt"
);
test_split_ext
(
"aaa/bbb/ccc/mylog."
,
"aaa/bbb/ccc/mylog."
,
""
);
test_split_ext
(
"aaa/bbb/ccc/.mylog.txt"
,
"aaa/bbb/ccc/.mylog"
,
".txt"
);
test_split_ext
(
"/aaa/bbb/ccc/mylog.txt"
,
"/aaa/bbb/ccc/mylog"
,
".txt"
);
test_split_ext
(
"/aaa/bbb/ccc/.mylog"
,
"/aaa/bbb/ccc/.mylog"
,
""
);
test_split_ext
(
"../mylog.txt"
,
"../mylog"
,
".txt"
);
test_split_ext
(
".././mylog.txt"
,
".././mylog"
,
".txt"
);
test_split_ext
(
".././mylog.txt/xxx"
,
".././mylog.txt/xxx"
,
""
);
test_split_ext
(
"/mylog.txt"
,
"/mylog"
,
".txt"
);
test_split_ext
(
"//mylog.txt"
,
"//mylog"
,
".txt"
);
test_split_ext
(
""
,
""
,
""
);
test_split_ext
(
"."
,
"."
,
""
);
test_split_ext
(
"..txt"
,
"."
,
".txt"
);
test_split_ext
(
SPDLOG_FILENAME_T
(
"mylog.txt"
),
SPDLOG_FILENAME_T
(
"mylog"
),
SPDLOG_FILENAME_T
(
".txt"
)
);
test_split_ext
(
SPDLOG_FILENAME_T
(
".mylog.txt"
),
SPDLOG_FILENAME_T
(
".mylog"
),
SPDLOG_FILENAME_T
(
".txt"
)
);
test_split_ext
(
SPDLOG_FILENAME_T
(
".mylog"
),
SPDLOG_FILENAME_T
(
".mylog"
),
SPDLOG_FILENAME_T
(
""
)
);
test_split_ext
(
SPDLOG_FILENAME_T
(
"/aaa/bb.d/mylog"
),
SPDLOG_FILENAME_T
(
"/aaa/bb.d/mylog"
),
SPDLOG_FILENAME_T
(
""
)
);
test_split_ext
(
SPDLOG_FILENAME_T
(
"/aaa/bb.d/mylog.txt"
),
SPDLOG_FILENAME_T
(
"/aaa/bb.d/mylog"
),
SPDLOG_FILENAME_T
(
".txt"
)
);
test_split_ext
(
SPDLOG_FILENAME_T
(
"aaa/bbb/ccc/mylog.txt"
),
SPDLOG_FILENAME_T
(
"aaa/bbb/ccc/mylog"
),
SPDLOG_FILENAME_T
(
".txt"
)
);
test_split_ext
(
SPDLOG_FILENAME_T
(
"aaa/bbb/ccc/mylog."
),
SPDLOG_FILENAME_T
(
"aaa/bbb/ccc/mylog."
),
SPDLOG_FILENAME_T
(
""
)
);
test_split_ext
(
SPDLOG_FILENAME_T
(
"aaa/bbb/ccc/.mylog.txt"
),
SPDLOG_FILENAME_T
(
"aaa/bbb/ccc/.mylog"
),
SPDLOG_FILENAME_T
(
".txt"
)
);
test_split_ext
(
SPDLOG_FILENAME_T
(
"/aaa/bbb/ccc/mylog.txt"
),
SPDLOG_FILENAME_T
(
"/aaa/bbb/ccc/mylog"
),
SPDLOG_FILENAME_T
(
".txt"
)
);
test_split_ext
(
SPDLOG_FILENAME_T
(
"/aaa/bbb/ccc/.mylog"
),
SPDLOG_FILENAME_T
(
"/aaa/bbb/ccc/.mylog"
),
SPDLOG_FILENAME_T
(
""
)
);
test_split_ext
(
SPDLOG_FILENAME_T
(
"../mylog.txt"
),
SPDLOG_FILENAME_T
(
"../mylog"
),
SPDLOG_FILENAME_T
(
".txt"
)
);
test_split_ext
(
SPDLOG_FILENAME_T
(
".././mylog.txt"
),
SPDLOG_FILENAME_T
(
".././mylog"
),
SPDLOG_FILENAME_T
(
".txt"
)
);
test_split_ext
(
SPDLOG_FILENAME_T
(
".././mylog.txt/xxx"
),
SPDLOG_FILENAME_T
(
".././mylog.txt/xxx"
),
SPDLOG_FILENAME_T
(
""
)
);
test_split_ext
(
SPDLOG_FILENAME_T
(
"/mylog.txt"
),
SPDLOG_FILENAME_T
(
"/mylog"
),
SPDLOG_FILENAME_T
(
".txt"
)
);
test_split_ext
(
SPDLOG_FILENAME_T
(
"//mylog.txt"
),
SPDLOG_FILENAME_T
(
"//mylog"
),
SPDLOG_FILENAME_T
(
".txt"
)
);
test_split_ext
(
SPDLOG_FILENAME_T
(
""
),
SPDLOG_FILENAME_T
(
""
),
SPDLOG_FILENAME_T
(
""
)
);
test_split_ext
(
SPDLOG_FILENAME_T
(
"."
),
SPDLOG_FILENAME_T
(
"."
),
SPDLOG_FILENAME_T
(
""
)
);
test_split_ext
(
SPDLOG_FILENAME_T
(
"..txt"
),
SPDLOG_FILENAME_T
(
"."
),
SPDLOG_FILENAME_T
(
".txt"
)
);
}
tests/test_file_logging.cpp
View file @
a453bccf
...
...
@@ -3,10 +3,13 @@
*/
#include "includes.h"
#define SIMPLE_LOG "test_logs/simple_log"
#define ROTATING_LOG "test_logs/rotating_log"
TEST_CASE
(
"simple_file_logger"
,
"[simple_logger]]"
)
{
prepare_logdir
();
s
td
::
string
filename
=
"test_logs/simple_log"
;
s
pdlog
::
filename_t
filename
=
SPDLOG_FILENAME_T
(
SIMPLE_LOG
)
;
auto
logger
=
spdlog
::
create
<
spdlog
::
sinks
::
basic_file_sink_mt
>
(
"logger"
,
filename
);
logger
->
set_pattern
(
"%v"
);
...
...
@@ -15,29 +18,29 @@ TEST_CASE("simple_file_logger", "[simple_logger]]")
logger
->
info
(
"Test message {}"
,
2
);
logger
->
flush
();
require_message_count
(
filename
,
2
);
require_message_count
(
SIMPLE_LOG
,
2
);
using
spdlog
::
details
::
os
::
default_eol
;
REQUIRE
(
file_contents
(
filename
)
==
fmt
::
format
(
"Test message 1{}Test message 2{}"
,
default_eol
,
default_eol
));
REQUIRE
(
file_contents
(
SIMPLE_LOG
)
==
fmt
::
format
(
"Test message 1{}Test message 2{}"
,
default_eol
,
default_eol
));
}
TEST_CASE
(
"flush_on"
,
"[flush_on]]"
)
{
prepare_logdir
();
s
td
::
string
filename
=
"test_logs/simple_log"
;
s
pdlog
::
filename_t
filename
=
SPDLOG_FILENAME_T
(
SIMPLE_LOG
)
;
auto
logger
=
spdlog
::
create
<
spdlog
::
sinks
::
basic_file_sink_mt
>
(
"logger"
,
filename
);
logger
->
set_pattern
(
"%v"
);
logger
->
set_level
(
spdlog
::
level
::
trace
);
logger
->
flush_on
(
spdlog
::
level
::
info
);
logger
->
trace
(
"Should not be flushed"
);
REQUIRE
(
count_lines
(
filename
)
==
0
);
REQUIRE
(
count_lines
(
SIMPLE_LOG
)
==
0
);
logger
->
info
(
"Test message {}"
,
1
);
logger
->
info
(
"Test message {}"
,
2
);
require_message_count
(
filename
,
3
);
require_message_count
(
SIMPLE_LOG
,
3
);
using
spdlog
::
details
::
os
::
default_eol
;
REQUIRE
(
file_contents
(
filename
)
==
REQUIRE
(
file_contents
(
SIMPLE_LOG
)
==
fmt
::
format
(
"Should not be flushed{}Test message 1{}Test message 2{}"
,
default_eol
,
default_eol
,
default_eol
));
}
...
...
@@ -45,7 +48,7 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
{
prepare_logdir
();
size_t
max_size
=
1024
*
10
;
s
td
::
string
basename
=
"test_logs/rotating_log"
;
s
pdlog
::
filename_t
basename
=
SPDLOG_FILENAME_T
(
ROTATING_LOG
)
;
auto
logger
=
spdlog
::
rotating_logger_mt
(
"logger"
,
basename
,
max_size
,
0
);
for
(
int
i
=
0
;
i
<
10
;
++
i
)
...
...
@@ -54,14 +57,14 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
}
logger
->
flush
();
require_message_count
(
basename
,
10
);
require_message_count
(
ROTATING_LOG
,
10
);
}
TEST_CASE
(
"rotating_file_logger2"
,
"[rotating_logger]]"
)
{
prepare_logdir
();
size_t
max_size
=
1024
*
10
;
s
td
::
string
basename
=
"test_logs/rotating_log"
;
s
pdlog
::
filename_t
basename
=
SPDLOG_FILENAME_T
(
ROTATING_LOG
)
;
{
// make an initial logger to create the first output file
...
...
@@ -83,7 +86,7 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
logger
->
flush
();
require_message_count
(
basename
,
10
);
require_message_count
(
ROTATING_LOG
,
10
);
for
(
int
i
=
0
;
i
<
1000
;
i
++
)
{
...
...
@@ -92,7 +95,6 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
}
logger
->
flush
();
REQUIRE
(
get_filesize
(
basename
)
<=
max_size
);
auto
filename1
=
basename
+
".1"
;
REQUIRE
(
get_filesize
(
filename1
)
<=
max_size
);
REQUIRE
(
get_filesize
(
ROTATING_LOG
)
<=
max_size
);
REQUIRE
(
get_filesize
(
ROTATING_LOG
".1"
)
<=
max_size
);
}
tests/test_pattern_formatter.cpp
View file @
a453bccf
...
...
@@ -372,7 +372,7 @@ TEST_CASE("clone-custom_formatter", "[pattern_formatter]")
//
#ifdef _WIN32
static
const
char
*
const
test_path
=
"
\\
a
\\
b
\\
myfile.cpp"
;
static
const
char
*
const
test_path
=
"
\\
a
\\
b
\\
c/
myfile.cpp"
;
#else
static
const
char
*
const
test_path
=
"/a/b//myfile.cpp"
;
#endif
...
...
tests/utils.cpp
View file @
a453bccf
...
...
@@ -84,7 +84,7 @@ bool ends_with(std::string const &value, std::string const &ending)
std
::
size_t
count_files
(
const
std
::
string
&
folder
)
{
size_t
counter
=
0
;
WIN32_FIND_DATA
ffd
;
WIN32_FIND_DATA
A
ffd
;
// Start iterating over the files in the folder directory.
HANDLE
hFind
=
::
FindFirstFileA
((
folder
+
"
\\
*"
).
c_str
(),
&
ffd
);
...
...
@@ -94,7 +94,7 @@ std::size_t count_files(const std::string &folder)
{
if
(
ffd
.
cFileName
[
0
]
!=
'.'
)
counter
++
;
}
while
(
::
FindNextFile
(
hFind
,
&
ffd
)
!=
0
);
}
while
(
::
FindNextFile
A
(
hFind
,
&
ffd
)
!=
0
);
::
FindClose
(
hFind
);
}
else
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment