diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/interface-curses.cpp | 19 | ||||
-rw-r--r-- | src/ioview.cpp | 11 | ||||
-rw-r--r-- | src/main.cpp | 36 | ||||
-rw-r--r-- | src/parser.cpp | 28 | ||||
-rw-r--r-- | src/qregisters.cpp | 75 | ||||
-rw-r--r-- | src/qregisters.h | 14 | ||||
-rw-r--r-- | src/spawn.cpp | 17 |
7 files changed, 168 insertions, 32 deletions
diff --git a/src/interface-curses.cpp b/src/interface-curses.cpp index 5f8a00c..1dcca8f 100644 --- a/src/interface-curses.cpp +++ b/src/interface-curses.cpp @@ -178,6 +178,13 @@ InterfaceCurses::init_interactive(void) void InterfaceCurses::init_batch(void) { + /* + * NOTE: It's still safe to use g_getenv(). + * Actually the process environment has not yet been + * imported into the Q-Register table. + * Also, the batch mode initialization will be + * simplified soon, anyway. + */ const gchar *term = g_getenv("TERM"); /* @@ -227,7 +234,13 @@ InterfaceCurses::init_interactive(void) { const gchar *term = g_getenv("TERM"); - /* at least try to report a broken $TERM */ + /* + * At least try to report a broken $TERM. + * g_getenv() may still be used here since we must refer to + * same value as used in init_batch() as opposed to the + * current value of the "$TERM" register. + * Also, this code will have to be simplified soon, anyway. + */ if (!term || !*term) { g_fprintf(stderr, "Error initializing interactive mode: " "$TERM is unset or empty.\n"); @@ -816,6 +829,10 @@ InterfaceCurses::event_loop_impl(void) * Set window title to a reasonable default, * in case it is not reset immediately by the * shell. + * FIXME: It may be unsafe to access $TERM here + * and the value of Q-Register $TERM may have + * diverged. This should be adapted once we rewrite + * batch-mode initialization! */ #if !PDCURSES && defined(HAVE_TIGETSTR) set_window_title(g_getenv("TERM") ? : ""); diff --git a/src/ioview.cpp b/src/ioview.cpp index e43fc2c..713b3fb 100644 --- a/src/ioview.cpp +++ b/src/ioview.cpp @@ -36,6 +36,7 @@ #include "interface.h" #include "undo.h" #include "error.h" +#include "qregisters.h" #include "ioview.h" #ifdef HAVE_WINDOWS_H @@ -675,12 +676,14 @@ IOView::save(const gchar *filename) * * This supports only strings with a "~" prefix. * A user name after "~" is not supported. - * The $HOME environment variable is used to retrieve + * The $HOME environment variable/register is used to retrieve * the current user's home directory. */ gchar * expand_path(const gchar *path) { + gchar *home, *ret; + if (!path) path = ""; @@ -693,7 +696,11 @@ expand_path(const gchar *path) * 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); + home = QRegisters::globals["$HOME"]->get_string(); + ret = g_build_filename(home, path+1, NIL); + g_free(home); + + return ret; } #ifdef G_OS_UNIX diff --git a/src/main.cpp b/src/main.cpp index 6966d22..120d73e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -196,9 +196,15 @@ static inline void initialize_environment(const gchar *program) { gchar *default_configpath, *abs_path; - gchar **env; /* + * Initialize some "special" environment variables. + * For ease of use and because there are no threads yet, + * we modify the process environment directly. + * Later it is imported into the global Q-Register table + * and the process environment should no longer be accessed + * directly. + * * Initialize and canonicalize $HOME. * Therefore we can refer to $HOME as the * current user's home directory on any platform @@ -237,20 +243,19 @@ initialize_environment(const gchar *program) g_setenv("SCITECOPATH", abs_path, TRUE); g_free(abs_path); - env = g_listenv(); - - for (gchar **key = env; *key; key++) { - gchar name[1 + strlen(*key) + 1]; - QRegister *reg; - - name[0] = '$'; - strcpy(name + 1, *key); - - reg = QRegisters::globals.insert(name); - reg->set_string(g_getenv(*key)); - } - - g_strfreev(env); + /* + * Import process environment into global Q-Register + * table. While it is safe to use g_setenv() early + * on at startup, it might be problematic later on + * (e.g. it's non-thread-safe). + * Therefore the environment registers in the global + * table should be used from now on to set and get + * environment variables. + * When spawning external processes that should inherit + * the environment variables, the environment should + * be exported via QRegisters::globals.get_environ(). + */ + QRegisters::globals.set_environ(); } /* @@ -388,6 +393,7 @@ main(int argc, char **argv) } if (!mung_file && mung_profile) + /* NOTE: Still safe to use g_getenv() */ mung_file = g_build_filename(g_getenv("SCITECOCONFIG"), INI_FILE, NIL); diff --git a/src/parser.cpp b/src/parser.cpp index 3e3f387..f88ae90 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1595,16 +1595,18 @@ UndoTokenChangeDir::run(void) * * If <directory> is omitted, the working directory * is changed to the current user's home directory - * as set by the \fBHOME\fP environment variable. - * This variable is alwas initialized by \*(ST + * as set by the \fBHOME\fP environment variable + * (i.e. its corresponding \(lq$HOME\(rq environment + * register). + * This variable is always initialized by \*(ST * (see \fBsciteco\fP(1)). * Therefore the expression \(lqFG\fB$\fP\(rq is - * roughly equivalent to both \(lqFG~\fB$\fP\(rq and + * exactly 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 - * may be used retrieve the current working directory. + * the special global Q-Register \(lq$\(rq (dollar sign) + * which may be used retrieve the current working directory. * * String-building characters are enabled on this * command and directories can be tab-completed. @@ -1612,19 +1614,25 @@ UndoTokenChangeDir::run(void) State * StateChangeDir::got_file(const gchar *filename) { + gchar *dir; + BEGIN_EXEC(&States::start); /* passes ownership of string to undo token object */ undo.push(new UndoTokenChangeDir(g_get_current_dir())); - if (!*filename) - filename = g_getenv("HOME"); + dir = *filename ? g_strdup(filename) + : QRegisters::globals["$HOME"]->get_string(); - if (g_chdir(filename)) + if (g_chdir(dir)) { /* FIXME: Is errno usable on Windows here? */ - throw Error("Cannot change working directory " - "to \"%s\"", filename); + Error err("Cannot change working directory " + "to \"%s\"", dir); + g_free(dir); + throw err; + } + g_free(dir); return &States::start; } diff --git a/src/qregisters.cpp b/src/qregisters.cpp index 20c1380..22d7300 100644 --- a/src/qregisters.cpp +++ b/src/qregisters.cpp @@ -529,7 +529,80 @@ QRegisterTable::edit(QRegister *reg) QRegisters::current = reg; } -/* +void +QRegisterTable::set_environ(void) +{ + /* + * NOTE: Using g_get_environ() would be more efficient, + * but it appears to be broken, at least on Wine + * and Windows 2000. + */ + gchar **env = g_listenv(); + + for (gchar **key = env; *key; key++) { + gchar name[1 + strlen(*key) + 1]; + QRegister *reg; + + name[0] = '$'; + strcpy(name + 1, *key); + + reg = insert(name); + reg->set_string(g_getenv(*key)); + } + + g_strfreev(env); +} + +gchar ** +QRegisterTable::get_environ(void) +{ + QRegister *first = nfind("$"); + + gint envp_len = 1; + gchar **envp, **p; + + /* + * Iterate over all registers beginning with "$" to + * guess the size required for the environment array. + * This may waste a few bytes because not __every__ + * register beginning with "$" is an environment + * register. + */ + for (QRegister *cur = first; + cur && cur->name[0] == '$'; + cur = (QRegister *)cur->next()) + envp_len++; + + p = envp = (gchar **)g_malloc(sizeof(gchar *)*envp_len); + + for (QRegister *cur = first; + cur && cur->name[0] == '$'; + cur = (QRegister *)cur->next()) { + gchar *value; + + /* + * Ignore the "$" register (not an environment + * variable register) and registers whose + * name contains "=" (not allowed in environment + * variable names). + */ + if (!cur->name[1] || strchr(cur->name+1, '=')) + continue; + + value = cur->get_string(); + /* more efficient than g_environ_setenv() */ + *p++ = g_strconcat(cur->name+1, "=", value, NIL); + g_free(value); + } + + *p = NULL; + + return envp; +} + +/** + * Free resources associated with table. + * * This is similar to RBTree::clear() but * has the advantage that we can check whether some * register is currently edited. diff --git a/src/qregisters.h b/src/qregisters.h index ec40bb4..0ca230e 100644 --- a/src/qregisters.h +++ b/src/qregisters.h @@ -255,6 +255,10 @@ public: insert(QRegister *reg) { reg->must_undo = must_undo; + /* FIXME: Returns already existing regs with the same name. + This could be used to optimize commands that initialize + a register if it does not yet exist (saves one table + lookup): */ RBTree::insert(reg); return reg; } @@ -283,6 +287,13 @@ public: return operator [](buf); } + inline QRegister * + nfind(const gchar *name) + { + QRegister reg(name); + return (QRegister *)RBTree::nfind(®); + } + void edit(QRegister *reg); inline QRegister * edit(const gchar *name) @@ -295,6 +306,9 @@ public: return reg; } + void set_environ(void); + gchar **get_environ(void); + void clear(void); }; diff --git a/src/spawn.cpp b/src/spawn.cpp index 9f951f5..b5d64e9 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -198,6 +198,14 @@ parse_shell_command_line(const gchar *cmdline, GError **error) * \(lq0,128ED\(rq, and is recommended when writing cross-platform * macros using the EC command. * + * The spawned process inherits both \*(ST's current working + * directory and its environment variables. + * More precisely, \*(ST uses its environment registers + * to construct the spawned process' environment. + * Therefore it is also straight forward to change the working + * directory or some environment variable temporarily + * for a spawned process. + * * Note that when run interactively and subsequently rubbed * out, \*(ST can easily undo all changes to the editor * state. @@ -211,7 +219,7 @@ parse_shell_command_line(const gchar *cmdline, GError **error) * * In interactive mode, \*(ST performs TAB-completion * of filenames in the <command> string parameter but - * by doing so does not attempt any escaping of shell-relevant + * does not attempt any escaping of shell-relevant * characters like whitespaces. */ StateExecuteCommand::StateExecuteCommand() : StateExpectString() @@ -308,7 +316,7 @@ StateExecuteCommand::done(const gchar *str) */ return &States::start; - gchar **argv; + gchar **argv, **envp; static const gint flags = G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL; @@ -330,11 +338,14 @@ StateExecuteCommand::done(const gchar *str) if (!argv) goto gerror; - g_spawn_async_with_pipes(NULL, argv, NULL, (GSpawnFlags)flags, + envp = QRegisters::globals.get_environ(); + + g_spawn_async_with_pipes(NULL, argv, envp, (GSpawnFlags)flags, NULL, NULL, &pid, &stdin_fd, &stdout_fd, NULL, &ctx.error); + g_strfreev(envp); g_strfreev(argv); if (ctx.error) |