/**
   Silly function
*/
static void  builtin_complete_remove(const wcstring_list_t &cmd,
                                     const wcstring_list_t &path,
                                     const wchar_t *short_opt,
                                     const wcstring_list_t &gnu_opt,
                                     const wcstring_list_t &old_opt)
{
    for (size_t i=0; i<cmd.size(); i++)
    {
        builtin_complete_remove2(cmd.at(i).c_str(),
                                 COMMAND,
                                 short_opt,
                                 gnu_opt,
                                 old_opt);
    }

    for (size_t i=0; i<path.size(); i++)
    {
        builtin_complete_remove2(path.at(i).c_str(),
                                 PATH,
                                 short_opt,
                                 gnu_opt,
                                 old_opt);
    }

}
Esempio n. 2
0
/// Silly function.
static void builtin_complete_add2(const wchar_t *cmd, int cmd_type, const wchar_t *short_opt,
                                  const wcstring_list_t &gnu_opt, const wcstring_list_t &old_opt,
                                  int result_mode, const wchar_t *condition, const wchar_t *comp,
                                  const wchar_t *desc, int flags) {
    size_t i;
    const wchar_t *s;

    for (s = short_opt; *s; s++) {
        complete_add(cmd, cmd_type, wcstring(1, *s), option_type_short, result_mode, condition,
                     comp, desc, flags);
    }

    for (i = 0; i < gnu_opt.size(); i++) {
        complete_add(cmd, cmd_type, gnu_opt.at(i), option_type_double_long, result_mode, condition,
                     comp, desc, flags);
    }

    for (i = 0; i < old_opt.size(); i++) {
        complete_add(cmd, cmd_type, old_opt.at(i), option_type_single_long, result_mode, condition,
                     comp, desc, flags);
    }

    if (old_opt.empty() && gnu_opt.empty() && wcslen(short_opt) == 0) {
        complete_add(cmd, cmd_type, wcstring(), option_type_args_only, result_mode, condition, comp,
                     desc, flags);
    }
}
Esempio n. 3
0
static void builtin_complete_remove_cmd(const wcstring &cmd,
                                        int cmd_type,
                                        const wchar_t *short_opt,
                                        const wcstring_list_t &gnu_opt,
                                        const wcstring_list_t &old_opt)
{
    bool removed = false;
    size_t i;
    for (i=0; short_opt[i] != L'\0'; i++)
    {
        complete_remove(cmd, cmd_type, wcstring(1, short_opt[i]), option_type_short);
        removed = true;
    }
    
    for (i=0; i < old_opt.size(); i++)
    {
        complete_remove(cmd, cmd_type, old_opt.at(i), option_type_single_long);
        removed = true;
    }
    
    for (i=0; i < gnu_opt.size(); i++)
    {
        complete_remove(cmd, cmd_type, gnu_opt.at(i), option_type_double_long);
        removed = true;
    }
    
    if (! removed)
    {
        // This means that all loops were empty
        complete_remove_all(cmd, cmd_type);
    }
}
/**
   Silly function
*/
static void  builtin_complete_add(const wcstring_list_t &cmd,
                                  const wcstring_list_t &path,
                                  const wchar_t *short_opt,
                                  wcstring_list_t &gnu_opt,
                                  wcstring_list_t &old_opt,
                                  int result_mode,
                                  int authoritative,
                                  const wchar_t *condition,
                                  const wchar_t *comp,
                                  const wchar_t *desc,
                                  int flags)
{
    for (size_t i=0; i<cmd.size(); i++)
    {
        builtin_complete_add2(cmd.at(i).c_str(),
                              COMMAND,
                              short_opt,
                              gnu_opt,
                              old_opt,
                              result_mode,
                              condition,
                              comp,
                              desc,
                              flags);

        if (authoritative != -1)
        {
            complete_set_authoritative(cmd.at(i).c_str(),
                                       COMMAND,
                                       authoritative);
        }

    }

    for (size_t i=0; i<path.size(); i++)
    {
        builtin_complete_add2(path.at(i).c_str(),
                              PATH,
                              short_opt,
                              gnu_opt,
                              old_opt,
                              result_mode,
                              condition,
                              comp,
                              desc,
                              flags);

        if (authoritative != -1)
        {
            complete_set_authoritative(path.at(i).c_str(),
                                       PATH,
                                       authoritative);
        }

    }
}
Esempio n. 5
0
static void update_export_array_if_necessary(bool recalc) {
    ASSERT_IS_MAIN_THREAD();
    if (recalc && !get_proc_had_barrier()) {
        set_proc_had_barrier(true);
        env_universal_barrier();
    }

    if (has_changed_exported) {
        std::map<wcstring, wcstring> vals;

        debug(4, L"env_export_arr() recalc");

        get_exported(top, &vals);

        if (uvars()) {
            const wcstring_list_t uni = uvars()->get_names(true, false);
            for (size_t i = 0; i < uni.size(); i++) {
                const wcstring &key = uni.at(i);
                const env_var_t val = uvars()->get(key);

                if (!val.missing() && val != ENV_NULL) {
                    // Note that std::map::insert does NOT overwrite a value already in the map,
                    // which we depend on here.
                    vals.insert(std::pair<wcstring, wcstring>(key, val));
                }
            }
        }

        std::vector<std::string> local_export_buffer;
        export_func(vals, local_export_buffer);
        export_array.set(local_export_buffer);
        has_changed_exported = false;
    }
}
Esempio n. 6
0
static int update_values( wcstring_list_t &list, 
						  std::vector<long> &indexes,
						  wcstring_list_t &values ) 
{
	size_t i;

	/* Replace values where needed */
	for( i = 0; i < indexes.size(); i++ ) 
	{
		/*
		  The '- 1' below is because the indices in fish are
		  one-based, but the vector uses zero-based indices
		*/
		long ind = indexes[i] - 1;
		const wcstring newv = values[ i ];
		if( ind < 0 )
		{
			return 1;
		}
        if ( ind >= list.size() )
        {
            list.resize( ind+1 );
        }
		
//		free((void *) al_get(list, ind));
		list[ ind ] = newv; 
	}
  
	return 0;
}
Esempio n. 7
0
void function_prepare_environment(const wcstring &name, const wchar_t *const *argv,
                                  const std::map<wcstring, env_var_t> &inherited_vars) {
    // Three components of the environment:
    // 1. argv
    // 2. named arguments
    // 3. inherited variables
    env_set_argv(argv);

    const wcstring_list_t named_arguments = function_get_named_arguments(name);
    if (!named_arguments.empty()) {
        const wchar_t *const *arg;
        size_t i;
        for (i = 0, arg = argv; i < named_arguments.size(); i++) {
            env_set(named_arguments.at(i).c_str(), *arg, ENV_LOCAL | ENV_USER);

            if (*arg) arg++;
        }
    }

    for (std::map<wcstring, env_var_t>::const_iterator it = inherited_vars.begin(),
                                                       end = inherited_vars.end();
         it != end; ++it) {
        env_set(it->first, it->second.missing() ? NULL : it->second.c_str(), ENV_LOCAL | ENV_USER);
    }
}
Esempio n. 8
0
static void print_colors(io_streams_t &streams) {
    const wcstring_list_t result = rgb_color_t::named_color_names();
    size_t i;
    for (i = 0; i < result.size(); i++) {
        streams.out.append(result.at(i));
        streams.out.push_back(L'\n');
    }
}
Esempio n. 9
0
/// Print terminfo key binding names to string buffer used for standard output.
///
/// \param all if set, all terminfo key binding names will be printed. If not set, only ones that
/// are defined for this terminal are printed.
void builtin_bind_t::key_names(bool all, io_streams_t &streams) {
    const wcstring_list_t names = input_terminfo_get_names(!all);
    for (size_t i = 0; i < names.size(); i++) {
        const wcstring &name = names.at(i);

        streams.out.append_format(L"%ls\n", name.c_str());
    }
}
Esempio n. 10
0
static void print_colors(void)
{
    const wcstring_list_t result = rgb_color_t::named_color_names();
    size_t i;
    for (i=0; i < result.size(); i++)
    {
        stdout_buffer.append(result.at(i));
        stdout_buffer.push_back(L'\n');
    }
}
Esempio n. 11
0
void parse_util_set_argv( const wchar_t * const *argv, const wcstring_list_t &named_arguments )
{
	if( *argv )
	{
		const wchar_t * const *arg;
		wcstring sb;
		
		for( arg=argv; *arg; arg++ )
		{
			if( arg != argv )
			{
				sb.append(ARRAY_SEP_STR);
			}
            sb.append(*arg);
		}
			
		env_set( L"argv", sb.c_str(), ENV_LOCAL );
	}
	else
	{
		env_set( L"argv", 0, ENV_LOCAL );
	}				

	if( named_arguments.size() )
	{
		const wchar_t * const *arg;
		size_t i;
		
		for( i=0, arg=argv; i < named_arguments.size(); i++ )
		{
			env_set( named_arguments.at(i).c_str(), *arg, ENV_LOCAL );

			if( *arg )
				arg++;
		}
			
		
	}
	
}
Esempio n. 12
0
static bool run_test_test(int expected, wcstring_list_t &lst) {
    parser_t parser(PARSER_TYPE_GENERAL, true);
    size_t i, count = lst.size();
    wchar_t **argv = new wchar_t *[count+2];
    argv[0] = (wchar_t *)L"test";
    for (i=0; i < count; i++) {
        argv[i+1] = (wchar_t *)lst.at(i).c_str();
    }
    argv[i+1] = NULL;
    int result = builtin_test(parser, argv);
    delete[] argv;
    return expected == result;
}
Esempio n. 13
0
/**
   Silly function
*/
static void builtin_complete_remove3(const wchar_t *cmd,
                                     int cmd_type,
                                     wchar_t short_opt,
                                     const wcstring_list_t &long_opt)
{
    for (size_t i=0; i<long_opt.size(); i++)
    {
        complete_remove(cmd,
                        cmd_type,
                        short_opt,
                        long_opt.at(i).c_str());
    }
}
Esempio n. 14
0
void history_tests_t::test_history_races_pound_on_history()
{
    /* Called in child process to modify history */
    history_t *hist = new history_t(L"race_test");
    hist->chaos_mode = true;
    const wcstring_list_t lines = generate_history_lines(getpid());
    for (size_t idx = 0; idx < lines.size(); idx++)
    {
        const wcstring &line = lines.at(idx);
        hist->add(line);
        hist->save();
    }
    delete hist;
}
Esempio n. 15
0
/**
   Erase from a list of wcstring values at specified indexes 
*/
static void erase_values(wcstring_list_t &list, const std::vector<long> &indexes) 
{
    // Make a set of indexes.
    // This both sorts them into ascending order and removes duplicates.
    const std::set<long> indexes_set(indexes.begin(), indexes.end());
    
    // Now walk the set backwards, so we encounter larger indexes first, and remove elements at the given (1-based) indexes.
    std::set<long>::const_reverse_iterator iter;
    for (iter = indexes_set.rbegin(); iter != indexes_set.rend(); iter++) {
        long val = *iter;
        if (val > 0 && val <= list.size()) {
            // One-based indexing!
            list.erase(list.begin() + val - 1);
        }
    }
}
Esempio n. 16
0
/**
   Replace completion strings with a comp_t structure
*/
static std::vector<comp_t *> mangle_completions( wcstring_list_t &lst, const wchar_t *prefix )
{
    std::vector<comp_t *> result;
	for( size_t i=0; i<lst.size(); i++ )
	{
        wcstring &next = lst.at(i);
		size_t start, end;
        
        comp_t zerod = {};
		comp_t *comp = new comp_t(zerod);
		
		for( start=end=0; 1; end++ )
		{
			wchar_t c = next.c_str()[end];
			
			if( (c == COMPLETE_ITEM_SEP) || (c==COMPLETE_SEP) || !c)
			{
                wcstring start2 = wcstring(next, start, end - start);
                wcstring str = escape_string(start2, ESCAPE_ALL | ESCAPE_NO_QUOTED);
				comp->comp_width += my_wcswidth( str.c_str() );
				comp->comp.push_back(str);
				start = end+1;
			}

			if( c == COMPLETE_SEP )
			{
				comp->desc = next.c_str() + start;
				break;
			}	
			
			if( !c )
				break;
			
		}

		comp->comp_width  += (int)(my_wcswidth(prefix)*comp->comp.size() + 2*(comp->comp.size()-1));
		comp->desc_width = comp->desc.empty()?0:my_wcswidth( comp->desc.c_str() );
		
		comp->pref_width = comp->comp_width + comp->desc_width + (comp->desc_width?4:0);
		
        result.push_back(comp);
	}
	
	recalc_width( result, prefix );
    return result;
}
Esempio n. 17
0
/**
   Merge multiple completions with the same description to the same line
*/
static void join_completions( wcstring_list_t lst )
{
    std::map<wcstring, long> desc_table;

	for( size_t i=0; i<lst.size(); i++ )
	{
		const wchar_t *item = lst.at(i).c_str();
		const wchar_t *desc = wcschr( item, COMPLETE_SEP );
		long prev_idx;
		
		if( !desc )
			continue;
		desc++;
        prev_idx = desc_table[desc] - 1;
		if( prev_idx == -1 )
		{
            desc_table[desc] = (long)(i+1);
		}
		else
		{
			const wchar_t *old = lst.at(i).c_str();
			const wchar_t *old_end = wcschr( old, COMPLETE_SEP );
			
			if( old_end )
			{
				
                wcstring foo;
                foo.append(old, old_end - old);
                foo.push_back(COMPLETE_ITEM_SEP);
                foo.append(item);
                
                lst.at(prev_idx) = foo;
                lst.at(i).clear();
			}
			
		}
		
	}	

    /* Remove empty strings */
    lst.erase(remove(lst.begin(), lst.end(), wcstring()), lst.end());
}
Esempio n. 18
0
static int update_values(wcstring_list_t &list, std::vector<long> &indexes,
                         wcstring_list_t &values) {
    // Replace values where needed.
    for (size_t i = 0; i < indexes.size(); i++) {
        // The '- 1' below is because the indices in fish are one-based, but the vector uses
        // zero-based indices.
        long ind = indexes[i] - 1;
        const wcstring newv = values[i];
        if (ind < 0) {
            return 1;
        }
        if ((size_t)ind >= list.size()) {
            list.resize(ind + 1);
        }

        list[ind] = newv;
    }

    return 0;
}
Esempio n. 19
0
/**
   Substitute any series of whitespace with a single space character
   inside completion descriptions. Remove all whitespace from
   beginning/end of completion descriptions.
*/
static void mangle_descriptions( wcstring_list_t &lst )
{
	int skip;
	for( size_t i=0; i<lst.size(); i++ )
	{
        wcstring &next = lst.at(i);
		size_t in, out;
		skip=1;
		
        size_t next_idx = 0;
		while( next_idx < next.size() && next[next_idx] != COMPLETE_SEP )
			next_idx++;
		
		if( next_idx == next.size() )
			continue;
		
		in=out=next_idx + 1;
		
		while( in < next.size() )
		{
			if( next[in] == L' ' || next[in]==L'\t' || next[in]<32 )
			{
				if( !skip )
					next[out++]=L' ';
				skip=1;					
			}
			else
			{
				next[out++] = next[in];					
				skip=0;
			}
			in++;
		}
        next.resize(out);
	}
}
Esempio n. 20
0
/**
   Call env_set. If this is a path variable, e.g. PATH, validate the
   elements. On error, print a description of the problem to stderr.
*/
static int my_env_set(const wchar_t *key, const wcstring_list_t &val, int scope, io_streams_t &streams)
{
    size_t i;
    int retcode = 0;
    const wchar_t *val_str=NULL;

    if (is_path_variable(key))
    {
        /* Fix for https://github.com/fish-shell/fish-shell/issues/199 . Return success if any path setting succeeds. */
        bool any_success = false;

        /* Don't bother validating (or complaining about) values that are already present.
           When determining already-present values, use ENV_DEFAULT instead of the passed-in scope because in:
              set -l PATH stuff $PATH
           where we are temporarily shadowing a variable, we want to compare against the shadowed value, not the
           (missing) local value.
           Also don't bother to complain about relative paths, which don't start with /.
        */
        wcstring_list_t existing_values;
        const env_var_t existing_variable = env_get_string(key, ENV_DEFAULT);
        if (! existing_variable.missing_or_empty())
            tokenize_variable_array(existing_variable, existing_values);

        for (i=0; i< val.size() ; i++)
        {
            const wcstring &dir = val.at(i);
            if (!string_prefixes_string(L"/", dir) || list_contains_string(existing_values, dir))
            {
                any_success = true;
                continue;
            }

            bool show_perror = false;
            int show_hint = 0;
            bool error = false;

            struct stat buff;
            if (wstat(dir, &buff))
            {
                error = true;
                show_perror = true;
            }

            if (!(S_ISDIR(buff.st_mode)))
            {
                error = true;
            }

            if (!error)
            {
                any_success = true;
            }
            else
            {
                streams.err.append_format(_(BUILTIN_SET_PATH_ERROR), L"set", dir.c_str(), key);
                const wchar_t *colon = wcschr(dir.c_str(), L':');

                if (colon && *(colon+1))
                {
                    show_hint = 1;
                }

            }

            if (show_perror)
            {
                builtin_wperror(L"set", streams);
            }

            if (show_hint)
            {
                streams.err.append_format(_(BUILTIN_SET_PATH_HINT), L"set", key, key, wcschr(dir.c_str(), L':')+1);
            }

        }

        /* Fail at setting the path if we tried to set it to something non-empty, but it wound up empty. */
        if (! val.empty() && ! any_success)
        {
            return 1;
        }

    }

    wcstring sb;
    if (! val.empty())
    {
        for (i=0; i< val.size() ; i++)
        {
            sb.append(val[i]);
            if (i<val.size() - 1)
            {
                sb.append(ARRAY_SEP_STR);
            }
        }
        val_str = sb.c_str();
    }

    switch (env_set(key, val_str, scope | ENV_USER))
    {
        case ENV_PERM:
        {
            streams.err.append_format(_(L"%ls: Tried to change the read-only variable '%ls'\n"), L"set", key);
            retcode=1;
            break;
        }

        case ENV_SCOPE:
        {
            streams.err.append_format(_(L"%ls: Tried to set the special variable '%ls' with the wrong scope\n"), L"set", key);
            retcode=1;
            break;
        }

        case ENV_INVALID:
        {
            streams.err.append_format(_(L"%ls: Tried to set the special variable '%ls' to an invalid value\n"), L"set", key);
            retcode=1;
            break;
        }
    }

    return retcode;
}
Esempio n. 21
0
/**
   Call env_set. If this is a path variable, e.g. PATH, validate the
   elements. On error, print a description of the problem to stderr.
*/
static int my_env_set( const wchar_t *key, wcstring_list_t &val, int scope )
{
	size_t i;
	int retcode = 0;
	const wchar_t *val_str=NULL;
		
	if( is_path_variable( key ) )
	{
		int error = 0;
		
		for( i=0; i< val.size() ; i++ )
		{
			int show_perror = 0;
			int show_hint = 0;
			
			struct stat buff;
			const wchar_t *dir = val[i].c_str();
			
			if( wstat( dir, &buff ) )
			{
				error = 1;
				show_perror = 1;
			}

			if( !( S_ISDIR(buff.st_mode) ) )
			{
				error = 1;
				
			}
			
			if( error )
			{
				const wchar_t *colon;
                append_format(stderr_buffer, _(BUILTIN_SET_PATH_ERROR), L"set", dir, key);
				colon = wcschr( dir, L':' );
				
				if( colon && *(colon+1) ) 
				{
					show_hint = 1;
				}
				
			}
			
			if( show_perror )
			{
				builtin_wperror( L"set" );
			}
			
			if( show_hint )
			{
                append_format(stderr_buffer, _(BUILTIN_SET_PATH_HINT), L"set", key, key, wcschr( dir, L':' )+1);
			}
			
			if( error )
			{
				break;
			}
			
		}

		if( error )
		{
			return 1;
		}
		
	}

	wcstring sb;
	if(  val.size() )
	{
		for( i=0; i< val.size() ; i++ )
		{
            sb.append(val[i]);
			if( i<val.size() - 1 )
			{
				sb.append( ARRAY_SEP_STR );
			}
		}
		val_str = sb.c_str();
	}
	
	switch( env_set( key, val_str, scope | ENV_USER ) )
	{
		case ENV_PERM:
		{
            append_format(stderr_buffer, _(L"%ls: Tried to change the read-only variable '%ls'\n"), L"set", key);
			retcode=1;
			break;
		}
		
		case ENV_INVALID:
		{
			append_format(stderr_buffer, _(L"%ls: Unknown error"), L"set" );
			retcode=1;
			break;
		}
	}

	return retcode;
}
Esempio n. 22
0
/**
   Call env_set. If this is a path variable, e.g. PATH, validate the
   elements. On error, print a description of the problem to stderr.
*/
static int my_env_set(const wchar_t *key, const wcstring_list_t &val, int scope)
{
    size_t i;
    int retcode = 0;
    const wchar_t *val_str=NULL;

    if (is_path_variable(key))
    {
        /* Fix for https://github.com/fish-shell/fish-shell/issues/199 . Return success if any path setting succeeds. */
        bool any_success = false;

        /* Don't bother validating (or complaining about) values that are already present */
        wcstring_list_t existing_values;
        const env_var_t existing_variable = env_get_string(key, scope);
        if (! existing_variable.missing_or_empty())
            tokenize_variable_array(existing_variable, existing_values);

        for (i=0; i< val.size() ; i++)
        {
            const wcstring &dir = val.at(i);
            if (list_contains_string(existing_values, dir))
            {
                any_success = true;
                continue;
            }

            bool show_perror = false;
            int show_hint = 0;
            bool error = false;

            struct stat buff;
            if (wstat(dir, &buff))
            {
                error = true;
                show_perror = true;
            }

            if (!(S_ISDIR(buff.st_mode)))
            {
                error = true;
            }

            if (!error)
            {
                any_success = true;
            }
            else
            {
                append_format(stderr_buffer, _(BUILTIN_SET_PATH_ERROR), L"set", dir.c_str(), key);
                const wchar_t *colon = wcschr(dir.c_str(), L':');

                if (colon && *(colon+1))
                {
                    show_hint = 1;
                }

            }

            if (show_perror)
            {
                builtin_wperror(L"set");
            }

            if (show_hint)
            {
                append_format(stderr_buffer, _(BUILTIN_SET_PATH_HINT), L"set", key, key, wcschr(dir.c_str(), L':')+1);
            }

        }

        /* Fail at setting the path if we tried to set it to something non-empty, but it wound up empty. */
        if (! val.empty() && ! any_success)
        {
            return 1;
        }

    }

    wcstring sb;
    if (! val.empty())
    {
        for (i=0; i< val.size() ; i++)
        {
            sb.append(val[i]);
            if (i<val.size() - 1)
            {
                sb.append(ARRAY_SEP_STR);
            }
        }
        val_str = sb.c_str();
    }

    switch (env_set(key, val_str, scope | ENV_USER))
    {
        case ENV_PERM:
        {
            append_format(stderr_buffer, _(L"%ls: Tried to change the read-only variable '%ls'\n"), L"set", key);
            retcode=1;
            break;
        }

        case ENV_SCOPE:
        {
            append_format(stderr_buffer, _(L"%ls: Tried to set the special variable '%ls' with the wrong scope\n"), L"set", key);
            retcode=1;
            break;
        }

        case ENV_INVALID:
        {
            append_format(stderr_buffer, _(L"%ls: Tried to set the special variable '%ls' to an invalid value\n"), L"set", key);
            retcode=1;
            break;
        }
    }

    return retcode;
}
Esempio n. 23
0
/// The status builtin. Gives various status information on fish.
int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
    wchar_t *cmd = argv[0];
    int argc = builtin_count_args(argv);
    status_cmd_opts_t opts;

    int optind;
    int retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams);
    if (retval != STATUS_CMD_OK) return retval;

    if (opts.print_help) {
        builtin_print_help(parser, streams, cmd, streams.out);
        return STATUS_CMD_OK;
    }

    // If a status command hasn't already been specified via a flag check the first word.
    // Note that this can be simplified after we eliminate allowing subcommands as flags.
    if (optind < argc) {
        status_cmd_t subcmd = str_to_enum(argv[optind], status_enum_map, status_enum_map_len);
        if (subcmd != STATUS_UNDEF) {
            if (!set_status_cmd(cmd, opts, subcmd, streams)) {
                return STATUS_CMD_ERROR;
            }
            optind++;
        } else {
            streams.err.append_format(BUILTIN_ERR_INVALID_SUBCMD, cmd, argv[1]);
            return STATUS_INVALID_ARGS;
        }
    }

    // Every argument that we haven't consumed already is an argument for a subcommand.
    const wcstring_list_t args(argv + optind, argv + argc);

    switch (opts.status_cmd) {
        case STATUS_UNDEF: {
            CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
            if (is_login) {
                streams.out.append_format(_(L"This is a login shell\n"));
            } else {
                streams.out.append_format(_(L"This is not a login shell\n"));
            }

            streams.out.append_format(
                _(L"Job control: %ls\n"),
                job_control_mode == JOB_CONTROL_INTERACTIVE
                    ? _(L"Only on interactive jobs")
                    : (job_control_mode == JOB_CONTROL_NONE ? _(L"Never") : _(L"Always")));
            streams.out.append(parser.stack_trace());
            break;
        }
        case STATUS_SET_JOB_CONTROL: {
            if (opts.new_job_control_mode != -1) {
                // Flag form was used.
                CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
            } else {
                if (args.size() != 1) {
                    const wchar_t *subcmd_str = enum_to_str(opts.status_cmd, status_enum_map);
                    streams.err.append_format(BUILTIN_ERR_ARG_COUNT2, cmd, subcmd_str, 1,
                                              args.size());
                    return STATUS_INVALID_ARGS;
                }
                opts.new_job_control_mode = job_control_str_to_mode(args[0].c_str(), cmd, streams);
                if (opts.new_job_control_mode == -1) {
                    return STATUS_CMD_ERROR;
                }
            }
            job_control_mode = opts.new_job_control_mode;
            break;
        }
        case STATUS_FEATURES: {
            print_features(streams);
            break;
        }
        case STATUS_TEST_FEATURE: {
            if (args.size() != 1) {
                const wchar_t *subcmd_str = enum_to_str(opts.status_cmd, status_enum_map);
                streams.err.append_format(BUILTIN_ERR_ARG_COUNT2, cmd, subcmd_str, 1, args.size());
                return STATUS_INVALID_ARGS;
            }
            const auto *metadata = features_t::metadata_for(args.front().c_str());
            if (!metadata) {
                retval = TEST_FEATURE_NOT_RECOGNIZED;
            } else {
                retval = feature_test(metadata->flag) ? TEST_FEATURE_ON : TEST_FEATURE_OFF;
            }
            break;
        }
        case STATUS_FILENAME: {
            CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
            const wchar_t *fn = parser.current_filename();

            if (!fn) fn = _(L"Standard input");
            streams.out.append_format(L"%ls\n", fn);
            break;
        }
        case STATUS_FUNCTION: {
            CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
            const wchar_t *fn = parser.get_function_name(opts.level);

            if (!fn) fn = _(L"Not a function");
            streams.out.append_format(L"%ls\n", fn);
            break;
        }
        case STATUS_LINE_NUMBER: {
            CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
            // TBD is how to interpret the level argument when fetching the line number.
            // See issue #4161.
            // streams.out.append_format(L"%d\n", parser.get_lineno(opts.level));
            streams.out.append_format(L"%d\n", parser.get_lineno());
            break;
        }
        case STATUS_IS_INTERACTIVE: {
            CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
            retval = !is_interactive_session;
            break;
        }
        case STATUS_IS_COMMAND_SUB: {
            CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
            retval = !is_subshell;
            break;
        }
        case STATUS_IS_BLOCK: {
            CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
            retval = !is_block;
            break;
        }
        case STATUS_IS_BREAKPOINT: {
            CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
            retval = !is_breakpoint;
            break;
        }
        case STATUS_IS_LOGIN: {
            CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
            retval = !is_login;
            break;
        }
        case STATUS_IS_FULL_JOB_CTRL: {
            CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
            retval = job_control_mode != JOB_CONTROL_ALL;
            break;
        }
        case STATUS_IS_INTERACTIVE_JOB_CTRL: {
            CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
            retval = job_control_mode != JOB_CONTROL_INTERACTIVE;
            break;
        }
        case STATUS_IS_NO_JOB_CTRL: {
            CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
            retval = job_control_mode != JOB_CONTROL_NONE;
            break;
        }
        case STATUS_STACK_TRACE: {
            CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
            streams.out.append(parser.stack_trace());
            break;
        }
        case STATUS_CURRENT_CMD: {
            CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
            // HACK: Go via the deprecated variable to get the command.
            const auto var = env_get(L"_");
            if (!var.missing_or_empty()) {
                streams.out.append(var->as_string());
                streams.out.push_back(L'\n');
            } else {
                streams.out.append(program_name);
                streams.out.push_back(L'\n');
            }
            break;
        }
        case STATUS_FISH_PATH: {
            CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
            streams.out.append(str2wcstring(get_executable_path("fish")));
            streams.out.push_back(L'\n');
            break;
        }
    }

    return retval;
}
Esempio n. 24
0
/**
   This internal helper function does all the real work. By using two
   functions, the internal function can return on various places in
   the code, and the caller can take care of various cleanup work.

     cmd: the command name ('grep')
     really_load: whether to actually parse it as a function, or just check it it exists
     reload: whether to reload it if it's already loaded
     path_list: the set of paths to check

     Result: if really_load is true, returns whether the function was loaded. Otherwise returns whether the function existed.
*/
bool autoload_t::locate_file_and_maybe_load_it(const wcstring &cmd, bool really_load, bool reload, const wcstring_list_t &path_list)
{
    /* Note that we are NOT locked in this function! */
    bool reloaded = 0;

    /* Try using a cached function. If we really want the function to be loaded, require that it be really loaded. If we're not reloading, allow stale functions. */
    {
        bool allow_stale_functions = ! reload;

        /* Take a lock */
        scoped_lock locker(lock);

        /* Get the function */
        autoload_function_t * func = this->get_node(cmd);

        /* Determine if we can use this cached function */
        bool use_cached;
        if (! func)
        {
            /* Can't use a function that doesn't exist */
            use_cached = false;
        }
        else if (really_load && ! func->is_placeholder && ! func->is_loaded)
        {
            /* Can't use an unloaded function */
            use_cached = false;
        }
        else if (! allow_stale_functions && is_stale(func))
        {
            /* Can't use a stale function */
            use_cached = false;
        }
        else
        {
            /* I guess we can use it */
            use_cached = true;
        }

        /* If we can use this function, return whether we were able to access it */
        if (use_cached)
        {
            return func->is_internalized || func->access.accessible;
        }
    }
    /* The source of the script will end up here */
    wcstring script_source;
    bool has_script_source = false;

    /* Whether we found an accessible file */
    bool found_file = false;

    /* Look for built-in scripts via a binary search */
    const builtin_script_t *matching_builtin_script = NULL;
    if (builtin_script_count > 0)
    {
        const builtin_script_t test_script = {cmd.c_str(), NULL};
        const builtin_script_t *array_end = builtin_scripts + builtin_script_count;
        const builtin_script_t *found = std::lower_bound(builtin_scripts, array_end, test_script, script_name_precedes_script_name);
        if (found != array_end && ! wcscmp(found->name, test_script.name))
        {
            /* We found it */
            matching_builtin_script = found;
        }
    }
    if (matching_builtin_script)
    {
        has_script_source = true;
        script_source = str2wcstring(matching_builtin_script->def);

        /* Make a node representing this function */
        scoped_lock locker(lock);
        autoload_function_t *func = this->get_autoloaded_function_with_creation(cmd, really_load);

        /* This function is internalized */
        func->is_internalized = true;

        /* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */
        if (really_load) func->is_loaded = true;
    }

    if (! has_script_source)
    {
        /* Iterate over path searching for suitable completion files */
        for (size_t i=0; i<path_list.size(); i++)
        {
            wcstring next = path_list.at(i);
            wcstring path = next + L"/" + cmd + L".fish";

            const file_access_attempt_t access = access_file(path, R_OK);
            if (access.accessible)
            {
                /* Found it! */
                found_file = true;

                /* Now we're actually going to take the lock. */
                scoped_lock locker(lock);
                autoload_function_t *func = this->get_node(cmd);

                /* Generate the source if we need to load it */
                bool need_to_load_function = really_load && (func == NULL || func->access.mod_time != access.mod_time || ! func->is_loaded);
                if (need_to_load_function)
                {

                    /* Generate the script source */
                    wcstring esc = escape_string(path, 1);
                    script_source = L". " + esc;
                    has_script_source = true;

                    /* Remove any loaded command because we are going to reload it. Note that this will deadlock if command_removed calls back into us. */
                    if (func && func->is_loaded)
                    {
                        command_removed(cmd);
                        func->is_placeholder = false;
                    }

                    /* Mark that we're reloading it */
                    reloaded = true;
                }

                /* Create the function if we haven't yet. This does not load it. Do not trigger eviction unless we are actually loading, because we don't want to evict off of the main thread. */
                if (! func)
                {
                    func = get_autoloaded_function_with_creation(cmd, really_load);
                }

                /* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */
                if (need_to_load_function) func->is_loaded = true;

                /* Unconditionally record our access time */
                func->access = access;

                break;
            }
        }

        /*
          If no file or builtin script was found we insert a placeholder function.
          Later we only research if the current time is at least five seconds later.
          This way, the files won't be searched over and over again.
        */
        if (! found_file && ! has_script_source)
        {
            scoped_lock locker(lock);
            /* Generate a placeholder */
            autoload_function_t *func = this->get_node(cmd);
            if (! func)
            {
                func = new autoload_function_t(cmd);
                func->is_placeholder = true;
                if (really_load)
                {
                    this->add_node(func);
                }
                else
                {
                    this->add_node_without_eviction(func);
                }
            }
            func->access.last_checked = time(NULL);
        }
    }

    /* If we have a script, either built-in or a file source, then run it */
    if (really_load && has_script_source)
    {
        if (exec_subshell(script_source, false /* do not apply exit status */) == -1)
        {
            /* Do nothing on failure */
        }

    }

    if (really_load)
    {
        return reloaded;
    }
    else
    {
        return found_file || has_script_source;
    }
}
Esempio n. 25
0
/* Tests whether the specified string cpath is the prefix of anything we could cd to. directories is a list of possible parent directories (typically either the working directory, or the cdpath). This does I/O!

   We expect the path to already be unescaped.
*/
bool is_potential_path(const wcstring &const_path, const wcstring_list_t &directories, path_flags_t flags, wcstring *out_path)
{
    ASSERT_IS_BACKGROUND_THREAD();
    
    const bool require_dir = !! (flags & PATH_REQUIRE_DIR);
    wcstring clean_path;
	int has_magic = 0;
	bool result = false;
    
    wcstring path(const_path);
    if (flags & PATH_EXPAND_TILDE)
        expand_tilde(path);    
    
    //	debug( 1, L"%ls -> %ls ->%ls", path, tilde, unescaped );
    
    for( size_t i=0; i < path.size(); i++)
    {
        wchar_t c = path.at(i);
        switch( c )
        {
            case PROCESS_EXPAND:
            case VARIABLE_EXPAND:
            case VARIABLE_EXPAND_SINGLE:
            case BRACKET_BEGIN:
            case BRACKET_END:
            case BRACKET_SEP:
            case ANY_CHAR:
            case ANY_STRING:
            case ANY_STRING_RECURSIVE:
            {
                has_magic = 1;
                break;		
            }
				
            case INTERNAL_SEPARATOR:
            {
                break;
            }
				
            default:
            {
                clean_path.push_back(c);
                break;
            }
				
        }
        
    }
    
    if( ! has_magic && ! clean_path.empty() )
    {
        /* Don't test the same path multiple times, which can happen if the path is absolute and the CDPATH contains multiple entries */
        std::set<wcstring> checked_paths;
        
        /* Keep a cache of which paths / filesystems are case sensitive */
        case_sensitivity_cache_t case_sensitivity_cache;
        
        for (size_t wd_idx = 0; wd_idx < directories.size() && ! result; wd_idx++) {
            const wcstring &wd = directories.at(wd_idx);
            
            const wcstring abs_path = apply_working_directory(clean_path, wd);
            
            /* Skip this if it's empty or we've already checked it */
            if (abs_path.empty() || checked_paths.count(abs_path))
                continue;
            checked_paths.insert(abs_path);
            
            /* If we end with a slash, then it must be a directory */
            bool must_be_full_dir = abs_path.at(abs_path.size()-1) == L'/';
            if (must_be_full_dir) 
            {
                struct stat buf;
                if (0 == wstat(abs_path, &buf) && S_ISDIR(buf.st_mode)) {
                    result = true;
                    /* Return the path suffix, not the whole absolute path */
                    if (out_path)
                        *out_path = clean_path;
                }
            }
            else
            {
                DIR *dir = NULL;
                
                /* We do not end with a slash; it does not have to be a directory */
                const wcstring dir_name = wdirname(abs_path);
                const wcstring base_name = wbasename(abs_path);
                if (dir_name == L"/" && base_name == L"/")
                {
                    result = true;
                    if (out_path)
                        *out_path = clean_path;
                }
                else if ((dir = wopendir(dir_name))) {
                    // We opened the dir_name; look for a string where the base name prefixes it
                    wcstring ent;
                    
                    // Check if we're case insensitive
                    bool case_insensitive = fs_is_case_insensitive(dir_name, dirfd(dir), case_sensitivity_cache);
                    
                    // Don't ask for the is_dir value unless we care, because it can cause extra filesystem acces */
                    bool is_dir = false;
                    while (wreaddir_resolving(dir, dir_name, ent, require_dir ? &is_dir : NULL))
                    {                    

                        /* Determine which function to call to check for prefixes */
                        bool (*prefix_func)(const wcstring &, const wcstring &);
                        if (case_insensitive) {
                            prefix_func = string_prefixes_string_case_insensitive;
                        } else {
                            prefix_func = string_prefixes_string;
                        }

                        if (prefix_func(base_name, ent) && (! require_dir || is_dir))
                        {
                            result = true;
                            if (out_path) {
                                /* We want to return the path in the same "form" as it was given. Take the given path, get its basename. Append that to the output if the basename actually prefixes the path (which it won't if the given path contains no slashes), and isn't a slash (so we don't duplicate slashes). Then append the directory entry. */
                                
                                out_path->clear();
                                const wcstring path_base = wdirname(const_path);
                                
                                
                                if (prefix_func(path_base, const_path)) {
                                    out_path->append(path_base);
                                    if (! string_suffixes_string(L"/", *out_path))
                                        out_path->push_back(L'/');
                                }
                                out_path->append(ent);
                                /* We actually do want a trailing / for directories, since it makes autosuggestion a bit nicer */
                                if (is_dir)
                                    out_path->push_back(L'/');
                            }
                            break;
                        }
                    }
                    closedir(dir);
                }
            }
        }
    }
    return result;
}
Esempio n. 26
0
/**
   Silly function
*/
static void  builtin_complete_add2(const wchar_t *cmd,
                                   int cmd_type,
                                   const wchar_t *short_opt,
                                   const wcstring_list_t &gnu_opt,
                                   const wcstring_list_t &old_opt,
                                   int result_mode,
                                   const wchar_t *condition,
                                   const wchar_t *comp,
                                   const wchar_t *desc,
                                   int flags)
{
    size_t i;
    const wchar_t *s;

    for (s=short_opt; *s; s++)
    {
        complete_add(cmd,
                     cmd_type,
                     *s,
                     0,
                     0,
                     result_mode,
                     condition,
                     comp,
                     desc,
                     flags);
    }

    for (i=0; i<gnu_opt.size(); i++)
    {
        complete_add(cmd,
                     cmd_type,
                     0,
                     gnu_opt.at(i).c_str(),
                     0,
                     result_mode,
                     condition,
                     comp,
                     desc,
                     flags);
    }

    for (i=0; i<old_opt.size(); i++)
    {
        complete_add(cmd,
                     cmd_type,
                     0,
                     old_opt.at(i).c_str(),
                     1,
                     result_mode,
                     condition,
                     comp,
                     desc,
                     flags);
    }

    if (old_opt.empty() && gnu_opt.empty() && wcslen(short_opt) == 0)
    {
        complete_add(cmd,
                     cmd_type,
                     0,
                     0,
                     0,
                     result_mode,
                     condition,
                     comp,
                     desc,
                     flags);
    }
}
Esempio n. 27
0
/**
   Call env_set. If this is a path variable, e.g. PATH, validate the
   elements. On error, print a description of the problem to stderr.
*/
static int my_env_set( const wchar_t *key, const wcstring_list_t &val, int scope )
{
    size_t i;
    int retcode = 0;
    const wchar_t *val_str=NULL;
    
    if( is_path_variable( key ) )
    {
        /* Fix for https://github.com/fish-shell/fish-shell/issues/199 . Return success if any path setting succeeds. */
        bool any_success = false, any_error = false;
        
        for( i=0; i< val.size() ; i++ )
        {
            bool show_perror = false;
            int show_hint = 0;
            bool error = false;
            
            struct stat buff;
            const wchar_t *dir = val[i].c_str();
            
            if( wstat( dir, &buff ) )
            {
                error = true;
                show_perror = true;
            }
            
            if( !( S_ISDIR(buff.st_mode) ) )
            {
                error = true;
            }
            
            if( !error )
            {
                any_success = true;
            }
            else
            {
                any_error = true;
                const wchar_t *colon;
                append_format(stderr_buffer, _(BUILTIN_SET_PATH_ERROR), L"set", dir, key);
                colon = wcschr( dir, L':' );
                
                if( colon && *(colon+1) ) 
                {
                    show_hint = 1;
                }
                
            }
            
            if( show_perror )
            {
                builtin_wperror( L"set" );
            }
            
            if( show_hint )
            {
                append_format(stderr_buffer, _(BUILTIN_SET_PATH_HINT), L"set", key, key, wcschr( dir, L':' )+1);
            }
            
        }
        
        /* Fail at setting the path if we tried to set it to something non-empty, but it wound up empty. */
        if( ! val.empty() && ! any_success )
        {
            return 1;
        }
        
    }
    
    wcstring sb;
    if(  val.size() )
    {
        for( i=0; i< val.size() ; i++ )
        {
            sb.append(val[i]);
            if( i<val.size() - 1 )
            {
                sb.append( ARRAY_SEP_STR );
            }
        }
        val_str = sb.c_str();
    }
    
    switch( env_set( key, val_str, scope | ENV_USER ) )
    {
        case ENV_PERM:
        {
            append_format(stderr_buffer, _(L"%ls: Tried to change the read-only variable '%ls'\n"), L"set", key);
            retcode=1;
            break;
        }
            
        case ENV_INVALID:
        {
            append_format(stderr_buffer, _(L"%ls: Unknown error"), L"set" );
            retcode=1;
            break;
        }
    }
    
    return retcode;
}