diff options
Diffstat (limited to 'builtin.c')
-rw-r--r-- | builtin.c | 2879 |
1 files changed, 2879 insertions, 0 deletions
diff --git a/builtin.c b/builtin.c new file mode 100644 index 00000000..92e4671b --- /dev/null +++ b/builtin.c @@ -0,0 +1,2879 @@ +/** \file builtin.c + Functions for executing builtin functions. + + How to add a new builtin function: + + 1). Create a function in builtin.c with the following signature: + + <tt>static int builtin_NAME( wchar_t ** args )</tt> + + where NAME is the name of the builtin, and args is a zero-terminated list of arguments. + + 2). Add a line like hash_put( &builtin, L"NAME", &builtin_NAME ); to builtin_init. This will enable the parser to find the builtin function. + + 3). Add a line like hash_put( desc, L"NAME", L"Frobble the bloogle" ); to the proper part of builtin_get_desc, containing a short description of what the builtin does. This description is used by the completion system. + + 4). Create a file names doc_src/NAME.txt, contining the manual for the builtin in Doxygen-format. Check the other builtin manuals for proper syntax. + + 5). Add an entry to the BUILTIN_DOC_SRC variable of Makefile.in. Note that the entries should be sorted alpabetically! + + 6). Add an entry to the manual at the builtin-overview subsection + +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <wchar.h> +#include <unistd.h> +#include <termios.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <dirent.h> +#include <string.h> +#include <signal.h> +#include <wctype.h> +#include <sys/time.h> + +#include "config.h" +#include "util.h" +#include "wutil.h" +#include "builtin.h" +#include "function.h" +#include "complete.h" +#include "proc.h" +#include "parser.h" +#include "reader.h" +#include "env.h" +#include "expand.h" +#include "common.h" +#include "wgetopt.h" +#include "sanity.h" +#include "tokenizer.h" +#include "builtin_help.h" +#include "wildcard.h" +#include "input_common.h" +#include "input.h" +#include "intern.h" + +/** + The default prompt for the read command +*/ +#define DEFAULT_READ_PROMPT L"set_color green; echo read; set_color normal; echo \"> \"" + +/** + The mode name to pass to history and input +*/ + +#define READ_MODE_NAME L"fish_read" +/** + Table of all builtins +*/ +static hash_table_t builtin; + +int builtin_out_redirect; +int builtin_err_redirect; + +/** + Buffers for storing the output of builtin functions +*/ +string_buffer_t *sb_out=0, *sb_err=0; +/** + Stack containing builtin I/O for recursive builtin calls. +*/ +static array_list_t io_stack; + +/** + The file from which builtin functions should attempt to read, use + instead of stdin. +*/ +static int builtin_stdin; + +/** + Table containing descriptions for all builtins +*/ +static hash_table_t *desc=0; + +int builtin_count_args( wchar_t **argv ) +{ + int argc = 1; + while( argv[argc] != 0 ) + { + argc++; + } + return argc; +} + +/** + This function works like wperror, but it prints its result into + the sb_err string_buffer_t instead of to stderr. Used by the builtin + commands. +*/ +static void builtin_wperror( const wchar_t *s) +{ + if( s != 0 ) + { + sb_append2( sb_err, s, L": ", 0 ); + } + char *err = strerror( errno ); + wchar_t *werr = str2wcs( err ); + if( werr ) + { + sb_append2( sb_err, werr, L"\n", 0 ); + free( werr ); + } +} + + +/* + Here follows the definition of all builtin commands. The function + names are all on the form builtin_NAME where NAME is the name of the + builtin. so the function name for the builtin 'jobs' is + 'builtin_jobs'. + + Two builtins, 'command' and 'builtin' are not defined here as they + are part of the parser. (They are not parsed as commands, instead + they only slightly alter the parser state) + +*/ + + +/** + Noop function. A fake function which successfully does nothing, for + builtins which are handled by the parser, such as command and + while. +*/ +static int builtin_ignore( wchar_t **argv ) +{ + return 0; +} + +void builtin_print_help( wchar_t *cmd, string_buffer_t *b ) +{ + const char *h; + + if( b == sb_err ) + { + sb_append( sb_err, + parser_current_line() ); + } + + h = builtin_help_get( cmd ); + + if( !h ) + return; + + + + wchar_t *str = str2wcs(builtin_help_get( cmd )); + if( str ) + { + sb_append( b, str ); + free( str ); + } +} +/** + The bind builtin, used for setting character sequences +*/ +static int builtin_bind( wchar_t **argv ) +{ + int i; + int argc=builtin_count_args( argv ); + + woptind=0; + + const static struct woption + long_options[] = + { + { + L"set-mode", required_argument, 0, 'M' + } + , + { + 0, 0, 0, 0 + } + } + ; + + while( 1 ) + { + int opt_index = 0; + + int opt = wgetopt_long( argc, + argv, + L"M:", + long_options, + &opt_index ); + if( opt == -1 ) + break; + + switch( opt ) + { + case 0: + if(long_options[opt_index].flag != 0) + break; + sb_printf( sb_err, + L"%ls%ls %ls\n", + argv[0], + BUILTIN_ERR_UNKNOWN, + long_options[opt_index].name ); + builtin_print_help( argv[0], sb_err ); + + return 1; + + case 'M': + input_set_mode( woptarg ); + break; + + case '?': + builtin_print_help( argv[0], sb_err ); + + return 1; + + } + + } + + for( i=woptind; i<argc; i++ ) + { +// fwprintf( stderr, L"Parse binding '%ls'\n", argv[i] ); + + input_parse_inputrc_line( argv[i] ); + } + + return 0; +} + + +/** + The builtin builtin, used for given builtins precedence over functions. Mostly handled by the parser. All this code does is some additional operational modes, such as printing a list of all builtins. +*/ +static int builtin_builtin( wchar_t **argv ) +{ + int argc=builtin_count_args( argv ); + int list=0; + + woptind=0; + + const static struct woption + long_options[] = + { + { + L"names", no_argument, 0, 'n' + } + , + { + L"help", no_argument, 0, 'h' + } + , + { + 0, 0, 0, 0 + } + } + ; + + while( 1 ) + { + int opt_index = 0; + + int opt = wgetopt_long( argc, + argv, + L"nh", + long_options, + &opt_index ); + if( opt == -1 ) + break; + + switch( opt ) + { + case 0: + if(long_options[opt_index].flag != 0) + break; + sb_append2( sb_err, + argv[0], + BUILTIN_ERR_UNKNOWN, + L" ", + long_options[opt_index].name, + L"\n", + 0); + builtin_print_help( argv[0], sb_err ); + + + return 1; + case 'h': + builtin_print_help( argv[0], sb_err ); + return 0; + + case 'n': + list=1; + break; + + case '?': + builtin_print_help( argv[0], sb_err ); + + return 1; + + } + + } + + if( list ) + { + array_list_t names; + wchar_t **names_arr; + int i; + + al_init( &names ); + builtin_get_names( &names ); + names_arr = list_to_char_arr( &names ); + qsort( names_arr, + al_get_count( &names ), + sizeof(wchar_t *), + (int (*)(const void *, const void *))&wcsfilecmp ); + for( i=0; i<al_get_count( &names ); i++ ) + { + if( wcscmp( names_arr[i], L"count" ) == 0 ) + continue; + + sb_append2( sb_out, + names_arr[i], + L"\n", + 0 ); + } + free( names_arr ); + al_destroy( &names ); + } + return 0; +} + +/** + A generic bultin that only supports showing a help message. This is + only a placeholder that prints the help message. Useful for + commands that live in hte parser. +*/ + +static int builtin_generic( wchar_t **argv ) +{ + int argc=builtin_count_args( argv ); + woptind=0; + + const static struct woption + long_options[] = + { + { + L"help", no_argument, 0, 'h' + } + , + { + 0, 0, 0, 0 + } + } + ; + + while( 1 ) + { + int opt_index = 0; + + int opt = wgetopt_long( argc, + argv, + L"h", + long_options, + &opt_index ); + if( opt == -1 ) + break; + + switch( opt ) + { + case 0: + if(long_options[opt_index].flag != 0) + break; + sb_append2( sb_err, + argv[0], + BUILTIN_ERR_UNKNOWN, + L" ", + long_options[opt_index].name, + L"\n", + 0); + builtin_print_help( argv[0], sb_err ); + return 1; + + case 'h': + builtin_print_help( argv[0], sb_out ); + return 0; + + case '?': + builtin_print_help( argv[0], sb_err ); + + return 1; + + } + + } + return 1; +} + +/** + The exec bultin. This is only a placeholder that prints the help message. Ther actual implementation lives in exec.c. +*/ + +static int builtin_exec( wchar_t **argv ) +{ + int argc=builtin_count_args( argv ); + woptind=0; + + const static struct woption + long_options[] = + { + { + L"help", no_argument, 0, 'h' + } + , + { + 0, 0, 0, 0 + } + } + ; + + while( 1 ) + { + int opt_index = 0; + + int opt = wgetopt_long( argc, + argv, + L"h", + long_options, + &opt_index ); + if( opt == -1 ) + break; + + switch( opt ) + { + case 0: + if(long_options[opt_index].flag != 0) + break; + sb_append2( sb_err, + argv[0], + BUILTIN_ERR_UNKNOWN, + L" ", + long_options[opt_index].name, + L"\n", + 0); + builtin_print_help( argv[0], sb_err ); + return 1; + + case 'h': + builtin_print_help( argv[0], sb_out ); + return 0; + + case '?': + builtin_print_help( argv[0], sb_err ); + + return 1; + + } + + } + return 1; +} + + +/** + The functions builtin, used for listing and erasing functions. +*/ +static int builtin_functions( wchar_t **argv ) +{ + int i; + int erase=0; + wchar_t *desc=0; + + array_list_t names; + wchar_t **names_arr; + + int argc=builtin_count_args( argv ); + int list=0; + int show_hidden=0; + + woptind=0; + + const static struct woption + long_options[] = + { + { + L"erase", no_argument, 0, 'e' + } + , + { + L"description", required_argument, 0, 'd' + } + , + { + L"names", no_argument, 0, 'n' + } + , + { + L"all", no_argument, 0, 'a' + } + , + { + 0, 0, 0, 0 + } + } + ; + + while( 1 ) + { + int opt_index = 0; + + int opt = wgetopt_long( argc, + argv, + L"ed:na", + long_options, + &opt_index ); + if( opt == -1 ) + break; + + switch( opt ) + { + case 0: + if(long_options[opt_index].flag != 0) + break; + sb_append2( sb_err, + argv[0], + BUILTIN_ERR_UNKNOWN, + L" ", + long_options[opt_index].name, + L"\n", + 0); + builtin_print_help( argv[0], sb_err ); + + + return 1; + + case 'e': + erase=1; + break; + + case 'd': + desc=woptarg; + break; + + case 'n': + list=1; + break; + + case 'a': + show_hidden=1; + break; + + case '?': + builtin_print_help( argv[0], sb_err ); + + return 1; + + } + + } + + + + /* + Erase, desc and list are mutually exclusive + */ + if( (erase + (desc!=0) + list) > 1 ) + { + sb_append2( sb_err, + argv[0], + L": Invalid combination of options\n", + 0); + builtin_print_help( argv[0], sb_err ); + + return 1; + } + + + if( erase ) + { + int i; + for( i=woptind; i<argc; i++ ) + function_remove( argv[i] ); + return 0; + } + else if( desc ) + { + wchar_t *func; + + if( argc-woptind != 1 ) + { + sb_append2( sb_err, + L"functions: Expected exactly one function name\n", + 0); + builtin_print_help( argv[0], sb_err ); + + return 1; + } + func = argv[woptind]; + if( !function_exists( func ) ) + { + sb_append2( sb_err, + L"functions: Function ", + func, + L" does not exist\n", + 0); + builtin_print_help( argv[0], sb_err ); + + return 1; + } + + function_set_desc( func, desc ); + + return 0; + } + else if( list ) + { + al_init( &names ); + function_get_names( &names, show_hidden ); + names_arr = list_to_char_arr( &names ); + qsort( names_arr, + al_get_count( &names ), + sizeof(wchar_t *), + (int (*)(const void *, const void *))&wcsfilecmp ); + for( i=0; i<al_get_count( &names ); i++ ) + { + sb_append2( sb_out, + names_arr[i], + L"\n", + 0 ); + } + free( names_arr ); + al_destroy( &names ); + return 0; + } + + + switch( argc - woptind ) + { + case 0: + { + sb_append( sb_out, L"Current function definitions are:\n\n" ); + al_init( &names ); + function_get_names( &names, show_hidden ); + names_arr = list_to_char_arr( &names ); + qsort( names_arr, + al_get_count( &names ), + sizeof(wchar_t *), + (int (*)(const void *, const void *))&wcsfilecmp ); + for( i=0; i<al_get_count( &names ); i++ ) + { + sb_append2( sb_out, + L"function ", + names_arr[i], + L"\n\t", + function_get_definition(names_arr[i]), + L"\nend\n\n", + 0); + } + free( names_arr ); + al_destroy( &names ); + break; + } + + default: + { + for( i=woptind; i<argc; i++ ) + sb_append2( sb_out, + L"function ", + argv[i], + L"\n\t", + function_get_definition(argv[i]), + L"\nend\n\n", + 0); + + break; + } + } + return 0; + + +} + + +/** + The function builtin, used for providing subroutines. + It calls various functions from function.c to perform any heavy lifting. +*/ +static int builtin_function( wchar_t **argv ) +{ + int argc = builtin_count_args( argv ); + int res=0; + wchar_t *desc=0; + + woptind=0; + + const static struct woption + long_options[] = + { + { + L"description", required_argument, 0, 'd' + } + , + { + 0, 0, 0, 0 + } + } + ; + + while( 1 ) + { + int opt_index = 0; + + int opt = wgetopt_long( argc, + argv, + L"d:", + long_options, + &opt_index ); + if( opt == -1 ) + break; + + switch( opt ) + { + case 0: + if(long_options[opt_index].flag != 0) + break; + sb_append2( sb_err, + argv[0], + BUILTIN_ERR_UNKNOWN, + L" ", + long_options[opt_index].name, + L"\n", + 0); + builtin_print_help( argv[0], sb_err ); + + return 1; + + case 'd': + desc=woptarg; + break; + + case '?': + builtin_print_help( argv[0], sb_err ); + + return 1; + + } + + } + + if( argc-woptind != 1 ) + { + sb_printf( sb_err, + L"%ls: Expected one argument, got %d\n", + argv[0], + argc-woptind ); + res=1; + } + else if( !wcsvarname( argv[woptind] ) ) + { + sb_append2( sb_err, + argv[0], + L": illegal function name \'", + argv[woptind], + L"\'\n", + 0 ); + res=1; + } + else if( parser_is_reserved(argv[woptind] ) ) + { + + sb_append2( sb_err, + argv[0], + L": the name \'", + argv[woptind], + L"\' is reserved,\nand can not be used as a function name\n", + 0 ); + + res=1; + + } + + + + if( res ) + { + int i; + array_list_t names; + wchar_t **names_arr; + int chars=0; + + builtin_print_help( argv[0], sb_err ); + + sb_append( sb_err, L"Current functions are: " ); + chars += wcslen( L"Current functions are: " ); + al_init( &names ); + function_get_names( &names, 0 ); + names_arr = list_to_char_arr( &names ); + qsort( names_arr, + al_get_count( &names ), + sizeof(wchar_t *), + (int (*)(const void *, const void *))&wcsfilecmp ); + for( i=0; i<al_get_count( &names ); i++ ) + { + wchar_t *nxt = names_arr[i]; + int l = wcslen( nxt + 2 ); + if( chars+l > reader_get_width() ) + { + chars = 0; + sb_append(sb_err, L"\n" ); + } + + sb_append2( sb_err, + nxt, L" ", 0 ); + } + free( names_arr ); + al_destroy( &names ); + sb_append( sb_err, L"\n" ); + + parser_push_block( FAKE ); + } + else + { + parser_push_block( FUNCTION_DEF ); + current_block->function_name=wcsdup(argv[woptind]); + current_block->function_description=desc?wcsdup(desc):0; + } + + current_block->tok_pos = parser_get_pos(); + current_block->skip = 1; + + return 0; + +} + +/** + The random builtin. For generating random numbers. +*/ + +static int builtin_random( wchar_t **argv ) +{ + static int seeded=0; + int argc = builtin_count_args( argv ); + + woptind=0; + + const static struct woption + long_options[] = + { + { + L"help", no_argument, 0, 'h' + } + , + { + 0, 0, 0, 0 + } + } + ; + + while( 1 ) + { + int opt_index = 0; + + int opt = wgetopt_long( argc, + argv, + L"h", + long_options, + &opt_index ); + if( opt == -1 ) + break; + + switch( opt ) + { + case 0: + if(long_options[opt_index].flag != 0) + break; + sb_append2( sb_err, + argv[0], + BUILTIN_ERR_UNKNOWN, + L" ", + long_options[opt_index].name, + L"\n", + 0); + builtin_print_help( argv[0], sb_err ); + + return 1; + + case 'h': + builtin_print_help( argv[0], sb_err ); + break; + + case '?': + builtin_print_help( argv[0], sb_err ); + + return 1; + + } + + } + + switch( argc-woptind ) + { + + case 0: + { + if( !seeded ) + { + seeded=1; + srand( time( 0 ) ); + } + sb_printf( sb_out, L"%d\n", rand()%32767 ); + break; + } + + case 1: + { + int foo; + wchar_t *end=0; + + errno=0; + foo = wcstol( argv[woptind], &end, 10 ); + if( errno || *end ) + { + sb_append2( sb_err, + argv[0], + L": Seed value '" , argv[woptind], L"' is not a valid number\n", 0); + + return 1; + } + seeded=1; + srand( foo ); + break; + } + + default: + { + sb_printf( sb_err, + L"%ls: Expected zero or one argument, got %d\n", + argc-woptind ); + builtin_print_help( argv[0], sb_err ); + return 1; + } + } + return 0; +} + + +/** + The read builtin. Reads from stdin and stores the values in environment variables. +*/ +static int builtin_read( wchar_t **argv ) +{ + wchar_t *buff=0; + int i, argc = builtin_count_args( argv ); + wchar_t *ifs; + int place = ENV_USER; + wchar_t *nxt; + wchar_t *prompt = DEFAULT_READ_PROMPT; + wchar_t *commandline = L""; + + woptind=0; + + while( 1 ) + { + const static struct woption + long_options[] = + { + { + L"export", no_argument, 0, 'x' + } + , + { + L"global", no_argument, 0, 'g' + } + , + { + L"local", no_argument, 0, 'l' + } + , + { + L"unexport", no_argument, 0, 'u' + } + , + { + L"prompt", required_argument, 0, 'p' + } + , + { + L"command", required_argument, 0, 'c' + } + , + { + 0, 0, 0, 0 + } + } + ; + + int opt_index = 0; + + int opt = wgetopt_long( argc, + argv, + L"xglup:c:", + long_options, + &opt_index ); + if( opt == -1 ) + break; + + switch( opt ) + { + case 0: + if(long_options[opt_index].flag != 0) + break; + sb_append2( sb_err, + argv[0], + BUILTIN_ERR_UNKNOWN, + L" ", + long_options[opt_index].name, + L"\n", + 0 ); + builtin_print_help( argv[0], sb_err ); + + return 1; + + case L'x': + place |= ENV_EXPORT; + break; + case L'g': + place |= ENV_GLOBAL; + break; + case L'l': + place |= ENV_LOCAL; + break; + case L'u': + place |= ENV_UNEXPORT; + break; + case L'p': + prompt = woptarg; + break; + case L'c': + commandline = woptarg; + break; + + case L'?': + builtin_print_help( argv[0], sb_err ); + + + return 1; + } + + } + + if( ( place & ENV_UNEXPORT ) && ( place & ENV_EXPORT ) ) + { + sb_append2( sb_err, + argv[0], + BUILTIN_ERR_EXPUNEXP, + L"\n", + parser_current_line(), + L"\n", + 0 ); + builtin_print_help( argv[0], sb_err ); + return 1; + } + + if( (place&ENV_LOCAL) && (place & ENV_GLOBAL) ) + { + sb_append2( sb_err, + argv[0], + BUILTIN_ERR_GLOCAL, + L"\n", + parser_current_line(), + L"\n", + 0 ); + builtin_print_help( argv[0], sb_err ); + + return 1; + } + + if( woptind == argc ) + { + sb_append2( sb_err, + argv[0], + BUILTIN_ERR_MISSING, + L"\n", + parser_current_line(), + L"\n", + 0 ); + builtin_print_help( argv[0], sb_err ); + return 1; + } + + /* + The call to reader_readline may change woptind, so we save it away here + */ + i=woptind; + + ifs = env_get( L"IFS" ); + if( ifs == 0 ) + ifs = L""; + + /* + Check if we should read interactively using \c reader_readline() + */ + if( isatty(0) && builtin_stdin == 0 ) + { + reader_push( READ_MODE_NAME ); + reader_set_prompt( prompt ); + + reader_set_buffer( commandline, wcslen( commandline ) ); + buff = wcsdup(reader_readline( )); + reader_pop(); + } + else + { + string_buffer_t sb; + sb_init( &sb ); + while( 1 ) + { + int eof=0; + int finished=0; + + wchar_t res=0; + static mbstate_t state; + memset (&state, '\0', sizeof (state)); + + while( !finished ) + { + char b; + int read_res = read_blocked( builtin_stdin, &b, 1 ); + if( read_res <= 0 ) + { + eof=1; + break; + } + + int sz = mbrtowc( &res, &b, 1, &state ); + + switch( sz ) + { + case -1: + memset (&state, '\0', sizeof (state)); + break; + + case -2: + break; + case 0: + eof=1; + finished = 1; + break; + + default: + finished=1; + break; + + } + } + + if( eof ) + break; + if( res == L'\n' ) + break; + + sb_append_char( &sb, res ); + } + buff = wcsdup( (wchar_t *)sb.buff ); + sb_destroy( &sb ); + } + + wchar_t *state; + + nxt = wcstok( buff, (i<argc-1)?ifs:L"", &state ); +// fwprintf( stderr, L"first token %ls, %d args, start at %d\n", nxt, argc, i ); + + while( i<argc ) + { + env_set( argv[i], nxt != 0 ? nxt: L"", place ); + + i++; + if( nxt != 0 ) + nxt = wcstok( 0, (i<argc-1)?ifs:L"", &state); + } + + free( buff ); + return 0; +} + +static int builtin_status( wchar_t **argv ) +{ + enum + { + NORMAL, + SUBST, + BLOCK, + INTERACTIVE, + LOGIN + } + ; + + int mode = NORMAL; + + int argc = builtin_count_args( argv ); + woptind=0; + + const static struct woption + long_options[] = + { + { + L"help", no_argument, 0, 'h' + } + , + { + L"is-command-substitution", no_argument, 0, 'c' + } + , + { + L"is-block", no_argument, 0, 'b' + } + , + { + L"is-interactive", no_argument, 0, 'i' + } + , + { + L"is-login", no_argument, 0, 'l' + } + , + { + 0, 0, 0, 0 + } + } + ; + + while( 1 ) + { + int opt_index = 0; + + int opt = wgetopt_long( argc, + argv, + L"hcbil", + long_options, + &opt_index ); + if( opt == -1 ) + break; + + switch( opt ) + { + case 0: + if(long_options[opt_index].flag != 0) + break; + sb_append2( sb_err, + argv[0], + BUILTIN_ERR_UNKNOWN, + L" ", + long_options[opt_index].name, + L"\n", + 0); + builtin_print_help( argv[0], sb_err ); + + return 1; + + case 'h': + builtin_print_help( argv[0], sb_err ); + break; + + case 'i': + mode = INTERACTIVE; + break; + + case 'c': + mode = SUBST; + break; + + case 'b': + mode = BLOCK; + break; + + case 'l': + mode = LOGIN; + break; + + case '?': + builtin_print_help( argv[0], sb_err ); + + return 1; + + } + + } + + switch( mode ) + { + case INTERACTIVE: + return !is_interactive_session; + + case SUBST: + return !is_subshell; + + case BLOCK: + return !is_block; + + case LOGIN: + return !is_login; + + } + + return 0; +} + + +/** + The eval builtin. Concatenates the arguments and calls eval on the + result. +*/ +static int builtin_eval( wchar_t **argv ) +{ + wchar_t *tot, **ptr, *next; + int totlen=0; + + for( ptr = argv+1; *ptr; ptr++ ) + { + totlen += wcslen( *ptr) + 1; + } + tot = malloc( sizeof(wchar_t)*totlen ); + if( !tot ) + { + die_mem(); + } + for( ptr = argv+1, next=tot; *ptr; ptr++ ) + { + int len = wcslen( *ptr ); + wcscpy( next, *ptr ); + next+=len; + *next++=L' '; + } + *(next-1)=L'\0'; + eval( tot, block_io, TOP ); + free( tot ); + return proc_get_last_status(); +} + +/** + The exit builtin. Calls reader_exit to exit and returns the value specified. +*/ +static int builtin_exit( wchar_t **argv ) +{ + int argc = builtin_count_args( argv ); + + int ec=0; + switch( argc ) + { + case 1: + break; + case 2: + { + wchar_t *end; + errno = 0; + ec = wcstol(argv[1],&end,10); + if( errno || *end != 0) + { + sb_append2( sb_err, argv[0], L": Argument must be an integer '", argv[1], L"'\n", 0 ); + builtin_print_help( argv[0], sb_err ); + return 1; + } + break; + } + + default: + sb_append2( sb_err, argv[0], L": Too many arguments\n", 0 ); + builtin_print_help( argv[0], sb_err ); + return 1; + + } + reader_exit( 1 ); + return ec; +} + +/** + Helper function for builtin_cd, used for seting the current working directory +*/ +static int set_pwd(wchar_t *env) +{ + wchar_t dir_path[4096]; + wchar_t *res = wgetcwd( dir_path, 4096 ); + if( !res ) + { + builtin_wperror( L"wgetcwd" ); + return 0; + } + env_set( env, dir_path, ENV_EXPORT | ENV_GLOBAL ); + return 1; +} + +/** + The cd builtin. Changes the current directory to the one specified + or to $HOME if none is specified. If '-' is the directory specified, + the directory is changed to the previous working directory. The + directory can be relative to any directory in the CDPATH variable. +*/ +static int builtin_cd( wchar_t **argv ) +{ + wchar_t *dir_in; + wchar_t *dir; + int res=0; + + if( argv[1] == 0 ) + { + dir_in = env_get( L"HOME" ); + if( !dir_in ) + { + sb_append2( sb_err, + argv[0], + L": Could not find home directory\n", + 0 ); + + } + } + else + dir_in = argv[1]; + + dir = parser_cdpath_get( dir_in ); + + if( !dir ) + { + sb_append2( sb_err, + argv[0], + L": ", + dir_in, + L" is not a directory or you do not have permission to enter it\n", + 0 ); + sb_append2( sb_err, + parser_current_line(), + 0 ); + return 1; + } + + if( wchdir( dir ) != 0 ) + { + sb_append2( sb_err, + argv[0], + L": ", + dir, + L" is not a directory\n", + 0 ); + sb_append2( sb_err, + parser_current_line(), + 0 ); + + free( dir ); + + return 1; + } + + if (!set_pwd(L"PWD")) + { + res=1; + sb_append( sb_err, L"Could not set PWD variable\n" ); + } + +// fwprintf( stderr, L"cd '%ls' -> '%ls', set PWD to '%ls'\n", argv[1]?argv[1]:L"-", dir, env_get( L"PWD" ) ); + + free( dir ); + + return res; +} + +/** + The complete builtin. Used for specifying programmable + tab-completions. Calls the functions in complete.c for any heavy + lifting. +*/ +static int builtin_complete( wchar_t **argv ) +{ + + int argc=0; + int result_mode=SHARED, long_mode=0; + int cmd_type=-1; + int remove = 0; + int authorative = 1; + + wchar_t *cmd=0, short_opt=L'\0', *long_opt=L"", *comp=L"", *desc=L"", *condition=L"", *load=0; + + argc = builtin_count_args( argv ); + + woptind=0; + + while( 1 ) + { + const static struct woption + long_options[] = + { + { + L"exclusive", no_argument, 0, 'x' + } + , + { + L"no-files", no_argument, 0, 'f' + } + , + { + L"require-parameter", no_argument, 0, 'r' + } + , + { + L"path", required_argument, 0, 'p' + } + , + { + L"command", required_argument, 0, 'c' + } + , + { + L"short-option", required_argument, 0, 's' + } + , + { + L"long-option", required_argument, 0, 'l' } + , + { + L"old-option", required_argument, 0, 'o' + } + , + { + L"description", required_argument, 0, 'd' + } + , + { + L"arguments", required_argument, 0, 'a' + } + , + { + L"erase", no_argument, 0, 'e' + } + , + { + L"unauthorative", no_argument, 0, 'u' + } + , + { + L"condition", required_argument, 0, 'n' + } + , + { + L"load", required_argument, 0, 'y' + } + , + { + 0, 0, 0, 0 + } + } + ; + + int opt_index = 0; + + int opt = wgetopt_long( argc, + argv, + L"a:c:p:s:l:o:d:frxeun:y:", + long_options, + &opt_index ); + if( opt == -1 ) + break; + + switch( opt ) + { + case 0: + if(long_options[opt_index].flag != 0) + break; + sb_append2( sb_err, + argv[0], + L": Unknown option ", + long_options[opt_index].name, + L"\n", + 0 ); + sb_append( sb_err, + parser_current_line() ); +// builtin_print_help( argv[0], sb_err ); + + + return 1; + + + case 'x': + result_mode |= EXCLUSIVE; + break; + + case 'f': + result_mode |= NO_FILES; + break; + + case 'r': + result_mode |= NO_COMMON; + break; + + case 'p': + cmd_type = PATH; + cmd = expand_backslash( wcsdup(woptarg), 1); + break; + + case 'c': + cmd_type = COMMAND; + cmd = expand_backslash( wcsdup(woptarg), 1); + break; + + case 'd': + desc = woptarg; + break; + + case 'u': + authorative=0; + break; + + case 's': + if( wcslen( woptarg ) > 1 ) + { + sb_append2( sb_err, + argv[0], + L": Parameter too long ", + woptarg, + L"\n", + 0); + sb_append( sb_err, + parser_current_line() ); +// builtin_print_help( argv[0], sb_err ); + + return 1; + } + + short_opt = woptarg[0]; + break; + + case 'l': + long_opt = woptarg; + break; + + case 'o': + long_mode=1; + long_opt = woptarg; + break; + + case 'a': + comp = woptarg; + break; + + + case 'e': + remove = 1; + + break; + + case 'n': + condition = woptarg; + break; + + case 'y': + load = woptarg; + break; + + + case '?': + // builtin_print_help( argv[0], sb_err ); + + return 1; + + } + + } + + if( woptind != argc ) + { + sb_append2( sb_err, + argv[0], + L": Too many arguments\n", + 0); + sb_append( sb_err, + parser_current_line() ); + // builtin_print_help( argv[0], sb_err ); + + return 1; + } + + if( load ) + { + complete_load( load, 1 ); + return 0; + } + + + if( cmd == 0 ) + { + /* No arguments specified, meaning we print the definitions of + * all specified completions to stdout.*/ + complete_print( sb_out ); + } + else + { + if( remove ) + { + /* Remove the specified completion */ + complete_remove( cmd, + cmd_type, + short_opt, + long_opt ); + } + else + { + /* Add the specified completion */ + complete_add( cmd, + cmd_type, + short_opt, + long_opt, + long_mode, + result_mode, + authorative, + condition, + comp, + desc ); + } + free( cmd ); + + } + return 0; +} + +/** + The source builtin. Can be called through either 'source' or + '.'. Evaluates the contents of a file. +*/ +static int builtin_source( wchar_t ** argv ) +{ + int stdin_org; + int res; + +/* + if( wcsstr( argv[1], L"fish_complete" ) ) + { + fwprintf( stderr, L"Woot\n" ); + return 0; + } +*/ + + if( (argv[1] == 0) || (argv[2]!=0) ) + { + + sb_append2( sb_err, argv[0], L": Expected exactly one argument\n", 0 ); + builtin_print_help( argv[0], sb_err ); + + return 1; + } + + if( (stdin_org=dup( 0 )) == -1) + { + builtin_wperror(L"dup"); + return 1; + } + + if( close( 0 ) ) + { + builtin_wperror(L"close"); + return 1; + } + + if( wopen( argv[1], O_RDONLY ) == -1 ) + { + builtin_wperror( L"open" ); + res = 1; + } + else + { + reader_push_current_filename( argv[1] ); + res = reader_read(); + if( res ) + { + sb_printf( sb_err, + L"%ls : Error while reading file '%ls'\n", + argv[0], + argv[1] + ); + + } + + if( close( 0 ) ) + { + builtin_wperror(L"close"); + res = errno; + } + reader_pop_current_filename(); + } + + if( dup( stdin_org ) == -1) + { + builtin_wperror(L"dup"); + res = errno; + fwprintf( stderr, L"Could not restore stdout\n" ); + sanity_lose(); + } + + if( close( stdin_org ) ) + { + builtin_wperror(L"close"); + res = errno; + fwprintf( stderr, L"Could not restore stdout\n" ); + sanity_lose(); + } + + return res; +} + + +/** + Make the specified job the first job of the job list. Moving jobs + around in the list makes the list reflect the order in which the + jobs where used. +*/ +static void make_first( job_t *j ) +{ + job_t *prev=0; + job_t *curr; + for( curr = first_job; curr != j; curr = curr->next ) + { + prev=curr; + } + if( curr == j ) + { + if( prev == 0 ) + return; + else + { + prev->next = curr->next; + curr->next = first_job; + first_job = curr; + } + } +} + + +/** + Builtin for putting a job in the foreground +*/ +static int builtin_fg( wchar_t **argv ) +{ + job_t *j; + + if( argv[1] == 0 ) + { + /* + Last constructed job in the job que by default + */ + for( j=first_job; ((j!=0) && (!j->constructed)); j=j->next ) + ; + } + else if( argv[2] != 0 ) + { + /* + Specifying what more than one job to put to the foreground + is a syntax error, we still try to locate the job argv[1], + since we want to know if this is an ambigous job + specification or if this is an malformed job id + */ + int pid = wcstol( argv[1], 0, 10 ); + j = job_get_from_pid( pid ); + if( j != 0 ) + { + sb_append2( sb_err, + argv[0], + L": Ambiguous job\n", + 0); + } + else + { + sb_append2( sb_err, + argv[0], + L": Not a job (", + argv[1], + L")\n", 0 ); + } + builtin_print_help( argv[0], sb_err ); + + return 1; + } + else + { + int pid = abs(wcstol( argv[1], 0, 10 )); + j = job_get_from_pid( pid ); + } + + if( j == 0 ) + { + sb_append2( sb_err, + argv[0], + L": No suitable job\n", + 0); + builtin_print_help( argv[0], sb_err ); + return 1; + } + else + { + if( builtin_err_redirect ) + { + sb_printf( sb_err, + L"Send job %d, '%ls' to foreground\n", + j->job_id, + j->command ); + } + else + { + fwprintf( stderr, + L"Send job %d, '%ls' to foreground\n", + j->job_id, + j->command ); + } + } + + wchar_t *ft = tok_first( j->command ); + if( ft != 0 ) + env_set( L"_", ft, ENV_EXPORT ); + free(ft); + reader_write_title(); +/* + fwprintf( stderr, L"Send job %d, \'%ls\' to foreground\n", + j->job_id, + j->command ); +*/ + make_first( j ); + j->fg=1; + + + job_continue( j, job_is_stopped(j) ); + return 0; +} + +/** + Helper function for builtin_bg() +*/ +static void send_to_bg( job_t *j, wchar_t *name ) +{ + if( j == 0 ) + { + sb_append2( sb_err, L"bg", L": Unknown job ", name, L"\n", 0 ); + builtin_print_help( L"bg", sb_err ); + return; + } + else + { + sb_printf( sb_err, + L"Send job %d '%ls' to background\n", + j->job_id, + j->command ); + } + make_first( j ); + j->fg=0; + job_continue( j, job_is_stopped(j) ); +} + + +/** + Builtin for putting a job in the background +*/ +static int builtin_bg( wchar_t **argv ) +{ + if( argv[1] == 0 ) + { + job_t *j; + for( j=first_job; ((j!=0) && (!j->constructed) && (!job_is_stopped(j))); j=j->next ) + ; + send_to_bg( j, L"(default)"); + return 0; + } + for( argv++; *argv != 0; argv++ ) + { + int pid = wcstol( *argv, 0, 10 ); + send_to_bg( job_get_from_pid( pid ), *argv); + } + return 0; +} + + +#ifdef HAVE__PROC_SELF_STAT +/** + Calculates the cpu usage (in percent) of the specified job. +*/ +static int cpu_use( job_t *j ) +{ + double u=0; + process_t *p; + + for( p=j->first_process; p; p=p->next ) + { + struct timeval t; + int jiffies; + gettimeofday( &t, 0 ); + jiffies = proc_get_jiffies( p ); + + double t1 = 1000000.0*p->last_time.tv_sec+p->last_time.tv_usec; + double t2 = 1000000.0*t.tv_sec+t.tv_usec; + +/* fwprintf( stderr, L"t1 %f t2 %f p1 %d p2 %d\n", + t1, t2, jiffies, p->last_jiffies ); +*/ + + u += ((double)(jiffies-p->last_jiffies))/(t2-t1); + } + return u*1000000; +} +#endif + +/** + Builtin for printing running jobs +*/ +static int builtin_jobs( wchar_t **argv ) +{ + + enum + { + DEFAULT, + PRINT_PID, + PRINT_COMMAND + } + ; + + + int argc=0; + job_t *j; + int found=0; + int mode=DEFAULT; + argc = builtin_count_args( argv ); + + woptind=0; + + while( 1 ) + { + const static struct woption + long_options[] = + { + { + L"pid", no_argument, 0, 'p' + } + , + { + L"command", no_argument, 0, 'c' + } + , + { + 0, 0, 0, 0 + } + } + ; + + int opt_index = 0; + + int opt = wgetopt_long( argc, + argv, + L"pc", + long_options, + &opt_index ); + if( opt == -1 ) + break; + + switch( opt ) + { + case 0: + if(long_options[opt_index].flag != 0) + break; + sb_append2( sb_err, + argv[0], + L": Unknown option ", + long_options[opt_index].name, + L"\n", + 0 ); + sb_append( sb_err, + parser_current_line() ); +// builtin_print_help( argv[0], sb_err ); + + + return 1; + + + case 'p': + mode=PRINT_PID; + break; + + case 'c': + mode=PRINT_COMMAND; + break; + + case '?': + // builtin_print_help( argv[0], sb_err ); + + return 1; + + } + } + + if( mode==DEFAULT ) + { + + for( j= first_job; j; j=j->next ) + { + /* + Ignore unconstructed jobs, i.e. ourself. + */ + if( j->constructed ) + { + if( !found ) + { + /* + Print table header before first job + */ + sb_append( sb_out, L"Job\tGroup\t"); +#ifdef HAVE__PROC_SELF_STAT + sb_append( sb_out, L"CPU\t" ); +#endif + sb_append( sb_out, L"State\tCommand\n" ); + } + + found = 1; + + sb_printf( sb_out, L"%d\t%d\t", j->job_id, j->pgid ); + + +#ifdef HAVE__PROC_SELF_STAT + sb_printf( sb_out, L"%d\t", cpu_use(j) ); +#endif + sb_append2( sb_out, job_is_stopped(j)?L"stopped\t":L"running\t", + j->command, L"\n", 0 ); + + } + } + if( !found ) + { + sb_append2( sb_out, argv[0], L": There are no running jobs\n", 0 ); + } + } + else + { + long pid; + wchar_t *end; + job_t *j; + + if( woptind != argc-1 ) + { + sb_append2( sb_err, argv[0], L": Expected exactly one argument\n", 0 ); + } + + + errno=0; + pid=wcstol( argv[woptind], &end, 10 ); + if( errno || *end ) + { + sb_append2( sb_err, argv[0], L": Not a process id: ", argv[woptind], L"\n", 0 ); + return 1; + + } + + j = job_get_from_pid( pid ); + if( !j ) + { + sb_printf( sb_err, L"%ls: No suitable job: %d\n", argv[0], pid ); + return 1; + } + process_t *p; + for( p=j->first_process; p; p=p->next ) + { + switch( mode ) + { + case PRINT_PID: + { + sb_printf( sb_out, L"%d\n", p->pid ); + break; + } + + case PRINT_COMMAND: + { + sb_printf( sb_out, L"%ls\n", p->argv[0] ); + break; + } + } + } + } + + return 0; +} + +/** + Builtin for looping over a list +*/ +static int builtin_for( wchar_t **argv ) +{ + int argc = builtin_count_args( argv ); + int res=1; + + + if( argc < 3) + { + sb_append2( sb_err, + argv[0], + L": Expected at least two arguments\n", + 0); + builtin_print_help( argv[0], sb_err ); + } + else if ( !wcsvarname(argv[1]) ) + { + sb_append2( sb_err, + argv[0], + L": \'", + argv[1], + L"\' invalid variable name\n", + 0); + builtin_print_help( argv[0], sb_err ); + } + else if (wcscmp( argv[2], L"in") != 0 ) + { + sb_append2( sb_err, + argv[0], + L": Second argument must be \'in\'\n", + 0); + builtin_print_help( argv[0], sb_err ); + } + else + { + res=0; + } + + + if( res ) + { + parser_push_block( FAKE ); + } + else + { + parser_push_block( FOR ); + al_init( ¤t_block->for_vars); + + int i; + current_block->tok_pos = parser_get_pos(); + current_block->for_variable = wcsdup( argv[1] ); + + for( i=argc-1; i>3; i-- ) + { + al_push( ¤t_block->for_vars, wcsdup(argv[ i ] )); + } + if( argc > 3 ) + { + env_set( current_block->for_variable, argv[3], 0); + } + else + { + current_block->skip=1; + } + } + return res; +} + +static int builtin_begin( wchar_t **argv ) +{ + parser_push_block( BEGIN ); + current_block->tok_pos = parser_get_pos(); + return 0; +} + + +/** + Builtin for ending a block of code, such as a for-loop or an if statement. + + The end command is whare a lot of the block-level magic happens. +*/ +static int builtin_end( wchar_t **argv ) +{ + if( !current_block->outer || + current_block->type == OR || + current_block->type == AND ) + { + sb_append2( sb_err, + argv[0], + L": Not inside of block\n", + 0); + builtin_print_help( argv[0], sb_err ); + return 1; + } + else + { + /** + By default, 'end' kills the current block scope. But if we + are rewinding a loop, this should be set to false, so that + variables in the current loop scope won't die between laps. + */ + int kill_block = 1; + + switch( current_block->type ) + { + case WHILE: + { + /* + If this is a while loop, we rewind the loop unless + it's the last lap, in which case we continue. + */ + if( !( current_block->skip && (current_block->loop_status != LOOP_CONTINUE ))) + { + current_block->loop_status = LOOP_NORMAL; + current_block->skip = 0; + kill_block = 0; + parser_set_pos( current_block->tok_pos); + current_block->while_state = WHILE_TEST_AGAIN; + } + + break; + } + + case IF: + case SUBST: + case BEGIN: + /* + Nothing special happens at the end of these. The scope just ends. + */ + + break; + + case FOR: + { + /* + set loop variable to next element, and rewind to the beginning of the block. + */ + if( current_block->loop_status == LOOP_BREAK ) + { + while( al_get_count( ¤t_block->for_vars ) ) + { + free( (void *)al_pop( ¤t_block->for_vars ) ); + } + } + + if( al_get_count( ¤t_block->for_vars ) ) + { + wchar_t *val = (wchar_t *)al_pop( ¤t_block->for_vars ); + env_set( current_block->for_variable, val, 0); + current_block->loop_status = LOOP_NORMAL; + current_block->skip = 0; + free(val); + + kill_block = 0; + parser_set_pos( current_block->tok_pos ); +/* + fwprintf( stderr, + L"jump to %d\n", + current_block->tok_pos ); */ + } + break; + } + + case FUNCTION_DEF: + { + /** + Copy the text from the beginning of the function + until the end command and use as the new definition + for the specified function + */ + wchar_t *def = wcsndup( parser_get_buffer()+current_block->tok_pos, + parser_get_job_pos()-current_block->tok_pos ); + + //fwprintf( stderr, L"Function: %ls\n", def ); + if( !parser_test( def, 1 ) ) + { + function_add( current_block->function_name, + def, + current_block->function_description); + } + free(def); + } + break; + + } + if( kill_block ) + { + parser_pop_block(); + } +// fwprintf( stderr, L"End with status %d\n", proc_get_last_status() ); + + + /* + If everything goes ok, return status of last command to execute. + */ + return proc_get_last_status(); + } +} + +/** + Builtin for executing commands if an if statement is false +*/ +static int builtin_else( wchar_t **argv ) +{ + if( current_block == 0 || + current_block->type != IF || + current_block->if_state != 1) + { + sb_append2( sb_err, + argv[0], + L": not inside of if block\n", + 0); + builtin_print_help( argv[0], sb_err ); + return 1; + } + else + { + current_block->if_state++; + current_block->skip = !current_block->skip; + env_pop(); + env_push(0); + } + + /* + If everything goes ok, return status of last command to execute. + */ + return proc_get_last_status(); +} + +/** + This function handles both the 'continue' and the 'break' builtins + that are used for loop control. +*/ +static int builtin_break_continue( wchar_t **argv ) +{ + int is_break = (wcscmp(argv[0],L"break")==0); + int argc = builtin_count_args( argv ); + + block_t *b = current_block; + + if( argc != 1 ) + { + sb_append2( sb_err, + argv[0], + L": Unknown option \'", argv[1], L"\'", 0 ); + builtin_print_help( argv[0], sb_err ); + return 1; + } + + + while( (b != 0) && + ( b->type != WHILE) && + (b->type != FOR ) ) + { + b = b->outer; + } + + if( b == 0 ) + { + sb_append2( sb_err, + argv[0], + L": Not inside of loop\n", 0 ); + builtin_print_help( argv[0], sb_err ); + return 1; + } + + b = current_block; + while( ( b->type != WHILE) && + (b->type != FOR ) ) + { + b->skip=1; + b = b->outer; + } + b->skip=1; + b->loop_status = is_break?LOOP_BREAK:LOOP_CONTINUE; + return 0; +} + +/** + Function for handling the \c return builtin +*/ +static int builtin_return( wchar_t **argv ) +{ + int argc = builtin_count_args( argv ); + int status = 0; + + block_t *b = current_block; + + switch( argc ) + { + case 1: + break; + case 2: + { + wchar_t *end; + errno = 0; + status = wcstol(argv[1],&end,10); + if( errno || *end != 0) + { + sb_append2( sb_err, + argv[0], + L": Argument must be an integer '", + argv[1], + L"'\n", + 0 ); + builtin_print_help( argv[0], sb_err ); + return 1; + } +// fwprintf( stderr, L"Return with status %d\n", status ); + break; + } + default: + sb_append2( sb_err, + argv[0], + L": Too many arguments\n", 0 ); + builtin_print_help( argv[0], sb_err ); + return 1; + } + + + while( (b != 0) && + ( b->type != FUNCTION_CALL) ) + { + b = b->outer; + } + + if( b == 0 ) + { + sb_append2( sb_err, + argv[0], + L": Not inside of function\n", 0 ); + builtin_print_help( argv[0], sb_err ); + return 1; + } + + b = current_block; + while( ( b->type != FUNCTION_CALL)) + { + b->skip=1; + b = b->outer; + } + b->skip=1; +// proc_set_last_status( status ); + + return status; +} + +/** + Builtin for executing one of several blocks of commands depending on the value of an argument. +*/ +static int builtin_switch( wchar_t **argv ) +{ + int res=0; + int argc = builtin_count_args( argv ); + + if( argc != 2 ) + { + sb_printf( sb_err, + L"%ls : syntax error, expected exactly one argument, got %d\n", + argv[0], + argc-1 ); + + builtin_print_help( argv[0], sb_err ); + res=1; + parser_push_block( FAKE ); + } + else + { + parser_push_block( SWITCH ); + current_block->switch_value = wcsdup( argv[1]); + current_block->skip=1; + current_block->switch_taken=0; + } + + return res; +} + +/** + Builtin used together with the switch builtin for conditional execution +*/ +static int builtin_case( wchar_t **argv ) +{ + int argc = builtin_count_args( argv ); + int i; + wchar_t *unescaped=0; + + if( current_block->type != SWITCH ) + { + sb_append2( sb_err, + argv[0], + L": syntax error, case command while not in switch block\n", + 0); + builtin_print_help( L"case", sb_err ); + return 1; + } + + current_block->skip = 1; + + if( current_block->switch_taken ) + { + return 0; + } + + for( i=1; i<argc; i++ ) + { + free( unescaped ); + unescaped = expand_backslash( wcsdup( argv[i] ), 1); + + if( wildcard_match( current_block->switch_value, unescaped ) ) + { + current_block->skip = 0; + current_block->switch_taken = 1; + break; + } + } + free( unescaped ); + + return 0; +} + + +/* + END OF BUILTIN COMMANDS + Below are functions for handling the builtin commands +*/ +void builtin_init() +{ + al_init( &io_stack ); + hash_init( &builtin, &hash_wcs_func, &hash_wcs_cmp ); + + hash_put( &builtin, L"exit", &builtin_exit ); + hash_put( &builtin, L"builtin", &builtin_builtin ); + hash_put( &builtin, L"cd", &builtin_cd ); + hash_put( &builtin, L"function", &builtin_function ); + hash_put( &builtin, L"functions", &builtin_functions ); + hash_put( &builtin, L"complete", &builtin_complete ); + hash_put( &builtin, L"end", &builtin_end ); + hash_put( &builtin, L"else", &builtin_else ); + hash_put( &builtin, L"eval", &builtin_eval ); + hash_put( &builtin, L"for", &builtin_for ); + hash_put( &builtin, L".", &builtin_source ); + hash_put( &builtin, L"set", &builtin_set ); + hash_put( &builtin, L"fg", &builtin_fg ); + hash_put( &builtin, L"bg", &builtin_bg ); + hash_put( &builtin, L"jobs", &builtin_jobs ); + hash_put( &builtin, L"read", &builtin_read ); + hash_put( &builtin, L"break", &builtin_break_continue ); + hash_put( &builtin, L"continue", &builtin_break_continue ); + hash_put( &builtin, L"return", &builtin_return ); + hash_put( &builtin, L"commandline", &builtin_commandline ); + hash_put( &builtin, L"switch", &builtin_switch ); + hash_put( &builtin, L"case", &builtin_case ); + hash_put( &builtin, L"bind", &builtin_bind ); + hash_put( &builtin, L"random", &builtin_random ); + hash_put( &builtin, L"status", &builtin_status ); + + /* + Builtins that are handled directly by the parser. They are + bound to a noop function only so that they show up in the + listings of builtin commands, etc.. + */ + hash_put( &builtin, L"command", &builtin_ignore ); + hash_put( &builtin, L"if", &builtin_ignore ); + hash_put( &builtin, L"while", &builtin_ignore ); + hash_put( &builtin, L"not", &builtin_generic ); + hash_put( &builtin, L"and", &builtin_generic ); + hash_put( &builtin, L"or", &builtin_generic ); + hash_put( &builtin, L"exec", &builtin_exec ); + hash_put( &builtin, L"begin", &builtin_begin ); + + /* + This is not a builtin, but fish handles it's help display + internally, to do some ugly special casing to make sure 'count + -h', but 'count (echo -h)' does not. + */ + hash_put( &builtin, L"count", &builtin_ignore ); + + intern_static( L"exit" ); + intern_static( L"builtin" ); + intern_static( L"cd" ); + intern_static( L"function" ); + intern_static( L"functions" ); + intern_static( L"complete" ); + intern_static( L"end" ); + intern_static( L"else" ); + intern_static( L"eval" ); + intern_static( L"for" ); + intern_static( L"." ); + intern_static( L"set" ); + intern_static( L"fg" ); + intern_static( L"bg" ); + intern_static( L"jobs" ); + intern_static( L"read" ); + intern_static( L"break" ); + intern_static( L"continue" ); + intern_static( L"return" ); + intern_static( L"commandline" ); + intern_static( L"switch" ); + intern_static( L"case" ); + intern_static( L"bind" ); + intern_static( L"random" ); + intern_static( L"command" ); + intern_static( L"if" ); + intern_static( L"while" ); + intern_static( L"exec" ); + intern_static( L"count" ); + intern_static( L"not" ); + intern_static( L"and" ); + intern_static( L"or" ); + intern_static( L"begin" ); + intern_static( L"status" ); + + builtin_help_init(); +} + +void builtin_destroy() +{ + if( desc ) + { + hash_destroy( desc ); + free( desc ); + } + + al_destroy( &io_stack ); + hash_destroy( &builtin ); + builtin_help_destroy(); +} + +int builtin_exists( wchar_t *cmd ) +{ + /* + Count is not a builtin, but it's help is handled internally by + fish, so it is in the hash_table_t. + */ + if( wcscmp( cmd, L"count" )==0) + return 0; + + return (hash_get(&builtin, cmd) != 0 ); +} + +/** + Return true if the specified builtin should handle it's own help, + false otherwise. +*/ +static int internal_help( wchar_t *cmd ) +{ + if( wcscmp( cmd, L"for" ) == 0 || + wcscmp( cmd, L"while" ) == 0 || + wcscmp( cmd, L"function" ) == 0 || + wcscmp( cmd, L"if" ) == 0 || + wcscmp( cmd, L"end" ) == 0 || + wcscmp( cmd, L"switch" ) == 0 ) + return 1; + return 0; +} + + +int builtin_run( wchar_t **argv ) +{ + int (*cmd)(wchar_t **argv)=0; + cmd = hash_get( &builtin, argv[0] ); + + if( argv[1] != 0 && !internal_help(argv[0]) ) + { + if( argv[2] == 0 && (parser_is_help( argv[1], 0 ) ) ) + { + builtin_print_help( argv[0], sb_out ); + return 0; + } + } + + if( cmd != 0 ) + { + int status; + + status = cmd(argv); +// fwprintf( stderr, L"Builtin: Set status of %ls to %d\n", argv[0], status ); + + return status; + + } + else + { + debug( 0, L"Unknown builtin: ", argv[0], 0 ); + } + return 1; +} + + +void builtin_get_names( array_list_t *list ) +{ + hash_get_keys( &builtin, list ); +} + +const wchar_t *builtin_get_desc( const wchar_t *b ) +{ + + if( !desc ) + { + desc = malloc( sizeof( hash_table_t ) ); + if( !desc) + return 0; + + hash_init( desc, &hash_wcs_func, &hash_wcs_cmp ); + + hash_put( desc, L"exit", L"Exit the shell" ); + hash_put( desc, L"cd", L"Change working directory" ); + hash_put( desc, L"function", L"Define a new function" ); + hash_put( desc, L"functions", L"List or remove functions" ); + hash_put( desc, L"complete", L"Edit command specific completions" ); + hash_put( desc, L"end", L"End a block of commands" ); + hash_put( desc, L"else", L"Evaluate block if condition is false" ); + hash_put( desc, L"eval", L"Evaluate parameters as a command" ); + hash_put( desc, L"for", L"Perform a set of commands multiple times" ); + hash_put( desc, L".", L"Evaluate contents of file" ); + hash_put( desc, L"set", L"Handle environment variables" ); + hash_put( desc, L"fg", L"Send job to foreground" ); + hash_put( desc, L"bg", L"Send job to background" ); + hash_put( desc, L"jobs", L"Print currently running jobs" ); + hash_put( desc, L"read", L"Read a line of input into variables" ); + hash_put( desc, L"break", L"Stop the innermost loop" ); + hash_put( desc, L"continue", L"Skip the rest of the current lap of the innermost loop" ); + hash_put( desc, L"return", L"Stop the innermost currently evaluated function" ); + hash_put( desc, L"commandline", L"Set the commandline" ); + hash_put( desc, L"switch", L"Conditionally execute a block of commands" ); + hash_put( desc, L"case", L"Conditionally execute a block of commands" ); + hash_put( desc, L"builtin", L"Run a builtin command" ); + hash_put( desc, L"command", L"Run a program" ); + hash_put( desc, L"if", L"Conditionally execute a command" ); + hash_put( desc, L"while", L"Perform a command multiple times" ); + hash_put( desc, L"bind", L"Handle key bindings"); + hash_put( desc, L"random", L"Generate random number"); + hash_put( desc, L"exec", L"Run command in current process"); + hash_put( desc, L"not", L"Negate exit status of job"); + hash_put( desc, L"or", L"Execute second command if first fails"); + hash_put( desc, L"and", L"Execute second command if first suceeds"); + hash_put( desc, L"begin", L"Create a block of code" ); + hash_put( desc, L"status", L"Return status information about fish" ); + } + + return hash_get( desc, b ); +} + + +void builtin_push_io( int in) +{ + if( builtin_stdin != -1 ) + { + al_push( &io_stack, (void *)(long)builtin_stdin ); + al_push( &io_stack, sb_out ); + al_push( &io_stack, sb_err ); + } + builtin_stdin = in; + sb_out = malloc(sizeof(string_buffer_t)); + sb_err = malloc(sizeof(string_buffer_t)); + sb_init( sb_out ); + sb_init( sb_err ); +} + +void builtin_pop_io() +{ + builtin_stdin = 0; + sb_destroy( sb_out ); + sb_destroy( sb_err ); + free( sb_out); + free(sb_err); + + if( al_get_count( &io_stack ) >0 ) + { + sb_err = (string_buffer_t *)al_pop( &io_stack ); + sb_out = (string_buffer_t *)al_pop( &io_stack ); + builtin_stdin = (int)(long)al_pop( &io_stack ); + } + else + { + sb_out = sb_err = 0; + builtin_stdin = 0; + } +} + |