Ejemplo n.º 1
0
void tokenize_variable_array( const wchar_t *val, array_list_t *out )
{
	if( val )
	{
		wchar_t *cpy = wcsdup( val );
		wchar_t *pos, *start;
		wchar_t *next;

		if( !cpy )
		{
			DIE_MEM();
		}

		for( start=pos=cpy; *pos; pos++ )
		{
			if( *pos == ARRAY_SEP )
			{

				*pos=0;
				next = start==cpy?cpy:wcsdup(start);
				if( !next )
					DIE_MEM();
				al_push( out, next );
				start=pos+1;
			}
		}
		next = start==cpy?cpy:wcsdup(start);
		if( !next )
			DIE_MEM();
		al_push( out, next );
	}
}
Ejemplo n.º 2
0
char *wcs2str( const wchar_t *in )
{
    if (! in)
        return NULL;
	char *out;
    size_t desired_size = MAX_UTF8_BYTES*wcslen(in)+1;
    char local_buff[512];
    if (desired_size <= sizeof local_buff / sizeof *local_buff) {
        // convert into local buff, then use strdup() so we don't waste malloc'd space
        char *result = wcs2str_internal(in, local_buff);
        if (result) {
            // It converted into the local buffer, so copy it
            result = strdup(result);
            if (! result) {
                DIE_MEM();
            }
        }
        return result;
        
    } else {
        // here we fall into the bad case of allocating a buffer probably much larger than necessary
        out = (char *)malloc( MAX_UTF8_BYTES*wcslen(in)+1 );
        if (!out) {
            DIE_MEM();
        }
        return wcs2str_internal( in, out );
    }

	return wcs2str_internal( in, out );
}
Ejemplo n.º 3
0
    compiled_regex_t(const wchar_t *argv0, const wchar_t *pattern, bool ignore_case,
                     io_streams_t &streams)
        : code(0), match(0) {
        // Disable some sequences that can lead to security problems.
        uint32_t options = PCRE2_NEVER_UTF;
#if PCRE2_CODE_UNIT_WIDTH < 32
        options |= PCRE2_NEVER_BACKSLASH_C;
#endif

        int err_code = 0;
        PCRE2_SIZE err_offset = 0;

        code =
            pcre2_compile(PCRE2_SPTR(pattern), PCRE2_ZERO_TERMINATED,
                          options | (ignore_case ? PCRE2_CASELESS : 0), &err_code, &err_offset, 0);
        if (code == 0) {
            string_error(streams, _(L"%ls: Regular expression compile error: %ls\n"), argv0,
                         pcre2_strerror(err_code).c_str());
            string_error(streams, L"%ls: %ls\n", argv0, pattern);
            string_error(streams, L"%ls: %*ls\n", argv0, err_offset, L"^");
            return;
        }

        match = pcre2_match_data_create_from_pattern(code, 0);
        if (match == 0) {
            DIE_MEM();
        }
    }
