diff options
-rw-r--r-- | doc/sciteco.1.in | 24 | ||||
-rw-r--r-- | doc/sciteco.7.template | 51 | ||||
-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 |
9 files changed, 227 insertions, 48 deletions
diff --git a/doc/sciteco.1.in b/doc/sciteco.1.in index 0f091db..ef49579 100644 --- a/doc/sciteco.1.in +++ b/doc/sciteco.1.in @@ -149,15 +149,27 @@ environment are inserted into the global Q-Register table. A dollar sign is prepended before each variable name, so that for instance the variable \(lqHOME\(rq can be examined by macros by reading the string-content of Q-Register \(lq$HOME\(rq. -Changes to these Q-Registers are currently not applied to -the corresponding environment variables. .LP -The following environment variables are initialized with default -values by \*(ST if they are unset: +Changes to these environment registers are not applied to +the process environment for technical reasons. +Nevertheless, \*(ST will always access the environment registers +instead of the process environment when it needs to evaluate +an environment variable internally. +Furthermore, when spawning subprocesses, \*(ST will export +all Q-Registers beginning with \(lq$\(rq that do not have +a \(lq=\(rq in their name into the subprocess environment. +Therefore, the subset of Q-Registers whose name begins with \(lq$\(rq +can be considered practically identical to the process environment +and \*(ST macros can access, modify and extend the environment using +these registers. +.LP +The following environment variables and registers are initialized with +default values by \*(ST if they are unset: .TP .B HOME Home directory of the current user. -This may be used e.g. by the \fBFG\fP command. +This may be used e.g. by the \fBFG\fP command and for +tilde-expansions. If unset, it defaults to the current user's home directory as set by .BR passwd (5) @@ -191,7 +203,7 @@ defaults to the standard library installation path at . .LP The \fBHOME\fP, \fBSCITECOCONFIG\fP and \fBSCITECOPATH\fP environment -variables are canonicalized to absolute paths. +variables and registers are canonicalized to absolute paths. Therefore it is possible to define them relative to the working directory of \*(ST when it starts up while macros can work with the corresponding registers to locate files diff --git a/doc/sciteco.7.template b/doc/sciteco.7.template index db31daf..79b2eff 100644 --- a/doc/sciteco.7.template +++ b/doc/sciteco.7.template @@ -989,17 +989,26 @@ Macro invocations might create new local Q-Register tables for the executed code. \*(ST initializes the Q-Registers \(lqA\(rq to \(lqZ\(rq and \(lq0\(rq to \(lq9\(rq in every Q-Register table. -Furthermore \*(ST defines and initializes the following special -global registers: +.LP +There are global Q-Registers with special significance for \*(ST +because they may be accessed by commands opaquely. +Some of these registers represent information beyond their +textual and numeric cells \(em they overwrite default operations +with custom side-effects in order to support unique idioms. +Some of the registers with special significance are initialized +by \*(ST while others must be manually defined. +The following list is an overview of all special global registers: .TP 2 .BR _ " (underscore)" Search string and search condition register. Also used by the globbing command \fBEN\fP, so it is also the glob string and condition register. +It is initialized automatically on startup. .TP .BR - " (minus)" Replacement string register. Its integer part is currently unused. +It is initialized automatically on startup. .TP .BR * " (asterisk)" File name (string part) and id (numeric part) of current @@ -1026,6 +1035,7 @@ temporarily: .EX [* ! ...change current buffer... ! ]* .EE +The register is initialized automatically on startup. .TP .BR $ " (dollar)" The process' current working directory (string part). @@ -1047,14 +1057,36 @@ The \(lq$\(rq register may also be edited but changing its string contents this way has no effect on the current working directory. Appending to the \(lq$\(rq register is unsupported. +The register is initialized automatically on startup. .TP .BR $ " (Escape)" Command-line replacement register. Its integer part is unused. +It is initialized automatically on startup. +.TP +.BI $ variable +Global Q-Registers beginning with a dollar sign that +do not contain any \(lq=\(rq represent the process environment +(the environment variables). +The register \(lq$\(rq does \fBnot\fP belong to the process +environment. +Some environment variables are initialized with default values +if the corresponding environment variable is unset and +some may be accessed internally by \*(ST commands. +In other respects, the environment registers are ordinary +non-customized registers that support all operations. +Their numeric parts are currently unused. +The mechanisms involved are documented more elaborately in +.BR sciteco (1). +.TP +.BI ^F key +Function key registers as documented in section +\fBKEY TRANSLATION\fP. +Their string-content represents a function key macro +and their numeric part is a function key mask. +None of those registers are automatically initialized +on startup. .LP -The \*(ST runtime may access other special Q-Registers -(e.g. function key macros), but they are not initialized by -default. Some commands may create and initialize new registers if necessary, while it is an error to access undefined registers for some other commands. @@ -1297,9 +1329,8 @@ current working directory (as can be set e.g. via the Nevertheless, \*(ST will function properly after changing the working directory as \*(ST canonicalizes relative paths to absolute paths if necessary. -Both buffer file names and some special environment -variables (as well as their corresponding Q-Registers) -documented in +Both buffer file names and some special Q-Registers +corresponding to environment variables documented in .BR sciteco (1) are canonicalized in this way. .LP @@ -1338,7 +1369,7 @@ In \*(ST this expansion takes place \fIafter\fP processing string building characters. Unlike the UNIX-shell, \*(ST will only expand the \fIcurrent user's\fP home directory using the value of the -\(lqHOME\(rq environment variable. +\(lq$HOME\(rq environment register. Thus the \(lq~\fIusername\fP\(rq syntax is \fBnot\fP supported. .LP Last but not least, some commands accept glob patterns @@ -1504,7 +1535,7 @@ Note that, \*(ST itself is designed not to produce non-forward-slash separators and at least allows the user to generate forward-slashes in portable macros. This is not the case, for instance when working with environment -variables. +registers. .TP .IB n \(dqS .TQ 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) |