static wcstring path_create_data() { bool done = false; wcstring res; const env_var_t xdg_dir = env_get_string(L"XDG_DATA_HOME"); if (!xdg_dir.missing()) { res = xdg_dir + L"/fish"; if (!create_directory(res)) { done = true; } } else { const env_var_t home = env_get_string(L"HOME"); if (!home.missing()) { res = home + L"/.local/share/fish"; if (!create_directory(res)) { done = true; } } } if (!done) { res.clear(); debug(0, _(L"Unable to create a data directory for fish. Your history will not be saved. " L"Please set the $XDG_DATA_HOME variable to a directory where the current user " L"has write access.")); } return res; }
static wcstring path_create_config() { bool done = false; wcstring res; const env_var_t xdg_dir = env_get_string(L"XDG_CONFIG_HOME"); if (!xdg_dir.missing()) { res = xdg_dir + L"/fish"; if (!create_directory(res)) { done = true; } } else { const env_var_t home = env_get_string(L"HOME"); if (!home.missing()) { res = home + L"/.config/fish"; if (!create_directory(res)) { done = true; } } } if (!done) { res.clear(); debug(0, _(L"Unable to create a configuration directory for fish. Your personal settings " L"will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory " L"where the current user has write access.")); } return res; }
/** Set up default values for various variables if not defined. */ static void env_set_defaults() { if( env_get_string(L"USER").missing() ) { struct passwd *pw = getpwuid( getuid()); wchar_t *unam = str2wcs( pw->pw_name ); env_set( L"USER", unam, ENV_GLOBAL ); free( unam ); } if( env_get_string(L"HOME").missing() ) { const env_var_t unam = env_get_string( L"USER" ); char *unam_narrow = wcs2str( unam.c_str() ); struct passwd *pw = getpwnam( unam_narrow ); wchar_t *dir = str2wcs( pw->pw_dir ); env_set( L"HOME", dir, ENV_GLOBAL ); free( dir ); free( unam_narrow ); } env_set_pwd(); }
/** Set up default values for various variables if not defined. */ static void env_set_defaults() { if (env_get_string(L"USER").missing()) { struct passwd *pw = getpwuid(getuid()); if (pw->pw_name != NULL) { const wcstring wide_name = str2wcstring(pw->pw_name); env_set(L"USER", wide_name.c_str(), ENV_GLOBAL); } } if (env_get_string(L"HOME").missing()) { const env_var_t unam = env_get_string(L"USER"); char *unam_narrow = wcs2str(unam.c_str()); struct passwd *pw = getpwnam(unam_narrow); if (pw->pw_dir != NULL) { const wcstring dir = str2wcstring(pw->pw_dir); env_set(L"HOME", dir.c_str(), ENV_GLOBAL); } free(unam_narrow); } env_set_pwd(); }
MODULE initialise_the_program (void) { char *db_name, *db_user, *db_pwd, *db_type, *db_extra; dbyte type; db_name = env_get_string ("db_name", NULL); db_user = env_get_string ("db_user", NULL); db_pwd = env_get_string ("db_pwd", NULL); db_type = env_get_string ("db_type", NULL); db_extra = env_get_string ("db_extra", NULL); type = get_db_type (db_type); dbio_connect (db_name, db_user, db_pwd, db_extra, TRUE, type); buffer.data = mem_alloc (BUFFER_MAX + 1); if (buffer.data) the_next_event = ok_event; else the_next_event = error_event; }
void kill_add(const wcstring &str) { ASSERT_IS_MAIN_THREAD(); if (str.empty()) return; wcstring cmd; wcstring escaped_str; kill_list.push_front(str); /* Check to see if user has set the FISH_CLIPBOARD_CMD variable, and, if so, use it instead of checking the display, etc. I couldn't think of a safe way to allow overide of the echo command too, so, the command used must accept the input via stdin. */ const env_var_t clipboard_wstr = env_get_string(L"FISH_CLIPBOARD_CMD"); if (!clipboard_wstr.missing()) { escaped_str = escape(str.c_str(), ESCAPE_ALL); cmd.assign(L"echo -n "); cmd.append(escaped_str); cmd.append(clipboard_wstr); } else { /* This is for sending the kill to the X copy-and-paste buffer */ if (!has_xsel()) { return; } const env_var_t disp_wstr = env_get_string(L"DISPLAY"); if (!disp_wstr.missing()) { escaped_str = escape(str.c_str(), ESCAPE_ALL); cmd.assign(L"echo -n "); cmd.append(escaped_str); cmd.append(L" | xsel -i -b"); } } if (! cmd.empty()) { if (exec_subshell(cmd, false /* do not apply exit status */) == -1) { /* Do nothing on failiure */ } cut_buffer = escaped_str; } }
/** When fishd isn't started, this function is provided to env_universal as a callback, it tries to start up fishd. It's implementation is a bit of a hack, since it evaluates a bit of shellscript, and it might be used at times when that might not be the best idea. */ static void start_fishd() { struct passwd *pw = getpwuid(getuid()); debug(3, L"Spawning new copy of fishd"); if (!pw) { debug(0, _(L"Could not get user information")); return; } wcstring cmd = format_string(FISHD_CMD, pw->pw_name); /* Prefer the fishd in __fish_bin_dir, if exists */ const env_var_t bin_dir = env_get_string(L"__fish_bin_dir"); if (! bin_dir.missing_or_empty()) { wcstring path = bin_dir + L"/fishd"; if (waccess(path, X_OK) == 0) { /* The path command just looks like 'fishd', so insert the bin path to make it absolute */ cmd.insert(0, bin_dir + L"/"); } } parser_t &parser = parser_t::principal_parser(); parser.eval(cmd, io_chain_t(), TOP); }
/// Properly sets all locale information. static void handle_locale(const wchar_t *env_var_name) { debug(2, L"handle_locale() called in response to '%ls' changing", env_var_name); const char *old_msg_locale = setlocale(LC_MESSAGES, NULL); const env_var_t val = env_get_string(env_var_name, ENV_EXPORT); const std::string &value = wcs2string(val); const std::string &name = wcs2string(env_var_name); debug(2, L"locale var %s='%s'", name.c_str(), value.c_str()); if (val.empty()) { unsetenv(name.c_str()); } else { setenv(name.c_str(), value.c_str(), 1); } char *locale = setlocale(LC_ALL, ""); fish_setlocale(); debug(2, L"handle_locale() setlocale(): '%s'", locale); const char *new_msg_locale = setlocale(LC_MESSAGES, NULL); debug(3, L"old LC_MESSAGES locale: '%s'", old_msg_locale); debug(3, L"new LC_MESSAGES locale: '%s'", new_msg_locale); #ifdef HAVE__NL_MSG_CAT_CNTR if (strcmp(old_msg_locale, new_msg_locale)) { // Make change known to GNU gettext. extern int _nl_msg_cat_cntr; _nl_msg_cat_cntr++; } #endif }
/* Given a string, return whether it prefixes a path that we could cd into. Return that path in out_path. Expects path to be unescaped. */ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_directory, path_flags_t flags, wcstring *out_path) { wcstring_list_t directories; if (string_prefixes_string(L"./", path)) { /* Ignore the CDPATH in this case; just use the working directory */ directories.push_back(working_directory); } else { /* Get the CDPATH */ env_var_t cdpath = env_get_string(L"CDPATH"); if (cdpath.missing_or_empty()) cdpath = L"."; /* Tokenize it into directories */ wcstokenizer tokenizer(cdpath, ARRAY_SEP_STR); wcstring next_path; while (tokenizer.next(next_path)) { /* Ensure that we use the working directory for relative cdpaths like "." */ directories.push_back(apply_working_directory(next_path, working_directory)); } } /* Call is_potential_path with all of these directories */ bool result = is_potential_path(path, directories, flags | PATH_REQUIRE_DIR, out_path); #if 0 if (out_path) { printf("%ls -> %ls\n", path.c_str(), out_path->c_str()); } #endif return result; }
/// Print the names of all environment variables in the scope, with or without shortening, with or /// without values, with or without escaping static void print_variables(int include_values, int esc, bool shorten_ok, int scope, io_streams_t &streams) { wcstring_list_t names = env_get_names(scope); sort(names.begin(), names.end()); for (size_t i = 0; i < names.size(); i++) { const wcstring key = names.at(i); const wcstring e_key = escape_string(key, 0); streams.out.append(e_key); if (include_values) { env_var_t value = env_get_string(key, scope); if (!value.missing()) { int shorten = 0; if (shorten_ok && value.length() > 64) { shorten = 1; value.resize(60); } wcstring e_value = esc ? expand_escape_variable(value) : value; streams.out.append(L" "); streams.out.append(e_value); if (shorten) { streams.out.push_back(ellipsis_char); } } } streams.out.append(L"\n"); } }
/// Insert a list of all dynamically loaded functions into the specified list. static void autoload_names(std::set<wcstring> &names, int get_hidden) { size_t i; const env_var_t path_var_wstr = env_get_string(L"fish_function_path"); if (path_var_wstr.missing()) return; const wchar_t *path_var = path_var_wstr.c_str(); wcstring_list_t path_list; tokenize_variable_array(path_var, path_list); for (i = 0; i < path_list.size(); i++) { const wcstring &ndir_str = path_list.at(i); const wchar_t *ndir = (wchar_t *)ndir_str.c_str(); DIR *dir = wopendir(ndir); if (!dir) continue; wcstring name; while (wreaddir(dir, name)) { const wchar_t *fn = name.c_str(); const wchar_t *suffix; if (!get_hidden && fn[0] == L'_') continue; suffix = wcsrchr(fn, L'.'); if (suffix && (wcscmp(suffix, L".fish") == 0)) { wcstring name(fn, suffix - fn); names.insert(name); } } closedir(dir); } }
static std::map<wcstring, env_var_t> snapshot_vars(const wcstring_list_t &vars) { std::map<wcstring, env_var_t> result; for (wcstring_list_t::const_iterator it = vars.begin(), end = vars.end(); it != end; ++it) { result.insert(std::make_pair(*it, env_get_string(*it))); } return result; }
/// Make sure the PATH variable contains something. static void setup_path() { const env_var_t path = env_get_string(L"PATH"); if (path.missing_or_empty()) { const wchar_t *value = L"/usr/bin" ARRAY_SEP_STR L"/bin"; env_set(L"PATH", value, ENV_GLOBAL | ENV_EXPORT); } }
rgb_color_t highlight_get_color( int highlight, bool is_background ) { size_t i; int idx=0; rgb_color_t result; if( highlight < 0 ) return rgb_color_t::normal(); if( highlight > (1<<VAR_COUNT) ) return rgb_color_t::normal(); for( i=0; i<VAR_COUNT; i++ ) { if( highlight & (1<<i )) { idx = i; break; } } env_var_t val_wstr = env_get_string( highlight_var[idx]); // debug( 1, L"%d -> %d -> %ls", highlight, idx, val ); if (val_wstr.missing()) val_wstr = env_get_string( highlight_var[0]); if( ! val_wstr.missing() ) result = parse_color( val_wstr, is_background ); if( highlight & HIGHLIGHT_VALID_PATH ) { env_var_t val2_wstr = env_get_string( L"fish_color_valid_path" ); const wcstring val2 = val2_wstr.missing() ? L"" : val2_wstr.c_str(); rgb_color_t result2 = parse_color( val2, is_background ); if( result.is_normal() ) result = result2; else { if( result2.is_bold() ) result.set_bold(true); if( result2.is_underline() ) result.set_underline(true); } } return result; }
env_var_t env_vars_snapshot_t::get(const wcstring &key) const { // If we represent the current state, bounce to env_get_string. if (this->is_current()) { return env_get_string(key); } std::map<wcstring, wcstring>::const_iterator iter = vars.find(key); return iter == vars.end() ? env_var_t::missing_var() : env_var_t(iter->second); }
wcstring env_get_pwd_slash(void) { env_var_t pwd = env_get_string(L"PWD"); if (pwd.missing_or_empty()) { return L""; } if (!string_suffixes_string(L"/", pwd)) { pwd.push_back(L'/'); } return pwd; }
env_vars_snapshot_t::env_vars_snapshot_t(const wchar_t *const *keys) { ASSERT_IS_MAIN_THREAD(); wcstring key; for (size_t i = 0; keys[i]; i++) { key.assign(keys[i]); const env_var_t val = env_get_string(key); if (!val.missing()) { vars[key] = val; } } }
long env_get_number ( const char *name, long default_value) { char *variable_value; variable_value = env_get_string (name, NULL); return (variable_value? atol (variable_value): default_value); }
Bool env_get_boolean ( const char *name, Bool default_value) { char *variable_value; variable_value = env_get_string (name, NULL); return (variable_value? (conv_str_bool (variable_value) != 0): default_value); }
/// Properly sets all timezone information. static void handle_timezone(const wchar_t *env_var_name) { debug(2, L"handle_timezone() called in response to '%ls' changing", env_var_name); const env_var_t val = env_get_string(env_var_name, ENV_EXPORT); const std::string &value = wcs2string(val); const std::string &name = wcs2string(env_var_name); debug(2, L"timezone var %s='%s'", name.c_str(), value.c_str()); if (val.empty()) { unsetenv(name.c_str()); } else { setenv(name.c_str(), value.c_str(), 1); } tzset(); }
static wcstring fishd_get_config() { bool done = false; wcstring result; env_var_t xdg_dir = env_get_string(L"XDG_CONFIG_HOME", ENV_GLOBAL | ENV_EXPORT); if (! xdg_dir.missing_or_empty()) { result = xdg_dir; append_path_component(result, L"/fish"); if (!create_directory(result)) { done = true; } } else { env_var_t home = env_get_string(L"HOME", ENV_GLOBAL | ENV_EXPORT); if (! home.missing_or_empty()) { result = home; append_path_component(result, L"/.config/fish"); if (!create_directory(result)) { done = 1; } } } if (! done) { /* Bad juju */ debug(0, _(L"Unable to create a configuration directory for fish. Your personal settings will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory where the current user has write access.")); result.clear(); } return result; }
static void path_create(wcstring &path, const wcstring &xdg_var, const wcstring &which_dir, const wcstring &custom_error_msg) { bool path_done = false; bool using_xdg = false; int saved_errno = 0; // The vars we fetch must be exported. Allowing them to be universal doesn't make sense and // allowing that creates a lock inversion that deadlocks the shell since we're called before // uvars are available. const env_var_t xdg_dir = env_get_string(xdg_var, ENV_GLOBAL | ENV_EXPORT); if (!xdg_dir.missing_or_empty()) { using_xdg = true; path = xdg_dir + L"/fish"; if (create_directory(path) != -1) { path_done = true; } else { saved_errno = errno; } } else { const env_var_t home = env_get_string(L"HOME", ENV_GLOBAL | ENV_EXPORT); if (!home.missing_or_empty()) { path = home + (which_dir == L"config" ? L"/.config/fish" : L"/.local/share/fish"); if (create_directory(path) != -1) { path_done = true; } else { saved_errno = errno; } } } if (!path_done) { maybe_issue_path_warning(which_dir, custom_error_msg, using_xdg, xdg_var, path, saved_errno); path.clear(); } return; }
int autoload_t::load(const wcstring &cmd, bool reload) { int res; CHECK_BLOCK(0); ASSERT_IS_MAIN_THREAD(); env_var_t path_var = env_get_string(env_var_name); /* Do we know where to look? */ if (path_var.empty()) return 0; /* Check if the lookup path has changed. If so, drop all loaded files. path_var may only be inspected on the main thread. */ if (path_var != this->last_path) { this->last_path = path_var; this->last_path_tokenized.clear(); tokenize_variable_array(this->last_path, this->last_path_tokenized); scoped_lock locker(lock); this->evict_all_nodes(); } /* Mark that we're loading this. Hang onto the iterator for fast erasing later. Note that std::set has guarantees about not invalidating iterators, so this is safe to do across the callouts below. */ typedef std::set<wcstring>::iterator set_iterator_t; std::pair<set_iterator_t, bool> insert_result = is_loading_set.insert(cmd); set_iterator_t where = insert_result.first; bool inserted = insert_result.second; /** Warn and fail on infinite recursion. It's OK to do this because this function is only called on the main thread. */ if (! inserted) { /* We failed to insert */ debug(0, _(L"Could not autoload item '%ls', it is already being autoloaded. " L"This is a circular dependency in the autoloading scripts, please remove it."), cmd.c_str()); return 1; } /* Try loading it */ res = this->locate_file_and_maybe_load_it(cmd, true, reload, this->last_path_tokenized); /* Clean up */ is_loading_set.erase(where); return res; }
/** Attempts tilde expansion of the string specified, modifying it in place. */ static void expand_home_directory(wcstring &input) { const wchar_t * const in = input.c_str(); if (in[0] == HOME_DIRECTORY) { int tilde_error = 0; size_t tail_idx; wcstring home; if (in[1] == '/' || in[1] == '\0') { /* Current users home directory */ home = env_get_string(L"HOME"); tail_idx = 1; } else { /* Some other users home directory */ const wchar_t *name_end = wcschr(in, L'/'); if (name_end) { tail_idx = name_end - in; } else { tail_idx = wcslen(in); } wcstring name_str = input.substr(1, tail_idx - 1); std::string name_cstr = wcs2string(name_str); struct passwd *userinfo = getpwnam(name_cstr.c_str()); if (userinfo == NULL) { tilde_error = 1; input[0] = L'~'; } else { home = str2wcstring(userinfo->pw_dir); } } if (! tilde_error) { input.replace(input.begin(), input.begin() + tail_idx, home); } } }
int autoload_t::load(const wcstring &cmd, bool reload) { int res; CHECK_BLOCK(0); ASSERT_IS_MAIN_THREAD(); env_var_t path_var = env_get_string(env_var_name); /* Do we know where to look? */ if (path_var.empty()) return 0; /* Check if the lookup path has changed. If so, drop all loaded files. path_var may only be inspected on the main thread. */ if (path_var != this->last_path) { this->last_path = path_var; scoped_lock locker(lock); this->evict_all_nodes(); } /** Warn and fail on infinite recursion. It's OK to do this because this function is only called on the main thread. */ if (this->is_loading(cmd)) { debug(0, _(L"Could not autoload item '%ls', it is already being autoloaded. " L"This is a circular dependency in the autoloading scripts, please remove it."), cmd.c_str()); return 1; } /* Mark that we're loading this */ is_loading_set.insert(cmd); /* Get the list of paths from which we will try to load */ std::vector<wcstring> path_list; tokenize_variable_array(path_var, path_list); /* Try loading it */ res = this->locate_file_and_maybe_load_it(cmd, true, reload, path_list); /* Clean up */ bool erased = !! is_loading_set.erase(cmd); assert(erased); return res; }
/// Push all curses/terminfo env vars into the global environment where they can be found by those /// libraries. static void handle_curses(const wchar_t *env_var_name) { debug(2, L"handle_curses() called in response to '%ls' changing", env_var_name); const env_var_t val = env_get_string(env_var_name, ENV_EXPORT); const std::string &name = wcs2string(env_var_name); const std::string &value = wcs2string(val); debug(2, L"curses var %s='%s'", name.c_str(), value.c_str()); if (val.empty()) { unsetenv(name.c_str()); } else { setenv(name.c_str(), value.c_str(), 1); } // TODO: Modify input_init() to allow calling it when the terminfo env vars are dynamically // changed. At the present time it can be called just once. Also, we should really only do this // if the TERM var is set. // input_init(); }
// Update the wait_on_escape_ms value in response to the fish_escape_delay_ms user variable being // set. void update_wait_on_escape_ms() { env_var_t escape_time_ms = env_get_string(L"fish_escape_delay_ms"); if (escape_time_ms.missing_or_empty()) { wait_on_escape_ms = WAIT_ON_ESCAPE_DEFAULT; return; } long tmp = fish_wcstol(escape_time_ms.c_str()); if (errno || tmp < 10 || tmp >= 5000) { fwprintf(stderr, L"ignoring fish_escape_delay_ms: value '%ls' " L"is not an integer or is < 10 or >= 5000 ms\n", escape_time_ms.c_str()); } else { wait_on_escape_ms = (int)tmp; } }
/// Does this look like the escape sequence for setting a screen name. static bool is_screen_name_escape_seq(const wchar_t *code, size_t *resulting_length) { bool found = false; if (code[1] == L'k') { const env_var_t term_name = env_get_string(L"TERM"); if (!term_name.missing() && string_prefixes_string(L"screen", term_name)) { const wchar_t *const screen_name_end_sentinel = L"\x1b\\"; const wchar_t *screen_name_end = wcsstr(&code[2], screen_name_end_sentinel); if (screen_name_end == NULL) { // Consider just <esc>k to be the code. *resulting_length = 2; } else { const wchar_t *escape_sequence_end = screen_name_end + wcslen(screen_name_end_sentinel); *resulting_length = escape_sequence_end - code; } found = true; } } return found; }
/** Check the X clipboard. If it has been changed, add the new clipboard contents to the fish killring. */ static void kill_check_x_buffer() { if (!has_xsel()) return; const env_var_t disp = env_get_string(L"DISPLAY"); if (! disp.missing()) { size_t i; wcstring cmd = L"xsel -t 500 -b"; wcstring new_cut_buffer=L""; wcstring_list_t list; if (exec_subshell(cmd, list, false /* do not apply exit status */) != -1) { for (i=0; i<list.size(); i++) { wcstring next_line = escape_string(list.at(i), 0); if (i > 0) new_cut_buffer += L"\\n"; new_cut_buffer += next_line; } if (new_cut_buffer.size() > 0) { /* The buffer is inserted with backslash escapes, since we don't really like tabs, newlines, etc. anyway. */ if (cut_buffer != new_cut_buffer) { cut_buffer = new_cut_buffer; kill_list.push_front(new_cut_buffer); } } } } }
/** The set builtin. Creates, updates and erases environment variables and environemnt variable arrays. */ static int builtin_set( parser_t &parser, wchar_t **argv ) { /** Variables used for parsing the argument list */ static const struct woption long_options[] = { { L"export", no_argument, 0, 'x' } , { L"global", no_argument, 0, 'g' } , { L"local", no_argument, 0, 'l' } , { L"erase", no_argument, 0, 'e' } , { L"names", no_argument, 0, 'n' } , { L"unexport", no_argument, 0, 'u' } , { L"universal", no_argument, 0, 'U' } , { L"long", no_argument, 0, 'L' } , { L"query", no_argument, 0, 'q' } , { L"help", no_argument, 0, 'h' } , { 0, 0, 0, 0 } } ; const wchar_t *short_options = L"+xglenuULqh"; int argc = builtin_count_args(argv); /* Flags to set the work mode */ int local = 0, global = 0, exportv = 0; int erase = 0, list = 0, unexport=0; int universal = 0, query=0; bool shorten_ok = true; /* Variables used for performing the actual work */ wchar_t *dest = 0; int retcode=0; int scope; int slice=0; int i; wchar_t *bad_char; /* Parse options to obtain the requested operation and the modifiers */ woptind = 0; while (1) { int c = wgetopt_long(argc, argv, short_options, long_options, 0); if (c == -1) { break; } switch(c) { case 0: break; case 'e': erase = 1; break; case 'n': list = 1; break; case 'x': exportv = 1; break; case 'l': local = 1; break; case 'g': global = 1; break; case 'u': unexport = 1; break; case 'U': universal = 1; break; case 'L': shorten_ok = false; break; case 'q': query = 1; break; case 'h': builtin_print_help( parser, argv[0], stdout_buffer ); return 0; case '?': builtin_unknown_option( parser, argv[0], argv[woptind-1] ); return 1; default: break; } } /* Ok, all arguments have been parsed, let's validate them */ /* If we are checking the existance of a variable (-q) we can not also specify scope */ if( query && (erase || list) ) { append_format(stderr_buffer, BUILTIN_ERR_COMBO, argv[0] ); builtin_print_help( parser, argv[0], stderr_buffer ); return 1; } /* We can't both list and erase varaibles */ if( erase && list ) { append_format(stderr_buffer, BUILTIN_ERR_COMBO, argv[0] ); builtin_print_help( parser, argv[0], stderr_buffer ); return 1; } /* Variables can only have one scope */ if( local + global + universal > 1 ) { append_format(stderr_buffer, BUILTIN_ERR_GLOCAL, argv[0] ); builtin_print_help( parser, argv[0], stderr_buffer ); return 1; } /* Variables can only have one export status */ if( exportv && unexport ) { append_format(stderr_buffer, BUILTIN_ERR_EXPUNEXP, argv[0] ); builtin_print_help( parser, argv[0], stderr_buffer ); return 1; } /* Calculate the scope value for variable assignement */ scope = (local ? ENV_LOCAL : 0) | (global ? ENV_GLOBAL : 0) | (exportv ? ENV_EXPORT : 0) | (unexport ? ENV_UNEXPORT : 0) | (universal ? ENV_UNIVERSAL:0) | ENV_USER; if( query ) { /* Query mode. Return the number of variables that do not exist out of the specified variables. */ int i; for( i=woptind; i<argc; i++ ) { wchar_t *arg = argv[i]; int slice=0; if( !(dest = wcsdup(arg))) { DIE_MEM(); } if( wcschr( dest, L'[' ) ) { slice = 1; *wcschr( dest, L'[' )=0; } if( slice ) { std::vector<long> indexes; wcstring_list_t result; size_t j; env_var_t dest_str = env_get_string(dest); if (! dest_str.missing()) tokenize_variable_array( dest_str, result ); if( !parse_index( indexes, arg, dest, result.size() ) ) { builtin_print_help( parser, argv[0], stderr_buffer ); retcode = 1; break; } for( j=0; j < indexes.size() ; j++ ) { long idx = indexes[j]; if( idx < 1 || (size_t)idx > result.size() ) { retcode++; } } } else { if( !env_exist( arg, scope ) ) { retcode++; } } free( dest ); } return retcode; } if( list ) { /* Maybe we should issue an error if there are any other arguments? */ print_variables(0, 0, shorten_ok, scope); return 0; } if( woptind == argc ) { /* Print values of variables */ if( erase ) { append_format(stderr_buffer, _(L"%ls: Erase needs a variable name\n"), argv[0] ); builtin_print_help( parser, argv[0], stderr_buffer ); retcode = 1; } else { print_variables( 1, 1, shorten_ok, scope ); } return retcode; } if( !(dest = wcsdup(argv[woptind]))) { DIE_MEM(); } if( wcschr( dest, L'[' ) ) { slice = 1; *wcschr( dest, L'[' )=0; } if( !wcslen( dest ) ) { free( dest ); append_format(stderr_buffer, BUILTIN_ERR_VARNAME_ZERO, argv[0] ); builtin_print_help( parser, argv[0], stderr_buffer ); return 1; } if( (bad_char = wcsvarname( dest ) ) ) { append_format(stderr_buffer, BUILTIN_ERR_VARCHAR, argv[0], *bad_char ); builtin_print_help( parser, argv[0], stderr_buffer ); free( dest ); return 1; } if( slice && erase && (scope != ENV_USER) ) { free( dest ); append_format(stderr_buffer, _(L"%ls: Can not specify scope when erasing array slice\n"), argv[0] ); builtin_print_help( parser, argv[0], stderr_buffer ); return 1; } /* set assignment can work in two modes, either using slices or using the whole array. We detect which mode is used here. */ if( slice ) { /* Slice mode */ int idx_count, val_count; wcstring_list_t values; std::vector<long> indexes; wcstring_list_t result; const env_var_t dest_str = env_get_string(dest); if (! dest_str.missing()) tokenize_variable_array( dest_str, result ); for( ; woptind<argc; woptind++ ) { if( !parse_index( indexes, argv[woptind], dest, result.size() ) ) { builtin_print_help( parser, argv[0], stderr_buffer ); retcode = 1; break; } val_count = argc-woptind-1; idx_count = indexes.size(); if( !erase ) { if( val_count < idx_count ) { append_format(stderr_buffer, _(BUILTIN_SET_ARG_COUNT), argv[0] ); builtin_print_help( parser, argv[0], stderr_buffer ); retcode=1; break; } if( val_count == idx_count ) { woptind++; break; } } } if( !retcode ) { /* Slice indexes have been calculated, do the actual work */ if( erase ) { erase_values(result, indexes); my_env_set( dest, result, scope); } else { wcstring_list_t value; // al_init(&value); while( woptind < argc ) { value.push_back( argv[woptind++] ); } if( update_values( result, indexes, value ) ) { append_format(stderr_buffer, L"%ls: ", argv[0] ); append_format(stderr_buffer, ARRAY_BOUNDS_ERR ); stderr_buffer.push_back(L'\n'); } my_env_set(dest, result, scope); // al_destroy( &value ); } } // al_foreach( &result, &free ); // al_destroy( &result ); // al_destroy(&indexes); // al_destroy(&values); } else { woptind++; /* No slicing */ if( erase ) { if( woptind != argc ) { append_format(stderr_buffer, _(L"%ls: Values cannot be specfied with erase\n"), argv[0] ); builtin_print_help( parser, argv[0], stderr_buffer ); retcode=1; } else { retcode = env_remove( dest, scope ); } } else { wcstring_list_t val; for( i=woptind; i<argc; i++ ) val.push_back(argv[i]); retcode = my_env_set( dest, val, scope ); } } free( dest ); return retcode; }