Ejemplo n.º 4
0
/**
   Locate the specified entry. Create it if it doesn't exist.
*/
static complete_entry_t *complete_get_exact_entry( const wchar_t *cmd,
												   int cmd_type )
{
	complete_entry_t *c;

	complete_init();

	c = complete_find_exact_entry( cmd, cmd_type );

	if( c == 0 )
	{
		if( !(c = malloc( sizeof(complete_entry_t) )))
		{
			DIE_MEM();
		}

		c->next = first_entry;
		first_entry = c;

		c->first_option = 0;

		c->cmd = intern( cmd );
		c->cmd_type = cmd_type;
		c->short_opt_str = wcsdup(L"");
		c->authoritative = 1;
	}

	return c;
}
Ejemplo n.º 5
0
/**
   Remove backslashes from all newlines. This makes a string from the
   history file better formated for on screen display. The memory for
   the return value will be reused by subsequent calls to this
   function.
*/
static wchar_t *history_unescape_newlines( wchar_t *in )
{
    static string_buffer_t *out = 0;
    if( !out )
    {
        out = sb_halloc( global_context );
        if( !out )
        {
            DIE_MEM();
        }
    }
    else
    {
        sb_clear( out );
    }
    for( ; *in; in++ )
    {
        if( *in == L'\\' )
        {
            if( *(in+1)!= L'\n')
            {
                sb_append_char( out, *in );
            }
        }
        else
        {
            sb_append_char( out, *in );
        }
    }
    return (wchar_t *)out->buff;
}
Ejemplo n.º 6
0
wchar_t *wcsdupcat_internal( const wchar_t *a, ... )
{
	int len=wcslen(a);
	int pos;
	va_list va, va2;
	wchar_t *arg;

	va_start( va, a );
	va_copy( va2, va );
	while( (arg=va_arg(va, wchar_t *) )!= 0 )
	{
		len += wcslen( arg );
	}
	va_end( va );

	wchar_t *res = malloc( sizeof(wchar_t)*(len +1 ));
	if( res == 0 )
	{
		DIE_MEM();
	}

	wcscpy( res, a );
	pos = wcslen(a);
	while( (arg=va_arg(va2, wchar_t *) )!= 0 )
	{
		wcscpy( res+pos, arg );
		pos += wcslen(arg);
	}
	va_end( va2 );
	return res;

}
Ejemplo n.º 7
0
int fgetws2( wchar_t **b, int *len, FILE *f )
{
	int i=0;
	wint_t c;
	
	wchar_t *buff = *b;

	while( 1 )
	{
		/* Reallocate the buffer if necessary */
		if( i+1 >= *len )
		{
			int new_len = maxi( 128, (*len)*2);
			buff = (wchar_t *)realloc( buff, sizeof(wchar_t)*new_len );
			if( buff == 0 )
			{
				DIE_MEM();
			}
			else
			{
				*len = new_len;
				*b = buff;
			}			
		}
		
		errno=0;		
		
		c = getwc( f );
		
		if( errno == EILSEQ )
		{
			continue;
		}
	
//fwprintf( stderr, L"b\n" );
		
		switch( c )
		{
			/* End of line */ 
			case WEOF:
			case L'\n':
			case L'\0':
				buff[i]=L'\0';
				return i;				
				/* Ignore carriage returns */
			case L'\r':
				break;
				
			default:
				buff[i++]=c;
				break;
		}		


	}

}
Ejemplo n.º 8
0
void *safe_realloc_internal(void *ptr, size_t size, char *file, int line)
{
  void *ret = realloc(ptr, size);
  if(!ret)
    DIE_MEM();

  update_entry(ptr, ret, size);
  return ret;
}
Ejemplo n.º 9
0
void *safe_malloc_internal(size_t size, char *file, int line)
{
  void *ret = malloc(size);
  if(!ret)
    DIE_MEM();
  memset(ret, 0, size);

  add_entry(file, line, ret, size);
  return ret;
}
Ejemplo n.º 10
0
wcstring vformat_string(const wchar_t *format, va_list va_orig)
{    
    const int saved_err = errno;
    /*
      As far as I know, there is no way to check if a
      vswprintf-call failed because of a badly formated string
      option or because the supplied destination string was to
      small. In GLIBC, errno seems to be set to EINVAL either way. 

      Because of this, on failiure we try to
      increase the buffer size until the free space is
      larger than max_size, at which point it will
      conclude that the error was probably due to a badly
      formated string option, and return an error. Make
      sure to null terminate string before that, though.
    */
    const size_t max_size = (128*1024*1024);
    wchar_t static_buff[256];
    size_t size = 0;
    wchar_t *buff = NULL;
    int status = -1;
    while (status < 0) {
        /* Reallocate if necessary */
        if (size == 0) {
            buff = static_buff;
            size = sizeof static_buff;
        } else {
            size *= 2;
            if (size >= max_size) {
                buff[0] = '\0';
                break;
            }
            buff = (wchar_t *)realloc( (buff == static_buff ? NULL : buff), size);
            if (buff == NULL) {
                DIE_MEM();
            }
        }
                
        /* Try printing */
		va_list va;
		va_copy( va, va_orig );
        status = vswprintf(buff, size / sizeof(wchar_t), format, va);
        va_end(va);   
    }
    
    wcstring result = wcstring(buff);
    
    if (buff != static_buff)
        free(buff);
    
    errno = saved_err;
    return result;
}
Ejemplo n.º 11
0
char *wcs2str( const wchar_t *in )
{
	char *out;

	out = malloc( MAX_UTF8_BYTES*wcslen(in)+1 );

	if( !out )
	{
		DIE_MEM();
	}

	return wcs2str_internal( in, out );
}
Ejemplo n.º 12
0
/// A return value of true means all is well (even if no replacements were performed), false
/// indicates an unrecoverable error.
bool regex_replacer_t::replace_matches(const wchar_t *arg) {
    if (regex.code == 0) {
        // pcre2_compile() failed
        return false;
    }

    uint32_t options = PCRE2_SUBSTITUTE_OVERFLOW_LENGTH | PCRE2_SUBSTITUTE_EXTENDED |
                       (opts.all ? PCRE2_SUBSTITUTE_GLOBAL : 0);
    size_t arglen = wcslen(arg);
    PCRE2_SIZE bufsize = (arglen == 0) ? 16 : 2 * arglen;
    wchar_t *output = (wchar_t *)malloc(sizeof(wchar_t) * bufsize);
    int pcre2_rc;

    bool done = false;
    while (!done) {
        if (output == NULL) {
            DIE_MEM();
        }
        PCRE2_SIZE outlen = bufsize;
        pcre2_rc = pcre2_substitute(regex.code, PCRE2_SPTR(arg), arglen,
                                    0,  // start offset
                                    options, regex.match,
                                    0,  // match context
                                    PCRE2_SPTR(replacement.c_str()), PCRE2_ZERO_TERMINATED,
                                    (PCRE2_UCHAR *)output, &outlen);

        if (pcre2_rc != PCRE2_ERROR_NOMEMORY || bufsize >= outlen) {
            done = true;
        } else {
            bufsize = outlen;
            // cppcheck-suppress memleakOnRealloc
            output = (wchar_t *)realloc(output, sizeof(wchar_t) * bufsize);
        }
    }

    bool rc = true;
    if (pcre2_rc < 0) {
        string_error(streams, _(L"%ls: Regular expression substitute error: %ls\n"), argv0,
                     pcre2_strerror(pcre2_rc).c_str());
        rc = false;
    } else {
        if (!opts.quiet) {
            streams.out.append(output);
            streams.out.append(L'\n');
        }
        total_replaced += pcre2_rc;
    }

    free(output);
    return rc;
}
Ejemplo n.º 13
0
/**
   Add backslashes to all newlines, so that the returning string is
   suitable for writing to the history file. The memory for the return
   value will be reused by subsequent calls to this function.
*/
static wchar_t *history_escape_newlines( wchar_t *in )
{
    static string_buffer_t *out = 0;
    if( !out )
    {
        out = sb_halloc( global_context );
        if( !out )
        {
            DIE_MEM();
        }
    }
    else
    {
        sb_clear( out );
    }
    for( ; *in; in++ )
    {
        if( *in == L'\\' )
        {
            sb_append_char( out, *in );
            if( *(in+1) )
            {
                in++;
                sb_append_char( out, *in );
            }
            else
            {
                /*
                  This is a weird special case. When we are trying to
                  save a string that ends with a backslash, we need to
                  handle it specially, otherwise this command would be
                  combined with the one following it. We hack around
                  this by adding an additional newline.
                */
                sb_append_char( out, L'\n' );
            }

        }
        else if( *in == L'\n' )
        {
            sb_append_char( out, L'\\' );
            sb_append_char( out, *in );
        }
        else
        {
            sb_append_char( out, *in );
        }

    }
    return (wchar_t *)out->buff;
}
Ejemplo n.º 14
0
wchar_t *str2wcs( const char *in )
{
	wchar_t *out;
	size_t len = strlen(in);
	
	out = (wchar_t *)malloc( sizeof(wchar_t)*(len+1) );

	if( !out )
	{
		DIE_MEM();
	}

	return str2wcs_internal( in, out );
}
Ejemplo n.º 15
0
/**
   For wgettext: Wide to narrow character conversion. Internal implementation that
   avoids exessive calls to malloc
*/
static char *wgettext_wcs2str( const wchar_t *in )
{
	size_t len = MAX_UTF8_BYTES*wcslen(in)+1;
	if( len > wcs2str_buff_count )
	{
		wcs2str_buff = realloc( wcs2str_buff, len );
		if( !wcs2str_buff )
		{
			DIE_MEM();
		}
	}

	return wcs2str_internal( in, wcs2str_buff);
}
Ejemplo n.º 16
0
/**
   Perform string escaping of a strinng by only quoting it. Assumes
   the string has already been checked for characters that can not be
   escaped this way.
 */
