clone a git repository using git init, fetch, and checkout - c++

Introduction
I'm doing experimentations using the popular libgit2 written in C.
I'm trying to do a clone but using an un-common way. In order, the git commands:
git init
git remote add origin https://repository.git
git fetch origin
git checkout master
By using git bash and the following commands, I can get an existing repository with all its history.
Question
Now, let's see my current C++ implementation. The following code is trying to copy the behaviour of the previous written git commands.
#define url "https://repository.git"
#define path "./"
#define user "user"
#define pass "pass"
/** credential callback **/
int credentials(git_cred **cred, const char *, const char *, unsigned int, void *) {
return git_cred_userpass_plaintext_new(cred, user, pass);
}
class Git {
public:
Git() {
git_libgit2_init();
}
~Git() {
git_repository_free(repository);
git_libgit2_shutdown();
}
void update() {
init();
fetch();
checkout();
}
private:
void init() {
assertSuccess(git_repository_init(&repository, path, GIT_CVAR_FALSE));
git_remote *remote = nullptr;
git_remote_callbacks options = GIT_REMOTE_CALLBACKS_INIT;
assertSuccess(git_remote_create(&remote, repository, "origin", url));
options.credentials = credentials;
git_remote_connect(remote, GIT_DIRECTION_FETCH, &options, nullptr, nullptr);
}
void fetch() {
git_remote* remote = nullptr;
assertSuccess(git_remote_lookup(&remote, repository, "origin"));
git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
options.callbacks.credentials = credentials;
assertSuccess(git_remote_fetch(remote, nullptr, &options, nullptr));
}
void checkout() {
git_checkout_options options = GIT_CHECKOUT_OPTIONS_INIT;
options.checkout_strategy = GIT_CHECKOUT_FORCE;
assertSuccess(git_checkout_head(repository, &options));
assertSuccess(git_checkout_index(repository, nullptr, &options));
assertSuccess(git_repository_set_head(repository, "refs/heads/master"));
git_object *treeish = nullptr;
assertSuccess(git_revparse_single(&treeish, repository, "master"));
assertSuccess(git_checkout_tree(repository, treeish, &options));
}
void assertSuccess(int error) {
if (!error) return;
const git_error *e = giterr_last();
std::cout << "code: " << e->klass << " error: " << e->message << std::endl;
exit(1);
}
private:
git_repository *repository = nullptr;
};
int main() {
Git git;
git.update();
return 0;
}
Obviously, this does not work. Running this program (calling Git().update()), I'm getting the following error during the checkout step:
code: 4 error: reference 'refs/heads/master' not found
The git repository has been created and I can see the remote origin that has been set successfully though git bash. I can do a manual git checkout master from git bash so I guess my current implementation of checkout is a failure.
Could someone highlight me about this error? I couldn't find enough resources nor support on all found example on internet.
EDIT
Since testing my code might help, let me give my CMakeLists.txt for compile libgit2. (source code https://github.com/libgit2/libgit2)
cmake_minimum_required(VERSION 3.13)
project(test)
include_directories(libgit/include)
LINK_DIRECTORIES(${LIBSSH2_LIBRARY_DIRS})
add_subdirectory(libgit)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_BUILD_TYPE Release)
add_executable(test src/Git.h)
target_link_libraries(test git2)

The missing link is that, since you're building a repository from scratch, your repository is still unborn (ie, its HEAD points to a non-existent refs/heads/master ref). On top of that, checkout's only concern in libgit2-land is about bringing out files from the ODB, it doesn't write or update references.
Hence, you are missing the step where git checkout uses (in all likelihood) git update-ref to make master point to origin/master's OID, and you can do that via git_reference_create and friends.
Something like the following (brain-compiled):
static int setup_tracking_branch(char *branch_name, git_reference *upstream)
{
git_reference *tracking;
git_oid up_oid = git_reference_target_peel(upstream);
char *ui_name;
#if 0
/* should be constructed from `upstream`. IIRC there are some
* git_reference accessors that can help
* (eg. `refs/remotes/origin/heads/master` is `origin/master`).
*/
#else
ui_name = "origin/master";
#endif
if (git_reference_create_matching(&tracking,
git_reference_owner(upstream),
branch_name, up_oid, 0, NULL, "branch: created from %s", ui_name) < 0 ||
git_branch_set_upstream(tracking, git_reference_name(upstream)) < 0) {
printf("failed to create remote-tracking branch\n");
return -1;
}
cleanup:
git_reference_free(tracking);
return 0;
}
This takes the name you want the new branch to be (-b), and the remote branch to track (-t), though it is clearly not a complete reimplementation, or even correct, so YMMV.

