mirror of
				https://github.com/fmang/opustags.git
				synced 2025-11-03 18:29:10 +01:00 
			
		
		
		
	Cancel --edit when the editor closes without saving
This commit is contained in:
		
							
								
								
									
										35
									
								
								src/cli.cc
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								src/cli.cc
									
									
									
									
									
								
							@@ -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()); // it’s 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 let’s leave users a chance to recover it.
 | 
			
		||||
 
 | 
			
		||||
@@ -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 path’s mtime, i.e. the last data modification
 | 
			
		||||
 * timestamp.
 | 
			
		||||
 */
 | 
			
		||||
ot::status get_file_timestamp(const char* path, timespec& mtime);
 | 
			
		||||
 | 
			
		||||
/** \} */
 | 
			
		||||
 | 
			
		||||
/***********************************************************************************************//**
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +0,0 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
cat > "$1" << EOF
 | 
			
		||||
 | 
			
		||||
# Here’s a file with nothing but comments and newlines.
 | 
			
		||||
 | 
			
		||||
EOF
 | 
			
		||||
@@ -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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user