mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-13 13:39:02 +00:00
Merge branch 'master' into pager
This commit is contained in:
commit
104cf87b89
100 changed files with 25169 additions and 3767 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -6,6 +6,7 @@ Makefile
|
|||
autom4te.cache/
|
||||
build/
|
||||
command_list.txt
|
||||
command_list_toc.txt
|
||||
confdefs.h
|
||||
config.h
|
||||
config.h.in
|
||||
|
@ -18,7 +19,6 @@ doc_src/commands.hdr
|
|||
doc_src/index.hdr
|
||||
po/*.gmo
|
||||
fish
|
||||
fish.spec
|
||||
fish_indent
|
||||
fish_pager
|
||||
fish_tests
|
||||
|
|
89
Makefile.in
89
Makefile.in
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
#
|
||||
|
||||
#
|
||||
|
@ -20,14 +20,7 @@
|
|||
|
||||
#
|
||||
# Makefile for the fish shell. Can build fish and associated
|
||||
# applications, install them, recalculate dependencies and also create
|
||||
# binary distributions in tar.bz2, tar.gz and rpm formats.
|
||||
#
|
||||
|
||||
#
|
||||
# The fish buildprocess is quite complex. Do not stare directly into
|
||||
# the Makefile. Doing so may cause nausea, dizziness and
|
||||
# hallucinations.
|
||||
# applications, install them, and recalculate dependencies.
|
||||
#
|
||||
|
||||
# Used by docdir
|
||||
|
@ -99,7 +92,7 @@ FISH_OBJS := function.o builtin.o complete.o env.o exec.o expand.o \
|
|||
env_universal.o env_universal_common.o input_common.o event.o \
|
||||
signal.o io.o parse_util.o common.o screen.o path.o autoload.o \
|
||||
parser_keywords.o iothread.o color.o postfork.o \
|
||||
builtin_test.o
|
||||
builtin_test.o parse_tree.o parse_productions.o parse_execution.cpp
|
||||
|
||||
FISH_INDENT_OBJS := fish_indent.o print_help.o common.o \
|
||||
parser_keywords.o wutil.o tokenizer.o
|
||||
|
@ -167,11 +160,6 @@ HDR_FILES := $(HDR_FILES_SRC:.hdr.in=.hdr)
|
|||
# Use a pattern rule so that Make knows to only issue one invocation
|
||||
# per http://www.gnu.org/software/make/manual/make.html#Pattern-Intro
|
||||
|
||||
# Internalized scripts are currently disabled.
|
||||
# For now, we just generate empty arrays.
|
||||
# To generate them again, you would run this:
|
||||
# ./internalize_scripts.py share/functions/*.fish share/completions/*.fish
|
||||
|
||||
|
||||
#
|
||||
# Files containing documentation for external commands.
|
||||
|
@ -186,48 +174,6 @@ HELP_SRC := $(wildcard doc_src/*.txt)
|
|||
|
||||
TEST_IN := $(wildcard tests/test*.in)
|
||||
|
||||
|
||||
#
|
||||
# Files that should be added to the tar archives
|
||||
#
|
||||
|
||||
#
|
||||
# Files in ./doc_src/
|
||||
#
|
||||
|
||||
DOC_SRC_DIR_FILES := $(HDR_FILES_SRC) $(HELP_SRC)
|
||||
|
||||
|
||||
#
|
||||
# Files in ./
|
||||
#
|
||||
|
||||
MAIN_DIR_FILES_UNSORTED := Doxyfile Doxyfile.user Doxyfile.help \
|
||||
Makefile.in configure configure.ac config.h.in install-sh \
|
||||
key_reader.cpp $(MIME_OBJS:.o=.h) \
|
||||
$(MIME_OBJS:.o=.cpp) $(FISH_OBJS:.o=.h) $(BUILTIN_FILES) \
|
||||
$(COMMON_FILES) $(COMMON_FILES:.cpp=.h) $(FISH_OBJS:.o=.cpp) \
|
||||
fish.spec.in INSTALL README user_doc.head.html \
|
||||
ChangeLog config.sub config.guess fish_tests.cpp fish.cpp fish_pager.cpp \
|
||||
fishd.cpp make_vcs_completions.fish $(FISH_INDENT_OBJS:.o=.cpp)
|
||||
|
||||
#
|
||||
# The sorting is not meaningful in itself, but it has the side effect
|
||||
# of removing duplicates, which means there will be fewer warnings
|
||||
# during building.
|
||||
#
|
||||
|
||||
MAIN_DIR_FILES := $(sort $(MAIN_DIR_FILES_UNSORTED))
|
||||
|
||||
|
||||
#
|
||||
# Files in ./tests/
|
||||
#
|
||||
|
||||
TESTS_DIR_FILES := $(TEST_IN) $(TEST_IN:.in=.out) $(TEST_IN:.in=.err) \
|
||||
$(TEST_IN:.in=.status) tests/test.fish tests/gen_output.fish
|
||||
|
||||
|
||||
#
|
||||
# Files in ./share/completions/
|
||||
#
|
||||
|
@ -449,7 +395,7 @@ doc.h: $(HDR_FILES)
|
|||
#
|
||||
|
||||
%.po:messages.pot
|
||||
if test $(HAVE_GETTEXT) = 1;then \
|
||||
if test "$(HAVE_GETTEXT)" = 1;then \
|
||||
if test -f $*.po; then \
|
||||
msgmerge -U --backup=existing $*.po messages.pot;\
|
||||
else \
|
||||
|
@ -463,7 +409,7 @@ doc.h: $(HDR_FILES)
|
|||
#
|
||||
|
||||
messages.pot: *.cpp *.h share/completions/*.fish share/functions/*.fish
|
||||
if test $(HAVE_GETTEXT) = 1;then \
|
||||
if test "$(HAVE_GETTEXT)" = 1; then \
|
||||
xgettext -k_ -kN_ *.cpp *.h -o messages.pot; \
|
||||
if xgettext -j -k_ -kN_ -k--description -LShell share/completions/*.fish share/functions/*.fish -o messages.pot; then true; else \
|
||||
echo "Your xgettext version is too old to build the messages.pot file"\
|
||||
|
@ -508,7 +454,7 @@ share/man: $(HELP_SRC)
|
|||
-mkdir share/man
|
||||
touch share/man
|
||||
-rm -Rf share/man/man1
|
||||
./build_tools/build_documentation.sh Doxyfile.help ./doc_src ./share
|
||||
PROJECT_NUMBER=`echo $(FISH_BUILD_VERSION)| sed "s/-.*//"` ./build_tools/build_documentation.sh Doxyfile.help ./doc_src ./share
|
||||
|
||||
#
|
||||
# The build rules for installing/uninstalling fish
|
||||
|
@ -606,6 +552,8 @@ install-force: all install-translations
|
|||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/man/man1
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools/web_config
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools/web_config/js
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools/web_config/partials
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools/web_config/sample_prompts
|
||||
$(INSTALL) -m 644 etc/config.fish $(DESTDIR)$(sysconfdir)/fish/
|
||||
$(INSTALL) -m 644 share/config.fish $(DESTDIR)$(datadir)/fish/
|
||||
|
@ -629,6 +577,14 @@ install-force: all install-translations
|
|||
$(INSTALL) -m 644 $$i $(DESTDIR)$(datadir)/fish/tools/web_config/; \
|
||||
true; \
|
||||
done;
|
||||
for i in share/tools/web_config/js/*; do\
|
||||
$(INSTALL) -m 644 $$i $(DESTDIR)$(datadir)/fish/tools/web_config/js/; \
|
||||
true; \
|
||||
done;
|
||||
for i in share/tools/web_config/partials/*; do\
|
||||
$(INSTALL) -m 644 $$i $(DESTDIR)$(datadir)/fish/tools/web_config/partials/; \
|
||||
true; \
|
||||
done;
|
||||
for i in share/tools/web_config/sample_prompts/*.fish; do\
|
||||
$(INSTALL) -m 644 $$i $(DESTDIR)$(datadir)/fish/tools/web_config/sample_prompts/; \
|
||||
true; \
|
||||
|
@ -714,7 +670,7 @@ uninstall-legacy: uninstall
|
|||
.PHONY: uninstall-legacy
|
||||
|
||||
install-translations: $(TRANSLATIONS)
|
||||
if test $(HAVE_GETTEXT) = 1; then \
|
||||
if test "$(HAVE_GETTEXT)" = 1; then \
|
||||
for i in $(TRANSLATIONS); do \
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/locale/`basename $$i .gmo`/LC_MESSAGES; \
|
||||
$(INSTALL) -m 644 $$i $(DESTDIR)$(datadir)/locale/`basename $$i .gmo`/LC_MESSAGES/fish.mo; \
|
||||
|
@ -724,7 +680,7 @@ install-translations: $(TRANSLATIONS)
|
|||
.PHONY: install-translations
|
||||
|
||||
uninstall-translations:
|
||||
if test $(HAVE_GETTEXT) = 1; then \
|
||||
if test "$(HAVE_GETTEXT)" = 1; then \
|
||||
for i in $(TRANSLATIONS_SRC); do \
|
||||
rm -f $(DESTDIR)$(datadir)/locale/*/LC_MESSAGES/fish.mo; \
|
||||
done; \
|
||||
|
@ -800,13 +756,6 @@ depend:
|
|||
./config.status
|
||||
.PHONY: depend
|
||||
|
||||
#
|
||||
# Build the RPM spec file.
|
||||
#
|
||||
|
||||
fish.spec: fish.spec.in
|
||||
./config.status
|
||||
|
||||
#
|
||||
# Cleanup targets
|
||||
#
|
||||
|
@ -816,7 +765,6 @@ fish.spec: fish.spec.in
|
|||
#
|
||||
|
||||
distclean: clean
|
||||
rm -f fish.spec
|
||||
rm -f config.status config.log config.h Makefile
|
||||
.PHONY: distclean
|
||||
|
||||
|
@ -832,7 +780,6 @@ distclean: clean
|
|||
|
||||
clean:
|
||||
rm -f *.o doc.h doc.tmp doc_src/*.doxygen doc_src/*.cpp doc_src/*.o doc_src/commands.hdr
|
||||
rm -f $(GENERATED_INTERN_SCRIPT_FILES)
|
||||
rm -f tests/tmp.err tests/tmp.out tests/tmp.status tests/foo.txt
|
||||
rm -f $(PROGRAMS) fish_tests key_reader
|
||||
rm -f command_list.txt command_list_toc.txt toc.txt
|
||||
|
|
|
@ -195,7 +195,6 @@ autoload_function_t *autoload_t::get_autoloaded_function_with_creation(const wcs
|
|||
bool autoload_t::locate_file_and_maybe_load_it(const wcstring &cmd, bool really_load, bool reload, const wcstring_list_t &path_list)
|
||||
{
|
||||
/* Note that we are NOT locked in this function! */
|
||||
size_t i;
|
||||
bool reloaded = 0;
|
||||
|
||||
/* Try using a cached function. If we really want the function to be loaded, require that it be really loaded. If we're not reloading, allow stale functions. */
|
||||
|
@ -276,7 +275,7 @@ bool autoload_t::locate_file_and_maybe_load_it(const wcstring &cmd, bool really_
|
|||
if (! has_script_source)
|
||||
{
|
||||
/* Iterate over path searching for suitable completion files */
|
||||
for (i=0; i<path_list.size(); i++)
|
||||
for (size_t i=0; i<path_list.size(); i++)
|
||||
{
|
||||
wcstring next = path_list.at(i);
|
||||
wcstring path = next + L"/" + cmd + L".fish";
|
||||
|
|
|
@ -86,7 +86,7 @@ done
|
|||
# Input is kept as . because we cd to the input directory beforehand
|
||||
# This prevents doxygen from generating "documentation" for intermediate directories
|
||||
DOXYPARAMS=$(cat <<EOF
|
||||
PROJECT_NUMBER=2.0.0
|
||||
PROJECT_NUMBER=$PROJECT_NUMBER
|
||||
INPUT=.
|
||||
OUTPUT_DIRECTORY=$OUTPUTDIR
|
||||
QUIET=YES
|
||||
|
|
584
builtin.cpp
584
builtin.cpp
|
@ -64,6 +64,7 @@
|
|||
#include "expand.h"
|
||||
#include "path.h"
|
||||
#include "history.h"
|
||||
#include "parse_tree.h"
|
||||
|
||||
/**
|
||||
The default prompt for the read command
|
||||
|
@ -164,7 +165,7 @@ static const io_chain_t *real_io;
|
|||
/**
|
||||
Counts the number of non null pointers in the specified array
|
||||
*/
|
||||
static int builtin_count_args(wchar_t **argv)
|
||||
static int builtin_count_args(const wchar_t * const * argv)
|
||||
{
|
||||
int argc = 1;
|
||||
while (argv[argc] != NULL)
|
||||
|
@ -243,9 +244,6 @@ wcstring builtin_help_get(parser_t &parser, const wchar_t *name)
|
|||
|
||||
static void builtin_print_help(parser_t &parser, const wchar_t *cmd, wcstring &b)
|
||||
{
|
||||
|
||||
int is_short = 0;
|
||||
|
||||
if (&b == &stderr_buffer)
|
||||
{
|
||||
stderr_buffer.append(parser.current_line());
|
||||
|
@ -259,7 +257,7 @@ static void builtin_print_help(parser_t &parser, const wchar_t *cmd, wcstring &b
|
|||
wchar_t *str = wcsdup(h.c_str());
|
||||
if (str)
|
||||
{
|
||||
|
||||
bool is_short = false;
|
||||
if (&b == &stderr_buffer)
|
||||
{
|
||||
|
||||
|
@ -278,7 +276,7 @@ static void builtin_print_help(parser_t &parser, const wchar_t *cmd, wcstring &b
|
|||
int cut=0;
|
||||
int i;
|
||||
|
||||
is_short = 1;
|
||||
is_short = true;
|
||||
|
||||
/*
|
||||
First move down 4 lines
|
||||
|
@ -689,7 +687,7 @@ static int builtin_bind(parser_t &parser, wchar_t **argv)
|
|||
default:
|
||||
{
|
||||
res = STATUS_BUILTIN_ERROR;
|
||||
append_format(stderr_buffer, _(L"%ls: Expected zero or two parameters, got %d"), argv[0], argc-woptind);
|
||||
append_format(stderr_buffer, _(L"%ls: Expected zero or two parameters, got %d\n"), argv[0], argc-woptind);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -737,7 +735,6 @@ static int builtin_block(parser_t &parser, wchar_t **argv)
|
|||
int scope=UNSET;
|
||||
int erase = 0;
|
||||
int argc=builtin_count_args(argv);
|
||||
int type = (1<<EVENT_ANY);
|
||||
|
||||
woptind=0;
|
||||
|
||||
|
@ -831,29 +828,34 @@ static int builtin_block(parser_t &parser, wchar_t **argv)
|
|||
}
|
||||
else
|
||||
{
|
||||
block_t *block=parser.current_block;
|
||||
size_t block_idx = 0;
|
||||
block_t *block = parser.block_at_index(block_idx);
|
||||
|
||||
event_blockage_t eb = {};
|
||||
eb.typemask = type;
|
||||
eb.typemask = (1<<EVENT_ANY);
|
||||
|
||||
switch (scope)
|
||||
{
|
||||
case LOCAL:
|
||||
{
|
||||
if (!block->outer)
|
||||
block=0;
|
||||
// If this is the outermost block, then we're global
|
||||
if (block_idx + 1 >= parser.block_count())
|
||||
{
|
||||
block = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GLOBAL:
|
||||
{
|
||||
block=0;
|
||||
block=NULL;
|
||||
}
|
||||
case UNSET:
|
||||
{
|
||||
while (block &&
|
||||
block->type() != FUNCTION_CALL &&
|
||||
block->type() != FUNCTION_CALL_NO_SHADOW)
|
||||
block = block->outer;
|
||||
while (block != NULL && block->type() != FUNCTION_CALL && block->type() != FUNCTION_CALL_NO_SHADOW)
|
||||
{
|
||||
// Set it in function scope
|
||||
block = parser.block_at_index(++block_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (block)
|
||||
|
@ -1036,20 +1038,22 @@ static int builtin_emit(parser_t &parser, wchar_t **argv)
|
|||
static int builtin_generic(parser_t &parser, wchar_t **argv)
|
||||
{
|
||||
int argc=builtin_count_args(argv);
|
||||
|
||||
/* Hackish - if we have no arguments other than the command, we are a "naked invocation" and we just print help */
|
||||
if (argc == 1)
|
||||
{
|
||||
builtin_print_help(parser, argv[0], stdout_buffer);
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
woptind=0;
|
||||
|
||||
static const struct woption
|
||||
long_options[] =
|
||||
{
|
||||
{
|
||||
L"help", no_argument, 0, 'h'
|
||||
}
|
||||
,
|
||||
{
|
||||
0, 0, 0, 0
|
||||
}
|
||||
}
|
||||
;
|
||||
{ L"help", no_argument, 0, 'h' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
while (1)
|
||||
{
|
||||
|
@ -1746,12 +1750,325 @@ static int builtin_pwd(parser_t &parser, wchar_t **argv)
|
|||
}
|
||||
}
|
||||
|
||||
/* This is nearly identical to builtin_function, and is intended to be the successor (with no block manipulation, no function/end split) */
|
||||
int define_function(parser_t &parser, const wcstring_list_t &c_args, const wcstring &contents, wcstring *out_err)
|
||||
{
|
||||
assert(out_err != NULL);
|
||||
|
||||
/* wgetopt expects 'function' as the first argument. Make a new wcstring_list with that property. */
|
||||
wcstring_list_t args;
|
||||
args.push_back(L"function");
|
||||
args.insert(args.end(), c_args.begin(), c_args.end());
|
||||
|
||||
/* Hackish const_cast matches the one in builtin_run */
|
||||
const null_terminated_array_t<wchar_t> argv_array(args);
|
||||
wchar_t **argv = const_cast<wchar_t **>(argv_array.get());
|
||||
|
||||
int argc = builtin_count_args(argv);
|
||||
int res=STATUS_BUILTIN_OK;
|
||||
wchar_t *desc=0;
|
||||
std::vector<event_t> events;
|
||||
std::auto_ptr<wcstring_list_t> named_arguments(NULL);
|
||||
|
||||
wchar_t *name = 0;
|
||||
bool shadows = true;
|
||||
|
||||
woptind=0;
|
||||
|
||||
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"help", no_argument, 0, 'h' },
|
||||
{ L"argument-names", no_argument, 0, 'a' },
|
||||
{ L"no-scope-shadowing", no_argument, 0, 'S' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
while (1 && (!res))
|
||||
{
|
||||
int opt_index = 0;
|
||||
|
||||
int opt = wgetopt_long(argc,
|
||||
argv,
|
||||
L"d:s:j:p:v:e:haS",
|
||||
long_options,
|
||||
&opt_index);
|
||||
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);
|
||||
|
||||
res = 1;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
desc=woptarg;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
{
|
||||
int sig = wcs2sig(woptarg);
|
||||
|
||||
if (sig < 0)
|
||||
{
|
||||
append_format(*out_err,
|
||||
_(L"%ls: Unknown signal '%ls'\n"),
|
||||
argv[0],
|
||||
woptarg);
|
||||
res=1;
|
||||
break;
|
||||
}
|
||||
events.push_back(event_t::signal_event(sig));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'v':
|
||||
{
|
||||
if (wcsvarname(woptarg))
|
||||
{
|
||||
append_format(*out_err,
|
||||
_(L"%ls: Invalid variable name '%ls'\n"),
|
||||
argv[0],
|
||||
woptarg);
|
||||
res=STATUS_BUILTIN_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
events.push_back(event_t::variable_event(woptarg));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case 'e':
|
||||
{
|
||||
events.push_back(event_t::generic_event(woptarg));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'j':
|
||||
case 'p':
|
||||
{
|
||||
pid_t pid;
|
||||
wchar_t *end;
|
||||
event_t e(EVENT_ANY);
|
||||
|
||||
if ((opt == 'j') &&
|
||||
(wcscasecmp(woptarg, L"caller") == 0))
|
||||
{
|
||||
int job_id = -1;
|
||||
|
||||
if (is_subshell)
|
||||
{
|
||||
size_t block_idx = 0;
|
||||
|
||||
/* Find the outermost substitution block */
|
||||
for (block_idx = 0; ; block_idx++)
|
||||
{
|
||||
const block_t *b = parser.block_at_index(block_idx);
|
||||
if (b == NULL || b->type() == SUBST)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Go one step beyond that, to get to the caller */
|
||||
const block_t *caller_block = parser.block_at_index(block_idx + 1);
|
||||
if (caller_block != NULL && caller_block->job != NULL)
|
||||
{
|
||||
job_id = caller_block->job->job_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (job_id == -1)
|
||||
{
|
||||
append_format(*out_err,
|
||||
_(L"%ls: Cannot find calling job for event handler\n"),
|
||||
argv[0]);
|
||||
res=1;
|
||||
}
|
||||
else
|
||||
{
|
||||
e.type = EVENT_JOB_ID;
|
||||
e.param1.job_id = job_id;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
errno = 0;
|
||||
pid = fish_wcstoi(woptarg, &end, 10);
|
||||
if (errno || !end || *end)
|
||||
{
|
||||
append_format(*out_err,
|
||||
_(L"%ls: Invalid process id %ls\n"),
|
||||
argv[0],
|
||||
woptarg);
|
||||
res=1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
e.type = EVENT_EXIT;
|
||||
e.param1.pid = (opt=='j'?-1:1)*abs(pid);
|
||||
}
|
||||
if (res)
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
else
|
||||
{
|
||||
events.push_back(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'a':
|
||||
if (named_arguments.get() == NULL)
|
||||
named_arguments.reset(new wcstring_list_t);
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
shadows = 0;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
builtin_print_help(parser, argv[0], stdout_buffer);
|
||||
return STATUS_BUILTIN_OK;
|
||||
|
||||
case '?':
|
||||
builtin_unknown_option(parser, argv[0], argv[woptind-1]);
|
||||
res = 1;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!res)
|
||||
{
|
||||
|
||||
if (argc == woptind)
|
||||
{
|
||||
append_format(*out_err,
|
||||
_(L"%ls: Expected function name\n"),
|
||||
argv[0]);
|
||||
res=1;
|
||||
}
|
||||
else if (wcsfuncname(argv[woptind]))
|
||||
{
|
||||
append_format(*out_err,
|
||||
_(L"%ls: Illegal function name '%ls'\n"),
|
||||
argv[0],
|
||||
argv[woptind]);
|
||||
|
||||
res=1;
|
||||
}
|
||||
else if (parser_keywords_is_reserved(argv[woptind]))
|
||||
{
|
||||
|
||||
append_format(*out_err,
|
||||
_(L"%ls: The name '%ls' is reserved,\nand can not be used as a function name\n"),
|
||||
argv[0],
|
||||
argv[woptind]);
|
||||
|
||||
res=1;
|
||||
}
|
||||
else if (! wcslen(argv[woptind]))
|
||||
{
|
||||
append_format(*out_err, _(L"%ls: No function name given\n"), argv[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
name = argv[woptind++];
|
||||
|
||||
if (named_arguments.get())
|
||||
{
|
||||
while (woptind < argc)
|
||||
{
|
||||
if (wcsvarname(argv[woptind]))
|
||||
{
|
||||
append_format(*out_err,
|
||||
_(L"%ls: Invalid variable name '%ls'\n"),
|
||||
argv[0],
|
||||
argv[woptind]);
|
||||
res = STATUS_BUILTIN_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
named_arguments->push_back(argv[woptind++]);
|
||||
}
|
||||
}
|
||||
else if (woptind != argc)
|
||||
{
|
||||
append_format(*out_err,
|
||||
_(L"%ls: Expected one argument, got %d\n"),
|
||||
argv[0],
|
||||
argc);
|
||||
res=1;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (res)
|
||||
{
|
||||
builtin_print_help(parser, argv[0], *out_err);
|
||||
}
|
||||
else
|
||||
{
|
||||
function_data_t d;
|
||||
|
||||
d.name = name;
|
||||
if (desc)
|
||||
d.description = desc;
|
||||
d.events.swap(events);
|
||||
d.shadows = shadows;
|
||||
if (named_arguments.get())
|
||||
d.named_arguments.swap(*named_arguments);
|
||||
|
||||
for (size_t i=0; i<d.events.size(); i++)
|
||||
{
|
||||
event_t &e = d.events.at(i);
|
||||
e.function_name = d.name;
|
||||
}
|
||||
|
||||
d.definition = contents.c_str();
|
||||
|
||||
// TODO: fix def_offset inside function_add
|
||||
function_add(d, parser);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
The function builtin, used for providing subroutines.
|
||||
It calls various functions from function.c to perform any heavy lifting.
|
||||
*/
|
||||
static int builtin_function(parser_t &parser, wchar_t **argv)
|
||||
{
|
||||
/* Hack hack hack - with the new parser, this is only invoked for help */
|
||||
if (parser_use_ast())
|
||||
{
|
||||
builtin_print_help(parser, argv[0], stdout_buffer);
|
||||
return STATUS_BUILTIN_OK;
|
||||
}
|
||||
|
||||
int argc = builtin_count_args(argv);
|
||||
int res=STATUS_BUILTIN_OK;
|
||||
wchar_t *desc=0;
|
||||
|
@ -1863,18 +2180,21 @@ static int builtin_function(parser_t &parser, wchar_t **argv)
|
|||
|
||||
if (is_subshell)
|
||||
{
|
||||
block_t *b = parser.current_block;
|
||||
size_t block_idx = 0;
|
||||
|
||||
while (b && (b->type() != SUBST))
|
||||
b = b->outer;
|
||||
|
||||
if (b)
|
||||
/* Find the outermost substitution block */
|
||||
for (block_idx = 0; ; block_idx++)
|
||||
{
|
||||
b=b->outer;
|
||||
const block_t *b = parser.block_at_index(block_idx);
|
||||
if (b == NULL || b->type() == SUBST)
|
||||
break;
|
||||
}
|
||||
if (b->job)
|
||||
|
||||
/* Go one step beyond that, to get to the caller */
|
||||
const block_t *caller_block = parser.block_at_index(block_idx + 1);
|
||||
if (caller_block != NULL && caller_block->job != NULL)
|
||||
{
|
||||
job_id = b->job->job_id;
|
||||
job_id = caller_block->job->job_id;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1974,6 +2294,10 @@ static int builtin_function(parser_t &parser, wchar_t **argv)
|
|||
|
||||
res=1;
|
||||
}
|
||||
else if (! wcslen(argv[woptind]))
|
||||
{
|
||||
append_format(stderr_buffer, _(L"%ls: No function name given\n"), argv[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
@ -2058,8 +2382,8 @@ static int builtin_function(parser_t &parser, wchar_t **argv)
|
|||
}
|
||||
}
|
||||
|
||||
parser.current_block->tok_pos = parser.get_pos();
|
||||
parser.current_block->skip = 1;
|
||||
parser.current_block()->tok_pos = parser.get_pos();
|
||||
parser.current_block()->skip = 1;
|
||||
|
||||
return STATUS_BUILTIN_OK;
|
||||
}
|
||||
|
@ -2712,7 +3036,7 @@ static int builtin_status(parser_t &parser, wchar_t **argv)
|
|||
|
||||
case STACK_TRACE:
|
||||
{
|
||||
parser.stack_trace(parser.current_block, stdout_buffer);
|
||||
parser.stack_trace(0, stdout_buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2727,7 +3051,7 @@ static int builtin_status(parser_t &parser, wchar_t **argv)
|
|||
job_control_mode==JOB_CONTROL_INTERACTIVE?_(L"Only on interactive jobs"):
|
||||
(job_control_mode==JOB_CONTROL_NONE ? _(L"Never") : _(L"Always")));
|
||||
|
||||
parser.stack_trace(parser.current_block, stdout_buffer);
|
||||
parser.stack_trace(0, stdout_buffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -3010,10 +3334,7 @@ static int builtin_source(parser_t &parser, wchar_t ** argv)
|
|||
|
||||
argc = builtin_count_args(argv);
|
||||
|
||||
const wchar_t *fn;
|
||||
const wchar_t *fn_intern;
|
||||
|
||||
|
||||
const wchar_t *fn, *fn_intern;
|
||||
|
||||
if (argc < 2 || (wcscmp(argv[1], L"-") == 0))
|
||||
{
|
||||
|
@ -3046,19 +3367,8 @@ static int builtin_source(parser_t &parser, wchar_t ** argv)
|
|||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
fn = wrealpath(argv[1], NULL);
|
||||
|
||||
if (!fn)
|
||||
{
|
||||
fn_intern = intern(argv[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
fn_intern = intern(fn);
|
||||
free((void *)fn);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
parser.push_block(new source_block_t(fn_intern));
|
||||
reader_push_current_filename(fn_intern);
|
||||
|
@ -3361,6 +3671,12 @@ static int builtin_for(parser_t &parser, wchar_t **argv)
|
|||
int argc = builtin_count_args(argv);
|
||||
int res=STATUS_BUILTIN_ERROR;
|
||||
|
||||
/* Hackish - if we have no arguments other than the command, we are a "naked invocation" and we just print help */
|
||||
if (argc == 1)
|
||||
{
|
||||
builtin_print_help(parser, argv[0], stdout_buffer);
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
if (argc < 3)
|
||||
{
|
||||
|
@ -3413,19 +3729,19 @@ static int builtin_for(parser_t &parser, wchar_t **argv)
|
|||
}
|
||||
else
|
||||
{
|
||||
parser.current_block->skip=1;
|
||||
parser.current_block()->skip=1;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
The begin builtin. Creates a nex block.
|
||||
The begin builtin. Creates a new block.
|
||||
*/
|
||||
static int builtin_begin(parser_t &parser, wchar_t **argv)
|
||||
{
|
||||
parser.push_block(new scope_block_t(BEGIN));
|
||||
parser.current_block->tok_pos = parser.get_pos();
|
||||
parser.current_block()->tok_pos = parser.get_pos();
|
||||
return proc_get_last_status();
|
||||
}
|
||||
|
||||
|
@ -3437,7 +3753,7 @@ static int builtin_begin(parser_t &parser, wchar_t **argv)
|
|||
*/
|
||||
static int builtin_end(parser_t &parser, wchar_t **argv)
|
||||
{
|
||||
if (!parser.current_block->outer)
|
||||
if (! parser.block_at_index(1))
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
_(L"%ls: Not inside of block\n"),
|
||||
|
@ -3455,7 +3771,8 @@ static int builtin_end(parser_t &parser, wchar_t **argv)
|
|||
*/
|
||||
bool kill_block = true;
|
||||
|
||||
switch (parser.current_block->type())
|
||||
block_t * const current_block = parser.current_block();
|
||||
switch (current_block->type())
|
||||
{
|
||||
case WHILE:
|
||||
{
|
||||
|
@ -3463,13 +3780,13 @@ static int builtin_end(parser_t &parser, wchar_t **argv)
|
|||
If this is a while loop, we rewind the loop unless
|
||||
it's the last lap, in which case we continue.
|
||||
*/
|
||||
if (!(parser.current_block->skip && (parser.current_block->loop_status != LOOP_CONTINUE)))
|
||||
if (!(current_block->skip && (current_block->loop_status != LOOP_CONTINUE)))
|
||||
{
|
||||
parser.current_block->loop_status = LOOP_NORMAL;
|
||||
parser.current_block->skip = 0;
|
||||
current_block->loop_status = LOOP_NORMAL;
|
||||
current_block->skip = 0;
|
||||
kill_block = false;
|
||||
parser.set_pos(parser.current_block->tok_pos);
|
||||
while_block_t *blk = static_cast<while_block_t *>(parser.current_block);
|
||||
parser.set_pos(current_block->tok_pos);
|
||||
while_block_t *blk = static_cast<while_block_t *>(current_block);
|
||||
blk->status = WHILE_TEST_AGAIN;
|
||||
}
|
||||
|
||||
|
@ -3492,9 +3809,9 @@ static int builtin_end(parser_t &parser, wchar_t **argv)
|
|||
/*
|
||||
set loop variable to next element, and rewind to the beginning of the block.
|
||||
*/
|
||||
for_block_t *fb = static_cast<for_block_t *>(parser.current_block);
|
||||
for_block_t *fb = static_cast<for_block_t *>(current_block);
|
||||
wcstring_list_t &for_vars = fb->sequence;
|
||||
if (parser.current_block->loop_status == LOOP_BREAK)
|
||||
if (current_block->loop_status == LOOP_BREAK)
|
||||
{
|
||||
for_vars.clear();
|
||||
}
|
||||
|
@ -3505,18 +3822,18 @@ static int builtin_end(parser_t &parser, wchar_t **argv)
|
|||
for_vars.pop_back();
|
||||
const wcstring &for_variable = fb->variable;
|
||||
env_set(for_variable, val.c_str(), ENV_LOCAL);
|
||||
parser.current_block->loop_status = LOOP_NORMAL;
|
||||
parser.current_block->skip = 0;
|
||||
current_block->loop_status = LOOP_NORMAL;
|
||||
current_block->skip = 0;
|
||||
|
||||
kill_block = false;
|
||||
parser.set_pos(parser.current_block->tok_pos);
|
||||
parser.set_pos(current_block->tok_pos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FUNCTION_DEF:
|
||||
{
|
||||
function_def_block_t *fdb = static_cast<function_def_block_t *>(parser.current_block);
|
||||
function_def_block_t *fdb = static_cast<function_def_block_t *>(current_block);
|
||||
function_data_t &d = fdb->function_data;
|
||||
|
||||
if (d.name.empty())
|
||||
|
@ -3535,8 +3852,8 @@ static int builtin_end(parser_t &parser, wchar_t **argv)
|
|||
for the specified function
|
||||
*/
|
||||
|
||||
wchar_t *def = wcsndup(parser.get_buffer()+parser.current_block->tok_pos,
|
||||
parser.get_job_pos()-parser.current_block->tok_pos);
|
||||
wchar_t *def = wcsndup(parser.get_buffer()+current_block->tok_pos,
|
||||
parser.get_job_pos()-current_block->tok_pos);
|
||||
d.definition = def;
|
||||
|
||||
function_add(d, parser);
|
||||
|
@ -3569,9 +3886,9 @@ static int builtin_else(parser_t &parser, wchar_t **argv)
|
|||
{
|
||||
bool block_ok = false;
|
||||
if_block_t *if_block = NULL;
|
||||
if (parser.current_block != NULL && parser.current_block->type() == IF)
|
||||
if (parser.current_block() != NULL && parser.current_block()->type() == IF)
|
||||
{
|
||||
if_block = static_cast<if_block_t *>(parser.current_block);
|
||||
if_block = static_cast<if_block_t *>(parser.current_block());
|
||||
/* Ensure that we're past IF but not up to an ELSE */
|
||||
if (if_block->if_expr_evaluated && ! if_block->else_evaluated)
|
||||
{
|
||||
|
@ -3612,7 +3929,6 @@ static int builtin_break_continue(parser_t &parser, wchar_t **argv)
|
|||
int is_break = (wcscmp(argv[0],L"break")==0);
|
||||
int argc = builtin_count_args(argv);
|
||||
|
||||
block_t *b = parser.current_block;
|
||||
|
||||
if (argc != 1)
|
||||
{
|
||||
|
@ -3625,15 +3941,16 @@ static int builtin_break_continue(parser_t &parser, wchar_t **argv)
|
|||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
|
||||
while ((b != 0) &&
|
||||
(b->type() != WHILE) &&
|
||||
(b->type() != FOR))
|
||||
/* Find the index of the enclosing for or while loop. Recall that incrementing loop_idx goes 'up' to outer blocks */
|
||||
size_t loop_idx;
|
||||
for (loop_idx = 0; loop_idx < parser.block_count(); loop_idx++)
|
||||
{
|
||||
b = b->outer;
|
||||
const block_t *b = parser.block_at_index(loop_idx);
|
||||
if (b->type() == WHILE || b->type() == FOR)
|
||||
break;
|
||||
}
|
||||
|
||||
if (b == 0)
|
||||
if (loop_idx >= parser.block_count())
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
_(L"%ls: Not inside of loop\n"),
|
||||
|
@ -3642,15 +3959,17 @@ static int builtin_break_continue(parser_t &parser, wchar_t **argv)
|
|||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
b = parser.current_block;
|
||||
while ((b->type() != WHILE) &&
|
||||
(b->type() != FOR))
|
||||
/* Skip blocks interior to the loop */
|
||||
size_t block_idx = loop_idx;
|
||||
while (block_idx--)
|
||||
{
|
||||
b->skip=1;
|
||||
b = b->outer;
|
||||
parser.block_at_index(block_idx)->skip = true;
|
||||
}
|
||||
b->skip=1;
|
||||
b->loop_status = is_break?LOOP_BREAK:LOOP_CONTINUE;
|
||||
|
||||
/* 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;
|
||||
return STATUS_BUILTIN_OK;
|
||||
}
|
||||
|
||||
|
@ -3679,8 +3998,6 @@ static int builtin_return(parser_t &parser, wchar_t **argv)
|
|||
int argc = builtin_count_args(argv);
|
||||
int status = proc_get_last_status();
|
||||
|
||||
block_t *b = parser.current_block;
|
||||
|
||||
switch (argc)
|
||||
{
|
||||
case 1:
|
||||
|
@ -3709,15 +4026,16 @@ static int builtin_return(parser_t &parser, wchar_t **argv)
|
|||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
|
||||
while ((b != 0) &&
|
||||
(b->type() != FUNCTION_CALL &&
|
||||
b->type() != FUNCTION_CALL_NO_SHADOW))
|
||||
/* Find the function block */
|
||||
size_t function_block_idx;
|
||||
for (function_block_idx = 0; function_block_idx < parser.block_count(); function_block_idx++)
|
||||
{
|
||||
b = b->outer;
|
||||
const block_t *b = parser.block_at_index(function_block_idx);
|
||||
if (b->type() == FUNCTION_CALL || b->type() == FUNCTION_CALL_NO_SHADOW)
|
||||
break;
|
||||
}
|
||||
|
||||
if (b == 0)
|
||||
if (function_block_idx >= parser.block_count())
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
_(L"%ls: Not inside of function\n"),
|
||||
|
@ -3726,16 +4044,14 @@ static int builtin_return(parser_t &parser, wchar_t **argv)
|
|||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
b = parser.current_block;
|
||||
while ((b->type() != FUNCTION_CALL &&
|
||||
b->type() != FUNCTION_CALL_NO_SHADOW))
|
||||
/* Skip everything up to (and then including) the function block */
|
||||
for (size_t i=0; i < function_block_idx; i++)
|
||||
{
|
||||
block_t *b = parser.block_at_index(i);
|
||||
b->mark_as_fake();
|
||||
b->skip=1;
|
||||
b = b->outer;
|
||||
b->skip = true;
|
||||
}
|
||||
b->skip=1;
|
||||
|
||||
parser.block_at_index(function_block_idx)->skip = true;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -3748,6 +4064,13 @@ static int builtin_switch(parser_t &parser, wchar_t **argv)
|
|||
int res=STATUS_BUILTIN_OK;
|
||||
int argc = builtin_count_args(argv);
|
||||
|
||||
/* Hackish - if we have no arguments other than the command, we are a "naked invocation" and we just print help */
|
||||
if (argc == 1)
|
||||
{
|
||||
builtin_print_help(parser, argv[0], stdout_buffer);
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
if (argc != 2)
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
|
@ -3762,7 +4085,7 @@ static int builtin_switch(parser_t &parser, wchar_t **argv)
|
|||
else
|
||||
{
|
||||
parser.push_block(new switch_block_t(argv[1]));
|
||||
parser.current_block->skip=1;
|
||||
parser.current_block()->skip=1;
|
||||
res = proc_get_last_status();
|
||||
}
|
||||
|
||||
|
@ -3779,7 +4102,7 @@ static int builtin_case(parser_t &parser, wchar_t **argv)
|
|||
int i;
|
||||
wchar_t *unescaped=0;
|
||||
|
||||
if (parser.current_block->type() != SWITCH)
|
||||
if (parser.current_block()->type() != SWITCH)
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
_(L"%ls: 'case' command while not in switch block\n"),
|
||||
|
@ -3788,8 +4111,8 @@ static int builtin_case(parser_t &parser, wchar_t **argv)
|
|||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
parser.current_block->skip = 1;
|
||||
switch_block_t *sb = static_cast<switch_block_t *>(parser.current_block);
|
||||
parser.current_block()->skip = 1;
|
||||
switch_block_t *sb = static_cast<switch_block_t *>(parser.current_block());
|
||||
if (sb->switch_taken)
|
||||
{
|
||||
return proc_get_last_status();
|
||||
|
@ -3806,7 +4129,7 @@ static int builtin_case(parser_t &parser, wchar_t **argv)
|
|||
|
||||
if (match)
|
||||
{
|
||||
parser.current_block->skip = 0;
|
||||
parser.current_block()->skip = 0;
|
||||
sb->switch_taken = true;
|
||||
break;
|
||||
}
|
||||
|
@ -3952,6 +4275,49 @@ static int builtin_history(parser_t &parser, wchar_t **argv)
|
|||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
#pragma mark Simulator
|
||||
|
||||
int builtin_parse(parser_t &parser, wchar_t **argv)
|
||||
{
|
||||
struct sigaction act;
|
||||
sigemptyset(& act.sa_mask);
|
||||
act.sa_flags=0;
|
||||
act.sa_handler=SIG_DFL;
|
||||
sigaction(SIGINT, &act, 0);
|
||||
|
||||
std::vector<char> txt;
|
||||
for (;;)
|
||||
{
|
||||
char buff[256];
|
||||
ssize_t amt = read_loop(builtin_stdin, buff, sizeof buff);
|
||||
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;
|
||||
parse_error_list_t errors;
|
||||
bool success = parse_tree_from_string(src, parse_flag_none, &parse_tree, &errors, true);
|
||||
if (! success)
|
||||
{
|
||||
stdout_buffer.append(L"Parsing failed:\n");
|
||||
for (size_t i=0; i < errors.size(); i++)
|
||||
{
|
||||
stdout_buffer.append(errors.at(i).describe(src));
|
||||
stdout_buffer.push_back(L'\n');
|
||||
}
|
||||
|
||||
stdout_buffer.append(L"(Reparsed with continue after error)\n");
|
||||
parse_tree.clear();
|
||||
errors.clear();
|
||||
parse_tree_from_string(src, parse_flag_continue_after_error, &parse_tree, &errors, true);
|
||||
}
|
||||
const wcstring dump = parse_dump_tree(parse_tree, src);
|
||||
stdout_buffer.append(dump);
|
||||
}
|
||||
return STATUS_BUILTIN_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
END OF BUILTIN COMMANDS
|
||||
|
@ -3967,6 +4333,7 @@ static int builtin_history(parser_t &parser, wchar_t **argv)
|
|||
static const builtin_data_t builtin_datas[]=
|
||||
{
|
||||
{ L"[", &builtin_test, N_(L"Test a condition") },
|
||||
{ L"__fish_parse", &builtin_parse, N_(L"Try out the new parser") },
|
||||
{ L"and", &builtin_generic, N_(L"Execute command if previous command suceeded") },
|
||||
{ L"begin", &builtin_begin, N_(L"Create a block of code") },
|
||||
{ L"bg", &builtin_bg, N_(L"Send job to background") },
|
||||
|
@ -4111,7 +4478,7 @@ void builtin_get_names(std::vector<completion_t> &list)
|
|||
{
|
||||
for (size_t i=0; i < BUILTIN_COUNT; i++)
|
||||
{
|
||||
list.push_back(completion_t(builtin_datas[i].name));
|
||||
append_completion(list, builtin_datas[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4158,4 +4525,3 @@ void builtin_pop_io(parser_t &parser)
|
|||
builtin_stdin = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -176,7 +176,10 @@ const wchar_t *builtin_complete_get_temporary_buffer();
|
|||
Run the __fish_print_help function to obtain the help information
|
||||
for the specified command.
|
||||
*/
|
||||
|
||||
wcstring builtin_help_get(parser_t &parser, const wchar_t *cmd);
|
||||
|
||||
/** Defines a function, like builtin_function. Returns 0 on success. args should NOT contain 'function' as the first argument. */
|
||||
int define_function(parser_t &parser, const wcstring_list_t &args, const wcstring &contents, wcstring *out_err);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -143,17 +143,13 @@ static void write_part(const wchar_t *begin,
|
|||
int cut_at_cursor,
|
||||
int tokenize)
|
||||
{
|
||||
wcstring out;
|
||||
wchar_t *buff;
|
||||
size_t pos;
|
||||
|
||||
pos = get_cursor_pos()-(begin-get_buffer());
|
||||
size_t pos = get_cursor_pos()-(begin-get_buffer());
|
||||
|
||||
if (tokenize)
|
||||
{
|
||||
buff = wcsndup(begin, end-begin);
|
||||
wchar_t *buff = wcsndup(begin, end-begin);
|
||||
// fwprintf( stderr, L"Subshell: %ls, end char %lc\n", buff, *end );
|
||||
out.clear();
|
||||
wcstring out;
|
||||
tokenizer_t tok(buff, TOK_ACCEPT_UNFINISHED);
|
||||
for (; tok_has_next(&tok); tok_next(&tok))
|
||||
{
|
||||
|
|
|
@ -290,7 +290,6 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
|
|||
int result_mode=SHARED;
|
||||
int remove = 0;
|
||||
int authoritative = -1;
|
||||
int flags = COMPLETE_AUTO_SPACE;
|
||||
|
||||
wcstring short_opt;
|
||||
wcstring_list_t gnu_opt, old_opt;
|
||||
|
@ -497,15 +496,19 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
|
|||
{
|
||||
if (condition && wcslen(condition))
|
||||
{
|
||||
if (parser.test(condition))
|
||||
const wcstring condition_string = condition;
|
||||
parse_error_list_t errors;
|
||||
if (parse_util_detect_errors(condition_string, &errors))
|
||||
{
|
||||
append_format(stderr_buffer,
|
||||
L"%ls: Condition '%ls' contained a syntax error\n",
|
||||
L"%ls: Condition '%ls' contained a syntax error",
|
||||
argv[0],
|
||||
condition);
|
||||
|
||||
parser.test(condition, NULL, &stderr_buffer, argv[0]);
|
||||
|
||||
for (size_t i=0; i < errors.size(); i++)
|
||||
{
|
||||
append_format(stderr_buffer, L"\n%s: ", argv[0]);
|
||||
stderr_buffer.append(errors.at(i).describe(condition_string));
|
||||
}
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
|
@ -596,6 +599,8 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
|
|||
}
|
||||
else
|
||||
{
|
||||
int flags = COMPLETE_AUTO_SPACE;
|
||||
|
||||
if (remove)
|
||||
{
|
||||
builtin_complete_remove(cmd,
|
||||
|
|
|
@ -164,7 +164,6 @@ static int builtin_jobs(parser_t &parser, wchar_t **argv)
|
|||
int found=0;
|
||||
int mode=JOBS_DEFAULT;
|
||||
int print_last = 0;
|
||||
const job_t *j;
|
||||
|
||||
argc = builtin_count_args(argv);
|
||||
woptind=0;
|
||||
|
@ -305,7 +304,7 @@ static int builtin_jobs(parser_t &parser, wchar_t **argv)
|
|||
return 1;
|
||||
}
|
||||
|
||||
j = job_get_from_pid(pid);
|
||||
const job_t *j = job_get_from_pid(pid);
|
||||
|
||||
if (j && !job_is_completed(j))
|
||||
{
|
||||
|
|
|
@ -697,7 +697,6 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
|
|||
/*
|
||||
Slice mode
|
||||
*/
|
||||
size_t idx_count, val_count;
|
||||
std::vector<long> indexes;
|
||||
wcstring_list_t result;
|
||||
|
||||
|
@ -714,8 +713,8 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
|
|||
break;
|
||||
}
|
||||
|
||||
val_count = argc-woptind-1;
|
||||
idx_count = indexes.size();
|
||||
size_t idx_count = indexes.size();
|
||||
size_t val_count = argc-woptind-1;
|
||||
|
||||
if (!erase)
|
||||
{
|
||||
|
|
149
common.cpp
149
common.cpp
|
@ -105,7 +105,7 @@ void show_stackframe()
|
|||
return;
|
||||
|
||||
void *trace[32];
|
||||
int i, trace_size = 0;
|
||||
int trace_size = 0;
|
||||
|
||||
trace_size = backtrace(trace, 32);
|
||||
char **messages = backtrace_symbols(trace, trace_size);
|
||||
|
@ -113,7 +113,7 @@ void show_stackframe()
|
|||
if (messages)
|
||||
{
|
||||
debug(0, L"Backtrace:");
|
||||
for (i=0; i<trace_size; i++)
|
||||
for (int i=0; i<trace_size; i++)
|
||||
{
|
||||
fwprintf(stderr, L"%s\n", messages[i]);
|
||||
}
|
||||
|
@ -508,7 +508,7 @@ const wchar_t *wcsfuncname(const wchar_t *str)
|
|||
}
|
||||
|
||||
|
||||
int wcsvarchr(wchar_t chr)
|
||||
bool wcsvarchr(wchar_t chr)
|
||||
{
|
||||
return iswalnum(chr) || chr == L'_';
|
||||
}
|
||||
|
@ -761,7 +761,7 @@ void debug_safe(int level, const char *msg, const char *param1, const char *para
|
|||
errno = errno_old;
|
||||
}
|
||||
|
||||
void format_long_safe(char buff[128], long val)
|
||||
void format_long_safe(char buff[64], long val)
|
||||
{
|
||||
if (val == 0)
|
||||
{
|
||||
|
@ -795,7 +795,7 @@ void format_long_safe(char buff[128], long val)
|
|||
}
|
||||
}
|
||||
|
||||
void format_long_safe(wchar_t buff[128], long val)
|
||||
void format_long_safe(wchar_t buff[64], long val)
|
||||
{
|
||||
if (val == 0)
|
||||
{
|
||||
|
@ -830,19 +830,18 @@ void format_long_safe(wchar_t buff[128], long val)
|
|||
|
||||
void write_screen(const wcstring &msg, wcstring &buff)
|
||||
{
|
||||
const wchar_t *start, *pos;
|
||||
int line_width = 0;
|
||||
int tok_width = 0;
|
||||
int screen_width = common_get_width();
|
||||
|
||||
if (screen_width)
|
||||
{
|
||||
start = pos = msg.c_str();
|
||||
const wchar_t *start = msg.c_str();
|
||||
const wchar_t *pos = start;
|
||||
while (1)
|
||||
{
|
||||
int overflow = 0;
|
||||
|
||||
tok_width=0;
|
||||
int tok_width=0;
|
||||
|
||||
/*
|
||||
Tokenize on whitespace, and also calculate the width of the token
|
||||
|
@ -917,61 +916,28 @@ void write_screen(const wcstring &msg, wcstring &buff)
|
|||
buff.push_back(L'\n');
|
||||
}
|
||||
|
||||
/**
|
||||
Perform string escaping of a strinng by only quoting it. Assumes
|
||||
the string has already been checked for characters that can not be
|
||||
escaped this way.
|
||||
*/
|
||||
static wchar_t *escape_simple(const wchar_t *in)
|
||||
/* Escape a string, storing the result in out_str */
|
||||
static void escape_string_internal(const wchar_t *orig_in, size_t in_len, wcstring *out_str, escape_flags_t flags)
|
||||
{
|
||||
wchar_t *out;
|
||||
size_t len = wcslen(in);
|
||||
out = (wchar_t *)malloc(sizeof(wchar_t)*(len+3));
|
||||
if (!out)
|
||||
DIE_MEM();
|
||||
|
||||
out[0] = L'\'';
|
||||
wcscpy(&out[1], in);
|
||||
out[len+1]=L'\'';
|
||||
out[len+2]=0;
|
||||
return out;
|
||||
}
|
||||
|
||||
wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
|
||||
{
|
||||
const wchar_t *in = in_orig;
|
||||
assert(orig_in != NULL);
|
||||
|
||||
const wchar_t *in = orig_in;
|
||||
bool escape_all = !!(flags & ESCAPE_ALL);
|
||||
bool no_quoted = !!(flags & ESCAPE_NO_QUOTED);
|
||||
bool no_tilde = !!(flags & ESCAPE_NO_TILDE);
|
||||
|
||||
wchar_t *out;
|
||||
wchar_t *pos;
|
||||
|
||||
int need_escape=0;
|
||||
int need_complex_escape=0;
|
||||
|
||||
if (!in)
|
||||
/* Avoid dereferencing all over the place */
|
||||
wcstring &out = *out_str;
|
||||
|
||||
if (!no_quoted && in_len == 0)
|
||||
{
|
||||
debug(0, L"%s called with null input", __func__);
|
||||
FATAL_EXIT();
|
||||
out.assign(L"''");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!no_quoted && (wcslen(in) == 0))
|
||||
{
|
||||
out = wcsdup(L"''");
|
||||
if (!out)
|
||||
DIE_MEM();
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
out = (wchar_t *)malloc(sizeof(wchar_t)*(wcslen(in)*4 + 1));
|
||||
pos = out;
|
||||
|
||||
if (!out)
|
||||
DIE_MEM();
|
||||
|
||||
while (*in != 0)
|
||||
{
|
||||
|
||||
|
@ -981,14 +947,14 @@ wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
|
|||
int val = *in - ENCODE_DIRECT_BASE;
|
||||
int tmp;
|
||||
|
||||
*(pos++) = L'\\';
|
||||
*(pos++) = L'X';
|
||||
out += L'\\';
|
||||
out += L'X';
|
||||
|
||||
tmp = val/16;
|
||||
*pos++ = tmp > 9? L'a'+(tmp-10):L'0'+tmp;
|
||||
out += tmp > 9? L'a'+(tmp-10):L'0'+tmp;
|
||||
|
||||
tmp = val%16;
|
||||
*pos++ = tmp > 9? L'a'+(tmp-10):L'0'+tmp;
|
||||
out += tmp > 9? L'a'+(tmp-10):L'0'+tmp;
|
||||
need_escape=need_complex_escape=1;
|
||||
|
||||
}
|
||||
|
@ -998,32 +964,32 @@ wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
|
|||
switch (c)
|
||||
{
|
||||
case L'\t':
|
||||
*(pos++) = L'\\';
|
||||
*(pos++) = L't';
|
||||
out += L'\\';
|
||||
out += L't';
|
||||
need_escape=need_complex_escape=1;
|
||||
break;
|
||||
|
||||
case L'\n':
|
||||
*(pos++) = L'\\';
|
||||
*(pos++) = L'n';
|
||||
out += L'\\';
|
||||
out += L'n';
|
||||
need_escape=need_complex_escape=1;
|
||||
break;
|
||||
|
||||
case L'\b':
|
||||
*(pos++) = L'\\';
|
||||
*(pos++) = L'b';
|
||||
out += L'\\';
|
||||
out += L'b';
|
||||
need_escape=need_complex_escape=1;
|
||||
break;
|
||||
|
||||
case L'\r':
|
||||
*(pos++) = L'\\';
|
||||
*(pos++) = L'r';
|
||||
out += L'\\';
|
||||
out += L'r';
|
||||
need_escape=need_complex_escape=1;
|
||||
break;
|
||||
|
||||
case L'\x1b':
|
||||
*(pos++) = L'\\';
|
||||
*(pos++) = L'e';
|
||||
out += L'\\';
|
||||
out += L'e';
|
||||
need_escape=need_complex_escape=1;
|
||||
break;
|
||||
|
||||
|
@ -1033,8 +999,8 @@ wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
|
|||
{
|
||||
need_escape=need_complex_escape=1;
|
||||
if (escape_all)
|
||||
*pos++ = L'\\';
|
||||
*pos++ = *in;
|
||||
out += L'\\';
|
||||
out += *in;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1063,9 +1029,9 @@ wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
|
|||
{
|
||||
need_escape=1;
|
||||
if (escape_all)
|
||||
*pos++ = L'\\';
|
||||
out += L'\\';
|
||||
}
|
||||
*pos++ = *in;
|
||||
out += *in;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1075,9 +1041,9 @@ wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
|
|||
{
|
||||
if (*in <27 && *in > 0)
|
||||
{
|
||||
*(pos++) = L'\\';
|
||||
*(pos++) = L'c';
|
||||
*(pos++) = L'a' + *in -1;
|
||||
out += L'\\';
|
||||
out += L'c';
|
||||
out += L'a' + *in -1;
|
||||
|
||||
need_escape=need_complex_escape=1;
|
||||
break;
|
||||
|
@ -1086,15 +1052,15 @@ wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
|
|||
|
||||
|
||||
int tmp = (*in)%16;
|
||||
*pos++ = L'\\';
|
||||
*pos++ = L'x';
|
||||
*pos++ = ((*in>15)? L'1' : L'0');
|
||||
*pos++ = tmp > 9? L'a'+(tmp-10):L'0'+tmp;
|
||||
out += L'\\';
|
||||
out += L'x';
|
||||
out += ((*in>15)? L'1' : L'0');
|
||||
out += tmp > 9? L'a'+(tmp-10):L'0'+tmp;
|
||||
need_escape=need_complex_escape=1;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pos++ = *in;
|
||||
out += *in;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1103,7 +1069,6 @@ wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
|
|||
|
||||
in++;
|
||||
}
|
||||
*pos = 0;
|
||||
|
||||
/*
|
||||
Use quoted escaping if possible, since most people find it
|
||||
|
@ -1111,18 +1076,32 @@ wchar_t *escape(const wchar_t *in_orig, escape_flags_t flags)
|
|||
*/
|
||||
if (!no_quoted && need_escape && !need_complex_escape && escape_all)
|
||||
{
|
||||
free(out);
|
||||
out = escape_simple(in_orig);
|
||||
wchar_t single_quote = L'\'';
|
||||
out.clear();
|
||||
out.reserve(2 + in_len);
|
||||
out.push_back(single_quote);
|
||||
out.append(orig_in, in_len);
|
||||
out.push_back(single_quote);
|
||||
}
|
||||
}
|
||||
|
||||
wchar_t *escape(const wchar_t *in, escape_flags_t flags)
|
||||
{
|
||||
if (!in)
|
||||
{
|
||||
debug(0, L"%s called with null input", __func__);
|
||||
FATAL_EXIT();
|
||||
}
|
||||
|
||||
return out;
|
||||
wcstring tmp;
|
||||
escape_string_internal(in, wcslen(in), &tmp, flags);
|
||||
return wcsdup(tmp.c_str());
|
||||
}
|
||||
|
||||
wcstring escape_string(const wcstring &in, escape_flags_t flags)
|
||||
{
|
||||
wchar_t *tmp = escape(in.c_str(), flags);
|
||||
wcstring result(tmp);
|
||||
free(tmp);
|
||||
wcstring result;
|
||||
escape_string_internal(in.c_str(), in.size(), &result, flags);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
8
common.h
8
common.h
|
@ -348,8 +348,8 @@ void format_size_safe(char buff[128], unsigned long long sz);
|
|||
void debug_safe(int level, const char *msg, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL, const char *param5 = NULL, const char *param6 = NULL, const char *param7 = NULL, const char *param8 = NULL, const char *param9 = NULL, const char *param10 = NULL, const char *param11 = NULL, const char *param12 = NULL);
|
||||
|
||||
/** Writes out a long safely */
|
||||
void format_long_safe(char buff[128], long val);
|
||||
void format_long_safe(wchar_t buff[128], long val);
|
||||
void format_long_safe(char buff[64], long val);
|
||||
void format_long_safe(wchar_t buff[64], long val);
|
||||
|
||||
|
||||
template<typename T>
|
||||
|
@ -612,10 +612,10 @@ const wchar_t *wcsfuncname(const wchar_t *str);
|
|||
/**
|
||||
Test if the given string is valid in a variable name
|
||||
|
||||
\return 1 if this is a valid name, 0 otherwise
|
||||
\return true if this is a valid name, false otherwise
|
||||
*/
|
||||
|
||||
int wcsvarchr(wchar_t chr);
|
||||
bool wcsvarchr(wchar_t chr);
|
||||
|
||||
|
||||
/**
|
||||
|
|
284
complete.cpp
284
complete.cpp
|
@ -44,6 +44,7 @@
|
|||
#include "parser_keywords.h"
|
||||
#include "wutil.h"
|
||||
#include "path.h"
|
||||
#include "parse_tree.h"
|
||||
#include "iothread.h"
|
||||
|
||||
/*
|
||||
|
@ -465,7 +466,13 @@ void completion_autoload_t::command_removed(const wcstring &cmd)
|
|||
/** Create a new completion entry */
|
||||
void append_completion(std::vector<completion_t> &completions, const wcstring &comp, const wcstring &desc, complete_flags_t flags, string_fuzzy_match_t match)
|
||||
{
|
||||
completions.push_back(completion_t(comp, desc, match, flags));
|
||||
/* If we just constructed the completion and used push_back, we would get two string copies. Try to avoid that by making a stubby completion in the vector first, and then copying our string in. */
|
||||
completions.push_back(completion_t(wcstring()));
|
||||
completion_t *last = &completions.back();
|
||||
last->completion = comp;
|
||||
last->description = desc;
|
||||
last->match = match;
|
||||
last->flags = flags;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1190,7 +1197,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool
|
|||
wcstring_list_t names = function_get_names(str_cmd.at(0) == L'_');
|
||||
for (size_t i=0; i < names.size(); i++)
|
||||
{
|
||||
possible_comp.push_back(completion_t(names.at(i)));
|
||||
append_completion(possible_comp, names.at(i));
|
||||
}
|
||||
|
||||
this->complete_strings(str_cmd, 0, &complete_function_desc, possible_comp, 0);
|
||||
|
@ -1229,7 +1236,7 @@ void completer_t::complete_from_args(const wcstring &str,
|
|||
std::vector<completion_t> possible_comp;
|
||||
|
||||
bool is_autosuggest = (this->type() == COMPLETE_AUTOSUGGEST);
|
||||
parser_t parser(is_autosuggest ? PARSER_TYPE_COMPLETIONS_ONLY : PARSER_TYPE_GENERAL, false);
|
||||
parser_t parser(is_autosuggest ? PARSER_TYPE_COMPLETIONS_ONLY : PARSER_TYPE_GENERAL, false /* don't show errors */);
|
||||
|
||||
/* If type is COMPLETE_AUTOSUGGEST, it means we're on a background thread, so don't call proc_push_interactive */
|
||||
if (! is_autosuggest)
|
||||
|
@ -1240,7 +1247,7 @@ void completer_t::complete_from_args(const wcstring &str,
|
|||
if (! is_autosuggest)
|
||||
proc_pop_interactive();
|
||||
|
||||
this->complete_strings(str, desc.c_str(), 0, possible_comp, flags);
|
||||
this->complete_strings(escape_string(str, ESCAPE_ALL), desc.c_str(), 0, possible_comp, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1360,7 +1367,9 @@ struct local_options_t
|
|||
bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spopt, const wcstring &sstr, bool use_switches)
|
||||
{
|
||||
|
||||
const wchar_t * const cmd_orig = scmd_orig.c_str(), * const popt = spopt.c_str(), * const str = sstr.c_str();
|
||||
const wchar_t * const cmd_orig = scmd_orig.c_str();
|
||||
const wchar_t * const popt = spopt.c_str();
|
||||
const wchar_t * const str = sstr.c_str();
|
||||
|
||||
bool use_common=1, use_files=1;
|
||||
|
||||
|
@ -1475,7 +1484,7 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop
|
|||
{
|
||||
if (o->result_mode & NO_COMMON) use_common = false;
|
||||
if (o->result_mode & NO_FILES) use_files = false;
|
||||
complete_from_args(str, o->comp.c_str(), o->localized_desc(), o->flags);
|
||||
complete_from_args(str, o->comp, o->localized_desc(), o->flags);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1688,7 +1697,7 @@ bool completer_t::complete_variable(const wcstring &str, size_t start_offset)
|
|||
desc = format_string(COMPLETE_VAR_DESC_VAL, value.c_str());
|
||||
}
|
||||
|
||||
append_completion(this->completions, comp.c_str(), desc.c_str(), flags, match);
|
||||
append_completion(this->completions, comp, desc, flags, match);
|
||||
|
||||
res = true;
|
||||
}
|
||||
|
@ -1788,225 +1797,154 @@ bool completer_t::try_complete_user(const wcstring &str)
|
|||
return res;
|
||||
}
|
||||
|
||||
void complete(const wcstring &cmd, std::vector<completion_t> &comps, completion_request_flags_t flags)
|
||||
void complete(const wcstring &cmd_with_subcmds, std::vector<completion_t> &comps, completion_request_flags_t flags)
|
||||
{
|
||||
/* Determine the innermost subcommand */
|
||||
const wchar_t *cmdsubst_begin, *cmdsubst_end;
|
||||
parse_util_cmdsubst_extent(cmd_with_subcmds.c_str(), cmd_with_subcmds.size(), &cmdsubst_begin, &cmdsubst_end);
|
||||
assert(cmdsubst_begin != NULL && cmdsubst_end != NULL && cmdsubst_end >= cmdsubst_begin);
|
||||
const wcstring cmd = wcstring(cmdsubst_begin, cmdsubst_end - cmdsubst_begin);
|
||||
|
||||
/* Make our completer */
|
||||
completer_t completer(cmd, flags);
|
||||
|
||||
const wchar_t *tok_begin, *tok_end, *cmdsubst_begin, *cmdsubst_end, *prev_begin, *prev_end;
|
||||
wcstring current_token, prev_token;
|
||||
wcstring current_command;
|
||||
int on_command=0;
|
||||
size_t pos;
|
||||
const size_t pos = cmd.size();
|
||||
bool done=false;
|
||||
int use_command = 1;
|
||||
int use_function = 1;
|
||||
int use_builtin = 1;
|
||||
int had_ddash = 0;
|
||||
bool use_command = 1;
|
||||
bool use_function = 1;
|
||||
bool use_builtin = 1;
|
||||
|
||||
// debug( 1, L"Complete '%ls'", cmd );
|
||||
|
||||
size_t cursor_pos = cmd.size();
|
||||
// debug( 1, L"Complete '%ls'", cmd );
|
||||
|
||||
const wchar_t *cmd_cstr = cmd.c_str();
|
||||
parse_util_cmdsubst_extent(cmd_cstr, cursor_pos, &cmdsubst_begin, &cmdsubst_end);
|
||||
parse_util_token_extent(cmd_cstr, cursor_pos, &tok_begin, &tok_end, &prev_begin, &prev_end);
|
||||
|
||||
if (!cmdsubst_begin)
|
||||
done=1;
|
||||
|
||||
const wchar_t *tok_begin = NULL, *prev_begin = NULL, *prev_end = NULL;
|
||||
parse_util_token_extent(cmd_cstr, cmd.size(), &tok_begin, NULL, &prev_begin, &prev_end);
|
||||
|
||||
/**
|
||||
If we are completing a variable name or a tilde expansion user
|
||||
name, we do that and return. No need for any other completions.
|
||||
*/
|
||||
|
||||
const wcstring current_token = tok_begin;
|
||||
|
||||
if (!done)
|
||||
{
|
||||
wcstring tmp = tok_begin;
|
||||
done = completer.try_complete_variable(tmp) || completer.try_complete_user(tmp);
|
||||
done = completer.try_complete_variable(current_token) || completer.try_complete_user(current_token);
|
||||
}
|
||||
|
||||
if (!done)
|
||||
{
|
||||
pos = cursor_pos-(cmdsubst_begin-cmd_cstr);
|
||||
//const size_t prev_token_len = (prev_begin ? prev_end - prev_begin : 0);
|
||||
//const wcstring prev_token(prev_begin, prev_token_len);
|
||||
|
||||
const wcstring buff = wcstring(cmdsubst_begin, cmdsubst_end-cmdsubst_begin);
|
||||
parse_node_tree_t tree;
|
||||
parse_tree_from_string(cmd, parse_flag_continue_after_error | parse_flag_accept_incomplete_tokens, &tree, NULL);
|
||||
|
||||
int had_cmd=0;
|
||||
int end_loop=0;
|
||||
/* Find the plain statement that contains the position */
|
||||
const parse_node_t *plain_statement = tree.find_node_matching_source_location(symbol_plain_statement, pos, NULL);
|
||||
if (plain_statement != NULL)
|
||||
{
|
||||
assert(plain_statement->has_source() && plain_statement->type == symbol_plain_statement);
|
||||
|
||||
tokenizer_t tok(buff.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS);
|
||||
while (tok_has_next(&tok) && !end_loop)
|
||||
{
|
||||
switch (tok_last_type(&tok))
|
||||
{
|
||||
/* Get the command node */
|
||||
const parse_node_t *cmd_node = tree.get_child(*plain_statement, 0, parse_token_type_string);
|
||||
|
||||
case TOK_STRING:
|
||||
{
|
||||
/* Get the actual command string */
|
||||
if (cmd_node != NULL)
|
||||
current_command = cmd_node->get_source(cmd);
|
||||
|
||||
const wcstring ncmd = tok_last(&tok);
|
||||
int is_ddash = (ncmd == L"--") && ((tok_get_pos(&tok)+2) < (long)pos);
|
||||
/* Check the decoration */
|
||||
switch (tree.decoration_for_plain_statement(*plain_statement))
|
||||
{
|
||||
case parse_statement_decoration_none:
|
||||
use_command = true;
|
||||
use_function = true;
|
||||
use_builtin = true;
|
||||
break;
|
||||
|
||||
if (!had_cmd)
|
||||
{
|
||||
case parse_statement_decoration_command:
|
||||
use_command = true;
|
||||
use_function = false;
|
||||
use_builtin = false;
|
||||
break;
|
||||
|
||||
if (parser_keywords_is_subcommand(ncmd))
|
||||
{
|
||||
if (ncmd == L"builtin")
|
||||
{
|
||||
use_function = 0;
|
||||
use_command = 0;
|
||||
use_builtin = 1;
|
||||
}
|
||||
else if (ncmd == L"command")
|
||||
{
|
||||
use_command = 1;
|
||||
use_function = 0;
|
||||
use_builtin = 0;
|
||||
}
|
||||
case parse_statement_decoration_builtin:
|
||||
use_command = false;
|
||||
use_function = false;
|
||||
use_builtin = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (!is_ddash ||
|
||||
((use_command && use_function && use_builtin)))
|
||||
{
|
||||
current_command = ncmd;
|
||||
|
||||
size_t token_end = tok_get_pos(&tok) + ncmd.size();
|
||||
|
||||
on_command = (pos <= token_end);
|
||||
had_cmd=1;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_ddash)
|
||||
{
|
||||
had_ddash = 1;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case TOK_END:
|
||||
case TOK_PIPE:
|
||||
case TOK_BACKGROUND:
|
||||
{
|
||||
had_cmd=0;
|
||||
had_ddash = 0;
|
||||
use_command = 1;
|
||||
use_function = 1;
|
||||
use_builtin = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case TOK_ERROR:
|
||||
{
|
||||
end_loop=1;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tok_get_pos(&tok) >= (long)pos)
|
||||
{
|
||||
end_loop=1;
|
||||
}
|
||||
|
||||
tok_next(&tok);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Get the string to complete
|
||||
*/
|
||||
|
||||
current_token.assign(tok_begin, cursor_pos-(tok_begin-cmd_cstr));
|
||||
|
||||
if (prev_begin)
|
||||
{
|
||||
prev_token.assign(prev_begin, prev_end - prev_begin);
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_token.clear();
|
||||
}
|
||||
|
||||
// debug( 0, L"on_command: %d, %ls %ls\n", on_command, current_command, current_token );
|
||||
|
||||
/*
|
||||
Check if we are using the 'command' or 'builtin' builtins
|
||||
_and_ we are writing a switch instead of a command. In that
|
||||
case, complete using the builtins completions, not using a
|
||||
subcommand.
|
||||
*/
|
||||
|
||||
if ((on_command || current_token == L"--") &&
|
||||
string_prefixes_string(L"-", current_token) &&
|
||||
!(use_command && use_function && use_builtin))
|
||||
{
|
||||
if (use_command == 0)
|
||||
current_command = L"builtin";
|
||||
else
|
||||
current_command = L"command";
|
||||
|
||||
had_cmd = 1;
|
||||
on_command = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Use command completions if in between commands
|
||||
*/
|
||||
if (!had_cmd)
|
||||
{
|
||||
on_command=1;
|
||||
}
|
||||
|
||||
|
||||
if (on_command)
|
||||
if (cmd_node && cmd_node->location_in_or_at_end_of_source_range(pos))
|
||||
{
|
||||
/* Complete command filename */
|
||||
completer.complete_cmd(current_token, use_function, use_builtin, use_command);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Get all the arguments */
|
||||
const parse_node_tree_t::parse_node_list_t all_arguments = tree.find_nodes(*plain_statement, symbol_argument);
|
||||
|
||||
/* See whether we are in an argument. We may also be in a redirection, or nothing at all. */
|
||||
size_t matching_arg_index = -1;
|
||||
for (size_t i=0; i < all_arguments.size(); i++)
|
||||
{
|
||||
const parse_node_t *node = all_arguments.at(i);
|
||||
if (node->location_in_or_at_end_of_source_range(pos))
|
||||
{
|
||||
matching_arg_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool had_ddash = false;
|
||||
wcstring current_argument, previous_argument;
|
||||
if (matching_arg_index != (size_t)(-1))
|
||||
{
|
||||
/* Get the current argument and the previous argument, if we have one */
|
||||
current_argument = all_arguments.at(matching_arg_index)->get_source(cmd);
|
||||
|
||||
if (matching_arg_index > 0)
|
||||
previous_argument = all_arguments.at(matching_arg_index - 1)->get_source(cmd);
|
||||
|
||||
/* Check to see if we have a preceding double-dash */
|
||||
for (size_t i=0; i < matching_arg_index; i++)
|
||||
{
|
||||
if (all_arguments.at(i)->get_source(cmd) == L"--")
|
||||
{
|
||||
had_ddash = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool do_file = false;
|
||||
|
||||
wcstring current_command_unescape, prev_token_unescape, current_token_unescape;
|
||||
wcstring current_command_unescape, previous_argument_unescape, current_argument_unescape;
|
||||
if (unescape_string(current_command, ¤t_command_unescape, UNESCAPE_DEFAULT) &&
|
||||
unescape_string(prev_token, &prev_token_unescape, UNESCAPE_DEFAULT) &&
|
||||
unescape_string(current_token, ¤t_token_unescape, UNESCAPE_INCOMPLETE))
|
||||
unescape_string(previous_argument, &previous_argument_unescape, UNESCAPE_DEFAULT) &&
|
||||
unescape_string(current_argument, ¤t_argument_unescape, UNESCAPE_INCOMPLETE))
|
||||
{
|
||||
do_file = completer.complete_param(current_command_unescape,
|
||||
prev_token_unescape,
|
||||
current_token_unescape,
|
||||
previous_argument_unescape,
|
||||
current_argument_unescape,
|
||||
!had_ddash);
|
||||
}
|
||||
|
||||
/* If we have found no command specific completions at
|
||||
all, fall back to using file completions.
|
||||
*/
|
||||
/* If we have found no command specific completions at all, fall back to using file completions. */
|
||||
if (completer.empty())
|
||||
do_file = true;
|
||||
|
||||
/* If we're autosuggesting, and the token is empty, don't do file suggestions */
|
||||
if ((flags & COMPLETION_REQUEST_AUTOSUGGESTION) && current_token_unescape.empty())
|
||||
/* And if we're autosuggesting, and the token is empty, don't do file suggestions */
|
||||
if ((flags & COMPLETION_REQUEST_AUTOSUGGESTION) && current_argument_unescape.empty())
|
||||
do_file = false;
|
||||
|
||||
/*
|
||||
This function wants the unescaped string
|
||||
*/
|
||||
/* This function wants the unescaped string */
|
||||
completer.complete_param_expand(current_token, do_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
comps = completer.get_completions();
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ public:
|
|||
int flags;
|
||||
|
||||
/* Construction. Note: defining these so that they are not inlined reduces the executable size. */
|
||||
completion_t(const wcstring &comp, const wcstring &desc = L"", string_fuzzy_match_t match = string_fuzzy_match_t(fuzzy_match_exact), int flags_val = 0);
|
||||
completion_t(const wcstring &comp, const wcstring &desc = wcstring(), string_fuzzy_match_t match = string_fuzzy_match_t(fuzzy_match_exact), int flags_val = 0);
|
||||
completion_t(const completion_t &);
|
||||
completion_t &operator=(const completion_t &);
|
||||
|
||||
|
@ -268,7 +268,7 @@ void complete_load(const wcstring &cmd, bool reload);
|
|||
\param flags completion flags
|
||||
|
||||
*/
|
||||
void append_completion(std::vector<completion_t> &completions, const wcstring &comp, const wcstring &desc = L"", int flags = 0, string_fuzzy_match_t match = string_fuzzy_match_t(fuzzy_match_exact));
|
||||
void append_completion(std::vector<completion_t> &completions, const wcstring &comp, const wcstring &desc = wcstring(), int flags = 0, string_fuzzy_match_t match = string_fuzzy_match_t(fuzzy_match_exact));
|
||||
|
||||
/* Function used for testing */
|
||||
void complete_set_variable_names(const wcstring_list_t *names);
|
||||
|
|
|
@ -398,7 +398,7 @@ fi
|
|||
# installed
|
||||
#
|
||||
|
||||
AC_ARG_VAR( [docdir], [Documentation direcotry] )
|
||||
AC_ARG_VAR( [docdir], [Documentation directory] )
|
||||
|
||||
if test -z $docdir; then
|
||||
docdir=$datadir/doc/fish
|
||||
|
@ -873,7 +873,7 @@ case $target_os in
|
|||
esac
|
||||
|
||||
# Tell the world what we know
|
||||
AC_CONFIG_FILES([Makefile fish.spec])
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
if test ! x$local_found_posix_switch = xyes; then
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
\subsection eval-example Example
|
||||
|
||||
The folloing code will call the ls command. Note that \c fish does not
|
||||
The following code will call the ls command. Note that \c fish does not
|
||||
support the use of environment variables as direct commands; \c eval can
|
||||
be used to work around this.
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ Version 2, June 1991
|
|||
|
||||
<PRE>
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
@ -995,7 +995,7 @@ Version 2, June 1991
|
|||
|
||||
<PRE>
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
|
|
@ -312,7 +312,7 @@ lesson.pdf
|
|||
/var/run/sntp.log
|
||||
</pre>
|
||||
|
||||
<p>If that directory traversal is taking a long time, you can control-C out of it.
|
||||
<p>If that directory traversal is taking a long time, you can Control-C out of it.
|
||||
|
||||
<h2 id="tut_pipes_and_redirections">Pipes and Redirections</h2>
|
||||
|
||||
|
@ -500,7 +500,7 @@ You can iterate over a list (or a slice) with a <i>for loop</i>:
|
|||
> <b>for</b> <i>val</i> <b>in</b> <i>$PATH</i>
|
||||
<b>echo</b> <i>"entry: $val"</i>
|
||||
<b>end</b>
|
||||
entry: usr/bin/
|
||||
entry: /usr/bin/
|
||||
entry: /bin
|
||||
entry: /usr/sbin
|
||||
entry: /sbin
|
||||
|
|
6
env.cpp
6
env.cpp
|
@ -299,7 +299,6 @@ static bool var_is_locale(const wcstring &key)
|
|||
static void handle_locale()
|
||||
{
|
||||
const env_var_t lc_all = env_get_string(L"LC_ALL");
|
||||
int i;
|
||||
const wcstring old_locale = wsetlocale(LC_MESSAGES, NULL);
|
||||
|
||||
/*
|
||||
|
@ -330,7 +329,7 @@ static void handle_locale()
|
|||
wsetlocale(LC_ALL, lang.c_str());
|
||||
}
|
||||
|
||||
for (i=2; locale_variable[i]; i++)
|
||||
for (int i=2; locale_variable[i]; i++)
|
||||
{
|
||||
const env_var_t val = env_get_string(locale_variable[i]);
|
||||
|
||||
|
@ -479,7 +478,7 @@ static void env_set_defaults()
|
|||
if (pw->pw_name != NULL)
|
||||
{
|
||||
const wcstring wide_name = str2wcstring(pw->pw_name);
|
||||
env_set(L"USER", NULL, ENV_GLOBAL);
|
||||
env_set(L"USER", wide_name.c_str(), ENV_GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -892,6 +891,7 @@ int env_set(const wcstring &key, const wchar_t *val, int var_mode)
|
|||
if (!is_universal)
|
||||
{
|
||||
event_t ev = event_t::variable_event(key);
|
||||
ev.arguments.reserve(3);
|
||||
ev.arguments.push_back(L"VARIABLE");
|
||||
ev.arguments.push_back(L"SET");
|
||||
ev.arguments.push_back(key);
|
||||
|
|
|
@ -426,8 +426,6 @@ void env_universal_barrier()
|
|||
|
||||
void env_universal_set(const wcstring &name, const wcstring &value, bool exportv)
|
||||
{
|
||||
message_t *msg;
|
||||
|
||||
if (!s_env_univeral_inited)
|
||||
return;
|
||||
|
||||
|
@ -439,7 +437,7 @@ void env_universal_set(const wcstring &name, const wcstring &value, bool exportv
|
|||
}
|
||||
else
|
||||
{
|
||||
msg = create_message(exportv?SET_EXPORT:SET,
|
||||
message_t *msg = create_message(exportv?SET_EXPORT:SET,
|
||||
name.c_str(),
|
||||
value.c_str());
|
||||
|
||||
|
@ -459,7 +457,6 @@ int env_universal_remove(const wchar_t *name)
|
|||
{
|
||||
int res;
|
||||
|
||||
message_t *msg;
|
||||
if (!s_env_univeral_inited)
|
||||
return 1;
|
||||
|
||||
|
@ -476,7 +473,7 @@ int env_universal_remove(const wchar_t *name)
|
|||
}
|
||||
else
|
||||
{
|
||||
msg= create_message(ERASE, name, 0);
|
||||
message_t *msg = create_message(ERASE, name, 0);
|
||||
msg->count=1;
|
||||
env_universal_server.unsent.push(msg);
|
||||
env_universal_barrier();
|
||||
|
|
12
event.cpp
12
event.cpp
|
@ -144,12 +144,15 @@ static int event_match(const event_t &classv, const event_t &instance)
|
|||
*/
|
||||
static int event_is_blocked(const event_t &e)
|
||||
{
|
||||
block_t *block;
|
||||
const block_t *block;
|
||||
parser_t &parser = parser_t::principal_parser();
|
||||
for (block = parser.current_block; block; block = block->outer)
|
||||
|
||||
size_t idx = 0;
|
||||
while ((block = parser.block_at_index(idx++)))
|
||||
{
|
||||
if (event_block_list_blocks_type(block->event_blocks, e.type))
|
||||
return true;
|
||||
|
||||
}
|
||||
return event_block_list_blocks_type(parser.global_event_blocks, e.type);
|
||||
}
|
||||
|
@ -565,9 +568,6 @@ static void event_fire_internal(const event_t &event)
|
|||
*/
|
||||
static void event_fire_delayed()
|
||||
{
|
||||
|
||||
size_t i;
|
||||
|
||||
/*
|
||||
If is_event is one, we are running the event-handler non-recursively.
|
||||
|
||||
|
@ -579,7 +579,7 @@ static void event_fire_delayed()
|
|||
{
|
||||
event_list_t new_blocked;
|
||||
|
||||
for (i=0; i<blocked.size(); i++)
|
||||
for (size_t i=0; i<blocked.size(); i++)
|
||||
{
|
||||
event_t *e = blocked.at(i);
|
||||
if (event_is_blocked(*e))
|
||||
|
|
83
exec.cpp
83
exec.cpp
|
@ -394,12 +394,13 @@ static void io_cleanup_fds(const std::vector<int> &opened_fds)
|
|||
repeatedly reopened for every command in the block, which would
|
||||
reset the cursor position.
|
||||
|
||||
\return the transmogrified chain on sucess, or 0 on failiure
|
||||
\return true on success, false on failure. Returns the output chain and opened_fds by reference
|
||||
*/
|
||||
static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t &out_chain, std::vector<int> &out_opened_fds)
|
||||
static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t *out_chain, std::vector<int> *out_opened_fds)
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
assert(out_chain.empty());
|
||||
assert(out_chain != NULL && out_opened_fds != NULL);
|
||||
assert(out_chain->empty());
|
||||
|
||||
/* Just to be clear what we do for an empty chain */
|
||||
if (in_chain.empty())
|
||||
|
@ -479,8 +480,8 @@ static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t &out_chain, s
|
|||
if (success)
|
||||
{
|
||||
/* Yay */
|
||||
out_chain.swap(result_chain);
|
||||
out_opened_fds.swap(opened_fds);
|
||||
out_chain->swap(result_chain);
|
||||
out_opened_fds->swap(opened_fds);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -496,19 +497,24 @@ static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t &out_chain, s
|
|||
Morph an io redirection chain into redirections suitable for
|
||||
passing to eval, call eval, and clean up morphed redirections.
|
||||
|
||||
\param def the code to evaluate
|
||||
\param def the code to evaluate, or the empty string if none
|
||||
\param node_offset the offset of the node to evalute, or NODE_OFFSET_INVALID
|
||||
\param block_type the type of block to push on evaluation
|
||||
\param io the io redirections to be performed on this block
|
||||
*/
|
||||
|
||||
static void internal_exec_helper(parser_t &parser,
|
||||
const wchar_t *def,
|
||||
const wcstring &def,
|
||||
node_offset_t node_offset,
|
||||
enum block_type_t block_type,
|
||||
const io_chain_t &ios)
|
||||
{
|
||||
// If we have a valid node offset, then we must not have a string to execute
|
||||
assert(node_offset == NODE_OFFSET_INVALID || def.empty());
|
||||
|
||||
io_chain_t morphed_chain;
|
||||
std::vector<int> opened_fds;
|
||||
bool transmorgrified = io_transmogrify(ios, morphed_chain, opened_fds);
|
||||
bool transmorgrified = io_transmogrify(ios, &morphed_chain, &opened_fds);
|
||||
|
||||
int is_block_old=is_block;
|
||||
is_block=1;
|
||||
|
@ -524,7 +530,14 @@ static void internal_exec_helper(parser_t &parser,
|
|||
|
||||
signal_unblock();
|
||||
|
||||
if (node_offset == NODE_OFFSET_INVALID)
|
||||
{
|
||||
parser.eval(def, morphed_chain, block_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
parser.eval_block_node(node_offset, morphed_chain, block_type);
|
||||
}
|
||||
|
||||
signal_block();
|
||||
|
||||
|
@ -564,6 +577,12 @@ static bool can_use_posix_spawn_for_job(const job_t *job, const process_t *proce
|
|||
/* What exec does if no_exec is set. This only has to handle block pushing and popping. See #624. */
|
||||
static void exec_no_exec(parser_t &parser, const job_t *job)
|
||||
{
|
||||
if (parser_use_ast())
|
||||
{
|
||||
/* With the new parser, commands aren't responsible for pushing / popping blocks, so there's nothing to do */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Hack hack hack. If this is an 'end' job, then trigger a pop. If this is a job that would create a block, trigger a push. See #624 */
|
||||
const process_t *p = job->first_process;
|
||||
if (p && p->type == INTERNAL_BUILTIN)
|
||||
|
@ -580,7 +599,8 @@ static void exec_no_exec(parser_t &parser, const job_t *job)
|
|||
}
|
||||
else if (builtin_name == L"end")
|
||||
{
|
||||
if (parser.current_block == NULL || parser.current_block->type() == TOP)
|
||||
const block_t *block = parser.current_block();
|
||||
if (block == NULL || block->type() == TOP)
|
||||
{
|
||||
fprintf(stderr, "Warning: not popping the root block\n");
|
||||
}
|
||||
|
@ -681,7 +701,7 @@ void exec_job(parser_t &parser, job_t *j)
|
|||
j->first_process->completed=1;
|
||||
return;
|
||||
}
|
||||
|
||||
assert(0 && "This should be unreachable");
|
||||
}
|
||||
|
||||
signal_block();
|
||||
|
@ -806,7 +826,6 @@ void exec_job(parser_t &parser, job_t *j)
|
|||
{
|
||||
pipe_write.reset(new io_pipe_t(p->pipe_write_fd, false));
|
||||
process_net_io_chain.push_back(pipe_write);
|
||||
|
||||
}
|
||||
|
||||
/* The explicit IO redirections associated with the process */
|
||||
|
@ -925,7 +944,7 @@ void exec_job(parser_t &parser, job_t *j)
|
|||
|
||||
if (! exec_error)
|
||||
{
|
||||
internal_exec_helper(parser, def.c_str(), TOP, process_net_io_chain);
|
||||
internal_exec_helper(parser, def, NODE_OFFSET_INVALID, TOP, process_net_io_chain);
|
||||
}
|
||||
|
||||
parser.allow_function();
|
||||
|
@ -935,12 +954,14 @@ void exec_job(parser_t &parser, job_t *j)
|
|||
}
|
||||
|
||||
case INTERNAL_BLOCK:
|
||||
case INTERNAL_BLOCK_NODE:
|
||||
{
|
||||
if (p->next)
|
||||
{
|
||||
block_output_io_buffer.reset(io_buffer_t::create(0));
|
||||
if (block_output_io_buffer.get() == NULL)
|
||||
{
|
||||
/* We failed (e.g. no more fds could be created). */
|
||||
exec_error = true;
|
||||
job_mark_process_as_failed(j, p);
|
||||
}
|
||||
|
@ -953,10 +974,19 @@ void exec_job(parser_t &parser, job_t *j)
|
|||
|
||||
if (! exec_error)
|
||||
{
|
||||
internal_exec_helper(parser, p->argv0(), TOP, process_net_io_chain);
|
||||
if (p->type == INTERNAL_BLOCK)
|
||||
{
|
||||
/* The block contents (as in, fish code) are stored in argv0 (ugh) */
|
||||
assert(p->argv0() != NULL);
|
||||
internal_exec_helper(parser, p->argv0(), NODE_OFFSET_INVALID, TOP, process_net_io_chain);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(p->type == INTERNAL_BLOCK_NODE);
|
||||
internal_exec_helper(parser, wcstring(), p->internal_block_node, TOP, process_net_io_chain);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case INTERNAL_BUILTIN:
|
||||
|
@ -1103,6 +1133,20 @@ void exec_job(parser_t &parser, job_t *j)
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case EXTERNAL:
|
||||
/* External commands are handled in the next switch statement below */
|
||||
break;
|
||||
|
||||
case INTERNAL_EXEC:
|
||||
/* We should have handled exec up above */
|
||||
assert(0 && "INTERNAL_EXEC process found in pipeline, where it should never be. Aborting.");
|
||||
break;
|
||||
|
||||
case INTERNAL_BUFFER:
|
||||
/* Internal buffers are handled in the next switch statement below */
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (exec_error)
|
||||
|
@ -1114,6 +1158,7 @@ void exec_job(parser_t &parser, job_t *j)
|
|||
{
|
||||
|
||||
case INTERNAL_BLOCK:
|
||||
case INTERNAL_BLOCK_NODE:
|
||||
case INTERNAL_FUNCTION:
|
||||
{
|
||||
int status = proc_get_last_status();
|
||||
|
@ -1130,7 +1175,7 @@ void exec_job(parser_t &parser, job_t *j)
|
|||
No buffer, so we exit directly. This means we
|
||||
have to manually set the exit status.
|
||||
*/
|
||||
if (p->next == 0)
|
||||
if (p->next == NULL)
|
||||
{
|
||||
proc_set_last_status(job_get_flag(j, JOB_NEGATE)?(!status):status);
|
||||
}
|
||||
|
@ -1463,6 +1508,12 @@ void exec_job(parser_t &parser, job_t *j)
|
|||
break;
|
||||
}
|
||||
|
||||
case INTERNAL_EXEC:
|
||||
{
|
||||
/* We should have handled exec up above */
|
||||
assert(0 && "INTERNAL_EXEC process found in pipeline, where it should never be. Aborting.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p->type == INTERNAL_BUILTIN)
|
||||
|
@ -1531,6 +1582,8 @@ static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst, boo
|
|||
const int prev_status = proc_get_last_status();
|
||||
char sep=0;
|
||||
|
||||
//fprintf(stderr, "subcmd %ls\n", cmd.c_str());
|
||||
|
||||
const env_var_t ifs = env_get_string(L"IFS");
|
||||
|
||||
if (! ifs.missing_or_empty())
|
||||
|
|
79
expand.cpp
79
expand.cpp
|
@ -785,6 +785,14 @@ static int expand_pid(const wcstring &instr_with_sep,
|
|||
expand_flags_t flags,
|
||||
std::vector<completion_t> &out)
|
||||
{
|
||||
/* Hack. If there's no INTERNAL_SEP and no PROCESS_EXPAND, then there's nothing to do. Check out this "null terminated string." */
|
||||
const wchar_t some_chars[] = {INTERNAL_SEPARATOR, PROCESS_EXPAND, L'\0'};
|
||||
if (instr_with_sep.find_first_of(some_chars) == wcstring::npos)
|
||||
{
|
||||
/* Nothing to do */
|
||||
append_completion(out, instr_with_sep);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* expand_string calls us with internal separators in instr...sigh */
|
||||
wcstring instr = instr_with_sep;
|
||||
|
@ -1372,7 +1380,7 @@ static int expand_brackets(parser_t &parser, const wcstring &instr, int flags, s
|
|||
/**
|
||||
Perform cmdsubst expansion
|
||||
*/
|
||||
static int expand_cmdsubst(parser_t &parser, const wcstring &input, std::vector<completion_t> &outList)
|
||||
static int expand_cmdsubst(parser_t &parser, const wcstring &input, std::vector<completion_t> &out_list)
|
||||
{
|
||||
wchar_t *paran_begin=0, *paran_end=0;
|
||||
std::vector<wcstring> sub_res;
|
||||
|
@ -1390,7 +1398,7 @@ static int expand_cmdsubst(parser_t &parser, const wcstring &input, std::vector<
|
|||
L"Mismatched parenthesis");
|
||||
return 0;
|
||||
case 0:
|
||||
outList.push_back(completion_t(input));
|
||||
append_completion(out_list, input);
|
||||
return 1;
|
||||
case 1:
|
||||
|
||||
|
@ -1455,15 +1463,15 @@ static int expand_cmdsubst(parser_t &parser, const wcstring &input, std::vector<
|
|||
*/
|
||||
for (i=0; i<sub_res.size(); i++)
|
||||
{
|
||||
wcstring sub_item = sub_res.at(i);
|
||||
wcstring sub_item2 = escape_string(sub_item, 1);
|
||||
|
||||
for (j=0; j < tail_expand.size(); j++)
|
||||
{
|
||||
const wcstring &sub_item = sub_res.at(i);
|
||||
const wcstring sub_item2 = escape_string(sub_item, 1);
|
||||
|
||||
wcstring whole_item;
|
||||
|
||||
wcstring tail_item = tail_expand.at(j).completion;
|
||||
for (j=0; j < tail_expand.size(); j++)
|
||||
{
|
||||
whole_item.clear();
|
||||
const wcstring &tail_item = tail_expand.at(j).completion;
|
||||
|
||||
//sb_append_substring( &whole_item, in, len1 );
|
||||
whole_item.append(in, paran_begin-in);
|
||||
|
@ -1481,7 +1489,7 @@ static int expand_cmdsubst(parser_t &parser, const wcstring &input, std::vector<
|
|||
whole_item.append(tail_item);
|
||||
|
||||
//al_push( out, whole_item.buff );
|
||||
outList.push_back(completion_t(whole_item));
|
||||
append_completion(out_list, whole_item);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1602,6 +1610,31 @@ static void unexpand_tildes(const wcstring &input, std::vector<completion_t> *co
|
|||
}
|
||||
}
|
||||
|
||||
// If the given path contains the user's home directory, replace that with a tilde
|
||||
// We don't try to be smart about case insensitivity, etc.
|
||||
wcstring replace_home_directory_with_tilde(const wcstring &str)
|
||||
{
|
||||
// only absolute paths get this treatment
|
||||
wcstring result = str;
|
||||
if (string_prefixes_string(L"/", result))
|
||||
{
|
||||
wcstring home_directory = L"~";
|
||||
expand_tilde(home_directory);
|
||||
if (! string_suffixes_string(L"/", home_directory))
|
||||
{
|
||||
home_directory.push_back(L'/');
|
||||
}
|
||||
|
||||
// Now check if the home_directory prefixes the string
|
||||
if (string_prefixes_string(home_directory, result))
|
||||
{
|
||||
// Success
|
||||
result.replace(0, home_directory.size(), L"~/");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
Remove any internal separators. Also optionally convert wildcard characters to
|
||||
regular equivalents. This is done to support EXPAND_SKIP_WILDCARDS.
|
||||
|
@ -1640,7 +1673,7 @@ int expand_string(const wcstring &input, std::vector<completion_t> &output, expa
|
|||
|
||||
if ((!(flags & ACCEPT_INCOMPLETE)) && expand_is_clean(input.c_str()))
|
||||
{
|
||||
output.push_back(completion_t(input));
|
||||
append_completion(output, input);
|
||||
return EXPAND_OK;
|
||||
}
|
||||
|
||||
|
@ -1656,7 +1689,7 @@ int expand_string(const wcstring &input, std::vector<completion_t> &output, expa
|
|||
parser.error(CMDSUBST_ERROR, -1, L"Command substitutions not allowed");
|
||||
return EXPAND_ERROR;
|
||||
}
|
||||
in->push_back(completion_t(input));
|
||||
append_completion(*in, input);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1684,7 +1717,7 @@ int expand_string(const wcstring &input, std::vector<completion_t> &output, expa
|
|||
next[i] = L'$';
|
||||
}
|
||||
}
|
||||
out->push_back(completion_t(next));
|
||||
append_completion(*out, next);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1700,7 +1733,7 @@ int expand_string(const wcstring &input, std::vector<completion_t> &output, expa
|
|||
|
||||
for (i=0; i < in->size(); i++)
|
||||
{
|
||||
wcstring next = in->at(i).completion;
|
||||
const wcstring &next = in->at(i).completion;
|
||||
|
||||
if (!expand_brackets(parser, next, flags, *out))
|
||||
{
|
||||
|
@ -1720,7 +1753,7 @@ int expand_string(const wcstring &input, std::vector<completion_t> &output, expa
|
|||
|
||||
if (flags & ACCEPT_INCOMPLETE)
|
||||
{
|
||||
if (next[0] == PROCESS_EXPAND)
|
||||
if (! next.empty() && next.at(0) == PROCESS_EXPAND)
|
||||
{
|
||||
/*
|
||||
If process expansion matches, we are not
|
||||
|
@ -1733,7 +1766,7 @@ int expand_string(const wcstring &input, std::vector<completion_t> &output, expa
|
|||
}
|
||||
else
|
||||
{
|
||||
out->push_back(completion_t(next));
|
||||
append_completion(*out, next);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1815,7 +1848,7 @@ int expand_string(const wcstring &input, std::vector<completion_t> &output, expa
|
|||
{
|
||||
if (!(flags & ACCEPT_INCOMPLETE))
|
||||
{
|
||||
out->push_back(completion_t(next_str));
|
||||
append_completion(*out, next_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1842,7 +1875,7 @@ bool expand_one(wcstring &string, expand_flags_t flags)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (expand_string(string, completions, flags))
|
||||
if (expand_string(string, completions, flags | EXPAND_NO_DESCRIPTIONS))
|
||||
{
|
||||
if (completions.size() == 1)
|
||||
{
|
||||
|
@ -1945,19 +1978,19 @@ bool fish_openSUSE_dbus_hack_hack_hack_hack(std::vector<completion_t> *args)
|
|||
val.resize(last_good + 1);
|
||||
|
||||
args->clear();
|
||||
args->push_back(completion_t(L"set"));
|
||||
append_completion(*args, L"set");
|
||||
if (key == L"DBUS_SESSION_BUS_ADDRESS")
|
||||
args->push_back(completion_t(L"-x"));
|
||||
args->push_back(completion_t(key));
|
||||
args->push_back(completion_t(val));
|
||||
append_completion(*args, L"-x");
|
||||
append_completion(*args, key);
|
||||
append_completion(*args, val);
|
||||
result = true;
|
||||
}
|
||||
else if (string_prefixes_string(L"export DBUS_SESSION_BUS_ADDRESS;", cmd))
|
||||
{
|
||||
/* Nothing, we already exported it */
|
||||
args->clear();
|
||||
args->push_back(completion_t(L"echo"));
|
||||
args->push_back(completion_t(L"-n"));
|
||||
append_completion(*args, L"echo");
|
||||
append_completion(*args, L"-n");
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
|
3
expand.h
3
expand.h
|
@ -176,6 +176,9 @@ wcstring expand_escape_variable(const wcstring &in);
|
|||
*/
|
||||
void expand_tilde(wcstring &input);
|
||||
|
||||
/** Perform the opposite of tilde expansion on the string, which is modified in place */
|
||||
wcstring replace_home_directory_with_tilde(const wcstring &str);
|
||||
|
||||
/**
|
||||
Test if the specified argument is clean, i.e. it does not contain
|
||||
any tokens which need to be expanded or otherwise altered. Clean
|
||||
|
|
2
fish.cpp
2
fish.cpp
|
@ -12,7 +12,7 @@ GNU General Public License for more details.
|
|||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
|
|
221
fish.spec.in
221
fish.spec.in
|
@ -1,221 +0,0 @@
|
|||
Summary: A friendly interactive shell
|
||||
Name: @PACKAGE_NAME@
|
||||
|
||||
Version: @PACKAGE_VERSION@
|
||||
Release: 0%{?dist}
|
||||
|
||||
License: GPL
|
||||
Group: System Environment/Shells
|
||||
URL: http://fishshell.com/
|
||||
|
||||
Source0: http://ridiculousfish.com/shell/files/%{version}/%{name}-%{version}.tar.bz2
|
||||
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
||||
BuildRequires: ncurses-devel gettext groff
|
||||
|
||||
|
||||
# Locate correct build time-dependencies for providing X headers
|
||||
%if "%fedora" >= "5"
|
||||
|
||||
# Modern Fedora version, has modular X.org
|
||||
BuildRequires: xorg-x11-proto-devel libX11-devel libXt-devel libXext-devel
|
||||
|
||||
%endif
|
||||
|
||||
%if "%fedora" < "5"
|
||||
%if "%fedora" >= "3"
|
||||
|
||||
# Semi-old Fedora version, has non-modular X.org
|
||||
BuildRequires: xorg-x11-devel
|
||||
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?fedora}
|
||||
%if "%fedora" < "3"
|
||||
|
||||
# Ancient Fedora version, has XFree86
|
||||
BuildRequires: XFree86-devel
|
||||
|
||||
%endif
|
||||
%else
|
||||
|
||||
# The %fedora variable has not been correctly defined, or this is is
|
||||
# not a Fedora system, try guessing BuildRequires by looking at the
|
||||
# directory structure
|
||||
%define xinclude /usr%(if [ -d /usr/X11R6/include ]; then echo /X11R6; fi)/include
|
||||
BuildRequires: %{xinclude}/X11/StringDefs.h, %{xinclude}/X11/Xlib.h
|
||||
BuildRequires: %{xinclude}/X11/Intrinsic.h, %{xinclude}/X11/Xatom.h
|
||||
|
||||
%endif
|
||||
|
||||
|
||||
%description
|
||||
|
||||
fish is a shell geared towards interactive use. Its features are
|
||||
focused on user friendliness and discoverability. The language syntax
|
||||
is simple but incompatible with other shell languages.
|
||||
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
|
||||
|
||||
|
||||
%build
|
||||
# The docdir argument is to make the name of the cosumantation
|
||||
# directory 'fish-VERSION', instead of the default, which is simply
|
||||
# 'fish'.
|
||||
%configure docdir=%_datadir/doc/%{name}-%{version}
|
||||
make %{?_smp_mflags}
|
||||
|
||||
|
||||
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
make install DESTDIR="$RPM_BUILD_ROOT"
|
||||
|
||||
# Find translation files
|
||||
%find_lang %{name}.\*
|
||||
|
||||
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
|
||||
|
||||
|
||||
%post
|
||||
# Add fish to the list of allowed shells in /etc/shells
|
||||
if ! grep %_bindir/fish %_sysconfdir/shells >/dev/null; then
|
||||
echo %_bindir/fish >>%_sysconfdir/shells
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
%postun
|
||||
# Remove fish from the list of allowed shells in /etc/shells
|
||||
if [ "$1" = 0 ]; then
|
||||
grep -v %_bindir/fish %_sysconfdir/shells >%_sysconfdir/fish.tmp
|
||||
mv %_sysconfdir/fish.tmp %_sysconfdir/shells
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
%files -f %{name}.\*.lang
|
||||
|
||||
%defattr(-,root,root,-)
|
||||
|
||||
# The documentation directory
|
||||
%doc %_datadir/doc/%{name}-%{version}
|
||||
|
||||
# man files
|
||||
%_mandir/man1/fish.1*
|
||||
%_mandir/man1/fish_pager.1*
|
||||
%_mandir/man1/fish_indent.1*
|
||||
%_mandir/man1/fishd.1*
|
||||
%_mandir/man1/mimedb.1*
|
||||
%_mandir/man1/set_color.1*
|
||||
|
||||
# The program binaries
|
||||
%attr(0755,root,root) %_bindir/fish
|
||||
%attr(0755,root,root) %_bindir/fish_indent
|
||||
%attr(0755,root,root) %_bindir/fish_pager
|
||||
%attr(0755,root,root) %_bindir/fishd
|
||||
%attr(0755,root,root) %_bindir/mimedb
|
||||
%attr(0755,root,root) %_bindir/set_color
|
||||
|
||||
# Configuration files
|
||||
%config %_sysconfdir/fish/config.fish
|
||||
%dir %_sysconfdir/fish
|
||||
|
||||
# Non-configuration initialization files
|
||||
%dir %_datadir/fish
|
||||
%_datadir/fish/config.fish
|
||||
|
||||
# Program specific tab-completions
|
||||
%dir %_datadir/fish/completions
|
||||
%_datadir/fish/completions/*.fish
|
||||
|
||||
# Dynamically loaded shellscript functions
|
||||
%dir %_datadir/fish/functions
|
||||
%_datadir/fish/functions/*.fish
|
||||
|
||||
# Documentation for builtins and shellscript functions
|
||||
%dir %_datadir/fish/man
|
||||
%_datadir/fish/man/*.1
|
||||
|
||||
|
||||
|
||||
%changelog
|
||||
* Sat Apr 21 2007 Axel Liljencrantz<axel@liljencrantz.se> 1.23.0-0
|
||||
- Add fish_indent command
|
||||
|
||||
* Thu Feb 8 2007 Axel Liljencrantz<axel@liljencrantz.se> 1.22.3-0
|
||||
- Tell rpm about the help pages in %_datadir/fish/man/
|
||||
|
||||
* Sat Oct 14 2006 Axel Liljencrantz<axel@liljencrantz.se> 1.22.0-0
|
||||
- Update names of various configuration files
|
||||
|
||||
* Fri Aug 4 2006 Axel Liljencrantz<axel@liljencrantz.se> 1.21.10-4
|
||||
- Add better translation finding code from fedora spec to main spec. Thank you to Michael Schwendt.
|
||||
- Add missing dependency libXext-devel.
|
||||
- Remove one nesting level from dependency checking code.
|
||||
|
||||
* Tue Aug 1 2006 Axel Liljencrantz<axel@liljencrantz.se> 1.21.10-1
|
||||
- Improved the dependency check for X headers. Thank you to Michael Schwendt for pointers on how to do this
|
||||
|
||||
* Mon Jul 31 2006 Axel Liljencrantz<axel@liljencrantz.se> 1.21.10-1
|
||||
- Fixed spelling and punctuation as a per patch from Paul Howarth
|
||||
- Fixed dependencies as per patch from Paul Howarth
|
||||
|
||||
* Tue Nov 29 2005 Axel Liljencrantz <axel@liljencrantz.se> 1.17.0-0
|
||||
- 1.17.0
|
||||
|
||||
* Sat Sep 24 2005 Axel Liljencrantz <axel@liljencrantz.se> 1.14.0-0
|
||||
- 1.14.0
|
||||
|
||||
* Mon Sep 12 2005 Axel Liljencrantz <axel@liljencrantz.se> 1.13.4-0
|
||||
- 1.13.4
|
||||
|
||||
* Wed Sep 07 2005 Axel Liljencrantz <axel@liljencrantz.se> 1.13.3-0
|
||||
- 1.13.3
|
||||
|
||||
* Tue Sep 06 2005 Axel Liljencrantz <axel@liljencrantz.se> 1.13.2-0
|
||||
- 1.13.2
|
||||
|
||||
* Fri Aug 30 2005 Axel Liljencrantz <axel@liljencrantz.se> 1.13.1-0
|
||||
- 1.13.1
|
||||
|
||||
* Sun Aug 28 2005 Axel Liljencrantz <axel@liljencrantz.se> 1.13.0-0
|
||||
- 1.13.0
|
||||
|
||||
* Sat Aug 13 2005 Axel Liljencrantz <axel@liljencrantz.se> 1.13.0-0
|
||||
- Add completions subdirectory
|
||||
|
||||
* Thu Jul 28 2005 Axel Liljencrantz <axel@liljencrantz.se> 1.12.1-0
|
||||
- 1.12.1
|
||||
|
||||
* Fri Jul 15 2005 Axel Liljencrantz <axel@liljencrantz.se> 1.12.0-1
|
||||
- 1.12.0
|
||||
|
||||
* Thu Jun 30 2005 Michael Schwendt <mschwendt@users.sf.net> 1.11.1-9
|
||||
- Set CFLAGS the proper way
|
||||
|
||||
* Thu Jun 30 2005 Axel Liljencrantz <axel@liljencrantz.se> 1.11.1-8
|
||||
- Fix revision number in changelog
|
||||
|
||||
* Wed Jun 29 2005 Axel Liljencrantz <axel@liljencrantz.se> 1.11.1-7
|
||||
- Send post-script output to /dev/null
|
||||
|
||||
* Wed Jun 29 2005 Axel Liljencrantz <axel@liljencrantz.se> 1.11.1-6
|
||||
- Add changelog section to spec file
|
||||
- Add macros to source tags
|
||||
- Add smp_mflags to 'make all'
|
||||
- Fix typo in post install scriptlet test
|
||||
- Set CFLAGS from spec file
|
|
@ -66,6 +66,7 @@
|
|||
D033781115DC6D4C00A634BA /* completions in CopyFiles */ = {isa = PBXBuildFile; fileRef = D025C02715D1FEA100B9DB63 /* completions */; };
|
||||
D033781215DC6D5200A634BA /* functions in CopyFiles */ = {isa = PBXBuildFile; fileRef = D025C02815D1FEA100B9DB63 /* functions */; };
|
||||
D033781315DC6D5400A634BA /* tools in CopyFiles */ = {isa = PBXBuildFile; fileRef = D025C02915D1FEA100B9DB63 /* tools */; };
|
||||
D052D80B1868F7FC003ABCBD /* parse_execution.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D052D8091868F7FC003ABCBD /* parse_execution.cpp */; };
|
||||
D07B247315BCC15700D4ADB4 /* add-shell in Resources */ = {isa = PBXBuildFile; fileRef = D07B247215BCC15700D4ADB4 /* add-shell */; };
|
||||
D07B247615BCC4BE00D4ADB4 /* install.sh in Resources */ = {isa = PBXBuildFile; fileRef = D07B247515BCC4BE00D4ADB4 /* install.sh */; };
|
||||
D07D266A15E33B86009E43F6 /* config.fish in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0C4FD9415A7D7EE00212EF1 /* config.fish */; };
|
||||
|
@ -74,8 +75,50 @@
|
|||
D07D266E15E33B86009E43F6 /* tools in Copy Files */ = {isa = PBXBuildFile; fileRef = D025C02915D1FEA100B9DB63 /* tools */; };
|
||||
D07D267215E34171009E43F6 /* config.fish in Copy Files */ = {isa = PBXBuildFile; fileRef = D0CBD580159EE48F0024809C /* config.fish */; };
|
||||
D0879AC816BF9AAB00E98E56 /* fish_term_icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = D0879AC616BF9A1A00E98E56 /* fish_term_icon.icns */; };
|
||||
D08A329417B4458D00F3A533 /* fish_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D08A329317B4458D00F3A533 /* fish_tests.cpp */; };
|
||||
D08A329517B445C200F3A533 /* function.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854413B3ACEE0099B651 /* function.cpp */; };
|
||||
D08A329617B445FD00F3A533 /* builtin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853513B3ACEE0099B651 /* builtin.cpp */; };
|
||||
D08A329717B4463B00F3A533 /* complete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853713B3ACEE0099B651 /* complete.cpp */; };
|
||||
D08A329817B4463B00F3A533 /* env.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853A13B3ACEE0099B651 /* env.cpp */; };
|
||||
D08A329917B4463B00F3A533 /* exec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853C13B3ACEE0099B651 /* exec.cpp */; };
|
||||
D08A329A17B4463B00F3A533 /* expand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853D13B3ACEE0099B651 /* expand.cpp */; };
|
||||
D08A329B17B4463B00F3A533 /* highlight.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854713B3ACEE0099B651 /* highlight.cpp */; };
|
||||
D08A329C17B4463B00F3A533 /* history.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854813B3ACEE0099B651 /* history.cpp */; };
|
||||
D08A329D17B4463B00F3A533 /* kill.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854F13B3ACEE0099B651 /* kill.cpp */; };
|
||||
D08A329E17B4463B00F3A533 /* parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855413B3ACEE0099B651 /* parser.cpp */; };
|
||||
D08A329F17B4463B00F3A533 /* proc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855713B3ACEE0099B651 /* proc.cpp */; };
|
||||
D08A32A017B4463B00F3A533 /* reader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855813B3ACEE0099B651 /* reader.cpp */; };
|
||||
D08A32A117B4463B00F3A533 /* sanity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855913B3ACEE0099B651 /* sanity.cpp */; };
|
||||
D08A32A217B4463B00F3A533 /* tokenizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855D13B3ACEE0099B651 /* tokenizer.cpp */; };
|
||||
D08A32A317B4463B00F3A533 /* wgetopt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855F13B3ACEE0099B651 /* wgetopt.cpp */; };
|
||||
D08A32A417B4463B00F3A533 /* wildcard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0856013B3ACEE0099B651 /* wildcard.cpp */; };
|
||||
D08A32A517B4463B00F3A533 /* wutil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0856113B3ACEE0099B651 /* wutil.cpp */; };
|
||||
D08A32A617B4464300F3A533 /* input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854A13B3ACEE0099B651 /* input.cpp */; };
|
||||
D08A32A717B446A300F3A533 /* autoload.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C6FCC914CFA4B0004CE8AD /* autoload.cpp */; };
|
||||
D08A32A817B446A300F3A533 /* builtin_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0F3373A1506DE3C00ECEFC0 /* builtin_test.cpp */; };
|
||||
D08A32A917B446A300F3A533 /* color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0B6B0FE14E88BA400AD6C10 /* color.cpp */; };
|
||||
D08A32AA17B446A300F3A533 /* common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853613B3ACEE0099B651 /* common.cpp */; };
|
||||
D08A32AB17B446A300F3A533 /* env_universal_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853813B3ACEE0099B651 /* env_universal_common.cpp */; };
|
||||
D08A32AC17B446A300F3A533 /* env_universal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853913B3ACEE0099B651 /* env_universal.cpp */; };
|
||||
D08A32AD17B446A300F3A533 /* event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853B13B3ACEE0099B651 /* event.cpp */; };
|
||||
D08A32AE17B446A300F3A533 /* input_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854913B3ACEE0099B651 /* input_common.cpp */; };
|
||||
D08A32AF17B446A300F3A533 /* intern.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854B13B3ACEE0099B651 /* intern.cpp */; };
|
||||
D08A32B017B446A300F3A533 /* io.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854C13B3ACEE0099B651 /* io.cpp */; };
|
||||
D08A32B117B446A300F3A533 /* iothread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854D13B3ACEE0099B651 /* iothread.cpp */; };
|
||||
D08A32B217B446A300F3A533 /* output.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855113B3ACEE0099B651 /* output.cpp */; };
|
||||
D08A32B317B446A300F3A533 /* parse_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855213B3ACEE0099B651 /* parse_util.cpp */; };
|
||||
D08A32B417B446A300F3A533 /* parser_keywords.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855313B3ACEE0099B651 /* parser_keywords.cpp */; };
|
||||
D08A32B517B446A300F3A533 /* path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855513B3ACEE0099B651 /* path.cpp */; };
|
||||
D08A32B617B446A300F3A533 /* postfork.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D09B1C1914FC7B5B00F91077 /* postfork.cpp */; };
|
||||
D08A32B717B446A300F3A533 /* screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855A13B3ACEE0099B651 /* screen.cpp */; };
|
||||
D08A32B817B446A300F3A533 /* signal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855C13B3ACEE0099B651 /* signal.cpp */; };
|
||||
D08A32B917B446B100F3A533 /* parse_productions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0FE8EE7179FB75F008C9F21 /* parse_productions.cpp */; };
|
||||
D08A32BA17B446B100F3A533 /* parse_tree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C52F351765284C00BFAB82 /* parse_tree.cpp */; };
|
||||
D08A32BC17B4473B00F3A533 /* libncurses.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D02A8C15983CFA008E62BD /* libncurses.dylib */; };
|
||||
D08A32BD17B4474000F3A533 /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D02A8A15983CDF008E62BD /* libiconv.dylib */; };
|
||||
D0A564FE168D23D800AF6161 /* man in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0A564F1168D0BAB00AF6161 /* man */; };
|
||||
D0A56501168D258300AF6161 /* man in Copy Files */ = {isa = PBXBuildFile; fileRef = D0A564F1168D0BAB00AF6161 /* man */; };
|
||||
D0C52F371765284C00BFAB82 /* parse_tree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C52F351765284C00BFAB82 /* parse_tree.cpp */; };
|
||||
D0CBD587159EF0E10024809C /* launch_fish.scpt in Resources */ = {isa = PBXBuildFile; fileRef = D0CBD586159EF0E10024809C /* launch_fish.scpt */; };
|
||||
D0D02A67159837AD008E62BD /* complete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853713B3ACEE0099B651 /* complete.cpp */; };
|
||||
D0D02A69159837B2008E62BD /* env.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853A13B3ACEE0099B651 /* env.cpp */; };
|
||||
|
@ -151,6 +194,7 @@
|
|||
D0F019FD15A977CA0034B3B1 /* config.fish in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0C4FD9415A7D7EE00212EF1 /* config.fish */; };
|
||||
D0F01A0315A978910034B3B1 /* osx_fish_launcher.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D02AFA159871B2008E62BD /* osx_fish_launcher.m */; };
|
||||
D0F01A0515A978A10034B3B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0CBD583159EEE010024809C /* Foundation.framework */; };
|
||||
D0FE8EE8179FB760008C9F21 /* parse_productions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0FE8EE7179FB75F008C9F21 /* parse_productions.cpp */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
@ -293,6 +337,15 @@
|
|||
name = "Copy Files";
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
D08A328B17B4455100F3A533 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = /usr/share/man/man1/;
|
||||
dstSubfolderSpec = 0;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
D0F019F015A977010034B3B1 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -337,9 +390,13 @@
|
|||
D03238891849D1980032CF2C /* pager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pager.cpp; sourceTree = "<group>"; };
|
||||
D032388A1849D1980032CF2C /* pager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pager.h; sourceTree = "<group>"; };
|
||||
D03EE83814DF88B200FC7150 /* lru.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = lru.h; sourceTree = "<group>"; };
|
||||
D052D8091868F7FC003ABCBD /* parse_execution.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = parse_execution.cpp; sourceTree = "<group>"; };
|
||||
D052D80A1868F7FC003ABCBD /* parse_execution.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parse_execution.h; sourceTree = "<group>"; };
|
||||
D07B247215BCC15700D4ADB4 /* add-shell */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = "add-shell"; path = "build_tools/osx_package_scripts/add-shell"; sourceTree = "<group>"; };
|
||||
D07B247515BCC4BE00D4ADB4 /* install.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = install.sh; path = osx/install.sh; sourceTree = "<group>"; };
|
||||
D0879AC616BF9A1A00E98E56 /* fish_term_icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = fish_term_icon.icns; path = osx/fish_term_icon.icns; sourceTree = "<group>"; };
|
||||
D08A328D17B4455100F3A533 /* fish_tests */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fish_tests; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D08A329317B4458D00F3A533 /* fish_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fish_tests.cpp; sourceTree = "<group>"; };
|
||||
D09B1C1914FC7B5B00F91077 /* postfork.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = postfork.cpp; sourceTree = "<group>"; };
|
||||
D09B1C1A14FC7B5B00F91077 /* postfork.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = postfork.h; sourceTree = "<group>"; };
|
||||
D0A0850313B3ACEE0099B651 /* builtin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin.h; sourceTree = "<group>"; };
|
||||
|
@ -444,6 +501,8 @@
|
|||
D0B6B0FE14E88BA400AD6C10 /* color.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = color.cpp; sourceTree = "<group>"; };
|
||||
D0B6B0FF14E88BA400AD6C10 /* color.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = color.h; sourceTree = "<group>"; };
|
||||
D0C4FD9415A7D7EE00212EF1 /* config.fish */ = {isa = PBXFileReference; lastKnownFileType = text; name = config.fish; path = etc/config.fish; sourceTree = "<group>"; };
|
||||
D0C52F351765284C00BFAB82 /* parse_tree.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = parse_tree.cpp; sourceTree = "<group>"; };
|
||||
D0C52F361765284C00BFAB82 /* parse_tree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parse_tree.h; sourceTree = "<group>"; };
|
||||
D0C6FCC914CFA4B0004CE8AD /* autoload.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = autoload.cpp; sourceTree = "<group>"; };
|
||||
D0C6FCCB14CFA4B7004CE8AD /* autoload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = autoload.h; sourceTree = "<group>"; };
|
||||
D0C861EA16CC7054003B5A04 /* builtin_set_color.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_set_color.cpp; sourceTree = "<group>"; };
|
||||
|
@ -460,11 +519,23 @@
|
|||
D0D02AE415986537008E62BD /* fish_pager */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fish_pager; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D0D02AFA159871B2008E62BD /* osx_fish_launcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = osx_fish_launcher.m; path = osx/osx_fish_launcher.m; sourceTree = "<group>"; };
|
||||
D0D2693C159835CA005D9B9C /* fish */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fish; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D0D9B2B318555D92001AE279 /* parse_constants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = parse_constants.h; sourceTree = "<group>"; };
|
||||
D0F3373A1506DE3C00ECEFC0 /* builtin_test.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_test.cpp; sourceTree = "<group>"; };
|
||||
D0F5E28415A7A32D00315DFF /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = "<group>"; };
|
||||
D0FE8EE6179CA8A5008C9F21 /* parse_productions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parse_productions.h; sourceTree = "<group>"; };
|
||||
D0FE8EE7179FB75F008C9F21 /* parse_productions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = parse_productions.cpp; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
D08A328A17B4455100F3A533 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D08A32BD17B4474000F3A533 /* libiconv.dylib in Frameworks */,
|
||||
D08A32BC17B4473B00F3A533 /* libncurses.dylib in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D0D02AB915985EF9008E62BD /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -528,6 +599,13 @@
|
|||
name = "Other Build Products";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D08A328E17B4455100F3A533 /* fish_tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
path = fish_tests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D0A084F013B3AC130099B651 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -537,6 +615,7 @@
|
|||
D0D02A8E15983D5F008E62BD /* Libraries */,
|
||||
D0D02AAB15985C14008E62BD /* Resources */,
|
||||
D031890A15E36DB500D9CC39 /* Other Build Products */,
|
||||
D08A328E17B4455100F3A533 /* fish_tests */,
|
||||
D0D2693215983562005D9B9C /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
|
@ -585,6 +664,13 @@
|
|||
D0A0853C13B3ACEE0099B651 /* exec.cpp */,
|
||||
D0A0850C13B3ACEE0099B651 /* expand.h */,
|
||||
D0A0853D13B3ACEE0099B651 /* expand.cpp */,
|
||||
D0D9B2B318555D92001AE279 /* parse_constants.h */,
|
||||
D0FE8EE6179CA8A5008C9F21 /* parse_productions.h */,
|
||||
D0FE8EE7179FB75F008C9F21 /* parse_productions.cpp */,
|
||||
D0C52F361765284C00BFAB82 /* parse_tree.h */,
|
||||
D0C52F351765284C00BFAB82 /* parse_tree.cpp */,
|
||||
D052D80A1868F7FC003ABCBD /* parse_execution.h */,
|
||||
D052D8091868F7FC003ABCBD /* parse_execution.cpp */,
|
||||
D0A0850D13B3ACEE0099B651 /* fallback.h */,
|
||||
D0A0853E13B3ACEE0099B651 /* fallback.cpp */,
|
||||
D0A0850E13B3ACEE0099B651 /* function.h */,
|
||||
|
@ -662,6 +748,7 @@
|
|||
D0A0856613B3ACEE0099B651 /* xdgmimemagic.cpp */,
|
||||
D0A0852F13B3ACEE0099B651 /* xdgmimeparent.h */,
|
||||
D0A0856713B3ACEE0099B651 /* xdgmimeparent.cpp */,
|
||||
D08A329317B4458D00F3A533 /* fish_tests.cpp */,
|
||||
);
|
||||
name = Sources;
|
||||
sourceTree = "<group>";
|
||||
|
@ -703,6 +790,7 @@
|
|||
D0D02ABC15985EF9008E62BD /* fishd */,
|
||||
D0D02AD01598642A008E62BD /* fish_indent */,
|
||||
D0D02AE415986537008E62BD /* fish_pager */,
|
||||
D08A328D17B4455100F3A533 /* fish_tests */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
|
@ -735,6 +823,23 @@
|
|||
/* End PBXLegacyTarget section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
D08A328C17B4455100F3A533 /* fish_tests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = D08A329217B4455100F3A533 /* Build configuration list for PBXNativeTarget "fish_tests" */;
|
||||
buildPhases = (
|
||||
D08A328917B4455100F3A533 /* Sources */,
|
||||
D08A328A17B4455100F3A533 /* Frameworks */,
|
||||
D08A328B17B4455100F3A533 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = fish_tests;
|
||||
productName = fish_tests;
|
||||
productReference = D08A328D17B4455100F3A533 /* fish_tests */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
D0D02A9915985A75008E62BD /* fish.app */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = D0D02AA415985A75008E62BD /* Build configuration list for PBXNativeTarget "fish.app" */;
|
||||
|
@ -844,6 +949,7 @@
|
|||
D0D02ABB15985EF9008E62BD /* fishd */,
|
||||
D0D02ACF1598642A008E62BD /* fish_indent */,
|
||||
D0D02AE315986537008E62BD /* fish_pager */,
|
||||
D08A328C17B4455100F3A533 /* fish_tests */,
|
||||
D0A564E6168CFDD800AF6161 /* man_pages */,
|
||||
D0A084F713B3AC130099B651 /* Makefile */,
|
||||
);
|
||||
|
@ -1024,6 +1130,52 @@
|
|||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
D08A328917B4455100F3A533 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D08A32B917B446B100F3A533 /* parse_productions.cpp in Sources */,
|
||||
D08A32BA17B446B100F3A533 /* parse_tree.cpp in Sources */,
|
||||
D08A32A717B446A300F3A533 /* autoload.cpp in Sources */,
|
||||
D08A32A817B446A300F3A533 /* builtin_test.cpp in Sources */,
|
||||
D08A32A917B446A300F3A533 /* color.cpp in Sources */,
|
||||
D08A32AA17B446A300F3A533 /* common.cpp in Sources */,
|
||||
D08A32AB17B446A300F3A533 /* env_universal_common.cpp in Sources */,
|
||||
D08A32AC17B446A300F3A533 /* env_universal.cpp in Sources */,
|
||||
D08A32AD17B446A300F3A533 /* event.cpp in Sources */,
|
||||
D08A32AE17B446A300F3A533 /* input_common.cpp in Sources */,
|
||||
D08A32AF17B446A300F3A533 /* intern.cpp in Sources */,
|
||||
D08A32B017B446A300F3A533 /* io.cpp in Sources */,
|
||||
D08A32B117B446A300F3A533 /* iothread.cpp in Sources */,
|
||||
D08A32B217B446A300F3A533 /* output.cpp in Sources */,
|
||||
D08A32B317B446A300F3A533 /* parse_util.cpp in Sources */,
|
||||
D08A32B417B446A300F3A533 /* parser_keywords.cpp in Sources */,
|
||||
D08A32B517B446A300F3A533 /* path.cpp in Sources */,
|
||||
D08A32B617B446A300F3A533 /* postfork.cpp in Sources */,
|
||||
D08A32B717B446A300F3A533 /* screen.cpp in Sources */,
|
||||
D08A32B817B446A300F3A533 /* signal.cpp in Sources */,
|
||||
D08A32A617B4464300F3A533 /* input.cpp in Sources */,
|
||||
D08A329717B4463B00F3A533 /* complete.cpp in Sources */,
|
||||
D08A329817B4463B00F3A533 /* env.cpp in Sources */,
|
||||
D08A329917B4463B00F3A533 /* exec.cpp in Sources */,
|
||||
D08A329A17B4463B00F3A533 /* expand.cpp in Sources */,
|
||||
D08A329B17B4463B00F3A533 /* highlight.cpp in Sources */,
|
||||
D08A329C17B4463B00F3A533 /* history.cpp in Sources */,
|
||||
D08A329D17B4463B00F3A533 /* kill.cpp in Sources */,
|
||||
D08A329E17B4463B00F3A533 /* parser.cpp in Sources */,
|
||||
D08A329F17B4463B00F3A533 /* proc.cpp in Sources */,
|
||||
D08A32A017B4463B00F3A533 /* reader.cpp in Sources */,
|
||||
D08A32A117B4463B00F3A533 /* sanity.cpp in Sources */,
|
||||
D08A32A217B4463B00F3A533 /* tokenizer.cpp in Sources */,
|
||||
D08A32A317B4463B00F3A533 /* wgetopt.cpp in Sources */,
|
||||
D08A32A417B4463B00F3A533 /* wildcard.cpp in Sources */,
|
||||
D08A32A517B4463B00F3A533 /* wutil.cpp in Sources */,
|
||||
D08A329617B445FD00F3A533 /* builtin.cpp in Sources */,
|
||||
D08A329417B4458D00F3A533 /* fish_tests.cpp in Sources */,
|
||||
D08A329517B445C200F3A533 /* function.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D0D02AB815985EF9008E62BD /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -1080,6 +1232,7 @@
|
|||
D0D02A83159839D5008E62BD /* iothread.cpp in Sources */,
|
||||
D0D02A84159839D5008E62BD /* parse_util.cpp in Sources */,
|
||||
D0D02A85159839D5008E62BD /* path.cpp in Sources */,
|
||||
D052D80B1868F7FC003ABCBD /* parse_execution.cpp in Sources */,
|
||||
D0D02A86159839D5008E62BD /* postfork.cpp in Sources */,
|
||||
D0D02A87159839D5008E62BD /* screen.cpp in Sources */,
|
||||
D0D02A88159839D5008E62BD /* signal.cpp in Sources */,
|
||||
|
@ -1108,6 +1261,8 @@
|
|||
D0D02A7B15983928008E62BD /* env_universal_common.cpp in Sources */,
|
||||
D032388B1849D1980032CF2C /* pager.cpp in Sources */,
|
||||
D0D02A89159839DF008E62BD /* fish.cpp in Sources */,
|
||||
D0C52F371765284C00BFAB82 /* parse_tree.cpp in Sources */,
|
||||
D0FE8EE8179FB760008C9F21 /* parse_productions.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -1335,6 +1490,74 @@
|
|||
};
|
||||
name = Release;
|
||||
};
|
||||
D08A328F17B4455100F3A533 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
D08A329017B4455100F3A533 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
D08A329117B4455100F3A533 /* Release_C++11 */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = "Release_C++11";
|
||||
};
|
||||
D0A084F813B3AC130099B651 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
|
@ -1611,6 +1834,16 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
D08A329217B4455100F3A533 /* Build configuration list for PBXNativeTarget "fish_tests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
D08A328F17B4455100F3A533 /* Debug */,
|
||||
D08A329017B4455100F3A533 /* Release */,
|
||||
D08A329117B4455100F3A533 /* Release_C++11 */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
D0A084F513B3AC130099B651 /* Build configuration list for PBXProject "fish" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
|
|
@ -12,7 +12,7 @@ GNU General Public License for more details.
|
|||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
|
|
911
fish_tests.cpp
911
fish_tests.cpp
File diff suppressed because it is too large
Load diff
|
@ -186,10 +186,15 @@ void function_add(const function_data_t &data, const parser_t &parser)
|
|||
/* Remove the old function */
|
||||
function_remove(data.name);
|
||||
|
||||
|
||||
/* Create and store a new function */
|
||||
const wchar_t *filename = reader_current_filename();
|
||||
int def_offset = parser.line_number_of_character_at_offset(parser.current_block->tok_pos) - 1;
|
||||
|
||||
int def_offset = -1;
|
||||
if (parser.current_block() != NULL)
|
||||
{
|
||||
def_offset = parser.line_number_of_character_at_offset(parser.current_block()->tok_pos);
|
||||
}
|
||||
|
||||
const function_map_t::value_type new_pair(data.name, function_info_t(data, filename, def_offset, is_autoload));
|
||||
loaded_functions.insert(new_pair);
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ struct function_data_t
|
|||
/**
|
||||
Function definition
|
||||
*/
|
||||
wchar_t *definition;
|
||||
const wchar_t *definition;
|
||||
/**
|
||||
List of all event handlers for this function
|
||||
*/
|
||||
|
|
1023
highlight.cpp
1023
highlight.cpp
File diff suppressed because it is too large
Load diff
|
@ -84,6 +84,7 @@ struct file_detection_context_t;
|
|||
\param error a list in which a description of each error will be inserted. May be 0, in whcich case no error descriptions will be generated.
|
||||
*/
|
||||
void highlight_shell(const wcstring &buffstr, std::vector<int> &color, size_t pos, wcstring_list_t *error, const env_vars_snapshot_t &vars);
|
||||
void highlight_shell_new_parser(const wcstring &buffstr, std::vector<int> &color, size_t pos, wcstring_list_t *error, const env_vars_snapshot_t &vars);
|
||||
|
||||
/**
|
||||
Perform syntax highlighting for the text in buff. Matching quotes and paranthesis are highlighted. The result is
|
||||
|
@ -133,5 +134,9 @@ enum
|
|||
typedef unsigned int path_flags_t;
|
||||
bool is_potential_path(const wcstring &const_path, const wcstring_list_t &directories, path_flags_t flags, wcstring *out_path = NULL);
|
||||
|
||||
/* For testing */
|
||||
void highlight_shell_classic(const wcstring &buff, std::vector<int> &color, size_t pos, wcstring_list_t *error, const env_vars_snapshot_t &vars);
|
||||
void highlight_shell_new_parser(const wcstring &buff, std::vector<int> &color, size_t pos, wcstring_list_t *error, const env_vars_snapshot_t &vars);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ public:
|
|||
{
|
||||
return contents;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return contents.empty();
|
||||
|
|
259
parse_constants.h
Normal file
259
parse_constants.h
Normal file
|
@ -0,0 +1,259 @@
|
|||
/**\file parse_constants.h
|
||||
|
||||
Constants used in the programmatic representation of fish code.
|
||||
*/
|
||||
|
||||
#ifndef fish_parse_constants_h
|
||||
#define fish_parse_constants_h
|
||||
|
||||
#define PARSE_ASSERT(a) assert(a)
|
||||
#define PARSER_DIE() do { fprintf(stderr, "Parser dying!\n"); exit_without_destructors(-1); } while (0)
|
||||
|
||||
|
||||
enum parse_token_type_t
|
||||
{
|
||||
token_type_invalid,
|
||||
|
||||
// Non-terminal tokens
|
||||
symbol_job_list,
|
||||
symbol_job,
|
||||
symbol_job_continuation,
|
||||
symbol_statement,
|
||||
symbol_block_statement,
|
||||
symbol_block_header,
|
||||
symbol_for_header,
|
||||
symbol_while_header,
|
||||
symbol_begin_header,
|
||||
symbol_function_header,
|
||||
|
||||
symbol_if_statement,
|
||||
symbol_if_clause,
|
||||
symbol_else_clause,
|
||||
symbol_else_continuation,
|
||||
|
||||
symbol_switch_statement,
|
||||
symbol_case_item_list,
|
||||
symbol_case_item,
|
||||
|
||||
symbol_boolean_statement,
|
||||
symbol_decorated_statement,
|
||||
symbol_plain_statement,
|
||||
symbol_arguments_or_redirections_list,
|
||||
symbol_argument_or_redirection,
|
||||
|
||||
symbol_argument_list,
|
||||
|
||||
symbol_argument,
|
||||
symbol_redirection,
|
||||
|
||||
symbol_optional_background,
|
||||
|
||||
symbol_end_command,
|
||||
|
||||
// Terminal types
|
||||
parse_token_type_string,
|
||||
parse_token_type_pipe,
|
||||
parse_token_type_redirection,
|
||||
parse_token_type_background,
|
||||
parse_token_type_end,
|
||||
|
||||
// Special terminal type that means no more tokens forthcoming
|
||||
parse_token_type_terminate,
|
||||
|
||||
// Very special terminal types that don't appear in the production list
|
||||
parse_special_type_parse_error,
|
||||
parse_special_type_tokenizer_error,
|
||||
parse_special_type_comment,
|
||||
|
||||
FIRST_TERMINAL_TYPE = parse_token_type_string,
|
||||
LAST_TERMINAL_TYPE = parse_token_type_terminate,
|
||||
|
||||
LAST_TOKEN_OR_SYMBOL = parse_token_type_terminate,
|
||||
FIRST_PARSE_TOKEN_TYPE = parse_token_type_string
|
||||
};
|
||||
|
||||
enum parse_keyword_t
|
||||
{
|
||||
parse_keyword_none,
|
||||
parse_keyword_if,
|
||||
parse_keyword_else,
|
||||
parse_keyword_for,
|
||||
parse_keyword_in,
|
||||
parse_keyword_while,
|
||||
parse_keyword_begin,
|
||||
parse_keyword_function,
|
||||
parse_keyword_switch,
|
||||
parse_keyword_case,
|
||||
parse_keyword_end,
|
||||
parse_keyword_and,
|
||||
parse_keyword_or,
|
||||
parse_keyword_not,
|
||||
parse_keyword_command,
|
||||
parse_keyword_builtin,
|
||||
|
||||
LAST_KEYWORD = parse_keyword_builtin
|
||||
};
|
||||
|
||||
/* Statement decorations. This matches the order of productions in decorated_statement */
|
||||
enum parse_statement_decoration_t
|
||||
{
|
||||
parse_statement_decoration_none,
|
||||
parse_statement_decoration_command,
|
||||
parse_statement_decoration_builtin
|
||||
};
|
||||
|
||||
/* Parse error code list */
|
||||
enum parse_error_code_t
|
||||
{
|
||||
parse_error_none,
|
||||
|
||||
/* Matching values from enum parser_error */
|
||||
parse_error_syntax,
|
||||
parse_error_eval,
|
||||
parse_error_cmdsubst,
|
||||
|
||||
parse_error_generic, // unclassified error types
|
||||
|
||||
parse_error_tokenizer, //tokenizer error
|
||||
|
||||
parse_error_unbalancing_end, //end outside of block
|
||||
parse_error_unbalancing_else, //else outside of if
|
||||
parse_error_unbalancing_case, //case outside of switch
|
||||
|
||||
parse_error_double_pipe, // foo || bar, has special error message
|
||||
parse_error_double_background // foo && bar, has special error message
|
||||
};
|
||||
|
||||
enum {
|
||||
PARSER_TEST_ERROR = 1,
|
||||
PARSER_TEST_INCOMPLETE = 2
|
||||
};
|
||||
typedef unsigned int parser_test_error_bits_t;
|
||||
|
||||
|
||||
/** Maximum number of function calls. */
|
||||
#define FISH_MAX_STACK_DEPTH 128
|
||||
|
||||
/** Error message on a function that calls itself immediately */
|
||||
#define INFINITE_FUNC_RECURSION_ERR_MSG _( L"The function '%ls' calls itself immediately, which would result in an infinite loop.")
|
||||
|
||||
|
||||
/** Error message on reaching maximum call stack depth */
|
||||
#define CALL_STACK_LIMIT_EXCEEDED_ERR_MSG _( L"The function call stack limit has been exceeded. Do you have an accidental infinite loop?")
|
||||
|
||||
/** Error message when a non-string token is found when expecting a command name */
|
||||
#define CMD_OR_ERR_MSG _( L"Expected a command, but instead found a pipe. Did you mean 'COMMAND; or COMMAND'? See the help section for the 'or' builtin command by typing 'help or'.")
|
||||
|
||||
/** Error message when a non-string token is found when expecting a command name */
|
||||
#define CMD_AND_ERR_MSG _( L"Expected a command, but instead found a '&'. Did you mean 'COMMAND; and COMMAND'? See the help section for the 'and' builtin command by typing 'help and'.")
|
||||
|
||||
/** Error message when encountering an illegal command name */
|
||||
#define ILLEGAL_CMD_ERR_MSG _( L"Illegal command name '%ls'")
|
||||
|
||||
/** Error message when encountering an illegal file descriptor */
|
||||
#define ILLEGAL_FD_ERR_MSG _( L"Illegal file descriptor in redirection '%ls'")
|
||||
|
||||
/** Error message for wildcards with no matches */
|
||||
#define WILDCARD_ERR_MSG _( L"No matches for wildcard '%ls'.")
|
||||
|
||||
/** Error when using break outside of loop */
|
||||
#define INVALID_BREAK_ERR_MSG _( L"break command while not inside of loop" )
|
||||
|
||||
/** Error when using continue outside of loop */
|
||||
#define INVALID_CONTINUE_ERR_MSG _( L"continue command while not inside of loop" )
|
||||
|
||||
/** Error when using return builtin outside of function definition */
|
||||
#define INVALID_RETURN_ERR_MSG _( L"'return' builtin command outside of function definition" )
|
||||
|
||||
/** Error message for Posix-style assignment: foo=bar */
|
||||
#define COMMAND_ASSIGN_ERR_MSG _( L"Unknown command '%ls'. Did you mean 'set %ls %ls'? See the help section on the set command by typing 'help set'.")
|
||||
|
||||
/**
|
||||
While block description
|
||||
*/
|
||||
#define WHILE_BLOCK N_( L"'while' block" )
|
||||
|
||||
/**
|
||||
For block description
|
||||
*/
|
||||
#define FOR_BLOCK N_( L"'for' block" )
|
||||
|
||||
/**
|
||||
Breakpoint block
|
||||
*/
|
||||
#define BREAKPOINT_BLOCK N_( L"Block created by breakpoint" )
|
||||
|
||||
|
||||
|
||||
/**
|
||||
If block description
|
||||
*/
|
||||
#define IF_BLOCK N_( L"'if' conditional block" )
|
||||
|
||||
|
||||
/**
|
||||
Function definition block description
|
||||
*/
|
||||
#define FUNCTION_DEF_BLOCK N_( L"function definition block" )
|
||||
|
||||
|
||||
/**
|
||||
Function invocation block description
|
||||
*/
|
||||
#define FUNCTION_CALL_BLOCK N_( L"function invocation block" )
|
||||
|
||||
/**
|
||||
Function invocation block description
|
||||
*/
|
||||
#define FUNCTION_CALL_NO_SHADOW_BLOCK N_( L"function invocation block with no variable shadowing" )
|
||||
|
||||
|
||||
/**
|
||||
Switch block description
|
||||
*/
|
||||
#define SWITCH_BLOCK N_( L"'switch' block" )
|
||||
|
||||
|
||||
/**
|
||||
Fake block description
|
||||
*/
|
||||
#define FAKE_BLOCK N_( L"unexecutable block" )
|
||||
|
||||
|
||||
/**
|
||||
Top block description
|
||||
*/
|
||||
#define TOP_BLOCK N_( L"global root block" )
|
||||
|
||||
|
||||
/**
|
||||
Command substitution block description
|
||||
*/
|
||||
#define SUBST_BLOCK N_( L"command substitution block" )
|
||||
|
||||
|
||||
/**
|
||||
Begin block description
|
||||
*/
|
||||
#define BEGIN_BLOCK N_( L"'begin' unconditional block" )
|
||||
|
||||
|
||||
/**
|
||||
Source block description
|
||||
*/
|
||||
#define SOURCE_BLOCK N_( L"Block created by the . builtin" )
|
||||
|
||||
/**
|
||||
Source block description
|
||||
*/
|
||||
#define EVENT_BLOCK N_( L"event handler block" )
|
||||
|
||||
|
||||
/**
|
||||
Unknown block description
|
||||
*/
|
||||
#define UNKNOWN_BLOCK N_( L"unknown/invalid block" )
|
||||
|
||||
|
||||
|
||||
#endif
|
1476
parse_execution.cpp
Normal file
1476
parse_execution.cpp
Normal file
File diff suppressed because it is too large
Load diff
114
parse_execution.h
Normal file
114
parse_execution.h
Normal file
|
@ -0,0 +1,114 @@
|
|||
/**\file parse_execution.h
|
||||
|
||||
Provides the "linkage" between a parse_node_tree_t and actual execution structures (job_t, etc.).
|
||||
*/
|
||||
|
||||
#ifndef FISH_PARSE_EXECUTION_H
|
||||
#define FISH_PARSE_EXECUTION_H
|
||||
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
#include "parse_tree.h"
|
||||
#include "proc.h"
|
||||
|
||||
class job_t;
|
||||
struct profile_item_t;
|
||||
struct block_t;
|
||||
|
||||
enum parse_execution_result_t
|
||||
{
|
||||
/* The job was successfully executed (though it have failed on its own). */
|
||||
parse_execution_success,
|
||||
|
||||
/* The job did not execute due to some error (e.g. failed to wildcard expand). An error will have been printed and proc_last_status will have been set. */
|
||||
parse_execution_errored,
|
||||
|
||||
/* The job was cancelled (e.g. Ctrl-C) */
|
||||
parse_execution_cancelled,
|
||||
|
||||
/* The job was skipped (e.g. due to a not-taken 'and' command). This is a special return allowed only from the populate functions, not the run functions. */
|
||||
parse_execution_skipped
|
||||
};
|
||||
|
||||
class parse_execution_context_t
|
||||
{
|
||||
private:
|
||||
const parse_node_tree_t tree;
|
||||
const wcstring src;
|
||||
io_chain_t block_io;
|
||||
parser_t * const parser;
|
||||
//parse_error_list_t errors;
|
||||
|
||||
int eval_level;
|
||||
std::vector<profile_item_t*> profile_items;
|
||||
|
||||
/* No copying allowed */
|
||||
parse_execution_context_t(const parse_execution_context_t&);
|
||||
parse_execution_context_t& operator=(const parse_execution_context_t&);
|
||||
|
||||
/* Should I cancel? */
|
||||
bool should_cancel_execution(const block_t *block) const;
|
||||
|
||||
/* Ways that we can stop executing a block. These are in a sort of ascending order of importance, e.g. `exit` should trump `break` */
|
||||
enum execution_cancellation_reason_t
|
||||
{
|
||||
execution_cancellation_none,
|
||||
execution_cancellation_loop_control,
|
||||
execution_cancellation_skip,
|
||||
execution_cancellation_exit
|
||||
};
|
||||
execution_cancellation_reason_t cancellation_reason(const block_t *block) const;
|
||||
|
||||
/* Report an error. Always returns true. */
|
||||
parse_execution_result_t report_error(const parse_node_t &node, const wchar_t *fmt, ...);
|
||||
/* Wildcard error helper */
|
||||
parse_execution_result_t report_unmatched_wildcard_error(const parse_node_t &unmatched_wildcard);
|
||||
|
||||
/* Command not found support */
|
||||
void handle_command_not_found(const wcstring &cmd, const parse_node_t &statement_node, int err_code);
|
||||
|
||||
/* Utilities */
|
||||
wcstring get_source(const parse_node_t &node) const;
|
||||
const parse_node_t *get_child(const parse_node_t &parent, node_offset_t which, parse_token_type_t expected_type = token_type_invalid) const;
|
||||
node_offset_t get_offset(const parse_node_t &node) const;
|
||||
const parse_node_t *infinite_recursive_statement_in_job_list(const parse_node_t &job_list, wcstring *out_func_name) const;
|
||||
|
||||
/* Indicates whether a job is a simple block (one block, no redirections) */
|
||||
bool job_is_simple_block(const parse_node_t &node) const;
|
||||
|
||||
enum process_type_t process_type_for_command(const parse_node_t &plain_statement, const wcstring &cmd) const;
|
||||
|
||||
/* These create process_t structures from statements */
|
||||
parse_execution_result_t populate_job_process(job_t *job, process_t *proc, const parse_node_t &statement_node);
|
||||
parse_execution_result_t populate_boolean_process(job_t *job, process_t *proc, const parse_node_t &bool_statement);
|
||||
parse_execution_result_t populate_plain_process(job_t *job, process_t *proc, const parse_node_t &statement);
|
||||
parse_execution_result_t populate_block_process(job_t *job, process_t *proc, const parse_node_t &statement_node);
|
||||
|
||||
/* These encapsulate the actual logic of various (block) statements. */
|
||||
parse_execution_result_t run_block_statement(const parse_node_t &statement);
|
||||
parse_execution_result_t run_for_statement(const parse_node_t &header, const parse_node_t &contents);
|
||||
parse_execution_result_t run_if_statement(const parse_node_t &statement);
|
||||
parse_execution_result_t run_switch_statement(const parse_node_t &statement);
|
||||
parse_execution_result_t run_while_statement(const parse_node_t &header, const parse_node_t &contents);
|
||||
parse_execution_result_t run_function_statement(const parse_node_t &header, const parse_node_t &contents);
|
||||
parse_execution_result_t run_begin_statement(const parse_node_t &header, const parse_node_t &contents);
|
||||
|
||||
wcstring_list_t determine_arguments(const parse_node_t &parent, const parse_node_t **out_unmatched_wildcard_node);
|
||||
|
||||
/* Determines the IO chain. Returns true on success, false on error */
|
||||
bool determine_io_chain(const parse_node_t &statement, io_chain_t *out_chain);
|
||||
|
||||
parse_execution_result_t run_1_job(const parse_node_t &job_node, const block_t *associated_block);
|
||||
parse_execution_result_t run_job_list(const parse_node_t &job_list_node, const block_t *associated_block);
|
||||
parse_execution_result_t populate_job_from_job_node(job_t *j, const parse_node_t &job_node, const block_t *associated_block);
|
||||
|
||||
public:
|
||||
parse_execution_context_t(const parse_node_tree_t &t, const wcstring &s, parser_t *p);
|
||||
|
||||
/* Start executing at the given node offset. Returns 0 if there was no error, 1 if there was an error */
|
||||
parse_execution_result_t eval_node_at_offset(node_offset_t offset, const block_t *associated_block, const io_chain_t &io);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
544
parse_productions.cpp
Normal file
544
parse_productions.cpp
Normal file
|
@ -0,0 +1,544 @@
|
|||
#include "parse_productions.h"
|
||||
|
||||
using namespace parse_productions;
|
||||
#define NO_PRODUCTION ((production_option_idx_t)(-1))
|
||||
|
||||
static bool production_is_empty(const production_t production)
|
||||
{
|
||||
return production[0] == token_type_invalid;
|
||||
}
|
||||
|
||||
/* Empty productions are allowed but must be first. Validate that the given production is in the valid range, i.e. it is either not empty or there is a non-empty production after it */
|
||||
static bool production_is_valid(const production_options_t production_list, production_option_idx_t which)
|
||||
{
|
||||
if (which < 0 || which >= MAX_PRODUCTIONS)
|
||||
return false;
|
||||
|
||||
bool nonempty_found = false;
|
||||
for (int i=which; i < MAX_PRODUCTIONS; i++)
|
||||
{
|
||||
if (! production_is_empty(production_list[i]))
|
||||
{
|
||||
nonempty_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nonempty_found;
|
||||
}
|
||||
|
||||
#define PRODUCTIONS(sym) static const production_options_t productions_##sym
|
||||
#define RESOLVE(sym) static production_option_idx_t resolve_##sym (const parse_token_t &token1, const parse_token_t &token2)
|
||||
#define RESOLVE_ONLY(sym) static production_option_idx_t resolve_##sym (const parse_token_t &input1, const parse_token_t &input2) { return 0; }
|
||||
|
||||
#define KEYWORD(x) ((x) + LAST_TOKEN_OR_SYMBOL + 1)
|
||||
|
||||
|
||||
/* A job_list is a list of jobs, separated by semicolons or newlines */
|
||||
PRODUCTIONS(job_list) =
|
||||
{
|
||||
{},
|
||||
{symbol_job, symbol_job_list},
|
||||
{parse_token_type_end, symbol_job_list}
|
||||
};
|
||||
|
||||
RESOLVE(job_list)
|
||||
{
|
||||
switch (token1.type)
|
||||
{
|
||||
case parse_token_type_string:
|
||||
// some keywords are special
|
||||
switch (token1.keyword)
|
||||
{
|
||||
case parse_keyword_end:
|
||||
case parse_keyword_else:
|
||||
case parse_keyword_case:
|
||||
// End this job list
|
||||
return 0;
|
||||
|
||||
default:
|
||||
// Normal string
|
||||
return 1;
|
||||
}
|
||||
|
||||
case parse_token_type_pipe:
|
||||
case parse_token_type_redirection:
|
||||
case parse_token_type_background:
|
||||
return 1;
|
||||
|
||||
case parse_token_type_end:
|
||||
// Empty line
|
||||
return 2;
|
||||
|
||||
case parse_token_type_terminate:
|
||||
// no more commands, just transition to empty
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return NO_PRODUCTION;
|
||||
}
|
||||
}
|
||||
|
||||
/* A job is a non-empty list of statements, separated by pipes. (Non-empty is useful for cases like if statements, where we require a command). To represent "non-empty", we require a statement, followed by a possibly empty job_continuation */
|
||||
|
||||
PRODUCTIONS(job) =
|
||||
{
|
||||
{symbol_statement, symbol_job_continuation}
|
||||
};
|
||||
RESOLVE_ONLY(job)
|
||||
|
||||
PRODUCTIONS(job_continuation) =
|
||||
{
|
||||
{},
|
||||
{parse_token_type_pipe, symbol_statement, symbol_job_continuation}
|
||||
};
|
||||
RESOLVE(job_continuation)
|
||||
{
|
||||
switch (token1.type)
|
||||
{
|
||||
case parse_token_type_pipe:
|
||||
// Pipe, continuation
|
||||
return 1;
|
||||
|
||||
default:
|
||||
// Not a pipe, no job continuation
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* A statement is a normal command, or an if / while / and etc */
|
||||
PRODUCTIONS(statement) =
|
||||
{
|
||||
{symbol_boolean_statement},
|
||||
{symbol_block_statement},
|
||||
{symbol_if_statement},
|
||||
{symbol_switch_statement},
|
||||
{symbol_decorated_statement}
|
||||
};
|
||||
RESOLVE(statement)
|
||||
{
|
||||
// Go to decorated statements if the subsequent token looks like '--'
|
||||
// If we are 'begin', then we expect to be invoked with no arguments. But if we are anything else, we require an argument, so do the same thing if the subsequent token is a line end.
|
||||
if (token1.type == parse_token_type_string)
|
||||
{
|
||||
// If the next token looks like an option (starts with a dash), then parse it as a decorated statement
|
||||
if (token2.has_dash_prefix)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
// Likewise if the next token doesn't look like an argument at all. This corresponds to e.g. a "naked if".
|
||||
bool naked_invocation_invokes_help = (token1.keyword != parse_keyword_begin && token1.keyword != parse_keyword_end);
|
||||
if (naked_invocation_invokes_help && (token2.type == parse_token_type_end || token2.type == parse_token_type_terminate))
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
switch (token1.type)
|
||||
{
|
||||
case parse_token_type_string:
|
||||
switch (token1.keyword)
|
||||
{
|
||||
case parse_keyword_and:
|
||||
case parse_keyword_or:
|
||||
case parse_keyword_not:
|
||||
return 0;
|
||||
|
||||
case parse_keyword_for:
|
||||
case parse_keyword_while:
|
||||
case parse_keyword_function:
|
||||
case parse_keyword_begin:
|
||||
return 1;
|
||||
|
||||
case parse_keyword_if:
|
||||
return 2;
|
||||
|
||||
case parse_keyword_else:
|
||||
return NO_PRODUCTION;
|
||||
|
||||
case parse_keyword_switch:
|
||||
return 3;
|
||||
|
||||
case parse_keyword_end:
|
||||
return NO_PRODUCTION;
|
||||
|
||||
// 'in' is only special within a for_header
|
||||
case parse_keyword_in:
|
||||
case parse_keyword_none:
|
||||
case parse_keyword_command:
|
||||
case parse_keyword_builtin:
|
||||
case parse_keyword_case:
|
||||
return 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case parse_token_type_pipe:
|
||||
case parse_token_type_redirection:
|
||||
case parse_token_type_background:
|
||||
case parse_token_type_terminate:
|
||||
return NO_PRODUCTION;
|
||||
//parse_error(L"statement", token);
|
||||
|
||||
default:
|
||||
return NO_PRODUCTION;
|
||||
}
|
||||
}
|
||||
|
||||
PRODUCTIONS(if_statement) =
|
||||
{
|
||||
{symbol_if_clause, symbol_else_clause, symbol_end_command, symbol_arguments_or_redirections_list}
|
||||
};
|
||||
RESOLVE_ONLY(if_statement)
|
||||
|
||||
PRODUCTIONS(if_clause) =
|
||||
{
|
||||
{ KEYWORD(parse_keyword_if), symbol_job, parse_token_type_end, symbol_job_list }
|
||||
};
|
||||
RESOLVE_ONLY(if_clause)
|
||||
|
||||
PRODUCTIONS(else_clause) =
|
||||
{
|
||||
{ },
|
||||
{ KEYWORD(parse_keyword_else), symbol_else_continuation }
|
||||
};
|
||||
RESOLVE(else_clause)
|
||||
{
|
||||
switch (token1.keyword)
|
||||
{
|
||||
case parse_keyword_else:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
PRODUCTIONS(else_continuation) =
|
||||
{
|
||||
{symbol_if_clause, symbol_else_clause},
|
||||
{parse_token_type_end, symbol_job_list}
|
||||
};
|
||||
RESOLVE(else_continuation)
|
||||
{
|
||||
switch (token1.keyword)
|
||||
{
|
||||
case parse_keyword_if:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
PRODUCTIONS(switch_statement) =
|
||||
{
|
||||
{ KEYWORD(parse_keyword_switch), parse_token_type_string, parse_token_type_end, symbol_case_item_list, symbol_end_command, symbol_arguments_or_redirections_list}
|
||||
};
|
||||
RESOLVE_ONLY(switch_statement)
|
||||
|
||||
PRODUCTIONS(case_item_list) =
|
||||
{
|
||||
{},
|
||||
{symbol_case_item, symbol_case_item_list},
|
||||
{parse_token_type_end, symbol_case_item_list}
|
||||
};
|
||||
RESOLVE(case_item_list)
|
||||
{
|
||||
if (token1.keyword == parse_keyword_case) return 1;
|
||||
else if (token1.type == parse_token_type_end) return 2; //empty line
|
||||
else return 0;
|
||||
}
|
||||
|
||||
PRODUCTIONS(case_item) =
|
||||
{
|
||||
{KEYWORD(parse_keyword_case), symbol_argument_list, parse_token_type_end, symbol_job_list}
|
||||
};
|
||||
RESOLVE_ONLY(case_item)
|
||||
|
||||
PRODUCTIONS(argument_list) =
|
||||
{
|
||||
{},
|
||||
{symbol_argument, symbol_argument_list}
|
||||
};
|
||||
RESOLVE(argument_list)
|
||||
{
|
||||
switch (token1.type)
|
||||
{
|
||||
case parse_token_type_string:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
PRODUCTIONS(block_statement) =
|
||||
{
|
||||
{symbol_block_header, parse_token_type_end, symbol_job_list, symbol_end_command, symbol_arguments_or_redirections_list}
|
||||
};
|
||||
RESOLVE_ONLY(block_statement)
|
||||
|
||||
PRODUCTIONS(block_header) =
|
||||
{
|
||||
{symbol_for_header},
|
||||
{symbol_while_header},
|
||||
{symbol_function_header},
|
||||
{symbol_begin_header}
|
||||
};
|
||||
RESOLVE(block_header)
|
||||
{
|
||||
switch (token1.keyword)
|
||||
{
|
||||
case parse_keyword_for:
|
||||
return 0;
|
||||
case parse_keyword_while:
|
||||
return 1;
|
||||
case parse_keyword_function:
|
||||
return 2;
|
||||
case parse_keyword_begin:
|
||||
return 3;
|
||||
default:
|
||||
return NO_PRODUCTION;
|
||||
}
|
||||
}
|
||||
|
||||
PRODUCTIONS(for_header) =
|
||||
{
|
||||
{KEYWORD(parse_keyword_for), parse_token_type_string, KEYWORD(parse_keyword_in), symbol_argument_list}
|
||||
};
|
||||
RESOLVE_ONLY(for_header)
|
||||
|
||||
PRODUCTIONS(while_header) =
|
||||
{
|
||||
{KEYWORD(parse_keyword_while), symbol_job}
|
||||
};
|
||||
RESOLVE_ONLY(while_header)
|
||||
|
||||
PRODUCTIONS(begin_header) =
|
||||
{
|
||||
{KEYWORD(parse_keyword_begin)}
|
||||
};
|
||||
RESOLVE_ONLY(begin_header)
|
||||
|
||||
PRODUCTIONS(function_header) =
|
||||
{
|
||||
{KEYWORD(parse_keyword_function), symbol_argument, symbol_argument_list}
|
||||
};
|
||||
RESOLVE_ONLY(function_header)
|
||||
|
||||
/* A boolean statement is AND or OR or NOT */
|
||||
PRODUCTIONS(boolean_statement) =
|
||||
{
|
||||
{KEYWORD(parse_keyword_and), symbol_statement},
|
||||
{KEYWORD(parse_keyword_or), symbol_statement},
|
||||
{KEYWORD(parse_keyword_not), symbol_statement}
|
||||
};
|
||||
RESOLVE(boolean_statement)
|
||||
{
|
||||
switch (token1.keyword)
|
||||
{
|
||||
case parse_keyword_and:
|
||||
return 0;
|
||||
case parse_keyword_or:
|
||||
return 1;
|
||||
case parse_keyword_not:
|
||||
return 2;
|
||||
default:
|
||||
return NO_PRODUCTION;
|
||||
}
|
||||
}
|
||||
|
||||
PRODUCTIONS(decorated_statement) =
|
||||
{
|
||||
{symbol_plain_statement},
|
||||
{KEYWORD(parse_keyword_command), symbol_plain_statement},
|
||||
{KEYWORD(parse_keyword_builtin), symbol_plain_statement},
|
||||
};
|
||||
RESOLVE(decorated_statement)
|
||||
{
|
||||
/* If this is e.g. 'command --help' then the command is 'command' and not a decoration. If the second token is not a string, then this is a naked 'command' and we should execute it as undecorated. */
|
||||
if (token2.type != parse_token_type_string || token2.has_dash_prefix)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (token1.keyword)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case parse_keyword_command:
|
||||
return 1;
|
||||
case parse_keyword_builtin:
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
PRODUCTIONS(plain_statement) =
|
||||
{
|
||||
{parse_token_type_string, symbol_arguments_or_redirections_list, symbol_optional_background}
|
||||
};
|
||||
RESOLVE_ONLY(plain_statement)
|
||||
|
||||
PRODUCTIONS(arguments_or_redirections_list) =
|
||||
{
|
||||
{},
|
||||
{symbol_argument_or_redirection, symbol_arguments_or_redirections_list}
|
||||
};
|
||||
RESOLVE(arguments_or_redirections_list)
|
||||
{
|
||||
switch (token1.type)
|
||||
{
|
||||
case parse_token_type_string:
|
||||
case parse_token_type_redirection:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
PRODUCTIONS(argument_or_redirection) =
|
||||
{
|
||||
{symbol_argument},
|
||||
{symbol_redirection}
|
||||
};
|
||||
RESOLVE(argument_or_redirection)
|
||||
{
|
||||
switch (token1.type)
|
||||
{
|
||||
case parse_token_type_string:
|
||||
return 0;
|
||||
case parse_token_type_redirection:
|
||||
return 1;
|
||||
default:
|
||||
return NO_PRODUCTION;
|
||||
}
|
||||
}
|
||||
|
||||
PRODUCTIONS(argument) =
|
||||
{
|
||||
{parse_token_type_string}
|
||||
};
|
||||
RESOLVE_ONLY(argument)
|
||||
|
||||
PRODUCTIONS(redirection) =
|
||||
{
|
||||
{parse_token_type_redirection, parse_token_type_string}
|
||||
};
|
||||
RESOLVE_ONLY(redirection)
|
||||
|
||||
PRODUCTIONS(optional_background) =
|
||||
{
|
||||
{},
|
||||
{ parse_token_type_background }
|
||||
};
|
||||
|
||||
RESOLVE(optional_background)
|
||||
{
|
||||
switch (token1.type)
|
||||
{
|
||||
case parse_token_type_background:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
PRODUCTIONS(end_command) =
|
||||
{
|
||||
{KEYWORD(parse_keyword_end)}
|
||||
};
|
||||
RESOLVE_ONLY(end_command)
|
||||
|
||||
#define TEST(sym) case (symbol_##sym): production_list = & productions_ ## sym ; resolver = resolve_ ## sym ; break;
|
||||
const production_t *parse_productions::production_for_token(parse_token_type_t node_type, const parse_token_t &input1, const parse_token_t &input2, production_option_idx_t *out_which_production, wcstring *out_error_text)
|
||||
{
|
||||
bool log_it = false;
|
||||
if (log_it)
|
||||
{
|
||||
fprintf(stderr, "Resolving production for %ls with input token <%ls>\n", token_type_description(node_type).c_str(), input1.describe().c_str());
|
||||
}
|
||||
|
||||
/* Fetch the list of productions and the function to resolve them */
|
||||
const production_options_t *production_list = NULL;
|
||||
production_option_idx_t (*resolver)(const parse_token_t &input1, const parse_token_t &input2) = NULL;
|
||||
switch (node_type)
|
||||
{
|
||||
TEST(job_list)
|
||||
TEST(job)
|
||||
TEST(statement)
|
||||
TEST(job_continuation)
|
||||
TEST(boolean_statement)
|
||||
TEST(block_statement)
|
||||
TEST(if_statement)
|
||||
TEST(if_clause)
|
||||
TEST(else_clause)
|
||||
TEST(else_continuation)
|
||||
TEST(switch_statement)
|
||||
TEST(decorated_statement)
|
||||
TEST(case_item_list)
|
||||
TEST(case_item)
|
||||
TEST(argument_list)
|
||||
TEST(block_header)
|
||||
TEST(for_header)
|
||||
TEST(while_header)
|
||||
TEST(begin_header)
|
||||
TEST(function_header)
|
||||
TEST(plain_statement)
|
||||
TEST(arguments_or_redirections_list)
|
||||
TEST(argument_or_redirection)
|
||||
TEST(argument)
|
||||
TEST(redirection)
|
||||
TEST(optional_background)
|
||||
TEST(end_command)
|
||||
|
||||
case parse_token_type_string:
|
||||
case parse_token_type_pipe:
|
||||
case parse_token_type_redirection:
|
||||
case parse_token_type_background:
|
||||
case parse_token_type_end:
|
||||
case parse_token_type_terminate:
|
||||
fprintf(stderr, "Terminal token type %ls passed to %s\n", token_type_description(node_type).c_str(), __FUNCTION__);
|
||||
PARSER_DIE();
|
||||
break;
|
||||
|
||||
case parse_special_type_parse_error:
|
||||
case parse_special_type_tokenizer_error:
|
||||
case parse_special_type_comment:
|
||||
fprintf(stderr, "Special type %ls passed to %s\n", token_type_description(node_type).c_str(), __FUNCTION__);
|
||||
PARSER_DIE();
|
||||
break;
|
||||
|
||||
|
||||
case token_type_invalid:
|
||||
fprintf(stderr, "token_type_invalid passed to %s\n", __FUNCTION__);
|
||||
PARSER_DIE();
|
||||
break;
|
||||
|
||||
}
|
||||
PARSE_ASSERT(production_list != NULL);
|
||||
PARSE_ASSERT(resolver != NULL);
|
||||
|
||||
const production_t *result = NULL;
|
||||
production_option_idx_t which = resolver(input1, input2);
|
||||
|
||||
if (log_it)
|
||||
{
|
||||
fprintf(stderr, "\tresolved to %u\n", (unsigned)which);
|
||||
}
|
||||
|
||||
|
||||
if (which == NO_PRODUCTION)
|
||||
{
|
||||
if (log_it)
|
||||
{
|
||||
fprintf(stderr, "Node type '%ls' has no production for input '%ls' (in %s)\n", token_type_description(node_type).c_str(), input1.describe().c_str(), __FUNCTION__);
|
||||
}
|
||||
result = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
PARSE_ASSERT(production_is_valid(*production_list, which));
|
||||
result = &((*production_list)[which]);
|
||||
}
|
||||
*out_which_production = which;
|
||||
return result;
|
||||
}
|
||||
|
71
parse_productions.h
Normal file
71
parse_productions.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/**\file parse_tree.h
|
||||
|
||||
Programmatic representation of fish code.
|
||||
*/
|
||||
|
||||
#ifndef FISH_PARSE_TREE_CONSTRUCTION_H
|
||||
#define FISH_PARSE_TREE_CONSTRUCTION_H
|
||||
|
||||
#include "parse_tree.h"
|
||||
#include <inttypes.h>
|
||||
|
||||
namespace parse_productions
|
||||
{
|
||||
|
||||
#define MAX_PRODUCTIONS 5
|
||||
#define MAX_SYMBOLS_PER_PRODUCTION 6
|
||||
|
||||
typedef uint32_t production_tag_t;
|
||||
|
||||
/* A production is an array of unsigned char. Symbols are encoded directly as their symbol value. Keywords are encoded with an offset of LAST_TOKEN_OR_SYMBOL + 1. So essentially we glom together keywords and symbols. */
|
||||
typedef uint8_t production_element_t;
|
||||
|
||||
/* An index into a production option list */
|
||||
typedef uint8_t production_option_idx_t;
|
||||
|
||||
/* A production is an array of production elements */
|
||||
typedef production_element_t const production_t[MAX_SYMBOLS_PER_PRODUCTION];
|
||||
|
||||
/* A production options is an array of (possible) productions */
|
||||
typedef production_t production_options_t[MAX_PRODUCTIONS];
|
||||
|
||||
/* Resolve the type from a production element */
|
||||
inline parse_token_type_t production_element_type(production_element_t elem)
|
||||
{
|
||||
if (elem > LAST_TOKEN_OR_SYMBOL)
|
||||
{
|
||||
return parse_token_type_string;
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<parse_token_type_t>(elem);
|
||||
}
|
||||
}
|
||||
|
||||
/* Resolve the keyword from a production element */
|
||||
inline parse_keyword_t production_element_keyword(production_element_t elem)
|
||||
{
|
||||
if (elem > LAST_TOKEN_OR_SYMBOL)
|
||||
{
|
||||
// First keyword is LAST_TOKEN_OR_SYMBOL + 1
|
||||
return static_cast<parse_keyword_t>(elem - LAST_TOKEN_OR_SYMBOL - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
return parse_keyword_none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if an element is valid */
|
||||
inline bool production_element_is_valid(production_element_t elem)
|
||||
{
|
||||
return elem != token_type_invalid;
|
||||
}
|
||||
|
||||
/* Fetch a production. We are passed two input tokens. The first input token is guaranteed to not be invalid; the second token may be invalid if there's no more tokens. */
|
||||
const production_t *production_for_token(parse_token_type_t node_type, const parse_token_t &input1, const parse_token_t &input2, production_option_idx_t *out_which_production, wcstring *out_error_text);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
1492
parse_tree.cpp
Normal file
1492
parse_tree.cpp
Normal file
File diff suppressed because it is too large
Load diff
271
parse_tree.h
Normal file
271
parse_tree.h
Normal file
|
@ -0,0 +1,271 @@
|
|||
/**\file parse_tree.h
|
||||
|
||||
Programmatic representation of fish code.
|
||||
*/
|
||||
|
||||
#ifndef FISH_PARSE_PRODUCTIONS_H
|
||||
#define FISH_PARSE_PRODUCTIONS_H
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
#include "common.h"
|
||||
#include "tokenizer.h"
|
||||
#include "parse_constants.h"
|
||||
#include <vector>
|
||||
#include <inttypes.h>
|
||||
|
||||
class parse_node_t;
|
||||
class parse_node_tree_t;
|
||||
typedef size_t node_offset_t;
|
||||
#define NODE_OFFSET_INVALID (static_cast<node_offset_t>(-1))
|
||||
|
||||
struct parse_error_t
|
||||
{
|
||||
/** Text of the error */
|
||||
wcstring text;
|
||||
|
||||
/** Code for the error */
|
||||
enum parse_error_code_t code;
|
||||
|
||||
/** Offset and length of the token in the source code that triggered this error */
|
||||
size_t source_start;
|
||||
size_t source_length;
|
||||
|
||||
/** Return a string describing the error, suitable for presentation to the user. If skip_caret is false, the offending line with a caret is printed as well */
|
||||
wcstring describe(const wcstring &src, bool skip_caret = false) const;
|
||||
};
|
||||
typedef std::vector<parse_error_t> parse_error_list_t;
|
||||
|
||||
/* Returns a description of a list of parse errors */
|
||||
wcstring parse_errors_description(const parse_error_list_t &errors, const wcstring &src, const wchar_t *prefix = NULL);
|
||||
|
||||
/** A struct representing the token type that we use internally */
|
||||
struct parse_token_t
|
||||
{
|
||||
enum parse_token_type_t type; // The type of the token as represented by the parser
|
||||
enum parse_keyword_t keyword; // Any keyword represented by this token
|
||||
bool has_dash_prefix; // Hackish: whether the source contains a dash prefix
|
||||
size_t source_start;
|
||||
size_t source_length;
|
||||
|
||||
wcstring describe() const;
|
||||
wcstring user_presentable_description() const;
|
||||
};
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
parse_flag_none = 0,
|
||||
|
||||
/* Attempt to build a "parse tree" no matter what. This may result in a 'forest' of disconnected trees. This is intended to be used by syntax highlighting. */
|
||||
parse_flag_continue_after_error = 1 << 0,
|
||||
|
||||
/* Include comment tokens */
|
||||
parse_flag_include_comments = 1 << 1,
|
||||
|
||||
/* Indicate that the tokenizer should accept incomplete tokens */
|
||||
parse_flag_accept_incomplete_tokens = 1 << 2,
|
||||
|
||||
/* Indicate that the parser should not generate the terminate token, allowing an 'unfinished' tree where some nodes may have no productions. */
|
||||
parse_flag_leave_unterminated = 1 << 3
|
||||
|
||||
};
|
||||
typedef unsigned int parse_tree_flags_t;
|
||||
|
||||
wcstring parse_dump_tree(const parse_node_tree_t &tree, const wcstring &src);
|
||||
|
||||
wcstring token_type_description(parse_token_type_t type);
|
||||
wcstring keyword_description(parse_keyword_t type);
|
||||
|
||||
/** Class for nodes of a parse tree */
|
||||
class parse_node_t
|
||||
{
|
||||
public:
|
||||
|
||||
/* Type of the node */
|
||||
enum parse_token_type_t type;
|
||||
|
||||
/* Start in the source code */
|
||||
size_t source_start;
|
||||
|
||||
/* Length of our range in the source code */
|
||||
size_t source_length;
|
||||
|
||||
/* Parent */
|
||||
node_offset_t parent;
|
||||
|
||||
/* Children */
|
||||
node_offset_t child_start;
|
||||
uint8_t child_count;
|
||||
|
||||
/* Which production was used */
|
||||
uint8_t production_idx;
|
||||
|
||||
/* Description */
|
||||
wcstring describe(void) const;
|
||||
|
||||
/* Constructor */
|
||||
explicit parse_node_t(parse_token_type_t ty) : type(ty), source_start(-1), source_length(0), parent(NODE_OFFSET_INVALID), child_start(0), child_count(0), production_idx(-1)
|
||||
{
|
||||
}
|
||||
|
||||
node_offset_t child_offset(node_offset_t which) const
|
||||
{
|
||||
PARSE_ASSERT(which < child_count);
|
||||
return child_start + which;
|
||||
}
|
||||
|
||||
/* Indicate if this node has a range of source code associated with it */
|
||||
bool has_source() const
|
||||
{
|
||||
return source_start != (size_t)(-1);
|
||||
}
|
||||
|
||||
/* Gets source for the node, or the empty string if it has no source */
|
||||
wcstring get_source(const wcstring &str) const
|
||||
{
|
||||
if (! has_source())
|
||||
return wcstring();
|
||||
else
|
||||
return wcstring(str, this->source_start, this->source_length);
|
||||
}
|
||||
|
||||
/* Returns whether the given location is within the source range or at its end */
|
||||
bool location_in_or_at_end_of_source_range(size_t loc) const
|
||||
{
|
||||
return has_source() && source_start <= loc && loc - source_start <= source_length;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* The parse tree itself */
|
||||
class parse_node_tree_t : public std::vector<parse_node_t>
|
||||
{
|
||||
public:
|
||||
|
||||
/* Get the node corresponding to a child of the given node, or NULL if there is no such child. If expected_type is provided, assert that the node has that type.
|
||||
*/
|
||||
const parse_node_t *get_child(const parse_node_t &parent, node_offset_t which, parse_token_type_t expected_type = token_type_invalid) const;
|
||||
|
||||
/* Find the first direct child of the given node of the given type. asserts on failure
|
||||
*/
|
||||
const parse_node_t &find_child(const parse_node_t &parent, parse_token_type_t type) const;
|
||||
|
||||
/* Get the node corresponding to the parent of the given node, or NULL if there is no such child. If expected_type is provided, only returns the parent if it is of that type. Note the asymmetry: get_child asserts since the children are known, but get_parent does not, since the parent may not be known. */
|
||||
const parse_node_t *get_parent(const parse_node_t &node, parse_token_type_t expected_type = token_type_invalid) const;
|
||||
|
||||
/* Returns the first ancestor of the given type, or NULL. */
|
||||
const parse_node_t *get_first_ancestor_of_type(const parse_node_t &node, parse_token_type_t desired_type) const;
|
||||
|
||||
/* Find all the nodes of a given type underneath a given node, up to max_count of them */
|
||||
typedef std::vector<const parse_node_t *> parse_node_list_t;
|
||||
parse_node_list_t find_nodes(const parse_node_t &parent, parse_token_type_t type, size_t max_count = (size_t)(-1)) const;
|
||||
|
||||
/* Finds the last node of a given type underneath a given node, or NULL if it could not be found. If parent is NULL, this finds the last node in the tree of that type. */
|
||||
const parse_node_t *find_last_node_of_type(parse_token_type_t type, const parse_node_t *parent = NULL) const;
|
||||
|
||||
/* Finds a node containing the given source location. If 'parent' is not NULL, it must be an ancestor. */
|
||||
const parse_node_t *find_node_matching_source_location(parse_token_type_t type, size_t source_loc, const parse_node_t *parent) const;
|
||||
|
||||
/* Indicate if the given argument_list or arguments_or_redirections_list is a root list, or has a parent */
|
||||
bool argument_list_is_root(const parse_node_t &node) const;
|
||||
|
||||
/* Utilities */
|
||||
|
||||
/* Given a plain statement, get the decoration (from the parent node), or none if there is no decoration */
|
||||
enum parse_statement_decoration_t decoration_for_plain_statement(const parse_node_t &node) const;
|
||||
|
||||
/* Given a plain statement, get the command by reference (from the child node). Returns true if successful. Clears the command on failure. */
|
||||
bool command_for_plain_statement(const parse_node_t &node, const wcstring &src, wcstring *out_cmd) const;
|
||||
|
||||
/* Given a plain statement, return true if the statement is part of a pipeline. If include_first is set, the first command in a pipeline is considered part of it; otherwise only the second or additional commands are */
|
||||
bool statement_is_in_pipeline(const parse_node_t &node, bool include_first) const;
|
||||
|
||||
/* Given a redirection, get the redirection type (or TOK_NONE) and target (file path, or fd) */
|
||||
enum token_type type_for_redirection(const parse_node_t &node, const wcstring &src, int *out_fd, wcstring *out_target) const;
|
||||
|
||||
/* If the given node is a block statement, returns the header node (for_header, while_header, begin_header, or function_header). Otherwise returns NULL */
|
||||
const parse_node_t *header_node_for_block_statement(const parse_node_t &node) const;
|
||||
|
||||
/* Given a node list (e.g. of type symbol_job_list) and a node type (e.g. symbol_job), return the next element of the given type in that list, and the tail (by reference). Returns NULL if we've exhausted the list. */
|
||||
const parse_node_t *next_node_in_node_list(const parse_node_t &node_list, parse_token_type_t item_type, const parse_node_t **list_tail) const;
|
||||
|
||||
/* Given a job, return all of its statements. These are 'specific statements' (e.g. symbol_decorated_statement, not symbol_statement) */
|
||||
parse_node_list_t specific_statements_for_job(const parse_node_t &job) const;
|
||||
};
|
||||
|
||||
/* The big entry point. Parse a string! */
|
||||
bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t flags, parse_node_tree_t *output, parse_error_list_t *errors, bool log_it = false);
|
||||
|
||||
/* Fish grammar:
|
||||
|
||||
# A job_list is a list of jobs, separated by semicolons or newlines
|
||||
|
||||
job_list = <empty> |
|
||||
job job_list
|
||||
<TOK_END> job_list
|
||||
|
||||
# A job is a non-empty list of statements, separated by pipes. (Non-empty is useful for cases like if statements, where we require a command). To represent "non-empty", we require a statement, followed by a possibly empty job_continuation
|
||||
|
||||
job = statement job_continuation
|
||||
job_continuation = <empty> |
|
||||
<TOK_PIPE> statement job_continuation
|
||||
|
||||
# A statement is a normal command, or an if / while / and etc
|
||||
|
||||
statement = boolean_statement | block_statement | if_statement | switch_statement | decorated_statement
|
||||
|
||||
# A block is a conditional, loop, or begin/end
|
||||
|
||||
if_statement = if_clause else_clause end_command arguments_or_redirections_list
|
||||
if_clause = <IF> job STATEMENT_TERMINATOR job_list
|
||||
else_clause = <empty> |
|
||||
<ELSE> else_continuation
|
||||
else_continuation = if_clause else_clause |
|
||||
STATEMENT_TERMINATOR job_list
|
||||
|
||||
switch_statement = SWITCH <TOK_STRING> STATEMENT_TERMINATOR case_item_list end_command arguments_or_redirections_list
|
||||
case_item_list = <empty> |
|
||||
case_item case_item_list |
|
||||
<TOK_END> case_item_list
|
||||
|
||||
case_item = CASE argument_list STATEMENT_TERMINATOR job_list
|
||||
|
||||
block_statement = block_header <TOK_END> job_list end_command arguments_or_redirections_list
|
||||
block_header = for_header | while_header | function_header | begin_header
|
||||
for_header = FOR var_name IN argument_list
|
||||
while_header = WHILE job
|
||||
begin_header = BEGIN
|
||||
|
||||
# Functions take arguments, and require at least one (the name). No redirections allowed.
|
||||
function_header = FUNCTION argument argument_list
|
||||
|
||||
# A boolean statement is AND or OR or NOT
|
||||
|
||||
boolean_statement = AND statement | OR statement | NOT statement
|
||||
|
||||
# A decorated_statement is a command with a list of arguments_or_redirections, possibly with "builtin" or "command"
|
||||
|
||||
decorated_statement = plain_statement | COMMAND plain_statement | BUILTIN plain_statement
|
||||
plain_statement = <TOK_STRING> arguments_or_redirections_list optional_background
|
||||
|
||||
argument_list = <empty> | argument argument_list
|
||||
|
||||
arguments_or_redirections_list = <empty> |
|
||||
argument_or_redirection arguments_or_redirections_list
|
||||
argument_or_redirection = argument | redirection
|
||||
argument = <TOK_STRING>
|
||||
|
||||
redirection = <TOK_REDIRECTION> <TOK_STRING>
|
||||
|
||||
terminator = <TOK_END> | <TOK_BACKGROUND>
|
||||
|
||||
optional_background = <empty> | <TOK_BACKGROUND>
|
||||
|
||||
end_command = END
|
||||
|
||||
*/
|
||||
|
||||
#endif
|
383
parse_util.cpp
383
parse_util.cpp
|
@ -38,18 +38,13 @@
|
|||
#include "env.h"
|
||||
#include "signal.h"
|
||||
#include "wildcard.h"
|
||||
#include "parse_tree.h"
|
||||
#include "parser.h"
|
||||
|
||||
/**
|
||||
Maximum number of autoloaded items opf a specific type to keep in
|
||||
memory at a time.
|
||||
Error message for improper use of the exec builtin
|
||||
*/
|
||||
#define AUTOLOAD_MAX 10
|
||||
|
||||
/**
|
||||
Minimum time, in seconds, before an autoloaded item will be
|
||||
unloaded
|
||||
*/
|
||||
#define AUTOLOAD_MIN_AGE 60
|
||||
#define EXEC_ERR_MSG _(L"The '%ls' command can not be used in a pipeline")
|
||||
|
||||
int parse_util_lineno(const wchar_t *str, size_t offset)
|
||||
{
|
||||
|
@ -164,7 +159,7 @@ int parse_util_locate_cmdsubst(const wchar_t *in, wchar_t **begin, wchar_t **end
|
|||
|
||||
CHECK(in, 0);
|
||||
|
||||
for (pos = (wchar_t *)in; *pos; pos++)
|
||||
for (pos = const_cast<wchar_t *>(in); *pos; pos++)
|
||||
{
|
||||
if (prev != '\\')
|
||||
{
|
||||
|
@ -240,6 +235,42 @@ int parse_util_locate_cmdsubst(const wchar_t *in, wchar_t **begin, wchar_t **end
|
|||
return 1;
|
||||
}
|
||||
|
||||
int parse_util_locate_cmdsubst_range(const wcstring &str, size_t *inout_cursor_offset, wcstring *out_contents, size_t *out_start, size_t *out_end, bool accept_incomplete)
|
||||
{
|
||||
/* Clear the return values */
|
||||
out_contents->clear();
|
||||
*out_start = 0;
|
||||
*out_end = str.size();
|
||||
|
||||
/* Nothing to do if the offset is at or past the end of the string. */
|
||||
if (*inout_cursor_offset >= str.size())
|
||||
return 0;
|
||||
|
||||
/* Defer to the wonky version */
|
||||
const wchar_t * const buff = str.c_str();
|
||||
const wchar_t * const valid_range_start = buff + *inout_cursor_offset, *valid_range_end = buff + str.size();
|
||||
wchar_t *cmdsub_begin = NULL, *cmdsub_end = NULL;
|
||||
int ret = parse_util_locate_cmdsubst(valid_range_start, &cmdsub_begin, &cmdsub_end, accept_incomplete);
|
||||
if (ret > 0)
|
||||
{
|
||||
/* The command substitutions must not be NULL and must be in the valid pointer range, and the end must be bigger than the beginning */
|
||||
assert(cmdsub_begin != NULL && cmdsub_begin >= valid_range_start && cmdsub_begin <= valid_range_end);
|
||||
assert(cmdsub_end != NULL && cmdsub_end > cmdsub_begin && cmdsub_end >= valid_range_start && cmdsub_end <= valid_range_end);
|
||||
|
||||
/* Assign the substring to the out_contents */
|
||||
const wchar_t *interior_begin = cmdsub_begin + 1;
|
||||
out_contents->assign(interior_begin, cmdsub_end - interior_begin);
|
||||
|
||||
/* Return the start and end */
|
||||
*out_start = cmdsub_begin - buff;
|
||||
*out_end = cmdsub_end - buff;
|
||||
|
||||
/* Update the inout_cursor_offset. Note this may cause it to exceed str.size(), though overflow is not likely */
|
||||
*inout_cursor_offset = 1 + *out_end;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void parse_util_cmdsubst_extent(const wchar_t *buff, size_t cursor_pos, const wchar_t **a, const wchar_t **b)
|
||||
{
|
||||
const wchar_t * const cursor = buff + cursor_pos;
|
||||
|
@ -768,3 +799,335 @@ wcstring parse_util_escape_string_with_quote(const wcstring &cmd, wchar_t quote)
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* We are given a parse tree, the index of a node within the tree, its indent, and a vector of indents the same size as the original source string. Set the indent correspdonding to the node's source range, if appropriate.
|
||||
|
||||
trailing_indent is the indent for nodes with unrealized source, i.e. if I type 'if false <ret>' then we have an if node with an empty job list (without source) but we want the last line to be indented anyways.
|
||||
|
||||
switch statements also indent.
|
||||
|
||||
max_visited_node_idx is the largest index we visited.
|
||||
*/
|
||||
static void compute_indents_recursive(const parse_node_tree_t &tree, node_offset_t node_idx, int node_indent, parse_token_type_t parent_type, std::vector<int> *indents, int *trailing_indent, node_offset_t *max_visited_node_idx)
|
||||
{
|
||||
/* Guard against incomplete trees */
|
||||
if (node_idx > tree.size())
|
||||
return;
|
||||
|
||||
/* Update max_visited_node_idx */
|
||||
if (node_idx > *max_visited_node_idx)
|
||||
*max_visited_node_idx = node_idx;
|
||||
|
||||
/* We could implement this by utilizing the fish grammar. But there's an easy trick instead: almost everything that wraps a job list should be indented by 1. So just find all of the job lists. One exception is switch; the other exception is job_list itself: a job_list is a job and a job_list, and we want that child list to be indented the same as the parent. So just find all job_lists whose parent is not a job_list, and increment their indent by 1. */
|
||||
|
||||
const parse_node_t &node = tree.at(node_idx);
|
||||
const parse_token_type_t node_type = node.type;
|
||||
|
||||
/* Increment the indent if we are either a root job_list, or root case_item_list */
|
||||
const bool is_root_job_list = (node_type == symbol_job_list && parent_type != symbol_job_list);
|
||||
const bool is_root_case_item_list = (node_type == symbol_case_item_list && parent_type != symbol_case_item_list);
|
||||
if (is_root_job_list || is_root_case_item_list)
|
||||
{
|
||||
node_indent += 1;
|
||||
}
|
||||
|
||||
/* If we have source, store the trailing indent unconditionally. If we do not have source, store the trailing indent only if ours is bigger; this prevents the trailing "run" of terminal job lists from affecting the trailing indent. For example, code like this:
|
||||
|
||||
if foo
|
||||
|
||||
will be parsed as this:
|
||||
|
||||
job_list
|
||||
job
|
||||
if_statement
|
||||
job [if]
|
||||
job_list [empty]
|
||||
job_list [empty]
|
||||
|
||||
There's two "terminal" job lists, and we want the innermost one.
|
||||
|
||||
Note we are relying on the fact that nodes are in the same order as the source, i.e. an in-order traversal of the node tree also traverses the source from beginning to end.
|
||||
*/
|
||||
if (node.has_source() || node_indent > *trailing_indent)
|
||||
{
|
||||
*trailing_indent = node_indent;
|
||||
}
|
||||
|
||||
|
||||
/* Store the indent into the indent array */
|
||||
if (node.has_source())
|
||||
{
|
||||
assert(node.source_start < indents->size());
|
||||
indents->at(node.source_start) = node_indent;
|
||||
}
|
||||
|
||||
|
||||
/* Recursive to all our children */
|
||||
for (node_offset_t idx = 0; idx < node.child_count; idx++)
|
||||
{
|
||||
/* Note we pass our type to our child, which becomes its parent node type */
|
||||
compute_indents_recursive(tree, node.child_start + idx, node_indent, node_type, indents, trailing_indent, max_visited_node_idx);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> parse_util_compute_indents(const wcstring &src)
|
||||
{
|
||||
/* Make a vector the same size as the input string, which contains the indents. Initialize them to -1. */
|
||||
const size_t src_size = src.size();
|
||||
std::vector<int> indents(src_size, -1);
|
||||
|
||||
/* Parse the string. We pass continue_after_error to produce a forest; the trailing indent of the last node we visited becomes the input indent of the next. I.e. in the case of 'switch foo ; cas', we get an invalid parse tree (since 'cas' is not valid) but we indent it as if it were a case item list */
|
||||
parse_node_tree_t tree;
|
||||
parse_tree_from_string(src, parse_flag_continue_after_error | parse_flag_accept_incomplete_tokens, &tree, NULL /* errors */);
|
||||
|
||||
/* Start indenting at the first node. If we have a parse error, we'll have to start indenting from the top again */
|
||||
node_offset_t start_node_idx = 0;
|
||||
int last_trailing_indent = 0;
|
||||
|
||||
while (start_node_idx < tree.size())
|
||||
{
|
||||
/* The indent that we'll get for the last line */
|
||||
int trailing_indent = 0;
|
||||
|
||||
/* Biggest offset we visited */
|
||||
node_offset_t max_visited_node_idx = 0;
|
||||
|
||||
/* Invoke the recursive version. As a hack, pass job_list for the 'parent' token type, which will prevent the really-root job list from indenting */
|
||||
compute_indents_recursive(tree, start_node_idx, last_trailing_indent, symbol_job_list, &indents, &trailing_indent, &max_visited_node_idx);
|
||||
|
||||
/* We may have more to indent. The trailing indent becomes our current indent. Start at the node after the last we visited. */
|
||||
last_trailing_indent = trailing_indent;
|
||||
start_node_idx = max_visited_node_idx + 1;
|
||||
}
|
||||
|
||||
int last_indent = 0;
|
||||
for (size_t i=0; i<src_size; i++)
|
||||
{
|
||||
int this_indent = indents.at(i);
|
||||
if (this_indent < 0)
|
||||
{
|
||||
indents.at(i) = last_indent;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* New indent level */
|
||||
last_indent = this_indent;
|
||||
/* Make all whitespace before a token have the new level. This avoid using the wrong indentation level if a new line starts with whitespace. */
|
||||
size_t prev_char_idx = i;
|
||||
while (prev_char_idx--)
|
||||
{
|
||||
if (!wcschr(L" \n\t\r", src.at(prev_char_idx)))
|
||||
break;
|
||||
indents.at(prev_char_idx) = last_indent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure trailing whitespace has the trailing indent. This makes sure a new line is correctly indented even if it is empty. */
|
||||
size_t suffix_idx = src_size;
|
||||
while (suffix_idx--)
|
||||
{
|
||||
if (!wcschr(L" \n\t\r", src.at(suffix_idx)))
|
||||
break;
|
||||
indents.at(suffix_idx) = last_trailing_indent;
|
||||
}
|
||||
|
||||
return indents;
|
||||
}
|
||||
|
||||
/* Append a syntax error to the given error list */
|
||||
static bool append_syntax_error(parse_error_list_t *errors, const parse_node_t &node, const wchar_t *fmt, ...)
|
||||
{
|
||||
parse_error_t error;
|
||||
error.source_start = node.source_start;
|
||||
error.source_length = node.source_length;
|
||||
error.code = parse_error_syntax;
|
||||
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
error.text = vformat_string(fmt, va);
|
||||
va_end(va);
|
||||
|
||||
errors->push_back(error);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns 1 if the specified command is a builtin that may not be used in a pipeline
|
||||
*/
|
||||
static int parser_is_pipe_forbidden(const wcstring &word)
|
||||
{
|
||||
return contains(word,
|
||||
L"exec",
|
||||
L"case",
|
||||
L"break",
|
||||
L"return",
|
||||
L"continue");
|
||||
}
|
||||
|
||||
// Check if the first argument under the given node is --help
|
||||
static bool first_argument_is_help(const parse_node_tree_t &node_tree, const parse_node_t &node, const wcstring &src)
|
||||
{
|
||||
bool is_help = false;
|
||||
const parse_node_tree_t::parse_node_list_t arg_nodes = node_tree.find_nodes(node, symbol_argument, 1);
|
||||
if (! arg_nodes.empty())
|
||||
{
|
||||
// Check the first argument only
|
||||
const parse_node_t &arg = *arg_nodes.at(0);
|
||||
const wcstring first_arg_src = arg.get_source(src);
|
||||
is_help = parser_t::is_help(first_arg_src.c_str(), 3);
|
||||
}
|
||||
return is_help;
|
||||
}
|
||||
|
||||
parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, parse_error_list_t *out_errors)
|
||||
{
|
||||
parse_node_tree_t node_tree;
|
||||
parse_error_list_t parse_errors;
|
||||
|
||||
// Whether we encountered a parse error
|
||||
bool errored = false;
|
||||
|
||||
// Whether we encountered an unclosed block
|
||||
// We detect this via an 'end_command' block without source
|
||||
bool has_unclosed_block = false;
|
||||
|
||||
// Parse the input string into a parse tree
|
||||
// Some errors are detected here
|
||||
bool parsed = parse_tree_from_string(buff_src, parse_flag_leave_unterminated, &node_tree, &parse_errors);
|
||||
if (! parsed)
|
||||
{
|
||||
errored = true;
|
||||
}
|
||||
|
||||
// Expand all commands
|
||||
// Verify 'or' and 'and' not used inside pipelines
|
||||
// Verify pipes via parser_is_pipe_forbidden
|
||||
// Verify return only within a function
|
||||
|
||||
if (! errored)
|
||||
{
|
||||
const size_t node_tree_size = node_tree.size();
|
||||
for (size_t i=0; i < node_tree_size; i++)
|
||||
{
|
||||
const parse_node_t &node = node_tree.at(i);
|
||||
if (node.type == symbol_end_command && ! node.has_source())
|
||||
{
|
||||
// an 'end' without source is an unclosed block
|
||||
has_unclosed_block = true;
|
||||
}
|
||||
else if (node.type == symbol_boolean_statement)
|
||||
{
|
||||
// 'or' and 'and' can be in a pipeline, as long as they're first
|
||||
// These numbers 0 and 1 correspond to productions for boolean_statement. This should be cleaned up.
|
||||
bool is_and = (node.production_idx == 0), is_or = (node.production_idx == 1);
|
||||
if ((is_and || is_or) && node_tree.statement_is_in_pipeline(node, false /* don't count first */))
|
||||
{
|
||||
errored = append_syntax_error(&parse_errors, node, EXEC_ERR_MSG, is_and ? L"and" : L"or");
|
||||
}
|
||||
}
|
||||
else if (node.type == symbol_plain_statement)
|
||||
{
|
||||
wcstring command;
|
||||
if (node_tree.command_for_plain_statement(node, buff_src, &command))
|
||||
{
|
||||
// Check that we can expand the command
|
||||
if (! expand_one(command, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS))
|
||||
{
|
||||
errored = append_syntax_error(&parse_errors, node, ILLEGAL_CMD_ERR_MSG, command.c_str());
|
||||
}
|
||||
|
||||
// Check that pipes are sound
|
||||
if (! errored && parser_is_pipe_forbidden(command))
|
||||
{
|
||||
// forbidden commands cannot be in a pipeline at all
|
||||
if (node_tree.statement_is_in_pipeline(node, true /* count first */))
|
||||
{
|
||||
errored = append_syntax_error(&parse_errors, node, EXEC_ERR_MSG, command.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Check that we don't return from outside a function
|
||||
// But we allow it if it's 'return --help'
|
||||
if (! errored && command == L"return")
|
||||
{
|
||||
const parse_node_t *ancestor = &node;
|
||||
bool found_function = false;
|
||||
while (ancestor != NULL)
|
||||
{
|
||||
const parse_node_t *possible_function_header = node_tree.header_node_for_block_statement(*ancestor);
|
||||
if (possible_function_header != NULL && possible_function_header->type == symbol_function_header)
|
||||
{
|
||||
found_function = true;
|
||||
break;
|
||||
}
|
||||
ancestor = node_tree.get_parent(*ancestor);
|
||||
|
||||
}
|
||||
if (! found_function && ! first_argument_is_help(node_tree, node, buff_src))
|
||||
{
|
||||
errored = append_syntax_error(&parse_errors, node, INVALID_RETURN_ERR_MSG);
|
||||
}
|
||||
}
|
||||
|
||||
// Check that we don't break or continue from outside a loop
|
||||
if (! errored && (command == L"break" || command == L"continue"))
|
||||
{
|
||||
// Walk up until we hit a 'for' or 'while' loop. If we hit a function first, stop the search; we can't break an outer loop from inside a function.
|
||||
// This is a little funny because we can't tell if it's a 'for' or 'while' loop from the ancestor alone; we need the header. That is, we hit a block_statement, and have to check its header.
|
||||
bool found_loop = false, end_search = false;
|
||||
const parse_node_t *ancestor = &node;
|
||||
while (ancestor != NULL && ! end_search)
|
||||
{
|
||||
const parse_node_t *loop_or_function_header = node_tree.header_node_for_block_statement(*ancestor);
|
||||
if (loop_or_function_header != NULL)
|
||||
{
|
||||
switch (loop_or_function_header->type)
|
||||
{
|
||||
case symbol_while_header:
|
||||
case symbol_for_header:
|
||||
// this is a loop header, so we can break or continue
|
||||
found_loop = true;
|
||||
end_search = true;
|
||||
break;
|
||||
|
||||
case symbol_function_header:
|
||||
// this is a function header, so we cannot break or continue. We stop our search here.
|
||||
found_loop = false;
|
||||
end_search = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
// most likely begin / end style block, which makes no difference
|
||||
break;
|
||||
}
|
||||
}
|
||||
ancestor = node_tree.get_parent(*ancestor);
|
||||
}
|
||||
|
||||
if (! found_loop && ! first_argument_is_help(node_tree, node, buff_src))
|
||||
{
|
||||
errored = append_syntax_error(&parse_errors, node, (command == L"break" ? INVALID_BREAK_ERR_MSG : INVALID_CONTINUE_ERR_MSG));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parser_test_error_bits_t res = 0;
|
||||
|
||||
if (errored)
|
||||
res |= PARSER_TEST_ERROR;
|
||||
|
||||
if (has_unclosed_block)
|
||||
res |= PARSER_TEST_INCOMPLETE;
|
||||
|
||||
if (out_errors)
|
||||
{
|
||||
out_errors->swap(parse_errors);
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
|
24
parse_util.h
24
parse_util.h
|
@ -8,6 +8,7 @@
|
|||
#define FISH_PARSE_UTIL_H
|
||||
|
||||
#include "autoload.h"
|
||||
#include "parse_tree.h"
|
||||
#include <wchar.h>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
@ -27,6 +28,25 @@ int parse_util_locate_cmdsubst(const wchar_t *in,
|
|||
wchar_t **end,
|
||||
bool accept_incomplete);
|
||||
|
||||
/**
|
||||
Alternative API. Iterate over command substitutions.
|
||||
|
||||
\param str the string to search for subshells
|
||||
\param inout_cursor_offset On input, the location to begin the search. On output, either the end of the string, or just after the closed-paren.
|
||||
\param out_contents On output, the contents of the command substitution
|
||||
\param out_start On output, the offset of the start of the command substitution (open paren)
|
||||
\param out_end On output, the offset of the end of the command substitution (close paren), or the end of the string if it was incomplete
|
||||
\param accept_incomplete whether to permit missing closing parenthesis
|
||||
\return -1 on syntax error, 0 if no subshells exist and 1 on sucess
|
||||
*/
|
||||
|
||||
int parse_util_locate_cmdsubst_range(const wcstring &str,
|
||||
size_t *inout_cursor_offset,
|
||||
wcstring *out_contents,
|
||||
size_t *out_start,
|
||||
size_t *out_end,
|
||||
bool accept_incomplete);
|
||||
|
||||
/**
|
||||
Find the beginning and end of the command substitution under the
|
||||
cursor. If no subshell is found, the entire string is returned. If
|
||||
|
@ -140,5 +160,9 @@ void parse_util_get_parameter_info(const wcstring &cmd, const size_t pos, wchar_
|
|||
*/
|
||||
wcstring parse_util_escape_string_with_quote(const wcstring &cmd, wchar_t quote);
|
||||
|
||||
/** Given a string, parse it as fish code and then return the indents. The return value has the same size as the string */
|
||||
std::vector<int> parse_util_compute_indents(const wcstring &src);
|
||||
|
||||
parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, parse_error_list_t *out_errors = NULL);
|
||||
|
||||
#endif
|
||||
|
|
1307
parser.cpp
1307
parser.cpp
File diff suppressed because it is too large
Load diff
99
parser.h
99
parser.h
|
@ -11,11 +11,9 @@
|
|||
#include "util.h"
|
||||
#include "event.h"
|
||||
#include "function.h"
|
||||
#include "parse_tree.h"
|
||||
#include <vector>
|
||||
|
||||
#define PARSER_TEST_ERROR 1
|
||||
#define PARSER_TEST_INCOMPLETE 2
|
||||
|
||||
/**
|
||||
event_blockage_t represents a block on events of the specified type
|
||||
*/
|
||||
|
@ -98,37 +96,18 @@ public:
|
|||
bool had_command; /**< Set to non-zero once a command has been executed in this block */
|
||||
int tok_pos; /**< The start index of the block */
|
||||
|
||||
/**
|
||||
Status for the current loop block. Can be any of the values from the loop_status enum.
|
||||
*/
|
||||
node_offset_t node_offset; /* Offset of the node */
|
||||
|
||||
/** Status for the current loop block. Can be any of the values from the loop_status enum. */
|
||||
int loop_status;
|
||||
|
||||
/**
|
||||
The job that is currently evaluated in the specified block.
|
||||
*/
|
||||
/** The job that is currently evaluated in the specified block. */
|
||||
job_t *job;
|
||||
|
||||
#if 0
|
||||
union
|
||||
{
|
||||
int while_state; /**< True if the loop condition has not yet been evaluated*/
|
||||
wchar_t *for_variable; /**< Name of the variable to loop over */
|
||||
int if_state; /**< The state of the if block, can be one of IF_STATE_UNTESTED, IF_STATE_FALSE, IF_STATE_TRUE */
|
||||
wchar_t *switch_value; /**< The value to test in a switch block */
|
||||
const wchar_t *source_dest; /**< The name of the file to source*/
|
||||
event_t *event; /**<The event that triggered this block */
|
||||
wchar_t *function_call_name;
|
||||
} param1;
|
||||
#endif
|
||||
|
||||
/**
|
||||
Name of file that created this block
|
||||
*/
|
||||
/** Name of file that created this block */
|
||||
const wchar_t *src_filename;
|
||||
|
||||
/**
|
||||
Line number where this block was created
|
||||
*/
|
||||
/** Line number where this block was created */
|
||||
int src_lineno;
|
||||
|
||||
/** Whether we should pop the environment variable stack when we're popped off of the block stack */
|
||||
|
@ -137,11 +116,6 @@ public:
|
|||
/** List of event blocks. */
|
||||
event_blockage_list_t event_blocks;
|
||||
|
||||
/**
|
||||
Next outer block
|
||||
*/
|
||||
block_t *outer;
|
||||
|
||||
/** Destructor */
|
||||
virtual ~block_t();
|
||||
};
|
||||
|
@ -296,12 +270,13 @@ struct profile_item_t
|
|||
};
|
||||
|
||||
struct tokenizer_t;
|
||||
class parse_execution_context_t;
|
||||
|
||||
class parser_t
|
||||
{
|
||||
friend class parse_execution_context_t;
|
||||
private:
|
||||
enum parser_type_t parser_type;
|
||||
std::vector<block_t> blocks;
|
||||
|
||||
/** Whether or not we output errors */
|
||||
const bool show_errors;
|
||||
|
@ -312,6 +287,12 @@ private:
|
|||
/** Position of last error */
|
||||
int err_pos;
|
||||
|
||||
/** Indication that we should skip all blocks */
|
||||
bool cancellation_requested;
|
||||
|
||||
/** Stack of execution contexts. We own these pointers and must delete them */
|
||||
std::vector<parse_execution_context_t *> execution_contexts;
|
||||
|
||||
/** Description of last error */
|
||||
wcstring err_buff;
|
||||
|
||||
|
@ -333,6 +314,9 @@ private:
|
|||
/** The jobs associated with this parser */
|
||||
job_list_t my_job_list;
|
||||
|
||||
/** The list of blocks, allocated with new. It's our responsibility to delete these */
|
||||
std::vector<block_t *> block_stack;
|
||||
|
||||
/**
|
||||
Keeps track of how many recursive eval calls have been made. Eval
|
||||
doesn't call itself directly, recursion happens on blocks and on
|
||||
|
@ -344,6 +328,7 @@ private:
|
|||
parser_t(const parser_t&);
|
||||
parser_t& operator=(const parser_t&);
|
||||
|
||||
|
||||
void parse_job_argument_list(process_t *p, job_t *j, tokenizer_t *tok, std::vector<completion_t>&, bool);
|
||||
int parse_job(process_t *p, job_t *j, tokenizer_t *tok);
|
||||
void skipped_exec(job_t * j);
|
||||
|
@ -353,7 +338,10 @@ private:
|
|||
void print_errors_stderr();
|
||||
|
||||
/** Create a job */
|
||||
job_t *job_create();
|
||||
job_t *job_create(const io_chain_t &io);
|
||||
|
||||
/** Adds a job to the beginning of the job list. */
|
||||
void job_add(job_t *job);
|
||||
|
||||
public:
|
||||
std::vector<profile_item_t*> profile_items;
|
||||
|
@ -377,9 +365,6 @@ public:
|
|||
/** Create a parser of the given type */
|
||||
parser_t(enum parser_type_t type, bool show_errors);
|
||||
|
||||
/** The current innermost block, allocated with new */
|
||||
block_t *current_block;
|
||||
|
||||
/** Global event blocks */
|
||||
event_blockage_list_t global_event_blocks;
|
||||
|
||||
|
@ -395,11 +380,15 @@ public:
|
|||
|
||||
\return 0 on success, 1 otherwise
|
||||
*/
|
||||
int eval(const wcstring &cmdStr, const io_chain_t &io, enum block_type_t block_type);
|
||||
int eval(const wcstring &cmd_str, const io_chain_t &io, enum block_type_t block_type);
|
||||
int eval_new_parser(const wcstring &cmd, const io_chain_t &io, enum block_type_t block_type);
|
||||
|
||||
/** Evaluates a block node at the given node offset in the topmost execution context */
|
||||
int eval_block_node(node_offset_t node_idx, const io_chain_t &io, enum block_type_t block_type);
|
||||
|
||||
/**
|
||||
Evaluate line as a list of parameters, i.e. tokenize it and perform parameter expansion and cmdsubst execution on the tokens.
|
||||
The output is inserted into output, and should be freed by the caller.
|
||||
The output is inserted into output.
|
||||
|
||||
\param line Line to evaluate
|
||||
\param output List to insert output to
|
||||
|
@ -408,7 +397,7 @@ public:
|
|||
\param line Line to evaluate
|
||||
\param output List to insert output to
|
||||
*/
|
||||
int eval_args(const wchar_t *line, std::vector<completion_t> &output);
|
||||
void eval_args(const wchar_t *line, std::vector<completion_t> &output);
|
||||
|
||||
/**
|
||||
Sets the current evaluation error. This function should only be used by libraries that are called by
|
||||
|
@ -417,7 +406,7 @@ public:
|
|||
\param p The character offset at which the error occured
|
||||
\param str The printf-style error message filter
|
||||
*/
|
||||
void error(int ec, int p, const wchar_t *str, ...);
|
||||
void error(int ec, size_t p, const wchar_t *str, ...);
|
||||
|
||||
/**
|
||||
Returns a string describing the current parser pisition in the format 'FILENAME (line LINE_NUMBER): LINE'.
|
||||
|
@ -442,6 +431,20 @@ public:
|
|||
/** Set the current position in the latest string of the tokenizer. */
|
||||
void set_pos(int p);
|
||||
|
||||
/** Returns the block at the given index. 0 corresponds to the innermost block. Returns NULL when idx is at or equal to the number of blocks. */
|
||||
const block_t *block_at_index(size_t idx) const;
|
||||
block_t *block_at_index(size_t idx);
|
||||
|
||||
/** Returns the current (innermost) block */
|
||||
const block_t *current_block() const;
|
||||
block_t *current_block();
|
||||
|
||||
/** Count of blocks */
|
||||
size_t block_count() const
|
||||
{
|
||||
return block_stack.size();
|
||||
}
|
||||
|
||||
/** Get the string currently parsed */
|
||||
const wchar_t *get_buffer() const;
|
||||
|
||||
|
@ -457,6 +460,9 @@ public:
|
|||
/** Remove the outermost block namespace */
|
||||
void pop_block();
|
||||
|
||||
/** Remove the outermost block, asserting it's the given one */
|
||||
void pop_block(const block_t *b);
|
||||
|
||||
/** Return a description of the given blocktype */
|
||||
const wchar_t *get_block_desc(int block) const;
|
||||
|
||||
|
@ -484,7 +490,7 @@ public:
|
|||
\param out if non-null, any errors in the command will be filled out into this buffer
|
||||
\param prefix the prefix string to prepend to each error message written to the \c out buffer
|
||||
*/
|
||||
int test(const wchar_t * buff, int *block_level = NULL, wcstring *out = NULL, const wchar_t *prefix = NULL);
|
||||
void get_backtrace(const wcstring &src, const parse_error_list_t &errors, wcstring *output) const;
|
||||
|
||||
/**
|
||||
Test if the specified string can be parsed as an argument list,
|
||||
|
@ -521,7 +527,7 @@ public:
|
|||
\param s the string to test
|
||||
\param min_match is the minimum number of characters that must match in a long style option, i.e. the longest common prefix between --help and any other option. If less than 3, 3 will be assumed.
|
||||
*/
|
||||
int is_help(const wchar_t *s, int min_match) const;
|
||||
static int is_help(const wchar_t *s, int min_match);
|
||||
|
||||
/**
|
||||
Returns the file currently evaluated by the parser. This can be
|
||||
|
@ -533,11 +539,14 @@ public:
|
|||
/**
|
||||
Write a stack trace starting at the specified block to the specified wcstring
|
||||
*/
|
||||
void stack_trace(block_t *b, wcstring &buff);
|
||||
void stack_trace(size_t block_idx, wcstring &buff) const;
|
||||
|
||||
int get_block_type(const wchar_t *cmd) const;
|
||||
const wchar_t *get_block_command(int type) const;
|
||||
};
|
||||
|
||||
/* Temporary */
|
||||
bool parser_use_ast(void);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
13
proc.cpp
13
proc.cpp
|
@ -136,7 +136,9 @@ static bool proc_had_barrier = false;
|
|||
int get_is_interactive(void)
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
return is_interactive;
|
||||
/* is_interactive is initialized to -1; ensure someone has popped/pushed it before then */
|
||||
assert(is_interactive >= 0);
|
||||
return is_interactive > 0;
|
||||
}
|
||||
|
||||
bool get_proc_had_barrier()
|
||||
|
@ -515,7 +517,8 @@ static void handle_child_status(pid_t pid, int status)
|
|||
process_t::process_t() :
|
||||
argv_array(),
|
||||
argv0_narrow(),
|
||||
type(0),
|
||||
type(),
|
||||
internal_block_node(NODE_OFFSET_INVALID),
|
||||
actual_cmd(),
|
||||
pid(0),
|
||||
pipe_write_fd(0),
|
||||
|
@ -638,6 +641,9 @@ int job_reap(bool interactive)
|
|||
|
||||
locked++;
|
||||
|
||||
/* Preserve the exit status */
|
||||
const int saved_status = proc_get_last_status();
|
||||
|
||||
/*
|
||||
job_read may fire an event handler, we do not want to call
|
||||
ourselves recursively (to avoid infinite recursion).
|
||||
|
@ -752,6 +758,9 @@ int job_reap(bool interactive)
|
|||
if (found)
|
||||
fflush(stdout);
|
||||
|
||||
/* Restore the exit status. */
|
||||
proc_set_last_status(saved_status);
|
||||
|
||||
locked = 0;
|
||||
|
||||
return found;
|
||||
|
|
20
proc.h
20
proc.h
|
@ -20,6 +20,7 @@
|
|||
#include "util.h"
|
||||
#include "io.h"
|
||||
#include "common.h"
|
||||
#include "parse_tree.h"
|
||||
|
||||
/**
|
||||
The status code use when a command was not found
|
||||
|
@ -54,7 +55,7 @@
|
|||
/**
|
||||
Types of processes
|
||||
*/
|
||||
enum
|
||||
enum process_type_t
|
||||
{
|
||||
/**
|
||||
A regular external command
|
||||
|
@ -72,6 +73,10 @@ enum
|
|||
A block of commands
|
||||
*/
|
||||
INTERNAL_BLOCK,
|
||||
|
||||
/** A block of commands, represented as a node */
|
||||
INTERNAL_BLOCK_NODE,
|
||||
|
||||
/**
|
||||
The exec builtin
|
||||
*/
|
||||
|
@ -81,8 +86,7 @@ enum
|
|||
*/
|
||||
INTERNAL_BUFFER,
|
||||
|
||||
}
|
||||
;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -151,8 +155,10 @@ public:
|
|||
INTERNAL_BUILTIN, \c INTERNAL_FUNCTION, \c INTERNAL_BLOCK,
|
||||
INTERNAL_EXEC, or INTERNAL_BUFFER
|
||||
*/
|
||||
int type;
|
||||
enum process_type_t type;
|
||||
|
||||
/* For internal block processes only, the node offset of the block */
|
||||
node_offset_t internal_block_node;
|
||||
|
||||
/** Sets argv */
|
||||
void set_argv(const wcstring_list_t &argv)
|
||||
|
@ -505,18 +511,12 @@ void job_free(job_t* j);
|
|||
*/
|
||||
void job_promote(job_t *job);
|
||||
|
||||
/**
|
||||
Create a new job.
|
||||
*/
|
||||
job_t *job_create();
|
||||
|
||||
/**
|
||||
Return the job with the specified job id.
|
||||
If id is 0 or less, return the last job used.
|
||||
*/
|
||||
job_t *job_get(job_id_t id);
|
||||
|
||||
|
||||
/**
|
||||
Return the job with the specified pid.
|
||||
*/
|
||||
|
|
196
reader.cpp
196
reader.cpp
|
@ -99,6 +99,7 @@ commence.
|
|||
#include "path.h"
|
||||
#include "parse_util.h"
|
||||
#include "parser_keywords.h"
|
||||
#include "parse_tree.h"
|
||||
|
||||
/**
|
||||
Maximum length of prefix string when printing completion
|
||||
|
@ -518,7 +519,7 @@ wcstring combine_command_and_autosuggestion(const wcstring &cmdline, const wcstr
|
|||
static void reader_repaint()
|
||||
{
|
||||
// Update the indentation
|
||||
parser_t::principal_parser().test(data->command_line.c_str(), &data->indents[0]);
|
||||
data->indents = parse_util_compute_indents(data->command_line);
|
||||
|
||||
// Combine the command and autosuggestion into one string
|
||||
wcstring full_line = combine_command_and_autosuggestion(data->command_line, data->autosuggestion);
|
||||
|
@ -659,117 +660,55 @@ bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t curso
|
|||
const size_t subcmd_offset = cmdsub_begin - buff;
|
||||
|
||||
const wcstring subcmd = wcstring(cmdsub_begin, cmdsub_end - cmdsub_begin);
|
||||
const wchar_t *subcmd_cstr = subcmd.c_str();
|
||||
const size_t subcmd_cursor_pos = cursor_pos - subcmd_offset;
|
||||
|
||||
/* Get the token containing the cursor */
|
||||
const wchar_t *subcmd_tok_begin = NULL, *subcmd_tok_end = NULL;
|
||||
assert(cursor_pos >= subcmd_offset);
|
||||
size_t subcmd_cursor_pos = cursor_pos - subcmd_offset;
|
||||
parse_util_token_extent(subcmd_cstr, subcmd_cursor_pos, &subcmd_tok_begin, &subcmd_tok_end, NULL, NULL);
|
||||
/* Parse this subcmd */
|
||||
parse_node_tree_t parse_tree;
|
||||
parse_tree_from_string(subcmd, parse_flag_continue_after_error | parse_flag_accept_incomplete_tokens, &parse_tree, NULL);
|
||||
|
||||
/* Compute the offset of the token before the cursor within the subcmd */
|
||||
assert(subcmd_tok_begin >= subcmd_cstr);
|
||||
assert(subcmd_tok_end >= subcmd_tok_begin);
|
||||
const size_t subcmd_tok_begin_offset = subcmd_tok_begin - subcmd_cstr;
|
||||
const size_t subcmd_tok_length = subcmd_tok_end - subcmd_tok_begin;
|
||||
/* Look for plain statements where the cursor is at the end of the command */
|
||||
const parse_node_t *matching_cmd_node = NULL;
|
||||
const size_t len = parse_tree.size();
|
||||
for (size_t i=0; i < len; i++)
|
||||
{
|
||||
const parse_node_t &node = parse_tree.at(i);
|
||||
|
||||
/* Now parse the subcmd, looking for commands */
|
||||
bool had_cmd = false, previous_token_is_cmd = false;
|
||||
tokenizer_t tok(subcmd_cstr, TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS);
|
||||
for (; tok_has_next(&tok); tok_next(&tok))
|
||||
{
|
||||
size_t tok_pos = static_cast<size_t>(tok_get_pos(&tok));
|
||||
if (tok_pos > subcmd_tok_begin_offset)
|
||||
{
|
||||
/* We've passed the token we're interested in */
|
||||
break;
|
||||
}
|
||||
/* Only interested in plain statements with source */
|
||||
if (node.type != symbol_plain_statement || ! node.has_source())
|
||||
continue;
|
||||
|
||||
int last_type = tok_last_type(&tok);
|
||||
/* Skip decorated statements */
|
||||
if (parse_tree.decoration_for_plain_statement(node) != parse_statement_decoration_none)
|
||||
continue;
|
||||
|
||||
switch (last_type)
|
||||
{
|
||||
case TOK_STRING:
|
||||
{
|
||||
if (had_cmd)
|
||||
{
|
||||
/* Parameter to the command. */
|
||||
}
|
||||
else
|
||||
{
|
||||
const wcstring potential_cmd = tok_last(&tok);
|
||||
if (parser_keywords_is_subcommand(potential_cmd))
|
||||
{
|
||||
if (potential_cmd == L"command" || potential_cmd == L"builtin")
|
||||
{
|
||||
/* 'command' and 'builtin' defeat abbreviation expansion. Skip this command. */
|
||||
had_cmd = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Other subcommand. Pretend it doesn't exist so that we can expand the following command */
|
||||
had_cmd = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It's a normal command */
|
||||
had_cmd = true;
|
||||
if (tok_pos == subcmd_tok_begin_offset)
|
||||
{
|
||||
/* This is the token we care about! */
|
||||
previous_token_is_cmd = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* Get the command node. Skip it if we can't or it has no source */
|
||||
const parse_node_t *cmd_node = parse_tree.get_child(node, 0, parse_token_type_string);
|
||||
if (cmd_node == NULL || ! cmd_node->has_source())
|
||||
continue;
|
||||
|
||||
case TOK_REDIRECT_NOCLOB:
|
||||
case TOK_REDIRECT_OUT:
|
||||
case TOK_REDIRECT_IN:
|
||||
case TOK_REDIRECT_APPEND:
|
||||
case TOK_REDIRECT_FD:
|
||||
{
|
||||
if (!had_cmd)
|
||||
{
|
||||
break;
|
||||
}
|
||||
tok_next(&tok);
|
||||
break;
|
||||
}
|
||||
|
||||
case TOK_PIPE:
|
||||
case TOK_BACKGROUND:
|
||||
case TOK_END:
|
||||
{
|
||||
had_cmd = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case TOK_COMMENT:
|
||||
case TOK_ERROR:
|
||||
default:
|
||||
/* Now see if its source range contains our cursor, including at the end */
|
||||
if (subcmd_cursor_pos >= cmd_node->source_start && subcmd_cursor_pos <= cmd_node->source_start + cmd_node->source_length)
|
||||
{
|
||||
/* Success! */
|
||||
matching_cmd_node = cmd_node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now if we found a command node, expand it */
|
||||
bool result = false;
|
||||
if (previous_token_is_cmd)
|
||||
if (matching_cmd_node != NULL)
|
||||
{
|
||||
/* The token is a command. Try expanding it as an abbreviation. */
|
||||
const wcstring token = wcstring(subcmd, subcmd_tok_begin_offset, subcmd_tok_length);
|
||||
assert(matching_cmd_node->type == parse_token_type_string);
|
||||
const wcstring token = matching_cmd_node->get_source(subcmd);
|
||||
wcstring abbreviation;
|
||||
if (expand_abbreviation(token, &abbreviation))
|
||||
{
|
||||
/* There was an abbreviation! Replace the token in the full command. Maintain the relative position of the cursor. */
|
||||
if (output != NULL)
|
||||
{
|
||||
size_t cmd_tok_begin_offset = subcmd_tok_begin_offset + subcmd_offset;
|
||||
output->assign(cmdline);
|
||||
output->replace(cmd_tok_begin_offset, subcmd_tok_length, abbreviation);
|
||||
output->replace(subcmd_offset + matching_cmd_node->source_start, matching_cmd_node->source_length, abbreviation);
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
|
@ -1494,7 +1433,7 @@ struct autosuggestion_context_t
|
|||
{
|
||||
const completion_t &comp = completions.at(0);
|
||||
size_t cursor = this->cursor_pos;
|
||||
this->autosuggestion = completion_apply_to_command_line(comp.completion.c_str(), comp.flags, this->search_string, &cursor, true /* append only */);
|
||||
this->autosuggestion = completion_apply_to_command_line(comp.completion, comp.flags, this->search_string, &cursor, true /* append only */);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -2129,11 +2068,9 @@ static void reader_interactive_destroy()
|
|||
|
||||
void reader_sanity_check()
|
||||
{
|
||||
if (get_is_interactive())
|
||||
/* Note: 'data' is non-null if we are interactive, except in the testing environment */
|
||||
if (get_is_interactive() && data != NULL)
|
||||
{
|
||||
if (!data)
|
||||
sanity_lose();
|
||||
|
||||
if (!(data->buff_pos <= data->command_length()))
|
||||
sanity_lose();
|
||||
|
||||
|
@ -2263,7 +2200,6 @@ static void handle_token_history(int forward, int reset)
|
|||
*/
|
||||
if (data->history_search.go_backwards())
|
||||
{
|
||||
wcstring item = data->history_search.current_string();
|
||||
data->token_history_buff = data->history_search.current_string();
|
||||
}
|
||||
current_pos = data->token_history_buff.size();
|
||||
|
@ -2533,28 +2469,26 @@ void reader_run_command(parser_t &parser, const wcstring &cmd)
|
|||
|
||||
int reader_shell_test(const wchar_t *b)
|
||||
{
|
||||
int res = parser_t::principal_parser().test(b);
|
||||
assert(b != NULL);
|
||||
wcstring bstr = b;
|
||||
|
||||
/* Append a newline, to act as a statement terminator */
|
||||
bstr.push_back(L'\n');
|
||||
|
||||
parse_error_list_t errors;
|
||||
int res = parse_util_detect_errors(bstr, &errors);
|
||||
|
||||
if (res & PARSER_TEST_ERROR)
|
||||
{
|
||||
wcstring sb;
|
||||
wcstring error_desc;
|
||||
parser_t::principal_parser().get_backtrace(bstr, errors, &error_desc);
|
||||
|
||||
const int tmp[1] = {0};
|
||||
const int tmp2[1] = {0};
|
||||
const wcstring empty;
|
||||
|
||||
s_write(&data->screen,
|
||||
empty,
|
||||
empty,
|
||||
empty,
|
||||
0,
|
||||
tmp,
|
||||
tmp2,
|
||||
0);
|
||||
|
||||
|
||||
parser_t::principal_parser().test(b, NULL, &sb, L"fish");
|
||||
fwprintf(stderr, L"%ls", sb.c_str());
|
||||
// ensure we end with a newline. Also add an initial newline, because it's likely the user just hit enter and so there's junk on the current line
|
||||
if (! string_suffixes_string(L"\n", error_desc))
|
||||
{
|
||||
error_desc.push_back(L'\n');
|
||||
}
|
||||
fwprintf(stderr, L"\n%ls", error_desc.c_str());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@ -2804,10 +2738,10 @@ static void reader_super_highlight_me_plenty(size_t match_highlight_pos)
|
|||
}
|
||||
|
||||
|
||||
int exit_status()
|
||||
bool shell_is_exiting()
|
||||
{
|
||||
if (get_is_interactive())
|
||||
return job_list_is_empty() && data->end_loop;
|
||||
return job_list_is_empty() && data != NULL && data->end_loop;
|
||||
else
|
||||
return end_loop;
|
||||
}
|
||||
|
@ -2823,14 +2757,11 @@ static void handle_end_loop()
|
|||
job_t *j;
|
||||
int stopped_jobs_count=0;
|
||||
int is_breakpoint=0;
|
||||
block_t *b;
|
||||
parser_t &parser = parser_t::principal_parser();
|
||||
const parser_t &parser = parser_t::principal_parser();
|
||||
|
||||
for (b = parser.current_block;
|
||||
b;
|
||||
b = b->outer)
|
||||
for (size_t i = 0; i < parser.block_count(); i++)
|
||||
{
|
||||
if (b->type() == BREAKPOINT)
|
||||
if (parser.block_at_index(i)->type() == BREAKPOINT)
|
||||
{
|
||||
is_breakpoint = 1;
|
||||
break;
|
||||
|
@ -3061,6 +2992,7 @@ const wchar_t *reader_readline(void)
|
|||
is_interactive_read = 1;
|
||||
c=input_readch();
|
||||
is_interactive_read = was_interactive_read;
|
||||
//fprintf(stderr, "C: %lx\n", (long)c);
|
||||
|
||||
if (((!wchar_private(c))) && (c>31) && (c != 127))
|
||||
{
|
||||
|
@ -3235,6 +3167,9 @@ const wchar_t *reader_readline(void)
|
|||
const wchar_t *token_begin, *token_end;
|
||||
parse_util_token_extent(cmdsub_begin, data->buff_pos - (cmdsub_begin-buff), &token_begin, &token_end, 0, 0);
|
||||
|
||||
/* Hack: the token may extend past the end of the command substitution, e.g. in (echo foo) the last token is 'foo)'. Don't let that happen. */
|
||||
if (token_end > cmdsub_end) token_end = cmdsub_end;
|
||||
|
||||
/* Figure out how many steps to get from the current position to the end of the current token. */
|
||||
size_t end_of_token_offset = token_end - buff;
|
||||
|
||||
|
@ -3389,7 +3324,7 @@ const wchar_t *reader_readline(void)
|
|||
{
|
||||
//history_reset();
|
||||
data->history_search.go_to_end();
|
||||
reader_set_buffer(data->search_buff.c_str(), data->search_buff.size());
|
||||
reader_set_buffer(data->search_buff, data->search_buff.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3466,13 +3401,10 @@ const wchar_t *reader_readline(void)
|
|||
case 0:
|
||||
{
|
||||
/* Finished command, execute it. Don't add items that start with a leading space. */
|
||||
if (! data->command_line.empty() && data->command_line.at(0) != L' ')
|
||||
{
|
||||
if (data->history != NULL)
|
||||
if (data->history != NULL && ! data->command_line.empty() && data->command_line.at(0) != L' ')
|
||||
{
|
||||
data->history->add_with_file_detection(data->command_line);
|
||||
}
|
||||
}
|
||||
finished=1;
|
||||
data->buff_pos=data->command_length();
|
||||
reader_repaint();
|
||||
|
@ -3961,13 +3893,15 @@ static int read_ni(int fd, const io_chain_t &io)
|
|||
res = 1;
|
||||
}
|
||||
|
||||
wcstring sb;
|
||||
if (! parser.test(str.c_str(), 0, &sb, L"fish"))
|
||||
parse_error_list_t errors;
|
||||
if (! parse_util_detect_errors(str, &errors))
|
||||
{
|
||||
parser.eval(str, io, TOP);
|
||||
}
|
||||
else
|
||||
{
|
||||
wcstring sb;
|
||||
parser.get_backtrace(str, errors, &sb);
|
||||
fwprintf(stderr, L"%ls", sb.c_str());
|
||||
res = 1;
|
||||
}
|
||||
|
|
2
reader.h
2
reader.h
|
@ -217,7 +217,7 @@ void reader_set_exit_on_interrupt(bool flag);
|
|||
/**
|
||||
Returns true if the shell is exiting, 0 otherwise.
|
||||
*/
|
||||
int exit_status();
|
||||
bool shell_is_exiting();
|
||||
|
||||
/**
|
||||
The readers interrupt signal handler. Cancels all currently running blocks.
|
||||
|
|
141
share/completions/adb.fish
Normal file
141
share/completions/adb.fish
Normal file
|
@ -0,0 +1,141 @@
|
|||
# Completions for Android adb command
|
||||
|
||||
function __fish_adb_no_subcommand --description 'Test if adb has yet to be given the subcommand'
|
||||
for i in (commandline -opc)
|
||||
if contains -- $i connect disconnect devices push pull sync shell emu logcat install uninstall jdwp forward bugreport backup restore version help wait-for-device start-server kill-server remount reboot get-state get-serialno get-devpath status-window root usb tcpip ppp
|
||||
return 1
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function __fish_adb_get_devices --description 'Run adb devices and parse output'
|
||||
# This seems reasonably portable for all the platforms adb runs on
|
||||
set -l count (ps x | grep -c adb)
|
||||
set -l TAB \t
|
||||
# Don't run adb devices unless the server is already started - it takes a while to init
|
||||
if [ $count -gt 1 ]
|
||||
# The tail is to strip the header line, the sed is to massage the -l format
|
||||
# into a simple "identifier <TAB> modelname" format which is what we want for complete
|
||||
adb devices -l | tail -n +2 | sed -E -e "s/([^ ]+) +./\1$TAB/" -e "s/$TAB.*model:([^ ]+).*/$TAB\1/"
|
||||
end
|
||||
end
|
||||
|
||||
function __fish_adb_run_command --description 'Runs adb with any -s parameters already given on the command line'
|
||||
set -l sopt
|
||||
set -l sopt_is_next
|
||||
set -l cmd (commandline -poc)
|
||||
set -e cmd[1]
|
||||
for i in $cmd
|
||||
if test $sopt_is_next
|
||||
set sopt -s $i
|
||||
break
|
||||
else
|
||||
switch $i
|
||||
case -s
|
||||
set sopt_is_next 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# If no -s option, see if there's a -d or -e instead
|
||||
if test -z "$sopt"
|
||||
if contains -- -d $cmd
|
||||
set sopt '-d'
|
||||
else if contains -- -e $cmd
|
||||
set sopt '-e'
|
||||
end
|
||||
end
|
||||
|
||||
# adb returns CRLF (seemingly) so strip CRs
|
||||
adb $sopt shell $argv | sed s/\r//
|
||||
end
|
||||
|
||||
function __fish_adb_list_packages
|
||||
__fish_adb_run_command pm list packages | sed s/package://
|
||||
end
|
||||
|
||||
|
||||
function __fish_adb_list_uninstallable_packages
|
||||
# -3 doesn't exactly mean show uninstallable, but it's the closest you can get to with pm list
|
||||
__fish_adb_run_command pm list packages -3 | sed s/package://
|
||||
end
|
||||
|
||||
# Generic options, must come before command
|
||||
complete -n '__fish_adb_no_subcommand' -c adb -s s -x -a "(__fish_adb_get_devices)" -d 'Device to communicate with'
|
||||
complete -n '__fish_adb_no_subcommand' -c adb -s d -d 'Communicate with first USB device'
|
||||
complete -n '__fish_adb_no_subcommand' -c adb -s e -d 'Communicate with emulator'
|
||||
|
||||
# Commands
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'connect' -d 'Connect to device'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'disconnect' -d 'Disconnect from device'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'devices' -d 'List all connected devices'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'push' -d 'Copy file to device'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'pull' -d 'Copy file from device'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'sync' -d 'Copy host->device only if changed'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'shell' -d 'Run remote shell [command]'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'emu' -d 'Run emulator console command'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'logcat' -d 'View device log'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'install' -d 'Install package'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'uninstall' -d 'Uninstall package'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'jdwp' -d 'List PIDs of processes hosting a JDWP transport'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'forward' -d 'Port forwarding'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'bugreport' -d 'Return bugreport information'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'backup' -d 'Perform device backup'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'restore' -d 'Restore device from backup'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'version' -d 'Show adb version'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'help' -d 'Show adb help'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'wait-for-device' -d 'Block until device is online'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'start-server' -d 'Ensure that there is a server running'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'kill-server' -d 'Kill the server if it is running'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'remount' -d 'Remounts the /system partition on the device read-write'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'reboot' -d 'Reboots the device, optionally into the bootloader or recovery program'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'get-state' -d 'Prints state of the device'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'get-serialno' -d 'Prints serial number of the device'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'get-devpath' -d 'Prints device path'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'status-window' -d 'Continuously print the device status'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'root' -d 'Restart the adbd daemon with root permissions'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'usb' -d 'Restart the adbd daemon listening on USB'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'tcpip' -d 'Restart the adbd daemon listening on TCP'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'ppp' -d 'Run PPP over USB'
|
||||
complete -f -n '__fish_adb_no_subcommand' -c adb -a 'sideload' -d 'Install zip-file on device in sideload mode'
|
||||
|
||||
# install options
|
||||
complete -n '__fish_seen_subcommand_from install' -c adb -s l -d 'Forward-lock the app'
|
||||
complete -n '__fish_seen_subcommand_from install' -c adb -s r -d 'Reinstall the app keeping its data'
|
||||
complete -n '__fish_seen_subcommand_from install' -c adb -s s -d 'Install on SD card instead of internal storage'
|
||||
complete -n '__fish_seen_subcommand_from install' -c adb -l 'algo' -d 'Algorithm name'
|
||||
complete -n '__fish_seen_subcommand_from install' -c adb -l 'key' -d 'Hex-encoded key'
|
||||
complete -n '__fish_seen_subcommand_from install' -c adb -l 'iv' -d 'Hex-encoded iv'
|
||||
|
||||
# uninstall
|
||||
complete -n '__fish_seen_subcommand_from uninstall' -c adb -s k -d 'Keep the data and cache directories'
|
||||
complete -n '__fish_seen_subcommand_from uninstall' -c adb -f -u -a "(__fish_adb_list_uninstallable_packages)"
|
||||
|
||||
# devices
|
||||
complete -n '__fish_seen_subcommand_from devices' -c adb -s l -d 'Also list device qualifiers'
|
||||
|
||||
# disconnect
|
||||
complete -n '__fish_seen_subcommand_from disconnect' -c adb -x -a "(__fish_adb_get_devices)" -d 'Device to disconnect'
|
||||
|
||||
# backup
|
||||
complete -n '__fish_seen_subcommand_from backup' -c adb -s f -d 'File to write backup data to'
|
||||
complete -n '__fish_seen_subcommand_from backup' -c adb -o 'apk' -d 'Enable backup of the .apks themselves'
|
||||
complete -n '__fish_seen_subcommand_from backup' -c adb -o 'noapk' -d 'Disable backup of the .apks themselves (default)'
|
||||
complete -n '__fish_seen_subcommand_from backup' -c adb -o 'obb' -d 'Enable backup of any installed apk expansion'
|
||||
complete -n '__fish_seen_subcommand_from backup' -c adb -o 'noobb' -d 'Disable backup of any installed apk expansion (default)'
|
||||
complete -n '__fish_seen_subcommand_from backup' -c adb -o 'shared' -d 'Enable backup of the device\'s shared storage / SD card contents'
|
||||
complete -n '__fish_seen_subcommand_from backup' -c adb -o 'noshared' -d 'Disable backup of the device\'s shared storage / SD card contents (default)'
|
||||
complete -n '__fish_seen_subcommand_from backup' -c adb -o 'all' -d 'Back up all installed applications'
|
||||
complete -n '__fish_seen_subcommand_from backup' -c adb -o 'system' -d 'Include system applications in -all (default)'
|
||||
complete -n '__fish_seen_subcommand_from backup' -c adb -o 'nosystem' -d 'Exclude system applications in -all'
|
||||
complete -n '__fish_seen_subcommand_from backup' -c adb -f -a "(__fish_adb_list_packages)" -d 'Package(s) to backup'
|
||||
|
||||
# reboot
|
||||
complete -n '__fish_seen_subcommand_from reboot' -c adb -x -a 'bootloader recovery'
|
||||
|
||||
# forward
|
||||
complete -n '__fish_seen_subcommand_from forward' -c adb -l 'list' -d 'List all forward socket connections'
|
||||
complete -n '__fish_seen_subcommand_from forward' -c adb -l 'no-rebind' -d 'Fails the forward if local is already forwarded'
|
||||
complete -n '__fish_seen_subcommand_from forward' -c adb -l 'remove' -d 'Remove a specific forward socket connection'
|
||||
complete -n '__fish_seen_subcommand_from forward' -c adb -l 'remove-all' -d 'Remove all forward socket connections'
|
15
share/completions/elixir.fish
Normal file
15
share/completions/elixir.fish
Normal file
|
@ -0,0 +1,15 @@
|
|||
complete -f -c elixir -s v -d "Prints version and exit"
|
||||
complete -f -c elixir -s e -d "Evaluates the given command"
|
||||
complete -c elixir -s r -d "Requires the given files/patterns"
|
||||
complete -c elixir -s S -d "Finds and executes the given script"
|
||||
complete -c elixir -s pr -d "Requires the given files/patterns in parallel"
|
||||
complete -c elixir -s pa -d "Prepends the given path to Erlang code path"
|
||||
complete -c elixir -s pz -d "Appends the given path to Erlang code path"
|
||||
complete -c elixir -l app -d "Start the given app and its dependencies"
|
||||
complete -f -c elixir -l erl -d "Switches to be passed down to erlang"
|
||||
complete -f -c elixir -l name -d "Makes and assigns a name to the distributed node"
|
||||
complete -f -c elixir -l sname -d "Makes and assigns a short name to the distributed node"
|
||||
complete -f -c elixir -l cookie -d "Sets a cookie for this distributed node"
|
||||
complete -f -c elixir -l hidden -d "Makes a hidden node"
|
||||
complete -f -c elixir -l detached -d "Starts the Erlang VM detached from console"
|
||||
complete -f -c elixir -l no-halt -d "Does not halt the Erlang VM after execution"
|
16
share/completions/iex.fish
Normal file
16
share/completions/iex.fish
Normal file
|
@ -0,0 +1,16 @@
|
|||
complete -f -c iex -s v -d "Prints version and exit"
|
||||
complete -f -c iex -s e -d "Evaluates the given command"
|
||||
complete -c iex -s r -d "Requires the given files/patterns"
|
||||
complete -c iex -s S -d "Finds and executes the given script"
|
||||
complete -c iex -s pr -d "Requires the given files/patterns in parallel"
|
||||
complete -c iex -s pa -d "Prepends the given path to Erlang code path"
|
||||
complete -c iex -s pz -d "Appends the given path to Erlang code path"
|
||||
complete -c iex -l app -d "Start the given app and its dependencies"
|
||||
complete -f -c iex -l erl -d "Switches to be passed down to erlang"
|
||||
complete -f -c iex -l name -d "Makes and assigns a name to the distributed node"
|
||||
complete -f -c iex -l sname -d "Makes and assigns a short name to the distributed node"
|
||||
complete -f -c iex -l cookie -d "Sets a cookie for this distributed node"
|
||||
complete -f -c iex -l hidden -d "Makes a hidden node"
|
||||
complete -f -c iex -l detached -d "Starts the Erlang VM detached from console"
|
||||
complete -f -c iex -l remsh -d "Connects to a node using a remote shell"
|
||||
complete -c iex -l dot-iex -d "Overrides default .iex file and uses path instead; path can be empty, then no file will be loaded"
|
7
share/completions/lsusb.fish
Normal file
7
share/completions/lsusb.fish
Normal file
|
@ -0,0 +1,7 @@
|
|||
complete -c lsusb -s v -l verbose --description "Increase verbosity (show descriptors)"
|
||||
complete -x -c lsusb -s s -a '(__fish_complete_usb)' --description "Show only devices with specified device and/or bus numbers (in decimal)"
|
||||
complete -c lsusb -s d --description "Show only devices with the specified vendor and product ID numbers (in hexadecimal)"
|
||||
complete -c lsusb -s D -l device --description "Selects which device lsusb will examine"
|
||||
complete -c lsusb -s t -l tree --description "Dump the physical USB device hierarchy as a tree"
|
||||
complete -c lsusb -s V -l version --description "Show version of program"
|
||||
complete -c lsusb -s h -l help --description "Show usage and help"
|
93
share/completions/mix.fish
Normal file
93
share/completions/mix.fish
Normal file
|
@ -0,0 +1,93 @@
|
|||
function __fish_mix_needs_command
|
||||
set cmd (commandline -opc)
|
||||
if [ (count $cmd) -eq 1 -a $cmd[1] = 'mix' ]
|
||||
return 0
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
function __fish_mix_using_command
|
||||
set cmd (commandline -opc)
|
||||
if [ (count $cmd) -gt 1 ]
|
||||
if [ $argv[1] = $cmd[2] ]
|
||||
return 0
|
||||
end
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
|
||||
complete -f -c mix -n '__fish_mix_needs_command' -a archive -d "Archive this project into a .ez file"
|
||||
complete -f -c mix -n '__fish_mix_needs_command' -a clean -d "Clean generated application files"
|
||||
complete -f -c mix -n '__fish_mix_needs_command' -a cmd -d "Executes the given command"
|
||||
complete -c mix -n '__fish_mix_needs_command' -a compile -d "Compile source files"
|
||||
complete -f -c mix -n '__fish_mix_needs_command' -a deps -d "List dependencies and their status"
|
||||
complete -f -c mix -n '__fish_mix_needs_command' -a deps.clean -d "Remove the given dependencies' files"
|
||||
complete -f -c mix -n '__fish_mix_needs_command' -a deps.compile -d "Compile dependencies"
|
||||
complete -f -c mix -n '__fish_mix_needs_command' -a deps.get -d "Get all out of date dependencies"
|
||||
complete -f -c mix -n '__fish_mix_needs_command' -a deps.unlock -d "Unlock the given dependencies"
|
||||
complete -f -c mix -n '__fish_mix_needs_command' -a deps.update -d "Update the given dependencies"
|
||||
complete -f -c mix -n '__fish_mix_needs_command' -a do -d "Executes the tasks separated by comma"
|
||||
complete -f -c mix -n '__fish_mix_needs_command' -a escriptize -d "Generates an escript for the project"
|
||||
complete -f -c mix -n '__fish_mix_needs_command' -a help -d "Print help information for tasks"
|
||||
complete -c mix -n '__fish_mix_needs_command' -a local -d "List local tasks"
|
||||
complete -c mix -n '__fish_mix_needs_command' -a local.install -d "Install a task or an archive locally"
|
||||
complete -c mix -n '__fish_mix_needs_command' -a local.rebar -d "Install rebar locally"
|
||||
complete -c mix -n '__fish_mix_needs_command' -a local.uninstall -d "Uninstall local tasks or archives"
|
||||
complete -c mix -n '__fish_mix_needs_command' -a new -d "Creates a new Elixir project"
|
||||
complete -f -c mix -n '__fish_mix_needs_command' -a run -d "Run the given file or expression"
|
||||
complete -f -c mix -n '__fish_mix_needs_command' -a test -d "Run a project's tests"
|
||||
|
||||
# archive subcommand
|
||||
complete -f -c mix -n '__fish_mix_using_command archive' -s o -d "specify output file name"
|
||||
complete -f -c mix -n '__fish_mix_using_command archive' -l no-compile -d "skip compilation"
|
||||
|
||||
# clean subcommand
|
||||
complete -f -c mix -n '__fish_mix_using_command clean' -l all -d "Clean everything, including dependencies"
|
||||
|
||||
# escriptize subcommand
|
||||
complete -f -c mix -n '__fish_mix_using_command escriptize' -l force -d "forces compilation regardless of modification times"
|
||||
complete -f -c mix -n '__fish_mix_using_command escriptize' -l no-compile -d "skips compilation to .beam files"
|
||||
|
||||
# new subcommand
|
||||
complete -f -c mix -n '__fish_mix_using_command new' -l bare -d "can be given to not generate an OTP application skeleton"
|
||||
complete -f -c mix -n '__fish_mix_using_command new' -l module
|
||||
complete -f -c mix -n '__fish_mix_using_command new' -l umbrella -d "can be given to generate an umbrella project"
|
||||
|
||||
# run subcommand
|
||||
complete -c mix -n '__fish_mix_using_command run' -l eval -s e -d "Evaluates the given code"
|
||||
complete -c mix -n '__fish_mix_using_command run' -l require -s r -d "Requires pattern before running the command"
|
||||
complete -c mix -n '__fish_mix_using_command run' -l parallel-require -s pr -d "Requires pattern in parallel"
|
||||
complete -c mix -n '__fish_mix_using_command run' -l no-halt -d "Does not halt the system after running the command"
|
||||
complete -c mix -n '__fish_mix_using_command run' -l no-compile -d "Does not compile even if files require compilation"
|
||||
complete -c mix -n '__fish_mix_using_command run' -l no-start -d "Does not start applications after compilation"
|
||||
|
||||
# test subcommand
|
||||
complete -c mix -n '__fish_mix_using_command test' -l trace -d "run tests with detailed reporting. Automatically sets `--max-cases` to 1"
|
||||
complete -c mix -n '__fish_mix_using_command test' -l max-cases -d "set the maximum number of cases running async"
|
||||
complete -c mix -n '__fish_mix_using_command test' -l cover -d "the directory to include coverage results"
|
||||
complete -c mix -n '__fish_mix_using_command test' -l force -d "forces compilation regardless of modification times"
|
||||
complete -c mix -n '__fish_mix_using_command test' -l no-compile -d "do not compile, even if files require compilation"
|
||||
complete -c mix -n '__fish_mix_using_command test' -l no-start -d "do not start applications after compilation"
|
||||
complete -c mix -n '__fish_mix_using_command test' -l no-color -d "disable color in the output"
|
||||
|
||||
# help subcommand
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a archive -d "Archive this project into a .ez file"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a clean -d "Clean generated application files"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a cmd -d "Executes the given command"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a compile -d "Compile source files"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a deps -d "List dependencies and their status"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a deps.clean -d "Remove the given dependencies' files"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a deps.compile -d "Compile dependencies"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a deps.get -d "Get all out of date dependencies"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a deps.unlock -d "Unlock the given dependencies"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a deps.update -d "Update the given dependencies"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a do -d "Executes the tasks separated by comma"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a escriptize -d "Generates an escript for the project"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a local -d "List local tasks"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a local.install -d "Install a task or an archive locally"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a local.rebar -d "Install rebar locally"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a local.uninstall -d "Uninstall local tasks or archives"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a new -d "Creates a new Elixir project"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a run -d "Run the given file or expression"
|
||||
complete -f -c mix -n '__fish_mix_using_command help' -a test -d "Run a project's tests"
|
|
@ -89,10 +89,12 @@ function __fish_reconstruct_path -d "Update PATH when fish_user_paths changes" -
|
|||
|
||||
set -e __fish_added_user_paths
|
||||
for x in $fish_user_paths[-1..1]
|
||||
if not contains $x $local_path
|
||||
set local_path $x $local_path
|
||||
if set -l idx (contains --index $x $local_path)
|
||||
set -e local_path[$idx]
|
||||
else
|
||||
set -g __fish_added_user_paths $__fish_added_user_paths $x
|
||||
end
|
||||
set local_path $x $local_path
|
||||
end
|
||||
set -xg PATH $local_path
|
||||
end
|
||||
|
|
|
@ -298,12 +298,12 @@ function __fish_complete_svn -d 'Complete svn and its wrappers' --argument-names
|
|||
#
|
||||
# Completions for the 'relocate' subcommand
|
||||
#
|
||||
_svn_cmpl_ $relocate -xa '( svn info | grep URL: | cut --delimiter " " --fields 2 ) http:// ftp:// svn+ssh:// svn+ssh://(__fish_print_hostnames)'
|
||||
_svn_cmpl_ $relocate -xa '( svn info | grep URL: | cut -d " " -f 2 ) http:// ftp:// svn+ssh:// svn+ssh://(__fish_print_hostnames)'
|
||||
|
||||
#
|
||||
# Completions for the 'switch', 'sw' subcommands
|
||||
#
|
||||
_svn_cmpl_ $switch -l relocate --description 'Relocate via URL-rewriting' -xa '( svn info | grep URL: | cut --delimiter " " --fields 2 ) http:// ftp:// svn+ssh:// svn+ssh://(__fish_print_hostnames)'
|
||||
_svn_cmpl_ $switch -l relocate --description 'Relocate via URL-rewriting' -xa '( svn info | grep URL: | cut -d " " -f 2 ) http:// ftp:// svn+ssh:// svn+ssh://(__fish_print_hostnames)'
|
||||
|
||||
#
|
||||
# Completions for the 'status', 'st' subcommands
|
||||
|
|
3
share/functions/__fish_complete_usb.fish
Normal file
3
share/functions/__fish_complete_usb.fish
Normal file
|
@ -0,0 +1,3 @@
|
|||
function __fish_complete_usb
|
||||
lsusb | awk '{print $2 ":" $4}'| cut -c1-7
|
||||
end
|
|
@ -371,6 +371,7 @@ function __fish_git_prompt --description "Prompt function for Git"
|
|||
set -l informative_status
|
||||
|
||||
__fish_git_prompt_validate_chars
|
||||
__fish_git_prompt_validate_colors
|
||||
|
||||
if test "true" = $inside_worktree
|
||||
if test -n "$__fish_git_prompt_show_informative_status"
|
||||
|
@ -403,8 +404,6 @@ function __fish_git_prompt --description "Prompt function for Git"
|
|||
end
|
||||
end
|
||||
|
||||
__fish_git_prompt_validate_colors
|
||||
|
||||
set -l branch_color $___fish_git_prompt_color_branch
|
||||
set -l branch_done $___fish_git_prompt_color_branch_done
|
||||
if test -n "$__fish_git_prompt_showcolorhints"
|
||||
|
|
|
@ -16,10 +16,15 @@ function __fish_print_hostnames -d "Print a list of known hostnames"
|
|||
# Does not match hostnames with @directives specified
|
||||
sgrep -Eoh '^[^#@|, ]*' ~/.ssh/known_hosts{,2} ^/dev/null
|
||||
|
||||
# Print hosts from system wide ssh configuration file
|
||||
if [ -e /etc/ssh/ssh_config ]
|
||||
# Ignore lines containing wildcards
|
||||
sgrep -Eoi '^ *host[^*]*$' /etc/ssh/ssh_config | cut -d '=' -f 2 | tr ' ' '\n'
|
||||
end
|
||||
|
||||
# Print hosts from ssh configuration file
|
||||
if [ -e ~/.ssh/config ]
|
||||
# Ignore lines containing wildcards
|
||||
sgrep -Eoi '^ *host[^*]*$' ~/.ssh/config | cut -d '=' -f 2 | tr ' ' '\n'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ function __fish_print_packages
|
|||
end
|
||||
|
||||
# Remove package version information from output and pipe into cache file
|
||||
rpm -qa >$cache_file |sed -e 's/-[^-]*-[^-]*$//' | sed -e 's/$/'\t$package'/' &
|
||||
rpm -qa | sed -e 's/-[^-]*-[^-]*$//' | sed -e 's/$/'\t$package'/' >$cache_file &
|
||||
end
|
||||
|
||||
# This completes the package name from the portage tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
function __fish_print_svn_rev --description 'Print svn revisions'
|
||||
svn info | grep "Last Changed Rev" | cut --delimiter " " --fields 4
|
||||
svn info | grep "Last Changed Rev" | cut -d " " -f 4
|
||||
echo \{\tRevision at start of the date
|
||||
echo HEAD\tLatest in repository
|
||||
echo BASE\tBase rev of item\'s working copy
|
||||
|
|
|
@ -89,6 +89,9 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fis
|
|||
bind \e\[1\;9B history-token-search-forward # iTerm2
|
||||
bind \e\[1\;9C forward-word #iTerm2
|
||||
bind \e\[1\;9D backward-word #iTerm2
|
||||
# Bash compatibility
|
||||
# https://github.com/fish-shell/fish-shell/issues/89
|
||||
bind \e. history-token-search-backward
|
||||
bind \ed forward-kill-word
|
||||
bind -k ppage beginning-of-history
|
||||
bind -k npage end-of-history
|
||||
|
|
|
@ -7,7 +7,7 @@ function math --description "Perform math calculations in bc"
|
|||
return 0
|
||||
end
|
||||
|
||||
set -l out (echo $argv|bc)
|
||||
set -l out (echo $argv|env BC_LINE_LENGTH=0 bc)
|
||||
echo $out
|
||||
switch $out
|
||||
case 0
|
||||
|
|
461
share/tools/web_config/fishconfig.css
Normal file
461
share/tools/web_config/fishconfig.css
Normal file
|
@ -0,0 +1,461 @@
|
|||
body {
|
||||
background-color: #292939;
|
||||
font-family: Courier, "Courier New", monospace;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#ancestor {
|
||||
width: 800px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
#parent {
|
||||
width: 100%;
|
||||
min-height: 480px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
#tab_parent {
|
||||
display: table;
|
||||
width: 100%;
|
||||
height: 50px;;
|
||||
}
|
||||
|
||||
.tab {
|
||||
display: table-cell;
|
||||
border: 1px solid #111;
|
||||
border-right: none;
|
||||
padding-bottom: 15px;
|
||||
padding-top: 15px;
|
||||
font-size: 17pt;
|
||||
text-align: center;
|
||||
width: 15%;
|
||||
background-color: #292929;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#tab_parent :first-child {
|
||||
border-top-left-radius: 8px;
|
||||
}
|
||||
|
||||
#tab_parent :last-child {
|
||||
border-right: 1px solid #111;
|
||||
border-top-right-radius: 8px;
|
||||
}
|
||||
|
||||
.tab_first {
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
|
||||
.tab_last {
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
|
||||
.selected_tab {
|
||||
background-color: black;
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
#tab_contents {
|
||||
padding-top: 35px;
|
||||
width: 100%;
|
||||
background-color: black;
|
||||
border-bottom-left-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
clear: both;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.master_detail_table {
|
||||
display: table;
|
||||
margin-top: 10px;
|
||||
margin-left: 12px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.master {
|
||||
display: table-cell;
|
||||
text-align: right;
|
||||
min-width: 200px;
|
||||
font-size: 16pt;
|
||||
padding-bottom: 20px;
|
||||
padding-top: 35px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.detail {
|
||||
display: table-cell;
|
||||
border: 1px solid #555;
|
||||
background-color: #181818;
|
||||
padding-top: 30px;
|
||||
padding-bottom: 20px;
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
border-radius: 5;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.detail_function {
|
||||
white-space: pre-wrap;
|
||||
width: 100%;
|
||||
font-size: 11pt;
|
||||
color: #BBB;
|
||||
}
|
||||
|
||||
.master_element {
|
||||
padding-top: 6px;
|
||||
padding-bottom: 11px;
|
||||
padding-left: 5px;
|
||||
padding-right: 22px;
|
||||
font-size: 12pt;
|
||||
/* Make our border overlap the detail, even if we're unselected (so it doesn't jump when selected) */
|
||||
position: relative;
|
||||
left: 1px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 0px;
|
||||
}
|
||||
|
||||
.selected_master_elem {
|
||||
border: 1px solid #555;
|
||||
border-right: none;
|
||||
background-color: #181818;
|
||||
|
||||
border-top-left-radius: 5;
|
||||
border-bottom-left-radius: 5;
|
||||
|
||||
/* Pad one less than .master_element, to accomodate our border. */
|
||||
padding-top: 5px;
|
||||
padding-bottom: 10px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.master_element_text {
|
||||
text-decoration: none;
|
||||
padding-bottom: 1px;
|
||||
border-bottom-style: inherit;
|
||||
border-bottom-color: inherit;
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.master_element_description {
|
||||
text-decoration: none;
|
||||
padding-top: 15px;
|
||||
font-size: 10pt;
|
||||
border-bottom-style: inherit;
|
||||
border-bottom-color: inherit;
|
||||
border-bottom-width: 1px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.selected_master_elem > .master_element_description {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* We have a newline between the label and description; hide it initially, but show it when it's selected */
|
||||
.master_element > br { display: none; }
|
||||
.selected_master_elem > br { display: inherit; }
|
||||
|
||||
/* Set this class to suppress the border bottom on master_element_texts with visible descriptions */
|
||||
.master_element_no_border { border-bottom-width: 0 }
|
||||
|
||||
.colorpicker_term256 {
|
||||
border: solid #444 1px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.colorpicker_modifiers {
|
||||
margin-top: 10px;
|
||||
display:inline-block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
color: #AAA;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
.colorpicker_modifier_cell {
|
||||
cursor: pointer;
|
||||
display:inline-block;
|
||||
text-align: center;
|
||||
border: groove #333 2px;
|
||||
padding: 5px;
|
||||
margin-top: 5px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.modifier_cell_selected {
|
||||
color: #CCC;
|
||||
border-color: #AAA;
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
.data_table {
|
||||
table-layout:fixed;
|
||||
color: #CCC;
|
||||
width: 100%;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.data_table_row {
|
||||
}
|
||||
|
||||
.data_table_cell {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
vertical-align: top;
|
||||
overflow:hidden;
|
||||
border-bottom: #444 dotted 1px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.history_text {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
vertical-align: top;
|
||||
overflow:hidden;
|
||||
border-bottom: #444 dotted 1px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.history_delete {
|
||||
width: 20px;
|
||||
border-bottom: #444 dotted 1px;
|
||||
}
|
||||
|
||||
/* The CSS we apply when a table row is filtered */
|
||||
.data_table_row_filtered {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.no_overflow {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.colorpicker_target {
|
||||
margin: 0 0 -50px 0;
|
||||
position: relative;
|
||||
bottom: 47px;
|
||||
float: left; /* for some reason this makes the cells that it overlaps (before adjusting its bottom) clickable in Safari */
|
||||
}
|
||||
|
||||
.colorpicker_target_tab {
|
||||
cursor: pointer;
|
||||
color: #555;
|
||||
border: solid 1px #555;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
display: inline-block;
|
||||
background-color: black;
|
||||
margin-right: -2px;
|
||||
min-width: 110px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.colorpicker_target_selected {
|
||||
background-color: #181818; /* same as #detail */
|
||||
color: white;
|
||||
}
|
||||
|
||||
.colorpicker_term256_row {
|
||||
}
|
||||
|
||||
.colorpicker_term256_cell {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border: solid black 1px;
|
||||
}
|
||||
|
||||
.colorpicker_term256_selection_indicator {
|
||||
width: 19px;
|
||||
height: 16px;
|
||||
margin: -2px;
|
||||
border: solid white 3px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.colorpicker_cell_selected {
|
||||
border: dashed white 3px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.colorpicker_text_sample, .colorpicker_text_sample_tight {
|
||||
font-size: 12pt;
|
||||
padding: 25px;
|
||||
margin: 5px 20px 25px 20px; /* top right bottom left */
|
||||
cursor: pointer;
|
||||
line-height: 1.8em;
|
||||
border: solid #777 1px;
|
||||
position: relative; /* so that our absolutely positioned elements work */
|
||||
}
|
||||
|
||||
.colorpicker_text_sample_tight {
|
||||
font-size: 10pt;
|
||||
line-height: 1.2em;
|
||||
margin: 0px 6px;
|
||||
max-width: 220px;
|
||||
padding: 5px;
|
||||
white-space:nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
.color_picker_background_cells {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
.color_picker_background_cells div {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-style: solid;
|
||||
border-color: #777;
|
||||
border-width: 0 0 1px 1px; /* top right bottom left */
|
||||
float: left;
|
||||
}
|
||||
|
||||
.color_scheme_choice_label {
|
||||
margin-left: 10px;
|
||||
margin-bottom: 3px;
|
||||
cursor: pointer;
|
||||
font-size: 12pt;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.color_scheme_choices_list {
|
||||
overflow-y: hidden; /* makes our height account for floats */
|
||||
padding: 0 10px 15px 10px; /* top right bottom left */
|
||||
}
|
||||
|
||||
.color_scheme_choice_container {
|
||||
float: left;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.fake_cursor {
|
||||
background-color: #999;
|
||||
}
|
||||
|
||||
.error_msg {
|
||||
color: red;
|
||||
font-size: 12pt;
|
||||
margin-left: 24pt;
|
||||
margin-top: 5pt;
|
||||
margin-bottom: 5pt;
|
||||
}
|
||||
|
||||
img.delete_icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#table_filter_container {
|
||||
/* top right bottom left*/
|
||||
padding: 0 10 10 30;
|
||||
text-align: right;
|
||||
position: relative;
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
.filter_text_box {
|
||||
width: 250px;
|
||||
padding: 5 10 5 10;
|
||||
background-color: #888;
|
||||
border: #222 solid 3px;
|
||||
border-radius: 15px;
|
||||
font-size: 12pt;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.text_box_transient {
|
||||
color: #C8C8C8;
|
||||
}
|
||||
|
||||
.prompt_demo {
|
||||
font-size: 12pt;
|
||||
padding: 10px;
|
||||
margin: 5px 20px 25px; /* top right bottom left */
|
||||
cursor: pointer;
|
||||
line-height: 1.8em;
|
||||
border: solid #777 1px;
|
||||
position: relative; /* so that our absolutely positioned elements work */
|
||||
}
|
||||
|
||||
.save_button, .prompt_save_button {
|
||||
border-radius: 5px;
|
||||
border: solid rgba(71,71,71,0.5) 1px;
|
||||
padding: 5px 8px;
|
||||
font-size: 10pt;
|
||||
display: inline-block;
|
||||
margin-top: 12px;
|
||||
background-color: rgba(128,128,128,0.2);
|
||||
color: #FFF;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.prompt_save_button {
|
||||
background-color: #333;
|
||||
border: solid #525252 1px;
|
||||
color: #ffffff;
|
||||
margin: 2px 20px 25px; /* top right bottom left */
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
.prompt_demo_choice_label {
|
||||
margin: 25px 20px 5px;
|
||||
font-size: 12pt;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.prompt_demo_text {
|
||||
white-space: pre;
|
||||
line-height: 170%;
|
||||
padding: 4px 12px;
|
||||
font-size: 14pt;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
vertical-align: middle;
|
||||
display: table-cell;
|
||||
height: 72px; /* this is really the min height */
|
||||
}
|
||||
|
||||
.prompt_function {
|
||||
display: block;
|
||||
border: 1px solid #555;
|
||||
background-color: #181818;
|
||||
margin: 5px 20px 5px;
|
||||
border-radius: 5;
|
||||
}
|
||||
|
||||
.prompt_function_text {
|
||||
white-space: pre-wrap;
|
||||
padding: 15px 3px;
|
||||
width: 100%;
|
||||
height: 25%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.external_link_img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: text-top;
|
||||
margin-left: 10px;
|
||||
};
|
File diff suppressed because it is too large
Load diff
4
share/tools/web_config/jquery.js
vendored
4
share/tools/web_config/jquery.js
vendored
File diff suppressed because one or more lines are too long
14847
share/tools/web_config/js/angular.js
vendored
Normal file
14847
share/tools/web_config/js/angular.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
69
share/tools/web_config/js/app.js
Normal file
69
share/tools/web_config/js/app.js
Normal file
|
@ -0,0 +1,69 @@
|
|||
fishconfig = angular.module("fishconfig", ["filters", "controllers"]);
|
||||
|
||||
fishconfig.config(
|
||||
["$routeProvider", function($routeProvider) {
|
||||
$routeProvider
|
||||
.when("/colors", {
|
||||
controller: "colorsController",
|
||||
templateUrl: "partials/colors.html"
|
||||
})
|
||||
.when("/prompt", {
|
||||
controller: "promptController",
|
||||
templateUrl: "partials/prompt.html"
|
||||
})
|
||||
.when("/functions", {
|
||||
controller: "functionsController",
|
||||
templateUrl: "partials/functions.html"
|
||||
})
|
||||
.when("/variables", {
|
||||
controller: "variablesController",
|
||||
templateUrl: "partials/variables.html"
|
||||
})
|
||||
.when("/history", {
|
||||
controller: "historyController",
|
||||
templateUrl: "partials/history.html"
|
||||
})
|
||||
.when("/bindings", {
|
||||
controller: "bindingsController",
|
||||
templateUrl: "partials/bindings.html"
|
||||
})
|
||||
.otherwise({
|
||||
redirectTo: "/colors"
|
||||
})
|
||||
}]);
|
||||
|
||||
/* Inspired from http://blog.tomaka17.com/2012/12/random-tricks-when-using-angularjs/ */
|
||||
fishconfig.config(function($httpProvider, $compileProvider) {
|
||||
var global_error_element = null;
|
||||
|
||||
var showMessage = function(content) {
|
||||
global_error_element.text(content);
|
||||
};
|
||||
|
||||
$httpProvider.responseInterceptors.push(function($q) {
|
||||
return function(promise) {
|
||||
return promise.then(function(successResponse) {
|
||||
showMessage('');
|
||||
return successResponse;
|
||||
}, function(errorResponse) {
|
||||
switch (errorResponse.status) {
|
||||
case 0:
|
||||
showMessage("The request received an error. Perhaps the server has shut down.");
|
||||
break;
|
||||
case 500:
|
||||
showMessage('Server internal error: ' + errorResponse.data);
|
||||
break;
|
||||
default:
|
||||
showMessage('Error ' + errorResponse.status + ': ' + errorResponse.data);
|
||||
}
|
||||
return $q.reject(errorResponse);
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
$compileProvider.directive('errorMessage', function() {
|
||||
return {
|
||||
link: function(scope, element, attrs) { global_error_element = element; }
|
||||
};
|
||||
});
|
||||
});
|
624
share/tools/web_config/js/colorutils.js
Normal file
624
share/tools/web_config/js/colorutils.js
Normal file
|
@ -0,0 +1,624 @@
|
|||
/* TODO: Write an angularjs service to wrap these methods */
|
||||
|
||||
term_256_colors = [ //247
|
||||
"ffd7d7",
|
||||
"d7afaf",
|
||||
"af8787",
|
||||
"875f5f",
|
||||
"5f0000",
|
||||
"870000",
|
||||
"af0000",
|
||||
"d70000",
|
||||
"ff0000",
|
||||
"ff5f5f",
|
||||
"d75f5f",
|
||||
"d78787",
|
||||
"ff8787",
|
||||
"ffafaf",
|
||||
"ffaf87",
|
||||
"ffaf5f",
|
||||
"ffaf00",
|
||||
"ff875f",
|
||||
"ff8700",
|
||||
"ff5f00",
|
||||
"d75f00",
|
||||
"af5f5f",
|
||||
"af5f00",
|
||||
"d78700",
|
||||
"d7875f",
|
||||
"af875f",
|
||||
"af8700",
|
||||
"875f00",
|
||||
"d7af87",
|
||||
"ffd7af",
|
||||
"ffd787",
|
||||
"ffd75f",
|
||||
"d7af00",
|
||||
"d7af5f",
|
||||
"ffd700",
|
||||
"ffff5f",
|
||||
"ffff00",
|
||||
"ffff87",
|
||||
"ffffaf",
|
||||
"ffffd7",
|
||||
"d7ff00",
|
||||
"afd75f",
|
||||
"d7d700",
|
||||
"d7d787",
|
||||
"d7d7af",
|
||||
"afaf87",
|
||||
"87875f",
|
||||
"5f5f00",
|
||||
"878700",
|
||||
"afaf00",
|
||||
"afaf5f",
|
||||
"d7d75f",
|
||||
"d7ff5f",
|
||||
"d7ff87",
|
||||
"87ff00",
|
||||
"afff00",
|
||||
"afff5f",
|
||||
"afd700",
|
||||
"87d700",
|
||||
"87af00",
|
||||
"5f8700",
|
||||
"87af5f",
|
||||
"5faf00",
|
||||
"afd787",
|
||||
"d7ffd7",
|
||||
"d7ffaf",
|
||||
"afffaf",
|
||||
"afff87",
|
||||
"5fff00",
|
||||
"5fd700",
|
||||
"87d75f",
|
||||
"5fd75f",
|
||||
"87ff5f",
|
||||
"5fff5f",
|
||||
"87ff87",
|
||||
"afd7af",
|
||||
"87d787",
|
||||
"87d7af",
|
||||
"87af87",
|
||||
"5f875f",
|
||||
"5faf5f",
|
||||
"005f00",
|
||||
"008700",
|
||||
"00af00",
|
||||
"00d700",
|
||||
"00ff00",
|
||||
"00ff5f",
|
||||
"5fff87",
|
||||
"00ff87",
|
||||
"87ffaf",
|
||||
"afffd7",
|
||||
"5fd787",
|
||||
"00d75f",
|
||||
"5faf87",
|
||||
"00af5f",
|
||||
"5fffaf",
|
||||
"00ffaf",
|
||||
"5fd7af",
|
||||
"00d787",
|
||||
"00875f",
|
||||
"00af87",
|
||||
"00d7af",
|
||||
"5fffd7",
|
||||
"87ffd7",
|
||||
"00ffd7",
|
||||
"d7ffff",
|
||||
"afd7d7",
|
||||
"87afaf",
|
||||
"5f8787",
|
||||
"5fafaf",
|
||||
"87d7d7",
|
||||
"5fd7d7",
|
||||
"5fffff",
|
||||
"00ffff",
|
||||
"87ffff",
|
||||
"afffff",
|
||||
"00d7d7",
|
||||
"00d7ff",
|
||||
"5fd7ff",
|
||||
"5fafd7",
|
||||
"00afd7",
|
||||
"00afff",
|
||||
"0087af",
|
||||
"00afaf",
|
||||
"008787",
|
||||
"005f5f",
|
||||
"005f87",
|
||||
"0087d7",
|
||||
"0087ff",
|
||||
"5fafff",
|
||||
"87afff",
|
||||
"5f87d7",
|
||||
"5f87ff",
|
||||
"005fd7",
|
||||
"005fff",
|
||||
"005faf",
|
||||
"5f87af",
|
||||
"87afd7",
|
||||
"afd7ff",
|
||||
"87d7ff",
|
||||
"d7d7ff",
|
||||
"afafd7",
|
||||
"8787af",
|
||||
"afafff",
|
||||
"8787d7",
|
||||
"8787ff",
|
||||
"5f5fff",
|
||||
"5f5fd7",
|
||||
"5f5faf",
|
||||
"5f5f87",
|
||||
"00005f",
|
||||
"000087",
|
||||
"0000af",
|
||||
"0000d7",
|
||||
"0000ff",
|
||||
"5f00ff",
|
||||
"5f00d7",
|
||||
"5f00af",
|
||||
"5f0087",
|
||||
"8700af",
|
||||
"8700d7",
|
||||
"8700ff",
|
||||
"af00ff",
|
||||
"af00d7",
|
||||
"d700ff",
|
||||
"d75fff",
|
||||
"d787ff",
|
||||
"ffafd7",
|
||||
"ffafff",
|
||||
"ffd7ff",
|
||||
"d7afff",
|
||||
"d7afd7",
|
||||
"af87af",
|
||||
"af87d7",
|
||||
"af87ff",
|
||||
"875fd7",
|
||||
"875faf",
|
||||
"875fff",
|
||||
"af5fff",
|
||||
"af5fd7",
|
||||
"af5faf",
|
||||
"d75fd7",
|
||||
"d787d7",
|
||||
"ff87ff",
|
||||
"ff5fff",
|
||||
"ff5fd7",
|
||||
"ff00ff",
|
||||
"ff00af",
|
||||
"ff00d7",
|
||||
"d700af",
|
||||
"d700d7",
|
||||
"af00af",
|
||||
"870087",
|
||||
"5f005f",
|
||||
"87005f",
|
||||
"af005f",
|
||||
"af0087",
|
||||
"d70087",
|
||||
"d7005f",
|
||||
"ff0087",
|
||||
"ff005f",
|
||||
"ff5f87",
|
||||
"d75f87",
|
||||
"d75faf",
|
||||
"ff5faf",
|
||||
"ff87af",
|
||||
"ff87d7",
|
||||
"d787af",
|
||||
"af5f87",
|
||||
"875f87",
|
||||
"000000",
|
||||
"080808",
|
||||
"121212",
|
||||
"1c1c1c",
|
||||
"262626",
|
||||
"303030",
|
||||
"3a3a3a",
|
||||
"444444",
|
||||
"4e4e4e",
|
||||
"585858",
|
||||
"5f5f5f",
|
||||
"626262",
|
||||
"6c6c6c",
|
||||
"767676",
|
||||
"808080",
|
||||
"878787",
|
||||
"8a8a8a",
|
||||
"949494",
|
||||
"9e9e9e",
|
||||
"a8a8a8",
|
||||
"afafaf",
|
||||
"b2b2b2",
|
||||
"bcbcbc",
|
||||
"c6c6c6",
|
||||
"d0d0d0",
|
||||
"d7d7d7",
|
||||
"dadada",
|
||||
"e4e4e4",
|
||||
"eeeeee",
|
||||
"ffffff",
|
||||
]
|
||||
|
||||
/* Returns array of values from a dictionary (or any object) */
|
||||
function dict_values(dict) {
|
||||
var result = [];
|
||||
for (var i in dict) result.push(dict[i]);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Return the array of colors as an array of N arrays of length items_per_row */
|
||||
function get_colors_as_nested_array(colors, items_per_row) {
|
||||
var result = new Array();
|
||||
for (var idx = 0; idx < colors.length; idx += items_per_row) {
|
||||
var row = new Array();
|
||||
for (var subidx = 0; subidx < items_per_row && idx + subidx < colors.length; subidx++) {
|
||||
row.push(colors[idx + subidx]);
|
||||
}
|
||||
result.push(row);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Given an RGB color as a hex string, like FF0033, convert to HSL, apply the function to adjust its lightness, then return the new color as an RGB string */
|
||||
function adjust_lightness(color_str, func) {
|
||||
/* Hack to handle for example F00 */
|
||||
if (color_str.length == 3) {
|
||||
color_str = color_str[0] + color_str[0] + color_str[1] + color_str[1] + color_str[2] + color_str[2]
|
||||
}
|
||||
|
||||
/* More hacks */
|
||||
if (color_str == 'black') color_str = '000000';
|
||||
if (color_str == 'white') color_str = 'FFFFFF';
|
||||
|
||||
|
||||
rgb = parseInt(color_str, 16)
|
||||
r = (rgb >> 16) & 0xFF
|
||||
g = (rgb >> 8) & 0xFF
|
||||
b = (rgb >> 0) & 0xFF
|
||||
|
||||
hsl = rgb_to_hsl(r, g, b)
|
||||
new_lightness = func(hsl[2])
|
||||
function to_int_str(val) {
|
||||
str = Math.round(val).toString(16)
|
||||
while (str.length < 2)
|
||||
str = '0' + str
|
||||
return str
|
||||
}
|
||||
|
||||
new_rgb = hsl_to_rgb(hsl[0], hsl[1], new_lightness)
|
||||
return to_int_str(new_rgb[0]) + to_int_str(new_rgb[1]) + to_int_str(new_rgb[2])
|
||||
}
|
||||
|
||||
/* Given a color, compute a "border color" for it that can show it selected */
|
||||
function border_color_for_color(color_str) {
|
||||
return adjust_lightness(color_str, function(lightness){
|
||||
var adjust = .5
|
||||
var new_lightness = lightness + adjust
|
||||
if (new_lightness > 1.0 || new_lightness < 0.0) {
|
||||
new_lightness -= 2 * adjust
|
||||
}
|
||||
return new_lightness
|
||||
})
|
||||
}
|
||||
|
||||
/* Use this function to make a color that contrasts well with the given color */
|
||||
function text_color_for_color(color_str) {
|
||||
var adjust = .5
|
||||
function compute_constrast(lightness){
|
||||
var new_lightness = lightness + adjust
|
||||
if (new_lightness > 1.0 || new_lightness < 0.0) {
|
||||
new_lightness -= 2 * adjust
|
||||
}
|
||||
return new_lightness
|
||||
}
|
||||
return adjust_lightness(color_str, compute_constrast);
|
||||
}
|
||||
|
||||
function rgb_to_hsl(r, g, b){
|
||||
r /= 255, g /= 255, b /= 255;
|
||||
var max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||||
var h, s, l = (max + min) / 2;
|
||||
|
||||
if(max == min){
|
||||
h = s = 0; // achromatic
|
||||
}else{
|
||||
var d = max - min;
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
switch(max){
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
h /= 6;
|
||||
}
|
||||
|
||||
return [h, s, l];
|
||||
}
|
||||
|
||||
function hsl_to_rgb(h, s, l){
|
||||
var r, g, b;
|
||||
|
||||
if(s == 0){
|
||||
r = g = b = l; // achromatic
|
||||
}else{
|
||||
function hue2rgb(p, q, t){
|
||||
if(t < 0) t += 1;
|
||||
if(t > 1) t -= 1;
|
||||
if(t < 1/6) return p + (q - p) * 6 * t;
|
||||
if(t < 1/2) return q;
|
||||
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
||||
return p;
|
||||
}
|
||||
|
||||
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
var p = 2 * l - q;
|
||||
r = hue2rgb(p, q, h + 1.0/3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1.0/3);
|
||||
}
|
||||
|
||||
return [r * 255, g * 255, b * 255]
|
||||
}
|
||||
|
||||
|
||||
/* Given a color, compute the master text color for it, by giving it a minimum brightness */
|
||||
function master_color_for_color(color_str) {
|
||||
return adjust_lightness(color_str, function(lightness){
|
||||
if (lightness < .33) {
|
||||
lightness = .33
|
||||
}
|
||||
return lightness
|
||||
})
|
||||
}
|
||||
|
||||
/* Given a color name, like 'normal' or 'red' or 'FF00F0', return an RGB color string (or empty string) */
|
||||
function interpret_color(str) {
|
||||
str = str.toLowerCase()
|
||||
if (str == 'black') return '000000'
|
||||
if (str == 'red') return 'FF0000'
|
||||
if (str == 'green') return '00FF00'
|
||||
if (str == 'brown') return '725000'
|
||||
if (str == 'yellow') return 'FFFF00'
|
||||
if (str == 'blue') return '0000FF'
|
||||
if (str == 'magenta') return 'FF00FF'
|
||||
if (str == 'purple') return 'FF00FF'
|
||||
if (str == 'cyan') return '00FFFF'
|
||||
if (str == 'white') return 'FFFFFF'
|
||||
if (str == 'normal') return ''
|
||||
return str
|
||||
}
|
||||
|
||||
var color_scheme_fish_default = {
|
||||
"name": "fish default",
|
||||
"colors": [],
|
||||
'preferred_background': 'black',
|
||||
|
||||
'autosuggestion': '555',
|
||||
'command': '005fd7',
|
||||
'param': '00afff',
|
||||
'redirection': '00afff',
|
||||
'comment': '990000',
|
||||
'error': 'ff0000',
|
||||
'escape': '00a6b2',
|
||||
'operator': '00a6b2',
|
||||
'quote': '999900',
|
||||
'end': '009900'
|
||||
};
|
||||
|
||||
|
||||
var TomorrowTheme = {
|
||||
tomorrow_night: {'Background': '1d1f21', 'Current Line': '282a2e', 'Selection': '373b41', 'Foreground': 'c5c8c6', 'Comment': '969896', 'Red': 'cc6666', 'Orange': 'de935f', 'Yellow': 'f0c674', 'Green': 'b5bd68', 'Aqua': '8abeb7', 'Blue': '81a2be', 'Purple': 'b294bb'
|
||||
},
|
||||
|
||||
tomorrow: {'Background': 'ffffff', 'Current Line': 'efefef', 'Selection': 'd6d6d6', 'Foreground': '4d4d4c', 'Comment': '8e908c', 'Red': 'c82829', 'Orange': 'f5871f', 'Yellow': 'eab700', 'Green': '718c00', 'Aqua': '3e999f', 'Blue': '4271ae', 'Purple': '8959a8'
|
||||
},
|
||||
|
||||
tomorrow_night_bright: {'Background': '000000', 'Current Line': '2a2a2a', 'Selection': '424242', 'Foreground': 'eaeaea', 'Comment': '969896', 'Red': 'd54e53', 'Orange': 'e78c45', 'Yellow': 'e7c547', 'Green': 'b9ca4a', 'Aqua': '70c0b1', 'Blue': '7aa6da', 'Purple': 'c397d8'},
|
||||
|
||||
apply: function(theme, receiver){
|
||||
receiver['autosuggestion'] = theme['Comment']
|
||||
receiver['command'] = theme['Purple']
|
||||
receiver['comment'] = theme['Yellow'] /* we want to use comment for autosuggestions */
|
||||
receiver['end'] = theme['Purple']
|
||||
receiver['error'] = theme['Red']
|
||||
receiver['param'] = theme['Blue']
|
||||
receiver['quote'] = theme['Green']
|
||||
receiver['redirection'] = theme['Aqua']
|
||||
|
||||
receiver['colors'] = []
|
||||
for (var key in theme) receiver['colors'].push(theme[key])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var solarized = {
|
||||
base03: '002b36', base02: '073642', base01: '586e75', base00: '657b83', base0: '839496', base1: '93a1a1', base2: 'eee8d5', base3: 'fdf6e3', yellow: 'b58900', orange: 'cb4b16', red: 'dc322f', magenta: 'd33682', violet: '6c71c4', blue: '268bd2', cyan: '2aa198', green: '859900'
|
||||
};
|
||||
|
||||
/* Sample color schemes */
|
||||
var color_scheme_solarized_light = {
|
||||
name: "Solarized Light",
|
||||
colors: dict_values(solarized),
|
||||
|
||||
preferred_background: '#' + solarized.base3,
|
||||
|
||||
autosuggestion: solarized.base1,
|
||||
command: solarized.base01,
|
||||
comment: solarized.base1,
|
||||
end: solarized.blue,
|
||||
error: solarized.red,
|
||||
param: solarized.base00,
|
||||
quote: solarized.base0,
|
||||
redirection: solarized.violet,
|
||||
|
||||
url: 'http://ethanschoonover.com/solarized'
|
||||
};
|
||||
|
||||
var color_scheme_solarized_dark = {
|
||||
name: "Solarized Dark",
|
||||
colors: dict_values(solarized),
|
||||
preferred_background: '#' + solarized.base03,
|
||||
|
||||
autosuggestion: solarized.base01,
|
||||
command: solarized.base1,
|
||||
comment: solarized.base01,
|
||||
end: solarized.blue,
|
||||
error: solarized.red,
|
||||
param: solarized.base0,
|
||||
quote: solarized.base00,
|
||||
redirection: solarized.violet,
|
||||
|
||||
url: 'http://ethanschoonover.com/solarized'
|
||||
};
|
||||
|
||||
var color_scheme_tomorrow = {
|
||||
name: 'Tomorrow',
|
||||
preferred_background: 'white',
|
||||
url: 'https://github.com/chriskempson/tomorrow-theme'
|
||||
}
|
||||
TomorrowTheme.apply(TomorrowTheme.tomorrow, color_scheme_tomorrow)
|
||||
|
||||
var color_scheme_tomorrow_night = {
|
||||
name: 'Tomorrow Night',
|
||||
preferred_background: '#232323',
|
||||
url: 'https://github.com/chriskempson/tomorrow-theme'
|
||||
}
|
||||
TomorrowTheme.apply(TomorrowTheme.tomorrow_night, color_scheme_tomorrow_night)
|
||||
|
||||
var color_scheme_tomorrow_night_bright = {
|
||||
'name': 'Tomorrow Night Bright',
|
||||
'preferred_background': 'black',
|
||||
'url': 'https://github.com/chriskempson/tomorrow-theme',
|
||||
|
||||
}
|
||||
TomorrowTheme.apply(TomorrowTheme.tomorrow_night_bright, color_scheme_tomorrow_night_bright)
|
||||
|
||||
function construct_scheme_analogous(label, background, color_list) {
|
||||
return {
|
||||
name: label,
|
||||
preferred_background: background,
|
||||
colors: color_list,
|
||||
|
||||
command: color_list[0],
|
||||
quote: color_list[6],
|
||||
param: color_list[5],
|
||||
autosuggestion: color_list[4],
|
||||
|
||||
error: color_list[9],
|
||||
comment: color_list[12],
|
||||
|
||||
end: color_list[10],
|
||||
redirection: color_list[11]
|
||||
};
|
||||
}
|
||||
|
||||
function construct_scheme_triad(label, background, color_list) {
|
||||
return {
|
||||
name: label,
|
||||
preferred_background: background,
|
||||
colors: color_list,
|
||||
|
||||
command: color_list[0],
|
||||
quote: color_list[2],
|
||||
param: color_list[1],
|
||||
autosuggestion: color_list[3],
|
||||
redirection: color_list[4],
|
||||
|
||||
error: color_list[8],
|
||||
comment: color_list[10],
|
||||
|
||||
end: color_list[7],
|
||||
};
|
||||
}
|
||||
|
||||
function construct_scheme_complementary(label, background, color_list) {
|
||||
return {
|
||||
name: label,
|
||||
preferred_background: background,
|
||||
colors: color_list,
|
||||
|
||||
command: color_list[0],
|
||||
quote: color_list[4],
|
||||
param: color_list[3],
|
||||
autosuggestion: color_list[2],
|
||||
redirection: color_list[6],
|
||||
|
||||
error: color_list[5],
|
||||
comment: color_list[8],
|
||||
|
||||
end: color_list[9],
|
||||
};
|
||||
}
|
||||
|
||||
function construct_color_scheme_mono(label, background, from_end) {
|
||||
var mono_colors = ['000000', '121212', '1c1c1c', '262626', '303030', '3a3a3a', '444444', '4e4e4e', '585858', '5f5f5f', '626262', '6c6c6c', '767676', '808080', '878787', '8a8a8a', '949494', '9e9e9e', 'a8a8a8', 'afafaf', 'b2b2b2', 'bcbcbc', 'c6c6c6', 'd0d0d0', 'd7d7d7', 'dadada', 'e4e4e4', 'eeeeee', 'ffffff'];
|
||||
|
||||
if (from_end) mono_colors.reverse();
|
||||
|
||||
return {
|
||||
name: label,
|
||||
preferred_background: background,
|
||||
colors: mono_colors,
|
||||
|
||||
autosuggestion: '777777',
|
||||
command: mono_colors[0],
|
||||
comment: mono_colors[7],
|
||||
end: mono_colors[12],
|
||||
error: mono_colors[20],
|
||||
param: mono_colors[4],
|
||||
quote: mono_colors[10],
|
||||
redirection: mono_colors[15]
|
||||
};
|
||||
}
|
||||
|
||||
var additional_color_schemes = [
|
||||
construct_scheme_analogous('Snow Day', 'white', ['164CC9', '325197', '072D83', '4C7AE4', '7596E4', '4319CC', '4C3499', '260885', '724EE5', '9177E5', '02BDBD', '248E8E', '007B7B', '39DEDE', '65DEDE']),
|
||||
|
||||
construct_scheme_analogous('Lava', '#232323', ['FF9400', 'BF8330', 'A66000', 'FFAE40', 'FFC473', 'FFC000', 'BF9C30', 'A67D00', 'FFD040', 'FFDD73', 'FF4C00', 'BF5B30', 'A63100', 'FF7940', 'FF9D73']),
|
||||
|
||||
construct_scheme_analogous('Seaweed', '#232323', ['00BF32', '248F40', '007C21', '38DF64', '64DF85', '04819E', '206676', '015367', '38B2CE', '60B9CE', '8EEB00', '7CB02C', '5C9900', 'ACF53D', 'C0F56E']),
|
||||
|
||||
construct_scheme_triad('Fairground', '#003', ['0772A1', '225E79', '024A68', '3BA3D0', '63AFD0', 'D9005B', 'A3295C', '8D003B', 'EC3B86', 'EC6AA1', 'FFE100', 'BFAE30', 'A69200', 'FFE840', 'FFEE73']),
|
||||
|
||||
construct_scheme_complementary('Bay Cruise', 'black', ['009999', '1D7373', '006363', '33CCCC', '5CCCCC', 'FF7400', 'BF7130', 'A64B00', 'FF9640', 'FFB273']),
|
||||
|
||||
{
|
||||
'name': 'Old School',
|
||||
'preferred_background': 'black',
|
||||
|
||||
colors: ['00FF00', '30BE30', '00A400', '44FF44', '7BFF7B', 'FF0000', 'BE3030', 'A40000', 'FF7B7B', '777777', 'CCCCCC'],
|
||||
|
||||
autosuggestion: '777777',
|
||||
command: '00FF00',
|
||||
comment: '30BE30',
|
||||
end: 'FF7B7B',
|
||||
error: 'A40000',
|
||||
param: '30BE30',
|
||||
quote: '44FF44',
|
||||
redirection: '7BFF7B'
|
||||
},
|
||||
|
||||
{
|
||||
'name': 'Just a Touch',
|
||||
'preferred_background': 'black',
|
||||
|
||||
colors: ['B0B0B0', '969696', '789276', 'F4F4F4', 'A0A0F0', '666A80', 'F0F0F0', 'D7D7D7', 'B7B7B7', 'FFA779', 'FAFAFA'],
|
||||
|
||||
autosuggestion: '9C9C9C',
|
||||
command: 'F4F4F4',
|
||||
comment: 'B0B0B0',
|
||||
end: '969696',
|
||||
error: 'FFA779',
|
||||
param: 'A0A0F0',
|
||||
quote: '666A80',
|
||||
redirection: 'FAFAFA'
|
||||
},
|
||||
|
||||
construct_color_scheme_mono('Mono Lace', 'white', false),
|
||||
construct_color_scheme_mono('Mono Smoke', 'black', true)
|
||||
];
|
||||
|
232
share/tools/web_config/js/controllers.js
Normal file
232
share/tools/web_config/js/controllers.js
Normal file
|
@ -0,0 +1,232 @@
|
|||
controllers = angular.module("controllers", []);
|
||||
|
||||
controllers.controller("main", function($scope, $location) {
|
||||
$scope.currentTab = "colors";
|
||||
|
||||
$scope.changeView = function(view) {
|
||||
$location.path(view);
|
||||
$scope.currentTab = view;
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
controllers.controller("colorsController", function($scope, $http) {
|
||||
$scope.changeSelectedColorScheme= function(newScheme) {
|
||||
$scope.selectedColorScheme = newScheme;
|
||||
if ($scope.selectedColorScheme.preferred_background) {
|
||||
$scope.terminalBackgroundColor = $scope.selectedColorScheme.preferred_background;
|
||||
}
|
||||
$scope.selectedColorSetting = 'command';
|
||||
$scope.colorArraysArray = $scope.getColorArraysArray();
|
||||
//TODO: Save button should be shown only when colors are changed
|
||||
$scope.showSaveButton = true;
|
||||
}
|
||||
|
||||
$scope.changeTerminalBackgroundColor = function(color) {
|
||||
$scope.terminalBackgroundColor = color;
|
||||
}
|
||||
|
||||
$scope.text_color_for_color = function(color) {
|
||||
return text_color_for_color(color);
|
||||
}
|
||||
|
||||
$scope.getColorArraysArray = function() {
|
||||
var result = null;
|
||||
if ( $scope.selectedColorScheme.colors && $scope.selectedColorScheme.colors.length > 0)
|
||||
result = get_colors_as_nested_array($scope.selectedColorScheme.colors, 32);
|
||||
else
|
||||
result = get_colors_as_nested_array(term_256_colors, 32);
|
||||
return result;
|
||||
}
|
||||
|
||||
$scope.selectColorSetting = function(name) {
|
||||
$scope.selectedColorSetting = name;
|
||||
}
|
||||
|
||||
$scope.changeSelectedTextColor = function(color) {
|
||||
$scope.selectedColorScheme[$scope.selectedColorSetting] = color;
|
||||
}
|
||||
|
||||
$scope.sampleTerminalBackgroundColors = ['white', '#' + solarized.base3, '#300', '#003', '#' + solarized.base03, '#232323', 'black'];
|
||||
|
||||
/* Array of FishColorSchemes */
|
||||
$scope.colorSchemes = [color_scheme_fish_default, color_scheme_solarized_light, color_scheme_solarized_dark, color_scheme_tomorrow, color_scheme_tomorrow_night, color_scheme_tomorrow_night_bright];
|
||||
for (var i=0; i < additional_color_schemes.length; i++)
|
||||
$scope.colorSchemes.push(additional_color_schemes[i])
|
||||
|
||||
|
||||
$scope.getCurrentTheme = function() {
|
||||
$http.get("/colors/").success(function(data, status, headers, config) {
|
||||
var currentScheme = { "name": "Current", "colors":[], "preferred_background": "" };
|
||||
for (var i in data) {
|
||||
currentScheme[data[i].name] = data[i].color;
|
||||
}
|
||||
$scope.colorSchemes.splice(0, 0, currentScheme);
|
||||
$scope.changeSelectedColorScheme(currentScheme);
|
||||
})};
|
||||
|
||||
$scope.setTheme = function() {
|
||||
var settingNames = ["autosuggestion", "command", "param", "redirection", "comment", "error", "quote", "end"];
|
||||
for (name in settingNames) {
|
||||
var postData = "what=" + settingNames[name] + "&color=" + $scope.selectedColorScheme[settingNames[name]] + "&background_color=&bold=&underline=";
|
||||
$http.post("/set_color/", postData, { headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(data, status, headers, config) {
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getCurrentTheme();
|
||||
});
|
||||
|
||||
controllers.controller("promptController", function($scope, $http) {
|
||||
$scope.selectedPrompt = null;
|
||||
|
||||
$scope.fetchSamplePrompts= function() {
|
||||
$http.get("/sample_prompts/").success(function(data, status, headers, config) {
|
||||
$scope.samplePrompts = data;
|
||||
$scope.samplePromptsArrayArray = get_colors_as_nested_array($scope.samplePrompts, 1);
|
||||
|
||||
if ($scope.selectedPrompt == null) {
|
||||
$scope.selectPrompt($scope.samplePrompts[0]);
|
||||
}
|
||||
})};
|
||||
|
||||
$scope.selectPrompt = function(promptt) {
|
||||
$scope.selectedPrompt= promptt;
|
||||
}
|
||||
|
||||
$scope.setNewPrompt = function(selectedPrompt) {
|
||||
$http.post("/set_prompt/","what=" + encodeURIComponent(selectedPrompt.function), { headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(data, status, headers, config){
|
||||
|
||||
// Update attributes of current prompt and select it
|
||||
$scope.samplePrompts[0].demo = selectedPrompt.demo;
|
||||
$scope.samplePrompts[0].function = selectedPrompt.function;
|
||||
$scope.samplePrompts[0].font_size = selectedPrompt.font_size;
|
||||
$scope.selectedPrompt = $scope.samplePrompts[0];
|
||||
})};
|
||||
|
||||
$scope.fetchSamplePrompts();
|
||||
});
|
||||
|
||||
controllers.controller("functionsController", function($scope, $http) {
|
||||
$scope.selectedFunction = null;
|
||||
$scope.functionDefinition = "";
|
||||
|
||||
$scope.selectFunction = function(fun) {
|
||||
$scope.selectedFunction = fun;
|
||||
$scope.fetchFunctionDefinition($scope.selectedFunction);
|
||||
}
|
||||
|
||||
$scope.fetchFunctions= function() {
|
||||
$http.get("/functions/").success(function(data, status, headers, config) {
|
||||
$scope.functions = data;
|
||||
$scope.selectFunction($scope.functions[0]);
|
||||
})};
|
||||
|
||||
$scope.cleanupFishFunction = function (contents) {
|
||||
/* Replace leading tabs and groups of four spaces at the beginning of a line with two spaces. */
|
||||
lines = contents.split('\n')
|
||||
rx = /^[\t ]+/
|
||||
for (var i=0; i < lines.length; i++) {
|
||||
line = lines[i]
|
||||
/* Get leading tabs and spaces */
|
||||
whitespace_arr = rx.exec(line)
|
||||
if (whitespace_arr) {
|
||||
/* Replace four spaces with two spaces, and tabs with two spaces */
|
||||
var whitespace = whitespace_arr[0]
|
||||
new_whitespace = whitespace.replace(/( )|(\t)/g, ' ')
|
||||
lines[i] = new_whitespace + line.slice(whitespace.length)
|
||||
}
|
||||
}
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
$scope.fetchFunctionDefinition = function(name) {
|
||||
$http.post("/get_function/","what=" + name, { headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(data, status, headers, config) {
|
||||
$scope.functionDefinition = $scope.cleanupFishFunction(data[0]);
|
||||
})};
|
||||
|
||||
$scope.fetchFunctions();
|
||||
});
|
||||
|
||||
controllers.controller("variablesController", function($scope, $http) {
|
||||
$scope.query = null;
|
||||
|
||||
$scope.fetchVariables= function() {
|
||||
$http.get("/variables/").success(function(data, status, headers, config) {
|
||||
$scope.variables = data;
|
||||
})};
|
||||
|
||||
$scope.fetchVariables();
|
||||
});
|
||||
|
||||
controllers.controller("historyController", function($scope, $http, $timeout) {
|
||||
$scope.historyItems = [];
|
||||
$scope.historySize = 0;
|
||||
// Stores items which are yet to be added in history items
|
||||
$scope.remainingItems = [];
|
||||
$scope.selectedItems = [];
|
||||
|
||||
// Populate history items in parts
|
||||
$scope.loadHistory = function() {
|
||||
if ($scope.remainingItems.length <= 0) {
|
||||
$scope.loadingText = "";
|
||||
return;
|
||||
}
|
||||
|
||||
var toLoad = $scope.remainingItems.splice(0, 100);
|
||||
for (i in toLoad) {
|
||||
$scope.historyItems.push(toLoad[i]);
|
||||
}
|
||||
|
||||
$scope.loadingText = "Loading " + $scope.historyItems.length + "/" + $scope.historySize;
|
||||
$timeout($scope.loadHistory, 100);
|
||||
}
|
||||
|
||||
$scope.selectItem = function(item) {
|
||||
var index = $scope.selectedItems.indexOf(item);
|
||||
if ( index >= 0) {
|
||||
$scope.selectedItems.splice(index,1);
|
||||
}
|
||||
else {
|
||||
$scope.selectedItems.push(item);
|
||||
}
|
||||
}
|
||||
// Get history from server
|
||||
$scope.fetchHistory = function() {
|
||||
$http.get("/history/").success(function(data, status, headers, config) {
|
||||
$scope.historySize = data.length;
|
||||
$scope.remainingItems = data;
|
||||
|
||||
/* Call this function 10 times/second */
|
||||
$timeout($scope.loadHistory, 100);
|
||||
})};
|
||||
|
||||
$scope.deleteHistoryItem = function(item) {
|
||||
index = $scope.historyItems.indexOf(item);
|
||||
$http.post("/delete_history_item/","what=" + encodeURIComponent(item), { headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(data, status, headers, config) {
|
||||
$scope.historyItems.splice(index, 1);
|
||||
})};
|
||||
|
||||
var queryInputTimeout = null;
|
||||
$scope.$watch("queryInput", function() {
|
||||
if (queryInputTimeout){
|
||||
$timeout.cancel(queryInputTimeout);
|
||||
}
|
||||
|
||||
queryInputTimeout = $timeout(function() {
|
||||
$scope.query = $scope.queryInput;
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
$scope.fetchHistory();
|
||||
});
|
||||
|
||||
controllers.controller("bindingsController", function($scope, $http) {
|
||||
$scope.bindings = [];
|
||||
$scope.fetchBindings = function() {
|
||||
$http.get("/bindings/").success(function(data, status, headers, config) {
|
||||
$scope.bindings = data;
|
||||
})};
|
||||
|
||||
$scope.fetchBindings();
|
||||
});
|
35
share/tools/web_config/js/filters.js
Normal file
35
share/tools/web_config/js/filters.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
filters = angular.module("filters", []);
|
||||
|
||||
filters.filter("filterVariable", function() {
|
||||
return function(variables, query) {
|
||||
var result = []
|
||||
if (variables == undefined) return result;
|
||||
if (query == null) { return variables };
|
||||
|
||||
for(i=0; i<variables.length; ++i) {
|
||||
variable = variables[i];
|
||||
if (variable.name.indexOf(query) != -1 || variable.value.indexOf(query) != -1) {
|
||||
result.push(variable);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
filters.filter("filterBinding", function() {
|
||||
return function(bindings, query) {
|
||||
var result = []
|
||||
if (bindings == undefined) return result;
|
||||
if (query == null) { return bindings};
|
||||
|
||||
for(i=0; i<bindings.length; ++i) {
|
||||
binding = bindings[i];
|
||||
if (binding.command.indexOf(query) != -1 || binding.readable_binding.toLowerCase().indexOf(query.toLowerCase()) != -1) {
|
||||
result.push(binding);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
13
share/tools/web_config/partials/bindings.html
Normal file
13
share/tools/web_config/partials/bindings.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<div id="table_filter_container" style="display: block;">
|
||||
<input id="table_filter_text_box" class="filter_text_box text_box_transient" placeholder="Filter" ng-model="query">
|
||||
</div>
|
||||
|
||||
<table class="data_table">
|
||||
<tbody>
|
||||
<tr class="data_table_row" ng-repeat="binding in bindings | filterBinding:query">
|
||||
<td ng-class="{ data_table_cell: true, no_overflow: !binding._is_selected }" style="text-align: right; padding-right: 30px;" ng-click="binding._is_selected = !binding._is_selected">{{ binding.command }}</td>
|
||||
<!-- Some bindings are listed multiple times for e.g. function backward-char is bound to \e\[D as well as key left. Users may want to know why some bindings are listed twice, so the actual binding is shown in next line on a click -->
|
||||
<td ng-class="{ data_table_cell: true, no_overflow: !binding._is_selected }" style="text-align: left; padding-right: 30px;" ng-click="binding._is_selected = !binding._is_selected">{{ binding.readable_binding }} <div ng-show="binding._is_selected"> {{ binding.binding }} </div> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
65
share/tools/web_config/partials/colors.html
Normal file
65
share/tools/web_config/partials/colors.html
Normal file
|
@ -0,0 +1,65 @@
|
|||
<div>
|
||||
<!-- ko with: color_picker -->
|
||||
<span style="padding-left: 25px">Click to customize each color: </span><br>
|
||||
<div class="colorpicker_text_sample" ng-style="{'background-color': terminalBackgroundColor}">
|
||||
<span style="position: absolute; left: 10px; top: -6px;" data-ng-style="{'color': text_color_for_color(selectedColorScheme.preferred_background || 'white')}">{{ selectedColorScheme.name }}</span><br>
|
||||
<div class="color_picker_background_cells">
|
||||
<div ng-style="{'background-color': color}" ng-repeat="color in sampleTerminalBackgroundColors" ng-click="changeTerminalBackgroundColor(color)"></div>
|
||||
</div>
|
||||
<!-- This is the sample text -->
|
||||
<span data-ng-style="{ 'color': selectedColorScheme.command}" ng-click="selectColorSetting('command')">/bright/vixens</span>
|
||||
<span data-ng-style="{ 'color': selectedColorScheme.param}" ng-click="selectColorSetting('param')">jump</span>
|
||||
<span data-ng-style="{ 'color': selectedColorScheme.end}" ng-click="selectColorSetting('end')">|</span>
|
||||
<span data-ng-style="{ 'color': selectedColorScheme.command}" ng-click="selectColorSetting('command')">dozy</span>
|
||||
<span data-ng-style="{ 'color': selectedColorScheme.quote}" ng-click="selectColorSetting('quote')"> "fowl" </span>
|
||||
<span data-ng-style="{ 'color': selectedColorScheme.redirection}" ng-click="selectColorSetting('redirection')">> quack</span>
|
||||
<span data-ng-style="{ 'color': selectedColorScheme.end}" ng-click="selectColorSetting('end')">&</span>
|
||||
<br>
|
||||
<span data-ng-style="{ 'color': selectedColorScheme.command}" ng-click="selectColorSetting('command')">echo</span>
|
||||
<span data-ng-style="{ 'color': selectedColorScheme.error}" ng-click="selectColorSetting('error')">'Errors are the portals to discovery</span>
|
||||
<br>
|
||||
<span data-ng-style="{ 'color': selectedColorScheme.comment}" ng-click="selectColorSetting('comment')"># This is a comment</span>
|
||||
<br>
|
||||
<span data-ng-style="{ 'color': selectedColorScheme.command}" ng-click="selectColorSetting('command')">Th</span><span data-ng-style="{ 'color': selectedColorScheme.autosuggestion }" ng-click="selectColorSetting('autosuggestion')"><span class="fake_cursor"><span style="visibility: hidden">i</span></span>s is an autosuggestion</span>
|
||||
|
||||
<span class="save_button" style="position: absolute; right: 5px; bottom: 5px;" title="Terminal background color is not set automatically on Apply. See your terminal documentation to set its background color." data-ng-style="{'color': text_color_for_color(selectedColorScheme.preferred_background || 'white')}" ng-show="showSaveButton" ng-click="setTheme()">Apply</span>
|
||||
|
||||
</div>
|
||||
|
||||
<table class="colorpicker_term256" style="margin: 0px 20px;">
|
||||
<tbody>
|
||||
<tr class="colorpicker_term256_row" data-ng-repeat="color_array in colorArraysArray">
|
||||
<td class="colorpicker_term256_cell" data-ng-style="{'background-color': color, 'color': color}" ng-click="changeSelectedTextColor(color)" ng-repeat="color in color_array">
|
||||
<div class="colorpicker_term256_selection_indicator" ng-show="selectedColorScheme[selectedColorSetting] == color" ng-style="{'border-color': border_color_for_color(color)}"</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<!-- /ko -->
|
||||
</table>
|
||||
|
||||
<div class="color_scheme_choices_list">
|
||||
<div class="color_scheme_choice_container" data-ng-repeat="colorScheme in colorSchemes" ng-click="changeSelectedColorScheme(colorScheme)">
|
||||
<div class="color_scheme_choice_label">
|
||||
<!-- This click/clickBubble nonsense is so that we can have a separate URL inside a parent with an onClick handler -->
|
||||
<span>{{colorScheme.name }}</span><!--a data-bind="if: $data.url, click: function(){return true;}, clickBubble: false, attr: { href: $data.url}"><img class="external_link_img" src="external_link.png"></a-->
|
||||
</div>
|
||||
<div class="colorpicker_text_sample_tight" data-ng-style="{'background-color': colorScheme.preferred_background}">
|
||||
<span data-ng-style="{'color': colorScheme.command}">/bright/vixens</span>
|
||||
<span data-ng-style="{'color': colorScheme.param}">jump</span>
|
||||
<span data-ng-style="{'color': colorScheme.end}">|</span>
|
||||
<span data-ng-style="{'color': colorScheme.command}">dozy</span>
|
||||
<span data-ng-style="{'color': colorScheme.quote}"> "fowl" </span>
|
||||
<span data-ng-style="{'color': colorScheme.redirection}">> quack</span>
|
||||
<span data-ng-style="{'color': colorScheme.end}">&</span>
|
||||
<br>
|
||||
<span data-ng-style="{'color': colorScheme.command}">echo</span>
|
||||
<span data-ng-style="{'color': colorScheme.error}">'Errors are the portals to discovery</span>
|
||||
<br>
|
||||
<span data-ng-style="{ 'color': colorScheme.comment}"># This is a comment</span>
|
||||
<br>
|
||||
<span data-ng-style="{ 'color': colorScheme.command}">Th</span><span data-ng-style="{ 'color': colorScheme.autosuggestion}"><span class="fake_cursor"><span style="visibility: hidden">i</span></span>s is an autosuggestion</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
</div>
|
12
share/tools/web_config/partials/functions.html
Normal file
12
share/tools/web_config/partials/functions.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<div class="master_detail_table">
|
||||
<div class="master">
|
||||
<div ng-repeat="func in functions">
|
||||
<div id="master_{{func}}" ng-class="{'master_element': true, 'selected_master_elem': func == selectedFunction }" ng-style="'color: #aaaaaa'" ng-click="selectFunction(func)">
|
||||
<span class="master_element_text" style="font-size: 11pt;">{{ func }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<div class="detail_function">{{ functionDefinition }}</div>
|
||||
</div>
|
||||
</div>
|
16
share/tools/web_config/partials/history.html
Normal file
16
share/tools/web_config/partials/history.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
<div id="table_filter_container">
|
||||
<span ng-show="loadingText.length > 0"> {{ loadingText }} </span>
|
||||
<input id="table_filter_text_box" class="filter_text_box text_box_transient" placeholder="Filter" ng-model="queryInput">
|
||||
</div>
|
||||
<table class="data_table">
|
||||
<tbody>
|
||||
<tr ng-repeat="item in historyItems | filter:query">
|
||||
<td ng-class="{'history_text': true, 'no_overflow': selectedItems.indexOf(item) < 0}" ng-click="selectItem(item)">{{ item }}</td>
|
||||
<td class="history_delete">
|
||||
<a ng-click="deleteHistoryItem(item)">
|
||||
<img class="delete_icon" src="delete.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
10
share/tools/web_config/partials/prompt.html
Normal file
10
share/tools/web_config/partials/prompt.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
<div style="padding: 0 10px 15px;">
|
||||
<div ng-repeat="prompt in samplePrompts">
|
||||
<div class="prompt_demo_choice_label">{{ prompt.name }}</div>
|
||||
<div ng-bind-html-unsafe='prompt.demo' class="prompt_demo" ng-click="selectPrompt(prompt)"></div>
|
||||
<div class="prompt_function" ng-show="selectedPrompt == prompt">
|
||||
<div class="prompt_function_text">{{ prompt.function }}</div>
|
||||
</div>
|
||||
<span class="prompt_save_button" ng-click="setNewPrompt(selectedPrompt)" ng-show="selectedPrompt == prompt && selectedPrompt != samplePrompts[0]">Use</span>
|
||||
</div>
|
||||
</div>
|
13
share/tools/web_config/partials/variables.html
Normal file
13
share/tools/web_config/partials/variables.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<div id="table_filter_container">
|
||||
<input id="table_filter_text_box" class="filter_text_box text_box_transient" placeholder="Filter" ng-model="query">
|
||||
</div>
|
||||
|
||||
<table class="data_table">
|
||||
<tbody>
|
||||
<tr class="data_table_row" ng-repeat="variable in variables | filterVariable:query">
|
||||
<td class="data_table_cell no_overflow" style="text-align: right; padding-right: 30px;">{{ variable.name }}</td>
|
||||
<!-- Small hack to select/unselect variables -->
|
||||
<td ng-class="{'data_table_cell': true, 'no_overflow': !variable._is_selected}" style="text-align: left; padding-right: 30px;" ng-click="variable._is_selected=!variable._is_selected">{{ variable.value }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
|
@ -44,4 +44,5 @@ function fish_prompt --description 'Write out the prompt'
|
|||
|
||||
echo -n '$ '
|
||||
|
||||
set_color normal
|
||||
end
|
||||
|
|
|
@ -101,7 +101,7 @@ def parse_color(color_str):
|
|||
# Regular color
|
||||
color = better_color(color, parse_one_color(comp))
|
||||
|
||||
return [color, background_color, bold, underline]
|
||||
return {"color": color, "background": background_color, "bold": bold, "underline": underline}
|
||||
|
||||
def parse_bool(val):
|
||||
val = val.lower()
|
||||
|
@ -254,7 +254,161 @@ class FishVar:
|
|||
flags = []
|
||||
if self.universal: flags.append('universal')
|
||||
if self.exported: flags.append('exported')
|
||||
return [self.name, self.value, ', '.join(flags)]
|
||||
return {"name": self.name, "value": self.value, "Flags": ', '.join(flags)}
|
||||
|
||||
class FishBinding:
|
||||
"""A class that represents keyboard binding """
|
||||
|
||||
def __init__(self, command, binding, readable_binding, description=None):
|
||||
self.command = command
|
||||
self.binding = binding
|
||||
self.readable_binding = readable_binding
|
||||
self.description = description
|
||||
|
||||
def get_json_obj(self):
|
||||
return {"command" : self.command, "binding": self.binding, "readable_binding": self.readable_binding, "description": self.description }
|
||||
|
||||
def get_readable_binding(command):
|
||||
return command
|
||||
|
||||
class BindingParser:
|
||||
""" Class to parse codes for bind command """
|
||||
|
||||
#TODO: What does snext and sprevious mean ?
|
||||
readable_keys= { "dc":"Delete", "npage": "Page Up", "ppage":"Page Down",
|
||||
"sdc": "Shift Delete", "shome": "Shift Home",
|
||||
"left": "Left Arrow", "right": "Right Arrow",
|
||||
"up": "Up Arrow", "down": "Down Arrow",
|
||||
"sleft": "Shift Left", "sright": "Shift Right"
|
||||
}
|
||||
|
||||
def set_buffer(self, buffer, is_key=False):
|
||||
""" Sets code to parse """
|
||||
|
||||
self.buffer = buffer
|
||||
self.is_key = is_key
|
||||
self.index = 0
|
||||
|
||||
def get_char(self):
|
||||
""" Gets next character from buffer """
|
||||
|
||||
c = self.buffer[self.index]
|
||||
self.index += 1
|
||||
return c
|
||||
|
||||
def unget_char(self):
|
||||
""" Goes back by one character for parsing """
|
||||
|
||||
self.index -= 1
|
||||
|
||||
def end(self):
|
||||
""" Returns true if reached end of buffer """
|
||||
|
||||
return self.index >= len(self.buffer)
|
||||
|
||||
def parse_control_sequence(self):
|
||||
""" Parses terminal specifiec control sequences """
|
||||
|
||||
result = ''
|
||||
c = self.get_char()
|
||||
|
||||
# \e0 is used to denote start of control sequence
|
||||
if c == 'O':
|
||||
c = self.get_char()
|
||||
|
||||
# \[1\; is start of control sequence
|
||||
if c == '1':
|
||||
self.get_char();c = self.get_char()
|
||||
if c == ";":
|
||||
c = self.get_char()
|
||||
|
||||
# 3 is Alt
|
||||
if c == '3':
|
||||
result += "ALT - "
|
||||
c = self.get_char()
|
||||
|
||||
# 5 is Ctrl
|
||||
if c == '5':
|
||||
result += "CTRL - "
|
||||
c = self.get_char()
|
||||
|
||||
# 9 is Alt
|
||||
if c == '9':
|
||||
result += "ALT - "
|
||||
c = self.get_char()
|
||||
|
||||
if c == 'A':
|
||||
result += 'Up Arrow'
|
||||
elif c == 'B':
|
||||
result += 'Down Arrow'
|
||||
elif c == 'C':
|
||||
result += 'Right Arrow'
|
||||
elif c == 'D':
|
||||
result += "Left Arrow"
|
||||
elif c == 'F':
|
||||
result += "End"
|
||||
elif c == 'H':
|
||||
result += "Home"
|
||||
|
||||
return result
|
||||
|
||||
def get_readable_binding(self):
|
||||
""" Gets a readable representation of binding """
|
||||
|
||||
if self.is_key:
|
||||
try:
|
||||
result = BindingParser.readable_keys[self.buffer]
|
||||
except KeyError:
|
||||
result = self.buffer.title()
|
||||
else:
|
||||
result = self.parse_binding()
|
||||
|
||||
return result
|
||||
|
||||
def parse_binding(self):
|
||||
readable_command = ''
|
||||
result = ''
|
||||
alt = ctrl = False
|
||||
|
||||
while not self.end():
|
||||
c = self.get_char()
|
||||
|
||||
if c == '\\':
|
||||
c = self.get_char()
|
||||
if c == 'e':
|
||||
d = self.get_char()
|
||||
if d == 'O':
|
||||
self.unget_char()
|
||||
result += self.parse_control_sequence()
|
||||
elif d == '\\':
|
||||
if self.get_char() == '[':
|
||||
result += self.parse_control_sequence()
|
||||
else:
|
||||
self.unget_char()
|
||||
self.unget_char()
|
||||
alt = True
|
||||
else:
|
||||
alt = True
|
||||
self.unget_char()
|
||||
elif c == 'c':
|
||||
ctrl = True
|
||||
elif c == 'n':
|
||||
result += 'Enter'
|
||||
elif c == 't':
|
||||
result += 'Tab'
|
||||
elif c == 'b':
|
||||
result += 'Backspace'
|
||||
else:
|
||||
result += c
|
||||
else:
|
||||
result += c
|
||||
if ctrl:
|
||||
readable_command += 'CTRL - '
|
||||
if alt:
|
||||
readable_command += 'ALT - '
|
||||
|
||||
return readable_command + result
|
||||
|
||||
|
||||
class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
|
||||
|
@ -314,7 +468,9 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
|||
for match in re.finditer(r"^fish_color_(\S+) ?(.*)", line):
|
||||
color_name, color_value = [x.strip() for x in match.group(1, 2)]
|
||||
color_desc = descriptions.get(color_name, '')
|
||||
result.append([color_name, color_desc, parse_color(color_value)])
|
||||
data = { "name": color_name, "description" : color_desc }
|
||||
data.update(parse_color(color_value))
|
||||
result.append(data)
|
||||
remaining.discard(color_name)
|
||||
|
||||
# Ensure that we have all the color names we know about, so that if the
|
||||
|
@ -363,6 +519,39 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
|||
|
||||
return [vars[key].get_json_obj() for key in sorted(vars.keys(), key=str.lower)]
|
||||
|
||||
def do_get_bindings(self):
|
||||
""" Get key bindings """
|
||||
|
||||
# Running __fish_config_interactive print fish greeting and
|
||||
# loads key bindings
|
||||
greeting, err = run_fish_cmd(' __fish_config_interactive')
|
||||
|
||||
# Load the key bindings and then list them with bind
|
||||
out, err = run_fish_cmd('__fish_config_interactive; bind')
|
||||
|
||||
# Remove fish greeting from output
|
||||
out = out[len(greeting):]
|
||||
|
||||
# Put all the bindings into a list
|
||||
bindings = []
|
||||
binding_parser = BindingParser()
|
||||
|
||||
for line in out.split('\n'):
|
||||
comps = line.split(' ', 2)
|
||||
if len(comps) < 3:
|
||||
continue
|
||||
if comps[1] == '-k':
|
||||
key_name, command = comps[2].split(' ', 1)
|
||||
binding_parser.set_buffer(key_name, True)
|
||||
fish_binding = FishBinding(command=command, binding=key_name, readable_binding=binding_parser.get_readable_binding())
|
||||
else:
|
||||
binding_parser.set_buffer(comps[1])
|
||||
fish_binding = FishBinding(command=comps[2], binding=comps[1], readable_binding=binding_parser.get_readable_binding())
|
||||
|
||||
bindings.append(fish_binding)
|
||||
|
||||
return [ binding.get_json_obj() for binding in bindings ]
|
||||
|
||||
def do_get_history(self):
|
||||
# Use \x1e ("record separator") to distinguish between history items. The first
|
||||
# backslash is so Python passes one backslash to fish
|
||||
|
@ -371,7 +560,6 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
|||
if result: result.pop() # Trim off the trailing element
|
||||
return result
|
||||
|
||||
|
||||
def do_get_color_for_variable(self, name):
|
||||
"Return the color with the given name, or the empty string if there is none"
|
||||
out, err = run_fish_cmd("echo -n $" + name)
|
||||
|
@ -448,13 +636,15 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
|||
# Maybe not we're not parsing hashes, or maybe we already were not
|
||||
if not parsing_hashes:
|
||||
function_lines.append(line)
|
||||
result['function'] = ''.join(function_lines).strip()
|
||||
func = ''.join(function_lines).strip()
|
||||
result.update(self.do_get_sample_prompt(func))
|
||||
return result
|
||||
|
||||
def do_get_sample_prompts_list(self):
|
||||
result = []
|
||||
# Start with the "Current" meta-sample
|
||||
result.append({'name': 'Current'})
|
||||
result[0].update(self.do_get_current_prompt())
|
||||
|
||||
# Read all of the prompts in sample_prompts
|
||||
paths = glob.iglob('sample_prompts/*.fish')
|
||||
|
@ -502,6 +692,8 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
|||
elif re.match(r"/color/(\w+)/", p):
|
||||
name = re.match(r"/color/(\w+)/", p).group(1)
|
||||
output = self.do_get_color_for_variable(name)
|
||||
elif p == '/bindings/':
|
||||
output = self.do_get_bindings()
|
||||
else:
|
||||
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ echo x-{1}
|
|||
echo x-{1,2}
|
||||
echo foo-{1,2{3,4}}
|
||||
|
||||
# Escpaed newlines
|
||||
# Escaped newlines
|
||||
echo foo\ bar
|
||||
echo foo\
|
||||
bar
|
||||
|
@ -99,6 +99,12 @@ echo Test 5 $sta
|
|||
echo Test redirections
|
||||
begin ; echo output ; echo errput 1>&2 ; end 2>&1 | tee /tmp/tee_test.txt ; cat /tmp/tee_test.txt
|
||||
|
||||
# Verify that we can pipe something other than stdout
|
||||
# The first line should be printed, since we output to stdout but pipe stderr to /dev/null
|
||||
# The second line should not be printed, since we output to stderr and pipe it to /dev/null
|
||||
begin ; echo is_stdout ; end 2>| cat > /dev/null
|
||||
begin ; echo is_stderr 1>&2 ; end 2>| cat > /dev/null
|
||||
|
||||
# echo tests
|
||||
|
||||
echo 'abc\ndef'
|
||||
|
|
|
@ -23,6 +23,7 @@ errput
|
|||
output
|
||||
errput
|
||||
output
|
||||
is_stdout
|
||||
abc\ndef
|
||||
abc
|
||||
def
|
||||
|
|
|
@ -20,15 +20,6 @@ case one
|
|||
echo $status
|
||||
end
|
||||
|
||||
# Test that non-case tokens inside `switch` don't blow away status
|
||||
# (why are these even allowed?)
|
||||
false
|
||||
switch one
|
||||
true
|
||||
case one
|
||||
echo $status
|
||||
end
|
||||
|
||||
#test contains -i
|
||||
echo test contains -i
|
||||
contains -i string a b c string d
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
3
|
||||
0
|
||||
|
||||
1
|
||||
1
|
||||
test contains -i
|
||||
4
|
||||
|
|
|
@ -35,3 +35,39 @@ emit test3 foo bar
|
|||
|
||||
# test empty argument
|
||||
emit
|
||||
|
||||
echo "Test break and continue"
|
||||
# This should output Ping once
|
||||
for i in a b c
|
||||
if not contains $i c ; continue ; end
|
||||
echo Ping
|
||||
end
|
||||
|
||||
# This should output Pong not at all
|
||||
for i in a b c
|
||||
if not contains $i c ; break ; end
|
||||
echo Pong
|
||||
end
|
||||
|
||||
# This should output Foop three times, and Boop not at all
|
||||
set i a a a
|
||||
while contains $i a
|
||||
set -e i[-1]
|
||||
echo Foop
|
||||
continue
|
||||
echo Boop
|
||||
end
|
||||
|
||||
# This should output Doop once
|
||||
set i a a a
|
||||
while contains $i a
|
||||
set -e i[-1]
|
||||
echo Doop
|
||||
break
|
||||
echo Darp
|
||||
end
|
||||
|
||||
# Test implicit cd. This should do nothing.
|
||||
./
|
||||
|
||||
false
|
||||
|
|
|
@ -2,3 +2,9 @@ Testing that builtins can truncate files
|
|||
abc
|
||||
before:test1
|
||||
received event test3 with args: foo bar
|
||||
Test break and continue
|
||||
Ping
|
||||
Foop
|
||||
Foop
|
||||
Foop
|
||||
Doop
|
||||
|
|
229
tokenizer.cpp
229
tokenizer.cpp
|
@ -14,7 +14,7 @@ segments.
|
|||
#include <wctype.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "fallback.h"
|
||||
#include "util.h"
|
||||
|
@ -50,7 +50,7 @@ segments.
|
|||
/**
|
||||
Error string for when trying to pipe from fd 0
|
||||
*/
|
||||
#define PIPE_ERROR _( L"Can not use fd 0 as pipe output" )
|
||||
#define PIPE_ERROR _( L"Cannot use stdin (fd 0) as pipe output" )
|
||||
|
||||
/**
|
||||
Characters that separate tokens. They are ordered by frequency of occurrence to increase parsing speed.
|
||||
|
@ -64,7 +64,6 @@ static const wchar_t *tok_desc[] =
|
|||
{
|
||||
N_(L"Tokenizer not yet initialized"),
|
||||
N_(L"Tokenizer error"),
|
||||
N_(L"Invalid token"),
|
||||
N_(L"String"),
|
||||
N_(L"Pipe"),
|
||||
N_(L"End of command"),
|
||||
|
@ -77,6 +76,8 @@ static const wchar_t *tok_desc[] =
|
|||
N_(L"Comment")
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Set the latest tokens string to be the specified error message
|
||||
*/
|
||||
|
@ -95,16 +96,8 @@ int tok_get_error(tokenizer_t *tok)
|
|||
|
||||
tokenizer_t::tokenizer_t(const wchar_t *b, tok_flags_t flags) : buff(NULL), orig_buff(NULL), last_type(TOK_NONE), last_pos(0), has_next(false), accept_unfinished(false), show_comments(false), last_quote(0), error(0), squash_errors(false), cached_lineno_offset(0), cached_lineno_count(0)
|
||||
{
|
||||
|
||||
/* We can only generate error messages on the main thread due to wgettext() thread safety issues. */
|
||||
if (!(flags & TOK_SQUASH_ERRORS))
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
}
|
||||
|
||||
CHECK(b,);
|
||||
|
||||
|
||||
this->accept_unfinished = !!(flags & TOK_ACCEPT_UNFINISHED);
|
||||
this->show_comments = !!(flags & TOK_SHOW_COMMENTS);
|
||||
this->squash_errors = !!(flags & TOK_SQUASH_ERRORS);
|
||||
|
@ -435,65 +428,141 @@ static void read_comment(tokenizer_t *tok)
|
|||
tok->last_type = TOK_COMMENT;
|
||||
}
|
||||
|
||||
/**
|
||||
Read a FD redirection.
|
||||
|
||||
|
||||
/* Reads a redirection or an "fd pipe" (like 2>|) from a string. Returns how many characters were consumed. If zero, then this string was not a redirection.
|
||||
|
||||
Also returns by reference the redirection mode, and the fd to redirection. If there is overflow, *out_fd is set to -1.
|
||||
*/
|
||||
static void read_redirect(tokenizer_t *tok, int fd)
|
||||
static size_t read_redirection_or_fd_pipe(const wchar_t *buff, enum token_type *out_redirection_mode, int *out_fd)
|
||||
{
|
||||
bool errored = false;
|
||||
int fd = 0;
|
||||
enum token_type redirection_mode = TOK_NONE;
|
||||
|
||||
if ((*tok->buff == L'>') ||
|
||||
(*tok->buff == L'^'))
|
||||
size_t idx = 0;
|
||||
|
||||
/* Determine the fd. This may be specified as a prefix like '2>...' or it may be implicit like '>' or '^'. Try parsing out a number; if we did not get any digits then infer it from the first character. Watch out for overflow. */
|
||||
long long big_fd = 0;
|
||||
for (; iswdigit(buff[idx]); idx++)
|
||||
{
|
||||
tok->buff++;
|
||||
if (*tok->buff == *(tok->buff-1))
|
||||
{
|
||||
tok->buff++;
|
||||
redirection_mode = TOK_REDIRECT_APPEND;
|
||||
}
|
||||
else
|
||||
{
|
||||
redirection_mode = TOK_REDIRECT_OUT;
|
||||
/* Note that it's important we consume all the digits here, even if it overflows. */
|
||||
if (big_fd <= INT_MAX)
|
||||
big_fd = big_fd * 10 + (buff[idx] - L'0');
|
||||
}
|
||||
|
||||
if (*tok->buff == L'|')
|
||||
fd = (big_fd > INT_MAX ? -1 : static_cast<int>(big_fd));
|
||||
|
||||
if (idx == 0)
|
||||
{
|
||||
if (fd == 0)
|
||||
/* We did not find a leading digit, so there's no explicit fd. Infer it from the type */
|
||||
switch (buff[idx])
|
||||
{
|
||||
TOK_CALL_ERROR(tok, TOK_OTHER, PIPE_ERROR);
|
||||
return;
|
||||
}
|
||||
tok->buff++;
|
||||
tok->last_token = to_string<int>(fd);
|
||||
tok->last_type = TOK_PIPE;
|
||||
return;
|
||||
case L'>': fd = STDOUT_FILENO; break;
|
||||
case L'<': fd = STDIN_FILENO; break;
|
||||
case L'^': fd = STDERR_FILENO; break;
|
||||
default: errored = true; break;
|
||||
}
|
||||
}
|
||||
else if (*tok->buff == L'<')
|
||||
|
||||
/* Either way we should have ended on the redirection character itself like '>' */
|
||||
wchar_t redirect_char = buff[idx++]; //note increment of idx
|
||||
if (redirect_char == L'>' || redirect_char == L'^')
|
||||
{
|
||||
redirection_mode = TOK_REDIRECT_OUT;
|
||||
if (buff[idx] == redirect_char)
|
||||
{
|
||||
/* Doubled up like ^^ or >>. That means append */
|
||||
redirection_mode = TOK_REDIRECT_APPEND;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
else if (redirect_char == L'<')
|
||||
{
|
||||
tok->buff++;
|
||||
redirection_mode = TOK_REDIRECT_IN;
|
||||
}
|
||||
else
|
||||
{
|
||||
TOK_CALL_ERROR(tok, TOK_OTHER, REDIRECT_ERROR);
|
||||
/* Something else */
|
||||
errored = true;
|
||||
}
|
||||
|
||||
tok->last_token = to_string(fd);
|
||||
/* Optional characters like & or ?, or the pipe char | */
|
||||
wchar_t opt_char = buff[idx];
|
||||
if (opt_char == L'&')
|
||||
{
|
||||
redirection_mode = TOK_REDIRECT_FD;
|
||||
idx++;
|
||||
}
|
||||
else if (opt_char == L'?')
|
||||
{
|
||||
redirection_mode = TOK_REDIRECT_NOCLOB;
|
||||
idx++;
|
||||
}
|
||||
else if (opt_char == L'|')
|
||||
{
|
||||
/* So the string looked like '2>|'. This is not a redirection - it's a pipe! That gets handled elsewhere. */
|
||||
redirection_mode = TOK_PIPE;
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (*tok->buff == L'&')
|
||||
/* Don't return valid-looking stuff on error */
|
||||
if (errored)
|
||||
{
|
||||
tok->buff++;
|
||||
tok->last_type = TOK_REDIRECT_FD;
|
||||
idx = 0;
|
||||
redirection_mode = TOK_NONE;
|
||||
}
|
||||
else if (*tok->buff == L'?')
|
||||
|
||||
/* Return stuff */
|
||||
if (out_redirection_mode != NULL)
|
||||
*out_redirection_mode = redirection_mode;
|
||||
if (out_fd != NULL)
|
||||
*out_fd = fd;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
enum token_type redirection_type_for_string(const wcstring &str, int *out_fd)
|
||||
{
|
||||
enum token_type mode = TOK_NONE;
|
||||
int fd = 0;
|
||||
read_redirection_or_fd_pipe(str.c_str(), &mode, &fd);
|
||||
/* Redirections only, no pipes */
|
||||
if (mode == TOK_PIPE || fd < 0)
|
||||
mode = TOK_NONE;
|
||||
if (out_fd != NULL)
|
||||
*out_fd = fd;
|
||||
return mode;
|
||||
}
|
||||
|
||||
int fd_redirected_by_pipe(const wcstring &str)
|
||||
{
|
||||
/* Hack for the common case */
|
||||
if (str == L"|")
|
||||
{
|
||||
tok->buff++;
|
||||
tok->last_type = TOK_REDIRECT_NOCLOB;
|
||||
return STDOUT_FILENO;
|
||||
}
|
||||
else
|
||||
|
||||
enum token_type mode = TOK_NONE;
|
||||
int fd = 0;
|
||||
read_redirection_or_fd_pipe(str.c_str(), &mode, &fd);
|
||||
/* Pipes only */
|
||||
if (mode != TOK_PIPE || fd < 0)
|
||||
fd = -1;
|
||||
return fd;
|
||||
}
|
||||
|
||||
int oflags_for_redirection_type(enum token_type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
tok->last_type = redirection_mode;
|
||||
case TOK_REDIRECT_APPEND: return O_CREAT | O_APPEND | O_WRONLY;
|
||||
case TOK_REDIRECT_OUT: return O_CREAT | O_WRONLY | O_TRUNC;
|
||||
case TOK_REDIRECT_NOCLOB: return O_CREAT | O_EXCL | O_WRONLY;
|
||||
case TOK_REDIRECT_IN: return O_RDONLY;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -516,7 +585,7 @@ static bool my_iswspace(wchar_t c)
|
|||
|
||||
const wchar_t *tok_get_desc(int type)
|
||||
{
|
||||
if (type < 0 || (size_t)type >= sizeof(tok_desc))
|
||||
if (type < 0 || (size_t)type >= (sizeof tok_desc / sizeof *tok_desc))
|
||||
{
|
||||
return _(L"Invalid token type");
|
||||
}
|
||||
|
@ -606,36 +675,56 @@ void tok_next(tokenizer_t *tok)
|
|||
break;
|
||||
|
||||
case L'>':
|
||||
read_redirect(tok, 1);
|
||||
return;
|
||||
case L'<':
|
||||
read_redirect(tok, 0);
|
||||
return;
|
||||
case L'^':
|
||||
read_redirect(tok, 2);
|
||||
return;
|
||||
{
|
||||
/* There's some duplication with the code in the default case below. The key difference here is that we must never parse these as a string; a failed redirection is an error! */
|
||||
enum token_type mode = TOK_NONE;
|
||||
int fd = -1;
|
||||
size_t consumed = read_redirection_or_fd_pipe(tok->buff, &mode, &fd);
|
||||
if (consumed == 0 || fd < 0)
|
||||
{
|
||||
TOK_CALL_ERROR(tok, TOK_OTHER, REDIRECT_ERROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
tok->buff += consumed;
|
||||
tok->last_type = mode;
|
||||
tok->last_token = to_string(fd);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
/* Maybe a redirection like '2>&1', maybe a pipe like 2>|, maybe just a string */
|
||||
size_t consumed = 0;
|
||||
enum token_type mode = TOK_NONE;
|
||||
int fd = -1;
|
||||
if (iswdigit(*tok->buff))
|
||||
{
|
||||
const wchar_t *orig = tok->buff;
|
||||
int fd = 0;
|
||||
while (iswdigit(*tok->buff))
|
||||
fd = (fd*10) + (*(tok->buff++) - L'0');
|
||||
consumed = read_redirection_or_fd_pipe(tok->buff, &mode, &fd);
|
||||
|
||||
switch (*(tok->buff))
|
||||
if (consumed > 0)
|
||||
{
|
||||
case L'^':
|
||||
case L'>':
|
||||
case L'<':
|
||||
read_redirect(tok, fd);
|
||||
return;
|
||||
/* It looks like a redirection or a pipe. But we don't support piping fd 0. Note that fd 0 may be -1, indicating overflow; but we don't treat that as a tokenizer error. */
|
||||
if (mode == TOK_PIPE && fd == 0)
|
||||
{
|
||||
TOK_CALL_ERROR(tok, TOK_OTHER, PIPE_ERROR);
|
||||
}
|
||||
tok->buff = orig;
|
||||
else
|
||||
{
|
||||
tok->buff += consumed;
|
||||
tok->last_type = mode;
|
||||
tok->last_token = to_string(fd);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Not a redirection or pipe, so just a stirng */
|
||||
read_string(tok);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
@ -693,13 +782,19 @@ wcstring tok_first(const wchar_t *str)
|
|||
return result;
|
||||
}
|
||||
|
||||
int tok_get_pos(tokenizer_t *tok)
|
||||
int tok_get_pos(const tokenizer_t *tok)
|
||||
{
|
||||
CHECK(tok, 0);
|
||||
|
||||
return (int)tok->last_pos;
|
||||
}
|
||||
|
||||
size_t tok_get_extent(const tokenizer_t *tok)
|
||||
{
|
||||
CHECK(tok, 0);
|
||||
size_t current_pos = tok->buff - tok->orig_buff;
|
||||
return current_pos > tok->last_pos ? current_pos - tok->last_pos : 0;
|
||||
}
|
||||
|
||||
|
||||
void tok_set_pos(tokenizer_t *tok, int pos)
|
||||
{
|
||||
|
|
17
tokenizer.h
17
tokenizer.h
|
@ -19,10 +19,9 @@ enum token_type
|
|||
{
|
||||
TOK_NONE, /**< Tokenizer not yet constructed */
|
||||
TOK_ERROR, /**< Error reading token */
|
||||
TOK_INVALID,/**< Invalid token */
|
||||
TOK_STRING,/**< String token */
|
||||
TOK_PIPE,/**< Pipe token */
|
||||
TOK_END,/**< End token */
|
||||
TOK_END,/**< End token (semicolon or newline, not literal end) */
|
||||
TOK_REDIRECT_OUT, /**< redirection token */
|
||||
TOK_REDIRECT_APPEND,/**< redirection append token */
|
||||
TOK_REDIRECT_IN,/**< input redirection token */
|
||||
|
@ -143,7 +142,10 @@ int tok_has_next(tokenizer_t *tok);
|
|||
/**
|
||||
Returns the position of the beginning of the current token in the original string
|
||||
*/
|
||||
int tok_get_pos(tokenizer_t *tok);
|
||||
int tok_get_pos(const tokenizer_t *tok);
|
||||
|
||||
/** Returns the extent of the current token */
|
||||
size_t tok_get_extent(const tokenizer_t *tok);
|
||||
|
||||
/** Returns the token type after the current one, without adjusting the position. Optionally returns the next string by reference. */
|
||||
enum token_type tok_peek_next(tokenizer_t *tok, wcstring *out_next_string);
|
||||
|
@ -185,6 +187,15 @@ const wchar_t *tok_get_desc(int type);
|
|||
*/
|
||||
int tok_get_error(tokenizer_t *tok);
|
||||
|
||||
/* Helper function to determine redirection type from a string, or TOK_NONE if the redirection is invalid. Also returns the fd by reference. */
|
||||
enum token_type redirection_type_for_string(const wcstring &str, int *out_fd = NULL);
|
||||
|
||||
/* Helper function to determine which fd is redirected by a pipe */
|
||||
int fd_redirected_by_pipe(const wcstring &str);
|
||||
|
||||
/* Helper function to return oflags (as in open(2)) for a redirection type */
|
||||
int oflags_for_redirection_type(enum token_type type);
|
||||
|
||||
enum move_word_style_t
|
||||
{
|
||||
move_word_style_punctuation, //stop at punctuation
|
||||
|
|
|
@ -476,7 +476,7 @@ const wchar_t *wgettext(const wchar_t *in)
|
|||
{
|
||||
cstring mbs_in = wcs2string(key);
|
||||
char *out = fish_gettext(mbs_in.c_str());
|
||||
val = new wcstring(format_string(L"%s", out));
|
||||
val = new wcstring(format_string(L"%s", out)); //note that this writes into the map!
|
||||
}
|
||||
errno = err;
|
||||
return val->c_str();
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __XDG_MIME_ALIAS_H__
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __XDG_MIME_GLOB_H__
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __XDG_MIME_INT_H__
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __XDG_MIME_MAGIC_H__
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __XDG_MIME_PARENT_H__
|
||||
|
|
Loading…
Reference in a new issue