2005-09-20 13:26:39 +00:00
/**\file expand.c
String expansion functions . These functions perform several kinds of
2006-02-12 11:29:30 +00:00
parameter expansion .
2005-09-20 13:26:39 +00:00
*/
2006-08-11 01:18:35 +00:00
# include "config.h"
2005-09-20 13:26:39 +00:00
# include <stdlib.h>
# include <stdio.h>
# include <wchar.h>
# include <string.h>
# include <wctype.h>
# include <errno.h>
# include <pwd.h>
# include <unistd.h>
# include <sys/types.h>
# include <termios.h>
# include <dirent.h>
# include <sys/stat.h>
# include <unistd.h>
# include <signal.h>
2005-12-25 22:00:44 +00:00
# include <assert.h>
2005-09-20 13:26:39 +00:00
# ifdef SunOS
# include <procfs.h>
# endif
2006-02-28 13:17:16 +00:00
# include "fallback.h"
2005-09-20 13:26:39 +00:00
# include "util.h"
2006-02-28 13:17:16 +00:00
2005-09-20 13:26:39 +00:00
# include "common.h"
# include "wutil.h"
# include "env.h"
# include "proc.h"
# include "parser.h"
# include "expand.h"
# include "wildcard.h"
# include "exec.h"
2006-02-09 15:50:20 +00:00
# include "signal.h"
2005-09-20 13:26:39 +00:00
# include "tokenizer.h"
# include "complete.h"
2006-07-19 22:55:49 +00:00
2006-01-30 16:51:50 +00:00
# include "parse_util.h"
2006-08-24 13:39:04 +00:00
# include "halloc.h"
2006-02-10 00:35:39 +00:00
# include "halloc_util.h"
2005-09-20 13:26:39 +00:00
2006-07-20 13:02:46 +00:00
/**
Error issued on invalid variable name
*/
# define COMPLETE_VAR_DESC _( L"The '$' character begins a variable name. The character '%lc', which directly followed a '$', is not allowed as a part of a variable name, and variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'.")
/**
Error issued on invalid variable name
*/
# define COMPLETE_VAR_NULL_DESC _( L"The '$' begins a variable name. It was given at the end of an argument. Variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'.")
/**
Error issued on invalid variable name
*/
2006-07-20 23:33:19 +00:00
# define COMPLETE_VAR_BRACKET_DESC _( L"Did you mean %ls{$%ls}%ls? The '$' character begins a variable name. A bracket, which directly followed a '$', is not allowed as a part of a variable name, and variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'." )
2006-07-20 13:02:46 +00:00
/**
Error issued on invalid variable name
*/
# define COMPLETE_VAR_PARAN_DESC _( L"Did you mean (COMMAND)? In fish, the '$' character is only used for accessing variables. To learn more about command substitution in fish, type 'help expand-command-substitution'.")
2005-09-20 13:26:39 +00:00
/**
Description for child process
*/
2006-01-17 12:48:30 +00:00
# define COMPLETE_CHILD_PROCESS_DESC _( L"Child process")
2005-09-20 13:26:39 +00:00
/**
Description for non - child process
*/
2006-01-17 12:48:30 +00:00
# define COMPLETE_PROCESS_DESC _( L"Process")
2005-09-20 13:26:39 +00:00
/**
Description for long job
*/
2006-01-17 12:48:30 +00:00
# define COMPLETE_JOB_DESC _( L"Job")
2005-09-20 13:26:39 +00:00
/**
Description for short job . The job command is concatenated
*/
2006-01-17 12:48:30 +00:00
# define COMPLETE_JOB_DESC_VAL _( L"Job: ")
2005-09-20 13:26:39 +00:00
/**
Description for the shells own pid
*/
2006-01-17 12:48:30 +00:00
# define COMPLETE_SELF_DESC _( L"Shell process")
2005-09-20 13:26:39 +00:00
/**
Description for the shells own pid
*/
2006-01-17 12:48:30 +00:00
# define COMPLETE_LAST_DESC _( L"Last background job")
2005-09-20 13:26:39 +00:00
/**
String in process expansion denoting ourself
*/
# define SELF_STR L"self"
/**
String in process expansion denoting last background job
*/
# define LAST_STR L"last"
2005-10-26 10:51:02 +00:00
/**
Characters which make a string unclean if they are the first
character of the string . See \ c is_clean ( ) .
*/
# define UNCLEAN_FIRST L"~%"
/**
Unclean characters . See \ c is_clean ( ) .
*/
# define UNCLEAN L"$*?\\\"'({})"
2006-05-21 22:16:04 +00:00
int expand_is_clean ( const wchar_t * in )
2005-10-26 10:51:02 +00:00
{
2006-01-30 19:53:10 +00:00
2005-10-26 10:51:02 +00:00
const wchar_t * str = in ;
2006-06-21 00:48:36 +00:00
CHECK ( in , 1 ) ;
2005-11-29 16:52:02 +00:00
/*
Test characters that have a special meaning in the first character position
*/
2005-10-26 10:51:02 +00:00
if ( wcschr ( UNCLEAN_FIRST , * str ) )
return 0 ;
2006-01-30 19:53:10 +00:00
2005-11-29 16:52:02 +00:00
/*
Test characters that have a special meaning in any character position
*/
2005-10-26 10:51:02 +00:00
while ( * str )
{
if ( wcschr ( UNCLEAN , * str ) )
return 0 ;
str + + ;
}
2006-01-30 19:53:10 +00:00
2005-10-26 10:51:02 +00:00
return 1 ;
}
2005-09-20 13:26:39 +00:00
/**
2006-01-30 19:53:10 +00:00
Return the environment variable value for the string starting at \ c in .
2005-09-20 13:26:39 +00:00
*/
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
}
/**
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-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
case L ' \n ' :
case L ' \t ' :
case L ' \r ' :
case L ' \b ' :
case L ' \e ' :
return 0 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
default :
2006-01-30 19:53:10 +00:00
return is_quotable ( str + 1 ) ;
2005-09-20 13:26:39 +00:00
}
return 0 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
wchar_t * expand_escape_variable ( const wchar_t * in )
{
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
array_list_t l ;
string_buffer_t buff ;
2006-01-30 19:53:10 +00:00
2006-06-21 00:48:36 +00:00
CHECK ( in , 0 ) ;
2005-09-20 13:26:39 +00:00
al_init ( & l ) ;
2006-05-29 11:13:42 +00:00
tokenize_variable_array ( in , & l ) ;
2005-09-20 13:26:39 +00:00
sb_init ( & buff ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
switch ( al_get_count ( & l ) )
{
case 0 :
2005-11-02 16:49:13 +00:00
sb_append ( & buff , L " '' " ) ;
2006-01-30 19:53:10 +00:00
break ;
2006-05-21 22:16:04 +00:00
2005-09-20 13:26:39 +00:00
case 1 :
{
wchar_t * el = ( wchar_t * ) al_get ( & l , 0 ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
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
{
2006-02-06 15:15:52 +00:00
wchar_t * val = 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 " " ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
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
{
2006-02-06 15:15:52 +00:00
wchar_t * val = escape ( el , 1 ) ;
2005-09-20 13:26:39 +00:00
sb_append ( & buff , val ) ;
free ( val ) ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
free ( el ) ;
2006-01-30 19:53:10 +00:00
}
}
2005-09-20 13:26:39 +00:00
}
al_destroy ( & l ) ;
return ( wchar_t * ) buff . buff ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
/**
Tests if all characters in the wide string are numeric
*/
static int iswnumeric ( const wchar_t * n )
{
2006-05-21 21:46:01 +00:00
for ( ; * n ; n + + )
{
if ( * n < L ' 0 ' | | * n > L ' 9 ' )
{
return 0 ;
}
}
return 1 ;
2005-09-20 13:26:39 +00:00
}
/**
See if the process described by \ c proc matches the commandline \ c
cmd
*/
2006-01-30 19:53:10 +00:00
static int match_pid ( const wchar_t * cmd ,
2005-09-20 13:26:39 +00:00
const wchar_t * proc ,
2006-12-14 11:58:11 +00:00
int flags ,
int * offset )
2005-09-20 13:26:39 +00:00
{
/* Test for direct match */
2006-12-14 11:58:11 +00:00
2005-09-20 13:26:39 +00:00
if ( wcsncmp ( cmd , proc , wcslen ( proc ) ) = = 0 )
2006-12-14 11:58:11 +00:00
{
if ( offset )
* offset = 0 ;
2005-09-20 13:26:39 +00:00
return 1 ;
2006-12-14 11:58:11 +00:00
}
2006-01-30 19:53:10 +00:00
/*
2006-02-07 11:48:57 +00:00
Test if the commandline is a path to the command , if so we try
to match against only the command part
2005-09-20 13:26:39 +00:00
*/
wchar_t * first_token = tok_first ( cmd ) ;
if ( first_token = = 0 )
return 0 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
wchar_t * start = 0 ;
wchar_t prev = 0 ;
wchar_t * p ;
2006-01-30 19:53:10 +00:00
/*
2006-02-07 11:48:57 +00:00
This should be done by basename ( ) , if it wasn ' t for the fact
that is does not accept wide strings
2005-09-20 13:26:39 +00:00
*/
for ( p = first_token ; * p ; p + + )
{
if ( * p = = L ' / ' & & prev ! = L ' \\ ' )
start = p ;
prev = * p ;
}
if ( start )
{
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( wcsncmp ( start + 1 , proc , wcslen ( proc ) ) = = 0 )
{
2006-12-14 11:58:11 +00:00
if ( offset )
* offset = start + 1 - first_token ;
2005-09-20 13:26:39 +00:00
free ( first_token ) ;
2006-12-14 11:58:11 +00:00
2005-09-20 13:26:39 +00:00
return 1 ;
}
}
free ( first_token ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
return 0 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
/**
Searches for a job with the specified job id , or a job or process
which has the string \ c proc as a prefix of its commandline .
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 .
*/
2006-01-30 19:53:10 +00:00
static int find_process ( const wchar_t * proc ,
int flags ,
2005-09-20 13:26:39 +00:00
array_list_t * out )
{
DIR * dir ;
2006-05-21 21:46:01 +00:00
struct wdirent * next ;
wchar_t * pdir_name ;
wchar_t * pfile_name ;
2005-09-20 13:26:39 +00:00
wchar_t * cmd = 0 ;
int sz = 0 ;
int found = 0 ;
2006-01-30 19:53:10 +00:00
wchar_t * result ;
2005-09-20 13:26:39 +00:00
job_t * j ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( iswnumeric ( proc ) | | ( wcslen ( proc ) = = 0 ) )
{
/*
2006-01-30 19:53:10 +00:00
This is a numeric job string , like ' % 2 '
2005-09-20 13:26:39 +00:00
*/
if ( flags & ACCEPT_INCOMPLETE )
{
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-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( wcsncmp ( proc , jid , wcslen ( proc ) ) = = 0 )
{
2006-01-30 19:53:10 +00:00
al_push ( out ,
wcsdupcat2 ( jid + wcslen ( proc ) ,
2006-01-17 12:48:30 +00:00
COMPLETE_SEP_STR ,
2006-01-30 19:53:10 +00:00
COMPLETE_JOB_DESC_VAL ,
j - > command ,
2006-01-17 12:48:30 +00:00
( void * ) 0 ) ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
else
{
2006-01-30 19:53:10 +00:00
2007-01-09 03:20:05 +00:00
int jid ;
wchar_t * end ;
errno = 0 ;
jid = wcstol ( proc , & end , 10 ) ;
if ( jid > 0 & & ! errno & & ! * end )
2005-09-20 13:26:39 +00:00
{
2006-04-28 13:21:37 +00:00
j = job_get ( jid ) ;
if ( ( j ! = 0 ) & & ( j - > command ! = 0 ) )
2005-09-20 13:26:39 +00:00
{
2006-04-28 13:21:37 +00:00
{
result = malloc ( sizeof ( wchar_t ) * 16 ) ;
swprintf ( result , 16 , L " %d " , j - > pgid ) ;
al_push ( out , result ) ;
found = 1 ;
}
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
}
}
if ( found )
return 1 ;
for ( j = first_job ; j ! = 0 ; j = j - > next )
{
2006-12-14 11:58:11 +00:00
int offset ;
2005-09-20 13:26:39 +00:00
if ( j - > command = = 0 )
continue ;
2006-12-14 11:58:11 +00:00
if ( match_pid ( j - > command , proc , flags , & offset ) )
2005-09-20 13:26:39 +00:00
{
if ( flags & ACCEPT_INCOMPLETE )
{
2006-12-14 11:58:11 +00:00
wchar_t * res = wcsdupcat2 ( j - > command + offset + wcslen ( proc ) ,
2006-01-17 12:48:30 +00:00
COMPLETE_SEP_STR ,
COMPLETE_JOB_DESC ,
( void * ) 0 ) ;
2005-09-20 13:26:39 +00: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-12-14 11:58:11 +00:00
int offset ;
2005-09-20 13:26:39 +00:00
if ( p - > actual_cmd = = 0 )
continue ;
2006-01-30 19:53:10 +00:00
2006-12-14 11:58:11 +00:00
if ( match_pid ( p - > actual_cmd , proc , flags , & offset ) )
2005-09-20 13:26:39 +00:00
{
if ( flags & ACCEPT_INCOMPLETE )
{
2006-12-14 11:58:11 +00:00
wchar_t * res = wcsdupcat2 ( p - > actual_cmd + offset + wcslen ( proc ) ,
2006-02-07 11:48:57 +00:00
COMPLETE_SEP_STR ,
COMPLETE_CHILD_PROCESS_DESC ,
( void * ) 0 ) ;
2005-09-20 13:26:39 +00: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-30 19:53:10 +00:00
This system does not have a / proc filesystem .
2005-09-20 13:26:39 +00:00
*/
return 1 ;
}
2006-01-30 19:53:10 +00:00
2006-05-21 21:46:01 +00:00
pdir_name = malloc ( sizeof ( wchar_t ) * 256 ) ;
pfile_name = malloc ( sizeof ( wchar_t ) * 64 ) ;
wcscpy ( pdir_name , L " /proc/ " ) ;
while ( ( next = wreaddir ( dir ) ) ! = 0 )
2005-09-20 13:26:39 +00:00
{
2006-05-21 21:46:01 +00:00
wchar_t * name = next - > d_name ;
2005-09-20 13:26:39 +00:00
struct stat buf ;
2006-05-21 21:46:01 +00:00
if ( ! iswnumeric ( name ) )
2005-09-20 13:26:39 +00:00
continue ;
2006-05-21 21:46:01 +00:00
wcscpy ( pdir_name + 6 , name ) ;
if ( wstat ( pdir_name , & buf ) )
2005-09-20 13:26:39 +00:00
{
continue ;
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
if ( buf . st_uid ! = getuid ( ) )
{
continue ;
}
2006-05-21 21:46:01 +00:00
wcscpy ( pfile_name , pdir_name ) ;
wcscat ( pfile_name , L " /cmdline " ) ;
2006-01-30 19:53:10 +00:00
2006-05-21 21:46:01 +00:00
if ( ! wstat ( pfile_name , & buf ) )
2005-09-20 13:26:39 +00:00
{
/*
the ' cmdline ' file exists , it should contain the commandline
*/
FILE * cmdfile ;
2006-01-30 19:53:10 +00:00
2006-05-21 21:46:01 +00:00
if ( ( cmdfile = wfopen ( pfile_name , " r " ) ) = = 0 )
2005-09-20 13:26:39 +00:00
{
wperror ( L " fopen " ) ;
continue ;
}
2006-01-30 19:53:10 +00:00
2006-02-09 15:50:20 +00:00
signal_block ( ) ;
2006-01-30 19:53:10 +00:00
fgetws2 ( & cmd , & sz , cmdfile ) ;
2006-02-09 15:50:20 +00:00
signal_unblock ( ) ;
2005-09-20 13:26:39 +00:00
fclose ( cmdfile ) ;
}
else
{
# ifdef SunOS
2006-05-21 21:46:01 +00:00
wcscpy ( pfile_name , pdir_name ) ;
wcscat ( pfile_name , L " /psinfo " ) ;
if ( ! wstat ( pfile_name , & buf ) )
2005-09-20 13:26:39 +00:00
{
psinfo_t info ;
FILE * psfile ;
2006-01-30 19:53:10 +00:00
2006-05-21 21:46:01 +00:00
if ( ( psfile = wfopen ( pfile_name , " r " ) ) = = 0 )
2005-09-20 13:26:39 +00:00
{
wperror ( L " fopen " ) ;
continue ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( fread ( & info , sizeof ( info ) , 1 , psfile ) )
{
if ( cmd ! = 0 )
free ( cmd ) ;
cmd = str2wcs ( info . pr_fname ) ;
}
fclose ( psfile ) ;
}
else
# endif
{
if ( cmd ! = 0 )
{
* cmd = 0 ;
}
}
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( cmd ! = 0 )
{
2006-12-14 11:58:11 +00:00
int offset ;
if ( match_pid ( cmd , proc , flags , & offset ) )
2006-01-30 19:53:10 +00:00
{
2005-09-20 13:26:39 +00:00
if ( flags & ACCEPT_INCOMPLETE )
{
2006-12-14 11:58:11 +00:00
wchar_t * res = wcsdupcat2 ( cmd + offset + wcslen ( proc ) ,
2006-02-07 11:48:57 +00:00
COMPLETE_SEP_STR ,
2006-01-17 12:48:30 +00:00
COMPLETE_PROCESS_DESC ,
( void * ) 0 ) ;
2005-09-20 13:26:39 +00:00
if ( res )
al_push ( out , res ) ;
2006-12-14 11:58:11 +00:00
2005-09-20 13:26:39 +00:00
}
else
{
2006-05-21 21:46:01 +00:00
wchar_t * res = wcsdup ( name ) ;
2005-09-20 13:26:39 +00:00
if ( res )
2005-09-24 19:35:08 +00:00
al_push ( out , res ) ;
2005-09-20 13:26:39 +00:00
}
}
}
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( cmd ! = 0 )
free ( cmd ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
free ( pdir_name ) ;
free ( pfile_name ) ;
2006-01-30 19:53:10 +00:00
closedir ( dir ) ;
2005-09-20 13:26:39 +00:00
return 1 ;
}
/**
Process id expansion
*/
2006-01-30 19:53:10 +00:00
static int expand_pid ( wchar_t * in ,
2005-09-20 13:26:39 +00:00
int flags ,
array_list_t * out )
{
2006-06-20 00:50:10 +00:00
2006-07-21 01:08:31 +00:00
CHECK ( in , 0 ) ;
CHECK ( out , 0 ) ;
2005-09-20 13:26:39 +00:00
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 12:48:30 +00:00
wchar_t * res = wcsdupcat2 ( SELF_STR + wcslen ( in + 1 ) , COMPLETE_SEP_STR , COMPLETE_SELF_DESC , ( void * ) 0 ) ;
2006-01-30 19:53:10 +00:00
al_push ( out , res ) ;
}
2005-09-20 13:26:39 +00:00
else if ( wcsncmp ( in + 1 , LAST_STR , wcslen ( in + 1 ) ) = = 0 )
{
2006-01-17 12:48:30 +00:00
wchar_t * res = wcsdupcat2 ( LAST_STR + wcslen ( in + 1 ) , COMPLETE_SEP_STR , COMPLETE_LAST_DESC , ( void * ) 0 ) ;
2006-01-30 19:53:10 +00:00
al_push ( out , res ) ;
}
2005-09-20 13:26:39 +00: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-30 19:53:10 +00:00
return 1 ;
}
2005-09-20 13:26:39 +00: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-30 19:53:10 +00:00
return 1 ;
}
}
2005-09-20 13:26:39 +00:00
int prev = al_get_count ( out ) ;
if ( ! find_process ( in + 1 , flags , out ) )
return 0 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( prev = = al_get_count ( out ) )
{
if ( flags & ACCEPT_INCOMPLETE )
free ( in ) ;
else
{
2006-10-19 15:41:27 +00:00
free ( in ) ;
return 0 ;
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
else
{
free ( in ) ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
return 1 ;
}
2006-07-20 13:02:46 +00:00
void expand_variable_error ( const wchar_t * token , int token_pos , int error_pos )
{
int stop_pos = token_pos + 1 ;
switch ( token [ stop_pos ] )
{
case BRACKET_BEGIN :
{
2006-07-20 23:33:19 +00:00
wchar_t * cpy = wcsdup ( token ) ;
* ( cpy + token_pos ) = 0 ;
wchar_t * name = & cpy [ stop_pos + 1 ] ;
wchar_t * end = wcschr ( name , BRACKET_END ) ;
wchar_t * post ;
int is_var = 0 ;
if ( end )
{
post = end + 1 ;
* end = 0 ;
if ( ! wcsvarname ( name ) )
{
is_var = 1 ;
}
}
if ( is_var )
{
error ( SYNTAX_ERROR ,
error_pos ,
COMPLETE_VAR_BRACKET_DESC ,
cpy ,
name ,
post ) ;
}
else
{
error ( SYNTAX_ERROR ,
error_pos ,
COMPLETE_VAR_BRACKET_DESC ,
L " " ,
L " VARIABLE " ,
L " " ) ;
}
free ( cpy ) ;
2006-07-20 13:02:46 +00:00
break ;
}
case INTERNAL_SEPARATOR :
{
error ( SYNTAX_ERROR ,
error_pos ,
2006-07-20 23:33:19 +00:00
COMPLETE_VAR_PARAN_DESC ) ;
2006-07-20 13:02:46 +00:00
break ;
}
case 0 :
{
error ( SYNTAX_ERROR ,
error_pos ,
COMPLETE_VAR_NULL_DESC ) ;
break ;
}
default :
{
error ( SYNTAX_ERROR ,
error_pos ,
COMPLETE_VAR_DESC ,
token [ stop_pos ] ) ;
break ;
}
}
}
2006-08-24 13:39:04 +00:00
static int parse_slice ( wchar_t * in , wchar_t * * end_ptr , array_list_t * idx )
{
wchar_t * end ;
int pos = 1 ;
// debug( 0, L"parse_slice on '%ls'", in );
while ( 1 )
{
long tmp ;
while ( iswspace ( in [ pos ] ) | | ( in [ pos ] = = INTERNAL_SEPARATOR ) )
pos + + ;
if ( in [ pos ] = = L ' ] ' )
{
pos + + ;
break ;
}
errno = 0 ;
tmp = wcstol ( & in [ pos ] , & end , 10 ) ;
if ( ( errno ) | | ( end = = & in [ pos ] ) )
{
return 1 ;
}
// debug( 0, L"Push idx %d", tmp );
al_push_long ( idx , tmp ) ;
pos = end - in ;
}
if ( end_ptr )
{
// debug( 0, L"Remainder is '%ls', slice def was %d characters long", in+pos, pos );
* end_ptr = in + pos ;
}
// debug( 0, L"ok, done" );
return 0 ;
}
2006-02-12 19:03:01 +00:00
/**
Expand all environment variables in the string * ptr .
This function is slow , fragile and complicated . There are lots of
little corner cases , like $ $ foo should do a double expansion ,
$ foo $ bar should not double expand bar , etc . Also , it ' s easy to
accidentally leak memory on array out of bounds errors an various
other situations . All in all , this function should be rewritten ,
split out into multiple logical units and carefully tested . After
that , it can probably be optimized to do fewer memory allocations ,
fewer string scans and overall just less work . But until that
happens , don ' t edit it unless you know exactly what you are doing ,
and do proper testing afterwards .
*/
static int expand_variables ( wchar_t * in , array_list_t * out , int last_idx )
2006-02-12 11:29:30 +00:00
{
2006-02-12 19:03:01 +00: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-07-21 01:08:31 +00:00
CHECK ( in , 0 ) ;
CHECK ( out , 0 ) ;
2006-02-12 11:29:30 +00:00
if ( ! var_tmp )
{
var_tmp = sb_halloc ( global_context ) ;
if ( ! var_tmp )
2006-07-03 10:39:57 +00:00
DIE_MEM ( ) ;
2006-02-12 11:29:30 +00:00
}
else
{
sb_clear ( var_tmp ) ;
}
if ( ! var_idx_list )
{
var_idx_list = al_halloc ( global_context ) ;
if ( ! var_idx_list )
2006-07-03 10:39:57 +00:00
DIE_MEM ( ) ;
2006-02-12 11:29:30 +00:00
}
else
{
al_truncate ( var_idx_list , 0 ) ;
}
2006-01-30 19:53:10 +00:00
2006-02-12 11:29:30 +00:00
for ( i = last_idx ; ( i > = 0 ) & & is_ok & & ! empty ; i - - )
2005-09-20 13:26:39 +00:00
{
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_val ;
2006-01-30 19:53:10 +00:00
wchar_t * new_in ;
2005-11-02 16:49:13 +00:00
int is_single = ( c = = VARIABLE_EXPAND_SINGLE ) ;
2006-02-12 11:29:30 +00:00
int var_name_stop_pos ;
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 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
stop_pos + + ;
}
2006-02-12 11:29:30 +00:00
var_name_stop_pos = stop_pos ;
2005-09-20 13:26:39 +00:00
/* printf( "Stop for '%c'\n", in[stop_pos]);*/
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
var_len = stop_pos - start_pos ;
2005-12-07 13:03:30 +00:00
if ( var_len = = 0 )
{
2006-07-20 13:02:46 +00:00
expand_variable_error ( in , stop_pos - 1 , - 1 ) ;
2005-12-07 13:03:30 +00:00
is_ok = 0 ;
break ;
}
2006-01-30 19:53:10 +00:00
2006-02-12 11:29:30 +00:00
sb_append_substring ( var_tmp , & in [ start_pos ] , var_len ) ;
2006-01-30 19:53:10 +00:00
2006-02-12 11:29:30 +00:00
var_val = expand_var ( ( wchar_t * ) var_tmp - > buff ) ;
2006-01-30 19:53:10 +00:00
2006-02-12 11:29:30 +00:00
if ( var_val )
2005-11-02 16:49:13 +00:00
{
int all_vars = 1 ;
2006-02-12 11:29:30 +00:00
array_list_t var_item_list ;
al_init ( & var_item_list ) ;
2006-08-24 13:39:04 +00:00
2005-11-02 16:49:13 +00:00
if ( in [ stop_pos ] = = L ' [ ' )
2006-01-30 19:53:10 +00:00
{
2006-08-24 13:39:04 +00:00
wchar_t * slice_end ;
all_vars = 0 ;
if ( parse_slice ( & in [ stop_pos ] , & slice_end , var_idx_list ) )
2005-11-02 16:49:13 +00:00
{
2006-08-24 13:39:04 +00:00
error ( SYNTAX_ERROR ,
- 1 ,
L " Invalid index value " ) ;
is_ok = 0 ;
}
stop_pos = ( slice_end - in ) ;
}
2005-11-02 16:49:13 +00:00
if ( is_ok )
2006-01-30 19:53:10 +00:00
{
2006-05-29 11:13:42 +00:00
tokenize_variable_array ( var_val , & var_item_list ) ;
2005-11-02 16:49:13 +00:00
if ( ! all_vars )
2006-01-30 19:53:10 +00:00
{
2005-11-02 16:49:13 +00:00
int j ;
2006-02-12 11:29:30 +00:00
for ( j = 0 ; j < al_get_count ( var_idx_list ) ; j + + )
2005-11-02 16:49:13 +00:00
{
2006-07-31 16:55:11 +00:00
long tmp = al_get_long ( var_idx_list , j ) ;
2006-06-04 09:35:32 +00:00
if ( tmp < 0 )
{
tmp = al_get_count ( & var_item_list ) + tmp + 1 ;
}
2006-07-31 16:48:16 +00:00
/*
Check that we are within array
bounds . If not , truncate the list to
exit .
*/
2006-02-12 11:29:30 +00:00
if ( tmp < 1 | | tmp > al_get_count ( & var_item_list ) )
2005-09-20 13:26:39 +00:00
{
2006-01-30 19:53:10 +00:00
error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
- 1 ,
2006-06-04 20:14:51 +00:00
ARRAY_BOUNDS_ERR ) ;
2005-11-02 16:49:13 +00:00
is_ok = 0 ;
2006-02-12 11:29:30 +00:00
al_truncate ( var_idx_list , j ) ;
2005-11-02 16:49:13 +00:00
break ;
2006-01-30 19:53:10 +00:00
}
2005-11-02 16:49:13 +00:00
else
{
2006-07-31 16:48:16 +00:00
/* Replace each index in var_idx_list inplace with the string value at the specified index */
al_set ( var_idx_list , j , wcsdup ( al_get ( & var_item_list , tmp - 1 ) ) ) ;
2005-09-20 13:26:39 +00:00
}
}
2006-07-31 16:48:16 +00:00
/* Free strings in list var_item_list and truncate it */
2006-06-12 21:47:42 +00:00
al_foreach ( & var_item_list , & free ) ;
2006-02-12 11:29:30 +00:00
al_truncate ( & var_item_list , 0 ) ;
2006-01-30 19:53:10 +00:00
/* Add items from list idx back to list l */
2006-02-12 11:29:30 +00:00
al_push_all ( & var_item_list , var_idx_list ) ;
2005-11-02 16:49:13 +00:00
}
2006-01-30 19:53:10 +00:00
}
2006-02-07 11:48:57 +00:00
if ( is_ok )
2005-11-02 16:49:13 +00:00
{
2006-02-07 11:48:57 +00:00
if ( is_single )
{
string_buffer_t res ;
in [ i ] = 0 ;
2006-02-09 15:50:20 +00:00
2006-02-12 11:29:30 +00:00
sb_init ( & res ) ;
2006-02-07 11:48:57 +00:00
sb_append ( & res , in ) ;
sb_append_char ( & res , INTERNAL_SEPARATOR ) ;
2006-01-30 19:53:10 +00:00
2006-02-12 11:29:30 +00:00
for ( j = 0 ; j < al_get_count ( & var_item_list ) ; j + + )
2006-01-30 19:53:10 +00:00
{
2006-02-12 11:29:30 +00:00
wchar_t * next = ( wchar_t * ) al_get ( & var_item_list , j ) ;
2006-02-07 11:48:57 +00:00
if ( is_ok )
{
if ( j ! = 0 )
sb_append ( & res , L " " ) ;
sb_append ( & res , next ) ;
}
free ( next ) ;
2005-11-02 16:49:13 +00:00
}
2006-02-07 11:48:57 +00:00
sb_append ( & res , & in [ stop_pos ] ) ;
2006-02-12 11:29:30 +00:00
is_ok & = expand_variables ( ( wchar_t * ) res . buff , out , i ) ;
2006-01-30 19:53:10 +00:00
}
2006-02-07 11:48:57 +00:00
else
2005-09-20 13:26:39 +00:00
{
2006-02-12 11:29:30 +00:00
for ( j = 0 ; j < al_get_count ( & var_item_list ) ; j + + )
2005-09-20 13:26:39 +00:00
{
2006-02-12 11:29:30 +00:00
wchar_t * next = ( wchar_t * ) al_get ( & var_item_list , j ) ;
if ( is_ok & & ( i = = 0 ) & & ( ! in [ stop_pos ] ) )
2005-09-20 13:26:39 +00:00
{
2006-02-12 11:29:30 +00:00
al_push ( out , next ) ;
}
else
{
if ( is_ok )
2006-02-07 11:48:57 +00:00
{
2006-02-12 11:29:30 +00: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 11:48:57 +00:00
{
2006-07-03 10:39:57 +00:00
DIE_MEM ( ) ;
2006-02-07 11:48:57 +00:00
}
else
2006-02-12 11:29:30 +00:00
{
2006-04-13 12:18:51 +00:00
wcslcpy ( new_in , in , start_pos ) ;
2006-02-12 11:29:30 +00:00
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 ] ) ;
is_ok & = expand_variables ( new_in , out , i ) ;
}
2006-02-07 11:48:57 +00:00
}
2006-02-12 11:29:30 +00:00
free ( next ) ;
2005-09-20 13:26:39 +00:00
}
2006-02-12 11:29:30 +00:00
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
}
2006-02-07 11:48:57 +00:00
2005-11-02 16:49:13 +00:00
free ( in ) ;
2006-02-12 11:29:30 +00:00
al_destroy ( & var_item_list ) ;
2006-01-30 19:53:10 +00:00
return is_ok ;
2005-11-02 16:49:13 +00:00
}
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
{
/*
2006-01-30 19:53:10 +00:00
Expansion to single argument .
2005-11-02 16:49:13 +00:00
*/
string_buffer_t res ;
sb_init ( & res ) ;
2006-01-30 19:53:10 +00:00
2005-11-02 16:49:13 +00:00
in [ i ] = 0 ;
2006-01-30 19:53:10 +00:00
2005-11-02 16:49:13 +00:00
sb_append ( & res , in ) ;
sb_append ( & res , & in [ stop_pos ] ) ;
2006-02-12 11:29:30 +00:00
is_ok & = expand_variables ( ( wchar_t * ) res . buff , out , i ) ;
2005-11-02 16:49:13 +00:00
free ( in ) ;
return is_ok ;
}
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
prev_char = c ;
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( ! empty )
{
al_push ( out , in ) ;
}
else
{
free ( in ) ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00: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-30 19:53:10 +00:00
int bracket_count = 0 ;
2005-09-20 13:26:39 +00:00
wchar_t * bracket_begin = 0 , * bracket_end = 0 ;
wchar_t * last_sep = 0 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
wchar_t * item_begin ;
int len1 , len2 , tot_len ;
2006-06-20 00:50:10 +00:00
2006-07-21 01:08:31 +00:00
CHECK ( in , 0 ) ;
CHECK ( out , 0 ) ;
2006-01-30 19:53:10 +00:00
for ( pos = in ;
2006-06-17 14:26:52 +00:00
( * pos ) & & ! syntax_error ;
2005-09-20 13:26:39 +00:00
pos + + )
{
switch ( * pos )
{
case BRACKET_BEGIN :
{
2006-06-17 14:26:52 +00:00
bracket_begin = pos ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
bracket_count + + ;
break ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
case BRACKET_END :
{
bracket_count - - ;
2006-06-17 14:26:52 +00:00
if ( bracket_end < bracket_begin )
{
2005-09-20 13:26:39 +00:00
bracket_end = pos ;
2006-06-17 14:26:52 +00:00
}
2005-09-20 13:26:39 +00:00
if ( bracket_count < 0 )
{
syntax_error = 1 ;
}
break ;
}
case BRACKET_SEP :
{
if ( bracket_count = = 1 )
last_sep = pos ;
}
}
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( bracket_count > 0 )
{
if ( ! ( flags & ACCEPT_INCOMPLETE ) )
2006-06-17 14:26:52 +00:00
{
2005-09-20 13:26:39 +00:00
syntax_error = 1 ;
2006-06-17 14:26:52 +00:00
}
2005-09-20 13:26:39 +00:00
else
{
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00: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 ) ;
}
return expand_brackets ( ( wchar_t * ) mod . buff , 1 , out ) ;
}
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( syntax_error )
{
2006-01-30 19:53:10 +00:00
error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
- 1 ,
2006-02-14 11:48:04 +00:00
_ ( L " Mismatched brackets " ) ) ;
2005-09-20 13:26:39 +00:00
return 0 ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( bracket_begin = = 0 )
{
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-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
whole_item = malloc ( sizeof ( wchar_t ) * ( tot_len + item_len + 1 ) ) ;
2006-04-13 12:18:51 +00:00
wcslcpy ( whole_item , in , len1 + 1 ) ;
wcslcpy ( whole_item + len1 , item_begin , item_len + 1 ) ;
2005-09-20 13:26:39 +00:00
wcscpy ( whole_item + len1 + item_len , bracket_end + 1 ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
expand_brackets ( whole_item , flags , out ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
item_begin = pos + 1 ;
if ( pos = = bracket_end )
break ;
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( * pos = = BRACKET_BEGIN )
{
bracket_count + + ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( * pos = = BRACKET_END )
{
bracket_count - - ;
}
}
2006-01-30 19:53:10 +00:00
free ( in ) ;
return 1 ;
2005-09-20 13:26:39 +00:00
}
/**
2006-08-22 14:38:31 +00:00
Perform cmdsubst expansion
2005-09-20 13:26:39 +00:00
*/
2006-08-22 14:38:31 +00:00
static int expand_cmdsubst ( wchar_t * in , array_list_t * out )
2005-09-20 13:26:39 +00:00
{
2006-06-14 13:22:40 +00:00
wchar_t * paran_begin = 0 , * paran_end = 0 ;
2006-08-24 13:39:04 +00:00
int len1 ;
2005-09-20 13:26:39 +00:00
wchar_t prev = 0 ;
wchar_t * subcmd ;
2006-08-24 13:39:04 +00:00
array_list_t * sub_res , * tail_expand ;
2005-09-20 13:26:39 +00:00
int i , j ;
2006-01-30 16:51:50 +00:00
const wchar_t * item_begin ;
2006-08-24 13:39:04 +00:00
wchar_t * tail_begin = 0 ;
void * context ;
2005-09-20 13:26:39 +00:00
2006-07-21 01:08:31 +00:00
CHECK ( in , 0 ) ;
CHECK ( out , 0 ) ;
2006-08-24 13:39:04 +00:00
2006-01-30 19:53:10 +00:00
switch ( parse_util_locate_cmdsubst ( in ,
2006-01-30 16:51:50 +00:00
& paran_begin ,
& paran_end ,
0 ) )
2005-09-20 13:26:39 +00:00
{
case - 1 :
2006-01-30 19:53:10 +00:00
error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
- 1 ,
L " Mismatched parans " ) ;
2005-09-20 13:26:39 +00:00
return 0 ;
case 0 :
al_push ( out , in ) ;
2006-01-30 19:53:10 +00:00
return 1 ;
2005-09-20 13:26:39 +00:00
case 1 :
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
break ;
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
2006-08-24 13:39:04 +00:00
context = halloc ( 0 , 0 ) ;
2005-09-20 13:26:39 +00:00
len1 = ( paran_begin - in ) ;
prev = 0 ;
item_begin = paran_begin + 1 ;
2006-08-24 13:39:04 +00:00
sub_res = al_halloc ( context ) ;
if ( ! ( subcmd = halloc ( context , sizeof ( wchar_t ) * ( paran_end - paran_begin ) ) ) )
2005-09-20 13:26:39 +00:00
{
2006-08-24 13:39:04 +00:00
halloc_free ( context ) ;
2006-01-30 19:53:10 +00:00
return 0 ;
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
2006-04-13 12:18:51 +00:00
wcslcpy ( subcmd , paran_begin + 1 , paran_end - paran_begin ) ;
2006-01-30 19:53:10 +00:00
subcmd [ paran_end - paran_begin - 1 ] = 0 ;
2006-10-19 15:36:03 +00:00
if ( exec_subshell ( subcmd , sub_res ) = = - 1 )
{
halloc_free ( context ) ;
error ( CMDSUBST_ERROR , - 1 , L " Unknown error while evaulating command substitution " ) ;
return 0 ;
}
2006-08-24 13:39:04 +00:00
tail_begin = paran_end + 1 ;
if ( * tail_begin = = L ' [ ' )
{
array_list_t * slice_idx = al_halloc ( context ) ;
wchar_t * slice_end ;
if ( parse_slice ( tail_begin , & slice_end , slice_idx ) )
{
halloc_free ( context ) ;
error ( SYNTAX_ERROR , - 1 , L " Invalid index value " ) ;
return 0 ;
}
else
{
array_list_t * sub_res2 = al_halloc ( context ) ;
tail_begin = slice_end ;
for ( i = 0 ; i < al_get_count ( slice_idx ) ; i + + )
{
long idx = al_get_long ( slice_idx , i ) ;
if ( idx < 0 )
{
idx = al_get_count ( sub_res ) + idx + 1 ;
}
if ( idx < 1 | | idx > al_get_count ( sub_res ) )
{
halloc_free ( context ) ;
error ( SYNTAX_ERROR , - 1 , L " Invalid index value " ) ;
return 0 ;
}
idx = idx - 1 ;
al_push ( sub_res2 , al_get ( sub_res , idx ) ) ;
// debug( 0, L"Pushing item '%ls' with index %d onto sliced result", al_get( sub_res, idx ), idx );
al_set ( sub_res , idx , 0 ) ;
}
al_foreach ( sub_res , & free ) ;
sub_res = sub_res2 ;
2006-09-08 14:12:41 +00:00
}
2006-08-24 13:39:04 +00:00
}
tail_expand = al_halloc ( context ) ;
2006-01-30 19:53:10 +00:00
2006-08-24 13:39:04 +00:00
/*
Recursively call ourselves to expand any remaining command
2006-09-08 14:12:41 +00:00
substitutions . The result of this recursive call usiung the tail
2006-08-24 13:39:04 +00:00
of the string is inserted into the tail_expand array list
*/
expand_cmdsubst ( wcsdup ( tail_begin ) , tail_expand ) ;
2006-01-30 19:53:10 +00:00
2006-08-24 13:39:04 +00:00
/*
Combine the result of the current command substitution with the
2006-09-08 14:12:41 +00:00
result of the recursive tail expansion
2006-08-24 13:39:04 +00:00
*/
for ( i = 0 ; i < al_get_count ( sub_res ) ; i + + )
2005-09-20 13:26:39 +00:00
{
2005-10-07 10:36:51 +00:00
wchar_t * sub_item , * sub_item2 ;
2006-08-24 13:39:04 +00:00
sub_item = ( wchar_t * ) al_get ( sub_res , i ) ;
2006-02-06 15:15:52 +00:00
sub_item2 = escape ( sub_item , 1 ) ;
2006-01-30 19:53:10 +00:00
free ( sub_item ) ;
2005-10-07 10:36:51 +00:00
int item_len = wcslen ( sub_item2 ) ;
2006-01-30 19:53:10 +00:00
2006-08-24 13:39:04 +00:00
for ( j = 0 ; j < al_get_count ( tail_expand ) ; j + + )
2005-09-20 13:26:39 +00:00
{
string_buffer_t whole_item ;
2006-08-24 13:39:04 +00:00
wchar_t * tail_item = ( wchar_t * ) al_get ( tail_expand , j ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
sb_init ( & whole_item ) ;
2006-01-30 19:53:10 +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 ) ;
2006-01-30 19:53:10 +00:00
sb_append ( & whole_item , tail_item ) ;
2006-02-22 15:41:52 +00:00
al_push ( out , whole_item . buff ) ;
2005-09-20 13:26:39 +00:00
}
2006-02-22 15:41:52 +00:00
2005-10-07 10:36:51 +00:00
free ( sub_item2 ) ;
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
free ( in ) ;
2006-01-30 19:53:10 +00:00
2006-08-24 13:39:04 +00:00
al_foreach ( tail_expand , & free ) ;
halloc_free ( context ) ;
2006-01-30 19:53:10 +00:00
return 1 ;
2005-09-20 13:26:39 +00:00
}
2006-06-20 00:50:10 +00:00
/**
Wrapper around unescape funtion . Issues an error ( ) on failiure .
*/
2006-02-06 15:15:52 +00:00
static 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
{
2006-01-30 19:53:10 +00:00
2006-07-21 01:08:31 +00:00
CHECK ( in , 0 ) ;
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 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( in [ 1 ] = = ' / ' | | in [ 1 ] = = ' \0 ' )
{
/* Current users home directory */
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
home = env_get ( L " HOME " ) ;
if ( home )
home = wcsdup ( home ) ;
else
home = wcsdup ( L " " ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( ! home )
{
* in = L ' ~ ' ;
tilde_error = 1 ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00: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-30 19:53:10 +00:00
{
2005-09-20 13:26:39 +00:00
name_end = in + wcslen ( in ) ;
}
name = wcsndup ( in + 1 , name_end - in - 1 ) ;
old_in = name_end ;
2006-01-30 19:53:10 +00:00
char * name_str = wcs2str ( name ) ;
struct passwd * userinfo =
2005-09-20 13:26:39 +00:00
getpwnam ( name_str ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( userinfo = = 0 )
{
tilde_error = 1 ;
* in = L ' ~ ' ;
}
else
{
2006-01-30 19:53:10 +00:00
home = str2wcs ( userinfo - > pw_dir ) ;
2005-09-20 13:26:39 +00:00
if ( ! home )
{
* in = L ' ~ ' ;
tilde_error = 1 ;
}
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
free ( name_str ) ;
free ( name ) ;
}
2006-01-30 19:53:10 +00:00
2005-11-29 16:52:02 +00:00
if ( ! tilde_error & & home & & old_in )
2005-09-20 13:26:39 +00:00
{
2006-01-30 19:53:10 +00:00
new_in = wcsdupcat ( home , old_in ) ;
2005-11-29 16:52:02 +00:00
}
free ( home ) ;
free ( in ) ;
2006-01-30 19:53:10 +00:00
return new_in ;
2005-09-20 13:26:39 +00:00
}
2005-11-29 16:52:02 +00:00
return in ;
2006-01-30 19:53:10 +00:00
}
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
{
2006-06-21 00:48:36 +00:00
CHECK ( in , 0 ) ;
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
}
2006-01-30 19:53:10 +00:00
return in ;
2005-09-20 13:26:39 +00:00
}
/**
Remove any internal separators . Also optionally convert wildcard characters to
2006-07-21 01:08:31 +00:00
regular equivalents . This is done to support EXPAND_SKIP_WILDCARDS .
2005-09-20 13:26:39 +00:00
*/
static void remove_internal_separator ( const void * s , int conv )
{
wchar_t * in = ( wchar_t * ) s ;
wchar_t * out = in ;
2006-07-21 01:08:31 +00:00
CHECK ( s , ) ;
2005-09-20 13:26:39 +00:00
while ( * in )
{
switch ( * in )
{
case INTERNAL_SEPARATOR :
in + + ;
break ;
2006-02-12 11:29:30 +00:00
2005-09-20 13:26:39 +00:00
case ANY_CHAR :
in + + ;
* out + + = conv ? L ' ? ' : ANY_CHAR ;
break ;
case ANY_STRING :
in + + ;
* out + + = conv ? L ' * ' : ANY_STRING ;
break ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
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
*/
2006-02-09 15:50:20 +00:00
int expand_string ( void * context ,
wchar_t * str ,
2006-01-30 19:53:10 +00:00
array_list_t * end_out ,
2005-09-20 13:26:39 +00:00
int flags )
{
array_list_t list1 , list2 ;
array_list_t * in , * out ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
int i ;
2006-08-22 14:38:31 +00:00
int cmdsubst_ok = 1 ;
2005-12-03 16:43:56 +00:00
int res = EXPAND_OK ;
2006-02-09 15:50:20 +00:00
int start_count = al_get_count ( end_out ) ;
2006-01-30 19:53:10 +00:00
2006-06-21 00:48:36 +00:00
CHECK ( str , EXPAND_ERROR ) ;
CHECK ( end_out , EXPAND_ERROR ) ;
2006-05-21 22:16:04 +00:00
if ( ( ! ( flags & ACCEPT_INCOMPLETE ) ) & & expand_is_clean ( str ) )
2005-10-26 10:51:02 +00:00
{
2006-02-09 15:50:20 +00:00
halloc_register ( context , str ) ;
2005-10-26 10:51:02 +00:00
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 ) ;
2006-08-22 14:38:31 +00:00
if ( EXPAND_SKIP_CMDSUBST & flags )
2005-09-20 13:26:39 +00:00
{
2006-06-14 13:22:40 +00:00
wchar_t * begin , * end ;
2006-02-22 15:41:52 +00:00
if ( parse_util_locate_cmdsubst ( str ,
& begin ,
& end ,
1 ) ! = 0 )
2005-09-20 13:26:39 +00:00
{
2006-08-22 14:38:31 +00:00
error ( CMDSUBST_ERROR , - 1 , L " Command substitutions not allowed " ) ;
2006-02-22 15:41:52 +00:00
free ( str ) ;
al_destroy ( & list1 ) ;
al_destroy ( & list2 ) ;
return EXPAND_ERROR ;
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
al_push ( & list1 , str ) ;
}
else
{
2006-08-22 14:38:31 +00:00
cmdsubst_ok = expand_cmdsubst ( str , & list1 ) ;
2005-09-20 13:26:39 +00:00
}
2006-08-22 14:38:31 +00:00
if ( ! cmdsubst_ok )
2005-09-20 13:26:39 +00:00
{
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 ;
2006-01-30 19:53:10 +00:00
2007-01-18 16:27:00 +00:00
/*
We accept incomplete strings here , since complete uses
expand_string to expand incomplete strings from the
commandline .
*/
int unescape_flags = UNESCAPE_SPECIAL | UNESCAPE_INCOMPLETE ;
next = expand_unescape ( ( wchar_t * ) al_get ( in , i ) , unescape_flags ) ;
2005-10-07 10:36:51 +00:00
free ( ( void * ) al_get ( in , i ) ) ;
2006-01-30 19:53:10 +00:00
2005-10-07 10:36:51 +00:00
if ( ! next )
2006-02-22 15:41:52 +00:00
{
debug ( 2 , L " Got null string on line %d of file %s " , __LINE__ , __FILE__ ) ;
continue ;
}
2006-01-30 19:53:10 +00:00
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
{
2006-02-12 11:29:30 +00:00
if ( ! expand_variables ( next , out , wcslen ( next ) - 1 ) )
2005-09-20 13:26:39 +00:00
{
al_destroy ( in ) ;
al_destroy ( out ) ;
2006-01-30 19:53:10 +00:00
return EXPAND_ERROR ;
2005-09-20 13:26:39 +00:00
}
}
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
al_truncate ( in , 0 ) ;
in = & list2 ;
out = & list1 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
for ( i = 0 ; i < al_get_count ( in ) ; i + + )
{
wchar_t * next = ( wchar_t * ) al_get ( in , i ) ;
2006-02-22 15:41:52 +00:00
if ( ! next )
{
debug ( 2 , L " Got null string on line %d of file %s " , __LINE__ , __FILE__ ) ;
continue ;
}
2005-09-20 13:26:39 +00:00
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 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
for ( i = 0 ; i < al_get_count ( in ) ; i + + )
{
2006-01-30 19:53:10 +00:00
wchar_t * next = ( wchar_t * ) al_get ( in , i ) ;
2006-02-22 15:41:52 +00:00
if ( ! next )
{
debug ( 2 , L " Got null string on line %d of file %s " , __LINE__ , __FILE__ ) ;
continue ;
}
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
}
2006-01-30 19:53:10 +00:00
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 ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
in = & list2 ;
2006-01-30 19:53:10 +00:00
out = & list1 ;
2005-09-20 13:26:39 +00:00
for ( i = 0 ; i < al_get_count ( in ) ; i + + )
{
wchar_t * next = ( wchar_t * ) al_get ( in , i ) ;
int wc_res ;
2006-02-22 15:41:52 +00:00
if ( ! next )
{
debug ( 2 , L " Got null string on line %d of file %s " , __LINE__ , __FILE__ ) ;
continue ;
}
2006-01-30 19:53:10 +00:00
remove_internal_separator ( next , EXPAND_SKIP_WILDCARDS & flags ) ;
2006-02-12 11:29:30 +00:00
2006-01-30 19:53:10 +00:00
if ( ( ( flags & ACCEPT_INCOMPLETE ) & & ( ! ( flags & EXPAND_SKIP_WILDCARDS ) ) ) | |
2005-09-20 13:26:39 +00:00
wildcard_has ( next , 1 ) )
{
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00: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 :
2006-02-22 15:41:52 +00:00
{
2005-09-20 13:26:39 +00:00
if ( ! ( flags & ACCEPT_INCOMPLETE ) )
2006-01-30 19:53:10 +00:00
{
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
}
2006-02-22 15:41:52 +00:00
}
2005-09-20 13:26:39 +00:00
case 1 :
2006-02-22 15:41:52 +00:00
{
int j ;
2005-12-03 16:43:56 +00:00
res = EXPAND_WILDCARD_MATCH ;
2005-09-20 13:26:39 +00:00
sort_list ( out ) ;
2006-02-22 15:41:52 +00:00
for ( j = 0 ; j < al_get_count ( out ) ; j + + )
{
wchar_t * next = ( wchar_t * ) al_get ( out , j ) ;
if ( ! next )
{
debug ( 2 , L " Got null string on line %d of file %s " , __LINE__ , __FILE__ ) ;
continue ;
}
al_push ( end_out , next ) ;
}
2006-01-30 19:53:10 +00:00
al_truncate ( out , 0 ) ;
2005-09-20 13:26:39 +00:00
break ;
2006-02-22 15:41:52 +00:00
}
2007-01-09 16:47:05 +00:00
case - 1 :
{
al_foreach ( out , & free ) ;
al_destroy ( in ) ;
al_destroy ( out ) ;
return EXPAND_ERROR ;
}
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
}
else
{
if ( flags & ACCEPT_INCOMPLETE )
2006-02-22 15:41:52 +00:00
{
2005-09-20 13:26:39 +00:00
free ( next ) ;
2006-02-22 15:41:52 +00:00
}
2005-09-20 13:26:39 +00:00
else
2006-02-22 15:41:52 +00:00
{
2005-09-20 13:26:39 +00:00
al_push ( end_out , next ) ;
2006-02-22 15:41:52 +00:00
}
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
}
al_destroy ( in ) ;
al_destroy ( out ) ;
}
2006-02-09 15:50:20 +00:00
if ( context )
{
for ( i = start_count ; i < al_get_count ( end_out ) ; i + + )
{
2006-02-10 00:35:39 +00:00
halloc_register ( context , ( void * ) al_get ( end_out , i ) ) ;
2006-02-09 15:50:20 +00:00
}
}
2005-12-03 16:43:56 +00:00
return res ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
2006-02-09 15:50:20 +00:00
wchar_t * expand_one ( void * context , wchar_t * string , int flags )
2005-09-20 13:26:39 +00:00
{
array_list_t l ;
int res ;
wchar_t * one ;
2006-06-21 00:48:36 +00:00
CHECK ( string , 0 ) ;
2006-05-21 22:16:04 +00:00
if ( ( ! ( flags & ACCEPT_INCOMPLETE ) ) & & expand_is_clean ( string ) )
2006-02-09 15:50:20 +00:00
{
halloc_register ( context , string ) ;
2006-01-30 19:53:10 +00:00
return string ;
2006-02-09 15:50:20 +00:00
}
2005-09-20 13:26:39 +00:00
al_init ( & l ) ;
2006-02-09 15:50:20 +00:00
res = expand_string ( 0 , string , & l , flags ) ;
2005-09-20 13:26:39 +00: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 10:51:02 +00:00
2006-06-12 21:47:42 +00:00
al_foreach ( & l , & free ) ;
2005-09-20 13:26:39 +00:00
al_destroy ( & l ) ;
2006-02-09 15:50:20 +00:00
2006-02-10 00:35:39 +00:00
halloc_register ( context , one ) ;
2005-09-20 13:26:39 +00:00
return one ;
}