/** \file builtin_commandline.c Functions defining the commandline builtin Functions used for implementing the commandline builtin. */ #include "config.h" #include #include #include #include #include #include #include #include "fallback.h" #include "util.h" #include "wutil.h" #include "builtin.h" #include "common.h" #include "wgetopt.h" #include "reader.h" #include "proc.h" #include "parser.h" #include "tokenizer.h" #include "input_common.h" #include "input.h" #include "parse_util.h" /** Which part of the comandbuffer are we operating on */ enum { STRING_MODE=1, /**< Operate on entire buffer */ JOB_MODE, /**< Operate on job under cursor */ PROCESS_MODE, /**< Operate on process under cursor */ TOKEN_MODE /**< Operate on token under cursor */ } ; /** For text insertion, how should it be done */ enum { REPLACE_MODE=1, /**< Replace current text */ INSERT_MODE, /**< Insert at cursor position */ APPEND_MODE /**< Insert at end of current token/command/buffer */ } ; /** Pointer to what the commandline builtin considers to be the current contents of the command line buffer. */ static wchar_t *current_buffer=0; /** What the commandline builtin considers to be the current cursor position. */ static int current_cursor_pos = -1; /** Returns the current commandline buffer. */ static wchar_t *get_buffer() { return current_buffer; } /** Returns the position of the cursor */ static int get_cursor_pos() { return current_cursor_pos; } /** Replace/append/insert the selection with/at/after the specified string. \param begin beginning of selection \param end end of selection \param insert the string to insert \param append_mode can be one of REPLACE_MODE, INSERT_MODE or APPEND_MODE, affects the way the test update is performed */ static void replace_part( const wchar_t *begin, const wchar_t *end, wchar_t *insert, int append_mode ) { const wchar_t *buff = get_buffer(); string_buffer_t out; int out_pos=get_cursor_pos(); sb_init( &out ); sb_append_substring( &out, buff, begin-buff ); switch( append_mode) { case REPLACE_MODE: { sb_append( &out, insert ); out_pos = wcslen( insert ) + (begin-buff); break; } case APPEND_MODE: { sb_append_substring( &out, begin, end-begin ); sb_append( &out, insert ); break; } case INSERT_MODE: { int cursor = get_cursor_pos() -(begin-buff); sb_append_substring( &out, begin, cursor ); sb_append( &out, insert ); sb_append_substring( &out, begin+cursor, end-begin-cursor ); out_pos += wcslen( insert ); break; } } sb_append( &out, end ); reader_set_buffer( (wchar_t *)out.buff, out_pos ); sb_destroy( &out ); } /** Output the specified selection. \param begin start of selection \param end end of selection \param cut_at_cursor whether printing should stop at the surrent cursor position \param tokenize whether the string should be tokenized, printing one string token on every line and skipping non-string tokens */ static void write_part( const wchar_t *begin, const wchar_t *end, int cut_at_cursor, int tokenize ) { tokenizer tok; string_buffer_t out; wchar_t *buff; size_t pos; pos = get_cursor_pos()-(begin-get_buffer()); if( tokenize ) { buff = wcsndup( begin, end-begin ); // fwprintf( stderr, L"Subshell: %ls, end char %lc\n", buff, *end ); sb_init( &out ); for( tok_init( &tok, buff, TOK_ACCEPT_UNFINISHED ); tok_has_next( &tok ); tok_next( &tok ) ) { if( (cut_at_cursor) && (tok_get_pos( &tok)+wcslen(tok_last( &tok)) >= pos) ) break; switch( tok_last_type( &tok ) ) { case TOK_STRING: { wchar_t *tmp = unescape( tok_last( &tok ), UNESCAPE_INCOMPLETE ); sb_append( &out, tmp, L"\n", NULL ); free( tmp ); break; } } } sb_append( sb_out, (wchar_t *)out.buff ); free( buff ); tok_destroy( &tok ); sb_destroy( &out ); } else { wchar_t *buff, *esc; if( cut_at_cursor ) { end = begin+pos; } buff = wcsndup( begin, end-begin ); esc = unescape( buff, UNESCAPE_INCOMPLETE ); // debug( 0, L"woot2 %ls -> %ls", buff, esc ); sb_append( sb_out, esc ); sb_append( sb_out, L"\n" ); free( esc ); free( buff ); } } /** The commandline builtin. It is used for specifying a new value for the commandline. */ static int builtin_commandline( parser_t &parser, wchar_t **argv ) { int buffer_part=0; int cut_at_cursor=0; int argc = builtin_count_args( argv ); int append_mode=0; int function_mode = 0; int tokenize = 0; int cursor_mode = 0; int line_mode = 0; int search_mode = 0; wchar_t *begin, *end; current_buffer = (wchar_t *)builtin_complete_get_temporary_buffer(parser); if( current_buffer ) { current_cursor_pos = wcslen( current_buffer ); } else { current_buffer = reader_get_buffer(); current_cursor_pos = reader_get_cursor_pos(); } if( !get_buffer() ) { if (is_interactive_session) { /* Prompt change requested while we don't have a prompt, most probably while reading the init files. Just ignore it. */ return 1; } sb_append( sb_err, argv[0], L": Can not set commandline in non-interactive mode\n", NULL ); builtin_print_help( parser, argv[0], sb_err ); return 1; } woptind=0; while( 1 ) { static const struct woption long_options[] = { { L"append", no_argument, 0, 'a' } , { L"insert", no_argument, 0, 'i' } , { L"replace", no_argument, 0, 'r' } , { L"current-job", no_argument, 0, 'j' } , { L"current-process", no_argument, 0, 'p' } , { L"current-token", no_argument, 0, 't' } , { L"current-buffer", no_argument, 0, 'b' } , { L"cut-at-cursor", no_argument, 0, 'c' } , { L"function", no_argument, 0, 'f' } , { L"tokenize", no_argument, 0, 'o' } , { L"help", no_argument, 0, 'h' } , { L"input", required_argument, 0, 'I' } , { L"cursor", no_argument, 0, 'C' } , { L"line", no_argument, 0, 'L' } , { L"search-mode", no_argument, 0, 'S' } , { 0, 0, 0, 0 } } ; int opt_index = 0; int opt = wgetopt_long( argc, argv, L"abijpctwforhI:CLS", long_options, &opt_index ); if( opt == -1 ) break; switch( opt ) { case 0: if(long_options[opt_index].flag != 0) break; sb_printf( sb_err, BUILTIN_ERR_UNKNOWN, argv[0], long_options[opt_index].name ); builtin_print_help( parser, argv[0], sb_err ); return 1; case L'a': append_mode = APPEND_MODE; break; case L'b': buffer_part = STRING_MODE; break; case L'i': append_mode = INSERT_MODE; break; case L'r': append_mode = REPLACE_MODE; break; case 'c': cut_at_cursor=1; break; case 't': buffer_part = TOKEN_MODE; break; case 'j': buffer_part = JOB_MODE; break; case 'p': buffer_part = PROCESS_MODE; break; case 'f': function_mode=1; break; case 'o': tokenize=1; break; case 'I': current_buffer = woptarg; current_cursor_pos = wcslen( woptarg ); break; case 'C': cursor_mode = 1; break; case 'L': line_mode = 1; break; case 'S': search_mode = 1; break; case 'h': builtin_print_help( parser, argv[0], sb_out ); return 0; case L'?': builtin_unknown_option( parser, argv[0], argv[woptind-1] ); return 1; } } if( function_mode ) { int i; /* Check for invalid switch combinations */ if( buffer_part || cut_at_cursor || append_mode || tokenize || cursor_mode || line_mode || search_mode ) { sb_printf(sb_err, BUILTIN_ERR_COMBO, argv[0] ); builtin_print_help( parser, argv[0], sb_err ); return 1; } if( argc == woptind ) { sb_printf( sb_err, BUILTIN_ERR_MISSING, argv[0] ); builtin_print_help( parser, argv[0], sb_err ); return 1; } for( i=woptind; i 1) ) { sb_append( sb_err, argv[0], L": Too many arguments\n", NULL ); builtin_print_help( parser, argv[0], sb_err ); return 1; } if( (buffer_part || tokenize || cut_at_cursor) && (cursor_mode || line_mode || search_mode) ) { sb_printf( sb_err, BUILTIN_ERR_COMBO, argv[0] ); builtin_print_help( parser, argv[0], sb_err ); return 1; } if( (tokenize || cut_at_cursor) && (argc-woptind) ) { sb_printf( sb_err, BUILTIN_ERR_COMBO2, argv[0], L"--cut-at-cursor and --tokenize can not be used when setting the commandline" ); builtin_print_help( parser, argv[0], sb_err ); return 1; } if( append_mode && !(argc-woptind) ) { sb_printf( sb_err, BUILTIN_ERR_COMBO2, argv[0], L"insertion mode switches can not be used when not in insertion mode" ); builtin_print_help( parser, argv[0], sb_err ); return 1; } /* Set default modes */ if( !append_mode ) { append_mode = REPLACE_MODE; } if( !buffer_part ) { buffer_part = STRING_MODE; } if( cursor_mode ) { if( argc-woptind ) { wchar_t *endptr; int new_pos; errno = 0; new_pos = wcstol( argv[woptind], &endptr, 10 ); if( *endptr || errno ) { sb_printf( sb_err, BUILTIN_ERR_NOT_NUMBER, argv[0], argv[woptind] ); builtin_print_help( parser, argv[0], sb_err ); } current_buffer = reader_get_buffer(); new_pos = maxi( 0, mini( new_pos, wcslen( current_buffer ) ) ); reader_set_buffer( current_buffer, new_pos ); return 0; } else { sb_printf( sb_out, L"%d\n", reader_get_cursor_pos() ); return 0; } } if( line_mode ) { int pos = reader_get_cursor_pos(); wchar_t *buff = reader_get_buffer(); sb_printf( sb_out, L"%d\n", parse_util_lineno( buff, pos ) ); return 0; } if( search_mode ) { return !reader_search_mode(); } switch( buffer_part ) { case STRING_MODE: { begin = get_buffer(); end = begin+wcslen(begin); break; } case PROCESS_MODE: { parse_util_process_extent( get_buffer(), get_cursor_pos(), &begin, &end ); break; } case JOB_MODE: { parse_util_job_extent( get_buffer(), get_cursor_pos(), &begin, &end ); break; } case TOKEN_MODE: { parse_util_token_extent( get_buffer(), get_cursor_pos(), &begin, &end, 0, 0 ); break; } } switch(argc-woptind) { case 0: { write_part( begin, end, cut_at_cursor, tokenize ); break; } case 1: { replace_part( begin, end, argv[woptind], append_mode ); break; } default: { string_buffer_t sb; int i; sb_init( &sb ); sb_append( &sb, argv[woptind] ); for( i=woptind+1; i