示例#1
0
/**
 Perform cmdsubst expansion
 */
static int expand_cmdsubst(parser_t &parser, const wcstring &input, std::vector<completion_t> &outList)
{
    wchar_t *paran_begin=0, *paran_end=0;
    std::vector<wcstring> sub_res;
    size_t i, j;
    wchar_t *tail_begin = 0;

    const wchar_t * const in = input.c_str();

    int parse_ret;
    switch (parse_ret = parse_util_locate_cmdsubst(in,
                        &paran_begin,
                        &paran_end,
                        0))
    {
        case -1:
            parser.error(SYNTAX_ERROR,
                         -1,
                         L"Mismatched parenthesis");
            return 0;
        case 0:
            outList.push_back(completion_t(input));
            return 1;
        case 1:

            break;
    }

    const wcstring subcmd(paran_begin + 1, paran_end-paran_begin - 1);

    if (exec_subshell(subcmd, sub_res) == -1)
    {
        parser.error(CMDSUBST_ERROR, -1, L"Unknown error while evaulating command substitution");
        return 0;
    }

    tail_begin = paran_end + 1;
    if (*tail_begin == L'[')
    {
        std::vector<long> slice_idx;
        wchar_t *slice_end;

        if (parse_slice(tail_begin, &slice_end, slice_idx, sub_res.size()))
        {
            parser.error(SYNTAX_ERROR, -1, L"Invalid index value");
            return 0;
        }
        else
        {
            std::vector<wcstring> sub_res2;
            tail_begin = slice_end;
            for (i=0; i < slice_idx.size(); i++)
            {
                long idx = slice_idx.at(i);
                if (idx < 1 || (size_t)idx > sub_res.size())
                {
                    parser.error(SYNTAX_ERROR,
                                 -1,
                                 ARRAY_BOUNDS_ERR);
                    return 0;
                }
                idx = idx-1;

                sub_res2.push_back(sub_res.at(idx));
                //        debug( 0, L"Pushing item '%ls' with index %d onto sliced result", al_get( sub_res, idx ), idx );
                //sub_res[idx] = 0; // ??
            }
            sub_res = sub_res2;
        }
    }


    /*
       Recursively call ourselves to expand any remaining command
       substitutions. The result of this recursive call using the tail
       of the string is inserted into the tail_expand array list
       */
    std::vector<completion_t> tail_expand;
    expand_cmdsubst(parser, tail_begin, tail_expand);

    /*
       Combine the result of the current command substitution with the
       result of the recursive tail expansion
       */
    for (i=0; i<sub_res.size(); i++)
    {
        wcstring sub_item = sub_res.at(i);
        wcstring sub_item2 = escape_string(sub_item, 1);

        for (j=0; j < tail_expand.size(); j++)
        {

            wcstring whole_item;

            wcstring tail_item = tail_expand.at(j).completion;

            //sb_append_substring( &whole_item, in, len1 );
            whole_item.append(in, paran_begin-in);

            //sb_append_char( &whole_item, INTERNAL_SEPARATOR );
            whole_item.push_back(INTERNAL_SEPARATOR);

            //sb_append_substring( &whole_item, sub_item2, item_len );
            whole_item.append(sub_item2);

            //sb_append_char( &whole_item, INTERNAL_SEPARATOR );
            whole_item.push_back(INTERNAL_SEPARATOR);

            //sb_append( &whole_item, tail_item );
            whole_item.append(tail_item);

            //al_push( out, whole_item.buff );
            outList.push_back(completion_t(whole_item));
        }
    }

    return 1;
}
示例#2
0
int expand_string(const wcstring &input, std::vector<completion_t> &output, expand_flags_t flags)
{
    parser_t parser(PARSER_TYPE_ERRORS_ONLY, true /* show errors */);
    std::vector<completion_t> list1, list2;
    std::vector<completion_t> *in, *out;

    size_t i;
    int res = EXPAND_OK;

    if ((!(flags & ACCEPT_INCOMPLETE)) && expand_is_clean(input.c_str()))
    {
        output.push_back(completion_t(input));
        return EXPAND_OK;
    }

    if (EXPAND_SKIP_CMDSUBST & flags)
    {
        wchar_t *begin, *end;

        if (parse_util_locate_cmdsubst(input.c_str(),
                                       &begin,
                                       &end,
                                       1) != 0)
        {
            parser.error(CMDSUBST_ERROR, -1, L"Command substitutions not allowed");
            return EXPAND_ERROR;
        }
        list1.push_back(completion_t(input));
    }
    else
    {
        int cmdsubst_ok = expand_cmdsubst(parser, input, list1);
        if (! cmdsubst_ok)
            return EXPAND_ERROR;
    }

    in = &list1;
    out = &list2;

    for (i=0; i < in->size(); i++)
    {
        /*
         We accept incomplete strings here, since complete uses
         expand_string to expand incomplete strings from the
         commandline.
         */
        int unescape_flags = UNESCAPE_SPECIAL | UNESCAPE_INCOMPLETE;
        wcstring next = expand_unescape_string(in->at(i).completion, unescape_flags);

        if (EXPAND_SKIP_VARIABLES & flags)
        {
            for (size_t i=0; i < next.size(); i++)
            {
                if (next.at(i) == VARIABLE_EXPAND)
                {
                    next[i] = L'$';
                }
            }
            out->push_back(completion_t(next));
        }
        else
        {
            if (!expand_variables2(parser, next, *out, next.size() - 1))
            {
                return EXPAND_ERROR;
            }
        }
    }

    in->clear();

    in = &list2;
    out = &list1;

    for (i=0; i < in->size(); i++)
    {
        wcstring next = in->at(i).completion;

        if (!expand_brackets(parser, next, flags, *out))
        {
            return EXPAND_ERROR;
        }
    }
    in->clear();

    in = &list1;
    out = &list2;

    for (i=0; i < in->size(); i++)
    {
        wcstring next = in->at(i).completion;

        expand_home_directory(next);


        if (flags & ACCEPT_INCOMPLETE)
        {
            if (next[0] == PROCESS_EXPAND)
            {
                /*
                 If process expansion matches, we are not
                 interested in other completions, so we
                 short-circut and return
                 */
                if (!(flags & EXPAND_SKIP_PROCESS))
                    expand_pid(next, flags, output);
                return EXPAND_OK;
            }
            else
            {
                out->push_back(completion_t(next));
            }
        }
        else
        {
            if (!(flags & EXPAND_SKIP_PROCESS) && ! expand_pid(next, flags, *out))
            {
                return EXPAND_ERROR;
            }
        }
    }

    in->clear();

    in = &list2;
    out = &list1;

    for (i=0; i < in->size(); i++)
    {
        wcstring next_str = in->at(i).completion;
        int wc_res;

        remove_internal_separator(next_str, (EXPAND_SKIP_WILDCARDS & flags) ? true : false);
        const wchar_t *next = next_str.c_str();

        if (((flags & ACCEPT_INCOMPLETE) && (!(flags & EXPAND_SKIP_WILDCARDS))) ||
                wildcard_has(next, 1))
        {
            const wchar_t *start, *rest;
            std::vector<completion_t> *list = out;

            if (next[0] == '/')
            {
                start = L"/";
                rest = &next[1];
            }
            else
            {
                start = L"";
                rest = next;
            }

            if (flags & ACCEPT_INCOMPLETE)
            {
                list = &output;
            }

            wc_res = wildcard_expand_string(rest, start, flags, *list);

            if (!(flags & ACCEPT_INCOMPLETE))
            {

                switch (wc_res)
                {
                    case 0:
                    {
                        if (!(flags & ACCEPT_INCOMPLETE))
                        {
                            if (res == EXPAND_OK)
                                res = EXPAND_WILDCARD_NO_MATCH;
                            break;
                        }
                    }

                    case 1:
                    {
                        size_t j;
                        res = EXPAND_WILDCARD_MATCH;
                        sort_completions(*out);

                        for (j=0; j< out->size(); j++)
                        {
                            output.push_back(out->at(j));
                        }
                        out->clear();
                        break;
                    }

                    case -1:
                    {
                        return EXPAND_ERROR;
                    }

                }
            }

        }
        else
        {
            if (flags & ACCEPT_INCOMPLETE)
            {
            }
            else
            {
                output.push_back(completion_t(next));
            }
        }

    }

    return res;
}
示例#3
0
/**
   The real implementation of wildcard expansion is in this
   function. Other functions are just wrappers around this one.

   This function traverses the relevant directory tree looking for
   matches, and recurses when needed to handle wildcrards spanning
   multiple components and recursive wildcards. 
 */
