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.
*write[!]* [-sync] [-method <writemethod>] [<filename>]::
*write[!]* [-force] [-sync] [-method <writemethod>] [<filename>]::
*alias* w +
write buffer to <filename> or use its 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.
*-force*:::
Equivalent to `!`, allow overwiting existing files if `<filename>`
is given and set permissions temporarily if necessary.
*-sync*:::
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" } },
{ "method", { true, "explicit writemethod (replace|overwrite)" } },
{ "force", { false, "Allow overwriting existing file with explicit filename" } },
},
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()))
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>(); });
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,
parser.positional_count() > 0 ? parser[0] : Optional<String>{},
(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));
}

View File

@ -247,6 +247,13 @@ bool file_exists(StringView filename)
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)
{
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);
bool file_exists(StringView filename);
bool regular_file_exists(StringView filename);
Vector<String> list_files(StringView directory);

View File

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