Ejemplo n.º 1
0
wcstring parser_t::current_line()
{
    if (execution_contexts.empty())
    {
        return wcstring();
    }
    const parse_execution_context_t *context = execution_contexts.back();
    assert(context != NULL);

    int source_offset = context->get_current_source_offset();
    if (source_offset < 0)
    {
        return wcstring();
    }

    const int lineno = this->get_lineno();
    const wchar_t *file = this->current_filename();

    wcstring prefix;

    /* If we are not going to print a stack trace, at least print the line number and filename */
    if (!get_is_interactive() || is_function())
    {
        if (file)
        {
            append_format(prefix, _(L"%ls (line %d): "), user_presentable_path(file).c_str(), lineno);
        }
        else if (is_within_fish_initialization)
        {
            append_format(prefix, L"%ls: ", _(L"Startup"), lineno);
        }
        else
        {
            append_format(prefix, L"%ls: ", _(L"Standard input"), lineno);
        }
    }

    bool is_interactive = get_is_interactive();
    bool skip_caret = is_interactive && ! is_function();

    /* Use an error with empty text */
    assert(source_offset >= 0);
    parse_error_t empty_error = {};
    empty_error.source_start = source_offset;

    wcstring line_info = empty_error.describe_with_prefix(context->get_source(), prefix, is_interactive, skip_caret);
    if (! line_info.empty())
    {
        line_info.push_back(L'\n');
    }

    parser_t::stack_trace(0, line_info);
    return line_info;
}
Ejemplo n.º 2
0
/**
   Set the new value of the specified resource limit. This function
   does _not_ multiply the limit value by the multiplier constant used
   by the commandline ulimit.
*/
static int set(int resource, int hard, int soft, rlim_t value)
{
    struct rlimit ls;
    getrlimit(resource, &ls);

    if (hard)
    {
        ls.rlim_max = value;
    }

    if (soft)
    {
        ls.rlim_cur = value;

        /*
          Do not attempt to set the soft limit higher than the hard limit
        */
        if ((value == RLIM_INFINITY && ls.rlim_max != RLIM_INFINITY) ||
                (value != RLIM_INFINITY && ls.rlim_max != RLIM_INFINITY && value > ls.rlim_max))
        {
            ls.rlim_cur = ls.rlim_max;
        }
    }

    if (setrlimit(resource, &ls))
    {
        if (errno == EPERM)
            append_format(stderr_buffer, L"ulimit: Permission denied when changing resource of type '%ls'\n", get_desc(resource));
        else
            builtin_wperror(L"ulimit");
        return 1;
    }
    return 0;
}
Ejemplo n.º 3
0
/**
   Print the value of the specified resource limit
*/
static void print(int resource, int hard)
{
    rlim_t l = get(resource, hard);

    if (l == RLIM_INFINITY)
        stdout_buffer.append(L"unlimited\n");
    else
        append_format(stdout_buffer, L"%d\n", l / get_multiplier(resource));

}
Ejemplo n.º 4
0
static std::string html_colorize(const wcstring &text,
                                 const std::vector<highlight_spec_t> &colors) {
    if (text.empty()) {
        return "";
    }

    assert(colors.size() == text.size());
    wcstring html = L"<pre><code>";
    highlight_spec_t last_color = highlight_spec_normal;
    for (size_t i = 0; i < text.size(); i++) {
        // Handle colors.
        highlight_spec_t color = colors.at(i);
        if (i > 0 && color != last_color) {
            html.append(L"</span>");
        }
        if (i == 0 || color != last_color) {
            append_format(html, L"<span class=\"%ls\">", html_class_name_for_color(color));
        }
        last_color = color;

        // Handle text.
        wchar_t wc = text.at(i);
        switch (wc) {
            case L'&': {
                html.append(L"&amp;");
                break;
            }
            case L'\'': {
                html.append(L"&apos;");
                break;
            }
            case L'"': {
                html.append(L"&quot;");
                break;
            }
            case L'<': {
                html.append(L"&lt;");
                break;
            }
            case L'>': {
                html.append(L"&gt;");
                break;
            }
            default: {
                html.push_back(wc);
                break;
            }
        }
    }
    html.append(L"</span></code></pre>");
    return wcs2string(html);
}
Ejemplo n.º 5
0
/**
   Print values of all resource limits
*/
static void print_all(int hard)
{
    int i;
    int w=0;

    for (i=0; resource_arr[i].desc; i++)
    {
        w=maxi(w, my_wcswidth(resource_arr[i].desc));
    }

    for (i=0; resource_arr[i].desc; i++)
    {
        struct rlimit ls;
        rlim_t l;
        getrlimit(resource_arr[i].resource, &ls);
        l = hard ? ls.rlim_max:ls.rlim_cur;

        const wchar_t *unit = ((resource_arr[i].resource==RLIMIT_CPU)?L"(seconds, ":(get_multiplier(resource_arr[i].resource)==1?L"(":L"(kB, "));

        append_format(stdout_buffer,
                      L"%-*ls %10ls-%lc) ",
                      w,
                      resource_arr[i].desc,
                      unit,
                      resource_arr[i].switch_char);

        if (l == RLIM_INFINITY)
        {
            stdout_buffer.append(L"unlimited\n");
        }
        else
        {
            append_format(stdout_buffer, L"%d\n", l/get_multiplier(resource_arr[i].resource));
        }
    }

}
Ejemplo n.º 6
0
static wcstring full_escape(const wchar_t *in)
{
    wcstring out;
    for (; *in; in++)
    {
        if (*in < 32)
        {
            append_format(out, L"\\x%.2x", *in);
        }
        else if (*in < 128)
        {
            out.push_back(*in);
        }
        else if (*in < 65536)
        {
            append_format(out, L"\\u%.4x", *in);
        }
        else
        {
            append_format(out, L"\\U%.8x", *in);
        }
    }
    return out;
}
Ejemplo n.º 7
0
/**
   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;

}
Ejemplo n.º 8
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;
}
Ejemplo n.º 9
0
/** 
	Extract indexes from a destination argument of the form name[index1 index2...]

	\param indexes the list to insert the new indexes into
	\param src the source string to parse
	\param name the name of the element. Return null if the name in \c src does not match this name
	\param var_count the number of elements in the array to parse. 

	\return the total number of indexes parsed, or -1 on error
*/
static int parse_index( std::vector<long> &indexes,
						const wchar_t *src,
						const wchar_t *name,
						int var_count )
{
	size_t len;
	
	int count = 0;
	const wchar_t *src_orig = src;
	
	if (src == 0)
	{
		return 0;
	}
	
	while (*src != L'\0' && (iswalnum(*src) || *src == L'_'))
	{
		src++;
	}
	
	if (*src != L'[')
	{
		append_format(stderr_buffer, _(BUILTIN_SET_ARG_COUNT), L"set" );					
		return 0;
	}
	
	len = src-src_orig;
	
	if( (wcsncmp( src_orig, name, len )!=0) || (wcslen(name) != (len)) )
	{
		append_format(stderr_buffer, 
				   _(L"%ls: Multiple variable names specified in single call (%ls and %.*ls)\n"),
				   L"set", 
				   name,
				   len,
				   src_orig);
		return 0;
	}

	src++;	

	while (iswspace(*src)) 
	{
		src++;
	}
	
	while (*src != L']') 
	{
		wchar_t *end;
		
		long l_ind;

		errno = 0;
		
		l_ind = wcstol(src, &end, 10);
		
		if( end==src || errno ) 
		{
			append_format(stderr_buffer, _(L"%ls: Invalid index starting at '%ls'\n"), L"set", src);
			return 0;
		}

		if( l_ind < 0 )
		{
			l_ind = var_count+l_ind+1;
		}
		
		indexes.push_back( l_ind );
		src = end;
		count++;
		while (iswspace(*src)) src++;
	}

	return count;
}
Ejemplo n.º 10
0
/**
   set_color builtin
*/
static int builtin_set_color(parser_t &parser, wchar_t **argv)
{
    /** Variables used for parsing the argument list */
    const struct woption long_options[] =
    {
        { L"background", required_argument, 0, 'b'},
        { L"help", no_argument, 0, 'h' },
        { L"bold", no_argument, 0, 'o' },
        { L"underline", no_argument, 0, 'u' },
        { L"version", no_argument, 0, 'v' },
        { L"print-colors", no_argument, 0, 'c' },
        { 0, 0, 0, 0 }
    };

    const wchar_t *short_options = L"b:hvocu";

    int argc = builtin_count_args(argv);

    /* Some code passes variables to set_color that don't exist, like $fish_user_whatever. As a hack, quietly return failure. */
    if (argc <= 1)
    {
        return EXIT_FAILURE;
    }

    const wchar_t *bgcolor = NULL;
    bool bold = false, underline=false;
    int errret;

    /* 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 'b':
                bgcolor = woptarg;
                break;

            case 'h':
                builtin_print_help(parser, argv[0], stdout_buffer);
                return STATUS_BUILTIN_OK;

            case 'o':
                bold = true;
                break;

            case 'u':
                underline = true;
                break;

            case 'c':
                print_colors();
                return STATUS_BUILTIN_OK;

            case '?':
                return STATUS_BUILTIN_ERROR;
        }
    }

    /* Remaining argument is foreground color */
    const wchar_t *fgcolor = NULL;
    if (woptind < argc)
    {
        if (woptind + 1 == argc)
        {
            fgcolor = argv[woptind];
        }
        else
        {
            append_format(stderr_buffer,
                          _(L"%ls: Too many arguments\n"),
                          argv[0]);
            return STATUS_BUILTIN_ERROR;
        }
    }

    if (fgcolor == NULL && bgcolor == NULL && !bold && !underline)
    {
        append_format(stderr_buffer,
                      _(L"%ls: Expected an argument\n"),
                      argv[0]);
        return STATUS_BUILTIN_ERROR;
    }

    const rgb_color_t fg = rgb_color_t(fgcolor ? fgcolor : L"");
    if (fgcolor && (fg.is_none() || fg.is_ignore()))
    {
        append_format(stderr_buffer, _(L"%ls: Unknown color '%ls'\n"), argv[0], fgcolor);
        return STATUS_BUILTIN_ERROR;
    }

    const rgb_color_t bg = rgb_color_t(bgcolor ? bgcolor : L"");
    if (bgcolor && (bg.is_none() || bg.is_ignore()))
    {
        append_format(stderr_buffer, _(L"%ls: Unknown color '%ls'\n"), argv[0], bgcolor);
        return STATUS_BUILTIN_ERROR;
    }

    /* Make sure that the term exists */
    if (cur_term == NULL && setupterm(0, STDOUT_FILENO, &errret) == ERR)
    {
        append_format(stderr_buffer, _(L"%ls: Could not set up terminal\n"), argv[0]);
        return STATUS_BUILTIN_ERROR;
    }

    /*
       Test if we have at least basic support for setting fonts, colors
       and related bits - otherwise just give up...
    */
    if (! exit_attribute_mode)
    {
        return STATUS_BUILTIN_ERROR;
    }


    /* Save old output function so we can restore it */
    int (* const saved_writer_func)(char) = output_get_writer();

    /* Set our output function, which writes to a std::string */
    builtin_set_color_output.clear();
    output_set_writer(set_color_builtin_outputter);

    if (bold)
    {
        if (enter_bold_mode)
            writembs(tparm(enter_bold_mode));
    }

    if (underline)
    {
        if (enter_underline_mode)
            writembs(enter_underline_mode);
    }

    if (bgcolor != NULL)
    {
        if (bg.is_normal())
        {
            write_color(rgb_color_t::black(), false /* not is_fg */);
            writembs(tparm(exit_attribute_mode));
        }
    }

    if (fgcolor != NULL)
    {
        if (fg.is_normal() || fg.is_reset())
        {
            write_color(rgb_color_t::black(), true /* is_fg */);
            writembs(tparm(exit_attribute_mode));
        }
        else
        {
            write_color(fg, true /* is_fg */);
        }
    }

    if (bgcolor != NULL)
    {
        if (! bg.is_normal() && ! bg.is_reset())
        {
            write_color(bg, false /* not is_fg */);
        }
    }

    /* Restore saved writer function */
    output_set_writer(saved_writer_func);

    /* Output the collected string */
    stdout_buffer.append(str2wcstring(builtin_set_color_output));
    builtin_set_color_output.clear();

    return STATUS_BUILTIN_OK;
}
Ejemplo n.º 11
0
/**
  Extract indexes from a destination argument of the form name[index1 index2...]

  \param indexes the list to insert the new indexes into
  \param src the source string to parse
  \param name the name of the element. Return null if the name in \c src does not match this name
  \param var_count the number of elements in the array to parse.

  \return the total number of indexes parsed, or -1 on error
*/
static int parse_index(std::vector<long> &indexes,
                       const wchar_t *src,
                       const wchar_t *name,
                       size_t var_count)
{
    size_t len;

    int count = 0;
    const wchar_t *src_orig = src;

    if (src == 0)
    {
        return 0;
    }

    while (*src != L'\0' && (iswalnum(*src) || *src == L'_'))
    {
        src++;
    }

    if (*src != L'[')
    {
        append_format(stderr_buffer, _(BUILTIN_SET_ARG_COUNT), L"set");
        return 0;
    }

    len = src-src_orig;

    if ((wcsncmp(src_orig, name, len)!=0) || (wcslen(name) != (len)))
    {
        append_format(stderr_buffer,
                      _(L"%ls: Multiple variable names specified in single call (%ls and %.*ls)\n"),
                      L"set",
                      name,
                      len,
                      src_orig);
        return 0;
    }

    src++;

    while (iswspace(*src))
    {
        src++;
    }

    while (*src != L']')
    {
        wchar_t *end;

        long l_ind;

        errno = 0;

        l_ind = wcstol(src, &end, 10);

        if (end==src || errno)
        {
            append_format(stderr_buffer, _(L"%ls: Invalid index starting at '%ls'\n"), L"set", src);
            return 0;
        }

        if (l_ind < 0)
        {
            l_ind = var_count+l_ind+1;
        }

        src = end;
        if (*src==L'.' && *(src+1)==L'.')
        {
            src+=2;
            long l_ind2 = wcstol(src, &end, 10);
            if (end==src || errno)
            {
                return 1;
            }
            src = end;

            if (l_ind2 < 0)
            {
                l_ind2 = var_count+l_ind2+1;
            }
            int direction = l_ind2<l_ind ? -1 : 1 ;
            for (long jjj = l_ind; jjj*direction <= l_ind2*direction; jjj+=direction)
            {
                // debug(0, L"Expand range [set]: %i\n", jjj);
                indexes.push_back(jjj);
                count++;
            }
        }
        else
        {
            indexes.push_back(l_ind);
            count++;
        }
        while (iswspace(*src)) src++;
    }

    return count;
}
Ejemplo n.º 12
0
/* the actual string format parser
 * Although it's not in the documentation of `printf()`, but in addition to the
 * `%d` conversion specifier, this supports `%i`, which takes an `int` argument
 * instead of a `long`. It is used only for formatting error messages (since
 * Sparkling integers are all `long`s), but feel free to use it yourself.
 *
 * if `errmsg' is not a NULL pointer, and an error occurred while creating the
 * format string, then on return, `*errmsg' will point to a string containing
 * a message that describes the error.
 */
