diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cmdline.cpp | 26 | ||||
-rw-r--r-- | src/glob.cpp | 36 | ||||
-rw-r--r-- | src/glob.h | 17 | ||||
-rw-r--r-- | src/ioview.cpp | 27 | ||||
-rw-r--r-- | src/ioview.h | 2 | ||||
-rw-r--r-- | src/parser.cpp | 34 | ||||
-rw-r--r-- | src/parser.h | 8 | ||||
-rw-r--r-- | src/qregisters.cpp | 14 | ||||
-rw-r--r-- | src/qregisters.h | 6 | ||||
-rw-r--r-- | src/ring.cpp | 22 | ||||
-rw-r--r-- | src/ring.h | 4 |
11 files changed, 130 insertions, 66 deletions
diff --git a/src/cmdline.cpp b/src/cmdline.cpp index 82c8434..94e68ba 100644 --- a/src/cmdline.cpp +++ b/src/cmdline.cpp @@ -592,24 +592,24 @@ static gchar * filename_complete(const gchar *filename, gchar completed, GFileTest file_test) { + gchar *filename_expanded; + gsize filename_len; + gchar *dirname, *basename, dir_sep; gsize dirname_len; - gchar *dirname, dir_sep; - const gchar *basename, *cur_basename; + const gchar *cur_basename; GDir *dir; GSList *files = NULL; guint files_len = 0; gchar *insert = NULL; - gsize filename_len; gsize prefix_len = 0; - if (!filename) - filename = ""; - filename_len = strlen(filename); - if (is_glob_pattern(filename)) return NULL; + filename_expanded = expand_path(filename); + filename_len = strlen(filename_expanded); + /* * Derive base and directory names. * We do not use g_path_get_basename() or g_path_get_dirname() @@ -617,13 +617,14 @@ filename_complete(const gchar *filename, gchar completed, * in order to construct paths of entries in dirname * that are suitable for auto completion. */ - dirname_len = file_get_dirname_len(filename); - dirname = g_strndup(filename, dirname_len); - basename = filename + dirname_len; + dirname_len = file_get_dirname_len(filename_expanded); + dirname = g_strndup(filename_expanded, dirname_len); + basename = filename_expanded + dirname_len; dir = g_dir_open(dirname_len ? dirname : ".", 0, NULL); if (!dir) { g_free(dirname); + g_free(filename_expanded); return NULL; } @@ -632,7 +633,7 @@ filename_complete(const gchar *filename, gchar completed, * directory separators are allowed in directory * names passed to glib. * To imitate glib's behaviour, we use - * the last valid directory separator in `filename` + * the last valid directory separator in `filename_expanded` * to generate new separators. * This also allows forward-slash auto-completion * on Windows. @@ -684,8 +685,9 @@ filename_complete(const gchar *filename, gchar completed, if (prefix_len > 0) insert = g_strndup((gchar *)files->data + filename_len, prefix_len); - g_free(dirname); g_dir_close(dir); + g_free(dirname); + g_free(filename_expanded); if (!insert && files_len > 1) { files = g_slist_sort(files, (GCompareFunc)g_strcmp0); diff --git a/src/glob.cpp b/src/glob.cpp index 66d6736..4c1fa1f 100644 --- a/src/glob.cpp +++ b/src/glob.cpp @@ -238,22 +238,22 @@ Globber::~Globber() * have to edit that register anyway. */ State * -StateGlob_pattern::done(const gchar *str) +StateGlob_pattern::got_file(const gchar *filename) { BEGIN_EXEC(&States::glob_filename); - if (*str) { + if (*filename) { QRegister *glob_reg = QRegisters::globals["_"]; glob_reg->undo_set_string(); - glob_reg->set_string(str); + glob_reg->set_string(filename); } return &States::glob_filename; } State * -StateGlob_filename::done(const gchar *str) +StateGlob_filename::got_file(const gchar *filename) { BEGIN_EXEC(&States::start); @@ -287,16 +287,16 @@ StateGlob_filename::done(const gchar *str) pattern_str = glob_reg->get_string(); - if (*str) { + if (*filename) { /* * Match pattern against provided file name */ - if (g_pattern_match_simple(pattern_str, str) && - (!teco_test_mode || g_file_test(str, file_flags))) { + if (g_pattern_match_simple(pattern_str, filename) && + (!teco_test_mode || g_file_test(filename, file_flags))) { if (!colon_modified) { interface.ssm(SCI_BEGINUNDOACTION); - interface.ssm(SCI_ADDTEXT, strlen(str), - (sptr_t)str); + interface.ssm(SCI_ADDTEXT, strlen(filename), + (sptr_t)filename); interface.ssm(SCI_ADDTEXT, 1, (sptr_t)"\n"); interface.ssm(SCI_SCROLLCARET); interface.ssm(SCI_ENDUNDOACTION); @@ -310,11 +310,11 @@ StateGlob_filename::done(const gchar *str) * returning SUCCESS if at least one file matches */ Globber globber(pattern_str, file_flags); - gchar *filename = globber.next(); + gchar *globbed_filename = globber.next(); - matching = filename != NULL; + matching = globbed_filename != NULL; - g_free(filename); + g_free(globbed_filename); } else { /* * Match pattern against directory contents (globbing), @@ -322,14 +322,14 @@ StateGlob_filename::done(const gchar *str) */ Globber globber(pattern_str, file_flags); - gchar *filename; + gchar *globbed_filename; interface.ssm(SCI_BEGINUNDOACTION); - while ((filename = globber.next())) { - size_t len = strlen(filename); + while ((globbed_filename = globber.next())) { + size_t len = strlen(globbed_filename); /* overwrite trailing null */ - filename[len] = '\n'; + globbed_filename[len] = '\n'; /* * FIXME: Once we're 8-bit clean, we should @@ -337,9 +337,9 @@ StateGlob_filename::done(const gchar *str) * (there may be linebreaks in filename). */ interface.ssm(SCI_ADDTEXT, len+1, - (sptr_t)filename); + (sptr_t)globbed_filename); - g_free(filename); + g_free(globbed_filename); matching = true; } @@ -18,8 +18,6 @@ #ifndef __GLOB_H #define __GLOB_H -#include <string.h> - #include <glib.h> #include <glib/gstdio.h> @@ -34,7 +32,16 @@ namespace SciTECO { static inline bool is_glob_pattern(const gchar *str) { - return strchr(str, '*') || strchr(str, '?'); + if (!str) + return false; + + while (*str) { + if (*str == '*' || *str == '?') + return true; + str++; + } + + return false; } class Globber { @@ -60,12 +67,12 @@ public: StateGlob_pattern() : StateExpectFile(true, false) {} private: - State *done(const gchar *str); + State *got_file(const gchar *filename); }; class StateGlob_filename : public StateExpectFile { private: - State *done(const gchar *str); + State *got_file(const gchar *filename); }; namespace States { diff --git a/src/ioview.cpp b/src/ioview.cpp index 323377f..e43fc2c 100644 --- a/src/ioview.cpp +++ b/src/ioview.cpp @@ -669,6 +669,33 @@ IOView::save(const gchar *filename) /* * Auxiliary functions */ + +/** + * Perform tilde expansion on a file name or path. + * + * This supports only strings with a "~" prefix. + * A user name after "~" is not supported. + * The $HOME environment variable is used to retrieve + * the current user's home directory. + */ +gchar * +expand_path(const gchar *path) +{ + if (!path) + path = ""; + + if (path[0] != '~' || (path[1] && !G_IS_DIR_SEPARATOR(path[1]))) + return g_strdup(path); + + /* + * $HOME should not have a trailing directory separator since + * it is canonicalized to an absolute path at startup, + * but this ensures that a proper path is constructed even if + * it does (e.g. $HOME is changed later on). + */ + return g_build_filename(g_getenv("HOME"), path+1, NIL); +} + #ifdef G_OS_UNIX gchar * diff --git a/src/ioview.h b/src/ioview.h index d9d8265..e0f23f4 100644 --- a/src/ioview.h +++ b/src/ioview.h @@ -34,6 +34,8 @@ namespace SciTECO { * Auxiliary functions */ +gchar *expand_path(const gchar *path); + /** * Get absolute/full version of a possibly relative path. * Works with existing and non-existing paths (in the latter case, diff --git a/src/parser.cpp b/src/parser.cpp index 6462933..3e3f387 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -40,6 +40,7 @@ #include "spawn.h" #include "glob.h" #include "cmdline.h" +#include "ioview.h" #include "error.h" namespace SciTECO { @@ -473,6 +474,7 @@ StateExpectString::custom(gchar chr) if (string_building) machine.reset(); + /* FIXME: possible memleak because of `string`? */ next = done(string ? : ""); g_free(string); return next; @@ -502,6 +504,23 @@ StateExpectString::custom(gchar chr) return this; } +State * +StateExpectFile::done(const gchar *str) +{ + gchar *filename = expand_path(str); + State *next; + + try { + next = got_file(filename); + } catch (...) { + g_free(filename); + throw; + } + + g_free(filename); + return next; +} + StateStart::StateStart() : State() { transitions['\0'] = this; @@ -1579,8 +1598,9 @@ UndoTokenChangeDir::run(void) * as set by the \fBHOME\fP environment variable. * This variable is alwas initialized by \*(ST * (see \fBsciteco\fP(1)). - * Therefore \(lqFG\fB$\fP\(rq is roughly equivalent - * to \(lqFG^EQ[$HOME]\fB$\fP\(rq. + * Therefore the expression \(lqFG\fB$\fP\(rq is + * roughly equivalent to both \(lqFG~\fB$\fP\(rq and + * \(lqFG^EQ[$HOME]\fB$\fP\(rq. * * The current working directory is also mapped to * the Q-Register \(lq$\(rq (dollar sign) which @@ -1590,20 +1610,20 @@ UndoTokenChangeDir::run(void) * command and directories can be tab-completed. */ State * -StateChangeDir::done(const gchar *str) +StateChangeDir::got_file(const gchar *filename) { BEGIN_EXEC(&States::start); /* passes ownership of string to undo token object */ undo.push(new UndoTokenChangeDir(g_get_current_dir())); - if (!*str) - str = g_getenv("HOME"); + if (!*filename) + filename = g_getenv("HOME"); - if (g_chdir(str)) + if (g_chdir(filename)) /* FIXME: Is errno usable on Windows here? */ throw Error("Cannot change working directory " - "to \"%s\"", str); + "to \"%s\"", filename); return &States::start; } diff --git a/src/parser.h b/src/parser.h index 605c1a5..feffb95 100644 --- a/src/parser.h +++ b/src/parser.h @@ -160,6 +160,12 @@ class StateExpectFile : public StateExpectString { public: StateExpectFile(bool _building = true, bool _last = true) : StateExpectString(_building, _last) {} + +private: + State *done(const gchar *str); + +protected: + virtual State *got_file(const gchar *filename) = 0; }; class StateExpectDir : public StateExpectFile { @@ -236,7 +242,7 @@ public: class StateChangeDir : public StateExpectDir { private: - State *done(const gchar *str); + State *got_file(const gchar *filename); }; class StateCondCommand : public State { diff --git a/src/qregisters.cpp b/src/qregisters.cpp index f15f0d7..20c1380 100644 --- a/src/qregisters.cpp +++ b/src/qregisters.cpp @@ -831,13 +831,13 @@ StateEQCommand::got_register(QRegister ®) } State * -StateLoadQReg::done(const gchar *str) +StateLoadQReg::got_file(const gchar *filename) { BEGIN_EXEC(&States::start); - if (*str) { + if (*filename) { /* Load file into Q-Register */ - register_argument->load(str); + register_argument->load(filename); } else { /* Edit Q-Register */ current_doc_undo_edit(); @@ -871,10 +871,10 @@ StateEPctCommand::got_register(QRegister ®) } State * -StateSaveQReg::done(const gchar *str) +StateSaveQReg::got_file(const gchar *filename) { BEGIN_EXEC(&States::start); - register_argument->save(str); + register_argument->save(filename); return &States::start; } @@ -1158,12 +1158,12 @@ StateMacro::got_register(QRegister ®) * If <file> could not be read, the command yields an error. */ State * -StateMacroFile::done(const gchar *str) +StateMacroFile::got_file(const gchar *filename) { BEGIN_EXEC(&States::start); /* don't create new local Q-Registers if colon modifier is given */ - Execute::file(str, !eval_colon()); + Execute::file(filename, !eval_colon()); return &States::start; } diff --git a/src/qregisters.h b/src/qregisters.h index 2933dc8..ec40bb4 100644 --- a/src/qregisters.h +++ b/src/qregisters.h @@ -420,7 +420,7 @@ private: class StateLoadQReg : public StateExpectFile { private: - State *done(const gchar *str); + State *got_file(const gchar *filename); }; class StateEPctCommand : public StateExpectQReg { @@ -430,7 +430,7 @@ private: class StateSaveQReg : public StateExpectFile { private: - State *done(const gchar *str); + State *got_file(const gchar *filename); }; class StateQueryQReg : public StateExpectQReg { @@ -494,7 +494,7 @@ private: class StateMacroFile : public StateExpectFile { private: - State *done(const gchar *str); + State *got_file(const gchar *filename); }; class StateCopyToQReg : public StateExpectQReg { diff --git a/src/ring.cpp b/src/ring.cpp index 906e72a..4646ea1 100644 --- a/src/ring.cpp +++ b/src/ring.cpp @@ -355,26 +355,26 @@ StateEditFile::initial(void) } State * -StateEditFile::done(const gchar *str) +StateEditFile::got_file(const gchar *filename) { BEGIN_EXEC(&States::start); if (!allowFilename) { - if (*str) + if (*filename) throw Error("If a buffer is selected by id, the <EB> " "string argument must be empty"); return &States::start; } - if (is_glob_pattern(str)) { - Globber globber(str, G_FILE_TEST_IS_REGULAR); - gchar *filename; + if (is_glob_pattern(filename)) { + Globber globber(filename, G_FILE_TEST_IS_REGULAR); + gchar *globbed_filename; - while ((filename = globber.next())) - do_edit(filename); + while ((globbed_filename = globber.next())) + do_edit(globbed_filename); } else { - do_edit(*str ? str : NULL); + do_edit(*filename ? filename : NULL); } return &States::start; @@ -422,14 +422,14 @@ StateEditFile::done(const gchar *str) * characters are enabled by default. */ State * -StateSaveFile::done(const gchar *str) +StateSaveFile::got_file(const gchar *filename) { BEGIN_EXEC(&States::start); if (QRegisters::current) - QRegisters::current->save(str); + QRegisters::current->save(filename); else - ring.current->save(*str ? str : NULL); + ring.current->save(*filename ? filename : NULL); return &States::start; } @@ -223,12 +223,12 @@ private: void do_edit(tecoInt id); void initial(void); - State *done(const gchar *str); + State *got_file(const gchar *filename); }; class StateSaveFile : public StateExpectFile { private: - State *done(const gchar *str); + State *got_file(const gchar *filename); }; namespace States { |