Cancel --edit when the editor closes without saving

This commit is contained in:
Frédéric Mangano 2020-10-24 12:58:01 +02:00
parent b3b092d241
commit ba2236facb
6 changed files with 41 additions and 24 deletions

View File

@ -283,25 +283,39 @@ static ot::status edit_tags_interactively(ot::opus_tags& tags, const std::option
return {ot::st::error,
"No editor specified in environment variable VISUAL or EDITOR."};
// Building the temporary tags file.
std::string tags_path = base_path.value_or("tags") + ".XXXXXX.opustags";
int fd = mkstemps(const_cast<char*>(tags_path.data()), 9);
FILE* tags_file;
if (fd == -1 || (tags_file = fdopen(fd, "w")) == nullptr)
return {ot::st::standard_error,
"Could not open '" + tags_path + "': " + strerror(errno)};
ot::print_comments(tags.comments, tags_file);
fputs("\n"
"# Edit these tags to your liking and close your editor to apply them.\n"
"# If you delete all the tags however, tag edition will be cancelled.\n",
tags_file);
if (fclose(tags_file) != 0)
return {ot::st::standard_error, "fclose error: "s + strerror(errno)};
return {ot::st::standard_error, tags_path + ": fclose error: "s + strerror(errno)};
ot::status rc = ot::run_editor(editor, tags_path.c_str());
if (rc != ot::st::ok)
// Spawn the editor, and watch the modification timestamps.
ot::status rc;
timespec before, after;
if ((rc = ot::get_file_timestamp(tags_path.c_str(), before)) != ot::st::ok)
return rc;
ot::status editor_rc = ot::run_editor(editor, tags_path.c_str());
if ((rc = ot::get_file_timestamp(tags_path.c_str(), after)) != ot::st::ok)
return rc; // probably because the file was deleted
bool modified = (before.tv_sec != after.tv_sec || before.tv_nsec != after.tv_nsec);
if (editor_rc != ot::st::ok) {
if (modified)
fprintf(stderr, "warning: Leaving %s on the disk.\n", tags_path.c_str());
else
remove(tags_path.c_str());
return rc;
} else if (!modified) {
remove(tags_path.c_str());
fputs("Cancelling edition because the tags file was not modified.\n", stderr);
return ot::st::cancel;
}
// Applying the new tags.
tags_file = fopen(tags_path.c_str(), "r");
if (tags_file == nullptr)
return {ot::st::standard_error, "Error opening " + tags_path + ": " + strerror(errno)};
@ -311,11 +325,6 @@ static ot::status edit_tags_interactively(ot::opus_tags& tags, const std::option
}
fclose(tags_file);
if (tags.comments.size() == 0) {
remove(tags_path.c_str()); // its empty anyway
return {ot::st::error, "Tag edition was cancelled because all the tags were deleted."};
}
// Remove the temporary tags file only on success, because unlike the
// partial Ogg file that is irrecoverable, the edited tags file
// contains user data, so lets leave users a chance to recover it.

View File

@ -27,6 +27,7 @@
#include <iconv.h>
#include <ogg/ogg.h>
#include <stdio.h>
#include <time.h>
#include <functional>
#include <list>
@ -58,6 +59,7 @@ enum class st {
error,
standard_error, /**< Error raised by the C standard library. */
int_overflow,
cancel,
/* System */
badly_encoded,
information_lost,
@ -172,6 +174,12 @@ private:
*/
ot::status run_editor(const char* editor, const char* path);
/**
* Return the specified paths mtime, i.e. the last data modification
* timestamp.
*/
ot::status get_file_timestamp(const char* path, timespec& mtime);
/** \} */
/***********************************************************************************************//**

View File

@ -177,3 +177,12 @@ ot::status ot::run_editor(const char* editor, const char* path)
return st::ok;
}
ot::status ot::get_file_timestamp(const char* path, timespec& mtime)
{
struct stat st;
if (stat(path, &st) == -1)
return {st::standard_error, path + ": stat error: "s + strerror(errno)};
mtime = st.st_mtim; // more precise than st_mtime
return st::ok;
}

View File

@ -14,7 +14,6 @@ add_executable(oggdump EXCLUDE_FROM_ALL oggdump.cc)
target_link_libraries(oggdump ot)
configure_file(gobble.opus . COPYONLY)
configure_file(emptier . COPYONLY)
add_custom_target(
check

View File

@ -1,6 +0,0 @@
#!/bin/sh
cat > "$1" << EOF
# Heres a file with nothing but comments and newlines.
EOF

View File

@ -230,14 +230,12 @@ $ENV{EDITOR} = 'sed -i -e y/a/A/';
is_deeply(opustags(qw(gobble.opus --add artist=aaah -o screaming.opus -e)), ['', '', 0], 'edit a file with EDITOR');
is(md5('screaming.opus'), '682229df1df6b0ca147e2778737d449e', 'the tags were modified');
$ENV{EDITOR} = './emptier';
is_deeply(opustags(qw(--add mystery=1 -i screaming.opus -e)), ['', "screaming.opus: error: Tag edition was cancelled because all the tags were deleted.\n", 256], 'edit a file with EDITOR');
$ENV{EDITOR} = 'true';
is_deeply(opustags(qw(--add mystery=1 -i screaming.opus -e)), ['', "Cancelling edition because the tags file was not modified.\n", 256], 'close -e without saving');
is(md5('screaming.opus'), '682229df1df6b0ca147e2778737d449e', 'the tags were not modified');
$ENV{EDITOR} = '';
unlink('screaming.opus');
####################################################################################################
# Test muxed streams