Related

Catch2 installation on ubuntu 20.04 #include <catch2/catch.hpp>

I am trying to install Catch2 on ubuntu 20.04.
Used instruction from here.
This is what i do:
$ git clone https://github.com/catchorg/Catch2.git
$ cd Catch2
$ cmake -Bbuild -H. -DBUILD_TESTING=OFF
$ sudo cmake --build build/ --target install
Than it saing me that all ok: link for output.
BUT:
When I try to compile the example: // from here
main.cpp
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#define CATCH_CONFIG_ENABLE_BENCHMARKING
#include <catch2/catch.hpp>
std::uint64_t Fibonacci(std::uint64_t number) {
return number < 2 ? 1 : Fibonacci(number - 1) + Fibonacci(number - 2);
}
TEST_CASE("Fibonacci") {
CHECK(Fibonacci(0) == 1);
// some more asserts..
CHECK(Fibonacci(5) == 8);
// some more asserts..
// now let's benchmark:
BENCHMARK("Fibonacci 20") {
return Fibonacci(20);
};
BENCHMARK("Fibonacci 25") {
return Fibonacci(25);
};
BENCHMARK("Fibonacci 30") {
return Fibonacci(30);
};
BENCHMARK("Fibonacci 35") {
return Fibonacci(35);
};
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(Persistent-world LANGUAGES CXX)
add_executable(${PROJECT_NAME} main.cpp )
find_package(Catch2 REQUIRED)
target_link_libraries(${PROJECT_NAME} Catch2::Catch2)
It output such ERROR: catch2/catch.hpp: No such file or directory
Thanks in advance
The problem is quite simple: clonning catchorg/Catch2 now gets you a v3 branch by default, which works differently. The most important change is that it is no longer single header, and that the catch2/catch.hpp header no longer exists.
You can either switch to the v2 branch before configuring and installing the build, or adapt your code to the changes in v3, starting with this documentation on v2 -> v3 migration.
To get the default main, link against Catch2::Catch2WithMain target.
Admin helped me.
On catch v3. I need:
cmake_minimum_required(VERSION 3.5)
project(Catch2 LANGUAGES CXX)
add_executable(${PROJECT_NAME} main.cpp )
find_package(Catch2)
target_link_libraries(${PROJECT_NAME} Catch2::Catch2WithMain)
If you just link against Catch2::Catch2, you won't get the default main and have to write your own, and your own main needs to invoke the tests. See e.g. that
Than i understand that with main it should looks like:
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#define CATCH_CONFIG_ENABLE_BENCHMARKING
#include <catch2/catch_all.hpp>
std::uint64_t Fibonacci(std::uint64_t number) {
return number < 2 ? 1 : Fibonacci(number - 1) + Fibonacci(number - 2);
}
TEST_CASE("Fibonacci") {
CHECK(Fibonacci(0) == 1);
// some more asserts..
CHECK(Fibonacci(5) == 8);
// some more asserts..
// now let's benchmark:
BENCHMARK("Fibonacci 20") {
return Fibonacci(20);
};
BENCHMARK("Fibonacci 25") {
return Fibonacci(25);
};
BENCHMARK("Fibonacci 30") {
return Fibonacci(30);
};
BENCHMARK("Fibonacci 35") {
return Fibonacci(35);
};
}
int main( int argc, char* argv[] )
{
Catch::Session session; // There must be exactly one instance
// writing to session.configData() here sets defaults
// this is the preferred way to set them
int returnCode = session.applyCommandLine( argc, argv );
if( returnCode != 0 ) // Indicates a command line error
return returnCode;
// writing to session.configData() or session.Config() here
// overrides command line args
// only do this if you know you need to
int numFailed = session.run();
// numFailed is clamped to 255 as some unices only use the lower 8 bits.
// This clamping has already been applied, so just return it here
// You can also do any post run clean-up here
return numFailed;
}

How to use functions, written on c++, in node.js

I have a basic c++ file.
I have a node server. One of the functions there gets a number, makes calculation and returns another number. And I want to make this function work faster rewriting it on c++. So I want to be able to call the function written on c++ in a .cpp file from .js file.
When I write node index.js all .cpp files should be compiled, then functions from them should be "require();" in .js files and then I want to be able to use them in .js file calling as common functions: e.g. calc(number, param);. How do I do it?
I tried to read some articles about that and watched some videos on YouTube and made something, but when it starts I get a lor of errors like
gyp ERR! find Python Python is not set from command line or npm configuration
gyp ERR! find Python Python is not set from environment variable PYTHON
gyp ERR! find Python checking if "python" can be used
gyp ERR! find Python - "python" is not in PATH or produced an error
As I understand, it wants me to install python on my computer. But what for? I do not need python, I want to compile and execute c++ from node.js.
My implementation:
index.js
var testlib = require('./build/Release/testlib');
testlib.addThousandToNumber(20, function(err, res)
{
if (err)
console.error(err);
else
console.log(res);
});
package.json
"name": "testlib",
"version": "1.0.0",
"description": "",
"main": "run.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"install": "node-gyp clean configure build"
},
"author": "",
"license": "ISC",
"dependencies": {
"nan": "^2.14.1",
"node-gyp": "^7.0.0"
}
}
binding.gyp
"targets": [{
"target_name": "testlib",
"sources": ["testlib.cpp", "testworker.cpp"],
"include_dirs": ["<!(node -e \"require('nan')\""]
}]
}
testlib.cpp
#include <nan.h>
#include "testworker.h"
NAN_METHOD(addThoudsandToNumber)
{
auto number = Nan::To<int>(info[0]).FromJust();
auto *callback = new Nan::Callback(info[1].As<v8::Function());
Nan::AsyncQueueWorker(new TestWorker(number, callback));
}
NAN_MODULE_INIT(init)
{
Nan::SetMethod(target, "addThousandTonumber", addThousandToNumber);
}
NODU_MODULE(testlib, init);
testworker.cpp
#include "testworker.h"
void TestWorker::Execute()
{
for (int i = 0; i < 1000; i++)
{
myNumber++;
}
}
void TestWorker::HandleOKCallback()
{
Nan::HandleScope scope;
auto NumberJS = Nan::New(myNumber);
v8::Local<v8::Value> argv[] = {Nan::Undefined(), numberJS};
myCallback->Call(2, argv);
}
testworker.h
#include <nan.h>
class TestWorker : public Nan::AsyncWorker
{
public:
TestWorker(int number, Nan::Callback * callback) :
Nan::AsyncWorker(callback), myNumber(number), myCallback(callback) { }
~TestWorker() { delete callback }
void Execute();
void HandleOKCallback();
private:
int myNumber;
Nan::Callback * myCallback;
}
May be this is what you are looking for: cmake-js
"CMake.js is a Node.js/io.js native addon build tool which works (almost) exactly like node-gyp, but instead of gyp, it is based on CMake build system"
If your primary goal is just to make some high-performance calculations in C++ with the web interface, you might be interested in compiling and linking your function directly into a HTTP engine, like in Node++:
https://github.com/rekmus/nodepp
void npp_app_main()
{
if ( REQ("") ) // landing
{
// ...
}
else if ( REQ("calc") ) // calculate
{
int number;
// get number from the query string:
if ( QSI("number", &number) )
{
int result = calc(number, param);
// return result to the client:
OUT("{\"result\":%d}", result);
RES_CONTENT_TYPE_JSON;
}
}
else
{
RES_STATUS(404);
}
}
Besides the calc time, the engine latency is around 5-20 µs on an average PC (Linux). The added benefit is that Node++ is fairly independent (requires only OS, GCC and OpenSSL if you want HTTPS).

