2005-09-20 21:26:39 +08:00
/**\file expand.c
String expansion functions . These functions perform several kinds of
2006-02-12 19:29:30 +08:00
parameter expansion .
2005-09-20 21:26:39 +08:00
*/
# include <stdlib.h>
# include <stdio.h>
# include <wchar.h>
# include <string.h>
# include <wctype.h>
# include <errno.h>
# include <pwd.h>
# include <unistd.h>
# include <sys/types.h>
# include <termios.h>
# include <dirent.h>
# include <sys/stat.h>
# include <unistd.h>
# include <signal.h>
2005-12-26 06:00:44 +08:00
# include <assert.h>
2005-09-20 21:26:39 +08:00
# ifdef SunOS
# include <procfs.h>
# endif
# include "config.h"
# include "util.h"
# include "common.h"
# include "wutil.h"
# include "env.h"
# include "proc.h"
# include "parser.h"
# include "expand.h"
# include "wildcard.h"
# include "exec.h"
2006-02-09 23:50:20 +08:00
# include "signal.h"
2005-09-20 21:26:39 +08:00
# include "tokenizer.h"
# include "complete.h"
2006-01-17 20:48:30 +08:00
# include "translate.h"
2006-01-31 00:51:50 +08:00
# include "parse_util.h"
2006-02-10 08:35:39 +08:00
# include "halloc_util.h"
2005-09-20 21:26:39 +08:00
/**
Description for child process
*/
2006-01-17 20:48:30 +08:00
# define COMPLETE_CHILD_PROCESS_DESC _( L"Child process")
2005-09-20 21:26:39 +08:00
/**
Description for non - child process
*/
2006-01-17 20:48:30 +08:00
# define COMPLETE_PROCESS_DESC _( L"Process")
2005-09-20 21:26:39 +08:00
/**
Description for long job
*/
2006-01-17 20:48:30 +08:00
# define COMPLETE_JOB_DESC _( L"Job")
2005-09-20 21:26:39 +08:00
/**
Description for short job . The job command is concatenated
*/
2006-01-17 20:48:30 +08:00
# define COMPLETE_JOB_DESC_VAL _( L"Job: ")
2005-09-20 21:26:39 +08:00
/**
Description for the shells own pid
*/
2006-01-17 20:48:30 +08:00
# define COMPLETE_SELF_DESC _( L"Shell process")
2005-09-20 21:26:39 +08:00
/**
Description for the shells own pid
*/
2006-01-17 20:48:30 +08:00
# define COMPLETE_LAST_DESC _( L"Last background job")
2006-01-24 04:40:14 +08:00
/**
Error issued on invalid variable name
*/
2006-01-21 20:38:28 +08:00
# 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'.")
2006-01-17 20:48:30 +08:00
2006-01-24 04:40:14 +08:00
/**
Error issued on invalid variable name
*/
2006-01-21 20:38:28 +08:00
# 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'.")
2006-01-17 20:48:30 +08:00
2006-01-24 04:40:14 +08:00
/**
Error issued on invalid variable name
*/
2006-01-21 20:38:28 +08:00
# define COMPLETE_VAR_BRACKET_DESC _( L"Did you mean {$VARIABLE}? 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-01-17 20:48:30 +08:00
2006-01-24 04:40:14 +08:00
/**
Error issued on invalid variable name
*/
2006-01-17 20:48:30 +08:00
# define COMPLETE_VAR_PARAN_DESC _( L"Did you mean (COMMAND)? In fish, the '$' character is only used for accessing variables. To learn more about command substitution in fish, type 'help expand-command-substitution'.")
2005-09-20 21:26:39 +08:00
2005-12-07 21:03:30 +08:00
2005-09-20 21:26:39 +08:00
/**
String in process expansion denoting ourself
*/
# define SELF_STR L"self"
/**
String in process expansion denoting last background job
*/
# define LAST_STR L"last"
2005-10-26 18:51:02 +08:00
/**
Characters which make a string unclean if they are the first
character of the string . See \ c is_clean ( ) .
*/
# define UNCLEAN_FIRST L"~%"
/**
Unclean characters . See \ c is_clean ( ) .
*/
# define UNCLEAN L"$*?\\\"'({})"
/**
2005-10-26 22:48:23 +08:00
Test if the specified argument is clean , i . e . it does not contain
any tokens which need to be expanded or otherwise altered . Clean
strings can be passed through expand_string and expand_one without
changing them . About 90 % of all strings are clean , so skipping
2005-12-07 23:57:17 +08:00
expansion on them actually does save a small amount of time , since
it avoids multiple memory allocations during the expansion process .
2005-10-26 18:51:02 +08:00
*/
static int is_clean ( const wchar_t * in )
{
2006-01-31 03:53:10 +08:00
2005-10-26 18:51:02 +08:00
const wchar_t * str = in ;
2005-11-30 00:52:02 +08:00
/*
Test characters that have a special meaning in the first character position
*/
2005-10-26 18:51:02 +08:00
if ( wcschr ( UNCLEAN_FIRST , * str ) )
return 0 ;
2006-01-31 03:53:10 +08:00
2005-11-30 00:52:02 +08:00
/*
Test characters that have a special meaning in any character position
*/
2005-10-26 18:51:02 +08:00
while ( * str )
{
if ( wcschr ( UNCLEAN , * str ) )
return 0 ;
str + + ;
}
2006-01-31 03:53:10 +08:00
2005-10-26 18:51:02 +08:00
return 1 ;
}
2005-09-20 21:26:39 +08:00
/**
2006-01-31 03:53:10 +08:00
Return the environment variable value for the string starting at \ c in .
2005-09-20 21:26:39 +08:00
*/
static wchar_t * expand_var ( wchar_t * in )
{
if ( ! in )
return 0 ;
2005-11-03 00:49:13 +08:00
return env_get ( in ) ;
2005-09-20 21:26:39 +08:00
}
void expand_variable_array ( const wchar_t * val , array_list_t * out )
{
if ( val )
{
wchar_t * cpy = wcsdup ( val ) ;
wchar_t * pos , * start ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( ! cpy )
{
die_mem ( ) ;
}
for ( start = pos = cpy ; * pos ; pos + + )
{
if ( * pos = = ARRAY_SEP )
{
* pos = 0 ;
2006-02-07 02:11:01 +08:00
al_push ( out , start = = cpy ? cpy : wcsdup ( start ) ) ;
2005-09-20 21:26:39 +08:00
start = pos + 1 ;
}
}
2006-02-07 02:11:01 +08:00
al_push ( out , start = = cpy ? cpy : wcsdup ( start ) ) ;
2005-09-20 21:26:39 +08:00
}
}
/**
Test if the specified string does not contain character which can
not be used inside a quoted string .
*/
static int is_quotable ( wchar_t * str )
{
switch ( * str )
{
case 0 :
return 1 ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
case L ' \n ' :
case L ' \t ' :
case L ' \r ' :
case L ' \b ' :
case L ' \e ' :
return 0 ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
default :
2006-01-31 03:53:10 +08:00
return is_quotable ( str + 1 ) ;
2005-09-20 21:26:39 +08:00
}
return 0 ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
wchar_t * expand_escape_variable ( const wchar_t * in )
{
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
array_list_t l ;
string_buffer_t buff ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
al_init ( & l ) ;
expand_variable_array ( in , & l ) ;
sb_init ( & buff ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
switch ( al_get_count ( & l ) )
{
case 0 :
2005-11-03 00:49:13 +08:00
sb_append ( & buff , L " '' " ) ;
2006-01-31 03:53:10 +08:00
break ;
2005-09-20 21:26:39 +08:00
case 1 :
{
wchar_t * el = ( wchar_t * ) al_get ( & l , 0 ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( wcschr ( el , L ' ' ) & & is_quotable ( el ) )
{
sb_append2 ( & buff ,
2005-11-03 00:49:13 +08:00
L " ' " ,
2005-09-20 21:26:39 +08:00
el ,
2005-11-03 00:49:13 +08:00
L " ' " ,
2005-10-01 02:28:26 +08:00
( void * ) 0 ) ;
2005-09-20 21:26:39 +08:00
}
else
{
2006-02-06 23:15:52 +08:00
wchar_t * val = escape ( el , 1 ) ;
2005-09-20 21:26:39 +08:00
sb_append ( & buff , val ) ;
free ( val ) ;
}
free ( el ) ;
break ;
}
default :
{
int j ;
for ( j = 0 ; j < al_get_count ( & l ) ; j + + )
{
wchar_t * el = ( wchar_t * ) al_get ( & l , j ) ;
if ( j )
sb_append ( & buff , L " " ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( is_quotable ( el ) )
{
sb_append2 ( & buff ,
2005-11-03 00:49:13 +08:00
L " ' " ,
2005-09-20 21:26:39 +08:00
el ,
2005-11-03 00:49:13 +08:00
L " ' " ,
2005-10-01 02:28:26 +08:00
( void * ) 0 ) ;
2005-09-20 21:26:39 +08:00
}
else
{
2006-02-06 23:15:52 +08:00
wchar_t * val = escape ( el , 1 ) ;
2005-09-20 21:26:39 +08:00
sb_append ( & buff , val ) ;
free ( val ) ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
free ( el ) ;
2006-01-31 03:53:10 +08:00
}
}
2005-09-20 21:26:39 +08:00
}
al_destroy ( & l ) ;
return ( wchar_t * ) buff . buff ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
/**
Tests if all characters in the string are numeric
*/
static int isnumeric ( const char * n )
{
if ( * n = = ' \0 ' )
return 1 ;
if ( * n < ' 0 ' | | * n > ' 9 ' )
return 0 ;
return isnumeric ( n + 1 ) ;
}
/**
Tests if all characters in the wide string are numeric
*/
static int iswnumeric ( const wchar_t * n )
{
if ( * n = = L ' \0 ' )
return 1 ;
if ( * n < L ' 0 ' | | * n > L ' 9 ' )
return 0 ;
return iswnumeric ( n + 1 ) ;
}
/**
See if the process described by \ c proc matches the commandline \ c
cmd
*/
2006-01-31 03:53:10 +08:00
static int match_pid ( const wchar_t * cmd ,
2005-09-20 21:26:39 +08:00
const wchar_t * proc ,
int flags )
{
/* Test for direct match */
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( wcsncmp ( cmd , proc , wcslen ( proc ) ) = = 0 )
return 1 ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( flags & ACCEPT_INCOMPLETE )
return 0 ;
2006-01-31 03:53:10 +08:00
/*
2006-02-07 19:48:57 +08: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 21:26:39 +08:00
*/
wchar_t * first_token = tok_first ( cmd ) ;
if ( first_token = = 0 )
return 0 ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
wchar_t * start = 0 ;
wchar_t prev = 0 ;
wchar_t * p ;
2006-01-31 03:53:10 +08:00
/*
2006-02-07 19:48:57 +08:00
This should be done by basename ( ) , if it wasn ' t for the fact
that is does not accept wide strings
2005-09-20 21:26:39 +08:00
*/
for ( p = first_token ; * p ; p + + )
{
if ( * p = = L ' / ' & & prev ! = L ' \\ ' )
start = p ;
prev = * p ;
}
if ( start )
{
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( wcsncmp ( start + 1 , proc , wcslen ( proc ) ) = = 0 )
{
free ( first_token ) ;
return 1 ;
}
}
free ( first_token ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
return 0 ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
/**
Searches for a job with the specified job id , or a job or process
which has the string \ c proc as a prefix of its commandline .
2005-11-30 03:50:30 +08:00
If accept_incomplete is true , the remaining string for any matches
are inserted .
2005-09-20 21:26:39 +08:00
If accept_incomplete is false , 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 .
*/
2006-01-31 03:53:10 +08:00
static int find_process ( const wchar_t * proc ,
int flags ,
2005-09-20 21:26:39 +08:00
array_list_t * out )
{
DIR * dir ;
struct dirent * next ;
char * pdir_name ;
char * pfile_name ;
wchar_t * cmd = 0 ;
int sz = 0 ;
int found = 0 ;
2006-01-31 03:53:10 +08:00
wchar_t * result ;
2005-09-20 21:26:39 +08:00
job_t * j ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( iswnumeric ( proc ) | | ( wcslen ( proc ) = = 0 ) )
{
/*
2006-01-31 03:53:10 +08:00
This is a numeric job string , like ' % 2 '
2005-09-20 21:26:39 +08:00
*/
if ( flags & ACCEPT_INCOMPLETE )
{
for ( j = first_job ; j ! = 0 ; j = j - > next )
{
wchar_t jid [ 16 ] ;
if ( j - > command = = 0 )
continue ;
swprintf ( jid , 16 , L " %d " , j - > job_id ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( wcsncmp ( proc , jid , wcslen ( proc ) ) = = 0 )
{
2006-01-31 03:53:10 +08:00
al_push ( out ,
wcsdupcat2 ( jid + wcslen ( proc ) ,
2006-01-17 20:48:30 +08:00
COMPLETE_SEP_STR ,
2006-01-31 03:53:10 +08:00
COMPLETE_JOB_DESC_VAL ,
j - > command ,
2006-01-17 20:48:30 +08:00
( void * ) 0 ) ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
else
{
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
int jid = wcstol ( proc , 0 , 10 ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
j = job_get ( jid ) ;
if ( ( j ! = 0 ) & & ( j - > command ! = 0 ) )
{
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
{
result = malloc ( sizeof ( wchar_t ) * 16 ) ;
swprintf ( result , 16 , L " %d " , j - > pgid ) ;
//fwprintf( stderr, L"pid %d %ls\n", j->pgid, result );
al_push ( out , result ) ;
found = 1 ;
}
2006-01-31 03:53:10 +08:00
}
2005-09-20 21:26:39 +08:00
}
}
if ( found )
return 1 ;
for ( j = first_job ; j ! = 0 ; j = j - > next )
{
// fwprintf( stderr, L"..." );
if ( j - > command = = 0 )
continue ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
// fwprintf( stderr, L"match '%ls' '%ls'\n\n\n", j->command, proc );
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( match_pid ( j - > command , proc , flags ) )
{
if ( flags & ACCEPT_INCOMPLETE )
{
2006-01-31 03:53:10 +08:00
wchar_t * res = wcsdupcat2 ( j - > command + wcslen ( proc ) ,
2006-01-17 20:48:30 +08:00
COMPLETE_SEP_STR ,
COMPLETE_JOB_DESC ,
( void * ) 0 ) ;
2005-09-20 21:26:39 +08:00
// fwprintf( stderr, L"Woot %ls\n", res );
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
al_push ( out , res ) ;
}
else
{
result = malloc ( sizeof ( wchar_t ) * 16 ) ;
swprintf ( result , 16 , L " %d " , j - > pgid ) ;
al_push ( out , result ) ;
found = 1 ;
}
}
}
if ( found )
{
return 1 ;
}
for ( j = first_job ; j ; j = j - > next )
{
process_t * p ;
if ( j - > command = = 0 )
continue ;
for ( p = j - > first_process ; p ; p = p - > next )
{
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
// fwprintf( stderr, L"..." );
if ( p - > actual_cmd = = 0 )
continue ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
// fwprintf( stderr, L"match '%ls' '%ls'\n\n\n", j->command, proc );
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( match_pid ( p - > actual_cmd , proc , flags ) )
{
if ( flags & ACCEPT_INCOMPLETE )
{
2006-01-31 03:53:10 +08:00
wchar_t * res = wcsdupcat2 ( p - > actual_cmd + wcslen ( proc ) ,
2006-02-07 19:48:57 +08:00
COMPLETE_SEP_STR ,
COMPLETE_CHILD_PROCESS_DESC ,
( void * ) 0 ) ;
2005-09-20 21:26:39 +08:00
al_push ( out , res ) ;
}
else
{
result = malloc ( sizeof ( wchar_t ) * 16 ) ;
swprintf ( result , 16 , L " %d " , p - > pid ) ;
al_push ( out , result ) ;
found = 1 ;
}
}
}
}
if ( found )
{
return 1 ;
}
if ( ! ( dir = opendir ( " /proc " ) ) )
{
/*
2006-01-31 03:53:10 +08:00
This system does not have a / proc filesystem .
2005-09-20 21:26:39 +08:00
*/
return 1 ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
pdir_name = malloc ( 256 ) ;
pfile_name = malloc ( 64 ) ;
strcpy ( pdir_name , " /proc/ " ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
while ( ( next = readdir ( dir ) ) ! = 0 )
{
char * name = next - > d_name ;
struct stat buf ;
if ( ! isnumeric ( name ) )
continue ;
strcpy ( pdir_name + 6 , name ) ;
if ( stat ( pdir_name , & buf ) )
{
continue ;
2006-01-31 03:53:10 +08:00
}
2005-09-20 21:26:39 +08:00
if ( buf . st_uid ! = getuid ( ) )
{
continue ;
}
strcpy ( pfile_name , pdir_name ) ;
strcat ( pfile_name , " /cmdline " ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( ! stat ( pfile_name , & buf ) )
{
/*
the ' cmdline ' file exists , it should contain the commandline
*/
FILE * cmdfile ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( ( cmdfile = fopen ( pfile_name , " r " ) ) = = 0 )
{
wperror ( L " fopen " ) ;
continue ;
}
2006-01-31 03:53:10 +08:00
2006-02-09 23:50:20 +08:00
signal_block ( ) ;
2006-01-31 03:53:10 +08:00
fgetws2 ( & cmd , & sz , cmdfile ) ;
2006-02-09 23:50:20 +08:00
signal_unblock ( ) ;
2005-09-20 21:26:39 +08:00
fclose ( cmdfile ) ;
}
else
{
# ifdef SunOS
strcpy ( pfile_name , pdir_name ) ;
strcat ( pfile_name , " /psinfo " ) ;
if ( ! stat ( pfile_name , & buf ) )
{
psinfo_t info ;
FILE * psfile ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( ( psfile = fopen ( pfile_name , " r " ) ) = = 0 )
{
wperror ( L " fopen " ) ;
continue ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08: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-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( cmd ! = 0 )
{
if ( match_pid ( cmd , proc , flags ) )
2006-01-31 03:53:10 +08:00
{
2005-09-20 21:26:39 +08:00
if ( flags & ACCEPT_INCOMPLETE )
{
2006-01-17 20:48:30 +08:00
wchar_t * res = wcsdupcat2 ( cmd + wcslen ( proc ) ,
2006-02-07 19:48:57 +08:00
COMPLETE_SEP_STR ,
2006-01-17 20:48:30 +08:00
COMPLETE_PROCESS_DESC ,
( void * ) 0 ) ;
2005-09-20 21:26:39 +08:00
if ( res )
al_push ( out , res ) ;
}
else
{
wchar_t * res = str2wcs ( name ) ;
if ( res )
2005-09-25 03:35:08 +08:00
al_push ( out , res ) ;
2005-09-20 21:26:39 +08:00
}
}
}
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( cmd ! = 0 )
free ( cmd ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
free ( pdir_name ) ;
free ( pfile_name ) ;
2006-01-31 03:53:10 +08:00
closedir ( dir ) ;
2005-09-20 21:26:39 +08:00
return 1 ;
}
/**
Process id expansion
*/
2006-01-31 03:53:10 +08:00
static int expand_pid ( wchar_t * in ,
2005-09-20 21:26:39 +08:00
int flags ,
array_list_t * out )
{
if ( * in ! = PROCESS_EXPAND )
{
al_push ( out , in ) ;
return 1 ;
}
if ( flags & ACCEPT_INCOMPLETE )
{
if ( wcsncmp ( in + 1 , SELF_STR , wcslen ( in + 1 ) ) = = 0 )
{
2006-01-17 20:48:30 +08:00
wchar_t * res = wcsdupcat2 ( SELF_STR + wcslen ( in + 1 ) , COMPLETE_SEP_STR , COMPLETE_SELF_DESC , ( void * ) 0 ) ;
2006-01-31 03:53:10 +08:00
al_push ( out , res ) ;
}
2005-09-20 21:26:39 +08:00
else if ( wcsncmp ( in + 1 , LAST_STR , wcslen ( in + 1 ) ) = = 0 )
{
2006-01-17 20:48:30 +08:00
wchar_t * res = wcsdupcat2 ( LAST_STR + wcslen ( in + 1 ) , COMPLETE_SEP_STR , COMPLETE_LAST_DESC , ( void * ) 0 ) ;
2006-01-31 03:53:10 +08:00
al_push ( out , res ) ;
}
2005-09-20 21:26:39 +08:00
}
else
{
if ( wcscmp ( ( in + 1 ) , SELF_STR ) = = 0 )
{
wchar_t * str = malloc ( sizeof ( wchar_t ) * 32 ) ;
free ( in ) ;
swprintf ( str , 32 , L " %d " , getpid ( ) ) ;
al_push ( out , str ) ;
2006-01-31 03:53:10 +08:00
return 1 ;
}
2005-09-20 21:26:39 +08:00
if ( wcscmp ( ( in + 1 ) , LAST_STR ) = = 0 )
{
wchar_t * str ;
if ( proc_last_bg_pid > 0 )
{
str = malloc ( sizeof ( wchar_t ) * 32 ) ;
free ( in ) ;
swprintf ( str , 32 , L " %d " , proc_last_bg_pid ) ;
al_push ( out , str ) ;
}
2006-01-31 03:53:10 +08:00
return 1 ;
}
}
2005-09-20 21:26:39 +08:00
// fwprintf( stderr, L"expand_pid() %ls\n", in );
int prev = al_get_count ( out ) ;
if ( ! find_process ( in + 1 , flags , out ) )
return 0 ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( prev = = al_get_count ( out ) )
{
// fwprintf( stderr, L"no match\n" );
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( flags & ACCEPT_INCOMPLETE )
free ( in ) ;
else
{
* in = L ' % ' ;
// fwprintf( stderr, L"return %ls\n", in );
al_push ( out , in ) ;
}
2006-01-31 03:53:10 +08:00
}
2005-09-20 21:26:39 +08:00
else
{
// fwprintf( stderr, L"match\n" );
free ( in ) ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
return 1 ;
}
2006-02-13 03:03:01 +08:00
/**
Expand all environment variables in the string * ptr .
This function is slow , fragile and complicated . There are lots of
little corner cases , like $ $ foo should do a double expansion ,
$ foo $ bar should not double expand bar , etc . Also , it ' s easy to
accidentally leak memory on array out of bounds errors an various
other situations . All in all , this function should be rewritten ,
split out into multiple logical units and carefully tested . After
that , it can probably be optimized to do fewer memory allocations ,
fewer string scans and overall just less work . But until that
happens , don ' t edit it unless you know exactly what you are doing ,
and do proper testing afterwards .
*/
static int expand_variables ( wchar_t * in , array_list_t * out , int last_idx )
2006-02-12 19:29:30 +08:00
{
2006-02-13 03:03:01 +08:00
wchar_t c ;
wchar_t prev_char = 0 ;
int i , j ;
int is_ok = 1 ;
int empty = 0 ;
static string_buffer_t * var_tmp = 0 ;
static array_list_t * var_idx_list = 0 ;
2006-02-12 19:29:30 +08:00
if ( ! var_tmp )
{
var_tmp = sb_halloc ( global_context ) ;
if ( ! var_tmp )
die_mem ( ) ;
}
else
{
sb_clear ( var_tmp ) ;
}
if ( ! var_idx_list )
{
var_idx_list = al_halloc ( global_context ) ;
if ( ! var_idx_list )
die_mem ( ) ;
}
else
{
al_truncate ( var_idx_list , 0 ) ;
}
2006-01-31 03:53:10 +08:00
2006-02-12 19:29:30 +08:00
for ( i = last_idx ; ( i > = 0 ) & & is_ok & & ! empty ; i - - )
2005-09-20 21:26:39 +08:00
{
c = in [ i ] ;
2005-11-03 00:49:13 +08:00
if ( ( c = = VARIABLE_EXPAND ) | | ( c = = VARIABLE_EXPAND_SINGLE ) )
2005-09-20 21:26:39 +08:00
{
int start_pos = i + 1 ;
int stop_pos ;
int var_len , new_len ;
wchar_t * var_val ;
2006-01-31 03:53:10 +08:00
wchar_t * new_in ;
2005-11-03 00:49:13 +08:00
int is_single = ( c = = VARIABLE_EXPAND_SINGLE ) ;
2006-02-12 19:29:30 +08:00
int var_name_stop_pos ;
2005-09-20 21:26:39 +08:00
stop_pos = start_pos ;
while ( 1 )
{
if ( ! ( in [ stop_pos ] ) )
break ;
if ( ! ( iswalnum ( in [ stop_pos ] ) | |
( wcschr ( L " _ " , in [ stop_pos ] ) ! = 0 ) ) )
break ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
stop_pos + + ;
}
2006-02-12 19:29:30 +08:00
var_name_stop_pos = stop_pos ;
2005-09-20 21:26:39 +08:00
/* printf( "Stop for '%c'\n", in[stop_pos]);*/
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
var_len = stop_pos - start_pos ;
2005-12-07 21:03:30 +08:00
if ( var_len = = 0 )
{
2006-01-17 20:48:30 +08:00
switch ( in [ stop_pos ] )
2005-12-07 21:03:30 +08:00
{
2006-01-17 20:48:30 +08:00
case BRACKET_BEGIN :
{
error ( SYNTAX_ERROR ,
2006-01-31 03:53:10 +08:00
- 1 ,
2006-01-17 20:48:30 +08:00
COMPLETE_VAR_BRACKET_DESC ) ;
break ;
}
2006-01-31 03:53:10 +08:00
2006-01-17 20:48:30 +08:00
case INTERNAL_SEPARATOR :
{
error ( SYNTAX_ERROR ,
2006-01-31 03:53:10 +08:00
- 1 ,
2006-01-17 20:48:30 +08:00
COMPLETE_VAR_PARAN_DESC ) ;
break ;
}
2006-01-31 03:53:10 +08:00
2006-01-17 20:48:30 +08:00
case 0 :
{
error ( SYNTAX_ERROR ,
- 1 ,
COMPLETE_VAR_NULL_DESC ,
in [ stop_pos ] ) ;
break ;
}
2006-01-31 03:53:10 +08:00
2006-01-17 20:48:30 +08:00
default :
{
error ( SYNTAX_ERROR ,
- 1 ,
COMPLETE_VAR_DESC ,
in [ stop_pos ] ) ;
2006-01-31 03:53:10 +08:00
break ;
2006-01-17 20:48:30 +08:00
}
2005-12-07 21:03:30 +08:00
}
2006-01-31 03:53:10 +08:00
2005-12-07 21:03:30 +08:00
is_ok = 0 ;
break ;
}
2006-01-31 03:53:10 +08:00
2006-02-12 19:29:30 +08:00
sb_append_substring ( var_tmp , & in [ start_pos ] , var_len ) ;
2006-01-31 03:53:10 +08:00
2006-02-12 19:29:30 +08:00
var_val = expand_var ( ( wchar_t * ) var_tmp - > buff ) ;
2006-01-31 03:53:10 +08:00
2006-02-12 19:29:30 +08:00
if ( var_val )
2005-11-03 00:49:13 +08:00
{
int all_vars = 1 ;
2006-02-12 19:29:30 +08:00
array_list_t var_item_list ;
al_init ( & var_item_list ) ;
2005-11-03 00:49:13 +08:00
if ( in [ stop_pos ] = = L ' [ ' )
2006-01-31 03:53:10 +08:00
{
2005-11-03 00:49:13 +08:00
wchar_t * end ;
2006-01-31 03:53:10 +08:00
2005-11-03 00:49:13 +08:00
all_vars = 0 ;
2006-01-31 03:53:10 +08:00
2005-11-03 00:49:13 +08:00
stop_pos + + ;
while ( 1 )
{
int tmp ;
2006-01-31 03:53:10 +08:00
2005-11-03 00:49:13 +08:00
while ( iswspace ( in [ stop_pos ] ) | | ( in [ stop_pos ] = = INTERNAL_SEPARATOR ) )
stop_pos + + ;
2006-01-31 03:53:10 +08:00
2005-11-03 00:49:13 +08:00
if ( in [ stop_pos ] = = L ' ] ' )
{
stop_pos + + ;
break ;
}
2005-09-20 21:26:39 +08:00
2005-11-03 00:49:13 +08:00
errno = 0 ;
tmp = wcstol ( & in [ stop_pos ] , & end , 10 ) ;
if ( ( errno ) | | ( end = = & in [ stop_pos ] ) )
{
2006-01-31 03:53:10 +08:00
error ( SYNTAX_ERROR ,
- 1 ,
2005-12-08 00:06:47 +08:00
L " Expected integer or \' ] \' " ) ;
2006-01-31 03:53:10 +08:00
2005-11-03 00:49:13 +08:00
is_ok = 0 ;
break ;
2005-09-20 21:26:39 +08:00
}
2006-02-12 19:29:30 +08:00
al_push ( var_idx_list , ( void * ) tmp ) ;
2005-11-03 00:49:13 +08:00
stop_pos = end - in ;
2005-09-20 21:26:39 +08:00
}
2005-11-03 00:49:13 +08:00
}
2006-01-31 03:53:10 +08:00
2005-11-03 00:49:13 +08:00
if ( is_ok )
2006-01-31 03:53:10 +08:00
{
2006-02-12 19:29:30 +08:00
expand_variable_array ( var_val , & var_item_list ) ;
2005-11-03 00:49:13 +08:00
if ( ! all_vars )
2006-01-31 03:53:10 +08:00
{
2005-11-03 00:49:13 +08:00
int j ;
2006-02-12 19:29:30 +08:00
for ( j = 0 ; j < al_get_count ( var_idx_list ) ; j + + )
2005-11-03 00:49:13 +08:00
{
2006-02-12 19:29:30 +08:00
int tmp = ( int ) al_get ( var_idx_list , j ) ;
if ( tmp < 1 | | tmp > al_get_count ( & var_item_list ) )
2005-09-20 21:26:39 +08:00
{
2006-01-31 03:53:10 +08:00
error ( SYNTAX_ERROR ,
2005-12-08 00:06:47 +08:00
- 1 ,
L " Array index out of bounds " ) ;
2005-11-03 00:49:13 +08:00
is_ok = 0 ;
2006-02-12 19:29:30 +08:00
al_truncate ( var_idx_list , j ) ;
2005-11-03 00:49:13 +08:00
break ;
2006-01-31 03:53:10 +08:00
}
2005-11-03 00:49:13 +08:00
else
{
/* Move string from list l to list idx */
2006-02-12 19:29:30 +08:00
al_set ( var_idx_list , j , al_get ( & var_item_list , tmp - 1 ) ) ;
al_set ( & var_item_list , tmp - 1 , 0 ) ;
2005-09-20 21:26:39 +08:00
}
}
2005-11-03 00:49:13 +08:00
/* Free remaining strings in list l and truncate it */
2006-02-12 19:29:30 +08:00
al_foreach ( & var_item_list , ( void ( * ) ( const void * ) ) & free ) ;
al_truncate ( & var_item_list , 0 ) ;
2006-01-31 03:53:10 +08:00
/* Add items from list idx back to list l */
2006-02-12 19:29:30 +08:00
al_push_all ( & var_item_list , var_idx_list ) ;
2005-11-03 00:49:13 +08:00
}
2006-01-31 03:53:10 +08:00
}
2006-02-07 19:48:57 +08:00
if ( is_ok )
2005-11-03 00:49:13 +08:00
{
2006-02-07 19:48:57 +08:00
if ( is_single )
{
string_buffer_t res ;
in [ i ] = 0 ;
2006-02-09 23:50:20 +08:00
2006-02-12 19:29:30 +08:00
sb_init ( & res ) ;
2006-02-07 19:48:57 +08:00
sb_append ( & res , in ) ;
sb_append_char ( & res , INTERNAL_SEPARATOR ) ;
2006-01-31 03:53:10 +08:00
2006-02-12 19:29:30 +08:00
for ( j = 0 ; j < al_get_count ( & var_item_list ) ; j + + )
2006-01-31 03:53:10 +08:00
{
2006-02-12 19:29:30 +08:00
wchar_t * next = ( wchar_t * ) al_get ( & var_item_list , j ) ;
2006-02-07 19:48:57 +08:00
if ( is_ok )
{
if ( j ! = 0 )
sb_append ( & res , L " " ) ;
sb_append ( & res , next ) ;
}
free ( next ) ;
2005-11-03 00:49:13 +08:00
}
2006-02-07 19:48:57 +08:00
sb_append ( & res , & in [ stop_pos ] ) ;
2006-02-12 19:29:30 +08:00
is_ok & = expand_variables ( ( wchar_t * ) res . buff , out , i ) ;
2006-01-31 03:53:10 +08:00
}
2006-02-07 19:48:57 +08:00
else
2005-09-20 21:26:39 +08:00
{
2006-02-12 19:29:30 +08:00
for ( j = 0 ; j < al_get_count ( & var_item_list ) ; j + + )
2005-09-20 21:26:39 +08:00
{
2006-02-12 19:29:30 +08:00
wchar_t * next = ( wchar_t * ) al_get ( & var_item_list , j ) ;
if ( is_ok & & ( i = = 0 ) & & ( ! in [ stop_pos ] ) )
2005-09-20 21:26:39 +08:00
{
2006-02-12 19:29:30 +08:00
al_push ( out , next ) ;
}
else
{
if ( is_ok )
2006-02-07 19:48:57 +08:00
{
2006-02-12 19:29:30 +08:00
new_len = wcslen ( in ) - ( stop_pos - start_pos + 1 ) ;
new_len + = wcslen ( next ) + 2 ;
if ( ! ( new_in = malloc ( sizeof ( wchar_t ) * new_len ) ) )
2006-02-07 19:48:57 +08:00
{
2006-02-12 19:29:30 +08:00
die_mem ( ) ;
2006-02-07 19:48:57 +08:00
}
else
2006-02-12 19:29:30 +08:00
{
wcsncpy ( new_in , in , start_pos - 1 ) ;
if ( start_pos > 1 & & new_in [ start_pos - 2 ] ! = VARIABLE_EXPAND )
{
new_in [ start_pos - 1 ] = INTERNAL_SEPARATOR ;
new_in [ start_pos ] = L ' \0 ' ;
}
else
new_in [ start_pos - 1 ] = L ' \0 ' ;
wcscat ( new_in , next ) ;
wcscat ( new_in , & in [ stop_pos ] ) ;
2005-09-20 21:26:39 +08:00
// fwprintf( stderr, L"New value %ls\n", new_in );
2006-02-12 19:29:30 +08:00
is_ok & = expand_variables ( new_in , out , i ) ;
}
2006-02-07 19:48:57 +08:00
}
2006-02-12 19:29:30 +08:00
free ( next ) ;
2005-09-20 21:26:39 +08:00
}
2006-02-12 19:29:30 +08:00
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
}
2005-09-20 21:26:39 +08:00
}
2006-02-07 19:48:57 +08:00
2005-11-03 00:49:13 +08:00
free ( in ) ;
2006-02-12 19:29:30 +08:00
al_destroy ( & var_item_list ) ;
2006-01-31 03:53:10 +08:00
return is_ok ;
2005-11-03 00:49:13 +08:00
}
else
{
/*
Expand a non - existing variable
*/
if ( c = = VARIABLE_EXPAND )
2005-09-20 21:26:39 +08:00
{
2005-11-03 00:49:13 +08:00
/*
2005-12-07 23:57:17 +08:00
Regular expansion , i . e . expand this argument to nothing
2005-11-03 00:49:13 +08:00
*/
2005-09-20 21:26:39 +08:00
empty = 1 ;
}
2005-11-03 00:49:13 +08:00
else
{
/*
2006-01-31 03:53:10 +08:00
Expansion to single argument .
2005-11-03 00:49:13 +08:00
*/
string_buffer_t res ;
sb_init ( & res ) ;
2006-01-31 03:53:10 +08:00
2005-11-03 00:49:13 +08:00
in [ i ] = 0 ;
2006-01-31 03:53:10 +08:00
2005-11-03 00:49:13 +08:00
sb_append ( & res , in ) ;
sb_append ( & res , & in [ stop_pos ] ) ;
2006-02-12 19:29:30 +08:00
is_ok & = expand_variables ( ( wchar_t * ) res . buff , out , i ) ;
2005-11-03 00:49:13 +08:00
free ( in ) ;
return is_ok ;
}
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
prev_char = c ;
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( ! empty )
{
al_push ( out , in ) ;
}
else
{
free ( in ) ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
return is_ok ;
}
/**
Perform bracket expansion
*/
static int expand_brackets ( wchar_t * in , int flags , array_list_t * out )
{
wchar_t * pos ;
int syntax_error = 0 ;
2006-01-31 03:53:10 +08:00
int bracket_count = 0 ;
2005-09-20 21:26:39 +08:00
wchar_t * bracket_begin = 0 , * bracket_end = 0 ;
wchar_t * last_sep = 0 ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
wchar_t * item_begin ;
int len1 , len2 , tot_len ;
// fwprintf( stderr, L"expand %ls\n", in );
2006-01-31 03:53:10 +08:00
for ( pos = in ;
( ! bracket_end ) & & ( * pos ) & & ! syntax_error ;
2005-09-20 21:26:39 +08:00
pos + + )
{
switch ( * pos )
{
case BRACKET_BEGIN :
{
if ( ( bracket_count = = 0 ) & & ( bracket_begin = = 0 ) )
bracket_begin = pos ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
bracket_count + + ;
break ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
case BRACKET_END :
{
bracket_count - - ;
if ( ( bracket_count = = 0 ) & & ( bracket_end = = 0 ) )
bracket_end = pos ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( bracket_count < 0 )
{
syntax_error = 1 ;
}
break ;
}
case BRACKET_SEP :
{
if ( bracket_count = = 1 )
last_sep = pos ;
}
}
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( bracket_count > 0 )
{
if ( ! ( flags & ACCEPT_INCOMPLETE ) )
syntax_error = 1 ;
else
{
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
string_buffer_t mod ;
sb_init ( & mod ) ;
if ( last_sep )
{
sb_append_substring ( & mod , in , bracket_begin - in + 1 ) ;
sb_append ( & mod , last_sep + 1 ) ;
sb_append_char ( & mod , BRACKET_END ) ;
}
else
{
sb_append ( & mod , in ) ;
sb_append_char ( & mod , BRACKET_END ) ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
return expand_brackets ( ( wchar_t * ) mod . buff , 1 , out ) ;
}
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( syntax_error )
{
2006-01-31 03:53:10 +08:00
error ( SYNTAX_ERROR ,
2005-12-08 00:06:47 +08:00
- 1 ,
L " Mismatched brackets " ) ;
2005-09-20 21:26:39 +08:00
return 0 ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( bracket_begin = = 0 )
{
al_push ( out , in ) ;
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-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
whole_item = malloc ( sizeof ( wchar_t ) * ( tot_len + item_len + 1 ) ) ;
wcsncpy ( whole_item , in , len1 ) ;
2006-01-31 03:53:10 +08:00
wcsncpy ( whole_item + len1 , item_begin , item_len ) ;
2005-09-20 21:26:39 +08:00
wcscpy ( whole_item + len1 + item_len , bracket_end + 1 ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
expand_brackets ( whole_item , flags , out ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
item_begin = pos + 1 ;
if ( pos = = bracket_end )
break ;
2006-01-31 03:53:10 +08:00
}
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( * pos = = BRACKET_BEGIN )
{
bracket_count + + ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( * pos = = BRACKET_END )
{
bracket_count - - ;
}
}
2006-01-31 03:53:10 +08:00
free ( in ) ;
return 1 ;
2005-09-20 21:26:39 +08:00
}
/**
Perform subshell expansion
*/
static int expand_subshell ( wchar_t * in , array_list_t * out )
{
2006-01-31 00:51:50 +08:00
const wchar_t * paran_begin = 0 , * paran_end = 0 ;
2005-09-20 21:26:39 +08:00
int len1 , len2 ;
wchar_t prev = 0 ;
wchar_t * subcmd ;
array_list_t sub_res , tail_expand ;
int i , j ;
2006-01-31 00:51:50 +08:00
const wchar_t * item_begin ;
2005-09-20 21:26:39 +08:00
2006-01-31 03:53:10 +08:00
switch ( parse_util_locate_cmdsubst ( in ,
2006-01-31 00:51:50 +08:00
& paran_begin ,
& paran_end ,
0 ) )
2005-09-20 21:26:39 +08:00
{
case - 1 :
2006-01-31 03:53:10 +08:00
error ( SYNTAX_ERROR ,
2005-12-08 00:06:47 +08:00
- 1 ,
L " Mismatched parans " ) ;
2005-09-20 21:26:39 +08:00
return 0 ;
case 0 :
al_push ( out , in ) ;
2006-01-31 03:53:10 +08:00
return 1 ;
2005-09-20 21:26:39 +08:00
case 1 :
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
break ;
2006-01-31 03:53:10 +08:00
}
2005-09-20 21:26:39 +08:00
len1 = ( paran_begin - in ) ;
len2 = wcslen ( paran_end ) - 1 ;
prev = 0 ;
item_begin = paran_begin + 1 ;
al_init ( & sub_res ) ;
if ( ! ( subcmd = malloc ( sizeof ( wchar_t ) * ( paran_end - paran_begin ) ) ) )
{
al_destroy ( & sub_res ) ;
2006-01-31 03:53:10 +08:00
return 0 ;
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
wcsncpy ( subcmd , paran_begin + 1 , paran_end - paran_begin - 1 ) ;
2006-01-31 03:53:10 +08:00
subcmd [ paran_end - paran_begin - 1 ] = 0 ;
2005-09-20 21:26:39 +08:00
if ( exec_subshell ( subcmd , & sub_res ) = = - 1 )
{
al_foreach ( & sub_res , ( void ( * ) ( const void * ) ) & free ) ;
al_destroy ( & sub_res ) ;
free ( subcmd ) ;
return 0 ;
2006-01-31 03:53:10 +08:00
}
2005-09-20 21:26:39 +08:00
al_init ( & tail_expand ) ;
expand_subshell ( wcsdup ( paran_end + 1 ) , & tail_expand ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
for ( i = 0 ; i < al_get_count ( & sub_res ) ; i + + )
{
2005-10-07 18:36:51 +08:00
wchar_t * sub_item , * sub_item2 ;
2005-09-20 21:26:39 +08:00
sub_item = ( wchar_t * ) al_get ( & sub_res , i ) ;
2006-02-06 23:15:52 +08:00
sub_item2 = escape ( sub_item , 1 ) ;
2006-01-31 03:53:10 +08:00
free ( sub_item ) ;
2005-10-07 18:36:51 +08:00
int item_len = wcslen ( sub_item2 ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
for ( j = 0 ; j < al_get_count ( & tail_expand ) ; j + + )
{
string_buffer_t whole_item ;
wchar_t * tail_item = ( wchar_t * ) al_get ( & tail_expand , j ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
sb_init ( & whole_item ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
sb_append_substring ( & whole_item , in , len1 ) ;
sb_append_char ( & whole_item , INTERNAL_SEPARATOR ) ;
2005-10-07 18:36:51 +08:00
sb_append_substring ( & whole_item , sub_item2 , item_len ) ;
2005-09-20 21:26:39 +08:00
sb_append_char ( & whole_item , INTERNAL_SEPARATOR ) ;
2006-01-31 03:53:10 +08:00
sb_append ( & whole_item , tail_item ) ;
al_push ( out , whole_item . buff ) ;
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-10-07 18:36:51 +08:00
free ( sub_item2 ) ;
2006-01-31 03:53:10 +08:00
}
2005-09-20 21:26:39 +08:00
free ( in ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
al_destroy ( & sub_res ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
al_foreach ( & tail_expand , ( void ( * ) ( const void * ) ) & free ) ;
al_destroy ( & tail_expand ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
free ( subcmd ) ;
2006-01-31 03:53:10 +08:00
return 1 ;
2005-09-20 21:26:39 +08:00
}
2006-02-06 23:15:52 +08:00
static wchar_t * expand_unescape ( const wchar_t * in , int escape_special )
2005-09-20 21:26:39 +08:00
{
2005-10-07 18:36:51 +08:00
wchar_t * res = unescape ( in , escape_special ) ;
if ( ! res )
2005-12-08 00:06:47 +08:00
error ( SYNTAX_ERROR , - 1 , L " Unexpected end of string " ) ;
2005-10-07 18:36:51 +08:00
return res ;
2005-09-20 21:26:39 +08:00
}
/**
Attempts tilde expansion . Of the string specified . If tilde
2005-11-30 00:52:02 +08:00
expansion is performed , the original string is freed and a new
string allocated using malloc is returned , otherwise , the original
string is returned .
2005-09-20 21:26:39 +08:00
*/
2005-11-30 00:52:02 +08:00
static wchar_t * expand_tilde_internal ( wchar_t * in )
2005-09-20 21:26:39 +08:00
{
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( in [ 0 ] = = HOME_DIRECTORY )
{
int tilde_error = 0 ;
wchar_t * home = 0 ;
2005-11-30 00:52:02 +08:00
wchar_t * new_in = 0 ;
wchar_t * old_in = 0 ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
// fwprintf( stderr, L"Tilde expand ~%ls\n", (*ptr)+1 );
if ( in [ 1 ] = = ' / ' | | in [ 1 ] = = ' \0 ' )
{
/* Current users home directory */
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
home = env_get ( L " HOME " ) ;
if ( home )
home = wcsdup ( home ) ;
else
home = wcsdup ( L " " ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( ! home )
{
* in = L ' ~ ' ;
tilde_error = 1 ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
old_in = & in [ 1 ] ;
}
else
{
/* Some other users home directory */
wchar_t * name ;
wchar_t * name_end = wcschr ( in , L ' / ' ) ;
if ( name_end = = 0 )
2006-01-31 03:53:10 +08:00
{
2005-09-20 21:26:39 +08:00
name_end = in + wcslen ( in ) ;
}
name = wcsndup ( in + 1 , name_end - in - 1 ) ;
old_in = name_end ;
2006-01-31 03:53:10 +08:00
char * name_str = wcs2str ( name ) ;
struct passwd * userinfo =
2005-09-20 21:26:39 +08:00
getpwnam ( name_str ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( userinfo = = 0 )
{
tilde_error = 1 ;
* in = L ' ~ ' ;
}
else
{
2006-01-31 03:53:10 +08:00
home = str2wcs ( userinfo - > pw_dir ) ;
2005-09-20 21:26:39 +08:00
if ( ! home )
{
* in = L ' ~ ' ;
tilde_error = 1 ;
}
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
free ( name_str ) ;
free ( name ) ;
}
2006-01-31 03:53:10 +08:00
2005-11-30 00:52:02 +08:00
if ( ! tilde_error & & home & & old_in )
2005-09-20 21:26:39 +08:00
{
2006-01-31 03:53:10 +08:00
new_in = wcsdupcat ( home , old_in ) ;
2005-11-30 00:52:02 +08:00
}
free ( home ) ;
free ( in ) ;
2006-01-31 03:53:10 +08:00
return new_in ;
2005-09-20 21:26:39 +08:00
}
2005-11-30 00:52:02 +08:00
return in ;
2006-01-31 03:53:10 +08:00
}
2005-09-20 21:26:39 +08:00
2005-11-30 00:52:02 +08:00
wchar_t * expand_tilde ( wchar_t * in )
2005-09-20 21:26:39 +08:00
{
if ( in [ 0 ] = = L ' ~ ' )
{
in [ 0 ] = HOME_DIRECTORY ;
2005-11-30 00:52:02 +08:00
return expand_tilde_internal ( in ) ;
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
return in ;
2005-09-20 21:26:39 +08:00
}
/**
Remove any internal separators . Also optionally convert wildcard characters to
regular equivalents . This is done to support EXPAN_SKIP_WILDCARDS .
*/
static void remove_internal_separator ( const void * s , int conv )
{
wchar_t * in = ( wchar_t * ) s ;
wchar_t * out = in ;
while ( * in )
{
switch ( * in )
{
case INTERNAL_SEPARATOR :
in + + ;
break ;
2006-02-12 19:29:30 +08:00
2005-09-20 21:26:39 +08:00
case ANY_CHAR :
in + + ;
* out + + = conv ? L ' ? ' : ANY_CHAR ;
break ;
case ANY_STRING :
in + + ;
* out + + = conv ? L ' * ' : ANY_STRING ;
break ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
default :
* out + + = * in + + ;
}
}
* out = 0 ;
}
/**
2005-12-07 23:57:17 +08:00
The real expansion function . expand_one is just a wrapper around this one .
2005-09-20 21:26:39 +08:00
*/
2006-02-09 23:50:20 +08:00
int expand_string ( void * context ,
wchar_t * str ,
2006-01-31 03:53:10 +08:00
array_list_t * end_out ,
2005-09-20 21:26:39 +08:00
int flags )
{
array_list_t list1 , list2 ;
array_list_t * in , * out ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
int i ;
int subshell_ok = 1 ;
2005-12-04 00:43:56 +08:00
int res = EXPAND_OK ;
2006-02-09 23:50:20 +08:00
int start_count = al_get_count ( end_out ) ;
2006-01-31 03:53:10 +08:00
2005-12-04 09:54:02 +08:00
// debug( 1, L"Expand %ls", str );
2006-01-31 03:53:10 +08:00
2005-10-26 18:51:02 +08:00
if ( ( ! ( flags & ACCEPT_INCOMPLETE ) ) & & is_clean ( str ) )
{
2006-02-09 23:50:20 +08:00
halloc_register ( context , str ) ;
2005-10-26 18:51:02 +08:00
al_push ( end_out , str ) ;
2005-12-04 00:43:56 +08:00
return EXPAND_OK ;
2005-10-26 18:51:02 +08:00
}
2005-09-20 21:26:39 +08:00
al_init ( & list1 ) ;
al_init ( & list2 ) ;
if ( EXPAND_SKIP_SUBSHELL & flags )
{
wchar_t * pos = str ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
while ( 1 )
{
pos = wcschr ( pos , L ' ( ' ) ;
if ( pos = = 0 )
break ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( ( pos = = str ) | | ( * ( pos - 1 ) ! = L ' \\ ' ) )
{
2005-12-08 00:06:47 +08:00
error ( SUBSHELL_ERROR , - 1 , L " Subshells not allowed " ) ;
2005-10-25 19:03:52 +08:00
free ( str ) ;
2005-09-20 21:26:39 +08:00
al_destroy ( & list1 ) ;
al_destroy ( & list2 ) ;
2005-12-04 00:43:56 +08:00
return EXPAND_ERROR ;
2005-09-20 21:26:39 +08:00
}
pos + + ;
2006-01-31 03:53:10 +08:00
}
2005-09-20 21:26:39 +08:00
al_push ( & list1 , str ) ;
}
else
{
subshell_ok = expand_subshell ( str , & list1 ) ;
}
if ( ! subshell_ok )
{
al_destroy ( & list1 ) ;
2005-12-04 00:43:56 +08:00
return EXPAND_ERROR ;
2005-09-20 21:26:39 +08:00
}
else
{
in = & list1 ;
out = & list2 ;
for ( i = 0 ; i < al_get_count ( in ) ; i + + )
{
2005-10-07 18:36:51 +08:00
wchar_t * next ;
2006-01-31 03:53:10 +08:00
next = expand_unescape ( ( wchar_t * ) al_get ( in , i ) ,
2005-10-07 18:36:51 +08:00
1 ) ;
free ( ( void * ) al_get ( in , i ) ) ;
2006-01-31 03:53:10 +08:00
2005-10-07 18:36:51 +08:00
if ( ! next )
2006-01-31 03:53:10 +08:00
continue ;
2005-09-20 21:26:39 +08:00
if ( EXPAND_SKIP_VARIABLES & flags )
{
wchar_t * tmp ;
for ( tmp = next ; * tmp ; tmp + + )
if ( * tmp = = VARIABLE_EXPAND )
* tmp = L ' $ ' ;
al_push ( out , next ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
else
{
2006-02-12 19:29:30 +08:00
if ( ! expand_variables ( next , out , wcslen ( next ) - 1 ) )
2005-09-20 21:26:39 +08:00
{
al_destroy ( in ) ;
al_destroy ( out ) ;
2006-01-31 03:53:10 +08:00
return EXPAND_ERROR ;
2005-09-20 21:26:39 +08:00
}
}
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
al_truncate ( in , 0 ) ;
in = & list2 ;
out = & list1 ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
for ( i = 0 ; i < al_get_count ( in ) ; i + + )
{
wchar_t * next = ( wchar_t * ) al_get ( in , i ) ;
if ( ! expand_brackets ( next , flags , out ) )
{
al_destroy ( in ) ;
al_destroy ( out ) ;
2005-12-04 00:43:56 +08:00
return EXPAND_ERROR ;
2005-09-20 21:26:39 +08:00
}
}
al_truncate ( in , 0 ) ;
in = & list1 ;
out = & list2 ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
for ( i = 0 ; i < al_get_count ( in ) ; i + + )
{
2006-01-31 03:53:10 +08:00
wchar_t * next = ( wchar_t * ) al_get ( in , i ) ;
2005-11-30 00:52:02 +08:00
if ( ! ( next = expand_tilde_internal ( next ) ) )
2005-09-20 21:26:39 +08:00
{
al_destroy ( in ) ;
al_destroy ( out ) ;
2005-12-04 00:43:56 +08:00
return EXPAND_ERROR ;
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( flags & ACCEPT_INCOMPLETE )
{
if ( * next = = PROCESS_EXPAND )
{
2005-12-04 00:43:56 +08:00
/*
2005-12-07 23:57:17 +08:00
If process expansion matches , we are not
2005-12-04 00:43:56 +08:00
interested in other completions , so we
short - circut and return
*/
2005-09-20 21:26:39 +08:00
expand_pid ( next , flags , end_out ) ;
al_destroy ( in ) ;
al_destroy ( out ) ;
2005-12-04 00:43:56 +08:00
return EXPAND_OK ;
2005-09-20 21:26:39 +08:00
}
else
al_push ( out , next ) ;
}
else
{
if ( ! expand_pid ( next , flags , out ) )
{
al_destroy ( in ) ;
al_destroy ( out ) ;
2005-12-04 00:43:56 +08:00
return EXPAND_ERROR ;
2005-09-20 21:26:39 +08:00
}
}
}
al_truncate ( in , 0 ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
in = & list2 ;
2006-01-31 03:53:10 +08:00
out = & list1 ;
2005-09-20 21:26:39 +08:00
for ( i = 0 ; i < al_get_count ( in ) ; i + + )
{
wchar_t * next = ( wchar_t * ) al_get ( in , i ) ;
int wc_res ;
2006-01-31 03:53:10 +08:00
remove_internal_separator ( next , EXPAND_SKIP_WILDCARDS & flags ) ;
2006-02-12 19:29:30 +08:00
2006-01-31 03:53:10 +08:00
if ( ( ( flags & ACCEPT_INCOMPLETE ) & & ( ! ( flags & EXPAND_SKIP_WILDCARDS ) ) ) | |
2005-09-20 21:26:39 +08:00
wildcard_has ( next , 1 ) )
{
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( next [ 0 ] = = ' / ' )
{
wc_res = wildcard_expand ( & next [ 1 ] , L " / " , flags , out ) ;
}
else
{
wc_res = wildcard_expand ( next , L " " , flags , out ) ;
}
free ( next ) ;
switch ( wc_res )
{
case 0 :
if ( ! ( flags & ACCEPT_INCOMPLETE ) )
2006-01-31 03:53:10 +08:00
{
2005-12-04 00:43:56 +08:00
if ( res = = EXPAND_OK )
res = EXPAND_WILDCARD_NO_MATCH ;
2005-12-04 09:54:02 +08:00
break ;
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
case 1 :
2005-12-04 00:43:56 +08:00
res = EXPAND_WILDCARD_MATCH ;
2005-09-20 21:26:39 +08:00
sort_list ( out ) ;
al_push_all ( end_out , out ) ;
2006-01-31 03:53:10 +08:00
al_truncate ( out , 0 ) ;
2005-09-20 21:26:39 +08:00
break ;
2006-01-31 03:53:10 +08:00
}
2005-09-20 21:26:39 +08:00
}
else
{
if ( flags & ACCEPT_INCOMPLETE )
free ( next ) ;
else
al_push ( end_out , next ) ;
2006-01-31 03:53:10 +08:00
}
2005-09-20 21:26:39 +08:00
}
al_destroy ( in ) ;
al_destroy ( out ) ;
}
2006-02-09 23:50:20 +08:00
if ( context )
{
for ( i = start_count ; i < al_get_count ( end_out ) ; i + + )
{
2006-02-10 08:35:39 +08:00
halloc_register ( context , ( void * ) al_get ( end_out , i ) ) ;
2006-02-09 23:50:20 +08:00
}
}
2005-12-04 00:43:56 +08:00
return res ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
2006-02-09 23:50:20 +08:00
wchar_t * expand_one ( void * context , wchar_t * string , int flags )
2005-09-20 21:26:39 +08:00
{
array_list_t l ;
int res ;
wchar_t * one ;
2005-10-26 18:51:02 +08:00
if ( ( ! ( flags & ACCEPT_INCOMPLETE ) ) & & is_clean ( string ) )
2006-02-09 23:50:20 +08:00
{
halloc_register ( context , string ) ;
2006-01-31 03:53:10 +08:00
return string ;
2006-02-09 23:50:20 +08:00
}
2005-09-20 21:26:39 +08:00
al_init ( & l ) ;
2006-02-09 23:50:20 +08:00
res = expand_string ( 0 , string , & l , flags ) ;
2005-09-20 21:26:39 +08:00
if ( ! res )
{
one = 0 ;
}
else
{
if ( al_get_count ( & l ) ! = 1 )
{
one = 0 ;
}
else
{
one = ( wchar_t * ) al_get ( & l , 0 ) ;
al_set ( & l , 0 , 0 ) ;
}
}
2005-10-26 18:51:02 +08:00
2005-09-20 21:26:39 +08:00
al_foreach ( & l , ( void ( * ) ( const void * ) ) & free ) ;
al_destroy ( & l ) ;
2006-02-09 23:50:20 +08:00
2006-02-10 08:35:39 +08:00
halloc_register ( context , one ) ;
2005-09-20 21:26:39 +08:00
return one ;
}