aboutsummaryrefslogtreecommitdiffhomepage
path: root/wildcard.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'wildcard.cpp')
-rw-r--r--wildcard.cpp1588
1 files changed, 794 insertions, 794 deletions
diff --git a/wildcard.cpp b/wildcard.cpp
index d32c8e01..07cfcb7c 100644
--- a/wildcard.cpp
+++ b/wildcard.cpp
@@ -43,7 +43,7 @@ wildcards using **.
/**
The maximum length of a filename token. This is a fallback value,
- an attempt to find the true value using patchconf is always made.
+ an attempt to find the true value using patchconf is always made.
*/
#define MAX_FILE_LENGTH 1024
@@ -108,95 +108,95 @@ static std::map<wcstring, wcstring> suffix_map;
int wildcard_has( const wchar_t *str, int internal )
{
- if( !str )
- {
- debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ );
- return 0;
- }
-
- if( internal )
- {
- for( ; *str; str++ )
- {
- if( ( *str == ANY_CHAR ) || (*str == ANY_STRING) || (*str == ANY_STRING_RECURSIVE) )
- return 1;
- }
- }
- else
- {
+ if( !str )
+ {
+ debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ );
+ return 0;
+ }
+
+ if( internal )
+ {
+ for( ; *str; str++ )
+ {
+ if( ( *str == ANY_CHAR ) || (*str == ANY_STRING) || (*str == ANY_STRING_RECURSIVE) )
+ return 1;
+ }
+ }
+ else
+ {
wchar_t prev=0;
- for( ; *str; str++ )
- {
- if( ( (*str == L'*' ) || (*str == L'?' ) ) && (prev != L'\\') )
- return 1;
- prev = *str;
- }
- }
-
- return 0;
+ for( ; *str; str++ )
+ {
+ if( ( (*str == L'*' ) || (*str == L'?' ) ) && (prev != L'\\') )
+ return 1;
+ prev = *str;
+ }
+ }
+
+ return 0;
}
/**
Check whether the string str matches the wildcard string wc.
-
+
\param str String to be matched.
\param wc The wildcard.
- \param is_first Whether files beginning with dots should not be matched against wildcards.
+ \param is_first Whether files beginning with dots should not be matched against wildcards.
*/
static bool wildcard_match2(const wchar_t *str,
- const wchar_t *wc,
- bool is_first )
+ const wchar_t *wc,
+ bool is_first )
{
- if( *str == 0 && *wc==0 )
- return true;
-
+ if( *str == 0 && *wc==0 )
+ return true;
+
/* Hackish fix for https://github.com/fish-shell/fish-shell/issues/270. Prevent wildcards from matching . or .., but we must still allow literal matches. */
if (is_first && contains(str, L".", L".."))
{
/* The string is '.' or '..'. Return true if the wildcard exactly matches. */
return ! wcscmp(str, wc);
}
-
- if( *wc == ANY_STRING || *wc == ANY_STRING_RECURSIVE)
- {
- /* Ignore hidden file */
- if( is_first && *str == L'.' )
- {
- return false;
- }
-
- /* Try all submatches */
- do
- {
- if( wildcard_match2( str, wc+1, false ) )
- return true;
- }
- while( *(str++) != 0 );
- return false;
- }
- else if( *str == 0 )
- {
- /*
- End of string, but not end of wildcard, and the next wildcard
- element is not a '*', so this is not a match.
- */
- return false;
- }
-
- if( *wc == ANY_CHAR )
- {
- if( is_first && *str == L'.' )
- {
- return false;
- }
-
- return wildcard_match2( str+1, wc+1, false );
- }
-
- if( *wc == *str )
- return wildcard_match2( str+1, wc+1, false );
-
- return false;
+
+ if( *wc == ANY_STRING || *wc == ANY_STRING_RECURSIVE)
+ {
+ /* Ignore hidden file */
+ if( is_first && *str == L'.' )
+ {
+ return false;
+ }
+
+ /* Try all submatches */
+ do
+ {
+ if( wildcard_match2( str, wc+1, false ) )
+ return true;
+ }
+ while( *(str++) != 0 );
+ return false;
+ }
+ else if( *str == 0 )
+ {
+ /*
+ End of string, but not end of wildcard, and the next wildcard
+ element is not a '*', so this is not a match.
+ */
+ return false;
+ }
+
+ if( *wc == ANY_CHAR )
+ {
+ if( is_first && *str == L'.' )
+ {
+ return false;
+ }
+
+ return wildcard_match2( str+1, wc+1, false );
+ }
+
+ if( *wc == *str )
+ return wildcard_match2( str+1, wc+1, false );
+
+ return false;
}
/**
@@ -204,119 +204,119 @@ static bool wildcard_match2(const wchar_t *str,
possible completion of the string, the remainder of the string is
inserted into the out vector.
*/
-static bool wildcard_complete_internal(const wcstring &orig,
- const wchar_t *str,
- const wchar_t *wc,
- bool is_first,
- const wchar_t *desc,
- wcstring (*desc_func)(const wcstring &),
- std::vector<completion_t> &out,
- int flags )
+static bool wildcard_complete_internal(const wcstring &orig,
+ const wchar_t *str,
+ const wchar_t *wc,
+ bool is_first,
+ const wchar_t *desc,
+ wcstring (*desc_func)(const wcstring &),
+ std::vector<completion_t> &out,
+ int flags )
{
- if( !wc || ! str || orig.empty())
- {
- debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ );
- return 0;
- }
-
- if( *wc == 0 &&
- ( (str[0] != L'.') || (!is_first)) )
- {
- wcstring out_completion;
+ if( !wc || ! str || orig.empty())
+ {
+ debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ );
+ return 0;
+ }
+
+ if( *wc == 0 &&
+ ( (str[0] != L'.') || (!is_first)) )
+ {
+ wcstring out_completion;
wcstring out_desc = (desc ? desc : L"");
- if( flags & COMPLETE_NO_CASE )
- {
- out_completion = orig;
- }
- else
- {
- out_completion = str;
- }
+ if( flags & COMPLETE_NO_CASE )
+ {
+ out_completion = orig;
+ }
+ else
+ {
+ out_completion = str;
+ }
size_t complete_sep_loc = out_completion.find(PROG_COMPLETE_SEP);
if (complete_sep_loc != wcstring::npos)
- {
- /* This completion has an embedded description, do not use the generic description */
+ {
+ /* This completion has an embedded description, do not use the generic description */
out_desc.assign(out_completion, complete_sep_loc + 1, out_completion.size() - complete_sep_loc - 1);
- out_completion.resize(complete_sep_loc);
- }
- else
- {
- if( desc_func )
- {
- /*
- A description generating function is specified, call
- it. If it returns something, use that as the
- description.
- */
- wcstring func_desc = desc_func( orig );
- if (! func_desc.empty())
- out_desc = func_desc;
- }
-
- }
-
+ out_completion.resize(complete_sep_loc);
+ }
+ else
+ {
+ if( desc_func )
+ {
+ /*
+ A description generating function is specified, call
+ it. If it returns something, use that as the
+ description.
+ */
+ wcstring func_desc = desc_func( orig );
+ if (! func_desc.empty())
+ out_desc = func_desc;
+ }
+
+ }
+
/* Note: out_completion may be empty if the completion really is empty, e.g. tab-completing 'foo' when a file 'foo' exists. */
append_completion(out, out_completion, out_desc, flags);
- return true;
- }
-
-
- if( *wc == ANY_STRING )
- {
- bool res=false;
-
- /* Ignore hidden file */
- if( is_first && str[0] == L'.' )
- return false;
-
- /* Try all submatches */
- do
- {
- res = wildcard_complete_internal( orig, str, wc+1, 0, desc, desc_func, out, flags );
- if (res)
- break;
- }
- while (*str++ != 0);
- return res;
-
- }
- else if( *wc == ANY_CHAR )
- {
- return wildcard_complete_internal( orig, str+1, wc+1, 0, desc, desc_func, out, flags );
- }
- else if( *wc == *str )
- {
- return wildcard_complete_internal( orig, str+1, wc+1, 0, desc, desc_func, out, flags );
- }
- else if( towlower(*wc) == towlower(*str) )
- {
- return wildcard_complete_internal( orig, str+1, wc+1, 0, desc, desc_func, out, flags | COMPLETE_NO_CASE );
- }
- return false;
+ return true;
+ }
+
+
+ if( *wc == ANY_STRING )
+ {
+ bool res=false;
+
+ /* Ignore hidden file */
+ if( is_first && str[0] == L'.' )
+ return false;
+
+ /* Try all submatches */
+ do
+ {
+ res = wildcard_complete_internal( orig, str, wc+1, 0, desc, desc_func, out, flags );
+ if (res)
+ break;
+ }
+ while (*str++ != 0);
+ return res;
+
+ }
+ else if( *wc == ANY_CHAR )
+ {
+ return wildcard_complete_internal( orig, str+1, wc+1, 0, desc, desc_func, out, flags );
+ }
+ else if( *wc == *str )
+ {
+ return wildcard_complete_internal( orig, str+1, wc+1, 0, desc, desc_func, out, flags );
+ }
+ else if( towlower(*wc) == towlower(*str) )
+ {
+ return wildcard_complete_internal( orig, str+1, wc+1, 0, desc, desc_func, out, flags | COMPLETE_NO_CASE );
+ }
+ return false;
}
bool wildcard_complete(const wcstring &str,
- const wchar_t *wc,
- const wchar_t *desc,
- wcstring (*desc_func)(const wcstring &),
- std::vector<completion_t> &out,
- int flags )
+ const wchar_t *wc,
+ const wchar_t *desc,
+ wcstring (*desc_func)(const wcstring &),
+ std::vector<completion_t> &out,
+ int flags )
{
- bool res;
- res = wildcard_complete_internal( str, str.c_str(), wc, true, desc, desc_func, out, flags );
- return res;
+ bool res;
+ res = wildcard_complete_internal( str, str.c_str(), wc, true, desc, desc_func, out, flags );
+ return res;
}
bool wildcard_match( const wcstring &str, const wcstring &wc )
{
- return wildcard_match2( str.c_str(), wc.c_str(), true );
+ return wildcard_match2( str.c_str(), wc.c_str(), true );
}
/**
- Creates a path from the specified directory and filename.
+ Creates a path from the specified directory and filename.
*/
static wcstring make_path(const wcstring &base_dir, const wcstring &name) {
return base_dir + name;
@@ -331,35 +331,35 @@ static wcstring complete_get_desc_suffix_internal( const wcstring &suff )
{
wcstring cmd = wcstring(SUFFIX_CMD_STR) + suff;
-
+
wcstring_list_t lst;
wcstring desc;
-
- if( exec_subshell( cmd, lst ) != -1 )
- {
- if( lst.size()>0 )
- {
+
+ if( exec_subshell( cmd, lst ) != -1 )
+ {
+ if( lst.size()>0 )
+ {
const wcstring & ln = lst.at(0);
- if( ln.size() > 0 && ln != L"unknown" )
- {
- desc = ln;
- /*
- I have decided I prefer to have the description
- begin in uppercase and the whole universe will just
- have to accept it. Hah!
- */
- desc[0]=towupper(desc[0]);
- }
- }
- }
-
- if( desc.empty() )
- {
- desc = COMPLETE_FILE_DESC;
- }
-
+ if( ln.size() > 0 && ln != L"unknown" )
+ {
+ desc = ln;
+ /*
+ I have decided I prefer to have the description
+ begin in uppercase and the whole universe will just
+ have to accept it. Hah!
+ */
+ desc[0]=towupper(desc[0]);
+ }
+ }
+ }
+
+ if( desc.empty() )
+ {
+ desc = COMPLETE_FILE_DESC;
+ }
+
suffix_map[suff] = desc.c_str();
- return desc;
+ return desc;
}
@@ -369,34 +369,34 @@ static wcstring complete_get_desc_suffix_internal( const wcstring &suff )
static wcstring complete_get_desc_suffix( const wchar_t *suff_orig )
{
- size_t len;
- wchar_t *suff;
- wchar_t *pos;
- wchar_t *tmp;
-
- len = wcslen(suff_orig );
-
- if( len == 0 )
- return COMPLETE_FILE_DESC;
-
- suff = wcsdup(suff_orig);
-
- /*
- Drop characters that are commonly used as backup suffixes from the suffix
- */
- for( pos=suff; *pos; pos++ )
- {
- if( wcschr( L"?;#~@&", *pos ) )
- {
- *pos=0;
- break;
- }
- }
-
- tmp = escape( suff, 1 );
- free(suff);
- suff = tmp;
-
+ size_t len;
+ wchar_t *suff;
+ wchar_t *pos;
+ wchar_t *tmp;
+
+ len = wcslen(suff_orig );
+
+ if( len == 0 )
+ return COMPLETE_FILE_DESC;
+
+ suff = wcsdup(suff_orig);
+
+ /*
+ Drop characters that are commonly used as backup suffixes from the suffix
+ */
+ for( pos=suff; *pos; pos++ )
+ {
+ if( wcschr( L"?;#~@&", *pos ) )
+ {
+ *pos=0;
+ break;
+ }
+ }
+
+ tmp = escape( suff, 1 );
+ free(suff);
+ suff = tmp;
+
std::map<wcstring, wcstring>::iterator iter = suffix_map.find(suff);
wcstring desc;
if (iter != suffix_map.end()) {
@@ -405,9 +405,9 @@ static wcstring complete_get_desc_suffix( const wchar_t *suff_orig )
desc = complete_get_desc_suffix_internal( suff );
}
- free( suff );
+ free( suff );
- return desc;
+ return desc;
}
@@ -421,130 +421,130 @@ static wcstring complete_get_desc_suffix( const wchar_t *suff_orig )
\param lbuf The struct buf output of calling lstat on the file
\param stat_res The result of calling stat on the file
\param buf The struct buf output of calling stat on the file
- \param err The errno value after a failed stat call on the file.
+ \param err The errno value after a failed stat call on the file.
*/
-static wcstring file_get_desc( const wcstring &filename,
- int lstat_res,
- struct stat lbuf,
- int stat_res,
- struct stat buf,
- int err )
+static wcstring file_get_desc( const wcstring &filename,
+ int lstat_res,
+ struct stat lbuf,
+ int stat_res,
+ struct stat buf,
+ int err )
{
- const wchar_t *suffix;
-
- if( !lstat_res )
- {
- if( S_ISLNK(lbuf.st_mode))
- {
- if( !stat_res )
- {
- if( S_ISDIR(buf.st_mode) )
- {
- return COMPLETE_DIRECTORY_SYMLINK_DESC;
- }
- else
- {
-
- if( ( buf.st_mode & S_IXUSR ) ||
- ( buf.st_mode & S_IXGRP ) ||
- ( buf.st_mode & S_IXOTH ) )
- {
-
- if( waccess( filename, X_OK ) == 0 )
- {
- /*
- Weird group permissions and other such
- issues make it non-trivial to find out
- if we can actually execute a file using
- the result from stat. It is much safer
- to use the access function, since it
- tells us exactly what we want to know.
- */
- return COMPLETE_EXEC_LINK_DESC;
- }
- }
- }
-
- return COMPLETE_SYMLINK_DESC;
-
- }
- else
- {
- switch( err )
- {
- case ENOENT:
- {
- return COMPLETE_ROTTEN_SYMLINK_DESC;
- }
-
- case ELOOP:
- {
- return COMPLETE_LOOP_SYMLINK_DESC;
- }
- }
- /*
- On unknown errors we do nothing. The file will be
- given the default 'File' description or one based on the suffix.
- */
- }
-
- }
- else if( S_ISCHR(buf.st_mode) )
- {
- return COMPLETE_CHAR_DESC;
- }
- else if( S_ISBLK(buf.st_mode) )
- {
- return COMPLETE_BLOCK_DESC;
- }
- else if( S_ISFIFO(buf.st_mode) )
- {
- return COMPLETE_FIFO_DESC;
- }
- else if( S_ISSOCK(buf.st_mode))
- {
- return COMPLETE_SOCKET_DESC;
- }
- else if( S_ISDIR(buf.st_mode) )
- {
- return COMPLETE_DIRECTORY_DESC;
- }
- else
- {
- if( ( buf.st_mode & S_IXUSR ) ||
- ( buf.st_mode & S_IXGRP ) ||
- ( buf.st_mode & S_IXOTH ) )
- {
-
- if( waccess( filename, X_OK ) == 0 )
- {
- /*
- Weird group permissions and other such issues
- make it non-trivial to find out if we can
- actually execute a file using the result from
- stat. It is much safer to use the access
- function, since it tells us exactly what we want
- to know.
- */
- return COMPLETE_EXEC_DESC;
- }
- }
- }
- }
-
- suffix = wcsrchr( filename.c_str(), L'.' );
- if( suffix != 0 && !wcsrchr( suffix, L'/' ) )
- {
- return complete_get_desc_suffix( suffix );
- }
-
- return COMPLETE_FILE_DESC ;
+ const wchar_t *suffix;
+
+ if( !lstat_res )
+ {
+ if( S_ISLNK(lbuf.st_mode))
+ {
+ if( !stat_res )
+ {
+ if( S_ISDIR(buf.st_mode) )
+ {
+ return COMPLETE_DIRECTORY_SYMLINK_DESC;
+ }
+ else
+ {
+
+ if( ( buf.st_mode & S_IXUSR ) ||
+ ( buf.st_mode & S_IXGRP ) ||
+ ( buf.st_mode & S_IXOTH ) )
+ {
+
+ if( waccess( filename, X_OK ) == 0 )
+ {
+ /*
+ Weird group permissions and other such
+ issues make it non-trivial to find out
+ if we can actually execute a file using
+ the result from stat. It is much safer
+ to use the access function, since it
+ tells us exactly what we want to know.
+ */
+ return COMPLETE_EXEC_LINK_DESC;
+ }
+ }
+ }
+
+ return COMPLETE_SYMLINK_DESC;
+
+ }
+ else
+ {
+ switch( err )
+ {
+ case ENOENT:
+ {
+ return COMPLETE_ROTTEN_SYMLINK_DESC;
+ }
+
+ case ELOOP:
+ {
+ return COMPLETE_LOOP_SYMLINK_DESC;
+ }
+ }
+ /*
+ On unknown errors we do nothing. The file will be
+ given the default 'File' description or one based on the suffix.
+ */
+ }
+
+ }
+ else if( S_ISCHR(buf.st_mode) )
+ {
+ return COMPLETE_CHAR_DESC;
+ }
+ else if( S_ISBLK(buf.st_mode) )
+ {
+ return COMPLETE_BLOCK_DESC;
+ }
+ else if( S_ISFIFO(buf.st_mode) )
+ {
+ return COMPLETE_FIFO_DESC;
+ }
+ else if( S_ISSOCK(buf.st_mode))
+ {
+ return COMPLETE_SOCKET_DESC;
+ }
+ else if( S_ISDIR(buf.st_mode) )
+ {
+ return COMPLETE_DIRECTORY_DESC;
+ }
+ else
+ {
+ if( ( buf.st_mode & S_IXUSR ) ||
+ ( buf.st_mode & S_IXGRP ) ||
+ ( buf.st_mode & S_IXOTH ) )
+ {
+
+ if( waccess( filename, X_OK ) == 0 )
+ {
+ /*
+ Weird group permissions and other such issues
+ make it non-trivial to find out if we can
+ actually execute a file using the result from
+ stat. It is much safer to use the access
+ function, since it tells us exactly what we want
+ to know.
+ */
+ return COMPLETE_EXEC_DESC;
+ }
+ }
+ }
+ }
+
+ suffix = wcsrchr( filename.c_str(), L'.' );
+ if( suffix != 0 && !wcsrchr( suffix, L'/' ) )
+ {
+ return complete_get_desc_suffix( suffix );
+ }
+
+ return COMPLETE_FILE_DESC ;
}
/**
- Add the specified filename if it matches the specified wildcard.
+ Add the specified filename if it matches the specified wildcard.
If the filename matches, first get the description of the specified
filename. If this is a regular file, append the filesize to the
@@ -556,78 +556,78 @@ static wcstring file_get_desc( const wcstring &filename,
\param wc the wildcard to match against
\param is_cmd whether we are performing command completion
*/
-static void wildcard_completion_allocate( std::vector<completion_t> &list,
- const wcstring &fullname,
- const wcstring &completion,
- const wchar_t *wc,
+static void wildcard_completion_allocate( std::vector<completion_t> &list,
+ const wcstring &fullname,
+ const wcstring &completion,
+ const wchar_t *wc,
expand_flags_t expand_flags)
{
- struct stat buf, lbuf;
+ struct stat buf, lbuf;
wcstring sb;
- wcstring munged_completion;
-
- int flags = 0;
- int stat_res, lstat_res;
- int stat_errno=0;
-
- long long sz;
-
- /*
- If the file is a symlink, we need to stat both the file itself
- _and_ the destination file. But we try to avoid this with
- non-symlinks by first doing an lstat, and if the file is not a
- link we copy the results over to the regular stat buffer.
- */
- if( ( lstat_res = lwstat( fullname, &lbuf ) ) )
- {
+ wcstring munged_completion;
+
+ int flags = 0;
+ int stat_res, lstat_res;
+ int stat_errno=0;
+
+ long long sz;
+
+ /*
+ If the file is a symlink, we need to stat both the file itself
+ _and_ the destination file. But we try to avoid this with
+ non-symlinks by first doing an lstat, and if the file is not a
+ link we copy the results over to the regular stat buffer.
+ */
+ if( ( lstat_res = lwstat( fullname, &lbuf ) ) )
+ {
/* lstat failed! */
- sz=-1;
- stat_res = lstat_res;
- }
- else
- {
- if (S_ISLNK(lbuf.st_mode))
- {
-
- if( ( stat_res = wstat( fullname, &buf ) ) )
- {
- sz=-1;
- }
- else
- {
- sz = (long long)buf.st_size;
- }
-
- /*
- In order to differentiate between e.g. rotten symlinks
- and symlink loops, we also need to know the error status of wstat.
- */
- stat_errno = errno;
- }
- else
- {
- stat_res = lstat_res;
- memcpy( &buf, &lbuf, sizeof( struct stat ) );
- sz = (long long)buf.st_size;
- }
- }
-
-
+ sz=-1;
+ stat_res = lstat_res;
+ }
+ else
+ {
+ if (S_ISLNK(lbuf.st_mode))
+ {
+
+ if( ( stat_res = wstat( fullname, &buf ) ) )
+ {
+ sz=-1;
+ }
+ else
+ {
+ sz = (long long)buf.st_size;
+ }
+
+ /*
+ In order to differentiate between e.g. rotten symlinks
+ and symlink loops, we also need to know the error status of wstat.
+ */
+ stat_errno = errno;
+ }
+ else
+ {
+ stat_res = lstat_res;
+ memcpy( &buf, &lbuf, sizeof( struct stat ) );
+ sz = (long long)buf.st_size;
+ }
+ }
+
+
bool wants_desc = ! (expand_flags & EXPAND_NO_DESCRIPTIONS);
- wcstring desc;
+ wcstring desc;
if (wants_desc)
desc = file_get_desc( fullname.c_str(), lstat_res, lbuf, stat_res, buf, stat_errno );
-
- if( sz >= 0 && S_ISDIR(buf.st_mode) )
- {
- flags = flags | COMPLETE_NO_SPACE;
+
+ if( sz >= 0 && S_ISDIR(buf.st_mode) )
+ {
+ flags = flags | COMPLETE_NO_SPACE;
munged_completion = completion;
munged_completion.push_back(L'/');
if (wants_desc)
sb.append(desc);
- }
- else
- {
+ }
+ else
+ {
if (wants_desc)
{
if (! desc.empty())
@@ -637,10 +637,10 @@ static void wildcard_completion_allocate( std::vector<completion_t> &list,
}
sb.append(format_size(sz));
}
- }
-
+ }
+
const wcstring &completion_to_use = munged_completion.empty() ? completion : munged_completion;
- wildcard_complete(completion_to_use, wc, sb.c_str(), NULL, list, flags);
+ wildcard_complete(completion_to_use, wc, sb.c_str(), NULL, list, flags);
}
/**
@@ -649,30 +649,30 @@ static void wildcard_completion_allocate( std::vector<completion_t> &list,
EXECUTABLES_ONLY and DIRECTORIES_ONLY.
*/
static int test_flags( const wchar_t *filename,
- int flags )
+ int flags )
{
- if( flags & DIRECTORIES_ONLY )
- {
- struct stat buf;
- if( wstat( filename, &buf ) == -1 )
- {
- return 0;
- }
-
- if( !S_ISDIR( buf.st_mode ) )
- {
- return 0;
- }
- }
-
-
- if( flags & EXECUTABLES_ONLY )
- {
- if ( waccess( filename, X_OK ) != 0)
- return 0;
- }
-
- return 1;
+ if( flags & DIRECTORIES_ONLY )
+ {
+ struct stat buf;
+ if( wstat( filename, &buf ) == -1 )
+ {
+ return 0;
+ }
+
+ if( !S_ISDIR( buf.st_mode ) )
+ {
+ return 0;
+ }
+ }
+
+
+ if( flags & EXECUTABLES_ONLY )
+ {
+ if ( waccess( filename, X_OK ) != 0)
+ return 0;
+ }
+
+ return 1;
}
/** Appends a completion to the completion list, if the string is missing from the set. */
@@ -692,415 +692,415 @@ typedef std::pair<dev_t, ino_t> file_id_t;
This function traverses the relevant directory tree looking for
matches, and recurses when needed to handle wildcrards spanning
multiple components and recursive wildcards.
-
+
Because this function calls itself recursively with substrings,
it's important that the parameters be raw pointers instead of wcstring,
which would be too expensive to construct for all substrings.
*/
static int wildcard_expand_internal( const wchar_t *wc,
- const wchar_t *base_dir,
- expand_flags_t flags,
- std::vector<completion_t> &out,
+ const wchar_t *base_dir,
+ expand_flags_t flags,
+ std::vector<completion_t> &out,
std::set<wcstring> &completion_set,
std::set<file_id_t> &visited_files
)
{
- /* 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 */
- size_t base_len;
-
- /* Variables for testing for presense of recursive wildcards */
- const wchar_t *wc_recursive;
- bool is_recursive;
-
- /* Slightly 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 *
- */
- size_t 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, completion_set, visited_files );
- 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 )
- {
+ /* 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 */
+ size_t base_len;
+
+ /* Variables for testing for presense of recursive wildcards */
+ const wchar_t *wc_recursive;
+ bool is_recursive;
+
+ /* Slightly 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 *
+ */
+ size_t 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, completion_set, visited_files );
+ 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"",
+ 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;
+ }
+ }
+ }
+ }
+ else
+ {
+ res = 1;
insert_completion_if_missing(base_dir, out, completion_set);
- }
- }
- else
- {
- /*
- This is the last wildcard segment, and it is not empty. Match files/directories.
- */
+ }
+ }
+ else
+ {
+ /*
+ This is the last wildcard segment, and it is not empty. Match files/directories.
+ */
wcstring next;
- while (wreaddir(dir, 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,
+ 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,
+ wc,
flags);
-
- }
- }
- }
- else
- {
- if( wildcard_match2( name, wc, true ) )
- {
+
+ }
+ }
+ }
+ else
+ {
+ if( wildcard_match2( name, wc, true ) )
+ {
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)
- {
+ 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)
+ {
insert_completion_if_missing(long_name, out, completion_set);
- }
- 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 directory 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 );
-
+ }
+ 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 directory 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, true );
- 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, true );
- free( wc_sub );
- }
-
- if( whole_match || partial_match )
- {
- 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 )
- {
+ 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, true );
+ 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, true );
+ free( wc_sub );
+ }
+
+ if( whole_match || partial_match )
+ {
+ 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 )
+ {
// Insert a "file ID" into visited_files
// If the insertion fails, we've already visited this file (i.e. a symlink loop)
// If we're not recursive, insert anyways (in case we loop back around in a future recursive segment), but continue on; the idea being that literal path components should still work
const file_id_t file_id(buf.st_dev, buf.st_ino);
- if( S_ISDIR(buf.st_mode) && (visited_files.insert(file_id).second || ! is_recursive))
- {
- size_t 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( S_ISDIR(buf.st_mode) && (visited_files.insert(file_id).second || ! is_recursive))
+ {
+ size_t 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,
completion_set,
visited_files );
- 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;
+
+ }
+
+ /*
+ Recursive matching
+ */
+ if( partial_match )
+ {
+
+ new_res = wildcard_expand_internal( wcschr( wc, ANY_STRING_RECURSIVE ),
+ new_dir,
+ flags | WILDCARD_RECURSIVE,
+ out,
completion_set,
visited_files);
- if( new_res == -1 )
- {
- res = -1;
- break;
- }
- res |= new_res;
-
- }
- }
- }
- }
- }
- }
-
- free( wc_str );
- free( new_dir );
- }
- closedir( dir );
-
- return res;
+ if( new_res == -1 )
+ {
+ res = -1;
+ break;
+ }
+ res |= new_res;
+
+ }
+ }
+ }
+ }
+ }
+ }
+
+ free( wc_str );
+ free( new_dir );
+ }
+ closedir( dir );
+
+ return res;
}
int wildcard_expand( const wchar_t *wc,
- const wchar_t *base_dir,
- expand_flags_t flags,
- std::vector<completion_t> &out )
+ const wchar_t *base_dir,
+ expand_flags_t flags,
+ std::vector<completion_t> &out )
{
- size_t c = out.size();
-
+ size_t c = out.size();
+
/* Make a set of used completion strings so we can do fast membership tests inside wildcard_expand_internal. Otherwise wildcards like '**' are very slow, because we end up with an N^2 membership test.
*/
- std::set<wcstring> completion_set;
- for (std::vector<completion_t>::const_iterator iter = out.begin(); iter != out.end(); ++iter)
- {
+ std::set<wcstring> completion_set;
+ for (std::vector<completion_t>::const_iterator iter = out.begin(); iter != out.end(); ++iter)
+ {
completion_set.insert(iter->completion);
- }
-
- std::set<file_id_t> visited_files;
- int res = wildcard_expand_internal( wc, base_dir, flags, out, completion_set, visited_files );
-
- if( flags & ACCEPT_INCOMPLETE )
- {
+ }
+
+ std::set<file_id_t> visited_files;
+ int res = wildcard_expand_internal( wc, base_dir, flags, out, completion_set, visited_files );
+
+ if( flags & ACCEPT_INCOMPLETE )
+ {
wcstring wc_base;
- const wchar_t *wc_base_ptr = wcsrchr( wc, L'/' );
- if( wc_base_ptr )
- {
+ const wchar_t *wc_base_ptr = wcsrchr( wc, L'/' );
+ if( wc_base_ptr )
+ {
wc_base = wcstring(wc, (wc_base_ptr-wc)+1);
- }
-
- for( size_t i=c; i<out.size(); i++ )
- {
- completion_t &c = out.at( i );
-
- if( c.flags & COMPLETE_NO_CASE )
- {
- c.completion = format_string(L"%ls%ls%ls", base_dir, wc_base.c_str(), c.completion.c_str());
- }
- }
- }
- return res;
+ }
+
+ for( size_t i=c; i<out.size(); i++ )
+ {
+ completion_t &c = out.at( i );
+
+ if( c.flags & COMPLETE_NO_CASE )
+ {
+ c.completion = format_string(L"%ls%ls%ls", base_dir, wc_base.c_str(), c.completion.c_str());
+ }
+ }
+ }
+ return res;
}
int wildcard_expand_string(const wcstring &wc, const wcstring &base_dir, expand_flags_t flags, std::vector<completion_t> &outputs )