2005-09-20 21:26:39 +08:00
/**\file expand.c
String expansion functions . These functions perform several kinds of
2012-11-18 18:23:22 +08:00
parameter expansion .
2005-09-20 21:26:39 +08:00
*/
2006-08-11 09:18:35 +08:00
# include "config.h"
2005-09-20 21:26:39 +08:00
# include <stdlib.h>
# include <stdio.h>
# include <wchar.h>
# include <string.h>
# include <wctype.h>
# include <errno.h>
# include <pwd.h>
# include <unistd.h>
2013-03-12 04:38:18 +08:00
# include <limits.h>
# include <sys/param.h>
2005-09-20 21:26:39 +08:00
# include <sys/types.h>
2013-05-24 02:03:34 +08:00
# ifdef HAVE_SYS_SYSCTL_H
2012-07-17 03:05:36 +08:00
# include <sys/sysctl.h>
2013-05-24 02:03:34 +08:00
# endif
2005-09-20 21:26:39 +08:00
# include <termios.h>
# include <dirent.h>
# include <sys/stat.h>
# include <unistd.h>
# include <signal.h>
2012-07-17 03:05:36 +08:00
# include <algorithm>
2005-09-20 21:26:39 +08:00
2005-12-26 06:00:44 +08:00
# include <assert.h>
2011-12-27 11:18:46 +08:00
# include <vector>
2005-12-26 06:00:44 +08:00
2005-09-20 21:26:39 +08:00
# ifdef SunOS
# include <procfs.h>
# endif
2006-02-28 21:17:16 +08:00
# include "fallback.h"
2005-09-20 21:26:39 +08:00
# include "util.h"
2006-02-28 21:17:16 +08:00
2005-09-20 21:26:39 +08:00
# include "common.h"
# include "wutil.h"
# include "env.h"
# include "proc.h"
# include "parser.h"
# include "expand.h"
# include "wildcard.h"
# include "exec.h"
2006-02-09 23:50:20 +08:00
# include "signal.h"
2005-09-20 21:26:39 +08:00
# include "tokenizer.h"
# include "complete.h"
2006-07-20 06:55:49 +08:00
2006-01-31 00:51:50 +08:00
# include "parse_util.h"
2005-09-20 21:26:39 +08:00
2006-07-20 21:02:46 +08:00
/**
Error issued on invalid variable name
*/
# define COMPLETE_VAR_DESC _( L"The '$' character begins a variable name. The character '%lc', which directly followed a '$', is not allowed as a part of a variable name, and variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'.")
2012-07-09 09:51:52 +08:00
/**
Error issued on $ ?
*/
# define COMPLETE_YOU_WANT_STATUS _( L"$? is not a valid variable in fish. If you want the exit status of the last command, try $status.")
2006-07-20 21:02:46 +08:00
/**
Error issued on invalid variable name
*/
# define COMPLETE_VAR_NULL_DESC _( L"The '$' begins a variable name. It was given at the end of an argument. Variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'.")
/**
Error issued on invalid variable name
*/
2006-07-21 07:33:19 +08:00
# define COMPLETE_VAR_BRACKET_DESC _( L"Did you mean %ls{$%ls}%ls? The '$' character begins a variable name. A bracket, which directly followed a '$', is not allowed as a part of a variable name, and variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'." )
2006-07-20 21:02:46 +08:00
/**
Error issued on invalid variable name
*/
# define COMPLETE_VAR_PARAN_DESC _( L"Did you mean (COMMAND)? In fish, the '$' character is only used for accessing variables. To learn more about command substitution in fish, type 'help expand-command-substitution'.")
2005-09-20 21:26:39 +08:00
/**
Description for child process
*/
2006-01-17 20:48:30 +08:00
# define COMPLETE_CHILD_PROCESS_DESC _( L"Child process")
2005-09-20 21:26:39 +08:00
/**
Description for non - child process
*/
2006-01-17 20:48:30 +08:00
# define COMPLETE_PROCESS_DESC _( L"Process")
2005-09-20 21:26:39 +08:00
/**
Description for long job
*/
2006-01-17 20:48:30 +08:00
# define COMPLETE_JOB_DESC _( L"Job")
2005-09-20 21:26:39 +08:00
/**
Description for short job . The job command is concatenated
*/
2007-02-24 16:11:31 +08:00
# define COMPLETE_JOB_DESC_VAL _( L"Job: %ls")
2005-09-20 21:26:39 +08:00
/**
Description for the shells own pid
*/
2006-01-17 20:48:30 +08:00
# define COMPLETE_SELF_DESC _( L"Shell process")
2005-09-20 21:26:39 +08:00
/**
Description for the shells own pid
*/
2006-01-17 20:48:30 +08:00
# define COMPLETE_LAST_DESC _( L"Last background job")
2005-09-20 21:26:39 +08:00
/**
String in process expansion denoting ourself
*/
# define SELF_STR L"self"
/**
String in process expansion denoting last background job
*/
# define LAST_STR L"last"
2005-10-26 18:51:02 +08:00
/**
Characters which make a string unclean if they are the first
2009-02-21 18:43:30 +08:00
character of the string . See \ c expand_is_clean ( ) .
2005-10-26 18:51:02 +08:00
*/
# define UNCLEAN_FIRST L"~%"
/**
2009-02-21 18:43:30 +08:00
Unclean characters . See \ c expand_is_clean ( ) .
2005-10-26 18:51:02 +08:00
*/
# define UNCLEAN L"$*?\\\"'({})"
2012-11-19 08:30:30 +08:00
static void remove_internal_separator ( wcstring & s , bool conv ) ;
2012-07-17 03:05:36 +08:00
2012-11-19 08:30:30 +08:00
int expand_is_clean ( const wchar_t * in )
2005-10-26 18:51:02 +08:00
{
2006-01-31 03:53:10 +08:00
2012-11-19 08:30:30 +08:00
const wchar_t * str = in ;
2005-10-26 18:51:02 +08:00
2012-11-19 08:30:30 +08:00
CHECK ( in , 1 ) ;
2006-06-21 08:48:36 +08:00
2012-11-19 08:30:30 +08:00
/*
Test characters that have a special meaning in the first character position
*/
if ( wcschr ( UNCLEAN_FIRST , * str ) )
return 0 ;
/*
Test characters that have a special meaning in any character position
*/
while ( * str )
{
if ( wcschr ( UNCLEAN , * str ) )
return 0 ;
str + + ;
}
2006-01-31 03:53:10 +08:00
2012-11-19 08:30:30 +08:00
return 1 ;
2005-10-26 18:51:02 +08:00
}
2005-09-20 21:26:39 +08:00
/**
2006-01-31 03:53:10 +08:00
Return the environment variable value for the string starting at \ c in .
2005-09-20 21:26:39 +08:00
*/
2012-02-26 10:54:49 +08:00
static env_var_t expand_var ( const wchar_t * in )
2005-09-20 21:26:39 +08:00
{
2012-11-19 08:30:30 +08:00
if ( ! in )
return env_var_t : : missing_var ( ) ;
return env_get_string ( in ) ;
2005-09-20 21:26:39 +08:00
}
/**
Test if the specified string does not contain character which can
not be used inside a quoted string .
*/
2012-11-19 08:30:30 +08:00
static int is_quotable ( const wchar_t * str )
2005-09-20 21:26:39 +08:00
{
2012-11-19 08:30:30 +08:00
switch ( * str )
{
2012-11-19 16:31:03 +08:00
case 0 :
return 1 ;
2012-11-18 18:23:22 +08:00
2012-11-19 16:31:03 +08:00
case L ' \n ' :
case L ' \t ' :
case L ' \r ' :
case L ' \b ' :
case L ' \x1b ' :
return 0 ;
2012-11-18 18:23:22 +08:00
2012-11-19 16:31:03 +08:00
default :
return is_quotable ( str + 1 ) ;
2012-11-19 08:30:30 +08:00
}
return 0 ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
2012-11-19 08:30:30 +08:00
static int is_quotable ( const wcstring & str )
{
2012-02-08 16:15:06 +08:00
return is_quotable ( str . c_str ( ) ) ;
}
2006-01-31 03:53:10 +08:00
2012-11-19 08:30:30 +08:00
wcstring expand_escape_variable ( const wcstring & in )
2012-02-08 16:15:06 +08:00
{
2006-01-31 03:53:10 +08:00
2012-11-19 08:30:30 +08:00
wcstring_list_t lst ;
wcstring buff ;
2006-06-21 08:48:36 +08:00
2012-11-19 08:30:30 +08:00
tokenize_variable_array ( in , lst ) ;
2006-01-31 03:53:10 +08:00
2012-11-19 08:30:30 +08:00
switch ( lst . size ( ) )
{
2012-11-19 16:31:03 +08:00
case 0 :
buff . append ( L " '' " ) ;
break ;
2012-11-18 18:23:22 +08:00
2012-11-19 16:31:03 +08:00
case 1 :
2012-11-19 08:30:30 +08:00
{
2012-11-19 16:31:03 +08:00
const wcstring & el = lst . at ( 0 ) ;
2012-11-19 08:30:30 +08:00
2012-11-19 16:31:03 +08:00
if ( el . find ( L ' ' ) ! = wcstring : : npos & & is_quotable ( el ) )
2012-11-19 08:30:30 +08:00
{
buff . append ( L " ' " ) ;
buff . append ( el ) ;
buff . append ( L " ' " ) ;
}
else
{
buff . append ( escape_string ( el , 1 ) ) ;
}
2012-11-19 16:31:03 +08:00
break ;
}
default :
{
for ( size_t j = 0 ; j < lst . size ( ) ; j + + )
{
const wcstring & el = lst . at ( j ) ;
if ( j )
buff . append ( L " " ) ;
if ( is_quotable ( el ) )
{
buff . append ( L " ' " ) ;
buff . append ( el ) ;
buff . append ( L " ' " ) ;
}
else
{
buff . append ( escape_string ( el , 1 ) ) ;
}
}
2012-11-19 08:30:30 +08:00
}
}
return buff ;
2005-09-20 21:26:39 +08:00
}
/**
Tests if all characters in the wide string are numeric
*/
2012-11-19 08:30:30 +08:00
static int iswnumeric ( const wchar_t * n )
2005-09-20 21:26:39 +08:00
{
2012-11-19 08:30:30 +08:00
for ( ; * n ; n + + )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
if ( * n < L ' 0 ' | | * n > L ' 9 ' )
{
return 0 ;
}
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
return 1 ;
2005-09-20 21:26:39 +08:00
}
/**
See if the process described by \ c proc matches the commandline \ c
cmd
*/
2012-11-19 08:30:30 +08:00
static bool match_pid ( const wcstring & cmd ,
const wchar_t * proc ,
int flags ,
size_t * offset )
2005-09-20 21:26:39 +08:00
{
2012-11-19 08:30:30 +08:00
/* 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 . c_str ( ) , proc , wcslen ( proc ) ) = = 0 )
{
if ( offset )
* offset = 0 ;
return true ;
}
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
/* Get the command to match against. We're only interested in the last path component. */
const wcstring base_cmd = wbasename ( cmd ) ;
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
bool result = string_prefixes_string ( proc , base_cmd ) ;
if ( result )
{
/* It's a match. Return the offset within the full command. */
if ( offset )
2012-07-21 11:39:31 +08:00
* offset = cmd . size ( ) - base_cmd . size ( ) ;
2012-07-17 03:05:36 +08:00
}
return result ;
}
2006-01-31 03:53:10 +08:00
2012-07-17 03:05:36 +08:00
/** Helper class for iterating over processes. The names returned have been unescaped (e.g. may include spaces) */
# ifdef KERN_PROCARGS2
2006-01-31 03:53:10 +08:00
2012-07-17 03:05:36 +08:00
/* BSD / OS X process completions */
2006-01-31 03:53:10 +08:00
2012-11-19 08:30:30 +08:00
class process_iterator_t
{
2012-07-17 03:05:36 +08:00
std : : vector < pid_t > pids ;
size_t idx ;
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
wcstring name_for_pid ( pid_t pid ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
public :
2012-07-17 03:05:36 +08:00
process_iterator_t ( ) ;
bool next_process ( wcstring * str , pid_t * pid ) ;
} ;
2006-12-14 19:58:11 +08:00
2012-07-17 03:05:36 +08:00
wcstring process_iterator_t : : name_for_pid ( pid_t pid )
{
wcstring result ;
int mib [ 4 ] , maxarg = 0 , numArgs = 0 ;
size_t size = 0 ;
char * args = NULL , * stringPtr = NULL ;
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
mib [ 0 ] = CTL_KERN ;
mib [ 1 ] = KERN_ARGMAX ;
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
size = sizeof ( maxarg ) ;
2012-11-19 08:30:30 +08:00
if ( sysctl ( mib , 2 , & maxarg , & size , NULL , 0 ) = = - 1 )
{
2012-07-17 03:05:36 +08:00
return result ;
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
args = ( char * ) malloc ( maxarg ) ;
if ( args = = NULL )
{
2012-07-17 03:05:36 +08:00
return result ;
}
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
mib [ 0 ] = CTL_KERN ;
mib [ 1 ] = KERN_PROCARGS2 ;
mib [ 2 ] = pid ;
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
size = ( size_t ) maxarg ;
2012-11-19 08:30:30 +08:00
if ( sysctl ( mib , 3 , args , & size , NULL , 0 ) = = - 1 )
{
free ( args ) ;
2012-07-17 03:05:36 +08:00
return result ; ;
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
memcpy ( & numArgs , args , sizeof ( numArgs ) ) ;
2012-07-17 03:05:36 +08:00
stringPtr = args + sizeof ( numArgs ) ;
result = str2wcstring ( stringPtr ) ;
free ( args ) ;
return result ;
}
2006-01-31 03:53:10 +08:00
2012-07-17 03:05:36 +08:00
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 ;
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
// 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.
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
result = NULL ;
done = false ;
2012-11-19 08:30:30 +08:00
do
{
2012-07-17 03:05:36 +08:00
assert ( result = = NULL ) ;
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
// Call sysctl with a NULL buffer.
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
length = 0 ;
2012-11-19 08:30:30 +08:00
err = sysctl ( ( int * ) name , ( sizeof ( name ) / sizeof ( * name ) ) - 1 ,
2012-07-17 03:05:36 +08:00
NULL , & length ,
NULL , 0 ) ;
2012-11-19 08:30:30 +08:00
if ( err = = - 1 )
{
2012-07-17 03:05:36 +08:00
err = errno ;
}
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
// Allocate an appropriately sized buffer based on the results
// from the previous call.
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( err = = 0 )
{
2012-07-17 03:05:36 +08:00
result = ( struct kinfo_proc * ) malloc ( length ) ;
2012-11-19 08:30:30 +08:00
if ( result = = NULL )
{
2012-07-17 03:05:36 +08:00
err = ENOMEM ;
}
}
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
// Call sysctl again with the new buffer. If we get an ENOMEM
// error, toss away our buffer and start again.
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( err = = 0 )
{
err = sysctl ( ( int * ) name , ( sizeof ( name ) / sizeof ( * name ) ) - 1 ,
2012-07-17 03:05:36 +08:00
result , & length ,
NULL , 0 ) ;
2012-11-19 08:30:30 +08:00
if ( err = = - 1 )
{
2012-07-17 03:05:36 +08:00
err = errno ;
}
2012-11-19 08:30:30 +08:00
if ( err = = 0 )
{
2012-07-17 03:05:36 +08:00
done = true ;
2012-11-19 08:30:30 +08:00
}
else if ( err = = ENOMEM )
{
2012-07-17 03:05:36 +08:00
assert ( result ! = NULL ) ;
free ( result ) ;
result = NULL ;
err = 0 ;
}
}
2012-11-19 08:30:30 +08:00
}
while ( err = = 0 & & ! done ) ;
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
// 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 ) ;
}
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
if ( result )
2012-11-18 18:23:22 +08:00
free ( result ) ;
2012-07-17 03:05:36 +08:00
}
# else
/* /proc style process completions */
2012-11-19 08:30:30 +08:00
class process_iterator_t
{
2012-07-17 03:05:36 +08:00
DIR * dir ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
public :
2012-07-17 03:05:36 +08:00
process_iterator_t ( ) ;
~ process_iterator_t ( ) ;
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
bool next_process ( wcstring * out_str , pid_t * out_pid ) ;
} ;
process_iterator_t : : process_iterator_t ( void )
{
2012-11-19 08:30:30 +08:00
dir = opendir ( " /proc " ) ;
2012-07-17 03:05:36 +08:00
}
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 ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( ! iswnumeric ( name . c_str ( ) ) )
continue ;
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
wcstring path = wcstring ( L " /proc/ " ) + name ;
struct stat buf ;
2012-11-19 08:30:30 +08:00
if ( wstat ( path , & buf ) )
continue ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( buf . st_uid ! = getuid ( ) )
continue ;
2012-07-17 03:05:36 +08:00
/* remember the pid */
2012-08-05 02:07:42 +08:00
pid = fish_wcstoi ( name . c_str ( ) , NULL , 10 ) ;
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
/* the 'cmdline' file exists, it should contain the commandline */
FILE * cmdfile ;
2012-07-17 03:19:41 +08:00
if ( ( cmdfile = wfopen ( path + L " /cmdline " , " r " ) ) )
2012-07-17 03:05:36 +08:00
{
wcstring full_command_line ;
2012-11-19 08:30:30 +08:00
fgetws2 ( & full_command_line , cmdfile ) ;
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
/* The command line needs to be escaped */
2012-11-23 01:11:36 +08:00
cmd = tok_first ( full_command_line . c_str ( ) ) ;
2012-07-17 03:05:36 +08:00
}
# ifdef SunOS
2012-07-17 03:19:41 +08:00
else if ( ( cmdfile = wfopen ( path + L " /psinfo " , " r " ) ) )
2012-07-17 03:05:36 +08:00
{
psinfo_t info ;
if ( fread ( & info , sizeof ( info ) , 1 , cmdfile ) )
{
/* The filename is unescaped */
cmd = str2wcstring ( info . pr_fname ) ;
}
}
# endif
if ( cmdfile )
fclose ( cmdfile ) ;
}
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
bool result = ! cmd . empty ( ) ;
if ( result )
{
* out_str = cmd ;
* out_pid = pid ;
}
return result ;
}
2006-01-31 03:53:10 +08:00
2012-07-17 03:05:36 +08:00
# 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 ;
2005-09-20 21:26:39 +08:00
}
/**
Searches for a job with the specified job id , or a job or process
which has the string \ c proc as a prefix of its commandline .
2007-02-24 16:11:31 +08:00
If the ACCEPT_INCOMPLETE flag is set , the remaining string for any matches
2005-11-30 03:50:30 +08:00
are inserted .
2005-09-20 21:26:39 +08:00
2007-02-24 16:11:31 +08:00
Otherwise , any job matching the specified string is matched , and
the job pgid is returned . If no job matches , all child processes
are searched . If no child processes match , and < tt > fish < / tt > can
understand the contents of the / proc filesystem , all the users
processes are searched for matches .
2005-09-20 21:26:39 +08:00
*/
2012-11-19 08:30:30 +08:00
static int find_process ( const wchar_t * proc ,
expand_flags_t flags ,
std : : vector < completion_t > & out )
2005-09-20 21:26:39 +08:00
{
2012-11-19 08:30:30 +08:00
int found = 0 ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( ! ( flags & EXPAND_SKIP_JOBS ) )
2012-08-07 15:01:48 +08:00
{
ASSERT_IS_MAIN_THREAD ( ) ;
const job_t * j ;
2006-01-31 03:53:10 +08:00
2013-09-19 08:19:30 +08:00
// do the empty param check first, because an empty string passes our 'numeric' check
if ( wcslen ( proc ) = = 0 )
{
/*
This is an empty job expansion : ' % '
It expands to the last job backgrounded .
*/
job_iterator_t jobs ;
while ( ( j = jobs . next ( ) ) )
{
if ( ! j - > command_is_empty ( ) )
{
append_completion ( out , to_string < long > ( j - > pgid ) ) ;
break ;
}
}
/*
You don ' t * really * want to flip a coin between killing
the last process backgrounded and all processes , do you ?
Let ' s not try other match methods with the solo ' % ' syntax .
*/
found = 1 ;
}
else if ( iswnumeric ( proc ) )
2012-08-07 15:01:48 +08:00
{
/*
This is a numeric job string , like ' % 2 '
*/
2006-01-31 03:53:10 +08:00
2012-11-19 08:30:30 +08:00
if ( flags & ACCEPT_INCOMPLETE )
2012-08-07 15:01:48 +08:00
{
job_iterator_t jobs ;
while ( ( j = jobs . next ( ) ) )
{
wchar_t jid [ 16 ] ;
2012-11-19 08:30:30 +08:00
if ( j - > command_is_empty ( ) )
2012-08-07 15:01:48 +08:00
continue ;
2012-11-19 08:30:30 +08:00
swprintf ( jid , 16 , L " %d " , j - > job_id ) ;
2012-08-07 15:01:48 +08:00
2012-11-19 08:30:30 +08:00
if ( wcsncmp ( proc , jid , wcslen ( proc ) ) = = 0 )
2012-08-07 15:01:48 +08:00
{
wcstring desc_buff = format_string ( COMPLETE_JOB_DESC_VAL , j - > command_wcstr ( ) ) ;
2012-11-19 08:30:30 +08:00
append_completion ( out ,
jid + wcslen ( proc ) ,
desc_buff ,
0 ) ;
2012-08-07 15:01:48 +08:00
}
}
}
else
{
int jid ;
wchar_t * end ;
2012-11-18 18:23:22 +08:00
2012-08-07 15:01:48 +08:00
errno = 0 ;
2012-11-19 08:30:30 +08:00
jid = fish_wcstoi ( proc , & end , 10 ) ;
if ( jid > 0 & & ! errno & & ! * end )
2012-08-07 15:01:48 +08:00
{
2012-11-19 08:30:30 +08:00
j = job_get ( jid ) ;
2013-09-19 08:19:30 +08:00
if ( ( j ! = 0 ) & & ( j - > command_wcstr ( ) ! = 0 ) & & ( ! j - > command_is_empty ( ) ) )
2012-08-07 15:01:48 +08:00
{
2013-09-19 08:19:30 +08:00
append_completion ( out , to_string < long > ( j - > pgid ) ) ;
2012-08-07 15:01:48 +08:00
}
}
}
2013-09-19 08:19:30 +08:00
/*
Stop here so you can ' t match a random process name
when you ' re just trying to use job control .
*/
found = 1 ;
2012-08-07 15:01:48 +08:00
}
2012-11-19 08:30:30 +08:00
if ( found )
2012-08-07 15:01:48 +08:00
return 1 ;
2005-09-20 21:26:39 +08:00
2012-08-07 15:01:48 +08:00
job_iterator_t jobs ;
while ( ( j = jobs . next ( ) ) )
{
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( j - > command_is_empty ( ) )
2012-08-07 15:01:48 +08:00
continue ;
2012-11-18 18:23:22 +08:00
2012-08-07 15:01:48 +08:00
size_t offset ;
2012-11-19 08:30:30 +08:00
if ( match_pid ( j - > command ( ) , proc , flags , & offset ) )
2012-08-07 15:01:48 +08:00
{
2012-11-19 08:30:30 +08:00
if ( flags & ACCEPT_INCOMPLETE )
2012-08-07 15:01:48 +08:00
{
2012-11-19 08:30:30 +08:00
append_completion ( out ,
j - > command_wcstr ( ) + offset + wcslen ( proc ) ,
COMPLETE_JOB_DESC ,
0 ) ;
2012-08-07 15:01:48 +08:00
}
else
{
append_completion ( out , to_string < long > ( j - > pgid ) ) ;
found = 1 ;
}
}
}
2005-09-20 21:26:39 +08:00
2012-11-19 08:30:30 +08:00
if ( found )
2012-08-07 15:01:48 +08:00
{
return 1 ;
}
2005-09-20 21:26:39 +08:00
2012-08-07 15:01:48 +08:00
jobs . reset ( ) ;
while ( ( j = jobs . next ( ) ) )
{
process_t * p ;
2012-11-19 08:30:30 +08:00
if ( j - > command_is_empty ( ) )
2012-08-07 15:01:48 +08:00
continue ;
2012-11-19 08:30:30 +08:00
for ( p = j - > first_process ; p ; p = p - > next )
2012-08-07 15:01:48 +08:00
{
2012-11-19 08:30:30 +08:00
if ( p - > actual_cmd . empty ( ) )
2012-08-07 15:01:48 +08:00
continue ;
2012-11-18 18:23:22 +08:00
2012-08-07 15:01:48 +08:00
size_t offset ;
2012-11-19 08:30:30 +08:00
if ( match_pid ( p - > actual_cmd , proc , flags , & offset ) )
2012-08-07 15:01:48 +08:00
{
2012-11-19 08:30:30 +08:00
if ( flags & ACCEPT_INCOMPLETE )
2012-08-07 15:01:48 +08:00
{
2012-11-19 08:30:30 +08:00
append_completion ( out ,
wcstring ( p - > actual_cmd , offset + wcslen ( proc ) ) ,
COMPLETE_CHILD_PROCESS_DESC ,
0 ) ;
2012-08-07 15:01:48 +08:00
}
else
{
2012-11-19 08:30:30 +08:00
append_completion ( out ,
to_string < long > ( p - > pid ) ,
L " " ,
0 ) ;
2012-08-07 15:01:48 +08:00
found = 1 ;
}
}
}
}
2005-09-20 21:26:39 +08:00
2012-11-19 08:30:30 +08:00
if ( found )
2012-08-07 15:01:48 +08:00
{
return 1 ;
}
}
2005-09-20 21:26:39 +08:00
2012-07-17 03:05:36 +08:00
/* Iterate over all processes */
wcstring process_name ;
pid_t process_pid ;
process_iterator_t iterator ;
while ( iterator . next_process ( & process_name , & process_pid ) )
{
2012-08-05 02:07:42 +08:00
size_t offset ;
2012-11-19 08:30:30 +08:00
if ( match_pid ( process_name , proc , flags , & offset ) )
2012-07-17 03:05:36 +08:00
{
2012-11-19 08:30:30 +08:00
if ( flags & ACCEPT_INCOMPLETE )
2012-07-17 03:05:36 +08:00
{
2012-11-19 08:30:30 +08:00
append_completion ( out ,
process_name . c_str ( ) + offset + wcslen ( proc ) ,
COMPLETE_PROCESS_DESC ,
0 ) ;
2012-07-17 03:05:36 +08:00
}
else
{
2012-07-18 03:47:01 +08:00
append_completion ( out , to_string < long > ( process_pid ) ) ;
2012-07-17 03:05:36 +08:00
}
}
}
2006-01-31 03:53:10 +08:00
2012-11-19 08:30:30 +08:00
return 1 ;
2005-09-20 21:26:39 +08:00
}
/**
Process id expansion
*/
2012-11-19 08:30:30 +08:00
static int expand_pid ( const wcstring & instr_with_sep ,
expand_flags_t flags ,
std : : vector < completion_t > & out )
2005-09-20 21:26:39 +08:00
{
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
/* expand_string calls us with internal separators in instr...sigh */
wcstring instr = instr_with_sep ;
remove_internal_separator ( instr , false ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( instr . empty ( ) | | instr . at ( 0 ) ! = PROCESS_EXPAND )
{
2012-07-18 03:47:01 +08:00
append_completion ( out , instr ) ;
2012-11-19 08:30:30 +08:00
return 1 ;
}
2012-11-18 18:23:22 +08:00
2012-01-31 03:15:06 +08:00
const wchar_t * const in = instr . c_str ( ) ;
2005-09-20 21:26:39 +08:00
2012-11-19 08:30:30 +08:00
if ( flags & ACCEPT_INCOMPLETE )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
if ( wcsncmp ( in + 1 , SELF_STR , wcslen ( in + 1 ) ) = = 0 )
{
append_completion ( out ,
2013-01-15 17:52:03 +08:00
& SELF_STR [ wcslen ( in + 1 ) ] ,
2012-11-19 08:30:30 +08:00
COMPLETE_SELF_DESC ,
0 ) ;
}
else if ( wcsncmp ( in + 1 , LAST_STR , wcslen ( in + 1 ) ) = = 0 )
{
append_completion ( out ,
2013-01-15 17:52:03 +08:00
& LAST_STR [ wcslen ( in + 1 ) ] ,
2012-11-19 08:30:30 +08:00
COMPLETE_LAST_DESC ,
0 ) ;
}
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
else
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
if ( wcscmp ( ( in + 1 ) , SELF_STR ) = = 0 )
{
2012-02-02 08:27:14 +08:00
2012-07-18 03:47:01 +08:00
append_completion ( out , to_string < long > ( getpid ( ) ) ) ;
2006-01-31 03:53:10 +08:00
2012-11-19 08:30:30 +08:00
return 1 ;
}
if ( wcscmp ( ( in + 1 ) , LAST_STR ) = = 0 )
{
if ( proc_last_bg_pid > 0 )
{
2012-07-18 03:47:01 +08:00
append_completion ( out , to_string < long > ( proc_last_bg_pid ) ) ;
2012-11-19 08:30:30 +08:00
}
2006-01-31 03:53:10 +08:00
2012-11-19 08:30:30 +08:00
return 1 ;
}
2012-11-18 18:23:22 +08:00
}
2006-01-31 03:53:10 +08:00
2012-11-19 08:30:30 +08:00
size_t prev = out . size ( ) ;
if ( ! find_process ( in + 1 , flags , out ) )
return 0 ;
2006-01-31 03:53:10 +08:00
2012-11-19 08:30:30 +08:00
if ( prev = = out . size ( ) )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
if ( ! ( flags & ACCEPT_INCOMPLETE ) )
{
return 0 ;
}
2012-11-18 18:23:22 +08:00
}
2006-01-31 03:53:10 +08:00
2012-11-19 08:30:30 +08:00
return 1 ;
2005-09-20 21:26:39 +08:00
}
2012-01-31 03:15:06 +08:00
2006-07-20 21:02:46 +08:00
2012-11-19 08:30:30 +08:00
void expand_variable_error ( parser_t & parser , const wchar_t * token , size_t token_pos , int error_pos )
2006-07-20 21:02:46 +08:00
{
2012-11-19 08:30:30 +08:00
size_t stop_pos = token_pos + 1 ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
switch ( token [ stop_pos ] )
{
2012-11-19 16:31:03 +08:00
case BRACKET_BEGIN :
2012-11-18 18:23:22 +08:00
{
2012-11-19 16:31:03 +08:00
wchar_t * cpy = wcsdup ( token ) ;
* ( cpy + token_pos ) = 0 ;
wchar_t * name = & cpy [ stop_pos + 1 ] ;
wchar_t * end = wcschr ( name , BRACKET_END ) ;
wchar_t * post ;
int is_var = 0 ;
if ( end )
{
post = end + 1 ;
* end = 0 ;
2012-11-19 08:30:30 +08:00
2012-11-19 16:31:03 +08:00
if ( ! wcsvarname ( name ) )
{
is_var = 1 ;
}
}
if ( is_var )
{
parser . error ( SYNTAX_ERROR ,
error_pos ,
COMPLETE_VAR_BRACKET_DESC ,
cpy ,
name ,
post ) ;
}
else
2012-11-19 08:30:30 +08:00
{
2012-11-19 16:31:03 +08:00
parser . error ( SYNTAX_ERROR ,
error_pos ,
COMPLETE_VAR_BRACKET_DESC ,
L " " ,
L " VARIABLE " ,
L " " ) ;
2012-11-19 08:30:30 +08:00
}
2012-11-19 16:31:03 +08:00
free ( cpy ) ;
break ;
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
2012-11-19 16:31:03 +08:00
case INTERNAL_SEPARATOR :
2012-11-19 08:30:30 +08:00
{
parser . error ( SYNTAX_ERROR ,
error_pos ,
2012-11-19 16:31:03 +08:00
COMPLETE_VAR_PARAN_DESC ) ;
break ;
2012-11-19 08:30:30 +08:00
}
2012-11-19 16:31:03 +08:00
case 0 :
2012-11-19 08:30:30 +08:00
{
parser . error ( SYNTAX_ERROR ,
error_pos ,
2012-11-19 16:31:03 +08:00
COMPLETE_VAR_NULL_DESC ) ;
break ;
2012-11-19 08:30:30 +08:00
}
2012-11-19 16:31:03 +08:00
default :
{
wchar_t token_stop_char = token [ stop_pos ] ;
// Unescape (see http://github.com/fish-shell/fish-shell/issues/50)
if ( token_stop_char = = ANY_CHAR )
token_stop_char = L ' ? ' ;
else if ( token_stop_char = = ANY_STRING | | token_stop_char = = ANY_STRING_RECURSIVE )
token_stop_char = L ' * ' ;
2012-11-19 08:30:30 +08:00
2012-11-19 16:31:03 +08:00
parser . error ( SYNTAX_ERROR ,
error_pos ,
( token_stop_char = = L ' ? ' ? COMPLETE_YOU_WANT_STATUS : COMPLETE_VAR_DESC ) ,
token_stop_char ) ;
break ;
}
2012-11-18 18:23:22 +08:00
}
2006-07-20 21:02:46 +08:00
}
2008-01-14 00:47:47 +08:00
/**
Parse an array slicing specification
*/
2012-11-19 08:30:30 +08:00
static int parse_slice ( const wchar_t * in , wchar_t * * end_ptr , std : : vector < long > & idx , size_t array_size )
2011-12-27 11:18:46 +08:00
{
2012-11-19 08:30:30 +08:00
wchar_t * end ;
2012-11-18 18:23:22 +08:00
2012-08-05 04:02:44 +08:00
const long size = ( long ) array_size ;
2012-11-19 08:30:30 +08:00
size_t pos = 1 ; //skip past the opening square bracket
2012-11-18 18:23:22 +08:00
// debug( 0, L"parse_slice on '%ls'", in );
2012-11-19 08:30:30 +08:00
while ( 1 )
{
long tmp ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
while ( iswspace ( in [ pos ] ) | | ( in [ pos ] = = INTERNAL_SEPARATOR ) )
pos + + ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( in [ pos ] = = L ' ] ' )
{
pos + + ;
break ;
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
errno = 0 ;
tmp = wcstol ( & in [ pos ] , & end , 10 ) ;
if ( ( errno ) | | ( end = = & in [ pos ] ) )
{
return 1 ;
}
2012-11-18 18:23:22 +08:00
// debug( 0, L"Push idx %d", tmp );
2012-11-19 08:30:30 +08:00
long i1 = tmp > - 1 ? tmp : ( long ) array_size + tmp + 1 ;
pos = end - in ;
while ( in [ pos ] = = INTERNAL_SEPARATOR )
pos + + ;
if ( in [ pos ] = = L ' . ' & & in [ pos + 1 ] = = L ' . ' )
{
pos + = 2 ;
while ( in [ pos ] = = INTERNAL_SEPARATOR )
pos + + ;
long tmp1 = wcstol ( & in [ pos ] , & end , 10 ) ;
if ( ( errno ) | | ( end = = & in [ pos ] ) )
{
return 1 ;
}
pos = end - in ;
// debug( 0, L"Push range %d %d", tmp, tmp1 );
long i2 = tmp1 > - 1 ? tmp1 : size + tmp1 + 1 ;
// debug( 0, L"Push range idx %d %d", i1, i2 );
short direction = i2 < i1 ? - 1 : 1 ;
for ( long jjj = i1 ; jjj * direction < = i2 * direction ; jjj + = direction )
{
// debug(0, L"Expand range [subst]: %i\n", jjj);
idx . push_back ( jjj ) ;
}
continue ;
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
// debug( 0, L"Push idx %d", tmp );
idx . push_back ( i1 ) ;
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( end_ptr )
{
2012-11-18 18:23:22 +08:00
// debug( 0, L"Remainder is '%ls', slice def was %d characters long", in+pos, pos );
2012-11-19 08:30:30 +08:00
* end_ptr = ( wchar_t * ) ( in + pos ) ;
}
2012-11-18 18:23:22 +08:00
// debug( 0, L"ok, done" );
2012-11-19 08:30:30 +08:00
return 0 ;
2006-08-24 21:39:04 +08:00
}
2006-02-13 03:03:01 +08:00
/**
Expand all environment variables in the string * ptr .
This function is slow , fragile and complicated . There are lots of
little corner cases , like $ $ foo should do a double expansion ,
$ foo $ bar should not double expand bar , etc . Also , it ' s easy to
accidentally leak memory on array out of bounds errors an various
other situations . All in all , this function should be rewritten ,
split out into multiple logical units and carefully tested . After
that , it can probably be optimized to do fewer memory allocations ,
fewer string scans and overall just less work . But until that
happens , don ' t edit it unless you know exactly what you are doing ,
and do proper testing afterwards .
*/
2012-11-19 08:30:30 +08:00
static int expand_variables_internal ( parser_t & parser , wchar_t * const in , std : : vector < completion_t > & out , long last_idx ) ;
2012-01-31 13:33:15 +08:00
2012-11-19 08:30:30 +08:00
static int expand_variables2 ( parser_t & parser , const wcstring & instr , std : : vector < completion_t > & out , long last_idx )
{
2012-01-31 13:33:15 +08:00
wchar_t * in = wcsdup ( instr . c_str ( ) ) ;
int result = expand_variables_internal ( parser , in , out , last_idx ) ;
free ( in ) ;
return result ;
}
2012-11-19 08:30:30 +08:00
static int expand_variables_internal ( parser_t & parser , wchar_t * const in , std : : vector < completion_t > & out , long last_idx )
2011-12-27 11:18:46 +08:00
{
2012-11-19 08:30:30 +08:00
int is_ok = 1 ;
int empty = 0 ;
2012-11-18 18:23:22 +08:00
2012-01-31 13:33:15 +08:00
wcstring var_tmp ;
std : : vector < long > var_idx_list ;
2012-11-18 18:23:22 +08:00
// CHECK( out, 0 );
2012-11-19 08:30:30 +08:00
for ( long i = last_idx ; ( i > = 0 ) & & is_ok & & ! empty ; i - - )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
const wchar_t c = in [ i ] ;
if ( ( c = = VARIABLE_EXPAND ) | | ( c = = VARIABLE_EXPAND_SINGLE ) )
{
long start_pos = i + 1 ;
long stop_pos ;
long var_len ;
int is_single = ( c = = VARIABLE_EXPAND_SINGLE ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
stop_pos = start_pos ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
while ( 1 )
{
if ( ! ( in [ stop_pos ] ) )
break ;
if ( ! ( iswalnum ( in [ stop_pos ] ) | |
( wcschr ( L " _ " , in [ stop_pos ] ) ! = 0 ) ) )
break ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
stop_pos + + ;
}
2012-11-18 18:23:22 +08:00
/* printf( "Stop for '%c'\n", in[stop_pos]);*/
2012-11-19 08:30:30 +08:00
var_len = stop_pos - start_pos ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( var_len = = 0 )
{
expand_variable_error ( parser , in , stop_pos - 1 , - 1 ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
is_ok = 0 ;
break ;
}
2012-11-18 18:23:22 +08:00
2012-01-31 13:33:15 +08:00
var_tmp . append ( in + start_pos , var_len ) ;
2012-11-19 08:30:30 +08:00
env_var_t var_val = expand_var ( var_tmp . c_str ( ) ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( ! var_val . missing ( ) )
{
int all_vars = 1 ;
2012-01-31 13:33:15 +08:00
wcstring_list_t var_item_list ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( is_ok )
{
tokenize_variable_array ( var_val . c_str ( ) , var_item_list ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( in [ stop_pos ] = = L ' [ ' )
{
wchar_t * slice_end ;
all_vars = 0 ;
if ( parse_slice ( in + stop_pos , & slice_end , var_idx_list , var_item_list . size ( ) ) )
{
parser . error ( SYNTAX_ERROR ,
- 1 ,
L " Invalid index value " ) ;
is_ok = 0 ;
break ;
}
stop_pos = ( slice_end - in ) ;
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( ! all_vars )
{
2012-01-31 13:33:15 +08:00
wcstring_list_t string_values ( var_idx_list . size ( ) ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
for ( size_t j = 0 ; j < var_idx_list . size ( ) ; j + + )
{
long tmp = var_idx_list . at ( j ) ;
/*
Check that we are within array
bounds . If not , truncate the list to
exit .
*/
if ( tmp < 1 | | ( size_t ) tmp > var_item_list . size ( ) )
{
parser . error ( SYNTAX_ERROR ,
2012-01-31 13:33:15 +08:00
- 1 ,
2012-11-19 08:30:30 +08:00
ARRAY_BOUNDS_ERR ) ;
is_ok = 0 ;
var_idx_list . resize ( j ) ;
break ;
}
else
{
/* Replace each index in var_idx_list inplace with the string value at the specified index */
//al_set( var_idx_list, j, wcsdup((const wchar_t *)al_get( &var_item_list, tmp-1 ) ) );
2012-01-31 13:33:15 +08:00
string_values . at ( j ) = var_item_list . at ( tmp - 1 ) ;
2012-11-19 08:30:30 +08:00
}
}
2012-11-18 18:23:22 +08:00
2012-01-31 13:33:15 +08:00
// string_values is the new var_item_list
var_item_list . swap ( string_values ) ;
2012-11-19 08:30:30 +08:00
}
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( is_ok )
{
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( is_single )
{
2012-01-31 13:33:15 +08:00
in [ i ] = 0 ;
wcstring res = in ;
res . push_back ( INTERNAL_SEPARATOR ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
for ( size_t j = 0 ; j < var_item_list . size ( ) ; j + + )
{
2012-11-18 18:23:22 +08:00
const wcstring & next = var_item_list . at ( j ) ;
2012-11-19 08:30:30 +08:00
if ( is_ok )
{
if ( j ! = 0 )
res . append ( L " " ) ;
res . append ( next ) ;
}
}
2012-01-31 13:33:15 +08:00
res . append ( in + stop_pos ) ;
2012-11-19 08:30:30 +08:00
is_ok & = expand_variables2 ( parser , res , out , i ) ;
}
else
{
for ( size_t j = 0 ; j < var_item_list . size ( ) ; j + + )
{
const wcstring & next = var_item_list . at ( j ) ;
if ( is_ok & & ( i = = 0 ) & & ( ! in [ stop_pos ] ) )
{
2012-07-18 03:47:01 +08:00
append_completion ( out , next ) ;
2012-11-19 08:30:30 +08:00
}
else
{
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( is_ok )
{
2012-01-31 13:33:15 +08:00
wcstring new_in ;
2012-11-18 18:23:22 +08:00
2012-01-31 13:33:15 +08:00
if ( start_pos > 0 )
new_in . append ( in , start_pos - 1 ) ;
2012-11-18 18:23:22 +08:00
2012-01-31 13:33:15 +08:00
// at this point new_in.size() is start_pos - 1
2012-11-19 08:30:30 +08:00
if ( start_pos > 1 & & new_in [ start_pos - 2 ] ! = VARIABLE_EXPAND )
2012-01-31 13:33:15 +08:00
{
new_in . push_back ( INTERNAL_SEPARATOR ) ;
2012-11-18 18:23:22 +08:00
}
2012-01-31 13:33:15 +08:00
new_in . append ( next ) ;
2012-11-18 18:23:22 +08:00
new_in . append ( in + stop_pos ) ;
2012-11-19 08:30:30 +08:00
is_ok & = expand_variables2 ( parser , new_in , out , i ) ;
}
}
}
}
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
return is_ok ;
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
else
{
/*
Expand a non - existing variable
*/
if ( c = = VARIABLE_EXPAND )
{
/*
Regular expansion , i . e . expand this argument to nothing
*/
empty = 1 ;
}
else
{
/*
Expansion to single argument .
*/
wcstring res ;
2012-01-31 13:33:15 +08:00
in [ i ] = 0 ;
res . append ( in ) ;
res . append ( in + stop_pos ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
is_ok & = expand_variables2 ( parser , res , out , i ) ;
return is_ok ;
}
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
}
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
if ( ! empty )
{
2012-07-18 03:47:01 +08:00
append_completion ( out , in ) ;
2012-11-19 08:30:30 +08:00
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
return is_ok ;
2011-12-27 11:18:46 +08:00
}
2005-09-20 21:26:39 +08:00
/**
Perform bracket expansion
*/
2012-11-19 08:30:30 +08:00
static int expand_brackets ( parser_t & parser , const wcstring & instr , int flags , std : : vector < completion_t > & out )
2005-09-20 21:26:39 +08:00
{
2012-11-19 08:30:30 +08:00
bool syntax_error = false ;
int bracket_count = 0 ;
2012-11-18 18:23:22 +08:00
2012-11-21 05:52:53 +08:00
const wchar_t * bracket_begin = NULL , * bracket_end = NULL ;
const wchar_t * last_sep = NULL ;
2005-09-20 21:26:39 +08:00
2012-11-19 08:30:30 +08:00
const wchar_t * item_begin ;
2012-11-21 05:52:53 +08:00
size_t length_preceding_brackets , length_following_brackets , tot_len ;
2006-01-31 03:53:10 +08:00
2012-08-06 02:58:17 +08:00
const wchar_t * const in = instr . c_str ( ) ;
2012-11-18 18:23:22 +08:00
2012-11-21 05:52:53 +08:00
/* Locate the first non-nested bracket pair */
for ( const wchar_t * pos = in ; ( * pos ) & & ! syntax_error ; pos + + )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
switch ( * pos )
{
2012-11-19 16:31:03 +08:00
case BRACKET_BEGIN :
{
2012-11-21 05:52:53 +08:00
if ( bracket_count = = 0 )
bracket_begin = pos ;
2012-11-19 16:31:03 +08:00
bracket_count + + ;
break ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
}
2012-11-19 16:31:03 +08:00
case BRACKET_END :
{
bracket_count - - ;
if ( bracket_count < 0 )
{
syntax_error = true ;
}
2012-11-21 05:52:53 +08:00
else if ( bracket_count = = 0 )
{
bracket_end = pos ;
break ;
}
2012-11-19 16:31:03 +08:00
}
case BRACKET_SEP :
2012-11-19 08:30:30 +08:00
{
2012-11-19 16:31:03 +08:00
if ( bracket_count = = 1 )
last_sep = pos ;
2012-11-19 08:30:30 +08:00
}
2012-11-18 18:23:22 +08:00
}
}
2012-11-19 08:30:30 +08:00
if ( bracket_count > 0 )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
if ( ! ( flags & ACCEPT_INCOMPLETE ) )
{
syntax_error = true ;
}
else
{
2012-11-21 05:52:53 +08:00
/* The user hasn't typed an end bracket yet; make one up and append it, then expand that. */
2012-02-23 04:00:02 +08:00
wcstring mod ;
2012-11-19 08:30:30 +08:00
if ( last_sep )
{
mod . append ( in , bracket_begin - in + 1 ) ;
mod . append ( last_sep + 1 ) ;
mod . push_back ( BRACKET_END ) ;
}
else
{
2012-02-23 04:00:02 +08:00
mod . append ( in ) ;
mod . push_back ( BRACKET_END ) ;
2012-11-19 08:30:30 +08:00
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
return expand_brackets ( parser , mod , 1 , out ) ;
}
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
if ( syntax_error )
{
parser . error ( SYNTAX_ERROR ,
- 1 ,
_ ( L " Mismatched brackets " ) ) ;
return 0 ;
}
2012-11-18 18:23:22 +08:00
2012-11-21 05:52:53 +08:00
if ( bracket_begin = = NULL )
2012-11-19 08:30:30 +08:00
{
2012-11-21 05:52:53 +08:00
append_completion ( out , instr ) ;
2012-11-19 08:30:30 +08:00
return 1 ;
}
2012-11-21 05:52:53 +08:00
length_preceding_brackets = ( bracket_begin - in ) ;
length_following_brackets = wcslen ( bracket_end ) - 1 ;
tot_len = length_preceding_brackets + length_following_brackets ;
2012-11-19 08:30:30 +08:00
item_begin = bracket_begin + 1 ;
2012-11-21 05:52:53 +08:00
for ( const wchar_t * pos = ( bracket_begin + 1 ) ; true ; pos + + )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
if ( bracket_count = = 0 )
{
if ( ( * pos = = BRACKET_SEP ) | | ( pos = = bracket_end ) )
{
2012-08-05 04:02:44 +08:00
assert ( pos > = item_begin ) ;
2012-11-19 08:30:30 +08:00
size_t item_len = pos - item_begin ;
2012-11-18 18:23:22 +08:00
2012-08-06 02:58:17 +08:00
wcstring whole_item ;
whole_item . reserve ( tot_len + item_len + 2 ) ;
2012-11-21 05:52:53 +08:00
whole_item . append ( in , length_preceding_brackets ) ;
2012-08-06 02:58:17 +08:00
whole_item . append ( item_begin , item_len ) ;
whole_item . append ( bracket_end + 1 ) ;
2012-11-19 08:30:30 +08:00
expand_brackets ( parser , whole_item , flags , out ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
item_begin = pos + 1 ;
if ( pos = = bracket_end )
break ;
}
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( * pos = = BRACKET_BEGIN )
{
bracket_count + + ;
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( * pos = = BRACKET_END )
{
bracket_count - - ;
}
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
return 1 ;
2005-09-20 21:26:39 +08:00
}
2011-12-27 11:18:46 +08:00
/**
Perform cmdsubst expansion
*/
2012-11-19 08:30:30 +08:00
static int expand_cmdsubst ( parser_t & parser , const wcstring & input , std : : vector < completion_t > & outList )
2011-12-27 11:18:46 +08:00
{
2012-11-19 08:30:30 +08:00
wchar_t * paran_begin = 0 , * paran_end = 0 ;
2011-12-27 11:18:46 +08:00
std : : vector < wcstring > sub_res ;
2012-11-19 08:30:30 +08:00
size_t i , j ;
wchar_t * tail_begin = 0 ;
2012-11-18 18:23:22 +08:00
2011-12-27 11:18:46 +08:00
const wchar_t * const in = input . c_str ( ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
int parse_ret ;
2013-07-17 16:35:30 +08:00
switch ( parse_ret = parse_util_locate_cmdsubst ( in , & paran_begin , & paran_end , false ) )
2012-11-19 08:30:30 +08:00
{
2012-11-19 16:31:03 +08:00
case - 1 :
parser . error ( SYNTAX_ERROR ,
- 1 ,
L " Mismatched parenthesis " ) ;
return 0 ;
case 0 :
outList . push_back ( completion_t ( input ) ) ;
return 1 ;
case 1 :
2012-11-18 18:23:22 +08:00
2012-11-19 16:31:03 +08:00
break ;
2012-11-19 08:30:30 +08:00
}
2012-11-18 18:23:22 +08:00
2011-12-27 11:18:46 +08:00
const wcstring subcmd ( paran_begin + 1 , paran_end - paran_begin - 1 ) ;
2012-11-18 18:23:22 +08:00
2013-02-01 07:57:08 +08:00
if ( exec_subshell ( subcmd , sub_res , true /* do apply exit status */ ) = = - 1 )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
parser . error ( CMDSUBST_ERROR , - 1 , L " Unknown error while evaulating command substitution " ) ;
return 0 ;
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
tail_begin = paran_end + 1 ;
if ( * tail_begin = = L ' [ ' )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
std : : vector < long > slice_idx ;
wchar_t * slice_end ;
if ( parse_slice ( tail_begin , & slice_end , slice_idx , sub_res . size ( ) ) )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
parser . error ( SYNTAX_ERROR , - 1 , L " Invalid index value " ) ;
return 0 ;
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
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 ;
2012-11-18 18:23:22 +08:00
2011-12-27 11:18:46 +08:00
sub_res2 . push_back ( sub_res . at ( idx ) ) ;
2012-11-18 18:23:22 +08:00
// debug( 0, L"Pushing item '%ls' with index %d onto sliced result", al_get( sub_res, idx ), idx );
2012-11-19 08:30:30 +08:00
//sub_res[idx] = 0; // ??
}
sub_res = sub_res2 ;
}
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
/*
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
*/
2012-01-17 00:56:47 +08:00
std : : vector < completion_t > tail_expand ;
2012-11-19 08:30:30 +08:00
expand_cmdsubst ( parser , tail_begin , tail_expand ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
/*
Combine the result of the current command substitution with the
result of the recursive tail expansion
*/
for ( i = 0 ; i < sub_res . size ( ) ; i + + )
2011-12-27 11:18:46 +08:00
{
wcstring sub_item = sub_res . at ( i ) ;
wcstring sub_item2 = escape_string ( sub_item , 1 ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
for ( j = 0 ; j < tail_expand . size ( ) ; j + + )
2011-12-27 11:18:46 +08:00
{
2012-11-18 18:23:22 +08:00
2011-12-27 11:18:46 +08:00
wcstring whole_item ;
2012-11-18 18:23:22 +08:00
2012-01-17 00:56:47 +08:00
wcstring tail_item = tail_expand . at ( j ) . completion ;
2012-11-18 18:23:22 +08:00
2011-12-27 11:18:46 +08:00
//sb_append_substring( &whole_item, in, len1 );
2012-08-05 04:02:44 +08:00
whole_item . append ( in , paran_begin - in ) ;
2012-11-18 18:23:22 +08:00
2011-12-27 11:18:46 +08:00
//sb_append_char( &whole_item, INTERNAL_SEPARATOR );
whole_item . push_back ( INTERNAL_SEPARATOR ) ;
2012-11-18 18:23:22 +08:00
2011-12-27 11:18:46 +08:00
//sb_append_substring( &whole_item, sub_item2, item_len );
2012-11-19 08:30:30 +08:00
whole_item . append ( sub_item2 ) ;
2012-11-18 18:23:22 +08:00
2011-12-27 11:18:46 +08:00
//sb_append_char( &whole_item, INTERNAL_SEPARATOR );
whole_item . push_back ( INTERNAL_SEPARATOR ) ;
2012-11-18 18:23:22 +08:00
2011-12-27 11:18:46 +08:00
//sb_append( &whole_item, tail_item );
whole_item . append ( tail_item ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
//al_push( out, whole_item.buff );
2012-02-02 08:27:14 +08:00
outList . push_back ( completion_t ( whole_item ) ) ;
2011-12-27 11:18:46 +08:00
}
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
return 1 ;
2005-09-20 21:26:39 +08:00
}
2006-06-20 08:50:10 +08:00
/**
Wrapper around unescape funtion . Issues an error ( ) on failiure .
*/
2012-02-01 09:01:19 +08:00
__attribute__ ( ( unused ) )
2012-11-19 08:30:30 +08:00
static wchar_t * expand_unescape ( parser_t & parser , const wchar_t * in , int escape_special )
2005-09-20 21:26:39 +08:00
{
2012-11-19 08:30:30 +08:00
wchar_t * res = unescape ( in , escape_special ) ;
if ( ! res )
parser . error ( SYNTAX_ERROR , - 1 , L " Unexpected end of string " ) ;
return res ;
2005-09-20 21:26:39 +08:00
}
2012-11-19 08:30:30 +08:00
static wcstring expand_unescape_string ( const wcstring & in , int escape_special )
2011-12-27 11:18:46 +08:00
{
wcstring tmp = in ;
unescape_string ( tmp , escape_special ) ;
/* Need to detect error here */
2012-11-19 08:30:30 +08:00
return tmp ;
2011-12-27 11:18:46 +08:00
}
2013-04-08 14:54:43 +08:00
/* Given that input[0] is HOME_DIRECTORY or tilde (ugh), return the user's name. Return the empty string if it is just a tilde. Also return by reference the index of the first character of the remaining part of the string (e.g. the subsequent slash) */
static wcstring get_home_directory_name ( const wcstring & input , size_t * out_tail_idx )
2005-09-20 21:26:39 +08:00
{
2012-11-19 08:30:30 +08:00
const wchar_t * const in = input . c_str ( ) ;
2013-04-08 14:54:43 +08:00
assert ( in [ 0 ] = = HOME_DIRECTORY | | in [ 0 ] = = L ' ~ ' ) ;
size_t tail_idx ;
const wchar_t * name_end = wcschr ( in , L ' / ' ) ;
if ( name_end )
{
tail_idx = name_end - in ;
}
else
{
tail_idx = wcslen ( in ) ;
}
* out_tail_idx = tail_idx ;
return input . substr ( 1 , tail_idx - 1 ) ;
}
/** Attempts tilde expansion of the string specified, modifying it in place. */
static void expand_home_directory ( wcstring & input )
{
if ( ! input . empty ( ) & & input . at ( 0 ) = = HOME_DIRECTORY )
2012-11-19 08:30:30 +08:00
{
2011-12-27 11:18:46 +08:00
size_t tail_idx ;
2013-04-08 14:54:43 +08:00
wcstring username = get_home_directory_name ( input , & tail_idx ) ;
2013-05-05 17:33:17 +08:00
2013-04-08 14:54:43 +08:00
bool tilde_error = false ;
2011-12-27 11:18:46 +08:00
wcstring home ;
2013-04-08 14:54:43 +08:00
if ( username . empty ( ) )
2012-11-19 08:30:30 +08:00
{
/* Current users home directory */
home = env_get_string ( L " HOME " ) ;
2011-12-27 11:18:46 +08:00
tail_idx = 1 ;
2012-11-19 08:30:30 +08:00
}
else
{
/* Some other users home directory */
2013-04-08 14:54:43 +08:00
std : : string name_cstr = wcs2string ( username ) ;
2012-11-19 08:30:30 +08:00
struct passwd * userinfo = getpwnam ( name_cstr . c_str ( ) ) ;
if ( userinfo = = NULL )
{
2013-04-08 14:54:43 +08:00
tilde_error = true ;
2012-03-26 14:31:03 +08:00
input [ 0 ] = L ' ~ ' ;
2012-11-19 08:30:30 +08:00
}
else
{
home = str2wcstring ( userinfo - > pw_dir ) ;
}
}
2006-01-31 03:53:10 +08:00
2011-12-27 11:18:46 +08:00
if ( ! tilde_error )
{
input . replace ( input . begin ( ) , input . begin ( ) + tail_idx , home ) ;
}
2012-11-19 08:30:30 +08:00
}
2006-01-31 03:53:10 +08:00
}
2005-09-20 21:26:39 +08:00
2012-08-24 02:21:35 +08:00
void expand_tilde ( wcstring & input )
2011-12-27 11:18:46 +08:00
{
2013-04-08 14:54:43 +08:00
// Avoid needless COW behavior by ensuring we use const at
const wcstring & tmp = input ;
if ( ! tmp . empty ( ) & & tmp . at ( 0 ) = = L ' ~ ' )
2012-11-19 08:30:30 +08:00
{
input . at ( 0 ) = HOME_DIRECTORY ;
expand_home_directory ( input ) ;
}
2011-12-27 11:18:46 +08:00
}
2013-04-08 14:54:43 +08:00
static void unexpand_tildes ( const wcstring & input , std : : vector < completion_t > * completions )
{
// If input begins with tilde, then try to replace the corresponding string in each completion with the tilde
// If it does not, there's nothing to do
if ( input . empty ( ) | | input . at ( 0 ) ! = L ' ~ ' )
return ;
2013-05-05 17:33:17 +08:00
2013-04-08 14:54:43 +08:00
// We only operate on completions that replace their contents
// If we don't have any, we're done.
// In particular, empty vectors are common.
bool has_candidate_completion = false ;
for ( size_t i = 0 ; i < completions - > size ( ) ; i + + )
{
if ( completions - > at ( i ) . flags & COMPLETE_REPLACES_TOKEN )
{
has_candidate_completion = true ;
break ;
}
}
if ( ! has_candidate_completion )
return ;
2013-05-05 17:33:17 +08:00
2013-04-08 14:54:43 +08:00
size_t tail_idx ;
wcstring username_with_tilde = L " ~ " ;
username_with_tilde . append ( get_home_directory_name ( input , & tail_idx ) ) ;
2013-05-05 17:33:17 +08:00
2013-04-08 14:54:43 +08:00
// Expand username_with_tilde
wcstring home = username_with_tilde ;
expand_tilde ( home ) ;
2013-05-05 17:33:17 +08:00
2013-04-08 14:54:43 +08:00
// Now for each completion that starts with home, replace it with the username_with_tilde
for ( size_t i = 0 ; i < completions - > size ( ) ; i + + )
{
completion_t & comp = completions - > at ( i ) ;
if ( ( comp . flags & COMPLETE_REPLACES_TOKEN ) & & string_prefixes_string ( home , comp . completion ) )
{
comp . completion . replace ( 0 , home . size ( ) , username_with_tilde ) ;
2013-05-05 17:33:17 +08:00
2013-04-08 14:54:43 +08:00
// And mark that our tilde is literal, so it doesn't try to escape it
comp . flags | = COMPLETE_DONT_ESCAPE_TILDES ;
}
}
}
2005-09-20 21:26:39 +08:00
/**
Remove any internal separators . Also optionally convert wildcard characters to
2006-07-21 09:08:31 +08:00
regular equivalents . This is done to support EXPAND_SKIP_WILDCARDS .
2005-09-20 21:26:39 +08:00
*/
2012-11-19 08:30:30 +08:00
static void remove_internal_separator ( wcstring & str , bool conv )
2005-09-20 21:26:39 +08:00
{
2012-07-17 03:05:36 +08:00
/* Remove all instances of INTERNAL_SEPARATOR */
str . erase ( std : : remove ( str . begin ( ) , str . end ( ) , ( wchar_t ) INTERNAL_SEPARATOR ) , str . end ( ) ) ;
2012-11-18 18:23:22 +08:00
2012-07-17 03:05:36 +08:00
/* If conv is true, replace all instances of ANY_CHAR with '?', ANY_STRING with '*', ANY_STRING_RECURSIVE with '*' */
if ( conv )
{
for ( size_t idx = 0 ; idx < str . size ( ) ; idx + + )
{
switch ( str . at ( idx ) )
{
2012-11-19 16:31:03 +08:00
case ANY_CHAR :
str . at ( idx ) = L ' ? ' ;
break ;
case ANY_STRING :
case ANY_STRING_RECURSIVE :
str . at ( idx ) = L ' * ' ;
break ;
2012-07-17 03:05:36 +08:00
}
}
}
2011-12-27 11:18:46 +08:00
}
2012-11-19 08:30:30 +08:00
int expand_string ( const wcstring & input , std : : vector < completion_t > & output , expand_flags_t flags )
2013-05-05 17:33:17 +08:00
{
2012-11-19 08:30:30 +08:00
parser_t parser ( PARSER_TYPE_ERRORS_ONLY , true /* show errors */ ) ;
2013-05-05 17:33:17 +08:00
2012-11-19 08:30:30 +08:00
size_t i ;
int res = EXPAND_OK ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( ( ! ( flags & ACCEPT_INCOMPLETE ) ) & & expand_is_clean ( input . c_str ( ) ) )
{
output . push_back ( completion_t ( input ) ) ;
return EXPAND_OK ;
}
2013-05-05 17:33:17 +08:00
2013-04-08 14:54:43 +08:00
std : : vector < completion_t > clist1 , clist2 ;
std : : vector < completion_t > * in = & clist1 , * out = & clist2 ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( EXPAND_SKIP_CMDSUBST & flags )
{
wchar_t * begin , * end ;
2012-11-18 18:23:22 +08:00
2013-07-17 16:35:30 +08:00
if ( parse_util_locate_cmdsubst ( input . c_str ( ) , & begin , & end , true ) ! = 0 )
2012-11-19 08:30:30 +08:00
{
parser . error ( CMDSUBST_ERROR , - 1 , L " Command substitutions not allowed " ) ;
return EXPAND_ERROR ;
}
2013-04-08 14:54:43 +08:00
in - > push_back ( completion_t ( input ) ) ;
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
else
{
2013-04-08 14:54:43 +08:00
int cmdsubst_ok = expand_cmdsubst ( parser , input , * in ) ;
2012-01-31 13:33:15 +08:00
if ( ! cmdsubst_ok )
return EXPAND_ERROR ;
2012-11-19 08:30:30 +08:00
}
2013-05-05 17:33:17 +08:00
2012-11-19 08:30:30 +08:00
for ( i = 0 ; i < in - > size ( ) ; i + + )
2012-01-31 13:33:15 +08:00
{
/*
We accept incomplete strings here , since complete uses
expand_string to expand incomplete strings from the
commandline .
*/
2012-11-18 18:23:22 +08:00
int unescape_flags = UNESCAPE_SPECIAL | UNESCAPE_INCOMPLETE ;
2012-11-19 08:30:30 +08:00
wcstring next = expand_unescape_string ( in - > at ( i ) . completion , unescape_flags ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( EXPAND_SKIP_VARIABLES & flags )
2012-01-31 13:33:15 +08:00
{
2012-11-19 08:30:30 +08:00
for ( size_t i = 0 ; i < next . size ( ) ; i + + )
{
if ( next . at ( i ) = = VARIABLE_EXPAND )
{
2012-01-31 13:33:15 +08:00
next [ i ] = L ' $ ' ;
2011-12-27 11:18:46 +08:00
}
2012-01-31 13:33:15 +08:00
}
2012-02-02 08:27:14 +08:00
out - > push_back ( completion_t ( next ) ) ;
2012-01-31 13:33:15 +08:00
}
else
{
2012-11-19 08:30:30 +08:00
if ( ! expand_variables2 ( parser , next , * out , next . size ( ) - 1 ) )
2012-01-31 13:33:15 +08:00
{
return EXPAND_ERROR ;
}
}
}
2012-11-18 18:23:22 +08:00
2012-01-31 13:33:15 +08:00
in - > clear ( ) ;
2013-04-08 14:54:43 +08:00
std : : swap ( in , out ) ; // note: this swaps the pointers only (last output is next input)
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
for ( i = 0 ; i < in - > size ( ) ; i + + )
2012-01-31 13:33:15 +08:00
{
wcstring next = in - > at ( i ) . completion ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( ! expand_brackets ( parser , next , flags , * out ) )
2012-01-31 13:33:15 +08:00
{
return EXPAND_ERROR ;
}
}
in - > clear ( ) ;
2013-04-08 14:54:43 +08:00
std : : swap ( in , out ) ; // note: this swaps the pointers only (last output is next input)
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
for ( i = 0 ; i < in - > size ( ) ; i + + )
2012-01-31 13:33:15 +08:00
{
wcstring next = in - > at ( i ) . completion ;
2012-11-18 18:23:22 +08:00
2013-01-13 04:55:23 +08:00
if ( ! ( EXPAND_SKIP_HOME_DIRECTORIES & flags ) )
2013-01-13 04:53:40 +08:00
expand_home_directory ( next ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( flags & ACCEPT_INCOMPLETE )
2012-01-31 13:33:15 +08:00
{
2012-11-19 08:30:30 +08:00
if ( next [ 0 ] = = PROCESS_EXPAND )
2012-01-31 13:33:15 +08:00
{
/*
If process expansion matches , we are not
interested in other completions , so we
2013-01-24 19:17:55 +08:00
short - circuit and return
2012-01-31 13:33:15 +08:00
*/
2012-11-19 08:30:30 +08:00
if ( ! ( flags & EXPAND_SKIP_PROCESS ) )
expand_pid ( next , flags , output ) ;
2012-01-31 13:33:15 +08:00
return EXPAND_OK ;
}
else
{
2012-02-02 08:27:14 +08:00
out - > push_back ( completion_t ( next ) ) ;
2012-01-31 13:33:15 +08:00
}
}
else
{
2012-11-19 08:30:30 +08:00
if ( ! ( flags & EXPAND_SKIP_PROCESS ) & & ! expand_pid ( next , flags , * out ) )
2012-01-31 13:33:15 +08:00
{
return EXPAND_ERROR ;
}
}
}
2012-11-18 18:23:22 +08:00
2012-01-31 13:33:15 +08:00
in - > clear ( ) ;
2013-04-08 14:54:43 +08:00
std : : swap ( in , out ) ; // note: this swaps the pointers only (last output is next input)
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
for ( i = 0 ; i < in - > size ( ) ; i + + )
2012-01-31 13:33:15 +08:00
{
wcstring next_str = in - > at ( i ) . completion ;
int wc_res ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
remove_internal_separator ( next_str , ( EXPAND_SKIP_WILDCARDS & flags ) ? true : false ) ;
2012-01-31 13:33:15 +08:00
const wchar_t * next = next_str . c_str ( ) ;
2013-09-12 09:50:14 +08:00
const bool has_wildcard = wildcard_has ( next , 1 ) ;
if ( has_wildcard & & ( flags & EXECUTABLES_ONLY ) )
{
// Don't do wildcard expansion for executables. See #785. So do nothing here.
}
else if ( ( ( flags & ACCEPT_INCOMPLETE ) & & ( ! ( flags & EXPAND_SKIP_WILDCARDS ) ) ) | |
has_wildcard )
2012-01-31 13:33:15 +08:00
{
const wchar_t * start , * rest ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( next [ 0 ] = = ' / ' )
2012-01-31 13:33:15 +08:00
{
start = L " / " ;
rest = & next [ 1 ] ;
}
else
{
start = L " " ;
rest = next ;
}
2012-11-18 18:23:22 +08:00
2013-04-08 14:54:43 +08:00
std : : vector < completion_t > expanded ;
wc_res = wildcard_expand_string ( rest , start , flags , expanded ) ;
2012-11-19 08:30:30 +08:00
if ( flags & ACCEPT_INCOMPLETE )
2012-01-31 13:33:15 +08:00
{
2013-04-08 14:54:43 +08:00
out - > insert ( out - > end ( ) , expanded . begin ( ) , expanded . end ( ) ) ;
2012-01-31 13:33:15 +08:00
}
2013-04-08 14:54:43 +08:00
else
2012-01-31 13:33:15 +08:00
{
2012-11-19 08:30:30 +08:00
switch ( wc_res )
2012-01-31 13:33:15 +08:00
{
2012-11-19 16:31:03 +08:00
case 0 :
2012-01-31 13:33:15 +08:00
{
2013-04-08 14:54:43 +08:00
if ( res = = EXPAND_OK )
res = EXPAND_WILDCARD_NO_MATCH ;
break ;
2012-01-31 13:33:15 +08:00
}
2012-11-18 18:23:22 +08:00
2012-11-19 16:31:03 +08:00
case 1 :
2012-01-31 13:33:15 +08:00
{
2012-11-19 16:31:03 +08:00
res = EXPAND_WILDCARD_MATCH ;
2013-09-01 06:01:02 +08:00
std : : sort ( expanded . begin ( ) , expanded . end ( ) , completion_t : : is_alphabetically_less_than ) ;
2013-04-08 14:54:43 +08:00
out - > insert ( out - > end ( ) , expanded . begin ( ) , expanded . end ( ) ) ;
2012-11-19 16:31:03 +08:00
break ;
2012-01-31 13:33:15 +08:00
}
2012-11-19 08:30:30 +08:00
2012-11-19 16:31:03 +08:00
case - 1 :
{
return EXPAND_ERROR ;
}
2012-11-18 18:23:22 +08:00
2012-01-31 13:33:15 +08:00
}
}
}
else
{
2013-05-05 17:33:17 +08:00
if ( ! ( flags & ACCEPT_INCOMPLETE ) )
2012-01-31 13:33:15 +08:00
{
2013-04-08 14:54:43 +08:00
out - > push_back ( completion_t ( next_str ) ) ;
2012-01-31 13:33:15 +08:00
}
}
}
2013-05-05 17:33:17 +08:00
2013-04-08 14:54:43 +08:00
// Hack to un-expand tildes (see #647)
2013-05-05 17:33:17 +08:00
if ( ! ( flags & EXPAND_SKIP_HOME_DIRECTORIES ) )
2013-04-08 14:54:43 +08:00
{
unexpand_tildes ( input , out ) ;
}
2013-05-05 17:33:17 +08:00
2013-04-08 14:54:43 +08:00
// Return our output
output . insert ( output . end ( ) , out - > begin ( ) , out - > end ( ) ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
return res ;
2011-12-27 11:18:46 +08:00
}
2007-02-19 07:25:20 +08:00
2012-11-19 08:30:30 +08:00
bool expand_one ( wcstring & string , expand_flags_t flags )
{
std : : vector < completion_t > completions ;
bool result = false ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( ( ! ( flags & ACCEPT_INCOMPLETE ) ) & & expand_is_clean ( string . c_str ( ) ) )
{
2012-01-30 18:45:55 +08:00
return true ;
2012-11-19 08:30:30 +08:00
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( expand_string ( string , completions , flags ) )
{
if ( completions . size ( ) = = 1 )
{
2012-01-30 18:45:55 +08:00
string = completions . at ( 0 ) . completion ;
result = true ;
}
2012-01-30 18:23:58 +08:00
}
2012-11-19 08:30:30 +08:00
return result ;
2012-01-30 18:23:58 +08:00
}
2013-01-05 05:09:01 +08:00
/*
https : //github.com/fish-shell/fish-shell/issues/367
With them the Seed of Wisdom did I sow ,
And with my own hand labour ' d it to grow :
And this was all the Harvest that I reap ' d - - -
" I came like Water, and like Wind I go. "
*/
static std : : string escape_single_quoted_hack_hack_hack_hack ( const char * str )
{
std : : string result ;
size_t len = strlen ( str ) ;
result . reserve ( len + 2 ) ;
result . push_back ( ' \' ' ) ;
for ( size_t i = 0 ; i < len ; i + + )
{
char c = str [ i ] ;
// Escape backslashes and single quotes only
if ( c = = ' \\ ' | | c = = ' \' ' )
result . push_back ( ' \\ ' ) ;
result . push_back ( c ) ;
}
result . push_back ( ' \' ' ) ;
return result ;
}
bool fish_xdm_login_hack_hack_hack_hack ( std : : vector < std : : string > * cmds , int argc , const char * const * argv )
{
bool result = false ;
if ( cmds & & cmds - > size ( ) = = 1 )
{
const std : : string & cmd = cmds - > at ( 0 ) ;
if ( cmd = = " exec \" ${@} \" " | | cmd = = " exec \" $@ \" " )
{
/* We're going to construct a new command that starts with exec, and then has the remaining arguments escaped */
std : : string new_cmd = " exec " ;
for ( int i = 1 ; i < argc ; i + + )
{
const char * arg = argv [ i ] ;
if ( arg )
{
new_cmd . push_back ( ' ' ) ;
new_cmd . append ( escape_single_quoted_hack_hack_hack_hack ( arg ) ) ;
}
}
cmds - > at ( 0 ) = new_cmd ;
result = true ;
}
}
return result ;
}
bool fish_openSUSE_dbus_hack_hack_hack_hack ( std : : vector < completion_t > * args )
{
static signed char isSUSE = - 1 ;
if ( isSUSE = = 0 )
return false ;
bool result = false ;
if ( args & & ! args - > empty ( ) )
{
const wcstring & cmd = args - > at ( 0 ) . completion ;
if ( cmd . find ( L " DBUS_SESSION_BUS_ " ) ! = wcstring : : npos )
{
/* See if we are SUSE */
if ( isSUSE < 0 )
{
struct stat buf = { } ;
isSUSE = ( 0 = = stat ( " /etc/SuSE-release " , & buf ) ) ;
}
if ( isSUSE )
{
/* Look for an equal sign */
size_t where = cmd . find ( L ' = ' ) ;
if ( where ! = wcstring : : npos )
{
/* Oh my. It's presumably of the form foo=bar; find the = and split */
const wcstring key = wcstring ( cmd , 0 , where ) ;
/* Trim whitespace and semicolon */
wcstring val = wcstring ( cmd , where + 1 ) ;
size_t last_good = val . find_last_not_of ( L " \n ; " ) ;
if ( last_good ! = wcstring : : npos )
val . resize ( last_good + 1 ) ;
args - > clear ( ) ;
args - > push_back ( completion_t ( L " set " ) ) ;
if ( key = = L " DBUS_SESSION_BUS_ADDRESS " )
args - > push_back ( completion_t ( L " -x " ) ) ;
args - > push_back ( completion_t ( key ) ) ;
args - > push_back ( completion_t ( val ) ) ;
result = true ;
}
else if ( string_prefixes_string ( L " export DBUS_SESSION_BUS_ADDRESS; " , cmd ) )
{
/* Nothing, we already exported it */
args - > clear ( ) ;
args - > push_back ( completion_t ( L " echo " ) ) ;
args - > push_back ( completion_t ( L " -n " ) ) ;
result = true ;
}
}
}
}
return result ;
}
2013-07-17 15:38:04 +08:00
bool expand_abbreviation ( const wcstring & src , wcstring * output )
{
if ( src . empty ( ) )
return false ;
/* Get the abbreviations. Return false if we have none */
env_var_t var = env_get_string ( USER_ABBREVIATIONS_VARIABLE_NAME ) ;
if ( var . missing_or_empty ( ) )
return false ;
bool result = false ;
wcstring line ;
wcstokenizer tokenizer ( var , ARRAY_SEP_STR ) ;
while ( tokenizer . next ( line ) )
{
/* Line is expected to be of the form 'foo=bar'. Parse out the first =. Be forgiving about spaces, but silently skip on failure (no equals, or equals at the end or beginning). Try to avoid copying any strings until we are sure this is a match. */
size_t equals = line . find ( L ' = ' ) ;
if ( equals = = wcstring : : npos | | equals = = 0 | | equals + 1 = = line . size ( ) )
continue ;
/* Find the character just past the end of the command. Walk backwards, skipping spaces. */
size_t cmd_end = equals ;
while ( cmd_end > 0 & & iswspace ( line . at ( cmd_end - 1 ) ) )
cmd_end - - ;
/* See if this command matches */
if ( line . compare ( 0 , cmd_end , src ) = = 0 )
{
/* Success. Set output to everythign past the end of the string. */
if ( output ! = NULL )
output - > assign ( line , equals + 1 , wcstring : : npos ) ;
result = true ;
break ;
}
}
return result ;
}