summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFrank LENORMAND <lenormf@gmail.com>2017-04-19 18:47:07 +0300
committerFrank LENORMAND <lenormf@gmail.com>2017-04-20 17:13:42 +0300
commit51ab59cd3624476832272112ae5814fecef12f69 (patch)
tree9d54c258cd188a16ebabecdb5985fccf08262230 /src
parent30e6387071b6aee2239d155822091dc834090b7f (diff)
src: Implement a `write!` command
This commit allows "forced" writes to a write-protected file, by attempting to temporarily grant the current user write permissions on it. After the buffer has been written, the previous permissions are restored if the file existed, or set to 0644 otherwise.
Diffstat (limited to 'src')
-rw-r--r--src/commands.cc16
-rw-r--r--src/file.cc22
-rw-r--r--src/file.hh2
3 files changed, 36 insertions, 4 deletions
diff --git a/src/commands.cc b/src/commands.cc
index 6143179d..355f560d 100644
--- a/src/commands.cc
+++ b/src/commands.cc
@@ -309,6 +309,7 @@ const CommandDesc force_edit_cmd = {
edit<true>
};
+template<bool force = false>
void write_buffer(const ParametersParser& parser, Context& context, const ShellContext&)
{
Buffer& buffer = context.buffer();
@@ -326,7 +327,7 @@ void write_buffer(const ParametersParser& parser, Context& context, const ShellC
buffer.name() : parse_filename(parser[0]);
context.hooks().run_hook("BufWritePre", filename, context);
- write_buffer_to_file(buffer, filename);
+ write_buffer_to_file(buffer, filename, force);
context.hooks().run_hook("BufWritePost", filename, context);
}
@@ -342,6 +343,18 @@ const CommandDesc write_cmd = {
write_buffer,
};
+const CommandDesc force_write_cmd = {
+ "write!",
+ "w!",
+ "write [filename]: write the current buffer to its file "
+ "or to [filename] if specified, even when the file is write protected",
+ single_optional_param,
+ CommandFlags::None,
+ CommandHelper{},
+ filename_completer,
+ write_buffer<true>,
+};
+
void write_all_buffers(Context& context)
{
// Copy buffer list because hooks might be creating/deleting buffers
@@ -2102,6 +2115,7 @@ void register_commands()
register_command(edit_cmd);
register_command(force_edit_cmd);
register_command(write_cmd);
+ register_command(force_write_cmd);
register_command(write_all_cmd);
register_command(write_all_quit_cmd);
register_command(kill_cmd);
diff --git a/src/file.cc b/src/file.cc
index bf592689..49fa00e5 100644
--- a/src/file.cc
+++ b/src/file.cc
@@ -278,9 +278,27 @@ void write_buffer_to_fd(Buffer& buffer, int fd)
}
}
-void write_buffer_to_file(Buffer& buffer, StringView filename)
+void write_buffer_to_file(Buffer& buffer, StringView filename, bool force)
{
- int fd = open(filename.zstr(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
+ struct stat st;
+ auto zfilename = filename.zstr();
+
+ if (force)
+ {
+ if (::stat(zfilename, &st) == 0)
+ {
+ if (::chmod(zfilename, st.st_mode | S_IWUSR) < 0)
+ throw runtime_error("couldn't change file permissions");
+ }
+ else
+ force = false;
+ }
+ auto restore_mode = on_scope_end([&]{
+ if (force and ::chmod(zfilename, st.st_mode) < 0)
+ throw runtime_error("couldn't restore file permissions");
+ });
+
+ int fd = open(zfilename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1)
throw file_access_error(filename, strerror(errno));
diff --git a/src/file.hh b/src/file.hh
index 7cb5f88c..23fbe0a2 100644
--- a/src/file.hh
+++ b/src/file.hh
@@ -49,7 +49,7 @@ struct MappedFile
struct stat st {};
};
-void write_buffer_to_file(Buffer& buffer, StringView filename);
+void write_buffer_to_file(Buffer& buffer, StringView filename, bool force = false);
void write_buffer_to_fd(Buffer& buffer, int fd);
void write_buffer_to_backup_file(Buffer& buffer);