2016-04-19 02:25:12 +00:00
|
|
|
// 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( parser_t &parser, 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 { L"NAME", &builtin_NAME, N_(L"Bla bla bla") }, to the builtin_data_t
|
|
|
|
// variable. The description is used by the completion system. Note that this array is sorted.
|
|
|
|
//
|
|
|
|
// 3). Create a file doc_src/NAME.txt, containing the manual for the builtin in Doxygen-format.
|
|
|
|
// Check the other builtin manuals for proper syntax.
|
|
|
|
//
|
|
|
|
// 4). Use 'git add doc_src/NAME.txt' to start tracking changes to the documentation file.
|
2016-05-18 22:30:21 +00:00
|
|
|
#include "config.h" // IWYU pragma: keep
|
2005-09-20 13:26:39 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
#include <assert.h>
|
2005-09-20 13:26:39 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2016-04-20 02:49:15 +00:00
|
|
|
#include <limits.h>
|
2016-04-19 02:25:12 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/stat.h>
|
2005-09-24 19:55:58 +00:00
|
|
|
#include <time.h>
|
2016-04-19 02:25:12 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <wchar.h>
|
|
|
|
#include <wctype.h>
|
2015-07-25 15:14:25 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <map>
|
2016-06-24 00:24:19 +00:00
|
|
|
#include <memory>
|
2015-07-25 15:14:25 +00:00
|
|
|
#include <string>
|
|
|
|
#include <utility>
|
2005-09-20 13:26:39 +00:00
|
|
|
|
|
|
|
#include "builtin.h"
|
2016-04-20 02:49:15 +00:00
|
|
|
#include "builtin_commandline.h"
|
|
|
|
#include "builtin_complete.h"
|
|
|
|
#include "builtin_jobs.h"
|
|
|
|
#include "builtin_printf.h"
|
|
|
|
#include "builtin_set.h"
|
|
|
|
#include "builtin_set_color.h"
|
|
|
|
#include "builtin_string.h"
|
|
|
|
#include "builtin_test.h"
|
|
|
|
#include "builtin_ulimit.h"
|
|
|
|
#include "common.h"
|
2005-09-20 13:26:39 +00:00
|
|
|
#include "complete.h"
|
|
|
|
#include "env.h"
|
2005-10-05 22:37:08 +00:00
|
|
|
#include "event.h"
|
2006-11-17 16:24:38 +00:00
|
|
|
#include "exec.h"
|
2016-04-19 02:25:12 +00:00
|
|
|
#include "expand.h"
|
2016-04-20 02:49:15 +00:00
|
|
|
#include "fallback.h" // IWYU pragma: keep
|
2016-04-19 02:25:12 +00:00
|
|
|
#include "function.h"
|
2007-01-29 16:26:24 +00:00
|
|
|
#include "highlight.h"
|
2016-04-19 02:25:12 +00:00
|
|
|
#include "history.h"
|
|
|
|
#include "input.h"
|
|
|
|
#include "intern.h"
|
2016-04-20 02:49:15 +00:00
|
|
|
#include "io.h"
|
2016-04-19 02:25:12 +00:00
|
|
|
#include "parse_constants.h"
|
2006-02-14 19:56:36 +00:00
|
|
|
#include "parse_util.h"
|
2016-04-19 02:25:12 +00:00
|
|
|
#include "parser.h"
|
2007-04-22 09:50:26 +00:00
|
|
|
#include "parser_keywords.h"
|
2006-10-19 11:50:23 +00:00
|
|
|
#include "path.h"
|
2016-04-19 02:25:12 +00:00
|
|
|
#include "proc.h"
|
|
|
|
#include "reader.h"
|
|
|
|
#include "signal.h"
|
|
|
|
#include "tokenizer.h"
|
2014-09-22 02:18:56 +00:00
|
|
|
#include "wcstringutil.h"
|
2016-04-19 02:25:12 +00:00
|
|
|
#include "wgetopt.h"
|
2016-04-20 02:49:15 +00:00
|
|
|
#include "wutil.h" // IWYU pragma: keep
|
2006-06-20 21:20:16 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// The default prompt for the read command.
|
2012-07-03 02:28:04 +00:00
|
|
|
#define DEFAULT_READ_PROMPT L"set_color green; echo -n read; set_color normal; echo -n \"> \""
|
2005-09-20 13:26:39 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// The mode name to pass to history and input.
|
2005-09-20 13:26:39 +00:00
|
|
|
#define READ_MODE_NAME L"fish_read"
|
2005-12-15 13:59:02 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// The send stuff to foreground message.
|
|
|
|
#define FG_MSG _(L"Send job %d, '%ls' to foreground\n")
|
2005-12-11 23:30:01 +00:00
|
|
|
|
2016-05-29 05:28:26 +00:00
|
|
|
/// Data structure to describe a builtin.
|
2016-04-19 02:25:12 +00:00
|
|
|
struct builtin_data_t {
|
|
|
|
// Name of the builtin.
|
2012-11-19 00:30:30 +00:00
|
|
|
const wchar_t *name;
|
2016-04-19 02:25:12 +00:00
|
|
|
// Function pointer tothe builtin implementation.
|
2015-09-21 18:24:49 +00:00
|
|
|
int (*func)(parser_t &parser, io_streams_t &streams, wchar_t **argv);
|
2016-04-19 02:25:12 +00:00
|
|
|
// Description of what the builtin does.
|
2012-11-19 00:30:30 +00:00
|
|
|
const wchar_t *desc;
|
|
|
|
|
2012-02-01 04:22:25 +00:00
|
|
|
bool operator<(const wcstring &) const;
|
2012-02-01 03:47:56 +00:00
|
|
|
bool operator<(const builtin_data_t *) const;
|
|
|
|
};
|
2006-02-05 13:08:40 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
bool builtin_data_t::operator<(const wcstring &other) const {
|
2012-02-01 04:22:25 +00:00
|
|
|
return wcscmp(this->name, other.c_str()) < 0;
|
2012-02-01 03:47:56 +00:00
|
|
|
}
|
2006-02-05 13:08:40 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
bool builtin_data_t::operator<(const builtin_data_t *other) const {
|
2012-02-01 03:47:56 +00:00
|
|
|
return wcscmp(this->name, other->name) < 0;
|
|
|
|
}
|
2005-09-20 13:26:39 +00:00
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Counts the number of non null pointers in the specified array.
|
2016-04-19 02:25:12 +00:00
|
|
|
int builtin_count_args(const wchar_t *const *argv) {
|
2012-11-19 00:30:30 +00:00
|
|
|
int argc = 1;
|
2016-04-19 02:25:12 +00:00
|
|
|
while (argv[argc] != NULL) {
|
2012-11-19 00:30:30 +00:00
|
|
|
argc++;
|
|
|
|
}
|
|
|
|
return argc;
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// This function works like wperror, but it prints its result into the streams.err string instead
|
|
|
|
/// to stderr. Used by the builtin commands.
|
2016-04-21 06:00:54 +00:00
|
|
|
void builtin_wperror(const wchar_t *s, io_streams_t &streams) {
|
2015-09-21 18:24:49 +00:00
|
|
|
char *err = strerror(errno);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (s != NULL) {
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.err.append(s);
|
|
|
|
streams.err.append(L": ");
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
if (err != NULL) {
|
2012-12-19 21:31:06 +00:00
|
|
|
const wcstring werr = str2wcstring(err);
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.err.append(werr);
|
|
|
|
streams.err.push_back(L'\n');
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Count the number of times the specified character occurs in the specified string.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int count_char(const wchar_t *str, wchar_t c) {
|
2012-11-19 00:30:30 +00:00
|
|
|
int res = 0;
|
2016-04-19 02:25:12 +00:00
|
|
|
for (; *str; str++) {
|
|
|
|
res += (*str == c);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
return res;
|
2006-05-26 11:24:02 +00:00
|
|
|
}
|
2005-09-20 13:26:39 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
wcstring builtin_help_get(parser_t &parser, io_streams_t &streams, const wchar_t *name) {
|
|
|
|
// This won't ever work if no_exec is set.
|
|
|
|
if (no_exec) return wcstring();
|
2013-05-05 09:33:17 +00:00
|
|
|
|
2011-12-31 23:57:30 +00:00
|
|
|
wcstring_list_t lst;
|
2012-02-01 04:22:25 +00:00
|
|
|
wcstring out;
|
|
|
|
const wcstring name_esc = escape_string(name, 1);
|
2014-10-10 07:11:23 +00:00
|
|
|
wcstring cmd = format_string(L"__fish_print_help %ls", name_esc.c_str());
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!streams.out_is_redirected && isatty(STDOUT_FILENO)) {
|
2014-10-10 07:11:23 +00:00
|
|
|
// since we're using a subshell, __fish_print_help can't tell we're in
|
|
|
|
// a terminal. Tell it ourselves.
|
|
|
|
int cols = common_get_width();
|
|
|
|
cmd = format_string(L"__fish_print_help --tty-width %d %ls", cols, name_esc.c_str());
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
if (exec_subshell(cmd, lst, false /* don't apply exit status */) >= 0) {
|
|
|
|
for (size_t i = 0; i < lst.size(); i++) {
|
2012-02-01 04:22:25 +00:00
|
|
|
out.append(lst.at(i));
|
|
|
|
out.push_back(L'\n');
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return out;
|
2006-11-17 16:24:38 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Print help for the specified builtin. If \c b is sb_err, also print the line information.
|
|
|
|
///
|
|
|
|
/// If \c b is the buffer representing standard error, and the help message is about to be printed
|
|
|
|
/// to an interactive screen, it may be shortened to fit the screen.
|
2016-04-19 02:25:12 +00:00
|
|
|
void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t *cmd,
|
|
|
|
output_stream_t &b) {
|
2016-05-04 04:31:32 +00:00
|
|
|
bool is_stderr = &b == &streams.err;
|
2016-04-19 02:25:12 +00:00
|
|
|
if (is_stderr) {
|
2015-09-21 18:24:49 +00:00
|
|
|
b.append(parser.current_line());
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
|
2015-09-21 18:24:49 +00:00
|
|
|
const wcstring h = builtin_help_get(parser, streams, cmd);
|
2016-04-19 02:25:12 +00:00
|
|
|
|
|
|
|
if (!h.size()) return;
|
|
|
|
|
2012-11-19 00:30:30 +00:00
|
|
|
wchar_t *str = wcsdup(h.c_str());
|
2016-04-19 02:25:12 +00:00
|
|
|
if (str) {
|
2014-01-12 21:33:35 +00:00
|
|
|
bool is_short = false;
|
2016-04-19 02:25:12 +00:00
|
|
|
if (is_stderr) {
|
|
|
|
// Interactive mode help to screen - only print synopsis if the rest won't fit.
|
2012-11-19 00:30:30 +00:00
|
|
|
int screen_height, lines;
|
2016-04-19 02:25:12 +00:00
|
|
|
|
2012-11-19 00:30:30 +00:00
|
|
|
screen_height = common_get_height();
|
|
|
|
lines = count_char(str, L'\n');
|
2016-05-15 03:35:54 +00:00
|
|
|
if (!shell_is_interactive() || (lines > 2 * screen_height / 3)) {
|
2012-11-19 00:30:30 +00:00
|
|
|
wchar_t *pos;
|
2016-04-19 02:25:12 +00:00
|
|
|
int cut = 0;
|
2012-11-19 00:30:30 +00:00
|
|
|
int i;
|
2016-04-19 02:25:12 +00:00
|
|
|
|
2014-01-12 21:33:35 +00:00
|
|
|
is_short = true;
|
2016-04-19 02:25:12 +00:00
|
|
|
|
|
|
|
// First move down 4 lines.
|
2012-11-19 00:30:30 +00:00
|
|
|
pos = str;
|
2016-04-19 02:25:12 +00:00
|
|
|
for (i = 0; (i < 4) && pos && *pos; i++) {
|
|
|
|
pos = wcschr(pos + 1, L'\n');
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
|
|
|
|
if (pos && *pos) {
|
|
|
|
// Then find the next empty line.
|
|
|
|
for (; *pos; pos++) {
|
|
|
|
if (*pos == L'\n') {
|
2012-11-19 00:30:30 +00:00
|
|
|
wchar_t *pos2;
|
|
|
|
int is_empty = 1;
|
2016-04-19 02:25:12 +00:00
|
|
|
|
|
|
|
for (pos2 = pos + 1; *pos2; pos2++) {
|
|
|
|
if (*pos2 == L'\n') break;
|
|
|
|
|
|
|
|
if (*pos2 != L'\t' && *pos2 != L' ') {
|
2012-11-19 00:30:30 +00:00
|
|
|
is_empty = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
if (is_empty) {
|
|
|
|
// And cut it.
|
|
|
|
*(pos2 + 1) = L'\0';
|
2012-11-19 00:30:30 +00:00
|
|
|
cut = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
|
|
|
|
// We did not find a good place to cut message to shorten it - so we make sure we
|
|
|
|
// don't print anything.
|
|
|
|
if (!cut) {
|
2012-11-19 00:30:30 +00:00
|
|
|
*str = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
|
2012-02-22 18:51:06 +00:00
|
|
|
b.append(str);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (is_short) {
|
2015-09-21 18:24:49 +00:00
|
|
|
b.append_format(_(L"%ls: Type 'help %ls' for related documentation\n\n"), cmd, cmd);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
|
2012-11-19 00:30:30 +00:00
|
|
|
free(str);
|
|
|
|
}
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|
2006-06-20 21:20:16 +00:00
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Perform error reporting for encounter with unknown option.
|
2016-04-21 06:00:54 +00:00
|
|
|
void builtin_unknown_option(parser_t &parser, io_streams_t &streams, const wchar_t *cmd,
|
|
|
|
const wchar_t *opt) {
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.err.append_format(BUILTIN_ERR_UNKNOWN, cmd, opt);
|
|
|
|
builtin_print_help(parser, streams, cmd, streams.err);
|
2007-01-21 14:55:27 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Perform error reporting for encounter with missing argument.
|
2016-04-21 06:00:54 +00:00
|
|
|
void builtin_missing_argument(parser_t &parser, io_streams_t &streams, const wchar_t *cmd,
|
|
|
|
const wchar_t *opt) {
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.err.append_format(BUILTIN_ERR_MISSING, cmd, opt);
|
|
|
|
builtin_print_help(parser, streams, cmd, streams.err);
|
2007-08-01 19:44:50 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 02:49:15 +00:00
|
|
|
// Here follows the definition of all builtin commands. The function names are all of the form
|
2016-04-19 02:25:12 +00:00
|
|
|
// builtin_NAME where NAME is the name of the builtin. so the function name for the builtin 'fg' is
|
|
|
|
// 'builtin_fg'.
|
|
|
|
//
|
|
|
|
// A few builtins, including 'while', 'command' and 'builtin' are not defined here as they are
|
|
|
|
// handled directly by the parser. (They are not parsed as commands, instead they only alter the
|
|
|
|
// parser state)
|
|
|
|
//
|
|
|
|
// The builtins 'break' and 'continue' are so closely related that they share the same
|
|
|
|
// implementation, namely 'builtin_break_continue.
|
|
|
|
//
|
|
|
|
// Several other builtins, including jobs, ulimit and set are so big that they have been given their
|
2016-04-20 02:49:15 +00:00
|
|
|
// own module. These files are all named 'builtin_NAME.cpp', where NAME is the name of the builtin.
|
2015-09-12 20:36:22 +00:00
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// List a single key binding.
|
|
|
|
/// Returns false if no binding with that sequence and mode exists.
|
2016-04-19 02:25:12 +00:00
|
|
|
static bool builtin_bind_list_one(const wcstring &seq, const wcstring &bind_mode,
|
|
|
|
io_streams_t &streams) {
|
2014-09-23 04:04:06 +00:00
|
|
|
std::vector<wcstring> ecmds;
|
|
|
|
wcstring sets_mode;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!input_mapping_get(seq, bind_mode, &ecmds, &sets_mode)) {
|
2014-09-23 04:04:06 +00:00
|
|
|
return false;
|
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.append(L"bind");
|
2013-12-31 00:52:41 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Append the mode flags if applicable.
|
|
|
|
if (bind_mode != DEFAULT_BIND_MODE) {
|
2014-09-23 04:04:06 +00:00
|
|
|
const wcstring emode = escape_string(bind_mode, ESCAPE_ALL);
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.append(L" -M ");
|
|
|
|
streams.out.append(emode);
|
2014-09-23 04:04:06 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
if (sets_mode != bind_mode) {
|
2014-09-23 04:04:06 +00:00
|
|
|
const wcstring esets_mode = escape_string(sets_mode, ESCAPE_ALL);
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.append(L" -m ");
|
|
|
|
streams.out.append(esets_mode);
|
2014-09-23 04:04:06 +00:00
|
|
|
}
|
2014-03-31 17:01:39 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Append the name.
|
2014-09-23 04:04:06 +00:00
|
|
|
wcstring tname;
|
2016-04-19 02:25:12 +00:00
|
|
|
if (input_terminfo_get_name(seq, &tname)) {
|
|
|
|
// Note that we show -k here because we have an input key name.
|
|
|
|
streams.out.append_format(L" -k %ls", tname.c_str());
|
|
|
|
} else {
|
|
|
|
// No key name, so no -k; we show the escape sequence directly.
|
2014-09-23 04:04:06 +00:00
|
|
|
const wcstring eseq = escape_string(seq, ESCAPE_ALL);
|
2016-04-19 02:25:12 +00:00
|
|
|
streams.out.append_format(L" %ls", eseq.c_str());
|
2014-09-23 04:04:06 +00:00
|
|
|
}
|
2014-09-22 19:37:11 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Now show the list of commands.
|
|
|
|
for (size_t i = 0; i < ecmds.size(); i++) {
|
2014-09-23 04:04:06 +00:00
|
|
|
const wcstring &ecmd = ecmds.at(i);
|
|
|
|
const wcstring escaped_ecmd = escape_string(ecmd, ESCAPE_ALL);
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.push_back(' ');
|
|
|
|
streams.out.append(escaped_ecmd);
|
2014-09-23 04:04:06 +00:00
|
|
|
}
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.push_back(L'\n');
|
2014-09-22 19:37:11 +00:00
|
|
|
|
2014-09-23 04:04:06 +00:00
|
|
|
return true;
|
|
|
|
}
|
2014-09-22 19:37:11 +00:00
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// List all current key bindings.
|
2016-04-19 02:25:12 +00:00
|
|
|
static void builtin_bind_list(const wchar_t *bind_mode, io_streams_t &streams) {
|
2014-09-23 04:04:06 +00:00
|
|
|
const std::vector<input_mapping_name_t> lst = input_mapping_get_names();
|
2014-09-22 19:37:11 +00:00
|
|
|
|
2014-09-23 04:04:06 +00:00
|
|
|
for (std::vector<input_mapping_name_t>::const_iterator it = lst.begin(), end = lst.end();
|
2016-04-19 02:25:12 +00:00
|
|
|
it != end; ++it) {
|
|
|
|
if (bind_mode != NULL && bind_mode != it->mode) {
|
2014-09-23 04:04:06 +00:00
|
|
|
continue;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2014-09-23 04:04:06 +00:00
|
|
|
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_bind_list_one(it->seq, it->mode, streams);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2007-09-25 16:14:47 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Print terminfo key binding names to string buffer used for standard output.
|
|
|
|
///
|
|
|
|
/// \param all if set, all terminfo key binding names will be printed. If not set, only ones that
|
|
|
|
/// are defined for this terminal are printed.
|
2016-04-19 02:25:12 +00:00
|
|
|
static void builtin_bind_key_names(int all, io_streams_t &streams) {
|
2012-11-19 00:30:30 +00:00
|
|
|
const wcstring_list_t names = input_terminfo_get_names(!all);
|
2016-04-19 02:25:12 +00:00
|
|
|
for (size_t i = 0; i < names.size(); i++) {
|
2012-02-08 01:06:45 +00:00
|
|
|
const wcstring &name = names.at(i);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
streams.out.append_format(L"%ls\n", name.c_str());
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2007-09-25 16:14:47 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Print all the special key binding functions to string buffer used for standard output.
|
2016-04-19 02:25:12 +00:00
|
|
|
static void builtin_bind_function_names(io_streams_t &streams) {
|
2012-01-24 04:48:47 +00:00
|
|
|
wcstring_list_t names = input_function_get_names();
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
for (size_t i = 0; i < names.size(); i++) {
|
2012-11-19 00:30:30 +00:00
|
|
|
const wchar_t *seq = names.at(i).c_str();
|
2016-04-19 02:25:12 +00:00
|
|
|
streams.out.append_format(L"%ls\n", seq);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2007-09-25 16:14:47 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Wraps input_terminfo_get_sequence(), appending the correct error messages as needed.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int get_terminfo_sequence(const wchar_t *seq, wcstring *out_seq, io_streams_t &streams) {
|
|
|
|
if (input_terminfo_get_sequence(seq, out_seq)) {
|
2014-09-23 02:21:08 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2014-09-23 04:30:44 +00:00
|
|
|
wcstring eseq = escape_string(seq, 0);
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (errno) {
|
|
|
|
case ENOENT: {
|
|
|
|
streams.err.append_format(_(L"%ls: No key with name '%ls' found\n"), L"bind",
|
|
|
|
eseq.c_str());
|
2014-09-23 02:21:08 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case EILSEQ: {
|
|
|
|
streams.err.append_format(_(L"%ls: Key with name '%ls' does not have any mapping\n"),
|
|
|
|
L"bind", eseq.c_str());
|
2014-09-23 02:21:08 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
default: {
|
|
|
|
streams.err.append_format(_(L"%ls: Unknown error trying to bind to key named '%ls'\n"),
|
|
|
|
L"bind", eseq.c_str());
|
2014-09-23 02:21:08 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Add specified key binding.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_bind_add(const wchar_t *seq, const wchar_t *const *cmds, size_t cmds_len,
|
2015-09-21 18:24:49 +00:00
|
|
|
const wchar_t *mode, const wchar_t *sets_mode, int terminfo,
|
2016-04-19 02:25:12 +00:00
|
|
|
io_streams_t &streams) {
|
|
|
|
if (terminfo) {
|
2013-04-15 20:07:17 +00:00
|
|
|
wcstring seq2;
|
2016-04-19 02:25:12 +00:00
|
|
|
if (get_terminfo_sequence(seq, &seq2, streams)) {
|
2014-01-15 10:39:19 +00:00
|
|
|
input_mapping_add(seq2.c_str(), cmds, cmds_len, mode, sets_mode);
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2012-11-19 00:30:30 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2014-01-15 10:39:19 +00:00
|
|
|
input_mapping_add(seq, cmds, cmds_len, mode, sets_mode);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2007-09-25 16:14:47 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Erase specified key bindings
|
|
|
|
///
|
|
|
|
/// \param seq an array of all key bindings to erase
|
|
|
|
/// \param all if specified, _all_ key bindings will be erased
|
|
|
|
/// \param mode if specified, only bindings from that mode will be erased. If not given and \c all
|
|
|
|
/// is \c false, \c DEFAULT_BIND_MODE will be used.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_bind_erase(wchar_t **seq, int all, const wchar_t *mode, int use_terminfo,
|
|
|
|
io_streams_t &streams) {
|
|
|
|
if (all) {
|
2014-09-23 04:04:06 +00:00
|
|
|
const std::vector<input_mapping_name_t> lst = input_mapping_get_names();
|
|
|
|
for (std::vector<input_mapping_name_t>::const_iterator it = lst.begin(), end = lst.end();
|
2016-04-19 02:25:12 +00:00
|
|
|
it != end; ++it) {
|
|
|
|
if (mode == NULL || mode == it->mode) {
|
2014-09-23 04:04:06 +00:00
|
|
|
input_mapping_erase(it->seq, it->mode);
|
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2006-01-30 19:53:10 +00:00
|
|
|
|
2014-09-23 02:21:08 +00:00
|
|
|
return 0;
|
2016-05-04 22:19:47 +00:00
|
|
|
}
|
2014-09-23 02:21:08 +00:00
|
|
|
|
2016-05-04 22:19:47 +00:00
|
|
|
int res = 0;
|
|
|
|
if (mode == NULL) mode = DEFAULT_BIND_MODE;
|
2014-09-23 04:04:06 +00:00
|
|
|
|
2016-05-04 22:19:47 +00:00
|
|
|
while (*seq) {
|
|
|
|
if (use_terminfo) {
|
|
|
|
wcstring seq2;
|
|
|
|
if (get_terminfo_sequence(*seq++, &seq2, streams)) {
|
|
|
|
input_mapping_erase(seq2, mode);
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2016-05-04 22:19:47 +00:00
|
|
|
res = 1;
|
2014-09-23 02:21:08 +00:00
|
|
|
}
|
2016-05-04 22:19:47 +00:00
|
|
|
} else {
|
|
|
|
input_mapping_erase(*seq++, mode);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
2016-05-04 22:19:47 +00:00
|
|
|
|
|
|
|
return res;
|
2005-12-11 22:21:01 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// The bind builtin, used for setting character sequences.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_bind(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2015-07-26 01:16:00 +00:00
|
|
|
wgetopter_t w;
|
2016-04-19 02:25:12 +00:00
|
|
|
enum { BIND_INSERT, BIND_ERASE, BIND_KEY_NAMES, BIND_FUNCTION_NAMES };
|
|
|
|
int argc = builtin_count_args(argv);
|
2012-11-19 00:30:30 +00:00
|
|
|
int mode = BIND_INSERT;
|
|
|
|
int res = STATUS_BUILTIN_OK;
|
|
|
|
int all = 0;
|
2013-12-31 00:52:41 +00:00
|
|
|
const wchar_t *bind_mode = DEFAULT_BIND_MODE;
|
2013-12-31 13:53:29 +00:00
|
|
|
bool bind_mode_given = false;
|
2014-01-15 11:04:52 +00:00
|
|
|
const wchar_t *sets_bind_mode = DEFAULT_BIND_MODE;
|
|
|
|
bool sets_bind_mode_given = false;
|
2012-11-19 00:30:30 +00:00
|
|
|
int use_terminfo = 0;
|
2006-01-30 19:53:10 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
w.woptind = 0;
|
2006-01-30 19:53:10 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
static const struct woption long_options[] = {{L"all", no_argument, 0, 'a'},
|
|
|
|
{L"erase", no_argument, 0, 'e'},
|
|
|
|
{L"function-names", no_argument, 0, 'f'},
|
|
|
|
{L"help", no_argument, 0, 'h'},
|
|
|
|
{L"key", no_argument, 0, 'k'},
|
|
|
|
{L"key-names", no_argument, 0, 'K'},
|
|
|
|
{L"mode", required_argument, 0, 'M'},
|
|
|
|
{L"sets-mode", required_argument, 0, 'm'},
|
|
|
|
{0, 0, 0, 0}};
|
2006-01-30 19:53:10 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
while (1) {
|
2012-11-19 00:30:30 +00:00
|
|
|
int opt_index = 0;
|
2016-04-19 02:25:12 +00:00
|
|
|
int opt = w.wgetopt_long(argc, argv, L"aehkKfM:m:", long_options, &opt_index);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (opt == -1) break;
|
2005-09-20 13:26:39 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (opt) {
|
|
|
|
case 0: {
|
|
|
|
if (long_options[opt_index].flag != 0) break;
|
|
|
|
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0],
|
|
|
|
long_options[opt_index].name);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2005-09-20 13:26:39 +00:00
|
|
|
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'a': {
|
2012-11-19 08:31:03 +00:00
|
|
|
all = 1;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'e': {
|
2012-11-19 08:31:03 +00:00
|
|
|
mode = BIND_ERASE;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'h': {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.out);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'k': {
|
2012-11-19 08:31:03 +00:00
|
|
|
use_terminfo = 1;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'K': {
|
2012-11-19 08:31:03 +00:00
|
|
|
mode = BIND_KEY_NAMES;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'f': {
|
2012-11-19 08:31:03 +00:00
|
|
|
mode = BIND_FUNCTION_NAMES;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'M': {
|
2015-07-26 01:16:00 +00:00
|
|
|
bind_mode = w.woptarg;
|
2013-12-31 13:53:29 +00:00
|
|
|
bind_mode_given = true;
|
2013-12-31 00:52:41 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'm': {
|
2015-07-26 01:16:00 +00:00
|
|
|
sets_bind_mode = w.woptarg;
|
2014-01-15 11:04:52 +00:00
|
|
|
sets_bind_mode_given = true;
|
2013-12-31 00:52:41 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case '?': {
|
|
|
|
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2013-12-31 13:53:29 +00:00
|
|
|
}
|
2006-01-30 19:53:10 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// if mode is given, but not new mode, default to new mode to mode.
|
|
|
|
if (bind_mode_given && !sets_bind_mode_given) {
|
2014-03-31 17:01:39 +00:00
|
|
|
sets_bind_mode = bind_mode;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2006-01-30 19:53:10 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (mode) {
|
|
|
|
case BIND_ERASE: {
|
|
|
|
if (builtin_bind_erase(&argv[w.woptind], all, bind_mode_given ? bind_mode : NULL,
|
|
|
|
use_terminfo, streams)) {
|
2014-09-23 02:21:08 +00:00
|
|
|
res = STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case BIND_INSERT: {
|
|
|
|
switch (argc - w.woptind) {
|
|
|
|
case 0: {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_bind_list(bind_mode_given ? bind_mode : NULL, streams);
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case 1: {
|
2014-09-23 04:30:44 +00:00
|
|
|
wcstring seq;
|
2016-04-19 02:25:12 +00:00
|
|
|
if (use_terminfo) {
|
|
|
|
if (!get_terminfo_sequence(argv[w.woptind], &seq, streams)) {
|
2014-09-23 04:30:44 +00:00
|
|
|
res = STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
// get_terminfo_sequence already printed the error.
|
2014-09-23 04:30:44 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2015-07-26 01:16:00 +00:00
|
|
|
seq = argv[w.woptind];
|
2014-09-23 04:30:44 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!builtin_bind_list_one(seq, bind_mode, streams)) {
|
2014-09-23 04:30:44 +00:00
|
|
|
res = STATUS_BUILTIN_ERROR;
|
2015-07-26 01:16:00 +00:00
|
|
|
wcstring eseq = escape_string(argv[w.woptind], 0);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (use_terminfo) {
|
|
|
|
streams.err.append_format(_(L"%ls: No binding found for key '%ls'\n"),
|
|
|
|
argv[0], eseq.c_str());
|
|
|
|
} else {
|
|
|
|
streams.err.append_format(
|
|
|
|
_(L"%ls: No binding found for sequence '%ls'\n"), argv[0],
|
|
|
|
eseq.c_str());
|
2014-09-23 04:30:44 +00:00
|
|
|
}
|
|
|
|
}
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
default: {
|
|
|
|
if (builtin_bind_add(argv[w.woptind], argv + (w.woptind + 1),
|
|
|
|
argc - (w.woptind + 1), bind_mode, sets_bind_mode,
|
|
|
|
use_terminfo, streams)) {
|
2014-09-23 02:21:08 +00:00
|
|
|
res = STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case BIND_KEY_NAMES: {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_bind_key_names(all, streams);
|
2012-11-19 00:30:30 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case BIND_FUNCTION_NAMES: {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_bind_function_names(streams);
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
default: {
|
2012-11-19 08:31:03 +00:00
|
|
|
res = STATUS_BUILTIN_ERROR;
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.err.append_format(_(L"%ls: Invalid state\n"), argv[0]);
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2012-03-26 05:41:22 +00:00
|
|
|
}
|
2005-12-17 12:25:46 +00:00
|
|
|
|
2012-11-19 00:30:30 +00:00
|
|
|
return res;
|
|
|
|
}
|
2005-09-20 13:26:39 +00:00
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// The block builtin, used for temporarily blocking events.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_block(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2015-07-26 01:16:00 +00:00
|
|
|
wgetopter_t w;
|
2016-04-19 02:25:12 +00:00
|
|
|
enum {
|
2012-11-19 00:30:30 +00:00
|
|
|
UNSET,
|
|
|
|
GLOBAL,
|
|
|
|
LOCAL,
|
2016-04-19 02:25:12 +00:00
|
|
|
};
|
2006-01-30 19:53:10 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
int scope = UNSET;
|
2012-11-19 00:30:30 +00:00
|
|
|
int erase = 0;
|
2016-04-19 02:25:12 +00:00
|
|
|
int argc = builtin_count_args(argv);
|
2012-03-07 19:35:22 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
w.woptind = 0;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
static const struct woption long_options[] = {{L"erase", no_argument, 0, 'e'},
|
|
|
|
{L"local", no_argument, 0, 'l'},
|
|
|
|
{L"global", no_argument, 0, 'g'},
|
|
|
|
{L"help", no_argument, 0, 'h'},
|
|
|
|
{0, 0, 0, 0}};
|
2012-10-17 09:56:03 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
while (1) {
|
2012-11-19 00:30:30 +00:00
|
|
|
int opt_index = 0;
|
2016-04-19 02:25:12 +00:00
|
|
|
int opt = w.wgetopt_long(argc, argv, L"elgh", long_options, &opt_index);
|
|
|
|
if (opt == -1) break;
|
|
|
|
|
|
|
|
switch (opt) {
|
|
|
|
case 0: {
|
|
|
|
if (long_options[opt_index].flag != 0) break;
|
|
|
|
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0],
|
|
|
|
long_options[opt_index].name);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'h': {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.out);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'g': {
|
2012-11-19 08:31:03 +00:00
|
|
|
scope = GLOBAL;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'l': {
|
2012-11-19 08:31:03 +00:00
|
|
|
scope = LOCAL;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'e': {
|
2012-11-19 08:31:03 +00:00
|
|
|
erase = 1;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case '?': {
|
|
|
|
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (erase) {
|
|
|
|
if (scope != UNSET) {
|
|
|
|
streams.err.append_format(_(L"%ls: Can not specify scope when removing block\n"),
|
|
|
|
argv[0]);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (parser.global_event_blocks.empty()) {
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.err.append_format(_(L"%ls: No blocks defined\n"), argv[0]);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
parser.global_event_blocks.pop_front();
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2013-12-21 01:41:21 +00:00
|
|
|
size_t block_idx = 0;
|
|
|
|
block_t *block = parser.block_at_index(block_idx);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
|
|
|
event_blockage_t eb = {};
|
2016-04-19 02:25:12 +00:00
|
|
|
eb.typemask = (1 << EVENT_ANY);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (scope) {
|
|
|
|
case LOCAL: {
|
2013-12-21 01:41:21 +00:00
|
|
|
// If this is the outermost block, then we're global
|
2016-04-19 02:25:12 +00:00
|
|
|
if (block_idx + 1 >= parser.block_count()) {
|
2013-12-21 01:41:21 +00:00
|
|
|
block = NULL;
|
|
|
|
}
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case GLOBAL: {
|
|
|
|
block = NULL;
|
2012-11-19 08:31:03 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case UNSET: {
|
|
|
|
while (block != NULL && block->type() != FUNCTION_CALL &&
|
|
|
|
block->type() != FUNCTION_CALL_NO_SHADOW) {
|
2013-12-21 01:41:21 +00:00
|
|
|
// Set it in function scope
|
|
|
|
block = parser.block_at_index(++block_idx);
|
|
|
|
}
|
2012-11-19 08:31:03 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
if (block) {
|
2012-11-19 00:30:30 +00:00
|
|
|
block->event_blocks.push_front(eb);
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2012-11-19 00:30:30 +00:00
|
|
|
parser.global_event_blocks.push_front(eb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return STATUS_BUILTIN_OK;
|
2012-10-17 09:56:03 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// The builtin builtin, used for giving 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, printing help, etc.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_builtin(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|
|
|
int argc = builtin_count_args(argv);
|
|
|
|
int list = 0;
|
2015-07-26 01:16:00 +00:00
|
|
|
wgetopter_t w;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
static const struct woption long_options[] = {
|
|
|
|
{L"names", no_argument, 0, 'n'}, {L"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
while (1) {
|
2012-11-19 00:30:30 +00:00
|
|
|
int opt_index = 0;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
int opt = w.wgetopt_long(argc, argv, L"nh", long_options, &opt_index);
|
|
|
|
if (opt == -1) break;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (opt) {
|
|
|
|
case 0: {
|
|
|
|
if (long_options[opt_index].flag != 0) break;
|
|
|
|
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0],
|
|
|
|
long_options[opt_index].name);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'h': {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.out);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'n': {
|
|
|
|
list = 1;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case '?': {
|
|
|
|
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2012-10-17 09:56:03 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (list) {
|
2012-11-19 00:30:30 +00:00
|
|
|
wcstring_list_t names = builtin_get_names();
|
|
|
|
sort(names.begin(), names.end());
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
for (size_t i = 0; i < names.size(); i++) {
|
2012-11-19 00:30:30 +00:00
|
|
|
const wchar_t *el = names.at(i).c_str();
|
|
|
|
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.append(el);
|
|
|
|
streams.out.append(L"\n");
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2012-10-17 09:56:03 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Implementation of the builtin emit command, used to create events.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_emit(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2015-07-26 01:16:00 +00:00
|
|
|
wgetopter_t w;
|
2016-04-19 02:25:12 +00:00
|
|
|
int argc = builtin_count_args(argv);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
static const struct woption long_options[] = {{L"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
while (1) {
|
2012-11-19 00:30:30 +00:00
|
|
|
int opt_index = 0;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
int opt = w.wgetopt_long(argc, argv, L"h", long_options, &opt_index);
|
|
|
|
if (opt == -1) break;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (opt) {
|
|
|
|
case 0: {
|
|
|
|
if (long_options[opt_index].flag != 0) break;
|
|
|
|
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0],
|
|
|
|
long_options[opt_index].name);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'h': {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.out);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case '?': {
|
|
|
|
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!argv[w.woptind]) {
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.err.append_format(L"%ls: expected event name\n", argv[0]);
|
2012-12-22 20:21:31 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2015-07-26 01:16:00 +00:00
|
|
|
const wchar_t *eventname = argv[w.woptind];
|
|
|
|
wcstring_list_t args(argv + w.woptind + 1, argv + argc);
|
2012-12-20 00:11:55 +00:00
|
|
|
event_fire_generic(eventname, &args);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
|
|
|
return STATUS_BUILTIN_OK;
|
2012-10-17 09:56:03 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Implementation of the builtin 'command'. Actual command running is handled by the parser, this
|
|
|
|
/// just processes the flags.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_command(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2015-07-26 01:16:00 +00:00
|
|
|
wgetopter_t w;
|
2016-04-19 02:25:12 +00:00
|
|
|
int argc = builtin_count_args(argv);
|
|
|
|
int print_path = 0;
|
2014-07-10 01:21:06 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
w.woptind = 0;
|
2014-07-10 01:21:06 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
static const struct woption long_options[] = {
|
|
|
|
{L"search", no_argument, 0, 's'}, {L"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
|
2014-07-10 01:21:06 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
while (1) {
|
2014-07-10 01:21:06 +00:00
|
|
|
int opt_index = 0;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
int opt = w.wgetopt_long(argc, argv, L"svh", long_options, &opt_index);
|
|
|
|
if (opt == -1) break;
|
2014-07-10 01:21:06 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (opt) {
|
|
|
|
case 0: {
|
|
|
|
if (long_options[opt_index].flag != 0) break;
|
|
|
|
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0],
|
|
|
|
long_options[opt_index].name);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2014-07-10 01:21:06 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'h': {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.out);
|
2014-07-10 01:21:06 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2014-07-11 02:16:32 +00:00
|
|
|
case 's':
|
2016-04-19 02:25:12 +00:00
|
|
|
case 'v': {
|
|
|
|
print_path = 1;
|
2014-07-10 01:21:06 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case '?': {
|
|
|
|
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
2014-07-10 01:21:06 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2014-07-10 01:21:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!print_path) {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.out);
|
2014-07-10 01:21:06 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
int found = 0;
|
2014-07-10 01:21:06 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
for (int idx = w.woptind; argv[idx]; ++idx) {
|
2014-07-10 01:21:06 +00:00
|
|
|
const wchar_t *command_name = argv[idx];
|
|
|
|
wcstring path;
|
2016-04-19 02:25:12 +00:00
|
|
|
if (path_get_path(command_name, &path)) {
|
|
|
|
streams.out.append_format(L"%ls\n", path.c_str());
|
2014-07-10 01:21:06 +00:00
|
|
|
++found;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return found ? STATUS_BUILTIN_OK : STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// 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 the parser.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_generic(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2015-07-26 01:16:00 +00:00
|
|
|
wgetopter_t w;
|
2016-04-19 02:25:12 +00:00
|
|
|
int argc = builtin_count_args(argv);
|
2014-01-15 09:40:40 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Hackish - if we have no arguments other than the command, we are a "naked invocation" and we
|
|
|
|
// just print help.
|
|
|
|
if (argc == 1) {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.out);
|
2014-01-12 23:10:59 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
2014-01-15 09:40:40 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
static const struct woption long_options[] = {{L"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
while (1) {
|
2012-11-19 00:30:30 +00:00
|
|
|
int opt_index = 0;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
int opt = w.wgetopt_long(argc, argv, L"h", long_options, &opt_index);
|
|
|
|
if (opt == -1) break;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (opt) {
|
|
|
|
case 0: {
|
|
|
|
if (long_options[opt_index].flag != 0) break;
|
|
|
|
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0],
|
|
|
|
long_options[opt_index].name);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'h': {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.out);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case '?': {
|
|
|
|
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-05-19 23:59:56 +00:00
|
|
|
}
|
2012-03-07 19:35:22 +00:00
|
|
|
}
|
2016-05-08 22:57:56 +00:00
|
|
|
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
2012-10-17 09:56:03 +00:00
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Return a definition of the specified function. Used by the functions builtin.
|
2016-04-19 02:25:12 +00:00
|
|
|
static wcstring functions_def(const wcstring &name) {
|
|
|
|
CHECK(!name.empty(), L"");
|
2015-09-21 18:24:49 +00:00
|
|
|
wcstring out;
|
2012-11-19 00:30:30 +00:00
|
|
|
wcstring desc, def;
|
|
|
|
function_get_desc(name, &desc);
|
|
|
|
function_get_definition(name, &def);
|
|
|
|
event_t search(EVENT_ANY);
|
|
|
|
search.function_name = name;
|
|
|
|
std::vector<event_t *> ev;
|
2012-12-20 10:48:36 +00:00
|
|
|
event_get(search, &ev);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
|
|
|
out.append(L"function ");
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Typically we prefer to specify the function name first, e.g. "function foo --description bar"
|
|
|
|
// But If the function name starts with a -, we'll need to output it after all the options.
|
2012-11-19 00:30:30 +00:00
|
|
|
bool defer_function_name = (name.at(0) == L'-');
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!defer_function_name) {
|
2013-10-09 14:39:24 +00:00
|
|
|
out.append(escape_string(name, true));
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!desc.empty()) {
|
2012-11-19 00:30:30 +00:00
|
|
|
wcstring esc_desc = escape_string(desc, true);
|
|
|
|
out.append(L" --description ");
|
|
|
|
out.append(esc_desc);
|
|
|
|
}
|
|
|
|
|
2016-05-08 22:57:56 +00:00
|
|
|
if (function_get_shadow_builtin(name)) {
|
|
|
|
out.append(L" --shadow-builtin");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!function_get_shadow_scope(name)) {
|
2012-11-19 00:30:30 +00:00
|
|
|
out.append(L" --no-scope-shadowing");
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
for (size_t i = 0; i < ev.size(); i++) {
|
2012-11-19 00:30:30 +00:00
|
|
|
event_t *next = ev.at(i);
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (next->type) {
|
|
|
|
case EVENT_SIGNAL: {
|
2012-11-19 08:31:03 +00:00
|
|
|
append_format(out, L" --on-signal %ls", sig2wcs(next->param1.signal));
|
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case EVENT_VARIABLE: {
|
2012-11-19 08:31:03 +00:00
|
|
|
append_format(out, L" --on-variable %ls", next->str_param1.c_str());
|
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case EVENT_EXIT: {
|
2012-11-19 08:31:03 +00:00
|
|
|
if (next->param1.pid > 0)
|
|
|
|
append_format(out, L" --on-process-exit %d", next->param1.pid);
|
|
|
|
else
|
|
|
|
append_format(out, L" --on-job-exit %d", -next->param1.pid);
|
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case EVENT_JOB_ID: {
|
2012-11-19 08:31:03 +00:00
|
|
|
const job_t *j = job_get(next->param1.job_id);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (j) append_format(out, L" --on-job-exit %d", j->pgid);
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case EVENT_GENERIC: {
|
2012-11-19 08:31:03 +00:00
|
|
|
append_format(out, L" --on-event %ls", next->str_param1.c_str());
|
|
|
|
break;
|
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wcstring_list_t named = function_get_named_arguments(name);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!named.empty()) {
|
2012-11-19 00:30:30 +00:00
|
|
|
append_format(out, L" --argument");
|
2016-04-19 02:25:12 +00:00
|
|
|
for (size_t i = 0; i < named.size(); i++) {
|
2012-11-19 00:30:30 +00:00
|
|
|
append_format(out, L" %ls", named.at(i).c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Output the function name if we deferred it.
|
|
|
|
if (defer_function_name) {
|
2012-11-19 00:30:30 +00:00
|
|
|
out.append(L" -- ");
|
2013-10-09 14:39:24 +00:00
|
|
|
out.append(escape_string(name, true));
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Output any inherited variables as `set -l` lines.
|
|
|
|
std::map<wcstring, env_var_t> inherit_vars = function_get_inherit_vars(name);
|
|
|
|
for (std::map<wcstring, env_var_t>::const_iterator it = inherit_vars.begin(),
|
|
|
|
end = inherit_vars.end();
|
|
|
|
it != end; ++it) {
|
2014-10-02 22:59:24 +00:00
|
|
|
wcstring_list_t lst;
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!it->second.missing()) {
|
2014-10-02 22:59:24 +00:00
|
|
|
tokenize_variable_array(it->second, lst);
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// This forced tab is crummy, but we don't know what indentation style the function uses.
|
2014-10-02 22:59:24 +00:00
|
|
|
append_format(out, L"\n\tset -l %ls", it->first.c_str());
|
2016-04-19 02:25:12 +00:00
|
|
|
for (wcstring_list_t::const_iterator arg_it = lst.begin(), arg_end = lst.end();
|
|
|
|
arg_it != arg_end; ++arg_it) {
|
2014-10-02 22:59:24 +00:00
|
|
|
wcstring earg = escape_string(*arg_it, ESCAPE_ALL);
|
|
|
|
out.push_back(L' ');
|
|
|
|
out.append(earg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// This forced tab is sort of crummy - not all functions start with a tab.
|
2012-11-19 00:30:30 +00:00
|
|
|
append_format(out, L"\n\t%ls", def.c_str());
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Append a newline before the 'end', unless there already is one there.
|
|
|
|
if (!string_suffixes_string(L"\n", def)) {
|
2012-11-19 00:30:30 +00:00
|
|
|
out.push_back(L'\n');
|
|
|
|
}
|
|
|
|
out.append(L"end\n");
|
2015-09-21 18:24:49 +00:00
|
|
|
return out;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// The functions builtin, used for listing and erasing functions.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2015-07-26 01:16:00 +00:00
|
|
|
wgetopter_t w;
|
2012-11-19 00:30:30 +00:00
|
|
|
int i;
|
2016-04-19 02:25:12 +00:00
|
|
|
int erase = 0;
|
|
|
|
wchar_t *desc = 0;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
int argc = builtin_count_args(argv);
|
|
|
|
int list = 0;
|
|
|
|
int show_hidden = 0;
|
2012-11-19 00:30:30 +00:00
|
|
|
int res = STATUS_BUILTIN_OK;
|
|
|
|
int query = 0;
|
|
|
|
int copy = 0;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
static const 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'},
|
|
|
|
{L"help", no_argument, 0, 'h'}, {L"query", no_argument, 0, 'q'},
|
|
|
|
{L"copy", no_argument, 0, 'c'}, {0, 0, 0, 0}};
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
while (1) {
|
2012-11-19 00:30:30 +00:00
|
|
|
int opt_index = 0;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
int opt = w.wgetopt_long(argc, argv, L"ed:nahqc", long_options, &opt_index);
|
|
|
|
if (opt == -1) break;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (opt) {
|
|
|
|
case 0: {
|
|
|
|
if (long_options[opt_index].flag != 0) break;
|
|
|
|
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0],
|
|
|
|
long_options[opt_index].name);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'e': {
|
|
|
|
erase = 1;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'd': {
|
|
|
|
desc = w.woptarg;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'n': {
|
|
|
|
list = 1;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'a': {
|
|
|
|
show_hidden = 1;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'h': {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.out);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'q': {
|
2012-11-19 08:31:03 +00:00
|
|
|
query = 1;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'c': {
|
2012-11-19 08:31:03 +00:00
|
|
|
copy = 1;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case '?': {
|
|
|
|
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Erase, desc, query, copy and list are mutually exclusive.
|
|
|
|
if ((erase + (!!desc) + list + query + copy) > 1) {
|
|
|
|
streams.err.append_format(_(L"%ls: Invalid combination of options\n"), argv[0]);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (erase) {
|
2012-11-19 00:30:30 +00:00
|
|
|
int i;
|
2016-04-19 02:25:12 +00:00
|
|
|
for (i = w.woptind; i < argc; i++) function_remove(argv[i]);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
2016-04-19 02:25:12 +00:00
|
|
|
} else if (desc) {
|
2012-11-19 00:30:30 +00:00
|
|
|
wchar_t *func;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (argc - w.woptind != 1) {
|
|
|
|
streams.err.append_format(_(L"%ls: Expected exactly one function name\n"), argv[0]);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
2015-07-26 01:16:00 +00:00
|
|
|
func = argv[w.woptind];
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!function_exists(func)) {
|
|
|
|
streams.err.append_format(_(L"%ls: Function '%ls' does not exist\n"), argv[0], func);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
function_set_desc(func, desc);
|
|
|
|
|
|
|
|
return STATUS_BUILTIN_OK;
|
2016-04-19 02:25:12 +00:00
|
|
|
} else if (list || (argc == w.woptind)) {
|
2015-09-21 18:24:49 +00:00
|
|
|
int is_screen = !streams.out_is_redirected && isatty(STDOUT_FILENO);
|
2012-11-19 00:30:30 +00:00
|
|
|
size_t i;
|
|
|
|
wcstring_list_t names = function_get_names(show_hidden);
|
|
|
|
std::sort(names.begin(), names.end());
|
2016-04-19 02:25:12 +00:00
|
|
|
if (is_screen) {
|
2012-11-19 00:30:30 +00:00
|
|
|
wcstring buff;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
for (i = 0; i < names.size(); i++) {
|
2012-11-19 00:30:30 +00:00
|
|
|
buff.append(names.at(i));
|
|
|
|
buff.append(L", ");
|
|
|
|
}
|
|
|
|
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.append(reformat_for_screen(buff));
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
|
|
|
for (i = 0; i < names.size(); i++) {
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.append(names.at(i).c_str());
|
|
|
|
streams.out.append(L"\n");
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return STATUS_BUILTIN_OK;
|
2016-04-19 02:25:12 +00:00
|
|
|
} else if (copy) {
|
2012-11-19 00:30:30 +00:00
|
|
|
wcstring current_func;
|
|
|
|
wcstring new_func;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (argc - w.woptind != 2) {
|
|
|
|
streams.err.append_format(_(L"%ls: Expected exactly two names (current function name, "
|
|
|
|
L"and new function name)\n"),
|
|
|
|
argv[0]);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
2015-07-26 01:16:00 +00:00
|
|
|
current_func = argv[w.woptind];
|
2016-04-19 02:25:12 +00:00
|
|
|
new_func = argv[w.woptind + 1];
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!function_exists(current_func)) {
|
|
|
|
streams.err.append_format(_(L"%ls: Function '%ls' does not exist\n"), argv[0],
|
|
|
|
current_func.c_str());
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if ((wcsfuncname(new_func) != 0) || parser_keywords_is_reserved(new_func)) {
|
|
|
|
streams.err.append_format(_(L"%ls: Illegal function name '%ls'\n"), argv[0],
|
|
|
|
new_func.c_str());
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Keep things simple: don't allow existing names to be copy targets.
|
|
|
|
if (function_exists(new_func)) {
|
|
|
|
streams.err.append_format(
|
|
|
|
_(L"%ls: Function '%ls' already exists. Cannot create copy '%ls'\n"), argv[0],
|
|
|
|
new_func.c_str(), current_func.c_str());
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (function_copy(current_func, new_func)) return STATUS_BUILTIN_OK;
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
for (i = w.woptind; i < argc; i++) {
|
2012-11-19 00:30:30 +00:00
|
|
|
if (!function_exists(argv[i]))
|
|
|
|
res++;
|
2016-04-19 02:25:12 +00:00
|
|
|
else {
|
|
|
|
if (!query) {
|
|
|
|
if (i != w.woptind) streams.out.append(L"\n");
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.append(functions_def(argv[i]));
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
static unsigned int builtin_echo_digit(wchar_t wc, unsigned int base) {
|
|
|
|
assert(base == 8 || base == 16); // base must be hex or octal
|
|
|
|
switch (wc) {
|
|
|
|
case L'0': {
|
2012-11-19 08:31:03 +00:00
|
|
|
return 0;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'1': {
|
2012-11-19 08:31:03 +00:00
|
|
|
return 1;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'2': {
|
2012-11-19 08:31:03 +00:00
|
|
|
return 2;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'3': {
|
2012-11-19 08:31:03 +00:00
|
|
|
return 3;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'4': {
|
2012-11-19 08:31:03 +00:00
|
|
|
return 4;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'5': {
|
2012-11-19 08:31:03 +00:00
|
|
|
return 5;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'6': {
|
2012-11-19 08:31:03 +00:00
|
|
|
return 6;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'7': {
|
2012-11-19 08:31:03 +00:00
|
|
|
return 7;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (base == 16) switch (wc) {
|
|
|
|
case L'8': {
|
2012-11-19 08:31:03 +00:00
|
|
|
return 8;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'9': {
|
2012-11-19 08:31:03 +00:00
|
|
|
return 9;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 08:31:03 +00:00
|
|
|
case L'a':
|
2016-04-19 02:25:12 +00:00
|
|
|
case L'A': {
|
2012-11-19 08:31:03 +00:00
|
|
|
return 10;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 08:31:03 +00:00
|
|
|
case L'b':
|
2016-04-19 02:25:12 +00:00
|
|
|
case L'B': {
|
2012-11-19 08:31:03 +00:00
|
|
|
return 11;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 08:31:03 +00:00
|
|
|
case L'c':
|
2016-04-19 02:25:12 +00:00
|
|
|
case L'C': {
|
2012-11-19 08:31:03 +00:00
|
|
|
return 12;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 08:31:03 +00:00
|
|
|
case L'd':
|
2016-04-19 02:25:12 +00:00
|
|
|
case L'D': {
|
2012-11-19 08:31:03 +00:00
|
|
|
return 13;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 08:31:03 +00:00
|
|
|
case L'e':
|
2016-04-19 02:25:12 +00:00
|
|
|
case L'E': {
|
2012-11-19 08:31:03 +00:00
|
|
|
return 14;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 08:31:03 +00:00
|
|
|
case L'f':
|
2016-04-19 02:25:12 +00:00
|
|
|
case L'F': {
|
2012-11-19 08:31:03 +00:00
|
|
|
return 15;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
return UINT_MAX;
|
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Parse a numeric escape sequence in str, returning whether we succeeded. Also return the number
|
|
|
|
/// of characters consumed and the resulting value. Supported escape sequences:
|
|
|
|
///
|
|
|
|
/// \0nnn: octal value, zero to three digits
|
|
|
|
/// \nnn: octal value, one to three digits
|
|
|
|
/// \xhh: hex value, one to two digits
|
2016-04-19 02:25:12 +00:00
|
|
|
static bool builtin_echo_parse_numeric_sequence(const wchar_t *str, size_t *consumed,
|
|
|
|
unsigned char *out_val) {
|
2012-11-19 00:30:30 +00:00
|
|
|
bool success = false;
|
2016-04-19 02:25:12 +00:00
|
|
|
unsigned int start = 0; // the first character of the numeric part of the sequence
|
2012-11-19 00:30:30 +00:00
|
|
|
|
|
|
|
unsigned int base = 0, max_digits = 0;
|
2016-04-19 02:25:12 +00:00
|
|
|
if (builtin_echo_digit(str[0], 8) != UINT_MAX) {
|
2012-11-19 00:30:30 +00:00
|
|
|
// Octal escape
|
|
|
|
base = 8;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// If the first digit is a 0, we allow four digits (including that zero); otherwise, we
|
|
|
|
// allow 3.
|
2012-11-19 00:30:30 +00:00
|
|
|
max_digits = (str[0] == L'0' ? 4 : 3);
|
2016-04-19 02:25:12 +00:00
|
|
|
} else if (str[0] == L'x') {
|
2012-11-19 00:30:30 +00:00
|
|
|
// Hex escape
|
|
|
|
base = 16;
|
|
|
|
max_digits = 2;
|
|
|
|
|
|
|
|
// Skip the x
|
|
|
|
start = 1;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (base != 0) {
|
2012-11-19 00:30:30 +00:00
|
|
|
unsigned int idx;
|
2016-04-19 02:25:12 +00:00
|
|
|
unsigned char val = 0; // resulting character
|
|
|
|
for (idx = start; idx < start + max_digits; idx++) {
|
2012-11-19 00:30:30 +00:00
|
|
|
unsigned int digit = builtin_echo_digit(str[idx], base);
|
|
|
|
if (digit == UINT_MAX) break;
|
|
|
|
val = val * base + digit;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// We succeeded if we consumed at least one digit.
|
|
|
|
if (idx > start) {
|
2012-11-19 00:30:30 +00:00
|
|
|
*consumed = idx;
|
|
|
|
*out_val = val;
|
|
|
|
success = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// The echo builtin.
|
|
|
|
///
|
|
|
|
/// Bash only respects -n if it's the first argument. We'll do the same. We also support a new
|
|
|
|
/// option -s to mean "no spaces"
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_echo(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2012-11-19 00:30:30 +00:00
|
|
|
/* Skip first arg */
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!*argv++) return STATUS_BUILTIN_ERROR;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Process options. Options must come at the beginning - the first non-option kicks us out.
|
2012-11-19 00:30:30 +00:00
|
|
|
bool print_newline = true, print_spaces = true, interpret_special_chars = false;
|
2014-05-16 07:19:07 +00:00
|
|
|
size_t option_idx = 0;
|
2016-04-19 02:25:12 +00:00
|
|
|
for (option_idx = 0; argv[option_idx] != NULL; option_idx++) {
|
2014-05-16 07:19:07 +00:00
|
|
|
const wchar_t *arg = argv[option_idx];
|
|
|
|
assert(arg != NULL);
|
|
|
|
bool arg_is_valid_option = false;
|
2016-04-19 02:25:12 +00:00
|
|
|
if (arg[0] == L'-') {
|
2014-05-16 07:19:07 +00:00
|
|
|
// We have a leading dash. Ensure that every subseqnent character is a valid option.
|
|
|
|
size_t i = 1;
|
2016-04-19 02:25:12 +00:00
|
|
|
while (arg[i] != L'\0' && wcschr(L"nesE", arg[i]) != NULL) {
|
2014-05-16 07:19:07 +00:00
|
|
|
i++;
|
2013-09-19 21:20:05 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
// We must have at least two characters to be a valid option, and have consumed the
|
|
|
|
// whole string.
|
2014-05-16 07:19:07 +00:00
|
|
|
arg_is_valid_option = (i >= 2 && arg[i] == L'\0');
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
|
|
|
|
if (!arg_is_valid_option) {
|
|
|
|
// This argument is not an option, so there are no more options.
|
2012-11-19 00:30:30 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
|
2014-05-16 07:19:07 +00:00
|
|
|
// Ok, we are sure the argument is an option. Parse it.
|
|
|
|
assert(arg_is_valid_option);
|
2016-04-19 02:25:12 +00:00
|
|
|
for (size_t i = 1; arg[i] != L'\0'; i++) {
|
|
|
|
switch (arg[i]) {
|
|
|
|
case L'n': {
|
2014-05-16 07:19:07 +00:00
|
|
|
print_newline = false;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'e': {
|
2014-05-16 07:19:07 +00:00
|
|
|
interpret_special_chars = true;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L's': {
|
2014-05-16 07:19:07 +00:00
|
|
|
print_spaces = false;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'E': {
|
2014-05-16 07:19:07 +00:00
|
|
|
interpret_special_chars = false;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
default: {
|
2014-05-16 07:19:07 +00:00
|
|
|
assert(0 && "Unexpected character in builtin_echo argument");
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2014-05-16 07:19:07 +00:00
|
|
|
}
|
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// The special character \c can be used to indicate no more output.
|
2012-11-19 00:30:30 +00:00
|
|
|
bool continue_output = true;
|
2016-04-19 02:25:12 +00:00
|
|
|
|
2014-05-16 07:19:07 +00:00
|
|
|
/* Skip over the options */
|
2016-04-19 02:25:12 +00:00
|
|
|
const wchar_t *const *args_to_echo = argv + option_idx;
|
|
|
|
for (size_t idx = 0; continue_output && args_to_echo[idx] != NULL; idx++) {
|
|
|
|
if (print_spaces && idx > 0) {
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.push_back(' ');
|
2014-05-16 07:19:07 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2014-05-16 07:19:07 +00:00
|
|
|
const wchar_t *str = args_to_echo[idx];
|
2016-04-19 02:25:12 +00:00
|
|
|
for (size_t j = 0; continue_output && str[j]; j++) {
|
|
|
|
if (!interpret_special_chars || str[j] != L'\\') {
|
|
|
|
// Not an escape.
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.push_back(str[j]);
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
|
|
|
// Most escapes consume one character in addition to the backslash; the numeric
|
|
|
|
// sequences may consume more, while an unrecognized escape sequence consumes none.
|
2012-11-19 00:30:30 +00:00
|
|
|
wchar_t wc;
|
|
|
|
size_t consumed = 1;
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (str[j + 1]) {
|
|
|
|
case L'a': {
|
2012-11-19 08:31:03 +00:00
|
|
|
wc = L'\a';
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'b': {
|
2012-11-19 08:31:03 +00:00
|
|
|
wc = L'\b';
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'e': {
|
2013-07-12 07:40:29 +00:00
|
|
|
wc = L'\x1B';
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'f': {
|
2012-11-19 08:31:03 +00:00
|
|
|
wc = L'\f';
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'n': {
|
2012-11-19 08:31:03 +00:00
|
|
|
wc = L'\n';
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'r': {
|
2012-11-19 08:31:03 +00:00
|
|
|
wc = L'\r';
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L't': {
|
2012-11-19 08:31:03 +00:00
|
|
|
wc = L'\t';
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'v': {
|
2012-11-19 08:31:03 +00:00
|
|
|
wc = L'\v';
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'\\': {
|
2012-11-19 08:31:03 +00:00
|
|
|
wc = L'\\';
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'c': {
|
2012-11-19 08:31:03 +00:00
|
|
|
wc = 0;
|
|
|
|
continue_output = false;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
default: {
|
|
|
|
// Octal and hex escape sequences.
|
2012-11-19 08:31:03 +00:00
|
|
|
unsigned char narrow_val = 0;
|
2016-04-19 02:25:12 +00:00
|
|
|
if (builtin_echo_parse_numeric_sequence(str + j + 1, &consumed,
|
|
|
|
&narrow_val)) {
|
|
|
|
// Here consumed must have been set to something. The narrow_val is a
|
|
|
|
// literal byte that we want to output (#1894).
|
2015-01-15 19:21:07 +00:00
|
|
|
wc = ENCODE_DIRECT_BASE + narrow_val % 256;
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
|
|
|
// Not a recognized escape. We consume only the backslash.
|
2012-11-19 08:31:03 +00:00
|
|
|
wc = L'\\';
|
|
|
|
consumed = 0;
|
|
|
|
}
|
|
|
|
break;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Skip over characters that were part of this escape sequence (but not the
|
|
|
|
// backslash, which will be handled by the loop increment.
|
2012-11-19 00:30:30 +00:00
|
|
|
j += consumed;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (continue_output) {
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.push_back(wc);
|
2014-05-16 07:19:07 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
if (print_newline && continue_output) {
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.push_back('\n');
|
2014-05-16 07:19:07 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// The pwd builtin. We don't respect -P to resolve symbolic links because we
|
|
|
|
/// try to always resolve them.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_pwd(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2016-03-11 02:17:39 +00:00
|
|
|
wcstring res = wgetcwd();
|
2016-04-19 02:25:12 +00:00
|
|
|
if (res.empty()) {
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
2016-05-04 22:19:47 +00:00
|
|
|
streams.out.append(res);
|
|
|
|
streams.out.push_back(L'\n');
|
|
|
|
return STATUS_BUILTIN_OK;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Adds a function to the function set. It calls into function.cpp to perform any heavy lifting.
|
2016-05-08 22:57:56 +00:00
|
|
|
int builtin_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args,
|
|
|
|
const wcstring &contents, int definition_line_offset, wcstring *out_err) {
|
2015-07-26 01:16:00 +00:00
|
|
|
wgetopter_t w;
|
2013-12-27 09:38:43 +00:00
|
|
|
assert(out_err != NULL);
|
2014-01-15 09:40:40 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// wgetopt expects 'function' as the first argument. Make a new wcstring_list with that
|
|
|
|
// property.
|
2013-12-27 11:58:42 +00:00
|
|
|
wcstring_list_t args;
|
|
|
|
args.push_back(L"function");
|
|
|
|
args.insert(args.end(), c_args.begin(), c_args.end());
|
2013-12-27 09:38:43 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Hackish const_cast matches the one in builtin_run.
|
2013-12-27 09:38:43 +00:00
|
|
|
const null_terminated_array_t<wchar_t> argv_array(args);
|
|
|
|
wchar_t **argv = const_cast<wchar_t **>(argv_array.get());
|
2014-01-15 09:40:40 +00:00
|
|
|
|
2012-11-19 00:30:30 +00:00
|
|
|
int argc = builtin_count_args(argv);
|
2016-04-19 02:25:12 +00:00
|
|
|
int res = STATUS_BUILTIN_OK;
|
|
|
|
wchar_t *desc = 0;
|
2012-11-19 00:30:30 +00:00
|
|
|
std::vector<event_t> events;
|
2016-04-19 02:25:12 +00:00
|
|
|
|
2015-05-17 21:17:01 +00:00
|
|
|
bool has_named_arguments = false;
|
|
|
|
wcstring_list_t named_arguments;
|
2014-10-02 22:59:24 +00:00
|
|
|
wcstring_list_t inherit_vars;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-05-08 22:57:56 +00:00
|
|
|
bool shadow_builtin = false;
|
|
|
|
bool shadow_scope = true;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2014-08-16 01:14:36 +00:00
|
|
|
wcstring_list_t wrap_targets;
|
2016-04-19 02:25:12 +00:00
|
|
|
|
|
|
|
// If -a/--argument-names is specified before the function name, then the function name is the
|
|
|
|
// last positional, e.g. `function -a arg1 arg2 name`. If it is specified after the function
|
|
|
|
// name (or not specified at all) then the function name is the first positional. This is the
|
|
|
|
// common case.
|
2015-05-17 21:17:01 +00:00
|
|
|
bool name_is_first_positional = true;
|
|
|
|
wcstring_list_t positionals;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
const struct woption long_options[] = {{L"description", required_argument, 0, 'd'},
|
|
|
|
{L"on-signal", required_argument, 0, 's'},
|
|
|
|
{L"on-job-exit", required_argument, 0, 'j'},
|
|
|
|
{L"on-process-exit", required_argument, 0, 'p'},
|
|
|
|
{L"on-variable", required_argument, 0, 'v'},
|
|
|
|
{L"on-event", required_argument, 0, 'e'},
|
|
|
|
{L"wraps", required_argument, 0, 'w'},
|
|
|
|
{L"help", no_argument, 0, 'h'},
|
|
|
|
{L"argument-names", no_argument, 0, 'a'},
|
2016-05-08 22:57:56 +00:00
|
|
|
{L"shadow-builtin", no_argument, 0, 'B'},
|
2016-04-19 02:25:12 +00:00
|
|
|
{L"no-scope-shadowing", no_argument, 0, 'S'},
|
|
|
|
{L"inherit-variable", required_argument, 0, 'V'},
|
|
|
|
{0, 0, 0, 0}};
|
|
|
|
|
2016-05-08 22:57:56 +00:00
|
|
|
while (1 && !res) {
|
2012-11-19 00:30:30 +00:00
|
|
|
int opt_index = 0;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// The leading - here specifies RETURN_IN_ORDER.
|
2016-05-08 22:57:56 +00:00
|
|
|
int opt = w.wgetopt_long(argc, argv, L"-d:s:j:p:v:e:w:haBSV:", long_options, &opt_index);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (opt == -1) break;
|
|
|
|
switch (opt) {
|
|
|
|
case 0: {
|
|
|
|
if (long_options[opt_index].flag != 0) break;
|
|
|
|
append_format(*out_err, BUILTIN_ERR_UNKNOWN, argv[0], long_options[opt_index].name);
|
2012-11-19 08:31:03 +00:00
|
|
|
res = 1;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'd': {
|
|
|
|
desc = w.woptarg;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 's': {
|
2015-07-26 01:16:00 +00:00
|
|
|
int sig = wcs2sig(w.woptarg);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (sig < 0) {
|
|
|
|
append_format(*out_err, _(L"%ls: Unknown signal '%ls'"), argv[0], w.woptarg);
|
|
|
|
res = 1;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
events.push_back(event_t::signal_event(sig));
|
2012-11-19 00:30:30 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case 'v': {
|
|
|
|
if (wcsvarname(w.woptarg)) {
|
|
|
|
append_format(*out_err, _(L"%ls: Invalid variable name '%ls'"), argv[0],
|
2015-07-26 01:16:00 +00:00
|
|
|
w.woptarg);
|
2016-04-19 02:25:12 +00:00
|
|
|
res = STATUS_BUILTIN_ERROR;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-07-26 01:16:00 +00:00
|
|
|
events.push_back(event_t::variable_event(w.woptarg));
|
2012-11-19 00:30:30 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case 'e': {
|
2015-07-26 01:16:00 +00:00
|
|
|
events.push_back(event_t::generic_event(w.woptarg));
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'j':
|
2016-04-19 02:25:12 +00:00
|
|
|
case 'p': {
|
2012-11-19 08:31:03 +00:00
|
|
|
pid_t pid;
|
|
|
|
wchar_t *end;
|
|
|
|
event_t e(EVENT_ANY);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if ((opt == 'j') && (wcscasecmp(w.woptarg, L"caller") == 0)) {
|
2012-11-19 08:31:03 +00:00
|
|
|
int job_id = -1;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (is_subshell) {
|
2013-12-21 01:41:21 +00:00
|
|
|
size_t block_idx = 0;
|
2014-01-15 09:40:40 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Find the outermost substitution block.
|
|
|
|
for (block_idx = 0;; block_idx++) {
|
2013-12-21 01:41:21 +00:00
|
|
|
const block_t *b = parser.block_at_index(block_idx);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (b == NULL || b->type() == SUBST) break;
|
2012-11-19 08:31:03 +00:00
|
|
|
}
|
2014-01-15 09:40:40 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Go one step beyond that, to get to the caller.
|
2013-12-21 01:41:21 +00:00
|
|
|
const block_t *caller_block = parser.block_at_index(block_idx + 1);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (caller_block != NULL && caller_block->job != NULL) {
|
2013-12-21 01:41:21 +00:00
|
|
|
job_id = caller_block->job->job_id;
|
2012-11-19 08:31:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (job_id == -1) {
|
2013-12-27 09:38:43 +00:00
|
|
|
append_format(*out_err,
|
2015-12-27 23:55:49 +00:00
|
|
|
_(L"%ls: Cannot find calling job for event handler"),
|
2012-11-19 08:31:03 +00:00
|
|
|
argv[0]);
|
2016-04-19 02:25:12 +00:00
|
|
|
res = 1;
|
|
|
|
} else {
|
2012-11-19 08:31:03 +00:00
|
|
|
e.type = EVENT_JOB_ID;
|
|
|
|
e.param1.job_id = job_id;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2012-11-19 08:31:03 +00:00
|
|
|
errno = 0;
|
2015-07-26 01:16:00 +00:00
|
|
|
pid = fish_wcstoi(w.woptarg, &end, 10);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (errno || !end || *end) {
|
|
|
|
append_format(*out_err, _(L"%ls: Invalid process id %ls"), argv[0],
|
2015-07-26 01:16:00 +00:00
|
|
|
w.woptarg);
|
2016-04-19 02:25:12 +00:00
|
|
|
res = 1;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2012-11-19 08:31:03 +00:00
|
|
|
e.type = EVENT_EXIT;
|
2016-04-19 02:25:12 +00:00
|
|
|
e.param1.pid = (opt == 'j' ? -1 : 1) * abs(pid);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!res) {
|
2012-11-19 08:31:03 +00:00
|
|
|
events.push_back(e);
|
|
|
|
}
|
|
|
|
break;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case 'a': {
|
2015-05-17 21:17:01 +00:00
|
|
|
has_named_arguments = true;
|
2016-04-19 02:25:12 +00:00
|
|
|
// The function name is the first positional unless -a comes before all positionals.
|
|
|
|
name_is_first_positional = !positionals.empty();
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2016-05-08 22:57:56 +00:00
|
|
|
case 'B': {
|
|
|
|
shadow_builtin = true;
|
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case 'S': {
|
2016-05-08 22:57:56 +00:00
|
|
|
shadow_scope = false;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'w': {
|
2015-07-26 01:16:00 +00:00
|
|
|
wrap_targets.push_back(w.woptarg);
|
2014-08-16 01:14:36 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'V': {
|
|
|
|
if (wcsvarname(w.woptarg)) {
|
|
|
|
append_format(*out_err, _(L"%ls: Invalid variable name '%ls'"), argv[0],
|
|
|
|
w.woptarg);
|
2014-10-02 22:59:24 +00:00
|
|
|
res = STATUS_BUILTIN_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-07-26 01:16:00 +00:00
|
|
|
inherit_vars.push_back(w.woptarg);
|
2014-10-02 22:59:24 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case 'h': {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.out);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 1: {
|
2015-07-26 01:16:00 +00:00
|
|
|
assert(w.woptarg != NULL);
|
|
|
|
positionals.push_back(w.woptarg);
|
2015-05-17 21:17:01 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case '?': {
|
|
|
|
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
2012-11-19 08:31:03 +00:00
|
|
|
res = 1;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!res) {
|
|
|
|
// Determine the function name, and remove it from the list of positionals.
|
2015-05-17 21:20:18 +00:00
|
|
|
wcstring function_name;
|
2016-04-19 02:25:12 +00:00
|
|
|
bool name_is_missing = positionals.empty();
|
|
|
|
if (!name_is_missing) {
|
|
|
|
if (name_is_first_positional) {
|
2015-05-17 21:17:01 +00:00
|
|
|
function_name = positionals.front();
|
|
|
|
positionals.erase(positionals.begin());
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2015-05-17 21:17:01 +00:00
|
|
|
function_name = positionals.back();
|
|
|
|
positionals.erase(positionals.end() - 1);
|
|
|
|
}
|
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (name_is_missing) {
|
|
|
|
append_format(*out_err, _(L"%ls: Expected function name"), argv[0]);
|
|
|
|
res = 1;
|
|
|
|
} else if (wcsfuncname(function_name)) {
|
|
|
|
append_format(*out_err, _(L"%ls: Illegal function name '%ls'"), argv[0],
|
2015-05-17 21:17:01 +00:00
|
|
|
function_name.c_str());
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
res = 1;
|
|
|
|
} else if (parser_keywords_is_reserved(function_name)) {
|
|
|
|
append_format(
|
|
|
|
*out_err,
|
|
|
|
_(L"%ls: The name '%ls' is reserved,\nand can not be used as a function name"),
|
|
|
|
argv[0], function_name.c_str());
|
|
|
|
|
|
|
|
res = 1;
|
|
|
|
} else if (function_name.empty()) {
|
2015-12-27 23:55:49 +00:00
|
|
|
append_format(*out_err, _(L"%ls: No function name given"), argv[0]);
|
2016-04-19 02:25:12 +00:00
|
|
|
res = 1;
|
|
|
|
} else {
|
|
|
|
if (has_named_arguments) {
|
|
|
|
// All remaining positionals are named arguments.
|
2015-05-17 21:17:01 +00:00
|
|
|
named_arguments.swap(positionals);
|
2016-04-19 02:25:12 +00:00
|
|
|
for (size_t i = 0; i < named_arguments.size(); i++) {
|
|
|
|
if (wcsvarname(named_arguments.at(i))) {
|
|
|
|
append_format(*out_err, _(L"%ls: Invalid variable name '%ls'"), argv[0],
|
2015-05-17 21:17:01 +00:00
|
|
|
named_arguments.at(i).c_str());
|
2012-11-19 00:30:30 +00:00
|
|
|
res = STATUS_BUILTIN_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
} else if (!positionals.empty()) {
|
|
|
|
// +1 because we already got the function name.
|
|
|
|
append_format(*out_err, _(L"%ls: Expected one argument, got %lu"), argv[0],
|
2015-12-27 23:55:49 +00:00
|
|
|
(unsigned long)(positionals.size() + 1));
|
2016-04-19 02:25:12 +00:00
|
|
|
res = 1;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
2015-05-17 21:20:18 +00:00
|
|
|
|
2016-05-08 22:57:56 +00:00
|
|
|
if (!res) {
|
|
|
|
bool function_name_shadows_builtin = false;
|
|
|
|
wcstring_list_t builtin_names = builtin_get_names();
|
|
|
|
for (size_t i = 0; i < builtin_names.size(); i++) {
|
|
|
|
const wchar_t *el = builtin_names.at(i).c_str();
|
|
|
|
if (el == function_name) {
|
|
|
|
function_name_shadows_builtin = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (function_name_shadows_builtin && !shadow_builtin) {
|
|
|
|
append_format(
|
|
|
|
*out_err,
|
|
|
|
_(L"%ls: function name shadows a builtin so you must use '--shadow-builtin'"),
|
|
|
|
argv[0]);
|
|
|
|
res = STATUS_BUILTIN_ERROR;
|
|
|
|
} else if (!function_name_shadows_builtin && shadow_builtin) {
|
|
|
|
append_format(*out_err, _(L"%ls: function name does not shadow a builtin so you "
|
|
|
|
L"must not use '--shadow-builtin'"),
|
|
|
|
argv[0]);
|
|
|
|
res = STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!res) {
|
|
|
|
// Here we actually define the function!
|
2015-12-20 02:03:46 +00:00
|
|
|
function_data_t d;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2015-12-20 02:03:46 +00:00
|
|
|
d.name = function_name;
|
2016-04-19 02:25:12 +00:00
|
|
|
if (desc) d.description = desc;
|
2015-12-20 02:03:46 +00:00
|
|
|
d.events.swap(events);
|
2016-05-08 22:57:56 +00:00
|
|
|
d.shadow_builtin = shadow_builtin;
|
|
|
|
d.shadow_scope = shadow_scope;
|
2015-12-20 02:03:46 +00:00
|
|
|
d.named_arguments.swap(named_arguments);
|
|
|
|
d.inherit_vars.swap(inherit_vars);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
for (size_t i = 0; i < d.events.size(); i++) {
|
2015-12-20 02:03:46 +00:00
|
|
|
event_t &e = d.events.at(i);
|
|
|
|
e.function_name = d.name;
|
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2015-12-20 02:03:46 +00:00
|
|
|
d.definition = contents.c_str();
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2015-12-20 02:03:46 +00:00
|
|
|
function_add(d, parser, definition_line_offset);
|
2014-10-02 22:59:24 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Handle wrap targets.
|
|
|
|
for (size_t w = 0; w < wrap_targets.size(); w++) {
|
2015-12-20 02:03:46 +00:00
|
|
|
complete_add_wrapper(function_name, wrap_targets.at(w));
|
|
|
|
}
|
2014-08-16 01:14:36 +00:00
|
|
|
}
|
2013-12-27 09:38:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
2016-07-11 01:22:49 +00:00
|
|
|
/// The random builtin generates random numbers.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_random(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|
|
|
static int seeded = 0;
|
2012-11-19 00:30:30 +00:00
|
|
|
static struct drand48_data seed_buffer;
|
|
|
|
|
|
|
|
int argc = builtin_count_args(argv);
|
|
|
|
|
2015-07-26 01:16:00 +00:00
|
|
|
wgetopter_t w;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
static const struct woption long_options[] = {{L"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
while (1) {
|
2012-11-19 00:30:30 +00:00
|
|
|
int opt_index = 0;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
int opt = w.wgetopt_long(argc, argv, L"h", long_options, &opt_index);
|
|
|
|
if (opt == -1) break;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (opt) {
|
|
|
|
case 0: {
|
|
|
|
if (long_options[opt_index].flag != 0) break;
|
|
|
|
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0],
|
|
|
|
long_options[opt_index].name);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'h': {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.out);
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case '?': {
|
|
|
|
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (argc - w.woptind) {
|
|
|
|
case 0: {
|
2012-11-19 08:31:03 +00:00
|
|
|
long res;
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!seeded) {
|
|
|
|
seeded = 1;
|
2012-11-19 08:31:03 +00:00
|
|
|
srand48_r(time(0), &seed_buffer);
|
|
|
|
}
|
|
|
|
lrand48_r(&seed_buffer, &res);
|
2016-05-29 05:28:26 +00:00
|
|
|
streams.out.append_format(L"%ld\n", res % 32768);
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case 1: {
|
2012-11-19 08:31:03 +00:00
|
|
|
long foo;
|
2016-04-19 02:25:12 +00:00
|
|
|
wchar_t *end = 0;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
errno = 0;
|
2015-07-26 01:16:00 +00:00
|
|
|
foo = wcstol(argv[w.woptind], &end, 10);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (errno || *end) {
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.err.append_format(_(L"%ls: Seed value '%ls' is not a valid number\n"),
|
2016-04-19 02:25:12 +00:00
|
|
|
argv[0], argv[w.woptind]);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
seeded = 1;
|
2012-11-19 08:31:03 +00:00
|
|
|
srand48_r(foo, &seed_buffer);
|
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
default: {
|
|
|
|
streams.err.append_format(_(L"%ls: Expected zero or one argument, got %d\n"), argv[0],
|
|
|
|
argc - w.woptind);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return STATUS_BUILTIN_OK;
|
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// The read builtin. Reads from stdin and stores the values in environment variables.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2015-07-26 01:16:00 +00:00
|
|
|
wgetopter_t w;
|
2014-09-22 02:18:56 +00:00
|
|
|
wcstring buff;
|
2012-11-19 00:30:30 +00:00
|
|
|
int i, argc = builtin_count_args(argv);
|
|
|
|
int place = ENV_USER;
|
|
|
|
const wchar_t *prompt = DEFAULT_READ_PROMPT;
|
2014-09-22 04:09:21 +00:00
|
|
|
const wchar_t *right_prompt = L"";
|
2012-11-19 00:30:30 +00:00
|
|
|
const wchar_t *commandline = L"";
|
2016-04-19 02:25:12 +00:00
|
|
|
int exit_res = STATUS_BUILTIN_OK;
|
2012-11-19 00:30:30 +00:00
|
|
|
const wchar_t *mode_name = READ_MODE_NAME;
|
2016-04-19 02:25:12 +00:00
|
|
|
int nchars = 0;
|
2014-08-19 12:28:08 +00:00
|
|
|
wchar_t *end;
|
2012-11-19 00:30:30 +00:00
|
|
|
int shell = 0;
|
2014-07-14 05:36:26 +00:00
|
|
|
int array = 0;
|
2014-09-22 02:18:56 +00:00
|
|
|
bool split_null = false;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
while (1) {
|
|
|
|
static const struct woption long_options[] = {{L"export", no_argument, 0, 'x'},
|
|
|
|
{L"global", no_argument, 0, 'g'},
|
|
|
|
{L"local", no_argument, 0, 'l'},
|
|
|
|
{L"universal", no_argument, 0, 'U'},
|
|
|
|
{L"unexport", no_argument, 0, 'u'},
|
|
|
|
{L"prompt", required_argument, 0, 'p'},
|
|
|
|
{L"right-prompt", required_argument, 0, 'R'},
|
|
|
|
{L"command", required_argument, 0, 'c'},
|
|
|
|
{L"mode-name", required_argument, 0, 'm'},
|
|
|
|
{L"nchars", required_argument, 0, 'n'},
|
|
|
|
{L"shell", no_argument, 0, 's'},
|
|
|
|
{L"array", no_argument, 0, 'a'},
|
|
|
|
{L"null", no_argument, 0, 'z'},
|
|
|
|
{L"help", no_argument, 0, 'h'},
|
|
|
|
{0, 0, 0, 0}};
|
2012-11-19 00:30:30 +00:00
|
|
|
|
|
|
|
int opt_index = 0;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
int opt = w.wgetopt_long(argc, argv, L"xglUup:R:c:hm:n:saz", long_options, &opt_index);
|
|
|
|
if (opt == -1) break;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (opt) {
|
|
|
|
case 0: {
|
|
|
|
if (long_options[opt_index].flag != 0) break;
|
|
|
|
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0],
|
|
|
|
long_options[opt_index].name);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'x': {
|
2012-11-19 08:31:03 +00:00
|
|
|
place |= ENV_EXPORT;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'g': {
|
2012-11-19 08:31:03 +00:00
|
|
|
place |= ENV_GLOBAL;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'l': {
|
2012-11-19 08:31:03 +00:00
|
|
|
place |= ENV_LOCAL;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'U': {
|
2012-11-19 08:31:03 +00:00
|
|
|
place |= ENV_UNIVERSAL;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'u': {
|
2012-11-19 08:31:03 +00:00
|
|
|
place |= ENV_UNEXPORT;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'p': {
|
2015-07-26 01:16:00 +00:00
|
|
|
prompt = w.woptarg;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'R': {
|
2015-07-26 01:16:00 +00:00
|
|
|
right_prompt = w.woptarg;
|
2014-09-22 04:09:21 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'c': {
|
2015-07-26 01:16:00 +00:00
|
|
|
commandline = w.woptarg;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'm': {
|
2015-07-26 01:16:00 +00:00
|
|
|
mode_name = w.woptarg;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'n': {
|
2014-08-19 12:28:08 +00:00
|
|
|
errno = 0;
|
2015-07-26 01:16:00 +00:00
|
|
|
nchars = fish_wcstoi(w.woptarg, &end, 10);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (errno || *end != 0) {
|
|
|
|
switch (errno) {
|
2016-05-03 23:23:30 +00:00
|
|
|
case ERANGE: {
|
2016-04-19 02:25:12 +00:00
|
|
|
streams.err.append_format(_(L"%ls: Argument '%ls' is out of range\n"),
|
|
|
|
argv[0], w.woptarg);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2014-08-19 12:28:08 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-05-03 23:23:30 +00:00
|
|
|
}
|
|
|
|
default: {
|
2016-04-19 02:25:12 +00:00
|
|
|
streams.err.append_format(
|
|
|
|
_(L"%ls: Argument '%ls' must be an integer\n"), argv[0], w.woptarg);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2014-08-19 12:28:08 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-05-03 23:23:30 +00:00
|
|
|
}
|
2014-08-19 12:28:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 's': {
|
2012-11-19 08:31:03 +00:00
|
|
|
shell = 1;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'a': {
|
2014-07-14 05:36:26 +00:00
|
|
|
array = 1;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'z': {
|
2014-09-22 02:18:56 +00:00
|
|
|
split_null = true;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'h': {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.out);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case L'?': {
|
|
|
|
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if ((place & ENV_UNEXPORT) && (place & ENV_EXPORT)) {
|
|
|
|
streams.err.append_format(BUILTIN_ERR_EXPUNEXP, argv[0]);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if ((place & ENV_LOCAL ? 1 : 0) + (place & ENV_GLOBAL ? 1 : 0) +
|
|
|
|
(place & ENV_UNIVERSAL ? 1 : 0) >
|
|
|
|
1) {
|
|
|
|
streams.err.append_format(BUILTIN_ERR_GLOCAL, argv[0]);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (array && w.woptind + 1 != argc) {
|
|
|
|
streams.err.append_format(_(L"%ls: --array option requires a single variable name.\n"),
|
|
|
|
argv[0]);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2014-07-14 05:36:26 +00:00
|
|
|
|
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Verify all variable names.
|
|
|
|
for (i = w.woptind; i < argc; i++) {
|
2012-11-19 00:30:30 +00:00
|
|
|
wchar_t *src;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!wcslen(argv[i])) {
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.err.append_format(BUILTIN_ERR_VARNAME_ZERO, argv[0]);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
for (src = argv[i]; *src; src++) {
|
|
|
|
if ((!iswalnum(*src)) && (*src != L'_')) {
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.err.append_format(BUILTIN_ERR_VARCHAR, argv[0], *src);
|
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// The call to reader_readline may change woptind, so we save it away here.
|
|
|
|
i = w.woptind;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Check if we should read interactively using \c reader_readline().
|
|
|
|
if (isatty(0) && streams.stdin_fd == STDIN_FILENO && !split_null) {
|
2012-11-19 00:30:30 +00:00
|
|
|
const wchar_t *line;
|
|
|
|
|
|
|
|
reader_push(mode_name);
|
|
|
|
reader_set_left_prompt(prompt);
|
2014-09-22 04:09:21 +00:00
|
|
|
reader_set_right_prompt(right_prompt);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (shell) {
|
2012-11-19 00:30:30 +00:00
|
|
|
reader_set_complete_function(&complete);
|
|
|
|
reader_set_highlight_function(&highlight_shell);
|
|
|
|
reader_set_test_function(&reader_shell_test);
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
// No autosuggestions or abbreviations in builtin_read.
|
2012-11-19 00:30:30 +00:00
|
|
|
reader_set_allow_autosuggesting(false);
|
2013-07-17 07:38:04 +00:00
|
|
|
reader_set_expand_abbreviations(false);
|
2013-01-22 10:25:17 +00:00
|
|
|
reader_set_exit_on_interrupt(true);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
|
|
|
reader_set_buffer(commandline, wcslen(commandline));
|
|
|
|
proc_push_interactive(1);
|
|
|
|
|
|
|
|
event_fire_generic(L"fish_prompt");
|
2014-08-19 12:28:08 +00:00
|
|
|
line = reader_readline(nchars);
|
2012-11-19 00:30:30 +00:00
|
|
|
proc_pop_interactive();
|
2016-04-19 02:25:12 +00:00
|
|
|
if (line) {
|
|
|
|
if (0 < nchars && nchars < wcslen(line)) {
|
|
|
|
// Line may be longer than nchars if a keybinding used `commandline -i`
|
2014-08-19 12:28:08 +00:00
|
|
|
// note: we're deliberately throwing away the tail of the commandline.
|
|
|
|
// It shouldn't be unread because it was produced with `commandline -i`,
|
|
|
|
// not typed.
|
2014-09-22 02:18:56 +00:00
|
|
|
buff = wcstring(line, nchars);
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2014-09-22 02:18:56 +00:00
|
|
|
buff = wcstring(line);
|
2014-08-19 12:28:08 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2012-11-19 00:30:30 +00:00
|
|
|
exit_res = STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
reader_pop();
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
|
|
|
int eof = 0;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2014-09-22 02:18:56 +00:00
|
|
|
buff.clear();
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
while (1) {
|
2016-03-11 02:17:39 +00:00
|
|
|
int finished = 0;
|
|
|
|
wchar_t res = 0;
|
2014-09-22 03:47:03 +00:00
|
|
|
mbstate_t state = {};
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
while (!finished) {
|
2012-11-19 00:30:30 +00:00
|
|
|
char b;
|
2016-04-19 02:25:12 +00:00
|
|
|
if (read_blocked(streams.stdin_fd, &b, 1) <= 0) {
|
|
|
|
eof = 1;
|
2012-11-19 00:30:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (MB_CUR_MAX == 1) // single-byte locale
|
2012-11-19 00:30:30 +00:00
|
|
|
{
|
2016-03-11 02:17:39 +00:00
|
|
|
res = (unsigned char)b;
|
|
|
|
finished = 1;
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2016-03-11 02:17:39 +00:00
|
|
|
size_t sz = mbrtowc(&res, &b, 1, &state);
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (sz) {
|
|
|
|
case (size_t)-1: {
|
2016-03-11 02:17:39 +00:00
|
|
|
memset(&state, 0, sizeof(state));
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case (size_t)-2: {
|
2016-03-11 02:17:39 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
default: {
|
2016-03-11 02:17:39 +00:00
|
|
|
finished = 1;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2016-03-11 02:17:39 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (eof) break;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!split_null && res == L'\n') break;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (split_null && res == L'\0') break;
|
2014-09-22 02:18:56 +00:00
|
|
|
|
|
|
|
buff.push_back(res);
|
2014-08-19 12:28:08 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (0 < nchars && (size_t)nchars <= buff.size()) {
|
2014-08-19 12:28:08 +00:00
|
|
|
break;
|
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (buff.empty() && eof) {
|
2012-11-19 00:30:30 +00:00
|
|
|
exit_res = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (i != argc && !exit_res) {
|
2012-11-19 00:30:30 +00:00
|
|
|
env_var_t ifs = env_get_string(L"IFS");
|
2016-04-19 02:25:12 +00:00
|
|
|
if (ifs.missing_or_empty()) {
|
|
|
|
// Every character is a separate token.
|
2014-09-22 02:18:56 +00:00
|
|
|
size_t bufflen = buff.size();
|
2016-04-19 02:25:12 +00:00
|
|
|
if (array) {
|
|
|
|
if (bufflen > 0) {
|
|
|
|
wcstring chars(bufflen + (bufflen - 1), ARRAY_SEP);
|
2014-09-22 02:18:56 +00:00
|
|
|
wcstring::iterator out = chars.begin();
|
2016-04-19 02:25:12 +00:00
|
|
|
for (wcstring::const_iterator it = buff.begin(), end = buff.end(); it != end;
|
|
|
|
++it) {
|
2014-09-22 02:18:56 +00:00
|
|
|
*out = *it;
|
|
|
|
out += 2;
|
2014-07-14 05:36:26 +00:00
|
|
|
}
|
|
|
|
env_set(argv[i], chars.c_str(), place);
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2014-07-14 05:36:26 +00:00
|
|
|
env_set(argv[i], NULL, place);
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2014-07-14 05:36:26 +00:00
|
|
|
size_t j = 0;
|
2016-04-19 02:25:12 +00:00
|
|
|
for (; i + 1 < argc; ++i) {
|
|
|
|
if (j < bufflen) {
|
2014-09-22 02:18:56 +00:00
|
|
|
wchar_t buffer[2] = {buff[j++], 0};
|
2014-08-22 19:52:41 +00:00
|
|
|
env_set(argv[i], buffer, place);
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2014-08-22 19:52:41 +00:00
|
|
|
env_set(argv[i], L"", place);
|
|
|
|
}
|
2014-07-14 05:36:26 +00:00
|
|
|
}
|
|
|
|
if (i < argc) env_set(argv[i], &buff[j], place);
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
} else if (array) {
|
2014-07-14 05:36:26 +00:00
|
|
|
wcstring tokens;
|
2014-09-22 02:18:56 +00:00
|
|
|
tokens.reserve(buff.size());
|
2014-07-14 05:36:26 +00:00
|
|
|
bool empty = true;
|
2016-04-19 02:25:12 +00:00
|
|
|
|
|
|
|
for (wcstring_range loc = wcstring_tok(buff, ifs); loc.first != wcstring::npos;
|
|
|
|
loc = wcstring_tok(buff, ifs, loc)) {
|
2014-09-22 02:18:56 +00:00
|
|
|
if (!empty) tokens.push_back(ARRAY_SEP);
|
|
|
|
tokens.append(buff, loc.first, loc.second);
|
2014-07-14 05:36:26 +00:00
|
|
|
empty = false;
|
|
|
|
}
|
|
|
|
env_set(argv[i], empty ? NULL : tokens.c_str(), place);
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
|
|
|
wcstring_range loc = wcstring_range(0, 0);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
while (i < argc) {
|
|
|
|
loc = wcstring_tok(buff, (i + 1 < argc) ? ifs : wcstring(), loc);
|
|
|
|
env_set(argv[i], loc.first == wcstring::npos ? L"" : &buff.c_str()[loc.first],
|
|
|
|
place);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2014-09-22 02:18:56 +00:00
|
|
|
++i;
|
2014-07-14 05:36:26 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return exit_res;
|
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// The status builtin. Gives various status information on fish.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2015-07-26 01:16:00 +00:00
|
|
|
wgetopter_t w;
|
2016-04-19 02:25:12 +00:00
|
|
|
enum {
|
2012-11-19 00:30:30 +00:00
|
|
|
NORMAL,
|
|
|
|
IS_SUBST,
|
|
|
|
IS_BLOCK,
|
|
|
|
IS_INTERACTIVE,
|
|
|
|
IS_LOGIN,
|
|
|
|
IS_FULL_JOB_CONTROL,
|
|
|
|
IS_INTERACTIVE_JOB_CONTROL,
|
|
|
|
IS_NO_JOB_CONTROL,
|
|
|
|
STACK_TRACE,
|
|
|
|
DONE,
|
|
|
|
CURRENT_FILENAME,
|
|
|
|
CURRENT_LINE_NUMBER
|
2016-04-19 02:25:12 +00:00
|
|
|
};
|
2012-11-19 00:30:30 +00:00
|
|
|
|
|
|
|
int mode = NORMAL;
|
|
|
|
|
|
|
|
int argc = builtin_count_args(argv);
|
2016-04-19 02:25:12 +00:00
|
|
|
int res = STATUS_BUILTIN_OK;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
const 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'},
|
|
|
|
{L"is-full-job-control", no_argument, &mode, IS_FULL_JOB_CONTROL},
|
|
|
|
{L"is-interactive-job-control", no_argument, &mode, IS_INTERACTIVE_JOB_CONTROL},
|
|
|
|
{L"is-no-job-control", no_argument, &mode, IS_NO_JOB_CONTROL},
|
|
|
|
{L"current-filename", no_argument, 0, 'f'},
|
|
|
|
{L"current-line-number", no_argument, 0, 'n'},
|
|
|
|
{L"job-control", required_argument, 0, 'j'},
|
|
|
|
{L"print-stack-trace", no_argument, 0, 't'},
|
|
|
|
{0, 0, 0, 0}};
|
|
|
|
|
|
|
|
while (1) {
|
2012-11-19 00:30:30 +00:00
|
|
|
int opt_index = 0;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
int opt = w.wgetopt_long(argc, argv, L":cbilfnhj:t", long_options, &opt_index);
|
|
|
|
if (opt == -1) break;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (opt) {
|
|
|
|
case 0: {
|
|
|
|
if (long_options[opt_index].flag != 0) break;
|
|
|
|
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0],
|
|
|
|
long_options[opt_index].name);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'c': {
|
2012-11-19 08:31:03 +00:00
|
|
|
mode = IS_SUBST;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'b': {
|
2012-11-19 08:31:03 +00:00
|
|
|
mode = IS_BLOCK;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'i': {
|
2012-11-19 08:31:03 +00:00
|
|
|
mode = IS_INTERACTIVE;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'l': {
|
2012-11-19 08:31:03 +00:00
|
|
|
mode = IS_LOGIN;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'f': {
|
2012-11-19 08:31:03 +00:00
|
|
|
mode = CURRENT_FILENAME;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'n': {
|
2012-11-19 08:31:03 +00:00
|
|
|
mode = CURRENT_LINE_NUMBER;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'h': {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.out);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'j': {
|
2015-07-26 01:16:00 +00:00
|
|
|
if (wcscmp(w.woptarg, L"full") == 0)
|
2012-11-19 08:31:03 +00:00
|
|
|
job_control_mode = JOB_CONTROL_ALL;
|
2015-07-26 01:16:00 +00:00
|
|
|
else if (wcscmp(w.woptarg, L"interactive") == 0)
|
2012-11-19 08:31:03 +00:00
|
|
|
job_control_mode = JOB_CONTROL_INTERACTIVE;
|
2015-07-26 01:16:00 +00:00
|
|
|
else if (wcscmp(w.woptarg, L"none") == 0)
|
2012-11-19 08:31:03 +00:00
|
|
|
job_control_mode = JOB_CONTROL_NONE;
|
2016-04-19 02:25:12 +00:00
|
|
|
else {
|
|
|
|
streams.err.append_format(L"%ls: Invalid job control mode '%ls'\n", L"status",
|
|
|
|
w.woptarg);
|
2012-11-19 08:31:03 +00:00
|
|
|
res = 1;
|
|
|
|
}
|
|
|
|
mode = DONE;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 't': {
|
2012-11-19 08:31:03 +00:00
|
|
|
mode = STACK_TRACE;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case ':': {
|
|
|
|
builtin_missing_argument(parser, streams, argv[0], argv[w.woptind - 1]);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case '?': {
|
|
|
|
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!res) {
|
|
|
|
switch (mode) {
|
|
|
|
case CURRENT_FILENAME: {
|
2012-11-19 08:31:03 +00:00
|
|
|
const wchar_t *fn = parser.current_filename();
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!fn) fn = _(L"Standard input");
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
streams.out.append_format(L"%ls\n", fn);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case CURRENT_LINE_NUMBER: {
|
|
|
|
streams.out.append_format(L"%d\n", parser.get_lineno());
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case IS_INTERACTIVE: {
|
2012-11-19 08:31:03 +00:00
|
|
|
return !is_interactive_session;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case IS_SUBST: {
|
2012-11-19 08:31:03 +00:00
|
|
|
return !is_subshell;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case IS_BLOCK: {
|
2012-11-19 08:31:03 +00:00
|
|
|
return !is_block;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case IS_LOGIN: {
|
2012-11-19 08:31:03 +00:00
|
|
|
return !is_login;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case IS_FULL_JOB_CONTROL: {
|
2012-11-19 08:31:03 +00:00
|
|
|
return job_control_mode != JOB_CONTROL_ALL;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case IS_INTERACTIVE_JOB_CONTROL: {
|
2012-11-19 08:31:03 +00:00
|
|
|
return job_control_mode != JOB_CONTROL_INTERACTIVE;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case IS_NO_JOB_CONTROL: {
|
2012-11-19 08:31:03 +00:00
|
|
|
return job_control_mode != JOB_CONTROL_NONE;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case STACK_TRACE: {
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.append(parser.stack_trace());
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case NORMAL: {
|
2012-11-19 08:31:03 +00:00
|
|
|
if (is_login)
|
2016-04-19 02:25:12 +00:00
|
|
|
streams.out.append_format(_(L"This is a login shell\n"));
|
2012-11-19 08:31:03 +00:00
|
|
|
else
|
2016-04-19 02:25:12 +00:00
|
|
|
streams.out.append_format(_(L"This is not a login shell\n"));
|
2005-09-20 13:26:39 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
streams.out.append_format(
|
|
|
|
_(L"Job control: %ls\n"),
|
|
|
|
job_control_mode == JOB_CONTROL_INTERACTIVE
|
|
|
|
? _(L"Only on interactive jobs")
|
|
|
|
: (job_control_mode == JOB_CONTROL_NONE ? _(L"Never") : _(L"Always")));
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.append(parser.stack_trace());
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
2005-09-20 13:26:39 +00:00
|
|
|
|
2012-11-19 00:30:30 +00:00
|
|
|
return res;
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// The exit builtin. Calls reader_exit to exit and returns the value specified.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_exit(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2012-11-19 00:30:30 +00:00
|
|
|
int argc = builtin_count_args(argv);
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
long ec = 0;
|
|
|
|
switch (argc) {
|
|
|
|
case 1: {
|
2012-11-19 08:31:03 +00:00
|
|
|
ec = proc_get_last_status();
|
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case 2: {
|
2012-11-19 08:31:03 +00:00
|
|
|
wchar_t *end;
|
|
|
|
errno = 0;
|
2016-04-19 02:25:12 +00:00
|
|
|
ec = wcstol(argv[1], &end, 10);
|
|
|
|
if (errno || *end != 0) {
|
|
|
|
streams.err.append_format(_(L"%ls: Argument '%ls' must be an integer\n"), argv[0],
|
|
|
|
argv[1]);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
default: {
|
|
|
|
streams.err.append_format(BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
|
2012-11-19 08:31:03 +00:00
|
|
|
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
reader_exit(1, 0);
|
|
|
|
return (int)ec;
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// The cd builtin. Changes the current directory to the one specified or to $HOME if none is
|
|
|
|
/// specified. The directory can be relative to any directory in the CDPATH variable.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2012-11-19 00:30:30 +00:00
|
|
|
env_var_t dir_in;
|
|
|
|
wcstring dir;
|
2016-04-19 02:25:12 +00:00
|
|
|
int res = STATUS_BUILTIN_OK;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (argv[1] == NULL) {
|
2012-11-19 00:30:30 +00:00
|
|
|
dir_in = env_get_string(L"HOME");
|
2016-04-19 02:25:12 +00:00
|
|
|
if (dir_in.missing_or_empty()) {
|
|
|
|
streams.err.append_format(_(L"%ls: Could not find home directory\n"), argv[0]);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2016-02-28 03:38:15 +00:00
|
|
|
dir_in = env_var_t(argv[1]);
|
2012-05-09 09:33:42 +00:00
|
|
|
}
|
2006-01-30 19:53:10 +00:00
|
|
|
|
2012-07-21 05:11:05 +00:00
|
|
|
bool got_cd_path = false;
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!dir_in.missing()) {
|
2012-07-21 05:11:05 +00:00
|
|
|
got_cd_path = path_get_cdpath(dir_in, &dir);
|
2012-05-09 09:33:42 +00:00
|
|
|
}
|
2006-01-30 19:53:10 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!got_cd_path) {
|
|
|
|
if (errno == ENOTDIR) {
|
|
|
|
streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"), argv[0],
|
|
|
|
dir_in.c_str());
|
|
|
|
} else if (errno == ENOENT) {
|
|
|
|
streams.err.append_format(_(L"%ls: The directory '%ls' does not exist\n"), argv[0],
|
|
|
|
dir_in.c_str());
|
|
|
|
} else if (errno == EROTTEN) {
|
|
|
|
streams.err.append_format(_(L"%ls: '%ls' is a rotten symlink\n"), argv[0],
|
|
|
|
dir_in.c_str());
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.err.append_format(_(L"%ls: Unknown error trying to locate directory '%ls'\n"),
|
2016-04-19 02:25:12 +00:00
|
|
|
argv[0], dir_in.c_str());
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
2016-05-15 03:35:54 +00:00
|
|
|
if (!shell_is_interactive()) {
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.err.append(parser.current_line());
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
res = 1;
|
2016-04-19 02:25:12 +00:00
|
|
|
} else if (wchdir(dir) != 0) {
|
2012-11-19 00:30:30 +00:00
|
|
|
struct stat buffer;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = wstat(dir, &buffer);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!status && S_ISDIR(buffer.st_mode)) {
|
|
|
|
streams.err.append_format(_(L"%ls: Permission denied: '%ls'\n"), argv[0], dir.c_str());
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
|
|
|
streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"), argv[0], dir.c_str());
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
2016-05-15 03:35:54 +00:00
|
|
|
if (!shell_is_interactive()) {
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.err.append(parser.current_line());
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
res = 1;
|
2016-04-19 02:25:12 +00:00
|
|
|
} else if (!env_set_pwd()) {
|
|
|
|
res = 1;
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.err.append_format(_(L"%ls: Could not set PWD variable\n"), argv[0]);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Implementation of the builtin count command, used to count the number of arguments sent to it.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_count(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2012-11-19 00:30:30 +00:00
|
|
|
int argc;
|
|
|
|
argc = builtin_count_args(argv);
|
2016-04-19 02:25:12 +00:00
|
|
|
streams.out.append_format(L"%d\n", argc - 1);
|
|
|
|
return !(argc - 1);
|
2007-07-31 21:23:32 +00:00
|
|
|
}
|
2005-09-20 13:26:39 +00:00
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Implementation of the builtin contains command, used to check if a specified string is part of
|
|
|
|
/// a list.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_contains(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2015-07-26 01:16:00 +00:00
|
|
|
wgetopter_t w;
|
2012-11-19 00:30:30 +00:00
|
|
|
int argc;
|
|
|
|
argc = builtin_count_args(argv);
|
|
|
|
wchar_t *needle;
|
2013-04-28 21:35:00 +00:00
|
|
|
bool should_output_index = false;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
const struct woption long_options[] = {
|
|
|
|
{L"help", no_argument, 0, 'h'}, {L"index", no_argument, 0, 'i'}, {0, 0, 0, 0}};
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
while (1) {
|
2012-11-19 00:30:30 +00:00
|
|
|
int opt_index = 0;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
int opt = w.wgetopt_long(argc, argv, L"+hi", long_options, &opt_index);
|
|
|
|
if (opt == -1) break;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (opt) {
|
|
|
|
case 0: {
|
|
|
|
assert(opt_index >= 0 &&
|
|
|
|
(size_t)opt_index < sizeof long_options / sizeof *long_options);
|
|
|
|
if (long_options[opt_index].flag != 0) break;
|
|
|
|
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0],
|
|
|
|
long_options[opt_index].name);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'h': {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.out);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case ':': {
|
|
|
|
builtin_missing_argument(parser, streams, argv[0], argv[w.woptind - 1]);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case '?': {
|
|
|
|
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'i': {
|
2013-04-28 21:35:00 +00:00
|
|
|
should_output_index = true;
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-26 01:16:00 +00:00
|
|
|
needle = argv[w.woptind];
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!needle) {
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.err.append_format(_(L"%ls: Key not specified\n"), argv[0]);
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
|
|
|
for (int i = w.woptind + 1; i < argc; i++) {
|
|
|
|
if (!wcscmp(needle, argv[i])) {
|
|
|
|
if (should_output_index) streams.out.append_format(L"%d\n", i - w.woptind);
|
2016-03-04 02:49:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
2007-08-01 22:53:18 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// The . (dot) builtin, sometimes called source. Evaluates the contents of a file.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_source(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2012-03-02 08:27:40 +00:00
|
|
|
ASSERT_IS_MAIN_THREAD();
|
2012-11-19 00:30:30 +00:00
|
|
|
int fd;
|
|
|
|
int res = STATUS_BUILTIN_OK;
|
|
|
|
struct stat buf;
|
|
|
|
int argc;
|
|
|
|
|
|
|
|
argc = builtin_count_args(argv);
|
|
|
|
|
2013-12-16 00:43:22 +00:00
|
|
|
const wchar_t *fn, *fn_intern;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (argc < 2 || (wcscmp(argv[1], L"-") == 0)) {
|
2012-11-19 00:30:30 +00:00
|
|
|
fn = L"-";
|
|
|
|
fn_intern = fn;
|
2015-09-21 18:24:49 +00:00
|
|
|
fd = dup(streams.stdin_fd);
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
|
|
|
if ((fd = wopen_cloexec(argv[1], O_RDONLY)) == -1) {
|
|
|
|
streams.err.append_format(_(L"%ls: Error encountered while sourcing file '%ls':\n"),
|
|
|
|
argv[0], argv[1]);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_wperror(L"source", streams);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (fstat(fd, &buf) == -1) {
|
2012-11-19 00:30:30 +00:00
|
|
|
close(fd);
|
2016-04-19 02:25:12 +00:00
|
|
|
streams.err.append_format(_(L"%ls: Error encountered while sourcing file '%ls':\n"),
|
|
|
|
argv[0], argv[1]);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_wperror(L"source", streams);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!S_ISREG(buf.st_mode)) {
|
2012-11-19 00:30:30 +00:00
|
|
|
close(fd);
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.err.append_format(_(L"%ls: '%ls' is not a file\n"), argv[0], argv[1]);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2013-12-16 00:43:22 +00:00
|
|
|
fn_intern = intern(argv[1]);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
parser.push_block(new source_block_t(fn_intern));
|
|
|
|
reader_push_current_filename(fn_intern);
|
|
|
|
|
2016-04-06 20:04:44 +00:00
|
|
|
env_set_argv(argc > 1 ? argv + 2 : argv + 1);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2015-09-21 18:24:49 +00:00
|
|
|
res = reader_read(fd, streams.io_chain ? *streams.io_chain : io_chain_t());
|
2012-11-19 00:30:30 +00:00
|
|
|
|
|
|
|
parser.pop_block();
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (res) {
|
|
|
|
streams.err.append_format(_(L"%ls: Error while reading file '%ls'\n"), argv[0],
|
|
|
|
fn_intern == intern_static(L"-") ? L"<stdin>" : fn_intern);
|
|
|
|
} else {
|
2012-11-19 00:30:30 +00:00
|
|
|
res = proc_get_last_status();
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Do not close fd after calling reader_read. reader_read automatically closes it before calling
|
|
|
|
// eval.
|
2012-11-19 00:30:30 +00:00
|
|
|
reader_pop_current_filename();
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// 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 were used.
|
2016-04-19 02:25:12 +00:00
|
|
|
static void make_first(job_t *j) { job_promote(j); }
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Builtin for putting a job in the foreground.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|
|
|
job_t *j = NULL;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (argv[1] == 0) {
|
|
|
|
// Select last constructed job (I.e. first job in the job que) that is possible to put in
|
|
|
|
// the foreground.
|
2012-11-19 00:30:30 +00:00
|
|
|
job_iterator_t jobs;
|
2016-04-19 02:25:12 +00:00
|
|
|
while ((j = jobs.next())) {
|
2012-11-19 00:30:30 +00:00
|
|
|
if (job_get_flag(j, JOB_CONSTRUCTED) && (!job_is_completed(j)) &&
|
2016-04-19 02:25:12 +00:00
|
|
|
((job_is_stopped(j) || (!job_get_flag(j, JOB_FOREGROUND))) &&
|
|
|
|
job_get_flag(j, JOB_CONTROL))) {
|
2012-11-19 00:30:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!j) {
|
|
|
|
streams.err.append_format(_(L"%ls: There are no suitable jobs\n"), argv[0]);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
} else if (argv[2] != 0) {
|
2016-06-18 14:41:27 +00:00
|
|
|
// Specifying more than one job to put to the foreground is a syntax error, we still
|
2016-04-19 02:25:12 +00:00
|
|
|
// 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.
|
2012-11-19 00:30:30 +00:00
|
|
|
wchar_t *endptr;
|
|
|
|
int pid;
|
|
|
|
int found_job = 0;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
pid = fish_wcstoi(argv[1], &endptr, 10);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!(*endptr || errno)) {
|
2012-11-19 00:30:30 +00:00
|
|
|
j = job_get_from_pid(pid);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (j) found_job = 1;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (found_job) {
|
|
|
|
streams.err.append_format(_(L"%ls: Ambiguous job\n"), argv[0]);
|
|
|
|
} else {
|
|
|
|
streams.err.append_format(_(L"%ls: '%ls' is not a job\n"), argv[0], argv[1]);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
j = 0;
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2012-11-19 00:30:30 +00:00
|
|
|
wchar_t *end;
|
|
|
|
int pid;
|
|
|
|
errno = 0;
|
|
|
|
pid = abs(fish_wcstoi(argv[1], &end, 10));
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (*end || errno) {
|
|
|
|
streams.err.append_format(BUILTIN_ERR_NOT_NUMBER, argv[0], argv[1]);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2012-11-19 00:30:30 +00:00
|
|
|
j = job_get_from_pid(pid);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!j || !job_get_flag(j, JOB_CONSTRUCTED) || job_is_completed(j)) {
|
|
|
|
streams.err.append_format(_(L"%ls: No suitable job: %d\n"), argv[0], pid);
|
|
|
|
j = 0;
|
|
|
|
} else if (!job_get_flag(j, JOB_CONTROL)) {
|
|
|
|
streams.err.append_format(_(L"%ls: Can't put job %d, '%ls' to foreground because "
|
|
|
|
L"it is not under job control\n"),
|
|
|
|
argv[0], pid, j->command_wcstr());
|
|
|
|
j = 0;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (j) {
|
|
|
|
if (streams.err_is_redirected) {
|
|
|
|
streams.err.append_format(FG_MSG, j->job_id, j->command_wcstr());
|
|
|
|
} else {
|
|
|
|
// If we aren't redirecting, send output to real stderr, since stuff in sb_err won't get
|
|
|
|
// printed until the command finishes.
|
|
|
|
fwprintf(stderr, FG_MSG, j->job_id, j->command_wcstr());
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
2015-07-26 07:58:32 +00:00
|
|
|
const wcstring ft = tok_first(j->command());
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!ft.empty()) env_set(L"_", ft.c_str(), ENV_EXPORT);
|
2014-08-03 04:01:40 +00:00
|
|
|
reader_write_title(j->command());
|
2012-11-19 00:30:30 +00:00
|
|
|
|
|
|
|
make_first(j);
|
|
|
|
job_set_flag(j, JOB_FOREGROUND, 1);
|
2005-09-20 13:26:39 +00:00
|
|
|
|
2012-11-19 00:30:30 +00:00
|
|
|
job_continue(j, job_is_stopped(j));
|
|
|
|
}
|
2016-06-18 14:41:27 +00:00
|
|
|
return j ? STATUS_BUILTIN_OK : STATUS_BUILTIN_ERROR;
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Helper function for builtin_bg().
|
2016-04-19 02:25:12 +00:00
|
|
|
static int send_to_bg(parser_t &parser, io_streams_t &streams, job_t *j, const wchar_t *name) {
|
|
|
|
if (j == 0) {
|
|
|
|
streams.err.append_format(_(L"%ls: Unknown job '%ls'\n"), L"bg", name);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, L"bg", streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
} else if (!job_get_flag(j, JOB_CONTROL)) {
|
|
|
|
streams.err.append_format(
|
|
|
|
_(L"%ls: Can't put job %d, '%ls' to background because it is not under job control\n"),
|
|
|
|
L"bg", j->job_id, j->command_wcstr());
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, L"bg", streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
2016-05-04 22:19:47 +00:00
|
|
|
|
|
|
|
streams.err.append_format(_(L"Send job %d '%ls' to background\n"), j->job_id,
|
|
|
|
j->command_wcstr());
|
2012-11-19 00:30:30 +00:00
|
|
|
make_first(j);
|
|
|
|
job_set_flag(j, JOB_FOREGROUND, 0);
|
|
|
|
job_continue(j, job_is_stopped(j));
|
|
|
|
return STATUS_BUILTIN_OK;
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Builtin for putting a job in the background.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_bg(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2012-11-19 00:30:30 +00:00
|
|
|
int res = STATUS_BUILTIN_OK;
|
2006-01-30 19:53:10 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (argv[1] == 0) {
|
2012-11-19 00:30:30 +00:00
|
|
|
job_t *j;
|
2012-01-30 00:36:21 +00:00
|
|
|
job_iterator_t jobs;
|
2016-04-19 02:25:12 +00:00
|
|
|
while ((j = jobs.next())) {
|
|
|
|
if (job_is_stopped(j) && job_get_flag(j, JOB_CONTROL) && (!job_is_completed(j))) {
|
2012-11-19 00:30:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (!j) {
|
|
|
|
streams.err.append_format(_(L"%ls: There are no suitable jobs\n"), argv[0]);
|
2016-06-18 15:00:40 +00:00
|
|
|
res = STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2015-09-21 18:24:49 +00:00
|
|
|
res = send_to_bg(parser, streams, j, _(L"(default)"));
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
} else {
|
2012-11-19 00:30:30 +00:00
|
|
|
wchar_t *end;
|
|
|
|
int i;
|
|
|
|
int pid;
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
for (i = 1; argv[i]; i++) {
|
|
|
|
errno = 0;
|
2012-11-19 00:30:30 +00:00
|
|
|
pid = fish_wcstoi(argv[i], &end, 10);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (errno || pid < 0 || *end || !job_get_from_pid(pid)) {
|
|
|
|
streams.err.append_format(_(L"%ls: '%ls' is not a job\n"), argv[0], argv[i]);
|
2016-06-18 15:00:40 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2016-06-18 15:00:40 +00:00
|
|
|
res |= send_to_bg(parser, streams, job_get_from_pid(pid), *argv);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// This function handles both the 'continue' and the 'break' builtins that are used for loop
|
|
|
|
/// control.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_break_continue(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|
|
|
int is_break = (wcscmp(argv[0], L"break") == 0);
|
2012-11-19 00:30:30 +00:00
|
|
|
int argc = builtin_count_args(argv);
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (argc != 1) {
|
|
|
|
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], argv[1]);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Find the index of the enclosing for or while loop. Recall that incrementing loop_idx goes
|
|
|
|
// 'up' to outer blocks.
|
2013-12-21 01:41:21 +00:00
|
|
|
size_t loop_idx;
|
2016-04-19 02:25:12 +00:00
|
|
|
for (loop_idx = 0; loop_idx < parser.block_count(); loop_idx++) {
|
2013-12-21 01:41:21 +00:00
|
|
|
const block_t *b = parser.block_at_index(loop_idx);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (b->type() == WHILE || b->type() == FOR) break;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (loop_idx >= parser.block_count()) {
|
|
|
|
streams.err.append_format(_(L"%ls: Not inside of loop\n"), argv[0]);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Skip blocks interior to the loop.
|
2013-12-21 01:41:21 +00:00
|
|
|
size_t block_idx = loop_idx;
|
2016-04-19 02:25:12 +00:00
|
|
|
while (block_idx--) {
|
2013-12-21 01:41:21 +00:00
|
|
|
parser.block_at_index(block_idx)->skip = true;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2014-01-15 09:40:40 +00:00
|
|
|
|
2013-12-21 01:41:21 +00:00
|
|
|
/* Skip the loop itself */
|
|
|
|
block_t *loop_block = parser.block_at_index(loop_idx);
|
|
|
|
loop_block->skip = true;
|
|
|
|
loop_block->loop_status = is_break ? LOOP_BREAK : LOOP_CONTINUE;
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Implementation of the builtin breakpoint command, used to launch the interactive debugger.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_breakpoint(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2012-11-19 00:30:30 +00:00
|
|
|
parser.push_block(new breakpoint_block_t());
|
|
|
|
|
2015-09-21 18:24:49 +00:00
|
|
|
reader_read(STDIN_FILENO, streams.io_chain ? *streams.io_chain : io_chain_t());
|
2012-11-19 00:30:30 +00:00
|
|
|
|
|
|
|
parser.pop_block();
|
|
|
|
|
|
|
|
return proc_get_last_status();
|
2006-11-11 10:54:00 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Function for handling the \c return builtin.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_return(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2012-11-19 00:30:30 +00:00
|
|
|
int argc = builtin_count_args(argv);
|
|
|
|
int status = proc_get_last_status();
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (argc) {
|
|
|
|
case 1: {
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 2: {
|
2012-11-19 08:31:03 +00:00
|
|
|
wchar_t *end;
|
|
|
|
errno = 0;
|
2016-04-19 02:25:12 +00:00
|
|
|
status = fish_wcstoi(argv[1], &end, 10);
|
|
|
|
if (errno || *end != 0) {
|
|
|
|
streams.err.append_format(_(L"%ls: Argument '%ls' must be an integer\n"), argv[0],
|
|
|
|
argv[1]);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
default: {
|
|
|
|
streams.err.append_format(_(L"%ls: Too many arguments\n"), argv[0]);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Find the function block.
|
2013-12-21 01:41:21 +00:00
|
|
|
size_t function_block_idx;
|
2016-04-19 02:25:12 +00:00
|
|
|
for (function_block_idx = 0; function_block_idx < parser.block_count(); function_block_idx++) {
|
2013-12-21 01:41:21 +00:00
|
|
|
const block_t *b = parser.block_at_index(function_block_idx);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (b->type() == FUNCTION_CALL || b->type() == FUNCTION_CALL_NO_SHADOW) break;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (function_block_idx >= parser.block_count()) {
|
|
|
|
streams.err.append_format(_(L"%ls: Not inside of function\n"), argv[0]);
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.err);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
2014-01-15 09:40:40 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Skip everything up to (and then including) the function block.
|
|
|
|
for (size_t i = 0; i < function_block_idx; i++) {
|
2013-12-21 01:41:21 +00:00
|
|
|
block_t *b = parser.block_at_index(i);
|
|
|
|
b->skip = true;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2013-12-21 01:41:21 +00:00
|
|
|
parser.block_at_index(function_block_idx)->skip = true;
|
2012-11-19 00:30:30 +00:00
|
|
|
return status;
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|
|
|
|
|
2016-06-27 19:49:34 +00:00
|
|
|
// Formats a single history record, including a trailing newline. Returns true
|
|
|
|
// if bytes were written to the output stream and false otherwise.
|
|
|
|
static bool format_history_record(const history_item_t &item, const bool with_time,
|
|
|
|
output_stream_t *const out) {
|
|
|
|
if (with_time) {
|
|
|
|
const time_t seconds = item.timestamp();
|
|
|
|
struct tm timestamp;
|
|
|
|
if (!localtime_r(&seconds, ×tamp)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
char timestamp_string[22];
|
|
|
|
if (strftime(timestamp_string, 22, "%Y-%m-%d %H:%M:%S ", ×tamp) != 21) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
out->append(str2wcstring(timestamp_string));
|
|
|
|
}
|
|
|
|
out->append(item.str());
|
|
|
|
out->append(L"\n");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// History of commands executed by user.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2012-06-05 04:24:42 +00:00
|
|
|
int argc = builtin_count_args(argv);
|
|
|
|
|
2012-11-19 00:30:30 +00:00
|
|
|
bool search_history = false;
|
2012-06-05 04:24:42 +00:00
|
|
|
bool delete_item = false;
|
|
|
|
bool search_prefix = false;
|
|
|
|
bool save_history = false;
|
|
|
|
bool clear_history = false;
|
2014-07-25 17:08:21 +00:00
|
|
|
bool merge_history = false;
|
2016-06-27 19:49:34 +00:00
|
|
|
bool with_time = false;
|
2012-06-05 04:24:42 +00:00
|
|
|
|
2016-06-27 19:49:34 +00:00
|
|
|
static const struct woption long_options[] = {
|
|
|
|
{L"prefix", no_argument, 0, 'p'}, {L"delete", no_argument, 0, 'd'},
|
|
|
|
{L"search", no_argument, 0, 's'}, {L"contains", no_argument, 0, 'c'},
|
|
|
|
{L"save", no_argument, 0, 'v'}, {L"clear", no_argument, 0, 'l'},
|
|
|
|
{L"merge", no_argument, 0, 'm'}, {L"help", no_argument, 0, 'h'},
|
|
|
|
{L"with-time", no_argument, 0, 't'}, {0, 0, 0, 0}};
|
2012-06-05 04:24:42 +00:00
|
|
|
|
|
|
|
int opt = 0;
|
|
|
|
int opt_index = 0;
|
2015-07-26 01:16:00 +00:00
|
|
|
|
|
|
|
wgetopter_t w;
|
2012-06-05 04:24:42 +00:00
|
|
|
history_t *history = reader_get_history();
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Use the default history if we have none (which happens if invoked non-interactively, e.g.
|
|
|
|
// from webconfig.py.
|
|
|
|
if (!history) history = &history_t::history_with_name(L"fish");
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-07-11 01:22:49 +00:00
|
|
|
while ((opt = w.wgetopt_long_only(argc, argv, L"pdscvlmht", long_options, &opt_index)) != EOF) {
|
2016-04-19 02:25:12 +00:00
|
|
|
switch (opt) {
|
|
|
|
case 'p': {
|
2012-11-19 08:31:03 +00:00
|
|
|
search_prefix = true;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'd': {
|
2012-11-19 08:31:03 +00:00
|
|
|
delete_item = true;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 's': {
|
2012-11-19 08:31:03 +00:00
|
|
|
search_history = true;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'c': {
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'v': {
|
2012-11-19 08:31:03 +00:00
|
|
|
save_history = true;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'l': {
|
2012-11-19 08:31:03 +00:00
|
|
|
clear_history = true;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case 'm': {
|
2014-07-25 17:08:21 +00:00
|
|
|
merge_history = true;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2016-06-27 19:49:34 +00:00
|
|
|
case 't': {
|
|
|
|
with_time = true;
|
|
|
|
break;
|
|
|
|
}
|
2016-04-19 02:25:12 +00:00
|
|
|
case 'h': {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.out);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
case '?': {
|
|
|
|
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], argv[w.woptind - 1]);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
break;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
|
|
|
default: {
|
|
|
|
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], argv[w.woptind - 1]);
|
2012-11-19 08:31:03 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
}
|
2012-06-05 04:24:42 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2012-06-05 04:24:42 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Everything after is an argument.
|
2015-07-26 01:16:00 +00:00
|
|
|
const wcstring_list_t args(argv + w.woptind, argv + argc);
|
2012-07-27 07:31:00 +00:00
|
|
|
|
2016-07-11 01:22:49 +00:00
|
|
|
if (argc == 1) {
|
2016-06-27 19:49:34 +00:00
|
|
|
for (int i = 1; // 0 is the current input
|
|
|
|
!history->item_at_index(i).empty(); ++i) {
|
|
|
|
if (!format_history_record(history->item_at_index(i), with_time, &streams.out)) {
|
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
}
|
2012-06-05 04:24:42 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (merge_history) {
|
2014-07-25 17:08:21 +00:00
|
|
|
history->incorporate_external_changes();
|
2016-02-16 19:38:31 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
2014-07-25 17:08:21 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (search_history) {
|
2012-06-05 04:24:42 +00:00
|
|
|
int res = STATUS_BUILTIN_ERROR;
|
2016-04-19 02:25:12 +00:00
|
|
|
for (wcstring_list_t::const_iterator iter = args.begin(); iter != args.end(); ++iter) {
|
2012-07-27 07:31:00 +00:00
|
|
|
const wcstring &search_string = *iter;
|
2016-04-19 02:25:12 +00:00
|
|
|
if (search_string.empty()) {
|
|
|
|
streams.err.append_format(BUILTIN_ERR_COMBO2, argv[0],
|
|
|
|
L"Use --search with either --contains or --prefix");
|
2012-07-27 07:31:00 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
history_search_t searcher = history_search_t(
|
|
|
|
*history, search_string,
|
|
|
|
search_prefix ? HISTORY_SEARCH_TYPE_PREFIX : HISTORY_SEARCH_TYPE_CONTAINS);
|
|
|
|
while (searcher.go_backwards()) {
|
2016-06-27 19:49:34 +00:00
|
|
|
if (!format_history_record(searcher.current_item(), with_time, &streams.out)) {
|
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
2012-07-27 07:31:00 +00:00
|
|
|
res = STATUS_BUILTIN_OK;
|
|
|
|
}
|
2012-06-05 04:24:42 +00:00
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (delete_item) {
|
|
|
|
for (wcstring_list_t::const_iterator iter = args.begin(); iter != args.end(); ++iter) {
|
2012-07-27 07:31:00 +00:00
|
|
|
wcstring delete_string = *iter;
|
|
|
|
if (delete_string[0] == '"' && delete_string[delete_string.length() - 1] == '"')
|
|
|
|
delete_string = delete_string.substr(1, delete_string.length() - 2);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2012-07-27 07:31:00 +00:00
|
|
|
history->remove(delete_string);
|
|
|
|
}
|
2012-06-05 04:24:42 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (save_history) {
|
2012-06-05 04:24:42 +00:00
|
|
|
history->save();
|
|
|
|
return STATUS_BUILTIN_OK;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (clear_history) {
|
2012-06-05 04:24:42 +00:00
|
|
|
history->clear();
|
|
|
|
history->save();
|
|
|
|
return STATUS_BUILTIN_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-04 02:02:46 +00:00
|
|
|
#if 0
|
|
|
|
// Disabled for the 2.2.0 release: https://github.com/fish-shell/fish-shell/issues/1809.
|
2015-09-21 18:24:49 +00:00
|
|
|
int builtin_parse(parser_t &parser, io_streams_t &streams, wchar_t **argv)
|
2013-06-15 21:32:38 +00:00
|
|
|
{
|
2013-06-30 22:38:31 +00:00
|
|
|
struct sigaction act;
|
|
|
|
sigemptyset(& act.sa_mask);
|
|
|
|
act.sa_flags=0;
|
|
|
|
act.sa_handler=SIG_DFL;
|
|
|
|
sigaction(SIGINT, &act, 0);
|
|
|
|
|
2013-06-15 21:32:38 +00:00
|
|
|
std::vector<char> txt;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
char buff[256];
|
2015-09-21 18:24:49 +00:00
|
|
|
ssize_t amt = read_loop(streams.stdin_fd, buff, sizeof buff);
|
2013-06-15 21:32:38 +00:00
|
|
|
if (amt <= 0) break;
|
|
|
|
txt.insert(txt.end(), buff, buff + amt);
|
|
|
|
}
|
|
|
|
if (! txt.empty())
|
|
|
|
{
|
|
|
|
const wcstring src = str2wcstring(&txt.at(0), txt.size());
|
|
|
|
parse_node_tree_t parse_tree;
|
2013-06-15 22:21:35 +00:00
|
|
|
parse_error_list_t errors;
|
2014-12-23 18:58:45 +00:00
|
|
|
bool success = parse_tree_from_string(src, parse_flag_include_comments, &parse_tree, &errors);
|
2013-06-15 22:21:35 +00:00
|
|
|
if (! success)
|
|
|
|
{
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.append(L"Parsing failed:\n");
|
2013-06-15 22:21:35 +00:00
|
|
|
for (size_t i=0; i < errors.size(); i++)
|
|
|
|
{
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.append(errors.at(i).describe(src));
|
|
|
|
streams.out.push_back(L'\n');
|
2013-06-15 22:21:35 +00:00
|
|
|
}
|
2014-01-15 09:40:40 +00:00
|
|
|
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.append(L"(Reparsed with continue after error)\n");
|
2013-12-08 22:13:23 +00:00
|
|
|
parse_tree.clear();
|
|
|
|
errors.clear();
|
2014-12-23 18:58:45 +00:00
|
|
|
parse_tree_from_string(src, parse_flag_continue_after_error | parse_flag_include_comments, &parse_tree, &errors);
|
2013-12-08 22:13:23 +00:00
|
|
|
}
|
|
|
|
const wcstring dump = parse_dump_tree(parse_tree, src);
|
2015-09-21 18:24:49 +00:00
|
|
|
streams.out.append(dump);
|
2013-06-15 21:32:38 +00:00
|
|
|
}
|
|
|
|
return STATUS_BUILTIN_OK;
|
|
|
|
}
|
2016-04-04 02:02:46 +00:00
|
|
|
#endif
|
2012-06-05 04:24:42 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
int builtin_true(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2014-09-29 20:26:28 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
int builtin_false(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
2014-09-29 20:26:28 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-04-14 00:14:50 +00:00
|
|
|
/// An implementation of the external realpath command that doesn't support any options. It's meant
|
|
|
|
/// to be used only by scripts which need to be portable. In general scripts shouldn't invoke this
|
|
|
|
/// directly. They should just use `realpath` which will fallback to this builtin if an external
|
|
|
|
/// command cannot be found. This behaves like the external `realpath --canonicalize-existing`;
|
|
|
|
/// that is, it requires all path components, including the final, to exist.
|
|
|
|
int builtin_fish_realpath(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|
|
|
int argc = builtin_count_args(argv);
|
|
|
|
|
|
|
|
if (argc != 2) {
|
|
|
|
streams.err.append_format(_(L"%ls: Expected one argument, got %d\n"), argv[0], argc - 1);
|
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
wchar_t *real_path = wrealpath(argv[1], NULL);
|
|
|
|
if (real_path) {
|
|
|
|
// Yay! We could resolve the path.
|
|
|
|
streams.out.append(real_path);
|
|
|
|
free((void *)real_path);
|
|
|
|
} else {
|
|
|
|
// The path isn't a simple filename and couldn't be resolved to an absolute path.
|
|
|
|
streams.err.append_format(_(L"%ls: Invalid path: %ls\n"), argv[0], argv[1]);
|
|
|
|
return STATUS_BUILTIN_ERROR;
|
|
|
|
}
|
|
|
|
streams.out.append(L"\n");
|
|
|
|
return STATUS_BUILTIN_OK;
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// END OF BUILTIN COMMANDS
|
|
|
|
// Below are functions for handling the builtin commands.
|
|
|
|
// THESE MUST BE SORTED BY NAME! Completion lookup uses binary search.
|
2005-12-15 13:59:02 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
// Data about all the builtin commands in fish.
|
|
|
|
// Functions that are bound to builtin_generic are handled directly by the parser.
|
|
|
|
// NOTE: These must be kept in sorted order!
|
|
|
|
static const builtin_data_t builtin_datas[] = {
|
|
|
|
{L"[", &builtin_test, N_(L"Test a condition")},
|
2015-05-03 04:35:49 +00:00
|
|
|
#if 0
|
2016-04-04 02:02:46 +00:00
|
|
|
// Disabled for the 2.2.0 release: https://github.com/fish-shell/fish-shell/issues/1809.
|
2014-01-08 23:13:08 +00:00
|
|
|
{ L"__fish_parse", &builtin_parse, N_(L"Try out the new parser") },
|
2015-05-03 04:35:49 +00:00
|
|
|
#endif
|
2016-04-19 02:25:12 +00:00
|
|
|
{L"and", &builtin_generic, N_(L"Execute command if previous command suceeded")},
|
|
|
|
{L"begin", &builtin_generic, N_(L"Create a block of code")},
|
|
|
|
{L"bg", &builtin_bg, N_(L"Send job to background")},
|
|
|
|
{L"bind", &builtin_bind, N_(L"Handle fish key bindings")},
|
|
|
|
{L"block", &builtin_block, N_(L"Temporarily block delivery of events")},
|
|
|
|
{L"break", &builtin_break_continue, N_(L"Stop the innermost loop")},
|
|
|
|
{L"breakpoint", &builtin_breakpoint,
|
|
|
|
N_(L"Temporarily halt execution of a script and launch an interactive debug prompt")},
|
|
|
|
{L"builtin", &builtin_builtin, N_(L"Run a builtin command instead of a function")},
|
|
|
|
{L"case", &builtin_generic, N_(L"Conditionally execute a block of commands")},
|
|
|
|
{L"cd", &builtin_cd, N_(L"Change working directory")},
|
|
|
|
{L"command", &builtin_command, N_(L"Run a program instead of a function or builtin")},
|
|
|
|
{L"commandline", &builtin_commandline, N_(L"Set or get the commandline")},
|
|
|
|
{L"complete", &builtin_complete, N_(L"Edit command specific completions")},
|
|
|
|
{L"contains", &builtin_contains, N_(L"Search for a specified string in a list")},
|
|
|
|
{L"continue", &builtin_break_continue,
|
|
|
|
N_(L"Skip the rest of the current lap of the innermost loop")},
|
|
|
|
{L"count", &builtin_count, N_(L"Count the number of arguments")},
|
|
|
|
{L"echo", &builtin_echo, N_(L"Print arguments")},
|
|
|
|
{L"else", &builtin_generic, N_(L"Evaluate block if condition is false")},
|
|
|
|
{L"emit", &builtin_emit, N_(L"Emit an event")},
|
|
|
|
{L"end", &builtin_generic, N_(L"End a block of commands")},
|
|
|
|
{L"exec", &builtin_generic, N_(L"Run command in current process")},
|
|
|
|
{L"exit", &builtin_exit, N_(L"Exit the shell")},
|
|
|
|
{L"false", &builtin_false, N_(L"Return an unsuccessful result")},
|
|
|
|
{L"fg", &builtin_fg, N_(L"Send job to foreground")},
|
2016-04-14 00:14:50 +00:00
|
|
|
{L"fish_realpath", &builtin_fish_realpath,
|
|
|
|
N_(L"Convert path to absolute path without symlinks")},
|
2016-04-19 02:25:12 +00:00
|
|
|
{L"for", &builtin_generic, N_(L"Perform a set of commands multiple times")},
|
|
|
|
{L"function", &builtin_generic, N_(L"Define a new function")},
|
|
|
|
{L"functions", &builtin_functions, N_(L"List or remove functions")},
|
|
|
|
{L"history", &builtin_history, N_(L"History of commands executed by user")},
|
|
|
|
{L"if", &builtin_generic, N_(L"Evaluate block if condition is true")},
|
|
|
|
{L"jobs", &builtin_jobs, N_(L"Print currently running jobs")},
|
|
|
|
{L"not", &builtin_generic, N_(L"Negate exit status of job")},
|
|
|
|
{L"or", &builtin_generic, N_(L"Execute command if previous command failed")},
|
|
|
|
{L"printf", &builtin_printf, N_(L"Prints formatted text")},
|
|
|
|
{L"pwd", &builtin_pwd, N_(L"Print the working directory")},
|
|
|
|
{L"random", &builtin_random, N_(L"Generate random number")},
|
|
|
|
{L"read", &builtin_read, N_(L"Read a line of input into variables")},
|
|
|
|
{L"return", &builtin_return, N_(L"Stop the currently evaluated function")},
|
|
|
|
{L"set", &builtin_set, N_(L"Handle environment variables")},
|
|
|
|
{L"set_color", &builtin_set_color, N_(L"Set the terminal color")},
|
|
|
|
{L"source", &builtin_source, N_(L"Evaluate contents of file")},
|
|
|
|
{L"status", &builtin_status, N_(L"Return status information about fish")},
|
|
|
|
{L"string", &builtin_string, N_(L"Manipulate strings")},
|
|
|
|
{L"switch", &builtin_generic, N_(L"Conditionally execute a block of commands")},
|
|
|
|
{L"test", &builtin_test, N_(L"Test a condition")},
|
|
|
|
{L"true", &builtin_true, N_(L"Return a successful result")},
|
|
|
|
{L"ulimit", &builtin_ulimit, N_(L"Set or get the shells resource usage limits")},
|
|
|
|
{L"while", &builtin_generic, N_(L"Perform a command multiple times")}};
|
2012-02-01 03:47:56 +00:00
|
|
|
|
|
|
|
#define BUILTIN_COUNT (sizeof builtin_datas / sizeof *builtin_datas)
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
static const builtin_data_t *builtin_lookup(const wcstring &name) {
|
2012-02-01 03:47:56 +00:00
|
|
|
const builtin_data_t *array_end = builtin_datas + BUILTIN_COUNT;
|
|
|
|
const builtin_data_t *found = std::lower_bound(builtin_datas, array_end, name);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (found != array_end && name == found->name) {
|
2012-02-01 03:47:56 +00:00
|
|
|
return found;
|
|
|
|
}
|
2016-05-04 22:19:47 +00:00
|
|
|
return NULL;
|
2006-02-05 13:08:40 +00:00
|
|
|
}
|
2006-01-30 19:53:10 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
void builtin_init() {
|
|
|
|
for (size_t i = 0; i < BUILTIN_COUNT; i++) {
|
2012-11-19 00:30:30 +00:00
|
|
|
intern_static(builtin_datas[i].name);
|
|
|
|
}
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
void builtin_destroy() {}
|
2005-09-20 13:26:39 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
int builtin_exists(const wcstring &cmd) { return !!builtin_lookup(cmd); }
|
2005-09-20 13:26:39 +00:00
|
|
|
|
2016-04-20 01:17:39 +00:00
|
|
|
/// Return true if the specified builtin should handle it's own help, false otherwise.
|
2016-04-19 02:25:12 +00:00
|
|
|
static int internal_help(const wchar_t *cmd) {
|
2012-11-19 00:30:30 +00:00
|
|
|
CHECK(cmd, 0);
|
2016-04-19 02:25:12 +00:00
|
|
|
return contains(cmd, L"for", L"while", L"function", L"if", L"end", L"switch", L"case", L"count",
|
|
|
|
L"printf");
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
int builtin_run(parser_t &parser, const wchar_t *const *argv, io_streams_t &streams) {
|
|
|
|
int (*cmd)(parser_t & parser, io_streams_t & streams, const wchar_t *const *argv) = NULL;
|
2005-09-20 13:26:39 +00:00
|
|
|
|
2012-11-19 00:30:30 +00:00
|
|
|
CHECK(argv, STATUS_BUILTIN_ERROR);
|
|
|
|
CHECK(argv[0], STATUS_BUILTIN_ERROR);
|
|
|
|
|
2012-02-01 03:47:56 +00:00
|
|
|
const builtin_data_t *data = builtin_lookup(argv[0]);
|
2016-04-19 02:25:12 +00:00
|
|
|
cmd = (int (*)(parser_t & parser, io_streams_t & streams, const wchar_t *const *))(
|
|
|
|
data ? data->func : NULL);
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (argv[1] != 0 && !internal_help(argv[0])) {
|
|
|
|
if (argv[2] == 0 && (parse_util_argument_is_help(argv[1], 0))) {
|
2015-09-21 18:24:49 +00:00
|
|
|
builtin_print_help(parser, streams, argv[0], streams.out);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
if (data != NULL) {
|
2016-05-04 22:19:47 +00:00
|
|
|
return cmd(parser, streams, argv);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2016-05-04 22:19:47 +00:00
|
|
|
|
|
|
|
debug(0, UNKNOWN_BUILTIN_ERR_MSG, argv[0]);
|
2012-11-19 00:30:30 +00:00
|
|
|
return STATUS_BUILTIN_ERROR;
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
wcstring_list_t builtin_get_names(void) {
|
2012-02-01 03:47:56 +00:00
|
|
|
wcstring_list_t result;
|
|
|
|
result.reserve(BUILTIN_COUNT);
|
2016-04-19 02:25:12 +00:00
|
|
|
for (size_t i = 0; i < BUILTIN_COUNT; i++) {
|
2012-02-01 03:47:56 +00:00
|
|
|
result.push_back(builtin_datas[i].name);
|
|
|
|
}
|
|
|
|
return result;
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
void builtin_get_names(std::vector<completion_t> *list) {
|
2015-07-28 01:45:47 +00:00
|
|
|
assert(list != NULL);
|
|
|
|
list->reserve(list->size() + BUILTIN_COUNT);
|
2016-04-19 02:25:12 +00:00
|
|
|
for (size_t i = 0; i < BUILTIN_COUNT; i++) {
|
2014-01-07 22:57:58 +00:00
|
|
|
append_completion(list, builtin_datas[i].name);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2012-01-16 16:56:47 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 02:25:12 +00:00
|
|
|
wcstring builtin_get_desc(const wcstring &name) {
|
2012-05-18 02:37:46 +00:00
|
|
|
wcstring result;
|
2012-11-19 00:30:30 +00:00
|
|
|
const builtin_data_t *builtin = builtin_lookup(name);
|
2016-04-19 02:25:12 +00:00
|
|
|
if (builtin) {
|
2012-05-18 02:37:46 +00:00
|
|
|
result = _(builtin->desc);
|
|
|
|
}
|
|
|
|
return result;
|
2005-09-20 13:26:39 +00:00
|
|
|
}
|