static wchar_t *escape_simple( const wchar_t *in )
{
	wchar_t *out;
	size_t len = wcslen(in);
	out = (wchar_t *)malloc( sizeof(wchar_t)*(len+3));
	if( !out )
		DIE_MEM();
	
	out[0] = L'\'';
	wcscpy(&out[1], in );
	out[len+1]=L'\'';
	out[len+2]=0;
	return out;
}
Ejemplo n.º 17
0
void add_entry(char *file, int line, void *memory, size_t size)
{
#ifdef TESTMEMORY
  entry_t *current = (entry_t*) malloc(sizeof(entry_t));
  if(!current)
    DIE_MEM();

  /* Put the new entry at the front of the list. */
  current->next = first;
  first         = current;

  current->file   = file;
  current->line   = line;
  current->memory = memory;
  current->size   = size;
#endif
}
Ejemplo n.º 18
0
/**
   Test if the specified script returns zero. The result is cached, so
   that if multiple completions use the same condition, it needs only
   be evaluated once. condition_cache_clear must be called after a
   completion run to make sure that there are no stale completions.
*/
static int condition_test( const wchar_t *condition )
{
	const void *test_res;

	if( !condition || !wcslen(condition) )
	{
//		fwprintf( stderr, L"No condition specified\n" );
		return 1;
	}

	if( !condition_cache )
	{
		condition_cache = malloc( sizeof( hash_table_t ) );
		if( !condition_cache )
		{
			DIE_MEM();

		}

		hash_init( condition_cache,
				   &hash_wcs_func,
				   &hash_wcs_cmp );


	}

	test_res = hash_get( condition_cache, condition );

	if (test_res == CC_NOT_TESTED )
	{
		test_res = exec_subshell( condition, 0 )?CC_FALSE:CC_TRUE;
		hash_put( condition_cache, condition, test_res );

		/*
		  Restore previous status information
		*/
	}

	if( wcscmp( test_res, CC_TRUE ) == 0 )
	{
		return 1;
	}

	return 0;
}
Ejemplo n.º 19
0
/**
   Convert the specified wide aharacter string to a narrow character
   string. This function uses an internal temporary buffer for storing
   the result so subsequent results will overwrite previous results.
*/
static char *wutil_wcs2str( const wchar_t *in )
{
	size_t new_sz;

	wutil_calls++;

	new_sz =MAX_UTF8_BYTES*wcslen(in)+1;
	if( tmp_len < new_sz )
	{
		new_sz = maxi( new_sz, TMP_LEN_MIN );
		tmp = realloc( tmp, new_sz );
		if( !tmp )
		{
			DIE_MEM();
		}
		tmp_len = new_sz;
	}

	return wcs2str_internal( in, tmp );
}
Ejemplo n.º 20
0
/**
   Convert the specified wide character string to a narrow character
   string. This function uses an internal temporary buffer for storing
   the result so subsequent results will overwrite previous results.
*/
static wchar_t *wutil_str2wcs( const char *in )
{
	size_t new_sz;

	wutil_calls++;

	new_sz = sizeof(wchar_t)*(strlen(in)+1);
	if( tmp2_len < new_sz )
	{
		new_sz = maxi( new_sz, TMP_LEN_MIN );
		tmp2 = realloc( tmp2, new_sz );
		if( !tmp2 )
		{
			DIE_MEM();
		}
		tmp2_len = new_sz;
	}

	return str2wcs_internal( in, tmp2 );
}
Ejemplo n.º 21
0
wchar_t *parse_util_unescape_wildcards(const wchar_t *str)
{
    wchar_t *in, *out;
    wchar_t *unescaped;

    CHECK(str, 0);

    unescaped = wcsdup(str);

    if (!unescaped)
    {
        DIE_MEM();
    }

    for (in=out=unescaped; *in; in++)
    {
        switch (*in)
        {
            case L'\\':
            {
                switch (*(in + 1))
                {
                    case L'*':
                    case L'?':
                    {
                        in++;
                        *(out++)=*in;
                        break;
                    }
                    case L'\\':
                    {
                        in++;
                        *(out++)=L'\\';
                        *(out++)=L'\\';
                        break;
                    }
                    default:
                    {
                        *(out++)=*in;
                        break;
                    }
                }
                break;
            }

            case L'*':
            {
                *(out++)=ANY_STRING;
                break;
            }

            case L'?':
            {
                *(out++)=ANY_CHAR;
                break;
            }

            default:
            {
                *(out++)=*in;
                break;
            }
        }
    }
    *out = *in;
    return unescaped;
}
Ejemplo n.º 22
0
/**
   Get the beginning and end of the job or process definition under the cursor
*/
static void job_or_process_extent(const wchar_t *buff,
                                  size_t cursor_pos,
                                  const wchar_t **a,
                                  const wchar_t **b,
                                  int process)
{
    const wchar_t *begin, *end;
    long pos;
    wchar_t *buffcpy;
    int finished=0;

    CHECK(buff,);

    if (a)
    {
        *a=0;
    }

    if (b)
    {
        *b = 0;
    }

    parse_util_cmdsubst_extent(buff, cursor_pos, &begin, &end);
    if (!end || !begin)
    {
        return;
    }

    pos = cursor_pos - (begin - buff);

    if (a)
    {
        *a = begin;
    }

    if (b)
    {
        *b = end;
    }

    buffcpy = wcsndup(begin, end-begin);

    if (!buffcpy)
    {
        DIE_MEM();
    }

    tokenizer_t tok(buffcpy, TOK_ACCEPT_UNFINISHED);
    for (; tok_has_next(&tok) && !finished; tok_next(&tok))
    {
        int tok_begin = tok_get_pos(&tok);

        switch (tok_last_type(&tok))
        {
            case TOK_PIPE:
            {
                if (!process)
                {
                    break;
                }
            }

            case TOK_END:
            case TOK_BACKGROUND:
            {

                if (tok_begin >= pos)
                {
                    finished=1;
                    if (b)
                    {
                        *b = (wchar_t *)begin + tok_begin;
                    }
                }
                else
                {
                    if (a)
                    {
                        *a = (wchar_t *)begin + tok_begin+1;
                    }
                }

                break;
            }

            default:
            {
                break;
            }
        }
    }

    free(buffcpy);
}
Ejemplo n.º 23
0
void parse_util_token_extent( const wchar_t *buff,
							  int cursor_pos,
							  const wchar_t **tok_begin,
							  const wchar_t **tok_end,
							  const wchar_t **prev_begin, 
							  const wchar_t **prev_end )
{
	const wchar_t *begin, *end;
	int pos;
	wchar_t *buffcpy;

	tokenizer tok;

	const wchar_t *a = NULL, *b = NULL, *pa = NULL, *pb = NULL;
	
	CHECK( buff, );
		
	assert( cursor_pos >= 0 );
	
	parse_util_cmdsubst_extent( buff, cursor_pos, &begin, &end );

	if( !end || !begin )
	{
		return;
	}
	
	pos = cursor_pos - (begin - buff);
	
	a = buff + pos;
	b = a;
	pa = buff + pos;
	pb = pa;
	
	assert( begin >= buff );
	assert( begin <= (buff+wcslen(buff) ) );
	assert( end >= begin );
	assert( end <= (buff+wcslen(buff) ) );
	
	buffcpy = wcsndup( begin, end-begin );
	
	if( !buffcpy )
	{
		DIE_MEM();
	}

	for( tok_init( &tok, buffcpy, TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS );
		 tok_has_next( &tok );
		 tok_next( &tok ) )
	{
		int tok_begin = tok_get_pos( &tok );
		int tok_end=tok_begin;

		/*
		  Calculate end of token
		*/
		if( tok_last_type( &tok ) == TOK_STRING )
		{
			tok_end +=wcslen(tok_last(&tok));
		}
		
		/*
		  Cursor was before beginning of this token, means that the
		  cursor is between two tokens, so we set it to a zero element
		  string and break
		*/
		if( tok_begin > pos )
		{
			a = b = (wchar_t *)buff + pos;
			break;
		}

		/*
		  If cursor is inside the token, this is the token we are
		  looking for. If so, set a and b and break
		*/
		if( (tok_last_type( &tok ) == TOK_STRING) && (tok_end >= pos ) )
		{			
			a = begin + tok_get_pos( &tok );
			b = a + wcslen(tok_last(&tok));
			break;
		}
		
		/*
		  Remember previous string token
		*/
		if( tok_last_type( &tok ) == TOK_STRING )
		{
			pa = begin + tok_get_pos( &tok );
			pb = pa + wcslen(tok_last(&tok));
		}
	}

	free( buffcpy);
	
	tok_destroy( &tok );

	if( tok_begin )
	{
		*tok_begin = a;
	}

	if( tok_end )
	{
		*tok_end = b;
	}

	if( prev_begin )
	{
		*prev_begin = pa;
	}

	if( prev_end )
	{
		*prev_end = pb;
	}
	
	assert( pa >= buff );
	assert( pa <= (buff+wcslen(buff) ) );
	assert( pb >= pa );
	assert( pb <= (buff+wcslen(buff) ) );

}
Ejemplo n.º 24
0
Archivo: exec.c Proyecto: CodeMonk/fish
/**
   Make a copy of the specified io redirection chain, but change file
   redirection into fd redirection. This makes the redirection chain
   suitable for use as block-level io, since the file won't be
   repeatedly reopened for every command in the block, which would
   reset the cursor position.

   \return the transmogrified chain on sucess, or 0 on failiure
*/
static io_data_t *io_transmogrify( io_data_t * in )
{
	io_data_t *out;

	if( !in )
		return 0;
	
	out = malloc( sizeof( io_data_t ) );
	if( !out )
		DIE_MEM();
	
	out->fd = in->fd;
	out->io_mode = IO_FD;
	out->param2.close_old = 1;
	out->next=0;
		
	switch( in->io_mode )
	{
		/*
		  These redirections don't need transmogrification. They can be passed through.
		*/
		case IO_FD:
		case IO_CLOSE:
		case IO_BUFFER:
		case IO_PIPE:
		{
			memcpy( out, in, sizeof(io_data_t));
			break;
		}

		/*
		  Transmogrify file redirections
		*/
		case IO_FILE:
		{
			int fd;
			
			if( (fd=wopen( in->param1.filename, in->param2.flags, OPEN_MASK ) )==-1 )
			{
				debug( 1, 
					   FILE_ERROR,
					   in->param1.filename );
								
				wperror( L"open" );
				free( out );
				return 0;
			}	

			out->param1.old_fd = fd;
			break;
		}
	}
	
	if( in->next)
	{
		out->next = io_transmogrify( in->next );
		if( !out->next )
		{
			io_untransmogrify( in, out );
			return 0;
		}
	}
	
	return out;
}
Ejemplo n.º 25
0
int complete_is_valid_option( const wchar_t *str,
							  const wchar_t *opt,
							  array_list_t *errors )
{
	complete_entry_t *i;
	complete_entry_opt_t *o;
	wchar_t *cmd, *path;
	int found_match = 0;
	int authoritative = 1;
	int opt_found=0;
	hash_table_t gnu_match_hash;
	int is_gnu_opt=0;
	int is_old_opt=0;
	int is_short_opt=0;
	int is_gnu_exact=0;
	int gnu_opt_len=0;
	char *short_validated;

	void *context;

	CHECK( str, 0 );
	CHECK( opt, 0 );

	/*
	  Check some generic things like -- and - options.
	*/
	switch( wcslen(opt ) )
	{

		case 0:
		case 1:
		{
			return 1;
		}

		case 2:
		{
			if( wcscmp( L"--", opt ) == 0 )
			{
				return 1;
			}
			break;
		}
	}

	if( opt[0] != L'-' )
	{
		if( errors )
		{
			al_push( errors, wcsdup(L"Option does not begin with a '-'") );
		}
		return 0;
	}

	context = halloc( 0, 0 );

	if( !(short_validated = halloc( context, wcslen( opt ) )))
	{
		DIE_MEM();
	}



	memset( short_validated, 0, wcslen( opt ) );

	hash_init( &gnu_match_hash,
			   &hash_wcs_func,
			   &hash_wcs_cmp );

	is_gnu_opt = opt[1]==L'-';
	if( is_gnu_opt )
	{
		wchar_t *opt_end = wcschr(opt, L'=' );
		if( opt_end )
		{
			gnu_opt_len = (opt_end-opt)-2;
		}
		else
		{
			gnu_opt_len = wcslen(opt)-2;
		}
	}

	parse_cmd_string( context, str, &path, &cmd );

	/*
	  Make sure completions are loaded for the specified command
	*/
	complete_load( cmd, 0 );

	for( i=first_entry; i; i=i->next )
	{
		wchar_t *match = i->cmd_type?path:cmd;
		const wchar_t *a;

		if( !wildcard_match( match, i->cmd ) )
		{
			continue;
		}

		found_match = 1;

		if( !i->authoritative )
		{
			authoritative = 0;
			break;
		}


		if( is_gnu_opt )
		{

			for( o = i->first_option; o; o=o->next )
			{
				if( o->old_mode )
				{
					continue;
				}

				if( wcsncmp( &opt[2], o->long_opt, gnu_opt_len )==0)
				{
					hash_put( &gnu_match_hash, o->long_opt, L"" );
					if( (wcsncmp( &opt[2],
								  o->long_opt,
								  wcslen( o->long_opt) )==0) )
					{
						is_gnu_exact=1;
					}
				}
			}
		}
		else
		{
			/* Check for old style options */
			for( o = i->first_option; o; o=o->next )
			{
				if( !o->old_mode )
					continue;


				if( wcscmp( &opt[1], o->long_opt )==0)
				{
					opt_found = 1;
					is_old_opt = 1;
					break;
				}

			}

			if( is_old_opt )
				break;


			for( a = &opt[1]; *a; a++ )
			{

				wchar_t *str_pos = wcschr(i->short_opt_str, *a);

				if  (str_pos )
				{
					if( *(str_pos +1)==L':' )
					{
						/*
						  This is a short option with an embedded argument,
						  call complete_is_valid_argument on the argument.
						*/
						wchar_t nopt[3];
						nopt[0]=L'-';
						nopt[1]=opt[1];
						nopt[2]=L'\0';

						short_validated[a-opt] =
							complete_is_valid_argument( str, nopt, &opt[2]);
					}
					else
					{
						short_validated[a-opt]=1;
					}
				}
			}
		}
	}

	if( authoritative )
	{

		if( !is_gnu_opt && !is_old_opt )
			is_short_opt = 1;


		if( is_short_opt )
		{
			int j;

			opt_found=1;
			for( j=1; j<wcslen(opt); j++)
			{
				if ( !short_validated[j])
				{
					if( errors )
					{
						wchar_t str[2];
						str[0] = opt[j];
						str[1]=0;
						al_push( errors,
								 wcsdupcat(_( L"Unknown option: " ), L"'", str, L"'" ) );
					}

					opt_found = 0;
					break;
				}

			}
		}

		if( is_gnu_opt )
		{
			opt_found = is_gnu_exact || (hash_get_count( &gnu_match_hash )==1);
			if( errors && !opt_found )
			{
				if( hash_get_count( &gnu_match_hash )==0)
				{
					al_push( errors,
							 wcsdupcat( _(L"Unknown option: "), L"'", opt, L"\'" ) );
				}
				else
				{
					al_push( errors,
							 wcsdupcat( _(L"Multiple matches for option: "), L"'", opt, L"\'" ) );
				}
			}
		}
	}

	hash_destroy( &gnu_match_hash );

	halloc_free( context );

	return (authoritative && found_match)?opt_found:1;
}
Ejemplo n.º 26
0
/**
   Returns an item_t for the specified adress. The adress must come from the item list of the specified mode.

   Later calls to this function may erase the output of a previous call to this function.
*/
static item_t *item_get( history_mode_t *m, void *d )
{
    char *begin = (char *)d;

    if( item_is_new( m, d ) )
    {
        return (item_t *)d;
    }
    else
    {

        char *end = m->mmap_start + m->mmap_length;
        char *pos=begin;

        int was_backslash = 0;
        static string_buffer_t *out = 0;
        static item_t narrow_item;
        int first_char = 1;
        int timestamp_mode = 0;

        narrow_item.timestamp = 0;

        if( !out )
        {
            out = sb_halloc( global_context );
            if( !out )
            {
                DIE_MEM();
            }
        }
        else
        {
            sb_clear( out );
        }

        while( 1 )
        {
            wchar_t c;
            mbstate_t state;
            size_t res;

            memset( &state, 0, sizeof(state) );

            res = mbrtowc( &c, pos, end-pos, &state );

            if( res == (size_t)-1 )
            {
                pos++;
                continue;
            }
            else if( res == (size_t)-2 )
            {
                break;
            }
            else if( res == (size_t)0 )
            {
                pos++;
                continue;
            }
            pos += res;

            if( c == L'\n' )
            {
                if( timestamp_mode )
                {
                    wchar_t *time_string = (wchar_t *)out->buff;
                    while( *time_string && !iswdigit(*time_string))
                        time_string++;
                    errno=0;

                    if( *time_string )
                    {
                        time_t tm;
                        wchar_t *end;

                        errno = 0;
                        tm = (time_t)wcstol( time_string, &end, 10 );

                        if( tm && !errno && !*end )
                        {
                            narrow_item.timestamp = tm;
                        }

                    }

                    sb_clear( out );
                    timestamp_mode = 0;
                    continue;
                }
                if( !was_backslash )
                    break;
            }

            if( first_char )
            {
                if( c == L'#' )
                    timestamp_mode = 1;
            }

            first_char = 0;

            sb_append_char( out, c );

            was_backslash = ( (c == L'\\') && !was_backslash);

        }

        narrow_item.data = history_unescape_newlines((wchar_t *)out->buff);
        return &narrow_item;
    }

}
Ejemplo n.º 27
0
wchar_t *unescape( const wchar_t * orig, int flags )
{
	
	int mode = 0; 
	int in_pos, out_pos, len;
	int c;
	int bracket_count=0;
	wchar_t prev=0;	
	wchar_t *in;
	int unescape_special = flags & UNESCAPE_SPECIAL;
	int allow_incomplete = flags & UNESCAPE_INCOMPLETE;
	
	CHECK( orig, 0 );
		
	len = wcslen( orig );
	in = wcsdup( orig );

	if( !in )
		DIE_MEM();
	
	for( in_pos=0, out_pos=0; 
		 in_pos<len; 
		 (prev=(out_pos>=0)?in[out_pos]:0), out_pos++, in_pos++ )
	{
		c = in[in_pos];
		switch( mode )
		{

			/*
			  Mode 0 means unquoted string
			*/
			case 0:
			{
				if( c == L'\\' )
				{
					switch( in[++in_pos] )
					{
						
						/*
						  A null character after a backslash is an
						  error, return null
						*/
						case L'\0':
						{
							if( !allow_incomplete )
							{
								free(in);
								return 0;
							}
						}
												
						/*
						  Numeric escape sequences. No prefix means
						  octal escape, otherwise hexadecimal.
						*/
						
						case L'0':
						case L'1':
						case L'2':
						case L'3':
						case L'4':
						case L'5':
						case L'6':
						case L'7':
						case L'u':
						case L'U':
						case L'x':
						case L'X':
						{
							int i;
							long long res=0;
							int chars=2;
							int base=16;
							
							int byte = 0;
							wchar_t max_val = ASCII_MAX;
							
							switch( in[in_pos] )
							{
								case L'u':
								{
									chars=4;
									max_val = UCS2_MAX;
									break;
								}
								
								case L'U':
								{
									chars=8;
									max_val = WCHAR_MAX;
									break;
								}
								
								case L'x':
								{
									break;
								}
								
								case L'X':
								{
									byte=1;
									max_val = BYTE_MAX;
									break;
								}
								
								default:
								{
									base=8;
									chars=3;
									in_pos--;
									break;
								}								
							}
					
							for( i=0; i<chars; i++ )
							{
								int d = convert_digit( in[++in_pos],base);
								
								if( d < 0 )
								{
									in_pos--;
									break;
								}
								
								res=(res*base)|d;
							}

							if( (res <= max_val) )
							{
								in[out_pos] = (byte?ENCODE_DIRECT_BASE:0)+res;
							}
							else
							{	
								free(in);	
								return 0;
							}
							
							break;
						}

						/*
						  \a means bell (alert)
						*/
						case L'a':
						{
							in[out_pos]=L'\a';
							break;
						}
						
						/*
						  \b means backspace
						*/
						case L'b':
						{
							in[out_pos]=L'\b';
							break;
						}
						
						/*
						  \cX means control sequence X
						*/
						case L'c':
						{
							in_pos++;
							if( in[in_pos] >= L'a' &&
								in[in_pos] <= (L'a'+32) )
							{
								in[out_pos]=in[in_pos]-L'a'+1;
							}
							else if( in[in_pos] >= L'A' &&
									 in[in_pos] <= (L'A'+32) )
							{
								in[out_pos]=in[in_pos]-L'A'+1;
							}
							else
							{
								free(in);	
								return 0;
							}
							break;
							
						}
						
						/*
						  \x1b means escape
						*/
						case L'e':
						{
							in[out_pos]=L'\x1b';
							break;
						}
						
						/*
						  \f means form feed
						*/
						case L'f':
						{
							in[out_pos]=L'\f';
							break;
						}

						/*
						  \n means newline
						*/
						case L'n':
						{
							in[out_pos]=L'\n';
							break;
						}
						
						/*
						  \r means carriage return
						*/
						case L'r':
						{
							in[out_pos]=L'\r';
							break;
						}
						
						/*
						  \t means tab
						 */
						case L't':
						{
							in[out_pos]=L'\t';
							break;
						}

						/*
						  \v means vertical tab
						*/
						case L'v':
						{
							in[out_pos]=L'\v';
							break;
						}
						
						default:
						{
							if( unescape_special )
								in[out_pos++] = INTERNAL_SEPARATOR;							
							in[out_pos]=in[in_pos];
							break;
						}
					}
				}
				else 
				{
					switch( in[in_pos])
					{
						case L'~':
						{
							if( unescape_special && (in_pos == 0) )
							{
								in[out_pos]=HOME_DIRECTORY;
							}
							else
							{
								in[out_pos] = L'~';
							}
							break;
						}

						case L'%':
						{
							if( unescape_special && (in_pos == 0) )
							{
								in[out_pos]=PROCESS_EXPAND;
							}
							else
							{
								in[out_pos]=in[in_pos];						
							}
							break;
						}

						case L'*':
						{
							if( unescape_special )
							{
								if( out_pos > 0 && in[out_pos-1]==ANY_STRING )
								{
									out_pos--;
									in[out_pos] = ANY_STRING_RECURSIVE;
								}
								else
									in[out_pos]=ANY_STRING;
							}
							else
							{
								in[out_pos]=in[in_pos];						
							}
							break;
						}

						case L'?':
						{
							if( unescape_special )
							{
								in[out_pos]=ANY_CHAR;
							}
							else
							{
								in[out_pos]=in[in_pos];						
							}
							break;					
						}

						case L'$':
						{
							if( unescape_special )
							{
								in[out_pos]=VARIABLE_EXPAND;
							}
							else
							{
								in[out_pos]=in[in_pos];											
							}
							break;					
						}

						case L'{':
						{
							if( unescape_special )
							{
								bracket_count++;
								in[out_pos]=BRACKET_BEGIN;
							}
							else
							{
								in[out_pos]=in[in_pos];						
							}
							break;					
						}
						
						case L'}':
						{
							if( unescape_special )
							{
								bracket_count--;
								in[out_pos]=BRACKET_END;
							}
							else
							{
								in[out_pos]=in[in_pos];						
							}
							break;					
						}
						
						case L',':
						{
							if( unescape_special && bracket_count && prev!=BRACKET_SEP)
							{
								in[out_pos]=BRACKET_SEP;
							}
							else
							{
								in[out_pos]=in[in_pos];						
							}
							break;					
						}
						
						case L'\'':
						{
							mode = 1;
							if( unescape_special )
								in[out_pos] = INTERNAL_SEPARATOR;							
							else
								out_pos--;						
							break;					
						}
				
						case L'\"':
						{
							mode = 2;
							if( unescape_special )
								in[out_pos] = INTERNAL_SEPARATOR;							
							else
								out_pos--;						
							break;
						}

						default:
						{
							in[out_pos] = in[in_pos];
							break;
						}
					}
				}		
				break;
			}

			/*
			  Mode 1 means single quoted string, i.e 'foo'
			*/
			case 1:
			{
				if( c == L'\\' )
				{
					switch( in[++in_pos] )
					{
						case '\\':
						case L'\'':
						case L'\n':
						{
							in[out_pos]=in[in_pos];
							break;
						}
						
						case 0:
						{
							if( !allow_incomplete )
							{
								free(in);
								return 0;
							}
							else
							{
								//We may ever escape a NULL character, but still appending a \ in case I am wrong.
								in[out_pos] = L'\\';
							}
						}
							break;	
						default:
						{
							in[out_pos++] = L'\\';
							in[out_pos]= in[in_pos];
						}
					}
					
				}
				if( c == L'\'' )
				{
					if( unescape_special )
						in[out_pos] = INTERNAL_SEPARATOR;							
					else
						out_pos--;						
					mode = 0;
				}
				else
				{
					in[out_pos] = in[in_pos];
				}
				
				break;
			}

			/*
			  Mode 2 means double quoted string, i.e. "foo"
			*/
			case 2:
			{
				switch( c )
				{
					case '"':
					{
						mode = 0;
						if( unescape_special )
							in[out_pos] = INTERNAL_SEPARATOR;							
						else
							out_pos--;						
						break;
					}
				
					case '\\':
					{
						switch( in[++in_pos] )
						{
							case L'\0':
							{
								if( !allow_incomplete )
								{
									free(in);
									return 0;
								}
								else
								{
									//We probably don't need it since NULL character is always appended before ending this function.
									in[out_pos]=in[in_pos];
								}
							}
								break;		
							case '\\':
							case L'$':
							case '"':
							case '\n':
							{
								in[out_pos]=in[in_pos];
								break;
							}

							default:
							{
								in[out_pos++] = L'\\';
								in[out_pos] = in[in_pos];
								break;
							}
						}
						break;
					}
					
					case '$':
					{
						if( unescape_special )
						{
							in[out_pos]=VARIABLE_EXPAND_SINGLE;
						}
						else
						{
							in[out_pos]=in[in_pos];
						}
						break;
					}
			
					default:
					{
						in[out_pos] = in[in_pos];
						break;
					}
				
				}						
				break;
			}
		}
	}

	if( !allow_incomplete && mode )
	{
		free( in );
		return 0;
	}

	in[out_pos]=L'\0';
	return in;	
}
Ejemplo n.º 28
0
/**
   The set builtin. Creates, updates and erases environment variables
   and environemnt variable arrays.
*/
static int builtin_set( parser_t &parser, wchar_t **argv ) 
{
	
	/**
	   Variables used for parsing the argument list
	*/
	static const struct woption
		long_options[] = 
		{
			{ 
				L"export", no_argument, 0, 'x' 
			}
			,
			{ 
				L"global", no_argument, 0, 'g' 
			}
			,
			{ 
				L"local", no_argument, 0, 'l'  
			}
			,
			{ 
				L"erase", no_argument, 0, 'e'  
			}
			,
			{ 
				L"names", no_argument, 0, 'n' 
			} 
			,
			{ 
				L"unexport", no_argument, 0, 'u' 
			} 
			,
			{ 
				L"universal", no_argument, 0, 'U'
			}
            ,
			{ 
				L"long", no_argument, 0, 'L'
			} 
			,
			{ 
				L"query", no_argument, 0, 'q' 
			} 
			,
			{ 
				L"help", no_argument, 0, 'h' 
			} 
			,
			{ 
				0, 0, 0, 0 
			}
		}
	;
	
	const wchar_t *short_options = L"+xglenuULqh";

	int argc = builtin_count_args(argv);

	/*
	  Flags to set the work mode
	*/
	int local = 0, global = 0, exportv = 0;
	int erase = 0, list = 0, unexport=0;
	int universal = 0, query=0;
	bool shorten_ok = true;

	/*
	  Variables used for performing the actual work
	*/
	wchar_t *dest = 0;
	int retcode=0;
	int scope;
	int slice=0;
	int i;
	
	wchar_t *bad_char;
	
	
	/* Parse options to obtain the requested operation and the modifiers */
	woptind = 0;
	while (1) 
	{
		int c = wgetopt_long(argc, argv, short_options, long_options, 0);

		if (c == -1) 
		{
			break;
		}
    
		switch(c) 
		{
			case 0:
				break;

			case 'e':
				erase = 1;
				break;

			case 'n':
				list = 1;
				break;

			case 'x':
				exportv = 1;
				break;

			case 'l':
				local = 1;
				break;

			case 'g':
				global = 1;
				break;

			case 'u':
				unexport = 1;
				break;

			case 'U':
				universal = 1;
				break;
            
            case 'L':
                shorten_ok = false;
                break;

			case 'q':
				query = 1;
				break;

			case 'h':
				builtin_print_help( parser, argv[0], stdout_buffer );
				return 0;

			case '?':
				builtin_unknown_option( parser, argv[0], argv[woptind-1] );
				return 1;

			default:
				break;
		}
	}

	/*
	  Ok, all arguments have been parsed, let's validate them
	*/

	/*
	  If we are checking the existance of a variable (-q) we can not
	  also specify scope
	*/

	if( query && (erase || list) )
	{
		append_format(stderr_buffer,
				  BUILTIN_ERR_COMBO,
				  argv[0] );
		
		builtin_print_help( parser, argv[0], stderr_buffer );
		return 1;
	}
	

	/* We can't both list and erase varaibles */
	if( erase && list ) 
	{
		append_format(stderr_buffer,
				  BUILTIN_ERR_COMBO,
				  argv[0] );		

		builtin_print_help( parser, argv[0], stderr_buffer );
		return 1;
	}

	/*
	  Variables can only have one scope
	*/
	if( local + global + universal > 1 ) 
	{
		append_format(stderr_buffer,
				   BUILTIN_ERR_GLOCAL,
				   argv[0] );
		builtin_print_help( parser, argv[0], stderr_buffer );
		return 1;
	}

	/*
	  Variables can only have one export status
	*/
	if( exportv && unexport ) 
	{
		append_format(stderr_buffer,
				   BUILTIN_ERR_EXPUNEXP,
				   argv[0] );
		builtin_print_help( parser, argv[0], stderr_buffer );
		return 1;
	}

	/*
	  Calculate the scope value for variable assignement
	*/
	scope = (local ? ENV_LOCAL : 0) | (global ? ENV_GLOBAL : 0) | (exportv ? ENV_EXPORT : 0) | (unexport ? ENV_UNEXPORT : 0) | (universal ? ENV_UNIVERSAL:0) | ENV_USER; 

	if( query )
	{
		/*
		  Query mode. Return the number of variables that do not exist
		  out of the specified variables.
		*/
		int i;
		for( i=woptind; i<argc; i++ )
		{
			wchar_t *arg = argv[i];
			int slice=0;

			if( !(dest = wcsdup(arg)))
			{
				DIE_MEM();		
			}

			if( wcschr( dest, L'[' ) )
			{
				slice = 1;
				*wcschr( dest, L'[' )=0;
			}
			
			if( slice )
			{
				std::vector<long> indexes;
				wcstring_list_t result;
				size_t j;
				
                env_var_t dest_str = env_get_string(dest);
                if (! dest_str.missing())
                    tokenize_variable_array( dest_str, result );
								
				if( !parse_index( indexes, arg, dest, result.size() ) )
				{
					builtin_print_help( parser, argv[0], stderr_buffer );
					retcode = 1;
					break;
				}
				for( j=0; j < indexes.size() ; j++ )
				{
					long idx = indexes[j];
					if( idx < 1 || (size_t)idx > result.size() )
					{
						retcode++;
					}
				}
			}
			else
			{
				if( !env_exist( arg, scope ) )
				{
					retcode++;
				}
			}
			
			free( dest );
			
		}
		return retcode;
	}
	
	if( list ) 
	{
		/* Maybe we should issue an error if there are any other arguments? */
		print_variables(0, 0, shorten_ok, scope);
		return 0;
	} 
	
	if( woptind == argc )
	{
		/*
		  Print values of variables
		*/

		if( erase ) 
		{
			append_format(stderr_buffer,
					   _(L"%ls: Erase needs a variable name\n"), 
					   argv[0] );
			
			builtin_print_help( parser, argv[0], stderr_buffer );
			retcode = 1;
		}
		else
		{
			print_variables( 1, 1, shorten_ok, scope );
		}
		
		return retcode;
	}

	if( !(dest = wcsdup(argv[woptind])))
	{
		DIE_MEM();		
	}

	if( wcschr( dest, L'[' ) )
	{
		slice = 1;
		*wcschr( dest, L'[' )=0;
	}
	
	if( !wcslen( dest ) )
	{
		free( dest );
		append_format(stderr_buffer, BUILTIN_ERR_VARNAME_ZERO, argv[0] );
		builtin_print_help( parser, argv[0], stderr_buffer );
		return 1;
	}
	
	if( (bad_char = wcsvarname( dest ) ) )
	{
		append_format(stderr_buffer, BUILTIN_ERR_VARCHAR, argv[0], *bad_char );
		builtin_print_help( parser, argv[0], stderr_buffer );
		free( dest );
		return 1;
	}
	
	if( slice && erase && (scope != ENV_USER) )
	{
		free( dest );
		append_format(stderr_buffer, _(L"%ls: Can not specify scope when erasing array slice\n"), argv[0] );
		builtin_print_help( parser, argv[0], stderr_buffer );
		return 1;
	}
	
	/*
	  set assignment can work in two modes, either using slices or
	  using the whole array. We detect which mode is used here.
	*/
	
	if( slice )
	{

		/*
		  Slice mode
		*/
		int idx_count, val_count;
		wcstring_list_t values;
		std::vector<long> indexes;
		wcstring_list_t result;
		
        const env_var_t dest_str = env_get_string(dest);
        if (! dest_str.missing())
            tokenize_variable_array( dest_str, result );
		
		for( ; woptind<argc; woptind++ )
		{			
			if( !parse_index( indexes, argv[woptind], dest, result.size() ) )
			{
				builtin_print_help( parser, argv[0], stderr_buffer );
				retcode = 1;
				break;
			}
			
			val_count = argc-woptind-1;
			idx_count = indexes.size();

			if( !erase )
			{
				if( val_count < idx_count )
				{
					append_format(stderr_buffer, _(BUILTIN_SET_ARG_COUNT), argv[0] );
					builtin_print_help( parser, argv[0], stderr_buffer );
					retcode=1;
					break;
				}
				if( val_count == idx_count )
				{
					woptind++;
					break;
				}
			}
		}		

		if( !retcode )
		{
			/*
			  Slice indexes have been calculated, do the actual work
			*/

			if( erase )
			{
				erase_values(result, indexes);
				my_env_set( dest, result, scope);
			}
			else
			{
				wcstring_list_t value;
//				al_init(&value);

				while( woptind < argc ) 
				{
					value.push_back( argv[woptind++] );
				}

				if( update_values( result, 
								   indexes,
								   value ) )
				{
					append_format(stderr_buffer, L"%ls: ", argv[0] );
					append_format(stderr_buffer, ARRAY_BOUNDS_ERR );
					stderr_buffer.push_back(L'\n');
				}
				
				my_env_set(dest, result, scope);
								
//				al_destroy( &value );
								
			}			
		}

//		al_foreach( &result, &free );
//		al_destroy( &result );

//		al_destroy(&indexes);
//		al_destroy(&values);
		
	}
	else
	{
		woptind++;
		
		/*
		  No slicing
		*/
		if( erase )
		{
			if( woptind != argc )
			{
				append_format(stderr_buffer, 
						   _(L"%ls: Values cannot be specfied with erase\n"),
						   argv[0] );
				builtin_print_help( parser, argv[0], stderr_buffer );
				retcode=1;
			}
			else
			{
				retcode = env_remove( dest, scope );
			}
		}
		else
		{
            wcstring_list_t val;
			for( i=woptind; i<argc; i++ )
                val.push_back(argv[i]);
			retcode = my_env_set( dest, val, scope );
		}		
	}
	
	free( dest );
	
	return retcode;

}
Ejemplo n.º 29
0
/**
   The real implementation of wildcard expansion is in this
   function. Other functions are just wrappers around this one.

   This function traverses the relevant directory tree looking for
   matches, and recurses when needed to handle wildcrards spanning
   multiple components and recursive wildcards. 
 */