New Vulkan project in CLion on Mac OS will not create VkInstance

After my first successful attempt at a 3D engine using Java and OpenGL (LWJGL3), I have decided to try my hand at Vulkan, using C++.
I have barely any experience with C/C++ and I am aware of the steep learning curve of Vulkan. This is however not a problem.
I decided to follow this tutorial: https://vulkan-tutorial.com/Introduction
It has showed me how to create a new project with Vulkan using XCode (as I am on Mac OS Mojave). I would, however, like to continue the rest of the tutorial using CLion as I would be switching between multiple operating systems.
I tried my hand at creating a CLion project and succeeded in making my first CMakeLists file, however something seems to be wrong. The file currently consists of the following:
cmake_minimum_required(VERSION 3.12)
project(VulkanTesting)
set(CMAKE_CXX_STANDARD 14)
add_executable(VulkanTesting main.cpp)
include_directories(/usr/local/include)
include_directories(/Users/[username]/Documents/Vulkan/SDK/vulkansdk-macos-1.1.92.1/macOS/include)
target_link_libraries(VulkanTesting /usr/local/lib/libglfw.3.3.dylib)
target_link_libraries(VulkanTesting /Users/[username]/Documents/Vulkan/SDK/vulkansdk-macos-1.1.92.1/macOS/lib/libvulkan.1.dylib)
target_link_libraries(VulkanTesting /Users/[username]/Documents/Vulkan/SDK/vulkansdk-macos-1.1.92.1/macOS/lib/libvulkan.1.1.92.dylib)
# Don't know if I need the next two lines
link_directories(/usr/local/lib)
link_directories(/Users/[username]/Documents/Vulkan/SDK/vulkansdk-macos-1.1.92.1/macOS/lib)
The reason I showed the above file will become apparent in the question.
The 'Program' so far is the following:
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <iostream>
#include <stdexcept>
#include <functional>
#include <cstdlib>
#include <vector>
const int WIDTH = 800;
const int HEIGHT = 600;
class HelloTriangleApplication {
public:
void run() {
initWindow();
initVulkan();
mainLoop();
cleanup();
}
private:
GLFWwindow* window;
VkInstance instance;
void initWindow(){
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
window = glfwCreateWindow(WIDTH, HEIGHT, "My first Vulkan window", nullptr, nullptr);
}
void initVulkan() {
createInstance();
}
void createInstance(){
// Instantiate Application Info
VkApplicationInfo applicationInfo = {};
applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
applicationInfo.pApplicationName = "Hello Triangle";
applicationInfo.applicationVersion = VK_MAKE_VERSION(1,0,0);
applicationInfo.pEngineName = "No Engine";
applicationInfo.engineVersion = VK_MAKE_VERSION(1,0,0);
applicationInfo.apiVersion = VK_API_VERSION_1_0;
// Instantiate Instance Creation Info
VkInstanceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &applicationInfo;
// Get GLFW platform specific extensions
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
// Fill in required extensions in Instance Creation Info
createInfo.enabledExtensionCount = glfwExtensionCount;
createInfo.ppEnabledExtensionNames = glfwExtensions;
// For validation layers, this is a later step in the tutorial.
createInfo.enabledLayerCount = 0;
// Create the Vulkan instance, and check if it was successful.
VkResult result = vkCreateInstance(&createInfo, nullptr, &instance);
if(result != VK_SUCCESS){
std::cout << "glfwExtensionCount: " << glfwExtensionCount << "\n";
std::cout << "glfwExtensionNames: " << &glfwExtensions << "\n";
std::cout << "result: " << result << "\n";
throw std::runtime_error("Failed to create Vulkan Instance");
}
}
void mainLoop() {
while(!glfwWindowShouldClose(window)){
glfwPollEvents();
}
}
void cleanup() {
glfwDestroyWindow(window);
glfwTerminate();
}
};
int main() {
HelloTriangleApplication app;
try {
app.run();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
The problem I am having is that when I try to run the program, it will not create a VkInstance. The function returns VK_ERROR_INCOMPATIBLE_DRIVER. Now, I doubt that the driver is in fact incompatible as I have run the demo applications that came with the Vulkan SDK for one, and for another I have been able to run the exact same 'program' in XCode. When I investigated the problem a bit further, I noticed that the glfwGetRequiredInstanceExtensions function returns no extensions when the program is run in CLion like this, but does return one in the XCode equivalent.
This all leads me to believe that there is something I have done wrong in linking the libraries/frameworks in the Cmake file because I am aware of the fact that Vulkan is not directly supported in Mac OS, but instead (somehow?) passes through a layer to communicate with Metal.
Do I need to specify a way for the program to pass its Vulkan functionality through a Metal layer, and is this done automagically in XCode, or is there another problem with my approach?
Any help would be greatly appreciated!
You might want to look at the MacOS Getting Started Guide on the LunarXchange website and in your SDK. There is a section at the end that shows how to use CMake to build a Vulkan app and run it on MacOS. You also may want to use the FindVulkan CMake module instead of manually setting the include directories and the target link libraries.
But my first guess about your specific problem is that you may not be setting the VK_ICD_FILENAMES environment variable. You are correct in your observation that there is no direct support for Vulkan. Instead, the support is provided by the MoltenVK library which is treated as a Vulkan driver. But this "driver" is not installed in any system directory by the SDK. The SDK is just unzipped in your home directory structure, so you must tell the Vulkan loader where to find it via this environment variable.
Again, the CMake section at the end of the Getting Started Guide demonstrates the use of this environment variable. And the entire guide goes into additional detail about how the various Vulkan and MoltenVK components work.

Creating folders in C++

I have recently started working in C++ and came across this situation when I have to create a directory while executing my code. The code is working fine when I have to create a single folder but it fails when I have to create another folder withing this newly created folder.
Suppose, I am in C: and want to store my file in C:/A/B/ .The following piece of code using mkdir() works fine if I have to store my file in C:/A/ but fails when I am adding another folder B.
Following is my code snippet:
#include <sys/stat.h>
#include <string>
using namespace std;
int main()
{
string stringpath = "C:/A/B/";
int status = mkdir(stringpath.c_str(),0777);
if(status!=0)
{
//.....
}
else
{
//....
}
}
Can someone help me in creating this directory where I can have any number of folders inside the parent directory? (P.S:I have added the header files sys/stat.h,iostream and string)
This is how you do it in C++17:
#include <filesystem>
namespace fs = std::filesystem;
fs::create_directories("./a/b/c")
mkdir() creates only the last component of the specified path. In your example, it will create only B. If any of the parent directories do not exist (ie, if A does not exist), the function fails with ENOENT. You need to split up the path and call mkdir() for every intermediate directory in the path, ignoring EEXIST errors as you go.
status = mkdir("C:/A/", 0777);
if ((status < 0) && (errno != EEXIST)) ...
status = mkdir("C:/A/B/", 0777);
if ((status < 0) && (errno != EEXIST)) ...
If you don't want to handle this manually, use a wrapper that handles it for you, such as Boost's create_directories() function:
bool create_directories(const path& p);
bool create_directories(const path& p, system::error_code& ec);
Effects: Establishes the postcondition by calling create_directory() for any element of p that does not exist.
Postcondition: is_directory(p)
Returns: true if a new directory was created, otherwise false.
Throws: As specified in Error reporting.
Complexity: O(n+1)where n is the number of elements of p that do not exist.
You can call the following:
string stringpath = "C:/A/B/";
int status = mkdir(stringpath.c_str(),0777);
If
C:/A/ directory exists. If its not exists, then do the following:
string stringpath = "C:/A/";
int status = mkdir(stringpath.c_str(),0777);
stringpath = "C:/A/B/";
int status = mkdir(stringpath.c_str(),0777);
In C++11 you can use the experimental functios:
#include <experimental/filesystem>
...
std::stringstream bufH;
bufH << dirName << fName;
if (!std::experimental::filesystem::exists(bufH.str()))
{
std::experimental::filesystem::create_directories(bufH.str());
}
Try the octal flag 7777 like this to have all the rights necessary to create this folder.
int status = mkdir(stringpath.c_str(), 7777);
Or do a chmod in the A folder like that :
chmod -r 7777 *

How to delete all files in a folder, but not delete the folder using NIX standard libraries?

I am trying to create a program that deletes the contents of the /tmp folder, I am using C/C++ on linux.
system("exec rm -r /tmp")
deletes everything in the folder but it deletes the folder too which I dont want.
Is there any way to do this by some sort of bash script, called via system(); or is there a direct way i can do this in C/C++?
My question is similar to this one, but im not on OS X... how to delete all files in a folder, but not the folder itself?
#include <stdio.h>
#include <dirent.h>
int main()
{
// These are data types defined in the "dirent" header
DIR *theFolder = opendir("path/of/folder");
struct dirent *next_file;
char filepath[256];
while ( (next_file = readdir(theFolder)) != NULL )
{
// build the path for each file in the folder
sprintf(filepath, "%s/%s", "path/of/folder", next_file->d_name);
remove(filepath);
}
closedir(theFolder);
return 0;
}
You don't want to spawn a new shell via system() or something like that - that's a lot of overhead to do something very simple and it makes unnecessary assumptions (and dependencies) about what's available on the system.
In C/C++, you could do:
system("exec rm -r /tmp/*")
In Bash, you could do:
rm -r /tmp/*
This will delete everything inside /tmp, but not /tmp itself.
you can do
system("exec find /tmp -mindepth 1 -exec rm {} ';'");
by using use the wildcard * character you can delete all the files with any type of extension.
system("exec rm -r /tmp/*")
In C/C++ you can use (including hidden directories):
system("rm -r /tmp/* /tmp/.*");
system("find /tmp -mindepth 1 -delete");
But what if 'rm' or 'find' utilities are not availabe to sh?, better go 'ftw' and 'remove':
#define _XOPEN_SOURCE 500
#include <ftw.h>
static int remove_cb(const char *fpath, const struct stat *sb, int typeFlag, struct FTW *ftwbuf)
{
if (ftwbuf->level)
remove(fpath);
return 0;
}
int main(void)
{
nftw("./dir", remove_cb, 10, FTW_DEPTH);
return 0;
}
I realize this is very old question, but building on Demitri's great answer I created a function that will recursively delete files in subfolders if desired
It also does some error handling, in that it passes back errno. The function header is written for parsing by doxygen. This function works in the simple example cases I used, and deletes hidden folders and hidden files.
I hope this helps someone else in the future
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#define SUCCESS_STAT 0
/**
* checks if a specific directory exists
* #param dir_path the path to check
* #return if the path exists
*/
bool dirExists(std::string dir_path)
{
struct stat sb;
if (stat(dir_path.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode))
return true;
else
return false;
}
/**
* deletes all the files in a folder (but not the folder itself). optionally
* this can traverse subfolders and delete all contents when recursive is true
* #param dirpath the directory to delete the contents of (can be full or
* relative path)
* #param recursive true = delete all files/folders in all subfolders
* false = delete only files in toplevel dir
* #return SUCCESS_STAT on success
* errno on failure, values can be from unlink or rmdir
* #note this does NOT delete the named directory, only its contents
*/
int DeleteFilesInDirectory(std::string dirpath, bool recursive)
{
if (dirpath.empty())
return SUCCESS_STAT;
DIR *theFolder = opendir(dirpath.c_str());
struct dirent *next_file;
char filepath[1024];
int ret_val;
if (theFolder == NULL)
return errno;
while ( (next_file = readdir(theFolder)) != NULL )
{
// build the path for each file in the folder
sprintf(filepath, "%s/%s", dirpath.c_str(), next_file->d_name);
//we don't want to process the pointer to "this" or "parent" directory
if ((strcmp(next_file->d_name,"..") == 0) ||
(strcmp(next_file->d_name,"." ) == 0) )
{
continue;
}
//dirExists will check if the "filepath" is a directory
if (dirExists(filepath))
{
if (!recursive)
//if we aren't recursively deleting in subfolders, skip this dir
continue;
ret_val = DeleteFilesInDirectory(filepath, recursive);
if (ret_val != SUCCESS_STAT)
{
closedir(theFolder);
return ret_val;
}
}
ret_val = remove(filepath);
//ENOENT occurs when i folder is empty, or is a dangling link, in
//which case we will say it was a success because the file is gone
if (ret_val != SUCCESS_STAT && ret_val != ENOENT)
{
closedir(theFolder);
return ret_val;
}
}
closedir(theFolder);
return SUCCESS_STAT;
}
You could use nftw(3). First, make a pass to collect the set of file paths to remove. Then use unlink (for non-directories) and rmdir(2) in a second pass
From C++17 onwards you can use std::filesystem. The code below will use directory_iterator to list all the files and subdirectories in a directory and call remove_all to delete them:
#include <filesystem>
namespace fs = std::filesystem;
void delete_dir_content(const fs::path& dir_path) {
for (auto& path: fs::directory_iterator(dir_path)) {
fs::remove_all(path);
}
}
Note that this will throw a filesystem_error exception on underlying OS API errors. You can avoid this with:
void delete_dir_content(const fs::path& dir_path) {
for (auto& path: fs::directory_iterator(dir_path)) {
std::error_code err;
std::uintmax_t n = fs::remove_all(path, err);
if (static_cast<std::uintmax_t>(-1) == n) {
std::cout << "Failed to remove_all(" << path << ") with error: " << err.message() << std::endl;
}
}
}