From b4f53143b0e05fd3061cdf2e65e17a6a2904090b Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 24 Jul 2015 00:50:58 -0700 Subject: Migrate source files into src/ directory This change moves source files into a src/ directory, and puts object files into an obj/ directory. The Makefile and xcode project are updated accordingly. Fixes #1866 --- src/builtin_commandline.cpp | 674 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 src/builtin_commandline.cpp (limited to 'src/builtin_commandline.cpp') diff --git a/src/builtin_commandline.cpp b/src/builtin_commandline.cpp new file mode 100644 index 00000000..01bc123a --- /dev/null +++ b/src/builtin_commandline.cpp @@ -0,0 +1,674 @@ +/** \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 const wchar_t *current_buffer=0; +/** + What the commandline builtin considers to be the current cursor + position. + */ +static size_t current_cursor_pos = (size_t)(-1); + +/** + Returns the current commandline buffer. +*/ +static const wchar_t *get_buffer() +{ + return current_buffer; +} + +/** + Returns the position of the cursor +*/ +static size_t get_cursor_pos() +{ + return current_cursor_pos; +} + +static pthread_mutex_t transient_commandline_lock = PTHREAD_MUTEX_INITIALIZER; +static wcstring_list_t *get_transient_stack() +{ + ASSERT_IS_MAIN_THREAD(); + ASSERT_IS_LOCKED(transient_commandline_lock); + // A pointer is a little more efficient than an object as a static because we can elide the thread-safe initialization + static wcstring_list_t *result = NULL; + if (! result) + { + result = new wcstring_list_t(); + } + return result; +} + +static bool get_top_transient(wcstring *out_result) +{ + ASSERT_IS_MAIN_THREAD(); + bool result = false; + scoped_lock locker(transient_commandline_lock); + const wcstring_list_t *stack = get_transient_stack(); + if (! stack->empty()) + { + out_result->assign(stack->back()); + result = true; + } + return result; +} + +builtin_commandline_scoped_transient_t::builtin_commandline_scoped_transient_t(const wcstring &cmd) +{ + ASSERT_IS_MAIN_THREAD(); + scoped_lock locker(transient_commandline_lock); + wcstring_list_t *stack = get_transient_stack(); + stack->push_back(cmd); + this->token = stack->size(); +} + +builtin_commandline_scoped_transient_t::~builtin_commandline_scoped_transient_t() +{ + ASSERT_IS_MAIN_THREAD(); + scoped_lock locker(transient_commandline_lock); + wcstring_list_t *stack = get_transient_stack(); + assert(this->token == stack->size()); + stack->pop_back(); +} + +/** + 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, + const wchar_t *insert, + int append_mode) +{ + const wchar_t *buff = get_buffer(); + size_t out_pos = get_cursor_pos(); + + wcstring out; + + out.append(buff, begin - buff); + + switch (append_mode) + { + case REPLACE_MODE: + { + + out.append(insert); + out_pos = wcslen(insert) + (begin-buff); + break; + + } + case APPEND_MODE: + { + out.append(begin, end-begin); + out.append(insert); + break; + } + case INSERT_MODE: + { + long cursor = get_cursor_pos() -(begin-buff); + out.append(begin, cursor); + out.append(insert); + out.append(begin+cursor, end-begin-cursor); + out_pos += wcslen(insert); + break; + } + } + out.append(end); + reader_set_buffer(out, out_pos); +} + +/** + 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) +{ + size_t pos = get_cursor_pos()-(begin-get_buffer()); + + if (tokenize) + { + wchar_t *buff = wcsndup(begin, end-begin); +// fwprintf( stderr, L"Subshell: %ls, end char %lc\n", buff, *end ); + wcstring out; + tokenizer_t tok(buff, TOK_ACCEPT_UNFINISHED); + for (; 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: + { + wcstring tmp = tok_last(&tok); + unescape_string_in_place(&tmp, UNESCAPE_INCOMPLETE); + out.append(tmp); + out.push_back(L'\n'); + break; + } + + default: + { + break; + } + } + } + + stdout_buffer.append(out); + + free(buff); + } + else + { + if (cut_at_cursor) + { + end = begin+pos; + } + +// debug( 0, L"woot2 %ls -> %ls", buff, esc ); + wcstring tmp = wcstring(begin, end - begin); + unescape_string_in_place(&tmp, UNESCAPE_INCOMPLETE); + stdout_buffer.append(tmp); + stdout_buffer.append(L"\n"); + + } +} + + +/** + 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 selection_mode = 0; + + int tokenize = 0; + + int cursor_mode = 0; + int line_mode = 0; + int search_mode = 0; + int paging_mode = 0; + const wchar_t *begin = NULL, *end = NULL; + + scoped_push saved_current_buffer(¤t_buffer); + scoped_push saved_current_cursor_pos(¤t_cursor_pos); + + wcstring transient_commandline; + if (get_top_transient(&transient_commandline)) + { + current_buffer = transient_commandline.c_str(); + current_cursor_pos = transient_commandline.size(); + } + 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; + } + + stderr_buffer.append(argv[0]); + stderr_buffer.append(L": Can not set commandline in non-interactive mode\n"); + builtin_print_help(parser, argv[0], stderr_buffer); + 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' }, + { L"selection", no_argument, 0, 's' }, + { L"paging-mode", no_argument, 0, 'P' }, + { 0, 0, 0, 0 } + }; + + int opt_index = 0; + + int opt = wgetopt_long(argc, + argv, + L"abijpctwforhI:CLSsP", + long_options, + &opt_index); + if (opt == -1) + break; + + switch (opt) + { + case 0: + if (long_options[opt_index].flag != 0) + break; + append_format(stderr_buffer, + BUILTIN_ERR_UNKNOWN, + argv[0], + long_options[opt_index].name); + builtin_print_help(parser, argv[0], stderr_buffer); + + 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 's': + selection_mode = 1; + break; + + case 'P': + paging_mode = 1; + break; + + case 'h': + builtin_print_help(parser, argv[0], stdout_buffer); + 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 || paging_mode) + { + append_format(stderr_buffer, + BUILTIN_ERR_COMBO, + argv[0]); + + builtin_print_help(parser, argv[0], stderr_buffer); + return 1; + } + + + if (argc == woptind) + { + append_format(stderr_buffer, + BUILTIN_ERR_MISSING, + argv[0]); + + builtin_print_help(parser, argv[0], stderr_buffer); + return 1; + } + for (i=woptind; i 1)) + { + + append_format(stderr_buffer, + argv[0], + L": Too many arguments\n", + NULL); + builtin_print_help(parser, argv[0], stderr_buffer); + return 1; + } + + if ((buffer_part || tokenize || cut_at_cursor) && (cursor_mode || line_mode || search_mode || paging_mode)) + { + append_format(stderr_buffer, + BUILTIN_ERR_COMBO, + argv[0]); + + builtin_print_help(parser, argv[0], stderr_buffer); + return 1; + } + + + if ((tokenize || cut_at_cursor) && (argc-woptind)) + { + append_format(stderr_buffer, + 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], stderr_buffer); + return 1; + } + + if (append_mode && !(argc-woptind)) + { + append_format(stderr_buffer, + BUILTIN_ERR_COMBO2, + argv[0], + L"insertion mode switches can not be used when not in insertion mode"); + + builtin_print_help(parser, argv[0], stderr_buffer); + 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; + long new_pos; + errno = 0; + + new_pos = wcstol(argv[woptind], &endptr, 10); + if (*endptr || errno) + { + append_format(stderr_buffer, + BUILTIN_ERR_NOT_NUMBER, + argv[0], + argv[woptind]); + builtin_print_help(parser, argv[0], stderr_buffer); + } + + current_buffer = reader_get_buffer(); + new_pos = maxi(0L, mini(new_pos, (long)wcslen(current_buffer))); + reader_set_buffer(current_buffer, (size_t)new_pos); + return 0; + } + else + { + append_format(stdout_buffer, L"%lu\n", (unsigned long)reader_get_cursor_pos()); + return 0; + } + + } + + if (line_mode) + { + size_t pos = reader_get_cursor_pos(); + const wchar_t *buff = reader_get_buffer(); + append_format(stdout_buffer, L"%lu\n", (unsigned long)parse_util_lineno(buff, pos)); + return 0; + + } + + if (search_mode) + { + return ! reader_search_mode(); + } + + if (paging_mode) + { + return ! reader_has_pager_contents(); + } + + + 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: + { + wcstring sb = argv[woptind]; + int i; + + for (i=woptind+1; i