2005-09-20 13:26:39 +00:00
/**\file expand.c
String expansion functions . These functions perform several kinds of
2011-12-27 03:18:46 +00:00
parameter expansion .
2005-09-20 13:26:39 +00:00
*/
2006-08-11 01:18:35 +00:00
# include "config.h"
2005-09-20 13:26:39 +00: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>
# include <sys/types.h>
2012-07-16 19:05:36 +00:00
# include <sys/sysctl.h>
2005-09-20 13:26:39 +00:00
# include <termios.h>
# include <dirent.h>
# include <sys/stat.h>
# include <unistd.h>
# include <signal.h>
2012-07-16 19:05:36 +00:00
# include <algorithm>
2005-09-20 13:26:39 +00:00
2005-12-25 22:00:44 +00:00
# include <assert.h>
2011-12-27 03:18:46 +00:00
# include <vector>
2005-12-25 22:00:44 +00:00
2005-09-20 13:26:39 +00:00
# ifdef SunOS
# include <procfs.h>
# endif
2006-02-28 13:17:16 +00:00
# include "fallback.h"
2005-09-20 13:26:39 +00:00
# include "util.h"
2006-02-28 13:17:16 +00:00
2005-09-20 13:26:39 +00: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 15:50:20 +00:00
# include "signal.h"
2005-09-20 13:26:39 +00:00
# include "tokenizer.h"
# include "complete.h"
2006-07-19 22:55:49 +00:00
2006-01-30 16:51:50 +00:00
# include "parse_util.h"
2005-09-20 13:26:39 +00:00
2006-07-20 13:02:46 +00: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 01:51:52 +00: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 13:02:46 +00: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-20 23:33:19 +00: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 13:02:46 +00: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 13:26:39 +00:00
/**
Description for child process
*/
2006-01-17 12:48:30 +00:00
# define COMPLETE_CHILD_PROCESS_DESC _( L"Child process")
2005-09-20 13:26:39 +00:00
/**
Description for non - child process
*/
2006-01-17 12:48:30 +00:00
# define COMPLETE_PROCESS_DESC _( L"Process")
2005-09-20 13:26:39 +00:00
/**
Description for long job
*/
2006-01-17 12:48:30 +00:00
# define COMPLETE_JOB_DESC _( L"Job")
2005-09-20 13:26:39 +00:00
/**
Description for short job . The job command is concatenated
*/
2007-02-24 08:11:31 +00:00
# define COMPLETE_JOB_DESC_VAL _( L"Job: %ls")
2005-09-20 13:26:39 +00:00
/**
Description for the shells own pid
*/
2006-01-17 12:48:30 +00:00
# define COMPLETE_SELF_DESC _( L"Shell process")
2005-09-20 13:26:39 +00:00
/**
Description for the shells own pid
*/
2006-01-17 12:48:30 +00:00
# define COMPLETE_LAST_DESC _( L"Last background job")
2005-09-20 13:26:39 +00: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 10:51:02 +00:00
/**
Characters which make a string unclean if they are the first
2009-02-21 10:43:30 +00:00
character of the string . See \ c expand_is_clean ( ) .
2005-10-26 10:51:02 +00:00
*/
# define UNCLEAN_FIRST L"~%"
/**
2009-02-21 10:43:30 +00:00
Unclean characters . See \ c expand_is_clean ( ) .
2005-10-26 10:51:02 +00:00
*/
# define UNCLEAN L"$*?\\\"'({})"
2012-07-16 19:05:36 +00:00
static void remove_internal_separator ( wcstring & s , bool conv ) ;
2006-05-21 22:16:04 +00:00
int expand_is_clean ( const wchar_t * in )
2005-10-26 10:51:02 +00:00
{
2006-01-30 19:53:10 +00:00
2005-10-26 10:51:02 +00:00
const wchar_t * str = in ;
2006-06-21 00:48:36 +00:00
CHECK ( in , 1 ) ;
2005-11-29 16:52:02 +00:00
/*
Test characters that have a special meaning in the first character position
*/
2005-10-26 10:51:02 +00:00
if ( wcschr ( UNCLEAN_FIRST , * str ) )
return 0 ;
2006-01-30 19:53:10 +00:00
2005-11-29 16:52:02 +00:00
/*
Test characters that have a special meaning in any character position
*/
2005-10-26 10:51:02 +00:00
while ( * str )
{
if ( wcschr ( UNCLEAN , * str ) )
return 0 ;
str + + ;
}
2006-01-30 19:53:10 +00:00
2005-10-26 10:51:02 +00:00
return 1 ;
}
2005-09-20 13:26:39 +00:00
/**
2006-01-30 19:53:10 +00:00
Return the environment variable value for the string starting at \ c in .
2005-09-20 13:26:39 +00:00
*/
2012-02-26 02:54:49 +00:00
static env_var_t expand_var ( const wchar_t * in )
2005-09-20 13:26:39 +00:00
{
if ( ! in )
2012-02-26 02:54:49 +00:00
return env_var_t : : missing_var ( ) ;
return env_get_string ( in ) ;
2005-09-20 13:26:39 +00:00
}
/**
Test if the specified string does not contain character which can
not be used inside a quoted string .
*/
2012-02-08 08:15:06 +00:00
static int is_quotable ( const wchar_t * str )
2005-09-20 13:26:39 +00:00
{
switch ( * str )
{
case 0 :
return 1 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
case L ' \n ' :
case L ' \t ' :
case L ' \r ' :
case L ' \b ' :
2007-08-22 07:52:39 +00:00
case L ' \x1b ' :
2005-09-20 13:26:39 +00:00
return 0 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
default :
2006-01-30 19:53:10 +00:00
return is_quotable ( str + 1 ) ;
2005-09-20 13:26:39 +00:00
}
return 0 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
2012-02-08 08:15:06 +00:00
static int is_quotable ( const wcstring & str ) {
return is_quotable ( str . c_str ( ) ) ;
}
2006-01-30 19:53:10 +00:00
2012-02-08 08:15:06 +00:00
wcstring expand_escape_variable ( const wcstring & in )
{
2006-01-30 19:53:10 +00:00
2012-02-08 08:15:06 +00:00
wcstring_list_t lst ;
wcstring buff ;
2006-06-21 00:48:36 +00:00
2012-02-10 09:37:30 +00:00
tokenize_variable_array ( in , lst ) ;
2006-01-30 19:53:10 +00:00
2012-02-08 08:15:06 +00:00
switch ( lst . size ( ) )
2005-09-20 13:26:39 +00:00
{
case 0 :
2012-02-08 08:15:06 +00:00
buff . append ( L " '' " ) ;
2006-01-30 19:53:10 +00:00
break ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
case 1 :
{
2012-02-08 08:15:06 +00:00
const wcstring & el = lst . at ( 0 ) ;
2006-01-30 19:53:10 +00:00
2012-02-08 08:15:06 +00:00
if ( el . find ( L ' ' ) ! = wcstring : : npos & & is_quotable ( el ) )
2005-09-20 13:26:39 +00:00
{
2012-02-08 08:15:06 +00:00
buff . append ( L " ' " ) ;
buff . append ( el ) ;
buff . append ( L " ' " ) ;
2005-09-20 13:26:39 +00:00
}
else
{
2012-02-08 08:15:06 +00:00
buff . append ( escape_string ( el , 1 ) ) ;
2005-09-20 13:26:39 +00:00
}
break ;
}
default :
{
2012-02-08 08:15:06 +00:00
for ( size_t j = 0 ; j < lst . size ( ) ; j + + )
2005-09-20 13:26:39 +00:00
{
2012-02-08 08:15:06 +00:00
const wcstring & el = lst . at ( j ) ;
2005-09-20 13:26:39 +00:00
if ( j )
2012-02-08 08:15:06 +00:00
buff . append ( L " " ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( is_quotable ( el ) )
{
2012-02-08 08:15:06 +00:00
buff . append ( L " ' " ) ;
buff . append ( el ) ;
buff . append ( L " ' " ) ;
2005-09-20 13:26:39 +00:00
}
else
{
2012-02-08 08:15:06 +00:00
buff . append ( escape_string ( el , 1 ) ) ;
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
}
}
2005-09-20 13:26:39 +00:00
}
2012-02-08 08:15:06 +00:00
return buff ;
2005-09-20 13:26:39 +00:00
}
/**
Tests if all characters in the wide string are numeric
*/
static int iswnumeric ( const wchar_t * n )
{
2006-05-21 21:46:01 +00:00
for ( ; * n ; n + + )
{
if ( * n < L ' 0 ' | | * n > L ' 9 ' )
{
return 0 ;
}
}
return 1 ;
2005-09-20 13:26:39 +00:00
}
/**
See if the process described by \ c proc matches the commandline \ c
cmd
*/
2012-07-21 03:39:31 +00:00
static bool match_pid ( const wcstring & cmd ,
2012-07-16 19:05:36 +00:00
const wchar_t * proc ,
int flags ,
2012-08-04 18:07:42 +00:00
size_t * offset )
2005-09-20 13:26:39 +00:00
{
2012-07-16 19:05:36 +00: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. */
2012-07-21 03:39:31 +00:00
if ( proc [ 0 ] ! = L ' \0 ' & & wcsncmp ( cmd . c_str ( ) , proc , wcslen ( proc ) ) = = 0 )
2006-12-14 11:58:11 +00:00
{
if ( offset )
* offset = 0 ;
2012-07-16 19:05:36 +00:00
return true ;
2006-12-14 11:58:11 +00:00
}
2012-07-16 19:05:36 +00:00
/* Get the command to match against. We're only interested in the last path component. */
const wcstring base_cmd = wbasename ( cmd ) ;
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 03:39:31 +00:00
* offset = cmd . size ( ) - base_cmd . size ( ) ;
2012-07-16 19:05:36 +00:00
}
return result ;
}
2006-01-30 19:53:10 +00:00
2012-07-16 19:05:36 +00:00
/** Helper class for iterating over processes. The names returned have been unescaped (e.g. may include spaces) */
# ifdef KERN_PROCARGS2
2006-01-30 19:53:10 +00:00
2012-07-16 19:05:36 +00:00
/* BSD / OS X process completions */
2006-01-30 19:53:10 +00:00
2012-07-16 19:05:36 +00:00
class process_iterator_t {
std : : vector < pid_t > pids ;
size_t idx ;
wcstring name_for_pid ( pid_t pid ) ;
public :
process_iterator_t ( ) ;
bool next_process ( wcstring * str , pid_t * pid ) ;
} ;
2006-12-14 11:58:11 +00:00
2012-07-16 19:05:36 +00: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 ;
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 ;
}
2006-01-30 19:53:10 +00:00
2012-07-16 19:05:36 +00: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 ;
// 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 */
2012-08-04 18:07:42 +00:00
pid = fish_wcstoi ( name . c_str ( ) , NULL , 10 ) ;
2012-07-16 19:05:36 +00:00
/* the 'cmdline' file exists, it should contain the commandline */
FILE * cmdfile ;
2012-07-16 19:19:41 +00:00
if ( ( cmdfile = wfopen ( path + L " /cmdline " , " r " ) ) )
2012-07-16 19:05:36 +00:00
{
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
2012-07-16 19:19:41 +00:00
else if ( ( cmdfile = wfopen ( path + L " /psinfo " , " r " ) ) )
2012-07-16 19:05:36 +00: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 ) ;
}
bool result = ! cmd . empty ( ) ;
if ( result )
{
* out_str = cmd ;
* out_pid = pid ;
}
return result ;
}
2006-01-30 19:53:10 +00:00
2012-07-16 19:05:36 +00: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 13:26:39 +00: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 08:11:31 +00:00
If the ACCEPT_INCOMPLETE flag is set , the remaining string for any matches
2005-11-29 19:50:30 +00:00
are inserted .
2005-09-20 13:26:39 +00:00
2007-02-24 08:11:31 +00: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 13:26:39 +00:00
*/
2006-01-30 19:53:10 +00:00
static int find_process ( const wchar_t * proc ,
int flags ,
2012-01-16 16:56:47 +00:00
std : : vector < completion_t > & out )
2005-09-20 13:26:39 +00:00
{
int found = 0 ;
2006-01-30 19:53:10 +00:00
2012-02-28 02:43:24 +00:00
const job_t * j ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( iswnumeric ( proc ) | | ( wcslen ( proc ) = = 0 ) )
{
/*
2006-01-30 19:53:10 +00:00
This is a numeric job string , like ' % 2 '
2005-09-20 13:26:39 +00:00
*/
if ( flags & ACCEPT_INCOMPLETE )
{
2012-01-30 00:36:21 +00:00
job_iterator_t jobs ;
while ( ( j = jobs . next ( ) ) )
2005-09-20 13:26:39 +00:00
{
wchar_t jid [ 16 ] ;
2012-03-09 07:21:07 +00:00
if ( j - > command_is_empty ( ) )
2005-09-20 13:26:39 +00:00
continue ;
swprintf ( jid , 16 , L " %d " , j - > job_id ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( wcsncmp ( proc , jid , wcslen ( proc ) ) = = 0 )
{
2012-03-09 07:21:07 +00:00
wcstring desc_buff = format_string ( COMPLETE_JOB_DESC_VAL , j - > command_wcstr ( ) ) ;
2012-07-17 19:47:01 +00:00
append_completion ( out ,
2007-02-24 08:11:31 +00:00
jid + wcslen ( proc ) ,
2012-02-22 20:00:02 +00:00
desc_buff ,
2007-02-24 08:11:31 +00:00
0 ) ;
2005-09-20 13:26:39 +00:00
}
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
else
{
2006-01-30 19:53:10 +00:00
2007-01-09 03:20:05 +00:00
int jid ;
wchar_t * end ;
2011-12-27 03:18:46 +00:00
2007-01-09 03:20:05 +00:00
errno = 0 ;
2012-08-04 18:07:42 +00:00
jid = fish_wcstoi ( proc , & end , 10 ) ;
2007-01-09 03:20:05 +00:00
if ( jid > 0 & & ! errno & & ! * end )
2005-09-20 13:26:39 +00:00
{
2006-04-28 13:21:37 +00:00
j = job_get ( jid ) ;
2012-03-09 07:21:07 +00:00
if ( ( j ! = 0 ) & & ( j - > command_wcstr ( ) ! = 0 ) )
2005-09-20 13:26:39 +00:00
{
2006-04-28 13:21:37 +00:00
{
2012-07-17 19:47:01 +00:00
append_completion ( out , to_string < long > ( j - > pgid ) ) ;
2006-04-28 13:21:37 +00:00
found = 1 ;
}
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
}
}
if ( found )
return 1 ;
2012-01-30 00:36:21 +00:00
job_iterator_t jobs ;
while ( ( j = jobs . next ( ) ) )
2005-09-20 13:26:39 +00:00
{
2011-12-27 03:18:46 +00:00
2012-07-16 19:05:36 +00:00
if ( j - > command_is_empty ( ) )
2005-09-20 13:26:39 +00:00
continue ;
2012-08-04 18:07:42 +00:00
size_t offset ;
2012-07-21 03:39:31 +00:00
if ( match_pid ( j - > command ( ) , proc , flags , & offset ) )
2005-09-20 13:26:39 +00:00
{
if ( flags & ACCEPT_INCOMPLETE )
{
2012-07-17 19:47:01 +00:00
append_completion ( out ,
2012-03-09 07:21:07 +00:00
j - > command_wcstr ( ) + offset + wcslen ( proc ) ,
2007-02-24 08:11:31 +00:00
COMPLETE_JOB_DESC ,
0 ) ;
2005-09-20 13:26:39 +00:00
}
else
{
2012-07-17 19:47:01 +00:00
append_completion ( out , to_string < long > ( j - > pgid ) ) ;
2005-09-20 13:26:39 +00:00
found = 1 ;
}
}
}
if ( found )
{
return 1 ;
}
2012-01-30 00:36:21 +00:00
jobs . reset ( ) ;
while ( ( j = jobs . next ( ) ) )
2005-09-20 13:26:39 +00:00
{
process_t * p ;
2012-03-09 07:21:07 +00:00
if ( j - > command_is_empty ( ) )
2005-09-20 13:26:39 +00:00
continue ;
for ( p = j - > first_process ; p ; p = p - > next )
{
2012-07-21 03:39:31 +00:00
if ( p - > actual_cmd . empty ( ) )
2005-09-20 13:26:39 +00:00
continue ;
2012-08-04 18:07:42 +00:00
size_t offset ;
2006-12-14 11:58:11 +00:00
if ( match_pid ( p - > actual_cmd , proc , flags , & offset ) )
2005-09-20 13:26:39 +00:00
{
if ( flags & ACCEPT_INCOMPLETE )
{
2012-07-17 19:47:01 +00:00
append_completion ( out ,
2012-07-21 03:39:31 +00:00
wcstring ( p - > actual_cmd , offset + wcslen ( proc ) ) ,
2007-02-24 08:11:31 +00:00
COMPLETE_CHILD_PROCESS_DESC ,
0 ) ;
2005-09-20 13:26:39 +00:00
}
else
{
2012-07-17 19:47:01 +00:00
append_completion ( out ,
to_string < long > ( p - > pid ) ,
L " " ,
0 ) ;
2005-09-20 13:26:39 +00:00
found = 1 ;
}
}
}
}
if ( found )
{
return 1 ;
}
2012-07-16 19:05:36 +00: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-04 18:07:42 +00:00
size_t offset ;
2012-07-21 03:39:31 +00:00
if ( match_pid ( process_name , proc , flags , & offset ) )
2012-07-16 19:05:36 +00:00
{
if ( flags & ACCEPT_INCOMPLETE )
{
2012-07-17 19:47:01 +00:00
append_completion ( out ,
2012-07-16 19:05:36 +00:00
process_name . c_str ( ) + offset + wcslen ( proc ) ,
COMPLETE_PROCESS_DESC ,
0 ) ;
}
else
{
2012-07-17 19:47:01 +00:00
append_completion ( out , to_string < long > ( process_pid ) ) ;
2012-07-16 19:05:36 +00:00
}
}
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
return 1 ;
}
/**
Process id expansion
*/
2012-07-16 19:05:36 +00:00
static int expand_pid ( const wcstring & instr_with_sep ,
2005-09-20 13:26:39 +00:00
int flags ,
2012-01-16 16:56:47 +00:00
std : : vector < completion_t > & out )
2005-09-20 13:26:39 +00:00
{
2012-01-30 19:15:06 +00:00
2012-07-16 19:05:36 +00:00
/* expand_string calls us with internal separators in instr...sigh */
wcstring instr = instr_with_sep ;
remove_internal_separator ( instr , false ) ;
2012-01-30 19:15:06 +00:00
if ( instr . empty ( ) | | instr . at ( 0 ) ! = PROCESS_EXPAND )
2005-09-20 13:26:39 +00:00
{
2012-07-17 19:47:01 +00:00
append_completion ( out , instr ) ;
2005-09-20 13:26:39 +00:00
return 1 ;
}
2012-01-30 19:15:06 +00:00
const wchar_t * const in = instr . c_str ( ) ;
2005-09-20 13:26:39 +00:00
if ( flags & ACCEPT_INCOMPLETE )
{
if ( wcsncmp ( in + 1 , SELF_STR , wcslen ( in + 1 ) ) = = 0 )
{
2012-07-17 19:47:01 +00:00
append_completion ( out ,
2007-02-24 08:11:31 +00:00
SELF_STR + wcslen ( in + 1 ) ,
2011-12-27 03:18:46 +00:00
COMPLETE_SELF_DESC ,
2007-02-24 08:11:31 +00:00
0 ) ;
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
else if ( wcsncmp ( in + 1 , LAST_STR , wcslen ( in + 1 ) ) = = 0 )
{
2012-07-17 19:47:01 +00:00
append_completion ( out ,
2011-12-27 03:18:46 +00:00
LAST_STR + wcslen ( in + 1 ) ,
COMPLETE_LAST_DESC ,
2007-02-24 08:11:31 +00:00
0 ) ;
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
}
else
{
if ( wcscmp ( ( in + 1 ) , SELF_STR ) = = 0 )
{
2012-02-02 00:27:14 +00:00
2012-07-17 19:47:01 +00:00
append_completion ( out , to_string < long > ( getpid ( ) ) ) ;
2006-01-30 19:53:10 +00:00
return 1 ;
}
2005-09-20 13:26:39 +00:00
if ( wcscmp ( ( in + 1 ) , LAST_STR ) = = 0 )
{
if ( proc_last_bg_pid > 0 )
{
2012-07-17 19:47:01 +00:00
append_completion ( out , to_string < long > ( proc_last_bg_pid ) ) ;
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
return 1 ;
}
}
2012-01-30 07:22:42 +00:00
size_t prev = out . size ( ) ;
2005-09-20 13:26:39 +00:00
if ( ! find_process ( in + 1 , flags , out ) )
return 0 ;
2006-01-30 19:53:10 +00:00
2012-01-16 16:56:47 +00:00
if ( prev = = out . size ( ) )
2005-09-20 13:26:39 +00:00
{
2012-01-30 19:15:06 +00:00
if ( ! ( flags & ACCEPT_INCOMPLETE ) )
2005-09-20 13:26:39 +00:00
{
2006-10-19 15:41:27 +00:00
return 0 ;
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
return 1 ;
}
2012-01-30 19:15:06 +00:00
2006-07-20 13:02:46 +00:00
2012-08-04 20:02:44 +00:00
void expand_variable_error ( parser_t & parser , const wchar_t * token , size_t token_pos , int error_pos )
2006-07-20 13:02:46 +00:00
{
2012-08-04 20:02:44 +00:00
size_t stop_pos = token_pos + 1 ;
2011-12-27 03:18:46 +00:00
2006-07-20 13:02:46 +00:00
switch ( token [ stop_pos ] )
{
case BRACKET_BEGIN :
{
2006-07-20 23:33:19 +00: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 ;
2011-12-27 03:18:46 +00:00
2006-07-20 23:33:19 +00:00
if ( ! wcsvarname ( name ) )
{
is_var = 1 ;
}
}
2011-12-27 03:18:46 +00:00
2006-07-20 23:33:19 +00:00
if ( is_var )
{
2012-01-20 19:24:43 +00:00
parser . error ( SYNTAX_ERROR ,
2006-07-20 23:33:19 +00:00
error_pos ,
COMPLETE_VAR_BRACKET_DESC ,
cpy ,
name ,
2011-12-27 03:18:46 +00:00
post ) ;
2006-07-20 23:33:19 +00:00
}
else
{
2012-01-20 19:24:43 +00:00
parser . error ( SYNTAX_ERROR ,
2006-07-20 23:33:19 +00:00
error_pos ,
COMPLETE_VAR_BRACKET_DESC ,
L " " ,
L " VARIABLE " ,
L " " ) ;
}
free ( cpy ) ;
2011-12-27 03:18:46 +00:00
2006-07-20 13:02:46 +00:00
break ;
}
2011-12-27 03:18:46 +00:00
2006-07-20 13:02:46 +00:00
case INTERNAL_SEPARATOR :
{
2012-01-20 19:24:43 +00:00
parser . error ( SYNTAX_ERROR ,
2006-07-20 13:02:46 +00:00
error_pos ,
2011-12-27 03:18:46 +00:00
COMPLETE_VAR_PARAN_DESC ) ;
2006-07-20 13:02:46 +00:00
break ;
}
2011-12-27 03:18:46 +00:00
2006-07-20 13:02:46 +00:00
case 0 :
{
2012-01-20 19:24:43 +00:00
parser . error ( SYNTAX_ERROR ,
2006-07-20 13:02:46 +00:00
error_pos ,
COMPLETE_VAR_NULL_DESC ) ;
break ;
}
2011-12-27 03:18:46 +00:00
2006-07-20 13:02:46 +00:00
default :
{
2012-07-09 01:40:50 +00:00
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-01-20 19:24:43 +00:00
parser . error ( SYNTAX_ERROR ,
2006-07-20 13:02:46 +00:00
error_pos ,
2012-07-09 01:51:52 +00:00
( token_stop_char = = L ' ? ' ? COMPLETE_YOU_WANT_STATUS : COMPLETE_VAR_DESC ) ,
2012-07-09 01:40:50 +00:00
token_stop_char ) ;
2006-07-20 13:02:46 +00:00
break ;
}
}
}
2008-01-13 16:47:47 +00:00
/**
Parse an array slicing specification
*/
2012-08-04 20:02:44 +00:00
static int parse_slice ( const wchar_t * in , wchar_t * * end_ptr , std : : vector < long > & idx , size_t array_size )
2011-12-27 03:18:46 +00:00
{
wchar_t * end ;
2012-08-04 20:02:44 +00:00
const long size = ( long ) array_size ;
size_t pos = 1 ; //skip past the opening square bracket
2011-12-27 03:18:46 +00:00
// debug( 0, L"parse_slice on '%ls'", in );
while ( 1 )
{
long tmp ;
while ( iswspace ( in [ pos ] ) | | ( in [ pos ] = = INTERNAL_SEPARATOR ) )
pos + + ;
if ( in [ pos ] = = L ' ] ' )
{
pos + + ;
break ;
}
errno = 0 ;
tmp = wcstol ( & in [ pos ] , & end , 10 ) ;
if ( ( errno ) | | ( end = = & in [ pos ] ) )
{
return 1 ;
}
// debug( 0, L"Push idx %d", tmp );
2012-08-04 20:02:44 +00:00
long i1 = tmp > - 1 ? tmp : ( long ) array_size + tmp + 1 ;
2011-12-27 03:18:46 +00:00
pos = end - in ;
2012-07-08 02:55:48 +00:00
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 ;
}
2011-12-27 03:18:46 +00:00
pos = end - in ;
2012-07-08 02:55:48 +00:00
// 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-07-07 03:04:29 +00:00
// debug( 0, L"Push idx %d", tmp );
2012-07-08 02:55:48 +00:00
idx . push_back ( i1 ) ;
2011-12-27 03:18:46 +00:00
}
if ( end_ptr )
{
// debug( 0, L"Remainder is '%ls', slice def was %d characters long", in+pos, pos );
* end_ptr = ( wchar_t * ) ( in + pos ) ;
}
// debug( 0, L"ok, done" );
2006-08-24 13:39:04 +00:00
return 0 ;
}
2006-02-12 19:03:01 +00: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-08-04 20:02:44 +00:00
static int expand_variables_internal ( parser_t & parser , wchar_t * const in , std : : vector < completion_t > & out , long last_idx ) ;
2012-01-31 05:33:15 +00:00
2012-08-04 20:02:44 +00:00
static int expand_variables2 ( parser_t & parser , const wcstring & instr , std : : vector < completion_t > & out , long last_idx ) {
2012-01-31 05:33:15 +00:00
wchar_t * in = wcsdup ( instr . c_str ( ) ) ;
int result = expand_variables_internal ( parser , in , out , last_idx ) ;
free ( in ) ;
return result ;
}
2012-08-04 20:02:44 +00:00
static int expand_variables_internal ( parser_t & parser , wchar_t * const in , std : : vector < completion_t > & out , long last_idx )
2011-12-27 03:18:46 +00:00
{
2012-01-16 16:56:47 +00:00
int is_ok = 1 ;
int empty = 0 ;
2012-01-31 05:33:15 +00:00
wcstring var_tmp ;
std : : vector < long > var_idx_list ;
// CHECK( out, 0 );
2012-01-16 16:56:47 +00:00
2012-08-04 20:02:44 +00:00
for ( long i = last_idx ; ( i > = 0 ) & & is_ok & & ! empty ; i - - )
2012-01-16 16:56:47 +00:00
{
2012-01-31 05:33:15 +00:00
const wchar_t c = in [ i ] ;
2012-01-16 16:56:47 +00:00
if ( ( c = = VARIABLE_EXPAND ) | | ( c = = VARIABLE_EXPAND_SINGLE ) )
{
2012-08-04 20:02:44 +00:00
long start_pos = i + 1 ;
long stop_pos ;
long var_len ;
2012-01-16 16:56:47 +00:00
int is_single = ( c = = VARIABLE_EXPAND_SINGLE ) ;
stop_pos = start_pos ;
2012-01-31 05:33:15 +00:00
2012-01-16 16:56:47 +00:00
while ( 1 )
{
if ( ! ( in [ stop_pos ] ) )
break ;
if ( ! ( iswalnum ( in [ stop_pos ] ) | |
2012-01-31 05:33:15 +00:00
( wcschr ( L " _ " , in [ stop_pos ] ) ! = 0 ) ) )
2012-01-16 16:56:47 +00:00
break ;
2012-01-31 05:33:15 +00:00
2012-01-16 16:56:47 +00:00
stop_pos + + ;
}
2012-03-26 08:21:10 +00:00
2012-01-31 05:33:15 +00:00
/* printf( "Stop for '%c'\n", in[stop_pos]);*/
2012-01-16 16:56:47 +00:00
var_len = stop_pos - start_pos ;
2012-01-31 05:33:15 +00:00
2012-01-16 16:56:47 +00:00
if ( var_len = = 0 )
{
2012-01-30 18:28:30 +00:00
expand_variable_error ( parser , in , stop_pos - 1 , - 1 ) ;
2012-01-16 16:56:47 +00:00
is_ok = 0 ;
break ;
}
2012-01-31 05:33:15 +00:00
var_tmp . append ( in + start_pos , var_len ) ;
2012-02-26 02:54:49 +00:00
env_var_t var_val = expand_var ( var_tmp . c_str ( ) ) ;
2012-01-31 05:33:15 +00:00
2012-02-26 02:54:49 +00:00
if ( ! var_val . missing ( ) )
2012-01-16 16:56:47 +00:00
{
int all_vars = 1 ;
2012-01-31 05:33:15 +00:00
wcstring_list_t var_item_list ;
2012-01-16 16:56:47 +00:00
if ( is_ok )
{
2012-07-08 02:55:48 +00:00
tokenize_variable_array ( var_val . c_str ( ) , var_item_list ) ;
2012-01-16 16:56:47 +00:00
if ( in [ stop_pos ] = = L ' [ ' )
{
wchar_t * slice_end ;
all_vars = 0 ;
2012-07-08 02:55:48 +00:00
if ( parse_slice ( in + stop_pos , & slice_end , var_idx_list , var_item_list . size ( ) ) )
2012-01-16 16:56:47 +00:00
{
2012-01-30 18:28:30 +00:00
parser . error ( SYNTAX_ERROR ,
2012-01-31 05:33:15 +00:00
- 1 ,
L " Invalid index value " ) ;
2012-01-16 16:56:47 +00:00
is_ok = 0 ;
2012-07-08 02:55:48 +00:00
break ;
2012-01-16 16:56:47 +00:00
}
stop_pos = ( slice_end - in ) ;
}
2012-01-31 05:33:15 +00:00
2012-01-16 16:56:47 +00:00
if ( ! all_vars )
{
2012-01-31 05:33:15 +00:00
wcstring_list_t string_values ( var_idx_list . size ( ) ) ;
for ( size_t j = 0 ; j < var_idx_list . size ( ) ; j + + )
2012-01-16 16:56:47 +00:00
{
2012-01-31 05:33:15 +00:00
long tmp = var_idx_list . at ( j ) ;
2012-01-16 16:56:47 +00:00
/*
2012-01-31 05:33:15 +00:00
Check that we are within array
bounds . If not , truncate the list to
exit .
*/
if ( tmp < 1 | | ( size_t ) tmp > var_item_list . size ( ) )
2012-01-16 16:56:47 +00:00
{
2012-01-30 18:28:30 +00:00
parser . error ( SYNTAX_ERROR ,
2012-01-31 05:33:15 +00:00
- 1 ,
ARRAY_BOUNDS_ERR ) ;
2012-01-16 16:56:47 +00:00
is_ok = 0 ;
2012-01-31 05:33:15 +00:00
var_idx_list . resize ( j ) ;
2012-01-16 16:56:47 +00:00
break ;
}
else
{
/* Replace each index in var_idx_list inplace with the string value at the specified index */
2012-01-31 05:33:15 +00:00
//al_set( var_idx_list, j, wcsdup((const wchar_t *)al_get( &var_item_list, tmp-1 ) ) );
string_values . at ( j ) = var_item_list . at ( tmp - 1 ) ;
2012-01-16 16:56:47 +00:00
}
}
2012-01-31 05:33:15 +00:00
// string_values is the new var_item_list
var_item_list . swap ( string_values ) ;
2012-01-16 16:56:47 +00:00
}
}
2012-01-31 05:33:15 +00:00
2012-01-16 16:56:47 +00:00
if ( is_ok )
{
if ( is_single )
{
2012-01-31 05:33:15 +00:00
in [ i ] = 0 ;
wcstring res = in ;
res . push_back ( INTERNAL_SEPARATOR ) ;
for ( size_t j = 0 ; j < var_item_list . size ( ) ; j + + )
2012-01-16 16:56:47 +00:00
{
2012-01-31 05:33:15 +00:00
const wcstring & next = var_item_list . at ( j ) ;
2012-01-16 16:56:47 +00:00
if ( is_ok )
{
if ( j ! = 0 )
2012-01-31 05:33:15 +00:00
res . append ( L " " ) ;
res . append ( next ) ;
2012-01-16 16:56:47 +00:00
}
}
2012-01-31 05:33:15 +00:00
res . append ( in + stop_pos ) ;
is_ok & = expand_variables2 ( parser , res , out , i ) ;
2012-01-16 16:56:47 +00:00
}
else
{
2012-01-31 05:33:15 +00:00
for ( size_t j = 0 ; j < var_item_list . size ( ) ; j + + )
2012-01-16 16:56:47 +00:00
{
2012-01-31 05:33:15 +00:00
const wcstring & next = var_item_list . at ( j ) ;
2012-01-16 16:56:47 +00:00
if ( is_ok & & ( i = = 0 ) & & ( ! in [ stop_pos ] ) )
{
2012-07-17 19:47:01 +00:00
append_completion ( out , next ) ;
2012-01-16 16:56:47 +00:00
}
else
{
if ( is_ok )
{
2012-01-31 05:33:15 +00:00
wcstring new_in ;
if ( start_pos > 0 )
new_in . append ( in , start_pos - 1 ) ;
// at this point new_in.size() is start_pos - 1
if ( start_pos > 1 & & new_in [ start_pos - 2 ] ! = VARIABLE_EXPAND )
{
new_in . push_back ( INTERNAL_SEPARATOR ) ;
}
new_in . append ( next ) ;
new_in . append ( in + stop_pos ) ;
is_ok & = expand_variables2 ( parser , new_in , out , i ) ;
2012-01-16 16:56:47 +00:00
}
}
}
}
}
return is_ok ;
}
else
{
/*
2012-01-31 05:33:15 +00:00
Expand a non - existing variable
*/
2012-01-16 16:56:47 +00:00
if ( c = = VARIABLE_EXPAND )
{
/*
2012-01-31 05:33:15 +00:00
Regular expansion , i . e . expand this argument to nothing
*/
2012-01-16 16:56:47 +00:00
empty = 1 ;
}
else
{
/*
2012-01-31 05:33:15 +00:00
Expansion to single argument .
*/
wcstring res ;
in [ i ] = 0 ;
res . append ( in ) ;
res . append ( in + stop_pos ) ;
is_ok & = expand_variables2 ( parser , res , out , i ) ;
2012-01-16 16:56:47 +00:00
return is_ok ;
}
}
2012-01-31 05:33:15 +00:00
2012-01-16 16:56:47 +00:00
}
}
2012-01-31 05:33:15 +00:00
2012-01-16 16:56:47 +00:00
if ( ! empty )
{
2012-07-17 19:47:01 +00:00
append_completion ( out , in ) ;
2012-01-16 16:56:47 +00:00
}
2012-01-31 05:33:15 +00:00
2012-01-16 16:56:47 +00:00
return is_ok ;
2011-12-27 03:18:46 +00:00
}
2005-09-20 13:26:39 +00:00
/**
Perform bracket expansion
*/
2012-02-01 01:06:47 +00:00
static int expand_brackets ( parser_t & parser , const wchar_t * in , int flags , std : : vector < completion_t > & out )
2005-09-20 13:26:39 +00:00
{
2012-02-01 01:06:47 +00:00
const wchar_t * pos ;
2005-09-20 13:26:39 +00:00
int syntax_error = 0 ;
2006-01-30 19:53:10 +00:00
int bracket_count = 0 ;
2005-09-20 13:26:39 +00:00
2012-02-01 01:06:47 +00:00
const wchar_t * bracket_begin = 0 , * bracket_end = 0 ;
const wchar_t * last_sep = 0 ;
2006-01-30 19:53:10 +00:00
2012-02-01 01:06:47 +00:00
const wchar_t * item_begin ;
2012-08-04 20:02:44 +00:00
size_t len1 , len2 , tot_len ;
2006-06-20 00:50:10 +00:00
2006-07-21 01:08:31 +00:00
CHECK ( in , 0 ) ;
2012-01-16 16:56:47 +00:00
// CHECK( out, 0 );
2011-12-27 03:18:46 +00:00
2006-01-30 19:53:10 +00:00
for ( pos = in ;
2006-06-17 14:26:52 +00:00
( * pos ) & & ! syntax_error ;
2005-09-20 13:26:39 +00:00
pos + + )
{
switch ( * pos )
{
case BRACKET_BEGIN :
{
2006-06-17 14:26:52 +00:00
bracket_begin = pos ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
bracket_count + + ;
break ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
case BRACKET_END :
{
bracket_count - - ;
2006-06-17 14:26:52 +00:00
if ( bracket_end < bracket_begin )
{
2005-09-20 13:26:39 +00:00
bracket_end = pos ;
2006-06-17 14:26:52 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
if ( bracket_count < 0 )
{
syntax_error = 1 ;
}
break ;
}
case BRACKET_SEP :
{
if ( bracket_count = = 1 )
last_sep = pos ;
}
}
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( bracket_count > 0 )
{
if ( ! ( flags & ACCEPT_INCOMPLETE ) )
2006-06-17 14:26:52 +00:00
{
2005-09-20 13:26:39 +00:00
syntax_error = 1 ;
2006-06-17 14:26:52 +00:00
}
2005-09-20 13:26:39 +00:00
else
{
2012-02-22 20:00:02 +00:00
wcstring mod ;
2005-09-20 13:26:39 +00:00
if ( last_sep )
{
2012-02-22 20:00:02 +00:00
mod . append ( in , bracket_begin - in + 1 ) ;
mod . append ( last_sep + 1 ) ;
mod . push_back ( BRACKET_END ) ;
2005-09-20 13:26:39 +00:00
}
else
{
2012-02-22 20:00:02 +00:00
mod . append ( in ) ;
mod . push_back ( BRACKET_END ) ;
2005-09-20 13:26:39 +00:00
}
2012-02-22 20:00:02 +00:00
return expand_brackets ( parser , mod . c_str ( ) , 1 , out ) ;
2005-09-20 13:26:39 +00:00
}
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( syntax_error )
{
2012-01-23 04:47:13 +00:00
parser . error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
- 1 ,
2006-02-14 11:48:04 +00:00
_ ( L " Mismatched brackets " ) ) ;
2005-09-20 13:26:39 +00:00
return 0 ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( bracket_begin = = 0 )
{
2012-07-17 19:47:01 +00:00
append_completion ( out , in ) ;
2005-09-20 13:26:39 +00:00
return 1 ;
}
len1 = ( bracket_begin - in ) ;
len2 = wcslen ( bracket_end ) - 1 ;
tot_len = len1 + len2 ;
item_begin = bracket_begin + 1 ;
for ( pos = ( bracket_begin + 1 ) ; 1 ; pos + + )
{
if ( bracket_count = = 0 )
{
if ( ( * pos = = BRACKET_SEP ) | | ( pos = = bracket_end ) )
{
wchar_t * whole_item ;
2012-08-04 20:02:44 +00:00
assert ( pos > = item_begin ) ;
size_t item_len = pos - item_begin ;
2006-01-30 19:53:10 +00:00
2011-12-27 03:18:46 +00:00
whole_item = ( wchar_t * ) malloc ( sizeof ( wchar_t ) * ( tot_len + item_len + 1 ) ) ;
2006-04-13 12:18:51 +00:00
wcslcpy ( whole_item , in , len1 + 1 ) ;
wcslcpy ( whole_item + len1 , item_begin , item_len + 1 ) ;
2005-09-20 13:26:39 +00:00
wcscpy ( whole_item + len1 + item_len , bracket_end + 1 ) ;
2006-01-30 19:53:10 +00:00
2012-01-23 04:47:13 +00:00
expand_brackets ( parser , whole_item , flags , out ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
item_begin = pos + 1 ;
if ( pos = = bracket_end )
break ;
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( * pos = = BRACKET_BEGIN )
{
bracket_count + + ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( * pos = = BRACKET_END )
{
bracket_count - - ;
}
}
2006-01-30 19:53:10 +00:00
return 1 ;
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
/**
Perform cmdsubst expansion
*/
2012-02-01 05:09:11 +00:00
static int expand_cmdsubst ( parser_t & parser , const wcstring & input , std : : vector < completion_t > & outList )
2011-12-27 03:18:46 +00:00
{
wchar_t * paran_begin = 0 , * paran_end = 0 ;
std : : vector < wcstring > sub_res ;
size_t i , j ;
wchar_t * tail_begin = 0 ;
const wchar_t * const in = input . c_str ( ) ;
2012-01-18 18:33:19 +00:00
int parse_ret ;
switch ( parse_ret = parse_util_locate_cmdsubst ( in ,
2012-02-02 00:27:14 +00:00
& paran_begin ,
& paran_end ,
0 ) )
2011-12-27 03:18:46 +00:00
{
case - 1 :
2012-01-23 04:47:13 +00:00
parser . error ( SYNTAX_ERROR ,
2012-02-02 00:27:14 +00:00
- 1 ,
2012-07-24 13:39:03 +00:00
L " Mismatched parenthesis " ) ;
2011-12-27 03:18:46 +00:00
return 0 ;
case 0 :
2012-02-02 00:27:14 +00:00
outList . push_back ( completion_t ( input ) ) ;
2011-12-27 03:18:46 +00:00
return 1 ;
case 1 :
break ;
}
2012-08-04 20:02:44 +00:00
2011-12-27 03:18:46 +00:00
const wcstring subcmd ( paran_begin + 1 , paran_end - paran_begin - 1 ) ;
2012-02-08 07:35:41 +00:00
if ( exec_subshell ( subcmd , sub_res ) = = - 1 )
2011-12-27 03:18:46 +00:00
{
2012-01-23 04:47:13 +00:00
parser . error ( CMDSUBST_ERROR , - 1 , L " Unknown error while evaulating command substitution " ) ;
2011-12-27 03:18:46 +00:00
return 0 ;
}
tail_begin = paran_end + 1 ;
if ( * tail_begin = = L ' [ ' )
{
std : : vector < long > slice_idx ;
wchar_t * slice_end ;
2012-07-07 14:01:28 +00:00
if ( parse_slice ( tail_begin , & slice_end , slice_idx , sub_res . size ( ) ) )
2011-12-27 03:18:46 +00:00
{
2012-01-23 04:47:13 +00:00
parser . error ( SYNTAX_ERROR , - 1 , L " Invalid index value " ) ;
2011-12-27 03:18:46 +00:00
return 0 ;
}
else
{
std : : vector < wcstring > sub_res2 ;
tail_begin = slice_end ;
for ( i = 0 ; i < slice_idx . size ( ) ; i + + )
{
long idx = slice_idx . at ( i ) ;
if ( idx < 1 | | ( size_t ) idx > sub_res . size ( ) )
{
2012-07-08 02:55:48 +00:00
parser . error ( SYNTAX_ERROR ,
- 1 ,
ARRAY_BOUNDS_ERR ) ;
2011-12-27 03:18:46 +00:00
return 0 ;
}
idx = idx - 1 ;
sub_res2 . push_back ( sub_res . at ( idx ) ) ;
// debug( 0, L"Pushing item '%ls' with index %d onto sliced result", al_get( sub_res, idx ), idx );
//sub_res[idx] = 0; // ??
}
sub_res = sub_res2 ;
}
}
/*
Recursively call ourselves to expand any remaining command
substitutions . The result of this recursive call using the tail
of the string is inserted into the tail_expand array list
*/
2012-01-16 16:56:47 +00:00
std : : vector < completion_t > tail_expand ;
2012-02-01 05:09:11 +00:00
expand_cmdsubst ( parser , tail_begin , tail_expand ) ;
2011-12-27 03:18:46 +00: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 + + )
{
wcstring sub_item = sub_res . at ( i ) ;
wcstring sub_item2 = escape_string ( sub_item , 1 ) ;
for ( j = 0 ; j < tail_expand . size ( ) ; j + + )
{
wcstring whole_item ;
2012-01-16 16:56:47 +00:00
wcstring tail_item = tail_expand . at ( j ) . completion ;
2011-12-27 03:18:46 +00:00
//sb_append_substring( &whole_item, in, len1 );
2012-08-04 20:02:44 +00:00
whole_item . append ( in , paran_begin - in ) ;
2011-12-27 03:18:46 +00:00
//sb_append_char( &whole_item, INTERNAL_SEPARATOR );
whole_item . push_back ( INTERNAL_SEPARATOR ) ;
//sb_append_substring( &whole_item, sub_item2, item_len );
whole_item . append ( sub_item2 ) ;
//sb_append_char( &whole_item, INTERNAL_SEPARATOR );
whole_item . push_back ( INTERNAL_SEPARATOR ) ;
//sb_append( &whole_item, tail_item );
whole_item . append ( tail_item ) ;
//al_push( out, whole_item.buff );
2012-02-02 00:27:14 +00:00
outList . push_back ( completion_t ( whole_item ) ) ;
2011-12-27 03:18:46 +00:00
}
}
2006-01-30 19:53:10 +00:00
return 1 ;
2005-09-20 13:26:39 +00:00
}
2006-06-20 00:50:10 +00:00
/**
Wrapper around unescape funtion . Issues an error ( ) on failiure .
*/
2012-02-01 01:01:19 +00:00
__attribute__ ( ( unused ) )
2012-01-23 04:47:13 +00:00
static wchar_t * expand_unescape ( parser_t & parser , const wchar_t * in , int escape_special )
2005-09-20 13:26:39 +00:00
{
2005-10-07 10:36:51 +00:00
wchar_t * res = unescape ( in , escape_special ) ;
if ( ! res )
2012-01-23 04:47:13 +00:00
parser . error ( SYNTAX_ERROR , - 1 , L " Unexpected end of string " ) ;
2005-10-07 10:36:51 +00:00
return res ;
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
static wcstring expand_unescape_string ( const wcstring & in , int escape_special )
{
wcstring tmp = in ;
unescape_string ( tmp , escape_special ) ;
/* Need to detect error here */
return tmp ;
}
2005-09-20 13:26:39 +00:00
/**
2011-12-27 03:18:46 +00:00
Attempts tilde expansion of the string specified , modifying it in place .
2005-09-20 13:26:39 +00:00
*/
2011-12-27 03:18:46 +00:00
static void expand_tilde_internal ( wcstring & input )
2005-09-20 13:26:39 +00:00
{
2011-12-27 03:18:46 +00:00
const wchar_t * const in = input . c_str ( ) ;
2005-09-20 13:26:39 +00:00
if ( in [ 0 ] = = HOME_DIRECTORY )
{
int tilde_error = 0 ;
2011-12-27 03:18:46 +00:00
size_t tail_idx ;
wcstring home ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( in [ 1 ] = = ' / ' | | in [ 1 ] = = ' \0 ' )
{
/* Current users home directory */
2006-01-30 19:53:10 +00:00
2011-12-27 03:18:46 +00:00
home = env_get_string ( L " HOME " ) ;
tail_idx = 1 ;
2005-09-20 13:26:39 +00:00
}
else
{
/* Some other users home directory */
2011-12-27 03:18:46 +00:00
const wchar_t * name_end = wcschr ( in , L ' / ' ) ;
if ( name_end )
{
tail_idx = name_end - in ;
}
else
{
tail_idx = wcslen ( in ) ;
}
2012-03-26 06:31:03 +00:00
wcstring name_str = input . substr ( 1 , tail_idx - 1 ) ;
std : : string name_cstr = wcs2string ( name_str ) ;
struct passwd * userinfo = getpwnam ( name_cstr . c_str ( ) ) ;
if ( userinfo = = NULL )
2005-09-20 13:26:39 +00:00
{
tilde_error = 1 ;
2012-03-26 06:31:03 +00:00
input [ 0 ] = L ' ~ ' ;
2005-09-20 13:26:39 +00:00
}
else
{
2011-12-27 03:18:46 +00:00
home = str2wcstring ( userinfo - > pw_dir ) ;
2005-09-20 13:26:39 +00:00
}
}
2006-01-30 19:53:10 +00:00
2011-12-27 03:18:46 +00:00
if ( ! tilde_error )
{
input . replace ( input . begin ( ) , input . begin ( ) + tail_idx , home ) ;
}
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
2011-12-27 03:18:46 +00:00
void expand_tilde ( wcstring & input )
{
2012-06-02 21:04:25 +00:00
if ( ! input . empty ( ) & & input . at ( 0 ) = = L ' ~ ' )
2005-09-20 13:26:39 +00:00
{
2012-06-02 21:04:25 +00:00
input . at ( 0 ) = HOME_DIRECTORY ;
2011-12-27 03:18:46 +00:00
expand_tilde_internal ( input ) ;
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
}
2005-09-20 13:26:39 +00:00
/**
Remove any internal separators . Also optionally convert wildcard characters to
2006-07-21 01:08:31 +00:00
regular equivalents . This is done to support EXPAND_SKIP_WILDCARDS .
2005-09-20 13:26:39 +00:00
*/
2012-07-16 19:05:36 +00:00
static void remove_internal_separator ( wcstring & str , bool conv )
2005-09-20 13:26:39 +00:00
{
2012-07-16 19:05:36 +00:00
/* Remove all instances of INTERNAL_SEPARATOR */
str . erase ( std : : remove ( str . begin ( ) , str . end ( ) , ( wchar_t ) INTERNAL_SEPARATOR ) , str . end ( ) ) ;
/* 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 ) )
{
case ANY_CHAR :
str . at ( idx ) = L ' ? ' ;
break ;
case ANY_STRING :
case ANY_STRING_RECURSIVE :
str . at ( idx ) = L ' * ' ;
break ;
}
}
}
2011-12-27 03:18:46 +00:00
}
2012-02-24 20:13:35 +00:00
int expand_string ( const wcstring & input , std : : vector < completion_t > & output , expand_flags_t flags )
2011-12-27 03:18:46 +00:00
{
2012-05-06 20:36:51 +00:00
parser_t parser ( PARSER_TYPE_ERRORS_ONLY , true /* show errors */ ) ;
2012-01-29 08:41:39 +00:00
std : : vector < completion_t > list1 , list2 ;
2012-01-16 16:56:47 +00:00
std : : vector < completion_t > * in , * out ;
2011-12-27 03:18:46 +00:00
size_t i ;
int res = EXPAND_OK ;
if ( ( ! ( flags & ACCEPT_INCOMPLETE ) ) & & expand_is_clean ( input . c_str ( ) ) )
{
2012-02-02 00:27:14 +00:00
output . push_back ( completion_t ( input ) ) ;
2011-12-27 03:18:46 +00:00
return EXPAND_OK ;
}
2012-01-31 05:33:15 +00:00
2011-12-27 03:18:46 +00:00
if ( EXPAND_SKIP_CMDSUBST & flags )
{
wchar_t * begin , * end ;
if ( parse_util_locate_cmdsubst ( input . c_str ( ) ,
& begin ,
& end ,
1 ) ! = 0 )
{
2012-01-23 04:47:13 +00:00
parser . error ( CMDSUBST_ERROR , - 1 , L " Command substitutions not allowed " ) ;
2011-12-27 03:18:46 +00:00
return EXPAND_ERROR ;
}
2012-02-02 00:27:14 +00:00
list1 . push_back ( completion_t ( input ) ) ;
2011-12-27 03:18:46 +00:00
}
else
{
2012-02-01 05:09:11 +00:00
int cmdsubst_ok = expand_cmdsubst ( parser , input , list1 ) ;
2012-01-31 05:33:15 +00:00
if ( ! cmdsubst_ok )
return EXPAND_ERROR ;
2011-12-27 03:18:46 +00:00
}
2012-01-31 05:33:15 +00:00
in = & list1 ;
out = & list2 ;
2011-12-27 03:18:46 +00:00
2012-01-31 05:33:15 +00:00
for ( i = 0 ; i < in - > size ( ) ; i + + )
{
/*
We accept incomplete strings here , since complete uses
expand_string to expand incomplete strings from the
commandline .
*/
int unescape_flags = UNESCAPE_SPECIAL | UNESCAPE_INCOMPLETE ;
wcstring next = expand_unescape_string ( in - > at ( i ) . completion , unescape_flags ) ;
2011-12-27 03:18:46 +00:00
2012-01-31 05:33:15 +00:00
if ( EXPAND_SKIP_VARIABLES & flags )
{
for ( size_t i = 0 ; i < next . size ( ) ; i + + ) {
if ( next . at ( i ) = = VARIABLE_EXPAND ) {
next [ i ] = L ' $ ' ;
2011-12-27 03:18:46 +00:00
}
2012-01-31 05:33:15 +00:00
}
2012-02-02 00:27:14 +00:00
out - > push_back ( completion_t ( next ) ) ;
2012-01-31 05:33:15 +00:00
}
else
{
if ( ! expand_variables2 ( parser , next , * out , next . size ( ) - 1 ) )
{
return EXPAND_ERROR ;
}
}
}
in - > clear ( ) ;
in = & list2 ;
out = & list1 ;
for ( i = 0 ; i < in - > size ( ) ; i + + )
{
wcstring next = in - > at ( i ) . completion ;
2012-02-01 01:06:47 +00:00
if ( ! expand_brackets ( parser , next . c_str ( ) , flags , * out ) )
2012-01-31 05:33:15 +00:00
{
return EXPAND_ERROR ;
}
}
in - > clear ( ) ;
in = & list1 ;
out = & list2 ;
for ( i = 0 ; i < in - > size ( ) ; i + + )
{
wcstring next = in - > at ( i ) . completion ;
2011-12-27 03:18:46 +00:00
2012-01-31 05:33:15 +00:00
expand_tilde_internal ( next ) ;
2011-12-27 03:18:46 +00:00
2012-01-31 05:33:15 +00:00
if ( flags & ACCEPT_INCOMPLETE )
{
if ( next [ 0 ] = = PROCESS_EXPAND )
{
/*
If process expansion matches , we are not
interested in other completions , so we
short - circut and return
*/
2012-06-29 23:40:54 +00:00
if ( ! ( flags & EXPAND_SKIP_PROCESS ) )
expand_pid ( next , flags , output ) ;
2012-01-31 05:33:15 +00:00
return EXPAND_OK ;
}
else
{
2012-02-02 00:27:14 +00:00
out - > push_back ( completion_t ( next ) ) ;
2012-01-31 05:33:15 +00:00
}
}
else
{
2012-06-29 23:40:54 +00:00
if ( ! ( flags & EXPAND_SKIP_PROCESS ) & & ! expand_pid ( next , flags , * out ) )
2012-01-31 05:33:15 +00:00
{
return EXPAND_ERROR ;
}
}
}
in - > clear ( ) ;
in = & list2 ;
out = & list1 ;
for ( i = 0 ; i < in - > size ( ) ; i + + )
{
wcstring next_str = in - > at ( i ) . completion ;
int wc_res ;
2011-12-27 03:18:46 +00:00
2012-07-16 19:05:36 +00:00
remove_internal_separator ( next_str , ( EXPAND_SKIP_WILDCARDS & flags ) ? true : false ) ;
2012-01-31 05:33:15 +00:00
const wchar_t * next = next_str . c_str ( ) ;
2011-12-27 03:18:46 +00:00
2012-01-31 05:33:15 +00:00
if ( ( ( flags & ACCEPT_INCOMPLETE ) & & ( ! ( flags & EXPAND_SKIP_WILDCARDS ) ) ) | |
wildcard_has ( next , 1 ) )
{
const wchar_t * start , * rest ;
std : : vector < completion_t > * list = out ;
2011-12-27 03:18:46 +00:00
2012-01-31 05:33:15 +00:00
if ( next [ 0 ] = = ' / ' )
{
start = L " / " ;
rest = & next [ 1 ] ;
}
else
{
start = L " " ;
rest = next ;
}
2011-12-27 03:18:46 +00:00
2012-01-31 05:33:15 +00:00
if ( flags & ACCEPT_INCOMPLETE )
{
list = & output ;
}
2011-12-27 03:18:46 +00:00
2012-01-31 05:33:15 +00:00
wc_res = wildcard_expand_string ( rest , start , flags , * list ) ;
2011-12-27 03:18:46 +00:00
2012-01-31 05:33:15 +00:00
if ( ! ( flags & ACCEPT_INCOMPLETE ) )
{
2011-12-27 03:18:46 +00:00
2012-01-31 05:33:15 +00:00
switch ( wc_res )
{
case 0 :
{
if ( ! ( flags & ACCEPT_INCOMPLETE ) )
{
if ( res = = EXPAND_OK )
res = EXPAND_WILDCARD_NO_MATCH ;
break ;
}
}
case 1 :
{
size_t j ;
res = EXPAND_WILDCARD_MATCH ;
sort_completions ( * out ) ;
for ( j = 0 ; j < out - > size ( ) ; j + + )
{
output . push_back ( out - > at ( j ) ) ;
}
out - > clear ( ) ;
break ;
}
case - 1 :
{
return EXPAND_ERROR ;
}
}
}
2011-12-27 03:18:46 +00:00
2012-01-31 05:33:15 +00:00
}
else
{
if ( flags & ACCEPT_INCOMPLETE )
{
}
else
{
2012-02-02 00:27:14 +00:00
output . push_back ( completion_t ( next ) ) ;
2012-01-31 05:33:15 +00:00
}
}
}
2011-12-27 03:18:46 +00:00
return res ;
}
2007-02-18 23:25:20 +00:00
2012-02-24 20:13:35 +00:00
bool expand_one ( wcstring & string , expand_flags_t flags ) {
2012-01-30 10:45:55 +00:00
std : : vector < completion_t > completions ;
bool result = false ;
if ( ( ! ( flags & ACCEPT_INCOMPLETE ) ) & & expand_is_clean ( string . c_str ( ) ) )
{
return true ;
}
2012-02-06 00:42:24 +00:00
if ( expand_string ( string , completions , flags ) ) {
2012-01-30 10:45:55 +00:00
if ( completions . size ( ) = = 1 ) {
string = completions . at ( 0 ) . completion ;
result = true ;
}
2012-01-30 10:23:58 +00:00
}
2012-01-30 10:45:55 +00:00
return result ;
2012-01-30 10:23:58 +00:00
}