static int wildcard_expand_internal( const wchar_t *wc,
									 const wchar_t *base_dir,
									 expand_flags_t flags,
									 std::vector<completion_t> &out,
                                     std::set<wcstring> &completion_set,
                                     std::set<file_id_t> &visited_files
                                   )
{

	/* Points to the end of the current wildcard segment */
	const wchar_t *wc_end;

	/* Variables for traversing a directory */
	DIR *dir;
	
	/* The result returned */
	int res = 0;
	
	/* Length of the directory to search in */
	size_t base_len;

	/* Variables for testing for presense of recursive wildcards */
	const wchar_t *wc_recursive;
	int is_recursive;

	/* Slightly mangled version of base_dir */
	const wchar_t *dir_string;
	
	//	debug( 3, L"WILDCARD_EXPAND %ls in %ls", wc, base_dir );

	if( reader_interrupted() )
	{
		return -1;
	}
	
	if( !wc || !base_dir )
	{
		debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ );
		return 0;		
	}

	if( flags & ACCEPT_INCOMPLETE )
	{	
		/* 
		   Avoid excessive number of returned matches for wc ending with a * 
		*/
		size_t len = wcslen(wc);
		if( len && (wc[len-1]==ANY_STRING) )
		{
			wchar_t * foo = wcsdup( wc );
			foo[len-1]=0;
			int res = wildcard_expand_internal( foo, base_dir, flags, out, completion_set, visited_files );
			free( foo );
			return res;			
		}
	}

	/*
	  Initialize various variables
	*/

	dir_string = base_dir[0]==L'\0'?L".":base_dir;
	
	if( !(dir = wopendir( dir_string )))
	{
		return 0;
	}

	wc_end = wcschr(wc,L'/');
	base_len = wcslen( base_dir );

	/*
	  Test for recursive match string in current segment
	*/	
	wc_recursive = wcschr( wc, ANY_STRING_RECURSIVE );
	is_recursive = ( wc_recursive && (!wc_end || wc_recursive < wc_end));

	/*
	  Is this segment of the wildcard the last?
	*/
	if( !wc_end )
	{
		/*
		  Wildcard segment is the last segment,

		  Insert all matching files/directories
		*/
		if( wc[0]=='\0' )
		{
			/*
			  The last wildcard segment is empty. Insert everything if
			  completing, the directory itself otherwise.
			*/
			if( flags & ACCEPT_INCOMPLETE )
			{
                wcstring next;
				while(wreaddir(dir, next))
				{
					if( next[0] != L'.' )
					{
						wcstring long_name = make_path( base_dir, next );
						
						if( test_flags( long_name.c_str(), flags ) )
						{
							wildcard_completion_allocate( out,
														  long_name,
														  next,
														  L"",
                                                          flags);
						}
					}					
				}
			}
			else
			{
				res = 1;
                insert_completion_if_missing(base_dir, out, completion_set);
			}
		}
		else
		{
			/*
			  This is the last wildcard segment, and it is not empty. Match files/directories.
			*/
            wcstring next;
			while (wreaddir(dir, next))
			{
                const wchar_t * const name = next.c_str();
				if( flags & ACCEPT_INCOMPLETE )
				{
					
					const wcstring long_name = make_path( base_dir, next );

					/*
					  Test for matches before stating file, so as to minimize the number of calls to the much slower stat function 
					*/
					std::vector<completion_t> test;
					if( wildcard_complete( name,
										   wc,
										   L"",
										   0,
										   test,
										   0 ) )
					{
						if( test_flags( long_name.c_str(), flags ) )
						{
							wildcard_completion_allocate( out,
														  long_name,
                                                          name,
														  wc,
                                                          flags);
							
						}
					}					
				}
				else
				{
					if( wildcard_match2( name, wc, 1 ) )
					{
                        const wcstring long_name = make_path(base_dir, next);
						int skip = 0;
						
						if( is_recursive )
						{
							/*
							  In recursive mode, we are only
							  interested in adding files -directories
							  will be added in the next pass.
							*/
							struct stat buf;
							if( !wstat( long_name, &buf ) )
							{
								skip = S_ISDIR(buf.st_mode);
							}							
						}
						if (! skip)
						{
                            insert_completion_if_missing(long_name, out, completion_set);
						}
						res = 1;
					}
				}
			}
		}
	}

	if( wc_end || is_recursive )
	{
		/*
		  Wilcard segment is not the last segment.  Recursively call
		  wildcard_expand for all matching subdirectories.
		*/
		
		/*
		  wc_str is the part of the wildcarded string from the
		  beginning to the first slash
		*/
		wchar_t *wc_str;

		/*
		  new_dir is a scratch area containing the full path to a
		  file/directory we are iterating over
		*/
		wchar_t *new_dir;

		/*
		  The maximum length of a file element
		*/
		long ln=MAX_FILE_LENGTH;
		char * narrow_dir_string = wcs2str( dir_string );

		/*
		  In recursive mode, we look through the directory twice. If
		  so, this rewind is needed.
		*/
		rewinddir( dir );

		if( narrow_dir_string )
		{
			/* 
			   Find out how long the filename can be in a worst case
			   scenario
			*/
			ln = pathconf( narrow_dir_string, _PC_NAME_MAX ); 

			/*
			  If not specified, use som large number as fallback
			*/
			if( ln < 0 )
				ln = MAX_FILE_LENGTH;		
			free( narrow_dir_string );
		}
		new_dir= (wchar_t *)malloc( sizeof(wchar_t)*(base_len+ln+2)  );

		wc_str = wc_end?wcsndup(wc, wc_end-wc):wcsdup(wc);

		if( (!new_dir) || (!wc_str) )
		{
			DIE_MEM();
		}

		wcscpy( new_dir, base_dir );
		
        wcstring next;
		while (wreaddir(dir, next))
		{
			const wchar_t *name = next.c_str();
			
			/*
			  Test if the file/directory name matches the whole
			  wildcard element, i.e. regular matching.
			*/
			int whole_match = wildcard_match2( name, wc_str, 1 );
			int partial_match = 0;
			
			/* 
			   If we are doing recursive matching, also check if this
			   directory matches the part up to the recusrive
			   wildcard, if so, then we can search all subdirectories
			   for matches.
			*/
			if( is_recursive )
			{
				const wchar_t *end = wcschr( wc, ANY_STRING_RECURSIVE );
				wchar_t *wc_sub = wcsndup( wc, end-wc+1);
				partial_match = wildcard_match2( name, wc_sub, 1 );
				free( wc_sub );
			}			

			if( whole_match || partial_match )
			{
				struct stat buf;			
				char *dir_str;
				int stat_res;
				int new_res;

				wcscpy(&new_dir[base_len], name );
				dir_str = wcs2str( new_dir );
				
				if( dir_str )
				{
					stat_res = stat( dir_str, &buf );
					free( dir_str );
					
					if( !stat_res )
					{
                        // Insert a "file ID" into visited_files
                        // If the insertion fails, we've already visited this file (i.e. a symlink loop)
                        const file_id_t file_id(buf.st_dev, buf.st_ino);
						if( S_ISDIR(buf.st_mode) && visited_files.insert(file_id).second)
						{
							size_t new_len = wcslen( new_dir );
							new_dir[new_len] = L'/';
							new_dir[new_len+1] = L'\0';
							
							/*
							  Regular matching
							*/
							if( whole_match )
							{
								const wchar_t *new_wc = L"";
								if( wc_end )
								{
									new_wc=wc_end+1;
									/*
									  Accept multiple '/' as a single direcotry separator
									*/
									while(*new_wc==L'/')
									{
										new_wc++;
									}
								}
								
								new_res = wildcard_expand_internal( new_wc,
																	new_dir, 
																	flags, 
																	out,
                                                                    completion_set,
                                                                    visited_files );

								if( new_res == -1 )
								{
									res = -1;
									break;
								}								
								res |= new_res;
								
							}
							
							/*
							  Recursive matching
							*/
							if( partial_match )
							{
								
								new_res = wildcard_expand_internal( wcschr( wc, ANY_STRING_RECURSIVE ), 
																	new_dir,
																	flags | WILDCARD_RECURSIVE, 
																	out,
                                                                    completion_set,
                                                                    visited_files);

								if( new_res == -1 )
								{
									res = -1;
									break;
								}								
								res |= new_res;
								
							}
						}								
					}
				}
			}
		}
		
		free( wc_str );
		free( new_dir );
	}
	closedir( dir );

	return res;
}
Ejemplo n.º 30
0
wchar_t *escape( const wchar_t *in_orig, escape_flags_t flags )
{
	const wchar_t *in = in_orig;
	
	bool escape_all = !! (flags & ESCAPE_ALL);
	bool no_quoted  = !! (flags & ESCAPE_NO_QUOTED);
    bool no_tilde = !! (flags & ESCAPE_NO_TILDE);
	
	wchar_t *out;
	wchar_t *pos;

	int need_escape=0;
	int need_complex_escape=0;

	if( !in )
	{
		debug( 0, L"%s called with null input", __func__ );
		FATAL_EXIT();
	}

	if( !no_quoted && (wcslen( in ) == 0) )
	{
		out = wcsdup(L"''");
		if( !out )
			DIE_MEM();
		return out;
	}
	
	
	out = (wchar_t *)malloc( sizeof(wchar_t)*(wcslen(in)*4 + 1));
	pos = out;
	
	if( !out )
		DIE_MEM();
	
	while( *in != 0 )
	{

		if( ( *in >= ENCODE_DIRECT_BASE) &&
			( *in < ENCODE_DIRECT_BASE+256) )
		{
			int val = *in - ENCODE_DIRECT_BASE;
			int tmp;
			
			*(pos++) = L'\\';
			*(pos++) = L'X';
			
			tmp = val/16;			
			*pos++ = tmp > 9? L'a'+(tmp-10):L'0'+tmp;
			
			tmp = val%16;			
			*pos++ = tmp > 9? L'a'+(tmp-10):L'0'+tmp;
			need_escape=need_complex_escape=1;
			
		}
		else
		{
            wchar_t c = *in;
			switch( c )
			{
				case L'\t':
					*(pos++) = L'\\';
					*(pos++) = L't';					
					need_escape=need_complex_escape=1;
					break;
					
				case L'\n':
					*(pos++) = L'\\';
					*(pos++) = L'n';					
					need_escape=need_complex_escape=1;
					break;
					
				case L'\b':
					*(pos++) = L'\\';
					*(pos++) = L'b';					
					need_escape=need_complex_escape=1;
					break;
					
				case L'\r':
					*(pos++) = L'\\';
					*(pos++) = L'r';					
					need_escape=need_complex_escape=1;
					break;
					
				case L'\x1b':
					*(pos++) = L'\\';
					*(pos++) = L'e';					
					need_escape=need_complex_escape=1;
					break;
					

				case L'\\':
				case L'\'':
				{
					need_escape=need_complex_escape=1;
					if( escape_all )
						*pos++ = L'\\';
					*pos++ = *in;
					break;
				}

				case L'&':
				case L'$':
				case L' ':
				case L'#':
				case L'^':
				case L'<':
				case L'>':
				case L'(':
				case L')':
				case L'[':
				case L']':
				case L'{':
				case L'}':
				case L'?':
				case L'*':
				case L'|':
				case L';':
				case L'"':
				case L'%':
				case L'~':
				{
                    if (! no_tilde || c != L'~')
                    {
                        need_escape=1;
                        if( escape_all )
                            *pos++ = L'\\';
                    }
					*pos++ = *in;
					break;
				}
				
				default:
				{
					if( *in < 32 )
					{
						if( *in <27 && *in > 0 )
						{
							*(pos++) = L'\\';
							*(pos++) = L'c';
							*(pos++) = L'a' + *in -1;
							
							need_escape=need_complex_escape=1;
							break;
								
						}
						

						int tmp = (*in)%16;
						*pos++ = L'\\';
						*pos++ = L'x';
						*pos++ = ((*in>15)? L'1' : L'0');
						*pos++ = tmp > 9? L'a'+(tmp-10):L'0'+tmp;
						need_escape=need_complex_escape=1;
					}
					else
					{
						*pos++ = *in;
					}
					break;
				}
			}
		}
		
		in++;
	}
	*pos = 0;

	/*
	  Use quoted escaping if possible, since most people find it
	  easier to read. 
	 */
	if( !no_quoted && need_escape && !need_complex_escape && escape_all )
	{
		free( out );
		out = escape_simple( in_orig );
	}
	
	return out;
}