diff --git a/builtin_set.cpp b/builtin_set.cpp index 633416497..598d6c402 100644 --- a/builtin_set.cpp +++ b/builtin_set.cpp @@ -428,7 +428,7 @@ static int builtin_set(parser_t &parser, wchar_t **argv) int slice=0; int i; - wchar_t *bad_char; + const wchar_t *bad_char = NULL; /* Parse options to obtain the requested operation and the modifiers */ diff --git a/common.cpp b/common.cpp index 9e4fb50d9..81d7b8396 100644 --- a/common.cpp +++ b/common.cpp @@ -486,17 +486,17 @@ void append_format(wcstring &str, const wchar_t *format, ...) va_end(va); } -wchar_t *wcsvarname(const wchar_t *str) +const wchar_t *wcsvarname(const wchar_t *str) { while (*str) { if ((!iswalnum(*str)) && (*str != L'_')) { - return (wchar_t *)str; + return str; } str++; } - return 0; + return NULL; } const wchar_t *wcsfuncname(const wchar_t *str) diff --git a/common.h b/common.h index 188215093..ca4c83371 100644 --- a/common.h +++ b/common.h @@ -650,7 +650,7 @@ char **wcsv2strv(const wchar_t * const *in); \return null if this is a valid name, and a pointer to the first invalid character otherwise */ -wchar_t *wcsvarname(const wchar_t *str); +const wchar_t *wcsvarname(const wchar_t *str); /** diff --git a/env_universal_common.cpp b/env_universal_common.cpp index baf94a19f..1fe2c4665 100644 --- a/env_universal_common.cpp +++ b/env_universal_common.cpp @@ -42,21 +42,6 @@ */ #define SET_EXPORT_MBS "SET_EXPORT" -/** - Non-wide version of the erase command -*/ -#define ERASE_MBS "ERASE" - -/** - Non-wide version of the barrier command -*/ -#define BARRIER_MBS "BARRIER" - -/** - Non-wide version of the barrier_reply command -*/ -#define BARRIER_REPLY_MBS "BARRIER_REPLY" - /** Error message */ @@ -409,6 +394,72 @@ void set_body(message_t *msg, ...) va_end(arg_list); } +/* Converts input to UTF-8 and appends it to receiver, using storage as temp storage */ +static bool append_utf8(const wcstring &input, std::string *receiver, std::string *storage) +{ + bool result = false; + if (wchar_to_utf8_string(input, storage)) + { + receiver->append(*storage); + result = true; + } + return result; +} + +/* Creates a file entry like "SET fish_color_cwd:FF0". Appends the result to *result (as UTF8). Returns true on success. storage may be used for temporary storage, to avoid allocations */ +static bool append_file_entry(fish_message_type_t type, const wcstring &key_in, const wcstring &val_in, std::string *result, std::string *storage) +{ + assert(storage != NULL); + assert(result != NULL); + + // Record the length on entry, in case we need to back up + bool success = true; + const size_t result_length_on_entry = result->size(); + + // Append header like "SET " + result->append(type==SET ? SET_MBS : SET_EXPORT_MBS); + result->push_back(' '); + + // Append variable name like "fish_color_cwd" + if (wcsvarname(key_in.c_str())) + { + debug(0, L"Illegal variable name: '%ls'", key_in.c_str()); + success = false; + } + if (success && ! append_utf8(key_in, result, storage)) + { + debug(0, L"Could not convert %ls to narrow character string", key_in.c_str()); + success = false; + } + + // Append ":" + if (success) + { + result->push_back(':'); + } + + // Append value + if (success && ! append_utf8(full_escape(val_in.c_str()), result, storage)) + { + debug(0, L"Could not convert %ls to narrow character string", val_in.c_str()); + success = false; + } + + // Append newline + if (success) + { + result->push_back('\n'); + } + + // Don't modify result on failure. It's sufficient to simply resize it since all we ever did was append to it. + if (! success) + { + result->resize(result_length_on_entry); + } + + return success; +} + /* Returns an instance of message_t allocated via new */ message_t *create_message(fish_message_type_t type, const wchar_t *key_in, @@ -614,6 +665,7 @@ void env_universal_t::enqueue_all_internal(connection_t *c) const message_t *msg = create_message(entry.exportv ? SET_EXPORT : SET, key.c_str(), entry.val.c_str()); msg->count=1; c->unsent.push(msg); + fprintf(stderr, "%s", msg->body.c_str()); } try_send_all(c); } @@ -689,18 +741,52 @@ bool env_universal_t::load_from_path(const wcstring &path, callback_data_list_t return result; } -void env_universal_t::write_to_fd(int fd) +/* Writes our state to the fd. path is provided only for error reporting */ +bool env_universal_t::write_to_fd(int fd, const wcstring &path) { ASSERT_IS_LOCKED(lock); assert(fd >= 0); - connection_t conn(fd); + bool success = true; + + // Stuff we output to fd + std::string contents; + + // Temporary storage + std::string storage; + + // Write the save message. If this fails, we don't bother complaining. write_loop(fd, SAVE_MSG, strlen(SAVE_MSG)); - this->enqueue_all_internal(&conn); + + var_table_t::const_iterator iter = vars.begin(); + while (iter != vars.end()) + { + // Append the entry. Note that append_file_entry may fail, but that only affects one variable; soldier on. + const wcstring &key = iter->first; + const var_entry_t &entry = iter->second; + append_file_entry(entry.exportv ? SET_EXPORT : SET, key, entry.val, &contents, &storage); + + // Go to next + ++iter; + + // Flush if this is the last iteration or we exceed a page + if (iter == vars.end() || contents.size() >= 4096) + { + if (write_loop(fd, contents.data(), contents.size()) < 0) + { + int err = errno; + report_error(err, L"Unable to write to universal variables file '%ls'", path.c_str()); + success = false; + break; + } + contents.clear(); + } + } /* Since we just wrote out this file, it matches our internal state; pretend we read from it */ this->last_read_file = file_id_for_fd(fd); - /* Do not destroy the connection; we don't close the file */ + /* We don't close the file */ + return success; } bool env_universal_t::move_new_vars_file_into_place(const wcstring &src, const wcstring &dst) @@ -985,7 +1071,7 @@ bool env_universal_t::sync(callback_data_list_t *callbacks) if (success) { assert(private_fd >= 0); - this->write_to_fd(private_fd); + success = this->write_to_fd(private_fd, private_file_path); } if (success) diff --git a/env_universal_common.h b/env_universal_common.h index 19e56986e..083f7fbf6 100644 --- a/env_universal_common.h +++ b/env_universal_common.h @@ -209,7 +209,7 @@ class env_universal_t /* Functions concerned with saving */ bool open_and_acquire_lock(const wcstring &path, int *out_fd); bool open_temporary_file(const wcstring &directory, wcstring *out_path, int *out_fd); - void write_to_fd(int fd); + bool write_to_fd(int fd, const wcstring &path); bool move_new_vars_file_into_place(const wcstring &src, const wcstring &dst); /* File id from which we last read */