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>
# include <termios.h>
# include <dirent.h>
# include <sys/stat.h>
# include <unistd.h>
# include <signal.h>
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'.")
/**
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"$*?\\\"'({})"
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
*/
2006-01-30 19:53:10 +00:00
static int match_pid ( const wchar_t * cmd ,
2005-09-20 13:26:39 +00:00
const wchar_t * proc ,
2006-12-14 11:58:11 +00:00
int flags ,
int * offset )
2005-09-20 13:26:39 +00:00
{
/* Test for direct match */
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
if ( wcsncmp ( cmd , proc , wcslen ( proc ) ) = = 0 )
2006-12-14 11:58:11 +00:00
{
if ( offset )
* offset = 0 ;
2005-09-20 13:26:39 +00:00
return 1 ;
2006-12-14 11:58:11 +00:00
}
2011-12-27 03:18:46 +00:00
2006-01-30 19:53:10 +00:00
/*
2006-02-07 11:48:57 +00:00
Test if the commandline is a path to the command , if so we try
to match against only the command part
2005-09-20 13:26:39 +00:00
*/
wchar_t * first_token = tok_first ( cmd ) ;
if ( first_token = = 0 )
return 0 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
wchar_t * start = 0 ;
wchar_t prev = 0 ;
wchar_t * p ;
2006-01-30 19:53:10 +00:00
/*
2006-02-07 11:48:57 +00:00
This should be done by basename ( ) , if it wasn ' t for the fact
that is does not accept wide strings
2005-09-20 13:26:39 +00:00
*/
for ( p = first_token ; * p ; p + + )
{
if ( * p = = L ' / ' & & prev ! = L ' \\ ' )
start = p ;
prev = * p ;
}
if ( start )
{
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( wcsncmp ( start + 1 , proc , wcslen ( proc ) ) = = 0 )
{
2006-12-14 11:58:11 +00:00
if ( offset )
* offset = start + 1 - first_token ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
free ( first_token ) ;
2006-12-14 11:58:11 +00:00
2005-09-20 13:26:39 +00:00
return 1 ;
}
}
free ( first_token ) ;
2006-01-30 19:53:10 +00:00
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
}
/**
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
{
DIR * dir ;
2006-05-21 21:46:01 +00:00
wchar_t * pdir_name ;
wchar_t * pfile_name ;
2005-09-20 13:26:39 +00:00
wchar_t * cmd = 0 ;
int sz = 0 ;
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 ( ) ) ;
2011-12-27 03:18:46 +00:00
completion_allocate ( 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 ;
jid = wcstol ( proc , & end , 10 ) ;
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
{
2011-12-27 03:18:46 +00:00
2006-04-28 13:21:37 +00:00
{
2012-02-02 00:27:14 +00:00
wcstring result = format_string ( L " %ld " , ( long ) j - > pgid ) ;
out . push_back ( completion_t ( result ) ) ;
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
{
2006-12-14 11:58:11 +00:00
int offset ;
2011-12-27 03:18:46 +00:00
2012-03-09 07:21:07 +00:00
if ( j - > command_wcstr ( ) = = 0 )
2005-09-20 13:26:39 +00:00
continue ;
2011-12-27 03:18:46 +00:00
2012-03-09 07:21:07 +00:00
if ( match_pid ( j - > command_wcstr ( ) , proc , flags , & offset ) )
2005-09-20 13:26:39 +00:00
{
if ( flags & ACCEPT_INCOMPLETE )
{
2011-12-27 03:18:46 +00:00
completion_allocate ( 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-02-02 00:27:14 +00:00
wcstring result = format_string ( L " %ld " , ( long ) j - > pgid ) ;
out . push_back ( completion_t ( result ) ) ;
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 )
{
2006-12-14 11:58:11 +00:00
int offset ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
if ( p - > actual_cmd = = 0 )
continue ;
2006-01-30 19:53:10 +00:00
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 )
{
2011-12-27 03:18:46 +00:00
completion_allocate ( out ,
2007-02-24 08:11:31 +00:00
p - > actual_cmd + offset + wcslen ( proc ) ,
COMPLETE_CHILD_PROCESS_DESC ,
0 ) ;
2005-09-20 13:26:39 +00:00
}
else
{
2012-02-02 00:27:14 +00:00
wcstring result = to_string < int > ( p - > pid ) ;
out . push_back ( completion_t ( result ) ) ;
2005-09-20 13:26:39 +00:00
found = 1 ;
}
}
}
}
if ( found )
{
return 1 ;
}
if ( ! ( dir = opendir ( " /proc " ) ) )
{
/*
2006-01-30 19:53:10 +00:00
This system does not have a / proc filesystem .
2005-09-20 13:26:39 +00:00
*/
return 1 ;
}
2006-01-30 19:53:10 +00:00
2011-12-27 03:18:46 +00:00
pdir_name = ( wchar_t * ) malloc ( sizeof ( wchar_t ) * 256 ) ;
pfile_name = ( wchar_t * ) malloc ( sizeof ( wchar_t ) * 64 ) ;
2006-05-21 21:46:01 +00:00
wcscpy ( pdir_name , L " /proc/ " ) ;
2011-12-27 03:18:46 +00:00
wcstring nameStr ;
while ( wreaddir ( dir , nameStr ) )
2005-09-20 13:26:39 +00:00
{
2011-12-27 03:18:46 +00:00
const wchar_t * name = nameStr . c_str ( ) ;
2005-09-20 13:26:39 +00:00
struct stat buf ;
2006-05-21 21:46:01 +00:00
if ( ! iswnumeric ( name ) )
2005-09-20 13:26:39 +00:00
continue ;
2006-05-21 21:46:01 +00:00
wcscpy ( pdir_name + 6 , name ) ;
if ( wstat ( pdir_name , & buf ) )
2005-09-20 13:26:39 +00:00
{
continue ;
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
if ( buf . st_uid ! = getuid ( ) )
{
continue ;
}
2006-05-21 21:46:01 +00:00
wcscpy ( pfile_name , pdir_name ) ;
wcscat ( pfile_name , L " /cmdline " ) ;
2006-01-30 19:53:10 +00:00
2006-05-21 21:46:01 +00:00
if ( ! wstat ( pfile_name , & buf ) )
2005-09-20 13:26:39 +00:00
{
/*
the ' cmdline ' file exists , it should contain the commandline
*/
FILE * cmdfile ;
2006-01-30 19:53:10 +00:00
2006-05-21 21:46:01 +00:00
if ( ( cmdfile = wfopen ( pfile_name , " r " ) ) = = 0 )
2005-09-20 13:26:39 +00:00
{
wperror ( L " fopen " ) ;
continue ;
}
2006-01-30 19:53:10 +00:00
2006-02-09 15:50:20 +00:00
signal_block ( ) ;
2006-01-30 19:53:10 +00:00
fgetws2 ( & cmd , & sz , cmdfile ) ;
2006-02-09 15:50:20 +00:00
signal_unblock ( ) ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
fclose ( cmdfile ) ;
}
else
{
# ifdef SunOS
2006-05-21 21:46:01 +00:00
wcscpy ( pfile_name , pdir_name ) ;
wcscat ( pfile_name , L " /psinfo " ) ;
if ( ! wstat ( pfile_name , & buf ) )
2005-09-20 13:26:39 +00:00
{
psinfo_t info ;
FILE * psfile ;
2006-01-30 19:53:10 +00:00
2006-05-21 21:46:01 +00:00
if ( ( psfile = wfopen ( pfile_name , " r " ) ) = = 0 )
2005-09-20 13:26:39 +00:00
{
wperror ( L " fopen " ) ;
continue ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( fread ( & info , sizeof ( info ) , 1 , psfile ) )
{
if ( cmd ! = 0 )
free ( cmd ) ;
cmd = str2wcs ( info . pr_fname ) ;
}
fclose ( psfile ) ;
}
else
# endif
{
if ( cmd ! = 0 )
{
* cmd = 0 ;
}
}
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( cmd ! = 0 )
{
2006-12-14 11:58:11 +00:00
int offset ;
2011-12-27 03:18:46 +00:00
2006-12-14 11:58:11 +00:00
if ( match_pid ( cmd , proc , flags , & offset ) )
2006-01-30 19:53:10 +00:00
{
2005-09-20 13:26:39 +00:00
if ( flags & ACCEPT_INCOMPLETE )
{
2011-12-27 03:18:46 +00:00
completion_allocate ( out ,
2007-02-24 08:11:31 +00:00
cmd + offset + wcslen ( proc ) ,
COMPLETE_PROCESS_DESC ,
0 ) ;
2005-09-20 13:26:39 +00:00
}
else
{
2012-02-02 00:27:14 +00:00
if ( name )
out . push_back ( completion_t ( name ) ) ;
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 ( cmd ! = 0 )
free ( cmd ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
free ( pdir_name ) ;
free ( pfile_name ) ;
2006-01-30 19:53:10 +00:00
closedir ( dir ) ;
2005-09-20 13:26:39 +00:00
return 1 ;
}
/**
Process id expansion
*/
2012-01-30 19:15:06 +00:00
static int expand_pid ( const wcstring & instr ,
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
if ( instr . empty ( ) | | instr . at ( 0 ) ! = PROCESS_EXPAND )
2005-09-20 13:26:39 +00:00
{
2012-02-02 00:27:14 +00:00
out . push_back ( completion_t ( 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 )
{
2011-12-27 03:18:46 +00:00
completion_allocate ( 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 )
{
2011-12-27 03:18:46 +00:00
completion_allocate ( out ,
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
const wcstring pid_str = to_string < int > ( getpid ( ) ) ;
out . push_back ( completion_t ( pid_str ) ) ;
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-02-02 00:27:14 +00:00
const wcstring pid_str = to_string < int > ( proc_last_bg_pid ) ;
out . push_back ( completion_t ( pid_str ) ) ;
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-01-20 19:24:43 +00:00
void expand_variable_error ( parser_t & parser , const wchar_t * token , int token_pos , int error_pos )
2006-07-20 13:02:46 +00:00
{
int 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-01-20 19:24:43 +00:00
parser . error ( SYNTAX_ERROR ,
2006-07-20 13:02:46 +00:00
error_pos ,
COMPLETE_VAR_DESC ,
token [ stop_pos ] ) ;
break ;
}
}
}
2008-01-13 16:47:47 +00:00
/**
Parse an array slicing specification
*/
2012-02-02 00:27:14 +00:00
static int parse_slice ( const wchar_t * in , wchar_t * * end_ptr , std : : vector < long > & idx )
2011-12-27 03:18:46 +00:00
{
wchar_t * end ;
int pos = 1 ;
// 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 );
idx . push_back ( tmp ) ;
pos = end - in ;
}
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-01-31 05:33:15 +00:00
static int expand_variables_internal ( parser_t & parser , wchar_t * const in , std : : vector < completion_t > & out , int last_idx ) ;
static int expand_variables2 ( parser_t & parser , const wcstring & instr , std : : vector < completion_t > & out , int last_idx ) {
wchar_t * in = wcsdup ( instr . c_str ( ) ) ;
int result = expand_variables_internal ( parser , in , out , last_idx ) ;
free ( in ) ;
return result ;
}
static int expand_variables_internal ( parser_t & parser , wchar_t * const in , std : : vector < completion_t > & out , int 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-01-31 05:33:15 +00:00
for ( int 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 ) )
{
int start_pos = i + 1 ;
int stop_pos ;
2012-01-31 05:33:15 +00:00
int 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 ( in [ stop_pos ] = = L ' [ ' )
{
wchar_t * slice_end ;
all_vars = 0 ;
2012-02-02 00:27:14 +00:00
if ( parse_slice ( in + stop_pos , & slice_end , var_idx_list ) )
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 ;
}
stop_pos = ( slice_end - in ) ;
}
2012-01-31 05:33:15 +00:00
2012-01-16 16:56:47 +00:00
if ( is_ok )
{
2012-02-26 02:54:49 +00:00
tokenize_variable_array ( var_val . c_str ( ) , var_item_list ) ;
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
if ( tmp < 0 )
{
2012-01-31 05:33:15 +00:00
tmp = ( ( long ) var_item_list . size ( ) ) + tmp + 1 ;
2012-01-16 16:56:47 +00:00
}
2012-01-31 05:33:15 +00:00
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-02-02 00:27:14 +00:00
out . push_back ( completion_t ( 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-02-02 00:27:14 +00:00
out . push_back ( completion_t ( 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 ;
2005-09-20 13:26:39 +00:00
int 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-02-02 00:27:14 +00:00
out . push_back ( completion_t ( 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 ;
int 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 ;
int len1 ;
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 ,
L " Mismatched parans " ) ;
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 ;
}
len1 = ( paran_begin - in ) ;
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-02-02 00:27:14 +00:00
if ( parse_slice ( tail_begin , & slice_end , slice_idx ) )
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 < 0 )
{
idx = sub_res . size ( ) + idx + 1 ;
}
if ( idx < 1 | | ( size_t ) idx > sub_res . size ( ) )
{
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 ;
}
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 );
whole_item . append ( in , len1 ) ;
//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
static wchar_t * expand_tilde_internal_compat ( wchar_t * in )
2005-09-20 13:26:39 +00:00
{
2011-12-27 03:18:46 +00:00
wcstring tmp = in ;
expand_tilde_internal ( tmp ) ;
free ( in ) ;
return wcsdup ( tmp . c_str ( ) ) ;
}
2010-09-18 01:51:16 +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
*/
static void remove_internal_separator ( const void * s , int conv )
{
wchar_t * in = ( wchar_t * ) s ;
wchar_t * out = in ;
2011-12-27 03:18:46 +00:00
2006-07-21 01:08:31 +00:00
CHECK ( s , ) ;
2005-09-20 13:26:39 +00:00
while ( * in )
{
switch ( * in )
{
case INTERNAL_SEPARATOR :
in + + ;
break ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
case ANY_CHAR :
in + + ;
* out + + = conv ? L ' ? ' : ANY_CHAR ;
break ;
case ANY_STRING :
in + + ;
* out + + = conv ? L ' * ' : ANY_STRING ;
break ;
2006-01-30 19:53:10 +00:00
2009-02-02 23:47:51 +00:00
case ANY_STRING_RECURSIVE :
in + + ;
2009-02-21 10:43:30 +00:00
* out + + = conv ? L ' * ' : ANY_STRING_RECURSIVE ;
2009-02-02 23:47:51 +00:00
break ;
2005-09-20 13:26:39 +00:00
default :
* out + + = * in + + ;
}
}
* out = 0 ;
}
2011-12-27 03:18:46 +00:00
static void remove_internal_separator2 ( wcstring & s , int conv )
{
wchar_t * tmp = wcsdup ( s . c_str ( ) ) ;
remove_internal_separator ( tmp , conv ) ;
s = tmp ;
free ( tmp ) ;
}
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
*/
expand_pid ( next , flags , output ) ;
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
{
if ( ! expand_pid ( next , flags , * out ) )
{
return EXPAND_ERROR ;
}
}
}
in - > clear ( ) ;
in = & list2 ;
out = & list1 ;
for ( i = 0 ; i < in - > size ( ) ; i + + )
{
wcstring next_str = in - > at ( i ) . completion ;
int wc_res ;
2011-12-27 03:18:46 +00:00
2012-01-31 05:33:15 +00:00
remove_internal_separator2 ( next_str , EXPAND_SKIP_WILDCARDS & flags ) ;
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
}