aboutsummaryrefslogtreecommitdiffhomepage
path: root/expand.c
diff options
context:
space:
mode:
authorGravatar axel <axel@liljencrantz.se>2005-09-20 23:26:39 +1000
committerGravatar axel <axel@liljencrantz.se>2005-09-20 23:26:39 +1000
commit149594f974350bb364a76c73b91b1d5ffddaa1fa (patch)
tree95650e9982d5fabe4bd805d94c5d700cbbc1ca7f /expand.c
Initial revision
darcs-hash:20050920132639-ac50b-fa3b476891e1f5f67207cf4cc7bf623834cc5edc.gz
Diffstat (limited to 'expand.c')
-rw-r--r--expand.c1518
1 files changed, 1518 insertions, 0 deletions
diff --git a/expand.c b/expand.c
new file mode 100644
index 00000000..93461de2
--- /dev/null
+++ b/expand.c
@@ -0,0 +1,1518 @@
+/**\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"
+
+/**
+ String in process expansion denoting ourself
+*/
+#define SELF_STR L"self"
+
+/**
+ String in process expansion denoting last background job
+*/
+#define LAST_STR L"last"
+
+/**
+ Return the environment variable value for the string starting at in
+*/
+static wchar_t *expand_var( wchar_t *in )
+{
+ if( !in )
+ return 0;
+ return (in[0] == VARIABLE_EXPAND )? env_get( expand_var(in+1) ) : env_get( in );
+}
+
+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) );
+
+ free(cpy);
+ }
+}
+
+wchar_t *expand_escape( wchar_t *in,
+ 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:
+ sb_append( &buff, L"\'\'");
+ break;
+
+ case 1:
+ {
+ wchar_t *el = (wchar_t *)al_get( &l, 0 );
+
+ if( wcschr( el, L' ' ) && is_quotable( el ) )
+ {
+ sb_append2( &buff,
+ L"\'",
+ el,
+ L"\'",
+ 0 );
+ }
+ else
+ {
+ wchar_t *val = expand_escape( wcsdup(el), 1 );
+ 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,
+ L"\'",
+ el,
+ L"\'",
+ 0 );
+ }
+ else
+ {
+ wchar_t *val = expand_escape( wcsdup(el), 1 );
+ 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.
+
+ If accept_incomplete is true, the remaining string for any matches are inserted.
+
+ 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 )
+ al_push( out, str2wcs( name ) );
+ }
+ }
+ }
+ }
+
+ 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];
+ if( c == VARIABLE_EXPAND )
+ {
+ 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;
+
+
+
+// fwprintf( stderr, L"Expand %ls\n", in );
+
+
+// while (in[stop_pos]==VARIABLE_EXPAND)
+// stop_pos++;
+
+ 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;
+
+ if( !(var_name = malloc( sizeof(wchar_t)*(var_len+1) )))
+ {
+ die_mem();
+ }
+ else
+ {
+ wcsncpy( var_name, &in[start_pos], var_len );
+ var_name[var_len]='\0';
+/* printf( "Variable name is %s, len is %d\n", var_name, var_len );*/
+ wchar_t *var_val_orig = expand_var( var_name );
+
+ if( var_val_orig && (var_val = wcsdup( var_val_orig) ) )
+ {
+ int all_vars=1;
+ array_list_t idx;
+ al_init( &idx );
+ al_init( &l );
+
+ if( in[stop_pos] == L'[' )
+ {
+ wchar_t *end;
+
+ all_vars = 0;
+
+ stop_pos++;
+ while( 1 )
+ {
+ int tmp;
+
+ while( iswspace(in[stop_pos]) || (in[stop_pos]==INTERNAL_SEPARATOR))
+ stop_pos++;
+
+
+ if( in[stop_pos] == L']' )
+ {
+ stop_pos++;
+ break;
+ }
+
+ errno=0;
+ tmp = wcstol( &in[stop_pos], &end, 10 );
+ if( ( errno ) || ( end == &in[stop_pos] ) )
+ {
+ error( SYNTAX_ERROR,
+ L"Expected integer or \']\'",
+ -1 );
+ is_ok = 0;
+ break;
+ }
+ al_push( &idx, (void *)tmp );
+ stop_pos = end-in;
+ }
+ }
+
+ 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 ) )
+ {
+ error( SYNTAX_ERROR, L"Array index out of bounds", -1 );
+ 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 );
+ }
+ }
+ /* 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 );
+ }
+
+ for( j=0; j<al_get_count( &l); j++ )
+ {
+ wchar_t *next = (wchar_t *)al_get( &l, j );
+
+ if( is_ok )
+ {
+
+ new_len = wcslen(in) - (stop_pos-start_pos+1) + wcslen( next) +2;
+
+ if( !(new_in = malloc( sizeof(wchar_t)*new_len )))
+ {
+ error( OOM, L"Out of memory", -1 );
+ is_ok = 0;
+ }
+ 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 );
+ }
+ al_destroy( &l );
+ al_destroy( &idx );
+ free(in);
+ free(var_name );
+ return is_ok;
+ }
+ else
+ {
+ empty = 1;
+ }
+
+ free(var_name );
+ }
+ }
+ 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 )
+ {
+ error( SYNTAX_ERROR, L"Mismatched brackets", -1 );
+ 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:
+ error( SYNTAX_ERROR, L"Mismatched parans", -1 );
+ return 0;
+ case 0:
+ al_push( out, in );
+ return 1;
+ case 1:
+
+ break;
+
+ }
+
+
+
+ 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;
+ }
+
+ al_init( &tail_expand );
+ expand_subshell( wcsdup(paran_end+1), &tail_expand );
+
+ for( i=0; i<al_get_count( &sub_res ); i++ )
+ {
+ wchar_t *sub_item;
+ sub_item = (wchar_t *)al_get( &sub_res, i );
+ sub_item = expand_escape( sub_item, 1 );
+ int item_len = wcslen( sub_item );
+
+ 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 );
+
+ sb_init( &whole_item );
+
+ sb_append_substring( &whole_item, in, len1 );
+ sb_append_char( &whole_item, INTERNAL_SEPARATOR );
+ sb_append_substring( &whole_item, sub_item, item_len );
+ sb_append_char( &whole_item, INTERNAL_SEPARATOR );
+ sb_append( &whole_item, tail_item );
+
+ al_push( out, whole_item.buff );
+ }
+
+ free( sub_item );
+ }
+ free(in);
+
+ al_destroy( &sub_res );
+
+ al_foreach( &tail_expand, (void (*)(const void *))&free );
+ al_destroy( &tail_expand );
+
+ free( subcmd );
+ return 1;
+}
+
+
+wchar_t *expand_backslash( wchar_t * in, int escape_special )
+{
+ return unescape( in, escape_special );
+}
+
+/**
+ Attempts tilde expansion. Of the string specified. If tilde
+ expansion is performed, the argument is freed and a new string is
+ allocated in its place. Horrible call signature. Should be
+ altered. Fugly!
+*/
+static int tilde_expand( wchar_t **ptr )
+{
+ wchar_t *in = *ptr;
+
+ if( in[0] == HOME_DIRECTORY )
+ {
+ int tilde_error = 0;
+ wchar_t *home=0;
+ wchar_t *new_in;
+ wchar_t *old_in;
+
+// fwprintf( stderr, L"Tilde expand ~%ls\n", (*ptr)+1 );
+ if( in[1] == '/' || in[1] == '\0' )
+ {
+ /* Current users home directory */
+ struct passwd *userinfo;
+
+ 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 );
+
+ 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);
+ }
+
+ if( !tilde_error )
+ {
+ new_in = wcsdupcat( home, old_in );
+ free( in );
+ in = new_in;
+ free(home);
+ *ptr = in;
+ }
+
+ }
+ return 1;
+}
+
+wchar_t *expand_tilde(wchar_t *in)
+{
+ if( in[0] == L'~' )
+ {
+ in[0] = HOME_DIRECTORY;
+ tilde_expand( &in );
+ return in;
+ }
+ 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;
+
+// int changed=0;
+
+ 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;
+/* if( changed )
+ {
+ fwprintf( stderr, L" -> %ls\n", s );
+ }
+*/
+}
+
+
+/**
+ The real expansion function. All other expansion functions are wrappers to this one.
+*/
+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;
+
+ 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'\\' ) )
+ {
+ error( SUBSHELL_ERROR, L"Subshells not allowed", -1 );
+ al_destroy( &list1 );
+ al_destroy( &list2 );
+ return 0;
+ }
+ pos++;
+ }
+ al_push( &list1, str );
+ }
+ else
+ {
+ subshell_ok = expand_subshell( str, &list1 );
+ }
+
+ if( !subshell_ok )
+ {
+ al_destroy( &list1 );
+ return 0;
+ }
+ else
+ {
+ in = &list1;
+ out = &list2;
+
+ for( i=0; i<al_get_count( in ); i++ )
+ {
+ wchar_t *next = expand_backslash((wchar_t *)al_get( in, i ),
+ 1);
+
+ 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 );
+ return 0;
+ }
+ }
+ }
+
+ 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 );
+ return 0;
+ }
+ }
+ 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 );
+ if(!tilde_expand( &next ))
+ {
+ al_destroy( in );
+ al_destroy( out );
+ return 0;
+ }
+
+ if( flags & ACCEPT_INCOMPLETE )
+ {
+ if( *next == PROCESS_EXPAND )
+ {
+ expand_pid( next, flags, end_out );
+ al_destroy( in );
+ al_destroy( out );
+ return 1;
+ }
+ else
+ al_push( out, next );
+ }
+ else
+ {
+ if( !expand_pid( next, flags, out ) )
+ {
+ al_destroy( in );
+ al_destroy( out );
+ return 0;
+ }
+ }
+ }
+ 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) )
+ {
+ break;
+ }
+ case 1:
+ sort_list( out );
+ al_push_all( end_out, out );
+ al_truncate( out, 0 );
+ break;
+
+ default:
+ fwprintf( stderr, L"error\n" );
+ /*al_destroy( &list1 );*/
+ /*al_destroy( &list2 );*/
+ /*return 0;*/
+ }
+ }
+ else
+ {
+ if( flags & ACCEPT_INCOMPLETE)
+ free( next );
+ else
+ al_push( end_out, next );
+ }
+ }
+ al_destroy( in );
+ al_destroy( out );
+ }
+
+ return 1;
+}
+
+
+wchar_t *expand_one( wchar_t *string, int flags )
+{
+ array_list_t l;
+ int res;
+ wchar_t *one;
+
+ al_init( &l );
+ res = expand_string( string, &l, flags );
+ if( !res )
+ {
+ free( string );
+ one = 0;
+ }
+ else
+ {
+ if( al_get_count( &l ) != 1 )
+ {
+ one=0;
+ }
+ else
+ {
+ one = (wchar_t *)al_get( &l, 0 );
+ al_set( &l, 0, 0 );
+ }
+ }
+
+ al_foreach( &l, (void(*)(const void *))&free );
+ al_destroy( &l );
+ return one;
+}
+