diff options
| author | Maxime Coste <mawww@kakoune.org> | 2025-02-19 20:58:49 +1100 |
|---|---|---|
| committer | Maxime Coste <mawww@kakoune.org> | 2025-02-19 21:25:39 +1100 |
| commit | eb7d34333b0698e8e1a8af4f2be908ab08cb2089 (patch) | |
| tree | 4b53ba24111ce88901669c3e7651b5ec4edb4f87 /src | |
| parent | 9eda509282ebc881207c2d8278085cb53835641c (diff) | |
Cleanup file.cc/hh dependencies
file.cc/hh should not know about Context, Buffer, etc... It should
be a pretty low level set of helper functions. Move buffer related
functions to buffer_utils and extract busy indicators to callers.
Diffstat (limited to 'src')
| -rw-r--r-- | src/buffer.hh | 2 | ||||
| -rw-r--r-- | src/buffer_manager.cc | 2 | ||||
| -rw-r--r-- | src/buffer_utils.cc | 90 | ||||
| -rw-r--r-- | src/buffer_utils.hh | 14 | ||||
| -rw-r--r-- | src/commands.cc | 20 | ||||
| -rw-r--r-- | src/file.cc | 127 | ||||
| -rw-r--r-- | src/file.hh | 32 | ||||
| -rw-r--r-- | src/main.cc | 5 |
8 files changed, 154 insertions, 138 deletions
diff --git a/src/buffer.hh b/src/buffer.hh index 50423502..cd895c17 100644 --- a/src/buffer.hh +++ b/src/buffer.hh @@ -50,8 +50,6 @@ constexpr auto enum_desc(Meta::Type<ByteOrderMark>) class Buffer; -constexpr timespec InvalidTime = { -1, -1 }; - // A BufferIterator permits to iterate over the characters of a buffer class BufferIterator { diff --git a/src/buffer_manager.cc b/src/buffer_manager.cc index 8d2820b2..c7d8f0aa 100644 --- a/src/buffer_manager.cc +++ b/src/buffer_manager.cc @@ -4,7 +4,7 @@ #include "buffer.hh" #include "client_manager.hh" #include "exception.hh" -#include "file.hh" +#include "buffer_utils.hh" #include "ranges.hh" #include "string.hh" #include "regex.hh" diff --git a/src/buffer_utils.cc b/src/buffer_utils.cc index a4fb639b..a2d2e709 100644 --- a/src/buffer_utils.cc +++ b/src/buffer_utils.cc @@ -166,6 +166,96 @@ void reload_file_buffer(Buffer& buffer) buffer.flags() &= ~Buffer::Flags::New; } +void write_buffer_to_fd(Buffer& buffer, int fd) +{ + auto eolformat = buffer.options()["eolformat"].get<EolFormat>(); + StringView eoldata; + if (eolformat == EolFormat::Crlf) + eoldata = "\r\n"; + else + eoldata = "\n"; + + + BufferedWriter<false> writer{fd}; + if (buffer.options()["BOM"].get<ByteOrderMark>() == ByteOrderMark::Utf8) + writer.write("\xEF\xBB\xBF"); + + for (LineCount i = 0; i < buffer.line_count(); ++i) + { + // end of lines are written according to eolformat but always + // stored as \n + StringView linedata = buffer[i]; + writer.write(linedata.substr(0, linedata.length()-1)); + writer.write(eoldata); + } +} + +void write_buffer_to_file(Buffer& buffer, StringView filename, + WriteMethod method, WriteFlags flags) +{ + auto zfilename = filename.zstr(); + struct stat st; + + bool replace = method == WriteMethod::Replace; + bool force = flags & WriteFlags::Force; + + if ((replace or force) and (::stat(zfilename, &st) != 0 or + (st.st_mode & S_IFMT) != S_IFREG)) + { + force = false; + replace = false; + } + + if (force and ::chmod(zfilename, st.st_mode | S_IWUSR) < 0) + throw runtime_error(format("unable to change file permissions: {}", strerror(errno))); + + char temp_filename[PATH_MAX]; + const int fd = replace ? open_temp_file(filename, temp_filename) + : create_file(zfilename); + if (fd == -1) + { + auto saved_errno = errno; + if (force) + ::chmod(zfilename, st.st_mode); + throw file_access_error(filename, strerror(saved_errno)); + } + + { + auto close_fd = on_scope_end([fd]{ close(fd); }); + write_buffer_to_fd(buffer, fd); + if (flags & WriteFlags::Sync) + ::fsync(fd); + } + + if (replace and geteuid() == 0 and ::chown(temp_filename, st.st_uid, st.st_gid) < 0) + throw runtime_error(format("unable to set replacement file ownership: {}", strerror(errno))); + if (replace and ::chmod(temp_filename, st.st_mode) < 0) + throw runtime_error(format("unable to set replacement file permissions: {}", strerror(errno))); + if (force and not replace and ::chmod(zfilename, st.st_mode) < 0) + throw runtime_error(format("unable to restore file permissions: {}", strerror(errno))); + + if (replace and rename(temp_filename, zfilename) != 0) + { + if (force) + ::chmod(zfilename, st.st_mode); + throw runtime_error("replacing file failed"); + } + + if ((buffer.flags() & Buffer::Flags::File) and + real_path(filename) == real_path(buffer.name())) + buffer.notify_saved(get_fs_status(real_path(filename))); +} + +void write_buffer_to_backup_file(Buffer& buffer) +{ + const int fd = open_temp_file(buffer.name()); + if (fd >= 0) + { + write_buffer_to_fd(buffer, fd); + close(fd); + } +} + Buffer* create_fifo_buffer(String name, int fd, Buffer::Flags flags, AutoScroll scroll) { static ValueId fifo_watcher_id = get_free_value_id(); diff --git a/src/buffer_utils.hh b/src/buffer_utils.hh index bc0dab68..96e89743 100644 --- a/src/buffer_utils.hh +++ b/src/buffer_utils.hh @@ -81,6 +81,20 @@ Buffer* open_or_create_file_buffer(StringView filename, Buffer::Flags flags = Buffer::Flags::None); void reload_file_buffer(Buffer& buffer); +enum class WriteFlags +{ + None = 0, + Force = 0b01, + Sync = 0b10 +}; +constexpr bool with_bit_ops(Meta::Type<WriteFlags>) { return true; } + +void write_buffer_to_file(Buffer& buffer, StringView filename, + WriteMethod method, WriteFlags flags); +void write_buffer_to_fd(Buffer& buffer, int fd); +void write_buffer_to_backup_file(Buffer& buffer); + + void write_to_debug_buffer(StringView str); Vector<String> history_as_strings(const Vector<Buffer::HistoryNode>& history); diff --git a/src/commands.cc b/src/commands.cc index 0f892004..ddc94c3b 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -618,7 +618,11 @@ void do_write_buffer(Context& context, Optional<String> filename, WriteFlags fla auto method = write_method.value_or_compute([&] { return context.options()["writemethod"].get<WriteMethod>(); }); context.hooks().run_hook(Hook::BufWritePre, effective_filename, context); - write_buffer_to_file(context, buffer, effective_filename, method, flags); + BusyIndicator busy_indicator{context, [&](std::chrono::seconds elapsed) { + return DisplayLine{format("waiting while writing buffer '{}' ({}s)", buffer.name(), elapsed.count()), + context.faces()["Information"]}; + }}; + write_buffer_to_file(buffer, effective_filename, method, flags); context.hooks().run_hook(Hook::BufWritePost, effective_filename, context); } @@ -673,7 +677,11 @@ void write_all_buffers(const Context& context, bool sync = false, Optional<Write auto method = write_method.value_or_compute([&] { return context.options()["writemethod"].get<WriteMethod>(); }); auto flags = sync ? WriteFlags::Sync : WriteFlags::None; buffer->run_hook_in_own_context(Hook::BufWritePre, buffer->name(), context.name()); - write_buffer_to_file(context, *buffer, buffer->name(), method, flags); + BusyIndicator busy_indicator{context, [&](std::chrono::seconds elapsed) { + return DisplayLine{format("waiting while writing buffer ({}s)", elapsed.count()), + context.faces()["Information"]}; + }}; + write_buffer_to_file(*buffer, buffer->name(), method, flags); buffer->run_hook_in_own_context(Hook::BufWritePost, buffer->name(), context.name()); } } @@ -1555,7 +1563,13 @@ const CommandDesc echo_cmd = { message.push_back('\n'); if (auto filename = parser.get_switch("to-file")) - write_to_file(context, *filename, message); + { + BusyIndicator busy_indicator{context, [&](std::chrono::seconds elapsed) { + return DisplayLine{format("waiting while writing to '{}' ({}s)", *filename, elapsed.count()), + context.faces()["Information"]}; + }}; + write_to_file(*filename, message); + } else if (auto command = parser.get_switch("to-shell-script")) ShellManager::instance().eval(*command, context, message, ShellManager::Flags::None, shell_context); else if (parser.get_switch("debug")) diff --git a/src/file.cc b/src/file.cc index 2fbf2e78..b434e9dc 100644 --- a/src/file.cc +++ b/src/file.cc @@ -1,14 +1,15 @@ #include "file.hh" #include "assert.hh" -#include "buffer.hh" -#include "client.hh" -#include "exception.hh" #include "flags.hh" #include "event_manager.hh" #include "ranked_match.hh" #include "regex.hh" #include "string.hh" +#include "string_utils.hh" +#include "format.hh" +#include "ranges.hh" +#include "hash_map.hh" #include <limits> #include <cerrno> @@ -40,16 +41,12 @@ namespace Kakoune { -struct file_access_error : runtime_error -{ -public: - file_access_error(StringView filename, - StringView error_desc) - : runtime_error(format("{}: {}", filename, error_desc)) {} +file_access_error::file_access_error(StringView filename, + StringView error_desc) + : runtime_error(format("{}: {}", filename, error_desc)) {} - file_access_error(int fd, StringView error_desc) - : runtime_error(format("fd {}: {}", fd, error_desc)) {} -}; +file_access_error::file_access_error(int fd, StringView error_desc) + : runtime_error(format("fd {}: {}", fd, error_desc)) {} String parse_filename(StringView filename, StringView buf_dir) { @@ -272,64 +269,36 @@ void write(int fd, StringView data) else if (errno == EAGAIN and not atomic and EventManager::has_instance()) EventManager::instance().handle_next_events(EventMode::Urgent, nullptr, std::chrono::nanoseconds{}); else - throw file_access_error(format("fd: {}", fd), strerror(errno)); + throw file_access_error(fd, strerror(errno)); } } template void write<true>(int fd, StringView data); template void write<false>(int fd, StringView data); -static int create_file(const Context& context, const char* filename) +int create_file(const char* filename) { int fd; const int flags = O_CREAT | O_WRONLY | O_TRUNC | (EventManager::has_instance() ? O_NONBLOCK : 0); - using namespace std::chrono; - BusyIndicator busy_indicator{context, [&](seconds elapsed) { - return DisplayLine{format("waiting to open file ({}s)", elapsed.count()), - context.faces()["Information"]}; - }}; while ((fd = open(filename, flags, 0644)) == -1) { if (errno == ENXIO and EventManager::has_instance()) // trying to open a FIFO with no readers yet - EventManager::instance().handle_next_events(EventMode::Urgent, nullptr, nanoseconds{1'000'000}); + EventManager::instance().handle_next_events(EventMode::Urgent, nullptr, + std::chrono::nanoseconds{1'000'000}); else return -1; } return fd; } -void write_to_file(const Context& context, StringView filename, StringView data) +void write_to_file(StringView filename, StringView data) { - int fd = create_file(context, filename.zstr()); + int fd = create_file(filename.zstr()); if (fd == -1) throw file_access_error(filename, strerror(errno)); auto close_fd = on_scope_end([fd]{ close(fd); }); write(fd, data); } -void write_buffer_to_fd(Buffer& buffer, int fd) -{ - auto eolformat = buffer.options()["eolformat"].get<EolFormat>(); - StringView eoldata; - if (eolformat == EolFormat::Crlf) - eoldata = "\r\n"; - else - eoldata = "\n"; - - - BufferedWriter<false> writer{fd}; - if (buffer.options()["BOM"].get<ByteOrderMark>() == ByteOrderMark::Utf8) - writer.write("\xEF\xBB\xBF"); - - for (LineCount i = 0; i < buffer.line_count(); ++i) - { - // end of lines are written according to eolformat but always - // stored as \n - StringView linedata = buffer[i]; - writer.write(linedata.substr(0, linedata.length()-1)); - writer.write(eoldata); - } -} - int open_temp_file(StringView filename, char (&buffer)[PATH_MAX]) { String path = real_path(filename); @@ -349,72 +318,6 @@ int open_temp_file(StringView filename) return open_temp_file(filename, buffer); } -void write_buffer_to_file(const Context& context, Buffer& buffer, StringView filename, - WriteMethod method, WriteFlags flags) -{ - auto zfilename = filename.zstr(); - struct stat st; - - bool replace = method == WriteMethod::Replace; - bool force = flags & WriteFlags::Force; - - if ((replace or force) and (::stat(zfilename, &st) != 0 or - (st.st_mode & S_IFMT) != S_IFREG)) - { - force = false; - replace = false; - } - - if (force and ::chmod(zfilename, st.st_mode | S_IWUSR) < 0) - throw runtime_error(format("unable to change file permissions: {}", strerror(errno))); - - char temp_filename[PATH_MAX]; - const int fd = replace ? open_temp_file(filename, temp_filename) - : create_file(context, zfilename); - if (fd == -1) - { - auto saved_errno = errno; - if (force) - ::chmod(zfilename, st.st_mode); - throw file_access_error(filename, strerror(saved_errno)); - } - - { - auto close_fd = on_scope_end([fd]{ close(fd); }); - write_buffer_to_fd(buffer, fd); - if (flags & WriteFlags::Sync) - ::fsync(fd); - } - - if (replace and geteuid() == 0 and ::chown(temp_filename, st.st_uid, st.st_gid) < 0) - throw runtime_error(format("unable to set replacement file ownership: {}", strerror(errno))); - if (replace and ::chmod(temp_filename, st.st_mode) < 0) - throw runtime_error(format("unable to set replacement file permissions: {}", strerror(errno))); - if (force and not replace and ::chmod(zfilename, st.st_mode) < 0) - throw runtime_error(format("unable to restore file permissions: {}", strerror(errno))); - - if (replace and rename(temp_filename, zfilename) != 0) - { - if (force) - ::chmod(zfilename, st.st_mode); - throw runtime_error("replacing file failed"); - } - - if ((buffer.flags() & Buffer::Flags::File) and - real_path(filename) == real_path(buffer.name())) - buffer.notify_saved(get_fs_status(real_path(filename))); -} - -void write_buffer_to_backup_file(Buffer& buffer) -{ - const int fd = open_temp_file(buffer.name()); - if (fd >= 0) - { - write_buffer_to_fd(buffer, fd); - close(fd); - } -} - String find_file(StringView filename, StringView buf_dir, ConstArrayView<String> paths) { struct stat buf; diff --git a/src/file.hh b/src/file.hh index a0b55e56..3d0d8603 100644 --- a/src/file.hh +++ b/src/file.hh @@ -1,12 +1,13 @@ #ifndef file_hh_INCLUDED #define file_hh_INCLUDED +#include "array.hh" #include "array_view.hh" #include "enum.hh" +#include "exception.hh" #include "meta.hh" #include "string.hh" #include "units.hh" -#include "array.hh" #include "vector.hh" #include <sys/types.h> @@ -16,10 +17,16 @@ namespace Kakoune { -class Buffer; class String; class Regex; +struct file_access_error : runtime_error +{ + file_access_error(StringView filename, StringView error_desc); + file_access_error(int fd, StringView error_desc); +}; + + using CandidateList = Vector<String, MemoryDomain::Completion>; // parse ~/ and %/ in filename and returns the translated filename @@ -42,8 +49,10 @@ String read_fd(int fd, bool text = false); String read_file(StringView filename, bool text = false); template<bool force_blocking = false> void write(int fd, StringView data); -class Context; -void write_to_file(const Context&, StringView filename, StringView data); +void write_to_file(StringView filename, StringView data); +int create_file(const char* filename); +int open_temp_file(StringView filename); +int open_temp_file(StringView filename, char (&buffer)[PATH_MAX]); struct MappedFile { @@ -69,19 +78,6 @@ constexpr auto enum_desc(Meta::Type<WriteMethod>) }); } -enum class WriteFlags -{ - None = 0, - Force = 0b01, - Sync = 0b10 -}; -constexpr bool with_bit_ops(Meta::Type<WriteFlags>) { return true; } - -void write_buffer_to_file(const Context& context, Buffer& buffer, StringView filename, - WriteMethod method, WriteFlags flags); -void write_buffer_to_fd(Buffer& buffer, int fd); -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); @@ -90,6 +86,8 @@ Vector<String> list_files(StringView directory); void make_directory(StringView dir, mode_t mode); +constexpr timespec InvalidTime = { -1, -1 }; + struct FsStatus { timespec timestamp; diff --git a/src/main.cc b/src/main.cc index ac9af4ca..0d70bd6a 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1007,15 +1007,14 @@ int run_filter(StringView keystr, ConstArrayView<StringView> files, bool quiet, } }; - Context empty_context{Context::EmptyContextFlag{}}; for (auto& file : files) { Buffer* buffer = open_file_buffer(file, Buffer::Flags::NoHooks); if (not suffix_backup.empty()) - write_buffer_to_file(empty_context, *buffer, buffer->name() + suffix_backup, + write_buffer_to_file(*buffer, buffer->name() + suffix_backup, WriteMethod::Overwrite, WriteFlags::None); apply_to_buffer(*buffer); - write_buffer_to_file(empty_context, *buffer, buffer->name(), + write_buffer_to_file(*buffer, buffer->name(), WriteMethod::Overwrite, WriteFlags::None); buffer_manager.delete_buffer(*buffer); } |
