2005-09-20 13:26:39 +00:00
/**\file expand.c
String expansion functions . These functions perform several kinds of
parameter expansion .
*/
# 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>
# 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"
# include "tokenizer.h"
# include "complete.h"
/**
Description for child process
*/
# define COMPLETE_CHILD_PROCESS_DESC COMPLETE_SEP_STR L"Child process"
/**
Description for non - child process
*/
# define COMPLETE_PROCESS_DESC COMPLETE_SEP_STR L"Process"
/**
Description for long job
*/
# define COMPLETE_JOB_DESC COMPLETE_SEP_STR L"Job"
/**
Description for short job . The job command is concatenated
*/
# define COMPLETE_JOB_DESC_VAL COMPLETE_SEP_STR, L"Job: "
/**
Description for the shells own pid
*/
# define COMPLETE_SELF_DESC COMPLETE_SEP_STR L"Shell process"
/**
Description for the shells own pid
*/
# define COMPLETE_LAST_DESC COMPLETE_SEP_STR L"Last background job"
2005-12-07 13:03:30 +00:00
# define COMPLETE_VAR_DESC L"Variable name is zero characters long."
2005-12-09 03:50:42 +00:00
# define COMPLETE_VAR2_DESC L"Variable name is zero characters long. Did you mean{$VARIABLE}? To learn about variable expansion in fish, type 'help expand-variable'."
2005-12-07 13:03:30 +00:00
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
character of the string . See \ c is_clean ( ) .
*/
# define UNCLEAN_FIRST L"~%"
/**
Unclean characters . See \ c is_clean ( ) .
*/
# define UNCLEAN L"$*?\\\"'({})"
/**
2005-10-26 14:48:23 +00: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 15:57:17 +00: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 10:51:02 +00:00
*/
static int is_clean ( const wchar_t * in )
{
const wchar_t * str = in ;
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 ;
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 + + ;
}
return 1 ;
}
2005-09-20 13:26:39 +00:00
/**
2005-10-26 14:48:23 +00:00
Return the environment variable value for the string starting at \ c in .
2005-09-20 13:26:39 +00:00
*/
static wchar_t * expand_var ( wchar_t * in )
{
if ( ! in )
return 0 ;
2005-11-02 16:49:13 +00:00
return env_get ( in ) ;
2005-09-20 13:26:39 +00:00
}
void expand_variable_array ( const wchar_t * val , array_list_t * out )
{
if ( val )
{
wchar_t * cpy = wcsdup ( val ) ;
wchar_t * pos , * start ;
if ( ! cpy )
{
die_mem ( ) ;
}
for ( start = pos = cpy ; * pos ; pos + + )
{
if ( * pos = = ARRAY_SEP )
{
* pos = 0 ;
al_push ( out , wcsdup ( start ) ) ;
start = pos + 1 ;
}
}
al_push ( out , wcsdup ( start ) ) ;
2005-11-02 16:49:13 +00:00
2005-09-20 13:26:39 +00:00
free ( cpy ) ;
}
}
2005-10-07 10:36:51 +00:00
wchar_t * expand_escape ( const wchar_t * in ,
2005-09-20 13:26:39 +00:00
int escape_all )
{
return escape ( in , escape_all ) ;
}
/**
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 ;
case L ' \n ' :
case L ' \t ' :
case L ' \r ' :
case L ' \b ' :
case L ' \e ' :
return 0 ;
default :
return is_quotable ( str + 1 ) ;
}
return 0 ;
}
wchar_t * expand_escape_variable ( const wchar_t * in )
{
array_list_t l ;
string_buffer_t buff ;
al_init ( & l ) ;
expand_variable_array ( in , & l ) ;
sb_init ( & buff ) ;
switch ( al_get_count ( & l ) )
{
case 0 :
2005-11-02 16:49:13 +00:00
sb_append ( & buff , L " '' " ) ;
2005-09-20 13:26:39 +00:00
break ;
case 1 :
{
wchar_t * el = ( wchar_t * ) al_get ( & l , 0 ) ;
if ( wcschr ( el , L ' ' ) & & is_quotable ( el ) )
{
sb_append2 ( & buff ,
2005-11-02 16:49:13 +00:00
L " ' " ,
2005-09-20 13:26:39 +00:00
el ,
2005-11-02 16:49:13 +00:00
L " ' " ,
2005-09-30 18:28:26 +00:00
( void * ) 0 ) ;
2005-09-20 13:26:39 +00:00
}
else
{
2005-10-07 10:36:51 +00:00
wchar_t * val = expand_escape ( el , 1 ) ;
2005-09-20 13:26:39 +00: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 " " ) ;
if ( is_quotable ( el ) )
{
sb_append2 ( & buff ,
2005-11-02 16:49:13 +00:00
L " ' " ,
2005-09-20 13:26:39 +00:00
el ,
2005-11-02 16:49:13 +00:00
L " ' " ,
2005-09-30 18:28:26 +00:00
( void * ) 0 ) ;
2005-09-20 13:26:39 +00:00
}
else
{
2005-10-07 10:36:51 +00:00
wchar_t * val = expand_escape ( el , 1 ) ;
2005-09-20 13:26:39 +00:00
sb_append ( & buff , val ) ;
free ( val ) ;
}
free ( el ) ;
}
}
}
al_destroy ( & l ) ;
return ( wchar_t * ) buff . buff ;
}
/**
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
*/
static int match_pid ( const wchar_t * cmd ,
const wchar_t * proc ,
int flags )
{
/* Test for direct match */
if ( wcsncmp ( cmd , proc , wcslen ( proc ) ) = = 0 )
return 1 ;
if ( flags & ACCEPT_INCOMPLETE )
return 0 ;
/*
Test if the commandline is a path to the command , if so we try
to match against only the command part
*/
wchar_t * first_token = tok_first ( cmd ) ;
if ( first_token = = 0 )
return 0 ;
wchar_t * start = 0 ;
wchar_t prev = 0 ;
wchar_t * p ;
/*
This should be done by basename ( ) , if it wasn ' t for the fact
that is does not accept wide strings
*/
for ( p = first_token ; * p ; p + + )
{
if ( * p = = L ' / ' & & prev ! = L ' \\ ' )
start = p ;
prev = * p ;
}
if ( start )
{
if ( wcsncmp ( start + 1 , proc , wcslen ( proc ) ) = = 0 )
{
free ( first_token ) ;
return 1 ;
}
}
free ( first_token ) ;
return 0 ;
}
/**
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-29 19:50:30 +00:00
If accept_incomplete is true , the remaining string for any matches
are inserted .
2005-09-20 13:26:39 +00: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 .
*/
static int find_process ( const wchar_t * proc ,
int flags ,
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 ;
wchar_t * result ;
job_t * j ;
if ( iswnumeric ( proc ) | | ( wcslen ( proc ) = = 0 ) )
{
/*
This is a numeric job string , like ' % 2 '
*/
// fwprintf( stderr, L"Numeric\n\n\n" );
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 ) ;
// fwprintf( stderr, L"Jid %ls\n", jid );
if ( wcsncmp ( proc , jid , wcslen ( proc ) ) = = 0 )
{
al_push ( out ,
wcsdupcat2 ( jid + wcslen ( proc ) ,
COMPLETE_JOB_DESC_VAL , j - > command ,
0 ) ) ;
}
}
}
else
{
int jid = wcstol ( proc , 0 , 10 ) ;
j = job_get ( jid ) ;
if ( ( j ! = 0 ) & & ( j - > command ! = 0 ) )
{
{
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 ;
}
}
}
}
if ( found )
return 1 ;
for ( j = first_job ; j ! = 0 ; j = j - > next )
{
// fwprintf( stderr, L"..." );
if ( j - > command = = 0 )
continue ;
// fwprintf( stderr, L"match '%ls' '%ls'\n\n\n", j->command, proc );
if ( match_pid ( j - > command , proc , flags ) )
{
if ( flags & ACCEPT_INCOMPLETE )
{
wchar_t * res = wcsdupcat ( j - > command + wcslen ( proc ) ,
COMPLETE_JOB_DESC ) ;
// fwprintf( stderr, L"Woot %ls\n", res );
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 )
{
// fwprintf( stderr, L"..." );
if ( p - > actual_cmd = = 0 )
continue ;
// fwprintf( stderr, L"match '%ls' '%ls'\n\n\n", j->command, proc );
if ( match_pid ( p - > actual_cmd , proc , flags ) )
{
if ( flags & ACCEPT_INCOMPLETE )
{
wchar_t * res = wcsdupcat ( p - > actual_cmd + wcslen ( proc ) ,
COMPLETE_CHILD_PROCESS_DESC ) ;
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 " ) ) )
{
/*
This system does not have a / proc filesystem .
*/
return 1 ;
}
pdir_name = malloc ( 256 ) ;
pfile_name = malloc ( 64 ) ;
strcpy ( pdir_name , " /proc/ " ) ;
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 ;
}
if ( buf . st_uid ! = getuid ( ) )
{
continue ;
}
strcpy ( pfile_name , pdir_name ) ;
strcat ( pfile_name , " /cmdline " ) ;
if ( ! stat ( pfile_name , & buf ) )
{
/*
the ' cmdline ' file exists , it should contain the commandline
*/
FILE * cmdfile ;
if ( ( cmdfile = fopen ( pfile_name , " r " ) ) = = 0 )
{
wperror ( L " fopen " ) ;
continue ;
}
fgetws2 ( & cmd , & sz , cmdfile ) ;
fclose ( cmdfile ) ;
}
else
{
# ifdef SunOS
strcpy ( pfile_name , pdir_name ) ;
strcat ( pfile_name , " /psinfo " ) ;
if ( ! stat ( pfile_name , & buf ) )
{
psinfo_t info ;
FILE * psfile ;
if ( ( psfile = fopen ( pfile_name , " r " ) ) = = 0 )
{
wperror ( L " fopen " ) ;
continue ;
}
if ( fread ( & info , sizeof ( info ) , 1 , psfile ) )
{
if ( cmd ! = 0 )
free ( cmd ) ;
cmd = str2wcs ( info . pr_fname ) ;
}
fclose ( psfile ) ;
}
else
# endif
{
if ( cmd ! = 0 )
{
* cmd = 0 ;
}
}
}
if ( cmd ! = 0 )
{
if ( match_pid ( cmd , proc , flags ) )
{
if ( flags & ACCEPT_INCOMPLETE )
{
wchar_t * res = wcsdupcat ( cmd + wcslen ( proc ) ,
COMPLETE_PROCESS_DESC ) ;
if ( res )
al_push ( out , res ) ;
}
else
{
wchar_t * res = str2wcs ( name ) ;
if ( res )
2005-09-24 19:35:08 +00:00
al_push ( out , res ) ;
2005-09-20 13:26:39 +00:00
}
}
}
}
if ( cmd ! = 0 )
free ( cmd ) ;
free ( pdir_name ) ;
free ( pfile_name ) ;
closedir ( dir ) ;
return 1 ;
}
/**
Process id expansion
*/
static int expand_pid ( wchar_t * in ,
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 )
{
wchar_t * res = wcsdupcat ( SELF_STR + wcslen ( in + 1 ) , COMPLETE_SELF_DESC ) ;
al_push ( out , res ) ;
}
else if ( wcsncmp ( in + 1 , LAST_STR , wcslen ( in + 1 ) ) = = 0 )
{
wchar_t * res = wcsdupcat ( LAST_STR + wcslen ( in + 1 ) , COMPLETE_LAST_DESC ) ;
al_push ( out , res ) ;
}
}
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 ) ;
return 1 ;
}
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 ) ;
}
return 1 ;
}
}
// fwprintf( stderr, L"expand_pid() %ls\n", in );
int prev = al_get_count ( out ) ;
if ( ! find_process ( in + 1 , flags , out ) )
return 0 ;
if ( prev = = al_get_count ( out ) )
{
// fwprintf( stderr, L"no match\n" );
if ( flags & ACCEPT_INCOMPLETE )
free ( in ) ;
else
{
* in = L ' % ' ;
// fwprintf( stderr, L"return %ls\n", in );
al_push ( out , in ) ;
}
}
else
{
// fwprintf( stderr, L"match\n" );
free ( in ) ;
}
return 1 ;
}
/**
Expand all environment variables in the string * ptr .
*/
static int expand_variables ( wchar_t * in , array_list_t * out )
{
wchar_t c ;
wchar_t prev_char = 0 ;
int i , j ;
int is_ok = 1 ;
int empty = 0 ;
for ( i = wcslen ( in ) - 1 ; ( i > = 0 ) & & is_ok & & ! empty ; i - - )
{
c = in [ i ] ;
2005-11-02 16:49:13 +00:00
if ( ( c = = VARIABLE_EXPAND ) | | ( c = = VARIABLE_EXPAND_SINGLE ) )
2005-09-20 13:26:39 +00:00
{
int start_pos = i + 1 ;
int stop_pos ;
int var_len , new_len ;
wchar_t * var_name ;
wchar_t * var_val ;
wchar_t * new_in ;
array_list_t l ;
2005-11-02 16:49:13 +00:00
int is_single = ( c = = VARIABLE_EXPAND_SINGLE ) ;
2005-12-14 13:53:36 +00:00
2005-09-20 13:26:39 +00:00
stop_pos = start_pos ;
while ( 1 )
{
if ( ! ( in [ stop_pos ] ) )
break ;
if ( ! ( iswalnum ( in [ stop_pos ] ) | |
( wcschr ( L " _ " , in [ stop_pos ] ) ! = 0 ) ) )
break ;
stop_pos + + ;
}
/* printf( "Stop for '%c'\n", in[stop_pos]);*/
var_len = stop_pos - start_pos ;
2005-12-07 13:03:30 +00:00
if ( var_len = = 0 )
{
if ( in [ stop_pos ] = = BRACKET_BEGIN )
{
error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
- 1 , COMPLETE_VAR_DESC
COMPLETE_VAR2_DESC ) ;
2005-12-07 13:03:30 +00:00
}
else
{
error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
- 1 ,
COMPLETE_VAR_DESC ) ;
2005-12-07 13:03:30 +00:00
}
is_ok = 0 ;
break ;
}
2005-09-20 13:26:39 +00:00
if ( ! ( var_name = malloc ( sizeof ( wchar_t ) * ( var_len + 1 ) ) ) )
{
die_mem ( ) ;
}
2005-11-02 16:49:13 +00:00
wcsncpy ( var_name , & in [ start_pos ] , var_len ) ;
var_name [ var_len ] = ' \0 ' ;
2005-09-20 13:26:39 +00:00
/* printf( "Variable name is %s, len is %d\n", var_name, var_len );*/
2005-11-02 16:49:13 +00:00
wchar_t * var_val_orig = expand_var ( var_name ) ;
2005-09-20 13:26:39 +00:00
2005-11-02 16:49:13 +00:00
if ( var_val_orig & & ( var_val = wcsdup ( var_val_orig ) ) )
{
int all_vars = 1 ;
array_list_t idx ;
al_init ( & idx ) ;
al_init ( & l ) ;
2005-09-20 13:26:39 +00:00
2005-11-02 16:49:13 +00:00
if ( in [ stop_pos ] = = L ' [ ' )
{
wchar_t * end ;
2005-09-20 13:26:39 +00:00
2005-11-02 16:49:13 +00:00
all_vars = 0 ;
2005-09-20 13:26:39 +00:00
2005-11-02 16:49:13 +00:00
stop_pos + + ;
while ( 1 )
{
int tmp ;
2005-09-20 13:26:39 +00:00
2005-11-02 16:49:13 +00:00
while ( iswspace ( in [ stop_pos ] ) | | ( in [ stop_pos ] = = INTERNAL_SEPARATOR ) )
stop_pos + + ;
2005-09-20 13:26:39 +00:00
2005-11-02 16:49:13 +00:00
if ( in [ stop_pos ] = = L ' ] ' )
{
stop_pos + + ;
break ;
}
2005-09-20 13:26:39 +00:00
2005-11-02 16:49:13 +00:00
errno = 0 ;
tmp = wcstol ( & in [ stop_pos ] , & end , 10 ) ;
if ( ( errno ) | | ( end = = & in [ stop_pos ] ) )
{
error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
- 1 ,
L " Expected integer or \' ] \' " ) ;
2005-11-02 16:49:13 +00:00
is_ok = 0 ;
break ;
2005-09-20 13:26:39 +00:00
}
2005-11-02 16:49:13 +00:00
al_push ( & idx , ( void * ) tmp ) ;
stop_pos = end - in ;
2005-09-20 13:26:39 +00:00
}
2005-11-02 16:49:13 +00:00
}
2005-09-20 13:26:39 +00:00
2005-11-02 16:49:13 +00:00
if ( is_ok )
{
expand_variable_array ( var_val , & l ) ;
if ( ! all_vars )
{
int j ;
for ( j = 0 ; j < al_get_count ( & idx ) ; j + + )
{
int tmp = ( int ) al_get ( & idx , j ) ;
if ( tmp < 1 | | tmp > al_get_count ( & l ) )
2005-09-20 13:26:39 +00:00
{
2005-12-07 16:06:47 +00:00
error ( SYNTAX_ERROR ,
- 1 ,
L " Array index out of bounds " ) ;
2005-11-02 16:49:13 +00:00
is_ok = 0 ;
al_truncate ( & idx , j ) ;
break ;
}
else
{
/* Move string from list l to list idx */
al_set ( & idx , j , al_get ( & l , tmp - 1 ) ) ;
al_set ( & l , tmp - 1 , 0 ) ;
2005-09-20 13:26:39 +00:00
}
}
2005-11-02 16:49:13 +00:00
/* Free remaining strings in list l and truncate it */
al_foreach ( & l , ( void ( * ) ( const void * ) ) & free ) ;
al_truncate ( & l , 0 ) ;
/* Add items from list idx back to list l */
al_push_all ( & l , & idx ) ;
}
free ( var_val ) ;
}
if ( is_single )
{
string_buffer_t res ;
sb_init ( & res ) ;
in [ i ] = 0 ;
sb_append ( & res , in ) ;
2005-12-14 13:53:36 +00:00
sb_append_char ( & res , INTERNAL_SEPARATOR ) ;
2005-09-20 13:26:39 +00:00
2005-11-02 16:49:13 +00:00
for ( j = 0 ; j < al_get_count ( & l ) ; j + + )
{
wchar_t * next = ( wchar_t * ) al_get ( & l , j ) ;
if ( is_ok )
{
if ( j ! = 0 )
sb_append ( & res , L " " ) ;
sb_append ( & res , next ) ;
}
free ( next ) ;
}
sb_append ( & res , & in [ stop_pos ] ) ;
is_ok & = expand_variables ( wcsdup ( ( wchar_t * ) res . buff ) , out ) ;
sb_destroy ( & res ) ;
}
else
{
2005-09-20 13:26:39 +00:00
for ( j = 0 ; j < al_get_count ( & l ) ; j + + )
{
wchar_t * next = ( wchar_t * ) al_get ( & l , j ) ;
if ( is_ok )
{
2005-11-02 16:49:13 +00:00
2005-09-20 13:26:39 +00:00
new_len = wcslen ( in ) - ( stop_pos - start_pos + 1 ) + wcslen ( next ) + 2 ;
if ( ! ( new_in = malloc ( sizeof ( wchar_t ) * new_len ) ) )
{
2005-12-07 16:06:47 +00:00
die_mem ( ) ;
2005-09-20 13:26:39 +00:00
}
else
{
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 ] ) ;
// fwprintf( stderr, L"New value %ls\n", new_in );
is_ok & = expand_variables ( new_in , out ) ;
}
}
free ( next ) ;
2005-11-02 16:49:13 +00:00
}
2005-09-20 13:26:39 +00:00
}
2005-11-02 16:49:13 +00:00
al_destroy ( & l ) ;
al_destroy ( & idx ) ;
free ( in ) ;
free ( var_name ) ;
return is_ok ;
}
else
{
/*
Expand a non - existing variable
*/
if ( c = = VARIABLE_EXPAND )
2005-09-20 13:26:39 +00:00
{
2005-11-02 16:49:13 +00:00
/*
2005-12-07 15:57:17 +00:00
Regular expansion , i . e . expand this argument to nothing
2005-11-02 16:49:13 +00:00
*/
2005-09-20 13:26:39 +00:00
empty = 1 ;
}
2005-11-02 16:49:13 +00:00
else
{
/*
2005-12-07 15:57:17 +00:00
Expansion to single argument .
2005-11-02 16:49:13 +00:00
*/
string_buffer_t res ;
sb_init ( & res ) ;
in [ i ] = 0 ;
sb_append ( & res , in ) ;
sb_append ( & res , & in [ stop_pos ] ) ;
is_ok & = expand_variables ( wcsdup ( ( wchar_t * ) res . buff ) , out ) ;
sb_destroy ( & res ) ;
free ( in ) ;
free ( var_name ) ;
return is_ok ;
}
2005-09-20 13:26:39 +00:00
}
2005-11-02 16:49:13 +00:00
free ( var_name ) ;
2005-09-20 13:26:39 +00:00
}
2005-11-02 16:49:13 +00:00
2005-09-20 13:26:39 +00:00
prev_char = c ;
}
if ( ! empty )
{
al_push ( out , in ) ;
}
else
{
free ( in ) ;
}
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 ;
int bracket_count = 0 ;
wchar_t * bracket_begin = 0 , * bracket_end = 0 ;
wchar_t * last_sep = 0 ;
wchar_t * item_begin ;
int len1 , len2 , tot_len ;
// fwprintf( stderr, L"expand %ls\n", in );
for ( pos = in ;
( ! bracket_end ) & & ( * pos ) & & ! syntax_error ;
pos + + )
{
switch ( * pos )
{
case BRACKET_BEGIN :
{
if ( ( bracket_count = = 0 ) & & ( bracket_begin = = 0 ) )
bracket_begin = pos ;
bracket_count + + ;
break ;
}
case BRACKET_END :
{
bracket_count - - ;
if ( ( bracket_count = = 0 ) & & ( bracket_end = = 0 ) )
bracket_end = pos ;
if ( bracket_count < 0 )
{
syntax_error = 1 ;
}
break ;
}
case BRACKET_SEP :
{
if ( bracket_count = = 1 )
last_sep = pos ;
}
}
}
if ( bracket_count > 0 )
{
if ( ! ( flags & ACCEPT_INCOMPLETE ) )
syntax_error = 1 ;
else
{
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 ) ;
}
return expand_brackets ( ( wchar_t * ) mod . buff , 1 , out ) ;
}
}
if ( syntax_error )
{
2005-12-07 16:06:47 +00:00
error ( SYNTAX_ERROR ,
- 1 ,
L " Mismatched brackets " ) ;
2005-09-20 13:26:39 +00:00
return 0 ;
}
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 ;
whole_item = malloc ( sizeof ( wchar_t ) * ( tot_len + item_len + 1 ) ) ;
wcsncpy ( whole_item , in , len1 ) ;
wcsncpy ( whole_item + len1 , item_begin , item_len ) ;
wcscpy ( whole_item + len1 + item_len , bracket_end + 1 ) ;
expand_brackets ( whole_item , flags , out ) ;
item_begin = pos + 1 ;
if ( pos = = bracket_end )
break ;
}
}
if ( * pos = = BRACKET_BEGIN )
{
bracket_count + + ;
}
if ( * pos = = BRACKET_END )
{
bracket_count - - ;
}
}
free ( in ) ;
return 1 ;
}
int expand_locate_subshell ( wchar_t * in ,
wchar_t * * begin ,
wchar_t * * end ,
int allow_incomplete )
{
wchar_t * pos ;
wchar_t prev = 0 ;
int syntax_error = 0 ;
int paran_count = 0 ;
wchar_t * paran_begin = 0 , * paran_end = 0 ;
for ( pos = in ; * pos ; pos + + )
{
if ( prev ! = ' \\ ' )
{
if ( wcschr ( L " \' \" " , * pos ) )
{
wchar_t * end = quote_end ( pos ) ;
if ( end & & * end )
{
pos = end ;
}
else
break ;
}
else
{
if ( * pos = = ' ( ' )
{
if ( ( paran_count = = 0 ) & & ( paran_begin = = 0 ) )
paran_begin = pos ;
paran_count + + ;
}
else if ( * pos = = ' ) ' )
{
paran_count - - ;
if ( ( paran_count = = 0 ) & & ( paran_end = = 0 ) )
{
paran_end = pos ;
break ;
}
if ( paran_count < 0 )
{
syntax_error = 1 ;
break ;
}
}
}
}
prev = * pos ;
}
syntax_error | = ( paran_count < 0 ) ;
syntax_error | = ( ( paran_count > 0 ) & & ( ! allow_incomplete ) ) ;
if ( syntax_error )
{
return - 1 ;
}
if ( paran_begin = = 0 )
{
return 0 ;
}
* begin = paran_begin ;
* end = paran_count ? in + wcslen ( in ) : paran_end ;
return 1 ;
}
/**
Perform subshell expansion
*/
static int expand_subshell ( wchar_t * in , array_list_t * out )
{
wchar_t * paran_begin = 0 , * paran_end = 0 ;
int len1 , len2 ;
wchar_t prev = 0 ;
wchar_t * subcmd ;
array_list_t sub_res , tail_expand ;
int i , j ;
wchar_t * item_begin ;
switch ( expand_locate_subshell ( in ,
& paran_begin ,
& paran_end ,
0 ) )
{
case - 1 :
2005-12-07 16:06:47 +00:00
error ( SYNTAX_ERROR ,
- 1 ,
L " Mismatched parans " ) ;
2005-09-20 13:26:39 +00:00
return 0 ;
case 0 :
al_push ( out , in ) ;
return 1 ;
case 1 :
break ;
2005-10-27 15:21:48 +00:00
}
2005-09-20 13:26:39 +00: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 ) ;
return 0 ;
}
wcsncpy ( subcmd , paran_begin + 1 , paran_end - paran_begin - 1 ) ;
subcmd [ paran_end - paran_begin - 1 ] = 0 ;
if ( exec_subshell ( subcmd , & sub_res ) = = - 1 )
{
al_foreach ( & sub_res , ( void ( * ) ( const void * ) ) & free ) ;
al_destroy ( & sub_res ) ;
free ( subcmd ) ;
return 0 ;
}
2005-10-27 15:21:48 +00:00
2005-09-20 13:26:39 +00:00
al_init ( & tail_expand ) ;
expand_subshell ( wcsdup ( paran_end + 1 ) , & tail_expand ) ;
2005-10-27 15:21:48 +00:00
2005-09-20 13:26:39 +00:00
for ( i = 0 ; i < al_get_count ( & sub_res ) ; i + + )
{
2005-10-07 10:36:51 +00:00
wchar_t * sub_item , * sub_item2 ;
2005-09-20 13:26:39 +00:00
sub_item = ( wchar_t * ) al_get ( & sub_res , i ) ;
2005-10-07 10:36:51 +00:00
sub_item2 = expand_escape ( sub_item , 1 ) ;
free ( sub_item ) ;
int item_len = wcslen ( sub_item2 ) ;
2005-10-27 15:21:48 +00:00
2005-09-20 13:26:39 +00: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 ) ;
2005-10-27 15:21:48 +00:00
2005-09-20 13:26:39 +00:00
sb_init ( & whole_item ) ;
2005-10-27 15:21:48 +00:00
2005-09-20 13:26:39 +00:00
sb_append_substring ( & whole_item , in , len1 ) ;
sb_append_char ( & whole_item , INTERNAL_SEPARATOR ) ;
2005-10-07 10:36:51 +00:00
sb_append_substring ( & whole_item , sub_item2 , item_len ) ;
2005-09-20 13:26:39 +00:00
sb_append_char ( & whole_item , INTERNAL_SEPARATOR ) ;
sb_append ( & whole_item , tail_item ) ;
2005-10-27 15:21:48 +00:00
2005-09-20 13:26:39 +00:00
al_push ( out , whole_item . buff ) ;
}
2005-10-07 10:36:51 +00:00
free ( sub_item2 ) ;
2005-09-20 13:26:39 +00:00
}
free ( in ) ;
al_destroy ( & sub_res ) ;
al_foreach ( & tail_expand , ( void ( * ) ( const void * ) ) & free ) ;
al_destroy ( & tail_expand ) ;
2005-10-27 15:21:48 +00:00
2005-09-20 13:26:39 +00:00
free ( subcmd ) ;
return 1 ;
}
2005-10-07 10:36:51 +00:00
wchar_t * expand_unescape ( 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 )
2005-12-07 16:06:47 +00:00
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
}
/**
Attempts tilde expansion . Of the string specified . If tilde
2005-11-29 16:52:02 +00: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 13:26:39 +00:00
*/
2005-11-29 16:52:02 +00:00
static wchar_t * expand_tilde_internal ( wchar_t * in )
2005-09-20 13:26:39 +00:00
{
if ( in [ 0 ] = = HOME_DIRECTORY )
{
int tilde_error = 0 ;
wchar_t * home = 0 ;
2005-11-29 16:52:02 +00:00
wchar_t * new_in = 0 ;
wchar_t * old_in = 0 ;
2005-09-20 13:26:39 +00:00
// fwprintf( stderr, L"Tilde expand ~%ls\n", (*ptr)+1 );
if ( in [ 1 ] = = ' / ' | | in [ 1 ] = = ' \0 ' )
{
/* Current users home directory */
home = env_get ( L " HOME " ) ;
if ( home )
home = wcsdup ( home ) ;
else
home = wcsdup ( L " " ) ;
if ( ! home )
{
* in = L ' ~ ' ;
tilde_error = 1 ;
}
old_in = & in [ 1 ] ;
}
else
{
/* Some other users home directory */
wchar_t * name ;
wchar_t * name_end = wcschr ( in , L ' / ' ) ;
if ( name_end = = 0 )
{
name_end = in + wcslen ( in ) ;
}
name = wcsndup ( in + 1 , name_end - in - 1 ) ;
old_in = name_end ;
char * name_str = wcs2str ( name ) ;
2005-10-27 15:21:48 +00:00
2005-09-20 13:26:39 +00:00
struct passwd * userinfo =
getpwnam ( name_str ) ;
if ( userinfo = = 0 )
{
tilde_error = 1 ;
* in = L ' ~ ' ;
}
else
{
home = str2wcs ( userinfo - > pw_dir ) ;
if ( ! home )
{
* in = L ' ~ ' ;
tilde_error = 1 ;
}
}
free ( name_str ) ;
free ( name ) ;
}
2005-11-29 16:52:02 +00:00
if ( ! tilde_error & & home & & old_in )
2005-09-20 13:26:39 +00:00
{
new_in = wcsdupcat ( home , old_in ) ;
2005-11-29 16:52:02 +00:00
}
free ( home ) ;
free ( in ) ;
return new_in ;
2005-09-20 13:26:39 +00:00
}
2005-11-29 16:52:02 +00:00
return in ;
2005-09-20 13:26:39 +00:00
}
2005-11-29 16:52:02 +00:00
wchar_t * expand_tilde ( wchar_t * in )
2005-09-20 13:26:39 +00:00
{
if ( in [ 0 ] = = L ' ~ ' )
{
in [ 0 ] = HOME_DIRECTORY ;
2005-11-29 16:52:02 +00:00
return expand_tilde_internal ( in ) ;
2005-09-20 13:26:39 +00:00
}
return in ;
}
/**
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 ;
case ANY_CHAR :
in + + ;
* out + + = conv ? L ' ? ' : ANY_CHAR ;
break ;
case ANY_STRING :
in + + ;
* out + + = conv ? L ' * ' : ANY_STRING ;
break ;
default :
* out + + = * in + + ;
}
}
* out = 0 ;
}
/**
2005-12-07 15:57:17 +00:00
The real expansion function . expand_one is just a wrapper around this one .
2005-09-20 13:26:39 +00:00
*/
int expand_string ( wchar_t * str ,
array_list_t * end_out ,
int flags )
{
array_list_t list1 , list2 ;
array_list_t * in , * out ;
int i ;
int subshell_ok = 1 ;
2005-12-03 16:43:56 +00:00
int res = EXPAND_OK ;
2005-12-04 01:54:02 +00:00
// debug( 1, L"Expand %ls", str );
2005-10-26 10:51:02 +00:00
if ( ( ! ( flags & ACCEPT_INCOMPLETE ) ) & & is_clean ( str ) )
{
al_push ( end_out , str ) ;
2005-12-03 16:43:56 +00:00
return EXPAND_OK ;
2005-10-26 10:51:02 +00:00
}
2005-09-20 13:26:39 +00:00
al_init ( & list1 ) ;
al_init ( & list2 ) ;
if ( EXPAND_SKIP_SUBSHELL & flags )
{
wchar_t * pos = str ;
while ( 1 )
{
pos = wcschr ( pos , L ' ( ' ) ;
if ( pos = = 0 )
break ;
if ( ( pos = = str ) | | ( * ( pos - 1 ) ! = L ' \\ ' ) )
{
2005-12-07 16:06:47 +00:00
error ( SUBSHELL_ERROR , - 1 , L " Subshells not allowed " ) ;
2005-10-25 11:03:52 +00:00
free ( str ) ;
2005-09-20 13:26:39 +00:00
al_destroy ( & list1 ) ;
al_destroy ( & list2 ) ;
2005-12-03 16:43:56 +00:00
return EXPAND_ERROR ;
2005-09-20 13:26:39 +00:00
}
pos + + ;
}
al_push ( & list1 , str ) ;
}
else
{
subshell_ok = expand_subshell ( str , & list1 ) ;
}
if ( ! subshell_ok )
{
al_destroy ( & list1 ) ;
2005-12-03 16:43:56 +00:00
return EXPAND_ERROR ;
2005-09-20 13:26:39 +00:00
}
else
{
in = & list1 ;
out = & list2 ;
for ( i = 0 ; i < al_get_count ( in ) ; i + + )
{
2005-10-07 10:36:51 +00:00
wchar_t * next ;
2005-10-27 15:21:48 +00:00
2005-10-07 10:36:51 +00:00
next = expand_unescape ( ( wchar_t * ) al_get ( in , i ) ,
1 ) ;
free ( ( void * ) al_get ( in , i ) ) ;
2005-11-29 16:52:02 +00:00
2005-10-07 10:36:51 +00:00
if ( ! next )
continue ;
2005-09-20 13:26:39 +00:00
if ( EXPAND_SKIP_VARIABLES & flags )
{
wchar_t * tmp ;
for ( tmp = next ; * tmp ; tmp + + )
if ( * tmp = = VARIABLE_EXPAND )
* tmp = L ' $ ' ;
al_push ( out , next ) ;
}
else
{
if ( ! expand_variables ( next , out ) )
{
al_destroy ( in ) ;
al_destroy ( out ) ;
2005-12-03 16:43:56 +00:00
return EXPAND_ERROR ;
2005-09-20 13:26:39 +00:00
}
}
}
al_truncate ( in , 0 ) ;
in = & list2 ;
out = & list1 ;
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-03 16:43:56 +00:00
return EXPAND_ERROR ;
2005-09-20 13:26:39 +00:00
}
}
al_truncate ( in , 0 ) ;
in = & list1 ;
out = & list2 ;
for ( i = 0 ; i < al_get_count ( in ) ; i + + )
{
wchar_t * next = ( wchar_t * ) al_get ( in , i ) ;
2005-11-29 16:52:02 +00:00
if ( ! ( next = expand_tilde_internal ( next ) ) )
2005-09-20 13:26:39 +00:00
{
al_destroy ( in ) ;
al_destroy ( out ) ;
2005-12-03 16:43:56 +00:00
return EXPAND_ERROR ;
2005-09-20 13:26:39 +00:00
}
if ( flags & ACCEPT_INCOMPLETE )
{
if ( * next = = PROCESS_EXPAND )
{
2005-12-03 16:43:56 +00:00
/*
2005-12-07 15:57:17 +00:00
If process expansion matches , we are not
2005-12-03 16:43:56 +00:00
interested in other completions , so we
short - circut and return
*/
2005-09-20 13:26:39 +00:00
expand_pid ( next , flags , end_out ) ;
al_destroy ( in ) ;
al_destroy ( out ) ;
2005-12-03 16:43:56 +00:00
return EXPAND_OK ;
2005-09-20 13:26:39 +00:00
}
else
al_push ( out , next ) ;
}
else
{
if ( ! expand_pid ( next , flags , out ) )
{
al_destroy ( in ) ;
al_destroy ( out ) ;
2005-12-03 16:43:56 +00:00
return EXPAND_ERROR ;
2005-09-20 13:26:39 +00:00
}
}
}
al_truncate ( in , 0 ) ;
in = & list2 ;
out = & list1 ;
for ( i = 0 ; i < al_get_count ( in ) ; i + + )
{
wchar_t * next = ( wchar_t * ) al_get ( in , i ) ;
int wc_res ;
remove_internal_separator ( next , EXPAND_SKIP_WILDCARDS & flags ) ;
if ( ( ( flags & ACCEPT_INCOMPLETE ) & & ( ! ( flags & EXPAND_SKIP_WILDCARDS ) ) ) | |
wildcard_has ( next , 1 ) )
{
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 ) )
{
2005-12-03 16:43:56 +00:00
if ( res = = EXPAND_OK )
res = EXPAND_WILDCARD_NO_MATCH ;
2005-12-04 01:54:02 +00:00
break ;
2005-09-20 13:26:39 +00:00
}
2005-12-03 16:43:56 +00:00
2005-09-20 13:26:39 +00:00
case 1 :
2005-12-03 16:43:56 +00:00
res = EXPAND_WILDCARD_MATCH ;
2005-09-20 13:26:39 +00:00
sort_list ( out ) ;
al_push_all ( end_out , out ) ;
al_truncate ( out , 0 ) ;
break ;
}
}
else
{
if ( flags & ACCEPT_INCOMPLETE )
free ( next ) ;
else
al_push ( end_out , next ) ;
}
}
al_destroy ( in ) ;
al_destroy ( out ) ;
}
2005-12-03 16:43:56 +00:00
return res ;
2005-09-20 13:26:39 +00:00
}
wchar_t * expand_one ( wchar_t * string , int flags )
{
array_list_t l ;
int res ;
wchar_t * one ;
2005-10-26 10:51:02 +00:00
if ( ( ! ( flags & ACCEPT_INCOMPLETE ) ) & & is_clean ( string ) )
return string ;
2005-09-20 13:26:39 +00:00
al_init ( & l ) ;
res = expand_string ( string , & l , flags ) ;
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 10:51:02 +00:00
2005-09-20 13:26:39 +00:00
al_foreach ( & l , ( void ( * ) ( const void * ) ) & free ) ;
al_destroy ( & l ) ;
return one ;
}