Prevent overwriting existing file in :write <explicit filename>

Add a -force (equivalent to w!) switch that enables overwriting.
This commit is contained in:
Maxime Coste 2021-07-20 22:06:57 +10:00
parent 4d99434ddd
commit 40e3614cf4
5 changed files with 24 additions and 6 deletions

View File

@ -58,13 +58,17 @@ of the file onto the filesystem
Otherwise, does nothing. Otherwise, does nothing.
*write[!]* [-sync] [-method <writemethod>] [<filename>]:: *write[!]* [-force] [-sync] [-method <writemethod>] [<filename>]::
*alias* w + *alias* w +
write buffer to <filename> or use its name if filename is not write buffer to <filename> or use its name if filename is not
given. If the file is write-protected, its permissions are temporarily given. If the file is write-protected, its permissions are temporarily
changed to allow saving the buffer and restored afterwards when changed to allow saving the buffer and restored afterwards when
the write! command is used. the write! command is used.
*-force*:::
Equivalent to `!`, allow overwiting existing files if `<filename>`
is given and set permissions temporarily if necessary.
*-sync*::: *-sync*:::
Synchronise the filesystem after the write Synchronise the filesystem after the write

View File

@ -460,6 +460,7 @@ const ParameterDesc write_params{
{ {
{ "sync", { false, "force the synchronization of the file onto the filesystem" } }, { "sync", { false, "force the synchronization of the file onto the filesystem" } },
{ "method", { true, "explicit writemethod (replace|overwrite)" } }, { "method", { true, "explicit writemethod (replace|overwrite)" } },
{ "force", { false, "Allow overwriting existing file with explicit filename" } },
}, },
ParameterDesc::Flags::SwitchesOnlyAtStart, 0, 1 ParameterDesc::Flags::SwitchesOnlyAtStart, 0, 1
}; };
@ -488,7 +489,12 @@ void do_write_buffer(Context& context, Optional<String> filename, WriteFlags fla
(not filename or real_path(*filename) == buffer.name())) (not filename or real_path(*filename) == buffer.name()))
throw runtime_error("cannot overwrite the buffer when in readonly mode"); throw runtime_error("cannot overwrite the buffer when in readonly mode");
auto effective_filename = not filename ? buffer.name() : parse_filename(*filename); auto effective_filename = filename ? parse_filename(*filename) : buffer.name();
if (filename and not (flags & WriteFlags::Force) and
real_path(effective_filename) != buffer.name() and
regular_file_exists(effective_filename))
throw runtime_error("cannot overwrite existing file without -force");
auto method = write_method.value_or_compute([&] { return context.options()["writemethod"].get<WriteMethod>(); }); auto method = write_method.value_or_compute([&] { return context.options()["writemethod"].get<WriteMethod>(); });
context.hooks().run_hook(Hook::BufWritePre, effective_filename, context); context.hooks().run_hook(Hook::BufWritePre, effective_filename, context);
@ -502,7 +508,7 @@ void write_buffer(const ParametersParser& parser, Context& context, const ShellC
return do_write_buffer(context, return do_write_buffer(context,
parser.positional_count() > 0 ? parser[0] : Optional<String>{}, parser.positional_count() > 0 ? parser[0] : Optional<String>{},
(parser.get_switch("sync") ? WriteFlags::Sync : WriteFlags::None) | (parser.get_switch("sync") ? WriteFlags::Sync : WriteFlags::None) |
(force ? WriteFlags::Force : WriteFlags::None), (parser.get_switch("force") or force ? WriteFlags::Force : WriteFlags::None),
parser.get_switch("method").map(parse_write_method)); parser.get_switch("method").map(parse_write_method));
} }

View File

@ -247,6 +247,13 @@ bool file_exists(StringView filename)
return stat(filename.zstr(), &st) == 0; return stat(filename.zstr(), &st) == 0;
} }
bool regular_file_exists(StringView filename)
{
struct stat st;
return stat(filename.zstr(), &st) == 0 and
(st.st_mode & S_IFMT) == S_IFREG;
}
void write(int fd, StringView data) void write(int fd, StringView data)
{ {
const char* ptr = data.data(); const char* ptr = data.data();

View File

@ -81,6 +81,7 @@ void write_buffer_to_backup_file(Buffer& buffer);
String find_file(StringView filename, StringView buf_dir, ConstArrayView<String> paths); String find_file(StringView filename, StringView buf_dir, ConstArrayView<String> paths);
bool file_exists(StringView filename); bool file_exists(StringView filename);
bool regular_file_exists(StringView filename);
Vector<String> list_files(StringView directory); Vector<String> list_files(StringView directory);

View File

@ -16,7 +16,7 @@ main() {
try %{ source rc } try %{ source rc }
hook global RuntimeError .+ %{ hook global RuntimeError .+ %{
echo -debug -- error: %val{hook_param} echo -debug -- error: %val{hook_param}
eval -buffer *debug* write debug eval -buffer *debug* write -force debug
quit! quit!
} }
try %{ exec -with-maps -with-hooks "%sh{cat cmd}" } try %{ exec -with-maps -with-hooks "%sh{cat cmd}" }
@ -151,14 +151,14 @@ show_diff() {
finished_commands() { finished_commands() {
printf %s 'eval -client client0 %{ printf %s 'eval -client client0 %{
eval -buffer *debug* write debug eval -buffer *debug* write -force debug
nop %sh{ nop %sh{
' '
for env_var in $env_vars; do for env_var in $env_vars; do
printf 'printf %%s\\\\n "$%s" >%s\n' "$env_var" "$env_var" printf 'printf %%s\\\\n "$%s" >%s\n' "$env_var" "$env_var"
done done
printf %s ' } printf %s ' }
write out write -force out
quit! quit!
} }
' '