mirror of
https://github.com/fmang/opustags.git
synced 2025-01-15 20:53:16 +01:00
introduce partial files
This commit is contained in:
parent
289391a9df
commit
a74ea34352
@ -22,6 +22,7 @@ add_library(
|
||||
src/cli.cc
|
||||
src/ogg.cc
|
||||
src/opus.cc
|
||||
src/system.cc
|
||||
)
|
||||
target_link_libraries(libopustags PUBLIC ${OGG_LIBRARIES})
|
||||
|
||||
|
@ -83,6 +83,11 @@ struct status {
|
||||
std::string message;
|
||||
};
|
||||
|
||||
/***********************************************************************************************//**
|
||||
* \defgroup system System
|
||||
* \{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Smart auto-closing FILE* handle.
|
||||
*
|
||||
@ -92,6 +97,37 @@ struct file : std::unique_ptr<FILE, decltype(&fclose)> {
|
||||
file(FILE* f = nullptr) : std::unique_ptr<FILE, decltype(&fclose)>(f, &fclose) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* A partial file is a temporary file created to store the result of something. When it is complete,
|
||||
* it is moved to a final destination. Open it with #open and then you can either #commit it to save
|
||||
* it to its destination, or you can #abort to delete the temporary file. When the #partial_file
|
||||
* object is destroyed, it deletes the currently opened temporary file, if any.
|
||||
*/
|
||||
class partial_file {
|
||||
public:
|
||||
~partial_file() { abort(); }
|
||||
/**
|
||||
* Open a temporary file meant to be moved to the specified destination file path. The
|
||||
* temporary file is created in the same directory as its destination in order to make the
|
||||
* final move operation instant.
|
||||
*/
|
||||
ot::status open(const char* destination);
|
||||
/** Close then move the partial file to its final location. */
|
||||
ot::status commit();
|
||||
/** Delete the temporary file. */
|
||||
void abort();
|
||||
/** Get the underlying FILE* handle. */
|
||||
FILE* get() { return file.get(); }
|
||||
/** Get the name of the temporary file. */
|
||||
const char* name() const { return file == nullptr ? nullptr : temporary_name.c_str(); }
|
||||
private:
|
||||
std::string temporary_name;
|
||||
std::string final_name;
|
||||
ot::file file;
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/***********************************************************************************************//**
|
||||
* \defgroup ogg Ogg
|
||||
* \{
|
||||
|
52
src/system.cc
Normal file
52
src/system.cc
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* \file src/system.cc
|
||||
* \ingroup system
|
||||
*
|
||||
* Provide a high-level interface to system-related features, like filesystem manipulations.
|
||||
*
|
||||
* Ideally, all OS-specific features should be grouped here.
|
||||
*
|
||||
* This modules shoumd not depend on any other opustags module.
|
||||
*/
|
||||
|
||||
#include <opustags.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
ot::status ot::partial_file::open(const char* destination)
|
||||
{
|
||||
abort();
|
||||
final_name = destination;
|
||||
temporary_name = final_name + ".XXXXXX.part";
|
||||
int fd = mkstemps(const_cast<char*>(temporary_name.data()), 5);
|
||||
if (fd == -1)
|
||||
return {st::standard_error,
|
||||
"Could not create a partial file for '" + final_name + "': " +
|
||||
strerror(errno)};
|
||||
file = fdopen(fd, "w");
|
||||
if (file == nullptr)
|
||||
return {st::standard_error,
|
||||
"Could not get the partial file handle to '" + temporary_name + "': " +
|
||||
strerror(errno)};
|
||||
return st::ok;
|
||||
}
|
||||
|
||||
ot::status ot::partial_file::commit()
|
||||
{
|
||||
if (file == nullptr)
|
||||
return st::ok;
|
||||
file.reset();
|
||||
if (rename(temporary_name.c_str(), final_name.c_str()) == -1)
|
||||
return {st::standard_error,
|
||||
"Could not move the result file '" + temporary_name + "' to '" +
|
||||
final_name + "': " + strerror(errno) + "."};
|
||||
return st::ok;
|
||||
}
|
||||
|
||||
void ot::partial_file::abort()
|
||||
{
|
||||
if (file == nullptr)
|
||||
return;
|
||||
file.reset();
|
||||
remove(temporary_name.c_str());
|
||||
}
|
@ -1,3 +1,6 @@
|
||||
add_executable(system.t EXCLUDE_FROM_ALL system.cc)
|
||||
target_link_libraries(system.t libopustags)
|
||||
|
||||
add_executable(opus.t EXCLUDE_FROM_ALL opus.cc)
|
||||
target_link_libraries(opus.t libopustags)
|
||||
|
||||
@ -12,5 +15,5 @@ configure_file(gobble.opus . COPYONLY)
|
||||
add_custom_target(
|
||||
check
|
||||
COMMAND prove "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
DEPENDS opustags gobble.opus opus.t ogg.t cli.t
|
||||
DEPENDS opustags gobble.opus system.t opus.t ogg.t cli.t
|
||||
)
|
||||
|
44
t/system.cc
Normal file
44
t/system.cc
Normal file
@ -0,0 +1,44 @@
|
||||
#include <opustags.h>
|
||||
#include "tap.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void check_partial_files()
|
||||
{
|
||||
static const char* result = "partial_file.test";
|
||||
std::string name;
|
||||
{
|
||||
ot::partial_file bad_tmp;
|
||||
if (bad_tmp.open("/dev/null") != ot::st::standard_error)
|
||||
throw failure("cannot open a device as a partial file");
|
||||
if (bad_tmp.open(result) != ot::st::ok)
|
||||
throw failure("could not open a simple result file");
|
||||
name = bad_tmp.name();
|
||||
if (name.size() != strlen(result) + 12 ||
|
||||
name.compare(0, strlen(result), result) != 0)
|
||||
throw failure("the temporary name is surprising: " + name);
|
||||
}
|
||||
if (access(name.c_str(), F_OK) != -1)
|
||||
throw failure("the bad temporary file was not deleted");
|
||||
|
||||
ot::partial_file good_tmp;
|
||||
if (good_tmp.open(result) != ot::st::ok)
|
||||
throw failure("could not open the result file");
|
||||
name = good_tmp.name();
|
||||
if (good_tmp.commit() != ot::st::ok)
|
||||
throw failure("could not commit the result file");
|
||||
if (access(name.c_str(), F_OK) != -1)
|
||||
throw failure("the good temporary file was not deleted");
|
||||
if (access(result, F_OK) != 0)
|
||||
throw failure("the final result file is not there");
|
||||
if (remove(result) != 0)
|
||||
throw failure("could not remove the result file");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
std::cout << "1..1\n";
|
||||
run(check_partial_files, "test partial files");
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user