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.
This commit is contained in:
Frank LENORMAND 2017-04-19 18:47:07 +03:00
parent 30e6387071
commit 51ab59cd36
5 changed files with 45 additions and 8 deletions

View File

@ -676,8 +676,10 @@ command `q!` has to be used).
* `e[dit][!] <filename> [<line> [<column>]]`: open buffer on file, go to given
line and column. If file is already opened, just switch to this file.
use edit! to force reloading.
* `w[rite] [<filename>]`: write buffer to <filename> or use it's name if
filename is not given.
* `w[rite][!] [<filename>]`: write buffer to <filename> or use it's name if
filename is not given. If the file is write-protected, its
permissions are temporarily changed to allow saving the buffer and
restored afterwards when the write! command is used.
* `w[rite]a[ll]`: write all buffers that are associated to a file.
* `q[uit][!]`: exit Kakoune, use quit! to force quitting even if there is some
unsaved buffers remaining.

View File

@ -24,8 +24,11 @@ command *q!* has to be used).
open buffer on file, go to given line and column. If file is already
opened, just switch to this file. Use edit! to force reloading
*w[rite]* [<filename>]::
write buffer to <filename> or use it's name if filename is not given
*w[rite][!]* [<filename>]::
write buffer to <filename> or use it's name if filename is not
given. If the file is write-protected, its permissions are temporarily
changed to allow saving the buffer and restored afterwards when
the write! command is used.
*w[rite]a[ll]*::
write all buffers that are associated to a file

View File

@ -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);

View File

@ -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));

View File

@ -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);