aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/builtin_commandline.cpp
diff options
context:
space:
mode:
authorGravatar ridiculousfish <corydoras@ridiculousfish.com>2015-07-24 00:50:58 -0700
committerGravatar ridiculousfish <corydoras@ridiculousfish.com>2015-07-24 00:59:27 -0700
commitb4f53143b0e05fd3061cdf2e65e17a6a2904090b (patch)
tree4785bf31f7b89fc2420aa740d9a6967dc6c6f9b1 /src/builtin_commandline.cpp
parent9c2fdc6da57032c4448b59de5872086eea626b74 (diff)
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
Diffstat (limited to 'src/builtin_commandline.cpp')
-rw-r--r--src/builtin_commandline.cpp674
1 files changed, 674 insertions, 0 deletions
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 <stdlib.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <signal.h>
+
+#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<const wchar_t *> saved_current_buffer(&current_buffer);
+ scoped_push<size_t> saved_current_cursor_pos(&current_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<argc; i++)
+ {
+ wchar_t c = input_function_get_code(argv[i]);
+ if (c != (wchar_t)(-1))
+ {
+ /*
+ input_unreadch inserts the specified keypress or
+ readline function at the back of the queue of unused
+ keypresses
+ */
+ input_queue_ch(c);
+ }
+ else
+ {
+ append_format(stderr_buffer,
+ _(L"%ls: Unknown input function '%ls'\n"),
+ argv[0],
+ argv[i]);
+ builtin_print_help(parser, argv[0], stderr_buffer);
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+ if (selection_mode)
+ {
+ size_t start, len;
+ const wchar_t *buffer = reader_get_buffer();
+ if (reader_get_selection(&start, &len))
+ {
+ stdout_buffer.append(buffer + start, len);
+ }
+ return 0;
+ }
+
+ /*
+ Check for invalid switch combinations
+ */
+ if ((search_mode || line_mode || cursor_mode || paging_mode) && (argc-woptind > 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<argc; i++)
+ {
+ sb.push_back(L'\n');
+ sb.append(argv[i]);
+ }
+
+ replace_part(begin, end, sb.c_str(), append_mode);
+
+ break;
+ }
+ }
+
+ return 0;
+}