mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 12:53:13 +00:00
Implemented process expansion on OS X
Also fixed issue where process expansion would always fail for processes with spaces Fixes https://github.com/fish-shell/fish-shell/issues/56
This commit is contained in:
parent
548ea1e54a
commit
33c6410809
5 changed files with 367 additions and 209 deletions
34
common.cpp
34
common.cpp
|
@ -136,6 +136,40 @@ wcstring_list_t completions_to_wcstring_list( const std::vector<completion_t> &l
|
||||||
return strings;
|
return strings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fgetws2(wcstring *s, FILE *f)
|
||||||
|
{
|
||||||
|
int i=0;
|
||||||
|
wint_t c;
|
||||||
|
|
||||||
|
while( 1 )
|
||||||
|
{
|
||||||
|
errno=0;
|
||||||
|
|
||||||
|
c = getwc( f );
|
||||||
|
|
||||||
|
if( errno == EILSEQ )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( c )
|
||||||
|
{
|
||||||
|
/* End of line */
|
||||||
|
case WEOF:
|
||||||
|
case L'\n':
|
||||||
|
case L'\0':
|
||||||
|
return i;
|
||||||
|
/* Ignore carriage returns */
|
||||||
|
case L'\r':
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
i++;
|
||||||
|
s->push_back((wchar_t)c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int fgetws2( wchar_t **b, int *len, FILE *f )
|
int fgetws2( wchar_t **b, int *len, FILE *f )
|
||||||
{
|
{
|
||||||
|
|
3
common.h
3
common.h
|
@ -206,6 +206,9 @@ wcstring_list_t completions_to_wcstring_list( const std::vector<completion_t> &c
|
||||||
*/
|
*/
|
||||||
int fgetws2( wchar_t **buff, int *len, FILE *f );
|
int fgetws2( wchar_t **buff, int *len, FILE *f );
|
||||||
|
|
||||||
|
/** Like fgetws2, but reads into a string */
|
||||||
|
int fgetws2(wcstring *s, FILE *f);
|
||||||
|
|
||||||
void sort_strings( std::vector<wcstring> &strings);
|
void sort_strings( std::vector<wcstring> &strings);
|
||||||
|
|
||||||
void sort_completions( std::vector<completion_t> &strings);
|
void sort_completions( std::vector<completion_t> &strings);
|
||||||
|
|
534
expand.cpp
534
expand.cpp
|
@ -16,11 +16,13 @@ parameter expansion.
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -121,6 +123,8 @@ parameter expansion.
|
||||||
*/
|
*/
|
||||||
#define UNCLEAN L"$*?\\\"'({})"
|
#define UNCLEAN L"$*?\\\"'({})"
|
||||||
|
|
||||||
|
static void remove_internal_separator( wcstring &s, bool conv );
|
||||||
|
|
||||||
int expand_is_clean( const wchar_t *in )
|
int expand_is_clean( const wchar_t *in )
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -259,59 +263,288 @@ static int iswnumeric( const wchar_t *n )
|
||||||
See if the process described by \c proc matches the commandline \c
|
See if the process described by \c proc matches the commandline \c
|
||||||
cmd
|
cmd
|
||||||
*/
|
*/
|
||||||
static int match_pid( const wchar_t *cmd,
|
static bool match_pid( const wchar_t *cmd,
|
||||||
const wchar_t *proc,
|
const wchar_t *proc,
|
||||||
int flags,
|
int flags,
|
||||||
int *offset)
|
int *offset)
|
||||||
{
|
{
|
||||||
/* Test for direct match */
|
/* Test for a direct match. If the proc string is empty (e.g. the user tries to complete against %), then return an offset pointing at the base command. That ensures that you don't see a bunch of dumb paths when completing against all processes. */
|
||||||
|
if( proc[0] != L'\0' && wcsncmp( cmd, proc, wcslen( proc ) ) == 0 )
|
||||||
if( wcsncmp( cmd, proc, wcslen( proc ) ) == 0 )
|
|
||||||
{
|
{
|
||||||
if( offset )
|
if( offset )
|
||||||
*offset = 0;
|
*offset = 0;
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Get the command to match against. We're only interested in the last path component. */
|
||||||
Test if the commandline is a path to the command, if so we try
|
const wcstring base_cmd = wbasename(cmd);
|
||||||
to match against only the command part
|
|
||||||
*/
|
bool result = string_prefixes_string(proc, base_cmd);
|
||||||
wchar_t *first_token = tok_first( cmd );
|
if (result)
|
||||||
if( first_token == 0 )
|
{
|
||||||
return 0;
|
/* It's a match. Return the offset within the full command. */
|
||||||
|
if (offset)
|
||||||
|
*offset = wcslen(cmd) - base_cmd.size();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
wchar_t *start=0;
|
/** Helper class for iterating over processes. The names returned have been unescaped (e.g. may include spaces) */
|
||||||
wchar_t prev=0;
|
#ifdef KERN_PROCARGS2
|
||||||
wchar_t *p;
|
|
||||||
|
|
||||||
/*
|
/* BSD / OS X process completions */
|
||||||
This should be done by basename(), if it wasn't for the fact
|
|
||||||
that is does not accept wide strings
|
|
||||||
*/
|
|
||||||
for( p=first_token; *p; p++ )
|
|
||||||
{
|
|
||||||
if( *p == L'/' && prev != L'\\' )
|
|
||||||
start = p;
|
|
||||||
prev = *p;
|
|
||||||
}
|
|
||||||
if( start )
|
|
||||||
{
|
|
||||||
|
|
||||||
if( wcsncmp( start+1, proc, wcslen( proc ) ) == 0 )
|
class process_iterator_t {
|
||||||
{
|
std::vector<pid_t> pids;
|
||||||
if( offset )
|
size_t idx;
|
||||||
*offset = start+1-first_token;
|
|
||||||
|
wcstring name_for_pid(pid_t pid);
|
||||||
free( first_token );
|
|
||||||
|
public:
|
||||||
|
process_iterator_t();
|
||||||
|
bool next_process(wcstring *str, pid_t *pid);
|
||||||
|
};
|
||||||
|
|
||||||
return 1;
|
wcstring process_iterator_t::name_for_pid(pid_t pid)
|
||||||
}
|
{
|
||||||
}
|
wcstring result;
|
||||||
free( first_token );
|
int mib[4], maxarg = 0, numArgs = 0;
|
||||||
|
size_t size = 0;
|
||||||
|
char *args = NULL, *stringPtr = NULL;
|
||||||
|
|
||||||
|
mib[0] = CTL_KERN;
|
||||||
|
mib[1] = KERN_ARGMAX;
|
||||||
|
|
||||||
|
size = sizeof(maxarg);
|
||||||
|
if (sysctl(mib, 2, &maxarg, &size, NULL, 0) == -1) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
args = (char *)malloc( maxarg );
|
||||||
|
if ( args == NULL ) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
mib[0] = CTL_KERN;
|
||||||
|
mib[1] = KERN_PROCARGS2;
|
||||||
|
mib[2] = pid;
|
||||||
|
|
||||||
|
size = (size_t)maxarg;
|
||||||
|
if ( sysctl(mib, 3, args, &size, NULL, 0) == -1 ) {
|
||||||
|
free( args );
|
||||||
|
return result;;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy( &numArgs, args, sizeof(numArgs) );
|
||||||
|
stringPtr = args + sizeof(numArgs);
|
||||||
|
result = str2wcstring(stringPtr);
|
||||||
|
free(args);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid)
|
||||||
|
{
|
||||||
|
wcstring name;
|
||||||
|
pid_t pid = 0;
|
||||||
|
bool result = false;
|
||||||
|
while (idx < pids.size())
|
||||||
|
{
|
||||||
|
pid = pids.at(idx++);
|
||||||
|
name = name_for_pid(pid);
|
||||||
|
if (! name.empty())
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
*out_str = name;
|
||||||
|
*out_pid = pid;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
process_iterator_t::process_iterator_t() : idx(0)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct kinfo_proc * result;
|
||||||
|
bool done;
|
||||||
|
static const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
|
||||||
|
// Declaring name as const requires us to cast it when passing it to
|
||||||
|
// sysctl because the prototype doesn't include the const modifier.
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
|
||||||
|
// We start by calling sysctl with result == NULL and length == 0.
|
||||||
|
// That will succeed, and set length to the appropriate length.
|
||||||
|
// We then allocate a buffer of that size and call sysctl again
|
||||||
|
// with that buffer. If that succeeds, we're done. If that fails
|
||||||
|
// with ENOMEM, we have to throw away our buffer and loop. Note
|
||||||
|
// that the loop causes use to call sysctl with NULL again; this
|
||||||
|
// is necessary because the ENOMEM failure case sets length to
|
||||||
|
// the amount of data returned, not the amount of data that
|
||||||
|
// could have been returned.
|
||||||
|
|
||||||
|
result = NULL;
|
||||||
|
done = false;
|
||||||
|
do {
|
||||||
|
assert(result == NULL);
|
||||||
|
|
||||||
|
// Call sysctl with a NULL buffer.
|
||||||
|
|
||||||
|
length = 0;
|
||||||
|
err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1,
|
||||||
|
NULL, &length,
|
||||||
|
NULL, 0);
|
||||||
|
if (err == -1) {
|
||||||
|
err = errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate an appropriately sized buffer based on the results
|
||||||
|
// from the previous call.
|
||||||
|
|
||||||
|
if (err == 0) {
|
||||||
|
result = (struct kinfo_proc *)malloc(length);
|
||||||
|
if (result == NULL) {
|
||||||
|
err = ENOMEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call sysctl again with the new buffer. If we get an ENOMEM
|
||||||
|
// error, toss away our buffer and start again.
|
||||||
|
|
||||||
|
if (err == 0) {
|
||||||
|
err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1,
|
||||||
|
result, &length,
|
||||||
|
NULL, 0);
|
||||||
|
if (err == -1) {
|
||||||
|
err = errno;
|
||||||
|
}
|
||||||
|
if (err == 0) {
|
||||||
|
done = true;
|
||||||
|
} else if (err == ENOMEM) {
|
||||||
|
assert(result != NULL);
|
||||||
|
free(result);
|
||||||
|
result = NULL;
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (err == 0 && ! done);
|
||||||
|
|
||||||
|
// Clean up and establish post conditions.
|
||||||
|
if (err == 0 && result != NULL)
|
||||||
|
{
|
||||||
|
for (size_t idx = 0; idx < length / sizeof(struct kinfo_proc); idx++)
|
||||||
|
pids.push_back(result[idx].kp_proc.p_pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
free(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* /proc style process completions */
|
||||||
|
class process_iterator_t {
|
||||||
|
DIR *dir;
|
||||||
|
|
||||||
|
public:
|
||||||
|
process_iterator_t();
|
||||||
|
~process_iterator_t();
|
||||||
|
|
||||||
|
bool next_process(wcstring *out_str, pid_t *out_pid);
|
||||||
|
};
|
||||||
|
|
||||||
|
process_iterator_t::process_iterator_t(void)
|
||||||
|
{
|
||||||
|
dir = opendir( "/proc" );
|
||||||
|
}
|
||||||
|
|
||||||
|
process_iterator_t::~process_iterator_t(void)
|
||||||
|
{
|
||||||
|
if (dir)
|
||||||
|
closedir(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid)
|
||||||
|
{
|
||||||
|
wcstring cmd;
|
||||||
|
pid_t pid = 0;
|
||||||
|
while (cmd.empty())
|
||||||
|
{
|
||||||
|
wcstring name;
|
||||||
|
if (! dir || ! wreaddir(dir, name))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!iswnumeric(name.c_str()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
wcstring path = wcstring(L"/proc/") + name;
|
||||||
|
struct stat buf;
|
||||||
|
if (wstat(path, &buf))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( buf.st_uid != getuid() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* remember the pid */
|
||||||
|
pid = (long)wcstol(name.c_str(), NULL, 10);
|
||||||
|
|
||||||
|
/* the 'cmdline' file exists, it should contain the commandline */
|
||||||
|
FILE *cmdfile;
|
||||||
|
if ((cmdfile=wfopen(path + L"/cmdline", "r"))==0)
|
||||||
|
{
|
||||||
|
wcstring full_command_line;
|
||||||
|
signal_block();
|
||||||
|
fgetws2(&full_command_line, cmdfile);
|
||||||
|
signal_unblock();
|
||||||
|
|
||||||
|
/* The command line needs to be escaped */
|
||||||
|
wchar_t *first_arg = tok_first( full_command_line.c_str() );
|
||||||
|
if (first_arg)
|
||||||
|
{
|
||||||
|
cmd = first_arg;
|
||||||
|
free(first_arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef SunOS
|
||||||
|
else if ((cmdfile=wfopen(path + L"/psinfo", "r"))==0)
|
||||||
|
{
|
||||||
|
psinfo_t info;
|
||||||
|
if (fread(&info, sizeof(info), 1, cmdfile))
|
||||||
|
{
|
||||||
|
/* The filename is unescaped */
|
||||||
|
cmd = str2wcstring(info.pr_fname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (cmdfile)
|
||||||
|
fclose(cmdfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = ! cmd.empty();
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
*out_str = cmd;
|
||||||
|
*out_pid = pid;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::vector<wcstring> expand_get_all_process_names(void)
|
||||||
|
{
|
||||||
|
wcstring name;
|
||||||
|
pid_t pid;
|
||||||
|
process_iterator_t iterator;
|
||||||
|
std::vector<wcstring> result;
|
||||||
|
while (iterator.next_process(&name, &pid))
|
||||||
|
{
|
||||||
|
result.push_back(name);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -332,11 +565,6 @@ static int find_process( const wchar_t *proc,
|
||||||
int flags,
|
int flags,
|
||||||
std::vector<completion_t> &out )
|
std::vector<completion_t> &out )
|
||||||
{
|
{
|
||||||
DIR *dir;
|
|
||||||
wchar_t *pdir_name;
|
|
||||||
wchar_t *pfile_name;
|
|
||||||
wchar_t *cmd=0;
|
|
||||||
int sz=0;
|
|
||||||
int found = 0;
|
int found = 0;
|
||||||
|
|
||||||
const job_t *j;
|
const job_t *j;
|
||||||
|
@ -400,7 +628,7 @@ static int find_process( const wchar_t *proc,
|
||||||
{
|
{
|
||||||
int offset;
|
int offset;
|
||||||
|
|
||||||
if( j->command_wcstr() == 0 )
|
if( j->command_is_empty() )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if( match_pid( j->command_wcstr(), proc, flags, &offset ) )
|
if( match_pid( j->command_wcstr(), proc, flags, &offset ) )
|
||||||
|
@ -463,122 +691,29 @@ static int find_process( const wchar_t *proc,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !(dir = opendir( "/proc" )))
|
/* Iterate over all processes */
|
||||||
{
|
wcstring process_name;
|
||||||
/*
|
pid_t process_pid;
|
||||||
This system does not have a /proc filesystem.
|
process_iterator_t iterator;
|
||||||
*/
|
while (iterator.next_process(&process_name, &process_pid))
|
||||||
return 1;
|
{
|
||||||
}
|
int offset;
|
||||||
|
|
||||||
pdir_name = (wchar_t *)malloc( sizeof(wchar_t)*256 );
|
if( match_pid( process_name.c_str(), proc, flags, &offset ) )
|
||||||
pfile_name = (wchar_t *)malloc( sizeof(wchar_t)*64 );
|
{
|
||||||
wcscpy( pdir_name, L"/proc/" );
|
if( flags & ACCEPT_INCOMPLETE )
|
||||||
|
{
|
||||||
wcstring nameStr;
|
completion_allocate( out,
|
||||||
while (wreaddir(dir, nameStr))
|
process_name.c_str() + offset + wcslen(proc),
|
||||||
{
|
COMPLETE_PROCESS_DESC,
|
||||||
const wchar_t *name = nameStr.c_str();
|
0 );
|
||||||
struct stat buf;
|
}
|
||||||
|
else
|
||||||
if( !iswnumeric( name ) )
|
{
|
||||||
continue;
|
out.push_back(completion_t(to_string((long)process_pid)));
|
||||||
|
}
|
||||||
wcscpy( pdir_name + 6, name );
|
}
|
||||||
if( wstat( pdir_name, &buf ) )
|
}
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if( buf.st_uid != getuid() )
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
wcscpy( pfile_name, pdir_name );
|
|
||||||
wcscat( pfile_name, L"/cmdline" );
|
|
||||||
|
|
||||||
if( !wstat( pfile_name, &buf ) )
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
the 'cmdline' file exists, it should contain the commandline
|
|
||||||
*/
|
|
||||||
FILE *cmdfile;
|
|
||||||
|
|
||||||
if((cmdfile=wfopen( pfile_name, "r" ))==0)
|
|
||||||
{
|
|
||||||
wperror( L"fopen" );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
signal_block();
|
|
||||||
fgetws2( &cmd, &sz, cmdfile );
|
|
||||||
signal_unblock();
|
|
||||||
|
|
||||||
fclose( cmdfile );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#ifdef SunOS
|
|
||||||
wcscpy( pfile_name, pdir_name );
|
|
||||||
wcscat( pfile_name, L"/psinfo" );
|
|
||||||
if( !wstat( pfile_name, &buf ) )
|
|
||||||
{
|
|
||||||
psinfo_t info;
|
|
||||||
|
|
||||||
FILE *psfile;
|
|
||||||
|
|
||||||
if((psfile=wfopen( pfile_name, "r" ))==0)
|
|
||||||
{
|
|
||||||
wperror( L"fopen" );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( fread( &info, sizeof(info), 1, psfile ) )
|
|
||||||
{
|
|
||||||
if( cmd != 0 )
|
|
||||||
free( cmd );
|
|
||||||
cmd = str2wcs( info.pr_fname );
|
|
||||||
}
|
|
||||||
fclose( psfile );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
if( cmd != 0 )
|
|
||||||
{
|
|
||||||
*cmd = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( cmd != 0 )
|
|
||||||
{
|
|
||||||
int offset;
|
|
||||||
|
|
||||||
if( match_pid( cmd, proc, flags, &offset ) )
|
|
||||||
{
|
|
||||||
if( flags & ACCEPT_INCOMPLETE )
|
|
||||||
{
|
|
||||||
completion_allocate( out,
|
|
||||||
cmd + offset + wcslen(proc),
|
|
||||||
COMPLETE_PROCESS_DESC,
|
|
||||||
0 );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (name)
|
|
||||||
out.push_back(completion_t(name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( cmd != 0 )
|
|
||||||
free( cmd );
|
|
||||||
|
|
||||||
free( pdir_name );
|
|
||||||
free( pfile_name );
|
|
||||||
|
|
||||||
closedir( dir );
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -586,11 +721,15 @@ static int find_process( const wchar_t *proc,
|
||||||
/**
|
/**
|
||||||
Process id expansion
|
Process id expansion
|
||||||
*/
|
*/
|
||||||
static int expand_pid( const wcstring &instr,
|
static int expand_pid( const wcstring &instr_with_sep,
|
||||||
int flags,
|
int flags,
|
||||||
std::vector<completion_t> &out )
|
std::vector<completion_t> &out )
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/* expand_string calls us with internal separators in instr...sigh */
|
||||||
|
wcstring instr = instr_with_sep;
|
||||||
|
remove_internal_separator(instr, false);
|
||||||
|
|
||||||
if( instr.empty() || instr.at(0) != PROCESS_EXPAND )
|
if( instr.empty() || instr.at(0) != PROCESS_EXPAND )
|
||||||
{
|
{
|
||||||
out.push_back(completion_t(instr));
|
out.push_back(completion_t(instr));
|
||||||
|
@ -1366,49 +1505,28 @@ void expand_tilde( wcstring &input)
|
||||||
Remove any internal separators. Also optionally convert wildcard characters to
|
Remove any internal separators. Also optionally convert wildcard characters to
|
||||||
regular equivalents. This is done to support EXPAND_SKIP_WILDCARDS.
|
regular equivalents. This is done to support EXPAND_SKIP_WILDCARDS.
|
||||||
*/
|
*/
|
||||||
static void remove_internal_separator( const void *s, int conv )
|
static void remove_internal_separator( wcstring &str, bool conv )
|
||||||
{
|
{
|
||||||
wchar_t *in = (wchar_t *)s;
|
/* Remove all instances of INTERNAL_SEPARATOR */
|
||||||
wchar_t *out=in;
|
str.erase(std::remove(str.begin(), str.end(), (wchar_t)INTERNAL_SEPARATOR), str.end());
|
||||||
|
|
||||||
CHECK( s, );
|
/* If conv is true, replace all instances of ANY_CHAR with '?', ANY_STRING with '*', ANY_STRING_RECURSIVE with '*' */
|
||||||
|
if (conv)
|
||||||
while( *in )
|
{
|
||||||
{
|
for (size_t idx = 0; idx < str.size(); idx++)
|
||||||
switch( *in )
|
{
|
||||||
{
|
switch (str.at(idx))
|
||||||
case INTERNAL_SEPARATOR:
|
{
|
||||||
in++;
|
case ANY_CHAR:
|
||||||
break;
|
str.at(idx) = L'?';
|
||||||
|
break;
|
||||||
case ANY_CHAR:
|
case ANY_STRING:
|
||||||
in++;
|
case ANY_STRING_RECURSIVE:
|
||||||
*out++ = conv?L'?':ANY_CHAR;
|
str.at(idx) = L'*';
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case ANY_STRING:
|
}
|
||||||
in++;
|
}
|
||||||
*out++ = conv?L'*':ANY_STRING;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ANY_STRING_RECURSIVE:
|
|
||||||
in++;
|
|
||||||
*out++ = conv?L'*':ANY_STRING_RECURSIVE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
*out++ = *in++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*out=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void remove_internal_separator2( wcstring &s, int conv )
|
|
||||||
{
|
|
||||||
wchar_t *tmp = wcsdup(s.c_str());
|
|
||||||
remove_internal_separator(tmp, conv);
|
|
||||||
s = tmp;
|
|
||||||
free(tmp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1542,7 +1660,7 @@ int expand_string( const wcstring &input, std::vector<completion_t> &output, exp
|
||||||
wcstring next_str = in->at(i).completion;
|
wcstring next_str = in->at(i).completion;
|
||||||
int wc_res;
|
int wc_res;
|
||||||
|
|
||||||
remove_internal_separator2( next_str, EXPAND_SKIP_WILDCARDS & flags );
|
remove_internal_separator( next_str, (EXPAND_SKIP_WILDCARDS & flags) ? true : false );
|
||||||
const wchar_t *next = next_str.c_str();
|
const wchar_t *next = next_str.c_str();
|
||||||
|
|
||||||
if( ((flags & ACCEPT_INCOMPLETE) && (!(flags & EXPAND_SKIP_WILDCARDS))) ||
|
if( ((flags & ACCEPT_INCOMPLETE) && (!(flags & EXPAND_SKIP_WILDCARDS))) ||
|
||||||
|
|
4
expand.h
4
expand.h
|
@ -192,5 +192,9 @@ int expand_is_clean( const wchar_t *in );
|
||||||
*/
|
*/
|
||||||
void expand_variable_error( parser_t &parser, const wchar_t *token, int token_pos, int error_pos );
|
void expand_variable_error( parser_t &parser, const wchar_t *token, int token_pos, int error_pos );
|
||||||
|
|
||||||
|
/**
|
||||||
|
Testing function for getting all process names.
|
||||||
|
*/
|
||||||
|
std::vector<wcstring> expand_get_all_process_names(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -696,7 +696,6 @@ wchar_t *tok_first( const wchar_t *str )
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int tok_get_pos( tokenizer *tok )
|
int tok_get_pos( tokenizer *tok )
|
||||||
{
|
{
|
||||||
CHECK( tok, 0 );
|
CHECK( tok, 0 );
|
||||||
|
|
Loading…
Reference in a new issue