2006-11-30 00:21:02 +10:00
/** \file complete.c Functions related to tab-completion.
2005-09-20 23:26:39 +10:00
2009-02-03 08:46:45 +10:00
These functions are used for storing and retrieving tab - completion data , as well as for performing tab - completion .
2005-09-20 23:26:39 +10:00
*/
2006-08-11 11:18:35 +10:00
# include "config.h"
2005-09-20 23:26:39 +10:00
# include <stdlib.h>
# include <stdio.h>
# include <limits.h>
# include <string.h>
# include <wchar.h>
# include <wctype.h>
# include <unistd.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <dirent.h>
# include <errno.h>
# include <termios.h>
# include <ctype.h>
# include <pwd.h>
# include <signal.h>
2006-02-09 01:29:09 +10:00
# include <wchar.h>
2005-09-20 23:26:39 +10:00
2006-02-28 23:17:16 +10:00
# include "fallback.h"
2005-09-20 23:26:39 +10:00
# include "util.h"
2006-02-28 23:17:16 +10:00
2005-09-20 23:26:39 +10:00
# include "tokenizer.h"
# include "wildcard.h"
# include "proc.h"
# include "parser.h"
# include "function.h"
# include "complete.h"
# include "builtin.h"
# include "env.h"
# include "exec.h"
# include "expand.h"
# include "common.h"
# include "reader.h"
# include "history.h"
# include "intern.h"
2006-01-31 02:51:50 +10:00
# include "parse_util.h"
2007-04-22 19:50:26 +10:00
# include "parser_keywords.h"
2006-06-13 00:12:33 +10:00
# include "halloc.h"
2006-02-10 01:50:20 +10:00
# include "halloc_util.h"
2005-09-20 23:26:39 +10:00
# include "wutil.h"
2006-10-19 21:50:23 +10:00
# include "path.h"
2005-09-20 23:26:39 +10:00
/*
2006-02-04 07:30:06 +10:00
Completion description strings , mostly for different types of files , such as sockets , block devices , etc .
2005-09-20 23:26:39 +10:00
2006-02-04 07:30:06 +10:00
There are a few more completion description strings defined in
expand . c . Maybe all completion description strings should be defined
in the same file ?
2005-09-20 23:26:39 +10:00
*/
/**
Description for ~ USER completion
*/
2008-01-14 05:32:21 +10:00
# define COMPLETE_USER_DESC _( L"Home for %ls" )
2005-09-20 23:26:39 +10:00
/**
Description for short variables . The value is concatenated to this description
*/
2007-02-17 21:05:55 +10:00
# define COMPLETE_VAR_DESC_VAL _( L"Variable: %ls" )
2005-09-20 23:26:39 +10:00
/**
The maximum number of commands on which to perform description
lookup . The lookup process is quite time consuming , so this should
be set to a pretty low number .
*/
# define MAX_CMD_DESC_LOOKUP 10
/**
Condition cache value returned from hashtable when this condition
2006-02-04 07:30:06 +10:00
has not yet been tested . This value is NULL , so that when the hash
table returns NULL , this wil be seen as an untested condition .
2005-09-20 23:26:39 +10:00
*/
# define CC_NOT_TESTED 0
/**
2006-02-04 07:30:06 +10:00
Condition cache value returned from hashtable when the condition is
met . This can be any value , that is a valid pointer , and that is
different from CC_NOT_TESTED and CC_FALSE .
2005-09-20 23:26:39 +10:00
*/
# define CC_TRUE L"true"
/**
2006-02-04 07:30:06 +10:00
Condition cache value returned from hashtable when the condition is
not met . This can be any value , that is a valid pointer , and that
is different from CC_NOT_TESTED and CC_TRUE .
2005-09-20 23:26:39 +10:00
*/
# define CC_FALSE L"false"
2006-06-08 09:56:01 +10:00
/**
The special cased translation macro for completions . The empty
string needs to be special cased , since it can occur , and should
not be translated . ( Gettext returns the version information as the
response )
*/
2006-11-30 00:18:22 +10:00
# ifdef USE_GETTEXT
2007-10-14 07:26:06 +10:00
# define C_(wstr) ((wcscmp(wstr, L"")==0)?L"":wgettext(wstr))
2006-11-30 00:18:22 +10:00
# else
# define C_(string) (string)
# endif
2006-06-08 09:56:01 +10:00
2009-02-03 08:46:45 +10:00
/**
The maximum amount of time that we ' re willing to spend doing
username tilde completion . This special limit has been coded in
because user lookup can be extremely slow in cases of a humongous
LDAP database . ( Google , I ' m looking at you )
*/
# define MAX_USER_LOOKUP_TIME 0.2
2005-09-20 23:26:39 +10:00
/**
2006-01-31 05:53:10 +10:00
Struct describing a completion option entry .
2005-09-20 23:26:39 +10:00
If short_opt and long_opt are both zero , the comp field must not be
empty and contains a list of arguments to the command .
If either short_opt or long_opt are non - zero , they specify a switch
for the command . If \ c comp is also not empty , it contains a list
2006-02-04 07:30:06 +10:00
of non - switch arguments that may only follow directly after the
2011-12-26 19:18:46 -08:00
specified switch .
2005-09-20 23:26:39 +10:00
*/
typedef struct complete_entry_opt
{
/** Short style option */
wchar_t short_opt ;
/** Long style option */
const wchar_t * long_opt ;
/** Arguments to the option */
const wchar_t * comp ;
/** Description of the completion */
const wchar_t * desc ;
/** Condition under which to use the option */
const wchar_t * condition ;
2006-02-04 07:30:06 +10:00
/** Must be one of the values SHARED, NO_FILES, NO_COMMON,
EXCLUSIVE , and determines how completions should be performed
on the argument after the switch . */
2005-09-20 23:26:39 +10:00
int result_mode ;
/** True if old style long options are used */
int old_mode ;
2006-02-04 07:30:06 +10:00
/** Next option in the linked list */
2005-09-20 23:26:39 +10:00
struct complete_entry_opt * next ;
2007-03-01 07:43:27 +10:00
/** Completion flags */
int flags ;
2005-09-20 23:26:39 +10:00
}
2006-10-24 21:03:52 +10:00
complete_entry_opt_t ;
2005-09-20 23:26:39 +10:00
/**
Struct describing a command completion
*/
typedef struct complete_entry
{
/** True if command is a path */
int cmd_type ;
/** Command string */
const wchar_t * cmd ;
/** String containing all short option characters */
wchar_t * short_opt_str ;
/** Linked list of all options */
2006-10-24 21:03:52 +10:00
complete_entry_opt_t * first_option ;
2005-09-20 23:26:39 +10:00
/** Next command completion in the linked list */
struct complete_entry * next ;
/** True if no other options than the ones supplied are possible */
2007-08-02 03:35:24 +10:00
int authoritative ;
2005-09-20 23:26:39 +10:00
}
2006-10-24 21:03:52 +10:00
complete_entry_t ;
2005-09-20 23:26:39 +10:00
/** First node in the linked list of all completion entries */
2006-10-24 21:03:52 +10:00
static complete_entry_t * first_entry = 0 ;
2005-09-20 23:26:39 +10:00
/**
Table of completions conditions that have already been tested and
the corresponding test results
*/
static hash_table_t * condition_cache = 0 ;
2006-10-24 21:03:52 +10:00
static void complete_free_entry ( complete_entry_t * c ) ;
2006-07-22 20:16:51 +10:00
2007-02-17 21:05:55 +10:00
/**
Create a new completion entry
*/
2012-01-16 22:26:47 +05:30
void completion_allocate ( std : : vector < completion_t > & context ,
2009-02-03 08:46:45 +10:00
const wchar_t * comp ,
const wchar_t * desc ,
int flags )
2007-02-17 21:05:55 +10:00
{
2012-01-16 22:26:47 +05:30
// completion_t *res = (completion_t *)halloc( context, sizeof( completion_t) );
completion_t res ;
res . completion = comp ;
2007-02-24 18:11:31 +10:00
if ( desc )
2012-01-16 22:26:47 +05:30
res . description = desc ;
2007-02-25 20:37:15 +10:00
if ( flags & COMPLETE_AUTO_SPACE )
{
int len = wcslen ( comp ) ;
flags = flags & ( ~ COMPLETE_AUTO_SPACE ) ;
2011-12-26 19:18:46 -08:00
2007-02-25 20:37:15 +10:00
if ( ( len > 0 ) & & ( wcschr ( L " /=@: " , comp [ len - 1 ] ) ! = 0 ) )
flags | = COMPLETE_NO_SPACE ;
2011-12-26 19:18:46 -08:00
2007-02-25 20:37:15 +10:00
}
2011-12-26 19:18:46 -08:00
2012-01-16 22:26:47 +05:30
res . flags = flags ;
context . push_back ( res ) ;
2007-02-17 21:05:55 +10:00
}
2006-07-22 20:16:51 +10:00
/**
Destroys various structures used for tab - completion and free ( ) s the memory used by them .
*/
static void complete_destroy ( )
{
2006-10-24 21:03:52 +10:00
complete_entry_t * i = first_entry , * prev ;
2011-12-26 19:18:46 -08:00
2006-07-22 20:16:51 +10:00
while ( i )
{
prev = i ;
i = i - > next ;
complete_free_entry ( prev ) ;
}
first_entry = 0 ;
2011-12-26 19:18:46 -08:00
2006-07-22 20:16:51 +10:00
parse_util_load_reset ( L " fish_complete_path " , 0 ) ;
2011-12-26 19:18:46 -08:00
2006-07-22 20:16:51 +10:00
}
/**
2007-01-27 12:09:24 +10:00
The init function for the completion code . Currently , all it really
does is make sure complete_destroy is called on exit .
2006-07-22 20:16:51 +10:00
*/
static void complete_init ( )
{
static int is_init = 0 ;
if ( ! is_init )
{
is_init = 1 ;
halloc_register_function_void ( global_context , & complete_destroy ) ;
}
}
2005-09-20 23:26:39 +10:00
/**
This command clears the cache of condition tests created by \ c condition_test ( ) .
*/
static void condition_cache_clear ( )
{
if ( condition_cache )
{
hash_destroy ( condition_cache ) ;
2006-01-31 05:53:10 +10:00
free ( condition_cache ) ;
2005-09-20 23:26:39 +10:00
condition_cache = 0 ;
2006-01-31 05:53:10 +10:00
}
2005-09-20 23:26:39 +10:00
}
/**
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 ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
if ( ! condition | | ! wcslen ( condition ) )
{
// fwprintf( stderr, L"No condition specified\n" );
return 1 ;
}
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
if ( ! condition_cache )
{
2011-12-26 19:18:46 -08:00
condition_cache = ( hash_table_t * ) malloc ( sizeof ( hash_table_t ) ) ;
2005-09-20 23:26:39 +10:00
if ( ! condition_cache )
{
2006-07-03 20:39:57 +10:00
DIE_MEM ( ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
}
2006-01-31 05:53:10 +10:00
hash_init ( condition_cache ,
2005-09-20 23:26:39 +10:00
& hash_wcs_func ,
& hash_wcs_cmp ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
}
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
test_res = hash_get ( condition_cache , condition ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
if ( test_res = = CC_NOT_TESTED )
{
test_res = exec_subshell ( condition , 0 ) ? CC_FALSE : CC_TRUE ;
hash_put ( condition_cache , condition , test_res ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
/*
Restore previous status information
*/
}
2011-12-26 19:18:46 -08:00
if ( wcscmp ( ( const wchar_t * ) test_res , CC_TRUE ) = = 0 )
2005-09-20 23:26:39 +10:00
{
2006-01-31 05:53:10 +10:00
return 1 ;
2005-09-20 23:26:39 +10:00
}
return 0 ;
}
/**
2006-10-24 21:03:52 +10:00
Recursively free all complete_entry_opt_t structs and their contents
2005-09-20 23:26:39 +10:00
*/
2006-10-24 21:03:52 +10:00
static void complete_free_opt_recursive ( complete_entry_opt_t * o )
2005-09-20 23:26:39 +10:00
{
2007-01-28 13:24:16 +10:00
if ( ! o )
return ;
2011-12-26 19:18:46 -08:00
2007-01-28 13:24:16 +10:00
complete_free_opt_recursive ( o - > next ) ;
2006-11-16 23:08:33 +10:00
halloc_free ( o ) ;
2005-09-20 23:26:39 +10:00
}
/**
2006-10-24 21:03:52 +10:00
Free a complete_entry_t and its contents
2005-09-20 23:26:39 +10:00
*/
2006-10-24 21:03:52 +10:00
static void complete_free_entry ( complete_entry_t * c )
2005-09-20 23:26:39 +10:00
{
// free( c->cmd );
free ( c - > short_opt_str ) ;
complete_free_opt_recursive ( c - > first_option ) ;
free ( c ) ;
}
/**
Search for an exactly matching completion entry
*/
2006-10-24 21:03:52 +10:00
static complete_entry_t * complete_find_exact_entry ( const wchar_t * cmd ,
2006-11-30 00:21:02 +10:00
const int cmd_type )
2005-09-20 23:26:39 +10:00
{
2006-10-24 21:03:52 +10:00
complete_entry_t * i ;
2005-09-20 23:26:39 +10:00
for ( i = first_entry ; i ; i = i - > next )
{
if ( ( wcscmp ( cmd , i - > cmd ) = = 0 ) & & ( cmd_type = = i - > cmd_type ) )
{
return i ;
}
}
return 0 ;
}
2007-01-28 23:40:59 +10:00
/**
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 )
2005-09-20 23:26:39 +10:00
{
2006-10-24 21:03:52 +10:00
complete_entry_t * c ;
2006-10-19 21:50:23 +10:00
2006-07-22 20:16:51 +10:00
complete_init ( ) ;
2006-06-08 10:01:45 +10:00
c = complete_find_exact_entry ( cmd , cmd_type ) ;
2005-09-20 23:26:39 +10:00
if ( c = = 0 )
{
2011-12-26 19:18:46 -08:00
if ( ! ( c = ( complete_entry_t * ) malloc ( sizeof ( complete_entry_t ) ) ) )
2006-02-20 04:19:32 +10:00
{
2006-07-03 20:39:57 +10:00
DIE_MEM ( ) ;
2006-02-20 04:19:32 +10:00
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
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 " " ) ;
2007-08-02 03:35:24 +10:00
c - > authoritative = 1 ;
2005-09-20 23:26:39 +10:00
}
2007-01-28 23:40:59 +10:00
return c ;
}
2007-08-02 03:35:24 +10:00
void complete_set_authoritative ( const wchar_t * cmd ,
2009-02-03 08:46:45 +10:00
int cmd_type ,
int authoritative )
2007-01-28 23:40:59 +10:00
{
complete_entry_t * c ;
CHECK ( cmd , ) ;
c = complete_get_exact_entry ( cmd , cmd_type ) ;
2007-08-02 03:35:24 +10:00
c - > authoritative = authoritative ;
2007-01-28 23:40:59 +10:00
}
2007-01-28 13:24:16 +10:00
2007-01-28 23:40:59 +10:00
void complete_add ( const wchar_t * cmd ,
int cmd_type ,
wchar_t short_opt ,
const wchar_t * long_opt ,
int old_mode ,
int result_mode ,
const wchar_t * condition ,
const wchar_t * comp ,
2007-03-01 07:43:27 +10:00
const wchar_t * desc ,
int flags )
2007-01-28 23:40:59 +10:00
{
complete_entry_t * c ;
complete_entry_opt_t * opt ;
CHECK ( cmd , ) ;
c = complete_get_exact_entry ( cmd , cmd_type ) ;
2011-12-26 19:18:46 -08:00
opt = ( complete_entry_opt_t * ) halloc ( 0 , sizeof ( complete_entry_opt_t ) ) ;
2007-01-28 23:40:59 +10:00
opt - > next = c - > first_option ;
c - > first_option = opt ;
if ( short_opt ! = L ' \0 ' )
{
int len = 1 + ( ( result_mode & NO_COMMON ) ! = 0 ) ;
c - > short_opt_str =
2011-12-26 19:18:46 -08:00
( wchar_t * ) realloc ( c - > short_opt_str ,
2007-01-28 23:40:59 +10:00
sizeof ( wchar_t ) * ( wcslen ( c - > short_opt_str ) + 1 + len ) ) ;
wcsncat ( c - > short_opt_str ,
& short_opt , 1 ) ;
if ( len = = 2 )
2006-02-20 04:19:32 +10:00
{
2007-01-28 23:40:59 +10:00
wcscat ( c - > short_opt_str , L " : " ) ;
2006-02-20 04:19:32 +10:00
}
2007-01-28 23:40:59 +10:00
}
2011-12-26 19:18:46 -08:00
2007-01-28 23:40:59 +10:00
opt - > short_opt = short_opt ;
opt - > result_mode = result_mode ;
opt - > old_mode = old_mode ;
2005-09-20 23:26:39 +10:00
2007-01-28 23:40:59 +10:00
opt - > comp = comp ? halloc_wcsdup ( opt , comp ) : L " " ;
opt - > condition = condition ? halloc_wcsdup ( opt , condition ) : L " " ;
opt - > long_opt = long_opt ? halloc_wcsdup ( opt , long_opt ) : L " " ;
2007-03-01 07:43:27 +10:00
opt - > flags = flags ;
2011-12-26 19:18:46 -08:00
2007-01-28 23:40:59 +10:00
if ( desc & & wcslen ( desc ) )
{
opt - > desc = halloc_wcsdup ( opt , desc ) ;
}
else
{
opt - > desc = L " " ;
2006-02-20 04:19:32 +10:00
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
}
2006-12-14 09:58:38 +10:00
/**
Remove all completion options in the specified entry that match the
specified short / long option strings .
*/
static complete_entry_t * complete_remove_entry ( complete_entry_t * e ,
wchar_t short_opt ,
const wchar_t * long_opt )
2005-09-20 23:26:39 +10:00
{
2006-02-20 04:19:32 +10:00
2006-12-14 09:58:38 +10:00
complete_entry_opt_t * o , * oprev = 0 , * onext = 0 ;
2011-12-26 19:18:46 -08:00
2006-12-14 09:58:38 +10:00
if ( ( short_opt = = 0 ) & & ( long_opt = = 0 ) )
2005-09-20 23:26:39 +10:00
{
2006-12-14 09:58:38 +10:00
complete_free_opt_recursive ( e - > first_option ) ;
e - > first_option = 0 ;
}
else
{
2011-12-26 19:18:46 -08:00
2006-12-14 09:58:38 +10:00
for ( o = e - > first_option ; o ; o = onext )
2005-09-20 23:26:39 +10:00
{
2006-12-14 09:58:38 +10:00
onext = o - > next ;
2011-12-26 19:18:46 -08:00
2006-12-14 09:58:38 +10:00
if ( ( short_opt = = o - > short_opt ) | |
( wcscmp ( long_opt , o - > long_opt ) = = 0 ) )
2005-09-20 23:26:39 +10:00
{
2006-12-14 09:58:38 +10:00
wchar_t * pos ;
/* fwprintf( stderr,
2009-02-03 08:46:45 +10:00
L " remove option -%lc --%ls \n " ,
o - > short_opt ? o - > short_opt : L ' ' ,
o - > long_opt ) ;
2006-12-14 09:58:38 +10:00
*/
if ( o - > short_opt )
2005-09-20 23:26:39 +10:00
{
2006-12-14 09:58:38 +10:00
pos = wcschr ( e - > short_opt_str ,
o - > short_opt ) ;
if ( pos )
2005-09-20 23:26:39 +10:00
{
2006-12-14 09:58:38 +10:00
wchar_t * pos2 = pos + 1 ;
while ( * pos2 = = L ' : ' )
2005-09-20 23:26:39 +10:00
{
2006-12-14 09:58:38 +10:00
pos2 + + ;
2005-09-20 23:26:39 +10:00
}
2011-12-26 19:18:46 -08:00
2006-12-14 09:58:38 +10:00
memmove ( pos ,
pos2 ,
sizeof ( wchar_t ) * wcslen ( pos2 ) ) ;
2006-02-23 01:41:52 +10:00
}
2005-09-20 23:26:39 +10:00
}
2011-12-26 19:18:46 -08:00
2006-12-14 09:58:38 +10:00
if ( oprev = = 0 )
2005-09-20 23:26:39 +10:00
{
2006-12-14 09:58:38 +10:00
e - > first_option = o - > next ;
2005-09-20 23:26:39 +10:00
}
else
{
2006-12-14 09:58:38 +10:00
oprev - > next = o - > next ;
2005-09-20 23:26:39 +10:00
}
2006-12-14 09:58:38 +10:00
free ( o ) ;
2005-09-20 23:26:39 +10:00
}
2006-12-14 09:58:38 +10:00
else
{
oprev = o ;
}
}
}
2011-12-26 19:18:46 -08:00
2006-12-14 09:58:38 +10:00
if ( e & & ( e - > first_option = = 0 ) )
{
free ( e - > short_opt_str ) ;
free ( e ) ;
e = 0 ;
}
2011-12-26 19:18:46 -08:00
2006-12-14 09:58:38 +10:00
return e ;
2005-09-20 23:26:39 +10:00
2006-12-14 09:58:38 +10:00
}
void complete_remove ( const wchar_t * cmd ,
int cmd_type ,
wchar_t short_opt ,
const wchar_t * long_opt )
{
complete_entry_t * e , * eprev = 0 , * enext = 0 ;
CHECK ( cmd , ) ;
2011-12-26 19:18:46 -08:00
2006-12-14 09:58:38 +10:00
for ( e = first_entry ; e ; e = enext )
{
enext = e - > next ;
if ( ( cmd_type = = e - > cmd_type ) & &
( wcscmp ( cmd , e - > cmd ) = = 0 ) )
{
e = complete_remove_entry ( e , short_opt , long_opt ) ;
2005-09-20 23:26:39 +10:00
}
if ( e )
2006-02-23 01:41:52 +10:00
{
2005-09-20 23:26:39 +10:00
eprev = e ;
2006-02-23 01:41:52 +10:00
}
2006-12-14 09:58:38 +10:00
else
{
if ( eprev )
{
eprev - > next = enext ;
}
else
{
first_entry = enext ;
}
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
}
}
/**
2006-06-13 00:12:33 +10:00
Find the full path and commandname from a command string . Both
pointers are allocated using halloc and will be free ' d when \ c
context is halloc_free ' d .
2005-09-20 23:26:39 +10:00
*/
2011-12-26 19:18:46 -08:00
static void parse_cmd_string ( void * context ,
2006-06-13 00:12:33 +10:00
const wchar_t * str ,
2005-09-20 23:26:39 +10:00
wchar_t * * pathp ,
wchar_t * * cmdp )
{
wchar_t * cmd , * path ;
/* Get the path of the command */
2006-10-19 21:50:23 +10:00
path = path_get_path ( context , str ) ;
2005-09-20 23:26:39 +10:00
if ( path = = 0 )
2006-02-23 01:41:52 +10:00
{
/**
Use the empty string as the ' path ' for commands that can
not be found .
*/
2006-08-26 12:19:48 +10:00
path = halloc_wcsdup ( context , L " " ) ;
2006-02-23 01:41:52 +10:00
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
/* Make sure the path is not included in the command */
2012-01-05 13:58:48 -08:00
cmd = const_cast < wchar_t * > ( wcsrchr ( str , L ' / ' ) ) ;
2005-09-20 23:26:39 +10:00
if ( cmd ! = 0 )
2006-02-20 04:19:32 +10:00
{
2005-09-20 23:26:39 +10:00
cmd + + ;
2006-02-20 04:19:32 +10:00
}
2005-09-20 23:26:39 +10:00
else
2006-02-20 04:19:32 +10:00
{
2005-09-20 23:26:39 +10:00
cmd = ( wchar_t * ) str ;
2006-02-20 04:19:32 +10:00
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
* pathp = path ;
* cmdp = cmd ;
}
int complete_is_valid_option ( const wchar_t * str ,
const wchar_t * opt ,
2012-01-15 14:24:58 -08:00
array_list_t * errors ,
bool allow_autoload )
2005-09-20 23:26:39 +10:00
{
2006-10-24 21:03:52 +10:00
complete_entry_t * i ;
complete_entry_opt_t * o ;
2005-09-20 23:26:39 +10:00
wchar_t * cmd , * path ;
int found_match = 0 ;
2007-08-02 03:35:24 +10:00
int authoritative = 1 ;
2005-09-20 23:26:39 +10:00
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 ;
2006-02-20 04:19:32 +10:00
2006-06-13 00:12:33 +10:00
void * context ;
2011-12-26 19:18:46 -08:00
2006-06-21 10:48:36 +10:00
CHECK ( str , 0 ) ;
CHECK ( opt , 0 ) ;
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
/*
Check some generic things like - - and - options .
*/
switch ( wcslen ( opt ) )
{
case 0 :
case 1 :
2006-02-20 04:19:32 +10:00
{
2007-03-24 21:14:55 +10:00
return 1 ;
2006-02-20 04:19:32 +10:00
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
case 2 :
2006-02-20 04:19:32 +10:00
{
2005-09-20 23:26:39 +10:00
if ( wcscmp ( L " -- " , opt ) = = 0 )
2006-02-20 04:19:32 +10:00
{
2005-09-20 23:26:39 +10:00
return 1 ;
2006-02-20 04:19:32 +10:00
}
break ;
}
2005-09-20 23:26:39 +10:00
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
if ( opt [ 0 ] ! = L ' - ' )
{
if ( errors )
2006-02-20 04:19:32 +10:00
{
2005-09-20 23:26:39 +10:00
al_push ( errors , wcsdup ( L " Option does not begin with a '-' " ) ) ;
2006-02-20 04:19:32 +10:00
}
2005-09-20 23:26:39 +10:00
return 0 ;
}
2006-01-31 05:53:10 +10:00
2006-06-13 00:12:33 +10:00
context = halloc ( 0 , 0 ) ;
2011-12-26 19:18:46 -08:00
if ( ! ( short_validated = ( char * ) halloc ( context , wcslen ( opt ) ) ) )
2006-02-23 01:41:52 +10:00
{
2006-07-03 20:39:57 +10:00
DIE_MEM ( ) ;
2006-02-23 01:41:52 +10:00
}
2011-12-26 19:18:46 -08:00
2006-06-13 00:12:33 +10:00
2005-09-20 23:26:39 +10:00
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 )
{
2012-01-05 13:58:48 -08:00
const wchar_t * opt_end = wcschr ( opt , L ' = ' ) ;
2005-09-20 23:26:39 +10:00
if ( opt_end )
2006-02-20 04:19:32 +10:00
{
2005-09-20 23:26:39 +10:00
gnu_opt_len = ( opt_end - opt ) - 2 ;
2006-02-20 04:19:32 +10:00
}
2005-09-20 23:26:39 +10:00
else
2006-02-20 04:19:32 +10:00
{
2005-09-20 23:26:39 +10:00
gnu_opt_len = wcslen ( opt ) - 2 ;
2006-02-20 04:19:32 +10:00
}
2005-09-20 23:26:39 +10:00
}
2011-12-26 19:18:46 -08:00
2006-06-13 00:12:33 +10:00
parse_cmd_string ( context , str , & path , & cmd ) ;
2005-09-20 23:26:39 +10:00
/*
Make sure completions are loaded for the specified command
*/
2012-01-15 14:24:58 -08:00
if ( allow_autoload ) complete_load ( cmd , 0 ) ;
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
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 ) )
2006-02-23 01:41:52 +10:00
{
2005-09-20 23:26:39 +10:00
continue ;
2006-02-23 01:41:52 +10:00
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
found_match = 1 ;
2007-08-02 03:35:24 +10:00
if ( ! i - > authoritative )
2005-09-20 23:26:39 +10:00
{
2007-08-02 03:35:24 +10:00
authoritative = 0 ;
2005-09-20 23:26:39 +10:00
break ;
}
if ( is_gnu_opt )
{
for ( o = i - > first_option ; o ; o = o - > next )
{
if ( o - > old_mode )
2006-02-23 01:41:52 +10:00
{
2005-09-20 23:26:39 +10:00
continue ;
2006-02-23 01:41:52 +10:00
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
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 ) )
2006-02-23 01:41:52 +10:00
{
2005-09-20 23:26:39 +10:00
is_gnu_exact = 1 ;
2006-02-23 01:41:52 +10:00
}
2005-09-20 23:26:39 +10:00
}
}
}
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 ;
}
}
}
}
}
2007-08-02 03:35:24 +10:00
if ( authoritative )
2005-09-20 23:26:39 +10:00
{
if ( ! is_gnu_opt & & ! is_old_opt )
is_short_opt = 1 ;
if ( is_short_opt )
{
2012-01-13 23:44:18 -08:00
size_t j ;
2005-09-20 23:26:39 +10:00
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 ,
2007-09-29 07:32:27 +10:00
wcsdupcat ( _ ( L " Unknown option: " ) , L " ' " , str , L " ' " ) ) ;
2005-09-20 23:26:39 +10:00
}
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 ,
2007-09-29 07:32:27 +10:00
wcsdupcat ( _ ( L " Unknown option: " ) , L " ' " , opt , L " \' " ) ) ;
2005-09-20 23:26:39 +10:00
}
else
{
al_push ( errors ,
2007-09-29 07:32:27 +10:00
wcsdupcat ( _ ( L " Multiple matches for option: " ) , L " ' " , opt , L " \' " ) ) ;
2005-09-20 23:26:39 +10:00
}
}
}
}
hash_destroy ( & gnu_match_hash ) ;
2006-06-13 00:12:33 +10:00
halloc_free ( context ) ;
2012-01-15 14:24:58 -08:00
return ( authoritative & & found_match ) ? opt_found : 1 ;
2005-09-20 23:26:39 +10:00
}
int complete_is_valid_argument ( const wchar_t * str ,
const wchar_t * opt ,
const wchar_t * arg )
{
return 1 ;
}
/**
Copy any strings in possible_comp which have the specified prefix
2007-02-19 09:25:20 +10:00
to the list comp_out . The prefix may contain wildcards . The output
will consist of completion_t structs .
2005-09-20 23:26:39 +10:00
There are three ways to specify descriptions for each
completion . Firstly , if a description has already been added to the
completion , it is _not_ replaced . Secondly , if the desc_func
function is specified , use it to determine a dynamic
completion . Thirdly , if none of the above are available , the desc
string is used as a description .
\ param comp_out the destination list
\ param wc_escaped the prefix , possibly containing wildcards . The wildcard should not have been unescaped , i . e . ' * ' should be used for any string , not the ANY_STRING character .
2006-01-10 03:17:25 +10:00
\ param desc the default description , used for completions with no embedded description . The description _may_ contain a COMPLETE_SEP character , if not , one will be prefixed to it
2005-09-20 23:26:39 +10:00
\ param desc_func the function that generates a description for those completions witout an embedded description
\ param possible_comp the list of possible completions to iterate over
*/
2007-02-17 21:05:55 +10:00
2012-01-16 22:26:47 +05:30
static void complete_strings ( std : : vector < completion_t > & comp_out ,
2007-02-17 21:05:55 +10:00
const wchar_t * wc_escaped ,
const wchar_t * desc ,
const wchar_t * ( * desc_func ) ( const wchar_t * ) ,
2012-01-16 22:26:47 +05:30
std : : vector < completion_t > & possible_comp ,
2007-02-25 20:37:15 +10:00
int flags )
2007-02-17 21:05:55 +10:00
{
int i ;
wchar_t * wc , * tmp ;
tmp = expand_one ( 0 ,
wcsdup ( wc_escaped ) , EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_WILDCARDS ) ;
if ( ! tmp )
return ;
wc = parse_util_unescape_wildcards ( tmp ) ;
free ( tmp ) ;
2011-12-26 19:18:46 -08:00
2012-01-16 22:26:47 +05:30
for ( i = 0 ; i < possible_comp . size ( ) ; i + + )
2007-02-17 21:05:55 +10:00
{
2012-01-16 22:26:47 +05:30
wcstring temp = possible_comp . at ( i ) . completion ;
const wchar_t * next_str = temp . empty ( ) ? NULL : temp . c_str ( ) ;
2007-04-21 05:55:06 +10:00
2007-02-17 21:05:55 +10:00
if ( next_str )
2007-02-24 18:11:31 +10:00
{
2007-02-25 20:37:15 +10:00
wildcard_complete ( next_str , wc , desc , desc_func , comp_out , flags ) ;
2007-02-24 18:11:31 +10:00
}
2007-02-17 21:05:55 +10:00
}
free ( wc ) ;
}
2005-09-20 23:26:39 +10:00
/**
If command to complete is short enough , substitute
the description with the whatis information for the executable .
*/
2012-01-16 22:26:47 +05:30
static void complete_cmd_desc ( const wchar_t * cmd , std : : vector < completion_t > & comp )
2005-09-20 23:26:39 +10:00
{
int i ;
const wchar_t * cmd_start ;
int cmd_len ;
2006-02-04 03:27:36 +10:00
wchar_t * lookup_cmd = 0 ;
2005-09-20 23:26:39 +10:00
array_list_t list ;
hash_table_t lookup ;
wchar_t * esc ;
2006-11-16 23:04:00 +10:00
int skip ;
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
if ( ! cmd )
return ;
2006-01-10 03:17:25 +10:00
cmd_start = wcsrchr ( cmd , L ' / ' ) ;
if ( cmd_start )
cmd_start + + ;
else
2005-09-20 23:26:39 +10:00
cmd_start = cmd ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
cmd_len = wcslen ( cmd_start ) ;
/*
Using apropos with a single - character search term produces far
to many results - require at least two characters if we don ' t
know the location of the whatis - database .
*/
2006-02-04 03:27:36 +10:00
if ( cmd_len < 2 )
2005-09-20 23:26:39 +10:00
return ;
if ( wildcard_has ( cmd_start , 0 ) )
{
return ;
}
2006-01-31 05:53:10 +10:00
2006-11-16 23:04:00 +10:00
skip = 1 ;
2011-12-26 19:18:46 -08:00
2012-01-16 22:26:47 +05:30
for ( i = 0 ; i < comp . size ( ) ; i + + )
2006-11-16 23:04:00 +10:00
{
2012-01-16 22:26:47 +05:30
const completion_t & c = comp . at ( i ) ;
2011-12-26 19:18:46 -08:00
2012-01-16 22:26:47 +05:30
if ( c . completion . empty ( ) | | ( c . completion [ c . completion . size ( ) - 1 ] ! = L ' / ' ) )
2006-11-16 23:04:00 +10:00
{
skip = 0 ;
break ;
}
2011-12-26 19:18:46 -08:00
2006-11-16 23:04:00 +10:00
}
2011-12-26 19:18:46 -08:00
2006-11-16 23:04:00 +10:00
if ( skip )
{
return ;
}
2011-12-26 19:18:46 -08:00
2006-11-16 23:04:00 +10:00
2006-02-07 01:15:52 +10:00
esc = escape ( cmd_start , 1 ) ;
2006-01-31 05:53:10 +10:00
2006-02-04 03:27:36 +10:00
lookup_cmd = wcsdupcat ( L " __fish_describe_command " , esc ) ;
2006-01-31 05:53:10 +10:00
free ( esc ) ;
2006-01-08 12:56:56 +10:00
al_init ( & list ) ;
hash_init ( & lookup , & hash_wcs_func , & hash_wcs_cmp ) ;
2005-09-20 23:26:39 +10:00
2006-02-09 01:29:09 +10:00
2006-01-08 12:56:56 +10:00
/*
First locate a list of possible descriptions using a single
call to apropos or a direct search if we know the location
of the whatis database . This can take some time on slower
systems with a large set of manuals , but it should be ok
since apropos is only called once .
*/
2006-10-20 01:36:03 +10:00
if ( exec_subshell ( lookup_cmd , & list ) ! = - 1 )
{
2011-12-26 19:18:46 -08:00
2006-10-20 01:36:03 +10:00
/*
Then discard anything that is not a possible completion and put
the result into a hashtable with the completion as key and the
description as value .
2006-01-31 05:53:10 +10:00
2006-10-20 01:36:03 +10:00
Should be reasonably fast , since no memory allocations are needed .
*/
for ( i = 0 ; i < al_get_count ( & list ) ; i + + )
{
wchar_t * el = ( wchar_t * ) al_get ( & list , i ) ;
wchar_t * key , * key_end , * val_begin ;
2011-12-26 19:18:46 -08:00
2006-10-20 01:36:03 +10:00
if ( ! el )
continue ;
2006-01-31 05:53:10 +10:00
2006-10-20 01:36:03 +10:00
key = el + wcslen ( cmd_start ) ;
2007-01-08 00:24:30 +10:00
key_end = wcschr ( key , L ' \t ' ) ;
2006-10-19 01:50:59 +10:00
2006-10-20 01:36:03 +10:00
if ( ! key_end )
continue ;
2011-12-26 19:18:46 -08:00
2006-10-20 01:36:03 +10:00
* key_end = 0 ;
val_begin = key_end + 1 ;
2007-01-08 00:24:30 +10:00
2006-10-20 01:36:03 +10:00
/*
And once again I make sure the first character is uppercased
because I like it that way , and I get to decide these
things .
*/
val_begin [ 0 ] = towupper ( val_begin [ 0 ] ) ;
2011-12-26 19:18:46 -08:00
2006-10-20 01:36:03 +10:00
hash_put ( & lookup , key , val_begin ) ;
}
2006-01-08 12:56:56 +10:00
2006-10-20 01:36:03 +10:00
/*
Then do a lookup on every completion and if a match is found ,
change to the new description .
2006-01-31 05:53:10 +10:00
2006-10-20 01:36:03 +10:00
This needs to do a reallocation for every description added , but
there shouldn ' t be that many completions , so it should be ok .
*/
2012-01-16 22:26:47 +05:30
for ( i = 0 ; i < comp . size ( ) ; i + + )
2006-10-20 01:36:03 +10:00
{
2012-01-16 22:26:47 +05:30
completion_t & c = comp . at ( i ) ;
const wchar_t * el = c . completion . empty ( ) ? NULL : c . completion . c_str ( ) ;
2011-12-26 19:18:46 -08:00
2007-02-19 09:25:20 +10:00
wchar_t * new_desc ;
2011-12-26 19:18:46 -08:00
2006-10-20 01:36:03 +10:00
new_desc = ( wchar_t * ) hash_get ( & lookup ,
el ) ;
2011-12-26 19:18:46 -08:00
2006-10-20 01:36:03 +10:00
if ( new_desc )
{
2012-01-16 22:26:47 +05:30
c . description = new_desc ;
2006-10-20 01:36:03 +10:00
}
2005-09-20 23:26:39 +10:00
}
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
hash_destroy ( & lookup ) ;
2006-01-31 05:53:10 +10:00
al_foreach ( & list ,
2006-06-13 07:47:42 +10:00
& free ) ;
2006-01-31 05:53:10 +10:00
al_destroy ( & list ) ;
2006-02-04 03:27:36 +10:00
free ( lookup_cmd ) ;
2005-09-20 23:26:39 +10:00
}
2006-06-17 23:07:08 +10:00
/**
Returns a description for the specified function
*/
2006-01-24 09:32:13 +10:00
static const wchar_t * complete_function_desc ( const wchar_t * fn )
{
const wchar_t * res = function_get_desc ( fn ) ;
if ( ! res )
res = function_get_definition ( fn ) ;
return res ;
}
2005-09-20 23:26:39 +10:00
/**
Complete the specified command name . Search for executables in the
path , executables defined using an absolute path , functions ,
builtins and directories for implicit cd commands .
\ param cmd the command string to find completions for
2006-01-24 09:33:47 +10:00
\ param comp the list to add all completions to
2005-09-20 23:26:39 +10:00
*/
static void complete_cmd ( const wchar_t * cmd ,
2012-01-16 22:26:47 +05:30
std : : vector < completion_t > & comp ,
2009-02-03 08:46:45 +10:00
int use_function ,
int use_builtin ,
int use_command )
2005-09-20 23:26:39 +10:00
{
wchar_t * path_cpy ;
wchar_t * nxt_path ;
wchar_t * state ;
2012-01-16 22:26:47 +05:30
std : : vector < completion_t > possible_comp ;
2005-09-20 23:26:39 +10:00
wchar_t * nxt_completion ;
2006-01-31 05:53:10 +10:00
2012-01-14 02:42:17 -08:00
const env_var_t cdpath = env_get_string ( L " CDPATH " ) ;
2012-01-10 01:40:03 +05:30
// wchar_t *cdpath_cpy = wcsdup( cdpath?cdpath:L"." );
2012-01-14 02:42:17 -08:00
wchar_t * cdpath_cpy = wcsdup ( ! cdpath . missing ( ) ? cdpath . c_str ( ) : L " . " ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
if ( ( wcschr ( cmd , L ' / ' ) ! = 0 ) | | ( cmd [ 0 ] = = L ' ~ ' ) )
{
2006-01-31 05:53:10 +10:00
2006-12-14 10:03:26 +10:00
if ( use_command )
2005-12-04 02:43:56 +10:00
{
2011-12-26 19:18:46 -08:00
2012-01-16 22:26:47 +05:30
if ( expand_string2 ( wcsdup ( cmd ) ,
2007-02-19 09:25:20 +10:00
comp ,
2006-12-14 10:03:26 +10:00
ACCEPT_INCOMPLETE | EXECUTABLES_ONLY ) ! = EXPAND_ERROR )
{
2007-02-19 09:25:20 +10:00
complete_cmd_desc ( cmd , comp ) ;
2006-12-14 10:03:26 +10:00
}
2005-12-04 02:43:56 +10:00
}
2005-09-20 23:26:39 +10:00
}
else
{
2006-12-14 10:03:26 +10:00
if ( use_command )
2005-09-20 23:26:39 +10:00
{
2011-12-26 19:18:46 -08:00
2012-01-14 02:42:17 -08:00
const env_var_t path = env_get_string ( L " PATH " ) ;
if ( ! path . missing ( ) )
2005-09-20 23:26:39 +10:00
{
2011-12-26 19:18:46 -08:00
2012-01-10 01:40:03 +05:30
path_cpy = wcsdup ( path . c_str ( ) ) ;
2011-12-26 19:18:46 -08:00
2006-12-14 10:03:26 +10:00
for ( nxt_path = wcstok ( path_cpy , ARRAY_SEP_STR , & state ) ;
2007-10-28 19:06:05 +10:00
nxt_path ! = 0 ;
nxt_path = wcstok ( 0 , ARRAY_SEP_STR , & state ) )
2006-12-14 10:03:26 +10:00
{
2007-10-28 19:06:05 +10:00
int prev_count ;
int i ;
int path_len = wcslen ( nxt_path ) ;
int add_slash ;
2011-12-26 19:18:46 -08:00
2007-10-28 19:06:05 +10:00
if ( ! path_len )
{
continue ;
}
2011-12-26 19:18:46 -08:00
2007-10-28 19:06:05 +10:00
add_slash = nxt_path [ path_len - 1 ] ! = L ' / ' ;
2007-09-29 07:32:27 +10:00
nxt_completion = wcsdupcat ( nxt_path ,
2009-02-03 08:46:45 +10:00
add_slash ? L " / " : L " " ,
cmd ) ;
2006-12-14 10:03:26 +10:00
if ( ! nxt_completion )
continue ;
2011-12-26 19:18:46 -08:00
2012-01-16 22:26:47 +05:30
prev_count = comp . size ( ) ;
2011-12-26 19:18:46 -08:00
2012-01-16 22:26:47 +05:30
if ( expand_string2 (
2009-02-03 08:46:45 +10:00
nxt_completion ,
comp ,
ACCEPT_INCOMPLETE |
EXECUTABLES_ONLY ) ! = EXPAND_ERROR )
2006-10-05 07:42:04 +10:00
{
2012-01-16 22:26:47 +05:30
for ( i = prev_count ; i < comp . size ( ) ; i + + )
2007-10-28 19:06:05 +10:00
{
2012-01-16 22:26:47 +05:30
completion_t & c = comp . at ( i ) ;
if ( c . flags & COMPLETE_NO_CASE )
2007-10-28 19:06:05 +10:00
{
2012-01-16 22:26:47 +05:30
c . completion + = add_slash ;
2007-10-28 19:06:05 +10:00
}
}
2006-10-05 07:42:04 +10:00
}
2006-12-14 10:03:26 +10:00
}
free ( path_cpy ) ;
2007-03-18 08:56:11 +10:00
complete_cmd_desc ( cmd , comp ) ;
2006-10-19 01:51:51 +10:00
}
2005-09-20 23:26:39 +10:00
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
/*
These return the original strings - don ' t free them
*/
2012-01-16 22:26:47 +05:30
// al_init( &possible_comp );
2006-12-14 10:03:26 +10:00
if ( use_function )
{
2012-01-13 23:44:18 -08:00
//function_get_names( &possible_comp, cmd[0] == L'_' );
wcstring_list_t names = function_get_names ( cmd [ 0 ] = = L ' _ ' ) ;
for ( size_t i = 0 ; i < names . size ( ) ; i + + ) {
2012-01-16 23:54:57 +05:30
completion_t data_to_push ;
data_to_push . completion = names . at ( i ) ;
2012-01-16 22:26:47 +05:30
possible_comp . push_back ( data_to_push ) ;
2012-01-13 23:44:18 -08:00
}
2012-01-16 22:26:47 +05:30
complete_strings ( comp , cmd , 0 , & complete_function_desc , possible_comp , 0 ) ;
2006-12-14 10:03:26 +10:00
}
2012-01-16 22:26:47 +05:30
possible_comp . clear ( ) ;
2011-12-26 19:18:46 -08:00
2006-12-14 10:03:26 +10:00
if ( use_builtin )
{
2012-01-16 22:26:47 +05:30
builtin_get_names2 ( possible_comp ) ;
complete_strings ( comp , cmd , 0 , & builtin_get_desc , possible_comp , 0 ) ;
2006-12-14 10:03:26 +10:00
}
2012-01-16 22:26:47 +05:30
// al_destroy( &possible_comp );
2005-09-20 23:26:39 +10:00
}
2006-12-14 10:03:26 +10:00
if ( use_builtin | | ( use_function & & function_exists ( L " cd " ) ) )
2006-02-09 01:29:09 +10:00
{
2006-12-14 10:03:26 +10:00
/*
Tab complete implicit cd for directories in CDPATH
*/
if ( cmd [ 0 ] ! = L ' / ' & & ( wcsncmp ( cmd , L " ./ " , 2 ) ! = 0 ) )
2005-09-20 23:26:39 +10:00
{
2006-12-14 10:03:26 +10:00
for ( nxt_path = wcstok ( cdpath_cpy , ARRAY_SEP_STR , & state ) ;
nxt_path ! = 0 ;
nxt_path = wcstok ( 0 , ARRAY_SEP_STR , & state ) )
2006-02-20 04:19:32 +10:00
{
2006-12-14 10:03:26 +10:00
wchar_t * nxt_completion =
2007-09-29 07:32:27 +10:00
wcsdupcat ( nxt_path ,
2009-02-03 08:46:45 +10:00
( nxt_path [ wcslen ( nxt_path ) - 1 ] = = L ' / ' ? L " " : L " / " ) ,
cmd ) ;
2006-12-14 10:03:26 +10:00
if ( ! nxt_completion )
{
continue ;
}
2011-12-26 19:18:46 -08:00
2012-01-16 22:26:47 +05:30
if ( expand_string2 ( nxt_completion ,
2007-02-19 09:25:20 +10:00
comp ,
2006-12-14 10:03:26 +10:00
ACCEPT_INCOMPLETE | DIRECTORIES_ONLY ) ! = EXPAND_ERROR )
{
}
2005-09-20 23:26:39 +10:00
}
2006-02-20 04:19:32 +10:00
}
2005-09-20 23:26:39 +10:00
}
free ( cdpath_cpy ) ;
}
/**
Evaluate the argument list ( as supplied by complete - a ) and insert
2006-01-10 03:40:32 +10:00
any return matching completions . Matching is done using \ c
2005-09-20 23:26:39 +10:00
copy_strings_with_prefix , meaning the completion may contain
wildcards . Logically , this is not always the right thing to do , but
2006-01-10 03:40:32 +10:00
I have yet to come up with a case where this matters .
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
\ param str The string to complete .
\ param args The list of option arguments to be evaluated .
\ param desc Description of the completion
\ param comp_out The list into which the results will be inserted
*/
static void complete_from_args ( const wchar_t * str ,
const wchar_t * args ,
const wchar_t * desc ,
2012-01-16 22:26:47 +05:30
std : : vector < completion_t > & comp_out ,
2007-03-01 07:43:27 +10:00
int flags )
2005-09-20 23:26:39 +10:00
{
2006-01-31 05:53:10 +10:00
2012-01-16 22:26:47 +05:30
std : : vector < completion_t > possible_comp ;
2006-01-31 05:53:10 +10:00
2012-01-16 22:26:47 +05:30
// al_init( &possible_comp );
2005-09-20 23:26:39 +10:00
2006-02-16 23:36:32 +10:00
proc_push_interactive ( 0 ) ;
2012-01-16 22:26:47 +05:30
eval_args ( args , possible_comp ) ;
2006-02-16 23:36:32 +10:00
proc_pop_interactive ( ) ;
2011-12-26 19:18:46 -08:00
2012-01-16 22:26:47 +05:30
complete_strings ( comp_out , str , desc , 0 , possible_comp , flags ) ;
2005-09-20 23:26:39 +10:00
2012-01-16 22:26:47 +05:30
// al_foreach( &possible_comp, &free );
// al_destroy( &possible_comp );
2005-09-20 23:26:39 +10:00
}
/**
Match against an old style long option
*/
2006-10-24 21:03:52 +10:00
static int param_match_old ( complete_entry_opt_t * e ,
2007-01-19 02:45:28 +10:00
const wchar_t * optstr )
2005-09-20 23:26:39 +10:00
{
return ( optstr [ 0 ] = = L ' - ' ) & & ( wcscmp ( e - > long_opt , & optstr [ 1 ] ) = = 0 ) ;
}
/**
Match a parameter
*/
2007-01-19 02:45:28 +10:00
static int param_match ( const complete_entry_opt_t * e ,
const wchar_t * optstr )
2005-09-20 23:26:39 +10:00
{
if ( e - > short_opt ! = L ' \0 ' & &
e - > short_opt = = optstr [ 1 ] )
return 1 ;
if ( ! e - > old_mode & & ( wcsncmp ( L " -- " , optstr , 2 ) = = 0 ) )
{
if ( wcscmp ( e - > long_opt , & optstr [ 2 ] ) = = 0 )
{
return 1 ;
}
}
return 0 ;
}
/**
Test if a string is an option with an argument , like - - color = auto or - I / usr / include
*/
2007-01-19 02:45:28 +10:00
static wchar_t * param_match2 ( const complete_entry_opt_t * e ,
const wchar_t * optstr )
2005-09-20 23:26:39 +10:00
{
if ( e - > short_opt ! = L ' \0 ' & & e - > short_opt = = optstr [ 1 ] )
2007-01-19 02:45:28 +10:00
return ( wchar_t * ) & optstr [ 2 ] ;
2005-09-20 23:26:39 +10:00
if ( ! e - > old_mode & & ( wcsncmp ( L " -- " , optstr , 2 ) = = 0 ) )
{
int len = wcslen ( e - > long_opt ) ;
if ( wcsncmp ( e - > long_opt , & optstr [ 2 ] , len ) = = 0 )
{
if ( optstr [ len + 2 ] = = L ' = ' )
2007-01-19 02:45:28 +10:00
return ( wchar_t * ) & optstr [ len + 3 ] ;
2005-09-20 23:26:39 +10:00
}
}
return 0 ;
}
/**
Tests whether a short option is a viable completion
*/
2007-01-19 02:45:28 +10:00
static int short_ok ( const wchar_t * arg ,
2005-09-20 23:26:39 +10:00
wchar_t nextopt ,
2007-01-19 02:45:28 +10:00
const wchar_t * allopt )
2005-09-20 23:26:39 +10:00
{
2007-01-19 02:45:28 +10:00
const wchar_t * ptr ;
2005-09-20 23:26:39 +10:00
if ( arg [ 0 ] ! = L ' - ' )
return arg [ 0 ] = = L ' \0 ' ;
if ( arg [ 1 ] = = L ' - ' )
return 0 ;
if ( wcschr ( arg , nextopt ) ! = 0 )
return 0 ;
for ( ptr = arg + 1 ; * ptr ; ptr + + )
{
2012-01-05 13:58:48 -08:00
const wchar_t * tmp = wcschr ( allopt , * ptr ) ;
2005-09-20 23:26:39 +10:00
/* Unknown option */
if ( tmp = = 0 )
{
/*fwprintf( stderr, L"Unknown option %lc", *ptr );*/
return 0 ;
}
if ( * ( tmp + 1 ) = = L ' : ' )
{
/* fwprintf( stderr, L"Woot %ls", allopt );*/
return 0 ;
}
}
return 1 ;
}
2006-06-17 23:07:08 +10:00
/**
This is an event handler triggered when the definition of a
specifiec function is changed . It automatcally removes the
specified function .
This is to make sure that the function disappears if the file is
removed or if ti contains a syntax error .
*/
2006-02-08 19:20:05 +10:00
static void complete_load_handler ( const wchar_t * cmd )
2005-09-20 23:26:39 +10:00
{
2006-02-08 19:20:05 +10:00
complete_remove ( cmd , COMMAND , 0 , 0 ) ;
2005-09-20 23:26:39 +10:00
}
2006-02-08 19:20:05 +10:00
void complete_load ( const wchar_t * name , int reload )
{
2006-10-19 01:51:51 +10:00
CHECK ( name , ) ;
2011-12-26 19:18:46 -08:00
parse_util_load ( name ,
2006-07-13 00:22:42 +10:00
L " fish_complete_path " ,
2011-12-26 19:18:46 -08:00
& complete_load_handler ,
reload ) ;
2006-02-08 19:20:05 +10:00
}
2005-09-20 23:26:39 +10:00
/**
Find completion for the argument str of command cmd_orig with
previous option popt . Insert results into comp_out . Return 0 if file
completion should be disabled , 1 otherwise .
*/
2007-01-19 02:45:28 +10:00
static int complete_param ( const wchar_t * cmd_orig ,
const wchar_t * popt ,
const wchar_t * str ,
2007-01-10 22:45:28 +10:00
int use_switches ,
2012-01-16 22:26:47 +05:30
std : : vector < completion_t > & comp_out )
2005-09-20 23:26:39 +10:00
{
2006-10-24 21:03:52 +10:00
complete_entry_t * i ;
complete_entry_opt_t * o ;
2005-09-20 23:26:39 +10:00
array_list_t matches ;
wchar_t * cmd , * path ;
int use_common = 1 , use_files = 1 ;
2006-06-13 00:12:33 +10:00
void * context = halloc ( 0 , 0 ) ;
2011-12-26 19:18:46 -08:00
2006-06-13 00:12:33 +10:00
parse_cmd_string ( context , cmd_orig , & path , & cmd ) ;
2005-09-20 23:26:39 +10:00
complete_load ( cmd , 1 ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
al_init ( & matches ) ;
for ( i = first_entry ; i ; i = i - > next )
{
wchar_t * match = i - > cmd_type ? path : cmd ;
if ( ( ( ! wildcard_match ( match , i - > cmd ) ) ) )
{
2007-01-10 22:45:28 +10:00
continue ;
2005-09-20 23:26:39 +10:00
}
2011-12-26 19:18:46 -08:00
2007-01-10 22:45:28 +10:00
use_common = 1 ;
if ( use_switches )
2005-09-20 23:26:39 +10:00
{
2011-12-26 19:18:46 -08:00
2007-01-10 22:45:28 +10:00
if ( str [ 0 ] = = L ' - ' )
2005-09-20 23:26:39 +10:00
{
2007-01-10 22:45:28 +10:00
/* Check if we are entering a combined option and argument
( like - - color = auto or - I / usr / include ) */
for ( o = i - > first_option ; o ; o = o - > next )
2005-09-20 23:26:39 +10:00
{
2007-01-10 22:45:28 +10:00
wchar_t * arg ;
if ( ( arg = param_match2 ( o , str ) ) ! = 0 & & condition_test ( o - > condition ) )
2005-09-20 23:26:39 +10:00
{
use_common & = ( ( o - > result_mode & NO_COMMON ) = = 0 ) ;
use_files & = ( ( o - > result_mode & NO_FILES ) = = 0 ) ;
2007-03-01 07:43:27 +10:00
complete_from_args ( arg , o - > comp , C_ ( o - > desc ) , comp_out , o - > flags ) ;
2005-09-20 23:26:39 +10:00
}
2007-01-10 22:45:28 +10:00
2005-09-20 23:26:39 +10:00
}
}
2007-01-10 22:45:28 +10:00
else if ( popt [ 0 ] = = L ' - ' )
2005-09-20 23:26:39 +10:00
{
2007-01-10 22:45:28 +10:00
/* Set to true if we found a matching old-style switch */
int old_style_match = 0 ;
2011-12-26 19:18:46 -08:00
2007-01-10 22:45:28 +10:00
/*
If we are using old style long options , check for them
first
*/
2005-09-20 23:26:39 +10:00
for ( o = i - > first_option ; o ; o = o - > next )
{
2007-01-10 22:45:28 +10:00
if ( o - > old_mode )
2005-09-20 23:26:39 +10:00
{
2007-01-10 22:45:28 +10:00
if ( param_match_old ( o , popt ) & & condition_test ( o - > condition ) )
{
old_style_match = 1 ;
use_common & = ( ( o - > result_mode & NO_COMMON ) = = 0 ) ;
use_files & = ( ( o - > result_mode & NO_FILES ) = = 0 ) ;
2007-03-01 07:43:27 +10:00
complete_from_args ( str , o - > comp , C_ ( o - > desc ) , comp_out , o - > flags ) ;
2007-01-10 22:45:28 +10:00
}
}
}
2011-12-26 19:18:46 -08:00
2007-01-10 22:45:28 +10:00
/*
No old style option matched , or we are not using old
style options . We check if any short ( or gnu style
options do .
*/
if ( ! old_style_match )
{
for ( o = i - > first_option ; o ; o = o - > next )
{
/*
Gnu - style options with _optional_ arguments must
be specified as a single token , so that it can
be differed from a regular argument .
*/
if ( ! o - > old_mode & & wcslen ( o - > long_opt ) & & ! ( o - > result_mode & NO_COMMON ) )
continue ;
if ( param_match ( o , popt ) & & condition_test ( o - > condition ) )
{
use_common & = ( ( o - > result_mode & NO_COMMON ) = = 0 ) ;
use_files & = ( ( o - > result_mode & NO_FILES ) = = 0 ) ;
2007-03-01 07:43:27 +10:00
complete_from_args ( str , o - > comp , C_ ( o - > desc ) , comp_out , o - > flags ) ;
2006-01-31 05:53:10 +10:00
2007-01-10 22:45:28 +10:00
}
2005-09-20 23:26:39 +10:00
}
}
}
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
if ( use_common )
{
for ( o = i - > first_option ; o ; o = o - > next )
{
/*
If this entry is for the base command ,
check if any of the arguments match
*/
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
if ( ! condition_test ( o - > condition ) )
continue ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
if ( ( o - > short_opt = = L ' \0 ' ) & & ( o - > long_opt [ 0 ] = = L ' \0 ' ) )
{
use_files & = ( ( o - > result_mode & NO_FILES ) = = 0 ) ;
2007-03-01 07:43:27 +10:00
complete_from_args ( str , o - > comp , C_ ( o - > desc ) , comp_out , o - > flags ) ;
2005-09-20 23:26:39 +10:00
}
2011-12-26 19:18:46 -08:00
2007-01-10 22:45:28 +10:00
if ( wcslen ( str ) > 0 & & use_switches )
2005-09-20 23:26:39 +10:00
{
/*
Check if the short style option matches
*/
if ( o - > short_opt ! = L ' \0 ' & &
short_ok ( str , o - > short_opt , i - > short_opt_str ) )
{
2006-06-08 09:56:01 +10:00
const wchar_t * desc = C_ ( o - > desc ) ;
2007-02-18 22:08:41 +10:00
wchar_t completion [ 2 ] ;
completion [ 0 ] = o - > short_opt ;
completion [ 1 ] = 0 ;
2011-12-26 19:18:46 -08:00
2007-02-18 22:08:41 +10:00
completion_allocate ( comp_out , completion , desc , 0 ) ;
2005-09-20 23:26:39 +10:00
}
/*
Check if the long style option matches
*/
if ( o - > long_opt [ 0 ] ! = L ' \0 ' )
{
2007-03-01 07:43:27 +10:00
int match = 0 , match_no_case = 0 ;
2011-12-26 19:18:46 -08:00
2006-06-13 00:12:33 +10:00
string_buffer_t * whole_opt = sb_halloc ( context ) ;
2011-12-26 19:18:46 -08:00
sb_append ( whole_opt , o - > old_mode ? L " - " : L " -- " , o - > long_opt , NULL ) ;
2006-01-31 05:53:10 +10:00
2007-03-01 07:43:27 +10:00
match = wcsncmp ( str , ( wchar_t * ) whole_opt - > buff , wcslen ( str ) ) = = 0 ;
if ( ! match )
{
match_no_case = wcsncasecmp ( str , ( wchar_t * ) whole_opt - > buff , wcslen ( str ) ) = = 0 ;
}
2011-12-26 19:18:46 -08:00
2007-03-01 07:43:27 +10:00
if ( match | | match_no_case )
2006-01-31 05:53:10 +10:00
{
2007-02-18 22:08:41 +10:00
int has_arg = 0 ; /* Does this switch have any known arguments */
int req_arg = 0 ; /* Does this switch _require_ an argument */
2005-09-20 23:26:39 +10:00
2007-03-01 07:43:27 +10:00
int offset = 0 ;
int flags = 0 ;
2011-12-26 19:18:46 -08:00
2007-03-01 07:43:27 +10:00
if ( match )
offset = wcslen ( str ) ;
else
flags = COMPLETE_NO_CASE ;
2006-11-08 04:19:11 +10:00
has_arg = ! ! wcslen ( o - > comp ) ;
req_arg = ( o - > result_mode & NO_COMMON ) ;
2006-01-31 05:53:10 +10:00
2006-11-08 04:19:11 +10:00
if ( ! o - > old_mode & & ( has_arg & & ! req_arg ) )
2005-09-20 23:26:39 +10:00
{
2011-12-26 19:18:46 -08:00
2007-02-18 22:08:41 +10:00
/*
Optional arguments to a switch can
only be handled using the ' = ' , so we
add it as a completion . By default
we avoid using ' = ' and instead rely
on ' - - switch switch - arg ' , since it
is more commonly supported by
homebrew getopt - like functions .
*/
string_buffer_t completion ;
sb_init ( & completion ) ;
2007-03-01 07:43:27 +10:00
2007-02-18 22:08:41 +10:00
sb_printf ( & completion ,
2011-12-26 19:18:46 -08:00
L " %ls= " ,
2007-03-01 07:43:27 +10:00
( ( wchar_t * ) whole_opt - > buff ) + offset ) ;
2011-12-26 19:18:46 -08:00
2007-02-18 22:08:41 +10:00
completion_allocate ( comp_out ,
( wchar_t * ) completion . buff ,
C_ ( o - > desc ) ,
2011-12-26 19:18:46 -08:00
flags ) ;
2007-02-18 22:08:41 +10:00
sb_destroy ( & completion ) ;
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
}
2011-12-26 19:18:46 -08:00
2007-02-18 22:08:41 +10:00
completion_allocate ( comp_out ,
2007-03-01 07:43:27 +10:00
( ( wchar_t * ) whole_opt - > buff ) + offset ,
2007-02-18 22:08:41 +10:00
C_ ( o - > desc ) ,
2007-03-01 07:43:27 +10:00
flags ) ;
2011-12-26 19:18:46 -08:00
}
2005-09-20 23:26:39 +10:00
}
}
}
}
}
2011-12-26 19:18:46 -08:00
2006-06-13 00:12:33 +10:00
halloc_free ( context ) ;
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
return use_files ;
}
/**
Perform file completion on the specified string
*/
2006-01-31 05:53:10 +10:00
static void complete_param_expand ( wchar_t * str ,
2012-01-16 22:26:47 +05:30
std : : vector < completion_t > & comp_out ,
2005-09-20 23:26:39 +10:00
int do_file )
{
wchar_t * comp_str ;
2007-02-19 09:25:20 +10:00
int flags ;
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
if ( ( wcsncmp ( str , L " -- " , 2 ) ) = = 0 & & ( comp_str = wcschr ( str , L ' = ' ) ) )
{
comp_str + + ;
}
else
2006-02-20 04:19:32 +10:00
{
2005-09-20 23:26:39 +10:00
comp_str = str ;
2006-02-20 04:19:32 +10:00
}
2006-10-20 01:36:03 +10:00
2011-12-26 19:18:46 -08:00
flags = EXPAND_SKIP_CMDSUBST |
ACCEPT_INCOMPLETE |
2007-02-19 09:25:20 +10:00
( do_file ? 0 : EXPAND_SKIP_WILDCARDS ) ;
2011-12-26 19:18:46 -08:00
2012-01-16 22:26:47 +05:30
if ( expand_string2 ( wcsdup ( comp_str ) ,
2007-02-19 09:25:20 +10:00
comp_out ,
flags ) = = EXPAND_ERROR )
2006-10-20 01:36:03 +10:00
{
2006-10-24 21:03:52 +10:00
debug ( 3 , L " Error while expanding string '%ls' " , comp_str ) ;
2006-10-20 01:36:03 +10:00
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
}
2007-02-17 21:05:55 +10:00
2005-09-20 23:26:39 +10:00
/**
Complete the specified string as an environment variable
*/
2007-03-25 05:07:38 +10:00
static int complete_variable ( const wchar_t * whole_var ,
2009-02-03 08:46:45 +10:00
int start_offset ,
2012-01-16 22:26:47 +05:30
std : : vector < completion_t > & comp_list )
2005-09-20 23:26:39 +10:00
{
int i ;
2007-03-25 05:07:38 +10:00
const wchar_t * var = & whole_var [ start_offset ] ;
2005-09-20 23:26:39 +10:00
int varlen = wcslen ( var ) ;
int res = 0 ;
array_list_t names ;
al_init ( & names ) ;
env_get_names ( & names , 0 ) ;
for ( i = 0 ; i < al_get_count ( & names ) ; i + + )
{
wchar_t * name = ( wchar_t * ) al_get ( & names , i ) ;
int namelen = wcslen ( name ) ;
2011-12-26 19:18:46 -08:00
int match = 0 , match_no_case = 0 ;
2006-02-20 04:19:32 +10:00
2005-09-20 23:26:39 +10:00
if ( varlen > namelen )
continue ;
2007-03-25 05:07:38 +10:00
match = ( wcsncmp ( var , name , varlen ) = = 0 ) ;
2011-12-26 19:18:46 -08:00
2007-03-25 05:07:38 +10:00
if ( ! match )
{
match_no_case = ( wcsncasecmp ( var , name , varlen ) = = 0 ) ;
}
if ( match | | match_no_case )
2005-09-20 23:26:39 +10:00
{
2012-01-14 02:42:17 -08:00
const env_var_t value_unescaped = env_get_string ( name ) ;
if ( ! value_unescaped . missing ( ) )
2005-09-20 23:26:39 +10:00
{
2007-02-17 21:05:55 +10:00
string_buffer_t desc ;
2007-03-25 05:07:38 +10:00
string_buffer_t comp ;
int flags = 0 ;
int offset = 0 ;
2011-12-26 19:18:46 -08:00
2007-03-25 05:07:38 +10:00
sb_init ( & comp ) ;
if ( match )
{
2011-12-26 19:18:46 -08:00
sb_append ( & comp , & name [ varlen ] ) ;
2007-03-25 05:07:38 +10:00
offset = varlen ;
}
else
{
sb_append_substring ( & comp , whole_var , start_offset ) ;
sb_append ( & comp , name ) ;
2008-01-14 11:00:32 +10:00
flags = COMPLETE_NO_CASE | COMPLETE_DONT_ESCAPE ;
2007-03-25 05:07:38 +10:00
}
2011-12-26 19:18:46 -08:00
2012-01-14 02:42:17 -08:00
wcstring value = expand_escape_variable2 ( value_unescaped ) ;
2007-02-17 21:05:55 +10:00
sb_init ( & desc ) ;
2012-01-14 02:42:17 -08:00
sb_printf ( & desc , COMPLETE_VAR_DESC_VAL , value . c_str ( ) ) ;
2011-12-26 19:18:46 -08:00
completion_allocate ( comp_list ,
2009-02-03 08:46:45 +10:00
( wchar_t * ) comp . buff ,
( wchar_t * ) desc . buff ,
flags ) ;
2007-02-17 21:05:55 +10:00
res = 1 ;
2011-12-26 19:18:46 -08:00
2007-02-17 21:05:55 +10:00
sb_destroy ( & desc ) ;
2007-03-25 05:07:38 +10:00
sb_destroy ( & comp ) ;
2005-09-20 23:26:39 +10:00
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
}
}
al_destroy ( & names ) ;
return res ;
}
/**
2006-02-20 04:19:32 +10:00
Search the specified string for the \ $ sign . If found , try to
2011-12-26 19:18:46 -08:00
complete as an environment variable .
2006-02-20 04:19:32 +10:00
\ return 0 if unable to complete , 1 otherwise
2005-09-20 23:26:39 +10:00
*/
2006-01-31 05:53:10 +10:00
static int try_complete_variable ( const wchar_t * cmd ,
2012-01-16 22:26:47 +05:30
std : : vector < completion_t > & comp )
2005-09-20 23:26:39 +10:00
{
int len = wcslen ( cmd ) ;
int i ;
for ( i = len - 1 ; i > = 0 ; i - - )
{
if ( cmd [ i ] = = L ' $ ' )
{
/* wprintf( L"Var prefix \'%ls\'\n", &cmd[i+1] );*/
2007-03-25 05:07:38 +10:00
return complete_variable ( cmd , i + 1 , comp ) ;
2005-09-20 23:26:39 +10:00
}
if ( ! isalnum ( cmd [ i ] ) & & cmd [ i ] ! = L ' _ ' )
{
return 0 ;
}
}
return 0 ;
}
/**
2006-02-20 04:19:32 +10:00
Try to complete the specified string as a username . This is used by
~ USER type expansion .
2005-09-20 23:26:39 +10:00
2006-02-20 04:19:32 +10:00
\ return 0 if unable to complete , 1 otherwise
*/
2006-01-31 05:53:10 +10:00
static int try_complete_user ( const wchar_t * cmd ,
2012-01-16 22:26:47 +05:30
std : : vector < completion_t > & comp )
2005-09-20 23:26:39 +10:00
{
2009-02-03 08:46:45 +10:00
const wchar_t * first_char = cmd ;
int res = 0 ;
double start_time = timef ( ) ;
2011-12-26 19:18:46 -08:00
2009-02-03 08:46:45 +10:00
if ( * first_char = = L ' ~ ' & & ! wcschr ( first_char , L ' / ' ) )
2005-09-20 23:26:39 +10:00
{
2009-02-03 08:46:45 +10:00
const wchar_t * user_name = first_char + 1 ;
2012-01-05 13:58:48 -08:00
const wchar_t * name_end = wcschr ( user_name , L ' ~ ' ) ;
2009-02-03 08:46:45 +10:00
if ( name_end = = 0 )
2005-09-20 23:26:39 +10:00
{
2009-02-03 08:46:45 +10:00
struct passwd * pw ;
int name_len = wcslen ( user_name ) ;
2011-12-26 19:18:46 -08:00
2009-02-03 08:46:45 +10:00
setpwent ( ) ;
2011-12-26 19:18:46 -08:00
2009-02-03 08:46:45 +10:00
while ( ( pw = getpwent ( ) ) ! = 0 )
2005-09-20 23:26:39 +10:00
{
2009-02-03 08:46:45 +10:00
double current_time = timef ( ) ;
wchar_t * pw_name ;
2005-09-20 23:26:39 +10:00
2011-12-26 19:18:46 -08:00
if ( current_time - start_time > 0.2 )
2009-02-03 08:46:45 +10:00
{
return 1 ;
}
2011-12-26 19:18:46 -08:00
2009-02-03 08:46:45 +10:00
pw_name = str2wcs ( pw - > pw_name ) ;
2005-09-20 23:26:39 +10:00
2009-02-03 08:46:45 +10:00
if ( pw_name )
2005-09-20 23:26:39 +10:00
{
2009-02-03 08:46:45 +10:00
if ( wcsncmp ( user_name , pw_name , name_len ) = = 0 )
2005-09-20 23:26:39 +10:00
{
2009-02-03 08:46:45 +10:00
string_buffer_t desc ;
2011-12-26 19:18:46 -08:00
2009-02-03 08:46:45 +10:00
sb_init ( & desc ) ;
sb_printf ( & desc ,
COMPLETE_USER_DESC ,
pw_name ) ;
2011-12-26 19:18:46 -08:00
completion_allocate ( comp ,
2009-02-03 08:46:45 +10:00
& pw_name [ name_len ] ,
( wchar_t * ) desc . buff ,
COMPLETE_NO_SPACE ) ;
2011-12-26 19:18:46 -08:00
2009-02-03 08:46:45 +10:00
res = 1 ;
2011-12-26 19:18:46 -08:00
2009-02-03 08:46:45 +10:00
sb_destroy ( & desc ) ;
}
else if ( wcsncasecmp ( user_name , pw_name , name_len ) = = 0 )
{
2011-12-26 19:18:46 -08:00
string_buffer_t name ;
string_buffer_t desc ;
2009-02-03 08:46:45 +10:00
sb_init ( & name ) ;
sb_init ( & desc ) ;
sb_printf ( & name ,
L " ~%ls " ,
pw_name ) ;
sb_printf ( & desc ,
COMPLETE_USER_DESC ,
pw_name ) ;
2011-12-26 19:18:46 -08:00
completion_allocate ( comp ,
2009-02-03 08:46:45 +10:00
( wchar_t * ) name . buff ,
( wchar_t * ) desc . buff ,
COMPLETE_NO_CASE | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE ) ;
res = 1 ;
2011-12-26 19:18:46 -08:00
2009-02-03 08:46:45 +10:00
sb_destroy ( & desc ) ;
sb_destroy ( & name ) ;
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
}
2009-02-03 08:46:45 +10:00
free ( pw_name ) ;
2005-09-20 23:26:39 +10:00
}
}
2009-02-03 08:46:45 +10:00
endpwent ( ) ;
2005-09-20 23:26:39 +10:00
}
}
2009-02-03 08:46:45 +10:00
return res ;
}
2007-02-09 19:33:50 +10:00
2012-01-16 22:26:47 +05:30
void complete2 ( const wchar_t * cmd ,
std : : vector < completion_t > & comp )
2005-09-20 23:26:39 +10:00
{
2007-03-25 05:07:38 +10:00
wchar_t * tok_begin , * tok_end , * cmdsubst_begin , * cmdsubst_end , * prev_begin , * prev_end ;
2006-01-31 02:51:50 +10:00
wchar_t * buff ;
2005-09-20 23:26:39 +10:00
tokenizer tok ;
wchar_t * current_token = 0 , * current_command = 0 , * prev_token = 0 ;
int on_command = 0 ;
int pos ;
int done = 0 ;
2006-07-13 00:22:42 +10:00
int cursor_pos ;
2006-12-14 10:03:26 +10:00
int use_command = 1 ;
int use_function = 1 ;
int use_builtin = 1 ;
2007-01-10 22:45:28 +10:00
int had_ddash = 0 ;
2007-03-25 05:07:38 +10:00
2006-06-21 10:48:36 +10:00
CHECK ( cmd , ) ;
2012-01-16 22:26:47 +05:30
// CHECK( comp, );
2006-07-11 08:39:56 +10:00
2006-07-22 20:16:51 +10:00
complete_init ( ) ;
2006-07-11 08:39:56 +10:00
// debug( 1, L"Complete '%ls'", cmd );
2006-07-13 00:22:42 +10:00
cursor_pos = wcslen ( cmd ) ;
2007-03-25 05:07:38 +10:00
parse_util_cmdsubst_extent ( cmd , cursor_pos , & cmdsubst_begin , & cmdsubst_end ) ;
parse_util_token_extent ( cmd , cursor_pos , & tok_begin , & tok_end , & prev_begin , & prev_end ) ;
2005-09-20 23:26:39 +10:00
2007-03-25 05:07:38 +10:00
if ( ! cmdsubst_begin )
2006-01-31 05:53:10 +10:00
done = 1 ;
2005-09-20 23:26:39 +10:00
2007-03-25 05:07:38 +10:00
/**
If we are completing a variable name or a tilde expansion user
name , we do that and return . No need for any other competions .
2005-09-20 23:26:39 +10:00
*/
if ( ! done )
{
2007-03-25 05:07:38 +10:00
if ( try_complete_variable ( tok_begin , comp ) | | try_complete_user ( tok_begin , comp ) )
{
2005-09-20 23:26:39 +10:00
done = 1 ;
2007-03-25 05:07:38 +10:00
}
2005-09-20 23:26:39 +10:00
}
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
if ( ! done )
{
2007-03-25 05:07:38 +10:00
pos = cursor_pos - ( cmdsubst_begin - cmd ) ;
2011-12-26 19:18:46 -08:00
2007-03-25 05:07:38 +10:00
buff = wcsndup ( cmdsubst_begin , cmdsubst_end - cmdsubst_begin ) ;
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
if ( ! buff )
done = 1 ;
}
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
if ( ! done )
{
int had_cmd = 0 ;
int end_loop = 0 ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
tok_init ( & tok , buff , TOK_ACCEPT_UNFINISHED ) ;
2006-01-31 05:53:10 +10:00
2005-12-26 08:00:44 +10:00
while ( tok_has_next ( & tok ) & & ! end_loop )
2005-09-20 23:26:39 +10:00
{
2007-01-11 03:34:45 +10:00
2005-09-20 23:26:39 +10:00
switch ( tok_last_type ( & tok ) )
{
2007-01-11 03:34:45 +10:00
2005-09-20 23:26:39 +10:00
case TOK_STRING :
2006-12-14 10:03:26 +10:00
{
2007-01-11 03:34:45 +10:00
2007-01-10 22:45:28 +10:00
wchar_t * ncmd = tok_last ( & tok ) ;
2007-01-16 11:59:38 +10:00
int is_ddash = ( wcscmp ( ncmd , L " -- " ) = = 0 ) & & ( ( tok_get_pos ( & tok ) + 2 ) < pos ) ;
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
if ( ! had_cmd )
{
2007-01-11 03:34:45 +10:00
2007-04-22 19:50:26 +10:00
if ( parser_keywords_is_subcommand ( ncmd ) )
2006-12-14 10:03:26 +10:00
{
if ( wcscmp ( ncmd , L " builtin " ) = = 0 )
{
use_function = 0 ;
use_command = 0 ;
use_builtin = 1 ;
}
else if ( wcscmp ( ncmd , L " command " ) = = 0 )
{
use_command = 1 ;
use_function = 0 ;
use_builtin = 0 ;
}
2005-09-20 23:26:39 +10:00
break ;
2006-12-14 10:03:26 +10:00
}
2011-12-26 19:18:46 -08:00
2007-01-10 22:45:28 +10:00
if ( ! is_ddash | |
2007-01-11 03:34:45 +10:00
( ( use_command & & use_function & & use_builtin ) ) )
2006-12-14 10:03:26 +10:00
{
2007-01-11 03:34:45 +10:00
int token_end ;
2011-12-26 19:18:46 -08:00
2006-12-14 10:03:26 +10:00
free ( current_command ) ;
2007-01-11 03:34:45 +10:00
current_command = wcsdup ( ncmd ) ;
2011-12-26 19:18:46 -08:00
2007-01-11 03:34:45 +10:00
token_end = tok_get_pos ( & tok ) + wcslen ( ncmd ) ;
2011-12-26 19:18:46 -08:00
2007-01-11 03:34:45 +10:00
on_command = ( pos < = token_end ) ;
2006-12-14 10:03:26 +10:00
had_cmd = 1 ;
}
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
}
2007-01-10 22:45:28 +10:00
else
{
if ( is_ddash )
{
had_ddash = 1 ;
}
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
break ;
2006-12-14 10:03:26 +10:00
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
case TOK_END :
case TOK_PIPE :
case TOK_BACKGROUND :
2007-01-11 03:34:45 +10:00
{
2005-09-20 23:26:39 +10:00
had_cmd = 0 ;
2007-01-10 22:45:28 +10:00
had_ddash = 0 ;
2006-12-14 10:03:26 +10:00
use_command = 1 ;
use_function = 1 ;
use_builtin = 1 ;
2005-09-20 23:26:39 +10:00
break ;
2007-01-11 03:34:45 +10:00
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
case TOK_ERROR :
2007-01-11 03:34:45 +10:00
{
2005-09-20 23:26:39 +10:00
end_loop = 1 ;
break ;
2007-01-11 03:34:45 +10:00
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
}
2007-01-11 03:34:45 +10:00
2005-09-20 23:26:39 +10:00
if ( tok_get_pos ( & tok ) > = pos )
2007-01-11 03:34:45 +10:00
{
2005-09-20 23:26:39 +10:00
end_loop = 1 ;
2007-01-11 03:34:45 +10:00
}
2011-12-26 19:18:46 -08:00
2005-09-20 23:26:39 +10:00
tok_next ( & tok ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
}
tok_destroy ( & tok ) ;
2005-10-12 05:48:31 +10:00
free ( buff ) ;
2005-09-20 23:26:39 +10:00
/*
Get the string to complete
*/
2007-03-25 05:07:38 +10:00
current_token = wcsndup ( tok_begin , cursor_pos - ( tok_begin - cmd ) ) ;
2005-12-26 08:00:44 +10:00
prev_token = prev_begin ? wcsndup ( prev_begin , prev_end - prev_begin ) : wcsdup ( L " " ) ;
2011-12-26 19:18:46 -08:00
2006-07-11 08:39:56 +10:00
// debug( 0, L"on_command: %d, %ls %ls\n", on_command, current_command, current_token );
2006-12-14 10:03:26 +10:00
/*
Check if we are using the ' command ' or ' builtin ' builtins
2007-01-11 03:34:45 +10:00
_and_ we are writing a switch instead of a command . In that
2006-12-14 10:03:26 +10:00
case , complete using the builtins completions , not using a
subcommand .
*/
2011-12-26 19:18:46 -08:00
2006-12-14 10:03:26 +10:00
if ( ( on_command | | ( wcscmp ( current_token , L " -- " ) = = 0 ) ) & &
2011-12-26 19:18:46 -08:00
( current_token [ 0 ] = = L ' - ' ) & &
2006-12-14 10:03:26 +10:00
! ( use_command & & use_function & & use_builtin ) )
{
free ( current_command ) ;
if ( use_command = = 0 )
current_command = wcsdup ( L " builtin " ) ;
else
current_command = wcsdup ( L " command " ) ;
2011-12-26 19:18:46 -08:00
2006-12-14 10:03:26 +10:00
had_cmd = 1 ;
on_command = 0 ;
}
2011-12-26 19:18:46 -08:00
2006-12-14 10:03:26 +10:00
/*
Use command completions if in between commands
*/
2006-05-22 08:32:13 +10:00
if ( ! had_cmd )
{
on_command = 1 ;
}
2011-12-26 19:18:46 -08:00
2006-12-14 10:03:26 +10:00
/*
We don ' t want these to be null
*/
2006-05-22 08:32:13 +10:00
if ( ! current_token )
{
current_token = wcsdup ( L " " ) ;
}
2011-12-26 19:18:46 -08:00
2006-05-22 08:32:13 +10:00
if ( ! current_command )
{
current_command = wcsdup ( L " " ) ;
}
2011-12-26 19:18:46 -08:00
2006-05-22 08:32:13 +10:00
if ( ! prev_token )
{
prev_token = wcsdup ( L " " ) ;
}
2006-12-14 10:03:26 +10:00
2005-09-20 23:26:39 +10:00
if ( current_token & & current_command & & prev_token )
{
if ( on_command )
{
/* Complete command filename */
complete_cmd ( current_token ,
2006-12-14 10:03:26 +10:00
comp , use_function , use_builtin , use_command ) ;
2005-09-20 23:26:39 +10:00
}
else
{
2007-01-10 22:45:28 +10:00
int do_file = 0 ;
2011-12-26 19:18:46 -08:00
2007-01-19 02:45:28 +10:00
wchar_t * current_command_unescape = unescape ( current_command , 0 ) ;
wchar_t * prev_token_unescape = unescape ( prev_token , 0 ) ;
wchar_t * current_token_unescape = unescape ( current_token , UNESCAPE_INCOMPLETE ) ;
2011-12-26 19:18:46 -08:00
2007-01-27 03:14:13 +10:00
if ( current_token_unescape & & prev_token_unescape & & current_token_unescape )
{
2011-12-26 19:18:46 -08:00
do_file = complete_param ( current_command_unescape ,
prev_token_unescape ,
current_token_unescape ,
! had_ddash ,
2007-01-27 03:14:13 +10:00
comp ) ;
}
2011-12-26 19:18:46 -08:00
2007-01-19 02:45:28 +10:00
free ( current_command_unescape ) ;
free ( prev_token_unescape ) ;
free ( current_token_unescape ) ;
2011-12-26 19:18:46 -08:00
2006-08-27 10:57:40 +10:00
/*
If we have found no command specific completions at
all , fall back to using file completions .
*/
2012-01-16 22:26:47 +05:30
if ( comp . empty ( ) )
2006-08-27 10:57:40 +10:00
do_file = 1 ;
2011-12-26 19:18:46 -08:00
2007-01-19 02:45:28 +10:00
/*
This function wants the unescaped string
*/
2005-09-20 23:26:39 +10:00
complete_param_expand ( current_token , comp , do_file ) ;
}
}
}
2011-12-26 19:18:46 -08:00
2006-05-15 08:39:03 +10:00
free ( current_token ) ;
free ( current_command ) ;
free ( prev_token ) ;
2005-09-20 23:26:39 +10:00
condition_cache_clear ( ) ;
2007-02-09 19:33:50 +10:00
2005-09-20 23:26:39 +10:00
}
2012-01-16 22:26:47 +05:30
2005-10-25 01:26:25 +10:00
/**
Print the GNU longopt style switch \ c opt , and the argument \ c
argument to the specified stringbuffer , but only if arguemnt is
non - null and longer than 0 characters .
*/
2005-09-20 23:26:39 +10:00
static void append_switch ( string_buffer_t * out ,
2006-01-31 05:53:10 +10:00
const wchar_t * opt ,
2005-09-20 23:26:39 +10:00
const wchar_t * argument )
{
wchar_t * esc ;
2006-01-31 05:53:10 +10:00
2007-10-14 07:26:06 +10:00
if ( ! argument | | wcscmp ( argument , L " " ) = = 0 )
2005-09-20 23:26:39 +10:00
return ;
2006-02-07 01:15:52 +10:00
esc = escape ( argument , 1 ) ;
2005-09-20 23:26:39 +10:00
sb_printf ( out , L " --%ls %ls " , opt , esc ) ;
free ( esc ) ;
}
void complete_print ( string_buffer_t * out )
{
2006-10-24 21:03:52 +10:00
complete_entry_t * e ;
2006-01-31 05:53:10 +10:00
2006-10-19 01:51:51 +10:00
CHECK ( out , ) ;
2005-09-20 23:26:39 +10:00
for ( e = first_entry ; e ; e = e - > next )
{
2006-10-24 21:03:52 +10:00
complete_entry_opt_t * o ;
2005-09-20 23:26:39 +10:00
for ( o = e - > first_option ; o ; o = o - > next )
{
2012-01-13 23:44:18 -08:00
const wchar_t * modestr [ ] =
2005-09-20 23:26:39 +10:00
{
L " " ,
L " --no-files " ,
L " --require-parameter " ,
L " --exclusive "
}
;
sb_printf ( out ,
L " complete%ls " ,
modestr [ o - > result_mode ] ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
append_switch ( out ,
e - > cmd_type ? L " path " : L " command " ,
e - > cmd ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
if ( o - > short_opt ! = 0 )
{
sb_printf ( out ,
L " --short-option '%lc' " ,
o - > short_opt ) ;
}
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
append_switch ( out ,
o - > old_mode ? L " old-option " : L " long-option " ,
o - > long_opt ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
append_switch ( out ,
L " description " ,
2006-06-08 09:56:01 +10:00
C_ ( o - > desc ) ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
append_switch ( out ,
L " arguments " ,
o - > comp ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
append_switch ( out ,
L " condition " ,
o - > condition ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
sb_printf ( out , L " \n " ) ;
}
}
}