static int wildcard_expand_internal( const wchar_t *wc, 
									 const wchar_t *base_dir,
									 expand_flags_t flags,
									 std::vector<completion_t> &out )
{
	
	/* Points to the end of the current wildcard segment */
	const wchar_t *wc_end;

	/* Variables for traversing a directory */
	DIR *dir;
	
	/* The result returned */
	int res = 0;
	
	/* Length of the directory to search in */
	int base_len;

	/* Variables for testing for presense of recursive wildcards */
	const wchar_t *wc_recursive;
	int is_recursive;

	/* Sligtly mangled version of base_dir */
	const wchar_t *dir_string;
	
	//	debug( 3, L"WILDCARD_EXPAND %ls in %ls", wc, base_dir );

	if( reader_interrupted() )
	{
		return -1;
	}
	
	if( !wc || !base_dir )
	{
		debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ );
		return 0;		
	}

	if( flags & ACCEPT_INCOMPLETE )
	{	
		/* 
		   Avoid excessive number of returned matches for wc ending with a * 
		*/
		int len = wcslen(wc);
		if( len && (wc[len-1]==ANY_STRING) )
		{
			wchar_t * foo = wcsdup( wc );
			foo[len-1]=0;
			int res = wildcard_expand_internal( foo, base_dir, flags, out );
			free( foo );
			return res;			
		}
	}

	/*
	  Initialize various variables
	*/

	dir_string = base_dir[0]==L'\0'?L".":base_dir;
	
	if( !(dir = wopendir( dir_string )))
	{
		return 0;
	}

	wc_end = wcschr(wc,L'/');
	base_len = wcslen( base_dir );

	/*
	  Test for recursive match string in current segment
	*/	
	wc_recursive = wcschr( wc, ANY_STRING_RECURSIVE );
	is_recursive = ( wc_recursive && (!wc_end || wc_recursive < wc_end));

	/*
	  Is this segment of the wildcard the last?
	*/
	if( !wc_end )
	{
		/*
		  Wildcard segment is the last segment,

		  Insert all matching files/directories
		*/
		if( wc[0]=='\0' )
		{
			/*
			  The last wildcard segment is empty. Insert everything if
			  completing, the directory itself otherwise.
			*/
			if( flags & ACCEPT_INCOMPLETE )
			{
                wcstring next;
				while(wreaddir(dir, next))
				{
					if( next[0] != L'.' )
					{
						wcstring long_name = make_path( base_dir, next );
						
						if( test_flags( long_name.c_str(), flags ) )
						{
							wildcard_completion_allocate( out,
														  long_name,
														  next,
														  L"",
                                                          flags);
						}
					}					
				}
			}
			else
			{								
				res = 1;
				completion_t data_to_push(base_dir);
				if (std::find( out.begin(), out.end(), data_to_push ) == out.end()) {
					 out.push_back(data_to_push);
				}
			}							
		}
		else
		{
			/*
			  This is the last wildcard segment, and it is not empty. Match files/directories.
			*/
            wcstring next;
			while (wreaddir(dir, next))
			{
                const wchar_t * const name = next.c_str();
				if( flags & ACCEPT_INCOMPLETE )
				{
					
					const wcstring long_name = make_path( base_dir, next );

					/*
					  Test for matches before stating file, so as to minimize the number of calls to the much slower stat function 
					*/
					std::vector<completion_t> test;
					if( wildcard_complete( name,
										   wc,
										   L"",
										   0,
										   test,
										   0 ) )
					{
						if( test_flags( long_name.c_str(), flags ) )
						{
							wildcard_completion_allocate( out,
														  long_name,
                                                          name,
														  wc,
                                                          flags);
							
						}
					}					
				}
				else
				{
					if( wildcard_match2( name, wc, 1 ) )
					{
                        const wcstring long_name = make_path(base_dir, next);
						int skip = 0;
						
						if( is_recursive )
						{
							/*
							  In recursive mode, we are only
							  interested in adding files -directories
							  will be added in the next pass.
							*/
							struct stat buf;
							if( !wstat( long_name, &buf ) )
							{
								skip = S_ISDIR(buf.st_mode);
							}							
						}
						if (! skip)
						{
							out.push_back(completion_t(long_name) );
						}
						res = 1;
					}
				}
			}
		}
	}

	if( wc_end || is_recursive )
	{
		/*
		  Wilcard segment is not the last segment.  Recursively call
		  wildcard_expand for all matching subdirectories.
		*/
		
		/*
		  wc_str is the part of the wildcarded string from the
		  beginning to the first slash
		*/
		wchar_t *wc_str;

		/*
		  new_dir is a scratch area containing the full path to a
		  file/directory we are iterating over
		*/
		wchar_t *new_dir;

		/*
		  The maximum length of a file element
		*/
		long ln=MAX_FILE_LENGTH;
		char * narrow_dir_string = wcs2str( dir_string );

		/*
		  In recursive mode, we look through the direcotry twice. If
		  so, this rewind is needed.
		*/
		rewinddir( dir );

		if( narrow_dir_string )
		{
			/* 
			   Find out how long the filename can be in a worst case
			   scenario
			*/
			ln = pathconf( narrow_dir_string, _PC_NAME_MAX ); 

			/*
			  If not specified, use som large number as fallback
			*/
			if( ln < 0 )
				ln = MAX_FILE_LENGTH;		
			free( narrow_dir_string );
		}
		new_dir= (wchar_t *)malloc( sizeof(wchar_t)*(base_len+ln+2)  );

		wc_str = wc_end?wcsndup(wc, wc_end-wc):wcsdup(wc);

		if( (!new_dir) || (!wc_str) )
		{
			DIE_MEM();
		}

		wcscpy( new_dir, base_dir );
		
        wcstring next;
		while (wreaddir(dir, next))
		{
			const wchar_t *name = next.c_str();
			
			/*
			  Test if the file/directory name matches the whole
			  wildcard element, i.e. regular matching.
			*/
			int whole_match = wildcard_match2( name, wc_str, 1 );
			int partial_match = 0;
			
			/* 
			   If we are doing recursive matching, also check if this
			   directory matches the part up to the recusrive
			   wildcard, if so, then we can search all subdirectories
			   for matches.
			*/
			if( is_recursive )
			{
				const wchar_t *end = wcschr( wc, ANY_STRING_RECURSIVE );
				wchar_t *wc_sub = wcsndup( wc, end-wc+1);
				partial_match = wildcard_match2( name, wc_sub, 1 );
				free( wc_sub );
			}			

			if( whole_match || partial_match )
			{
				int new_len;
				struct stat buf;			
				char *dir_str;
				int stat_res;
				int new_res;

				wcscpy(&new_dir[base_len], name );
				dir_str = wcs2str( new_dir );
				
				if( dir_str )
				{
					stat_res = stat( dir_str, &buf );
					free( dir_str );
					
					if( !stat_res )
					{
						if( S_ISDIR(buf.st_mode) )
						{
							new_len = wcslen( new_dir );
							new_dir[new_len] = L'/';
							new_dir[new_len+1] = L'\0';
							
							/*
							  Regular matching
							*/
							if( whole_match )
							{
								const wchar_t *new_wc = L"";
								if( wc_end )
								{
									new_wc=wc_end+1;
									/*
									  Accept multiple '/' as a single direcotry separator
									*/
									while(*new_wc==L'/')
									{
										new_wc++;
									}
								}
								
								new_res = wildcard_expand_internal( new_wc,
																	new_dir, 
																	flags, 
																	out );

								if( new_res == -1 )
								{
									res = -1;
									break;
								}								
								res |= new_res;
								
							}
							
							/*
							  Recursive matching
							*/
							if( partial_match )
							{
								
								new_res = wildcard_expand_internal( wcschr( wc, ANY_STRING_RECURSIVE ), 
																	new_dir,
																	flags | WILDCARD_RECURSIVE, 
																	out );

								if( new_res == -1 )
								{
									res = -1;
									break;
								}								
								res |= new_res;
								
							}
						}								
					}
				}
			}
		}
		
		free( wc_str );
		free( new_dir );
	}
	closedir( dir );

	return res;
}