static char *make_format_string(
	const char *fmt,
	size_t *len,
	int argc,
	void *argv,
	int isval,
	char **errmsg
)
{
	struct string_builder bld;
	int argidx = 0;
	const char *s = fmt;
	const char *p = s;	/* points to the beginning of the next
				 * non-format part of the format string
				 */

	init_builder(&bld);

	while (*s) {
		if (*s == '%') {
			struct format_args args;
			init_format_args(&args);

			/* append preceding non-format string chunk */
			if (s > p) {
				append_string(&bld, p, s - p);
			}

			s++;

			/* Actually parse the format string.
			 * '#' flag: prepend base prefix (0b, 0, 0x)
			 */
			if (*s == '#') {
				args.flags |= FLAG_BASEPREFIX;
				s++;
			}

			/* ' ' (space) flag: prepend space if non-negative
			 * '+' flag: always prepend explicit + or - sign
			 */
			if (*s == ' ') {
				args.flags |= FLAG_PADSIGN;
				s++;
			} else if (*s == '+') {
				args.flags |= FLAG_EXPLICITSIGN;
				s++;
			}

			/* leading 0 flag: pad field with zeroes */
			if (*s == '0') {
				args.flags |= FLAG_ZEROPAD;
				s++;
			}

			/* field width specifier */
			if (isdigit(*s)) {
				args.width = 0;
				while (isdigit(*s)) {
					args.width *= 10;
					args.width += *s++ - '0';
				}
			} else if (*s == '*') {
				s++;
				if (isval) {
					SpnValue *widthptr;

					/* check argc if the caller wants us to do so */
					if (argc >= 0 && argidx >= argc) {
						format_errmsg(errmsg, OUT_OF_ARGUMENTS, argidx);
						free(bld.buf);
						return NULL;
					}

					/* width specifier must be an integer */
					widthptr = getarg_val(argv, &argidx);
					if (!isnum(widthptr)) {
					 	format_errmsg(
					 		errmsg,
					 		TYPE_MISMATCH,
					 		argidx,
					 		SPN_TTAG_NUMBER,
					 		widthptr->type
					 	);
						free(bld.buf);
						return NULL;
					}

					if (isfloat(widthptr)) {
				 		format_errmsg(
				 			errmsg,
				 			EXPECT_INTEGER,
				 			argidx
				 		);
						free(bld.buf);
						return NULL;
					}

					args.width = intvalue(widthptr);
				} else {
					const int *widthptr = getarg_raw(argv, &argidx);
					args.width = *widthptr;
				}
			}

			/* precision/maximal length specifier */
			if (*s == '.') {
				s++;

				if (*s == '+') {
					args.flags |= FLAG_EXPONENTSIGN;
					s++;
				}

				args.precision = 0;
				if (isdigit(*s)) {
					while (isdigit(*s)) {
						args.precision *= 10;
						args.precision += *s++ - '0';
					}
				} else if (*s == '*') {
					s++;
					if (isval) {
						SpnValue *precptr;

						/* check argc if the caller wants us to do so */
						if (argc >= 0 && argidx >= argc) {
							format_errmsg(errmsg, OUT_OF_ARGUMENTS, argidx);
							free(bld.buf);
							return NULL;
						}

						/* precision must be an integer too */
						precptr = getarg_val(argv, &argidx);

						if (!isnum(precptr)) {
							format_errmsg(
								errmsg,
								TYPE_MISMATCH,
								argidx,
								SPN_TTAG_NUMBER,
								precptr->type
							);
							free(bld.buf);
							return NULL;
						}

						if (isfloat(precptr)) {
					 		format_errmsg(
					 			errmsg,
					 			EXPECT_INTEGER,
					 			argidx
					 		);
							free(bld.buf);
							return NULL;
						}

						args.precision = intvalue(precptr);
					} else {
						const int *precptr = getarg_raw(argv, &argidx);
						args.precision = *precptr;
					}
				}
			}

			args.spec = *s++;

			/* check argc if the caller wants us to do so */
			if (argc >= 0 && argidx >= argc) {
				format_errmsg(errmsg, OUT_OF_ARGUMENTS, argidx);
				free(bld.buf);
				return NULL;
			}

			/* append parsed format string */
			if (append_format(&bld, &args, argv, &argidx, isval, errmsg) != 0) {
				free(bld.buf);
				return NULL;
			}

			/* update non-format chunk base pointer */
			p = s;
		} else {
			s++;
		}
	}

	/* if the format string doesn't end with a conversion specifier,
	 * then just append the last non-format (literal) string chunk
	 */
	if (s > p) {
		append_string(&bld, p, s - p);
	}

	/* append terminating NUL byte */
	expand_buffer(&bld, 1);
	bld.buf[bld.len] = 0;

	if (len != NULL) {
		*len = bld.len;
	}

	return bld.buf;
}
Ejemplo n.º 13
0
/**
   The ulimit builtin, used for setting resource limits. Defined in
   builtin_ulimit.c.
*/
static int builtin_ulimit(parser_t &parser, wchar_t ** argv)
{
    int hard=0;
    int soft=0;

    int what = RLIMIT_FSIZE;
    int report_all = 0;

    int argc = builtin_count_args(argv);

    woptind=0;

    while (1)
    {
        static const struct woption
                long_options[] =
        {
            {
                L"all", no_argument, 0, 'a'
            }
            ,
            {
                L"hard", no_argument, 0, 'H'
            }
            ,
            {
                L"soft", no_argument, 0, 'S'
            }
            ,
            {
                L"core-size", no_argument, 0, 'c'
            }
            ,
            {
                L"data-size", no_argument, 0, 'd'
            }
            ,
            {
                L"file-size", no_argument, 0, 'f'
            }
            ,
            {
                L"lock-size", no_argument, 0, 'l'
            }
            ,
            {
                L"resident-set-size", no_argument, 0, 'm'
            }
            ,
            {
                L"file-descriptor-count", no_argument, 0, 'n'
            }
            ,
            {
                L"stack-size", no_argument, 0, 's'
            }
            ,
            {
                L"cpu-time", no_argument, 0, 't'
            }
            ,
            {
                L"process-count", no_argument, 0, 'u'
            }
            ,
            {
                L"virtual-memory-size", no_argument, 0, 'v'
            }
            ,
            {
                L"help", no_argument, 0, 'h'
            }
            ,
            {
                0, 0, 0, 0
            }
        }
        ;


        int opt_index = 0;

        int opt = wgetopt_long(argc,
                               argv,
                               L"aHScdflmnstuvh",
                               long_options,
                               &opt_index);
        if (opt == -1)
            break;

        switch (opt)
        {
            case 0:
                if (long_options[opt_index].flag != 0)
                    break;
                append_format(stderr_buffer,
                              BUILTIN_ERR_UNKNOWN,
                              argv[0],
                              long_options[opt_index].name);
                builtin_print_help(parser, argv[0], stderr_buffer);

                return 1;

            case L'a':
                report_all=1;
                break;

            case L'H':
                hard=1;
                break;

            case L'S':
                soft=1;
                break;

            case L'c':
                what=RLIMIT_CORE;
                break;

            case L'd':
                what=RLIMIT_DATA;
                break;

            case L'f':
                what=RLIMIT_FSIZE;
                break;
#ifdef RLIMIT_MEMLOCK
            case L'l':
                what=RLIMIT_MEMLOCK;
                break;
#endif

#ifdef RLIMIT_RSS
            case L'm':
                what=RLIMIT_RSS;
                break;
#endif

            case L'n':
                what=RLIMIT_NOFILE;
                break;

            case L's':
                what=RLIMIT_STACK;
                break;

            case L't':
                what=RLIMIT_CPU;
                break;

#ifdef RLIMIT_NPROC
            case L'u':
                what=RLIMIT_NPROC;
                break;
#endif

#ifdef RLIMIT_AS
            case L'v':
                what=RLIMIT_AS;
                break;
#endif

            case L'h':
                builtin_print_help(parser, argv[0], stdout_buffer);
                return 0;

            case L'?':
                builtin_unknown_option(parser, argv[0], argv[woptind-1]);
                return 1;
        }
    }

    if (report_all)
    {
        if (argc - woptind == 0)
        {
            print_all(hard);
        }
        else
        {
            stderr_buffer.append(argv[0]);
            stderr_buffer.append(L": Too many arguments\n");
            builtin_print_help(parser, argv[0], stderr_buffer);
            return 1;
        }

        return 0;
    }

    switch (argc - woptind)
    {
        case 0:
        {
            /*
              Show current limit value
            */
            print(what, hard);
            break;
        }

        case 1:
        {
            /*
              Change current limit value
            */
            rlim_t new_limit;
            wchar_t *end;

            /*
              Set both hard and soft limits if nothing else was specified
            */
            if (!(hard+soft))
            {
                hard=soft=1;
            }

            if (wcscasecmp(argv[woptind], L"unlimited")==0)
            {
                new_limit = RLIM_INFINITY;
            }
            else if (wcscasecmp(argv[woptind], L"hard")==0)
            {
                new_limit = get(what, 1);
            }
            else if (wcscasecmp(argv[woptind], L"soft")==0)
            {
                new_limit = get(what, soft);
            }
            else
            {
                errno=0;
                new_limit = wcstol(argv[woptind], &end, 10);
                if (errno || *end)
                {
                    append_format(stderr_buffer,
                                  L"%ls: Invalid limit '%ls'\n",
                                  argv[0],
                                  argv[woptind]);
                    builtin_print_help(parser, argv[0], stderr_buffer);
                    return 1;
                }
                new_limit *= get_multiplier(what);
            }

            return set(what, hard, soft, new_limit);
        }

        default:
        {
            stderr_buffer.append(argv[0]);
            stderr_buffer.append(L": Too many arguments\n");
            builtin_print_help(parser, argv[0], stderr_buffer);
            return 1;
        }

    }
    return 0;
}
Ejemplo n.º 14
0
wcstring block_t::description() const {
    wcstring result;
    switch (this->type()) {
        case WHILE: {
            result.append(L"while");
            break;
        }
        case FOR: {
            result.append(L"for");
            break;
        }
        case IF: {
            result.append(L"if");
            break;
        }
        case FUNCTION_CALL: {
            result.append(L"function_call");
            break;
        }
        case FUNCTION_CALL_NO_SHADOW: {
            result.append(L"function_call_no_shadow");
            break;
        }
        case SWITCH: {
            result.append(L"switch");
            break;
        }
        case SUBST: {
            result.append(L"substitution");
            break;
        }
        case TOP: {
            result.append(L"top");
            break;
        }
        case BEGIN: {
            result.append(L"begin");
            break;
        }
        case SOURCE: {
            result.append(L"source");
            break;
        }
        case EVENT: {
            result.append(L"event");
            break;
        }
        case BREAKPOINT: {
            result.append(L"breakpoint");
            break;
        }
    }

    if (this->src_lineno >= 0) {
        append_format(result, L" (line %d)", this->src_lineno);
    }
    if (this->src_filename != NULL) {
        append_format(result, L" (file %ls)", this->src_filename);
    }
    return result;
}
Ejemplo n.º 15
0
/// Return a definition of the specified function. Used by the functions builtin.
static wcstring functions_def(const wcstring &name) {
    CHECK(!name.empty(), L"");  //!OCLINT(multiple unary operator)
    wcstring out;
    wcstring desc, def;
    function_get_desc(name, desc);
    function_get_definition(name, def);
    std::vector<std::shared_ptr<event_handler_t>> ev = event_get_function_handlers(name);

    out.append(L"function ");

    // Typically we prefer to specify the function name first, e.g. "function foo --description bar"
    // But If the function name starts with a -, we'll need to output it after all the options.
    bool defer_function_name = (name.at(0) == L'-');
    if (!defer_function_name) {
        out.append(escape_string(name, true));
    }

    if (!desc.empty()) {
        wcstring esc_desc = escape_string(desc, true);
        out.append(L" --description ");
        out.append(esc_desc);
    }

    auto props = function_get_properties(name);
    assert(props && "Should have function properties");
    if (!props->shadow_scope) {
        out.append(L" --no-scope-shadowing");
    }

    for (const auto &next : ev) {
        const event_description_t &d = next->desc;
        switch (d.type) {
            case event_type_t::signal: {
                append_format(out, L" --on-signal %ls", sig2wcs(d.param1.signal));
                break;
            }
            case event_type_t::variable: {
                append_format(out, L" --on-variable %ls", d.str_param1.c_str());
                break;
            }
            case event_type_t::exit: {
                if (d.param1.pid > 0)
                    append_format(out, L" --on-process-exit %d", d.param1.pid);
                else
                    append_format(out, L" --on-job-exit %d", -d.param1.pid);
                break;
            }
            case event_type_t::job_exit: {
                const job_t *j = job_t::from_job_id(d.param1.job_id);
                if (j) append_format(out, L" --on-job-exit %d", j->pgid);
                break;
            }
            case event_type_t::generic: {
                append_format(out, L" --on-event %ls", d.str_param1.c_str());
                break;
            }
            case event_type_t::any:
            default: {
                DIE("unexpected next->type");
                break;
            }
        }
    }

    const wcstring_list_t &named = props->named_arguments;
    if (!named.empty()) {
        append_format(out, L" --argument");
        for (const auto &name : named) {
            append_format(out, L" %ls", name.c_str());
        }
    }

    // Output the function name if we deferred it.
    if (defer_function_name) {
        out.append(L" -- ");
        out.append(escape_string(name, true));
    }

    // Output any inherited variables as `set -l` lines.
    std::map<wcstring, env_var_t> inherit_vars = function_get_inherit_vars(name);
    for (const auto &kv : inherit_vars) {
        wcstring_list_t lst;
        kv.second.to_list(lst);

        // This forced tab is crummy, but we don't know what indentation style the function uses.
        append_format(out, L"\n\tset -l %ls", kv.first.c_str());
        for (const auto &arg : lst) {
            wcstring earg = escape_string(arg, ESCAPE_ALL);
            out.push_back(L' ');
            out.append(earg);
        }
    }

    // This forced tab is sort of crummy - not all functions start with a tab.
    append_format(out, L"\n\t%ls", def.c_str());

    // Append a newline before the 'end', unless there already is one there.
    if (!string_suffixes_string(L"\n", def)) {
        out.push_back(L'\n');
    }
    out.append(L"end\n");
    return out;
}
Ejemplo n.º 16
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;
}
Ejemplo n.º 17
0
/**
   The complete builtin. Used for specifying programmable
   tab-completions. Calls the functions in complete.c for any heavy
   lifting. Defined in builtin_complete.c
*/
static int builtin_complete(parser_t &parser, wchar_t **argv)
{
    ASSERT_IS_MAIN_THREAD();
    bool res=false;
    int argc=0;
    int result_mode=SHARED;
    int remove = 0;
    int authoritative = -1;

    wcstring short_opt;
    wcstring_list_t gnu_opt, old_opt;
    const wchar_t *comp=L"", *desc=L"", *condition=L"";

    bool do_complete = false;
    wcstring do_complete_param;

    wcstring_list_t cmd;
    wcstring_list_t path;
    wcstring_list_t wrap_targets;

    static int recursion_level=0;

    argc = builtin_count_args(argv);

    woptind=0;

    while (! res)
    {
        static const struct woption
                long_options[] =
        {
            { L"exclusive", no_argument, 0, 'x' },
            { L"no-files", no_argument, 0, 'f' },
            { L"require-parameter", no_argument, 0, 'r' },
            { L"path", required_argument, 0, 'p' },
            { L"command", required_argument, 0, 'c' },
            { L"short-option", required_argument, 0, 's' },
            { L"long-option", required_argument, 0, 'l' },
            { L"old-option", required_argument, 0, 'o' },
            { L"description", required_argument, 0, 'd' },
            { L"arguments", required_argument, 0, 'a' },
            { L"erase", no_argument, 0, 'e' },
            { L"unauthoritative", no_argument, 0, 'u' },
            { L"authoritative", no_argument, 0, 'A' },
            { L"condition", required_argument, 0, 'n' },
            { L"wraps", required_argument, 0, 'w' },
            { L"do-complete", optional_argument, 0, 'C' },
            { L"help", no_argument, 0, 'h' },
            { 0, 0, 0, 0 }
        };

        int opt_index = 0;

        int opt = wgetopt_long(argc,
                               argv,
                               L"a:c:p:s:l:o:d:frxeuAn:C::w:h",
                               long_options,
                               &opt_index);
        if (opt == -1)
            break;

        switch (opt)
        {
            case 0:
                if (long_options[opt_index].flag != 0)
                    break;
                append_format(stderr_buffer,
                              BUILTIN_ERR_UNKNOWN,
                              argv[0],
                              long_options[opt_index].name);
                builtin_print_help(parser, argv[0], stderr_buffer);


                res = true;
                break;

            case 'x':
                result_mode |= EXCLUSIVE;
                break;

            case 'f':
                result_mode |= NO_FILES;
                break;

            case 'r':
                result_mode |= NO_COMMON;
                break;

            case 'p':
            case 'c':
            {
                wcstring tmp;
                if (unescape_string(woptarg, &tmp, UNESCAPE_SPECIAL))
                {
                    if (opt=='p')
                        path.push_back(tmp);
                    else
                        cmd.push_back(tmp);
                }
                else
                {
                    append_format(stderr_buffer, L"%ls: Invalid token '%ls'\n", argv[0], woptarg);
                    res = true;
                }
                break;
            }

            case 'd':
                desc = woptarg;
                break;

            case 'u':
                authoritative=0;
                break;

            case 'A':
                authoritative=1;
                break;

            case 's':
                short_opt.append(woptarg);
                break;

            case 'l':
                gnu_opt.push_back(woptarg);
                break;

            case 'o':
                old_opt.push_back(woptarg);
                break;

            case 'a':
                comp = woptarg;
                break;

            case 'e':
                remove = 1;
                break;

            case 'n':
                condition = woptarg;
                break;
                
            case 'w':
                wrap_targets.push_back(woptarg);
                break;

            case 'C':
                do_complete = true;
                do_complete_param = woptarg ? woptarg : reader_get_buffer();
                break;

            case 'h':
                builtin_print_help(parser, argv[0], stdout_buffer);
                return 0;

            case '?':
                builtin_unknown_option(parser, argv[0], argv[woptind-1]);
                res = true;
                break;

        }

    }

    if (!res)
    {
        if (condition && wcslen(condition))
        {
            const wcstring condition_string = condition;
            parse_error_list_t errors;
            if (parse_util_detect_errors(condition_string, &errors, false /* do not accept incomplete */))
            {
                append_format(stderr_buffer,
                              L"%ls: Condition '%ls' contained a syntax error",
                              argv[0],
                              condition);
                for (size_t i=0; i < errors.size(); i++)
                {
                    append_format(stderr_buffer, L"\n%s: ", argv[0]);
                    stderr_buffer.append(errors.at(i).describe(condition_string));
                }
                res = true;
            }
        }
    }

    if (!res)
    {
        if (comp && wcslen(comp))
        {
            wcstring prefix;
            if (argv[0])
            {
                prefix.append(argv[0]);
                prefix.append(L": ");
            }

            wcstring err_text;
            if (parser.detect_errors_in_argument_list(comp, &err_text, prefix.c_str()))
            {
                append_format(stderr_buffer,
                              L"%ls: Completion '%ls' contained a syntax error\n",
                              argv[0],
                              comp);
                stderr_buffer.append(err_text);
                stderr_buffer.push_back(L'\n');
                res = true;
            }
        }
    }

    if (!res)
    {
        if (do_complete)
        {
            const wchar_t *token;

            parse_util_token_extent(do_complete_param.c_str(), do_complete_param.size(), &token, 0, 0, 0);
            
            /* Create a scoped transient command line, so that bulitin_commandline will see our argument, not the reader buffer */
            builtin_commandline_scoped_transient_t temp_buffer(do_complete_param);

            if (recursion_level < 1)
            {
                recursion_level++;

                std::vector<completion_t> comp;
                complete(do_complete_param, comp, COMPLETION_REQUEST_DEFAULT);

                for (size_t i=0; i< comp.size() ; i++)
                {
                    const completion_t &next =  comp.at(i);

                    /* Make a fake commandline, and then apply the completion to it.  */
                    const wcstring faux_cmdline = token;
                    size_t tmp_cursor = faux_cmdline.size();
                    wcstring faux_cmdline_with_completion = completion_apply_to_command_line(next.completion, next.flags, faux_cmdline, &tmp_cursor, false);

                    /* completion_apply_to_command_line will append a space unless COMPLETE_NO_SPACE is set. We don't want to set COMPLETE_NO_SPACE because that won't close quotes. What we want is to close the quote, but not append the space. So we just look for the space and clear it. */
                    if (!(next.flags & COMPLETE_NO_SPACE) && string_suffixes_string(L" ", faux_cmdline_with_completion))
                    {
                        faux_cmdline_with_completion.resize(faux_cmdline_with_completion.size() - 1);
                    }

                    /* The input data is meant to be something like you would have on the command line, e.g. includes backslashes. The output should be raw, i.e. unescaped. So we need to unescape the command line. See #1127 */
                    unescape_string_in_place(&faux_cmdline_with_completion, UNESCAPE_DEFAULT);
                    stdout_buffer.append(faux_cmdline_with_completion);

                    /* Append any description */
                    if (! next.description.empty())
                    {
                        stdout_buffer.push_back(L'\t');
                        stdout_buffer.append(next.description);
                    }
                    stdout_buffer.push_back(L'\n');
                }

                recursion_level--;
            }
        }
        else if (woptind != argc)
        {
            append_format(stderr_buffer,
                          _(L"%ls: Too many arguments\n"),
                          argv[0]);
            builtin_print_help(parser, argv[0], stderr_buffer);

            res = true;
        }
        else if (cmd.empty() && path.empty())
        {
            /* No arguments specified, meaning we print the definitions of
             * all specified completions to stdout.*/
            complete_print(stdout_buffer);
        }
        else
        {
            int flags = COMPLETE_AUTO_SPACE;
        
            if (remove)
            {
                builtin_complete_remove(cmd,
                                        path,
                                        short_opt.c_str(),
                                        gnu_opt,
                                        old_opt);
                
            }
            else
            {
                builtin_complete_add(cmd,
                                     path,
                                     short_opt.c_str(),
                                     gnu_opt,
                                     old_opt,
                                     result_mode,
                                     authoritative,
                                     condition,
                                     comp,
                                     desc,
                                     flags);
            }
            
            // Handle wrap targets (probably empty)
            // We only wrap commands, not paths
            for (size_t w=0; w < wrap_targets.size(); w++)
            {
                const wcstring &wrap_target = wrap_targets.at(w);
                for (size_t i=0; i < cmd.size(); i++)
                {
                    
                    (remove ? complete_remove_wrapper : complete_add_wrapper)(cmd.at(i), wrap_target);
                }
            }
        }
    }

    return res ? 1 : 0;
}
Ejemplo n.º 18
0
/**
   The commandline builtin. It is used for specifying a new value for
   the commandline.
*/
static int builtin_commandline(parser_t &parser, wchar_t **argv)
{
    wgetopter_t w;
    int buffer_part=0;
    int cut_at_cursor=0;

    int argc = builtin_count_args(argv);
    int append_mode=0;

    int function_mode = 0;
    int selection_mode = 0;

    int tokenize = 0;

    int cursor_mode = 0;
    int line_mode = 0;
    int search_mode = 0;
    int paging_mode = 0;
    const wchar_t *begin = NULL, *end = NULL;
    
    scoped_push<const wchar_t *> saved_current_buffer(&current_buffer);
    scoped_push<size_t> saved_current_cursor_pos(&current_cursor_pos);
    
    wcstring transient_commandline;
    if (get_top_transient(&transient_commandline))
    {
        current_buffer = transient_commandline.c_str();
        current_cursor_pos = transient_commandline.size();
    }
    else
    {
        current_buffer = reader_get_buffer();
        current_cursor_pos = reader_get_cursor_pos();
    }

    if (!get_buffer())
    {
        if (is_interactive_session)
        {
            /*
              Prompt change requested while we don't have
              a prompt, most probably while reading the
              init files. Just ignore it.
            */
            return 1;
        }

        stderr_buffer.append(argv[0]);
        stderr_buffer.append(L": Can not set commandline in non-interactive mode\n");
        builtin_print_help(parser, argv[0], stderr_buffer);
        return 1;
    }

    w.woptind=0;

    while (1)
    {
        static const struct woption
                long_options[] =
        {
            { L"append", no_argument, 0, 'a' },
            { L"insert", no_argument, 0, 'i' },
            { L"replace", no_argument, 0, 'r' },
            { L"current-job", no_argument, 0, 'j' },
            { L"current-process", no_argument, 0, 'p' },
            { L"current-token", no_argument, 0, 't' },
            { L"current-buffer", no_argument, 0, 'b' },
            { L"cut-at-cursor", no_argument, 0, 'c' },
            { L"function", no_argument, 0, 'f' },
            { L"tokenize", no_argument, 0, 'o' },
            { L"help", no_argument, 0, 'h' },
            { L"input", required_argument, 0, 'I' },
            { L"cursor", no_argument, 0, 'C' },
            { L"line", no_argument, 0, 'L' },
            { L"search-mode", no_argument, 0, 'S' },
            { L"selection", no_argument, 0, 's' },
            { L"paging-mode", no_argument, 0, 'P' },
            { 0, 0, 0, 0 }
        };

        int opt_index = 0;

        int opt = w.wgetopt_long(argc,
                                 argv,
                                 L"abijpctwforhI:CLSsP",
                                 long_options,
                                 &opt_index);
        if (opt == -1)
            break;

        switch (opt)
        {
            case 0:
                if (long_options[opt_index].flag != 0)
                    break;
                append_format(stderr_buffer,
                              BUILTIN_ERR_UNKNOWN,
                              argv[0],
                              long_options[opt_index].name);
                builtin_print_help(parser, argv[0], stderr_buffer);

                return 1;

            case L'a':
                append_mode = APPEND_MODE;
                break;

            case L'b':
                buffer_part = STRING_MODE;
                break;


            case L'i':
                append_mode = INSERT_MODE;
                break;

            case L'r':
                append_mode = REPLACE_MODE;
                break;

            case 'c':
                cut_at_cursor=1;
                break;

            case 't':
                buffer_part = TOKEN_MODE;
                break;

            case 'j':
                buffer_part = JOB_MODE;
                break;

            case 'p':
                buffer_part = PROCESS_MODE;
                break;

            case 'f':
                function_mode=1;
                break;

            case 'o':
                tokenize=1;
                break;

            case 'I':
                current_buffer = w.woptarg;
                current_cursor_pos = wcslen(w.woptarg);
                break;

            case 'C':
                cursor_mode = 1;
                break;

            case 'L':
                line_mode = 1;
                break;

            case 'S':
                search_mode = 1;
                break;

            case 's':
                selection_mode = 1;
                break;

            case 'P':
                paging_mode = 1;
                break;

            case 'h':
                builtin_print_help(parser, argv[0], stdout_buffer);
                return 0;

            case L'?':
                builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
                return 1;
        }
    }

    if (function_mode)
    {
        int i;

        /*
          Check for invalid switch combinations
        */
        if (buffer_part || cut_at_cursor || append_mode || tokenize || cursor_mode || line_mode || search_mode || paging_mode)
        {
            append_format(stderr_buffer,
                          BUILTIN_ERR_COMBO,
                          argv[0]);

            builtin_print_help(parser, argv[0], stderr_buffer);
            return 1;
        }


        if (argc == w.woptind)
        {
            append_format(stderr_buffer,
                          BUILTIN_ERR_MISSING,
                          argv[0]);

            builtin_print_help(parser, argv[0], stderr_buffer);
            return 1;
        }
        for (i=w.woptind; i<argc; i++)
        {
            wchar_t c = input_function_get_code(argv[i]);
            if (c != (wchar_t)(-1))
            {
                /*
                  input_unreadch inserts the specified keypress or
                  readline function at the back of the queue of unused
                  keypresses
                */
                input_queue_ch(c);
            }
            else
            {
                append_format(stderr_buffer,
                              _(L"%ls: Unknown input function '%ls'\n"),
                              argv[0],
                              argv[i]);
                builtin_print_help(parser, argv[0], stderr_buffer);
                return 1;
            }
        }

        return 0;
    }

    if (selection_mode)
    {
        size_t start, len;
        const wchar_t *buffer = reader_get_buffer();
        if (reader_get_selection(&start, &len))
        {
            stdout_buffer.append(buffer + start, len);
        }
        return 0;
    }

    /*
      Check for invalid switch combinations
    */
    if ((search_mode || line_mode || cursor_mode || paging_mode) && (argc-w.woptind > 1))
    {

        append_format(stderr_buffer,
                      argv[0],
                      L": Too many arguments\n",
                      NULL);
        builtin_print_help(parser, argv[0], stderr_buffer);
        return 1;
    }

    if ((buffer_part || tokenize || cut_at_cursor) && (cursor_mode || line_mode || search_mode || paging_mode))
    {
        append_format(stderr_buffer,
                      BUILTIN_ERR_COMBO,
                      argv[0]);

        builtin_print_help(parser, argv[0], stderr_buffer);
        return 1;
    }


    if ((tokenize || cut_at_cursor) && (argc-w.woptind))
    {
        append_format(stderr_buffer,
                      BUILTIN_ERR_COMBO2,
                      argv[0],
                      L"--cut-at-cursor and --tokenize can not be used when setting the commandline");


        builtin_print_help(parser, argv[0], stderr_buffer);
        return 1;
    }

    if (append_mode && !(argc-w.woptind))
    {
        append_format(stderr_buffer,
                      BUILTIN_ERR_COMBO2,
                      argv[0],
                      L"insertion mode switches can not be used when not in insertion mode");

        builtin_print_help(parser, argv[0], stderr_buffer);
        return 1;
    }

    /*
      Set default modes
    */
    if (!append_mode)
    {
        append_mode = REPLACE_MODE;
    }

    if (!buffer_part)
    {
        buffer_part = STRING_MODE;
    }

    if (cursor_mode)
    {
        if (argc-w.woptind)
        {
            wchar_t *endptr;
            long new_pos;
            errno = 0;

            new_pos = wcstol(argv[w.woptind], &endptr, 10);
            if (*endptr || errno)
            {
                append_format(stderr_buffer,
                              BUILTIN_ERR_NOT_NUMBER,
                              argv[0],
                              argv[w.woptind]);
                builtin_print_help(parser, argv[0], stderr_buffer);
            }

            current_buffer = reader_get_buffer();
            new_pos = maxi(0L, mini(new_pos, (long)wcslen(current_buffer)));
            reader_set_buffer(current_buffer, (size_t)new_pos);
            return 0;
        }
        else
        {
            append_format(stdout_buffer, L"%lu\n", (unsigned long)reader_get_cursor_pos());
            return 0;
        }

    }

    if (line_mode)
    {
        size_t pos = reader_get_cursor_pos();
        const wchar_t *buff = reader_get_buffer();
        append_format(stdout_buffer, L"%lu\n", (unsigned long)parse_util_lineno(buff, pos));
        return 0;

    }

    if (search_mode)
    {
        return ! reader_search_mode();
    }

    if (paging_mode)
    {
        return ! reader_has_pager_contents();
    }


    switch (buffer_part)
    {
        case STRING_MODE:
        {
            begin = get_buffer();
            end = begin+wcslen(begin);
            break;
        }

        case PROCESS_MODE:
        {
            parse_util_process_extent(get_buffer(),
                                      get_cursor_pos(),
                                      &begin,
                                      &end);
            break;
        }

        case JOB_MODE:
        {
            parse_util_job_extent(get_buffer(),
                                  get_cursor_pos(),
                                  &begin,
                                  &end);
            break;
        }

        case TOKEN_MODE:
        {
            parse_util_token_extent(get_buffer(),
                                    get_cursor_pos(),
                                    &begin,
                                    &end,
                                    0, 0);
            break;
        }

    }

    switch (argc-w.woptind)
    {
        case 0:
        {
            write_part(begin, end, cut_at_cursor, tokenize);
            break;
        }

        case 1:
        {
            replace_part(begin, end, argv[w.woptind], append_mode);
            break;
        }

        default:
        {
            wcstring sb = argv[w.woptind];
            int i;

            for (i=w.woptind+1; i<argc; i++)
            {
                sb.push_back(L'\n');
                sb.append(argv[i]);
            }

            replace_part(begin, end, sb.c_str(), append_mode);

            break;
        }
    }

    return 0;
}
Ejemplo n.º 19
0
/**
   Print information about the specified job
*/
static void builtin_jobs_print(const job_t *j, int mode, int header)
{
    process_t *p;
    switch (mode)
    {
        case JOBS_DEFAULT:
        {

            if (header)
            {
                /*
                  Print table header before first job
                */
                stdout_buffer.append(_(L"Job\tGroup\t"));
#ifdef HAVE__PROC_SELF_STAT
                stdout_buffer.append(_(L"CPU\t"));
#endif
                stdout_buffer.append(_(L"State\tCommand\n"));
            }

            append_format(stdout_buffer, L"%d\t%d\t", j->job_id, j->pgid);

#ifdef HAVE__PROC_SELF_STAT
            append_format(stdout_buffer, L"%d%%\t", cpu_use(j));
#endif
            stdout_buffer.append(job_is_stopped(j)?_(L"stopped"):_(L"running"));
            stdout_buffer.append(L"\t");
            stdout_buffer.append(j->command_wcstr());
            stdout_buffer.append(L"\n");
            break;
        }

        case JOBS_PRINT_GROUP:
        {
            if (header)
            {
                /*
                  Print table header before first job
                */
                stdout_buffer.append(_(L"Group\n"));
            }
            append_format(stdout_buffer, L"%d\n", j->pgid);
            break;
        }

        case JOBS_PRINT_PID:
        {
            if (header)
            {
                /*
                  Print table header before first job
                */
                stdout_buffer.append(_(L"Procces\n"));
            }

            for (p=j->first_process; p; p=p->next)
            {
                append_format(stdout_buffer, L"%d\n", p->pid);
            }
            break;
        }

        case JOBS_PRINT_COMMAND:
        {
            if (header)
            {
                /*
                  Print table header before first job
                */
                stdout_buffer.append(_(L"Command\n"));
            }

            for (p=j->first_process; p; p=p->next)
            {
                append_format(stdout_buffer, L"%ls\n", p->argv0());
            }
            break;
        }
    }

}
Ejemplo n.º 20
0
/**
   The jobs builtin. Used fopr printing running jobs. Defined in builtin_jobs.c.
*/
static int builtin_jobs(parser_t &parser, wchar_t **argv)
{
    int argc=0;
    int found=0;
    int mode=JOBS_DEFAULT;
    int print_last = 0;
    const job_t *j;

    argc = builtin_count_args(argv);
    woptind=0;

    while (1)
    {
        static const struct woption
                long_options[] =
        {
            {
                L"pid", no_argument, 0, 'p'
            }
            ,
            {
                L"command", no_argument, 0, 'c'
            }
            ,
            {
                L"group", no_argument, 0, 'g'
            }
            ,
            {
                L"last", no_argument, 0, 'l'
            }
            ,
            {
                L"help", no_argument, 0, 'h'
            }
            ,
            {
                0, 0, 0, 0
            }
        }
        ;

        int opt_index = 0;

        int opt = wgetopt_long(argc,
                               argv,
                               L"pclgh",
                               long_options,
                               &opt_index);
        if (opt == -1)
            break;

        switch (opt)
        {
            case 0:
                if (long_options[opt_index].flag != 0)
                    break;
                append_format(stderr_buffer,
                              BUILTIN_ERR_UNKNOWN,
                              argv[0],
                              long_options[opt_index].name);

                builtin_print_help(parser, argv[0], stderr_buffer);


                return 1;


            case 'p':
                mode=JOBS_PRINT_PID;
                break;

            case 'c':
                mode=JOBS_PRINT_COMMAND;
                break;

            case 'g':
                mode=JOBS_PRINT_GROUP;
                break;

            case 'l':
            {
                print_last = 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;

        }
    }


    /*
      Do not babble if not interactive
    */
    if (builtin_out_redirect)
    {
        found=1;
    }

    if (print_last)
    {
        /*
          Ignore unconstructed jobs, i.e. ourself.
        */
        job_iterator_t jobs;
        const job_t *j;
        while ((j = jobs.next()))
        {

            if ((j->flags & JOB_CONSTRUCTED) && !job_is_completed(j))
            {
                builtin_jobs_print(j, mode, !found);
                return 0;
            }
        }

    }
    else
    {
        if (woptind < argc)
        {
            int i;

            found = 1;

            for (i=woptind; i<argc; i++)
            {
                int pid;
                wchar_t *end;
                errno=0;
                pid=fish_wcstoi(argv[i], &end, 10);
                if (errno || *end)
                {
                    append_format(stderr_buffer,
                                  _(L"%ls: '%ls' is not a job\n"),
                                  argv[0],
                                  argv[i]);
                    return 1;
                }

                j = job_get_from_pid(pid);

                if (j && !job_is_completed(j))
                {
                    builtin_jobs_print(j, mode, !found);
                }
                else
                {
                    append_format(stderr_buffer,
                                  _(L"%ls: No suitable job: %d\n"),
                                  argv[0],
                                  pid);
                    return 1;
                }
            }
        }
        else
        {
            job_iterator_t jobs;
            const job_t *j;
            while ((j = jobs.next()))
            {
                /*
                  Ignore unconstructed jobs, i.e. ourself.
                */
                if ((j->flags & JOB_CONSTRUCTED) && !job_is_completed(j))
                {
                    builtin_jobs_print(j, mode, !found);
                    found = 1;
                }
            }
        }
    }

    if (!found)
    {
        append_format(stdout_buffer,
                      _(L"%ls: There are no jobs\n"),
                      argv[0]);
    }

    return 0;
}
Ejemplo n.º 21
0
void parser_t::stack_trace_internal(size_t block_idx, wcstring *buff) const {
    // Check if we should end the recursion.
    if (block_idx >= this->block_count()) return;

    const block_t *b = this->block_at_index(block_idx);

    if (b->type() == EVENT) {
        // This is an event handler.
        const event_block_t *eb = static_cast<const event_block_t *>(b);
        wcstring description = event_get_desc(eb->event);
        append_format(*buff, _(L"in event handler: %ls\n"), description.c_str());
        buff->append(L"\n");

        // Stop recursing at event handler. No reason to believe that any other code is relevant.
        //
        // It might make sense in the future to continue printing the stack trace of the code that
        // invoked the event, if this is a programmatic event, but we can't currently detect that.
        return;
    }

    if (b->type() == FUNCTION_CALL || b->type() == FUNCTION_CALL_NO_SHADOW || b->type() == SOURCE ||
        b->type() == SUBST) {
        // These types of blocks should be printed.
        int i;

        switch (b->type()) {
            case SOURCE: {
                const source_block_t *sb = static_cast<const source_block_t *>(b);
                const wchar_t *source_dest = sb->source_file;
                append_format(*buff, _(L"from sourcing file %ls\n"),
                              user_presentable_path(source_dest).c_str());
                break;
            }
            case FUNCTION_CALL:
            case FUNCTION_CALL_NO_SHADOW: {
                const function_block_t *fb = static_cast<const function_block_t *>(b);
                append_format(*buff, _(L"in function '%ls'\n"), fb->name.c_str());
                break;
            }
            case SUBST: {
                append_format(*buff, _(L"in command substitution\n"));
                break;
            }
            default: {
                break;  // can't get here
            }
        }

        const wchar_t *file = b->src_filename;

        if (file) {
            append_format(*buff, _(L"\tcalled on line %d of file %ls\n"), b->src_lineno,
                          user_presentable_path(file).c_str());
        } else if (is_within_fish_initialization) {
            append_format(*buff, _(L"\tcalled during startup\n"));
        } else {
            append_format(*buff, _(L"\tcalled on standard input\n"));
        }

        if (b->type() == FUNCTION_CALL) {
            const function_block_t *fb = static_cast<const function_block_t *>(b);
            const process_t *const process = fb->process;
            if (process->argv(1)) {
                wcstring tmp;

                for (i = 1; process->argv(i); i++) {
                    if (i > 1) tmp.push_back(L' ');
                    tmp.append(process->argv(i));
                }
                append_format(*buff, _(L"\twith parameter list '%ls'\n"), tmp.c_str());
            }
        }

        append_format(*buff, L"\n");
    }

    // Recursively print the next block.
    parser_t::stack_trace_internal(block_idx + 1, buff);
}
Ejemplo n.º 22
0
/**
   Indent the specified input
 */
static int indent(wcstring &out, const wcstring &in, int flags)
{
    int res=0;
    int is_command = 1;
    int indent = 0;
    int do_indent = 1;
    int prev_type = 0;
    int prev_prev_type = 0;

    tokenizer_t tok(in.c_str(), TOK_SHOW_COMMENTS);
    for (; tok_has_next(&tok); tok_next(&tok))
    {
        int type = tok_last_type(&tok);
        const wchar_t *last = tok_last(&tok);

        switch (type)
        {
            case TOK_STRING:
            {
                if (is_command)
                {
                    int next_indent = indent;
                    is_command = 0;

                    wcstring unesc;
                    unescape_string(last, &unesc, UNESCAPE_SPECIAL);

                    if (parser_keywords_is_block(unesc))
                    {
                        next_indent++;
                    }
                    else if (unesc == L"else")
                    {
                        indent--;
                    }
                    /* case should have the same indent level as switch*/
                    else if (unesc == L"case")
                    {
                        indent--;
                    }
                    else if (unesc == L"end")
                    {
                        indent--;
                        next_indent--;
                    }


                    if (do_indent && flags && prev_type != TOK_PIPE)
                    {
                        insert_tabs(out, indent);
                    }

                    append_format(out, L"%ls", last);

                    indent = next_indent;

                }
                else
                {
                    if (prev_type != TOK_REDIRECT_FD)
                        out.append(L" ");
                    out.append(last);
                }

                break;
            }

            case TOK_END:
            {
                if (prev_type != TOK_END || prev_prev_type != TOK_END)
                    out.append(L"\n");
                do_indent = 1;
                is_command = 1;
                break;
            }

            case TOK_PIPE:
            {
                out.append(L" ");
                if (last[0] == '2' && !last[1])
                {
                    out.append(L"^");
                }
                else if (last[0] != '1' || last[1])
                {
                    out.append(last);
                    out.append(L">");
                }
                out.append(L" | ");
                is_command = 1;
                break;
            }

            case TOK_REDIRECT_OUT:
            {
                out.append(L" ");
                if (wcscmp(last, L"2") == 0)
                {
                    out.append(L"^");
                }
                else
                {
                    if (wcscmp(last, L"1") != 0)
                        out.append(last);
                    out.append(L"> ");
                }
                break;
            }

            case TOK_REDIRECT_APPEND:
            {
                out.append(L" ");
                if (wcscmp(last, L"2") == 0)
                {
                    out.append(L"^^");
                }
                else
                {
                    if (wcscmp(last, L"1") != 0)
                        out.append(last);
                    out.append(L">> ");
                }
                break;
            }

            case TOK_REDIRECT_IN:
            {
                out.append(L" ");
                if (wcscmp(last, L"0") != 0)
                    out.append(last);
                out.append(L"< ");
                break;
            }

            case TOK_REDIRECT_FD:
            {
                out.append(L" ");
                if (wcscmp(last, L"1") != 0)
                    out.append(last);
                out.append(L">& ");
                break;
            }

            case TOK_BACKGROUND:
            {
                out.append(L"&\n");
                do_indent = 1;
                is_command = 1;
                break;
            }

            case TOK_COMMENT:
            {
                if (do_indent && flags)
                {
                    insert_tabs(out, indent);
                }

                append_format(out, L"%ls", last);
                do_indent = 1;
                break;
            }

            default:
            {
                debug(0, L"Unknown token '%ls'", last);
                exit(1);
            }
        }

        prev_prev_type = prev_type;
        prev_type = type;

    }

    return res;
}
Ejemplo n.º 23
0
/**
   The complete builtin. Used for specifying programmable
   tab-completions. Calls the functions in complete.c for any heavy
   lifting. Defined in builtin_complete.c
*/
static int builtin_complete(parser_t &parser, wchar_t **argv)
{
    ASSERT_IS_MAIN_THREAD();
    bool res=false;
    int argc=0;
    int result_mode=SHARED;
    int remove = 0;
    int authoritative = -1;

    wcstring short_opt;
    wcstring_list_t gnu_opt, old_opt;
    const wchar_t *comp=L"", *desc=L"", *condition=L"";

    bool do_complete = false;
    wcstring do_complete_param;

    wcstring_list_t cmd;
    wcstring_list_t path;

    static int recursion_level=0;

    argc = builtin_count_args(argv);

    woptind=0;

    while (! res)
    {
        static const struct woption
                long_options[] =
        {
            {
                L"exclusive", no_argument, 0, 'x'
            }
            ,
            {
                L"no-files", no_argument, 0, 'f'
            }
            ,
            {
                L"require-parameter", no_argument, 0, 'r'
            }
            ,
            {
                L"path", required_argument, 0, 'p'
            }
            ,
            {
                L"command", required_argument, 0, 'c'
            }
            ,
            {
                L"short-option", required_argument, 0, 's'
            }
            ,
            {
                L"long-option", required_argument, 0, 'l'
            }
            ,
            {
                L"old-option", required_argument, 0, 'o'
            }
            ,
            {
                L"description", required_argument, 0, 'd'
            }
            ,
            {
                L"arguments", required_argument, 0, 'a'
            }
            ,
            {
                L"erase", no_argument, 0, 'e'
            }
            ,
            {
                L"unauthoritative", no_argument, 0, 'u'
            }
            ,
            {
                L"authoritative", no_argument, 0, 'A'
            }
            ,
            {
                L"condition", required_argument, 0, 'n'
            }
            ,
            {
                L"do-complete", optional_argument, 0, 'C'
            }
            ,
            {
                L"help", no_argument, 0, 'h'
            }
            ,
            {
                0, 0, 0, 0
            }
        }
        ;

        int opt_index = 0;

        int opt = wgetopt_long(argc,
                               argv,
                               L"a:c:p:s:l:o:d:frxeuAn:C::h",
                               long_options,
                               &opt_index);
        if (opt == -1)
            break;

        switch (opt)
        {
            case 0:
                if (long_options[opt_index].flag != 0)
                    break;
                append_format(stderr_buffer,
                              BUILTIN_ERR_UNKNOWN,
                              argv[0],
                              long_options[opt_index].name);
                builtin_print_help(parser, argv[0], stderr_buffer);


                res = true;
                break;

            case 'x':
                result_mode |= EXCLUSIVE;
                break;

            case 'f':
                result_mode |= NO_FILES;
                break;

            case 'r':
                result_mode |= NO_COMMON;
                break;

            case 'p':
            case 'c':
            {
                wcstring tmp;
                if (unescape_string(woptarg, &tmp, UNESCAPE_SPECIAL))
                {
                    if (opt=='p')
                        path.push_back(tmp);
                    else
                        cmd.push_back(tmp);
                }
                else
                {
                    append_format(stderr_buffer, L"%ls: Invalid token '%ls'\n", argv[0], woptarg);
                    res = true;
                }
                break;
            }

            case 'd':
                desc = woptarg;
                break;

            case 'u':
                authoritative=0;
                break;

            case 'A':
                authoritative=1;
                break;

            case 's':
                short_opt.append(woptarg);
                break;

            case 'l':
                gnu_opt.push_back(woptarg);
                break;

            case 'o':
                old_opt.push_back(woptarg);
                break;

            case 'a':
                comp = woptarg;
                break;

            case 'e':
                remove = 1;
                break;

            case 'n':
                condition = woptarg;
                break;

            case 'C':
                do_complete = true;
                do_complete_param = woptarg ? woptarg : reader_get_buffer();
                break;

            case 'h':
                builtin_print_help(parser, argv[0], stdout_buffer);
                return 0;

            case '?':
                builtin_unknown_option(parser, argv[0], argv[woptind-1]);
                res = true;
                break;

        }

    }

    if (!res)
    {
        if (condition && wcslen(condition))
        {
            const wcstring condition_string = condition;
            parse_error_list_t errors;
            if (parse_util_detect_errors(condition_string, &errors))
            {
                append_format(stderr_buffer,
                              L"%ls: Condition '%ls' contained a syntax error",
                              argv[0],
                              condition);
                for (size_t i=0; i < errors.size(); i++)
                {
                    append_format(stderr_buffer, L"\n%s: ", argv[0]);
                    stderr_buffer.append(errors.at(i).describe(condition_string));
                }
                res = true;
            }
        }
    }

    if (!res)
    {
        if (comp && wcslen(comp))
        {
            if (parser.test_args(comp, 0, 0))
            {
                append_format(stderr_buffer,
                              L"%ls: Completion '%ls' contained a syntax error\n",
                              argv[0],
                              comp);

                parser.test_args(comp, &stderr_buffer, argv[0]);

                res = true;
            }
        }
    }

    if (!res)
    {
        if (do_complete)
        {
            const wchar_t *token;

            parse_util_token_extent(do_complete_param.c_str(), do_complete_param.size(), &token, 0, 0, 0);

            const wchar_t *prev_temporary_buffer = temporary_buffer;
            temporary_buffer = do_complete_param.c_str();

            if (recursion_level < 1)
            {
                recursion_level++;

                std::vector<completion_t> comp;
                complete(do_complete_param, comp, COMPLETION_REQUEST_DEFAULT);

                for (size_t i=0; i< comp.size() ; i++)
                {
                    const completion_t &next =  comp.at(i);

                    const wchar_t *prepend;

                    if (next.flags & COMPLETE_REPLACES_TOKEN)
                    {
                        prepend = L"";
                    }
                    else
                    {
                        prepend = token;
                    }


                    if (!(next.description).empty())
                    {
                        append_format(stdout_buffer, L"%ls%ls\t%ls\n", prepend, next.completion.c_str(), next.description.c_str());
                    }
                    else
                    {
                        append_format(stdout_buffer, L"%ls%ls\n", prepend, next.completion.c_str());
                    }
                }

                recursion_level--;
            }

            temporary_buffer = prev_temporary_buffer;

        }
        else if (woptind != argc)
        {
            append_format(stderr_buffer,
                          _(L"%ls: Too many arguments\n"),
                          argv[0]);
            builtin_print_help(parser, argv[0], stderr_buffer);

            res = true;
        }
        else if (cmd.empty() && path.empty())
        {
            /* No arguments specified, meaning we print the definitions of
             * all specified completions to stdout.*/
            complete_print(stdout_buffer);
        }
        else
        {
            int flags = COMPLETE_AUTO_SPACE;

            if (remove)
            {
                builtin_complete_remove(cmd,
                                        path,
                                        short_opt.c_str(),
                                        gnu_opt,
                                        old_opt);
            }
            else
            {
                builtin_complete_add(cmd,
                                     path,
                                     short_opt.c_str(),
                                     gnu_opt,
                                     old_opt,
                                     result_mode,
                                     authoritative,
                                     condition,
                                     comp,
                                     desc,
                                     flags);
            }

        }
    }

    return res ? 1 : 0;
}
Ejemplo n.º 24
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;
}