diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2014-11-24 02:51:31 +0100 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2014-11-24 05:07:06 +0100 |
commit | d503c3b07c2157658f699294c44ad5be244727a5 (patch) | |
tree | f25b9927f5bdcfca98a82f3bc917548cbec5962b /src/ring.cpp | |
parent | 355cae277c29104b982f4dc7aa3e05fc06945325 (diff) | |
download | sciteco-d503c3b07c2157658f699294c44ad5be244727a5.tar.gz |
factored out file loading and saving into the View specialisation IOView
this will allow us to use the same algorithms for loading and saving
Q-Registers (from/to file).
* Saving with EW when a Q-Reg is edited has been fixed (was broken earlier)
* SciTECO save point files are now named .teco-X-BASENAME
When using IOView for Q-Regs, there will be no way to sensible count
the save points. Each write of a Q-Reg may be to another file.
Therefore, we number save-points globally.
If the sequence of writes has to be reconstructed manually,
one can still look at the save point files' modification dates
* give more informative error messages when saving a file fails
Diffstat (limited to 'src/ring.cpp')
-rw-r--r-- | src/ring.cpp | 367 |
1 files changed, 25 insertions, 342 deletions
diff --git a/src/ring.cpp b/src/ring.cpp index fdd8206..e7f7d75 100644 --- a/src/ring.cpp +++ b/src/ring.cpp @@ -19,21 +19,16 @@ #include "config.h" #endif -#include <limits.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <stdio.h> #include <bsd/sys/queue.h> #include <glib.h> #include <glib/gprintf.h> -#include <glib/gstdio.h> #include <Scintilla.h> #include "sciteco.h" #include "interface.h" +#include "ioview.h" #include "undo.h" #include "parser.h" #include "expressions.h" @@ -42,14 +37,6 @@ #include "error.h" #include "ring.h" -#ifdef HAVE_WINDOWS_H -/* here it shouldn't cause conflicts with other headers */ -#include <windows.h> - -/* still need to clean up */ -#undef interface -#endif - namespace SciTECO { namespace States { @@ -57,44 +44,6 @@ namespace States { StateSaveFile savefile; } -#ifdef G_OS_WIN32 - -typedef DWORD FileAttributes; -/* INVALID_FILE_ATTRIBUTES already defined */ - -static inline FileAttributes -get_file_attributes(const gchar *filename) -{ - return GetFileAttributes((LPCTSTR)filename); -} - -static inline void -set_file_attributes(const gchar *filename, FileAttributes attrs) -{ - SetFileAttributes((LPCTSTR)filename, attrs); -} - -#else - -typedef int FileAttributes; -#define INVALID_FILE_ATTRIBUTES (-1) - -static inline FileAttributes -get_file_attributes(const gchar *filename) -{ - struct stat buf; - - return g_stat(filename, &buf) ? INVALID_FILE_ATTRIBUTES : buf.st_mode; -} - -static inline void -set_file_attributes(const gchar *filename, FileAttributes attrs) -{ - g_chmod(filename, attrs); -} - -#endif /* !G_OS_WIN32 */ - void Buffer::UndoTokenClose::run(void) { @@ -103,57 +52,30 @@ Buffer::UndoTokenClose::run(void) delete buffer; } -/* - * The following simple implementation of file reading is actually the - * most efficient and useful in the common case of editing small files, - * since - * a) it works with minimal number of syscalls and - * b) small files cause little temporary memory overhead. - * Reading large files however could be very inefficient since the file - * must first be read into memory and then copied in-memory. Also it could - * result in thrashing. - * Alternatively we could iteratively read into a smaller buffer trading - * in speed against (temporary) memory consumption. - * The best way to do it could be memory mapping the file as we could - * let Scintilla copy from the file's virtual memory directly. - * Unfortunately since every page of the mapped file is - * only touched once by Scintilla TLB caching is useless and the TLB is - * effectively thrashed with entries of the mapped file. - * This results in the doubling of page faults and weighs out the other - * advantages of memory mapping (has been benchmarked). - * - * So in the future, the following approach could be implemented: - * 1.) On Unix/Posix, mmap() one page at a time, hopefully preventing - * TLB thrashing. - * 2.) On other platforms read into and copy from a statically sized buffer - * (perhaps page-sized) - */ void -Buffer::load(const gchar *filename) +Buffer::save(const gchar *filename) { - gchar *contents; - gsize size; - - GError *gerror = NULL; + if (!filename && !Buffer::filename) + throw Error("File name expected"); - if (!g_file_get_contents(filename, &contents, &size, &gerror)) - throw GlibError(gerror); + IOView::save(filename ? : Buffer::filename); - ssm(SCI_BEGINUNDOACTION); - ssm(SCI_CLEARALL); - ssm(SCI_APPENDTEXT, size, (sptr_t)contents); - ssm(SCI_ENDUNDOACTION); - - g_free(contents); - - /* NOTE: currently buffer cannot be dirty */ -#if 0 + /* + * Undirtify + * NOTE: info update is performed by set_filename() + */ interface.undo_info_update(this); - undo.push_var(dirty); - dirty = false; -#endif + undo.push_var(dirty) = false; - set_filename(filename); + /* + * FIXME: necessary also if the filename was not specified but the file + * is (was) new, in order to canonicalize the filename. + * May be circumvented by cananonicalizing without requiring the file + * name to exist (like readlink -f) + * NOTE: undo_info_update is already called above + */ + undo.push_str(Buffer::filename); + set_filename(filename ? : Buffer::filename); } void @@ -296,192 +218,6 @@ Ring::edit(const gchar *filename) } } -#if 0 - -/* - * TODO: on UNIX it may be better to open() the current file, unlink() it - * and keep the file descriptor in the UndoToken. - * When the operation is undone, the file descriptor's contents are written to - * the file (which should be efficient enough because it is written to the same - * filesystem). This way we could avoid messing around with save point files. - */ - -#else - -class UndoTokenRestoreSavePoint : public UndoToken { - gchar *savepoint; - Buffer *buffer; - -public: -#ifdef G_OS_WIN32 - FileAttributes orig_attrs; -#endif - - UndoTokenRestoreSavePoint(gchar *_savepoint, Buffer *_buffer) - : savepoint(_savepoint), buffer(_buffer) {} - ~UndoTokenRestoreSavePoint() - { - if (savepoint) - g_unlink(savepoint); - g_free(savepoint); - buffer->savepoint_id--; - } - - void - run(void) - { - if (!g_rename(savepoint, buffer->filename)) { - g_free(savepoint); - savepoint = NULL; -#ifdef G_OS_WIN32 - if (orig_attrs != INVALID_FILE_ATTRIBUTES) - set_file_attributes(buffer->filename, - orig_attrs); -#endif - } else { - interface.msg(InterfaceCurrent::MSG_WARNING, - "Unable to restore save point file \"%s\"", - savepoint); - } - } -}; - -static inline FileAttributes -make_savepoint(Buffer *buffer) -{ - gchar *dirname, *basename, *savepoint; - gchar savepoint_basename[FILENAME_MAX]; - - FileAttributes attributes = get_file_attributes(buffer->filename); - - basename = g_path_get_basename(buffer->filename); - g_snprintf(savepoint_basename, sizeof(savepoint_basename), - ".teco-%s-%d", basename, buffer->savepoint_id); - g_free(basename); - dirname = g_path_get_dirname(buffer->filename); - savepoint = g_build_filename(dirname, savepoint_basename, NIL); - g_free(dirname); - - if (!g_rename(buffer->filename, savepoint)) { - UndoTokenRestoreSavePoint *token; - - buffer->savepoint_id++; - token = new UndoTokenRestoreSavePoint(savepoint, buffer); -#ifdef G_OS_WIN32 - token->orig_attrs = attributes; - if (attributes != INVALID_FILE_ATTRIBUTES) - set_file_attributes(savepoint, - attributes | FILE_ATTRIBUTE_HIDDEN); -#endif - undo.push(token); - } else { - interface.msg(InterfaceCurrent::MSG_WARNING, - "Unable to create save point file \"%s\"", - savepoint); - g_free(savepoint); - } - - return attributes; -} - -#endif /* !G_OS_UNIX */ - -bool -Ring::save(const gchar *filename) -{ - const void *buffer; - sptr_t gap; - size_t size; - FILE *file; - -#ifdef G_OS_UNIX - struct stat file_stat; - file_stat.st_uid = -1; - file_stat.st_gid = -1; -#endif - FileAttributes attributes = INVALID_FILE_ATTRIBUTES; - - if (!current) - return false; - - if (!filename && !current->filename) - return false; - - /* - * Undirtify - * NOTE: info update is performed by current->set_filename() - */ - interface.undo_info_update(current); - undo.push_var(current->dirty) = false; - - /* - * FIXME: necessary also if the filename was not specified but the file - * is (was) new, in order to canonicalize the filename. - * May be circumvented by cananonicalizing without requiring the file - * name to exist (like readlink -f) - * NOTE: undo_info_update is already called above - */ - undo.push_str(current->filename); - current->set_filename(filename ? : current->filename); - - if (undo.enabled) { - if (g_file_test(current->filename, G_FILE_TEST_IS_REGULAR)) { -#ifdef G_OS_UNIX - g_stat(current->filename, &file_stat); -#endif - attributes = make_savepoint(current); - } else { - undo.push(new UndoTokenRemoveFile(current->filename)); - } - } - - /* leaves mode intact if file exists */ - file = g_fopen(current->filename, "w"); - if (!file) - return false; - - /* write part of buffer before gap */ - gap = interface.ssm(SCI_GETGAPPOSITION); - if (gap > 0) { - buffer = (const void *)interface.ssm(SCI_GETRANGEPOINTER, - 0, gap); - if (!fwrite(buffer, (size_t)gap, 1, file)) { - fclose(file); - return false; - } - } - - /* write part of buffer after gap */ - size = interface.ssm(SCI_GETLENGTH) - gap; - if (size > 0) { - buffer = (const void *)interface.ssm(SCI_GETRANGEPOINTER, - gap, size); - if (!fwrite(buffer, size, 1, file)) { - fclose(file); - return false; - } - } - - /* if file existed but has been renamed, restore attributes */ - if (attributes != INVALID_FILE_ATTRIBUTES) - set_file_attributes(current->filename, attributes); -#ifdef G_OS_UNIX - /* - * only a good try to inherit owner since process user must have - * CHOWN capability traditionally reserved to root only. - * That's why we don't handle the return value and are spammed - * with unused-result warnings by GCC. There is NO sane way to avoid - * this warning except, adding -Wno-unused-result which disabled all - * such warnings. - */ - fchown(fileno(file), file_stat.st_uid, file_stat.st_gid); -#endif - - fclose(file); - - return true; -} - void Ring::close(Buffer *buffer) { @@ -524,62 +260,6 @@ Ring::~Ring() } /* - * Auxiliary functions - */ -#ifdef G_OS_UNIX - -gchar * -get_absolute_path(const gchar *path) -{ - gchar buf[PATH_MAX]; - gchar *resolved; - - if (!path) - return NULL; - - if (!realpath(path, buf)) { - if (g_path_is_absolute(path)) { - resolved = g_strdup(path); - } else { - gchar *cwd = g_get_current_dir(); - resolved = g_build_filename(cwd, path, NIL); - g_free(cwd); - } - } else { - resolved = g_strdup(buf); - } - - return resolved; -} - -#elif defined(G_OS_WIN32) - -gchar * -get_absolute_path(const gchar *path) -{ - TCHAR buf[MAX_PATH]; - gchar *resolved = NULL; - - if (path && GetFullPathName(path, sizeof(buf), buf, NULL)) - resolved = g_strdup(buf); - - return resolved; -} - -#else - -/* - * FIXME: I doubt that works on any platform... - */ -gchar * -get_absolute_path(const gchar *path) -{ - return path ? g_file_read_link(path, NULL) : NULL; -} - -#endif /* !G_OS_UNIX && !G_OS_WIN32 */ - -/* * Command states */ @@ -707,7 +387,7 @@ StateEditFile::done(const gchar *str) * save point files. * It does not merely overwrite existing files when saving * but moves them to save point files instead. - * Save point files are called \(lq.teco-<filename>-<n>\(rq + * Save point files are called \(lq.teco-\fIn\fP-\fIfilename\fP\(rq, * where <filename> is the name of the saved file and <n> is * a number that is increased with every save operation. * Save point files are always created in the same directory @@ -730,8 +410,11 @@ StateSaveFile::done(const gchar *str) { BEGIN_EXEC(&States::start); - if (!ring.save(*str ? str : NULL)) - throw Error("Unable to save file"); + if (QRegisters::current) + throw Error("Cannot save Q-Register"); +// QRegisters::current->save(str); + else + ring.current->save(*str ? str : NULL); return &States::start; } |