Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit bef2388

Browse files
Rafi Wienerrafiw
Rafi Wiener
authored andcommittedDec 7, 2019
add read and write file validators #249
Signed-off-by: Rafi Wiener <rafiw@mellanox.com> Signed-off-by: Rafi Wiener <rafiwiener@gmail.com>
1 parent d9379cc commit bef2388

File tree

5 files changed

+77
-4
lines changed

5 files changed

+77
-4
lines changed
 

‎README.md

+2
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,8 @@ CLI11 has several Validators built-in that perform some common checks
377377
- `CLI::AsNumberWithUnit(...)`:🆕 Modify the `<NUMBER> <UNIT>` pair by matching the unit and multiplying the number by the corresponding factor. It can be used as a base for transformers, that accept things like size values (`1 KB`) or durations (`0.33 ms`).
378378
- `CLI::AsSizeValue(...)`: 🆕 Convert inputs like `100b`, `42 KB`, `101 Mb`, `11 Mib` to absolute values. `KB` can be configured to be interpreted as 10^3 or 2^10.
379379
- `CLI::ExistingFile`: Requires that the file exists if given.
380+
- `CLI::ExistingReadFile`: Requires that the file given exists and have permission to read it.
381+
- `CLI::ExistingWriteFile`: Requires that the file given exists and have permission to write to it.
380382
- `CLI::ExistingDirectory`: Requires that the directory exists.
381383
- `CLI::ExistingPath`: Requires that the path (file or directory) exists.
382384
- `CLI::NonexistentPath`: Requires that the path does not exist.

‎examples/validators.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ int main(int argc, char **argv) {
55
CLI::App app("Validator checker");
66

77
std::string file;
8-
app.add_option("-f,--file,file", file, "File name")->check(CLI::ExistingFile);
8+
app.add_option("-f,--file,file", file, "File name")->check(CLI::ExistingReadableFile);
99

1010
int count;
1111
app.add_option("-v,--value", count, "Value in range")->check(CLI::Range(3, 6));

‎include/CLI/Validators.hpp

+20-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <cmath>
99
#include <functional>
1010
#include <iostream>
11+
#include <fstream>
1112
#include <limits>
1213
#include <map>
1314
#include <memory>
@@ -267,7 +268,7 @@ class CustomValidator : public Validator {
267268
namespace detail {
268269

269270
/// CLI enumeration of different file types
270-
enum class path_type { nonexistant, file, directory };
271+
enum class path_type { nonexistant, file, directory, bad_permission };
271272

272273
#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
273274
/// get the type of the path from a file name
@@ -307,15 +308,25 @@ inline path_type check_path(const char *file) {
307308
/// Check for an existing file (returns error message if check fails)
308309
class ExistingFileValidator : public Validator {
309310
public:
310-
ExistingFileValidator() : Validator("FILE") {
311-
func_ = [](std::string &filename) {
311+
ExistingFileValidator(std::ios_base::openmode mode = std::ios::in) : Validator("FILE") {
312+
func_ = [mode](std::string &filename) {
312313
auto path_result = check_path(filename.c_str());
313314
if(path_result == path_type::nonexistant) {
314315
return "File does not exist: " + filename;
315316
}
316317
if(path_result == path_type::directory) {
317318
return "File is actually a directory: " + filename;
318319
}
320+
if(mode){
321+
std::fstream myfile;
322+
try {
323+
myfile.open(filename, mode);
324+
} catch(const std::exception &err) {
325+
}
326+
if(myfile.bad()){
327+
return "File doesn't have the wanted permission: " + filename;
328+
}
329+
}
319330
return std::string();
320331
};
321332
}
@@ -444,6 +455,12 @@ class Number : public Validator {
444455
/// Check for existing file (returns error message if check fails)
445456
const detail::ExistingFileValidator ExistingFile;
446457

458+
/// Check that the file exist and available for read
459+
const detail::ExistingFileValidator ExistingReadableFile(std::ios::in);
460+
461+
/// Check that the file exist and available for write
462+
const detail::ExistingFileValidator ExistingWritableFile(std::ios::out);
463+
447464
/// Check for an existing directory (returns error message if check fails)
448465
const detail::ExistingDirectoryValidator ExistingDirectory;
449466

‎tests/AppTest.cpp

+45
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "app_helper.hpp"
22
#include <complex>
33
#include <cstdlib>
4+
#include <sys/stat.h>
45

56
#include "gmock/gmock.h"
67

@@ -1697,6 +1698,50 @@ TEST_F(TApp, FileExists) {
16971698
EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
16981699
}
16991700

1701+
TEST_F(TApp, FileExistsForRead) {
1702+
std::string myfile{"TestNonFileNotUsed.txt"};
1703+
EXPECT_FALSE(CLI::ExistingReadableFile(myfile).empty());
1704+
1705+
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
1706+
EXPECT_TRUE(ok);
1707+
1708+
std::string filename = "Failed";
1709+
app.add_option("--file", filename)->check(CLI::ExistingReadableFile);
1710+
args = {"--file", myfile};
1711+
1712+
run();
1713+
1714+
EXPECT_EQ(myfile, filename);
1715+
#ifdef __linux__
1716+
my_chmod(myfile.c_str(), 0);
1717+
EXPECT_THROW(run(), CLI::ValidationError);
1718+
#endif
1719+
std::remove(myfile.c_str());
1720+
EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
1721+
}
1722+
1723+
TEST_F(TApp, FileExistsForWrite) {
1724+
std::string myfile{"TestNonFileNotUsed.txt"};
1725+
EXPECT_FALSE(CLI::ExistingWritableFile(myfile).empty());
1726+
1727+
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
1728+
EXPECT_TRUE(ok);
1729+
1730+
std::string filename = "Failed";
1731+
app.add_option("--file", filename)->check(CLI::ExistingWritableFile);
1732+
args = {"--file", myfile};
1733+
1734+
run();
1735+
EXPECT_EQ(myfile, filename);
1736+
1737+
my_chmod(myfile.c_str(), S_IREAD);
1738+
EXPECT_THROW(run(), CLI::ValidationError);
1739+
1740+
int ret = std::remove(myfile.c_str());
1741+
EXPECT_EQ(ret, 0);
1742+
EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
1743+
}
1744+
17001745
TEST_F(TApp, NotFileExists) {
17011746
std::string myfile{"TestNonFileNotUsed.txt"};
17021747
EXPECT_FALSE(CLI::ExistingFile(myfile).empty());

‎tests/app_helper.hpp

+9
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "gtest/gtest.h"
1010
#include <iostream>
11+
#include <sys/stat.h>
1112

1213
using input_t = std::vector<std::string>;
1314

@@ -55,3 +56,11 @@ inline void unset_env(std::string name) {
5556
unsetenv(name.c_str());
5657
#endif
5758
}
59+
60+
inline int my_chmod(const char *path, int mode) {
61+
#ifdef _WIN32
62+
return _chmod(path, mode);
63+
#else
64+
return chmod(path, mode);
65+
#endif
66+
}

0 commit comments

Comments
 (0)
Please sign in to comment.